[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: [tigase]\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: 'bug'\nassignees: ''\n\n---\n\n> # NOTE: Tigase has new home at [tigase.dev](https://tigase.dev/) - please submit all issues/pull-requests there in the relevant sub-project!\n\nFor main Tigase Server please submit it here: https://tigase.dev/tigase/_server/server-core/~issues/\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: 'enhancement'\nassignees: ''\n\n---\n\n> # NOTE: Tigase has new home at [tigase.dev](https://tigase.dev/) - please submit all issues/pull-requests there in the relevant sub-project!\n\nFor main Tigase Server please submit it here: https://tigase.dev/tigase/_server/server-core/~issues/"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/question.md",
    "content": "---\nname: General question\nabout: Just a question\ntitle: ''\nlabels: 'question'\nassignees: ''\n\n---\n\n> # NOTE: Tigase has new home at [tigase.dev](https://tigase.dev/) - please submit all issues/pull-requests there in the relevant sub-project!\n\nFor main Tigase Server please submit it here: https://tigase.dev/tigase/_server/server-core/~issues/"
  },
  {
    "path": ".gitignore",
    "content": ".settings\ntarget\ninit.properties\n.classpath\n.project\nscripts/admin\nall-production-servers.txt\nMANIFEST.MF\ncluster-1-servers.txt\ncluster-2-servers.txt\njars/**\nlogs/**\nlibs/**\n!logs/server-info.html\nconfig.txt\ndocs\npackages\ncerts/*\ncerts/default.pem\ncerts/test-d.pem\ncerts/tls-required.com.pem\nscripts/update-code-config.sh\netc/config-dump.properties\ndistribution/ChangeLog\ndistribution/License.html\ndistribution/README\ndistribution/package.html\netc/META-INF/maven/tigase/tigase-http-api/pom.xml\nscripts/pubsub/repository_migrator.groovy\ndatabase/README.txt\ndatabase/pubsub-*\ndatabase/*-pubsub-*\ndatabase/*-muc-*\ndatabase/*-message-archiving-*\ndatabase/*-unified-archive-*\ndatabase/muc-db-migrate.*derby.log\ntigase/setup\n.idea/\n*.iml\n/bin/\n/out/\nsrc/main/groovy/tigase/admin/DeleteItem.groovy\nsrc/main/groovy/tigase/admin/ListItems.groovy\nsrc/main/groovy/tigase/admin/ListNodes.groovy\nsrc/main/groovy/tigase/admin/RetrieveItem.groovy\netc/installation-id\netc/*.licence*\ndocumentation/text/components\ndocumentation/devguide/text/components\ndistribution-docs\ndistribution-packages\nlogs\netc/acs.licence\netc/config-dump.properties\netc/installation-id\nsrc/main/restructured/locale/*/LC_MESSAGES/Tigase_Administration/*.mo\nsrc/main/restructured/locale/*/LC_MESSAGES/Tigase_Development/*.mo\nsrc/main/restructured/locale/*/LC_MESSAGES/Tigase_Administration/*/*.mo\nsrc/main/restructured/locale/*/LC_MESSAGES/Tigase_Development/*/*.mo\nsrc/main/restructured/locale/*/LC_MESSAGES/*.mo\nsrc/main/restructured/locale/*/LC_MESSAGES/*.mo\nsrc/main/restructured/_build/\n"
  },
  {
    "path": ".mvn/extensions.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n\n    Tigase XMPP Server - The instant messaging server\n    Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published by\n    the Free Software Foundation, version 3 of the License.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program. Look for COPYING file in the top folder.\n    If not, see http://www.gnu.org/licenses/.\n\n-->\n<extensions>\n    <extension>\n        <!-- https://github.com/timgifford/maven-buildtime-extension -->\n        <!-- to run it add `-Dbuildtime.output.log=true` to the mvn command -->\n        <groupId>co.leantechniques</groupId>\n        <artifactId>maven-buildtime-extension</artifactId>\n        <version>3.0.5</version>\n    </extension>\n\n    <extension>\n        <groupId>com.soebes.maven.extensions</groupId>\n        <artifactId>maven-buildtime-profiler</artifactId>\n        <version>0.5.0</version>\n    </extension>\n\n    <extension>\n        <!-- requires enabling during execution: `-Dprofile -DprofileFormat=JSON,HTML` -->\n        <groupId>fr.jcgay.maven</groupId>\n        <artifactId>maven-profiler</artifactId>\n        <version>3.3</version>\n    </extension>\n</extensions>\n\n"
  },
  {
    "path": ".onedev-buildspec.yml",
    "content": "version: 40\nimports:\n- projectPath: Templates\n  revision: 0.0.8\njobs:\n- name: Maven CI\n  jobExecutor: linux\n  steps:\n  - !UseTemplateStep\n    name: maven build & deploy\n    templateName: Tigase Server - Maven build & deploy\n    paramMatrix:\n    - name: timestamp-to-make-distinct\n      valuesProvider: !ScriptingValues\n        scriptName: getDateTimestamp\n      secret: false\n    condition: SUCCESSFUL\n    optional: false\n  triggers:\n  - !BranchUpdateTrigger {}\n  projectDependencies:\n  - projectPath: tigase/_server/tigase-utils\n    buildProvider: !LastFinishedBuild\n      jobName: Maven CI\n    artifacts: '**'\n  - projectPath: tigase/_server/tigase-xmltools\n    buildProvider: !LastFinishedBuild\n      jobName: Maven CI\n    artifacts: '**'\n  retryCondition: never\n  maxRetries: 3\n  retryDelay: 30\n  timeout: 3600\n  postBuildActions:\n  - !SendNotificationAction\n    condition: failed\n    receivers: group(TigaseCoreTeam)\n"
  },
  {
    "path": ".readthedocs.yaml",
    "content": "# Required\nversion: 2\n\n# Set the version of Python and other tools you might need\nbuild:\n  os: ubuntu-22.04\n  tools:\n    python: \"3.11\"\n\npython:\n  install:\n    - requirements: src/main/restructured/requirements.txt\n\n# Build documentation in the docs/ directory with Sphinx\nsphinx:\n  configuration: src/main/restructured/conf.py"
  },
  {
    "path": "COPYING",
    "content": "                    GNU AFFERO GENERAL PUBLIC LICENSE\n                       Version 3, 19 November 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU Affero General Public License is a free, copyleft license for\nsoftware and other kinds of works, specifically designed to ensure\ncooperation with the community in the case of network server software.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nour General Public Licenses are intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  Developers that use our General Public Licenses protect your rights\nwith two steps: (1) assert copyright on the software, and (2) offer\nyou this License which gives you legal permission to copy, distribute\nand/or modify the software.\n\n  A secondary benefit of defending all users' freedom is that\nimprovements made in alternate versions of the program, if they\nreceive widespread use, become available for other developers to\nincorporate.  Many developers of free software are heartened and\nencouraged by the resulting cooperation.  However, in the case of\nsoftware used on network servers, this result may fail to come about.\nThe GNU General Public License permits making a modified version and\nletting the public access it on a server without ever releasing its\nsource code to the public.\n\n  The GNU Affero General Public License is designed specifically to\nensure that, in such cases, the modified source code becomes available\nto the community.  It requires the operator of a network server to\nprovide the source code of the modified version running there to the\nusers of that server.  Therefore, public use of a modified version, on\na publicly accessible server, gives the public access to the source\ncode of the modified version.\n\n  An older license, called the Affero General Public License and\npublished by Affero, was designed to accomplish similar goals.  This is\na different license, not a version of the Affero GPL, but Affero has\nreleased a new version of the Affero GPL which permits relicensing under\nthis license.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU Affero General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Remote Network Interaction; Use with the GNU General Public License.\n\n  Notwithstanding any other provision of this License, if you modify the\nProgram, your modified version must prominently offer all users\ninteracting with it remotely through a computer network (if your version\nsupports such interaction) an opportunity to receive the Corresponding\nSource of your version by providing access to the Corresponding Source\nfrom a network server at no charge, through some standard or customary\nmeans of facilitating copying of software.  This Corresponding Source\nshall include the Corresponding Source for any work covered by version 3\nof the GNU General Public License that is incorporated pursuant to the\nfollowing paragraph.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the work with which it is combined will remain governed by version\n3 of the GNU General Public License.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU Affero General Public License from time to time.  Such new versions\nwill be similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU Affero General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU Affero General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU Affero General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If your software can interact with users remotely through a computer\nnetwork, you should also make sure that it provides a way for users to\nget its source.  For example, if your program is a web application, its\ninterface could display a \"Source\" link that leads users to an archive\nof the code.  There are many ways you could offer source, and different\nsolutions will be better for different programs; see section 13 for the\nspecific requirements.\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU AGPL, see\n<http://www.gnu.org/licenses/>."
  },
  {
    "path": "License.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n\n<html><head>\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n <title>GNU Affero General Public License - GNU Project - Free Software Foundation (FSF)</title>\n</head>\n <link rel=\"alternate\" type=\"application/rdf+xml\"\n       href=\"http://www.gnu.org/licenses/agpl-3.0.rdf\" />\n<body>\n<h3 style=\"text-align: center;\">GNU AFFERO GENERAL PUBLIC LICENSE</h3>\n<p style=\"text-align: center;\">Version 3, 19 November 2007</p>\n\n<p>Copyright &copy; 2007 Free Software Foundation,\nInc. &lt;<a href=\"http://fsf.org/\">http://fsf.org/</a>&gt;\n <br />\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.</p>\n\n<h3><a name=\"preamble\"></a>Preamble</h3>\n\n<p>The GNU Affero General Public License is a free, copyleft license\nfor software and other kinds of works, specifically designed to ensure\ncooperation with the community in the case of network server software.</p>\n\n<p>The licenses for most software and other practical works are\ndesigned to take away your freedom to share and change the works.  By\ncontrast, our General Public Licenses are intended to guarantee your\nfreedom to share and change all versions of a program--to make sure it\nremains free software for all its users.</p>\n\n<p>When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.</p>\n\n<p>Developers that use our General Public Licenses protect your rights\nwith two steps: (1) assert copyright on the software, and (2) offer\nyou this License which gives you legal permission to copy, distribute\nand/or modify the software.</p>\n\n<p>A secondary benefit of defending all users' freedom is that\nimprovements made in alternate versions of the program, if they\nreceive widespread use, become available for other developers to\nincorporate.  Many developers of free software are heartened and\nencouraged by the resulting cooperation.  However, in the case of\nsoftware used on network servers, this result may fail to come about.\nThe GNU General Public License permits making a modified version and\nletting the public access it on a server without ever releasing its\nsource code to the public.</p>\n\n<p>The GNU Affero General Public License is designed specifically to\nensure that, in such cases, the modified source code becomes available\nto the community.  It requires the operator of a network server to\nprovide the source code of the modified version running there to the\nusers of that server.  Therefore, public use of a modified version, on\na publicly accessible server, gives the public access to the source\ncode of the modified version.</p>\n\n<p>An older license, called the Affero General Public License and\npublished by Affero, was designed to accomplish similar goals.  This is\na different license, not a version of the Affero GPL, but Affero has\nreleased a new version of the Affero GPL which permits relicensing under\nthis license.</p>\n\n<p>The precise terms and conditions for copying, distribution and\nmodification follow.</p>\n\n<h3><a name=\"terms\"></a>TERMS AND CONDITIONS</h3>\n\n<h4><a name=\"section0\"></a>0. Definitions.</h4>\n\n<p>&quot;This License&quot; refers to version 3 of the GNU Affero General Public\nLicense.</p>\n\n<p>&quot;Copyright&quot; also means copyright-like laws that apply to other kinds\nof works, such as semiconductor masks.</p>\n\n<p>&quot;The Program&quot; refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as &quot;you&quot;.  &quot;Licensees&quot; and\n&quot;recipients&quot; may be individuals or organizations.</p>\n\n<p>To &quot;modify&quot; a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a &quot;modified version&quot; of the\nearlier work or a work &quot;based on&quot; the earlier work.</p>\n\n<p>A &quot;covered work&quot; means either the unmodified Program or a work based\non the Program.</p>\n\n<p>To &quot;propagate&quot; a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.</p>\n\n<p>To &quot;convey&quot; a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.</p>\n\n<p>An interactive user interface displays &quot;Appropriate Legal Notices&quot;\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.</p>\n\n<h4><a name=\"section1\"></a>1. Source Code.</h4>\n\n<p>The &quot;source code&quot; for a work means the preferred form of the work\nfor making modifications to it.  &quot;Object code&quot; means any non-source\nform of a work.</p>\n\n<p>A &quot;Standard Interface&quot; means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.</p>\n\n<p>The &quot;System Libraries&quot; of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n&quot;Major Component&quot;, in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.</p>\n\n<p>The &quot;Corresponding Source&quot; for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.</p>\n\n<p>The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.</p>\n\n<p>The Corresponding Source for a work in source code form is that\nsame work.</p>\n\n<h4><a name=\"section2\"></a>2. Basic Permissions.</h4>\n\n<p>All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.</p>\n\n<p>You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.</p>\n\n<p>Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.</p>\n\n<h4><a name=\"section3\"></a>3. Protecting Users' Legal Rights From Anti-Circumvention Law.</h4>\n\n<p>No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.</p>\n\n<p>When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.</p>\n\n<h4><a name=\"section4\"></a>4. Conveying Verbatim Copies.</h4>\n\n<p>You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.</p>\n\n<p>You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.</p>\n\n<h4><a name=\"section5\"></a>5. Conveying Modified Source Versions.</h4>\n\n<p>You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:</p>\n\n<ul>\n\n<li>a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.</li>\n\n<li>b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    &quot;keep intact all notices&quot;.</li>\n\n<li>c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.</li>\n\n<li>d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.</li>\n\n</ul>\n\n<p>A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n&quot;aggregate&quot; if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.</p>\n\n<h4><a name=\"section6\"></a>6. Conveying Non-Source Forms.</h4>\n\n<p>You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:</p>\n\n<ul>\n\n<li>a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.</li>\n\n<li>b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.</li>\n\n<li>c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.</li>\n\n<li>d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.</li>\n\n<li>e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.</li>\n\n</ul>\n\n<p>A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.</p>\n\n<p>A &quot;User Product&quot; is either (1) a &quot;consumer product&quot;, which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, &quot;normally used&quot; refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.</p>\n\n<p>&quot;Installation Information&quot; for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.</p>\n\n<p>If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).</p>\n\n<p>The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.</p>\n\n<p>Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.</p>\n\n<h4><a name=\"section7\"></a>7. Additional Terms.</h4>\n\n<p>&quot;Additional permissions&quot; are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.</p>\n\n<p>When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.</p>\n\n<p>Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:</p>\n\n<ul>\n\n<li>a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or</li>\n\n<li>b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or</li>\n\n<li>c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or</li>\n\n<li>d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or</li>\n\n<li>e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or</li>\n\n<li>f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.</li>\n\n</ul>\n\n<p>All other non-permissive additional terms are considered &quot;further\nrestrictions&quot; within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further restriction,\nyou may remove that term.  If a license document contains a further\nrestriction but permits relicensing or conveying under this License, you\nmay add to a covered work material governed by the terms of that license\ndocument, provided that the further restriction does not survive such\nrelicensing or conveying.</p>\n\n<p>If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.</p>\n\n<p>Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.</p>\n\n<h4><a name=\"section8\"></a>8. Termination.</h4>\n\n<p>You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).</p>\n\n<p>However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.</p>\n\n<p>Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.</p>\n\n<p>Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.</p>\n\n<h4><a name=\"section9\"></a>9. Acceptance Not Required for Having Copies.</h4>\n\n<p>You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.</p>\n\n<h4><a name=\"section10\"></a>10. Automatic Licensing of Downstream Recipients.</h4>\n\n<p>Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.</p>\n\n<p>An &quot;entity transaction&quot; is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.</p>\n\n<p>You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.</p>\n\n<h4><a name=\"section11\"></a>11. Patents.</h4>\n\n<p>A &quot;contributor&quot; is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's &quot;contributor version&quot;.</p>\n\n<p>A contributor's &quot;essential patent claims&quot; are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, &quot;control&quot; includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.</p>\n\n<p>Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.</p>\n\n<p>In the following three paragraphs, a &quot;patent license&quot; is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To &quot;grant&quot; such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.</p>\n\n<p>If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  &quot;Knowingly relying&quot; means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.</p>\n\n<p>If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.</p>\n\n<p>A patent license is &quot;discriminatory&quot; if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.</p>\n\n<p>Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.</p>\n\n<h4><a name=\"section12\"></a>12. No Surrender of Others' Freedom.</h4>\n\n<p>If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.</p>\n\n<h4><a name=\"section13\"></a>13. Remote Network Interaction; Use with the GNU General Public License.</h4>\n\n<p>Notwithstanding any other provision of this License, if you modify the\nProgram, your modified version must prominently offer all users\ninteracting with it remotely through a computer network (if your version\nsupports such interaction) an opportunity to receive the Corresponding\nSource of your version by providing access to the Corresponding Source\nfrom a network server at no charge, through some standard or customary\nmeans of facilitating copying of software.  This Corresponding Source\nshall include the Corresponding Source for any work covered by version 3\nof the GNU General Public License that is incorporated pursuant to the\nfollowing paragraph.</p>\n\n<p>Notwithstanding any other provision of this License, you have permission\nto link or combine any covered work with a work licensed under version 3\nof the GNU General Public License into a single combined work, and to\nconvey the resulting work.  The terms of this License will continue to\napply to the part which is the covered work, but the work with which it is\ncombined will remain governed by version 3 of the GNU General Public\nLicense.</p>\n\n<h4><a name=\"section14\"></a>14. Revised Versions of this License.</h4>\n\n<p>The Free Software Foundation may publish revised and/or new versions of\nthe GNU Affero General Public License from time to time.  Such new\nversions will be similar in spirit to the present version, but may differ\nin detail to address new problems or concerns.</p>\n\n<p>Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU Affero\nGeneral Public License &quot;or any later version&quot; applies to it, you have\nthe option of following the terms and conditions either of that\nnumbered version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number\nof the GNU Affero General Public License, you may choose any version\never published by the Free Software Foundation.</p>\n\n<p>If the Program specifies that a proxy can decide which future\nversions of the GNU Affero General Public License can be used, that\nproxy's public statement of acceptance of a version permanently\nauthorizes you to choose that version for the Program.</p>\n\n<p>Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.</p>\n\n<h4><a name=\"section15\"></a>15. Disclaimer of Warranty.</h4>\n\n<p>THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM &quot;AS IS&quot; WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.</p>\n\n<h4><a name=\"section16\"></a>16. Limitation of Liability.</h4>\n\n<p>IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.</p>\n\n<h4><a name=\"section17\"></a>17. Interpretation of Sections 15 and 16.</h4>\n\n<p>If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.</p>\n\n</body></html>\n"
  },
  {
    "path": "README.md",
    "content": "> # NOTE: Tigase has new home at [tigase.dev](https://tigase.dev/) - please submit all issues/pull-requests there in the relevant sub-project!\n\n<p align=\"center\">\n  <a href=\"https://tigase.net/\">\n    <img\n      alt=\"Highly optimized, extremely modular and very flexible XMPP/Jabber server\"\n      src=\"https://github.com/tigase/website-assets/blob/master/tigase/images/tigase-logo.png?raw=true\"\n      width=\"300\"\n    />\n  </a>\n</p>\n\n<p align=\"center\">\n  Highly optimized, extremely modular and very flexible XMPP/Jabber server\n</p>\n\n<p align=\"center\">\n  <img alt=\"Tigase Tigase Logo\" src=\"https://github.com/tigase/website-assets/blob/master/tigase/images/tigase-logo.png?raw=true\" width=\"25\"/>\n  <a href='https://compliance.conversations.im/server/tigase.im'><img src='https://compliance.conversations.im/badge/tigase.im'></a> \n  <a href='https://xmpp.net/result.php?domain=tigase.im&amp;type=client'><img src='https://xmpp.net/badge.php?domain=tigase.im' alt='xmpp.net score' /></a>\n  <img src='https://img.shields.io/github/downloads/tigase/tigase-server/total' alt='GitHub All Releases' />\n</p>\n\n# What it is\n\nTigase XMPP Server is highly optimized, extremely modular and very flexible XMPP/Jabber server written in Java.\n\nThis repository contains source code of the main part of the Tigase XMPP Server.\n\n*The project exists since 2004 and we have recently moved it over to GitHub.*\n\nOther Tigase projects related to XMPP:\n\nTigase XMPP Server addons:\n* [MUC Component](https://github.com/tigase/tigase-muc) - Multi-User Chat: [XEP-0045](https://xmpp.org/extensions/xep-0045.html)\n* [PubSub Component](https://github.com/tigase/tigase-pubsub) - Publish-Subscribe: [XEP-0060](https://xmpp.org/extensions/xep-0060.html) and Personal Eventing Protocol: [XEP-0163](https://xmpp.org/extensions/xep-0163.html)\n* [Socks5 Proxy Component](https://github.com/tigase/tigase-socks5) - SOCKS5 Bytestreams: [XEP-0065](https://xmpp.org/extensions/xep-0065.html)\n* [STUN Component](https://github.com/tigase/tigase-stun) - [STUN](https://en.wikipedia.org/wiki/STUN) Component for Tigase\n* [HTTP API Component](https://github.com/tigase/tigase-http-api) - Component providing easy to use HTTP endpoints for server management and integration based on JDK built-in HTTP server.\n* [Jetty HTTP API Component](https://github.com/tigase/tigase-http-api-jetty) - High performance and high load component providing easy to use HTTP endpoints for server management and integration based on [Jetty HTTP Server](https://www.eclipse.org/jetty/).\n* [MongoDB Connector](https://github.com/tigase/tigase-mongodb) - Connector adding support for [MongoDB](https://www.mongodb.com) database to Tigase server.\n* [Message Archiving Component](https://github.com/tigase/tigase-message-archiving) - Component providing Message Archiving [XEP-0136](https://xmpp.org/extensions/xep-0136.html) and Message Archive Management [XEP-0313](https://xmpp.org/extensions/xep-0313.html) support.\n\nTools: \n* [Database Migrator Tool](https://github.com/tigase/tigase-database-migrator) - Tools helping with migration from other XMPP servers to Tigase based system.\n* [TTS-NG Test Suite](https://github.com/tigase/tigase-tts-ng) - Test Suite to run automated tests for the Tigase XMPP Server\n* [Tigase Monitor Console](https://github.com/tigase/tigase-monitor) - Stand-alone application for the Tigase XMPP Server monitoring and management console.\n* [Atom DSL Syntax](https://github.com/tigase/tigase-dsl-syntax-highlighter-for-atom) - [Atom](https://atom.io/) DSL syntex highlighter for Tigase XMPP Server configuration files.\n* [IntelliJ IDEA DSL Syntax](https://github.com/tigase/tigase-dsl-syntax-highlighter-for-atom) - [IntelliJ IDEA IDE](https://www.jetbrains.com/idea/) DSL syntex highlighter for Tigase XMPP Server configuration files.\n\nTigase XMPP Clients:\n* [StorkIM Client](https://github.com/tigase/stork) - Android XMPP Client\n* [SiskinIM Client](https://github.com/tigase/siskin-im) - iOS XMPP Client\n* [BeagleIM Client](https://github.com/tigase/beagle-im) - MacOS XMPP Client\n* [Swift Library](https://github.com/tigase/tigase-swift) - Tigase Swift XMPP Library\n* [Swift OMEMO Plugin](https://github.com/tigase/tigase-swift-omemo) - OMEMO support for Tigase Swift XMPP library\n\nTigase based IoT:\n* [Tigase IoT Framework](https://github.com/tigase/tigase-iot-framework) - Easy to use IoT framework to communicate and control Iot devices over XMPP\n* [Tigase IoT Framework - Examples](https://github.com/tigase/tigase-iot-framework-examples) - Examples on how to extend the Tigase IoT Framework with support for different devices\n* [Tigase RPi Library](https://github.com/tigase/tigase-rpi) -  Java low-level library to control sensors and devices connected to RasperryPi.\n\n# Features\n\nTigase XMPP Server contains full support for [RFC 6120 - XMPP CORE](http://xmpp.org/rfcs/rfc6120.html), [RFC 6121 - XMPP IM](http://xmpp.org/rfcs/rfc6120.html) and [RFC 7395 - XMPP over WebSockets](https://tools.ietf.org/html/rfc7395) making it accessible using XMPP client connections:\n* over TCP\n* over HTTP/HTTPS (BOSH)\n* over WebSockets\n\nand over server-to-server connections as well as over XMPP component connections.\n\nAdditionally Tigase XMPP Server provides HTTP API for integration with other services unable to communicate over XMPP.\n\nMoveover, Tigase XMPP Server comes with support for Push Notifications making it possible to push notification to mobile devices.\n\nFollowing features are supported by Tigase XMPP Server:\n* [XEP-0016: Flexible Offline Message Retrieval](http://xmpp.org/extensions/xep-0016.html)\n* [XEP-0030: Service Discovery](http://xmpp.org/extensions/xep-0030.html)\n* [XEP-0045: Multi User Chat](http://xmpp.org/extensions/xep-0045.html)\n* [XEP-0060: Publish-Subscribe](http://xmpp.org/extensions/xep-0060.html)\n* [XEP-0079: Advanced Message Processing](http://xmpp.org/extensions/xep-0079.html)\n* [XEP-0114: Jabber Component Protocol](http://xmpp.org/extensions/xep-0114.html)\n* [XEP-0115: Entity Capabilities](http://xmpp.org/extensions/xep-0115.html)\n* [XEP-0133: Service Administration](http://xmpp.org/extensions/xep-0133.html)\n* [XEP-0136: Message Archiving](http://xmpp.org/extensions/xep-0136.html)\n* [XEP-0163: Personal Eventing Protocol](http://xmpp.org/extensions/xep-0163.html)\n* [XEP-0198: Stream Management](http://xmpp.org/extensions/xep-0198.html)\n* [XEP-0199: XMPP Ping](http://xmpp.org/extensions/xep-0199.html)\n* [XEP-0206: XMPP over BOSH](http://xmpp.org/extensions/xep-0206.html)\n* [XEP-0225: Component Connections](http://xmpp.org/extensions/xep-0225.html)\n* [XEP-0237: Roster Versioning](http://xmpp.org/extensions/xep-0237.html)\n* [XEP-0280: Message Carbons](http://xmpp.org/extensions/xep-0280.html)\n* [XEP-0313: Message Archive Management](http://xmpp.org/extensions/xep-0313.html)\n* [XEP-0357: Push Notifications](http://xmpp.org/extensions/xep-0357.html)\n* [XEP-0363: HTTP File Upload](http://xmpp.org/extensions/xep-0363.html)\n* and many more...\n\n# Support\n\nWhen looking for support, please first search for answers to your question in the available online channels:\n\n* Our online documentation: [Tigase Docs](https://docs.tigase.net)\n* Existing issues in relevant project, for Tigase Server it's: [Tigase XMPP Server GitHub issues](https://github.com/tigase/tigase-server/issues)\n\nIf you didn't find an answer in the resources above, feel free to submit your question as [new issue on GitHub](https://github.com/tigase/tigase-server/issues/new/choose) or, if you have valid support subscription, open [new support ticket](https://tigase.net/technical-support).\n\n# Downloads\n\nYou can download distribution version of the Tigase XMPP Server directly from [here](https://github.com/tigase/tigase-server/releases).\n\nIf you wish to downloand SNAPSHOT build of the development version of Tigase XMPP Server you can grab it from [here](https://build.tigase.net/nightlies/dists/latest/tigase-server-dist-max.zip).\n\n# Installation and usage\n\nDocumentation of the project is part of the Tigase XMPP Server distribution package. Quickstart guide is also available [here](https://docs.tigase.net/en/latest/Tigase_Administration/Quick_Start_Guide/Intro.html).\n\n# Compilation \n\nCompilation of the project is very easy as it is typical Maven project. All you need to do is to execute\n````bash\nmvn package test\n````\nto compile the project and run unit tests.\n\n# License\n\n<img alt=\"Tigase Tigase Logo\" src=\"https://github.com/tigase/website-assets/blob/master/tigase/images/tigase-logo.png?raw=true\" width=\"25\"/> Official <a href=\"https://tigase.net/\">Tigase</a> repository is available at: https://github.com/tigase/tigase-server/.\n\nCopyright (c) 2004 Tigase, Inc.\n\nLicensed under AGPL License Version 3. Other licensing options available upon request.\n"
  },
  {
    "path": "etc/bosh-extra-headers.txt",
    "content": "Access-Control-Allow-Origin: *\nAccess-Control-Allow-Methods: GET, POST, OPTIONS\nAccess-Control-Allow-Headers: Content-Type\nAccess-Control-Max-Age: 86400\n"
  },
  {
    "path": "etc/client-access-policy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n\n    Tigase XMPP Server - The instant messaging server\n    Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published by\n    the Free Software Foundation, version 3 of the License.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program. Look for COPYING file in the top folder.\n    If not, see http://www.gnu.org/licenses/.\n\n-->\n<access-policy>\n    <cross-domain-access>\n        <policy>\n            <allow-from http-request-headers=\"*\">\n                <domain uri=\"*\"/>\n            </allow-from>\n            <grant-to>\n                <resource path=\"/\" include-subpaths=\"true\"/>\n            </grant-to>\n        </policy>\n    </cross-domain-access>\n</access-policy>"
  },
  {
    "path": "etc/config.properties",
    "content": "# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#   http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n\n#\n# Framework config properties.\n#\n\n# To override the packages the framework exports by default from the\n# class path, set this variable.\n#org.osgi.framework.system.packages=\n\n# To append packages to the default set of exported system packages,\n# set this value.\n#org.osgi.framework.system.packages.extra=javax.*\n\n# The following property makes specified packages from the class path\n# available to all bundles. You should avoid using this property.\norg.osgi.framework.bootdelegation=sun.*,com.sun.*,org.xml.sax.*\n\n# Felix tries to guess when to implicitly boot delegate in certain\n# situations to ease integration without outside code. This feature\n# is enabled by default, uncomment the following line to disable it.\n#felix.bootdelegation.implicit=false\n\n# The following property explicitly specifies the location of the bundle\n# cache, which defaults to \"felix-cache\" in the current working directory.\n# If this value is not absolute, then the felix.cache.rootdir controls\n# how the absolute location is calculated. (See next property)\n#org.osgi.framework.storage=${dollar}{felix.cache.rootdir}/felix-cache\n\n# The following property is used to convert a relative bundle cache\n# location into an absolute one by specifying the root to prepend to\n# the relative cache path. The default for this property is the\n# current working directory.\n#felix.cache.rootdir=${dollar}{user.dir}\n\n# The following property controls whether the bundle cache is flushed\n# the first time the framework is initialized. Possible values are\n# \"none\" and \"onFirstInit\"; the default is \"none\".\norg.osgi.framework.storage.clean=onFirstInit\n\n# The following property determines which actions are performed when\n# processing the auto-deploy directory. It is a comma-delimited list of\n# the following values: 'install', 'start', 'update', and 'uninstall'.\n# An undefined or blank value is equivalent to disabling auto-deploy\n# processing.\nfelix.auto.deploy.action=install,start\n\n# The following property specifies the directory to use as the bundle\n# auto-deploy directory; the default is 'bundle' in the working directory.\nfelix.auto.deploy.dir=jars\n\n# The following property is a space-delimited list of bundle URLs\n# to install when the framework starts. The ending numerical component\n# is the target start level. Any number of these properties may be\n# specified for different start levels.\n#felix.auto.install.1=\n\n# The following property is a space-delimited list of bundle URLs\n# to install and start when the framework starts. The ending numerical\n# component is the target start level. Any number of these properties\n# may be specified for different start levels.\n#felix.auto.start.1=\n\nfelix.log.level=1\n\n# Sets the initial start level of the framework upon startup.\n#org.osgi.framework.startlevel.beginning=1\n\n# Sets the start level of newly installed bundles.\n#felix.startlevel.bundle=1\n\n# Felix installs a stream and content handler factories by default,\n# uncomment the following line to not install them.\n#felix.service.urlhandlers=false\n\n# The launcher registers a shutdown hook to cleanly stop the framework\n# by default, uncomment the following line to disable it.\nfelix.shutdown.hook=true\n\n#\n# Bundle config properties.\n#\n\nosgi.shell.telnet.ip=127.0.0.1\nosgi.shell.telnet.port=9070\nosgi.shell.telnet.maxconn=2\nosgi.shell.telnet.socketTimeout=0\n\n# debugging\n#felix.log.level=4\n#org.apache.felix.http.debug=true"
  },
  {
    "path": "etc/config.tdsl",
    "content": "'config-type' = 'setup'\n\nhttp () {\n    setup () {\n        'admin-user' = 'admin'\n        'admin-password' = 'tigase'\n    }\n}"
  },
  {
    "path": "etc/cross-domain-policy.xml",
    "content": "<!--\n\n    Tigase XMPP Server - The instant messaging server\n    Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published by\n    the Free Software Foundation, version 3 of the License.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program. Look for COPYING file in the top folder.\n    If not, see http://www.gnu.org/licenses/.\n\n-->\n<!DOCTYPE cross-domain-policy SYSTEM \"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd\">\n<cross-domain-policy>\n    <allow-access-from domain=\"*\" to-ports=\"80,8080,5222,5223\"/>\n    <site-control permitted-cross-domain-policies=\"all\"/>\n</cross-domain-policy>\n"
  },
  {
    "path": "etc/init-debian.properties",
    "content": "config-type = --gen-config-def\n--admins = admin@$HOST_NAME\n--virt-hosts = $HOST_NAME\n--debug=server\n--user-db-uri = jdbc:derby:/var/lib/tigase/tigase-derbydb;create=true;logDevice=/var/log/tigase\nbasic-conf/logging/java.util.logging.FileHandler.pattern = /var/log/tigase/tigase.log\nvhost-man/vhost-repo-uri = jdbc:derby:/var/lib/tigase/tigase-derbydb;create=true;logDevice=/var/log/tigase\n"
  },
  {
    "path": "etc/init-mysql.properties",
    "content": "# Load standard set of the server components.\n# Look at the http://www.tigase.org/configuration-wizards\n# document for other possible values. Normally you don't\n# need to change this line.\nconfig-type=--gen-config-def\n# List of administrator accounts, please replace them with\n# administrator accounts in your installation\n--admins=admin@devel.tigase.org,admin@test-d\n# The line says that the database used by the Tigase server is 'mysql'\n# Look at the configuration wizards article for different options\n# You can also put here a Java class name if you have a custom\n# implementation for a database connector.\n--user-db=mysql\n# The line contains the database connection string. This is database\n# specific string and for each kind of database it may look differently.\n# Below string is for MySQL database. Please modify it for your system.\n# MySQL connector requires connection string in the following format:\n# jdbc:mysql://[hostname]/[database name]?user=[user name]&password=[user password]\n--user-db-uri=jdbc:mysql://localhost/tigasedb?user=tigase_user&password=mypass\n# Virtual domains for your server installation, comma separated list of vhosts\n--virt-hosts=devel.tigase.org,test-d\n# Select what packages you want to have logging switched for\n# The below setting is recommended for the initail setup and it is required\n# when asking for help with setting the server up\n--debug=server\n# Activate HTTP API component for web based configuration and installation\n--comp-name-1=http\n--comp-class-1=tigase.http.HttpMessageReceiver\n"
  },
  {
    "path": "etc/jmx.access",
    "content": "######################################################################\n#     Default Access Control File for Remote JMX(TM) Monitoring\n######################################################################\n#\n# Access control file for Remote JMX API access to monitoring.\n# This file defines the allowed access for different roles.  The\n# password file (jmxremote.password by default) defines the roles and their\n# passwords.  To be functional, a role must have an entry in\n# both the password and the access files.\n#\n# Default location of this file is $JRE/lib/management/jmxremote.access\n# You can specify an alternate location by specifying a property in \n# the management config file $JRE/lib/management/management.properties\n# (See that file for details)\n#\n# The file format for password and access files is syntactically the same\n# as the Properties file format.  The syntax is described in the Javadoc\n# for java.util.Properties.load.\n# Typical access file has multiple  lines, where each line is blank,\n# a comment (like this one), or an access control entry.\n#\n# An access control entry consists of a role name, and an\n# associated access level.  The role name is any string that does not\n# itself contain spaces or tabs.  It corresponds to an entry in the\n# password file (jmxremote.password).  The access level is one of the\n# following:\n#       \"readonly\" grants access to read attributes of MBeans.\n#                   For monitoring, this means that a remote client in this\n#                   role can read measurements but cannot perform any action\n#                   that changes the environment of the running program.\n#       \"readwrite\" grants access to read and write attributes of MBeans,\n#                   to invoke operations on them, and to create or remove them.\n#\t\t    This access should be granted to only trusted clients, \n#                   since they can potentially interfere with the smooth\n#\t\t    operation of a running program \n#\n# A given role should have at most one entry in this file.  If a role\n# has no entry, it has no access.\n# If multiple entries are found for the same role name, then the last\n# access entry is used.\n#\n#\n# Default access control entries:\n# o The \"monitorRole\" role has readonly access.  \n# o The \"controlRole\" role has readwrite access.\n\nmonitor readonly\nadmin readwrite\n"
  },
  {
    "path": "etc/jmx.password",
    "content": "# ----------------------------------------------------------------------\n#           Template for jmxremote.password\n#\n# o Copy this template to jmxremote.password\n# o Set the user/password entries in jmxremote.password\n# o Change the permission of jmxremote.password to read-only\n#   by the owner.\n#\n# See below for the location of jmxremote.password file.\n# ----------------------------------------------------------------------\n\n##############################################################\n#        Password File for Remote JMX Monitoring\n##############################################################\n#\n# Password file for Remote JMX API access to monitoring.  This\n# file defines the different roles and their passwords.  The access\n# control file (jmxremote.access by default) defines the allowed\n# access for each role.  To be functional, a role must have an entry\n# in both the password and the access files.\n#\n# Default location of this file is $JRE/lib/management/jmxremote.password\n# You can specify an alternate location by specifying a property in \n# the management config file $JRE/lib/management/management.properties\n# or by specifying a system property (See that file for details).\n\n\n##############################################################\n#    File permissions of the jmxremote.password file\n##############################################################\n#      Since there are cleartext passwords stored in this file,\n#      this file must be readable by ONLY the owner,\n#      otherwise the program will exit with an error. \n#\n# The file format for password and access files is syntactically the same\n# as the Properties file format.  The syntax is described in the Javadoc\n# for java.util.Properties.load.\n# Typical password file has multiple  lines, where each line is blank,\n# a comment (like this one), or a password entry.\n#\n#\n# A password entry consists of a role name and an associated\n# password.  The role name is any string that does not itself contain\n# spaces or tabs.  The password is again any string that does not\n# contain spaces or tabs.  Note that passwords appear in the clear in\n# this file, so it is a good idea not to use valuable passwords.\n#\n# A given role should have at most one entry in this file.  If a role\n# has no entry, it has no access.\n# If multiple entries are found for the same role name, then the last one\n# is used.\n#\n# In a typical installation, this file can be read by anybody on the\n# local machine, and possibly by people on other machines.\n# For # security, you should either restrict the access to this file,\n# or specify another, less accessible file in the management config file\n# as described above.\n#\n# Following are two commented-out entries.  The \"measureRole\" role has\n# password \"QED\".  The \"controlRole\" role has password \"R&D\".\n#\n# monitorRole  QED\n# controlRole   R&D\n\nadmin admin_pass\nmonitor monitor_pass"
  },
  {
    "path": "etc/logback.xml",
    "content": "<!--\n\n    Tigase XMPP Server - The instant messaging server\n    Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published by\n    the Free Software Foundation, version 3 of the License.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program. Look for COPYING file in the top folder.\n    If not, see http://www.gnu.org/licenses/.\n\n-->\n\n<!--\nTo enable logback for Tigase packages\n1) uncomment LevelChangePropagator context listener\n2) add jul-to-slf4j ('org.slf4j.bridge.SLF4JBridgeHandler') to the list of handlers:\n```\nlogging () {\n    rootHandlers = [ 'java.util.logging.ConsoleHandler', 'java.util.logging.FileHandler', 'org.slf4j.bridge.SLF4JBridgeHandler' ]\n}\n```\n-->\n<configuration scan=\"true\"  scanPeriod=\"120 seconds\">\n<!--    <contextListener class=\"ch.qos.logback.classic.jul.LevelChangePropagator\"/>-->\n\n<!--    <shutdownHook/>-->\n\n    <appender name=\"FILE\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <!-- daily rollover, files will be compressed -->\n        <file>logs/${HOSTNAME}/tigase.logback.log</file>\n\n        <!-- set immediateFlush to true to make sure everything is logged and\n        to false for much higher logging throughput -->\n        <immediateFlush>false</immediateFlush>\n\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <!-- automatically compress rolled files -->\n            <fileNamePattern>logs/${HOSTNAME}/tigase.logback.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>\n\n            <!-- keep 7 days' worth of history capped at 1GB total size and 250M per-file -->\n            <maxFileSize>250MB</maxFileSize>\n            <maxHistory>7</maxHistory>\n            <totalSizeCap>1GB</totalSizeCap>\n        </rollingPolicy>\n\n        <append>true</append>\n        <encoder class=\"ch.qos.logback.classic.encoder.PatternLayoutEncoder\">\n            <pattern>%-125(%yellow([%d{yyyy-MM-dd HH:mm:ss.SSS}]) [%highlight(%-5level)] [%20(%thread)] %blue(%logger{5}.%method\\(\\))): %msg %n</pattern>\n        </encoder>\n    </appender>\n\n    <!-- Offers higher throughput but Method name won't be available in logs, enabled in appender-ref -->\n    <!--<appender name=\"ASYNC\" class=\"ch.qos.logback.classic.AsyncAppender\">-->\n    <!--    <appender-ref ref=\"FILE\" />-->\n    <!--    <queueSize>1024</queueSize>-->\n    <!--</appender>-->\n\n    <!-- possible levels (TRACE, DEBUG, INFO, WARN and ERROR) -->\n    <!-- Mappings to JUL levels are described in http://www.slf4j.org/api/org/slf4j/bridge/SLF4JBridgeHandler.html  -->\n    <logger name=\"tigase.server\" level=\"DEBUG\"/>\n    <logger name=\"tigase.xmpp.impl\" level=\"TRACE\"/>\n<!--    <logger name=\"tigase.io\" level=\"TRACE\"/>-->\n<!--    <logger name=\"tigase.net\" level=\"TRACE\"/>-->\n<!--    <logger name=\"tigase.xmpp\" level=\"DEBUG\"/>-->\n\n    <!--jetty / http-api configuration-->\n    <logger name=\"org.eclipse.jetty\" level=\"warn\"/>\n\n    <root level=\"INFO\">\n        <!-- ASYNC appender offers higher throughput but Method name won't be available in logs -->\n        <appender-ref ref=\"FILE\"/>\n    </root>\n</configuration>\n\n"
  },
  {
    "path": "etc/snmp.acl",
    "content": "# ----------------------------------------------------------------------\n#           Template for SNMP Access Control List File\n#\n# o Copy this template to snmp.acl\n# o Set access control for SNMP support\n# o Change the permission of snmp.acl to be read-only\n#   by the owner.\n#\n# See below for the location of snmp.acl file.\n# ----------------------------------------------------------------------\n\n############################################################\n#            SNMP Access Control List File  \n############################################################\n#\n# Default location of this file is $JRE/lib/management/snmp.acl.\n# You can specify an alternate location by specifying a property in \n# the management config file $JRE/lib/management/management.properties\n# or by specifying a system property (See that file for details).\n#\n\n\n##############################################################\n#        File permissions of the snmp.acl file\n##############################################################\n# \n#      Since there are cleartext community strings stored in this file,\n#      this ACL file must be readable by ONLY the owner,\n#      otherwise the program will exit with an error. \n#\n##############################################################\n#\t\tFormat of the acl group\n##############################################################\n#\n# communities: a list of SNMP community strings to which the\n#              access control applies separated by commas.\n#\n# access: either \"read-only\" or \"read-write\".\n#\n# managers: a list of hosts to be granted the access rights.\n#    Each can be expressed as any one of the following:\n#    - hostname: hubble\n#    - ip v4 and v6 addresses: 123.456.789.12 , fe80::a00:20ff:fe9b:ea82\n#    - ip v4 and v6 netmask prefix notation: 123.456.789.0/24, \n#         fe80::a00:20ff:fe9b:ea82/64  \n#      see RFC 2373 (http://www.ietf.org/rfc/rfc2373.txt)\n#\n# An example of two community groups for multiple hosts:\n#    acl = {\n#     {\n#       communities = public, private\n#       access = read-only\n#       managers = hubble, snowbell, nanak\n#     }\n#     {\n#       communities = jerry\n#       access = read-write\n#       managers = hubble, telescope\n#     }\n#    }\n# \n##############################################################\n#                   Format of the trap group\n##############################################################\n#\n# trap-community: a single SNMP community string that will be included\n#                 in  the traps sent to the hosts.\n#\n# hosts: a list of hosts to which the SNMP agent will send traps.\n#\n# An example of two trap community definitions for multiple hosts:\n#    trap = {\n#      {\n#        trap-community = public\n#        hosts = hubble, snowbell\n#      }\n#      {\n#        trap-community = private\n#        hosts = telescope\n#      }\n#    }\n#\n############################################################\n#\n#  Update the community strings (public and private) below\n#  before copying this template file\n# \t\n# Common SNMP ACL Example\n# ------------------------\n#\n# o Only localhost can connect, and access rights\n#   are limited to read-only\n# o Traps are sent to localhost only\n#\n#\nacl = {\n  {\n    communities = public, private\n    access = read-write\n    managers = localhost, devel.tigase.org\n  }\n}\n# \n# \n# trap = {\n#   {\n#     trap-community = public\n#     hosts = localhost \n#   }\n# }\n"
  },
  {
    "path": "etc/tigase.conf",
    "content": "#osgiEnabled=(true|false)\n#osgiEnabled=false\nOSGI=${osgiEnabled}\nENC=\"-Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8\"\n#GC=\" -XX:NewRatio=2 -XX:-ReduceInitialCardMarks \"\n#EX=\"-XX:+OptimizeStringConcat -XX:+DoEscapeAnalysis -XX:+UseNUMA -XX:+UseCompressedOops -XX:+ShowCodeDetailsInExceptionMessages -XX:+UseCompactObjectHeaders \"\n\n#FULL_STACKTRACES=\" -XX:-OmitStackTraceInFastThrow \"\n\n#REMOTE_DEBUG=\" -agentlib:jdwp=transport=dt_socket,server=y,address=8000,suspend=n \"\n#GC_DEBUG=\" -XX:+PrintTenuringDistribution -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -Xloggc:logs/jvm.log -verbose:gc \"\n#JVM_DEBUG=\" -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/tigase/heapdump.bin \"\n#JVM_RESTART_ON_OOM=\" -XX:+CrashOnOutOfMemoryError \" #it may be preferred to restart JVM when OOM happens to restore service to operational state\n\n#TLS_DEBUG=\" -Djavax.net.debug=ssl:handshake:session:defaultctx \"\n\n## Note:Enabling NMT causes a 5% -10% performance overhead!\n#JVM_MEMORY=\" -XX:+UnlockDiagnosticVMOptions -XX:NativeMemoryTracking=summary -XX:+PrintNMTStatistics \"\n\n#JMX_REMOTE_IP=\"-Djava.rmi.server.hostname=0.0.0.0\"\n\n## AWS hostname resolver\n#export INTERNAL_IP=\"$(curl -s http://169.254.169.254/latest/meta-data/local-hostname)\"\n#export EXTERNAL_IP=\"$(curl -s http://169.254.169.254/latest/meta-data/public-hostname)\"\n\n#JAVA_HOME=\"${JDKPath}\"\nCLASSPATH=\"\"\n\n#DISABLE_LOGGER_COLOR=\" -Ddisable_logger_color=true \"\n\n#### Variables used to specify hostname and port of the database instance; can be used in conjunction\n#### with scripts/wait-for-it.sh script, that checks if the given coordinates are ready\n#DB_HOST=\n#DB_PORT=\n\n## Possible memory allocation improvements on some CentOS/RHEL systems\n## https://www.ibm.com/developerworks/community/blogs/kevgrig/entry/linux_glibc_2_10_rhel_6_malloc_may_show_excessive_virtual_memory_usage?lang=en\n# export MALLOC_ARENA_MAX=4\n\n## You should explicitly set Young Generation size only if you know what\n## you are doing and only after running Load Tests that confirms the assumption!\n#JAVA_YOUNG_GEN_EXPLICIT_SIZE=\" -Xmn<young size>[g|m|k] \"\n\n## Heap memory settings should be adjusted on per deployment basis to utilize all resources!\n## When configuring consider total of: Xmx/HEAP + (Xss * number of threads)\n#PRODUCTION_HEAP_SETTINGS=\" -XX:MaxRAMPercentage=80 \" # dynamic sizing, *preferred* and more universal\n#PRODUCTION_HEAP_SETTINGS=\" -Xms5g -Xmx5g \" # setting fixed size of HEAP\n\n## Per-thread stack size on top of HEAP!\nJAVA_XSS=\" -Xss228k \"\n## Uncomment and adjust if needed (most likely only on servers with more than 64 cores)\n#JAVA_DIRECT_MEMORY=\" -XX:MaxDirectMemorySize=128m \"\nJAVA_METASPACE=\" -XX:MaxMetaspaceSize=160m \" # because of aws/s3\n\nJAVA_OPTIONS=\"${GC} ${GC_DEBUG} ${JVM_DEBUG} ${JVM_RESTART_ON_OOM} ${TLS_DEBUG} ${JVM_MEMORY} ${REMOTE_DEBUG} ${EX} ${ENC} ${FULL_STACKTRACES} ${JMX_REMOTE_IP} ${DISABLE_LOGGER_COLOR} -server ${PRODUCTION_HEAP_SETTINGS} ${JAVA_YOUNG_GEN_EXPLICIT_SIZE} ${JAVA_XSS} ${JAVA_DIRECT_MEMORY} ${JAVA_METASPACE} \"\nTIGASE_OPTIONS=\" \"\n"
  },
  {
    "path": "licenseheader.txt",
    "content": "<#if licenseFirst??>\n${licenseFirst}\n</#if>\n${licensePrefix}Tigase Jabber/XMPP Server\n${licensePrefix}Copyright (C) 2004-${date?date?string(\"yyyy\")}, ${project.organization} <office@tigase.com>\n${licensePrefix?replace(\" +$\", \"\", \"r\")}\n${licensePrefix}This program is free software: you can redistribute it and/or modify\n${licensePrefix}it under the terms of the GNU Affero General Public License as published by\n${licensePrefix}the Free Software Foundation, either version 3 of the License,\n${licensePrefix}or (at your option) any later version.\n${licensePrefix?replace(\" +$\", \"\", \"r\")}\n${licensePrefix}This program is distributed in the hope that it will be useful,\n${licensePrefix}but WITHOUT ANY WARRANTY; without even the implied warranty of\n${licensePrefix}MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n${licensePrefix}GNU Affero General Public License for more details.\n${licensePrefix?replace(\" +$\", \"\", \"r\")}\n${licensePrefix}You should have received a copy of the GNU Affero General Public License\n${licensePrefix}along with this program. Look for COPYING file in the top folder.\n${licensePrefix}If not, see http://www.gnu.org/licenses/.\n<#if licenseLast??>\n${licenseLast}\n</#if>\n"
  },
  {
    "path": "package.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n\n<html>\n\t<head>\n\t\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n\t\t<title>Tigase XMPP (Jabber) server ver ${APP_VER}</title>\n\t</head>\n\t<body bgcolor=\"white\">\n\t\t<h2>About</h2>\n\n\t\t<p>Copyright &copy; 2004-2016 Tigase.org. &lt;http://www.tigase.org/&gt;</p>\n\n\t\t<p>The <strong>Tigase Jabber/XMPP Server</strong> is <strong>Open Source and Free (AGPLv3)</strong> <a href=\"https://www.oracle.com/java/index.html\">Java</a> based server.</p>\n\n\t\t<p>The server offers complete implementation of the XMPP protocol with a long list\n\t\tof extensions. Effcient, reliable and very extensible can be easily integrated\n\t\tit with your systems.</p>\n\n\t\t<p>The unique features of the Tigase server are</p>\n\n\t\t<ol>\n\t\t\t<li>High performance and scalability. Was tested with up to 500 000 concurrent\n\t\t\t users connected to a single machine and in cluster with over 1mln online users.</li>\n\t\t\t<li>High reliability. Tries to run as long as possible and tried to automaticaly\n\t\t\t recover from detected problems.</li>\n\t\t\t<li>Built-in many self monitoring functions. You can check your systems statistics\n\t\t\t via XMPP, JMX, HTTP, SNMP or you can automatically receive notifications about\n\t\t\t possible problems.</li>\n\t\t\t<li>Scripting support - scripts can be loaded/reloaded at run time.\n\t\t\t Many scripting languages are supported</li>\n\t\t\t<li>Virtual hosts support. You can have virtually unlimited virtual hosts which\n\t\t\t can be added/removed at runtime. You can temporarily block vhost or limit\n\t\t\t number of users per vhost.</li>\n\t\t\t<li>There is much more... check the official change log and the project website.</li>\n\t\t</ol>\n\n\t\t<p>Contributions to Tigase have been made by the following people:</p>\n\t\t<ul>\n\t\t\t<li><b>Tigase Team</li></b>\n\t\t\t<li>Eric Dziwea</li>\n\t\t\t<li>Artur Hefczyc (kobit)</li>\n\t\t\t<li>Wojciech Kapcia (wojtek)</li>\n\t\t\t<li>Bartosz Małkowski (bmalkow)</li>\n\t\t\t<li>Daniel Wisnewski</li>\n\t\t\t<li>Andrzej Wojcik</li>\n\t\t\t <br>\n\t\t\t<li><b>Other Contributors</li></b>\n\t\t\t<li>Mateusz Fiołka</li>\n\t\t\t<li>Tomasz Sterna (smoku)</li>\n\t\t\t<li>Daniele Ricci</li>\n\t\t\t<li>... and many others.</li>\n\t\t</ul>\n\n\t\t<h2>Installtion, configuration and compilation</h2>\n\n\t\t<p>The most recent documentation on all these topics is always available in the\n\t\tproject documentation website: <a href=\"http://docs.tigase.org/\">www.tigase.org</a>. Please\n\t\trefer to the website for all the details and always up to date guides.</p>\n\n\t\t<p>You would probably want to start with Quick Start:\n\t\t<a href=\"http://docs.tigase.org/tigase-server/snapshot/Administration_Guide/html/#QuickStart\">Tigase Quick Start</a> documentation.</p>\n\n\t\t<p>It is recommended that if you run into any issues, visit <a href=\"https://projects.tigase.org/\">Tigase Projects</a> website to visit forums\n\t\tand view trouble tickets that may already have an answer to your question.  Different components may be in different sections of the projects website.\n\t\tPlease ask general questions in the forum section of each project.\n\t  For example, the Tigase XMPP Server forums are located <a href=\"https://projects.tigase.org/projects/tigase-server/boards\">here</a>.</p>\n\n\t\t<p>The website also contains lots of other useful information like load tests\n\t\tresults, user discussions and online support and help always available to\n\t\tyou.</p>\n\n\t\t<p>This is ${APP_VER} release of the server. Please include the exact version\n\t\tnumber in all correspondence regarding the server.</p>\n\n\n\t\t<img src=\"Tiger.image\"/>\t<img src=\"JavaPowered.image\"/>\t<img src=\"SolarisReady.image\"/>\n </body>\n</html>\n"
  },
  {
    "path": "pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n\n    Tigase XMPP Server - The instant messaging server\n    Copyright © 2004 Tigase, Inc. (office@tigase.com)\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published by\n    the Free Software Foundation, version 3 of the License.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program. Look for COPYING file in the top folder.\n    If not, see http://www.gnu.org/licenses/.\n\n-->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <parent>\n        <groupId>tigase</groupId>\n        <artifactId>tigase-projects-parent</artifactId>\n        <version>1.0.7-SNAPSHOT</version>\n    </parent>\n\n    <properties>\n        <tests.excludeGroups>tigase.tests.SlowTest</tests.excludeGroups>\n        <!--<tests.logger_properties>src/test/resources/logging.properties</tests.logger_properties>-->\n        <tests.logger_properties />\n\n        <license.inlineHeader>${inlineHeader_agpl3}</license.inlineHeader>\n        <source_generation.main.phase>package</source_generation.main.phase>\n\n        <documentation_phases.all_converters>generate-resources</documentation_phases.all_converters>\n    </properties>\n\n    <artifactId>tigase-server</artifactId>\n    <packaging>bundle</packaging>\n    <version>8.5.0-SNAPSHOT</version>\n\n    <name>Tigase XMPP Server</name>\n    <description>The instant messaging server</description>\n\n    <issueManagement>\n        <system>Redmine</system>\n        <url>https://projects.tigase.org/projects/tigase-server</url>\n    </issueManagement>\n\n    <inceptionYear>2004</inceptionYear>\n\n    <developers>\n        <developer>\n            <id>kobit</id>\n            <name>Artur Hefczyc</name>\n            <email>kobit@tigase.org</email>\n            <url>http://www.tigase.org/</url>\n            <roles>\n                <role>architect</role>\n                <role>developer</role>\n            </roles>\n        </developer>\n    </developers>\n\n    <licenses>\n        <license>\n            <name>GNU Affero General Public License GPLv3</name>\n            <url>http://www.gnu.org/licenses/agpl.txt</url>\n        </license>\n    </licenses>\n\n    <scm>\n        <connection>scm:git:${scm_repo_server_base}/server-core.git</connection>\n        <developerConnection>scm:git:${scm_repo_server_base}/server-core.git</developerConnection>\n        <url>${scm_repo_server_base}/server-core</url>\n        <tag>HEAD</tag>\n    </scm>\n\n    <profiles>\n        <profile>\n            <id>osgi</id>\n            <activation>\n                <activeByDefault>true</activeByDefault>\n            </activation>\n            <properties>\n                <packaging.type>bundle</packaging.type>\n                <maven.javadoc.skip>true</maven.javadoc.skip>\n            </properties>\n        </profile>\n        <profile>\n            <id>jar</id>\n            <properties>\n                <packaging.type>jar</packaging.type>\n                <maven.javadoc.skip>true</maven.javadoc.skip>\n            </properties>\n        </profile>\n        <profile>\n            <id>dist</id>\n            <properties>\n                <packaging.type>bundle</packaging.type>\n                <maven.javadoc.skip>false</maven.javadoc.skip>\n                <tests.excludeGroups />\n\n                <guide_name>Tigase_Server</guide_name>\n            </properties>\n            <build>\n                <plugins>\n                    <plugin>\n                        <groupId>org.apache.maven.plugins</groupId>\n                        <artifactId>maven-javadoc-plugin</artifactId>\n                        <executions>\n                            <execution>\n                                <id>attach-javadocs</id>\n                                <phase>package</phase>\n                                <goals>\n                                    <goal>jar</goal>\n                                </goals>\n                                <configuration>\n                                    <includeDependencySources>true</includeDependencySources>\n                                    <dependencySourceIncludes>\n                                        <dependencySourceInclude>tigase:*</dependencySourceInclude>\n                                    </dependencySourceIncludes>\n\n                                    <maxmemory>512m</maxmemory>\n                                </configuration>\n                            </execution>\n                        </executions>\n                    </plugin>\n                    <plugin>\n                        <artifactId>maven-assembly-plugin</artifactId>\n                        <version>3.1.0</version>\n                        <executions>\n                            <execution>\n                                <id>resources</id>\n                                <phase>package</phase>\n                            </execution>\n                        </executions>\n                    </plugin>\n                    <plugin>\n                        <groupId>org.apache.maven.plugins</groupId>\n                        <artifactId>maven-jarsigner-plugin</artifactId>\n                    </plugin>\n                    <plugin>\n                        <groupId>org.asciidoctor</groupId>\n                        <artifactId>asciidoctor-maven-plugin</artifactId>\n                        <executions>\n                            <execution>\n                                <id>output-html</id>\n                                <phase>${documentation_phases.html}</phase>\n                                <configuration>\n                                    <sourceDirectory>./src/main/asciidoc/admin</sourceDirectory>\n                                    <outputDirectory>${guide_path}_-_Admin/html</outputDirectory>\n                                    <backend>html5</backend>\n                                </configuration>\n                            </execution>\n                            <execution>\n                                <id>output-html-dev</id>\n                                <phase>${documentation_phases.html}</phase>\n                                <goals>\n                                    <goal>process-asciidoc</goal>\n                                </goals>\n                                <configuration>\n                                    <sourceDirectory>./src/main/asciidoc/devguide</sourceDirectory>\n                                    <outputDirectory>${guide_path}_-_Developer/html</outputDirectory>\n                                    <backend>html5</backend>\n                                </configuration>\n                            </execution>\n                            <execution>\n                                <id>output-docbook</id>\n                                <phase>${documentation_phases.docbook}</phase>\n                                <configuration>\n                                    <sourceDirectory>./src/main/asciidoc/admin</sourceDirectory>\n                                    <outputDirectory>${project.build.directory}/docbook-admin/</outputDirectory>\n                                </configuration>\n                            </execution>\n                            <execution>\n                                <id>output-docbook-dev</id>\n                                <phase>${documentation_phases.docbook}</phase>\n                                <goals>\n                                    <goal>process-asciidoc</goal>\n                                </goals>\n                                <configuration>\n                                    <sourceDirectory>./src/main/asciidoc/devguide</sourceDirectory>\n                                    <outputDirectory>${project.build.directory}/docbook-devguide/</outputDirectory>\n                                </configuration>\n                            </execution>\n                        </executions>\n                    </plugin>\n\n                    <plugin>\n                        <groupId>com.agilejava.docbkx</groupId>\n                        <artifactId>docbkx-maven-plugin</artifactId>\n                        <executions>\n                            <!--admin guide-->\n                            <execution>\n                                <id>generate-webhelp</id>\n                                <phase>${documentation_phases.webhelp}</phase>\n                                <configuration>\n                                    <targetDirectory>${guide_path}_-_Admin/webhelp/</targetDirectory>\n                                    <sourceDirectory>${project.build.directory}/docbook-admin/</sourceDirectory>\n                                    <postProcess>\n                                        <copy todir=\"${guide_path}_-_Admin/webhelp/css\">\n                                            <fileset dir=\"src/main/asciidoc/css\" />\n                                        </copy>\n                                        <copy todir=\"${guide_path}_-_Admin/webhelp/images\">\n                                            <fileset dir=\"src/main/asciidoc/admin/images\" />\n                                        </copy>\n                                    </postProcess>\n                                </configuration>\n                            </execution>\n                            <execution>\n                                <id>generate-pdf</id>\n                                <phase>${documentation_phases.pdf}</phase>\n                                <configuration>\n                                    <targetDirectory>${guide_path}_-_Admin/pdf/</targetDirectory>\n                                    <imgSrcPath>${project.build.directory}/docbook-admin/</imgSrcPath>\n                                    <sourceDirectory>${project.build.directory}/docbook-admin/</sourceDirectory>\n                                    <postProcess>\n                                        <move failonerror=\"false\" file=\"${guide_path}_-_Admin/pdf/index.pdf\" tofile=\"${guide_path}_-_Admin/pdf/${guide_name}_Guide.pdf\" />\n                                    </postProcess>\n                                </configuration>\n                            </execution>\n                            <execution>\n                                <id>generate-html</id>\n                                <phase>${documentation_phases.html_chunked}</phase>\n                                <configuration>\n                                    <targetDirectory>${guide_path}_-_Admin/html_chunk/</targetDirectory>\n                                    <sourceDirectory>${project.build.directory}/docbook-admin/</sourceDirectory>\n                                    <postProcess>\n                                        <copy todir=\"${guide_path}_-_Admin/html_chunk/css\">\n                                            <fileset dir=\"src/main/asciidoc/css\" />\n                                        </copy>\n                                        <copy todir=\"${guide_path}_-_Admin/html_chunk/images\">\n                                            <fileset dir=\"src/main/asciidoc/admin/images\" />\n                                        </copy>\n                                    </postProcess>\n                                </configuration>\n                            </execution>\n                            <!--dev guide-->\n                            <execution>\n                                <id>generate-webhelp-devguide</id>\n                                <phase>${documentation_phases.webhelp}</phase>\n                                <goals>\n                                    <goal>generate-webhelp</goal>\n                                </goals>\n                                <configuration>\n                                    <targetDirectory>${guide_path}_-_Developer/webhelp/</targetDirectory>\n                                    <sourceDirectory>${project.build.directory}/docbook-devguide/</sourceDirectory>\n                                    <postProcess>\n                                        <copy todir=\"${guide_path}_-_Developer/webhelp/css\">\n                                            <fileset dir=\"src/main/asciidoc/css\" />\n                                        </copy>\n                                        <copy todir=\"${guide_path}_-_Developer/webhelp/images\">\n                                            <fileset dir=\"src/main/asciidoc/devguide/images\" />\n                                        </copy>\n                                    </postProcess>\n\n                                </configuration>\n                            </execution>\n                            <execution>\n                                <id>generate-pdf-devguide</id>\n                                <phase>${documentation_phases.pdf}</phase>\n                                <goals>\n                                    <goal>generate-pdf</goal>\n                                </goals>\n                                <configuration>\n                                    <targetDirectory>${guide_path}_-_Developer/pdf/</targetDirectory>\n                                    <imgSrcPath>${project.build.directory}/docbook-devguide/</imgSrcPath>\n                                    <ignoreImageScaling>true</ignoreImageScaling>\n                                    <fopLogLevel>OFF</fopLogLevel>\n                                    <sourceDirectory>${project.build.directory}/docbook-devguide/</sourceDirectory>\n                                    <postProcess>\n                                        <move failonerror=\"false\" file=\"${guide_path}_-_Developer/pdf/index.pdf\" tofile=\"${guide_path}_-_Developer/pdf/${guide_name}_Guide.pdf\" />\n                                    </postProcess>\n                                </configuration>\n                            </execution>\n                            <execution>\n                                <id>generate-html-devguide</id>\n                                <phase>${documentation_phases.html_chunked}</phase>\n                                <goals>\n                                    <goal>generate-html</goal>\n                                </goals>\n                                <configuration>\n                                    <chunkedOutput>true</chunkedOutput>\n                                    <sourceDirectory>${project.build.directory}/docbook-devguide/</sourceDirectory>\n                                    <targetDirectory>${guide_path}_-_Developer/html_chunk/</targetDirectory>\n                                    <targetsFilename>true</targetsFilename>\n                                    <useIdAsFilename>true</useIdAsFilename>\n                                    <cssDecoration>true</cssDecoration>\n                                    <annotationCss>true</annotationCss>\n                                    <htmlStylesheet>css/docbook-xsl.css</htmlStylesheet>\n                                    <postProcess>\n                                        <copy todir=\"${guide_path}_-_Developer/html_chunk/css\">\n                                            <fileset dir=\"src/main/asciidoc/css\" />\n                                        </copy>\n                                        <copy todir=\"${guide_path}_-_Developer/html_chunk/images\">\n                                            <fileset dir=\"src/main/asciidoc/devguide/images\" />\n                                        </copy>\n                                    </postProcess>\n                                </configuration>\n                            </execution>\n                        </executions>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n    </profiles>\n\n    <build>\n        <finalName>${project.artifactId}</finalName>\n        <plugins>\n\n            <plugin>\n                <groupId>org.apache.felix</groupId>\n                <artifactId>maven-bundle-plugin</artifactId>\n                <configuration>\n                    <instructions combine.inherited=\"append\">\n                        <Bundle-Activator>tigase.osgi.Activator</Bundle-Activator>\n                        <Export-Package>\n                            tigase.auth.*;version=${project.version},tigase.cluster.*;version=${project.version},tigase.component.*;version=${project.version},tigase.conf.*;version=${project.version},tigase.db.*;version=${project.version},tigase.disco.*;version=${project.version},tigase.disteventbus.*;version=${project.version},tigase.eventbus.*;version=${project.version},tigase.io.*;version=${project.version},tigase.kernel.*;version=${project.version},tigase.map.*;version=${project.version},tigase.monitor.*;version=${project.version},tigase.net.*;version=${project.version},tigase.osgi.*;version=${project.version},tigase.server.*;version=${project.version},tigase.stats.*;version=${project.version},tigase.sys.*;version=${project.version},tigase.util.common.*;version=${project.version},tigase.util.historyCache.*;version=${project.version},tigase.util.log.*;version=${project.version},tigase.util.processing.*;version=${project.version},tigase.util.reflection.*;version=${project.version},tigase.util.routing.*;version=${project.version},tigase.util.setup.*;version=${project.version},tigase.util.updater.*;version=${project.version},tigase.util.workqueue.*;version=${project.version},tigase.vhosts.*;version=${project.version},!tigase.xmpp.jid,!tigase.xmpp.rsm,tigase.xmpp.*;version=${project.version}\n                        </Export-Package>\n                        <Import-Package>!junit.*,!sun.*,!com.sun.*,*</Import-Package>\n                        <DynamicImport-Package>*</DynamicImport-Package>\n                        <Embed-Dependency>\n                            *;scope=compile|runtime;artifactId=!junit|org.osgi.core|slf4j-api|tigase-xmltools|tigase-utils\n                        </Embed-Dependency>\n                        <Bundle-SymbolicName>${project.artifactId};singleton=true</Bundle-SymbolicName>\n                    </instructions>\n                </configuration>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-jar-plugin</artifactId>\n            </plugin>\n\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-surefire-plugin</artifactId>\n            </plugin>\n            <plugin>\n                <groupId>org.codehaus.mojo</groupId>\n                <artifactId>build-helper-maven-plugin</artifactId>\n                <executions>\n                    <execution>\n                        <id>add-resource</id>\n                        <phase>none</phase>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n        <resources>\n            <resource>\n                <targetPath>database</targetPath>\n                <directory>src/main/database</directory>\n                <includes>\n                    <include>**/*.sql</include>\n                    <include>**/*.sh</include>\n                    <include>**/*.cmd</include>\n                    <include>**/*.txt</include>\n                </includes>\n            </resource>\n            <resource>\n                <targetPath>templates</targetPath>\n                <directory>src/main/resources/templates</directory>\n                <includes>\n                    <include>**/*.*</include>\n                </includes>\n            </resource>\n        </resources>\n\n    </build>\n    <dependencies>\n        <dependency>\n            <groupId>tigase</groupId>\n            <artifactId>tigase-utils</artifactId>\n            <version>4.5.0-SNAPSHOT</version>\n            <scope>compile</scope>\n        </dependency>\n        <dependency>\n            <groupId>tigase</groupId>\n            <artifactId>tigase-utils</artifactId>\n            <version>4.5.0-SNAPSHOT</version>\n            <scope>test</scope>\n            <classifier>tests</classifier>\n        </dependency>\n        <dependency>\n            <groupId>tigase</groupId>\n            <artifactId>tigase-xmltools</artifactId>\n            <version>4.3.0</version>\n            <scope>compile</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.felix</groupId>\n            <artifactId>org.osgi.core</artifactId>\n            <version>1.4.0</version>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.osgi</groupId>\n            <artifactId>org.osgi.compendium</artifactId>\n            <version>5.0.0</version>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>jul-to-slf4j</artifactId>\n            <version>${slf4j.version}</version>\n            <scope>provided</scope>\n            <optional>true</optional>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <version>4.12</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.codehaus.groovy</groupId>\n            <artifactId>groovy</artifactId>\n            <version>${groovyVersion}</version>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.codehaus.groovy</groupId>\n            <artifactId>groovy-json</artifactId>\n            <version>${groovyVersion}</version>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.codehaus.groovy</groupId>\n            <artifactId>groovy-jsr223</artifactId>\n            <version>${groovyVersion}</version>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.codehaus.groovy</groupId>\n            <artifactId>groovy-templates</artifactId>\n            <version>${groovyVersion}</version>\n            <scope>provided</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.codehaus.groovy</groupId>\n            <artifactId>groovy-xml</artifactId>\n            <version>${groovyVersion}</version>\n            <scope>provided</scope>\n        </dependency>\n        <!-- For JUnit tests to test repository implementations -->\n        <dependency>\n            <groupId>com.mysql</groupId>\n            <artifactId>mysql-connector-j</artifactId>\n            <version>8.1.0</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.microsoft.sqlserver</groupId>\n            <artifactId>mssql-jdbc</artifactId>\n            <version>10.2.1.jre17</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.postgresql</groupId>\n            <artifactId>postgresql</artifactId>\n            <version>42.3.8</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.derby</groupId>\n            <artifactId>derby</artifactId>\n            <version>10.15.2.0</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.derby</groupId>\n            <artifactId>derbyshared</artifactId>\n            <version>10.15.2.0</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <repositories>\n        <repository>\n            <id>tigase</id>\n            <url>https://maven-repo.tigase.org/repository/tigase</url>\n        </repository>\n    </repositories>\n</project>\n\n"
  },
  {
    "path": "prj.el",
    "content": "!!!\n!!! Tigase XMPP Server - The instant messaging server\n!!! Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n!!!\n!!! This program is free software: you can redistribute it and/or modify\n!!! it under the terms of the GNU Affero General Public License as published by\n!!! the Free Software Foundation, version 3 of the License.\n!!!\n!!! This program is distributed in the hope that it will be useful,\n!!! but WITHOUT ANY WARRANTY; without even the implied warranty of\n!!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n!!! GNU Affero General Public License for more details.\n!!!\n!!! You should have received a copy of the GNU Affero General Public License\n!!! along with this program. Look for COPYING file in the top folder.\n!!! If not, see http://www.gnu.org/licenses/.\n!!!\n\n(jde-project-file-version \"1.0\")\n(jde-set-variables\n '(jde-electric-return-p t)\n '(jde-enable-abbrev-mode t)\n '(jde-gen-k&r t)\n '(indent-tabs-mode t)\n '(user-mail-address \"artur.hefczyc@tigase.org\")\n '(jde-import-auto-collapse-imports nil)\n '(jde-import-auto-sort t)\n '(jde-auto-parse-buffer-interval 600)\n '(jde-global-classpath\n\t (quote\n\t\t(\n\t\t \"$JAVA_HOME/jre/lib/rt.jar\"\n\t\t \"/tmp/classes/\"\n\t\t \"$PROJECTS_HOME/tigase/server/libs/junit.jar\"\n\t\t \"$PROJECTS_HOME/tigase/server/libs/jdbc-mysql.jar\"\n\t\t \"$PROJECTS_HOME/tigase/server/libs/tigase-xmltools.jar\"\n\t\t \"$PROJECTS_HOME/tigase/server/libs/tigase-utils.jar\"\n\t\t \"$PROJECTS_HOME/tigase/server/libs/jml-1.0b2.jar\"\n\t\t )))\n '(jde-sourcepath\n\t (quote\n\t\t(\n\t\t \"$PROJECTS_HOME/tigase/server/src/main/java\"\n\t\t \"$PROJECTS_HOME/tigase/xmltools/src/main/java\"\n\t\t \"$PROJECTS_HOME/tigase/utils/src/main/java\"\n\t\t \"$PROJECTS_HOME/tigase/server/tests/unittests/src\"\n\t\t \"$JAVA_HOME/share/src\"\n\t\t \"$JAVA_HOME/share/tests/src\"\n\t\t )))\n '(jde-gen-buffer-boilerplate\n\t (quote\n\t\t(\n\t\t \"/*  Tigase Jabber/XMPP Server\"\n\t\t \" *  Copyright (C) 2004-2012 \\\"Artur Hefczyc\\\" <artur.hefczyc@tigase.org>\"\n\t\t \" *\"\n\t\t \" * This program is free software: you can redistribute it and/or modify\"\n\t\t \" * it under the terms of the GNU Affero General Public License as published by\"\n\t\t \" * the Free Software Foundation, version 3 of the License.\"\n\t\t \" *\"\n\t\t \" * This program is distributed in the hope that it will be useful,\"\n\t\t \" * but WITHOUT ANY WARRANTY; without even the implied warranty of\"\n\t\t \" * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\"\n\t\t \" * GNU Affero General Public License for more details.\"\n\t\t \" *\"\n\t\t \" * You should have received a copy of the GNU Affero General Public License\"\n\t\t \" * along with this program. Look for COPYING file in the top folder.\"\n\t\t \" * If not, see http://www.gnu.org/licenses/.\"\n\t\t \" *\"\n\t\t \" * $Rev: $\"\n\t\t \" * Last modified by $Author: $\"\n\t\t \" * $Date: $\"\n\t\t \" */\"\n\t\t )))\n )\n"
  },
  {
    "path": "scripts/config.sh",
    "content": "#!/bin/bash\n#\n# Tigase XMPP Server - The instant messaging server\n# Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, version 3 of the License.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. Look for COPYING file in the top folder.\n# If not, see http://www.gnu.org/licenses/.\n#\n\nCP=\"jars/tigase-server.jar:/usr/share/jdbc-mysql/lib/jdbc-mysql.jar:libs/tigase-xmltools.jar:libs/tigase-utils.jar\"\n\njava -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 -cp $CP tigase.conf.Configurator $*"
  },
  {
    "path": "scripts/db-create.cmd",
    "content": "@REM\n@REM Tigase XMPP Server - The instant messaging server\n@REM Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n@REM\n@REM This program is free software: you can redistribute it and/or modify\n@REM it under the terms of the GNU Affero General Public License as published by\n@REM the Free Software Foundation, version 3 of the License.\n@REM\n@REM This program is distributed in the hope that it will be useful,\n@REM but WITHOUT ANY WARRANTY; without even the implied warranty of\n@REM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n@REM GNU Affero General Public License for more details.\n@REM\n@REM You should have received a copy of the GNU Affero General Public License\n@REM along with this program. Look for COPYING file in the top folder.\n@REM If not, see http://www.gnu.org/licenses/.\n@REM\n\n@echo off\n\njava -cp \"jars/*\" tigase.db.util.DBSchemaLoader %*"
  },
  {
    "path": "scripts/db-create.sh",
    "content": "#!/bin/bash\n#\n# Tigase XMPP Server - The instant messaging server\n# Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, version 3 of the License.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. Look for COPYING file in the top folder.\n# If not, see http://www.gnu.org/licenses/.\n#\n\n\njava -cp \"jars/*\" tigase.db.util.DBSchemaLoader \"$@\""
  },
  {
    "path": "scripts/db-derby-connect.sh",
    "content": "#!/bin/bash\n#\n# Tigase XMPP Server - The instant messaging server\n# Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, version 3 of the License.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. Look for COPYING file in the top folder.\n# If not, see http://www.gnu.org/licenses/.\n#\n\n\n[[ \"$1\" = \"\" ]] && \\\n  echo \"Give me a path to the location where you want to have the database created\" && \\\n  exit 1\n\njava -Dij.protocol=jdbc:derby: -Dij.database=\"$1;create=true\" \\\n\t\t-Dderby.system.home=`pwd` \\\n\t\t-cp jars/derby.jar:jars/derbytools.jar:jars/tigase-server.jar \\\n\t\torg.apache.derby.tools.ij\n"
  },
  {
    "path": "scripts/debian/changelog",
    "content": "tigase (@version@) unstable; urgency=medium\n\n  * For full changelog, see\n  http://www.tigase.org/\n\n -- Sergey Nazarov <phearnot@renee.ru>  @builddate@\n2008-11-07 13:38  kobit\n\n\t* build.xml, package.html, src/main/izpack/install.xml,\n\t  src/main/izpack/java/TigaseConfigLoadPanel.java,\n\t  src/main/izpack/java/TigaseConfigSavePanel.java,\n\t  src/main/izpack/java/TigaseDBCheckPanel.java,\n\t  src/main/izpack/resources/pics/tigase-install-logo.png,\n\t  src/main/izpack/userInputSpec.xml: A few installer fixes, added\n\t  unix scripts\n\n2008-11-07 13:38  kobit\n\n\t* etc/tigase.conf, win-stuff/wrapper/wrapper.conf: Added jdbc\n\t  drivers for all 3 supported databases: derby, mysql, pgsql\n\n2008-11-06 11:37  kobit\n\n\t* License.html: GPLv3 license copy in HTML frmat\n\n2008-11-06 11:37  kobit\n\n\t* database/mysql-schema-4-props.sql, src/main/izpack,\n\t  src/main/izpack/install.xml, src/main/izpack/java,\n\t  src/main/izpack/java/DerbyPathPanel.java,\n\t  src/main/izpack/java/TigaseConfigConst.java,\n\t  src/main/izpack/java/TigaseConfigLoadPanel.java,\n\t  src/main/izpack/java/TigaseConfigSavePanel.java,\n\t  src/main/izpack/java/TigaseDBCheckPanel.java,\n\t  src/main/izpack/resources,\n\t  src/main/izpack/resources/packsLang.xml,\n\t  src/main/izpack/resources/pics,\n\t  src/main/izpack/resources/pics/tigase-install-logo.png,\n\t  src/main/izpack/resources/pics/tiger.png,\n\t  src/main/izpack/userInputSpec.xml: Installer files added\n\n2008-11-06 11:13  kobit\n\n\t* database/derby-create-db.sql, database/derby-schema-4-props.sql,\n\t  database/derby-schema-4-sp.schema, database/derby-schema-4.sql,\n\t  database/mysql-create-db.sql, database/mysql-schema-4-sp.schema,\n\t  database/mysql-schema-4.sql,\n\t  database/mysql-schema-upgrade-to-4.sql,\n\t  database/postgresql-create-db.sql,\n\t  database/postgresql-schema-4-props.sql,\n\t  database/postgresql-schema-4-sp.schema,\n\t  database/postgresql-schema-4.sql, etc/tigase.conf: Changes for\n\t  the installer\n\n2008-11-04 12:28  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Added more detailed logging for session closing event\n\n2008-10-30 12:54  kobit\n\n\t* src/main/java/tigase/xmpp/impl/roster/RosterFlat.java: Fixed a\n\t  bug in RosterFlat that the code didn't remove the resource part\n\n2008-10-30 11:48  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Added patch from Yate guys for the user status command containing\n\t  presence info\n\n2008-10-30 11:24  kobit\n\n\t* src/main/java/tigase/net/IOService.java,\n\t  src/main/java/tigase/server/xmppsession/PacketFilter.java,\n\t  src/main/java/tigase/util/RepositoryUtils.java,\n\t  src/main/java/tigase/xmpp/impl/AnonymousRoster.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqPrivacy.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java,\n\t  src/main/java/tigase/xmpp/impl/Roster.java,\n\t  src/main/java/tigase/xmpp/impl/RosterAbstract.java,\n\t  src/main/java/tigase/xmpp/impl/RosterFactory.java,\n\t  src/main/java/tigase/xmpp/impl/RosterFlat.java,\n\t  src/main/java/tigase/xmpp/impl/roster,\n\t  src/main/java/tigase/xmpp/impl/roster/Roster.java,\n\t  src/main/java/tigase/xmpp/impl/roster/RosterAbstract.java,\n\t  src/main/java/tigase/xmpp/impl/roster/RosterElement.java,\n\t  src/main/java/tigase/xmpp/impl/roster/RosterFactory.java,\n\t  src/main/java/tigase/xmpp/impl/roster/RosterFlat.java:\n\t  Implemented roster flat storage\n\n2008-10-28 22:42  kobit\n\n\t* src/main/java/tigase/net/IOService.java: Disconnection monitoring\n\t  and debugging added\n\n2008-10-28 22:27  kobit\n\n\t* src/main/java/tigase/cluster/SessionManagerClustered.java:\n\t  Disconnection monitoring and debugging added\n\n2008-10-28 22:20  kobit\n\n\t* src/main/java/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  Disconnection monitoring and debugging added\n\n2008-10-28 22:16  kobit\n\n\t* src/main/java/tigase/cluster/SessionManagerClustered.java:\n\t  Disconnection monitoring and debugging added\n\n2008-10-28 21:43  kobit\n\n\t* etc/init.properties, package.html,\n\t  src/main/java/tigase/cluster/ClusterConnectionManager.java,\n\t  src/main/java/tigase/cluster/SessionManagerClustered.java,\n\t  src/main/java/tigase/net/IOService.java,\n\t  src/main/java/tigase/net/SocketReadThread.java,\n\t  src/main/java/tigase/server/AbstractMessageReceiver.java,\n\t  src/main/java/tigase/util/TimeUtils.java: Disconnection\n\t  monitoring and debugging added\n\n2008-10-22 19:35  kobit\n\n\t* src/main/java/tigase/db/jdbc/JDBCRepository.java,\n\t  src/main/java/tigase/db/jdbc/TigaseAuth.java,\n\t  src/main/java/tigase/db/jdbc/TigaseCustomAuth.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  Fixed deadlock problem occuring in derby database, if a query\n\t  returns results it must be read and freed....\n\n2008-10-22 17:20  bmalkow\n\n\t* src/main/java/tigase/db/derby/StoredProcedures.java: add\n\t  connection closing\n\n2008-10-22 15:25  bmalkow\n\n\t* src/main/java/tigase/db/derby/StoredProcedures.java: fixed\n\t  problem with login user. I don't know why, but now it works\n\n2008-10-22 13:32  kobit\n\n\t* database/derby-schema-4.sql,\n\t  src/main/java/tigase/conf/Configurable.java,\n\t  src/main/java/tigase/db/derby/StoredProcedures.java,\n\t  src/main/java/tigase/db/jdbc/JDBCRepository.java,\n\t  src/main/java/tigase/db/jdbc/TigaseAuth.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  Fixes and changes for Derby database\n\n2008-10-21 20:15  kobit\n\n\t* src/main/java/tigase/conf/Configurable.java,\n\t  src/main/java/tigase/db/jdbc/TigaseCustomAuth.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  TigaseCustomAuth is finished, tested and added as a standard\n\t  authentication connector to the system\n\n2008-10-21 20:14  kobit\n\n\t* database/mysql-schema-4-sp.schema,\n\t  database/postgresql-schema-4-sp.schema: Fixed problem with the SP\n\t  incorrectly removing users from the database\n\n2008-10-21 17:33  kobit\n\n\t* src/main/java/tigase/db/jdbc/TigaseCustomAuth.java: Custom auth\n\t  implementation finished, not tested yet\n\n2008-10-21 17:16  kobit\n\n\t* database/mysql-schema-4-sp.schema,\n\t  database/postgresql-schema-4-sp.schema: Added new SP for password\n\t  update with procedure parameters in reverse order\n\n2008-10-21 16:32  kobit\n\n\t* src/main/java/tigase/db/jdbc/TigaseCustomAuth.java: JavaDoc\n\t  documentation updated\n\n2008-10-21 13:43  kobit\n\n\t* src/main/java/tigase/db/jdbc/TigaseCustomAuth.java: User\n\t  authentication connector allowing to use custom SQL queries\n\n2008-10-21 08:47  kobit\n\n\t* database/postgresql-schema-4-sp.schema: Added\n\t  MD5-USERNAME-PASSWORD password encoding support\n\n2008-10-20 23:40  kobit\n\n\t* src/main/java/tigase/cluster/ClusterConnectionManager.java,\n\t  src/main/java/tigase/cluster/ClusterElement.java,\n\t  src/main/java/tigase/cluster/ServerConnectionClustered.java,\n\t  src/main/java/tigase/cluster/SessionManagerClustered.java,\n\t  src/main/java/tigase/server/bosh/BoshConnectionManager.java,\n\t  src/main/java/tigase/server/bosh/BoshSession.java,\n\t  src/main/java/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/PacketFilter.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/xmpp/ConnectionStatus.java,\n\t  src/main/java/tigase/xmpp/RepositoryAccess.java,\n\t  src/main/java/tigase/xmpp/XMPPIOService.java,\n\t  src/main/java/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/main/java/tigase/xmpp/XMPPSession.java: Reimplementation of\n\t  the clustering code in the SM and lots of improvements in other\n\t  areas, all related to the clustering and user session management\n\t  for multiple user connections\n\n2008-10-16 12:02  kobit\n\n\t* database/mysql-schema-4-sp.schema: Added a new password encoding\n\t  option: MD5_USERNAME_PASSWORD\n\n2008-10-09 20:35  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Offline presence is\n\t  not now broadcasted if the client didn't send initial presence\n\t  before\n\n2008-10-08 22:41  kobit\n\n\t* src/main/java/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  Attempt to solve the first TLS problem\n\n2008-10-08 22:39  kobit\n\n\t* src/main/java/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  Attempt to solve the first TLS problem\n\n2008-10-08 22:31  kobit\n\n\t* src/main/java/tigase/io/TLSIO.java: Attempt to solve the first\n\t  TLS problem\n\n2008-10-08 22:26  kobit\n\n\t* src/main/java/tigase/io/SocketIO.java,\n\t  src/main/java/tigase/net/IOService.java: Attempt to solve the\n\t  first TLS problem\n\n2008-10-08 22:21  kobit\n\n\t* src/main/java/tigase/io/SocketIO.java: Attempt to solve the first\n\t  TLS problem\n\n2008-10-08 22:18  kobit\n\n\t* src/main/java/tigase/io/TLSIO.java,\n\t  src/main/java/tigase/net/IOService.java: Attempt to solve the\n\t  first TLS problem\n\n2008-10-08 22:14  kobit\n\n\t* src/main/java/tigase/net/IOService.java: Attempt to solve the\n\t  first TLS problem\n\n2008-10-08 22:10  kobit\n\n\t* src/main/java/tigase/io/TLSIO.java,\n\t  src/main/java/tigase/io/TLSWrapper.java,\n\t  src/main/java/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  Attempt to solve the first TLS problem\n\n2008-10-08 20:35  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Server vhosts session has now resource bond\n\n2008-10-08 17:11  kobit\n\n\t* src/main/java/tigase/cluster/SessionManagerClustered.java: Fixed\n\t  a problem with not releasing hold on session in some cases\n\n2008-10-08 15:35  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  sendAllOnHold can now process packet locally if there is no\n\t  packet rdirection address\n\n2008-10-08 15:25  kobit\n\n\t* src/main/java/tigase/cluster/SessionManagerClustered.java,\n\t  src/main/java/tigase/xmpp/XMPPSession.java: The session is put on\n\t  hold just after successful authentication\n\n2008-10-08 11:13  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Presence broadcast\n\t  sends only unavailable presence to all entities to which user\n\t  sent direct presence\n\n2008-10-03 15:51  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshSession.java: Removed\n\t  inserting HTML tags around http links detected in the message,\n\t  this is client side stuff\n\n2008-10-03 11:25  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Presence\n\t  subscription flow changed to match last changes in the RFC\n\n2008-10-01 22:12  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Added responses either the 'result' or 'error' to all USER_STATUS\n\t  commands\n\n2008-10-01 17:52  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/xmpp/XMPPSession.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java: Fixed problem with\n\t  the USER_STATUS command\n\n2008-10-01 15:47  kobit\n\n\t* src/main/java/tigase/server/xmppsession/PacketFilter.java,\n\t  src/main/java/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java: Now the server\n\t  requires user to bind the resource first before sending any other\n\t  packet\n\n2008-10-01 12:05  kobit\n\n\t* src/main/java/tigase/xmpp/XMPPDomBuilderHandler.java: Added more\n\t  logging infor\n\n2008-09-30 20:51  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  IsBrokenPacket detection fixed\n\n2008-09-30 16:05  kobit\n\n\t* src/main/java/tigase/cluster/SessionManagerClustered.java:\n\t  CHECK_USER_SESSION result may arrive when the user session is\n\t  gone already, added a code to handle this\n\n2008-09-30 07:13  kobit\n\n\t* src/main/java/tigase/cluster/ClusterConnectionManager.java,\n\t  src/main/java/tigase/xmpp/XMPPIOService.java: Added read/write\n\t  locking mechanism, apparently in certain cases multiple threads\n\t  may try to read/write on a single socket\n\n2008-09-29 20:06  kobit\n\n\t* src/main/java/tigase/server/MessageRouter.java,\n\t  src/main/java/tigase/xmpp/XMPPIOService.java: Removed silly\n\t  logging messages\n\n2008-09-29 18:41  kobit\n\n\t* src/main/java/tigase/xmpp/XMPPIOService.java: XML parser\n\t  exception printed to a log file\n\n2008-09-29 14:11  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java,\n\t  src/main/java/tigase/xmpp/impl/RosterAbstract.java: Fixed a bug\n\t  with presence subscription when contact replies with unsubscribed\n\t  to the subscription request\n\n2008-09-29 12:57  bmalkow\n\n\t* database/derby-schema-4.sql: fix\n\n2008-09-29 12:57  bmalkow\n\n\t* src/main/java/tigase/db/derby/StoredProcedures.java: fix\n\n2008-09-26 21:56  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Subscription\n\t  management has been simplified to meet requirements from\n\t  RFC-3921bis-5\n\n2008-09-26 14:10  kobit\n\n\t* src/main/java/tigase/db/UserAuthRepository.java: Comment fixed\n\t  for the digestAuth method\n\n2008-09-26 07:46  bmalkow\n\n\t* database/derby-schema-4.sql, src/main/java/tigase/db/derby,\n\t  src/main/java/tigase/db/derby/StoredProcedures.java: add: JavaDB\n\t  (Derby) DDL and Stored Procedures\n\n2008-09-24 13:03  kobit\n\n\t* src/main/java/tigase/xmpp/impl/OfflineMessages.java: Fixed\n\t  problem with sorting off-line presence packets, corrected offline\n\t  message removal and changes the time stamp from the xep-0091 to\n\t  xep-0203\n\n2008-09-20 17:57  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshConnectionManager.java,\n\t  src/main/java/tigase/server/bosh/BoshIOService.java,\n\t  src/main/java/tigase/server/bosh/BoshSession.java,\n\t  src/main/java/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/PacketFilter.java,\n\t  src/main/java/tigase/xmpp/XMPPIOService.java: Fixes in the Bosh\n\t  protocol for clustering supoprt - the BoshConnectionManager\n\t  exyends now ClientConnectionManager\n\n2008-09-18 18:29  kobit\n\n\t* src/main/java/tigase/db/jdbc/JDBCRepository.java: getUserUID now\n\t  expects result greater then 0\n\n2008-09-18 17:53  kobit\n\n\t* database/postgresql-schema-4-sp.schema: Removed returning from\n\t  the stored procedures for compatibility with postgresql 8.0\n\n2008-09-18 17:04  kobit\n\n\t* database/mysql-schema-4-sp.schema,\n\t  database/postgresql-schema-4-sp.schema,\n\t  src/main/java/tigase/db/jdbc/TigaseAuth.java: Removed OUT\n\t  parameters from stored procedures for compatibility with\n\t  postgresql 8.0. These parameters were not used in normal\n\t  operation anyway. Only in SQL database test script.\n\n2008-09-17 17:20  kobit\n\n\t* src/main/java/tigase/xmpp/RepositoryAccess.java,\n\t  src/main/java/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqIq.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqPrivacy.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqPrivate.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRegister.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/main/java/tigase/xmpp/impl/OfflineMessages.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java,\n\t  src/main/java/tigase/xmpp/impl/Privacy.java,\n\t  src/main/java/tigase/xmpp/impl/Roster.java,\n\t  src/main/java/tigase/xmpp/impl/RosterAbstract.java,\n\t  src/main/java/tigase/xmpp/impl/RosterFlat.java,\n\t  src/main/java/tigase/xmpp/impl/VCardTemp.java: The repository\n\t  access code no longer catches and hides database exceptions, they\n\t  are passed out to the calling code allowing for proper error\n\t  singaling\n\n2008-09-17 11:51  kobit\n\n\t* src/main/java/tigase/server/XMPPServer.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRoster.java: In some cases\n\t  strange characters cause database to throw error as en effect the\n\t  whole roster can not be loaded properly as well as it also\n\t  impacts presence, the fix should hangel it properly\n\n2008-09-17 08:59  kobit\n\n\t* pom.xml, src/main/java/tigase/conf/ConfigRepository.java,\n\t  src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/server/XMPPServer.java: Fixed a bug when the\n\t  server is run with incorrect (non-xml) configuration file, now\n\t  the server discovers it and terminates it's work with a proper\n\t  message\n\n2008-09-16 14:02  kobit\n\n\t* database/postgresql-schema-4-sp.schema: Fixed a bug in the stored\n\t  function\n\n2008-09-16 11:06  kobit\n\n\t* src/main/java/tigase/db/jdbc/JDBCRepository.java: Fixed a bug\n\t  where prepared statement was used instead of callable statement,\n\t  apparently MySQL can handle it, PostreSQL throws an error\n\n2008-09-16 11:06  kobit\n\n\t* database/postgresql-schema-4-sp.schema: Fixed a problem with\n\t  returing query results\n\n2008-09-16 09:58  kobit\n\n\t* database/postgresql-schema-4-sp.schema,\n\t  database/postgresql-schema-4.sql,\n\t  database/postgresql-schema-upgrade-to-4.sql: The first complete\n\t  (untested) version of the schema 4 and upgrade script for\n\t  postgresql\n\n2008-09-16 00:37  kobit\n\n\t* database/mysql-schema-4-sp.schema,\n\t  database/postgresql-schema-4-sp.schema,\n\t  database/postgresql-schema-4.sql: PostgreSQL schema ver 4.0\n\t  updated\n\n2008-09-15 20:48  kobit\n\n\t* database/postgresql-schema-4.sql: postgresql schema definition\n\t  updated to version 4.0\n\n2008-09-11 18:04  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  filter processing condition changed\n\n2008-09-11 16:38  kobit\n\n\t* database/mysql-schema-upgrade-to-4.sql: Fixed problem with\n\t  tig_nodes table upgrade\n\n2008-09-11 16:29  kobit\n\n\t* database/mysql-schema-upgrade-to-4.sql: Fixed tig_nodes\n\t  conversion\n\n2008-09-11 15:18  kobit\n\n\t* src/main/java/tigase/db/jdbc/JDBCRepository.java: Added code to\n\t  online fix the database and adding root node when missing\n\n2008-09-11 13:42  kobit\n\n\t* src/main/java/tigase/db/jdbc/JDBCRepository.java: Revertet back a\n\t  fix causing problems with database - the case with null submode\n\n2008-09-11 09:36  kobit\n\n\t* src/main/java/tigase/db/jdbc/JDBCRepository.java: Fixed problem\n\t  when the subnode is null\n\n2008-09-10 19:16  kobit\n\n\t* src/main/java/tigase/db/jdbc/JDBCRepository.java: Removed dumping\n\t  stack trace on the db schema version conflict\n\n2008-09-10 18:56  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  serverSession object initialization added\n\n2008-09-10 18:56  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  Added support for TigaseAuth connector\n\n2008-09-10 18:55  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Changed log level for statistics from finest to info\n\n2008-09-10 18:54  kobit\n\n\t* src/main/java/tigase/xmpp/XMPPImplIfc.java: init(UserRepository\n\t  rep) method added to allow custom repository configuration by\n\t  plugins\n\n2008-09-10 18:53  kobit\n\n\t* src/main/java/tigase/xmpp/XMPPProcessor.java: init(UserRepository\n\t  rep) method added to allow custom repository configuration by\n\t  plugins\n\n2008-09-10 18:52  kobit\n\n\t* src/main/java/tigase/xmpp/impl/UrnXmppPing.java: Added proper\n\t  response on user session missing - which means the user is not\n\t  connected and can not respond to the ping\n\n2008-09-10 18:51  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqRoster.java: Added roster\n\t  abstract to allow different Roster storage implementations\n\n2008-09-10 18:51  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqPrivacy.java: Added roster\n\t  abstract to allow different Roster storage implementations\n\n2008-09-10 18:50  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Added roster\n\t  abstract to allow different Roster storage implementations\n\n2008-09-10 18:50  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Roster.java,\n\t  src/main/java/tigase/xmpp/impl/RosterAbstract.java,\n\t  src/main/java/tigase/xmpp/impl/RosterFactory.java,\n\t  src/main/java/tigase/xmpp/impl/RosterFlat.java: Added roster\n\t  abstract to allow different Roster storage implementations\n\n2008-09-10 18:49  kobit\n\n\t* src/main/java/tigase/db/jdbc/TigaseAuth.java: A new\n\t  authentication connector whish can base on the stored procedures\n\t  in the database\n\n2008-09-10 18:48  kobit\n\n\t* src/main/java/tigase/db/jdbc/JDBCRepository.java: Database schema\n\t  upgrade to version 4.0\n\n2008-09-10 18:47  kobit\n\n\t* database/mysql-schema-4-sp.schema, database/mysql-schema-4.sql,\n\t  database/mysql-schema-upgrade-to-4.sql: A few fixes to stored\n\t  procedures and schema upgrade process\n\n2008-09-09 16:24  kobit\n\n\t* database/mysql-schema-4-sp.schema, database/mysql-schema-4.sql:\n\t  Added missing drop stored procedure expression and changed the\n\t  order of the table creation\n\n2008-09-08 13:51  kobit\n\n\t* database/mysql-schema-4-sp.schema, database/mysql-schema-4.sql,\n\t  database/mysql-schema-upgrade-to-4.sql: Improved schema upgrade\n\t  script, it correctly copies tables to new one with\n\t  auto-incrementing IDs and it should work with any database now\n\n2008-09-06 16:33  kobit\n\n\t* database/mysql-schema-4-sp.schema,\n\t  database/mysql-schema-4-test.sql, database/mysql-schema-4.sql,\n\t  database/mysql-schema-upgrade-to-4.sql: Proper schema upgrade\n\t  procedure which converts also UIDs and NIDs to autoincrementing\n\t  fields\n\n2008-09-05 16:38  kobit\n\n\t* database/mysql-schema-4-sp.schema: TigGetDBProperty funciton is\n\t  READS SQL DATA now\n\n2008-09-05 12:13  kobit\n\n\t* database/mysql-schema-4-sp.schema, database/mysql-schema-4.sql,\n\t  database/mysql-schema-upgrade-to-4.sql: MySQL Schema for Tigase\n\t  4.0 and schema upgarde scripts are ready\n\n2008-09-03 22:26  kobit\n\n\t* src/main/java/tigase/db/jdbc/JDBCRepository.java: More detailed\n\t  logging in case of exception\n\n2008-09-03 20:09  kobit\n\n\t* src/main/java/tigase/conf/Configurable.java,\n\t  src/main/java/tigase/db/UserAuthRepository.java,\n\t  src/main/java/tigase/db/jdbc/DrupalAuth.java,\n\t  src/main/java/tigase/db/jdbc/TigaseAuth.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  Added TigaseAuth - generic connector which can authenticate users\n\t  agains any SQL (JDBC) database if the database offers specific\n\t  stored procedures\n\n2008-09-03 13:02  kobit\n\n\t* pom.xml: utils dependency changed to 3.1.1\n\n2008-09-02 17:43  kobit\n\n\t* src/main/java/tigase/db/DummyRepository.java,\n\t  src/main/java/tigase/db/UserRepository.java,\n\t  src/main/java/tigase/db/jdbc/JDBCRepository.java,\n\t  src/main/java/tigase/db/xml/XMLRepository.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Blocked automatic adding anonymous users to the database when\n\t  userAutoCreate property is set to true\n\n2008-09-02 13:19  kobit\n\n\t* etc/init-mysql.properties, etc/tigase-mysql.conf: Changed default\n\t  vhost name from tigase.org to devel.tigase.org and added line to\n\t  clear the default classpath to reduce number of classes loaded at\n\t  runtime and avoid conflicts\n\n2008-09-02 13:17  kobit\n\n\t* database/mysql-schema-4.sql,\n\t  database/mysql-schema-upgrade-to-4.sql: Adjusted database schema\n\t  to handle JID of size according to XMPP spec - 2048\n\n2008-09-02 13:16  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Fixed problem with redirect result arriving before cluster\n\t  session_transfer result\n\n2008-09-02 13:16  kobit\n\n\t* src/main/java/tigase/cluster/SessionManagerClustered.java: Fixed\n\t  problem with redirect result arriving before cluster\n\t  session_transfer result\n\n2008-09-01 21:17  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Added support for\n\t  nickname for anonymous user connection\n\n2008-09-01 16:02  kobit\n\n\t* etc/init.properties: Removed anonymouse roster line which is not\n\t  needed anymore\n\n2008-08-29 15:01  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/main/java/tigase/xmpp/impl/SaslAuth.java: The authentication\n\t  plugin allows now for 3 tries before the user gets disconnected\n\n2008-08-29 14:17  kobit\n\n\t* src/main/java/tigase/xmpp/RepositoryAccess.java: Intendation\n\t  corrected\n\n2008-08-27 17:00  kobit\n\n\t* src/main/java/tigase/db/jdbc/DrupalAuth.java: drupal users table\n\t  is automatically altered if online_status field is missing\n\n2008-08-27 09:08  kobit\n\n\t* src/main/java/tigase/server/gateways/Gateway.java: Incorrect\n\t  VHost initalization bug fixed\n\n2008-08-27 07:32  kobit\n\n\t* src/main/java/tigase/xmpp/impl/BindResource.java: Fixed a bug\n\t  with the resource part incorrectly send to the client\n\n2008-08-27 07:31  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Performance\n\t  improvements - the dynamic roster is queried first and if it\n\t  returns null then the static roster is queried\n\n2008-08-22 16:57  kobit\n\n\t* database/mysql-schema-4.sql,\n\t  database/mysql-schema-upgrade-to-4.sql: Upgrade to the Tigase db\n\t  schema version 4.0\n\n2008-08-22 16:44  kobit\n\n\t* database/mysql-schema-4.sql,\n\t  database/mysql-schema-upgrade-to-4.sql,\n\t  database/postgresql-schema-4.sql,\n\t  database/sqlserver-schema-4.sql: Upgrade to the Tigase db schema\n\t  version 4.0\n\n2008-08-19 12:28  kobit\n\n\t* pom.xml, src/main/java/tigase/cluster/MethodCall.java,\n\t  src/main/java/tigase/cluster/MethodCallCriteria.java,\n\t  src/main/java/tigase/cluster/SessionManagerClustered.java,\n\t  src/main/java/tigase/cluster/methodcalls,\n\t  src/main/java/tigase/cluster/methodcalls/SessionTransferMC.java,\n\t  src/main/java/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/xmpp/ConnectionStatus.java,\n\t  src/main/java/tigase/xmpp/XMPPResourceConnection.java: Initial\n\t  fix for the session transfer bug\n\n2008-08-07 06:13  kobit\n\n\t* src/main/java/tigase/server/gateways/Gateway.java: Temp code just\n\t  compilable but not finished yet\n\n2008-08-07 06:12  kobit\n\n\t* src/main/java/tigase/cluster/ServerConnectionClustered.java:\n\t  Replaced DEF_S2S_NAME with getName()\n\n2008-08-07 06:12  kobit\n\n\t* src/main/java/tigase/cluster/SessionManagerClustered.java: Fixed\n\t  bug causing NPE for 'onHold()' connection mode\n\n2008-08-07 06:11  kobit\n\n\t* win-stuff/scripts/Run.bat: Changed loading initial.properties to\n\t  init.properties\n\n2008-08-07 05:22  bmalkow\n\n\t* src/main/java/tigase/server/DisableDisco.java,\n\t  src/main/java/tigase/server/MessageRouter.java: this ugly patch\n\t  allows to process Disco stanzas as normal stanzas, not by\n\t  getDisco*() methods\n\n2008-08-07 05:18  bmalkow\n\n\t* src/main/java/tigase/server/XMPPServer.java:\n\t  getImplementationVersion() should return '0.0.0-0' if manifest is\n\t  unavailable\n\n2008-08-07 05:17  bmalkow\n\n\t* src/main/java/tigase/server/Packet.java: add copying xmlns from\n\t  source stanza to error stanza\n\n2008-07-03 15:53  kobit\n\n\t* src/main/java/tigase/server/ssender/DrupalCommentsTask.java,\n\t  src/main/java/tigase/server/ssender/StanzaSender.java: Added\n\t  drupal comments notifier task\n\n2008-07-03 10:35  kobit\n\n\t* src/main/bash/users-list-moitor.sh: Corrected wording in the\n\t  welcome email\n\n2008-07-03 10:35  kobit\n\n\t* src/main/java/tigase/server/gateways/Gateway.java: is_moderated\n\t  better handling\n\n2008-07-03 00:27  kobit\n\n\t* src/main/java/tigase/server/gateways/Gateway.java: Fixes for the\n\t  Gateway to work with the new API\n\n2008-07-02 17:29  kobit\n\n\t* src/main/bash/users-list-moitor.sh: Added full path to adduser\n\t  program which is needed when run from the cron job and also mail\n\t  program is used to send the first greetings message to newly\n\t  created user account\n\n2008-07-02 09:08  kobit\n\n\t* src/main/php/drupal/modules/tigase_usrreg-5.x/tigase_usrreg.module:\n\t  Added missing semicolon\n\n2008-07-02 09:07  kobit\n\n\t* src/main/php/drupal/modules/short_news-5.x/short_news.module,\n\t  src/main/php/drupal/modules/tigase_usrreg-5.x,\n\t  src/main/php/drupal/modules/tigase_usrreg-5.x/tigase_usrreg.info,\n\t  src/main/php/drupal/modules/tigase_usrreg-5.x/tigase_usrreg.module:\n\t  Module used for adding user directories when the account is\n\t  created\n\n2008-07-01 23:10  kobit\n\n\t* src/main/java/tigase/server/sreceiver/StanzaReceiver.java: Ad-hoc\n\t  commands for task managemebt work again\n\n2008-07-01 23:04  kobit\n\n\t* src/main/bash, src/main/bash/cron-dnotify-check.sh,\n\t  src/main/bash/users-list-moitor.sh: User accounts from drupal to\n\t  system scripts\n\n2008-07-01 22:41  kobit\n\n\t* src/main/java/tigase/server/xmppsession/PacketFilter.java,\n\t  src/main/java/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqCommand.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqVersion.java,\n\t  src/main/java/tigase/xmpp/impl/ServiceDiscovery.java,\n\t  src/main/java/tigase/xmpp/impl/UrnXmppPing.java: Fixed a bug when\n\t  some packets were not routed correctly between different user\n\t  connections (resources), instead they were send back to the\n\t  sender\n\n2008-07-01 06:32  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshConnectionManager.java,\n\t  src/main/java/tigase/server/bosh/BoshIOService.java,\n\t  src/main/java/tigase/server/bosh/BoshSession.java: Added more\n\t  control over packets received from the client, if body element\n\t  name is incorrect or xmlns is incorrect or to=hostname is\n\t  incorrect the bosh returns an error message to the client and\n\t  terminates connection\n\n2008-07-01 06:31  kobit\n\n\t* src/main/java/tigase/server/sreceiver/ShortNewsPublisher.java:\n\t  Accept news without subject and put 'No subject' string if the\n\t  subject is missing\n\n2008-07-01 06:30  kobit\n\n\t* src/main/java/tigase/server/Packet.java: Added getXMLNS\n\t  convenience method\n\n2008-06-23 22:13  kobit\n\n\t* src/main/java/tigase/cluster/SessionManagerClustered.java: A fix\n\t  for slow database update in cluster mode when the user session\n\t  trasfer arrives to the node before database has been updated with\n\t  the authorization token - 2 secs for now....\n\n2008-06-21 22:14  kobit\n\n\t* src/main/java/tigase/cluster/ClientConnectionClustered.java:\n\t  Added logging information\n\n2008-06-21 22:08  kobit\n\n\t* src/main/java/tigase/conf/Configurable.java,\n\t  src/main/java/tigase/server/MessageRouterConfig.java: Added c2s\n\t  clustered implementation\n\n2008-06-21 22:06  kobit\n\n\t* src/main/java/tigase/cluster/ClientConnectionClustered.java,\n\t  src/main/java/tigase/server/ConnectionManager.java,\n\t  src/main/java/tigase/server/ServiceChecker.java: Added support\n\t  for disconnecting all connections when cluster node with\n\t  corresponding SM has been disconnected\n\n2008-06-21 16:39  kobit\n\n\t* src/main/java/tigase/cluster/SessionManagerClustered.java: user\n\t  session transfer is run with 1/2 sec delay to allow all queued\n\t  packet to be processed\n\n2008-06-21 15:42  kobit\n\n\t* src/main/java/tigase/cluster/SessionManagerClustered.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Cluster notifications are sent now from default (the first) vhost\n\n2008-06-21 15:31  kobit\n\n\t* src/main/java/tigase/cluster/SessionManagerClustered.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/main/java/tigase/xmpp/XMPPSession.java: Better calculation\n\t  session life time and added also packets forwarding for the\n\t  session being transfered\n\n2008-06-21 12:53  kobit\n\n\t* src/main/java/tigase/xmpp/XMPPIOService.java: Added XML parser\n\t  error better handling\n\n2008-06-21 12:51  kobit\n\n\t* src/main/java/tigase/xmpp/XMPPDomBuilderHandler.java,\n\t  src/main/java/tigase/xmpp/XMPPIOService.java: Added XML parser\n\t  error better handling\n\n2008-06-21 12:31  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Added log message on sending connection verify result\n\n2008-06-21 06:59  kobit\n\n\t* src/main/java/tigase/cluster/ClusterConnectionManager.java,\n\t  src/main/java/tigase/cluster/SessionManagerClustered.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Session closed after the session tranfser doesn't send unavilable\n\t  packet\n\n2008-06-20 22:37  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  handleLogin is not now called on user session transfer\n\n2008-06-20 22:04  kobit\n\n\t* src/main/java/tigase/cluster/SessionManagerClustered.java:\n\t  droping check_user_session packet when all nodes are checked\n\n2008-06-20 21:54  kobit\n\n\t* src/main/java/tigase/cluster/ClusterElement.java: Added more\n\t  logging messages and improved calculation of the next cluster\n\t  node\n\n2008-06-20 21:54  kobit\n\n\t* src/main/java/tigase/cluster/ClusterElement.java: Added more\n\t  logging messages and improved calculation of the next cluster\n\t  node\n\n2008-06-20 21:40  kobit\n\n\t* src/main/java/tigase/cluster/ClusterElement.java,\n\t  src/main/java/tigase/cluster/SessionManagerClustered.java:\n\t  Corrected stanza type settings\n\n2008-06-20 21:30  kobit\n\n\t* src/main/java/tigase/cluster/SessionManagerClustered.java: Added\n\t  detailed logging information\n\n2008-06-20 16:01  kobit\n\n\t* src/main/java/tigase/cluster/SessionManagerClustered.java:\n\t  Changed cluster notifications from headline to normal to make\n\t  them archivizable by offline message plugin\n\n2008-06-20 15:53  kobit\n\n\t* src/main/java/tigase/cluster/ClusterElement.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java: Try\n\t  without serverSession loaded\n\n2008-06-20 15:32  kobit\n\n\t* src/main/java/tigase/cluster/SessionManagerClustered.java:\n\t  Commented out packet-from usage, for now\n\n2008-06-20 15:12  kobit\n\n\t* src/main/java/tigase/cluster/ClusterConnectionManager.java:\n\t  Changed code to not pack clustered packets\n\n2008-06-20 14:57  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Changed logging parameters\n\n2008-06-20 09:29  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnections.java:\n\t  New s2s implementation supporting reuse of the incoming\n\t  connection\n\n2008-06-20 07:37  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Started support for reusing s2s connection by... google?\n\n2008-06-20 06:45  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnections.java:\n\t  Added detection for adding incoming connection when this is\n\t  outgoing in fact\n\n2008-06-19 23:58  kobit\n\n\t* src/main/java/tigase/cluster/ClusterConnectionManager.java,\n\t  src/main/java/tigase/cluster/ClusterElement.java,\n\t  src/main/java/tigase/cluster/ServerConnectionClustered.java,\n\t  src/main/java/tigase/cluster/SessionManagerClustered.java: Fixed\n\t  problem with user session transfer\n\n2008-06-19 23:35  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Added calculation s2s connections for debugging\n\n2008-06-19 23:35  kobit\n\n\t* src/main/java/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  Handling properly case when the IOService for REDIRECT command\n\t  does not exist\n\n2008-06-19 18:57  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java,\n\t  src/main/java/tigase/server/MessageRouter.java,\n\t  src/main/java/tigase/stats/StatisticsCollector.java: Fixed\n\t  retrieving disco items from components\n\n2008-06-19 14:04  kobit\n\n\t* src/main/java/tigase/server/MessageRouter.java,\n\t  src/main/java/tigase/stats/StatisticsCollector.java: Service\n\t  disovery processing corrected to call for all components based on\n\t  component id\n\n2008-06-19 13:33  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnections.java:\n\t  Fixed problem with statistics - number of open s2s connections\n\n2008-06-19 12:57  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnections.java:\n\t  Added extra logging information\n\n2008-06-19 12:42  kobit\n\n\t* src/main/java/tigase/server/MessageRouter.java: Added support for\n\t  configurable components IDs\n\n2008-06-19 10:51  kobit\n\n\t* src/main/java/tigase/server/MessageRouter.java: Fixed service\n\t  discovery problem when asking non-local domain or entity\n\n2008-06-19 10:01  kobit\n\n\t* pom.xml: tigase-utils dependency set to version 3.0.12 - opendns\n\t  workaround\n\n2008-06-19 07:13  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnections.java:\n\t  Added more loggin for s2s connections\n\n2008-06-18 06:24  kobit\n\n\t* src/main/java/tigase/cluster/SessionManagerClustered.java,\n\t  src/main/java/tigase/net/IOService.java,\n\t  src/main/java/tigase/server/Command.java,\n\t  src/main/java/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java: User\n\t  session inter-node transfer\n\n2008-06-18 05:43  kobit\n\n\t* src/main/java/tigase/cluster/SessionManagerClustered.java: User\n\t  session inter-node transfer\n\n2008-06-18 05:41  kobit\n\n\t* src/main/java/tigase/cluster/ClusterConnectionManager.java,\n\t  src/main/java/tigase/cluster/ClusterController.java,\n\t  src/main/java/tigase/cluster/ClusterMethods.java,\n\t  src/main/java/tigase/cluster/ServerConnectionClustered.java,\n\t  src/main/java/tigase/cluster/SessionManagerClustered.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/xmpp/RepositoryAccess.java,\n\t  src/main/java/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/main/java/tigase/xmpp/XMPPSession.java: Added support for\n\t  cluster method calls and querying other cluster nodes for\n\t  dialback key\n\n2008-06-17 11:21  bmalkow\n\n\t* src/main/java/tigase/xmpp/impl/PubSub.java: Plugin has moved to\n\t  tigase-pubsub project\n\n2008-06-17 09:38  kobit\n\n\t* src/main/java/tigase/cluster/ClusterConnectionManager.java,\n\t  src/main/java/tigase/cluster/ClusterController.java,\n\t  src/main/java/tigase/cluster/ClusterElement.java,\n\t  src/main/java/tigase/cluster/ClusterMethods.java,\n\t  src/main/java/tigase/cluster/ServerConnectionClustered.java,\n\t  src/main/java/tigase/cluster/SessionManagerClustered.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Added support for cluster method calls and querying other cluster\n\t  nodes for dialback key\n\n2008-06-16 10:56  kobit\n\n\t* src/main/java/tigase/server/MessageRouter.java: Infinite loop\n\t  detecting logic has changed a bit to block packets with 'error'\n\t  type only\n\n2008-06-16 09:56  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  Added pubsub plugin to the list of default plugins\n\n2008-06-16 09:53  bmalkow\n\n\t* src/main/java/tigase/xmpp/impl/PubSub.java: PEP: add notification\n\t  to account owner\n\n2008-06-16 09:51  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Fixed problem with storing incoming connections with incorrect\n\t  session id\n\n2008-06-16 09:50  kobit\n\n\t* src/main/php/drupal/modules/tigase_monitor-5.x/tigase_monitor.module:\n\t  Added statistics for s2s component\n\n2008-06-16 09:50  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java: Connection is\n\t  now tested evert 29 minutes instead of 60\n\n2008-06-16 09:38  bmalkow\n\n\t* src/main/java/tigase/xmpp/impl/PubSub.java: first simple\n\t  implementation of PEP\n\n2008-06-14 00:47  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnections.java:\n\t  Statistics added\n\n2008-06-13 19:59  kobit\n\n\t* src/main/java/tigase/cluster/ServerConnectionClustered.java,\n\t  src/main/java/tigase/conf/Configurable.java,\n\t  src/main/java/tigase/net/IOService.java,\n\t  src/main/java/tigase/server/ConnectionManager.java,\n\t  src/main/java/tigase/server/MessageRouterConfig.java,\n\t  src/main/java/tigase/server/xmppserver/ConnectionHandlerIfc.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManagerOLD.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnections.java,\n\t  src/main/java/tigase/xmpp/XMPPIOService.java: New implementation\n\t  of s2s protocol, initial version suitable for clustering\n\n2008-06-10 17:24  kobit\n\n\t* src/main/java/tigase/cluster/SessionManagerClustered.java: Fixed\n\t  a bug with adding new cluster node\n\n2008-06-10 17:14  kobit\n\n\t* src/main/java/tigase/cluster/SessionManagerClustered.java: Fixed\n\t  a bug with adding new cluster node\n\n2008-06-10 16:02  kobit\n\n\t* src/main/java/tigase/cluster/ClusterConnectionManager.java,\n\t  src/main/java/tigase/conf/Configurable.java: Changed wording in\n\t  messages and parameters\n\n2008-06-10 15:38  kobit\n\n\t* src/main/java/tigase/conf/Configurable.java,\n\t  src/main/java/tigase/server/MessageRouterConfig.java:\n\t  ClusterController it loaded automatically in cluster mode\n\n2008-06-10 14:01  kobit\n\n\t* src/main/java/tigase/cluster/ClusterConnectionManager.java,\n\t  src/main/java/tigase/cluster/ClusterElement.java,\n\t  src/main/java/tigase/cluster/SessionManagerClustered.java,\n\t  src/main/java/tigase/conf/Configurable.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Added cluster components registrator and notifications mechanism\n\n2008-06-10 14:00  kobit\n\n\t* src/main/java/tigase/cluster/ClusterController.java,\n\t  src/main/java/tigase/cluster/ClusteredComponent.java: Added\n\t  cluster components registrator and notifications mechanism\n\n2008-06-09 14:09  kobit\n\n\t* src/main/java/tigase/cluster/ClusterConnectionManager.java:\n\t  Connect all parameter name change to --cluster-connect-all\n\n2008-06-09 14:07  kobit\n\n\t* src/main/java/tigase/cluster/ClusterConnectionManager.java:\n\t  Connect all parameter added\n\n2008-06-09 13:22  kobit\n\n\t* src/main/java/tigase/cluster/ClusterConnectionManager.java: Fixed\n\t  a bug with multiple cluser nodes connections\n\n2008-06-09 13:22  kobit\n\n\t* pom.xml: Updated tigase-utils version dependency\n\n2008-06-09 12:20  kobit\n\n\t* src/main/java/tigase/cluster/SessionManagerClustered.java: Allows\n\t  for multiple cluster nodes connection on single connection\n\t  manager\n\n2008-06-09 12:19  kobit\n\n\t* src/main/java/tigase/cluster/ClusterConnectionManager.java:\n\t  Allows for multiple cluster nodes connection on single connection\n\t  manager\n\n2008-06-06 09:11  kobit\n\n\t* src/main/java/tigase/server/Packet.java,\n\t  src/main/java/tigase/xmpp/impl/VCardTemp.java: Removed direct\n\t  usage of errorResult method\n\n2008-06-06 09:06  bmalkow\n\n\t* src/main/java/tigase/server/Packet.java: add deprecated\n\t  annotation to errorResult\n\n2008-06-06 09:05  bmalkow\n\n\t* src/main/java/tigase/xmpp/impl/VCardTemp.java: change error\n\t  stanza generation way\n\n2008-06-06 09:02  bmalkow\n\n\t* src/main/java/tigase/xmpp/impl/VCardTemp.java: change error\n\t  stanza generation way\n\n2008-06-06 08:46  bmalkow\n\n\t* src/main/java/tigase/server/ErrorCondition.java,\n\t  src/main/java/tigase/server/Packet.java,\n\t  src/main/java/tigase/xmpp/Authorization.java: fix: remove\n\t  reduntant code\n\n2008-06-06 08:25  bmalkow\n\n\t* src/main/java/tigase/server/ErrorCondition.java,\n\t  src/main/java/tigase/server/Packet.java: add XEP-0086 (Error\n\t  Condition) implementation\n\n2008-06-05 19:52  kobit\n\n\t* src/main/java/tigase/cluster/ClusterElement.java,\n\t  src/main/java/tigase/cluster/SessionManagerClustered.java:\n\t  Cluster packets processing fixes\n\n2008-06-05 19:52  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Added XMLNS for all\n\t  created presence stanzas\n\n2008-06-05 18:42  kobit\n\n\t* src/main/java/tigase/cluster/ClusterConnectionManager.java: Fixed\n\t  routings for cluster connection manager bug\n\n2008-06-05 18:25  kobit\n\n\t* src/main/java/tigase/cluster/ClusterConnectionManager.java: New\n\t  routings for cluster connection manager are set properly\n\n2008-06-05 16:42  kobit\n\n\t* src/main/java/tigase/cluster/ClusterConnectionManager.java:\n\t  Setting correct remote host address for a node to connect to\n\n2008-06-05 16:39  kobit\n\n\t* src/main/java/tigase/cluster/ClusterConnectionManager.java,\n\t  src/main/java/tigase/server/ConnectionManager.java: Setting\n\t  correct remote host address for a node to connect to\n\n2008-06-05 13:54  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java: Added more\n\t  detailed logging info\n\n2008-06-05 13:42  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java: Added more\n\t  detailed logging info\n\n2008-06-05 13:35  kobit\n\n\t* src/main/java/tigase/cluster/ClusterConnectionManager.java:\n\t  Cleaned up the removing routing for disconnected service\n\n2008-06-05 12:41  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java: Replaced\n\t  integer numbers with constatnts\n\n2008-06-05 12:41  kobit\n\n\t* src/main/java/tigase/server/MessageRouterConfig.java: Moved\n\t  default component names to this interface to make it easier to\n\t  access them from other code places\n\n2008-06-05 12:40  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  Moved cluster-nodes parameter out to Configurable ifc for use in\n\t  other places\n\n2008-06-05 12:39  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshConnectionManager.java,\n\t  src/main/java/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  Moved default component names to this interface to make it easier\n\t  to access them from other code places\n\n2008-06-05 12:39  kobit\n\n\t* src/main/java/tigase/server/Packet.java: Setting\n\t  isServiceDiscovery flag for packet to speed packet processing up\n\n2008-06-05 12:38  kobit\n\n\t* src/main/java/tigase/conf/Configurable.java: Moved default\n\t  component names to this interface to make it easier to access\n\t  them from other code places\n\n2008-06-05 12:37  kobit\n\n\t* src/main/java/tigase/cluster/SessionManagerClustered.java:\n\t  Loading default cluster nodes list from properties file\n\n2008-06-05 12:37  kobit\n\n\t* src/main/java/tigase/cluster/ClusterConnectionManager.java:\n\t  Cluster nodes connectivity component\n\n2008-06-05 12:36  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/server/MessageRouter.java,\n\t  src/main/java/tigase/stats/StatisticsCollector.java: Service\n\t  Discovery handling improvements\n\n2008-06-04 17:55  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshSession.java: Changed links\n\t  generation from _top to _blank\n\n2008-06-04 15:19  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/server/MessageRouter.java,\n\t  src/main/java/tigase/stats/StatisticsCollector.java: Fixed\n\t  service discovert and ad-hoc command processing\n\n2008-06-04 12:22  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Fixed incorreclty handled writeRawData method\n\n2008-06-04 12:22  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java: Added proper\n\t  handling for writing raw data to the socket and fixed method:\n\t  writePacketsToSocket\n\n2008-06-04 12:21  kobit\n\n\t* src/main/java/tigase/server/ServerComponent.java: Added\n\t  getComponentId() method\n\n2008-06-04 12:20  kobit\n\n\t* src/main/java/tigase/server/MessageRouter.java: Major code\n\t  cleanup and improved routing selecting logic\n\n2008-06-04 12:20  kobit\n\n\t* src/main/java/tigase/server/AbstractComponentRegistrator.java:\n\t  Added component ID support\n\n2008-06-04 12:19  kobit\n\n\t* src/main/java/tigase/server/MessageRouterConfig.java: Added\n\t  cluster mode stuff\n\n2008-06-04 12:19  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshConnectionManager.java:\n\t  Added writeRawData(...) method to the handler\n\n2008-06-04 12:19  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshSessionTaskHandler.java:\n\t  Added writeRawData(...) method to the handler\n\n2008-06-04 12:18  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshSession.java: Fixed\n\t  incorrectly called writeRawData(...)\n\n2008-06-04 12:17  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java: Code\n\t  changes to reflect API changes, processPacket(packet, results)\n\t  can not be overwritten anymore, all stuff must be done in\n\t  processPacket(packet) now\n\n2008-06-04 12:17  kobit\n\n\t* src/main/java/tigase/server/sreceiver/StanzaReceiver.java: Code\n\t  changes to reflect API changes, processPacket(packet, results)\n\t  can not be overwritten anymore, all stuff must be done in\n\t  processPacket(packet) now\n\n2008-06-04 12:15  kobit\n\n\t* src/main/java/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  Removed tricky stuff done when activating TLS mode, they are not\n\t  needed anymore\n\n2008-06-04 12:14  kobit\n\n\t* src/main/java/tigase/net/SocketReadThread.java: selectNow() is\n\t  only called now if there was a key canceled for the selector,\n\t  fixes old bug with infinite loop\n\n2008-06-04 12:13  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java: Added support for\n\t  cluster mode parameters and triming configuration parameters\n\n2008-06-04 12:12  kobit\n\n\t* src/main/java/tigase/server/AbstractMessageReceiver.java: Added\n\t  suport for component ID and processPacket(packet, results) is now\n\t  final\n\n2008-06-04 12:11  kobit\n\n\t* src/main/java/tigase/xmpp/impl/SimpleForwarder.java: Removed,\n\t  commented out, unused code\n\n2008-06-04 12:10  kobit\n\n\t* src/main/java/tigase/cluster/SessionManagerClustered.java:\n\t  Loading cluster nodes configuration\n\n2008-06-04 12:09  kobit\n\n\t* src/main/java/tigase/conf/Configurable.java: Added constants for\n\t  cluster mode parameters\n\n2008-06-04 11:32  kobit\n\n\t* src/main/php/drupal/modules/tigase_monitor-5.x/tigase_monitor.module:\n\t  Fixed path to binary files from other than frontpage\n\n2008-05-16 08:11  kobit\n\n\t* src/main/java/tigase/cluster/ClusterElement.java,\n\t  src/main/java/tigase/cluster/SessionManagerClustered.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Cluster initial implementation ready, not tested yet, doesn't\n\t  support multiple connections for the same user yet\n\n2008-05-15 10:23  kobit\n\n\t* etc/init-mysql.properties: Added comments to the init properties\n\t  file\n\n2008-05-14 19:22  kobit\n\n\t* src/main/java/tigase/cluster/ClusterElement.java,\n\t  src/main/java/tigase/cluster/SessionManagerClustered.java,\n\t  src/main/java/tigase/server/AbstractMessageReceiver.java,\n\t  src/main/java/tigase/server/gateways/Gateway.java,\n\t  src/main/java/tigase/server/sreceiver/StanzaReceiver.java,\n\t  src/main/java/tigase/server/ssender/StanzaSender.java,\n\t  src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Refactoring and routing improvements continued..., may stop\n\t  working now but it compiles\n\n2008-05-14 14:56  kobit\n\n\t* src/main/java/tigase/cluster/SessionManagerClustered.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerClustered.java:\n\t  Class moved to different package\n\n2008-05-14 14:55  kobit\n\n\t* src/main/java/tigase/cluster/ClusterElement.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerClustered.java:\n\t  Cluster mode initial development\n\n2008-05-14 13:56  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManagerClustered.java:\n\t  Further improvements and code cleaning up to prepare sources for\n\t  cluster mode implementation\n\n2008-05-14 13:55  kobit\n\n\t* src/main/java/tigase/server/Packet.java: Further improvements and\n\t  code cleaning up to prepare sources for cluster mode\n\t  implementation\n\n2008-05-14 13:53  kobit\n\n\t* src/main/java/tigase/cluster/ClusterElement.java,\n\t  src/main/java/tigase/net/SocketReadThread.java,\n\t  src/main/java/tigase/server/Packet.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerClustered.java,\n\t  src/main/java/tigase/xmpp/impl/OfflineMessages.java,\n\t  src/main/java/tigase/xmpp/impl/VCardTemp.java: Further\n\t  improvements and code cleaning up to prepare sources for cluster\n\t  mode implementation\n\n2008-05-13 15:42  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/server/AbstractMessageReceiver.java,\n\t  src/main/java/tigase/server/MessageRouter.java,\n\t  src/main/java/tigase/server/ServerComponent.java,\n\t  src/main/java/tigase/server/bosh/BoshConnectionManager.java,\n\t  src/main/java/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerClustered.java,\n\t  src/main/java/tigase/stats/StatisticsCollector.java,\n\t  src/main/java/tigase/xmpp/impl/VCardTemp.java: Message routing\n\t  code cleanup and performance improvements, breaks the current/old\n\t  config filessvn status\n\n2008-05-13 10:17  bmalkow\n\n\t* src/main/java/tigase/xmpp/impl/VCardTemp.java: add: returning IQ\n\t  error when vcard not found\n\n2008-05-12 21:40  kobit\n\n\t* pom.xml: tigase-utils dependencies updated\n\n2008-05-12 10:50  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshSession.java,\n\t  src/main/java/tigase/server/bosh/BoshSessionCache.java:\n\t  Implementation of keeping chat history in the Bosh cache\n\n2008-05-12 10:50  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Replaces presence\n\t  string with constant\n\n2008-05-12 09:16  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerClustered.java:\n\t  Cluster code cleanup\n\n2008-05-09 18:01  kobit\n\n\t* src/main/java/tigase/xmpp/impl/OfflineMessages.java: Fixed\n\t  compilation error\n\n2008-05-09 17:58  kobit\n\n\t* src/main/java/tigase/xmpp/impl/OfflineMessages.java: Fixed a bug\n\t  with saving messages with an empty body\n\n2008-05-07 19:39  kobit\n\n\t* etc/tigase-mysql.conf: Updated sample configuration for mysql\n\t  database\n\n2008-05-07 19:31  kobit\n\n\t* etc/init-mysql.properties, etc/tigase-mysql.conf: Updated sample\n\t  configuration for mysql database\n\n2008-05-07 19:30  kobit\n\n\t* src/main/java/tigase/net/SocketReadThread.java: Corrected logging\n\t  messages slightly\n\n2008-05-02 13:43  kobit\n\n\t* src/main/java/tigase/net/SocketReadThread.java: Recreating\n\t  selector on IO error\n\n2008-05-02 13:43  kobit\n\n\t* src/main/java/tigase/xmpp/impl/AnonymousRoster.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java,\n\t  src/main/java/tigase/xmpp/impl/VCardTemp.java: Proper handling\n\t  error type packets\n\n2008-05-02 13:42  kobit\n\n\t* src/main/java/tigase/cluster,\n\t  src/main/java/tigase/cluster/ClusterElement.java,\n\t  src/main/java/tigase/server/Packet.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerClustered.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  Initial full cluster mode implementation\n\n2008-05-01 16:49  kobit\n\n\t* src/main/java/tigase/net/SocketReadThread.java: Recreating the\n\t  selector on empty selection loop\n\n2008-05-01 08:17  kobit\n\n\t* src/main/java/tigase/net/SocketReadThread.java: A fix for\n\t  concurrent modification exception\n\n2008-05-01 08:06  kobit\n\n\t* src/main/java/tigase/net/SocketReadThread.java: A fix for\n\t  concurrent modification exception\n\n2008-04-30 22:10  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Anonymous messages can not go outside the server installation\n\n2008-04-30 21:16  kobit\n\n\t* src/main/java/tigase/net/SocketReadThread.java: Selector select 0\n\t  keys, workaround for JVM bugs:\n\t  http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4850373 and\n\t  http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6403933\n\n2008-04-30 08:12  kobit\n\n\t* src/main/java/tigase/net/SocketReadThread.java: Better handling\n\t  for CanceledKeyException\n\n2008-04-29 16:45  kobit\n\n\t* src/main/php/drupal/modules/tigase_monitor-5.x/tigase_monitor.module:\n\t  Added running server version displaying to the monitor\n\n2008-04-29 16:45  kobit\n\n\t* src/main/java/tigase/net/SocketReadThread.java:\n\t  CancelledKeyException proper handling\n\n2008-04-27 15:13  kobit\n\n\t* src/main/java/tigase/net/IOService.java: Force stop on write data\n\t  exception\n\n2008-04-25 21:28  kobit\n\n\t* pom.xml: xmltools dependencies updated\n\n2008-04-25 16:45  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java: Fixed problem with\n\t  returning not real-online presence to anonymouse user\n\n2008-04-25 16:35  kobit\n\n\t* src/main/java/tigase/net/SocketReadThread.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRoster.java: Sending\n\t  initial presence to anonymouse user and added some more logging\n\t  info\n\n2008-04-25 16:08  kobit\n\n\t* src/main/java/tigase/net/IOService.java: Infinite loop with empty\n\t  read calls fix - network problems?\n\n2008-04-25 14:41  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/main/java/tigase/xmpp/impl/AnonymousRoster.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java,\n\t  src/main/java/tigase/xmpp/impl/Roster.java: Anonymous roster and\n\t  presence changed, they are now based on the direct presence and\n\t  can be easily adjusted to use for different users\n\n2008-04-24 22:01  kobit\n\n\t* src/main/java/tigase/net/SocketReadThread.java: Added handling\n\t  issue described here:\n\t  http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4850373\n\n2008-04-23 23:20  kobit\n\n\t* src/main/java/tigase/xmpp/XMPPIOService.java: Added forceStop()\n\t  when socket read was called but the service is not connected\n\n2008-04-23 23:12  kobit\n\n\t* src/main/java/tigase/xmpp/impl/DynamicRosterIfc.java: Added\n\t  JavaDoc documentationf or the interfacce\n\n2008-04-23 23:11  kobit\n\n\t* src/main/java/tigase/util/RepositoryUtils.java,\n\t  src/main/java/tigase/xmpp/impl/Roster.java: Added roster\n\t  export/import functionality\n\n2008-04-23 07:34  kobit\n\n\t* src/main/java/tigase/net/IOService.java,\n\t  src/main/java/tigase/net/SocketReadThread.java,\n\t  src/main/java/tigase/xmpp/XMPPIOService.java: Replaced stop()\n\t  with forceStop() on exception catch\n\n2008-04-22 21:44  kobit\n\n\t* etc/init.properties: Added anonymous roster handler\n\n2008-04-22 21:32  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/xmpp/impl/AnonymousRoster.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java: Implemented online\n\t  status sending between anonymous users and anonymous peers\n\n2008-04-22 20:15  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerConfig.java,\n\t  src/main/java/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/main/java/tigase/xmpp/impl/RosterPresence.java: Anonymous\n\t  login - dynamic roster update and presence sending\n\n2008-04-22 20:14  kobit\n\n\t* src/main/java/tigase/net/SocketReadThread.java: more detailed\n\t  logging to see what is really going on in there\n\n2008-04-21 11:02  kobit\n\n\t* pom.xml: Dependency for xmltools updated\n\n2008-04-21 09:19  kobit\n\n\t* src/main/java/tigase/server/xmppsession/PacketFilter.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Ignoring certain packet to avoid processing them -\n\t  stream:features with error type which came back from the\n\t  connection manager component after the connection has been lost\n\t  but before the features has been delivered\n\n2008-04-19 20:13  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Fixed a bug when message sent to the domain address was not\n\t  forwarded automaticaly to the server admins\n\n2008-04-19 20:12  kobit\n\n\t* src/main/php/drupal/modules/tigase_monitor-5.x/tigase_monitor.module:\n\t  Removed double forward slash from the download link\n\n2008-04-19 20:12  kobit\n\n\t* src/main/java/tigase/server/MessageRouter.java,\n\t  src/main/java/tigase/server/MessageRouterConfig.java,\n\t  src/main/java/tigase/util/UpdatesChecker.java: Updates checker\n\t  initial implementation\n\n2008-04-17 10:44  kobit\n\n\t* src/main/java/tigase/xmpp/RepositoryAccess.java: Fixed bug with\n\t  missing ANONYMOUS mech\n\n2008-04-17 10:31  kobit\n\n\t* src/main/java/tigase/xmpp/XMPPIOService.java: Added missing\n\t  import statement\n\n2008-04-17 10:30  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Fixed the bug causing infinite stream of packets sent to the\n\t  client and Checking isLoggable before running expensive\n\t  packet.toString for a better performance\n\n2008-04-17 10:29  kobit\n\n\t* src/main/java/tigase/server/MessageRouter.java: Checking\n\t  isLoggable before running expensive packet.toString for a better\n\t  performance\n\n2008-04-17 10:29  kobit\n\n\t* src/main/java/tigase/server/xmppsession/PacketFilter.java:\n\t  Checking isLoggable before running expensive packet.toString for\n\t  a better performance\n\n2008-04-17 10:28  kobit\n\n\t* src/main/java/tigase/net/IOService.java: Added one more log\n\t  message\n\n2008-04-17 10:28  kobit\n\n\t* src/main/java/tigase/xmpp/XMPPIOService.java: Checking isLoggable\n\t  before running expensive packet.toString for a better performance\n\n2008-04-17 10:25  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Checking isLoggable\n\t  before running expensive packet.toString for a better performance\n\n2008-04-16 20:39  kobit\n\n\t* build.properties, src/main/java/tigase/db/jdbc/DrupalAuth.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Anonymous login added\n\n2008-04-16 11:16  kobit\n\n\t* pom.xml: Dependency for tigase-utils updated\n\n2008-04-16 08:48  kobit\n\n\t* pom.xml: XMLTools depedency change\n\n2008-04-15 21:27  kobit\n\n\t* pom.xml: [maven-release-plugin] prepare for next development\n\t  iteration\n\n2008-04-15 21:27  kobit\n\n\t* pom.xml: [maven-release-plugin] prepare release\n\t  tigase-server-3.3.2\n\n2008-04-15 21:22  kobit\n\n\t* src/main/java/tigase/auth/SaslAnonymous.java,\n\t  src/main/java/tigase/auth/TigaseSaslProvider.java,\n\t  src/main/java/tigase/auth/TigaseSaslServerFactory.java,\n\t  src/main/java/tigase/server/Permissions.java,\n\t  src/main/java/tigase/server/xmppsession/PacketFilter.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerConfig.java,\n\t  src/main/java/tigase/xmpp/RepositoryAccess.java,\n\t  src/main/java/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/main/java/tigase/xmpp/impl/SaslAuth.java: SASL anonymous\n\t  added and the whole support for anonymous users - still in\n\t  development\n\n2008-04-15 21:20  kobit\n\n\t* src/main/java/tigase/server/gateways/Gateway.java,\n\t  src/main/java/tigase/server/gateways/GatewayConnection.java,\n\t  src/main/java/tigase/server/gateways/GatewayListener.java: Added\n\t  javadoc\n\n2008-04-03 08:40  kobit\n\n\t* src/main/java/tigase/db/jdbc/DrupalAuth.java,\n\t  src/main/java/tigase/db/jdbc/JDBCRepository.java,\n\t  src/main/java/tigase/db/jdbc/LibreSourceAuth.java,\n\t  src/main/java/tigase/server/sreceiver/ShortNewsPublisher.java,\n\t  src/main/java/tigase/server/ssender/DrupalForumTask.java,\n\t  src/main/java/tigase/server/ssender/JDBCTask.java: changes select\n\t  localtime to select 1\n\n2008-04-02 13:28  kobit\n\n\t* src/main/java/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  Changed attributes order in a stream open element in order to\n\t  make it compatible with nagios monitoring tool\n\n2008-03-31 20:11  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Roster.java: Properly handle the\n\t  case when buddy name is not set, is it possible at all???\n\n2008-03-27 07:07  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshConnectionManager.java,\n\t  src/main/java/tigase/server/bosh/BoshIOService.java: On SID not\n\t  found error, the bosh component now sends 404 error code\n\n2008-03-27 07:07  kobit\n\n\t* src/main/java/tigase/net/IOService.java: Graceful stop\n\t  implemented - the actual stop is performed when all data have\n\t  been sent\n\n2008-03-27 06:37  kobit\n\n\t* scripts/repo.sh: Slightly modified initial parameters in the\n\t  script\n\n2008-03-27 06:36  kobit\n\n\t* ant-definitions.xml, win-stuff/Tigase.iss,\n\t  win-stuff/wrapper/wrapper.conf: Modified binary build scripts in\n\t  order to add all new files, libraries, certificates and scripts\n\n2008-03-27 06:35  kobit\n\n\t* src/main/java/tigase/util/RepositoryUtils.java: Trimming data\n\t  before loading to database\n\n2008-03-27 06:30  kobit\n\n\t* src/main/java/tigase/util/RepositoryUtils.java: Added import user\n\t  data (roster) function from comma separated flat file, format is:\n\t  user_jid, password, roser_jid, roster_nick, subscription, group\n\n2008-03-20 17:19  kobit\n\n\t* src/main/java/tigase/server/Packet.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/xmpp/XMPPDomBuilderHandler.java,\n\t  src/main/java/tigase/xmpp/XMPPProcessor.java: Changed code to\n\t  properly handle and use new xmltools with intern() strings\n\n2008-03-20 17:18  kobit\n\n\t* src/main/java/tigase/xmpp/impl/VCardTemp.java: Fixed log message\n\t  content\n\n2008-03-19 11:15  kobit\n\n\t* pom.xml, src/main/java/tigase/db/jdbc/JDBCRepository.java:\n\t  Changes to work with SimpleCache when it is turned off\n\n2008-03-18 23:05  kobit\n\n\t* scripts/tigase.sh: Fixed a bug for the case when TIGASE_HOME is\n\t  set manually\n\n2008-03-18 14:40  kobit\n\n\t* src/main/java/tigase/io/SocketIO.java,\n\t  src/main/java/tigase/io/TLSIO.java,\n\t  src/main/java/tigase/net/IOService.java,\n\t  src/main/java/tigase/net/SocketReadThread.java: Fixed an issue\n\t  with never ending loop when the server couldn't write the rest\n\t  portion of data to the network socket\n\n2008-03-18 14:39  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshIOService.java: Added\n\t  'Server' header to make it easier to identify that the data come\n\t  from tigase bosh component\n\n2008-03-17 12:59  kobit\n\n\t* src/main/java/tigase/server/MessageRouter.java: Added statistics\n\t  to display memory usage by the server\n\n2008-03-16 08:14  kobit\n\n\t* src/main/java/tigase/net/ConnectionOpenThread.java,\n\t  src/main/java/tigase/net/SocketReadThread.java: Main worker\n\t  threads are now protected from unexpected finishing work on\n\t  exception\n\n2008-03-15 15:33  kobit\n\n\t* src/main/java/tigase/net/SocketReadThread.java: Catchinh all\n\t  exceptions happening at Selector.select() on 64bit arch\n\n2008-03-14 20:25  kobit\n\n\t* src/main/java/tigase/net/SocketReadThread.java: Catching up\n\t  CancelledKeyException which should not happen according to JAVA\n\t  API but it happens on Apple JVM, a bug?\n\n2008-03-14 20:23  kobit\n\n\t* src/main/java/tigase/xmpp/impl/SaslAuth.java: Closing connection\n\t  after unsuccessful authentication\n\n2008-03-14 20:23  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqAuth.java: Closing\n\t  connection after unsuccessful authentication\n\n2008-03-13 21:44  kobit\n\n\t* database/mysql-message-archive-schema.sql,\n\t  src/main/java/tigase/xmpp/impl/xep0136/MessageArchiveDB.java:\n\t  Message archive under development\n\n2008-03-07 16:42  kobit\n\n\t* src/main/java/tigase/db/RepositoryFactory.java: XMLRepository\n\t  initialization bug, it was loaded twice, once by UserRepo and\n\t  another by UserAuthRepo, accessing the same file from different\n\t  threads\n\n2008-03-07 16:41  kobit\n\n\t* src/main/java/tigase/net/ConnectionOpenThread.java: Catching up\n\t  socket exception in case it is closed immediately after it had\n\t  been opened before we start configuring the socket\n\n2008-03-05 17:28  kobit\n\n\t* src/main/java/tigase/util/RepositoryUtils.java: Rosetr cleaning\n\t  up function added\n\n2008-03-05 17:28  kobit\n\n\t* src/main/java/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/xmpp/XMPPResourceConnection.java: Added\n\t  xml:lang default stream value to the user session\n\n2008-03-05 17:26  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshIOService.java,\n\t  src/main/java/tigase/server/bosh/BoshSession.java,\n\t  src/main/java/tigase/server/bosh/BoshSessionCache.java,\n\t  src/main/java/tigase/server/bosh/Constants.java: Added resource\n\t  binding to the automatic Bosh caching\n\n2008-03-05 08:04  kobit\n\n\t* pom.xml: tigase-utils dependency update for version 3.0.5\n\n2008-03-05 07:59  kobit\n\n\t* pom.xml: tigase-utils dependency update for version 3.0.4\n\n2008-03-04 16:21  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Initial presence\n\t  processing bug fixed - the server didn't look in the dynamic\n\t  roster\n\n2008-03-04 11:36  kobit\n\n\t* database/mysql-message-archive-schema.sql,\n\t  src/main/java/tigase/xmpp/impl/xep0136/MessageArchiveDB.java:\n\t  MessageArchive in development\n\n2008-03-04 11:36  kobit\n\n\t* src/main/java/tigase/db/jdbc/JDBCRepository.java: New, improved\n\t  SimpleCache in use, caching data for 1minute only\n\n2008-03-04 11:35  kobit\n\n\t* pom.xml: Dependecy for tigase-utils change\n\n2008-03-03 13:23  kobit\n\n\t* pom.xml: Dependency for tigase-utils updated to utils ver. 3.0.2\n\n2008-03-03 13:13  kobit\n\n\t* src/main/java/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRegister.java: Commented\n\t  out user logout after calling unregister, instead the plugin\n\t  sends 'stop' to c2s component and then the disconnection finishes\n\t  correctly informing the client about successul unregistration\n\t  frst\n\n2008-03-02 10:08  kobit\n\n\t* database/mysql-schema.sql, etc/init.properties,\n\t  etc/initial.properties: Fixed sample files\n\n2008-03-02 10:08  kobit\n\n\t* database/mysql-message-archive-schema.sql,\n\t  scripts/user_roster.sh, src/main/java/tigase/xmpp/impl/xep0136,\n\t  src/main/java/tigase/xmpp/impl/xep0136/MessageArchive.java,\n\t  src/main/java/tigase/xmpp/impl/xep0136/MessageArchiveDB.java:\n\t  Message Archiving initial code\n\n2008-03-02 10:07  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqRoster.java: Fixed problem\n\t  with displaying roster entries which are not in any group\n\n2008-02-26 21:02  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshSession.java,\n\t  src/main/java/tigase/server/bosh/BoshSessionCache.java,\n\t  src/main/java/tigase/server/bosh/Constants.java: Added Bosh\n\t  Session cache implementation\n\n2008-02-21 12:27  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Fixed a bug related\n\t  to presence probe and dynamic roster - tigase used to send\n\t  forbidden\n\n2008-02-21 10:31  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java: Initial port\n\t  open delay increased to 10sec\n\n2008-02-21 09:50  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqPrivacy.java: Fixed NPE\n\t  when it tried to retrieve contact groups and the contact wasn't\n\t  in any group\n\n2008-02-19 23:27  kobit\n\n\t* src/main/java/tigase/server/MessageRouter.java: Fixed problem\n\t  with responding to service disco for error type packets\n\n2008-02-15 08:58  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  Fixed the problem with incorrect loading multiple parameters to\n\t  the Map for initRepository\n\n2008-02-14 17:55  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Subscription to\n\t  yourself problem fixed - ticket #84\n\n2008-02-14 10:22  kobit\n\n\t* src/main/java/tigase/db/DummyRepository.java,\n\t  src/main/java/tigase/db/RepositoryFactory.java,\n\t  src/main/java/tigase/db/UserAuthRepository.java,\n\t  src/main/java/tigase/db/UserAuthRepositoryImpl.java,\n\t  src/main/java/tigase/db/UserRepository.java,\n\t  src/main/java/tigase/db/jdbc/DrupalAuth.java,\n\t  src/main/java/tigase/db/jdbc/JDBCRepository.java,\n\t  src/main/java/tigase/db/jdbc/LibreSourceAuth.java,\n\t  src/main/java/tigase/db/xml/XMLRepository.java,\n\t  src/main/java/tigase/server/gateways/Gateway.java,\n\t  src/main/java/tigase/server/sreceiver/StanzaReceiver.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerConfig.java,\n\t  src/main/java/tigase/util/RepositoryUtils.java: Added an extra\n\t  Map parameter to the UserRepository and AuthRepository interfaces\n\n2008-02-14 10:20  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshSession.java: To speed up\n\t  processing the bosh session remembers now last received RID to\n\t  compare it with the next rid\n\n2008-02-10 16:29  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshSession.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/xmpp/XMPPSession.java,\n\t  src/main/java/tigase/xmpp/impl/RosterPresence.java: On page\n\t  reload in Bosh session disapears, actually it happens every time\n\t  there is anotherconnection made with the same resource while the\n\t  old connection hasn't been closed yet\n\n2008-02-08 13:21  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshIOService.java,\n\t  src/main/java/tigase/server/bosh/BoshSession.java: Proper RID\n\t  handling and ACK implementation added\n\n2008-02-08 13:21  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqRoster.java: A workaround\n\t  for the static roster, it is now ignoring all contacts which are\n\t  in 'Upline Support' group or is not in any group at all\n\n2008-02-08 13:20  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Roster.java: Performance\n\t  improvement - isSubscribed static method which now takes a single\n\t  parameter SubscriptionType and determines whether it is\n\t  subscribedfrom type\n\n2008-02-08 13:18  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: A fix for the case\n\t  when the same contact appears in the static and dynamic roster,\n\t  the dynamic roster should and now does it, overwrite settings in\n\t  the static roster\n\n2008-02-02 14:15  kobit\n\n\t* prj.el,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerConfig.java,\n\t  src/main/java/tigase/xmpp/impl/BindResource.java,\n\t  src/main/java/tigase/xmpp/impl/IBB.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqCommand.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqIq.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqOOB.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqPrivacy.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqPrivate.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRegister.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqSi.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqSocks5Bytestreams.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqStats.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqVersion.java,\n\t  src/main/java/tigase/xmpp/impl/Jingle.java,\n\t  src/main/java/tigase/xmpp/impl/Message.java,\n\t  src/main/java/tigase/xmpp/impl/OfflineMessages.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java,\n\t  src/main/java/tigase/xmpp/impl/Roster.java,\n\t  src/main/java/tigase/xmpp/impl/RosterPresence.java,\n\t  src/main/java/tigase/xmpp/impl/SaslAuth.java,\n\t  src/main/java/tigase/xmpp/impl/ServiceDiscovery.java,\n\t  src/main/java/tigase/xmpp/impl/SessionBind.java,\n\t  src/main/java/tigase/xmpp/impl/StartTLS.java,\n\t  src/main/java/tigase/xmpp/impl/UrnXmppPing.java,\n\t  src/main/java/tigase/xmpp/impl/VCardTemp.java: Added new plugin -\n\t  RosterPresence which replaces two old plugins: Presence and\n\t  JabberIqRoster to avoid concurrent execution of those 2 stanza\n\t  types. This solves the problem with adding new contacts on\n\t  multi-cpu or multi-core machines, Arrays.copyOf statement has\n\t  been removed from all plugins and it is not needed and affect\n\t  performance\n\n2008-01-29 18:13  kobit\n\n\t* src/main/java/tigase/server/MessageRouter.java,\n\t  src/main/java/tigase/server/MessageRouterConfig.java: Disco name\n\t  is now configurable and you can turn off showing the server\n\t  version\n\n2008-01-29 17:12  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: The subscription\n\t  presence is now send with correct 'from' address without resource\n\t  part which caused adding the user with resource to the roster in\n\t  some client/server combination\n\n2008-01-26 18:43  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Fixed the problem\n\t  with missing unavailable presence from the server when the user\n\t  is unsubscribed and the default available presence sent when user\n\t  has been subscribed\n\n2008-01-26 15:26  kobit\n\n\t* build.properties, pom.xml: Version number change to 3.3.2\n\n2008-01-26 14:33  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshSession.java:\n\t  connections.size() is tested against 'hold_requests' variable\n\t  instead of 'concurrent_reuqests'\n\n2008-01-26 14:20  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshSession.java:\n\t  connections.size() is tested against 'concurrent_requests'\n\t  variable instead of '1'\n\n2008-01-26 14:02  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshSession.java: Closing the\n\t  session after received terminate from the client\n\n2008-01-26 13:34  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshSession.java: Corrected a\n\t  timeout bug - server was using incorrect inactivity timeout\n\n2008-01-26 11:48  kobit\n\n\t* src/main/java/tigase/server/bosh/Constants.java,\n\t  src/main/java/tigase/xmpp/impl/Roster.java: For broken data in\n\t  database there might be no buddy subscription info, in such case\n\t  the subscription is updated to none\n\n2008-01-26 10:27  kobit\n\n\t* pom.xml, src/main/java/tigase/server/bosh/Constants.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRoster.java: Updated\n\t  xmltools dependency for version 3.0.4\n\n2008-01-25 11:16  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Added support for\n\t  pending_in subscriptions\n\n2008-01-25 11:16  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Roster.java: Added support for\n\t  pending_in subscriptions\n\n2008-01-21 12:04  kobit\n\n\t* src/main/java/tigase/xmpp/impl/OfflineMessages.java: Null pointer\n\t  catched\n\n2008-01-21 10:46  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshConnectionManager.java:\n\t  Stopping connection with invalid SID\n\n2008-01-21 10:30  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshConnectionManager.java,\n\t  src/main/java/tigase/server/bosh/BoshSession.java: Closing\n\t  connection on logout implemented\n\n2008-01-21 10:24  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshConnectionManager.java,\n\t  src/main/java/tigase/server/bosh/BoshSession.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Closing connection on logout implemented\n\n2008-01-21 09:41  kobit\n\n\t* src/main/java/tigase/xmpp/XMPPSession.java: Old resource\n\t  connection logout corrected\n\n2008-01-20 02:40  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshSession.java: Links target\n\t  set to _top\n\n2008-01-19 16:20  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshSession.java: Added links\n\t  conversion to clickable URLs\n\n2008-01-17 23:18  kobit\n\n\t* build.properties, pom.xml: Version number change to 3.3.1\n\n2008-01-15 22:15  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Added one more logging message\n\n2008-01-15 22:15  kobit\n\n\t* src/main/java/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  Fixed problem with system commands send to incorrect session\n\t  manager\n\n2008-01-15 22:14  kobit\n\n\t* src/main/java/tigase/xmpp/XMPPResourceConnection.java: Added\n\t  stanza ID generator in resource session to use by all plugins and\n\t  processors\n\n2008-01-15 09:22  kobit\n\n\t* src/main/java/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  Modified default configuration generator to not add default\n\t  routings if routing table already exist\n\n2008-01-15 00:44  kobit\n\n\t* src/main/java/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  Fixed an old bug with routings resolution, the old code tried to\n\t  resolve routings based on packet address while it should look at\n\t  the domain settings for the connection\n\n2008-01-14 22:19  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java: Moved\n\t  synchronization on connection resource from SM to Presence\n\t  plugin, to not blog the whole processing because of Presence\n\t  plugin constraints\n\n2008-01-14 13:16  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Fixed permission for result packets problem which prevented\n\t  admins to access the ad-hoc commands\n\n2008-01-13 23:40  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Fixed a bug with synchronization on connection resource\n\n2008-01-13 23:32  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Added proper synchronization on connection resource to avoid\n\t  conflict with accessing the object during logout by many threads\n\t  concurrently\n\n2008-01-13 23:31  kobit\n\n\t* src/main/java/tigase/server/gateways/Gateway.java: Added removal\n\t  of gateway connection on logout\n\n2008-01-13 23:31  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Corrected wrong log\n\t  message\n\n2008-01-13 22:41  kobit\n\n\t* src/main/java/tigase/server/gateways/Gateway.java: Added proper\n\t  handling multiple user connections to the transport\n\n2008-01-13 22:28  kobit\n\n\t* src/main/java/tigase/server/gateways/Gateway.java: Optimized\n\t  logout handler\n\n2008-01-13 22:07  kobit\n\n\t* build.properties, pom.xml,\n\t  src/main/java/tigase/server/gateways/Gateway.java,\n\t  src/main/java/tigase/server/gateways/GatewayConnection.java,\n\t  src/main/java/tigase/server/gateways/GatewayListener.java:\n\t  Gateway API has been changed, version change to 3.3.0\n\n2008-01-13 21:03  kobit\n\n\t* pom.xml: Version number change to 3.2.1\n\n2008-01-13 21:02  kobit\n\n\t* build.properties,\n\t  src/main/java/tigase/server/gateways/Gateway.java: Indentation\n\t  corrected\n\n2008-01-13 21:02  kobit\n\n\t* src/main/java/tigase/net/SocketReadThread.java,\n\t  src/main/java/tigase/server/ConnectionManager.java: Fixed problem\n\t  with sending packets in multithreading code\n\n2008-01-10 07:09  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Reverted back code,\n\t  now presences from not-subscribed IDs are NOT ignored\n\n2008-01-09 00:11  kobit\n\n\t* src/main/java/tigase/net/IOService.java,\n\t  src/main/java/tigase/server/bosh/BoshSession.java: Deadlock\n\t  problem solved, replaced all synchronized methods with\n\t  synchronized blocks\n\n2008-01-08 11:03  kobit\n\n\t* src/main/java/tigase/server/AbstractMessageReceiver.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java: Each\n\t  plugin - XMPPProcessorIfc is now executed in separate thread to\n\t  avoid slowdowns if one plugin runs slowlu due to resource\n\t  availability delays\n\n2008-01-08 11:02  kobit\n\n\t* src/main/java/tigase/db/jdbc/DrupalAuth.java,\n\t  src/main/java/tigase/db/jdbc/JDBCRepository.java,\n\t  src/main/java/tigase/db/jdbc/LibreSourceAuth.java: Changed code\n\t  to allow multithreading support\n\n2008-01-08 11:01  kobit\n\n\t* src/main/java/tigase/xmpp/RepositoryAccess.java: Removed final\n\t  keyword from methods definition, it is not needed anymore\n\n2007-12-22 23:43  kobit\n\n\t* build.properties, pom.xml: Version number change to 3.2.0\n\n2007-12-22 18:51  kobit\n\n\t* win-stuff/Tigase-minimal.iss: Configuration for minimal tigase\n\t  package\n\n2007-12-22 18:51  kobit\n\n\t* etc/tigase-mysql.conf: Initial properties moved to a property\n\t  file\n\n2007-12-22 18:48  kobit\n\n\t* prj.el: Code indetation corrected\n\n2007-12-22 18:47  kobit\n\n\t* src/main/java/tigase/io/CertFilesTrustManager.java,\n\t  src/main/java/tigase/io/SSLContextContainer.java,\n\t  src/main/java/tigase/io/SSLContextContainerIfc.java,\n\t  src/main/java/tigase/io/TLSUtil.java,\n\t  src/main/java/tigase/io/TelnetClient.java,\n\t  src/main/java/tigase/io/TelnetServer.java,\n\t  src/main/java/tigase/server/ConnectionManager.java: Added support\n\t  for pluggable SSLContextContainers for network layer\n\n2007-12-22 18:46  kobit\n\n\t* src/main/java/tigase/server/ssender/StanzaHandler.java,\n\t  src/main/java/tigase/server/ssender/StanzaSender.java: Added\n\t  handler for a single stanza packet\n\n2007-12-22 18:45  kobit\n\n\t* src/main/java/tigase/server/gateways/MsnConnection.java: Moved to\n\t  extras package\n\n2007-12-22 18:45  kobit\n\n\t* src/main/java/tigase/io/IOInterface.java,\n\t  src/main/java/tigase/io/SocketIO.java,\n\t  src/main/java/tigase/io/TLSIO.java,\n\t  src/main/java/tigase/net/IOService.java,\n\t  src/main/java/tigase/net/SocketReadThread.java: Implement fully\n\t  asynchronous communication over Java NIO\n\n2007-12-22 18:44  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java: Added support for\n\t  multiple extra components in initial.properties file, added\n\t  support for parameters types in initial.properties file\n\n2007-12-22 18:42  kobit\n\n\t* src/main/java/tigase/xmpp/impl/OfflineMessages.java: Fixed a bug\n\t  with incorrect order for off-line messages: #74\n\n2007-12-14 10:23  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  Support for plugins list in property file added\n\n2007-12-14 10:22  kobit\n\n\t* src/main/java/tigase/server/gateways/MsnConnection.java: Due to\n\t  library version change p2pMessageReceived listener had to be\n\t  added\n\n2007-12-14 10:21  kobit\n\n\t* src/main/java/tigase/conf/Configurable.java: Added --sm-plugins\n\t  parameter, so you can list plugins in property file for\n\t  configuration initialization\n\n2007-12-14 10:20  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqRoster.java: Changed xmlns\n\t  raw string to variable use\n\n2007-12-11 23:24  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Removed unused import\n\n2007-12-11 22:55  kobit\n\n\t* src/main/java/tigase/server/Command.java: Added new, predefined\n\t  commands: BROADCAST_TO_ONLINE and BROADCAST_TO_ALL\n\n2007-12-11 22:54  kobit\n\n\t* src/main/java/tigase/server/bosh/Constants.java: Added\n\t  MAX_PACKETS number to set the maximum number of queuet packets to\n\t  be send to the client\n\n2007-12-11 22:54  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshSession.java: If there is\n\t  huge number of packets waiting, they are sent in MAX_PACKETS\n\t  number at a time\n\n2007-12-11 22:52  kobit\n\n\t* src/main/java/tigase/server/Permissions.java: Added trusted user\n\t  permission\n\n2007-12-11 22:51  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Added message broadcating and trusted user permission support\n\n2007-12-11 22:50  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  Added trusted configuration settings for trusted permission which\n\t  allows user to do certain actions like broadcasting messages\n\n2007-12-11 22:50  kobit\n\n\t* src/main/java/tigase/server/gateways/MsnConnection.java: Improved\n\t  logging and roster synchronization\n\n2007-12-11 22:49  kobit\n\n\t* src/main/java/tigase/server/gateways/UserStatus.java: Corrected\n\t  user status translation, show message now contain correct XMPP\n\t  values\n\n2007-12-11 22:48  kobit\n\n\t* src/main/java/tigase/server/gateways/Gateway.java: Changed\n\t  registration message\n\n2007-12-11 22:47  kobit\n\n\t* src/main/java/tigase/conf/Configurable.java: Added trusted\n\t  configuration settings for trusted permission which allows user\n\t  to do certain actions like broadcasting messages\n\n2007-12-11 22:46  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqRoster.java: Dynamic,\n\t  large rosters are now sent in packets, 20 roster items in a\n\t  signle packet\n\n2007-12-03 13:33  kobit\n\n\t* src/main/java/tigase/xmpp/XMPPResourceConnection.java: Fixed\n\t  problem with temporary resource setting\n\n2007-12-01 22:04  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshSession.java: Added another\n\t  log entry to monitor restart command processing\n\n2007-12-01 21:45  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java: Removed an\n\t  instruction for service.stop() if there was a problem with\n\t  starting the servicce\n\n2007-12-01 21:31  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java: Added more\n\t  logging information to investigate problem with: 'attempt to stop\n\t  incorrect service: null' message\n\n2007-12-01 21:30  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshSession.java,\n\t  src/main/java/tigase/server/bosh/Constants.java: Added support\n\t  for stream restart functionality\n\n2007-12-01 21:30  kobit\n\n\t* src/main/java/tigase/xmpp/impl/SaslAuth.java: Stream features are\n\t  now returned correctly in all cases\n\n2007-12-01 21:30  kobit\n\n\t* src/main/java/tigase/xmpp/impl/BindResource.java: Stream features\n\t  are now returned correctly in all cases\n\n2007-12-01 21:29  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqAuth.java: Stream features\n\t  are now correctly returned in all cases\n\n2007-12-01 21:28  kobit\n\n\t* src/main/java/tigase/xmpp/XMPPResourceConnection.java: userJid is\n\t  now properly set after authentication\n\n2007-12-01 20:31  kobit\n\n\t* src/main/java/tigase/server/xmppsession/PacketFilter.java: Is it\n\t  really possible that getJID() returns null? let's try to catch it\n\n2007-12-01 20:30  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Added one log\n\t  message for a case when from attribute is null and commented out\n\t  initial presence delivery if the buddy is not subscribed....\n\n2007-11-30 12:30  kobit\n\n\t* src/main/java/tigase/xmpp/XMPPSession.java,\n\t  src/main/java/tigase/xmpp/impl/BindResource.java: Fix for a\n\t  problem with a new connection established for the same resource\n\t  name, the old connection wasn't properly closed and that caused\n\t  confusion on the server\n\n2007-11-28 21:29  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqRoster.java: Fixed problem\n\t  with roster requests with empty group element - group name is\n\t  empty\n\n2007-11-24 08:54  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Roster.java: Fixed the problem\n\t  with sending roster items with resource part\n\n2007-11-23 20:52  kobit\n\n\t* database/sqlserver-schema.sql: Index for tig_node table has been\n\t  changed to keep 3 fields\n\n2007-11-23 20:39  kobit\n\n\t* src/main/java/tigase/server/gateways/MsnConnection.java: On\n\t  gateway disconnection all roster elements from the gateway are\n\t  automaticaly marked as off-line now\n\n2007-11-23 20:38  kobit\n\n\t* src/main/java/tigase/server/gateways/Gateway.java: If gateway is\n\t  not connected, message received is sent back with an error\n\n2007-11-23 20:37  kobit\n\n\t* database/mysql-schema.sql, database/postgresql-schema.sql:\n\t  Modified unique index for tig_node to keep 3 fields instead of 2\n\n2007-11-23 20:37  kobit\n\n\t* src/main/java/tigase/db/jdbc/JDBCRepository.java: Added one more\n\t  log instruction\n\n2007-11-23 11:09  kobit\n\n\t* src/main/java/tigase/server/gateways/MsnConnection.java: Finally\n\t  fixed the problem with user status, bocking/unblocking\n\n2007-11-23 09:12  kobit\n\n\t* src/main/java/tigase/server/gateways/MsnConnection.java: Added\n\t  missing import and fixed compilation error\n\n2007-11-23 00:29  kobit\n\n\t* src/main/java/tigase/server/gateways/MsnConnection.java,\n\t  win-stuff/Tigase.iss: Suport for MsnList added\n\n2007-11-22 14:44  kobit\n\n\t* src/main/java/tigase/server/gateways/Gateway.java,\n\t  src/main/java/tigase/server/gateways/MsnConnection.java: Added\n\t  full support for adding, removing buddies in roster\n\n2007-11-22 14:43  kobit\n\n\t* src/main/java/tigase/server/gateways/GatewayListener.java:\n\t  formatJID, decodeLegacyName methods added\n\n2007-11-22 14:43  kobit\n\n\t* src/main/java/tigase/db/xml/XMLRepository.java: Added\n\t  synchronization and support for multithreaded use\n\n2007-11-22 14:42  kobit\n\n\t* src/main/java/tigase/server/gateways/GatewayConnection.java:\n\t  addBuddy, removeBuddy, getPromptMessage methods added\n\n2007-11-22 14:42  kobit\n\n\t* src/main/java/tigase/db/RepositoryFactory.java: Fixed a bug with\n\t  accessing XML repository by different components\n\n2007-11-22 14:32  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java: Commented out\n\t  System.out.printlns\n\n2007-11-22 14:28  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Fixed a bug with\n\t  Tigase sending available presence with unsubscribed presence\n\n2007-11-22 14:27  kobit\n\n\t* win-stuff/Tigase.iss: Character case changed\n\n2007-11-21 18:03  kobit\n\n\t* database/sqlserver-schema.sql: Database schema definition for\n\t  SQLServer\n\n2007-11-20 17:10  kobit\n\n\t* src/main/java/tigase/io/SSLContextContainerIfc.java: SSL\n\t  container interface to allow use of different container\n\t  implementation, for example implementation capable of loading\n\t  certificates from *.pem files\n\n2007-11-14 10:20  kobit\n\n\t* win-stuff/Tigase.iss: Now gateway stores roster buddy last status\n\t  in database\n\n2007-11-14 10:20  kobit\n\n\t* src/main/java/tigase/server/gateways/Gateway.java,\n\t  src/main/java/tigase/server/gateways/GatewayListener.java,\n\t  src/main/java/tigase/server/gateways/MsnConnection.java: Now\n\t  gateway stores roster buddy last status in database\n\n2007-11-14 10:19  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Roster.java: Fix for missing\n\t  subscriptions in roster coming for transports\n\n2007-11-13 23:53  kobit\n\n\t* src/main/java/tigase/server/gateways,\n\t  src/main/java/tigase/server/gateways/Gateway.java,\n\t  src/main/java/tigase/server/gateways/GatewayConnection.java,\n\t  src/main/java/tigase/server/gateways/GatewayException.java,\n\t  src/main/java/tigase/server/gateways/GatewayListener.java,\n\t  src/main/java/tigase/server/gateways/LoginGatewayException.java,\n\t  src/main/java/tigase/server/gateways/MsnConnection.java,\n\t  src/main/java/tigase/server/gateways/RosterItem.java,\n\t  src/main/java/tigase/server/gateways/UserStatus.java: Transports\n\t  initial implementation with MSN gateway\n\n2007-11-13 23:36  kobit\n\n\t* prj.el: Added more jar files\n\n2007-11-13 23:36  kobit\n\n\t* src/main/java/tigase/io/SSLContextContainer.java: Removed\n\t  System.exit(1) instruction\n\n2007-11-13 23:35  kobit\n\n\t* src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Added more logging information\n\n2007-11-13 23:35  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Removed System.exit(1) instruction\n\n2007-11-13 23:34  kobit\n\n\t* src/main/java/tigase/server/sreceiver/StanzaReceiver.java:\n\t  Removed System.exit(1) instruction\n\n2007-11-13 23:34  kobit\n\n\t* src/main/java/tigase/net/SocketReadThread.java: Removed\n\t  System.exit(1) instruction\n\n2007-11-13 23:33  kobit\n\n\t* src/main/java/tigase/net/ConnectionOpenThread.java: Removed\n\t  System.exit(1) instruction\n\n2007-11-13 23:33  kobit\n\n\t* src/main/java/tigase/db/xml/XMLRepository.java: Added support for\n\t  user autocreate mode\n\n2007-11-13 23:32  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqRegister.java: Fix for\n\t  internal transport support\n\n2007-11-13 23:31  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Roster.java: A few fixes for a\n\t  case when nick part does not exist and when current_subscription\n\t  is null\n\n2007-11-13 23:30  kobit\n\n\t* etc/tigase.conf: By default now it loads init.properties file\n\n2007-11-13 23:27  kobit\n\n\t* src/main/java/tigase/xmpp/ProcessorFactory.java: Full stack trace\n\t  output to the console as logs are not working at this stage\n\n2007-11-13 23:26  kobit\n\n\t* src/main/java/tigase/util/DBUtils.java: A few convenience methods\n\t  for repository initialization\n\n2007-11-07 22:43  kobit\n\n\t* win-stuff/Tigase.iss: Version change and etc/ content corrected\n\n2007-11-07 22:42  kobit\n\n\t* etc/initial.properties: Initial server properties, which can be\n\t  used for config generators\n\n2007-11-07 22:42  kobit\n\n\t* src/main/java/tigase/server/XMPPServer.java: Changed initial\n\t  logging configuration\n\n2007-11-07 22:41  kobit\n\n\t* win-stuff/scripts/Run.bat: Added parameter for initial properties\n\t  and changed classpath to use wildcards\n\n2007-11-07 16:35  kobit\n\n\t* src/main/java/tigase/server/XMPPServer.java: Initial\n\t  configuration generation is now outputed to the log file\n\t  correctly\n\n2007-11-07 16:34  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  Fixed initial configuration generation when database connector is\n\t  given as class name\n\n2007-11-07 16:34  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java: Added support for\n\t  initial parameters read from property file which is useful\n\t  especially on windows\n\n2007-11-07 12:30  kobit\n\n\t* src/main/java/tigase/xmpp/impl/DynamicRoster.java: Dynamic roster\n\t  classes are now comma separated list instead of string array of\n\t  class names\n\n2007-11-06 21:35  kobit\n\n\t* build.properties, win-stuff/Tigase.iss: Version change to 3.1.1\n\n2007-11-06 21:33  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Improvemements for plugins settings, plugins can share now the\n\t  same settings and stop listeners are receiving plugins settings\n\t  as a method parameter\n\n2007-11-06 21:32  kobit\n\n\t* src/main/java/tigase/xmpp/XMPPStopListenerIfc.java: Added plugin\n\t  settings to the method parameters list\n\n2007-11-06 21:27  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqRoster.java: Moved dynamic\n\t  roster initialization to separate class - DynamicRoster\n\n2007-11-06 21:26  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Added support for\n\t  dynamic rosters\n\n2007-11-06 21:26  kobit\n\n\t* src/main/java/tigase/xmpp/impl/DynamicRoster.java: Static class\n\t  with static helper methods for accessing dynamic rosters' data\n\n2007-11-03 16:49  kobit\n\n\t* win-stuff/Tigase.iss: Changed default content of etc directory\n\n2007-11-03 16:46  kobit\n\n\t* win-stuff/Licence.txt: License change to GPLv3\n\n2007-11-03 16:45  kobit\n\n\t* src/main/java/tigase/db/DummyRepository.java: Dummy user\n\t  repository\n\n2007-11-02 20:29  kobit\n\n\t* src/main/java/tigase/db/jdbc/DrupalAuth.java: JavaDoc comments\n\t  added\n\n2007-11-02 20:28  kobit\n\n\t* src/main/java/tigase/db/UserAuthRepository.java: JavaDoc comments\n\t  added\n\n2007-11-02 20:28  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqRoster.java: Improved\n\t  handling dynamic rosters\n\n2007-10-31 17:35  kobit\n\n\t* src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Corrected generating default regex routing, '.' character is now\n\t  backslashed to avoid special treatment by regex engibe\n\n2007-10-31 17:24  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqRoster.java: Fixed null\n\t  pointer exception in case of missing plugin settings for roster\n\n2007-10-31 17:12  kobit\n\n\t* prj.el, src/main/java/tigase/auth/TigaseSaslProvider.java,\n\t  src/main/java/tigase/db/jdbc/DrupalAuth.java,\n\t  src/main/java/tigase/server/AbstractMessageReceiver.java,\n\t  src/main/java/tigase/server/sreceiver/AbstractReceiverTask.java,\n\t  src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerConfig.java,\n\t  src/main/java/tigase/xmpp/XMPPProcessorIfc.java,\n\t  src/main/java/tigase/xmpp/impl/BindResource.java,\n\t  src/main/java/tigase/xmpp/impl/DynamicRosterIfc.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqCommand.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqIq.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqPrivacy.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqPrivate.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRegister.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqStats.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqVersion.java,\n\t  src/main/java/tigase/xmpp/impl/Jingle.java,\n\t  src/main/java/tigase/xmpp/impl/OfflineMessages.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java,\n\t  src/main/java/tigase/xmpp/impl/SaslAuth.java,\n\t  src/main/java/tigase/xmpp/impl/ServiceDiscovery.java,\n\t  src/main/java/tigase/xmpp/impl/SessionBind.java,\n\t  src/main/java/tigase/xmpp/impl/SimpleForwarder.java,\n\t  src/main/java/tigase/xmpp/impl/StartTLS.java,\n\t  src/main/java/tigase/xmpp/impl/UrnXmppPing.java,\n\t  src/main/java/tigase/xmpp/impl/VCardTemp.java: MNodifications for\n\t  dynamic rosters functionality\n\n2007-10-23 06:52  bmalkow\n\n\t* pom.xml, src/main/resources, src/main/resources/META-INF,\n\t  src/main/resources/META-INF/MANIFEST.MF: Add creating manifest by\n\t  maven (with version number)\n\n2007-10-18 09:03  kobit\n\n\t* pom.xml: [maven-release-plugin] prepare for next development\n\t  iteration\n\n2007-10-18 09:03  kobit\n\n\t* pom.xml: Dependencies update\n\n2007-10-18 08:57  kobit\n\n\t* scripts/repo.sh, win-stuff/Tigase.iss: Version change\n\n2007-10-17 13:08  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Corrected logic mistake in the s2s\n\n2007-10-17 12:56  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshIOService.java: Changed\n\t  strings literals to constants, might improve performance/resource\n\t  consumption slightly\n\n2007-10-17 12:54  kobit\n\n\t* src/main/java/tigase/server/AbstractMessageReceiver.java: Yet\n\t  another fix to packet counters for last second, minute, hour\n\n2007-10-17 12:54  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Changed code slightly to allow multiple s2s connections from\n\t  other servers, apparently this is correct\n\n2007-10-13 18:07  kobit\n\n\t* src/main/java/tigase/server/AbstractMessageReceiver.java:\n\t  Improved calculating number of last packets sent\n\n2007-10-13 18:06  kobit\n\n\t* src/main/java/tigase/net/ConnectionOpenThread.java: Changed\n\t  default TCP/IP packet size to 2k\n\n2007-10-13 18:06  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqPrivacy.java: Improved\n\t  retrieving privacy lists\n\n2007-10-11 23:53  kobit\n\n\t* src/main/java/tigase/server/AbstractMessageReceiver.java:\n\t  Corrected calculation of the number of packets in last second\n\n2007-10-11 23:37  kobit\n\n\t* src/main/java/tigase/server/AbstractMessageReceiver.java:\n\t  Corrected calculation of the number of packets in last second\n\n2007-10-09 16:36  kobit\n\n\t* src/main/php/drupal/modules/tigase_monitor-5.x/tigase_monitor.info,\n\t  src/main/php/drupal/modules/tigase_monitor-5.x/tigase_monitor.module:\n\t  tigase monitor updated for drupal 5.x\n\n2007-10-09 13:01  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/server/MessageRouter.java,\n\t  src/main/java/tigase/server/bosh/BoshSession.java,\n\t  src/main/java/tigase/server/sreceiver/AbstractReceiverTask.java,\n\t  src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/PacketFilter.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/xmpp/Authorization.java,\n\t  src/main/java/tigase/xmpp/PacketErrorTypeException.java,\n\t  src/main/java/tigase/xmpp/XMPPProcessorIfc.java,\n\t  src/main/java/tigase/xmpp/impl/BindResource.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqCommand.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqIq.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqPrivacy.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqPrivate.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRegister.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqStats.java,\n\t  src/main/java/tigase/xmpp/impl/Jingle.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java,\n\t  src/main/java/tigase/xmpp/impl/ServiceDiscovery.java,\n\t  src/main/java/tigase/xmpp/impl/SessionBind.java,\n\t  src/main/java/tigase/xmpp/impl/SimpleForwarder.java,\n\t  src/main/java/tigase/xmpp/impl/VCardTemp.java,\n\t  src/main/php/drupal/modules/short_news-5.x/short_news.module,\n\t  win-stuff/Tigase.iss: Authorization throws now exception if you\n\t  try to assing error type to the packet which is already an error,\n\t  it used to cause infinite loops in the server in many places.\n\t  This change aims to kill infinite loops in the server for\n\t  ever....\n\n2007-10-08 09:41  kobit\n\n\t* src/main/php/drupal/modules/short_news-5.x/short_news.module:\n\t  Modifications for compatibility with drupal-5.x\n\n2007-10-06 16:34  kobit\n\n\t* etc/tigase-mysql.conf,\n\t  src/main/php/drupal/modules/short_news-5.x,\n\t  src/main/php/drupal/modules/short_news-5.x/short_news.info,\n\t  src/main/php/drupal/modules/short_news-5.x/short_news.module,\n\t  src/main/php/drupal/modules/tigase_monitor-5.x,\n\t  src/main/php/drupal/modules/tigase_monitor-5.x/tigase_monitor.module:\n\t  Drupal v5.x modules\n\n2007-10-02 13:26  kobit\n\n\t* src/main/php/drupal/modules/tigase_monitor.module: Corrected link\n\t  description to detailed statistics\n\n2007-10-02 13:26  kobit\n\n\t* src/main/java/tigase/server/AbstractMessageReceiver.java: Added\n\t  statisics for packets processed in last second, minute and hour\n\n2007-10-02 13:25  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java: Increased\n\t  watchdog timeout from 1 min to 30 minutes, it cleans up\n\t  connections which are iddle for an hour, so there is no need for\n\t  running it every minute\n\n2007-10-02 13:24  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Catching possible authorization exception during resource set and\n\t  returning def for max queue size\n\n2007-10-02 13:23  kobit\n\n\t* src/main/java/tigase/server/xmppsession/PacketFilter.java:\n\t  Checking whether the session is authorized before processing,\n\t  eliminates generating authorization exceptions and improves\n\t  performance\n\n2007-10-02 13:22  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqPrivacy.java: Checking\n\t  whether the session is authorized before processing, eliminates\n\t  generating authorization exceptions and improves performance\n\n2007-10-02 13:21  kobit\n\n\t* src/main/java/tigase/xmpp/XMPPResourceConnection.java: userID and\n\t  userJID is cached now so it doesn't need to be generated on each\n\t  call, performance improvement\n\n2007-10-01 11:06  kobit\n\n\t* src/main/php/drupal/modules/tigase_monitor.module: Added details\n\t  and configuration page\n\n2007-09-29 22:55  kobit\n\n\t* src/main/php/drupal/modules/tigase_monitor.module: Rewriten\n\t  module to display number of registered account from the server\n\t  statistics rather than from drupal database and number of active\n\t  sessions\n\n2007-09-29 22:54  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Fixed a bug when s2s received packet without 'to' attribute due\n\t  to missconfiguration\n\n2007-09-29 22:54  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Added statustics for registered accounts number\n\n2007-09-29 22:53  kobit\n\n\t* src/main/java/tigase/server/sreceiver/StanzaReceiver.java: Fixed\n\t  a bug with task names in upper case\n\n2007-09-29 22:53  kobit\n\n\t* src/main/java/tigase/db/UserRepository.java,\n\t  src/main/java/tigase/db/jdbc/JDBCRepository.java,\n\t  src/main/java/tigase/db/xml/XMLRepository.java: Added method\n\t  returning number of user accounts in database\n\n2007-09-24 18:43  kobit\n\n\t* src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Fixed problem with packets addresses to the component which can\n\t  not be processed by the component\n\n2007-09-22 14:41  kobit\n\n\t* build.properties: Version change\n\n2007-09-22 13:24  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshSession.java,\n\t  src/main/java/tigase/xmpp/StanzaType.java: Added proper session\n\t  termination code for Bosh component\n\n2007-09-21 21:31  kobit\n\n\t* src/main/php/drupal/modules/tigase_monitor.module: Fixed problem\n\t  with calculating uptime in some cases\n\n2007-09-21 21:31  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshIOService.java: Fixed\n\t  problem with incorrectly calculated content length if data\n\t  contains multibyte UTF-8 characters\n\n2007-09-21 08:44  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshSession.java: Fixed problem\n\t  with missing version and xmlns attributes in body element\n\n2007-09-19 08:08  kobit\n\n\t* src/main/java/tigase/server/AbstractMessageReceiver.java: Changed\n\t  from HashSet to CopyOnWriteArraySet for routings tables\n\n2007-09-18 08:56  kobit\n\n\t* src/main/java/tigase/server/sreceiver/TesterTask.java: Tester\n\t  task for testing server functionality\n\n2007-09-18 08:56  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java: Spelling corrected\n\n2007-09-17 09:46  kobit\n\n\t* src/main/java/tigase/server/sreceiver/RepoRosterTask.java,\n\t  src/main/java/tigase/server/sreceiver/StanzaReceiver.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Fixed the problem when repository is not set or can't support\n\t  user autocreate mode\n\n2007-09-16 07:06  bmalkow\n\n\t* pom.xml: [maven-release-plugin] prepare for next development\n\t  iteration\n\n2007-09-16 07:06  bmalkow\n\n\t* pom.xml: [maven-release-plugin] prepare release\n\t  tigase-server-3.0.1-beta\n\n2007-09-16 01:33  kobit\n\n\t* src/main/php/drupal/modules/tigase_monitor.module: Added bosh\n\t  connections\n\n2007-09-15 16:02  kobit\n\n\t* src/main/php/drupal/modules/tigase_monitor.module: Fixed problem\n\t  with uptime when uptime is shorter than 1day\n\n2007-09-15 15:29  kobit\n\n\t* src/main/php/drupal/modules/tigase_monitor.module: Fixed problem\n\t  with uptime when uptime is shorter than 1day\n\n2007-09-15 14:06  kobit\n\n\t* win-stuff/Tigase.iss: Version change\n\n2007-09-15 14:06  kobit\n\n\t* build.properties, win-stuff/Tigase.iss: Version change\n\n2007-09-15 14:04  kobit\n\n\t* src/main/java/tigase/server/Packet.java,\n\t  src/main/java/tigase/server/bosh/BoshConnectionManager.java,\n\t  src/main/java/tigase/server/bosh/BoshSession.java,\n\t  src/main/java/tigase/server/xmppsession/PacketFilter.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Packet.toString() changed to display XMLNS\n\n2007-09-15 14:04  kobit\n\n\t* src/main/java/tigase/xmpp/XMPPDomBuilderHandler.java: Fixed minor\n\t  bug with default XMLNS settings for many children with different\n\t  XMLNSes\n\n2007-09-15 11:41  bmalkow\n\n\t* pom.xml: update dependecies\n\n2007-09-14 22:59  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshSession.java: Bosh is now\n\t  completed but untested...\n\n2007-09-14 22:03  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: presence\n\t  unavailable removes now jid from directpresence set\n\n2007-09-12 10:16  bmalkow\n\n\t* pom.xml: changes in maven group and version\n\n2007-09-12 10:04  bmalkow\n\n\t* pom.xml: changes in maven group and version\n\n2007-09-11 21:58  kobit\n\n\t* src/main/java/tigase/server/MessageRouterConfig.java: You can now\n\t  add components during configuration generation with parameters:\n\t  --comp-name and --comp-class\n\n2007-09-11 17:55  kobit\n\n\t* etc/tigase-mysql.conf,\n\t  src/main/java/tigase/server/bosh/BoshConnectionManager.java,\n\t  src/main/java/tigase/server/bosh/BoshSession.java,\n\t  src/main/java/tigase/server/bosh/Constants.java: Bosh - timeouts\n\t  implemented, untested\n\n2007-09-04 20:02  bmalkow\n\n\t* src/main/java/tigase/io/CertFilesTrustManager.java: Add\n\t  TrustManager based on PEM files, stored in (for example)\n\t  /etc/ssl/certs\n\n2007-08-09 09:03  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshConnectionManager.java,\n\t  src/main/java/tigase/server/bosh/BoshIOService.java,\n\t  src/main/java/tigase/server/bosh/BoshSession.java,\n\t  src/main/java/tigase/server/bosh/BoshSessionTaskHandler.java:\n\t  Added max_pause support, untested\n\n2007-08-03 17:45  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshConnectionManager.java,\n\t  src/main/java/tigase/server/bosh/BoshSession.java,\n\t  src/main/java/tigase/server/bosh/Constants.java: Initial, working\n\t  version of Bosh implementation\n\n2007-08-03 17:45  kobit\n\n\t* prj.el: License text changed\n\n2007-08-03 17:44  kobit\n\n\t* src/main/java/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  Packet id changed to be different from other commands\n\n2007-08-03 17:44  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqStats.java: Fixed problem\n\t  with ID retrieving in incorrect place of code\n\n2007-07-25 15:06  kobit\n\n\t* src/main/php/drupal/modules/tigase_monitor.module: Added real\n\t  statistics from tigase jabber server, these statistics are\n\t  updated every minute\n\n2007-07-21 16:36  kobit\n\n\t* src/main/php/drupal/modules/tigase_monitor.module: Link to all\n\t  downloads fixed\n\n2007-07-21 15:13  kobit\n\n\t* src/main/php/drupal/modules/short_news.module,\n\t  src/main/php/drupal/modules/tigase.module: Spelling corrections +\n\t  license update\n\n2007-07-21 15:12  kobit\n\n\t* src/main/php/drupal/modules/tigase_monitor.module: Real Tigase\n\t  monitor as a separate module with online_status and all other\n\t  stuff\n\n2007-07-21 15:12  kobit\n\n\t* src/main/java/tigase/db/jdbc/DrupalAuth.java: Added online_status\n\t  support\n\n2007-07-18 18:13  kobit\n\n\t* pom.xml: [maven-release-plugin] prepare for next development\n\t  iteration\n\n2007-07-18 18:13  kobit\n\n\t* pom.xml: [maven-release-plugin] prepare release\n\t  tigase-server-3.0.0\n\n2007-07-18 18:12  kobit\n\n\t* pom.xml: Dependencies change to libraries which also use GPLv3\n\n2007-07-18 18:10  kobit\n\n\t* build.properties: Version change to 3.0.1 - license change to\n\t  GPLv3\n\n2007-07-18 18:09  kobit\n\n\t* COPYING: Initial version of file with license GPLv3\n\n2007-07-18 17:38  kobit\n\n\t* database/mysql-schema.sql, database/postgresql-schema.sql,\n\t  scripts/config.sh, scripts/repo.sh, scripts/tigase.sh,\n\t  src/main/java/tigase/annotations/TODO.java,\n\t  src/main/java/tigase/auth/AuthorisationSystem.java,\n\t  src/main/java/tigase/auth/LoginHandler.java,\n\t  src/main/java/tigase/auth/ResourceConnectionCallback.java,\n\t  src/main/java/tigase/auth/SaslPLAIN.java,\n\t  src/main/java/tigase/auth/TigaseSaslProvider.java,\n\t  src/main/java/tigase/auth/TigaseSaslServerFactory.java,\n\t  src/main/java/tigase/conf/ConfigComponent.java,\n\t  src/main/java/tigase/conf/ConfigRepository.java,\n\t  src/main/java/tigase/conf/Configurable.java,\n\t  src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/db/AuthorizationException.java,\n\t  src/main/java/tigase/db/ConfigurationDB.java,\n\t  src/main/java/tigase/db/DBInitException.java,\n\t  src/main/java/tigase/db/DataOverwriteException.java,\n\t  src/main/java/tigase/db/MessageHistoryDB.java,\n\t  src/main/java/tigase/db/MessageOfflineDB.java,\n\t  src/main/java/tigase/db/NonAuthUserRepository.java,\n\t  src/main/java/tigase/db/RepositoryFactory.java,\n\t  src/main/java/tigase/db/TigaseDBException.java,\n\t  src/main/java/tigase/db/UserAuthRepository.java,\n\t  src/main/java/tigase/db/UserAuthRepositoryImpl.java,\n\t  src/main/java/tigase/db/UserDB.java,\n\t  src/main/java/tigase/db/UserExistsException.java,\n\t  src/main/java/tigase/db/UserNotFoundException.java,\n\t  src/main/java/tigase/db/UserRepository.java,\n\t  src/main/java/tigase/db/jdbc/DrupalAuth.java,\n\t  src/main/java/tigase/db/jdbc/JDBCRepository.java,\n\t  src/main/java/tigase/db/jdbc/LibreSourceAuth.java,\n\t  src/main/java/tigase/db/xml/XMLRepository.java,\n\t  src/main/java/tigase/disco/ServiceEntity.java,\n\t  src/main/java/tigase/disco/ServiceIdentity.java,\n\t  src/main/java/tigase/disco/XMPPService.java,\n\t  src/main/java/tigase/disco/XMPPServiceCollector.java,\n\t  src/main/java/tigase/http/HttpServer.java,\n\t  src/main/java/tigase/http/HttpService.java,\n\t  src/main/java/tigase/io/BufferUnderflowException.java,\n\t  src/main/java/tigase/io/IOInterface.java,\n\t  src/main/java/tigase/io/SSLContextContainer.java,\n\t  src/main/java/tigase/io/SampleSocketThread.java,\n\t  src/main/java/tigase/io/SocketIO.java,\n\t  src/main/java/tigase/io/TLSEventHandler.java,\n\t  src/main/java/tigase/io/TLSIO.java,\n\t  src/main/java/tigase/io/TLSStatus.java,\n\t  src/main/java/tigase/io/TLSUtil.java,\n\t  src/main/java/tigase/io/TLSWrapper.java,\n\t  src/main/java/tigase/io/TelnetClient.java,\n\t  src/main/java/tigase/io/TelnetServer.java,\n\t  src/main/java/tigase/net/Accept.java,\n\t  src/main/java/tigase/net/ConnectionOpenListener.java,\n\t  src/main/java/tigase/net/ConnectionOpenThread.java,\n\t  src/main/java/tigase/net/ConnectionType.java,\n\t  src/main/java/tigase/net/IOService.java,\n\t  src/main/java/tigase/net/IOServiceListener.java,\n\t  src/main/java/tigase/net/ServiceCommand.java,\n\t  src/main/java/tigase/net/SocketReadThread.java,\n\t  src/main/java/tigase/net/SocketType.java,\n\t  src/main/java/tigase/server/AbstractComponentRegistrator.java,\n\t  src/main/java/tigase/server/AbstractMessageReceiver.java,\n\t  src/main/java/tigase/server/Command.java,\n\t  src/main/java/tigase/server/ComponentRegistrator.java,\n\t  src/main/java/tigase/server/ConnectionManager.java,\n\t  src/main/java/tigase/server/MessageReceiver.java,\n\t  src/main/java/tigase/server/MessageRouter.java,\n\t  src/main/java/tigase/server/MessageRouterConfig.java,\n\t  src/main/java/tigase/server/Packet.java,\n\t  src/main/java/tigase/server/Permissions.java,\n\t  src/main/java/tigase/server/ServerComponent.java,\n\t  src/main/java/tigase/server/ThreadExceptionHandler.java,\n\t  src/main/java/tigase/server/XMPPServer.java,\n\t  src/main/java/tigase/server/bosh/BoshConnectionManager.java,\n\t  src/main/java/tigase/server/bosh/BoshIOService.java,\n\t  src/main/java/tigase/server/bosh/BoshSession.java,\n\t  src/main/java/tigase/server/bosh/Constants.java,\n\t  src/main/java/tigase/server/sreceiver/AbstractReceiverTask.java,\n\t  src/main/java/tigase/server/sreceiver/DefaultValues.java,\n\t  src/main/java/tigase/server/sreceiver/NewTaskCommand.java,\n\t  src/main/java/tigase/server/sreceiver/NewsDistributor.java,\n\t  src/main/java/tigase/server/sreceiver/PropertyConstants.java,\n\t  src/main/java/tigase/server/sreceiver/PropertyItem.java,\n\t  src/main/java/tigase/server/sreceiver/ReceiverTaskIfc.java,\n\t  src/main/java/tigase/server/sreceiver/RepoRosterTask.java,\n\t  src/main/java/tigase/server/sreceiver/RosterItem.java,\n\t  src/main/java/tigase/server/sreceiver/ShortNewsPublisher.java,\n\t  src/main/java/tigase/server/sreceiver/StanzaReceiver.java,\n\t  src/main/java/tigase/server/sreceiver/TaskCommandIfc.java,\n\t  src/main/java/tigase/server/sreceiver/TaskCommons.java,\n\t  src/main/java/tigase/server/sreceiver/TaskCreationPolicy.java,\n\t  src/main/java/tigase/server/sreceiver/TaskInstanceCommand.java,\n\t  src/main/java/tigase/server/sreceiver/TaskType.java,\n\t  src/main/java/tigase/server/ssender/DrupalForumTask.java,\n\t  src/main/java/tigase/server/ssender/FileTask.java,\n\t  src/main/java/tigase/server/ssender/JDBCTask.java,\n\t  src/main/java/tigase/server/ssender/SenderTask.java,\n\t  src/main/java/tigase/server/ssender/StanzaHandler.java,\n\t  src/main/java/tigase/server/ssender/StanzaSender.java,\n\t  src/main/java/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/PacketFilter.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerConfig.java,\n\t  src/main/java/tigase/stats/StatRecord.java,\n\t  src/main/java/tigase/stats/StatisticType.java,\n\t  src/main/java/tigase/stats/StatisticsCollector.java,\n\t  src/main/java/tigase/stats/StatisticsContainer.java,\n\t  src/main/java/tigase/stats/StatsComponent.java,\n\t  src/main/java/tigase/ui/UIComponent.java,\n\t  src/main/java/tigase/ui/WebUI.java,\n\t  src/main/java/tigase/ui/XMPPServiceComponent.java,\n\t  src/main/java/tigase/util/ElementUtils.java,\n\t  src/main/java/tigase/util/LogFormatter.java,\n\t  src/main/java/tigase/util/RepositoryUtils.java,\n\t  src/main/java/tigase/util/RoutingsContainer.java,\n\t  src/main/java/tigase/xmpp/Authorization.java,\n\t  src/main/java/tigase/xmpp/NotAuthorizedException.java,\n\t  src/main/java/tigase/xmpp/ProcessorFactory.java,\n\t  src/main/java/tigase/xmpp/RepositoryAccess.java,\n\t  src/main/java/tigase/xmpp/StanzaType.java,\n\t  src/main/java/tigase/xmpp/XMPPDomBuilderHandler.java,\n\t  src/main/java/tigase/xmpp/XMPPException.java,\n\t  src/main/java/tigase/xmpp/XMPPIOService.java,\n\t  src/main/java/tigase/xmpp/XMPPIOServiceListener.java,\n\t  src/main/java/tigase/xmpp/XMPPImplIfc.java,\n\t  src/main/java/tigase/xmpp/XMPPPostprocessorIfc.java,\n\t  src/main/java/tigase/xmpp/XMPPPreprocessorIfc.java,\n\t  src/main/java/tigase/xmpp/XMPPProcessor.java,\n\t  src/main/java/tigase/xmpp/XMPPProcessorIfc.java,\n\t  src/main/java/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/main/java/tigase/xmpp/XMPPSession.java,\n\t  src/main/java/tigase/xmpp/XMPPStopListenerIfc.java,\n\t  src/main/java/tigase/xmpp/impl/BindResource.java,\n\t  src/main/java/tigase/xmpp/impl/IBB.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqCommand.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqIq.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqOOB.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqPrivacy.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqPrivate.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRegister.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqSi.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqSocks5Bytestreams.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqStats.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqVersion.java,\n\t  src/main/java/tigase/xmpp/impl/Jingle.java,\n\t  src/main/java/tigase/xmpp/impl/Message.java,\n\t  src/main/java/tigase/xmpp/impl/OfflineMessages.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java,\n\t  src/main/java/tigase/xmpp/impl/Privacy.java,\n\t  src/main/java/tigase/xmpp/impl/Roster.java,\n\t  src/main/java/tigase/xmpp/impl/SaslAuth.java,\n\t  src/main/java/tigase/xmpp/impl/ServiceDiscovery.java,\n\t  src/main/java/tigase/xmpp/impl/SessionBind.java,\n\t  src/main/java/tigase/xmpp/impl/SimpleForwarder.java,\n\t  src/main/java/tigase/xmpp/impl/StartTLS.java,\n\t  src/main/java/tigase/xmpp/impl/UrnXmppPing.java,\n\t  src/main/java/tigase/xmpp/impl/VCardTemp.java: License change to\n\t  GPLv3\n\n2007-07-13 12:48  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshConnectionManager.java,\n\t  src/main/java/tigase/server/bosh/BoshIOService.java,\n\t  src/main/java/tigase/server/bosh/BoshSession.java: Bosh\n\t  implementation in progress\n\n2007-07-13 12:48  kobit\n\n\t* src/main/java/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  Changed public constants to private constants\n\n2007-06-27 16:03  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqCommand.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqIq.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRegister.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqStats.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqVersion.java,\n\t  src/main/java/tigase/xmpp/impl/Jingle.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java,\n\t  src/main/java/tigase/xmpp/impl/ServiceDiscovery.java,\n\t  src/main/java/tigase/xmpp/impl/SimpleForwarder.java,\n\t  src/main/java/tigase/xmpp/impl/UrnXmppPing.java,\n\t  src/main/java/tigase/xmpp/impl/VCardTemp.java: Removed redundand\n\t  'from' attribute setting which is already done in PacketFilter\n\n2007-06-27 15:24  kobit\n\n\t* src/main/java/tigase/server/AbstractMessageReceiver.java: Changed\n\t  access modifier from protected to private because this variable\n\t  is now accessible through public method for component\n\n2007-06-27 15:22  kobit\n\n\t* src/main/java/tigase/server/xmppsession/PacketFilter.java:\n\t  Removed 'forward' processing completely as it was conflicting\n\t  with all plugins processing stanzas on behalf of the user\n\n2007-06-27 15:21  kobit\n\n\t* src/main/java/tigase/server/sreceiver/StanzaReceiver.java:\n\t  Replaced custom code with more generic code accessing default\n\t  hostname\n\n2007-06-27 15:21  kobit\n\n\t* src/main/java/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  Replaced custom code with more generic code accessing default\n\t  hostname\n\n2007-06-27 15:20  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Fixed null pointer\n\t  exception in case of closing not authenticated session\n\n2007-06-18 16:21  kobit\n\n\t* etc/tigase-mysql.conf, src/main/java/tigase/net/IOService.java,\n\t  src/main/java/tigase/server/ConnectionManager.java,\n\t  src/main/java/tigase/server/bosh/BoshConnectionManager.java,\n\t  src/main/java/tigase/server/bosh/BoshSession.java,\n\t  src/main/java/tigase/server/bosh/Constants.java,\n\t  src/main/java/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/main/java/tigase/xmpp/XMPPIOService.java,\n\t  src/main/java/tigase/xmpp/XMPPIOServiceListener.java: Each\n\t  component can now provide own implementation/extension of\n\t  XMPPIOService. The specific component using this feature is Bosh\n\n2007-06-18 16:20  kobit\n\n\t* src/main/java/tigase/conf/Configurable.java,\n\t  src/main/java/tigase/server/MessageRouter.java: Added NULL\n\t  routing which is dummy routing used for domain names hosted on\n\t  the server\n\n2007-06-18 16:19  kobit\n\n\t* src/main/java/tigase/server/xmppsession/PacketFilter.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Fixed bug when user could inject a stanza to the system before\n\t  the session has been authenticated\n\n2007-06-09 09:27  kobit\n\n\t* src/main/java/tigase/server/AbstractMessageReceiver.java,\n\t  src/main/java/tigase/server/MessageRouter.java,\n\t  src/main/java/tigase/server/bosh/BoshConnectionManager.java,\n\t  src/main/java/tigase/server/sreceiver/StanzaReceiver.java,\n\t  src/main/java/tigase/server/ssender/StanzaSender.java,\n\t  src/main/java/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  Moved defHostname code to abstract class to make it automatically\n\t  available for all message receivers and set proper default\n\t  routing for all components\n\n2007-06-08 19:18  kobit\n\n\t* src/main/java/tigase/conf/Configurable.java,\n\t  src/main/java/tigase/server/AbstractMessageReceiver.java,\n\t  src/main/java/tigase/server/sreceiver/StanzaReceiver.java,\n\t  src/main/java/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  Added default hostname for c2s component as using the first\n\t  hostname as default from the list doesn't work correctly,\n\t  elements often gets reordered\n\n2007-06-08 18:55  kobit\n\n\t* src/main/java/tigase/server/sreceiver/AbstractReceiverTask.java:\n\t  Fixed problem with error stanza received sometimes from s2s\n\t  component in case of problem with deivery\n\n2007-06-08 18:20  kobit\n\n\t* src/main/java/tigase/server/sreceiver/StanzaReceiver.java: Fixed\n\t  problem with default policies for task types and added\n\t  defHostname config parameter\n\n2007-06-08 18:20  kobit\n\n\t* build.properties: Version change to 2.9.6\n\n2007-06-08 17:05  kobit\n\n\t* pom.xml: [maven-release-plugin] prepare for next development\n\t  iteration\n\n2007-06-08 17:05  kobit\n\n\t* pom.xml: [maven-release-plugin] prepare release\n\t  tigase-server-2.9.5\n\n2007-06-08 11:43  kobit\n\n\t* build.properties, win-stuff/Tigase.iss: Version change to 2.9.5\n\n2007-06-08 11:41  kobit\n\n\t* src/main/java/tigase/server/xmppsession/PacketFilter.java: Fixed\n\t  bug with presence probe processing\n\n2007-06-08 11:00  kobit\n\n\t* pom.xml: [maven-release-plugin] prepare for next development\n\t  iteration\n\n2007-06-08 11:00  kobit\n\n\t* pom.xml: [maven-release-plugin] prepare release\n\t  tigase-server-2.9.4\n\n2007-06-08 11:00  kobit\n\n\t* pom.xml: changed xmltools version dependcy\n\n2007-06-08 10:59  kobit\n\n\t* ant-definitions.xml: Added Mandriva script to binary releases\n\n2007-06-08 07:49  kobit\n\n\t* win-stuff/Tigase.iss: Version number update to 2.9.4\n\n2007-06-07 23:57  kobit\n\n\t* src/main/java/tigase/server/xmppsession/PacketFilter.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/xmpp/XMPPResourceConnection.java: Corrected\n\t  packet forwarding addressed to domain\n\n2007-06-07 23:37  kobit\n\n\t* src/main/java/tigase/server/xmppsession/PacketFilter.java: Fixed\n\t  problem with packet forwarding\n\n2007-06-07 23:16  kobit\n\n\t* src/main/java/tigase/server/AbstractMessageReceiver.java:\n\t  Commented out some extra logging\n\n2007-06-07 23:15  kobit\n\n\t* src/main/java/tigase/server/MessageRouter.java: Improved\n\t  processing of lost packet to local component\n\n2007-06-07 23:14  kobit\n\n\t* src/main/java/tigase/server/xmppsession/PacketFilter.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Improved packet filter - added default forwarder\n\n2007-06-07 23:13  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java: Increased default\n\t  log size to 10MB\n\n2007-06-07 23:12  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqRegister.java: Improved\n\t  authorization checking\n\n2007-06-06 23:06  kobit\n\n\t* scripts/mandriva, scripts/mandriva/init.d,\n\t  scripts/mandriva/init.d/tigase: Startup script for Mandriva\n\n2007-06-06 23:06  kobit\n\n\t* src/main/java/tigase/disco/ServiceEntity.java,\n\t  src/main/java/tigase/net/ConnectionOpenThread.java,\n\t  src/main/java/tigase/server/ConnectionManager.java,\n\t  src/main/java/tigase/server/MessageRouterConfig.java,\n\t  src/main/java/tigase/server/sreceiver/PropertyItem.java,\n\t  src/main/java/tigase/server/sreceiver/TaskCommons.java,\n\t  src/main/java/tigase/server/ssender/StanzaSender.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerConfig.java,\n\t  src/main/java/tigase/xmpp/impl/BindResource.java,\n\t  src/main/java/tigase/xmpp/impl/IBB.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqCommand.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqIq.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqOOB.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqPrivacy.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqPrivate.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRegister.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqSi.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqSocks5Bytestreams.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqStats.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqVersion.java,\n\t  src/main/java/tigase/xmpp/impl/Jingle.java,\n\t  src/main/java/tigase/xmpp/impl/Message.java,\n\t  src/main/java/tigase/xmpp/impl/OfflineMessages.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java,\n\t  src/main/java/tigase/xmpp/impl/SaslAuth.java,\n\t  src/main/java/tigase/xmpp/impl/ServiceDiscovery.java,\n\t  src/main/java/tigase/xmpp/impl/SessionBind.java,\n\t  src/main/java/tigase/xmpp/impl/StartTLS.java,\n\t  src/main/java/tigase/xmpp/impl/UrnXmppPing.java,\n\t  src/main/java/tigase/xmpp/impl/VCardTemp.java: Code cleanup\n\n2007-06-06 20:29  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshConnectionManager.java,\n\t  src/main/java/tigase/server/bosh/BoshIOService.java,\n\t  src/main/java/tigase/server/bosh/BoshSession.java,\n\t  src/main/java/tigase/server/bosh/Constants.java: Stream\n\t  initialization - implementation in progress\n\n2007-06-06 20:28  kobit\n\n\t* src/main/java/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  c2s component now first sends stream initialization back to\n\t  client and then sends requests for GETFEATURES to avoid sending\n\t  features before stream initialization\n\n2007-06-06 20:27  kobit\n\n\t* src/main/java/tigase/xmpp/XMPPIOService.java: Removed all locks\n\t  as now IOService is synchronized\n\n2007-06-06 19:49  kobit\n\n\t* src/main/java/tigase/xmpp/impl/UrnXmppPing.java,\n\t  src/main/java/tigase/xmpp/impl/VCardTemp.java: Code cleanup\n\n2007-06-06 16:27  kobit\n\n\t* src/main/java/tigase/xmpp/impl/ServiceDiscovery.java,\n\t  src/main/java/tigase/xmpp/impl/SessionBind.java,\n\t  src/main/java/tigase/xmpp/impl/StartTLS.java: Code cleanup\n\n2007-06-06 16:23  kobit\n\n\t* src/main/java/tigase/xmpp/impl/BindResource.java,\n\t  src/main/java/tigase/xmpp/impl/IBB.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqCommand.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqIq.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqOOB.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqPrivacy.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqPrivate.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRegister.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqSi.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqSocks5Bytestreams.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqStats.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqVersion.java,\n\t  src/main/java/tigase/xmpp/impl/Jingle.java,\n\t  src/main/java/tigase/xmpp/impl/Message.java,\n\t  src/main/java/tigase/xmpp/impl/OfflineMessages.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java,\n\t  src/main/java/tigase/xmpp/impl/SaslAuth.java: Code cleanup\n\n2007-06-06 15:48  kobit\n\n\t* src/main/java/tigase/net/IOService.java,\n\t  src/main/java/tigase/server/bosh/BoshConnectionManager.java,\n\t  src/main/java/tigase/server/sreceiver/PropertyItem.java,\n\t  src/main/java/tigase/server/sreceiver/StanzaReceiver.java,\n\t  src/main/java/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/main/java/tigase/xmpp/XMPPProcessor.java,\n\t  src/main/java/tigase/xmpp/impl/OfflineMessages.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java: Code cleanup\n\n2007-06-06 12:21  kobit\n\n\t* src/main/java/tigase/auth/SaslPLAIN.java,\n\t  src/main/java/tigase/server/MessageRouter.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/main/java/tigase/xmpp/impl/SaslAuth.java,\n\t  src/main/java/tigase/xmpp/impl/SessionBind.java: Code cleanup\n\n2007-06-06 12:06  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/server/sreceiver/TaskInstanceCommand.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Performance optimizations\n\n2007-06-06 11:55  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/db/jdbc/JDBCRepository.java,\n\t  src/main/java/tigase/db/jdbc/LibreSourceAuth.java,\n\t  src/main/java/tigase/io/SSLContextContainer.java,\n\t  src/main/java/tigase/io/TLSWrapper.java,\n\t  src/main/java/tigase/io/TelnetClient.java,\n\t  src/main/java/tigase/server/AbstractMessageReceiver.java,\n\t  src/main/java/tigase/server/Packet.java,\n\t  src/main/java/tigase/server/sreceiver/RepoRosterTask.java,\n\t  src/main/java/tigase/server/sreceiver/ShortNewsPublisher.java,\n\t  src/main/java/tigase/server/sreceiver/StanzaReceiver.java,\n\t  src/main/java/tigase/server/sreceiver/TaskInstanceCommand.java,\n\t  src/main/java/tigase/server/ssender/FileTask.java,\n\t  src/main/java/tigase/server/ssender/StanzaSender.java,\n\t  src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/stats/StatRecord.java,\n\t  src/main/java/tigase/stats/StatisticsCollector.java,\n\t  src/main/java/tigase/util/RepositoryUtils.java,\n\t  src/main/java/tigase/util/RoutingsContainer.java,\n\t  src/main/java/tigase/xmpp/XMPPIOService.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqPrivacy.java: Performance\n\t  optimizations\n\n2007-06-06 11:54  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshConnectionManager.java,\n\t  src/main/java/tigase/server/bosh/Constants.java: Added\n\t  configuration settings\n\n2007-06-06 11:54  kobit\n\n\t* src/main/java/tigase/server/bosh/BoshSession.java: Added\n\t  configuration settings\n\n2007-06-06 11:52  kobit\n\n\t* src/main/java/tigase/server/MessageRouter.java: Protection\n\t  against infinite loop, server now doesn't send out any packet\n\t  which is addressed to local domain\n\n2007-06-05 22:03  kobit\n\n\t* etc/tigase-mysql.conf, src/main/java/tigase/server/bosh,\n\t  src/main/java/tigase/server/bosh/BoshConnectionManager.java,\n\t  src/main/java/tigase/server/bosh/BoshIOService.java,\n\t  src/main/java/tigase/server/bosh/BoshSession.java,\n\t  src/main/java/tigase/server/bosh/Constants.java: Initial version\n\t  of BOSH component - XEP-0124\n\n2007-06-05 22:03  kobit\n\n\t* src/main/java/tigase/util/RoutingsContainer.java: Replaced\n\t  HashMap with LinkedHashMap\n\n2007-06-05 21:48  kobit\n\n\t* src/main/java/tigase/server/AbstractMessageReceiver.java:\n\t  Replaced HashMap with LinkedHashMap\n\n2007-06-05 21:47  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java: Moved\n\t  XMPPIOService creation to separate method so it can be\n\t  overwritten and return more specialized classes implementation\n\t  like BoshIOService\n\n2007-06-05 21:46  kobit\n\n\t* src/main/java/tigase/server/ssender/StanzaSender.java: Replaced\n\t  HashMap with LinkedHashMap\n\n2007-06-05 21:46  kobit\n\n\t* src/main/java/tigase/server/AbstractComponentRegistrator.java:\n\t  Replaced HashMap with LinkedHashMap\n\n2007-06-05 21:46  kobit\n\n\t* src/main/java/tigase/server/MessageRouterConfig.java: Replaced\n\t  HashMap with LinkedHashMap and added Bosh component to a list of\n\t  standard components\n\n2007-06-05 21:45  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Removed extra, not used import\n\n2007-06-05 21:44  kobit\n\n\t* src/main/java/tigase/server/sreceiver/TaskInstanceCommand.java:\n\t  Replaced HashMap with LinkedHashMap\n\n2007-06-05 21:44  kobit\n\n\t* src/main/java/tigase/server/sreceiver/StanzaReceiver.java:\n\t  Replaced HashMap with LinkedHashMap\n\n2007-06-05 21:44  kobit\n\n\t* src/main/java/tigase/server/sreceiver/NewTaskCommand.java:\n\t  Replaced HashMap with LinkedHashMap\n\n2007-06-05 21:43  kobit\n\n\t* src/main/java/tigase/conf/ConfigRepository.java: Replaced HashMap\n\t  with LinkedHashMap\n\n2007-06-05 21:43  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java: 1. Replaced HashMap\n\t  with LinkedHashMap, 2. changed default logging level to console\n\t  to FINER from ALL, 3. added stanza type checking for commands\n\t  requests\n\n2007-06-05 21:41  kobit\n\n\t* src/main/java/tigase/conf/Configurable.java: Added Bosh\n\t  implementation to a list of default components\n\n2007-06-05 21:41  kobit\n\n\t* src/main/java/tigase/xmpp/XMPPIOService.java: Unified i/o\n\t  interface, all methods now use writeRawData method which can be\n\t  overwritten in descendant classes like in BoshIOService to write\n\t  some HTTP headers before content\n\n2007-06-05 21:40  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Roster.java: Replaced HashMap with\n\t  LinkedHashMap\n\n2007-06-05 09:43  kobit\n\n\t* src/main/java/tigase/server/MessageRouter.java: Corrected service\n\t  discovery retrieving to work only for stanzas with 'get' type\n\n2007-06-01 17:18  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  s2s component now refuses to process packets (send out) which are\n\t  addressed to one of local domains\n\n2007-06-01 17:18  kobit\n\n\t* src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Added another default routings: .*.hostname\n\n2007-06-01 11:43  kobit\n\n\t* build.properties: Version number update to 2.9.4\n\n2007-06-01 11:43  kobit\n\n\t* pom.xml: Utils dependency update for version 2.4.2\n\n2007-06-01 11:43  kobit\n\n\t* src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Corrected default routing table, to '.*@hostname' added also\n\t  'hostname'\n\n2007-06-01 10:22  kobit\n\n\t* src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Default routing is now set to: .*@hostname instead of .*hostname\n\n2007-06-01 10:22  kobit\n\n\t* src/main/java/tigase/server/MessageRouter.java: regex routings\n\t  are now checked agains whole node ID part not just hostame\n\n2007-06-01 09:01  kobit\n\n\t* pom.xml: [maven-release-plugin] prepare for next development\n\t  iteration\n\n2007-06-01 09:01  kobit\n\n\t* pom.xml: [maven-release-plugin] prepare release\n\t  tigase-server-2.9.3\n\n2007-06-01 08:58  kobit\n\n\t* win-stuff/Tigase.iss: Version change to 2.9.3\n\n2007-05-31 21:32  kobit\n\n\t* etc/tigase-mysql.conf,\n\t  src/main/java/tigase/server/ConnectionManager.java: Logging a bit\n\t  optimized\n\n2007-05-31 21:31  kobit\n\n\t* src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Added GEN_CONFIG_COMP parameter processing, special default\n\t  confiuration for single component like MUC and external\n\t  component, IOService id is now a constant which means this\n\t  component can server single connection only\n\n2007-05-31 21:30  kobit\n\n\t* src/main/java/tigase/server/MessageRouterConfig.java: Added\n\t  GEN_CONFIG_COMP parameter processing, special default\n\t  confiuration for single component like MUC and external component\n\n2007-05-31 21:29  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  ADMINS and HOSTNAMES property key moved to Configurable\n\n2007-05-31 21:29  kobit\n\n\t* src/main/java/tigase/server/sreceiver/StanzaReceiver.java: ADMINS\n\t  property key moved to Configurable\n\n2007-05-31 21:28  kobit\n\n\t* src/main/java/tigase/server/sreceiver/ShortNewsPublisher.java:\n\t  super.processMessage call moved so commands are not distributed\n\t  to all subscribers\n\n2007-05-31 21:26  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java: Added GEN_COMP_NAME,\n\t  CLASS parameters processing\n\n2007-05-31 21:26  kobit\n\n\t* build.properties, pom.xml: Version change to 2.9.3\n\n2007-05-31 21:24  kobit\n\n\t* src/main/java/tigase/conf/Configurable.java: Added\n\t  --gen-config-comp parameter, added some more comments and moved\n\t  HOSTNAMES and ADMINS property key from SessionManager to this\n\t  class\n\n2007-05-30 11:30  bmalkow\n\n\t* pom.xml: make dependencies non-snapshot\n\n2007-05-29 22:29  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Corrected command processing - now commands not processed can be\n\t  sent out\n\n2007-05-29 21:36  kobit\n\n\t* src/main/java/tigase/server/sreceiver/ShortNewsPublisher.java,\n\t  win-stuff/Tigase.iss: Improved adding post confirmation - it now\n\t  should return warning in case if message is not correctly\n\t  formatted\n\n2007-05-29 21:06  kobit\n\n\t* src/main/java/tigase/server/AbstractMessageReceiver.java: Added\n\t  non-blocking method for inserting 'out' packets\n\n2007-05-29 21:05  kobit\n\n\t* src/main/java/tigase/server/MessageRouter.java: Protection from\n\t  infinite loop and self-locking\n\n2007-05-29 21:05  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqStats.java: Removed\n\t  replacing from address, now it is left as it was originally\n\n2007-05-29 21:04  kobit\n\n\t* src/main/java/tigase/stats/StatisticsCollector.java: Changed\n\t  GETSTATS result generation to just normal result sent directly to\n\t  requesting entity\n\n2007-05-29 09:29  kobit\n\n\t* scripts/tigase.sh: Fixed a problem with the script when it was\n\t  run on systems where /bin/sh was not a bash shell, now it\n\t  specifically requests that it must be run under bash, first line\n\t  modified to: #!/bin/bash\n\n2007-05-29 07:05  kobit\n\n\t* src/main/java/tigase/server/sreceiver/ShortNewsPublisher.java:\n\t  Added response feedback for new short news submition\n\n2007-05-29 06:59  kobit\n\n\t* build.properties, pom.xml: Version change to 2.9.2\n\n2007-05-29 06:58  kobit\n\n\t* src/main/java/tigase/stats/StatisticsCollector.java: Fixed\n\t  problem with infinite loop in MessageRouter for this GETSTATS\n\t  packet\n\n2007-05-28 12:52  kobit\n\n\t* src/main/java/tigase/server/MessageRouterConfig.java,\n\t  win-stuff/Tigase.iss: Fixed default configuration generation for\n\t  external compoentns\n\n2007-05-28 12:51  kobit\n\n\t* database/postgresql-schema.sql: Removed extra comma\n\n2007-05-28 12:51  kobit\n\n\t* src/main/java/tigase/db/jdbc/JDBCRepository.java: Corrected the\n\t  order in which records are removed from database - according to\n\t  requirements of foreign keys constraints\n\n2007-05-28 10:54  kobit\n\n\t* win-stuff/Tigase.iss: Version update to 2.9.1\n\n2007-05-28 10:52  kobit\n\n\t* database/mysql-schema.sql,\n\t  src/main/java/tigase/server/sreceiver/NewTaskCommand.java,\n\t  src/main/java/tigase/server/sreceiver/StanzaReceiver.java,\n\t  src/main/java/tigase/server/sreceiver/TaskCreationPolicy.java,\n\t  src/main/java/tigase/server/sreceiver/TaskType.java: Added task\n\t  creation configurable permissions\n\n2007-05-27 19:44  kobit\n\n\t* src/main/java/tigase/server/sreceiver/AbstractReceiverTask.java:\n\t  Moved posting permission checking from processMessage to\n\t  processPacket\n\n2007-05-27 19:43  kobit\n\n\t* src/main/java/tigase/server/sreceiver/RepoRosterTask.java: Added\n\t  verification if RosterItem is null before trying to remove it\n\t  from repository\n\n2007-05-27 19:42  kobit\n\n\t* src/main/java/tigase/server/sreceiver/NewsDistributor.java:\n\t  Changed constants from public to private\n\n2007-05-27 19:42  kobit\n\n\t* src/main/java/tigase/server/sreceiver/StanzaReceiver.java:\n\t  Corrected indendation\n\n2007-05-27 19:41  kobit\n\n\t* src/main/java/tigase/db/jdbc/DrupalAuth.java: Removed unnecesary\n\t  local variable rs and finally section\n\n2007-05-27 19:40  kobit\n\n\t* src/main/java/tigase/server/sreceiver/ShortNewsPublisher.java:\n\t  The first version of short news publishing module\n\n2007-05-27 19:39  kobit\n\n\t* src/main/php/drupal/modules/short_news.module: The first version\n\t  of short_news module\n\n2007-05-27 19:39  kobit\n\n\t* src/main/php/drupal/modules/tigase.module: Moved license\n\t  information to proper place\n\n2007-05-27 19:37  kobit\n\n\t* build.properties, pom.xml: Version number change to 2.9.1\n\n2007-05-27 19:37  kobit\n\n\t* database/mysql-schema.sql, database/postgresql-schema.sql:\n\t  publishing_date remabed to publishing_time and added news_type\n\t  field and key\n\n2007-05-26 10:31  kobit\n\n\t* src/main/java/tigase/server/Permissions.java,\n\t  src/main/java/tigase/server/sreceiver/AbstractReceiverTask.java,\n\t  src/main/java/tigase/server/sreceiver/NewsDistributor.java,\n\t  src/main/java/tigase/server/sreceiver/ReceiverTaskIfc.java,\n\t  src/main/java/tigase/server/sreceiver/RepoRosterTask.java,\n\t  src/main/java/tigase/server/sreceiver/TaskInstanceCommand.java,\n\t  src/main/java/tigase/server/ssender/DrupalForumTask.java,\n\t  src/main/java/tigase/server/ssender/StanzaSender.java,\n\t  src/main/java/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/main/java/tigase/xmpp/XMPPSession.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java: Fixed comments and\n\t  texts containing JIDUtils instead of JID after JID class\n\t  refactoring\n\n2007-05-26 10:30  kobit\n\n\t* database/mysql-schema.sql, database/postgresql-schema.sql: Added\n\t  short_news table, comments and licence note\n\n2007-05-25 20:18  bmalkow\n\n\t* tigase-server:\n\n2007-05-25 11:36  bmalkow\n\n\t* src/main/java/tigase/auth/SaslPLAIN.java,\n\t  src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/db/UserAuthRepositoryImpl.java,\n\t  src/main/java/tigase/db/jdbc/DrupalAuth.java,\n\t  src/main/java/tigase/db/jdbc/LibreSourceAuth.java,\n\t  src/main/java/tigase/server/AbstractMessageReceiver.java,\n\t  src/main/java/tigase/server/ConnectionManager.java,\n\t  src/main/java/tigase/server/MessageRouter.java,\n\t  src/main/java/tigase/server/Permissions.java,\n\t  src/main/java/tigase/server/sreceiver/AbstractReceiverTask.java,\n\t  src/main/java/tigase/server/sreceiver/NewTaskCommand.java,\n\t  src/main/java/tigase/server/sreceiver/NewsDistributor.java,\n\t  src/main/java/tigase/server/sreceiver/ReceiverTaskIfc.java,\n\t  src/main/java/tigase/server/sreceiver/RepoRosterTask.java,\n\t  src/main/java/tigase/server/sreceiver/StanzaReceiver.java,\n\t  src/main/java/tigase/server/sreceiver/TaskCommons.java,\n\t  src/main/java/tigase/server/sreceiver/TaskInstanceCommand.java,\n\t  src/main/java/tigase/server/ssender/DrupalForumTask.java,\n\t  src/main/java/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/PacketFilter.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/xmpp/RepositoryAccess.java,\n\t  src/main/java/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/main/java/tigase/xmpp/XMPPSession.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqCommand.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqIq.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqPrivate.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRegister.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqStats.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqVersion.java,\n\t  src/main/java/tigase/xmpp/impl/Jingle.java,\n\t  src/main/java/tigase/xmpp/impl/Message.java,\n\t  src/main/java/tigase/xmpp/impl/OfflineMessages.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java,\n\t  src/main/java/tigase/xmpp/impl/Roster.java,\n\t  src/main/java/tigase/xmpp/impl/ServiceDiscovery.java,\n\t  src/main/java/tigase/xmpp/impl/SimpleForwarder.java,\n\t  src/main/java/tigase/xmpp/impl/UrnXmppPing.java,\n\t  src/main/java/tigase/xmpp/impl/VCardTemp.java: refactoring:\n\t  renaming java.util.JID to tigase.util.JIDUtils\n\n2007-05-25 11:24  kobit\n\n\t* etc/tigase-mysql.conf: Added drupal forum tasks\n\n2007-05-25 11:24  kobit\n\n\t* win-stuff/Tigase.iss: Version number change\n\n2007-05-24 21:46  kobit\n\n\t* src/main/java/tigase/server/ssender/DrupalForumTask.java: Task\n\t  implementation finished and seems to work fine\n\n2007-05-24 21:45  kobit\n\n\t* src/main/java/tigase/server/ssender/StanzaSender.java: Added\n\t  generator for forum tasks\n\n2007-05-24 21:44  kobit\n\n\t* src/main/java/tigase/server/sreceiver/TaskCommons.java: Added\n\t  more options for creating presence stanza: status, nick name\n\n2007-05-24 21:44  kobit\n\n\t* src/main/java/tigase/server/sreceiver/AbstractReceiverTask.java:\n\t  Replaced sender address with more actions: REPLACE, LEAVE,\n\t  REMOVE, some tweaking to presence format (added status), added\n\t  error message sent back if not authorized user tries to post a\n\t  message\n\n2007-05-24 21:43  kobit\n\n\t* src/main/java/tigase/server/sreceiver/StanzaReceiver.java: Added\n\t  forum tasks for automatic config generation\n\n2007-05-24 21:42  kobit\n\n\t* src/main/java/tigase/server/sreceiver/PropertyConstants.java:\n\t  Replaced sender address with more actions: REPLACE, LEAVE, REMOVE\n\n2007-05-24 06:25  kobit\n\n\t* src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Replaced all _ in component names with -\n\n2007-05-24 06:25  kobit\n\n\t* src/main/java/tigase/server/ssender/StanzaSender.java: Changed\n\t  task name from nickname to full JID\n\n2007-05-24 06:25  kobit\n\n\t* src/main/java/tigase/server/MessageRouterConfig.java: Replaced\n\t  all _ in component names with -\n\n2007-05-24 06:24  kobit\n\n\t* src/main/java/tigase/server/Packet.java: Added static method:\n\t  getMessage(...)\n\n2007-05-24 06:24  kobit\n\n\t* src/main/java/tigase/server/ssender/StanzaHandler.java: Added new\n\t  handler method: void handleStanzas(Queue<Packet> results)\n\n2007-05-24 06:23  kobit\n\n\t* src/main/java/tigase/server/ssender/DrupalForumTask.java: New\n\t  task for sending notifications about new posts on drupal forum\n\n2007-05-24 06:22  kobit\n\n\t* src/main/java/tigase/db/jdbc/JDBCRepository.java: Corrected\n\t  javadoc\n\n2007-05-23 12:50  kobit\n\n\t* src/main/java/tigase/db/jdbc/JDBCRepository.java: Added some\n\t  javadoc comments.\n\n2007-05-23 12:49  kobit\n\n\t* src/main/java/tigase/db/jdbc/JDBCRepository.java: Added some\n\t  javadoc comments.\n\n2007-05-23 11:38  kobit\n\n\t* src/main/java/tigase/db/jdbc/JDBCRepository.java: removed\n\t  system.out.println statements\n\n2007-05-23 11:38  kobit\n\n\t* src/main/java/tigase/db/jdbc/JDBCRepository.java: removed\n\t  printStackTrace statement\n\n2007-05-23 11:37  kobit\n\n\t* src/main/java/tigase/xmpp/RepositoryAccess.java: Indentation\n\t  corrected\n\n2007-05-23 11:36  kobit\n\n\t* src/main/java/tigase/db/jdbc/JDBCRepository.java: Improved\n\t  max_uid, max_nid handling to make more suitable for distributed\n\t  environmane\n\n2007-05-23 11:01  kobit\n\n\t* database/mysql-schema.sql: Added missing commas\n\n2007-05-22 22:25  kobit\n\n\t* src/main/java/tigase/server/sreceiver/AbstractReceiverTask.java,\n\t  src/main/java/tigase/server/sreceiver/NewTaskCommand.java,\n\t  src/main/java/tigase/server/sreceiver/RepoRosterTask.java,\n\t  src/main/java/tigase/server/sreceiver/RosterItem.java,\n\t  src/main/java/tigase/server/sreceiver/StanzaReceiver.java,\n\t  src/main/java/tigase/server/sreceiver/TaskCommons.java,\n\t  src/main/java/tigase/server/sreceiver/TaskInstanceCommand.java:\n\t  Subscribers management commands have been implemented now...\n\n2007-05-22 21:25  kobit\n\n\t* src/main/java/tigase/server/sreceiver/TaskCommons.java: Fixed\n\t  class name and added 2 more methods for presence and message\n\t  packet creation\n\n2007-05-22 21:23  kobit\n\n\t* src/main/java/tigase/server/sreceiver/TaskCommandCommons.java,\n\t  src/main/java/tigase/server/sreceiver/TaskCommons.java: Renamed\n\t  as more stuff will be put there\n\n2007-05-22 12:39  kobit\n\n\t* src/main/java/tigase/server/Command.java,\n\t  src/main/java/tigase/server/sreceiver/AbstractReceiverTask.java,\n\t  src/main/java/tigase/server/sreceiver/ReceiverTaskIfc.java,\n\t  src/main/java/tigase/server/sreceiver/StanzaReceiver.java,\n\t  src/main/java/tigase/server/sreceiver/TaskInstanceCommand.java:\n\t  Subscription moderation is working now\n\n2007-05-22 12:34  kobit\n\n\t* src/main/java/tigase/auth/SaslPLAIN.java: Added some more\n\t  commends\n\n2007-05-21 23:05  kobit\n\n\t* src/main/java/tigase/server/sreceiver/AbstractReceiverTask.java,\n\t  src/main/java/tigase/server/sreceiver/NewTaskCommand.java,\n\t  src/main/java/tigase/server/sreceiver/PropertyConstants.java,\n\t  src/main/java/tigase/server/sreceiver/PropertyItem.java,\n\t  src/main/java/tigase/server/sreceiver/RepoRosterTask.java,\n\t  src/main/java/tigase/server/sreceiver/StanzaReceiver.java,\n\t  src/main/java/tigase/server/sreceiver/TaskInstanceCommand.java:\n\t  Task configuration editing command is now completed and working\n\t  fine\n\n2007-05-21 21:36  kobit\n\n\t* etc/tigase-mysql.conf,\n\t  src/main/java/tigase/server/sreceiver/AbstractReceiverTask.java,\n\t  src/main/java/tigase/server/sreceiver/NewTaskCommand.java,\n\t  src/main/java/tigase/server/sreceiver/NewsDistributor.java,\n\t  src/main/java/tigase/server/sreceiver/PropertyConstants.java,\n\t  src/main/java/tigase/server/sreceiver/PropertyItem.java,\n\t  src/main/java/tigase/server/sreceiver/ReceiverTaskIfc.java,\n\t  src/main/java/tigase/server/sreceiver/RepoRosterTask.java,\n\t  src/main/java/tigase/server/sreceiver/StanzaReceiver.java,\n\t  src/main/java/tigase/server/sreceiver/TaskCommandCommons.java:\n\t  Task adding, deleting works fine now...\n\n2007-05-21 21:36  kobit\n\n\t* database/mysql-schema.sql, database/postgresql-schema.sql: Added\n\t  foreig key constraints to the schema...\n\n2007-05-21 21:35  kobit\n\n\t* src/main/java/tigase/db/jdbc/JDBCRepository.java: Fixed a serious\n\t  bug with database access when there were many instances of this\n\t  class trying to save data to database. max_uid, max_nid were out\n\t  of sync, hopefuly making them static solves the problem\n\n2007-05-21 06:01  kobit\n\n\t* src/main/java/tigase/server/sreceiver/AbstractReceiverTask.java,\n\t  src/main/java/tigase/server/sreceiver/ReceiverTaskIfc.java,\n\t  src/main/java/tigase/server/sreceiver/RepoRosterTask.java,\n\t  src/main/java/tigase/server/sreceiver/StanzaReceiver.java,\n\t  src/main/java/tigase/server/sreceiver/TaskInstanceCommand.java:\n\t  Added task deleting functionality\n\n2007-05-21 05:59  kobit\n\n\t* src/main/java/tigase/server/sreceiver/NewTaskCommand.java: Added\n\t  setting owner property to a value of the command sender\n\n2007-05-21 05:58  kobit\n\n\t* src/main/java/tigase/disco/ServiceEntity.java: Added removing\n\t  ServiceEntity node from the tree\n\n2007-05-21 05:56  kobit\n\n\t* src/main/java/tigase/server/AbstractMessageReceiver.java:\n\t  Replaced ArrayList with LinkedList for statistics list\n\n2007-05-21 05:51  kobit\n\n\t* src/main/java/tigase/disco/ServiceEntity.java: Added removing\n\t  ServiceEntity node from the tree\n\n2007-05-19 08:34  kobit\n\n\t* etc/tigase-mysql.conf,\n\t  src/main/java/tigase/server/sreceiver/AbstractReceiverTask.java,\n\t  src/main/java/tigase/server/sreceiver/DefaultValues.java,\n\t  src/main/java/tigase/server/sreceiver/NewTaskCommand.java,\n\t  src/main/java/tigase/server/sreceiver/PropertyConstants.java,\n\t  src/main/java/tigase/server/sreceiver/PropertyItem.java,\n\t  src/main/java/tigase/server/sreceiver/ReceiverTaskIfc.java,\n\t  src/main/java/tigase/server/sreceiver/RepoRosterTask.java,\n\t  src/main/java/tigase/server/sreceiver/StanzaReceiver.java,\n\t  src/main/java/tigase/server/sreceiver/TaskCommandIfc.java,\n\t  src/main/java/tigase/server/sreceiver/TaskInstanceCommand.java:\n\t  Command for adding new tasks has been fully implemented, task\n\t  management command has been started, a lot of minor improvements\n\n2007-05-19 08:32  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java: --debug options is\n\t  aware now of multiple, comma separated packages, add component\n\t  command removes MessageRouter from the list of available\n\t  components, method checking validy of nickname has been moved to\n\t  JID class\n\n2007-05-19 08:30  kobit\n\n\t* scripts/tigase.sh: Improved 'run' command to behave similarly as\n\t  'start'- uses in the same way environment variables in quotes\n\n2007-05-19 08:29  kobit\n\n\t* src/main/java/tigase/server/AbstractMessageReceiver.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Moved addOutPackets(...) method to abstract class for convenience\n\n2007-05-19 08:28  kobit\n\n\t* src/main/java/tigase/server/Command.java: Added label to command\n\t  field\n\n2007-05-19 08:27  kobit\n\n\t* src/main/java/tigase/stats/StatisticsCollector.java: Added label\n\t  to command field\n\n2007-05-17 21:27  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java: A few fixes for\n\t  creating new component\n\n2007-05-15 22:35  kobit\n\n\t* ant-definitions.xml: Added gentoo startup files to build package\n\n2007-05-15 22:34  kobit\n\n\t* src/main/java/tigase/xmpp/impl/UrnXmppPing.java: Removed\n\t  whitespaces\n\n2007-05-15 22:34  kobit\n\n\t* src/main/java/tigase/server/XMPPServer.java: Renamed main\n\t  component from tigase-xmpp-server to message-router\n\n2007-05-15 22:33  kobit\n\n\t* src/main/java/tigase/server/sreceiver/ReceiverTaskIfc.java:\n\t  Corrected javadoc\n\n2007-05-15 22:33  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Jingle.java: Corrected javadoc\n\n2007-05-15 22:33  kobit\n\n\t* scripts/gentoo/init.d/tigase: Removed redundant variables\n\t  settings\n\n2007-05-15 22:13  kobit\n\n\t* scripts/gentoo, scripts/gentoo/conf.d,\n\t  scripts/gentoo/conf.d/tigase, scripts/gentoo/init.d,\n\t  scripts/gentoo/init.d/tigase, scripts/tigase.sh: Added tigase\n\t  startup scripts for Gentoo system\n\n2007-05-14 22:36  kobit\n\n\t* etc/tigase-mysql.conf,\n\t  src/main/java/tigase/conf/Configurable.java,\n\t  src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Adding components at runtime with ad-hoc commands is now\n\t  completed\n\n2007-05-14 10:06  kobit\n\n\t* src/main/java/tigase/conf/Configurable.java,\n\t  src/main/java/tigase/server/MessageRouterConfig.java: Defined\n\t  more constants for class names for components\n\n2007-05-14 10:05  kobit\n\n\t* src/main/java/tigase/server/sreceiver/StanzaReceiver.java: Added\n\t  identity for task item service discovery\n\n2007-05-13 12:53  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java: Fixed a bug with\n\t  --admins setting and now component parameters are sorted on the\n\t  ad-hoc command list\n\n2007-05-12 23:59  kobit\n\n\t* src/main/java/tigase/server/sreceiver/AbstractReceiverTask.java,\n\t  src/main/java/tigase/server/sreceiver/NewsDistributor.java,\n\t  src/main/java/tigase/server/sreceiver/ReceiverTaskIfc.java,\n\t  src/main/java/tigase/server/sreceiver/RepoRosterTask.java,\n\t  src/main/java/tigase/server/sreceiver/StanzaReceiver.java: Roster\n\t  and Tasks are now stored in database and all states are\n\t  persistent\n\n2007-05-12 23:59  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  Changed 'component' naming to 'plugin' naming which is more\n\t  appropriate and follows general naming convention in Tigase\n\t  project\n\n2007-05-12 23:58  kobit\n\n\t* src/main/java/tigase/db/RepositoryFactory.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/util/RepositoryUtils.java: Repository\n\t  instance is now stored separately for each component using it to\n\t  avoid multithreaded access to JDBC repository\n\n2007-05-12 23:57  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java: Added command line\n\t  options loading for all starting with '--gen-'\n\n2007-05-12 23:55  kobit\n\n\t* src/main/java/tigase/conf/Configurable.java: Added more contants\n\t  commonly used by different components\n\n2007-05-12 09:41  kobit\n\n\t* src/main/java/tigase/server/sreceiver/AbstractReceiverTask.java,\n\t  src/main/java/tigase/server/sreceiver/ReceiverTaskIfc.java,\n\t  src/main/java/tigase/server/sreceiver/RosterItem.java,\n\t  src/main/java/tigase/server/sreceiver/StanzaReceiver.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Subscription fully works, permissions for subscription and\n\t  posting messages work, posting and distributing messages works\n\n2007-05-11 23:25  kobit\n\n\t* build.properties, pom.xml: Version change to 2.9.0, this is kind\n\t  of alpha version with new features under development\n\n2007-05-11 23:23  kobit\n\n\t* src/main/java/tigase/server/Packet.java,\n\t  src/main/java/tigase/server/sreceiver/AbstractReceiverTask.java,\n\t  src/main/java/tigase/server/sreceiver/NewsDistributor.java,\n\t  src/main/java/tigase/server/sreceiver/ReceiverTaskIfc.java,\n\t  src/main/java/tigase/server/sreceiver/RosterItem.java,\n\t  src/main/java/tigase/server/sreceiver/StanzaReceiver.java:\n\t  Subscription via presence wors in both ways now\n\n2007-05-11 18:58  kobit\n\n\t* src/main/java/tigase/server/sreceiver/StanzaReceiver.java: Basic\n\t  stuff related to service discovery for exiting task instances\n\t  works now\n\n2007-05-11 18:57  kobit\n\n\t* src/main/java/tigase/disco/ServiceEntity.java: Added a few dummy\n\t  javadoc comments, they will be filled with useful stuff later on\n\n2007-05-11 18:57  kobit\n\n\t* src/main/java/tigase/disco/XMPPService.java: Defined separate\n\t  constants for service discovery XMLNSes\n\n2007-05-11 18:20  kobit\n\n\t* src/main/java/tigase/server/sreceiver/StanzaReceiver.java:\n\t  Initial code for service discovery for tasks instances\n\n2007-05-11 08:58  kobit\n\n\t* src/main/java/tigase/server/MessageRouterConfig.java,\n\t  src/main/java/tigase/server/sreceiver/AbstractReceiverTask.java,\n\t  src/main/java/tigase/server/sreceiver/NewsDistributor.java,\n\t  src/main/java/tigase/server/sreceiver/ReceiverTaskIfc.java,\n\t  src/main/java/tigase/server/sreceiver/StanzaReceiver.java:\n\t  Initial implementation of receiver tasks\n\n2007-05-10 22:36  kobit\n\n\t* src/main/java/tigase/server/sreceiver/ReceiverTaskIfc.java,\n\t  src/main/java/tigase/server/sreceiver/StanzaReceiver.java: Almost\n\t  API definition finished, some initial code for loading\n\t  configuration is done\n\n2007-05-10 19:05  kobit\n\n\t* src/main/java/tigase/conf/Configurable.java,\n\t  src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/server/AbstractMessageReceiver.java,\n\t  src/main/java/tigase/server/MessageRouterConfig.java,\n\t  src/main/java/tigase/server/sreceiver/StanzaReceiver.java,\n\t  src/main/java/tigase/server/ssender/StanzaSender.java,\n\t  src/main/java/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  Added support in config generator for multiple external\n\t  components --ext-comp_1, --ext-comp_2 and so on...\n\n2007-05-09 09:51  kobit\n\n\t* src/main/java/tigase/server/sreceiver/StanzaReceiver.java: Added\n\t  more configuration options - replace sender address and message\n\t  type\n\n2007-05-09 09:40  kobit\n\n\t* src/main/java/tigase/server/sreceiver,\n\t  src/main/java/tigase/server/sreceiver/StanzaReceiver.java:\n\t  Initial version - just javadoc\n\n2007-05-08 23:09  kobit\n\n\t* win-stuff, win-stuff/Licence.txt, win-stuff/Tigase.ico,\n\t  win-stuff/Tigase.iss, win-stuff/scripts,\n\t  win-stuff/scripts/InstallTigaseService.bat,\n\t  win-stuff/scripts/Run.bat, win-stuff/scripts/Tigase.bat,\n\t  win-stuff/scripts/Uninst.bat,\n\t  win-stuff/scripts/UninstallTigaseService.bat, win-stuff/wrapper,\n\t  win-stuff/wrapper/wrapper.conf, win-stuff/wrapper/wrapper.dll,\n\t  win-stuff/wrapper/wrapper.exe: Windows installation packaging\n\t  files\n\n2007-05-08 11:22  kobit\n\n\t* build.properties, pom.xml: Version change to 2.8.7\n\n2007-05-08 11:18  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqRegister.java: Fixed a bug\n\t  when the pluging didn't add 'from' attribute for authenticated\n\t  sessions and when the registration request was sent to\n\t  third-party entity like transport\n\n2007-05-07 21:44  kobit\n\n\t* src/main/java/tigase/xmpp/impl/BindResource.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqIq.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqPrivacy.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqPrivate.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/main/java/tigase/xmpp/impl/SaslAuth.java,\n\t  src/main/java/tigase/xmpp/impl/SessionBind.java,\n\t  src/main/java/tigase/xmpp/impl/VCardTemp.java: Added missing\n\t  disco-features, ticket #65: http://server.tigase.org/ticket/65\n\n2007-05-07 14:21  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqRegister.java: Further\n\t  corrections to the fix for the transport registration\n\n2007-05-07 13:16  kobit\n\n\t* build.properties, etc/tigase-mysql.conf, pom.xml,\n\t  src/main/java/tigase/conf/Configurator.java: Version change to\n\t  2.8.6\n\n2007-05-07 12:07  kobit\n\n\t* src/main/java/tigase/server/Command.java: Fix for a bug for a\n\t  case when the command contains no node attribute\n\n2007-05-07 12:07  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqRegister.java: Corrections\n\t  to make it possible to register user in external transport\n\n2007-05-07 12:06  kobit\n\n\t* src/main/java/tigase/xmpp/impl/UrnXmppPing.java: Added one extra\n\t  empty line to make it more readable\n\n2007-05-04 09:38  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  Corrected broken, not-compilable code\n\n2007-05-04 09:18  kobit\n\n\t* build.xml: Added UTF-8 encoding for the build file which is\n\t  default anyway\n\n2007-05-04 09:18  kobit\n\n\t* src/main/java/tigase/xmpp/impl/UrnXmppPing.java: Corrected UTF-8\n\t  character\n\n2007-05-04 09:17  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  Added autoCreateUser=true for connection string if Drupal or\n\t  LibreSource auth is used\n\n2007-05-03 09:37  kobit\n\n\t* src/main/php/drupal/modules/tigase.module: Corrected function\n\t  name\n\n2007-05-03 08:56  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqAuth.java: Corrected\n\t  indendation for the code\n\n2007-05-03 08:54  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqAuth.java: Corrected\n\t  indendation for the code\n\n2007-05-02 17:50  kobit\n\n\t* build.properties, pom.xml: Version change to 2.8.5\n\n2007-05-02 17:49  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  Changed default configuration generation - for Drupal and\n\t  LibreSource jabber:iq:register is not loaded\n\n2007-05-01 16:20  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Fixed fix ;-), corrected condition statement which decided when\n\t  send remote-server-not-found error\n\n2007-05-01 16:12  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Unknown domain error handling improved for dialback packets\n\n2007-05-01 16:07  kobit\n\n\t* src/main/java/tigase/server/Packet.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Fixed a bug in s2s implementation for a remote servers connecting\n\t  from domain which doesn't exist in DNS. It caused infinite loop\n\n2007-05-01 08:57  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Direct presence bug\n\t  fixed - #63\n\n2007-04-30 14:49  kobit\n\n\t* src/main/java/tigase/auth/SaslPLAIN.java,\n\t  src/main/java/tigase/db/jdbc/LibreSourceAuth.java: Changed back\n\t  to use plain plain passwords in LS database\n\n2007-04-30 14:29  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/disco/XMPPService.java,\n\t  src/main/java/tigase/server/MessageRouter.java,\n\t  src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/stats/StatisticsCollector.java: Solved\n\t  problem with missing features for top level service discovery\n\n2007-04-30 14:29  kobit\n\n\t* scripts/tigase.sh: Indentation corrections\n\n2007-04-29 22:43  kobit\n\n\t* build.properties, pom.xml: Version number change to 2.8.4\n\n2007-04-29 22:38  kobit\n\n\t* pom.xml, src/main/java/tigase/server/MessageRouter.java,\n\t  src/main/java/tigase/server/xmppsession/PacketFilter.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java: Fixed a bug with\n\t  ping to virtual domain and error response to unsupported packets\n\n2007-04-29 21:32  kobit\n\n\t* ant-definitions.xml: Added ChangeLog file for binary distribution\n\n2007-04-26 21:57  kobit\n\n\t* ant-definitions.xml: Added database schema scripts for\n\t  distribution packages\n\n2007-04-26 06:21  kobit\n\n\t* build.properties, pom.xml: Version change to 2.8.3\n\n2007-04-25 19:02  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  Added urn:xmpp:ping plugin for standard loaded plugins\n\n2007-04-25 18:32  kobit\n\n\t* src/main/java/tigase/server/MessageRouter.java: Fixed a bug when\n\t  server was dropping unsupported stanzas addressed to the server\n\t  itself instead of returning feature-not-supported error\n\n2007-04-25 17:12  kobit\n\n\t* prj.el, src/main/java/tigase/xmpp/impl/JabberIqPrivate.java:\n\t  Fixed error message when user tries to access other user private\n\t  data\n\n2007-04-25 17:12  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Removed some extra debug logs\n\n2007-04-23 06:53  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Fixed problem with connecting to all other than google servers,\n\t  apparently google is the only one which processes XML names\n\t  spaces correctly, (and now Tigase server does it correctly too)\n\n2007-04-23 06:13  kobit\n\n\t* ant-definitions.xml,\n\t  src/main/java/tigase/xmpp/impl/UrnXmppPing.java: Attempt to\n\t  remove warnings related to redundant casting and UTF-8 encoding\n\n2007-04-21 15:11  kobit\n\n\t* src/main/php, src/main/php/drupal, src/main/php/drupal/modules,\n\t  src/main/php/drupal/modules/tigase.module: Drupal module for\n\t  better integration with Tigase system - adding real online-status\n\t  for drupal users\n\n2007-04-21 15:10  kobit\n\n\t* src/main/java/tigase/server/ssender,\n\t  src/main/java/tigase/server/ssender/FileTask.java,\n\t  src/main/java/tigase/server/ssender/JDBCTask.java,\n\t  src/main/java/tigase/server/ssender/SenderTask.java,\n\t  src/main/java/tigase/server/ssender/StanzaHandler.java,\n\t  src/main/java/tigase/server/ssender/StanzaSender.java: Initial\n\t  version of stanza sender framework - sending stanzas taken from\n\t  directory or database\n\n2007-04-21 14:52  kobit\n\n\t* database/mysql-schema.sql, database/postgresql-schema.sql,\n\t  etc/tigase-mysql.conf,\n\t  src/main/java/tigase/server/ConnectionManager.java,\n\t  src/main/java/tigase/server/MessageRouter.java,\n\t  src/main/java/tigase/server/MessageRouterConfig.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  Added xmpp_stanza table for StanzaSender JDBC task\n\n2007-04-18 07:00  bmalkow\n\n\t* src/main/java/tigase/xmpp/impl/UrnXmppPing.java: Update\n\t  svn:keywords\n\n2007-04-18 06:58  bmalkow\n\n\t* src/main/java/tigase/xmpp/impl/UrnXmppPing.java: Add\n\t  implementation of XEP-0199 (XMPP Ping)\n\n2007-04-16 18:59  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqPrivate.java: Improved\n\t  error handling and handling the case when user doesn't send any\n\t  child element in query\n\n2007-04-16 10:03  kobit\n\n\t* build.properties, pom.xml: Version change to 2.8.2\n\n2007-04-16 10:03  kobit\n\n\t* etc/tigase-mysql.conf,\n\t  src/main/java/tigase/xmpp/impl/VCardTemp.java: Minor cleanup and\n\t  refactoring - renamed mehtod parseVCard to parseXML\n\n2007-04-16 10:02  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqPrivate.java: Initial\n\t  version of private storage implementation\n\n2007-04-16 06:05  kobit\n\n\t* build.properties, pom.xml: Version change to 2.8.1\n\n2007-04-16 06:04  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Minor changes related to different namespace and prefixes\n\t  processing in xmltools\n\n2007-04-15 22:13  kobit\n\n\t* build.properties, pom.xml: Version change to 2.8.0\n\n2007-04-15 22:13  kobit\n\n\t* src/main/java/tigase/xmpp/XMPPDomBuilderHandler.java: Added\n\t  better handling for extra namespaces and element prefixes,\n\t  prefixes should be dripped now and default namespace should be\n\t  updated for element to correct one\n\n2007-04-15 20:29  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Added problem\n\t  handling for the case when user sends subscribe to buddy before\n\t  adding him to his roster using Roster functionality\n\n2007-04-15 19:15  kobit\n\n\t* pom.xml: Dependency to 2.6.0 xmltools added\n\n2007-04-13 22:40  kobit\n\n\t* src/main/java/tigase/server/xmppsession/PacketFilter.java:\n\t  Changed log message slightly to avoid confusing for packet which\n\t  don't have matching plugin and they don't have 'to' attribute\n\t  set, in such case error message is generated, instead of droping\n\t  packet\n\n2007-04-13 21:47  kobit\n\n\t* src/main/java/tigase/xmpp/impl/VCardTemp.java: Correct support\n\t  for old vCard protocol\n\n2007-04-13 21:35  kobit\n\n\t* build.properties, pom.xml: Version change\n\n2007-04-13 21:35  kobit\n\n\t* src/main/java/tigase/disco/XMPPServiceCollector.java,\n\t  src/main/java/tigase/server/MessageRouter.java,\n\t  src/main/java/tigase/server/Packet.java,\n\t  src/main/java/tigase/server/xmppsession/PacketFilter.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqCommand.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqIq.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqStats.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqVersion.java,\n\t  src/main/java/tigase/xmpp/impl/Jingle.java,\n\t  src/main/java/tigase/xmpp/impl/OfflineMessages.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java,\n\t  src/main/java/tigase/xmpp/impl/Roster.java,\n\t  src/main/java/tigase/xmpp/impl/ServiceDiscovery.java,\n\t  src/main/java/tigase/xmpp/impl/SimpleForwarder.java: Minor\n\t  modifications related to clone() method change in xmltools which\n\t  now returns Element to avoid type casting\n\n2007-04-13 21:28  kobit\n\n\t* src/main/java/tigase/xmpp/impl/VCardTemp.java: Small change to\n\t  make it compatible with old vCard spec where element name was all\n\t  uppercase, clone() method in xmltools has been changed so the\n\t  class has been changed to reflect it\n\n2007-04-12 13:31  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Added capabilities to presence information for USER_STATUS\n\t  command\n\n2007-04-10 16:22  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  fixed problem with initial presence for session activated by\n\t  external component\n\n2007-04-10 14:28  kobit\n\n\t* database/postgresql-schema.sql, scripts/repo.sh: Updated user\n\t  repository access files\n\n2007-04-10 14:27  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  Fixed a bug where --test configuration was always generated\n\n2007-04-04 21:39  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/server/MessageRouterConfig.java,\n\t  src/main/java/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  Fixed problem with default configuration generation for component\n\t  names session_1/sess_man\n\n2007-04-04 21:27  kobit\n\n\t* build.properties, pom.xml: Version change\n\n2007-04-04 21:26  kobit\n\n\t* src/main/java/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  Fixed bug in generating default configuration, sess_man instead\n\t  of session_1\n\n2007-04-04 20:05  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java: Changed default\n\t  logging level for console output\n\n2007-04-04 20:00  kobit\n\n\t* src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Removed remote_host global class variable\n\n2007-04-04 07:53  kobit\n\n\t* pom.xml: Dependecies updated for new xmltools version 2.5.0\n\n2007-04-03 21:08  kobit\n\n\t* etc/tigase-mysql.conf,\n\t  src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Modified default config generation, routing part is generated\n\t  automatically\n\n2007-04-03 18:42  kobit\n\n\t* build.properties, pom.xml: Version number change to 2.7.1\n\n2007-04-03 18:42  kobit\n\n\t* src/main/java/tigase/server/AbstractMessageReceiver.java: Moved\n\t  time constants to parent class\n\n2007-04-03 18:42  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Added config wizard option to automaticly pick-up external\n\t  component domain\n\n2007-04-03 18:41  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java: Moved time\n\t  constants to parent class\n\n2007-04-03 18:40  kobit\n\n\t* src/main/java/tigase/server/MessageRouter.java: Uptime statistics\n\t  added\n\n2007-04-03 18:39  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java: Changed default\n\t  logging levels for normal mode, test and debug\n\n2007-04-02 12:48  kobit\n\n\t* prj.el,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  added test mode to SM so it loads different set of plugins in\n\t  test mode, this is necessary to avoid interfrence with other\n\t  functionalities during tests\n\n2007-04-02 12:45  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java: Removed depreciated\n\t  and commented-out code\n\n2007-03-31 21:47  kobit\n\n\t* etc/tigase-mysql.conf,\n\t  src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/server/AbstractMessageReceiver.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java: Spelling\n\t  corrections and default config params slightly changed\n\n2007-03-30 22:50  kobit\n\n\t* pom.xml: Version dependencies update\n\n2007-03-30 22:48  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Added Long/long data types for configuration storage and max\n\t  waiting time for packet parameter is back in configuration\n\n2007-03-30 22:21  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManagerConfig.java,\n\t  src/main/java/tigase/xmpp/impl/ServiceDiscovery.java: Deprecated\n\t  ServiceDiscovery class and removed loading disco from default\n\t  configuration, it is no longer used and needed\n\n2007-03-30 22:15  kobit\n\n\t* build.properties, pom.xml,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Temporarly disabled configuration option to set timeout for\n\t  waiting packets in s2s component\n\n2007-03-30 21:23  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Implemented timeout for packets sent through s2s, if Tigase can\n\t  not connect to remote server for some specified time it giveup\n\t  and returns all packages back to sender\n\n2007-03-30 21:21  kobit\n\n\t* src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Added 'XEP-0114' string to component name to avoid confusion\n\n2007-03-30 09:12  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/disco/ServiceEntity.java,\n\t  src/main/java/tigase/disco/XMPPServiceCollector.java,\n\t  src/main/java/tigase/server/AbstractMessageReceiver.java,\n\t  src/main/java/tigase/server/MessageReceiver.java,\n\t  src/main/java/tigase/server/MessageRouter.java,\n\t  src/main/java/tigase/server/MessageRouterConfig.java,\n\t  src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/stats/StatisticsCollector.java: Final fixes\n\t  for service discovery and cluster management framework\n\n2007-03-28 06:18  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/disco/ServiceEntity.java,\n\t  src/main/java/tigase/disco/XMPPServiceCollector.java,\n\t  src/main/java/tigase/server/AbstractMessageReceiver.java,\n\t  src/main/java/tigase/server/MessageRouter.java,\n\t  src/main/java/tigase/server/MessageRouterConfig.java,\n\t  src/main/java/tigase/server/Packet.java,\n\t  src/main/java/tigase/server/ServerComponent.java,\n\t  src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/stats/StatisticsCollector.java: Changes in\n\t  service-disco layer to allow return correct info from instances\n\t  where SM is not available\n\n2007-03-20 23:05  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java,\n\t  src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Corrected max reconnects value handling\n\n2007-03-20 22:53  kobit\n\n\t* src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Added remote hostname information to service discovery\n\n2007-03-20 22:52  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java: Fixed problem with\n\t  configuration file initialization\n\n2007-03-20 20:50  kobit\n\n\t* src/main/java/tigase/db/jdbc/DrupalAuth.java,\n\t  src/main/java/tigase/db/jdbc/JDBCRepository.java,\n\t  src/main/java/tigase/db/jdbc/LibreSourceAuth.java: Changed JDBC\n\t  connection validation query to some more efficient\n\n2007-03-20 20:49  kobit\n\n\t* src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Start of service-disco implementation for external component\n\n2007-03-20 00:04  kobit\n\n\t* src/main/java/tigase/db/jdbc/LibreSourceAuth.java: Added proper\n\t  code for add/remove user and update user password\n\n2007-03-19 23:24  kobit\n\n\t* src/main/java/tigase/db/UserAuthRepository.java,\n\t  src/main/java/tigase/db/UserAuthRepositoryImpl.java,\n\t  src/main/java/tigase/db/jdbc/DrupalAuth.java,\n\t  src/main/java/tigase/db/jdbc/JDBCRepository.java,\n\t  src/main/java/tigase/db/jdbc/LibreSourceAuth.java,\n\t  src/main/java/tigase/db/xml/XMLRepository.java,\n\t  src/main/java/tigase/server/AbstractMessageReceiver.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Added support for logout() event to database backend so Tigase\n\t  may now write presence status to database. It allows other\n\t  systems display online status. Currently it is implemented in\n\t  LibreSource connector only\n\n2007-03-19 17:21  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/net/ConnectionOpenThread.java,\n\t  src/main/java/tigase/server/AbstractMessageReceiver.java,\n\t  src/main/java/tigase/server/ConnectionManager.java,\n\t  src/main/java/tigase/server/MessageRouter.java,\n\t  src/main/java/tigase/server/MessageRouterConfig.java,\n\t  src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Fixes for problems with reconfiguration at runtime, now it is\n\t  also even possible to switch on/off components, add new\n\t  components, add/remove TCP/IP port listeners and so on....\n\n2007-03-14 12:06  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Added fix for proper handling malformed dialback requests sent by\n\t  very old jabber servers\n\n2007-03-13 10:56  kobit\n\n\t* src/main/java/tigase/auth/SaslPLAIN.java,\n\t  src/main/java/tigase/db/jdbc/LibreSourceAuth.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  Implementation of authentication agains LibreSource database is\n\t  completed - this is simple implementation, just authentication\n\t  and nothing more\n\n2007-03-13 10:55  kobit\n\n\t* src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Started implementation for service-discovery support\n\n2007-03-13 10:54  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java: Added fix for\n\t  a bug when service is stopped in accept method\n\n2007-03-12 16:03  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Fixed a problem with connection stopped in accept method, no\n\t  retry was performed then\n\n2007-03-12 15:55  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java: Fixed problem\n\t  when stop is called afer unssuccesful accept call\n\n2007-03-12 15:48  kobit\n\n\t* src/main/java/tigase/net/IOService.java: Fixed problem when stop\n\t  is called afer unssuccesful accept call\n\n2007-03-12 15:41  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Added fix for the case when connection is lost before handshaking\n\t  has started, like exception in accept phase\n\n2007-03-12 12:27  kobit\n\n\t* src/main/java/tigase/server/xmppsession/PacketFilter.java: Fixed\n\t  packet filter delivering packet to proper active resource - now\n\t  it behaves as described in RFC\n\n2007-03-12 11:56  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Commented out code for stop-other service and added proper\n\t  handling for case when packet write to socket was unsuccessful\n\n2007-03-10 22:50  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Added proper handling for the situation when accept connection is\n\t  closed before handshaking has been completed\n\n2007-03-10 22:45  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Changed the order in which packets are sent, first all waiting\n\t  controll ackets, next db:result on connect\n\n2007-03-10 15:53  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Added one more logging messgae to detect whether stopping old\n\t  connection is really an issue\n\n2007-03-10 15:50  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Fixed problem with initializing new connection for remote server\n\t  when old connection still seems active\n\n2007-03-07 11:28  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Another fix for asynchronous dialback processing\n\n2007-03-07 11:14  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  BIG, BIG bug fixed finally\n\n2007-03-07 09:54  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Even more logging to track down opening extra s2s connections\n\n2007-03-06 22:35  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Fixed bug in processing results from processDialback method,\n\t  after this method is finished service may be available in either\n\t  handsahking or established Map\n\n2007-03-06 22:12  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Added more logging for dialback verification results\n\n2007-03-06 21:38  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Implementation for invalid dialback verification\n\n2007-03-06 15:33  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Added diagnostic logs for stream opening - possible multiple\n\t  stream open calls\n\n2007-03-06 15:08  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Added logging for all dialback handshaking\n\n2007-03-06 14:38  kobit\n\n\t* src/main/java/tigase/server/Packet.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java,\n\t  src/main/java/tigase/xmpp/impl/Roster.java: Proper handling of\n\t  presence error in case of s2s can't send it to destination\n\t  address (or it should be just ignored)\n\n2007-03-06 13:46  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Added synchronization to service stopped handler\n\n2007-03-05 23:01  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Fixed disconnection other dialback connection method and added\n\t  more logging info\n\n2007-03-05 21:37  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java: Added logging\n\t  messages to watchdog\n\n2007-03-05 21:03  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java: Fixed log\n\t  message position, put in wrong place by mistake\n\n2007-03-05 20:51  kobit\n\n\t* src/main/java/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Added parameter about the maximum iddle time for connection\n\n2007-03-05 20:51  kobit\n\n\t* src/main/java/tigase/xmpp/XMPPIOService.java: Added last transfer\n\t  timestamp information\n\n2007-03-05 20:49  kobit\n\n\t* src/main/java/tigase/server/MessageRouter.java: Removed DNS\n\t  resolving from message router\n\n2007-03-05 20:48  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java: Added\n\t  connection watchdog thread\n\n2007-03-05 20:48  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Full support for stream:error, detecting improper hosts in\n\t  sender/receiver stanza addreses\n\n2007-03-05 10:00  kobit\n\n\t* src/main/java/tigase/net/IOService.java,\n\t  src/main/java/tigase/server/ConnectionManager.java,\n\t  src/main/java/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/main/java/tigase/xmpp/XMPPIOService.java: A few corrections\n\t  in IO/NET/SERVICES - check whether service ID is no duplicated\n\n2007-03-02 13:58  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  All handshaking connections are now also dumped to log file on\n\t  request\n\n2007-03-02 12:23  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java,\n\t  src/main/java/tigase/xmpp/XMPPIOService.java: Added extra logging\n\t  to detect s2s problems\n\n2007-03-01 11:10  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java,\n\t  src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Fixed a bug in s2s which caused automatic reconnecting s2s\n\t  connection even if it was not necessary\n\n2007-03-01 04:02  kobit\n\n\t* src/main/java/tigase/io/SocketIO.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/xmpp/XMPPIOService.java: Fixed a bug with\n\t  logging data buffer which is NULL\n\n2007-03-01 00:05  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Added extra log message\n\n2007-02-28 23:54  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Proper handling of stream:error in s2s\n\n2007-02-28 23:39  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Proper error response added if user account does not exist\n\n2007-02-28 23:25  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java: Proper error\n\t  response added if user account does not exist\n\n2007-02-28 21:59  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Fixed code for reconnecting s2s service if the first attempt was\n\t  unsuccessful\n\n2007-02-28 21:27  kobit\n\n\t* src/main/java/tigase/net/IOService.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/main/java/tigase/xmpp/XMPPIOService.java: Some fixes to s2s\n\t  implementation - retrying to connect if first try was\n\t  unsuccessfull and fixed a bug in IOService in stop() method -\n\t  handler is called in finally clausule\n\n2007-02-28 13:31  kobit\n\n\t* src/main/java/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  List of connected servers is now sorted to make it easier to find\n\t  particular connection\n\n2007-02-28 13:11  kobit\n\n\t* src/main/java/tigase/server/Command.java,\n\t  src/main/java/tigase/server/ConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/main/java/tigase/stats/StatRecord.java,\n\t  src/main/java/tigase/stats/StatisticType.java,\n\t  src/main/java/tigase/stats/StatisticsCollector.java: More\n\t  statistics about network connections and packet waiting in s2s\n\t  queues\n\n2007-02-27 23:04  kobit\n\n\t* src/main/java/tigase/xmpp/impl/JabberIqIq.java: Just for fun\n\n2007-02-22 23:04  kobit\n\n\t* etc/tigase-mysql.conf,\n\t  src/main/java/tigase/xmpp/impl/Presence.java: Solved initial\n\t  presence broadcating problem for many resources connected\n\n2007-02-22 00:07  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Jingle.java: Added session element\n\n2007-02-21 23:33  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/xmpp/impl/Jingle.java: Support for jingle in\n\t  Yate added\n\n2007-02-21 22:08  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  USER_STATUS - presence status fixed\n\n2007-02-21 22:00  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/stats/StatisticType.java: USER_STATUS\n\t  processing command moved to different place\n\n2007-02-15 06:52  kobit\n\n\t* build.properties, pom.xml: Version change to 2.6.4\n\n2007-02-15 06:51  kobit\n\n\t* src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/disco/XMPPService.java,\n\t  src/main/java/tigase/server/AbstractMessageReceiver.java,\n\t  src/main/java/tigase/server/Command.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/stats/StatRecord.java,\n\t  src/main/java/tigase/stats/StatisticsCollector.java: Improved\n\t  ad-hoc commands for configuration settings and statistics\n\t  retrieval\n\n2007-02-14 10:37  kobit\n\n\t* build.properties, pom.xml: Version number change\n\n2007-02-14 09:44  kobit\n\n\t* src/main/java/tigase/net/IOService.java,\n\t  src/main/java/tigase/xmpp/XMPPIOService.java: Fixed a bug with\n\t  UTF-8 encoder/decoder for network data\n\n2007-02-13 23:50  kobit\n\n\t* build.properties, pom.xml,\n\t  src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/disco/XMPPServiceCollector.java,\n\t  src/main/java/tigase/net/IOService.java,\n\t  src/main/java/tigase/server/AbstractComponentRegistrator.java,\n\t  src/main/java/tigase/server/Command.java,\n\t  src/main/java/tigase/stats/StatisticsCollector.java,\n\t  src/main/java/tigase/xmpp/impl/OfflineMessages.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java: Reconfiguration at\n\t  run-time ready - not well tested yet\n\n2007-02-12 14:25  kobit\n\n\t* src/main/java/tigase/server/xmppsession/PacketFilter.java:\n\t  Corrected handling stanzas: <iq type='result'/>\n\n2007-02-12 14:03  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Presence update to\n\t  other user accounts corrected - attributes from/to where set\n\t  incorrectly\n\n2007-02-12 12:04  kobit\n\n\t* src/main/java/tigase/xmpp/XMPPSession.java,\n\t  src/main/java/tigase/xmpp/impl/Presence.java: Resource priority\n\t  is now set correctly and message to account with multiple active\n\t  resources is handled properly\n\n2007-02-11 20:58  kobit\n\n\t* build.properties, pom.xml,\n\t  src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/disco,\n\t  src/main/java/tigase/disco/ServiceEntity.java,\n\t  src/main/java/tigase/disco/ServiceIdentity.java,\n\t  src/main/java/tigase/disco/XMPPService.java,\n\t  src/main/java/tigase/disco/XMPPServiceCollector.java,\n\t  src/main/java/tigase/server/MessageRouter.java,\n\t  src/main/java/tigase/server/MessageRouterConfig.java,\n\t  src/main/java/tigase/server/ServiceEntity.java,\n\t  src/main/java/tigase/server/ServiceIdentity.java,\n\t  src/main/java/tigase/server/XMPPService.java,\n\t  src/main/java/tigase/server/XMPPServiceCollector.java,\n\t  src/main/java/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/main/java/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/stats/StatisticsCollector.java,\n\t  src/main/java/tigase/xmpp/impl/ServiceDiscovery.java: Service\n\t  discovery API changed and small refactoring to move all disco\n\t  related classes to separate package\n\n2007-02-11 20:53  kobit\n\n\t* src/main/java/tigase/xmpp/impl/VCardTemp.java: Fixed a bug\n\t  related to strange stanza when user session is null....\n\n2007-02-11 14:06  kobit\n\n\t* src/main/java/tigase/conf/Configurable.java,\n\t  src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/server/AbstractComponentRegistrator.java,\n\t  src/main/java/tigase/server/MessageRouter.java,\n\t  src/main/java/tigase/server/ServiceEntity.java,\n\t  src/main/java/tigase/server/ServiceIdentity.java,\n\t  src/main/java/tigase/server/XMPPServer.java,\n\t  src/main/java/tigase/server/XMPPService.java,\n\t  src/main/java/tigase/server/XMPPServiceCollector.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/stats/StatisticsCollector.java,\n\t  src/main/java/tigase/stats/StatisticsContainer.java: New service\n\t  discovert API\n\n2007-02-08 11:30  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Fixed problem with\n\t  initial presence after subscription, server used to send presnece\n\t  without resource part, Thank to Daniele for the fix\n\n2007-02-07 23:34  kobit\n\n\t* src/main/java/tigase/io/TLSEventHandler.java,\n\t  src/main/java/tigase/io/TLSIO.java,\n\t  src/main/java/tigase/io/TLSWrapper.java,\n\t  src/main/java/tigase/io/TelnetClient.java,\n\t  src/main/java/tigase/io/TelnetServer.java,\n\t  src/main/java/tigase/net/IOService.java,\n\t  src/main/java/tigase/xmpp/XMPPIOService.java: Fixed socket write\n\t  method for TLS connection, bug causing possible data loss for\n\t  large buffers, Gajim still inserts line-breaks in the middle of\n\t  large buffer, somewhere near 16k data block\n\n2007-02-04 22:43  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManagerConfig.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqSi.java: More components\n\t  depreciated due to PacketFilter use\n\n2007-02-04 18:19  bmalkow\n\n\t* pom.xml: add maven release plugin configuration\n\n2007-02-03 17:15  kobit\n\n\t* src/main/java/tigase/server/XMPPServer.java,\n\t  src/main/java/tigase/server/XMPPServiceCollector.java: Added\n\t  server version information to service discovery\n\n2007-02-03 09:25  kobit\n\n\t* build.properties, pom.xml: Version change\n\n2007-02-03 09:23  kobit\n\n\t* src/main/java/tigase/server/xmppsession/SessionManager.java: Fix\n\t  for the bug introduced by PacketFilter caused to ignore privacy\n\t  lists\n\n2007-02-02 16:20  kobit\n\n\t* src/main/java/tigase/server/xmppsession/PacketFilter.java: Fixed\n\t  a problem with NullPointerException when there is no 'from'\n\t  attribute in PacketFilter\n\n2007-02-02 16:10  kobit\n\n\t* build.xml,\n\t  src/main/java/tigase/server/xmppsession/PacketFilter.java: Fixed\n\t  a problem with NullPointerException when there is no 'to'\n\t  attribute in PacketFilter\n\n2007-02-02 15:47  kobit\n\n\t* src/main/java/tigase/server/xmppsession/PacketFilter.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerConfig.java,\n\t  src/main/java/tigase/xmpp/impl/IBB.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqOOB.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqSocks5Bytestreams.java,\n\t  src/main/java/tigase/xmpp/impl/Message.java: Introduced\n\t  PacketFilter in place of SimpleForwarder\n\n2007-02-02 11:33  kobit\n\n\t* .classpath, .project, .settings: Removed eclipse configuration\n\t  files to avoid overwriting other developers eclipse settings\n\n2007-02-02 11:09  kobit\n\n\t* src/main/java/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java:\n\t  Fixed problem with incorrectly sent stream features\n\n2007-01-28 17:28  kobit\n\n\t* etc/tigase-mysql.conf, etc/tigase-pgsql.conf: sample\n\t  configuration file improved - added sample db connection uri\n\n2007-01-28 17:02  kobit\n\n\t* src/main/assembly/prodenv.xml: Removed some scripts not needed by\n\t  the user\n\n2007-01-28 16:02  bmalkow\n\n\t* pom.xml: fix error in dependency\n\n2007-01-28 15:54  kobit\n\n\t* etc, etc/tigase-mysql.conf, etc/tigase-pgsql.conf,\n\t  etc/tigase.conf, scripts/tigase.sh,\n\t  src/main/assembly/prodenv.xml: Configuration changed to better\n\t  integrate with maven\n\n2007-01-28 12:32  bmalkow\n\n\t* pom.xml: add version in package filename\n\n2007-01-28 12:17  bmalkow\n\n\t* pom.xml: add 'optional' attribute in dependency\n\n2007-01-28 12:04  bmalkow\n\n\t* pom.xml, src/main/assembly, src/main/assembly/prodenv.xml: add\n\t  simple assembly goal config\n\n2007-01-27 08:28  kobit\n\n\t* src/main/java/tigase/db/jdbc/JDBCRepository.java: Removed some\n\t  cache calls - calls for specific data items where cache was not\n\t  working correctly\n\n2007-01-26 22:22  kobit\n\n\t* database/mysql-schema.sql, database/postgresql-schema.sql,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  Fixed problem with default auth-db-url if set only user-db-url\n\n2007-01-26 21:07  kobit\n\n\t* src/main/java/tigase/db/jdbc/JDBCRepository.java: Changed cache\n\t  size back to 10000\n\n2007-01-26 16:29  kobit\n\n\t* src/main/java/tigase/db/jdbc/JDBCRepository.java: Increased cache\n\t  size\n\n2007-01-26 11:57  kobit\n\n\t* src/main/java/tigase/server/ConnectionManager.java: Allow\n\t  component reconnection for about 1000h and then giveup\n\n2007-01-26 08:44  kobit\n\n\t* src/main/java/tigase/server/Permissions.java,\n\t  src/main/java/tigase/server/xmppsession/Permissions.java: Moved\n\t  to right location\n\n2007-01-25 23:18  kobit\n\n\t* build.properties, pom.xml, prj.el,\n\t  src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/io/TLSWrapper.java,\n\t  src/main/java/tigase/server/Command.java,\n\t  src/main/java/tigase/server/MessageRouter.java,\n\t  src/main/java/tigase/server/Packet.java,\n\t  src/main/java/tigase/server/XMPPService.java,\n\t  src/main/java/tigase/server/XMPPServiceCollector.java,\n\t  src/main/java/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/main/java/tigase/server/xmppsession/Permissions.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManagerConfig.java,\n\t  src/main/java/tigase/stats/StatisticsCollector.java,\n\t  src/main/java/tigase/xmpp/XMPPImplIfc.java,\n\t  src/main/java/tigase/xmpp/XMPPProcessor.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqCommand.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqRegister.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqSi.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqStats.java,\n\t  src/main/java/tigase/xmpp/impl/JabberIqVersion.java,\n\t  src/main/java/tigase/xmpp/impl/OfflineMessages.java,\n\t  src/main/java/tigase/xmpp/impl/ServiceDiscovery.java,\n\t  src/main/java/tigase/xmpp/impl/StartTLS.java: ad-hoc commands,\n\t  service discovery, Yate integration first step\n\n2007-01-25 21:05  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: incoming direct\n\t  availability presence now allowed even if remote entity is not in\n\t  roster - required for MUC\n\n2007-01-25 14:56  kobit\n\n\t* src/main/java/tigase/xmpp/impl/Presence.java: Sending direct\n\t  presence bug fixed now\n\n2007-01-24 09:27  bmalkow\n\n\t* .classpath, pom.xml: fix errors in maven configuration\n\n2007-01-20 19:35  bmalkow\n\n\t* pom.xml: add deploy information to maven config\n\n2007-01-20 18:46  kobit\n\n\t* src/main/java/tigase/io/TLSWrapper.java: Indentation corrected\n\n2007-01-20 18:38  kobit\n\n\t* src/main/java/tigase/io/TLSWrapper.java: Indentation corrected\n\n2007-01-20 18:12  kobit\n\n\t* build.properties, build.xml,\n\t  src/main/java/tigase/conf/Configurator.java,\n\t  src/main/java/tigase/io/TLSWrapper.java,\n\t  src/main/java/tigase/server/XMPPService.java,\n\t  src/main/java/tigase/server/XMPPServiceCollector.java,\n\t  src/main/java/tigase/server/xmppsession/SessionManager.java,\n\t  src/main/java/tigase/xmpp/impl/ServiceDiscovery.java: service\n\t  discovery-items and ad-hoc command for configuration in progress\n\n2007-01-20 17:59  bmalkow\n\n\t* ., .classpath, .project, .settings,\n\t  .settings/org.eclipse.jdt.core.prefs,\n\t  .settings/org.eclipse.jdt.ui.prefs, pom.xml, src/main,\n\t  src/main/java, src/main/java/tigase, src/test, src/test/java,\n\t  src/tigase: convert to maven2 nature\n\n2007-01-19 17:37  kobit\n\n\t* src/tigase/server/Command.java, src/tigase/server/Packet.java:\n\t  Modified command so now command id is in node attribute instead\n\t  of action\n\n2007-01-19 10:28  kobit\n\n\t* scripts/tigase.sh, src/tigase/conf/Configurable.java,\n\t  src/tigase/conf/Configurator.java,\n\t  src/tigase/net/ConnectionOpenThread.java,\n\t  src/tigase/server/AbstractMessageReceiver.java,\n\t  src/tigase/server/ConnectionManager.java,\n\t  src/tigase/server/MessageRouter.java,\n\t  src/tigase/server/MessageRouterConfig.java,\n\t  src/tigase/server/XMPPServer.java,\n\t  src/tigase/server/XMPPService.java,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/server/xmppsession/SessionManagerConfig.java: Number\n\t  of changes, but the most important related do confifuration\n\t  generation\n\n2007-01-17 00:55  kobit\n\n\t* src/tigase/server/Command.java,\n\t  src/tigase/server/ConnectionManager.java,\n\t  src/tigase/server/Packet.java,\n\t  src/tigase/server/XMPPServiceCollector.java,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/stats/StatisticsCollector.java,\n\t  src/tigase/xmpp/StanzaType.java,\n\t  src/tigase/xmpp/impl/JabberIqStats.java,\n\t  src/tigase/xmpp/impl/ServiceDiscovery.java,\n\t  src/tigase/xmpp/impl/StartTLS.java: Switched to ad-hoc command\n\t  for communication between components and implemented USER_STATUS\n\t  command. Update from xmltools is requiredsvn status\n\n2007-01-15 17:26  kobit\n\n\t* src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/stats/StatRecord.java: Added repository information to\n\t  statuses\n\n2007-01-15 17:10  kobit\n\n\t* src/tigase/db/UserAuthRepository.java,\n\t  src/tigase/db/UserAuthRepositoryImpl.java,\n\t  src/tigase/db/UserRepository.java,\n\t  src/tigase/db/jdbc/DrupalAuth.java,\n\t  src/tigase/db/jdbc/JDBCRepository.java,\n\t  src/tigase/db/xml/XMLRepository.java: Added methods to retrieve\n\t  repository URI\n\n2007-01-14 02:25  kobit\n\n\t* src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/server/xmppsession/SessionManagerConfig.java,\n\t  src/tigase/stats/StatisticsCollector.java,\n\t  src/tigase/xmpp/impl/JabberIqStats.java: Changed stats\n\t  implementation to conform XEP-39\n\n2007-01-13 22:12  kobit\n\n\t* prj.el, src/tigase/net/ConnectionOpenThread.java,\n\t  src/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/tigase/server/xmppsession/SessionManagerConfig.java,\n\t  src/tigase/xmpp/impl/IBB.java,\n\t  src/tigase/xmpp/impl/JabberIqOOB.java,\n\t  src/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/tigase/xmpp/impl/JabberIqSi.java,\n\t  src/tigase/xmpp/impl/JabberIqSocks5Bytestreams.java,\n\t  src/tigase/xmpp/impl/JabberIqVersion.java,\n\t  src/tigase/xmpp/impl/Message.java,\n\t  src/tigase/xmpp/impl/SimpleForwarder.java: File transfer protocol\n\t  implemented\n\n2007-01-12 17:16  kobit\n\n\t* src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/xmpp/impl/JabberIqRoster.java: Fixed bug with\n\t  processing incorrect Roster stanza\n\n2007-01-11 22:44  kobit\n\n\t* src/tigase/server/ConnectionManager.java: Added more detailed\n\t  loggin info\n\n2007-01-11 22:41  kobit\n\n\t* scripts/tigase.sh, src/tigase/server/ConnectionManager.java:\n\t  Added more detailed loggin info\n\n2007-01-09 06:16  kobit\n\n\t* src/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Removed packing stanza into routed packet - not needed anymore??\n\n2007-01-08 23:40  kobit\n\n\t* database/postgresql-schema.sql: Postgresql DB schema\n\n2007-01-07 23:08  kobit\n\n\t* ant-definitions.xml, build.xml, manifest.temp: Build scripts\n\t  slightly changed to not force user to have svn tools installed\n\t  separately\n\n2006-12-18 16:38  kobit\n\n\t* scripts/config.sh, scripts/repo.sh,\n\t  src/tigase/db/UserAuthRepositoryImpl.java,\n\t  src/tigase/xmpp/XMPPDomBuilderHandler.java: Corrected support for\n\t  IDN domain names\n\n2006-12-18 10:29  kobit\n\n\t* src/tigase/db/jdbc/JDBCRepository.java: Set autocommit to true\n\t  for JDBC connection\n\n2006-12-17 23:42  kobit\n\n\t* src/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Component protocol (JEP-114) implemented - untested\n\n2006-12-17 23:05  kobit\n\n\t* src/tigase/server/XMPPServer.java,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/xmpp/impl/Privacy.java: Privacy lists management a few\n\t  bugs fixed\n\n2006-12-17 15:31  kobit\n\n\t* src/tigase/db/UserAuthRepositoryImpl.java,\n\t  src/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Fixed nullpointer exception bug in authorizatin module\n\n2006-12-17 15:21  kobit\n\n\t* src/tigase/server/xmppserver/ServerConnectionManager.java: Fixed\n\t  problem with command processing - temporarly, no command are\n\t  processed by this component for now\n\n2006-12-15 12:44  kobit\n\n\t* src/tigase/auth/LoginHandler.java,\n\t  src/tigase/auth/ResourceConnectionCallback.java,\n\t  src/tigase/auth/SaslPLAIN.java,\n\t  src/tigase/auth/TigaseSaslProvider.java,\n\t  src/tigase/auth/TigaseSaslServerFactory.java,\n\t  src/tigase/db/AuthorizationException.java,\n\t  src/tigase/db/DBInitException.java,\n\t  src/tigase/db/NonAuthUserRepository.java,\n\t  src/tigase/db/RepositoryFactory.java,\n\t  src/tigase/db/TigaseDBException.java,\n\t  src/tigase/db/UserAuthRepository.java,\n\t  src/tigase/db/UserAuthRepositoryImpl.java,\n\t  src/tigase/db/jdbc/DrupalAuth.java,\n\t  src/tigase/db/jdbc/JDBCRepository.java,\n\t  src/tigase/io/BufferUnderflowException.java,\n\t  src/tigase/io/IOInterface.java,\n\t  src/tigase/io/SSLContextContainer.java,\n\t  src/tigase/io/SampleSocketThread.java,\n\t  src/tigase/io/SocketIO.java, src/tigase/io/TLSIO.java,\n\t  src/tigase/io/TLSStatus.java, src/tigase/io/TLSUtil.java,\n\t  src/tigase/io/TLSWrapper.java, src/tigase/io/TelnetClient.java,\n\t  src/tigase/io/TelnetServer.java, src/tigase/net/Accept.java,\n\t  src/tigase/net/ConnectionOpenListener.java,\n\t  src/tigase/net/ConnectionOpenThread.java,\n\t  src/tigase/net/ConnectionType.java,\n\t  src/tigase/net/IOServiceListener.java,\n\t  src/tigase/net/SocketReadThread.java,\n\t  src/tigase/net/SocketType.java,\n\t  src/tigase/server/AbstractComponentRegistrator.java,\n\t  src/tigase/server/AbstractMessageReceiver.java,\n\t  src/tigase/server/Command.java,\n\t  src/tigase/server/ConnectionManager.java,\n\t  src/tigase/server/MessageRouterConfig.java,\n\t  src/tigase/server/XMPPServer.java,\n\t  src/tigase/server/xmppsession/SessionManagerConfig.java,\n\t  src/tigase/stats/StatRecord.java,\n\t  src/tigase/stats/StatisticType.java,\n\t  src/tigase/util/ElementUtils.java,\n\t  src/tigase/util/LogFormatter.java,\n\t  src/tigase/util/RepositoryUtils.java,\n\t  src/tigase/util/RoutingsContainer.java,\n\t  src/tigase/xmpp/RepositoryAccess.java,\n\t  src/tigase/xmpp/StanzaType.java,\n\t  src/tigase/xmpp/XMPPIOService.java,\n\t  src/tigase/xmpp/XMPPIOServiceListener.java,\n\t  src/tigase/xmpp/XMPPImplIfc.java,\n\t  src/tigase/xmpp/XMPPPostprocessorIfc.java,\n\t  src/tigase/xmpp/XMPPPreprocessorIfc.java,\n\t  src/tigase/xmpp/XMPPProcessorIfc.java,\n\t  src/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/tigase/xmpp/XMPPSession.java,\n\t  src/tigase/xmpp/XMPPStopListenerIfc.java,\n\t  src/tigase/xmpp/impl/BindResource.java,\n\t  src/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/tigase/xmpp/impl/JabberIqPrivacy.java,\n\t  src/tigase/xmpp/impl/JabberIqRegister.java,\n\t  src/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/tigase/xmpp/impl/JabberIqStats.java,\n\t  src/tigase/xmpp/impl/JabberIqVersion.java,\n\t  src/tigase/xmpp/impl/Message.java,\n\t  src/tigase/xmpp/impl/OfflineMessages.java,\n\t  src/tigase/xmpp/impl/Presence.java,\n\t  src/tigase/xmpp/impl/Privacy.java,\n\t  src/tigase/xmpp/impl/Roster.java,\n\t  src/tigase/xmpp/impl/SaslAuth.java,\n\t  src/tigase/xmpp/impl/ServiceDiscovery.java,\n\t  src/tigase/xmpp/impl/SessionBind.java,\n\t  src/tigase/xmpp/impl/StartTLS.java,\n\t  src/tigase/xmpp/impl/VCardTemp.java: e-mail address corrected\n\n2006-12-15 12:38  kobit\n\n\t* prj.el:\n\n2006-12-15 12:18  kobit\n\n\t* prj.el, src/tigase/db/UserAuthRepositoryImpl.java,\n\t  src/tigase/db/jdbc/DrupalAuth.java,\n\t  src/tigase/io/SSLContextContainer.java: Now uses own Tigase\n\t  implementation of Base64\n\n2006-12-14 15:49  kobit\n\n\t* src/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Some fixes in implementation, work not finished yet but it\n\t  compiles\n\n2006-12-14 15:49  kobit\n\n\t* src/tigase/auth/TigaseSaslServerFactory.java,\n\t  src/tigase/db/jdbc/JDBCRepository.java: Corrections related to\n\t  JDK-1.6 version change\n\n2006-12-14 15:32  kobit\n\n\t* build.properties: Version number change to 2.3.4\n\n2006-12-14 15:32  kobit\n\n\t* src/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  XEP-114 coponent protocol implementation in progress\n\n2006-12-14 15:31  kobit\n\n\t* src/tigase/xmpp/impl/JabberIqPrivacy.java,\n\t  src/tigase/xmpp/impl/Privacy.java: Active/default list is kept in\n\t  user session data to improve performance\n\n2006-12-14 15:30  kobit\n\n\t* src/tigase/db/jdbc/JDBCRepository.java: Increased cache size to\n\t  10k elements, cache.remove() has been improved so now\n\t  cache.clear() is replaced with cache.remove(user_id)\n\n2006-12-13 23:29  kobit\n\n\t* src/tigase/io/SSLContextContainer.java: Now adding all ICA\n\t  certificates for each domain keystore\n\n2006-12-13 12:52  kobit\n\n\t* certs/rsa-keystore: Dummy certificate\n\n2006-12-13 12:50  kobit\n\n\t* src/tigase/xmpp/impl/JabberIqPrivacy.java,\n\t  src/tigase/xmpp/impl/Privacy.java: Fixed a few minor bugs in\n\t  privacy lists management\n\n2006-12-13 12:50  kobit\n\n\t* src/tigase/io/SSLContextContainer.java: Fixed problem with loadin\n\t  certain types of certificates\n\n2006-12-10 19:22  kobit\n\n\t* src/tigase/server/xmppserver/ServerConnectionManager.java: vHosts\n\t  bug in s2s fixed now\n\n2006-12-10 15:49  kobit\n\n\t* build.properties,\n\t  src/tigase/server/xmppserver/ServerConnectionManager.java: vHosts\n\t  for s2s bug fixed\n\n2006-12-05 19:17  kobit\n\n\t* build.xml: Fixed build numbering problem\n\n2006-12-05 19:17  kobit\n\n\t* src/tigase/xmpp/impl/Presence.java: Fixed problem with sending\n\t  initial presence from all active resources\n\n2006-12-04 23:52  kobit\n\n\t* certs/rsa-keystore, src/tigase/io/SSLContextContainer.java,\n\t  src/tigase/io/TLSUtil.java, src/tigase/io/TelnetClient.java,\n\t  src/tigase/io/TelnetServer.java,\n\t  src/tigase/server/ConnectionManager.java: Default certificate now\n\t  implemented, SSL uses default and TLS uses default if certificate\n\t  for selected domain does not exist\n\n2006-12-03 21:55  kobit\n\n\t* certs/rsa-keystore, src/tigase/io/SSLContextContainer.java,\n\t  src/tigase/io/TLSUtil.java, src/tigase/io/TelnetClient.java,\n\t  src/tigase/io/TelnetServer.java, src/tigase/net/IOService.java,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  Implemented support for vHosts certificates\n\n2006-11-29 23:31  kobit\n\n\t* build.properties: Version change to 2.3.2\n\n2006-11-29 23:31  kobit\n\n\t* src/tigase/db/jdbc/JDBCRepository.java: Removed all\n\t  synchronization code from the implementation as the class is\n\t  intended to use in single thread\n\n2006-11-29 15:45  kobit\n\n\t* build.properties: Version change\n\n2006-11-29 15:05  kobit\n\n\t* src/tigase/server/AbstractMessageReceiver.java: Fixed dead lock\n\t  problem on multi-cpu machines - replaced single thread/queue with\n\t  2 queues and a thread for each\n\n2006-11-29 15:04  kobit\n\n\t* src/tigase/server/ConnectionManager.java: Changed loging level\n\t  for case when service for packet can not be found\n\n2006-11-29 15:03  kobit\n\n\t* src/tigase/server/xmppsession/SessionManager.java: Changed loging\n\t  level for case when session can not be found\n\n2006-11-29 15:03  kobit\n\n\t* src/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  Removed commented import statement\n\n2006-11-29 15:02  kobit\n\n\t* src/tigase/db/jdbc/JDBCRepository.java: Added synchronization\n\t  code around checking connection code\n\n2006-11-29 15:02  kobit\n\n\t* src/tigase/conf/Configurator.java: Added some more test code to\n\t  change logging level after some timeout to diagnoze lockup\n\t  problem\n\n2006-11-29 15:01  kobit\n\n\t* src/tigase/xmpp/RepositoryAccess.java: Changed loggin level from\n\t  warning to finest for usernotfound exception\n\n2006-11-27 21:39  kobit\n\n\t* src/tigase/db/jdbc/JDBCRepository.java: Wrapped cache object in\n\t  synchronized Map\n\n2006-11-27 14:38  kobit\n\n\t* src/tigase/util/SimpleCache.java: Moved to tigase-util subproject\n\n2006-11-26 23:42  kobit\n\n\t* build.properties: Version change\n\n2006-11-26 23:02  kobit\n\n\t* ant-definitions.xml, build.xml: Modied build system to\n\t  automatically create MANIFEST file\n\n2006-11-26 22:43  kobit\n\n\t* MANIFEST.MF: Removed file which is automatically generated for\n\t  each JAR file\n\n2006-11-26 22:43  kobit\n\n\t* MANIFEST.MF: Version change\n\n2006-11-26 22:42  kobit\n\n\t* MANIFEST.MF, ant-definitions.xml, build.xml, package.html,\n\t  scripts/tigase.sh, src/tigase/db/jdbc/DrupalAuth.java: Added\n\t  timestamp for login and last access in Drupal database\n\n2006-11-26 22:41  kobit\n\n\t* src/tigase/db/jdbc/JDBCRepository.java,\n\t  src/tigase/util/SimpleCache.java: Added simple caching for JDBC\n\t  connector\n\n2006-11-25 08:50  kobit\n\n\t* MANIFEST.MF, ant-definitions.xml: Modified building function to\n\t  include new startup script\n\n2006-11-20 00:16  kobit\n\n\t* MANIFEST.MF, src/tigase/server/xmppsession/SessionManager.java:\n\t  Fixed a bug with improperly handling disconnection in case of\n\t  user unregistration\n\n2006-11-18 19:30  kobit\n\n\t* MANIFEST.MF, src/tigase/db/UserAuthRepository.java,\n\t  src/tigase/db/UserAuthRepositoryImpl.java,\n\t  src/tigase/db/jdbc/DrupalAuth.java,\n\t  src/tigase/db/jdbc/JDBCRepository.java,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/xmpp/RepositoryAccess.java,\n\t  src/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/tigase/xmpp/impl/SaslAuth.java: Implemented fix for long\n\t  lasting and idle jdbc connection disconnection from server side\n\n2006-11-18 16:57  kobit\n\n\t* src/tigase/xmpp/impl/SaslAuth.java: Ticket #35:\n\t  http://server.tigase.org/ticket/35, removed printing tacktrace\n\t  into standatd output\n\n2006-11-16 12:50  kobit\n\n\t* MANIFEST.MF, src/tigase/conf/Configurator.java,\n\t  src/tigase/db/RepositoryFactory.java,\n\t  src/tigase/db/jdbc/DrupalAuth.java,\n\t  src/tigase/server/MessageRouter.java,\n\t  src/tigase/server/Packet.java,\n\t  src/tigase/util/RepositoryUtils.java: Fixed many small bugs found\n\t  during migration to Drupal\n\n2006-11-16 12:50  kobit\n\n\t* src/tigase/auth/SaslPLAIN.java: fixed the problem with the case\n\t  when client doesn't send authorization ID\n\n2006-11-14 22:32  kobit\n\n\t* MANIFEST.MF, scripts/tigase.sh: Statrup script added\n\n2006-11-13 23:02  kobit\n\n\t* MANIFEST.MF, src/tigase/util/RepositoryUtils.java: Added more\n\t  options and repository tests\n\n2006-11-13 23:02  kobit\n\n\t* src/tigase/db/xml/XMLRepository.java: JavaDoc fix\n\n2006-11-13 23:02  kobit\n\n\t* src/tigase/db/RepositoryFactory.java: Fixed problem with multiple\n\t  repository initialization for the same resource\n\n2006-11-13 21:01  kobit\n\n\t* src/tigase/db/xml/XMLRepository.java: Fixed the problem with\n\t  repository initialization on fresh installation where XML user\n\t  repo does not exist\n\n2006-11-12 19:25  kobit\n\n\t* MANIFEST.MF, src/tigase/util/RepositoryUtils.java: Added support\n\t  for UserAuthRepository\n\n2006-11-12 19:25  kobit\n\n\t* src/tigase/db/jdbc/DrupalAuth.java: addUser() implementation\n\t  added and checking for user status in drupal database\n\n2006-11-12 13:46  kobit\n\n\t* MANIFEST.MF, src/tigase/auth/SaslPLAIN.java,\n\t  src/tigase/db/UserAuthRepository.java,\n\t  src/tigase/db/UserAuthRepositoryImpl.java,\n\t  src/tigase/db/jdbc/DrupalAuth.java,\n\t  src/tigase/db/jdbc/JDBCRepository.java,\n\t  src/tigase/db/xml/XMLRepository.java,\n\t  src/tigase/io/SSLContextContainer.java,\n\t  src/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/tigase/xmpp/RepositoryAccess.java,\n\t  src/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/tigase/xmpp/impl/SaslAuth.java: Authorization framework is\n\t  finished and polished, drupal authentication is implemented and\n\t  workssvn add src/tigase/db/UserAuthRepositoryImpl.java\n\t  src/tigase/db/jdbc/DrupalAuth.java\n\n2006-11-11 20:28  kobit\n\n\t* MANIFEST.MF, certs/rsa-keystore, scripts/repo.sh,\n\t  src/tigase/auth/SaslPLAIN.java,\n\t  src/tigase/auth/TigaseSaslProvider.java,\n\t  src/tigase/auth/TigaseSaslServerFactory.java,\n\t  src/tigase/db/AuthorizationException.java,\n\t  src/tigase/db/UserAuthRepository.java,\n\t  src/tigase/db/jdbc/JDBCRepository.java,\n\t  src/tigase/db/xml/XMLRepository.java,\n\t  src/tigase/xmpp/RepositoryAccess.java,\n\t  src/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/tigase/xmpp/impl/SaslAuth.java: Authorization framework\n\t  rewriten again, it works but not well tested\n\n2006-11-06 23:58  kobit\n\n\t* MANIFEST.MF, src/tigase/auth/CommitHandler.java,\n\t  src/tigase/auth/DigestAuth.java,\n\t  src/tigase/auth/LoginHandler.java,\n\t  src/tigase/auth/PlainAuth.java,\n\t  src/tigase/auth/SaslCallbackHandler.java,\n\t  src/tigase/auth/SaslPLAIN.java,\n\t  src/tigase/auth/SessionCallback.java,\n\t  src/tigase/auth/TigaseConfiguration.java,\n\t  src/tigase/auth/TigaseSasl.java,\n\t  src/tigase/auth/TigaseSaslProvider.java,\n\t  src/tigase/auth/TigaseSaslServerFactory.java,\n\t  src/tigase/db/RepositoryFactory.java,\n\t  src/tigase/db/UserAuthRepository.java,\n\t  src/tigase/db/xml/XMLRepository.java,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/server/xmppsession/SessionManagerConfig.java,\n\t  src/tigase/util/RepositoryUtils.java,\n\t  src/tigase/xmpp/RepositoryAccess.java,\n\t  src/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/tigase/xmpp/impl/SaslAuth.java: Authorization framework\n\t  rewritten and PLAIN SASL implemented\n\n2006-11-06 22:18  kobit\n\n\t* src/tigase/auth/TigaseSasl.java: Small experiments with auth\n\t  framework\n\n2006-11-02 19:54  kobit\n\n\t* MANIFEST.MF,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/tigase/xmpp/XMPPIOService.java: Fixed the problem with 'to'\n\t  attribute in stream open element, ticket:\n\t  http://server.tigase.org/ticket/4\n\n2006-10-30 22:38  kobit\n\n\t* MANIFEST.MF, src/tigase/util/RepositoryUtils.java: Modified to\n\t  allow print repository content only for one given user account\n\n2006-10-30 21:27  kobit\n\n\t* scripts, scripts/config.sh, scripts/repo.sh: Command line tools\n\t  for repository and configuration maniulation\n\n2006-10-30 21:21  kobit\n\n\t* MANIFEST.MF, database/mysql-schema.sql,\n\t  database/simple-sqldb.sql,\n\t  src/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/tigase/xmpp/impl/Presence.java,\n\t  src/tigase/xmpp/impl/Roster.java: Filename change to reflect\n\t  database\n\n2006-10-30 15:13  kobit\n\n\t* MANIFEST.MF, src/tigase/conf/Configurator.java: Full off-line\n\t  configuration management implemented\n\n2006-10-29 10:57  kobit\n\n\t* src/tigase/db/NonAuthUserRepository.java,\n\t  src/tigase/server/ConnectionManager.java,\n\t  src/tigase/xmpp/RepositoryAccess.java,\n\t  src/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/tigase/xmpp/impl/OfflineMessages.java,\n\t  src/tigase/xmpp/impl/VCardTemp.java: Javadoc warning fixed -\n\t  javadoc is still not complete yet.\n\n2006-10-29 00:16  kobit\n\n\t* MANIFEST.MF, src/tigase/xmpp/impl/Presence.java: Fixed a bug with\n\t  extra probe sent for each presence: ticket -\n\t  http://server.tigase.org/ticket/31\n\n2006-10-29 00:08  kobit\n\n\t* MANIFEST.MF, src/tigase/conf/ConfigRepository.java,\n\t  src/tigase/conf/Configurator.java,\n\t  src/tigase/db/jdbc/JDBCRepository.java,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/server/xmppsession/SessionManagerConfig.java,\n\t  src/tigase/util/RepositoryUtils.java: Simple configuration and\n\t  repository management\n\n2006-10-28 16:15  kobit\n\n\t* src/tigase/util/RepositoryUtils.java: JDBC module is ready and\n\t  tested\n\n2006-10-28 15:26  kobit\n\n\t* MANIFEST.MF, src/tigase/db/jdbc/JDBCRepository.java,\n\t  src/tigase/util/RepositoryUtils.java: JDBC module is ready and\n\t  tested\n\n2006-10-28 09:32  kobit\n\n\t* MANIFEST.MF, src/tigase/db/jdbc/JDBCRepository.java: Jdbc\n\t  repository implementation is finished now but untested\n\n2006-10-27 16:10  kobit\n\n\t* MANIFEST.MF, database/simple-sqldb.sql,\n\t  src/tigase/auth/PlainAuth.java,\n\t  src/tigase/auth/SaslCallbackHandler.java,\n\t  src/tigase/db/DBInitException.java,\n\t  src/tigase/db/DataOverwriteException.java,\n\t  src/tigase/db/RepositoryFactory.java,\n\t  src/tigase/db/TigaseDBException.java,\n\t  src/tigase/db/UserExistsException.java,\n\t  src/tigase/db/UserNotFoundException.java,\n\t  src/tigase/db/UserRepository.java, src/tigase/db/jdbc,\n\t  src/tigase/db/jdbc/JDBCRepository.java,\n\t  src/tigase/db/xml/XMLRepository.java,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/xmpp/RepositoryAccess.java: JDBC database backend\n\t  implementation\n\n2006-10-26 08:44  kobit\n\n\t* MANIFEST.MF, database/simple-sqldb.sql,\n\t  src/tigase/db/UserRepository.java,\n\t  src/tigase/db/xml/XMLRepository.java,\n\t  src/tigase/xmpp/RepositoryAccess.java,\n\t  src/tigase/xmpp/XMPPResourceConnection.java: Added getUsers()\n\t  method - should be used only for conversion process from one\n\t  database to another....\n\n2006-10-26 08:05  kobit\n\n\t* database, database/simple-sqldb.sql: Database schema for SQL\n\t  databases\n\n2006-10-24 22:31  kobit\n\n\t* MANIFEST.MF, src/tigase/db/RepositoryFactory.java,\n\t  src/tigase/db/UserRepository.java,\n\t  src/tigase/db/xml/XMLRepository.java,\n\t  src/tigase/server/ConnectionManager.java,\n\t  src/tigase/server/xmppsession/SessionManager.java: Separation\n\t  user repository from auth repository (ticket:\n\t  http://server.tigase.org/ticket/9 )and cleaned up repository\n\t  initialization api\n\n2006-10-24 22:30  kobit\n\n\t* src/tigase/server/xmppsession/SessionManagerConfig.java:\n\t  Separated config constants from main class to make code cleaner\n\n2006-10-24 14:48  kobit\n\n\t* src/tigase/xmpp/RepositoryAccess.java: Implementation of offline\n\t  and public data finished\n\n2006-10-24 14:35  kobit\n\n\t* MANIFEST.MF, src/tigase/xmpp/impl/VCardTemp.java: Added support\n\t  for retrieving vCard info for off-line users.\n\n2006-10-24 14:27  kobit\n\n\t* MANIFEST.MF, src/tigase/db/DataOverwriteException.java,\n\t  src/tigase/db/NonAuthUserRepository.java,\n\t  src/tigase/db/UserRepository.java,\n\t  src/tigase/db/WriteOnlyUserRepository.java,\n\t  src/tigase/db/xml/XMLRepository.java,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/xmpp/XMPPPostprocessorIfc.java,\n\t  src/tigase/xmpp/XMPPPreprocessorIfc.java,\n\t  src/tigase/xmpp/XMPPProcessorIfc.java,\n\t  src/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/tigase/xmpp/XMPPStopListenerIfc.java,\n\t  src/tigase/xmpp/impl/BindResource.java,\n\t  src/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/tigase/xmpp/impl/JabberIqPrivacy.java,\n\t  src/tigase/xmpp/impl/JabberIqRegister.java,\n\t  src/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/tigase/xmpp/impl/JabberIqStats.java,\n\t  src/tigase/xmpp/impl/JabberIqVersion.java,\n\t  src/tigase/xmpp/impl/Message.java,\n\t  src/tigase/xmpp/impl/OfflineMessages.java,\n\t  src/tigase/xmpp/impl/Presence.java,\n\t  src/tigase/xmpp/impl/SaslAuth.java,\n\t  src/tigase/xmpp/impl/ServiceDiscovery.java,\n\t  src/tigase/xmpp/impl/SessionBind.java,\n\t  src/tigase/xmpp/impl/StartTLS.java,\n\t  src/tigase/xmpp/impl/VCardTemp.java: Implementation of offline\n\t  and public data finished\n\n2006-10-20 08:48  kobit\n\n\t* MANIFEST.MF, src/tigase/server/xmppsession/SessionManager.java:\n\t  Serious bug fixed with infinite error message loop\n\n2006-10-20 01:13  kobit\n\n\t* src/tigase/xmpp/impl/JabberIqVersion.java: Commented out double\n\t  attribute set\n\n2006-10-20 01:12  kobit\n\n\t* src/tigase/server/Packet.java: Added from and to attributes in\n\t  'result' packet\n\n2006-10-20 01:12  kobit\n\n\t* src/tigase/server/xmppsession/SessionManager.java: Fixed nasty\n\t  bug with infinite loop sending error packets\n\n2006-10-20 01:11  kobit\n\n\t* src/tigase/xmpp/impl/VCardTemp.java: Added vCard implementation\n\n2006-10-19 22:11  kobit\n\n\t* MANIFEST.MF, src/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/tigase/xmpp/impl/JabberIqRoster.java: Fixed problem with\n\t  removing user from the last group.\n\n2006-10-19 22:10  kobit\n\n\t* src/tigase/xmpp/impl/JabberIqVersion.java: Corrected version\n\t  retrieving between user clients.\n\n2006-10-18 21:14  kobit\n\n\t* MANIFEST.MF, src/tigase/server/Packet.java,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/xmpp/impl/BindResource.java,\n\t  src/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/tigase/xmpp/impl/JabberIqPrivacy.java,\n\t  src/tigase/xmpp/impl/JabberIqRegister.java,\n\t  src/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/tigase/xmpp/impl/JabberIqStats.java,\n\t  src/tigase/xmpp/impl/JabberIqVersion.java,\n\t  src/tigase/xmpp/impl/Message.java,\n\t  src/tigase/xmpp/impl/OfflineMessages.java,\n\t  src/tigase/xmpp/impl/Presence.java,\n\t  src/tigase/xmpp/impl/SaslAuth.java,\n\t  src/tigase/xmpp/impl/ServiceDiscovery.java,\n\t  src/tigase/xmpp/impl/SessionBind.java,\n\t  src/tigase/xmpp/impl/StartTLS.java: Privacy lists full\n\t  implementation finished\n\n2006-10-16 15:58  kobit\n\n\t* MANIFEST.MF, src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/xmpp/XMPPStopListenerIfc.java,\n\t  src/tigase/xmpp/impl/OfflineMessages.java,\n\t  src/tigase/xmpp/impl/Presence.java: Stop listener implementation\n\t  corrected\n\n2006-10-16 15:13  kobit\n\n\t* src/tigase/db/WriteOnlyUserRepository.java: This is a workaround\n\t  to allow plugins to write to repository even if there is no\n\t  authorized session. It is needed for off-line message storage\n\t  plugin\n\n2006-10-16 15:12  kobit\n\n\t* src/tigase/xmpp/OfflineMessageStorage.java: Reimplemented as\n\t  plugin not needed anymore\n\n2006-10-16 15:11  kobit\n\n\t* MANIFEST.MF, certs/rsa-keystore,\n\t  src/tigase/db/UserRepository.java, src/tigase/server/Packet.java,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/xmpp/ProcessorFactory.java,\n\t  src/tigase/xmpp/XMPPImplIfc.java,\n\t  src/tigase/xmpp/XMPPPostprocessorIfc.java,\n\t  src/tigase/xmpp/XMPPPreprocessorIfc.java,\n\t  src/tigase/xmpp/XMPPProcessor.java,\n\t  src/tigase/xmpp/XMPPProcessorIfc.java,\n\t  src/tigase/xmpp/XMPPStopListenerIfc.java,\n\t  src/tigase/xmpp/impl/BindResource.java,\n\t  src/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/tigase/xmpp/impl/JabberIqPrivacy.java,\n\t  src/tigase/xmpp/impl/JabberIqRegister.java,\n\t  src/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/tigase/xmpp/impl/JabberIqStats.java,\n\t  src/tigase/xmpp/impl/JabberIqVersion.java,\n\t  src/tigase/xmpp/impl/Message.java,\n\t  src/tigase/xmpp/impl/OfflineMessages.java,\n\t  src/tigase/xmpp/impl/Presence.java,\n\t  src/tigase/xmpp/impl/SaslAuth.java,\n\t  src/tigase/xmpp/impl/ServiceDiscovery.java,\n\t  src/tigase/xmpp/impl/SessionBind.java,\n\t  src/tigase/xmpp/impl/StartTLS.java: Major refactoring to add\n\t  preprocessing/postprocessing, stopped handlers, offline messages\n\t  are now implemented as a plugin\n\n2006-10-10 21:45  kobit\n\n\t* MANIFEST.MF, src/tigase/xmpp/impl/JabberIqPrivacy.java,\n\t  src/tigase/xmpp/impl/Privacy.java: Initial Privacy lists\n\t  implementation. You can manage privacy lists now but server\n\t  doesn't use them yet\n\n2006-10-10 21:44  kobit\n\n\t* src/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/tigase/xmpp/impl/SessionBind.java: Because of overloading\n\t  okResult() method from Package class minor adjustemts in code\n\t  were necessary\n\n2006-10-10 21:43  kobit\n\n\t* src/tigase/xmpp/XMPPResourceConnection.java: Changed log levels\n\t  from SEVERE to WARNING and added new method: getDataKeys()\n\n2006-10-10 21:42  kobit\n\n\t* src/tigase/db/xml/XMLRepository.java: Added better support for\n\t  concurrent access to repository. Now each thread can initialize\n\t  and access repository indepedently even for the same resource\n\t  (data file)\n\n2006-10-10 21:41  kobit\n\n\t* src/tigase/db/UserRepository.java: Added but commented out\n\t  method: getInstance(resource) as there is still no consistent\n\t  idea how it should work\n\n2006-10-10 21:40  kobit\n\n\t* src/tigase/server/Packet.java: Added 2 more methods:\n\t  okResult(Element, int) and getElemChildren()\n\n2006-10-10 21:38  kobit\n\n\t* src/tigase/server/xmppsession/SessionManager.java: Added privacy\n\t  component loading in default configuration and changed\n\t  initialization of XMLRepository to allow accessing repository\n\t  from different threads\n\n2006-10-10 21:37  kobit\n\n\t* src/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Changes related to reconnecting connections\n\n2006-10-10 21:36  kobit\n\n\t* src/tigase/server/ConnectionManager.java: All connections are\n\t  initialized with delay at startup time now to allow configuration\n\t  fully load first, especially initialization of SSL can take a bit\n\t  longer. Now all 'connect' type connections are not reconnecting\n\t  by default, you have to set counter for them to allow\n\t  reconnecting\n\n2006-10-10 21:34  kobit\n\n\t* src/tigase/io/TLSWrapper.java: Corrected logger package ID\n\n2006-10-09 16:02  kobit\n\n\t* package.html: Basic server documentation\n\n2006-10-09 16:02  kobit\n\n\t* MANIFEST.MF, src/tigase/io/SSLContextContainer.java,\n\t  src/tigase/server/ConnectionManager.java,\n\t  src/tigase/server/MessageRouter.java: Changed\n\t  ClientConnectionManager component addressing\n\n2006-10-09 16:02  kobit\n\n\t* src/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  Changed ClientConnectionManager component addressing\n\n2006-10-05 17:18  kobit\n\n\t* MANIFEST.MF, src/tigase/server/ConnectionManager.java,\n\t  src/tigase/server/MessageRouterConfig.java,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/tigase/server/xmppsession/SessionManager.java: Component\n\t  addressing for ClientConnectionManager has been changed to make\n\t  it more consistent and make all hostnames setting mean the same\n\n2006-10-04 16:37  kobit\n\n\t* libs: Added directory required by build file\n\n2006-10-04 16:35  kobit\n\n\t* MANIFEST.MF, ant-definitions.xml, build.xml: Corrected build file\n\n2006-10-04 16:35  kobit\n\n\t* src/tigase/xmpp/impl/ServiceDiscovery.java: Fixed null pointer\n\t  exception\n\n2006-10-03 11:20  kobit\n\n\t* MANIFEST.MF, src/tigase/server/Packet.java,\n\t  src/tigase/xmpp/impl/Presence.java,\n\t  src/tigase/xmpp/impl/SaslAuth.java: Fixed broken presence\n\n2006-10-02 21:43  kobit\n\n\t* src/tigase/xmpp/StanzaType.java: Added new - non-standard stanza\n\t  type used by kopete\n\n2006-10-02 21:42  kobit\n\n\t* src/tigase/xmpp/impl/Presence.java: Fixed problem with updating\n\t  presence for multiple user connections\n\n2006-09-30 16:48  kobit\n\n\t* MANIFEST.MF, src/tigase/xmpp/impl/JabberIqVersion.java: Null\n\t  pointer exception fixed\n\n2006-09-30 16:37  kobit\n\n\t* MANIFEST.MF, src/tigase/server/AbstractMessageReceiver.java,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/xmpp/OfflineMessageStorage.java: Message to admin\n\t  implemented\n\n2006-09-28 22:45  kobit\n\n\t* MANIFEST.MF, ant-definitions.xml,\n\t  src/tigase/server/XMPPServer.java,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/xmpp/impl/ServiceDiscovery.java: Fixed service\n\t  discovery and statistics reports\n\n2006-09-28 22:45  kobit\n\n\t* src/tigase/net/IOService.java: Commented out printing stack trace\n\t  on text console\n\n2006-09-28 22:44  kobit\n\n\t* src/tigase/xmpp/impl/SaslAuth.java: Corrected SASL implementation\n\t  for RFC compliance\n\n2006-09-28 22:44  kobit\n\n\t* src/tigase/xmpp/impl/Presence.java: Fixed null pointer exception\n\t  when probe was called for buddy not in roster\n\n2006-09-26 06:24  kobit\n\n\t* MANIFEST.MF, src/tigase/io/SocketIO.java: Fixed incorrect logger\n\t  id\n\n2006-09-26 06:23  kobit\n\n\t* src/tigase/xmpp/OfflineMessageStorage.java: Added time stamp and\n\t  off-line message delivery element\n\n2006-09-26 06:23  kobit\n\n\t* src/tigase/xmpp/impl/Presence.java: Fixed missing 'from'\n\t  attribute when user send message to himself and implemented\n\t  off-line message delivery on receiving presence packet\n\n2006-09-26 06:22  kobit\n\n\t* src/tigase/xmpp/impl/Message.java: Fixed missing 'from' attribute\n\t  when user send message to himself\n\n2006-09-25 18:54  kobit\n\n\t* MANIFEST.MF, src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/xmpp/impl/Presence.java,\n\t  src/tigase/xmpp/impl/SaslAuth.java: Off-line messages for XMPP\n\t  1.0 fixed. Now all off-line messages are delivered after\n\t  receiving initial presence message.\n\n2006-09-24 23:04  kobit\n\n\t* MANIFEST.MF, build.properties, build.xml,\n\t  src/tigase/io/BufferUnderflowException.java,\n\t  src/tigase/io/IOInterface.java, src/tigase/io/SocketIO.java,\n\t  src/tigase/io/TLSIO.java, src/tigase/io/TLSWrapper.java,\n\t  src/tigase/net/IOService.java: Fixed problem with disconnecting\n\t  SSL connection on receiving large packets of data. See ticket: #6\n\t  - http://server.tigase.org/ticket/6\n\n2006-09-24 23:02  kobit\n\n\t* src/tigase/xmpp/XMPPDomBuilderHandler.java: Added error message\n\t  for error() method, now it easier to find out what caused the\n\t  error.\n\n2006-09-19 14:00  kobit\n\n\t* MANIFEST.MF, src/tigase/server/xmppsession/SessionManager.java:\n\t  Fixed problem with Processors not loaded for specified ID, it\n\t  used to trow NullPointerException\n\n2006-09-19 09:11  kobit\n\n\t* MANIFEST.MF,\n\t  src/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Another correction for the code disconnecting second channel if\n\t  one of dialback connection has been closed\n\n2006-09-18 23:12  kobit\n\n\t* MANIFEST.MF, ant-definitions.xml, build.xml: Corrected build\n\t  process for generating distribution files\n\n2006-09-18 23:12  kobit\n\n\t* src/tigase/conf/Configurator.java: Changed tigase log name from\n\t  java.log to tigase.log\n\n2006-09-18 23:10  kobit\n\n\t* src/tigase/server/xmppserver/ServerConnectionManager.java: Some\n\t  servers disconnect one connection from s2s pair and don't accept\n\t  any data on the other connection, as a result communication is\n\t  broken. This fix closes other connection if one of them has been\n\t  closed by remote server\n\n2006-09-18 13:09  kobit\n\n\t* src/tigase/server/ConnectionManager.java: Changed default\n\t  configuration to use rsa-keystore\n\n2006-09-18 13:00  kobit\n\n\t* certs/rsa-keystore: Keystore with RSA, default contain DSA wich\n\t  is not liked by gaim\n\n2006-09-18 12:33  kobit\n\n\t* build.properties: version numer change after bug fixes\n\n2006-09-18 12:10  kobit\n\n\t* MANIFEST.MF, ant-definitions.xml,\n\t  src/tigase/server/xmppsession/SessionManager.java: Fixed problem\n\t  with gajim connection - now server sends back error for all\n\t  unsupported stanze received from the client, previously gajim\n\t  seemed to hang waiting for response on unsupported IQ stanzas\n\n2006-09-18 12:09  kobit\n\n\t* src/tigase/server/ComponentRegistrator.java: Fixed broken javadoc\n\t  comment\n\n2006-09-18 12:09  kobit\n\n\t* src/tigase/conf/Configurator.java: Changed default configuration,\n\t  handler for file logger is now setup correctly\n\n2006-09-18 12:08  kobit\n\n\t* src/tigase/auth/SaslCallbackHandler.java: Fixed broken javadoc\n\t  comment\n\n2006-09-17 18:48  kobit\n\n\t* MANIFEST.MF: This is version 2.2.0\n\n2006-09-17 18:48  kobit\n\n\t* build.properties: Version number change\n\n2006-09-17 18:47  kobit\n\n\t* src/tigase/server/xmppserver/ServerConnectionManager.java: Fixed\n\t  problem with remote server diconnect and impossible to establish\n\t  connection with that server later\n\n2006-09-17 18:47  kobit\n\n\t* src/tigase/xmpp/OfflineMessageStorage.java: Small correction to\n\t  preven storring for off-line delivery stanzas other than\n\t  <message/>\n\n2006-09-16 21:57  kobit\n\n\t* MANIFEST.MF, src/tigase/db/UserRepository.java,\n\t  src/tigase/db/xml/XMLRepository.java,\n\t  src/tigase/net/IOService.java, src/tigase/server/Command.java,\n\t  src/tigase/server/Packet.java,\n\t  src/tigase/server/XMPPServiceCollector.java,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/util/ElementUtils.java,\n\t  src/tigase/xmpp/OfflineMessageStorage.java,\n\t  src/tigase/xmpp/XMPPIOService.java,\n\t  src/tigase/xmpp/XMPPProcessor.java,\n\t  src/tigase/xmpp/XMPPProcessorIfc.java,\n\t  src/tigase/xmpp/impl/BindResource.java,\n\t  src/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/tigase/xmpp/impl/JabberIqRegister.java,\n\t  src/tigase/xmpp/impl/SaslAuth.java,\n\t  src/tigase/xmpp/impl/SessionBind.java,\n\t  src/tigase/xmpp/impl/StartTLS.java: Offline messages storage\n\t  added and fixed various bugs\n\n2006-09-14 12:17  kobit\n\n\t* MANIFEST.MF, src/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/tigase/xmpp/XMPPSession.java,\n\t  src/tigase/xmpp/impl/JabberIqAuth.java: Fixed problem with\n\t  setting resources, later after authorization\n\n2006-09-14 07:21  kobit\n\n\t* MANIFEST.MF,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/tigase/xmpp/XMPPSession.java,\n\t  src/tigase/xmpp/impl/JabberIqAuth.java: Fixed problem with\n\t  reconnecting resource - the old session for the same resource was\n\t  not removed correctly\n\n2006-09-14 04:54  kobit\n\n\t* MANIFEST.MF,\n\t  src/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Session id and dialback key is kept under different entries in\n\t  shared session data\n\n2006-09-13 16:48  kobit\n\n\t* MANIFEST.MF, src/tigase/server/ConnectionManager.java,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/tigase/xmpp/XMPPProcessor.java,\n\t  src/tigase/xmpp/XMPPSession.java: Corrected, simplified and\n\t  improved but not tested s2s - completely rewrited actually,\n\t  should handle google case easy\n\n2006-09-13 16:48  kobit\n\n\t* src/tigase/auth/DialbackAuth.java: Not used anymore....\n\n2006-09-13 16:47  kobit\n\n\t* src/tigase/xmpp/impl/Dialback.java: Not used anymore....\n\n2006-09-13 16:47  kobit\n\n\t* src/tigase/xmpp/impl/Dialback.java: Not used anymore....\n\n2006-09-11 19:35  kobit\n\n\t* MANIFEST.MF, src/tigase/server/MessageRouterConfig.java,\n\t  src/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/tigase/server/xmppsession/SessionManager.java: Switched to\n\t  use SRV DNS record for xmpp servcice\n\n2006-09-11 19:35  kobit\n\n\t* src/tigase/net/IOService.java: Removed unused import statement\n\n2006-09-11 19:34  kobit\n\n\t* src/tigase/xmpp/impl/Presence.java: Fixed problem with sending\n\t  presence notification just after subscription has been approved\n\n2006-09-11 19:33  kobit\n\n\t* src/tigase/xmpp/impl/Roster.java: Added more logging code\n\n2006-09-11 15:11  kobit\n\n\t* src/tigase/net/DNSResolver.java: Moved to tigase.util project\n\n2006-09-11 15:11  kobit\n\n\t* src/tigase/net/DNSResolver.java: Changed function parameter to\n\t  'final'\n\n2006-09-11 15:04  kobit\n\n\t* src/tigase/net/AllAddresses.java,\n\t  src/tigase/net/DNSResolver.java: Renamed to more appropropriate\n\t  name\n\n2006-09-11 15:03  kobit\n\n\t* src/tigase/net/AllAddresses.java: Generic function implemented\n\t  for retrieving IP address for xmpp SRV DNS record\n\n2006-09-11 13:02  kobit\n\n\t* src/tigase/net/ConnectionOpenThread.java: Server used to die on\n\t  SocketException, that was wrong, fixed now....\n\n2006-09-11 13:01  kobit\n\n\t* src/tigase/server/xmppsession/SessionManager.java: Corrected\n\t  default configuration generated during first startup\n\n2006-09-11 13:01  kobit\n\n\t* src/tigase/net/AllAddresses.java: Sample class showing how to\n\t  retrieve DNS records like SRV\n\n2006-09-10 22:33  kobit\n\n\t* MANIFEST.MF, src/tigase/auth/DialbackAuth.java,\n\t  src/tigase/auth/SessionCallback.java,\n\t  src/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/tigase/xmpp/impl/Dialback.java, tests/data/tigase-config.xml:\n\t  s2s implementation finishedsvn status Finally.\n\n2006-09-10 22:31  kobit\n\n\t* src/tigase/server/ConnectionManager.java: Due to asynchronouse\n\t  nature I had to exchange 2 lines. Now serviceStarted(serv) is\n\t  called before readThread.addSocketService(serv)\n\n2006-09-10 22:30  kobit\n\n\t* src/tigase/server/MessageRouter.java: It may happen only due to a\n\t  bug in server code that packet.getTo() == null for packet\n\t  processing by this class. So just in case added checking at the\n\t  beginning of the processPacket method. Also replaced InetAddress\n\t  class call with new JID.getNodeHostIP method\n\n2006-09-10 22:28  kobit\n\n\t* src/tigase/server/Packet.java: Added getElemCData() method\n\n2006-09-10 22:27  kobit\n\n\t* src/tigase/xmpp/XMPPIOService.java: Added assert debug(data) code\n\n2006-09-10 22:27  kobit\n\n\t* src/tigase/xmpp/impl/Presence.java: According to RFC server\n\t  should not resend 'out_(un)subscribed' notice but it make it very\n\t  difficuly to synchronize presence subscrition in case of earlier\n\t  problems. So for now my implemention will resend such notice\n\n2006-09-10 22:24  kobit\n\n\t* src/tigase/xmpp/XMPPDomBuilderHandler.java: Support for\n\t  xmlns:elname added\n\n2006-09-08 19:33  kobit\n\n\t* MANIFEST.MF, src/tigase/auth/DialbackAuth.java,\n\t  src/tigase/server/ConnectionManager.java,\n\t  src/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/tigase/xmpp/impl/Dialback.java, tests/data/tigase-config.xml:\n\t  s2s implementation continued...., connecting to remote server\n\t  now....\n\n2006-09-08 19:32  kobit\n\n\t* src/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Changed code to make use of new method in IOService -\n\t  connectionType()\n\n2006-09-08 19:31  kobit\n\n\t* src/tigase/server/xmppsession/SessionManager.java: A few more\n\t  logging messages added\n\n2006-09-08 19:30  kobit\n\n\t* src/tigase/net/ConnectionOpenThread.java: A few more logging\n\t  messages added\n\n2006-09-08 19:30  kobit\n\n\t* src/tigase/xmpp/XMPPIOService.java: New method added -\n\t  xmppStreamOpen()\n\n2006-09-08 19:29  kobit\n\n\t* src/tigase/net/IOService.java: Fixed a bug in writeData method\n\t  for NULL data\n\n2006-09-04 19:37  kobit\n\n\t* src/tigase/server/xmppclient/ClientConnectionManager.java: Fixed\n\t  bug when constructing stream_opened command some data were added\n\t  as pure string rather than DOM XML structure.\n\n2006-08-31 06:04  kobit\n\n\t* MANIFEST.MF, src/tigase/auth/DialbackAuth.java,\n\t  src/tigase/auth/DigestAuth.java, src/tigase/auth/semantic.cache,\n\t  src/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/tigase/xmpp/XMPPSession.java,\n\t  src/tigase/xmpp/impl/Dialback.java, tests/data/tigase-config.xml:\n\t  s2s implementation in progress\n\n2006-08-31 06:03  kobit\n\n\t* src/tigase/auth/TigaseConfiguration.java,\n\t  src/tigase/server/xmppsession/SessionManager.java: Changed\n\t  TigaseConfiguration to statis class\n\n2006-08-31 06:01  kobit\n\n\t* src/tigase/io/TelnetClient.java, src/tigase/io/TelnetServer.java:\n\t  Added support for sending files to test large portion of data\n\n2006-08-22 14:47  kobit\n\n\t* src/tigase/server/xmppserver/ServerConnectionManager.java:\n\t  Constant name change from SESSION_ID to SESSION_ID_KEY and\n\t  getUniqueId method corrected\n\n2006-08-22 14:46  kobit\n\n\t* src/tigase/server/ConnectionManager.java: TODO comments addded\n\t  for serviceStarted method\n\n2006-08-22 14:45  kobit\n\n\t* src/tigase/server/xmppcomponent/ComponentConnectionManager.java:\n\t  Constant name change from serv.SESSION_ID to serv.SESSION_ID_KEY\n\n2006-08-22 14:45  kobit\n\n\t* src/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  Constant name change from serv.SESSION_ID to serv.SESSION_ID_KEY\n\n2006-08-22 14:44  kobit\n\n\t* src/tigase/net/IOService.java: Added ConnectionType and\n\t  remote/local address retrieving for connection service\n\n2006-08-22 14:43  kobit\n\n\t* src/tigase/conf/Configurator.java: Changed depreciated code\n\t  accessing Logger.global\n\n2006-08-22 14:43  kobit\n\n\t* src/tigase/xmpp/XMPPResourceConnection.java: Added java doc for\n\t  connectionId variable\n\n2006-08-18 09:57  kobit\n\n\t* MANIFEST.MF, prj.el, src/tigase/server/Packet.java,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/tigase/xmpp/StanzaType.java,\n\t  src/tigase/xmpp/impl/Dialback.java: s2s implementation and\n\t  Dialback implementation\n\n2006-08-07 19:44  kobit\n\n\t* MANIFEST.MF, src/tigase/io/TelnetClient.java: It uses trust-all\n\t  mode si it doesn't get disconnected when server sends unknown\n\t  certificate\n\n2006-08-07 19:42  kobit\n\n\t* MANIFEST.MF, src/tigase/io/SSLContextContainer.java,\n\t  src/tigase/io/TLSUtil.java, src/tigase/io/TLSWrapper.java: Added\n\t  support for trust-all SSLContext\n\n2006-08-07 19:02  kobit\n\n\t* MANIFEST.MF, src/tigase/io/TelnetClient.java,\n\t  src/tigase/io/TelnetServer.java: SSL connection is working now\n\t  and debug options turns debuging on\n\n2006-08-07 18:25  kobit\n\n\t* MANIFEST.MF, src/tigase/annotations/TODO.java,\n\t  src/tigase/auth/AuthorisationSystem.java,\n\t  src/tigase/auth/CommitHandler.java,\n\t  src/tigase/auth/DigestAuth.java, src/tigase/auth/PlainAuth.java,\n\t  src/tigase/auth/ResourceConnectionCallback.java,\n\t  src/tigase/auth/SaslCallbackHandler.java,\n\t  src/tigase/auth/TigaseConfiguration.java,\n\t  src/tigase/auth/TigaseSasl.java,\n\t  src/tigase/conf/ConfigComponent.java,\n\t  src/tigase/conf/ConfigRepository.java,\n\t  src/tigase/conf/Configurable.java,\n\t  src/tigase/conf/Configurator.java,\n\t  src/tigase/db/ConfigurationDB.java,\n\t  src/tigase/db/MessageHistoryDB.java,\n\t  src/tigase/db/MessageOfflineDB.java, src/tigase/db/UserDB.java,\n\t  src/tigase/db/UserExistsException.java,\n\t  src/tigase/db/UserNotFoundException.java,\n\t  src/tigase/db/UserRepository.java,\n\t  src/tigase/db/xml/XMLRepository.java,\n\t  src/tigase/http/HttpServer.java,\n\t  src/tigase/http/HttpService.java, src/tigase/io/IOInterface.java,\n\t  src/tigase/io/SSLContextContainer.java,\n\t  src/tigase/io/SocketIO.java, src/tigase/io/TLSIO.java,\n\t  src/tigase/io/TLSStatus.java, src/tigase/io/TLSUtil.java,\n\t  src/tigase/io/TLSWrapper.java, src/tigase/net/Accept.java,\n\t  src/tigase/net/ConnectionOpenListener.java,\n\t  src/tigase/net/ConnectionOpenThread.java,\n\t  src/tigase/net/ConnectionType.java,\n\t  src/tigase/net/IOService.java,\n\t  src/tigase/net/IOServiceListener.java,\n\t  src/tigase/net/ServiceCommand.java,\n\t  src/tigase/net/SocketReadThread.java,\n\t  src/tigase/net/SocketType.java,\n\t  src/tigase/server/AbstractComponentRegistrator.java,\n\t  src/tigase/server/AbstractMessageReceiver.java,\n\t  src/tigase/server/Command.java,\n\t  src/tigase/server/ComponentRegistrator.java,\n\t  src/tigase/server/ConnectionManager.java,\n\t  src/tigase/server/MessageReceiver.java,\n\t  src/tigase/server/MessageRouter.java,\n\t  src/tigase/server/MessageRouterConfig.java,\n\t  src/tigase/server/Packet.java,\n\t  src/tigase/server/ServerComponent.java,\n\t  src/tigase/server/ThreadExceptionHandler.java,\n\t  src/tigase/server/XMPPServer.java,\n\t  src/tigase/server/XMPPService.java,\n\t  src/tigase/server/XMPPServiceCollector.java,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/stats/StatRecord.java,\n\t  src/tigase/stats/StatisticType.java,\n\t  src/tigase/stats/StatisticsCollector.java,\n\t  src/tigase/stats/StatisticsContainer.java,\n\t  src/tigase/stats/StatsComponent.java,\n\t  src/tigase/ui/UIComponent.java, src/tigase/ui/WebUI.java,\n\t  src/tigase/ui/XMPPServiceComponent.java,\n\t  src/tigase/util/ElementUtils.java,\n\t  src/tigase/util/LogFormatter.java,\n\t  src/tigase/util/RoutingsContainer.java,\n\t  src/tigase/xmpp/Authorization.java,\n\t  src/tigase/xmpp/NotAuthorizedException.java,\n\t  src/tigase/xmpp/ProcessorFactory.java,\n\t  src/tigase/xmpp/StanzaType.java,\n\t  src/tigase/xmpp/XMPPDomBuilderHandler.java,\n\t  src/tigase/xmpp/XMPPException.java,\n\t  src/tigase/xmpp/XMPPIOService.java,\n\t  src/tigase/xmpp/XMPPIOServiceListener.java,\n\t  src/tigase/xmpp/XMPPProcessor.java,\n\t  src/tigase/xmpp/XMPPProcessorIfc.java,\n\t  src/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/tigase/xmpp/XMPPSession.java,\n\t  src/tigase/xmpp/impl/BindResource.java,\n\t  src/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/tigase/xmpp/impl/JabberIqRegister.java,\n\t  src/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/tigase/xmpp/impl/JabberIqStats.java,\n\t  src/tigase/xmpp/impl/JabberIqVersion.java,\n\t  src/tigase/xmpp/impl/Message.java,\n\t  src/tigase/xmpp/impl/Presence.java,\n\t  src/tigase/xmpp/impl/Roster.java,\n\t  src/tigase/xmpp/impl/SaslAuth.java,\n\t  src/tigase/xmpp/impl/ServiceDiscovery.java,\n\t  src/tigase/xmpp/impl/SessionBind.java,\n\t  src/tigase/xmpp/impl/StartTLS.java: e-mail address changed to\n\t  correct one in tigase.org domain\n\n2006-08-07 18:24  kobit\n\n\t* src/tigase/io/TelnetClient.java, src/tigase/io/TelnetServer.java:\n\t  Added SSL support, not working yet though\n\n2006-08-07 14:48  kobit\n\n\t* src/tigase/io/SampleSocketThread.java: Corrected error message,\n\t  when socket gets disconnected\n\n2006-08-07 14:40  kobit\n\n\t* MANIFEST.MF, src/tigase/io/SampleSocketThread.java,\n\t  src/tigase/io/TelnetClient.java, src/tigase/io/TelnetServer.java:\n\t  Sample classes for plain connection using tigase.io API\n\n2006-08-07 14:40  kobit\n\n\t* src/tigase/io/SocketIO.java: SocketIO now initializes\n\t  SocketChannel properly on its own, ie. set non-blocking mode and\n\t  so on\n\n2006-08-07 14:19  kobit\n\n\t* MANIFEST.MF, src/tigase/io/TelnetServer.java: TelnetServer works\n\t  now for plain socket connection, test it with telnet linux\n\t  program\n\n2006-08-07 10:35  kobit\n\n\t* MANIFEST.MF, src/tigase/io/SampleSocketThread.java,\n\t  src/tigase/io/SimpleReaderThread.java,\n\t  src/tigase/io/TelnetServer.java: Smaple classes implementation in\n\t  progress\n\n2006-08-06 22:59  kobit\n\n\t* src/tigase/io/SimpleReaderThread.java,\n\t  src/tigase/io/TelnetClient.java, src/tigase/io/TelnetServer.java:\n\t  Sample classes presenting tigase.io use\n\n2006-08-06 22:57  kobit\n\n\t* MANIFEST.MF, prj.el, src/tigase/io/TLSIO.java,\n\t  src/tigase/io/TLSWrapper.java, src/tigase/net/IOService.java,\n\t  src/tigase/server/ConnectionManager.java,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java: Added\n\t  support for client connection\n\n2006-04-21 14:34  kobit\n\n\t* src/tigase/xmpp/impl/JabberIqAuth.java: Unblocked digest\n\t  authorization\n\n2006-04-21 13:54  kobit\n\n\t* MANIFEST.MF, src/tigase/xmpp/XMPPIOService.java: Fixes to\n\t  multithreaded tests\n\n2006-04-21 07:20  kobit\n\n\t* MANIFEST.MF, src/tigase/server/xmppsession/SessionManager.java,\n\t  tests/data/tigase-config.xml: Fixed a few minor configuration\n\t  bugs\n\n2006-04-21 07:15  kobit\n\n\t* MANIFEST.MF, src/tigase/auth/AuthorisationSystem.java,\n\t  src/tigase/db/ConfigurationDB.java,\n\t  src/tigase/db/MessageHistoryDB.java,\n\t  src/tigase/db/MessageOfflineDB.java, src/tigase/db/UserDB.java,\n\t  src/tigase/http/HttpServer.java, src/tigase/server/Command.java,\n\t  src/tigase/server/XMPPService.java,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/ui/UIComponent.java,\n\t  src/tigase/util/RoutingsContainer.java,\n\t  src/tigase/xmpp/XMPPIOService.java,\n\t  src/tigase/xmpp/XMPPSession.java,\n\t  src/tigase/xmpp/impl/StartTLS.java,\n\t  tests/data/tigase-config-1.xml, tests/data/tigase-config.xml:\n\t  Corrected thread synchronization issues\n\n2006-04-20 13:41  kobit\n\n\t* MANIFEST.MF, src/tigase/auth/CommitHandler.java,\n\t  src/tigase/auth/PlainAuth.java,\n\t  src/tigase/auth/SaslCallbackHandler.java,\n\t  src/tigase/auth/semantic.cache,\n\t  src/tigase/net/SocketReadThread.java,\n\t  src/tigase/server/AbstractMessageReceiver.java,\n\t  src/tigase/server/ConnectionManager.java,\n\t  src/tigase/server/MessageRouter.java,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/stats/StatRecord.java,\n\t  src/tigase/stats/StatisticType.java,\n\t  src/tigase/xmpp/XMPPIOService.java,\n\t  src/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/tigase/xmpp/XMPPSession.java,\n\t  src/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/tigase/xmpp/impl/Message.java,\n\t  src/tigase/xmpp/impl/Presence.java,\n\t  tests/data/tigase-config-2.xml: Fixed a few concurency issues\n\n2006-04-10 20:23  kobit\n\n\t* MANIFEST.MF, src/tigase/net/ConnectionOpenThread.java,\n\t  src/tigase/net/SocketReadThread.java,\n\t  tests/data/tigase-config-1.xml, tests/data/tigase-config-2.xml:\n\t  New XML-tools version support corrected, TestRoster now expects\n\t  XML data with \" instead of '\n\n2006-04-10 17:46  kobit\n\n\t* MANIFEST.MF: Compiler warnings fixed\n\n2006-04-10 16:47  kobit\n\n\t* src/tigase/util/ClassComparator.java: Separation util code from\n\t  the rest of components\n\n2006-04-10 16:46  kobit\n\n\t* src/tigase/util/ObjectComparator.java: Separation util code from\n\t  the rest of components\n\n2006-04-10 16:44  kobit\n\n\t* MANIFEST.MF, src/tigase/util/ClassUtil.java: Separation util code\n\t  from the rest of components\n\n2006-04-10 16:38  kobit\n\n\t* src/tigase/util/Algorithms.java, src/tigase/util/JID.java,\n\t  src/tigase/util/Telnet.java: Separation util code from the rest\n\t  of components\n\n2006-04-06 07:18  kobit\n\n\t* MANIFEST.MF, build.properties, src/tigase/io/TLSIO.java,\n\t  src/tigase/net/IOService.java,\n\t  src/tigase/net/SocketReadThread.java,\n\t  src/tigase/server/AbstractMessageReceiver.java,\n\t  src/tigase/server/ConnectionManager.java,\n\t  src/tigase/server/MessageRouter.java,\n\t  src/tigase/server/Packet.java, src/tigase/server/XMPPServer.java,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/tigase/xmpp/XMPPIOService.java,\n\t  src/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/tigase/xmpp/impl/JabberIqStats.java,\n\t  src/tigase/xmpp/impl/JabberIqVersion.java,\n\t  src/tigase/xmpp/impl/Presence.java,\n\t  src/tigase/xmpp/impl/StartTLS.java,\n\t  tests/data/tigase-config-1.xml, tests/data/tigase-config-2.xml:\n\t  Fixes for bugs: TLS start, multithreaded I/0, uncautch exception\n\t  in main threads\n\n2006-03-29 06:32  kobit\n\n\t* MANIFEST.MF, src/tigase/conf/Configurator.java,\n\t  src/tigase/server/AbstractComponentRegistrator.java,\n\t  src/tigase/server/AbstractMessageReceiver.java,\n\t  src/tigase/server/Command.java,\n\t  src/tigase/server/MessageRouter.java,\n\t  src/tigase/server/Packet.java,\n\t  src/tigase/server/ServerComponent.java,\n\t  src/tigase/server/XMPPService.java,\n\t  src/tigase/server/XMPPServiceCollector.java,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/stats/StatRecord.java,\n\t  src/tigase/stats/StatisticsCollector.java,\n\t  src/tigase/util/ElementUtils.java,\n\t  src/tigase/xmpp/impl/BindResource.java,\n\t  src/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/tigase/xmpp/impl/JabberIqRegister.java,\n\t  src/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/tigase/xmpp/impl/JabberIqStats.java,\n\t  src/tigase/xmpp/impl/JabberIqVersion.java,\n\t  src/tigase/xmpp/impl/ServiceDiscovery.java,\n\t  src/tigase/xmpp/impl/StartTLS.java,\n\t  tests/data/tigase-config-2.xml: All basic implementations\n\t  comleted\n\n2006-03-21 07:18  kobit\n\n\t* MANIFEST.MF, src/tigase/server/AbstractMessageReceiver.java,\n\t  src/tigase/server/Command.java, src/tigase/server/Packet.java,\n\t  src/tigase/server/XMPPServer.java,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/xmpp/IqType.java, src/tigase/xmpp/StanzaType.java,\n\t  src/tigase/xmpp/XMPPIOService.java,\n\t  src/tigase/xmpp/XMPPProcessor.java,\n\t  src/tigase/xmpp/XMPPProcessorIfc.java,\n\t  src/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/tigase/xmpp/XMPPSession.java,\n\t  src/tigase/xmpp/impl/BindResource.java,\n\t  src/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/tigase/xmpp/impl/JabberIqRegister.java,\n\t  src/tigase/xmpp/impl/JabberIqRoster.java,\n\t  src/tigase/xmpp/impl/JabberIqVersion.java,\n\t  src/tigase/xmpp/impl/Message.java,\n\t  src/tigase/xmpp/impl/Presence.java,\n\t  src/tigase/xmpp/impl/Roster.java,\n\t  src/tigase/xmpp/impl/SaslAuth.java,\n\t  src/tigase/xmpp/impl/SessionBind.java,\n\t  tests/data/tigase-config-1.xml, tests/data/tigase-config-2.xml:\n\t  Implemented presence, roster, message, version\n\n2006-02-20 22:59  kobit\n\n\t* MANIFEST.MF, src/tigase/auth/DigestAuth.java,\n\t  src/tigase/auth/PlainAuth.java, src/tigase/auth/semantic.cache,\n\t  src/tigase/server/Packet.java,\n\t  src/tigase/xmpp/Authorization.java, src/tigase/xmpp/IqType.java,\n\t  src/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/tigase/xmpp/impl/BindResource.java,\n\t  src/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/tigase/xmpp/impl/JabberIqRegister.java,\n\t  src/tigase/xmpp/impl/SaslAuth.java,\n\t  src/tigase/xmpp/impl/SessionBind.java,\n\t  tests/data/tigase-config-2.xml: Added session bind and bind\n\t  resource implementation\n\n2006-02-20 21:05  kobit\n\n\t* src/tigase/xmpp/impl/SaslAuth.java,\n\t  src/tigase/xmpp/impl/XmppSasl.java: SASL authorization\n\t  implememted\n\n2006-02-20 21:03  kobit\n\n\t* src/tigase/xmpp/impl/XMPPSasl.java,\n\t  src/tigase/xmpp/impl/XmppSasl.java: SASL authorization\n\t  implememted\n\n2006-02-20 21:01  kobit\n\n\t* src/tigase/xmpp/impl/SaslAuth.java,\n\t  src/tigase/xmpp/impl/XMPPSasl.java: SASL authorization\n\t  implememted\n\n2006-02-20 21:01  kobit\n\n\t* MANIFEST.MF, src/tigase/auth/SaslCallbackHandler.java,\n\t  src/tigase/auth/semantic.cache,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/xmpp/IqType.java,\n\t  src/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/tigase/xmpp/impl/SaslAuth.java,\n\t  tests/data/tigase-config-2.xml: SASL authorization implememted\n\n2006-02-20 16:42  kobit\n\n\t* tests/data/telnet-test.xml, tests/data/tigase-config-1.xml,\n\t  tests/data/tigase-config-2.xml, tests/data/tigase-config.xml:\n\t  Framework for authorization and some authorization implementation\n\t  is ready\n\n2006-02-20 16:41  kobit\n\n\t* src/tigase/xmpp/Authorization.java, src/tigase/xmpp/IqType.java,\n\t  src/tigase/xmpp/NotAuthorizedException.java,\n\t  src/tigase/xmpp/ProcessorFactory.java,\n\t  src/tigase/xmpp/XMPPException.java,\n\t  src/tigase/xmpp/XMPPIOServiceListener.java,\n\t  src/tigase/xmpp/XMPPProcessor.java,\n\t  src/tigase/xmpp/XMPPProcessorIfc.java,\n\t  src/tigase/xmpp/XMPPResourceConnection.java,\n\t  src/tigase/xmpp/XMPPSession.java: Framework for authorization and\n\t  some authorization implementation is ready\n\n2006-02-20 16:41  kobit\n\n\t* src/tigase/auth/CommitHandler.java,\n\t  src/tigase/auth/DigestAuth.java, src/tigase/auth/PlainAuth.java,\n\t  src/tigase/auth/ResourceConnectionCallback.java,\n\t  src/tigase/auth/SaslCallbackHandler.java,\n\t  src/tigase/auth/TigaseConfiguration.java,\n\t  src/tigase/auth/TigaseSasl.java, src/tigase/auth/semantic.cache,\n\t  src/tigase/db/UserExistsException.java,\n\t  src/tigase/db/UserNotFoundException.java,\n\t  src/tigase/db/UserRepository.java, src/tigase/db/xml,\n\t  src/tigase/db/xml/XMLRepository.java,\n\t  src/tigase/server/Command.java, src/tigase/util/Algorithms.java,\n\t  src/tigase/util/ClassComparator.java,\n\t  src/tigase/util/ClassUtil.java,\n\t  src/tigase/util/ObjectComparator.java,\n\t  src/tigase/util/RoutingsContainer.java, src/tigase/xmpp/impl,\n\t  src/tigase/xmpp/impl/JabberIqAuth.java,\n\t  src/tigase/xmpp/impl/JabberIqRegister.java,\n\t  src/tigase/xmpp/impl/SaslAuth.java: Framework for authorization\n\t  and some authorization implementation is ready\n\n2006-02-20 16:39  kobit\n\n\t* MANIFEST.MF, src/tigase/net/IOService.java,\n\t  src/tigase/net/Reply.java,\n\t  src/tigase/server/ConnectionManager.java,\n\t  src/tigase/server/Packet.java,\n\t  src/tigase/server/XMPPIOService.java,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/util/Telnet.java, src/tigase/xmpp,\n\t  src/tigase/xmpp/XMPPDomBuilderHandler.java,\n\t  src/tigase/xmpp/XMPPIOService.java: Framework for authorization\n\t  and some authorization implementation is ready\n\n2006-02-07 07:45  kobit\n\n\t* MANIFEST.MF, src/tigase/net/IOService.java,\n\t  src/tigase/server/ConnectionManager.java,\n\t  src/tigase/server/XMPPIOService.java: Better separation network\n\t  layer from protocol layer\n\n2006-02-06 22:21  kobit\n\n\t* MANIFEST.MF, prj.el, src/tigase/io,\n\t  src/tigase/io/IOInterface.java,\n\t  src/tigase/io/SSLContextContainer.java,\n\t  src/tigase/io/SocketIO.java, src/tigase/io/TLSIO.java,\n\t  src/tigase/io/TLSStatus.java, src/tigase/io/TLSUtil.java,\n\t  src/tigase/io/TLSWrapper.java, src/tigase/net,\n\t  src/tigase/net/Accept.java,\n\t  src/tigase/net/ConnectionOpenListener.java,\n\t  src/tigase/net/ConnectionOpenThread.java,\n\t  src/tigase/net/ConnectionType.java,\n\t  src/tigase/net/IOService.java,\n\t  src/tigase/net/IOServiceListener.java, src/tigase/net/Reply.java,\n\t  src/tigase/net/ServiceCommand.java,\n\t  src/tigase/net/SocketReadThread.java,\n\t  src/tigase/net/SocketType.java,\n\t  src/tigase/server/AbstractMessageReceiver.java,\n\t  src/tigase/server/ConnectionManager.java,\n\t  src/tigase/server/MessageReceiver.java,\n\t  src/tigase/server/MessageRouter.java,\n\t  src/tigase/server/MessageRouterConfig.java,\n\t  src/tigase/server/Packet.java,\n\t  src/tigase/server/ThreadExceptionHandler.java,\n\t  src/tigase/server/XMPPServer.java,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/util/JID.java, src/tigase/util/Telnet.java: Routing\n\t  between instances implemented\n\n2006-01-23 00:04  kobit\n\n\t* MANIFEST.MF, src/tigase/server/AbstractMessageReceiver.java,\n\t  src/tigase/server/ConnectionManager.java,\n\t  src/tigase/server/MessageRouter.java,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java:\n\t  abstract ConnectionManager introduced\n\n2006-01-15 23:48  kobit\n\n\t* MANIFEST.MF, src/tigase/conf/ConfigRepository.java,\n\t  src/tigase/server/AbstractComponentRegistrator.java,\n\t  src/tigase/server/AbstractMessageReceiver.java,\n\t  src/tigase/server/MessageReceiver.java,\n\t  src/tigase/server/MessageRouter.java,\n\t  src/tigase/server/MessageRouterConfig.java,\n\t  src/tigase/server/ServerComponent.java,\n\t  src/tigase/server/XMPPServer.java,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/tigase/server/xmppsession/SessionManager.java: Server basic\n\t  startup\n\n2006-01-05 23:59  kobit\n\n\t* src/tigase/util, src/tigase/util/LogFormatter.java: Improved\n\t  configuration management\n\n2006-01-05 23:58  kobit\n\n\t* MANIFEST.MF, src/tigase/conf/ConfigRepository.java,\n\t  src/tigase/conf/Configurator.java,\n\t  src/tigase/server/MessageRouter.java,\n\t  src/tigase/server/XMPPServer.java, tests/data/test_config.xml:\n\t  Improved configuration management\n\n2006-01-04 00:21  kobit\n\n\t* MANIFEST.MF, build.xml, src/tigase/conf/ConfigRepository.java,\n\t  src/tigase/conf/Configurable.java,\n\t  src/tigase/conf/Configurator.java,\n\t  src/tigase/server/AbstractMessageReceiver.java,\n\t  src/tigase/server/XMPPServer.java: Configuration basics\n\t  implemented\n\n2006-01-02 22:52  kobit\n\n\t* MANIFEST.MF, src/tigase/conf/ConfigRepository.java,\n\t  src/tigase/conf/Configurator.java, tests/data/test_config.xml:\n\t  XMLDB and config repository fixes\n\n2005-12-29 07:42  kobit\n\n\t* MANIFEST.MF, build.xml, src/tigase/conf/ConfigRepository.java,\n\t  src/tigase/conf/Configurator.java,\n\t  src/tigase/server/AbstractComponentRegistrator.java,\n\t  src/tigase/server/MessageRouter.java,\n\t  src/tigase/server/XMPPServer.java,\n\t  src/tigase/server/XMPPServiceCollector.java,\n\t  src/tigase/stats/StatisticsCollector.java, tests/data,\n\t  tests/data/test_config.xml: Added types to XMLDB and support for\n\t  them in Configurator\n\n2005-11-28 23:30  kobit\n\n\t* MANIFEST.MF, src/tigase/conf/Configurable.java,\n\t  src/tigase/conf/Configurator.java,\n\t  src/tigase/server/AbstractComponentRegistrator.java,\n\t  src/tigase/server/AbstractMessageReceiver.java,\n\t  src/tigase/server/ComponentRegistrator.java,\n\t  src/tigase/server/MessageReceiver.java,\n\t  src/tigase/server/MessageRouter.java,\n\t  src/tigase/server/XMPPServer.java,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/tigase/server/xmppsession/SessionManager.java: MessageRouter\n\t  implementation started\n\n2005-11-25 23:23  kobit\n\n\t* MANIFEST.MF, src/tigase/conf/Configurable.java,\n\t  src/tigase/server/MessageRouter.java,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/tigase/server/xmppsession/SessionManager.java: Configurable\n\t  interface corrected\n\n2005-11-25 23:19  kobit\n\n\t* MANIFEST.MF, src/tigase/annotations,\n\t  src/tigase/annotations/TODO.java,\n\t  src/tigase/conf/Configurable.java,\n\t  src/tigase/conf/Configurator.java,\n\t  src/tigase/server/AbstractMessageReceiver.java,\n\t  src/tigase/server/MessageReceiver.java,\n\t  src/tigase/server/MessageRouter.java,\n\t  src/tigase/server/XMPPServer.java,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/stats/StatRecord.java,\n\t  src/tigase/stats/StatisticType.java,\n\t  src/tigase/stats/StatisticsContainer.java:\n\t  AbstractMessageReceiver initial implementation\n\n2005-11-22 23:19  kobit\n\n\t* MANIFEST.MF, ant-definitions.xml, build.xml, jar,\n\t  src/tigase/conf/ConfigComponent.java,\n\t  src/tigase/conf/Configurable.java,\n\t  src/tigase/conf/Configurator.java,\n\t  src/tigase/http/HttpService.java,\n\t  src/tigase/http/HttpServiceIfc.java,\n\t  src/tigase/server/AbstractComponentRegistrator.java,\n\t  src/tigase/server/AbstractMessageReceiver.java,\n\t  src/tigase/server/ComponentRegistrator.java,\n\t  src/tigase/server/ComponentRegistratorIfc.java,\n\t  src/tigase/server/MessageReceiver.java,\n\t  src/tigase/server/MessageRouter.java,\n\t  src/tigase/server/Packet.java,\n\t  src/tigase/server/ServerComponent.java,\n\t  src/tigase/server/ServerComponentIfc.java,\n\t  src/tigase/server/XMPPService.java,\n\t  src/tigase/server/XMPPServiceCollector.java,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/stats/StatisticsCollector.java,\n\t  src/tigase/stats/StatisticsContainer.java,\n\t  src/tigase/stats/StatisticsContainerIfc.java,\n\t  src/tigase/stats/StatsComponent.java, src/tigase/ui/WebUI.java,\n\t  src/tigase/ui/XMPPServiceComponent.java: Initial, compilable\n\t  sources\n\n2005-11-21 07:33  kobit\n\n\t* ., ant-definitions.xml, build.properties, build.xml, certs,\n\t  certs/client_truststore, certs/dummy.cer, certs/keystore,\n\t  certs/truststore, jar, package-list, package-list/package-list,\n\t  prj.el, src, src/tigase, src/tigase/auth,\n\t  src/tigase/auth/AuthorisationSystem.java, src/tigase/conf,\n\t  src/tigase/conf/ConfigComponent.java,\n\t  src/tigase/conf/Configurable.java,\n\t  src/tigase/conf/Configurator.java, src/tigase/db,\n\t  src/tigase/db/ConfigurationDB.java,\n\t  src/tigase/db/MessageHistoryDB.java,\n\t  src/tigase/db/MessageOfflineDB.java, src/tigase/db/UserDB.java,\n\t  src/tigase/http, src/tigase/http/HttpServer.java,\n\t  src/tigase/http/HttpServiceIfc.java, src/tigase/server,\n\t  src/tigase/server/ComponentRegistratorIfc.java,\n\t  src/tigase/server/MessageReceiver.java,\n\t  src/tigase/server/MessageRouter.java,\n\t  src/tigase/server/Packet.java,\n\t  src/tigase/server/ServerComponentIfc.java,\n\t  src/tigase/server/XMPPService.java,\n\t  src/tigase/server/XMPPServiceCollector.java,\n\t  src/tigase/server/xmppclient,\n\t  src/tigase/server/xmppclient/ClientConnectionManager.java,\n\t  src/tigase/server/xmppcomponent,\n\t  src/tigase/server/xmppcomponent/ComponentConnectionManager.java,\n\t  src/tigase/server/xmppserver,\n\t  src/tigase/server/xmppserver/ServerConnectionManager.java,\n\t  src/tigase/server/xmppsession,\n\t  src/tigase/server/xmppsession/SessionManager.java,\n\t  src/tigase/stats, src/tigase/stats/StatisticsCollector.java,\n\t  src/tigase/stats/StatisticsContainerIfc.java,\n\t  src/tigase/stats/StatsComponent.java, src/tigase/ui,\n\t  src/tigase/ui/UIComponent.java, src/tigase/ui/WebUI.java,\n\t  src/tigase/ui/XMPPServiceComponent.java, tests, tests/unittests,\n\t  tests/unittests/src: Initial version of Tigase project.\n\n"
  },
  {
    "path": "scripts/debian/compat",
    "content": "5\n"
  },
  {
    "path": "scripts/debian/control",
    "content": "Source: tigase\nSection: net\nPriority: optional\nMaintainer: Sergey Nazarov <phearnot@renee.ru>\nBuild-Depends: debhelper (>= 5), cdbs, patchutils, sun-java6-jdk, ant\nStandards-Version: 3.7.2\n\nPackage: tigase\nSection: net\nPriority: optional\nPre-Depends: sun-java6-jre\nArchitecture: all\nDescription: A high performance XMPP (Jabber) server.\n Tigase is an instant messaging server that implements the XMPP\n (Jabber) protocol. It features high performance, a robust\n administration console, and a full plugin system. The server\n is 100% Java.\n .\n See http://www.tigase.org/\nHomepage: http://www.tigase.org/\n"
  },
  {
    "path": "scripts/debian/copyright",
    "content": "This package was debianized by Sergey Nazarov\n<phearnot@renee.ru> on January 9, 2009.\n The source was downloaded from http://www.tigase.org.\n\nUpstream author: Artur Hefczyc <artur.hefczyc@tigase.org>\n\nCopyright (C) 2004-2012 \"Artur Hefczyc\" <artur.hefczyc@tigase.org>\n\nYou are free to distribute this software under the terms of\nthe GNU Affero General Public License. On Debian systems, the complete\ntext of the GNU Affero General Public License can be found in the file\n'/usr/share/common-licenses/GPL'.\n\n"
  },
  {
    "path": "scripts/debian/init-debian.properties",
    "content": "config-type = --gen-config-def\n--admins = admin@$HOST_NAME\n--virt-hosts = $HOST_NAME\n--debug=server\n--user-db-uri = jdbc:derby:/var/lib/tigase/tigase-derbydb\nbasic-conf/logging/java.util.logging.FileHandler.pattern = /var/log/tigase/tigase.log\nvhost-man/vhost-repo-uri = jdbc:derby:/var/lib/tigase/tigase-derbydb\n"
  },
  {
    "path": "scripts/debian/rules",
    "content": "#!/usr/bin/make -f\n\n#export DH_VERBOSE=1\n\ninclude /usr/share/cdbs/1/rules/simple-patchsys.mk\ninclude /usr/share/cdbs/1/rules/debhelper.mk\ninclude /usr/share/cdbs/1/class/ant.mk\n\nDEST := $(CURDIR)/debian/tigase\nTARGET := $(CURDIR)\nTIGJARS := $(CURDIR)/jars\nTIGASE := $(DEST)/usr/share/tigase\nETCDIR := $(DEST)/etc/tigase\nLOGDIR := $(DEST)/var/log/tigase\nVARDIR := $(DEST)/var/lib/tigase\n\nJAVA_HOME := /usr/lib/jvm/java-6-openjdk\nANT_HOME := /usr/share/ant\nDEB_ANT_BUILDFILE := build.xml\nDEB_ANT_CLEAN_TARGET := clean\nDEB_JARS := ant-nodeps\n\t\ninstall/tigase::\n\tcp $(TIGJARS)/*.jar $(TIGASE)/jars\n\tcp $(TARGET)/libs/*.jar $(TIGASE)/libs\n\tcp -r $(TARGET)/certs $(TIGASE)/certs\n\tcp $(TARGET)/etc/init-debian.properties $(ETCDIR)/init.properties\n\n"
  },
  {
    "path": "scripts/debian/tigase-debian.conf",
    "content": "ENC=\"-Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8\"\nDRV=\"-Djdbc.drivers=com.mysql.jdbc.Driver:org.postgresql.Driver:org.apache.derby.jdbc.EmbeddedDriver\"\n#GC=\"-XX:+UseBiasedLocking -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:ParallelCMSThreads=2\"\nJAVA_HOME=\"/usr/lib/jvm/java-6-sun\"\nCLASSPATH=\"\"\nJAVA_OPTIONS=\"${GC} ${ENC} ${DRV} -server -Xms100M -Xmx200M -XX:PermSize=32m -XX:MaxPermSize=256m -XX:MaxDirectMemorySize=128m \"\nTIGASE_CONFIG=\"/etc/tigase/tigase-server.xml\"\nTIGASE_OPTIONS=\"\"\n"
  },
  {
    "path": "scripts/debian/tigase-install.sh",
    "content": "#!/bin/bash\n#\n# Tigase XMPP Server - The instant messaging server\n# Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, version 3 of the License.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. Look for COPYING file in the top folder.\n# If not, see http://www.gnu.org/licenses/.\n#\n\n\nCURDIR=`pwd`\nTARGET=/usr/share/tigase\nETCDIR=/etc/tigase\nLOGDIR=/var/log/tigase\nVARDIR=/var/lib/tigase\n\nif ! getent passwd tigase >/dev/null; then\n\tadduser --disabled-password  --quiet --system \\\n\t--home $VARDIR \\\n\t--gecos \"Tigase XMPP server\" --group tigase\nfi\nif ! getent group tigase >/dev/null; then\n\tadduser --system tigase\nfi\n\nmkdir -p $TARGET\nmkdir -p $ETCDIR\nmkdir -p $LOGDIR\nmkdir -p $VARDIR/tigase-derbydb\nmkdir -p $TARGET/jars/\nmkdir -p $TARGET/libs/\n\ncp -f $CURDIR/jars/*.jar $TARGET/jars/\ncp -f $CURDIR/libs/*.jar $TARGET/libs/\ncp -fr $CURDIR/certs/ $TARGET/\ncp -fr $CURDIR/database/ $TARGET/\ncp -fr $CURDIR/scripts/ $TARGET/\n\nchown -R tigase:tigase $TARGET\nchmod -R o-rwx $TARGET\n\ncp -f $CURDIR/scripts/debian/init-debian.properties $ETCDIR/init.properties\ncp -f $CURDIR/scripts/debian/tigase-debian.conf /etc/default/tigase\ncp -f $CURDIR/scripts/debian/tigase.init.d /etc/init.d/tigase\n\nchown -R tigase:tigase $ETCDIR\nchown -R tigase:tigase $LOGDIR\nchown -R tigase:tigase $VARDIR\n#chmod -R o-rwx $ETCDIR\n#chmod -R o-rwx $LOGDIR\n#chmod -R o-rwx $VARDIR\nchmod 755 /etc/init.d/tigase\n\nupdate-rc.d tigase defaults\n"
  },
  {
    "path": "scripts/debian/tigase.default",
    "content": "# Defaults for tigase initscript\n# sourced by /etc/init.d/tigase\n# installed at /etc/default/tigase by the maintainer scripts\n\n#\n# This is a POSIX shell fragment\n#\n\n# Additional options that are passed to the Daemon.\nTIGASE_OPTIONS=\"--property-file /etc/tigase/init.properties\"\n"
  },
  {
    "path": "scripts/debian/tigase.dirs",
    "content": "/etc/tigase\n/usr/share/tigase/libs\n/usr/share/tigase/jars\n/var/log/tigase\n/var/lib/tigase/embedded-db\n"
  },
  {
    "path": "scripts/debian/tigase.init.d",
    "content": "#!/bin/bash\n### BEGIN INIT INFO\n# Provides:          tigase\n# Required-Start:    networking\n# Required-Stop:     networking\n# Default-Start:     2 3 4 5\n# Default-Stop:      0 1 6\n# Short-Description: Start the Tigase XMPP server\n#\n### management instructions\n##\n## to install: update-rc.d tigase defaults\n##\n## on machines with NFS mounted it's essential to \n##   * NOT use 'noauto' option\n##   * add '$remote_fs' to Required-Start and Required-Stop\n##\n### END INIT INFO\n\n# Enable Tigase as OSGi bundle\n# OSGI=true\n\n# Settings paths and other variables\n# JAVA_HOME=\n\nUSERNAME=tigase\nUSERGROUP=tigase\nNAME=tigase\nDESC=\"Tigase XMPP server\"\n\nTIGASE_HOME=/usr/share/tigase\nTIGASE_CONFIG=/etc/tigase/tigase-server.xml\nTIGASE_OPTIONS=\nTIGASE_PARAMS=\n\nPIDFILE=\nTIGASE_CONSOLE_LOG=\n\nUSER=$USERNAME\neval HOME=\"~$USER\"\n\n# Attempt to locate JAVA_HOME, code borrowed from jabref package\nif [ -z $JAVA_HOME ]\nthen\n\tfor java_dir in /usr/lib/jvm/java-6-* ; do\n\t\ttest -d ${java_dir} && JAVA_HOME=${java_dir}\n\tdone\nfi\n\n# Include tigase defaults if available\nif [ -z \"${TIGASE_PARAMS}\" ] ; then\n\tif [ -r \"/etc/default/tigase\" ] ; then\n\t\tTIGASE_PARAMS=\"/etc/default/tigase\"\n\telif [ -r \"/etc/tigase/tigase.conf\" ] ; then\n\t\tTIGASE_PARAMS=\"/etc/tigase/tigase.conf\"\n\telif [ -r \"${TIGASE_HOME}/etc/tigase.conf\" ] ; then\n\t\tTIGASE_PARAMS=\"${TIGASE_HOME}/etc/tigase.conf\"\n\tfi\nfi\n\n[[ -f \"${TIGASE_PARAMS}\" ]] && . ${TIGASE_PARAMS}\n\nif [ -z \"${JAVA_HOME}\" ] ; then\n  echo \"JAVA_HOME is not set.\"\n  echo \"Please set it to correct value before starting the sever.\"\n  exit 1\nfi\n\nPATH=/sbin:/bin:/usr/sbin:/usr/bin:${JAVA_HOME}/bin\n\n# Find tigase-server jar\nif [ -n \"${OSGI}\" ] && ${OSGI} ; then\n\tLIB_DIR=jars\n\tJAR_FILE=${LIB_DIR}/org.apache.felix.main*.jar\nelse\n\tLIB_DIR=jars\n\tJAR_FILE=${LIB_DIR}/tigase-server*.jar\nfi\n\nfor j in ${TIGASE_HOME}/${JAR_FILE} ; do\n\tif [ -f ${j} ] ; then\n\t  TIGASE_JAR=${j}\n\t  break\n\tfi\ndone\n\nif [ -z \"${TIGASE_CONSOLE_LOG}\" ] ; then\n    if [ -w \"/var/log/${USERNAME}/\" ] ; then\n        TIGASE_CONSOLE_LOG=\"/var/log/${USERNAME}/tigase-console.log\"\n    elif [ -w \"${TIGASE_HOME}/logs/\" ] ; then\n        TIGASE_CONSOLE_LOG=\"${TIGASE_HOME}/logs/tigase-console.log\"\n    else\n        TIGASE_CONSOLE_LOG=\"/dev/null\"\n    fi\nfi\n\nif [ -z \"${PIDFILE}\" ] ; then\n    if [ ! -d \"/var/run/$NAME/\" ] ; then\n        mkdir \"/var/run/$NAME/\"\n        chown -R \"$USERNAME\":\"$USERGROUP\" \"/var/run/$NAME/\"\n    fi\n\n    if [ -w \"/var/run/$NAME/\" ] ; then\n        PIDFILE=\"/var/run/$NAME/$NAME.pid\"\n    elif [ -w \"${TIGASE_HOME}/logs/\" ] ; then\n        PIDFILE=\"${TIGASE_HOME}/logs/$NAME.pid\"\n    else\n        PIDFILE=\"/var/tmp/$NAME.pid\"\n    fi\nfi\n\n[[ -z \"${TIGASE_RUN}\" ]] && \\\n  TIGASE_RUN=\"tigase.server.XMPPServer -c ${TIGASE_CONFIG} ${TIGASE_OPTIONS}\"\n\n[[ -z \"${JAVA}\" ]] && JAVA=\"${JAVA_HOME}/bin/java\"\n\n[[ -z \"${CLASSPATH}\" ]] || CLASSPATH=\"${CLASSPATH}:\"\n\nCLASSPATH=\"${CLASSPATH}${TIGASE_JAR}\"\n\nfor lib in ${TIGASE_HOME}/${LIB_DIR}/*.jar ; do\n  CLASSPATH=\"${CLASSPATH}:$lib\"\ndone\n\nLOGBACK=\"-Dlogback.configurationFile=$TIGASE_HOME/etc/logback.xml\"\n\nif [ -n \"${OSGI}\" ] && ${OSGI} ; then\n\tTIGASE_CMD=\"${JAVA_OPTIONS} ${LOGBACK} -Dfelix.config.properties=file:$TIGASE_HOME/etc/config.properties -jar ${JAR_FILE}\"\nelse\n\tTIGASE_CMD=\"${JAVA_OPTIONS} ${LOGBACK} -cp ${CLASSPATH} ${TIGASE_RUN}\"\nfi\n\nif [ -d \"${TIGASE_HOME}\" ] ; then\n        cd ${TIGASE_HOME}\nelse\n  echo \"${TIGASE_HOME} is not set to correct value\"\n  echo \"Please set it to correct value before starting the sever.\"\n  exit 1\nfi\n\nset -e\n\n. /lib/lsb/init-functions\n\n#Helper functions\nstart() {\n\n\tif [ -f $PIDFILE ] && kill -0 `cat $PIDFILE` 2>/dev/null\n        then\n            echo \"Tigase is already running!\"\n            return 1\n        fi\n\n\tsu ${USERNAME} -c \"/sbin/start-stop-daemon --start --quiet --make-pidfile --chdir ${TIGASE_HOME} --pidfile $PIDFILE --chuid $USERNAME:$USERGROUP --exec $JAVA -- $TIGASE_CMD >>${TIGASE_CONSOLE_LOG} 2>&1 &\"\n\n\tsleep 3\n\tPID=`cat $PIDFILE`\n\n\tif [[ -z \"`ps -p ${PID} -o cmd=`\" ]]; then\n\t\trm -f \"$PIDFILE\"\n\t\treturn 1\n\telse\n\t\treturn 0\n\tfi\n}\n\nstop() {\n        su ${USERNAME} -c \"/sbin/start-stop-daemon --stop --quiet  --chdir ${TIGASE_HOME} --pidfile $PIDFILE --chuid $USERNAME:$USERGROUP  --exec $JAVA > /dev/null\"\n        \n\trm -f \"$PIDFILE\"\n}\n\ncase \"$1\" in\n  start)\n\tlog_daemon_msg \"Starting $DESC\"\n\tif start; then\n\t\tlog_end_msg 0\n\telse\n\t\tlog_end_msg 1\n\tfi\n\t;;\n  stop)\n\tlog_daemon_msg \"Stopping $DESC\"\n\tif stop; then\n\t\tlog_end_msg 0\n\telse\n\t\tlog_end_msg 1\n\tfi\n\t;;\n  restart|force-reload)\n\tlog_daemon_msg \"Restarting $DESC\"\n\tstop\n\tsleep 5\n\tif start; then\n\t\tlog_end_msg 0\n\telse\n\t\tlog_end_msg 1\n\tfi\n\t;;\n\n  clearrestart)\n    $0 stop $2\n    sleep 5\n    $0 clear $2\n    sleep 2\n    $0 start $2\n    ;;\n\n  clear)\n    LOGBACK=\"${TIGASE_HOME}/logs\"`date \"+%Y-%m-%d--%H:%M:%S\"`\n    log_daemon_msg \"Clearing logs $DESC, moving backup to \" ${LOGBACK}\n\tmkdir -p ${LOGBACK}\n\tmv \"${TIGASE_HOME}/logs\"/* ${LOGBACK}/\n\tlog_end_msg 0\n\tif [ -n \"${OSGI}\" ] && ${OSGI} ; then\n\t    log_daemon_msg \"Clearing osgi cache $DESC\"\n\t\trm -rf \"${TIGASE_HOME}/felix-cache\"/*;\n\t\tlog_end_msg 0\n\tfi\n\tlog_end_msg 0\n    ;;\n\n\n  check|status)\n\techo \"Checking arguments to Tigase: \"\n\techo\n\techo \"USERNAME            =  $USERNAME\"\n\techo \"USERGROUP           =  $USERGROUP\"\n\techo \"USER                =  $USER\"\n\techo \"HOME                =  $HOME\"\n\techo\n    echo \"OSGI                =  $OSGI\"\n\techo\n\techo \"TIGASE_HOME         =  $TIGASE_HOME\"\n\techo \"TIGASE_JAR          =  $TIGASE_JAR\"\n\techo \"TIGASE_CMD          =  $TIGASE_CMD\"\n\techo \"TIGASE_CONFIG       =  $TIGASE_CONFIG\"\n\techo \"TIGASE_PARAMS       =  $TIGASE_PARAMS\"\n\techo \"TIGASE_OPTIONS      =  $TIGASE_OPTIONS\"\n\techo \"TIGASE_CONSOLE_LOG  =  $TIGASE_CONSOLE_LOG\"\n\techo \"PIDFILE             =  $PIDFILE\"\n\techo \"JAVA_HOME           =  $JAVA_HOME\"\n\techo \"JAVA                =  $JAVA\"\n\techo \"JAVA_OPTIONS        =  $JAVA_OPTIONS\"\n\techo \"CLASSPATH           =  $CLASSPATH\"\n\n\tif [ -f $PIDFILE ] && kill -0 `cat $PIDFILE` 2>/dev/null\n\tthen\n\t\techo \"Tigase running pid=\"`cat $PIDFILE`\n\t\texit 0\n\tfi\n\texit 1\n\t;;\n\n  *)\n\tN=/etc/init.d/$NAME\n\techo \"Usage: $N {start|stop|restart|clearrestart|clear|force-reload|check|status}\" >&2\n\texit 1\n\t;;\nesac\n\nexit 0\n"
  },
  {
    "path": "scripts/debian/tigase.postinst",
    "content": "#! /bin/sh\nset -e\n\n# summary of how this script can be called:\n#        * <postinst> `configure' <most-recently-configured-version>\n#        * <old-postinst> `abort-upgrade' <new version>\n#        * <conflictor's-postinst> `abort-remove' `in-favour' <package>\n#          <new-version>\n#        * <deconfigured's-postinst> `abort-deconfigure' `in-favour'\n#          <failed-install-package> <version> `removing'\n#          <conflicting-package> <version>\n# for details, see http://www.debian.org/doc/debian-policy/ or\n# the debian-policy package\n#\n# quoting from the policy:\n#     Any necessary prompting should almost always be confined to the\n#     post-installation script, and should be protected with a conditional\n#     so that unnecessary prompting doesn't happen if a package's\n#     installation fails and the `postinst' is called with `abort-upgrade',\n#     `abort-remove' or `abort-deconfigure'.\n\ncase \"$1\" in\n\tconfigure)\n\t\tif ! getent passwd tigase >/dev/null; then\n\t\t\tadduser --disabled-password  --quiet --system \\\n\t\t\t--home /var/lib/tigase \\\n\t\t\t--gecos \"Tigase XMPP server\" --group tigase\n\t\tfi\n\t\tif ! getent group tigase >/dev/null; then\n\t\t\tadduser --system tigase\n\t\tfi\n \n\t\tmkdir -p /var/log/tigase\n\t\tmkdir -p /var/lib/tigase/tigase-derbydb\n\t\tchown -R tigase:tigase /usr/share/tigase\n\t\tchown -R tigase:tigase /var/lib/tigase\n\t\tchown -R tigase:tigase /var/log/tigase\n\t\tchown -R tigase:tigase /etc/tigase\n\t\tchmod -R o-rwx /usr/share/tigase\n\t\tchmod -R o-rwx /var/lib/tigase\n\t\tchmod -R o-rwx /var/log/tigase\n\t\tchmod -R o-rwx /etc/tigase\n\n    ;;\n\n    abort-upgrade|abort-remove|abort-deconfigure)\n\n    ;;\n\n    *)\n        echo \"postinst called with unknown argument \\`$1'\" >&2\n        exit 1\n    ;;\nesac\n\n# dh_installdeb will replace this with shell code automatically\n# generated by other debhelper scripts.\n\n#DEBHELPER#\n\nexit 0\n"
  },
  {
    "path": "scripts/debian/tigase.postrm",
    "content": "#!/bin/sh\n# postrm script for tigase\n#\n# see: dh_installdeb(1)\n\nset -e\n\n# summary of how this script can be called:\n#        * <postrm> `remove'\n#        * <postrm> `purge'\n#        * <old-postrm> `upgrade' <new-version>\n#        * <new-postrm> `failed-upgrade' <old-version>\n#        * <new-postrm> `abort-install'\n#        * <new-postrm> `abort-install' <old-version>\n#        * <new-postrm> `abort-upgrade' <old-version>\n#        * <disappearer's-postrm> `disappear' <r>overwrit>r> <new-version>\n# for details, see http://www.debian.org/doc/debian-policy/ or\n# the debian-policy package\n\n\ncase \"$1\" in\n     purge)\n\trm -Rf /etc/tigase\n\trm -Rf /var/log/tigase\n\trm -Rf /var/lib/tigase\n\trm -Rf /usr/share/tigase\n        ;; \n \n     remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)\n\n\n        ;;\n\n    *)\n        echo \"postrm called with unknown argument \\`$1'\" >&2\n        exit 1\n\nesac\n\n# dh_installdeb will replace this with shell code automatically\n# generated by other debhelper scripts.\n\n#DEBHELPER#\n\nexit 0\n\n"
  },
  {
    "path": "scripts/gentoo/conf.d/tigase",
    "content": "TIGASE_HOME=\"/home/tigase/tigase-server\"\nTIGASE_USER=tigase\nTIGASE_CONF=\"etc/tigase.conf\"\n"
  },
  {
    "path": "scripts/gentoo/init.d/tigase",
    "content": "#!/sbin/runscript\n\ndepend() {\n\tneed net\n\tuse dns logger postgresql mysql\n}\n\nstart()\t{\n\tebegin \"Starting Tigase\"\n\tsu - ${TIGASE_USER} -c \"${TIGASE_ENV} ${TIGASE_HOME}/bin/tigase.sh start ${TIGASE_HOME}/${TIGASE_CONF}\"\n\teend $?\n}\n\nstop ()\t{\n\tebegin \"Stopping Tigase\"\n\tsu - ${TIGASE_USER} -c \"${TIGASE_ENV} ${TIGASE_HOME}/bin/tigase.sh stop ${TIGASE_HOME}/${TIGASE_CONF}\"\n\tsleep 2\n\teend $?\n}\n\nsvc_restart () {\n\tebegin \"Restarting Tigase\"\n\tsu - ${TIGASE_USER} -c \"${TIGASE_ENV} ${TIGASE_HOME}/bin/tigase.sh stop ${TIGASE_HOME}/${TIGASE_CONF}\"\n\tsleep 10\n\tsu - ${TIGASE_USER} -c \"${TIGASE_ENV} ${TIGASE_HOME}/bin/tigase.sh start ${TIGASE_HOME}/${TIGASE_CONF}\"\n\teend $?\n}\n"
  },
  {
    "path": "scripts/machine-check.sh",
    "content": "#!/bin/bash\n#\n# Tigase XMPP Server - The instant messaging server\n# Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, version 3 of the License.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. Look for COPYING file in the top folder.\n# If not, see http://www.gnu.org/licenses/.\n#\n\n\nfunction usage() {\n  echo \"The script has to be run with following parameters:\"\n  echo \"$0 hostname username [vhost]\"\n  echo \"--------------------\"\n  echo \"  hostname - is the machine hostname which might be also used as the cluster node name\"\n  echo \"  username - is a user account from which the Tigase server will be run\"\n  echo \"  vhost    - is a virtual hostname used for the service if different than the hostname\"\n}\n\nfunction check_dns() {\n  if host $1 | grep \"not found\" > /dev/null ; then\n    echo \"WARNING - the $1 does NOT resolve to a valid IP address\"\n  else\n    if host $1 | grep \"127.0.0.1\" > /dev/null ; then\n      echo \"WARNING - DNS points to the localhost for the domain: $1\"\n    else\n      echo \"OK, DNS settings for $1\"\n    fi\n  fi\n  if host -t SRV _xmpp-server._tcp.$1 | grep SRV > /dev/null ; then\n    echo \"OK, SRV record found _xmpp-server._tcp.$1\"\n  else\n    echo \"WARNING - no SRV record _xmpp-server._tcp.$1\"\n  fi\n  if host -t SRV _xmpp-client._tcp.$1 | grep SRV > /dev/null ; then\n    echo \"OK, SRV record found _xmpp-client._tcp.$1\"\n  else\n    echo \"WARNING - no SRV record _xmpp-client._tcp.$1\"\n  fi\n}\n\nfunction check_net() {\n  if ping -q -c 2 -W 100 $1 &> /dev/null ; then\n    echo \"OK, The $1 host accessible through the network\"\n  else\n    echo \"WARNING - the $1 host seems to be unaccessible through the network!\"\n  fi\n}\n\nif [ \"$1\" = \"\" ] || [ \"$2\" = \"\" ] ; then\n  echo \"Missing hostname or username as a parameter, please run the script with correct parameters\"\n  usage\n  exit\nfi\n\nHST=$1\nUSR=$2\nVHST=$3\n\nif [ \"$VHST\" = \"\" ] ; then\n  VHST=$HST\nfi\n\n# Checking network configuration\n\ncheck_dns $HST\ncheck_dns $VHST\n\ncheck_net $HST\ncheck_net $VHST\n\n# Checking system configuration\n\nif echo $OSTYPE | grep -i linux > /dev/null ; then\n\n  if [ \"$USR\" = \"root\" ] ; then\n    echo \"WARNING - you should not run the Tigase server from the root account.\"\n  fi\n\n  if [ \"$UID\" == \"0\" ] ; then\n    # Checking ulimits\n    RES=`su -c - $USR \"ulimit -n\"`\n    if echo $RES | grep -i \"user $USR does not exist\" > /dev/null ; then\n      echo \"WARNING - the $USR user given does not exist, run 'adduser -m $USR' to create the account\"\n    else\n      if [ $RES -lt 300000 ] ; then\n        echo \"WARNING - maximum open files for the $USR user is too low: $RES\"\n        echo \"          To fix this, add following lines to file: /etc/security/limits.conf\"\n        echo \"$USR               soft    nofile         500000\"\n        echo \"$USR               hard    nofile         500000\"\n        echo \"-------\"\n        echo \"Add these lines? (yes/no)\"\n        read ans\n        if [ \"$ans\" = \"yes\" ] ; then\n          echo \"$USR               soft    nofile         500000\" >> /etc/security/limits.conf\n          echo \"$USR               hard    nofile         500000\" >> /etc/security/limits.conf\n        fi\n      else\n        echo \"OK, Max open files set to: $RES\"\n      fi\n    fi\n  else\n    echo \"I am running not within root account, can't check limits for the $USR\"\n  fi\n\n  # Checking kernel settings\n  RES=`/sbin/sysctl fs.file-max | sed -e \"s/fs.file-max *= *\\([0-9]\\+\\)/\\\\1/\"`\n  if [ $RES -lt 600000 ] ; then\n    echo \"WARNING - system wide fs.file-max is too low: $RES\"\n    echo \"          To fix this, add following line to file: /etc/sysctl.conf\"\n    echo \"fs.file-max=1000000\"\n    echo \"-------\"\n    echo \"Add the line and adjust sysctl for running system? (yes/no)\"\n    read ans\n    if [ \"$ans\" = \"yes\" ] ; then\n      echo \"fs.file-max=1000000\" >> /etc/sysctl.conf\n      /sbin/sysctl -p > /dev/null\n    fi\n  else\n    echo \"OK, system wide fs.file-max set to: $RES\"\n  fi\n\n  LO_RES=`/sbin/sysctl net.ipv4.ip_local_port_range | sed -e \"s/[^=]*= *\\([0-9]\\+\\)[^0-9]*\\([0-9]\\+\\)/\\\\1/\"`\n  HI_RES=`/sbin/sysctl net.ipv4.ip_local_port_range | sed -e \"s/[^=]*= *\\([0-9]\\+\\)[^0-9]*\\([0-9]\\+\\)/\\\\2/\"`\n  if [ $LO_RES -gt 5000 ] || [ $HI_RES -lt 64000 ] ; then\n    echo \"WARNING - IP port range is not optimal: $LO_RES   $HI_RES\"\n    echo \"          Recommended: 1024   65530\"\n    echo \"          To fix this, add following line to file: /etc/sysctl.conf\"\n    echo \"net.ipv4.ip_local_port_range=1024 65530\"\n    echo \"-------\"\n    echo \"Add the line and adjust sysctl for running system? (yes/no)\"\n    read ans     \n    if [ \"$ans\" = \"yes\" ] ; then\n      echo \"net.ipv4.ip_local_port_range=1024 65530\" >> /etc/sysctl.conf\n      /sbin/sysctl -p > /dev/null\n    fi\n  else\n    echo \"OK, IP port range set to: $LO_RES   $HI_RES\"\n  fi\n\nelse\n  echo \"The $OSTYPE is not supported for further checks yet\"\nfi\n\n"
  },
  {
    "path": "scripts/mandriva/init.d/tigase",
    "content": "#!/bin/bash\n#\n# Startup script for SER\n#\n# chkconfig: 345 85 15\n# description: Ser is a fast SIP Proxy.\n#\n# processname: ser\n# pidfile: /var/run/ser.pid\n# config: /etc/ser/ser.cfg\n\n# Source function library.\n. /etc/rc.d/init.d/functions\n\nexport JAVA_HOME=/usr/java/jdk1.6.0\nexport TIGASE_DIR=/opt/tigase/server/\ntigase=$TIGASE_DIR/scripts/tigase.sh\nprog=tigase\nconfig=$TIGASE_DIR/etc/tigase.conf\nRETVAL=0\n\nstart() {\n        gprintf \"Starting %s: \" \"$prog\"\n        daemon $tigase start $config\n        RETVAL=$?\n        echo\n        [ $RETVAL = 0 ] \n        return $RETVAL\n}\n\nstop() {\n\tgprintf \"Stopping %s: \" \"$prog\"\n        daemon $tigase stop $config\n\tRETVAL=$?\n\techo\n\t[ $RETVAL = 0 ] \n}\n\n\n# See how we were called.\ncase \"$1\" in\n  start)\n\tstart\n\t;;\n  stop)\n\tstop\n\t;;\n  status)\n        status $tigase\n\tRETVAL=$?\n\t;;\n  restart)\n\tstop\n\tsleep 10\n\tstart\n\t;;\n  condrestart)\n\tif [ -f /var/run/ser.pid ] ; then\n\t\tstop\n\t\tstart\n\tfi\n\t;;\n  *)\n\tgprintf \"Usage: %s {start|stop|restart|status|help}\\n\" \"$prog\"\n\texit 1\nesac\n\nexit $RETVAL\n"
  },
  {
    "path": "scripts/redhat/init.d/tigase",
    "content": "#!/bin/bash\n### BEGIN INIT INFO\n#\n# tigase        This starts and stops the Tigase XMPP server.\n#\n# chkconfig: 345 85 15\n# description: Tigase is a XMPP server.\n#\n# processname: tigase\n# pidfile: /var/run/tigase/tigase.pid\n# config: /home/tigase/tigase-server/etc/tigase.conf\n#\n### management instructions\n##\n## to install: /sbin/chkconfig --add tigase\n## to check:   /sbin/chkconfig --list tigase\n## to start:   /sbin/chkconfig tigase <on|off|reset>\n##\n### END INIT INFO\n\n# Settings paths and other variables\n\nJAVA_HOME=/usr/lib/jvm/java/\n\nUSERNAME=tigase\nUSERGROUP=tigase\nNAME=tigase\nDESC=\"Tigase XMPP server\"\n\nTIGASE_HOME=/home/tigase/tigase-server\nTIGASE_LIB=${TIGASE_HOME}/jars\nTIGASE_CONFIG=/etc/tigase/tigase-server.xml\nTIGASE_OPTIONS=\nTIGASE_PARAMS=\n\nPIDFILE=\nTIGASE_CONSOLE_LOG=\n\nUSER=$USERNAME\neval HOME=\"~$USER\"\n\n# Attempt to locate JAVA_HOME, code borrowed from jabref package\nif [ -z $JAVA_HOME ]\nthen\n\tfor java_dir in /usr/lib/jvm/java-6-* ; do\n\t\ttest -d ${java_dir} && JAVA_HOME=${java_dir}\n\tdone\nfi\n\n# Include tigase defaults if available\nif [ -z \"${TIGASE_PARAMS}\" ] ; then\n\tif [ -r \"/etc/default/tigase\" ] ; then\n\t\tTIGASE_PARAMS=\"/etc/default/tigase\"\n\telif [ -r \"/etc/tigase/tigase.conf\" ] ; then\n\t\tTIGASE_PARAMS=\"/etc/tigase/tigase.conf\"\n\telif [ -r \"${TIGASE_HOME}/etc/tigase.conf\" ] ; then\n\t\tTIGASE_PARAMS=\"${TIGASE_HOME}/etc/tigase.conf\"\n\tfi\nfi\n\n[[ -f \"${TIGASE_PARAMS}\" ]] && . ${TIGASE_PARAMS}\n\nif [ -z \"${JAVA_HOME}\" ] ; then\n\techo \"JAVA_HOME is not set.\"\n\techo \"Please set it to correct value before starting the sever.\"\n\texit 1\nfi\n\nPATH=/sbin:/bin:/usr/sbin:/usr/bin:${JAVA_HOME}/bin\n\n# Find tigase-server jar\nfor j in ${TIGASE_HOME}/jars/tigase-server*.jar ; do\n\tif [ -f ${j} ] ; then\n\t\tTIGASE_JAR=${j}\n\t\tbreak\n\tfi\ndone\n\nif [ -z \"${TIGASE_CONSOLE_LOG}\" ] ; then\n    if [ -w \"/var/log/${NAME}/\" ] ; then\n        TIGASE_CONSOLE_LOG=\"/var/log/${NAME}/tigase-console.log\"\n    elif [ -w \"${TIGASE_HOME}/logs/\" ] ; then\n        TIGASE_CONSOLE_LOG=\"${TIGASE_HOME}/logs/tigase-console.log\"\n    else\n        TIGASE_CONSOLE_LOG=\"/dev/null\"\n    fi\nfi\n\nif [ -z \"${PIDFILE}\" ] ; then\n    if [ -w \"/var/run/\" ] ; then\n    \tif [ ! -d \"/var/run/$NAME/\" ]; then\n    \t\tmkdir \"/var/run/$NAME/\";\n    \t\tchown $USERNAME:$USERGROUP \"/var/run/$NAME/\";\n    \tfi\n        PIDFILE=\"/var/run/$NAME/$NAME.pid\"\n    elif [ -w \"${TIGASE_HOME}/logs/\" ] ; then\n        PIDFILE=\"${TIGASE_HOME}/logs/$NAME.pid\"\n    else\n        PIDFILE=\"/var/tmp/$NAME.pid\"\n    fi\nfi\n\n[[ -z \"${TIGASE_RUN}\" ]] && \\\n\tTIGASE_RUN=\"tigase.server.XMPPServer -c ${TIGASE_CONFIG} ${TIGASE_OPTIONS}\"\n\n[[ -z \"${JAVA}\" ]] && JAVA=\"${JAVA_HOME}/bin/java\"\n\n[[ -z \"${CLASSPATH}\" ]] || CLASSPATH=\"${CLASSPATH}:\"\n\nCLASSPATH=\"${CLASSPATH}${TIGASE_JAR}\"\n\nfor lib in ${TIGASE_LIB}/*.jar ; do\n\tCLASSPATH=\"${CLASSPATH}:$lib\"\ndone\n\nTIGASE_CMD=\"${JAVA_OPTIONS} -cp ${CLASSPATH} ${TIGASE_RUN}\"\n\nif [ -d \"${TIGASE_HOME}\" ] ; then\n\tcd ${TIGASE_HOME}\nelse\n\techo \"${TIGASE_HOME} is not set to correct value\"\n\techo \"Please set it to correct value before starting the sever.\"\n\texit 1\nfi\n\n\n# Source function library.\n. /etc/rc.d/init.d/functions\n\n\n#Helper functions\nfunction checkRunning {\n\tlocal pid=\"$1\"\n\tif [ -z \"$pid\" -o \"$pid\" == \" \" ]; then\n\t\treturn 1;\n\tfi\n\t\n\tif [ ! -e /proc/$pid ]; then\n\t\trm -f $PIDFILE; return 1;\n\tfi\nreturn 0;\n}\n\nfunction getPid {\n\tif [ ! -f $PIDFILE ]; then\n\t\treturn 1;\n\tfi\n\tPID=\"$(<$PIDFILE)\"\n\tcheckRunning $PID || return 1\nreturn 0;\n}\n\nstart() {\n\tgetPid\n\tif [ $? -eq 0 ]; then\n\t\techo -ne \"Tigase is already running!\"\n\treturn 1\n\tfi\n\t\n\trm -f $PIDFILE\n\tlocal cmd=\"cd ${TIGASE_HOME}; setsid $JAVA $TIGASE_CMD >>${TIGASE_CONSOLE_LOG} 2>&1 & echo \\$! >$PIDFILE\"\n\tsu - ${USERNAME} -c \"$cmd\" || return 1\n\t\n\tsleep 1\n\tPID=\"$(<$PIDFILE)\"\n\tif checkRunning $PID; then :; else\n\t\techo -ne \"Tigase start failed, see logfile.\"\n\t\treturn 1\n\tfi\n\t\n\techo -ne \"Tigase started\"\n\treturn 0\n}\n\nstop() {\n    getPid\n\tif [ $? -ne 0 ]; then\n        echo -ne \"Tigase is not running!\"\n        return 1\n    fi\n\n    kill $PID || return 1\n    for ((i=0; i<15*10; i++)); do\n        checkRunning $PID\n            if [ $? -ne 0 ]; then\n                rm -f $PIDFILE\n                return 0\n            fi\n        sleep 1\n        done\n\n    echo -ne \"Tigase did not terminate within 15 seconds, sending SIGKILL...\"\n\n    kill -s KILL $PID || return 1\n    for ((i=0; i<15*10; i++)); do\n        checkRunning $PID\n        if [ $? -ne 0 ]; then\n            rm -f $PIDFILE\n            return 0\n        fi\n    sleep 0.1\n    done\n    echo -ne \"Error: Tigase could not be stopped\"\n    return 1\n}\n\ncase \"$1\" in\n\tstart)\n\techo \"Starting $DESC\"\n\tif start; then\n\t\tsuccess\n\t\techo\n\t\texit 0\n\telse\n\t\tfailure\n\t\techo\n\t\texit 1\n\tfi\n\techo\n\t;;\n\n\n\tstop)\n\techo \"Stopping $DESC\"\n\tif stop; then\n\t\tsuccess\n\t\techo\n\t\texit 0\n\telse\n\t\tfailure\n\t\techo\n\t\texit 1\n\tfi\n\techo\n\t;;\n\n\n\trestart|force-reload)\n\techo \"Restarting $DESC\"\n\tstop\n\tsleep 10\n\tif start; then\n\t\tsuccess\n\t\techo\n\t\texit 0\n\telse\n\t\tfailure\n\t\techo\n\t\texit 1\n\tfi\n\techo\n\t;;\n\n  clearrestart)\n    $0 stop $2\n    sleep 5\n    $0 clear $2\n    sleep 2\n    $0 start $2\n    ;;\n\n  clear)\n    LOGBACK=\"${TIGASE_HOME}/logs\"`date \"+%Y-%m-%d--%H:%M:%S\"`\n\techo \"Clearing logs, moving backup to \" ${LOGBACK}\n\tmkdir -p ${LOGBACK}\n\tmv \"${TIGASE_HOME}/logs\"/* ${LOGBACK}/\n\tif [ -n \"${OSGI}\" ] && ${OSGI} ; then\n\t\techo \"Clearing osgi cache\"\n\t\trm -rf \"${TIGASE_HOME}/felix-cache\"/*;\n\tfi\n    ;;\n\n\tcheck)\n\tgetPid\n\techo \"Checking arguments to Tigase: \"\n\techo\n\techo \"USERNAME            =  $USERNAME\"\n\techo \"USERGROUP           =  $USERGROUP\"\n\techo \"USER                =  $USER\"\n\techo \"HOME                =  $HOME\"\n\techo \"NAME                =  $NAME\"\n\techo\n\techo \"TIGASE_HOME         =  $TIGASE_HOME\"\n\techo \"TIGASE_JAR          =  $TIGASE_JAR\"\n\techo \"TIGASE_CMD          =  $TIGASE_CMD\"\n\techo \"TIGASE_CONFIG       =  $TIGASE_CONFIG\"\n\techo \"TIGASE_PARAMS       =  $TIGASE_PARAMS\"\n\techo \"TIGASE_OPTIONS      =  $TIGASE_OPTIONS\"\n\techo \"TIGASE_CONSOLE_LOG  =  $TIGASE_CONSOLE_LOG\"\n\techo \"PIDFILE             =  $PIDFILE\"\n\techo \"JAVA_HOME           =  $JAVA_HOME\"\n\techo \"JAVA                =  $JAVA\"\n\techo \"JAVA_OPTIONS        =  $JAVA_OPTIONS\"\n\techo \"CLASSPATH           =  $CLASSPATH\"\n\n\tif [ -f $PIDFILE ] && kill -0 `cat $PIDFILE` 2>/dev/null\n\tthen\n\t\techo \"Tigase running pid=\"`cat $PIDFILE`\n\t\texit 0\n\tfi\n\texit 1\n\t;;\n\n\t*)\n\tN=/etc/init.d/$NAME\n\techo \"Usage: $N {start|stop|restart|clearrestart|clear|force-reload|check|status}\" >&2\n\texit 1\n\t;;\nesac\n\nexit 0\n"
  },
  {
    "path": "scripts/repo.sh",
    "content": "#!/bin/bash\n#\n# Tigase XMPP Server - The instant messaging server\n# Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, version 3 of the License.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. Look for COPYING file in the top folder.\n# If not, see http://www.gnu.org/licenses/.\n#\n\nCP=\"jars/tigase-server.jar:libs/jdbc-mysql.jar:libs/jdbc-postgresql.jar:libs/tigase-xmltools.jar:libs/tigase-utils.jar\"\n\nD=\"-server -Xms100M -Xmx1500M -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 -Djdbc.drivers=com.mysql.jdbc.Driver:org.postgresql.Driver\"\n\nMYSQL_REP=\"-sc tigase.db.jdbc.JDBCRepository -su jdbc:mysql://localhost/nk_200k?user=root&password=mypass\"\nPGSQL_REP=\"-sc tigase.db.jdbc.JDBCRepository -su jdbc:postgresql://localhost/tigase?user=tigase\"\n\njava $D -cp $CP tigase.util.RepositoryUtils $MYSQL_REP $*\n"
  },
  {
    "path": "scripts/restart-all-servers.sh",
    "content": "#!/bin/bash\n#\n# Tigase XMPP Server - The instant messaging server\n# Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, version 3 of the License.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. Look for COPYING file in the top folder.\n# If not, see http://www.gnu.org/licenses/.\n#\n\n\n\nif [ \"$1\" == \"\" ] ; then\n  SERVS=`cat all-production-servers.txt`\nelse\n  SERVS=`cat $1`\nfi\n\n[[ -f script.conf ]] && . script.conf\n\nSERVERS=\"\"\n\nfor s in \"${SERVS}\" ; do\n  if [[ ${s} != \"#\"* ]] ; then\n    SERVERS=\"${SERVERS} ${s}\"\n  fi\ndone\n\nTIGASE_USER=\"tigase\"\nDIR=\"/home/${TIGASE_USER}/tigase-server\"\nPROP_FILE=\"cluster.properties\"\nCONF_FILE=\"cluster.tigase.conf\"\nJARS=\"target/tigase-server.jar\"\nLOG_TIMESTAMP=`date +\"%Y-%m-%d_%H-%M-%S\"`\nIPS=()\nCOLORS=('green' 'yellow' 'orange' 'blue' 'white' 'lightblue' 'gray' 'pink' 'lightgreen' 'red')\n\necho -e \"=== SERVERS:\\n${SERVERS}\"\necho -e \"=== DIR:\\n${DIR}\"\n\nread -p \"Press [Enter] key to start restart...\"\n\nfunction restart_server() {\n\n  s=$1\n  s_ip=$2\n  echo \"Restarting: ${s} ${s_ip}\"\n\n  if [ -f ${PROP_FILE} ] ; then\n    echo -e \"\\n\\n Copying ${PROP_FILE} file to ${s}\"\n    scp ${PROP_FILE} root@${s}:${DIR}/etc/init.properties\n  fi\n  if [ -f ${CONF_FILE} ] ; then\n    echo -e \"\\n\\n Copying ${CONF_FILE} file to ${s}\"\n    echo \"The cluster node ${s} IP is: ${s_ip}\"\n    sed -e \"s/\\(Djava.rmi.server.hostname=.*\\\"\\)/Djava.rmi.server.hostname=${s_ip}\\\"/\" ${CONF_FILE} > ${CONF_FILE}_${s}\n    scp ${CONF_FILE}_${s} root@${s}:${DIR}/etc/tigase.conf\n  fi\n  echo \"Copying jar files\"\n  for f in ${JARS} ; do\n    echo -e \"\\n\\n Copying ${f} file to ${s}\"\n    [[ -f ${f} ]]  && scp ${f} root@${s}:${DIR}/jars/\n  done\n  echo -e \"\\n\\n===\\trestarting ${s} ===\"\n  ssh root@${s} \"chown -R ${TIGASE_USER}:${TIGASE_USER} /home/${TIGASE_USER} ; service tigase stop ; sleep 10 ; cp -r $\\\n{DIR}/logs ${DIR}/logs_${LOG_TIMESTAMP} ; rm -f ${DIR}/logs/* ; service tigase start\"\n  echo -e \"===\\trestart of ${s} FINISHED ===\"\n}\n\nfunction restart_tmp() {\n\n  s=$1\n  s_ip=$2\n\techo \"Restarting: ${s} ${s_ip}\"\n  sleep 5\n\techo \"${s} restarted\"\n\n}\n\necho \"Resolving IPs...\"\n\ncnt=0\nfor s in ${SERVERS} ; do\n\n  IPS[$cnt]=`host ${s} | sed -e \"s/.*has address \\(.*\\)/\\1/\"`\n  echo \"${COLORS[$cnt]}:${s}:${IPS[$cnt]}\"\n  ((cnt++))\n\ndone\n\necho -ne \"nodes=\"\nCOLORS=('green' 'yellow' 'orange' 'blue' 'white' 'lightblue' 'gray' 'pink' 'lightgreen' 'red')\nfor i in \"${!IPS[@]}\"; do\n  echo -ne \"${COLORS[$i]}:${IPS[$i]},\"\ndone\necho \"\"\n\ncnt=0\nfor s in ${SERVERS} ; do\n\n  s_ip=${IPS[$cnt]}\n  ((cnt++))\n\n  restart_server $s $s_ip&\n\ndone\n"
  },
  {
    "path": "scripts/stop-all-servers.sh",
    "content": "#!/bin/bash\n#\n# Tigase XMPP Server - The instant messaging server\n# Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, version 3 of the License.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. Look for COPYING file in the top folder.\n# If not, see http://www.gnu.org/licenses/.\n#\n\n\n\nif [ \"$1\" == \"\" ] ; then\n  SERVERS=`cat all-production-servers.txt`\nelse\n  SERVERS=`cat $1`\nfi\n\nDIR=\"/home/tigase/tigase-server\"\n\necho -e \"=== SERVERS:\\n${SERVERS}\"\necho -e \"=== DIR:\\n${DIR}\"\n\nread -p \"Press [Enter] key to start restart...\"\n\nfor s in ${SERVERS} ; do\n\n  if [[ ${s} != \"#\"* ]] ; then\n\n\techo -e \"\\n\\n===\\trestarting ${s} ===\"\n\n\tssh root@${s} \"service tigase stop\"\n\n\techo -e \"===\\trestart of ${s} FINISHED ===\"\n\n  fi\n\ndone\n\n"
  },
  {
    "path": "scripts/systemd/tigase-server",
    "content": "# Defaults / Configuration options for Tigase XMPP Server"
  },
  {
    "path": "scripts/systemd/tigase-server.service",
    "content": "[Unit]\nDescription=Tigase XMPP Server\nAfter=syslog.target network-online.target\n\n[Service]\nType=forking\nUser=tigase\nEnvironmentFile=/etc/default/tigase-server\nWorkingDirectory=/home/tigase/tigase-server/\nPIDFile=/home/tigase/tigase-server/logs/tigase.pid\nExecStart=/home/tigase/tigase-server/scripts/tigase.sh start etc/tigase.conf\nExecStop=/home/tigase/tigase-server/scripts/tigase.sh stop etc/tigase.conf\nStandardOutput=syslog\nStandardError=syslog\nRestart=on-failure\nRestartSec=10\nKillMode=process\n\n[Install]\nWantedBy=multi-user.target"
  },
  {
    "path": "scripts/tigase.sh",
    "content": "#!/bin/bash\n#\n# Tigase XMPP Server - The instant messaging server\n# Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, version 3 of the License.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. Look for COPYING file in the top folder.\n# If not, see http://www.gnu.org/licenses/.\n#\n\nfunction usage()\n{\n  echo \"\";\n  echo \"Usage: $0 {start|stop|run|clear|clearrestart|restart|check|status|install-schema|upgrade-schema|upgrade-config|export-data|import-data} [params-file.conf] [parameters]\"\n  echo \"\";\n  echo -e \"\\tFor upgrade-schema task please add --help as a parameter a for list of parameters supported by this task.\"\n  exit 1\n}\n\nif [ -z \"${2}\" ] ; then\n  DEF_PARAMS=\"tigase.conf\"\n  # Gentoo style config location\n  if [ -f \"/etc/conf.d/${DEF_PARAMS}\" ] ; then\n\t\tTIGASE_PARAMS=\"/etc/conf.d/${DEF_PARAMS}\"\n  elif [ -f \"/etc/${DEF_PARAMS}\" ] ; then\n\t\tTIGASE_PARAMS=\"/etc/${DEF_PARAMS}\"\n  elif [ -f \"/etc/tigase/${DEF_PARAMS}\" ] ; then\n\t\tTIGASE_PARAMS=\"/etc/tigase/${DEF_PARAMS}\"\n  else\n\t\tTIGASE_PARAMS=\"\"\n  fi\n  echo \"No params-file.conf given. Using: '$TIGASE_PARAMS'\"\nelse\n  TIGASE_PARAMS=${2}\nfi\n\n[[ -f \"${TIGASE_PARAMS}\" ]] && . ${TIGASE_PARAMS}\n\nif [ -z \"${TIGASE_HOME}\" ] ; then\n  if [ ${0:0:1} = '/' ] ; then\n    TIGASE_HOME=${0}\n  else\n    TIGASE_HOME=${PWD}/${0}\n  fi\n  TIGASE_HOME=`dirname ${TIGASE_HOME}`\n  TIGASE_HOME=`dirname ${TIGASE_HOME}`\n\n  TIGASE_JAR=\"\"\nfi\n\nif [ -n \"${OSGI}\" ] && ${OSGI} ; then\n  echo \"OSGi mode is no longer supported by Tigase XMPP Server.\"\n  exit 1\n\tLIB_DIR=jars\n\tJAR_FILE=${LIB_DIR}/org.apache.felix.main*.jar\nelse\n\tLIB_DIR=jars\n\tJAR_FILE=${LIB_DIR}/tigase-server*.jar\nfi\n\nfor j in ${TIGASE_HOME}/${JAR_FILE} ; do\n\tif [ -f ${j} ] ; then\n\t  TIGASE_JAR=${j}\n\t  break\n\tfi\ndone\n\nif [ -z ${TIGASE_JAR} ] ; then\n\techo \"TIGASE_HOME is not set or main binary (${JAR_FILE}) was missing in ${TIGASE_HOME} location\"\n\techo \"Please set it to correct value before starting the sever.\"\n\texit 1\nfi\n\nif [ -z \"${TIGASE_CONSOLE_LOG}\" ] ; then\n  if [ ! -d \"logs\" ] ; then\n    mkdir logs\n  fi\n  if [ -w \"${TIGASE_HOME}/logs/\" ] ; then\n\t\tTIGASE_CONSOLE_LOG=\"${TIGASE_HOME}/logs/tigase-console.log\"\n  else\n\t\tTIGASE_CONSOLE_LOG=\"/dev/null\"\n  fi\nfi\n\nif [ -z \"${TIGASE_PID}\" ] ; then\n  if [ -w \"${TIGASE_HOME}/logs/\" ] ; then\n\t\tTIGASE_PID=\"${TIGASE_HOME}/logs/tigase.pid\"\n  else\n\t\tif [ -w \"/var/run/\" ] ; then\n\t    TIGASE_PID=\"/var/run/tigase.pid\"\n\t\telse\n\t    TIGASE_PID=\"/var/tmp/tigase.pid\"\n\t\tfi\n  fi\nfi\n\n[[ -z \"${TIGASE_RUN}\" ]] && \\\n  TIGASE_RUN=\"tigase.server.XMPPServer ${TIGASE_OPTIONS}\"\n\n[[ -z \"${SCHEMA_MANAGER}\" ]] && \\\n  SCHEMA_MANAGER=\"tigase.db.util.SchemaManager\"\n\n[[ -z \"${REPOSITORY_MANAGER}\" ]] && \\\n  REPOSITORY_MANAGER=\"tigase.db.util.importexport.RepositoryManager\"\n\n[[ -z \"${CONFIG_HOLDER}\" ]] && \\\n  CONFIG_HOLDER=\"tigase.conf.ConfigHolder\"\n\nJAVA=$(command -v java)\n\nif [ -n \"${JAVA_HOME}\" ] ; then\n  if [ -n \"$(command -v ${JAVA_HOME}/bin/java)\" ] ; then\n    JAVA=\"$(command -v ${JAVA_HOME}/bin/java)\"\n  fi\nfi\n\nif [ -z \"${JAVA}\" ] ; then\n  echo \"Java is not installed or not configured properly\"\n  echo \"Please make sure that 'java' can be executed or set JAVA_HOME to correct value before starting the sever.\"\n  exit 1\nfi\n\n[[ -z \"${CLASSPATH}\" ]] || CLASSPATH=\"${CLASSPATH}:\"\n\nCLASSPATH=\"${TIGASE_HOME}/${LIB_DIR}/*:${CLASSPATH}\"\n\nLOGBACK=\"-Dlogback.configurationFile=$TIGASE_HOME/etc/logback.xml\"\n\nif [ -n \"${OSGI}\" ] && ${OSGI} ; then\n\tTIGASE_CMD=\"${JAVA} ${JAVA_OPTIONS} ${LOGBACK} -Dfelix.config.properties=file:$TIGASE_HOME/etc/config.properties -jar ${JAR_FILE}\"\nelse\n\tTIGASE_CMD=\"${JAVA} ${JAVA_OPTIONS} ${LOGBACK} -cp ${CLASSPATH} ${TIGASE_RUN}\"\nfi\n\ncd \"${TIGASE_HOME}\"\n\ncase \"${1}\" in\n  start)\n    echo \"Starting Tigase: \"\n\n    if [ -f ${TIGASE_PID} ] && kill -0 $(<${TIGASE_PID}) 2>/dev/null\n    then\n      echo \"Already Running!!\"\n      exit 1\n    fi\n\n    echo -e \"==========\\nSTARTED Tigase `date` using:\\n    ${0} ${1} ${2} ${3}\\n==========\" >> ${TIGASE_CONSOLE_LOG}\n\n    nohup sh -c \"exec $TIGASE_CMD >>${TIGASE_CONSOLE_LOG} 2>&1\" >/dev/null &\n    echo $! > $TIGASE_PID\n    echo \"Tigase running pid=\"`cat $TIGASE_PID`\n    ;;\n\n  stop)\n    PID=`cat $TIGASE_PID 2>/dev/null`\n    if [ -z \"$PID\" ] ; then\n      echo \"Tigase is not running.\"\n      exit 0\n    fi\n    echo \"Shutting down Tigase: $PID\"\n\n\tkill $PID 2>/dev/null\n\tfor ((i=1; i <= 20; i++)) ; do\n\t  if ps -p $PID > /dev/null ; then\n\t\techo \"$i. Waiting for the server to terminate...\"\n\t\tsleep 1\n\t  else\n\t\techo \"$i. Tigase terminated.\"\n\t\tbreak\n\t  fi\n\tdone\n\t\n\tif ps -p $PID > /dev/null ; then\n      echo \"Forcing the server to terminate.\"\n      kill -9 $PID 2>/dev/null\n    fi\n    rm -f $TIGASE_PID\n    echo \"STOPPED `date`\" >>${TIGASE_CONSOLE_LOG}\n    ;;\n\n  restart)\n    $0 stop $2\n    sleep 5\n    $0 start $2\n    ;;\n\n  clearrestart)\n    $0 stop $2\n    sleep 5\n    $0 clear $2\n    sleep 2\n    $0 start $2\n    ;;\n\n  clear)\n    LOGBACK=\"${TIGASE_HOME}/logs\"`date \"+%Y-%m-%d--%H:%M:%S\"`\n\techo \"Clearing logs, moving backup to \" ${LOGBACK}\n\tmkdir -p ${LOGBACK}\n\tmv \"${TIGASE_HOME}/logs\"/* ${LOGBACK}/\n\tif [ -n \"${OSGI}\" ] && ${OSGI} ; then\n\t\techo \"Clearing osgi cache\"\n\t\trm -rf \"${TIGASE_HOME}/felix-cache\"/*;\n\tfi\n    ;;\n\n  run)\n    echo \"Running Tigase: \"\n\n    if [ -f $TIGASE_PID ]\n    then\n      echo \"Already Running!!\"\n      exit 1\n    fi\n\n    sh -c \"exec $TIGASE_CMD\"\n    ;;\n\n  export-data|import-data)\n    TMP=\"${@:2}\"\n    sh -c \"${JAVA} ${JAVA_OPTIONS} -DscriptName='${0}' ${LOGBACK} -cp ${CLASSPATH} ${REPOSITORY_MANAGER} ${1} ${TIGASE_OPTIONS} ${TMP}\"\n    ;;\n\n  upgrade-schema|install-schema|destroy-schema)\n    TMP=\"${@:2}\"\n    sh -c \"${JAVA} ${JAVA_OPTIONS} -DscriptName='${0}' ${LOGBACK} -cp ${CLASSPATH} ${SCHEMA_MANAGER} ${1} ${TIGASE_OPTIONS} ${TMP}\"\n    ;;\n\n  upgrade-config)\n    TMP=\"${@:3}\"\n    sh -c \"${JAVA} ${JAVA_OPTIONS} -DscriptName='${0}' ${LOGBACK} -cp ${CLASSPATH} ${CONFIG_HOLDER} ${1} ${TIGASE_OPTIONS} ${TMP}\"\n    ;;\n\n  check|status)\n    echo \"Checking arguments to Tigase: \"\n    echo \"OSGI            =  $OSGI\"\n    echo \"TIGASE_HOME     =  $TIGASE_HOME\"\n    echo \"TIGASE_JAR      =  $TIGASE_JAR\"\n    echo \"TIGASE_PARAMS   =  $TIGASE_PARAMS\"\n    echo \"TIGASE_RUN      =  $TIGASE_RUN\"\n    echo \"TIGASE_PID      =  $TIGASE_PID\"\n    echo \"TIGASE_OPTIONS  =  $TIGASE_OPTIONS\"\n    echo \"JAVA_HOME       =  $JAVA_HOME\"\n    echo \"JAVA            =  $JAVA\"\n    echo \"JAVA_OPTIONS    =  $JAVA_OPTIONS\"\n    echo \"JAVA_CMD        =  $JAVA_CMD\"\n    echo \"CLASSPATH       =  $CLASSPATH\"\n    echo \"TIGASE_CMD      =  $TIGASE_CMD\"\n    echo \"TIGASE_CONSOLE_LOG  =  $TIGASE_CONSOLE_LOG\"\n    echo\n\n    if [ -f ${TIGASE_PID} ] && kill -0 $(<${TIGASE_PID}) 2>/dev/null\n    then\n      echo \"Tigase running pid=\"`cat ${TIGASE_PID}`\n      exit 0\n    fi\n    exit 1\n    ;;\n  zap)\n\t\trm -f $TIGASE_PID\n\t\t;;\n\n\t*)\n    usage\n\t\t;;\nesac\n"
  },
  {
    "path": "scripts/update-all-servers.sh",
    "content": "#!/bin/bash\n#\n# Tigase XMPP Server - The instant messaging server\n# Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, version 3 of the License.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. Look for COPYING file in the top folder.\n# If not, see http://www.gnu.org/licenses/.\n#\n\n\nif [ \"$1\" == \"\" ] ; then\n\tLOCATIONS=`cat all-production-servers.txt`\nelse\n\tLOCATIONS=`cat $1`\nfi\nDIR=\"/home/tigase/tigase-server\"\n\necho -e \"=== LOCATIONS:\\n${LOCATIONS}\"\necho -e \"=== DIR:\\n${DIR}\"\n\nread -p \"Press [Enter] key to start update...\"\n\n\nfor s in ${LOCATIONS} ; do\n\n\techo -e \"\\n\\n===\\tuploading to ${s} ===\"\n\n\tscp jars/tigase-server.jar tigase@${s}:${DIR}/jars/\n\tscp libs/tigase-* tigase@${s}:${DIR}/libs/\n\tscp src/main/groovy/tigase/admin/*.groovy tigase@${s}:${DIR}/scripts/admin/\n\n\techo -e \"===\\tupload to ${s} DONE ===\"\n\ndone\n"
  },
  {
    "path": "scripts/update-code-functions.sh",
    "content": "#!/bin/bash\n#\n# Tigase XMPP Server - The instant messaging server\n# Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, version 3 of the License.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. Look for COPYING file in the top folder.\n# If not, see http://www.gnu.org/licenses/.\n#\n\n\nexport TMP_DIR=\"/tmp\"\n\nfunction build_xmltools() {\n   mvn -q clean package install &> ${TMP_DIR}/xmltools-build.txt\n   ant clean jar-dist &> ${TMP_DIR}/xmltools-build.txt\n   echo jars/tigase-xmltools.jar\n }\n\n function build_utils() {\n   mvn -q clean package install &> ${TMP_DIR}/utils-build.txt\n   cp -f ../xmltools/jars/tigase-xmltools.jar jars/\n   ant clean jar-dist &> ${TMP_DIR}/utils-build.txt\n   echo jars/tigase-utils.jar\n }\n\n function build_server() {\n   mvn -q clean package install &> ${TMP_DIR}/server-build.txt\n   ant clean jar-dist &> ${TMP_DIR}/server-build.txt\n   echo \"\"\n }\n\n function build_maven() {\n   mvn clean package &> ${TMP_DIR}/$1-build.txt\n   echo `find target -name \"tigase-*.jar\"`\n }\n\n function build_extras() {\n   build_maven extras\n }\n\n function build_muc() {\n   build_maven muc\n }\n\n function build_pubsub() {\n   build_maven pubsub\n }\n\nfunction build_archiving() {\n\tbuild_maven archiving\n}\n\nfunction build_socks5() {\n\tbuild_maven socks5\n}\n\nfunction build_stun() {\n\tbuild_maven stun\n}\n\nfunction build_tigase_http_api() {\n  build_maven \"tigase-http-api\"\n}\n"
  },
  {
    "path": "scripts/update-code.sh",
    "content": "#!/bin/bash\n#\n# Tigase XMPP Server - The instant messaging server\n# Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, version 3 of the License.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. Look for COPYING file in the top folder.\n# If not, see http://www.gnu.org/licenses/.\n#\n\n\nMY_DIR=`dirname ${0}`\n\n# Load external script with some utility functions\n. ${MY_DIR}/update-code-functions.sh\n\n# Load configuration\n# PROJECTS_DIR - directory with source codes for all projects:\n# xmltools, utils, extras, server, muc, pubsub, socks5, stun, archiving\n# TARGET_DIR - a directory where the installed serve binaries are located\n\nCONFIG=\"${MY_DIR}/update-code-config.sh\"\nif [ \"$1\" != \"\" ] ; then\n  CONFIG=\"$1\"\nfi\n. ${CONFIG}\n\nSRV_PACKAGES=\"xmltools utils extras server muc pubsub archiving socks5 stun tigase_http_api\"\n\nCUR_DIR=`pwd`\n\ncd ${PROJECTS_DIR}\n\n#set -x\n\necho \"Updating and building packages...\"\n\n# Build all server packages\nfor p in $SRV_PACKAGES ; do\n  echo \"Building package: $p\"\n  cd $p\n  mkdir -p jars\n  git pull\n  JAR=`build_$p`\n  echo \"jar file: $JAR\"\n  if [ ! -z \"${JAR}\" ] ; then\n    cp -f $JAR ../server/jars/tigase-$p.jar\n  fi\n  cd ..\ndone\n\n\ncd ${CUR_DIR}\n\nif [ \"${TARGET_DIR}\" != \"\" ] ; then\n  cp -fv ${PROJECTS_DIR}/server/jars/* ${TARGET_DIR}/bundle/\n\tcp -fv ${PROJECTS_DIR}/server/jars/* ${TARGET_DIR}/jars/\nfi\n"
  },
  {
    "path": "scripts/user_roster.sh",
    "content": "#!/bin/bash\n#\n# Tigase XMPP Server - The instant messaging server\n# Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, version 3 of the License.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. Look for COPYING file in the top folder.\n# If not, see http://www.gnu.org/licenses/.\n#\n\nCP=\"jars/tigase-server.jar:libs/jdbc-mysql.jar:libs/tigase-xmltools.jar:libs/tigase-utils.jar\"\n\nD=\"-server -Xms100M -Xmx1500M -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 -Djdbc.drivers=com.mysql.jdbc.Driver:org.postgresql.Driver\"\n\nXML_REP=\"-sc tigase.db.xml.XMLRepository -su ../testsuite/user-repository.xml_200k_backup\"\nD_MYSQL_REP=\"-dc tigase.db.jdbc.JDBCRepository -du jdbc:mysql://localhost/tigasetest200k?user=root&password=mypass\"\nS_MYSQL_REP=\"-sc tigase.db.jdbc.JDBCRepository -su jdbc:mysql://localhost/tigasetest?user=root&password=mypass\"\nS_PGSQL_REP=\"-sc tigase.db.jdbc.JDBCRepository -su jdbc:postgresql://localhost/tigase?user=tigase\"\n\njava $D -cp $CP tigase.util.RepositoryUtils $S_MYSQL_REP -u \"$1\" -pr\n"
  },
  {
    "path": "scripts/wait-for-it.sh",
    "content": "#!/usr/bin/env bash\n#\n# Tigase XMPP Server - The instant messaging server\n# Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, version 3 of the License.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. Look for COPYING file in the top folder.\n# If not, see http://www.gnu.org/licenses/.\n#\n\n# Use this script to test if a given TCP host/port are available\n# MIT Licensed, from https://github.com/vishnubob/wait-for-it\n\nWAITFORIT_cmdname=${0##*/}\n\nechoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo \"$@\" 1>&2; fi }\n\nusage()\n{\n    cat << USAGE >&2\nUsage:\n    $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args]\n    -h HOST | --host=HOST       Host or IP under test\n    -p PORT | --port=PORT       TCP port under test\n                                Alternatively, you specify the host and port as host:port\n    -s | --strict               Only execute subcommand if the test succeeds\n    -q | --quiet                Don't output any status messages\n    -t TIMEOUT | --timeout=TIMEOUT\n                                Timeout in seconds, zero for no timeout\n    -- COMMAND ARGS             Execute command with args after the test finishes\nUSAGE\n    exit 1\n}\n\nwait_for()\n{\n    if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then\n        echoerr \"$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT\"\n    else\n        echoerr \"$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout\"\n    fi\n    WAITFORIT_start_ts=$(date +%s)\n    while :\n    do\n        if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then\n            nc -z $WAITFORIT_HOST $WAITFORIT_PORT\n            WAITFORIT_result=$?\n        else\n            (echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1\n            WAITFORIT_result=$?\n        fi\n        if [[ $WAITFORIT_result -eq 0 ]]; then\n            WAITFORIT_end_ts=$(date +%s)\n            echoerr \"$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds\"\n            break\n        fi\n        sleep 1\n    done\n    return $WAITFORIT_result\n}\n\nwait_for_wrapper()\n{\n    # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692\n    if [[ $WAITFORIT_QUIET -eq 1 ]]; then\n        timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &\n    else\n        timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &\n    fi\n    WAITFORIT_PID=$!\n    trap \"kill -INT -$WAITFORIT_PID\" INT\n    wait $WAITFORIT_PID\n    WAITFORIT_RESULT=$?\n    if [[ $WAITFORIT_RESULT -ne 0 ]]; then\n        echoerr \"$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT\"\n    fi\n    return $WAITFORIT_RESULT\n}\n\n# process arguments\nwhile [[ $# -gt 0 ]]\ndo\n    case \"$1\" in\n        *:* )\n        WAITFORIT_hostport=(${1//:/ })\n        WAITFORIT_HOST=${WAITFORIT_hostport[0]}\n        WAITFORIT_PORT=${WAITFORIT_hostport[1]}\n        shift 1\n        ;;\n        --child)\n        WAITFORIT_CHILD=1\n        shift 1\n        ;;\n        -q | --quiet)\n        WAITFORIT_QUIET=1\n        shift 1\n        ;;\n        -s | --strict)\n        WAITFORIT_STRICT=1\n        shift 1\n        ;;\n        -h)\n        WAITFORIT_HOST=\"$2\"\n        if [[ $WAITFORIT_HOST == \"\" ]]; then break; fi\n        shift 2\n        ;;\n        --host=*)\n        WAITFORIT_HOST=\"${1#*=}\"\n        shift 1\n        ;;\n        -p)\n        WAITFORIT_PORT=\"$2\"\n        if [[ $WAITFORIT_PORT == \"\" ]]; then break; fi\n        shift 2\n        ;;\n        --port=*)\n        WAITFORIT_PORT=\"${1#*=}\"\n        shift 1\n        ;;\n        -t)\n        WAITFORIT_TIMEOUT=\"$2\"\n        if [[ $WAITFORIT_TIMEOUT == \"\" ]]; then break; fi\n        shift 2\n        ;;\n        --timeout=*)\n        WAITFORIT_TIMEOUT=\"${1#*=}\"\n        shift 1\n        ;;\n        --)\n        shift\n        WAITFORIT_CLI=(\"$@\")\n        break\n        ;;\n        --help)\n        usage\n        ;;\n        *)\n        echoerr \"Unknown argument: $1\"\n        usage\n        ;;\n    esac\ndone\n\nif [[ \"$WAITFORIT_HOST\" == \"\" || \"$WAITFORIT_PORT\" == \"\" ]]; then\n    echoerr \"Error: you need to provide a host and port to test.\"\n    usage\nfi\n\nWAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15}\nWAITFORIT_STRICT=${WAITFORIT_STRICT:-0}\nWAITFORIT_CHILD=${WAITFORIT_CHILD:-0}\nWAITFORIT_QUIET=${WAITFORIT_QUIET:-0}\n\n# Check to see if timeout is from busybox?\nWAITFORIT_TIMEOUT_PATH=$(type -p timeout)\nWAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH)\n\nWAITFORIT_BUSYTIMEFLAG=\"\"\nif [[ $WAITFORIT_TIMEOUT_PATH =~ \"busybox\" ]]; then\n    WAITFORIT_ISBUSY=1\n    # Check if busybox timeout uses -t flag\n    # (recent Alpine versions don't support -t anymore)\n    if timeout &>/dev/stdout | grep -q -e '-t '; then\n        WAITFORIT_BUSYTIMEFLAG=\"-t\"\n    fi\nelse\n    WAITFORIT_ISBUSY=0\nfi\n\nif [[ $WAITFORIT_CHILD -gt 0 ]]; then\n    wait_for\n    WAITFORIT_RESULT=$?\n    exit $WAITFORIT_RESULT\nelse\n    if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then\n        wait_for_wrapper\n        WAITFORIT_RESULT=$?\n    else\n        wait_for\n        WAITFORIT_RESULT=$?\n    fi\nfi\n\nif [[ $WAITFORIT_CLI != \"\" ]]; then\n    if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then\n        echoerr \"$WAITFORIT_cmdname: strict mode, refusing to execute subprocess\"\n        exit $WAITFORIT_RESULT\n    fi\n    exec \"${WAITFORIT_CLI[@]}\"\nelse\n    exit $WAITFORIT_RESULT\nfi\n"
  },
  {
    "path": "src/main/bash/cron-dnotify-check.sh",
    "content": "#!/bin/bash\n#\n# Tigase XMPP Server - The instant messaging server\n# Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, version 3 of the License.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. Look for COPYING file in the top folder.\n# If not, see http://www.gnu.org/licenses/.\n#\n\n\nmonitored_dir=\"/home/webapp/drupal-sites/users-list\"\nmydir=`dirname $0`\n\ncron_check=`ps axw | grep -c \"[d]notify -C\"`\n\nif [ $cron_check -eq 0 ] ; then \n#  echo \"dnotify monitor is not running, starting a new one....\"\n  /usr/bin/dnotify -C -b $monitored_dir -e $mydir/users-list-moitor.sh '{}'\nfi\n"
  },
  {
    "path": "src/main/bash/users-list-moitor.sh",
    "content": "#!/bin/bash\n#\n# Tigase XMPP Server - The instant messaging server\n# Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, version 3 of the License.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. Look for COPYING file in the top folder.\n# If not, see http://www.gnu.org/licenses/.\n#\n\n\nmydir=$1\n\nif [ \"$mydir\" == \"\" ] ; then\n  echo \"Directory name is missing...\"\n  exit 1\nfi\n\nfor i in $mydir/* ; do\n  newuser=`basename $i`\n  echo \"Adding new user: \"$newuser\n  /usr/sbin/useradd $newuser -m\n  rm -f $i\n  /usr/bin/mail -s \"Welcome to Tigase LiveCD\" -t ${newuser}@livecd.tigase.org <<EOFMAIL\nHi,\n\nThis is an automated message sent to every new user registered on the LiveCD.\n\nPlease enjoy the system and send me your comments.\n\nArtur Hefczyc\n.\nEOFMAIL\ndone\n"
  },
  {
    "path": "src/main/database/derby-common-0.0.1.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n\n-- QUERY START:\ncreate table tig_schema_versions (\n\t-- Component Name\n\tcomponent varchar(100) NOT NULL,\n\t-- Version of loaded schema\n\tversion varchar(100) NOT NULL,\n\t-- Time when schema was loaded last time\n\tlast_update timestamp NOT NULL\n);\n-- QUERY END:\n\n-- QUERY START:\ncreate unique index component on tig_schema_versions ( component );\n-- QUERY END:\n\n\n-- QUERY START:\nCREATE procedure TigGetComponentVersion(component varchar(100))\n  PARAMETER STYLE JAVA\n  LANGUAGE JAVA\n  MODIFIES SQL DATA\n  DYNAMIC RESULT SETS 1\n  EXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigGetComponentVersion';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigSetComponentVersion(component varchar(100), version varchar(100))\n  PARAMETER STYLE JAVA\n  LANGUAGE JAVA\n  MODIFIES SQL DATA\n  EXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigSetComponentVersion';\n-- QUERY END:"
  },
  {
    "path": "src/main/database/derby-common-0.0.2.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\nupdate tig_schema_versions\nset version = version || '-SNAPSHOT'\nwhere exists (\n    select 1\n    from tig_schema_versions\n    where\n        component = 'common'\n        and version = '0.0.1'\n) and version not like '%-SNAPSHOT%';\n-- QUERY END:"
  },
  {
    "path": "src/main/database/derby-common-0.0.3.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n--\n"
  },
  {
    "path": "src/main/database/derby-counter_data_logger-0.0.1.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\ncreate table tig_stats_log (\n  lid            bigint                    generated by default as identity not null,\n  ts             TIMESTAMP                 DEFAULT CURRENT_TIMESTAMP,\n  hostname       varchar(2049)    NOT NULL,\n  cpu_usage      double precision not null default 0,\n  mem_usage      double precision not null default 0,\n  uptime         bigint           not null default 0,\n  vhosts         int              not null default 0,\n  sm_packets     bigint           not null default 0,\n  muc_packets    bigint           not null default 0,\n  pubsub_packets bigint           not null default 0,\n  c2s_packets    bigint           not null default 0,\n  ws2s_conns     bigint           not null default 0,\n  s2s_packets    bigint           not null default 0,\n  ext_packets    bigint           not null default 0,\n  presences      bigint           not null default 0,\n  messages       bigint           not null default 0,\n  iqs            bigint           not null default 0,\n  registered     bigint           not null default 0,\n  c2s_conns      int              not null default 0,\n  ws2s_packets   int              not null default 0,\n  bosh_conns     int              not null default 0,\n  s2s_conns      int              not null default 0,\n  sm_sessions    int              not null default 0,\n  sm_connections int              not null default 0,\n  primary key (ts, hostname)\n);\n-- QUERY END:"
  },
  {
    "path": "src/main/database/derby-installer-create-db.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n"
  },
  {
    "path": "src/main/database/derby-installer-post.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n"
  },
  {
    "path": "src/main/database/derby-server-8.0.0-props.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\ncall TigSetComponentVersion('server', '8.0.0');\n-- QUERY END:\n"
  },
  {
    "path": "src/main/database/derby-server-8.0.0-schema.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\ncreate table tig_users (\n\tuid BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL PRIMARY KEY,\n\n\t-- Jabber User ID\n\tuser_id varchar(2049) NOT NULL,\n\t-- User password encrypted or not\n\tuser_pw varchar(255) default NULL,\n\t-- Time the account has been created\n\tacc_create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n\t-- Time of the last user login\n\tlast_login timestamp,\n\t-- Time of the last user logout\n\tlast_logout timestamp,\n\t-- User online status, if > 0 then user is online, the value\n\t-- indicates the number of user connections.\n\t-- It is incremented on each user login and decremented on each\n\t-- user logout.\n\tonline_status int default 0,\n\t-- Number of failed login attempts\n\tfailed_logins int default 0,\n\t-- User status, whether the account is active or disabled\n\t-- >0 - account active, 0 - account disabled\n\taccount_status int default 1\n);\n-- QUERY END:\n\n-- QUERY START:\ncreate unique index user_id on tig_users ( user_id );\n-- QUERY END:\n-- QUERY START:\ncreate index user_pw on tig_users (user_pw);\n-- QUERY END:\n-- QUERY START:\ncreate index last_login on tig_users (last_login);\n-- QUERY END:\n-- QUERY START:\ncreate index last_logout on tig_users (last_logout);\n-- QUERY END:\n-- QUERY START:\ncreate index account_status on tig_users (account_status);\n-- QUERY END:\n-- QUERY START:\ncreate index online_status on tig_users (online_status);\n-- QUERY END:\n\n-- QUERY START:\ncreate table tig_nodes (\n\tnid BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL PRIMARY KEY,\n\tparent_nid bigint,\n\tuid bigint NOT NULL references tig_users(uid),\n\tnode varchar(255) NOT NULL\n);\n-- QUERY END:\n-- QUERY START:\ncreate unique index tnode on tig_nodes ( parent_nid, uid, node );\n-- QUERY END:\n-- QUERY START:\ncreate index node on tig_nodes ( node );\n-- QUERY END:\n-- QUERY START:\n-- The new index is a duplicate of an existing index\ncreate index nuid on tig_nodes (uid);\n-- QUERY END:\n-- QUERY START:\ncreate index parent_nid on tig_nodes (parent_nid);\n-- QUERY END:\n\n-- QUERY START:\ncreate table tig_pairs (\n\tPID BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL PRIMARY KEY,\n\tnid bigint references tig_nodes(nid),\n\tuid bigint NOT NULL references tig_users(uid),\n\tpkey varchar(255) NOT NULL,\n\tpval CLOB\n);\n-- QUERY END:\n-- QUERY START:\n-- The new index is a duplicate of an existing index\ncreate index pkey on tig_pairs ( pkey );\n-- QUERY END:\n-- QUERY START:\n-- The new index is a duplicate of an existing index\ncreate index puid on tig_pairs (uid);\n-- QUERY END:\n-- QUERY START:\ncreate index pnid on tig_pairs (nid);\n-- QUERY END:\n\n-- QUERY START:\ncreate table tig_offline_messages (\n    msg_id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL PRIMARY KEY,\n    ts timestamp DEFAULT CURRENT_TIMESTAMP,\n    expired timestamp,\n    sender varchar(2049),\n    sender_sha1 char(40),\n    receiver varchar(2049) not null,\n    receiver_sha1 char(40) not null,\n\tmsg_type int not null default 0,\n\tmessage varchar(32672) not null\n);\n-- QUERY END:\n\n-- QUERY START:\ncreate index tig_offline_messages_expired on tig_offline_messages (expired);\n-- QUERY END:\n-- QUERY START:\ncreate index tig_offline_messages_receiver on tig_offline_messages (receiver_sha1);\n-- QUERY END:\n-- QUERY START:\ncreate index tig_offline_messages_receiver_sender on tig_offline_messages (receiver_sha1, sender_sha1);\n-- QUERY END:\n\n-- QUERY START:\ncreate table tig_broadcast_messages (\n    id varchar(128) not null,\n    expired timestamp not null,\n    msg varchar(32672) not null,\n    primary key (id)\n);\n-- QUERY END:\n\n-- QUERY START:\ncreate table tig_broadcast_jids (\n    jid_id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL PRIMARY KEY,\n    jid varchar(2049) not null,\n    jid_sha1 char(128) not null\n);\n-- QUERY END:\n\n-- QUERY START:\ncreate table tig_broadcast_recipients (\n    msg_id varchar(128) not null references tig_broadcast_messages(id),\n    jid_id bigint not null references tig_broadcast_jids(jid_id),\n    primary key (msg_id, jid_id)\n);\n-- QUERY END:\n\n-- QUERY START:\ncreate table tig_cluster_nodes (\n    hostname varchar(512) not null,\n\tsecondary varchar(512),\n    password varchar(255) not null,\n    last_update timestamp default current_timestamp,\n    port int,\n    cpu_usage double precision not null,\n    mem_usage double precision not null,\n    primary key (hostname)\n);\n-- QUERY END:\n\n-- ------------- Credentials support\n-- QUERY START:\ncreate table tig_user_credentials (\n    uid bigint not null references tig_users(uid),\n    username varchar(2049) not null,\n    mechanism varchar(128) not null,\n    value varchar(32672) not null,\n\n    primary key (uid, username, mechanism)\n);\n-- QUERY END:"
  },
  {
    "path": "src/main/database/derby-server-8.0.0-sp.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n\n-- QUERY START:\nCREATE function TigGetDBProperty(tkey varchar(255)) returns long varchar\n\tPARAMETER STYLE JAVA\n\tLANGUAGE JAVA\n\tREADS SQL DATA\n\tEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigGetDBProperty';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigPutDBProperty(tkey varchar(255), tval varchar(32672))\n\tPARAMETER STYLE JAVA\n\tLANGUAGE JAVA\n\tMODIFIES SQL DATA\n\tEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigPutDBProperty';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigInitdb()\n\tPARAMETER STYLE JAVA\n\tLANGUAGE JAVA\n\tMODIFIES SQL DATA\n\tEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigInitdb';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigAddUser(userId varchar(2049), userPw varchar(255))\n\tPARAMETER STYLE JAVA\n\tLANGUAGE JAVA\n\tMODIFIES SQL DATA\n\tDYNAMIC RESULT SETS 1\n\tEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigAddUser';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigAddUserPlainPw(userId varchar(2049), userPw varchar(255))\n\tPARAMETER STYLE JAVA\n\tLANGUAGE JAVA\n\tMODIFIES SQL DATA\n\tDYNAMIC RESULT SETS 1\n\tEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigAddUserPlainPw';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigGetUserDBUid(userId varchar(2049))\n\tPARAMETER STYLE JAVA\n\tLANGUAGE JAVA\n\tREADS SQL DATA\n\tDYNAMIC RESULT SETS 1\n\tEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigGetUserDBUid';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigRemoveUser(userId varchar(2049))\n\tPARAMETER STYLE JAVA\n\tLANGUAGE JAVA\n\tMODIFIES SQL DATA\n\tEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigRemoveUser';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigGetPassword(userId varchar(2049))\n\tPARAMETER STYLE JAVA\n\tLANGUAGE JAVA\n\tREADS SQL DATA\n\tDYNAMIC RESULT SETS 1\n\tEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigGetPassword';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigUpdatePasswordPlainPwRev(userId varchar(2049), userPw varchar(255))\n  PARAMETER STYLE JAVA\n  LANGUAGE JAVA\n  MODIFIES SQL DATA\n  EXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigUpdatePasswordPlainPwRev';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigUpdatePasswordPlainPw(userId varchar(2049), userPw varchar(255))\n\tPARAMETER STYLE JAVA\n\tLANGUAGE JAVA\n\tMODIFIES SQL DATA\n\tEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigUpdatePasswordPlainPw';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigUpdatePassword(userId varchar(2049), userPw varchar(255))\n\tPARAMETER STYLE JAVA\n\tLANGUAGE JAVA\n\tMODIFIES SQL DATA\n\tEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigUpdatePassword';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigOnlineUsers()\n\tPARAMETER STYLE JAVA\n\tLANGUAGE JAVA\n\tREADS SQL DATA\n\tDYNAMIC RESULT SETS 1\n\tEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigOnlineUsers';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigOfflineUsers()\n\tPARAMETER STYLE JAVA\n\tLANGUAGE JAVA\n\tREADS SQL DATA\n\tDYNAMIC RESULT SETS 1\n\tEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigOfflineUsers';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigAllUsers()\n\tPARAMETER STYLE JAVA\n\tLANGUAGE JAVA\n\tREADS SQL DATA\n\tDYNAMIC RESULT SETS 1\n\tEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigAllUsers';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigAllUsersCount()\n\tPARAMETER STYLE JAVA\n\tLANGUAGE JAVA\n\tREADS SQL DATA\n\tDYNAMIC RESULT SETS 1\n\tEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigAllUsersCount';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigUserLoginPlainPw(userId varchar(2049), userPw varchar(255))\n\tPARAMETER STYLE JAVA\n\tLANGUAGE JAVA\n\tMODIFIES SQL DATA\n\tDYNAMIC RESULT SETS 1\n\tEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigUserLoginPlainPw';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigUserLogin(userId varchar(2049), userPw varchar(255))\n\tPARAMETER STYLE JAVA\n\tLANGUAGE JAVA\n\tMODIFIES SQL DATA\n\tDYNAMIC RESULT SETS 1\n\tEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigUserLogin';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigUserLogout(userId varchar(2049))\n\tPARAMETER STYLE JAVA\n\tLANGUAGE JAVA\n\tMODIFIES SQL DATA\n\tEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigUserLogout';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigDisableAccount(userId varchar(2049))\n\tPARAMETER STYLE JAVA\n\tLANGUAGE JAVA\n\tMODIFIES SQL DATA\n\tEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigDisableAccount';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigEnableAccount(userId varchar(2049))\n\tPARAMETER STYLE JAVA\n\tLANGUAGE JAVA\n\tMODIFIES SQL DATA\n\tEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigEnableAccount';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigActiveAccounts()\n\tPARAMETER STYLE JAVA\n\tLANGUAGE JAVA\n\tREADS SQL DATA\n\tDYNAMIC RESULT SETS 1\n\tEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigActiveAccounts';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigDisabledAccounts()\n\tPARAMETER STYLE JAVA\n\tLANGUAGE JAVA\n\tREADS SQL DATA\n\tDYNAMIC RESULT SETS 1\n\tEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigDisabledAccounts';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigAddNode(parentNid bigint, uid bigint, node varchar(255))\n\tPARAMETER STYLE JAVA\n\tLANGUAGE JAVA\n\tMODIFIES SQL DATA\n\tDYNAMIC RESULT SETS 1\n\tEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigAddNode';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigUpdatePairs(nid bigint, uid bigint, tkey varchar(255), tval clob)\n\tPARAMETER STYLE JAVA\n\tLANGUAGE JAVA\n\tMODIFIES SQL DATA\n\tEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigUpdatePairs';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure TigUpdateLoginTime(userId varchar(2049))\nPARAMETER STYLE JAVA\nLANGUAGE JAVA\nMODIFIES SQL DATA\nEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigUpdateLoginTime';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure Tig_OfflineMessages_AddMessage(\"to\" varchar(2049), \"from\" varchar(2049), \"type\" int, \"ts\" timestamp, \"message\" varchar(32672), \"expired\" timestamp, \"limit\" bigint)\nPARAMETER STYLE JAVA\nLANGUAGE JAVA\nMODIFIES SQL DATA\nDYNAMIC RESULT SETS 1\nEXTERNAL NAME 'tigase.db.derby.MsgRepositoryStoredProcedures.addMessage';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure Tig_OfflineMessages_GetMessages(\"to\" varchar(2049))\nPARAMETER STYLE JAVA\nLANGUAGE JAVA\nREADS SQL DATA\nDYNAMIC RESULT SETS 1\nEXTERNAL NAME 'tigase.db.derby.MsgRepositoryStoredProcedures.getMessages';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure Tig_OfflineMessages_GetMessagesByIds(\"to\" varchar(2049), \"msg_id1\" varchar(50), \"_msg_id2\" varchar(50), \"_msg_id3\" varchar(50), \"_msg_id4\" varchar(50))\nPARAMETER STYLE JAVA\nLANGUAGE JAVA\nREADS SQL DATA\nDYNAMIC RESULT SETS 1\nEXTERNAL NAME 'tigase.db.derby.MsgRepositoryStoredProcedures.getMessagesByIds';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure Tig_OfflineMessages_GetMessagesCount(\"to\" varchar(2049))\nPARAMETER STYLE JAVA\nLANGUAGE JAVA\nREADS SQL DATA\nDYNAMIC RESULT SETS 1\nEXTERNAL NAME 'tigase.db.derby.MsgRepositoryStoredProcedures.getMessagesCount';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure Tig_OfflineMessages_ListMessages(\"to\" varchar(2049))\nPARAMETER STYLE JAVA\nLANGUAGE JAVA\nREADS SQL DATA\nDYNAMIC RESULT SETS 1\nEXTERNAL NAME 'tigase.db.derby.MsgRepositoryStoredProcedures.listMessages';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure Tig_OfflineMessages_DeleteMessages(\"to\" varchar(2049))\nPARAMETER STYLE JAVA\nLANGUAGE JAVA\nMODIFIES SQL DATA\nDYNAMIC RESULT SETS 1\nEXTERNAL NAME 'tigase.db.derby.MsgRepositoryStoredProcedures.deleteMessages';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure Tig_OfflineMessages_DeleteMessagesByIds(\"to\" varchar(2049), \"msg_id1\" varchar(50), \"_msg_id2\" varchar(50), \"_msg_id3\" varchar(50), \"_msg_id4\" varchar(50))\nPARAMETER STYLE JAVA\nLANGUAGE JAVA\nMODIFIES SQL DATA\nDYNAMIC RESULT SETS 1\nEXTERNAL NAME 'tigase.db.derby.MsgRepositoryStoredProcedures.deleteMessagesByIds';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure Tig_OfflineMessages_DeleteMessage(msg_id bigint)\nPARAMETER STYLE JAVA\nLANGUAGE JAVA\nMODIFIES SQL DATA\nEXTERNAL NAME 'tigase.db.derby.MsgRepositoryStoredProcedures.deleteMessage';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure Tig_OfflineMessages_GetExpiredMessages(\"limit\" int)\nPARAMETER STYLE JAVA\nLANGUAGE JAVA\nREADS SQL DATA\nDYNAMIC RESULT SETS 1\nEXTERNAL NAME 'tigase.db.derby.MsgRepositoryStoredProcedures.getExpiredMessages';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure Tig_OfflineMessages_GetExpiredMessagesBefore(\"before\" timestamp)\nPARAMETER STYLE JAVA\nLANGUAGE JAVA\nREADS SQL DATA\nDYNAMIC RESULT SETS 1\nEXTERNAL NAME 'tigase.db.derby.MsgRepositoryStoredProcedures.getExpiredMessagesBefore';\n-- QUERY END:\n\n\n-- ------------ Broadcast Messages\n-- QUERY START:\nCREATE procedure Tig_BroadcastMessages_AddMessage(\"msg_id\" varchar(128), \"expired\" timestamp, \"msg\" varchar(32672))\nPARAMETER STYLE JAVA\nLANGUAGE JAVA\nMODIFIES SQL DATA\nEXTERNAL NAME 'tigase.db.derby.MsgBroadcastRepositoryStoredProcedures.addMessage';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure Tig_BroadcastMessages_AddMessageRecipient(\"msg_id\" varchar(128), \"jid\" varchar(2049))\nPARAMETER STYLE JAVA\nLANGUAGE JAVA\nMODIFIES SQL DATA\nEXTERNAL NAME 'tigase.db.derby.MsgBroadcastRepositoryStoredProcedures.addMessageRecipient';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure Tig_BroadcastMessages_GetMessages(\"expired\" timestamp)\nPARAMETER STYLE JAVA\nLANGUAGE JAVA\nREADS SQL DATA\nDYNAMIC RESULT SETS 1\nEXTERNAL NAME 'tigase.db.derby.MsgBroadcastRepositoryStoredProcedures.getMessages';\n-- QUERY END:\n\n-- QUERY START:\nCREATE procedure Tig_BroadcastMessages_GetMessageRecipients(\"msg_id\" varchar(128))\nPARAMETER STYLE JAVA\nLANGUAGE JAVA\nREADS SQL DATA\nDYNAMIC RESULT SETS 1\nEXTERNAL NAME 'tigase.db.derby.MsgBroadcastRepositoryStoredProcedures.getMessageRecipients';\n-- QUERY END:\n\n-- QUERY START:\nCREATE PROCEDURE TigUpdateAccountStatus(\"userId\" VARCHAR(2049), \"status\" INT)\nPARAMETER STYLE JAVA\nLANGUAGE JAVA\nMODIFIES SQL DATA\nEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigUpdateAccountStatus';\n-- QUERY END:\n\n-- QUERY START:\nCREATE PROCEDURE TigAccountStatus(\"userId\" VARCHAR(2049))\nPARAMETER STYLE JAVA\nLANGUAGE JAVA\nDYNAMIC RESULT SETS 1\nEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigAccountStatus';\n-- QUERY END:\n\n\n-- ------------- Credentials support\n-- QUERY START:\nCREATE PROCEDURE TigUserCredential_Update(userId varchar(2049), username varchar(2049), mechanism varchar(128), value varchar(32672))\nPARAMETER STYLE JAVA\nLANGUAGE JAVA\nMODIFIES SQL DATA\nEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigUserCredentialUpdate';\n-- QUERY END:\n\n-- QUERY START:\nCREATE PROCEDURE TigUserCredentials_Get(userId varchar(2049), username varchar(2049))\nPARAMETER STYLE JAVA\nLANGUAGE JAVA\nDYNAMIC RESULT SETS 1\nEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigUserCredentialsGet';\n-- QUERY END:\n\n-- QUERY START:\nCREATE PROCEDURE TigUserUsernames_Get(userId varchar(2049))\nPARAMETER STYLE JAVA\nLANGUAGE JAVA\nDYNAMIC RESULT SETS 1\nEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigUserUsernamesGet';\n-- QUERY END:\n\n-- QUERY START:\nCREATE PROCEDURE TigUserCredential_Remove(userId varchar(2049), username varchar(2049))\nPARAMETER STYLE JAVA\nLANGUAGE JAVA\nMODIFIES SQL DATA\nEXTERNAL NAME 'tigase.db.derby.StoredProcedures.tigUserCredentialRemove';\n-- QUERY END:\n"
  },
  {
    "path": "src/main/database/derby-server-8.0.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\nrun 'database/derby-server-8.0.0-schema.sql';\n\nrun 'database/derby-server-8.0.0-sp.sql';\n\nrun 'database/derby-server-8.0.0-props.sql';\n\n-- LOAD FILE: database/derby-server-8.0.0-schema.sql\n\n-- LOAD FILE: database/derby-server-8.0.0-sp.sql\n\n-- LOAD FILE: database/derby-server-8.0.0-props.sql\n"
  },
  {
    "path": "src/main/database/derby-server-8.1.0-props.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\ncall TigSetComponentVersion('server', '8.1.0');\n-- QUERY END:\n"
  },
  {
    "path": "src/main/database/derby-server-8.1.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n\nrun 'database/derby-server-8.1.0-props.sql';\n\n-- LOAD FILE: database/derby-server-8.1.0-props.sql\n"
  },
  {
    "path": "src/main/database/derby-server-8.2.0-props.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\ncall TigSetComponentVersion('server', '8.2.0');\n-- QUERY END:\n"
  },
  {
    "path": "src/main/database/derby-server-8.2.0-schema.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\nALTER TABLE tig_users ADD COLUMN last_used timestamp;\n-- QUERY END:"
  },
  {
    "path": "src/main/database/derby-server-8.2.0-sp.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n\n-- QUERY START:\nDROP procedure TigPutDBProperty;\n-- QUERY END:\n\n-- QUERY START:\nDROP procedure TigUserLogin;\n-- QUERY END:\n-- QUERY START:\nDROP procedure TigUserLogout;\n-- QUERY END:\n-- QUERY START:\nDROP procedure TigOnlineUsers;\n-- QUERY END:\n-- QUERY START:\nDROP procedure TigOfflineUsers;\n-- QUERY END:\n-- QUERY START:\nDROP procedure TigUserLoginPlainPw;\n-- QUERY END:"
  },
  {
    "path": "src/main/database/derby-server-8.2.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\nrun 'database/derby-server-8.2.0-schema.sql';\n\nrun 'database/derby-server-8.2.0-sp.sql';\n\nrun 'database/derby-server-8.2.0-props.sql';\n\n-- LOAD FILE: database/derby-server-8.2.0-schema.sql\n\n-- LOAD FILE: database/derby-server-8.2.0-sp.sql\n\n-- LOAD FILE: database/derby-server-8.2.0-props.sql\n"
  },
  {
    "path": "src/main/database/derby-server-8.3.0-props.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\ncall TigSetComponentVersion('server', '8.3.0');\n-- QUERY END:\n"
  },
  {
    "path": "src/main/database/derby-server-8.3.0-schema.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n--\n"
  },
  {
    "path": "src/main/database/derby-server-8.3.0-sp.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n--\n"
  },
  {
    "path": "src/main/database/derby-server-8.3.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\nrun 'database/derby-server-8.3.0-schema.sql';\n\nrun 'database/derby-server-8.3.0-sp.sql';\n\nrun 'database/derby-server-8.3.0-props.sql';\n\n-- LOAD FILE: database/derby-server-8.3.0-schema.sql\n\n-- LOAD FILE: database/derby-server-8.3.0-sp.sql\n\n-- LOAD FILE: database/derby-server-8.3.0-props.sql\n"
  },
  {
    "path": "src/main/database/derby-server-8.4.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n"
  },
  {
    "path": "src/main/database/derby-server-8.5.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n"
  },
  {
    "path": "src/main/database/mssql-server-8.5.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n"
  },
  {
    "path": "src/main/database/mysql-common-0.0.1.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n\n-- QUERY START:\ncreate table if not EXISTS tig_schema_versions (\n\t-- Component Name\n\tcomponent varchar(100) NOT NULL,\n\t-- Version of loaded schema\n\tversion varchar(100) NOT NULL,\n\t-- Time when schema was loaded last time\n\tlast_update timestamp NOT NULL,\n\tprimary key (component)\n);\n-- QUERY END:\n\n-- QUERY START:\ndrop PROCEDURE if exists TigGetComponentVersion;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigSetComponentVersion;\n-- QUERY END:\n\ndelimiter //\n\n-- QUERY START:\ncreate PROCEDURE TigGetComponentVersion(_component varchar(100) CHARSET utf8)\n  begin\n    select version from tig_schema_versions where (component = _component);\n  end //\n-- QUERY END:\n\n-- QUERY START:\ncreate procedure TigSetComponentVersion(_component varchar(255) CHARSET utf8, _version mediumtext CHARSET utf8)\n  begin\n    INSERT INTO tig_schema_versions (component, version, last_update)\n    VALUES (_component, _version, now())\n    ON DUPLICATE KEY UPDATE\n      version = _version, last_update = now();\n  end //\n-- QUERY END:\n"
  },
  {
    "path": "src/main/database/mysql-common-0.0.2.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n\n-- QUERY START:\nupdate tig_schema_versions\nset version = concat(version, '-SNAPSHOT')\nwhere exists (\n    select 1\n    from (select * from tig_schema_versions) sv\n    where\n        sv.component = 'common'\n        and sv.version = '0.0.1'\n) and version not like '%-SNAPSHOT%';\n-- QUERY END:"
  },
  {
    "path": "src/main/database/mysql-common-0.0.3.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n\n-- QUERY START:\ndrop procedure if exists TigExecuteIf;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigExecuteIfNot;\n-- QUERY END:\n\ndelimiter //\n\n-- QUERY START:\ncreate procedure TigExecuteIf(cond int, query text)\nbegin\nset @s = (select if (\n        cond > 0,\n        query,\n        'select 1'\n    ));\nprepare stmt from @s;\nexecute stmt;\ndeallocate prepare stmt;\nend //\n-- QUERY END:\n\n-- QUERY START:\ncreate procedure TigExecuteIfNot(cond int, query text)\nbegin\nset @s = (select if (\n        cond <= 0,\n        query,\n        'select 1'\n    ));\nprepare stmt from @s;\nexecute stmt;\ndeallocate prepare stmt;\nend //\n-- QUERY END:\n\ndelimiter ;"
  },
  {
    "path": "src/main/database/mysql-counter_data_logger-0.0.1.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\ncreate table if not exists tig_stats_log (\n  lid            serial,\n  ts             TIMESTAMP                 DEFAULT CURRENT_TIMESTAMP,\n  hostname       varchar(2049)    NOT NULL,\n  cpu_usage      double precision not null default 0,\n  mem_usage      double precision not null default 0,\n  uptime         bigint           not null default 0,\n  vhosts         int              not null default 0,\n  sm_packets     bigint           not null default 0,\n  muc_packets    bigint           not null default 0,\n  pubsub_packets bigint           not null default 0,\n  c2s_packets    bigint           not null default 0,\n  s2s_packets    bigint           not null default 0,\n  ext_packets    bigint           not null default 0,\n  presences      bigint           not null default 0,\n  messages       bigint           not null default 0,\n  iqs            bigint           not null default 0,\n  registered     bigint           not null default 0,\n  c2s_conns      int              not null default 0,\n  s2s_conns      int              not null default 0,\n  bosh_conns     int              not null default 0,\n  primary key (ts, hostname(255))\n);\n-- QUERY END:\n\n-- QUERY START:\ncall TigExecuteIfNot(\n    (select count(1) from information_schema.COLUMNS where TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'tig_stats_log' AND COLUMN_NAME = 'ws2s_conns'),\n    \"ALTER TABLE tig_stats_log ADD `ws2s_conns` INT not null default 0\"\n);\n-- QUERY END:\n\n-- QUERY START:\ncall TigExecuteIfNot(\n    (select count(1) from information_schema.COLUMNS where TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'tig_stats_log' AND COLUMN_NAME = 'ws2s_packets'),\n    \"ALTER TABLE tig_stats_log ADD `ws2s_packets` bigint not null default 0\"\n);\n-- QUERY END:\n\n-- QUERY START:\ncall TigExecuteIfNot(\n    (select count(1) from information_schema.COLUMNS where TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'tig_stats_log' AND COLUMN_NAME = 'sm_sessions'),\n    \"ALTER TABLE tig_stats_log ADD `sm_sessions` bigint not null default 0\"\n);\n-- QUERY END:\n\n-- QUERY START:\ncall TigExecuteIfNot(\n    (select count(1) from information_schema.COLUMNS where TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'tig_stats_log' AND COLUMN_NAME = 'sm_connections'),\n    \"ALTER TABLE tig_stats_log ADD `sm_connections` bigint not null default 0\"\n);\n-- QUERY END:\n"
  },
  {
    "path": "src/main/database/mysql-installer-create-db.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START: create database\ncreate database IF NOT EXISTS ${dbName};\n-- QUERY END: create database\n\n-- QUERY START: add user\nCREATE USER IF NOT EXISTS '${dbUser}'@'%' IDENTIFIED BY '${dbPass}';\n-- QUERY END: add user\n\n-- QUERY START: add user\nCREATE USER IF NOT EXISTS '${dbUser}'@'localhost' IDENTIFIED BY '${dbPass}';\n-- QUERY END: add user\n\n-- QUERY START: add user\nCREATE USER IF NOT EXISTS '${dbUser}' IDENTIFIED BY '${dbPass}';\n-- QUERY END: user\n\n-- QUERY START: add user\nGRANT ALL ON ${dbName}.* TO '${dbUser}'@'%';\n-- QUERY END: add user\n\n-- QUERY START: add user\nGRANT ALL ON ${dbName}.* TO '${dbUser}'@'localhost';\n-- QUERY END: add user\n\n-- QUERY START: add user\nGRANT ALL ON ${dbName}.* TO '${dbUser}';\n-- QUERY END: user\n\n-- QUERY START: flush privileges\nFLUSH PRIVILEGES;\n-- QUERY END: flush privileges\n\n"
  },
  {
    "path": "src/main/database/mysql-installer-post.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n"
  },
  {
    "path": "src/main/database/mysql-server-8.0.0-props.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\nselect NOW(), ' - Setting schema version to 8.0.0';\n\n-- QUERY START:\ncall TigSetComponentVersion('server', '8.0.0');\n-- QUERY END:\n"
  },
  {
    "path": "src/main/database/mysql-server-8.0.0-schema.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\ncreate table if not exists tig_users (\n\tuid bigint unsigned NOT NULL auto_increment,\n\n\t-- Jabber User ID\n\tuser_id varchar(2049) NOT NULL,\n\t-- UserID SHA1 hash to prevent duplicate user_ids\n\tsha1_user_id char(128) NOT NULL,\n\t-- User password encrypted or not\n    -- DEPRECATED\n\tuser_pw varchar(255) default NULL,\n\t-- Time the account has been created\n\tacc_create_time timestamp DEFAULT CURRENT_TIMESTAMP,\n\t-- Time of the last user login\n    -- DEPRECATED\n\tlast_login timestamp NULL DEFAULT NULL,\n\t-- Time of the last user logout\n    -- DEPRECATED\n\tlast_logout timestamp NULL DEFAULT NULL,\n\t-- User online status, if > 0 then user is online, the value\n\t-- indicates the number of user connections.\n\t-- It is incremented on each user login and decremented on each\n\t-- user logout.\n    -- DEPRECATED\n\tonline_status int default 0,\n\t-- Number of failed login attempts\n\tfailed_logins int default 0,\n\t-- User status, whether the account is active or disabled\n\t-- >0 - account active, 0 - account disabled\n\taccount_status int default 1,\n\n\tprimary key (uid),\n\tunique key sha1_user_id (sha1_user_id),\n\tkey user_pw (user_pw),\n    key part_of_user_id (user_id(255)),\n\tkey last_login (last_login),\n\tkey last_logout (last_logout),\n\tkey account_status (account_status),\n\tkey online_status (online_status)\n)\nENGINE=InnoDB default character set utf8 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8;\n-- QUERY END:\n\n-- QUERY START:\ncreate table if not exists tig_nodes (\n       nid bigint unsigned NOT NULL auto_increment,\n       parent_nid bigint unsigned,\n       uid bigint unsigned NOT NULL,\n\n       node varchar(255) NOT NULL,\n\n       primary key (nid),\n       unique key tnode (parent_nid, uid, node),\n       key node (node),\n\t\t\t key uid (uid),\n\t\t\t key parent_nid (parent_nid),\n\t\t\t constraint tig_nodes_constr foreign key (uid) references tig_users (uid)\n)\nENGINE=InnoDB default character set utf8 ROW_FORMAT=DYNAMIC;\n-- QUERY END:\n\n-- QUERY START:\ncreate table if not exists tig_pairs (\n       pid INT NOT NULL AUTO_INCREMENT PRIMARY KEY,\n       nid bigint unsigned,\n       uid bigint unsigned NOT NULL,\n\n       pkey varchar(255) NOT NULL,\n       pval mediumtext character set utf8mb4 collate utf8mb4_unicode_ci,\n\n       key pkey (pkey),\n\t\t\t key uid (uid),\n\t\t\t key nid (nid),\n\t\t\t constraint tig_pairs_constr_1 foreign key (uid) references tig_users (uid),\n\t\t\t constraint tig_pairs_constr_2 foreign key (nid) references tig_nodes (nid)\n)\nENGINE=InnoDB default character set utf8 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8;\n-- QUERY END:\n\n-- QUERY START:\ncreate table if not exists tig_offline_messages (\n    msg_id bigint unsigned not null auto_increment,\n    ts timestamp(6) default current_timestamp(6),\n    expired timestamp null default null,\n    sender varchar(2049),\n    sender_sha1 char(128),\n    receiver varchar(2049) not null,\n    receiver_sha1 char(128) not null,\n    msg_type int not null default 0,\n    message mediumtext character set utf8mb4 collate utf8mb4_unicode_ci not null,\n    primary key (msg_id),\n    key tig_offline_messages_expired_index (expired),\n    key tig_offline_messages_receiver_sha1_index (receiver_sha1),\n    key tig_offline_messages_receiver_sha1_sender_sha1_index (receiver_sha1, sender_sha1)\n) ENGINE=InnoDB default character set utf8 ROW_FORMAT=DYNAMIC;\n-- QUERY END:\n\n-- QUERY START:\ncreate table if not exists tig_broadcast_messages (\n    id varchar(128) not null,\n    expired timestamp(6) not null,\n    msg mediumtext character set utf8mb4 collate utf8mb4_unicode_ci not null,\n    primary key (id)\n    );\n-- QUERY END:\n\n-- QUERY START:\ncreate table if not exists tig_broadcast_jids (\n    jid_id bigint unsigned not null auto_increment,\n    jid varchar(2049) not null,\n    jid_sha1 char(128) not null,\n\n    primary key (jid_id),\n    key tig_broadcast_jids_jid_sha1 (jid_sha1)\n);\n-- QUERY END:\n\n-- QUERY START:\ncreate table if not exists tig_broadcast_recipients (\n    msg_id varchar(128) not null references tig_broadcast_messages(id),\n    jid_id bigint not null references tig_broadcast_jids(jid_id),\n    primary key (msg_id, jid_id)\n);\n-- QUERY END:\n\n-- ------------ Clustering support\n\n-- QUERY START:\ncreate table if not exists tig_cluster_nodes (\n    hostname varchar(255) not null,\n    secondary varchar(512),\n    password varchar(255),\n    last_update timestamp(6) default current_timestamp(6) on update current_timestamp(6),\n    port int,\n    cpu_usage double precision unsigned not null,\n    mem_usage double precision unsigned not null,\n    primary key (hostname)\n) ENGINE=InnoDB default character set utf8 ROW_FORMAT=DYNAMIC;\n-- QUERY END:\n\n\n-- ------------- Credentials support\n-- QUERY START:\ncreate table if not exists tig_user_credentials (\n    uid bigint unsigned not null,\n    username varchar(2049) not null,\n    username_sha1 char(128) not null,\n    mechanism varchar(128) not null,\n    value mediumtext not null,\n\n    primary key (uid, username_sha1, mechanism),\n    constraint tig_credentials_uid foreign key (uid) references tig_users (uid)\n) ENGINE=InnoDB default character set utf8 ROW_FORMAT=DYNAMIC;\n-- QUERY END:"
  },
  {
    "path": "src/main/database/mysql-server-8.0.0-sp.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\ndrop procedure if exists TigInitdb;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigAddUser;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigGetUserDBUid;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigUpdatePassword;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigUpdatePasswordPlainPwRev;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigUserLogin;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigUserLogout;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigOnlineUsers;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigOfflineUsers;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigAllUsers;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigAllUsersCount;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigDisableAccount;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigEnableAccount;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigActiveAccounts;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigDisabledAccounts;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigAddNode;\n-- QUERY END:\n-- QUERY START:\ndrop function if exists TigGetDBProperty;\n-- QUERY END:\n\ndelimiter //\n\n-- QUERY START:\n-- DEPRECATED | Database properties get - function\ncreate function TigGetDBProperty(_tkey varchar(255) CHARSET utf8) returns mediumtext CHARSET utf8\nREADS SQL DATA\nbegin\n\tdeclare _result mediumtext CHARSET utf8;\n\n\tselect pval into _result from tig_pairs, tig_users\n\t\twhere (pkey = _tkey) AND (sha1_user_id = sha1(lower('db-properties')))\n\t\t\t\t\tAND (tig_pairs.uid = tig_users.uid);\n\n\treturn (_result);\nend //\n-- QUERY END:\n\n\n-- QUERY START:\n-- DEPRECATED | The initialization of the database.\n-- The procedure should be called manually somehow before starting the\n-- server. In theory the server could call the procedure automatically\n-- at the startup time but I don't know yet how to solve the problem\n-- with multiple cluster nodes starting at later time when the server\n-- is already running.\ncreate procedure TigInitdb()\nbegin\n  update tig_users set online_status = 0;\nend //\n-- QUERY END:\n\n-- QUERY START:\n-- Add a new user to the database assuming the user password is already\n-- encoded properly according to the database settings.\n-- If password is not encoded TigAddUserPlainPw should be used instead.\ncreate procedure TigAddUser(_user_id varchar(2049) CHARSET utf8, _user_pw varchar(255) CHARSET utf8)\nbegin\n\tdeclare res_uid bigint unsigned;\n\n\tinsert into tig_users (user_id, sha1_user_id, user_pw)\n\t\tvalues (_user_id, sha1(lower(_user_id)), _user_pw);\n\n\tselect LAST_INSERT_ID() into res_uid;\n\n\tinsert into tig_nodes (parent_nid, uid, node)\n\t\tvalues (NULL, res_uid, 'root');\n\n\tif _user_pw is NULL then\n\t\tupdate tig_users set account_status = -1 where uid = res_uid;\n\tend if;\n\n\tselect res_uid as uid;\nend //\n-- QUERY END:\n\n-- QUERY START:\n-- Low level database user id as big number. Used only for performance reasons\n-- and save database space. Besides JID is too large to server as UID\ncreate procedure TigGetUserDBUid(_user_id varchar(2049) CHARSET utf8)\nbegin\n\tselect uid from tig_users where sha1_user_id = sha1(lower(_user_id));\nend //\n-- QUERY END:\n\n-- QUERY START:\n-- Variant of TigUpdatePasswordPlainPw SP with parameters in reverse order.\n-- Some implementations require the parameters to be in the same order as\n-- the update query.\ncreate procedure TigUpdatePasswordPlainPwRev(_user_pw varchar(255) CHARSET utf8, _user_id varchar(2049) CHARSET utf8)\nbegin\n\tcall TigUpdatePasswordPlainPw(_user_id, _user_pw);\nend //\n-- QUERY END:\n\n-- QUERY START:\n-- Update user password\ncreate procedure TigUpdatePassword(_user_id varchar(2049) CHARSET utf8, _user_pw varchar(255) CHARSET utf8)\nbegin\n\tupdate tig_users set user_pw = _user_pw where sha1_user_id = sha1(lower(_user_id));\nend //\n-- QUERY END:\n\n-- QUERY START:\n-- DEPRECATED | List all online users\ncreate procedure TigOnlineUsers()\nbegin\n\tselect user_id, last_login, last_logout, online_status, failed_logins, account_status\n\t\tfrom tig_users where online_status > 0;\nend //\n-- QUERY END:\n\n-- QUERY START:\n-- DEPRECATED | List all offline users\ncreate procedure TigOfflineUsers()\nbegin\n\tselect user_id, last_login, last_logout, online_status, failed_logins, account_status\n\t\tfrom tig_users where online_status = 0;\nend //\n-- QUERY END:\n\n-- QUERY START:\n-- List of all users in database\ncreate procedure TigAllUsers()\nbegin\n\tselect user_id, last_login, last_logout, online_status, failed_logins, account_status\n\t\tfrom tig_users;\nend //\n-- QUERY END:\n\n-- QUERY START:\n-- All users count\ncreate procedure TigAllUsersCount()\nbegin\n\tselect count(*) from tig_users;\nend //\n-- QUERY END:\n\n\n-- QUERY START:\n-- DEPRECATED | Perforrm user login. It returns user_id uppon success and NULL\n-- on failure.\n-- If the login is successful it also increases online_status and sets\n-- last_login time to the current timestamp\ncreate procedure TigUserLogin(_user_id varchar(2049) CHARSET utf8, _user_pw varchar(255) CHARSET utf8)\nbegin\n\tif exists(select 1 from tig_users\n\t\twhere (account_status > 0) AND (sha1_user_id = sha1(lower(_user_id))) AND (user_pw = _user_pw) AND (user_id = _user_id))\n\tthen\n\t\tupdate tig_users\n\t\t\tset online_status = online_status + 1, last_login = CURRENT_TIMESTAMP\n\t\t\twhere sha1_user_id = sha1(lower(_user_id));\n\t\tselect _user_id as user_id;\n\telse\n\t\tupdate tig_users set failed_logins = failed_logins + 1 where sha1_user_id = sha1(lower(_user_id));\n\t\tselect NULL as user_id;\n\tend if;\nend //\n-- QUERY END:\n\n-- QUERY START:\n-- It decreases online_status and sets last_logout time to the current timestamp\ncreate procedure TigUserLogout(_user_id varchar(2049) CHARSET utf8)\nbegin\n\tupdate tig_users\n\t\tset online_status = greatest(online_status - 1, 0),\n\t\t\tlast_logout = CURRENT_TIMESTAMP\n\t\twhere sha1_user_id = sha1(lower(_user_id));\nend //\n-- QUERY END:\n\n-- QUERY START:\n-- Disable user account\ncreate procedure TigDisableAccount(_user_id varchar(2049) CHARSET utf8)\nbegin\n\tupdate tig_users set account_status = 0 where sha1_user_id = sha1(lower(_user_id));\nend //\n-- QUERY END:\n\n-- QUERY START:\n-- Enable user account\ncreate procedure TigEnableAccount(_user_id varchar(2049) CHARSET utf8)\nbegin\n\tupdate tig_users set account_status = 1 where sha1_user_id = sha1(lower(_user_id));\nend //\n-- QUERY END:\n\n-- QUERY START:\n-- Get list of all active user accounts\ncreate procedure TigActiveAccounts()\nbegin\n\tselect user_id, last_login, last_logout, online_status, failed_logins, account_status\n\t\tfrom tig_users where account_status > 0;\nend //\n-- QUERY END:\n\n-- QUERY START:\n-- Get list of all disabled user accounts\ncreate procedure TigDisabledAccounts()\nbegin\n\tselect user_id, last_login, last_logout, online_status, failed_logins, account_status\n\t\tfrom tig_users where account_status = 0;\nend //\n-- QUERY END:\n\n-- QUERY START:\n-- Helper procedure for adding a new node\ncreate procedure TigAddNode(_parent_nid bigint, _uid bigint, _node varchar(255) CHARSET utf8)\nbegin\n  if exists(SELECT 1 FROM tig_nodes WHERE parent_nid = _parent_nid AND uid = _uid AND node = _node)  then\n    SELECT nid FROM tig_nodes WHERE parent_nid = _parent_nid AND uid = _uid AND node = _node;\n  ELSEIF exists(SELECT 1 FROM tig_nodes WHERE _parent_nid is null AND uid = _uid AND 'root' = _node)  then\n    SELECT nid FROM tig_nodes WHERE uid = _uid AND node = _node;\n  ELSE\n\tinsert into tig_nodes (parent_nid, uid, node) values (_parent_nid, _uid, _node);\n\tselect LAST_INSERT_ID() as nid;\n  END IF;\nend //\n-- QUERY END:\n\ndelimiter ;\n\n-- QUERY START:\ndrop procedure if exists TigUpdateLoginTime;\n-- QUERY END:\n\ndelimiter //\n\n-- QUERY START:\n-- It sets last_login time to the current timestamp\ncreate procedure TigUpdateLoginTime(_user_id varchar(2049) charset utf8mb4 )\nbegin\n\tupdate tig_users\n\t\tset last_login = CURRENT_TIMESTAMP\n\t\twhere sha1_user_id = sha1(lower(_user_id));\nend //\n-- QUERY END:\n\ndelimiter ;\n\n-- QUERY START:\ndrop procedure if exists TigUpdatePairs;\n-- QUERY END:\n\ndelimiter //\n\n-- QUERY START:\n-- Procedure to efficiently and safely update data in tig_pairs table\ncreate procedure TigUpdatePairs(_nid bigint, _uid bigint, _tkey varchar(255) CHARSET utf8, _tval mediumtext CHARSET utf8mb4)\nbegin\n  if exists(SELECT 1 FROM tig_pairs WHERE nid = _nid AND uid = _uid AND pkey = _tkey)\n  then\n    UPDATE tig_pairs SET pval = _tval WHERE nid = _nid AND uid = _uid AND pkey = _tkey;\n  ELSE\n    INSERT INTO tig_pairs (nid, uid, pkey, pval) VALUES (_nid, _uid, _tkey, _tval);\n  END IF;\nend //\n-- QUERY END:\n\n\ndelimiter ;\n\n-- QUERY START:\ndrop procedure if exists Tig_OfflineMessages_AddMessage;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists Tig_OfflineMessages_GetMessages;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists Tig_OfflineMessages_GetMessagesByIds;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists Tig_OfflineMessages_GetMessagesCount;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists Tig_OfflineMessages_ListMessages;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists Tig_OfflineMessages_DeleteMessages;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists Tig_OfflineMessages_DeleteMessagesByIds;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists Tig_OfflineMessages_DeleteMessage;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists Tig_OfflineMessages_GetExpiredMessages;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists Tig_OfflineMessages_GetExpiredMessagesBefore;\n-- QUERY END:\n\ndelimiter //\n\n-- QUERY START:\ncreate procedure Tig_OfflineMessages_AddMessage(_to varchar(2049) charset utf8, _from varchar(2049) charset utf8, _type int, _ts timestamp(6), _message mediumtext charset utf8mb4, _expired timestamp(6), _limit bigint)\nbegin\n    declare msg_count bigint;\n    set msg_count = 0;\n\n    if _limit > 0  then\n        select count(msg_id) into msg_count from tig_offline_messages where receiver_sha1 = sha1(lower(_to)) and sender_sha1 = sha1(lower(_from));\n    end if;\n\n    if _limit = 0 or _limit > msg_count then\n\t    insert into tig_offline_messages ( receiver, receiver_sha1, sender, sender_sha1, msg_type, ts, message, expired )\n\t        values ( _to, sha1(lower(_to)), _from, sha1(lower(_from)), _type, _ts, _message, _expired );\n\n\t    select last_insert_id() as msg_id;\n\telse\n\t    select null as msg_id;\n\tend if;\nend //\n-- QUERY END:\n\n-- QUERY START:\ncreate procedure Tig_OfflineMessages_GetMessages(_to varchar(2049) charset utf8)\nbegin\n    select message, msg_id\n    from tig_offline_messages\n    where receiver_sha1 = sha1(lower(_to));\nend //\n-- QUERY END:\n\n-- QUERY START:\ncreate procedure Tig_OfflineMessages_GetMessagesByIds(_to varchar(2049) charset utf8, _msg_id1 varchar(50) charset utf8, _msg_id2 varchar(50) charset utf8, _msg_id3  varchar(50) charset utf8, _msg_id4 varchar(50) charset utf8)\nbegin\n    select message, msg_id\n    from tig_offline_messages\n    where receiver_sha1 = sha1(lower(_to))\n        and (\n            (_msg_id1 is not null and msg_id = _msg_id1)\n            or (_msg_id2 is not null and msg_id = _msg_id2)\n            or (_msg_id3 is not null and msg_id = _msg_id3)\n            or (_msg_id4 is not null and msg_id = _msg_id4)\n        );\nend //\n-- QUERY END:\n\n-- QUERY START:\ncreate procedure Tig_OfflineMessages_GetMessagesCount(_to varchar(2049) charset utf8)\nbegin\n    select msg_type , count(msg_type)\n    from tig_offline_messages\n    where receiver_sha1 = sha1(lower(_to))\n    group by msg_type;\nend //\n-- QUERY END:\n\n-- QUERY START:\ncreate procedure Tig_OfflineMessages_ListMessages(_to varchar(2049) charset utf8)\nbegin\n    select msg_id, msg_type, sender\n    from tig_offline_messages\n    where receiver_sha1 = sha1(lower(_to));\nend //\n-- QUERY END:\n\n-- QUERY START:\ncreate procedure Tig_OfflineMessages_DeleteMessages(_to varchar(2049) charset utf8)\nbegin\n    delete from tig_offline_messages where receiver_sha1 = sha1(lower(_to));\n    select row_count() as deleted_rows;\nend //\n-- QUERY END:\n\n-- QUERY START:\ncreate procedure Tig_OfflineMessages_DeleteMessagesByIds(_to varchar(2049) charset utf8, _msg_id1 varchar(50) charset utf8, _msg_id2 varchar(50) charset utf8, _msg_id3  varchar(50) charset utf8, _msg_id4 varchar(50) charset utf8)\nbegin\n    delete from tig_offline_messages\n    where receiver_sha1 = sha1(lower(_to))\n        and (\n            (_msg_id1 is not null and msg_id = _msg_id1)\n            or (_msg_id2 is not null and msg_id = _msg_id2)\n            or (_msg_id3 is not null and msg_id = _msg_id3)\n            or (_msg_id4 is not null and msg_id = _msg_id4)\n        );\n    select row_count() as deleted_rows;\nend //\n-- QUERY END:\n\n-- QUERY START:\ncreate procedure Tig_OfflineMessages_DeleteMessage(_msg_id bigint)\nbegin\n    delete from tig_offline_messages where msg_id = _msg_id;\n    select row_count() as deleted_rows;\nend //\n-- QUERY END:\n\n-- QUERY START:\ncreate procedure Tig_OfflineMessages_GetExpiredMessages(_limit int)\nbegin\n    select msg_id, expired, message\n    from tig_offline_messages\n    where expired is not null\n    order by expired asc\n    limit _limit;\nend //\n-- QUERY END:\n\n-- QUERY START:\ncreate procedure Tig_OfflineMessages_GetExpiredMessagesBefore(_expired timestamp(6))\nbegin\n    select msg_id, expired, message\n    from tig_offline_messages\n    where expired is not null\n        and (_expired is null or expired <= _expired)\n    order by expired asc;\nend //\n-- QUERY END:\n\ndelimiter ;\n\n-- ------------\n\n-- QUERY START:\ndrop procedure if exists TigUpdateAccountStatus;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigAccountStatus;\n-- QUERY END:\n\ndelimiter //\n\n-- QUERY START:\n-- Set user account status\ncreate procedure TigUpdateAccountStatus(_user_id varchar(2049) CHARSET utf8, _status INT)\n    begin\n        update tig_users set account_status = _status where sha1_user_id = sha1(lower(_user_id));\n    end //\n-- QUERY END:\n\n-- QUERY START:\n-- Get user account status\ncreate procedure TigAccountStatus(_user_id varchar(2049) CHARSET utf8)\n    begin\n        select account_status from tig_users where sha1_user_id = sha1(lower(_user_id));\n    end //\n-- QUERY END:\n\ndelimiter ;\n\n-- ------------ Broadcast Messages\n\n-- QUERY START:\ndrop procedure if exists Tig_BroadcastMessages_AddMessage;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists Tig_BroadcastMessages_AddMessageRecipient;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists Tig_BroadcastMessages_GetMessages;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists Tig_BroadcastMessages_GetMessageRecipients;\n-- QUERY END:\n\ndelimiter //\n\n-- QUERY START:\ncreate procedure Tig_BroadcastMessages_AddMessage(_msg_id varchar(128), _expired timestamp, _msg mediumtext charset utf8mb4)\nbegin\n    start transaction;\n        insert into tig_broadcast_messages (id, expired, msg)\n            values (_msg_id, _expired, _msg)\n            on duplicate key update expired = expired;\n    commit;\nend //\n-- QUERY END:\n\n-- QUERY START:\ncreate procedure Tig_BroadcastMessages_AddMessageRecipient(_msg_id varchar(128), _jid varchar(2049))\nbegin\n    declare _jid_id bigint;\n    declare _jid_sha1 char(128);\n\n    start transaction;\n        select jid_id into _jid_id from tig_broadcast_jids where jid_sha1 = sha1(lower(_jid));\n        if _jid_id is null then\n            insert into tig_broadcast_jids (jid, jid_sha1)\n                values (_jid, sha1(lower(_jid)))\n                on duplicate key update jid_id = LAST_INSERTED_ID(jid_id);\n            select LAST_INSERTED_ID() into _jid_id;\n        end if;\n\n        insert into tig_broadcast_recipients (msg_id, jid_id)\n            values (_msg_id, _jid_id)\n            on duplicate key update jid_id = jid_id;\n    commit;\nend //\n-- QUERY END:\n\n-- QUERY START:\ncreate procedure Tig_BroadcastMessages_GetMessages(_expired timestamp(6))\nbegin\n    select id, expired, msg\n    from tig_broadcast_messages\n    where expired >= _expired;\nend //\n-- QUERY END:\n\n-- QUERY START:\ncreate procedure Tig_BroadcastMessages_GetMessageRecipients(_msg_id varchar(128))\nbegin\n    select j.jid\n    from tig_broadcast_recipients r\n    inner join tig_broadcast_jids j on j.jid_id = r.jid_id\n    where r.msg_id = _msg_id;\nend //\n-- QUERY END:\n\ndelimiter ;\n\n-- ------------- Credentials support\n\n-- QUERY START:\ndrop procedure if exists TigUserCredential_Update;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigUserCredentials_Get;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigUserCredential_Remove;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigUserLoginPlainPw;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigAddUserPlainPw;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigGetPassword;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigUpdatePasswordPlainPw;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigRemoveUser;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigUserUsernames_Get;\n-- QUERY END:\n\ndelimiter //\n\n-- QUERY START:\ncreate procedure TigUserUsernames_Get(_user_id varchar(2049) CHARSET utf8)\n  begin\n    declare _user_id_sha1 char(128);\n\n    select sha1(lower(_user_id))\n    into _user_id_sha1;\n\n    select distinct\n      c.username\n    from tig_users u\n      inner join tig_user_credentials c on u.uid = c.uid\n    where\n      u.sha1_user_id = _user_id_sha1;\n  end\n-- QUERY END:\n\n-- QUERY START:\ncreate procedure TigUserCredential_Update(_user_id varchar(2049) CHARSET utf8, _username varchar(2049) CHARSET utf8, _mechanism varchar(128) CHARSET utf8, _value mediumtext CHARSET utf8)\nbegin\n    declare _uid bigint;\n    declare _user_id_sha1 char(128);\n    declare _username_sha1 char(128);\n\n    select uid into _uid from tig_users where sha1_user_id = sha1(lower(_user_id));\n\n    if _uid is not null then\n        start transaction;\n            insert into tig_user_credentials (uid, username, username_sha1, mechanism, value)\n                values (_uid, _username, sha1(_username), _mechanism, _value)\n                on duplicate key update value = _value;\n        commit;\n    end if;\nend //\n-- QUERY END:\n\n-- QUERY START:\ncreate procedure TigUserCredentials_Get(_user_id varchar(2049) CHARSET utf8, _username varchar(2049) CHARSET utf8)\nbegin\n    declare _user_id_sha1 char(128);\n\n    select mechanism, value, account_status\n    from tig_users u\n    inner join tig_user_credentials c on u.uid = c.uid\n    where\n        u.sha1_user_id = sha1(lower(_user_id))\n        and c.username_sha1 = sha1(_username);\nend //\n-- QUERY END:\n\n-- QUERY START:\ncreate procedure TigUserCredential_Remove(_user_id varchar(2049) CHARSET utf8, _username varchar(2049) CHARSET utf8)\nbegin\n    declare _uid bigint;\n    declare _user_id_sha1 char(128);\n\n    select uid into _uid from tig_users where sha1_user_id = sha1(lower(_user_id));\n\n    if _uid is not null then\n        start transaction;\n            delete from tig_user_credentials where uid = _uid and username_sha1 = sha1(_username);\n        commit;\n    end if;\nend //\n-- QUERY END:\n\n-- QUERY START:\n-- Takes plain text user password and converts it to internal representation\n-- and creates a new user account.\ncreate procedure TigAddUserPlainPw(_user_id varchar(2049) CHARSET utf8, _user_pw varchar(255) CHARSET utf8)\nbegin\n\tdeclare res_uid bigint unsigned;\n\n\tinsert into tig_users (user_id, sha1_user_id)\n\t\tvalues (_user_id, sha1(lower(_user_id)));\n\n\tselect LAST_INSERT_ID() into res_uid;\n\n\tinsert into tig_nodes (parent_nid, uid, node)\n\t\tvalues (NULL, res_uid, 'root');\n\n\tif _user_pw is NULL then\n\t\tupdate tig_users set account_status = -1 where uid = res_uid;\n    else\n        call TigUpdatePasswordPlainPw(_user_id, _user_pw);\n   \tend if;\n\n\tselect res_uid as uid;\nend //\n-- QUERY END:\n\n-- QUERY START:\n-- Returns user's password from the database\ncreate procedure TigGetPassword(_user_id varchar(2049) CHARSET utf8)\nbegin\n\tselect c.value\n\tfrom tig_users u\n\tinner join tig_user_credentials c on c.uid = u.uid\n\twhere\n\t    u.sha1_user_id = sha1(lower(_user_id))\n\t    and c.mechanism = 'PLAIN'\n\t    and c.username_sha1 = sha1('default');\nend //\n-- QUERY END:\n\n-- QUERY START:\ncreate procedure TigUpdatePasswordPlainPw(_user_id varchar(2049) CHARSET utf8, _user_pw varchar(255) CHARSET utf8)\nbegin\n    declare _passwordEncoding varchar(128) CHARSET utf8;\n    declare _encodedPassword varchar(255) CHARSET utf8;\n\n    select IFNULL(TigGetDBProperty('password-encoding'), 'PLAIN') into _passwordEncoding;\n    select case _passwordEncoding\n\t\twhen 'MD5-PASSWORD' then\n\t\t\tMD5(_user_pw)\n\t\twhen 'MD5-USERID-PASSWORD' then\n\t\t\tMD5(CONCAT(_user_id, _user_pw))\n\t\twhen 'MD5-USERNAME-PASSWORD' then\n\t\t\tMD5(CONCAT(substring_index(_user_id, '@', 1), _user_pw))\n\t\telse\n\t\t\t_user_pw\n\t\tend into _encodedPassword;\n\n    call TigUserCredential_Update(_user_id, 'default', _passwordEncoding, _encodedPassword);\nend //\n-- QUERY END:\n\n-- QUERY START:\n-- Performs user login for a plain text password, converting it to an internal\n-- representation if necessary\ncreate procedure TigUserLoginPlainPw(_user_id varchar(2049) CHARSET utf8, _user_pw varchar(255) CHARSET utf8)\nbegin\n    declare _passwordEncoding varchar(128) CHARSET utf8;\n    declare _encodedPassword varchar(255) CHARSET utf8;\n\n    select IFNULL(TigGetDBProperty('password-encoding'), 'PLAIN') into _passwordEncoding;\n    select case _passwordEncoding\n\t\twhen 'MD5-PASSWORD' then\n\t\t\tMD5(_user_pw)\n\t\twhen 'MD5-USERID-PASSWORD' then\n\t\t\tMD5(CONCAT(_user_id, _user_pw))\n\t\twhen 'MD5-USERNAME-PASSWORD' then\n\t\t\tMD5(CONCAT(substring_index(_user_id, '@', 1), _user_pw))\n\t\telse\n\t\t\t_user_pw\n\t\tend into _encodedPassword;\n\n\tif exists(select 1 from tig_users u\n\t    inner join tig_user_credentials c on c.uid = u.uid\n\t    where (u.account_status > 0) AND (u.sha1_user_id = sha1(lower(_user_id))) AND (u.user_id = _user_id)\n\t        AND (c.username_sha1 = sha1('default'))\n\t        AND (c.mechanism = _passwordEncoding) AND (c.value = _encodedPassword))\n\tthen\n\t\tupdate tig_users\n\t\t\tset online_status = online_status + 1, last_login = CURRENT_TIMESTAMP\n\t\t\twhere sha1_user_id = sha1(lower(_user_id));\n\t\tselect _user_id as user_id;\n\telse\n\t\tupdate tig_users set failed_logins = failed_logins + 1 where sha1_user_id = sha1(lower(_user_id));\n\t\tselect NULL as user_id;\n    end if;\nend //\n-- QUERY END:\n\n\n-- Removes a user from the database\n-- QUERY START:\ncreate procedure TigRemoveUser(_user_id varchar(2049) CHARSET utf8)\nbegin\n\tdeclare res_uid bigint unsigned;\n\n\tselect uid into res_uid from tig_users where sha1_user_id = sha1(lower(_user_id));\n\n    delete from tig_user_credentials where uid = res_uid;\n\tdelete from tig_pairs where uid = res_uid;\n\tdelete from tig_nodes where uid = res_uid;\n\tdelete from tig_users where uid = res_uid;\nend //\n-- QUERY END:\n\ndelimiter ;\n"
  },
  {
    "path": "src/main/database/mysql-server-8.0.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\nsource database/mysql-server-8.0.0-schema.sql;\n\nsource database/mysql-server-8.0.0-sp.sql;\n\nsource database/mysql-server-8.0.0-props.sql;\n\n-- LOAD FILE: database/mysql-server-8.0.0-schema.sql;\n\n-- LOAD FILE: database/mysql-server-8.0.0-sp.sql;\n\n-- LOAD FILE: database/mysql-server-8.0.0-props.sql;\n"
  },
  {
    "path": "src/main/database/mysql-server-8.1.0-props.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\nselect NOW(), ' - Setting schema version to 8.1.0';\n\n-- QUERY START:\ncall TigSetComponentVersion('server', '8.1.0');\n-- QUERY END:\n"
  },
  {
    "path": "src/main/database/mysql-server-8.1.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\nsource database/mysql-server-8.1.0-props.sql;\n\n-- LOAD FILE: database/mysql-server-8.1.0-props.sql;\n\n--\n"
  },
  {
    "path": "src/main/database/mysql-server-8.2.0-props.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\nselect NOW(), ' - Setting schema version to 8.2.0';\n\n-- QUERY START:\ncall TigSetComponentVersion('server', '8.2.0');\n-- QUERY END:\n"
  },
  {
    "path": "src/main/database/mysql-server-8.2.0-schema.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\ndrop procedure if exists TigServerUpgrade;\n-- QUERY END:\n\ndelimiter //\n\n-- QUERY START:\ncreate procedure TigServerUpgrade()\nbegin\n    if exists (select 1 from information_schema.columns where table_schema = database() and table_name = 'tig_users' and column_name = 'online_status') then\n        alter table tig_users\n            drop column online_status,\n            drop column last_logout,\n            change column last_login last_used timestamp null default null;\n    end if;\nend //\n-- QUERY END:\n\ndelimiter ;\n\n-- QUERY START:\ncall TigServerUpgrade();\n-- QUERY END:\n\n-- QUERY START:\ndrop procedure if exists TigServerUpgrade;\n-- QUERY END:\n\ndelimiter //\n\n-- QUERY START:\ncreate procedure TigServerUpgrade()\nbegin\n    if exists(SELECT 1 FROM information_schema.statistics s1 WHERE s1.table_schema = database() AND s1.table_name = 'tig_users' AND s1.index_name = 'online_status') then\n        drop index online_status on tig_users;\n    end if;\nend //\n-- QUERY END:\n\ndelimiter ;\n\n-- QUERY START:\ncall TigServerUpgrade();\n-- QUERY END:\n\n-- QUERY START:\ndrop procedure if exists TigServerUpgrade;\n-- QUERY END:\n\ndelimiter //\n\n-- QUERY START:\ncreate procedure TigServerUpgrade()\nbegin\n    if exists(SELECT 1 FROM information_schema.statistics s1 WHERE s1.table_schema = database() AND s1.table_name = 'tig_users' AND s1.index_name = 'last_login') then\n        drop index last_login on tig_users;\n    end if;\nend //\n-- QUERY END:\n\ndelimiter ;\n\n-- QUERY START:\ncall TigServerUpgrade();\n-- QUERY END:\n\n-- QUERY START:\ndrop procedure if exists TigServerUpgrade;\n-- QUERY END:\n\ndelimiter //\n\n-- QUERY START:\ncreate procedure TigServerUpgrade()\nbegin\n    if exists(SELECT 1 FROM information_schema.statistics s1 WHERE s1.table_schema = database() AND s1.table_name = 'tig_users' AND s1.index_name = 'last_logout') then\n        drop index last_logout on tig_users;\n    end if;\nend //\n-- QUERY END:\n\ndelimiter ;\n\n-- QUERY START:\ncall TigServerUpgrade();\n-- QUERY END:\n\n-- QUERY START:\ndrop procedure if exists TigServerUpgrade;\n-- QUERY END:\n\ndelimiter //\n\n-- QUERY START:\ncreate procedure TigServerUpgrade()\nbegin\n    if exists(SELECT 1 FROM information_schema.statistics s1 WHERE s1.table_schema = database() AND s1.table_name = 'tig_offline_messages' AND s1.index_name = 'tig_offline_messages_receiver_sha1_index') then\n        drop index tig_offline_messages_receiver_sha1_index on tig_offline_messages;\n    end if;\nend //\n-- QUERY END:\n\ndelimiter ;\n\n-- QUERY START:\ncall TigServerUpgrade();\n-- QUERY END:\n\n-- QUERY START:\ndrop procedure if exists TigServerUpgrade;\n-- QUERY END:\n\ndelimiter //\n\n-- QUERY START:\ncreate procedure TigServerUpgrade()\nbegin\n    if exists(select * from information_schema.COLUMNS where TABLE_SCHEMA = database() and TABLE_NAME = 'tig_offline_messages' and COLUMN_NAME = 'sender_sha1' and CHARACTER_MAXIMUM_LENGTH = 128) and exists(select * from information_schema.COLUMNS where TABLE_SCHEMA = database() and TABLE_NAME = 'tig_offline_messages' and COLUMN_NAME = 'receiver_sha1' and CHARACTER_MAXIMUM_LENGTH = 128) then\n        alter table tig_offline_messages modify column receiver_sha1 char(40) not null, modify column sender_sha1 char(40);\n    end if;\nend //\n-- QUERY END:\n\ndelimiter ;\n\n-- QUERY START:\ncall TigServerUpgrade();\n-- QUERY END:\n\n-- QUERY START:\ndrop procedure if exists TigServerUpgrade;\n-- QUERY END:"
  },
  {
    "path": "src/main/database/mysql-server-8.2.0-sp.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- database properties are deprecated and are being removed\n\n-- QUERY START:\ndrop procedure if exists TigPutDBProperty;\n-- QUERY END:\n\n-- QUERY START:\ndrop procedure if exists TigUserLogin;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigUserLogout;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigOnlineUsers;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigOfflineUsers;\n-- QUERY END:\n\n-- QUERY START:\ndrop procedure if exists TigAllUsers;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigUserLoginPlainPw;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigUpdateLoginTime;\n-- QUERY END:\n\n-- QUERY START:\ndrop procedure if exists Tig_OfflineMessages_DeleteMessage;\n-- QUERY END:\n\n\ndelimiter //\n\n-- QUERY START:\n-- List of all users in database\ncreate procedure TigAllUsers()\nbegin\n    select user_id, failed_logins, account_status\n    from tig_users;\nend //\n-- QUERY END:\n\n-- QUERY START:\n-- It sets last_used time to the current timestamp\ncreate procedure TigUpdateLoginTime(_user_id varchar(2049) charset utf8mb4 )\nbegin\n    update tig_users\n    set last_used = CURRENT_TIMESTAMP\n    where sha1_user_id = sha1(lower(_user_id));\nend //\n-- QUERY END:\n\n-- QUERY START:\n    create procedure Tig_OfflineMessages_DeleteMessage(_msg_id bigint)\n    begin\n        delete from tig_offline_messages where msg_id = _msg_id;\n    end //\n-- QUERY END:\n\ndelimiter ;\n"
  },
  {
    "path": "src/main/database/mysql-server-8.2.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\nsource database/mysql-server-8.2.0-schema.sql;\n\nsource database/mysql-server-8.2.0-sp.sql;\n\nsource database/mysql-server-8.2.0-props.sql;\n\n-- LOAD FILE: database/mysql-server-8.2.0-schema.sql;\n\n-- LOAD FILE: database/mysql-server-8.2.0-sp.sql;\n\n-- LOAD FILE: database/mysql-server-8.2.0-props.sql;\n\n"
  },
  {
    "path": "src/main/database/mysql-server-8.3.0-props.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\nselect NOW(), ' - Setting schema version to 8.3.0';\n\n-- QUERY START:\ncall TigSetComponentVersion('server', '8.3.0');\n-- QUERY END:\n"
  },
  {
    "path": "src/main/database/mysql-server-8.3.0-schema.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n--\n"
  },
  {
    "path": "src/main/database/mysql-server-8.3.0-sp.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- database properties are deprecated and are being removed\n"
  },
  {
    "path": "src/main/database/mysql-server-8.3.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\nsource database/mysql-server-8.3.0-schema.sql;\n\nsource database/mysql-server-8.3.0-sp.sql;\n\nsource database/mysql-server-8.3.0-props.sql;\n\n-- LOAD FILE: database/mysql-server-8.3.0-schema.sql;\n\n-- LOAD FILE: database/mysql-server-8.3.0-sp.sql;\n\n-- LOAD FILE: database/mysql-server-8.3.0-props.sql;\n\n"
  },
  {
    "path": "src/main/database/mysql-server-8.4.0-props.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\nselect NOW(), ' - Setting schema version to 8.3.0';\n\n-- QUERY START:\ncall TigSetComponentVersion('server', '8.3.0');\n-- QUERY END:\n"
  },
  {
    "path": "src/main/database/mysql-server-8.4.0-schema.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n--\n"
  },
  {
    "path": "src/main/database/mysql-server-8.4.0-sp.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\ndrop procedure if exists Tig_BroadcastMessages_AddMessage;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists Tig_BroadcastMessages_AddMessageRecipient;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigUserCredential_Update;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists TigUserCredential_Remove;\n-- QUERY END:\n\ndelimiter //\n\n-- QUERY START:\ncreate procedure Tig_BroadcastMessages_AddMessage(_msg_id varchar(128), _expired timestamp, _msg mediumtext charset utf8mb4)\nbegin\n    -- DO NOT REMOVE, required for properly handle exceptions within transactions!\n    DECLARE exit handler for sqlexception\n    BEGIN\n        -- ERROR\n        ROLLBACK;\n        RESIGNAL;\n    END;\n\n    start transaction;\n        insert into tig_broadcast_messages (id, expired, msg)\n            values (_msg_id, _expired, _msg)\n            on duplicate key update expired = expired;\n    commit;\nend //\n-- QUERY END:\n\n-- QUERY START:\ncreate procedure Tig_BroadcastMessages_AddMessageRecipient(_msg_id varchar(128), _jid varchar(2049))\nbegin\n    declare _jid_id bigint;\n    declare _jid_sha1 char(128);\n    -- DO NOT REMOVE, required for properly handle exceptions within transactions!\n    DECLARE exit handler for sqlexception\n    BEGIN\n        -- ERROR\n        ROLLBACK;\n        RESIGNAL;\n    END;\n\n    start transaction;\n        select jid_id into _jid_id from tig_broadcast_jids where jid_sha1 = sha1(lower(_jid));\n        if _jid_id is null then\n            insert into tig_broadcast_jids (jid, jid_sha1)\n                values (_jid, sha1(lower(_jid)))\n                on duplicate key update jid_id = LAST_INSERTED_ID(jid_id);\n            select LAST_INSERTED_ID() into _jid_id;\n        end if;\n\n        insert into tig_broadcast_recipients (msg_id, jid_id)\n            values (_msg_id, _jid_id)\n            on duplicate key update jid_id = jid_id;\n    commit;\nend //\n-- QUERY END:\n\n-- QUERY START:\ncreate procedure TigUserCredential_Update(_user_id varchar(2049) CHARSET utf8, _username varchar(2049) CHARSET utf8, _mechanism varchar(128) CHARSET utf8, _value mediumtext CHARSET utf8)\nbegin\n    declare _uid bigint;\n    declare _user_id_sha1 char(128);\n    declare _username_sha1 char(128);\n    -- DO NOT REMOVE, required for properly handle exceptions within transactions!\n    DECLARE exit handler for sqlexception\n    BEGIN\n        -- ERROR\n        ROLLBACK;\n        RESIGNAL;\n    END;\n\n    select uid into _uid from tig_users where sha1_user_id = sha1(lower(_user_id));\n\n    if _uid is not null then\n        start transaction;\n            insert into tig_user_credentials (uid, username, username_sha1, mechanism, value)\n                values (_uid, _username, sha1(_username), _mechanism, _value)\n                on duplicate key update value = _value;\n        commit;\n    end if;\nend //\n-- QUERY END:\n\n-- QUERY START:\ncreate procedure TigUserCredential_Remove(_user_id varchar(2049) CHARSET utf8, _username varchar(2049) CHARSET utf8)\nbegin\n    declare _uid bigint;\n    declare _user_id_sha1 char(128);\n    -- DO NOT REMOVE, required for properly handle exceptions within transactions!\n    DECLARE exit handler for sqlexception\n    BEGIN\n        -- ERROR\n        ROLLBACK;\n        RESIGNAL;\n    END;\n\n    select uid into _uid from tig_users where sha1_user_id = sha1(lower(_user_id));\n\n    if _uid is not null then\n        start transaction;\n            delete from tig_user_credentials where uid = _uid and username_sha1 = sha1(_username);\n        commit;\n    end if;\nend //\n-- QUERY END:"
  },
  {
    "path": "src/main/database/mysql-server-8.4.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\nsource database/mysql-server-8.4.0-schema.sql;\n\nsource database/mysql-server-8.4.0-sp.sql;\n\nsource database/mysql-server-8.4.0-props.sql;\n\n-- LOAD FILE: database/mysql-server-8.4.0-schema.sql;\n\n-- LOAD FILE: database/mysql-server-8.4.0-sp.sql;\n\n-- LOAD FILE: database/mysql-server-8.4.0-props.sql;"
  },
  {
    "path": "src/main/database/mysql-server-8.5.0-schema.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\ndrop procedure if exists TigServerUpgrade;\n-- QUERY END:\n\ndelimiter //\n\n-- QUERY START:\ncreate procedure TigServerUpgrade()\nbegin\n    if not exists (select 1 from information_schema.STATISTICS where TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'tig_offline_messages' and INDEX_NAME = 'tig_offline_messages_receiver_sha1_msg_type_index') then\n        create index tig_offline_messages_receiver_sha1_msg_type_index on tig_offline_messages (receiver_sha1, msg_type);\n    end if;\n    if exists (select 1 from information_schema.STATISTICS where TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'tig_offline_messages' and INDEX_NAME = 'tig_offline_messages_receiver_sha1_index') then\n        drop index tig_offline_messages_receiver_sha1_index on tig_offline_messages;\n    end if;\nend //\n-- QUERY END:\n\ndelimiter ;\n\n-- QUERY START:\ncall TigServerUpgrade();\n-- QUERY END:\n\n-- QUERY START:\ndrop procedure if exists TigServerUpgrade;\n-- QUERY END:\n\n\n"
  },
  {
    "path": "src/main/database/mysql-server-8.5.0-sp.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\ndrop procedure if exists Tig_OfflineMessages_AddMessage;\n-- QUERY END:\n-- QUERY START:\ndrop procedure if exists Tig_OfflineMessages_DeleteMessages;\n-- QUERY END:\n\ndelimiter //\n\n-- QUERY START:\ncreate procedure Tig_OfflineMessages_AddMessage(_to varchar(2049) charset utf8, _from varchar(2049) charset utf8, _type int, _ts timestamp(6), _message mediumtext charset utf8mb4, _expired timestamp(6), _limit bigint)\nbegin\n    declare msg_count bigint;\n    set msg_count = 0;\n\n    if _limit > 0  then\n        select count(msg_id) into msg_count from tig_offline_messages where receiver_sha1 = sha1(lower(_to)) and sender_sha1 = sha1(lower(_from));\n    end if;\n\n    if _limit = 0 or _limit > msg_count then\n\t    insert into tig_offline_messages ( receiver, receiver_sha1, sender, sender_sha1, msg_type, ts, message, expired )\n\t        values ( _to, sha1(lower(_to)), _from, sha1(lower(_from)), _type, _ts, _message, _expired );\n\n        select 1 as msg_id;\n    else\n        select null as msg_id;\n    end if;\nend //\n-- QUERY END:\n\n-- QUERY START:\ncreate procedure Tig_OfflineMessages_DeleteMessages(_to varchar(2049) charset utf8)\nbegin\ndelete from tig_offline_messages where receiver_sha1 = sha1(lower(_to));\nend //\n-- QUERY END:\n\ndelimiter ;"
  },
  {
    "path": "src/main/database/mysql-server-8.5.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\nsource database/mysql-server-8.5.0-schema.sql;\n\nsource database/mysql-server-8.5.0-sp.sql;\n\n-- LOAD FILE: database/mysql-server-8.5.0-schema.sql;\n\n-- LOAD FILE: database/mysql-server-8.5.0-sp.sql;\n"
  },
  {
    "path": "src/main/database/postgresql-common-0.0.1.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n\n-- QUERY START:\ncreate table if not EXISTS tig_schema_versions (\n\t-- Component Name\n\tcomponent varchar(100) NOT NULL,\n\t-- Version of loaded schema\n\tversion varchar(100) NOT NULL,\n\t-- Time when schema was loaded last time\n\tlast_update timestamp NOT NULL,\n\tprimary key (component)\n);\n-- QUERY END:\n\n-- QUERY START:\nCREATE OR REPLACE FUNCTION TigGetComponentVersion(_component VARCHAR(100))\n  RETURNS TEXT AS $$\nDECLARE _result TEXT;\nBEGIN\n\n  SELECT version INTO _result FROM tig_schema_versions WHERE (component = _component);\n  RETURN (_result);\n\nEND;\n$$ LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\nCREATE OR REPLACE FUNCTION TigSetComponentVersion(_component text, _version text)\n  RETURNS void AS $$\n\n  BEGIN\n      UPDATE tig_schema_versions SET version = _version, last_update = now() WHERE component = _component;\n      IF FOUND THEN\n        RETURN;\n      END IF;\n      BEGIN\n        INSERT INTO tig_schema_versions (component, version, last_update) VALUES (_component, _version, now());\n        EXCEPTION WHEN OTHERS THEN\n        UPDATE tig_schema_versions SET version = _version WHERE component = _component;\n      END;\n      RETURN;\n\n  END;\n$$ LANGUAGE 'plpgsql';\n-- QUERY END:\n\n"
  },
  {
    "path": "src/main/database/postgresql-common-0.0.2.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n\n-- QUERY START:\nupdate tig_schema_versions\nset version = version || '-SNAPSHOT'\nwhere exists (\n    select 1\n    from tig_schema_versions\n    where\n        component = 'common'\n        and version = '0.0.1'\n) and version not like '%-SNAPSHOT%';\n-- QUERY END:\n\n"
  },
  {
    "path": "src/main/database/postgresql-common-0.0.3.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n"
  },
  {
    "path": "src/main/database/postgresql-counter_data_logger-0.0.1.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\ncreate table tig_stats_log (\n  lid            serial,\n  ts             TIMESTAMP                 DEFAULT CURRENT_TIMESTAMP,\n  hostname       varchar(2049)    NOT NULL,\n  cpu_usage      double precision not null default 0,\n  mem_usage      double precision not null default 0,\n  uptime         bigint           not null default 0,\n  vhosts         int              not null default 0,\n  sm_packets     bigint           not null default 0,\n  muc_packets    bigint           not null default 0,\n  pubsub_packets bigint           not null default 0,\n  c2s_packets    bigint           not null default 0,\n  s2s_packets    bigint           not null default 0,\n  ext_packets    bigint           not null default 0,\n  presences      bigint           not null default 0,\n  messages       bigint           not null default 0,\n  iqs            bigint           not null default 0,\n  registered     bigint           not null default 0,\n  c2s_conns      int              not null default 0,\n  s2s_conns      int              not null default 0,\n  bosh_conns     int              not null default 0,\n  primary key (ts, hostname(255))\n);\n-- QUERY END:\n\n-- QUERY START:\ndo $$\nbegin\n    if not exists (select 1 from information_schema.columns where table_catalog = current_database() and table_schema = 'public' and table_name = 'tig_stats_log' and column_name = 'ws2s_conns') then\n       ALTER TABLE tig_stats_log ADD `ws2s_conns` INT not null default 0;\n    end if;\nend$$;\n-- QUERY END:\n\n-- QUERY START:\ndo $$\nbegin\n    if not exists (select 1 from information_schema.columns where table_catalog = current_database() and table_schema = 'public' and table_name = 'tig_stats_log' and column_name = 'ws2s_packets') then\n       ALTER TABLE tig_stats_log ADD `ws2s_packets` INT not null default 0;\n    end if;\nend$$;\n-- QUERY END:\n\n-- QUERY START:\ndo $$\nbegin\n    if not exists (select 1 from information_schema.columns where table_catalog = current_database() and table_schema = 'public' and table_name = 'tig_stats_log' and column_name = 'sm_sessions') then\n       ALTER TABLE tig_stats_log ADD `sm_sessions` INT not null default 0;\n    end if;\nend$$;\n-- QUERY END:\n\n-- QUERY START:\ndo $$\nbegin\n    if not exists (select 1 from information_schema.columns where table_catalog = current_database() and table_schema = 'public' and table_name = 'tig_stats_log' and column_name = 'sm_connections') then\n       ALTER TABLE tig_stats_log ADD `sm_connections` INT not null default 0;\n    end if;\nend$$;\n-- QUERY END:\n\n"
  },
  {
    "path": "src/main/database/postgresql-installer-create-db.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START: create database\ncreate database ${dbName};\n-- QUERY END: create database\n\n-- QUERY START: add user\ndo $$\nbegin\nIF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '${dbUser}') THEN\n\tcreate user ${dbUser} with password '${dbPass}';\nend if;\nend$$;\n-- QUERY END: add user\n\n-- QUERY START: GRANT ALL\nGRANT ALL ON database ${dbName} TO ${dbUser};\n-- QUERY END: GRANT ALL\n\n-- QUERY START: ALTER DATABASE\nALTER DATABASE ${dbName} OWNER TO ${dbUser};\n-- QUERY END: ALTER DATABASE\n\n-- QUERY START: Install uuid extension\nCREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";\n-- QUERY END: Install uuid extension\n\n\n\n"
  },
  {
    "path": "src/main/database/postgresql-installer-post.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- Permissions fix\n\n-- QUERY START: ALTER SCHEMA\nALTER SCHEMA public OWNER TO ${dbUser};\n-- QUERY END: ALTER SCHEMA\n\n-- QUERY START: GRANT ALL ON ALL TABLES\nGRANT ALL ON ALL TABLES IN SCHEMA public TO ${dbUser};\n-- QUERY END: GRANT ALL ON ALL TABLES\n\n-- QUERY START: GRANT ALL ON ALL FUNCTIONS\nGRANT ALL ON ALL FUNCTIONS IN SCHEMA public TO ${dbUser};\n-- QUERY END: GRANT ALL ON ALL FUNCTIONS\n\n-- QUERY START: GRANT ALL ON ALL SEQUENCES\nGRANT ALL ON ALL SEQUENCES IN SCHEMA public TO ${dbUser};\n-- QUERY END: GRANT ALL ON ALL SEQUENCES\n\n\n"
  },
  {
    "path": "src/main/database/postgresql-server-8.0.0-props.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\nselect now(), ' - Setting schema version to 8.0.0';\n\n-- QUERY START:\nselect TigSetComponentVersion('server', '8.0.0');\n-- QUERY END:\n"
  },
  {
    "path": "src/main/database/postgresql-server-8.0.0-schema.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\ncreate table if not exists tig_users (\n\tuid bigserial,\n\n\t-- Jabber User ID\n\tuser_id varchar(2049) NOT NULL,\n\t-- User password encrypted or not\n\tuser_pw varchar(255) default NULL,\n\t-- Time the account has been created\n\tacc_create_time timestamp with time zone DEFAULT CURRENT_TIMESTAMP,\n\t-- Time of the last user login\n\tlast_login timestamp with time zone,\n\t-- Time of the last user logout\n\tlast_logout timestamp with time zone,\n\t-- User online status, if > 0 then user is online, the value\n\t-- indicates the number of user connections.\n\t-- It is incremented on each user login and decremented on each\n\t-- user logout.\n\tonline_status int default 0,\n\t-- Number of failed login attempts\n\tfailed_logins int default 0,\n\t-- User status, whether the account is active or disabled\n\t-- >0 - account active, 0 - account disabled\n\taccount_status int default 1,\n\n\tprimary key (uid)\n);\n-- QUERY END:\n-- QUERY START:\ndo $$\nbegin\n    if to_regclass('public.user_id') is null then\n        create unique index user_id on tig_users ( lower(user_id) );\n    end if;\nend$$;\n-- QUERY END:\n-- QUERY START:\ndo $$\nbegin\n    if to_regclass('public.user_pw') is null then\n        create index user_pw on tig_users (user_pw);\n    end if;\nend$$;\n-- QUERY END:\n-- QUERY START:\ndo $$\nbegin\n    if to_regclass('public.last_login') is null then\n        create index last_login on tig_users (last_login);\n    end if;\nend$$;\n-- QUERY END:\n-- QUERY START:\ndo $$\nbegin\n    if to_regclass('public.last_logout') is null then\n        create index last_logout on tig_users (last_logout);\n    end if;\nend$$;\n-- QUERY END:\n-- QUERY START:\ndo $$\nbegin\n    if to_regclass('public.account_status') is null then\n        create index account_status on tig_users (account_status);\n    end if;\nend$$;\n-- QUERY END:\n-- QUERY START:\ndo $$\nbegin\n    if to_regclass('public.online_status') is null then\n        create index online_status on tig_users (online_status);\n    end if;\nend$$;\n-- QUERY END:\n\n-- QUERY START:\ncreate table if not exists tig_nodes (\n       nid bigserial,\n       parent_nid bigint,\n       uid bigint NOT NULL references tig_users(uid),\n\n       node varchar(255) NOT NULL,\n\n       primary key (nid)\n);\n-- QUERY END:\n-- QUERY START:\ndo $$\nbegin\n    if to_regclass('public.tnode') is null then\n        create unique index tnode on tig_nodes ( parent_nid, uid, node );\n    end if;\nend$$;\n-- QUERY END:\n-- QUERY START:\ndo $$\nbegin\n    if to_regclass('public.node') is null then\n        create index node on tig_nodes ( node );\n    end if;\nend$$;\n-- QUERY END:\n-- QUERY START:\ndo $$\nbegin\n    if to_regclass('public.nuid') is null then\n        create index nuid on tig_nodes (uid);\n    end if;\nend$$;\n-- QUERY END:\n-- QUERY START:\ndo $$\nbegin\n    if to_regclass('public.parent_nid') is null then\n        create index parent_nid on tig_nodes (parent_nid);\n    end if;\nend$$;\n-- QUERY END:\n\n-- QUERY START:\ncreate table if not exists tig_pairs (\n       pid BIGSERIAL PRIMARY KEY,\n       nid bigint references tig_nodes(nid),\n       uid bigint NOT NULL references tig_users(uid),\n\n       pkey varchar(255) NOT NULL,\n       pval text\n);\n-- QUERY END:\n-- QUERY START:\ndo $$\nbegin\n    if to_regclass('public.pkey') is null then\n        create index pkey on tig_pairs ( pkey );\n    end if;\nend$$;\n-- QUERY END:\n-- QUERY START:\ndo $$\nbegin\n    if to_regclass('public.puid') is null then\n        create index puid on tig_pairs (uid);\n    end if;\nend$$;\n-- QUERY END:\n-- QUERY START:\ndo $$\nbegin\n    if to_regclass('public.pnid') is null then\n        create index pnid on tig_pairs (nid);\n    end if;\nend$$;\n-- QUERY END:\n\n-- QUERY START:\ncreate table if not exists tig_offline_messages (\n    msg_id bigserial,\n    ts timestamp with time zone default now(),\n    expired timestamp with time zone,\n    sender varchar(2049),\n    receiver varchar(2049) not null,\n    msg_type int not null default 0,\n    message text not null,\n\n    primary key(msg_id)\n);\n-- QUERY END:\n\n-- QUERY START:\ncreate table if not exists tig_broadcast_messages (\n    id varchar(128) not null,\n    expired timestamp with time zone not null,\n    msg text not null,\n    primary key (id)\n);\n-- QUERY END:\n\n-- QUERY START:\ncreate table if not exists tig_broadcast_jids (\n    jid_id bigserial,\n    jid varchar(2049) not null,\n\n    primary key (jid_id)\n);\n-- QUERY END:\n-- QUERY START:\ndo $$\nbegin\n    if to_regclass('public.tig_broadcast_jids_jid') is null then\n        create index tig_broadcast_jids_jid on tig_broadcast_jids (lower(jid));\n    end if;\nend$$;\n-- QUERY END:\n\n-- QUERY START:\ncreate table if not exists tig_broadcast_recipients (\n    msg_id varchar(128) not null references tig_broadcast_messages(id),\n    jid_id bigint not null references tig_broadcast_jids(jid_id),\n    primary key (msg_id, jid_id)\n);\n-- QUERY END:\n\n-- QUERY START:\ndo $$\nbegin\n    if to_regclass('public.tig_offline_messages_expired') is null then\n        create index tig_offline_messages_expired on tig_offline_messages (expired);\n    end if;\n    if to_regclass('public.tig_offline_messages_receiver') is null then\n        create index tig_offline_messages_receiver on tig_offline_messages (lower(receiver));\n    end if;\n    if to_regclass('public.tig_offline_messages_receiver_sender') is null then\n        create index tig_offline_messages_receiver_sender on tig_offline_messages (lower(receiver), lower(sender));\n    end if;\nend$$;\n-- QUERY END:\n\n-- QUERY START:\ncreate table if not exists tig_cluster_nodes (\n    hostname varchar(512) not null,\n\tsecondary varchar(512),\n    password varchar(255) not null,\n    last_update timestamp with time zone default current_timestamp,\n    port int,\n    cpu_usage double precision not null,\n    mem_usage double precision not null,\n    primary key (hostname)\n);\n-- QUERY END:\n\n-- ------------- Credentials support\n-- QUERY START:\ncreate table if not exists tig_user_credentials (\n    uid bigint not null references tig_users(uid),\n    username varchar(2049) not null,\n    mechanism varchar(128) not null,\n    value text not null,\n\n    primary key (uid, username, mechanism)\n);\n-- QUERY END:"
  },
  {
    "path": "src/main/database/postgresql-server-8.0.0-sp.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\nCREATE OR REPLACE FUNCTION public.create_plpgsql_language ()\n        RETURNS TEXT\n        AS $$\n            CREATE LANGUAGE plpgsql;\n            SELECT 'language plpgsql created'::TEXT;\n        $$\nLANGUAGE 'sql';\n\nSELECT CASE WHEN\n              (SELECT true::BOOLEAN\n                 FROM pg_language\n                WHERE lanname='plpgsql')\n            THEN\n              (SELECT 'language already installed'::TEXT)\n            ELSE\n              (SELECT public.create_plpgsql_language())\n            END;\n\nDROP FUNCTION public.create_plpgsql_language ();\n-- QUERY END:\n\n-- QUERY START:\n-- Database properties get - function\ncreate or replace function TigGetDBProperty(varchar(255)) returns text as '\ndeclare\n  _result text;\n  _tkey alias for $1;\nbegin\n\n\tselect pval into _result from tig_pairs, tig_users\n\t\twhere (pkey = _tkey) AND (lower(user_id) = lower(''db-properties''))\n\t\t\t\t\tAND (tig_pairs.uid = tig_users.uid);\n\n\treturn _result;\nend;\n' LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\n-- The initialization of the database.\n-- The procedure should be called manually somehow before starting the\n-- server. In theory the server could call the procedure automatically\n-- at the startup time but I don't know yet how to solve the problem\n-- with multiple cluster nodes starting at later time when the server\n-- is already running.\ncreate or replace function TigInitdb() returns void as '\nbegin\n\tupdate tig_users set online_status = 0;\n  return;\nend;\n' LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\n-- Add a new user to the database assuming the user password is already\n-- encoded properly according to the database settings.\n-- If password is not encoded TigAddUserPlainPw should be used instead.\ncreate or replace function TigAddUser(varchar(2049), varchar(255))\n  returns bigint as '\ndeclare\n  _user_id alias for $1;\n  _user_pw alias for $2;\n  _res_uid bigint;\nbegin\n\tif exists( select uid from tig_users where\n\t\t(lower(user_id) = lower(_user_id)) AND (user_pw = _user_pw) )\n\tthen\n\t\treturn null;\n\telse\n\t\tinsert into tig_users (user_id, user_pw)\n\t\t\tvalues (_user_id, _user_pw);\n\t\tselect currval(''tig_users_uid_seq'') into _res_uid;\n\n\t\tinsert into tig_nodes (parent_nid, uid, node)\n\t\tvalues (NULL, _res_uid, ''root'');\n\n\t\treturn _res_uid as uid;\n\tend if;\nend;\n' LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\n-- Low level database user id as big number. Used only for performance reasons\n-- and save database space. Besides JID is too large to server as UID\ncreate or replace function TigGetUserDBUid(varchar(2049)) returns bigint as '\ndeclare\n  _user_id alias for $1;\n  res_uid bigint;\nbegin\n\tselect uid into res_uid from tig_users where lower(user_id) = lower(_user_id);\n  return res_uid;\nend;\n' LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\n-- Variant of TigUpdatePasswordPlainPw SP with parameters in reverse order.\n-- Some implementations require the parameters to be in the same order as\n-- the update query.\ncreate or replace function TigUpdatePasswordPlainPwRev(varchar(255), varchar(2049))\n  returns void as '\ndeclare\n  _user_pw alias for $1;\n  _user_id alias for $2;\nbegin\n  perform TigUpdatePasswordPlainPw(_user_id, _user_pw);\n  return;\nend;\n' LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\n-- Update user password\ncreate or replace function TigUpdatePassword(varchar(2049), varchar(255))\n  returns void as '\ndeclare\n  _user_id alias for $1;\n  _user_pw alias for $2;\nbegin\n\tupdate tig_users set user_pw = _user_pw where lower(user_id) = lower(_user_id);\n  return;\nend;\n' LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\n-- List all online users\ncreate or replace function TigOnlineUsers() returns void as '\nbegin\n  return;\n\tselect user_id, last_login, last_logout, online_status, failed_logins, account_status\n\t\tfrom tig_users where online_status > 0;\nend;\n' LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\n-- List all offline users\ncreate or replace function TigOfflineUsers() returns void as '\nbegin\n  return;\n\tselect user_id, last_login, last_logout, online_status, failed_logins, account_status\n\t\tfrom tig_users where online_status = 0;\nend;\n' LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\n-- List of all users in database\ncreate or replace function TigAllUsers() returns setof varchar(2049) as\n\t'select user_id from tig_users;'\nLANGUAGE 'sql';\n-- create or replace function TigAllUsers() returns void as '\n-- begin\n--  return;\n--\tselect user_id, last_login, last_logout, online_status, failed_logins, account_status\n--\t\tfrom tig_users;\n-- end;\n-- ' LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\n-- All users count\ncreate or replace function TigAllUsersCount() returns bigint as '\ndeclare\n  res_cnt bigint;\nbegin\n\tselect count(*) into res_cnt from tig_users;\n  return res_cnt;\nend;\n' LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\n-- Perforrm user login. It returns user_id uppon success and NULL\n-- on failure.\n-- If the login is successful it also increases online_status and sets\n-- last_login time to the current timestamp\ncreate or replace function TigUserLogin(varchar(2049), varchar(255))\n\t\t\t \t\t\t\t\t  returns varchar(2049) as '\ndeclare\n  _user_id alias for $1;\n  _user_pw alias for $2;\n  res_user_id varchar(2049);\nbegin\n\tif exists(select user_id from tig_users\n\t\twhere (account_status > 0) AND (lower(user_id) = lower(_user_id)) AND (user_pw = _user_pw))\n\tthen\n\t\tupdate tig_users\n\t\t\tset online_status = online_status + 1, last_login = now()\n\t\t\twhere lower(user_id) = lower(_user_id);\n    select _user_id into res_user_id;\n\telse\n\t\tupdate tig_users set failed_logins = failed_logins + 1 where lower(user_id) = lower(_user_id);\n    select NULL into res_user_id;\n\tend if;\n  return res_user_id;\nend;\n' LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\n-- It decreases online_status and sets last_logout time to the current timestamp\ncreate or replace function TigUserLogout(varchar(2049)) returns void as '\ndeclare\n  _user_id alias for $1;\nbegin\n\tupdate tig_users\n\t\tset online_status = greatest(online_status - 1, 0),\n\t\t\tlast_logout = now()\n\t\twhere lower(user_id) = lower(_user_id);\n  return;\nend;\n' LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\n-- Disable user account\ncreate or replace function TigDisableAccount(varchar(2049)) returns void as '\ndeclare\n  _user_id alias for $1;\nbegin\n\tupdate tig_users set account_status = 0 where lower(user_id) = lower(_user_id);\n  return;\nend;\n' LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\n-- Enable user account\ncreate or replace function TigEnableAccount(varchar(2049)) returns void as '\ndeclare\n  _user_id alias for $1;\nbegin\n\tupdate tig_users set account_status = 1 where lower(user_id) = lower(_user_id);\n  return;\nend;\n' LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\n-- Get list of all active user accounts\ncreate or replace function TigActiveAccounts() returns void as '\nbegin\n  return;\n\tselect user_id, last_login, last_logout, online_status, failed_logins, account_status\n\t\tfrom tig_users where account_status > 0;\nend;\n' LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\n-- Helper procedure for adding a new node\ncreate or replace function TigAddNode(bigint, bigint, varchar(255))\n  returns bigint as '\ndeclare\n  _parent_nid alias for $1;\n  _uid alias for $2;\n  _node alias for $3;\n  res_nid bigint;\nbegin\n\tinsert into tig_nodes (parent_nid, uid, node)\n\t\tvalues (_parent_nid, _uid, _node);\n  select currval(''tig_nodes_nid_seq'') into res_nid;\n  return res_nid;\nend;\n' LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\ncreate or replace function TigUsers2Ver4Convert() returns void as '\ndeclare\n  user_row RECORD;\nbegin\n\n  for user_row in\n\t\tselect user_id, pval as password\n\t\t\tfrom tig_users, tig_pairs\n\t\t\twhere tig_users.uid = tig_pairs.uid and pkey = ''password'' loop\n\t\tperform TigUpdatePasswordPlainPw(user_row.user_id, user_row.password);\n\tEND LOOP;\n  return;\nend;\n' LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\ncreate or replace function TigUpdatePairs(bigint, bigint, varchar(255), text) returns void as '\ndeclare\n  _nid alias for $1;\n  _uid alias for $2;\n  _tkey alias for $3;\n  _tval alias for $4;\nbegin\n  if exists(select 1 from tig_pairs where nid = _nid and uid = _uid and pkey = _tkey)\n  then\n        update tig_pairs set pval = _tval where nid = _nid and uid = _uid and pkey = _tkey;\n  else\n        insert into tig_pairs (nid, uid, pkey, pval) values (_nid, _uid, _tkey, _tval);\n  end if;\n  return;\nend;\n' LANGUAGE 'plpgsql';\n-- QUERY END:\n\n\n-- QUERY START:\n-- It sets last_login time to the current timestamp\ncreate or replace function TigUpdateLoginTime(varchar(2049)) returns void as $$\ndeclare\n  _user_id alias for $1;\nbegin\n\tupdate tig_users\n\t\tset last_login = now()\n\t\twhere lower(user_id) = lower(_user_id);\n  return;\nend;\n$$ LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\ndo $$\nbegin\nif exists( select 1 from pg_proc where proname = 'tigdisabledaccounts' and pg_get_function_result(oid) = 'TABLE(user_id character varying, last_login timestamp without time zone, last_logout timestamp without time zone, online_status integer, failed_logins integer, account_status integer)') then\n    drop function TigDisabledAccounts();\nend if;\nend$$;\n-- QUERY END:\n\n-- QUERY START:\n-- Get list of all disabled user accounts\ncreate or replace function TigDisabledAccounts() returns table(user_id varchar(2049), last_login timestamp with time zone, last_logout timestamp with time zone, online_status int, failed_logins int, account_status int) as '\n\tselect user_id, last_login, last_logout, online_status, failed_logins, account_status\n\t\tfrom tig_users where account_status = 0;\n' LANGUAGE 'sql';\n-- QUERY END:\n\n-- ------------ Offline Messages\n-- QUERY START:\ndo $$\nbegin\nif exists( select 1 from pg_proc where proname = lower('Tig_OfflineMessages_AddMessage') and pg_get_function_arguments(oid) = '_to character varying, _from character varying, _type integer, _ts timestamp without time zone, _message text, _expired timestamp without time zone, _limit bigint') then\n    drop function Tig_OfflineMessages_AddMessage(_to character varying, _from character varying, _type integer, _ts timestamp without time zone, _message text, _expired timestamp without time zone, _limit bigint);\nend if;\nend$$;\n-- QUERY END:\n\n-- QUERY START:\ncreate or replace function Tig_OfflineMessages_AddMessage(_to varchar(2049), _from varchar(2049), _type int, _ts timestamp with time zone, _message text, _expired timestamp with time zone, _limit bigint) returns bigint as $$\ndeclare\n    _msg_count bigint;\n    _msg_id bigint;\nbegin\n    perform _msg_count = 0;\n\n    if _limit > 0  then\n        select count(msg_id) into _msg_count from tig_offline_messages where lower(receiver) = lower(_to) and lower(sender) = lower(_from);\n    end if;\n\n    if _limit = 0 or _limit > _msg_count then\n        with inserted_msg as (\n\t        insert into tig_offline_messages ( receiver, sender, msg_type, ts, message, expired )\n\t            values ( _to, _from, _type, _ts, _message, _expired )\n\t        returning msg_id\n        )\n\t    select msg_id into _msg_id from inserted_msg;\n\tend if;\n\n\treturn _msg_id;\nend;\n$$ LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\ncreate or replace function  Tig_OfflineMessages_GetMessages(_to varchar(2049)) returns table(\n    \"message\" text, \"msg_id\" bigint\n) as $$\nbegin\n    return query select om.message, om.msg_id\n        from tig_offline_messages om\n        where lower(om.receiver) = lower(_to);\nend;\n$$ LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\ncreate or replace function  Tig_OfflineMessages_GetMessagesByIds(_to varchar(2049), _msg_id1 varchar(50), _msg_id2 varchar(50), _msg_id3 varchar(50), _msg_id4 varchar(50)) returns table(\n    \"message\" text, \"msg_id\" bigint\n) as $$\nbegin\n    return query select om.message, om.msg_id\n        from tig_offline_messages om\n        where lower(om.receiver) = lower(_to)\n            and (\n                (_msg_id1 is not null and om.msg_id = cast(_msg_id1 as bigint))\n                or (_msg_id2 is not null and om.msg_id = cast(_msg_id2 as bigint))\n                or (_msg_id3 is not null and om.msg_id = cast(_msg_id3 as bigint))\n                or (_msg_id4 is not null and om.msg_id = cast(_msg_id4 as bigint))\n            );\nend;\n$$ LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\ncreate or replace function Tig_OfflineMessages_GetMessagesCount(_to varchar(2049)) returns table(\n    \"msg_type\" int, \"count\" bigint\n) as $$\nbegin\n    return query select om.msg_type, count(om.msg_type)\n        from tig_offline_messages om\n        where lower(om.receiver) = lower(_to)\n        group by om.msg_type;\nend;\n$$ LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\ncreate or replace function  Tig_OfflineMessages_ListMessages(_to varchar(2049)) returns table(\n    \"msg_id\" bigint, \"msg_type\" int, \"sender\" varchar(2049)\n) as $$\nbegin\n    return query select om.msg_id, om.msg_type, om.sender\n        from tig_offline_messages om\n        where lower(om.receiver) = lower(_to);\nend;\n$$ LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\ncreate or replace function Tig_OfflineMessages_DeleteMessages(_to varchar(2049)) returns bigint as $$\ndeclare _deleted bigint;\nbegin\n    with deleted as (\n        delete from tig_offline_messages where lower(receiver) = lower(_to)\n        returning msg_id\n    )\n    select count(msg_id) into _deleted from deleted;\n    return _deleted;\nend;\n$$ LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\ncreate or replace function Tig_OfflineMessages_DeleteMessagesByIds(_to varchar(2049), _msg_id1 varchar(50), _msg_id2 varchar(50), _msg_id3 varchar(50), _msg_id4 varchar(50)) returns bigint as $$\ndeclare _deleted bigint;\nbegin\n    with deleted as (\n        delete from tig_offline_messages\n        where lower(receiver) = lower(_to)\n            and (\n                (_msg_id1 is not null and msg_id = cast(_msg_id1 as bigint))\n                or (_msg_id2 is not null and msg_id = cast(_msg_id2 as bigint))\n                or (_msg_id3 is not null and msg_id = cast(_msg_id3 as bigint))\n                or (_msg_id4 is not null and msg_id = cast(_msg_id4 as bigint))\n            )\n        returning msg_id\n    )\n    select count(msg_id) into _deleted from deleted;\n    return _deleted;\nend;\n$$ LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\ncreate or replace function Tig_OfflineMessages_DeleteMessage(_msg_id bigint) returns bigint as $$\ndeclare _deleted bigint;\nbegin\n    with deleted as (\n        delete from tig_offline_messages where msg_id = _msg_id\n        returning msg_id\n    )\n    select count(msg_id) into _deleted from deleted;\n    return _deleted;\nend;\n$$ LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\ndo $$\nbegin\nif exists( select 1 from pg_proc where proname = lower('Tig_OfflineMessages_GetExpiredMessages') and pg_get_function_result(oid) = 'TABLE(msg_id bigint, expired timestamp without time zone, message text)') then\n    drop function Tig_OfflineMessages_GetExpiredMessages(int);\nend if;\nend$$;\n-- QUERY END:\n\n-- QUERY START:\ncreate or replace function Tig_OfflineMessages_GetExpiredMessages(_limit int) returns table(\n    \"msg_id\" bigint, \"expired\" timestamp with time zone, \"message\" text\n) as $$\nbegin\n    return query select om.msg_id, om.expired, om.message\n        from tig_offline_messages om\n        where om.expired is not null\n        order by om.expired asc\n        limit _limit;\nend;\n$$ LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\ndo $$\nbegin\nif exists( select 1 from pg_proc where proname = lower('Tig_OfflineMessages_GetExpiredMessagesBefore') and pg_get_function_result(oid) = 'TABLE(msg_id bigint, expired timestamp without time zone, message text)') then\n    drop function Tig_OfflineMessages_GetExpiredMessagesBefore(timestamp without time zone);\nend if;\nend$$;\n-- QUERY END:\n\n-- QUERY START:\ncreate or replace function  Tig_OfflineMessages_GetExpiredMessagesBefore(_expired timestamp with time zone) returns table(\n    \"msg_id\" bigint, \"expired\" timestamp with time zone, \"message\" text\n) as $$\nbegin\n    return query select om.msg_id, om.expired, om.message\n        from tig_offline_messages om\n        where om.expired is not null\n            and (_expired is null or om.expired <= _expired)\n        order by om.expired asc;\nend;\n$$ LANGUAGE 'plpgsql';\n-- QUERY END:\n\n\n-- ------------ Broadcast Messages\n-- QUERY START:\ndo $$\nbegin\nif exists( select 1 from pg_proc where proname = lower('Tig_BroadcastMessages_AddMessage') and pg_get_function_arguments(oid) = '_msg_id character varying, _expired timestamp without time zone, _msg text') then\n    drop function Tig_BroadcastMessages_AddMessage(_msg_id character varying, _expired timestamp without time zone, _msg text);\nend if;\nend$$;\n-- QUERY END:\n\n-- QUERY START:\ncreate or replace function Tig_BroadcastMessages_AddMessage(_msg_id varchar(128), _expired timestamp with time zone, _msg text) returns void\nas $$\nbegin\n    begin\n        insert into tig_broadcast_messages (id, expired, msg)\n            select _msg_id, _expired, _msg\n            where not exists (\n                select 1 from tig_broadcast_messages where id = _msg_id\n            );\n    exception when unique_violation then\n    end;\nend;\n$$ LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\ncreate or replace function Tig_BroadcastMessages_AddMessageRecipient(_msg_id varchar(128), _jid varchar(2049)) returns void\nas $$\ndeclare _jid_id bigint;\nbegin\n    select jid_id into _jid_id from tig_broadcast_jids where lower(jid) = lower(_jid);\n    if _jid_id is null then\n        begin\n            with inserted as (\n                insert into tig_broadcast_jids (jid)\n                    values (_jid)\n                returning jid_id\n            )\n            select jid_id into _jid_id from inserted;\n        exception when unique_violation then\n            select jid_id into _jid_id from tig_broadcast_jids where lower(jid) = lower(_jid);\n        end;\n    end if;\n\n    begin\n        insert into tig_broadcast_recipients (msg_id, jid_id)\n            select _msg_id, _jid_id where not exists (\n                select 1 from tig_broadcast_recipients br where br.msg_id = _msg_id and br.jid_id = _jid_id\n            );\n    exception when unique_violation then\n    end;\nend;\n$$ LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\ndo $$\nbegin\nif exists( select 1 from pg_proc where proname = lower('Tig_BroadcastMessages_GetMessages') and pg_get_function_arguments(oid) = '_expired timestamp without time zone') then\n    drop function Tig_BroadcastMessages_GetMessages(_expired timestamp without time zone);\nend if;\nend$$;\n-- QUERY END:\n\n-- QUERY START:\ncreate or replace function  Tig_BroadcastMessages_GetMessages(_expired timestamp with time zone) returns table (\n    id varchar(128),\n    expired timestamp,\n    msg text\n) as $$\nbegin\n    return query select bm.id, bm.expired, bm.msg\n    from tig_broadcast_messages bm\n    where bm.expired >= _expired;\nend;\n$$ LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\ncreate or replace function Tig_BroadcastMessages_GetMessageRecipients(_msg_id varchar(128)) returns table (\n    jid varchar(2049)\n) as $$\nbegin\n    return query select j.jid\n    from tig_broadcast_recipients r\n    inner join tig_broadcast_jids j on j.jid_id = r.jid_id\n    where r.msg_id = _msg_id;\nend;\n$$ LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\nCREATE OR REPLACE FUNCTION TigUpdateAccountStatus(_user_id VARCHAR(2049), _status INT)\n    RETURNS VOID\nAS $$\nBEGIN\n    UPDATE tig_users\n    SET account_status = _status\n    WHERE lower(user_id) = lower(_user_id);\n    RETURN;\nEND;\n$$ LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\nCREATE OR REPLACE FUNCTION TigAccountStatus(_user_id VARCHAR(2049))\n    RETURNS TABLE(status INT)\nAS $$\nBEGIN\n    RETURN QUERY SELECT account_status\n                 FROM tig_users\n                 WHERE lower(user_id) = lower(_user_id);\nEND;\n$$ LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- ------------- Credentials support\n-- QUERY START:\ncreate or replace function TigUserCredential_Update(_user_id varchar(2049), _username varchar(2049), _mechanism varchar(128), _value text) returns void\nas $$\ndeclare _uid bigint;\nbegin\n    select uid into _uid from tig_users where lower(user_id) = lower(_user_id);\n    if _uid is not null then\n        update tig_user_credentials set value = _value  where uid = _uid and username = _username and mechanism = _mechanism;\n        insert into tig_user_credentials (uid, username, mechanism, value)\n            select _uid, _username, _mechanism, _value \n            where not exists (select 1 from tig_user_credentials where uid = _uid and username = _username and mechanism = _mechanism);\n    end if;\nend;\n$$ language 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\ncreate or replace function TigUserCredentials_Get(_user_id varchar(2049), _username varchar(2049)) returns table (\n    mechanism varchar(128),\n    value text,\n    account_status int\n) as $$\nbegin\n    return query select c.mechanism, c.value, u.account_status\n        from tig_users u\n        inner join tig_user_credentials c on c.uid = u.uid\n        where\n            lower(u.user_id) = lower(_user_id)\n            and c.username = _username;\nend;\n$$ language 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\ncreate or replace function TigUserUsernames_Get(_user_id varchar(2049)) returns table (\n    username varchar(2049)\n) as $$\nbegin\n    return query select distinct c.username\n        from tig_users u\n        inner join tig_user_credentials c on c.uid = u.uid\n        where\n            lower(u.user_id) = lower(_user_id);\nend;\n$$ language 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\ncreate or replace function TigUserCredential_Remove(_user_id varchar(2049), _username varchar(2049)) returns void\nas $$\ndeclare _uid bigint;\nbegin\n    select uid into _uid from tig_users where lower(user_id) = lower(_user_id);\n    if _uid is not null then\n        delete from tig_user_credentials where uid = _uid and username = _username;\n    end if;\nend;\n$$ language 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\n-- _user_id character varying, _user_pw character varying\n-- Add a new user to the database assuming the user password is already\n-- encoded properly according to the database settings.\n-- If password is not encoded TigAddUserPlainPw should be used instead.\ncreate or replace function TigAddUserPlainPw(_user_id varchar(2049), _user_pw varchar(255))\n  returns bigint as $$\ndeclare\n  _res_uid bigint;\nbegin\n\tinsert into tig_users (user_id)\n\t\tvalues (_user_id);\n\tselect currval('tig_users_uid_seq') into _res_uid;\n\n\tinsert into tig_nodes (parent_nid, uid, node)\n\t\tvalues (NULL, _res_uid, 'root');\n\n    if _user_pw is null then\n        update tig_users set account_status = -1 where lower(user_id) = lower(_user_id);\n    else\n        perform TigUpdatePasswordPlainPw(_user_id, _user_pw);\n    end if;\n\n\treturn _res_uid as uid;\nend;\n$$ language 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\ndo $$\nbegin\nif exists( select 1 from pg_proc where proname = lower('TigGetPassword') and pg_get_function_result(oid) = 'character varying') then\n    drop function TigGetPassword(character varying);\nend if;\nend$$;\n-- QUERY END:\n\n-- QUERY START:\n-- Returns user's password from the database\ncreate or replace function TigGetPassword(_user_id varchar(2049)) returns text as $$\ndeclare\n  res_pw text;\nbegin\n    select c.value into res_pw\n    from tig_users u\n    inner join tig_user_credentials c on c.uid = u.uid\n    where\n        lower(u.user_id) = lower(_user_id)\n        and c.username = 'default'\n        and c.mechanism = 'PLAIN';\n    return res_pw;\nend;\n$$ language 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\n-- Takes plain text user password and converts it to internal representation\ncreate or replace function TigUpdatePasswordPlainPw(_user_id varchar(2049), _user_pw varchar(255))\n  returns void as $$\ndeclare\n    _passwordEncoding text;\n    _encodedPassword text;\nbegin\n\tselect coalesce(TigGetDBProperty('password-encoding'), 'PLAIN') into _passwordEncoding;\n\t\n    select\n        case _passwordEncoding\n\t\t    when 'MD5-PASSWORD' then MD5(_user_pw)\n\t\t    when 'MD5-USERID-PASSWORD' then MD5(_user_id || _user_pw)\n\t        when 'MD5-USERNAME-PASSWORD' then MD5(split_part(_user_id, '@', 1) || _user_pw)\n\t\t    else _user_pw\n\t\tend into _encodedPassword;\n\n\tperform TigUserCredential_Update(_user_id, 'default', _passwordEncoding, _encodedPassword);\nend;\n$$ language 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\n-- Performs user login for a plain text password, converting it to an internal\n-- representation if necessary\ncreate or replace function TigUserLoginPlainPw(_user_id varchar(2049), _user_pw varchar(255))\nreturns varchar(2049) as $$\ndeclare\n    _res_user_id varchar(2049);\n    _passwordEncoding text;\n    _encodedPassword text;\nbegin\n\tselect coalesce(TigGetDBProperty('password-encoding'), 'PLAIN') into _passwordEncoding;\n\n    select\n        case _passwordEncoding\n\t\t    when 'MD5-PASSWORD' then MD5(_user_pw)\n\t\t    when 'MD5-USERID-PASSWORD' then MD5(_user_id || _user_pw)\n\t        when 'MD5-USERNAME-PASSWORD' then MD5(split_part(_user_id, '@', 1) || _user_pw)\n\t\t    else _user_pw\n\t\tend into _encodedPassword;\n\n\tselect u.user_id into _res_user_id\n\tfrom tig_users u\n\tinner join tig_user_credentials c on c.uid = u.uid\n\twhere\n\t    lower(u.user_id) = lower(_user_id)\n\t    and c.username = 'default'\n\t    and c.mechanism = _passwordEncoding\n\t    and c.value = _encodedPassword\n\t    and u.account_status > 0;\n\n    if _res_user_id is not null then\n\t\tupdate tig_users\n\t\t\tset online_status = online_status + 1, last_login = now()\n\t\t\twhere lower(user_id) = lower(_user_id);\n\telse\n\t\tupdate tig_users set failed_logins = failed_logins + 1 where lower(user_id) = lower(_user_id);\n    end if;\n\n    return _res_user_id;\nend;\n$$ language 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\n-- Removes a user from the database\ncreate or replace function TigRemoveUser(_user_id varchar(2049)) returns void as $$\ndeclare\n  res_uid bigint;\nbegin\n\tselect uid into res_uid from tig_users where lower(user_id) = lower(_user_id);\n\n    delete from tig_user_credentials where uid = res_uid;\n\tdelete from tig_pairs where uid = res_uid;\n\tdelete from tig_nodes where uid = res_uid;\n\tdelete from tig_users where uid = res_uid;\nend;\n$$ language 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\n-- Database properties set - procedure\ncreate or replace function TigPutDBProperty(varchar(255), text) returns void as '\ndeclare\n  _tkey alias for $1;\n  _tval alias for $2;\nbegin\n  if exists( select pval from tig_pairs, tig_users where\n\t\t(lower(user_id) = lower(''db-properties'')) AND (tig_users.uid = tig_pairs.uid)\n\t\tAND (pkey = _tkey))\n  then\n\t  update tig_pairs set pval = _tval from tig_users\n      where (lower(tig_users.user_id) = lower(''db-properties''))\n        AND (tig_users.uid = tig_pairs.uid)\n        AND (pkey = _tkey);\n  else\n    if not exists( select 1 from tig_users where user_id = ''db-properties'' ) then\n      perform TigAddUserPlainPw(''db-properties'', NULL);\n    end if;\n    insert into tig_pairs (pkey, pval, uid, nid)\n\t\t  select _tkey, _tval, tu.uid, tn.nid from tig_users tu  left join tig_nodes tn on tn.uid=tu.uid\n\t\t\t  where (lower(user_id) = lower(''db-properties'')  and tn.node=''root'' );\n  end if;\n  return;\nend;\n' LANGUAGE 'plpgsql';\n-- QUERY END:"
  },
  {
    "path": "src/main/database/postgresql-server-8.0.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n\\i database/postgresql-server-8.0.0-schema.sql\n\n\\i database/postgresql-server-8.0.0-sp.sql\n\n\\i database/postgresql-server-8.0.0-props.sql\n\n-- LOAD FILE: database/postgresql-server-8.0.0-schema.sql\n\n-- LOAD FILE: database/postgresql-server-8.0.0-sp.sql\n\n-- LOAD FILE: database/postgresql-server-8.0.0-props.sql\n"
  },
  {
    "path": "src/main/database/postgresql-server-8.1.0-props.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\nselect now(), ' - Setting schema version to 8.1.0';\n\n-- QUERY START:\nselect TigSetComponentVersion('server', '8.1.0');\n-- QUERY END:\n"
  },
  {
    "path": "src/main/database/postgresql-server-8.1.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n\\i database/postgresql-server-8.1.0-props.sql\n\n-- LOAD FILE: database/postgresql-server-8.1.0-props.sql\n"
  },
  {
    "path": "src/main/database/postgresql-server-8.2.0-props.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\nselect now(), ' - Setting schema version to 8.2.0';\n\n-- QUERY START:\nselect TigSetComponentVersion('server', '8.2.0');\n-- QUERY END:\n"
  },
  {
    "path": "src/main/database/postgresql-server-8.2.0-schema.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\ndo $$\nbegin\n    if exists (select 1 where (select to_regclass('public.last_login')) is not null) then\n        drop index last_login;\n    end if;\n    if exists (select 1 where (select to_regclass('public.last_logout')) is not null) then\n        drop index last_logout;\n    end if;\n    if exists (select 1 where (select to_regclass('public.online_status')) is not null) then\n        drop index online_status;\n    end if;\nend$$;\n-- QUERY END:\n\n-- QUERY START:\ndo $$\nbegin\n    if exists (select 1 from information_schema.columns where table_catalog = current_database() and table_schema = 'public' and table_name = 'tig_users' and column_name = 'last_login') then\n        alter table tig_users rename column last_login to last_used;\n    end if;\n    if exists (select 1 from information_schema.columns where table_catalog = current_database() and table_schema = 'public' and table_name = 'tig_users' and column_name = 'last_logout') then\n        alter table tig_users drop column last_logout;\n    end if;\n    if exists (select 1 from information_schema.columns where table_catalog = current_database() and table_schema = 'public' and table_name = 'tig_users' and column_name = 'online_status') then\n        alter table tig_users drop column online_status;\n    end if;\nend$$;\n-- QUERY END:\n\n-- QUERY START:\ndo $$\nbegin\n    if exists (select 1 where (select to_regclass('public.tig_offline_messages_receiver')) is not null) then\n        drop index tig_offline_messages_receiver;\n    end if;\nend$$;\n-- QUERY END:"
  },
  {
    "path": "src/main/database/postgresql-server-8.2.0-sp.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- database properties are deprecated and are being removed\n\n-- QUERY START:\ndo $$\nbegin\n    if exists( select 1 from pg_proc where proname = lower('TigPutDBProperty')) then\n        drop function TigPutDBProperty(varchar(255), text);\n    end if;\nend$$;\n-- QUERY END:\n\n-- QUERY START:\ndo $$\nbegin\n    if exists( select 1 from pg_proc where proname = lower('TigUserLogin')) then\n        drop function TigUserLogin(varchar(2049), varchar(255));\n    end if;\n    if exists( select 1 from pg_proc where proname = lower('TigUserLogout')) then\n        drop function TigUserLogout(varchar(2049));\n    end if;\n    if exists( select 1 from pg_proc where proname = lower('TigOnlineUsers')) then\n        drop function TigOnlineUsers();\n    end if;\n    if exists( select 1 from pg_proc where proname = lower('TigOfflineUsers')) then\n        drop function TigOfflineUsers();\n    end if;\n    if exists( select 1 from pg_proc where proname = lower('TigUserLoginPlainPw')) then\n        drop function TigUserLoginPlainPw(varchar(2049), varchar(255));\n    end if;\nend$$;\n-- QUERY END:\n\n-- QUERY START:\n-- It sets last_login time to the current timestamp\ncreate or replace function TigUpdateLoginTime(varchar(2049)) returns void as $$\ndeclare\n    _user_id alias for $1;\nbegin\n    update tig_users\n        set last_used = now()\n        where lower(user_id) = lower(_user_id);\n    return;\nend;\n$$ LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\ndo $$\nbegin\n    if exists( select 1 from pg_proc where proname = lower('Tig_OfflineMessages_DeleteMessage')) then\n        drop function Tig_OfflineMessages_DeleteMessage(_msg_id bigint);\n    end if;\nend$$;\n-- QUERY END:\n\n-- QUERY START:\ncreate function Tig_OfflineMessages_DeleteMessage(_msg_id bigint) returns void as $$\nbegin\n    delete from tig_offline_messages where msg_id = _msg_id;\nend;\n$$ LANGUAGE 'plpgsql';\n-- QUERY END:"
  },
  {
    "path": "src/main/database/postgresql-server-8.2.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n\\i database/postgresql-server-8.2.0-schema.sql\n\n\\i database/postgresql-server-8.2.0-sp.sql\n\n\\i database/postgresql-server-8.2.0-props.sql\n\n-- LOAD FILE: database/postgresql-server-8.2.0-schema.sql\n\n-- LOAD FILE: database/postgresql-server-8.2.0-sp.sql\n\n-- LOAD FILE: database/postgresql-server-8.2.0-props.sql\n\n"
  },
  {
    "path": "src/main/database/postgresql-server-8.3.0-props.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\nselect now(), ' - Setting schema version to 8.3.0';\n\n-- QUERY START:\nselect TigSetComponentVersion('server', '8.3.0');\n-- QUERY END:\n"
  },
  {
    "path": "src/main/database/postgresql-server-8.3.0-schema.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n--\n"
  },
  {
    "path": "src/main/database/postgresql-server-8.3.0-sp.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- database properties are deprecated and are being removed\n"
  },
  {
    "path": "src/main/database/postgresql-server-8.3.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n\\i database/postgresql-server-8.3.0-schema.sql\n\n    \\i database/postgresql-server-8.3.0-sp.sql\n\n    \\i database/postgresql-server-8.3.0-props.sql\n\n-- LOAD FILE: database/postgresql-server-8.3.0-schema.sql\n\n-- LOAD FILE: database/postgresql-server-8.3.0-sp.sql\n\n-- LOAD FILE: database/postgresql-server-8.3.0-props.sql\n\n"
  },
  {
    "path": "src/main/database/postgresql-server-8.4.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n"
  },
  {
    "path": "src/main/database/postgresql-server-8.5.0-schema.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\ndo $$\nbegin\nif exists (select 1 where (select to_regclass('public.tig_offline_messages_receiver_msg_type_index')) is null) then\n    create index tig_offline_messages_receiver_msg_type_index on tig_offline_messages (lower(receiver), msg_type);\nend if;\nend$$;\n-- QUERY END:"
  },
  {
    "path": "src/main/database/postgresql-server-8.5.0-sp.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\ndo $$\nbegin\n    if exists( select 1 from pg_proc where proname = lower('Tig_OfflineMessages_AddMessage') and pg_get_function_arguments(oid) = '_to character varying, _from character varying, _type integer, _ts timestamp without time zone, _message text, _expired timestamp without time zone, _limit bigint') then\n        drop function Tig_OfflineMessages_AddMessage(_to character varying, _from character varying, _type integer, _ts timestamp without time zone, _message text, _expired timestamp without time zone, _limit bigint);\n    end if;\nend$$;\n-- QUERY END:\n\n-- QUERY START:\ncreate or replace function Tig_OfflineMessages_AddMessage(_to varchar(2049), _from varchar(2049), _type int, _ts timestamp with time zone, _message text, _expired timestamp with time zone, _limit bigint) returns bigint as $$\ndeclare\n    _msg_count bigint;\n    _msg_id bigint;\nbegin\n    perform _msg_count = 0;\n\n    if _limit > 0  then\n        select count(msg_id) into _msg_count from tig_offline_messages where lower(receiver) = lower(_to) and lower(sender) = lower(_from);\n    end if;\n\n    if _limit = 0 or _limit > _msg_count then\n\t    insert into tig_offline_messages ( receiver, sender, msg_type, ts, message, expired )\n\t        values ( _to, _from, _type, _ts, _message, _expired )\n        select 1 into _msg_id from inserted_msg;\n    end if;\n\n    return _msg_id;\nend;\n$$ LANGUAGE 'plpgsql';\n-- QUERY END:\n\n-- QUERY START:\ndo $$\nbegin\n    if exists( select 1 from pg_proc where proname = lower('Tig_OfflineMessages_DeleteMessages')) then\ndrop function Tig_OfflineMessages_DeleteMessages(varchar(2049));\nend if;\nend$$;\n-- QUERY END:\n\n-- QUERY START:\ncreate or replace function Tig_OfflineMessages_DeleteMessages(_to varchar(2049)) returns void as $$\nbegin\n    delete from tig_offline_messages where lower(receiver) = lower(_to)\nend;\n$$ LANGUAGE 'plpgsql';\n-- QUERY END:"
  },
  {
    "path": "src/main/database/postgresql-server-8.5.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n\\i database/postgresql-server-8.5.0-schema.sql\n\n\\i database/postgresql-server-8.5.0-sp.sql\n"
  },
  {
    "path": "src/main/database/sqlserver-common-0.0.1.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\nSET QUOTED_IDENTIFIER ON\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF object_id('dbo.tig_schema_versions') IS NULL\ncreate table [dbo].[tig_schema_versions] (\n\t-- Component Name\n\t[component] [varchar](100) NOT NULL,\n\t-- Version of loaded schema\n\t[version] [varchar](100) NOT NULL,\n\t-- Time when schema was loaded last time\n\t[last_update] [datetime] NOT NULL,\n  CONSTRAINT [PK_tig_schema_versions] PRIMARY KEY CLUSTERED ( [component] ASC ) ON [PRIMARY]\n);\n-- QUERY END:\n\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigSetComponentVersion')\n  DROP PROCEDURE TigSetComponentVersion\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigGetComponentVersion')\n  DROP FUNCTION TigGetComponentVersion\n-- QUERY END:\nGO\n\n\n-- QUERY START:\n\ncreate procedure TigGetComponentVersion (@_component nvarchar(255))\nAS\n  begin\n    SELECT version FROM tig_schema_versions WHERE (component = @_component);\n  end\n-- QUERY END:\nGO\n\n-- QUERY START:\ncreate procedure dbo.TigSetComponentVersion @_component nvarchar(255), @_version nvarchar(255)\nAS\n  BEGIN\n    if exists (select 1 from dbo.tig_schema_versions where (component = @_component))\n      begin\n        UPDATE tig_schema_versions SET version = @_version, last_update = getutcdate() WHERE component = @_component;\n      end\n    else\n      begin\n        INSERT INTO tig_schema_versions (component, version, last_update) VALUES (@_component, @_version, getutcdate());\n      end\n  end\n-- QUERY END:\nGO\n"
  },
  {
    "path": "src/main/database/sqlserver-common-0.0.2.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\nSET QUOTED_IDENTIFIER ON\n-- QUERY END:\nGO\n\n-- QUERY START:\nupdate tig_schema_versions\nset version = concat(version, '-SNAPSHOT')\nwhere exists (\n    select 1\n    from tig_schema_versions\n    where\n        component = 'common'\n        and version = '0.0.1'\n) and version not like '%-SNAPSHOT%';\n-- QUERY END:\nGO"
  },
  {
    "path": "src/main/database/sqlserver-common-0.0.3.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n--\n"
  },
  {
    "path": "src/main/database/sqlserver-counter_data_logger-0.0.1.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\ncreate table tig_stats_log\n(\n  lid [bigint] IDENTITY(1, 1),\n  ts [datetime] DEFAULT getdate(),\n  hostname [nvarchar](2049) NOT NULL,\n  cpu_usage      double precision not null default 0,\n  mem_usage      double precision not null default 0,\n  uptime         bigint           not null default 0,\n  vhosts         int              not null default 0,\n  sm_packets     bigint           not null default 0,\n  muc_packets    bigint           not null default 0,\n  pubsub_packets bigint           not null default 0,\n  c2s_packets    bigint           not null default 0,\n  s2s_packets    bigint           not null default 0,\n  ext_packets    bigint           not null default 0,\n  presences      bigint           not null default 0,\n  messages       bigint           not null default 0,\n  iqs            bigint           not null default 0,\n  registered     bigint           not null default 0,\n  c2s_conns      int              not null default 0,\n  s2s_conns      int              not null default 0,\n  bosh_conns     int              not null default 0,\n  primary key (ts, hostname)\n);\n-- QUERY END:\n\n-- QUERY START:\nif not exists (select 1 from information_schema.columns where table_schema = database() and table_name = 'tig_stats_log' and column_name = 'ws2s_conns') then\n    ALTER TABLE tig_stats_log ADD `ws2s_conns` INT not null default 0\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nif not exists (select 1 from information_schema.columns where table_schema = database() and table_name = 'tig_stats_log' and column_name = 'ws2s_packets') then\n    ALTER TABLE tig_stats_log ADD `ws2s_packets` INT not null default 0\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nif not exists (select 1 from information_schema.columns where table_schema = database() and table_name = 'tig_stats_log' and column_name = 'sm_sessions') then\n    ALTER TABLE tig_stats_log ADD `sm_sessions` INT not null default 0\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nif not exists (select 1 from information_schema.columns where table_schema = database() and table_name = 'tig_stats_log' and column_name = 'sm_connections') then\n    ALTER TABLE tig_stats_log ADD `sm_connections` INT not null default 0\nend\n-- QUERY END:\nGO\n\n"
  },
  {
    "path": "src/main/database/sqlserver-installer-create-db.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START: USE database\nUSE [master]\n-- QUERY END: USE database\nGO\n\n-- QUERY START: create database\nCREATE DATABASE ${dbName};\n-- QUERY END: create database\nGO\n\n-- QUERY START: add user\nCREATE LOGIN [${dbUser}] WITH PASSWORD=N'${dbPass}', DEFAULT_DATABASE=[${dbName}]\n-- QUERY END: add user\nGO\n\n-- QUERY START: ALTER DATABASE\nIF NOT EXISTS (SELECT name FROM sys.filegroups WHERE is_default=1 AND name = N'PRIMARY') ALTER DATABASE [${dbName}] MODIFY FILEGROUP [PRIMARY] DEFAULT\n-- QUERY END: ALTER DATABASE\nGO\n\n-- QUERY START: USE DATABASE\nUSE [${dbName}]\n-- QUERY END: USE DATABASE\nGO\n\n-- QUERY START: GRANT ALL\nCREATE USER [${dbUser}] FOR LOGIN [${dbUser}]\n-- QUERY END: GRANT ALL\nGO\n\n-- QUERY START: ALTER DATABASE\nALTER USER [${dbUser}] WITH DEFAULT_SCHEMA=[dbo]\n-- QUERY END: ALTER DATABASE\nGO\n\n-- QUERY START: ALTER DATABASE\nALTER ROLE [db_owner] ADD MEMBER [${dbUser}]\n-- QUERY END: ALTER DATABASE\nGO\n"
  },
  {
    "path": "src/main/database/sqlserver-installer-post.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START: USE DATABASE\nUSE [${dbName}]\n-- QUERY END: USE DATABASE\nGO\n\n-- QUERY START: ALTER DATABASE\nALTER ROLE [db_owner] ADD MEMBER [${dbUser}]\n-- QUERY END: ALTER DATABASE\nGO\n"
  },
  {
    "path": "src/main/database/sqlserver-server-8.0.0-props.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\nSET QUOTED_IDENTIFIER ON\n-- QUERY END:\nGO\n\nselect GETDATE(), ' - Setting schema version to 8.0.0';\n\n-- QUERY START:\nexec dbo.TigPutDBProperty 'schema-version', '8.0.0';\n-- QUERY END:\nGO\n\n-- QUERY START:\nexec TigSetComponentVersion 'server', '8.0.0';\n-- QUERY END:\nGO\n"
  },
  {
    "path": "src/main/database/sqlserver-server-8.0.0-schema.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\nSET QUOTED_IDENTIFIER ON\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF object_id('dbo.tig_users') IS NULL\nCREATE TABLE [dbo].[tig_users](\n\t[uid] [bigint] IDENTITY(1,1) NOT NULL,\n\n\t-- Jabber User ID\n\t[user_id] [nvarchar](2049) NOT NULL,\n\n\t-- UserID SHA1 hash to prevent duplicate user_ids\n\t[sha1_user_id] [varbinary](32) NOT NULL,\n\t-- User password encrypted or not\n\t[user_pw] [nvarchar](255) NULL,\n\t-- Time the account has been created\n\t[acc_create_time] [datetime] DEFAULT getdate(),\n\t-- Time of the last user login\n\t[last_login] [datetime] default 0,\n\t-- Time of the last user logout\n\t[last_logout] [datetime] default 0,\n\t-- User online status, if > 0 then user is online, the value\n\t-- indicates the number of user connections.\n\t-- It is incremented on each user login and decremented on each\n\t-- user logout.\n\t[online_status] [int] default 0,\n\t-- Number of failed login attempts\n\t[failed_logins] [int] default 0,\n\t-- User status, whether the account is active or disabled\n\t-- >0 - account active, 0 - account disabled\n\t[account_status] [int] default 1,\n\t-- helper column for indexing due to limitation of SQL server\n\tuser_id_fragment AS LEFT (user_id, 256),\n\n\tCONSTRAINT [PK_tig_users] PRIMARY KEY CLUSTERED ( [uid] ASC ) ON [PRIMARY],\n\tCONSTRAINT [IX_tig_users_sha1_user_id] UNIQUE NONCLUSTERED ( [sha1_user_id] ASC ) ON [PRIMARY]\n) ON [PRIMARY]\n-- QUERY END:\n\n-- QUERY START:\nif not exists (select 1 from sys.indexes where object_id = object_id('dbo.tig_users') and name = 'IX_tig_users_account_status' )\nCREATE NONCLUSTERED INDEX [IX_tig_users_account_status] ON [dbo].[tig_users] ([account_status] ASC) ON [PRIMARY]\n-- QUERY END:\nGO\n\n-- QUERY START:\nif not exists (select 1 from sys.indexes where object_id = object_id('dbo.tig_users') and name = 'IX_tig_users_last_login' )\nCREATE NONCLUSTERED INDEX [IX_tig_users_last_login] ON [dbo].[tig_users] ([last_login] ASC) ON [PRIMARY]\n-- QUERY END:\nGO\n\n-- QUERY START:\nif not exists (select 1 from sys.indexes where object_id = object_id('dbo.tig_users') and name = 'IX_tig_users_last_logout' )\nCREATE NONCLUSTERED INDEX [IX_tig_users_last_logout] ON [dbo].[tig_users] ( [last_logout] ASC)  ON [PRIMARY]\n-- QUERY END:\nGO\n\n-- QUERY START:\nif not exists (select 1 from sys.indexes where object_id = object_id('dbo.tig_users') and name = 'IX_tig_users_online_status' )\nCREATE NONCLUSTERED INDEX [IX_tig_users_online_status] ON [dbo].[tig_users] ([online_status] ASC) ON [PRIMARY]\n-- QUERY END:\nGO\n\n-- QUERY START:\nif not exists (select 1 from sys.indexes where object_id = object_id('dbo.tig_users') and name = 'IX_tig_users_user_id_fragment' )\nCREATE NONCLUSTERED INDEX [IX_tig_users_user_id_fragment] ON [dbo].[tig_users] ( [user_id_fragment] ASC) ON [PRIMARY]\n-- QUERY END:\nGO\n\n-- QUERY START:\nif not exists (select 1 from sys.indexes where object_id = object_id('dbo.tig_users') and name = 'IX_tig_users_user_pw' )\nCREATE NONCLUSTERED INDEX [IX_tig_users_user_pw] ON [dbo].[tig_users] ([user_pw] ASC) ON [PRIMARY]\n-- QUERY END:\nGO\n\n\n\n-- QUERY START:\nIF object_id('dbo.tig_nodes') IS NULL\nCREATE TABLE [dbo].[tig_nodes](\n\t[nid] [bigint] IDENTITY(1,1) NOT NULL,\n\t[parent_nid] [bigint] NULL,\n\t[uid] [bigint] NOT NULL,\n\t[node] [nvarchar](255) NOT NULL,\n CONSTRAINT [PK_tig_nodes_nid] PRIMARY KEY CLUSTERED ( [nid] ASC ) ON [PRIMARY],\n CONSTRAINT [IX_tnode] UNIQUE NONCLUSTERED ( [parent_nid] ASC, [uid] ASC, [node] ASC ) ON [PRIMARY]\n) ON [PRIMARY]\n-- QUERY END:\nGO\n\n-- QUERY START:\nif not exists (select 1 from sys.indexes where object_id = object_id('dbo.tig_nodes') and name = 'IX_tig_nodes_node' )\nCREATE NONCLUSTERED INDEX [IX_tig_nodes_node] ON [dbo].[tig_nodes] ( [node] ASC) ON [PRIMARY]\n-- QUERY END:\nGO\n\n-- QUERY START:\nif not exists (select 1 from sys.indexes where object_id = object_id('dbo.tig_nodes') and name = 'IX_tig_nodes_parent_nid' )\nCREATE NONCLUSTERED INDEX [IX_tig_nodes_parent_nid] ON [dbo].[tig_nodes] ( [parent_nid] ASC ) ON [PRIMARY]\n-- QUERY END:\nGO\n\n-- QUERY START:\nif not exists (select 1 from sys.indexes where object_id = object_id('dbo.tig_nodes') and name = 'IX_tig_nodes_uid' )\nCREATE NONCLUSTERED INDEX [IX_tig_nodes_uid] ON [dbo].[tig_nodes] ( [uid] ASC ) ON [PRIMARY]\n-- QUERY END:\nGO\n\n-- QUERY START:\nif object_id('dbo.FK_tig_nodes_tig_users') is null\nALTER TABLE [dbo].[tig_nodes]  WITH CHECK ADD  CONSTRAINT [FK_tig_nodes_tig_users] FOREIGN KEY([uid])\nREFERENCES [dbo].[tig_users] ([uid])\n-- QUERY END:\nGO\n\n-- QUERY START:\nALTER TABLE [dbo].[tig_nodes] CHECK CONSTRAINT [FK_tig_nodes_tig_users]\n-- QUERY END:\nGO\n\n\n-- QUERY START:\nIF object_id('dbo.tig_pairs') IS NULL\nCREATE TABLE [dbo].[tig_pairs](\n\t[pid] [bigint] IDENTITY(1,1) NOT NULL,\n\t[nid] [bigint] NULL,\n\t[uid] [bigint] NOT NULL,\n\t[pkey] [nvarchar](255) NOT NULL,\n\t[pval] [ntext] NULL,\n    CONSTRAINT [PK_tig_pairs] PRIMARY KEY CLUSTERED ( [pid] ASC ) ON [PRIMARY]\n) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]\n-- QUERY END:\nGO\n\n-- QUERY START:\nif not exists (select 1 from sys.indexes where object_id = object_id('dbo.tig_pairs') and name = 'IX_tig_pairs_nid' )\nCREATE NONCLUSTERED INDEX [IX_tig_pairs_nid] ON [dbo].[tig_pairs] ( [nid] ASC ) ON [PRIMARY]\n-- QUERY END:\nGO\n\n-- QUERY START:\nif not exists (select 1 from sys.indexes where object_id = object_id('dbo.tig_pairs') and name = 'IX_tig_pairs_pkey' )\nCREATE NONCLUSTERED INDEX [IX_tig_pairs_pkey] ON [dbo].[tig_pairs] ( [pkey] ASC ) ON [PRIMARY]\n-- QUERY END:\nGO\n\n-- QUERY START:\nif not exists (select 1 from sys.indexes where object_id = object_id('dbo.tig_pairs') and name = 'IX_tig_pairs_uid' )\nCREATE NONCLUSTERED INDEX [IX_tig_pairs_uid] ON [dbo].[tig_pairs] ( [uid] ASC ) ON [PRIMARY]\n-- QUERY END:\nGO\n\n-- QUERY START:\nif object_id('dbo.FK_tig_pairs_tig_nodes') is null\nALTER TABLE [dbo].[tig_pairs]  WITH CHECK ADD  CONSTRAINT [FK_tig_pairs_tig_nodes] FOREIGN KEY([nid])\nREFERENCES [dbo].[tig_nodes] ([nid])\n-- QUERY END:\nGO\n\n-- QUERY START:\nALTER TABLE [dbo].[tig_pairs] CHECK CONSTRAINT [FK_tig_pairs_tig_nodes]\n-- QUERY END:\nGO\n\n-- QUERY START:\nif object_id('dbo.FK_tig_pairs_tig_users') is null\nALTER TABLE [dbo].[tig_pairs]  WITH CHECK ADD  CONSTRAINT [FK_tig_pairs_tig_users] FOREIGN KEY([uid])\nREFERENCES [dbo].[tig_users] ([uid])\n-- QUERY END:\nGO\n\n-- QUERY START:\nALTER TABLE [dbo].[tig_pairs] CHECK CONSTRAINT [FK_tig_pairs_tig_users]\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF object_id('dbo.tig_offline_messages') IS NULL\n    create table tig_offline_messages (\n        msg_id [bigint] IDENTITY(1,1),\n        ts [datetime] DEFAULT GETUTCDATE(),\n        expired [datetime],\n        sender nvarchar(2049),\n        sender_sha1 varbinary(20),\n        receiver nvarchar(2049) not null,\n        receiver_sha1 varbinary(20) not null,\n        msg_type int not null default 0,\n        message nvarchar(max) not null,\n\n        primary key (msg_id)\n    );\n-- QUERY END:\nGO\n-- QUERY START:\nif object_id('dbo.tig_broadcast_messages') is null\n    create table tig_broadcast_messages (\n\t\tid varchar(128) not null,\n\t    expired datetime not null,\n\t    msg nvarchar(max) not null,\n\t    primary key (id)\n\t);\n-- QUERY END:\nGO\n\n-- QUERY START:\nif object_id('dbo.tig_broadcast_jids') is null\n    create table tig_broadcast_jids (\n        jid_id [bigint] IDENTITY(1,1),\n        jid [nvarchar](2049) not null,\n        jid_sha1 [varbinary](20) not null,\n\n        primary key (jid_id)\n    );\n-- QUERY END:\nGO\n\n-- QUERY START:\nif exists (select 1 from sys.indexes where object_id = object_id('dbo.tig_offline_messages') and name = 'IX_tig_broadcast_jids_jid_sha1' )\n    create index IX_tig_broadcast_jids_jid_sha1 on tig_broadcast_jids (jid_sha1);\n-- QUERY END:\nGO\n\n-- QUERY START:\nif object_id('dbo.tig_broadcast_recipients') is null\n    create table tig_broadcast_recipients (\n        msg_id [varchar](128) not null references tig_broadcast_messages(id),\n        jid_id [bigint] not null references tig_broadcast_jids(jid_id),\n        primary key (msg_id, jid_id)\n    );\n-- QUERY END:\nGO\n\n-- QUERY START:\nif not exists (select 1 from sys.indexes where object_id = object_id('dbo.tig_offline_messages') and name = 'IX_tig_offline_messages_expired' )\n    create index IX_tig_offline_messages_expired on [dbo].[tig_offline_messages] (expired);\n-- QUERY END:\nGO\n\n-- QUERY START:\nif not exists (select 1 from sys.indexes where object_id = object_id('dbo.tig_offline_messages') and name = 'IX_tig_offline_messages_receiver_sha1' )\n    create index IX_tig_offline_messages_receiver_sha1 on [dbo].[tig_offline_messages] (receiver_sha1);\n-- QUERY END:\nGO\n\n-- QUERY START:\nif not exists (select 1 from sys.indexes where object_id = object_id('dbo.tig_offline_messages') and name = 'IX_tig_offline_messages_receiver_sha1_sender_sha1' )\n    create index IX_tig_offline_messages_receiver_sha1_sender_sha1 on [dbo].[tig_offline_messages] (receiver_sha1, sender_sha1);\n-- QUERY END:\nGO\n\n-- QUERY START:\nif object_id('dbo.tig_cluster_nodes') is null\n    create table [dbo].[tig_cluster_nodes] (\n        hostname nvarchar(450) not null,\n        secondary nvarchar(512),\n        password nvarchar(255) not null,\n\t\tlast_update [datetime] default getutcdate(),\n\t\tport int,\n\t\tcpu_usage double precision not null,\n\t\tmem_usage double precision not null,\n\n\t\tconstraint [PK_tig_cluster_nodes] PRIMARY KEY ClUSTERED ( [hostname] asc ) on [PRIMARY],\n\t\tconstraint [IX_tig_cluster_nodes_hostname] unique nonclustered ( [hostname] asc) on [PRIMARY]\n    );\n-- QUERY END:\nGO\n\n-- ------------- Credentials support\n-- QUERY START:\nif object_id('dbo.tig_user_credentials') is null\n    create table tig_user_credentials (\n        uid bigint not null references tig_users(uid),\n        username nvarchar(2049) not null,\n        username_sha1 varbinary(32) not null,\n        mechanism nvarchar(128) not null,\n        value nvarchar(max) not null,\n\n        primary key (uid, username_sha1, mechanism)\n    );\n-- QUERY END:\nGO\n\n-- QUERY START:\nif not exists (select 1 from tig_user_credentials) and exists (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigGetDBProperty')\nbegin\n    execute('insert into tig_user_credentials (uid, username, username_sha1, mechanism, value)\n        select uid, ''default'', HASHBYTES(''SHA1'', ''default''), coalesce(TigGetDBProperty(''password-encoding''), ''PLAIN''), user_pw\n        from tig_users\n        where\n            user_pw is not null;\n\n    update tig_users set user_pw = null where user_pw is not null;');\nend\n-- QUERY END:\nGO\n"
  },
  {
    "path": "src/main/database/sqlserver-server-8.0.0-sp.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\nSET QUOTED_IDENTIFIER ON\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigInitdb')\nDROP PROCEDURE TigInitdb\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigAddUser')\nDROP PROCEDURE TigAddUser\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigGetUserDBUid')\nDROP PROCEDURE TigGetUserDBUid\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigUpdatePassword')\nDROP PROCEDURE TigUpdatePassword\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigUpdatePasswordPlainPwRev')\nDROP PROCEDURE TigUpdatePasswordPlainPwRev\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigOnlineUsers')\nDROP PROCEDURE TigOnlineUsers\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigOfflineUsers')\nDROP PROCEDURE TigOfflineUsers\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigAllUsers')\nDROP PROCEDURE TigAllUsers\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigAllUsersCount')\nDROP PROCEDURE TigAllUsersCount\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigDisableAccount')\nDROP PROCEDURE TigDisableAccount\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigEnableAccount')\nDROP PROCEDURE TigEnableAccount\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigActiveAccounts')\nDROP PROCEDURE TigActiveAccounts\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigDisabledAccounts')\nDROP PROCEDURE TigDisabledAccounts\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigAddNode')\nDROP PROCEDURE TigAddNode\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'FN' AND name = 'TigGetDBProperty')\nDROP FUNCTION TigGetDBProperty\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigUpdatePairs')\nDROP PROCEDURE TigUpdatePairs\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'FN' AND name = 'InlineMax')\nDROP FUNCTION InlineMax\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- The initialization of the database.\n-- The procedure should be called manually somehow before starting the\n-- server. In theory the server could call the procedure automatically\n-- at the startup time but I don't know yet how to solve the problem\n-- with multiple cluster nodes starting at later time when the server\n-- is already running.\ncreate procedure dbo.TigInitdb\nAS\nbegin\n  update dbo.tig_users set online_status = 0;\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- Database properties get - function\ncreate function TigGetDBProperty(@_tkey nvarchar(255)) returns nvarchar(MAX)\nAS\nbegin\n--Declare @_result nvarchar(MAX);\nreturn (select pval  from dbo.tig_pairs AS p, dbo.tig_users AS u\n\t\twhere (pkey = @_tkey) AND (sha1_user_id = HASHBYTES('SHA1', LOWER(N'db-properties')))\n\t\t\t\t\tAND (p.uid = u.uid));\n\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- Add a new user to the database assuming the user password is already\n-- encoded properly according to the database settings.\n-- If password is not encoded TigAddUserPlainPw should be used instead.\ncreate procedure dbo.TigAddUser\n\t@_user_id nvarchar(2049),\n\t@_user_pw nvarchar(255)\nAS\n\tbegin\n\t\tSET NOCOUNT ON;\n\n\t\tdeclare @res_uid bigint;\n\n\t\tinsert into dbo.tig_users (user_id, sha1_user_id, user_pw)\n\t\t\tvalues (@_user_id, HASHBYTES('SHA1', LOWER(@_user_id)) , @_user_pw);\n\n\t\tset  @res_uid = (select SCOPE_IDENTITY());\n\n\t\tif (@res_uid is not NULL)\n\t\t\tinsert into dbo.tig_nodes (parent_nid, uid, node)\n\t\t\t\tvalues (NULL, @res_uid, N'root');\n\n\t\tif (@_user_pw is NULL)\n\t\t\tupdate dbo.tig_users set account_status = -1 where uid = @res_uid;\n\n\t\tselect @res_uid as uid;\n\tend;\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- Low level database user id as big number. Used only for performance reasons\n-- and save database space. Besides JID is too large to server as UID\ncreate procedure dbo.TigGetUserDBUid\n\t@_user_id nvarchar(2049)\nAS\nbegin\n\tselect uid from dbo.tig_users where sha1_user_id = hashbytes('SHA1', lower(@_user_id));\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- Update user password\ncreate procedure dbo.TigUpdatePassword\n\t@_user_id nvarchar(2049),\n\t@_user_pw nvarchar(255)\nAS\nbegin\n\tupdate dbo.tig_users set user_pw = @_user_pw where sha1_user_id = hashbytes('SHA1', lower(@_user_id));\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- Variant of TigUpdatePasswordPlainPw SP with parameters in reverse order.\n-- Some implementations require the parameters to be in the same order as\n-- the update query.\ncreate procedure dbo.TigUpdatePasswordPlainPwRev\n\t@_user_pw nvarchar(255),\n\t@_user_id nvarchar(2049)\nAS\nbegin\n\texec TigUpdatePasswordPlainPw @_user_id, @_user_pw;\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- List all online users\ncreate procedure dbo.TigOnlineUsers\nAS\nbegin\n\tselect user_id, last_login, last_logout, online_status, failed_logins, account_status\n\t\tfrom dbo.tig_users where online_status > 0;\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- List all offline users\ncreate procedure dbo.TigOfflineUsers\nAS\nbegin\n\tselect user_id, last_login, last_logout, online_status, failed_logins, account_status\n\t\tfrom dbo.tig_users where online_status = 0;\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- List of all users in database\ncreate procedure dbo.TigAllUsers\nAS\nbegin\n\tselect user_id, last_login, last_logout, online_status, failed_logins, account_status\n\t\tfrom dbo.tig_users;\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- All users count\ncreate procedure dbo.TigAllUsersCount\nAS\nbegin\n\tselect count(*) from dbo.tig_users;\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- helper function\ncreate function dbo.InlineMax(@val1 int, @val2 int)\nreturns int\nas\nbegin\n  if @val1 > @val2\n    return @val1\n  return isnull(@val2,@val1)\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- Disable user account\ncreate procedure dbo.TigDisableAccount\n\t@_user_id nvarchar(2049)\nAS\nbegin\n\tupdate dbo.tig_users set account_status = 0 where sha1_user_id = hashbytes('SHA1', lower(@_user_id));\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- Enable user account\ncreate procedure dbo.TigEnableAccount\n\t@_user_id nvarchar(2049)\nAS\nbegin\n\tupdate dbo.tig_users set account_status = 1 where sha1_user_id = hashbytes('SHA1', lower(@_user_id));\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- Get list of all active user accounts\ncreate procedure dbo.TigActiveAccounts\nAS\nbegin\n\tselect user_id, last_login, last_logout, online_status, failed_logins, account_status\n\t\tfrom dbo.tig_users where account_status > 0;\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- Get list of all disabled user accounts\ncreate procedure dbo.TigDisabledAccounts\nAS\nbegin\n\tselect user_id, last_login, last_logout, online_status, failed_logins, account_status\n\t\tfrom dbo.tig_users where account_status = 0;\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- Helper procedure for adding a new node\ncreate procedure dbo.TigAddNode\n@_parent_nid bigint,\n@_uid bigint,\n@_node nvarchar(255)\nAS\nbegin\n\tSET NOCOUNT ON;\n\n\tinsert into dbo.tig_nodes (parent_nid, uid, node)\n\t\tvalues (@_parent_nid, @_uid, @_node);\n\tselect SCOPE_IDENTITY() as nid;\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- Procedure to efficiently and safely update data in tig_pairs table\ncreate procedure dbo.TigUpdatePairs\n@_nid bigint,\n@_uid bigint,\n@_tkey nvarchar(255),\n@_tval ntext\nAS\nbegin\n  if exists(SELECT 1 FROM dbo.tig_pairs WHERE nid = @_nid AND uid = @_uid AND pkey = @_tkey)\n    UPDATE dbo.tig_pairs SET pval = @_tval WHERE nid = @_nid AND uid = @_uid AND pkey = @_tkey;\n  ELSE\n    INSERT INTO dbo.tig_pairs (nid, uid, pkey, pval) VALUES (@_nid, @_uid, @_tkey, @_tval);\nEND\n-- QUERY END:\nGO\n\n\n\n\n\n-- QUERY START:\nSET QUOTED_IDENTIFIER ON\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigUpdateLoginTime')\nDROP PROCEDURE TigUpdateLoginTime\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- It sets last_login time to the current timestamp\ncreate procedure dbo.TigUpdateLoginTime\n\t@_user_id nvarchar(2049)\nAS\nbegin\n\tupdate dbo.tig_users\n\t\tset last_login = GETUTCDATE()\n\t\twhere user_id = @_user_id;\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigUserLogin')\nDROP PROCEDURE TigUserLogin\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- Perforrm user login. It returns user_id uppon success and NULL\n-- on failure.\n-- If the login is successful it also increases online_status and sets\n-- last_login time to the current timestamp\ncreate procedure dbo.TigUserLogin\n\t@_user_id nvarchar(2049),\n\t@_user_pw nvarchar(255)\nAS\nbegin\n\tif exists(select 1 from dbo.tig_users\n\t\twhere (account_status > 0) AND (sha1_user_id = hashbytes('SHA1', lower(@_user_id)))\n\t\t\tAND (user_pw = @_user_pw) AND (user_id = @_user_id))\n\t\tbegin\n\t\tupdate dbo.tig_users\n\t\t\tset online_status = online_status + 1, last_login = GETUTCDATE()\n\t\t\t\twhere sha1_user_id = hashbytes('SHA1', lower(@_user_id));\n\t\t\tselect @_user_id as user_id;\n\t\tend\n\telse\n\t\tbegin\n\t\t\tupdate dbo.tig_users set failed_logins = failed_logins + 1 where sha1_user_id = hashbytes('SHA1', lower(@_user_id));\n\t\t\tselect NULL as user_id;\n\t\tend\n\tend\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigUserLogout')\nDROP PROCEDURE TigUserLogout\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- It decreases online_status and sets last_logout time to the current timestamp\ncreate procedure dbo.TigUserLogout\n\t@_user_id nvarchar(2049)\nAS\nbegin\n\tupdate dbo.tig_users\n\t\tset online_status = dbo.InlineMax(online_status - 1, 0),\n\t\t\tlast_logout = GETUTCDATE()\n\t\twhere user_id = @_user_id;\nend\n-- QUERY END:\nGO\n\n-- ------------ Offline Messages\n-- QUERY START:\nif exists (select 1 from sys.objects where type = 'P' and name = 'Tig_OfflineMessages_AddMessage')\n    drop procedure [dbo].[Tig_OfflineMessages_AddMessage];\n-- QUERY END:\nGO\n\n-- QUERY START:\ncreate procedure [dbo].[Tig_OfflineMessages_AddMessage]\n    @_to nvarchar(2049),\n    @_from nvarchar(2049),\n    @_type int,\n    @_ts datetime,\n    @_message nvarchar(max),\n    @_expired datetime,\n    @_limit bigint\nas\nbegin\n    set nocount on;\n    declare\n        @_msg_count bigint,\n        @_msg_id bigint;\n    set @_msg_count = 0;\n\n    if @_limit > 0\n        select @_msg_count = count(msg_id) from tig_offline_messages where receiver_sha1 = HASHBYTES('SHA1', lower(@_to)) and sender_sha1 = HASHBYTES('SHA1', lower(@_from));\n\n    if @_limit = 0 or @_limit > @_msg_count\n    begin\n\t    insert into tig_offline_messages ( receiver, receiver_sha1, sender, sender_sha1, msg_type, ts, message, expired )\n\t        select @_to, HASHBYTES('SHA1', lower(@_to)), @_from, HASHBYTES('SHA1', lower(@_from)), @_type, @_ts, @_message, @_expired;\n\t    select @_msg_id = @@IDENTITY;\n\tend\n\n\tselect @_msg_id as msg_id;\n\tset nocount off;\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nif exists (select 1 from sys.objects where type = 'P' and name = 'Tig_OfflineMessages_GetMessages')\n    drop procedure [dbo].[Tig_OfflineMessages_GetMessages];\n-- QUERY END:\nGO\n\n-- QUERY START:\ncreate procedure [dbo].[Tig_OfflineMessages_GetMessages]\n    @_to nvarchar(2049)\nas\nbegin\n    select om.message, om.msg_id\n        from tig_offline_messages om\n        where om.receiver_sha1 = HASHBYTES('SHA1', lower(@_to));\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nif exists (select 1 from sys.objects where type = 'P' and name = 'Tig_OfflineMessages_GetMessagesByIds')\n    drop procedure [dbo].[Tig_OfflineMessages_GetMessagesByIds];\n-- QUERY END:\nGO\n\n-- QUERY START:\ncreate procedure [dbo].[Tig_OfflineMessages_GetMessagesByIds]\n    @_to nvarchar(2049),\n    @_msg_id1 nvarchar(50),\n    @_msg_id2 nvarchar(50),\n    @_msg_id3 nvarchar(50),\n    @_msg_id4 nvarchar(50)\nas\nbegin\n    select om.message, om.msg_id\n    from tig_offline_messages om\n    where om.receiver_sha1 = HASHBYTES('SHA1', lower(@_to))\n        and (\n            (@_msg_id1 is not null and om.msg_id = @_msg_id1)\n            or (@_msg_id2 is not null and om.msg_id = @_msg_id2)\n            or (@_msg_id3 is not null and om.msg_id = @_msg_id3)\n            or (@_msg_id4 is not null and om.msg_id = @_msg_id4)\n        );\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nif exists (select 1 from sys.objects where type = 'P' and name = 'Tig_OfflineMessages_GetMessagesCount')\n    drop procedure [dbo].[Tig_OfflineMessages_GetMessagesCount];\n-- QUERY END:\nGO\n\n-- QUERY START:\ncreate procedure [dbo].[Tig_OfflineMessages_GetMessagesCount]\n    @_to nvarchar(2049)\nas\nbegin\n    select om.msg_type, count(om.msg_type)\n        from tig_offline_messages om\n        where om.receiver_sha1 = HASHBYTES('SHA1', lower(@_to))\n        group by om.msg_type;\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nif exists (select 1 from sys.objects where type = 'P' and name = 'Tig_OfflineMessages_ListMessages')\n    drop procedure [dbo].[Tig_OfflineMessages_ListMessages];\n-- QUERY END:\nGO\n\n-- QUERY START:\ncreate procedure [dbo].[Tig_OfflineMessages_ListMessages]\n    @_to nvarchar(2049)\nas\nbegin\n    select om.msg_id, om.msg_type, om.sender\n        from tig_offline_messages om\n        where om.receiver_sha1 = HASHBYTES('SHA1', lower(@_to));\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nif exists (select 1 from sys.objects where type = 'P' and name = 'Tig_OfflineMessages_DeleteMessages')\n    drop procedure [dbo].[Tig_OfflineMessages_DeleteMessages];\n-- QUERY END:\nGO\n\n-- QUERY START:\ncreate procedure  [dbo].[Tig_OfflineMessages_DeleteMessages]\n    @_to nvarchar(2049)\nas\nbegin\n    set nocount on;\n    delete from tig_offline_messages where receiver_sha1 = HASHBYTES('SHA1', lower(@_to))\n    select @@ROWCOUNT as affected_rows;\n    set nocount off;\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nif exists (select 1 from sys.objects where type = 'P' and name = 'Tig_OfflineMessages_DeleteMessagesByIds')\n    drop procedure [dbo].[Tig_OfflineMessages_DeleteMessagesByIds];\n-- QUERY END:\nGO\n\n-- QUERY START:\ncreate procedure [dbo].[Tig_OfflineMessages_DeleteMessagesByIds]\n    @_to nvarchar(2049),\n    @_msg_id1 nvarchar(50),\n    @_msg_id2 nvarchar(50),\n    @_msg_id3 nvarchar(50),\n    @_msg_id4 nvarchar(50)\nas\nbegin\n    set nocount on;\n    delete from tig_offline_messages\n    where receiver_sha1 = HASHBYTES('SHA1', lower(@_to))\n        and (\n            (@_msg_id1 is not null and msg_id = @_msg_id1)\n            or (@_msg_id2 is not null and msg_id = @_msg_id2)\n            or (@_msg_id3 is not null and msg_id = @_msg_id3)\n            or (@_msg_id4 is not null and msg_id = @_msg_id4)\n        );\n    select @@ROWCOUNT as affected_rows;\n    set nocount off;\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nif exists (select 1 from sys.objects where type = 'P' and name = 'Tig_OfflineMessages_DeleteMessage')\n    drop procedure [dbo].[Tig_OfflineMessages_DeleteMessage];\n-- QUERY END:\nGO\n\n-- QUERY START:\ncreate procedure [dbo].[Tig_OfflineMessages_DeleteMessage]\n    @_msg_id bigint\nas\nbegin\n    set nocount on;\n    delete from tig_offline_messages where msg_id = @_msg_id;\n    select @@ROWCOUNT as affected_rows;\n    set nocount off;\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nif exists (select 1 from sys.objects where type = 'P' and name = 'Tig_OfflineMessages_GetExpiredMessages')\n    drop procedure [dbo].[Tig_OfflineMessages_GetExpiredMessages];\n-- QUERY END:\nGO\n\n-- QUERY START:\ncreate procedure [dbo].[Tig_OfflineMessages_GetExpiredMessages]\n    @_limit int\nas\nbegin\n    select top (@_limit) om.msg_id, om.expired, om.message\n        from tig_offline_messages om\n        where om.expired is not null\n        order by om.expired asc;\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nif exists (select 1 from sys.objects where type = 'P' and name = 'Tig_OfflineMessages_GetExpiredMessagesBefore')\n    drop procedure [dbo].[Tig_OfflineMessages_GetExpiredMessagesBefore];\n-- QUERY END:\nGO\n\n-- QUERY START:\ncreate procedure [dbo].[Tig_OfflineMessages_GetExpiredMessagesBefore]\n    @_expired datetime\nas\nbegin\n    select om.msg_id, om.expired, om.message\n        from tig_offline_messages om\n        where om.expired is not null\n            and (@_expired is null or om.expired <= @_expired)\n        order by om.expired asc;\nend\n-- QUERY END:\nGO\n\n-- ------------ Broadcast Messages\n-- QUERY START:\nif exists (select 1 from sys.objects where type = 'P' and name = 'Tig_BroadcastMessages_AddMessage')\n    drop procedure [dbo].[Tig_BroadcastMessages_AddMessage];\n-- QUERY END:\nGO\n\n-- QUERY START:\ncreate procedure [dbo].[Tig_BroadcastMessages_AddMessage]\n    @_msg_id varchar(128),\n    @_expired datetime,\n    @_msg nvarchar(max)\nas\nbegin\n    if not exists (select 1 from tig_broadcast_messages where id = @_msg_id)\n    begin\n        insert into tig_broadcast_messages (id, expired, msg)\n            values (@_msg_id, @_expired, @_msg);\n    end\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nif exists (select 1 from sys.objects where type = 'P' and name = 'Tig_BroadcastMessages_AddMessageRecipient')\n    drop procedure [dbo].[Tig_BroadcastMessages_AddMessageRecipient];\n-- QUERY END:\nGO\n\n-- QUERY START:\ncreate procedure [dbo].[Tig_BroadcastMessages_AddMessageRecipient]\n    @_msg_id varchar(128),\n    @_jid nvarchar(2049)\nas\nbegin\n    declare @ErrorMessage nvarchar(max), @ErrorSeverity int, @ErrorState int;\n    declare @_jid_sha1 varbinary(20),\n            @_jid_id bigint;\n\n\n    set @_jid_sha1 = HASHBYTES('SHA1', lower(@_jid));\n\n    select @_jid_id = jid_id from tig_broadcast_jids where jid_sha1 = @_jid_sha1;\n    if @_jid_id is null\n    begin\n        begin try\n            insert into tig_broadcast_jids (jid, jid_sha1) values (@_jid, @_jid_sha1);\n            select @_jid_id = @@IDENTITY;\n        end try\n        begin catch\n            if error_number() = 2627\n                select @_jid_id = jid_id from tig_broadcast_jids where jid_sha1 = @_jid_sha1;\n            else\n            begin\n                select @ErrorMessage = ERROR_MESSAGE() + ' Line ' + cast(ERROR_LINE() as nvarchar(5)), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE();\n                raiserror (@ErrorMessage, @ErrorSeverity, @ErrorState);\n            end\n        end catch\n    end\n\n    begin try\n        insert into tig_broadcast_recipients (msg_id, jid_id)\n            select @_msg_id, @_jid_id where not exists (\n                select 1 from tig_broadcast_recipients br where br.msg_id = @_msg_id and br.jid_id = @_jid_id\n            );\n    end try\n    begin catch\n        if error_number() <> 2627\n        begin\n            select @ErrorMessage = ERROR_MESSAGE() + ' Line ' + cast(ERROR_LINE() as nvarchar(5)), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE();\n            raiserror (@ErrorMessage, @ErrorSeverity, @ErrorState);\n        end\n    end catch\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nif exists (select 1 from sys.objects where type = 'P' and name = 'Tig_BroadcastMessages_GetMessages')\n    drop procedure [dbo].[Tig_BroadcastMessages_GetMessages];\n-- QUERY END:\nGO\n\n-- QUERY START:\ncreate procedure [dbo].[Tig_BroadcastMessages_GetMessages]\n    @_expired datetime\nas\nbegin\n    select id, expired, msg\n    from tig_broadcast_messages\n    where expired >= @_expired;\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nif exists (select 1 from sys.objects where type = 'P' and name = 'Tig_BroadcastMessages_GetMessageRecipients')\n    drop procedure [dbo].[Tig_BroadcastMessages_GetMessageRecipients];\n-- QUERY END:\nGO\n\n-- QUERY START:\ncreate procedure [dbo].[Tig_BroadcastMessages_GetMessageRecipients]\n    @_msg_id varchar(128)\nas\nbegin\n    select j.jid\n    from tig_broadcast_recipients r\n    inner join tig_broadcast_jids j on j.jid_id = r.jid_id\n    where r.msg_id = @_msg_id;\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigUpdateAccountStatus')\nDROP PROCEDURE [dbo].[TigUpdateAccountStatus]\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- Upate account status\nCREATE PROCEDURE [dbo].[TigUpdateAccountStatus]\n        @_user_id NVARCHAR(2049),\n        @_status  INT\nAS\n    BEGIN\n        UPDATE dbo.tig_users\n        SET account_status = @_status\n        WHERE user_id = @_user_id;\n    END\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigAccountStatus')\n    DROP PROCEDURE [dbo].[TigAccountStatus]\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- Returns account_status\nCREATE PROCEDURE [dbo].[TigAccountStatus]\n        @_user_id NVARCHAR(2049)\nAS\n    BEGIN\n        SELECT account_status\n        FROM tig_users\n        WHERE user_id = @_user_id;\n    END\n-- QUERY END:\nGO\n\n-- ------------- Credentials support\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigUserCredential_Update')\nDROP PROCEDURE TigUserCredential_Update\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- It sets last_login time to the current timestamp\ncreate procedure dbo.TigUserCredential_Update\n\t@_user_id nvarchar(2049),\n\t@_username nvarchar(2049),\n\t@_mechanism nvarchar(128),\n\t@_value nvarchar(max)\nAS\nbegin\n    declare @_uid bigint;\n    declare @_username_sha1 varbinary(32);\n\n    select @_uid = uid, @_username_sha1 = HASHBYTES('SHA1', @_username) from tig_users where sha1_user_id = HASHBYTES('SHA1', lower(@_user_id));\n    if @_uid is not null\n    begin\n        update tig_user_credentials set value = @_value where uid = @_uid and username_sha1 = @_username_sha1 and mechanism = @_mechanism;\n        if @@ROWCOUNT = 0\n        begin\n            begin try\n                insert into tig_user_credentials (uid, username, username_sha1, mechanism, value)\n                    select @_uid, @_username, @_username_sha1, @_mechanism, @_value\n                    where not exists (\n                        select 1 from tig_user_credentials\n                        where\n                            uid = @_uid\n                            and username_sha1 = @_username_sha1\n                            and mechanism = @_mechanism\n                    );\n            end try\n            begin catch\n            \tIF ERROR_NUMBER() <> 2627\n                declare @ErrorMessage nvarchar(max), @ErrorSeverity int, @ErrorState int;\n                select @ErrorMessage = ERROR_MESSAGE() + ' Line ' + cast(ERROR_LINE() as nvarchar(5)), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE();\n\t\t\t\traiserror (@ErrorMessage, @ErrorSeverity, @ErrorState);\n            end catch\n        end\n    end\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigUserCredentials_Get')\nDROP PROCEDURE TigUserCredentials_Get\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- It sets last_login time to the current timestamp\ncreate procedure dbo.TigUserCredentials_Get\n\t@_user_id nvarchar(2049),\n\t@_username nvarchar(2049)\nAS\nbegin\n    select c.mechanism, c.value, u.account_status\n    from tig_users u\n    inner join tig_user_credentials c on c.uid = u.uid\n    where\n        u.sha1_user_id = HASHBYTES('SHA1', lower(@_user_id))\n        and c.username_sha1 = HASHBYTES('SHA1', @_username);\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigUserUsernames_Get')\nDROP PROCEDURE TigUserUsernames_Get\n-- QUERY END:\nGO\n\n-- QUERY START:\ncreate procedure dbo.TigUserUsernames_Get\n\t@_user_id nvarchar(2049)\nAS\nbegin\n    select distinct\n        c.username\n    from tig_users u\n    inner join tig_user_credentials c on c.uid = u.uid\n    where\n        u.sha1_user_id = HASHBYTES('SHA1', lower(@_user_id));\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigUserCredential_Remove')\nDROP PROCEDURE TigUserCredential_Remove\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- It sets last_login time to the current timestamp\ncreate procedure dbo.TigUserCredential_Remove\n\t@_user_id nvarchar(2049),\n\t@_username nvarchar(2049)\nAS\nbegin\n    declare @_uid bigint;\n    declare @_username_sha1 varbinary(32);\n\n    select @_uid = uid, @_username_sha1 = HASHBYTES('SHA1', @_username) from tig_users where sha1_user_id = HASHBYTES('SHA1', lower(@_user_id));\n\n    delete from tig_user_credentials\n    where\n        uid = @_uid\n        and username_sha1 = @_username_sha1;\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigAddUserPlainPw')\nDROP PROCEDURE TigAddUserPlainPw\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- Add a new user to the database assuming the user password is already\n-- encoded properly according to the database settings.\n-- If password is not encoded TigAddUserPlainPw should be used instead.\ncreate procedure dbo.TigAddUserPlainPw\n\t@_user_id nvarchar(2049),\n\t@_user_pw nvarchar(255)\nAS\n\tbegin\n\t\tSET NOCOUNT ON;\n\n\t\tdeclare @res_uid bigint;\n\n\t\tinsert into dbo.tig_users (user_id, sha1_user_id)\n\t\t\tvalues (@_user_id, HASHBYTES('SHA1', LOWER(@_user_id)));\n\n\t\tset  @res_uid = (select SCOPE_IDENTITY());\n\n\t\tif (@res_uid is not NULL)\n\t\t\tinsert into dbo.tig_nodes (parent_nid, uid, node)\n\t\t\t\tvalues (NULL, @res_uid, N'root');\n\n\t\tif (@_user_pw is NULL)\n\t\t\tupdate dbo.tig_users set account_status = -1 where uid = @res_uid;\n\t\telse\n\t\t    exec TigUpdatePasswordPlainPw @_user_id, @_user_pw\n\n\t\tselect @res_uid as uid;\n\tend;\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigGetPassword')\nDROP PROCEDURE TigGetPassword\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- Returns user's password from the database\ncreate procedure dbo.TigGetPassword\n\t@_user_id nvarchar(2049)\nAS\nbegin\n    select c.value\n    from tig_users u\n    inner join tig_user_credentials c on c.uid = u.uid\n    where\n        u.sha1_user_id = hashbytes('SHA1', lower(@_user_id))\n        and c.username_sha1 = hashbytes('SHA1', N'default')\n        and c.mechanism = N'PLAIN';\nend\n-- QUERY END:\nGO\n\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigUpdatePasswordPlainPw')\nDROP PROCEDURE TigUpdatePasswordPlainPw\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- Takes plain text user password and converts it to internal representation\ncreate procedure dbo.TigUpdatePasswordPlainPw\n\t@_user_id nvarchar(2049),\n\t@_user_pw nvarchar(255)\nAS\nbegin\n\tdeclare @_encoding nvarchar(512)\n\tdeclare @_encoded nvarchar(max)\n\tset @_encoding = coalesce(dbo.TigGetDBProperty(N'password-encoding'), N'PLAIN')\n\n\tset @_encoded = case @_encoding\n\t    when N'MD5-PASSWORD' then HASHBYTES('MD5', @_user_pw)\n\t    when N'MD5-USERID-PASSWORD' then HASHBYTES('MD5', @_user_id + @_user_pw)\n\t    when N'MD5-USERNAME-PASSWORD' then HASHBYTES('MD5', (LEFT (@_user_id, CHARINDEX(N'@',@_user_id)-1)) + @_user_pw)\n\t    else @_user_pw\n\t    end;\n\n\texec TigUserCredential_Update @_user_id, N'default', @_encoding, @_encoded\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigUserLoginPlainPw')\nDROP PROCEDURE TigUserLoginPlainPw\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- Performs user login for a plain text password, converting it to an internal\n-- representation if necessary\ncreate procedure dbo.TigUserLoginPlainPw\n\t@_user_id nvarchar(2049),\n\t@_user_pw nvarchar(255)\nAS\nbegin\n\tdeclare @_encoding nvarchar(512)\n\tdeclare @_encoded nvarchar(max)\n\tset @_encoding = coalesce(dbo.TigGetDBProperty(N'password-encoding'), N'PLAIN')\n\n\tset @_encoded = case @_encoding\n\t    when N'MD5-PASSWORD' then HASHBYTES('MD5', @_user_pw)\n\t    when N'MD5-USERID-PASSWORD' then HASHBYTES('MD5', @_user_id + @_user_pw)\n\t    when N'MD5-USERNAME-PASSWORD' then HASHBYTES('MD5', (LEFT (@_user_id, CHARINDEX(N'@',@_user_id)-1)) + @_user_pw)\n\t    else @_user_pw\n\t    end;\n\n    if exists (select 1 from dbo.tig_users u\n        inner join tig_user_credentials c on c.uid = u.uid\n\t\twhere u.sha1_user_id = hashbytes('SHA1', lower(@_user_id))\n\t\t    and u.account_status > 0\n\t\t    and c.username_sha1 = hashbytes('SHA1', N'default')\n\t\t    and c.mechanism = @_encoding\n\t\t\tand c.value = @_encoded)\n\t\tbegin\n\t\tupdate dbo.tig_users\n\t\t\tset online_status = online_status + 1, last_login = CURRENT_TIMESTAMP\n\t\t\t\twhere sha1_user_id = hashbytes('SHA1', lower(@_user_id));\n\t\t\tselect @_user_id as user_id;\n\t\tend\n\telse\n\t\tbegin\n\t\t\tupdate dbo.tig_users set failed_logins = failed_logins + 1 where sha1_user_id = hashbytes('SHA1', lower(@_user_id));\n\t\t\tselect NULL as user_id;\n\t\tend\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigRemoveUser')\nDROP PROCEDURE TigRemoveUser\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- Removes a user from the database\ncreate procedure dbo.TigRemoveUser\n\t@_user_id nvarchar(2049)\nAS\nbegin\n\tdeclare @res_uid bigint;\n\n\tset @res_uid = (select uid from dbo.tig_users where sha1_user_id = hashbytes('SHA1', lower(@_user_id)));\n\n    delete from dbo.tig_user_credentials where uid = @res_uid;\n\tdelete from dbo.tig_pairs where uid = @res_uid;\n\tdelete from dbo.tig_nodes where uid = @res_uid;\n\tdelete from dbo.tig_users where uid = @res_uid;\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigPutDBProperty')\nDROP PROCEDURE TigPutDBProperty\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- Database properties set - procedure\ncreate procedure dbo.TigPutDBProperty\n\t@_tkey nvarchar(255),\n\t@_tval ntext\n\tAS\n\tbegin\n\t\tDeclare @_nid int;\n\t\tDeclare @_uid int;\n\t\tDeclare @_count int;\n\t\tif exists (select 1 from dbo.tig_pairs, dbo.tig_users\n\t\t\t\t\twhere (sha1_user_id = HASHBYTES('SHA1', LOWER(N'db-properties')))\n\t\t\t\t\t\tAND (dbo.tig_users.uid = dbo.tig_pairs.uid)  AND (pkey = @_tkey))\n\t\t\tbegin\n\t\t\t\tselect @_nid = dbo.tig_pairs.nid, @_uid = dbo.tig_pairs.uid from dbo.tig_pairs, dbo.tig_users\n\t\t\t\t\twhere (sha1_user_id = HASHBYTES('SHA1', LOWER(N'db-properties')))\n\t\t\t\t\t\tAND (dbo.tig_users.uid = dbo.tig_pairs.uid)  AND (pkey = @_tkey);\n\t\t\t\tupdate dbo.tig_pairs set pval = @_tval\n\t\t\t\t\twhere (@_uid = uid) AND (pkey = @_tkey) ;\n\t\t\tend\n\t\telse\n\t\t\tbegin\n\t\t\t    if not exists (select 1 from tig_users where user_id = 'db-properties')\n                    exec dbo.TigAddUserPlainPw 'db-properties', NULL;\n\n\n\t\t\t\tselect @_nid = dbo.tig_pairs.nid, @_uid = dbo.tig_pairs.uid from dbo.tig_pairs, dbo.tig_users\n\t\t\t\t\twhere (sha1_user_id = HASHBYTES('SHA1', LOWER(N'db-properties')))\n\t\t\t\t\t\tAND (dbo.tig_users.uid = dbo.tig_pairs.uid)  AND (pkey = @_tkey);\n\t\t\t\tinsert into dbo.tig_pairs (pkey, pval, uid, nid)\n\t\t\t\t\tselect @_tkey, @_tval, tu.uid, tn.nid from dbo.tig_users tu  left join tig_nodes tn on tn.uid=tu.uid\n\t\t\t\t\t\twhere (sha1_user_id = HASHBYTES('SHA1', LOWER(N'db-properties')) and tn.node='root');\n\n\t\t\tend\n\tend\n-- QUERY END:\nGO\n"
  },
  {
    "path": "src/main/database/sqlserver-server-8.0.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n--  To load schema to PostgreSQL database execute following commands:\n--\n--  createuser tigase\n--  createdb -U tigase tigase\n--  psql -q -U tigase -d tigase -f postgresql-schema.sql\n\n-- LOAD FILE: database/sqlserver-server-8.0.0-schema.sql\n\n-- LOAD FILE: database/sqlserver-server-8.0.0-sp.sql\n\n-- LOAD FILE: database/sqlserver-server-8.0.0-props.sql\n"
  },
  {
    "path": "src/main/database/sqlserver-server-8.1.0-props.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\nSET QUOTED_IDENTIFIER ON\n-- QUERY END:\nGO\n\nselect GETDATE(), ' - Setting schema version to 8.1.0';\n\n-- QUERY START:\nexec dbo.TigPutDBProperty 'schema-version', '8.1.0';\n-- QUERY END:\nGO\n\n-- QUERY START:\nexec TigSetComponentVersion 'server', '8.1.0';\n-- QUERY END:\nGO\n"
  },
  {
    "path": "src/main/database/sqlserver-server-8.1.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- LOAD FILE: database/sqlserver-server-8.1.0-props.sql\n"
  },
  {
    "path": "src/main/database/sqlserver-server-8.2.0-props.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\nSET QUOTED_IDENTIFIER ON\n-- QUERY END:\nGO\n\nselect GETDATE(), ' - Setting schema version to 8.2.0';\n\n-- QUERY START:\nexec TigSetComponentVersion 'server', '8.2.0';\n-- QUERY END:\nGO\n"
  },
  {
    "path": "src/main/database/sqlserver-server-8.2.0-schema.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\nIF EXISTS (select 1 from sys.indexes where object_id = object_id('dbo.tig_users') and name = 'IX_tig_users_last_login' )\n    DROP INDEX [IX_tig_users_last_login] ON [dbo].[tig_users]\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (select 1 from sys.indexes where object_id = object_id('dbo.tig_users') and name = 'IX_tig_users_last_logout' )\n    DROP INDEX [IX_tig_users_last_logout] ON [dbo].[tig_users]\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (select 1 from sys.indexes where object_id = object_id('dbo.tig_users') and name = 'IX_tig_users_online_status' )\n    DROP INDEX [IX_tig_users_online_status] ON [dbo].[tig_users]\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF NOT EXISTS (SELECT * FROM sys.columns WHERE name = 'last_used' AND object_id = object_id('dbo.tig_users'))\nBEGIN\n    ALTER TABLE [tig_users] ADD [last_used] [datetime] default 0;\nEND\n-- QUERY END:\nGO\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.columns WHERE name = 'last_login' AND object_id = object_id('dbo.tig_users'))\nBEGIN\n    UPDATE [tig_users] SET [last_used] = [last_login] WHERE [last_used] = 0;\nEND\n-- QUERY END:\nGO\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.columns WHERE name = 'online_status' AND object_id = object_id('dbo.tig_users'))\nBEGIN\n    DECLARE @sqlStmt nvarchar(MAX)\n    DECLARE C CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY FOR\n        select 'ALTER TABLE tig_users DROP CONSTRAINT ' + dc.name + ';'\n        from sys.default_constraints as dc\n            left join sys.columns as sc on dc.parent_column_id = sc.column_id\n        where dc.parent_object_id = OBJECT_ID('dbo.tig_users')\n            and type_desc = 'DEFAULT_CONSTRAINT'\n            and sc.name in ('online_status', 'last_login', 'last_logout')\n    OPEN C\n    FETCH NEXT FROM C INTO @sqlStmt\n    WHILE @@FETCH_STATUS = 0\n    BEGIN\n        exec sp_executesql @sqlStmt\n        fetch next from c into @sqlStmt\n    END\n    ALTER TABLE [tig_users] DROP COLUMN IF EXISTS [online_status], [last_logout], [last_login]\nEND\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (select 1 from sys.indexes where object_id = object_id('dbo.tig_offline_messages') and name = 'IX_tig_offline_messages_receiver_sha1' )\n    DROP INDEX [IX_tig_offline_messages_receiver_sha1] ON [dbo].[tig_offline_messages]\n-- QUERY END:\nGO"
  },
  {
    "path": "src/main/database/sqlserver-server-8.2.0-sp.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- database properties are deprecated and are being removed\n\n-- QUERY START:\nSET QUOTED_IDENTIFIER ON\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigPutDBProperty')\n    DROP PROCEDURE TigPutDBProperty\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigUserLogin')\n    DROP PROCEDURE TigUserLogin\n-- QUERY END:\nGO\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigUserLogout')\n    DROP PROCEDURE TigUserLogout\n-- QUERY END:\nGO\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigOnlineUsers')\n    DROP PROCEDURE TigOnlineUsers\n-- QUERY END:\nGO\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigOfflineUsers')\n    DROP PROCEDURE TigOfflineUsers\n-- QUERY END:\nGO\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigUserLoginPlainPw')\n    DROP PROCEDURE TigUserLoginPlainPw\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigAllUsers')\n    DROP PROCEDURE TigAllUsers\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- List of all users in database\ncreate procedure dbo.TigAllUsers\nAS\nbegin\n    select user_id, failed_logins, account_status from dbo.tig_users;\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nIF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'TigUpdateLoginTime')\n    DROP PROCEDURE TigUpdateLoginTime\n-- QUERY END:\nGO\n\n-- QUERY START:\n-- It sets last_login time to the current timestamp\ncreate procedure dbo.TigUpdateLoginTime\n    @_user_id nvarchar(2049)\nAS\nbegin\n    update dbo.tig_users\n        set last_used = GETUTCDATE()\n        where user_id = @_user_id;\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nif exists (select 1 from sys.objects where type = 'P' and name = 'Tig_OfflineMessages_DeleteMessage')\n    drop procedure [dbo].[Tig_OfflineMessages_DeleteMessage];\n-- QUERY END:\nGO\n\n-- QUERY START:\ncreate procedure [dbo].[Tig_OfflineMessages_DeleteMessage]\n@_msg_id bigint\nas\nbegin\n    set nocount on;\n    delete from tig_offline_messages where msg_id = @_msg_id;\n    set nocount off;\nend\n-- QUERY END:\nGO\n"
  },
  {
    "path": "src/main/database/sqlserver-server-8.2.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- LOAD FILE: database/sqlserver-server-8.2.0-schema.sql\n\n-- LOAD FILE: database/sqlserver-server-8.2.0-sp.sql\n\n-- LOAD FILE: database/sqlserver-server-8.2.0-props.sql\n"
  },
  {
    "path": "src/main/database/sqlserver-server-8.3.0-props.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\nSET QUOTED_IDENTIFIER ON\n-- QUERY END:\nGO\n\nselect GETDATE(), ' - Setting schema version to 8.3.0';\n\n-- QUERY START:\nexec TigSetComponentVersion 'server', '8.3.0';\n-- QUERY END:\nGO\n"
  },
  {
    "path": "src/main/database/sqlserver-server-8.3.0-schema.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n--\n"
  },
  {
    "path": "src/main/database/sqlserver-server-8.3.0-sp.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- database properties are deprecated and are being removed\n"
  },
  {
    "path": "src/main/database/sqlserver-server-8.3.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- LOAD FILE: database/sqlserver-server-8.3.0-schema.sql\n\n-- LOAD FILE: database/sqlserver-server-8.3.0-sp.sql\n\n-- LOAD FILE: database/sqlserver-server-8.3.0-props.sql\n"
  },
  {
    "path": "src/main/database/sqlserver-server-8.4.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n"
  },
  {
    "path": "src/main/database/sqlserver-server-8.5.0-schema.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\nif not exists (select 1 from sys.indexes where object_id = object_id('dbo.tig_offline_messages') and name = 'IX_tig_offline_messages_receiver_sha1_msg_type' )\n    create index IX_tig_offline_messages_receiver_sha1_msg_type on [dbo].[tig_offline_messages] (receiver_sha1, msg_type);\n-- QUERY END:\nGO\n"
  },
  {
    "path": "src/main/database/sqlserver-server-8.5.0-sp.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- QUERY START:\nif exists (select 1 from sys.objects where type = 'P' and name = 'Tig_OfflineMessages_AddMessage')\n    drop procedure [dbo].[Tig_OfflineMessages_AddMessage];\n-- QUERY END:\nGO\n\n-- QUERY START:\ncreate procedure [dbo].[Tig_OfflineMessages_AddMessage]\n    @_to nvarchar(2049),\n    @_from nvarchar(2049),\n    @_type int,\n    @_ts datetime,\n    @_message nvarchar(max),\n    @_expired datetime,\n    @_limit bigint\nas\nbegin\n    set nocount on;\n    declare\n        @_msg_count bigint,\n        @_msg_id bigint;\n    set @_msg_count = 0;\n\n    if @_limit > 0\n        select @_msg_count = count(msg_id) from tig_offline_messages where receiver_sha1 = HASHBYTES('SHA1', lower(@_to)) and sender_sha1 = HASHBYTES('SHA1', lower(@_from));\n\n    if @_limit = 0 or @_limit > @_msg_count\n    begin\n        insert into tig_offline_messages ( receiver, receiver_sha1, sender, sender_sha1, msg_type, ts, message, expired )\n            select @_to, HASHBYTES('SHA1', lower(@_to)), @_from, HASHBYTES('SHA1', lower(@_from)), @_type, @_ts, @_message, @_expired;\n        set @_msg_id = 1;\n    end\n\n    select @_msg_id as msg_id;\n    set nocount off;\nend\n-- QUERY END:\nGO\n\n-- QUERY START:\nif exists (select 1 from sys.objects where type = 'P' and name = 'Tig_OfflineMessages_DeleteMessages')\n    drop procedure [dbo].[Tig_OfflineMessages_DeleteMessages];\n-- QUERY END:\nGO\n\n-- QUERY START:\ncreate procedure  [dbo].[Tig_OfflineMessages_DeleteMessages]\n    @_to nvarchar(2049)\nas\nbegin\n    set nocount on;\n    delete from tig_offline_messages where receiver_sha1 = HASHBYTES('SHA1', lower(@_to))\n    set nocount off;\nend\n-- QUERY END:\nGO"
  },
  {
    "path": "src/main/database/sqlserver-server-8.5.0.sql",
    "content": "--\n-- Tigase XMPP Server - The instant messaging server\n-- Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n--\n-- This program is free software: you can redistribute it and/or modify\n-- it under the terms of the GNU Affero General Public License as published by\n-- the Free Software Foundation, version 3 of the License.\n--\n-- This program is distributed in the hope that it will be useful,\n-- but WITHOUT ANY WARRANTY; without even the implied warranty of\n-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n-- GNU Affero General Public License for more details.\n--\n-- You should have received a copy of the GNU Affero General Public License\n-- along with this program. Look for COPYING file in the top folder.\n-- If not, see http://www.gnu.org/licenses/.\n--\n\n-- LOAD FILE: database/sqlserver-server-8.5.0-schema.sql\n\n-- LOAD FILE: database/sqlserver-server-8.5.0-sp.sql\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/AddUser.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n User add script as described in XEP-0133:\n http://xmpp.org/extensions/xep-0133.html#add-user\n\n AS:Description: Add user\n AS:CommandId: http://jabber.org/protocol/admin#add-user\n AS:Component: sess-man\n AS:Group: Users\n */\n\npackage tigase.admin\n\nimport tigase.db.AuthRepository\nimport tigase.db.TigaseDBException\nimport tigase.db.UserExistsException\nimport tigase.db.UserRepository\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.vhosts.VHostManagerIfc\nimport tigase.xmpp.jid.BareJID\n\nimport java.util.logging.Level\nimport java.util.logging.Logger\n\ndef log = Logger.getLogger(\"tigase.admin\");\n\ndef JID = \"accountjid\"\ndef PASSWORD = \"password\"\ndef PASSWORD_VERIFY = \"password-verify\"\ndef EMAIL = \"email\"\n\ndef p = (Packet) packet\ndef auth_repo = (AuthRepository) authRepository\ndef user_repo = (UserRepository) userRepository\ndef vhost_man = (VHostManagerIfc) vhostMan\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\ndef userJid = Command.getFieldValue(packet, JID)\ndef userPass = Command.getFieldValue(packet, PASSWORD)\ndef userPassVer = Command.getFieldValue(packet, PASSWORD_VERIFY)\ndef userEmail = Command.getFieldValue(packet, EMAIL)\n\n\nif (log.isLoggable(Level.FINEST)) {\n\tlog.log(Level.FINEST, \"Executing command add-user: ${userJid}. Request: ${p}, command: ${this}\")\n}\n\nif (userJid == null || userPass == null || userPassVer == null || userEmail == null) {\n\tdef result = p.commandResult(Command.DataType.form);\n\n\tCommand.addTitle(result, \"Adding a User\")\n\tCommand.addInstructions(result, \"Fill out this form to add a user.\")\n\n\tCommand.addFieldValue(result, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\",\n\t\t\t\t\t\t  \"hidden\")\n\tCommand.addFieldValue(result, JID, userJid ?: \"\", \"jid-single\",\n\t\t\t\t\t\t  \"The Jabber ID for the account to be added\")\n\tCommand.addFieldValue(result, PASSWORD, userPass ?: \"\", \"text-private\",\n\t\t\t\t\t\t  \"The password for this account\")\n\tCommand.addFieldValue(result, PASSWORD_VERIFY, userPassVer ?: \"\", \"text-private\",\n\t\t\t\t\t\t  \"Retype password\")\n\tCommand.addFieldValue(result, EMAIL, userEmail ?: \"\", \"text-single\",\n\t\t\t\t\t\t  \"Email address\")\n\n\treturn result\n}\n\ndef result = p.commandResult(Command.DataType.result)\ntry {\n\tdef bareJID = BareJID.bareJIDInstance(userJid)\n\tif (isAllowedForDomain.apply(bareJID.getDomain())) {\n\t\tauth_repo.addUser(bareJID, userPass)\n\t\tuser_repo.setData(bareJID, \"email\", userEmail);\n\t\tCommand.addTextField(result, \"Note\", \"Operation successful\");\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"User ${userJid} added correctly. Request packet: ${p}, command: ${this}\")\n\t\t}\n\t} else {\n\t\tCommand.addTextField(result, \"Error\", \"You do not have enough permissions to create account for this domain.\");\n\t}\n} catch (UserExistsException ex) {\n\tlog.log(Level.WARNING, \"Error while adding user ${userJid}: User already exists, can't be added. Request packet: ${p}, command: ${this}\", ex)\n\tCommand.addTextField(result, \"Note\", \"User already exists, can't be added.\");\n} catch (TigaseDBException ex) {\n\tlog.log(Level.WARNING, \"Error while adding user ${userJid}: Problem accessing database, user not added. Request packet: ${p}, command: ${this}\", ex)\n\tCommand.addTextField(result, \"Note\", \"Problem accessing database, user not added.\");\n}\n\nreturn result\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/AddUserTracker.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n Activate on the server user tracking mechanisms to aid in problem resolution.\n AS:Description: Activate log tracker for a user\n AS:CommandId: http://jabber.org/protocol/admin#add-user-tracker\n AS:Component: sess-man\n */\npackage tigase.admin\n\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.util.log.LogUserFilter\nimport tigase.xmpp.XMPPSession\nimport tigase.xmpp.jid.BareJID\n\nimport java.util.logging.*\n\ndef JID = \"accountjid\"\ndef FILE_NAME = \"file-name\"\ndef FILE_LIMIT = 25000000\ndef FILE_COUNT = 5\n\ndef p = (Packet) packet\n\ndef userJid = Command.getFieldValue(packet, JID)\ndef fileName = Command.getFieldValue(packet, FILE_NAME)\n\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\nif (!isServiceAdmin) {\n\tdef result = p.commandResult(Command.DataType.result);\n\tCommand.addTextField(result, \"Error\", \"You are not service administrator\");\n\treturn result\n}\n\n\nif (userJid == null) {\n\tdef result = p.commandResult(Command.DataType.form);\n\n\tCommand.addTitle(result, \"Adding a User Log Tracker\")\n\tCommand.addInstructions(result, \"Fill out this form to add a user log tracker.\")\n\n\tCommand.addFieldValue(result, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\",\n\t\t\t\t\t\t  \"hidden\")\n\tCommand.addFieldValue(result, JID, userJid ?: \"\", \"jid-single\",\n\t\t\t\t\t\t  \"The Jabber ID for the account to be tracked\")\n\tCommand.addFieldValue(result, FILE_NAME, fileName ?: \"\", \"text-single\",\n\t\t\t\t\t\t  \"File name to write user's log entries\")\n\n\treturn result\n}\n\n// Remove the old tracker if there is any active for that user\ndef hand = null\n\nHandler[] handlers = Logger.getLogger(\"\").getHandlers()\nhandlers.each {\n\tFilter filt = it.getFilter()\n\tif (filt != null && filt.class == LogUserFilter && ((LogUserFilter) filt).getId() == userJid) {\n\t\thand = it\n\t}\n}\nif (hand != null) {\n\tLogger.getLogger(\"\").removeHandler(hand)\n\thand.close()\n\tCommand.addTextField(result, \"Note\", \"Operation successful, tracker removed for \" + userJid);\n}\n\n// Ok now we can setup a new tracker\ndef result = p.commandResult(Command.DataType.result)\n\ndef users_sessions = (Map) userSessions\ndef bareJID = BareJID.bareJIDInstance(userJid)\n\nXMPPSession session = users_sessions.get(bareJID)\n\nif (fileName == null || fileName == \"\") {\n\tfileName = \"logs/\" + userJid\n}\nLogUserFilter filter = new LogUserFilter(bareJID, users_sessions)\n\nFileHandler handler = new FileHandler(fileName, FILE_LIMIT, FILE_COUNT)\nhandler.setLevel(Level.ALL)\nhandler.setFilter(filter)\nLogger.getLogger(\"\").addHandler(handler)\n\nCommand.addTextField(result, \"Note\", \"Operation successful\");\nCommand.addTextField(result, \"Note\", \"Tracking user \" + userJid)\n\nreturn result\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/BoshPreBindSession.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\nAS:Description: Pre-Bind BOSH user session\nAS:CommandId: pre-bind-bosh-session\nAS:Component: bosh\n */\n\npackage tigase.admin\n\nimport tigase.server.Command\nimport tigase.server.Iq\nimport tigase.vhosts.VHostItem\nimport tigase.vhosts.VHostManagerIfc\nimport tigase.xmpp.jid.BareJID\n\ntry {\n\n\tdef USER_JID = \"from\";\n\tdef RID = \"rid\"\n\tdef HOLD = \"hold\"\n\tdef WAIT = \"wait\"\n\tdef SID = \"sid\"\n\tdef HOSTNAME = \"hostname\"\n\n\tdef p = (Iq) packet\n\n\tdef vhost_man = (VHostManagerIfc) vhostMan\n\tdef admins = (Set) adminsSet\n\tdef stanzaFromBare = p.getStanzaFrom().getBareJID()\n\tdef isServiceAdmin = admins.contains(stanzaFromBare)\n\n\tdef userJid = Command.getFieldValue(p, USER_JID)\n\tdef rid = 0\n\tdef hold = Command.getFieldValue(p, HOLD)\n\tdef wait = Command.getFieldValue(p, WAIT)\n\n\tif (userJid == null || userJid.isEmpty()) {\n\t\tdef res = (Iq) p.commandResult(Command.DataType.form);\n\t\tCommand.addTitle(res, \"Pre-bind BOSH user session\")\n\t\tCommand.addInstructions(res, \"Fill out this form to create and pre-bind BOSH user session.\")\n\n\t\tCommand.addFieldValue(res, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\", \"hidden\")\n\n\t\tCommand.addFieldValue(res, USER_JID, \"\", \"jid-single\",\n\t\t\t\t\t\t\t  \"JID of the user for which session should be created - either BareJID or FullJID, the former will result in randomly generated resource\")\n\t\tCommand.addFieldValue(res, HOLD, hold ?: \"1\", \"text-single\", \"HOLD value (optional)\")\n\t\tCommand.addFieldValue(res, WAIT, wait ?: \"60\", \"text-single\", \"WAIT value (optional)\")\n\n\t\treturn res\n\t}\n\n\tdef bareJID = BareJID.bareJIDInstance(userJid)\n\tVHostItem vhost = vhost_man.getVHostItem(bareJID.getDomain())\n\n\tdef result = (Iq) p.commandResult(Command.DataType.result)\n\n\tif (vhost == null) {\n\t\tCommand.addTextField(result, \"Error\", \"Domain of the JID doesn't exists\");\n\t} else if (isAllowedForDomain.apply(bareJID.getDomain())) {\n\n\t\tMap args = new HashMap();\n\t\tif (userJid != null && !userJid.isEmpty()) {\n\t\t\targs.put(USER_JID, userJid)\n\t\t}\n\t\tif (hold != null && !hold.isEmpty()) {\n\t\t\targs.put(HOLD, hold)\n\t\t}\n\t\tif (wait != null && !wait.isEmpty()) {\n\t\t\targs.put(WAIT, wait)\n\t\t}\n\n\t\targs = boshCM.preBindSession(args)\n\n\t\trid = args.get(RID);\n\t\tdef sid = args.get(SID);\n\t\tdef hostname = args.get(HOSTNAME)\n\t\tuserJid = args.get(USER_JID)\n\n\t\tif (hostname != null) {\n\t\t\tCommand.addFieldValue(result, USER_JID, userJid, \"jid-single\", \"JID\")\n\t\t\tCommand.addFieldValue(result, HOSTNAME, hostname, \"jid-single\", \"hostname\")\n\t\t\tCommand.addFieldValue(result, RID, rid, \"text-single\", \"RID\")\n\t\t\tCommand.addFieldValue(result, SID, sid, \"text-single\", \"SID\")\n\t\t\tCommand.addFieldValue(result, HOLD, hold, \"text-single\", \"HOLD\")\n\t\t\tCommand.addFieldValue(result, WAIT, wait, \"text-single\", \"WAIT\")\n\t\t} else {\n\t\t\tCommand.addTextField(result, \"Error\", \"Error processing request, provided data is invalid\");\n\t\t}\n\t} else {\n\t\tCommand.addTextField(result, \"Error\", \"You do not have enough permissions\");\n\t}\n\treturn result\n\n} catch (Exception ex) {\n\tex.printStackTrace();\n}"
  },
  {
    "path": "src/main/groovy/tigase/admin/BroadcastToOnline.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n\n4.23 Send Announcement to Online Users as described in XEP-0133:\nhttp://xmpp.org/extensions/xep-0133.html#announce\n\nAS:Description: Send Announcement to Online Users\nAS:CommandId: http://jabber.org/protocol/admin#announce\nAS:Component: sess-man\nAS:Group: Notifications\n*/\n\npackage tigase.admin\n\nimport tigase.cluster.strategy.ClusteringStrategyIfc\nimport tigase.server.Command\nimport tigase.server.Iq\nimport tigase.server.Message\nimport tigase.server.Permissions\nimport tigase.xmpp.StanzaType\nimport tigase.xmpp.jid.JID\n\ndef FROM_JID = \"from-jid\"\ndef SUBJECT = \"subject\"\ndef MSG_TYPE = \"msg-type\"\ndef MSG_BODY = \"announcement\"\n\ndef p = (Iq) packet\n\ndef fromJid = Command.getFieldValue(p, FROM_JID)\ndef subject = Command.getFieldValue(p, SUBJECT)\ndef msg_type = Command.getFieldValue(p, MSG_TYPE)\ndef body = Command.getFieldValues(p, MSG_BODY)\n\ndef NOTIFY_CLUSTER = \"notify-cluster\"\nboolean clusterMode = Boolean.valueOf(System.getProperty(\"cluster-mode\", false.toString()));\nboolean notifyCluster = Boolean.valueOf(Command.getFieldValue(packet, NOTIFY_CLUSTER))\n\nif (fromJid == null || subject == null || msg_type == null || body == null) {\n\tdef res = (Iq) p.commandResult(Command.DataType.form);\n\tCommand.addTitle(res, \"Message to online users\")\n\tCommand.addInstructions(res, \"Fill out this form to make an announcement to all active users of this service.\")\n\n\tCommand.addFieldValue(res, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\", \"hidden\")\n\n\tCommand.addFieldValue(res, FROM_JID, fromJid ?: p.getStanzaFrom().getDomain().toString(), \"jid-single\",\n\t\t\t\t\t\t  \"From address\")\n\n\tCommand.addFieldValue(res, SUBJECT, subject ?: \"Message from administrators\", \"text-single\", \"Subject\")\n\n\tdef msg_types = [ \"normal\", \"headline\", \"chat\" ]\n\tCommand.addFieldValue(res, MSG_TYPE, msg_type ?: msg_types[0], \"Type\", (String[]) msg_types, (String[]) msg_types)\n\n\tif (body == null) {\n\t\tbody = [ \"\" ]\n\t}\n\n\tCommand.addFieldMultiValue(res, MSG_BODY, body as List)\n\n\tif (clusterMode) {\n\t\tCommand.addHiddenField(res, NOTIFY_CLUSTER, true.toString())\n\t}\n\n\n\treturn res\n}\n\nQueue results = new LinkedList()\nif (clusterMode && notifyCluster) {\n\tif (null != clusterStrategy) {\n\t\tdef cluster = (ClusteringStrategyIfc) clusterStrategy\n\t\tList<JID> cl_conns = cluster.getNodesConnected()\n\t\tif (cl_conns && cl_conns.size() > 0) {\n\t\t\tcl_conns.each { node ->\n\n\t\t\t\tdef forward = p.copyElementOnly();\n\t\t\t\tCommand.removeFieldValue(forward, NOTIFY_CLUSTER)\n\t\t\t\tCommand.addHiddenField(forward, NOTIFY_CLUSTER, false.toString())\n\t\t\t\tforward.setPacketTo(node);\n\t\t\t\tforward.setPermissions(Permissions.ADMIN);\n\n\t\t\t\tresults.offer(forward)\n\t\t\t}\n\t\t}\n\t}\n}\n\n\ndef jidFrom = JID.jidInstanceNS(fromJid)\ndef type = StanzaType.valueOf(msg_type)\ndef msg_body = body.join('\\n')\n\ndef msg = Message.getMessage(null, null, type, msg_body, subject, null, \"admin\")\ndef result = p.commandResult(Command.DataType.result)\nCommand.addTextField(result, \"Note\", \"Operation successful\");\nresults += result\ndef conns = (Map) userConnections\nconns.each { key, value ->\n\tif (value.isAuthorized()) {\n\t\tdef res = msg.copyElementOnly()\n\t\tres.initVars(jidFrom, value.getJID())\n\t\tres.setPacketTo(key)\n\t\tresults += res\n\t}\n\n}\n\nreturn (Queue) results\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/ChangeUserPassword.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n User change password script as described in XEP-0133:\n http://xmpp.org/extensions/xep-0133.html#\n AS:Description: Change user password\n AS:CommandId: http://jabber.org/protocol/admin#change-user-password\n AS:Component: sess-man\n AS:Group: Users\n */\n\npackage tigase.admin\n\nimport tigase.db.AuthRepository\nimport tigase.db.TigaseDBException\nimport tigase.db.UserNotFoundException\nimport tigase.db.UserRepository\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.vhosts.VHostManagerIfc\nimport tigase.xmpp.jid.BareJID\n\ndef JID = \"accountjid\"\ndef PASSWORD = \"password\"\n//def PASSWORD_VERIFY = \"password-verify\"\n\ndef p = (Packet) packet\ndef auth_repo = (AuthRepository) authRepository\ndef user_repo = (UserRepository) userRepository\ndef vhost_man = (VHostManagerIfc) vhostMan\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\ndef userJid = Command.getFieldValue(packet, JID)\ndef userPass = Command.getFieldValue(packet, PASSWORD)\n//def userPassVer = Command.getFieldValue(packet, PASSWORD_VERIFY)\n\nif (userJid == null || userPass == null /*|| userPassVer == null*/) {\n\tdef result = p.commandResult(Command.DataType.form);\n\n\tCommand.addTitle(result, \"Changing a User Password\")\n\tCommand.addInstructions(result, \"Fill out this form to change a user's password.\")\n\n\tCommand.addFieldValue(result, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\",\n\t\t\t\t\t\t  \"hidden\")\n\tCommand.addFieldValue(result, JID, userJid ?: \"\", \"jid-single\",\n\t\t\t\t\t\t  \"The Jabber ID for this account\")\n\tCommand.addFieldValue(result, PASSWORD, userPass ?: \"\", \"text-private\",\n\t\t\t\t\t\t  \"The new password for this account\")\n//\tCommand.addFieldValue(result, PASSWORD_VERIFY, userPassVer ?: \"\", \"text-private\",\n//\t\t\t\"Retype password\")\n\n\treturn result\n}\n\ndef result = p.commandResult(Command.DataType.result)\ntry {\n\tdef bareJID = BareJID.bareJIDInstance(userJid)\n\tif (isAllowedForDomain.apply(bareJID.getDomain())) {\n\t\tif (user_repo.userExists(bareJID)) {\n\t\t\tauth_repo.updatePassword(bareJID, userPass)\n\t\t\tCommand.addTextField(result, \"Note\", \"Operation successful\");\n\t\t} else {\n\t\t\tCommand.addTextField(result, \"Note\", \"User not exists, can't change password.\");\n\t\t}\n\t} else {\n\t\tCommand.addTextField(result, \"Error\",\n\t\t\t\t\t\t\t \"You do not have enough permissions to change account password for this domain.\");\n\t}\n} catch (UserNotFoundException ex) {\n\tCommand.addTextField(result, \"Note\", \"User not exists, can't change password.\");\n} catch (TigaseDBException ex) {\n\tCommand.addTextField(result, \"Note\", \"Problem accessing database, password not changed.\");\n}\n\nreturn result\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/CompManager.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n\nManage active server components\n\nAS:Description: Manage active server components\nAS:CommandId: comp-manager\nAS:Component: basic-conf\nAS:Group: Configuration\n */\n\npackage tigase.admin\n\nimport tigase.conf.ConfigRepositoryIfc\nimport tigase.conf.Configurable\nimport tigase.conf.Configurator\nimport tigase.osgi.ModulesManagerImpl\nimport tigase.server.Command\nimport tigase.server.Iq\nimport tigase.server.MessageRouterConfig\nimport tigase.server.XMPPServer\n\nclass DelayedReloadTask\n\t\textends Thread {\n\n\tvoid run() {\n\t\tThread.sleep(5000);\n\t\t((Configurator) XMPPServer.getConfigurator()).updateMessageRouter();\n\t}\n}\n\ntry {\n\n\tdef ACTION = \"action\";\n\tdef ACTION_ADD = \"Add\";\n\tdef ACTION_EDIT = \"Edit\";\n\tdef ACTION_REMOVE = \"Remove\";\n\tdef ACTION_LIST = \"List\";\n\n\tdef COMP_NAME = \"comp-name\";\n\tdef COMP_CLASS = \"comp-class\";\n\n\tdef SUBMIT = \"confirm\";\n\n\tdef getComponentProperties = { comp_name, comp_class ->\n\t\tdef conf = XMPPServer.getConfigurator();\n\t\tdef prop = conf.getProperties(comp_name);\n\t\ttry {\n\t\t\tdef comp = ModulesManagerImpl.getInstance().getServerComponent(comp_class);\n\t\t\tif (!comp) {\n\t\t\t\tdef INTERNAL_COMPONENTS = [ ];\n\t\t\t\tINTERNAL_COMPONENTS.addAll(MessageRouterConfig.COMPONENT_CLASSES.values());\n\t\t\t\tINTERNAL_COMPONENTS.addAll(MessageRouterConfig.COMP_CLUS_MAP.values());\n\n\t\t\t\tif (!XMPPServer.isOSGi() || INTERNAL_COMPONENTS.contains(comp_class)) {\n\t\t\t\t\tcomp = XMPPServer.class.getClassLoader().loadClass(comp_class).newInstance()\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (comp) {\n\t\t\t\tcomp.setName(comp_name)\n\t\t\t\tdef defProp = comp.getDefaults(conf.getDefConfigParams() ?: [ : ]) ?: [ : ];\n\t\t\t\tdefProp.putAll(prop);\n\t\t\t\treturn defProp;\n\t\t\t} else {\n\t\t\t\treturn [ : ];\n\t\t\t}\n\t\t} catch (Exception ex) {\n\t\t\tex.printStackTrace();\n\t\t\treturn [ : ];\n\t\t}\n\t};\n\n\tdef prepareComponentConfigForAdHoc = { res, comp_name, comp_class ->\n\t\tdef prop = getComponentProperties(comp_name, comp_class);\n\t\tdef keys = [ ];\n\t\tkeys.addAll(prop.keySet());\n\t\tkeys.sort { it }.each { key ->\n\t\t\tdef val = prop[key];\n\t\t\tif (val.getClass().isArray()) {\n\t\t\t\tdef tmp = [ ];\n\t\t\t\tfor (def x : val) {\n\t\t\t\t\ttmp.add(String.valueOf(x));\n\t\t\t\t}\n\t\t\t\tval = tmp.join(',');\n\t\t\t}\n\t\t\tif (!(val instanceof String)) {\n\t\t\t\tval = val.toString();\n\t\t\t}\n\t\t\tCommand.addFieldValue(res, key, val);\n\t\t}\n\t};\n\n\tdef getConfigFromAdHoc = { p, comp_name, comp_class ->\n\t\tdef defProp = getComponentProperties(comp_name, comp_class);\n\n\t\tdef props = [ : ];\n\t\tdefProp.each { key, defVal ->\n\t\t\tdef val = Command.getFieldValue(p, key);\n\n\t\t\tdef tmpVal = defVal;\n\t\t\tif (tmpVal.getClass().isArray()) {\n\t\t\t\tdef tmp = [ ];\n\t\t\t\tfor (def x : tmpVal) {\n\t\t\t\t\ttmp.add(String.valueOf(x));\n\t\t\t\t}\n\t\t\t\ttmpVal = tmp.join(',');\n\t\t\t}\n\n\t\t\tif (val && !val.equals(tmpVal)) {\n\t\t\t\tdef cls = defVal.getClass();\n\t\t\t\tif (val == \"null\") {\n\t\t\t\t\tval = null\n\t\t\t\t};\n\t\t\t\tif (cls == Long[].class) {\n\t\t\t\t\tdef out = [ ];\n\t\t\t\t\tval.split(',').each { out.add(Long.parseLong(it)) };\n\t\t\t\t\tval = (out as Long[]);\n\t\t\t\t} else if (cls == Integer[].class) {\n\t\t\t\t\tdef out = [ ];\n\t\t\t\t\tval.split(',').each { out.add(Integer.parseInt(it)) };\n\t\t\t\t\tval = (out as Integer[]);\n\t\t\t\t} else if (cls == String[].class) {\n\t\t\t\t\tdef out = [ ];\n\t\t\t\t\tval.split(',').each { out.add(it) };\n\t\t\t\t\tval = (out as String[]);\n\t\t\t\t} else if (cls == Long.class) {\n\t\t\t\t\tval = Long.parseLong(val);\n\t\t\t\t} else if (cls == Integer.class) {\n\t\t\t\t\tval = Integer.parseInt(val);\n\t\t\t\t} else if (cls == Boolean.class) {\n\t\t\t\t\tval = Boolean.parseBoolean(val);\n\t\t\t\t} else {\n\t\t\t\t\tprintln \"unknown convertion for key = \" + key + \" , \" + cls.getName()\n\t\t\t\t}\n\n\t\t\t\tprops.put(key, val);\n\t\t\t}\n\t\t}\n\n\t\treturn props;\n\t};\n\n\n\tdef conf_repo = (ConfigRepositoryIfc) comp_repo\n\tdef p = (Iq) packet\n\n\t// check permission\n\tdef admins = (Set) adminsSet\n\tdef stanzaFromBare = p.getStanzaFrom().getBareJID()\n\tdef isServiceAdmin = admins.contains(stanzaFromBare)\n\n\tif (!isServiceAdmin) {\n\t\tdef result = p.commandResult(Command.DataType.result)\n\t\tCommand.addTextField(result, \"Error\", \"You do not have enough permissions to access this data.\");\n\t\treturn result\n\t}\n\n\n\tdef comp_name = Command.getFieldValue(p, COMP_NAME)\n\tdef action = Command.getFieldValue(p, ACTION);\n\tdef submit = Command.getFieldValue(p, SUBMIT);\n\n\tif (action == null) {\n\t\tdef res = (Iq) p.commandResult(Command.DataType.form)\n\t\tdef actions = [ ACTION_LIST, ACTION_ADD, ACTION_EDIT, ACTION_REMOVE ];\n\t\tCommand.addFieldValue(res, ACTION, actions[0], \"Action\",\n\t\t\t\t\t\t\t  (String[]) actions, (String[]) actions)\n\t\treturn res;\n\t}\n\n\tif (action == ACTION_ADD && comp_name == null) {\n\t\tdef res = (Iq) p.commandResult(Command.DataType.form)\n\t\tCommand.addFieldValue(res, COMP_NAME, \"\", \"text-single\", \"Component name\");\n\t\tCommand.addFieldValue(res, COMP_CLASS, \"\", \"text-single\", \"Component class\");\n\t\tCommand.addHiddenField(res, ACTION, ACTION_ADD);\n\t\treturn res;\n\t} else if (action == ACTION_LIST && comp_name == null) {\n\t\tdef res = (Iq) p.commandResult(Command.DataType.form)\n\t\tdef compNames = [ ]\n\t\tdef conf = XMPPServer.getConfigurator();\n\n\t\tdef mrProps = conf.getProperties(\"message-router\");\n\t\tcompNames = mrProps.get(MessageRouterConfig.MSG_RECEIVERS_NAMES_PROP_KEY).toList();\n\t\tcompNames += mrProps.get(MessageRouterConfig.REGISTRATOR_NAMES_PROP_KEY).toList();\n\n\t\tcompNames.each {\n\t\t\tdef mr = conf.getComponent(it);\n\t\t\tif (mr) {\n\t\t\t\tCommand.addFieldMultiValue(res, it, Arrays.asList(mr.getComponentInfo().toString()))\n\t\t\t}\n\t\t}\n\t\tCommand.addHiddenField(res, ACTION, action);\n\t\treturn res\n\t} else if (comp_name == null) {\n\t\tdef res = (Iq) p.commandResult(Command.DataType.form)\n\t\tdef compNames = [ ]\n\t\tdef conf = XMPPServer.getConfigurator();\n\t\tconf_repo.getCompNames().findAll { conf.getComponent(it) != null }.each { compNames += it }\n\t\tcompNames.sort();\n\t\tCommand.addFieldValue(res, COMP_NAME, comp_name ?: compNames[0], \"Components\",\n\t\t\t\t\t\t\t  (String[]) compNames, (String[]) compNames)\n\t\tCommand.addHiddenField(res, ACTION, action ?: \"\");\n\t\treturn res\n\t} else {\n\t\tif (action == ACTION_REMOVE) {\n\t\t\tdef res = (Iq) p.commandResult(Command.DataType.result)\n\t\t\tdef conf = XMPPServer.getConfigurator();\n\t\t\tdef initProps = conf.getDefConfigParams().\n\t\t\t\t\tfindAll { return it.key.startsWith(Configurable.GEN_COMP_NAME) && it.value.equals(comp_name); };\n\t\t\tdef suffix = null;\n\t\t\tinitProps.each {\n\t\t\t\tsuffix = it.key.substring(Configurable.GEN_COMP_NAME.length());\n\t\t\t}\n\t\t\tconf.getDefConfigParams().remove(Configurable.GEN_COMP_NAME + suffix);\n\t\t\tconf.getDefConfigParams().remove(Configurable.GEN_COMP_CLASS + suffix);\n\n\t\t\tdef mrProps = conf.getProperties(\"message-router\");\n\t\t\tdef compNames = mrProps.get(MessageRouterConfig.MSG_RECEIVERS_NAMES_PROP_KEY).toList();\n\n\t\t\tif (compNames.contains(comp_name)) {\n\t\t\t\tcompNames.remove(comp_name);\n\t\t\t}\n\n\t\t\tmrProps = [ : ];\n\t\t\tmrProps.put(MessageRouterConfig.MSG_RECEIVERS_NAMES_PROP_KEY, (compNames as String[]));\n\t\t\tconf.putProperties(\"message-router\", mrProps);\n\n\t\t\tnew DelayedReloadTask().start();\n\t\t\tCommand.addTextField(res, \"Note\", \"Operation successful.\")\n\n\t\t\treturn res;\n\t\t} else if (!submit) {\n\t\t\tdef comp_class = Command.getFieldValue(p, COMP_CLASS);\n\n\t\t\tif (comp_class == null) {\n\t\t\t\tdef conf = XMPPServer.getConfigurator();\n\t\t\t\tdef initProps = conf.getDefConfigParams().\n\t\t\t\t\t\tfindAll { return it.key.startsWith(Configurable.GEN_COMP_NAME) && it.value.equals(comp_name); };\n\t\t\t\tdef suffix = null;\n\t\t\t\tinitProps.each {\n\t\t\t\t\tsuffix = it.key.substring(Configurable.GEN_COMP_NAME.length());\n\t\t\t\t}\n\t\t\t\tcomp_class = conf.getDefConfigParams().get(Configurable.GEN_COMP_CLASS + suffix);\n\n\t\t\t\tif (!comp_class) {\n\t\t\t\t\tcomp_class = MessageRouterConfig.COMPONENT_CLASSES.get(comp_name)\n\t\t\t\t};\n\t\t\t\tif (!comp_class) {\n\t\t\t\t\tcomp_class = MessageRouterConfig.COMP_CLUS_MAP.get(comp_name)\n\t\t\t\t};\n\t\t\t\tif (!comp_class && comp_name == \"basic-conf\") {\n\t\t\t\t\tcomp_class = XMPPServer.getConfigurator().\n\t\t\t\t\t\t\tgetClass().\n\t\t\t\t\t\t\tgetCanonicalName()\n\t\t\t\t};\n\t\t\t\tif (!comp_class) {\n\t\t\t\t\tthrow new Exception(\"Could not find component class for component: \" + comp_name)\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tdef res = (Iq) p.commandResult(Command.DataType.form)\n\t\t\tCommand.addFieldValue(res, COMP_NAME, comp_name, \"fixed\", \"Component name\");\n\t\t\tCommand.addHiddenField(res, COMP_CLASS, comp_class);\n\t\t\tCommand.addHiddenField(res, ACTION, action);\n\t\t\tCommand.addHiddenField(res, SUBMIT, SUBMIT);\n\n\t\t\tprepareComponentConfigForAdHoc(res, comp_name, comp_class);\n\n\t\t\treturn res;\n\t\t} else if (submit) {\n\t\t\tdef comp_class = Command.getFieldValue(p, COMP_CLASS);\n\n\t\t\t// here we should apply results of new or edit\n\t\t\tdef res = (Iq) p.commandResult(Command.DataType.result)\n\t\t\tdef props = getConfigFromAdHoc(p, comp_name, comp_class);\n\n\t\t\tdef conf = XMPPServer.getConfigurator();\n\t\t\tconf.putProperties(comp_name, props);\n\n\t\t\tdef initProps = conf.getDefConfigParams().\n\t\t\t\t\tfindAll { return it.key.startsWith(Configurable.GEN_COMP_NAME) && it.value.equals(comp_name); };\n\t\t\tif (initProps.isEmpty()) {\n\t\t\t\tdef suffix = 1;\n\t\t\t\twhile (!(conf.getDefConfigParams().\n\t\t\t\t\t\tfindAll {\n\t\t\t\t\t\t\treturn it.key.startsWith(Configurable.GEN_COMP_NAME) && it.key.endsWith(\"-\" + suffix);\n\t\t\t\t\t\t}.\n\t\t\t\t\t\tisEmpty())) {\n\t\t\t\t\tsuffix += 1;\n\t\t\t\t}\n\t\t\t\tconf.getDefConfigParams().put(Configurable.GEN_COMP_NAME + \"-\" + suffix, comp_name);\n\t\t\t\tconf.getDefConfigParams().put(Configurable.GEN_COMP_CLASS + \"-\" + suffix, comp_class);\n\t\t\t}\n\n\t\t\tdef mrProps = conf.getProperties(\"message-router\");\n\t\t\tdef compNames = mrProps.get(MessageRouterConfig.MSG_RECEIVERS_NAMES_PROP_KEY).toList();\n\n\t\t\tif (!compNames.contains(comp_name)) {\n\t\t\t\tcompNames.add(comp_name);\n\t\t\t}\n\n\t\t\tmrProps = [ : ];\n\t\t\tmrProps.put(MessageRouterConfig.MSG_RECEIVERS_NAMES_PROP_KEY, (compNames as String[]));\n\t\t\tmrProps.put(MessageRouterConfig.MSG_RECEIVERS_PROP_KEY + comp_name + \".class\", comp_class)\n\t\t\tconf.putProperties(\"message-router\", mrProps);\n\n\t\t\tnew DelayedReloadTask().start();\n\t\t\tCommand.addTextField(res, \"Note\", \"Operation successful.\");\n\t\t\treturn res;\n\t\t}\n\t}\n\n} catch (Exception ex) {\n\tex.printStackTrace();\n\tthrow ex;\n}"
  },
  {
    "path": "src/main/groovy/tigase/admin/CompRepoItemAdd.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n\nAdd an item in a component repository:\ntigase.db.ComponentRepository\nWorks only for some components which actually use the repository that way.\n\nAS:Description: Add new item\nAS:CommandId: comp-repo-item-add\nAS:Component: vhost-man,ext,ext-disco\nAS:ComponentClass: tigase.server.ext.ComponentProtocol,tigase.server.ext.ComponentProtocolManager\nAS:Group: Configuration\n */\n\npackage tigase.admin\n\nimport tigase.db.comp.ComponentRepository\nimport tigase.server.Command\nimport tigase.server.Iq\nimport tigase.server.Permissions\n\ndef MARKER = \"command-marker\"\n\ndef repo = (ComponentRepository) comp_repo\ndef p = (Iq) packet\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\ndef item = repo.getItemInstance()\ndef marker = Command.getFieldValue(p, MARKER)\n\ndef supportedComponents = [ \"vhost-man\" ]\ndef NOTIFY_CLUSTER = \"notify-cluster\"\nboolean clusterMode = Boolean.valueOf(System.getProperty(\"cluster-mode\", false.toString()));\nboolean notifyCluster = Boolean.valueOf(Command.getFieldValue(packet, NOTIFY_CLUSTER))\nQueue results = new LinkedList()\n\n\nif (marker == null) {\n\tdef result = p.commandResult(Command.DataType.form)\n\titem.addCommandFields(result)\n\tCommand.addHiddenField(result, MARKER, MARKER)\n\tif (clusterMode) {\n\t\tCommand.addHiddenField(result, NOTIFY_CLUSTER, true.toString())\n\t}\n\treturn result\n}\n\nif (clusterMode && notifyCluster && supportedComponents.contains(componentName)) {\n\tdef nodes = (List) connectedNodes\n\tif (nodes && nodes.size() > 0) {\n\t\tnodes.each { node ->\n\t\t\tdef forward = p.copyElementOnly();\n\t\t\tCommand.removeFieldValue(forward, NOTIFY_CLUSTER)\n\t\t\tCommand.addHiddenField(forward, NOTIFY_CLUSTER, false.toString())\n\t\t\tforward.setPacketTo(node);\n\t\t\tforward.setPermissions(Permissions.ADMIN);\n\n\t\t\tresults.offer(forward)\n\t\t}\n\t}\n}\n\ndef oldItem = item.getKey() != null ? repo.getItem(item.getKey()) : null;\ndef result = p.commandResult(Command.DataType.result)\n\ntry {\n\titem.initFromCommand(p)\n\n\tif (oldItem == null) {\n\t\tdef validateResult = repo.validateItem(item)\n\t\tif (validateResult == null || (isServiceAdmin && !item.getKey().isEmpty())) {\n\t\t\trepo.addItem(item)\n\t\t\tCommand.addTextField(result, \"Note\", \"Operation successful.\")\n\t\t\tif (validateResult != null) {\n\t\t\t\tCommand.addTextField(result, \"Note\", \"   \")\n\t\t\t\tCommand.addTextField(result, \"Warning\", validateResult)\n\t\t\t}\n\t\t} else {\n\t\t\tCommand.addTextField(result, \"Error\", \"The item did not pass validation checking.\")\n\t\t\tCommand.addTextField(result, \"Note\", \"   \")\n\t\t\tCommand.addTextField(result, \"Warning\", validateResult)\n\t\t}\n\t} else {\n\t\tCommand.addTextField(result, \"Error\", \"The item is already added, you can't add it twice.\")\n\t}\n} catch (IllegalArgumentException ex) {\n\tCommand.addTextField(result, \"Error\", ex.getMessage());\n}\n\nresults.add(result);\nreturn results;\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/CompRepoItemRemove.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n\nRemove an item from a component repository:\ntigase.db.ComponentRepository\nWorks only for some components which actually use the repository that way.\n\nAS:Description: Remove an item\nAS:CommandId: comp-repo-item-remove\nAS:Component: vhost-man,ext,ext-disco\nAS:ComponentClass: tigase.server.ext.ComponentProtocol,tigase.server.ext.ComponentProtocolManager\nAS:Group: Configuration\n */\n\npackage tigase.admin\n\nimport tigase.db.comp.ComponentRepository\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.server.Permissions\n\ndef ITEMS = \"item-list\"\n\ndef repo = (ComponentRepository) comp_repo\ndef p = (Packet) packet\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\ndef itemKey = Command.getFieldValue(packet, ITEMS)\n\ndef supportedComponents = [ \"vhost-man\" ]\ndef NOTIFY_CLUSTER = \"notify-cluster\"\nboolean clusterMode = Boolean.valueOf(System.getProperty(\"cluster-mode\", false.toString()));\nboolean notifyCluster = Boolean.valueOf(Command.getFieldValue(packet, NOTIFY_CLUSTER))\nQueue results = new LinkedList()\n\nif (itemKey == null) {\n\tdef items = repo.allItems()\n\tdef itemsStr = [ ]\n\tif (items.size() > 0) {\n\t\titems.each {\n\t\t\tif (isServiceAdmin || it.isOwner(stanzaFromBare.toString())) {\n\t\t\t\titemsStr += it.getKey()\n\t\t\t}\n\t\t}\n\t}\n\tif (itemsStr.size() > 0) {\n\t\tdef result = p.commandResult(Command.DataType.form)\n\t\tCommand.addFieldValue(result, ITEMS, itemsStr[0], \"List of items\",\n\t\t\t\t\t\t\t  (String[]) itemsStr, (String[]) itemsStr)\n\t\tif (clusterMode) {\n\t\t\tCommand.addHiddenField(result, NOTIFY_CLUSTER, true.toString())\n\t\t}\n\t\treturn result\n\t} else {\n\t\tdef result = p.commandResult(Command.DataType.result)\n\t\tCommand.addTextField(result, \"Note\", \"There are no items on the list\");\n\t\treturn result\n\t}\n}\n\nif (clusterMode && notifyCluster && supportedComponents.contains(componentName)) {\n\tdef nodes = (List) connectedNodes\n\tif (nodes && nodes.size() > 0) {\n\t\tnodes.each { node ->\n\t\t\tdef forward = p.copyElementOnly();\n\t\t\tCommand.removeFieldValue(forward, NOTIFY_CLUSTER)\n\t\t\tCommand.addHiddenField(forward, NOTIFY_CLUSTER, false.toString())\n\t\t\tforward.setPacketTo(node);\n\t\t\tforward.setPermissions(Permissions.ADMIN);\n\n\t\t\tresults.offer(forward)\n\t\t}\n\t}\n}\n\ndef result = p.commandResult(Command.DataType.result)\ndef item = repo.getItem(itemKey)\nif (item == null) {\n\tCommand.addTextField(result, \"Error\", \"No such item, deletion impossible.\");\n} else {\n\tif (isServiceAdmin || item.isOwner(stanzaFromBare.toString())) {\n\t\trepo.removeItem(itemKey)\n\t\tCommand.addTextField(result, \"Note\", \"Operation successful\")\n\t} else {\n\t\tCommand.addTextField(result, \"Error\",\n\t\t\t\t\t\t\t \"You are not the Item owner or you have no \" + \"enough permission to remove the item.\")\n\t}\n}\n\nresults.add(result);\nreturn results;\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/CompRepoItemUpdate.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n\nUpdate an item in a component repository:\ntigase.db.ComponentRepository\nWorks only for some components which actually use the repository that way.\n\nAS:Description: Update item configuration\nAS:CommandId: comp-repo-item-update\nAS:Component: vhost-man,ext,basic-conf,ext-disco\nAS:ComponentClass: tigase.server.ext.ComponentProtocol,tigase.server.ext.ComponentProtocolManager\nAS:Group: Configuration\n */\n\npackage tigase.admin\n\nimport tigase.db.comp.ComponentRepository\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.server.Permissions\n\ndef MARKER = \"command-marker\"\ndef ITEMS = \"item-list\"\n\ndef repo = (ComponentRepository) comp_repo\ndef p = (Packet) packet\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\ndef itemKey = Command.getFieldValue(packet, ITEMS)\ndef marker = Command.getFieldValue(packet, MARKER)\n\ndef supportedComponents = [ \"vhost-man\" ]\ndef NOTIFY_CLUSTER = \"notify-cluster\"\nboolean clusterMode = Boolean.valueOf(System.getProperty(\"cluster-mode\", false.toString()));\nboolean notifyCluster = Boolean.valueOf(Command.getFieldValue(packet, NOTIFY_CLUSTER))\nQueue results = new LinkedList()\n\nif (itemKey == null) {\n\tdef items = repo.allItems()\n\tdef itemsStr = [ ]\n\tif (items.size() > 0) {\n\t\titems.each {\n\t\t\tif (isServiceAdmin || it.isOwner(stanzaFromBare.toString()) || it.isAdmin(stanzaFromBare.toString())) {\n\t\t\t\titemsStr += it.getKey()\n\t\t\t}\n\t\t}\n\t}\n\tif (itemsStr.size() > 0) {\n\t\tdef result = p.commandResult(Command.DataType.form)\n\t\tCommand.addFieldValue(result, ITEMS, itemsStr[0], \"List of items\",\n\t\t\t\t\t\t\t  (String[]) itemsStr, (String[]) itemsStr)\n\t\treturn result\n\t} else {\n\t\tdef result = p.commandResult(Command.DataType.result)\n\t\tCommand.addTextField(result, \"Error\", \"There are no items on the list\");\n\t\treturn result\n\t}\n}\n\nif (marker == null) {\n\tdef item = repo.getItem(itemKey)\n\tif (item == null) {\n\t\tCommand.addTextField(result, \"Error\", \"No such item, update impossible.\");\n\t} else {\n\t\tif (isServiceAdmin || item.isOwner(stanzaFromBare.toString()) || item.isAdmin(stanzaFromBare.toString())) {\n\t\t\tdef result = p.commandResult(Command.DataType.form)\n\t\t\titem.addCommandFields(result)\n\t\t\tCommand.addHiddenField(result, MARKER, MARKER)\n\t\t\tCommand.addHiddenField(result, ITEMS, itemKey)\n\t\t\tif (clusterMode) {\n\t\t\t\tCommand.addHiddenField(result, NOTIFY_CLUSTER, true.toString())\n\t\t\t}\n\t\t\treturn result\n\t\t} else {\n\t\t\tdef result = p.commandResult(Command.DataType.result)\n\t\t\tCommand.addTextField(result, \"Error\", \"You do not have enough permissions to manage this item.\")\n\t\t\treturn result\n\t\t}\n\t}\n}\n\nif (clusterMode && notifyCluster && supportedComponents.contains(componentName)) {\n\tdef nodes = (List) connectedNodes\n\n\tif (nodes && nodes.size() > 0) {\n\t\tnodes.each { node ->\n\t\t\tdef forward = p.copyElementOnly();\n\t\t\tCommand.removeFieldValue(forward, NOTIFY_CLUSTER)\n\t\t\tCommand.addHiddenField(forward, NOTIFY_CLUSTER, false.toString())\n\t\t\tforward.setPacketTo(node);\n\t\t\tforward.setPermissions(Permissions.ADMIN);\n\n\t\t\tresults.offer(forward)\n\t\t}\n\t}\n}\n\ndef result = p.commandResult(Command.DataType.result)\n\ndef item = repo.getItemInstance()\ntry {\n\titem.initFromCommand(packet)\n\tdef oldItem = repo.getItem(item.getKey())\n\n\tif (oldItem == null) {\n\t\tCommand.addTextField(result, \"Error\", \"The item you try to update does not exist.\");\n\t} else {\n\t\tdef validateResult = repo.validateItem(item)\n\t\tif (validateResult == null || isServiceAdmin) {\n\t\t\tif (isServiceAdmin || oldItem.isOwner(stanzaFromBare.toString())\n\t\t\t\t\t// we allow changes by admins, except for changing the owner of the domain.\n\t\t\t\t\t|| oldItem.isAdmin(stanzaFromBare.toString()) && oldItem.getOwner().equals(item.getOwner())) {\n\t\t\t\trepo.addItem(item)\n\t\t\t\tCommand.addTextField(result, \"Note\", \"Operation successful\");\n\t\t\t\tif (validateResult != null) {\n\t\t\t\t\tCommand.addTextField(result, \"Note\", \"   \")\n\t\t\t\t\tCommand.addTextField(result, \"Warning\", validateResult)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tCommand.addTextField(result, \"Error\",\n\t\t\t\t\t\t\t\t\t \"You are not the Item owner or you have no \" + \"enough permission to change the item.\")\n\t\t\t}\n\t\t} else {\n\t\t\tCommand.addTextField(result, \"Error\", \"The item did not pass validation checking.\")\n\t\t\tCommand.addTextField(result, \"Note\", \"   \")\n\t\t\tCommand.addTextField(result, \"Warning\", validateResult)\n\t\t}\n\t}\n} catch (IllegalArgumentException ex) {\n\tCommand.addTextField(result, \"Error\", ex.getMessage());\n}\n\n\nresults.add(result);\nreturn results;\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/CompRepoReload.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n\nReload a component repository:\ntigase.db.ComponentRepository\nWorks only for some components which actually use the repository that way.\n\nAS:Description: Reload component repository\nAS:CommandId: comp-repo-reload\nAS:Component: vhost-man,ext,ext-disco\n*/\n\npackage tigase.admin\n\nimport tigase.db.comp.ComponentRepository\n\ndef repo = (ComponentRepository) comp_repo\nrepo.reload()\n\ndef result = \"Reloaded items - \" + repo.size() + \":\\n\"\ndef items = repo.allItems()\nif (items.size() > 0) {\n\titems.each { result += \"\\n\" + it.getKey() }\n}\n\nreturn result\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/ConnectionTime.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n Calculates maximum and average connection time for all connected users\n AS:Description: Connections time\n AS:CommandId: connection-time\n AS:Component: sess-man\n */\n\npackage tigase.admin\n\nimport tigase.server.Command\nimport tigase.server.Iq\nimport tigase.xmpp.XMPPResourceConnection\n\ndef user_connections = (Map) userConnections\ndef p = (Iq) packet\n\ndef total_time = 0\ndef max_time = 0\ndef start_time = System.currentTimeMillis()\nuser_connections.entrySet().each {\n\tif (!it.getKey().toString().startsWith(\"sess-man\")) {\n\t\tdef session = (XMPPResourceConnection) it.getValue()\n\t\tdef creation_time = start_time - session.getCreationTime()\n\t\ttotal_time += creation_time\n\t\tif (creation_time > max_time) {\n\t\t\tmax_time = creation_time\n\t\t}\n\t}\n}\ndef average_time = total_time / user_connections.size()\n\n\ndef res = (Iq) p.commandResult(Command.DataType.result)\nCommand.addFieldMultiValue(res, \"Connections time: \",\n\t\t\t\t\t\t   [ \"Longest connection: \" + (max_time / 1000),\n\t\t\t\t\t\t\t \"Average connection time:\" + (average_time / 1000) ])\nreturn res\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/DNSQuery.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n User add script as described in XEP-0133:\n http://xmpp.org/extensions/xep-0133.html#add-user\n AS:Description: DNS Query\n AS:CommandId: query-dns\n AS:Component: vhost-man\n */\n\npackage tigase.admin\n\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.util.dns.DNSEntry\nimport tigase.util.dns.DNSResolverFactory\n\ndef DOMAIN = \"domain-name\"\n\ndef p = (Packet) packet\n\ndef domain = Command.getFieldValue(packet, DOMAIN)\n\nif (domain == null) {\n\tdef result = p.commandResult(Command.DataType.form);\n\n\tCommand.addTitle(result, \"Provide domain name\")\n\tCommand.addInstructions(result, \"Fill out this form to query DNS for domain.\")\n\n\tCommand.addFieldValue(result, DOMAIN, domain ?: \"\", \"text-single\",\n\t\t\t\t\t\t  \"Domain name to query DNS\")\n\n\treturn result\n}\n\ndef result = p.commandResult(Command.DataType.result)\ndef response_data = [ ]\ntry {\n\tresponse_data += \"IP: \" + DNSResolverFactory.getInstance().getHostIP(domain)\n} catch (Exception ex) {\n\tresponse_data += \"IP: \" + ex.toString()\n}\ntry {\n\tDNSEntry[] entries = DNSResolverFactory.getInstance().getHostSRV_Entries(domain)\n\tint cnt = 1\n\tentries.each {\n\t\tresponse_data += \"SRV \" + (cnt++) + \": \" + it.toString()\n\t}\n\tresponse_data += \"Selected SRV: \" + DNSResolverFactory.getInstance().getHostSRV_Entry(domain).toString()\n\tresponse_data += \"Selected SRV IP: \" + DNSResolverFactory.getInstance().getHostSRV_IP(domain)\n\n\n\tCommand.addFieldMultiValue(result, \"DNS Response \", response_data);\n} catch (Exception ex) {\n\tCommand.addTextField(result, \"Note\", \"Problem querying DNS for domain: \" + domain);\n\t// There is a new API to pass exception, for now we have to do it manually\n\t//Command.addFieldMultiValue(result, \"Exception: \", ex)\n\tdef stes = [ ]\n\tstes += ex.toString()\n\tex.getStackTrace().each {\n\t\tstes += it.toString()\n\t}\n\tCommand.addFieldMultiValue(result, \"Exception: \", stes);\n\n}\n\nreturn result\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/DeleteMOTD.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\nDelete Message of the Day\nAS:Description: Delete Message of the Day\nAS:CommandId: http://jabber.org/protocol/admin#delete-motd\nAS:Component: sess-man\nAS:Group: Configuration\n */\npackage tigase.admin\n\nimport groovy.transform.CompileStatic\nimport tigase.kernel.core.Kernel\nimport tigase.server.Command\nimport tigase.server.Iq\nimport tigase.server.Packet\nimport tigase.server.xmppsession.SessionManager\nimport tigase.xmpp.impl.MotdProcessor\n\nKernel kernel = (Kernel) kernel;\nSessionManager component = (SessionManager) component\npacket = (Iq) packet\n\n@CompileStatic\nPacket process(Kernel kernel, SessionManager component, Iq p) {\n\n\tif (!component.isAdmin(p.getStanzaFrom())) {\n\t\tdef result = p.commandResult(Command.DataType.result)\n\t\tCommand.addTextField(result, \"Error\", \"You are not service administrator\")\n\t\treturn result\n\t}\n\n\tdef result = p.commandResult(Command.DataType.result)\n\tif (kernel.getDependencyManager().getBeanConfigs(MotdProcessor.class, null, null, true).isEmpty()) {\n\t\tCommand.addTextField(result, \"Error\", \"MotDProcessor is disabled\");\n\t\treturn result\n\t}\n\n\tMotdProcessor motdProcessor = kernel.getInstance(MotdProcessor.class)\n\tmotdProcessor.setMotd(null)\n\treturn result;\n}\n\nreturn process(kernel, component, packet)"
  },
  {
    "path": "src/main/groovy/tigase/admin/DeleteUser.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n User delete script as described in XEP-0133:\n http://xmpp.org/extensions/xep-0133.html#delete-user\n AS:Description: Delete user\n AS:CommandId: http://jabber.org/protocol/admin#delete-user\n AS:Component: sess-man\n AS:Group: Users\n */\n\npackage tigase.admin\n\nimport tigase.db.AuthRepository\nimport tigase.db.TigaseDBException\nimport tigase.db.UserNotFoundException\nimport tigase.db.UserRepository\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.server.Permissions\nimport tigase.vhosts.VHostManagerIfc\nimport tigase.xmpp.StanzaType\nimport tigase.xmpp.jid.BareJID\n\ndef JIDS = \"accountjids\"\n\ndef p = (Packet) packet\ndef auth_repo = (AuthRepository) authRepository\ndef user_repo = (UserRepository) userRepository\ndef vhost_man = (VHostManagerIfc) vhostMan\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\ndef NOTIFY_CLUSTER = \"notify-cluster\"\nboolean clusterMode = Boolean.valueOf(System.getProperty(\"cluster-mode\", false.toString()));\ndef notifyClusterStr = Command.getFieldValue(packet, NOTIFY_CLUSTER);\nboolean notifyCluster = (notifyClusterStr != null) ? Boolean.valueOf(notifyClusterStr) : true;\n\ndef user_sessions = (Map) userSessions;\n\ndef userJids = Command.getFieldValues(packet, JIDS)\n\nif (userJids == null) {\n\tdef result = p.commandResult(Command.DataType.form);\n\n\tCommand.addTitle(result, \"Deleting a User\")\n\tCommand.addInstructions(result, \"Fill out this form to delete a user.\")\n\n\tCommand.addFieldValue(result, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\",\n\t\t\t\t\t\t  \"hidden\")\n\tCommand.addFieldValue(result, JIDS, userJids ?: \"\", \"jid-multi\",\n\t\t\t\t\t\t  \"The Jabber ID(s) to delete\")\n\tif (clusterMode) {\n\t\tCommand.addHiddenField(result, NOTIFY_CLUSTER, true.toString())\n\t}\n\n\treturn result\n}\n\ndef results = new LinkedList<Packet>();\n\ndef closeUserSessions = { userJid ->\n\ttry {\n\t\tdef bareJID = BareJID.bareJIDInstance(userJid)\n\t\tdef sess = user_sessions.get(bareJID);\n\t\tif (sess != null) {\n\t\t\tdef conns = sess.getConnectionIds();\n\t\t\tfor (conn in conns) {\n\t\t\t\tdef res = sess.getResourceForConnectionId(conn);\n\t\t\t\tif (res != null) {\n\t\t\t\t\tdef commandClose = Command.CLOSE.getPacket(p.getStanzaTo(), conn,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   StanzaType.set, res.nextStanzaId());\n\t\t\t\t\tresults.offer(commandClose);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch (Exception ex) {\n\t\tex.printStackTrace();\n\t}\n};\n\nif (clusterMode) {\n\tif (!notifyCluster) {\n\t\tfor (userJid in userJids) {\n\t\t\tcloseUserSessions(userJid);\n\t\t}\n\t\treturn results;\n\t}\n}\n\n\ndef result = p.commandResult(Command.DataType.result)\nresults.offer(result);\ndef msgs = [ ];\ndef errors = [ ];\nfor (userJid in userJids) {\n\ttry {\n\t\tdef bareJID = BareJID.bareJIDInstance(userJid)\n\t\tif (isAllowedForDomain.apply(bareJID.getDomain())) {\n\t\t\tif (user_repo.userExists(bareJID)) {\n\t\t\t\tauth_repo.removeUser(bareJID)\n\t\t\t\ttry {\n\t\t\t\t\tuser_repo.removeUser(bareJID)\n\t\t\t\t} catch (UserNotFoundException ex) {\n\t\t\t\t\t// We ignore this error here. If auth_repo and user_repo are in fact the same\n\t\t\t\t\t// database, then user has been already removed with the auth_repo.removeUser(...)\n\t\t\t\t\t// then the second call to user_repo may throw the exception which is fine.\n\t\t\t\t}\n\t\t\t\tif (clusterMode && notifyCluster) {\n\t\t\t\t\tdef nodes = (List) clusterStrategy.getNodesConnected();\n\t\t\t\t\tif (nodes && nodes.size() > 0) {\n\t\t\t\t\t\tnodes.each { node ->\n\t\t\t\t\t\t\tdef forward = p.copyElementOnly();\n\t\t\t\t\t\t\tCommand.removeFieldValue(forward, NOTIFY_CLUSTER)\n\t\t\t\t\t\t\tCommand.addHiddenField(forward, NOTIFY_CLUSTER, false.toString())\n\t\t\t\t\t\t\tforward.setPacketTo(node);\n\t\t\t\t\t\t\tforward.setPermissions(Permissions.ADMIN);\n\n\t\t\t\t\t\t\tresults.offer(forward)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcloseUserSessions(userJid);\n\n\t\t\t\tmsgs.add(\"Operation successful for user \" + userJid);\n\t\t\t} else {\n\t\t\t\terrors.add(\"User \" + userJid + \" not found, can't be deleted.\");\n\t\t\t}\n\t\t} else {\n\t\t\terrors.add(\"You do not have enough permissions to delete accounts for domain \" + bareJID.getDomain() + \".\");\n\t\t}\n\t} catch (UserNotFoundException ex) {\n\t\terrors.add(\"User \" + userJid + \" not exists, can't be deleted.\");\n\t} catch (TigaseDBException ex) {\n\t\terrors.add(\"Problem accessing database, user \" + userJid + \" not deleted.\");\n\t}\n}\n\nif (!msgs.isEmpty()) {\n\tCommand.addFieldMultiValue(result, \"Notes\", msgs)\n};\n\nif (!errors.isEmpty()) {\n\tCommand.addFieldMultiValue(result, \"Errors\", errors)\n};\nreturn results\n\n\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/DeleteWelcomeMessage.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\nDelete Welcome Message\nAS:Description: Delete Welcome Message\nAS:CommandId: http://jabber.org/protocol/admin#delete-welcome\nAS:Component: sess-man\nAS:Group: Configuration\n */\npackage tigase.admin\n\nimport groovy.transform.CompileStatic\nimport tigase.kernel.core.Kernel\nimport tigase.server.Command\nimport tigase.server.Iq\nimport tigase.server.Packet\nimport tigase.server.xmppsession.SessionManager\nimport tigase.xmpp.impl.JabberIqRegister\n\nKernel kernel = (Kernel) kernel;\nSessionManager component = (SessionManager) component\npacket = (Iq) packet\n\n@CompileStatic\nPacket process(Kernel kernel, SessionManager component, Iq p) {\n\n\tif (!component.isAdmin(p.getStanzaFrom())) {\n\t\tdef result = p.commandResult(Command.DataType.result)\n\t\tCommand.addTextField(result, \"Error\", \"You are not service administrator\")\n\t\treturn result\n\t}\n\n\tdef result = p.commandResult(Command.DataType.result)\n\tif (kernel.getDependencyManager().getBeanConfigs(JabberIqRegister.class, null, null, true).isEmpty()) {\n\t\tCommand.addTextField(result, \"Error\", \"JabberIqRegister is disabled\");\n\t\treturn result\n\t}\n\n\tJabberIqRegister registerProcessor = kernel.getInstance(JabberIqRegister.class)\n\tregisterProcessor.setWelcomeMessage(null)\n\treturn result;\n}\n\nreturn process(kernel, component, packet)"
  },
  {
    "path": "src/main/groovy/tigase/admin/DisableUser.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n User modify script\n\n AS:Description: Disable user\n AS:CommandId: http://jabber.org/protocol/admin#disable-user\n AS:Component: sess-man\n AS:Group: Users\n */\n\npackage tigase.admin\n\nimport tigase.db.AuthRepository\nimport tigase.db.TigaseDBException\nimport tigase.db.UserNotFoundException\nimport tigase.db.UserRepository\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.server.Permissions\nimport tigase.vhosts.VHostItem\nimport tigase.vhosts.VHostManagerIfc\nimport tigase.xmpp.StanzaType\nimport tigase.xmpp.jid.BareJID\n\ndef JIDS = \"accountjids\"\n\ndef p = (Packet) packet\ndef auth_repo = (AuthRepository) authRepository\ndef user_repo = (UserRepository) userRepository\ndef vhost_man = (VHostManagerIfc) vhostMan\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\ndef NOTIFY_CLUSTER = \"notify-cluster\"\nboolean clusterMode = Boolean.valueOf(System.getProperty(\"cluster-mode\", false.toString()))\ndef notifyClusterStr = Command.getFieldValue(packet, NOTIFY_CLUSTER)\nboolean notifyCluster = (notifyClusterStr != null) ? Boolean.valueOf(notifyClusterStr) : true\n\ndef user_sessions = (Map) userSessions\n\ndef userJids = Command.getFieldValues(packet, JIDS)\n\nif (userJids == null) {\n\tdef result = p.commandResult(Command.DataType.form)\n\n\tCommand.addTitle(result, \"Disabling a User\")\n\tCommand.addInstructions(result, \"Fill out this form to disable a user.\")\n\n\tCommand.addFieldValue(result, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\",\n\t\t\t\t\t\t  \"hidden\")\n\tCommand.addFieldValue(result, JIDS, userJids ?: \"\", \"jid-multi\",\n\t\t\t\t\t\t  \"The Jabber ID(s) to disable\")\n\tif (clusterMode) {\n\t\tCommand.addHiddenField(result, NOTIFY_CLUSTER, true.toString())\n\t}\n\n\treturn result\n}\n\ndef results = new LinkedList<Packet>()\n\ndef closeUserSessions = { userJid ->\n\ttry {\n\t\tdef bareJID = BareJID.bareJIDInstance(userJid)\n\t\tdef sess = user_sessions.get(bareJID)\n\t\tif (sess != null) {\n\t\t\tdef conns = sess.getConnectionIds()\n\t\t\tfor (conn in conns) {\n\t\t\t\tdef res = sess.getResourceForConnectionId(conn)\n\t\t\t\tif (res != null) {\n\t\t\t\t\tdef commandClose = Command.CLOSE.getPacket(p.getStanzaTo(), conn,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   StanzaType.set, res.nextStanzaId())\n\t\t\t\t\tresults.offer(commandClose)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch (Exception ex) {\n\t\tex.printStackTrace()\n\t}\n}\n\nif (clusterMode) {\n\tif (!notifyCluster) {\n\t\tfor (userJid in userJids) {\n\t\t\tcloseUserSessions(userJid)\n\t\t}\n\t\treturn results\n\t}\n}\n\nif (clusterMode && notifyCluster) {\n\tdef nodes = (List) clusterStrategy.getNodesConnected()\n\tif (nodes && nodes.size() > 0) {\n\t\tnodes.each { node ->\n\t\t\tdef forward = p.copyElementOnly()\n\t\t\tCommand.removeFieldValue(forward, NOTIFY_CLUSTER)\n\t\t\tCommand.addHiddenField(forward, NOTIFY_CLUSTER, false.toString())\n\t\t\tforward.setPacketTo(node)\n\t\t\tforward.setPermissions(Permissions.ADMIN)\n\n\t\t\tresults.offer(forward)\n\t\t}\n\t}\n}\n\ndef result = p.commandResult(Command.DataType.result)\ndef msgs = [ ]\ndef errors = [ ]\nfor (userJid in userJids) {\n\ttry {\n\t\tdef bareJID = BareJID.bareJIDInstance(userJid)\n\t\tVHostItem vhost = vhost_man.getVHostItem(bareJID.getDomain())\n\t\tif (isAllowedForDomain.apply(bareJID.getDomain())) {\n\t\t\tif (user_repo.userExists(bareJID)) {\n\t\t\t\ttry {\n\t\t\t\t\tauth_repo.setAccountStatus(bareJID, AuthRepository.AccountStatus.disabled)\n\t\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\t\terrors.add(\"Account \" + userJid + \" state was not enabled: \" + ex.getMessage())\n\t\t\t\t}\n\t\t\t\tcloseUserSessions(userJid)\n\n\t\t\t\tmsgs.add(\"Operation successful for user \" + userJid)\n\t\t\t} else {\n\t\t\t\tmsgs.add(\"User \" + userJid + \" doesn't exist\")\n\t\t\t}\n\t\t} else {\n\t\t\terrors.add(\"You do not have enough permissions to enable accounts for domain \" + bareJID.getDomain() + \".\")\n\t\t}\n\t} catch (UserNotFoundException ex) {\n\t\terrors.add(\"User \" + userJid + \" not exists, can't be enabled.\")\n\t} catch (TigaseDBException ex) {\n\t\terrors.add(\"Problem accessing database, user \" + userJid + \" not enabled.\")\n\t}\n}\n\nif (!msgs.isEmpty()) {\n\tCommand.addFieldMultiValue(result, \"Notes\", msgs)\n}\n\nif (!errors.isEmpty()) {\n\tCommand.addFieldMultiValue(result, \"Errors\", errors)\n}\n\nresults.offer(result)\n\nreturn results\n\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/EditMOTD.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\nEdit Message of the Day\nAS:Description: Edit Message of the Day\nAS:CommandId: http://jabber.org/protocol/admin#edit-motd\nAS:Component: sess-man\nAS:Group: Configuration\n */\npackage tigase.admin\n\nimport groovy.transform.CompileStatic\nimport tigase.kernel.core.Kernel\nimport tigase.server.Command\nimport tigase.server.Iq\nimport tigase.server.Packet\nimport tigase.server.xmppsession.SessionManager\nimport tigase.xmpp.impl.MotdProcessor\n\nKernel kernel = (Kernel) kernel;\nSessionManager component = (SessionManager) component\npacket = (Iq) packet\n\n@CompileStatic\nPacket process(Kernel kernel, SessionManager component, Iq p) {\n\tString MOTD = \"motd\"\n\n\tif (!component.isAdmin(p.getStanzaFrom())) {\n\t\tdef result = p.commandResult(Command.DataType.result)\n\t\tCommand.addTextField(result, \"Error\", \"You are not service administrator\")\n\t\treturn result\n\t}\n\n\tif (kernel.getDependencyManager().getBeanConfigs(MotdProcessor.class, null, null, true).isEmpty()) {\n\t\tdef result = p.commandResult(Command.DataType.result)\n\t\tCommand.addTextField(result, \"Error\", \"MotDProcessor is disabled\");\n\t\treturn result\n\t}\n\n\tdef motd = Command.getFieldValues(p, MOTD)\n\tif (!motd) {\n\t\tdef result = p.commandResult(Command.DataType.form)\n\n\t\tCommand.addTitle(result, \"Editing the Message of the Day\")\n\t\tCommand.addInstructions(result, \"Fill out this form to edit the message of the day.\")\n\t\tCommand.addHiddenField(result, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\")\n\t\tMotdProcessor motdProcessor = kernel.getInstance(MotdProcessor.class)\n\t\tCommand.addFieldMultiValue(result, MOTD, (motdProcessor.getMotd()?.split(\"\\n\") ?: [ ]) as List<String>,\n\t\t\t\t\t\t\t\t   \"Message of the Day\")\n\n\t\treturn result\n\t} else {\n\t\tdef result = p.commandResult(Command.DataType.result)\n\t\tMotdProcessor motdProcessor = kernel.getInstance(MotdProcessor.class)\n\t\tmotdProcessor.setMotd(motd.join(\"\\n\"))\n\t\treturn result\n\t}\n}\n\nreturn process(kernel, component, packet)"
  },
  {
    "path": "src/main/groovy/tigase/admin/EditUser.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n User modify script\n\n AS:Description: Modify user\n AS:CommandId: modify-user\n AS:Component: sess-man\n AS:Group: Users\n */\n\npackage tigase.admin\n\nimport tigase.db.AuthRepository\nimport tigase.db.TigaseDBException\nimport tigase.db.UserRepository\nimport tigase.server.Command\nimport tigase.server.Iq\nimport tigase.vhosts.VHostManagerIfc\nimport tigase.xmpp.jid.BareJID\n\ndef JID = \"accountjid\"\ndef EMAIL = \"email\"\n\ndef p = (Iq) packet\ndef auth_repo = (AuthRepository) authRepository\ndef user_repo = (UserRepository) userRepository\ndef vhost_man = (VHostManagerIfc) vhostMan\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\ndef userJid = Command.getFieldValue(packet, JID)\ndef userEmail = Command.getFieldValue(packet, EMAIL)\n\nif (userJid == null) {\n\tdef result = p.commandResult(Command.DataType.form)\n\n\t//Command.addFieldValue(result, \"FORM_TYPE\", \"\", \"hidden\")\n\tCommand.addTitle(result, \"Modifying a User\")\n\tCommand.addInstructions(result, \"Fill out this form to modify a user.\")\n\n\tCommand.addFieldValue(result, JID, userJid ?: \"\", \"jid-single\",\n\t\t\t\t\t\t  \"The Jabber ID for the account to be modified\")\n\n\treturn result\n}\n\ndef result = null\ntry {\n\tdef bareJID = BareJID.bareJIDInstance(userJid)\n\tif (isAllowedForDomain.apply(bareJID.getDomain())) {\n\n\t\tif (Command.getFieldValue(packet, \"FORM_TYPE\") == null ||\n\t\t\t\tCommand.getFieldValue(packet, \"FORM_TYPE\").isEmpty()) {\n\t\t\t//if (Command.getFieldValue(packet, EMAIL) == null)\n\t\t\tresult = p.commandResult(Command.DataType.form)\n\n\t\t\tCommand.addTitle(result, \"Modifying a User\")\n\t\t\tCommand.addInstructions(result, \"Fill out this form to modify a user \" + (userJid ?: \"\"))\n\n\t\t\tCommand.addFieldValue(result, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\",\n\t\t\t\t\t\t\t\t  \"hidden\")\n\t\t\tCommand.addFieldValue(result, JID, userJid ?: \"\", \"hidden\")\n\t\t\tCommand.addFieldValue(result, EMAIL, user_repo.getData(bareJID, \"email\") ?: \"\", \"text-single\",\n\t\t\t\t\t\t\t\t  \"Email address\")\n\n\t\t\tCommand.addCheckBoxField(result, \"Account enabled\",\n\t\t\t\t\t\t\t\t\t auth_repo.getAccountStatus(bareJID) == AuthRepository.AccountStatus.active)\n//\t\t\t-- add disabled/enabled? vcard? roster?\n\t\t} else {\n\t\t\tresult = p.commandResult(Command.DataType.result)\n\t\t\tuser_repo.setData(bareJID, \"email\", userEmail)\n\t\t\tCommand.addTextField(result, \"Note\", \"Operation successful\")\n\t\t\ttry {\n\t\t\t\tauth_repo.setAccountStatus(bareJID, Command.getCheckBoxFieldValue(p,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"Account enabled\") ? AuthRepository.AccountStatus.active :\n\t\t\t\t\t\t\t\t\t\t\t\t\tAuthRepository.AccountStatus.disabled)\n\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\tCommand.addTextField(result, \"Warning\",\n\t\t\t\t\t\t\t\t\t \"Account state was not changed as it is not supported by used auth repository: \" +\n\t\t\t\t\t\t\t\t\t\t\t ex.getMessage())\n\t\t\t}\n\t\t}\n\t} else {\n\t\tresult = p.commandResult(Command.DataType.result)\n\t\tCommand.addTextField(result, \"Error\", \"You do not have enough permissions to create account for this domain.\")\n\t}\n} catch (TigaseDBException ex) {\n\tresult = p.commandResult(Command.DataType.result)\n\tCommand.addTextField(result, \"Note\", \"Problem accessing database, user not added.\")\n}\n\nreturn result"
  },
  {
    "path": "src/main/groovy/tigase/admin/EndUserSession.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n User delete script as described in XEP-0133:\n http://jabber.org/protocol/admin#end-user-session\n AS:Description: End User Session\n AS:CommandId: http://jabber.org/protocol/admin#end-user-session\n AS:Component: sess-man\n AS:Group: Users\n */\n\npackage tigase.admin\n\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.server.Permissions\nimport tigase.vhosts.VHostManagerIfc\nimport tigase.xmpp.StanzaType\nimport tigase.xmpp.XMPPResourceConnection\nimport tigase.xmpp.jid.JID\nimport tigase.xml.Element\n\ndef JIDS = \"accountjids\"\ndef REASON = \"reason\";\n\ndef p = (Packet) packet\n//def auth_repo =/**/ (AuthRepository)authRepository\n//def user_repo = (UserRepository)userRepository\ndef vhost_man = (VHostManagerIfc) vhostMan\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\ndef NOTIFY_CLUSTER = \"notify-cluster\"\nboolean clusterMode = Boolean.valueOf(System.getProperty(\"cluster-mode\", false.toString()));\ndef notifyClusterStr = Command.getFieldValue(packet, NOTIFY_CLUSTER);\nboolean notifyCluster = (notifyClusterStr != null) ? Boolean.valueOf(notifyClusterStr) : true;\n\ndef user_sessions = (Map) userSessions;\n\ndef userJids = Command.getFieldValues(packet, JIDS)\ndef reason = Command.getFieldValue(packet, REASON)\n\nif (userJids == null) {\n\tdef result = p.commandResult(Command.DataType.form);\n\n\tCommand.addTitle(result, \"Ending session for the users\")\n\tCommand.addInstructions(result,\n\t\t\t\t\t\t\t\"Fill out this form to end user(s) session(s). BareJID - will end all user sessions, FullJID - only particular session.\")\n\n\tCommand.addFieldValue(result, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\",\n\t\t\t\t\t\t  \"hidden\")\n\tCommand.addFieldValue(result, JIDS, userJids ?: \"\", \"jid-multi\",\n\t\t\t\t\t\t  \"The Jabber ID(s) for which end session\")\n\tCommand.addFieldValue(result, REASON, reason ?: \"\", \"text-single\", \"Reason to display to the user\");\n\tif (clusterMode) {\n\t\tCommand.addHiddenField(result, NOTIFY_CLUSTER, true.toString())\n\t}\n\n\treturn result\n}\n\ndef results = new LinkedList<Packet>();\n\n\nif (clusterMode && notifyCluster) {\n\tdef nodes = (List) clusterStrategy.getNodesConnected();\n\tif (nodes && nodes.size() > 0) {\n\t\tnodes.each { node ->\n\t\t\tdef forward = p.copyElementOnly();\n\t\t\tCommand.removeFieldValue(forward, NOTIFY_CLUSTER)\n\t\t\tCommand.addHiddenField(forward, NOTIFY_CLUSTER, false.toString())\n\t\t\tforward.setPacketTo(node);\n\t\t\tforward.setPermissions(Permissions.ADMIN);\n\n\t\t\tresults.offer(forward)\n\t\t}\n\t}\n}\n\ndef result = p.commandResult(Command.DataType.result)\nresults.offer(result);\ndef msgs = [ ];\ndef errors = [ ];\nfor (userJid in userJids) {\n\ttry {\n\n\t\tJID userFullJID = JID.jidInstance(userJid)\n\t\tdef sess = user_sessions.get(userFullJID.getBareJID());\n\t\tif (isAllowedForDomain.apply(userFullJID.getDomain())) {\n\n\t\t\tif (sess != null) {\n\t\t\t\tdef conns = sess.getConnectionIds();\n\t\t\t\tfor (conn in conns) {\n\t\t\t\t\tXMPPResourceConnection res = sess.getResourceForConnectionId(conn);\n\t\t\t\t\tif (res != null && userFullJID.getResource() == null ||\n\t\t\t\t\t\t\t(userFullJID.getResource() == res.getResource())) {\n\t\t\t\t\t\tdef commandClose = Command.CLOSE.getPacket(p.getStanzaTo(), conn,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   StanzaType.set, res.nextStanzaId());\n\t\t\t\t\t\tif (reason != null && !reason.isEmpty()) {\n\t\t\t\t\t\t\tElement commandEl = commandClose.getElemChild(\"command\", Command.XMLNS);\n\t\t\t\t\t\t\tElement el = new Element(\"undefined-condition\");\n\t\t\t\t\t\t\tel.setXMLNS(\"urn:ietf:params:xml:ns:xmpp-streams\");\n\t\t\t\t\t\t\tcommandEl.addChild(el);\n\t\t\t\t\t\t\tel = new Element(\"text\", reason);\n\t\t\t\t\t\t\tel.setXMLNS(\"urn:ietf:params:xml:ns:xmpp-streams\");\n\t\t\t\t\t\t\tcommandEl.addChild(el);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresults.offer(commandClose);\n\t\t\t\t\t\tmsgs.add(\"Operation successful for user \" + res.getjid());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\terrors.add(\"You do not have enough permissions to end sessions for accounts for domain \" +\n\t\t\t\t\t\t\t   bareJID.getDomain() + \".\");\n\t\t}\n\n\t} catch (Exception ex) {\n\t\tex.printStackTrace();\n\t}\n}\n\nif (!msgs.isEmpty()) {\n\tCommand.addFieldMultiValue(result, \"Notes\", msgs)\n};\n\nif (!errors.isEmpty()) {\n\tCommand.addFieldMultiValue(result, \"Errors\", errors)\n};\nreturn results\n\n\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/ForceStopServiceForKey.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n\nForce stop IOService for a given key.\n\nAS:Description: Force stop service\nAS:CommandId: force-stop-service\nAS:Component: cl-comp\n*/\n\npackage tigase.admin\n\nimport tigase.net.IOService\nimport tigase.server.Command\nimport tigase.server.Packet\n\ndef p = (Packet) packet\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\nif (!isServiceAdmin) {\n\tdef result = p.commandResult(Command.DataType.result);\n\tCommand.addTextField(result, \"Error\", \"You are not service administrator\");\n\treturn result\n}\n\ndef KEY = \"key\"\n\ndef key = Command.getFieldValue(packet, KEY)\n\nif (key == null) {\n\tdef result = p.commandResult(Command.DataType.form);\n\tCommand.addTitle(result, \"Force-stopping IOService for a given key\")\n\tCommand.addInstructions(result, \"Provide a key for IOService you wish to stop.\")\n\tCommand.addFieldValue(result, KEY, key ?: \"\", \"text-single\", \"Key\")\n\n\treturn result\n}\n\nMap services = (Map) servicesMap\n\nIOService serv = services.get(key)\n\nif (serv == null) {\n\treturn \"IOService for key: ${key} not found!\"\n} else {\n\tserv.forceStop()\n\treturn \"Stopped IOService for key: ${key}.\"\n}\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/GetAnyFile.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n\nGet any file\n\nAS:Description: Get any file\nAS:CommandId: get-any-file\nAS:Component: message-router\n*/\n\npackage tigase.admin\n\nimport tigase.server.Command\nimport tigase.server.Packet\n\ndef p = (Packet) packet\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\ndef FILE_PATH = \"file\";\n\ndef filepath = Command.getFieldValue(p, FILE_PATH);\n\ndef result = p.commandResult(filepath ? Command.DataType.result : Command.DataType.form);\n\nif (!isServiceAdmin) {\n\tCommand.addTextField(result, \"Error\", \"You are not service administrator\");\n} else if (filepath == null) {\n\tCommand.addFieldValue(result, FILE_PATH, \"\", \"text-single\", \"File\");\n} else {\n\tif (filepath == null) {\n\t\tCommand.addTextField(result, \"Error\", \"File not specified\");\n\t} else {\n\t\tdef file = new File(filepath);\n\t\tif (file.exists()) {\n\t\t\tdef lines = [ ];\n\t\t\tfile.eachLine { line -> lines += line; };\n\t\t\tCommand.addFieldMultiValue(result, \"Content\", lines);\n\t\t} else {\n\t\t\tCommand.addTextField(result, \"Error\", \"File not found\");\n\t\t}\n\t}\n}\n\nreturn result;\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/GetConfigFile.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n\nGet configuration file\n\nAS:Description: Get configuration file\nAS:CommandId: get-config-file\nAS:Component: message-router\nAS:Group: Configuration\n*/\n\npackage tigase.admin\n\nimport tigase.component.DSLBeanConfigurator\nimport tigase.kernel.core.Kernel\nimport tigase.server.Command\nimport tigase.server.Packet\n\ndef p = (Packet) packet\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\ndef CFGFILE_TYPE = \"config-file\";\ndef CFGFILE_OPTIONS = [ \"config.tdsl\", \"tigase.conf\" ];\n\ndef cfgfile = Command.getFieldValue(p, CFGFILE_TYPE);\n\ndef result = p.commandResult(cfgfile ? Command.DataType.result : Command.DataType.form);\n\nif (!isServiceAdmin) {\n\tCommand.addTextField(result, \"Error\", \"You are not service administrator\");\n} else if (cfgfile == null) {\n\tdef filesArray = CFGFILE_OPTIONS.toArray(new String[CFGFILE_OPTIONS.size()]);\n\tCommand.addFieldValue(result, CFGFILE_TYPE, \"config.tdsl\", \"File\", filesArray, filesArray);\n} else {\n\tdef filepath = [ ]\n\tswitch (cfgfile) {\n\t\tcase \"config.tdsl\":\n\t\t\tfilepath = [ ((Kernel) kernel).getInstance(DSLBeanConfigurator).\n\t\t\t\t\t\t\t\t getConfigHolder().\n\t\t\t\t\t\t\t\t getConfigFilePath().\n\t\t\t\t\t\t\t\t toString() ];\n\t\t\tbreak;\n\n\t\tcase \"tigase.conf\":\n\t\t\tdef filenames = [ \"/etc/default/tigase\", \"/etc/tigase/tigase.conf\", \"etc/tigase.conf\" ];\n\t\t\tfilenames.each { it ->\n\t\t\t\tdef file = new File(it);\n\t\t\t\tif (filepath.size() == 0 && file.exists()) {\n\t\t\t\t\tfilepath.add(it);\n\t\t\t\t}\n\t\t\t};\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tbreak;\n\t}\n\n\tif (filepath == null) {\n\t\tCommand.addTextField(result, \"Error\", \"Config file not specified\");\n\t} else {\n\t\tfilepath.each { it ->\n\t\t\tdef file = new File(it);\n\t\t\tdef lines = [ ];\n\t\t\tfile.eachLine { line -> lines += line; };\n\t\t\tCommand.addFieldMultiValue(result, \"Content\", lines);\n\t\t}\n\t}\n}\n\nreturn result;\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/GetConfigTDSLFromMemory.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n\nGet config.tdsl configuration from memory.\n\nAS:Description: Get config.tdsl configuration\nAS:CommandId: get-config-tdsl\nAS:Component: message-router\nAS:Group: Configuration\n*/\n\npackage tigase.admin\n\nimport tigase.component.DSLBeanConfigurator\nimport tigase.kernel.core.Kernel\nimport tigase.server.Command\nimport tigase.server.Iq\n\nKernel kernel = (Kernel) kernel;\nIq p = (Iq) packet\nSet<String> admins = (Set<String>) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\ndef result = p.commandResult(Command.DataType.result);\n\nif (!isServiceAdmin) {\n\tCommand.addTextField(result, \"Error\", \"You are not service administrator\");\n} else {\n\tDSLBeanConfigurator configurator = kernel.getInstance(DSLBeanConfigurator.class);\n\tStringWriter writer = new StringWriter();\n\tconfigurator.dumpConfiguration(writer);\n\t\n\tCommand.addFieldMultiValue(result, \"config.tdsl\", Arrays.asList(writer.toString().split(\"\\n\")));\n}\n\nreturn result;\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/GetListOfActiveUsers.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n Get list of online users script as described in XEP-0133:\n http://xmpp.org/extensions/xep-0133.html#get-active-users-list\n\n AS:Description: Get list of active users\n AS:CommandId: http://jabber.org/protocol/admin#get-active-users\n AS:Component: sess-man\n AS:Group: Statistics\n */\npackage tigase.admin\n\nimport tigase.db.AuthRepository\nimport tigase.db.TigaseDBException\nimport tigase.db.UserRepository\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.vhosts.VHostItem\nimport tigase.vhosts.VHostManagerIfc\nimport tigase.xmpp.jid.BareJID\n\ndef SECOND = 1000;\ndef MINUTE = 60 * SECOND;\ndef JID = \"domainjid\"\ndef MAX_ITEMS = \"max_items\"\ndef TIME_BEFORE_IDLE = 5 * MINUTE;\n\ndef p = (Packet) packet\ndef auth_repo = (AuthRepository) authRepository\ndef user_repo = (UserRepository) userRepository\ndef users_sessions = (Map) userSessions\ndef vhost_man = (VHostManagerIfc) vhostMan\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\ndef domainJid = Command.getFieldValue(packet, JID);\ndef maxItemsStr = Command.getFieldValue(packet, MAX_ITEMS);\nif (domainJid == null || maxItemsStr == null) {\n\tdef result = p.commandResult(Command.DataType.form);\n\tCommand.addTitle(result, \"Requesting List of Active Users\")\n\tCommand.addInstructions(result, \"Fill out this form to request the active users\\nof this service.\")\n\tCommand.addFieldValue(result, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\",\n\t\t\t\t\t\t  \"hidden\")\n//\tif (isServiceAdmin) {\n\t//Command.addFieldValue(result, JID, domainJid ?: \"\", \"jid-single\",\n\t//\t\t\"The domain for the list of active users\")\n//\t}\n//\telse {\n\tdef vhosts = [ ];\n\tvhost_man.repo.allItems().each {\n\t\tif (isAllowedForDomain.apply(it.getVhost().toString())) {\n\t\t\tvhosts += it.getVhost().toString()\n\t\t}\n\t}\n\tdef vhostsArr = vhosts.toArray(new String[vhosts.size()]);\n\tCommand.addFieldValue(result, JID, \"\", \"The domain for the list of active users\", vhostsArr, vhostsArr);\n//\t}\n\tCommand.addFieldValue(result, MAX_ITEMS, maxItemsStr ?: \"\", \"Maximum number of items to show\",\n\t\t\t\t\t\t  [ \"25\", \"50\", \"75\", \"100\", \"150\", \"200\", \"None\" ].toArray(new String[7]),\n\t\t\t\t\t\t  [ \"25\", \"50\", \"75\", \"100\", \"150\", \"200\", \"None\" ].toArray(new String[7]));\n\treturn result\n}\ndef result = p.commandResult(Command.DataType.result)\ntry {\n\tdef maxItems = maxItemsStr ? (maxItemsStr == \"None\" ? null : Integer.parseInt(maxItemsStr)) : 25;\n\tdef bareJID = BareJID.bareJIDInstance(domainJid)\n\tVHostItem vhost = vhost_man.getVHostItem(bareJID.getDomain())\n\tif (isAllowedForDomain.apply(bareJID.getDomain())) {\n\t\tdef users_list = [ ];\n\t\tusers_sessions.entrySet().each {\n\t\t\tif (!it.getKey().toString().startsWith(\"sess-man\") && it.getKey().getDomain().equals(bareJID.getDomain())) {\n\t\t\t\tif (!maxItems || users_list.size() < maxItems) {\n\t\t\t\t\tdef user = it.getKey().toString();\n\t\t\t\t\tdef session = it.getValue();\n\t\t\t\t\tdef active = false;\n\t\t\t\t\tsession.getActiveResources().each {\n\t\t\t\t\t\tactive = active || ((System.currentTimeMillis() - it.getLastAccessed()) < TIME_BEFORE_IDLE);\n\t\t\t\t\t}\n\t\t\t\t\tif (active == true) {\n//\t\t\t\t\t\tuser += \" (\" + session.getActiveResourcesSize() + \":\";\n//\t\t\t\t\t\tsession.getJIDs().each { user += it.getResource() + \", \"; }\n//\t\t\t\t\t\tuser = user[0..user.size() - 3] + \")\";\n\t\t\t\t\t\tusers_list += user;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tCommand.addFieldMultiValue(result, \"Users: \" + users_list.size(), users_list);\n\t} else {\n\t\tCommand.addTextField(result, \"Error\",\n\t\t\t\t\t\t\t \"You do not have enough permissions to list active accounts for this domain.\");\n\t}\n} catch (TigaseDBException ex) {\n\tCommand.addTextField(result, \"Note\", \"Problem accessing database, active users not listed.\");\n}\nreturn result\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/GetListOfClusterNodes.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n Get list of all connected cluster nodes\n\n AS:Description: Get list of all connected cluster nodes\n AS:CommandId: cluster-nodes-list\n AS:Component: cl-comp\n AS:Group: Configuration\n */\npackage tigase.admin\n\nimport tigase.server.Command\nimport tigase.server.Packet\n\ndef p = (Packet) packet\ndef result = p.commandResult(Command.DataType.result)\ntry {\n\tdef nodes = component.getNodesConnectedWithLocal().collect { return it.getDomain() }\n\tCommand.addFieldMultiValue(result, \"Cluster nodes:\", nodes);\n} catch (Exception ex) {\n\tCommand.addTextField(result, \"Note\",\n\t\t\t\t\t\t \"Problem with retrieving list of all connected cluster nodes: \" + ex.getMessage());\n}\nreturn result\n\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/GetListOfIdleUsers.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n Get list of online users script as described in XEP-0133:\n http://xmpp.org/extensions/xep-0133.html#get-idle-users-list\n\n AS:Description: Get list of idle users\n AS:CommandId: http://jabber.org/protocol/admin#get-idle-users\n AS:Component: sess-man\n AS:Group: Statistics\n */\npackage tigase.admin\n\nimport tigase.db.AuthRepository\nimport tigase.db.TigaseDBException\nimport tigase.db.UserRepository\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.vhosts.VHostManagerIfc\nimport tigase.xmpp.jid.BareJID\n\ndef SECOND = 1000;\ndef MINUTE = 60 * SECOND;\ndef JID = \"domainjid\"\ndef MAX_ITEMS = \"max_items\"\ndef TIME_BEFORE_IDLE = 5 * MINUTE;\n\ndef p = (Packet) packet\ndef auth_repo = (AuthRepository) authRepository\ndef user_repo = (UserRepository) userRepository\ndef users_sessions = (Map) userSessions\ndef vhost_man = (VHostManagerIfc) vhostMan\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\ndef domainJid = Command.getFieldValue(packet, JID);\ndef maxItemsStr = Command.getFieldValue(packet, MAX_ITEMS);\nif (domainJid == null || maxItemsStr == null) {\n\tdef result = p.commandResult(Command.DataType.form);\n\tCommand.addTitle(result, \"Requesting List of Idle Users\")\n\tCommand.addInstructions(result, \"Fill out this form to request the idle users\\nof this service.\")\n\tCommand.addFieldValue(result, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\",\n\t\t\t\t\t\t  \"hidden\")\n//\tif (isServiceAdmin) {\n\t//Command.addFieldValue(result, JID, domainJid ?: \"\", \"jid-single\",\n\t//\t\t\"The domain for the list of active users\")\n//\t}\n//\telse {\n\tdef vhosts = [ ];\n\tvhost_man.repo.allItems().each {\n\t\tif (isAllowedForDomain.apply(it.getVhost().toString())) {\n\t\t\tvhosts += it.getVhost().toString()\n\t\t}\n\t}\n\tdef vhostsArr = vhosts.toArray(new String[vhosts.size()]);\n\tCommand.addFieldValue(result, JID, \"\", \"The domain for the list of idle users\", vhostsArr, vhostsArr);\n//\t}\n\tCommand.addFieldValue(result, MAX_ITEMS, maxItemsStr ?: \"\", \"Maximum number of items to show\",\n\t\t\t\t\t\t  [ \"25\", \"50\", \"75\", \"100\", \"150\", \"200\", \"None\" ].toArray(new String[7]),\n\t\t\t\t\t\t  [ \"25\", \"50\", \"75\", \"100\", \"150\", \"200\", \"None\" ].toArray(new String[7]));\n\treturn result\n}\ndef result = p.commandResult(Command.DataType.result)\ntry {\n\tdef maxItems = maxItemsStr ? (maxItemsStr == \"None\" ? null : Integer.parseInt(maxItemsStr)) : 25;\n\tdef bareJID = BareJID.bareJIDInstance(domainJid)\n\tif (isAllowedForDomain.apply(bareJID.getDomain())) {\n\t\tdef users_list = [ ];\n\t\tusers_sessions.entrySet().each {\n\t\t\tif (!it.getKey().toString().startsWith(\"sess-man\") && it.getKey().getDomain().equals(bareJID.getDomain())) {\n\t\t\t\tif (!maxItems || users_list.size() < maxItems) {\n\t\t\t\t\tdef user = it.getKey().toString();\n\t\t\t\t\tdef session = it.getValue();\n\t\t\t\t\tdef active = false;\n\t\t\t\t\tsession.getActiveResources().each {\n\t\t\t\t\t\tactive = active || ((System.currentTimeMillis() - it.getLastAccessed()) < TIME_BEFORE_IDLE);\n\t\t\t\t\t}\n\t\t\t\t\tif (active == false) {\n//\t\t\t\t\t\tuser += \" (\" + session.getActiveResourcesSize() + \":\";\n//\t\t\t\t\t\tsession.getJIDs().each { user += it.getResource() + \", \"; }\n//\t\t\t\t\t\tuser = user[0..user.size() - 3] + \")\";\n\t\t\t\t\t\tusers_list += user;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tCommand.addFieldMultiValue(result, \"Users: \" + users_list.size(), users_list);\n\t} else {\n\t\tCommand.addTextField(result, \"Error\",\n\t\t\t\t\t\t\t \"You do not have enough permissions to list idle accounts for this domain.\");\n\t}\n} catch (TigaseDBException ex) {\n\tCommand.addTextField(result, \"Note\", \"Problem accessing database, idle users not listed.\");\n}\nreturn result\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/GetListOfOnlineUsers.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n Get list of online users script as described in XEP-0133:\n http://xmpp.org/extensions/xep-0133.html#get-online-users-list\n\n AS:Description: Get list of online users\n AS:CommandId: http://jabber.org/protocol/admin#get-online-users-list\n AS:Component: sess-man\n AS:Group: Statistics\n */\n\npackage tigase.admin\n\nimport tigase.db.AuthRepository\nimport tigase.db.TigaseDBException\nimport tigase.db.UserRepository\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.vhosts.VHostManagerIfc\nimport tigase.xmpp.jid.BareJID\n\ndef JID = \"domainjid\"\ndef MAX_ITEMS = \"max_items\"\n\ndef p = (Packet) packet\ndef auth_repo = (AuthRepository) authRepository\ndef user_repo = (UserRepository) userRepository\ndef users_sessions = (Map) userSessions\ndef vhost_man = (VHostManagerIfc) vhostMan\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\ndef domainJid = Command.getFieldValue(packet, JID);\ndef maxItemsStr = Command.getFieldValue(packet, MAX_ITEMS);\n\nif (domainJid == null || maxItemsStr == null) {\n\tdef result = p.commandResult(Command.DataType.form);\n\n\tCommand.addTitle(result, \"Requesting List of Online Users\")\n\tCommand.addInstructions(result, \"Fill out this form to request the online users\\nof this service.\")\n\n\tCommand.addFieldValue(result, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\",\n\t\t\t\t\t\t  \"hidden\")\n\n//\tif (isServiceAdmin) {\n\t//Command.addFieldValue(result, JID, domainJid ?: \"\", \"jid-single\",\n\t//\t\t\"The domain for the list of online users\")\n//\t}\n//\telse {\n\tdef vhosts = [ ];\n\tvhost_man.repo.allItems().each {\n\t\tif (isAllowedForDomain.apply(it.getVhost().toString())) {\n\t\t\tvhosts += it.getVhost().toString()\n\t\t}\n\t}\n\tvhosts = vhosts.sort();\n\tdef vhostsArr = vhosts.toArray(new String[vhosts.size()]);\n\tCommand.addFieldValue(result, JID, \"\", \"The domain for the list of online users\", vhostsArr, vhostsArr);\n//\t}\n\n\tCommand.addFieldValue(result, MAX_ITEMS, maxItemsStr ?: \"\", \"Maximum number of items to show\",\n\t\t\t\t\t\t  [ \"25\", \"50\", \"75\", \"100\", \"150\", \"200\", \"None\" ].toArray(new String[7]),\n\t\t\t\t\t\t  [ \"25\", \"50\", \"75\", \"100\", \"150\", \"200\", \"None\" ].toArray(new String[7]));\n\n\treturn result\n}\n\ndef result = p.commandResult(Command.DataType.result)\ntry {\n\tdef maxItems = maxItemsStr ? (maxItemsStr == \"None\" ? null : Integer.parseInt(maxItemsStr)) : 25;\n\n\tdef bareJID = BareJID.bareJIDInstance(domainJid)\n\tif (isAllowedForDomain.apply(bareJID.getDomain())) {\n\t\tdef users_list = [ ];\n\t\tusers_sessions.entrySet().each {\n\t\t\tif (!it.getKey().toString().startsWith(\"sess-man\") && it.getKey().getDomain().equals(bareJID.getDomain())) {\n\n\t\t\t\tif (!maxItems || users_list.size() < maxItems) {\n\t\t\t\t\tdef user = it.getKey().toString();\n\t\t\t\t\tdef session = it.getValue();\n\t\t\t\t\tuser += \" (\" + session.getActiveResourcesSize() + \":\";\n\t\t\t\t\tsession.getJIDs().each { user += it.getResource() + \", \"; }\n\t\t\t\t\tuser = user[0..user.size() - 3] + \")\";\n\n\t\t\t\t\tusers_list += user;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tCommand.addFieldMultiValue(result, \"Users: \" + users_list.size(), users_list);\n\t} else {\n\t\tCommand.addTextField(result, \"Error\",\n\t\t\t\t\t\t\t \"You do not have enough permissions to list online accounts for this domain.\");\n\t}\n} catch (TigaseDBException ex) {\n\tCommand.addTextField(result, \"Note\", \"Problem accessing database, online users not listed.\");\n}\n\nreturn result\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/GetNumberOfActiveUsers.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n Get list of online users script as described in XEP-0133:\n http://xmpp.org/extensions/xep-0133.html#get-active-users-num\n\n AS:Description: Get number of active users\n AS:CommandId: http://jabber.org/protocol/admin#get-active-users-num\n AS:Component: sess-man\n AS:Group: Statistics\n */\npackage tigase.admin\n\nimport tigase.db.AuthRepository\nimport tigase.db.TigaseDBException\nimport tigase.db.UserRepository\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.vhosts.VHostManagerIfc\nimport tigase.xmpp.jid.BareJID\n\ndef SECOND = 1000;\ndef MINUTE = 60 * SECOND;\ndef JID = \"domainjid\"\ndef TIME_BEFORE_IDLE = 5 * MINUTE;\n\ndef p = (Packet) packet\ndef auth_repo = (AuthRepository) authRepository\ndef user_repo = (UserRepository) userRepository\ndef users_sessions = (Map) userSessions\ndef vhost_man = (VHostManagerIfc) vhostMan\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\ndef domainJid = Command.getFieldValue(packet, JID);\nif (domainJid == null) {\n\tdef result = p.commandResult(Command.DataType.form);\n\tCommand.addTitle(result, \"Requesting Number of Active Users\")\n\tCommand.addInstructions(result, \"Fill out this form to request the number of active users\\nof this service.\")\n\tCommand.addFieldValue(result, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\",\n\t\t\t\t\t\t  \"hidden\")\n//\tif (isServiceAdmin) {\n\t//Command.addFieldValue(result, JID, domainJid ?: \"\", \"jid-single\",\n\t//\t\t\"The domain for the list of active users\")\n//\t}\n//\telse {\n\tdef vhosts = [ ];\n\tvhost_man.repo.allItems().each {\n\t\tif (isAllowedForDomain.apply(it.getVhost().toString())) {\n\t\t\tvhosts += it.getVhost().toString()\n\t\t}\n\t}\n\tdef vhostsArr = vhosts.toArray(new String[vhosts.size()]);\n\tCommand.addFieldValue(result, JID, \"\", \"The domain for the number of active users\", vhostsArr, vhostsArr);\n//\t}\n\treturn result\n}\ndef result = p.commandResult(Command.DataType.result)\ntry {\n\tdef bareJID = BareJID.bareJIDInstance(domainJid)\n\tif (isAllowedForDomain.apply(bareJID.getDomain())) {\n\t\tdef users_list = [ ];\n\t\tusers_sessions.entrySet().each {\n\t\t\tif (!it.getKey().toString().startsWith(\"sess-man\") && it.getKey().getDomain().equals(bareJID.getDomain())) {\n\t\t\t\t//if (!maxItems || users_list.size() < maxItems) {\n\t\t\t\tdef user = it.getKey().toString();\n\t\t\t\tdef session = it.getValue();\n\t\t\t\tdef active = false;\n\t\t\t\tsession.getActiveResources().each {\n\t\t\t\t\tactive = active || ((System.currentTimeMillis() - it.getLastAccessed()) < TIME_BEFORE_IDLE);\n\t\t\t\t}\n\t\t\t\tif (active == true) {\n//\t\t\t\t\t\tuser += \" (\" + session.getActiveResourcesSize() + \":\";\n//\t\t\t\t\t\tsession.getJIDs().each { user += it.getResource() + \", \"; }\n//\t\t\t\t\t\tuser = user[0..user.size() - 3] + \")\";\n\t\t\t\t\tusers_list += user;\n\t\t\t\t}\n\t\t\t\t//}\n\t\t\t}\n\t\t}\n\t\tCommand.addFieldValue(result, \"activeusersnum\", String.valueOf(users_list.size()), \"fixed\",\n\t\t\t\t\t\t\t  \"The number of active users\");\n\t} else {\n\t\tCommand.addTextField(result, \"Error\",\n\t\t\t\t\t\t\t \"You do not have enough permissions to get number of active accounts for this domain.\");\n\t}\n} catch (TigaseDBException ex) {\n\tCommand.addTextField(result, \"Note\", \"Problem accessing database, active users not counted.\");\n}\nreturn result\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/GetNumberOfIdleUsers.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n Get list of online users script as described in XEP-0133:\n http://xmpp.org/extensions/xep-0133.html#get-idle-users-num\n\n AS:Description: Get number of idle users\n AS:CommandId: http://jabber.org/protocol/admin#get-idle-users-num\n AS:Component: sess-man\n AS:Group: Statistics\n */\npackage tigase.admin\n\nimport tigase.db.AuthRepository\nimport tigase.db.TigaseDBException\nimport tigase.db.UserRepository\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.vhosts.VHostManagerIfc\nimport tigase.xmpp.jid.BareJID\n\ndef SECOND = 1000;\ndef MINUTE = 60 * SECOND;\ndef JID = \"domainjid\"\ndef TIME_BEFORE_IDLE = 5 * MINUTE;\n\ndef p = (Packet) packet\ndef auth_repo = (AuthRepository) authRepository\ndef user_repo = (UserRepository) userRepository\ndef users_sessions = (Map) userSessions\ndef vhost_man = (VHostManagerIfc) vhostMan\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\ndef domainJid = Command.getFieldValue(packet, JID);\nif (domainJid == null) {\n\tdef result = p.commandResult(Command.DataType.form);\n\tCommand.addTitle(result, \"Requesting Number of Idle Users\")\n\tCommand.addInstructions(result, \"Fill out this form to request the number of idle users\\nof this service.\")\n\tCommand.addFieldValue(result, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\",\n\t\t\t\t\t\t  \"hidden\")\n//\tif (isServiceAdmin) {\n\t//Command.addFieldValue(result, JID, domainJid ?: \"\", \"jid-single\",\n\t//\t\t\"The domain for the list of active users\")\n//\t}\n//\telse {\n\tdef vhosts = [ ];\n\tvhost_man.repo.allItems().each {\n\t\tif (isAllowedForDomain.apply(it.getVhost().toString())) {\n\t\t\tvhosts += it.getVhost().toString()\n\t\t}\n\t}\n\tdef vhostsArr = vhosts.toArray(new String[vhosts.size()]);\n\tCommand.addFieldValue(result, JID, \"\", \"The domain for the number of idle users\", vhostsArr, vhostsArr);\n//\t}\n\treturn result\n}\ndef result = p.commandResult(Command.DataType.result)\ntry {\n\tdef bareJID = BareJID.bareJIDInstance(domainJid)\n\tif (isAllowedForDomain.apply(bareJID.getDomain())) {\n\t\tdef users_list = [ ];\n\t\tusers_sessions.entrySet().each {\n\t\t\tif (!it.getKey().toString().startsWith(\"sess-man\") && it.getKey().getDomain().equals(bareJID.getDomain())) {\n\t\t\t\t//if (!maxItems || users_list.size() < maxItems) {\n\t\t\t\tdef user = it.getKey().toString();\n\t\t\t\tdef session = it.getValue();\n\t\t\t\tdef active = false;\n\t\t\t\tsession.getActiveResources().each {\n\t\t\t\t\tactive = active || ((System.currentTimeMillis() - it.getLastAccessed()) < TIME_BEFORE_IDLE);\n\t\t\t\t}\n\t\t\t\tif (active == false) {\n//\t\t\t\t\t\tuser += \" (\" + session.getActiveResourcesSize() + \":\";\n//\t\t\t\t\t\tsession.getJIDs().each { user += it.getResource() + \", \"; }\n//\t\t\t\t\t\tuser = user[0..user.size() - 3] + \")\";\n\t\t\t\t\tusers_list += user;\n\t\t\t\t}\n\t\t\t\t//}\n\t\t\t}\n\t\t}\n\t\tCommand.addFieldValue(result, \"idleusersnum\", String.valueOf(users_list.size()), \"fixed\",\n\t\t\t\t\t\t\t  \"The number of idle users\");\n\t} else {\n\t\tCommand.addTextField(result, \"Error\",\n\t\t\t\t\t\t\t \"You do not have enough permissions to get number of idle accounts for this domain.\");\n\t}\n} catch (TigaseDBException ex) {\n\tCommand.addTextField(result, \"Note\", \"Problem accessing database, idle users not counted.\");\n}\nreturn result\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/GetRegisteredUserList.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n Get registered user list script as described in XEP-0133:\n http://xmpp.org/extensions/xep-0133.html#get-registered-users-list\n\n AS:Description: Get registered user list\n AS:CommandId: http://jabber.org/protocol/admin#get-registered-users-list\n AS:Component: sess-man\n AS:Group: Users\n */\n\npackage tigase.admin\n\nimport tigase.db.AuthRepository\nimport tigase.db.TigaseDBException\nimport tigase.db.UserRepository\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.vhosts.VHostManagerIfc\nimport tigase.xmpp.jid.BareJID\n\ndef JID = \"domainjid\"\ndef MAX_ITEMS = \"max_items\"\n\ndef p = (Packet) packet\ndef auth_repo = (AuthRepository) authRepository\ndef user_repo = (UserRepository) userRepository\ndef vhost_man = (VHostManagerIfc) vhostMan\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\ndef domainJid = Command.getFieldValue(packet, JID);\ndef maxItemsStr = Command.getFieldValue(packet, MAX_ITEMS);\n\nif (domainJid == null || maxItemsStr == null) {\n\tdef result = p.commandResult(Command.DataType.form);\n\n\tCommand.addTitle(result, \"Requesting List of Registered Users\")\n\tCommand.addInstructions(result, \"Fill out this form to request the registered users \\n of this service.\")\n\n\tCommand.addFieldValue(result, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\",\n\t\t\t\t\t\t  \"hidden\")\n//\tif (isServiceAdmin) {\n\t//Command.addFieldValue(result, JID, domainJid ?: \"\", \"jid-single\",\n\t//\t\t\"The domain for the list of online users\")\n//\t}\n//\telse {\n\tdef vhosts = [ ];\n\tvhost_man.repo.allItems().each {\n\t\tif (isAllowedForDomain.apply(it.getVhost().toString())) {\n\t\t\tvhosts += it.getVhost().toString()\n\t\t}\n\t}\n\tvhosts = vhosts.sort();\n\tdef vhostsArr = vhosts.toArray(new String[vhosts.size()]);\n\tCommand.addFieldValue(result, JID, \"\", \"The domain for the list of registered users\", vhostsArr, vhostsArr);\n//\t}\n\n\tCommand.addFieldValue(result, MAX_ITEMS, maxItemsStr ?: \"\", \"Maximum number of items to show\",\n\t\t\t\t\t\t  [ \"25\", \"50\", \"75\", \"100\", \"150\", \"200\", \"None\" ].toArray(new String[7]),\n\t\t\t\t\t\t  [ \"25\", \"50\", \"75\", \"100\", \"150\", \"200\", \"None\" ].toArray(new String[7]));\n\n\treturn result\n}\n\ndef result = p.commandResult(Command.DataType.result)\ntry {\n\tdef maxItems = maxItemsStr ? (maxItemsStr == \"None\" ? null : Integer.parseInt(maxItemsStr)) : 25;\n\n\tdef bareJID = BareJID.bareJIDInstance(domainJid)\n\tif (isAllowedForDomain.apply(bareJID.getDomain())) {\n\t\tdef users_list = [ ];\n\t\tdef domain_user_repo = (user_repo instanceof tigase.db.UserRepositoryMDImpl) ? user_repo.getRepo(\n\t\t\t\tbareJID.getDomain()) : user_repo;\n\t\tdef users = domain_user_repo.getUsers();\n\t\tfor (user in users) {\n\t\t\tif (!user.getDomain().equals(bareJID.getDomain())) {\n\t\t\t\tcontinue\n\t\t\t};\n\n\t\t\tusers_list.add(user.toString());\n\t\t\tif (maxItems && users_list.size() > maxItems) {\n\t\t\t\tbreak\n\t\t\t};\n\t\t}\n\n\t\tCommand.addFieldMultiValue(result, \"Users: \" + users_list.size(), users_list);\n\t} else {\n\t\tCommand.addTextField(result, \"Error\", \"You do not have enough permissions to list accounts for this domain.\");\n\t}\n} catch (TigaseDBException ex) {\n\tCommand.addTextField(result, \"Note\", \"Problem accessing database, users not listed.\");\n}\n\nreturn result\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/GetTopActiveUsers.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n Retrieves from the server specified number of top active users\n AS:Description: Get top active users\n AS:CommandId: http://jabber.org/protocol/admin#get-top-active-users\n AS:Component: sess-man\n AS:Group: Statistics\n */\n\npackage tigase.admin\n\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.xmpp.XMPPSession\n\ndef TOP_NUM = \"top-num\"\n\ndef p = (Packet) packet\n\ndef topNum = Command.getFieldValue(packet, TOP_NUM)\n\nif (topNum == null) {\n\tdef result = p.commandResult(Command.DataType.form);\n\n\tCommand.addTitle(result, \"Get top active users\")\n\tCommand.addInstructions(result, \"Fill out this form to get top active users.\")\n\n\tCommand.addFieldValue(result, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\",\n\t\t\t\t\t\t  \"hidden\")\n\tCommand.addFieldValue(result, TOP_NUM, topNum ?: \"10\", \"text-single\",\n\t\t\t\t\t\t  \"Number of top active users to show\")\n\n\treturn result\n}\n\ndef user_sessions = (Map) userSessions\n\ndef mc = [ compare: { XMPPSession a, XMPPSession b ->\n\ta.getPacketsCounter() == b.getPacketsCounter() ? 0 : a.getPacketsCounter() > b.getPacketsCounter() ? -1 : 1\n} ] as Comparator\n\ndef sessions = [ ]\n// TODO: this is memory inefficient way to do it. We need to find a more memory friendly way\nuser_sessions.entrySet().each {\n\tif (!it.getKey().toString().startsWith(\"sess-man\")) {\n\t\tsessions += it.getValue()\n\t}\n}\n\nsessions.sort(mc)\ndef max = topNum.toInteger()\nif (max > sessions.size()) {\n\tmax = sessions.size()\n}\n\ndef usr_list = [ ]\n\nsessions[0..(max - 1)].each { XMPPSession it ->\n\tusr_list += it.getJIDs()[0].toString() + \" online \" + (it.getLiveTime() / 1000) + \" sec, \" +\n\t\t\tit.getPacketsCounter() + \" packets\"\n}\n\ndef result = p.commandResult(Command.DataType.result)\nCommand.addFieldMultiValue(result, \"Top active users \", usr_list);\nreturn result\n\n\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/GetUserInfo.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n Obtaining informations about user\n AS:Description: Get User Info\n AS:CommandId: get-user-info\n AS:Component: sess-man\n AS:Group: Users\n */\n\npackage tigase.admin\n\nimport tigase.cluster.strategy.ClusteringStrategyIfc\nimport tigase.db.UserRepository\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.vhosts.VHostManagerIfc\nimport tigase.xml.Element\nimport tigase.xmpp.XMPPResourceConnection\nimport tigase.xmpp.XMPPSession\nimport tigase.xmpp.jid.BareJID\n\ndef JID = \"accountjid\"\n\ndef p = (Packet) packet\ndef sessions = (Map<BareJID, XMPPSession>) userSessions\ndef vhost_man = (VHostManagerIfc) vhostMan\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\ndef user_repo = (UserRepository) userRepository\n\ndef userJid = Command.getFieldValue(packet, JID)\n\nif (userJid == null) {\n\tdef result = p.commandResult(Command.DataType.form);\n\n\tCommand.addTitle(result, \"Get User Info\")\n\tCommand.addInstructions(result, \"Fill out this form to gather informations about user.\")\n\n\tCommand.addFieldValue(result, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\", \"hidden\")\n\tCommand.addFieldValue(result, JID, userJid ?: \"\", \"jid-single\", \"The Jabber ID for statistics\")\n\tCommand.addCheckBoxField(result, \"Show connected resources in table\", true)\n\n\treturn result\n}\n\ndef bareJID = BareJID.bareJIDInstance(userJid)\ndef resourcesAsTable = Command.getCheckBoxFieldValue(p, \"Show connected resources in table\");\ndef result = p.commandResult(Command.DataType.result)\n\nif (isAllowedForDomain.apply(bareJID.getDomain())) {\n\n\tCommand.addTextField(result, \"JID\", \"JID: \" + userJid)\n\tdef userRes = [ ];\n\tif (binding.variables.containsKey(\"clusterStrategy\")) {\n\t\tdef cluster = (ClusteringStrategyIfc) clusterStrategy\n\t\tdef conns = cluster.getConnectionRecords(bareJID);\n\t\tif (cluster.containsJid(bareJID) && (conns != null)) {\n\t\t\tconns.each { rec -> userRes.add([ res: rec.getUserJid().getResource(), node: rec.getNode().getDomain() ]);\n\t\t\t}\n\t\t}\n\t}\n\n\tXMPPSession session = sessions.get(BareJID.bareJIDInstanceNS(userJid))\n\tif (session != null) {\n\t\tList<XMPPResourceConnection> conns = session.getActiveResources()\n\t\tconns.each { con -> userRes.add([ res: con.getResource(), node: con.getConnectionId()?.getDomain() ]);\n\t\t}\n\n\t}\n\tif (userRes.isEmpty()) {\n\t\tif (user_repo.userExists(bareJID)) {\n\t\t\tCommand.addTextField(result, \"Status\", \"Status: offline\")\n\t\t} else {\n\t\t\tCommand.addTextField(result, \"Error\", \"User doesn't exists\")\n\t\t\treturn result\n\t\t}\n\t} else {\n\t\tuserRes.sort { it.res };\n\t\tCommand.addTextField(result, \"Status\", \"Status: \" + (userRes.size() ? \"online\" : \"offline\"))\n\t\tCommand.addTextField(result, \"Active connections\", \"Active connections: \" + userRes.size())\n\t\tif (resourcesAsTable) {\n\t\t\tElement reported = new Element(\"reported\");\n\t\t\treported.addAttribute(\"label\", \"Connected resources\");\n\t\t\tdef cols = [ \"Resource\", \"Cluster node\" ];\n\t\t\tcols.each {\n\t\t\t\tElement el = new Element(\"field\");\n\t\t\t\tel.setAttribute(\"var\", it);\n\t\t\t\treported.addChild(el);\n\t\t\t}\n\t\t\tresult.getElement().getChild('command').getChild('x').addChild(reported);\n\t\t\tuserRes.each { con ->\n\t\t\t\tElement item = new Element(\"item\");\n\t\t\t\tElement res = new Element(\"field\");\n\t\t\t\tres.setAttribute(\"var\", \"Resource\");\n\t\t\t\tres.addChild(new Element(\"value\", con.res));\n\t\t\t\titem.addChild(res);\n\n\t\t\t\tElement node = new Element(\"field\");\n\t\t\t\tnode.setAttribute(\"var\", \"Cluster node\");\n\t\t\t\tnode.addChild(new Element(\"value\", con.node));\n\t\t\t\titem.addChild(node);\n\t\t\t\tresult.getElement().getChild('command').getChild('x').addChild(item);\n\t\t\t}\n\t\t} else {\n\t\t\tfor (def con : userRes) {\n\t\t\t\tCommand.addTextField(result, con.res + \" is connected to\", con.res + \" is connected to \" + con.node);\n\t\t\t}\n\t\t}\n\t}\n\n\tdef sessionManager = component;\n\tdef offlineMsgsRepo = sessionManager.processors.values().find { it.hasProperty(\"msg_repo\") }?.msg_repo;\n\tif (offlineMsgsRepo && offlineMsgsRepo.metaClass.respondsTo(offlineMsgsRepo, \"getMessagesCount\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t[ tigase.xmpp.jid.JID ] as Object[])) {\n\t\tdef offlineStats = 0;\n\t\ttry {\n\t\t\tofflineStats = offlineMsgsRepo.getMessagesCount(tigase.xmpp.jid.JID.jidInstance(bareJID));\n\t\t} catch (tigase.db.UserNotFoundException ex) {\n\t\t\t// ignoring this error for now as it is not important\n\t\t}\n\t\tdef msg = \"Offline messages: \" +\n\t\t\t\t(offlineStats ? (offlineStats[offlineStats.keySet().find { it.name() == \"message\" }] ?: 0) : 0);\n\t\tCommand.addTextField(result, msg, msg);\n\t}\n\n\tdef loginHistoryProcessor = sessionManager.outFilters[\"login-history\"];\n\tif (loginHistoryProcessor) {\n\t\tdef unifiedArchiveComp = tigase.server.XMPPServer.getComponent(\n\t\t\t\tloginHistoryProcessor.getComponentJid().getLocalpart())\n//sessionManager.parent.components_byId[loginHistoryProcessor.componentJid];\n\t\tif (unifiedArchiveComp) {\n\t\t\tdef ua_repo = unifiedArchiveComp.msg_repo;\n\t\t\tdef criteria = ua_repo.newCriteriaInstance();\n\t\t\tcriteria.setWith(bareJID.toString());\n\t\t\tcriteria.getRSM().hasBefore = true;\n\t\t\tcriteria.getRSM().max = 10;\n\t\t\tcriteria.itemType = \"login\";\n//\t\t\t\tdef logins = ua_repo.getItems(bareJID, criteria).reverse().collect { new java.util.Date(criteria.getStart().getTime() + \n//\t\t\t\t\t\t\t(Integer.parseInt(it.getAttribute(\"secs\"))*1000)).toString() + \" for resource '\" + it.getChildren().first().getCData() + \"'\" }.join(\"\\n\");\n\n\t\t\tElement reported = new Element(\"reported\");\n\t\t\treported.addAttribute(\"label\", \"Login times\");\n\t\t\tdef cols = [ \"Resource\", \"Date\" ];\n\t\t\tcols.each {\n\t\t\t\tElement el = new Element(\"field\");\n\t\t\t\tel.setAttribute(\"var\", it);\n\t\t\t\treported.addChild(el);\n\t\t\t}\n\t\t\tresult.getElement().getChild('command').getChild('x').addChild(reported);\n\n\t\t\tua_repo.getItems(bareJID, criteria).reverse().each {\n\t\t\t\tElement item = new Element(\"item\");\n\t\t\t\tElement res = new Element(\"field\");\n\t\t\t\tres.setAttribute(\"var\", \"Resource\");\n\t\t\t\tres.addChild(new Element(\"value\", it.getChildren().first().getCData()));\n\t\t\t\titem.addChild(res);\n\n\t\t\t\tString ts = new java.util.Date(\n\t\t\t\t\t\tcriteria.getStart().getTime() + (Integer.parseInt(it.getAttribute(\"secs\")) * 1000)).format(\n\t\t\t\t\t\t\"yyyy-MM-dd HH:mm:ss.S\");\n\t\t\t\tElement node = new Element(\"field\");\n\t\t\t\tnode.setAttribute(\"var\", \"Date\");\n\t\t\t\tnode.addChild(new Element(\"value\", ts));\n\t\t\t\titem.addChild(node);\n\t\t\t\tresult.getElement().getChild('command').getChild('x').addChild(item);\n\t\t\t}\n\t\t}\n\t}\n} else {\n\tCommand.addTextField(result, \"Error\",\n\t\t\t\t\t\t \"You do not have enough permissions to obtain statistics for user in this domain.\");\n}\n\nreturn result\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/GetUserRoster.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n\nObtaining User Statistics as described in in XEP-0133:\nhttp://xmpp.org/extensions/xep-0133.html#get-user-roster\n\nAS:Description: Get User Roster\nAS:CommandId: http://jabber.org/protocol/admin#get-user-roster\nAS:Component: sess-man\n*/\n\npackage tigase.admin\n\nimport tigase.db.UserRepository\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.vhosts.VHostManagerIfc\nimport tigase.xml.Element\nimport tigase.xmpp.XMPPSession\nimport tigase.xmpp.impl.roster.RosterAbstract\nimport tigase.xmpp.impl.roster.RosterElement\nimport tigase.xmpp.impl.roster.RosterFactory\nimport tigase.xmpp.impl.roster.RosterFlat\nimport tigase.xmpp.jid.BareJID\n\ndef JID = \"accountjid\"\ndef SHOW_AS_TABLE = \"Present roster in table (required for UI)\";\n\ndef p = (Packet) packet\ndef repository = (UserRepository) userRepository\ndef sessions = (Map<BareJID, XMPPSession>) userSessions\ndef vhost_man = (VHostManagerIfc) vhostMan\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\ndef userJid = Command.getFieldValue(packet, JID)\ndef showAsTable = Command.getCheckBoxFieldValue(packet, SHOW_AS_TABLE) ?: false;\n\nif (userJid == null) {\n\tdef result = p.commandResult(Command.DataType.form);\n\n\tCommand.addTitle(result, \"Getting a User's Roster\")\n\tCommand.addInstructions(result, \"Fill out this form to get a user's roster.\")\n\n\tCommand.addFieldValue(result, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\", \"hidden\")\n\tCommand.addFieldValue(result, JID, userJid ?: \"\", \"jid-single\", \"The Jabber ID for which to retrieve roster\")\n\tCommand.addCheckBoxField(result, SHOW_AS_TABLE, showAsTable);\n\n\treturn result\n}\n\ndef bareJID = BareJID.bareJIDInstance(userJid)\ndef result = p.commandResult(Command.DataType.result)\n\nif (isAllowedForDomain.apply(bareJID.getDomain())) {\n\n\tCommand.addFieldValue(result, JID, userJid ?: \"\", \"jid-single\", \"The Jabber ID for which to retrieve roster\")\n\n\tXMPPSession session = sessions.get(BareJID.bareJIDInstanceNS(userJid))\n\n\tif (!showAsTable) {\n\t\tElement query = new Element(\"query\");\n\t\tquery.setXMLNS(\"jabber:iq:roster\");\n\t\tif (session == null) {\n\t\t\tString rosterStr = repository.getData(bareJID, null,\n\t\t\t\t\t\t\t\t\t\t\t\t  RosterAbstract.ROSTER, null) ?: \"\"\n\t\t\tMap<BareJID, RosterElement> roster = new LinkedHashMap<BareJID, RosterElement>()\n\t\t\tRosterFlat.parseRosterUtil(rosterStr, roster, null)\n\t\t\troster.values().each {\n\t\t\t\tquery.addChild(it.getRosterItem());\n\t\t\t}\n\t\t} else {\n\t\t\tRosterAbstract rosterUtil = RosterFactory.getRosterImplementation(true)\n\t\t\tquery.addChildren(rosterUtil.getRosterItems(session.getActiveResources().get(0)));\n\t\t}\n\t\tif (query != null) {\n\t\t\tresult.getElement().getChild(\"command\").getChild(\"x\", \"jabber:x:data\").addChild(query);\n\t\t}\n\t} else {\n\t\tMap<BareJID, RosterElement> roster = new LinkedHashMap<BareJID, RosterElement>()\n\t\tif (session == null) {\n\t\t\tString rosterStr = repository.getData(bareJID, null,\n\t\t\t\t\t\t\t\t\t\t\t\t  RosterAbstract.ROSTER, null) ?: \"\"\n\t\t\tRosterFlat.parseRosterUtil(rosterStr, roster, null)\n\t\t} else {\n\t\t\tdef conn = session.getActiveResources().get(0)\n\t\t\tRosterAbstract rosterUtil = RosterFactory.getRosterImplementation(true)\n\t\t\trosterUtil.getBuddies(conn).\n\t\t\t\t\teach { buddyJid -> roster.put(buddyJid.getBareJID(), rosterUtil.getRosterElement(conn, buddyJid));\n\t\t\t\t\t}\n\t\t}\n\t\tif (roster.isEmpty()) {\n\t\t\tCommand.addTextField(result, \"Note\", \"Not found any roster entries for \" + bareJID);\n\t\t} else {\n\t\t\tCommand.addTextField(result, \"Note\", \"Found \" + roster.size() + \" entries in roster for \" + bareJID);\n\t\t\tElement reported = new Element(\"reported\");\n\t\t\treported.addAttribute(\"label\", \"Connected resources\");\n\t\t\tdef cols = [ \"JID\", \"Name\", \"Subscription\", \"Groups\" ];\n\t\t\tcols.each {\n\t\t\t\tElement el = new Element(\"field\");\n\t\t\t\tel.setAttribute(\"var\", it);\n\t\t\t\treported.addChild(el);\n\t\t\t}\n\t\t\tresult.getElement().getChild('command').getChild('x').addChild(reported);\n\t\t\troster.each { jid, rosterEntry ->\n\t\t\t\tElement item = new Element(\"item\");\n\t\t\t\tcols.each { col ->\n\t\t\t\t\tElement res = new Element(\"field\");\n\t\t\t\t\tres.setAttribute(\"var\", col);\n\t\t\t\t\tdef val = null;\n\t\t\t\t\tif (col == \"JID\") {\n\t\t\t\t\t\tval = rosterEntry.getJid().toString()\n\t\t\t\t\t} else if (col == \"Name\") {\n\t\t\t\t\t\tval = rosterEntry.\n\t\t\t\t\t\t\t\tgetName()\n\t\t\t\t\t} else if (col == \"Subscription\") {\n\t\t\t\t\t\tval = rosterEntry.getSubscription()?.name() ?: \"none\"\n\t\t\t\t\t} else if (col == \"Groups\") {\n\t\t\t\t\t\tval = rosterEntry.getGroups()?.join(\", \") ?: \"\"\n\t\t\t\t\t};\n\t\t\t\t\tres.addChild(new Element(\"value\", val));\n\t\t\t\t\titem.addChild(res);\n\t\t\t\t}\n\t\t\t\tresult.getElement().getChild('command').getChild('x').addChild(item);\n\t\t\t}\n\t\t}\n\t}\n} else {\n\tCommand.addTextField(result, \"Error\",\n\t\t\t\t\t\t \"You do not have enough permissions to retrieve roster for user in this domain.\");\n}\n\nreturn result\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/ListCommands.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n Get list of available adhoc commands with additional metadata.\n\n AS:Description: Get list available commands\n AS:CommandId: list-commands\n AS:Group: Example scripts\n AS:ComponentClass: tigase.server.BasicComponent\n */\npackage tigase.admin\n\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.xml.Element\n\ndef p = (Packet) packet\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\ndef result = p.commandResult(Command.DataType.form)\nCommand.addTitle(result, \"Retrieving list of commands\");\n\ndef type = Command.getFieldValue(p, \"type\");\nif (!type) {\n\tCommand.addInstructions(result, \"Select a informations to retrieve\")\n\tCommand.addFieldValue(result, \"type\", \"\", \"Informations to retrive\",\n\t\t\t\t\t\t  [ \"Groups\", \"Commands\" ].toArray(new String[2]),\n\t\t\t\t\t\t  [ \"groups\", \"commands\" ].toArray(new String[2]));\n\treturn result;\n} else if (type == \"groups\") {\n\tdef groups = [ ];\n\tCommand.addInstructions(result, \"Select a group for which to retrieve commands\");\n\tCommand.addFieldValue(result, \"type\", \"commands\", \"hidden\");\n\tadminCommands.each { id, script ->\n\t\tif (!component.canCallCommand(p.getStanzaFrom(), id)) {\n\t\t\treturn\n\t\t};\n\n\t\tdef group = script.getGroup();\n\t\tif (group == null) {\n\t\t\tgroup = \"--\";\n\t\t}\n\t\tif (!groups.contains(group)) {\n\t\t\tgroups.add(group)\n\t\t};\n\t}\n\tCommand.addFieldValue(result, \"group\", \"\", \"Group\", groups.toArray(new String[groups.size()]),\n\t\t\t\t\t\t  groups.toArray(new String[groups.size()]));\n\treturn result;\n} else if (type == \"commands\") {\n\tdef group = Command.getFieldValue(p, \"group\");\n\tCommand.addHiddenField(result, \"group\", group ?: \"\");\n\tCommand.addInstructions(result, \"Following commands are available\" + (group ? \" for group $group\" : \"\"));\n\tdef x = Command.getData(result).find { it.getName() == \"x\" && it.getXMLNS() };\n\tdef reported = new Element(\"reported\");\n\tdef fields = [ \"node\", \"group\", \"name\", \"jid\" ];\n\treported.addChildren(fields.collect {\n\t\tnew Element(\"field\", [ \"var\" ].toArray(new String[1]), [ it ].toArray(new String[1]));\n\t});\n\tx.addChild(reported);\n\tdef scripts = [ ];\n\tadminCommands.each { id, script ->\n\t\tif (!component.canCallCommand(p.getStanzaFrom(), id)) {\n\t\t\treturn\n\t\t};\n\n\t\tif (group && !group.equals(script.getGroup())) {\n\t\t\treturn\n\t\t};\n\n\t\tdef item = new Element(\"item\");\n\t\tfields.each {\n\t\t\tdef value = new Element(\"value\");\n\t\t\tdef field = new Element(\"field\");\n\t\t\tfield.setAttribute(\"var\", it);\n\t\t\tfield.addChild(value);\n\t\t\titem.addChild(field);\n\n\t\t\tif (it == \"node\") {\n\t\t\t\tvalue.setCData(id);\n\t\t\t} else if (it == \"group\") {\n\t\t\t\tvalue.setCData(script.getGroup());\n\t\t\t} else if (it == \"name\") {\n\t\t\t\tvalue.setCData(script.getDescription());\n\t\t\t} else if (it == \"jid\") {\n\t\t\t\tvalue.setCData(packet.getStanzaTo().toString());\n\t\t\t}\n\t\t}\n\t\tx.addChild(item);\n\n\t\tscripts.add([ id: id, name: script.getDescription() ]);\n\t}\n\n\tCommand.addFieldValue(result, \"commands\", \"\", \"Commands\",\n\t\t\t\t\t\t  scripts.collect { it.name }.toArray(new String[scripts.size()]),\n\t\t\t\t\t\t  scripts.collect { it.id }.toArray(new String[scripts.size()]));\n\treturn result;\n}"
  },
  {
    "path": "src/main/groovy/tigase/admin/ListServiceKeys.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n\nList all keys for active IOServices from the connection manager.\n\nAS:Description: List Service Keys\nAS:CommandId: service-keys\nAS:Component: cl-comp\n*/\n\npackage tigase.admin\n\nimport tigase.server.Command\nimport tigase.server.Packet\n\nMap services = (Map) servicesMap\n\ndef p = (Packet) packet\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\nif (!isServiceAdmin) {\n\tdef result = p.commandResult(Command.DataType.result);\n\tCommand.addTextField(result, \"Error\", \"You are not service administrator\");\n\treturn result\n}\n\nreturn services.keySet().toString()\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/LoadErrors.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\nLoad errors caught by the server during execution:\n\nAS:Description: Load errors\nAS:CommandId: load-errors\nAS:Component: monitor\n*/\n\npackage tigase.admin\n\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.util.log.LogFormatter\n\ndef p = (Packet) packet\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\nif (!isServiceAdmin) {\n\tdef result = p.commandResult(Command.DataType.result);\n\tCommand.addTextField(result, \"Error\", \"You are not service administrator\");\n\treturn result\n}\n\ndef result = [ ]\n\ndef size = LogFormatter.errors.size()\nif (size > 0) {\n\tLogFormatter.errors.each {\n\t\tdef entry = it.getValue()\n\t\tresult += \"\";\n\t\tresult += entry.getMessage() + \": \" + entry.getCounter()\n\t\tentry.getRecord().split(\"\\n\").each {\n\t\t\tresult += it\n\t\t}\n\t}\n} else {\n\tresult += \"No errors so far!\"\n}\n\ndef res = p.commandResult(Command.DataType.result)\nCommand.addFieldMultiValue(res, \"Errors: \" + size, result)\nreturn res\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/OAuthCredentials.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\nAdHoc Command for setting OAuth credentials for JabberIQRegister\n\nAS:Description: OAuth credentials\nAS:CommandId: oauth-credentials\nAS:Component: sess-man\n*/\n\npackage tigase.admin\n\nimport tigase.kernel.KernelException\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.xmpp.Authorization\nimport tigase.xmpp.impl.JabberIqRegister\n\ndef p = (Packet) packet\n\ndef oauthTokenKey = Command.getFieldValue(packet, \"oauthTokenKey\")\ndef oauthTokenSecret = Command.getFieldValue(packet, \"oauthTokenSecret\")\ndef signedFormRequired = Command.getFieldValue(packet, \"signedFormRequired\")\n\nif (signedFormRequired == null) {\n\tdef res = p.commandResult(Command.DataType.form)\n\tCommand.addTitle(res, \"OAuth Credentials\")\n\tCommand.addInstructions(res,\n\t\t\t\t\t\t\t\"It allows to set new OAuth credentials and enable or disable requirement of registration with Signed Form.\")\n\tCommand.addFieldValue(res, \"oauthTokenKey\", \"\", \"text-single\", \"OAuth Token Key\")\n\tCommand.addFieldValue(res, \"oauthTokenSecret\", \"\", \"text-single\", \"OAuth Token Secret\")\n\tCommand.addFieldValue(res, \"signedFormRequired\", Boolean.toString(JabberIqRegister.isSignedFormRequired()),\n\t\t\t\t\t\t  \"boolean\", \"Signed Form required to registration\")\n\treturn res\n} else {\n\ttry {\n\t\tJabberIqRegister jabberIqRegister = kernel.getInstance(JabberIqRegister.class);\n\t\tjabberIqRegister.setOAuthCredentials(oauthTokenKey, oauthTokenSecret)\n\t\tjabberIqRegister.setSignedFormRequired(signedFormRequired.equals(\"1\") || signedFormRequired.equals(\"true\"))\n\n\t\tdef res = p.commandResult(Command.DataType.result)\n\t\tCommand.addTitle(res, \"OAuth Credentials\")\n\t\tCommand.addInstructions(res, \"Credentials set.\")\n\t\treturn res\n\t} catch (KernelException ex) {\n\t\treturn Authorization.INTERNAL_SERVER_ERROR.getResponseMessage(p, \"JabberIqRegister processor is not loaded\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  false);\n\t}\n}\n\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/PluginManager.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n\nManage active server plugins\n\nAS:Description: Manage active server plugins\nAS:CommandId: plugin-manager\nAS:Component: basic-conf\nAS:Group: Configuration\n */\n\npackage tigase.admin\n\nimport tigase.conf.Configurable\nimport tigase.conf.Configurator\nimport tigase.osgi.ModulesManagerImpl\nimport tigase.server.Command\nimport tigase.server.Iq\nimport tigase.server.XMPPServer\n\nclass DelayedReloadTaskPlugMan\n\t\textends Thread {\n\n\tvoid run() {\n\t\tThread.sleep(5000);\n\t\t((Configurator) XMPPServer.getConfigurator()).updateMessageRouter();\n\t}\n}\n\ntry {\n\n\tdef SUBMIT = \"exec\";\n\n\tdef p = (Iq) packet\n\n// check permission\n\tdef admins = (Set) adminsSet\n\tdef stanzaFromBare = p.getStanzaFrom().getBareJID()\n\tdef isServiceAdmin = admins.contains(stanzaFromBare)\n\n\tif (!isServiceAdmin) {\n\t\tdef result = p.commandResult(Command.DataType.result)\n\t\tCommand.addTextField(result, \"Error\", \"You do not have enough permissions to access this data.\");\n\t\treturn result\n\t}\n\n//def submit = Command.getFieldValue(p, SUBMIT);\n\tdef submit = p.getElement().findChild([ \"iq\", \"command\", \"x\" ] as String[])?.getAttribute(\"type\");\n\n\tif (!submit) {\n\t\tdef res = (Iq) p.commandResult(Command.DataType.form)\n\n\t\tdef pluginsAll = [ ];\n\t\tif (XMPPServer.isOSGi()) {\n\t\t\tpluginsAll.addAll(ModulesManagerImpl.getInstance().plugins.keySet());\n\t\t} else {\n\t\t\tpluginsAll.addAll(tigase.xmpp.ProcessorFactory.processors.keySet());\n\t\t}\n\t\tdef conf = XMPPServer.getConfigurator();\n\t\tdef pluginsEnabled = [ ];\n\t\tpluginsEnabled.addAll(tigase.server.xmppsession.SessionManagerConfig.PLUGINS_FULL_PROP_VAL);\n\n\t\tdef pluginsStr = conf.getDefConfigParams().get(Configurable.GEN_SM_PLUGINS);\n\t\tif (pluginsStr) {\n\t\t\tpluginsStr.split(\",\").each { tmp ->\n\t\t\t\tdef id = tmp;\n\t\t\t\tswitch (tmp.charAt(0)) {\n\t\t\t\t\tcase '+':\n\t\t\t\t\t\tid = tmp.substring(1);\n\t\t\t\t\t\tif (!pluginsAll.contains(id)) {\n\t\t\t\t\t\t\tpluginsAll.add(id);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!pluginsEnabled.contains(id)) {\n\t\t\t\t\t\t\tpluginsEnabled.add(id);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '-':\n\t\t\t\t\t\tid = tmp.substring(1);\n\t\t\t\t\t\tif (!pluginsAll.contains(id)) {\n\t\t\t\t\t\t\tpluginsAll.add(id);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tpluginsEnabled.remove(id);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tpluginsEnabled.add(id);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tCommand.addHiddenField(res, SUBMIT, SUBMIT);\n\n\t\tpluginsAll.sort();\n\t\tpluginsAll.each { id -> Command.addCheckBoxField(res, id, pluginsEnabled.contains(id));\n\t\t}\n\n\t\treturn res;\n\t} else {\n\t\tdef pluginsEnabled = [ ];\n\t\tpluginsEnabled.addAll(tigase.server.xmppsession.SessionManagerConfig.PLUGINS_FULL_PROP_VAL);\n\n\t\tdef str = \"\";\n\n\t\tdef data = Command.getData(p, \"x\", \"jabber:x:data\");\n\t\tdata.getChildren().each { child ->\n\t\t\tif (child.getName() != 'field') {\n\t\t\t\treturn\n\t\t\t};\n\t\t\tif (child.getAttribute(\"value\") == SUBMIT) {\n\t\t\t\treturn\n\t\t\t};\n\t\t\tdef id = tigase.xml.XMLUtils.escape(child.getAttribute(\"var\"));\n\t\t\tdef enable = Command.getCheckBoxFieldValue(p, id);\n\t\t\tif (enable && pluginsEnabled.contains(id)) {\n\t\t\t\treturn\n\t\t\t};\n\t\t\tif (!enable && !pluginsEnabled.contains(id)) {\n\t\t\t\treturn\n\t\t\t};\n\t\t\tif (enable && !pluginsEnabled.contains(id)) {\n\t\t\t\tif (!str.isEmpty()) {\n\t\t\t\t\tstr += \",\"\n\t\t\t\t};\n\t\t\t\tstr += \"+\" + id;\n\t\t\t\tpluginsEnabled.add(id);\n\t\t\t} else if (!enable && pluginsEnabled.contains(id)) {\n\t\t\t\tif (!str.isEmpty()) {\n\t\t\t\t\tstr += \",\"\n\t\t\t\t};\n\t\t\t\tstr += \"-\" + id;\n\t\t\t\tpluginsEnabled.remove(id);\n\t\t\t}\n\t\t}\n\n\t\tdef conf = XMPPServer.getConfigurator();\n\t\tconf.getDefConfigParams().put(Configurable.GEN_SM_PLUGINS, str.isEmpty() ? null : str);\n\n\t\tdef props = [ : ];\n\t\tprops[tigase.server.xmppsession.SessionManagerConfig.PLUGINS_PROP_KEY] = (pluginsEnabled as String[]);\n\t\tconf.putProperties(\"sess-man\", props);\n\n\t\tnew DelayedReloadTaskPlugMan().start();\n\n\t\tdef res = (Iq) p.commandResult(Command.DataType.result)\n\n\t\tCommand.addTextField(res, \"Note\", \"Operation successful.\");\n\n\t\treturn res;\n\t}\n\n} catch (Exception ex) {\n\tex.printStackTrace();\n\tthrow ex;\n}"
  },
  {
    "path": "src/main/groovy/tigase/admin/ReEnableUser.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n User modify script\n\n AS:Description: Re-Enable User\n AS:CommandId: http://jabber.org/protocol/admin#reenable-user\n AS:Component: sess-man\n AS:Group: Users\n */\n\npackage tigase.admin\n\nimport tigase.db.AuthRepository\nimport tigase.db.UserRepository\nimport tigase.db.TigaseDBException\nimport tigase.db.UserNotFoundException\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.server.Permissions\nimport tigase.vhosts.VHostManagerIfc\nimport tigase.xmpp.StanzaType\nimport tigase.xmpp.jid.BareJID\n\ndef JIDS = \"accountjids\"\n\ndef p = (Packet) packet\ndef auth_repo = (AuthRepository) authRepository\ndef user_repo = (UserRepository) userRepository\ndef vhost_man = (VHostManagerIfc) vhostMan\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\ndef NOTIFY_CLUSTER = \"notify-cluster\"\nboolean clusterMode = Boolean.valueOf(System.getProperty(\"cluster-mode\", false.toString()))\ndef notifyClusterStr = Command.getFieldValue(packet, NOTIFY_CLUSTER)\nboolean notifyCluster = (notifyClusterStr != null) ? Boolean.valueOf(notifyClusterStr) : true\n\ndef user_sessions = (Map) userSessions\n\ndef userJids = Command.getFieldValues(packet, JIDS)\n\nif (userJids == null) {\n\tdef result = p.commandResult(Command.DataType.form)\n\n\tCommand.addTitle(result, \"Re-enabling a User\")\n\tCommand.addInstructions(result, \"Fill out this form to re-enable a user.\")\n\n\tCommand.addFieldValue(result, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\",\n\t\t\t\t\t\t  \"hidden\")\n\tCommand.addFieldValue(result, JIDS, userJids ?: \"\", \"jid-multi\",\n\t\t\t\t\t\t  \"The Jabber ID(s) to re-enable\")\n\tif (clusterMode) {\n\t\tCommand.addHiddenField(result, NOTIFY_CLUSTER, true.toString())\n\t}\n\n\treturn result\n}\n\ndef results = new LinkedList<Packet>()\n\ndef closeUserSessions = { userJid ->\n\ttry {\n\t\tdef bareJID = BareJID.bareJIDInstance(userJid)\n\t\tdef sess = user_sessions.get(bareJID)\n\t\tif (sess != null) {\n\t\t\tdef conns = sess.getConnectionIds()\n\t\t\tfor (conn in conns) {\n\t\t\t\tdef res = sess.getResourceForConnectionId(conn)\n\t\t\t\tif (res != null) {\n\t\t\t\t\tdef commandClose = Command.CLOSE.getPacket(p.getStanzaTo(), conn,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   StanzaType.set, res.nextStanzaId())\n\t\t\t\t\tresults.offer(commandClose)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} catch (Exception ex) {\n\t\tex.printStackTrace()\n\t}\n}\n\nif (clusterMode) {\n\tif (!notifyCluster) {\n\t\tfor (userJid in userJids) {\n\t\t\tcloseUserSessions(userJid)\n\t\t}\n\t\treturn results\n\t}\n}\n\nif (clusterMode && notifyCluster) {\n\tdef nodes = (List) clusterStrategy.getNodesConnected()\n\tif (nodes && nodes.size() > 0) {\n\t\tnodes.each { node ->\n\t\t\tdef forward = p.copyElementOnly()\n\t\t\tCommand.removeFieldValue(forward, NOTIFY_CLUSTER)\n\t\t\tCommand.addHiddenField(forward, NOTIFY_CLUSTER, false.toString())\n\t\t\tforward.setPacketTo(node)\n\t\t\tforward.setPermissions(Permissions.ADMIN)\n\n\t\t\tresults.offer(forward)\n\t\t}\n\t}\n}\n\ndef result = p.commandResult(Command.DataType.result)\ndef msgs = [ ]\ndef errors = [ ]\nfor (userJid in userJids) {\n\ttry {\n\t\tdef bareJID = BareJID.bareJIDInstance(userJid)\n\t\tif (isAllowedForDomain.apply(bareJID.getDomain())) {\n\t\t\tif (user_repo.userExists(bareJID)) {\n\t\t\t\ttry {\n\t\t\t\t\tauth_repo.setAccountStatus(bareJID, AuthRepository.AccountStatus.active)\n\t\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\t\terrors.add(\"Account \" + userJid + \" was not re-enabled: \" + ex.getMessage())\n\t\t\t\t}\n\t\t\t\tcloseUserSessions(userJid)\n\n\t\t\t\tmsgs.add(\"Operation successful for user \" + userJid)\n\t\t\t} else {\n\t\t\t\tmsgs.add(\"User \" + userJid + \" doesn't exist\")\n\t\t\t}\n\t\t} else {\n\t\t\terrors.add(\n\t\t\t\t\t\"You do not have enough permissions to re-enable accounts for domain \" + bareJID.getDomain() + \".\")\n\t\t}\n\t} catch (UserNotFoundException ex) {\n\t\terrors.add(\"User \" + userJid + \" not exists, can't be re-enabled.\")\n\t} catch (TigaseDBException ex) {\n\t\terrors.add(\"Problem accessing database, user \" + userJid + \" not re-enabled.\")\n\t}\n}\n\nif (!msgs.isEmpty()) {\n\tCommand.addFieldMultiValue(result, \"Notes\", msgs)\n}\n\nif (!errors.isEmpty()) {\n\tCommand.addFieldMultiValue(result, \"Errors\", errors)\n}\n\nresults.offer(result)\n\nreturn results\n\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/RemoveUserTracker.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n Activate on the server user tracking mechanisms to aid in problem resolution.\n AS:Description: Remove log tracker for a user\n AS:CommandId: http://jabber.org/protocol/admin#remove-user-tracker\n AS:Component: sess-man\n */\npackage tigase.admin\n\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.util.log.LogUserFilter\n\nimport java.util.logging.Filter\nimport java.util.logging.Handler\nimport java.util.logging.Logger\n\ndef JID = \"accountjid\"\n\ndef p = (Packet) packet\n\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\nif (!isServiceAdmin) {\n\tdef result = p.commandResult(Command.DataType.result);\n\tCommand.addTextField(result, \"Error\", \"You are not service administrator\");\n\treturn result\n}\n\ndef userJid = Command.getFieldValue(packet, JID)\n\nif (userJid == null) {\n\tdef result = p.commandResult(Command.DataType.form);\n\n\tCommand.addTitle(result, \"Removing a User Log Tracker\")\n\t//Command.addInstructions(result, \"Fill out this form to add a user log tracker.\")\n\n\tCommand.addFieldValue(result, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\",\n\t\t\t\t\t\t  \"hidden\")\n\tCommand.addFieldValue(result, JID, userJid ?: \"\", \"jid-single\",\n\t\t\t\t\t\t  \"The Jabber ID for the tracker to be removed\")\n\treturn result\n}\n\ndef result = p.commandResult(Command.DataType.result)\n\ndef hand = null\n\nHandler[] handlers = Logger.getLogger(\"\").getHandlers()\nhandlers.each {\n\tFilter filt = it.getFilter()\n\tif (filt != null && filt.class == LogUserFilter && ((LogUserFilter) filt).getId() == userJid) {\n\t\thand = it\n\t}\n}\n\nif (hand != null) {\n\tLogger.getLogger(\"\").removeHandler(hand)\n\thand.close()\n\tCommand.addTextField(result, \"Note\", \"Operation successful, tracker removed for \" + userJid);\n} else {\n\tCommand.addTextField(result, \"Note\", \"No tracker found for user \" + userJid)\n}\n\nreturn result\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/RosterFixer.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n\nThe roster fixer scripts is used in a case if for whatever reason user's roster got\nbroken, lost or otherwise messed up. If we know the user's contact list, this script\ncan be used to restore the contact list. It adds missing entries to the user's roster.\nIf the user is online, he gets a roster push with updated entries to make sure\nhe is up to date with all the changes.\nThe script accepts a user JID, action (update or remove) and a list of buddies in\na following format:\nbuddy_jid,buddy_name,subscription\nbuddy_jid is a JID (bare JID)\nbuddy_name is just a string, it is optional, if omit, localpart of the JID is used\nsubscription is one of following (none, from, to, both), it is optional, if omit 'both' is used\n\n\nAS:Description: Fixes user's roster\nAS:CommandId: roster-fixer\nAS:Component: sess-man\n*/\n\npackage tigase.admin\n\nimport tigase.db.UserRepository\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.vhosts.VHostManagerIfc\nimport tigase.xml.Element\nimport tigase.xmpp.XMPPSession\nimport tigase.xmpp.impl.roster.RosterAbstract\nimport tigase.xmpp.impl.roster.RosterElement\nimport tigase.xmpp.impl.roster.RosterFactory\nimport tigase.xmpp.impl.roster.RosterFlat\nimport tigase.xmpp.jid.BareJID\nimport tigase.xmpp.jid.JID\n\ndef ROSTER_OWNER_JID = \"roster-owner-jid\"\n\ndef ROSTER_BUDDY_LIST = \"roster-buddy-list\"\n\ndef ROSTER_ACTION = \"roster-action\"\n\ndef UPDATE = \"update\"\ndef REMOVE = \"remove\"\ndef subscriptions = [ \"both\", \"from\", \"to\", \"none\" ]\ndef actions = [ UPDATE, REMOVE ]\ndef actions_descr = [ \"Add/Update item\", \"Remove item\" ]\n//def notify_cluster = [\"no\", \"yes\"]\n\ndef p = (Packet) packet\ndef repository = (UserRepository) userRepository\ndef sessions = (Map<BareJID, XMPPSession>) userSessions\ndef vhost_man = (VHostManagerIfc) vhostMan\ndef admins = (Set) adminsSet\n\ndef stanzaFromBare = p.getStanzaFrom().getBareJID();\ndef isServiceAdmin = admins.contains(stanzaFromBare);\n\ndef rosterOwnerJid = Command.getFieldValue(packet, ROSTER_OWNER_JID)\ndef rosterAction = Command.getFieldValue(packet, ROSTER_ACTION)\ndef rosterBuddyList = Command.getFieldValues(packet, ROSTER_BUDDY_LIST) as List;\n\n//def rosterNotifyCluster = Command.getFieldValue(packet, ROSTER_NOTIFY_CLUSTER)\n\nif (rosterOwnerJid == null || rosterBuddyList == null || rosterAction == null) {\n\tdef res = p.commandResult(Command.DataType.form);\n\tCommand.addFieldValue(res, ROSTER_OWNER_JID, rosterOwnerJid ?: \"\",\n\t\t\t\t\t\t  \"jid-single\", \"Roster owner JID\")\n\tCommand.addFieldValue(res, ROSTER_ACTION, actions[0],\n\t\t\t\t\t\t  \"Action\", (String[]) actions_descr, (String[]) actions)\n\n\tif (rosterBuddyList == null) {\n\t\trosterBuddyList = [ \"\" ]\n\t}\n\tCommand.addFieldMultiValue(res, ROSTER_BUDDY_LIST, rosterBuddyList)\n\n//\tCommand.addFieldValue(res, ROSTER_NOTIFY_CLUSTER, notify_cluster[0],\n//    \"Notify cluster\", (String[])notify_cluster, (String[])notify_cluster)\n\n\treturn res\n}\n\ndef remove_item = rosterAction == REMOVE\n\ndef Queue<Packet> results = new LinkedList<Packet>()\n\ndef res_report = [ ]\n\ndef updateRoster = { sess, online, jid, i_jid, i_name, i_subscr ->\n\n\tif (online) {\n//\t\tsess.getActiveResources().each{ conn ->\n\t\tdef conn = sess.getActiveResources()[0]\n\t\t// Update online\n\t\tRosterAbstract rosterUtil = RosterFactory.getRosterImplementation(true)\n\t\tif (remove_item) {\n\t\t\trosterUtil.removeBuddy(conn, i_jid)\n\t\t\tElement item = new Element(\"item\",\n\t\t\t\t\t\t\t\t\t   (String[]) [ \"jid\", \"subscription\" ], (String[]) [ i_jid, \"remove\" ])\n\t\t\trosterUtil.updateBuddyChange(conn, results, item)\n\t\t\tElement pres = new Element(\"presence\",\n\t\t\t\t\t\t\t\t\t   (String[]) [ \"from\", \"to\", \"type\", \"xmlns\" ],\n\t\t\t\t\t\t\t\t\t   (String[]) [ jid, i_jid, \"unsubscribed\", \"jabber:client\" ]);\n\t\t\tresults.offer(Packet.packetInstance(pres))\n\t\t\tpres = new Element(\"presence\",\n\t\t\t\t\t\t\t   (String[]) [ \"from\", \"to\", \"type\", \"xmlns\" ],\n\t\t\t\t\t\t\t   (String[]) [ jid, i_jid, \"unsubscribe\", \"jabber:client\" ]);\n\t\t\tresults.offer(Packet.packetInstance(pres))\n\t\t\tres_report += \"Buddy: \" + i_jid + \" removed\"\n\t\t} else {\n\t\t\tif (rosterUtil.containsBuddy(conn, i_jid)) {\n\t\t\t\tres_report += \"Buddy: \" + i_jid + \" already in the roster, skipping\"\n\t\t\t} else {\n\t\t\t\trosterUtil.addBuddy(conn, i_jid, i_name, null, RosterAbstract.SubscriptionType.valueOf(i_subscr), null)\n\t\t\t\titem = rosterUtil.getBuddyItem(conn, i_jid)\n\t\t\t\trosterUtil.updateBuddyChange(conn, results, item)\n\t\t\t\tres_report += \"Buddy: \" + i_jid + \" added to the roster\"\n\t\t\t}\n\t\t}\n//\t\t}\n\t} else {\n\t\t// Update offline\n\t\tString rosterStr = repository.getData(jid.getBareJID(), null,\n\t\t\t\t\t\t\t\t\t\t\t  RosterAbstract.ROSTER, null) ?: \"\"\n\t\tMap<BareJID, RosterElement> roster = new LinkedHashMap<BareJID, RosterElement>()\n\t\tRosterFlat.parseRosterUtil(rosterStr, roster, null)\n\t\tif (remove_item) {\n\t\t\troster.remove(i_jid.getBareJID())\n\t\t\tres_report += \"Buddy: \" + i_jid + \" removed\"\n\t\t} else {\n\t\t\tif (roster.get(i_jid.getBareJID()) == null) {\n\t\t\t\tRosterElement rel = new RosterElement(i_jid, i_name, null)\n\t\t\t\trel.setSubscription(RosterAbstract.SubscriptionType.valueOf(i_subscr))\n\t\t\t\troster.put(i_jid, rel)\n\t\t\t\tres_report += \"Buddy: \" + i_jid + \" added to the roster\"\n\t\t\t} else {\n\t\t\t\tres_report += \"Buddy: \" + i_jid + \" already in the roster, skipping\"\n\t\t\t}\n\t\t}\n\t\tStringBuilder sb = new StringBuilder(200)\n\t\tfor (RosterElement relem : roster.values()) {\n\t\t\tsb.append(relem.getRosterElement().toString())\n\t\t}\n\t\trepository.setData(jid.getBareJID(), null, RosterAbstract.ROSTER, sb.toString());\n\t}\n}\n\ndef jidRosterOwnerJid = JID.jidInstanceNS(rosterOwnerJid);\n\nPacket result = p.commandResult(Command.DataType.result)\ndef vhost = vhost_man.getVHostItem(jidRosterOwnerJid.getDomain());\nif (vhost == null ||\n\t\t(!isAllowedForDomain.apply(jidRosterOwnerJid.getDomain()))) {\n\tCommand.addTextField(result, \"Error\", \"You do not have enough permissions to modify roster of \" + rosterOwnerJid);\n\tresults.add(result);\n\treturn results;\n}\n\ndef rosterItemJid = null\ndef sess = sessions == null ? null : sessions.get(jidRosterOwnerJid.getBareJID());\ndef conn = sess != null ? sess.getActiveResources().get(0) : null;\ndef online = true\nif (conn) {\n\tres_report += \"User: \" + jidRosterOwnerJid + \" is online, updating database and online connections\"\n} else {\n\tres_report += \"User: \" + jidRosterOwnerJid + \" is offline, updating database only\"\n\tonline = false\n}\n\nrosterBuddyList.each {\n\tdef buddy = it.split(\",\")\n\tif (it.contains(';')) {\n\t\tbuddy = it.split(\";\")\n\t}\n\trosterItemJid = buddy[0]\n\tdef jidRosterItemJid = JID.jidInstanceNS(rosterItemJid)\n\tdef rosterItemName = (buddy as List)[1] ?: jidRosterItemJid.getLocalpart()\n\tdef rosterItemSubscr = (buddy as List)[2] ?: \"both\"\n\n\tupdateRoster(sess, online, jidRosterOwnerJid, jidRosterItemJid, rosterItemName, rosterItemSubscr)\n\n\tif (!remove_item) {\n\n\t\tElement pres = new Element(\"presence\",\n\t\t\t\t\t\t\t\t   (String[]) [ \"from\", \"to\", \"type\", \"xmlns\" ],\n\t\t\t\t\t\t\t\t   (String[]) [ rosterOwnerJid, rosterItemJid,\n\t\t\t\t\t\t\t\t\t\t\t\t\"probe\", \"jabber:client\" ])\n\t\tresults.offer(Packet.packetInstance(pres))\n\t\tpres = new Element(\"presence\",\n\t\t\t\t\t\t   (String[]) [ \"from\", \"to\", \"type\", \"xmlns\" ], (String[]) [ rosterItemJid, rosterOwnerJid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"probe\", \"jabber:client\" ])\n\t\tresults.offer(Packet.packetInstance(pres))\n\t}\n\n}\n\n\nCommand.addTextField(result, \"Note\", \"Operation successful\");\nCommand.addFieldMultiValue(result, \"Report: \", res_report)\nresults.add(result)\n\nreturn results\n\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/RosterFixerCluster.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\nThe roster fixer scripts is used in a case if for whatever reason user's roster got\nbroken, lost or otherwise messed up. If we know the user's contact list, this script\ncan be used to restore the contact list. It adds missing entries to the user's roster.\nIf the user is online, he gets a roster push with updated entries to make sure\nhe is up to date with all the changes.\nThe script accepts a user JID, action (update or remove) and a list of buddies in\na following format:\nbuddy_jid,buddy_name,subscription\nbuddy_jid is a JID (bare JID)\nbuddy_name is just a string, it is optional, if omit, localpart of the JID is used\nsubscription is one of following (none, from, to, both), it is optional, if omit 'both' is used\nAS:Description: Fixes user's roster on Tigase cluster\nAS:CommandId: roster-fixer-cluster\nAS:Component: sess-man\n */\n\npackage tigase.admin\n\nimport tigase.cluster.strategy.ClusteringStrategyIfc\nimport tigase.cluster.strategy.ConnectionRecord\nimport tigase.db.UserRepository\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.vhosts.VHostManagerIfc\nimport tigase.xml.Element\nimport tigase.xmpp.StanzaType\nimport tigase.xmpp.XMPPSession\nimport tigase.xmpp.impl.roster.RosterAbstract\nimport tigase.xmpp.impl.roster.RosterElement\nimport tigase.xmpp.impl.roster.RosterFlat\nimport tigase.xmpp.jid.BareJID\nimport tigase.xmpp.jid.JID\n\ndef ROSTER_OWNER_JID = \"roster-owner-jid\"\n\ndef ROSTER_BUDDY_LIST = \"roster-buddy-list\"\n\ndef ROSTER_ACTION = \"roster-action\"\n\ndef DISCONNECTED_PHASE = \"disconnected-phase\"\n\ndef UPDATE = \"update\"\ndef REMOVE = \"remove\"\ndef subscriptions = [ \"both\", \"from\", \"to\", \"none\" ]\ndef actions = [ UPDATE, REMOVE ]\ndef actions_descr = [ \"Add/Update item\", \"Remove item\" ]\n//def notify_cluster = [\"no\", \"yes\"]\n\ndef p = (Packet) packet\ndef repository = (UserRepository) userRepository\ndef sessions = (Map<BareJID, XMPPSession>) userSessions\ndef vhost_man = (VHostManagerIfc) vhostMan\ndef admins = (Set) adminsSet\n\ndef stanzaFromBare = p.getStanzaFrom().getBareJID();\ndef isServiceAdmin = admins.contains(stanzaFromBare);\n\ndef rosterOwnerJid = Command.getFieldValue(packet, ROSTER_OWNER_JID)\ndef rosterAction = Command.getFieldValue(packet, ROSTER_ACTION)\ndef rosterBuddyList = Command.getFieldValues(packet, ROSTER_BUDDY_LIST) as List;\n\n//def rosterNotifyCluster = Command.getFieldValue(packet, ROSTER_NOTIFY_CLUSTER)\n\nboolean clusterMode = Boolean.valueOf(System.getProperty(\"cluster-mode\", false.toString()));\n\nif (rosterOwnerJid == null || rosterBuddyList == null || rosterAction == null) {\n\tdef res = p.commandResult(Command.DataType.form);\n\tCommand.addFieldValue(res, ROSTER_OWNER_JID, rosterOwnerJid ?: \"\",\n\t\t\t\t\t\t  \"jid-single\", \"Roster owner JID\")\n\tCommand.addFieldValue(res, ROSTER_ACTION, actions[0],\n\t\t\t\t\t\t  \"Action\", (String[]) actions_descr, (String[]) actions)\n\n\tif (rosterBuddyList == null) {\n\t\trosterBuddyList = [ \"\" ]\n\t}\n\tCommand.addFieldMultiValue(res, ROSTER_BUDDY_LIST, rosterBuddyList)\n\n\t//\tCommand.addFieldValue(res, ROSTER_NOTIFY_CLUSTER, notify_cluster[0],\n\t//    \"Notify cluster\", (String[])notify_cluster, (String[])notify_cluster)\n\n\treturn res\n}\n\ndef remove_item = rosterAction == REMOVE\ndef res_report = [ ]\ndef jidRosterOwnerJid = JID.jidInstanceNS(rosterOwnerJid)\ndef Queue<Packet> results = new LinkedList<Packet>()\n\nPacket result = p.commandResult(Command.DataType.result)\ndef vhost = vhost_man.getVHostItem(jidRosterOwnerJid.getDomain());\nif (vhost == null ||\n\t\t(!isAllowedForDomain.apply(jidRosterOwnerJid.getDomain()))) {\n\tCommand.addTextField(result, \"Error\", \"You do not have enough permissions to modify roster of \" + rosterOwnerJid);\n\tresults.offer(result);\n\treturn results;\n}\n\ndef disconnected = Command.getFieldValue(p, DISCONNECTED_PHASE)\n\n// Disconnecting all user's connections on the whole cluster\n\nif (!disconnected) {\n\n\tdef sess = sessions == null ? null : sessions.get(jidRosterOwnerJid.getBareJID())\n\tdef online = false\n\n\tif (sess && sess.getActiveResourcesSize() > 0) {\n\t\tsess.getActiveResources().each { conn ->\n\t\t\tdef commandClose = Command.CLOSE.getPacket(p.getStanzaTo(), conn.getConnectionId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t   StanzaType.set, conn.nextStanzaId())\n\t\t\tresults.offer(commandClose)\n\t\t\tres_report += \"User: \" + conn.getjid() + \" is online, disconnected.\"\n\t\t}\n\t\tonline = true\n\t}\n\n\tif (clusterMode) {\n\t\tif (null != clusterStrategy) {\n\t\t\tdef cluster = (ClusteringStrategyIfc) clusterStrategy\n\t\t\tSet<ConnectionRecord> cl_conns = cluster.getConnectionRecords(jidRosterOwnerJid.getBareJID())\n\t\t\tif (cl_conns && cl_conns.size() > 0) {\n\t\t\t\tcl_conns.each {\n\t\t\t\t\tdef commandClose = Command.CLOSE.getPacket(p.getStanzaTo(), it.getConnectionId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   StanzaType.set, \"77\")\n\t\t\t\t\tresults.offer(commandClose)\n\t\t\t\t\tres_report += \"User: \" + it.getUserJid() + \" is online on node: \" + it.getNode() + \", disconnected.\"\n\n\t\t\t\t}\n\t\t\t\tonline = true\n\t\t\t}\n\t\t}\n\t}\n\n\tif (online) {\n\t\tCommand.addFieldMultiValue(p, \"Report: \", res_report)\n\t\tCommand.addHiddenField(p, DISCONNECTED_PHASE, DISCONNECTED_PHASE)\n\t\tresults.offer(p)\n\n\t\treturn results\n\t}\n} else {\n\n\tCommand.getFieldValues(packet, \"Report: \").each {\n\t\tres_report += it\n\t}\n}\n\n// Waiting 2 secs\n\nsleep(2000)\n\n// Updating user's roster in DB\n\ndef updateRoster = { sess, online, jid, i_jid, i_name, i_subscr ->\n\n\t// Update offline\n\tString rosterStr = repository.getData(jid.getBareJID(), null,\n\t\t\t\t\t\t\t\t\t\t  RosterAbstract.ROSTER, null) ?: \"\"\n\tMap<BareJID, RosterElement> roster = new LinkedHashMap<BareJID, RosterElement>()\n\tRosterFlat.parseRosterUtil(rosterStr, roster, null)\n\tif (remove_item) {\n\t\troster.remove(i_jid.getBareJID())\n\t\tres_report += \"Buddy: \" + i_jid + \" removed\"\n\t} else {\n\t\tif (roster.get(i_jid.getBareJID()) == null) {\n\t\t\tRosterElement rel = new RosterElement(i_jid, i_name, null)\n\t\t\trel.setSubscription(RosterAbstract.SubscriptionType.valueOf(i_subscr))\n\t\t\troster.put(i_jid, rel)\n\t\t\tres_report += \"Buddy: \" + i_jid + \" added to the roster\"\n\t\t} else {\n\t\t\tres_report += \"Buddy: \" + i_jid + \" already in the roster, skipping\"\n\t\t}\n\t}\n\tStringBuilder sb = new StringBuilder(200)\n\tfor (RosterElement relem : roster.values()) {\n\t\tsb.append(relem.getRosterElement().toString())\n\t}\n\trepository.setData(jid.getBareJID(), null, RosterAbstract.ROSTER, sb.toString());\n\n}\n\n\ndef rosterItemJid = null\ndef sess = sessions == null ? null : sessions.get(jidRosterOwnerJid.getBareJID());\ndef conn = sess != null ? sess.getActiveResources().get(0) : null;\ndef online = true\nif (conn) {\n\tres_report += \"User: \" + jidRosterOwnerJid + \" is online, updating database and online connections\"\n} else {\n\tres_report += \"User: \" + jidRosterOwnerJid + \" is offline, updating database only\"\n\tonline = false\n}\n\nrosterBuddyList.each {\n\tdef buddy = it.split(\",\")\n\tif (it.contains(';')) {\n\t\tbuddy = it.split(\";\")\n\t}\n\trosterItemJid = buddy[0]\n\tdef jidRosterItemJid = JID.jidInstanceNS(rosterItemJid)\n\tdef rosterItemName = (buddy as List)[1] ?: jidRosterItemJid.getLocalpart()\n\tdef rosterItemSubscr = (buddy as List)[2] ?: \"both\"\n\n\tupdateRoster(sess, online, jidRosterOwnerJid, jidRosterItemJid, rosterItemName, rosterItemSubscr)\n\n\tif (!remove_item) {\n\n\t\tElement pres = new Element(\"presence\",\n\t\t\t\t\t\t\t\t   (String[]) [ \"from\", \"to\", \"type\" ], (String[]) [ rosterOwnerJid, rosterItemJid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"probe\" ])\n\t\tresults.offer(Packet.packetInstance(pres))\n\t\tpres = new Element(\"presence\",\n\t\t\t\t\t\t   (String[]) [ \"from\", \"to\", \"type\" ], (String[]) [ rosterItemJid, rosterOwnerJid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"probe\" ])\n\t\tresults.offer(Packet.packetInstance(pres))\n\t}\n\n}\n\n// Finishing the work and completing script\n\nCommand.addTextField(result, \"Note\", \"Operation successful\");\nCommand.addFieldMultiValue(result, \"Report: \", res_report)\nresults.add(result)\n\nreturn results\n\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/S2SBadConnectionStates.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n Script tries to find S2S connection in a bad state and reports connection details.\n AS:Description: S2S Bad State Connections\n AS:CommandId: s2s-bad-state-conns\n AS:Component: s2s\n */\n\npackage tigase.admin\n\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.server.xmppserver.CIDConnections\n\ndef p = (Packet) packet\ndef cidConns = (Map) cidConnections\n\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\nif (!isServiceAdmin) {\n\tdef result = p.commandResult(Command.DataType.result);\n\tCommand.addTextField(result, \"Error\", \"You are not service administrator\");\n\treturn result\n}\n\ndef conns = [ ]\n\nconns += \"Total count: \" + cidConns.size()\n\ncidConns.entrySet().each {\n\tCIDConnections con = it.getValue()\n\tif (con.getWaitingCount() > 0) {\n\t\tconns += it.getKey().toString() + \", out in progress: \" + con.getOutgoingInProgress() + \", waiting: \" +\n\t\t\t\tcon.getWaitingCount() + \", control: \" + con.getWaitingControlCount() + \", incoming: \" +\n\t\t\t\tcon.getIncomingCount() + \", outgoing: \" + con.getOutgoingCount() + \", out-handshaking: \" +\n\t\t\t\tcon.getOutgoingHandshakingCount()\n\t}\n}\n\ndef result = p.commandResult(Command.DataType.result)\nCommand.addFieldMultiValue(result, \"Connections with waiting packets\", conns);\nreturn result\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/S2SCIDState.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n Returns S2S connection state for given CID\n AS:Description: S2S get CID connection state\n AS:CommandId: s2s-get-cid-connection\n AS:Component: s2s\n */\n\npackage tigase.admin\n\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.server.xmppserver.CID\nimport tigase.server.xmppserver.CIDConnections\n\ndef p = (Packet) packet\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\ndef CID_KEY = \"cid\"\n\ndef cid = Command.getFieldValue(packet, CID_KEY)\n\nif (!isServiceAdmin) {\n\tdef result = p.commandResult(Command.DataType.result);\n\tCommand.addTextField(result, \"Error\", \"You are not service administrator\");\n\treturn result\n}\n\n\nif (cid == null) {\n\tdef result = p.commandResult(Command.DataType.form);\n\n\tCommand.addTitle(result, \"Get S2S connection state\")\n\tCommand.addInstructions(result, \"Fill out this form to get S2S connection state\")\n\n\tCommand.addFieldValue(result, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\",\n\t\t\t\t\t\t  \"hidden\")\n\tCommand.addFieldValue(result, CID_KEY, cid ?: \"\", \"text-single\",\n\t\t\t\t\t\t  \"S2S connection CID\")\n\n\treturn result\n}\n\ndef conns = [ ]\ndef cidConns = (Map) cidConnections\n\nCID c = new CID(cid)\nCIDConnections con = cidConns.get(c)\n\nif (con == null) {\n\tconns += \"No such CID connection found\"\n} else {\n\tconns += cid\n\tconns += \"out in progress: \" + con.getOutgoingInProgress()\n\tconns += \"waiting: \" + con.getWaitingCount()\n\tconns += \"control: \" + con.getWaitingControlCount()\n\tconns += \"incoming: \" + con.getIncomingCount()\n\tconns += \"outgoing: \" + con.getOutgoingCount()\n\tconns += \"out-handshaking: \" + con.getOutgoingHandshakingCount()\n}\n\ndef result = p.commandResult(Command.DataType.result)\nCommand.addFieldMultiValue(result, \"S2S connection\", conns);\nreturn result\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/S2SGetAllConnectionStates.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n Script lists all CID connections.\n AS:Description: S2S State of All Connections\n AS:CommandId: s2s-all-conns-state\n AS:Component: s2s\n */\n\npackage tigase.admin\n\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.server.xmppserver.CIDConnections\n\ndef p = (Packet) packet\ndef cidConns = (Map) cidConnections\n\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\nif (!isServiceAdmin) {\n\tdef result = p.commandResult(Command.DataType.result);\n\tCommand.addTextField(result, \"Error\", \"You are not service administrator\");\n\treturn result\n}\n\ndef conns = [ ]\n\nconns += \"Total count: \" + cidConns.size() + \":\"\n\ncidConns.entrySet().each {\n\tCIDConnections con = it.getValue()\n\tconns += it.getKey().toString() + \", out in progress: \" + con.getOutgoingInProgress() + \", waiting: \" +\n\t\t\tcon.getWaitingCount() + \", control: \" + con.getWaitingControlCount() + \", incoming: \" +\n\t\t\tcon.getIncomingCount() + \", outgoing: \" + con.getOutgoingCount() + \", out-handshaking: \" +\n\t\t\tcon.getOutgoingHandshakingCount()\n}\n\ndef result = p.commandResult(Command.DataType.result)\nCommand.addFieldMultiValue(result, \"Connections with waiting packets\", conns);\nreturn result\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/S2SResetBadConnections.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n Script tries to find S2S connection in a bad state and resets their state\n AS:Description: S2S Reset Bad State Connections\n AS:CommandId: s2s-reset-bad-state-conns\n AS:Component: s2s\n */\n\npackage tigase.admin\n\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.server.xmppserver.CIDConnections\n\ndef cidConns = (Map) cidConnections\n\ndef p = (Packet) packet\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\nif (!isServiceAdmin) {\n\tdef result = p.commandResult(Command.DataType.result);\n\tCommand.addTextField(result, \"Error\", \"You are not service administrator\");\n\treturn result\n}\n\n\ndef conns = [ ]\n\nconns += \"Total count: \" + cidConns.size()\n\ncidConns.entrySet().each {\n\tCIDConnections con = it.getValue()\n\t// Bad state is when the OutgoingInProgress is set to true but there is no outgoing\n\t// or outgoing handshaking connections.\n\tif (con.getOutgoingInProgress() && (con.getOutgoingCount() == 0) && (con.getOutgoingHandshakingCount() == 0)) {\n\t\tconns += it.getKey().toString() + \", waiting: \" + con.getWaitingCount()\n\t\tcon.resetOutgoingInProgress()\n\t}\n}\n\ndef result = p.commandResult(Command.DataType.result)\nCommand.addFieldMultiValue(result, \"Reset connections\", conns);\nreturn result\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/SSLCertificateAdd.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n\nAdd an SSL certificate for a given domain.\n\nAS:Description: Add SSL Certificate\nAS:CommandId: ssl-certificate-add\nAS:Component: vhost-man\n */\n\npackage tigase.admin\n\nimport groovy.transform.CompileStatic\nimport tigase.cert.CertificateEntry\nimport tigase.cert.CertificateUtil\nimport tigase.db.comp.ComponentRepository\nimport tigase.io.CertificateContainerIfc\nimport tigase.io.SSLContextContainerIfc\nimport tigase.kernel.core.Kernel\nimport tigase.server.Command\nimport tigase.server.Iq\nimport tigase.server.Packet\nimport tigase.vhosts.VHostItem\nimport tigase.xmpp.jid.BareJID\n\nimport java.security.cert.Certificate\nimport java.security.cert.X509Certificate\nimport java.util.function.Function\nimport java.util.logging.Level\nimport java.util.logging.Logger\n\nKernel kernel = (Kernel) kernel;\ndef repo = (ComponentRepository) comp_repo\ndef p = (Packet) packet\ndef admins = (Set) adminsSet\ndef log = Logger.getLogger(\"tigase.admin\");\n\n@CompileStatic\nPacket process(Kernel kernel, Logger log, ComponentRepository<VHostItem> repo, Iq packet, Set admins, Function<String,Boolean> isAllowedForDomain) {\n\tdef MARKER = \"command-marker\"\n\n\n\ttry {\n\n\t\tdef VHOST = \"VHost\"\n\t\tdef CERTIFICATE = \"Certificate in PEM format\"\n\t\tdef SAVE_TO_DISK = \"Save permanently (to disk or repository)\"\n\t\tdef USE_AS_DEFAULT = \"Use as default\"\n\n\t\tdef stanzaFromBare = packet.getStanzaFrom().getBareJID()\n\t\tdef isServiceAdmin = admins.contains(stanzaFromBare)\n\n\t\tdef itemKey = Command.getFieldValue(packet, VHOST)\n\t\tdef marker = Command.getFieldValue(packet, MARKER)\n\t\tdef pemCertVals = Command.getFieldValues(packet, CERTIFICATE)\n\t\tdef saveToDisk = Command.getCheckBoxFieldValue(packet, SAVE_TO_DISK)\n\t\tdef useAsDefault = Command.getCheckBoxFieldValue(packet, USE_AS_DEFAULT)\n\n// The first step - provide a list of all vhosts for the user\n\t\tif (itemKey == null) {\n\t\t\tCollection<VHostItem> items = repo.allItems()\n\t\t\tList<String> itemsStr = items.findAll { isAllowedForDomain.apply(it.getKey()) }.collect { it.getKey() };\n\t\t\tif (itemsStr.size() > 0) {\n\t\t\t\tString[] itemsStrArray = itemsStr.toArray(new String[itemsStr.size()]);\n\t\t\t\tdef result = packet.commandResult(Command.DataType.form)\n\t\t\t\tCommand.addFieldValue(result, VHOST, itemsStr[0], \"List of VHosts\",\n\t\t\t\t\t\t\t\t\t  itemsStrArray, itemsStrArray);\n\t\t\t\treturn result\n\t\t\t} else {\n\t\t\t\tdef result = packet.commandResult(Command.DataType.result)\n\t\t\t\tCommand.addTextField(result, \"Note\", \"You have no VHosts to manage\");\n\t\t\t\treturn result\n\t\t\t}\n\t\t}\n\n// The second step - provide a form to fill be by the user for selected vhost\n\t\tif (marker == null) {\n\t\t\tVHostItem item = repo.getItem(itemKey)\n\t\t\tif (item == null) {\n\t\t\t\tdef result = packet.commandResult(Command.DataType.result)\n\t\t\t\tCommand.addTextField(result, \"Error\", \"No such VHost, adding SSL Certificate impossible.\");\n\t\t\t\treturn result;\n\t\t\t} else {\n\t\t\t\tif (isAllowedForDomain.apply(itemKey)) {\n\t\t\t\t\tdef result = packet.commandResult(Command.DataType.form)\n\t\t\t\t\tCommand.addFieldValue(result, VHOST, itemKey, \"text-single\")\n\t\t\t\t\tCommand.addFieldMultiValue(result, CERTIFICATE, [ \"\" ])\n\t\t\t\t\tCommand.addCheckBoxField(result, SAVE_TO_DISK, true)\n\t\t\t\t\tCommand.addCheckBoxField(result, USE_AS_DEFAULT, false)\n\t\t\t\t\tCommand.addHiddenField(result, MARKER, MARKER)\n\t\t\t\t\treturn result\n\t\t\t\t} else {\n\t\t\t\t\tdef result = packet.commandResult(Command.DataType.result)\n\t\t\t\t\tCommand.addTextField(result, \"Error\", \"You do not have enough permissions to manage this VHost.\")\n\t\t\t\t\treturn result\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n// The last step - process the form submitted by the user\n\t\tdef result = packet.commandResult(Command.DataType.result)\n\t\tVHostItem item = repo.getItem(itemKey)\n\n\t\tif (item == null) {\n\t\t\tCommand.addTextField(result, \"Error\", \"No such VHost, loading SSL certificate impossible.\")\n\t\t} else {\n\t\t\tif (isAllowedForDomain.apply(itemKey)) {\n\t\t\t\tdef pemCert = pemCertVals.join('\\n')\n\t\t\t\t// Basic certificate checks\n\t\t\t\t// For XMPP service nonAdmins (domain owners) the alias must match CN name in the certificate\n\t\t\t\tCertificateEntry certEntry = CertificateUtil.parseCertificate(new CharArrayReader(pemCert.toCharArray()))\n\t\t\t\tif (certEntry.getPrivateKey() == null) {\n\t\t\t\t\tCommand.addTextField(result, \"Error\",\n\t\t\t\t\t\t\t\t\t\t \"Missing private key or private key encoded in uknown format.\")\n\t\t\t\t\tCommand.addTextField(result, \"Note\", \"Private key cannot be encoded with a password.\")\n\t\t\t\t} else {\n\t\t\t\t\tdef domainCertificate = (X509Certificate)CertificateUtil.sort(certEntry.getCertChain())[0]\n\t\t\t\t\tdef certCName = CertificateUtil.getCertCName(domainCertificate)\n\t\t\t\t\tdef subjectAltName = CertificateUtil.getCertAltCName(domainCertificate)\n\t\t\t\t\tif (hasPermissionToUpdate(item, isServiceAdmin, stanzaFromBare, log) &&\n\t\t\t\t\t\t\tisCertificateValidForVhost(itemKey, certCName, subjectAltName, log)) {\n\t\t\t\t\t\tCertificateContainerIfc certContainer = kernel.getInstance(CertificateContainerIfc.class);\n\t\t\t\t\t\tcertContainer.addCertificates(new CertificateContainerIfc.CertificateEntity(pemCert, itemKey, saveToDisk, useAsDefault));\n\n\t\t\t\t\t\tCommand.addTextField(result, \"Note\",\n\t\t\t\t\t\t\t\t\t\t\t \"SSL Certificate for domain: \" + itemKey + \" loaded successfully\")\n\t\t\t\t\t} else {\n\t\t\t\t\t\tCommand.addTextField(result, \"Error\",\n\t\t\t\t\t\t\t\t\t\t\t \"Neither certificate CName nor any of SubjectAlternativeNames match the domain name!\")\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tCommand.addTextField(result, \"Error\", \"You are not the VHost owner or you have no \" +\n\t\t\t\t\t\t\"enough permission to change the VHost.\")\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t} catch (Exception ex) {\n\t\tdef result = packet.commandResult(Command.DataType.result);\n\t\tdef errorMessage = ex.getMessage()\n\t\tif (ex.getCause()!=null) {\n\t\t\terrorMessage += \"\\n\" + ex.getCause().getMessage();\n\t\t}\n\t\tCommand.addTextField(result, \"Error\", errorMessage);\n\t\tlog.log(Level.FINE, \"Error while processing request\", ex)\n\t\treturn result;\n\t}\n}\n\nprivate static boolean hasPermissionToUpdate(VHostItem item, boolean isServiceAdmin, BareJID userJid, Logger log) {\n\tif (log.isLoggable(Level.FINEST)) {\n\t\tlog.log(Level.FINEST, \"hasPermissionToUpdate :: userJid: ${userJid}, isServiceAdmin: ${isServiceAdmin}, isOwner: ${item.isOwner(userJid.toString())}, isAdmin: ${item.isAdmin(userJid.toString())}\")\n\t}\n\tisServiceAdmin || item.isOwner(userJid.toString()) || item.isAdmin(userJid.toString())\n}\n\nprivate static boolean isCertificateValidForVhost(String itemKey, String certCName, List<String> subjectAltName,\n\t\t\t\t\t\t\t\t\t\t\t\t  Logger log) {\n\tdef wildcardItemKey = \"*.\" + itemKey\n\tdef result = certCName == itemKey || certCName == wildcardItemKey || isWildcardMatch(certCName, itemKey) || subjectAltName.contains(itemKey) ||\n\t\t\tsubjectAltName.contains(wildcardItemKey)\n\tif (log.isLoggable(Level.FINEST)) {\n\t\tLogger.getLogger(\"tigase.admin\").\n\t\t\t\tlog(Level.FINEST,\n\t\t\t\t\t\"isCertificateValidForVhost:: itemKey: ${itemKey}, wildcardItemKey: ${wildcardItemKey}, certCName: ${certCName}, subjectAltName: ${subjectAltName}, result: ${result}\")\n\t}\n\treturn result\n}\n\nprivate static isWildcardMatch(String certCName, String vhost) {\n\tdef unWildcardCName = certCName.startsWith(\"*.\") ? certCName.substring(2) : certCName;\n\tdef parentDomain = vhost.indexOf(\".\") != -1 ? vhost.substring(vhost.indexOf(\".\") + 1) : vhost;\n\treturn unWildcardCName == parentDomain;\n}\n\nreturn process(kernel, log, repo, p, admins, (Function<String,Boolean>) isAllowedForDomain);"
  },
  {
    "path": "src/main/groovy/tigase/admin/SetMOTD.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\nSet Message of the Day\nAS:Description: Set Message of the Day\nAS:CommandId: http://jabber.org/protocol/admin#set-motd\nAS:Component: sess-man\nAS:Group: Configuration\n */\npackage tigase.admin\n\nimport groovy.transform.CompileStatic\nimport tigase.kernel.KernelException\nimport tigase.kernel.core.Kernel\nimport tigase.server.Command\nimport tigase.server.Iq\nimport tigase.server.Packet\nimport tigase.server.xmppsession.SessionManager\nimport tigase.xmpp.impl.MotdProcessor\n\nimport java.util.logging.Level\nimport java.util.logging.Logger\n\nKernel kernel = (Kernel) kernel;\nSessionManager component = (SessionManager) component\npacket = (Iq) packet\n\n@CompileStatic\nPacket process(Kernel kernel, SessionManager component, Iq p) {\n\tif (!component.isAdmin(p.getStanzaFrom())) {\n\t\tdef result = p.commandResult(Command.DataType.result)\n\t\tCommand.addTextField(result, \"Error\", \"You are not service administrator\")\n\t\treturn result\n\t}\n\n\ttry {\n\t\tMotdProcessor motdProcessor = kernel.getInstance(MotdProcessor.class)\n\t\tString MOTD = \"motd\"\n\t\t\n\t\tdef motd = Command.getFieldValues(p, MOTD)\n\t\tif (!motd) {\n\t\t\tdef result = p.commandResult(Command.DataType.form)\n\n\t\t\tCommand.addTitle(result, \"Setting the Message of the Day\")\n\t\t\tCommand.addInstructions(result, \"Fill out this form to set the message of the day.\")\n\t\t\tCommand.addHiddenField(result, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\")\n\t\t\tCommand.addFieldMultiValue(result, MOTD, [ ], \"Message of the Day\")\n\n\t\t\treturn result\n\t\t} else {\n\t\t\tdef result = p.commandResult(Command.DataType.result)\n\t\t\tmotdProcessor.setMotd(motd.join(\"\\n\"))\n\t\t\treturn result\n\t\t}\n\t} catch (KernelException ex) {\n\t\tdef result = p.commandResult(Command.DataType.result)\n\t\tLogger.getLogger(\"tigase.admin.SetMOTD\").log(Level.WARNING, \"MotDProcessor is not enabled or misconfigured\", ex);\n\t\tCommand.addTextField(result, \"Error\",\n\t\t\t\t\t\t\t\t \"MotDProcessor is not enabled or misconfigured - please check server logs.\");\n\t\treturn result;\n\t}\n}\n\nreturn process(kernel, component, packet)"
  },
  {
    "path": "src/main/groovy/tigase/admin/SetWelcomeMessage.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\nSet Welcome Message\nAS:Description: Set Welcome Message\nAS:CommandId: http://jabber.org/protocol/admin#set-welcome\nAS:Component: sess-man\nAS:Group: Configuration\n */\npackage tigase.admin\n\nimport groovy.transform.CompileStatic\nimport tigase.kernel.core.Kernel\nimport tigase.server.Command\nimport tigase.server.Iq\nimport tigase.server.Packet\nimport tigase.server.xmppsession.SessionManager\nimport tigase.xmpp.impl.JabberIqRegister\n\nKernel kernel = (Kernel) kernel;\nSessionManager component = (SessionManager) component\npacket = (Iq) packet\n\n@CompileStatic\nPacket process(Kernel kernel, SessionManager component, Iq p) {\n\tString MOTD = \"welcome\"\n\n\tif (!component.isAdmin(p.getStanzaFrom())) {\n\t\tdef result = p.commandResult(Command.DataType.result)\n\t\tCommand.addTextField(result, \"Error\", \"You are not service administrator\")\n\t\treturn result\n\t}\n\n\tif (kernel.getDependencyManager().getBeanConfigs(JabberIqRegister.class, null, null, true).isEmpty()) {\n\t\tdef result = p.commandResult(Command.DataType.result)\n\t\tCommand.addTextField(result, \"Error\", \"JabberIqRegister is disabled\");\n\t\treturn result\n\t}\n\n\tdef motd = Command.getFieldValues(p, MOTD)\n\tif (!motd) {\n\t\tdef result = p.commandResult(Command.DataType.form)\n\n\t\tCommand.addTitle(result, \"Setting Welcome Message\")\n\t\tCommand.addInstructions(result, \"Fill out this form to set the welcome message for this service.\")\n\t\tCommand.addHiddenField(result, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\")\n\t\tCommand.addFieldMultiValue(result, MOTD, [ ], \"Welcome message\")\n\n\t\treturn result\n\t} else {\n\t\tdef result = p.commandResult(Command.DataType.result)\n\t\tJabberIqRegister registerProcessor = kernel.getInstance(JabberIqRegister.class)\n\t\tregisterProcessor.setWelcomeMessage(motd.join(\"\\n\"))\n\t\treturn result\n\t}\n}\n\nreturn process(kernel, component, packet)"
  },
  {
    "path": "src/main/groovy/tigase/admin/Shutdown.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\nScript executes graceful shutdown of cluster node\nAS:Description: Shutdown\nAS:CommandId: http://jabber.org/protocol/admin#shutdown\nAS:Component: message-router\nAS:Group: Configuration\n */\n\npackage tigase.admin\n\nimport tigase.server.Command\nimport tigase.server.Packet\n\ndef DELAY = \"delay\";\ndef NODE = \"node\";\ndef NOTIFY = \"Notify users\";\ndef MESSAGE = \"Message to users\";\ndef p = (Packet) packet\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\nif (!isServiceAdmin) {\n\tdef result = p.commandResult(Command.DataType.result);\n\tCommand.addTextField(result, \"Error\", \"This command is available only to administrator of this service\");\n\treturn result\n}\n\ndef nodes = Command.getFieldValues(p, NODE) as List;\ndef notify = Command.getCheckBoxFieldValue(p, NOTIFY);\ndef msg = (Command.getFieldValues(p, MESSAGE) as List) ?:\n\t\t  [ \"Server will be restarted.\", \"During restart you will be disconnected from XMPP server.\" ];\ndef delay = Command.getFieldValue(p, DELAY) ?: \"30\";\n\ndef result = null;\nif (nodes == null || nodes.isEmpty()) {\n\tresult = p.commandResult(Command.DataType.form)\n\tCommand.addTitle(result, \"Shutting Down the Service\")\n\tCommand.addInstructions(result, \"Fill out this form to shut down the service.\")\n\tnodes = component.getNodesConnectedWithLocal().collect { it.getDomain() };\n\tCommand.addFieldValue(result, NODE, [ ] as String[], \"Nodes to shutdown\", nodes as String[], nodes as String[])\n\tCommand.addFieldValue(result, DELAY, delay, \"Delay before node shutdown\",\n\t\t\t\t\t\t  [ \"30sec\", \"1min\", \"3min\", \"5min\" ] as String[], [ \"30\", 60, \"180\", \"300\" ] as String[]);\n\tCommand.addCheckBoxField(result, NOTIFY, false);\n\tCommand.addFieldMultiValue(result, MESSAGE, msg)\n} else {\n\tresult = p.commandResult(Command.DataType.result);\n\tnodes.each { node ->\n\t\tdef event = new tigase.eventbus.events.ShutdownEvent(node, delay as Long,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t (notify && msg) ? msg.join(\"\\n\") : null);\n\t\teventBus.fire(event);\n\t}\n\tCommand.addTextField(result, \"Info\", \"Shutdown of service started\");\n}\n\nreturn result"
  },
  {
    "path": "src/main/groovy/tigase/admin/SimServiceStopped.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n\nSimulate service stopped uncontrolled execution to help with testing weird network\nproblems.\n\nAS:Description: Simulate serviceStopped method call\nAS:CommandId: sim-serv-stopped\nAS:Component: cl-comp\n*/\n\npackage tigase.admin\n\nimport tigase.cluster.ClusterConnectionManager\nimport tigase.net.IOService\nimport tigase.server.Command\nimport tigase.server.Packet\n\ndef p = (Packet) packet\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\nif (!isServiceAdmin) {\n\tdef result = p.commandResult(Command.DataType.result);\n\tCommand.addTextField(result, \"Error\", \"You are not service administrator\");\n\treturn result\n}\n\ndef KEY = \"key\"\n\ndef key = Command.getFieldValue(packet, KEY)\n\nif (key == null) {\n\tdef result = p.commandResult(Command.DataType.form);\n\tCommand.addTitle(result, \"Simulate serviceStopped method call\")\n\tCommand.addInstructions(result, \"Provide a key for IOService you wish to test.\")\n\tCommand.addFieldValue(result, KEY, key ?: \"\", \"text-single\", \"Key\")\n\n\treturn result\n}\n\nMap services = (Map) servicesMap\n\nIOService serv = services.get(key)\n\nif (serv == null) {\n\treturn \"IOService for key: ${key} not found!\"\n} else {\n\tClusterConnectionManager clCM = (ClusterConnectionManager) clusterCM\n\tclCM.serviceStopped(serv)\n\treturn \"serviceStopped called for IOService for key: ${key}.\"\n}\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/UpdateConfig.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n\nUpdate config.tdsl configuration\n\nAS:Description: Update config.tdsl configuration\nAS:CommandId: update-config-tdsl\nAS:Component: message-router\nAS:Group: Configuration\n*/\n\npackage tigase.admin\n\nimport groovy.transform.CompileStatic\nimport tigase.component.DSLBeanConfigurator\nimport tigase.conf.ConfigReader\nimport tigase.conf.ConfigWriter\nimport tigase.kernel.core.Kernel\nimport tigase.server.Command\nimport tigase.server.Iq\nimport tigase.server.Packet\n\nKernel kernel = (Kernel) kernel;\nIq p = (Iq) packet\nSet<String> admins = (Set<String>) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\n@CompileStatic\nclass ConfigReloadTask\n\t\textends Thread {\n\n\tprivate final Map<String, Object> props;\n\tprivate final String configStr;\n\tprivate final DSLBeanConfigurator beanConfigurator;\n\t\n\tConfigReloadTask(DSLBeanConfigurator beanConfigurator, Map<String, Object> props, String configStr) {\n\t\tthis.props = props;\n\t\tthis.configStr = configStr;\n\t\tthis.beanConfigurator = beanConfigurator;\n\t}\n\n\tvoid run() {\n\t\tThread.sleep(5000);\n\n\t\tSystem.out.println(\"Updating configuration at \" + new Date());\n\t\tMap<String, Object> oldConfig = beanConfigurator.getConfigHolder().getProperties();\n\t\ttry {\n\t\t\tbeanConfigurator.getConfigHolder().setProperties(props);\n\t\t\tbeanConfigurator.configurationChanged();\n\n\t\t\tString filepath = beanConfigurator.\n\t\t\t\t\tgetConfigHolder().\n\t\t\t\t\tgetConfigFilePath().\n\t\t\t\t\ttoString();\n\n\t\t\tif (new File(filepath + \".backup\").exists()) {\n\t\t\t\tnew File(filepath + \".backup\").delete();\n\t\t\t}\n\t\t\tnew File(filepath).renameTo(filepath + \".backup\");\n\t\t\tnew File(filepath + \".new\").write(configStr);\n\n\t\t\t// Dumping new configuration\n\t\t\tSystem.out.println(\"Configuration updated and saved!\");\n\t\t\tFile f = new File(\"etc/config-dump.properties\");\n\t\t\tif (f.exists()) {\n\t\t\t\tf.delete();\n\t\t\t}\n\t\t\tbeanConfigurator.dumpConfiguration(f);\n\t\t} catch (Throwable ex) {\n\t\t\tSystem.out.println(\"Could not apply configuration:\");\n\t\t\tex.printStackTrace();\n\n\t\t\tbeanConfigurator.getConfigHolder().setProperties(oldConfig);\n\t\t\tbeanConfigurator.configurationChanged();\n\t\t}\n\t}\n}\n\n@CompileStatic\nPacket process(Kernel kernel, Iq p, boolean isServiceAdmin) {\n\tif (!isServiceAdmin) {\n\t\tPacket result = p.commandResult(Command.DataType.result);\n\t\tCommand.addTextField(result, \"Error\", \"You are not service administrator\");\n\t\treturn result;\n\t}\n\n\tString newConfig =  Arrays.asList(Command.getFieldValues(p, \"Config\") ?: new String[0]).join(\"\\n\");\n\tif (!newConfig.trim().isEmpty()) {\n\t\ttry {\n\t\t\tMap<String, Object> newConfigMap = new ConfigReader().read(new StringReader(newConfig));\n\n\t\t\tnew ConfigReloadTask(kernel.getInstance(DSLBeanConfigurator.class), newConfigMap, newConfig).start();\n\n\t\t\tPacket result = p.commandResult(Command.DataType.result);\n\t\t\tCommand.addTextField(result, \"Note\", \"Config initially validated and scheduled to be applied.\");\n\t\t\treturn result;\n\t\t} catch (ConfigReader.UnsupportedOperationException e) {\n\t\t\tPacket result = p.commandResult(Command.DataType.form);\n\t\t\tCommand.addTextField(result, \"Error\", e.getMessage() + \" at line \" + e.getLine() + \" position \" +\n\t\t\t\t\t\t\t\t\t\t e.getPosition() + \"\\nLine: \" + e.getLineContent());\n\t\t\tCommand.addFieldMultiValue(result, \"Config\", Command.getFieldValues(p, \"Config\") as List<String>)\n\t\t\treturn result;\n\t\t} catch (ConfigReader.ConfigException e) {\n\t\t\tPacket result = p.commandResult(Command.DataType.form);\n\t\t\tCommand.addTextField(result, \"Error\", \"There is an error in your config. Please fix it.\");\n\t\t\tCommand.addFieldMultiValue(result, \"Config\", Command.getFieldValues(p, \"Config\") as List<String>)\n\t\t\treturn result;\n\t\t}\n\t} else {\n\t\tPacket result = p.commandResult(Command.DataType.form);\n\t\tDSLBeanConfigurator configurator = kernel.getInstance(DSLBeanConfigurator.class);\n\t\tStringWriter writer = new StringWriter();\n\t\tnew ConfigWriter().write(writer, configurator.getConfigHolder().getProperties());\n\n\t\tCommand.addFieldMultiValue(result, \"Config\", Arrays.asList(writer.toString().split(\"\\n\")));\n\t\treturn result;\n\t}\n}\n\ntry {\n\treturn process(kernel, p, isServiceAdmin);\n} catch (Exception ex) {\n\tex.printStackTrace();\n\tthrow ex;\n}\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/UserDomainFilter.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\nAS:Description: Change user inter-domain communication permission.\nAS:CommandId: user-domain-perm\nAS:Component: sess-man\n*/\n\npackage tigase.admin\n\nimport tigase.db.UserNotFoundException\nimport tigase.db.UserRepository\nimport tigase.server.Command\nimport tigase.server.Iq\nimport tigase.vhosts.VHostManagerIfc\nimport tigase.vhosts.filter.CustomDomainFilter\nimport tigase.vhosts.filter.DomainFilterPolicy\nimport tigase.xmpp.impl.DomainFilter\n\ndef vhost_man = (VHostManagerIfc) vhostMan\n\ndef JID = \"jid\"\ndef FILTERING_POLICY = \"fiteringPolicy\"\ndef FILTERING_LIST = \"filteringList\"\n\ndef p = (Iq) packet\ndef jid = Command.getFieldValue(p, JID)\ndef domain = Command.getFieldValue(p, FILTERING_POLICY)\ndef domainList = Command.getFieldValue(p, FILTERING_LIST)\n\nif (jid == null || domain == null || (domain == DomainFilterPolicy.LIST.name() && domainList == null)) {\n\tdef res = (Iq) p.commandResult(Command.DataType.form);\n\tCommand.addFieldValue(res, JID, jid ?: \"\", \"jid-single\", \"User JID\")\n\tdef domainStr = [ ]\n\tDomainFilterPolicy.values().each { domainStr += it.name() }\n\tCommand.addFieldValue(res, FILTERING_POLICY, domain ?: domainStr[0], \"List of domains\",\n\t\t\t\t\t\t  (String[]) domainStr, (String[]) domainStr)\n\tCommand.addFieldValue(res, FILTERING_LIST, domainList ?: \"\", \"text-single\", \"Domains List\")\n\treturn res\n}\n\ndef bareJID = tigase.xmpp.jid.JID.jidInstanceNS(jid.toLowerCase()).getBareJID();\n\njid = bareJID.toString();\n\ndef repo = (UserRepository) userRepository\n\n\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\nif (!isAllowedForDomain.apply(bareJID.getDomain())) {\n\tdef result = p.commandResult(Command.DataType.result);\n\tCommand.addTextField(result, \"Error\", \"You do not have enough permissions to manage this domain\");\n\treturn result\n}\n\n\ntry {\n\tdef old_value = repo.getData(bareJID, null,\n\t\t\t\t\t\t\t\t DomainFilter.ALLOWED_DOMAINS_KEY, null)\n\tdef old_value_domains = \"\";\n\n\tif (DomainFilterPolicy.valuePoliciesWithDomainListStr().contains(old_value)) {\n\t\told_value_domains = repo.getData(bareJID, null, DomainFilter.ALLOWED_DOMAINS_LIST_KEY, null)\n\t}\n\n\tif (domainList != null) {\n\t\tdomainList = domainList.replaceAll(\"\\\\s\", \"\")\n\t}\n\tdef new_value = domain\n\tif (DomainFilterPolicy.valuePoliciesWithDomainListStr().contains(domain)) {\n\t\tif (DomainFilterPolicy.valueof(domain) == DomainFilterPolicy.CUSTOM) {\n\t\t\ttry {\n\t\t\t\tCustomDomainFilter.parseRules(domainList)\n\t\t\t} catch (Exception e) {\n\t\t\t\treturn \"Error parsing rules: \" + domainList\n\t\t\t}\n\t\t}\n\t\trepo.setData(bareJID, null, DomainFilter.ALLOWED_DOMAINS_LIST_KEY, domainList)\n\t}\n\trepo.setData(bareJID, null, DomainFilter.ALLOWED_DOMAINS_KEY, new_value)\n\n\treturn \"Changed an old value: $old_value (domains list: $old_value_domains) to a new value: $new_value (domains list: $domainList) for user: $jid\"\n} catch (e) {\n\tif (e in UserNotFoundException) {\n\t\treturn \"The user $jid was not found in the user repository\"\n\t} else {\n\t\treturn \"Unexpected error: \" + e\n\t}\n}\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/UserRosterManagement.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n\nUpdate user roster entry.\n\nAS:Description: Update user roster entry\nAS:CommandId: user-roster-management\nAS:Component: sess-man\n*/\n\npackage tigase.admin\n\nimport tigase.db.UserRepository\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.vhosts.VHostManagerIfc\nimport tigase.xmpp.XMPPSession\nimport tigase.xmpp.impl.roster.RosterAbstract\nimport tigase.xmpp.impl.roster.RosterElement\nimport tigase.xmpp.impl.roster.RosterFactory\nimport tigase.xmpp.jid.BareJID\nimport tigase.xmpp.jid.JID\n\nimport java.util.function.Function\n\nclass Field {\n\n\tString name;\n\tString label;\n\tString type;\n\tString defVal = \"\"\n}\n\nclass RosterChangesControler {\n\n\tUserRepository repository\n\tVHostManagerIfc vhost_man\n\tSet<BareJID> admins\n\n\tMap<String, XMPPSession> sessions\n\n\tFunction<String,Boolean> isAllowedForDomain\n\n\tField addOperation = new Field(name: \"addJid\", label: \"Add\")\n\tField removeOperation = new Field(name: \"removeJid\", label: \"Remove\")\n\tList<Field> operationTypes = [ addOperation, removeOperation ]\n\n\tField subscriptionNone = new Field(name: \"none\", label: \"None\")\n\tField subscriptionFrom = new Field(name: \"from\", label: \"From\")\n\tField subscriptionTo = new Field(name: \"to\", label: \"To\")\n\tField subscriptionBoth = new Field(name: \"both\", label: \"Both\")\n\tList<Field> subscriptionTypes = [ subscriptionNone, subscriptionFrom, subscriptionTo, subscriptionBoth ]\n\n\tField ownerJid = new Field(name: \"rosterOwnerJID\", label: \"Roster owner JID\", type: \"jid-single\")\n\tField jidToChange = new Field(name: \"jidToManipulate\", label: \"JID to manipulate\", type: \"jid-single\")\n\tField groups = new Field(name: \"groups\", label: \"Comma separated groups\", type: \"text-single\")\n\tField operationType = new Field(name: \"operationType\", label: \"Operation type\",\n\t\t\t\t\t\t\t\t\tdefVal: addOperation.name)\n\tField subscriptionType = new Field(name: \"subscriptionType\",\n\t\t\t\t\t\t\t\t\t   label: \"Subscription type\", defVal: subscriptionBoth.name)\n\tList<Field> formFields = [ ownerJid, jidToChange, groups, operationType, subscriptionType ]\n\n\tdef addField(Packet form, Field field, List<Field> listFields = [ ]) {\n\t\tif (listFields != null && listFields.size() == 0) {\n\t\t\tCommand.addFieldValue(form, field.name, field.defVal,\n\t\t\t\t\t\t\t\t  field.type, field.label)\n\t\t} else {\n\t\t\tdef listValues = (listFields.collect { it.name }).toArray(new String[0])\n\t\t\tdef listLabels = (listFields.collect { it.label }).toArray(new String[0])\n\t\t\tCommand.addFieldValue(form, field.name, field.defVal, field.label, listLabels, listValues)\n\t\t}\n\t}\n\n\tdef getFieldValue(Packet form, Field field) {\n\t\treturn Command.getFieldValue(form, field.name)\n\t}\n\n\tdef processPacket(Packet p) {\n\t\tif ((formFields.find { it.name != groups.name && Command.getFieldValue(p, it.name) == null }) == null) {\n\t\t\tString ownerJidStr = getFieldValue(p, ownerJid)\n\t\t\tString jidToManipulate = getFieldValue(p, jidToChange)\n\t\t\tString[] groups = (getFieldValue(p, groups) ?: \"\").split(\",\")\n\t\t\tString operationTypeStr = getFieldValue(p, operationType)\n\t\t\tString subscriptionTypeStr = getFieldValue(p, subscriptionType)\n\n\t\t\tPacket result = p.commandResult(Command.DataType.result)\n\n\t\t\tBareJID stanzaFromBare = p.getStanzaFrom().getBareJID();\n\n\t\t\tJID jidRosterOwnerJid = JID.jidInstanceNS(ownerJidStr);\n\t\t\tJID jidRosterItemJid = JID.jidInstanceNS(jidToManipulate);\n\n\t\t\tif (!isAllowedForDomain.apply(jidRosterOwnerJid.getDomain())) {\n\n\t\t\t\t//if ( !(isServiceAdmin || (vhost != null && (vhost.isOwner(stanzaFromBare.toString()) || vhost.isAdmin(stanzaFromBare.toString())))) ) {\n\t\t\t\tCommand.addTextField(result, \"Error\",\n\t\t\t\t\t\t\t\t\t \"You do not have enough permissions to modify roster of \" + ownerJidStr);\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tif (!isAllowedForDomain.apply(jidRosterItemJid.getDomain())) {\n\n\t\t\t\t//if ( !(isServiceAdmin || (vhost != null && (vhost.isOwner(stanzaFromBare.toString()) || vhost.isAdmin(stanzaFromBare.toString())))) ) {\n\n\t\t\t\tCommand.addTextField(result, \"Error\",\n\t\t\t\t\t\t\t\t\t \"You do not have enough permissions to modify roster of \" + jidToManipulate);\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tQueue<Packet> results;\n\t\t\tif (operationTypeStr == addOperation.name) {\n\t\t\t\tRosterElement item = new RosterElement(jidRosterItemJid, jidRosterItemJid.toString(), groups);\n\t\t\t\titem.setSubscription(subscription(subscriptionTypeStr));\n\t\t\t\tresults = RosterFactory.getRosterImplementation(true).addJidToRoster(repository, sessions.get(jidRosterOwnerJid.getBareJID()), jidRosterOwnerJid.getBareJID(), item);\n\t\t\t} else {\n\t\t\t\tresults = RosterFactory.getRosterImplementation(true).removeJidFromRoster(repository, sessions.get(jidRosterOwnerJid.getBareJID()), jidRosterOwnerJid.getBareJID(), jidRosterItemJid);\n\t\t\t}\n\n\t\t\tCommand.addTextField(result, \"Note\", \"Operation successful\");\n\t\t\tresults.add(result)\n\t\t\treturn results\n\t\t} else {\n\t\t\tPacket result = p.commandResult(Command.DataType.form)\n\t\t\taddField(result, ownerJid)\n\t\t\taddField(result, jidToChange)\n\t\t\taddField(result, groups)\n\t\t\taddField(result, operationType, operationTypes)\n\t\t\taddField(result, subscriptionType, subscriptionTypes)\n\t\t\treturn result\n\t\t}\n\t}\n\n\tdef subscription(String str) {\n\t\treturn RosterAbstract.SubscriptionType.valueOf(str)\n\t}\n\n}\n\ndef changesControler = new RosterChangesControler(repository: userRepository,\n\t\t\t\t\t\t\t\t\t\t\t\t  admins: adminsSet,\n\t\t\t\t\t\t\t\t\t\t\t\t  vhost_man: vhostMan,\n\t\t\t\t\t\t\t\t\t\t\t\t  sessions: userSessions,\n\t\t\t\t\t\t\t\t\t\t\t\t  isAllowedForDomain: (Function<String, Boolean>) isAllowedForDomain)\nchangesControler.processPacket((Packet) packet)"
  },
  {
    "path": "src/main/groovy/tigase/admin/UserRosterManagementExt.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n\nUpdate user roster entry, extended version.\nIf both given JIDs are local, rosters for both users are updated accordingly.\n\nAS:Description: Update user roster entry, extended version.\nAS:CommandId: user-roster-management-ext\nAS:Component: sess-man\n */\n\npackage tigase.admin\n\nimport tigase.cluster.strategy.ClusteringStrategyIfc\nimport tigase.db.UserRepository\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.server.Permissions\nimport tigase.vhosts.VHostManagerIfc\nimport tigase.xml.Element\nimport tigase.xmpp.XMPPSession\nimport tigase.xmpp.impl.roster.RosterAbstract\nimport tigase.xmpp.impl.roster.RosterElement\nimport tigase.xmpp.impl.roster.RosterFactory\nimport tigase.xmpp.impl.roster.RosterFlat\nimport tigase.xmpp.jid.BareJID\nimport tigase.xmpp.jid.JID\n\ntry {\n\tdef ROSTER_OWNER_JID = \"roster-owner-jid\"\n\tdef ROSTER_OWNER_PRESENCE = \"roster-owner-presence\"\n\tdef ROSTER_OWNER_NAME = \"roster-owner-name\"\n\tdef ROSTER_OWNER_GROUPS = \"roster-owner-groups\"\n\tdef ROSTER_ITEM_JID = \"roster-item-jid\"\n\tdef ROSTER_ITEM_NAME = \"roster-item-name\"\n\tdef ROSTER_ITEM_GROUPS = \"roster-item-groups\"\n\tdef ROSTER_ITEM_SUBSCR = \"roster-item-subscr\"\n\tdef ROSTER_ACTION = \"roster-action\"\n\tdef NOTIFY_CLUSTER = \"notify-cluster\"\n\n\tdef UPDATE = \"update\"\n\tdef REMOVE = \"remove\"\n\tdef UPDATE_EXT = \"update-ext\"\n\tdef REMOVE_EXT = \"remove-ext\"\n\tdef subscriptions = [ \"both\", \"from\", \"to\", \"none\" ]\n\tdef actions = [ UPDATE, REMOVE, UPDATE_EXT, REMOVE_EXT ]\n\tdef actions_descr = [ \"Add/Update item\", \"Remove item\", \"Add/Update both rosters\", \"Remove from both rosters\" ]\n\n\tQueue results = new LinkedList()\n\tdef p = (Packet) packet\n\tdef repository = (UserRepository) userRepository\n\tdef sessions = (Map<BareJID, XMPPSession>) userSessions\n\tdef vhost_man = (VHostManagerIfc) vhostMan\n\tdef admins = (Set) adminsSet\n\n\tdef stanzaFromBare = p.getStanzaFrom().getBareJID();\n\tdef isServiceAdmin = admins.contains(stanzaFromBare);\n\n\tdef rosterOwnerJid = Command.getFieldValue(packet, ROSTER_OWNER_JID)\n\tdef rosterOwnerName = Command.getFieldValue(packet, ROSTER_OWNER_NAME)\n\tdef rosterOwnerGroups = Command.getFieldValue(packet, ROSTER_OWNER_GROUPS)\n\tdef rosterItemJid = Command.getFieldValue(packet, ROSTER_ITEM_JID)\n\tdef rosterItemName = Command.getFieldValue(packet, ROSTER_ITEM_NAME)\n\tdef rosterItemGroups = Command.getFieldValue(packet, ROSTER_ITEM_GROUPS)\n\tdef rosterItemSubscr = Command.getFieldValue(packet, ROSTER_ITEM_SUBSCR)\n\tdef rosterAction = Command.getFieldValue(packet, ROSTER_ACTION)\n\tboolean clusterMode = Boolean.valueOf(System.getProperty(\"cluster-mode\", false.toString()));\n\tboolean notifyCluster = Boolean.valueOf(Command.getFieldValue(packet, NOTIFY_CLUSTER))\n\n\tif (rosterOwnerJid == null || rosterItemJid == null || rosterItemSubscr == null || rosterAction == null) {\n\t\tdef res = p.commandResult(Command.DataType.form);\n\t\tCommand.addFieldValue(res, ROSTER_OWNER_JID, rosterOwnerJid ?: \"\", \"jid-single\", \"Roster owner JID\")\n\t\tCommand.addFieldValue(res, ROSTER_OWNER_NAME, rosterOwnerName ?: \"\", \"text-single\", \"Roster owner name\")\n\t\tCommand.addFieldValue(res, ROSTER_OWNER_GROUPS, rosterOwnerGroups ?: \"\", \"text-single\",\n\t\t\t\t\t\t\t  \"Comma separated list of owner groups\")\n\t\tCommand.addFieldValue(res, ROSTER_ITEM_JID, rosterItemJid ?: \"\", \"jid-single\", \"Roster item JID\")\n\t\tCommand.addFieldValue(res, ROSTER_ITEM_NAME, rosterItemName ?: \"\", \"text-single\", \"Roster item name\")\n\t\tCommand.addFieldValue(res, ROSTER_ITEM_GROUPS, rosterItemGroups ?: \"\", \"text-single\",\n\t\t\t\t\t\t\t  \"Comma separated list of item groups\")\n\t\tCommand.addFieldValue(res, ROSTER_ITEM_SUBSCR, subscriptions[0], \"Roster item Subscription\",\n\t\t\t\t\t\t\t  (String[]) subscriptions, (String[]) subscriptions)\n\t\tCommand.addFieldValue(res, ROSTER_ACTION, actions[0], \"Action\", (String[]) actions_descr, (String[]) actions)\n\t\tif (clusterMode) {\n\t\t\tCommand.addHiddenField(res, NOTIFY_CLUSTER, true.toString())\n\t\t}\n\t\treturn res\n\t}\n\n\tif (clusterMode && notifyCluster) {\n\t\tif (null != clusterStrategy) {\n\t\t\tdef cluster = (ClusteringStrategyIfc) clusterStrategy\n\t\t\tList<JID> cl_conns = cluster.getNodesConnected()\n\t\t\tif (cl_conns && cl_conns.size() > 0) {\n\t\t\t\tcl_conns.each { node ->\n\n\t\t\t\t\tdef forward = p.copyElementOnly();\n\t\t\t\t\tCommand.removeFieldValue(forward, NOTIFY_CLUSTER)\n\t\t\t\t\tCommand.addHiddenField(forward, NOTIFY_CLUSTER, false.toString())\n\t\t\t\t\tforward.setPacketTo(node);\n\t\t\t\t\tforward.setPermissions(Permissions.ADMIN);\n\n\t\t\t\t\tresults.offer(forward)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n\tdef remove_item = rosterAction == REMOVE || rosterAction == REMOVE_EXT\n\n\tdef updateRoster = { jid, i_jid, i_name, i_groups, i_subscr, i_original_node ->\n\n\t\tdef sess = sessions == null ? null : sessions.get(jid.getBareJID());\n\t\tdef conn = (sess != null && sess.getActiveResourcesSize() > 0) ? sess.getActiveResources().get(0) : null;\n\t\tif (conn) {\n\t\t\t// Update online\n\t\t\tRosterAbstract rosterUtil = RosterFactory.getRosterImplementation(true)\n\t\t\tElement item = new Element(\"item\",\n\t\t\t\t\t\t\t\t\t   (String[]) [ \"jid\", \"subscription\" ], (String[]) [ i_jid, \"remove\" ])\n\t\t\tif (remove_item) {\n\t\t\t\trosterUtil.removeBuddy(conn, i_jid)\n\t\t\t} else {\n\t\t\t\trosterUtil.addBuddy(conn, i_jid, i_name ?: i_jid.getLocalpart(), i_groups ? i_groups.split(\",\") : null, RosterAbstract.SubscriptionType.valueOf(i_subscr), null)\n\t\t\t\titem = rosterUtil.getBuddyItem(conn, i_jid)\n\t\t\t}\n\t\t\trosterUtil.updateBuddyChange(conn, results, item)\n\t\t} else if (i_original_node) {\n\t\t\t// We need to synchronize on some object (ie. on UserRepository instance) to fix issue with\n\t\t\t// race condition when we modify roster of user which is offline\n\t\t\t// Is there a better object to use for synchronization?\n\t\t\tsynchronized (repository) {\n\t\t\t\t// Update offline and only on original node\n\t\t\t\tString rosterStr = repository.getData(jid.getBareJID(), null, RosterAbstract.ROSTER, null) ?: \"\"\n\t\t\t\tMap<BareJID, RosterElement> roster = new LinkedHashMap<BareJID, RosterElement>()\n\t\t\t\tRosterFlat.parseRosterUtil(rosterStr, roster, null)\n\t\t\t\tif (remove_item) {\n\t\t\t\t\troster.remove(i_jid.getBareJID())\n\t\t\t\t} else {\n\t\t\t\t\tRosterElement rel = new RosterElement(i_jid, i_name, i_groups ? i_groups.split(\",\") : null)\n\t\t\t\t\trel.setSubscription(RosterAbstract.SubscriptionType.valueOf(i_subscr))\n\t\t\t\t\trel.setPersistent(true);\n\t\t\t\t\troster.put(i_jid, rel)\n\t\t\t\t}\n\t\t\t\tStringBuilder sb = new StringBuilder(200)\n\t\t\t\tfor (RosterElement relem : roster.values()) {\n\t\t\t\t\tsb.append(relem.getRosterElement().toString())\n\t\t\t\t}\n\t\t\t\trepository.setData(jid.getBareJID(), null, RosterAbstract.ROSTER, sb.toString());\n\t\t\t}\n\t\t}\n\t}\n\n\tdef jidRosterOwnerJid = JID.jidInstanceNS(rosterOwnerJid);\n\tdef jidRosterItemJid = JID.jidInstanceNS(rosterItemJid);\n\n\tPacket result = p.commandResult(Command.DataType.result)\n\tdef vhost = vhost_man.getVHostItem(jidRosterOwnerJid.getDomain());\n\tif (vhost == null || (!isAllowedForDomain.apply(jidRosterOwnerJid.getDomain()))) {\n\t\tCommand.addTextField(result, \"Error\",\n\t\t\t\t\t\t\t \"You do not have enough permissions to modify roster of \" + rosterOwnerJid);\n\t\tresults.add(result);\n\t\treturn results;\n\t}\n\n\tupdateRoster(jidRosterOwnerJid, jidRosterItemJid, rosterItemName, rosterItemGroups, rosterItemSubscr, notifyCluster)\n\n\tElement pres;\n\tif (rosterAction == UPDATE_EXT || rosterAction == REMOVE_EXT) {\n\t\tdef subscr = rosterItemSubscr;\n\t\tswitch (rosterItemSubscr) {\n\t\t\tcase \"to\": subscr = \"from\"; break;\n\t\t\tcase \"from\": subscr = \"to\"; break;\n\t\t}\n\n\t\tvhost = vhost_man.getVHostItem(jidRosterItemJid.getDomain());\n\t\tif (vhost == null || (!isAllowedForDomain.apply(jidRosterItemJid.getDomain()))) {\n\t\t\tCommand.addTextField(result, \"Error\",\n\t\t\t\t\t\t\t\t \"You do not have enough permissions to modify roster of \" + rosterItemJid);\n\t\t\tresults.add(result);\n\t\t\treturn results;\n\t\t}\n\n\t\tupdateRoster(jidRosterItemJid, jidRosterOwnerJid, rosterOwnerName, rosterOwnerGroups, subscr, notifyCluster)\n\n\t\tif (!remove_item) {\n\t\t\tpres = new Element(\"presence\", (String[]) [ \"from\", \"to\", \"type\" ],\n\t\t\t\t\t\t\t   (String[]) [ rosterOwnerJid, rosterItemJid, \"probe\" ])\n\t\t\tresults.offer(Packet.packetInstance(pres))\n\t\t}\n\t}\n\tif (!remove_item) {\n\t\tpres = new Element(\"presence\", (String[]) [ \"from\", \"to\", \"type\" ],\n\t\t\t\t\t\t   (String[]) [ rosterItemJid, rosterOwnerJid, \"probe\" ])\n\t\tresults.offer(Packet.packetInstance(pres))\n\t}\n\n\tCommand.addTextField(result, \"Note\", \"Operation successful\");\n\tresults.add(result)\n\n\t//return results\n\treturn (Queue) results\n\n} catch (Exception ex) {\n\tex.printStackTrace();\n}"
  },
  {
    "path": "src/main/groovy/tigase/admin/UserStatistics.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n Obtaining User Statistics as described in in XEP-0133:\n http://xmpp.org/extensions/xep-0133.html#get-user-stats\n AS:Description: Get User Statistics\n AS:CommandId: http://jabber.org/protocol/admin#user-stats\n AS:Component: sess-man\n AS:Group: Statistics\n */\n\npackage tigase.admin\n\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.vhosts.VHostItem\nimport tigase.vhosts.VHostManagerIfc\nimport tigase.xmpp.XMPPResourceConnection\nimport tigase.xmpp.XMPPSession\nimport tigase.xmpp.jid.BareJID\n\ndef JID = \"accountjid\"\n\ndef p = (Packet) packet\ndef sessions = (Map<BareJID, XMPPSession>) userSessions\ndef vhost_man = (VHostManagerIfc) vhostMan\ndef admins = (Set) adminsSet\ndef stanzaFromBare = p.getStanzaFrom().getBareJID()\ndef isServiceAdmin = admins.contains(stanzaFromBare)\n\ndef userJid = Command.getFieldValue(packet, JID)\n\nif (userJid == null) {\n\tdef result = p.commandResult(Command.DataType.form);\n\n\tCommand.addTitle(result, \"Get User Statistics\")\n\tCommand.addInstructions(result, \"Fill out this form to gather user statistics.\")\n\n\tCommand.addFieldValue(result, \"FORM_TYPE\", \"http://jabber.org/protocol/admin\", \"hidden\")\n\tCommand.addFieldValue(result, JID, userJid ?: \"\", \"jid-single\", \"The Jabber ID for statistics\")\n\n\treturn result\n}\n\ndef bareJID = BareJID.bareJIDInstance(userJid)\nVHostItem vhost = vhost_man.getVHostItem(bareJID.getDomain())\ndef result = p.commandResult(Command.DataType.result)\n\nif (vhost != null && isAllowedForDomain.apply(bareJID.getDomain())) {\n\tXMPPSession session = sessions.get(BareJID.bareJIDInstanceNS(userJid))\n\n\tif (session == null) {\n\t\treturn \"There is no user's ${userJid} active session on the server\"\n\t} else {\n\t\tList<XMPPResourceConnection> conns = session.getActiveResources()\n\t\tString conns_str = \"Connections: \"\n\t\tfor (XMPPResourceConnection con : conns) {\n\t\t\tconns_str += con.toString() + \"###\\n\"\n\t\t}\n\t\treturn \"There is ${conns?.size()} active user's ${userJid} sessions, packets: ${session.getPacketsCounter()}\\n\" +\n\t\t\t\t\"user connections:\\n\" + conns_str\n\t}\n} else {\n\tCommand.addTextField(result, \"Error\",\n\t\t\t\t\t\t \"You do not have enough permissions to obtain statistics for user in this domain.\");\n}\n\nreturn result\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/example_HelloWorld.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\nExample Hello World admin script for the script development guide.\n\nAS:Description: [example] Hello World Script.\nAS:CommandId: hello\nAS:Component: sess-man\nAS:Group: Example scripts\n*/\n\npackage tigase.admin\n\nimport tigase.server.Command\nimport tigase.server.Packet\n\ndef p = (Packet) packet\n\ndef name = Command.getFieldValue(packet, \"name\")\n\nif (name == null) {\n\tdef res = p.commandResult(Command.DataType.form)\n\tCommand.addTitle(res, \"Hello World Script\")\n\tCommand.addInstructions(res, \"Please provide some details\")\n\tCommand.addFieldValue(res, \"name\", name ?: \"\", \"text-single\",\n\t\t\t\t\t\t  \"Your name\")\n\treturn res\n}\n\ndef res = p.commandResult(Command.DataType.result)\nCommand.addTitle(res, \"Hello World Script\")\nCommand.addInstructions(res, \"Hello ${name}, how are you?\")\n\nreturn res\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/example_TigaseScriptingGuide.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n\nThis is an example script for Tigase scripting support.\n\nAS:Description: [example] Tigase scripting guide\nAS:CommandId: groovy-example\nAS:Component: sess-man\nAS:Group: Example scripts\n*/\n\npackage tigase.admin\n\nimport tigase.server.Command\nimport tigase.server.Packet\n\nPacket p = (Packet) packet\nnum1 = Command.getFieldValue(p, \"num1\")\nnum2 = Command.getFieldValue(p, \"num2\")\n\nif (num1 == null || num2 == null) {\n\tPacket res = p.commandResult(Command.DataType.form)\n\tCommand.addTextField(res, \"Note\", \"This is Groovy script!\")\n\tCommand.addFieldValue(res, \"num1\", \"\", \"text-single\")\n\tCommand.addFieldValue(res, \"num2\", \"\", \"text-single\")\n\treturn res\n}\n\nreturn num1 + num2\n"
  },
  {
    "path": "src/main/groovy/tigase/admin/template.html",
    "content": "<!--\n\n    Tigase XMPP Server - The instant messaging server\n    Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published by\n    the Free Software Foundation, version 3 of the License.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program. Look for COPYING file in the top folder.\n    If not, see http://www.gnu.org/licenses/.\n\n-->\n<html>\n<head>\n    <title>Tigase XMPP Server - Admin console</title>\n    <style>\nbody {\n    margin: 0px;\n}\n.root {\n    margin: 0px;\n    padding: 0px;\n    width: 100%;\n}\n.header {\n    height: 100px;\n    background-color: #306799;\n    color: white;\n    box-shadow: 0px 3px 3px 0px lightgray;\n}\n.header > div {\n\tfloat: left;\n}\n.header > div > span.title {\npadding-left: 30px;\ndisplay: block;\nwidth: 300px;\npadding-top: 30px;\n/*float: left;*/\nfont-size: 1.6em;\n}\n.header > div > span.subtitle {\n/*float: left;*/\ndisplay: block;\nmargin: 5px 0px;\nfont-size: 0.9em;\n/*clear: left;*/\n}\n.content {\n    min-height: 600px;\n}\n.content > form > h3, .content > form > div {\n\tclear: both;\n}\n.content > form > * {\n\tdisplay: block;\n\tfloat: left;\n\tpadding: 3px;\n}\n.footer {\n    font-size: 0.9em;\n    text-align: center;\n\tclear: both;\n}\nul {\n\tpadding: 0px 0px;\n\tlist-style: none;\n}\nli {\n\tpadding: 0px 1em;\n}\nli > ul > li {\n\tmargin: 0px -1em;\n\tpadding: 0px 2em;\n}\nli > a {\n\tcolor: darkblue;\n\tfont-weight: bold;\n\ttext-decoration: none;\n}\ntable {\n\tclear: both;\n\tborder-spacing: 0px;\n\t\n}\nth, td {\n\tpadding: 5px 10px;\t\n\tborder: 1px solid gray;\n}\nlabel {\n\tclear: both;\n\tmin-width: 220px;\n}\ninput {\n\tmin-width: 300px;\n}\ninput.submit {\n\tclear: both;\n}\n\n    </style>\n    <% imports.each {\n    if (it.type == 'css' && it.src) { %>\n    <link rel='stylesheet' type='text/css' href='${src}'></link>\n    <% } else if (it.type == 'css' && it.content) { %>\n    <style>.test { ${it.content} }</style>\n    <% } else if (it.type == 'script') { %>\n    <script src=\"${it.src}\"></script>\n    <% } %>\n    <% } %>\n</head>\n<body>\n<div class=\"root\">\n    <div class=\"header\">\n        <img style=\"width: 64px;\nheight: 64px;\npadding: 15 15;\nfloat: left;\nbackground: radial-gradient(white 5%, rgba(255,255,255,0) 72%);\"\n             src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAtCAYAAAAeA21aAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAASrAAAEqwEnCx5kAAAX6ElEQVRo3s2aeZRcV3Xuf3euea7u6nmSWqMl2ZZlbBnHAxh7AZ4w2OAHhscQeHmQNxHg5QUMyWJcZIUXQhheICTBjMGBZbAx2MY2kmVLsuW2ZHWrWz1XdVd3zXPVHc77o0styZKNnaxAdq27Vvequvec79vf3meffa7E79j2fuibAJ22I/YKxCW6ptywtT+2UVUVt6EqUtO0EQgUWUaWJVGtm0JT5FYyW16YSxdOeAxtTJalE4rMbKZYfyyZKTvFn370Xz0f6XcFfPcHvk7DtHSPS78x4NbfNdAZvGrrQNyTqzRQZBnbdjAMDY9LQ1FkipUmrZaJEAJdU3EcgWXb9MUDZIo15leKybl06adLufLfvGpzz/MtyxH/8NGb/+MRsP09f4sqy163rl0Y8Onv7I353zyYCAcMl47mMuiM+XEkGdNykKS1KUkSCAFCCABURcZQFSrVOqVqg3Kljl9XqFQbTCxkp1LZ8lddmvoNR4jS/Z+58/dPwOUf/CaqIkulWjMSC3hut4Vz6Whv9A198UDEMDQ0r5toNIihqyBJLzkJCRCwToojBBJQqzepl2vYjSYnUzlzfD7zo5Zpf39sJv2Ix1Drl23psR0hnB98/PbfHQHb3vMVhEDzubQBkN7UFw/c2dcR2NgXD7hMB3xBLx6fB5/XhbTm7hf1OoBl20zNpClWGvR3RwgHPbhdOiAhhMC2HUqlKrlMAcl2xPRSPl+utxZzpVqj2jAfLNaaP+7vCJ6UFbX000+++d+PAO/rP0P1Zx9jz3/9f5t9Lv1OQ5Vv3zbYMRINeuREPECxaePyuAgHfcjy2uSFgEq1TqFcp9FoUSjX6YgGGOiJIrVVUSjX+KefHCBfquF16/QlInR1BEnEgwx0R1FVBUmCfLFKq1rHpcis5MpkizVcmuIs5crlVK7yQKnafNyy7Qee/PJ7T3L7+yGVgMfv/rcTsPGuv0aRZcXn0gaDPtddnWHvbd3RwKZY0C07kozhceFIMn6fi2qlwVK2RKtlsZItU6k3EY6DaTs4jkM44OHKS0ZJxEPrz7cdh2eOzXP42ByWZYMQeA0Nl6ERDHjYONRJf08MXVMRQlCuNGi0TDyagtlskc2VCXl1ZlJ5cy5dnGi0rGcmFrNfT4R9E8PdkcxqoSr+VQRseueX+cib9vCth44OybL81q6I711DidCQ160rusuF7nExvbBKrdFCUxVyhSq1pgkCfG4NQ9ewHAchoDceYKAjiNA0OjtDnIoAIQSStKYW07QRgGyZbIj7qFQaHJ1dIVdu4A74GBroOCukhADLdpCBSqVGo9ZEcWxSmZJTrbfqCyvFZ1dLtS95Df2Xr4iAXR/4Gv6oi9xyLeJ363f2xwPvGOmOXCzLkjSzlEd16Vxz+VY8Lp3HD51gYibNpRcMsqk7TH/Ux1KuQqNl4jI0kCTiATcD8QAL6QKH5vMMDnSuJzloJ78zxi8Wqyyn8yiyhK4qjPTFKDUtXG5jnYDzmeM4FMt1VOFQLFZp1pvky/Xi+HzmO8rLBb/jvV/FshyvIZTr/R7jr0Z7Iu+6cudAv5Bl6ZEjs6RyFa6+dBNd8RCSBF63weRMmucmU8iqwnBXmN0jCXYNd7C5N8rmnjAbusIA/GT/CXSvm6Dfc9aYL4SkaSqBgIdA0Is/6EXICrquvSR4IQSyLONx62iGjsvjRkgShiy5hHAuVH8b8Js/8UNURZZnlnPDca/nYz0x/02jvdGox21gqjpN2ca0bDb2xelNhHHaGo6GfPzBnk38+qkJ9h2ZYWG5wCWbe9i5IUEi6MGxbVYKVY4v5GjICgPxYDs5inVAjuOcIW0JRZFRFPkF3hXIcnsplc4Ezvp9lu0ghKDZsphLZYmHfQRDPopTS+pLKmDHe7/K7HJedRzxvuGu0Jd3b+q+JugxvLm6xcBQF+Gwj+NTKU4uZNi1uY+hvvg6AZIkEQl5GeqNIUmwmC5wdGaZJ44uMJ7KMZup4A76cfs89CTCqIqy5i1JotmymF1YoVFv4fe7kSQJxxHUGy0URUaSJGzbYTGVpVlvIssSpWqTaqNFrX1Zpg3CwWpZlAplCvky0/OrpNN5CoUqsaAbx3F4UQXc+onvIwTKTLrwxyPdoT/fOhB3H5lKs1Ksctv1uwkFPGTzFcanl/F7DIbb4E957JQnw0Ev11y2hR2bekmmC5SrDQrlGvlKA5dLR9POmIKA1Gqep4/No8gyr969EVmWME2bI8fn0TWV7aM92LbNU2OzBFWJ112xiYblUKi12jXEWunk1lV8hoYQDtENcVy6SipTor8jSLVhcuD4IiWf6/wE7HjPV7nviSn1mosG/tslm7rv3tgTcT/8zAyLqyUu2zlMRzQAYq0ayxaqxMI+QsEXxO8ZcSlJEh3RAJ3xILSlKASobQFKElSqDfYdnuLEbJrB3hhX7hnF53FRa7R4/OAJZpMZ3nDVDizL5rGDJ1hZKfDJu65i22DHeR1oWjaKIiO31eMIQbw9R0NTifg9PPT0zLkE/NW9h2lKDX7wi2N/GPG7794x3Okdm10hna/iMjQGeqLomoLtCHKFKgII+91I8ksvKAJwbIEkcZbXhRAspHLsf2aalWyJLSNd7L1oBK/HRbZQ4TeHpxifXuLyXcMEfW6eeHqKp4/Nc8dV29g+GH/R8WpNC69LAxlKtSYuXUWRJSRJQlNlJAmy5frZBIy8/f8ibJOf75+8dUt/7O7doz3ehiRTM9cKlv6uCP3dEWxnLVnlSjUAQn43svTbV9QX/sS0bMbGFxmbWMDj0rn5NTvp6gyjqQor2RIP7nuek/MrbNvQzaahTh4+cJyxEyk29ES49dVbXjL7B70GANlijVK9xUBHkEKlQdDrot60qNRbdEV9pwnY+b6vEvG7+c7Dz23pjfk/tm0wHpPdBrGgj8KhKUDgcml43AaOEAjAshxAsPbfKzfLsgn63bzuim10RANoqgISrObKPPD4UWYWswz3xrhkxxD7j8zw7ESSLX0xPnz75QwmQi9rjLDfjdetU2+ZBL2utZxi20wlc9QaZnGdgKFEiGK1GY74XZ/aNZLY7Qv4iMRD1BsmuVK9LR2lvTOTEI7AaVdz2UKtvRy9fPBCCNwunQ0DZ8SwBPlijV/tP87MYoaBnhjXXbGNybkVxiYW2b2xi/9x22Vsewnpv9BkWUKRJYSQkNthupyrUG2YLOeqX5EBtr77K/zLD/ZLkiT998HO0K2xsA+X34NL1xBiDagiy4S8rrPkrGprhJRqzVfs/fPJt9m02Hd4ksnZNN3xIDe8ehsAR44vEA96eN8bLv6t4IUQlGpNMqUatuMAoKkKbkMDYKVQZXwxi8tQnVqjdVS95k++jSwkOl5/0YVdEd/btg93ykLXCfo9OEKgKjJ+j0GjadKy7LNBnKo8hEA+FQhnLYXnxv1L2bHJJM+dSNLTEeL6K7fj9Rjc/9hRGs0WH7rlUi7e2PWi91q2w4HjixycSBH2uTg2lyHoMRjpDhP0Ggig1jTRVYWJhSwHx5O/aFnOb1QJiXKzpYc9rj8e6Q6PNGxBXyy4DkJTFaIhH8vZEqVq4xxUEiArMqJNhyRJmKYFp0LmDEJeymr1JoePzRMJennD1TuIRfw8tP84k7NpbrtyCzddNoqqvHiMPXJklk/f8zi2EHz2Pddy5GSaX4/NMpXK4XPr9HUEcBwhlrKV6WS2/EAyU/6MpshJVZdlvLp2XU8scGO1YVI0qwwOy+sTlxUZt0sDJEqVOsVyjWDAuya1ah2A3s4Qkny6szO/lGN2McNF2/oJBbzr0nwxIiRgfHoJy7a5/optJOJBjk2mODK+yO7Rbt553a51CZ/PsqUa9+4bJxr00BPzMzadpjPiFbYTd8I+l12sNosHJ1L3larNZ31u7Tc3v/8th+/64kH41Q9R77/3oHT9rXtu6Yn5Q/uOLuDzuXnVhSPrRYoqywTbsS+1P7BWg69kyvg9Bjs396Eo8nr9bZo2lWKViCzI5Ur4Aj407fxqkCRIZ0qMjS+y98JhhvvjZApVHjs8yXAiyEfv2Esi4ntR8C3T5nuPHEOWJTb3x8RMKp99cGW6KkmS23HEpx8dm3tUArMv7p9362r53k/ewb2fvGP9fnXv9Tv3BD3GFYamUqg2qLYsZhZWGR1OrPfgGi0LCYiEvAQDbgSQK1YRQvCqXcN0xgKc0cmiOx7kN4dOYKgye/tjHJrNINxudF09a58vSRKtlsVTYzMEfG5Gh7twBBydSNJqmLzzlj0MJkJMJrMMdIQo1hoUK01GusPrRO5/foHn51bpDHut/c8v3nfF9v6vPfrsrGFo6g0DnaFvBb2u0rc/chNjL0KgbNtix3BXeGOhYdLdEaJaa3Lw6CyVagNJWksu5VoTTVMY6o2txboEpXKdS3cOcdG2AZT2RuaUabpKvWXz4KGTJMJeXru9F7nZwGyZZ4G3bIenj87RaplcuWcUt6GztFLg2FSSmy7fxNW7hlgpVHnyeJKWZfPLw9M8O51eB3/g+CLffeQoiYiPI9PpB2fThQ/98vD0A5qiPuQx9M+Ypl09yzPnMdXQlTcFvIYU74pieNwsZ0rMJrOk0gVGhxPrcu/tCLFxqLMdzzDQG0ORJWRZPkfaqqrQ3xXmwPEkj43NcfPezVx7QS8PH0thyfJ6Ly+br2A7DtddsQ2/z02jZXLo6Cz9MT9vvWYbiizxswOTRINuKvUWBydS3HntBQCk81W++cARWpZNplibWy3U7u6LBxdMy+H+z76tAlR+a+YF5OFEeG+1YeJyG4wOddLbGcJxBKnV4tphhGUjHIc9O4dwGfp6QtNUBbld+bwwrjVVoS8RodGyeHRsDtO2iQe9bOzwU6vU1+vGSNDDJTuH8fvcIGB8epnZxQz/6TU76Ir4OTCe5NCJFLtGEozNpFFkmU29UQAWVos8ezJNLOCh1jQNn1u7a3a5sMu27Y7d/+XrHTd87Duxt3zqh5vf+Kf3bLrz8/8Cu/7X+RUQ8BleXVexbIHXYzDYE2UmmcW214oI2xEM9MYYHUysg3/JDgygSBLxqB+vW2cymeNkMs/m/hhb+qIsF6oU6w3KTQtNU3EZGs2WxcJSjkcOjLNntIs9m7qpN01+sm+c3Zu66Qh5uefho1x74RB+z1qN7zE0PIbK83OrvP7SjQmXpr5/Q3fkDkWWzFrTQpHlVjpfIVdpHEpmK1+gJR0433yV17353XfHIj50jwuXrmFaNkcnU2wc7KA3EUHXVDoiftSXuaafOsgwdJV0psj8Up5NfVG2DsTRVIXBjiB+TcYlQyZbZmYhw7MTixw+NofP0PjgLXsYSoR5ajyFZTvccfX2tapTldk92oVLX6ve/R6D/o4g2XKDZ6aW8Lg0WVVkz2qx5lMV2edz60G3oQY39ca2FCqNgXh3+Puvv/0P7cMP3nO2AoA1b7d7CafO5/oSkXYPH/T2oC+noDmlArdLY/NwFyfnMzw2Ns9rLhom5HOhtfuDg4kQu4biVBsmlXqL2XSReNDD9qEOHCHoivjY3B/D59YRQnDZlt6zCiGXrvLai4e5bFsf2WKNw5NLPDezQrVhYjsO2VKNWMBDwGsw0h25cHG1dN1irnjfOSGQL9eFQJKC8QiItXV/+4YuomHfWQnUcdb28i8kQZynCySxlihHBzuZ3bDKwYkkvzh0ktuu3ILSzhuyJOFzG/jcBp1hGOmOnPXc4e4wtE+KhGD9QOWFjRa/W8fv1hlMhLj1is0IAbVmi3K9hRDwswOTVBut2InF7AWW7ZxDgNJ36U23dUV9HbFoAEVVkCWIhXwEAp51b0rA1Ewan9e1XiCdOQnbdjBN65zvNE3B73VzYnaFJ48vslqs4XPpqIqMI0S7MXEakGnZFCoN8uU6y7kKc6tF5ldKHJ9fZTFTolhpUqo1abbW9iS6qpxDiCRJ6JqK323g9xioqkyp2uT4fObZazcP/9K/43VMPP6j0wqYSxf/Ycdw5+eXVwr093fgdunt87c1N651WyVOzK+g6QqDvfFzPFGq1JlPZdm1tb+tlNOboc5YgL0XjfDoUyf43iNHefDQSUa6w0QDHjb1RdnQHUbXVJKZEghommvgnp9fXQs/TaFp2kKWJHRFxrQdaTgRojvmx+fRGekK0xHyvmh4DneFeezZOQY6g7f/6vj0twxNOXrm/JUtV71ZEnBVT9QXVjQNw9Da+x0JAaQzRQxNYTGVo1iuM9gbO2ewydk02XyFjYMdCAG5QmV9mTzVD+xNhPF7XQT9bhotm6lUniePJ3lyPIksQaZUY3whWzs2t7KQLzVW51aKqXrTPGlazs8R4ht+t/69RsvOza0UW8v5SnV8IdO0beFfylWkcq1FZ9h73s2SKkuUak2y5bp/tVDLjU1mfn3/wSlmnrh37ftffeHt+278s+9+qVSuf86RZJeqKRjtPgDASqZEq2mytT9KrlhlKZ2ntyuK3e7ZCyHI5SvYlk2t3sLQNZ6fSq21z3piCCFQFJmBnii9icjaplkIbEeQyZY4MZXCtB3zsefmH/Z79H/2Gtq933zgmUataTHQGXB2jiTMXKVuPvT5d/DBv37gh/uPLWpzKwUu3ti1YWG1/KGt/bFbFldLkWjAzeb+2DkEKIpMbzyAZdmSz61fXW419IZlrjcw1N1/9A1W8tW/dwQ3XLG9//psOk+sM7JetzeaJs8tZdnWH8dnaCSXssSiAfR2Y9MRAsu06Iv5KNea2I4A22ZmMUN/z+kJnUpkp04vFGWtSTrYGeDI1PJTjhD/ud40U//88becVlb7ApC+cBeA2b5oDHxtLF9pvP/IdDp54Uji/xSqDdm07LW22hlWqjaZTOZQFdmuNcy//+z7rmz+/InZ0wSFLrie4e5IM1OsTZi28+rBmD+eLVZxeww0VaVUqtHhNdAUae1sr2Hidrvw+9xr3SIB6ZU8m7rDVG2Badl0+deKlVDIf85Jzqn4K1XqiHqd5Uy5MZXM/4XX0B6/7y/exsu19OH72H7NW5xipb7k8+g3WpYTalk2lu2gKBKFSoOZ5TwHnl9kdrnAkZPpw8ls6bPH53LFh7/4jtMEZJ75OdP7fkzi4jemVou1Zwq15o5EwN1dr7ckZJl8uUbIrTK7XMiPz2d+4DW0rU3TUmLtJqZAsLJaJOozkDWdbLFG1FDQdZWaJfC1u7Nngrdtm9WVHNVKXUwkc//o82hf0FXFfO6hH7xsAgC2Xn0b/R3+4sxysSpJ0kW5ct3ftCyml/LMLheZSuaYWMyaE8ns4ZmlwgdkSRo/8OV3n50jTv0hyxKyI/afTOXeZZr2hy8Y6rhFESKgC4dyzSZTrO2rN62PJrNl/B79roXFVYaHEsiShKyp+D0GlfbOsa5LRIMe5sstTteGrB9xLafzOM0Wh04s/Spbqt8thKg9+pd3cc//fkX4+emfv403/ul3LLemfOvozMqBYrX54RPJbNdStoKuKkQCHrtlWvcFvMYPn/qb9yyd7xkvWDtC8Lqv0S9Pylt2Dr43Efb9z9HeaO/CavEXcyvFjyA4UW2Ye/o7At8d7okOd3ZFiceCjI0vEFJh22AHP3nqJHtHE6iKzPHVKju39GHbpxNmoVChkisyMb+69Oz0ylv9Hv3RX37u7a8M+Qvtgg9D0AOTSxKrJ8FxAbU2vEq7nHvqvLeed/G87iP/iFtX1FS2OlhrWj5JEotAZrgrguk4yPCmvnjgGwNd4bDu9aLqKmPPzXDDJSOU602qDZN8uUEVhT07BtcSI5BczmFXa8wt5auTqdwf3X/fQ9++7U038KNPnP/9nd+FveI3RF77kX8CcCmy9L6dw52figW9wZYk8cTYLK+5cIhyrbl6MpWXOkKemOL1cMmOIUzLplisUsmXWM2Vzalk7m//5PbLP76ULRdvvHzz7w08wMt+QeKUTe/7MaN/cJtVrDWfTq2WpqqN1sUxn8u3pT+qrBZqlQPPL/6l21Dtjb3RzaWWTTTkI7NaxGnUmVvK5TPF6t+l85VP//zJyezn3vva3yt4+De8JJV48xdZns9Kl106siHkcb096HNtqLfMg784dPIrt+zd/PVdI53vkDUNv0enXK4zlcxNTi/n/+xDN13yozdctsmGl7+7/A9JwJnWrhoNwPno3z1kjs2sXNsd9n1fU+WorinOwkrpnpVC9UvFcuvQ0W++//eN+Sz7/ymfZnBjMeG6AAAAInpUWHRTb2Z0d2FyZQAAeNorLy/Xy8zLLk5OLEjVyy9KBwA22AZYEFPKXAAAAABJRU5ErkJggg==\"/>\n        <div>\n            <span class=\"title\" style=\"padding-left: 20px;\">Tigase XMPP Server</span>\n            <% def currentCommand = model.commands.find { command -> command.node == request.getParameter('_node') &&\n            command.jid == request.getParameter('_jid') };\n            if (currentCommand) { %><span class=\"subtitle\" style=\"padding-left: 20px;\">${currentCommand.name}</span><% }\n            %>\n        </div>\n    </div>\n    <div class=\"sidebar\" style=\"width:300px; float: left; background: lightgray;\">\n        <ul>\n            <% model.commands.findAll { !it.group }.each { it.group = \"Other\" };\n            def groups = model.commands.collect { it.group }.unique().sort().each { group -> %>\n            <li ${ (group== request.getParameter(\n            '_group')) ? 'style=\"background: rgba(255,255,255,0.3)\"' : '' }><a\n                href=\"?_group=${java.net.URLEncoder.encode(group ?: '')}\">${group}</a><% if\n            (request.getParameter(\"_group\") == group) { %>\n            <ul><% model.commands.findAll { it.group == group }.sort { it.name }.each { command -> %>\n                <li ${ (command.node== request.getParameter(\n                '_node') && command.jid == request.getParameter('_jid')) ? 'style=\"background: rgba(255,255,255,0.4)\"' :\n                ''}><a href=\"?_group=${java.net.URLEncoder.encode(group ?: '')}&_jid=${java.net.URLEncoder.encode(command.jid)}&_node=${java.net.URLEncoder.encode(command.node)}\">${command.name}</a>* <br>\n                <small>${command.jid}</small>\n                </li>\n                <% } %>\n            </ul>\n            <% } %>\n            </li>\n            <% } %>\n        </ul>\n    </div>\n    <div class=\"content\" style=\"float:left; padding: 0px 50px;\">\n        <% if (model.formFields) { %>\n        <% def table = null; %>\n        <form method=\"POST\">\n            <% model.formFields?.each { formField -> %>\n            <% if (formField.getName() == 'item' && table) {\n            %>\n            <tr><% table.each { col ->\n                %>\n                <td>${ formField.getChildren().find { it.getAttribute(\"var\") == col }?.getChildCData('field/value') ?:\n                    '' }\n                </td>\n                <%\n                } %>\n            </tr>\n            <%\n            return;\n            }\n            if (table != null) {%></table><%}\n            table = null;\n            %>\n            <% if (formField.getName() == 'title') { %>\n            <div class=\"title\"><h3>${formField.getCData()}</h3></div>\n            <% return; } %>\n            <% if (formField.getName() == 'instructions') { %>\n            <div class=\"instructions\">${formField.getCData()}</div>\n            <% return; } %>\n            <% if (formField.getName() == 'reported') {\n            if (formField.getAttribute(\"label\")) { %>\n            <div><b>${formField.getAttribute(\"label\")}</b></div>\n            <% }\n            table = formField.getChildren().findAll { it.getName() == 'field' }.collect { it.getAttribute(\"var\") } %>\n            <table>\n                <thead><% table.each { %>\n                <th>${it}</th>\n                <% } %>\n                </thead>\n                <% return; } %>\n                <% def type = formField.getAttribute('type') ?: 'text-single';\n                if (type == 'hidden') { %>\n                <input type='hidden' name=\"${formField.getAttribute('var')}\"\n                       value=\"${formField.getChildCData('field/value') ?: ''}\"/>* <br>\n                <% } else if (type == 'boolean') { %>\n                <label for=\"${formField.getAttribute('var')}\">${formField.getAttribute('label') ?:\n                    (formField.getAttribute('var') ?: '')}</label>\n                <input type=\"checkbox\" name=\"${formField.getAttribute('var')}\" ${(formField.getChildCData('field/value')\n                == 'true' || formField.getChildCData('field/value') == '1') ? 'checked' : ''}/>* <br>\n                <% } else if (type == 'text-private') { %>\n                <label for=\"${formField.getAttribute('var')}\">${formField.getAttribute('label') ?:\n                    (formField.getAttribute('var') ?: '')}</label>\n                <input type=\"password\" name=\"${formField.getAttribute('var')}\"\n                       value=\"${formField.getChildCData('field/value') ?: ''}\"/>* <br>\n                <% } else if (type == 'text-multi' || type == 'jid-multi') { %>\n                <label for=\"${formField.getAttribute('var')}\">${formField.getAttribute('label') ?:\n                    (formField.getAttribute('var') ?: '')}</label>\n                <textarea name=\"${formField.getAttribute('var')}\" rows='6' cols='50'>${formField.getChildren().findAll{ it.getName() == 'value' }.collect {it.getCData()?:''}.join('\\n')}</textarea>* <br>\n                <% } else if (type == 'list-single' || type == 'list-multi') { %>\n                <label for=\"${formField.getAttribute('var')}\">${formField.getAttribute('label') ?:\n                    (formField.getAttribute('var') ?: '')}</label>\n                <select name=\"${formField.getAttribute('var')}\" ${ (type== 'list-multi') ? 'multiple' : ''}><%\n                formField.getChildren().findAll { it.getName() == 'option' }.each { %>\n                <option value='${it.getChildCData(it.getName()+'\n                /value') ?: it.getAttribute('label')}' ${formField.getChildren().find{ child -> (child.getName() ==\n                'value') && (child.getCData() == (it.getChildCData(it.getName()+'/value') ?: it.getAttribute('label')))\n                } ? 'selected' : ''}>${it.getAttribute('label') ?: it.getChildCData(it.getName()+'/value')}</option>\n                <% } %></select>* <br>\n                <% } else if (type == 'text-single' || type == 'jid-single') { %>\n                <label for=\"${formField.getAttribute('var')}\">${formField.getAttribute('label') ?:\n                    (formField.getAttribute('var') ?: '')}</label><input type=\"text\"\n                                                                         name=\"${formField.getAttribute('var')}\"\n                                                                         value=\"${formField.getChildCData('field/value') ?: ''}\"/>* <br>\n                <% } else if (type == 'fixed') { %>\n                <input style=\"clear:both;\" disabled type=\"text\" name=\"${formField.getAttribute('var')}\"\n                       value=\"${formField.getChildCData('field/value') ?: ''}\"/>* <br>\n                <input type='hidden' name=\"${formField.getAttribute('var')}\"\n                       value=\"${formField.getChildCData('field/value') ?: ''}\"/>* <br>\n                <% } else { %>\n                <label>${formField.getCData() ?: ''}</label>\n                <% } %>\n                <% } %>\n                <% if (table != null) { %>\n            </table>\n            <% } %>\n            <% if (model.formFields?.find { el -> el.getName() == \"field\" }) { %>\n            <input class='submit' name=\"submit\" type=\"submit\"/>\n            <% } %>\n        </form>\n        <% } %>\n    </div>\n    <div class=\"footer\">Powered by Tigase XMPP Server ${tigase.server.XMPPServer.getImplementationVersion()}</div>\n</body>\n</html>"
  },
  {
    "path": "src/main/java/tigase/auth/AuthRepositoryAware.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth;\n\nimport tigase.db.AuthRepository;\n\nimport javax.security.auth.callback.CallbackHandler;\n\n/**\n * Interface should be implemented by {@linkplain CallbackHandler} instance if {@linkplain AuthRepository} from session\n * should be injected.\n */\npublic interface AuthRepositoryAware\n\t\textends Aware {\n\n\t/**\n\t * Sets {@linkplain AuthRepository}.\n\t *\n\t * @param repo {@linkplain AuthRepository}.\n\t */\n\tvoid setAuthRepository(AuthRepository repo);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/Aware.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth;\n\npublic interface Aware {\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/BruteForceLockerBean.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth;\n\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.EventBusEvent;\nimport tigase.eventbus.HandleEvent;\nimport tigase.kernel.DefaultTypesConverter;\nimport tigase.kernel.TypesConverter;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.map.ClusterMapFactory;\nimport tigase.server.Command;\nimport tigase.server.DataForm;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.stats.ComponentStatisticsProvider;\nimport tigase.stats.StatisticsList;\nimport tigase.vhosts.AbstractVHostItemExtension;\nimport tigase.vhosts.VHostItemExtensionBackwardCompatible;\nimport tigase.vhosts.VHostItemExtensionManager;\nimport tigase.vhosts.VHostItemExtensionProvider;\nimport tigase.xml.Element;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.io.Serializable;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Function;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.auth.BruteForceLockerBean.Mode.IpJid;\n\n@Bean(name = \"brute-force-locker\", parent = SessionManager.class, active = true)\npublic class BruteForceLockerBean\n\t\timplements Initializable, UnregisterAware, ComponentStatisticsProvider {\n\n\tprivate final static String ANY = \"*\";\n\n\tprivate static final String LOCK_ENABLED_KEY = \"brute-force-lock-enabled\";\n\tprivate static final String LOCK_AFTER_FAILS_KEY = \"brute-force-lock-after-fails\";\n\tprivate static final String LOCK_DISABLE_ACCOUNT_FAILS_KEY = \"brute-force-disable-after-fails\";\n\tprivate static final String LOCK_TIME_KEY = \"brute-force-lock-time\";\n\tprivate static final String LOCK_PERIOD_TIME_KEY = \"brute-force-period-time\";\n\tprivate static final String LOCK_MODE_KEY = \"brute-force-mode\";\n\tprivate static final String MAP_TYPE = \"brute-force-invalid-logins\";\n\n\tpublic enum Mode {\n\t\tIp,\n\t\tIpJid,\n\t\tJid;\n\n\t\tpublic Mode merge(Mode mode) {\n\t\t\tswitch (this) {\n\t\t\t\tcase Ip:\n\t\t\t\t\tif (mode == Ip) {\n\t\t\t\t\t\treturn Ip;\n\t\t\t\t\t}\n\t\t\t\t\treturn IpJid;\n\t\t\t\tcase Jid:\n\t\t\t\t\tif (mode == Jid) {\n\t\t\t\t\t\treturn Jid;\n\t\t\t\t\t}\n\t\t\t\t\treturn IpJid;\n\t\t\t\tcase IpJid:\n\t\t\t\t\treturn IpJid;\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\t}\n\t\n\tprivate final Logger log = Logger.getLogger(this.getClass().getName());\n\tprivate final Map<String, StatHolder> otherStatHolders = new ConcurrentHashMap<>();\n\tprivate final StatHolder statHolder = new StatHolder();\n\t@ConfigField(desc = \"Allows storing detailed, per IP/JID statistics of blocked attempts\")\n\tprivate boolean detailedStatistics = false;\n\t@Inject\n\tprivate EventBus eventBus;\n\tprivate Map<Key, Value> map;\n\t@Inject\n\tprivate SessionManager sessionManager;\n\n\tpublic static String getClientIp(XMPPResourceConnection session) {\n\t\ttry {\n\t\t\treturn Optional.ofNullable(session.getConnectionId())\n\t\t\t\t\t.map(JID::getResource)\n\t\t\t\t\t.map(res -> res.split(\"_\")[2])\n\t\t\t\t\t.filter(ip -> !\"null\".equals(ip))\n\t\t\t\t\t.orElse(null);\n\t\t} catch (Exception e) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic void addInvalidLogin(XMPPResourceConnection session, String ip, BareJID jid) {\n\t\taddInvalidLogin(session, ip, jid, System.currentTimeMillis());\n\t}\n\n\tpublic void addInvalidLogin(XMPPResourceConnection session, String ip, BareJID jid, final long currentTime) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Adding new entry, JID: {0}, ip: {1}, time: {2}\", new Object[]{jid, ip, currentTime});\n\t\t}\n\t\tif (ip == null || \"null\".equals(ip)) {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.log(Level.FINER, \"IP is null. Skip adding entry.\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (map == null) {\n\t\t\tlog.warning(\"Brute Force Locker is no initialized yet!\");\n\t\t\treturn;\n\t\t}\n\n\t\tfinal Key key = createKey(session, ip, jid);\n\t\tValue value = map.get(key);\n\n\t\tif (value == null) {\n\t\t\tvalue = new Value(session != null ? session.getDomain().getVhost().toString() : null, ip, jid);\n\t\t\tvalue.setBadLoginCounter(0);\n\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.log(Level.FINER,\"Entry didn't exists. Create new one.\");\n\t\t\t}\n\t\t}\n\n\t\tif (value.getInvalidateAtTime() < currentTime) {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.log(Level.FINER,\"Entry exists and is old, reset counter.\");\n\t\t\t}\n\t\t\tvalue.setBadLoginCounter(0);\n\t\t}\n\n\t\tvalue.setBadLoginCounter(value.getBadLoginCounter() + 1);\n\n\t\tBruteForceLockerVHostExtension extension = session != null ? session.getDomain().getExtension(BruteForceLockerVHostExtension.class) : null;\n\n\t\tfinal long lockAfterFails = extension == null ? 3 : extension.getLockAccountAfterFailedAttempt();\n\t\tif (value.getBadLoginCounter() <= lockAfterFails) {\n\t\t\tfinal long periodTime =\n\t\t\t\t\t(extension == null ? 10 : extension.getPeriodTime()) * 1000;\n\t\t\tvalue.setInvalidateAtTime(currentTime + periodTime);\n\t\t} else {\n\t\t\tfinal long lockTime = (extension == null ? 10 : extension.getLockTime()) * 1000;\n\t\t\tvalue.setInvalidateAtTime(currentTime + lockTime);\n\t\t}\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\n\t\t\t\t\t\"New invalidate time for \" + key + \" == \" + value.getInvalidateAtTime() + \"; getBadLoginCounter \" +\n\t\t\t\t\t\t\t\"== \" + value.getBadLoginCounter());\n\t\t}\n\n\t\tmap.put(key, value);\n\n\t\taddToStatistic(value);\n\t}\n\n\tpublic boolean canUserBeDisabled(XMPPResourceConnection session, String ip, BareJID jid) {\n\t\tfinal Key key = createKey(session, ip, jid);\n\n\t\tif (!key.isJIDPresent()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tValue value = map.get(key);\n\n\t\tif (value == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tBruteForceLockerVHostExtension extension = session != null ? session.getDomain().getExtension(BruteForceLockerVHostExtension.class) : null;\n\t\tfinal long disableAfterFails =\n\t\t\t\textension == null ? 20 : extension.getDisableAccountAfterFailedAttempts();\n\n\t\tif (disableAfterFails == 0) {\n\t\t\treturn false;\n\t\t} else {\n\t\t\treturn value.getBadLoginCounter() > disableAfterFails;\n\t\t}\n\t}\n\n\tpublic void clearAll() {\n\t\tif (map == null) {\n\t\t\tlog.warning(\"Brute Force Locker is no initialized yet!\");\n\t\t\treturn;\n\t\t}\n\t\tmap.clear();\n\t}\n\n\tpublic void clearOutdated() {\n\t\tclearOutdated(System.currentTimeMillis());\n\t}\n\n\tpublic void clearOutdated(final long currentTime) {\n\t\tif (map == null) {\n\t\t\tlog.warning(\"Brute Force Locker is no initialized yet!\");\n\t\t\treturn;\n\t\t}\n\n\t\tfinal HashSet<Key> toRemove = new HashSet<>();\n\n\t\tmap.forEach((key, value) -> {\n\t\t\tif (value.getInvalidateAtTime() < currentTime) {\n\t\t\t\ttoRemove.add(key);\n\t\t\t}\n\t\t});\n\t\ttoRemove.forEach(key -> map.remove(key));\n\t}\n\n\t@Override\n\tpublic void getStatistics(String compName, StatisticsList list) {\n\t\tclearOutdated();\n\t\tfinal String keyName = compName + \"/BruteForceLocker\";\n\t\tArrayList<Value> l = new ArrayList<>(this.map.values());\n\t\tfor (Value value : l) {\n\t\t\tlist.add(keyName, \"Present locks: \" + value.jid + \" from \" + value.ip, value.badLoginCounter, Level.FINER);\n\t\t}\n\n\t\tfinal StatHolder tmp = new StatHolder();\n\n\t\tthis.statHolder.ips.forEach((ip, count) -> tmp.addIP(ip, count));\n\t\tthis.statHolder.jids.forEach((jid, count) -> tmp.addJID(jid, count));\n\n\t\tthis.otherStatHolders.values().forEach(otherSH -> {\n\t\t\totherSH.ips.forEach((ip, count) -> tmp.addIP(ip, count));\n\t\t\totherSH.jids.forEach((jid, count) -> tmp.addJID(jid, count));\n\t\t});\n\n\t\tlist.add(keyName, \"Blocked IPs\", tmp.ips.size(), Level.INFO);\n\t\tlist.add(keyName, \"Blocked JIDs\", tmp.jids.size(), Level.INFO);\n\n\t\tlist.add(keyName, \"Total blocked IP attempts\", tmp.ips.values().stream().mapToInt(Integer::intValue).sum(),\n\t\t\t\t Level.FINE);\n\t\tlist.add(keyName, \"Total blocked JID attempts\", tmp.jids.values().stream().mapToInt(Integer::intValue).sum(),\n\t\t\t\t Level.FINE);\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tthis.map = ClusterMapFactory.get().createMap(MAP_TYPE, Key.class, Value.class);\n\t\tassert this.map != null : \"Distributed Map is NULL!\";\n\t\tassert this.sessionManager != null : \"SessionManager is NULL!\";\n\n\t\tif (eventBus != null) {\n\t\t\teventBus.registerAll(this);\n\t\t}\n\t}\n\n\tpublic boolean isEnabled(XMPPResourceConnection session) {\n\t\tBruteForceLockerVHostExtension extension = session != null ? session.getDomain().getExtension(BruteForceLockerVHostExtension.class) : null;\n\t\treturn extension != null && extension.isEnabled();\n\t}\n\n\t@HandleEvent(filter = HandleEvent.Type.remote)\n\tpublic void handleStatisticsEmitEvent(StatisticsEmitEvent event) {\n\t\tif (event.getNodeName() == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.otherStatHolders.put(event.getNodeName(), event.getStatHolder());\n\t}\n\n\tpublic boolean isLoginAllowed(XMPPResourceConnection session, final String ip, final BareJID jid) {\n\t\treturn isLoginAllowed(session, ip, jid, System.currentTimeMillis());\n\t}\n\n\tpublic boolean isLoginAllowed(XMPPResourceConnection session, final String ip, final BareJID jid,\n\t\t\t\t\t\t\t\t  final long currentTime) {\n\t\tif (ip == null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"IP is null. Return true.\");\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\tif (map == null) {\n\t\t\tlog.warning(\"Brute Force Locker is no initialized yet!\");\n\t\t\treturn false;\n\t\t}\n\n\t\tfinal Key key = createKey(session, ip, jid);\n\t\tValue value = map.get(key);\n\n\t\tif (value == null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"No entry for \" + key + \". Return true.\");\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\treturn isLoginAllowed(session, key, value, currentTime);\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\teventBus.unregisterAll(this);\n\t}\n\n\t@Override\n\tpublic void everyHour() {\n\n\t}\n\n\t@Override\n\tpublic void everyMinute() {\n\t\tString clusterNode = sessionManager.getComponentId().getDomain();\n\t\teventBus.fire(new StatisticsEmitEvent(clusterNode, this.statHolder));\n\t}\n\n\t@Override\n\tpublic void everySecond() {\n\n\t}\n\n\tvoid setMap(HashMap<Key, Value> map) {\n\t\tthis.map = map;\n\t}\n\n\tfinal Key createKey(XMPPResourceConnection session, String ip, BareJID jid) {\n\t\tBruteForceLockerVHostExtension extension = session != null ? session.getDomain().getExtension(BruteForceLockerVHostExtension.class) : null;\n\t\tfinal Mode mode = extension == null ? Mode.IpJid : extension.getMode();\n\t\treturn createKey(mode, session, ip, jid);\n\t}\n\n\tfinal Key createKey(final Mode mode, XMPPResourceConnection session, String ip, BareJID jid) {\n\t\tfinal String domain = session == null ? ANY : session.getDomain().getVhost().toString();\n\t\tswitch (mode) {\n\t\t\tcase Jid:\n\t\t\t\treturn new Key(ANY, jid == null ? ANY : jid.toString(), domain);\n\t\t\tcase Ip:\n\t\t\t\treturn new Key(ip == null ? ANY : ip, ANY, domain);\n\t\t\tcase IpJid:\n\t\t\t\treturn new Key(ip == null ? ANY : ip, jid == null ? ANY : jid.toString(), domain);\n\t\t\tdefault:\n\t\t\t\tthrow new RuntimeException(\"Unknown mode \" + mode);\n\t\t}\n\t}\n\n\tprivate void addToStatistic(Value v) {\n\t\tif (v.ip != null) {\n\t\t\tthis.statHolder.addIP(v.ip);\n\t\t}\n\t\tif (v.jid != null) {\n\t\t\tthis.statHolder.addJID(v.jid);\n\t\t}\n\t}\n\n\tprivate boolean isLoginAllowed(final XMPPResourceConnection session, final Key key, final Value value,\n\t\t\t\t\t\t\t\t   final long currentTime) {\n\t\tif (value.getInvalidateAtTime() < currentTime) {\n\t\t\tmap.remove(key);\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Entry existed, but was too old. Entry removed. Return true.\");\n\t\t\t}\n\t\t\treturn true;\n\t\t} else {\n\t\t\tBruteForceLockerVHostExtension extension = session != null ? session.getDomain().getExtension(BruteForceLockerVHostExtension.class) : null;\n\t\t\tlong lockAfterFails = extension == null ? 3 : extension.getLockAccountAfterFailedAttempt();\n\t\t\tboolean r = value.badLoginCounter <= lockAfterFails;\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Entry exist. lockAfterFails=\" + lockAfterFails + \", value\" + \".badLoginCounter=\" +\n\t\t\t\t\t\t\t\t   value.badLoginCounter + \", result=\" + r);\n\t\t\t}\n\t\t\treturn r;\n\t\t}\n\t}\n\n\tpublic static class Key\n\t\t\timplements TypesConverter.Parcelable {\n\n\t\tprivate String domain;\n\t\tprivate String ip;\n\t\tprivate String jid;\n\n\t\tpublic Key() {\n\t\t}\n\n\t\tpublic Key(String ip, String jid, String domain) {\n\t\t\tthis.ip = ip;\n\t\t\tthis.jid = jid;\n\t\t\tthis.domain = domain;\n\t\t}\n\n\t\t@Override\n\t\tpublic String[] encodeToStrings() {\n\t\t\treturn new String[]{jid, ip, domain};\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object o) {\n\t\t\tif (this == o) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tKey key = (Key) o;\n\n\t\t\tif (!domain.equals(key.domain)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!ip.equals(key.ip)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn jid.equals(key.jid);\n\t\t}\n\n\t\t@Override\n\t\tpublic void fillFromString(String[] encoded) {\n\t\t\tthis.jid = encoded[0];\n\t\t\tthis.ip = encoded[1];\n\t\t\tthis.domain = encoded[2];\n\t\t}\n\n\t\tpublic String getIp() {\n\t\t\treturn ip;\n\t\t}\n\n\t\tpublic void setIp(String ip) {\n\t\t\tthis.ip = ip;\n\t\t}\n\n\t\tpublic String getJid() {\n\t\t\treturn jid;\n\t\t}\n\n\t\tpublic void setJid(String jid) {\n\t\t\tthis.jid = jid;\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\tint result = domain.hashCode();\n\t\t\tresult = 31 * result + ip.hashCode();\n\t\t\tresult = 31 * result + jid.hashCode();\n\t\t\treturn result;\n\t\t}\n\n\t\tpublic boolean isJIDPresent() {\n\t\t\treturn jid != null && !jid.equals(ANY);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"Key[ip=\" + ip + \", jid=\" + jid + \", domain=\" + domain + \"]\";\n\t\t}\n\n\t}\n\n\tpublic static class LoginLockedException\n\t\t\textends Exception {\n\n\t}\n\n\tpublic static class StatHolder\n\t\t\timplements TypesConverter.Parcelable {\n\n\t\tprivate final Map<String, Integer> ips = new ConcurrentHashMap<>();\n\t\tprivate final Map<BareJID, Integer> jids = new ConcurrentHashMap<>();\n\t\tprivate final DefaultTypesConverter typesConverter = new DefaultTypesConverter();\n\n\t\tpublic Map<String, Integer> getIps() {\n\t\t\treturn ips;\n\t\t}\n\n\t\tpublic Map<BareJID, Integer> getJids() {\n\t\t\treturn jids;\n\t\t}\n\n\t\tpublic void clear() {\n\t\t\tips.clear();\n\t\t\tjids.clear();\n\t\t}\n\n\t\tpublic int addIP(String ip) {\n\t\t\treturn add(ips, ip, 1);\n\t\t}\n\n\t\tpublic int addJID(BareJID jid) {\n\t\t\treturn add(jids, jid, 1);\n\t\t}\n\n\t\tpublic int addIP(String ip, int value) {\n\t\t\treturn add(ips, ip, value);\n\t\t}\n\n\t\tpublic int addJID(BareJID jid, int value) {\n\t\t\treturn add(jids, jid, value);\n\t\t}\n\n\t\t@Override\n\t\tpublic String[] encodeToStrings() {\n\t\t\tString[] r = new String[2 + ips.size() * 2 + jids.size() * 2];\n\t\t\tr[0] = String.valueOf(ips.size());\n\t\t\tr[1] = String.valueOf(jids.size());\n\n\t\t\tfillTab(ips, r, 2);\n\t\t\tfillTab(jids, r, 2 + ips.size() * 2);\n\n\t\t\treturn r;\n\t\t}\n\n\t\t@Override\n\t\tpublic void fillFromString(String[] encoded) {\n\t\t\ttry {\n\t\t\t\tfinal int lenIps = Integer.parseInt(encoded[0]);\n\t\t\t\tfinal int lenJids = Integer.parseInt(encoded[1]);\n\n\t\t\t\tips.clear();\n\t\t\t\tips.putAll(read(encoded, 2, lenIps, key -> key));\n\n\t\t\t\tjids.clear();\n\t\t\t\tjids.putAll(read(encoded, 2 + lenIps * 2, lenJids, BareJID::bareJIDInstanceNS));\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new RuntimeException(\"Cannot decode parcel: \" + Arrays.toString(encoded), e);\n\t\t\t}\n\t\t}\n\n\t\tprivate <T> HashMap<T, Integer> read(final String[] src, final int offset, final int len,\n\t\t\t\t\t\t\t\t\t\t\t Function<String, T> keyCnv) {\n\t\t\tHashMap<T, Integer> r = new HashMap<>();\n\t\t\tfor (int i = 0; i < len; i++) {\n\t\t\t\tr.put(keyCnv.apply(src[offset + 2 * i]), Integer.parseInt(src[offset + 2 * i + 1]));\n\t\t\t}\n\t\t\treturn r;\n\t\t}\n\n\t\tprivate <T> void fillTab(Map<T, Integer> src, String[] dst, final int offset) {\n\t\t\tint idx = offset;\n\t\t\tfor (Map.Entry<T, Integer> x : src.entrySet()) {\n\t\t\t\tString key = x.getKey().toString();\n\t\t\t\tInteger value = x.getValue();\n\t\t\t\tdst[idx] = key;\n\t\t\t\tdst[idx + 1] = String.valueOf(value);\n\t\t\t\tidx = idx + 2;\n\t\t\t}\n\t\t}\n\n\t\tprivate <T> int add(Map<T, Integer> map, T key, int value) {\n\t\t\tsynchronized (map) {\n\t\t\t\tInteger v = map.get(key);\n\t\t\t\tv = (v == null ? 0 : v) + value;\n\t\t\t\tmap.put(key, v);\n\t\t\t\treturn v;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static class StatisticsEmitEvent\n\t\t\timplements Serializable, EventBusEvent {\n\n\t\tprivate String nodeName;\n\n\t\tprivate StatHolder statHolder;\n\n\t\tpublic StatisticsEmitEvent() {\n\t\t}\n\n\t\tpublic StatisticsEmitEvent(String nodeName, StatHolder statHolder) {\n\t\t\tthis.nodeName = nodeName;\n\t\t\tthis.statHolder = statHolder;\n\t\t}\n\n\t\tpublic String getNodeName() {\n\t\t\treturn nodeName;\n\t\t}\n\n\t\tpublic void setNodeName(String nodeName) {\n\t\t\tthis.nodeName = nodeName;\n\t\t}\n\n\t\tpublic StatHolder getStatHolder() {\n\t\t\treturn statHolder;\n\t\t}\n\n\t\tpublic void setStatHolder(StatHolder statHolder) {\n\t\t\tthis.statHolder = statHolder;\n\t\t}\n\t}\n\n\tpublic static class Value\n\t\t\timplements TypesConverter.Parcelable {\n\n\t\tprivate int badLoginCounter;\n\t\tprivate String domain;\n\t\t/** Invalidate this value at specific time */\n\t\tprivate long invalidateAtTime;\n\t\tprivate String ip;\n\t\tprivate BareJID jid;\n\n\t\tpublic Value() {\n\t\t}\n\n\t\tpublic Value(String domain, String ip, BareJID jid) {\n\t\t\tthis.domain = domain;\n\t\t\tthis.ip = ip;\n\t\t\tthis.jid = jid;\n\t\t}\n\n\t\t@Override\n\t\tpublic String[] encodeToStrings() {\n\t\t\treturn new String[]{Integer.toString(badLoginCounter), Long.toString(invalidateAtTime)};\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object o) {\n\t\t\tif (this == o) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tValue value = (Value) o;\n\n\t\t\tif (badLoginCounter != value.badLoginCounter) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn invalidateAtTime == value.invalidateAtTime;\n\t\t}\n\n\t\t@Override\n\t\tpublic void fillFromString(String[] encoded) {\n\t\t\tthis.badLoginCounter = Integer.valueOf(encoded[0]);\n\t\t\tthis.invalidateAtTime = Long.valueOf(encoded[1]);\n\t\t}\n\n\t\tpublic int getBadLoginCounter() {\n\t\t\treturn badLoginCounter;\n\t\t}\n\n\t\tpublic void setBadLoginCounter(int badLoginCounter) {\n\t\t\tthis.badLoginCounter = badLoginCounter;\n\t\t}\n\n\t\tpublic long getInvalidateAtTime() {\n\t\t\treturn invalidateAtTime;\n\t\t}\n\n\t\tpublic void setInvalidateAtTime(long invalidateAtTime) {\n\t\t\tthis.invalidateAtTime = invalidateAtTime;\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\tint result = badLoginCounter;\n\t\t\tresult = 31 * result + (int) (invalidateAtTime ^ (invalidateAtTime >>> 32));\n\t\t\treturn result;\n\t\t}\n\n\t}\n\n\t@Bean(name = BruteForceLockerVHostExtension.ID, parent = VHostItemExtensionManager.class, active = true)\n\tpublic static class BruteForceLockerVHostExtensionProvider\n\t\t\timplements VHostItemExtensionProvider<BruteForceLockerVHostExtension> {\n\n\t\t@Override\n\t\tpublic String getId() {\n\t\t\treturn BruteForceLockerVHostExtension.ID;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<BruteForceLockerVHostExtension> getExtensionClazz() {\n\t\t\treturn BruteForceLockerVHostExtension.class;\n\t\t}\n\t}\n\n\tpublic static class BruteForceLockerVHostExtension\n\t\t\textends AbstractVHostItemExtension<BruteForceLockerVHostExtension>\n\t\t\timplements VHostItemExtensionBackwardCompatible<BruteForceLockerVHostExtension> {\n\n\t\tpublic static final String ID = \"brute-force-locker\";\n\t\tprivate static final long DEF_lockAccountAfterFailedAttempt = 3l;\n\t\tprivate static final long DEF_disableAccountAfterFailedAttempts = 20l;\n\t\tprivate static final long DEF_periodTime = 60l;\n\t\tprivate static final long DEF_lockTime = 60l;\n\t\tprivate static final Mode DEF_mode = IpJid;\n\n\t\tprivate boolean enabled = true;\n\t\tprivate long lockAccountAfterFailedAttempt = DEF_lockAccountAfterFailedAttempt;\n\t\tprivate long disableAccountAfterFailedAttempts = DEF_disableAccountAfterFailedAttempts;\n\t\tprivate long periodTime = DEF_periodTime;\n\t\tprivate long lockTime = DEF_lockTime;\n\t\tprivate Mode mode = DEF_mode;\n\n\t\tpublic boolean isEnabled() {\n\t\t\treturn enabled;\n\t\t}\n\n\t\tpublic long getLockAccountAfterFailedAttempt() {\n\t\t\treturn lockAccountAfterFailedAttempt;\n\t\t}\n\n\t\tpublic long getDisableAccountAfterFailedAttempts() {\n\t\t\treturn disableAccountAfterFailedAttempts;\n\t\t}\n\n\t\tpublic long getPeriodTime() {\n\t\t\treturn periodTime;\n\t\t}\n\n\t\tpublic long getLockTime() {\n\t\t\treturn lockTime;\n\t\t}\n\n\t\tpublic Mode getMode() {\n\t\t\treturn mode;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getId() {\n\t\t\treturn ID;\n\t\t}\n\n\t\t@Override\n\t\tpublic void initFromElement(Element item) {\n\t\t\tenabled = !\"false\".equals(item.getAttributeStaticStr(\"enabled\"));\n\t\t\tString tmp = item.getAttributeStaticStr(\"lock-after-fails\");\n\t\t\tif (tmp != null) {\n\t\t\t\tlockAccountAfterFailedAttempt = Long.parseLong(tmp);\n\t\t\t}\n\t\t\ttmp = item.getAttributeStaticStr(\"disable-after-fails\");\n\t\t\tif (tmp != null) {\n\t\t\t\tdisableAccountAfterFailedAttempts = Long.parseLong(tmp);\n\t\t\t}\n\t\t\ttmp = item.getAttributeStaticStr(\"period-time\");\n\t\t\tif (tmp != null) {\n\t\t\t\tperiodTime = Long.parseLong(tmp);\n\t\t\t}\n\t\t\ttmp = item.getAttributeStaticStr(\"lock-time\");\n\t\t\tif (tmp != null) {\n\t\t\t\tlockTime = Long.parseLong(tmp);\n\t\t\t}\n\t\t\ttmp = item.getAttributeStaticStr(\"mode\");\n\t\t\tif (tmp != null) {\n\t\t\t\tmode = Mode.valueOf(tmp);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void initFromCommand(String prefix, Packet packet) throws IllegalArgumentException {\n\t\t\tOptional.ofNullable(Command.getFieldValue(packet, prefix + \"-enabled\"))\n\t\t\t\t\t.ifPresent(s -> enabled = Boolean.parseBoolean(s));\n\t\t\tString value = Command.getFieldValue(packet, prefix + \"-lock-after-fails\");\n\t\t\tif (value != null) {\n\t\t\t\tlockAccountAfterFailedAttempt = Long.parseLong(value);\n\t\t\t}\n\t\t\tvalue = Command.getFieldValue(packet, prefix + \"-disable-after-fails\");\n\t\t\tif (value != null) {\n\t\t\t\tdisableAccountAfterFailedAttempts = Long.parseLong(value);\n\t\t\t}\n\t\t\tvalue = Command.getFieldValue(packet, prefix + \"-period-time\");\n\t\t\tif (value != null) {\n\t\t\t\tperiodTime = Long.parseLong(value);\n\t\t\t}\n\t\t\tvalue = Command.getFieldValue(packet, prefix + \"-lock-time\");\n\t\t\tif (value != null) {\n\t\t\t\tlockTime = Long.parseLong(value);\n\t\t\t}\n\t\t\tvalue = Command.getFieldValue(packet, prefix + \"-mode\");\n\t\t\tif (value != null) {\n\t\t\t\tmode = Mode.valueOf(value);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic String toDebugString() {\n\t\t\treturn \"enabled: \" + enabled + \",mode: \" + mode + \", lockAfter: \" + lockAccountAfterFailedAttempt +\n\t\t\t\t\t\", disableAfter: \" + disableAccountAfterFailedAttempts + \", period: \" + periodTime +\n\t\t\t\t\t\", lockTime: \" + lockTime;\n\t\t}\n\n\t\t@Override\n\t\tpublic Element toElement() {\n\t\t\tif (enabled && lockAccountAfterFailedAttempt == DEF_lockAccountAfterFailedAttempt &&\n\t\t\t\t\tdisableAccountAfterFailedAttempts == DEF_disableAccountAfterFailedAttempts &&\n\t\t\t\t\tperiodTime == DEF_periodTime && lockTime == DEF_lockTime && mode == DEF_mode) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tElement el = new Element(ID);\n\t\t\tif (!enabled) {\n\t\t\t\tel.setAttribute(\"enabled\", String.valueOf(enabled));\n\t\t\t}\n\t\t\tif (lockAccountAfterFailedAttempt != DEF_lockAccountAfterFailedAttempt) {\n\t\t\t\tel.setAttribute(\"lock-after-fails\", String.valueOf(lockAccountAfterFailedAttempt));\n\t\t\t}\n\t\t\tif (disableAccountAfterFailedAttempts != DEF_disableAccountAfterFailedAttempts) {\n\t\t\t\tel.setAttribute(\"disable-after-fails\", String.valueOf(disableAccountAfterFailedAttempts));\n\t\t\t}\n\t\t\tif (periodTime != DEF_periodTime) {\n\t\t\t\tel.setAttribute(\"period-time\", String.valueOf(periodTime));\n\t\t\t}\n\t\t\tif (lockTime != DEF_lockTime) {\n\t\t\t\tel.setAttribute(\"lock-time\", String.valueOf(lockTime));\n\t\t\t}\n\t\t\tif (mode != DEF_mode) {\n\t\t\t\tel.setAttribute(\"mode\", mode.name());\n\t\t\t}\n\t\t\treturn el;\n\t\t}\n\t\t\n\t\t@Override\n\t\tpublic void addCommandFields(String prefix, Packet packet, boolean forDefault) {\n\t\t\tElement commandEl = packet.getElemChild(Command.COMMAND_EL, Command.XMLNS);\n\t\t\tDataForm.addFieldValue(commandEl, prefix + \"-enabled\", String.valueOf(enabled), \"boolean\",\n\t\t\t\t\t\t\t\t   \"Brute Force Prevention Enabled\");\n\t\t\tDataForm.addFieldValue(commandEl, prefix + \"-lock-after-fails\",\n\t\t\t\t\t\t\t\t   String.valueOf(lockAccountAfterFailedAttempt), \"text-single\",\n\t\t\t\t\t\t\t\t   \"Number of allowed invalid login\");\n\n\t\t\tDataForm.addFieldValue(commandEl, prefix + \"-disable-after-fails\",\n\t\t\t\t\t\t\t\t   String.valueOf(disableAccountAfterFailedAttempts), \"text-single\",\n\t\t\t\t\t\t\t\t   \"Disable account after failed login\");\n\t\t\tDataForm.addFieldValue(commandEl, prefix + \"-period-time\", String.valueOf(periodTime),\n\t\t\t\t\t\t\t\t   \"text-single\", \"Failed login in period of time [sec]\");\n\t\t\tDataForm.addFieldValue(commandEl, prefix + \"-lock-time\", String.valueOf(lockTime),\n\t\t\t\t\t\t\t\t   \"text-single\", \"Lock time [sec]\");\n\t\t\tDataForm.addFieldValue(commandEl, prefix + \"-mode\", mode.name(), \"Brute Force Prevention Mode\",\n\t\t\t\t\t\t\t\t   new String[]{Mode.Ip.name(), Mode.Jid.name(), IpJid.name()},\n\t\t\t\t\t\t\t\t   new String[]{Mode.Ip.name(), Mode.Jid.name(), IpJid.name()});\n\t\t}\n\n\n\t\t@Override\n\t\tpublic void initFromData(Map<String, Object> data) {\n\t\t\tBoolean enabled = (Boolean) data.remove(LOCK_ENABLED_KEY);\n\t\t\tif (enabled != null) {\n\t\t\t\tthis.enabled = enabled;\n\t\t\t}\n\t\t\tLong tmp = (Long) data.remove(LOCK_AFTER_FAILS_KEY);\n\t\t\tif (tmp != null) {\n\t\t\t\tlockAccountAfterFailedAttempt = tmp;\n\t\t\t}\n\t\t\ttmp = (Long) data.remove(LOCK_DISABLE_ACCOUNT_FAILS_KEY);\n\t\t\tif (tmp != null) {\n\t\t\t\tdisableAccountAfterFailedAttempts = tmp;\n\t\t\t}\n\t\t\ttmp = (Long) data.remove(LOCK_PERIOD_TIME_KEY);\n\t\t\tif (tmp != null) {\n\t\t\t\tperiodTime = tmp;\n\t\t\t}\n\t\t\ttmp = (Long) data.remove(LOCK_TIME_KEY);\n\t\t\tif (tmp != null) {\n\t\t\t\tlockTime = tmp;\n\t\t\t}\n\t\t\tString mode = (String) data.remove(LOCK_MODE_KEY);\n\t\t\tif (mode != null) {\n\t\t\t\tthis.mode = Mode.valueOf(mode);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic BruteForceLockerVHostExtension mergeWithDefaults(BruteForceLockerVHostExtension defaults) {\n\t\t\tBruteForceLockerVHostExtension merged = new BruteForceLockerVHostExtension();\n\n\t\t\tmerged.enabled = this.enabled || defaults.enabled;\n\t\t\tmerged.mode = this.mode.merge(defaults.mode);\n\t\t\tmerged.lockTime = Math.max(this.lockTime,  defaults.lockTime);\n\t\t\tmerged.periodTime = Math.max(this.periodTime, defaults.periodTime);\n\t\t\tmerged.disableAccountAfterFailedAttempts = Math.min(this.disableAccountAfterFailedAttempts, defaults.disableAccountAfterFailedAttempts);\n\t\t\tmerged.lockAccountAfterFailedAttempt = Math.min(this.lockAccountAfterFailedAttempt, defaults.lockAccountAfterFailedAttempt);\n\n\t\t\treturn merged;\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/CallbackHandlerFactory.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth;\n\nimport tigase.auth.callbacks.CallbackHandlerFactoryIfc;\nimport tigase.auth.impl.PlainCallbackHandler;\nimport tigase.auth.impl.ScramCallbackHandler;\nimport tigase.auth.impl.XTokenCallbackHandler;\nimport tigase.auth.mechanisms.*;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.xmpp.XMPPResourceConnection;\n\nimport javax.security.auth.callback.CallbackHandler;\n\n/**\n * Factory of {@linkplain CallbackHandler CallbackHandlers}.\n */\n@Bean(name = \"callback-handler-factory\", parent = TigaseSaslProvider.class, active = true)\npublic class CallbackHandlerFactory\n\t\timplements CallbackHandlerFactoryIfc {\n\n\tpublic static final String AUTH_JID = \"authentication-jid\";\n\n\tprivate static final String CALLBACK_HANDLER_KEY = \"callbackhandler\";\n\n\t@Override\n\tpublic CallbackHandler create(String mechanismName, XMPPResourceConnection session, NonAuthUserRepository repo)\n\t\t\tthrows ClassNotFoundException, InstantiationException, IllegalAccessException {\n\t\tString handlerClassName = getHandlerClassname(mechanismName, session, repo);\n\t\tif (handlerClassName == null) {\n\t\t\thandlerClassName = PlainCallbackHandler.class.getName();\n\t\t}\n\t\t@SuppressWarnings(\"unchecked\") Class<CallbackHandler> handlerClass = (Class<CallbackHandler>) Class.forName(\n\t\t\t\thandlerClassName);\n\n\t\tCallbackHandler handler = handlerClass.newInstance();\n\n\t\tif (handler instanceof SessionAware) {\n\t\t\t((SessionAware) handler).setSession(session);\n\t\t}\n\n\t\tif (handler instanceof DomainAware) {\n\t\t\t((DomainAware) handler).setDomain(session.getDomain().getVhost().getDomain());\n\t\t}\n\n\t\tif (handler instanceof NonAuthUserRepositoryAware) {\n\t\t\t((NonAuthUserRepositoryAware) handler).setNonAuthUserRepository(repo);\n\t\t}\n\n\t\tif (handler instanceof AuthRepositoryAware) {\n\t\t\t((AuthRepositoryAware) handler).setAuthRepository(session.getAuthRepository());\n\t\t}\n\n\t\tif (handler instanceof MechanismNameAware) {\n\t\t\t((MechanismNameAware) handler).setMechanismName(mechanismName);\n\t\t}\n\n\t\treturn handler;\n\t}\n\n\tprotected String getHandlerClassname(String mechanismName, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t NonAuthUserRepository repo) {\n\t\tswitch (mechanismName) {\n\t\t\tcase SaslSCRAM.NAME:\n\t\t\tcase SaslSCRAMPlus.NAME:\n\t\t\tcase SaslSCRAMSha256.NAME:\n\t\t\tcase SaslSCRAMSha256Plus.NAME:\n\t\t\tcase SaslSCRAMSha512.NAME:\n\t\t\tcase SaslSCRAMSha512Plus.NAME:\n\t\t\t\treturn ScramCallbackHandler.class.getName();\n\t\t\tcase SaslXTOKEN.NAME:\n\t\t\t\treturn XTokenCallbackHandler.class.getName();\n\t\t\tdefault:\n\t\t\t\treturn null;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/CredentialsDecoderBean.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth;\n\nimport tigase.auth.credentials.Credentials;\nimport tigase.db.beans.AuthRepositoryMDPoolBean;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.RegistrarBean;\nimport tigase.kernel.core.Kernel;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.security.NoSuchAlgorithmException;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n@Bean(name = \"credentialDecoders\", parent = AuthRepositoryMDPoolBean.AuthRepositoryConfigBean.class, active = true)\npublic class CredentialsDecoderBean\n\t\timplements RegistrarBean {\n\n\t@Inject\n\tprivate List<Credentials.Decoder> decoders;\n\n\tpublic List<String> getSupportedMechanisms() {\n\t\treturn decoders.stream().map(dec -> dec.getName()).collect(Collectors.toList());\n\t}\n\n\tpublic Credentials.Entry decode(BareJID user, String mechanism, String password) throws NoSuchAlgorithmException {\n\t\tfor (Credentials.Decoder decoder : decoders) {\n\t\t\tif (mechanism.equals(decoder.getName())) {\n\t\t\t\treturn decoder.decode(user, password);\n\t\t\t}\n\t\t}\n\n\t\tthrow new NoSuchAlgorithmException(\"No password decoder for mechanism \" + mechanism);\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/CredentialsEncoderBean.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth;\n\nimport tigase.auth.credentials.Credentials;\nimport tigase.db.beans.AuthRepositoryMDPoolBean;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.RegistrarBean;\nimport tigase.kernel.core.Kernel;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.security.NoSuchAlgorithmException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n@Bean(name = \"credentialEncoders\", parent = AuthRepositoryMDPoolBean.AuthRepositoryConfigBean.class, active = true)\npublic class CredentialsEncoderBean\n\t\timplements RegistrarBean {\n\n\t@Inject\n\tprivate List<Credentials.Encoder> encoders;\n\n\tpublic List<String> getSupportedMechanisms() {\n\t\treturn encoders.stream().map(enc -> enc.getName()).collect(Collectors.toList());\n\t}\n\n\tpublic String encode(BareJID user, String mechanism, String password) throws NoSuchAlgorithmException {\n\t\tfor (Credentials.Encoder encoder : encoders) {\n\t\t\tif (mechanism.equals(encoder.getName())) {\n\t\t\t\treturn encoder.encode(user, password);\n\t\t\t}\n\t\t}\n\n\t\tthrow new NoSuchAlgorithmException(\"No password encoder for mechanism \" + mechanism);\n\t}\n\n\tpublic List<String[]> encodeForAllMechanisms(BareJID user, String password) {\n\t\tList<String[]> entries = new ArrayList<>();\n\t\tfor (Credentials.Encoder enc : encoders) {\n\t\t\tString secret = enc.encode(user, password);\n\t\t\tif (secret != null) {\n\t\t\t\tentries.add(new String[]{enc.getName(), secret});\n\t\t\t}\n\t\t}\n\t\treturn entries;\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/DefaultMechanismSelector.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth;\n\nimport tigase.auth.mechanisms.SaslEXTERNAL;\nimport tigase.auth.mechanisms.SaslSCRAMPlus;\nimport tigase.auth.mechanisms.TigaseSaslServerFactory;\nimport tigase.cert.CertificateUtil;\nimport tigase.db.AuthRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.vhosts.VHostItem;\nimport tigase.xmpp.XMPPResourceConnection;\n\nimport javax.security.sasl.SaslServerFactory;\nimport java.security.cert.Certificate;\nimport java.security.cert.X509Certificate;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Stream;\n\n@Bean(name = \"mechanism-selector\", parent = TigaseSaslProvider.class, active = true)\npublic class DefaultMechanismSelector\n\t\timplements MechanismSelector {\n\n\tprivate static Logger log = Logger.getLogger(DefaultMechanismSelector.class.getName());\n\n\t@ConfigField(desc = \"List of allowed SASL mechanisms\", alias = \"allowed-mechanisms\")\n\tprivate HashSet<String> allowedMechanisms = new HashSet<String>();\n\n\t@ConfigField(desc = \"List of SASL mechanisms allowed with non-plain password stored in authentication repository\", alias = \"non-plain-password-allowed-mechanisms\")\n\tprivate HashSet<String> allowedMechanismsWithNonPlainPasswordInRepository = new HashSet<>();\n\n\t@ConfigField(desc = \"Disable SCRAM -PLUS mechanisms\")\n\tprivate boolean disableScramPlus = false;\n\n\t@Inject\n\tprivate AuthRepository authRepository;\n\n\tpublic DefaultMechanismSelector() {\n\t\tStream.of(\"ANONYMOUS\", \"PLAIN\", \"EXTERNAL\").forEach(allowedMechanismsWithNonPlainPasswordInRepository::add);\n\t}\n\n\t@Override\n\tpublic Collection<String> filterMechanisms(Enumeration<SaslServerFactory> serverFactories,\n\t\t\t\t\t\t\t\t\t\t\t   XMPPResourceConnection session) {\n\t\tfinal Map<String, ?> props = new HashMap<String, Object>();\n\t\tfinal ArrayList<String> result = new ArrayList<String>();\n\t\twhile (serverFactories.hasMoreElements()) {\n\t\t\tSaslServerFactory ss = serverFactories.nextElement();\n\t\t\tString[] x = ss.getMechanismNames(props);\n\t\t\tfor (String name : x) {\n\t\t\t\t// JKD9 introduced a change! now if factory implements more than one mechanism it will be returned multiple times!!\n\t\t\t\tif (result.contains(name)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (match(ss, name, session) && isAllowedForDomain(name, session.getDomain())) {\n\t\t\t\t\tresult.add(name);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tprotected boolean isAllowedForDomain(final String mechanismName, final VHostItem vhost) {\n\t\tfinal String[] saslAllowedMechanisms = vhost.getSaslAllowedMechanisms();\n\t\tif (saslAllowedMechanisms != null && saslAllowedMechanisms.length > 0) {\n\t\t\tfor (String allowed : saslAllowedMechanisms) {\n\t\t\t\tif (allowed.equals(mechanismName)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t} else if (!allowedMechanisms.isEmpty()) {\n\t\t\treturn allowedMechanisms.contains(mechanismName);\n\t\t}\n\t\treturn true;\n\t}\n\n\tprotected boolean match(SaslServerFactory factory, String mechanismName, XMPPResourceConnection session) {\n\t\tif (session.isTlsRequired() && !session.isEncrypted()) {\n\t\t\treturn false;\n\t\t}\n\t\tif (factory instanceof TigaseSaslServerFactory) {\n\t\t\tswitch (mechanismName) {\n\t\t\t\tcase \"EXTERNAL\":\n\t\t\t\t\treturn isJIDInCertificate(session);\n\t\t\t\tcase \"ANONYMOUS\":\n\t\t\t\t\treturn session.getDomain().isAnonymousEnabled();\n\t\t\t\tdefault:\n\t\t\t\t\tif (mechanismName.startsWith(\"SCRAM-\") && mechanismName.endsWith(\"-PLUS\") &&\n\t\t\t\t\t\t\t((!SaslSCRAMPlus.isAvailable(session)) || disableScramPlus)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn authRepository.isMechanismSupported(session.getDomain().getKey(), mechanismName);\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate boolean isJIDInCertificate(final XMPPResourceConnection session) {\n\t\tCertificate cert = (Certificate) session.getSessionData(SaslEXTERNAL.PEER_CERTIFICATE_KEY);\n\t\tif (cert == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfinal List<String> authJIDs = CertificateUtil.extractXmppAddrs((X509Certificate) cert);\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"{0}, Found authJIDs: {1} in certificate: {1}\",\n\t\t\t\t\tnew Object[]{session, String.valueOf(authJIDs), CertificateUtil.getCertCName(((X509Certificate) cert))});\n\t\t}\n\t\treturn authJIDs != null && !authJIDs.isEmpty();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/DomainAware.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth;\n\nimport javax.security.auth.callback.CallbackHandler;\n\n/**\n * Interface should be implemented by {@linkplain CallbackHandler} instance if domain name rom current XMPP Session\n * should be injected.\n */\npublic interface DomainAware\n\t\textends Aware {\n\n\t/**\n\t * Sets name of domain from XMPP Stream.\n\t *\n\t * @param domain domain name\n\t */\n\tvoid setDomain(String domain);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/MechanismNameAware.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth;\n\npublic interface MechanismNameAware {\n\n\tvoid setMechanismName(String mechanismName);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/MechanismSelector.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth;\n\nimport tigase.xmpp.XMPPResourceConnection;\n\nimport javax.security.sasl.SaslServerFactory;\nimport java.util.Collection;\nimport java.util.Enumeration;\n\n/**\n * Interface for implementing selectors of SASL mechanisms.\n */\npublic interface MechanismSelector {\n\n\t/**\n\t * Method filters all available SASL mechanisms from {@link SaslServerFactory factories} with current {@link\n\t * XMPPResourceConnection session} state.\n\t *\n\t * @param serverFactories {@link SaslServerFactory SaslServerFactory} enumeration.\n\t * @param session current session\n\t *\n\t * @return collection of all SASL mechanisms available in given session (and current XMPP Stream).\n\t */\n\tCollection<String> filterMechanisms(Enumeration<SaslServerFactory> serverFactories, XMPPResourceConnection session);\n\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/auth/NonAuthUserRepositoryAware.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth;\n\nimport tigase.db.NonAuthUserRepository;\n\npublic interface NonAuthUserRepositoryAware\n\t\textends Aware {\n\n\tvoid setNonAuthUserRepository(NonAuthUserRepository repo);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/PasswordResetterIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth;\n\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.xmpp.jid.BareJID;\n\npublic interface PasswordResetterIfc {\n\n\tvoid changePassword(String encodedToken, String password) throws RepositoryException;\n\n\tvoid validateToken(String encodedToken) throws RepositoryException, RuntimeException;\n\n\tvoid sendToken(BareJID bareJID, String url) throws RepositoryException, Exception;\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/PluginSettingsAware.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth;\n\nimport javax.security.auth.callback.CallbackHandler;\nimport java.util.Map;\n\n/**\n * Interface should be implemented by {@linkplain CallbackHandler} instance if plugin settings should be injected.\n *\n * @author Daniele Ricci\n */\npublic interface PluginSettingsAware {\n\n\tpublic void setPluginSettings(Map<String, Object> settings);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/SaslInvalidLoginExcepion.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth;\n\npublic class SaslInvalidLoginExcepion\n\t\textends XmppSaslException {\n\n\tprivate final String jid;\n\n\tpublic SaslInvalidLoginExcepion(SaslError saslError, String jid) {\n\t\tsuper(saslError);\n\t\tthis.jid = jid;\n\t}\n\n\tpublic SaslInvalidLoginExcepion(SaslError saslError, String jid, String detail) {\n\t\tsuper(saslError, detail);\n\t\tthis.jid = jid;\n\t}\n\n\tpublic String getJid() {\n\t\treturn jid;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/SessionAware.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth;\n\nimport tigase.xmpp.XMPPResourceConnection;\n\nimport javax.security.auth.callback.CallbackHandler;\n\n/**\n * Interface should be implemented by {@linkplain CallbackHandler} instance if current XMPP Session should be injected.\n */\npublic interface SessionAware\n\t\textends Aware {\n\n\t/**\n\t * Sets XMPP Session.\n\t *\n\t * @param session XMPP session.\n\t */\n\tvoid setSession(XMPPResourceConnection session);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/TigaseSaslProvider.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth;\n\nimport tigase.auth.callbacks.CallbackHandlerFactoryIfc;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.*;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xmpp.XMPPResourceConnection;\n\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.sasl.SaslServerFactory;\nimport java.security.Provider;\nimport java.security.Security;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CopyOnWriteArraySet;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\n/**\n * Describe class TigaseSaslProvider here.\n * <br>\n * Created: Sun Nov 5 22:31:20 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\n@Bean(name = \"sasl-provider\", parent = SessionManager.class, active = true)\npublic class TigaseSaslProvider\n\t\textends Provider\n\t\timplements Initializable, UnregisterAware, RegistrarBean {\n\n\tpublic static final String FACTORY_KEY = \"factory\";\n\n\tprivate static final String INFO = \"This is tigase provider (provides Tigase server specific mechanisms)\";\n\n\tprivate static final Logger log = Logger.getLogger(TigaseSaslProvider.class.getName());\n\n\tprivate static final String MY_NAME = \"tigase.sasl\";\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate static final double VERSION = 1.0;\n\n\t@Inject\n\tprivate CallbackHandlerFactoryIfc callbackHandlerFactory;\n\n\t@Inject\n\tprivate MechanismSelector mechanismSelector;\n\n\t@Inject(nullAllowed = true)\n\tprivate CopyOnWriteArraySet<SaslServerFactory> saslServerFactories = new CopyOnWriteArraySet<>();\n\n\tprivate ConcurrentHashMap<SaslServerFactory, List<Service>> saslServerFactoriesServices = new ConcurrentHashMap<>();\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic TigaseSaslProvider() {\n\t\tsuper(MY_NAME, VERSION, INFO);\n\t}\n\n\tpublic void setSaslServerFactories(CopyOnWriteArraySet<SaslServerFactory> saslServerFactories) {\n\t\tthis.saslServerFactories.stream()\n\t\t\t\t.filter(factory -> saslServerFactories == null || !saslServerFactories.contains(factory))\n\t\t\t\t.forEach(this::unregisterFactory);\n\t\tif (saslServerFactories != null) {\n\t\t\tsaslServerFactories.stream()\n\t\t\t\t\t.filter(factory -> !this.saslServerFactories.contains(factory))\n\t\t\t\t\t.forEach(this::registerFactory);\n\t\t}\n\t\tthis.saslServerFactories = saslServerFactories == null ? new CopyOnWriteArraySet<>() : saslServerFactories;\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\tSecurity.removeProvider(MY_NAME);\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tSecurity.insertProviderAt(this, 1);\n\t}\n\n\tpublic CallbackHandler create(String mechanismName, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t\t  Map<String, Object> settings)\n\t\t\tthrows ClassNotFoundException, InstantiationException, IllegalAccessException {\n\t\treturn callbackHandlerFactory.create(mechanismName, session, repo, settings);\n\t}\n\n\tpublic Collection<String> filterMechanisms(Enumeration<SaslServerFactory> serverFactories,\n\t\t\t\t\t\t\t\t\t\t\t   XMPPResourceConnection session) {\n\t\treturn mechanismSelector.filterMechanisms(serverFactories, session);\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\t}\n\n\t@Override\n\tprotected synchronized void putService(Service s) {\n\t\tlog.config(\"Registering SASL mechanism '\" + s.getAlgorithm() + \"' with factory \" + s.getClassName());\n\t\tsuper.putService(s);\n\t}\n\n\t@Override\n\tprotected synchronized void removeService(Service s) {\n\t\tlog.config(\"Unregistering SASL mechanism '\" + s.getAlgorithm() + \"' with factory \" + s.getClassName());\n\t\tsuper.removeService(s);\n\t}\n\n\tprivate void registerFactory(SaslServerFactory factory) {\n\t\tString factoryClassName = factory.getClass().getName();\n\n\t\tList<Service> services = Arrays.stream(factory.getMechanismNames(new HashMap<>()))\n\t\t\t\t.map(name -> new Provider.Service(this, \"SaslServerFactory\", name, factoryClassName, null, null))\n\t\t\t\t.collect(Collectors.toList());\n\n\t\tservices.forEach(this::putService);\n\t\tsaslServerFactoriesServices.put(factory, services);\n\t}\n\n\tprivate void unregisterFactory(SaslServerFactory factory) {\n\t\tList<Service> services = saslServerFactoriesServices.remove(factory);\n\t\tif (services != null) {\n\t\t\tservices.forEach(this::removeService);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/XmppSaslException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth;\n\nimport tigase.db.AuthRepository;\nimport tigase.xml.Element;\n\nimport javax.security.sasl.SaslException;\n\npublic class XmppSaslException\n\t\textends SaslException {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic enum SaslError {\n\t\t/**\n\t\t * The receiving entity acknowledges that the authentication handshake has been aborted by the initiating\n\t\t * entity.\n\t\t */\n\t\taborted(\"aborted\"),\n\t\t/**\n\t\t * The account of the initiating entity has been temporarily disabled.\n\t\t */\n\t\taccount_disabled(\"account-disabled\"),\n\t\t/**\n\t\t * The authentication failed because the initiating entity provided credentials that have expired.\n\t\t */\n\t\tcredentials_expired(\"credentials-expired\"),\n\t\t/**\n\t\t * The mechanism requested by the initiating entity cannot be used unless the confidentiality and integrity of\n\t\t * the underlying stream are protected (typically via TLS).\n\t\t */\n\t\tencryption_required(\"encryption-required\"),\n\t\t/**\n\t\t * The data provided by the initiating entity could not be processed because the base 64 encoding is incorrect.\n\t\t */\n\t\tincorrect_encoding(\"incorrect-encoding\"),\n\t\t/**\n\t\t * The authzid provided by the initiating entity is invalid, either because it is incorrectly formatted or\n\t\t * because the initiating entity does not have permissions to authorize that ID.\n\t\t */\n\t\tinvalid_authzid(\"invalid-authzid\"),\n\t\t/**\n\t\t * The initiating entity did not specify a mechanism, or requested a mechanism that is not supported by the\n\t\t * receiving entity.\n\t\t */\n\t\tinvalid_mechanism(\"invalid-mechanism\"),\n\t\t/**\n\t\t * The request is malformed (e.g., the {@code <auth/>} element includes initial response data but the mechanism\n\t\t * does not allow that, or the data sent violates the syntax for the specified SASL mechanism).\n\t\t */\n\t\tmalformed_request(\"malformed-request\"),\n\t\t/**\n\t\t * The mechanism requested by the initiating entity is weaker than server policy permits for that initiating\n\t\t * entity.\n\t\t */\n\t\tmechanism_too_weak(\"mechanism-too-weak\"),\n\t\t/**\n\t\t * The authentication failed because the initiating entity did not provide proper credentials, or because some\n\t\t * generic authentication failure has occurred but the receiving entity does not wish to disclose specific\n\t\t * information about the cause of the failure.\n\t\t */\n\t\tnot_authorized(\"not-authorized\"),\n\t\t/**\n\t\t * The authentication failed because of a temporary error condition within the receiving entity, and it is\n\t\t * advisable for the initiating entity to try again later.\n\t\t */\n\t\ttemporary_auth_failure(\"temporary-auth-failure\");\n\n\t\tprivate final String elementName;\n\n\t\tpublic static final String XMLNS = \"urn:ietf:params:xml:ns:xmpp-sasl\";\n\n\t\tSaslError(String elementName) {\n\t\t\tthis.elementName = elementName;\n\t\t}\n\n\t\tpublic String getElementName() {\n\t\t\treturn elementName;\n\t\t}\n\n\t\tpublic Element getElement() {\n\t\t\tElement error = new Element(getElementName());\n\t\t\terror.setXMLNS(XMLNS);\n\t\t\treturn error;\n\t\t}\n\n\t}\n\tprivate SaslError saslError;\n\n\tpublic XmppSaslException(SaslError saslError) {\n\t\tsuper();\n\t\tthis.saslError = saslError;\n\t}\n\n\tpublic XmppSaslException(SaslError saslError, String detail) {\n\t\tsuper(detail);\n\t\tthis.saslError = saslError;\n\t}\n\n\tpublic SaslError getSaslError() {\n\t\treturn saslError;\n\t}\n\n\tpublic String getSaslErrorElementName() {\n\t\treturn saslError == null ? null : saslError.getElementName();\n\t}\n\n\tpublic static XmppSaslException getExceptionFor(AuthRepository.AccountStatus status) {\n\t\tswitch (status) {\n\t\t\tcase disabled:\n\t\t\tcase banned:\n\t\t\tcase spam:\n\t\t\tcase undefined_inactive:\n\t\t\t\treturn new XmppSaslException(SaslError.account_disabled, \"Your account has been disabled, please contact the administration\");\n\t\t\tcase system:\n\t\t\t\treturn new XmppSaslException(SaslError.invalid_authzid);\n\t\t\tcase pending:\n\t\t\t\treturn new XmppSaslException(SaslError.account_disabled, \"Your account hasn't been activated yet. Please cheek your email for activation link\");\n\t\t\tdefault:\n\t\t\t\treturn new XmppSaslException(SaslError.not_authorized);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/adhoc/AbstractCredentialsCommand.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.adhoc;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.component.adhoc.AdHocCommand;\nimport tigase.component.adhoc.AdHocCommandException;\nimport tigase.component.adhoc.AdHocResponse;\nimport tigase.component.adhoc.AdhHocRequest;\nimport tigase.component.modules.impl.AdHocCommandModule;\nimport tigase.db.AuthRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.CmdAcl;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic abstract class AbstractCredentialsCommand\n\t\timplements AdHocCommand {\n\n\tprotected static final String FIELD_JID = \"jid\";\n\tprotected static final String FIELD_CREDENTIAL_ID = \"credentialId\";\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.1.0\", note = \"Using CredentialID instead of username for clarity\")\n\tprotected static final String FIELD_USERNAME = FIELD_CREDENTIAL_ID;\n\tprotected static final String FIELD_PASSWORD = \"password\";\n\tprotected final Logger log = Logger.getLogger(this.getClass().getName());\n\t@Inject\n\tprotected AuthRepository authRepository;\n\n\t@Inject\n\tprivate AdHocCommandModule.ScriptCommandProcessor scriptCommandProcessor;\n\n\tprotected void checkIfCanModifyJID(final AdhHocRequest request, final BareJID jidToModify)\n\t\t\tthrows AdHocCommandException {\n\t\tif (!jidToModify.equals(request.getSender().getBareJID())) {\n\t\t\tlog.log(Level.FINEST, \"Cannot modify credentials of different user\");\n\t\t\tthrow new AdHocCommandException(Authorization.NOT_AUTHORIZED,\n\t\t\t\t\t\t\t\t\t\t\t\"You are not allowed to modify credentials \" + \"of user \" + jidToModify);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void execute(AdhHocRequest request, AdHocResponse response) throws AdHocCommandException {\n\t\ttry {\n\t\t\tfinal Element data = request.getCommand().getChild(\"x\", \"jabber:x:data\");\n\t\t\tif (request.getAction() != null && \"cancel\".equals(request.getAction())) {\n\t\t\t\tresponse.cancelSession();\n\t\t\t} else if (data == null) {\n\t\t\t\tprocessNoForm(request, response);\n\t\t\t} else {\n\t\t\t\tForm form = new Form(data);\n\t\t\t\tprocessForm(form, request, response);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.FINEST, \"Error during processing command\", e);\n\t\t\tthrow new AdHocCommandException(Authorization.INTERNAL_SERVER_ERROR, e.getMessage());\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isAllowedFor(JID jid) {\n\t\treturn scriptCommandProcessor.isAllowed(getNode(), jid);\n\t}\n\n\t@Override\n\tpublic CmdAcl getDefaultACL() {\n\t\treturn new CmdAcl(\"LOCAL\");\n\t}\n\n\tprotected abstract void processForm(Form form, AdhHocRequest request, AdHocResponse response)\n\t\t\tthrows TigaseDBException, AdHocCommandException;\n\n\tprotected abstract void processNoForm(AdhHocRequest request, AdHocResponse response) throws TigaseDBException;\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/adhoc/AddUserCredentials.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.adhoc;\n\nimport tigase.auth.credentials.Credentials;\nimport tigase.component.adhoc.AdHocCommandException;\nimport tigase.component.adhoc.AdHocResponse;\nimport tigase.component.adhoc.AdhHocRequest;\nimport tigase.db.TigaseDBException;\nimport tigase.form.Field;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.jid.BareJID;\n\n@Bean(name = AddUserCredentials.NODE, parent = SessionManager.class, active = true)\npublic class AddUserCredentials\n\t\textends AbstractCredentialsCommand {\n\n\tpublic static final String NODE = \"auth-credentials-add\";\n\n\tpublic AddUserCredentials() {\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"Add user credentials\";\n\t}\n\n\t@Override\n\tpublic String getNode() {\n\t\treturn NODE;\n\t}\n\n\t@Override\n\tprotected void processForm(Form form, AdhHocRequest request, AdHocResponse response)\n\t\t\tthrows TigaseDBException, AdHocCommandException {\n\t\tBareJID jid = BareJID.bareJIDInstanceNS(form.getAsString(FIELD_JID));\n\n\t\tcheckIfCanModifyJID(request, jid);\n\n\t\tString credentialId = form.getAsString(FIELD_CREDENTIAL_ID);\n\t\tString password = form.getAsString(FIELD_PASSWORD);\n\n\t\tif (credentialId == null || credentialId.trim().isEmpty()) {\n\t\t\tcredentialId = Credentials.DEFAULT_CREDENTIAL_ID;\n\t\t}\n\n\t\tif (password == null || credentialId.isEmpty()) {\n\t\t\tthrow new AdHocCommandException(Authorization.BAD_REQUEST, \"Passwor cannot be null\");\n\t\t}\n\n\t\tForm resp = new Form(\"result\", null, null);\n\n\t\tauthRepository.updateCredential(jid, credentialId.trim(), password);\n\n\t\tresp.addField(Field.fieldFixed(\"OK\"));\n\n\t\tresponse.getElements().add(resp.getElement());\n\t\tresponse.completeSession();\n\t}\n\n\t@Override\n\tprotected void processNoForm(AdhHocRequest request, AdHocResponse response) {\n\t\tForm form = new Form(\"form\", getName(), null);\n\n\t\tform.addField(Field.fieldJidSingle(FIELD_JID, null, \"The Jabber ID for the account\"));\n\t\tform.addField(Field.fieldJidSingle(FIELD_CREDENTIAL_ID, null, \"Credential ID\"));\n\t\tform.addField(Field.fieldJidSingle(FIELD_PASSWORD, null, \"Password\"));\n\n\t\tresponse.startSession();\n\t\tresponse.getElements().add(form.getElement());\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/adhoc/DeleteUserCredentials.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.adhoc;\n\nimport tigase.auth.credentials.Credentials;\nimport tigase.component.adhoc.AdHocCommandException;\nimport tigase.component.adhoc.AdHocResponse;\nimport tigase.component.adhoc.AdhHocRequest;\nimport tigase.db.TigaseDBException;\nimport tigase.form.Field;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xmpp.jid.BareJID;\n\n@Bean(name = DeleteUserCredentials.NODE, parent = SessionManager.class, active = true)\npublic class DeleteUserCredentials\n\t\textends AbstractCredentialsCommand {\n\n\tpublic static final String NODE = \"auth-credentials-delete\";\n\n\tpublic DeleteUserCredentials() {\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"Delete user credentials\";\n\t}\n\n\t@Override\n\tpublic String getNode() {\n\t\treturn NODE;\n\t}\n\n\t@Override\n\tprotected void processForm(Form form, AdhHocRequest request, AdHocResponse response)\n\t\t\tthrows TigaseDBException, AdHocCommandException {\n\t\tBareJID jid = BareJID.bareJIDInstanceNS(form.getAsString(FIELD_JID));\n\n\t\tcheckIfCanModifyJID(request, jid);\n\n\t\tString credentialId = form.getAsString(FIELD_CREDENTIAL_ID);\n\n\t\tif (credentialId == null || credentialId.trim().isEmpty()) {\n\t\t\tcredentialId = Credentials.DEFAULT_CREDENTIAL_ID;\n\t\t}\n\n\t\tForm resp = new Form(\"result\", null, null);\n\n\t\tauthRepository.removeCredential(jid, credentialId.trim());\n\n\t\tresp.addField(Field.fieldFixed(\"OK\"));\n\n\t\tresponse.getElements().add(resp.getElement());\n\t\tresponse.completeSession();\n\t}\n\n\t@Override\n\tprotected void processNoForm(AdhHocRequest request, AdHocResponse response) {\n\t\tForm form = new Form(\"form\", getName(), null);\n\n\t\tform.addField(Field.fieldJidSingle(FIELD_JID, null, \"The Jabber ID for the account\"));\n\t\tform.addField(Field.fieldJidSingle(FIELD_CREDENTIAL_ID, null, \"Credential ID\"));\n\n\t\tresponse.startSession();\n\t\tresponse.getElements().add(form.getElement());\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/adhoc/ShowUserCredentials.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.adhoc;\n\nimport tigase.component.adhoc.AdHocCommandException;\nimport tigase.component.adhoc.AdHocResponse;\nimport tigase.component.adhoc.AdhHocRequest;\nimport tigase.db.TigaseDBException;\nimport tigase.form.Field;\nimport tigase.form.Fields;\nimport tigase.form.Form;\nimport tigase.form.MultiItemForm;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.Collection;\n\n@Bean(name = ShowUserCredentials.NODE, parent = SessionManager.class, active = true)\npublic class ShowUserCredentials\n\t\textends AbstractCredentialsCommand {\n\n\tpublic static final String NODE = \"auth-credentials-list\";\n\n\tpublic ShowUserCredentials() {\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"List user credentials\";\n\t}\n\n\t@Override\n\tpublic String getNode() {\n\t\treturn NODE;\n\t}\n\n\t@Override\n\tprotected void processForm(Form form, AdhHocRequest request, AdHocResponse response)\n\t\t\tthrows TigaseDBException, AdHocCommandException {\n\t\tBareJID jid = BareJID.bareJIDInstanceNS(form.getAsString(FIELD_JID));\n\n\t\tcheckIfCanModifyJID(request, jid);\n\n\t\tMultiItemForm resp = new MultiItemForm();\n\t\tresp.setType(\"form\");\n\n\t\tCollection<String> credentialIds = authRepository.getCredentialIds(jid);\n\t\tfor (String credentialId : credentialIds) {\n\t\t\tFields ff = new Fields();\n\t\t\tff.addField(Field.fieldTextSingle(\"credentialId\", credentialId, \"Credential ID\"));\n\t\t\tresp.addItem(ff);\n\t\t}\n\n\t\tresponse.getElements().add(resp.getElement());\n\t\tresponse.completeSession();\n\t}\n\n\t@Override\n\tprotected void processNoForm(AdhHocRequest request, AdHocResponse response) {\n\t\tForm form = new Form(\"form\", getName(), null);\n\n\t\tform.addField(Field.fieldJidSingle(FIELD_JID, null, \"The Jabber ID for the account\"));\n\n\t\tresponse.startSession();\n\t\tresponse.getElements().add(form.getElement());\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/callbacks/AuthorizationIdCallback.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.callbacks;\n\nimport javax.security.auth.callback.Callback;\n\npublic class AuthorizationIdCallback\n\t\timplements Callback, java.io.Serializable {\n\n\tprivate final String prompt;\n\tprivate String authzId;\n\n\tpublic AuthorizationIdCallback(String prompt, String authzId) {\n\t\tthis.prompt = prompt;\n\t\tthis.authzId = authzId;\n\t}\n\n\tpublic String getAuthzId() {\n\t\treturn authzId;\n\t}\n\n\tpublic void setAuthzId(String authzId) {\n\t\tthis.authzId = authzId;\n\t}\n\n\tpublic String getPrompt() {\n\t\treturn prompt;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/callbacks/CallbackHandlerFactoryIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.callbacks;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.xmpp.XMPPResourceConnection;\n\nimport javax.security.auth.callback.CallbackHandler;\nimport java.util.Map;\n\n/**\n * Created by andrzej on 20.03.2017.\n */\npublic interface CallbackHandlerFactoryIfc {\n\n\tCallbackHandler create(String mechanismName, XMPPResourceConnection session, NonAuthUserRepository repo)\n\t\t\tthrows ClassNotFoundException, InstantiationException, IllegalAccessException;\n\n\t@Deprecated\n\t@TigaseDeprecated(note = \"Settings are not use anymore\", since = \"8.1.0\")\n\tdefault CallbackHandler create(String mechanismName, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t   Map<String, Object> settings)\n\t\t\tthrows ClassNotFoundException, InstantiationException, IllegalAccessException {\n\t\treturn this.create(mechanismName, session, repo);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/callbacks/ChannelBindingCallback.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.callbacks;\n\nimport tigase.auth.mechanisms.AbstractSaslSCRAM;\n\nimport javax.security.auth.callback.Callback;\n\npublic class ChannelBindingCallback\n\t\timplements Callback, java.io.Serializable {\n\n\tprivate final String prompt;\n\tprivate final AbstractSaslSCRAM.BindType requestedBindType;\n\tprivate byte[] bindingData;\n\n\tpublic ChannelBindingCallback(String prompt, AbstractSaslSCRAM.BindType requestedBindType) {\n\t\tthis.prompt = prompt;\n\t\tthis.requestedBindType = requestedBindType;\n\t}\n\n\tpublic byte[] getBindingData() {\n\t\treturn bindingData;\n\t}\n\n\tpublic void setBindingData(byte[] bindingData) {\n\t\tthis.bindingData = bindingData;\n\t}\n\n\tpublic String getPrompt() {\n\t\treturn prompt;\n\t}\n\n\tpublic AbstractSaslSCRAM.BindType getRequestedBindType() {\n\t\treturn requestedBindType;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/callbacks/PBKDIterationsCallback.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.callbacks;\n\nimport javax.security.auth.callback.Callback;\n\npublic class PBKDIterationsCallback\n\t\timplements Callback, java.io.Serializable {\n\n\tprivate static final long serialVersionUID = -4342673378785456908L;\n\n\tprivate int interations;\n\n\tprivate String prompt;\n\n\tpublic PBKDIterationsCallback(String prompt) {\n\t\tthis.prompt = prompt;\n\t}\n\n\t/**\n\t * @return the interations\n\t */\n\tpublic int getInterations() {\n\t\treturn interations;\n\t}\n\n\t/**\n\t * @param interations the interations to set\n\t */\n\tpublic void setInterations(int interations) {\n\t\tthis.interations = interations;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/callbacks/ReplaceServerKeyCallback.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.callbacks;\n\nimport javax.security.auth.callback.Callback;\n\npublic class ReplaceServerKeyCallback\n\t\timplements Callback {\n\n\tprivate byte[] newServerKey;\n\n\tpublic byte[] getNewServerKey() {\n\t\treturn newServerKey;\n\t}\n\n\tpublic void setNewServerKey(byte[] newServerKey) {\n\t\tthis.newServerKey = newServerKey;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/callbacks/SaltCallback.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.callbacks;\n\nimport javax.security.auth.callback.Callback;\n\npublic class SaltCallback\n\t\timplements Callback, java.io.Serializable {\n\n\tprivate static final long serialVersionUID = -4342673378785456908L;\n\n\tprivate String prompt;\n\n\tprivate byte[] salt;\n\n\tpublic SaltCallback(String prompt) {\n\t\tthis.prompt = prompt;\n\t}\n\n\t/**\n\t * @return the salt\n\t */\n\tpublic byte[] getSalt() {\n\t\treturn salt;\n\t}\n\n\t/**\n\t * @param salt the salt to set\n\t */\n\tpublic void setSalt(byte[] salt) {\n\t\tthis.salt = salt;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/callbacks/ServerKeyCallback.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.callbacks;\n\nimport javax.security.auth.callback.Callback;\n\npublic class ServerKeyCallback\n\t\timplements Callback, java.io.Serializable {\n\n\tprivate final String prompt;\n\n\tprivate byte[] serverKey;\n\n\tpublic ServerKeyCallback(String prompt) {\n\t\tthis.prompt = prompt;\n\t}\n\n\t/**\n\t * @return the ServerKey\n\t */\n\tpublic byte[] getServerKey() {\n\t\treturn serverKey;\n\t}\n\n\t/**\n\t * @param serverKey the ServerKey to set\n\t */\n\tpublic void setServerKey(byte[] serverKey) {\n\t\tthis.serverKey = serverKey;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/callbacks/SharedSecretKeyCallback.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.callbacks;\n\nimport javax.security.auth.callback.Callback;\n\npublic class SharedSecretKeyCallback implements Callback, java.io.Serializable {\n\n\tprivate byte[] secret;\n\n\tpublic SharedSecretKeyCallback() {}\n\n\tpublic byte[] getSecret() {\n\t\treturn secret;\n\t}\n\n\tpublic void setSecret(byte[] secret) {\n\t\tthis.secret = secret;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/callbacks/StoredKeyCallback.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.callbacks;\n\nimport javax.security.auth.callback.Callback;\n\npublic class StoredKeyCallback\n\t\timplements Callback, java.io.Serializable {\n\n\tprivate final String prompt;\n\n\tprivate byte[] storedKey;\n\n\tpublic StoredKeyCallback(String prompt) {\n\t\tthis.prompt = prompt;\n\t}\n\n\t/**\n\t * @return the StoredKey\n\t */\n\tpublic byte[] getStoredKey() {\n\t\treturn storedKey;\n\t}\n\n\t/**\n\t * @param storedKey the StoredKey to set\n\t */\n\tpublic void setStoredKey(byte[] storedKey) {\n\t\tthis.storedKey = storedKey;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/callbacks/ValidateCertificateData.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.callbacks;\n\nimport tigase.xmpp.jid.BareJID;\n\nimport javax.security.auth.callback.Callback;\n\npublic class ValidateCertificateData\n\t\timplements Callback {\n\n\tprivate boolean authorized;\n\n\tprivate String authorizedID;\n\n\tprivate BareJID defaultAuthzid;\n\n\tpublic ValidateCertificateData() {\n\t}\n\n\tpublic ValidateCertificateData(BareJID jid) {\n\t\tsetDefaultAuthzid(jid);\n\t}\n\n\tpublic String getAuthorizedID() {\n\t\treturn authorizedID;\n\t}\n\n\tpublic void setAuthorizedID(String authorizedID) {\n\t\tthis.authorizedID = authorizedID;\n\t}\n\n\tpublic BareJID getDefaultAuthzid() {\n\t\treturn defaultAuthzid;\n\t}\n\n\tpublic void setDefaultAuthzid(BareJID defaultAuthzid) {\n\t\tthis.defaultAuthzid = defaultAuthzid;\n\t}\n\n\tpublic boolean isAuthorized() {\n\t\treturn authorized;\n\t}\n\n\tpublic void setAuthorized(boolean authorized) {\n\t\tthis.authorized = authorized;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/callbacks/VerifyPasswordCallback.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.callbacks;\n\nimport javax.security.auth.callback.Callback;\n\n/**\n * Class for validate password. Called by SASL mechanisms. If given password is valid then {@linkplain\n * VerifyPasswordCallback#setVerified(boolean) setVerified(true)} must be called.\n */\npublic class VerifyPasswordCallback\n\t\timplements Callback {\n\n\tprivate final String password;\n\n\tprivate boolean verified = false;\n\n\tpublic VerifyPasswordCallback(final String password) {\n\t\tthis.password = password;\n\t}\n\n\tpublic String getPassword() {\n\t\treturn password;\n\t}\n\n\tpublic boolean isVerified() {\n\t\treturn verified;\n\t}\n\n\tpublic void setVerified(boolean verified) {\n\t\tthis.verified = verified;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/callbacks/XMPPSessionCallback.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.callbacks;\n\nimport tigase.xmpp.XMPPResourceConnection;\n\nimport javax.security.auth.callback.Callback;\n\npublic class XMPPSessionCallback\n\t\timplements Callback, java.io.Serializable {\n\n\tprivate final String prompt;\n\tprivate XMPPResourceConnection session;\n\n\tpublic XMPPSessionCallback(String prompt) {\n\t\tthis.prompt = prompt;\n\t}\n\n\tpublic String getPrompt() {\n\t\treturn prompt;\n\t}\n\n\tpublic XMPPResourceConnection getSession() {\n\t\treturn session;\n\t}\n\n\tpublic void setSession(XMPPResourceConnection session) {\n\t\tthis.session = session;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/credentials/Credentials.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.credentials;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.xmpp.jid.BareJID;\n\nimport static tigase.db.AuthRepository.AccountStatus;\n\n/**\n * Interface implemented by classes handling user login credentials. In implementations of this interface multiple\n * credentials for single account may be stored but for single credentialId, ie. different credentials for different\n * authentication mechanisms.\n */\npublic interface Credentials {\n\n\tString DEFAULT_CREDENTIAL_ID = \"default\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.1.0\", note = \"Username changed to CredentialId\")\n\tString DEFAULT_USERNAME = DEFAULT_CREDENTIAL_ID;\n\n\t/**\n\t * Checks if account can perform logging-in\n\t */\n\tboolean canLogin();\n\n\t/**\n\t * @return account status of the account\n\t */\n\tAccountStatus getAccountStatus();\n\n\t/**\n\t * Find a credential for specified encryption mechanism\n\t *\n\t * @return instance of an entry if available or null\n\t */\n\tEntry getEntryForMechanism(String mechanism);\n\n\t/**\n\t * Returns first available instance of credentials entry\n\t *\n\t * @return first available instance of credentials entry\n\t */\n\tEntry getFirst();\n\n\t/**\n\t * Returns bare jid of an account\n\t *\n\t * @return bare jid of an account\n\t */\n\tBareJID getUser();\n\n\t/**\n\t * Checks if account is disabled\n\t */\n\tboolean isAccountDisabled();\n\n\t/**\n\t * Interface implemented by credentials decoder converting from value stored in database to the form represented by\n\t * implementation of Entry interface.\n\t */\n\tinterface Decoder<E extends Entry> {\n\n\t\t/**\n\t\t * Decode credentials stored in database to more suitable form.\n\t\t *\n\t\t * @return credentials stored in database in more suitable form\n\t\t */\n\t\tE decode(BareJID user, String value);\n\n\t\t/**\n\t\t * Name of the encryption mechanism for which decoder works\n\t\t *\n\t\t * @return name of the encryption mechanism for which decoder works\n\t\t */\n\t\tString getName();\n\n\t}\n\n\t/**\n\t * Interface implemented by credentials encoder converting them from plaintext value to encoded form stored in the\n\t * database.\n\t */\n\tinterface Encoder<E extends Entry> {\n\n\t\t/**\n\t\t * Encodes entry to store in database.\n\t\t *\n\t\t * @param user for which encode\n\t\t * @param password plaintext password to encode\n\t\t *\n\t\t * @return encoded authentication data.\n\t\t */\n\t\tString encode(BareJID user, String password);\n\n\t\t/**\n\t\t * Encodes entry to store in database.\n\t\t *\n\t\t * @param user for which encode\n\t\t * @param entry to encode\n\t\t *\n\t\t * @return encoded authentication data.\n\t\t */\n\t\tString encode(BareJID user, E entry);\n\n\t\t/**\n\t\t * Name of the encryption mechanism for which encoder works\n\t\t *\n\t\t * @return name of the encryption mechanism for which encoder works\n\t\t */\n\t\tString getName();\n\n\t}\n\n\t/**\n\t * Interface required to be implemented by classes representing credential entry.\n\t */\n\tinterface Entry {\n\n\t\t/**\n\t\t * Name of the encryption mechanism used to encode stored credentials.\n\t\t *\n\t\t * Note: Value returned by this method may be equal to SASL mechanism name used to encode this value,\n\t\t * but doesn't have to be only one of SASL mechanism name, ie. for passwords encoded for PLAIN mechanism\n\t\t * not stored in plain format in the repository.\n\t\t */\n\t\tString getMechanism();\n\n\t\t/**\n\t\t * Check if plaintext password will match stored credential\n\t\t */\n\t\tboolean verifyPlainPassword(String plain);\n\n\t}\n\n\t/**\n\t * Interface implemented by classes used as DTO for credentials read from repository.s\n\t */\n\tinterface RawEntry {\n\n\t\t/**\n\t\t * Name of the encryption mechanism used to encode stored credentials.\n\t\t *\n\t\t * Note: Value returned by this method may be equal to SASL mechanism name used to encode this value,\n\t\t * but doesn't have to be only one of SASL mechanism name, ie. for passwords encoded for PLAIN mechanism\n\t\t * not stored in plain format in the repository.\n\t\t */\n\t\tString getMechanism();\n\n\t\t/**\n\t\t * Encoded value\n\t\t */\n\t\tString getValue();\n\n\t\t/**\n\t\t * Checks if the provided string matches the name of the encryption mechanism used to encode data for storage\n\t\t * in the repository.\n\t\t */\n\t\tdefault boolean isForMechanism(String mechanism) {\n\t\t\treturn mechanism.equals(getMechanism());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/credentials/entries/MD5PasswordCredentialsEntry.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.credentials.entries;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.auth.CredentialsDecoderBean;\nimport tigase.auth.CredentialsEncoderBean;\nimport tigase.auth.credentials.Credentials;\nimport tigase.kernel.beans.Bean;\nimport tigase.util.Algorithms;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Deprecated\n@TigaseDeprecated(since = \"8.4.0\", removeIn = \"9.0.0\", note = \"SCRAM should be used for maximum security; it's possible to migrate to SCRAM seamlessly\")\npublic class MD5PasswordCredentialsEntry\n\t\timplements Credentials.Entry {\n\n\tprivate static final Logger log = Logger.getLogger(MD5PasswordCredentialsEntry.class.getCanonicalName());\n\n\tprivate final String passwordHash;\n\n\tpublic MD5PasswordCredentialsEntry(String passwordHash) {\n\t\tthis.passwordHash = passwordHash;\n\t}\n\n\t@Override\n\tpublic String getMechanism() {\n\t\treturn \"MD5-PASSWORD\";\n\t}\n\n\t@Override\n\tpublic boolean verifyPlainPassword(String plain) {\n\t\ttry {\n\t\t\tbyte[] hash = MessageDigest.getInstance(\"MD5\").digest(plain.getBytes(StandardCharsets.UTF_8));\n\t\t\treturn passwordHash.equalsIgnoreCase(Algorithms.bytesToHex(hash));\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.WARNING, \"failed to verify password digest\", ex);\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Bean(name = \"MD5-PASSWORD\", parent = CredentialsDecoderBean.class, active = false)\n\tpublic static class Decoder\n\t\t\timplements Credentials.Decoder<MD5PasswordCredentialsEntry> {\n\n\t\t@Override\n\t\tpublic MD5PasswordCredentialsEntry decode(BareJID user, String value) {\n\t\t\treturn new MD5PasswordCredentialsEntry(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn \"MD5-PASSWORD\";\n\t\t}\n\t}\n\n\t@Bean(name = \"MD5-PASSWORD\", parent = CredentialsEncoderBean.class, active = false)\n\tpublic static class Encoder\n\t\t\timplements Credentials.Encoder<MD5PasswordCredentialsEntry> {\n\n\t\t@Override\n\t\tpublic String encode(BareJID user, String password) {\n\t\t\ttry {\n\t\t\t\tbyte[] hash = MessageDigest.getInstance(\"MD5\").digest(password.getBytes(StandardCharsets.UTF_8));\n\t\t\t\treturn Algorithms.bytesToHex(hash);\n\t\t\t} catch (Exception ex) {\n\t\t\t\tthrow new RuntimeException(\"failed to generate password hash\", ex);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic String encode(BareJID user, MD5PasswordCredentialsEntry entry) {\n\t\t\treturn entry.passwordHash;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn \"MD5-PASSWORD\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/credentials/entries/MD5UserIdPasswordCredentialsEntry.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.credentials.entries;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.auth.CredentialsDecoderBean;\nimport tigase.auth.CredentialsEncoderBean;\nimport tigase.auth.credentials.Credentials;\nimport tigase.kernel.beans.Bean;\nimport tigase.util.Algorithms;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Deprecated\n@TigaseDeprecated(since = \"8.4.0\", removeIn = \"9.0.0\", note = \"SCRAM should be used for maximum security; it's possible to migrate to SCRAM seamlessly\")\npublic class MD5UserIdPasswordCredentialsEntry\n\t\timplements Credentials.Entry {\n\n\tprivate static final Logger log = Logger.getLogger(MD5UserIdPasswordCredentialsEntry.class.getCanonicalName());\n\tprivate final String passwordHash;\n\tprivate final BareJID user;\n\n\tpublic MD5UserIdPasswordCredentialsEntry(BareJID user, String passwordHash) {\n\t\tthis.user = user;\n\t\tthis.passwordHash = passwordHash;\n\t}\n\n\t@Override\n\tpublic String getMechanism() {\n\t\treturn \"MD5-USERID-PASSWORD\";\n\t}\n\n\t@Override\n\tpublic boolean verifyPlainPassword(String plain) {\n\t\ttry {\n\t\t\tbyte[] hash = MessageDigest.getInstance(\"MD5\")\n\t\t\t\t\t.digest((user.toString() + plain).getBytes(StandardCharsets.UTF_8));\n\t\t\treturn passwordHash.equalsIgnoreCase(Algorithms.bytesToHex(hash));\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.WARNING, \"failed to verify password digest\", ex);\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Bean(name = \"MD5-USERID-PASSWORD\", parent = CredentialsDecoderBean.class, active = false)\n\tpublic static class Decoder\n\t\t\timplements Credentials.Decoder<MD5UserIdPasswordCredentialsEntry> {\n\n\t\t@Override\n\t\tpublic MD5UserIdPasswordCredentialsEntry decode(BareJID user, String value) {\n\t\t\treturn new MD5UserIdPasswordCredentialsEntry(user, value);\n\t\t}\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn \"MD5-USERID-PASSWORD\";\n\t\t}\n\t}\n\n\t@Bean(name = \"MD5-USERID-PASSWORD\", parent = CredentialsEncoderBean.class, active = false)\n\tpublic static class Encoder\n\t\t\timplements Credentials.Encoder<MD5UserIdPasswordCredentialsEntry> {\n\n\t\t@Override\n\t\tpublic String encode(BareJID user, MD5UserIdPasswordCredentialsEntry entry) {\n\t\t\treturn entry.passwordHash;\n\t\t}\n\n\t\t@Override\n\t\tpublic String encode(BareJID user, String password) {\n\t\t\ttry {\n\t\t\t\tbyte[] hash = MessageDigest.getInstance(\"MD5\")\n\t\t\t\t\t\t.digest((user.toString() + password).getBytes(StandardCharsets.UTF_8));\n\t\t\t\treturn Algorithms.bytesToHex(hash);\n\t\t\t} catch (Exception ex) {\n\t\t\t\tthrow new RuntimeException(\"failed to generate password hash\", ex);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn \"MD5-USERID-PASSWORD\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/credentials/entries/MD5UsernamePasswordCredentialsEntry.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.credentials.entries;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.auth.CredentialsDecoderBean;\nimport tigase.auth.CredentialsEncoderBean;\nimport tigase.auth.credentials.Credentials;\nimport tigase.kernel.beans.Bean;\nimport tigase.util.Algorithms;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Deprecated\n@TigaseDeprecated(since = \"8.4.0\", removeIn = \"9.0.0\", note = \"SCRAM should be used for maximum security; it's possible to migrate to SCRAM seamlessly\")\npublic class MD5UsernamePasswordCredentialsEntry\n\t\timplements Credentials.Entry {\n\n\tprivate static final Logger log = Logger.getLogger(MD5UsernamePasswordCredentialsEntry.class.getCanonicalName());\n\tprivate final String passwordHash;\n\tprivate final BareJID user;\n\n\tprotected static String getUsername(BareJID user) {\n\t\tif (user.getLocalpart() == null) {\n\t\t\treturn user.getDomain();\n\t\t} else {\n\t\t\treturn user.getLocalpart();\n\t\t}\n\t}\n\n\tpublic MD5UsernamePasswordCredentialsEntry(BareJID user, String passwordHash) {\n\t\tthis.user = user;\n\t\tthis.passwordHash = passwordHash;\n\t}\n\n\t@Override\n\tpublic String getMechanism() {\n\t\treturn \"MD5-USERNAME-PASSWORD\";\n\t}\n\n\tpublic String getPasswordHash() {\n\t\treturn passwordHash;\n\t}\n\n\t@Override\n\tpublic boolean verifyPlainPassword(String plain) {\n\t\ttry {\n\t\t\tbyte[] hash = MessageDigest.getInstance(\"MD5\")\n\t\t\t\t\t.digest((getUsername(user) + plain).getBytes(StandardCharsets.UTF_8));\n\t\t\treturn passwordHash.equalsIgnoreCase(Algorithms.bytesToHex(hash));\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.WARNING, \"failed to verify password digest\", ex);\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Bean(name = \"MD5-USERNAME-PASSWORD\", parent = CredentialsDecoderBean.class, active = false)\n\tpublic static class Decoder\n\t\t\timplements Credentials.Decoder<MD5UsernamePasswordCredentialsEntry> {\n\n\t\t@Override\n\t\tpublic MD5UsernamePasswordCredentialsEntry decode(BareJID user, String value) {\n\t\t\treturn new MD5UsernamePasswordCredentialsEntry(user, value);\n\t\t}\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn \"MD5-USERNAME-PASSWORD\";\n\t\t}\n\t}\n\n\t@Bean(name = \"MD5-USERNAME-PASSWORD\", parent = CredentialsEncoderBean.class, active = false)\n\tpublic static class Encoder\n\t\t\timplements Credentials.Encoder<MD5UsernamePasswordCredentialsEntry> {\n\n\t\t@Override\n\t\tpublic String encode(BareJID user, MD5UsernamePasswordCredentialsEntry entry) {\n\t\t\treturn entry.getPasswordHash();\n\t\t}\n\n\t\t@Override\n\t\tpublic String encode(BareJID user, String password) {\n\t\t\ttry {\n\t\t\t\tbyte[] hash = MessageDigest.getInstance(\"MD5\")\n\t\t\t\t\t\t.digest((getUsername(user) + password).getBytes(StandardCharsets.UTF_8));\n\t\t\t\treturn Algorithms.bytesToHex(hash);\n\t\t\t} catch (Exception ex) {\n\t\t\t\tthrow new RuntimeException(\"failed to generate password hash\", ex);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn \"MD5-USERNAME-PASSWORD\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/credentials/entries/PlainCredentialsEntry.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.credentials.entries;\n\nimport tigase.auth.CredentialsDecoderBean;\nimport tigase.auth.CredentialsEncoderBean;\nimport tigase.auth.credentials.Credentials;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.xmpp.jid.BareJID;\n\npublic class PlainCredentialsEntry\n\t\timplements Credentials.Entry {\n\n\tprivate final String password;\n\n\tpublic PlainCredentialsEntry(String password) {\n\t\tthis.password = password;\n\t}\n\n\t@Override\n\tpublic String getMechanism() {\n\t\treturn \"PLAIN\";\n\t}\n\n\tpublic String getPassword() {\n\t\treturn password;\n\t}\n\n\t@Override\n\tpublic boolean verifyPlainPassword(String plain) {\n\t\treturn password == plain || password.equals(plain);\n\t}\n\n\t@Bean(name = \"PLAIN\", parent = CredentialsDecoderBean.class, active = true)\n\tpublic static class Decoder\n\t\t\timplements Credentials.Decoder<PlainCredentialsEntry> {\n\n\t\t@ConfigField(desc = \"Mechanism name\")\n\t\tprivate String name;\n\n\t\t@Override\n\t\tpublic PlainCredentialsEntry decode(BareJID user, String value) {\n\t\t\treturn new PlainCredentialsEntry(value);\n\t\t}\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn name;\n\t\t}\n\t}\n\n\t@Bean(name = \"PLAIN\", parent = CredentialsEncoderBean.class, active = false)\n\tpublic static class Encoder\n\t\t\timplements Credentials.Encoder<PlainCredentialsEntry> {\n\n\t\t@ConfigField(desc = \"Mechanism name\")\n\t\tprivate String name;\n\n\t\t@Override\n\t\tpublic String encode(BareJID user, String password) {\n\t\t\treturn password;\n\t\t}\n\n\t\t@Override\n\t\tpublic String encode(BareJID user, PlainCredentialsEntry entry) {\n\t\t\treturn entry.getPassword();\n\t\t}\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn name;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/credentials/entries/ScramCredentialsEntry.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.credentials.entries;\n\nimport tigase.auth.credentials.Credentials;\nimport tigase.auth.mechanisms.SCRAMHelper;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.util.Base64;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.security.InvalidKeyException;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.SecureRandom;\nimport java.util.Arrays;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class ScramCredentialsEntry\n\t\timplements Credentials.Entry {\n\n\tprivate static final Logger log = Logger.getLogger(ScramCredentialsEntry.class.getCanonicalName());\n\n\tprivate final String algorithm;\n\tprivate final int iterations;\n\tprivate final byte[] salt;\n\tprivate final byte[] serverKey;\n\tprivate final byte[] storedKey;\n\n\tpublic ScramCredentialsEntry(String algorithm, PlainCredentialsEntry entry)\n\t\t\tthrows NoSuchAlgorithmException, InvalidKeyException {\n\t\tfinal SecureRandom random = new SecureRandom();\n\t\tthis.algorithm = algorithm;\n\t\tthis.iterations = 4096;\n\t\tthis.salt = new byte[10];\n\t\trandom.nextBytes(salt);\n\n\t\tvar authData = SCRAMHelper.encodePlainPassword(algorithm, salt, iterations, entry.getPassword());\n\t\tthis.storedKey = authData.storedKey();\n\t\tthis.serverKey = authData.serverKey();\n\t}\n\n\tpublic ScramCredentialsEntry(String algorithm, byte[] salt, int iterations, byte[] saltedPassword)\n\t\t\tthrows NoSuchAlgorithmException, InvalidKeyException {\n\t\tthis.algorithm = algorithm;\n\t\tthis.iterations = iterations;\n\t\tthis.salt = salt;\n\n\t\tvar authData = SCRAMHelper.transcode(algorithm, saltedPassword);\n\t\tthis.storedKey = authData.storedKey();\n\t\tthis.serverKey = authData.serverKey();\n\t}\n\n\tpublic ScramCredentialsEntry(String algorithm, byte[] salt, int iterations, byte[] storedKey, byte[] serverKey) {\n\t\tthis.algorithm = algorithm;\n\t\tthis.iterations = iterations;\n\t\tthis.salt = salt;\n\t\tthis.storedKey = storedKey;\n\t\tthis.serverKey = serverKey;\n\t}\n\n\tpublic int getIterations() {\n\t\treturn iterations;\n\t}\n\n\t@Override\n\tpublic String getMechanism() {\n\t\treturn \"SCRAM-\" + algorithm;\n\t}\n\n\tpublic byte[] getSalt() {\n\t\treturn salt;\n\t}\n\n\tpublic byte[] getServerKey() {\n\t\treturn serverKey;\n\t}\n\n\tpublic byte[] getStoredKey() {\n\t\treturn storedKey;\n\t}\n\n\t@Override\n\tpublic boolean verifyPlainPassword(String password) {\n\t\ttry {\n\t\t\tvar expAuthData = SCRAMHelper.encodePlainPassword(algorithm, salt, iterations, password);\n\t\t\treturn Arrays.equals(this.serverKey, expAuthData.serverKey()) &&\n\t\t\t\t\tArrays.equals(this.storedKey, expAuthData.storedKey());\n\t\t} catch (InvalidKeyException | NoSuchAlgorithmException ex) {\n\t\t\tlog.log(Level.FINE, \"Password comparison failed\", ex);\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static class Decoder\n\t\t\timplements Credentials.Decoder<ScramCredentialsEntry> {\n\n\t\t@ConfigField(desc = \"Hash algorithm\")\n\t\tprivate String algorithm;\n\t\t@ConfigField(desc = \"Mechanism name\")\n\t\tprivate String name;\n\n\t\tpublic Decoder() {\n\n\t\t}\n\n\t\tprotected Decoder(String algorithm) {\n\t\t\tthis.algorithm = algorithm;\n\t\t}\n\n\t\t@Override\n\t\tpublic ScramCredentialsEntry decode(BareJID user, String value) {\n\t\t\tbyte[] salt = null;\n\t\t\tbyte[] saltedPassword = null;\n\t\t\tbyte[] storedKey = null;\n\t\t\tbyte[] serverKey = null;\n\t\t\tint iterations = 0;\n\n\t\t\tint pos = 0;\n\t\t\twhile (pos < value.length()) {\n\t\t\t\tchar c = value.charAt(pos);\n\t\t\t\tint x = value.indexOf(\",\", pos + 2);\n\t\t\t\tString part = value.substring(pos + 2, x == -1 ? value.length() : x);\n\n\t\t\t\tswitch (c) {\n\t\t\t\t\tcase 's':\n\t\t\t\t\t\tsalt = Base64.decode(part);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'i':\n\t\t\t\t\t\titerations = Integer.parseInt(part);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'p':\n\t\t\t\t\t\tsaltedPassword = Base64.decode(part);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 't':\n\t\t\t\t\t\tstoredKey = Base64.decode(part);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'e':\n\t\t\t\t\t\tserverKey = Base64.decode(part);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (x == -1) {\n\t\t\t\t\tbreak;\n\t\t\t\t} else {\n\t\t\t\t\tpos = x + 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ((storedKey == null || serverKey == null) && saltedPassword != null) {\n\t\t\t\treturn newInstance(salt, iterations, saltedPassword);\n\t\t\t} else if (storedKey != null && serverKey != null) {\n\t\t\t\treturn newInstance(salt, iterations, storedKey, serverKey);\n\t\t\t} else {\n\t\t\t\tthrow new RuntimeException(\"saltedPassword or storedKey&serverKey pair must be not null.\");\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn name;\n\t\t}\n\n\t\tprotected ScramCredentialsEntry newInstance(byte[] salt, int iterations, byte[] saltedPassword) {\n\t\t\ttry {\n\t\t\t\treturn new ScramCredentialsEntry(algorithm, salt, iterations, saltedPassword);\n\t\t\t} catch (NoSuchAlgorithmException | InvalidKeyException e) {\n\t\t\t\tthrow new RuntimeException(e);\n\t\t\t}\n\t\t}\n\n\t\tprotected ScramCredentialsEntry newInstance(byte[] salt, int iterations, byte[] storedKey, byte[] serverKey) {\n\t\t\treturn new ScramCredentialsEntry(algorithm, salt, iterations, storedKey, serverKey);\n\t\t}\n\t}\n\n\tpublic static class Encoder\n\t\t\timplements Credentials.Encoder<ScramCredentialsEntry> {\n\n\t\t@ConfigField(desc = \"Number of iterations\")\n\t\tprivate final int iterations = 4096;\n\t\tprivate final SecureRandom random = new SecureRandom();\n\t\t@ConfigField(desc = \"Hash algorithm\")\n\t\tprivate String algorithm;\n\t\t@ConfigField(desc = \"Mechanism name\")\n\t\tprivate String name;\n\n\t\tpublic Encoder() {\n\n\t\t}\n\n\t\tprotected Encoder(String algorithm) {\n\t\t\tthis.algorithm = algorithm;\n\t\t}\n\n\t\tpublic static String encode(byte[] salt, int iterations, byte[] storedKey, byte[] serverKey) {\n\t\t\treturn \"s=\" + tigase.util.Base64.encode(salt) + \",i=\" + iterations + \",t=\" +\n\t\t\t\t\ttigase.util.Base64.encode(storedKey) + \",e=\" +\n\t\t\t\t\ttigase.util.Base64.encode(serverKey);\n\t\t}\n\n\t\tpublic static String encode(ScramCredentialsEntry entry) {\n\t\t\treturn encode(entry.getSalt(), entry.getIterations(), entry.getStoredKey(), entry.getServerKey());\n\t\t}\n\n\t\t@Override\n\t\tpublic String encode(BareJID user, ScramCredentialsEntry entry) {\n\t\t\treturn encode(entry);\n\t\t}\n\n\t\t@Override\n\t\tpublic String encode(BareJID user, String password) {\n\t\t\tbyte[] salt = new byte[10];\n\t\t\trandom.nextBytes(salt);\n\t\t\tSCRAMHelper.AuthenticationData authData;\n\t\t\ttry {\n\t\t\t\tauthData = SCRAMHelper.encodePlainPassword(algorithm, salt, iterations, password);\n\t\t\t} catch (InvalidKeyException | NoSuchAlgorithmException e) {\n\t\t\t\tthrow new RuntimeException(\"Could not encode password\", e);\n\t\t\t}\n\n\t\t\treturn encode(salt, iterations, authData.storedKey(), authData.serverKey());\n\t\t}\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn name;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/credentials/entries/ScramSha1CredentialsEntry.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.credentials.entries;\n\nimport tigase.auth.CredentialsDecoderBean;\nimport tigase.auth.CredentialsEncoderBean;\nimport tigase.kernel.beans.Bean;\n\nimport java.security.InvalidKeyException;\nimport java.security.NoSuchAlgorithmException;\n\npublic class ScramSha1CredentialsEntry\n\t\textends ScramCredentialsEntry {\n\n\tprivate static final String ALGORITHM = \"SHA1\";\n\n\tpublic ScramSha1CredentialsEntry(PlainCredentialsEntry entry) throws NoSuchAlgorithmException, InvalidKeyException {\n\t\tsuper(\"SHA-1\", entry);\n\t}\n\n\tpublic ScramSha1CredentialsEntry(byte[] salt, int iterations, byte[] saltedPassword)\n\t\t\tthrows NoSuchAlgorithmException, InvalidKeyException {\n\t\tsuper(\"SHA-1\", salt, iterations, saltedPassword);\n\t}\n\n\tpublic ScramSha1CredentialsEntry(byte[] salt, int iterations, byte[] storedKey, byte[] serverKey) {\n\t\tsuper(\"SHA-1\", salt, iterations, storedKey, serverKey);\n\t}\n\n\t@Bean(name = \"SCRAM-SHA-1\", parent = CredentialsDecoderBean.class, active = true)\n\tpublic static class Decoder\n\t\t\textends ScramCredentialsEntry.Decoder {\n\n\t\tpublic Decoder() {\n\t\t\tsuper(ALGORITHM);\n\t\t}\n\n\t\t@Override\n\t\tprotected ScramSha1CredentialsEntry newInstance(byte[] salt, int iterations, byte[] saltedPassword) {\n\t\t\ttry {\n\t\t\t\treturn new ScramSha1CredentialsEntry(salt, iterations, saltedPassword);\n\t\t\t} catch (NoSuchAlgorithmException | InvalidKeyException e) {\n\t\t\t\tthrow new RuntimeException(e);\n\t\t\t}\n\t\t}\n\n\t\tprotected ScramSha1CredentialsEntry newInstance(byte[] salt, int iterations, byte[] storedKey,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tbyte[] serverKey) {\n\t\t\treturn new ScramSha1CredentialsEntry(salt, iterations, storedKey, serverKey);\n\t\t}\n\t}\n\n\t@Bean(name = \"SCRAM-SHA-1\", parent = CredentialsEncoderBean.class, active = true)\n\tpublic static class Encoder\n\t\t\textends ScramCredentialsEntry.Encoder {\n\n\t\tpublic Encoder() {\n\t\t\tsuper(ALGORITHM);\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/credentials/entries/ScramSha256CredentialsEntry.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.credentials.entries;\n\nimport tigase.auth.CredentialsDecoderBean;\nimport tigase.auth.CredentialsEncoderBean;\nimport tigase.kernel.beans.Bean;\n\nimport java.security.InvalidKeyException;\nimport java.security.NoSuchAlgorithmException;\n\npublic class ScramSha256CredentialsEntry\n\t\textends ScramCredentialsEntry {\n\n\tprivate static final String ALGORITHM = \"SHA-256\";\n\n\tpublic ScramSha256CredentialsEntry(PlainCredentialsEntry entry)\n\t\t\tthrows NoSuchAlgorithmException, InvalidKeyException {\n\t\tsuper(ALGORITHM, entry);\n\t}\n\n\tpublic ScramSha256CredentialsEntry(byte[] salt, int iterations, byte[] saltedPassword)\n\t\t\tthrows NoSuchAlgorithmException, InvalidKeyException {\n\t\tsuper(ALGORITHM, salt, iterations, saltedPassword);\n\t}\n\n\tpublic ScramSha256CredentialsEntry(byte[] salt, int iterations, byte[] storedKey, byte[] serverKey) {\n\t\tsuper(ALGORITHM, salt, iterations, storedKey, serverKey);\n\t}\n\n\t@Bean(name = \"SCRAM-SHA-256\", parent = CredentialsDecoderBean.class, active = true)\n\tpublic static class Decoder\n\t\t\textends ScramCredentialsEntry.Decoder {\n\n\t\tpublic Decoder() {\n\t\t\tsuper(ALGORITHM);\n\t\t}\n\n\t\t@Override\n\t\tprotected ScramSha256CredentialsEntry newInstance(byte[] salt, int iterations, byte[] saltedPassword) {\n\t\t\ttry {\n\t\t\t\treturn new ScramSha256CredentialsEntry(salt, iterations, saltedPassword);\n\t\t\t} catch (NoSuchAlgorithmException | InvalidKeyException e) {\n\t\t\t\tthrow new RuntimeException(e);\n\t\t\t}\n\t\t}\n\n\t\tprotected ScramSha256CredentialsEntry newInstance(byte[] salt, int iterations, byte[] storedKey,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  byte[] serverKey) {\n\t\t\treturn new ScramSha256CredentialsEntry(salt, iterations, storedKey, serverKey);\n\t\t}\n\t}\n\n\t@Bean(name = \"SCRAM-SHA-256\", parent = CredentialsEncoderBean.class, active = true)\n\tpublic static class Encoder\n\t\t\textends ScramCredentialsEntry.Encoder {\n\n\t\tpublic Encoder() {\n\t\t\tsuper(ALGORITHM);\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/credentials/entries/ScramSha512CredentialsEntry.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.credentials.entries;\n\nimport tigase.auth.CredentialsDecoderBean;\nimport tigase.auth.CredentialsEncoderBean;\nimport tigase.kernel.beans.Bean;\n\nimport java.security.InvalidKeyException;\nimport java.security.NoSuchAlgorithmException;\n\npublic class ScramSha512CredentialsEntry\n\t\textends ScramCredentialsEntry {\n\n\tprivate static final String ALGORITHM = \"SHA-512\";\n\n\tpublic ScramSha512CredentialsEntry(PlainCredentialsEntry entry)\n\t\t\tthrows NoSuchAlgorithmException, InvalidKeyException {\n\t\tsuper(ALGORITHM, entry);\n\t}\n\n\tpublic ScramSha512CredentialsEntry(byte[] salt, int iterations, byte[] saltedPassword)\n\t\t\tthrows NoSuchAlgorithmException, InvalidKeyException {\n\t\tsuper(ALGORITHM, salt, iterations, saltedPassword);\n\t}\n\n\tpublic ScramSha512CredentialsEntry(byte[] salt, int iterations, byte[] storedKey, byte[] serverKey) {\n\t\tsuper(ALGORITHM, salt, iterations, storedKey, serverKey);\n\t}\n\n\t@Bean(name = \"SCRAM-SHA-512\", parent = CredentialsDecoderBean.class, active = false)\n\tpublic static class Decoder\n\t\t\textends ScramCredentialsEntry.Decoder {\n\n\t\tpublic Decoder() {\n\t\t\tsuper(ALGORITHM);\n\t\t}\n\n\t\t@Override\n\t\tprotected ScramSha512CredentialsEntry newInstance(byte[] salt, int iterations, byte[] saltedPassword) {\n\t\t\ttry {\n\t\t\t\treturn new ScramSha512CredentialsEntry(salt, iterations, saltedPassword);\n\t\t\t} catch (NoSuchAlgorithmException | InvalidKeyException e) {\n\t\t\t\tthrow new RuntimeException(e);\n\t\t\t}\n\t\t}\n\n\t\tprotected ScramSha512CredentialsEntry newInstance(byte[] salt, int iterations, byte[] storedKey,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  byte[] serverKey) {\n\t\t\treturn new ScramSha512CredentialsEntry(salt, iterations, storedKey, serverKey);\n\t\t}\n\t}\n\n\t@Bean(name = \"SCRAM-SHA-512\", parent = CredentialsEncoderBean.class, active = true)\n\tpublic static class Encoder\n\t\t\textends ScramCredentialsEntry.Encoder {\n\n\t\tpublic Encoder() {\n\t\t\tsuper(ALGORITHM);\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/credentials/entries/XTokenCredentialsEntry.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.credentials.entries;\n\nimport tigase.auth.CredentialsDecoderBean;\nimport tigase.auth.CredentialsEncoderBean;\nimport tigase.auth.credentials.Credentials;\nimport tigase.auth.mechanisms.SaslXTOKEN;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.util.Base64;\nimport tigase.xmpp.jid.BareJID;\n\npublic class XTokenCredentialsEntry\n\t\timplements Credentials.Entry {\n\n\tprivate final byte[] secretKey;\n\tprivate boolean oneTime;\n\n\tpublic XTokenCredentialsEntry(byte[] secretKey, boolean oneTime) {\n\t\tthis.secretKey = secretKey;\n\t\tthis.oneTime = oneTime;\n\t}\n\n\tpublic byte[] getSecretKey() {\n\t\treturn secretKey;\n\t}\n\n\tpublic boolean isOneTime() {\n\t\treturn oneTime;\n\t}\n\n\t@Override\n\tpublic String getMechanism() {\n\t\treturn SaslXTOKEN.NAME;\n\t}\n\n\t@Override\n\tpublic boolean verifyPlainPassword(String plain) {\n\t\treturn false;\n\t}\n\n\tpublic String encoded() {\n\t\treturn \"t=\" + Base64.encode(getSecretKey()) + \",o=\" + isOneTime();\n\t}\n\n\t@Bean(name = SaslXTOKEN.NAME, parent = CredentialsDecoderBean.class, active = false)\n\tpublic static class Decoder\n\t\t\timplements Credentials.Decoder<XTokenCredentialsEntry> {\n\n\t\t@ConfigField(desc = \"Mechanism name\")\n\t\tprivate String name;\n\t\t\n\t\t@Override\n\t\tpublic XTokenCredentialsEntry decode(BareJID user, String value) {\n\t\t\tbyte[] secretKey = null;\n\t\t\tboolean isOT = false;\n\t\t\tint pos = 0;\n\t\t\twhile (pos < value.length()) {\n\t\t\t\tchar c = value.charAt(pos);\n\t\t\t\tint x = value.indexOf(\",\", pos + 2);\n\t\t\t\tString part = value.substring(pos + 2, x == -1 ? value.length() : x);\n\n\t\t\t\tswitch (c) {\n\t\t\t\t\tcase 't':\n\t\t\t\t\t\tsecretKey = Base64.decode(part);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'o':\n\t\t\t\t\t\tisOT = Boolean.parseBoolean(part);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (x == -1) {\n\t\t\t\t\tbreak;\n\t\t\t\t} else {\n\t\t\t\t\tpos = x + 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (secretKey == null) {\n\t\t\t\tthrow new RuntimeException(\"secret key cannot be null!\");\n\t\t\t}\n\t\t\treturn new XTokenCredentialsEntry(secretKey, isOT);\n\t\t}\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn name;\n\t\t}\n\t}\n\n\t@Bean(name = SaslXTOKEN.NAME, parent = CredentialsEncoderBean.class, active = false)\n\tpublic static class Encoder\n\t\t\timplements Credentials.Encoder<XTokenCredentialsEntry> {\n\n\t\t@ConfigField(desc = \"Mechanism name\")\n\t\tprivate String name;\n\t\t\n\t\t@Override\n\t\tpublic String encode(BareJID user, String password) {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic String encode(BareJID user, XTokenCredentialsEntry entry) {\n\t\t\treturn entry.encoded();\n\t\t}\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn name;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/impl/AuthRepoPlainCallbackHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.impl;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.auth.AuthRepositoryAware;\nimport tigase.auth.DomainAware;\nimport tigase.auth.XmppSaslException;\nimport tigase.auth.callbacks.VerifyPasswordCallback;\nimport tigase.auth.mechanisms.AbstractSasl;\nimport tigase.db.AuthRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.xmpp.jid.BareJID;\n\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.callback.NameCallback;\nimport javax.security.auth.callback.UnsupportedCallbackException;\nimport javax.security.sasl.AuthorizeCallback;\nimport javax.security.sasl.RealmCallback;\nimport javax.security.sasl.SaslException;\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.db.AuthRepository.*;\n\n/**\n * This is implementation of {@linkplain CallbackHandler} to use with old {@linkplain AuthRepository AuthRepositories}.\n * Callback {@linkplain VerifyPasswordCallback} uses method {@linkplain tigase.db.AuthRepositoryImpl#plainAuth(BareJID, String)} to\n * password verification.\n */\n@Deprecated\n@TigaseDeprecated(since = \"8.0.0\")\npublic class AuthRepoPlainCallbackHandler\n\t\timplements CallbackHandler, AuthRepositoryAware, DomainAware {\n\n\tprotected String domain;\n\n\tprotected BareJID jid = null;\n\tprotected Logger log = Logger.getLogger(this.getClass().getName());\n\tprotected AuthRepository repo;\n\n\t@Override\n\tpublic void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {\n\t\tfor (int i = 0; i < callbacks.length; i++) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Callback: {0}\", callbacks[i].getClass().getSimpleName());\n\t\t\t}\n\t\t\thandleCallback(callbacks[i]);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setAuthRepository(AuthRepository repo) {\n\t\tthis.repo = repo;\n\t}\n\n\t@Override\n\tpublic void setDomain(String domain) {\n\t\tthis.domain = domain;\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprotected void handleAuthorizeCallback(AuthorizeCallback authCallback) throws SaslException {\n\t\tString authenId = authCallback.getAuthenticationID();\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"AuthorizeCallback: authenId: {0}\", authenId);\n\t\t}\n\n\t\ttry {\n\t\t\tfinal AccountStatus accountStatus = repo.getAccountStatus(jid);\n\t\t\tif (accountStatus.isInactive()) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"User {0} is disabled, status: {1}\", new Object[] {jid, accountStatus});\n\t\t\t\t}\n\t\t\t\tthrow XmppSaslException.getExceptionFor(accountStatus);\n\t\t\t}\n\t\t} catch (TigaseDBException e) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Cannot check if user \" + jid + \" is enabled\", e);\n\t\t\t}\n\t\t}\n\n\t\tString authorId = authCallback.getAuthorizationID();\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"AuthorizeCallback: authorId: {0}\", authorId);\n\t\t}\n\t\tif (AbstractSasl.isAuthzIDIgnored() || authenId.equals(authorId)) {\n\t\t\tauthCallback.setAuthorized(true);\n\t\t}\n\t}\n\n\tprotected void handleCallback(Callback callback) throws UnsupportedCallbackException, IOException {\n\t\tif (callback instanceof RealmCallback) {\n\t\t\thandleRealmCallback((RealmCallback) callback);\n\t\t} else if (callback instanceof NameCallback) {\n\t\t\thandleNameCallback((NameCallback) callback);\n\t\t} else if (callback instanceof VerifyPasswordCallback) {\n\t\t\thandleVerifyPasswordCallback((VerifyPasswordCallback) callback);\n\t\t} else if (callback instanceof AuthorizeCallback) {\n\t\t\thandleAuthorizeCallback((AuthorizeCallback) callback);\n\t\t} else {\n\t\t\tthrow new UnsupportedCallbackException(callback, \"Unrecognized Callback\");\n\t\t}\n\n\t}\n\n\tprotected void handleNameCallback(NameCallback nc) throws IOException {\n\t\tString user_name = nc.getDefaultName();\n\t\tjid = BareJID.bareJIDInstanceNS(user_name, domain);\n\t\tnc.setName(jid.toString());\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"NameCallback: {0}\", user_name);\n\t\t}\n\t}\n\n\tprotected void handleRealmCallback(RealmCallback rc) throws IOException {\n\t\tString realm = domain;\n\n\t\tif (realm != null) {\n\t\t\trc.setText(realm);\n\t\t} // end of if (realm == null)\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"RealmCallback: {0}\", realm);\n\t\t}\n\t}\n\n\tprotected void handleVerifyPasswordCallback(VerifyPasswordCallback pc) throws IOException {\n\t\tString passwd = pc.getPassword();\n\n\t\ttry {\n\t\t\tMap<String, Object> map = new HashMap<String, Object>();\n\n\t\t\tmap.put(PROTOCOL_KEY, PROTOCOL_VAL_NONSASL);\n\t\t\tmap.put(USER_ID_KEY, jid);\n\t\t\tmap.put(PASSWORD_KEY, passwd);\n\t\t\tmap.put(REALM_KEY, jid.getDomain());\n\t\t\tmap.put(SERVER_NAME_KEY, jid.getDomain());\n\t\t\tpc.setVerified(repo.otherAuth(map));\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"VerifyPasswordCallback: {0}\", \"******\");\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tpc.setVerified(false);\n\n\t\t\tthrow new IOException(\"Password verification problem.\", e);\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/main/java/tigase/auth/impl/CertBasedCallbackHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.impl;\n\nimport tigase.auth.SessionAware;\nimport tigase.auth.callbacks.ValidateCertificateData;\nimport tigase.auth.mechanisms.SaslEXTERNAL;\nimport tigase.cert.CertificateUtil;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\n\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.callback.UnsupportedCallbackException;\nimport java.io.IOException;\nimport java.security.cert.Certificate;\nimport java.security.cert.X509Certificate;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class CertBasedCallbackHandler\n\t\timplements CallbackHandler, SessionAware {\n\n\tprotected Logger log = Logger.getLogger(this.getClass().getName());\n\n\tprivate XMPPResourceConnection session;\n\n\t@Override\n\tpublic void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {\n\t\ttry {\n\t\t\tfor (int i = 0; i < callbacks.length; i++) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Callback: {0}\", callbacks[i].getClass().getSimpleName());\n\t\t\t\t}\n\n\t\t\t\tif (callbacks[i] instanceof ValidateCertificateData) {\n\t\t\t\t\tValidateCertificateData authCallback = ((ValidateCertificateData) callbacks[i]);\n\n\t\t\t\t\tfinal String domain = session.getDomain().getVhost().getDomain();\n\t\t\t\t\tfinal BareJID defaultAuthzid = authCallback.getDefaultAuthzid();\n\t\t\t\t\tif (defaultAuthzid != null && !defaultAuthzid.getDomain().equals(domain)) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tCertificate cert = (Certificate) session.getSessionData(SaslEXTERNAL.PEER_CERTIFICATE_KEY);\n\t\t\t\t\tfinal List<String> authJIDs = CertificateUtil.extractXmppAddrs((X509Certificate) cert);\n\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"{0}, Found authJIDs: {1} in certificate: {1}\",\n\t\t\t\t\t\t\t\tnew Object[]{session, authJIDs, CertificateUtil.getCertCName(((X509Certificate) cert))});\n\t\t\t\t\t}\n\t\t\t\t\tfor (String string : authJIDs) {\n\t\t\t\t\t\tif (defaultAuthzid != null) {\n\t\t\t\t\t\t\tif (string.equals(defaultAuthzid.toString())) {\n\t\t\t\t\t\t\t\tauthCallback.setAuthorized(true);\n\t\t\t\t\t\t\t\tauthCallback.setAuthorizedID(string);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (BareJID.bareJIDInstance(string).getDomain().equals(domain)) {\n\t\t\t\t\t\t\tauthCallback.setAuthorized(true);\n\t\t\t\t\t\t\tauthCallback.setAuthorizedID(string);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthrow new UnsupportedCallbackException(callbacks[i], \"Unrecognized Callback\");\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (TigaseStringprepException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setSession(XMPPResourceConnection session) {\n\t\tthis.session = session;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/impl/PlainCallbackHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.impl;\n\nimport tigase.auth.AuthRepositoryAware;\nimport tigase.auth.DomainAware;\nimport tigase.auth.SessionAware;\nimport tigase.auth.XmppSaslException;\nimport tigase.auth.callbacks.AuthorizationIdCallback;\nimport tigase.auth.callbacks.VerifyPasswordCallback;\nimport tigase.auth.credentials.Credentials;\nimport tigase.auth.mechanisms.AbstractSasl;\nimport tigase.db.AuthRepository;\nimport tigase.db.UserNotFoundException;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\n\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.callback.NameCallback;\nimport javax.security.auth.callback.UnsupportedCallbackException;\nimport javax.security.sasl.AuthorizeCallback;\nimport javax.security.sasl.RealmCallback;\nimport javax.security.sasl.SaslException;\nimport java.io.IOException;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.auth.CallbackHandlerFactory.AUTH_JID;\nimport static tigase.auth.credentials.Credentials.DEFAULT_CREDENTIAL_ID;\n\n/**\n * Implementation of CallbackHandler for authentication with SASL PLAIN or using plaintext password.\n */\npublic class PlainCallbackHandler\n\t\timplements CallbackHandler, AuthRepositoryAware, DomainAware, SessionAware {\n\n\tprotected String domain;\n\n\tprotected BareJID jid = null;\n\tprotected Logger log = Logger.getLogger(this.getClass().getName());\n\tprotected AuthRepository repo;\n\tprivate boolean loggingInForbidden = false;\n\tprivate XMPPResourceConnection session;\n\tprivate String credentialId;\n\n\t@Override\n\tpublic void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {\n\t\tfor (int i = 0; i < callbacks.length; i++) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Callback: {0}\", callbacks[i].getClass().getSimpleName());\n\t\t\t}\n\t\t\thandleCallback(callbacks[i]);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setAuthRepository(AuthRepository repo) {\n\t\tthis.repo = repo;\n\t}\n\n\t@Override\n\tpublic void setDomain(String domain) {\n\t\tthis.domain = domain;\n\t}\n\n\t@Override\n\tpublic void setSession(XMPPResourceConnection session) {\n\t\tthis.session = session;\n\t}\n\n\tprotected void handleAuthorizeCallback(AuthorizeCallback authCallback) {\n\t\tString authenId = authCallback.getAuthenticationID();\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"AuthorizeCallback: authenId: {0}\", authenId);\n\t\t}\n\n\t\tif (loggingInForbidden) {\n\t\t\tauthCallback.setAuthorized(false);\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"User {0} is disabled\", jid);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tString authorId = authCallback.getAuthorizationID();\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"AuthorizeCallback: authorId: {0}\", authorId);\n\t\t}\n\n\t\tauthCallback.setAuthorized(true);\n\t\tsession.removeSessionData(AUTH_JID);\n\t}\n\n\tprotected void handleCallback(Callback callback) throws UnsupportedCallbackException, IOException {\n\t\tif (callback instanceof RealmCallback) {\n\t\t\thandleRealmCallback((RealmCallback) callback);\n\t\t} else if (callback instanceof NameCallback) {\n\t\t\thandleNameCallback((NameCallback) callback);\n\t\t} else if (callback instanceof AuthorizationIdCallback) {\n\t\t\thandleAuthorizationIdCallback((AuthorizationIdCallback) callback);\n\t\t} else if (callback instanceof VerifyPasswordCallback) {\n\t\t\thandleVerifyPasswordCallback((VerifyPasswordCallback) callback);\n\t\t} else if (callback instanceof AuthorizeCallback) {\n\t\t\thandleAuthorizeCallback((AuthorizeCallback) callback);\n\t\t} else {\n\t\t\tthrow new UnsupportedCallbackException(callback, \"Unrecognized Callback\");\n\t\t}\n\n\t}\n\n\tprotected void handleNameCallback(NameCallback nc) throws IOException {\n\t\tcredentialId = DEFAULT_CREDENTIAL_ID;\n\n\t\tBareJID jid = BareJID.bareJIDInstanceNS(nc.getDefaultName());\n\t\tif (jid.getLocalpart() == null || !domain.equalsIgnoreCase(jid.getDomain())) {\n\t\t\tjid = BareJID.bareJIDInstanceNS(nc.getDefaultName(), domain);\n\t\t}\n\t\tsetJid(jid);\n\t\tnc.setName(jid.toString());\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"NameCallback: {0}\", credentialId);\n\t\t}\n\t}\n\n\tprotected void handleRealmCallback(RealmCallback rc) throws IOException {\n\t\tString realm = domain;\n\n\t\tif (realm != null) {\n\t\t\trc.setText(realm);\n\t\t} // end of if (realm == null)\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"RealmCallback: {0}\", realm);\n\t\t}\n\t}\n\n\tprotected void handleVerifyPasswordCallback(VerifyPasswordCallback pc) throws IOException {\n\t\tfinal String password = pc.getPassword();\n\t\ttry {\n\t\t\tCredentials credentials = repo.getCredentials(jid, credentialId);\n\n\t\t\tlog.log(Level.FINE,\n\t\t\t\t\t\"Fetched credentials for: \" + jid + \" with credentialsId: \" + credentialId + \", credentials: \" +\n\t\t\t\t\t\t\tcredentials);\n\n\t\t\tCredentials.Entry entry = credentials.getEntryForMechanism(\"PLAIN\");\n\t\t\tif (entry == null) {\n\t\t\t\tentry = credentials.getFirst();\n\t\t\t}\n\n\t\t\tloggingInForbidden = !credentials.canLogin();\n\n\t\t\tif (loggingInForbidden) {\n\t\t\t\tthrow XmppSaslException.getExceptionFor(credentials.getAccountStatus());\n\t\t\t}\n\n\t\t\tfinal boolean verified = !loggingInForbidden && entry != null && entry.verifyPlainPassword(password);\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Verification result: {0}, loggingInForbidden: {1}, entry: {2}, credentials: {3}\",\n\t\t\t\t\t\tnew Object[]{verified, loggingInForbidden, entry, credentials});\n\t\t\t}\n\t\t\tpc.setVerified(verified);\n\t\t} catch (UserNotFoundException e) {\n\t\t\tlog.log(Level.FINE, \"User not found: \" + e);\n\t\t\tpc.setVerified(false);\n\t\t} catch (SaslException e) {\n\t\t\tlog.log(Level.FINE, \"User inactive: \" + e);\n\t\t\tthrow e;\n\t\t} catch (Exception e) {\n\t\t\tthrow new IOException(\"Password verification problem.\", e);\n\t\t}\n\t}\n\n\tprivate void handleAuthorizationIdCallback(AuthorizationIdCallback callback) throws XmppSaslException {\n\t\tif (!AbstractSasl.isAuthzIDIgnored() && callback.getAuthzId() != null &&\n\t\t\t\t!callback.getAuthzId().equals(jid.toString())) {\n\t\t\ttry {\n\t\t\t\tcredentialId = jid.getLocalpart();\n\t\t\t\tsetJid(BareJID.bareJIDInstance(callback.getAuthzId()));\n\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\tlog.warning(\"Malformed AuthorizationId: \" + ex.getMessage());\n\t\t\t\tthrow new XmppSaslException(XmppSaslException.SaslError.invalid_authzid);\n\t\t\t}\n\t\t} else {\n\t\t\tcredentialId = DEFAULT_CREDENTIAL_ID;\n\t\t\tcallback.setAuthzId(jid.toString());\n\t\t}\n\t}\n\n\tprivate void setJid(BareJID jid) {\n\t\tthis.jid = jid;\n\t\tif (jid != null) {\n\t\t\tthis.session.putSessionData(AUTH_JID, jid);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/impl/PlainSPCallbackHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.impl;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.auth.AuthRepositoryAware;\nimport tigase.auth.DomainAware;\nimport tigase.auth.callbacks.VerifyPasswordCallback;\nimport tigase.auth.mechanisms.AbstractSasl;\nimport tigase.auth.mechanisms.AbstractSaslSCRAM;\nimport tigase.db.AuthRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.util.Base64;\nimport tigase.xmpp.jid.BareJID;\n\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.callback.NameCallback;\nimport javax.security.auth.callback.UnsupportedCallbackException;\nimport javax.security.sasl.AuthorizeCallback;\nimport javax.security.sasl.RealmCallback;\nimport javax.security.sasl.SaslException;\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * This is implementation of {@linkplain CallbackHandler} to use with old {@linkplain AuthRepository AuthRepositories}.\n * Callback {@linkplain VerifyPasswordCallback} uses method {@linkplain tigase.db.AuthRepositoryImpl#plainAuth(BareJID, String)} to\n * password verification.\n */\n@Deprecated\n@TigaseDeprecated(since = \"8.0.0\")\npublic class PlainSPCallbackHandler\n\t\timplements CallbackHandler, AuthRepositoryAware, DomainAware {\n\n\tprotected String domain;\n\n\tprotected BareJID jid = null;\n\tprotected Logger log = Logger.getLogger(this.getClass().getName());\n\tprotected AuthRepository repo;\n\n\t@Override\n\tpublic void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {\n\t\tfor (int i = 0; i < callbacks.length; i++) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Callback: {0}\", callbacks[i].getClass().getSimpleName());\n\t\t\t}\n\t\t\thandleCallback(callbacks[i]);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setAuthRepository(AuthRepository repo) {\n\t\tthis.repo = repo;\n\t}\n\n\t@Override\n\tpublic void setDomain(String domain) {\n\t\tthis.domain = domain;\n\t}\n\n\tprotected void handleAuthorizeCallback(AuthorizeCallback authCallback) {\n\t\tString authenId = authCallback.getAuthenticationID();\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"AuthorizeCallback: authenId: {0}\", authenId);\n\t\t}\n\n\t\ttry {\n\t\t\tfinal AuthRepository.AccountStatus accountStatus = repo.getAccountStatus(jid);\n\t\t\tif (accountStatus.isInactive()) {\n\t\t\t\tauthCallback.setAuthorized(false);\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"User {0} is disabled\", jid);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t} catch (TigaseDBException e) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Cannot check if user \" + jid + \" is enabled\", e);\n\t\t\t}\n\t\t}\n\n\t\tString authorId = authCallback.getAuthorizationID();\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"AuthorizeCallback: authorId: {0}\", authorId);\n\t\t}\n\t\tif (AbstractSasl.isAuthzIDIgnored() || authenId.equals(authorId)) {\n\t\t\tauthCallback.setAuthorized(true);\n\t\t}\n\t}\n\n\tprotected void handleCallback(Callback callback) throws UnsupportedCallbackException, IOException {\n\t\tif (callback instanceof RealmCallback) {\n\t\t\thandleRealmCallback((RealmCallback) callback);\n\t\t} else if (callback instanceof NameCallback) {\n\t\t\thandleNameCallback((NameCallback) callback);\n\t\t} else if (callback instanceof VerifyPasswordCallback) {\n\t\t\thandleVerifyPasswordCallback((VerifyPasswordCallback) callback);\n\t\t} else if (callback instanceof AuthorizeCallback) {\n\t\t\thandleAuthorizeCallback((AuthorizeCallback) callback);\n\t\t} else {\n\t\t\tthrow new UnsupportedCallbackException(callback, \"Unrecognized Callback\");\n\t\t}\n\n\t}\n\n\tprotected void handleNameCallback(NameCallback nc) throws IOException {\n\t\tString user_name = nc.getDefaultName();\n\t\tjid = BareJID.bareJIDInstanceNS(user_name, domain);\n\t\tnc.setName(jid.toString());\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"NameCallback: {0}\", user_name);\n\t\t}\n\t}\n\n\tprotected void handleRealmCallback(RealmCallback rc) throws IOException {\n\t\tString realm = domain;\n\n\t\tif (realm != null) {\n\t\t\trc.setText(realm);\n\t\t} // end of if (realm == null)\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"RealmCallback: {0}\", realm);\n\t\t}\n\t}\n\n\tprotected void handleVerifyPasswordCallback(VerifyPasswordCallback pc) throws IOException {\n\t\tfinal String password = pc.getPassword();\n\t\ttry {\n\t\t\tString pwd = repo.getPassword(jid);\n\t\t\tif (pwd == null) {\n\t\t\t\tthrow new SaslException(\"User \" + jid + \" not found.\");\n\t\t\t}\n\t\t\tbyte[] buffer = Base64.decode(pwd);\n\t\t\tbyte[] salt = new byte[20];\n\t\t\tbyte[] saltedPassword = new byte[20];\n\t\t\tSystem.arraycopy(buffer, 0, salt, 0, salt.length);\n\t\t\tSystem.arraycopy(buffer, salt.length, saltedPassword, 0, saltedPassword.length);\n\n\t\t\tbyte[] np = AbstractSaslSCRAM.hi(\"SHA1\", AbstractSaslSCRAM.normalize(password), salt, 4096);\n\n\t\t\tpc.setVerified(Arrays.equals(saltedPassword, np));\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"VerifyPasswordCallback: {0}\", \"******\");\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tpc.setVerified(false);\n\t\t\tthrow new IOException(\"Password verification problem.\", e);\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/main/java/tigase/auth/impl/ScramCallbackHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.impl;\n\nimport tigase.auth.*;\nimport tigase.auth.callbacks.*;\nimport tigase.auth.credentials.Credentials;\nimport tigase.auth.credentials.entries.PlainCredentialsEntry;\nimport tigase.auth.credentials.entries.ScramCredentialsEntry;\nimport tigase.auth.mechanisms.AbstractSasl;\nimport tigase.auth.mechanisms.AbstractSaslSCRAM;\nimport tigase.db.AuthRepository;\nimport tigase.util.Base64;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\n\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.callback.NameCallback;\nimport javax.security.auth.callback.UnsupportedCallbackException;\nimport javax.security.sasl.AuthorizeCallback;\nimport javax.security.sasl.SaslException;\nimport java.io.IOException;\nimport java.security.MessageDigest;\nimport java.security.cert.X509Certificate;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.auth.CallbackHandlerFactory.AUTH_JID;\nimport static tigase.auth.credentials.Credentials.DEFAULT_CREDENTIAL_ID;\n\n/**\n * Implementation of CallbackHandler to support authentication using SASL SCRAM-* authentication mechanism.\n */\npublic class ScramCallbackHandler\n\t\timplements CallbackHandler, AuthRepositoryAware, SessionAware, DomainAware, MechanismNameAware {\n\n\tprivate static final Logger log = Logger.getLogger(ScramCallbackHandler.class.getCanonicalName());\n\tprivate String credentialId = null;\n\tprivate ScramCredentialsEntry credentialsEntry;\n\tprivate boolean credentialsFetched;\n\tprivate String domain;\n\tprivate BareJID jid = null;\n\tprivate boolean loggingInForbidden = false;\n\tprivate String mechanismName;\n\tprivate AuthRepository repo;\n\tprivate XMPPResourceConnection session;\n\n\tpublic ScramCallbackHandler() {\n\t}\n\n\t@Override\n\tpublic void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {\n\t\tfor (int i = 0; i < callbacks.length; i++) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Callback: {0}\", callbacks[i].getClass().getSimpleName());\n\t\t\t}\n\t\t\thandleCallback(callbacks[i]);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setAuthRepository(AuthRepository repo) {\n\t\tthis.repo = repo;\n\t}\n\n\t@Override\n\tpublic void setDomain(String domain) {\n\t\tthis.domain = domain;\n\t}\n\n\t@Override\n\tpublic void setMechanismName(String mechanismName) {\n\t\tthis.mechanismName = mechanismName;\n\t}\n\n\t@Override\n\tpublic void setSession(XMPPResourceConnection session) {\n\t\tthis.session = session;\n\t}\n\n\tprotected void handleAuthorizeCallback(AuthorizeCallback authCallback) throws SaslException {\n\t\tString authenId = authCallback.getAuthenticationID();\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"AuthorizeCallback: authenId: {0}\", authenId);\n\t\t}\n\n\t\tfetchCredentials();\n\t\tif (loggingInForbidden) {\n\t\t\tauthCallback.setAuthorized(false);\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"User {0} is disabled\", jid);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tString authorId = authCallback.getAuthorizationID();\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"AuthorizeCallback: authorId: {0}\", authorId);\n\t\t}\n\t\tauthCallback.setAuthorized(true);\n\t\tsession.removeSessionData(AUTH_JID);\n\t}\n\n\tprotected void handleCallback(Callback callback) throws UnsupportedCallbackException, IOException {\n\t\tif (callback instanceof XMPPSessionCallback) {\n\t\t\t((XMPPSessionCallback) callback).setSession(session);\n\t\t} else if (callback instanceof ChannelBindingCallback) {\n\t\t\thandleChannelBindingCallback((ChannelBindingCallback) callback);\n\t\t} else if (callback instanceof PBKDIterationsCallback) {\n\t\t\thandlePBKDIterationsCallback((PBKDIterationsCallback) callback);\n\t\t} else if (callback instanceof NameCallback) {\n\t\t\thandleNameCallback((NameCallback) callback);\n\t\t} else if (callback instanceof AuthorizationIdCallback) {\n\t\t\thandleAuthorizationIdCallback((AuthorizationIdCallback) callback);\n\t\t} else if (callback instanceof SaltCallback) {\n\t\t\thandleSaltCallback((SaltCallback) callback);\n\t\t} else if (callback instanceof ServerKeyCallback serverKeyCallback) {\n\t\t\thandleServerKeyCallback(serverKeyCallback);\n\t\t} else if (callback instanceof StoredKeyCallback storedKeyCallback) {\n\t\t\thandleStoredKeyCallback(storedKeyCallback);\n\t\t} else if (callback instanceof AuthorizeCallback) {\n\t\t\thandleAuthorizeCallback((AuthorizeCallback) callback);\n\t\t} else {\n\t\t\tthrow new UnsupportedCallbackException(callback, \"Unrecognized Callback \" + callback);\n\t\t}\n\t}\n\n\tprotected void handleNameCallback(NameCallback nc) throws IOException {\n\t\tcredentialId = DEFAULT_CREDENTIAL_ID;\n\t\tBareJID jid = BareJID.bareJIDInstanceNS(nc.getDefaultName());\n\t\tif (jid.getLocalpart() == null || !domain.equalsIgnoreCase(jid.getDomain())) {\n\t\t\tjid = BareJID.bareJIDInstanceNS(nc.getDefaultName(), domain);\n\t\t}\n\t\tsetJid(jid);\n\t\tnc.setName(jid.toString());\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"NameCallback: {0}\", credentialId);\n\t\t}\n\t}\n\n\tprotected void handlePBKDIterationsCallback(PBKDIterationsCallback callback) throws SaslException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"PBKDIterationsCallback: {0}\", jid);\n\t\t}\n\t\tfetchCredentials();\n\t\tif (credentialsEntry != null) {\n\t\t\tcallback.setInterations(credentialsEntry.getIterations());\n\t\t}\n\t}\n\n\tprotected void handleSaltCallback(SaltCallback callback) throws SaslException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"SaltCallback: {0}\", jid);\n\t\t}\n\n\t\tfetchCredentials();\n\t\tif (credentialsEntry != null) {\n\t\t\tcallback.setSalt(credentialsEntry.getSalt());\n\t\t} else {\n\t\t\tcallback.setSalt(null);\n\t\t}\n\t}\n\n\tprivate void fetchCredentials() throws SaslException {\n\t\tif (credentialsFetched) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tCredentials credentials = repo.getCredentials(jid, credentialId);\n\n\t\t\tlog.log(Level.FINE,\n\t\t\t\t\t\"Fetched credentials for: \" + jid + \" with credentialsId: \" + credentialId + \", credentials: \" +\n\t\t\t\t\t\t\tcredentials);\n\n\t\t\tif (credentials == null) {\n\t\t\t\tloggingInForbidden = true;\n\t\t\t} else {\n\t\t\t\tString mech = mechanismName.endsWith(\"-PLUS\") ? mechanismName.substring(0, mechanismName.length() -\n\t\t\t\t\t\t\"-PLUS\".length()) : mechanismName;\n\n\t\t\t\tCredentials.Entry entry = credentials.getEntryForMechanism(mech);\n\t\t\t\tif (entry == null) {\n\t\t\t\t\tentry = credentials.getEntryForMechanism(\"PLAIN\");\n\t\t\t\t}\n\t\t\t\tif (entry instanceof ScramCredentialsEntry) {\n\t\t\t\t\tcredentialsEntry = (ScramCredentialsEntry) entry;\n\t\t\t\t} else if (entry instanceof PlainCredentialsEntry) {\n\t\t\t\t\tcredentialsEntry = new ScramCredentialsEntry(mech.replace(\"SCRAM-\", \"\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t (PlainCredentialsEntry) entry);\n\t\t\t\t}\n\n\t\t\t\tloggingInForbidden = !credentials.canLogin();\n\t\t\t\tif (loggingInForbidden) {\n\t\t\t\t\tthrow XmppSaslException.getExceptionFor(credentials.getAccountStatus());\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (SaslException e) {\n\t\t\tthrow e;\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.FINE, \"Could not retrieve credentials for user \" + jid + \" with credentialId \" + credentialId,\n\t\t\t\t\tex);\n\t\t}\n\t\tcredentialsFetched = true;\n\t}\n\n\tprivate void handleAuthorizationIdCallback(AuthorizationIdCallback callback) {\n\t\tif (!AbstractSasl.isAuthzIDIgnored() && callback.getAuthzId() != null &&\n\t\t\t\t!callback.getAuthzId().equals(jid.toString())) {\n\t\t\ttry {\n\t\t\t\tcredentialId = jid.getLocalpart();\n\t\t\t\tsetJid(BareJID.bareJIDInstance(callback.getAuthzId()));\n\t\t\t} catch (Exception ex) {\n\t\t\t\tthrow new RuntimeException(ex);\n\t\t\t}\n\t\t} else {\n\t\t\tcredentialId = DEFAULT_CREDENTIAL_ID;\n\t\t\tcallback.setAuthzId(jid.toString());\n\t\t}\n\t}\n\n\tprivate void handleChannelBindingCallback(ChannelBindingCallback callback) {\n\t\tif (callback.getRequestedBindType() == AbstractSaslSCRAM.BindType.tls_exporter) {\n\t\t\tcallback.setBindingData((byte[]) session.getSessionData(AbstractSaslSCRAM.TLS_EXPORTER_KEY));\n\t\t} else if (callback.getRequestedBindType() == AbstractSaslSCRAM.BindType.tls_unique) {\n\t\t\tcallback.setBindingData((byte[]) session.getSessionData(AbstractSaslSCRAM.TLS_UNIQUE_ID_KEY));\n\t\t} else if (callback.getRequestedBindType() == AbstractSaslSCRAM.BindType.tls_server_end_point) {\n\t\t\ttry {\n\t\t\t\tX509Certificate cert = (X509Certificate) session.getSessionData(\n\t\t\t\t\t\tAbstractSaslSCRAM.LOCAL_CERTIFICATE_KEY);\n\t\t\t\tString usealgo;\n\t\t\t\tfinal String algo = cert.getSigAlgName();\n\t\t\t\tint withIdx = algo.indexOf(\"with\");\n\t\t\t\tif (withIdx <= 0) {\n\t\t\t\t\tthrow new RuntimeException(\"Unable to parse SigAlgName: \" + algo);\n\t\t\t\t}\n\t\t\t\tusealgo = algo.substring(0, withIdx);\n\t\t\t\tif (usealgo.equalsIgnoreCase(\"MD5\") || usealgo.equalsIgnoreCase(\"SHA1\")) {\n\t\t\t\t\tusealgo = \"SHA-256\";\n\t\t\t\t}\n\t\t\t\tfinal MessageDigest md = MessageDigest.getInstance(usealgo);\n\t\t\t\tfinal byte[] der = cert.getEncoded();\n\t\t\t\tmd.update(der);\n\t\t\t\tcallback.setBindingData(md.digest());\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new RuntimeException(e);\n\t\t\t}\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Channel binding {0}: {1} in session-id {2}\",\n\t\t\t\t\tnew Object[]{callback.getRequestedBindType(),\n\t\t\t\t\t\t\t\t callback.getBindingData() == null ? \"null\" : Base64.encode(callback.getBindingData()),\n\t\t\t\t\t\t\t\t session});\n\t\t}\n\t}\n\n\tprivate void handleServerKeyCallback(ServerKeyCallback callback) throws SaslException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"ServerKeyCallback: {0}\", jid);\n\t\t}\n\n\t\tfetchCredentials();\n\t\tif (credentialsEntry != null) {\n\t\t\tcallback.setServerKey(credentialsEntry.getServerKey());\n\t\t} else {\n\t\t\tcallback.setServerKey(null);\n\t\t}\n\t}\n\n\tprivate void handleStoredKeyCallback(StoredKeyCallback callback) throws SaslException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"StoredKeyCallback: {0}\", jid);\n\t\t}\n\n\t\tfetchCredentials();\n\t\tif (credentialsEntry != null) {\n\t\t\tcallback.setStoredKey(credentialsEntry.getStoredKey());\n\t\t} else {\n\t\t\tcallback.setStoredKey(null);\n\t\t}\n\t}\n\n\tprivate void setJid(BareJID jid) {\n\t\tthis.jid = jid;\n\t\tif (jid != null) {\n\t\t\tthis.session.putSessionData(AUTH_JID, jid);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/impl/XTokenCallbackHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.impl;\n\nimport tigase.auth.AuthRepositoryAware;\nimport tigase.auth.DomainAware;\nimport tigase.auth.SessionAware;\nimport tigase.auth.XmppSaslException;\nimport tigase.auth.callbacks.AuthorizationIdCallback;\nimport tigase.auth.callbacks.ReplaceServerKeyCallback;\nimport tigase.auth.callbacks.ServerKeyCallback;\nimport tigase.auth.callbacks.SharedSecretKeyCallback;\nimport tigase.auth.credentials.Credentials;\nimport tigase.auth.credentials.entries.XTokenCredentialsEntry;\nimport tigase.auth.mechanisms.AbstractSasl;\nimport tigase.auth.mechanisms.SaslXTOKEN;\nimport tigase.db.AuthRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.util.Base64;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\n\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.callback.NameCallback;\nimport javax.security.auth.callback.UnsupportedCallbackException;\nimport javax.security.sasl.AuthorizeCallback;\nimport javax.security.sasl.RealmCallback;\nimport javax.security.sasl.SaslException;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.auth.CallbackHandlerFactory.AUTH_JID;\nimport static tigase.auth.credentials.Credentials.DEFAULT_CREDENTIAL_ID;\n\npublic class XTokenCallbackHandler implements CallbackHandler, AuthRepositoryAware, DomainAware, SessionAware {\n\n\tprotected String domain;\n\n\tprotected BareJID jid = null;\n\tprotected Logger log = Logger.getLogger(this.getClass().getName());\n\tprotected AuthRepository repo;\n\tprivate boolean loggingInForbidden = false;\n\tprivate XMPPResourceConnection session;\n\tprivate String credentialId;\n\tprivate XTokenCredentialsEntry entry;\n\n\t@Override\n\tpublic void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {\n\t\tfor (int i = 0; i < callbacks.length; i++) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Callback: {0}\", callbacks[i].getClass().getSimpleName());\n\t\t\t}\n\t\t\thandleCallback(callbacks[i]);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setAuthRepository(AuthRepository repo) {\n\t\tthis.repo = repo;\n\t}\n\n\t@Override\n\tpublic void setDomain(String domain) {\n\t\tthis.domain = domain;\n\t}\n\n\t@Override\n\tpublic void setSession(XMPPResourceConnection session) {\n\t\tthis.session = session;\n\t}\n\n\tprotected void handleAuthorizeCallback(AuthorizeCallback authCallback) {\n\t\tString authenId = authCallback.getAuthenticationID();\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"AuthorizeCallback: authenId: {0}\", authenId);\n\t\t}\n\n\t\tif (loggingInForbidden) {\n\t\t\tauthCallback.setAuthorized(false);\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"User {0} is disabled\", jid);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tString authorId = authCallback.getAuthorizationID();\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"AuthorizeCallback: authorId: {0}\", authorId);\n\t\t}\n\n\t\tauthCallback.setAuthorized(true);\n\t\tsession.removeSessionData(AUTH_JID);\n\t}\n\n\tprotected void handleCallback(Callback callback) throws UnsupportedCallbackException, IOException {\n\t\tif (callback instanceof RealmCallback) {\n\t\t\thandleRealmCallback((RealmCallback) callback);\n\t\t} else if (callback instanceof NameCallback) {\n\t\t\thandleNameCallback((NameCallback) callback);\n\t\t} else if (callback instanceof AuthorizationIdCallback) {\n\t\t\thandleAuthorizationIdCallback((AuthorizationIdCallback) callback);\n\t\t} else if (callback instanceof ServerKeyCallback) {\n\t\t\thandleServerKeyCallback((ServerKeyCallback) callback);\n\t\t} else if (callback instanceof AuthorizeCallback) {\n\t\t\thandleAuthorizeCallback((AuthorizeCallback) callback);\n\t\t} else if (callback instanceof ReplaceServerKeyCallback) {\n\t\t\thandleReplaceServerKeyCallback((ReplaceServerKeyCallback) callback);\n\t\t} else if (callback instanceof SharedSecretKeyCallback) {\n\t\t\thandleSharedSecretKeyCallback((SharedSecretKeyCallback) callback);\n\t\t} else {\n\t\t\tthrow new UnsupportedCallbackException(callback, \"Unrecognized Callback\");\n\t\t}\n\n\t}\n\t\n\tprotected void handleNameCallback(NameCallback nc) throws IOException {\n\t\tcredentialId = DEFAULT_CREDENTIAL_ID;\n\n\t\tBareJID jid = BareJID.bareJIDInstanceNS(nc.getDefaultName());\n\t\tif (jid.getLocalpart() == null || !domain.equalsIgnoreCase(jid.getDomain())) {\n\t\t\tjid = BareJID.bareJIDInstanceNS(nc.getDefaultName(), domain);\n\t\t}\n\t\tsetJid(jid);\n\t\tnc.setName(jid.toString());\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"NameCallback: {0}\", credentialId);\n\t\t}\n\t}\n\n\tprotected void handleRealmCallback(RealmCallback rc) throws IOException {\n\t\tString realm = domain;\n\n\t\tif (realm != null) {\n\t\t\trc.setText(realm);\n\t\t} // end of if (realm == null)\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"RealmCallback: {0}\", realm);\n\t\t}\n\t}\n\n\tprotected void handleServerKeyCallback(ServerKeyCallback pc) throws IOException {\n\t\ttry {\n\t\t\tCredentials credentials = repo.getCredentials(jid, credentialId);\n\n\t\t\tlog.log(Level.FINE,\n\t\t\t\t\t\"Fetched credentials for: \" + jid + \" with credentialsId: \" + credentialId + \", credentials: \" +\n\t\t\t\t\t\t\tcredentials);\n\n\t\t\tentry = (XTokenCredentialsEntry) credentials.getEntryForMechanism(SaslXTOKEN.NAME);\n\n\t\t\tloggingInForbidden = !credentials.canLogin();\n\t\t\tif (loggingInForbidden) {\n\t\t\t\tthrow XmppSaslException.getExceptionFor(credentials.getAccountStatus());\n\t\t\t}\n\n\t\t\tpc.setServerKey(entry.getSecretKey());\n\t\t} catch (SaslException e) {\n\t\t\tlog.log(Level.FINE, \"User inactive: \" + e);\n\t\t\tthrow e;\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.FINE, \"Could not retrieve credentials for user \" + jid + \" with credentialId \" + credentialId,\n\t\t\t\t\tex);\n\t\t}\n\t}\n\n\tprivate void handleAuthorizationIdCallback(AuthorizationIdCallback callback) throws XmppSaslException {\n\t\tif (!AbstractSasl.isAuthzIDIgnored() && callback.getAuthzId() != null &&\n\t\t\t\t!callback.getAuthzId().equals(jid.toString())) {\n\t\t\ttry {\n\t\t\t\tcredentialId = jid.getLocalpart();\n\t\t\t\tsetJid(BareJID.bareJIDInstance(callback.getAuthzId()));\n\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\tlog.warning(\"Malformed AuthorizationId: \" + ex.getMessage());\n\t\t\t\tthrow new XmppSaslException(XmppSaslException.SaslError.invalid_authzid);\n\t\t\t}\n\t\t} else {\n\t\t\tcredentialId = DEFAULT_CREDENTIAL_ID;\n\t\t\tcallback.setAuthzId(jid.toString());\n\t\t}\n\t}\n\n\tprivate void handleReplaceServerKeyCallback(ReplaceServerKeyCallback callback) throws XmppSaslException {\n\t\ttry {\n\t\t\tif (entry != null && entry.isOneTime()) {\n\t\t\t\tbyte[] data = SaslXTOKEN.generateSecretKey();\n\t\t\t\trepo.updateCredential(jid, credentialId, SaslXTOKEN.NAME, \"t=\" + Base64.encode(data) + \",o=\" + false);\n\t\t\t\tcallback.setNewServerKey(data);\n\t\t\t}\n\t\t} catch (TigaseDBException e) {\n\t\t\tthrow new XmppSaslException(XmppSaslException.SaslError.temporary_auth_failure);\n\t\t}\n\t}\n\n\tprivate void handleSharedSecretKeyCallback(SharedSecretKeyCallback sskc) {\n\t\tString appKey = System.getProperty(\"client-application-secret\");\n\t\tif (appKey != null) {\n\t\t\tsskc.setSecret(appKey.getBytes(StandardCharsets.UTF_8));\n\t\t}\n\t}\n\n\tprivate void setJid(BareJID jid) {\n\t\tthis.jid = jid;\n\t\tif (jid != null) {\n\t\t\tthis.session.putSessionData(AUTH_JID, jid);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/mechanisms/AbstractSasl.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.mechanisms;\n\nimport tigase.auth.XmppSaslException;\n\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.callback.UnsupportedCallbackException;\nimport javax.security.sasl.SaslException;\nimport javax.security.sasl.SaslServer;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.logging.Logger;\n\npublic abstract class AbstractSasl\n\t\timplements SaslServer {\n\n\tpublic static final String SASL_STRICT_MODE_KEY = \"sasl-strict\";\n\tpublic static String PASSWORD_NOT_VERIFIED_MSG = \"Password not verified\";\n\tprotected final CallbackHandler callbackHandler;\n\tprotected final Logger log = Logger.getLogger(this.getClass().getName());\n\tprotected final Map<String, Object> negotiatedProperty = new HashMap<String, Object>();\n\tprotected final Map<? super String, ?> props;\n\tprotected String authorizedId = null;\n\tprotected boolean complete = false;\n\n\tpublic static boolean isAuthzIDIgnored() {\n\t\tString x = System.getProperty(SASL_STRICT_MODE_KEY, \"true\");\n\t\treturn !Boolean.parseBoolean(x);\n\t}\n\n\tprotected static final boolean isEmpty(Object x) {\n\t\treturn x == null || x.toString().length() == 0;\n\t}\n\n\tprotected AbstractSasl(Map<? super String, ?> props, CallbackHandler callbackHandler) {\n\t\tthis.props = props;\n\t\tthis.callbackHandler = callbackHandler;\n\t}\n\n\t@Override\n\tpublic void dispose() {\n\t\tthis.authorizedId = null;\n\t}\n\n\t@Override\n\tpublic Object getNegotiatedProperty(String propName) {\n\t\tif (!isComplete()) {\n\t\t\tthrow new IllegalStateException(\"Server negotiation not complete\");\n\t\t}\n\t\treturn negotiatedProperty.get(propName);\n\t}\n\n\tprotected void handleCallbacks(Callback... callbacks) throws SaslException {\n\t\ttry {\n\t\t\tcallbackHandler.handle(callbacks);\n\t\t} catch (XmppSaslException e) {\n\t\t\tthrow e;\n\t\t} catch (IOException e) {\n\t\t\tthrow new SaslException(e.getMessage(), e);\n\t\t} catch (UnsupportedCallbackException e) {\n\t\t\tthrow new SaslException(\"Callback not supported by handler\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isComplete() {\n\t\treturn complete;\n\t}\n\n\tprotected String[] split(final byte[] byteArray, final String defaultValue) {\n\t\tif (byteArray == null) {\n\t\t\treturn new String[]{};\n\t\t}\n\t\tArrayList<String> result = new ArrayList<String>();\n\n\t\tint pi = 0;\n\t\tfor (int i = 0; i < byteArray.length; i++) {\n\t\t\tif (byteArray[i] == 0) {\n\t\t\t\tString item;\n\t\t\t\tif (pi == i) {\n\t\t\t\t\titem = defaultValue;\n\t\t\t\t\tpi = i + 1;\n\t\t\t\t} else {\n\t\t\t\t\titem = new String(byteArray, pi, i - pi);\n\t\t\t\t\tpi = i + 1;\n\t\t\t\t}\n\t\t\t\tresult.add(item);\n\t\t\t}\n\n\t\t}\n\t\tif (pi < byteArray.length) {\n\t\t\tString item = new String(byteArray, pi, byteArray.length - pi);\n\t\t\tresult.add(item);\n\t\t} else {\n\t\t\tresult.add(defaultValue);\n\t\t}\n\n\t\treturn result.toArray(new String[]{});\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/mechanisms/AbstractSaslSCRAM.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.mechanisms;\n\nimport tigase.auth.SaslInvalidLoginExcepion;\nimport tigase.auth.XmppSaslException;\nimport tigase.auth.XmppSaslException.SaslError;\nimport tigase.auth.callbacks.*;\nimport tigase.util.Base64;\nimport tigase.xml.Element;\nimport tigase.xmpp.XMPPResourceConnection;\n\nimport javax.crypto.Mac;\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.SecretKeySpec;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.callback.NameCallback;\nimport javax.security.sasl.AuthorizeCallback;\nimport javax.security.sasl.SaslException;\nimport java.io.ByteArrayOutputStream;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.security.InvalidKeyException;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.SecureRandom;\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.Random;\nimport java.util.logging.Level;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\npublic abstract class AbstractSaslSCRAM\n\t\textends AbstractSasl {\n\n\tpublic static final String TLS_UNIQUE_ID_KEY = \"TLS_UNIQUE_ID_KEY\";\n\tpublic static final String TLS_EXPORTER_KEY = \"TLS_EXPORTER_KEY\";\n\tpublic static final String LOCAL_CERTIFICATE_KEY = \"LOCAL_CERTIFICATE_KEY\";\n\tprivate static final Charset CHARSET = StandardCharsets.UTF_8;\n\tfinal static byte[] DEFAULT_CLIENT_KEY = \"Client Key\".getBytes(CHARSET);\n\tfinal static byte[] DEFAULT_SERVER_KEY = \"Server Key\".getBytes(CHARSET);\n\tprivate final static String ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n\tprivate final static Pattern CLIENT_FIRST_MESSAGE = Pattern.compile(\n\t\t\t\"^(?<gs2Header>(?:y|n|p=(?<cbName>[a-zA-z0-9.-]+)),\" +\n\t\t\t\t\t\"(?:a=(?<authzid>(?:[^,\\\\x00-\\\\x20]|=2C|=3D)+))?,)(?<clientFirstBare>(?<mext>m=[^\\\\000=]+,)\" +\n\t\t\t\t\t\"?n=(?<username>(?:[^,\\\\x00-\\\\x20]|=2C|=3D)+),r=(?<nonce>[^,\\\\x00-\\\\x20]+)(?:,.*)?)$\");\n\tprivate final static Pattern CLIENT_LAST_MESSAGE = Pattern.compile(\"^(?<withoutProof>c=(?<cb>[a-zA-Z0-9/+=]+),\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"r=(?<nonce>[^,\\\\x00-\\\\x20]+)(?:,.*)?),p=(?<proof>[a-zA-Z0-9/+=]+)$\");\n\n\tpublic enum BindType {\n\t\t/**\n\t\t * Client doesn't support channel binding.\n\t\t */\n\t\tn,\n\t\t/**\n\t\t * Client does support channel binding but thinks the server does not.\n\t\t */\n\t\ty,\n\t\t/**\n\t\t * Client requires channel binding: <code>tls-unique</code>.\n\t\t */\n\t\ttls_unique,\n\t\t/**\n\t\t * Client requires channel binding: <code>tls-server-end-point</code>.\n\t\t */\n\t\ttls_server_end_point,\n\t\t/**\n\t\t * Client requires channel binding: <code>tls-exporter</code>.\n\t\t */\n\t\ttls_exporter\n\t}\n\n\tprivate final String algorithm;\n\tprivate final String hmacAlgorithm;\n\tprivate final String mechanismName;\n\tprivate final Random random = new SecureRandom();\n\tprivate final String serverNonce;\n\tprivate byte[] bindingData = null;\n\tprivate String cfmAuthzid;\n\tprivate String cfmBareMessage;\n\tprivate String cfmGs2header;\n\tprivate String cfmUsername;\n\tprivate BindType requestedBindType;\n\tprivate byte[] serverKey;\n\tprivate String sfmMessage;\n\tprivate String sfmNonce;\n\tprivate Step step = Step.clientFirstMessage;\n\tprivate byte[] storedKey;\n\n\tpublic static Element getSupportedChannelBindings(XMPPResourceConnection session) {\n\t\tElement bindings = new Element(\"sasl-channel-binding\");\n\t\tbindings.setXMLNS(\"urn:xmpp:sasl-cb:0\");\n\t\tbindings.addChild(new Element(\"channel-binding\", new String[]{\"type\"}, new String[]{\"tls-server-end-point\"}));\n\t\tif (session.getSessionData(AbstractSaslSCRAM.TLS_UNIQUE_ID_KEY) != null) {\n\t\t\tbindings.addChild(new Element(\"channel-binding\", new String[]{\"type\"}, new String[]{\"tls-unique\"}));\n\t\t}\n\t\tif (session.getSessionData(AbstractSaslSCRAM.TLS_EXPORTER_KEY) != null) {\n\t\t\tbindings.addChild(new Element(\"channel-binding\", new String[]{\"type\"}, new String[]{\"tls-exporter\"}));\n\t\t}\n\t\treturn bindings;\n\t}\n\n\tpublic static byte[] h(String algorithm, byte[] data) throws NoSuchAlgorithmException {\n\t\tMessageDigest digest = MessageDigest.getInstance(algorithm);\n\t\treturn digest.digest(data);\n\t}\n\n\tpublic static byte[] hi(String algorithm, byte[] password, final byte[] salt, final int iterations)\n\t\t\tthrows InvalidKeyException, NoSuchAlgorithmException {\n\t\tif (algorithm.startsWith(\"SHA-\")) {\n\t\t\talgorithm = algorithm.replace(\"SHA-\", \"SHA\");\n\t\t}\n\t\tfinal SecretKeySpec k = new SecretKeySpec(password, \"Hmac\" + algorithm);\n\n\t\tbyte[] z = new byte[salt.length + 4];\n\t\tSystem.arraycopy(salt, 0, z, 0, salt.length);\n\t\tSystem.arraycopy(new byte[]{0, 0, 0, 1}, 0, z, salt.length, 4);\n\n\t\tbyte[] u = hmac(k, z);\n\t\tbyte[] result = new byte[u.length];\n\t\tSystem.arraycopy(u, 0, result, 0, result.length);\n\n\t\tint i = 1;\n\t\twhile (i < iterations) {\n\t\t\tu = hmac(k, u);\n\t\t\tfor (int j = 0; j < u.length; j++) {\n\t\t\t\tresult[j] ^= u[j];\n\t\t\t}\n\t\t\t++i;\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprotected static byte[] hmac(final SecretKey key, byte[] data)\n\t\t\tthrows NoSuchAlgorithmException, InvalidKeyException {\n\t\tMac mac = Mac.getInstance(key.getAlgorithm());\n\t\tmac.init(key);\n\t\treturn mac.doFinal(data);\n\t}\n\n\tpublic static byte[] hmac(String algorithm, final byte[] key, byte[] data)\n\t\t\tthrows NoSuchAlgorithmException, InvalidKeyException {\n\t\tif (algorithm.startsWith(\"SHA-\")) {\n\t\t\talgorithm = algorithm.replace(\"SHA-\", \"SHA\");\n\t\t}\n\t\tfinal SecretKeySpec k = new SecretKeySpec(key, \"Hmac\" + algorithm);\n\t\treturn hmac(k, data);\n\t}\n\n\tpublic static byte[] normalize(String str) {\n\t\treturn str.getBytes(CHARSET);\n\t}\n\n\tprotected AbstractSaslSCRAM(String mechanismName, String algorithm, Map<? super String, ?> props,\n\t\t\t\t\t\t\t\tCallbackHandler callbackHandler) {\n\t\tsuper(props, callbackHandler);\n\t\tthis.mechanismName = mechanismName;\n\t\tthis.algorithm = algorithm;\n\t\tthis.hmacAlgorithm = \"Hmac\" + (algorithm.startsWith(\"SHA-\") ? algorithm.replace(\"SHA-\", \"SHA\") : algorithm);\n\t\tserverNonce = randomString();\n\t}\n\n\tprotected AbstractSaslSCRAM(String mechanismName, String algorithm, Map<? super String, ?> props,\n\t\t\t\t\t\t\t\tCallbackHandler callbackHandler, String serverOnce) {\n\t\tsuper(props, callbackHandler);\n\t\tthis.mechanismName = mechanismName;\n\t\tthis.algorithm = algorithm;\n\t\tthis.hmacAlgorithm = \"Hmac\" + (algorithm.startsWith(\"SHA-\") ? algorithm.replace(\"SHA-\", \"SHA\") : algorithm);\n\t\tthis.serverNonce = serverOnce;\n\t}\n\n\t@Override\n\tpublic byte[] evaluateResponse(byte[] response) throws SaslException {\n\t\ttry {\n\t\t\treturn switch (step) {\n\t\t\t\tcase clientFirstMessage -> processClientFirstMessage(response);\n\t\t\t\tcase clientFinalMessage -> processClientLastMessage(response);\n\t\t\t\tdefault -> throw new SaslException(getMechanismName() + \": Server at illegal state\");\n\t\t\t};\n\t\t} catch (SaslException e) {\n\t\t\tthrow e;\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.FINEST, \"Error evaluating response\", e);\n\t\t\tthrow new SaslException(\"SASL Failed\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getAuthorizationID() {\n\t\treturn authorizedId;\n\t}\n\n\t@Override\n\tpublic String getMechanismName() {\n\t\treturn mechanismName;\n\t}\n\n\t@Override\n\tpublic byte[] unwrap(byte[] incoming, int offset, int len) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic byte[] wrap(byte[] outgoing, int offset, int len) {\n\t\treturn null;\n\t}\n\n\tprotected byte[] calculateC() {\n\t\ttry {\n\t\t\tfinal ByteArrayOutputStream result = new ByteArrayOutputStream();\n\n\t\t\tresult.write(this.cfmGs2header.getBytes(CHARSET));\n\n\t\t\tif (this.requestedBindType == BindType.tls_unique ||\n\t\t\t\t\tthis.requestedBindType == BindType.tls_server_end_point ||\n\t\t\t\t\tthis.requestedBindType == BindType.tls_exporter) {\n\t\t\t\tresult.write(bindingData);\n\t\t\t}\n\t\t\treturn result.toByteArray();\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tprotected abstract void checkRequestedBindType(BindType requestedBindType) throws SaslException;\n\n\tprotected byte[] h(byte[] data) throws NoSuchAlgorithmException {\n\t\tMessageDigest digest = MessageDigest.getInstance(algorithm);\n\t\treturn digest.digest(data);\n\t}\n\n\tprotected SecretKey key(final byte[] key) {\n\t\treturn new SecretKeySpec(key, hmacAlgorithm);\n\t}\n\n\tprotected byte[] processClientFirstMessage(byte[] data) throws SaslException {\n\t\tMatcher r = CLIENT_FIRST_MESSAGE.matcher(new String(data, CHARSET));\n\t\tif (!r.matches()) {\n\t\t\tthrow new SaslException(\"Bad challenge syntax\");\n\t\t}\n\n\t\tthis.cfmGs2header = r.group(\"gs2Header\");\n\t\tString cfmCbname = r.group(\"cbName\");\n\t\tthis.requestedBindType = createBindType(this.cfmGs2header, cfmCbname);\n\t\tthis.cfmAuthzid = r.group(\"authzid\");\n\t\tthis.cfmBareMessage = r.group(\"clientFirstBare\");\n//\t\tfinal String cfmMext = r.group(\"mext\");\n\t\tthis.cfmUsername = r.group(\"username\");\n\t\tfinal String cfmNonce = r.group(\"nonce\");\n\n\t\tcheckRequestedBindType(requestedBindType);\n\n\t\tfinal ChannelBindingCallback cc = new ChannelBindingCallback(\"Channel binding data\", this.requestedBindType);\n\t\tfinal NameCallback nc = new NameCallback(\"Authentication identity\", cfmUsername);\n\t\tfinal AuthorizationIdCallback ai = new AuthorizationIdCallback(\"Authorization identity\", this.cfmAuthzid);\n\t\tfinal PBKDIterationsCallback ic = new PBKDIterationsCallback(\"PBKD2 iterations\");\n\t\tfinal SaltCallback sc = new SaltCallback(\"Salt\");\n\t\tfinal ServerKeyCallback sec = new ServerKeyCallback(\"Server Key\");\n\t\tfinal StoredKeyCallback stc = new StoredKeyCallback(\"Stored Key\");\n\n\t\thandleCallbacks(nc, ai, ic, sc, sec, stc, cc);\n\n\t\tvalidateKeyPresence(sec.getServerKey(), \"Server Key\");\n\t\tthis.serverKey = sec.getServerKey();\n\n\t\tvalidateKeyPresence(stc.getStoredKey(), \"Stored Key\");\n\t\tthis.storedKey = stc.getStoredKey();\n\n\t\tthis.cfmAuthzid = ai.getAuthzId();\n\t\tif (this.cfmAuthzid == null) {\n\t\t\tthis.cfmAuthzid = nc.getName();\n\t\t}\n\n\t\tvalidateBindingsData(requestedBindType, cc.getBindingData());\n\t\tthis.bindingData = cc.getBindingData();\n\n\t\tthis.sfmNonce = cfmNonce + serverNonce;\n\n\t\tthis.sfmMessage = \"r=\" + sfmNonce + \",\" + \"s=\" + Base64.encode(sc.getSalt()) + \",\" + \"i=\" + ic.getInterations();\n\t\tstep = Step.clientFinalMessage;\n\t\treturn sfmMessage.getBytes(CHARSET);\n\t}\n\n\tprotected byte[] processClientLastMessage(byte[] data)\n\t\t\tthrows SaslException, InvalidKeyException, NoSuchAlgorithmException {\n\t\tMatcher r = CLIENT_LAST_MESSAGE.matcher(new String(data, CHARSET));\n\t\tif (!r.matches()) {\n\t\t\tthrow new SaslException(\"Bad challenge syntax\");\n\t\t}\n\n\t\tfinal String clmWithoutProof = r.group(\"withoutProof\");\n\t\tfinal byte[] clmCb = Base64.decode(r.group(\"cb\"));\n\t\tfinal String clmNonce = r.group(\"nonce\");\n\t\tfinal String clmProof = r.group(\"proof\");\n\n\t\tbyte[] calculatedCb = calculateC();\n\t\tif (!(new String(clmCb, CHARSET)).startsWith(cfmGs2header)) {\n\t\t\tthrow new XmppSaslException(SaslError.not_authorized, \"Invalid GS2 header\");\n\t\t} else if (!Arrays.equals(clmCb, calculatedCb)) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Channel bindings does not match. expected: {0}; received: {1}\",\n\t\t\t\t\t\tnew Object[]{calculatedCb, clmCb});\n\t\t\t}\n\t\t\tthrow new SaslInvalidLoginExcepion(SaslError.not_authorized, cfmAuthzid, \"Channel bindings does not match\");\n\t\t}\n\n\t\tif (!clmNonce.equals(sfmNonce)) {\n\t\t\tthrow new SaslInvalidLoginExcepion(SaslError.not_authorized, cfmAuthzid, \"Wrong nonce\");\n\t\t}\n\n\t\tfinal String authMessage = cfmBareMessage + \",\" + sfmMessage + \",\" + clmWithoutProof;\n\t\tbyte[] clientSignature = hmac(key(storedKey), authMessage.getBytes(CHARSET));\n\n\t\tbyte[] clientProof = Base64.decode(clmProof);\n\t\tbyte[] calculatedClientKey = xor(clientProof, clientSignature);\n\t\tbyte[] calculatedStoredKey = h(calculatedClientKey);\n\t\tboolean proofMatch = Arrays.equals(calculatedStoredKey, storedKey);\n\n\t\tif (!proofMatch) {\n\t\t\tthrow new SaslInvalidLoginExcepion(SaslError.not_authorized, cfmAuthzid, PASSWORD_NOT_VERIFIED_MSG);\n\t\t}\n\n\t\tfinal AuthorizeCallback ac = new AuthorizeCallback(cfmUsername, cfmAuthzid);\n\t\thandleCallbacks(ac);\n\t\tif (ac.isAuthorized()) {\n\t\t\tauthorizedId = ac.getAuthorizedID();\n\t\t} else {\n\t\t\tthrow new SaslInvalidLoginExcepion(SaslError.invalid_authzid, cfmAuthzid,\n\t\t\t\t\t\t\t\t\t\t\t   \"SCRAM: \" + cfmAuthzid + \" is not authorized to act as \" + cfmAuthzid);\n\t\t}\n\n\t\tbyte[] serverSignature = hmac(key(serverKey), authMessage.getBytes(CHARSET));\n\n\t\tString serverStringMessage = \"v=\" + Base64.encode(serverSignature);\n\t\tstep = Step.finished;\n\t\tcomplete = true;\n\t\treturn serverStringMessage.getBytes(CHARSET);\n\t}\n\n\tprotected void validateBindingsData(BindType requestedBindType, byte[] bindingData) {\n\t\tif (requestedBindType == BindType.tls_server_end_point && bindingData == null) {\n\t\t\tthrow new RuntimeException(\"Binding data not found!\");\n\t\t} else if (requestedBindType == BindType.tls_unique && bindingData == null) {\n\t\t\tthrow new RuntimeException(\"Binding data not found!\");\n\t\t} else if (requestedBindType == BindType.tls_exporter && bindingData == null) {\n\t\t\tthrow new RuntimeException(\"Binding data not found!\");\n\t\t}\n\t}\n\n\tprotected byte[] xor(final byte[] a, final byte[] b) {\n\t\tfinal int l = a.length;\n\t\tbyte[] r = new byte[l];\n\t\tfor (int i = 0; i < l; i++) {\n\t\t\tr[i] = (byte) (a[i] ^ b[i]);\n\t\t}\n\t\treturn r;\n\t}\n\n\tprivate BindType createBindType(final String cfmGs2header, final String cfmCbname) throws SaslException {\n\t\tfinal char t = cfmGs2header.charAt(0);\n\n\t\tif ('n' == t) {\n\t\t\treturn BindType.n;\n\t\t} else if ('y' == t) {\n\t\t\treturn BindType.y;\n\t\t} else if (\"tls-unique\".equals(cfmCbname)) {\n\t\t\treturn BindType.tls_unique;\n\t\t} else if (\"tls-server-end-point\".equals(cfmCbname)) {\n\t\t\treturn BindType.tls_server_end_point;\n\t\t} else if (\"tls-exporter\".equals(cfmCbname)) {\n\t\t\treturn BindType.tls_exporter;\n\t\t} else {\n\t\t\tthrow new SaslException(\"Unsupported channel binding type\");\n\t\t}\n\t}\n\n\tprivate String randomString() {\n\t\tfinal int length = 20;\n\t\tfinal int x = ALPHABET.length();\n\t\tchar[] buffer = new char[length];\n\t\tfor (int i = 0; i < length; i++) {\n\t\t\tint r = random.nextInt(x);\n\t\t\tbuffer[i] = ALPHABET.charAt(r);\n\t\t}\n\t\treturn new String(buffer);\n\t}\n\n\tprivate void validateKeyPresence(final byte[] key, final String keyName) throws SaslException {\n\t\tif (key == null) {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"There is no {1} of user {0}.\", new Object[]{cfmUsername, keyName});\n\t\t\t}\n\t\t\tthrow new SaslException(\"Unknown user\");\n\t\t} else if (key.length == 0) {\n\t\t\tif (log.isLoggable(Level.WARNING)) {\n\t\t\t\tlog.log(Level.WARNING, \"User {0} exists, but his {1} is empty.\", new Object[]{cfmUsername, keyName});\n\t\t\t}\n\t\t\tthrow new SaslException(\"Unknown user\");\n\t\t}\n\t}\n\n\tprivate enum Step {\n\t\tclientFinalMessage,\n\t\tclientFirstMessage,\n\t\tfinished\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/mechanisms/SCRAMHelper.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.mechanisms;\n\nimport java.security.InvalidKeyException;\nimport java.security.NoSuchAlgorithmException;\n\npublic class SCRAMHelper {\n\n\t/**\n\t * Encode plain password to SCRAM authentication data: ServerKey and StoredKey.\n\t *\n\t * @param algorithm algorithm.\n\t * @param salt salt.\n\t * @param iterations iterations.\n\t * @param plainPassword plain password.\n\t *\n\t * @return authentication data\n\t */\n\tpublic static AuthenticationData encodePlainPassword(String algorithm, byte[] salt, int iterations,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t String plainPassword)\n\t\t\tthrows NoSuchAlgorithmException, InvalidKeyException {\n\t\tvar saltedPassword = AbstractSaslSCRAM.hi(algorithm, AbstractSaslSCRAM.normalize(plainPassword), salt,\n\t\t\t\t\t\t\t\t\t\t\t\t  iterations);\n\t\treturn transcode(algorithm, saltedPassword);\n\t}\n\n\t/**\n\t * Transcode Salted Password (PBKDF2) to SCRAM authentication data: ServerKey and StoredKey.\n\t *\n\t * @param algorithm algorithm.\n\t * @param saltedPassword salted password to transcode.\n\t *\n\t * @return authentication data.\n\t */\n\tpublic static AuthenticationData transcode(String algorithm, byte[] saltedPassword)\n\t\t\tthrows NoSuchAlgorithmException, InvalidKeyException {\n\t\tbyte[] ck = AbstractSaslSCRAM.hmac(algorithm, saltedPassword, AbstractSaslSCRAM.DEFAULT_CLIENT_KEY);\n\t\tvar storedKey = AbstractSaslSCRAM.h(algorithm, ck);\n\t\tvar serverKey = AbstractSaslSCRAM.hmac(algorithm, saltedPassword, AbstractSaslSCRAM.DEFAULT_SERVER_KEY);\n\t\treturn new AuthenticationData(algorithm, storedKey, serverKey);\n\t}\n\n\tprivate SCRAMHelper() {\n\t}\n\n\tpublic record AuthenticationData(String algorithm, byte[] storedKey, byte[] serverKey) {\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/mechanisms/SaslANONYMOUS.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.mechanisms;\n\nimport tigase.auth.XmppSaslException;\nimport tigase.auth.XmppSaslException.SaslError;\n\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.callback.NameCallback;\nimport javax.security.sasl.SaslException;\nimport java.util.Map;\nimport java.util.UUID;\n\n/**\n * SASL-ANONYMOUS mechanism. <br> Called {@linkplain Callback callbacks} in order: <ul> <li>{@link NameCallback}</li>\n * </ul>\n */\npublic class SaslANONYMOUS\n\t\textends AbstractSasl {\n\n\tpublic static final String IS_ANONYMOUS_PROPERTY = \"IS_ANONYMOUS\";\n\tpublic static final String NAME = \"ANONYMOUS\";\n\n\tSaslANONYMOUS(Map<? super String, ?> props, CallbackHandler callbackHandler) {\n\t\tsuper(props, callbackHandler);\n\t\tnegotiatedProperty.put(IS_ANONYMOUS_PROPERTY, Boolean.TRUE);\n\t}\n\n\t@Override\n\tpublic byte[] evaluateResponse(byte[] response) throws SaslException {\n\t\tNameCallback nc = new NameCallback(\"ANONYMOUS identity\", UUID.randomUUID().toString());\n\t\thandleCallbacks(nc);\n\n\t\tthis.authorizedId = nc.getName() != null ? nc.getName() : nc.getDefaultName();\n\n\t\tif (this.authorizedId == null) {\n\t\t\tthrow new XmppSaslException(SaslError.temporary_auth_failure);\n\t\t}\n\n\t\tcomplete = true;\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getAuthorizationID() {\n\t\treturn authorizedId;\n\t}\n\n\t@Override\n\tpublic String getMechanismName() {\n\t\treturn NAME;\n\t}\n\n\t@Override\n\tpublic byte[] unwrap(byte[] incoming, int offset, int len) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic byte[] wrap(byte[] outgoing, int offset, int len) {\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/mechanisms/SaslEXTERNAL.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.mechanisms;\n\nimport tigase.auth.SaslInvalidLoginExcepion;\nimport tigase.auth.XmppSaslException;\nimport tigase.auth.XmppSaslException.SaslError;\nimport tigase.auth.callbacks.ValidateCertificateData;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xmpp.jid.BareJID;\n\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.sasl.SaslException;\nimport java.util.Map;\n\npublic class SaslEXTERNAL\n\t\textends AbstractSasl {\n\n\tpublic static final String PEER_CERTIFICATE_KEY = \"PEER_CERTIFICATE_ENTRY_KEY\";\n\tpublic static final String NAME = \"EXTERNAL\";\n\n\tSaslEXTERNAL(Map<? super String, ?> props, CallbackHandler callbackHandler) {\n\t\tsuper(props, callbackHandler);\n\t}\n\n\t@Override\n\tpublic byte[] evaluateResponse(byte[] response) throws SaslException {\n\t\tBareJID jid;\n\t\ttry {\n\t\t\tif (response != null && response.length > 0) {\n\t\t\t\tjid = BareJID.bareJIDInstance(new String(response));\n\t\t\t} else {\n\t\t\t\tjid = null;\n\t\t\t}\n\t\t} catch (TigaseStringprepException e) {\n\t\t\tthrow new XmppSaslException(SaslError.malformed_request);\n\t\t}\n\n\t\tfinal ValidateCertificateData ac = new ValidateCertificateData(jid);\n\t\thandleCallbacks(ac);\n\n\t\tif (ac.isAuthorized() == true) {\n\t\t\tauthorizedId = ac.getAuthorizedID();\n\t\t} else {\n\t\t\tthrow new SaslInvalidLoginExcepion(SaslError.invalid_authzid,jid.toString());\n\t\t}\n\n\t\tcomplete = true;\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getAuthorizationID() {\n\t\treturn authorizedId;\n\t}\n\n\t@Override\n\tpublic String getMechanismName() {\n\t\treturn NAME;\n\t}\n\n\t@Override\n\tpublic byte[] unwrap(byte[] incoming, int offset, int len) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic byte[] wrap(byte[] outgoing, int offset, int len) {\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/mechanisms/SaslPLAIN.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.mechanisms;\n\nimport tigase.auth.SaslInvalidLoginExcepion;\nimport tigase.auth.XmppSaslException;\nimport tigase.auth.XmppSaslException.SaslError;\nimport tigase.auth.callbacks.AuthorizationIdCallback;\nimport tigase.auth.callbacks.VerifyPasswordCallback;\n\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.callback.NameCallback;\nimport javax.security.sasl.AuthorizeCallback;\nimport javax.security.sasl.SaslException;\nimport java.util.Map;\n\n/**\n * SASL-PLAIN mechanism. <br> Called {@linkplain Callback callbacks} in order: <ul> <li>{@link NameCallback}</li>\n * <li>{@link VerifyPasswordCallback}</li> <li>{@link AuthorizeCallback}</li> </ul>\n */\npublic class SaslPLAIN\n\t\textends AbstractSasl {\n\n\tpublic static final String NAME = \"PLAIN\";\n\n\tSaslPLAIN(Map<? super String, ?> props, CallbackHandler callbackHandler) {\n\t\tsuper(props, callbackHandler);\n\t}\n\n\t@Override\n\tpublic byte[] evaluateResponse(byte[] response) throws SaslException {\n\n\t\tString[] data = split(response, \"\");\n\n\t\tif (data.length != 3) {\n\t\t\tthrow new XmppSaslException(SaslError.malformed_request, \"Invalid number of message parts\");\n\t\t}\n\n\t\tfinal String authzid = data[0];\n\t\tfinal String authcid = data[1];\n\t\tfinal String passwd = data[2];\n\n\t\tif (authcid.length() < 1) {\n\t\t\tthrow new XmppSaslException(SaslError.malformed_request, \"Authentication identity string is empty\");\n\t\t}\n\n\t\tif (authcid.length() > 255) {\n\t\t\tthrow new XmppSaslException(SaslError.malformed_request, \"Authentication identity string is too long\");\n\t\t}\n\n\t\tif (!isEmpty(authzid) && authzid.length() > 255) {\n\t\t\tthrow new XmppSaslException(SaslError.malformed_request, \"Authorization identity string is too long\");\n\t\t}\n\n\t\tif (passwd.length() > 255) {\n\t\t\tthrow new XmppSaslException(SaslError.malformed_request, \"Password string is too long\");\n\t\t}\n\n\t\tfinal NameCallback nc = new NameCallback(\"Authentication identity\", authcid);\n\t\tfinal AuthorizationIdCallback ai = new AuthorizationIdCallback(\"Authorization identity\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   isEmpty(authzid) ? null : authzid);\n\t\tfinal VerifyPasswordCallback vpc = new VerifyPasswordCallback(passwd);\n\n\t\thandleCallbacks(nc, ai, vpc);\n\n\t\tif (vpc.isVerified() == false) {\n\t\t\tthrow new SaslInvalidLoginExcepion(SaslError.not_authorized, nc.getName(), PASSWORD_NOT_VERIFIED_MSG);\n\t\t}\n\n\t\tfinal String authorizationJID = ai.getAuthzId() == null ? nc.getName() : ai.getAuthzId();\n\n\t\tfinal AuthorizeCallback ac = new AuthorizeCallback(nc.getName(), authorizationJID);\n\t\thandleCallbacks(ac);\n\n\t\tif (ac.isAuthorized() == true) {\n\t\t\tauthorizedId = ac.getAuthorizedID();\n\t\t} else {\n\t\t\tthrow new SaslInvalidLoginExcepion(SaslError.invalid_authzid, nc.getName(),\n\t\t\t\t\t\t\t\t\t\t\t   \"PLAIN: \" + authcid + \" is not authorized to act as \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t   authorizationJID);\n\t\t}\n\n\t\tcomplete = true;\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getAuthorizationID() {\n\t\treturn authorizedId;\n\t}\n\n\t@Override\n\tpublic String getMechanismName() {\n\t\treturn NAME;\n\t}\n\n\t@Override\n\tpublic byte[] unwrap(byte[] incoming, int offset, int len) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic byte[] wrap(byte[] outgoing, int offset, int len) {\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/mechanisms/SaslSCRAM.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.mechanisms;\n\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.sasl.SaslException;\nimport java.util.Map;\n\npublic class SaslSCRAM\n\t\textends AbstractSaslSCRAM {\n\n\tpublic final static String NAME = \"SCRAM-SHA-1\";\n\tprotected final static String ALGO = \"SHA1\";\n\n\tpublic SaslSCRAM(Map<? super String, ?> props, CallbackHandler callbackHandler) {\n\t\tsuper(NAME, ALGO, props, callbackHandler);\n\t}\n\n\tSaslSCRAM(Map<? super String, ?> props, CallbackHandler callbackHandler, String once) {\n\t\tsuper(NAME, ALGO, props, callbackHandler, once);\n\t}\n\n\t@Override\n\tprotected void checkRequestedBindType(BindType requestedBindType) throws SaslException {\n\t\tswitch (requestedBindType) {\n\t\t\tcase n:\n\t\t\t\tbreak;\n\t\t\tcase y:\n\t\t\t\t// throw new SaslException(\"Server supports PLUS. Please use 'p'\");\n\t\t\t\tbreak;\n\t\t\tcase tls_server_end_point:\n\t\t\tcase tls_exporter:\n\t\t\tcase tls_unique:\n\t\t\t\tthrow new SaslException(\"Invalid request for \" + NAME);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/mechanisms/SaslSCRAMPlus.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.mechanisms;\n\nimport tigase.xmpp.XMPPResourceConnection;\n\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.sasl.SaslException;\nimport java.util.Collection;\nimport java.util.Map;\n\npublic class SaslSCRAMPlus\n\t\textends AbstractSaslSCRAM {\n\n\tpublic final static String NAME = \"SCRAM-SHA-1-PLUS\";\n\tprotected final static String ALGO = \"SHA1\";\n\n\tpublic static boolean containsScramPlus(Collection<String> mechanisms) {\n\t\tfor (String name : mechanisms) {\n\t\t\tif (name.startsWith(\"SCRAM-\") && name.endsWith(\"-PLUS\")) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic static boolean isAvailable(XMPPResourceConnection session) {\n\t\treturn session.getSessionData(AbstractSaslSCRAM.TLS_UNIQUE_ID_KEY) != null ||\n\t\t\t\tsession.getSessionData(AbstractSaslSCRAM.LOCAL_CERTIFICATE_KEY) != null;\n\t}\n\n\tpublic SaslSCRAMPlus(Map<? super String, ?> props, CallbackHandler callbackHandler) {\n\t\tsuper(NAME, ALGO, props, callbackHandler);\n\t}\n\n\tSaslSCRAMPlus(Map<? super String, ?> props, CallbackHandler callbackHandler, String once) {\n\t\tsuper(NAME, ALGO, props, callbackHandler, once);\n\t}\n\n\t@Override\n\tprotected void checkRequestedBindType(BindType requestedBindType) throws SaslException {\n\t\tswitch (requestedBindType) {\n\t\t\tcase n:\n\t\t\t\tthrow new SaslException(\"Invalid request for \" + NAME);\n\t\t\tcase y:\n\t\t\t\tthrow new SaslException(\"Server supports PLUS. Please use 'p'\");\n\t\t\tcase tls_server_end_point:\n\t\t\tcase tls_exporter:\n\t\t\tcase tls_unique:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/mechanisms/SaslSCRAMSha256.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.mechanisms;\n\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.sasl.SaslException;\nimport java.util.Map;\n\npublic class SaslSCRAMSha256\n\t\textends AbstractSaslSCRAM {\n\n\tpublic final static String NAME = \"SCRAM-SHA-256\";\n\tprotected final static String ALGO = \"SHA-256\";\n\n\tpublic SaslSCRAMSha256(Map<? super String, ?> props, CallbackHandler callbackHandler) {\n\t\tsuper(NAME, ALGO, props, callbackHandler);\n\t}\n\n\tSaslSCRAMSha256(Map<? super String, ?> props, CallbackHandler callbackHandler, String once) {\n\t\tsuper(NAME, ALGO, props, callbackHandler, once);\n\t}\n\n\t@Override\n\tprotected void checkRequestedBindType(BindType requestedBindType) throws SaslException {\n\t\tswitch (requestedBindType) {\n\t\t\tcase n:\n\t\t\t\tbreak;\n\t\t\tcase y:\n\t\t\t\t// throw new SaslException(\"Server supports PLUS. Please use 'p'\");\n\t\t\t\tbreak;\n\t\t\tcase tls_server_end_point:\n\t\t\tcase tls_exporter:\n\t\t\tcase tls_unique:\n\t\t\t\tthrow new SaslException(\"Invalid request for \" + NAME);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/mechanisms/SaslSCRAMSha256Plus.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.mechanisms;\n\nimport tigase.xmpp.XMPPResourceConnection;\n\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.sasl.SaslException;\nimport java.util.Map;\n\npublic class SaslSCRAMSha256Plus\n\t\textends AbstractSaslSCRAM {\n\n\tpublic final static String NAME = \"SCRAM-SHA-256-PLUS\";\n\tprotected final static String ALGO = \"SHA-256\";\n\n\tpublic static boolean isAvailable(XMPPResourceConnection session) {\n\t\treturn session.getSessionData(AbstractSaslSCRAM.TLS_UNIQUE_ID_KEY) != null ||\n\t\t\t\tsession.getSessionData(AbstractSaslSCRAM.LOCAL_CERTIFICATE_KEY) != null;\n\t}\n\n\tpublic SaslSCRAMSha256Plus(Map<? super String, ?> props, CallbackHandler callbackHandler) {\n\t\tsuper(NAME, ALGO, props, callbackHandler);\n\t}\n\n\tSaslSCRAMSha256Plus(Map<? super String, ?> props, CallbackHandler callbackHandler, String once) {\n\t\tsuper(NAME, ALGO, props, callbackHandler, once);\n\t}\n\n\t@Override\n\tprotected void checkRequestedBindType(BindType requestedBindType) throws SaslException {\n\t\tswitch (requestedBindType) {\n\t\t\tcase n:\n\t\t\t\tthrow new SaslException(\"Invalid request for \" + NAME);\n\t\t\tcase y:\n\t\t\t\tthrow new SaslException(\"Server supports PLUS. Please use 'p'\");\n\t\t\tcase tls_server_end_point:\n\t\t\tcase tls_exporter:\n\t\t\tcase tls_unique:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/mechanisms/SaslSCRAMSha512.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.mechanisms;\n\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.sasl.SaslException;\nimport java.util.Map;\n\npublic class SaslSCRAMSha512\n\t\textends AbstractSaslSCRAM {\n\n\tpublic final static String NAME = \"SCRAM-SHA-512\";\n\tprotected final static String ALGO = \"SHA-512\";\n\n\tpublic SaslSCRAMSha512(Map<? super String, ?> props, CallbackHandler callbackHandler) {\n\t\tsuper(NAME, ALGO, props, callbackHandler);\n\t}\n\n\tSaslSCRAMSha512(Map<? super String, ?> props, CallbackHandler callbackHandler, String once) {\n\t\tsuper(NAME, ALGO, props, callbackHandler, once);\n\t}\n\n\t@Override\n\tprotected void checkRequestedBindType(BindType requestedBindType) throws SaslException {\n\t\tswitch (requestedBindType) {\n\t\t\tcase n:\n\t\t\t\tbreak;\n\t\t\tcase y:\n\t\t\t\t// throw new SaslException(\"Server supports PLUS. Please use 'p'\");\n\t\t\t\tbreak;\n\t\t\tcase tls_server_end_point:\n\t\t\tcase tls_exporter:\n\t\t\tcase tls_unique:\n\t\t\t\tthrow new SaslException(\"Invalid request for \" + NAME);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/mechanisms/SaslSCRAMSha512Plus.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.mechanisms;\n\nimport tigase.xmpp.XMPPResourceConnection;\n\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.sasl.SaslException;\nimport java.util.Map;\n\npublic class SaslSCRAMSha512Plus\n\t\textends AbstractSaslSCRAM {\n\n\tpublic final static String NAME = \"SCRAM-SHA-512-PLUS\";\n\tprotected final static String ALGO = \"SHA-512\";\n\n\tpublic static boolean isAvailable(XMPPResourceConnection session) {\n\t\treturn session.getSessionData(AbstractSaslSCRAM.TLS_UNIQUE_ID_KEY) != null ||\n\t\t\t\tsession.getSessionData(AbstractSaslSCRAM.LOCAL_CERTIFICATE_KEY) != null;\n\t}\n\n\tpublic SaslSCRAMSha512Plus(Map<? super String, ?> props, CallbackHandler callbackHandler) {\n\t\tsuper(NAME, ALGO, props, callbackHandler);\n\t}\n\n\tSaslSCRAMSha512Plus(Map<? super String, ?> props, CallbackHandler callbackHandler, String once) {\n\t\tsuper(NAME, ALGO, props, callbackHandler, once);\n\t}\n\n\t@Override\n\tprotected void checkRequestedBindType(BindType requestedBindType) throws SaslException {\n\t\tswitch (requestedBindType) {\n\t\t\tcase n:\n\t\t\t\tthrow new SaslException(\"Invalid request for \" + NAME);\n\t\t\tcase y:\n\t\t\t\tthrow new SaslException(\"Server supports PLUS. Please use 'p'\");\n\t\t\tcase tls_server_end_point:\n\t\t\tcase tls_exporter:\n\t\t\tcase tls_unique:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/mechanisms/SaslXTOKEN.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.mechanisms;\n\nimport tigase.auth.SaslInvalidLoginExcepion;\nimport tigase.auth.XmppSaslException;\nimport tigase.auth.callbacks.AuthorizationIdCallback;\nimport tigase.auth.callbacks.ReplaceServerKeyCallback;\nimport tigase.auth.callbacks.ServerKeyCallback;\nimport tigase.auth.callbacks.SharedSecretKeyCallback;\n\nimport javax.crypto.Mac;\nimport javax.crypto.spec.SecretKeySpec;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.callback.NameCallback;\nimport javax.security.sasl.AuthorizeCallback;\nimport javax.security.sasl.SaslException;\nimport java.nio.charset.StandardCharsets;\nimport java.security.InvalidKeyException;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.SecureRandom;\nimport java.util.Arrays;\nimport java.util.Map;\n\npublic class SaslXTOKEN\n\t\textends AbstractSasl {\n\n\tpublic static final String NAME = \"XTOKEN-HMAC-SHA-256\";\n\n\tprivate static final SecureRandom secureRandom = new SecureRandom();\n\tpublic static byte[] generateSecretKey() {\n\t\tbyte[] data = new byte[32];\n\t\tsecureRandom.nextBytes(data);\n\t\treturn data;\n\t}\n\n\tprivate Step step = Step.firstMessage;\n\n\tenum Step {\n\t\tfirstMessage,\n\t\tsecondMessage,\n\t\tfinished\n\t}\n\n\tSaslXTOKEN(Map<? super String, ?> props, CallbackHandler callbackHandler) {\n\t\tsuper(props, callbackHandler);\n\t}\n\t\n\t@Override\n\tpublic String getMechanismName() {\n\t\treturn NAME;\n\t}\n\n\t@Override\n\tpublic byte[] evaluateResponse(byte[] response) throws SaslException {\n\t\tif (response.length <= 64 || response[32] !=  0x00 || response[65] != 0x00) {\n\t\t\tthrow new XmppSaslException(XmppSaslException.SaslError.malformed_request, \"Invalid token format - too short\");\n\t\t}\n\n\t\tbyte[] data = Arrays.copyOfRange(response, 0, 32);\n\t\tbyte[] token = Arrays.copyOfRange(response, 33, 65);\n\n\t\tString authcid = new String(Arrays.copyOfRange(response, 66, response.length), StandardCharsets.UTF_8);\n\n\t\tfinal NameCallback nc = new NameCallback(\"Authentication identity\", authcid);\n\t\tfinal AuthorizationIdCallback ai = new AuthorizationIdCallback(\"Authorization identity\", null);\n\t\tfinal ServerKeyCallback vtc = new ServerKeyCallback(null);\n\t\tfinal SharedSecretKeyCallback sskc = new SharedSecretKeyCallback();\n\t\thandleCallbacks(nc, ai, vtc, sskc);\n\n\t\tif (vtc.getServerKey() == null) {\n\t\t\tthrow new SaslInvalidLoginExcepion(XmppSaslException.SaslError.not_authorized, nc.getName(), PASSWORD_NOT_VERIFIED_MSG);\n\t\t}\n\n\t\ttry {\n\t\t\tSecretKeySpec secretKeySpec = new SecretKeySpec(vtc.getServerKey(), \"SHA-256\");\n\t\t\tMac mac = Mac.getInstance(\"HmacSHA256\");\n\t\t\tmac.init(secretKeySpec);\n\t\t\tmac.update(data);\n\t\t\tif (sskc.getSecret() != null) {\n\t\t\t\tmac.update(sskc.getSecret());\n\t\t\t}\n\t\t\tbyte[] hmac = mac.doFinal();\n\t\t\tboolean proofMatch = Arrays.equals(hmac, token);\n\n\t\t\tif (!proofMatch) {\n\t\t\t\tthrow new SaslInvalidLoginExcepion(XmppSaslException.SaslError.not_authorized, authcid,\n\t\t\t\t\t\t\t\t\t\t\t\t   PASSWORD_NOT_VERIFIED_MSG);\n\t\t\t}\n\n\t\t\tfinal String authorizationJID = ai.getAuthzId() == null ? nc.getName() : ai.getAuthzId();\n\n\t\t\tfinal AuthorizeCallback ac = new AuthorizeCallback(nc.getName(), authorizationJID);\n\t\t\thandleCallbacks(ac);\n\n\t\t\tif (ac.isAuthorized() == true) {\n\t\t\t\tauthorizedId = ac.getAuthorizedID();\n\t\t\t} else {\n\t\t\t\tthrow new SaslInvalidLoginExcepion(XmppSaslException.SaslError.invalid_authzid, nc.getName(),\n\t\t\t\t\t\t\t\t\t\t\t\t   getMechanismName() + \": \" + authcid + \" is not authorized to act as \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   authorizationJID);\n\t\t\t}\n\n\t\t\tfinal ReplaceServerKeyCallback rtc = new ReplaceServerKeyCallback();\n\t\t\thandleCallbacks(rtc);\n\t\t\tcomplete = true;\n\n\t\t\tbyte[] authzidData = authorizedId.getBytes(StandardCharsets.UTF_8);\n\t\t\tif (rtc.getNewServerKey() != null) {\n\t\t\t\tbyte[] result = new byte[authzidData.length + 1 + rtc.getNewServerKey().length];\n\t\t\t\tSystem.arraycopy(authzidData, 0, result, 0, authzidData.length);\n\t\t\t\tresult[authzidData.length] = 0x00;\n\t\t\t\tSystem.arraycopy(rtc.getNewServerKey(), 0, result, authzidData.length + 1, rtc.getNewServerKey().length);\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\treturn authzidData;\n\t\t} catch (NoSuchAlgorithmException | InvalidKeyException ex) {\n\t\t\tthrow new SaslInvalidLoginExcepion(XmppSaslException.SaslError.invalid_authzid, nc.getName(),\n\t\t\t\t\t\t\t\t\t\t\t   getMechanismName() + \": \" + ex.getMessage());\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getAuthorizationID() {\n\t\treturn authorizedId;\n\t}\n\n\t@Override\n\tpublic byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException {\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/auth/mechanisms/TigaseSaslServerFactory.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.mechanisms;\n\nimport tigase.auth.TigaseSaslProvider;\nimport tigase.kernel.beans.Bean;\n\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.sasl.SaslException;\nimport javax.security.sasl.SaslServer;\nimport javax.security.sasl.SaslServerFactory;\nimport java.util.Map;\n\n@Bean(name = \"tigaseSaslServerFactory\", parent = TigaseSaslProvider.class, active = true)\npublic class TigaseSaslServerFactory\n\t\timplements SaslServerFactory {\n\n\tpublic static final String ANONYMOUS_MECHANISM_ALLOWED = \"anonymous-mechanism-allowed\";\n\n\tpublic TigaseSaslServerFactory() {\n\t}\n\n\t@Override\n\tpublic SaslServer createSaslServer(final String mechanism, final String protocol, final String serverName,\n\t\t\t\t\t\t\t\t\t   final Map<String, ?> props, final CallbackHandler callbackHandler)\n\t\t\tthrows SaslException {\n\t\tswitch (mechanism) {\n\t\t\tcase SaslSCRAM.NAME:\n\t\t\t\treturn new SaslSCRAM(props, callbackHandler);\n\t\t\tcase SaslSCRAMPlus.NAME:\n\t\t\t\treturn new SaslSCRAMPlus(props, callbackHandler);\n\t\t\tcase SaslSCRAMSha256.NAME:\n\t\t\t\treturn new SaslSCRAMSha256(props, callbackHandler);\n\t\t\tcase SaslSCRAMSha256Plus.NAME:\n\t\t\t\treturn new SaslSCRAMSha256Plus(props, callbackHandler);\n\t\t\tcase SaslSCRAMSha512.NAME:\n\t\t\t\treturn new SaslSCRAMSha512(props, callbackHandler);\n\t\t\tcase SaslSCRAMSha512Plus.NAME:\n\t\t\t\treturn new SaslSCRAMSha512Plus(props, callbackHandler);\n\t\t\tcase SaslPLAIN.NAME:\n\t\t\t\treturn new SaslPLAIN(props, callbackHandler);\n\t\t\tcase SaslANONYMOUS.NAME:\n\t\t\t\treturn new SaslANONYMOUS(props, callbackHandler);\n\t\t\tcase SaslEXTERNAL.NAME:\n\t\t\t\treturn new SaslEXTERNAL(props, callbackHandler);\n\t\t\tcase SaslXTOKEN.NAME:\n\t\t\t\treturn new SaslXTOKEN(props, callbackHandler);\n\t\t\tdefault:\n\t\t\t\tthrow new SaslException(\"Mechanism not supported yet.\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic String[] getMechanismNames(Map<String, ?> props) {\n\t\treturn new String[]{SaslSCRAMSha512Plus.NAME, SaslSCRAMSha256Plus.NAME, SaslSCRAMPlus.NAME,\n\t\t\t\t\t\t\tSaslSCRAMSha512.NAME, SaslSCRAMSha256.NAME, SaslSCRAM.NAME, \"PLAIN\", \"EXTERNAL\",\n\t\t\t\t\t\t\t\"ANONYMOUS\", SaslXTOKEN.NAME};\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/cluster/AmpComponentClustered.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster;\n\nimport tigase.cluster.api.*;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.selector.ClusterModeRequired;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Message;\nimport tigase.server.Packet;\nimport tigase.server.Priority;\nimport tigase.server.amp.AmpComponent;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.concurrent.CopyOnWriteArraySet;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.server.amp.AmpFeatureIfc.FROM_CONN_ID;\n\n/**\n * @author andrzej\n */\n@Bean(name = \"amp\", parent = Kernel.class, active = true)\n@ConfigType({ConfigTypeEnum.DefaultMode, ConfigTypeEnum.SessionManagerMode, ConfigTypeEnum.ConnectionManagersMode,\n\t\t\t ConfigTypeEnum.ComponentMode})\n@ClusterModeRequired(active = true)\npublic class AmpComponentClustered\n\t\textends AmpComponent\n\t\timplements ClusteredComponentIfc {\n\n\tprivate static final Logger log = Logger.getLogger(AmpComponentClustered.class.getCanonicalName());\n\n\t@Inject\n\tprivate ClusterControllerIfc clusterController = null;\n\tprivate Set<CommandListener> commandListeners = new CopyOnWriteArraySet<CommandListener>();\n\n\tpublic AmpComponentClustered() {\n\t\tcommandListeners.add(new PacketForwardCommand(\"packet-forward\"));\n\t}\n\n\t@Override\n\tpublic void processPacket(Packet packet) {\n\t\tif (packet.getPacketFrom() == null || getComponentId().getDomain().equals(packet.getPacketFrom().getDomain())) {\n\t\t\tif (packet.getElemName() == Message.ELEM_NAME &&\n\t\t\t\t\tpacket.getElement().getChild(\"broadcast\", \"http://tigase.org/protocol/broadcast\") != null &&\n\t\t\t\t\tpacket.getAttributeStaticStr(FROM_CONN_ID) == null) {\n\t\t\t\tPacket forward = packet.copyElementOnly();\n\t\t\t\tforward.setStableId(packet.getStableId());\n\t\t\t\tforwardPacket(forward);\n\t\t\t}\n\t\t}\n\t\tsuper.processPacket(packet); //To change body of generated methods, choose Tools | Templates.\n\t}\n\n\t@Override\n\tpublic void setClusterController(ClusterControllerIfc cl_controller) {\n\t\tsuper.setClusterController(cl_controller);\n\t\tif (clusterController != null) {\n\t\t\tfor (CommandListener listener : commandListeners) {\n\t\t\t\tclusterController.removeCommandListener(listener);\n\t\t\t}\n\t\t}\n\t\tclusterController = cl_controller;\n\t\tif (clusterController != null) {\n\t\t\tfor (CommandListener listener : commandListeners) {\n\t\t\t\tclusterController.setCommandListener(listener);\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected void forwardPacket(Packet packet) {\n\t\tList<JID> toNodes = new ArrayList<JID>();\n\t\tfor (JID jid : getNodesConnected()) {\n\t\t\t// jid of local node should not be part of getNodesConnected but let's keep this check for now\n\t\t\tif (jid.equals(getComponentId())) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttoNodes.add(jid);\n\t\t}\n\t\tif (!toNodes.isEmpty()) {\n\t\t\tclusterController.sendToNodes(\"packet-forward\", null, packet.getElement(), getComponentId(), null,\n\t\t\t\t\t\t\t\t\t\t  toNodes.toArray(new JID[toNodes.size()]));\n\t\t}\n\t}\n\n\tprotected class PacketForwardCommand\n\t\t\textends CommandListenerAbstract {\n\n\t\tpublic PacketForwardCommand(String name) {\n\t\t\tsuper(name, Priority.HIGH);\n\t\t}\n\n\t\t@Override\n\t\tpublic void executeCommand(JID fromNode, Set<JID> visitedNodes, Map<String, String> data,\n\t\t\t\t\t\t\t\t   Queue<Element> packets) throws ClusterCommandException {\n\t\t\tElement packetEl = null;\n\t\t\twhile ((packetEl = packets.poll()) != null) {\n\t\t\t\ttry {\n\t\t\t\t\tPacket packet = Packet.packetInstance(packetEl);\n\t\t\t\t\tpacket.setPacketFrom(fromNode);\n\t\t\t\t\tpacket.setPacketTo(getComponentId());\n\t\t\t\t\tAmpComponentClustered.this.addPacket(packet);\n\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\tlog.log(Level.WARNING, \"exception converting element to packet after forwarding from other node\",\n\t\t\t\t\t\t\tex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/cluster/BoshConnectionClustered.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster;\n\nimport tigase.cluster.api.ClusteredComponentIfc;\nimport tigase.eventbus.EventListener;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.selector.ClusterModeRequired;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.ServiceChecker;\nimport tigase.server.bosh.BoshConnectionManager;\nimport tigase.server.xmppclient.SeeOtherHostIfc;\nimport tigase.util.common.TimerTask;\nimport tigase.xmpp.XMPPIOService;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.List;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * @author andrzej\n */\n@Bean(name = \"bosh\", parent = Kernel.class, active = true)\n@ConfigType({ConfigTypeEnum.DefaultMode, ConfigTypeEnum.ConnectionManagersMode})\n@ClusterModeRequired(active = true)\npublic class BoshConnectionClustered\n\t\textends BoshConnectionManager\n\t\timplements ClusteredComponentIfc {\n\n\tprivate static final Logger log = Logger.getLogger(BoshConnectionClustered.class.getName());\n\n\tprivate EventListener<ClusterConnectionManager.ClusterInitializedEvent> clusterEventHandler = null;\n\n\tpublic BoshConnectionClustered() {\n\t\tdelayPortListening = true;\n\t}\n\n\t@Override\n\tpublic void onNodeDisconnected(JID jid) {\n\t\tsuper.onNodeDisconnected(jid);\n\n\t\tList<JID> connectedNodes = getNodesConnectedWithLocal();\n\t\tif (see_other_host_strategy != null) {\n\t\t\tsee_other_host_strategy.setNodes(connectedNodes);\n\t\t}\n\n\t\tfinal String hostname = jid.getDomain();\n\n\t\tdoForAllServices(new ServiceChecker<XMPPIOService<Object>>() {\n\t\t\t@Override\n\t\t\tpublic void check(XMPPIOService<Object> service) {\n\t\t\t\tJID dataReceiver = service.getDataReceiver();\n\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Checking service for dataReceiver: {0}\", dataReceiver);\n\t\t\t\t}\n\t\t\t\tif ((dataReceiver != null) && dataReceiver.getDomain().equals(hostname)) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.finest(\"Stopping service because corresponding cluster node stopped.\");\n\t\t\t\t\t}\n\t\t\t\t\tservice.stop();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tpublic String getDiscoDescription() {\n\t\treturn super.getDiscoDescription() + \" clustered\";\n\t}\n\n\t@Override\n\tpublic SeeOtherHostIfc getSeeOtherHostInstance(String see_other_host_class) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Configuring see_other_host clustered strategy for: \" + see_other_host_class);\n\t\t}\n\t\tif (see_other_host_class == null) {\n\t\t\tsee_other_host_class = SeeOtherHostIfc.CM_SEE_OTHER_HOST_CLASS_PROP_DEF_VAL_CLUSTER;\n\t\t}\n\t\tsee_other_host_strategy = super.getSeeOtherHostInstance(see_other_host_class);\n\t\tList<JID> connectedNodes = getNodesConnectedWithLocal();\n\t\tif (see_other_host_strategy != null) {\n\t\t\tsee_other_host_strategy.setNodes(connectedNodes);\n\t\t}\n\n\t\treturn see_other_host_strategy;\n\t}\n\n\t@Override\n\tpublic void start() {\n\t\tsuper.start();\n\n\t\tif (clusterEventHandler == null) {\n\t\t\tclusterEventHandler = (ClusterConnectionManager.ClusterInitializedEvent event) -> {\n\t\t\t\tBoshConnectionClustered.this.connectWaitingTasks();\n\t\t\t\tlog.log(Level.INFO, \"Starting listening on ports of component: {0}\",\n\t\t\t\t\t\tBoshConnectionClustered.this.getName());\n\t\t\t\teventBus.removeListener(clusterEventHandler);\n\t\t\t};\n\t\t}\n\n\t\teventBus.addListener(ClusterConnectionManager.ClusterInitializedEvent.class, clusterEventHandler);\n\n\t\tif (delayPortListening) {\n\t\t\taddTimerTask(new TimerTask() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tlog.log(Level.FINE,\n\t\t\t\t\t\t\t\"Cluster synchronization timed-out, starting pending connections for \" + getName());\n\t\t\t\t\tBoshConnectionClustered.this.connectWaitingTasks();\n\t\t\t\t}\n\t\t\t}, connectionDelay * 30);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void stop() {\n\t\tsuper.stop();\n\t\teventBus.removeListener(clusterEventHandler);\n\t\tclusterEventHandler = null;\n\t}\n\n\t@Override\n\tprotected void onNodeConnected(JID jid) {\n\t\tsuper.onNodeConnected(jid);\n\n\t\tList<JID> connectedNodes = getNodesConnectedWithLocal();\n\t\tif (see_other_host_strategy != null) {\n\t\t\tsee_other_host_strategy.setNodes(connectedNodes);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/cluster/ClientConnectionClustered.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster;\n\nimport tigase.cluster.api.ClusteredComponentIfc;\nimport tigase.eventbus.EventListener;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.selector.ClusterModeRequired;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.ServiceChecker;\nimport tigase.server.xmppclient.ClientConnectionManager;\nimport tigase.server.xmppclient.SeeOtherHostIfc;\nimport tigase.util.common.TimerTask;\nimport tigase.xmpp.XMPPIOService;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.List;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Describe class ClientConnectionClustered here.\n * <br>\n * Created: Sat Jun 21 22:23:18 2008\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"c2s\", parent = Kernel.class, active = true)\n@ConfigType({ConfigTypeEnum.DefaultMode, ConfigTypeEnum.ConnectionManagersMode})\n@ClusterModeRequired(active = true)\npublic class ClientConnectionClustered\n\t\textends ClientConnectionManager\n\t\timplements ClusteredComponentIfc {\n\n\tprivate static final Logger log = Logger.getLogger(ClientConnectionClustered.class.getName());\n\n\tprivate EventListener<ClusterConnectionManager.ClusterInitializedEvent> clusterEventHandler = null;\n\t//\tprivate SeeOtherHostIfc see_other_host_strategy = null;\n\t@SuppressWarnings(\"serial\")\n\tprivate List<BareJID> connectedNodes = new CopyOnWriteArrayList<BareJID>() {\n\t\t{\n\t\t\tadd(getDefHostName());\n\t\t}\n\t};\n\n\tpublic ClientConnectionClustered() {\n\t\tdelayPortListening = true;\n\t}\n\n\t@Override\n\tpublic void onNodeDisconnected(JID jid) {\n\t\tsuper.onNodeDisconnected(jid);\n\n\t\tList<JID> connectedNodes = getNodesConnectedWithLocal();\n\t\tif (see_other_host_strategy != null) {\n\t\t\tsee_other_host_strategy.setNodes(connectedNodes);\n\t\t}\n\n\t\tfinal String hostname = jid.getDomain();\n\n\t\tdoForAllServices(new ServiceChecker<XMPPIOService<Object>>() {\n\t\t\t@Override\n\t\t\tpublic void check(XMPPIOService<Object> service) {\n\t\t\t\tJID dataReceiver = service.getDataReceiver();\n\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Checking service for dataReceiver: {0}\", dataReceiver);\n\t\t\t\t}\n\t\t\t\tif ((dataReceiver != null) && dataReceiver.getDomain().equals(hostname)) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.finest(\"Stopping service because corresponding cluster node stopped.\");\n\t\t\t\t\t}\n\t\t\t\t\tservice.stop();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tpublic String getDiscoDescription() {\n\t\treturn super.getDiscoDescription() + \" clustered\";\n\t}\n\n\t@Override\n\tpublic SeeOtherHostIfc getSeeOtherHostInstance(String see_other_host_class) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Configuring see_other_host clustered strategy for: \" + see_other_host_class);\n\t\t}\n\t\tif (see_other_host_class == null) {\n\t\t\tsee_other_host_class = SeeOtherHostIfc.CM_SEE_OTHER_HOST_CLASS_PROP_DEF_VAL_CLUSTER;\n\t\t}\n\t\tsee_other_host_strategy = super.getSeeOtherHostInstance(see_other_host_class);\n\t\tif (see_other_host_strategy != null) {\n\t\t\tsee_other_host_strategy.setNodes(getNodesConnectedWithLocal());\n\t\t}\n\n\t\treturn see_other_host_strategy;\n\t}\n\n\t@Override\n\tpublic void start() {\n\t\tsuper.start();\n\n\t\tif (clusterEventHandler == null) {\n\t\t\tclusterEventHandler = (ClusterConnectionManager.ClusterInitializedEvent event) -> {\n\t\t\t\tClientConnectionClustered.this.connectWaitingTasks();\n\t\t\t\tlog.log(Level.INFO, \"Starting listening on ports of component: {0}\",\n\t\t\t\t\t\tClientConnectionClustered.this.getName());\n\t\t\t\teventBus.removeListener(clusterEventHandler);\n\t\t\t};\n\t\t}\n\n\t\teventBus.addListener(ClusterConnectionManager.ClusterInitializedEvent.class, clusterEventHandler);\n\n\t\tif (delayPortListening) {\n\t\t\taddTimerTask(new TimerTask() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tlog.log(Level.FINE,\n\t\t\t\t\t\t\t\"Cluster synchronization timed-out, starting pending connections for \" + getName());\n\t\t\t\t\tClientConnectionClustered.this.connectWaitingTasks();\n\t\t\t\t}\n\t\t\t}, connectionDelay * 30);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void stop() {\n\t\tsuper.stop();\n\t\teventBus.removeListener(clusterEventHandler);\n\t\tclusterEventHandler = null;\n\t}\n\n\t@Override\n\tprotected void onNodeConnected(JID jid) {\n\t\tsuper.onNodeConnected(jid);\n\n\t\tList<JID> connectedNodes = getNodesConnectedWithLocal();\n\t\tif (see_other_host_strategy != null) {\n\t\t\tsee_other_host_strategy.setNodes(connectedNodes);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/cluster/ClusterConnection.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster;\n\nimport tigase.xmpp.XMPPIOService;\n\nimport java.util.List;\nimport java.util.concurrent.CopyOnWriteArrayList;\n\n/**\n * @author andrzej\n */\npublic class ClusterConnection {\n\n\tprivate final String addr;\n\tprivate final CopyOnWriteArrayList<XMPPIOService<Object>> conns = new CopyOnWriteArrayList<>();\n\n\tpublic ClusterConnection(String addr) {\n\t\tthis.addr = addr;\n\t}\n\n\tpublic boolean addConn(XMPPIOService<Object> conn) {\n\t\treturn conns.add(conn);\n\t}\n\n\tpublic boolean removeConn(XMPPIOService<Object> conn) {\n\t\treturn conns.remove(conn);\n\t}\n\n\tpublic int size() {\n\t\treturn conns.size();\n\t}\n\n\tpublic List<XMPPIOService<Object>> getConnections() {\n\t\treturn conns;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn addr + conns;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/cluster/ClusterConnectionManager.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster;\n\nimport tigase.cluster.api.*;\nimport tigase.cluster.repo.ClusterRepoItem;\nimport tigase.cluster.repo.ClusterRepoItemEvent;\nimport tigase.db.DBInitException;\nimport tigase.db.DataSource;\nimport tigase.db.DataSourceHelper;\nimport tigase.db.TigaseDBException;\nimport tigase.db.beans.DataSourceBean;\nimport tigase.db.comp.AbstractSDComponentRepositoryBean;\nimport tigase.db.comp.ComponentRepository;\nimport tigase.db.comp.ComponentRepositoryDataSourceAware;\nimport tigase.db.comp.RepositoryChangeListenerIfc;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.EventBusEvent;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.beans.selector.ClusterModeRequired;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\nimport tigase.net.ConnectionType;\nimport tigase.net.SocketType;\nimport tigase.server.ConnectionManager;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.ServiceChecker;\nimport tigase.stats.MaxDailyCounterQueue;\nimport tigase.stats.StatisticsList;\nimport tigase.sys.TigaseRuntime;\nimport tigase.util.Algorithms;\nimport tigase.util.common.TimerTask;\nimport tigase.util.datetime.TimeUtils;\nimport tigase.util.reflection.ReflectionHelper;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.PacketErrorTypeException;\nimport tigase.xmpp.XMPPIOService;\nimport tigase.xmpp.jid.JID;\n\nimport javax.script.Bindings;\nimport java.lang.reflect.Type;\nimport java.net.InetAddress;\nimport java.net.NetworkInterface;\nimport java.net.SocketException;\nimport java.net.UnknownHostException;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentSkipListMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.zip.Deflater;\n\n/**\n * Class ClusterConnectionManager\n * <br>\n * Created: Tue Nov 22 07:07:11 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"cl-comp\", parent = Kernel.class, active = true)\n@ConfigType({ConfigTypeEnum.DefaultMode, ConfigTypeEnum.SessionManagerMode, ConfigTypeEnum.ConnectionManagersMode,\n\t\t\t ConfigTypeEnum.ComponentMode})\n@ClusterModeRequired(active = true)\npublic class ClusterConnectionManager\n\t\textends ConnectionManager<XMPPIOService<Object>>\n\t\timplements ClusteredComponentIfc, RepositoryChangeListenerIfc<ClusterRepoItem>, ClusterConnectionHandler {\n\n\tpublic static final int SOCKET_BUFFER_CL_PROP_VAL = 128 * 1024;\n\n\tpublic static final String CLUSTER_CONNECTIONS_PER_NODE_PROP_KEY = \"cluster-connections-per-node\";\n\n\tpublic static final int CLUSTER_CONNECTIONS_PER_NODE_VAL = 5;\n\n\tpublic static final String CLUSTER_CONTR_ID_PROP_KEY = \"cluster-controller-id\";\n\n\tpublic static final String COMPRESS_STREAM_PROP_KEY = \"compress-stream\";\n\n\tpublic static final String CONNECT_ALL_PAR = \"--cluster-connect-all\";\n\n\tpublic static final String CONNECT_ALL_PROP_KEY = \"connect-all\";\n\n\tpublic static final String NON_CLUSTER_TRAFFIC_ALLOWED_PROP_KEY = \"non-cluster-traffic-allowed\";\n\tpublic static final boolean NON_CLUSTER_TRAFFIC_ALLOWED_PROP_VAL = true;\n\tpublic static final String IDENTITY_TYPE_KEY = \"identity-type\";\n\tpublic static final String IDENTITY_TYPE_VAL = \"generic\";\n\tpublic static final String PORT_ROUTING_TABLE_PROP_KEY = \"routing-table\";\n\tpublic static final String RETURN_SERVICE_DISCO_KEY = \"service-disco\";\n\tpublic static final String SECRET_PROP_KEY = \"secret\";\n\tpublic static final String XMLNS = \"tigase:cluster\";\n\tpublic static final boolean RETURN_SERVICE_DISCO_VAL = true;\n\tpublic static final boolean CONNECT_ALL_PROP_VAL = false;\n\tpublic static final boolean COMPRESS_STREAM_PROP_VAL = false;\n\tpublic final static String EVENTBUS_REPOSITORY_NOTIFICATIONS_ENABLED_KEY = \"eventbus-repository-notifications\";\n\tpublic final static boolean EVENTBUS_REPOSITORY_NOTIFICATIONS_ENABLED_VALUE = false;\n\tprivate static final Logger log = Logger.getLogger(ClusterConnectionManager.class.getName());\n\n\tpublic static enum REPO_ITEM_UPDATE_TYPE {\n\t\tADDED,\n\t\tUPDATED,\n\t\tREMOVED\n\t}\n\t/**\n\t * Default value for the system property for configuration protection from system overload and DOS attack.\n\t */\n\tpublic static int ELEMENTS_NUMBER_LIMIT_CLUSTER_PROP_VAL = 100 * 1000;\n\n\t@Inject\n\tprivate ClusterControllerIfc clusterController = null;\n\tprivate tigase.eventbus.EventListener<ClusterInitializedEvent> clusterEventHandler = null;\n\t@ConfigField(desc = \"Compress stream\", alias = COMPRESS_STREAM_PROP_KEY)\n\tprivate boolean compress_stream = COMPRESS_STREAM_PROP_VAL;\n\t@ConfigField(desc = \"Connect to all nodes\", alias = CONNECT_ALL_PROP_KEY)\n\tprivate boolean connect_all = CONNECT_ALL_PROP_VAL;\n\t// private long packetsSent = 0;\n\t// private long packetsReceived = 0;\n\t@Inject\n\tprivate ClusterConnectionSelectorIfc connectionSelector = null;\n\tprivate Map<String, ClusterConnection> connectionsPool = new ConcurrentSkipListMap<>();\n\t@Inject\n\tprivate DataSourceBean dataSourceBean = null;\n\t@Inject\n\tprivate EventBus eventBus = null;\n\tprivate String identity_type = IDENTITY_TYPE_VAL;\n\tprivate boolean initialClusterConnectedDone = false;\n\t// private String cluster_controller_id = null;\n\tprivate IOServiceStatisticsGetter ioStatsGetter = new IOServiceStatisticsGetter();\n\tprivate long[] lastDay = new long[24];\n\tprivate int lastDayIdx = 0;\n\tprivate long[] lastHour = new long[60];\n\tprivate int lastHourIdx = 0;\n\tprivate MaxDailyCounterQueue<Integer> maxNodes = new MaxDailyCounterQueue<>(31);\n\tprivate int maxNodesWithinLastWeek = 0;\n\tprivate int nodesNo = 0;\n\t@ConfigField(desc = \"Allow non cluster traffic over cluster connection\", alias = NON_CLUSTER_TRAFFIC_ALLOWED_PROP_KEY)\n\tprivate boolean nonClusterTrafficAllowed = true;\n\t@ConfigField(desc = \"Number of connections to open per node\", alias = \"connections-per-node\")\n\tprivate int per_node_conns = CLUSTER_CONNECTIONS_PER_NODE_VAL;\n\t@Inject\n\tprivate ComponentRepository<ClusterRepoItem> repo = null;\n\tprivate final TimerTask repoReloadTimerTask = new TimerTask() {\n\t\t@Override\n\t\tpublic void run() {\n\t\t\ttry {\n\t\t\t\tif (repo != null) {\n\t\t\t\t\trepo.reload();\n\t\t\t\t}\n\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\tlog.log(Level.WARNING, \"Items reloading failed\", ex);\n\t\t\t}\n\t\t}\n\t};\n\tprivate CommandListener sendPacket = new SendPacket(ClusterControllerIfc.DELIVER_CLUSTER_PACKET_CMD);\n\tprivate long servConnectedTimeouts = 0;\n\tprivate long totalNodeDisconnects = 0;\n\n\tpublic ClusterConnectionManager() {\n\t\tsuper(SOCKET_BUFFER_CL_PROP_VAL);\n\t\tserviceConnectedTimeout = 10;\n\t\telements_number_limit = ELEMENTS_NUMBER_LIMIT_CLUSTER_PROP_VAL;\n\t\tif (getDefHostName().toString().equalsIgnoreCase(\"localhost\")) {\n\t\t\tTigaseRuntime.getTigaseRuntime()\n\t\t\t\t\t.shutdownTigase(new String[]{\"ERROR! Tigase is running in Clustered Mode yet the hostname\",\n\t\t\t\t\t\t\t\t\t\t\t\t \"of the machine was resolved to *localhost* which will cause\",\n\t\t\t\t\t\t\t\t\t\t\t\t \"malfunctioning of Tigase in clustered environment!\", \"\",\n\t\t\t\t\t\t\t\t\t\t\t\t \"To prevent further issues with the clustering Tigase will be shutdown.\",\n\t\t\t\t\t\t\t\t\t\t\t\t \"\",\n\t\t\t\t\t\t\t\t\t\t\t\t \"Please make sure that FQDN hostname of the machine is set correctly\",\n\t\t\t\t\t\t\t\t\t\t\t\t \"and restart the server.\"});\n\t\t}\n\n\t\tconnectionDelay = 5 * SECOND;\n\n\t\twatchdogPingType = WATCHDOG_PING_TYPE.XMPP;\n\t\twatchdogDelay = 30 * SECOND;\n\t\twatchdogTimeout = -1 * SECOND;\n\t}\n\n\t@Override\n\tprotected boolean enableServiceConnectedTimeout(XMPPIOService<Object> service) {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int hashCodeForPacket(Packet packet) {\n\n\t\t// If this is a cluster packet let's try to do a bit more smart hashing\n\t\t// based on the stanza from/to addresses\n\t\tif (packet.getElemName() == ClusterElement.CLUSTER_EL_NAME) {\n\n\t\t\t// TODO: Look for a simpler, more efficient algorithm to distribute\n\t\t\t// cluster packets among different threads.\n\t\t\t// This looks like an overkill to me, however I don't see any better way\n\t\t\tClusterElement clel = new ClusterElement(packet.getElement());\n\n\t\t\t// If there is no XMPP stanzas with an address inside the cluster packet,\n\t\t\t// we can try Map data and User ID inside it if it exists.\n\t\t\tString userId = clel.getMethodParam(\"userId\");\n\n\t\t\tif (userId != null) {\n\t\t\t\treturn userId.hashCode();\n\t\t\t}\n\n\t\t\tQueue<Element> children = clel.getDataPackets();\n\n\t\t\tif ((children != null) && (children.size() > 0)) {\n\t\t\t\tElement child = children.peek();\n\t\t\t\tString stanzaAdd = child.getAttributeStaticStr(Packet.TO_ATT);\n\n\t\t\t\tif (stanzaAdd != null) {\n\t\t\t\t\treturn stanzaAdd.hashCode();\n\t\t\t\t} else {\n\n\t\t\t\t\t// This might be user's initial presence. In such a case we take\n\t\t\t\t\t// stanzaFrom instead\n\t\t\t\t\tstanzaAdd = child.getAttributeStaticStr(Packet.FROM_ATT);\n\t\t\t\t\tif (stanzaAdd != null) {\n\t\t\t\t\t\treturn stanzaAdd.hashCode();\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// This may happen for some cluster packets, like:\n\t\t\t\t\t\t// resp-sync-online-sm-cmd and this is correct\n\t\t\t\t\t\tlog.log(Level.FINE, \"No stanzaTo or from for cluster packet: {0}\", packet);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// There is a separate connection to each cluster node, ideally we want to\n\t\t// process packets in a separate thread for each connection, so let's try\n\t\t// to get the hash code by the destination node address\n\t\tif (packet.getStanzaTo() != null) {\n\t\t\treturn packet.getStanzaTo().hashCode();\n\t\t}\n\n\t\treturn packet.getTo() != null ? packet.getTo().hashCode() : packet.toString().hashCode();\n\t}\n\n\t@Override\n\tpublic void initBindings(Bindings binds) {\n\t\tsuper.initBindings(binds);\n\t\tbinds.put(\"clusterCM\", this);\n\t\tbinds.put(ComponentRepository.COMP_REPO_BIND, repo);\n\t}\n\n\t@Override\n\tpublic void itemAdded(ClusterRepoItem repoItem) {\n\t\tlog.log(Level.CONFIG, \"Loaded repoItem: {0}\", repoItem.toString());\n\n\t\tString host = repoItem.getHostname();\n\n\t\tboolean isCorrect = false;\n\t\ttry {\n\t\t\tInetAddress addr = InetAddress.getByName(host);\n\n\t\t\t// we ignore any local addresses\n\t\t\tisCorrect = !addr.isAnyLocalAddress() && !addr.isLoopbackAddress() &&\n\t\t\t\t\t!(NetworkInterface.getByInetAddress(addr) != null);\n\t\t\tif (!isCorrect && log.isLoggable(Level.CONFIG)) {\n\t\t\t\tlog.log(Level.CONFIG, \"ClusterRepoItem of local machine, skipping connection attempt: {0}\", repoItem);\n\t\t\t}\n\t\t} catch (UnknownHostException | SocketException ex) {\n\t\t\tlog.log(Level.WARNING, \"Incorrect ClusterRepoItem, skipping connection attempt: \" + repoItem, ex);\n\t\t}\n\n\t\tif (isCorrect) {\n\t\t\tfor (int i = 0; i < per_node_conns; ++i) {\n\t\t\t\tlog.log(Level.CONFIG, \"Trying to connect to cluster node: {0}\", host);\n\n\t\t\t\tMap<String, Object> port_props = new LinkedHashMap<String, Object>(12);\n\n\t\t\t\tport_props.put(SECRET_PROP_KEY, repoItem.getPassword());\n\t\t\t\tport_props.put(PORT_LOCAL_HOST_PROP_KEY, getDefHostName());\n\t\t\t\tport_props.put(PORT_TYPE_PROP_KEY, ConnectionType.connect);\n\t\t\t\tport_props.put(PORT_SOCKET_PROP_KEY, SocketType.plain);\n\t\t\t\tport_props.put(PORT_REMOTE_HOST_PROP_KEY, host);\n\t\t\t\tport_props.put(PORT_IFC_PROP_KEY, new String[]{host});\n\t\t\t\tport_props.put(MAX_RECONNECTS_PROP_KEY, 99999999);\n\t\t\t\tport_props.put(PORT_KEY, repoItem.getPortNo());\n\t\t\t\taddWaitingTask(port_props);\n\t\t\t}\n\n\t\t\tsendEvent(REPO_ITEM_UPDATE_TYPE.ADDED, repoItem);\n\t\t\t// reconnectService(port_props, connectionDelay);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void itemRemoved(ClusterRepoItem item) {\n\t\t// remove and close all cluster connections to item which is now gone\n\t\tClusterConnection clusterConnection = connectionsPool.get(item.getHostname());\n\t\tif (clusterConnection != null && clusterConnection.size() > 0) {\n\t\t\tfor (XMPPIOService service : clusterConnection.getConnections()) {\n\t\t\t\tclusterConnection.removeConn(service);\n\t\t\t\t// in most cases those connections should already be closed\n\t\t\t\tservice.stop();\n\t\t\t}\n\t\t}\n\t\tsendEvent(REPO_ITEM_UPDATE_TYPE.REMOVED, item);\n\t}\n\n\t@Override\n\tpublic void itemUpdated(ClusterRepoItem item) {\n\t\tsendEvent(REPO_ITEM_UPDATE_TYPE.UPDATED, item);\n\t}\n\n\t@Override\n\tpublic void nodeConnected(String node) {\n\t\tsuper.nodeConnected(node);\n\n\t\tmaxNodes.add(getNodesConnectedWithLocal().size());\n\t\tmaxNodesWithinLastWeek = maxNodes.getMaxValueInRange(7).orElse(-1);\n\t}\n\n\t@Override\n\tpublic synchronized void everyHour() {\n\t\tsuper.everyHour();\n\n\t\tmaxNodes.add(getNodesConnectedWithLocal().size());\n\t\tmaxNodesWithinLastWeek = maxNodes.getMaxValueInRange(7).orElse(-1);\n\t}\n\n\t@Override\n\tpublic void nodeDisconnected(String node) {\n\t\tsuper.nodeDisconnected(node);\n\n\t\tmaxNodes.add(getNodesConnectedWithLocal().size());\n\t\tmaxNodesWithinLastWeek = maxNodes.getMaxValueInRange(7).orElse(-1);\n\t}\n\n\t@Override\n\tpublic int processingInThreads() {\n\n\t\t// TODO: The number of threads should be equal or greater to number of\n\t\t// cluster nodes.\n\t\t// This should work well as far as nodesNo is initialized before this\n\t\t// method is called which is true only during program startup time.\n\t\t// In case of reconfiguration or new node joining this might not be\n\t\t// the case. Low priority issue though.\n\t\treturn Math.max(Runtime.getRuntime().availableProcessors(), nodesNo) * 8;\n\t}\n\n\t@Override\n\tpublic int processingOutThreads() {\n\n\t\t// TODO: The number of threads should be equal or greater to number of\n\t\t// cluster nodes.\n\t\t// This should work well as far as nodesNo is initialized before this\n\t\t// method is called which is true only during program startup time.\n\t\t// In case of reconfiguration or new node joining this might not be\n\t\t// the case. Low priority issue though.\n\t\treturn Math.max(Runtime.getRuntime().availableProcessors(), nodesNo) * 8;\n\t}\n\n\t@Override\n\tpublic void processOutPacket(Packet packet) {\n\t\tif (packet.getElemName() == ClusterElement.CLUSTER_EL_NAME) {\n\t\t\tclusterController.handleClusterPacket(packet.getElement());\n\t\t} else {\n\n\t\t\t// This should, actually, not happen. Let's log it here\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.log(Level.FINER, \"Unexpected packet on cluster connection: {0}\", packet);\n\t\t\t}\n\t\t\tsuper.processOutPacket(packet);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processPacket(Packet packet) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Processing packet: {0}\", packet);\n\t\t}\n\t\tif ((packet.getStanzaTo() != null) && packet.getStanzaTo().equals(getComponentId())) {\n\t\t\ttry {\n\t\t\t\taddOutPacket(Authorization.FEATURE_NOT_IMPLEMENTED.getResponseMessage(packet, \"Not implemented\", true));\n\t\t\t} catch (PacketErrorTypeException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Packet processing exception: {0}\", e);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\t\tif (packet.getElemName() == ClusterElement.CLUSTER_EL_NAME || packet.getElemName() == \"route\") {\n\t\t\twritePacketToSocket(packet);\n\t\t} else {\n\n\t\t\tif (nonClusterTrafficAllowed) {\n\t\t\t\twritePacketToSocket(packet.packRouted());\n\t\t\t} else {\n\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\tlog.log(Level.FINER, \"Unexpected packet for the cluster connetcion: {0}\", packet);\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t}\n\n\t@Override\n\tpublic Queue<Packet> processSocketData(XMPPIOService<Object> serv) {\n\t\tPacket p = null;\n\n\t\twhile ((p = serv.getReceivedPackets().poll()) != null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Processing socket data: {0}\", p);\n\t\t\t}\n\t\t\tif (p.getElemName().equals(\"handshake\")) {\n\t\t\t\tprocessHandshake(p, serv);\n\t\t\t} else {\n\t\t\t\tif (p.getAttributeStaticStr(new String[]{Iq.ELEM_NAME, \"ping\"}, \"xmlns\") == \"urn:xmpp:ping\" && p.getStanzaTo() != null &&\n\t\t\t\t\t\tgetDefHostName().getDomain().equals(p.getStanzaTo().getDomain()) && p.getStanzaFrom() != null &&\n\t\t\t\t\t\tp.getStanzaFrom().getDomain().equals(serv.getSessionData().get(PORT_REMOTE_HOST_PROP_KEY))) {\n\t\t\t\t\t// received PING between cluster nodes to confirm connectivity\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Received XMPP ping [{0}]\", serv);\n\t\t\t\t\t}\n\t\t\t\t\tserv.getSessionData().put(\"lastConnectivityCheck\", System.currentTimeMillis());\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// ++packetsReceived;\n\t\t\t\tPacket result = p;\n\n\t\t\t\tif (p.isRouted()) {\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tresult = p.unpackRouted();\n\t\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Packet stringprep addressing problem, dropping packet: {0}\", p);\n\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t}    // end of if (p.isRouted())\n\t\t\t\taddOutPacket(result);\n\t\t\t}\n\t\t}        // end of while ()\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean processUndeliveredPacket(Packet packet, Long stamp, String errorMessage) {\n\t\t// readd packet - this may be good as we would retry to send packet\n\t\t// which delivery failed due to IO error\n\t\ttry {\n\t\t\t// We are only redeliverying <cluster/> or <route/> packets as others, if sent over cluster connection, would be wrapped in <route/>.\n\t\t\t// If they are not wrapped, that means that those are handshake packets and should not be resent.\n\t\t\tif (packet.getElemName() == ClusterElement.CLUSTER_EL_NAME || packet.getElemName() == \"route\") {\n\t\t\t\taddPacket(packet);\n\t\t\t} else {\n\t\t\t\tlog.log(Level.FINEST, () -> \"skipping redelivery of packet \" + packet);\n\t\t\t}\n\t\t} catch (NullPointerException ex) {\n\t\t\tlog.log(Level.WARNING, \"could not redeliver cluster packet on broken cluster connection:\", packet.toString());\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void reconnectionFailed(Map<String, Object> port_props) {\n\n\t\t// TODO: handle this somehow\n\t}\n\n\tpublic int schedulerThreads() {\n\t\treturn 4;\n\t}\n\n\t@Override\n\tpublic void serviceStarted(XMPPIOService<Object> serv) {\n\t\tif (!repoReloadTimerTask.isScheduled()) {\n\t\t\taddTimerTaskWithTimeout(repoReloadTimerTask, 0, 15 * SECOND);\n\t\t}\n\n\t\tsuper.serviceStarted(serv);\n\t\tlog.log(Level.CONFIG, \"Cluster connection opened: {0}, type: {1}, id={2}\",\n\t\t\t\tnew Object[]{serv.getRemoteAddress(), serv.connectionType().toString(), serv.getUniqueId()});\n\t\tif (compress_stream) {\n\t\t\tlog.log(Level.CONFIG, \"Starting stream compression for: {0}\", serv.getUniqueId());\n\t\t\tserv.startZLib(Deflater.BEST_COMPRESSION);\n\t\t}\n\t\tswitch (serv.connectionType()) {\n\t\t\tcase connect:\n\n\t\t\t\t// Send init xmpp stream here\n\t\t\t\tString remote_host = (String) serv.getSessionData().get(PORT_REMOTE_HOST_PROP_KEY);\n\n\t\t\t\tserv.getSessionData().put(XMPPIOService.HOSTNAME_KEY, getDefHostName().toString());\n\t\t\t\tserv.getSessionData()\n\t\t\t\t\t\t.put(PORT_ROUTING_TABLE_PROP_KEY,\n\t\t\t\t\t\t\t new String[]{remote_host, \".*@\" + remote_host, \".*\\\\.\" + remote_host});\n\n\t\t\t\tString data = \"<stream:stream\" + \" xmlns='\" + XMLNS + \"'\" +\n\t\t\t\t\t\t\" xmlns:stream='http://etherx.jabber.org/streams'\" + \" from='\" + getDefHostName() + \"'\" +\n\t\t\t\t\t\t\" to='\" + remote_host + \"'\" + \">\";\n\n\t\t\t\tlog.log(Level.CONFIG, \"cid: {0}, sending: {1}\",\n\t\t\t\t\t\tnew Object[]{(String) serv.getSessionData().get(\"cid\"), data});\n\t\t\t\tserv.xmppStreamOpen(data);\n\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\n\t\t\t\t// Do nothing, more data should come soon...\n\t\t\t\tbreak;\n\t\t}    // end of switch (service.connectionType())\n\t}\n\n\t@Override\n\tpublic boolean serviceStopped(XMPPIOService<Object> service) {\n\t\tboolean result = super.serviceStopped(service);\n\n\t\t// Make sure it runs just once for each disconnect\n\t\tif (result) {\n\t\t\tMap<String, Object> sessionData = service.getSessionData();\n\t\t\tString[] routings = (String[]) sessionData.get(PORT_ROUTING_TABLE_PROP_KEY);\n\t\t\tString addr = (String) sessionData.get(PORT_REMOTE_HOST_PROP_KEY);\n\t\t\tClusterConnection conns = connectionsPool.get(addr);\n\n\t\t\tif (conns == null) {\n\t\t\t\tconns = new ClusterConnection(addr);\n\t\t\t\tconnectionsPool.put(addr, conns);\n\t\t\t}\n\n\t\t\tsynchronized (conns) {\n\t\t\t\tint size = conns.size();\n\n\t\t\t\tconns.removeConn(service);\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\"serviceStopped: result={0} / size={1} / connPool={2} / serv={3} / conns={4} / connsSize: {5}, / type={6}\",\n\t\t\t\t\t\t\tnew Object[]{result, size, connectionsPool, service, conns, conns.size(), service.connectionType()});\n\t\t\t\t}\n\n\t\t\t\tif (size != 0 && conns.size() == 0) {\n\t\t\t\t\tif (routings != null) {\n\t\t\t\t\t\tupdateRoutings(routings, false);\n\t\t\t\t\t}\n\n\t\t\t\t\t// removeRouting(serv.getRemoteHost());\n\t\t\t\t\tlog.log(Level.INFO, \"Disconnected from: {0}\", addr);\n\t\t\t\t\tupdateServiceDiscoveryItem(addr, addr, XMLNS + \" disconnected\", true);\n\t\t\t\t\tclusterController.nodeDisconnected(addr);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tConnectionType type = service.connectionType();\n\n\t\t\tif (type == ConnectionType.connect) {\n\t\t\t\t// make sure that item exists, as in other case there is no point to reconnect\n\t\t\t\tClusterRepoItem item = repo.getItem(addr);\n\t\t\t\tif (item != null) {\n\t\t\t\t\taddWaitingTask(sessionData);\n\t\t\t\t}\n\t\t\t}    // end of if (type == ConnectionType.connect)\n\t\t\t++totalNodeDisconnects;\n\n\t\t\tint hour = TimeUtils.getHourNow();\n\n\t\t\tif (lastDayIdx != hour) {\n\t\t\t\tlastDayIdx = hour;\n\t\t\t\tlastDay[hour] = 0;\n\t\t\t\tArrays.fill(lastHour, 0);\n\t\t\t}\n\t\t\t++lastDay[hour];\n\n\t\t\tint minute = TimeUtils.getMinuteNow();\n\n\t\t\t++lastHour[minute];\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic void setRepo(ComponentRepository<ClusterRepoItem> repo) {\n\t\tif (this.repo != null) {\n\t\t\tthis.repo.removeRepoChangeListener(this);\n\t\t}\n\t\tthis.repo = repo;\n\t\tif (this.repo != null) {\n\t\t\tthis.repo.addRepoChangeListener(this);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void tlsHandshakeCompleted(XMPPIOService<Object> service) {\n\t}\n\n\t@Override\n\tpublic void updateConnectionDetails(Map<String, Object> port_props) {\n\t\tString host = (String) port_props.get(PORT_REMOTE_HOST_PROP_KEY);\n\t\tClusterRepoItem item = repo.getItem(host);\n\n\t\tif (item != null) {\n\t\t\tport_props.put(SECRET_PROP_KEY, item.getPassword());\n\t\t\tport_props.put(PORT_KEY, item.getPortNo());\n\t\t} else {\n\t\t\tport_props.put(MAX_RECONNECTS_PROP_KEY, 0);\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"ClusterRepoItem: {0}, port_props: {1}\", new Object[]{item, port_props});\n\t\t}\n\t}\n\n\t@Override\n\tpublic void xmppStreamClosed(XMPPIOService<Object> serv) {\n\t\tlog.log(Level.CONFIG, \"Stream closed.\");\n\t}\n\n\t@Override\n\tpublic String[] xmppStreamOpened(XMPPIOService<Object> service, Map<String, String> attribs) {\n\t\tlog.log(Level.CONFIG, \"Stream opened: {0}, service: {1}\", new Object[]{attribs, service});\n\t\tswitch (service.connectionType()) {\n\t\t\tcase connect: {\n\t\t\t\tString id = attribs.get(\"id\");\n\n\t\t\t\tservice.getSessionData().put(XMPPIOService.SESSION_ID_KEY, id);\n\n\t\t\t\tClusterRepoItem item = repo.getItem(getDefHostName().getDomain());\n\t\t\t\tString secret = item.getPassword();\n\n\t\t\t\ttry {\n\t\t\t\t\tString digest = Algorithms.hexDigest(id, secret, \"SHA\");\n\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Calculating digest: id={0}, secret={1}, digest={2}, item={3}\",\n\t\t\t\t\t\t\t\tnew Object[]{id, secret, digest, item});\n\t\t\t\t\t}\n\n\t\t\t\t\treturn new String[] { \"<handshake>\" + digest + \"</handshake>\" };\n\t\t\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\t\t\tlog.log(Level.SEVERE, \"Can not generate digest for pass phrase.\", e);\n\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcase accept: {\n\t\t\t\tString remote_host = attribs.get(\"from\");\n\n\t\t\t\tservice.getSessionData().put(XMPPIOService.HOSTNAME_KEY, getDefHostName().toString());\n\t\t\t\tservice.getSessionData().put(PORT_REMOTE_HOST_PROP_KEY, remote_host);\n\t\t\t\tservice.getSessionData()\n\t\t\t\t\t\t.put(PORT_ROUTING_TABLE_PROP_KEY,\n\t\t\t\t\t\t\t new String[]{remote_host, \".*@\" + remote_host, \".*\\\\.\" + remote_host});\n\n\t\t\t\tString id = UUID.randomUUID().toString();\n\n\t\t\t\tservice.getSessionData().put(XMPPIOService.SESSION_ID_KEY, id);\n\t\t\t\tupdateConnectionDetails(service.getSessionData());\n\n\t\t\t\treturn new String[] { \"<stream:stream\" + \" xmlns='\" + XMLNS + \"'\" +\n\t\t\t\t\t\t\" xmlns:stream='http://etherx.jabber.org/streams'\" + \" from='\" + getDefHostName() + \"'\" +\n\t\t\t\t\t\t\" to='\" + remote_host + \"'\" + \" id='\" + id + \"'\" + \">\" };\n\t\t\t}\n\n\t\t\tdefault:\n\n\t\t\t\t// Do nothing, more data should come soon...\n\t\t\t\tbreak;\n\t\t}    // end of switch (service.connectionType())\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getDiscoCategoryType() {\n\t\treturn identity_type;\n\t}\n\n\t@Override\n\tpublic String getDiscoDescription() {\n\t\treturn \"Cluster connection manager\";\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t\tsuper.getStatistics(list);\n\t\tlist.add(getName(), \"Total disconnects\", totalNodeDisconnects, Level.FINE);\n\t\tlist.add(getName(), \"Service connected time-outs\", servConnectedTimeouts, Level.FINE);\n\t\tlist.add(getName(), \"Last day disconnects\", Arrays.toString(lastDay), Level.FINE);\n\t\tlist.add(getName(), \"Last hour disconnects\", Arrays.toString(lastHour), Level.FINE);\n\t\tioStatsGetter.reset();\n\t\tdoForAllServices(ioStatsGetter);\n\t\tlist.add(getName(), \"Average compression ratio\", ioStatsGetter.getAverageCompressionRatio(), Level.FINE);\n\t\tlist.add(getName(), \"Average decompression ratio\", ioStatsGetter.getAverageDecompressionRatio(), Level.FINE);\n\t\tlist.add(getName(), \"Waiting to send\", ioStatsGetter.getWaitingToSend(), Level.FINE);\n\n\t\tlist.add(getName(), \"Max daily cluster nodes count in last month\", maxNodes, Level.INFO);\n\t\tlist.add(getName(), \"Max nodes count within last week\", maxNodesWithinLastWeek, Level.INFO);\n\n\t\tif ((!list.checkLevel(Level.INFO)) && getNodesConnected().size() > 0) {\n\t\t\t// in FINEST level every component will provide this data\n\t\t\tlist.add(getName(), \"Known cluster nodes\", getNodesConnected().size(), Level.INFO);\n\t\t}\n\n\t\t// list.add(getName(), StatisticType.MSG_RECEIVED_OK.getDescription(),\n\t\t// packetsReceived,\n\t\t// Level.FINE);\n\t\t// list.add(getName(), StatisticType.MSG_SENT_OK.getDescription(),\n\t\t// packetsSent,\n\t\t// Level.FINE);\n\t}\n\n\t@Override\n\tpublic void setClusterController(ClusterControllerIfc cl_controller) {\n\t\tsuper.setClusterController(cl_controller);\n\t\tclusterController = cl_controller;\n\t\tclusterController.removeCommandListener(sendPacket);\n\t\tclusterController.setCommandListener(sendPacket);\n\t}\n\n\t@Override\n\tpublic void start() {\n\t\tsuper.start();\n\n\t\tif (clusterEventHandler == null) {\n\t\t\tclusterEventHandler = (ClusterInitializedEvent event) -> {\n\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\tlog.log(Level.FINE, \"Setting initialClusterConnectedDone to true (was: {0})\",\n\t\t\t\t\t\t\tinitialClusterConnectedDone);\n\t\t\t\t}\n\t\t\t\tinitialClusterConnectedDone = true;\n\t\t\t\teventBus.removeListener(clusterEventHandler);\n\t\t\t};\n\t\t}\n\n\t\teventBus.addListener(ClusterInitializedEvent.class, clusterEventHandler);\n\t}\n\n\t@Override\n\tpublic void stop() {\n\t\tsuper.stop();\n\t\teventBus.removeListener(clusterEventHandler);\n\t\tclusterEventHandler = null;\n\t}\n\n\tboolean isInitialClusterConnectedDone() {\n\t\treturn initialClusterConnectedDone;\n\t}\n\n\t@Override\n\tprotected void serviceConnected(XMPPIOService<Object> serv) {\n\t\tString[] routings = (String[]) serv.getSessionData().get(PORT_ROUTING_TABLE_PROP_KEY);\n\t\tString addr = (String) serv.getSessionData().get(PORT_REMOTE_HOST_PROP_KEY);\n\n\t\tClusterConnection conns = connectionsPool.get(addr);\n\n\t\tif (conns == null) {\n\t\t\tconns = new ClusterConnection(addr);\n\t\t\tconnectionsPool.put(addr, conns);\n\t\t}\n\n\t\tsynchronized (conns) {\n\t\t\tint size = conns.size();\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"New service connected: size = {0} / connectionsPool={1} / serv={2} / conns={3}, connsSize={4}\",\n\t\t\t\t\t\tnew Object[]{size, connectionsPool, serv, conns, conns.size()});\n\t\t\t}\n\n\t\t\t// setting userJid to hostname of remote cluster node\n\t\t\tserv.setUserJid((String) serv.getSessionData().get(PORT_REMOTE_HOST_PROP_KEY));\n\n\t\t\tconns.addConn(serv);\n\t\t\tif (size == 0 && conns.size() > 0) {\n\t\t\t\tupdateRoutings(routings, true);\n\t\t\t\tlog.log(Level.INFO, \"Connected to: {0}\", addr);\n\t\t\t\tupdateServiceDiscoveryItem(addr, addr, XMLNS + \" connected\", true);\n\t\t\t\tclusterController.nodeConnected(addr);\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\t// initial cluster connection done\n\t\t\tint connectedSize = getNodesConnected().size();\n\t\t\tint repoSize = repo.allItems().size();\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\"All repo nodes connected! Connected: {0}, repo size: {1}, initialClusterConnectedDone: {2}\",\n\t\t\t\t\t\tnew Object[]{connectedSize, repoSize, initialClusterConnectedDone});\n\t\t\t}\n\n\t\t\tsynchronized (this) {\n\t\t\t\tif (!initialClusterConnectedDone && (repoSize <= 1 || repoSize > 1 && connectedSize >= repoSize - 1)) {\n\t\t\t\t\tinitialClusterConnectedDone = true;\n\n\t\t\t\t\teventBus.fire(new ClusterInitializedEvent());\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (TigaseDBException e) {\n\t\t\tlog.log(Level.WARNING, \"There was an error while reading size of cluster repository\", e);\n\t\t}\n\n\n\t\tsuper.serviceConnected(serv);\n\t}\n\n\t@Override\n\tprotected boolean writePacketToSocket(Packet p) {\n\n\t\t// ++packetsSent;\n\t\tString ip = p.getTo().getDomain();\n\t\tClusterConnection conns = connectionsPool.get(ip);\n\n\t\tXMPPIOService<Object> serv = connectionSelector.selectConnection(p, conns);\n\t\tif (serv != null) {\n\t\t\treturn super.writePacketToSocket(serv, p);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"No cluster connection to send a packet: {0}\", p);\n\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tprotected int[] getDefPlainPorts() {\n\t\tif (repo == null) {\n\t\t\treturn new int[]{ClusterRepoItem.PORT_NO_PROP_VAL};\n\t\t}\n\t\tClusterRepoItem item = repo.getItem(getDefHostName().getDomain());\n\n\t\treturn new int[]{item.getPortNo()};\n\t}\n\n\t@Override\n\tprotected String getDefTrafficThrottling() {\n\t\treturn \"xmpp:25m:0:disc,bin:20000m:0:disc\";\n\t}\n\n\t@Override\n\tprotected long getMaxInactiveTime() {\n\t\treturn 3 * MINUTE;\n\t}\n\n\t@Override\n\tprotected Integer getMaxQueueSize(int def) {\n\t\treturn def * 10;\n\t}\n\n\t@Override\n\tprotected Map<String, Object> getParamsForPort(int port) {\n\t\tMap<String, Object> defs = new LinkedHashMap<String, Object>(10);\n\n\t\tdefs.put(PORT_TYPE_PROP_KEY, ConnectionType.accept);\n\t\tdefs.put(PORT_SOCKET_PROP_KEY, SocketType.plain);\n\t\tdefs.put(PORT_IFC_PROP_KEY, PORT_IFC_PROP_VAL);\n\n\t\treturn defs;\n\t}\n\n\t@Override\n\tprotected XMPPIOService<Object> getXMPPIOServiceInstance() {\n\t\treturn new XMPPIOService<>();\n\t}\n\n\t@Override\n\tprotected boolean isHighThroughput() {\n\t\treturn true;\n\t}\n\n\tprivate void sendEvent(REPO_ITEM_UPDATE_TYPE action, ClusterRepoItem item) {\n\n\t\t// either RepositoryItem was wrong or EventBus is not enabled - skiping broadcasting the event;\n\t\tif (eventBus == null || item == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tClusterRepoItemEvent event = new ClusterRepoItemEvent(item, action);\n\t\teventBus.fire(event);\n\t}\n\n\tprivate void processHandshake(Packet p, XMPPIOService<Object> serv) {\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Processing handshake: packet={0} / service={1} / sessionData={2}\",\n\t\t\t\t\tnew Object[]{p, serv, serv.getSessionData()});\n\t\t}\n\n\t\tString serv_addr = (String) serv.getSessionData().get(PORT_REMOTE_HOST_PROP_KEY);\n\t\ttry {\n\t\t\tInetAddress addr = InetAddress.getByName(serv_addr);\n\n\t\t\t// we ignore any local addresses\n\t\t\tif ((addr.isAnyLocalAddress() || addr.isLoopbackAddress()) ||\n\t\t\t\t\tNetworkInterface.getByInetAddress(addr) != null) {\n\t\t\t\tlog.log(Level.WARNING, \"Cluster handshake received from this instance, terminating: {0}\", serv_addr);\n\t\t\t\tserv.stop();\n\t\t\t\treturn;\n\t\t\t}\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.WARNING, \"Cluster handshake received from this instance, terminating: \" + serv_addr, ex);\n\t\t\tserv.stop();\n\t\t}\n\n\t\tswitch (serv.connectionType()) {\n\t\t\tcase connect: {\n\t\t\t\tString data = p.getElemCData();\n\n\t\t\t\tif (data == null) {\n\t\t\t\t\tserviceConnected(serv);\n\t\t\t\t} else {\n\t\t\t\t\tlog.log(Level.WARNING, \"Incorrect packet received: {0}\", p);\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase accept: {\n\t\t\t\tString digest = p.getElemCData();\n\t\t\t\tString id = (String) serv.getSessionData().get(XMPPIOService.SESSION_ID_KEY);\n\t\t\t\tString secret = (String) serv.getSessionData().get(SECRET_PROP_KEY);\n\n\t\t\t\ttry {\n\t\t\t\t\tString loc_digest = Algorithms.hexDigest(id, secret, \"SHA\");\n\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\t\"Calculating digest: secret={0}, digest={1}, loc_digest={2}, sessionData={3}\",\n\t\t\t\t\t\t\t\tnew Object[]{secret, digest, loc_digest, serv.getSessionData()});\n\t\t\t\t\t}\n\t\t\t\t\tif ((digest != null) && digest.equals(loc_digest)) {\n\t\t\t\t\t\tPacket resp = Packet.packetInstance(new Element(\"handshake\"), null, null);\n\n\t\t\t\t\t\twritePacketToSocket(serv, resp);\n\t\t\t\t\t\tserviceConnected(serv);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (secret == null) {\n\t\t\t\t\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\t\t\t\t\"Remote hostname not found in local configuration or time difference between cluster nodes is too big. Connection not accepted: {0}\",\n\t\t\t\t\t\t\t\t\tserv);\n\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\t\t\t\"Remote hostname not found in local configuration or time difference between cluster nodes is too big. Connection not accepted! Remote host: {0}, sessionData: {1}, repoItem: {2}, service: {3}\",\n\t\t\t\t\t\t\t\t\t\tnew Object[]{serv_addr, serv.getSessionData(), repo.getItem(serv_addr), serv});\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlog.log(Level.WARNING, \"Handshaking password doesn''t match, disconnecting: {0}\", serv);\n\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\t\t\t\t\t\"Handshaking password doesn''t match, disconnecting! Remote host: {0}, sessionData: {1}, repoItem: {2}, service: {3}\",\n\t\t\t\t\t\t\t\t\t\tnew Object[]{serv_addr, serv.getSessionData(), repo.getItem(serv_addr), serv});\n\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\t\t\t\t\t\tserv.stop();\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.log(Level.SEVERE, \"Handshaking error.\", e);\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\n\t\t\t\t// Do nothing, more data should come soon...\n\t\t\t\tbreak;\n\t\t}    // end of switch (service.connectionType())\n\t}\n\n\tprivate void updateRoutings(String[] routings, boolean add) {\n\t\tif (add) {\n\t\t\tfor (String route : routings) {\n\t\t\t\ttry {\n\t\t\t\t\taddRegexRouting(route);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Can not add regex routing ''{0}'' : {1}\", new Object[]{route, e});\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor (String route : routings) {\n\t\t\t\ttry {\n\t\t\t\t\tremoveRegexRouting(route);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Can not remove regex routing ''{0}'' : {1}\", new Object[]{route, e});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static class ClusterInitializedEvent implements EventBusEvent {\n\n\t\tpublic ClusterInitializedEvent() {\n\n\t\t}\n\n\t}\n\n\t@Bean(name = \"clConRepositoryBean\", parent = ClusterConnectionManager.class, active = true)\n\tpublic static class DefClConRepositoryBean\n\t\t\textends AbstractSDComponentRepositoryBean<ClusterRepoItem> {\n\n\t\tprivate static DataSourceHelper.Matcher matcher = (Class clazz) -> {\n\t\t\treturn ReflectionHelper.classMatchesClassWithParameters(clazz, ComponentRepositoryDataSourceAware.class,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew Type[]{ClusterRepoItem.class,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   DataSource.class});\n\t\t};\n\t\tprivate ComponentRepository<ClusterRepoItem> repo = null;\n\n\t\t@Override\n\t\tprotected Class<? extends ComponentRepositoryDataSourceAware<ClusterRepoItem, DataSource>> findClassForDataSource(\n\t\t\t\tDataSource dataSource) throws DBInitException {\n\t\t\tClass cls = DataSourceHelper.getDefaultClass(ComponentRepository.class, dataSource.getResourceUri(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t matcher);\n\t\t\treturn (Class<ComponentRepositoryDataSourceAware<ClusterRepoItem, DataSource>>) cls;\n\t\t}\n\n\t}\n\n\tprivate class IOServiceStatisticsGetter\n\t\t\timplements ServiceChecker<XMPPIOService<Object>> {\n\n\t\tprivate int clIOQueue = 0;\n\t\tprivate float compressionRatio = 0f;\n\t\tprivate int counter = 0;\n\t\tprivate float decompressionRatio = 0f;\n\t\tprivate StatisticsList list = new StatisticsList(Level.ALL);\n\n\t\t@Override\n\t\tpublic void check(XMPPIOService<Object> service) {\n\t\t\tservice.getStatistics(list, true);\n\t\t\tcompressionRatio += list.getValue(\"zlibio\", \"Average compression rate\", -1f);\n\t\t\tdecompressionRatio += list.getValue(\"zlibio\", \"Average decompression rate\", -1f);\n\t\t\t++counter;\n\t\t\tclIOQueue += service.waitingToSendSize();\n\t\t}\n\n\t\tpublic void reset() {\n\n\t\t\t// Statistics are reset on the low socket level instead. This way we do\n\t\t\t// not loose\n\t\t\t// any stats in case of the disconnection.\n\t\t\t// bytesReceived = 0;\n\t\t\t// bytesSent = 0;\n\t\t\tclIOQueue = 0;\n\t\t\tcounter = 0;\n\t\t\tcompressionRatio = 0f;\n\t\t\tdecompressionRatio = 0f;\n\t\t}\n\n\t\tpublic float getAverageCompressionRatio() {\n\t\t\treturn compressionRatio / counter;\n\t\t}\n\n\t\tpublic float getAverageDecompressionRatio() {\n\t\t\treturn decompressionRatio / counter;\n\t\t}\n\n\t\tpublic int getWaitingToSend() {\n\t\t\treturn clIOQueue;\n\t\t}\n\t}\n\n\tprivate class SendPacket\n\t\t\textends CommandListenerAbstract {\n\n\t\tprivate SendPacket(String name) {\n\t\t\tsuper(name, null);\n\t\t}\n\n\t\t@Override\n\t\tpublic void executeCommand(JID fromNode, Set<JID> visitedNodes, Map<String, String> data,\n\t\t\t\t\t\t\t\t   Queue<Element> packets) throws ClusterCommandException {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Called fromNode: {0}, visitedNodes: {1}, data: {2}, packets: {3}\",\n\t\t\t\t\t\tnew Object[]{fromNode, visitedNodes, data, packets});\n\t\t\t}\n\t\t\tfor (Element element : packets) {\n\t\t\t\ttry {\n\t\t\t\t\taddPacketNB(Packet.packetInstance(element));\n\n\t\t\t\t\t// writePacketToSocket();\n\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Stringprep exception for packet: {0}\", element);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\tprotected class Watchdog\n\t\t\textends ConnectionManager.Watchdog {\n\n\t\t@Override\n\t\tprotected long getDurationSinceLastTransfer(final XMPPIOService service) {\n\t\t\tLong lastTransfer = (Long) service.getSessionData().get(\"lastConnectivityCheck\");\n\t\t\tif (lastTransfer == null) {\n\t\t\t\tservice.getSessionData().put(\"lastConnectivityCheck\", System.currentTimeMillis() - watchdogTimeout);\n\t\t\t\treturn watchdogTimeout;\n\t\t\t}\n\t\t\treturn System.currentTimeMillis() - lastTransfer;\n\t\t}\n\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/cluster/ClusterConnectionSelector.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster;\n\nimport tigase.cluster.api.ClusterConnectionHandler;\nimport tigase.cluster.api.ClusterConnectionSelectorIfc;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Packet;\nimport tigase.server.Priority;\nimport tigase.xmpp.XMPPIOService;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Advanced implementation of ClusterConnectionSelectorIfc which separates packets with priority CLUSTER or higher from\n * other packets in cluster connections by using separate connections for them\n *\n * @author andrzej\n */\n@Bean(name = \"clusterConnectionSelector\", parent = ClusterConnectionManager.class, active = true)\npublic class ClusterConnectionSelector\n\t\timplements ClusterConnectionSelectorIfc {\n\n\tprotected static final String CLUSTER_SYS_CONNECTIONS_PER_NODE_PROP_KEY = \"cluster-sys-connections-per-node\";\n\n\t@ConfigField(desc = \"Number of cluster connetions per node\", alias = \"cluster-connections-per-node\")\n\tprivate int allConns = ClusterConnectionManager.CLUSTER_CONNECTIONS_PER_NODE_VAL;\n\t@Inject(nullAllowed = true)\n\tprivate ClusterConnectionHandler handler;\n\t@ConfigField(desc = \"Number of system connections per node\", alias = \"cluster-sys-connections-per-node\")\n\tprivate int sysConns = 2;\n\n\t@Override\n\tpublic XMPPIOService<Object> selectConnection(Packet p, ClusterConnection conn) {\n\t\tif (conn == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tint code = Math.abs(handler.hashCodeForPacket(p));\n\t\tList<XMPPIOService<Object>> conns = conn.getConnections();\n\t\tif (conns.size() > 0) {\n\t\t\tif (conns.size() > sysConns) {\n\t\t\t\tif (p.getPriority() != null && p.getPriority().ordinal() <= Priority.CLUSTER.ordinal()) {\n\t\t\t\t\treturn conns.get(code % sysConns);\n\t\t\t\t} else {\n\t\t\t\t\treturn conns.get(sysConns + (code % (conns.size() - sysConns)));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn conns.get(code % conns.size());\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void setClusterConnectionHandler(ClusterConnectionHandler handler) {\n\t\tthis.handler = handler;\n\t}\n\n\t@Override\n\tpublic void setProperties(Map<String, Object> props) {\n\t\tif (props.containsKey(CLUSTER_SYS_CONNECTIONS_PER_NODE_PROP_KEY)) {\n\t\t\tsysConns = (Integer) props.get(CLUSTER_SYS_CONNECTIONS_PER_NODE_PROP_KEY);\n\t\t}\n\t\tif (props.containsKey(ClusterConnectionManager.CLUSTER_CONNECTIONS_PER_NODE_PROP_KEY)) {\n\t\t\tallConns = (Integer) props.get(ClusterConnectionManager.CLUSTER_CONNECTIONS_PER_NODE_PROP_KEY);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/cluster/ClusterConnectionSelectorOld.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster;\n\nimport tigase.cluster.api.ClusterConnectionHandler;\nimport tigase.cluster.api.ClusterConnectionSelectorIfc;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Packet;\nimport tigase.xmpp.XMPPIOService;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * ClusterConnectionSelectorOld class implements old cluster connection selection algoritm which before was part of\n * ClusterConnectionManager class.\n *\n * @author andrzej\n */\n@Bean(name = \"clusterConnectionSelector\", active = true)\npublic class ClusterConnectionSelectorOld\n\t\timplements ClusterConnectionSelectorIfc {\n\n\tprivate ClusterConnectionHandler handler;\n\n\t@Override\n\tpublic XMPPIOService<Object> selectConnection(Packet p, ClusterConnection conn) {\n\t\tif (conn == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tint code = Math.abs(handler.hashCodeForPacket(p));\n\t\tList<XMPPIOService<Object>> conns = conn.getConnections();\n\t\tif (conns.size() > 0) {\n\t\t\treturn conns.get(code % conns.size());\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void setClusterConnectionHandler(ClusterConnectionHandler handler) {\n\t\tthis.handler = handler;\n\t}\n\n\t@Override\n\tpublic void setProperties(Map<String, Object> props) {\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/cluster/ClusterController.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster;\n\nimport tigase.cluster.api.*;\nimport tigase.conf.Configurable;\nimport tigase.eventbus.component.EventBusComponent;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.selector.ClusterModeRequired;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.AbstractComponentRegistrator;\nimport tigase.server.Packet;\nimport tigase.server.Priority;\nimport tigase.server.ServerComponent;\nimport tigase.xml.Element;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.ArrayDeque;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentSkipListMap;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.concurrent.atomic.AtomicLong;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Describe class ClusterController here.\n * <br>\n * Created: Mon Jun 9 20:03:28 2008\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"cluster-contr\", parent = Kernel.class, active = true, exportable = true)\n@ConfigType({ConfigTypeEnum.DefaultMode, ConfigTypeEnum.SessionManagerMode, ConfigTypeEnum.ConnectionManagersMode,\n\t\t\t ConfigTypeEnum.ComponentMode})\n@ClusterModeRequired(active = true)\npublic class ClusterController\n\t\textends AbstractComponentRegistrator<ClusteredComponentIfc>\n\t\timplements Configurable, ClusterControllerIfc {\n\n\tpublic static final String MY_DOMAIN_NAME_PROP_KEY = \"domain-name\";\n\n\tpublic static final String MY_DOMAIN_NAME_PROP_VAL = \"localhost\";\n\tprivate static final Logger log = Logger.getLogger(ClusterController.class.getName());\n\n\tprivate ConcurrentSkipListMap<String, CommandListener> commandListeners = new ConcurrentSkipListMap<String, CommandListener>();\n\tprivate AtomicLong currId = new AtomicLong(1L);\n\n\tprivate final CopyOnWriteArrayList<ClusteredComponentIfc> clusteredComponents = new CopyOnWriteArrayList<>();\n\n\t@Override\n\tpublic void componentAdded(ClusteredComponentIfc component) {\n\t\t// we are not passing wrapper to ClusterConnectionManager as we need to\n\t\t// check later if it's command is added\n\t\tif (component instanceof ClusterConnectionManager) {\n\t\t\tcomponent.setClusterController(this);\n\t\t} else {\n\t\t\tWrapper wrapper = new Wrapper(this, component);\n\t\t\tcomponent.setClusterController(wrapper);\n\t\t}\n\t\tupdateServiceDiscoveryItem(getName(), component.getName(), \"Component: \" + component.getName(), true);\n\t\tif (component instanceof EventBusComponent) {\n\t\t\tclusteredComponents.add(0, component);\n\t\t} else {\n\t\t\tclusteredComponents.add(component);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void componentRemoved(ClusteredComponentIfc component) {\n\t\tclusteredComponents.remove(component);\n\t}\n\n\t@Override\n\tpublic void handleClusterPacket(Element packet) {\n\t\tClusterElement clel = new ClusterElement(packet);\n\t\tCommandListener cmdList = commandListeners.get(clel.getMethodName());\n\n\t\tif (cmdList != null) {\n\t\t\tclel.addVisitedNode(JID.jidInstanceNS(packet.getAttributeStaticStr(Packet.TO_ATT)));\n\n\t\t\tMap<String, String> data = clel.getAllMethodParams();\n\t\t\tSet<JID> visitedNodes = clel.getVisitedNodes();\n\t\t\tQueue<Element> packets = clel.getDataPackets();\n\n\t\t\ttry {\n\t\t\t\tcmdList.executeCommand(clel.getFirstNode(), visitedNodes, data, packets);\n\n\t\t\t\t// TODO Send result back (possibly)\n\t\t\t} catch (ClusterCommandException ex) {\n\n\t\t\t\t// TODO Send error back\n//\t\t\t\tex.printStackTrace();\n\t\t\t\tlog.log(Level.WARNING, \"Error handling cluster packet\", ex);\n\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"Missing CommandListener for cluster method: {0}\", clel.getMethodName());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void nodeConnected(String node) {\n\t\tsuper.nodeConnected(node);\n\t\tfor (ClusteredComponentIfc comp : clusteredComponents) {\n\t\t\tcomp.nodeConnected(node);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void nodeDisconnected(String node) {\n\t\tsuper.nodeDisconnected(node);\n\t\tfor (ClusteredComponentIfc comp : clusteredComponents) {\n\t\t\tcomp.nodeDisconnected(node);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processPacket(final Packet packet, final Queue<Packet> results) {\n\t}\n\n\t@Override\n\tpublic void removeCommandListener(CommandListener listener) {\n\t\tremoveCommandListener(listener.getName(), listener);\n\t}\n\n\t@Override\n\tpublic void sendToNodes(String command, Map<String, String> data, Queue<Element> packets, JID fromNode,\n\t\t\t\t\t\t\tSet<JID> visitedNodes, JID... toNodes) {\n\t\t// this command should not be prefixed as we passed original instance instead of \n\t\t// wrapper to ClusterConnectionManager\n\t\tCommandListener packetSender = commandListeners.get(DELIVER_CLUSTER_PACKET_CMD);\n\n\t\tif (packetSender == null) {\n\t\t\tlog.log(Level.SEVERE,\n\t\t\t\t\t\"Misconfiguration or packaging error, can not send a \" + \"cluster packet! No CommandListener for \" +\n\t\t\t\t\t\t\tDELIVER_CLUSTER_PACKET_CMD);\n\n\t\t\treturn;\n\t\t}\n\n\t\tQueue<Element> results = new ArrayDeque<Element>();\n\n\t\t// retrive listener for command and it's priority to if available\n\t\tCommandListener listener = commandListeners.get(command);\n\t\tPriority priority = listener != null ? listener.getPriority() : null;\n\n\t\t// TODO: Maybe more optimal would be creating the object once and then clone\n\t\t// it? However, the 'to' parameter must be double-checked whether all\n\t\t// internal states are set properly for each different to parameter\n\t\tfor (JID to : toNodes) {\n\t\t\tClusterElement clel = ClusterElement.createClusterMethodCall(fromNode, to, StanzaType.set, command, data);\n\n\t\t\t// set priority to ClusterElement so it will get proper priority for processing\n\t\t\tif (priority != null) {\n\t\t\t\tclel.setPriority(priority);\n\t\t\t}\n\t\t\tclel.addVisitedNodes(visitedNodes);\n\t\t\tclel.addDataPackets(packets);\n\n\t\t\tElement result = clel.getClusterElement(nextId());\n\n\t\t\tresults.offer(result);\n\t\t}\n\t\ttry {\n\t\t\tpacketSender.executeCommand(null, null, null, results);\n\t\t} catch (ClusterCommandException ex) {\n\t\t\tlog.log(Level.WARNING, \"Error sending packet to nodes\", ex);\n\t\t\t// TODO Auto-generated catch block\n\t\t}\n\t}\n\n\t@Override\n\tpublic void sendToNodes(String command, Queue<Element> packets, JID fromNode, Set<JID> visitedNodes,\n\t\t\t\t\t\t\tJID... toNodes) {\n\t\tsendToNodes(command, null, packets, fromNode, visitedNodes, toNodes);\n\t}\n\n\t@Override\n\tpublic void sendToNodes(String command, Map<String, String> data, JID fromNode, Set<JID> visitedNodes,\n\t\t\t\t\t\t\tJID... toNodes) {\n\t\tsendToNodes(command, data, (Queue<Element>) null, fromNode, visitedNodes, toNodes);\n\t}\n\n\t@Override\n\tpublic void sendToNodes(String command, Map<String, String> data, JID fromNode, JID... toNodes) {\n\t\tsendToNodes(command, data, (Queue<Element>) null, fromNode, null, toNodes);\n\t}\n\n\t@Override\n\tpublic void sendToNodes(String command, JID fromNode, JID... toNodes) {\n\t\tsendToNodes(command, null, (Queue<Element>) null, fromNode, null, toNodes);\n\t}\n\n\t@Override\n\tpublic void sendToNodes(String command, Element packet, JID fromNode, Set<JID> visitedNodes, JID... toNodes) {\n\t\tsendToNodes(command, null, packet, fromNode, visitedNodes, toNodes);\n\t}\n\n\t@Override\n\tpublic void sendToNodes(String command, Map<String, String> data, Element packet, JID fromNode,\n\t\t\t\t\t\t\tSet<JID> visitedNodes, JID... toNodes) {\n\t\tQueue<Element> packets = new ArrayDeque<Element>();\n\n\t\tpackets.offer(packet);\n\t\tsendToNodes(command, data, packets, fromNode, visitedNodes, toNodes);\n\t}\n\n\t@Override\n\tpublic String getDiscoCategoryType() {\n\t\treturn \"load\";\n\t}\n\n\t@Override\n\tpublic String getDiscoDescription() {\n\t\treturn \"Cluster controller\";\n\t}\n\n\t@Override\n\tpublic boolean isCorrectType(ServerComponent component) {\n\t\treturn (component instanceof ClusteredComponentIfc) && !(component instanceof ClusterControllerIfc);\n\t}\n\n\t@Override\n\tpublic void setCommandListener(CommandListener listener) {\n\t\tsetCommandListener(listener.getName(), listener);\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\tsuper.setName(name);\n\t}\n\n\tprivate String nextId() {\n\t\treturn \"cl-\" + currId.incrementAndGet();\n\t}\n\n\tprivate void removeCommandListener(String name, CommandListener listener) {\n\t\tcommandListeners.remove(name, listener);\n\t}\n\n\tprivate void setCommandListener(String name, CommandListener listener) {\n\t\tcommandListeners.put(name, listener);\n\t}\n\n\tprivate class Wrapper\n\t\t\timplements ClusterControllerIfc {\n\n\t\tprivate final ClusteredComponentIfc component;\n\t\tprivate final ClusterController controller;\n\t\tprivate final String name;\n\n\t\tpublic Wrapper(ClusterController controller, ClusteredComponentIfc component) {\n\t\t\tthis.controller = controller;\n\t\t\tthis.component = component;\n\t\t\tname = component.getName();\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleClusterPacket(Element packet) {\n\t\t\tcontroller.handleClusterPacket(packet);\n\t\t}\n\n\t\t@Override\n\t\tpublic void nodeConnected(String addr) {\n\t\t\tthrow new UnsupportedOperationException(\"This method should not be called.\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void nodeDisconnected(String addr) {\n\t\t\tthrow new UnsupportedOperationException(\"This method should not be called.\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void removeCommandListener(CommandListener listener) {\n\t\t\tString name = this.name + \"-\" + listener.getName();\n\t\t\tcontroller.removeCommandListener(name, listener);\n\t\t}\n\n\t\t@Override\n\t\tpublic void sendToNodes(String command, Map<String, String> data, Queue<Element> packets, JID fromNode,\n\t\t\t\t\t\t\t\tSet<JID> visitedNodes, JID... toNodes) {\n\t\t\tcommand = name + \"-\" + command;\n\t\t\tcontroller.sendToNodes(command, data, packets, fromNode, visitedNodes, toNodes);\n\t\t}\n\n\t\t@Override\n\t\tpublic void sendToNodes(String command, Queue<Element> packets, JID fromNode, Set<JID> visitedNodes,\n\t\t\t\t\t\t\t\tJID... toNodes) {\n\t\t\tcommand = name + \"-\" + command;\n\t\t\tcontroller.sendToNodes(command, packets, fromNode, visitedNodes, toNodes);\n\t\t}\n\n\t\t@Override\n\t\tpublic void sendToNodes(String command, Map<String, String> data, JID fromNode, Set<JID> visitedNodes,\n\t\t\t\t\t\t\t\tJID... toNodes) {\n\t\t\tcommand = name + \"-\" + command;\n\t\t\tcontroller.sendToNodes(command, data, fromNode, visitedNodes, toNodes);\n\t\t}\n\n\t\t@Override\n\t\tpublic void sendToNodes(String command, Map<String, String> data, JID fromNode, JID... toNodes) {\n\t\t\tcommand = name + \"-\" + command;\n\t\t\tcontroller.sendToNodes(command, data, fromNode, toNodes);\n\t\t}\n\n\t\t@Override\n\t\tpublic void sendToNodes(String command, JID fromNode, JID... toNodes) {\n\t\t\tcommand = name + \"-\" + command;\n\t\t\tcontroller.sendToNodes(command, fromNode, toNodes);\n\t\t}\n\n\t\t@Override\n\t\tpublic void sendToNodes(String command, Element packet, JID fromNode, Set<JID> visitedNodes, JID... toNodes) {\n\t\t\tcommand = name + \"-\" + command;\n\t\t\tcontroller.sendToNodes(command, packet, fromNode, visitedNodes, toNodes);\n\t\t}\n\n\t\t@Override\n\t\tpublic void sendToNodes(String command, Map<String, String> data, Element packet, JID fromNode,\n\t\t\t\t\t\t\t\tSet<JID> visitedNodes, JID... toNodes) {\n\t\t\tcommand = name + \"-\" + command;\n\t\t\tcontroller.sendToNodes(command, data, packet, fromNode, visitedNodes, toNodes);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setCommandListener(CommandListener listener) {\n\t\t\tString name = this.name + \"-\" + listener.getName();\n\t\t\tcontroller.setCommandListener(name, listener);\n\t\t}\n\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/cluster/SessionManagerClustered.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster;\n\nimport tigase.cluster.api.ClusterControllerIfc;\nimport tigase.cluster.api.ClusteredComponentIfc;\nimport tigase.cluster.api.SessionManagerClusteredIfc;\nimport tigase.cluster.strategy.ClusteringStrategyIfc;\nimport tigase.cluster.strategy.ConnectionRecordIfc;\nimport tigase.eventbus.FillRoutedEvent;\nimport tigase.eventbus.RouteEvent;\nimport tigase.eventbus.component.stores.Subscription;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.beans.selector.ClusterModeRequired;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Command;\nimport tigase.server.ComponentInfo;\nimport tigase.server.Message;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.server.xmppsession.UserSessionEvent;\nimport tigase.server.xmppsession.UserSessionEventWithProcessorResultWriter;\nimport tigase.stats.StatisticsList;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.XMPPSession;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport javax.script.Bindings;\nimport java.time.LocalDateTime;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.xmpp.StreamError.ResourceConstraint;\n\n/**\n * Class SessionManagerClusteredOld\n * <br>\n * Created: Tue Nov 22 07:07:11 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\n@Bean(name = \"sess-man\", parent = Kernel.class, active = true, exportable = true)\n@ConfigType({ConfigTypeEnum.DefaultMode, ConfigTypeEnum.SessionManagerMode})\n@ClusterModeRequired(active = true)\npublic class SessionManagerClustered\n\t\textends SessionManager\n\t\timplements ClusteredComponentIfc, SessionManagerClusteredIfc {\n\n\tpublic static final String CLUSTER_STRATEGY_VAR = \"clusterStrategy\";\n\n\tpublic static final String MY_DOMAIN_NAME_PROP_KEY = \"domain-name\";\n\n\tpublic static final String STRATEGY_CLASS_PROP_KEY = \"sm-cluster-strategy-class\";\n\n\tpublic static final String STRATEGY_CLASS_PROP_VAL = \"tigase.cluster.strategy.DefaultClusteringStrategy\";\n\n\tpublic static final String STRATEGY_CLASS_PROPERTY = \"--sm-cluster-strategy-class\";\n\n\tpublic static final int SYNC_MAX_BATCH_SIZE = 1000;\n\n\tprivate static final Logger log = Logger.getLogger(SessionManagerClustered.class.getName());\n\tprivate ClusterControllerIfc clusterController = null;\n\n\t;\n\n\tprivate ComponentInfo cmpInfo = null;\n\t@ConfigField(desc = \"Component own internal JID\")\n\tprivate JID my_address;\n\t@ConfigField(desc = \"Server domain name\")\n\tprivate JID my_hostname;\n\tprivate int nodesNo = 0;\n\t@Inject\n\tprivate ClusteringStrategyIfc strategy = null;\n\tpublic SessionManagerClustered() {\n\t\tString[] local_domains = DNSResolverFactory.getInstance().getDefaultHosts();\n\n\t\tString my_domain = local_domains[0];\n\n\t\ttry {\n\t\t\tmy_hostname = JID.jidInstance(my_domain);\n\t\t\tmy_address = JID.jidInstance(getName(), my_domain, null);\n\t\t} catch (TigaseStringprepException e) {\n\t\t\tlog.log(Level.WARNING, \"Creating component source address failed stringprep processing: {0}@{1}\",\n\t\t\t\t\tnew Object[]{getName(), my_hostname});\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean containsJid(BareJID jid) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Called for jid: {0}\", jid);\n\t\t}\n\n\t\treturn super.containsJid(jid) || strategy.containsJid(jid);\n\t}\n\n\t@Override\n\tpublic synchronized void everySecond() {\n\t\tsuper.everySecond();\n\t\tif (strategy != null) {\n\t\t\tstrategy.everySecond();\n\t\t}\n\t}\n\n\t@Override\n\tpublic synchronized void everyMinute() {\n\t\tsuper.everyMinute();\n\t\tif (strategy != null) {\n\t\t\tstrategy.everyMinute();\n\t\t}\n\t}\n\n\t@Override\n\tpublic synchronized void everyHour() {\n\t\tsuper.everyHour();\n\t\tif (strategy != null) {\n\t\t\tstrategy.everyHour();\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean fastAddOutPacket(Packet packet) {\n\t\treturn super.fastAddOutPacket(packet);\n\t}\n\n\t@Override\n\tpublic void handleLocalPacket(Packet packet, XMPPResourceConnection conn) {\n\t\tif (strategy != null) {\n\t\t\tstrategy.handleLocalPacket(packet, conn);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void handleLogin(BareJID userId, XMPPResourceConnection conn) {\n\t\tsuper.handleLogin(userId, conn);\n\t\tstrategy.handleLocalUserLogin(userId, conn);\n\t}\n\n\t@Override\n\tpublic void handleLogout(BareJID userId, XMPPResourceConnection conn) {\n\t\t// Exception here should not normally happen, but if it does, then\n\t\t// consequences might be severe, let's catch it then\n\t\ttry {\n\t\t\tif (conn.isAuthorized() && conn.isResourceSet()) {\n\t\t\t\tstrategy.handleLocalUserLogout(userId, conn);\n\t\t\t}\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.WARNING, \"This should not happen, check it out!, \", ex);\n\t\t}\n\t\tsuper.handleLogout(userId, conn); //To change body of generated methods, choose Tools | Templates.\n\t}\n\n\t@Override\n\tpublic void handleResourceBind(XMPPResourceConnection conn) {\n\t\tsuper.handleResourceBind(conn);\n\t\tstrategy.handleLocalResourceBind(conn);\n\t}\n\n\t@Override\n\tprotected void checkSingleUserConnectionsLimit(XMPPResourceConnection conn) {\n\t\t// this call should not be forwarded, in cluster strategy should take care of that in handleLocalResourceBind()\n\t\tInteger limit = getSingleUserConnectionsLimit();\n\t\tif (limit != null) {\n\t\t\ttry {\n\t\t\t\tList<ConnectionRecordIfc> conns;\n\t\t\t\tif (strategy.hasCompleteJidsInfo() && (conns = strategy.getConnectionRecordsByCreationTime(conn.getBareJID())) != null) {\n\t\t\t\t\tif (conns.size() > limit) {\n\t\t\t\t\t\tList<ConnectionRecordIfc> toDisconnect = conns.subList(0, conns.size() - limit);\n\t\t\t\t\t\tfor (ConnectionRecordIfc rec : toDisconnect) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tPacket cmd = Command.CLOSE.getPacket(getComponentId(), rec.getConnectionId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t StanzaType.set, conn.nextStanzaId());\n\n\t\t\t\t\t\t\t\tvar errorElement = ResourceConstraint.prepareStreamErrorElement(\n\t\t\t\t\t\t\t\t\t\"Exceeded the limit of allowed connections\");\n\t\t\t\t\t\t\t\tcmd.getElement().getChild(\"command\").addChild(errorElement);\n\t\t\t\t\t\t\t\tfastAddOutPacket(cmd);\n\t\t\t\t\t\t\t} catch (Exception ex) {\n\n\t\t\t\t\t\t\t\t// TODO Auto-generated catch block\n\t\t\t\t\t\t\t\tlog.log(Level.WARNING, \"Error executing cluster command\", ex);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// if strategy is not aware of all user connections, apply restrictions on single node..\n\t\t\t\t\tsuper.checkSingleUserConnectionsLimit(conn);\n\t\t\t\t}\n\t\t\t} catch (NotAuthorizedException ex) {\n\t\t\t\t// if connection is not authorized, we can skip it\n\t\t\t\tlog.log(Level.CONFIG, \"Exception during closing old connection, ignoring.\", ex);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void initBindings(Bindings binds) {\n\t\tsuper.initBindings(binds);\n\t\tbinds.put(CLUSTER_STRATEGY_VAR, strategy);\n\t}\n\n\t@Override\n\tpublic void onNodeConnected(JID jid) {\n\t\tsuper.onNodeConnected(jid);\n\n\t\tif (!getComponentId().equals(jid)) {\n\t\t\tstrategy.nodeConnected(jid);\n\n\t\t\tsendAdminNotification(jid.getDomain(), STATUS.CONNECTED);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onNodeDisconnected(JID jid) {\n\t\tsuper.onNodeDisconnected(jid);\n\n\t\tif (!getComponentId().equals(jid)) {\n\t\t\tstrategy.nodeDisconnected(jid);\n\n\t\t\t// Not sure what to do here, there might be still packets\n\t\t\t// from the cluster node waiting....\n\t\t\t// delTrusted(jid);\n\n\t\t\tsendAdminNotification(jid.getDomain(), STATUS.DISCONNECTED);\n\t\t}\n\t}\n\n\t@Override\n\tpublic int processingInThreads() {\n\t\treturn Math.max(nodesNo, super.processingInThreads());\n\t}\n\n\t@Override\n\tpublic int processingOutThreads() {\n\t\treturn Math.max(nodesNo, super.processingOutThreads());\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t * <br>\n\t * <br>\n\t * <br>\n\t * This is a standard component method for processing packets. The method takes care of cases where the packet\n\t * cannot be processed locally, in such a case it is forwarded to another node.\n\t *\n\t * @param packet to be processed\n\t */\n\t@Override\n\tpublic void processPacket(Packet packet) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Received packet: {0}\", packet);\n\t\t}\n\n\t\tif (packet.isCommand() && processCommand(packet)) {\n\t\t\tpacket.processedBy(\"SessionManager\");\n\n\t\t} else {\n\t\t\tif (messageArchive != null) {\n\t\t\t\tmessageArchive.generateStableId(packet);\n\t\t\t}\n\n\t\t\tXMPPResourceConnection conn = getXMPPResourceConnection(packet);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Ressource connection found: {0}\", conn);\n\t\t\t}\n\n\t\t\tboolean clusterOK = strategy.processPacket(packet, conn);\n\n\t\t\tif (conn == null) {\n\t\t\t\tif (isBrokenPacket(packet) || processAdminsOrDomains(packet)) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Ignoring/dropping packet: {0}\", packet);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\n\t\t\t\t\t// Process is as packet to offline user only if there are no other\n\t\t\t\t\t// nodes for the packet to be processed.\n\t\t\t\t\tif (!clusterOK) {\n\n\t\t\t\t\t\t// Process packet for offline user\n\t\t\t\t\t\tprocessPacket(packet, (XMPPResourceConnection) null);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tprocessPacket(packet, conn);\n\n\t\t\t\t// smTm = System.currentTimeMillis() - startTime;\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void processPacket(Packet packet, XMPPResourceConnection conn) {\n\t\tsuper.processPacket(packet, conn);\n\t}\n\n\t@Override\n\tpublic void processPresenceUpdate(XMPPSession session, Element packet) {\n\t\tsuper.processPresenceUpdate(session, packet);\n\t}\n\n\t@Override\n\tpublic ComponentInfo getComponentInfo() {\n\t\tcmpInfo = super.getComponentInfo();\n\t\tcmpInfo.getComponentData().put(\"ClusteringStrategy\", (strategy != null) ? strategy.getClass() : null);\n\n\t\treturn cmpInfo;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t * <br>\n\t * If the installation knows about user's JID, that he is connected to the system, then this method returns all\n\t * user's connection IDs. As an optimization we can forward packets to all user's connections directly from a single\n\t * node.\n\t *\n\t * @param jid a user's JID for whom we query information.\n\t *\n\t * @return a list of all user's connection IDs.\n\t */\n\t@Override\n\tpublic JID[] getConnectionIdsForJid(BareJID jid) {\n\t\tJID[] ids = super.getConnectionIdsForJid(jid);\n\n\t\tif (ids == null) {\n\t\t\tids = strategy.getConnectionIdsForJid(jid);\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Called for jid: {0}, results: {1}\", new Object[]{jid, Arrays.toString(ids)});\n\t\t}\n\n\t\treturn ids;\n\t}\n\n\t@Override\n\tpublic String getDiscoDescription() {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"disco description from SM Clustered\");\n\t\t}\n\n\t\tString result;\n\n\t\tresult = super.getDiscoDescription();\n\t\tresult += \" clustered, \";\n\t\tresult += strategy;\n\n\t\treturn result;\n\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t\tsuper.getStatistics(list);\n\t\tif (strategy != null) {\n\t\t\tstrategy.getStatistics(list);\n\t\t}\n\t}\n\n\t/**\n\t * Returns active clustering strategy object.\n\t *\n\t * @return active clustering strategy object.\n\t */\n\tpublic ClusteringStrategyIfc getStrategy() {\n\t\treturn strategy;\n\t}\n\n\t@Override\n\tpublic XMPPResourceConnection getXMPPResourceConnection(Packet p) {\n\t\treturn super.getXMPPResourceConnection(p);\n\t}\n\n\t@Override\n\tpublic ConcurrentHashMap<JID, XMPPResourceConnection> getXMPPResourceConnections() {\n\t\treturn connectionsByFrom;\n\t}\n\n\t@Override\n\tpublic ConcurrentHashMap<BareJID, XMPPSession> getXMPPSessions() {\n\t\treturn sessionsByNodeId;\n\t}\n\n\t@Override\n\tpublic boolean hasCompleteJidsInfo() {\n\t\treturn strategy.hasCompleteJidsInfo();\n\t}\n\n\t@Override\n\tpublic boolean hasXMPPResourceConnectionForConnectionJid(JID connJid) {\n\t\treturn this.connectionsByFrom.containsKey(connJid);\n\t}\n\n\t@Override\n\tpublic void setClusterController(ClusterControllerIfc cl_controller) {\n\t\tsuper.setClusterController(cl_controller);\n\t\tclusterController = cl_controller;\n\t\tif (strategy != null) {\n\t\t\tstrategy.setClusterController(clusterController);\n\t\t}\n\n//  clusterController.removeCommandListener(USER_CONNECTED_CMD, userConnected);\n//  clusterController.removeCommandListener(USER_DISCONNECTED_CMD, userDisconnected);\n//  clusterController.removeCommandListener(USER_PRESENCE_CMD, userPresence);\n//  clusterController.removeCommandListener(REQUEST_SYNCONLINE_CMD, requestSyncOnline);\n//  clusterController.removeCommandListener(RESPOND_SYNCONLINE_CMD, respondSyncOnline);\n//  clusterController.setCommandListener(USER_CONNECTED_CMD, userConnected);\n//  clusterController.setCommandListener(USER_DISCONNECTED_CMD, userDisconnected);\n//  clusterController.setCommandListener(USER_PRESENCE_CMD, userPresence);\n//  clusterController.setCommandListener(REQUEST_SYNCONLINE_CMD, requestSyncOnline);\n//  clusterController.setCommandListener(RESPOND_SYNCONLINE_CMD, respondSyncOnline);\n\t}\n\n\t@FillRoutedEvent\n\tprotected boolean fillRoutedUserSessionWithProcessorResultWriter(UserSessionEventWithProcessorResultWriter event) {\n\t\tevent.setPacketWriter(this::addOutPackets);\n\t\treturn true;\n\t}\n\n\t@FillRoutedEvent\n\tprotected boolean fillRoutedUserSessionEvent(UserSessionEvent event) {\n\t\tXMPPSession session = getSession(event.getUserJid().getBareJID());\n\t\tif (session != null && (event.getUserJid().getResource() == null ||\n\t\t\t\tsession.getResourceForResource(event.getUserJid().getResource()) != null)) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"for event {0} setting session to {1}\", new Object[]{event, session});\n\t\t\t}\n\t\t\tevent.setSession(session);\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@RouteEvent\n\tprotected Collection<Subscription> routeUserSessionEvent(UserSessionEvent event,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Collection<Subscription> subscriptions) {\n\t\tif (strategy.hasCompleteJidsInfo()) {\n\t\t\tSet<ConnectionRecordIfc> records = strategy.getConnectionRecords(event.getUserJid().getBareJID());\n\t\t\tif (records == null) {\n\t\t\t\trecords = Collections.emptySet();\n\t\t\t}\n\t\t\tif (event.getUserJid().getResource() != null) {\n\t\t\t\tIterator<ConnectionRecordIfc> it = records.iterator();\n\t\t\t\twhile (it.hasNext()) {\n\t\t\t\t\tif (!it.next().getUserJid().equals(event.getUserJid())) {\n\t\t\t\t\t\tit.remove();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tIterator<Subscription> it = subscriptions.iterator();\n\t\t\twhile (it.hasNext()) {\n\t\t\t\tSubscription s = it.next();\n\t\t\t\tif (!s.isInClusterSubscription()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tboolean remove = true;\n\n\t\t\t\tfor (ConnectionRecordIfc rec : records) {\n\t\t\t\t\tif (rec.getNode().getDomain().equals(s.getJid().getDomain())) {\n\t\t\t\t\t\tremove = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (remove) {\n\t\t\t\t\tit.remove();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn subscriptions;\n\t}\n\n\t/**\n\t * The method intercept user's disconnect event. On user disconnect the method takes a list of cluster nodes from\n\t * the strategy and sends a notification to all those nodes about the event.\n\t *\n\t * @param conn {@link XMPPResourceConnection} to be closed\n\t * @param closeOnly whether to perform additional processing before closing\n\t *\n\t * @see SessionManager#closeSession\n\t */\n\t@Override\n\tprotected void closeSession(XMPPResourceConnection conn, boolean closeOnly) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Called for conn: {0}, closeOnly: {1}\", new Object[]{conn, closeOnly});\n\t\t}\n\n\t\t// Exception here should not normally happen, but if it does, then\n\t\t// consequences might be severe, let's catch it then\n\t\ttry {\n\t\t\tif (conn.isAuthorized() && conn.isResourceSet()) {\n\t\t\t\tBareJID userId = conn.getBareJID();\n\n\t\t\t\tstrategy.handleLocalUserLogout(userId, conn);\n\t\t\t}\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.WARNING, \"This should not happen, check it out!, \", ex);\n\t\t}\n\n\t\t// Exception here should not normally happen, but if it does, then\n\t\t// consequences might be severe, let's catch it then\n\t\ttry {\n\t\t\tsuper.closeSession(conn, closeOnly);\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.WARNING, \"This should not happen, check it out!, \", ex);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void xmppStreamMoved(XMPPResourceConnection conn, JID oldConnId, JID newConnId, String sendReponse) {\n\t\ttry {\n\t\t\tstrategy.handleLocalUserChangedConnId(conn.getBareJID(), conn, oldConnId, newConnId);\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.WARNING, \"This should not happen, check it out!, \", ex);\n\t\t}\n\t\tsuper.xmppStreamMoved(conn, oldConnId, newConnId, sendReponse);\n\t}\n\n\tprivate void sendAdminNotification(String node, STATUS stat) {\n\t\tString message = String.format(\"Cluster node %1$s %2$s: %3$s (%4$s)\", String.valueOf(node), stat.message,\n\t\t\t\t\t\t\t\t\t   String.valueOf(getDefHostName().getDomain()), LocalDateTime.now().toString());\n\n\t\tfinal JID from = vHostManager != null ? JID.jidInstance(vHostManager.getDefVHostItem()) : this.my_address;\n\t\tPacket p_msg = Message.getMessage(from, my_hostname, StanzaType.chat, message, null,\n\t\t\t\t\t\t\t\t\t\t  \"cluster_status_update\", newPacketId(null));\n\t\tsendToAdmins(p_msg);\n\n\t}\n\n\tprivate enum STATUS {\n\t\tCONNECTED(\"connected to\"),\n\t\tDISCONNECTED(\"disconnected from\");\n\n\t\tString message;\n\n\t\tSTATUS(String message) {\n\t\t\tthis.message = message;\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/cluster/VirtualComponent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster;\n\nimport tigase.conf.Configurable;\nimport tigase.disco.ServiceEntity;\nimport tigase.disco.ServiceIdentity;\nimport tigase.disco.XMPPService;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.ComponentInfo;\nimport tigase.server.DisableDisco;\nimport tigase.server.Packet;\nimport tigase.server.ServerComponent;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.vhosts.VHostListener;\nimport tigase.vhosts.VHostManagerIfc;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * The purpose of this component implementation is to forward packets to a target real component implementation in the\n * cluster installation. Let's say you have a cluster installation with full Tigase server installed on each node and\n * you also want to use a component which doesn't have clustered implementation yet. In such case you deploy the\n * component on one of the cluster nodes and put the virtual component on all other nodes. With proper configuration\n * they pretend to be the component returning a correct service disco information and forward all packets for this\n * component to a cluster node with real component running.\n * <br>\n * This is a very lightweight implementation which doesn't use much resources either memory or CPU.\n * <br>\n * It can work well for any kind of a component: MUC, PubSub, transport either native Tigase components or third-party\n * components connected via XEP-0114 - external protocol component.\n * <br>\n * Basic configuration parameters are actually the same as for a real component. You set a real component name as a name\n * for the virtual component and a vritual component class name to load. Let's say we want to deploy MUC component this\n * way. The MUC component is visible as <code>muc.domain.our</code> in our installation. Thus the name of the component\n * is: <code>muc</code>:\n * <br>\n * <pre>\n * --comp-name-1=muc\n * --comp-class-1=tigase.cluster.VirtualComponent\n * </pre>\n * <br>\n * This is pretty much all you need to load a virtual component. A few other options are needed to point to correct\n * destination addresses for forwarded packets and to set correct service discovery parameters:\n * <br>\n * <pre>\n * muc/redirect-to=muc@cluster-node-with-real-muc.domain.our\n * muc/disco-name=Multi User Chat\n * muc/disco-node=\n * muc/disco-type=text\n * muc/disco-category=conference\n * muc/disco-features=http://jabber.org/protocol/muc\n * muc/fixed-domain=example.com\n * </pre>\n * <br>\n * Above options set all possible parameters to setup virtual MUC component. Created: Dec 13, 2008 7:44:35 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\npublic class VirtualComponent\n\t\timplements ServerComponent, XMPPService, Configurable, DisableDisco, VHostListener {\n\n\t/**\n\t * Parameter to set service discovery item category name for the virtual component. Please refer to service\n\t * discovery documentation for a correct category or check what is returned by your real component instance.\n\t */\n\tpublic static final String DISCO_CATEGORY_PROP_KEY = \"disco-category\";\n\n\tpublic static final String DISCO_CATEGORY_PROP_VAL = \"conference\";\n\n\t/**\n\t * Comma separated list of features for the service discovery item represented by this virtual component. Please\n\t * check with the real component to obtain a correct list of features.\n\t */\n\tpublic static final String DISCO_FEATURES_PROP_KEY = \"disco-features\";\n\n\tpublic static final String DISCO_FEATURES_PROP_VAL = \"http://jabber.org/protocol/muc\";\n\n\t/**\n\t * Parameter to set service discovery item name for the virtual component instance. You should refer to service\n\t * discovery documentation for a proper name for your component.\n\t */\n\tpublic static final String DISCO_NAME_PROP_KEY = \"disco-name\";\n\n\tpublic static final String DISCO_NAME_PROP_VAL = \"Multi User Chat\";\n\n\t/**\n\t * Parameter to set service discovery node name. In most cases you should leave it empty unless you really know what\n\t * you are doing.\n\t */\n\tpublic static final String DISCO_NODE_PROP_KEY = \"disco-node\";\n\n\tpublic static final String DISCO_NODE_PROP_VAL = \"\";\n\n\t/**\n\t * Parameter to set service discovery item type for the virtual component. You should refer to a service discovery\n\t * documentation for a correct type for your component. Or, alternatively you can have a look what returns your real\n\t * component.\n\t */\n\tpublic static final String DISCO_TYPE_PROP_KEY = \"disco-type\";\n\n\t/** A default value for service discovery item type, which is 'text' */\n\tpublic static final String DISCO_TYPE_PROP_VAL = \"text\";\n\n\t/**\n\t * If set, then it is used as the component domain name part. This domains is displayed on the service discovery\n\t * information, instead of virtual host based on the user's query.\n\t */\n\tpublic static final String FIXED_DOMAIN_PROP_KEY = \"fixed-domain\";\n\n\t/**\n\t * Virtual component parameter setting packet redirect destination address.\n\t */\n\tpublic static final String REDIRECT_TO_PROP_KEY = \"redirect-to\";\n\n\tprivate static final Logger log = Logger.getLogger(\"tigase.cluster.VirtualComponent\");\n\n\tprotected VHostManagerIfc vHostManager = null;\n\tprivate ComponentInfo cmpInfo = null;\n\tprivate JID componentId = null;\n\t@ConfigField(desc = \"Component category for discovery\")\n\tprivate String discoCategory = null;\n\t@ConfigField(desc = \"Component features for discovery\")\n\tprivate String[] discoFeatures = null;\n\t@ConfigField(desc = \"Component name for discovery\")\n\tprivate String discoName = null;\n\t@ConfigField(desc = \"Component node for discovery\")\n\tprivate String discoNode = null;\n\t@ConfigField(desc = \"Component type for discovery\")\n\tprivate String discoType = null;\n\t@ConfigField(desc = \"Fixed domain\")\n\tprivate String fixedDomain = null;\n\t@ConfigField(desc = \"Component name\")\n\tprivate String name = null;\n\t@ConfigField(desc = \"Redirect to\")\n\tprivate JID redirectTo = null;\n\tprivate ServiceEntity serviceEntity = null;\n\n\t@Override\n\tpublic boolean handlesLocalDomains() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean handlesNameSubdomains() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean handlesNonLocalDomains() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void initializationCompleted() {\n\t}\n\n\t@Override\n\tpublic void processPacket(Packet packet, Queue<Packet> results) {\n\t\tif (redirectTo != null) {\n\t\t\tpacket.setPacketTo(redirectTo);\n\t\t\tresults.add(packet);\n\t\t} else {\n\t\t\tlog.log(Level.CONFIG, \"No redirectTo address, dropping packet: {0}\", packet);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void release() {\n\t}\n\n\t@Override\n\tpublic JID getComponentId() {\n\t\treturn componentId;\n\t}\n\n\t@Override\n\tpublic ComponentInfo getComponentInfo() {\n\t\tif (cmpInfo == null) {\n\t\t\tcmpInfo = new ComponentInfo(getName(), this.getClass());\n\t\t}\n\t\treturn cmpInfo;\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getDefaults(Map<String, Object> params) {\n\t\tMap<String, Object> defs = new LinkedHashMap<String, Object>();\n\n\t\tdefs.put(REDIRECT_TO_PROP_KEY, \"\");\n\t\tif (params.get(CLUSTER_NODES) != null) {\n\t\t\tString[] cl_nodes = ((String) params.get(CLUSTER_NODES)).split(\",\");\n\n\t\t\tfor (String node : cl_nodes) {\n\t\t\t\tif (!node.equals(DNSResolverFactory.getInstance().getDefaultHost())) {\n\t\t\t\t\tdefs.put(REDIRECT_TO_PROP_KEY, BareJID.toString(getName(), node));\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tdefs.put(DISCO_NAME_PROP_KEY, DISCO_NAME_PROP_VAL);\n\t\tdefs.put(DISCO_NODE_PROP_KEY, DISCO_NODE_PROP_VAL);\n\t\tdefs.put(DISCO_TYPE_PROP_KEY, DISCO_TYPE_PROP_VAL);\n\t\tdefs.put(DISCO_CATEGORY_PROP_KEY, DISCO_CATEGORY_PROP_VAL);\n\t\tdefs.put(DISCO_FEATURES_PROP_KEY, DISCO_FEATURES_PROP_VAL);\n\t\tdefs.put(FIXED_DOMAIN_PROP_KEY, null);\n\n\t\treturn defs;\n\t}\n\n\t@Override\n\tpublic List<Element> getDiscoFeatures(JID from) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Element getDiscoInfo(String node, JID jid, JID from) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic List<Element> getDiscoItems(String node, JID jid, JID from) {\n\t\tString domain = jid.toString();\n\n\t\tif (fixedDomain != null) {\n\t\t\tdomain = fixedDomain;\n\t\t}\n\n\t\tElement result = serviceEntity.getDiscoItem(null, getName() + \".\" + domain);\n\n\t\treturn Arrays.asList(result);\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t\tthis.componentId = JID.jidInstanceNS(name, DNSResolverFactory.getInstance().getDefaultHost(), null);\n\t}\n\n\t@Override\n\tpublic boolean isInitializationComplete() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void setProperties(Map<String, Object> properties) {\n\t\tfixedDomain = (String) properties.get(FIXED_DOMAIN_PROP_KEY);\n\t\tif (fixedDomain != null) {\n\t\t\tthis.componentId = JID.jidInstanceNS(null, name + \".\" + fixedDomain, null);\n\t\t}\n\n\t\tString redirect = (String) properties.get(REDIRECT_TO_PROP_KEY);\n\n\t\tif (redirect != null) {\n\t\t\tif (redirect.isEmpty()) {\n\t\t\t\tredirectTo = null;\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tredirectTo = JID.jidInstance(redirect);\n\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\tredirectTo = null;\n\t\t\t\t\tlog.log(Level.WARNING, \"stringprep processing failed for given redirect address: {0}\", redirect);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (properties.get(DISCO_NAME_PROP_KEY) != null) {\n\t\t\tdiscoName = (String) properties.get(DISCO_NAME_PROP_KEY);\n\t\t}\n\t\tif (properties.get(DISCO_NODE_PROP_KEY) != null) {\n\t\t\tdiscoNode = (String) properties.get(DISCO_NODE_PROP_KEY);\n\t\t\tif (discoNode.isEmpty()) {\n\t\t\t\tdiscoNode = null;\n\t\t\t}\n\t\t}\n\t\tif (properties.get(DISCO_CATEGORY_PROP_KEY) != null) {\n\t\t\tdiscoCategory = (String) properties.get(DISCO_CATEGORY_PROP_KEY);\n\t\t}\n\t\tif (properties.get(DISCO_TYPE_PROP_KEY) != null) {\n\t\t\tdiscoType = (String) properties.get(DISCO_TYPE_PROP_KEY);\n\t\t}\n\t\tif (properties.get(DISCO_TYPE_PROP_KEY) != null) {\n\t\t\tdiscoFeatures = ((String) properties.get(DISCO_TYPE_PROP_KEY)).split(\",\");\n\t\t}\n\t\tif ((discoName != null) && (discoCategory != null) && (discoType != null) && (discoFeatures != null)) {\n\t\t\tserviceEntity = new ServiceEntity(getName(), null, discoName);\n\t\t\tserviceEntity.addIdentities(new ServiceIdentity(discoCategory, discoType, discoName));\n\t\t\tfor (String feature : discoFeatures) {\n\t\t\t\tserviceEntity.addFeatures(feature);\n\t\t\t}\n\t\t}\n\t\tcmpInfo = new ComponentInfo(getName(), this.getClass());\n\t}\n\n\t@Override\n\tpublic void setVHostManager(VHostManagerIfc manager) {\n\t\tthis.vHostManager = manager;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/cluster/WebSocketClientConnectionClustered.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster;\n\nimport tigase.cluster.api.ClusteredComponentIfc;\nimport tigase.eventbus.EventListener;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.selector.ClusterModeRequired;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.ServiceChecker;\nimport tigase.server.websocket.WebSocketClientConnectionManager;\nimport tigase.server.xmppclient.SeeOtherHostIfc;\nimport tigase.util.common.TimerTask;\nimport tigase.xmpp.XMPPIOService;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.List;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Describe class WebSocketClientConnectionClustered here.\n * <br>\n * Created: Sat Jun 21 22:23:18 2008\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\n@Bean(name = \"ws2s\", parent = Kernel.class, active = true)\n@ConfigType({ConfigTypeEnum.DefaultMode, ConfigTypeEnum.ConnectionManagersMode})\n@ClusterModeRequired(active = true)\npublic class WebSocketClientConnectionClustered\n\t\textends WebSocketClientConnectionManager\n\t\timplements ClusteredComponentIfc {\n\n\tprivate static final Logger log = Logger.getLogger(WebSocketClientConnectionClustered.class.getName());\n\n\tprivate EventListener<ClusterConnectionManager.ClusterInitializedEvent> clusterEventHandler = null;\n\tprivate SeeOtherHostIfc see_other_host_strategy = null;\n\n\tpublic WebSocketClientConnectionClustered() {\n\t\tdelayPortListening = true;\n\t}\n\n\t@Override\n\tpublic void onNodeDisconnected(JID jid) {\n\t\tsuper.onNodeDisconnected(jid);\n\n\t\tList<JID> connectedNodes = getNodesConnectedWithLocal();\n\t\tif (see_other_host_strategy != null) {\n\t\t\tsee_other_host_strategy.setNodes(connectedNodes);\n\t\t}\n\n\t\t// }\n\t\tfinal String hostname = jid.getDomain();\n\n\t\tdoForAllServices(new ServiceChecker<XMPPIOService<Object>>() {\n\t\t\t@Override\n\t\t\tpublic void check(XMPPIOService<Object> service) {\n\t\t\t\tJID dataReceiver = service.getDataReceiver();\n\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Checking service for dataReceiver: {0}\", dataReceiver);\n\t\t\t\t}\n\t\t\t\tif ((dataReceiver != null) && dataReceiver.getDomain().equals(hostname)) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.finest(\"Stopping service because corresponding cluster node stopped.\");\n\t\t\t\t\t}\n\t\t\t\t\tservice.stop();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tpublic String getDiscoDescription() {\n\t\treturn super.getDiscoDescription() + \" clustered\";\n\t}\n\n\t@Override\n\tpublic SeeOtherHostIfc getSeeOtherHostInstance(String see_other_host_class) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Configuring see_other_host clustered strategy for: \" + see_other_host_class);\n\t\t}\n\t\tif (see_other_host_class == null) {\n\t\t\tsee_other_host_class = SeeOtherHostIfc.CM_SEE_OTHER_HOST_CLASS_PROP_DEF_VAL_CLUSTER;\n\t\t}\n\t\tsee_other_host_strategy = super.getSeeOtherHostInstance(see_other_host_class);\n\t\tif (see_other_host_strategy != null) {\n\t\t\tsee_other_host_strategy.setNodes(getNodesConnectedWithLocal());\n\t\t}\n\n\t\treturn see_other_host_strategy;\n\t}\n\n\t@Override\n\tpublic void start() {\n\t\tsuper.start();\n\n\t\tif (clusterEventHandler == null) {\n\t\t\tclusterEventHandler = (ClusterConnectionManager.ClusterInitializedEvent event) -> {\n\t\t\t\tWebSocketClientConnectionClustered.this.connectWaitingTasks();\n\t\t\t\tlog.log(Level.INFO, \"Starting listening on ports of component: {0}\",\n\t\t\t\t\t\tWebSocketClientConnectionClustered.this.getName());\n\t\t\t\teventBus.removeListener(clusterEventHandler);\n\t\t\t};\n\t\t}\n\n\t\teventBus.addListener(ClusterConnectionManager.ClusterInitializedEvent.class, clusterEventHandler);\n\n\t\tif (delayPortListening) {\n\t\t\taddTimerTask(new TimerTask() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tlog.log(Level.FINE,\n\t\t\t\t\t\t\t\"Cluster synchronization timed-out, starting pending connections for \" + getName());\n\t\t\t\t\tWebSocketClientConnectionClustered.this.connectWaitingTasks();\n\t\t\t\t}\n\t\t\t}, connectionDelay * 30);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void stop() {\n\t\tsuper.stop();\n\t\teventBus.removeListener(clusterEventHandler);\n\t\tclusterEventHandler = null;\n\t}\n\n\t@Override\n\tprotected void onNodeConnected(JID jid) {\n\t\tsuper.onNodeConnected(jid);\n\n\t\tList<JID> connectedNodes = getNodesConnectedWithLocal();\n\t\tif (see_other_host_strategy != null) {\n\t\t\tsee_other_host_strategy.setNodes(connectedNodes);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/cluster/api/ClusterCommandException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster.api;\n\n/**\n * @author Artur Hefczyc Created Mar 16, 2011\n */\npublic class ClusterCommandException\n\t\textends Exception {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic ClusterCommandException() {\n\t\tsuper();\n\t}\n\n\tpublic ClusterCommandException(String arg0) {\n\t\tsuper(arg0);\n\t}\n\n\tpublic ClusterCommandException(Throwable arg0) {\n\t\tsuper(arg0);\n\t}\n\n\tpublic ClusterCommandException(String arg0, Throwable arg1) {\n\t\tsuper(arg0, arg1);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/cluster/api/ClusterConnectionHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster.api;\n\nimport tigase.server.Packet;\n\n/**\n * ClusterConnectionHandler interface used by ClusterConnectionSelectorIfc implementations to separate implementation\n * from ClusterConnectionManager\n *\n * @author andrzej\n */\npublic interface ClusterConnectionHandler {\n\n\t/**\n\t * Generates hashCode for particular packet used to spread processing between thread or connections\n\t */\n\tint hashCodeForPacket(Packet packet);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/cluster/api/ClusterConnectionSelectorIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster.api;\n\nimport tigase.cluster.ClusterConnection;\nimport tigase.server.Packet;\nimport tigase.xmpp.XMPPIOService;\n\nimport java.util.Map;\n\n/**\n * Interface ClusterConnectionSelectorIfc is base interface for classes responsible for selecting connection which\n * should be used to send packet between cluster nodes\n *\n * @author andrzej\n */\npublic interface ClusterConnectionSelectorIfc {\n\n\t/**\n\t * Method returns XMPPIOService instances which should be used to send packet between cluster nodes\n\t */\n\tXMPPIOService<Object> selectConnection(Packet packet, ClusterConnection conn);\n\n\tvoid setClusterConnectionHandler(ClusterConnectionHandler handler);\n\n\tvoid setProperties(Map<String, Object> props);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/cluster/api/ClusterControllerIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster.api;\n\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.Set;\n\n/**\n * @author Artur Hefczyc Created Mar 16, 2011\n */\npublic interface ClusterControllerIfc {\n\n\tpublic static final String DELIVER_CLUSTER_PACKET_CMD = \"deliver-cluster-packet-cmd\";\n\n\t/**\n\t * Method handles cluster packet received from cluster connection.\n\t *\n\t * @param packet which should be handled\n\t */\n\tvoid handleClusterPacket(Element packet);\n\n\t/**\n\t * Method is called on cluster node connection event. This is a notification to the component that a new cluster\n\t * node has connected.\n\t *\n\t * @param addr is a hostname of a cluster node generating the event.\n\t */\n\tvoid nodeConnected(String addr);\n\n\t/**\n\t * Method is called on cluster node disconnection event. This is a notification to the component that there was\n\t * network connection lost to one of the cluster nodes.\n\t *\n\t * @param addr is a hostname of a cluster node generating the event.\n\t */\n\tvoid nodeDisconnected(String addr);\n\n\tvoid removeCommandListener(CommandListener listener);\n\n\t/**\n\t * Method which sends command to desired nodes\n\t *\n\t * @param command ID string of the command\n\t * @param data additional data to be included in the packet\n\t * @param packets collection of elements to be send to desired nodes\n\t * @param fromNode address of the source node\n\t * @param visitedNodes list of all already visited nodes\n\t * @param toNodes list of nodes to which packet should be sent\n\t */\n\tvoid sendToNodes(String command, Map<String, String> data, Queue<Element> packets, JID fromNode,\n\t\t\t\t\t Set<JID> visitedNodes, JID... toNodes);\n\n\t/**\n\t * Method which sends command to desired nodes\n\t *\n\t * @param command ID string of the command\n\t * @param packets collection of elements to be send to desired nodes\n\t * @param fromNode address of the source node\n\t * @param visitedNodes list of all already visited nodes\n\t * @param toNodes list of nodes to which packet should be sent\n\t */\n\tvoid sendToNodes(String command, Queue<Element> packets, JID fromNode, Set<JID> visitedNodes, JID... toNodes);\n\n\t/**\n\t * Method which sends command to desired nodes\n\t *\n\t * @param command ID string of the command\n\t * @param data additional data to be included in the packet\n\t * @param fromNode address of the source node\n\t * @param visitedNodes list of all already visited nodes\n\t * @param toNodes list of nodes to which packet should be sent\n\t */\n\tvoid sendToNodes(String command, Map<String, String> data, JID fromNode, Set<JID> visitedNodes, JID... toNodes);\n\n\t/**\n\t * Method which sends command to desired nodes\n\t *\n\t * @param command ID string of the command\n\t * @param data additional data to be included in the packet\n\t * @param fromNode address of the source node\n\t * @param toNodes list of nodes to which packet should be sent\n\t */\n\tvoid sendToNodes(String command, Map<String, String> data, JID fromNode, JID... toNodes);\n\n\t/**\n\t * Method which sends command to desired nodes\n\t *\n\t * @param command ID string of the command\n\t * @param fromNode address of the source node\n\t * @param toNodes list of nodes to which packet should be sent\n\t */\n\tvoid sendToNodes(String command, JID fromNode, JID... toNodes);\n\n\t/**\n\t * Method which sends command to desired nodes\n\t *\n\t * @param command ID string of the command\n\t * @param packet collection of elements to be send to desired nodes\n\t * @param fromNode address of the source node\n\t * @param visitedNodes list of all already visited nodes\n\t * @param toNodes list of nodes to which packet should be sent\n\t */\n\tvoid sendToNodes(String command, Element packet, JID fromNode, Set<JID> visitedNodes, JID... toNodes);\n\n\t/**\n\t * Method which sends command to desired nodes\n\t *\n\t * @param command ID string of the command\n\t * @param data additional data to be included in the packet\n\t * @param packet element to be send to desired nodes\n\t * @param fromNode address of the source node\n\t * @param visitedNodes list of all already visited nodes\n\t * @param toNodes list of nodes to which packet should be sent\n\t */\n\tvoid sendToNodes(String command, Map<String, String> data, Element packet, JID fromNode, Set<JID> visitedNodes,\n\t\t\t\t\t JID... toNodes);\n\n\tvoid setCommandListener(CommandListener listener);\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/cluster/api/ClusterElement.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster.api;\n\nimport tigase.server.Packet;\nimport tigase.server.Priority;\nimport tigase.xml.Element;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Class ClusterElement is a utility class for handling tigase cluster specific packets. The cluster packet has the\n * following form:\n * <br>\n * {@code <cluster xmlns=\"tigase:cluster\" from=\"source\" to=\"dest\" type=\"set\"> <data> <message xmlns=\"jabber:client\"\n * from=\"source-u\" to=\"dest-x\" type=\"chat\"> <body>Hello world!</body> </message> </data> <control> <first-node>node1 JID\n * address</first-node> <visited-nodes> <node-id>node1 JID address</node-id> <node-id>node2 JID address</node-id>\n * </visited-nodes> <method-call name=\"method name\"> <par name=\"param1 name\">value</par> <par name=\"param2\n * name\">value</par> <results> <val name=\"val1 name\">value</var> <val name=\"val2 name\">value</var> </results>\n * </method-call> </control> </cluster> }\n * <br>\n * If none of nodes could process the packet it goes back to the first node as this node is the most likely to process\n * the packet correctly.\n * <br>\n * Created: Fri May 2 09:40:40 2008\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class ClusterElement {\n\n\tpublic static final String CLUSTER_CONTROL_EL_NAME = \"control\";\n\n\tpublic static final String CLUSTER_DATA_EL_NAME = \"data\";\n\n\tpublic static final String CLUSTER_EL_NAME = \"cluster\";\n\n\tpublic static final String CLUSTER_METHOD_EL_NAME = \"method-call\";\n\n\tpublic static final String CLUSTER_METHOD_PAR_EL_NAME = \"par\";\n\n\tpublic static final String CLUSTER_METHOD_RESULTS_EL_NAME = \"results\";\n\n\tpublic static final String CLUSTER_METHOD_RESULTS_VAL_EL_NAME = \"val\";\n\n\tpublic static final String CLUSTER_NAME_ATTR = \"name\";\n\n\tpublic static final String FIRST_NODE_EL_NAME = \"first-node\";\n\n\tpublic static final String NODE_ID_EL_NAME = \"node-id\";\n\n\tpublic static final String VISITED_NODES_EL_NAME = \"visited-nodes\";\n\n\tpublic static final String XMLNS = \"tigase:cluster\";\n\tpublic static final String[] VISITED_NODES_PATH = {CLUSTER_EL_NAME, CLUSTER_CONTROL_EL_NAME, VISITED_NODES_EL_NAME};\n\tpublic static final String[] FIRST_NODE_PATH = {CLUSTER_EL_NAME, CLUSTER_CONTROL_EL_NAME, FIRST_NODE_EL_NAME};\n\n\t// public static final String PACKET_FROM_ATTR_NAME = \"packet-from\";\n\tpublic static final String[] CLUSTER_METHOD_RESULTS_PATH = {CLUSTER_EL_NAME, CLUSTER_CONTROL_EL_NAME,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCLUSTER_METHOD_EL_NAME, CLUSTER_METHOD_RESULTS_EL_NAME};\n\tpublic static final String[] CLUSTER_METHOD_PATH = {CLUSTER_EL_NAME, CLUSTER_CONTROL_EL_NAME,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tCLUSTER_METHOD_EL_NAME};\n\tpublic static final String[] CLUSTER_DATA_PATH = {CLUSTER_EL_NAME, CLUSTER_DATA_EL_NAME};\n\tpublic static final String[] CLUSTER_CONTROL_PATH = {CLUSTER_EL_NAME, CLUSTER_CONTROL_EL_NAME};\n\tprivate static final Logger log = Logger.getLogger(\"tigase.cluster.ClusterElement\");\n\n\tprivate Element elem = null;\n\tprivate JID first_node = null;\n\tprivate String method_name = null;\n\tprivate Map<String, String> method_params = null;\n\tprivate Map<String, String> method_results = null;\n\tprivate Queue<Element> packets = null;\n\tprivate Priority priority = null;\n\tprivate Set<JID> visited_nodes = null;\n\n\tpublic static Element clusterElement(JID from, JID to, StanzaType type) {\n\t\tElement cluster_el = new Element(CLUSTER_EL_NAME, new String[]{\"from\", \"to\", \"type\"},\n\t\t\t\t\t\t\t\t\t\t new String[]{from.toString(), to.toString(), type.toString()});\n\n\t\tcluster_el.setXMLNS(XMLNS);\n\t\tcluster_el.addChild(\n\t\t\t\tnew Element(CLUSTER_CONTROL_EL_NAME, new Element[]{new Element(VISITED_NODES_EL_NAME)}, null, null));\n\n\t\treturn cluster_el;\n\t}\n\n\tpublic static Element createClusterElement(JID from, JID to, StanzaType type, String packet_from) {\n\t\tElement cluster_el = clusterElement(from, to, type);\n\n\t\tcluster_el.addChild(new Element(CLUSTER_DATA_EL_NAME));\n\n\t\t// new String[] {PACKET_FROM_ATTR_NAME}, new String[] {packet_from}));\n\t\treturn cluster_el;\n\t}\n\n\tpublic static ClusterElement createClusterMethodCall(JID from, JID to, StanzaType type, String method_name,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t Map<String, String> params) {\n\t\tElement cluster_el = clusterElement(from, to, type);\n\t\tElement method_call = new Element(CLUSTER_METHOD_EL_NAME, new String[]{CLUSTER_NAME_ATTR},\n\t\t\t\t\t\t\t\t\t\t  new String[]{method_name});\n\n\t\tif (params != null) {\n\t\t\tfor (Map.Entry<String, String> entry : params.entrySet()) {\n\t\t\t\tmethod_call.addChild(\n\t\t\t\t\t\tnew Element(CLUSTER_METHOD_PAR_EL_NAME, entry.getValue(), new String[]{CLUSTER_NAME_ATTR},\n\t\t\t\t\t\t\t\t\tnew String[]{entry.getKey()}));\n\t\t\t}\n\t\t}\n\t\tcluster_el.findChildStaticStr(CLUSTER_CONTROL_PATH).addChild(method_call);\n\n\t\tClusterElement result_cl = new ClusterElement(cluster_el);\n\n\t\tresult_cl.addVisitedNode(from);\n\n\t\treturn result_cl;\n\t}\n\n\tpublic static ClusterElement createForNextNode(ClusterElement clel, List<JID> cluster_nodes, JID comp_id) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Calculating a next node from nodes: \" +\n\t\t\t\t\t\t\t   ((cluster_nodes != null) ? cluster_nodes.toString() : \"null\"));\n\t\t}\n\t\tif ((cluster_nodes != null) && (cluster_nodes.size() > 0)) {\n\t\t\tJID next_node = null;\n\n\t\t\tfor (JID cluster_node : cluster_nodes) {\n\t\t\t\tif (!clel.isVisitedNode(cluster_node) && !cluster_node.equals(comp_id)) {\n\t\t\t\t\tnext_node = cluster_node;\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.finest(\"Found next cluster node: \" + next_node);\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (next_node != null) {\n\t\t\t\tClusterElement result = clel.nextClusterNode(next_node);\n\n\t\t\t\tresult.addVisitedNode(comp_id);\n\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Creates a new <code>ClusterElement</code> instance.\n\t */\n\tpublic ClusterElement(Element elem) {\n\t\tthis.elem = elem;\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Parsing cluster element: \" + elem.toString());\n\t\t}\n\n\t\tList<Element> children = elem.getChildrenStaticStr(CLUSTER_DATA_PATH);\n\n\t\tif ((children != null) && (children.size() > 0)) {\n\t\t\tpackets = new ArrayDeque<Element>(children);\n\t\t}\n\n\t\tString fNode = elem.getCDataStaticStr(FIRST_NODE_PATH);\n\n\t\tif (fNode != null) {\n\t\t\tfirst_node = JID.jidInstanceNS(fNode);\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"First node found: \" + first_node);\n\t\t}\n\t\tvisited_nodes = new LinkedHashSet<JID>();\n\n\t\tList<Element> nodes = elem.getChildrenStaticStr(VISITED_NODES_PATH);\n\n\t\tif (nodes != null) {\n\t\t\tint cnt = 0;\n\n\t\t\tfor (Element node : nodes) {\n\t\t\t\tvisited_nodes.add(JID.jidInstanceNS(node.getCData()));\n\t\t\t\t++cnt;\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Found and added visited nodes: \" + cnt);\n\t\t\t}\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"No visited nodes found\");\n\t\t\t}\n\t\t}\n\n\t\tString priorityStr = elem.getAttributeStaticStr(Packet.PRIORITY_ATT);\n\t\tif (priorityStr != null) {\n\t\t\tpriority = Priority.valueOf(priorityStr);\n\t\t}\n\n\t\tElement method_call = elem.findChildStaticStr(CLUSTER_METHOD_PATH);\n\n\t\tif (method_call != null) {\n\t\t\tparseMethodCall(method_call);\n\t\t}\n\t}\n\n\tpublic ClusterElement(JID from, JID to, StanzaType type, Packet packet) {\n\t\tif (packet != null) {\n\t\t\tpackets = new ArrayDeque<Element>();\n\t\t\tvisited_nodes = new LinkedHashSet<JID>();\n\t\t\telem = createClusterElement(from, to, type, packet.getFrom().toString());\n\t\t\tif (packet.getElement().getXMLNS() == null) {\n\t\t\t\tpacket.getElement().setXMLNS(\"jabber:client\");\n\t\t\t}\n\t\t\taddDataPacket(packet);\n\t\t}\n\t}\n\n\tpublic void addDataPacket(Packet packet) {\n\t\taddDataPacket(packet.getElement());\n\t}\n\n\tpublic void addDataPacket(Element packet) {\n\t\tif (packets == null) {\n\t\t\tpackets = new ArrayDeque<Element>();\n\t\t}\n\t\tpackets.offer(packet);\n\t\tif (elem.findChildStaticStr(CLUSTER_DATA_PATH) == null) {\n\t\t\telem.addChild(new Element(CLUSTER_DATA_EL_NAME));\n\t\t}\n\t\telem.findChildStaticStr(CLUSTER_DATA_PATH).addChild(packet);\n\t}\n\n\tpublic void addDataPackets(Queue<Element> packets) {\n\t\tif (packets != null) {\n\t\t\tfor (Element elem : packets) {\n\t\t\t\taddDataPacket(elem);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tpublic void addMethodResult(String key, String val) {\n\t\tElement res = elem.findChildStaticStr(CLUSTER_METHOD_RESULTS_PATH);\n\n\t\tif (res == null) {\n\t\t\tres = new Element(CLUSTER_METHOD_RESULTS_EL_NAME);\n\t\t\telem.findChildStaticStr(CLUSTER_METHOD_PATH).addChild(res);\n\t\t}\n\t\tres.addChild(new Element(CLUSTER_METHOD_RESULTS_VAL_EL_NAME, val, new String[]{CLUSTER_NAME_ATTR},\n\t\t\t\t\t\t\t\t new String[]{key}));\n\t\tif (method_results == null) {\n\t\t\tmethod_results = new LinkedHashMap<String, String>();\n\t\t}\n\t\tmethod_results.put(key, val);\n\t}\n\n\tpublic void addVisitedNode(JID node_id) {\n\t\tif (visited_nodes.size() == 0) {\n\t\t\tfirst_node = node_id;\n\t\t\telem.findChildStaticStr(CLUSTER_CONTROL_PATH).addChild(new Element(FIRST_NODE_EL_NAME, node_id.toString()));\n\t\t}\n\t\tif (visited_nodes.add(node_id)) {\n\t\t\telem.findChildStaticStr(VISITED_NODES_PATH).addChild(new Element(NODE_ID_EL_NAME, node_id.toString()));\n\t\t}\n\t}\n\n\tpublic void addVisitedNodes(Set<JID> nodes) {\n\t\tif (nodes != null) {\n\t\t\tfor (JID node : nodes) {\n\t\t\t\taddVisitedNode(node);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic ClusterElement createMethodResponse(JID from, StanzaType type, Map<String, String> results) {\n\t\treturn createMethodResponse(from, null, type, results);\n\t}\n\n\tpublic ClusterElement createMethodResponse(JID from, JID to, StanzaType type, Map<String, String> results) {\n\t\tElement result_el = elem.clone();\n\n\t\tresult_el.setAttribute(\"from\", from.toString());\n\t\tresult_el.setAttribute(\"to\", ((to != null) ? to.toString() : first_node.toString()));\n\t\tresult_el.setAttribute(\"type\", type.name());\n\n\t\tElement res = new Element(CLUSTER_METHOD_RESULTS_EL_NAME);\n\n\t\tresult_el.findChildStaticStr(CLUSTER_METHOD_PATH).addChild(res);\n\n\t\tClusterElement result_cl = new ClusterElement(result_el);\n\n\t\tif (results != null) {\n\t\t\tfor (Map.Entry<String, String> entry : results.entrySet()) {\n\t\t\t\tresult_cl.addMethodResult(entry.getKey(), entry.getValue());\n\t\t\t}\n\t\t}\n\n\t\treturn result_cl;\n\t}\n\n\tpublic Map<String, String> getAllMethodParams() {\n\t\treturn method_params;\n\t}\n\n\tpublic Map<String, String> getAllMethodResults() {\n\t\treturn method_results;\n\t}\n\n\tpublic Element getClusterElement(String id) {\n\t\telem.setAttribute(\"id\", id);\n\n\t\treturn elem;\n\t}\n\n\tpublic Queue<Element> getDataPackets() {\n\t\treturn packets;\n\t}\n\n\tpublic JID getFirstNode() {\n\t\treturn first_node;\n\t}\n\n\tpublic String getMethodName() {\n\t\treturn method_name;\n\t}\n\n\tpublic String getMethodParam(String par_name) {\n\t\treturn (method_params == null) ? null : method_params.get(par_name);\n\t}\n\n\tpublic long getMethodParam(String par_name, long def) {\n\t\tString val_str = getMethodParam(par_name);\n\n\t\tif (val_str == null) {\n\t\t\treturn def;\n\t\t} else {\n\t\t\ttry {\n\t\t\t\treturn Long.parseLong(val_str);\n\t\t\t} catch (NumberFormatException e) {\n\t\t\t\treturn def;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic String getMethodResultVal(String val_name) {\n\t\treturn (method_results == null) ? null : method_results.get(val_name);\n\t}\n\n\tpublic long getMethodResultVal(String val_name, long def) {\n\t\tString val_str = getMethodResultVal(val_name);\n\n\t\tif (val_str == null) {\n\t\t\treturn def;\n\t\t} else {\n\t\t\ttry {\n\t\t\t\treturn Long.parseLong(val_str);\n\t\t\t} catch (NumberFormatException e) {\n\t\t\t\treturn def;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic Priority getPriority() {\n\t\treturn priority;\n\t}\n\n\tpublic void setPriority(Priority priority) {\n\t\tthis.priority = priority;\n\t\tthis.elem.setAttribute(Packet.PRIORITY_ATT, priority.name());\n\t}\n\n\tpublic Set<JID> getVisitedNodes() {\n\t\treturn visited_nodes;\n\t}\n\n\tpublic boolean isVisitedNode(JID node_id) {\n\t\treturn visited_nodes.contains(node_id);\n\t}\n\n\tpublic ClusterElement nextClusterNode(JID node_id) {\n\t\tElement next_el = elem.clone();\n\t\tString from = elem.getAttributeStaticStr(Packet.TO_ATT);\n\n\t\tnext_el.setAttribute(\"from\", from);\n\t\tnext_el.setAttribute(\"to\", node_id.toString());\n\n\t\t// next_el.setAttribute(\"type\", StanzaType.set.toString());\n\t\tClusterElement next_cl = new ClusterElement(next_el);\n\n\t\treturn next_cl;\n\t}\n\n\tprotected void parseMethodCall(Element method_call) {\n\t\tmethod_name = method_call.getAttributeStaticStr(CLUSTER_NAME_ATTR);\n\t\tif (method_name != null) {\n\t\t\tmethod_name = method_name.intern();\n\t\t}\n\t\tmethod_params = new LinkedHashMap<String, String>();\n\n\t\tList<Element> children = method_call.getChildren();\n\n\t\tif (children != null) {\n\t\t\tfor (Element child : children) {\n\t\t\t\tif (child.getName() == CLUSTER_METHOD_PAR_EL_NAME) {\n\t\t\t\t\tString par_name = child.getAttributeStaticStr(CLUSTER_NAME_ATTR);\n\n\t\t\t\t\tmethod_params.put(par_name, child.getCData());\n\t\t\t\t}\n\t\t\t\tif (child.getName() == CLUSTER_METHOD_RESULTS_EL_NAME) {\n\t\t\t\t\tif (method_results == null) {\n\t\t\t\t\t\tmethod_results = new LinkedHashMap<String, String>();\n\t\t\t\t\t}\n\n\t\t\t\t\tList<Element> res_children = child.getChildren();\n\n\t\t\t\t\tif (res_children != null) {\n\t\t\t\t\t\tfor (Element res_child : res_children) {\n\t\t\t\t\t\t\tif (res_child.getName() == CLUSTER_METHOD_RESULTS_VAL_EL_NAME) {\n\t\t\t\t\t\t\t\tString val_name = res_child.getAttributeStaticStr(CLUSTER_NAME_ATTR);\n\n\t\t\t\t\t\t\t\tmethod_results.put(val_name, res_child.getCData());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/cluster/api/ClusteredComponentIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster.api;\n\nimport tigase.server.ServerComponent;\n\n/**\n * Describe interface ClusteredComponent here.\n * <br>\n * Created: Mon Jun  9 20:00:46 2008\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface ClusteredComponentIfc\n\t\textends ServerComponent {\n\n\t/**\n\t * Set's the configures the cluster controller object for cluster communication and API.\n\t *\n\t * @param cl_controller cluster controller object used for cluster communication\n\t */\n\tvoid setClusterController(ClusterControllerIfc cl_controller);\n\n\t/**\n\t * Method is called on cluster node connection event. This is a notification to the component that a new cluster\n\t * node has connected.\n\t *\n\t * @param node is a hostname of a cluster node generating the event.\n\t */\n\tvoid nodeConnected(String node);\n\n\t/**\n\t * Method is called on cluster node disconnection event. This is a notification to the component that there was\n\t * network connection lost to one of the cluster nodes.\n\t *\n\t * @param node is a hostname of a cluster node generating the event.\n\t */\n\tvoid nodeDisconnected(String node);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/cluster/api/CommandListener.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster.api;\n\nimport tigase.server.Priority;\nimport tigase.stats.StatisticsList;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.Set;\n\n/**\n * @author Artur Hefczyc Created Mar 16, 2011\n */\npublic interface CommandListener\n\t\textends Comparable<CommandListener> {\n\n\t/**\n\t * Method is responsible for executing commands from other nodes and appropriate processing\n\t *\n\t * @param fromNode address of the node from which command was received\n\t * @param visitedNodes collection of already visited nodes\n\t * @param data additional data associated with the command in addition to the main {@link Element}\n\t * @param packets collection of {@link Element} commands to be executed\n\t *\n\t * @throws ClusterCommandException execution exception\n\t */\n\tvoid executeCommand(JID fromNode, Set<JID> visitedNodes, Map<String, String> data, Queue<Element> packets)\n\t\t\tthrows ClusterCommandException;\n\n\t/**\n\t * Method allows retrieval name of the particular command\n\t *\n\t * @return a value of <code>String</code> name of the command\n\t */\n\tString getName();\n\n\t/**\n\t * Method allows setting name of the command\n\t *\n\t * @param name to be used\n\t */\n\tvoid setName(String name);\n\n\t/**\n\t * Method returns priority of particular command which should be used to assign proper priority for processing of\n\t * this command\n\t */\n\tPriority getPriority();\n\n\t/**\n\t * Method allows retrieval possible statistics for particular command\n\t *\n\t * @param list collection to which statistics should be appended\n\t */\n\tvoid getStatistics(StatisticsList list);\n}\n"
  },
  {
    "path": "src/main/java/tigase/cluster/api/CommandListenerAbstract.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n* To change this template, choose Tools | Templates\n* and open the template in the editor.\n */\npackage tigase.cluster.api;\n\nimport tigase.server.Priority;\nimport tigase.stats.StatisticsList;\n\n/**\n * @author kobit\n */\npublic abstract class CommandListenerAbstract\n\t\timplements CommandListener {\n\n\tprivate static long syncInTraffic = 0;\n\tprivate static long syncOutTraffic = 0;\n\n\tprivate String commandName;\n\tprivate Priority priority;\n\n\tpublic static long getSyncInTraffic() {\n\t\treturn syncInTraffic;\n\t}\n\n\tpublic static long getSyncOutTraffic() {\n\t\treturn syncOutTraffic;\n\t}\n\n\tpublic CommandListenerAbstract(String name, Priority priority) {\n\t\tsetName(name);\n\t\tsetPriority(priority);\n\t}\n\n\t@Override\n\tpublic int compareTo(CommandListener cl) {\n\t\treturn commandName.compareTo(cl.getName());\n\t}\n\n\t@Override\n\tpublic boolean equals(Object cl) {\n\t\treturn ((cl != null) && (cl instanceof CommandListener) &&\n\t\t\t\tcommandName.equals(((CommandListener) cl).getName()));\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint hash = 265;\n\n\t\thash = hash + ((this.commandName != null) ? this.commandName.hashCode() : 0);\n\n\t\treturn hash;\n\t}\n\n\tpublic synchronized void incSyncInTraffic() {\n\t\t++syncInTraffic;\n\t}\n\n\tpublic synchronized void incSyncOutTraffic() {\n\t\t++syncOutTraffic;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn commandName;\n\t}\n\n\t@Override\n\tpublic final void setName(String name) {\n\t\tcommandName = name;\n\t}\n\n\t@Override\n\tpublic Priority getPriority() {\n\t\treturn priority;\n\t}\n\n\tpublic void setPriority(Priority priority) {\n\t\tthis.priority = priority;\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/cluster/api/SessionManagerClusteredIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n* To change this template, choose Tools | Templates\n* and open the template in the editor.\n */\npackage tigase.cluster.api;\n\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManagerHandler;\nimport tigase.xml.Element;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.XMPPSession;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.List;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * @author kobit\n */\npublic interface SessionManagerClusteredIfc\n\t\textends SessionManagerHandler {\n\n\tpublic static final String SESSION_FOUND_KEY = \"user-session-found-key\";\n\n\tboolean fastAddOutPacket(Packet packet);\n\n\tvoid processPacket(Packet el_packet, XMPPResourceConnection conn);\n\n\tvoid processPresenceUpdate(XMPPSession session, Element element);\n\n\tXMPPResourceConnection getXMPPResourceConnection(Packet el_packet);\n\n\tConcurrentHashMap<JID, XMPPResourceConnection> getXMPPResourceConnections();\n\n\tConcurrentHashMap<BareJID, XMPPSession> getXMPPSessions();\n\n\t/**\n\t * Method to check if there is XMPPResourceConnection instance for connection JID.\n\t *\n\t * @return true - if there is XMPPResourceConnection for connection JID\n\t */\n\tboolean hasXMPPResourceConnectionForConnectionJid(JID connJid);\n\n\tJID getComponentId();\n\n\tList<JID> getNodesConnected();\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/cluster/repo/ClConConfigRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster.repo;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.cluster.ClusterConnectionManager;\nimport tigase.db.DBInitException;\nimport tigase.db.comp.ConfigRepository;\nimport tigase.eventbus.EventBus;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.kernel.beans.config.ConfigAlias;\nimport tigase.kernel.beans.config.ConfigAliases;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.sys.ShutdownHook;\nimport tigase.sys.TigaseRuntime;\nimport tigase.util.dns.DNSResolverFactory;\n\nimport java.util.Date;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n * @version 5.2.0, 13/03/09\n */\n@ConfigAliases({@ConfigAlias(field = \"items\", alias = \"cluster-nodes\")})\npublic class ClConConfigRepository\n\t\textends ConfigRepository<ClusterRepoItem>\n\t\timplements ShutdownHook, Initializable, UnregisterAware {\n\n\tprivate static final Logger log = Logger.getLogger(ClConConfigRepository.class.getName());\n\n\t@ConfigField(desc = \"Automatically remove obsolote items\", alias = \"repo-auto-remove-obsolete-items\")\n\tprotected boolean auto_remove_obsolete_items = true;\n\tprotected boolean firstLoadDone = false;\n\tprotected long lastReloadTime = 0;\n\tprotected long lastReloadTimeFactor = 10;\n\t@Inject\n\tprivate EventBus eventBus;\n\n\tpublic ClConConfigRepository() {\n\t\tautoReloadInterval = 15;\n\n\t\tif (getItem(DNSResolverFactory.getInstance().getDefaultHost()) == null) {\n\t\t\tClusterRepoItem item = getItemInstance();\n\n\t\t\titem.initFromPropertyString(DNSResolverFactory.getInstance().getDefaultHost());\n\t\t\taddItem(item);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void destroy() {\n\t\t// Nothing to do\n\t}\n\n\t@Override\n\tpublic String[] getDefaultPropetyItems() {\n\t\treturn ClConRepoDefaults.getDefaultPropetyItems();\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"Cluster repository clean-up\";\n\t}\n\n\t@Override\n\tpublic String getPropertyKey() {\n\t\treturn ClConRepoDefaults.getPropertyKey();\n\t}\n\n\t@Override\n\tpublic String getConfigKey() {\n\t\treturn ClConRepoDefaults.getConfigKey();\n\t}\n\n\t@Override\n\tpublic ClusterRepoItem getItemInstance() {\n\t\treturn ClConRepoDefaults.getItemInstance();\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\t@Override\n\tpublic void initRepository(String resource_uri, Map<String, String> params) throws DBInitException {\n\t\t// Nothing to do\n\t}\n\n\t@Override\n\tpublic void reload() {\n\t\tsuper.reload();\n\n\t\tString host = DNSResolverFactory.getInstance().getDefaultHost();\n\n\t\t// we check if we already realoded repo from repository and have all items (own item will have\n\t\t// correct update time), if so we set flag that first load was made and if there was only one item\n\t\t// we send even that cluster was initiated\n\t\tif (!firstLoadDone) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\"First Cluster repository reload done: {0}, items size: {1}, last updated own item: {2}\",\n\t\t\t\t\t\tnew Object[]{firstLoadDone, items.size(), items.get(host).getLastUpdate()});\n\t\t\t}\n\n\t\t\tif (items.get(host) != null && items.get(host).getLastUpdate() > 0) {\n\t\t\t\tfirstLoadDone = true;\n\n\t\t\t\tif (items.size() == 1) {\n\t\t\t\t\teventBus.fire(new ClusterConnectionManager.ClusterInitializedEvent());\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\n\t\tClusterRepoItem item = getItem(host);\n\t\ttry {\n\t\t\titem = (item != null) ? (ClusterRepoItem) (item.clone()) : null;\n\t\t} catch (CloneNotSupportedException ex) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.SEVERE, \"Cloning of ClusterRepoItem has failed\", ex);\n\t\t\t}\n\t\t}\n\n\t\tif (item == null) {\n\t\t\titem = getItemInstance();\n\t\t\titem.setHostname(host);\n\t\t}\n\t\titem.setSecondaryHostname(DNSResolverFactory.getInstance().getSecondaryHost());\n\t\titem.setLastUpdate(System.currentTimeMillis());\n\t\titem.setCpuUsage(TigaseRuntime.getTigaseRuntime().getCPUUsage());\n\t\titem.setMemUsage(TigaseRuntime.getTigaseRuntime().getHeapMemUsage());\n\t\tstoreItem(item);\n\n\t\tif (auto_remove_obsolete_items) {\n\t\t\t// we remove item after 6 * autoreload * 1000, because it may happen that we loaded an item\n\t\t\t// which would be removed on the next execution but it could be updated in the mean while\n\t\t\tremoveObsoloteItems(6000);\n\t\t}\n\n\t}\n\n\tpublic void itemLoaded(ClusterRepoItem item) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Item loaded: {0}\", item);\n\t\t}\n\t\tif (System.currentTimeMillis() - item.getLastUpdate() <= 5000 * autoReloadInterval &&\n\t\t\t\tclusterRecordValid(item)) {\n\t\t\taddItem(item);\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\"Removing stale item: {0}; current time: {1}, last update: {2} ({3}), diff: {4}, autoreload {5}\",\n\t\t\t\t\t\tnew Object[]{item, System.currentTimeMillis(), item.getLastUpdate(),\n\t\t\t\t\t\t\t\t\t new Date(item.getLastUpdate()), System.currentTimeMillis() - item.getLastUpdate(),\n\t\t\t\t\t\t\t\t\t 5000 * autoReloadInterval});\n\t\t\t}\n\t\t\tif (auto_remove_obsolete_items) {\n\t\t\t\tremoveItem(item.getHostname());\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean itemChanged(ClusterRepoItem oldItem, ClusterRepoItem newItem) {\n\t\treturn !oldItem.getPassword().equals(newItem.getPassword()) || (oldItem.getPortNo() != newItem.getPortNo()) ||\n\t\t\t\t!Objects.equals(oldItem.getSecondaryHostname(), newItem.getSecondaryHostname());\n\t}\n\n\t@Override\n\tpublic String shutdown() {\n\t\tString host = DNSResolverFactory.getInstance().getDefaultHost();\n\t\tremoveItem(host);\n\t\treturn \"== \" + \"Removing cluster_nodes item: \" + host + \"\\n\";\n\t}\n\n\tpublic void storeItem(ClusterRepoItem item) {\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tsuper.initialize();\n\t\tTigaseRuntime.getTigaseRuntime().addShutdownHook(this);\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\tTigaseRuntime.getTigaseRuntime().removeShutdownHook(this);\n\t\tsuper.beforeUnregister();\n\t}\n\n\tprotected void removeObsoloteItems(long factor) {\n\t\tIterator<ClusterRepoItem> iterator = iterator();\n\t\twhile (iterator.hasNext()) {\n\t\t\tClusterRepoItem next = iterator.next();\n\t\t\tif ((next.getLastUpdate() > 0) &&\n\t\t\t\t\tSystem.currentTimeMillis() - next.getLastUpdate() > factor * autoReloadInterval) {\n\t\t\t\tremoveItem(next.getHostname());\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate boolean clusterRecordValid(ClusterRepoItem item) {\n\n\t\t// we ignore faulty addresses\n\t\tboolean isCorrect = !item.getHostname().equalsIgnoreCase(\"localhost\");\n\n\t\tif (!isCorrect && log.isLoggable(Level.FINE)) {\n\t\t\tlog.log(Level.FINE, \"Incorrect entry in cluster table, skipping: {0}\", item);\n\t\t}\n\t\treturn isCorrect;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/cluster/repo/ClConDirRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n* To change this template, choose Tools | Templates\n* and open the template in the editor.\n */\npackage tigase.cluster.repo;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.DBInitException;\nimport tigase.db.Repository;\n\nimport java.io.*;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * @author kobit\n */\n@Repository.Meta(supportedUris = {\"file://.*\"})\npublic class ClConDirRepository\n\t\textends ClConConfigRepository\n\t\timplements ClusterRepoConstants {\n\n\tpublic static final String REPO_FILE_EXTENSION = \".rep\";\n\n\tpublic static final String REPO_URI_DB_DEF_VAL = \"etc/\";\n\n\tprivate static final Logger log = Logger.getLogger(ClConDirRepository.class.getName());\n\n\tprivate DirFilter dirFilter = new DirFilter();\n\tprivate File repo_dir = new File(REPO_URI_DB_DEF_VAL);\n\n\t@Override\n\tpublic void destroy() {\n\t\t// Nothing to do here\n\t\tsuper.destroy();\n\t}\n\n\t@Override\n\tpublic void getDefaults(Map<String, Object> defs, Map<String, Object> params) {\n\t\tsuper.getDefaults(defs, params);\n\t\tdefs.put(REPO_URI_PROP_KEY, REPO_URI_DB_DEF_VAL);\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic void initRepository(String conn_str, Map<String, String> params) throws DBInitException {\n\t\t// Nothing to do here\n\t\tsuper.initRepository(conn_str, params);\n\t}\n\n\t@Override\n\tpublic void setProperties(Map<String, Object> properties) {\n\t\tsuper.setProperties(properties);\n\t\trepo_dir = new File((String) properties.get(REPO_URI_PROP_KEY));\n\t}\n\n\t@Override\n\tpublic void storeItem(ClusterRepoItem item) {\n\t\ttry {\n\t\t\tFile file = new File(repo_dir, item.getHostname() + REPO_FILE_EXTENSION);\n\t\t\tBufferedWriter bw = new BufferedWriter(new FileWriter(file, false));\n\n\t\t\tbw.write(item.toPropertyString());\n\t\t\tbw.newLine();\n\t\t\tbw.close();\n\t\t} catch (IOException e) {\n\t\t\tlog.log(Level.WARNING, \"Problem getting elements from DB: \", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void reload() {\n\t\tsuper.reload();\n\t\ttry {\n\t\t\tFile[] files = repo_dir.listFiles(dirFilter);\n\n\t\t\tfor (File file : files) {\n\t\t\t\tBufferedReader br = new BufferedReader(new FileReader(file));\n\t\t\t\tString data = br.readLine();\n\n\t\t\t\tbr.close();\n\n\t\t\t\tClusterRepoItem item = getItemInstance();\n\n\t\t\t\titem.initFromPropertyString(data);\n\t\t\t\titemLoaded(item);\n\t\t\t}\n\t\t} catch (IOException e) {\n\t\t\tlog.log(Level.WARNING, \"Problem getting elements from DB: \", e);\n\t\t}\n\t\tif (auto_remove_obsolete_items) {\n\t\t\tremoveObsoloteItems(5000);\n\t\t}\n\t}\n\n\tprivate class DirFilter\n\t\t\timplements FileFilter {\n\n\t\t@Override\n\t\tpublic boolean accept(File file) {\n\t\t\treturn file.isFile() && file.getName().endsWith(REPO_FILE_EXTENSION);\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/cluster/repo/ClConRepoDefaults.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster.repo;\n\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.xmpp.jid.BareJID;\n\n/**\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n * @version 5.2.0, 13/03/09\n */\npublic abstract class ClConRepoDefaults {\n\n\tprivate static final BareJID clcon_user = BareJID.bareJIDInstanceNS(\"cl-conn-manager\");\n\n\tstatic String getConfigKey() {\n\t\treturn \"cluster-nodes\";\n\t}\n\n\tstatic String[] getDefaultPropetyItems() {\n\t\treturn new String[]{DNSResolverFactory.getInstance().getDefaultHost()};\n\t}\n\n\tstatic ClusterRepoItem getItemInstance() {\n\t\treturn new ClusterRepoItem();\n\t}\n\n\tstatic String getPropertyKey() {\n\t\treturn \"--cluster-nodes\";\n\t}\n\n\tpublic static BareJID getRepoUser() {\n\t\treturn clcon_user;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/cluster/repo/ClConSQLRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster.repo;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.*;\nimport tigase.db.comp.ComponentRepositoryDataSourceAware;\nimport tigase.db.util.JDBCPasswordObfuscator;\nimport tigase.db.util.RepositoryVersionAware;\nimport tigase.sys.TigaseRuntime;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Timestamp;\nimport java.util.Date;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n * @version 5.2.0, 13/03/09\n */\n@Repository.Meta(supportedUris = {\"jdbc:[^:]+:.*\"})\n@Repository.SchemaId(id = Schema.SERVER_SCHEMA_ID, name = Schema.SERVER_SCHEMA_NAME)\npublic class ClConSQLRepository\n\t\textends ClConConfigRepository\n\t\timplements ClusterRepoConstants, ComponentRepositoryDataSourceAware<ClusterRepoItem, DataRepository>,\n\t\t           RepositoryVersionAware {\n\n\tprivate static final Logger log = Logger.getLogger(ClConSQLRepository.class.getName());\n\tprivate static final BareJID repoUser =  BareJID.bareJIDInstanceNS(ClConRepoDefaults.getConfigKey());\n\n\t//J-\n\t/* @formatter:off */\n\tprivate static final String GET_ITEM_QUERY =\n\t\t\t\"select \" + HOSTNAME_COLUMN + \", \" + SECONDARY_HOSTNAME_COLUMN + \", \" + PASSWORD_COLUMN + \", \" +\n\t\t\t\t\tLASTUPDATE_COLUMN + \", \" + PORT_COLUMN + \", \" + CPU_USAGE_COLUMN + \", \" + MEM_USAGE_COLUMN +\n\t\t\t\t\t\" from \" + TABLE_NAME + \" where \" + HOSTNAME_COLUMN + \" = ?\";\n\tprivate static final String GET_ALL_ITEMS_QUERY =\n\t\t\t\"select \" + HOSTNAME_COLUMN + \", \" + SECONDARY_HOSTNAME_COLUMN + \", \" + PASSWORD_COLUMN + \", \" +\n\t\t\t\t\tLASTUPDATE_COLUMN + \", \" + PORT_COLUMN + \", \" + CPU_USAGE_COLUMN + \", \" + MEM_USAGE_COLUMN +\n\t\t\t\t\t\" from \" + TABLE_NAME;\n\tprivate static final String DELETE_ITEM_QUERY =\n\t\t\t\"delete from \" + TABLE_NAME + \" where (\" + HOSTNAME_COLUMN + \" = ?)\";\n\tprivate static final String INSERT_ITEM_QUERY =\n\t\t\t\"insert into \" + TABLE_NAME + \" (\" + HOSTNAME_COLUMN + \", \" + SECONDARY_HOSTNAME_COLUMN + \", \" +\n\t\t\t\t\tPASSWORD_COLUMN + \", \" + LASTUPDATE_COLUMN + \", \" + PORT_COLUMN + \", \" + CPU_USAGE_COLUMN + \", \" +\n\t\t\t\t\tMEM_USAGE_COLUMN + \") \" + \" (select ?, ?, ?, ?, ?, ?, ? from \" + TABLE_NAME + \" WHERE \" +\n\t\t\t\t\tHOSTNAME_COLUMN + \"=? HAVING count(*)=0)\";\n\tprivate static final String UPDATE_ITEM_QUERY =\n\t\t\t\"update \" + TABLE_NAME + \" set \" + HOSTNAME_COLUMN + \"= ?, \" + SECONDARY_HOSTNAME_COLUMN + \"= ?, \" +\n\t\t\t\t\tPASSWORD_COLUMN + \"= ?, \" + LASTUPDATE_COLUMN + \" = ?,\" + PORT_COLUMN + \"= ?, \" + CPU_USAGE_COLUMN +\n\t\t\t\t\t\"= ?, \" + MEM_USAGE_COLUMN + \"= ? \" + \" where \" + HOSTNAME_COLUMN + \"= ?\";\n\t/* @formatter:on */\n\t//J+\n\n\tprivate DataRepository data_repo = null;\n\n\t@Override\n\tpublic void destroy() {\n\t\t// This implementation of ClConConfigRepository is using shared connection\n\t\t// pool to database which is cached by RepositoryFactory and maybe be used\n\t\t// in other places, so we can not destroy it.\n\t\tsuper.destroy();\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\t@Override\n\tpublic void getDefaults(Map<String, Object> defs, Map<String, Object> params) {\n\t\tsuper.getDefaults(defs, params);\n\n\t\tString repo_uri = RepositoryFactory.DERBY_REPO_URL_PROP_VAL;\n\n\t\tif (params.get(RepositoryFactory.GEN_USER_DB_URI) != null) {\n\t\t\trepo_uri = (String) params.get(RepositoryFactory.GEN_USER_DB_URI);\n\t\t}\n\t\tdefs.put(REPO_URI_PROP_KEY, repo_uri);\n\t}\n\n\t@Override\n\tpublic void setDataSource(DataRepository data_repo) {\n\t\ttry {\n\t\t\tcheckDB(data_repo);\n\n\t\t\tdata_repo.initPreparedStatement(GET_ITEM_QUERY, GET_ITEM_QUERY);\n\t\t\tdata_repo.initPreparedStatement(GET_ALL_ITEMS_QUERY, GET_ALL_ITEMS_QUERY);\n\t\t\tdata_repo.initPreparedStatement(INSERT_ITEM_QUERY, INSERT_ITEM_QUERY);\n\t\t\tdata_repo.initPreparedStatement(UPDATE_ITEM_QUERY, UPDATE_ITEM_QUERY);\n\t\t\tdata_repo.initPreparedStatement(DELETE_ITEM_QUERY, DELETE_ITEM_QUERY);\n\t\t\tthis.data_repo = data_repo;\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Problem initializing database: \", e);\n\t\t}\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\t@Override\n\tpublic void initRepository(String conn_str, Map<String, String> params) throws DBInitException {\n\t\tsuper.initRepository(conn_str, params);\n\t\ttry {\n\t\t\tdata_repo = RepositoryFactory.getDataRepository(null, conn_str, params);\n\t\t\tdata_repo.checkSchemaVersion(this, true);\n\t\t\tsetDataSource(data_repo);\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Problem initializing database: \", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void removeItem(String key) {\n\t\tsuper.removeItem(key);\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Removing item form database: {0}\", key);\n\t\t}\n\t\ttry {\n\t\t\tPreparedStatement removeItem = data_repo.getPreparedStatement(repoUser, DELETE_ITEM_QUERY);\n\t\t\tsynchronized (removeItem) {\n\t\t\t\tremoveItem.setString(1, key);\n\t\t\t\tremoveItem.executeUpdate();\n\t\t\t}\n\n\t\t} catch (SQLException e) {\n\t\t\tlog.log(Level.WARNING, \"Problem removing element: \" + key + \" from DB: \", e);\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void storeItem(ClusterRepoItem item) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Storing item to repository: {0}\", item);\n\t\t}\n\t\ttry {\n\t\t\tPreparedStatement updateItemSt = data_repo.getPreparedStatement(repoUser, UPDATE_ITEM_QUERY);\n\t\t\tPreparedStatement insertItemSt = data_repo.getPreparedStatement(repoUser, INSERT_ITEM_QUERY);\n\n\t\t\t// relatively most DB compliant UPSERT\n\t\t\tDate date = new Date();\n\n\t\t\tsynchronized (updateItemSt) {\n\t\t\t\tupdateItemSt.setString(1, item.getHostname());\n\t\t\t\tupdateItemSt.setString(2, item.getSecondaryHostname());\n\t\t\t\tupdateItemSt.setString(3, item.getPassword());\n\t\t\t\tdata_repo.setTimestamp(updateItemSt, 4, new Timestamp(date.getTime()));\n\t\t\t\tupdateItemSt.setInt(5, item.getPortNo());\n\t\t\t\tupdateItemSt.setFloat(6, item.getCpuUsage());\n\t\t\t\tupdateItemSt.setFloat(7, item.getMemUsage());\n\t\t\t\tupdateItemSt.setString(8, item.getHostname());\n\t\t\t\tupdateItemSt.executeUpdate();\n\t\t\t}\n\n\t\t\tsynchronized (insertItemSt) {\n\t\t\t\tinsertItemSt.setString(1, item.getHostname());\n\t\t\t\tinsertItemSt.setString(2, item.getSecondaryHostname());\n\t\t\t\tinsertItemSt.setString(3, item.getPassword());\n\t\t\t\tdata_repo.setTimestamp(insertItemSt, 4, new Timestamp(date.getTime()));\n\t\t\t\tinsertItemSt.setInt(5, item.getPortNo());\n\t\t\t\tinsertItemSt.setFloat(6, item.getCpuUsage());\n\t\t\t\tinsertItemSt.setFloat(7, item.getMemUsage());\n\t\t\t\tinsertItemSt.setString(8, item.getHostname());\n\t\t\t\tinsertItemSt.executeUpdate();\n\t\t\t}\n\n\t\t} catch (SQLException e) {\n\t\t\tlog.log(Level.WARNING, \"Problem storing element to DB: \" + item, e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void reload() {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Reloading items\");\n\t\t}\n\n\t\tif ((System.currentTimeMillis() - lastReloadTime) <= (autoReloadInterval * lastReloadTimeFactor)) {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"Last reload performed in {0}, skipping: \",\n\t\t\t\t\t\t(System.currentTimeMillis() - lastReloadTime));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tlastReloadTime = System.currentTimeMillis();\n\n\t\tsuper.reload();\n\n\t\ttry {\n\t\t\tResultSet rs = null;\n\t\t\tPreparedStatement getAllItemsSt = data_repo.getPreparedStatement(repoUser, GET_ALL_ITEMS_QUERY);\n\n\t\t\tsynchronized (getAllItemsSt) {\n\t\t\t\ttry {\n\t\t\t\t\trs = getAllItemsSt.executeQuery();\n\t\t\t\t\twhile (rs.next()) {\n\t\t\t\t\t\tClusterRepoItem item = getItemInstance();\n\n\t\t\t\t\t\titem.setHostname(rs.getString(HOSTNAME_COLUMN));\n\t\t\t\t\t\titem.setSecondaryHostname(rs.getString(SECONDARY_HOSTNAME_COLUMN));\n\t\t\t\t\t\titem.setPassword(rs.getString(PASSWORD_COLUMN));\n\t\t\t\t\t\titem.setLastUpdate(data_repo.getTimestamp(rs, LASTUPDATE_COLUMN).getTime());\n\t\t\t\t\t\titem.setPort(rs.getInt(PORT_COLUMN));\n\t\t\t\t\t\titem.setCpuUsage(rs.getFloat(CPU_USAGE_COLUMN));\n\t\t\t\t\t\titem.setMemUsage(rs.getFloat(MEM_USAGE_COLUMN));\n\t\t\t\t\t\titemLoaded(item);\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tlog.log(Level.WARNING, \"Problem getting elements from DB: \", e);\n\n\t\t\t//if there is a SQLException we should return to avoid triggering `removeObsoloteItems()` method as\n\t\t\t// possibly not items have been properly reloaded and some may have stale update-date information.\n\t\t\treturn;\n\t\t}\n\t\t// make sure we remove items which are gone from the database after timeout (those have last update not updated)\n\t\t// and are not removed from in-memory cache by above query\n\t\tif (auto_remove_obsolete_items) {\n\t\t\tremoveObsoloteItems(5000);\n\t\t}\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\t@Override\n\tpublic void setProperties(Map<String, Object> properties) {\n\t\tsuper.setProperties(properties);\n\t}\n\n\t@Override\n\tpublic void store() {\n\n\t\t// Do nothing everything is written on demand to DB\n\t}\n\n\t/**\n\t * Performs database check, creates missing schema if necessary\n\t *\n\t */\n\tprivate void checkDB(DataRepository data_repo) throws SQLException {\n\t\tif (!data_repo.checkTable(TABLE_NAME)) {\n\t\t\tlog.log(Level.INFO, \"DB for external component is not OK, stopping server...\");\n\n\t\t\tTigaseRuntime.getTigaseRuntime()\n\t\t\t\t\t.shutdownTigase(new String[]{\"ERROR! Terminating the server process.\",\n\t\t\t\t\t\t\t\t\t\t\t\t \"Problem initializing the server: missing tig_cluster_nodes table on \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t JDBCPasswordObfuscator.obfuscatePassword(data_repo.getResourceUri()),\n\t\t\t\t\t\t\t\t\t\t\t\t \"Please fix the problem and start the server again.\"}, 1);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/cluster/repo/ClusterRepoConstants.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster.repo;\n\npublic interface ClusterRepoConstants {\n\n\tpublic static final String CPU_USAGE_COLUMN = \"cpu_usage\";\n\n\tpublic static final String HOSTNAME_COLUMN = \"hostname\";\n\n\tpublic static final String SECONDARY_HOSTNAME_COLUMN = \"secondary\";\n\n\tpublic static final String LASTUPDATE_COLUMN = \"last_update\";\n\n\tpublic static final String MEM_USAGE_COLUMN = \"mem_usage\";\n\n\tpublic static final String PASSWORD_COLUMN = \"password\";\n\n\tpublic static final String PORT_COLUMN = \"port\";\n\n\tpublic static final String REPO_URI_PROP_KEY = \"repo-uri\";\n\n\tpublic static final String TABLE_NAME = \"tig_cluster_nodes\";\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/cluster/repo/ClusterRepoItem.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster.repo;\n\nimport tigase.db.comp.RepositoryItemAbstract;\nimport tigase.server.Command;\nimport tigase.server.Packet;\nimport tigase.util.Algorithms;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.text.DateFormat;\nimport java.text.ParseException;\nimport java.util.Date;\nimport java.util.UUID;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class ClusterRepoItem\n\t\textends RepositoryItemAbstract\n\t\timplements Cloneable {\n\n\tpublic static final String CPU_USAGE_ATTR = \"cpu\";\n\n\tpublic static final String CPU_USAGE_LABEL = \"CPU usage\";\n\n\tpublic static final String HOSTNAME_ATTR = \"host\";\n\n\tpublic static final String SECONDARY_HOSTNAME_ATTR = \"secondary\";\n\n\tpublic static final String HOSTNAME_LABEL = \"Hostname\";\n\n\tpublic static final String SECONDARY_HOSTNAME_LABEL = \"SecondaryHostname\";\n\n\tpublic static final String LAST_UPDATE_ATTR = \"updated\";\n\n\tpublic static final String LAST_UPDATE_LABEL = \"Last update\";\n\n\tpublic static final String MEM_USAGE_ATTR = \"mem\";\n\n\tpublic static final String MEM_USAGE_LABEL = \"Memory usage\";\n\n\tpublic static final String PASSWORD_ATTR = \"passwd\";\n\n\tpublic static final String PASSWORD_LABEL = \"Password\";\n\n\tpublic static final String PASSWORD_PROP_VAL = \"someSecret\";\n\n\tpublic static final String PORT_NO_ATTR = \"port\";\n\n\tpublic static final String PORT_NO_LABEL = \"Port number\";\n\n\tpublic static final int PORT_NO_PROP_VAL = 5277;\n\n\tpublic static final String REPO_ITEM_ELEM_NAME = \"item\";\n\n\tprivate static final Logger log = Logger.getLogger(ClusterRepoItem.class.getName());\n\n\tprivate float cpuUsage = 0f;\n\tprivate String hostname = null;\n\tprivate long lastUpdate = 0l;\n\tprivate float memUsage = 0f;\n\tprivate String password = Algorithms.sha256(UUID.randomUUID().toString());\n\tprivate int portNo = PORT_NO_PROP_VAL;\n\tprivate String secondaryHostname = null;\n\n\t@Override\n\tpublic void addCommandFields(Packet packet) {\n\t\tCommand.addFieldValue(packet, HOSTNAME_LABEL, ((hostname != null) ? hostname : \"\"));\n\t\tCommand.addFieldValue(packet, SECONDARY_HOSTNAME_LABEL, ((secondaryHostname != null) ? secondaryHostname : \"\"));\n\t\tCommand.addFieldValue(packet, PASSWORD_LABEL, ((password != null) ? password : \"\"));\n\t\tCommand.addFieldValue(packet, PORT_NO_LABEL, ((portNo > 0) ? \"\" + portNo : \"\"));\n\t\tCommand.addFieldValue(packet, LAST_UPDATE_LABEL, \"\" + new Date(lastUpdate));\n\t\tCommand.addFieldValue(packet, CPU_USAGE_LABEL, \"\" + cpuUsage);\n\t\tCommand.addFieldValue(packet, MEM_USAGE_LABEL, \"\" + memUsage);\n\t\tsuper.addCommandFields(packet);\n\t}\n\n\t@Override\n\tpublic void initFromCommand(Packet packet) {\n\t\tsuper.initFromCommand(packet);\n\t\thostname = Command.getFieldValue(packet, HOSTNAME_LABEL);\n\t\tsecondaryHostname = Command.getFieldValue(packet, SECONDARY_HOSTNAME_LABEL);\n\t\tpassword = Command.getFieldValue(packet, PASSWORD_LABEL);\n\n\t\tString tmp = Command.getFieldValue(packet, LAST_UPDATE_LABEL);\n\n\t\tif ((tmp != null) && !tmp.isEmpty()) {\n\t\t\ttry {\n\t\t\t\tlastUpdate = DateFormat.getInstance().parse(tmp).getTime();\n\t\t\t} catch (ParseException ex) {\n\t\t\t\tlastUpdate = System.currentTimeMillis();\n\t\t\t}\n\t\t}\n\t\ttmp = Command.getFieldValue(packet, PORT_NO_LABEL);\n\t\tif ((tmp != null) && !tmp.isEmpty()) {\n\t\t\tportNo = parsePortNo(tmp);\n\t\t}\n\t\ttmp = Command.getFieldValue(packet, CPU_USAGE_LABEL);\n\t\tif ((tmp != null) && !tmp.isEmpty()) {\n\t\t\tcpuUsage = Float.parseFloat(tmp);\n\t\t}\n\t\ttmp = Command.getFieldValue(packet, MEM_USAGE_LABEL);\n\t\tif ((tmp != null) && !tmp.isEmpty()) {\n\t\t\tmemUsage = Float.parseFloat(tmp);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void initFromElement(Element elem) {\n\t\tif (elem.getName() != REPO_ITEM_ELEM_NAME) {\n\t\t\tthrow new IllegalArgumentException(\"Incorrect element name, expected: \" + REPO_ITEM_ELEM_NAME);\n\t\t}\n\t\tsuper.initFromElement(elem);\n\t\thostname = elem.getAttributeStaticStr(HOSTNAME_ATTR);\n\t\tsecondaryHostname = elem.getAttributeStaticStr(SECONDARY_HOSTNAME_ATTR);\n\t\tpassword = elem.getAttributeStaticStr(PASSWORD_ATTR);\n\t\tportNo = parsePortNo(elem.getAttributeStaticStr(PORT_NO_ATTR));\n\t\tlastUpdate = Long.parseLong(elem.getAttributeStaticStr(LAST_UPDATE_ATTR));\n\t\tcpuUsage = Float.parseFloat(elem.getAttributeStaticStr(CPU_USAGE_ATTR));\n\t\tmemUsage = Float.parseFloat(elem.getAttributeStaticStr(MEM_USAGE_ATTR));\n\t}\n\n\t@Override\n\tpublic void initFromPropertyString(String propString) {\n\t\tString[] props = propString.split(\":\");\n\n\t\tif (props.length > 0) {\n\t\t\thostname = BareJID.parseJID(props[0])[1];\n\t\t}\n\t\tif ((props.length > 1) && !props[1].trim().isEmpty()) {\n\t\t\tpassword = props[1];\n\t\t}\n\t\tif (props.length > 2) {\n\t\t\tportNo = parsePortNo(props[2]);\n\t\t}\n\t\tif (props.length > 3) {\n\t\t\tlastUpdate = Long.parseLong(props[3]);\n\t\t}\n\t\tif (props.length > 4) {\n\t\t\tcpuUsage = Float.parseFloat(props[4]);\n\t\t}\n\t\tif (props.length > 5) {\n\t\t\tmemUsage = Float.parseFloat(props[5]);\n\t\t}\n\t\tif (props.length > 6) {\n\t\t\tsecondaryHostname = BareJID.parseJID(props[6])[1];\n\t\t}\n\t}\n\n\t@Override\n\tpublic Element toElement() {\n\t\tElement elem = super.toElement();\n\n\t\telem.addAttribute(HOSTNAME_ATTR, hostname);\n\t\telem.addAttribute(SECONDARY_HOSTNAME_ATTR, secondaryHostname);\n\t\telem.addAttribute(PASSWORD_ATTR, password);\n\t\telem.addAttribute(PORT_NO_ATTR, \"\" + portNo);\n\t\telem.addAttribute(LAST_UPDATE_ATTR, \"\" + lastUpdate);\n\t\telem.addAttribute(CPU_USAGE_ATTR, \"\" + cpuUsage);\n\t\telem.addAttribute(MEM_USAGE_ATTR, \"\" + memUsage);\n\n\t\treturn elem;\n\t}\n\n\t@Override\n\tpublic String toPropertyString() {\n\t\treturn hostname + \":\" + password + \":\" + portNo + \":\" + lastUpdate + \":\" + cpuUsage + \":\" + memUsage + \":\" +\n\t\t\t\tsecondaryHostname;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn toPropertyString();\n\t}\n\n\tpublic float getCpuUsage() {\n\t\treturn cpuUsage;\n\t}\n\n\tprotected void setCpuUsage(float cpuUsage) {\n\t\tthis.cpuUsage = cpuUsage;\n\t}\n\n\t@Override\n\tpublic String getElemName() {\n\t\treturn REPO_ITEM_ELEM_NAME;\n\t}\n\n\tpublic String getHostname() {\n\t\treturn hostname;\n\t}\n\n\tprotected void setHostname(String hostname) {\n\t\tthis.hostname = hostname;\n\t}\n\n\t@Override\n\tpublic String getKey() {\n\t\treturn hostname;\n\t}\n\n\t@Override\n\tprotected void setKey(String key) {\n\t\tsetHostname(key);\n\t}\n\n\tpublic long getLastUpdate() {\n\t\treturn lastUpdate;\n\t}\n\n\tprotected void setLastUpdate(long update) {\n\t\tthis.lastUpdate = update;\n\t}\n\n\tpublic float getMemUsage() {\n\t\treturn memUsage;\n\t}\n\n\tprotected void setMemUsage(float memUsage) {\n\t\tthis.memUsage = memUsage;\n\t}\n\n\tpublic String getPassword() {\n\t\treturn password;\n\t}\n\n\tprotected void setPassword(String password) {\n\t\tthis.password = password;\n\t}\n\n\tpublic String getSecondaryHostname() {\n\t\treturn secondaryHostname;\n\t}\n\n\tprotected void setSecondaryHostname(String secondaryHostname) {\n\t\tthis.secondaryHostname = secondaryHostname;\n\t}\n\n\tpublic int getPortNo() {\n\t\treturn portNo;\n\t}\n\n\t@Override\n\tprotected Object clone() throws CloneNotSupportedException {\n\t\treturn super.clone();\n\t}\n\n\tprotected void setPort(int port) {\n\t\tthis.portNo = port;\n\t}\n\n\tprivate int parsePortNo(String input) {\n\t\tint result;\n\n\t\ttry {\n\t\t\tresult = Integer.parseInt(input);\n\t\t} catch (Exception e) {\n\t\t\tresult = 5277;\n\t\t\tlog.log(Level.WARNING, \"Incorrect port number, can''t parse: {0}\", input);\n\t\t}\n\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/cluster/repo/ClusterRepoItemEvent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster.repo;\n\nimport tigase.cluster.ClusterConnectionManager.REPO_ITEM_UPDATE_TYPE;\n\nimport java.io.Serializable;\n\n/**\n * @author andrzej\n */\npublic class ClusterRepoItemEvent {\n\n\tprivate final REPO_ITEM_UPDATE_TYPE action;\n\tprivate final ClusterRepoItem item;\n\n\tpublic ClusterRepoItemEvent(ClusterRepoItem item, REPO_ITEM_UPDATE_TYPE action) {\n\t\tthis.item = item;\n\t\tthis.action = action;\n\t}\n\n\tpublic REPO_ITEM_UPDATE_TYPE getAction() {\n\t\treturn action;\n\t}\n\n\tpublic ClusterRepoItem getItem() {\n\t\treturn item;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"item=\" + item + \", action=\" + action;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/cluster/strategy/ClusteringStrategyIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster.strategy;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.cluster.api.ClusterControllerIfc;\nimport tigase.server.Packet;\nimport tigase.stats.StatisticHolder;\nimport tigase.stats.StatisticsList;\nimport tigase.sys.OnlineJidsReporter;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * Created: May 2, 2009 4:36:03 PM\n *\n* @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface ClusteringStrategyIfc<E extends ConnectionRecordIfc>\n\t\textends OnlineJidsReporter, StatisticHolder {\n\n\tpublic void handleLocalPresenceSet(XMPPResourceConnection conn);\n\n\tpublic void handleLocalResourceBind(XMPPResourceConnection conn);\n\n\tpublic void handleLocalUserLogin(BareJID userId, XMPPResourceConnection conn);\n\n\tpublic void handleLocalUserLogout(BareJID userId, XMPPResourceConnection conn);\n\n\tpublic void handleLocalUserChangedConnId(BareJID userId, XMPPResourceConnection conn, JID oldConnId, JID newConnId);\n\n\tpublic String getInfo();\n\n\tvoid handleLocalPacket(Packet packet, XMPPResourceConnection conn);\n\n\t/**\n\t * This is a handler method which is called when a new node connects to the cluster.\n\t *\n\t * @param node is a cluster node id.\n\t */\n\tvoid nodeConnected(JID node);\n\n\t/**\n\t * This is a handler method which is called when a node disconnects from the cluster.\n\t *\n\t * @param node is a cluster node id.\n\t */\n\tvoid nodeDisconnected(JID node);\n\n\tboolean processPacket(Packet packet, XMPPResourceConnection conn);\n\n\t/**\n\t * The method returns all cluster nodes currently connected to the cluster node.\n\t *\n\t * @return List of all cluster nodes currently connected to the cluster node.\n\t */\n\tList<JID> getNodesConnected();\n\n\t/**\n\t * Returns a ConnectionRecord object associated with this user's full JID if it exists in the cache or null if it\n\t * does not. All parts of the user's JID are checked and ConnectionRecord is returned only if there is a match for\n\t * all parts.\n\t *\n\t * @param jid is an instance of the user's full JID.\n\t *\n\t * @return ConnectionRecord instance associated with given user's JID or null if there is no ConnectionRecord in the\n\t * cache.\n\t */\n\tE getConnectionRecord(JID jid);\n\n\tE getConnectionRecordInstance();\n\n\t/**\n\t * Returns a set with all ConnectionRecords found in the cache for a given user ID, that is BareJID. In other words\n\t * all user's resources/connectionIDs found in the cache associated with user's account.\n\t *\n\t * @param bareJID is an instance of the user's BareJID, that is account ID.\n\t *\n\t * @return a Set instance with all ConnectionRecords found for a given BareJID. Note, the result may be null or it\n\t * maybe an empty Set or non-empty set.\n\t */\n\tSet<E> getConnectionRecords(BareJID bareJID);\n\n\tdefault List<E> getConnectionRecordsByCreationTime(BareJID bareJID) {\n\t\treturn null;\n\t}\n\n\t/**\n\t * This method is used for configuration purpose. Following the convention used in the Tigase project this method is\n\t * supposed to provide configuration defaults. All parameters which exist in configuration file overwrite\n\t * corresponding default parameters. If some parameters are missing in configuration file defaults are used then.\n\t * <br>\n\t * A compiled set of parameters is then passed to <code>setProperties</code> method.\n\t *\n\t * @param params a <code>Map</code> with properties loaded from init.properties file which should be used for\n\t * generating defaults.\n\t *\n\t * @return a <code>Map</code> with all the class default configuration parameters.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tMap<String, Object> getDefaults(Map<String, Object> params);\n\n\t/**\n\t * Add the strategy statistics to the List.\n\t */\n\tvoid getStatistics(StatisticsList list);\n\n\tvoid setClusterController(ClusterControllerIfc clComp);\n\n\t/**\n\t * Method used to pass configuration parameters to the class. Parameters are stored in <code>Map</code> which\n\t * contains compiles set of defaults overwritten by parameters loaded from configuration file.\n\t * <br>\n\t * If he implementation took a good care of providing defaults for all parameters no parameter should be missing.\n\t *\n\t * @param props a <code>Map</code> with all configuration parameters for the class.\n\t */\n\tvoid setProperties(Map<String, Object> props);\n\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/cluster/strategy/ConnectionRecord.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster.strategy;\n\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.JID;\n\n/**\n * @author Artur Hefczyc Created Mar 15, 2011\n */\npublic class ConnectionRecord\n\t\timplements ConnectionRecordIfc, Comparable<ConnectionRecord> {\n\n\tprivate static final String CONNECTION_ID_ELEMENT = \"connection-id\";\n\tprivate static final String JID_ELEMENT = \"user-jid\";\n\tprivate static final String NODE_ELEMENT = \"node-jid\";\n\tprivate static final String SESSION_ID_ELEMENT = \"session-id\";\n\tprivate static final String TOP_ELEMENT = \"conn-rec\";\n\n\tprivate JID connectionId;\n\tprivate JID node;\n\tprivate String sessionId;\n\tprivate JID userJid;\n\n\n\tpublic ConnectionRecord() {\n\t\tsuper();\n\t}\n\n\t@Override\n\tpublic int compareTo(ConnectionRecord rec) {\n\t\treturn connectionId.compareTo(rec.connectionId);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object rec) {\n\t\tboolean result = false;\n\n\t\tif (rec instanceof ConnectionRecord) {\n\t\t\tresult = connectionId.equals(((ConnectionRecord) rec).connectionId);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn connectionId.hashCode();\n\t}\n\n\t@Override\n\tpublic Element toElement() {\n\t\tElement result = new Element(TOP_ELEMENT);\n\n\t\tresult.addChild(new Element(NODE_ELEMENT, node.toString()));\n\t\tresult.addChild(new Element(JID_ELEMENT, userJid.toString()));\n\t\tresult.addChild(new Element(CONNECTION_ID_ELEMENT, connectionId.toString()));\n\t\tresult.addChild(new Element(SESSION_ID_ELEMENT, sessionId));\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\n\t\tsb.append(\"ConnectionRecord=[\");\n\t\tsb.append(\"node: \").append(node);\n\t\tsb.append(\", userJid: \").append(userJid);\n\t\tsb.append(\", connectionId: \").append(connectionId);\n\t\tsb.append(\", sessionId: \").append(sessionId);\n\t\tsb.append(\"]\");\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic JID getConnectionId() {\n\t\treturn connectionId;\n\t}\n\n\tpublic void setConnectionId(JID connectionId) {\n\t\tthis.connectionId = connectionId;\n\t}\n\n\t@Override\n\tpublic JID getNode() {\n\t\treturn node;\n\t}\n\n\t@Override\n\tpublic String getSessionId() {\n\t\treturn sessionId;\n\t}\n\n\t@Override\n\tpublic JID getUserJid() {\n\t\treturn userJid;\n\t}\n\n\t@Override\n\tpublic void setElement(Element elem) {\n\t\tthis.node = JID.jidInstanceNS(elem.getChild(NODE_ELEMENT).getCData());\n\t\tthis.userJid = JID.jidInstanceNS(elem.getChild(JID_ELEMENT).getCData());\n\t\tthis.connectionId = JID.jidInstanceNS(elem.getChild(CONNECTION_ID_ELEMENT).getCData());\n\t\tthis.sessionId = elem.getChild(SESSION_ID_ELEMENT).getCData();\n\t}\n\n\t@Override\n\tpublic void setRecordFields(JID node, JID userJid, String sessionId, JID connectionId) {\n\t\tthis.node = node;\n\t\tthis.userJid = userJid;\n\t\tthis.connectionId = connectionId;\n\t\tthis.sessionId = sessionId;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/cluster/strategy/ConnectionRecordIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n* To change this template, choose Tools | Templates\n* and open the template in the editor.\n */\npackage tigase.cluster.strategy;\n\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.JID;\n\n/**\n * @author kobit\n */\npublic interface ConnectionRecordIfc {\n\n\tElement toElement();\n\n\tJID getConnectionId();\n\n\tJID getNode();\n\n\tString getSessionId();\n\n\tJID getUserJid();\n\n\tvoid setElement(Element elem);\n\n\tvoid setRecordFields(JID node, JID userJid, String sessionId, JID connectionId);\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/cluster/strategy/DefaultClusteringStrategy.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster.strategy;\n\nimport tigase.cluster.SessionManagerClustered;\nimport tigase.cluster.api.ClusterCommandException;\nimport tigase.cluster.api.CommandListenerAbstract;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.*;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: May 13, 2009 9:53:44 AM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\n@Bean(name = \"strategy\", parent = SessionManagerClustered.class, active = true)\npublic class DefaultClusteringStrategy<E extends ConnectionRecordIfc>\n\t\textends DefaultClusteringStrategyAbstract<E> {\n\n\tpublic static final String CONNECTION_ID = \"connectionId\";\n\n\tpublic static final String RESOURCE = \"resource\";\n\n\tpublic static final String SM_ID = \"smId\";\n\n\tpublic static final String USER_ID = \"userId\";\n\n\tpublic static final String XMPP_SESSION_ID = \"xmppSessionId\";\n\tprivate static final String AUTH_TIME = \"auth-time\";\n\tprivate static final String INITIAL_PRESENCE_KEY = \"cluster-initial-presence\";\n\n\tprivate static final Logger log = Logger.getLogger(DefaultClusteringStrategy.class.getName());\n\tprivate static final String PRESENCE_TYPE_INITIAL = \"initial\";\n\tprivate static final String PRESENCE_TYPE_KEY = \"presence-type\";\n\tprivate static final String PRESENCE_TYPE_UPDATE = \"update\";\n\tprivate static final String USER_CONNECTED_CMD = \"user-connected-sm-cmd\";\n\tprivate static final String USER_PRESENCE_CMD = \"user-presence-sm-cmd\";\n\n\t// Simple random generator, we do not need a strong randomization here.\n\t// Just enough to ensure better traffic distribution\n\tprivate Random rand = new Random();\n\n\n\tpublic DefaultClusteringStrategy() {\n\t\tsuper();\n\t\taddCommandListener(new UserPresenceCommand(USER_PRESENCE_CMD));\n\t\taddCommandListener(new UserConnectedCommand(USER_CONNECTED_CMD));\n\t}\n\t\n\t@Override\n\tpublic void handleLocalPacket(Packet packet, XMPPResourceConnection conn) {\n\t\tif (packet.getElemName() == Presence.ELEM_NAME) {\n\t\t\ttry {\n\t\t\t\tif ((packet.getStanzaFrom() != null) && !conn.isUserId(packet.getStanzaFrom().getBareJID())) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (packet.getType() != null) {\n\t\t\t\t\tswitch (packet.getType()) {\n\t\t\t\t\t\tcase subscribe:\n\t\t\t\t\t\tcase subscribed:\n\t\t\t\t\t\tcase unsubscribe:\n\t\t\t\t\t\tcase unsubscribed:\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tboolean initPresence = conn.getSessionData(INITIAL_PRESENCE_KEY) == null;\n\t\t\t\tMap<String, String> params = prepareConnectionParams(conn);\n\n\t\t\t\tif (initPresence) {\n\t\t\t\t\tconn.putSessionData(INITIAL_PRESENCE_KEY, INITIAL_PRESENCE_KEY);\n\t\t\t\t\tparams.put(PRESENCE_TYPE_KEY, PRESENCE_TYPE_INITIAL);\n\t\t\t\t} else {\n\t\t\t\t\tparams.put(PRESENCE_TYPE_KEY, PRESENCE_TYPE_UPDATE);\n\t\t\t\t}\n\n\t\t\t\tElement presence = packet.getElement();    // conn.getPresence();\n\t\t\t\tList<JID> cl_nodes = getNodesForPacketForward(sm.getComponentId(), null,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  Packet.packetInstance(presence));\n\n\t\t\t\tif ((cl_nodes != null) && (cl_nodes.size() > 0)) {\n\n\t\t\t\t\t// ++clusterSyncOutTraffic;\n\t\t\t\t\tcluster.sendToNodes(USER_PRESENCE_CMD, params, presence, sm.getComponentId(), null,\n\t\t\t\t\t\t\t\t\t\tcl_nodes.toArray(new JID[cl_nodes.size()]));\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.WARNING, \"Problem with broadcast user presence for: \" + conn, e);\n\t\t\t}\n\t\t}\n\t\tsuper.handleLocalPacket(packet, conn);\n\t}\n\n\t@Override\n\tpublic void handleLocalResourceBind(XMPPResourceConnection conn) {\n\t\ttry {\n\t\t\tMap<String, String> params = prepareConnectionParams(conn);\n\t\t\tList<JID> cl_nodes = getNodesConnected();\n\n\t\t\t// ++clusterSyncOutTraffic;\n\t\t\tcluster.sendToNodes(USER_CONNECTED_CMD, params, sm.getComponentId(),\n\t\t\t\t\t\t\t\tcl_nodes.toArray(new JID[cl_nodes.size()]));\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Problem with broadcast user presence for: \" + conn, e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void handleLocalUserLogout(BareJID userId, XMPPResourceConnection conn) {\n\t\ttry {\n\t\t\tif (!conn.isAuthorized()) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tElement presence = conn.getPresence();\n\n\t\t\tif (presence == null) {\n\t\t\t\tpresence = new Element(Presence.ELEM_NAME);\n\t\t\t\tpresence.setXMLNS(Presence.CLIENT_XMLNS);\n\t\t\t} else {\n\t\t\t\tpresence = presence.clone();\n\t\t\t}\n\t\t\tpresence.setAttribute(\"from\", conn.getJID().toString());\n\t\t\tpresence.setAttribute(\"type\", StanzaType.unavailable.name());\n\n\t\t\tMap<String, String> params = prepareConnectionParams(conn);\n\t\t\tList<JID> cl_nodes = getNodesConnected();\n\n\t\t\tif ((cl_nodes != null) && (cl_nodes.size() > 0)) {\n\n\t\t\t\t// ++clusterSyncOutTraffic;\n\t\t\t\tcluster.sendToNodes(USER_PRESENCE_CMD, params, presence, sm.getComponentId(), null,\n\t\t\t\t\t\t\t\t\tcl_nodes.toArray(new JID[cl_nodes.size()]));\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Problem with broadcast user presence for: \" + conn, e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic List<JID> getNodesForPacketForward(JID fromNode, Set<JID> visitedNodes, Packet packet) {\n\t\tif (visitedNodes != null) {\n\t\t\tif (isIqResponseToNode(packet)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tList<JID> result = selectNodes(fromNode, visitedNodes);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Visited nodes not null: {0}, selecting new node: {1}, for packet: {2}\",\n\t\t\t\t\t\tnew Object[]{visitedNodes, result, packet});\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\n\t\t// Presence status change set by the user have a special treatment:\n\t\tif ((packet.getElemName() == \"presence\") && (packet.getType() != StanzaType.error) &&\n\t\t\t\t(packet.getStanzaFrom() != null) && (packet.getStanzaTo() == null)) {\n\t\t\tList<JID> result = getNodesConnected();\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Presence packet found: {0}, selecting all nodes: {1}\",\n\t\t\t\t\t\tnew Object[]{packet, result});\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t\tif (isSuitableForForward(packet)) {\n\t\t\tList<JID> result = null;\n\t\t\tif (isIqResponseToNode(packet)) {\n\t\t\t\tresult = getNodesForIqResponse(packet);\n\t\t\t} else {\n\t\t\t\tresult = selectNodes(fromNode, visitedNodes);\n\t\t\t}\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Visited nodes null, selecting new node: {0}, for packet: {1}\",\n\t\t\t\t\t\tnew Object[]{result, packet});\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Packet not suitable for forwarding: {0}\", new Object[]{packet});\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * A utility method used to prepare a Map of data with user session data before it can be sent over to another\n\t * cluster node. This is supposed to contain all the user's session essential information which directly identify\n\t * user's resource and network connection. This information allows to detect two different user's connection made\n\t * for the same resource. This may happen if both connections are established to different nodes.\n\t *\n\t * @param conn is user's XMPPResourceConnection for which Map structure is prepare.\n\t *\n\t * @return a Map structure with all user's connection essential data.\n\t */\n\tprotected Map<String, String> prepareConnectionParams(XMPPResourceConnection conn)\n\t\t\tthrows NotAuthorizedException, NoConnectionIdException {\n\t\tMap<String, String> params = new LinkedHashMap<String, String>();\n\n\t\tparams.put(USER_ID, conn.getBareJID().toString());\n\t\tparams.put(RESOURCE, conn.getResource());\n\t\tparams.put(CONNECTION_ID, conn.getConnectionId().toString());\n\t\tparams.put(XMPP_SESSION_ID, conn.getSessionId());\n\t\tparams.put(AUTH_TIME, \"\" + conn.getAuthTime());\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Called for conn: {0}, result: \", new Object[]{conn, params});\n\t\t}\n\n\t\treturn params;\n\t}\n\n\t/**\n\t * Method takes the data received from other cluster node and creates a ConnectionRecord with all essential\n\t * connection information. This might be used later to identify user's XMPPResourceConnection or use the clustering\n\t * strategy API.\n\t */\n\tprotected ConnectionRecordIfc getConnectionRecord(JID node, Map<String, String> data) {\n\t\tBareJID userId = BareJID.bareJIDInstanceNS(data.get(USER_ID));\n\t\tString resource = data.get(RESOURCE);\n\t\tJID jid = JID.jidInstanceNS(userId, resource);\n\t\tString sessionId = data.get(XMPP_SESSION_ID);\n\t\tJID connectionId = JID.jidInstanceNS(data.get(CONNECTION_ID));\n\t\tConnectionRecordIfc rec = this.getConnectionRecordInstance();    // new ConnectionRecord(node, jid, sessionId, connectionId);\n\n\t\trec.setRecordFields(node, jid, sessionId, connectionId);\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"ConnectionRecord created: {0}\", new Object[]{rec});\n\t\t}\n\n\t\treturn rec;\n\t}\n\n\tprivate List<JID> selectNodes(JID fromNode, Set<JID> visitedNodes) {\n\t\tList<JID> result = null;\n\t\tList<JID> cl_nodes_list = getNodesConnected();\n\t\tint size = cl_nodes_list.size();\n\n\t\tif (size == 0) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"No connected cluster nodes found, returning null\");\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tint idx = rand.nextInt(size);\n\n\t\tif ((visitedNodes == null) || (visitedNodes.size() == 0)) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"No visited nodes yet, trying random idx: \" + idx);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tresult = Collections.singletonList(cl_nodes_list.get(idx));\n\t\t\t} catch (IndexOutOfBoundsException ioobe) {\n\n\t\t\t\t// This may happen if the node disconnected in the meantime....\n\t\t\t\ttry {\n\t\t\t\t\tresult = Collections.singletonList(cl_nodes_list.get(0));\n\t\t\t\t} catch (IndexOutOfBoundsException ioobe2) {\n\n\t\t\t\t\t// Yes, this may happen too if there were only 2 nodes before\n\t\t\t\t\t// disconnect....\n\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\tlog.log(Level.FINE,\n\t\t\t\t\t\t\t\t\"IndexOutOfBoundsException twice! Should not happen very often, returning null\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor (JID jid : cl_nodes_list) {\n\t\t\t\tif (!visitedNodes.contains(jid)) {\n\t\t\t\t\tresult = Collections.singletonList(jid);\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If all nodes visited already. We have to either send it back to the\n\t\t\t// first node\n\t\t\t// or if this is the first node return null\n\t\t\tif ((result == null) && !sm.getComponentId().equals(fromNode)) {\n\t\t\t\tresult = Collections.singletonList(fromNode);\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"All nodes visited, sending it back to the first node: \" + result);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"List of result nodes: \" + result);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate class UserConnectedCommand\n\t\t\textends CommandListenerAbstract {\n\n\t\tpublic UserConnectedCommand(String name) {\n\t\t\tsuper(name, Priority.CLUSTER);\n\t\t}\n\n\t\t@Override\n\t\tpublic void executeCommand(JID fromNode, Set<JID> visitedNodes, Map<String, String> data,\n\t\t\t\t\t\t\t\t   Queue<Element> packets) throws ClusterCommandException {\n\n\t\t\t// ++clusterSyncInTraffic;\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Called fromNode: {0}, visitedNodes: {1}, data: {2}, packets: {3}\",\n\t\t\t\t\t\tnew Object[]{fromNode, visitedNodes, data, packets});\n\t\t\t}\n\n\t\t\t// Queue<Packet> results = new ArrayDeque<Packet>(10);\n\t\t\tConnectionRecordIfc rec = getConnectionRecord(fromNode, data);\n\n\t\t\t// strategy.usersConnected(results, rec);\n\t\t\t// addOutPackets(results);\n\t\t\t// There is one more thing....\n\t\t\t// If the new connection is for the same resource we have here then the\n\t\t\t// old connection must be destroyed.\n\t\t\tXMPPSession session = sm.getXMPPSessions().get(rec.getUserJid().getBareJID());\n\n\t\t\tif (session != null) {\n\t\t\t\tXMPPResourceConnection conn = session.getResourceForResource(rec.getUserJid().getResource());\n\n\t\t\t\tif (conn != null) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.finest(\"Duplicate resource connection, logingout the older connection: \" + rec);\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tPacket cmd = Command.CLOSE.getPacket(sm.getComponentId(), conn.getConnectionId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t StanzaType.set, conn.nextStanzaId());\n\t\t\t\t\t\tElement err_el = new Element(\"conflict\");\n\n\t\t\t\t\t\terr_el.setXMLNS(\"urn:ietf:params:xml:ns:xmpp-streams\");\n\t\t\t\t\t\tcmd.getElement().getChild(\"command\").addChild(err_el);\n\t\t\t\t\t\tsm.fastAddOutPacket(cmd);\n\t\t\t\t\t} catch (Exception ex) {\n\n\t\t\t\t\t\t// TODO Auto-generated catch block\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Error executing cluster command\", ex);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"User connected jid: \" + rec.getUserJid() + \", fromNode: \" + fromNode);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate class UserPresenceCommand\n\t\t\textends CommandListenerAbstract {\n\n\n\t\tpublic UserPresenceCommand(String name) {\n\t\t\tsuper(name, Priority.CLUSTER);\n\t\t}\n\n\t\t@Override\n\t\tpublic void executeCommand(JID fromNode, Set<JID> visitedNodes, Map<String, String> data,\n\t\t\t\t\t\t\t\t   Queue<Element> packets) throws ClusterCommandException {\n\n\t\t\t// ++clusterSyncInTraffic;\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Called fromNode: {0}, visitedNodes: {1}, data: {2}, packets: {3}\",\n\t\t\t\t\t\tnew Object[]{fromNode, visitedNodes, data, packets});\n\t\t\t}\n\n\t\t\tConnectionRecordIfc rec = getConnectionRecord(fromNode, data);\n\t\t\tXMPPSession session = sm.getXMPPSessions().get(rec.getUserJid().getBareJID());\n\t\t\tElement elem = packets.poll();\n\n\t\t\t// Notify strategy about presence update\n//    strategy.presenceUpdate(elem, rec);\n\t\t\t// Update all user's resources with the new presence\n\t\t\tif (session != null) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"User''s {0} XMPPSession found: {1}\",\n\t\t\t\t\t\t\tnew Object[]{rec.getUserJid().getBareJID(), session});\n\t\t\t\t}\n\t\t\t\tfor (XMPPResourceConnection conn : session.getActiveResources()) {\n\t\t\t\t\tElement conn_presence = conn.getPresence();\n\n\t\t\t\t\tif (conn.isAuthorized() && conn.isResourceSet() && (conn_presence != null)) {\n\t\t\t\t\t\ttry {\n\n\t\t\t\t\t\t\t// Send user's presence from remote connection to local connection\n\t\t\t\t\t\t\tElement clone = elem.clone();\n\t\t\t\t\t\t\t// Set proper to attribute\n\t\t\t\t\t\t\tclone.setAttribute(\"to\", conn.getJID().toString());\n\t\t\t\t\t\t\tPacket presence = Packet.packetInstance(clone);\n\n\t\t\t\t\t\t\tpresence.setPacketTo(conn.getConnectionId());\n\t\t\t\t\t\t\tsm.fastAddOutPacket(presence);\n\n\t\t\t\t\t\t\t// Send user's presence from local connection to remote connection\n\t\t\t\t\t\t\t// but only if this was an initial presence\n\t\t\t\t\t\t\tif ((data != null) && PRESENCE_TYPE_INITIAL.equals(data.get(PRESENCE_TYPE_KEY))) {\n\t\t\t\t\t\t\t\tpresence = Packet.packetInstance(conn_presence);\n\t\t\t\t\t\t\t\tpresence.setPacketTo(rec.getConnectionId());\n\t\t\t\t\t\t\t\tsm.fastAddOutPacket(presence);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Error executing command\", ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tsm.processPresenceUpdate(session, elem);\n\t\t\t} else {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\"No user session for presence update: {0}, visitedNodes: {1}, data: {2}, packets: {3}\",\n\t\t\t\t\t\t\tnew Object[]{fromNode, visitedNodes, data, packets});\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"User presence jid: \" + rec.getUserJid() + \", fromNode: \" + fromNode);\n\t\t\t}\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/cluster/strategy/DefaultClusteringStrategyAbstract.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster.strategy;\n\nimport tigase.cluster.api.ClusterControllerIfc;\nimport tigase.cluster.api.CommandListener;\nimport tigase.cluster.api.SessionManagerClusteredIfc;\nimport tigase.cluster.strategy.cmd.PacketForwardCmd;\nimport tigase.eventbus.EventBus;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Iq;\nimport tigase.server.Message;\nimport tigase.server.Packet;\nimport tigase.stats.StatisticsList;\nimport tigase.util.Algorithms;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.xml.Element;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CopyOnWriteArraySet;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.cluster.api.SessionManagerClusteredIfc.SESSION_FOUND_KEY;\n\n/**\n * Created: May 13, 2009 9:53:44 AM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\npublic abstract class DefaultClusteringStrategyAbstract<E extends ConnectionRecordIfc>\n\t\timplements ClusteringStrategyIfc<E> {\n\n\tprivate static final String ERROR_FORWARDING_KEY = \"error-forwarding\";\n\n\tprivate static final Logger log = Logger.getLogger(DefaultClusteringStrategyAbstract.class.getName());\n\tprivate static final String PACKET_FORWARD_CMD = \"packet-forward-sm-cmd\";\n\t@Inject\n\tprotected ClusterControllerIfc cluster = null;\n\tprotected String comp = \"sess-man\";\n\n\t@Inject\n\tprotected EventBus eventBus = null;\n\tprotected String prefix = \"strategy/\" + this.getClass().getSimpleName() + \"/\";\n\t@Inject\n\tprotected SessionManagerClusteredIfc sm = null;\n\tprivate JID ampJID = JID.jidInstanceNS(\"amp\", DNSResolverFactory.getInstance().getDefaultHost());\n\tprivate Set<CommandListener> commands = new CopyOnWriteArraySet<CommandListener>();\n\t@ConfigField(desc=\"Forward error packets within cluster\", alias = \"error-forwarding\")\n\tprivate ErrorForwarding errorForwarding = ErrorForwarding.forward;\n\tprivate static EnumSet<StanzaType> ERROR_OR_RESULT = EnumSet.of(StanzaType.error, StanzaType.result);\n\n\tprivate Map<String, JID> iqResourceToClusterNode = new ConcurrentHashMap<>();\n\n\tpublic DefaultClusteringStrategyAbstract() {\n\t\tsuper();\n\t\taddCommandListener(new PacketForwardCmd(PACKET_FORWARD_CMD, this));\n\t}\n\n\t// private ClusteringMetadataIfc<E> metadata = null;\n\n\t@Override\n\tpublic void statisticExecutedIn(long executionTime) {\n\n\t}\n\n\t@Override\n\tpublic void everyHour() {\n\n\t}\n\n\t@Override\n\tpublic void everyMinute() {\n\n\t}\n\n\t@Override\n\tpublic void everySecond() {\n\n\t}\n\n\t@Override\n\tpublic void getStatistics(String compName, StatisticsList list) {\n\n\t}\n\n\t@Override\n\tpublic void setStatisticsPrefix(String prefix) {\n\t\tthis.prefix = prefix;\n\t}\n\n\tpublic final void addCommandListener(CommandListener cmd) {\n\t\tcommands.add(cmd);\n\t}\n\n\t@Override\n\tpublic boolean containsJid(BareJID jid) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean containsJidLocally(BareJID jid) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean containsJidLocally(JID jid) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void handleLocalPacket(Packet packet, XMPPResourceConnection conn) {\n\t}\n\n\t@Override\n\tpublic void handleLocalPresenceSet(XMPPResourceConnection conn) {\n\t\t// Do nothing\n\t}\n\n\t@Override\n\tpublic void handleLocalResourceBind(XMPPResourceConnection conn) {\n\t\t// Do nothing\n\t}\n\n\t@Override\n\tpublic void handleLocalUserLogin(BareJID userId, XMPPResourceConnection conn) {\n\n\t\t// Do nothing\n\t}\n\n\t@Override\n\tpublic void handleLocalUserLogout(BareJID userId, XMPPResourceConnection conn) {\n\n\t\t// Do nothing\n\t}\n\n\t@Override\n\tpublic void handleLocalUserChangedConnId(BareJID userId, XMPPResourceConnection conn, JID oldConnId,\n\t\t\t\t\t\t\t\t\t\t\t JID newConnId) {\n\t\t// Do nothing\n\t}\n\n\t@Override\n\tpublic boolean processPacket(Packet packet, XMPPResourceConnection conn) {\n\t\tList<JID> toNodes = getNodesForPacketForward(sm.getComponentId(), null, packet);\n\t\tboolean result = (toNodes != null) && (toNodes.size() > 0);\n\n\t\tif (result) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Forwarding packet {0} to nodes: {1}\", new Object[]{packet, toNodes});\n\t\t\t}\n\n\t\t\tMap<String, String> data = null;\n\n\t\t\tif (conn != null || packet.getPacketFrom() != null || packet.getStableId() != null) {\n\t\t\t\tdata = new LinkedHashMap<String, String>();\n\t\t\t\tif (conn != null) {\n\t\t\t\t\tdata.put(SESSION_FOUND_KEY, sm.getComponentId().toString());\n\t\t\t\t}\n\t\t\t\tif (packet.getPacketFrom() != null) {\n\t\t\t\t\tdata.put(PacketForwardCmd.PACKET_FROM_KEY, packet.getPacketFrom().toString());\n\t\t\t\t}\n\t\t\t\tif (packet.getStableId() != null) {\n\t\t\t\t\tdata.put(PacketForwardCmd.STABLE_ID_KEY, packet.getStableId());\n\t\t\t\t}\n\t\t\t}\n\t\t\tcluster.sendToNodes(PACKET_FORWARD_CMD, data, packet.getElement(), sm.getComponentId(), null,\n\t\t\t\t\t\t\t\ttoNodes.toArray(new JID[toNodes.size()]));\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"No cluster nodes found for packet forward: {0}\", new Object[]{packet});\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Method attempts to send the packet to the next cluster node. Returns true on successful attempt and false on\n\t * failure. The true result does not mean that the packet has been delivered though. Only that it was sent. The send\n\t * attempt may fail if there is no more cluster nodes to send the packet or if the clustering strategy logic decided\n\t * that the packet does not have to be sent.\n\t *\n\t * @param packet to be sent to a next cluster node\n\t * @param visitedNodes a list of nodes already visited by the packet.\n\t *\n\t * @return true if the packet was sent to next cluster node and false otherwise.\n\t */\n\tpublic boolean sendToNextNode(JID fromNode, Set<JID> visitedNodes, Map<String, String> data, Packet packet) {\n\t\tboolean result = false;\n\t\tList<JID> nextNodes = getNodesForPacketForward(fromNode, visitedNodes, packet);\n\n\t\tif ((nextNodes != null) && (nextNodes.size() > 0)) {\n\t\t\tcluster.sendToNodes(PACKET_FORWARD_CMD, data, packet.getElement(), fromNode, visitedNodes,\n\t\t\t\t\t\t\t\tnextNodes.toArray(new JID[nextNodes.size()]));\n\t\t\tresult = true;\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Called for packet: {0}, visitedNodes: {1}, result: {2}\",\n\t\t\t\t\tnew Object[]{packet, visitedNodes, result});\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getInfo();\n\t}\n\n\t@Override\n\tpublic List<JID> getNodesConnected() {\n\t\treturn sm.getNodesConnected();\n\t}\n\n\t@Override\n\tpublic JID[] getConnectionIdsForJid(BareJID jid) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic E getConnectionRecord(JID jid) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic E getConnectionRecordInstance() {\n\t\treturn (E) (new ConnectionRecord());\n\t}\n\n\t@Override\n\tpublic Set<E> getConnectionRecords(BareJID bareJID) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getDefaults(Map<String, Object> params) {\n\t\tMap<String, Object> props = new HashMap<String, Object>();\n\n\t\tprops.put(ERROR_FORWARDING_KEY, ErrorForwarding.drop.name());\n\n\t\treturn props;\n\t}\n\n\t@Override\n\tpublic String getInfo() {\n\t\treturn \"basic strategy\";\n\t}\n\n\tpublic List<JID> getNodesForPacketForward(JID fromNode, Set<JID> visitedNodes, Packet packet) {\n\n\t\t// If visited nodes is not null then we return null as this strategy never\n\t\t// sends packets in ring, the first node decides where to send a packet\n\t\tif (visitedNodes != null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<JID> nodes = null;\n\n//  JID jidLookup = packet.getStanzaTo();\n//\n//  // Presence status change set by the user have a special treatment:\n//  if (presenceStatusUpdate(packet)) {\n//    jidLookup = packet.getStanzaFrom();\n//  }\n\t\tif (isSuitableForForward(packet)) {\n\t\t\tif (isIqResponseToNode(packet)) {\n\t\t\t\tnodes = getNodesForIqResponse(packet);\n\t\t\t} else {\n\t\t\t\t// nodes = metadata.getNodesForJid(jidLookup);\n\t\t\t\tnodes = getNodesConnected();\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Selected nodes: {0}, for packet: {1}\", new Object[]{nodes, packet});\n\t\t\t}\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Packet not suitable for forwarding: {0}\", new Object[]{packet});\n\t\t\t}\n\t\t}\n\n\t\treturn nodes;\n\t}\n\n\tpublic boolean isIqResponseToNode(Packet packet) {\n\t\tif (packet.getElemName() == Iq.ELEM_NAME && ERROR_OR_RESULT.contains(packet.getType())) {\n\t\t\tJID to = packet.getStanzaTo();\n\t\t\treturn to != null && sm.isLocalDomain(to.getBareJID().toString(), false) && to.getResource() != null;\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic List<JID> getNodesForIqResponse(Packet packet) {\n\t\tJID clusterNode = iqResourceToClusterNode.get(packet.getStanzaTo().getResource());\n\t\tif (clusterNode != null) {\n\t\t\treturn Collections.singletonList(clusterNode);\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t\tfor (CommandListener cmd : commands) {\n\t\t\tcmd.getStatistics(list);\n\t\t}\n\t\tgetStatistics(\"sess-man/\", list);\n\t}\n\n\t@Override\n\tpublic boolean hasCompleteJidsInfo() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void nodeConnected(JID node) {\n\t\tString hash = Algorithms.sha256(node.getDomain());\n\t\tiqResourceToClusterNode.put(hash, node);\n\t}\n\n\t@Override\n\tpublic void nodeDisconnected(JID node) {\n\t\tString hash = Algorithms.sha256(node.getDomain());\n\t\tiqResourceToClusterNode.remove(hash, node);\n\t}\n\n\t@Override\n\tpublic void setClusterController(ClusterControllerIfc clComp) {\n\t\tcluster = clComp;\n\t\tfor (CommandListener cmd : commands) {\n\t\t\tcluster.removeCommandListener(cmd);\n\t\t\tcluster.setCommandListener(cmd);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setProperties(Map<String, Object> props) {\n\n\t\t// This code is bad as some commands are added in other methods and in\n\t\t// constructors - this code would break those commands!\n//\t\t// we need to remember that this method can be called more than once\n//\t\t// and we need to clean list of commands if we are adding any command here\n//\t\tCommandListener[] oldCmds = commands.toArray(new CommandListener[commands.size()]);\n//\n//\t\tfor (CommandListener oldCmd : oldCmds) {\n//\t\t\tif (PACKET_FORWARD_CMD.equals(oldCmd.getName())) {\n//\t\t\t\tcommands.remove(oldCmd);\n//\t\t\t}\n//\t\t}\n//\t\taddCommandListener(new PacketForwardCmd(PACKET_FORWARD_CMD, sm, this));\n\t\tif (props.containsKey(ERROR_FORWARDING_KEY)) {\n\t\t\terrorForwarding = ErrorForwarding.valueOf((String) props.get(ERROR_FORWARDING_KEY));\n\t\t}\n\t}\n\n\tpublic SessionManagerClusteredIfc getSM() {\n\t\treturn this.sm;\n\t}\n\n\tpublic void fireEvent(Object event) {\n\t\teventBus.fire(event);\n\t}\n\n\tprotected boolean isSuitableForForward(Packet packet) {\n\t\tswitch (errorForwarding) {\n\t\t\tcase forward:\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\n\t\t\t\t// Do not forward any error packets by default\n\t\t\t\tif (packet.getType() == StanzaType.error) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t}\n\n\t\t// Artur: Moved it to the front of the method for performance reasons.\n\t\t// TODO: make sure it does not affect logic.\n\t\t// Andrzej: this blocks sending responses from other components if\n\t\t// packet was processed on other node that node of user session, ie.\n\t\t// when new PubSub clustered component is used\n//\t\tif ((packet.getPacketFrom() != null) &&!sm.getComponentId().equals(packet\n//\t\t\t\t.getPacketFrom())) {\n//\t\t\treturn false;\n//\t\t}\n\t\t// Andrzej: Added in place of condition which was checked above as due to\n\t\t// lack of this condition messages sent from client with \"from\" attribute\n\t\t// set are duplicated\n\t\tif (packet.getPacketFrom() != null && sm.hasXMPPResourceConnectionForConnectionJid(packet.getPacketFrom())) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// This is for packet forwarding logic.\n\t\t// Some packets are for certain not forwarded like packets without to\n\t\t// attribute set.\n\t\tif ((packet.getStanzaTo() == null) || sm.isLocalDomain(packet.getStanzaTo().toString(), false) ||\n\t\t\t\tsm.getComponentId().getBareJID().equals((packet.getStanzaTo().getBareJID()))) {\n\t\t\treturn false;\n\t\t}\n\n//\t\tif (sm.isLocalDomain(packet.getStanzaTo().getBareJID().toString(), false)) {\n//\t\t\tif (packet.getElemName() == Iq.ELEM_NAME &&\n//\t\t\t\t\t(packet.getType() == StanzaType.error || packet.getType() == StanzaType.result) &&\n//\t\t\t\t\tpacket.getStanzaTo().getResource() != null) {\n//\t\t\t\treturn true;\n//\t\t\t}\n//\t\t\treturn false;\n//\t\t}\n\n\t\t// Also packets sent from the server to user are not being forwarded like\n\t\t// service discovery perhaps?\n\t\tif ((packet.getStanzaFrom() == null) || sm.isLocalDomain(packet.getStanzaFrom().toString(), false) ||\n\t\t\t\tsm.getComponentId().getBareJID().equals((packet.getStanzaFrom().getBareJID()))) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// If the packet is to some external domain, it is not forwarded to other\n\t\t// nodes either. It is also not forwarded if it is addressed to some\n\t\t// component.\n\t\tif (!sm.isLocalDomain(packet.getStanzaTo().getDomain(), false)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// server needs to respond on Iq stanzas sent to bare jid, but there is\n\t\t// no need to forward it to cluster node with users session\n\t\tif (packet.getElemName() == Iq.ELEM_NAME && packet.getStanzaTo() != null &&\n\t\t\t\tpacket.getStanzaTo().getResource() == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (packet.getElemName() == Message.ELEM_NAME && packet.getType() != StanzaType.error &&\n\t\t\t\tampJID.equals(packet.getPacketFrom())) {\n\t\t\tElement amp = packet.getElement().getChild(\"amp\", \"http://jabber.org/protocol/amp\");\n\t\t\tif (amp != null && amp.getAttributeStaticStr(\"status\") == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprivate static enum ErrorForwarding {\n\t\tforward,\n\t\tdrop\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/cluster/strategy/SMNonCachingAllNodes.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster.strategy;\n\nimport tigase.cluster.api.SessionManagerClusteredIfc;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Packet;\nimport tigase.stats.StatisticsList;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: May 13, 2009 9:53:44 AM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic abstract class SMNonCachingAllNodes\n\t\timplements ClusteringStrategyIfc<ConnectionRecord> {\n\n\tprivate static final Logger log = Logger.getLogger(SMNonCachingAllNodes.class.getName());\n\t// Simple random generator, we do not need a strong randomization here.\n\t// Just enough to ensure better traffic distribution\n\tprivate Random rand = new Random();\n\t@Inject\n\tprivate SessionManagerClusteredIfc sm = null;\n\n\t@Override\n\tpublic boolean containsJid(BareJID jid) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void nodeConnected(JID jid) {\n\t}\n\n\t@Override\n\tpublic void nodeDisconnected(JID jid) {\n\t}\n\n\t@Override\n\tpublic List<JID> getNodesConnected() {\n\t\treturn sm.getNodesConnected();\n\t}\n\n\t@Override\n\tpublic JID[] getConnectionIdsForJid(BareJID jid) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Set<ConnectionRecord> getConnectionRecords(BareJID bareJID) {\n\t\treturn null;\n\t}\n\n\tpublic List<JID> getNodesForJid(JID jid) {\n\t\treturn getNodesConnected();\n\t}\n\n\tpublic List<JID> getNodesForPacketForward(JID fromNode, Set<JID> visitedNodes, Packet packet) {\n\n\t\t// If the packet visited other nodes already it means it went through other\n\t\t// checking\n\t\t// like isSuitableForForward, etc... so there is no need for doing it again\n\t\tif (visitedNodes != null) {\n\t\t\tList<JID> result = selectNodes(fromNode, visitedNodes);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Visited nodes not null: {0}, selecting new node: {1}, for packet: {2}\",\n\t\t\t\t\t\tnew Object[]{visitedNodes, result, packet});\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\n\t\t// Presence status change set by the user have a special treatment:\n\t\tif ((packet.getElemName() == \"presence\") && (packet.getType() != StanzaType.error) &&\n\t\t\t\t(packet.getStanzaFrom() != null) && (packet.getStanzaTo() == null)) {\n\t\t\tList<JID> result = getNodesConnected();\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Presence packet found: {0}, selecting all nodes: {1}\",\n\t\t\t\t\t\tnew Object[]{packet, result});\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t\tif (isSuitableForForward(packet)) {\n\t\t\tList<JID> result = selectNodes(fromNode, visitedNodes);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Visited nodes null, selecting new node: {0}, for packet: {1}\",\n\t\t\t\t\t\tnew Object[]{result, packet});\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Packet not suitable for forwarding: {0}\", new Object[]{packet});\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic List<JID> getNodesForUserConnect(JID jid) {\n\t\treturn getNodesConnected();\n\t}\n\n\tpublic List<JID> getNodesForUserDisconnect(JID jid) {\n\t\treturn getNodesConnected();\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t}\n\n\t@Override\n\tpublic boolean hasCompleteJidsInfo() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void setProperties(Map<String, Object> props) {\n\t}\n\n\tprotected boolean isSuitableForForward(Packet packet) {\n\n\t\t// Do not forward any error packets for now.\n\t\tif (packet.getType() == StanzaType.error) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Artur: Moved it to the front of the method for performance reasons.\n\t\t// TODO: make sure it does not affect logic.\n\t\tif ((packet.getPacketFrom() != null) && !sm.getComponentId().equals(packet.getPacketFrom())) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// This is for packet forwarding logic.\n\t\t// Some packets are for certain not forwarded like packets without to\n\t\t// attribute set.\n\t\tif ((packet.getStanzaTo() == null) || sm.isLocalDomain(packet.getStanzaTo().toString(), false) ||\n\t\t\t\tsm.getComponentId().equals((packet.getStanzaTo().getBareJID()))) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Also packets sent from the server to user are not being forwarded like\n\t\t// service discovery perhaps?\n\t\tif ((packet.getStanzaFrom() == null) || sm.isLocalDomain(packet.getStanzaFrom().toString(), false) ||\n\t\t\t\tsm.getComponentId().equals((packet.getStanzaFrom().getBareJID()))) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// If the packet is to some external domain, it is not forwarded to other\n\t\t// nodes either. It is also not forwarded if it is addressed to some\n\t\t// component.\n\t\tif (!sm.isLocalDomain(packet.getStanzaTo().getDomain(), false)) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t */\n\tprivate List<JID> selectNodes(JID fromNode, Set<JID> visitedNodes) {\n\t\tList<JID> result = null;\n\t\tList<JID> cl_nodes_list = getNodesConnected();\n\t\tint size = cl_nodes_list.size();\n\n\t\tif (size == 0) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"No connected cluster nodes found, returning null\");\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tint idx = rand.nextInt(size);\n\n\t\tif ((visitedNodes == null) || (visitedNodes.size() == 0)) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"No visited nodes yet, trying random idx: \" + idx);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tresult = Collections.singletonList(cl_nodes_list.get(idx));\n\t\t\t} catch (IndexOutOfBoundsException ioobe) {\n\n\t\t\t\t// This may happen if the node disconnected in the meantime....\n\t\t\t\ttry {\n\t\t\t\t\tresult = Collections.singletonList(cl_nodes_list.get(0));\n\t\t\t\t} catch (IndexOutOfBoundsException ioobe2) {\n\n\t\t\t\t\t// Yes, this may happen too if there were only 2 nodes before\n\t\t\t\t\t// disconnect....\n\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\tlog.log(Level.FINE,\n\t\t\t\t\t\t\t\t\"IndexOutOfBoundsException twice! Should not happen very often, returning null\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor (JID jid : cl_nodes_list) {\n\t\t\t\tif (!visitedNodes.contains(jid)) {\n\t\t\t\t\tresult = Collections.singletonList(jid);\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If all nodes visited already. We have to either send it back to the\n\t\t\t// first node\n\t\t\t// or if this is the first node return null\n\t\t\tif ((result == null) && !sm.getComponentId().equals(fromNode)) {\n\t\t\t\tresult = Collections.singletonList(fromNode);\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"All nodes visited, sending it back to the first node: \" + result);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"List of result nodes: \" + result);\n\t\t}\n\n\t\treturn result;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/cluster/strategy/cmd/PacketForwardCmd.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n* To change this template, choose Tools | Templates\n* and open the template in the editor.\n */\npackage tigase.cluster.strategy.cmd;\n\nimport tigase.cluster.SessionManagerClustered;\nimport tigase.cluster.api.ClusterCommandException;\nimport tigase.cluster.api.CommandListenerAbstract;\nimport tigase.cluster.api.SessionManagerClusteredIfc;\nimport tigase.cluster.strategy.DefaultClusteringStrategyAbstract;\nimport tigase.server.Packet;\nimport tigase.server.Priority;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.Set;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n * @version 5.2.0, 13/06/22\n */\npublic class PacketForwardCmd\n\t\textends CommandListenerAbstract {\n\n\tpublic static final String PACKET_FROM_KEY = \"packet-from\";\n\tpublic static final String STABLE_ID_KEY = \"stable-id\";\n\tprivate static final Logger log = Logger.getLogger(PacketForwardCmd.class.getName());\n\n\tprivate DefaultClusteringStrategyAbstract strategy;\n\n\t\tpublic PacketForwardCmd(String name, DefaultClusteringStrategyAbstract strategy) {\n\t\tsuper(name, Priority.HIGH);\n\t\tthis.strategy = strategy;\n\t}\n\n\t@Override\n\tpublic void executeCommand(JID fromNode, Set<JID> visitedNodes, Map<String, String> data, Queue<Element> packets)\n\t\t\tthrows ClusterCommandException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Called fromNode: {0}, visitedNodes: {1}, data: {2}, packets: {3}\",\n\t\t\t\t\tnew Object[]{fromNode, visitedNodes, data, packets});\n\t\t}\n\t\tif ((packets != null) && (packets.size() > 0)) {\n\t\t\tSessionManagerClusteredIfc sm = getSM();\n\t\t\tfor (Element elem : packets) {\n\t\t\t\ttry {\n\t\t\t\t\tPacket el_packet = Packet.packetInstance(elem);\n\t\t\t\t\tString packetFromStr = data.get(PACKET_FROM_KEY);\n\t\t\t\t\tif (packetFromStr != null) {\n\t\t\t\t\t\tel_packet.setPacketFrom(JID.jidInstanceNS(packetFromStr));\n\t\t\t\t\t}\n\t\t\t\t\tString stableId = data.get(STABLE_ID_KEY);\n\t\t\t\t\tif (stableId != null) {\n\t\t\t\t\t\tel_packet.setStableId(stableId);\n\t\t\t\t\t}\n\t\t\t\t\tXMPPResourceConnection conn = sm.getXMPPResourceConnection(el_packet);\n\t\t\t\t\tMap<String, String> locdata = null;\n\n\t\t\t\t\tif (conn != null) {\n\t\t\t\t\t\tlocdata = new LinkedHashMap<String, String>();\n\t\t\t\t\t\tif (data != null) {\n\t\t\t\t\t\t\tlocdata.putAll(data);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdata.put(SessionManagerClusteredIfc.SESSION_FOUND_KEY, sm.getComponentId().toString());\n\t\t\t\t\t}\n\n\t\t\t\t\t// The commented if below causes the packet to stop being forwarded\n\t\t\t\t\t// if it reached a host on which there is a user session to handle\n\t\t\t\t\t// it.\n\t\t\t\t\t// This is incorrect though, as there might be multiple users'\n\t\t\t\t\t// connections\n\t\t\t\t\t// to different nodes and each node should receive the packet.\n\t\t\t\t\t// if (conn != null || !sendToNextNode(fromNode, visitedNodes, data,\n\t\t\t\t\t// Packet.packetInstance(elem))) {\n\t\t\t\t\t// Instead, always send the packet to next node:\n\t\t\t\t\tboolean isSent;\n\n\t\t\t\t\tisSent = strategy.sendToNextNode(fromNode, visitedNodes, data, Packet.packetInstance(elem));\n\n\t\t\t\t\t// If there is a user session for the packet, process it\n\t\t\t\t\tif (conn != null) {\n\n\t\t\t\t\t\t// Hold on! If this is the first node (fromNode) it means the\n\t\t\t\t\t\t// packet was already processed here....\n\t\t\t\t\t\tif (!sm.getComponentId().equals(fromNode)) {\n\t\t\t\t\t\t\tsm.processPacket(el_packet, conn);\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t// Ignore the packet, it has been processed already\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// No user session, but if this is the first node the packet has\n\t\t\t\t\t\t// returned, so maybe this is a packet for offline storage?\n\t\t\t\t\t\tif (sm.getComponentId().equals(fromNode)) {\n\n\t\t\t\t\t\t\t// However it could have been processed on another node already\n\t\t\t\t\t\t\tif ((data == null) || (data.get(SessionManagerClustered.SESSION_FOUND_KEY) == null)) {\n\t\t\t\t\t\t\t\tsm.processPacket(el_packet, conn);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\tlog.warning(\"Addressing problem, stringprep failed for packet: \" + elem);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tlog.finest(\"Empty packets list in the forward command\");\n\t\t}\n\t}\n\n\tprivate SessionManagerClusteredIfc getSM() {\n\t\treturn strategy.getSM();\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/component/AbstractComponent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.component.modules.Module;\nimport tigase.component.modules.StanzaProcessor;\nimport tigase.component.responses.AsyncCallback;\nimport tigase.component.responses.ResponseManager;\nimport tigase.conf.ConfigurationException;\nimport tigase.disco.XMPPService;\nimport tigase.eventbus.EventBus;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.AbstractMessageReceiver;\nimport tigase.server.DisableDisco;\nimport tigase.server.Packet;\n\nimport javax.script.Bindings;\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Base class for implement XMPP Component.\n *\n * @author bmalkow\n * @deprecated Do not use this class at all. Use {@link AbstractKernelBasedComponent} instead. This class is here just\n * because developer wants to know how some parts of code worked before migration to Kernel Based TCF.\n */\n@Deprecated\n@TigaseDeprecated(since = \"8.0.0\")\npublic abstract class AbstractComponent\n\t\textends AbstractMessageReceiver\n\t\timplements XMPPService, DisableDisco {\n\n\tprotected static final String COMPONENT = \"component\";\n\tprotected final Logger log = Logger.getLogger(this.getClass().getName());\n\n\t@Inject(bean = \"eventBus\")\n\tprotected EventBus eventBus;\n\n\t@Inject\n\tprivate Kernel kernel;\n\t@Inject\n\tprivate ResponseManager responseManager;\n\t@Inject\n\tprivate StanzaProcessor stanzaProcessor;\n\n\tpublic AbstractComponent() {\n\t}\n\n\t@Override\n\tpublic synchronized void everyMinute() {\n\t\tsuper.everyMinute();\n\t\tif (responseManager != null) {\n\t\t\tresponseManager.checkTimeouts();\n\t\t}\n\t}\n\n\t/**\n\t * Returns version of component. Used for Service Discovery purposes.\n\t *\n\t * @return version of component.\n\t */\n\tpublic abstract String getComponentVersion();\n\n\t@Override\n\tpublic Map<String, Object> getDefaults(Map<String, Object> params) {\n\t\tfinal Map<String, Object> props = super.getDefaults(params);\n\n\t\tMap<String, Class<? extends Module>> modules = getDefaultModulesList();\n\t\tif (modules != null) {\n\t\t\tfor (Entry<String, Class<? extends Module>> m : modules.entrySet()) {\n\t\t\t\tprops.put(\"modules/\" + m.getKey(), m.getValue());\n\t\t\t}\n\t\t}\n\n\t\treturn props;\n\t}\n\n\tpublic Kernel getKernel() {\n\t\treturn kernel;\n\t}\n\n\tpublic void setKernel(Kernel kernel) {\n\t\tthis.kernel = kernel;\n\t}\n\n\tpublic ResponseManager getResponseManager() {\n\t\treturn responseManager;\n\t}\n\n\tpublic void setResponseManager(ResponseManager responseManager) {\n\t\tthis.responseManager = responseManager;\n\t}\n\n\tpublic StanzaProcessor getStanzaProcessor() {\n\t\treturn stanzaProcessor;\n\t}\n\n\tpublic void setStanzaProcessor(StanzaProcessor stanzaProcessor) {\n\t\tthis.stanzaProcessor = stanzaProcessor;\n\t}\n\n\t@Override\n\tpublic void initBindings(Bindings binds) {\n\t\tsuper.initBindings(binds); // To change body of generated methods,\n\n\t\t// choose Tools | Templates.\n\t\tbinds.put(COMPONENT, this);\n\t}\n\n\t/**\n\t * Is this component discoverable by disco#items for domain by non admin users.\n\t *\n\t * @return <code>true</code> - if yes\n\t */\n\tpublic abstract boolean isDiscoNonAdmin();\n\n\t@Override\n\tpublic void processPacket(Packet packet) {\n\t\tstanzaProcessor.processPacket(packet);\n\t}\n\n\t@Override\n\tpublic void setProperties(Map<String, Object> props) throws ConfigurationException {\n\t\ttry {\n\t\t\tsuper.setProperties(props);\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Zjebalo sie\", e);\n\t\t}\n\t\ttry {\n\t\t\tinitModules(props);\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Can''t initialize modules!\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void updateServiceEntity() {\n\t\tsuper.updateServiceEntity();\n\t\tthis.updateServiceDiscoveryItem(getName(), null, getDiscoDescription(), !isDiscoNonAdmin());\n\t}\n\n\tEventBus getEventBus() {\n\t\treturn eventBus;\n\t}\n\n\tpublic void setEventBus(EventBus eventBus) {\n\t\tthis.eventBus = eventBus;\n\t}\n\n\tprotected void addOutPacket(Packet packet, AsyncCallback asyncCallback) {\n\t\taddOutPacket(packet);\n\t}\n\n\t/**\n\t * Returns default map of components. Keys in map are used as component identifiers.<br>\n\t * <br>\n\t * This map may be modified by <code>config.tdsl</code>:<br> <code>&lt;component_name&gt;/modules/&lt;module_name&gt;[S]=&lt;module_class&gt;</code>\n\t *\n\t * @return map of default modules.\n\t */\n\tprotected abstract Map<String, Class<? extends Module>> getDefaultModulesList();\n\n\tprotected void initModules(Map<String, Object> props)\n\t\t\tthrows InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {\n\t\tfor (Entry<String, Object> e : props.entrySet()) {\n\t\t\tif (e.getKey().startsWith(\"modules/\")) {\n\t\t\t\tfinal String id = e.getKey().substring(8);\n\t\t\t\tkernel.registerBean(id).asClass((Class<?>) e.getValue()).exec();\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static class DefaultPacketWriter\n\t\t\timplements PacketWriter {\n\n\t\tprotected final Logger log = Logger.getLogger(this.getClass().getName());\n\t\t@Inject(bean = \"service\")\n\t\tprivate AbstractComponent component;\n\t\t@Inject\n\t\tprivate ResponseManager responseManager;\n\n\t\tpublic AbstractComponent getComponent() {\n\t\t\treturn component;\n\t\t}\n\n\t\tpublic void setComponent(AbstractComponent component) {\n\t\t\tthis.component = component;\n\t\t}\n\n\t\tpublic ResponseManager getResponseManager() {\n\t\t\treturn responseManager;\n\t\t}\n\n\t\tpublic void setResponseManager(ResponseManager responseManager) {\n\t\t\tthis.responseManager = responseManager;\n\t\t}\n\n\t\t@Override\n\t\tpublic void write(Collection<Packet> elements) {\n\t\t\tif (elements != null) {\n\t\t\t\tfor (Packet element : elements) {\n\t\t\t\t\tif (element != null) {\n\t\t\t\t\t\twrite(element);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void write(Packet packet) {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.finer(\"Sent: \" + packet.getElement());\n\t\t\t}\n\t\t\tcomponent.addOutPacket(packet);\n\t\t}\n\n\t\t@Override\n\t\tpublic void write(Packet packet, AsyncCallback callback) {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.finer(\"Sent: \" + packet.getElement());\n\t\t\t}\n\t\t\tresponseManager.registerResponseHandler(packet, ResponseManager.DEFAULT_TIMEOUT, callback);\n\t\t\tcomponent.addOutPacket(packet, callback);\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/AbstractKernelBasedComponent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component;\n\nimport tigase.component.adhoc.AdHocCommandManager;\nimport tigase.component.modules.StanzaProcessor;\nimport tigase.component.modules.impl.config.ConfiguratorCommand;\nimport tigase.component.responses.AsyncCallback;\nimport tigase.component.responses.ResponseManager;\nimport tigase.disco.XMPPService;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.RegistrarBean;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.AbstractMessageReceiver;\nimport tigase.server.DisableDisco;\nimport tigase.server.Packet;\n\nimport javax.script.Bindings;\nimport javax.script.ScriptEngineManager;\nimport java.util.Collection;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic abstract class AbstractKernelBasedComponent\n\t\textends AbstractMessageReceiver\n\t\timplements XMPPService, DisableDisco, RegistrarBean {\n\n\tprotected final EventBus eventBus = EventBusFactory.getInstance();\n\tprotected final Logger log = Logger.getLogger(this.getClass().getName());\n\tprotected Kernel kernel = null;\n\t@Inject\n\tprivate StanzaProcessor stanzaProcessor;\n\n\tpublic String getComponentVersion() {\n\t\tString version = this.getClass().getPackage().getImplementationVersion();\n\t\treturn version == null ? \"0.0.0\" : version;\n\t}\n\n\tpublic Kernel getKernel() {\n\t\treturn this.kernel;\n\t}\n\n\t@Override\n\tpublic void initBindings(Bindings binds) {\n\t\tsuper.initBindings(binds);\n\t\tbinds.put(\"kernel\", kernel);\n\t}\n\n\t@Override\n\tpublic void start() {\n\t\tsuper.start();\n\t}\n\n\t/**\n\t * Is this component discoverable by disco#items for domain by non admin users.\n\t *\n\t * @return <code>true</code> - if yes\n\t */\n\tpublic abstract boolean isDiscoNonAdmin();\n\n\t@Override\n\tpublic void processPacket(Packet packet) {\n\t\tstanzaProcessor.processPacket(packet);\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\t\tthis.kernel = kernel;\n\n\t\t//kernel.registerBean(\"component\").asInstance(this).exec();\n\t\t//kernel.ln(\"service\", kernel, \"component\");\n\t\tkernel.registerBean(\"adHocCommandManager\").asClass(AdHocCommandManager.class).exec();\n\t\tkernel.registerBean(\"scriptCommandProcessor\").asClass(ComponenScriptCommandProcessor.class).exec();\n\t\tkernel.registerBean(\"writer\").asClass(DefaultPacketWriter.class).exec();\n\t\tkernel.registerBean(\"responseManager\").asClass(ResponseManager.class).exec();\n\t\tkernel.registerBean(\"stanzaProcessor\").asClass(StanzaProcessor.class).exec();\n\t\tkernel.registerBean(ConfiguratorCommand.class).exec();\n\n\t\tregisterModules(kernel);\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\t\tthis.kernel = null;\n\t}\n\n\t@Override\n\tpublic void updateServiceEntity() {\n\t\tsuper.updateServiceEntity();\n\t\tthis.updateServiceDiscoveryItem(getName(), null, getDiscoDescription(), !isDiscoNonAdmin());\n\t}\n\n\t@Override\n\tprotected ScriptEngineManager createScriptEngineManager() {\n\t\tScriptEngineManager result = super.createScriptEngineManager();\n\t\tresult.setBindings(new BindingsKernel(kernel));\n\t\treturn result;\n\t}\n\n\tprotected abstract void registerModules(Kernel kernel);\n\n\t@Bean(name = \"writer\", active = true)\n\tpublic static final class DefaultPacketWriter\n\t\t\timplements PacketWriter {\n\n\t\tprotected final Logger log = Logger.getLogger(this.getClass().getName());\n\t\t@Inject(nullAllowed = false, bean = \"service\")\n\t\tprivate AbstractKernelBasedComponent component;\n\t\t@Inject(nullAllowed = false)\n\t\tprivate ResponseManager responseManager;\n\n\t\t@Override\n\t\tpublic void write(Collection<Packet> elements) {\n\t\t\tif (elements != null) {\n\t\t\t\tfor (Packet element : elements) {\n\t\t\t\t\tif (element != null) {\n\t\t\t\t\t\twrite(element);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void write(Packet packet) {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.finer(\"Sent: \" + packet);\n\t\t\t}\n\t\t\tcomponent.addOutPacket(packet);\n\t\t}\n\n\t\t@Override\n\t\tpublic void write(Packet packet, AsyncCallback callback) {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.finer(\"Sent: \" + packet);\n\t\t\t}\n\t\t\tresponseManager.registerResponseHandler(packet, ResponseManager.DEFAULT_TIMEOUT, callback);\n\t\t\tcomponent.addOutPacket(packet);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/BackwardCompatibilityHelper.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component;\n\nimport tigase.conf.Configurable;\nimport tigase.db.AuthRepository;\nimport tigase.db.UserRepository;\nimport tigase.kernel.core.Kernel;\n\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Map;\n\n/**\n * Created by andrzej on 12.08.2016.\n */\npublic class BackwardCompatibilityHelper {\n\n\tpublic static Object convertToArray(Collection collection) {\n\t\tIterator iter = collection.iterator();\n\t\tif (!iter.hasNext()) {\n\t\t\treturn null;\n\t\t}\n\n\t\tClass objCls = iter.next().getClass();\n\t\tif (objCls == Integer.class) {\n\t\t\treturn convertToIntArray(collection);\n\t\t} else if (objCls == Long.class) {\n\t\t\treturn convertToLongArray(collection);\n\t\t} else if (objCls == Double.class) {\n\t\t\treturn convertToDoubleArray(collection);\n\t\t} else if (objCls == Float.class) {\n\t\t\treturn convertToFloatArray(collection);\n\t\t} else if (objCls == Boolean.class) {\n\t\t\treturn convertToBoolArray(collection);\n\t\t} else if (objCls == String.class) {\n\t\t\treturn convertToStringArray(collection);\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static Object convertToBoolArray(Collection col) {\n\t\tboolean[] arr = new boolean[col.size()];\n\t\tint pos = 0;\n\t\tIterator iter = col.iterator();\n\t\twhile (iter.hasNext()) {\n\t\t\tBoolean v = (Boolean) iter.next();\n\t\t\tarr[pos++] = v.booleanValue();\n\t\t}\n\t\treturn arr;\n\t}\n\n\tpublic static Object convertToDoubleArray(Collection col) {\n\t\tdouble[] arr = new double[col.size()];\n\t\tint pos = 0;\n\t\tIterator iter = col.iterator();\n\t\twhile (iter.hasNext()) {\n\t\t\tNumber v = (Number) iter.next();\n\t\t\tarr[pos++] = v.doubleValue();\n\t\t}\n\t\treturn arr;\n\t}\n\n\tpublic static Object convertToFloatArray(Collection col) {\n\t\tfloat[] arr = new float[col.size()];\n\t\tint pos = 0;\n\t\tIterator iter = col.iterator();\n\t\twhile (iter.hasNext()) {\n\t\t\tNumber v = (Number) iter.next();\n\t\t\tarr[pos++] = v.floatValue();\n\t\t}\n\t\treturn arr;\n\t}\n\n\tpublic static Object convertToIntArray(Collection col) {\n\t\tint[] arr = new int[col.size()];\n\t\tint pos = 0;\n\t\tIterator iter = col.iterator();\n\t\twhile (iter.hasNext()) {\n\t\t\tNumber v = (Number) iter.next();\n\t\t\tarr[pos++] = v.intValue();\n\t\t}\n\t\treturn arr;\n\t}\n\n\tpublic static Object convertToLongArray(Collection col) {\n\t\tlong[] arr = new long[col.size()];\n\t\tint pos = 0;\n\t\tIterator iter = col.iterator();\n\t\twhile (iter.hasNext()) {\n\t\t\tNumber v = (Number) iter.next();\n\t\t\tarr[pos++] = v.longValue();\n\t\t}\n\t\treturn arr;\n\t}\n\n\tpublic static Object convertToStringArray(Collection col) {\n\t\tString[] arr = new String[col.size()];\n\t\tint pos = 0;\n\t\tIterator iter = col.iterator();\n\t\twhile (iter.hasNext()) {\n\t\t\tString v = (String) iter.next();\n\t\t\tarr[pos++] = v;\n\t\t}\n\t\treturn arr;\n\t}\n\n\tpublic static Map<String, Object> fillProps(Map<String, Object> beanProperties) {\n\t\tMap<String, Object> result = new HashMap<>();\n\n\t\tfor (Map.Entry<String, Object> e : beanProperties.entrySet()) {\n\t\t\tString key = e.getKey();\n\t\t\tObject value = e.getValue();\n\t\t\tif (value instanceof Collection) {\n\t\t\t\tvalue = convertToArray((Collection) value);\n\t\t\t\tif (value != null) {\n\t\t\t\t\tresult.put(key, value);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (value instanceof Map) {\n\t\t\t\tString prefix = key;\n\t\t\t\tfor (Map.Entry<String, Object> e1 : ((Map<String, Object>) value).entrySet()) {\n\t\t\t\t\tresult.put(key + \"/\" + e1.getKey(), e1.getValue());\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tresult.put(key, value);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic static Map<String, Object> getDefConfigParams(Kernel kernel, String configType, String dbUri,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t Map<String, Object> params) {\n\t\tMap<String, Object> initProperties = new HashMap<>();\n\t\tinitProperties.put(\"config-type\", configType);\n\t\tfor (Map.Entry<String, Object> e : params.entrySet()) {\n\t\t\tif (e.getKey().startsWith(\"-\")) {\n\t\t\t\tinitProperties.put(e.getKey(), e.getValue());\n\t\t\t}\n\t\t}\n\n\t\t// Injecting default DB URI for backward compatibility\n\t\tinitProperties.put(Configurable.USER_REPO_URL_PROP_KEY, dbUri);\n\t\tinitProperties.put(Configurable.GEN_USER_DB_URI, dbUri);\n\t\tUserRepository userRepo = kernel.getInstance(UserRepository.class);\n\t\tinitProperties.put(Configurable.SHARED_USER_REPO_PROP_KEY, userRepo);\n\t\tAuthRepository authRepo = kernel.getInstance(AuthRepository.class);\n\t\tinitProperties.put(Configurable.SHARED_AUTH_REPO_PROP_KEY, authRepo);\n\n\t\treturn initProperties;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/BindingsKernel.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component;\n\nimport tigase.kernel.core.Kernel;\n\nimport javax.script.SimpleBindings;\n\npublic class BindingsKernel\n\t\textends SimpleBindings {\n\n\tprivate Kernel kernel;\n\n\tpublic BindingsKernel(Kernel kernel) {\n\t\tthis.kernel = kernel;\n\t}\n\n\tpublic BindingsKernel() {\n\t}\n\n\t@Override\n\tpublic boolean containsKey(Object key) {\n\t\tboolean v = super.containsKey(key);\n\t\tif (!v) {\n\t\t\tv = kernel.isBeanClassRegistered(key.toString());\n\t\t}\n\t\treturn v;\n\t}\n\n\t@Override\n\tpublic Object get(Object key) {\n\t\tObject v = super.get(key);\n\t\tif (v == null && kernel.isBeanClassRegistered(key.toString())) {\n\t\t\tv = kernel.getInstance(key.toString());\n\t\t}\n\t\treturn v;\n\t}\n\n\tpublic Kernel getKernel() {\n\t\treturn kernel;\n\t}\n\n\tpublic void setKernel(Kernel kernel) {\n\t\tthis.kernel = kernel;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/ComponenScriptCommandProcessor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component;\n\nimport tigase.component.modules.impl.AdHocCommandModule.ScriptCommandProcessor;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.BasicComponent;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.List;\nimport java.util.Queue;\n\n@Bean(name = \"scriptCommandProcessor\", active = true)\npublic class ComponenScriptCommandProcessor\n\t\timplements ScriptCommandProcessor {\n\n\t@Inject(bean = \"service\", nullAllowed = false)\n\tprivate BasicComponent component;\n\n\t@Override\n\tpublic List<Element> getScriptItems(String node, JID jid, JID from) {\n\t\treturn component.getScriptItems(node, jid, from);\n\t}\n\n\t@Override\n\tpublic boolean processScriptCommand(Packet pc, Queue<Packet> results) {\n\t\treturn component.processScriptCommand(pc, results);\n\t}\n\n\t@Override\n\tpublic boolean isAllowed(String node, JID from) {\n\t\treturn component.canCallCommand(from, node);\n\t}\n\n\t@Override\n\tpublic boolean isAllowed(String node, String domain, JID from) {\n\t\treturn component.canCallCommand(from, domain, node);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/DSLBeanConfigurator.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component;\n\nimport tigase.conf.ConfigHolder;\nimport tigase.conf.ConfigWriter;\nimport tigase.db.util.JDBCPasswordObfuscator;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.RegistrarBean;\nimport tigase.kernel.beans.config.*;\nimport tigase.kernel.core.BeanConfig;\nimport tigase.kernel.core.DependencyManager;\nimport tigase.kernel.core.Kernel;\nimport tigase.osgi.ModulesManagerImpl;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.Writer;\nimport java.lang.reflect.Field;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\n/**\n * Created by andrzej on 11.08.2016.\n */\n@Bean(name = BeanConfigurator.DEFAULT_CONFIGURATOR_NAME, active = true)\npublic class DSLBeanConfigurator\n\t\textends AbstractBeanConfigurator {\n\n\tprivate static final Logger log = Logger.getLogger(DSLBeanConfigurator.class.getCanonicalName());\n\tprivate ConfigHolder configHolder;\n\n\tprivate Map<String, Object> props;\n\n\t@Override\n\tpublic Map<String, Object> getConfiguration(BeanConfig beanConfig) {\n\t\tif (props == null) {\n\t\t\treturn new HashMap<>();\n\t\t}\n\n\t\tMap<String, String> aliassesToFields = getFieldAliasses(beanConfig);\n\n\t\treturn getBeanConfigurationProperties(beanConfig, aliassesToFields);\n\t}\n\n\tpublic Map<String, Object> getProperties() {\n\t\treturn props;\n\t}\n\n\tpublic void setProperties(Map<String, Object> props) {\n\t\tthis.props = props;\n\t}\n\n\tpublic ConfigHolder getConfigHolder() {\n\t\treturn configHolder;\n\t}\n\n\tpublic void setConfigHolder(ConfigHolder config) {\n\t\tthis.configHolder = config;\n\t\tsetProperties(config.getProperties());\n\t}\n\n\tpublic void dumpConfiguration(File f) throws IOException {\n\t\tlog.log(Level.INFO, \"Dumping full server configuration to: {0}\", f);\n\t\tMap<String, Object> dump = new LinkedHashMap<>(props);\n\t\tdumpConfiguration(dump, kernel);\n\n\t\tnew ConfigWriter().resolveVariables().write(f, dump);\n\t}\n\n\tpublic void dumpConfiguration(Writer writer) throws IOException {\n\t\tMap<String, Object> dump = new LinkedHashMap<>(props);\n\t\tdumpConfiguration(dump, kernel);\n\n\t\tnew ConfigWriter().resolveVariables().write(writer, dump);\n\t}\n\n\tprotected boolean hasDirectConfiguration(BeanConfig beanConfig) {\n\t\tArrayDeque<String> kernels = getBeanConfigPath(beanConfig);\n\t\tMap<String, Object> result = props;\n\n\t\tString name;\n\t\twhile (result != null && (name = kernels.poll()) != null) {\n\t\t\tObject r = result.get(name);\n\t\t\tif (r instanceof Map) {\n\t\t\t\tresult = (Map<String, Object>) r;\n\t\t\t} else {\n\t\t\t\tresult = null;\n\t\t\t}\n\t\t}\n\n\t\treturn result != null;\n\t}\n\n\tprotected Map<String, Object> getBeanConfigurationProperties(BeanConfig beanConfig,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Map<String, String> aliasesToFields) {\n\t\tHashMap<String, Object> result = new HashMap<>();\n\t\tArrayDeque<String> path = getBeanConfigPath(beanConfig);\n\t\tQueue<Map<String, Object>> configPath = new ArrayDeque<>();\n\t\tMap<String, Object> props = this.props;\n\t\tconfigPath.add(props);\n\n\t\tString name;\n\t\twhile ((name = path.poll()) != null) {\n\t\t\tprops = (Map<String, Object>) props.get(name);\n\t\t\tif (props == null) {\n\t\t\t\tconfigPath.offer(Collections.emptyMap());\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tconfigPath.offer(props);\n\t\t}\n\n\t\twhile ((props = configPath.poll()) != null) {\n\t\t\tfor (Map.Entry<String, Object> e : props.entrySet()) {\n\t\t\t\tif (configPath.isEmpty()) {\n\t\t\t\t\tString fieldName = aliasesToFields.get(e.getKey());\n\t\t\t\t\tif (fieldName != null) {\n\t\t\t\t\t\tresult.put(fieldName, e.getValue());\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult.put(e.getKey(), e.getValue());\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\t\t\t\t\tString fieldName = aliasesToFields.get(e.getKey());\n\t\t\t\t\tif (fieldName != null) {\n\t\t\t\t\t\tresult.put(fieldName, e.getValue());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tresult.put(\"name\", beanConfig.getBeanName());\n\n\t\treturn result;\n\t}\n\n\tprotected Map<String, String> getFieldAliasses(BeanConfig beanConfig) {\n\t\tMap<String, String> configAliasses = new HashMap<>();\n\t\tClass<?> cls = beanConfig.getClazz();\n\n\t\tField[] fields = DependencyManager.getAllFields(cls);\n\t\tfor (Field field : fields) {\n\t\t\tConfigField cf = field.getAnnotation(ConfigField.class);\n\t\t\tif (cf != null) {\n\t\t\t\tif (!cf.alias().isEmpty()) {\n\t\t\t\t\tconfigAliasses.put(cf.alias(), field.getName());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tdo {\n\t\t\tConfigAliases ca = cls.getAnnotation(ConfigAliases.class);\n\t\t\tif (ca != null) {\n\t\t\t\tfor (ConfigAlias a : ca.value()) {\n\t\t\t\t\tconfigAliasses.put(a.alias(), a.field());\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} while ((cls = cls.getSuperclass()) != null);\n\t\treturn configAliasses;\n\t}\n\n\t@Override\n\tprotected Map<String, BeanDefinition> getBeanDefinitions(Map<String, Object> values) {\n\t\tMap<String, BeanDefinition> beanDefinitions = super.getBeanDefinitions(values);\n\n\t\tfor (Map.Entry<String, Object> e : values.entrySet()) {\n\t\t\tif (e.getValue() instanceof BeanDefinition) {\n\t\t\t\tbeanDefinitions.put(e.getKey(), (BeanDefinition) e.getValue());\n\t\t\t}\n\t\t}\n\n\t\treturn beanDefinitions;\n\t}\n\n\tprivate void dumpConfiguration(Map<String, Object> dump, Kernel kernel) {\n\t\tList<BeanConfig> beansToDump = kernel.getDependencyManager()\n\t\t\t\t.getBeanConfigs()\n\t\t\t\t.stream()\n\t\t\t\t.filter(bc -> !Kernel.class.isAssignableFrom(bc.getClazz()) &&\n\t\t\t\t\t\t!(bc instanceof Kernel.DelegatedBeanConfig))\n\t\t\t\t.collect(Collectors.toList());\n\n\t\tfor (BeanConfig bc : beansToDump) {\n\t\t\tBeanDefinition forBean = getBeanDefinitionFromDump(dump, bc.getBeanName());\n\n\t\t\tif (forBean.getClazzName() == null) {\n\t\t\t\tforBean.setClazzName(bc.getClazz().getName());\n\t\t\t}\n\t\t\tforBean.setActive(bc.getState() != BeanConfig.State.inactive);\n\t\t\tforBean.setExportable(bc.isExportable());\n\n\t\t\ttry {\n\t\t\t\tif (forBean.isActive()) {\n\t\t\t\t\tif (RegistrarBean.class.isAssignableFrom(bc.getClazz())) {\n\t\t\t\t\t\tKernel subkernel = bc.getKernel();\n\t\t\t\t\t\tif (subkernel != kernel) {\n\t\t\t\t\t\t\tdumpConfiguration(forBean, subkernel);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tObject bean = bc.getKernel().getInstanceIfExistsOr(bc.getBeanName(), bc1 -> {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\treturn bc1.getClazz().newInstance();\n\t\t\t\t\t\t} catch (InstantiationException | IllegalAccessException e) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"failed to instantiate class for retrieval of default configuration\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t});\n\n\t\t\t\t\tMap<Field, Object> defaults = grabCurrentConfig(bean, bc.getBeanName());\n\t\t\t\t\tMap<String, Object> cfg =\n\t\t\t\t\t\t\tbc.getState() != BeanConfig.State.initialized ? getConfiguration(bc) : null;\n\t\t\t\t\tSet<String> validProps = new HashSet<>();\n\t\t\t\t\tif (defaults != null) {\n\t\t\t\t\t\tdefaults.forEach((field, defaultValue) -> {\n\t\t\t\t\t\t\tConfigField cf = field.getAnnotation(ConfigField.class);\n\t\t\t\t\t\t\tObject configuredValue = null;\n\t\t\t\t\t\t\tif (cfg != null) {\n\t\t\t\t\t\t\t\tconfiguredValue = cfg.get(field.getName());\n\t\t\t\t\t\t\t\tif (configuredValue == null && cf != null && !cf.alias().isEmpty()) {\n\t\t\t\t\t\t\t\t\tconfiguredValue = cfg.get(cf.alias());\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tString prop = (cf == null || cf.alias().isEmpty()) ? field.getName() : cf.alias();\n\t\t\t\t\t\t\tObject value = configuredValue == null ? defaultValue : configuredValue;\n\n\t\t\t\t\t\t\tif (cf.type() != null && value != null) {\n\t\t\t\t\t\t\t\tif (cf.type().equals(ConfigFieldType.Password) ) {\n\t\t\t\t\t\t\t\t\tvalue = \"*\".repeat((Objects.toString(value)).length());\n\t\t\t\t\t\t\t\t} else if (cf.type().equals(ConfigFieldType.JdbcUrl)) {\n\t\t\t\t\t\t\t\t\tvalue = JDBCPasswordObfuscator.obfuscatePassword(Objects.toString(value));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tforBean.put(prop, value);\n\t\t\t\t\t\t\tvalidProps.add(prop);\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tnew ArrayList<Map.Entry>(forBean.entrySet()).stream()\n\t\t\t\t\t\t\t.filter(e -> !validProps.contains(e.getKey()))\n\t\t\t\t\t\t\t.filter(e -> !(e.getValue() instanceof BeanDefinition))\n\t\t\t\t\t\t\t.map(e -> e.getKey())\n\t\t\t\t\t\t\t.forEach(forBean::remove);\n\t\t\t\t\tforBean.remove(\"name\");\n\t\t\t\t} else {\n\t\t\t\t\tdumpConfigFromSubBeans(forBean, kernel);\n\t\t\t\t}\n\t\t\t} catch (Exception ex) {\n\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\"failed to retrieve default values for bean \" + bc.getBeanName() + \", class = \" + bc.getClazz(),\n\t\t\t\t\t\tex);\n\t\t\t}\n\t\t}\n\n//\t\tList<BeanConfig> kernelBeans = kernel.getDependencyManager().getBeanConfigs().stream()\n//\t\t\t\t.filter(bc -> Kernel.class.isAssignableFrom(bc.getClazz()))\n//\t\t\t\t.filter(bc -> bc.getState() == BeanConfig.State.initialized)\n//\t\t\t\t.collect(Collectors.toList());\n//\t\tfor (BeanConfig bc : kernelBeans) {\n//\t\t\tKernel subkernel = kernel.getInstance(bc.getBeanName());\n//\t\t\tif (subkernel == kernel)\n//\t\t\t\tcontinue;\n//\t\t\t----- here are added new entries!\n//\t\t\tBeanDefinition forKernel = getBeanDefinitionFromDump(dump, subkernel.getName());\n//\t\t\t----- and they are not removed here!\n//\t\t\tdumpConfiguration(forKernel, subkernel);\n//\t\t}\n\t}\n\n\tprivate void dumpConfigFromSubBeans(BeanDefinition beanDef, Kernel kernel) {\n\t\ttry {\n\t\t\tObject bean = ModulesManagerImpl.getInstance().forName(beanDef.getClazzName()).newInstance();\n\n\t\t\tMap<String, Object> cfg = new HashMap<>(beanDef);\n\n\t\t\tMap<Field, Object> defaults = grabCurrentConfig(bean, beanDef.getBeanName());\n\t\t\tif (defaults != null) {\n\t\t\t\tSet<String> validProps = new HashSet<>();\n\t\t\t\tdefaults.forEach((field, v) -> {\n\t\t\t\t\tConfigField cf = field.getAnnotation(ConfigField.class);\n\t\t\t\t\tObject v1 = beanDef.remove(field.getName());\n\t\t\t\t\tif (v1 == null) {\n\t\t\t\t\t\tv1 = beanDef.remove(cf.alias());\n\t\t\t\t\t}\n\t\t\t\t\tString prop = (cf == null || cf.alias().isEmpty()) ? field.getName() : cf.alias();\n\t\t\t\t\tbeanDef.put(prop, v1 == null ? v : v1);\n\t\t\t\t\tvalidProps.add(prop);\n\t\t\t\t});\n\t\t\t\tnew ArrayList<Map.Entry>(beanDef.entrySet()).stream()\n\t\t\t\t\t\t.filter(e -> !validProps.contains(e.getKey()))\n\t\t\t\t\t\t.filter(e -> !(e.getValue() instanceof BeanDefinition))\n\t\t\t\t\t\t.map(e -> e.getKey())\n\t\t\t\t\t\t.forEach(beanDef::remove);\n\t\t\t}\n\n\t\t\tbeanDef.remove(\"name\");\n\n\t\t\tif (RegistrarBean.class.isAssignableFrom(bean.getClass())) {\n\t\t\t\tKernel tmpKernel = new Kernel(\"DLSConfiguratorTmpKernel\") {\n//\t\t\t\t\t@Override\n//\t\t\t\t\tprotected BeanConfig registerBean(BeanConfig beanConfig, BeanConfig factoryBeanConfig,\n//\t\t\t\t\t\t\t\t\t\t\t\t\t  Object beanInstance) {\n//\t\t\t\t\t\treturn null;\n//\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void registerLinks(String beanName) {\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic <T> T getInstance(String beanName) {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tprotected <T> T getInstance(Class<T> beanClass, boolean allowNonExportable) {\n\t\t\t\t\t\tif (AbstractBeanConfigurator.class.isAssignableFrom(beanClass)) {\n\t\t\t\t\t\t\treturn (T) DSLBeanConfigurator.this;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tprotected void injectIfRequired(BeanConfig beanConfig) {\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\ttmpKernel.registerBean(AbstractBeanConfigurator.DEFAULT_CONFIGURATOR_NAME)\n\t\t\t\t\t\t.asInstance(this)\n\t\t\t\t\t\t.exportable()\n\t\t\t\t\t\t.exec();\n\t\t\t\tif (bean instanceof RegistrarBean) {\n\t\t\t\t\t((RegistrarBean) bean).register(tmpKernel);\n\t\t\t\t}\n\n\t\t\t\tfinal Map<String, Class<?>> subbeans = new HashMap<>();\n\t\t\t\ttmpKernel.getDependencyManager()\n\t\t\t\t\t\t.getBeanConfigs()\n\t\t\t\t\t\t.stream()\n\t\t\t\t\t\t.filter(bc -> !Kernel.class.isAssignableFrom(bc.getClazz()))\n\t\t\t\t\t\t.forEach(bc -> subbeans.put(bc.getBeanName(), bc.getClazz()));\n\t\t\t\tsubbeans.putAll(getBeanClassesFromAnnotations(kernel, bean.getClass()));\n\t\t\t\tMap<String, BeanDefinition> beansFromConfig = mergeWithBeansPropertyValue(getBeanDefinitions(beanDef),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  beanDef);\n\t\t\t\tsubbeans.entrySet().stream().filter(e -> !beansFromConfig.containsKey(e.getKey())).map(e -> {\n\t\t\t\t\tBeanDefinition def = new BeanDefinition();\n\t\t\t\t\tdef.setBeanName(e.getKey());\n\t\t\t\t\tdef.setClazzName(e.getValue().getName());\n\n\t\t\t\t\tBean b = e.getValue().getAnnotation(Bean.class);\n\t\t\t\t\tif (b != null) {\n\t\t\t\t\t\tdef.setActive(b.active());\n\t\t\t\t\t}\n\n\t\t\t\t\tObject tmp = beanDef.get(def.getBeanName());\n\t\t\t\t\tcfg.entrySet()\n\t\t\t\t\t\t\t.stream()\n\t\t\t\t\t\t\t.filter(x -> !(x.getValue() instanceof BeanDefinition))\n\t\t\t\t\t\t\t.forEach(x -> def.put(x.getKey(), x.getValue()));\n\n\t\t\t\t\tif (tmp != null && tmp instanceof Map) {\n\t\t\t\t\t\tdef.putAll((Map<String, Object>) tmp);\n\t\t\t\t\t}\n\t\t\t\t\tbeanDef.put(def.getBeanName(), def);\n\t\t\t\t\treturn def;\n\t\t\t\t}).forEach(def -> {\n\t\t\t\t\tdumpConfigFromSubBeans(def, kernel);\n\t\t\t\t});\n\t\t\t\tbeansFromConfig.values().stream().map(def -> {\n\t\t\t\t\tif (def.getClazzName() == null) {\n\t\t\t\t\t\tClass x = subbeans.get(def.getBeanName());\n\t\t\t\t\t\tif (x != null) {\n\t\t\t\t\t\t\tdef.setClazzName(x.getName());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn def;\n\t\t\t\t}).forEach(def -> dumpConfigFromSubBeans(def, kernel));\n\t\t\t}\n\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.FINEST, \"exception retrieving configuration of subbeans = \" + beanDef.getBeanName(), ex);\n\t\t}\n\t}\n\n\tprivate BeanDefinition getBeanDefinitionFromDump(Map<String, Object> dump, String name) {\n\t\tObject tmp = dump.get(name);\n\n\t\tif (tmp == null || (!(tmp instanceof BeanDefinition))) {\n\t\t\tBeanDefinition def = new BeanDefinition();\n\t\t\tdef.setBeanName(name);\n\t\t\tdump.entrySet()\n\t\t\t\t\t.stream()\n\t\t\t\t\t.filter(e -> !(e.getValue() instanceof BeanDefinition))\n\t\t\t\t\t.forEach(e -> def.putIfAbsent(e.getKey(), e.getValue()));\n\t\t\tif (tmp != null && tmp instanceof Map) {\n\t\t\t\tdef.putAll((Map<String, Object>) tmp);\n\t\t\t}\n\t\t\tdump.put(name, def);\n\t\t\ttmp = def;\n\t\t} else {\n\t\t\tBeanDefinition def = new BeanDefinition((BeanDefinition) tmp);\n\t\t\tdump.entrySet()\n\t\t\t\t\t.stream()\n\t\t\t\t\t.filter(e -> !(e.getValue() instanceof BeanDefinition))\n\t\t\t\t\t.forEach(e -> def.putIfAbsent(e.getKey(), e.getValue()));\n\t\t\tdump.put(name, def);\n\t\t\ttmp = def;\n\t\t}\n\n\t\treturn (BeanDefinition) tmp;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/DSLBeanConfiguratorWithBackwardCompatibility.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component;\n\nimport tigase.conf.Configurable;\nimport tigase.conf.ConfigurationException;\nimport tigase.db.comp.ComponentRepository;\nimport tigase.kernel.core.BeanConfig;\n\nimport java.lang.reflect.Method;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.component.BackwardCompatibilityHelper.getDefConfigParams;\nimport static tigase.conf.Configurable.GEN_CONFIG_DEF;\n\n/**\n * Created by andrzej on 12.08.2016.\n */\npublic class DSLBeanConfiguratorWithBackwardCompatibility\n\t\textends DSLBeanConfigurator {\n\n\tprivate static final Logger log = Logger.getLogger(\n\t\t\tDSLBeanConfiguratorWithBackwardCompatibility.class.getCanonicalName());\n\n\t@Override\n\tpublic void configure(BeanConfig beanConfig, Object bean, Map<String, Object> values) {\n\t\t// execute bean based configuration\n\t\tsuper.configure(beanConfig, bean, values);\n\n\t\ttry {\n\t\t\tif (bean instanceof Configurable) {\n\t\t\t\tConfigurable conf = (Configurable) bean;\n\t\t\t\tMethod getDefaultsMethod = bean.getClass().getMethod(\"getDefaults\", Map.class);\n\t\t\t\tMethod setPropertiesMethod = bean.getClass().getMethod(\"setProperties\", Map.class);\n\n\t\t\t\tif (getDefaultsMethod != null && getDefaultsMethod.getAnnotation(Deprecated.class) == null) {\n\t\t\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\t\t\"Class {0} is using deprecated configuration using methods getDefaults() and setProperties()\",\n\t\t\t\t\t\t\tbean.getClass().getCanonicalName());\n\t\t\t\t}\n\n\t\t\t\tif (setPropertiesMethod != null && setPropertiesMethod.getAnnotation(Deprecated.class) == null) {\n\t\t\t\t\tString configType = (String) getProperties().getOrDefault(\"config-type\", GEN_CONFIG_DEF);\n\t\t\t\t\tString dbUri = getPropertyAtPath(\"dataSource\", \"repo-uri\");\n\t\t\t\t\tMap<String, Object> params = getDefConfigParams(getKernel(), configType, dbUri, getProperties());\n\t\t\t\t\tMap<String, Object> props = new HashMap<>(params);\n\n\t\t\t\t\tprops.putAll(conf.getDefaults(params));\n\t\t\t\t\tfillProps(beanConfig, props);\n\t\t\t\t\t((Configurable) bean).setProperties(props);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (bean instanceof ComponentRepository) {\n\t\t\t\tComponentRepository conf = (ComponentRepository) bean;\n\t\t\t\tMethod getDefaultsMethod = bean.getClass().getMethod(\"getDefaults\", Map.class, Map.class);\n\t\t\t\tMethod setPropertiesMethod = bean.getClass().getMethod(\"setProperties\", Map.class);\n\n\t\t\t\tif (getDefaultsMethod != null && getDefaultsMethod.getAnnotation(Deprecated.class) == null) {\n\t\t\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\t\t\"Class {0} is using deprecated configuration using methods getDefaults() and setProperties()\",\n\t\t\t\t\t\t\tbean.getClass().getCanonicalName());\n\t\t\t\t}\n\t\t\t\tif (setPropertiesMethod != null && setPropertiesMethod.getAnnotation(Deprecated.class) == null) {\n\t\t\t\t\tString configType = (String) getProperties().getOrDefault(\"config-type\", GEN_CONFIG_DEF);\n\t\t\t\t\tString dbUri = getPropertyAtPath(\"dataSource\", \"repo-uri\");\n\t\t\t\t\tMap<String, Object> params = getDefConfigParams(getKernel(), configType, dbUri, getProperties());\n\t\t\t\t\tMap<String, Object> props = new HashMap<>();\n\n\t\t\t\t\tconf.getDefaults(props, params);\n\t\t\t\t\tfillProps(beanConfig, props);\n\t\t\t\t\t((ComponentRepository) bean).setProperties(props);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (NoSuchMethodException ex) {\n\t\t\t// method getDefaults() not found - this should not happen\n\t\t} catch (ConfigurationException ex) {\n\t\t\tthrow new RuntimeException(\"Could not configure bean \" + beanConfig.getBeanName(), ex);\n\t\t}\n\t}\n\n\tpublic Map<String, Object> getBeanConfigurationProperties(BeanConfig beanConfig) {\n\t\treturn getBeanConfigurationProperties(beanConfig, Collections.emptyMap());\n\t}\n\n\tprivate void fillProps(BeanConfig beanConfig, Map<String, Object> result) {\n\t\tMap<String, Object> props = getBeanConfigurationProperties(beanConfig);\n\t\tresult.putAll(BackwardCompatibilityHelper.fillProps(props));\n\t}\n\n\tprivate <T> T getPropertyAtPath(String... path) {\n\t\tObject val = getProperties();\n\t\tfor (int i = 0; i < path.length; i++) {\n\t\t\tval = ((Map<String, Object>) val).get(path[i]);\n\t\t\tif (val == null) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn (T) val;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/PacketWriter.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component;\n\nimport tigase.component.responses.AsyncCallback;\nimport tigase.server.Packet;\n\nimport java.util.Collection;\n\n/**\n * Interface for writing {@linkplain Packet Packets} to XMPP stream.\n *\n * @author bmalkow\n */\npublic interface PacketWriter {\n\n\t/**\n\t * Writes collection of {@linkplain Packet Packets}.\n\t *\n\t * @param packets collection of {@linkplain Packet Packets} to be written.\n\t */\n\tvoid write(Collection<Packet> packets);\n\n\t/**\n\t * Writes single {@linkplain Packet}.\n\t *\n\t * @param packet {@link Packet} to be written.\n\t */\n\tvoid write(final Packet packet);\n\n\tpublic void write(Packet packet, AsyncCallback callback);\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/ScheduledTask.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component;\n\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.beans.config.ConfigurationChangedAware;\nimport tigase.server.AbstractMessageReceiver;\nimport tigase.util.common.TimerTask;\n\nimport java.time.Duration;\nimport java.util.Collection;\n\n/**\n * Abstract class extending TimerTask which allows easy configuration and usage of TimerTask as a bean\n * <br>\n * Created by andrzej on 09.08.2016.\n */\npublic abstract class ScheduledTask\n\t\textends TimerTask\n\t\timplements ConfigurationChangedAware, Initializable, UnregisterAware {\n\n\t@Inject(bean = \"service\")\n\tprotected AbstractMessageReceiver component;\n\t@ConfigField(desc = \"Delay\", alias = \"delay\")\n\tprivate Duration delay;\n\t@ConfigField(desc = \"Execution period\", alias = \"period\")\n\tprivate Duration period;\n\n\t/**\n\t * Default constructor allows providing default values\n\t *\n\t * @param delay default value of delay Duration\n\t * @param period default value of period Duration\n\t */\n\tpublic ScheduledTask(Duration delay, Duration period) {\n\t\tthis.period = period;\n\t\tthis.delay = delay;\n\t}\n\n\t@Override\n\tpublic void beanConfigurationChanged(Collection<String> changedFields) {\n\t\tif (changedFields.contains(\"period\") || changedFields.contains(\"delay\")) {\n\t\t\treset(true);\n\t\t\tinitialize();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tif (component == null || !component.isInitializationComplete()) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (delay == null && period == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (delay != null && delay.isZero() && period != null && period.isZero()) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (delay != null) {\n\t\t\tif (period != null) {\n\t\t\t\tcomponent.addTimerTask(this, delay.toMillis(), period.toMillis());\n\t\t\t} else {\n\t\t\t\tcomponent.addTimerTask(this, delay.toMillis());\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\tcancel();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/adhoc/AdHocCommand.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component.adhoc;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.server.CmdAcl;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Optional;\nimport java.util.function.Consumer;\n\npublic interface AdHocCommand {\n\t\n\tvoid execute(final AdhHocRequest request, AdHocResponse response) throws AdHocCommandException;\n\n\tdefault void execute(final AdhHocRequest request, AdHocResponse response, Runnable completionHandler, Consumer<AdHocCommandException> exceptionHandler) throws AdHocCommandException {\n\t\texecute(request, response);\n\t\tcompletionHandler.run();\n\t}\n\n\tString getName();\n\n\tString getNode();\n\n\tdefault Optional<String> getGroup() {\n\t\treturn Optional.empty();\n\t}\n\n\t@TigaseDeprecated(since = \"8.5.0\", removeIn = \"9.0.0\")\n\t@Deprecated\n\tboolean isAllowedFor(JID jid);\n\n\tdefault boolean isAllowedFor(JID from, JID to) {\n\t\treturn isAllowedFor(from);\n\t}\n\n\tdefault boolean isForSelf() {\n\t\treturn false;\n\t}\n\n\tdefault CmdAcl getDefaultACL() {\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/adhoc/AdHocCommandException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component.adhoc;\n\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\n\npublic class AdHocCommandException\n\t\textends Exception {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate Authorization errorCondition;\n\n\tprivate Element item;\n\n\tprivate String message;\n\n\tprivate String xmlns = \"urn:ietf:params:xml:ns:xmpp-stanzas\";\n\n\tpublic AdHocCommandException(final Authorization errorCondition) {\n\t\tthis(null, errorCondition, (String) null);\n\t}\n\n\tpublic AdHocCommandException(final Authorization errorCondition, String message) {\n\t\tthis(null, errorCondition, message);\n\t}\n\n\tpublic AdHocCommandException(final Element item, final Authorization errorCondition) {\n\t\tthis(item, errorCondition, (String) null);\n\t}\n\n\tpublic AdHocCommandException(final Element item, final Authorization errorCondition, final String message) {\n\t\tthis.item = item;\n\t\tthis.errorCondition = errorCondition;\n\t\tthis.message = message;\n\t}\n\n\t/**\n\t * @return Returns the code.\n\t */\n\tpublic String getCode() {\n\t\treturn String.valueOf(this.errorCondition.getErrorCode());\n\t}\n\n\tpublic Authorization getErrorCondition() {\n\t\treturn errorCondition;\n\t}\n\n\t/**\n\t * @return Returns the item.\n\t */\n\tpublic Element getItem() {\n\t\treturn item;\n\t}\n\n\t@Override\n\tpublic String getMessage() {\n\t\treturn message;\n\t}\n\n\t/**\n\t * @return Returns the name.\n\t */\n\tpublic String getName() {\n\t\treturn errorCondition.getCondition();\n\t}\n\n\t/**\n\t * @return Returns the type.\n\t */\n\tpublic String getType() {\n\t\treturn errorCondition.getErrorType();\n\t}\n\n\tpublic Element makeElement() {\n\t\treturn makeElement(true);\n\t}\n\n\tpublic Element makeElement(boolean insertOriginal) {\n\t\tElement answer = insertOriginal ? item.clone() : new Element(item.getName());\n\n\t\tanswer.addAttribute(\"id\", item.getAttributeStaticStr(\"id\"));\n\t\tanswer.addAttribute(\"type\", \"error\");\n\t\tanswer.addAttribute(\"to\", item.getAttributeStaticStr(\"from\"));\n\t\tanswer.addAttribute(\"from\", item.getAttributeStaticStr(\"to\"));\n\t\tif (this.message != null) {\n\t\t\tElement text = new Element(\"text\", this.message, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t   new String[]{\"urn:ietf:params:xml:ns:xmpp-stanzas\"});\n\n\t\t\tanswer.addChild(text);\n\t\t}\n\t\tanswer.addChild(makeErrorElement());\n\n\t\treturn answer;\n\t}\n\n\tpublic Element makeElement(Element sourceElement) {\n\t\tthis.item = sourceElement;\n\n\t\treturn makeElement(true);\n\t}\n\n\tpublic Element makeErrorElement() {\n\t\tElement error = new Element(\"error\");\n\n\t\terror.setAttribute(\"code\", String.valueOf(this.errorCondition.getErrorCode()));\n\t\terror.setAttribute(\"type\", this.errorCondition.getErrorType());\n\t\terror.addChild(new Element(this.errorCondition.getCondition(), new String[]{\"xmlns\"}, new String[]{xmlns}));\n\n\t\treturn error;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/adhoc/AdHocCommandManager.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component.adhoc;\n\nimport tigase.component.adhoc.AdHocResponse.State;\nimport tigase.component.exceptions.ComponentException;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Packet;\nimport tigase.util.cache.SimpleCacheSynchronized;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.PacketErrorTypeException;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Consumer;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Bean(name = \"adHocCommandManager\", active = true)\npublic class AdHocCommandManager {\n\n\tprivate static final Logger log = Logger.getLogger(AdHocCommandManager.class.getCanonicalName());\n\n\tprivate final Map<String, AdHocCommand> commands = new HashMap<String, AdHocCommand>();\n\tprivate final SimpleCacheSynchronized<String, AdHocSession> sessions = new SimpleCacheSynchronized<>(100, 10 * 1000);\n\t@Inject(nullAllowed = true)\n\tprivate AdHocCommand[] allCommands;\n\n\tpublic Collection<AdHocCommand> getAllCommands() {\n\t\treturn this.commands.values();\n\t}\n\n\tpublic void setAllCommands(AdHocCommand[] allCommands) {\n\t\tthis.allCommands = allCommands;\n\t\tthis.commands.clear();\n\t\tif (allCommands != null) {\n\t\t\tfor (AdHocCommand adHocCommand : allCommands) {\n\t\t\t\tthis.commands.put(adHocCommand.getNode(), adHocCommand);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic AdHocCommand getCommand(String nodeName) {\n\t\treturn this.commands.get(nodeName);\n\t}\n\n\t/**\n\t * Method checks if exists implementation for this command in this CommandManager\n\t *\n\t * @param node name for which perform the check\n\t *\n\t * @return true - if command exists for this node\n\t */\n\tpublic boolean hasCommand(String node) {\n\t\treturn this.commands.containsKey(node);\n\t}\n\n\tpublic void process(Packet packet, Consumer<Packet> resultConsumer) throws AdHocCommandException {\n\t\tfinal Element element = packet.getElement();\n\t\t@SuppressWarnings(\"unused\") final JID senderJid = packet.getStanzaFrom();\n\t\tfinal Element command = element.getChild(\"command\", \"http://jabber.org/protocol/commands\");\n\t\tfinal String node = command.getAttributeStaticStr(\"node\");\n\t\tfinal String action = command.getAttributeStaticStr(\"action\");\n\t\tfinal String sessionId = command.getAttributeStaticStr(\"sessionid\");\n\t\tAdHocCommand adHocCommand = getCommand(node);\n\n\t\tif (adHocCommand == null) {\n\t\t} else {\n\t\t\tboolean selfQuery = packet.getStanzaTo() == null || (senderJid.getBareJID().equals(packet.getStanzaTo().getBareJID()) && packet.getStanzaTo().getResource() == null);\n\t\t\tif (selfQuery == adHocCommand.isForSelf() && adHocCommand.isAllowedFor(senderJid, packet.getStanzaTo())) {\n\t\t\t\tprocess(packet, command, node, action, sessionId, adHocCommand, resultConsumer);\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tPacket errorResponse = new ComponentException(Authorization.NOT_ALLOWED, \"You are not allowed to execute this command!\").makeElement(packet, true);\n\t\t\t\t\tresultConsumer.accept(errorResponse);\n\t\t\t\t} catch (PacketErrorTypeException e1) {\n\t\t\t\t\tif (log.isLoggable(Level.WARNING)) {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Problem during generate error response\", e1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void process(Packet packet, Element commandElement, String node, String action, String sessionId,\n\t\t\t\t\t\t  AdHocCommand adHocCommand, Consumer<Packet> resultConsumer) throws AdHocCommandException {\n\t\tState currentState = null;\n\t\tfinal AdhHocRequest request = new AdhHocRequest(packet, commandElement, node, packet.getStanzaFrom(), action,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsessionId);\n\t\tfinal AdHocResponse response = new AdHocResponse(sessionId, currentState);\n\t\tfinal AdHocSession session = (sessionId == null) ? new AdHocSession() : this.sessions.get(sessionId);\n\n\t\tadHocCommand.execute(request, response, () -> {\n\t\t\tElement commandResult = new Element(\"command\", new String[]{\"xmlns\", \"node\",},\n\t\t\t\t\t\t\t\t\t\t\t\tnew String[]{\"http://jabber.org/protocol/commands\", node});\n\n\t\t\tcommandResult.addAttribute(\"status\", response.getNewState().name());\n\t\t\tif ((response.getCurrentState() == null) && (response.getNewState() == State.executing)) {\n\t\t\t\tthis.sessions.put(response.getSessionid(), session);\n\t\t\t} else if ((response.getSessionid() != null) &&\n\t\t\t\t\t((response.getNewState() == State.canceled) || (response.getNewState() == State.completed))) {\n\t\t\t\tthis.sessions.remove(response.getSessionid());\n\t\t\t}\n\t\t\tif (response.getSessionid() != null) {\n\t\t\t\tcommandResult.addAttribute(\"sessionid\", response.getSessionid());\n\t\t\t}\n\t\t\tfor (Element r : response.getElements()) {\n\t\t\t\tcommandResult.addChild(r);\n\t\t\t}\n\t\t\tresultConsumer.accept(packet.okResult(commandResult, 0));\n\t\t}, (ex) -> {\n\t\t\ttry {\n\t\t\t\tPacket errorResponse = new ComponentException(ex.getErrorCondition(), ex.getMessage()).makeElement(packet, true);\n\t\t\t\tresultConsumer.accept(errorResponse);\n\t\t\t} catch (PacketErrorTypeException e1) {\n\t\t\t\tif (log.isLoggable(Level.WARNING)) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Problem during generate error response\", e1);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic void registerCommand(AdHocCommand command) {\n\t\tif (!this.commands.containsKey(command.getNode())) {\n\t\t\tthis.commands.put(command.getNode(), command);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/adhoc/AdHocResponse.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component.adhoc;\n\nimport tigase.server.Command;\nimport tigase.server.DataForm;\nimport tigase.xml.Element;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.UUID;\nimport java.util.function.Consumer;\n\npublic class AdHocResponse {\n\n\tprivate final State currentState;\n\tprivate final ArrayList<Element> elements = new ArrayList<Element>();\n\tprivate State newState = State.completed;\n\tprivate String sessionid;\n\n\tAdHocResponse(String sessionid, State currState) {\n\t\tthis.sessionid = sessionid;\n\t\tthis.currentState = currState;\n\t}\n\n\tpublic void cancelSession() {\n\t\tthis.newState = State.canceled;\n\t}\n\n\tpublic void completeSession() {\n\t\tthis.newState = State.completed;\n\t}\n\n\tpublic Collection<Element> getElements() {\n\t\treturn elements;\n\t}\n\n\tpublic Element addDataForm(Command.DataType dataType) {\n\t\tElement data = DataForm.createDataForm(dataType);\n\t\telements.add(data);\n\t\treturn data;\n\t}\n\n\tpublic Element addDataForm(Command.DataType dataType, Consumer<Element> consumer) {\n\t\tElement data = addDataForm(dataType);\n\t\tconsumer.accept(data);\n\t\treturn data;\n\t}\n\n\tpublic void startSession() {\n\t\tthis.newState = State.executing;\n\t\tthis.sessionid = UUID.randomUUID().toString();\n\t}\n\n\tState getCurrentState() {\n\t\treturn currentState;\n\t}\n\n\tpublic State getNewState() {\n\t\treturn newState;\n\t}\n\n\tpublic void setNewState(State newState) {\n\t\tthis.newState = newState;\n\t}\n\n\tString getSessionid() {\n\t\treturn sessionid;\n\t}\n\n\tvoid setSessionid(String sessionid) {\n\t\tthis.sessionid = sessionid;\n\t}\n\n\tpublic static enum State {\n\t\tcanceled,\n\t\tcompleted,\n\t\texecuting\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/adhoc/AdHocScriptCommandManager.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component.adhoc;\n\nimport tigase.server.Packet;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.List;\n\n/**\n * @author andrzej\n */\npublic interface AdHocScriptCommandManager {\n\n\tList<Element> getCommandListItems(final JID senderJid, final JID toJid);\n\n\tList<Packet> process(Packet packet);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/adhoc/AdHocSession.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component.adhoc;\n\npublic class AdHocSession {\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/adhoc/AdhHocRequest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component.adhoc;\n\nimport tigase.server.Packet;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.JID;\n\npublic class AdhHocRequest {\n\n\tprivate final String action;\n\n\tprivate final Element command;\n\n\tprivate final Packet iq;\n\n\tprivate final String node;\n\n\tprivate final JID sender;\n\n\tprivate final String sessionId;\n\n\tAdhHocRequest(Packet iq, Element command, String node, JID sender, String action, String sessionId) {\n\t\tsuper();\n\t\tthis.iq = iq;\n\t\tthis.command = command;\n\t\tthis.node = node;\n\t\tthis.action = action;\n\t\tthis.sessionId = sessionId;\n\t\tthis.sender = sender;\n\t}\n\n\tpublic String getAction() {\n\t\treturn action;\n\t}\n\n\tpublic boolean isAction(String action) {\n\t\treturn this.action != null && this.action.equals(action);\n\t}\n\n\tpublic Element getCommand() {\n\t\treturn command;\n\t}\n\n\tpublic Packet getIq() {\n\t\treturn iq;\n\t}\n\n\tpublic String getNode() {\n\t\treturn node;\n\t}\n\n\tpublic JID getSender() {\n\t\treturn sender;\n\t}\n\n\tpublic JID getRecipient() {\n\t\treturn iq.getStanzaTo();\n\t}\n\n\tpublic String getSessionId() {\n\t\treturn sessionId;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/exceptions/ComponentException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component.exceptions;\n\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.XMPPProcessorException;\n\npublic class ComponentException\n\t\textends XMPPProcessorException {\n\n\tpublic ComponentException(Authorization errorCondition) {\n\t\tsuper(errorCondition);\n\t}\n\n\tpublic ComponentException(Authorization errorCondition, String text) {\n\t\tsuper(errorCondition, text);\n\t}\n\n\tpublic ComponentException(Authorization errorCondition, String text, Throwable cause) {\n\t\tsuper(errorCondition, text, cause);\n\t}\n\n\tpublic ComponentException(Authorization errorCondition, String text, String message) {\n\t\tsuper(errorCondition, text, message);\n\t}\n\n\tpublic ComponentException(Authorization errorCondition, String text, String message, Throwable cause) {\n\t\tsuper(errorCondition, text, message, cause);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/exceptions/RepositoryException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component.exceptions;\n\npublic class RepositoryException\n\t\textends Exception {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic RepositoryException() {\n\t\tsuper();\n\t}\n\n\tpublic RepositoryException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic RepositoryException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n\tpublic RepositoryException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/modules/AbstractModule.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component.modules;\n\nimport tigase.component.PacketWriter;\nimport tigase.component.responses.AsyncCallback;\nimport tigase.eventbus.EventBus;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\n\nimport java.util.logging.Logger;\n\n/**\n * Abstract class for help building a module. It has implemented few default methods from {@code Module}, {@code\n * ContextAware} and {@code InitializingModule}.\n *\n * @author bmalkow\n */\npublic abstract class AbstractModule\n\t\timplements Module {\n\n\tprotected final Logger log = Logger.getLogger(this.getClass().getName());\n\t@Inject(bean = \"eventBus\")\n\tprotected EventBus eventBus;\n\t@Inject\n\tprotected PacketWriter writer;\n\n\tpublic EventBus getEventBus() {\n\t\treturn eventBus;\n\t}\n\n\tpublic void setEventBus(EventBus eventBus) {\n\t\tthis.eventBus = eventBus;\n\t}\n\n\tpublic PacketWriter getWriter() {\n\t\treturn writer;\n\t}\n\n\tpublic void setWriter(PacketWriter writer) {\n\t\tthis.writer = writer;\n\t}\n\n\t/**\n\t * Fires event.\n\t *\n\t * @param event event to fire.\n\t */\n\tprotected void fireEvent(Element event) {\n\t\teventBus.fire(event);\n\t}\n\n\tprotected void write(Packet packet) {\n\t\twriter.write(packet);\n\t}\n\n\tprotected void write(Packet packet, AsyncCallback asyncCallback) {\n\t\twriter.write(packet, asyncCallback);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/modules/Module.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component.modules;\n\nimport tigase.component.exceptions.ComponentException;\nimport tigase.criteria.Criteria;\nimport tigase.server.Packet;\nimport tigase.util.stringprep.TigaseStringprepException;\n\n/**\n * Basic interface to implement component module. Single module should implement fragment of component logic. Is easy to\n * add or remove in component (Server) configuration.\n *\n * @author bmalkow\n */\npublic interface Module {\n\n\tString[] EMPTY_FEATURES = new String[0];\n\n\t/**\n\t * Returns XMPP features offered by module. Features will be returned by Service Discovery.\n\t *\n\t * @return array of features or <code>null</code>.\n\t */\n\tdefault String[] getFeatures() {\n\t\treturn EMPTY_FEATURES;\n\t}\n\n\t/**\n\t * Returns critera used by Component to select module to handle incoming stanza.\n\t *\n\t * @return criteria of selecting module.\n\t */\n\tCriteria getModuleCriteria();\n\n\t/**\n\t * Returns true if Packet can be procesed by module. Default implementation uses Criteria.\n\t */\n\tdefault boolean canHandle(Packet packet) {\n\t\tCriteria criteria = getModuleCriteria();\n\t\treturn criteria != null && criteria.match(packet.getElement());\n\t}\n\n\t/**\n\t * Process incoming stanza.\n\t *\n\t * @param packet received {@link Packet stanza}.\n\t *\n\t * @throws ComponentException if stanza can't be processed correctly. ComponentException is converted to error\n\t * stanza and returned to stanza sender.\n\t * @throws TigaseStringprepException if there was an error during stringprep processing.\n\t */\n\tvoid process(final Packet packet) throws ComponentException, TigaseStringprepException;\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/modules/StanzaProcessor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component.modules;\n\nimport tigase.component.PacketWriter;\nimport tigase.component.exceptions.ComponentException;\nimport tigase.component.responses.ResponseManager;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Packet;\nimport tigase.stats.ComponentStatisticsProvider;\nimport tigase.stats.StatisticsInvocationHandler;\nimport tigase.stats.StatisticsList;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\n@Bean(name = \"stanzaProcessor\", active = true)\npublic class StanzaProcessor implements ComponentStatisticsProvider {\n\n\tprivate Logger log = Logger.getLogger(this.getClass().getName());\n\n\t@Inject(type = Module.class, nullAllowed = true)\n\tprivate List<Module> modules;\n\n\t@Inject\n\tprivate ResponseManager responseManager;\n\n\t@Inject\n\tprivate PacketWriter writer;\n\n\tprivate ConcurrentHashMap<Class<? extends Module>,ModuleStatistics> modulesStatistics = new ConcurrentHashMap<>();\n\n\tpublic List<Module> getModules() {\n\t\treturn modules;\n\t}\n\n\tpublic void setModules(List<Module> modules) {\n\t\tthis.modules = modules == null ? Collections.emptyList() : modules;\n\t\tmodulesStatistics = new ConcurrentHashMap<Class<? extends Module>, ModuleStatistics>(\n\t\t\t\tmodules.stream().collect(Collectors.toMap(Module::getClass, ModuleStatistics::new)));\n\t}\n\n\tpublic ResponseManager getResponseManager() {\n\t\treturn responseManager;\n\t}\n\n\tpublic void setResponseManager(ResponseManager responseManager) {\n\t\tthis.responseManager = responseManager;\n\t}\n\n\tpublic PacketWriter getWriter() {\n\t\treturn writer;\n\t}\n\n\tpublic void setWriter(PacketWriter writer) {\n\t\tthis.writer = writer;\n\t}\n\n\tpublic void processPacket(Packet packet) {\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.finer(\"Received: \" + packet.getElement());\n\t\t}\n\t\ttry {\n\t\t\tfinal JID senderJID = packet.getStanzaFrom();\n\t\t\tif (senderJID == null) {\n\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\tlog.finer(packet.getElemName() + \" stanza without 'from' attribute ignored.\");\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tRunnable responseHandler = responseManager.getResponseHandler(packet);\n\n\t\t\tboolean handled;\n\t\t\tif (responseHandler != null) {\n\t\t\t\thandled = true;\n\t\t\t\tresponseHandler.run();\n\t\t\t} else {\n\t\t\t\thandled = this.process(packet);\n\t\t\t}\n\n\t\t\tif (!handled) {\n\t\t\t\tif (shouldSendException(packet)) {\n\t\t\t\t\tthrow new ComponentException(Authorization.FEATURE_NOT_IMPLEMENTED);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (TigaseStringprepException e) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, e.getMessage() + \" when processing \" + packet.toString());\n\t\t\t}\n\t\t\tsendException(packet, new ComponentException(Authorization.JID_MALFORMED));\n\t\t} catch (ComponentException e) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, e.getMessageWithPosition() + \" when processing \" + packet.toString(), e);\n\t\t\t}\n\t\t\tsendException(packet, e);\n\t\t} catch (Exception e) {\n\t\t\tif (log.isLoggable(Level.SEVERE)) {\n\t\t\t\tlog.log(Level.SEVERE, e.getMessage() + \" when processing \" + packet.toString(), e);\n\t\t\t}\n\t\t\tsendException(packet, new ComponentException(Authorization.INTERNAL_SERVER_ERROR));\n\t\t}\n\t}\n\n\t/**\n\t * Converts {@link ComponentException} to XMPP error stanza and sends it to sender of packet.\n\t *\n\t * @param packet packet what caused exception.\n\t * @param e exception.\n\t */\n\tpublic void sendException(final Packet packet, final ComponentException e) {\n\t\ttry {\n\t\t\tif (!shouldSendException(packet)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tPacket result = e.makeElement(packet, true);\n\t\t\t// Why do we need this? Make error should return proper from/to values\n//\t\t\tElement el = result.getElement();\n//\n//\t\t\tel.setAttribute(\"from\", BareJID.bareJIDInstance(el.getAttributeStaticStr(Packet.FROM_ATT)).toString());\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Sending back: \" + result.toString());\n\t\t\t}\n\t\t\twriter.write(result);\n\t\t} catch (Exception e1) {\n\t\t\tif (log.isLoggable(Level.WARNING)) {\n\t\t\t\tlog.log(Level.WARNING, \"Problem during generate error response\", e1);\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected boolean shouldSendException(final Packet packet) {\n\t\tfinal StanzaType type = packet.getType();\n\n\t\t// https://xmpp.org/rfcs/rfc6120.html#stanzas-semantics-iq\n\t\t// An entity that receives a stanza of type \"result\" or \"error\" MUST NOT respond to the stanza\n\t\t// by sending a further IQ response of type \"result\" or \"error\"; however, the requesting entity\n\t\t// MAY send another request (e.g., an IQ of type \"set\" to provide obligatory information discovered\n\t\t// through a get/result pair).\n\t\tif (type == StanzaType.error || type == StanzaType.result) {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.finer(packet.getElemName() + \" stanza with type='\" + type + \"' ignored\");\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tpublic void everyHour() {\n\t\tmodulesStatistics.values().forEach(StatisticsInvocationHandler.Statistics::everyHour);\n\t}\n\n\tpublic void everyMinute() {\n\t\tmodulesStatistics.values().forEach(StatisticsInvocationHandler.Statistics::everyMinute);\n\t}\n\n\tpublic void everySecond() {\n\t\tmodulesStatistics.values().forEach(StatisticsInvocationHandler.Statistics::everySecond);\n\t}\n\t\n\t@Override\n\tpublic void getStatistics(String compName, StatisticsList list) {\n\t\tfor (ModuleStatistics statistics : modulesStatistics.values()) {\n\t\t\tstatistics.getStatistics(compName, \"stanzaProcessor\", list);\n\t\t}\n\t}\n\n\tprivate boolean process(final Packet packet) throws ComponentException, TigaseStringprepException {\n\t\tboolean handled = false;\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.finest(\"Processing packet: \" + packet.toString());\n\t\t}\n\n\t\tfor (Module module : this.modules) {\n\t\t\tif (module.canHandle(packet)) {\n\t\t\t\thandled = true;\n\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\tlog.finer(\"Handled by module \" + module.getClass());\n\t\t\t\t}\n\t\t\t\texecute(module, packet);\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"Finished \" + module.getClass());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn handled;\n\t}\n\n\tprivate void execute(Module module, Packet packet) throws ComponentException, TigaseStringprepException {\n\t\tModuleStatistics statistics = modulesStatistics.get(module.getClass());\n\t\ttry {\n\t\t\tlong start = System.currentTimeMillis();\n\t\t\tmodule.process(packet);\n\t\t\tlong end = System.currentTimeMillis();\n\t\t\tif (statistics != null) {\n\t\t\t\tstatistics.updateExecutionTime(end - start);\n\t\t\t}\n\t\t} catch (Throwable ex) {\n\t\t\tif (statistics != null) {\n\t\t\t\tstatistics.executionFailed();\n\t\t\t}\n\t\t\tthrow ex;\n\t\t}\n\t}\n\t\n\tpublic static class ModuleStatistics extends StatisticsInvocationHandler.Statistics {\n\n\t\tprivate static String generateModuleName(Module module) {\n\t\t\treturn module.getClass().getSimpleName();\n\t\t}\n\n\t\tpublic ModuleStatistics(Module module) {\n\t\t\tsuper(generateModuleName(module));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/modules/impl/AdHocCommandModule.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component.modules.impl;\n\nimport tigase.component.adhoc.AdHocCommand;\nimport tigase.component.adhoc.AdHocCommandException;\nimport tigase.component.adhoc.AdHocCommandManager;\nimport tigase.component.exceptions.ComponentException;\nimport tigase.component.modules.AbstractModule;\nimport tigase.criteria.Criteria;\nimport tigase.criteria.ElementCriteria;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Command;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.ArrayDeque;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Queue;\nimport java.util.function.Consumer;\n\n@Bean(name = AdHocCommandModule.ID, active = true)\npublic class AdHocCommandModule\n\t\textends AbstractModule\n\t\timplements Initializable {\n\n\tpublic final static String ID = \"commands\";\n\tpublic static final String XMLNS = Command.XMLNS;\n\tprotected static final String[] COMMAND_PATH = {\"iq\", \"command\"};\n\tprotected static final Criteria CRIT = ElementCriteria.nameType(\"iq\", \"set\")\n\t\t\t.add(ElementCriteria.name(\"command\", Command.XMLNS));\n\t@Inject(nullAllowed = false)\n\tprotected AdHocCommandManager commandsManager;\n\t@Inject(nullAllowed = false)\n\tprotected ScriptCommandProcessor scriptProcessor;\n\n\tpublic AdHocCommandModule() {\n\t\tthis.commandsManager = new AdHocCommandManager();\n\t}\n\n\tpublic List<Element> getCommandListItems(final JID senderJid, final JID toJid) {\n\t\tArrayList<Element> commandsList = new ArrayList<Element>();\n\t\taddCommandListItemsElements(Command.XMLNS, toJid, senderJid, commandsList::add);\n\t\treturn commandsList;\n\t}\n\n\tpublic AdHocCommandManager getCommandsManager() {\n\t\treturn commandsManager;\n\t}\n\n\tpublic void setCommandsManager(AdHocCommandManager commandsManager) {\n\t\tthis.commandsManager = commandsManager;\n\t}\n\n\t@Override\n\tpublic String[] getFeatures() {\n\t\treturn new String[]{Command.XMLNS};\n\t}\n\n\t@Override\n\tpublic Criteria getModuleCriteria() {\n\t\treturn CRIT;\n\t}\n\n\tpublic List<Element> getScriptItems(String node, JID stanzaTo, JID stanzaFrom) {\n\t\tArrayList<Element> result = new ArrayList<Element>();\n\n\t\taddCommandListItemsElements(node, stanzaTo, stanzaFrom, result::add);\n\n\t\treturn result;\n\t}\n\n\tpublic void addCommandListItemsElements(String node, JID stanzaTo, final JID stanzaFrom, Consumer<Element> collector) {\n\t\tif (stanzaTo == null) {\n\t\t\tstanzaTo = stanzaFrom.copyWithoutResource();\n\t\t}\n\t\tboolean selfQuery = stanzaFrom.getBareJID().equals(stanzaTo.getBareJID()) && stanzaTo.getResource() == null;\n\t\tfor (AdHocCommand c : commandsManager.getAllCommands()) {\n\t\t\tif (c.isForSelf() != selfQuery) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (c.isAllowedFor(stanzaFrom, stanzaTo)) {\n\t\t\t\tElement i = new Element(\"item\", new String[]{\"jid\", \"node\", \"name\"},\n\t\t\t\t\t\t\t\t\t\tnew String[]{stanzaTo.toString(), c.getNode(), c.getName()});\n\t\t\t\tc.getGroup().ifPresent(group -> i.setAttribute(\"group\", group));\n\t\t\t\tcollector.accept(i);\n\t\t\t}\n\t\t}\n\t\tif (!selfQuery) {\n\t\t\tList<Element> scripts = scriptProcessor.getScriptItems(node, stanzaTo, stanzaFrom);\n\t\t\tif (scripts != null) {\n\t\t\t\tscripts.forEach(collector);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic ScriptCommandProcessor getScriptProcessor() {\n\t\treturn scriptProcessor;\n\t}\n\n\tpublic void setScriptProcessor(ScriptCommandProcessor scriptProcessor) {\n\t\tthis.scriptProcessor = scriptProcessor;\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tif (scriptProcessor == null) {\n\t\t\tthrow new RuntimeException(\"scriptProcessor cannot be null!\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void process(Packet packet) throws ComponentException {\n\t\tString node = packet.getAttributeStaticStr(COMMAND_PATH, \"node\");\n\t\tif (commandsManager.hasCommand(node)) {\n\t\t\ttry {\n\t\t\t\tthis.commandsManager.process(packet, writer::write);\n\t\t\t} catch (AdHocCommandException e) {\n\t\t\t\tthrow new ComponentException(e.getErrorCondition(), e.getMessage());\n\t\t\t}\n\t\t} else {\n\t\t\tprocessScriptAdHoc(packet);\n\t\t}\n\t}\n\n\tpublic void register(AdHocCommand command) {\n\t\tthis.commandsManager.registerCommand(command);\n\t}\n\n\tprotected void processScriptAdHoc(Packet packet) {\n\t\tQueue<Packet> results = new ArrayDeque<Packet>();\n\n\t\tif (scriptProcessor.processScriptCommand(packet, results)) {\n\t\t\tfor (Packet p : results) {\n\t\t\t\twrite(p);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static interface ScriptCommandProcessor {\n\n\t\tList<Element> getScriptItems(String node, JID jid, JID from);\n\n\t\tboolean processScriptCommand(Packet pc, Queue<Packet> results);\n\n\t\tboolean isAllowed(String node, JID from);\n\n\t\tboolean isAllowed(String node, String domain, JID from);\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/modules/impl/DiscoveryModule.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component.modules.impl;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.component.exceptions.ComponentException;\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.component.modules.AbstractModule;\nimport tigase.component.modules.Module;\nimport tigase.criteria.Criteria;\nimport tigase.criteria.ElementCriteria;\nimport tigase.criteria.Or;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.BasicComponent;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\nimport tigase.xmpp.rsm.RSM;\n\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\n@Bean(name = DiscoveryModule.ID, active = true)\npublic class DiscoveryModule\n\t\textends AbstractModule {\n\n\tpublic final static String DISCO_INFO_XMLNS = \"http://jabber.org/protocol/disco#info\";\n\n\tpublic final static String DISCO_ITEMS_XMLNS = \"http://jabber.org/protocol/disco#items\";\n\n\tprivate static final String[] FEATURES = { DISCO_INFO_XMLNS, DISCO_ITEMS_XMLNS };\n\n\tpublic final static String ID = \"disco\";\n\n\t@Inject(nullAllowed = true)\n\tprivate AdHocCommandModule adHocCommandModule;\n\n\t@Inject(bean = \"service\")\n\tprotected BasicComponent component;\n\n\tprivate Criteria criteria;\n\n\t@Inject(type = Module.class)\n\tprivate List<Module> modules;\n\n\tpublic DiscoveryModule() {\n\t\tthis.criteria = ElementCriteria.nameType(\"iq\", \"get\")\n\t\t\t\t.add(new Or(ElementCriteria.name(\"query\", DISCO_INFO_XMLNS),\n\t\t\t\t\t\t\tElementCriteria.name(\"query\", DISCO_ITEMS_XMLNS)));\n\t}\n\n\tpublic AdHocCommandModule getAdHocCommandModule() {\n\t\treturn adHocCommandModule;\n\t}\n\n\tpublic void setAdHocCommandModule(AdHocCommandModule adHocCommandModule) {\n\t\tthis.adHocCommandModule = adHocCommandModule;\n\t}\n\n\tpublic Set<String> getAvailableFeatures(BareJID serviceJID, String node, BareJID senderJID) {\n\t\treturn getAvailableFeatures();\n\t}\n\n\tpublic Set<String> getAvailableFeatures() {\n\t\tfinal HashSet<String> features = new HashSet<String>();\n\t\tfor (Module m : modules) {\n\t\t\tString[] fs = m.getFeatures();\n\t\t\tif (fs != null) {\n\t\t\t\tfor (String string : fs) {\n\t\t\t\t\tfeatures.add(string);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn features;\n\t}\n\n\t@Override\n\tpublic String[] getFeatures() {\n\t\treturn FEATURES;\n\t}\n\n\t@Override\n\tpublic Criteria getModuleCriteria() {\n\t\treturn criteria;\n\t}\n\n\tpublic List<Module> getModules() {\n\t\treturn modules;\n\t}\n\n\tpublic void setModules(List<Module> modules) {\n\t\tthis.modules = modules;\n\t}\n\n\t@Override\n\tpublic void process(Packet packet) throws ComponentException, TigaseStringprepException {\n\t\tfinal Element q = packet.getElement().getChild(\"query\");\n\t\tfinal JID senderJID = packet.getStanzaFrom();\n\t\tfinal JID jid = packet.getStanzaTo();\n\t\tfinal String node = q.getAttributeStaticStr(\"node\");\n\n\t\ttry {\n\t\t\tif (q.getXMLNS().equals(DISCO_INFO_XMLNS)) {\n\t\t\t\tprocessDiscoInfo(packet, jid, node, senderJID);\n\t\t\t} else if (q.getXMLNS().equals(DISCO_ITEMS_XMLNS) && node != null &&\n\t\t\t\t\tnode.equals(AdHocCommandModule.XMLNS)) {\n\t\t\t\tprocessAdHocCommandItems(packet, jid, node, senderJID);\n\t\t\t} else if (q.getXMLNS().equals(DISCO_ITEMS_XMLNS)) {\n\t\t\t\tprocessDiscoItems(packet, jid, node, senderJID);\n\t\t\t} else {\n\t\t\t\tthrow new ComponentException(Authorization.BAD_REQUEST);\n\t\t\t}\n\t\t} catch (ComponentException e) {\n\t\t\tthrow e;\n\t\t} catch (RepositoryException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tprotected void processAdHocCommandItems(Packet packet, JID jid, String node, JID senderJID)\n\t\t\tthrows ComponentException, RepositoryException {\n\t\tif (adHocCommandModule == null) {\n\t\t\tthrow new ComponentException(Authorization.ITEM_NOT_FOUND);\n\t\t}\n\n\t\tElement resultQuery = new Element(\"query\", new String[]{Packet.XMLNS_ATT}, new String[]{DISCO_ITEMS_XMLNS});\n\t\tPacket result = packet.okResult(resultQuery, 0);\n\n\t\tList<Element> items = adHocCommandModule.getScriptItems(node, packet.getStanzaTo(), packet.getStanzaFrom());\n\n\t\tresultQuery.addChildren(items);\n\n\t\twrite(result);\n\t}\n\n\tprotected void processDiscoInfo(Packet packet, JID jid, String node, JID senderJID)\n\t\t\tthrows ComponentException, RepositoryException {\n\t\tPacket resultIq = prepareDiscoInfoResponse(packet, jid, node, senderJID);\n\t\twrite(resultIq);\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(removeIn = \"9.0.0\", since = \"8.1.0\", note = \"Deprecating method with type-o\")\n\tprotected Packet prepareDiscoInfoReponse(Packet packet, JID jid, String node, JID senderJID) {\n\t\treturn prepareDiscoInfoResponse(packet, jid, node, senderJID);\n\t}\n\n\tprotected Packet prepareDiscoInfoResponse(Packet packet, JID jid, String node, JID senderJID) {\n\t\tElement resultQuery = new Element(\"query\", new String[]{\"xmlns\"}, new String[]{DISCO_INFO_XMLNS});\n\t\tPacket resultIq = packet.okResult(resultQuery, 0);\n\n\t\tresultQuery.addChild(new Element(\"identity\", new String[]{\"category\", \"type\", \"name\"},\n\t\t\t\t\t\t\t\t\t\t new String[]{component.getDiscoCategory(), component.getDiscoCategoryType(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t  component.getDiscoDescription()}));\n\t\tfor (String f : getAvailableFeatures(jid.getBareJID(), node, senderJID.getBareJID())) {\n\t\t\tresultQuery.addChild(new Element(\"feature\", new String[]{\"var\"}, new String[]{f}));\n\t\t}\n\t\tElement form = component.getDiscoExtensionsForm(jid.getDomain());\n\t\tif (form != null) {\n\t\t\tresultQuery.addChild(form);\n\t\t}\n\t\treturn resultIq;\n\t}\n\n\tprotected List<Element> prepareDiscoItems(JID jid, String node, JID senderJID, RSM rsm) throws ComponentException, RepositoryException {\n\t\treturn Collections.emptyList();\n\t}\n\n\tprotected void processDiscoItems(Packet packet, JID jid, String node, JID senderJID)\n\t\t\tthrows ComponentException, RepositoryException {\n\t\tElement resultQuery = new Element(\"query\", new String[]{Packet.XMLNS_ATT}, new String[]{DISCO_ITEMS_XMLNS});\n\t\t\n\t\tElement rsmEl = packet.getElement().getChildStaticStr(Iq.QUERY_NAME, \"http://jabber.org/protocol/disco#items\").getChildStaticStr(\"set\", RSM.XMLNS);\n\t\tRSM rsm = null;\n\t\tif (rsmEl != null) {\n\t\t\trsm = RSM.parseRootElement(packet.getElement().getChild(\"query\"));\n\t\t}\n\n\t\tList<Element> results = prepareDiscoItems(jid, node, senderJID, rsm);\n\n\t\tif (node != null) {\n\t\t\tresultQuery.setAttribute(\"node\", node);\n\t\t}\n\n\t\tresultQuery.addChildren(results);\n\n\t\tif (rsm != null && !results.isEmpty()) {\n\t\t\tresultQuery.addChild(rsm.toElement());\n\t\t}\n\n\t\twrite(packet.okResult(resultQuery, 0));\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/modules/impl/JabberVersionModule.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component.modules.impl;\n\nimport tigase.component.AbstractKernelBasedComponent;\nimport tigase.component.exceptions.ComponentException;\nimport tigase.component.modules.AbstractModule;\nimport tigase.criteria.Criteria;\nimport tigase.criteria.ElementCriteria;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\n\n@Bean(name = JabberVersionModule.ID, active = true)\npublic class JabberVersionModule\n\t\textends AbstractModule {\n\n\tpublic final static String ID = \"jabber:iq:version\";\n\tprivate static final Criteria CRIT = ElementCriteria.nameType(\"iq\", \"get\")\n\t\t\t.add(ElementCriteria.name(\"query\", \"jabber:iq:version\"));\n\t@Inject(bean = \"service\")\n\tprivate AbstractKernelBasedComponent component;\n\n\tpublic JabberVersionModule() {\n\t}\n\n\t@Override\n\tpublic String[] getFeatures() {\n\t\treturn new String[]{\"jabber:iq:version\"};\n\t}\n\n\t@Override\n\tpublic Criteria getModuleCriteria() {\n\t\treturn CRIT;\n\t}\n\n\t@Override\n\tpublic void process(Packet packet) throws ComponentException {\n\t\tElement query = new Element(\"query\", new String[]{\"xmlns\"}, new String[]{\"jabber:iq:version\"});\n\n\t\tquery.addChild(new Element(\"name\", component.getDiscoDescription()));\n\t\tquery.addChild(new Element(\"version\", component.getComponentVersion()));\n\t\tquery.addChild(new Element(\"os\", System.getProperty(\"os.name\") + \"-\" + System.getProperty(\"os.arch\") + \"-\" +\n\t\t\t\tSystem.getProperty(\"os.version\") + \", \" + System.getProperty(\"java.vm.name\") + \"-\" +\n\t\t\t\tSystem.getProperty(\"java.version\") + \" \" + System.getProperty(\"java.vm.vendor\")));\n\n\t\twrite(packet.okResult(query, 0));\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/modules/impl/XmppPingModule.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component.modules.impl;\n\nimport tigase.component.exceptions.ComponentException;\nimport tigase.component.modules.AbstractModule;\nimport tigase.criteria.Criteria;\nimport tigase.criteria.ElementCriteria;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\n\n@Bean(name = XmppPingModule.ID, active = true)\npublic class XmppPingModule\n\t\textends AbstractModule {\n\n\tpublic final static String ID = \"urn:xmpp:ping\";\n\tprivate static final Criteria CRIT = ElementCriteria.nameType(\"iq\", \"get\")\n\t\t\t.add(ElementCriteria.name(\"ping\", \"urn:xmpp:ping\"));\n\n\tpublic XmppPingModule() {\n\t}\n\n\t@Override\n\tpublic String[] getFeatures() {\n\t\treturn new String[]{\"urn:xmpp:ping\"};\n\t}\n\n\t@Override\n\tpublic Criteria getModuleCriteria() {\n\t\treturn CRIT;\n\t}\n\n\t@Override\n\tpublic void process(Packet iq) throws ComponentException {\n\t\tPacket reposnse = iq.okResult((Element) null, 0);\n\t\twrite(reposnse);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/modules/impl/config/ConfiguratorCommand.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component.modules.impl.config;\n\nimport tigase.component.adhoc.AdHocCommand;\nimport tigase.component.adhoc.AdHocCommandException;\nimport tigase.component.adhoc.AdHocResponse;\nimport tigase.component.adhoc.AdhHocRequest;\nimport tigase.component.modules.impl.AdHocCommandModule;\nimport tigase.form.Field;\nimport tigase.form.Form;\nimport tigase.kernel.BeanUtils;\nimport tigase.kernel.TypesConverter;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Converter;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.AbstractBeanConfigurator;\nimport tigase.kernel.beans.config.BeanConfigurator;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.core.BeanConfig;\nimport tigase.kernel.core.DependencyManager;\nimport tigase.kernel.core.Kernel;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.logging.Logger;\n\nimport static java.util.Collections.sort;\n\n@Bean(name = \"BeanConfiguratorAdHocCommand\", active = true)\npublic class ConfiguratorCommand\n\t\timplements AdHocCommand {\n\n\tprotected final Logger log = Logger.getLogger(this.getClass().getName());\n\t@Inject(bean = \"defaultTypesConverter\")\n\tprotected TypesConverter defaultTypesConverter;\n\t@Inject(bean = BeanConfigurator.DEFAULT_CONFIGURATOR_NAME)\n\tprivate AbstractBeanConfigurator beanConfigurator;\n\t@Inject(bean = \"kernel\")\n\tprivate Kernel kernel;\n\t@Inject\n\tprivate AdHocCommandModule.ScriptCommandProcessor scriptCommandProcessor;\n\n\t@Override\n\tpublic void execute(AdhHocRequest request, AdHocResponse response) throws AdHocCommandException {\n\t\tfinal Element data = request.getCommand().getChild(\"x\", \"jabber:x:data\");\n\t\tfinal Form f = data == null ? null : new Form(data);\n\n\t\tif (request.getAction() != null && \"cancel\".equals(request.getAction())) {\n\t\t\tresponse.cancelSession();\n\t\t} else if (data == null) {\n\t\t\tForm form = new Form(\"form\", \"Select Bean to configure\", null);\n\n\t\t\tArrayList<String> items = getConfigurableBeansNames();\n\t\t\tArrayList<String> options = new ArrayList<>();\n\t\t\tArrayList<String> values = new ArrayList<>();\n\n\t\t\tsort(items);\n\n\t\t\toptions.add(\"-- All --\");\n\t\t\tvalues.add(\"-\");\n\t\t\tfor (String bn : items) {\n\t\t\t\toptions.add(bn);\n\t\t\t\tvalues.add(bn);\n\t\t\t}\n\n\t\t\tform.addField(Field.fieldListSingle(\"bean\", \"-\", \"Bean to configure\", options.toArray(new String[]{}),\n\t\t\t\t\t\t\t\t\t\t\t\tvalues.toArray(new String[]{})));\n\n\t\t\tresponse.getElements().add(form.getElement());\n\t\t\tresponse.startSession();\n\t\t} else if (f != null && f.getAsString(\"bean\") != null) {\n\t\t\ttry {\n\t\t\t\tString n = f.getAsString(\"bean\");\n\t\t\t\tn = n.equals(\"-\") ? null : n.trim();\n\t\t\t\tForm form = new Form(\"form\", \"Configure Beans\", null);\n\n\t\t\t\tArrayList<ConfigFieldItem> citems = getConfigItems(n);\n\t\t\t\tsort(citems);\n\n\t\t\t\tBeanConfig bc = null;\n\n\t\t\t\tfor (ConfigFieldItem cfi : citems) {\n\t\t\t\t\tfinal String key = cfi.beanConfig.getBeanName() + \"/\" + cfi.field.getName();\n\t\t\t\t\tfinal Object bean = kernel.getInstance(cfi.beanConfig.getBeanName());\n\t\t\t\t\tfinal Object value = BeanUtils.getValue(bean, cfi.field);\n\n\t\t\t\t\tif (bc == null || !bc.equals(cfi.beanConfig)) {\n\t\t\t\t\t\tform.addField(Field.fieldFixed(\"Bean: \" + cfi.beanConfig.getBeanName()));\n\t\t\t\t\t\tbc = cfi.beanConfig;\n\t\t\t\t\t}\n\n\t\t\t\t\tTypesConverter converter = defaultTypesConverter;\n\t\t\t\t\tConverter cAnn = cfi.field.getAnnotation(Converter.class);\n\t\t\t\t\tif (cAnn != null) {\n\t\t\t\t\t\tconverter = kernel.getInstance(cAnn.converter());\n\t\t\t\t\t}\n\n\t\t\t\t\tField field = Field.fieldTextSingle(key, value == null ? \"\" : converter.toString(value),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcfi.configField.desc());\n\t\t\t\t\tform.addField(field);\n\t\t\t\t}\n\n\t\t\t\tresponse.getElements().add(form.getElement());\n\t\t\t\tresponse.startSession();\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new AdHocCommandException(Authorization.INTERNAL_SERVER_ERROR, e.getMessage());\n\t\t\t}\n\t\t} else {\n\t\t\tfinal HashMap<BeanConfig, HashMap<String, Object>> values = new HashMap<>();\n\n\t\t\tfor (final Field field : f.getAllFields()) {\n\t\t\t\tint i = field.getVar().indexOf('/');\n\t\t\t\tfinal String bn = field.getVar().substring(0, i);\n\t\t\t\tfinal String fn = field.getVar().substring(i + 1);\n\t\t\t\tfinal String value = field.getValue();\n\n\t\t\t\tBeanConfig beanConfig = kernel.getDependencyManager().getBeanConfig(bn);\n\t\t\t\tif (!values.containsKey(beanConfig)) {\n\t\t\t\t\tvalues.put(beanConfig, new HashMap<>());\n\t\t\t\t}\n\t\t\t\tHashMap<String, Object> valuesToSet = values.get(beanConfig);\n\t\t\t\tvaluesToSet.put(fn, value);\n\t\t\t}\n\t\t\tfor (Map.Entry<BeanConfig, HashMap<String, Object>> entry : values.entrySet()) {\n\t\t\t\tfinal BeanConfig beanConfig = entry.getKey();\n\t\t\t\tfinal Object bean = kernel.getInstance(beanConfig.getBeanName());\n\n\t\t\t\tbeanConfigurator.configure(beanConfig, bean, entry.getValue());\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"Configurator\";\n\t}\n\n\t@Override\n\tpublic String getNode() {\n\t\treturn \"bean-configurator\";\n\t}\n\n\t@Override\n\tpublic boolean isAllowedFor(JID jid) {\n\t\treturn scriptCommandProcessor.isAllowed(getNode(), jid);\n\t}\n\n\tprivate ArrayList<ConfigFieldItem> getConfigItems(final String beanName) {\n\t\tArrayList<ConfigFieldItem> result = new ArrayList<>();\n\t\tfor (BeanConfig bc : kernel.getDependencyManager().getBeanConfigs()) {\n\t\t\tif (beanName != null && !beanName.equals(bc.getBeanName())) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfinal Class<?> cl = bc.getClazz();\n\t\t\tjava.lang.reflect.Field[] fields = DependencyManager.getAllFields(cl);\n\t\t\tfor (java.lang.reflect.Field field : fields) {\n\t\t\t\tfinal ConfigField cf = field.getAnnotation(ConfigField.class);\n\t\t\t\tif (cf != null) {\n\t\t\t\t\tConfigFieldItem cfi = new ConfigFieldItem();\n\t\t\t\t\tcfi.beanConfig = bc;\n\t\t\t\t\tcfi.configField = cf;\n\t\t\t\t\tcfi.field = field;\n\n\t\t\t\t\tresult.add(cfi);\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate ArrayList<String> getConfigurableBeansNames() {\n\t\tArrayList<String> result = new ArrayList<>();\n\t\tfor (BeanConfig bc : kernel.getDependencyManager().getBeanConfigs()) {\n\t\t\tfinal Class<?> cl = bc.getClazz();\n\t\t\tjava.lang.reflect.Field[] fields = DependencyManager.getAllFields(cl);\n\t\t\tfor (java.lang.reflect.Field field : fields) {\n\t\t\t\tfinal ConfigField cf = field.getAnnotation(ConfigField.class);\n\t\t\t\tif (cf != null) {\n\t\t\t\t\tresult.add(bc.getBeanName());\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate class ConfigFieldItem\n\t\t\timplements Comparable<ConfigFieldItem> {\n\n\t\tBeanConfig beanConfig;\n\t\tConfigField configField;\n\t\tjava.lang.reflect.Field field;\n\n\t\t@Override\n\t\tpublic int compareTo(ConfigFieldItem o) {\n\t\t\tString t0 = beanConfig.getBeanName() + \"#\" + field.getName();\n\t\t\tString t1 = o.beanConfig.getBeanName() + \"#\" + o.field.getName();\n\n\t\t\treturn t0.compareTo(t1);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/responses/AsyncCallback.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component.responses;\n\nimport tigase.server.Packet;\nimport tigase.xmpp.StanzaType;\n\n/**\n * Main interface for callback of all <a href='http://xmpp.org/rfcs/rfc6120.html#stanzas-semantics-iq'>IQ</a>\n * asynchronous request-response mechanism.\n *\n * @author bmalkow\n */\npublic interface AsyncCallback {\n\n\t/**\n\t * Called when received response has type {@linkplain StanzaType#error error}.\n\t *\n\t * @param responseStanza received IQ stanza\n\t * @param errorCondition error condition\n\t */\n\tvoid onError(Packet responseStanza, String errorCondition);\n\n\t/**\n\t * Called when received response has type {@linkplain StanzaType#result result}.\n\t *\n\t * @param responseStanza received stanza\n\t */\n\tvoid onSuccess(Packet responseStanza);\n\n\t/**\n\t * Called when response wasn't received in given time.\n\t */\n\tvoid onTimeout();\n\n}"
  },
  {
    "path": "src/main/java/tigase/component/responses/DefaultResponseHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component.responses;\n\nimport tigase.component.responses.ResponseManager.Entry;\nimport tigase.server.Packet;\n\npublic class DefaultResponseHandler\n\t\timplements Runnable {\n\n\tprivate final Entry entry;\n\n\tprivate final Packet packet;\n\n\tpublic DefaultResponseHandler(Packet packet, ResponseManager.Entry entry) {\n\t\tthis.packet = packet;\n\t\tthis.entry = entry;\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\tfinal String type = this.packet.getElement().getAttributeStaticStr(\"type\");\n\n\t\tif (type != null && type.equals(\"result\")) {\n\t\t\tentry.getCallback().onSuccess(packet);\n\t\t} else if (type != null && type.equals(\"error\")) {\n\t\t\tString condition = packet.getErrorCondition();\n\t\t\tentry.getCallback().onError(packet, condition);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/component/responses/ResponseManager.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component.responses;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Packet;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.logging.Logger;\n\n@Bean(name = \"responseManager\", active = true)\npublic class ResponseManager {\n\n\tpublic static final long DEFAULT_TIMEOUT = 1000 * 60;\n\tprotected final Logger log = Logger.getLogger(this.getClass().getName());\n\tprivate final Map<String, Entry> handlers = new HashMap<String, Entry>();\n\n\tpublic void checkTimeouts() {\n\t\tlong now = (new Date()).getTime();\n\t\tIterator<java.util.Map.Entry<String, Entry>> it = this.getHandlers().entrySet().iterator();\n\t\twhile (it.hasNext()) {\n\t\t\tjava.util.Map.Entry<String, Entry> e = it.next();\n\t\t\tif (e.getValue().timestamp + e.getValue().timeout < now) {\n\t\t\t\tit.remove();\n\t\t\t\te.getValue().callback.onTimeout();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Returns handler for response of sent {@code  <iq/>} stanza.\n\t *\n\t * @param element response {@code  <iq/>} stanza.\n\t *\n\t * @return Runnable object with handler\n\t */\n\tpublic Runnable getResponseHandler(final Packet element) {\n\t\tif (!\"iq\".equals(element.getElemName())) {\n\t\t\treturn null;\n\t\t}\n\n\t\tfinal String type = element.getElement().getAttributeStaticStr(\"type\");\n\t\tif (type == null || type.equals(\"set\") || type.equals(\"get\")) {\n\t\t\treturn null;\n\t\t}\n\n\t\tfinal String id = element.getElement().getAttributeStaticStr(\"id\");\n\t\tif (id == null) {\n\t\t\treturn null;\n\t\t}\n\t\tfinal Entry entry = this.getHandlers().get(id);\n\t\tif (entry == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (!verify(element, entry)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tthis.getHandlers().remove(id);\n\n\t\tRunnable r = new DefaultResponseHandler(element, entry);\n\t\treturn r;\n\t}\n\n\t/**\n\t * Register callback for response of sent {@code <iq/>} stanza.\n\t *\n\t * @param stanza sent {@code <iq/>} stanza.\n\t * @param timeout timeout. After it method {@linkplain AsyncCallback#onTimeout() onTimeout()} will be called.\n\t * @param callback callback\n\t *\n\t * @return id of stanza\n\t */\n\tpublic String registerResponseHandler(final Packet stanza, final Long timeout, final AsyncCallback callback) {\n\t\tif (stanza == null) {\n\t\t\treturn null;\n\t\t}\n\t\tJID to = stanza.getStanzaTo();\n\t\tString id = stanza.getElement().getAttributeStaticStr(\"id\");\n\t\tif (id == null) {\n\t\t\tid = UUID.randomUUID().toString();\n\t\t\tstanza.getElement().setAttribute(\"id\", id);\n\t\t}\n\n\t\tif (callback != null) {\n\t\t\tEntry entry = new Entry(to, (new Date()).getTime(), timeout == null ? DEFAULT_TIMEOUT : timeout, callback);\n\t\t\tthis.getHandlers().put(id, entry);\n\t\t}\n\n\t\treturn id;\n\t}\n\n\tprotected Map<String, Entry> getHandlers() {\n\t\treturn handlers;\n\t}\n\n\tprivate boolean verify(final Packet response, final Entry entry) {\n\t\tfinal JID jid = response.getStanzaFrom();\n\n\t\tif (jid != null && entry.jid != null && jid.getBareJID().equals(entry.jid.getBareJID())) {\n\t\t\treturn true;\n\t\t} else if (entry.jid == null && jid == null) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\tprotected static final class Entry {\n\n\t\tprivate final AsyncCallback callback;\n\n\t\tprivate final JID jid;\n\n\t\tprivate final long timeout;\n\n\t\tprivate final long timestamp;\n\n\t\tpublic Entry(JID jid, long timestamp, long timeout, AsyncCallback callback) {\n\t\t\tsuper();\n\t\t\tthis.jid = jid;\n\t\t\tthis.timestamp = timestamp;\n\t\t\tthis.timeout = timeout;\n\t\t\tthis.callback = callback;\n\t\t}\n\n\t\tAsyncCallback getCallback() {\n\t\t\treturn callback;\n\t\t}\n\n\t\tJID getJid() {\n\t\t\treturn jid;\n\t\t}\n\n\t\tlong getTimeout() {\n\t\t\treturn timeout;\n\t\t}\n\n\t\tlong getTimestamp() {\n\t\t\treturn timestamp;\n\t\t}\n\n\t}\n}"
  },
  {
    "path": "src/main/java/tigase/conf/AbstractConfigBuilder.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\nimport tigase.kernel.beans.config.AbstractBeanConfigurator;\n\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.Consumer;\n\n/**\n * Created by andrzej on 11.05.2017.\n */\npublic class AbstractConfigBuilder<T extends Map, S extends AbstractConfigBuilder> {\n\n\tprotected final T map;\n\n\tprotected AbstractConfigBuilder(T map) {\n\t\tthis.map = map;\n\t}\n\n\tpublic AbstractBeanConfigurator.BeanDefinition.Builder bean() {\n\t\treturn new AbstractBeanConfigurator.BeanDefinition.Builder(map);\n\t}\n\n\tpublic void property(String key, Object value) {\n\t\tmap.put(key, value);\n\t}\n\n\tpublic T build() {\n\t\treturn map;\n\t}\n\n\tpublic S with(AbstractBeanConfigurator.BeanDefinition... beans) {\n\t\tfor (AbstractBeanConfigurator.BeanDefinition bean : beans) {\n\t\t\tmap.put(bean.getBeanName(), bean);\n\t\t}\n\t\treturn (S) this;\n\t}\n\n\tpublic S with(String property, Optional optional) {\n\t\tif (optional.isPresent()) {\n\t\t\twith(property, optional.get());\n\t\t}\n\t\treturn (S) this;\n\t}\n\n\tpublic S with(String property, Object value) {\n\t\tif (value instanceof Optional) {\n\t\t\treturn with(property, (Optional) value);\n\t\t}\n\t\tmap.put(property, value);\n\t\treturn (S) this;\n\t}\n\n\tpublic S withBean(Consumer<AbstractBeanConfigurator.BeanDefinition.Builder>... builders) {\n\t\tfor (Consumer<AbstractBeanConfigurator.BeanDefinition.Builder> builder : builders) {\n\t\t\tAbstractBeanConfigurator.BeanDefinition.Builder defBuilder = new AbstractBeanConfigurator.BeanDefinition.Builder();\n\t\t\tbuilder.accept(defBuilder);\n\t\t\twith(defBuilder.build());\n\t\t}\n\t\treturn (S) this;\n\t}\n}"
  },
  {
    "path": "src/main/java/tigase/conf/ConfigBuilder.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\nimport java.util.HashMap;\n\n/**\n * Created by andrzej on 11.05.2017.\n */\npublic class ConfigBuilder\n\t\textends AbstractConfigBuilder<HashMap, ConfigBuilder> {\n\n\tpublic ConfigBuilder() {\n\t\tsuper(new HashMap<>());\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/conf/ConfigHelper.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\nimport tigase.kernel.beans.config.AbstractBeanConfigurator;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Created by andrzej on 08.11.2016.\n */\npublic class ConfigHelper {\n\n\tpublic static Map<String, Object> merge(Map<String, Object>... props) {\n\t\tswitch (props.length) {\n\t\t\tcase 0:\n\t\t\t\treturn null;\n\t\t\tcase 1:\n\t\t\t\treturn props[0];\n\t\t\tdefault:\n\t\t\t\tfinal Map<String, Object> mergedProps = new HashMap<>();\n\t\t\t\tfor (Map<String, Object> config : props) {\n\t\t\t\t\tmergeConfigs(mergedProps, config);\n\t\t\t\t}\n\t\t\t\treturn mergedProps;\n\t\t}\n\t}\n\n\tprivate static void mergeConfigs(Map<String, Object> result, Map<String, Object> input) {\n\t\tinput.forEach((key, value) -> {\n\t\t\tObject currVal = result.get(key);\n\t\t\tif (currVal == null) {\n\t\t\t\tresult.put(key, value);\n\t\t\t} else {\n\t\t\t\tif (currVal instanceof AbstractBeanConfigurator.BeanDefinition) {\n\t\t\t\t\tif (value instanceof AbstractBeanConfigurator.BeanDefinition) {\n\t\t\t\t\t\tresult.put(key, value);\n\t\t\t\t\t} else if (value instanceof Map) {\n\t\t\t\t\t\tmergeConfigs((Map<String, Object>) currVal, (Map<String, Object>) value);\n\t\t\t\t\t}\n\t\t\t\t} else if (currVal instanceof Map && value instanceof Map) {\n\t\t\t\t\tmergeConfigs((Map<String, Object>) currVal, (Map<String, Object>) value);\n\t\t\t\t} else {\n\t\t\t\t\tresult.put(key, value);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/conf/ConfigHolder.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\nimport tigase.cluster.ClusterConnectionManager;\nimport tigase.db.RepositoryFactory;\nimport tigase.io.CertificateContainer;\nimport tigase.io.SSLContextContainer;\nimport tigase.io.SSLContextContainerIfc;\nimport tigase.kernel.beans.config.AbstractBeanConfigurator;\nimport tigase.server.ConnectionManager;\nimport tigase.server.amp.ActionAbstract;\nimport tigase.server.bosh.BoshConnectionManager;\nimport tigase.server.ext.CompRepoItem;\nimport tigase.server.ext.ComponentProtocol;\nimport tigase.server.monitor.MonitorRuntime;\nimport tigase.server.xmppsession.SessionManagerConfig;\nimport tigase.sys.TigaseRuntime;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.util.dns.DNSResolverIfc;\nimport tigase.util.ui.console.CommandlineParameter;\nimport tigase.util.ui.console.ParameterParser;\nimport tigase.util.ui.console.Task;\nimport tigase.util.workqueue.NonpriorityQueue;\nimport tigase.util.workqueue.PriorityQueueAbstract;\nimport tigase.vhosts.VHostItem;\nimport tigase.vhosts.VHostItemImpl;\nimport tigase.xmpp.XMPPIOService;\nimport tigase.xmpp.impl.roster.RosterFactory;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.StringWriter;\nimport java.io.Writer;\nimport java.lang.reflect.Field;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.*;\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport static tigase.conf.Configurable.CLUSTER_NODES_PROP_KEY;\nimport static tigase.io.SSLContextContainerIfc.*;\n\n/**\n * Created by andrzej on 18.09.2016.\n */\npublic class ConfigHolder {\n\n\tpublic static final String PROPERTIES_CONFIG_FILE_DEF = ConfiguratorAbstract.PROPERTY_FILENAME_PROP_DEF;\n\tpublic static final String PROPERTIES_CONFIG_FILE_KEY = ConfiguratorAbstract.PROPERTY_FILENAME_PROP_KEY;\n\tpublic static final String TDSL_CONFIG_FILE_DEF = ConfiguratorAbstract.PROPERTY_FILENAME_PROP_DEF.replace(\n\t\t\t\"/init.properties\", \"/config.tdsl\");\n\tpublic static final String TDSL_CONFIG_FILE_KEY = \"--config-file\";\n\tprivate static final Logger log = Logger.getLogger(ConfigHolder.class.getCanonicalName());\n\tprivate Path configFile = Paths.get(TDSL_CONFIG_FILE_DEF);\n\tprivate Map<String, Object> props = new LinkedHashMap<>();\n\n\tpublic static Path backupOldConfigFile(Path initPropsFile) throws IOException {\n\t\tPath initPropsFileOld = initPropsFile.resolveSibling(initPropsFile.getFileName() + \".old\");\n\t\tint i = 0;\n\t\twhile (Files.exists(initPropsFileOld)) {\n\t\t\ti++;\n\t\t\tinitPropsFileOld = initPropsFile.resolveSibling(initPropsFile.getFileName() + \".old.\" + i);\n\t\t}\n\n\t\tFiles.deleteIfExists(initPropsFileOld);\n\t\tFiles.move(initPropsFile, initPropsFileOld);\n\t\treturn initPropsFileOld;\n\t}\n\n\tpublic static void main(String[] args) throws Exception {\n\t\tString scriptName = System.getProperty(\"scriptName\");\n\t\tParameterParser parser = new ParameterParser(true);\n\n\t\tparser.setTasks(new Task[]{new Task.Builder().name(\"upgrade-config\")\n\t\t\t\t\t\t\t\t\t\t   .description(\"Checks configuration file and upgrades it if needed\")\n\t\t\t\t\t\t\t\t\t\t   .function(ConfigHolder::upgradeConfig).build()});\n\t\tparser.addOption(\n\t\t\t\tnew CommandlineParameter.Builder(null, PROPERTIES_CONFIG_FILE_KEY.replace(\"--\", \"\")).defaultValue(\n\t\t\t\t\t\tPROPERTIES_CONFIG_FILE_DEF)\n\t\t\t\t\t\t.description(\"Path to properties configuration file\")\n\t\t\t\t\t\t.requireArguments(true)\n\t\t\t\t\t\t.build());\n\n\t\tparser.addOption(new CommandlineParameter.Builder(null, TDSL_CONFIG_FILE_KEY.replace(\"--\", \"\")).defaultValue(\n\t\t\t\tTDSL_CONFIG_FILE_DEF).description(\"Path to DSL configuration file\").requireArguments(true).build());\n\n\t\tProperties props = parser.parseArgs(args);\n\t\tOptional<Task> task = parser.getTask();\n\t\tif (props != null && task.isPresent()) {\n\t\t\ttask.get().execute(props);\n\t\t} else {\n\t\t\tString executionCommand = null;\n\t\t\tif (scriptName != null) {\n\t\t\t\texecutionCommand = \"$ \" + scriptName + \" [task] [params-file.conf] [options]\" + \"\\n\\t\\t\" +\n\t\t\t\t\t\t\"if the option defines default then <value> is optional\";\n\t\t\t}\n\n\t\t\tSystem.out.println(parser.getHelp(executionCommand));\n\t\t}\n\t}\n\n\tprivate static void putIfAbsent(Map<String, Object> props, String newKey, Object value) {\n\t\tMap<String, Object> parentProps = null;\n\t\tString[] parts = newKey.split(\"/\");\n\t\tfor (int i = 0; i < parts.length - 1; i++) {\n\t\t\tparentProps = props;\n\t\t\tprops = (Map<String, Object>) props.computeIfAbsent(parts[i], (k) -> new HashMap<>());\n\t\t}\n\t\tString key = parts[parts.length - 1];\n\t\tif (key.equals(\"class\")) {\n\t\t\tif (!(props instanceof AbstractBeanConfigurator.BeanDefinition)) {\n\t\t\t\tAbstractBeanConfigurator.BeanDefinition tmp = new AbstractBeanConfigurator.BeanDefinition();\n\t\t\t\ttmp.setBeanName(parts[parts.length - 2]);\n\t\t\t\ttmp.putAll(props);\n\t\t\t\tprops = tmp;\n\t\t\t\tparentProps.put(tmp.getBeanName(), tmp);\n\t\t\t}\n\t\t\t((AbstractBeanConfigurator.BeanDefinition) props).setClazzName(value.toString());\n\t\t} else {\n\t\t\tprops.put(key, value);\n\t\t}\n\t}\n\n\tpublic static void removeIfExistsAnd(Map<String, Object> props, String oldKey,\n\t\t\t\t\t\t\t\t\t\t BiConsumer<BiConsumer<String, Object>, Object> consumer) {\n\t\tObject value = props.remove(oldKey);\n\t\tif (value != null) {\n\t\t\tconsumer.accept((k, v) -> {\n\t\t\t\tputIfAbsent(props, k, v);\n\t\t\t}, value);\n\t\t}\n\t}\n\n\tpublic static Optional renameIfExists(Map<String, Object> props, String oldKey, String newKey,\n\t\t\t\t\t\t\t\t\t\t  Function<Object, Object> converter) {\n\t\tOptional value = Optional.ofNullable(props.remove(oldKey));\n\t\tvalue = value.map(converter);\n\t\tif (value.isPresent()) {\n\t\t\tputIfAbsent(props, newKey, value.get());\n\t\t}\n\t\treturn value;\n\t}\n\n\tprivate static void upgradeConfig(Properties props) throws Exception {\n\t\tString[] args = props.entrySet()\n\t\t\t\t.stream()\n\t\t\t\t.flatMap(e -> Stream.of(\"--\" + e.getKey(), e.getValue()))\n\t\t\t\t.toArray(x -> new String[x]);\n\n\t\tConfigHolder holder = new ConfigHolder();\n\n\t\ttry {\n\t\t\tholder.fixShutdownThreadIssue();\n\n\t\t\tOptional<String[]> output = holder.loadConfiguration(args);\n\n\t\t\tTigaseRuntime.getTigaseRuntime()\n\t\t\t\t\t.shutdownTigase(output.orElse(new String[]{\n\t\t\t\t\t\t\t\"Configuration file \" + holder.configFile + \" is in DSL format and is valid.\"}));\n\n\t\t} catch (ConfigReader.UnsupportedOperationException e) {\n\t\t\tTigaseRuntime.getTigaseRuntime()\n\t\t\t\t\t.shutdownTigase(new String[]{\"ERROR! Error in configuration file: \" + holder.configFile,\n\t\t\t\t\t\t\t\t\t\t\t\t e.getMessage() + \" at line \" + e.getLine() + \" position \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t e.getPosition(), \"Line: \" + e.getLineContent()});\n\t\t} catch (ConfigReader.ConfigException e) {\n\t\t\tTigaseRuntime.getTigaseRuntime()\n\t\t\t\t\t.shutdownTigase(new String[]{\"ERROR! Error in configuration file: \" + holder.configFile,\n\t\t\t\t\t\t\t\t\t\t\t\t \"Issue with configuration file: \" + e});\n\t\t} catch (RuntimeException e) {\n\t\t\tTigaseRuntime.getTigaseRuntime()\n\t\t\t\t\t.shutdownTigase(new String[]{\"Error! Failed to save upgraded configuration file\", e.getMessage()});\n\t\t}\n\t}\n\n\tprotected static boolean upgradeDSL(Map<String, Object> props) {\n\t\tString before = props.toString();\n\t\tprops.remove(\"--config-file\");\n\t\trenameIfExists(props, \"--cluster-mode\", \"cluster-mode\", Function.identity());\n\t\trenameIfExists(props, Configurable.GEN_VIRT_HOSTS, \"virtual-hosts\", ConfigHolder::convertToListOfStringsIfString);\n\t\tif (!(props.get(\"virtual-hosts\") instanceof Map)) {\n\t\t\trenameIfExists(props, \"virtual-hosts\", \"default-virtual-host\", (val) -> {\n\t\t\t\tList<String> list = ((List<String>) val);\n\t\t\t\tif (list == null || list.isEmpty()) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tVHostItem item = new VHostItemImpl();\n\t\t\t\titem.initFromPropertyString(list.get(0));\n\t\t\t\treturn item.getKey();\n\t\t\t});\n\t\t} else {\n\t\t\trenameIfExists(props, \"virtual-hosts\", \"default-virtual-host\", (val) -> {\n\t\t\t\tMap<String, Object> map = ((Map<String, Object>) val);\n\t\t\t\tif (map == null || map.isEmpty()) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\treturn map.keySet().stream().sorted().findFirst().orElse(null);\n\t\t\t});\n\t\t}\n\t\trenameIfExists(props, Configurable.GEN_DEBUG, \"debug\", ConfigHolder::convertToListOfStringsIfString);\n\t\trenameIfExists(props, Configurable.GEN_DEBUG_PACKAGES, \"debug-packages\", ConfigHolder::convertToListOfStringsIfString);\n\t\trenameIfExists(props, \"--packet.debug.full\", \"logging/packet-debug-full\", Function.identity());\n\t\trenameIfExists(props, \"--\" + PriorityQueueAbstract.QUEUE_IMPLEMENTATION,\n\t\t\t\t\t   \"priority-\" + PriorityQueueAbstract.QUEUE_IMPLEMENTATION, value -> {\n\t\t\t\t\tif (value instanceof String) {\n\t\t\t\t\t\tString str = ((String) value);\n\t\t\t\t\t\tfinal Pattern compile = Pattern.compile(\"tigase\\\\.util\\\\.(.*Queue.*)\");\n\t\t\t\t\t\tfinal Matcher matcher = compile.matcher(str);\n\n\t\t\t\t\t\tif (matcher.matches()) {\n\t\t\t\t\t\t\treturn \"tigase.util.workqueue.\" + matcher.group(1);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treturn value;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn value;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\tif (Boolean.parseBoolean(\"\" + props.remove(\"--nonpriority-queue\"))) {\n\t\t\tprops.putIfAbsent(\"priority-\" + PriorityQueueAbstract.QUEUE_IMPLEMENTATION,\n\t\t\t\t\t\t\t  NonpriorityQueue.class.getCanonicalName());\n\t\t}\n\t\trenameIfExists(props, Configurable.CLUSTER_NODES, CLUSTER_NODES_PROP_KEY, ConfigHolder::convertToListOfStringsIfString);\n\t\trenameIfExists(props, \"--client-port-delay-listening\", \"client-port-delay-listening\", Function.identity());\n\t\trenameIfExists(props, \"--shutdown-thread-dump\", \"shutdown-thread-dump\", Function.identity());\n\t\trenameIfExists(props, ActionAbstract.AMP_SECURITY_LEVEL, \"amp-security-level\", Function.identity());\n\t\tremoveIfExistsAnd(props, \"--cm-see-other-host\", (setter, value) -> {\n\t\t\tsetter.accept(\"c2s/seeOtherHost/class\", value);\n\t\t\tsetter.accept(\"bosh/seeOtherHost/class\", value);\n\t\t\tsetter.accept(\"ws2s/seeOtherHost/class\", value);\n\t\t});\n\t\trenameIfExists(props, \"--watchdog_timeout\", \"watchdog-timeout\", Function.identity());\n\t\trenameIfExists(props, \"--watchdog_delay\", \"watchdog-timeout\", Function.identity());\n\t\trenameIfExists(props, \"--watchdog_ping_type\", \"watchdog-ping-type\", Function.identity());\n\t\trenameIfExists(props, \"--installation-id\", \"installation-id\", Function.identity());\n\t\trenameIfExists(props, ConnectionManager.NET_BUFFER_HT_PROP_KEY,\n\t\t\t\t\t   ConnectionManager.NET_BUFFER_HT_PROP_KEY.substring(2), Function.identity());\n\t\trenameIfExists(props, ConnectionManager.NET_BUFFER_ST_PROP_KEY,\n\t\t\t\t\t   ConnectionManager.NET_BUFFER_ST_PROP_KEY.substring(2), Function.identity());\n\t\trenameIfExists(props, ConnectionManager.HT_TRAFFIC_THROTTLING_PROP_KEY,\n\t\t\t\t\t   ConnectionManager.HT_TRAFFIC_THROTTLING_PROP_KEY.substring(2), Function.identity());\n\t\trenameIfExists(props, ConnectionManager.ST_TRAFFIC_THROTTLING_PROP_KEY,\n\t\t\t\t\t   ConnectionManager.ST_TRAFFIC_THROTTLING_PROP_KEY.substring(2), Function.identity());\n\t\trenameIfExists(props, \"--ws-allow-unmasked-frames\", \"ws-allow-unmasked-frames\", Function.identity());\n\t\trenameIfExists(props, \"--vhost-tls-required\", \"vhost-tls-required\", Function.identity());\n\t\trenameIfExists(props, \"--vhost-register-enabled\", \"vhost-register-enabled\", Function.identity());\n\t\trenameIfExists(props, \"--vhost-message-forward-jid\", \"vhost-message-forward-jid\", Function.identity());\n\t\trenameIfExists(props, \"--vhost-presence-forward-jid\", \"vhost-presence-forward-jid\", Function.identity());\n\t\trenameIfExists(props, \"--vhost-max-users\", \"vhost-max-users\", Function.identity());\n\t\trenameIfExists(props, \"--vhost-anonymous-enabled\", \"vhost-anonymous-enabled\", Function.identity());\n\t\trenameIfExists(props, \"--vhost-disable-dns-check\", \"vhost-disable-dns-check\", Function.identity());\n\t\trenameIfExists(props, \"--s2s-secret\", \"vhost-man/defaults/s2s-secret\", Function.identity());\n\t\trenameIfExists(props, \"--test\", \"logging/rootLevel\",\n\t\t\t\t\t   value -> Boolean.TRUE.equals(value) ? Level.WARNING : Level.CONFIG);\n\n\t\trenameIfExists(props, \"--\" + SSLContextContainerIfc.DEFAULT_DOMAIN_CERT_KEY,\n\t\t\t\t\t   \"certificate-container/\" + SSLContextContainerIfc.DEFAULT_DOMAIN_CERT_KEY, Function.identity());\n\t\trenameIfExists(props, \"--\" + CertificateContainer.SNI_DISABLE_KEY,\n\t\t\t\t\t   \"certificate-container/\" + CertificateContainer.SNI_DISABLE_KEY, Function.identity());\n\t\trenameIfExists(props, \"--\" + SSLContextContainerIfc.SERVER_CERTS_LOCATION_KEY,\n\t\t\t\t\t   \"certificate-container/\" + SSLContextContainerIfc.SERVER_CERTS_LOCATION_KEY,\n\t\t\t\t\t   Function.identity());\n\t\trenameIfExists(props, \"--\" + SSLContextContainerIfc.TRUSTED_CERTS_DIR_KEY,\n\t\t\t\t\t   \"certificate-container/\" + SSLContextContainerIfc.TRUSTED_CERTS_DIR_KEY, Function.identity());\n\t\trenameIfExists(props, \"--pem-privatekey-password\", \"certificate-container/pem-privatekey-password\",\n\t\t\t\t\t   Function.identity());\n\t\trenameIfExists(props, \"--tls-jdk-nss-bug-workaround-active\", \"tls-jdk-nss-bug-workaround-active\",\n\t\t\t\t\t   Function.identity());\n\t\trenameIfExists(props, \"--tls-enabled-protocols\", \"tls-enabled-protocols\", ConfigHolder::convertToListOfStringsIfString);\n\t\trenameIfExists(props, \"--tls-enabled-ciphers\", \"tls-enabled-ciphers\", ConfigHolder::convertToListOfStringsIfString);\n\t\trenameIfExists(props, \"--hardened-mode\", \"hardened-mode\", Function.identity());\n\t\trenameIfExists(props, \"hardened-mode\", \"hardened-mode\", val -> SSLContextContainer.HardenedModeVHostItemExtension.parseHardenedModeFromString(\n\t\t\t\t\tString.valueOf(val)));\n\n\t\tprops.entrySet().stream()\n\t\t\t\t.filter(Map.class::isInstance)\n\t\t\t\t.map(Map.class::cast)\n\t\t\t\t.filter(component -> component.containsKey(\"sslContextContainer\") )\n\t\t\t\t.map(component -> component.get(\"sslContextContainer\") )\n\t\t\t\t.filter(Map.class::isInstance)\n\t\t\t\t.map(Map.class::cast)\n\t\t\t\t.forEach(container -> {\n\t\t\t\t\trenameIfExists(props, \"hardened-mode\", \"hardened-mode\", val -> SSLContextContainer.HardenedModeVHostItemExtension.parseHardenedModeFromString(\n\t\t\t\t\t\t\tString.valueOf(val)));\n\t\t\t\t});\n\n\t\tboolean allowInvalidCerts = Boolean.parseBoolean((String) props.remove(ALLOW_INVALID_CERTS_KEY));\n\t\tboolean allowSelfSignedCerts = Boolean.parseBoolean((String) props.remove(ALLOW_SELF_SIGNED_CERTS_KEY));\n\t\tif (allowInvalidCerts || allowSelfSignedCerts) {\n\t\t\tprops.put(\"certificate-container/ssl-trust-model\",\n\t\t\t\t\t  allowInvalidCerts ? \"all\" : (allowSelfSignedCerts ? \"selfsigned\" : \"trusted\"));\n\t\t}\n\n\t\trenameIfExists(props, \"--\" + BoshConnectionManager.BOSH_EXTRA_HEADERS_FILE_PROP_KEY,\n\t\t\t\t\t   BoshConnectionManager.BOSH_EXTRA_HEADERS_FILE_PROP_KEY, Function.identity());\n\t\trenameIfExists(props, \"--\" + BoshConnectionManager.BOSH_CLOSE_CONNECTION_PROP_KEY,\n\t\t\t\t\t   BoshConnectionManager.BOSH_CLOSE_CONNECTION_PROP_KEY, Function.identity());\n\t\trenameIfExists(props, \"--\" + BoshConnectionManager.CLIENT_ACCESS_POLICY_FILE_PROP_KEY,\n\t\t\t\t\t   BoshConnectionManager.CLIENT_ACCESS_POLICY_FILE_PROP_KEY, Function.identity());\n\t\trenameIfExists(props, \"--stringprep-processor\", \"stringprep-processor\", Function.identity());\n\t\trenameIfExists(props, ClusterConnectionManager.CONNECT_ALL_PAR,\n\t\t\t\t\t   \"cl-comp/\" + ClusterConnectionManager.CONNECT_ALL_PROP_KEY, Function.identity());\n\t\trenameIfExists(props, \"--cluster-connections-per-node\", \"cl-comp/connections-per-node\", Function.identity());\n\n\t\trenameIfExists(props, \"--\" + DNSResolverFactory.TIGASE_RESOLVER_CLASS,\n\t\t\t\t\t   \"dns-resolver/\" + DNSResolverFactory.TIGASE_RESOLVER_CLASS, Function.identity());\n\t\trenameIfExists(props, \"--\" + DNSResolverIfc.TIGASE_PRIMARY_ADDRESS,\n\t\t\t\t\t   \"dns-resolver/\" + DNSResolverIfc.TIGASE_PRIMARY_ADDRESS, Function.identity());\n\t\trenameIfExists(props, \"--\" + DNSResolverIfc.TIGASE_SECONDARY_ADDRESS,\n\t\t\t\t\t   \"dns-resolver/\" + DNSResolverIfc.TIGASE_SECONDARY_ADDRESS, Function.identity());\n\n\t\trenameIfExists(props, \"--s2s-skip-tls-hostnames\", \"s2s/skip-tls-hostnames\", ConfigHolder::convertToListOfStringsIfString);\n\t\trenameIfExists(props, \"--\" + Configurator.SCRIPTS_DIR_PROP_KEY, Configurator.SCRIPTS_DIR_PROP_KEY,\n\t\t\t\t\t   Function.identity());\n\n\t\trenameIfExists(props, \"--\" + XMPPIOService.CROSS_DOMAIN_POLICY_FILE_PROP_KEY,\n\t\t\t\t\t   XMPPIOService.CROSS_DOMAIN_POLICY_FILE_PROP_KEY, Function.identity());\n\t\trenameIfExists(props, \"--domain-filter-policy\", \"domain-filter-policy\", Function.identity());\n\t\tprops.remove(\"--script-dir\");\n\t\tremoveIfExistsAnd(props, \"--new-connections-throttling\", (setter, value) -> {\n\t\t\tString[] all_ports_thr = ((String) value).split(\",\");\n\n\t\t\tfor (String port_thr : all_ports_thr) {\n\t\t\t\tString[] port_thr_ar = port_thr.trim().split(\":\");\n\n\t\t\t\tif (port_thr_ar.length == 2) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tint port_no = Integer.parseInt(port_thr_ar[0]);\n\t\t\t\t\t\tlong throttling = Long.parseLong(port_thr_ar[1]);\n\t\t\t\t\t\tswitch (port_no) {\n\t\t\t\t\t\t\tcase 5222:\n\t\t\t\t\t\t\tcase 5223:\n\t\t\t\t\t\t\t\tsetter.accept(\"c2s/connections/\" + port_no + \"/new-connections-throttling\", throttling);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase 5280:\n\t\t\t\t\t\t\t\tsetter.accept(\"bosh/connections/\" + port_no + \"/new-connections-throttling\",\n\t\t\t\t\t\t\t\t\t\t\t  throttling);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase 5277:\n\t\t\t\t\t\t\t\tsetter.accept(\"cl-comp/connections/\" + port_no + \"/new-connections-throttling\", throttling);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase 5290:\n\t\t\t\t\t\t\t\tsetter.accept(\"ws2s/connections/\" + port_no + \"/new-connections-throttling\",\n\t\t\t\t\t\t\t\t\t\t\t  throttling);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase 5269:\n\t\t\t\t\t\t\t\tsetter.accept(\"s2s/connections/\" + port_no + \"/new-connections-throttling\", throttling);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tStream.of(\"c2s\", \"bosh\", \"ws2s\", \"s2s\", \"ext\").forEach(cmp -> {\n\t\t\t\t\t\t\t\t\tMap<String, Object> cmpCfg = (Map<String, Object>) props.get(cmp);\n\t\t\t\t\t\t\t\t\tif (cmpCfg == null) {\n\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tMap<String, Object> cmpConns = (Map<String, Object>) cmpCfg.get(\"connections\");\n\t\t\t\t\t\t\t\t\tif (cmpConns == null) {\n\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tMap<String, Object> portSettings = (Map<String, Object>) cmpConns.get(\"\" + port_no);\n\t\t\t\t\t\t\t\t\tif (portSettings == null) {\n\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tportSettings.putIfAbsent(\"new-connections-throttling\", throttling);\n\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (Exception e) {\n\n\t\t\t\t\t\t// bad configuration\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Connections throttling configuration error, bad format, \" +\n\t\t\t\t\t\t\t\t\t\t\"check the documentation for a correct syntax, \" + \"port throttling config: {0}\",\n\t\t\t\t\t\t\t\tport_thr);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\n\t\t\t\t\t// bad configuration\n\t\t\t\t\tlog.log(Level.WARNING, \"Connections throttling configuration error, bad format, \" +\n\t\t\t\t\t\t\t\"check the documentation for a correct syntax, \" + \"port throttling config: {0}\", port_thr);\n\t\t\t\t}\n\t\t\t}\n\n\t\t});\n\t\trenameIfExists(props, \"--\" + RosterFactory.ROSTER_IMPL_PROP_KEY, RosterFactory.ROSTER_IMPL_PROP_KEY,\n\t\t\t\t\t   Function.identity());\n\t\trenameIfExists(props, \"--s2s-ejabberd-bug-workaround-active\", \"s2s/dialback/ejabberd-bug-workaround\",\n\t\t\t\t\t   (v) -> Boolean.parseBoolean(\"\" + v));\n\t\trenameIfExists(props, \"--\" + SessionManagerConfig.SM_THREADS_POOL_PROP_KEY,\n\t\t\t\t\t   \"sess-man/\" + SessionManagerConfig.SM_THREADS_POOL_PROP_KEY, Function.identity());\n\n\t\tremoveIfExistsAnd(props, \"--\" + SSL_CONTAINER_CLASS_KEY, (setter, value) -> {\n\t\t\tMap<String, Object> cfg = (Map<String, Object>) props.getOrDefault(\"rootSslContextContainer\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   new HashMap<>());\n\t\t\tif (cfg instanceof AbstractBeanConfigurator.BeanDefinition) {\n\t\t\t\t((AbstractBeanConfigurator.BeanDefinition) cfg).setClazzName(value.toString());\n\t\t\t} else {\n\t\t\t\tAbstractBeanConfigurator.BeanDefinition.Builder builder = new AbstractBeanConfigurator.BeanDefinition.Builder()\n\t\t\t\t\t\t.name(\"rootSslContextContainer\")\n\t\t\t\t\t\t.active(true)\n\t\t\t\t\t\t.clazz(value.toString());\n\t\t\t\tcfg.forEach(builder::property);\n\t\t\t\tprops.put(\"rootSslContextContainer\", builder.build());\n\t\t\t}\n\t\t});\n\n\t\tStream.of(\"c2s\", \"bosh\", \"ws2s\").forEach(cmp -> {\n\t\t\tMap<String, Object> cmpCfg = (Map<String, Object>) props.get(cmp);\n\t\t\tif (cmpCfg != null) {\n\t\t\t\tMap<String, Object> oldCfg = (Map<String, Object>) cmpCfg.remove(\"cm-see-other-host\");\n\t\t\t\tMap<String, Object> newCfg = (Map<String, Object>) cmpCfg.computeIfAbsent(\"seeOtherHost\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  (x) -> new HashMap<String, Object>());\n\t\t\t\tif (oldCfg != null) {\n\t\t\t\t\tnewCfg.putAll(oldCfg);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tremoveIfExistsAnd(props, RepositoryFactory.USER_REPO_POOL_CLASS, (setter, value) -> {\n\t\t\tMap<String, Object> repos = ((Map<String, Object>) props.get(\"userRepository\"));\n\t\t\t(repos != null ? repos.keySet() : Collections.singleton(\"default\")).forEach(name -> {\n\t\t\t\tsetter.accept(\"userRepository/\" + name + \"/pool-class\", value);\n\t\t\t});\n\t\t});\n\t\tremoveIfExistsAnd(props, RepositoryFactory.USER_REPO_POOL_SIZE, (setter, value) -> {\n\t\t\tMap<String, Object> repos = ((Map<String, Object>) props.get(\"userRepository\"));\n\t\t\t(repos != null ? repos.keySet() : Collections.singleton(\"default\")).forEach(name -> {\n\t\t\t\tsetter.accept(\"userRepository/\" + name + \"/pool-size\", value);\n\t\t\t});\n\t\t});\n\n\t\tOptional.ofNullable(props.get(\"sess-man\")).filter(Map.class::isInstance).map(Map.class::cast).ifPresent(sessManCfg -> {\n\t\t\tObject saslMechanisms = (String) sessManCfg.remove(\"enabled-mechanisms\");\n\t\t\tif (saslMechanisms != null) {\n\t\t\t\tif (saslMechanisms instanceof String) {\n\t\t\t\t\tputIfAbsent(props, \"sess-man/sasl-provider/allowed-mechanisms\",\n\t\t\t\t\t\t\t\tArrays.asList(((String) saslMechanisms).split(\",\")));\n\t\t\t\t} else {\n\t\t\t\t\tputIfAbsent(props, \"sess-man/sasl-provider/allowed-mechanisms\", saslMechanisms);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfinal Object jabberIqLast = sessManCfg.remove(\"jabber:iq:last\");\n\t\t\tif (jabberIqLast != null) {\n\t\t\t\tAbstractBeanConfigurator.BeanDefinition.Builder builder = new AbstractBeanConfigurator.BeanDefinition.Builder()\n\t\t\t\t\t\t.name(\"jabber:iq:last-marker\")\n\t\t\t\t\t\t.active(true);\n\t\t\t\tfinal AbstractBeanConfigurator.BeanDefinition build = builder.build();\n\t\t\t\tsessManCfg.put(\"jabber:iq:last-marker\", build);\n\t\t\t\tputIfAbsent(props, \"sess-man/jabber:iq:last-marker/jabber:iq:last\", jabberIqLast);\n\t\t\t}\n\t\t});\n\n\t\tprops.remove(\"--api-keys\");\n\t\t\n\t\tprops.computeIfPresent(\"http\", (k, v) -> {\n\t\t\tif (v instanceof Map) {\n\t\t\t\t((Map<String, Object>) v).remove(\"api-keys\");\n\t\t\t\t((Map<String, Object>) v).computeIfPresent(\"rest\", (k1, v1) -> {\n\t\t\t\t\tif (v1 instanceof Map) {\n\t\t\t\t\t\t((Map<String, Object>) v1).remove(\"api-keys\");\n\t\t\t\t\t}\n\t\t\t\t\treturn  v1;\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn v;\n\t\t});\n\n\t\tprops.values()\n\t\t\t\t.stream()\n\t\t\t\t.filter(it -> it instanceof AbstractBeanConfigurator.BeanDefinition)\n\t\t\t\t.map(it -> (AbstractBeanConfigurator.BeanDefinition) it)\n\t\t\t\t.filter(it -> ComponentProtocol.class.getName().equals(it.getClazzName())).forEach(it -> {\n\t\t\t\t\tit.computeIfPresent(\"repository\", (k,v) -> {\n\t\t\t\t\t\tif (v instanceof Map) {\n\t\t\t\t\t\t\tObject v1 = ((Map)v).remove(\"items\");\n\t\t\t\t\t\t\tif (v1 != null && v1 instanceof List) {\n\t\t\t\t\t\t\t\tList<String> v2 = (List<String>) v1;\n\t\t\t\t\t\t\t\tif (!v2.isEmpty()) {\n\t\t\t\t\t\t\t\t\tOldConfigHolder.saveOldExternalComponentConfigItems(v2.toArray(new String[v2.size()]));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else if (v1 instanceof Map) {\n\t\t\t\t\t\t\t\tMap<String, Object> v2 = (Map<String, Object>) v1;\n\t\t\t\t\t\t\t\tif (!v2.isEmpty()) {\n\t\t\t\t\t\t\t\t\tString[] components = v2.entrySet().stream().map(e -> {\n\t\t\t\t\t\t\t\t\t\tMap<String, Object> p = ((Map<String, Object>) e.getValue());\n\t\t\t\t\t\t\t\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\t\t\t\t\t\t\t\tString[] parts = Stream.of(e.getKey(), p.get(\"password\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t p.get(CompRepoItem.CONN_TYPE_ATTR),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t p.get(CompRepoItem.PORT_NO_ATTR),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t p.get(CompRepoItem.REMOTE_HOST_ATTR),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t p.get(CompRepoItem.PROTO_XMLNS_ATTR),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t p.get(CompRepoItem.LB_NAME_ATTR),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t p.get(CompRepoItem.SOCKET_ATTR))\n\t\t\t\t\t\t\t\t\t\t\t\t.map(String::valueOf).toArray(x -> new String[x]);\n\t\t\t\t\t\t\t\t\t\tfor (String part : parts) {\n\t\t\t\t\t\t\t\t\t\t\tif (\"null\".equals(part)) {\n\t\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tif (sb.length() > 0) {\n\t\t\t\t\t\t\t\t\t\t\t\tsb.append(\":\");\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tsb.append(part);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\treturn sb.toString();\n\t\t\t\t\t\t\t\t\t}).toArray(x -> new String[x]);\n\t\t\t\t\t\t\t\t\tOldConfigHolder.saveOldExternalComponentConfigItems(components);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn v;\n\t\t\t\t\t});\n\t\t});\n\n\t\t// move handlers from basic-conf to logging/handlers\n\t\tOptional.ofNullable(props.get(\"basic-conf\"))\n\t\t\t\t.filter(Map.class::isInstance)\n\t\t\t\t.map(Map.class::cast)\n\t\t\t\t.map(v -> v.remove(\"logging\"))\n\t\t\t\t.filter(Map.class::isInstance)\n\t\t\t\t.map(Map.class::cast)\n\t\t\t\t.map(v -> v.get(\"handlers\"))\n\t\t\t\t.filter(String.class::isInstance)\n\t\t\t\t.map(String.class::cast)\n\t\t\t\t.map(v -> v.split(\" \"))\n\t\t\t\t.map(Arrays::asList)\n\t\t\t\t.ifPresent(handlers -> {\n\t\t\t\t\t((Map) props.computeIfAbsent(\"logging\", k -> new HashMap<>())).putIfAbsent(\n\t\t\t\t\t\t\t\"rootHandlers\", handlers);\n\t\t\t\t});\n\n\t\tOptional.ofNullable(props.get(\"logging\")).filter(Map.class::isInstance).map(Map.class::cast).ifPresent(logging -> {\n\t\t\tCollection<String> handlers = Optional.ofNullable(logging.get(\"rootHandlers\"))\n\t\t\t\t\t.filter(Map.class::isInstance)\n\t\t\t\t\t.map(Map.class::cast)\n\t\t\t\t\t.map(map -> (Collection<String>) map.keySet())\n\t\t\t\t\t.orElseGet(\n\t\t\t\t\t\t\t() -> Arrays.asList(\"java.util.logging.ConsoleHandler\", \"java.util.logging.FileHandler\"));\n\t\t\tfor (String handler : handlers) {\n\t\t\t\tOptional.ofNullable(logging.remove(handler))\n\t\t\t\t\t\t.ifPresent(cfg -> ((Map) logging.computeIfAbsent(\"handlers\", k -> new HashMap<>())).put(handler,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcfg));\n\t\t\t}\n\n\t\t\tfor (String key : new ArrayList<>((Set<String>) logging.keySet())) {\n\t\t\t\tObject value = logging.get(key);\n\t\t\t\tif (value instanceof Map) {\n\t\t\t\t\tMap map = (Map) value;\n\t\t\t\t\tif (map.containsKey(\"level\") || map.containsKey(\"useParentHandlers\")) {\n\t\t\t\t\t\tlogging.remove(key);\n\t\t\t\t\t\t((Map) logging.computeIfAbsent(\"loggers\", k -> new HashMap<>())).putIfAbsent(key, value);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tOptional.ofNullable(props.get(\"basic-conf\"))\n\t\t\t\t.filter(Map.class::isInstance)\n\t\t\t\t.map(Map.class::cast)\n\t\t\t\t.ifPresent(basicConf -> {\n\t\t\t        new ArrayList<String>(basicConf.keySet()).stream().filter(key -> key.startsWith(CertificateContainer.PER_DOMAIN_CERTIFICATE_KEY)).forEach(key -> {\n\t\t\t        \tObject value = basicConf.remove(key);\n\t\t\t\t\t\tMap<String, Object> customCerts = (Map<String, Object>) ((Map) props.computeIfAbsent(\"certificate-container\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   k -> new HashMap<>())).computeIfAbsent(\n\t\t\t\t\t\t\t\t\"custom-certificates\", k -> new HashMap<>());\n\n\t\t\t\t\t\tcustomCerts.putIfAbsent(key.replace(CertificateContainer.PER_DOMAIN_CERTIFICATE_KEY, \"\"), value);\n\t\t\t\t\t});\n\t\t\t\t\tnew ArrayList<String>(basicConf.keySet()).stream().filter(key -> key.startsWith(\"virtual-hosts-cert-\")).forEach(key -> {\n\t\t\t\t\t\tObject value = basicConf.remove(key);\n\t\t\t\t\t\tMap<String, Object> customCerts = (Map<String, Object>) ((Map) props.computeIfAbsent(\"certificate-container\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t k -> new HashMap<>())).computeIfAbsent(\n\t\t\t\t\t\t\t\t\"custom-certificates\", k -> new HashMap<>());\n\n\t\t\t\t\t\tcustomCerts.putIfAbsent(key.replace(\"virtual-hosts-cert-\", \"\"), value);\n\t\t\t\t\t});\n\t\t});\n\n\t\t// remove basic-conf if it is empty\n\t\tOptional.ofNullable(props.get(\"basic-conf\"))\n\t\t\t\t.filter(Map.class::isInstance)\n\t\t\t\t.map(Map.class::cast)\n\t\t\t\t.filter(Map::isEmpty)\n\t\t\t\t.ifPresent(v -> props.remove(\"basic-conf\"));\n\n\t\tOptional.ofNullable(props.get(\"message-router\")).filter(Map.class::isInstance).map(Map.class::cast).ifPresent(messageRouterCfg -> {\n\t\t\tOptional.ofNullable(messageRouterCfg.get(\"components\")).filter(Map.class::isInstance).map(Map.class::cast).ifPresent(componentsCfg -> {\n\t\t\t\tOptional.ofNullable(componentsCfg.get(\"msg-receivers\")).filter(Map.class::isInstance).map(Map.class::cast).ifPresent(msgReceiversCfg -> {\n\t\t\t\t\t((Stream<Map.Entry<String, Object>>) msgReceiversCfg.entrySet().stream()).filter(e -> e.getKey().endsWith(\".active\")).forEach(e -> {\n\t\t\t\t\t\tString compName = e.getKey().replace(\".active\", \"\" );\n\t\t\t\t\t\tAbstractBeanConfigurator.BeanDefinition bean = (AbstractBeanConfigurator.BeanDefinition) props.compute(compName, (k, v) -> {\n\t\t\t\t\t\t\tif (AbstractBeanConfigurator.BeanDefinition.class.isInstance(v)) {\n\t\t\t\t\t\t\t\treturn v;\n\t\t\t\t\t\t\t} else if (Map.class.isInstance(v)) {\n\t\t\t\t\t\t\t\tAbstractBeanConfigurator.BeanDefinition def = new AbstractBeanConfigurator.BeanDefinition();\n\t\t\t\t\t\t\t\tdef.setBeanName(k);\n\t\t\t\t\t\t\t\tdef.putAll((Map<String, Object>) v);\n\t\t\t\t\t\t\t\treturn def;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tAbstractBeanConfigurator.BeanDefinition def = new AbstractBeanConfigurator.BeanDefinition();\n\t\t\t\t\t\t\t\tdef.setBeanName(k);\n\t\t\t\t\t\t\t\treturn def;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t\tbean.setActive(Boolean.parseBoolean(String.valueOf(e.getValue())));\n\t\t\t\t\t});\n\t\t\t\t\tSet<String> toRemove = ((Stream<String>) messageRouterCfg.keySet().stream()).filter(e -> e.endsWith(\".active\")).collect(\n\t\t\t\t\t\t\tCollectors.toSet());\n\t\t\t\t\ttoRemove.forEach(messageRouterCfg::remove);\n\n\t\t\t\t\tif (messageRouterCfg.isEmpty()) {\n\t\t\t\t\t\tcomponentsCfg.remove(\"msg-receivers\");\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tif (componentsCfg.isEmpty()) {\n\t\t\t\t\tmessageRouterCfg.remove(\"components\");\n\t\t\t\t}\n\t\t\t});\n\t\t\tif (messageRouterCfg.isEmpty()) {\n\t\t\t\tprops.remove(\"message-router\");\n\t\t\t}\n\t\t});\n\n\t\tOptional.ofNullable(props.get(\"sess-man\")).filter(Map.class::isInstance).map(Map.class::cast).ifPresent(sessManCfg -> {\n\t\t\tOptional.ofNullable(sessManCfg.remove(\"urn:xmpp:push:0:ext\")).ifPresent(extCfg -> {\n\t\t\t\tAbstractBeanConfigurator.BeanDefinition cfg = new AbstractBeanConfigurator.BeanDefinition();\n\t\t\t\tcfg.setBeanName(\"urn:xmpp:push:0\");\n\t\t\t\tcfg.setActive(true);\n\n\t\t\t\tcfg.compute(\"away\", (k,v) -> {\n\t\t\t\t\tAbstractBeanConfigurator.BeanDefinition c = new AbstractBeanConfigurator.BeanDefinition();\n\t\t\t\t\tc.setBeanName(\"away\");\n\t\t\t\t\tc.setActive(true);\n\t\t\t\t\treturn c;\n\t\t\t\t});\n\t\t\t\tsessManCfg.put(cfg.getBeanName(), cfg);\n\t\t\t});\n\t\t});\n\t\t\n\t\tString after = props.toString();\n\t\treturn !before.equals(after);\n\t}\n\n\tpublic Optional<String[]> loadConfiguration(String[] args) throws IOException, ConfigReader.ConfigException {\n\t\tboolean hasCustomConfigFile = false;\n\t\tfor (int i = 0; i < args.length; i++) {\n\t\t\tif (TDSL_CONFIG_FILE_KEY.equals(args[i]) && (i + 1) < args.length) {\n\t\t\t\tconfigFile = Paths.get(args[++i]);\n\t\t\t\thasCustomConfigFile = true;\n\t\t\t}\n\t\t\tif (PROPERTIES_CONFIG_FILE_KEY.equals(args[i]) && (i + 1) < args.length) {\n\t\t\t\tif (!hasCustomConfigFile) {\n\t\t\t\t\tPath propsFilePath = Paths.get(args[++i]);\n\t\t\t\t\tconfigFile = propsFilePath.resolveSibling(configFile.getFileName());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tOldConfigHolder oldConfigHolder = new OldConfigHolder();\n\t\toldConfigHolder.convert(args, configFile);\n\t\tOptional<String[]> output = oldConfigHolder.getOutput();\n\n\t\tloadFromDSLFiles();\n\t\tif (upgradeDSL(props)) {\n\t\t\ttry {\n\t\t\t\tOptional<Path> configFileOld = Optional.ofNullable(backupOldConfigFile(configFile));\n\t\t\t\tsaveToDSLFile(configFile.toFile());\n\t\t\t\tlog.log(Level.CONFIG, \"Configuration file {0} was updated to match current format.\" +\n\t\t\t\t\t\tconfigFileOld.map(path -> \" Previous version of configuration file was saved as \" + path)\n\t\t\t\t\t\t\t\t.orElse(\"\"), new Object[]{configFile});\n\n\t\t\t\tif (!oldConfigHolder.getOutput().isPresent()) {\n\t\t\t\t\toutput = Optional.of(Stream.of(java.text.MessageFormat.format(\n\t\t\t\t\t\t\t\"Configuration file {0} was updated to match current format.\", configFile),\n\t\t\t\t\t\t\t\t\t\t\t\t   configFileOld.map(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   path -> \"Previous version of configuration file was saved as \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   path).orElse(\"\"))\n\t\t\t\t\t\t\t\t\t\t\t\t .filter(line -> !line.isEmpty())\n\t\t\t\t\t\t\t\t\t\t\t\t .toArray(x -> new String[x]));\n\t\t\t\t}\n\t\t\t} catch (IOException ex) {\n\t\t\t\tlog.log(Level.SEVERE, \"could not replace configuration file with file in DSL format\", ex);\n\t\t\t\tthrow new RuntimeException(ex.getMessage(), ex);\n\t\t\t}\n\t\t}\n//// We can't rely on ConfigField type annotation here to determine which fields should be obfuscated\n//// thus we won't be including parsed configuration in console-log file.\n//\t\ttry (Writer w = new StringWriter()) {\n//\t\t\tnew ConfigWriter().resolveVariables().write(w, props);\n//\t\t\tlog.log(Level.CONFIG, \"Loaded configuration:\\n\" + w.toString());\n//\t\t}\n\n\t\treturn output;\n\t}\n\n\tpublic Map<String, Object> getProperties() {\n\t\treturn props;\n\t}\n\n\tpublic void setProperties(Map<String, Object> props) {\n\t\tthis.props = props;\n\t}\n\n\tpublic void saveToDSLFile(File f) throws IOException {\n\t\tnew ConfigWriter().write(f, props);\n\t}\n\n\tpublic Path getConfigFilePath() {\n\t\treturn configFile;\n\t}\n\n\tprivate void loadFromDSLFiles() throws IOException, ConfigReader.ConfigException {\n\t\tlog.log(Level.CONFIG, \"Loading configuration from file: {0}\", configFile);\n\t\tMap<String, Object> loaded = new ConfigReader().read(configFile.toFile());\n\t\tprops.putAll(loaded);\n\t}\n\n\tprivate void fixShutdownThreadIssue() {\n\t\tMonitorRuntime.getMonitorRuntime();\n\t\ttry {\n\t\t\tField f = MonitorRuntime.class.getDeclaredField(\"mainShutdownThread\");\n\t\t\tf.setAccessible(true);\n\t\t\tMonitorRuntime monitorRuntime = MonitorRuntime.getMonitorRuntime();\n\t\t\tRuntime.getRuntime().removeShutdownHook((Thread) f.get(monitorRuntime));\n\t\t} catch (NoSuchFieldException | IllegalAccessException ex) {\n\t\t\tlog.log(Level.FINEST, \"There was an error with unregistration of shutdown hook\", ex);\n\t\t}\n\t}\n\n\tprivate static Stream<String> stringToStreamOfStrings(String val) {\n\t\treturn Arrays.stream(val.split(\",\")).map(String::trim);\n\t}\n\n\tprivate static List<String> stringToListOfStrings(String val) {\n\t\treturn stringToStreamOfStrings(val).collect(Collectors.toList());\n\t}\n\t\n\tprivate static Object convertToListOfStringsIfString(Object val) {\n\t\tif (val instanceof String) {\n\t\t\treturn stringToListOfStrings((String) val);\n\t\t} else {\n\t\t\treturn val;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/conf/ConfigItem.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\nimport tigase.db.comp.RepositoryItemAbstract;\nimport tigase.server.Command;\nimport tigase.server.Packet;\nimport tigase.util.repository.DataTypes;\nimport tigase.xml.Element;\n\nimport java.util.logging.Logger;\n\n/**\n * Created: Dec 10, 2009 2:40:26 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class ConfigItem\n\t\textends RepositoryItemAbstract {\n\n\tpublic static final String CLUSTER_NODE_ATTR = \"cluster-node\";\n\n\tpublic static final String COMPONENT_NAME_ATTR = \"comp-name\";\n\n\t// public static final String CLUSTER_NODE_LABEL = \"Cluster node\";\n\n\tpublic static final String COMPONENT_NAME_LABEL = \"Component name\";\n\n\tpublic static final String FLAG_ATTR = \"flag\";\n\n\tpublic static final String KEY_NAME_ATTR = \"key-name\";\n\n\tpublic static final String KEY_NAME_LABEL = \"Property key name\";\n\n\tpublic static final String NODE_NAME_ATTR = \"node-name\";\n\n\tpublic static final String NODE_NAME_LABEL = \"Property node name\";\n\n\tpublic static final String REPO_ITEM_ELEM_NAME = \"prop\";\n\n\tpublic static final String VALUE_ATTR = \"value\";\n\n\tpublic static final String VALUE_LABEL = \"Propety value\";\n\n\tpublic static final String VALUE_TYPE_ATTR = \"value-type\";\n\n\tprivate static final Logger log = Logger.getLogger(ConfigItem.class.getName());\n\n\tpublic enum FLAGS {\n\t\tINITIAL,\n\t\tDEFAULT,\n\t\tUPDATED;\n\t}\n\tprivate String clusterNode = null;\n\tprivate String compName = null;\n\tprivate FLAGS flag = FLAGS.DEFAULT;\n\tprivate String keyName = null;\n\tprivate long lastModificationTime = -1;\n\tprivate String nodeName = null;\n\n\tprivate Object value = null;\n\n\t@Override\n\tpublic void addCommandFields(Packet packet) {\n\n\t\t// Command.addFieldValue(packet, CLUSTER_NODE_LABEL,\n\t\t// (clusterNode != null ? clusterNode : \"\"));\n\t\tif (compName != null) {\n\t\t\tCommand.addTextField(packet, COMPONENT_NAME_LABEL, compName);\n\t\t}\n\t\tif (nodeName != null) {\n\t\t\tCommand.addTextField(packet, NODE_NAME_LABEL, nodeName);\n\t\t}\n\t\tCommand.addTextField(packet, KEY_NAME_LABEL, ((keyName != null) ? keyName : \"\"));\n\t\tCommand.addTextField(packet, \"    \", \"    \");\n\n\t\tString value_label = VALUE_LABEL;\n\t\tString value_str = \"\";\n\n\t\tif (value != null) {\n\t\t\tvalue_str = DataTypes.valueToString(value);\n\t\t\tvalue_label += \" [\" + DataTypes.getTypeId(value) + \"]\";\n\t\t}\n\t\tCommand.addFieldValue(packet, value_label, value_str);\n\t\tsuper.addCommandFields(packet);\n\t}\n\n\tpublic String getClusterNode() {\n\t\treturn clusterNode;\n\t}\n\n\tpublic String getCompName() {\n\t\treturn compName;\n\t}\n\n\t/**\n\t * Returns a configuration property key which is constructed in a following way: <code> nodeName + \"/\" + keyName\n\t * </code>\n\t */\n\tpublic String getConfigKey() {\n\t\treturn ((nodeName != null) ? nodeName + \"/\" : \"\") + keyName;\n\t}\n\n\t/**\n\t * Returns a configuration property value.\n\t */\n\tpublic Object getConfigVal() {\n\t\treturn value;\n\t}\n\n\tpublic String getConfigValToString() {\n\t\treturn (value == null) ? null : DataTypes.valueToString(value);\n\t}\n\n\t@Override\n\tpublic String getElemName() {\n\t\treturn REPO_ITEM_ELEM_NAME;\n\t}\n\n\tpublic FLAGS getFlag() {\n\t\treturn flag;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t * <br>\n\t * Returns ConfigItem key which is constructed in a following way: {@code compName + \"/\" + nodeName + \"/\" +\n\t * keyName}\n\t */\n\t@Override\n\tpublic String getKey() {\n\t\treturn ((compName != null) ? compName + \"/\" : \"\") + ((nodeName != null) ? nodeName + \"/\" : \"\") + keyName;\n\t}\n\n\tprotected void setKey(String key) {\n\t\t// nothing to do..\n\t}\n\n\t/**\n\t * Returns a property key which is constructed in a following way: {@code keyName}\n\t */\n\tpublic String getKeyName() {\n\t\treturn keyName;\n\t}\n\n\tpublic String getNodeName() {\n\t\treturn nodeName;\n\t}\n\n\t@Override\n\tpublic void initFromCommand(Packet packet) {\n\t\tsuper.initFromCommand(packet);\n\n\t\tString tmp = Command.getFieldValue(packet, COMPONENT_NAME_LABEL);\n\n\t\tif ((tmp != null) && !tmp.isEmpty()) {\n\t\t\tcompName = tmp;\n\t\t}\n\t\ttmp = Command.getFieldValue(packet, NODE_NAME_LABEL);\n\t\tif ((tmp != null) && !tmp.isEmpty()) {\n\t\t\tnodeName = tmp;\n\t\t}\n\t\ttmp = Command.getFieldValue(packet, KEY_NAME_LABEL);\n\t\tif ((tmp != null) && !tmp.isEmpty()) {\n\t\t\tkeyName = tmp;\n\t\t}\n\n\t\tString value_label = Command.getFieldKeyStartingWith(packet, VALUE_LABEL);\n\t\tchar t = DataTypes.decodeTypeIdFromName(value_label);\n\n\t\ttmp = Command.getFieldValue(packet, value_label);\n\t\tif ((tmp != null) && !tmp.isEmpty()) {\n\t\t\tvalue = DataTypes.decodeValueType(t, tmp);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void initFromElement(Element elem) {\n\t\tthrow new UnsupportedOperationException(\"Not supported yet.\");\n\t}\n\n\t@Override\n\tpublic void initFromPropertyString(String propString) {\n\t\tint idx_eq = propString.indexOf('=');\n\n\t\t// String key = prop[0].trim();\n\t\t// Object val = prop[1];\n\t\tString key = propString.substring(0, idx_eq);\n\t\tObject val = propString.substring(idx_eq + 1);\n\t\tString[] prop = idx_eq == -1 ? new String[]{propString} : new String[]{key, (String) val};\n\n\t\tif (key.matches(\".*\\\\[[FLISBlisb]\\\\]$\")) {\n\t\t\tchar c = key.charAt(key.length() - 2);\n\n\t\t\tkey = key.substring(0, key.length() - 3);\n\t\t\tval = DataTypes.decodeValueType(c, prop[1]);\n\t\t}\n\n\t\tint idx1 = key.indexOf(\"/\");\n\n\t\tif (idx1 > 0) {\n\t\t\tString compNameMeth = key.substring(0, idx1);\n\t\t\tint idx2 = key.lastIndexOf(\"/\");\n\t\t\tString nodeNameMeth = null;\n\t\t\tString keyNameMeth = key.substring(idx2 + 1);\n\n\t\t\tif (idx1 != idx2) {\n\t\t\t\tnodeNameMeth = key.substring(idx1 + 1, idx2);\n\t\t\t}\n\t\t\tset(compNameMeth, nodeNameMeth, keyNameMeth, val);\n\t\t} else {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"You have to provide a key with at least\" + \" 'component_name/key_name': \" + key);\n\t\t}\n\t}\n\n\tpublic boolean isCompNodeKey(String comp, String node, String key) {\n\t\treturn isComponent(comp) && isNode(node) && isKey(key);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (o instanceof ConfigItem) {\n\t\t\treturn getKey().equals(((ConfigItem) o).getKey());\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn getKey().hashCode();\n\t}\n\n\t/**\n\t * Checks if the given component name is equal to this item compName.\n\t *\n\t */\n\tpublic boolean isComponent(String comp) {\n\t\tif (compName != comp) {\n\t\t\treturn (compName != null) ? compName.equals(comp) : false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Checks if the given key is equal to this item keyName.\n\t *\n\t */\n\tpublic boolean isKey(String key) {\n\t\tif (keyName != key) {\n\t\t\treturn (keyName != null) ? keyName.equals(key) : false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Checks if the given node is equal to this item nodeName\n\t *\n\t */\n\tpublic boolean isNode(String node) {\n\t\tif (nodeName != node) {\n\n\t\t\t// At least one is not null\n\t\t\treturn (nodeName != null) ? nodeName.equals(node) : false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Checks if the given node and key are equal to this item nodeName and keyName. This method call works the same way\n\t * as following statement: {@code isNode(node) && isKey(key) }\n\t */\n\tpublic boolean isNodeKey(String node, String key) {\n\t\treturn isNode(node) && isKey(key);\n\t}\n\n\tpublic void set(String clusterNode_m, String compName_m, String nodeName_m, String key_m, String value_str_m,\n\t\t\t\t\tchar val_type_m, String flag_str_m) {\n\t\tObject value_m = DataTypes.decodeValueType(val_type_m, value_str_m);\n\t\tFLAGS flag_m = FLAGS.DEFAULT;\n\n\t\ttry {\n\t\t\tflag_m = FLAGS.valueOf(flag_str_m);\n\t\t} catch (Exception e) {\n\t\t\tlog.warning(\"Incorrect config item flag: \" + flag_str_m + \", setting DEFAULT.\");\n\t\t\tflag_m = FLAGS.DEFAULT;\n\t\t}\n\t\tset(clusterNode_m, compName_m, nodeName_m, key_m, value_m, flag_m);\n\t}\n\n\tpublic void set(String clusterNode_m, String compName_m, String nodeName_m, String key_m, Object value_m,\n\t\t\t\t\tFLAGS flag_m) {\n\t\tif (clusterNode_m != null) {\n\t\t\tthis.clusterNode = clusterNode_m;\n\t\t}\n\t\tif (compName_m != null) {\n\t\t\tthis.compName = compName_m;\n\t\t}\n\t\tif (nodeName_m != null) {\n\t\t\tthis.nodeName = nodeName_m;\n\t\t}\n\t\tif (key_m != null) {\n\t\t\tthis.keyName = key_m;\n\t\t}\n\t\tif (value_m != null) {\n\t\t\tthis.value = value_m;\n\t\t}\n\t\tif (flag_m != null) {\n\t\t\tthis.flag = flag_m;\n\t\t}\n\t\tlastModificationTime = System.currentTimeMillis();\n\t}\n\n\tpublic void set(String compName_m, String nodeName_m, String key_m, String value_str_m, char val_type_m,\n\t\t\t\t\tString flag_str_m) {\n\t\tset(null, compName_m, nodeName_m, key_m, value_str_m, val_type_m, flag_str_m);\n\t}\n\n\tpublic void set(String compName, String nodeName, String key, Object value) {\n\t\tset(null, compName, nodeName, key, value, null);\n\t}\n\n\tpublic void set(String clusterNode, String compName, String nodeName, String key, Object value) {\n\t\tset(clusterNode, compName, nodeName, key, value, null);\n\t}\n\n\tpublic void setNodeKey(String clusterNode, String compName, String nodeKey, Object value) {\n\t\tint key_idx = nodeKey.lastIndexOf(\"/\");\n\t\tString method_key = nodeKey;\n\t\tString method_node = null;\n\n\t\tif (key_idx >= 0) {\n\t\t\tmethod_key = nodeKey.substring(key_idx + 1);\n\t\t\tmethod_node = nodeKey.substring(0, key_idx);\n\t\t}\n\t\tset(clusterNode, compName, method_node, method_key, value);\n\t}\n\n\t// public static final String REPO_ITEM_ELEM_NAME = \"prop\";\n\t// public static final String CLUSTER_NODE_ATTR = \"cluster-node\";\n\t// public static final String COMPONENT_NAME_ATTR = \"comp-name\";\n\t// public static final String NODE_NAME_ATTR = \"node-name\";\n\t// public static final String KEY_NAME_ATTR = \"key-name\";\n\t// public static final String VALUE_ATTR = \"value\";\n\t// public static final String FLAG_ATTR = \"flag\";\n\t// public static final String VALUE_TYPE_ATTR = \"value-type\";\n\t//\n\t// private String clusterNode = null;\n\t// private String compName = null;\n\t// private String nodeName = null;\n\t// private String keyName = null;\n\t// private Object value = null;\n\t// private FLAGS flag = FLAGS.DEFAULT;\n\n\t@Override\n\tpublic Element toElement() {\n\t\tElement elem = super.toElement();\n\n\t\tif (clusterNode != null) {\n\t\t\telem.addAttribute(CLUSTER_NODE_ATTR, clusterNode);\n\t\t}\n\t\telem.addAttribute(COMPONENT_NAME_ATTR, compName);\n\t\tif (nodeName != null) {\n\t\t\telem.addAttribute(NODE_NAME_ATTR, nodeName);\n\t\t}\n\t\telem.addAttribute(KEY_NAME_ATTR, keyName);\n\t\telem.addAttribute(VALUE_ATTR, DataTypes.valueToString(value));\n\t\telem.addAttribute(VALUE_TYPE_ATTR, \"\" + DataTypes.getTypeId(value));\n\t\telem.addAttribute(FLAG_ATTR, flag.name());\n\n\t\treturn elem;\n\t}\n\n\t@Override\n\tpublic String toPropertyString() {\n\t\tchar t = DataTypes.getTypeId(value);\n\t\tString result = getKey() + \"[\" + t + \"]=\";\n\t\tString varr = DataTypes.valueToString(value);\n\n\t\tresult += varr;\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getKey() + \"=\" + value;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/conf/ConfigReader.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\nimport tigase.kernel.beans.config.AbstractBeanConfigurator;\n\nimport java.io.*;\nimport java.util.*;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\n/**\n * Created by andrzej on 05.06.2016.\n */\npublic class ConfigReader {\n\n//\tprivate State state = State.NORMAL;\n//\tprivate ArrayDeque<State> stack = new ArrayDeque<>();\n\n\tpublic static enum State {\n\t\tMAP,\n\t\tQUOTE,\n\t\tLIST,\n\t\tCOMMENT,\n\t\tBEAN,\n\t\tENVIRONMENT,\n\t\tPROPERTY\n\t}\n\tprivate static Pattern DOUBLE_PATTERN = Pattern.compile(\"(\\\\-?[0-9]+\\\\.[0-9]+)([dDfF]*)\");\n\tprivate static Pattern INTEGER_PATTERN = Pattern.compile(\"(\\\\-?[0-9]+)([lL]*)\");\n\tprivate static double x = 2.1f;\n\tprivate StateHolder holder = new StateHolder();\n\n\tpublic static Map<String, Object> flatTree(Map<String, Object> props) {\n\t\tMap<String, Object> result = new HashMap<>();\n\t\tflatTree(result, null, props);\n\t\treturn result;\n\t}\n\n//\tprivate void injectBeans(Map<String, Object> props) {\n//\t\tList<String> beans\n//\t\tprops.entrySet().forEach();\n//\t}\n\n\tprivate static void flatTree(Map<String, Object> result, String prefix, Map<String, Object> props) {\n\t\tprops.forEach((k, v) -> {\n\t\t\tString key = prefix == null ? k : (prefix + \"/\" + k);\n\t\t\tif (v instanceof AbstractBeanConfigurator.BeanDefinition) {\n\t\t\t\tAbstractBeanConfigurator.BeanDefinition beanDefinition = (AbstractBeanConfigurator.BeanDefinition) v;\n\t\t\t\tif (beanDefinition.getClazzName() != null) {\n\t\t\t\t\tresult.put(key + \"/class\", beanDefinition.getClazzName());\n\t\t\t\t}\n\t\t\t\tif (beanDefinition.isActive()) {\n\t\t\t\t\tresult.put(key + \"/active\", \"true\");\n\t\t\t\t} else {\n\t\t\t\t\tresult.put(key + \"/active\", \"false\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (v instanceof Map) {\n\t\t\t\tflatTree(result, key, (Map<String, Object>) v);\n\t\t\t} else {\n\t\t\t\tresult.put(key, v);\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic ConfigReader() {\n\n\t}\n\n\tpublic Map<String, Object> read(Reader reader) throws IOException, ConfigException {\n\t\tBufferedReader buffReader = new BufferedReader(reader);\n\t\tMap<String, Object> props = process(buffReader);\n\t\treturn props;\n\t}\n\n\tpublic Map<String, Object> read(File f) throws IOException, ConfigException {\n\t\tMap<String, Object> props;\n\n\t\ttry (FileReader reader = new FileReader(f)) {\n\t\t\tprops = read(reader);\n\t\t}\n\n\t\treturn props;\n\t}\n\n\tprotected Object decodeValue(String string_in) {\n\t\tString string = string_in.trim();\n\t\t// Decoding doubles and floats\n\t\tMatcher matcher = DOUBLE_PATTERN.matcher(string);\n\t\tif (matcher.matches()) {\n\t\t\tString value = matcher.group(1);\n\t\t\tString type = matcher.group(2);\n\t\t\tif (type.isEmpty() || \"D\".equals(type) || \"d\".equals(type)) {\n\t\t\t\treturn Double.parseDouble(value);\n\t\t\t} else {\n\t\t\t\treturn Float.parseFloat(value);\n\t\t\t}\n\t\t}\n\n\t\t// Decoding integers and longs\n\t\tmatcher = INTEGER_PATTERN.matcher(string);\n\t\tif (matcher.matches()) {\n\t\t\tString value = matcher.group(1);\n\t\t\tString type = matcher.group(2);\n\t\t\tif (\"l\".equals(type) || \"L\".equals(type)) {\n\t\t\t\treturn Long.parseLong(value);\n\t\t\t} else {\n\t\t\t\treturn Integer.parseInt(value);\n\t\t\t}\n\t\t}\n\n\t\t// Decoding booleans\n\t\tswitch (string) {\n\t\t\tcase \"true\":\n\t\t\t\treturn true;\n\t\t\tcase \"false\":\n\t\t\t\treturn false;\n\t\t\tcase \"null\":\n\t\t\t\treturn null;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\n\t\treturn string_in;\n\t}\n\n\tprotected void setBeanDefinitionValue(Object val) {\n\t\tif (holder.key == null || holder.key.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\n\t\tswitch (holder.key) {\n\t\t\tcase \"class\":\n\t\t\t\tholder.bean.setClazzName((String) val);\n\t\t\t\tbreak;\n\t\t\tcase \"active\":\n\t\t\t\tholder.bean.setActive((Boolean) val);\n\t\t\t\tbreak;\n\t\t\tcase \"exportable\":\n\t\t\t\tholder.bean.setExportable((Boolean) val);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthrow new RuntimeException(\n\t\t\t\t\t\t\"Error in configuration file - unknown bean definition field: \" + holder.key);\n\t\t}\n\n\t}\n\n\tprivate Map<String, Object> process(Reader reader) throws IOException, ConfigException {\n\t\tholder.map = new HashMap<>();\n\t\tint line = 1;\n\t\tint pos = 0;\n\t\tint read = 0;\n\t\tStringBuilder lineContent = new StringBuilder();\n\t\ttry {\n\t\t\twhile ((read = reader.read()) != -1) {\n\t\t\t\tchar c = (char) read;\n\t\t\t\tif (c != '\\n') {\n\t\t\t\t\tlineContent.append(c);\n\t\t\t\t} else {\n\t\t\t\t\tlineContent = new StringBuilder();\n\t\t\t\t}\n\t\t\t\tpos++;\n\n\t\t\t\tif (holder.state == State.QUOTE) {\n\t\t\t\t\tif (holder.quoteChar == c) {\n\t\t\t\t\t\tholder.parent.value = holder.sb.toString();\n\t\t\t\t\t\tholder = holder.parent;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tholder.sb.append(c);\n\t\t\t\t\t}\n\t\t\t\t\tif (c == '\\n') {\n\t\t\t\t\t\tline++;\n\t\t\t\t\t\tpos = 0;\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (holder.state == State.COMMENT) {\n\t\t\t\t\tif (c != '\\n') {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tholder = holder.parent;\n\t\t\t\t}\n\n\t\t\t\tswitch (c) {\n\t\t\t\t\tcase '#': {\n\t\t\t\t\t\tStateHolder tmp = new StateHolder();\n\t\t\t\t\t\ttmp.state = State.COMMENT;\n\t\t\t\t\t\ttmp.parent = holder;\n\t\t\t\t\t\tholder = tmp;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tcase ':':\n\t\t\t\t\tcase '=':\n\t\t\t\t\t\tif (holder.key != null) {\n\t\t\t\t\t\t\tholder.sb.append(c);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tholder.key = holder.value != null ? holder.value.toString() : holder.sb.toString().trim();\n\t\t\t\t\t\tholder.value = null;\n\t\t\t\t\t\tholder.sb = new StringBuilder();\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase '[': {\n\t\t\t\t\t\tStateHolder tmp = new StateHolder();\n\t\t\t\t\t\ttmp.state = State.LIST;\n\t\t\t\t\t\ttmp.parent = holder;\n\t\t\t\t\t\ttmp.list = new ArrayList();\n\t\t\t\t\t\tholder = tmp;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase ']': {\n\t\t\t\t\t\tif (holder.variable != null &&\n\t\t\t\t\t\t\t\t(holder.state != State.PROPERTY && holder.state != State.ENVIRONMENT)) {\n\t\t\t\t\t\t\tif (holder.variable instanceof CompositeVariable) {\n\t\t\t\t\t\t\t\tObject value = holder.value;\n\t\t\t\t\t\t\t\tif (value == null) {\n\t\t\t\t\t\t\t\t\tvalue = decodeValue(holder.sb.toString());\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t((CompositeVariable) holder.variable).add(value);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tholder.value = holder.variable;\n\t\t\t\t\t\t\tholder.variable = null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tList val = holder.list;\n\t\t\t\t\t\tif (holder.value == null) {\n\t\t\t\t\t\t\tString valueStr = holder.sb.toString().trim();\n\t\t\t\t\t\t\tif (!valueStr.isEmpty()) {\n\t\t\t\t\t\t\t\tObject value = decodeValue(valueStr);\n\t\t\t\t\t\t\t\tholder.list.add(value);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tholder.list.add(holder.value);\n\t\t\t\t\t\t}\n//\t\t\t\t\tif (holder.value != null) {\n//\t\t\t\t\t\tval.add(holder.value instanceof String ? decodeValue((String) holder.value) : holder.value);\n//\t\t\t\t\t}\n\t\t\t\t\t\tholder = holder.parent;\n\t\t\t\t\t\tholder.value = val;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase '\\'':\n\t\t\t\t\tcase '\\\"': {\n\t\t\t\t\t\tStateHolder tmp = new StateHolder();\n\t\t\t\t\t\ttmp.state = State.QUOTE;\n\t\t\t\t\t\ttmp.parent = holder;\n\t\t\t\t\t\tholder = tmp;\n\t\t\t\t\t\tholder.quoteChar = c;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase '{': {\n\t\t\t\t\t\tif (holder.key == null) {\n\t\t\t\t\t\t\tholder.key = (holder.value != null && holder.value instanceof String)\n\t\t\t\t\t\t\t\t\t\t ? ((String) holder.value).trim()\n\t\t\t\t\t\t\t\t\t\t : holder.sb.toString().trim();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tholder.sb = new StringBuilder();\n\t\t\t\t\t\tStateHolder tmp = new StateHolder();\n\t\t\t\t\t\ttmp.state = State.MAP;\n\t\t\t\t\t\ttmp.parent = holder;\n\t\t\t\t\t\ttmp.map = (holder.value instanceof AbstractBeanConfigurator.BeanDefinition)\n\t\t\t\t\t\t\t\t  ? ((Map) holder.value)\n\t\t\t\t\t\t\t\t  : new HashMap();\n\t\t\t\t\t\tholder = tmp;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase '}': {\n\t\t\t\t\t\tMap val = holder.map;\n\t\t\t\t\t\tif (holder.parent == null) {\n\t\t\t\t\t\t\tthrow new java.lang.UnsupportedOperationException(\"Unexpected char: '}'\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tholder = holder.parent;\n\t\t\t\t\t\t// special use case to convert maps with active or class into bean definition\n\t\t\t\t\t\tholder.value = val;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase '(': {\n\t\t\t\t\t\tString key = (holder.value != null && holder.value instanceof String)\n\t\t\t\t\t\t\t\t\t ? ((String) holder.value).trim()\n\t\t\t\t\t\t\t\t\t : holder.sb.toString().trim();\n\t\t\t\t\t\tStateHolder tmp = new StateHolder();\n\t\t\t\t\t\tswitch (key) {\n\t\t\t\t\t\t\tcase \"env\":\n\t\t\t\t\t\t\t\ttmp.state = State.ENVIRONMENT;\n\t\t\t\t\t\t\t\ttmp.variable = new ConfigReader.EnvironmentVariable();\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase \"prop\":\n\t\t\t\t\t\t\t\ttmp.state = State.PROPERTY;\n\t\t\t\t\t\t\t\ttmp.variable = new ConfigReader.PropertyVariable();\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tholder.key = key;\n\t\t\t\t\t\t\t\ttmp.state = State.BEAN;\n\t\t\t\t\t\t\t\ttmp.bean = new AbstractBeanConfigurator.BeanDefinition();\n\t\t\t\t\t\t\t\ttmp.bean.setBeanName(holder.key);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttmp.parent = holder;\n\t\t\t\t\t\tholder = tmp;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase ')': {\n\t\t\t\t\t\tObject val = null;\n\t\t\t\t\t\tswitch (holder.state) {\n\t\t\t\t\t\t\tcase ENVIRONMENT:\n\t\t\t\t\t\t\tcase PROPERTY:\n\t\t\t\t\t\t\t\tAbstractEnvironmentPropertyVariable prop = (AbstractEnvironmentPropertyVariable) holder.variable;\n\t\t\t\t\t\t\t\tString value = holder.value.toString().trim();\n\t\t\t\t\t\t\t\tif (prop.getName() == null) {\n\t\t\t\t\t\t\t\t\tprop.setName(value);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tprop.setDefValue(value);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tval = holder.variable;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase BEAN:\n\t\t\t\t\t\t\t\tval = holder.bean;\n\t\t\t\t\t\t\t\tObject val1 =\n\t\t\t\t\t\t\t\t\t\tholder.value != null ? holder.value : decodeValue(holder.sb.toString().trim());\n\t\t\t\t\t\t\t\tsetBeanDefinitionValue(val1);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tholder = holder.parent;\n\t\t\t\t\t\tholder.value = val;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase '\\n':\n\t\t\t\t\t\tline++;\n\t\t\t\t\t\tpos = 0;\n\t\t\t\t\t\t// there should be no break here!\n\t\t\t\t\tcase ',':\n\t\t\t\t\t\tif (holder.variable != null &&\n\t\t\t\t\t\t\t\t(holder.state != State.PROPERTY && holder.state != State.ENVIRONMENT)) {\n\t\t\t\t\t\t\tif (holder.variable instanceof CompositeVariable) {\n\t\t\t\t\t\t\t\tObject value = holder.value;\n\t\t\t\t\t\t\t\tif (value == null) {\n\t\t\t\t\t\t\t\t\tvalue = decodeValue(holder.sb.toString());\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t((CompositeVariable) holder.variable).add(value);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tholder.value = holder.variable;\n\t\t\t\t\t\t\tholder.variable = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tswitch (holder.state) {\n\t\t\t\t\t\t\tcase MAP:\n\t\t\t\t\t\t\t\tif (holder.key == null || holder.key.isEmpty()) {\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tholder.map.put(holder.key, holder.value != null\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   ? holder.value\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   : decodeValue(holder.sb.toString().trim()));\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase LIST:\n\t\t\t\t\t\t\t\tif (holder.value == null) {\n\t\t\t\t\t\t\t\t\tString valueStr = holder.sb.toString().trim();\n\t\t\t\t\t\t\t\t\tif (valueStr.isEmpty()) {\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tObject value = decodeValue(valueStr);\n\t\t\t\t\t\t\t\t\tholder.list.add(value);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tholder.list.add(holder.value);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase BEAN:\n\t\t\t\t\t\t\t\tObject val =\n\t\t\t\t\t\t\t\t\t\tholder.value != null ? holder.value : decodeValue(holder.sb.toString().trim());\n\t\t\t\t\t\t\t\tsetBeanDefinitionValue(val);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase ENVIRONMENT:\n\t\t\t\t\t\t\tcase PROPERTY:\n\t\t\t\t\t\t\t\t((AbstractEnvironmentPropertyVariable) holder.variable).setName(\n\t\t\t\t\t\t\t\t\t\tholder.value.toString().trim());\n\t\t\t\t\t\t\t\tholder.value = holder.variable;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tholder.key = null;\n\t\t\t\t\t\tholder.sb = new StringBuilder();\n\t\t\t\t\t\tholder.value = null;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '+':\n\t\t\t\t\tcase '-':\n\t\t\t\t\tcase '/':\n\t\t\t\t\tcase '*':\n\t\t\t\t\t\tif (holder.state == State.LIST) {\n\t\t\t\t\t\t\tObject value = holder.value;\n\t\t\t\t\t\t\tif (value == null) {\n\t\t\t\t\t\t\t\tif (c == '-' && holder.sb.toString().trim().isEmpty()) {\n\t\t\t\t\t\t\t\t\tholder.sb.append(c);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tvalue = decodeValue(holder.sb.toString());\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tholder.value = null;\n\t\t\t\t\t\t\tholder.sb = new StringBuilder();\n\t\t\t\t\t\t\tif (holder.variable == null) {\n\t\t\t\t\t\t\t\tCompositeVariable var = new CompositeVariable();\n\t\t\t\t\t\t\t\tholder.variable = var;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t((CompositeVariable) holder.variable).add(c, value);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tObject value = holder.value;\n\t\t\t\t\t\t\tif (value == null) {\n\t\t\t\t\t\t\t\tif (c == '-' && holder.sb.toString().trim().isEmpty()) {\n\t\t\t\t\t\t\t\t\tholder.sb.append(c);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tvalue = decodeValue(holder.sb.toString());\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (holder.key != null && value != null) {\n\t\t\t\t\t\t\t\tholder.value = null;\n\t\t\t\t\t\t\t\tholder.sb = new StringBuilder();\n\t\t\t\t\t\t\t\tif (holder.variable == null) {\n\t\t\t\t\t\t\t\t\tCompositeVariable var = new CompositeVariable();\n\t\t\t\t\t\t\t\t\tholder.variable = var;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t((CompositeVariable) holder.variable).add(c, value);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tholder.sb.append(c);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (java.lang.UnsupportedOperationException ex) {\n\t\t\twhile ((read = reader.read()) != -1) {\n\t\t\t\tchar c = (char) read;\n\t\t\t\tif (c != '\\n') {\n\t\t\t\t\tlineContent.append(c);\n\t\t\t\t} else {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new UnsupportedOperationException(ex.getMessage(), line, pos, lineContent.toString(), ex);\n\t\t}\n\t\tif (holder.state != State.MAP || holder.parent != null) {\n\t\t\tthrow new InvalidFormatException(\n\t\t\t\t\t\"Parsing error - invalid file structure, state = \" + holder.state + \", parent = \" + holder.parent);\n\t\t}\n\n\t\tif (holder.key != null && !holder.key.isEmpty()) {\n\t\t\tholder.map.put(holder.key, holder.value != null ? holder.value : decodeValue(holder.sb.toString().trim()));\n\t\t}\n\n\t\treturn holder.map;\n\t}\n\n\tpublic interface Variable {\n\n\t\tObject calculateValue();\n\n\t}\n\n\tpublic static abstract class AbstractEnvironmentPropertyVariable\n\t\t\timplements Variable {\n\n\t\tprivate String defValue;\n\t\tprivate String name;\n\n\t\tprotected AbstractEnvironmentPropertyVariable() {\n\t\t}\n\n\t\tprotected AbstractEnvironmentPropertyVariable(String name, String defValue) {\n\t\t\tthis.setName(name);\n\t\t\tthis.setDefValue(defValue);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\tif (obj instanceof AbstractEnvironmentPropertyVariable) {\n\t\t\t\tAbstractEnvironmentPropertyVariable v = (AbstractEnvironmentPropertyVariable) obj;\n\n\t\t\t\tif (v.name == this.name || (v.name != null && v.name.equals(this.name))) {\n\t\t\t\t\tif (v.defValue == this.defValue || (v.defValue != null && v.defValue.equals(this.defValue))) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tprotected String getName() {\n\t\t\treturn this.name;\n\t\t}\n\n\t\tprotected void setName(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\tprotected String getDefValue() {\n\t\t\treturn defValue;\n\t\t}\n\n\t\tprotected void setDefValue(String defValue) {\n\t\t\tthis.defValue = defValue;\n\t\t}\n\t}\n\n\tpublic static class CompositeVariable\n\t\t\timplements Variable {\n\n\t\tpublic enum Operation {\n\t\t\tmultiply,\n\t\t\tdivide,\n\t\t\tadd,\n\t\t\tsubstract;\n\n\t\t\tpublic Number execute(Number arg1, Number arg2) {\n\t\t\t\tif (arg1 instanceof Double || arg2 instanceof Double) {\n\t\t\t\t\tdouble a1 = arg1.doubleValue();\n\t\t\t\t\tdouble a2 = arg2.doubleValue();\n\t\t\t\t\tswitch (this) {\n\t\t\t\t\t\tcase multiply:\n\t\t\t\t\t\t\treturn a1 * a2;\n\t\t\t\t\t\tcase divide:\n\t\t\t\t\t\t\treturn a1 / a2;\n\t\t\t\t\t\tcase add:\n\t\t\t\t\t\t\treturn a1 + a2;\n\t\t\t\t\t\tcase substract:\n\t\t\t\t\t\t\treturn a1 - a2;\n\t\t\t\t\t}\n\t\t\t\t} else if (arg1 instanceof Float || arg2 instanceof Float) {\n\t\t\t\t\tfloat a1 = arg1.floatValue();\n\t\t\t\t\tfloat a2 = arg2.floatValue();\n\t\t\t\t\tswitch (this) {\n\t\t\t\t\t\tcase multiply:\n\t\t\t\t\t\t\treturn a1 * a2;\n\t\t\t\t\t\tcase divide:\n\t\t\t\t\t\t\treturn a1 / a2;\n\t\t\t\t\t\tcase add:\n\t\t\t\t\t\t\treturn a1 + a2;\n\t\t\t\t\t\tcase substract:\n\t\t\t\t\t\t\treturn a1 - a2;\n\t\t\t\t\t}\n\t\t\t\t} else if (arg1 instanceof Long || arg2 instanceof Long) {\n\t\t\t\t\tlong a1 = arg1.longValue();\n\t\t\t\t\tlong a2 = arg2.longValue();\n\t\t\t\t\tswitch (this) {\n\t\t\t\t\t\tcase multiply:\n\t\t\t\t\t\t\treturn a1 * a2;\n\t\t\t\t\t\tcase divide:\n\t\t\t\t\t\t\treturn a1 / a2;\n\t\t\t\t\t\tcase add:\n\t\t\t\t\t\t\treturn a1 + a2;\n\t\t\t\t\t\tcase substract:\n\t\t\t\t\t\t\treturn a1 - a2;\n\t\t\t\t\t}\n\t\t\t\t} else if (arg1 instanceof Integer || arg2 instanceof Integer) {\n\t\t\t\t\tint a1 = arg1.intValue();\n\t\t\t\t\tint a2 = arg2.intValue();\n\t\t\t\t\tswitch (this) {\n\t\t\t\t\t\tcase multiply:\n\t\t\t\t\t\t\treturn a1 * a2;\n\t\t\t\t\t\tcase divide:\n\t\t\t\t\t\t\treturn a1 / a2;\n\t\t\t\t\t\tcase add:\n\t\t\t\t\t\t\treturn a1 + a2;\n\t\t\t\t\t\tcase substract:\n\t\t\t\t\t\t\treturn a1 - a2;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthrow new RuntimeException(\"Invalid argument exception\");\n\t\t\t}\n\t\t}\n\t\tprivate final List<Operation> operations = new ArrayList<>();\n\t\tprivate final List<Object> values = new ArrayList<>();\n\n\t\t@Override\n\t\tpublic Object calculateValue() {\n\t\t\tif (values.isEmpty()) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tList<Object> values = this.values.stream()\n\t\t\t\t\t.map(val -> (val instanceof Variable) ? ((Variable) val).calculateValue() : val)\n\t\t\t\t\t.collect(Collectors.toList());\n\n\t\t\tint j=0;\n\t\t\tObject first = values.get(j);\n\t\t\tj++;\n\t\t\twhile (first == null && j<values.size()) {\n\t\t\t\tfirst = values.get(j);\n\t\t\t\tj++;\n\t\t\t}\n\t\t\tif (first instanceof EnvironmentVariable || first instanceof PropertyVariable) {\n\t\t\t\tfirst = \"String\";\n\t\t\t} else if (first instanceof CompositeVariable) {\n\t\t\t\tfirst = calculateValue();\n\t\t\t}\n\t\t\tif (first instanceof String) {\n\t\t\t\tif (!operations.stream().allMatch(o -> o == Operation.add)) {\n\t\t\t\t\tthrow new java.lang.UnsupportedOperationException(\"Invalid operation for String!\");\n\t\t\t\t}\n\t\t\t\treturn ((List<String>) (List) values).stream().collect(Collectors.joining());\n\t\t\t} else if (first instanceof Number) {\n\t\t\t\tList<Operation> operations = new ArrayList<>(this.operations);\n\t\t\t\tfor (Operation operation : Operation.values()) {\n\t\t\t\t\tIterator<Operation> it = operations.iterator();\n\t\t\t\t\tint pos = 0;\n\t\t\t\t\twhile (it.hasNext()) {\n\t\t\t\t\t\tOperation o = it.next();\n\t\t\t\t\t\tif (!operation.equals(o)) {\n\t\t\t\t\t\t\tpos++;\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tNumber arg1 = (Number) values.remove(pos);\n\t\t\t\t\t\tNumber arg2 = (Number) values.remove(pos);\n\n\t\t\t\t\t\tNumber result = operation.execute(arg1, arg2);\n\t\t\t\t\t\tvalues.add(pos, result);\n\t\t\t\t\t\tit.remove();\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (values.size() > 1) {\n\t\t\t\t\tthrow new RuntimeException(\"Variable calculation exception\");\n\t\t\t\t}\n\n\t\t\t\treturn values.stream().findFirst().get();\n\t\t\t} else {\n\t\t\t\tthrow new java.lang.UnsupportedOperationException(\"Cannot calculate composite variable!\");\n\t\t\t}\n\t\t}\n\n\t\tpublic void add(Object value) {\n\t\t\tvalues.add(value);\n\t\t}\n\n\t\tpublic void add(char operation, Object value) {\n\t\t\tOperation o = null;\n\t\t\tswitch (operation) {\n\t\t\t\tcase '+':\n\t\t\t\t\to = Operation.add;\n\t\t\t\t\tbreak;\n\t\t\t\tcase '-':\n\t\t\t\t\to = Operation.substract;\n\t\t\t\t\tbreak;\n\t\t\t\tcase '*':\n\t\t\t\t\to = Operation.multiply;\n\t\t\t\t\tbreak;\n\t\t\t\tcase '/':\n\t\t\t\t\to = Operation.divide;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new java.lang.UnsupportedOperationException();\n\t\t\t}\n\t\t\tif (value instanceof String && o != Operation.add) {\n\t\t\t\tthrow new java.lang.UnsupportedOperationException(\"Cannot \" + o.name() + \" a String\");\n\t\t\t}\n\t\t\toperations.add(o);\n\t\t\tvalues.add(value);\n\t\t}\n\n\t\tpublic List<Object> getArguments() {\n\t\t\treturn values;\n\t\t}\n\n\t\tpublic List<Operation> getOperations() {\n\t\t\treturn operations;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\tif (obj instanceof CompositeVariable) {\n\t\t\t\tCompositeVariable v = (CompositeVariable) obj;\n\t\t\t\tif (v.values.size() != values.size()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tif (v.operations.size() != operations.size()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tfor (int i = 0; i < values.size(); i++) {\n\t\t\t\t\tif (!values.get(i).equals(v.values.get(i))) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfor (int i = 0; i < operations.size(); i++) {\n\t\t\t\t\tif (!operations.get(i).equals(v.operations.get(i))) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic static class ConfigException\n\t\t\textends Exception {\n\n\t\tpublic ConfigException(String msg) {\n\t\t\tsuper(msg);\n\t\t}\n\n\t\tpublic ConfigException(String msg, Throwable cause) {\n\t\t\tsuper(msg, cause);\n\t\t}\n\t}\n\n\tpublic static class EnvironmentVariable\n\t\t\textends AbstractEnvironmentPropertyVariable {\n\n\t\tpublic EnvironmentVariable() {\n\t\t}\n\n\t\tpublic EnvironmentVariable(String name, String defValue) {\n\t\t\tsuper(name, defValue);\n\t\t}\n\n\t\t@Override\n\t\tpublic Object calculateValue() {\n\t\t\tObject val = System.getenv(getName());\n\t\t\tif (val == null) {\n\t\t\t\tval = getDefValue();\n\t\t\t}\n\t\t\treturn val;\n\t\t}\n\n\t}\n\n\tpublic static class InvalidFormatException\n\t\t\textends ConfigException {\n\n\t\tpublic InvalidFormatException(String msg) {\n\t\t\tsuper(msg);\n\t\t}\n\n\t}\n\n\tpublic static class PropertyVariable\n\t\t\textends AbstractEnvironmentPropertyVariable {\n\n\t\tpublic PropertyVariable() {\n\t\t}\n\n\t\tpublic PropertyVariable(String name, String defValue) {\n\t\t\tsuper(name, defValue);\n\t\t}\n\n\t\t@Override\n\t\tpublic Object calculateValue() {\n\t\t\treturn System.getProperty(getName(), getDefValue());\n\t\t}\n\n\t}\n\n\tpublic static class UnsupportedOperationException\n\t\t\textends ConfigException {\n\n\t\tprivate final int line;\n\t\tprivate final String lineContent;\n\t\tprivate final int position;\n\n\t\tpublic UnsupportedOperationException(String msg, int line, int position, String lineContent, Throwable cause) {\n\t\t\tsuper(msg, cause);\n\t\t\tthis.line = line;\n\t\t\tthis.position = position;\n\t\t\tthis.lineContent = lineContent;\n\t\t}\n\n\t\tpublic int getLine() {\n\t\t\treturn line;\n\t\t}\n\n\t\tpublic int getPosition() {\n\t\t\treturn position;\n\t\t}\n\n\t\tpublic String getLineContent() {\n\t\t\treturn lineContent;\n\t\t}\n\t}\n\n\tpublic class StateHolder {\n\n\t\tpublic AbstractBeanConfigurator.BeanDefinition bean;\n\t\tpublic String key;\n\t\tpublic List list;\n\t\tpublic Map<String, Object> map;\n\t\tpublic StateHolder parent = null;\n\t\tpublic char quoteChar = '\\'';\n\t\tpublic StringBuilder sb = new StringBuilder();\n\t\tpublic State state = State.MAP;\n\t\tpublic Object value;\n\t\tpublic Variable variable;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/conf/ConfigRepositoryIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.comp.ComponentRepository;\n\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * Created: Dec 10, 2009 2:04:20 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface ConfigRepositoryIfc\n\t\textends ComponentRepository<ConfigItem> {\n\n\tpublic static final String RELOAD_DELAY = \"--reload-delay\";\n\n\tpublic static final String RESOURCE_URI = \"--resource-uri\";\n\n\t/**\n\t * Returns all known settings for the given component name.\n\t * @return map with configuration\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tMap<String, Object> getProperties(String compName) throws ConfigurationException;\n\n\t/**\n\t * Get set of config items stored for component\n\t * @return set of component items\n\t */\n\tSet<ConfigItem> getItemsForComponent(String compName);\n\n\t/**\n\t * Sets/adds properties for the given component name.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tvoid putProperties(String compName, Map<String, Object> props) throws ConfigurationException;\n\n\t/**\n\t * Returns a configuration setting for a given component, node and key. If the configuration parameters is not\n\t * found, returns given default value.\n\t * @return value\n\t */\n\tObject get(String compName, String node, String key, Object def);\n\n\t/**\n\t * Puts/sets/adds/updates a configuration setting to the configuration repository.\n\t */\n\tvoid set(String compName, String node, String key, Object value);\n\n\t/**\n\t * Returns all component names for which there are some configuration settings available.\n\t * @return array of component names\n\t */\n\tString[] getCompNames();\n\n\t/**\n\t * Returns an array of all configuration keys for a given component and configuration node.\n\t * @return array of keys for component and node\n\t */\n\tString[] getKeys(String compName, String node);\n\n\t/**\n\t * Removes a configuration setting from the configuration repository.\n\t */\n\tvoid remove(String compName, String node, String key);\n\n\t/**\n\t * Method adds an Item to the configuration repository where the key is the item key constructed of component name,\n\t * node name and property key name.\n\t */\n\tvoid addItem(String key, Object value) throws ConfigurationException;\n\n\t/**\n\t * This is used to load a configuration for a selected cluster node. The configuration repository (file or database)\n\t * may contain settings for all cluster nodes, some of the settings may be exclusive to one or another cluster node.\n\t * This method informs the repository what node name (hostname) it is running on.\n\t */\n\tvoid setDefHostname(String hostname);\n\n\tMap<String, Object> getInitProperties();\n}\n"
  },
  {
    "path": "src/main/java/tigase/conf/ConfigSQLRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.DBInitException;\nimport tigase.db.DataRepository;\nimport tigase.db.RepositoryFactory;\nimport tigase.db.TigaseDBException;\nimport tigase.util.repository.DataTypes;\n\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Dec 15, 2009 10:44:00 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Deprecated\n@TigaseDeprecated(since = \"8.0.0\")\npublic class ConfigSQLRepository\n\t\textends ConfigurationCache {\n\n\tpublic static final String CONFIG_REPO_URI_PROP_KEY = \"tigase-config-repo-uri\";\n\n\tpublic static final String CONFIG_REPO_URI_INIT_KEY = \"--tigase-config-repo-uri\";\n\n\tprivate static final Logger log = Logger.getLogger(ConfigSQLRepository.class.getName());\n\n\tprivate JDBCAccess dbAccess = new JDBCAccess();\n\n\t@Override\n\tpublic void addItem(String compName, ConfigItem item) {\n\t\tdbAccess.addItem(item);\n\t}\n\n\t@Override\n\tpublic Collection<ConfigItem> allItems() throws TigaseDBException {\n\t\treturn dbAccess.getAllItems();\n\t}\n\n\t@Override\n\tpublic String[] getCompNames() {\n\t\treturn dbAccess.getComponentNames();\n\t}\n\n\t@Override\n\tpublic ConfigItem getItem(String compName, String node, String key) {\n\t\treturn dbAccess.getItem(compName, node, key);\n\t}\n\n\t@Override\n\tpublic Set<ConfigItem> getItemsForComponent(String compName) {\n\t\treturn dbAccess.getCompItems(compName);\n\t}\n\n\t@Override\n\tpublic String[] getKeys(String compName, String node) {\n\t\treturn dbAccess.getKeys(compName, node);\n\t}\n\n\t@Override\n\tpublic void initRepository(String repo_uri, Map<String, String> params) throws DBInitException {\n\t\tString config_db_uri = System.getProperty(CONFIG_REPO_URI_PROP_KEY);\n\n\t\tif (config_db_uri == null) {\n\t\t\tconfig_db_uri = (String) params.get(CONFIG_REPO_URI_INIT_KEY);\n\t\t}\n\n\t\tif (config_db_uri == null) {\n\t\t\tconfig_db_uri = (String) params.get(RepositoryFactory.GEN_USER_DB_URI);\n\t\t}\n\n\t\tif (config_db_uri == null) {\n\t\t\tlog.severe(\"Missing configuration database connection string.\");\n\t\t\tlog.severe(\"Tigase needs a database connection string to load configuration.\");\n\t\t\tlog.severe(\"You can provide it in a few ways and the Tigase server checks\");\n\t\t\tlog.severe(\"following parameters in the order below:\");\n\t\t\tlog.severe(\"1. System property: -Dtigase-config-repo-uri=db-connection-string\");\n\t\t\tlog.severe(\"2. init.properties file or command line parameter: \" +\n\t\t\t\t\t\t\t   \"--tigase-config-repo-uri=db-connection-string\");\n\t\t\tlog.severe(\"3. init.properties file or command line parameter: \" + \"--user-db-uri=db-connection-string\");\n\t\t\tlog.severe(\"Please correct the error and restart the server.\");\n\t\t\tSystem.exit(1);\n\t\t}\n\n\t\ttry {\n\t\t\tdbAccess.initRepository(config_db_uri, null);\n\t\t} catch (SQLException ex) {\n\t\t\tlog.log(Level.SEVERE, \"Problem connecting to configuration database: \", ex);\n\t\t\tlog.severe(\"Please check whether the database connection string is correct: \" + config_db_uri);\n\t\t\tSystem.exit(1);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void removeItem(String compName, ConfigItem item) {\n\t\tdbAccess.removeItem(item);\n\t}\n\n\t@Override\n\tpublic int size() {\n\t\treturn dbAccess.getPropertiesCount();\n\t}\n\n\tprivate class JDBCAccess {\n\n\t\t\tpublic static final String TABLE_NAME = \"tigase_configuration\";\n\t\tprivate static final String CLUSTER_NODE_COLUMN = \"cluster_node\";\n\t\tprivate static final String COMPONENT_NAME_COLUMN = \"component_name\";\n\t\tprivate static final String NODE_NAME_COLUMN = \"key_node\";\n\t\tprivate static final String KEY_NAME_COLUMN = \"key_name\";\n\t\tprivate static final String VALUE_COLUMN = \"value\";\n\t\tprivate static final String FLAG_COLUMN = \"flag\";\n\t\tprivate static final String VALUE_TYPE_COLUMN = \"value_type\";\n\t\tprivate static final String LAST_UPDATE_COLUMN = \"last_update\";\n\t\tprivate static final String CREATE_TABLE_QUERY =\n\t\t\t\t\"create table \" + TABLE_NAME + \" (\" + \"  \" + COMPONENT_NAME_COLUMN + \" varchar(127) NOT NULL,\" + \"  \" +\n\t\t\t\t\t\tKEY_NAME_COLUMN + \" varchar(127) NOT NULL,\" + \"  \" + VALUE_COLUMN + \" varchar(8191) NOT NULL,\" +\n\t\t\t\t\t\t\"  \" + CLUSTER_NODE_COLUMN + \" varchar(255) NOT NULL DEFAULT '',\" + \"  \" + NODE_NAME_COLUMN +\n\t\t\t\t\t\t\" varchar(127) NOT NULL DEFAULT '',\" + \"  \" + FLAG_COLUMN +\n\t\t\t\t\t\t\" varchar(32) NOT NULL DEFAULT 'DEFAULT',\" + \"  \" + VALUE_TYPE_COLUMN +\n\t\t\t\t\t\t\" varchar(8) NOT NULL DEFAULT 'S',\" + \"  \" + LAST_UPDATE_COLUMN + \" timestamp,\" +\n\t\t\t\t\t\t\"  primary key(\" + CLUSTER_NODE_COLUMN + \", \" + COMPONENT_NAME_COLUMN + \", \" +\n\t\t\t\t\t\tNODE_NAME_COLUMN + \", \" + KEY_NAME_COLUMN + \", \" + FLAG_COLUMN + \"))\";\n\t\tprivate static final String CLUSTER_NODE_WHERE_PART =\n\t\t\t\t\" (\" + CLUSTER_NODE_COLUMN + \" = '' \" + \" OR \" + CLUSTER_NODE_COLUMN + \" = ?) \";\n\t\tprivate static final String ITEM_WHERE_PART =\n\t\t\t\t\" where \" + CLUSTER_NODE_WHERE_PART + \" AND (\" + COMPONENT_NAME_COLUMN + \" = ?) \" + \" AND (\" +\n\t\t\t\t\t\tNODE_NAME_COLUMN + \" = ?) \" + \" AND (\" + KEY_NAME_COLUMN + \" = ?) \";\n\n\t\tprivate static final String GET_ITEM_QUERY = \"select * from \" + TABLE_NAME + ITEM_WHERE_PART;\n\t\tprivate static final String ADD_ITEM_QUERY =\n\t\t\t\t\"insert into \" + TABLE_NAME + \" (\" + CLUSTER_NODE_COLUMN + \", \" + COMPONENT_NAME_COLUMN + \", \" +\n\t\t\t\t\t\tNODE_NAME_COLUMN + \", \" + KEY_NAME_COLUMN + \", \" + VALUE_COLUMN + \", \" + VALUE_TYPE_COLUMN +\n\t\t\t\t\t\t\", \" + FLAG_COLUMN + \") \" + \" values (?, ?, ?, ?, ?, ?, ?)\";\n\t\tprivate static final String UPDATE_ITEM_QUERY =\n\t\t\t\t\"update \" + TABLE_NAME + \" set \" + VALUE_COLUMN + \" = ? \" + \" where (\" + CLUSTER_NODE_COLUMN +\n\t\t\t\t\t\t\" = ?) \" + \" AND (\" + COMPONENT_NAME_COLUMN + \" = ?) \" + \" AND (\" + NODE_NAME_COLUMN +\n\t\t\t\t\t\t\" = ?) \" + \" AND (\" + KEY_NAME_COLUMN + \" = ?)\";\n\t\tprivate static final String DELETE_ITEM_QUERY = \"delete from \" + TABLE_NAME + ITEM_WHERE_PART;\n\t\tprivate static final String GET_ALL_ITEMS_QUERY =\n\t\t\t\t\"select * from \" + TABLE_NAME + \" where \" + CLUSTER_NODE_WHERE_PART;\n\t\tprivate static final String GET_COMPONENT_ITEMS_QUERY =\n\t\t\t\t\"select * from \" + TABLE_NAME + \" where \" + CLUSTER_NODE_WHERE_PART + \" AND (\" + COMPONENT_NAME_COLUMN +\n\t\t\t\t\t\t\" = ?)\";\n\t\tprivate static final String GET_UPDATED_ITEMS_QUERY =\n\t\t\t\t\"select * from \" + TABLE_NAME + \" where \" + CLUSTER_NODE_WHERE_PART + \" AND (\" + FLAG_COLUMN +\n\t\t\t\t\t\t\" <> 'INITIAL')\" + \" AND (\" + LAST_UPDATE_COLUMN + \" > ?)\";\n\t\tprivate static final String GET_COMPONENT_NAMES_QUERY =\n\t\t\t\t\"select distinct(\" + COMPONENT_NAME_COLUMN + \") from \" + TABLE_NAME + \" where \" + CLUSTER_NODE_COLUMN;\n\t\tprivate static final String GET_PROPERTIES_COUNT_QUERY =\n\t\t\t\t\"select count(*) as count from \" + TABLE_NAME + \" where \" + CLUSTER_NODE_COLUMN;\n\t\tprivate static final String GET_KEYS_QUERY =\n\t\t\t\t\"select \" + KEY_NAME_COLUMN + \" from \" + TABLE_NAME + \" where \" + CLUSTER_NODE_WHERE_PART + \" AND (\" +\n\t\t\t\t\t\tCOMPONENT_NAME_COLUMN + \" = ?)\" + \" AND (\" + NODE_NAME_COLUMN + \" = ?)\";\n\n\t\tprivate DataRepository data_repo = null;\n\n\t\tpublic void initRepository(String conn_str, Map<String, String> params) throws SQLException {\n\t\t\ttry {\n\t\t\t\tdata_repo = RepositoryFactory.getDataRepository(null, conn_str, params);\n\n\t\t\t\t// Check if DB is correctly setup and contains all required tables.\n\t\t\t\tcheckDB();\n\t\t\t\tdata_repo.initPreparedStatement(GET_ITEM_QUERY, GET_ITEM_QUERY);\n\t\t\t\tdata_repo.initPreparedStatement(GET_ALL_ITEMS_QUERY, GET_ALL_ITEMS_QUERY);\n\t\t\t\tdata_repo.initPreparedStatement(GET_COMPONENT_ITEMS_QUERY, GET_COMPONENT_ITEMS_QUERY);\n\t\t\t\tdata_repo.initPreparedStatement(ADD_ITEM_QUERY, ADD_ITEM_QUERY);\n\t\t\t\tdata_repo.initPreparedStatement(UPDATE_ITEM_QUERY, UPDATE_ITEM_QUERY);\n\t\t\t\tdata_repo.initPreparedStatement(DELETE_ITEM_QUERY, DELETE_ITEM_QUERY);\n\t\t\t\tdata_repo.initPreparedStatement(GET_UPDATED_ITEMS_QUERY, GET_UPDATED_ITEMS_QUERY);\n\t\t\t\tdata_repo.initPreparedStatement(GET_COMPONENT_NAMES_QUERY, GET_COMPONENT_NAMES_QUERY);\n\t\t\t\tdata_repo.initPreparedStatement(GET_PROPERTIES_COUNT_QUERY, GET_PROPERTIES_COUNT_QUERY);\n\t\t\t\tdata_repo.initPreparedStatement(GET_KEYS_QUERY, GET_KEYS_QUERY);\n\t\t\t} catch (Exception e) {\n\t\t\t}\n\t\t}\n\n\t\tprivate void addItem(ConfigItem item) {\n\t\t\ttry {\n\t\t\t\tPreparedStatement addItemSt = data_repo.getPreparedStatement(null, ADD_ITEM_QUERY);\n\n\t\t\t\tsynchronized (addItemSt) {\n\t\t\t\t\taddItemSt.setString(1, ((item.getClusterNode() != null) ? item.getClusterNode() : \"\"));\n\t\t\t\t\taddItemSt.setString(2, item.getCompName());\n\t\t\t\t\taddItemSt.setString(3, ((item.getNodeName() != null) ? item.getNodeName() : \"\"));\n\t\t\t\t\taddItemSt.setString(4, item.getKeyName());\n\t\t\t\t\taddItemSt.setString(5, item.getConfigValToString());\n\t\t\t\t\taddItemSt.setString(6, \"\" + DataTypes.getTypeId(item.getConfigVal()));\n\t\t\t\t\taddItemSt.setString(7, item.getFlag().name());\n\t\t\t\t\taddItemSt.executeUpdate();\n\t\t\t\t}\n\t\t\t} catch (SQLException e) {\n\n\t\t\t\t// Maybe the configuration item is already there, let's try to update it then\n\t\t\t\ttry {\n\t\t\t\t\tPreparedStatement updateItemSt = data_repo.getPreparedStatement(null, UPDATE_ITEM_QUERY);\n\n\t\t\t\t\tsynchronized (updateItemSt) {\n\t\t\t\t\t\tupdateItemSt.setString(1, item.getConfigValToString());\n\t\t\t\t\t\tupdateItemSt.setString(2, ((item.getClusterNode() != null) ? item.getClusterNode() : \"\"));\n\t\t\t\t\t\tupdateItemSt.setString(3, item.getCompName());\n\t\t\t\t\t\tupdateItemSt.setString(4, ((item.getNodeName() != null) ? item.getNodeName() : \"\"));\n\t\t\t\t\t\tupdateItemSt.setString(5, item.getKeyName());\n\t\t\t\t\t\tupdateItemSt.executeUpdate();\n\t\t\t\t\t}\n\t\t\t\t} catch (SQLException ex) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Problem adding/updating an item to DB: \" + item.toElement() + \"\\n\", ex);\n\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.warning(e + \"Exception while adding config item: \" + item.toString());\n\t\t\t}\n\t\t}\n\n\t\tprivate void checkDB() throws SQLException {\n\t\t\tResultSet rs = null;\n\t\t\tStatement st = null;\n\n\t\t\ttry {\n\t\t\t\tif (!data_repo.checkTable(TABLE_NAME)) {\n\t\t\t\t\tst = data_repo.createStatement(null);\n\t\t\t\t\tst.executeUpdate(CREATE_TABLE_QUERY);\n\t\t\t\t} else {\n\t\t\t\t\tlog.log(Level.CONFIG, \"DB for server configuration OK.\");\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tdata_repo.release(st, rs);\n\t\t\t\trs = null;\n\t\t\t\tst = null;\n\t\t\t}\n\t\t}\n\n\t\tprivate ConfigItem createItemFromRS(ResultSet rs) throws SQLException {\n\t\t\tConfigItem result = getItemInstance();\n\t\t\tString clusterNode = rs.getString(CLUSTER_NODE_COLUMN);\n\t\t\tString compName = rs.getString(COMPONENT_NAME_COLUMN);\n\t\t\tString nodeName = rs.getString(NODE_NAME_COLUMN);\n\t\t\tString keyName = rs.getString(KEY_NAME_COLUMN);\n\t\t\tString value_str = rs.getString(VALUE_COLUMN);\n\t\t\tString value_type = rs.getString(VALUE_TYPE_COLUMN);\n\t\t\tString flag_str = rs.getString(FLAG_COLUMN);\n\n\t\t\tresult.set(clusterNode, compName, nodeName, keyName, value_str, value_type.charAt(0), flag_str);\n\n\t\t\treturn result;\n\t\t}\n\n\t\tprivate Collection<ConfigItem> getAllItems() {\n\t\t\tList<ConfigItem> result = new ArrayList<ConfigItem>();\n\n\t\t\ttry {\n\t\t\t\tResultSet rs = null;\n\t\t\t\tPreparedStatement getAllItemsSt = data_repo.getPreparedStatement(null, GET_ALL_ITEMS_QUERY);\n\n\t\t\t\tsynchronized (getAllItemsSt) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tgetAllItemsSt.setString(1, getDefHostname());\n\t\t\t\t\t\trs = getAllItemsSt.executeQuery();\n\n\t\t\t\t\t\twhile (rs.next()) {\n\t\t\t\t\t\t\tConfigItem item = createItemFromRS(rs);\n\n\t\t\t\t\t\t\tif (item.getFlag() != ConfigItem.FLAGS.INITIAL) {\n\t\t\t\t\t\t\t\tresult.add(item);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (SQLException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Problem getting elements from DB: \", e);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\n\t\tprivate Set<ConfigItem> getCompItems(String compName) {\n\t\t\tSet<ConfigItem> result = new LinkedHashSet<ConfigItem>();\n\n\t\t\ttry {\n\t\t\t\tResultSet rs = null;\n\t\t\t\tPreparedStatement getCompItemsSt = data_repo.getPreparedStatement(null, GET_COMPONENT_ITEMS_QUERY);\n\n\t\t\t\tsynchronized (getCompItemsSt) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tgetCompItemsSt.setString(1, getDefHostname());\n\t\t\t\t\t\tgetCompItemsSt.setString(2, compName);\n\t\t\t\t\t\trs = getCompItemsSt.executeQuery();\n\n\t\t\t\t\t\twhile (rs.next()) {\n\t\t\t\t\t\t\tConfigItem item = createItemFromRS(rs);\n\n\t\t\t\t\t\t\tif (item.getFlag() != ConfigItem.FLAGS.INITIAL) {\n\t\t\t\t\t\t\t\tresult.add(item);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (SQLException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Problem getting elements from DB: \", e);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\n\t\tprivate String[] getComponentNames() {\n\t\t\tList<String> result = new ArrayList<String>();\n\n\t\t\ttry {\n\t\t\t\tResultSet rs = null;\n\t\t\t\tPreparedStatement getCompNamesSt = data_repo.getPreparedStatement(null, GET_COMPONENT_NAMES_QUERY);\n\n\t\t\t\tsynchronized (getCompNamesSt) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tgetCompNamesSt.setString(1, getDefHostname());\n\t\t\t\t\t\trs = getCompNamesSt.executeQuery();\n\n\t\t\t\t\t\twhile (rs.next()) {\n\t\t\t\t\t\t\tresult.add(rs.getString(COMPONENT_NAME_COLUMN));\n\t\t\t\t\t\t}\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (SQLException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Problem getting component names from DB: \", e);\n\t\t\t}\n\n\t\t\treturn result.toArray(new String[result.size()]);\n\t\t}\n\n\t\tprivate ConfigItem getItem(String compName, String node, String key) {\n\t\t\tConfigItem result = null;\n\n\t\t\ttry {\n\t\t\t\tResultSet rs = null;\n\t\t\t\tPreparedStatement getItemSt = data_repo.getPreparedStatement(null, GET_ITEM_QUERY);\n\n\t\t\t\tsynchronized (getItemSt) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tgetItemSt.setString(1, getDefHostname());\n\t\t\t\t\t\tgetItemSt.setString(2, compName);\n\t\t\t\t\t\tgetItemSt.setString(3, node);\n\t\t\t\t\t\tgetItemSt.setString(4, key);\n\t\t\t\t\t\trs = getItemSt.executeQuery();\n\n\t\t\t\t\t\twhile (rs.next()) {\n\t\t\t\t\t\t\tConfigItem item = createItemFromRS(rs);\n\n\t\t\t\t\t\t\tif (item.getFlag() != ConfigItem.FLAGS.INITIAL) {\n\t\t\t\t\t\t\t\tresult = item;\n\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (SQLException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Problem getting elements from DB: \", e);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\n\t\tprivate String[] getKeys(String compName, String node) {\n\t\t\tList<String> result = new ArrayList<String>();\n\n\t\t\ttry {\n\t\t\t\tResultSet rs = null;\n\t\t\t\tPreparedStatement getKeysSt = data_repo.getPreparedStatement(null, GET_KEYS_QUERY);\n\n\t\t\t\tsynchronized (getKeysSt) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tgetKeysSt.setString(1, getDefHostname());\n\t\t\t\t\t\tgetKeysSt.setString(2, compName);\n\t\t\t\t\t\tgetKeysSt.setString(3, node);\n\t\t\t\t\t\trs = getKeysSt.executeQuery();\n\n\t\t\t\t\t\twhile (rs.next()) {\n\t\t\t\t\t\t\tresult.add(rs.getString(KEY_NAME_COLUMN));\n\t\t\t\t\t\t}\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (SQLException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Problem getting keys from DB: \", e);\n\t\t\t}\n\n\t\t\treturn result.toArray(new String[result.size()]);\n\t\t}\n\n\t\tprivate int getPropertiesCount() {\n\t\t\tint result = 0;\n\n\t\t\ttry {\n\t\t\t\tResultSet rs = null;\n\t\t\t\tPreparedStatement getPropertiesCountSt = data_repo.getPreparedStatement(null,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tGET_PROPERTIES_COUNT_QUERY);\n\n\t\t\t\tsynchronized (getPropertiesCountSt) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tgetPropertiesCountSt.setString(1, getDefHostname());\n\t\t\t\t\t\trs = getPropertiesCountSt.executeQuery();\n\n\t\t\t\t\t\twhile (rs.next()) {\n\t\t\t\t\t\t\tresult = rs.getInt(\"count\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (SQLException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Problem getting elements count from DB: \", e);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\n\t\tprivate void removeItem(ConfigItem item) {\n\t\t\ttry {\n\t\t\t\tPreparedStatement deleteItemSt = data_repo.getPreparedStatement(null, DELETE_ITEM_QUERY);\n\n\t\t\t\tsynchronized (deleteItemSt) {\n\t\t\t\t\tdeleteItemSt.setString(1, ((item.getClusterNode() != null) ? item.getClusterNode() : \"\"));\n\t\t\t\t\tdeleteItemSt.setString(2, item.getCompName());\n\t\t\t\t\tdeleteItemSt.setString(3, ((item.getNodeName() != null) ? item.getNodeName() : \"\"));\n\t\t\t\t\tdeleteItemSt.setString(4, item.getKeyName());\n\t\t\t\t\tdeleteItemSt.executeUpdate();\n\t\t\t\t}\n\t\t\t} catch (SQLException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Problem removing an item from DB: \" + item.toElement(), e);\n\t\t\t}\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/conf/ConfigWriter.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\nimport tigase.kernel.beans.config.AbstractBeanConfigurator;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.Writer;\nimport java.lang.reflect.Array;\nimport java.util.*;\n\n/**\n * Created by andrzej on 05.06.2016.\n */\npublic class ConfigWriter {\n\n\tprivate static final char[] RESTRICTED_CHARS = \"=:,[]#+-*/@.\".toCharArray();\n\tprivate int indent = 0;\n\tprivate boolean resolveVariables = false;\n\n\tpublic static Map<String, Object> buildTree(Map<String, Object> props) {\n\t\tMap<String, Object> result = new LinkedHashMap<>();\n\t\tprops.forEach((k, v) -> {\n\t\t\tString[] parts = k.split(\"/\");\n\t\t\tMap<String, Object> map = result;\n\t\t\tMap<String, Object> parent = null;\n\t\t\tfor (int i = 0; i < parts.length - 1; i++) {\n\t\t\t\tparent = map;\n\t\t\t\tmap = (Map<String, Object>) map.computeIfAbsent(parts[i], (String key) -> {\n\t\t\t\t\treturn new HashMap<String, Object>();\n\t\t\t\t});\n\t\t\t}\n\t\t\tString key = parts[parts.length - 1];\n\t\t\tAbstractBeanConfigurator.BeanDefinition beanDefinition;\n\t\t\tswitch (key) {\n\t\t\t\tcase \"active\":\n\t\t\t\t\tif (map instanceof AbstractBeanConfigurator.BeanDefinition) {\n\t\t\t\t\t\tbeanDefinition = (AbstractBeanConfigurator.BeanDefinition) map;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbeanDefinition = new AbstractBeanConfigurator.BeanDefinition();\n\t\t\t\t\t\tbeanDefinition.setBeanName(parts[parts.length - 2]);\n\t\t\t\t\t\tbeanDefinition.putAll(map);\n\t\t\t\t\t\tparent.put(beanDefinition.getBeanName(), beanDefinition);\n\t\t\t\t\t}\n\n\t\t\t\t\tbeanDefinition.setActive((v instanceof Boolean) ? ((Boolean) v) : \"true\".equals(v.toString()));\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"class\":\n\t\t\t\t\tif (map instanceof AbstractBeanConfigurator.BeanDefinition) {\n\t\t\t\t\t\tbeanDefinition = (AbstractBeanConfigurator.BeanDefinition) map;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbeanDefinition = new AbstractBeanConfigurator.BeanDefinition();\n\t\t\t\t\t\tbeanDefinition.setBeanName(parts[parts.length - 2]);\n\t\t\t\t\t\tbeanDefinition.putAll(map);\n\t\t\t\t\t\tparent.put(beanDefinition.getBeanName(), beanDefinition);\n\t\t\t\t\t}\n\n\t\t\t\t\tbeanDefinition.setClazzName((String) v);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tObject old = map.get(key);\n\t\t\t\t\tif (old != null && old instanceof Map && v instanceof Map) {\n\t\t\t\t\t\t((Map<String, Object>) old).putAll((Map<String, Object>) v);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmap.put(key, v);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t});\n\n\t\treturn result;\n\t}\n\n\tpublic static boolean hasRestrictedChars(String str) {\n\t\tfor (char ch : RESTRICTED_CHARS) {\n\t\t\tif (str.indexOf(ch) > -1) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic ConfigWriter() {\n\t}\n\n\tpublic ConfigWriter resolveVariables() {\n\t\tthis.resolveVariables = true;\n\t\treturn this;\n\t}\n\n\tpublic void write(File f, Map<String, Object> props) throws IOException {\n\t\ttry (FileWriter writer = new FileWriter(f, false)) {\n\t\t\twrite(writer, props);\n\t\t}\n\t}\n\n\tpublic void write(Writer writer, Map<String, Object> props) throws IOException {\n\t\twriteMap(writer, props);\n\t}\n\n\tprivate void writeObject(Writer writer, Object obj) throws IOException {\n\t\twriteObject(writer, obj, \"\\n\");\n\t}\n\n\tprivate void writeObject(Writer writer, Object obj, String newLine) throws IOException {\n\t\tif (obj == null) {\n\t\t\twriter.write(\"null\");\n\t\t\tif (newLine != null) {\n\t\t\t\twriter.write(newLine);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (obj instanceof AbstractBeanConfigurator.BeanDefinition) {\n\t\t\tAbstractBeanConfigurator.BeanDefinition def = (AbstractBeanConfigurator.BeanDefinition) obj;\n\t\t\twriter.write(\"(\");\n\t\t\tindent++;\n\t\t\tboolean first = true;\n\t\t\tif (def.getClazzName() != null) {\n\t\t\t\twriter.write(\"class: \");\n\t\t\t\twriter.write(def.getClazzName());\n\t\t\t\tfirst = false;\n\t\t\t}\n\t\t\tif (!def.isActive()) {\n\t\t\t\tif (!first) {\n\t\t\t\t\twriter.write(\",\");\n\t\t\t\t\twriter.write(\"\\n\");\n\t\t\t\t\twriteIndent(writer);\n\t\t\t\t}\n\t\t\t\twriter.write(\"active: false\");\n\t\t\t}\n\t\t\tif (def.isExportable()) {\n\t\t\t\tif (!first) {\n\t\t\t\t\twriter.write(\",\");\n\t\t\t\t\twriter.write(\"\\n\");\n\t\t\t\t\twriteIndent(writer);\n\t\t\t\t}\n\t\t\t\twriter.write(\"exportable: true\");\n\t\t\t}\n\t\t\tindent--;\n\t\t\tif (def.isEmpty()) {\n\t\t\t\twriter.write(\") {}\");\n\t\t\t} else {\n\t\t\t\twriter.write(\") {\\n\");\n\t\t\t\tindent++;\n\t\t\t\twriteMap(writer, (Map<String, Object>) obj);\n\t\t\t\tindent--;\n\t\t\t\twriteIndent(writer);\n\t\t\t\twriter.write(\"}\");\n\t\t\t}\n\t\t\tif (newLine != null) {\n\t\t\t\twriter.write(newLine);\n\t\t\t}\n\t\t} else if (obj instanceof ConfigReader.Variable) {\n\t\t\tif (resolveVariables) {\n\t\t\t\twriteObject(writer, ((ConfigReader.Variable) obj).calculateValue(), newLine);\n\t\t\t} else if (obj instanceof ConfigReader.EnvironmentVariable) {\n\t\t\t\tConfigReader.EnvironmentVariable variable = (ConfigReader.EnvironmentVariable) obj;\n\t\t\t\twriter.write(\"env('\");\n\t\t\t\twriter.write(variable.getName());\n\t\t\t\tif (variable.getDefValue() != null) {\n\t\t\t\t\twriter.write(\"', '\");\n\t\t\t\t\twriter.write(variable.getDefValue());\n\t\t\t\t}\n\t\t\t\twriter.write(\"')\");\n\t\t\t\tif (newLine != null) {\n\t\t\t\t\twriter.write(newLine);\n\t\t\t\t}\n\t\t\t} else if (obj instanceof ConfigReader.PropertyVariable) {\n\t\t\t\tConfigReader.PropertyVariable variable = (ConfigReader.PropertyVariable) obj;\n\t\t\t\twriter.write(\"prop('\");\n\t\t\t\twriter.write(variable.getName());\n\t\t\t\tif (variable.getDefValue() != null) {\n\t\t\t\t\twriter.write(\"', '\");\n\t\t\t\t\twriter.write(variable.getDefValue());\n\t\t\t\t}\n\t\t\t\twriter.write(\"')\");\n\t\t\t\tif (newLine != null) {\n\t\t\t\t\twriter.write(newLine);\n\t\t\t\t}\n\t\t\t} else if (obj instanceof ConfigReader.CompositeVariable) {\n\t\t\t\tConfigReader.CompositeVariable variable = (ConfigReader.CompositeVariable) obj;\n\t\t\t\tList<Object> arguments = variable.getArguments();\n\t\t\t\tList<ConfigReader.CompositeVariable.Operation> operations = variable.getOperations();\n\t\t\t\twriteObject(writer, arguments.get(0), null);\n\t\t\t\tfor (int i = 0; i < operations.size(); i++) {\n\t\t\t\t\tConfigReader.CompositeVariable.Operation o = operations.get(i);\n\t\t\t\t\tswitch (o) {\n\t\t\t\t\t\tcase multiply:\n\t\t\t\t\t\t\twriter.write(\" * \");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase divide:\n\t\t\t\t\t\t\twriter.write(\" / \");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase add:\n\t\t\t\t\t\t\twriter.write(\" + \");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase substract:\n\t\t\t\t\t\t\twriter.write(\" - \");\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\twriteObject(writer, arguments.get(i + 1), null);\n\t\t\t\t}\n\t\t\t\tif (newLine != null) {\n\t\t\t\t\twriter.write(newLine);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (obj instanceof Map) {\n\t\t\tMap<String, Object> map = (Map<String, Object>) obj;\n\t\t\tif (map.isEmpty()) {\n\t\t\t\twriter.write(\"{}\");\n\t\t\t} else {\n\t\t\t\twriter.write(\"{\\n\");\n\t\t\t\tindent++;\n\t\t\t\twriteMap(writer, map);\n\t\t\t\tindent--;\n\t\t\t\twriteIndent(writer);\n\t\t\t\twriter.write(\"}\");\n\t\t\t}\n\t\t\tif (newLine != null) {\n\t\t\t\twriter.write(newLine);\n\t\t\t}\n\t\t} else if (obj instanceof Collection) {\n\t\t\tList list = (obj instanceof List) ? (List) obj : new ArrayList((Collection) obj);\n\t\t\tboolean simple = true;\n\t\t\tfor (Object o : list) {\n\t\t\t\tsimple &= (o instanceof Number) || (o instanceof String);\n\t\t\t}\n\t\t\tif (simple && list.size() < 6) {\n\t\t\t\twriter.write(\"[ \");\n\t\t\t\twriteListSimple(writer, list);\n\t\t\t\twriter.write(\" ]\");\n\t\t\t\tif (newLine != null) {\n\t\t\t\t\twriter.write(newLine);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\twriter.write(\"[\\n\");\n\t\t\t\tindent++;\n\t\t\t\twriteList(writer, list);\n\t\t\t\tindent--;\n\t\t\t\twriteIndent(writer);\n\t\t\t\twriter.write(\"]\");\n\t\t\t\tif (newLine != null) {\n\t\t\t\t\twriter.write(newLine);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (obj.getClass().isArray()) {\n\t\t\tList tmp = new ArrayList();\n\t\t\tfor (int i = 0; i < Array.getLength(obj); i++) {\n\t\t\t\ttmp.add(Array.get(obj, i));\n\t\t\t}\n\t\t\twriteObject(writer, tmp);\n\t\t} else if (obj instanceof String) {\n\t\t\twriter.write('\\'');\n\t\t\twriter.write((String) obj);\n\t\t\twriter.write(\"\\'\");\n\t\t\tif (newLine != null) {\n\t\t\t\twriter.write(newLine);\n\t\t\t}\n\t\t} else {\n\t\t\tif (obj instanceof JID || obj instanceof BareJID || obj instanceof Enum) {\n\t\t\t\twriteString(writer, obj.toString());\n\t\t\t} else {\n\t\t\t\twriter.write(obj.toString());\n\t\t\t}\n\t\t\tif (obj instanceof Long) {\n\t\t\t\twriter.write(\"L\");\n\t\t\t}\n\t\t\tif (obj instanceof Float) {\n\t\t\t\twriter.write(\"f\");\n\t\t\t}\n\t\t\tif (newLine != null) {\n\t\t\t\twriter.write(newLine);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void writeMap(Writer writer, Map<String, Object> map) throws IOException {\n\t\tList<Map.Entry<String, Object>> items = new ArrayList<>(map.entrySet());\n\n\t\titems.sort((a, b) -> {\n\t\t\tboolean a_ = a.getKey().startsWith(\"--\");\n\t\t\tboolean b_ = b.getKey().startsWith(\"--\");\n\n\t\t\tif (a_ && !b_) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tif (!b_ && a_) {\n\t\t\t\treturn 1;\n\t\t\t}\n\n\t\t\tif ((a.getValue() instanceof Map) && !(b.getValue() instanceof Map)) {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif (!(a.getValue() instanceof Map) && (b.getValue() instanceof Map)) {\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\tif ((a.getValue() instanceof Map) && (b.getValue() instanceof Map)) {\n\t\t\t\tif (\"dataSource\".equals(a.getKey())) {\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t\tif (\"dataSource\".equals(b.getKey())) {\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tif (\"userRepository\".equals(a.getKey())) {\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t\tif (\"userRepository\".equals(b.getKey())) {\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t\tif (\"authRepository\".equals(a.getKey())) {\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t\tif (\"authRepository\".equals(b.getKey())) {\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn a.getKey().compareTo(b.getKey());\n\t\t});\n\n\t\tfor (Map.Entry<String, Object> e : items) {\n\t\t\twriteIndent(writer);\n\t\t\tif (indent == 0 && e.getKey().startsWith(\"--\")) {\n\t\t\t\twriter.write(e.getKey());\n\t\t\t} else {\n\t\t\t\twriteString(writer, e.getKey());\n\t\t\t}\n\t\t\tif (e.getValue() instanceof Map) {\n\t\t\t\twriter.write(\" \");\n\t\t\t} else {\n\t\t\t\twriter.write(\" = \");\n\t\t\t}\n\t\t\tif (indent == 0 && e.getKey().startsWith(\"--\") && (e.getValue() instanceof String)) {\n\t\t\t\twriteString(writer, (String) e.getValue());\n\t\t\t\twriter.write(\"\\n\");\n\t\t\t} else {\n\t\t\t\twriteObject(writer, e.getValue());\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void writeIndent(Writer writer) throws IOException {\n\t\tfor (int i = 0; i < indent; i++) {\n\t\t\twriter.write(\"    \");\n\t\t}\n\t}\n\n\tprivate void writeList(Writer writer, List list) throws IOException {\n\t\tboolean first = true;\n\t\tfor (Object obj : list) {\n\t\t\tif (!first) {\n\t\t\t\twriter.write(\",\\n\");\n\t\t\t} else {\n\t\t\t\tfirst = false;\n\t\t\t}\n\t\t\twriteIndent(writer);\n\t\t\twriteObject(writer, obj, null);\n\t\t}\n\t\tif (!list.isEmpty()) {\n\t\t\twriter.write(\"\\n\");\n\t\t}\n\t}\n\n\tprivate void writeListSimple(Writer writer, List list) throws IOException {\n\t\tboolean first = true;\n\t\tfor (Object obj : list) {\n\t\t\tif (!first) {\n\t\t\t\twriter.write(\", \");\n\t\t\t} else {\n\t\t\t\tfirst = false;\n\t\t\t}\n\t\t\twriteObject(writer, obj, null);\n\t\t}\n\t}\n\n\tprivate void writeString(Writer writer, String str) throws IOException {\n\t\tif (str == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (hasRestrictedChars(str)) {\n\t\t\twriter.append('\\'');\n\t\t\twriter.write(str);\n\t\t\twriter.append('\\'');\n\t\t} else {\n\t\t\twriter.write(str);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/conf/ConfigXMLRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\nimport tigase.db.DBInitException;\nimport tigase.db.TigaseDBException;\nimport tigase.xml.db.NodeNotFoundException;\nimport tigase.xml.db.XMLDB;\nimport tigase.xml.db.XMLDBException;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Class <code>ConfigXMLRepository</code> provides access to configuration settings.\n * <br>\n * <p> Created: Sat Nov 13 18:53:21 2004 </p>\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class ConfigXMLRepository\n\t\textends ConfigurationCache {\n\n\tpublic static final String COMPONENT_NODE = \"component\";\n\n\tpublic static final String ROOT_NODE = \"tigase-config\";\n\n\tpublic static final String XMPP_CONFIG_FILE_PROPERTY_KEY = \"xmpp.config.file\";\n\n\tpublic static final String XMPP_CONFIG_FILE_PROPERTY_VAL = \"xmpp_server.xml\";\n\tprivate static final Logger log = Logger.getLogger(\"tigase.conf.ConfigRepository\");\n\tprivate static Map<String, ConfigXMLRepository> configs = new LinkedHashMap<String, ConfigXMLRepository>();\n\tprivate static ConfigXMLRepository def_config = null;\n\n\tprivate String config_file = null;\n\tprivate XMLDB xmldb = null;\n\n\tpublic static ConfigXMLRepository getConfigRepository() throws XMLDBException {\n\t\treturn getConfigRepository(null);\n\t}\n\n\tpublic static ConfigXMLRepository getConfigRepository(final String file_name) throws XMLDBException {\n\t\treturn getConfigRepository(false, file_name);\n\t}\n\n\tpublic static ConfigXMLRepository getConfigRepository(final boolean debug, final String file_name)\n\t\t\tthrows XMLDBException {\n\t\tConfigXMLRepository config = null;\n\n\t\tif (file_name == null) {\n\t\t\tconfig = def_config;\n\t\t}    // end of if (file_name == null)\n\t\telse {\n\t\t\tconfig = configs.get(file_name);\n\t\t}    // end of if (file_name == null) else\n\n\t\tif (config == null) {\n\t\t\tif (file_name == null) {\n\t\t\t\tconfig = new ConfigXMLRepository(debug);\n\t\t\t}    // end of if (file_name == null)\n\t\t\telse {\n\t\t\t\tconfig = new ConfigXMLRepository(debug, file_name);\n\t\t\t}    // end of if (file_name == null) else\n\t\t}      // end of if (config == null)\n\n\t\treturn config;\n\t}\n\n\tpublic ConfigXMLRepository() {\n\t}\n\n\tprivate ConfigXMLRepository(final boolean debug) throws XMLDBException {\n\t\tconfig_file = System.getProperty(XMPP_CONFIG_FILE_PROPERTY_KEY, XMPP_CONFIG_FILE_PROPERTY_VAL);\n\t\tinit();\n\t\tdef_config = this;\n\t}\n\n\tprivate ConfigXMLRepository(final boolean debug, final String file) throws XMLDBException {\n\t\tconfig_file = file;\n\t\tinit();\n\t}\n\n\t@Override\n\tpublic void addItem(String compName, ConfigItem item) {\n\t\ttry {\n\t\t\txmldb.setData(item.getCompName(), item.getNodeName(), item.getKeyName(), item.getConfigVal());\n\t\t} catch (NodeNotFoundException e1) {\n\t\t\ttry {\n\t\t\t\txmldb.addNode1(item.getCompName());\n\t\t\t\txmldb.setData(item.getCompName(), item.getNodeName(), item.getKeyName(), item.getConfigVal());\n\t\t\t} catch (Exception e2) {\n\t\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\t\"Can't add item for compName=\" + item.getCompName() + \", node=\" + item.getNodeName() +\n\t\t\t\t\t\t\t\t\", key=\" + item.getKeyName() + \", value=\" + item.getConfigValToString(), e2);\n\t\t\t}    // end of try-catch\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Can't add item for compName=\" + item.getCompName() + \", node=\" + item.getNodeName() + \", key=\" +\n\t\t\t\t\t\t\titem.getKeyName() + \", value=\" + item.getConfigValToString(), e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Collection<ConfigItem> allItems() throws TigaseDBException {\n\t\tSet<ConfigItem> result = new LinkedHashSet<ConfigItem>();\n\t\tString[] compNames = getCompNames();\n\n\t\tif (compNames != null) {\n\t\t\tfor (String comp : compNames) {\n\t\t\t\tresult.addAll(getItemsForComponent(comp));\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic String[] getCompNames() {\n\t\tList<String> comps = xmldb.getAllNode1s();\n\n\t\tif (comps != null) {\n\t\t\treturn comps.toArray(new String[comps.size()]);\n\t\t}    // end of if (comps != null)\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic ConfigItem getItem(String compName, String node, String key) {\n\t\ttry {\n\t\t\tObject value = xmldb.getData(compName, node, key, null);\n\t\t\tConfigItem item = getItemInstance();\n\n\t\t\titem.set(compName, node, key, value);\n\n\t\t\treturn item;\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Can't load value for compName=\" + compName + \", node=\" + node + \", key=\" + key, e);\n\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic Set<ConfigItem> getItemsForComponent(String compName) {\n\t\tSet<ConfigItem> result = new LinkedHashSet<ConfigItem>();\n\t\tList<String> allNodes = new ArrayList<String>();\n\t\tString subnode = \"\";\n\n\t\tgetSubnodes(allNodes, compName, subnode);\n\n\t\tString[] keys = getKeys(compName, null);\n\n\t\tlog.config(\"Found keys: \" + Arrays.toString(keys));\n\t\taddVals(result, compName, null, keys);\n\n\t\tfor (String node : allNodes) {\n\t\t\tkeys = getKeys(compName, node);\n\t\t\tlog.config(\"In node : '\" + node + \"' found keys: \" + Arrays.toString(keys));\n\t\t\taddVals(result, compName, node, keys);\n\t\t}    // end of for (String node : allNodes)\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic String[] getKeys(final String root, final String node) {\n\t\ttry {\n\t\t\treturn xmldb.getKeys(root, node);\n\t\t}    // end of try\n\t\tcatch (NodeNotFoundException e) {\n\t\t\treturn null;\n\t\t}    // end of try-catch\n\t}\n\n\t@Override\n\tpublic void initRepository(String repo_uri, Map<String, String> params) throws DBInitException {\n\t\tconfig_file = (String) params.get(\"-c\");\n\n\t\ttry {\n\t\t\tinit();\n\t\t} catch (XMLDBException ex) {\n\t\t\tthrow new DBInitException(\"Can not initialize configuration repository: \", ex);\n\t\t}\n\t}\n\n\tpublic String nodeForPackage(Class cls) {\n\t\treturn cls.getPackage().getName().replace('.', '/');\n\t}\n\n\t@Override\n\tpublic void removeItem(String compName, ConfigItem item) {\n\t\ttry {\n\t\t\txmldb.removeData(item.getCompName(), item.getNodeName(), item.getKeyName());\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Can't remove item for compName=\" + item.getCompName() + \", node=\" + item.getNodeName() + \", key=\" +\n\t\t\t\t\t\t\titem.getKeyName() + \", value=\" + item.getConfigValToString(), e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic int size() {\n\t\treturn (int) xmldb.getAllNode1sCount();\n\t}\n\n\t@Override\n\tpublic void store() throws TigaseDBException {\n\t\ttry {\n\t\t\txmldb.sync();\n\t\t} catch (IOException ex) {\n\t\t\tthrow new TigaseDBException(\"Problem saving configuration data: \", ex);\n\t\t}\n\t}\n\n\t/** *********** Old code ************ */\n\tprivate void addVals(Set<ConfigItem> props, String compName, String node, String[] keys) {\n\t\tif (keys != null) {\n\t\t\tfor (String key : keys) {\n\t\t\t\ttry {\n\t\t\t\t\tObject value = xmldb.getData(compName, node, key, null);\n\t\t\t\t\tConfigItem item = getItemInstance();\n\n\t\t\t\t\titem.set(compName, node, key, value);\n\t\t\t\t\tprops.add(item);\n\t\t\t\t} catch (NodeNotFoundException ex) {\n\t\t\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\t\t\"Can't load value for compName=\" + compName + \", node=\" + node + \", key=\" + key, ex);\n\t\t\t\t}\n\t\t\t}    // end of for (String key : keys)\n\t\t}      // end of if (keys != null)\n\t}\n\n\tprivate void getSubnodes(List<String> result, String root, String node) {\n\t\tString[] subnodes = getSubnodes(root, node);\n\t\tString node_tmp = (node.equals(\"\") ? node : node + \"/\");\n\n\t\tif (subnodes != null) {\n\t\t\tfor (String subnode : subnodes) {\n\t\t\t\tresult.add(node_tmp + subnode);\n\t\t\t\tlog.config(\"Adding subnode: \" + node_tmp + subnode);\n\t\t\t\tgetSubnodes(result, root, node_tmp + subnode);\n\t\t\t}    // end of for (String subnode : subnodes)\n\t\t}      // end of if (subnodes != null)\n\t}\n\n\tprivate String[] getSubnodes(final String root, final String node) {\n\t\ttry {\n\t\t\treturn xmldb.getSubnodes(root, node);\n\t\t} catch (NodeNotFoundException e) {\n\t\t\treturn null;\n\t\t}    // end of try-catch\n\t}\n\n\tprivate void init() throws XMLDBException {\n\t\ttry {\n\t\t\tif (new File(config_file).exists()) {\n\t\t\t\txmldb = new XMLDB(config_file);\n\t\t\t} else {\n\t\t\t\txmldb = XMLDB.createDB(config_file, ROOT_NODE, COMPONENT_NODE);\n\t\t\t}\n\t\t} catch (IOException e) {\n\t\t\tlog.warning(\"Can not open existing configuration file, creating new one, \" + e);\n\t\t\txmldb = XMLDB.createDB(config_file, ROOT_NODE, COMPONENT_NODE);\n\t\t}    // end of try-catch\n\n\t\tconfigs.put(config_file, this);\n\t}\n}    // ConfigXMLRepository\n\n"
  },
  {
    "path": "src/main/java/tigase/conf/Configurable.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.RepositoryFactory;\nimport tigase.server.ServerComponent;\n\nimport java.util.Map;\n\n/**\n * Interface Configurable\n * <br>\n * Objects inheriting this interface can be configured. In Tigase system object can't request configuration properties.\n * Configuration of the object is passed to it at some time. Actually it can be passed at any time. This allows dynamic\n * system reconfiguration at runtime.\n * <br>\n * Created: Tue Nov 22 07:07:11 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface Configurable\n\t\textends ServerComponent {\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String ADMINS_PROP_KEY = \"admins\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String AMP_CLASS_NAME = \"tigase.server.amp.AmpComponent\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String AMP_CLUST_CLASS_NAME = \"tigase.cluster.AmpComponentClustered\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String BOSH_CLUST_COMP_CLASS_NAME = \"tigase.cluster.BoshConnectionClustered\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String BOSH_COMP_CLASS_NAME = \"tigase.server.bosh.BoshConnectionManager\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String C2S_CLUST_COMP_CLASS_NAME = \"tigase.cluster.ClientConnectionClustered\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String C2S_COMP_CLASS_NAME = \"tigase.server.xmppclient.ClientConnectionManager\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String CL_COMP_CLASS_NAME = \"tigase.cluster.ClusterConnectionManager\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String CLUSTER_CONECT = \"cluster-connect\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String CLUSTER_CONTR_CLASS_NAME = \"tigase.cluster.ClusterController\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String CLUSTER_LISTEN = \"cluster-listen\";\n\n\t/**\n\t * Constant <code>CLUSTER_MODE</code> sets the cluster mode to either <code>true</code> or <code>false</code>. By\n\t * default cluster mode is set to <code>false</code>.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String CLUSTER_MODE = \"--cluster-mode\";\n\n\t/**\n\t * Constant <code>CLUSTER_NODES</code> is for setting list of cluster nodes the instance should try to connect to.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String CLUSTER_NODES = \"--cluster-nodes\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String CLUSTER_NODES_PROP_KEY = \"cluster-nodes\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String COMP_PROT_CLASS_NAME = \"tigase.server.ext.ComponentProtocol\";\n\n\tpublic static final String COMPONENT_ID_PROP_KEY = \"component-id\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String DEF_AMP_NAME = \"amp\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String DEF_BOSH_NAME = \"bosh\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String DEF_C2S_NAME = \"c2s\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String DEF_CL_COMP_NAME = \"cl-comp\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String DEF_CLUST_CONTR_NAME = \"cluster-contr\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String DEF_COMP_PROT_NAME = \"ext\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String DEF_EVENTBUS_NAME = \"eventbus\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String DEF_EXT_COMP_NAME = \"ext-comp\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String DEF_HOSTNAME_PROP_KEY = \"def-hostname\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String DEF_MONITOR_NAME = \"monitor\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String DEF_S2S_NAME = \"s2s\";\n\n\tpublic static final String DEF_SM_NAME = \"sess-man\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String DEF_STATS_NAME = \"stats\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String DEF_VHOST_MAN_NAME = \"vhost-man\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String DEF_WS2S_NAME = \"ws2s\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String EVENTBUS_CLASS_NAME = \"tigase.disteventbus.component.EventBusComponent\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String EXT_COMP_CLASS_NAME = \"tigase.server.xmppcomponent.ComponentConnectionManager\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_ADMINS = \"--admins\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_COMP_CLASS = \"--comp-class\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_COMP_NAME = \"--comp-name\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_CONF = \"--gen-\";\n\n\t/**\n\t * Constant <code>GEN_CONFIG</code> keeps the string with which all configuration types starts.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_CONFIG = \"--gen-config\";\n\n\t/**\n\t * Constant <code>GEN_CONFIG_ALL</code> keeps parameter name for configuration with all available components loaded\n\t * directly to the server.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_CONFIG_ALL = GEN_CONFIG + \"-all\";\n\n\t/**\n\t * Constant <code>GEN_CONFIG_SM</code> keeps parameter name for configuration with SessionManager loaded and\n\t * XEP-0114 component preconfigured to connect to server instance with ClientConnectionManager.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_CONFIG_SM = GEN_CONFIG + \"-sm\";\n\n\t/**\n\t * Constant <code>GEN_CONFIG_DEF</code> keeps parameter name for the most typical configuration: SessionManager,\n\t * ClientConnectionManager and S2SConnectionManager loaded.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_CONFIG_DEF = GEN_CONFIG + \"-default\";\n\n\t/**\n\t * Constant <code>GEN_CONFIG_CS</code> keeps parameter name for configuration with ClientConnectionManager loaded\n\t * and XEP-0114 component preconfigured to connect to server instance with SessionManager loaded.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_CONFIG_CS = GEN_CONFIG + \"-cs\";\n\n\t/**\n\t * Constant <code>GEN_CONFIG_COMP</code> keeps parameter name for configuration with a single (given as an extra\n\t * parameter) component and XEP-0114 or XEP-0225 component loaded and preconfigured to connect to other Jabber/XMPP\n\t * server instance (either Tigase or any different server).\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_CONFIG_COMP = GEN_CONFIG + \"-comp\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_DEBUG = \"--debug\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_DEBUG_PACKAGES = \"--debug-packages\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_EXT_COMP = \"--ext-comp\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_MAX_QUEUE_SIZE = \"--max-queue-size\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_SCRIPT_DIR = \"--script-dir\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_SM_PLUGINS = \"--sm-plugins\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_SREC_ADMINS = \"--gen-srec-admins\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_SREC_DB = \"--gen-srec-db\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_SREC_DB_URI = \"--gen-srec-db-uri\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_TEST = \"--test\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_TRUSTED = \"--trusted\";\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_USER_DB = RepositoryFactory.GEN_USER_DB;\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_VIRT_HOSTS = \"--virt-hosts\";\n\n\tpublic static final String HOSTNAMES_PROP_KEY = \"hostnames\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String MONITOR_CLASS_NAME = \"tigase.monitor.MonitorComponent\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String MONITOR_CLUST_CLASS_NAME = \"tigase.cluster.MonitorClustered\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String MONITORING = \"--monitoring\";\n\n\tpublic static final String ROUTER_COMP_CLASS_NAME = \"tigase.server.MessageRouter\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String S2S_CLUST_COMP_CLASS_NAME = \"tigase.cluster.S2SConnectionClustered\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String S2S_COMP_CLASS_NAME = \"tigase.server.xmppserver.S2SConnectionManager\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String SM_CLUST_COMP_CLASS_NAME = \"tigase.cluster.SessionManagerClustered\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String SM_COMP_CLASS_NAME = \"tigase.server.xmppsession.SessionManager\";\n\n\tpublic static final String STANZA_WHITE_CHAR_ACK = \"white-char\";\n\n\tpublic static final String STANZA_XMPP_ACK = \"xmpp\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String STATS_CLASS_NAME = \"tigase.stats.StatisticsCollector\";\n\n\tpublic static final String STRINGPREP_PROCESSOR = \"--stringprep-processor\";\n\n\tpublic static final String TRUSTED_PROP_KEY = \"trusted\";\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String USER_REPO_POOL_CLASS = RepositoryFactory.USER_REPO_POOL_CLASS;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String USER_DOMAIN_POOL_CLASS = RepositoryFactory.USER_DOMAIN_POOL_CLASS;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_USER_DB_URI_PROP_KEY = RepositoryFactory.GEN_USER_DB_URI_PROP_KEY;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_USER_DB_URI = \"--\" + RepositoryFactory.GEN_USER_DB_URI_PROP_KEY;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_AUTH_DB_URI = RepositoryFactory.GEN_AUTH_DB_URI;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String GEN_AUTH_DB = RepositoryFactory.GEN_AUTH_DB;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String AUTH_REPO_POOL_CLASS = RepositoryFactory.AUTH_REPO_POOL_CLASS;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String AUTH_DOMAIN_POOL_CLASS = RepositoryFactory.AUTH_DOMAIN_POOL_CLASS;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String USER_REPO_POOL_SIZE = RepositoryFactory.USER_REPO_POOL_SIZE;\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String VHOST_MAN_CLASS_NAME = \"tigase.vhosts.VHostManager\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String VHOST_MAN_CLUST_CLASS_NAME = \"tigase.cluster.VHostManagerClustered\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String WS2S_CLASS_NAME = \"tigase.server.websocket.WebSocketClientConnectionManager\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String WS2S_CLUST_CLASS_NAME = \"tigase.cluster.WebSocketClientConnectionClustered\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String XMPP_STANZA_ACK = \"--stanza-ack\";\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String XML_REPO_URL_PROP_VAL = RepositoryFactory.XML_REPO_URL_PROP_VAL;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String XML_REPO_CLASS_PROP_VAL = RepositoryFactory.XML_REPO_CLASS_PROP_VAL;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String USER_REPO_URL_PROP_KEY = RepositoryFactory.USER_REPO_URL_PROP_KEY;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String USER_REPO_POOL_SIZE_PROP_KEY = RepositoryFactory.USER_REPO_POOL_SIZE_PROP_KEY;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String USER_REPO_PARAMS_NODE = RepositoryFactory.USER_REPO_PARAMS_NODE;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String USER_REPO_DOMAINS_PROP_KEY = RepositoryFactory.USER_REPO_DOMAINS_PROP_KEY;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String TIGASE_CUSTOM_AUTH_REPO_CLASS_PROP_VAL = RepositoryFactory.TIGASE_CUSTOM_AUTH_REPO_CLASS_PROP_VAL;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String TIGASE_AUTH_REPO_URL_PROP_VAL = RepositoryFactory.TIGASE_AUTH_REPO_URL_PROP_VAL;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String TIGASE_AUTH_REPO_CLASS_PROP_VAL = RepositoryFactory.TIGASE_AUTH_REPO_CLASS_PROP_VAL;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String SHARED_USER_REPO_PROP_KEY = RepositoryFactory.SHARED_USER_REPO_PROP_KEY;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String SHARED_USER_REPO_PARAMS_PROP_KEY = RepositoryFactory.SHARED_USER_REPO_PARAMS_PROP_KEY;\n\n\t/**\n\t * @deprecated\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String SHARED_AUTH_REPO_PROP_KEY = RepositoryFactory.SHARED_AUTH_REPO_PROP_KEY;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String SHARED_AUTH_REPO_PARAMS_PROP_KEY = RepositoryFactory.SHARED_AUTH_REPO_PARAMS_PROP_KEY;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String PGSQL_REPO_URL_PROP_VAL = RepositoryFactory.PGSQL_REPO_URL_PROP_VAL;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String PGSQL_REPO_CLASS_PROP_VAL = RepositoryFactory.PGSQL_REPO_CLASS_PROP_VAL;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String MYSQL_REPO_URL_PROP_VAL = RepositoryFactory.MYSQL_REPO_URL_PROP_VAL;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String MYSQL_REPO_CLASS_PROP_VAL = RepositoryFactory.MYSQL_REPO_CLASS_PROP_VAL;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String LIBRESOURCE_REPO_URL_PROP_VAL = RepositoryFactory.LIBRESOURCE_REPO_URL_PROP_VAL;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String LIBRESOURCE_REPO_CLASS_PROP_VAL = RepositoryFactory.LIBRESOURCE_REPO_CLASS_PROP_VAL;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String DUMMY_REPO_CLASS_PROP_VAL = RepositoryFactory.DUMMY_REPO_CLASS_PROP_VAL;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String DRUPALWP_REPO_CLASS_PROP_VAL = RepositoryFactory.DRUPALWP_REPO_CLASS_PROP_VAL;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String DRUPAL_REPO_URL_PROP_VAL = RepositoryFactory.DRUPAL_REPO_URL_PROP_VAL;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String DERBY_REPO_URL_PROP_VAL = RepositoryFactory.DERBY_REPO_URL_PROP_VAL;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String DERBY_REPO_CLASS_PROP_VAL = RepositoryFactory.DERBY_REPO_CLASS_PROP_VAL;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String AUTH_REPO_URL_PROP_KEY = RepositoryFactory.AUTH_REPO_URL_PROP_KEY;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String AUTH_REPO_PARAMS_NODE = RepositoryFactory.AUTH_REPO_PARAMS_NODE;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String AUTH_REPO_DOMAINS_PROP_KEY = RepositoryFactory.AUTH_REPO_DOMAINS_PROP_KEY;\n\n\t/**\n\t * Returns default configuration settings for the component as a <code>Map</code> with keys as configuration\n\t * property IDs and values as the configuration property values. All the default parameters returned from this\n\t * method are later passed to the <code>setProperties(...)</code> method. Some of them may have changed value if\n\t * they have been overwritten in the server configuration. The configuration property value can be of any of the\n\t * basic types: <code>int</code>, <code>long</code>, <code>boolean</code>, <code>String</code>.\n\t *\n\t * @param params is a <code>Map</code> with some initial properties set for the starting up server. These parameters\n\t * can be used as a hints to generate component's default configuration.\n\t *\n\t * @return a <code>Map</code> with the component default configuration.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tMap<String, Object> getDefaults(Map<String, Object> params);\n\n\t/**\n\t * Sets all configuration properties for the object.\n\t *\n\t * @param properties {@link Map} with the configuration\n\t *\n\t * @throws tigase.conf.ConfigurationException - if setting configuration will fail which will make it unable to\n\t * work\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tvoid setProperties(Map<String, Object> properties) throws ConfigurationException;\n}\n"
  },
  {
    "path": "src/main/java/tigase/conf/ConfigurationCache.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.DBInitException;\nimport tigase.db.TigaseDBException;\nimport tigase.db.comp.RepositoryChangeListenerIfc;\nimport tigase.kernel.beans.config.ConfigField;\n\nimport java.io.FileWriter;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Dec 10, 2009 2:02:41 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class ConfigurationCache\n\t\timplements ConfigRepositoryIfc {\n\n\tpublic static final String CONFIG_DUMP_FILE_PROP_DEF = \"etc/config-dump.properties\";\n\n\tpublic static final String CONFIG_DUMP_FILE_PROP_KEY = \"config-dump-file\";\n\n\tprivate static final Logger log = Logger.getLogger(ConfigurationCache.class.getName());\n\n\t/**\n\t * Even though every element has a component name field the whole configuration is grouped by the component name\n\t * anyway to improve access time to the configuration. Very rarely we need access to whole configuration, in most\n\t * cases we access configuration for a particular server component.\n\t */\n\tprivate Map<String, Set<ConfigItem>> config = new LinkedHashMap<String, Set<ConfigItem>>();\n\t@ConfigField(desc = \"File to dump configuration to\")\n\tprivate String configDumpFileName = CONFIG_DUMP_FILE_PROP_DEF;\n\tprivate String hostname = null;\n\tprivate RepositoryChangeListenerIfc<ConfigItem> repoChangeList = null;\n\n\t@Override\n\tpublic void addRepoChangeListener(RepositoryChangeListenerIfc<ConfigItem> repoChangeListener) {\n\t\tthis.repoChangeList = repoChangeListener;\n\t}\n\n\t@Override\n\tpublic void removeRepoChangeListener(RepositoryChangeListenerIfc<ConfigItem> repoChangeListener) {\n\t\tthis.repoChangeList = null;\n\t}\n\n\tpublic void addItem(String compName, ConfigItem item) {\n\t\tSet<ConfigItem> confItems = config.get(compName);\n\n\t\tif (confItems == null) {\n\t\t\tconfItems = new LinkedHashSet<ConfigItem>();\n\t\t\tconfig.put(compName, confItems);\n\t\t}\n\n\t\tboolean updated = confItems.remove(item);\n\n\t\tconfItems.add(item);\n\t\tif (repoChangeList != null) {\n\t\t\tif (updated) {\n\t\t\t\trepoChangeList.itemUpdated(item);\n\t\t\t} else {\n\t\t\t\trepoChangeList.itemAdded(item);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void addItemNoStore(ConfigItem item) {\n\t\taddItem(item.getCompName(), item);\n\t}\n\n\t@Override\n\tpublic void addItem(ConfigItem item) throws TigaseDBException {\n\t\taddItemNoStore(item);\n\t}\n\n\t@Override\n\tpublic void addItem(String key, Object value) throws ConfigurationException {\n\t\tint idx1 = key.indexOf(\"/\");\n\n\t\tif (idx1 > 0) {\n\t\t\tString compName = key.substring(0, idx1);\n\t\t\tint idx2 = key.lastIndexOf(\"/\");\n\t\t\tString nodeName = null;\n\t\t\tString keyName = key.substring(idx2 + 1);\n\n\t\t\tif (idx1 != idx2) {\n\t\t\t\tnodeName = key.substring(idx1 + 1, idx2);\n\t\t\t}\n\n\t\t\tConfigItem item = getItemInstance();\n\n\t\t\titem.set(getDefHostname(), compName, nodeName, keyName, value);\n\t\t\taddItem(compName, item);\n\t\t} else {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"You have to provide a key with at least\" + \" 'component_name/key_name': \" + key);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Collection<ConfigItem> allItems() throws TigaseDBException {\n\t\tList<ConfigItem> result = new ArrayList<ConfigItem>();\n\n\t\tfor (Set<ConfigItem> items : config.values()) {\n\t\t\tresult.addAll(items);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean contains(String key) {\n\t\treturn getItem(key) != null;\n\t}\n\n\t@Override\n\tpublic void destroy() {\n\t\t// Nothing to do now, maybe later we would add some logic here to move \n\t\t// deinitialization of config repository here - added to implement all methods\n\t\t// needed by ComponentRepository interface which was extended while\n\t\t// implementing support for autodiscovery based on URI and adding\n\t\t// more flexibility in use of repositories\n\t}\n\n\t@Override\n\tpublic Object get(String compName, String node, String key, Object def) {\n\t\tConfigItem item = getItem(compName, node, key);\n\n\t\tif (item != null) {\n\t\t\treturn item.getConfigVal();\n\t\t}\n\n\t\treturn def;\n\t}\n\n\t@Override\n\tpublic String[] getCompNames() {\n\t\treturn config.keySet().toArray(new String[config.size()]);\n\t}\n\n\tpublic String getDefHostname() {\n\t\treturn this.hostname;\n\t}\n\n\t@Override\n\tpublic void setDefHostname(String hostname) {\n\t\tthis.hostname = hostname;\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic void getDefaults(Map<String, Object> defs, Map<String, Object> params) {\n\t\tdefs.put(CONFIG_DUMP_FILE_PROP_KEY, CONFIG_DUMP_FILE_PROP_DEF);\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getInitProperties() {\n\t\treturn null;\n\t}\n\n\tpublic ConfigItem getItem(String compName, String node, String key) {\n\t\tSet<ConfigItem> confItems = getItemsForComponent(compName);\n\n\t\tif (confItems != null) {\n\t\t\tfor (ConfigItem item : confItems) {\n\t\t\t\tif (item.isNodeKey(node, key)) {\n\t\t\t\t\treturn item;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic ConfigItem getItem(String key) {\n\t\tint idx1 = key.indexOf(\"/\");\n\n\t\tif (idx1 > 0) {\n\t\t\tString compName = key.substring(0, idx1);\n\t\t\tint idx2 = key.lastIndexOf(\"/\");\n\t\t\tString nodeName = null;\n\t\t\tString keyName = key.substring(idx2 + 1);\n\n\t\t\tif (idx1 != idx2) {\n\t\t\t\tnodeName = key.substring(idx1 + 1, idx2);\n\t\t\t}\n\n\t\t\treturn getItem(compName, nodeName, keyName);\n\t\t} else {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"You have to provide a key with at least\" + \" 'component_name/key_name': \" + key);\n\t\t}\n\t}\n\n\t@Override\n\tpublic ConfigItem getItemInstance() {\n\t\treturn new ConfigItem();\n\t}\n\n\t@Override\n\tpublic Set<ConfigItem> getItemsForComponent(String compName) {\n\t\treturn config.get(compName);\n\t}\n\n\t@Override\n\tpublic String[] getKeys(String compName, String node) {\n\t\tSet<String> keysForNode = new LinkedHashSet<String>();\n\t\tSet<ConfigItem> confItems = config.get(compName);\n\n\t\tfor (ConfigItem item : confItems) {\n\t\t\tif (item.isNode(node)) {\n\t\t\t\tkeysForNode.add(item.getKeyName());\n\t\t\t}\n\t\t}\n\t\tif (keysForNode.size() > 0) {\n\t\t\treturn keysForNode.toArray(new String[keysForNode.size()]);\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic Map<String, Object> getProperties(String compName) throws ConfigurationException {\n\n\t\t// It must not return a null value, even if configuration for the\n\t\t// component does not exist yet, it has to initialized to create new one.\n\t\tMap<String, Object> result = new LinkedHashMap<String, Object>();\n\n\t\t// Let's convert the internal representation of the configuration to that\n\t\t// used by the components.\n\t\tSet<ConfigItem> confItems = getItemsForComponent(compName);\n\n\t\tif (confItems != null) {\n\t\t\tfor (ConfigItem item : confItems) {\n\t\t\t\tString key = item.getConfigKey();\n\t\t\t\tObject value = item.getConfigVal();\n\n\t\t\t\tresult.put(key, value);\n\t\t\t}\n\t\t}\n\n\t\t// Hopefuly this doesn't happen.... or I have a bug somewhere\n\t\treturn result;\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic void initRepository(String resource_uri, Map<String, String> params) throws DBInitException {\n\t\t// Nothing to do..\n\t}\n\n\t@Override\n\tpublic Iterator<ConfigItem> iterator() {\n\t\ttry {\n\t\t\tCollection<ConfigItem> items = allItems();\n\n\t\t\treturn (items != null) ? items.iterator() : null;\n\t\t} catch (TigaseDBException ex) {\n\t\t\tlog.log(Level.WARNING, \"Problem accessing repository: \", ex);\n\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void putProperties(String compName, Map<String, Object> props) throws ConfigurationException {\n\t\tfor (Map.Entry<String, Object> entry : props.entrySet()) {\n\t\t\tConfigItem item = new ConfigItem();\n\n\t\t\titem.setNodeKey(getDefHostname(), compName, entry.getKey(), entry.getValue());\n\t\t\taddItem(compName, item);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void reload() throws TigaseDBException {\n\n\t\t// Do nothing, this is in memory config repository only\n\t}\n\n\t@Override\n\tpublic void remove(String compName, String node, String key) {\n\t\tConfigItem item = getItem(compName, node, key);\n\n\t\tif (item != null) {\n\t\t\tremoveItem(compName, item);\n\t\t}\n\t}\n\n\tpublic void removeItem(String compName, ConfigItem item) {\n\t\tSet<ConfigItem> confItems = config.get(compName);\n\n\t\tif (confItems != null) {\n\t\t\tconfItems.remove(item);\n\t\t}\n\t\tif (repoChangeList != null) {\n\t\t\trepoChangeList.itemRemoved(item);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void removeItem(String key) throws TigaseDBException {\n\t\tremoveItemNoStore(key);\n\t}\n\n\t@Override\n\tpublic void removeItemNoStore(String key) {\n\t\tConfigItem item = getItem(key);\n\n\t\tif (item != null) {\n\t\t\tremoveItem(item.getCompName(), item);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void set(String compName, String node, String key, Object value) {\n\t\tConfigItem item = getItem(compName, node, key);\n\n\t\tif (item == null) {\n\t\t\titem = getItemInstance();\n\t\t}\n\t\titem.set(getDefHostname(), compName, node, key, value);\n\t\taddItem(compName, item);\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic void setProperties(Map<String, Object> properties) {\n\t\tconfigDumpFileName = (String) properties.get(CONFIG_DUMP_FILE_PROP_KEY);\n\t}\n\n\t@Override\n\tpublic int size() {\n\t\tint result = 0;\n\n\t\tfor (Set<ConfigItem> items : config.values()) {\n\t\t\tresult += items.size();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic void store() throws TigaseDBException {\n\t\tif (!isOff(configDumpFileName)) {\n\t\t\tlog.log(Level.WARNING, \"Dumping server configuration to: {0}\", configDumpFileName);\n\t\t\ttry {\n\t\t\t\tFileWriter fw = new FileWriter(configDumpFileName, false);\n\n\t\t\t\tfor (Map.Entry<String, Set<ConfigItem>> entry : config.entrySet()) {\n\t\t\t\t\tfor (ConfigItem item : entry.getValue()) {\n\t\t\t\t\t\tfw.write(item.toPropertyString());\n\t\t\t\t\t\tfw.write(\"\\n\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfw.close();\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.WARNING, \"Cannot dump server configuration.\", e);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"Dumping server configuration is OFF: {0}\", configDumpFileName);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String validateItem(ConfigItem item) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void setAutoloadTimer(long delay) {\n\t}\n\n\tprivate boolean isOff(String str) {\n\t\treturn (str == null) || str.trim().isEmpty() || str.equalsIgnoreCase(\"off\") || str.equalsIgnoreCase(\"none\") ||\n\t\t\t\tstr.equalsIgnoreCase(\"false\") || str.equalsIgnoreCase(\"no\");\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/conf/ConfigurationException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\nimport java.io.IOException;\n\n/**\n * @author kobit\n */\npublic class ConfigurationException\n\t\textends IOException {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * Creates a new instance of <code>ConfigurationException</code> without detail message.\n\t */\n\tpublic ConfigurationException() {\n\t\tsuper();\n\t}\n\n\n\tpublic ConfigurationException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic ConfigurationException(String string, Exception e) {\n\t\tsuper(string, e);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/conf/Configurator.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Dec 7, 2009 4:09:52 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class Configurator\n\t\textends ConfiguratorAbstract {\n\n\tprivate static final Logger log = Logger.getLogger(Configurator.class.getCanonicalName());\n\n\t@Override\n\tpublic String getDiscoDescription() {\n\t\treturn \"Configuration management\";\n\t}\n\n\t@Override\n\tpublic String getDiscoCategoryType() {\n\t\treturn \"generic\";\n\t}\n\n\t@Override\n\tpublic void componentAdded(Configurable component) throws ConfigurationException {\n\t\ttry {\n\t\t\tsuper.componentAdded(component);\n\t\t} catch (NullPointerException ex) {\n\t\t\tlog.log(Level.WARNING, \"ignoring NPE\", ex);\n\t\t}\n\t}\n\n\tpublic void updateMessageRouter() {\n\t\ttry {\n\t\t\tsetup(getComponent(\"message-router\"));\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Problem instantiating component:\", e);\n\t\t}     // end of try-catch\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/conf/ConfiguratorAbstract.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.*;\nimport tigase.db.comp.ComponentRepository;\nimport tigase.db.comp.RepositoryChangeListenerIfc;\nimport tigase.io.TLSUtil;\nimport tigase.server.AbstractComponentRegistrator;\nimport tigase.server.ServerComponent;\nimport tigase.stats.StatisticsContainer;\nimport tigase.stats.StatisticsList;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.util.log.LogFormatter;\nimport tigase.util.repository.DataTypes;\nimport tigase.xmpp.jid.BareJID;\n\nimport javax.script.Bindings;\nimport java.io.*;\nimport java.util.*;\nimport java.util.Map.Entry;\nimport java.util.logging.Level;\nimport java.util.logging.LogManager;\nimport java.util.logging.Logger;\n\nimport static tigase.io.SSLContextContainerIfc.*;\n\n/**\n * Created: Dec 7, 2009 4:15:31 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic abstract class ConfiguratorAbstract\n\t\textends AbstractComponentRegistrator<Configurable>\n\t\timplements RepositoryChangeListenerIfc<ConfigItem>, StatisticsContainer {\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String AUTH_DOMAIN_POOL_CLASS_PROP_KEY = RepositoryFactory.AUTH_DOMAIN_POOL_CLASS_PROP_KEY;\n\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String AUTH_DOMAIN_POOL_CLASS_PROP_VAL = RepositoryFactory.AUTH_DOMAIN_POOL_CLASS_PROP_VAL;\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String CONFIG_REPO_CLASS_INIT_KEY = \"--tigase-config-repo-class\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String CONFIG_REPO_CLASS_PROP_KEY = \"tigase-config-repo-class\";\n\n\tpublic static final String INIT_PROPERTIES_MAP_BIND = \"initProperties\";\n\tpublic static final String PROPERTY_FILENAME_PROP_KEY = \"--property-file\";\n\tpublic static final String PROPERTY_FILENAME_PROP_DEF = \"etc/init.properties\";\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String USER_DOMAIN_POOL_CLASS_PROP_KEY = RepositoryFactory.USER_DOMAIN_POOL_CLASS_PROP_KEY;\n\t/**\n\t * @deprecated moved to RepositoryFactory\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String USER_DOMAIN_POOL_CLASS_PROP_VAL = RepositoryFactory.USER_DOMAIN_POOL_CLASS_PROP_VAL;\n\tpublic static final String LOGGING_KEY = \"logging/\";\n\tprivate static final Logger log = Logger.getLogger(ConfiguratorAbstract.class.getName());\n\tpublic static String logManagerConfiguration = null;\n\tprivate static MonitoringSetupIfc monitoring = null;\n\n\tprivate AuthRepositoryMDImpl auth_repo_impl = null;\n\tprivate Map<String, String> auth_repo_params = null;\n\tprivate AuthRepository auth_repository = null;\n\t// Default user auth repository instance which can be shared among components\n\tprivate ConfigRepositoryIfc configRepo = new ConfigurationCache();\n\t/**\n\t * Properties from the command line parameters and initRepository.properties file or any other source which are used\n\t * to generate default configuration. All the settings starting with '--'\n\t */\n\tprivate Map<String, Object> initProperties = new LinkedHashMap<String, Object>(100);\n\t/**\n\t * Configuration settings read from the initRepository.properties file or any other source which provides startup\n\t * configuration.\n\t */\n\tprivate List<String> initSettings = new LinkedList<String>();\n\t// Common logging setup\n\tprivate Map<String, String> loggingSetup = new LinkedHashMap<String, String>(10);\n\tprivate boolean setup_in_progress = false;\n\tprivate UserRepositoryMDImpl user_repo_impl = null;\n\tprivate Map<String, String> user_repo_params = null;\n\t// Default user repository instance which can be shared among components\n\tprivate UserRepository user_repository = null;\n\n\tpublic static Object getMXBean(String objName) {\n\t\tif (monitoring != null) {\n\t\t\treturn monitoring.getMXBean(objName);\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic static void loadFromPropertiesFiles(String property_filenames, Map<String, Object> initProperties,\n\t\t\t\t\t\t\t\t\t\t\t   List<String> initSettings) {\n\t\tif (property_filenames != null) {\n\t\t\tString[] prop_files = property_filenames.split(\",\");\n\n\t\t\tif (prop_files.length == 1) {\n\t\t\t\tFile f = new File(prop_files[0]);\n\t\t\t\tif (!f.exists()) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Provided property file {0} does NOT EXISTS! Using default one {1}\",\n\t\t\t\t\t\t\tnew String[]{f.getAbsolutePath(), PROPERTY_FILENAME_PROP_DEF});\n\t\t\t\t\tprop_files[0] = PROPERTY_FILENAME_PROP_DEF;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (String property_filename : prop_files) {\n\t\t\t\tlog.log(Level.CONFIG, \"Loading initial properties from property file: {0}\", property_filename);\n\t\t\t\ttry (FileReader fileReader = new FileReader(property_filename)) {\n\t\t\t\t\tProperties defProps = new Properties();\n\n\t\t\t\t\tdefProps.load(fileReader);\n\n\t\t\t\t\tSet<String> prop_keys = defProps.stringPropertyNames();\n\n\t\t\t\t\tfor (String key : prop_keys) {\n\t\t\t\t\t\tString value = defProps.getProperty(key).trim();\n\n\t\t\t\t\t\tif (key.startsWith(\"-\") || key.equals(\"config-type\")) {\n\t\t\t\t\t\t\tif (GEN_TEST.equalsIgnoreCase(key)) {\n\t\t\t\t\t\t\t\tinitProperties.put(key.trim().substring(2), DataTypes.parseBool(value));\n\t\t\t\t\t\t\t\tinitProperties.put(key.trim(), DataTypes.parseBool(value));\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tinitProperties.put(key.trim(), value);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// defProperties.remove(key);\n\t\t\t\t\t\t\tlog.log(Level.CONFIG, \"Added default config parameter: ({0}={1})\",\n\t\t\t\t\t\t\t\t\tnew Object[]{key, value});\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tinitSettings.add(key + \"=\" + value);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (FileNotFoundException e) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Given property file was not found: {0}\", property_filename);\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Can not read property file: \" + property_filename, e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Set all parameters starting with '--' as a system properties with removed\n\t\t// the starting '-' characters.\n\t\tfor (Map.Entry<String, Object> entry : initProperties.entrySet()) {\n\t\t\tif (entry.getKey().startsWith(\"--\")) {\n\t\t\t\tSystem.setProperty(entry.getKey().substring(2),\n\t\t\t\t\t\t\t\t   ((entry.getValue() == null) ? null : entry.getValue().toString()));\n\n\t\t\t\t// In cluster mode we switch DB cache off as this does not play well.\n\t\t\t\tif (CLUSTER_MODE.equals(entry.getKey())) {\n\t\t\t\t\tif (\"true\".equalsIgnoreCase(entry.getValue().toString())) {\n\t\t\t\t\t\tSystem.setProperty(\"tigase.cache\", \"false\");\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Tigase cache turned off\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static void loadFromPropertiesFiles(Map<String, Object> initProperties, List<String> initSettings) {\n\t\tString property_filenames = (String) initProperties.get(PROPERTY_FILENAME_PROP_KEY);\n\n\t\t// if no property file was specified then use default one.\n\t\tif (property_filenames == null) {\n\t\t\tproperty_filenames = PROPERTY_FILENAME_PROP_DEF;\n\t\t\tlog.log(Level.WARNING, \"No property file not specified! Using default one {0}\", property_filenames);\n\t\t}\n\n\t\tloadFromPropertiesFiles(property_filenames, initProperties, initSettings);\n\t}\n\n\tpublic static void loadLogManagerConfig(String config) {\n\t\tlogManagerConfiguration = config;\n\t\ttry {\n\t\t\tfinal ByteArrayInputStream bis = new ByteArrayInputStream(config.getBytes());\n\n\t\t\tLogManager.getLogManager().readConfiguration(bis);\n\t\t\tbis.close();\n\t\t} catch (IOException e) {\n\t\t\tlog.log(Level.SEVERE, \"Can not configure logManager\", e);\n\t\t}    // end of try-catch\n\t}\n\n\tpublic static void parseArgs(Map<String, Object> initProperties, List<String> initSettings, String[] args) {\n\t\tinitProperties.put(GEN_TEST, Boolean.FALSE);\n\t\tinitProperties.put(\"config-type\", GEN_CONFIG_DEF);\n\t\tif ((args != null) && (args.length > 0)) {\n\t\t\tfor (int i = 0; i < args.length; i++) {\n\t\t\t\tString key = null;\n\t\t\t\tObject val = null;\n\n\t\t\t\tif (args[i].startsWith(GEN_CONFIG)) {\n\t\t\t\t\tkey = \"config-type\";\n\t\t\t\t\tval = args[i];\n\t\t\t\t}\n\t\t\t\tif (args[i].startsWith(GEN_TEST)) {\n\t\t\t\t\tkey = args[i];\n\t\t\t\t\tval = Boolean.TRUE;\n\t\t\t\t}\n\t\t\t\tif ((key == null) && args[i].startsWith(\"-\") && !args[i].startsWith(GEN_CONFIG)) {\n\t\t\t\t\tkey = args[i];\n\t\t\t\t\tval = args[++i];\n\t\t\t\t}\n\t\t\t\tif ((key != null) && (val != null)) {\n\t\t\t\t\tinitProperties.put(key, val);\n\n\t\t\t\t\t// System.out.println(\"Setting defaults: \" + key + \"=\" +\n\t\t\t\t\t// val.toString());\n\t\t\t\t\tlog.log(Level.CONFIG, \"Setting defaults: {0} = {1}\", new Object[]{key, val.toString()});\n\t\t\t\t}    // end of if (key != null)\n\t\t\t}      // end of for (int i = 0; i < args.length; i++)\n\t\t}\n\t}\n\n\tpublic static void putMXBean(String objName, Object bean) {\n\t\tif (monitoring != null) {\n\t\t\tmonitoring.putMXBean(objName, bean);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void componentAdded(Configurable component) throws ConfigurationException {\n\t\tif (log.isLoggable(Level.CONFIG)) {\n\t\t\tlog.log(Level.CONFIG, \" component: {0}\", component.getName());\n\t\t}\n\t\tsetup(component);\n\t}\n\n\t@Override\n\tpublic void componentRemoved(Configurable component) {\n\t}\n\n\tpublic void init(String[] args) throws ConfigurationException, TigaseDBException {\n\t\tparseArgs(initProperties, initSettings, args);\n\n\t\tString stringprep = (String) initProperties.get(STRINGPREP_PROCESSOR);\n\n\t\tif (stringprep != null) {\n\t\t\tBareJID.useStringprepProcessor(stringprep);\n\t\t}\n\n\t\tString cnf_class_name = System.getProperty(CONFIG_REPO_CLASS_PROP_KEY);\n\n\t\tif (cnf_class_name != null) {\n\t\t\tinitProperties.put(CONFIG_REPO_CLASS_INIT_KEY, cnf_class_name);\n\t\t}\n\t\tcnf_class_name = (String) initProperties.get(CONFIG_REPO_CLASS_INIT_KEY);\n\t\tif (cnf_class_name != null) {\n\t\t\ttry {\n\t\t\t\tconfigRepo = (ConfigRepositoryIfc) Class.forName(cnf_class_name).newInstance();\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.SEVERE, \"Problem initializing configuration system: \", e);\n\t\t\t\tlog.log(Level.SEVERE, \"Please check settings, and rerun the server.\");\n\t\t\t\tlog.log(Level.SEVERE, \"Server is stopping now.\");\n\t\t\t\tSystem.err.println(\"Problem initializing configuration system: \" + e);\n\t\t\t\tSystem.err.println(\"Please check settings, and rerun the server.\");\n\t\t\t\tSystem.err.println(\"Server is stopping now.\");\n\t\t\t\tSystem.exit(1);\n\t\t\t}\n\t\t}\n\t\tconfigRepo.addRepoChangeListener(this);\n\t\tString host = getDefHostName() != null\n\t\t\t\t\t  ? getDefHostName().getDomain()\n\t\t\t\t\t  : DNSResolverFactory.getInstance().getDefaultHost();\n\t\tconfigRepo.setDefHostname(host);\n\t\ttry {\n\t\t\t// loss of generic types is intentional to make parameter match API\n\t\t\t// and internally all requests are done like:\n\t\t\t// String x = (String) initProperties.get(\"param\");\n\t\t\t// so it should be safe to loss generic types of Map\n\t\t\tconfigRepo.initRepository(null, (Map) initProperties);\n\t\t} catch (DBInitException ex) {\n\t\t\tthrow new ConfigurationException(ex.getMessage(), ex);\n\t\t}\n\t\tfor (String prop : initSettings) {\n\t\t\tConfigItem item = configRepo.getItemInstance();\n\n\t\t\titem.initFromPropertyString(prop);\n\t\t\tconfigRepo.addItem(item);\n\t\t}\n\n\t\tMap<String, Object> repoInitProps = configRepo.getInitProperties();\n\n\t\tif (repoInitProps != null) {\n\t\t\tinitProperties.putAll(repoInitProps);\n\t\t}\n\n\t\t// Not sure if this is the correct pleace to initialize monitoring\n\t\t// maybe it should be initialized initRepository initializationCompleted but\n\t\t// Then some stuff might be missing. Let's try to do it here for now\n\t\t// and maybe change it later.\n\t\tString property_filenames = (String) initProperties.get(PROPERTY_FILENAME_PROP_KEY);\n\n\t\tif (property_filenames != null) {\n\t\t\tString[] prop_files = property_filenames.split(\",\");\n\n\t\t\tinitMonitoring((String) initProperties.get(MONITORING), new File(prop_files[0]).getParent());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void initBindings(Bindings binds) {\n\t\tsuper.initBindings(binds);\n\t\tbinds.put(ComponentRepository.COMP_REPO_BIND, configRepo);\n\t\tbinds.put(INIT_PROPERTIES_MAP_BIND, initProperties);\n\t}\n\n\t@Override\n\tpublic void initializationCompleted() {\n\t\tif (isInitializationComplete()) {\n\n\t\t\t// Do we really need to do this again?\n\t\t\treturn;\n\t\t}\n\t\tsuper.initializationCompleted();\n\t\tif (monitoring != null) {\n\t\t\tmonitoring.initializationCompleted();\n\t\t}\n\t\ttry {\n\n\t\t\t// Dump the configuration....\n\t\t\tconfigRepo.store();\n\t\t} catch (TigaseDBException ex) {\n\t\t\tlog.log(Level.WARNING, \"Cannot store configuration.\", ex);\n\t\t}\n\n\t\tSystem.out.println(\n\t\t\t\t\"== \" + new Date() + \" Server finished starting up and (if there wasn't any error) is ready to use\\n\");\n\n\t}\n\n\t@Override\n\tpublic void itemAdded(ConfigItem item) {\n\n\t\t// Ignored, adding configuration settings does not make sense, for now...\n\t\t// right now, just print a log message\n\t\t// log.log(Level.INFO, \"Adding configuration item not supported yet: {0}\", item);\n\t}\n\n\t@Override\n\tpublic void itemRemoved(ConfigItem item) {\n\n\t\t// Ignored, removing configuration settings does not make sense, for now...\n\t\t// right now, just print a log message\n\t\tlog.log(Level.CONFIG, \"Removing configuration item not supported yet: {0}\", item);\n\t}\n\n\t@Override\n\tpublic void itemUpdated(ConfigItem item) {\n\t\tlog.log(Level.CONFIG, \"Updating configuration item: {0}\", item);\n\n\t\tConfigurable component = getComponent(item.getCompName());\n\n\t\tif (component != null) {\n\t\t\tMap<String, Object> prop = new HashMap<>();\n\t\t\tprop.put(item.getConfigKey(), item.getConfigVal());\n\n\t\t\ttry {\n\t\t\t\tcomponent.setProperties(prop);\n\t\t\t} catch (ConfigurationException ex) {\n\t\t\t\tlog.log(Level.SEVERE, \"Component reconfiguration failed: \" + ex.getMessage(), ex);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"Cannot find component for configuration item: {0}\", item);\n\t\t}\n\t}\n\n\tpublic void parseArgs(final String[] args) {\n\t\tparseArgs(initProperties, initSettings, args);\n\t}\n\n\tpublic void putProperties(String compId, Map<String, Object> props) throws ConfigurationException {\n\t\tconfigRepo.putProperties(compId, props);\n\t}\n\n\tpublic void setup(Configurable component) throws ConfigurationException {\n\n\t\t// Try to avoid recursive execution of the method\n\t\tif (component == this) {\n\t\t\tif (setup_in_progress) {\n\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\tsetup_in_progress = true;\n\t\t\t}\n\t\t}\n\n\t\tString compId = component.getName();\n\n\t\tlog.log(Level.CONFIG, \"Setting up component: {0}\", compId);\n\n\t\tMap<String, Object> prop = null;\n\n\t\ttry {\n\t\t\tprop = configRepo.getProperties(compId);\n\t\t} catch (ConfigurationException ex) {\n\t\t\tlog.log(Level.WARNING, \"Propblem retrieving configuration properties for component: \" + compId, ex);\n\n\t\t\treturn;\n\t\t}\n\n\t\tMap<String, Object> defs = component.getDefaults(getDefConfigParams());\n\n\t\tlog.log(Level.CONFIG, \"Component {0} defaults: {1}\", new Object[]{compId, defs});\n\n\t\tSet<Map.Entry<String, Object>> defs_entries = defs.entrySet();\n\t\tboolean modified = false;\n\n\t\tfor (Map.Entry<String, Object> entry : defs_entries) {\n\t\t\tif (!prop.containsKey(entry.getKey())) {\n\t\t\t\tprop.put(entry.getKey(), entry.getValue());\n\t\t\t\tmodified = true;\n\t\t\t}    // end of if ()\n\t\t}      // end of for ()\n\t\tif (modified) {\n\t\t\ttry {\n\t\t\t\tlog.log(Level.CONFIG, \"Component {0} configuration: {1}\", new Object[]{compId, prop});\n\t\t\t\tconfigRepo.putProperties(compId, prop);\n\t\t\t} catch (ConfigurationException ex) {\n\t\t\t\tlog.log(Level.WARNING, \"Propblem with saving configuration properties for component: \" + compId, ex);\n\t\t\t}\n\t\t}    // end of if (modified)\n\t\tprop.put(RepositoryFactory.SHARED_USER_REPO_PROP_KEY, user_repo_impl);\n\t\tprop.put(RepositoryFactory.SHARED_USER_REPO_PARAMS_PROP_KEY, user_repo_params);\n\t\tprop.put(RepositoryFactory.SHARED_AUTH_REPO_PROP_KEY, auth_repo_impl);\n\t\tprop.put(RepositoryFactory.SHARED_AUTH_REPO_PARAMS_PROP_KEY, auth_repo_params);\n\t\tcomponent.setProperties(prop);\n\t\tif (component == this) {\n\t\t\tsetup_in_progress = false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getDefaults(Map<String, Object> params) {\n\t\tMap<String, Object> defaults = super.getDefaults(params);\n\t\tString levelStr = \".level\";\n\n\t\tif ((Boolean) params.get(GEN_TEST)) {\n\t\t\tdefaults.put(LOGGING_KEY + levelStr, \"WARNING\");\n\t\t} else {\n\t\t\tdefaults.put(LOGGING_KEY + levelStr, \"CONFIG\");\n\t\t}\n\t\tdefaults.put(LOGGING_KEY + \"handlers\", \"java.util.logging.ConsoleHandler java.util.logging.FileHandler\");\n\t\tdefaults.put(LOGGING_KEY + \"java.util.logging.ConsoleHandler.formatter\", LogFormatter.class.getName());\n\t\tdefaults.put(LOGGING_KEY + \"java.util.logging.ConsoleHandler.level\", \"INFO\");\n\t\tdefaults.put(LOGGING_KEY + \"java.util.logging.FileHandler.append\", \"true\");\n\t\tdefaults.put(LOGGING_KEY + \"java.util.logging.FileHandler.count\", \"5\");\n\t\tdefaults.put(LOGGING_KEY + \"java.util.logging.FileHandler.formatter\", LogFormatter.class.getName());\n\t\tdefaults.put(LOGGING_KEY + \"java.util.logging.FileHandler.limit\", \"10000000\");\n\t\tdefaults.put(LOGGING_KEY + \"java.util.logging.FileHandler.pattern\", \"logs/tigase.log\");\n\t\tdefaults.put(LOGGING_KEY + \"tigase.useParentHandlers\", \"true\");\n\t\tdefaults.put(LOGGING_KEY + \"java.util.logging.FileHandler.level\", \"ALL\");\n\t\tif (params.get(GEN_DEBUG) != null) {\n\t\t\tString[] packs = ((String) params.get(GEN_DEBUG)).split(\",\");\n\n\t\t\tfor (String pack : packs) {\n\t\t\t\tdefaults.put(LOGGING_KEY + \"tigase.\" + pack + \".level\", \"ALL\");\n\t\t\t}    // end of for (String pack: packs)\n\t\t}\n\t\tif (params.get(GEN_DEBUG_PACKAGES) != null) {\n\t\t\tString[] packs = ((String) params.get(GEN_DEBUG_PACKAGES)).split(\",\");\n\n\t\t\tfor (String pack : packs) {\n\t\t\t\tdefaults.put(LOGGING_KEY + pack + \".level\", \"ALL\");\n\t\t\t}    // end of for (String pack: packs)\n\t\t}\n\n\t\tString repo_pool = null;\n\n\t\trepo_pool = (String) params.get(RepositoryFactory.USER_DOMAIN_POOL_CLASS);\n\t\tif (repo_pool == null) {\n\t\t\trepo_pool = RepositoryFactory.USER_DOMAIN_POOL_CLASS_PROP_VAL;\n\t\t}\n\t\tdefaults.put(RepositoryFactory.USER_DOMAIN_POOL_CLASS_PROP_KEY, repo_pool);\n\t\trepo_pool = (String) params.get(RepositoryFactory.AUTH_DOMAIN_POOL_CLASS);\n\t\tif (repo_pool == null) {\n\t\t\trepo_pool = RepositoryFactory.AUTH_DOMAIN_POOL_CLASS_PROP_VAL;\n\t\t}\n\t\tdefaults.put(RepositoryFactory.AUTH_DOMAIN_POOL_CLASS_PROP_KEY, repo_pool);\n\n\t\tString user_repo_class = null;//RepositoryFactory.DUMMY_REPO_CLASS_PROP_VAL;\n\t\tString user_repo_url = RepositoryFactory.DERBY_REPO_URL_PROP_VAL;\n\t\tString auth_repo_class = null;//RepositoryFactory.DUMMY_REPO_CLASS_PROP_VAL;\n\t\tString auth_repo_url = RepositoryFactory.DERBY_REPO_URL_PROP_VAL;\n\n\t\tif (params.get(RepositoryFactory.GEN_USER_DB) != null) {\n\t\t\tuser_repo_class = (String) params.get(RepositoryFactory.GEN_USER_DB);\n\t\t\tauth_repo_class = RepositoryFactory.TIGASE_CUSTOM_AUTH_REPO_CLASS_PROP_VAL;\n\t\t}\n\t\tif (params.get(RepositoryFactory.GEN_USER_DB_URI) != null) {\n\t\t\tuser_repo_url = (String) params.get(RepositoryFactory.GEN_USER_DB_URI);\n\t\t\tauth_repo_url = user_repo_url;\n\t\t}\n\t\tif (params.get(RepositoryFactory.GEN_AUTH_DB) != null) {\n\t\t\tauth_repo_class = (String) params.get(RepositoryFactory.GEN_AUTH_DB);\n\t\t}\n\t\tif (params.get(RepositoryFactory.GEN_AUTH_DB_URI) != null) {\n\t\t\tauth_repo_url = (String) params.get(RepositoryFactory.GEN_AUTH_DB_URI);\n\t\t}\n\t\tif (params.get(RepositoryFactory.USER_REPO_POOL_SIZE) != null) {\n\t\t\tdefaults.put(RepositoryFactory.USER_REPO_POOL_SIZE_PROP_KEY,\n\t\t\t\t\t\t params.get(RepositoryFactory.USER_REPO_POOL_SIZE));\n\t\t} else {\n\t\t\tdefaults.put(RepositoryFactory.USER_REPO_POOL_SIZE_PROP_KEY,\n\t\t\t\t\t\t RepositoryFactory.USER_REPO_POOL_SIZE_PROP_VAL);\n\t\t}\n\t\tif (params.get(RepositoryFactory.DATA_REPO_POOL_SIZE) != null) {\n\t\t\tdefaults.put(RepositoryFactory.DATA_REPO_POOL_SIZE_PROP_KEY,\n\t\t\t\t\t\t params.get(RepositoryFactory.DATA_REPO_POOL_SIZE));\n\t\t} else if (params.get(RepositoryFactory.USER_REPO_POOL_SIZE) != null) {\n\t\t\tdefaults.put(RepositoryFactory.DATA_REPO_POOL_SIZE_PROP_KEY,\n\t\t\t\t\t\t params.get(RepositoryFactory.USER_REPO_POOL_SIZE));\n\t\t} else {\n\t\t\tdefaults.put(RepositoryFactory.DATA_REPO_POOL_SIZE_PROP_KEY,\n\t\t\t\t\t\t RepositoryFactory.DATA_REPO_POOL_SIZE_PROP_VAL);\n\t\t}\n\t\tif (params.get(RepositoryFactory.AUTH_REPO_POOL_SIZE) != null) {\n\t\t\tdefaults.put(RepositoryFactory.AUTH_REPO_POOL_SIZE_PROP_KEY,\n\t\t\t\t\t\t params.get(RepositoryFactory.AUTH_REPO_POOL_SIZE));\n\t\t} else if (params.get(RepositoryFactory.DATA_REPO_POOL_SIZE) != null) {\n\t\t\tdefaults.put(RepositoryFactory.AUTH_REPO_POOL_SIZE_PROP_KEY,\n\t\t\t\t\t\t params.get(RepositoryFactory.DATA_REPO_POOL_SIZE));\n\t\t} else if (params.get(RepositoryFactory.USER_REPO_POOL_SIZE) != null) {\n\t\t\tdefaults.put(RepositoryFactory.AUTH_REPO_POOL_SIZE_PROP_KEY,\n\t\t\t\t\t\t params.get(RepositoryFactory.USER_REPO_POOL_SIZE));\n\t\t} else {\n\t\t\tdefaults.put(RepositoryFactory.DATA_REPO_POOL_SIZE_PROP_KEY,\n\t\t\t\t\t\t RepositoryFactory.AUTH_REPO_POOL_SIZE_PROP_VAL);\n\t\t}\n\t\tif (user_repo_class != null) {\n\t\t\tdefaults.put(RepositoryFactory.USER_REPO_CLASS_PROP_KEY, user_repo_class);\n\t\t}\n\t\tdefaults.put(RepositoryFactory.USER_REPO_URL_PROP_KEY, user_repo_url);\n\t\tif (auth_repo_class != null) {\n\t\t\tdefaults.put(RepositoryFactory.AUTH_REPO_CLASS_PROP_KEY, auth_repo_class);\n\t\t}\n\t\tdefaults.put(RepositoryFactory.AUTH_REPO_URL_PROP_KEY, auth_repo_url);\n\n\t\tList<String> user_repo_domains = new ArrayList<String>(10);\n\t\tList<String> auth_repo_domains = new ArrayList<String>(10);\n\n\t\tfor (Map.Entry<String, Object> entry : params.entrySet()) {\n\t\t\tif (entry.getKey().startsWith(RepositoryFactory.GEN_USER_DB_URI)) {\n\t\t\t\tString[] domains = parseUserRepoParams(entry, params, user_repo_class, defaults);\n\n\t\t\t\tif (domains != null) {\n\t\t\t\t\tuser_repo_domains.addAll(Arrays.asList(domains));\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (entry.getKey().startsWith(RepositoryFactory.GEN_AUTH_DB_URI)) {\n\t\t\t\tString[] domains = parseAuthRepoParams(entry, params, auth_repo_class, defaults);\n\n\t\t\t\tif (domains != null) {\n\t\t\t\t\tauth_repo_domains.addAll(Arrays.asList(domains));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (user_repo_domains.size() > 0) {\n\t\t\tdefaults.put(RepositoryFactory.USER_REPO_DOMAINS_PROP_KEY,\n\t\t\t\t\t\t user_repo_domains.toArray(new String[user_repo_domains.size()]));\n\t\t}\n\t\tif (auth_repo_domains.size() > 0) {\n\t\t\tdefaults.put(RepositoryFactory.AUTH_REPO_DOMAINS_PROP_KEY,\n\t\t\t\t\t\t auth_repo_domains.toArray(new String[auth_repo_domains.size()]));\n\t\t}\n\n\t\t// TLS/SSL configuration\n\t\tif (params.get(\"--\" + SSL_CONTAINER_CLASS_KEY) != null) {\n\t\t\tdefaults.put(SSL_CONTAINER_CLASS_KEY, (String) params.get(\"--\" + SSL_CONTAINER_CLASS_KEY));\n\t\t} else {\n\t\t\tdefaults.put(SSL_CONTAINER_CLASS_KEY, SSL_CONTAINER_CLASS_VAL);\n\t\t}\n\t\tif (params.get(\"--\" + SERVER_CERTS_LOCATION_KEY) != null) {\n\t\t\tdefaults.put(SERVER_CERTS_LOCATION_KEY, (String) params.get(\"--\" + SERVER_CERTS_LOCATION_KEY));\n\t\t} else {\n\t\t\tdefaults.put(SERVER_CERTS_LOCATION_KEY, SERVER_CERTS_LOCATION_VAL);\n\t\t}\n\t\tif (params.get(\"--\" + DEFAULT_DOMAIN_CERT_KEY) != null) {\n\t\t\tdefaults.put(DEFAULT_DOMAIN_CERT_KEY, (String) params.get(\"--\" + DEFAULT_DOMAIN_CERT_KEY));\n\t\t} else {\n\t\t\tdefaults.put(DEFAULT_DOMAIN_CERT_KEY, DEFAULT_DOMAIN_CERT_VAL);\n\t\t}\n\t\tconfigRepo.getDefaults(defaults, params);\n\n\t\treturn defaults;\n\t}\n\n\tpublic Map<String, Object> getDefConfigParams() {\n\t\treturn initProperties;\n\t}\n\n\tpublic String getMessageRouterClassName() {\n\t\treturn \"tigase.server.MessageRouter\";\n\t}\n\n\tpublic Map<String, Object> getProperties(String nodeId) throws ConfigurationException {\n\t\treturn configRepo.getProperties(nodeId);\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t\tsuper.getStatistics(list);\n\t}\n\n\t@Override\n\tpublic boolean isCorrectType(ServerComponent component) {\n\t\treturn component instanceof Configurable;\n\t}\n\n\t@Override\n\tpublic void setProperties(Map<String, Object> props) throws ConfigurationException {\n\t\tif (props.size() == 0) {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Properties size is 0, incorrect system state, probably OSGI mode and configuration is not yet loaded.\");\n\n\t\t\treturn;\n\t\t} else {\n\t\t\tlog.log(Level.CONFIG, \"Properties size is {0}, and here are all propeties: {1}\",\n\t\t\t\t\tnew Object[]{props.size(), props});\n\t\t}\n\t\tsetupLogManager(props);\n\t\tsuper.setProperties(props);\n\t\tif (props.size() == 1) {\n\t\t\tlog.log(Level.CONFIG, \"Propeties size is {0}, and here are all propeties: {1}\",\n\t\t\t\t\tnew Object[]{props.size(), props});\n\n\t\t\treturn;\n\t\t}\n\t\tconfigRepo.setProperties(props);\n\t\tTLSUtil.configureSSLContext(props);\n\n\t\tString[] user_repo_domains = (String[]) props.get(RepositoryFactory.USER_REPO_DOMAINS_PROP_KEY);\n\t\tString[] auth_repo_domains = (String[]) props.get(RepositoryFactory.AUTH_REPO_DOMAINS_PROP_KEY);\n\t\tString authRepoMDImpl = (String) props.get(RepositoryFactory.AUTH_DOMAIN_POOL_CLASS_PROP_KEY);\n\t\tString userRepoMDImpl = (String) props.get(RepositoryFactory.USER_DOMAIN_POOL_CLASS_PROP_KEY);\n\n\t\ttry {\n\n\t\t\t// Authentication multi-domain repository pool initialization\n\t\t\tMap<String, String> params = getRepoParams(props, RepositoryFactory.AUTH_REPO_PARAMS_NODE, null);\n\t\t\tString conn_url = (String) props.get(RepositoryFactory.AUTH_REPO_URL_PROP_KEY);\n\n\t\t\tauth_repo_impl = (AuthRepositoryMDImpl) Class.forName(authRepoMDImpl).newInstance();\n\t\t\tauth_repo_impl.initRepository(conn_url, params);\n\n\t\t\t// User multi-domain repository pool initialization\n\t\t\tparams = getRepoParams(props, RepositoryFactory.USER_REPO_PARAMS_NODE, null);\n\t\t\tconn_url = (String) props.get(RepositoryFactory.USER_REPO_URL_PROP_KEY);\n\t\t\tuser_repo_impl = (UserRepositoryMDImpl) Class.forName(userRepoMDImpl).newInstance();\n\t\t\tuser_repo_impl.initRepository(conn_url, params);\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.SEVERE, \"An error initializing domain repository pool: \", ex);\n\t\t}\n\t\tuser_repository = null;\n\t\tauth_repository = null;\n\t\tif (user_repo_domains != null) {\n\t\t\tfor (String domain : user_repo_domains) {\n\t\t\t\ttry {\n\t\t\t\t\taddUserRepo(props, domain);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.log(Level.SEVERE, \"Can''t initialize user repository for domain: \" + domain, e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (user_repository == null) {\n\t\t\ttry {\n\t\t\t\taddUserRepo(props, null);\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.SEVERE, \"Can''t initialize user default repository: \", e);\n\t\t\t}\n\t\t}\n\t\tif (auth_repo_domains != null) {\n\t\t\tfor (String domain : auth_repo_domains) {\n\t\t\t\ttry {\n\t\t\t\t\taddAuthRepo(props, domain);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.log(Level.SEVERE, \"Can''t initialize user repository for domain: \" + domain, e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (auth_repository == null) {\n\t\t\ttry {\n\t\t\t\taddAuthRepo(props, null);\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.SEVERE, \"Can''t initialize auth default repository: \", e);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void addAuthRepo(Map<String, Object> props, String domain)\n\t\t\tthrows DBInitException, ClassNotFoundException, InstantiationException, IllegalAccessException {\n\t\tMap<String, String> params = getRepoParams(props, RepositoryFactory.AUTH_REPO_PARAMS_NODE, domain);\n\t\tString cls_name = (String) props.get(\n\t\t\t\tRepositoryFactory.AUTH_REPO_CLASS_PROP_KEY + ((domain == null) ? \"\" : \"/\" + domain));\n\t\tString conn_url = (String) props.get(\n\t\t\t\tRepositoryFactory.AUTH_REPO_URL_PROP_KEY + ((domain == null) ? \"\" : \"/\" + domain));\n\n\t\tAuthRepository repo = RepositoryFactory.getAuthRepository(cls_name, conn_url, params);\n\n\t\tif ((domain == null) || domain.trim().isEmpty()) {\n\t\t\tauth_repo_impl.addRepo(\"\", repo);\n\t\t\tauth_repo_impl.setDefault(repo);\n\t\t\tauth_repository = repo;\n\t\t} else {\n\t\t\tauth_repo_impl.addRepo(domain, repo);\n\t\t}\n\t\tlog.log(Level.CONFIG, \"[{0}] Initialized {1} as user auth repository pool, url: {3}\",\n\t\t\t\tnew Object[]{((domain != null) ? domain : \"DEFAULT\"), cls_name, conn_url});\n\t}\n\n\tprivate void addUserRepo(Map<String, Object> props, String domain)\n\t\t\tthrows DBInitException, ClassNotFoundException, InstantiationException, IllegalAccessException {\n\t\tMap<String, String> params = getRepoParams(props, RepositoryFactory.USER_REPO_PARAMS_NODE, domain);\n\t\tString cls_name = (String) props.get(\n\t\t\t\tRepositoryFactory.USER_REPO_CLASS_PROP_KEY + ((domain == null) ? \"\" : \"/\" + domain));\n\t\tString conn_url = (String) props.get(\n\t\t\t\tRepositoryFactory.USER_REPO_URL_PROP_KEY + ((domain == null) ? \"\" : \"/\" + domain));\n\n\t\tUserRepository repo = RepositoryFactory.getUserRepository(cls_name, conn_url, params);\n\n\t\tif ((domain == null) || domain.trim().isEmpty()) {\n\t\t\tuser_repo_impl.addRepo(\"\", repo);\n\t\t\tuser_repo_impl.setDefault(repo);\n\t\t\tuser_repository = repo;\n\t\t} else {\n\t\t\tuser_repo_impl.addRepo(domain, repo);\n\t\t}\n\t\tlog.log(Level.CONFIG, \"[{0}] Initialized {1} as user repository pool, url: {2}\",\n\t\t\t\tnew Object[]{((domain != null) ? domain : \"DEFAULT\"), cls_name, conn_url});\n\t}\n\n\tprivate void initMonitoring(String settings, String configDir) {\n\t\tif ((monitoring == null) && (settings != null)) {\n\t\t\ttry {\n\t\t\t\tString mon_cls = \"tigase.management.MonitoringSetup\";\n\n\t\t\t\tmonitoring = (MonitoringSetupIfc) Class.forName(mon_cls).newInstance();\n\t\t\t\tmonitoring.initMonitoring(settings, configDir);\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.WARNING, \"Can not initialize monitoring: \", e);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate String[] parseAuthRepoParams(Entry<String, Object> entry, Map<String, Object> params,\n\t\t\t\t\t\t\t\t\t\t String auth_repo_class, Map<String, Object> defaults) {\n\t\tString key = entry.getKey();\n\t\tint br_open = key.indexOf('[');\n\t\tint br_close = key.indexOf(']');\n\n\t\tif ((br_open < 0) || (br_close < 0)) {\n\n\t\t\t// default database is configured elsewhere\n\t\t\treturn null;\n\t\t}\n\n\t\tString repo_class = auth_repo_class;\n\t\tString options = key.substring(br_open + 1, br_close);\n\t\tString[] domains = options.split(\",\");\n\n\t\tlog.log(Level.CONFIG, \"Found DB domain: {0}\", Arrays.toString(domains));\n\n\t\tString get_user_db = RepositoryFactory.GEN_AUTH_DB + \"[\" + options + \"]\";\n\n\t\tif (params.get(get_user_db) != null) {\n\t\t\trepo_class = (String) params.get(get_user_db);\n\t\t}\n\t\tfor (String domain : domains) {\n\t\t\tdefaults.put(RepositoryFactory.AUTH_REPO_CLASS_PROP_KEY + \"/\" + domain, repo_class);\n\t\t\tlog.log(Level.CONFIG, \"Setting defaults: {0}/{1}={2}\",\n\t\t\t\t\tnew Object[]{RepositoryFactory.AUTH_REPO_CLASS_PROP_KEY, domain, repo_class});\n\t\t\tdefaults.put(RepositoryFactory.AUTH_REPO_URL_PROP_KEY + \"/\" + domain, entry.getValue());\n\t\t\tlog.log(Level.CONFIG, \"Setting defaults: {0}{1}={2}\",\n\t\t\t\t\tnew Object[]{RepositoryFactory.AUTH_REPO_URL_PROP_KEY, domain, entry.getValue()});\n\t\t}\n\n\t\treturn domains;\n\t}\n\n\tprivate String[] parseUserRepoParams(Entry<String, Object> entry, Map<String, Object> params,\n\t\t\t\t\t\t\t\t\t\t String user_repo_class, Map<String, Object> defaults) {\n\t\tString key = entry.getKey();\n\t\tint br_open = key.indexOf('[');\n\t\tint br_close = key.indexOf(']');\n\n\t\tif ((br_open < 0) || (br_close < 0)) {\n\n\t\t\t// default database is configured elsewhere\n\t\t\treturn null;\n\t\t}\n\n\t\tString repo_class = user_repo_class;\n\t\tString options = key.substring(br_open + 1, br_close);\n\t\tString[] domains = options.split(\",\");\n\n\t\tlog.log(Level.CONFIG, \"Found DB domain: {0}\", Arrays.toString(domains));\n\n\t\tString get_user_db = RepositoryFactory.GEN_USER_DB + \"[\" + options + \"]\";\n\n\t\tif (params.get(get_user_db) != null) {\n\t\t\trepo_class = (String) params.get(get_user_db);\n\t\t}\n\t\tfor (String domain : domains) {\n\t\t\tdefaults.put(RepositoryFactory.USER_REPO_CLASS_PROP_KEY + \"/\" + domain, repo_class);\n\t\t\tlog.log(Level.CONFIG, \"Setting defaults: {0}{1}={2}\",\n\t\t\t\t\tnew Object[]{RepositoryFactory.USER_REPO_CLASS_PROP_KEY, domain, repo_class});\n\t\t\tdefaults.put(RepositoryFactory.USER_REPO_URL_PROP_KEY + \"/\" + domain, entry.getValue());\n\t\t\tlog.log(Level.CONFIG, \"Setting defaults: {0}{1}={2}\",\n\t\t\t\t\tnew Object[]{RepositoryFactory.USER_REPO_URL_PROP_KEY, domain, entry.getValue()});\n\t\t}\n\n\t\treturn domains;\n\t}\n\n\tprivate void setupLogManager(Map<String, Object> properties) {\n\t\tSet<Map.Entry<String, Object>> entries = properties.entrySet();\n\t\tStringBuilder buff = new StringBuilder(200);\n\n\t\tfor (Map.Entry<String, Object> entry : entries) {\n\t\t\tif (entry.getKey().startsWith(LOGGING_KEY)) {\n\t\t\t\tString key = entry.getKey().substring(LOGGING_KEY.length());\n\t\t\t\tloggingSetup.put(key, entry.getValue().toString());\n\t\t\t}\n\t\t}\n\n\t\tfor (String key : loggingSetup.keySet()) {\n\t\t\tString entry = loggingSetup.get(key);\n\t\t\tbuff.append(key).append(\"=\").append(entry).append(\"\\n\");\n\t\t\tif (key.equals(\"java.util.logging.FileHandler.pattern\")) {\n\t\t\t\tFile log_path = new File(entry).getParentFile();\n\t\t\t\tif (!log_path.exists()) {\n\t\t\t\t\tlog_path.mkdirs();\n\t\t\t\t}\n\t\t\t}    // end of if (key.equals())\n\t\t}      // end of if (entry.getKey().startsWith(LOGGING_KEY))\n\n\t\t// System.out.println(\"Setting logging: \\n\" + buff.toString());\n\t\tloadLogManagerConfig(buff.toString());\n\t\tlog.config(\"DONE\");\n\t}\n\n\tprivate Map<String, String> getRepoParams(Map<String, Object> props, String repo_type, String domain) {\n\t\tMap<String, String> result = new LinkedHashMap<String, String>(10);\n\t\tString prop_start = repo_type + ((domain == null) ? \"\" : \"/\" + domain);\n\n\t\tfor (Map.Entry<String, Object> entry : props.entrySet()) {\n\t\t\tif (entry.getKey().startsWith(prop_start)) {\n\n\t\t\t\t// Split the key to configuration nodes separated with '/'\n\t\t\t\tString[] nodes = entry.getKey().split(\"/\");\n\n\t\t\t\t// The plugin ID part may contain many IDs separated with comma ','\n\t\t\t\t// We have to make sure that the default repository does not pick up\n\t\t\t\t// properties set for a specific domain\n\t\t\t\tif (((domain == null) && (nodes.length == 2)) || ((domain != null) && (nodes.length == 3))) {\n\n\t\t\t\t\t// if (nodes.length > 1) {\n\t\t\t\t\tresult.put(nodes[nodes.length - 1], entry.getValue().toString());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/conf/ConfiguratorOld.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\nimport tigase.db.RepositoryFactory;\nimport tigase.disco.ServiceEntity;\nimport tigase.disco.ServiceIdentity;\nimport tigase.server.*;\nimport tigase.util.ClassUtil;\nimport tigase.xml.Element;\nimport tigase.xml.XMLUtils;\nimport tigase.xml.db.Types.DataType;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.PacketErrorTypeException;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Class ConfiguratorOld\n * <br>\n * Created: Tue Nov 22 07:07:11 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class ConfiguratorOld\n\t\textends ConfiguratorAbstract {\n\n\tprivate static final Logger log = Logger.getLogger(\"tigase.conf.Configurator\");\n\tprivate static boolean add = false;\n\tprivate static boolean force = true;\n\tprivate static String key = null;\n\tprivate static MonitoringSetupIfc monitoring = null;\n\tprivate static boolean print = false;\n\tprivate static boolean set = false;\n\tprivate static String value = null;\n\n\tprivate String config_file_name = null;\n\tprivate ServiceEntity config_list = null;\n\tprivate ServiceEntity config_set = null;\n\tprivate Map<String, Object> defConfigParams = new LinkedHashMap<String, Object>();\n\tprivate Map<String, Object> defProperties = new LinkedHashMap<String, Object>();\n\tprivate boolean demoMode = false;\n\tprivate ConfigXMLRepository repository = null;\n\t/**\n\t * This variable is used only for configuration at run-time to add new components....\n\t */\n\tprivate String routerCompName = null;\n\tprivate ServiceEntity serviceEntity = null;\n\n\tpublic static Object getMXBean(String objName) {\n\t\tif (monitoring != null) {\n\t\t\treturn monitoring.getMXBean(objName);\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate static String help() {\n\t\treturn \"\\n\" + \"Parameters:\\n\" + \" -h             this help message\\n\" + \" -c file        configuration file\\n\" +\n\t\t\t\t\" -key key       node/key for the value to set\\n\" +\n\t\t\t\t\" -value value   value to set in configuration file\\n\" +\n\t\t\t\t\" -set           set given value for given key\\n\" +\n\t\t\t\t\" -add           add given value to the values list for given key\\n\" +\n\t\t\t\t\" -print         print content of all configuration settings or of given node/key\\n\" +\n\t\t\t\t\" -f             force creation of the new property - dangerous option...\\n\" + \"Samples:\\n\" +\n\t\t\t\t\" Setting admin account - overwriting any previous value(s)\\n\" +\n\t\t\t\t\" $ ./scripts/config.sh -c tigase-config.xml -print -set -key \" + DEF_SM_NAME +\n\t\t\t\t\"/admins -value admin1@localhost\\n\" + \" Adding next admin account leaving old value(s)\\n\" +\n\t\t\t\t\" $ ./scripts/config.sh -c tigase-config.xml -print -add -key \" + DEF_SM_NAME +\n\t\t\t\t\"/admins -value admin2@localhost\\n\" + \"\\n\" +\n\t\t\t\t\"Note: adding -print option is useful always, even with -set or -add\\n\" +\n\t\t\t\t\"      option as it prints set value afterwards.\\n\";\n\t}\n\n\tpublic static void main(final String[] args) throws Exception {\n\t\tforce = false;\n\t\tif ((args != null) && (args.length > 0)) {\n\t\t\tfor (int i = 0; i < args.length; i++) {\n\t\t\t\tif (args[i].equals(\"-h\")) {\n\t\t\t\t\tSystem.out.print(help());\n\t\t\t\t\tSystem.exit(0);\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\t\t\t\tif (args[i].equals(\"-key\")) {\n\t\t\t\t\tkey = args[++i];\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\t\t\t\tif (args[i].equals(\"-value\")) {\n\t\t\t\t\tvalue = args[++i];\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\t\t\t\tif (args[i].equals(\"-set\")) {\n\t\t\t\t\tset = true;\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\t\t\t\tif (args[i].equals(\"-add\")) {\n\t\t\t\t\tadd = true;\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\t\t\t\tif (args[i].equals(\"-print\")) {\n\t\t\t\t\tprint = true;\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\t\t\t\tif (args[i].equals(\"-f\")) {\n\t\t\t\t\tforce = true;\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\t\t\t}      // end of for (int i = 0; i < args.length; i++)\n\t\t}\n\n\t\tConfiguratorOld conf = new ConfiguratorOld();\n\n\t\tconf.init(args);\n\t\tif (set || add) {\n\t\t\tconf.setValue(key, value, add, true, null);\n\t\t}    // end of if (set)\n\t\tif (print) {\n\t\t\tMap<String, Object> allprop = conf.getAllProperties(key);\n\n\t\t\tfor (Map.Entry<String, Object> entry : allprop.entrySet()) {\n\t\t\t\tprint(entry.getKey(), entry.getValue());\n\t\t\t}    // end of for (Map.Entry entry: prop.entrySet())\n\t\t}      // end of if (print)\n\t}\n\n\tprivate static String objectToString(Object value) {\n\t\tString val_str = null;\n\t\tDataType type = DataType.valueof(value.getClass().getSimpleName());\n\n\t\ttry {\n\t\t\tStringBuilder sb = new StringBuilder();\n\n\t\t\tswitch (type) {\n\t\t\t\tcase STRING_ARR:\n\t\t\t\t\tfor (String s : (String[]) value) {\n\t\t\t\t\t\tif (sb.length() == 0) {\n\t\t\t\t\t\t\tsb.append(s);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tsb.append(\", \").append(s);\n\t\t\t\t\t\t}    // end of else\n\t\t\t\t\t}      // end of for (String s: (String[])value)\n\t\t\t\t\tval_str = sb.toString();\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase INTEGER_ARR:\n\t\t\t\t\tfor (int s : (int[]) value) {\n\t\t\t\t\t\tif (sb.length() == 0) {\n\t\t\t\t\t\t\tsb.append(s);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tsb.append(\", \").append(s);\n\t\t\t\t\t\t}    // end of else\n\t\t\t\t\t}      // end of for (String s: (String[])value)\n\t\t\t\t\tval_str = sb.toString();\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase LONG_ARR:\n\t\t\t\t\tfor (long s : (long[]) value) {\n\t\t\t\t\t\tif (sb.length() == 0) {\n\t\t\t\t\t\t\tsb.append(s);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tsb.append(\", \").append(s);\n\t\t\t\t\t\t}    // end of else\n\t\t\t\t\t}      // end of for (String s: (String[])value)\n\t\t\t\t\tval_str = sb.toString();\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DOUBLE_ARR:\n\t\t\t\t\tfor (double s : (double[]) value) {\n\t\t\t\t\t\tif (sb.length() == 0) {\n\t\t\t\t\t\t\tsb.append(s);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tsb.append(\", \").append(s);\n\t\t\t\t\t\t}    // end of else\n\t\t\t\t\t}      // end of for (String s: (String[])value)\n\t\t\t\t\tval_str = sb.toString();\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase BOOLEAN_ARR:\n\t\t\t\t\tfor (boolean s : (boolean[]) value) {\n\t\t\t\t\t\tif (sb.length() == 0) {\n\t\t\t\t\t\t\tsb.append(s);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tsb.append(\", \").append(s);\n\t\t\t\t\t\t}    // end of else\n\t\t\t\t\t}      // end of for (String s: (String[])value)\n\t\t\t\t\tval_str = sb.toString();\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tval_str = value.toString();\n\n\t\t\t\t\tbreak;\n\t\t\t}        // end of switch (type)\n\t\t} catch (ClassCastException e) {\n\t\t\tlog.warning(\"ERROR! Problem with type casting for property: \" + key);\n\t\t}          // end of try-catch\n\n\t\treturn val_str;\n\t}\n\n\tprivate static void print(String key, Object value) {\n\t\tSystem.out.println(key + \" = \" + objectToString(value));\n\t}\n\n\tpublic static void putMXBean(String objName, Object bean) {\n\t\tif (monitoring != null) {\n\t\t\tmonitoring.putMXBean(objName, bean);\n\t\t}\n\t}\n\n\tpublic ConfiguratorOld() {\n\t}\n\n\t@Override\n\tpublic void componentAdded(Configurable component) throws ConfigurationException {\n\t\tsuper.componentAdded(component);\n\t\tif (log.isLoggable(Level.CONFIG)) {\n\t\t\tlog.config(\" component: \" + component.getName());\n\t\t}\n\n\t\tServiceEntity item = config_list.findNode(component.getName());\n\n\t\tif (item == null) {\n\t\t\titem = new ServiceEntity(getName(), component.getName(), \"Component: \" + component.getName());\n\t\t\titem.addFeatures(CMD_FEATURES);\n\t\t\titem.addIdentities(new ServiceIdentity[]{\n\t\t\t\t\tnew ServiceIdentity(\"automation\", \"command-node\", \"Component: \" + component.getName())});\n\t\t\tconfig_list.addItems(new ServiceEntity[]{item});\n\t\t}\n\t\tif (config_set.findNode(component.getName()) == null) {\n\t\t\tconfig_set.addItems(new ServiceEntity[]{item});\n\t\t}\n\t\tif (component.getClass().getName().equals(ROUTER_COMP_CLASS_NAME)) {\n\t\t\trouterCompName = component.getName();\n\t\t}    // end of if (component.getClass().getName().equals())\n\t}\n\n\t@Override\n\tpublic void init(String[] args) throws ConfigurationException {\n\n\t\t// String fileName, String[] args) throws XMLDBException {\n\t\t// System.out.println(\"configurator init...\");\n\t\tparseArgs(args);\n\n\t\t// System.out.println(\"configurator after parse args, reading config from file: \" + fileName);\n\t\ttry {\n\t\t\trepository = ConfigXMLRepository.getConfigRepository(config_file_name);\n\t\t} catch (Exception e) {\n\t\t\tthrow new ConfigurationException(\"Problem reading configuration repository\", e);\n\t\t}\n\n\t\t// System.out.println(\"configurator after config repository load\");\n\t\tdefConfigParams.putAll(getAllProperties(null));\n\n\t\t// System.out.println(\"configurator after defparams.putall all properties\");\n\t\tSet<String> prop_keys = defProperties.keySet();\n\n\t\t// System.out.println(\"configurator starting loop....\");\n\t\tfor (String key : prop_keys) {\n\n\t\t\t// System.out.println(\"Analyzing key: \" + key);\n\t\t\tint idx1 = key.indexOf(\"/\");\n\n\t\t\tif (idx1 > 0) {\n\t\t\t\tString root = key.substring(0, idx1);\n\t\t\t\tString node = key.substring(idx1 + 1);\n\t\t\t\tString prop_key = null;\n\t\t\t\tint idx2 = node.lastIndexOf(\"/\");\n\n\t\t\t\tif (idx2 > 0) {\n\t\t\t\t\tprop_key = node.substring(idx2 + 1);\n\t\t\t\t\tnode = node.substring(0, idx2);\n\t\t\t\t} else {\n\t\t\t\t\tprop_key = node;\n\t\t\t\t\tnode = null;\n\t\t\t\t}\n\t\t\t\trepository.set(root, node, prop_key, defProperties.get(key));\n\t\t\t\tlog.config(\"Added default config property: (\" + key + \"=\" + defProperties.get(key) + \"), classname: \" +\n\t\t\t\t\t\t\t\t   defProperties.get(key).getClass().getName());\n\n\t\t\t\t// System.out.println(\"Added default config property: (\"\n\t\t\t\t// + key + \"=\" + defProperties.getProperty(key) + \")\");\n\t\t\t} else {\n\t\t\t\tlog.warning(\"Ignoring default property, component part is missing: \" + key);\n\t\t\t}\n\t\t}\n\n\t\t// Not sure if this is the correct pleace to initialize monitoring\n\t\t// maybe it should be initialized init initializationCompleted but\n\t\t// Then some stuff might be missing. Let's try to do it here for now\n\t\t// and maybe change it later.\n\t\tinitMonitoring((String) defConfigParams.get(MONITORING), new File(config_file_name).getParent());\n\t}\n\n\t@Override\n\tpublic void initializationCompleted() {\n\t\tif (isInitializationComplete()) {\n\n\t\t\t// Do we really need to do this again?\n\t\t\treturn;\n\t\t}\n\t\tsuper.initializationCompleted();\n\t\tif (monitoring != null) {\n\t\t\tmonitoring.initializationCompleted();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void parseArgs(final String[] args) {\n\t\tdefConfigParams.put(GEN_TEST, Boolean.FALSE);\n\t\tdefConfigParams.put(\"config-type\", GEN_CONFIG_DEF);\n\t\tif ((args != null) && (args.length > 0)) {\n\t\t\tfor (int i = 0; i < args.length; i++) {\n\t\t\t\tString key = null;\n\t\t\t\tObject val = null;\n\n\t\t\t\tif (args[i].equals(\"-c\")) {\n\t\t\t\t\tif (i + 1 == args.length) {\n\t\t\t\t\t\tSystem.out.print(help());\n\t\t\t\t\t\tSystem.exit(1);\n\t\t\t\t\t}    // end of if (i+1 == args.length)\n\t\t\t\t\telse {\n\t\t\t\t\t\tconfig_file_name = args[++i];\n\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}    // end of else\n\t\t\t\t}      // end of if (args[i].equals(\"-h\"))\n\t\t\t\tif (args[i].startsWith(GEN_CONFIG)) {\n\t\t\t\t\tkey = \"config-type\";\n\t\t\t\t\tval = args[i];\n\t\t\t\t}\n\t\t\t\tif (args[i].startsWith(GEN_TEST)) {\n\t\t\t\t\tkey = args[i];\n\t\t\t\t\tval = Boolean.TRUE;\n\t\t\t\t}\n\t\t\t\tif (args[i].equals(RepositoryFactory.GEN_USER_DB) ||\n\t\t\t\t\t\targs[i].equals(RepositoryFactory.GEN_USER_DB_URI) ||\n\t\t\t\t\t\targs[i].equals(RepositoryFactory.GEN_AUTH_DB) ||\n\t\t\t\t\t\targs[i].equals(RepositoryFactory.GEN_AUTH_DB_URI) || args[i].startsWith(GEN_COMP_NAME) ||\n\t\t\t\t\t\targs[i].startsWith(GEN_COMP_CLASS) || args[i].startsWith(GEN_EXT_COMP) ||\n\t\t\t\t\t\targs[i].equals(GEN_VIRT_HOSTS) || args[i].equals(GEN_ADMINS) || args[i].equals(GEN_DEBUG) ||\n\t\t\t\t\t\t(args[i].startsWith(GEN_CONF) && !args[i].startsWith(GEN_CONFIG)) ||\n\t\t\t\t\t\targs[i].equals(PROPERTY_FILENAME_PROP_KEY) || args[i].equals(CLUSTER_MODE)) {\n\t\t\t\t\tkey = args[i];\n\t\t\t\t\tval = args[++i];\n\t\t\t\t}\n\t\t\t\tif (key != null) {\n\t\t\t\t\tdefConfigParams.put(key, val);\n\n\t\t\t\t\t// System.out.println(\"Setting defaults: \" + key + \"=\" + val.toString());\n\t\t\t\t\tlog.config(\"Setting defaults: \" + key + \"=\" + val.toString());\n\t\t\t\t}    // end of if (key != null)\n\t\t\t}      // end of for (int i = 0; i < args.length; i++)\n\t\t}\n\n\t\tString property_filename = (String) defConfigParams.get(PROPERTY_FILENAME_PROP_KEY);\n\n\t\tif (property_filename != null) {\n\t\t\tlog.config(\"Loading initial properties from property file: \" + property_filename);\n\t\t\ttry {\n\t\t\t\tProperties defProps = new Properties();\n\n\t\t\t\tdefProps.load(new FileReader(property_filename));\n\n\t\t\t\tSet<String> prop_keys = defProps.stringPropertyNames();\n\n\t\t\t\tfor (String key : prop_keys) {\n\t\t\t\t\tString value = defProps.getProperty(key).trim();\n\n\t\t\t\t\tif (key.startsWith(\"--\") || key.equals(\"config-type\")) {\n\t\t\t\t\t\tdefConfigParams.put(key.trim(), value);\n\n\t\t\t\t\t\t// defProperties.remove(key);\n\t\t\t\t\t\tlog.config(\"Added default config parameter: (\" + key + \"=\" + value + \")\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\tObject val = value;\n\n\t\t\t\t\t\tif (key.matches(\".*\\\\[[LISBlisb]\\\\]$\")) {\n\t\t\t\t\t\t\tchar c = key.charAt(key.length() - 2);\n\n\t\t\t\t\t\t\tkey = key.substring(0, key.length() - 3);\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tswitch (c) {\n\t\t\t\t\t\t\t\t\tcase 'L':\n\n\t\t\t\t\t\t\t\t\t\t// Long value\n\t\t\t\t\t\t\t\t\t\tval = Long.decode(value);\n\n\t\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\t\tcase 'I':\n\n\t\t\t\t\t\t\t\t\t\t// Integer value\n\t\t\t\t\t\t\t\t\t\tval = Integer.decode(value);\n\n\t\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\t\tcase 'B':\n\n\t\t\t\t\t\t\t\t\t\t// Boolean value\n\t\t\t\t\t\t\t\t\t\tval = Boolean.valueOf(Boolean.parseBoolean(value));\n\t\t\t\t\t\t\t\t\t\tlog.config(\"Found Boolean property: \" + val.toString());\n\n\t\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\t\tcase 's':\n\n\t\t\t\t\t\t\t\t\t\t// Comma separated, Strings array\n\t\t\t\t\t\t\t\t\t\tval = value.split(\",\");\n\n\t\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\t\tcase 'i':\n\n\t\t\t\t\t\t\t\t\t\t// Comma separated, int array\n\t\t\t\t\t\t\t\t\t\tString[] ints_str = value.split(\",\");\n\t\t\t\t\t\t\t\t\t\tint[] ints = new int[ints_str.length];\n\t\t\t\t\t\t\t\t\t\tint k = 0;\n\n\t\t\t\t\t\t\t\t\t\tfor (String i : ints_str) {\n\t\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\t\tints[k++] = Integer.parseInt(i);\n\t\t\t\t\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\t\t\t\t\tlog.warning(\"Incorrect int array settins: \" + i);\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tval = ints;\n\n\t\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\t\tcase 'l':\n\n\t\t\t\t\t\t\t\t\t\t// Comma separated, long array\n\t\t\t\t\t\t\t\t\t\tString[] longs_str = value.split(\",\");\n\t\t\t\t\t\t\t\t\t\tlong[] longs = new long[longs_str.length];\n\t\t\t\t\t\t\t\t\t\tint j = 0;\n\n\t\t\t\t\t\t\t\t\t\tfor (String i : longs_str) {\n\t\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\t\tlongs[j++] = Long.parseLong(i);\n\t\t\t\t\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\t\t\t\t\tlog.warning(\"Incorrect long array settins: \" + i);\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tval = longs;\n\n\t\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\t\tdefault:\n\n\t\t\t\t\t\t\t\t\t\t// Do nothing, default to String\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\tlog.log(Level.CONFIG, \"Incorrect parameter modifier\", e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdefProperties.put(key.trim(), val);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (FileNotFoundException e) {\n\t\t\t\tlog.warning(\"Given property file was not found: \" + property_filename);\n\t\t\t} catch (IOException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Can not read property file: \" + property_filename, e);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processPacket(final Packet packet, final Queue<Packet> results) {\n\t\tif (!packet.isCommand()) {\n\t\t\treturn;\n\t\t}\n\n\t\tIq iqc = (Iq) packet;\n\n\t\tif ((iqc.getType() != null) && (iqc.getType() == StanzaType.error)) {\n\t\t\tlog.log(Level.CONFIG, \"Ignoring error packet: \" + iqc.toString());\n\n\t\t\treturn;\n\t\t}\n\n\t\tString nick = iqc.getTo().getLocalpart();\n\n\t\tif ((nick == null) || !getName().equals(nick)) {\n\t\t\treturn;\n\t\t}\n\n\t\tString msg = \"Please be careful, you are service admin and all changes\" +\n\t\t\t\t\" you make are instantly applied to live system!\";\n\t\tboolean admin = true;\n\n\t\tif (iqc.getPermissions() != Permissions.ADMIN) {\n\t\t\tif (demoMode) {\n\t\t\t\tadmin = false;\n\t\t\t\tmsg = \"You are not admin. You can safely play with the settings as\" + \" you can not change anything.\";\n\t\t\t\tif ((iqc.getStrCommand() != null) && iqc.getStrCommand().endsWith(DEF_SM_NAME)) {\n\t\t\t\t\tPacket result = iqc.commandResult(Command.DataType.result);\n\n\t\t\t\t\tCommand.addFieldValue(result, \"Note\", msg, \"fixed\");\n\t\t\t\t\tCommand.addFieldValue(result, \"Note\", \"Restricted area, only admin can see these settings.\",\n\t\t\t\t\t\t\t\t\t\t  \"fixed\");\n\t\t\t\t\tresults.offer(result);\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tresults.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"You are not authorized for this action.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  true));\n\t\t\t\t} catch (PacketErrorTypeException e) {\n\t\t\t\t\tlog.warning(\"Packet processing exception: \" + e);\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Command received: \" + iqc.toString());\n\t\t}\n\n\t\tCommand.Action action = Command.getAction(iqc);\n\n\t\tif (action == Command.Action.cancel) {\n\t\t\tPacket result = iqc.commandResult(null);\n\n\t\t\tresults.offer(result);\n\n\t\t\treturn;\n\t\t}\n\t\tswitch (iqc.getCommand()) {\n\t\t\tcase OTHER:\n\t\t\t\tif (iqc.getStrCommand() != null) {\n\t\t\t\t\tif (iqc.getStrCommand().startsWith(\"config/list/\")) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tString[] spl = iqc.getStrCommand().split(\"/\");\n\t\t\t\t\t\t\tPacket result = iqc.commandResult(Command.DataType.result);\n\n\t\t\t\t\t\t\tCommand.addFieldValue(result, \"Note\", msg, \"fixed\");\n\n\t\t\t\t\t\t\tMap<String, Object> allprop = getAllProperties(spl[2]);\n\n\t\t\t\t\t\t\tfor (Map.Entry<String, Object> entry : allprop.entrySet()) {\n\t\t\t\t\t\t\t\tCommand.addFieldValue(result, XMLUtils.escape(entry.getKey()),\n\t\t\t\t\t\t\t\t\t\t\t\t\t  XMLUtils.escape(objectToString(entry.getValue())));\n\t\t\t\t\t\t\t}    // end of for (Map.Entry entry: prop.entrySet())\n\t\t\t\t\t\t\tresults.offer(result);\n\t\t\t\t\t\t} catch (ConfigurationException ex) {\n\t\t\t\t\t\t\tLogger.getLogger(ConfiguratorOld.class.getName()).log(Level.SEVERE, null, ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (iqc.getStrCommand().startsWith(\"config/set/\")) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tString[] spl = iqc.getStrCommand().split(\"/\");\n\t\t\t\t\t\t\tPacket result = iqc.commandResult(Command.DataType.result);\n\n\t\t\t\t\t\t\tCommand.addFieldValue(result, \"Note\", msg, \"fixed\");\n\t\t\t\t\t\t\tif (Command.getData(packet) == null) {\n\t\t\t\t\t\t\t\tprepareConfigData(result, spl[2]);\n\t\t\t\t\t\t\t\tresults.offer(result);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tupdateConfigChanges(packet, result, spl[2], admin);\n\t\t\t\t\t\t\t\tresults.offer(result);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (ConfigurationException ex) {\n\t\t\t\t\t\t\tLogger.getLogger(ConfiguratorOld.class.getName()).log(Level.SEVERE, null, ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tpublic void setup(String name) throws ConfigurationException {\n\t\tConfigurable component = getComponent(name);\n\n\t\tsetup(component);\n\t}\n\n\tpublic Map<String, Object> getAllProperties(String key) throws ConfigurationException {\n\t\tMap<String, Object> result = new LinkedHashMap<String, Object>();\n\t\tString[] comps = getComponents();\n\n\t\tif (comps != null) {\n\t\t\tfor (String comp : comps) {\n\t\t\t\tMap<String, Object> prop = getProperties(comp);\n\n\t\t\t\tfor (Map.Entry<String, Object> entry : prop.entrySet()) {\n\t\t\t\t\tString entry_key = comp + \"/\" + entry.getKey();\n\n\t\t\t\t\tif (key == null) {\n\t\t\t\t\t\tresult.put(entry_key, entry.getValue());\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (entry_key.startsWith(key)) {\n\t\t\t\t\t\t\tresult.put(entry_key, entry.getValue());\n\t\t\t\t\t\t}    // end of if (entry_key.startsWith(key))\n\t\t\t\t\t}      // end of if (key == null) else\n\t\t\t\t}        // end of for (Map.Entry entry: prop.entrySet())\n\t\t\t}          // end of for (String comp: comps)\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic String[] getComponents() {\n\t\treturn repository.getCompNames();\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getDefaults(Map<String, Object> params) {\n\t\tMap<String, Object> defaults = super.getDefaults(params);\n\n\t\tdefaults.put(\"demo-mode\", demoMode);\n\n\t\treturn defaults;\n\t}\n\n\t@Override\n\tpublic Map<String, Object> getDefConfigParams() {\n\t\treturn defConfigParams;\n\t}\n\n\t@Override\n\tpublic List<Element> getDiscoFeatures(JID from) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Element getDiscoInfo(String node, JID jid, JID from) {\n\t\tif ((jid != null) && getName().equals(jid.getLocalpart()) && isAdmin(from)) {\n\t\t\treturn serviceEntity.getDiscoInfo(node);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic List<Element> getDiscoItems(String node, JID jid, JID from) {\n\t\tif (isAdmin(from)) {\n\t\t\tif (getName().equals(jid.getLocalpart())) {\n\t\t\t\treturn serviceEntity.getDiscoItems(node, jid.toString());\n\t\t\t} else {\n\t\t\t\tif (node == null) {\n\t\t\t\t\treturn Arrays.asList(serviceEntity.getDiscoItem(null, BareJID.toString(getName(), jid.toString())));\n\t\t\t\t} else {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\tsuper.setName(name);\n\t\tserviceEntity = new ServiceEntity(name, \"config\", \"Server configuration\");\n\t\tserviceEntity.addIdentities(\n\t\t\t\tnew ServiceIdentity[]{new ServiceIdentity(\"automation\", \"command-list\", \"Configuration commands\")});\n\t\tserviceEntity.addFeatures(DEF_FEATURES);\n\t\tconfig_list = new ServiceEntity(name, \"list\", \"List\");\n\t\tconfig_list.addIdentities(\n\t\t\t\tnew ServiceIdentity[]{new ServiceIdentity(\"automation\", \"command-list\", \"Config listings\")});\n\t\tconfig_list.addFeatures(DEF_FEATURES);\n\t\tconfig_set = new ServiceEntity(name, \"set\", \"Set\");\n\t\tconfig_set.addIdentities(\n\t\t\t\tnew ServiceIdentity[]{new ServiceIdentity(\"automation\", \"command-list\", \"Config settings\")});\n\t\tconfig_set.addFeatures(DEF_FEATURES);\n\n\t\tServiceEntity item = new ServiceEntity(getName(), \"--none--\", \"Add new component...\");\n\n\t\titem.addFeatures(CMD_FEATURES);\n\t\titem.addIdentities(new ServiceIdentity(\"automation\", \"command-node\", \"Add new component...\"));\n\t\tconfig_set.addItems(item);\n\t\tserviceEntity.addItems(config_list, config_set);\n\t}\n\n\t@Override\n\tpublic void setProperties(final Map<String, Object> props) throws ConfigurationException {\n\t\tsuper.setProperties(props);\n\n\t\t// setupLogManager(props);\n\t\tdemoMode = (Boolean) props.get(\"demo-mode\");\n\t}\n\n\tpublic Object setPropertyValue(String key, String val, Packet result_pack, boolean admin) {\n\t\tObject result = null;\n\n\t\ttry {\n\t\t\tif (admin) {\n\t\t\t\tresult = setValue(key, val, false, false, null);\n\t\t\t}\n\t\t\tif (result != null) {\n\t\t\t\tCommand.addFieldValue(result_pack, XMLUtils.escape(key), XMLUtils.escape(val));\n\t\t\t} else {\n\t\t\t\tCommand.addFieldValue(result_pack, \"Note\",\n\t\t\t\t\t\t\t\t\t  \"You can not set new properties yet, you can just modify existing ones.\",\n\t\t\t\t\t\t\t\t\t  \"fixed\");\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tCommand.addFieldValue(result_pack, \"Note\", \"Error setting property: \" + e, \"fixed\");\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic Object setValue(String node_key, String value, boolean add, boolean feedback, Map<String, Object> orig)\n\t\t\tthrows Exception {\n\t\tint root_idx = node_key.indexOf('/');\n\t\tString root = (root_idx > 0) ? node_key.substring(0, root_idx) : \"\";\n\t\tint key_idx = node_key.lastIndexOf('/');\n\t\tString key = (key_idx > 0) ? node_key.substring(key_idx + 1) : node_key;\n\t\tString subnode = null;\n\n\t\tif (root_idx != key_idx) {\n\t\t\tsubnode = node_key.substring(root_idx + 1, key_idx);\n\t\t}\n\n\t\tObject old_val = null;\n\n\t\tif (orig == null) {\n\t\t\told_val = repository.get(root, subnode, key, null);\n\t\t} else {\n\t\t\told_val = orig.get(node_key);\n\t\t}    // end of if (orig == null) else\n\t\tif (old_val != null) {\n\t\t\tObject new_val = null;\n\t\t\tDataType type = DataType.valueof(old_val.getClass().getSimpleName());\n\n\t\t\tswitch (type) {\n\t\t\t\tcase INTEGER:\n\t\t\t\t\tnew_val = Integer.decode(value);\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase INTEGER_ARR:\n\t\t\t\t\tif (add) {\n\t\t\t\t\t\tint old_len = ((int[]) old_val).length;\n\n\t\t\t\t\t\tnew_val = Arrays.copyOf((int[]) old_val, old_len + 1);\n\t\t\t\t\t\t((int[]) new_val)[old_len] = Integer.decode(value);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tString[] spl = value.split(\",\");\n\n\t\t\t\t\t\tnew_val = new int[spl.length];\n\t\t\t\t\t\tfor (int i = 0; i < spl.length; i++) {\n\t\t\t\t\t\t\t((int[]) new_val)[i] = Integer.decode(spl[i].trim());\n\t\t\t\t\t\t}\n\t\t\t\t\t}    // end of if (add) else\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase LONG:\n\t\t\t\t\tnew_val = Long.decode(value);\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase LONG_ARR:\n\t\t\t\t\tif (add) {\n\t\t\t\t\t\tint old_len = ((long[]) old_val).length;\n\n\t\t\t\t\t\tnew_val = Arrays.copyOf((long[]) old_val, old_len + 1);\n\t\t\t\t\t\t((long[]) new_val)[old_len] = Long.decode(value);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tString[] spl = value.split(\",\");\n\n\t\t\t\t\t\tnew_val = new long[spl.length];\n\t\t\t\t\t\tfor (int i = 0; i < spl.length; i++) {\n\t\t\t\t\t\t\t((long[]) new_val)[i] = Long.decode(spl[i].trim());\n\t\t\t\t\t\t}\n\t\t\t\t\t}    // end of if (add) else\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase STRING:\n\t\t\t\t\tnew_val = value;\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase STRING_ARR:\n\t\t\t\t\tif (add) {\n\t\t\t\t\t\tint old_len = ((String[]) old_val).length;\n\n\t\t\t\t\t\tnew_val = Arrays.copyOf((String[]) old_val, old_len + 1);\n\t\t\t\t\t\t((String[]) new_val)[old_len] = value;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tString[] spl = value.split(\",\");\n\n\t\t\t\t\t\tnew_val = new String[spl.length];\n\t\t\t\t\t\tfor (int i = 0; i < spl.length; i++) {\n\t\t\t\t\t\t\t((String[]) new_val)[i] = spl[i].trim();\n\t\t\t\t\t\t}\n\t\t\t\t\t}    // end of if (add) else\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DOUBLE:\n\t\t\t\t\tnew_val = new Double(Double.parseDouble(value));\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DOUBLE_ARR:\n\t\t\t\t\tif (add) {\n\t\t\t\t\t\tint old_len = ((double[]) old_val).length;\n\n\t\t\t\t\t\tnew_val = Arrays.copyOf((double[]) old_val, old_len + 1);\n\t\t\t\t\t\t((double[]) new_val)[old_len] = Double.parseDouble(value);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tString[] spl = value.split(\",\");\n\n\t\t\t\t\t\tnew_val = new double[spl.length];\n\t\t\t\t\t\tfor (int i = 0; i < spl.length; i++) {\n\t\t\t\t\t\t\t((double[]) new_val)[i] = Double.parseDouble(spl[i].trim());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase BOOLEAN:\n\t\t\t\t\tnew_val = Boolean.valueOf(parseBoolean(value));\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase BOOLEAN_ARR:\n\t\t\t\t\tif (add) {\n\t\t\t\t\t\tint old_len = ((boolean[]) old_val).length;\n\n\t\t\t\t\t\tnew_val = Arrays.copyOf((boolean[]) old_val, old_len + 1);\n\t\t\t\t\t\t((boolean[]) new_val)[old_len] = parseBoolean(value);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tString[] spl = value.split(\",\");\n\n\t\t\t\t\t\tnew_val = new boolean[spl.length];\n\t\t\t\t\t\tfor (int i = 0; i < spl.length; i++) {\n\t\t\t\t\t\t\t((boolean[]) new_val)[i] = parseBoolean(spl[i].trim());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tnew_val = value;\n\n\t\t\t\t\tbreak;\n\t\t\t}      // end of switch (type)\n\t\t\tif (orig == null) {\n\t\t\t\trepository.set(root, subnode, key, new_val);\n\t\t\t\trepository.store();\n\t\t\t} else {\n\t\t\t\torig.put(node_key, new_val);\n\t\t\t}      // end of if (orig == null) else\n\n\t\t\treturn new_val;\n\t\t} else {\n\t\t\tif (force) {\n\t\t\t\tif (orig == null) {\n\t\t\t\t\trepository.set(root, subnode, key, value);\n\t\t\t\t\trepository.store();\n\t\t\t\t} else {\n\t\t\t\t\torig.put(node_key, value);\n\t\t\t\t}    // end of if (orig == null) else\n\t\t\t\tif (feedback) {\n\t\t\t\t\tSystem.out.println(\"Forced to set new key=value: \" + key + \"=\" + value);\n\t\t\t\t}\n\n\t\t\t\treturn value;\n\t\t\t} else {\n\t\t\t\tif (feedback) {\n\t\t\t\t\tSystem.out.println(\"Error, given key does not exist in config yet.\");\n\t\t\t\t\tSystem.out.println(\"You can only modify existing values, you can add new.\");\n\t\t\t\t\tSystem.out.println(\"Use '-f' switch to force creation of the new property.\");\n\t\t\t\t}\n\n\t\t\t\treturn null;\n\t\t\t}      // end of if (force) else\n\t\t}        // end of else\n\t}\n\n\t//private boolean isValidCompName(String name) {\n//  return !(name.contains(\" \")\n//    || name.contains(\"\\t\")\n//    || name.contains(\"@\")\n//    || name.contains(\"&\"));\n//}\n\tprivate boolean checkComponentName(Packet result, String name) {\n\t\tString msg = name;\n\n\t\tif (msg != null) {\n\t\t\tCommand.addFieldValue(result, \"Info\", \"Note!! \" + msg + \", please provide valid component name.\", \"fixed\");\n\t\t\tnewComponentCommand(result);\n\n\t\t\treturn false;\n\t\t}    // end of if (new_comp_name == null || new_comp_name.length() == 0)\n\n\t\tString[] comp_names = getComponents();\n\n\t\tfor (String comp_name : comp_names) {\n\t\t\tif (comp_name.equals(name)) {\n\t\t\t\tCommand.addFieldValue(result, \"Info\", \"Note!! Component with provided name already exists.\", \"fixed\");\n\t\t\t\tCommand.addFieldValue(result, \"Info\", \"Please provide different component name.\", \"fixed\");\n\t\t\t\tnewComponentCommand(result);\n\n\t\t\t\treturn false;\n\t\t\t}    // end of if (comp_name.equals(new_comp_name))\n\t\t}      // end of for (String comp_name: comp_names)\n\n\t\treturn true;\n\t}\n\n\tprivate void createNewComponent(Packet packet, Packet result, boolean admin) {\n\t\tString new_comp_name = Command.getFieldValue(packet, \"Component name\");\n\t\tString new_comp_class = Command.getFieldValue(packet, \"Component class\");\n\n\t\ttry {\n\t\t\tMessageReceiver mr = (MessageReceiver) Class.forName(new_comp_class).newInstance();\n\n\t\t\tmr.setName(new_comp_name);\n\t\t\tif (mr instanceof Configurable) {\n\t\t\t\tMap<String, Object> comp_props = ((Configurable) mr).getDefaults(defConfigParams);\n\t\t\t\tMap<String, Object> new_params = new LinkedHashMap<String, Object>(comp_props);\n\n\t\t\t\t// Convert String values to proper Objecy values\n\t\t\t\tfor (Map.Entry<String, Object> entry : comp_props.entrySet()) {\n\t\t\t\t\tString val = Command.getFieldValue(packet, XMLUtils.escape(entry.getKey()));\n\n\t\t\t\t\tif (val == null) {\n\t\t\t\t\t\tval = \"\";\n\t\t\t\t\t}\n\t\t\t\t\tval = XMLUtils.unescape(val);\n\t\t\t\t\tlog.log(Level.CONFIG, \"New component value: \" + entry.getKey() + \"=\" + val);\n\t\t\t\t\tsetValue(entry.getKey(), val, false, false, new_params);\n\t\t\t\t}    // end of for (Map.Entry entry: prop.entrySet())\n\t\t\t\tif (admin) {\n\n\t\t\t\t\t// Now we can save all properties to config repository:\n\t\t\t\t\tfor (Map.Entry<String, Object> entry : new_params.entrySet()) {\n\t\t\t\t\t\tString key = entry.getKey();\n\t\t\t\t\t\tString subnode = null;\n\t\t\t\t\t\tint key_idx = entry.getKey().lastIndexOf('/');\n\n\t\t\t\t\t\tif (key_idx > 0) {\n\t\t\t\t\t\t\tkey = entry.getKey().substring(key_idx + 1);\n\t\t\t\t\t\t\tsubnode = entry.getKey().substring(0, key_idx);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tlog.log(Level.CONFIG, \"Saving property to repository: \" + \"root=\" + new_comp_name + \", subnode=\" + subnode +\n\t\t\t\t\t\t\t\t\t\t \", key=\" + key + \", value=\" + entry.getValue());\n\t\t\t\t\t\trepository.set(new_comp_name, subnode, key, entry.getValue());\n\t\t\t\t\t}    // end of for (Map.Entry entry: prop.entrySet())\n\n\t\t\t\t\t// And load the component itself.....\n\t\t\t\t\t// Set class name for the component\n\t\t\t\t\trepository.set(routerCompName, \"/components/msg-receivers\", new_comp_name + \".class\",\n\t\t\t\t\t\t\t\t   new_comp_class);\n\n\t\t\t\t\t// Activate the component\n\t\t\t\t\trepository.set(routerCompName, \"/components/msg-receivers\", new_comp_name + \".active\", true);\n\n\t\t\t\t\t// Add to the list of automaticaly loaded components\n\t\t\t\t\tsetValue(routerCompName + \"/components/msg-receivers/id-names\", new_comp_name, true, false, null);\n\n\t\t\t\t\t// repository.sync();\n\t\t\t\t\tsetup(routerCompName);\n\t\t\t\t}    // end of if (admin)\n\t\t\t}\n\t\t\tCommand.addNote(result, \"New component created: \" + new_comp_name);\n\t\t\tCommand.addFieldValue(result, \"Note\", \"New component created: \" + new_comp_name, \"fixed\");\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.SEVERE, \"Problem instantiating component:\", e);\n\t\t\tCommand.addFieldValue(result, \"Component class\",\n\t\t\t\t\t\t\t\t  \"ERROR!! Problem instantiating component, \" + \"look in log file for details...\",\n\t\t\t\t\t\t\t\t  \"text-single\", \"Component class\");\n\t\t}    // end of try-catch\n\t}\n\n\tprivate void initMonitoring(String settings, String configDir) {\n\t\tif ((monitoring == null) && (settings != null)) {\n\t\t\ttry {\n\t\t\t\tmonitoring = (MonitoringSetupIfc) Class.forName(\"tigase.management.MonitoringSetup\").newInstance();\n\t\t\t\tmonitoring.initMonitoring(settings, configDir);\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.WARNING, \"Can not initialize monitoring: \", e);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void newComponentCommand(Packet result) {\n\t\tCommand.addFieldValue(result, \"Info\", \"Press:\", \"fixed\");\n\t\tCommand.addFieldValue(result, \"Info\", \"'Next' to set all parameters for the new component.\", \"fixed\");\n\t\tCommand.setStatus(result, Command.Status.executing);\n\t\tCommand.addAction(result, Command.Action.next);\n\t\tCommand.addFieldValue(result, \"Component name\", \"\", \"text-single\", \"Component name\");\n\t\ttry {\n\t\t\tSet<Class<MessageReceiver>> receiv_cls = ClassUtil.getClassesImplementing(MessageReceiver.class);\n\n\t\t\t// All message receivers except MessageRouter\n\t\t\tString[] receiv_cls_names = new String[receiv_cls.size() - 1];\n\t\t\tString[] receiv_cls_simple = new String[receiv_cls.size() - 1];\n\t\t\tint idx = 0;\n\n\t\t\tfor (Class<MessageReceiver> reciv : receiv_cls) {\n\t\t\t\tif (!reciv.getName().equals(ROUTER_COMP_CLASS_NAME)) {\n\t\t\t\t\treceiv_cls_names[idx] = reciv.getName();\n\t\t\t\t\treceiv_cls_simple[idx++] = reciv.getSimpleName();\n\t\t\t\t}    // end of if (!reciv.getName().equals(ROUTER_COMP_CLASS_NAME))\n\t\t\t}      // end of for (MessageReceiver.class reciv: receiv_cls)\n\t\t\tCommand.addFieldValue(result, \"Component class\", EXT_COMP_CLASS_NAME, \"Component class\", receiv_cls_simple,\n\t\t\t\t\t\t\t\t  receiv_cls_names);\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.SEVERE, \"Problem loading MessageReceiver implementations\", e);\n\t\t\tCommand.addFieldValue(result, \"Component class\",\n\t\t\t\t\t\t\t\t  \"ERROR!! Problem loading MessageReceiver implementations, \" +\n\t\t\t\t\t\t\t\t\t\t  \"look in log file for details...\", \"text-single\", \"Component class\");\n\t\t}    // end of try-catch\n\t}\n\n\tprivate void newComponentCommand(Packet packet, Packet result, boolean admin) {\n\t\tString params_set = Command.getFieldValue(packet, \"Params set\");\n\n\t\tif ((Command.getAction(packet) != null) && Command.getAction(packet).equals(\"prev\")) {\n\t\t\tnewComponentCommand(result);\n\n\t\t\treturn;\n\t\t}    // end of if ()\n\t\tif (params_set != null) {\n\t\t\tcreateNewComponent(packet, result, admin);\n\n\t\t\treturn;\n\t\t}    // end of if (params_set != null)\n\n\t\tString new_comp_name = Command.getFieldValue(packet, \"Component name\");\n\t\tString new_comp_class = Command.getFieldValue(packet, \"Component class\");\n\n\t\tif (!checkComponentName(result, new_comp_name)) {\n\t\t\treturn;\n\t\t}    // end of if (!checkComponentName(new_comp_name))\n\t\tCommand.setStatus(result, Command.Status.executing);\n\t\tCommand.addFieldValue(result, \"Component name\", new_comp_name, \"hidden\");\n\t\tCommand.addFieldValue(result, \"Component class\", new_comp_class, \"hidden\");\n\t\tCommand.addFieldValue(result, \"Info1\", \"Press:\", \"fixed\");\n\t\ttry {\n\t\t\tMessageReceiver mr = (MessageReceiver) Class.forName(new_comp_class).newInstance();\n\n\t\t\tCommand.addFieldValue(result, \"Info4\",\n\t\t\t\t\t\t\t\t  \"Component name: \" + new_comp_name + \", class: \" + mr.getClass().getSimpleName(),\n\t\t\t\t\t\t\t\t  \"fixed\");\n\t\t\tif (mr instanceof ConnectionManager) {\n\t\t\t\tString ports = Command.getFieldValue(packet, \"TCP/IP ports\");\n\n\t\t\t\tif (ports == null) {\n\t\t\t\t\tCommand.addFieldValue(result, \"Info2\", \"1. 'Next' to set more component parameters.\", \"fixed\");\n\t\t\t\t\tCommand.addFieldValue(result, \"Info3\", \"2. 'Previous' to go back and select different component.\",\n\t\t\t\t\t\t\t\t\t\t  \"fixed\");\n\t\t\t\t\tCommand.addAction(result, Command.Action.next);\n\t\t\t\t\tCommand.addAction(result, Command.Action.prev);\n\t\t\t\t\tCommand.addFieldValue(result, \"Info4\",\n\t\t\t\t\t\t\t\t\t\t  \"This component uses TCP/IP ports, please provide port numbers:\", \"fixed\");\n\t\t\t\t\tCommand.addFieldValue(result, \"TCP/IP ports\", \"5557\");\n\n\t\t\t\t\treturn;\n\t\t\t\t} else {\n\t\t\t\t\tString[] ports_arr = ports.split(\",\");\n\t\t\t\t\tint[] ports_i = new int[ports_arr.length];\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tfor (int i = 0; i < ports_arr.length; i++) {\n\t\t\t\t\t\t\tports_i[i] = Integer.decode(ports_arr[i].trim());\n\t\t\t\t\t\t}    // end of for (int i = 0; i < ports_arr.length; i++)\n\t\t\t\t\t\tdefConfigParams.put(new_comp_name + \"/connections/ports\", ports_i);\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tCommand.addFieldValue(result, \"Info2\", \"1. 'Next' to set more component parameters.\", \"fixed\");\n\t\t\t\t\t\tCommand.addFieldValue(result, \"Info3\",\n\t\t\t\t\t\t\t\t\t\t\t  \"2. 'Previous' to go back and select different component.\", \"fixed\");\n\t\t\t\t\t\tCommand.addAction(result, Command.Action.next);\n\t\t\t\t\t\tCommand.addAction(result, Command.Action.prev);\n\t\t\t\t\t\tCommand.addFieldValue(result, \"Info4\",\n\t\t\t\t\t\t\t\t\t\t\t  \"Incorrect TCP/IP ports provided, please provide port numbers:\", \"fixed\");\n\t\t\t\t\t\tCommand.addFieldValue(result, \"TCP/IP ports\", ports);\n\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}    // end of try-catch\n\t\t\t\t}      // end of else\n\t\t\t}\n\t\t\tCommand.addFieldValue(result, \"Info2\", \"1. 'Finish' to create component with this parameters.\", \"fixed\");\n\t\t\tCommand.addFieldValue(result, \"Info3\", \"2. 'Previous' to go back and select different component.\", \"fixed\");\n\t\t\tCommand.addAction(result, Command.Action.complete);\n\t\t\tCommand.addAction(result, Command.Action.prev);\n\t\t\tmr.setName(new_comp_name);\n\t\t\tif (mr instanceof Configurable) {\n\n\t\t\t\t// Load defaults into sorted Map:\n\t\t\t\tMap<String, Object> comp_props = new TreeMap<String, Object>(\n\t\t\t\t\t\t((Configurable) mr).getDefaults(defConfigParams));\n\n\t\t\t\tfor (Map.Entry<String, Object> entry : comp_props.entrySet()) {\n\t\t\t\t\tCommand.addFieldValue(result, XMLUtils.escape(entry.getKey()),\n\t\t\t\t\t\t\t\t\t\t  XMLUtils.escape(objectToString(entry.getValue())));\n\t\t\t\t}    // end of for (Map.Entry entry: prop.entrySet())\n\t\t\t} else {\n\t\t\t\tCommand.addFieldValue(result, \"Info6\", \"Component is not configurable, do you want to create it?\",\n\t\t\t\t\t\t\t\t\t  \"fixed\");\n\t\t\t}    // end of else\n\t\t\tCommand.addFieldValue(result, \"Params set\", \"true\", \"hidden\");\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.SEVERE, \"Problem instantiating component:\", e);\n\t\t\tCommand.addFieldValue(result, \"Component class\",\n\t\t\t\t\t\t\t\t  \"ERROR!! Problem instantiating component, \" + \"look in log file for details...\",\n\t\t\t\t\t\t\t\t  \"text-single\", \"Component class\");\n\t\t}    // end of try-catch\n\t}\n\n\tprivate boolean parseBoolean(String val) {\n\t\treturn val.equalsIgnoreCase(\"true\") || val.equalsIgnoreCase(\"yes\") || val.equalsIgnoreCase(\"on\");\n\t}\n\n\tprivate void prepareConfigData(Packet result, String comp_name) throws ConfigurationException {\n\t\tif (comp_name.equals(\"--none--\")) {\n\t\t\tnewComponentCommand(result);\n\n\t\t\treturn;\n\t\t}    // end of if (comp_name.equals(\"--none--\"))\n\t\tCommand.setStatus(result, Command.Status.executing);\n\t\tCommand.addAction(result, Command.Action.complete);\n\n\t\t// Let's try to sort them to make it easier to find options on\n\t\t// configuration page.\n\t\tMap<String, Object> allprop = new TreeMap<String, Object>(getAllProperties(comp_name));\n\n\t\tfor (Map.Entry<String, Object> entry : allprop.entrySet()) {\n\t\t\tCommand.addFieldValue(result, XMLUtils.escape(entry.getKey()),\n\t\t\t\t\t\t\t\t  XMLUtils.escape(objectToString(entry.getValue())));\n\t\t}    // end of for (Map.Entry entry: prop.entrySet())\n\t\tCommand.addFieldValue(result, XMLUtils.escape(\"new-prop-name\"), XMLUtils.escape(comp_name + \"/\"), \"text-single\",\n\t\t\t\t\t\t\t  \"New property name\");\n\t\tCommand.addFieldValue(result, XMLUtils.escape(\"new-prop-value\"), \"\", \"text-single\", \"New property value\");\n\t}\n\n\tprivate void updateConfigChanges(Packet packet, Packet result, String comp_name, boolean admin)\n\t\t\tthrows ConfigurationException {\n\t\tif (comp_name.equals(\"--none--\")) {\n\t\t\tnewComponentCommand(packet, result, admin);\n\n\t\t\treturn;\n\t\t}    // end of if (comp_name.equals(\"--none--\"))\n\t\tCommand.addNote(result, \"You changed following settings:\");\n\t\tCommand.addFieldValue(result, \"Note\", \"You changed following settings:\", \"fixed\");\n\n\t\tMap<String, Object> allprop = getAllProperties(comp_name);\n\t\tboolean changed = false;\n\n\t\tfor (Map.Entry<String, Object> entry : allprop.entrySet()) {\n\t\t\tString tmp_val = Command.getFieldValue(packet, XMLUtils.escape(entry.getKey()));\n\t\t\tString old_val = objectToString(entry.getValue());\n\t\t\tString new_val = old_val;\n\n\t\t\tif (tmp_val != null) {\n\t\t\t\tnew_val = XMLUtils.unescape(tmp_val);\n\t\t\t}\n\t\t\tif ((new_val != null) && (old_val != null) && !new_val.equals(old_val)) {\n\t\t\t\tdefConfigParams.put(entry.getKey(), setPropertyValue(entry.getKey(), new_val, result, admin));\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t}    // end of for (Map.Entry entry: prop.entrySet())\n\n\t\tString prop_value = Command.getFieldValue(packet, \"new-prop-value\");\n\n\t\tif ((prop_value != null) && (prop_value.trim().length() > 0)) {\n\t\t\tsetPropertyValue(XMLUtils.unescape(Command.getFieldValue(packet, \"new-prop-name\")),\n\t\t\t\t\t\t\t XMLUtils.unescape(prop_value), result, admin);\n\t\t\tchanged = true;\n\t\t}\n\t\tif (changed && admin) {\n\t\t\tsetup(comp_name);\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/conf/LoggingBean.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.beans.config.ConfigurationChangedAware;\nimport tigase.server.Packet;\nimport tigase.server.monitor.MonitorRuntime;\nimport tigase.util.log.LogFormatter;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.*;\nimport java.util.logging.*;\nimport java.util.stream.Stream;\n\n/**\n * Created by andrzej on 02.04.2017.\n */\npublic class LoggingBean\n\t\timplements ConfigurationChangedAware {\n\n\t@ConfigField(desc = \"Debug\", alias = \"debug\")\n\tprivate String[] debug;\n\t@ConfigField(desc = \"Debug packages\", alias = \"debug-packages\")\n\tprivate String[] debugPackages;\n\t@ConfigField(desc = \"Handlers\")\n\tprivate HashMap<String, HashMap<String, Object>> handlers = new HashMap<>();\n\t@ConfigField(desc = \"Loggers\")\n\tprivate HashMap<String, HashMap<String, Object>> loggers = new HashMap<>();\n\t@ConfigField(desc = \"Packet full debug\", alias = \"packet-debug-full\")\n\tprivate boolean packetFullDebug = Packet.FULL_DEBUG;\n\t@ConfigField(desc = \"Root handlers\")\n\tprivate String[] rootHandlers = new String[0];\n\t@ConfigField(desc = \"Root level\")\n\tprivate Level rootLevel = Level.CONFIG;\n\t@ConfigField(desc = \"Log thread dump on shutdown\", alias = \"shutdown-thread-dump\")\n\tprivate boolean shutdownThreadDump = true;\n\n\tprivate final static Logger log = Logger.getLogger(LoggingBean.class.getName());\n\n\tpublic LoggingBean() {\n\t\tHashMap<String, HashMap<String, Object>> loggers = new HashMap<>();\n\t\tloggers.computeIfAbsent(\"tigase.kernel.core.Kernel\", (name) -> {\n\t\t\tHashMap<String, Object> props = new HashMap<>();\n\t\t\tprops.put(\"level\", \"CONFIG\");\n\t\t\treturn props;\n\t\t});\n\t\tsetLoggers(loggers);\n\t\tsetHandlers(new HashMap<>());\n\n\t\trootHandlers = new String[]{ConsoleHandler.class.getCanonicalName(), FileHandler.class.getCanonicalName()};\n\t}\n\n\tpublic synchronized Map<String,Level> getPackageLoggingLevels() {\n\t\t// how to merge debug with loggers? shouldn't that be unified??\n\t\t// should we move \"tigase\" to \"debug\" and leave rest with loggers??\n\t\t// we should also prepare a good merging mechanism\n\t\tStream<String> debugStream = Stream.concat(\n\t\t\t\tOptional.ofNullable(debug).map(Arrays::stream).orElse(Stream.empty()).map(s -> \"tigase.\" + s),\n\t\t\t\tOptional.ofNullable(debugPackages).map(Arrays::stream).orElse(Stream.empty()));\n\n\t\tMap<String, Level> result = new HashMap<>();\n\t\tdebugStream.forEach(p -> result.put(p, Level.ALL));\n\n\t\tfor (String p : loggers.keySet()) {\n\t\t\tOptional.ofNullable(loggers.get(p)).map(v -> v.get(\"level\")).filter(String.class::isInstance).map(v -> {\n\t\t\t\ttry {\n\t\t\t\t\treturn Level.parse((String) v);\n\t\t\t\t} catch (IllegalArgumentException ex) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}).filter(Objects::nonNull).ifPresent(level -> result.put(p, level));\n\t\t}\n\n\t\tlog.log(Level.FINE, \"Currently configured loggers: \" + result);\n\n\t\treturn result;\n\t}\n\n\tpublic synchronized void setPackageLoggingLevel(String packageName, Level level) throws RuntimeException {\n\t\tlog.log(Level.CONFIG, \"Setting log level for package: {0} to {1}\", new Object[]{packageName, level});\n\t\tif (packageName.startsWith(\"tigase.\")) {\n\t\t\tString part = packageName.substring(\"tigase.\".length());\n\t\t\tOptional.ofNullable(debug)\n\t\t\t\t\t.map(Arrays::stream)\n\t\t\t\t\t.map(s -> s.filter(name -> !part.equals(name)))\n\t\t\t\t\t.map(s -> s.toArray(String[]::new))\n\t\t\t\t\t.ifPresent(value -> this.debug = value);\n\t\t}\n\t\tOptional.ofNullable(debugPackages)\n\t\t\t\t.map(Arrays::stream)\n\t\t\t\t.map(s -> s.filter(name -> !packageName.equals(name)))\n\t\t\t\t.map(s -> s.toArray(String[]::new))\n\t\t\t\t.ifPresent(value -> this.debugPackages = value);\n\n\t\tHashMap<String, Object> value = loggers.get(packageName);\n\t\tif (value == null) {\n\t\t\tif (level != Level.OFF) {\n\t\t\t\tvalue = new HashMap<>();\n\t\t\t\tvalue.put(\"level\", level.getName());\n\t\t\t\tloggers.put(packageName, value);\n\t\t\t}\n\t\t} else {\n\t\t\tif (level == Level.OFF) {\n\t\t\t\tif (value.size() == 1) {\n\t\t\t\t\tloggers.remove(packageName);\n\t\t\t\t} else {\n\t\t\t\t\tvalue.remove(\"level\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tvalue.put(\"level\", level.getName());\n\t\t\t}\n\t\t}\n\n\t\tbeanConfigurationChanged(Collections.emptySet());\n\t}\n\n\tpublic void setLoggers(HashMap<String, HashMap<String, Object>> loggers) {\n\t\tloggers.compute(\"tigase\", (name, props) -> {\n\t\t\tif (props == null) {\n\t\t\t\tprops = new HashMap<>();\n\t\t\t}\n\t\t\tprops.putIfAbsent(\"useParentHandlers\", true);\n\t\t\treturn props;\n\t\t});\n\t\tthis.loggers = loggers;\n\t}\n\n\tpublic void setHandlers(HashMap<String, HashMap<String, Object>> handlers) {\n\t\thandlers.compute(ConsoleHandler.class.getCanonicalName(), (cls, props) -> {\n\t\t\tif (props == null) {\n\t\t\t\tprops = new HashMap<>();\n\t\t\t}\n\t\t\tprops.putIfAbsent(\"level\", Level.INFO);\n\t\t\tprops.putIfAbsent(\"formatter\", LogFormatter.class.getCanonicalName());\n\t\t\treturn props;\n\t\t});\n\n\t\thandlers.compute(FileHandler.class.getCanonicalName(), (cls, props) -> {\n\t\t\tif (props == null) {\n\t\t\t\tprops = new HashMap<>();\n\t\t\t}\n\t\t\tprops.putIfAbsent(\"level\", Level.ALL);\n\t\t\tprops.putIfAbsent(\"append\", true);\n\t\t\tprops.putIfAbsent(\"count\", 5);\n\t\t\tprops.putIfAbsent(\"limit\", 10000000);\n\t\t\tprops.putIfAbsent(\"formatter\", LogFormatter.class.getCanonicalName());\n\t\t\tprops.putIfAbsent(\"pattern\", \"logs/tigase.log\");\n\t\t\treturn props;\n\t\t});\n\t\tthis.handlers = handlers;\n\t}\n\n\tpublic boolean getPacketFullDebug() {\n\t\treturn Packet.FULL_DEBUG;\n\t}\n\n\tpublic void setPacketFullDebug(boolean packetFullDebug) {\n\t\tthis.packetFullDebug = packetFullDebug;\n\t\tPacket.FULL_DEBUG = packetFullDebug;\n\t}\n\n\t@Override\n\tpublic void beanConfigurationChanged(Collection<String> changedFields) {\n\t\tStringBuilder sb = new StringBuilder();\n\n\t\tsb.append(\".level=\").append(rootLevel.getName()).append(\"\\n\");\n\n\t\tStream.concat(Optional.ofNullable(debug).stream().flatMap(Arrays::stream).map(s -> \"tigase.\" + s),\n\t\t\t\t\t  Optional.ofNullable(debugPackages).stream().flatMap(Arrays::stream))\n\t\t\t\t.forEach(name -> sb.append(name).append(\".level=\").append(Level.ALL).append(\"\\n\"));\n\n\t\tloggers.forEach((name, props) -> {\n\t\t\tprops.forEach((key, value) -> {\n\t\t\t\tlog.log(Level.CONFIG, \"Setting log level for loggerName: {0} to: {1}\", new Object[]{name, props});\n\t\t\t\tsb.append(name).append(\".\").append(key).append(\"=\");\n\t\t\t\tif (value instanceof Collection) {\n\t\t\t\t\tCollection col = (Collection) value;\n\t\t\t\t\tboolean first = true;\n\t\t\t\t\tfor (Object item : col) {\n\t\t\t\t\t\tif (!first) {\n\t\t\t\t\t\t\tsb.append(\" \");\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfirst = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsb.append(item);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tsb.append(value);\n\t\t\t\t}\n\t\t\t\tsb.append(\"\\n\");\n\t\t\t});\n\t\t});\n\t\thandlers.forEach((name, props) -> {\n\t\t\tprops.forEach((key, value) -> {\n\t\t\t\tsb.append(name).append(\".\").append(key).append(\"=\").append(value).append(\"\\n\");\n\t\t\t});\n\t\t});\n\t\tsb.append(\"handlers=\");\n\t\tif (rootHandlers != null) {\n\t\t\tfor (int i = 0; i < rootHandlers.length; i++) {\n\t\t\t\tif (i > 0) {\n\t\t\t\t\tsb.append(\" \");\n\t\t\t\t}\n\t\t\t\tsb.append(rootHandlers[i]);\n\t\t\t}\n\t\t}\n\n\t\tbyte[] data = sb.toString().getBytes(StandardCharsets.UTF_8);\n\t\ttry (ByteArrayInputStream in = new ByteArrayInputStream(data)) {\n\t\t\tLogManager.getLogManager().reset();\n\t\t\tLogManager.getLogManager().updateConfiguration(in, (k) -> k.endsWith(\".handlers\")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  ? ((o, n) -> (o == null ? n : o))\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  : ((o, n) -> n));\n\t\t\tlog.log(Level.CONFIG, \"Initialised LogManager with configuration: {0}\", new Object[]{sb});\n\t\t} catch (IOException ex) {\n\t\t\tthrow new RuntimeException(\"Failed to load logging configuration\", ex);\n\t\t}\n\t}\n\n\tpublic boolean isShutdownThreadDump() {\n\t\treturn MonitorRuntime.getMonitorRuntime().isShutdownThreadDump();\n\t}\n\n\tpublic void setShutdownThreadDump(boolean shutdownThreadDump) {\n\t\tMonitorRuntime.getMonitorRuntime().setShutdownThreadDump(shutdownThreadDump);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/conf/MonitoringBeanIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\n/**\n * Created by andrzej on 13.10.2016.\n */\npublic interface MonitoringBeanIfc {\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/conf/MonitoringSetupIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\n/**\n * Created: Jan 10, 2009 8:43:04 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface MonitoringSetupIfc {\n\n\tpublic enum MONITOR {\n\t\tjmx,\n\t\thttp,\n\t\tsnmp;\n\t}\n\n\tvoid initMonitoring(String settings, String configDir);\n\n\tvoid initializationCompleted();\n\n\tvoid putMXBean(String objName, Object bean);\n\n\tObject getMXBean(String objName);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/conf/OldConfigHolder.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\nimport tigase.db.RepositoryFactory;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.ext.AbstractCompDBRepository;\nimport tigase.server.ext.ComponentProtocol;\nimport tigase.server.xmppsession.SessionManagerConfig;\nimport tigase.util.reflection.ClassUtilBean;\nimport tigase.util.repository.DataTypes;\nimport tigase.xmpp.XMPPImplIfc;\nimport tigase.xmpp.XMPPProcessor;\n\nimport java.io.*;\nimport java.lang.reflect.Modifier;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport static tigase.conf.ConfigHolder.renameIfExists;\nimport static tigase.conf.Configurable.GEN_SM_PLUGINS;\nimport static tigase.conf.Configurable.GEN_TEST;\nimport static tigase.conf.ConfiguratorAbstract.PROPERTY_FILENAME_PROP_DEF;\nimport static tigase.conf.ConfiguratorAbstract.PROPERTY_FILENAME_PROP_KEY;\n\n/**\n * Created by andrzej on 10.07.2017.\n */\npublic class OldConfigHolder {\n\n\tprivate static final Logger log = Logger.getLogger(OldConfigHolder.class.getCanonicalName());\n\n\tpublic enum Format {\n\t\tdsl,\n\t\tproperties\n\t}\n\tprivate List<String> output = new ArrayList<>();\n\tprivate Path[] propertyFileNames = null;\n\tprivate Map<String, Object> props = new LinkedHashMap<>();\n\n\tpublic Optional<String[]> getOutput() {\n\t\tif (output.isEmpty()) {\n\t\t\treturn Optional.empty();\n\t\t}\n\t\treturn Optional.ofNullable(output.toArray(new String[output.size()]));\n\t}\n\n\tpublic void convert(String[] args, Path tdslPath) throws IOException, ConfigReader.ConfigException {\n\t\tList<String> settings = new LinkedList<>();\n\t\tConfiguratorAbstract.parseArgs(props, settings, args);\n\t\tprops.remove(GEN_TEST);\n\n\t\tif (detectPathAndFormat() == Format.dsl) {\n\t\t\tOptional<Path> backup = Optional.empty();\n\t\t\tif (Files.exists(tdslPath)) {\n\t\t\t\tbackup = Optional.ofNullable(ConfigHolder.backupOldConfigFile(tdslPath));\n\t\t\t}\n\t\t\tFiles.move(propertyFileNames[0], tdslPath);\n\t\t\tlogOutput(\"Configuration file {0} was updated to match current configuration format and renamed to {1}.\",\n\t\t\t\t\t  propertyFileNames[0], tdslPath);\n\t\t\tbackup.ifPresent(\n\t\t\t\t\tbackupPath -> logOutput(\"Previous version of a configuration file was saved as {0}\", backupPath));\n\t\t\treturn;\n\t\t}\n\n\t\tif (propertyFileNames.length == 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tloadFromPropertyFiles();\n\t\tconvertFromOldFormat();\n\n\t\tprops = ConfigWriter.buildTree(props);\n\t\tprops.remove(\"--property-file\");\n\t\tprops.remove(\"--config-file\");\n\n\t\tif (Files.exists(tdslPath)) {\n\t\t\tPath backup = ConfigHolder.backupOldConfigFile(tdslPath);\n\t\t\tlogOutput(\"Existing DSL config file {0} was renamed to {1} as conversion from property files was started.\",\n\t\t\t\t\t  tdslPath, backup);\n\t\t}\n\n\t\tnew ConfigWriter().write(tdslPath.toFile(), props);\n\t\tlogOutput(\"Configuration files {0} were updated to DSL configuration format and saved as {1}.\",\n\t\t\t\t  Arrays.stream(propertyFileNames).map(path -> path.toString()).collect(Collectors.joining()),\n\t\t\t\t  tdslPath);\n\n\t\tfor (Path path : propertyFileNames) {\n\t\t\tPath backup = ConfigHolder.backupOldConfigFile(path);\n\t\t\tlogOutput(\"Old configuration file {0} was renamed to {1}.\", path, backup);\n\t\t}\n\t}\n\n\tprotected Map<String, Object> getProperties() {\n\t\treturn props;\n\t}\n\n\tprotected Format detectPathAndFormat() {\n\t\tString property_filenames = (String) props.remove(PROPERTY_FILENAME_PROP_KEY);\n\t\tif (property_filenames == null) {\n\t\t\tproperty_filenames = PROPERTY_FILENAME_PROP_DEF;\n\t\t\tlog.log(Level.FINEST, \"No property file not specified! Using default one {0}\", property_filenames);\n\t\t}\n\n\t\tif (property_filenames != null) {\n\t\t\tString[] prop_filenames = property_filenames.split(\",\");\n\n\t\t\tif (prop_filenames.length == 1) {\n\t\t\t\tFile f = new File(prop_filenames[0]);\n\t\t\t\tif (!f.exists()) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Provided property file {0} does NOT EXISTS! Trying to use default one {1}\",\n\t\t\t\t\t\t\tnew String[]{f.getAbsolutePath(), PROPERTY_FILENAME_PROP_DEF});\n\t\t\t\t\tprop_filenames[0] = PROPERTY_FILENAME_PROP_DEF;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpropertyFileNames = Arrays.stream(prop_filenames)\n\t\t\t\t\t.map(filename -> Paths.get(filename))\n\t\t\t\t\t.filter(path -> Files.exists(path))\n\t\t\t\t\t.toArray(x -> new Path[x]);\n\n\t\t\tfor (Path path : propertyFileNames) {\n\t\t\t\tFile file = path.toFile();\n\t\t\t\ttry (BufferedReader reader = new BufferedReader(new FileReader(file))) {\n\t\t\t\t\tString line;\n\t\t\t\t\twhile ((line = reader.readLine()) != null) {\n\t\t\t\t\t\tif (line.startsWith(\"--user-db\")) {\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (line.contains(\"{\")) {\n\t\t\t\t\t\t\tif (line.contains(\"{clusterNode}\")) {\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (line.contains(\"{ call\") && !(line.contains(\"\\'{ call\") || line.contains(\"\\\"{ call\"))) {\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn Format.dsl;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Error detecting paths script\", e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn Format.properties;\n\t}\n\n\tprotected Map<String, Object> loadFromPropertyStrings(List<String> settings) {\n\t\tfor (String propString : settings) {\n\t\t\tint idx_eq = propString.indexOf('=');\n\n\t\t\t// String key = prop[0].trim();\n\t\t\t// Object val = prop[1];\n\t\t\tString key = propString.substring(0, idx_eq).trim();\n\t\t\tString valStr = propString.substring(idx_eq + 1);\n\t\t\tif (valStr != null) {\n\t\t\t\tvalStr = valStr.trim();\n\t\t\t}\n\n\t\t\tObject val = valStr;\n\t\t\tif (key.matches(\".*\\\\[[LISBlisb]\\\\]$\")) {\n\t\t\t\tchar c = key.charAt(key.length() - 2);\n\n\t\t\t\tkey = key.substring(0, key.length() - 3);\n\t\t\t\t// decoding value for basckward compatibility\n\t\t\t\tif (val != null) {\n\t\t\t\t\tval = DataTypes.decodeValueType(c, valStr);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tprops.put(key, val);\n\t\t}\n\t\treturn props;\n\t}\n\n\tprotected void convertFromOldFormat() throws ConfigReader.ConfigException {\n\t\t// converting old component configuration to new one\n\t\tMap<String, Object> toAdd = new HashMap<>();\n\t\tList<String> toRemove = new ArrayList<>();\n\n\t\t//List<String> dataSourceNames = new ArrayList<>();\n\t\tMap<String, Map<String, Object>> dataSources = new HashMap<>();\n\n\t\tPattern commandsPattern = Pattern.compile(\"^([^\\\\/]+)\\\\/command\\\\/(.+)$\");\n\t\tPattern processorsPattern = Pattern.compile(\"^([^\\\\/]+)\\\\/processors\\\\/(.+)$\");\n\n\t\tif (props.containsKey(RepositoryFactory.AUTH_DOMAIN_POOL_CLASS)) {\n\t\t\tthrow new ConfigReader.ConfigException(\n\t\t\t\t\t\"Cannot convert property \" + RepositoryFactory.AUTH_DOMAIN_POOL_CLASS +\n\t\t\t\t\t\t\t\"!\\nPlease check if provided class is compatible with Tigase XMPP Server data sources. If so, then please remove this property, convert the configuration file and then manually modify configuration.\");\n\t\t}\n\n\t\tif (props.containsKey(RepositoryFactory.USER_DOMAIN_POOL_CLASS)) {\n\t\t\tthrow new ConfigReader.ConfigException(\n\t\t\t\t\t\"Cannot convert property \" + RepositoryFactory.USER_DOMAIN_POOL_CLASS +\n\t\t\t\t\t\t\t\"!\\nPlease check if provided class is compatible with Tigase XMPP Server data sources. If so, then please remove this property, convert the configuration file and then manually modify configuration.\");\n\t\t}\n\n\t\tObject defDataPoolSize = props.remove(RepositoryFactory.DATA_REPO_POOL_SIZE);\n\t\tObject defAuthRepoPool = props.remove(RepositoryFactory.AUTH_REPO_POOL_SIZE);\n\n\t\tprops.forEach((k, v) -> {\n\t\t\tif (k.equals(\"config-type\")) {\n\t\t\t\tswitch ((String) v) {\n\t\t\t\t\tcase \"--gen-config-all\":\n\t\t\t\t\tcase \"--gen-config-def\":\n\t\t\t\t\tcase \"--gen-config-default\":\n\t\t\t\t\t\ttoAdd.put(k, \"default\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"--gen-config-sm\":\n\t\t\t\t\t\ttoAdd.put(k, \"session-manager\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"--gen-config-cs\":\n\t\t\t\t\t\ttoAdd.put(k, \"connection-managers\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"--gen-config-comp\":\n\t\t\t\t\t\ttoAdd.put(k, \"component\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (k.startsWith(\"--comp-name\")) {\n\t\t\t\tString suffix = k.replace(\"--comp-name-\", \"\");\n\t\t\t\tString name = ((String) v).trim();\n\t\t\t\tString cls = ((String) props.get(\"--comp-class-\" + suffix)).trim();\n\t\t\t\tString active = \"true\";\n\t\t\t\ttoAdd.put(name + \"/class\", cls);\n\t\t\t\ttoAdd.put(name + \"/active\", active);\n\n\t\t\t\tif (\"tigase.http.HttpMessageReceiver\".equals(cls)) {\n\t\t\t\t\tprops.entrySet().stream().filter(e -> e.getKey().startsWith(name + \"/http/\")).forEach(e -> {\n\t\t\t\t\t\tString key = e.getKey().replace(name + \"/http/\", \"\");\n\t\t\t\t\t\tMap<String, Object> httpServerCfg = (Map<String, Object>) toAdd.computeIfAbsent(\"httpServer\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(k1) -> new HashMap<>());\n\t\t\t\t\t\tif (key.equals(\"server-class\")) {\n\t\t\t\t\t\t\thttpServerCfg.put(\"class\", e.getValue());\n\t\t\t\t\t\t\ttoRemove.add(e.getKey());\n\t\t\t\t\t\t} else if (key.equals(\"ports\")) {\n\t\t\t\t\t\t\tMap<String, Object> connections = (Map<String, Object>) httpServerCfg.computeIfAbsent(\n\t\t\t\t\t\t\t\t\t\"connections\", (k1) -> new HashMap<>());\n\t\t\t\t\t\t\tfor (int port : ((int[]) e.getValue())) {\n\t\t\t\t\t\t\t\tconnections.compute(String.valueOf(port), (k1, v1) -> {\n\t\t\t\t\t\t\t\t\tif (v1 == null) {\n\t\t\t\t\t\t\t\t\t\tv1 = new HashMap<>();\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t((Map) v1).put(\"active\", true);\n\t\t\t\t\t\t\t\t\treturn v1;\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!Arrays.stream((int[]) e.getValue()).filter(v1 -> v1 == 8080).findAny().isPresent()) {\n\t\t\t\t\t\t\t\tconnections.compute(String.valueOf(8080), (k1, v1) -> {\n\t\t\t\t\t\t\t\t\tif (v1 == null) {\n\t\t\t\t\t\t\t\t\t\tv1 = new HashMap<>();\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t((Map) v1).put(\"active\", true);\n\t\t\t\t\t\t\t\t\treturn v1;\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\ttoRemove.add(e.getKey());\n\t\t\t\t\t\t} else if (key.endsWith(\"/socket\") || key.endsWith(\"/domain\")) {\n\t\t\t\t\t\t\tMap<String, Object> connections = (Map<String, Object>) httpServerCfg.computeIfAbsent(\n\t\t\t\t\t\t\t\t\t\"connections\", (k1) -> new HashMap<>());\n\t\t\t\t\t\t\tString[] parts = key.split(\"/\");\n\t\t\t\t\t\t\tString port = parts[0];\n\t\t\t\t\t\t\tMap<String, Object> portCfg = (Map<String, Object>) connections.computeIfAbsent(port,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(k1) -> new HashMap<>());\n\t\t\t\t\t\t\tportCfg.put(parts[1], e.getValue());\n\n\t\t\t\t\t\t\ttoRemove.add(e.getKey());\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\thttpServerCfg.put(key, e.getValue());\n\t\t\t\t\t\t\ttoRemove.add(e.getKey());\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t}\n\t\t\tif (k.startsWith(\"--user-db\") || k.startsWith(\"--auth-db\") || k.startsWith(\"--amp-repo\")) {\n\t\t\t\tString domain = \"default\";\n\t\t\t\tif (k.endsWith(\"]\")) {\n\t\t\t\t\tdomain = k.substring(k.indexOf('[') + 1, k.length() - 1);\n\t\t\t\t}\n\t\t\t\ttoRemove.add(k);\n\n\t\t\t\tMap<String, Object> ds = dataSources.computeIfAbsent(domain, key -> new HashMap<>());\n\t\t\t\tif (k.startsWith(\"--user-db-uri\")) {\n\t\t\t\t\tds.put(\"user-uri\", (String) v);\n\t\t\t\t} else if (k.startsWith(\"--user-db\")) {\n\t\t\t\t\tds.put(\"user-type\", (String) v);\n\t\t\t\t}\n\t\t\t\tif (k.startsWith(\"--auth-db-uri\")) {\n\t\t\t\t\tds.put(\"auth-uri\", (String) v);\n\t\t\t\t} else if (k.startsWith(\"--auth-db\")) {\n\t\t\t\t\tds.put(\"auth-type\", (String) v);\n\t\t\t\t}\n\t\t\t\tif (k.startsWith(\"--amp-repo-uri\")) {\n\t\t\t\t\tds.put(\"amp-uri\", (String) v);\n\t\t\t\t} else if (k.startsWith(\"--amp-repo-class\")) {\n\t\t\t\t\tds.put(\"amp-type\", (String) v);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (k.equals(\"--sm-cluster-strategy-class\")) {\n\t\t\t\ttoAdd.put(\"sess-man/strategy/class\", v.toString());\n\t\t\t\ttoAdd.put(\"sess-man/strategy/active\", \"true\");\n\t\t\t\ttoRemove.add(k);\n\t\t\t}\n\t\t\tif (k.contains(\"pubsub-repo-url\")) {\n\t\t\t\tprops.put(\"dataSource/pubsub/uri\", v);\n\t\t\t\tprops.put(\"dataSource/pubsub/active\", \"true\");\n\t\t\t\tprops.put(\"pubsub/dao/default/data-source\", \"pubsub\");\n\t\t\t\ttoRemove.add(k);\n\t\t\t}\n\t\t\tif (k.startsWith(\"sess-man/plugins-conf/\")) {\n\t\t\t\tif (k.equals(\"sess-man/plugins-conf/dynamic-roster-classes\")) {\n\n\t\t\t\t\ttoAdd.put(\"sess-man/dynamic-rosters/active\", \"true\");\n\t\t\t\t\tstringToStreamOfStrings(v.toString()).forEach(clazzName -> {\n\t\t\t\t\t\tString[] parts = clazzName.split(\"\\\\.\");\n\t\t\t\t\t\ttoAdd.put(\"sess-man/dynamic-rosters/\" + parts[parts.length - 1] + \"/active\", \"true\");\n\t\t\t\t\t\ttoAdd.put(\"sess-man/dynamic-rosters/\" + parts[parts.length - 1] + \"/class\", clazzName);\n\t\t\t\t\t});\n\t\t\t\t\ttoRemove.add(k);\n\t\t\t\t} else {\n\t\t\t\t\ttoRemove.add(k);\n\t\t\t\t\tk = k.replace(\"/plugins-conf/\", \"/\");\n\t\t\t\t\tif (k.endsWith(\"/amp/msg-offline\")) {\n\t\t\t\t\t\ttoAdd.put(k.replace(\"/msg-offline\", \"/msgoffline/active\"), v);\n\t\t\t\t\t} else if (k.endsWith(\"/presence-state/disable-roster-lazy-loading\")) {\n\t\t\t\t\t\tk = k.replace(\"/disable-roster-lazy-loading\", \"/enable-roster-lazy-loading\");\n\t\t\t\t\t\tif (\"true\".equals(v)) {\n\t\t\t\t\t\t\ttoAdd.put(k, false);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttoAdd.put(k, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (k.equals(\"/presence-state/extended-presence-processors\")) {\n\t\t\t\t\t\tString k1 = k.replace(\"presence-state/extended-presence-processors\", \"\");\n\t\t\t\t\t\tfinal String prefix = k;\n\t\t\t\t\t\tstringToStreamOfStrings(v.toString()).forEach(ext -> {\n\t\t\t\t\t\t\tString[] parts = ext.split(\".\");\n\t\t\t\t\t\t\ttoAdd.put(prefix + \"/\" + parts[parts.length - 1] + \"/class\", ext);\n\t\t\t\t\t\t\ttoAdd.put(prefix + \"/\" + parts[parts.length - 1] + \"/active\", true);\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttoAdd.put(k, v);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (k.startsWith(\"stats/stats-archiv/\")) {\n\t\t\t\ttoRemove.add(k);\n\t\t\t\ttoAdd.put(k.replace(\"stats/stats-archiv/\", \"stats/\"), v);\n\t\t\t}\n\t\t\tif (k.contains(\"/command/\")) {\n\t\t\t\tMatcher m = commandsPattern.matcher(k);\n\t\t\t\tif (m.matches()) {\n\t\t\t\t\tString cmp = m.group(1);\n\t\t\t\t\tString cmdId = m.group(2).replace(\"\\\\:\", \":\");\n\t\t\t\t\tMap<String, Object> acls = (Map<String, Object>) toAdd.computeIfAbsent(cmp + \"/commands\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   (k1) -> new HashMap<>());\n\t\t\t\t\tList<String> values = stringToStreamOfStrings(v.toString()).map(x -> {\n\t\t\t\t\t\tint idx = x.indexOf(':');\n\t\t\t\t\t\tif (idx > 0) {\n\t\t\t\t\t\t\treturn x.substring(idx + 1);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn x;\n\t\t\t\t\t}).collect(Collectors.toList());\n\t\t\t\t\tacls.put(cmdId, values.size() > 1 ? values : values.get(0));\n\t\t\t\t\ttoRemove.add(k);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (k.equals(\"--\" + SessionManagerConfig.SM_THREADS_POOL_PROP_KEY)) {\n\t\t\t\tprops.put(\"sess-man/\" + SessionManagerConfig.SM_THREADS_POOL_PROP_KEY, v);\n\t\t\t}\n\t\t\tif (k.endsWith(\"/processors\")) {\n\t\t\t\tString cmp = k.replace(\"/processors\", \"\");\n\t\t\t\tArrays.stream((String[]) v).forEach(procId -> {\n\t\t\t\t\ttoAdd.compute(cmp, (k1, v1) -> {\n\t\t\t\t\t\tif (v1 == null) {\n\t\t\t\t\t\t\tv1 = new HashMap<>();\n\t\t\t\t\t\t}\n\t\t\t\t\t\t((Map<String, Object>) v1).compute(procId, (k2, v2) -> {\n\t\t\t\t\t\t\tif (v2 == null) {\n\t\t\t\t\t\t\t\tv2 = new HashMap<>();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t((Map) v2).put(\"active\", true);\n\t\t\t\t\t\t\treturn v2;\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn v1;\n\t\t\t\t\t});\n\t\t\t\t\ttoAdd.put(cmp + \"/\" + procId + \"/active\", true);\n\t\t\t\t});\n\t\t\t\ttoRemove.add(k);\n\t\t\t}\n\t\t\tif (k.contains(\"/processors/\")) {\n\t\t\t\tMatcher m = processorsPattern.matcher(k);\n\t\t\t\tif (m.matches()) {\n\t\t\t\t\tString cmp = m.group(1);\n\t\t\t\t\tString key = m.group(2).replace(\"\\\\:\", \":\");\n\t\t\t\t\tString[] parts = key.split(\"/\");\n\t\t\t\t\tMap<String, Object> cmpCfg = (Map<String, Object>) toAdd.computeIfAbsent(cmp,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t (k1) -> new HashMap<>());\n\t\t\t\t\tMap<String, Object> procCfg = (Map<String, Object>) cmpCfg.computeIfAbsent(parts[0],\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   (k1) -> new HashMap<>());\n\t\t\t\t\tprocCfg.put(parts[1], v);\n\t\t\t\t}\n\t\t\t\ttoRemove.add(k);\n\t\t\t}\n\t\t\tif (k.startsWith(\"basic-conf/logging/\")) {\n\t\t\t\tString t = k.replace(\"basic-conf/logging/\", \"\");\n\t\t\t\tif (t.contains(\".\")) {\n\t\t\t\t\tint idx = t.lastIndexOf('.');\n\t\t\t\t\tif (idx > 0) {\n\t\t\t\t\t\tString group = t.substring(0, idx);\n\t\t\t\t\t\tString key = t.substring(idx + 1);\n\t\t\t\t\t\tMap<String, Object> logging = (Map<String, Object>) toAdd.computeIfAbsent(\"logging\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  (k1) -> new HashMap<>());\n\t\t\t\t\t\tMap<String, Object> handler = (Map<String, Object>) logging.computeIfAbsent(group,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(k1) -> new HashMap<>());\n\t\t\t\t\t\thandler.put(key, v);\n\t\t\t\t\t\ttoRemove.add(k);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (k.startsWith(\"basic-conf/user-repo-params/\")) {\n\t\t\t\tString[] tmp = k.replace(\"basic-conf/user-repo-params/\", \"\").split(\"/\");\n\t\t\t\tString domain = \"default\";\n\t\t\t\tString prop = null;\n\t\t\t\tif (tmp.length == 1) {\n\t\t\t\t\tprop = tmp[0];\n\t\t\t\t} else {\n\t\t\t\t\tdomain = tmp[0];\n\t\t\t\t\tprop = tmp[1];\n\t\t\t\t}\n\t\t\t\tMap<String, Object> ds = dataSources.computeIfAbsent(domain, key -> new HashMap<>());\n\t\t\t\tMap<String, Object> authParams = (Map<String, Object>) ds.computeIfAbsent(\"user-params\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  (x) -> new HashMap<String, Object>());\n\t\t\t\tauthParams.put(prop, v.toString());\n\t\t\t\ttoRemove.add(k);\n\t\t\t}\n\t\t\tif (k.startsWith(\"basic-conf/auth-repo-params/\")) {\n\t\t\t\tString[] tmp = k.replace(\"basic-conf/auth-repo-params/\", \"\").split(\"/\");\n\t\t\t\tString domain = \"default\";\n\t\t\t\tString prop = null;\n\t\t\t\tif (tmp.length == 1) {\n\t\t\t\t\tprop = tmp[0];\n\t\t\t\t} else {\n\t\t\t\t\tdomain = tmp[0];\n\t\t\t\t\tprop = tmp[1];\n\t\t\t\t}\n\t\t\t\tMap<String, Object> ds = dataSources.computeIfAbsent(domain, key -> new HashMap<>());\n\t\t\t\tMap<String, Object> authParams = (Map<String, Object>) ds.computeIfAbsent(\"auth-params\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  (x) -> new HashMap<String, Object>());\n\t\t\t\tauthParams.put(prop, v.toString());\n\t\t\t\ttoRemove.add(k);\n\t\t\t}\n\t\t});\n\n//\t\t\tList<String> userDbDomains = new ArrayList<>();\n//\t\t\tList<String> authDbDomains = new ArrayList<>();\n\t\tdataSources.forEach((domain, cfg) -> {\n\t\t\tString userType = (String) cfg.get(\"user-type\");\n\t\t\tString userUri = (String) cfg.get(\"user-uri\");\n\t\t\tString authType = (String) cfg.get(\"auth-type\");\n\t\t\tString authUri = (String) cfg.get(\"auth-uri\");\n\t\t\tString ampUri = (String) cfg.get(\"amp-uri\");\n\t\t\tString ampType = (String) cfg.get(\"amp-type\");\n\t\t\tMap<String, Object> authParams = (Map<String, Object>) cfg.get(\"auth-params\");\n\t\t\tMap<String, Object> userParams = (Map<String, Object>) cfg.get(\"user-params\");\n\n\t\t\tString sourcePrefix = \"dataSource/\" + domain + \"/\";\n\t\t\tString authPrefix = \"authRepository/\" + domain + \"/\";\n\t\t\tString userPrefix = \"userRepository/\" + domain + \"/\";\n\n\t\t\tif (userUri != null) {\n\t\t\t\tprops.put(\"dataSource/\" + domain + \"/uri\", userUri);\n\t\t\t\tprops.put(\"dataSource/\" + domain + \"/active\", \"true\");\n\t\t\t\tif (defDataPoolSize != null) {\n\t\t\t\t\tprops.put(sourcePrefix + \"pool-size\", defDataPoolSize);\n\t\t\t\t}\n\t\t\t\tprops.put(\"userRepository/\" + domain + \"/active\", \"true\");\n\t\t\t\tprops.put(\"authRepository/\" + domain + \"/active\", \"true\");\n\t\t\t}\n\n\t\t\tif ((authUri != null) && (userUri == null || !userUri.contains(authUri))) {\n\t\t\t\tprops.put(\"dataSource/\" + domain + \"-auth/uri\", authUri);\n\t\t\t\tprops.put(\"dataSource/\" + domain + \"-auth/active\", \"true\");\n\t\t\t\tif (defDataPoolSize != null) {\n\t\t\t\t\tprops.put(\"dataSource/\" + domain + \"-auth/pool-size\", defDataPoolSize);\n\t\t\t\t}\n\t\t\t\tprops.put(\"authRepository/\" + domain + \"/data-source\", domain + \"-auth\");\n\t\t\t\tprops.put(\"authRepository/\" + domain + \"/active\", \"true\");\n\t\t\t}\n\n\t\t\tif (ampUri != null && (userUri == null || !userUri.equals(ampUri))) {\n\t\t\t\tprops.put(\"dataSource/\" + domain + \"-amp/uri\", ampUri);\n\t\t\t\tprops.put(\"dataSource/\" + domain + \"-amp/active\", \"true\");\n\t\t\t\tprops.put(\"msgRepository/\" + domain + \"/data-source\", domain + \"-amp\");\n\t\t\t\tprops.put(\"msgRepository/\" + domain + \"/active\", \"true\");\n\t\t\t}\n\n\t\t\tif (userType != null && !userType.equals(\"mysql\") && !userType.equals(\"pgsql\") &&\n\t\t\t\t\t!userType.equals(\"derby\") && !userType.equals(\"sqlserver\")) {\n\t\t\t\tString cls = RepositoryFactory.getRepoClass(userType);\n\t\t\t\tprops.put(\"userRepository/\" + domain + \"/cls\", cls);\n\t\t\t}\n\t\t\tif (authType != null && !authType.equals(\"tigase-custom-auth\")) {\n\t\t\t\tString cls = RepositoryFactory.getRepoClass(authType);\n\t\t\t\tprops.put(\"authRepository/\" + domain + \"/cls\", cls);\n\t\t\t}\n\t\t\tif (ampType != null) {\n\t\t\t\tprops.put(\"msgRepository/\" + domain + \"/cls\", ampType);\n\t\t\t}\n\t\t\tif (authParams != null) {\n\t\t\t\tauthParams.forEach((k, v) -> {\n\t\t\t\t\tif (k.equals(RepositoryFactory.AUTH_REPO_CLASS_PROP_KEY)) {\n\t\t\t\t\t\tprops.put(authPrefix + \"/cls\", v);\n\t\t\t\t\t} else if (k.equals(RepositoryFactory.AUTH_REPO_POOL_CLASS_PROP_KEY)) {\n\t\t\t\t\t\tprops.put(authPrefix + \"/pool-class\", v);\n\t\t\t\t\t} else if (k.equals(RepositoryFactory.AUTH_REPO_POOL_SIZE_PROP_KEY)) {\n\t\t\t\t\t\tprops.put(authPrefix + \"/pool-size\", v);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tprops.put(authPrefix + k, v);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (userParams != null) {\n\t\t\t\tuserParams.forEach((k, v) -> {\n\t\t\t\t\tif (k.equals(RepositoryFactory.USER_REPO_CLASS_PROP_KEY)) {\n\t\t\t\t\t\tprops.put(userPrefix + \"/cls\", v);\n\t\t\t\t\t} else if (k.equals(RepositoryFactory.USER_REPO_POOL_CLASS_PROP_KEY)) {\n\t\t\t\t\t\tprops.put(userPrefix + \"/pool-class\", v);\n\t\t\t\t\t} else if (k.equals(RepositoryFactory.USER_REPO_POOL_SIZE_PROP_KEY)) {\n\t\t\t\t\t\tprops.put(userPrefix + \"/pool-size\", v);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tprops.put(userPrefix + k, v);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (defAuthRepoPool != null) {\n\t\t\t\tprops.putIfAbsent(authPrefix + \"/pool-size\", defAuthRepoPool);\n\t\t\t}\n\t\t});\n\n\t\tString external = (String) props.remove(\"--external\");\n\t\tif (external != null) {\n\t\t\tOptional<String> extCmpName = toAdd.entrySet()\n\t\t\t\t\t.stream()\n\t\t\t\t\t.filter(e -> e.getKey().endsWith(\"/class\") &&\n\t\t\t\t\t\t\te.getValue().equals(ComponentProtocol.class.getCanonicalName()))\n\t\t\t\t\t.map(e -> e.getKey().replace(\"/class\", \"\"))\n\t\t\t\t\t.findFirst();\n\n\t\t\textCmpName.ifPresent(cmpName -> {\n\t\t\t\tString[] components = external.split(\",\");\n\t\t\t\tsaveOldExternalComponentConfigItems(components);\n\t\t\t});\n\t\t\trenameIfExists(props, ComponentProtocol.EXTCOMP_BIND_HOSTNAMES,\n\t\t\t\t\t\t   ComponentProtocol.EXTCOMP_BIND_HOSTNAMES_PROP_KEY,\n\t\t\t\t\t\t   value -> stringToListOfStrings(value.toString()));\n\t\t} else {\n\t\t\tprops.remove(ComponentProtocol.EXTCOMP_BIND_HOSTNAMES);\n\t\t}\n\n\t\tString admins = (String) props.remove(\"--admins\");\n\t\tif (admins != null) {\n\t\t\tprops.put(\"admins\", stringToListOfStrings(admins));\n\t\t}\n\n\t\tString trusted = (String) props.remove(\"--trusted\");\n\t\tif (trusted != null) {\n\t\t\tprops.put(\"trusted\", stringToListOfStrings(trusted));\n\t\t}\n\n\t\tString maxQueueSize = (String) props.remove(\"--max-queue-size\");\n\t\tif (maxQueueSize != null) {\n\t\t\tprops.put(\"max-queue-size\", Integer.valueOf(maxQueueSize.trim()));\n\t\t}\n\n\t\tString monitoring = (String) props.remove(\"--monitoring\");\n\t\tif (monitoring != null) {\n\t\t\tprops.put(\"monitoring/active\", \"true\");\n\t\t\tstringToStreamOfStrings(monitoring).forEach(e -> {\n\t\t\t\tString[] tmp = e.split(\":\");\n\t\t\t\tif (tmp.length == 2) {\n\t\t\t\t\tprops.put(\"monitoring/\" + tmp[0] + \"/active\", \"true\");\n\t\t\t\t\tprops.put(\"monitoring/\" + tmp[0] + \"/port\", tmp[1]);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tString statsArchiv = (String) props.remove(\"--stats-archiv\");\n\t\tif (statsArchiv != null) {\n\t\t\tstringToStreamOfStrings(statsArchiv).forEach(archiver -> {\n\t\t\t\tString[] parts = archiver.split(\":\");\n\t\t\t\tString k = \"stats/\" + parts[1];\n\t\t\t\tprops.put(k + \"/class\", parts[0]);\n\t\t\t\tprops.put(k + \"/active\", \"true\");\n\t\t\t\tif (parts.length > 2) {\n\t\t\t\t\tprops.put(k + \"/frequency\", parts[2]);\n\t\t\t\t}\n\t\t\t\tif (\"tigase.mongo.stats.CounterDataLoggerMongo\".equals(parts[1]) ||\n\t\t\t\t\t\t\"tigase.stats.CounterDataLogger\".equals(parts[1])) {\n\t\t\t\t\tprops.putIfAbsent(k + \"/db-url\", props.get(\"dataSource/default/uri\"));\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\tString statsHistory = (String) props.remove(\"--stats-history\");\n\t\tif (statsHistory != null) {\n\t\t\tString[] parts = statsHistory.split(\",\");\n\t\t\tprops.putIfAbsent(\"stats/stats-history-size\", parts[0]);\n\t\t\tif (parts.length > 1) {\n\t\t\t\tprops.putIfAbsent(\"stats/stats-update-interval\", parts[1]);\n\t\t\t}\n\t\t}\n\n\t\tIterator<Map.Entry<String, Object>> it = props.entrySet().iterator();\n\t\twhile (it.hasNext()) {\n\t\t\tMap.Entry<String, Object> e = it.next();\n\t\t\tif (e.getKey().startsWith(\"--comp-\")) {\n\t\t\t\tit.remove();\n\t\t\t}\n\t\t}\n\n\t\tfor (String k : toRemove) {\n\t\t\tprops.remove(k);\n\t\t}\n\n\t\tprops.putAll(toAdd);\n\n\t\t// converting list of sess-man processors from --sm-plugins to sess-man/beans\n\t\t// and converting concurrency settings as well\n\t\tif (!\"connection-managers\".equals(props.get(\"config-type\")) && !\"component\".equals(props.get(\"config-type\"))) {\n\t\t\tprops.put(\"sess-man/active\", \"true\");\n\t\t}\n\t\tString plugins = (String) props.remove(GEN_SM_PLUGINS);\n\t\tif (plugins != null) {\n\t\t\tSet<XMPPProcessor> knownProcessors = ClassUtilBean.getInstance()\n\t\t\t\t\t.getAllClasses()\n\t\t\t\t\t.stream()\n\t\t\t\t\t.filter(cls -> XMPPProcessor.class.isAssignableFrom(cls) &&\n\t\t\t\t\t\t\t!(Modifier.isAbstract(cls.getModifiers()) || Modifier.isInterface(cls.getModifiers())))\n\t\t\t\t\t.map(cls -> {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\treturn (XMPPProcessor) cls.newInstance();\n\t\t\t\t\t\t} catch (InstantiationException | IllegalAccessException e) {\n\t\t\t\t\t\t\tlog.log(Level.WARNING, \"Failed to instanticate\");\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.collect(Collectors.toSet());\n\n\t\t\tMap<String, String> plugins_concurrency = new HashMap<>();\n\t\t\tstringToStreamOfStrings(plugins).forEach(part -> {\n\t\t\t\tString[] tmp = part.split(\"=\");\n\t\t\t\tfinal String name = (tmp[0].charAt(0) == '+' || tmp[0].charAt(0) == '-') ? tmp[0].substring(1) : tmp[0];\n\t\t\t\tboolean active = !tmp[0].startsWith(\"-\");\n\n\t\t\t\tprops.put(\"sess-man/\" + name + \"/active\", String.valueOf(active));\n\n\t\t\t\tif (tmp.length > 1) {\n\t\t\t\t\tplugins_concurrency.put(name, tmp[1]);\n\t\t\t\t}\n\n\t\t\t\ttry {\n\n\t\t\t\t\tXMPPImplIfc proc = knownProcessors.stream()\n\t\t\t\t\t\t\t.filter(p -> p != null && name.equals(p.id()))\n\t\t\t\t\t\t\t.findFirst()\n\t\t\t\t\t\t\t.orElse(null);\n\n\t\t\t\t\tif (proc != null) {\n\t\t\t\t\t\tBean ann = proc.getClass().getAnnotation(Bean.class);\n\t\t\t\t\t\tif (ann == null) {\n\t\t\t\t\t\t\tprops.put(\"sess-man/\" + name + \"/class\", proc.getClass().getCanonicalName());\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"could not find class for processor \" + name);\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tlog.log(Level.WARNING, \"not able to get instance of processor \" + name, ex);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tString concurrency = (String) props.get(SessionManagerConfig.PLUGINS_CONCURRENCY_PROP_KEY);\n\t\t\tif (concurrency != null) {\n\t\t\t\tstringToStreamOfStrings(concurrency).forEach(part -> {\n\t\t\t\t\tString[] tmp = part.split(\"=\");\n\t\t\t\t\tplugins_concurrency.put(tmp[0], tmp[1]);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tfor (Map.Entry<String, String> e : plugins_concurrency.entrySet()) {\n\t\t\t\tString prefix = \"sess-man/\" + e.getKey() + \"/\";\n\t\t\t\tString[] tmp = e.getValue().split(\":\");\n\t\t\t\ttry {\n\t\t\t\t\tprops.put(prefix + \"threadsNo\", Integer.parseInt(tmp[0]));\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Plugin \" + e.getKey() + \" concurrency parsing error for: \" + tmp[0], ex);\n\t\t\t\t}\n\t\t\t\tif (tmp.length > 1) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tprops.put(prefix + \"queueSize\", Integer.parseInt(tmp[1]));\n\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Plugin \" + e.getKey() + \" queueSize parsing error for: \" + tmp[1], ex);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static void saveOldExternalComponentConfigItems(String[] components) {\n\t\tFile f = new File(AbstractCompDBRepository.ITEMS_IMPORT_FILE);\n\t\tif (f.exists()) {\n\t\t\tf.delete();\n\t\t}\n\t\ttry (FileWriter writer = new FileWriter(f)) {\n\t\t\tfor (int i=0; i<components.length; i++) {\n\t\t\t\tif (i > 0) {\n\t\t\t\t\twriter.write('\\n');\n\t\t\t\t}\n\t\t\t\twriter.write(components[i]);\n\t\t\t}\n\t\t} catch (IOException ex) {\n\t\t\t// ignoring...\n\t\t}\n\t}\n\n\tprivate void logOutput(String msg, Object... args) {\n\t\tif (log.isLoggable(Level.CONFIG)) {\n\t\t\tlog.log(Level.CONFIG, msg, args);\n\t\t}\n\t\tif (args.length > 0) {\n\t\t\toutput.add(java.text.MessageFormat.format(msg, args));\n\t\t} else {\n\t\t\toutput.add(msg);\n\t\t}\n\t}\n\n\tprivate void loadFromPropertyFiles() throws ConfigReader.ConfigException {\n\t\tLinkedList<String> settings = new LinkedList<>();\n\t\tfor (Path path : propertyFileNames) {\n\t\t\tConfiguratorAbstract.loadFromPropertiesFiles(path.toString(), props, settings);\n\t\t\tloadFromPropertyStrings(settings);\n\t\t}\n\t}\n\n\tprivate Stream<String> stringToStreamOfStrings(String val) {\n\t\treturn Arrays.stream(val.split(\",\")).map(String::trim);\n\t}\n\n\tprivate List<String> stringToListOfStrings(String val) {\n\t\treturn stringToStreamOfStrings(val).collect(Collectors.toList());\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/conf/SetLoggingCommand.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\nimport tigase.kernel.core.Kernel;\nimport tigase.server.*;\nimport tigase.server.script.AbstractScriptCommand;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.PacketErrorTypeException;\n\nimport javax.script.Bindings;\nimport java.util.Arrays;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Ad-hoc command used to reconfigure logging of Tigase XMPP Server.\n */\npublic class SetLoggingCommand\n\t\textends AbstractScriptCommand {\n\n\tprivate static final Logger log = Logger.getLogger(SetLoggingCommand.class.getCanonicalName());\n\n\tprivate static final Level[] LEVELS = {Level.OFF, Level.SEVERE, Level.WARNING, Level.INFO, Level.CONFIG, Level.FINE,\n\t\t\t\t\t\t\t\t\t\t   Level.FINER, Level.FINEST, Level.ALL};\n\tprivate final Kernel kernel;\n\n\tpublic SetLoggingCommand(Kernel kernel) {\n\t\tthis.kernel = kernel;\n\t\tsuper.init(\"logging-set\", \"Set package logging\", \"Configuration\");\n\t}\n\n\t@Override\n\tpublic Bindings getBindings() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void runCommand(Iq packet, Bindings binds, Queue<Packet> results) {\n\t\ttry {\n\t\t\ttry {\n\t\t\t\tLoggingBean loggingBean = kernel.getParent().getInstance(LoggingBean.class);\n\t\t\t\tString packageName = Command.getFieldValue(packet, \"package-name\");\n\t\t\t\tLevel level = null;\n\t\t\t\tString levelStr = Command.getFieldValue(packet, \"level\");\n\t\t\t\tif (levelStr != null) {\n\t\t\t\t\tlevel = Level.parse(levelStr);\n\t\t\t\t}\n\t\t\t\tif (packageName == null || packageName.isBlank() || level == null) {\n\t\t\t\t\tIq result = (Iq) packet.commandResult(Command.DataType.form);\n\t\t\t\t\tnew Command.Builder(result).addAction(Command.Action.execute)\n\t\t\t\t\t\t\t.addDataForm(Command.DataType.form)\n\t\t\t\t\t\t\t.addTitle(\"Set packet logging\")\n\t\t\t\t\t\t\t.withFields(builder -> {\n\t\t\t\t\t\t\t\tbuilder.addField(DataForm.FieldType.TextSingle, \"package-name\")\n\t\t\t\t\t\t\t\t\t\t.setLabel(\"Package name\")\n\t\t\t\t\t\t\t\t\t\t.setRequired(true)\n\t\t\t\t\t\t\t\t\t\t.setValue(packageName)\n\t\t\t\t\t\t\t\t\t\t.build();\n\t\t\t\t\t\t\t\tbuilder.addField(DataForm.FieldType.ListSingle, \"level\")\n\t\t\t\t\t\t\t\t\t\t.setLabel(\"Level\")\n\t\t\t\t\t\t\t\t\t\t.setRequired(true)\n\t\t\t\t\t\t\t\t\t\t.setOptions(Arrays.stream(LEVELS).map(Level::getName).toArray(String[]::new))\n\t\t\t\t\t\t\t\t\t\t.setValue(levelStr)\n\t\t\t\t\t\t\t\t\t\t.build();\n\t\t\t\t\t\t\t});\n\t\t\t\t\tresults.offer(result);\n\t\t\t\t} else {\n\t\t\t\t\tloggingBean.setPackageLoggingLevel(packageName, level);\n\t\t\t\t\tresults.offer(packet.commandResult(Command.DataType.result));\n\t\t\t\t}\n\t\t\t} catch (Throwable ex) {\n\t\t\t\tif (log.isLoggable(Level.WARNING)) {\n\t\t\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\t\t\t\t\t  \"Execution of command \" + getCommandId() + \" failed! \" + ex.getMessage(), ex);\n\t\t\t\t}\n\t\t\t\tresults.offer(Authorization.INTERNAL_SERVER_ERROR.getResponseMessage(packet, \"Execution of command \" +\n\t\t\t\t\t\tgetCommandId() + \" failed: \" + ex.getMessage(), false));\n\t\t\t}\n\t\t} catch (PacketErrorTypeException ex) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Packet already of type 'error'\");\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/AbstractAuthRepositoryWithCredentials.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport tigase.auth.CredentialsDecoderBean;\nimport tigase.auth.CredentialsEncoderBean;\nimport tigase.auth.credentials.Credentials;\nimport tigase.auth.credentials.entries.PlainCredentialsEntry;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic abstract class AbstractAuthRepositoryWithCredentials\n\t\timplements AuthRepository {\n\n\tprivate static final Logger log = Logger.getLogger(AbstractAuthRepositoryWithCredentials.class.getCanonicalName());\n\n\tprivate CredentialsDecoderBean credentialsDecoder;\n\tprivate CredentialsEncoderBean credentialsEncoder;\n\n\t@Override\n\tpublic String getPassword(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\tCredentials credentials = getCredentials(user, Credentials.DEFAULT_CREDENTIAL_ID);\n\t\tif (credentials != null) {\n\t\t\tCredentials.Entry entry = credentials.getEntryForMechanism(\"PLAIN\");\n\t\t\tif (entry != null && entry instanceof PlainCredentialsEntry) {\n\t\t\t\treturn ((PlainCredentialsEntry) entry).getPassword();\n\t\t\t} else {\n\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\tlog.log(Level.FINE, \"No password in plaintext stored for user {0}, returning null...\", user);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean isMechanismSupported(String domain, String mechanism) {\n\t\tif (\"PLAIN\".equals(mechanism)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (mechanism.endsWith(\"-PLUS\")) {\n\t\t\tmechanism = mechanism.substring(0, mechanism.length() - \"-PLUS\".length());\n\t\t}\n\t\treturn credentialsDecoder.getSupportedMechanisms().contains(mechanism);\n\t}\n\n\t@Override\n\tpublic void setCredentialsCodecs(CredentialsEncoderBean encoder, CredentialsDecoderBean decoder) {\n\t\tthis.credentialsEncoder = encoder;\n\t\tthis.credentialsDecoder = decoder;\n\t}\n\n\tprotected CredentialsDecoderBean getCredentialsDecoder() {\n\t\treturn credentialsDecoder;\n\t}\n\n\tprotected CredentialsEncoderBean getCredentialsEncoder() {\n\t\treturn credentialsEncoder;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/AuthRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport org.jspecify.annotations.NonNull;\nimport org.jspecify.annotations.Nullable;\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.auth.CredentialsDecoderBean;\nimport tigase.auth.CredentialsEncoderBean;\nimport tigase.auth.credentials.Credentials;\nimport tigase.auth.credentials.entries.PlainCredentialsEntry;\nimport tigase.eventbus.EventBusEvent;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.security.NoSuchAlgorithmException;\nimport java.time.Duration;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Interface <code>AuthRepository</code> defines a proxy bridge between user authentication data storage and the Tigase\n * server authentication logic. Important thing about the authentication repository is that it not only stores login\n * credentials but also performs actual user authentication. This is because available authentication mechanisms depend\n * on the way data are stored in the repository (database).\n * <br>\n * Created: Sun Nov  5 21:15:46 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface AuthRepository\n\t\textends Repository {\n\n\t/**\n\t * Property key name for <code>otherAuth</code> method call. It is used to provide an extra authentication data by\n\t * the client to the authentication logic. Please note the <code>RESULT_KEY</code> property key is used to provide\n\t * authentication data from the server to the client. This property is used to provide authentication data from the\n\t * client to the server.\n\t */\n\tString DATA_KEY = \"data\";\n\n\tString DIGEST_ID_KEY = \"digest-id\";\n\n\tString DIGEST_KEY = \"digest\";\n\n\t/**\n\t * Property key name for <code>otherAuth</code> method call. It is used to provide desired authentication mechanism\n\t * to the authentication logic.\n\t */\n\tString MACHANISM_KEY = \"mechanism\";\n\n\tString PASSWORD_KEY = \"password\";\n\n\t// Query params (and otherAuth)\n\n\t/**\n\t * Property key name for <code>otherAuth</code> method call. It is used to provide desired authentication protocol\n\t * to the authentication logic.\n\t */\n\tString PROTOCOL_KEY = \"protocol\";\n\n\t/**\n\t * Property value for <code>otherAuth</code> method call. It is used to provide desired authentication NON-SASL\n\t * protocol to the authentication logic.\n\t */\n\tString PROTOCOL_VAL_NONSASL = \"nonsasl\";\n\n\t/**\n\t * Property value for <code>otherAuth</code> method call. It is used to provide desired authentication SASL protocol\n\t * to the authentication logic.\n\t */\n\tString PROTOCOL_VAL_SASL = \"sasl\";\n\n\t/**\n\t * Property key name for <code>otherAuth</code> method call. It is used to provide authentication realm to the\n\t * authentication logic. In most cases, the realm is just a domain name.\n\t */\n\tString REALM_KEY = \"realm\";\n\n\t/**\n\t * Property key name for <code>otherAuth</code> method call. It is used to provide authentication handshaking data\n\t * during login process. Some authentication mechanisms require exchanging requests between the client and the\n\t * server. This property key points back to the data which need to be sent back to the client.\n\t */\n\tString RESULT_KEY = \"result\";\n\n\t/**\n\t * Property key name for <code>otherAuth</code> method call. It is used to provide authentication domain to the\n\t * authentication logic. It is highly recommended that this property is always set, even if the authentication\n\t * protocol/mechanism does not need it strictly.\n\t */\n\tString SERVER_NAME_KEY = \"server-name\";\n\n\t/**\n\t * Property key name for <code>otherAuth</code> method call. It is used to provide a user ID on successful user\n\t * login. Please note, the key points to the object of <code>BareJID</code> type.\n\t */\n\tString USER_ID_KEY = \"user-id\";\n\n\tenum AccountStatus {\n\t\tactive(1),\n\t\tdisabled(0),\n\t\tbanned(-5),\n\t\tpending(-2),\n\t\tsystem(-1),\n\t\tvip(2),\n\t\tpaid(3),\n\t\tspam(-50),\n\t\tundefined_active(Integer.MAX_VALUE),\n\t\tundefined_inactive(Integer.MIN_VALUE);\n\n\t\tprivate static final HashMap<Integer, AccountStatus> statuses = new HashMap<>();\n\n\t\tstatic {\n\t\t\tfor (AccountStatus v : AccountStatus.values()) {\n\t\t\t\tstatuses.put(v.getValue(), v);\n\t\t\t}\n\t\t}\n\n\t\tpublic boolean isInactive() {\n\t\t\treturn value <= 0;\n\t\t}\n\n\t\tprivate final int value;\n\n\t\tpublic static AccountStatus byValue(int value) {\n\t\t\tAccountStatus accountStatus = statuses.get(value);\n\t\t\tif (accountStatus == null) {\n\t\t\t\tLogger.getLogger(AuthRepository.class.getName()).log(Level.INFO, \"Undefined AccountStatus for value: \" + value);\n\t\t\t\taccountStatus = value <= 0 ? AccountStatus.undefined_inactive : AccountStatus.undefined_active;\n\t\t\t}\n\t\t\treturn accountStatus;\n\t\t}\n\n\t\tAccountStatus(int value) {\n\t\t\tthis.value = value;\n\t\t}\n\n\t\tpublic int getValue() {\n\t\t\treturn value;\n\t\t}\n\t}\n\n\tvoid addUser(BareJID user, String password) throws TigaseDBException;\n\n\tAccountStatus getAccountStatus(BareJID user) throws TigaseDBException;\n\n\tdefault Credentials getCredentials(BareJID user, String credentialId) throws TigaseDBException {\n\t\tString password = getPassword(user);\n\t\tif (password != null) {\n\t\t\treturn new SingleCredential(user, getAccountStatus(user), new PlainCredentialsEntry(password));\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tString getPassword(BareJID user) throws TigaseDBException;\n\n\t/**\n\t * <code>getResourceUri</code> method returns database connection string.\n\t *\n\t * @return a <code>String</code> value of database connection string.\n\t */\n\tString getResourceUri();\n\n\tdefault Collection<String> getCredentialIds(BareJID user) throws TigaseDBException {\n\t\treturn Collections.emptyList();\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.1.0\", note = \"Adjusting naming, 'name' was renamed to correct 'credentialId'\")\n\tdefault Collection<String> getUsernames(BareJID user) throws TigaseDBException {\n\t\treturn getCredentialIds(user);\n\t}\n\n\t/**\n\t * @param duration Time range within which active users should be counted. Method is only used by statistics.\n\t *\n\t * @return number of active users in required range\n\t */\n\tlong getActiveUsersCountIn(Duration duration);\n\n\t/**\n\t * This method is only used by the server statistics component to report number of registered users.\n\t *\n\t * @return a <code>long</code> number of registered users in the repository.\n\t */\n\tlong getUsersCount();\n\n\t/**\n\t * This method is only used by the server statistics component to report number of registered users for given\n\t * domain.\n\t *\n\t * @param domain for which get the statistics\n\t *\n\t * @return a <code>long</code> number of registered users in the repository.\n\t */\n\tlong getUsersCount(String domain);\n\n\tdefault boolean isMechanismSupported(String domain, String mechanism) {\n\t\treturn \"PLAIN\".equals(mechanism);\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\", note = \"Use getAccountStatus()\")\n\tdefault boolean isUserDisabled(BareJID user) throws TigaseDBException {\n\t\tAccountStatus s = getAccountStatus(user);\n\t\treturn s == AccountStatus.disabled;\n\t}\n\n\t/**\n\t * Do some actions on repository, when user logs in. (for example update <code>last_login_time</code>)\n\t *\n\t * @param jid JID of logged user.\n\t *\n\t * @throws TigaseDBException if an error occurs\n\t */\n\tvoid loggedIn(BareJID jid) throws TigaseDBException;\n\n\tvoid logout(BareJID user) throws TigaseDBException;\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tboolean otherAuth(Map<String, Object> authProps)\n\t\t\tthrows TigaseDBException, AuthorizationException;\n\n\t/**\n\t * <code>queryAuth</code> returns mechanisms available for authentication.\n\t *\n\t * @param authProps a <code>Map</code> value with parameters for authentication.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tvoid queryAuth(Map<String, Object> authProps);\n\n\tdefault void removeCredential(BareJID user, String credentialId) throws TigaseDBException {\n\n\t}\n\n\tvoid removeUser(BareJID user) throws TigaseDBException;\n\n\tvoid setAccountStatus(BareJID user, AccountStatus status) throws TigaseDBException;\n\n\tdefault void setCredentialsCodecs(CredentialsEncoderBean encoder, CredentialsDecoderBean decoder) {\n\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tdefault void setUserDisabled(BareJID user, Boolean value) throws TigaseDBException {\n\t\tAccountStatus status = getAccountStatus(user);\n\t\tif (status == AccountStatus.active || status == AccountStatus.disabled) {\n\t\t\tsetAccountStatus(user, value ? AccountStatus.disabled : AccountStatus.active);\n\t\t}\n\t}\n\n\tdefault void updateCredential(BareJID user, String credentialId, String password)\n\t\t\tthrows TigaseDBException {\n\t\tupdatePassword(user, password);\n\t}\n\n\tdefault void updateCredential(BareJID user, String credentialId, String mechanism, String data) throws TigaseDBException {\n\t\tthrow new TigaseDBException(\"Feature not supported by AuthRepository implementation \" + this.getClass().getSimpleName());\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tvoid updatePassword(BareJID user, String password) throws TigaseDBException;\n\n\tclass DefaultCredentials\n\t\t\timplements Credentials {\n\n\t\tprivate static final Logger log = Logger.getLogger(DefaultCredentials.class.getCanonicalName());\n\t\tprivate final AccountStatus accountStatus;\n\t\tprivate final CredentialsDecoderBean decoder;\n\t\tprivate final List<RawEntry> entries;\n\t\tprivate final BareJID user;\n\n\t\tpublic DefaultCredentials(BareJID user, AccountStatus accountStatus, List<RawEntry> entries,\n\t\t\t\t\t\t\t\t  CredentialsDecoderBean decoderBean) {\n\t\t\tthis.accountStatus = accountStatus;\n\t\t\tthis.user = user;\n\t\t\tthis.entries = entries;\n\t\t\tthis.decoder = decoderBean;\n\t\t}\n\n\t\t@Override\n\t\tpublic Credentials.Entry getEntryForMechanism(String mechanism) {\n\t\t\tfor (RawEntry entry : entries) {\n\t\t\t\tif (entry.isForMechanism(mechanism)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn decoder.decode(user, mechanism, entry.getValue());\n\t\t\t\t\t} catch (NoSuchAlgorithmException ex) {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Could not decode credentials for \" + mechanism, ex);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean canLogin() {\n\t\t\treturn !(isAccountDisabled() || accountStatus.isInactive());\n\t\t}\n\n\t\t@Override\n\t\tpublic Credentials.Entry getFirst() {\n\t\t\tif (entries.isEmpty()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tfor (RawEntry entry : entries) {\n\t\t\t\ttry {\n\t\t\t\t\treturn decoder.decode(user, entry.getMechanism(), entry.getValue());\n\t\t\t\t} catch (NoSuchAlgorithmException ex) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Could not decode credentials for \" + entry.getMechanism(), ex);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic BareJID getUser() {\n\t\t\treturn user;\n\t\t}\n\n\t\tpublic boolean isAccountDisabled() {\n\t\t\treturn accountStatus == AccountStatus.disabled;\n\t\t}\n\n\t\t@Override\n\t\tpublic AccountStatus getAccountStatus() {\n\t\t\treturn accountStatus;\n\t\t}\n\n\t\tpublic static class RawEntry\n\t\t\t\timplements Credentials.RawEntry {\n\n\t\t\tprivate final String mechanism;\n\t\t\tprivate final String value;\n\n\t\t\tpublic RawEntry(String mechanism, String value) {\n\t\t\t\tthis.mechanism = mechanism;\n\t\t\t\tthis.value = value;\n\t\t\t}\n\n\t\t\tpublic String getMechanism() {\n\t\t\t\treturn mechanism;\n\t\t\t}\n\n\t\t\tpublic String getValue() {\n\t\t\t\treturn value;\n\t\t\t}\n\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tfinal StringBuffer sb = new StringBuffer(\"DefaultCredentials\");\n\t\t\tsb.append(\", user=\").append(user);\n\t\t\tsb.append(\", accountStatus=\").append(accountStatus);\n\t\t\tsb.append(\", entries=\").append(entries.size());\n\t\t\treturn sb.toString();\n\t\t}\n\t}\n\n\tclass SingleCredential\n\t\t\timplements Credentials {\n\n\t\tprivate final AccountStatus accountStatus;\n\t\tprivate final Credentials.Entry entry;\n\t\tprivate final BareJID user;\n\n\t\tpublic SingleCredential(BareJID user, AccountStatus accountStatus, Credentials.Entry entry) {\n\t\t\tthis.user = user;\n\t\t\tthis.entry = entry;\n\t\t\tthis.accountStatus = accountStatus;\n\t\t}\n\n\t\t@Override\n\t\tpublic Entry getEntryForMechanism(String mechanism) {\n\t\t\tif (mechanism.equals(entry.getMechanism())) {\n\t\t\t\treturn entry;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic Entry getFirst() {\n\t\t\treturn entry;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean canLogin() {\n\t\t\treturn !(isAccountDisabled() || accountStatus.isInactive());\n\t\t}\n\n\t\t@Override\n\t\tpublic BareJID getUser() {\n\t\t\treturn user;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isAccountDisabled() {\n\t\t\treturn accountStatus == AccountStatus.disabled;\n\t\t}\n\n\t\t@Override\n\t\tpublic AccountStatus getAccountStatus() {\n\t\t\treturn accountStatus;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tfinal StringBuffer sb = new StringBuffer(\"SingleCredential\");\n\t\t\tsb.append(\", user=\").append(user);\n\t\t\tsb.append(\", accountStatus=\").append(accountStatus);\n\t\t\treturn sb.toString();\n\t\t}\n\t}\n\n\tpublic static class PasswordChangedEvent implements EventBusEvent {\n\n\t\tprivate final BareJID jid;\n\t\tprivate final String credentialId;\n\t\tprivate final String mechanism;\n\n\t\tpublic PasswordChangedEvent(@NonNull BareJID jid, @NonNull String credentialId, @Nullable String mechanism) {\n\t\t\tthis.jid = jid;\n\t\t\tthis.credentialId = credentialId;\n\t\t\tthis.mechanism = mechanism;\n\t\t}\n\n\t\tpublic @NonNull BareJID getJid() {\n\t\t\treturn jid;\n\t\t}\n\n\t\tpublic @NonNull String getCredentialId() {\n\t\t\treturn credentialId;\n\t\t}\n\n\t\t/**\n\t\t * If mechanism is <code>null</code>, then credential for all mechanism was updated.\n\t\t * @return\n\t\t */\n\t\tpublic @Nullable String getMechanism() {\n\t\t\treturn mechanism;\n\t\t}\n\t}\n}    // AuthRepository\n\n"
  },
  {
    "path": "src/main/java/tigase/db/AuthRepositoryImpl.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport tigase.auth.XmppSaslException;\nimport tigase.util.Algorithms;\nimport tigase.util.Base64;\nimport tigase.xmpp.jid.BareJID;\n\nimport javax.security.auth.callback.*;\nimport javax.security.sasl.*;\nimport java.io.IOException;\nimport java.security.NoSuchAlgorithmException;\nimport java.time.Duration;\nimport java.util.Map;\nimport java.util.TreeMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.auth.credentials.Credentials.DEFAULT_CREDENTIAL_ID;\n\n/**\n * Describe class AuthRepositoryImpl here.\n * <br>\n * Created: Sat Nov 11 21:46:50 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class AuthRepositoryImpl\n\t\timplements AuthRepository {\n\n\tpublic static final String ACCOUNT_STATUS_KEY = \"account_status\";\n\tprotected static final Logger log = Logger.getLogger(\"tigase.db.UserAuthRepositoryImpl\");\n\tprotected static final String DISABLED_KEY = \"disabled\";\n\tprotected static final String PASSWORD_KEY = \"password\";\n\tprivate static final String[] non_sasl_mechs = {\"password\", \"digest\"};\n\tprivate static final String[] sasl_mechs = {\"PLAIN\", \"DIGEST-MD5\", \"CRAM-MD5\"};\n\n\t// ~--- fields ---------------------------------------------------------------\n\tprivate UserRepository repo = null;\n\n\t/**\n\t * Creates a new <code>AuthRepositoryImpl</code> instance.\n\t */\n\tpublic AuthRepositoryImpl(UserRepository repo) {\n\t\tthis.repo = repo;\n\t}\n\n\t@Override\n\tpublic void loggedIn(BareJID jid) throws TigaseDBException {\n\n\t}\n\n\t@Override\n\tpublic void addUser(BareJID user, final String password) throws UserExistsException, TigaseDBException {\n\t\trepo.addUser(user);\n\t\tlog.log(Level.FINE, \"Repo user added: \" + user);\n\t\tupdateCredential(user, DEFAULT_CREDENTIAL_ID, password);\n\t\tlog.log(Level.FINE, \"Password updated: \" + user + \":\" + password);\n\t}\n\n\t@Override\n\tpublic boolean isMechanismSupported(String domain, String mechanism) {\n\t\tif (\"PLAIN\".equals(mechanism)) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String getResourceUri() {\n\t\treturn repo.getResourceUri();\n\t}\n\n\t@Override\n\tpublic long getActiveUsersCountIn(Duration duration) {\n\t\treturn -1;\n\t}\n\n\t@Override\n\tpublic long getUsersCount() {\n\t\treturn repo.getUsersCount();\n\t}\n\n\t@Override\n\tpublic long getUsersCount(String domain) {\n\t\treturn repo.getUsersCount(domain);\n\t}\n\n\t@Override\n\t@Deprecated\n\tpublic void initRepository(final String string, Map<String, String> params) throws DBInitException {\n\t}\n\n\t@Override\n\tpublic void logout(BareJID user) {\n\t}\n\n\t@Override\n\tpublic boolean otherAuth(final Map<String, Object> props)\n\t\t\tthrows UserNotFoundException, TigaseDBException, AuthorizationException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"otherAuth: {0}\", props);\n\t\t}\n\n\t\tString proto = (String) props.get(PROTOCOL_KEY);\n\n\t\t// TODO: this equals should be most likely replaced with == here.\n\t\t// The property value is always set using the constant....\n\t\tif (proto.equals(PROTOCOL_VAL_SASL)) {\n\t\t\treturn saslAuth(props);\n\t\t}    // end of if (proto.equals(PROTOCOL_VAL_SASL))\n\t\tif (proto.equals(PROTOCOL_VAL_NONSASL)) {\n\t\t\tString password = (String) props.get(PASSWORD_KEY);\n\t\t\tBareJID user_id = (BareJID) props.get(USER_ID_KEY);\n\n\t\t\tif (password != null) {\n\t\t\t\treturn plainAuth(user_id, password);\n\t\t\t}\n\n\t\t\tString digest = (String) props.get(DIGEST_KEY);\n\n\t\t\tif (digest != null) {\n\t\t\t\tString digest_id = (String) props.get(DIGEST_ID_KEY);\n\n\t\t\t\treturn digestAuth(user_id, digest, digest_id, \"SHA\");\n\t\t\t}\n\t\t}    // end of if (proto.equals(PROTOCOL_VAL_SASL))\n\n\t\tthrow new AuthorizationException(\"Protocol is not supported.\");\n\t}\n\n\t@Override\n\tpublic void queryAuth(final Map<String, Object> authProps) {\n\t\tString protocol = (String) authProps.get(PROTOCOL_KEY);\n\n\t\tif (protocol.equals(PROTOCOL_VAL_NONSASL)) {\n\t\t\tauthProps.put(RESULT_KEY, non_sasl_mechs);\n\t\t}    // end of if (protocol.equals(PROTOCOL_VAL_NONSASL))\n\t\tif (protocol.equals(PROTOCOL_VAL_SASL)) {\n\t\t\tauthProps.put(RESULT_KEY, sasl_mechs);\n\t\t}    // end of if (protocol.equals(PROTOCOL_VAL_NONSASL))\n\t}\n\n\t@Override\n\tpublic void removeUser(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\trepo.removeUser(user);\n\t}\n\n\t// Implementation of tigase.db.AuthRepository\n\n\t@Override\n\tpublic void updatePassword(BareJID user, final String password) throws TigaseDBException {\n\t\trepo.setData(user, PASSWORD_KEY, password);\n\t}\n\n\tpublic String getPassword(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\treturn repo.getData(user, PASSWORD_KEY);\n\t}\n\n\t@Override\n\tpublic AccountStatus getAccountStatus(BareJID user) throws TigaseDBException {\n\t\tString value = repo.getData(user, ACCOUNT_STATUS_KEY);\n\t\treturn value == null ? null : AccountStatus.valueOf(value);\n\t}\n\n\t@Override\n\tpublic boolean isUserDisabled(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\tAccountStatus st = getAccountStatus(user);\n\t\tif (st == null) {\n\t\t\tString value = repo.getData(user, DISABLED_KEY);\n\t\t\treturn Boolean.parseBoolean(value);\n\t\t} else {\n\t\t\treturn st == AccountStatus.disabled;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setAccountStatus(BareJID user, AccountStatus value) throws TigaseDBException {\n\t\tif (value == null) {\n\t\t\trepo.removeData(user, ACCOUNT_STATUS_KEY);\n\t\t} else {\n\t\t\trepo.setData(user, ACCOUNT_STATUS_KEY, value.name());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserDisabled(BareJID user, Boolean value) throws UserNotFoundException, TigaseDBException {\n\t\tAccountStatus status = getAccountStatus(user);\n\t\tif (status == AccountStatus.active || status == AccountStatus.disabled) {\n\t\t\tsetAccountStatus(user, value ? AccountStatus.disabled : AccountStatus.active);\n\t\t}\n\t}\n\n\tprivate boolean digestAuth(BareJID user, final String digest, final String id, final String alg)\n\t\t\tthrows UserNotFoundException, TigaseDBException, AuthorizationException {\n\t\tfinal String db_password = getPassword(user);\n\n\t\ttry {\n\t\t\tfinal String digest_db_pass = Algorithms.hexDigest(id, db_password, alg);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Comparing passwords, given: \" + digest + \", db: \" + digest_db_pass);\n\t\t\t}\n\n\t\t\treturn digest.equals(digest_db_pass);\n\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\tthrow new AuthorizationException(\"No such algorithm.\", e);\n\t\t}    // end of try-catch\n\t}\n\n\tprivate boolean plainAuth(BareJID user, final String password) throws UserNotFoundException, TigaseDBException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"plainAuth: {0}:{1}\", new Object[]{user, password});\n\t\t}\n\n\t\tString db_password = getPassword(user);\n\n\t\treturn (password != null) && (db_password != null) && db_password.equals(password);\n\t}\n\n\t// ~--- methods --------------------------------------------------------------\n\tprivate boolean saslAuth(final Map<String, Object> props) throws AuthorizationException {\n\t\ttry {\n\t\t\tSaslServer ss = (SaslServer) props.get(\"SaslServer\");\n\n\t\t\tif (ss == null) {\n\t\t\t\tMap<String, String> sasl_props = new TreeMap<String, String>();\n\n\t\t\t\tsasl_props.put(Sasl.QOP, \"auth\");\n\t\t\t\tss = Sasl.createSaslServer((String) props.get(MACHANISM_KEY), \"xmpp\",\n\t\t\t\t\t\t\t\t\t\t   (String) props.get(SERVER_NAME_KEY), sasl_props,\n\t\t\t\t\t\t\t\t\t\t   new SaslCallbackHandler(props));\n\t\t\t\tprops.put(\"SaslServer\", ss);\n\t\t\t}    // end of if (ss == null)\n\n\t\t\tString data_str = (String) props.get(DATA_KEY);\n\t\t\tbyte[] in_data = ((data_str != null) ? Base64.decode(data_str) : new byte[0]);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"response: \" + new String(in_data));\n\t\t\t}\n\n\t\t\tbyte[] challenge = ss.evaluateResponse(in_data);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"challenge: \" + ((challenge != null) ? new String(challenge) : \"null\"));\n\t\t\t}\n\n\t\t\tString challenge_str = (((challenge != null) && (challenge.length > 0)) ? Base64.encode(challenge) : null);\n\n\t\t\tprops.put(RESULT_KEY, challenge_str);\n\t\t\tif (ss.isComplete()) {\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\treturn false;\n\t\t\t}    // end of if (ss.isComplete()) else\n\t\t} catch (SaslException e) {\n\t\t\tthrow new AuthorizationException(\"Sasl exception.\", e);\n\t\t}      // end of try-catch\n\t}\n\n\tprivate class SaslCallbackHandler\n\t\t\timplements CallbackHandler {\n\n\t\tprivate Map<String, Object> options = null;\n\n\t\tprivate SaslCallbackHandler(final Map<String, Object> options) {\n\t\t\tthis.options = options;\n\t\t}\n\n\t\t@Override\n\t\tpublic void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException {\n\t\t\tBareJID jid = null;\n\n\t\t\tfor (int i = 0; i < callbacks.length; i++) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"Callback: \" + callbacks[i].getClass().getSimpleName());\n\t\t\t\t}\n\t\t\t\tif (callbacks[i] instanceof RealmCallback) {\n\t\t\t\t\tRealmCallback rc = (RealmCallback) callbacks[i];\n\t\t\t\t\tString realm = (String) options.get(REALM_KEY);\n\n\t\t\t\t\tif (realm != null) {\n\t\t\t\t\t\trc.setText(realm);\n\t\t\t\t\t}        // end of if (realm == null)\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.finest(\"RealmCallback: \" + realm);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (callbacks[i] instanceof NameCallback) {\n\t\t\t\t\t\tNameCallback nc = (NameCallback) callbacks[i];\n\t\t\t\t\t\tString user_name = nc.getName();\n\n\t\t\t\t\t\tif (user_name == null) {\n\t\t\t\t\t\t\tuser_name = nc.getDefaultName();\n\t\t\t\t\t\t}      // end of if (name == null)\n\t\t\t\t\t\tjid = BareJID.bareJIDInstanceNS(user_name, (String) options.get(REALM_KEY));\n\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tfinal AccountStatus accountStatus = getAccountStatus(jid);\n\t\t\t\t\t\t\tif (accountStatus.isInactive()) {\n\t\t\t\t\t\t\t\tthrow XmppSaslException.getExceptionFor(accountStatus);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (TigaseDBException e) {\n\t\t\t\t\t\t\tthrow new IOException(\"Account Status retrieving problem.\", e);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\toptions.put(USER_ID_KEY, jid);\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.finest(\"NameCallback: \" + user_name);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (callbacks[i] instanceof PasswordCallback) {\n\t\t\t\t\t\t\tPasswordCallback pc = (PasswordCallback) callbacks[i];\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tString passwd = getPassword(jid);\n\n\t\t\t\t\t\t\t\tpc.setPassword(passwd.toCharArray());\n\t\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\t\tlog.finest(\"PasswordCallback: \" + passwd);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\tthrow new IOException(\"Password retrieving problem.\", e);\n\t\t\t\t\t\t\t}    // end of try-catch\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (callbacks[i] instanceof AuthorizeCallback) {\n\t\t\t\t\t\t\t\tAuthorizeCallback authCallback = ((AuthorizeCallback) callbacks[i]);\n\t\t\t\t\t\t\t\tString authenId = authCallback.getAuthenticationID();\n\t\t\t\t\t\t\t\tString authorId = authCallback.getAuthorizationID();\n\n\t\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\t\tlog.finest(\"AuthorizeCallback: authenId: \" + authenId);\n\t\t\t\t\t\t\t\t\tlog.finest(\"AuthorizeCallback: authorId: \" + authorId);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// if (authenId.equals(authorId)) {\n\t\t\t\t\t\t\t\tauthCallback.setAuthorized(true);\n\n\t\t\t\t\t\t\t\t// } // end of if (authenId.equals(authorId))\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthrow new UnsupportedCallbackException(callbacks[i], \"Unrecognized Callback\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}    // AuthRepositoryImpl\n"
  },
  {
    "path": "src/main/java/tigase/db/AuthRepositoryMDImpl.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport tigase.auth.credentials.Credentials;\nimport tigase.db.beans.AuthRepositoryMDPoolBean;\nimport tigase.db.beans.MDPoolBeanWithStatistics;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.time.Duration;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Implementation of multi domain pool for authentication repositories. Created: Mar 27, 2010 9:10:21 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic abstract class AuthRepositoryMDImpl\n\t\textends MDPoolBeanWithStatistics<AuthRepository, AuthRepositoryMDPoolBean.AuthRepositoryConfigBean>\n\t\timplements AuthRepository {\n\n\tprivate static final Logger log = Logger.getLogger(AuthRepositoryMDImpl.class.getName());\n\n\tprivate EventBus eventBus = EventBusFactory.getInstance();\n\n\tpublic AuthRepositoryMDImpl() {\n\t\tsuper(AuthRepository.class);\n\t}\n\n\t@Override\n\tpublic void addUser(BareJID user, String password) throws TigaseDBException {\n\t\tAuthRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\trepo.addUser(user, password);\n\t\t\teventBus.fire(new UserRepository.UserAddedEvent(user));\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic AccountStatus getAccountStatus(BareJID user) throws TigaseDBException {\n\t\tAuthRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\treturn repo.getAccountStatus(user);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic Credentials getCredentials(BareJID user, String credentialId) throws TigaseDBException {\n\t\tAuthRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\treturn repo.getCredentials(user, credentialId);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getPassword(BareJID user) throws TigaseDBException {\n\t\tAuthRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\treturn repo.getPassword(user);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getResourceUri() {\n\t\treturn getDefaultRepository().getResourceUri();\n\t}\n\n\t@Override\n\tpublic Collection<String> getCredentialIds(BareJID user) throws TigaseDBException {\n\t\tAuthRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\treturn repo.getCredentialIds(user);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic long getActiveUsersCountIn(Duration duration) {\n\t\tAuthRepository repo = getRepo(null);\n\n\t\tif (repo != null) {\n\t\t\treturn repo.getActiveUsersCountIn(duration);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"Couldn't obtain user repository, not even default one!\");\n\t\t}\n\n\t\treturn -1;\n\t}\n\n\t@Override\n\tpublic long getUsersCount() {\n\t\treturn repositoriesStream().mapToLong(AuthRepository::getUsersCount).sum();\n\t}\n\n\t@Override\n\tpublic long getUsersCount(String domain) {\n\t\tAuthRepository repo = getRepo(domain);\n\n\t\tif (repo != null) {\n\t\t\treturn repo.getUsersCount(domain);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"Couldn't obtain user repository for domain: \" + domain + \", not even default one!\");\n\t\t}\n\n\t\treturn -1;\n\t}\n\n\t@Override\n\tpublic void initRepository(String resource_uri, Map<String, String> params) throws DBInitException {\n\t\tlog.log(Level.CONFIG, \"Multi-domain repository pool initialized: \" + resource_uri + \", params: \" + params);\n\t}\n\n\t@Override\n\tpublic boolean isMechanismSupported(String domain, String mechanism) {\n\t\tAuthRepository repo = getRepo(domain);\n\t\tif (repo != null) {\n\t\t\treturn repo.isMechanismSupported(domain, mechanism);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"Couldn't obtain user repository for domain: \" + domain + \", not even default one!\");\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isUserDisabled(BareJID user) throws TigaseDBException {\n\t\tAuthRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\treturn repo.isUserDisabled(user);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic void loggedIn(BareJID user) throws TigaseDBException {\n\t\tAuthRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\trepo.loggedIn(user);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void logout(BareJID user) throws TigaseDBException {\n\t\tAuthRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\trepo.logout(user);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean otherAuth(Map<String, Object> authProps) throws TigaseDBException, AuthorizationException {\n\t\tAuthRepository repo = getRepo((String) authProps.get(SERVER_NAME_KEY));\n\n\t\tif (repo != null) {\n\t\t\treturn repo.otherAuth(authProps);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"Couldn't obtain user repository for domain: \" + authProps.get(SERVER_NAME_KEY) +\n\t\t\t\t\t\", not even default one!\");\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t// ~--- methods\n\t// --------------------------------------------------------------\n\n\t@Override\n\tpublic void queryAuth(Map<String, Object> authProps) {\n\t\tAuthRepository repo = getRepo((String) authProps.get(SERVER_NAME_KEY));\n\n\t\tif (repo != null) {\n\t\t\trepo.queryAuth(authProps);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"Couldn't obtain user repository for domain: \" + authProps.get(SERVER_NAME_KEY) +\n\t\t\t\t\t\", not even default one!\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void removeCredential(BareJID user, String credentialId) throws TigaseDBException {\n\t\tAuthRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\trepo.removeCredential(user, credentialId);\n\t\t\teventBus.fire(new AuthRepository.PasswordChangedEvent(user, credentialId, null));\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void removeUser(BareJID user) throws TigaseDBException {\n\t\tAuthRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\teventBus.fire(new UserRepository.UserBeforeRemovedEvent(user));\n\t\t\trepo.removeUser(user);\n\n\t\t\teventBus.fire(new UserRepository.UserRemovedEvent(user));\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setAccountStatus(BareJID user, AccountStatus status) throws TigaseDBException {\n\t\tAuthRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\trepo.setAccountStatus(user, status);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserDisabled(BareJID user, Boolean value) throws TigaseDBException {\n\t\tAuthRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\trepo.setUserDisabled(user, value);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void updateCredential(BareJID user, String credentialId, String password) throws TigaseDBException {\n\t\tAuthRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\trepo.updateCredential(user, credentialId, password);\n\t\t\teventBus.fire(new AuthRepository.PasswordChangedEvent(user, credentialId, null));\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void updateCredential(BareJID user, String credentialId, String mechanism, String data)\n\t\t\tthrows TigaseDBException {\n\t\tAuthRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\trepo.updateCredential(user, credentialId, mechanism, data);\n\t\t\teventBus.fire(new AuthRepository.PasswordChangedEvent(user, credentialId, mechanism));\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void updatePassword(BareJID user, String password) throws TigaseDBException {\n\t\tAuthRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\trepo.updatePassword(user, password);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/AuthRepositoryPool.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport tigase.auth.credentials.Credentials;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.time.Duration;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Pool for authentication repositories. * <br> This pool should be used if connection to authentication storage is\n * blocking or synchronized, ie. implemented using single connection.* <br> If implementation of\n * <code>AuthRepository</code> uses connection pool or non blocking, concurrent access to authentication storage (ie.\n * <code>DataSourcePool</code>), then this pool is not need.\n * <br>\n * Created: Mar 27, 2010 11:31:17 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class AuthRepositoryPool\n\t\timplements AuthRepository, RepositoryPool<AuthRepository> {\n\n\tprivate static final Logger log = Logger.getLogger(AuthRepositoryPool.class.getName());\n\n\tprivate LinkedBlockingQueue<AuthRepository> repoPool = new LinkedBlockingQueue<AuthRepository>();\n\n\tpublic void addRepo(AuthRepository repo) {\n\t\trepoPool.offer(repo);\n\t}\n\n\t@Override\n\tpublic void addUser(BareJID user, String password) throws TigaseDBException {\n\t\tAuthRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\trepo.addUser(user, password);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"repo is NULL, pool empty? - \" + repoPool.size());\n\t\t}\n\t}\n\n\t@Override\n\tpublic AccountStatus getAccountStatus(BareJID user) throws TigaseDBException {\n\t\tAuthRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.getAccountStatus(user);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"repo is NULL, pool empty? - \" + repoPool.size());\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic Credentials getCredentials(BareJID user, String credentialId) throws TigaseDBException {\n\t\tAuthRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.getCredentials(user, credentialId);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"repo is NULL, pool empty? - \" + repoPool.size());\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getPassword(BareJID user) throws TigaseDBException {\n\t\tAuthRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.getPassword(user);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"repo is NULL, pool empty? - \" + repoPool.size());\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getResourceUri() {\n\t\tAuthRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.getResourceUri();\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"repo is NULL, pool empty? - \" + repoPool.size());\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic long getActiveUsersCountIn(Duration duration) {\n\t\tAuthRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.getActiveUsersCountIn(duration);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"repo is NULL, pool empty? - \" + repoPool.size());\n\t\t}\n\n\t\treturn -1;\n\t}\n\n\t@Override\n\tpublic Collection<String> getCredentialIds(BareJID user) throws TigaseDBException {\n\t\tAuthRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.getCredentialIds(user);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"repo is NULL, pool empty? - \" + repoPool.size());\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic long getUsersCount() {\n\t\tAuthRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.getUsersCount();\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"repo is NULL, pool empty? - \" + repoPool.size());\n\t\t}\n\n\t\treturn -1;\n\t}\n\n\t@Override\n\tpublic long getUsersCount(String domain) {\n\t\tAuthRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.getUsersCount(domain);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"repo is NULL, pool empty? - \" + repoPool.size());\n\t\t}\n\n\t\treturn -1;\n\t}\n\n\t@Override\n\t@Deprecated\n\tpublic void initRepository(String resource_uri, Map<String, String> params) throws DBInitException {\n\t}\n\n\t@Override\n\tpublic boolean isMechanismSupported(String domain, String mechanism) {\n\t\tAuthRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.isMechanismSupported(domain, mechanism);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"repo is NULL, pool empty? - \" + repoPool.size());\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isUserDisabled(BareJID user) throws TigaseDBException {\n\t\tAuthRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.isUserDisabled(user);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"repo is NULL, pool empty? - \" + repoPool.size());\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void loggedIn(BareJID user) throws TigaseDBException {\n\t\tAuthRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\trepo.loggedIn(user);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"repo is NULL, pool empty? - \" + repoPool.size());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void logout(BareJID user) throws TigaseDBException {\n\t\tAuthRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\trepo.logout(user);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"repo is NULL, pool empty? - \" + repoPool.size());\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean otherAuth(Map<String, Object> authProps)\n\t\t\tthrows TigaseDBException, AuthorizationException {\n\t\tAuthRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.otherAuth(authProps);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"repo is NULL, pool empty? - \" + repoPool.size());\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void queryAuth(Map<String, Object> authProps) {\n\t\tAuthRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\trepo.queryAuth(authProps);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"repo is NULL, pool empty? - \" + repoPool.size());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void removeCredential(BareJID user, String credentialId) throws TigaseDBException {\n\t\tAuthRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\trepo.removeCredential(user, credentialId);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"repo is NULL, pool empty? - \" + repoPool.size());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void removeUser(BareJID user) throws TigaseDBException {\n\t\tAuthRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\trepo.removeUser(user);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"repo is NULL, pool empty? - \" + repoPool.size());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setAccountStatus(BareJID user, AccountStatus status) throws TigaseDBException {\n\t\tAuthRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\trepo.setAccountStatus(user, status);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"repo is NULL, pool empty? - \" + repoPool.size());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setUserDisabled(BareJID user, Boolean value) throws TigaseDBException {\n\t\tAuthRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\trepo.setUserDisabled(user, value);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"repo is NULL, pool empty? - \" + repoPool.size());\n\t\t}\n\t}\n\n\tpublic AuthRepository takeRepo() {\n\t\ttry {\n\t\t\treturn repoPool.take();\n\t\t} catch (InterruptedException ex) {\n\t\t\tlog.log(Level.WARNING, \"Couldn't obtain user auth repository from the pool\", ex);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void updateCredential(BareJID user, String credentialId, String password) throws TigaseDBException {\n\t\tAuthRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\trepo.updateCredential(user, credentialId, password);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"repo is NULL, pool empty? - \" + repoPool.size());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void updateCredential(BareJID user, String credentialId, String mechanism, String data)\n\t\t\tthrows TigaseDBException {\n\t\tAuthRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\trepo.updateCredential(user, credentialId, mechanism, data);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"repo is NULL, pool empty? - \" + repoPool.size());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void updatePassword(BareJID user, String password) throws TigaseDBException {\n\t\tAuthRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\trepo.updatePassword(user, password);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"repo is NULL, pool empty? - \" + repoPool.size());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/AuthorizationException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\n/**\n * Describe class AuthorizationException here.\n * <br>\n * Created: Fri Nov 10 18:07:21 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class AuthorizationException\n\t\textends Exception {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * Creates a new <code>AuthorizationException</code> instance.\n\t */\n\tpublic AuthorizationException() {\n\t\tsuper();\n\t}\n\n\tpublic AuthorizationException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic AuthorizationException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n} // AuthorizationException\n"
  },
  {
    "path": "src/main/java/tigase/db/DBInitException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\n/**\n * Describe class DBInitException here.\n * <br>\n * Created: Thu Oct 26 12:18:05 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class DBInitException\n\t\textends TigaseDBException {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic DBInitException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic DBInitException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n} // DBInitException\n"
  },
  {
    "path": "src/main/java/tigase/db/DataOverwriteException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\n/**\n * The <code>DataOverwriteException</code> exception is thrown when application tries to ovrewrite data in repository\n * but does not have permission to do so. <br> Created: Wed Oct 27 14:17:44 2004 <br>\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\npublic class DataOverwriteException\n\t\textends TigaseDBException {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * Creates a new <code>DataOverwriteException</code> instance.\n\t */\n\tpublic DataOverwriteException(String message) {\n\t\tsuper(message);\n\t}\n\n\t/**\n\t * Creates a new <code>DataOverwriteException</code> instance.\n\t */\n\tpublic DataOverwriteException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n} // DataOverwriteException\n"
  },
  {
    "path": "src/main/java/tigase/db/DataRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.sql.*;\nimport java.util.Calendar;\nimport java.util.TimeZone;\n\n/**\n * The interface defines a generic data repository for storing arbitrary data in any application specific form. This\n * interface unifies database (repository) access allowing for easier way to create database connections pools or\n * database fail-over mechanisms.\n * <br>\n * Created: Jun 16, 2010 3:34:32 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface DataRepository\n\t\textends DataSource {\n\n\tCalendar UTC_CALENDAR = Calendar.getInstance(TimeZone.getTimeZone(\"UTC\"));\n\n\t/**\n\t * Helper enumeration with types of supported databases.\n\t */\n\tpublic static enum dbTypes {\n\t\tderby,\n\t\tmysql,\n\t\tpostgresql,\n\t\t@Deprecated\n\t\t@TigaseDeprecated(since = \"8.3.0\", removeIn = \"9.0.0\")\n\t\tjtds,\n\t\tsqlserver,\n\t\tother\n\t}\n\n\t/**\n\t * The method checks whether a table for the given name exists in the database.\n\t *\n\t * @param tableName is a <code>String</code> value of the table name to check\n\t *\n\t * @return <code>true</code> <code>boolean</code> value if the table exist in the database and <code>false</code> if\n\t * the table was not found.\n\t *\n\t * @throws SQLException if there was a problem accessing database.\n\t */\n\tboolean checkTable(String tableName) throws SQLException;\n\n\t/**\n\t * The method checks whether a table for the given name exists in the database and if it does not, it automatically\n\t * creates it.\n\t *\n\t * @param tableName is a <code>String</code> value of the table name to check\n\t * @param createTableQuery is a <code>String</code> with the query to create table\n\t *\n\t * @return <code>true</code> <code>boolean</code> value if the table exist in the database and <code>false</code> if\n\t * the table was not found.\n\t *\n\t * @throws SQLException if there was a problem accessing database.\n\t */\n\tboolean checkTable(String tableName, String createTableQuery) throws SQLException;\n\n\t/**\n\t * Commits current transaction on the DataRepository connection. Please note that calling this method on the\n\t * repository pool has no effect. You have to obtain particular repository handle first, before you can start\n\t * transaction.\n\t *\n\t */\n\tvoid commit() throws SQLException;\n\n\t/**\n\t * Creates a SQL statement on which SQL queries can be executed later by the higher repository layer.\n\t *\n\t * @param user_id user id for which the statement has to be created. This is an optional parameter and null can be\n\t * provided. It is used mainly to group queries for the same user on the same DB connection.\n\t *\n\t * @return a newly created <code>Statement</code>\n\t *\n\t * @throws SQLException if a JDBC error occurs.\n\t */\n\tStatement createStatement(BareJID user_id) throws SQLException;\n\n\t/**\n\t * Ends current transaction on the DataRepository connection. Please note that calling this method on the repository\n\t * pool has no effect. You have to obtain particular repository handle first, before you can start transaction.\n\t *\n\t */\n\tvoid endTransaction() throws SQLException;\n\n\t/**\n\t * Initializes a prepared statement for a given query and stores it internally under the given id key. It can be\n\t * retrieved later using <code>getPreparedStatement(stIdKey)</code> method.\n\t *\n\t * @param stIdKey is a statement identification key.\n\t * @param query is a query for the prepared statement.\n\t *\n\t */\n\tvoid initPreparedStatement(String stIdKey, String query) throws SQLException;\n\n\t/**\n\t * Initializes a prepared statement for a given query and stores it internally under the given id key. It can be\n\t * retrieved later using <code>getPreparedStatement(stIdKey)</code> method.\n\t *\n\t * @param stIdKey is a statement identification key.\n\t * @param query is a query for the prepared statement.\n\t * @param autoGeneratedKeys defines if statement should return auto generated keys\n\t *\n\t */\n\tvoid initPreparedStatement(String stIdKey, String query, int autoGeneratedKeys) throws SQLException;\n\n\t/**\n\t * A helper method to release resources from the statement and result set. This is most common operation for all\n\t * database calls, therefore it does make sense to add such a utility method to the API.\n\t *\n\t * @param stmt a <code>Statement</code> variable to release resources for. Might be null.\n\t * @param rs a <code>ResultSet</code> variable to release resources for. Might be null.\n\t */\n\tvoid release(Statement stmt, ResultSet rs);\n\n\tvoid releaseRepoHandle(DataRepository repo);\n\n\t/**\n\t * Rolls back started transaction on the DataRepository connection. Please note that calling this method on the\n\t * repository pool has no effect. You have to obtain particular repository handle first, before you can start\n\t * transaction.\n\t *\n\t */\n\tvoid rollback() throws SQLException;\n\n\t/**\n\t * Starts transaction on the DataRepository connection. Please note that calling this method on the repository pool\n\t * has no effect. You have to obtain particular repository handle first, before you can start transaction.\n\t *\n\t */\n\tvoid startTransaction() throws SQLException;\n\n\t/**\n\t * Returns <code>DataRepository</code> instance. If this is a repository pool then it returns particular instance\n\t * from the pool. It this is a real repository instance it returns itself. This is exclusive take, no other thread\n\t * may use this handle until it is returned to the pool.\n\t *\n\t * @param user_id is user account ID for which we acquire the handle.\n\t *\n\t * @return DataRepository instance.\n\t */\n\tDataRepository takeRepoHandle(BareJID user_id);\n\n\t/**\n\t * Returns type of DataRepository database\n\t *\n\t * @return a value of <code>dbTypes</code>\n\t */\n\tdbTypes getDatabaseType();\n\n\tint getPoolSize();\n\n\t/**\n\t * Returns a prepared statement for a given key.\n\t *\n\t * @param user_id user id for which the statement has to be created. This is an optional parameter and null can be\n\t * provided. It is used mainly to group queries for the same user on the same DB connection.\n\t * @param stIdKey is a statement identification key.\n\t *\n\t * @return a <code>PreparedStatement</code> for the given id key or null if such a statement does not exist.\n\t *\n\t */\n\tPreparedStatement getPreparedStatement(BareJID user_id, String stIdKey) throws SQLException;\n\n\t/**\n\t * Returns a prepared statement for a given key.\n\t *\n\t * @param hashCode user for selection of connection to use. It is used mainly to group queries for the same user on\n\t * the same DB connection.\n\t * @param stIdKey is a statement identification key.\n\t *\n\t * @return a <code>PreparedStatement</code> for the given id key or null if such a statement does not exist.\n\t *\n\t */\n\tPreparedStatement getPreparedStatement(int hashCode, String stIdKey) throws SQLException;\n\n\t/**\n\t * Returns a DB connection string or DB connection URI.\n\t *\n\t * @return a <code>String</code> value representing database connection string.\n\t */\n\tString getResourceUri();\n\n\t/**\n\t * Helper method to set timestamp into prepared statements. Provides proper calendar when needed to adjust\n\t * timestamps so that they are stored in the database in proper time zone.\n\t */\n\tdefault void setTimestamp(PreparedStatement stmt, int pos, java.sql.Timestamp timestamp) throws SQLException {\n\t\tswitch (getDatabaseType()) {\n\t\t\tcase jtds:\n\t\t\tcase sqlserver:\n\t\t\t\tstmt.setTimestamp(pos, timestamp, UTC_CALENDAR);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tstmt.setTimestamp(pos, timestamp);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Helper method to get timestamp from result set. Provides proper calendar when needed to adjust timestamps so that\n\t * they are stored in the database in proper time zone.\n\t */\n\tdefault Timestamp getTimestamp(ResultSet rs, int pos) throws SQLException {\n\t\tswitch (getDatabaseType()) {\n\t\t\tcase jtds:\n\t\t\tcase sqlserver:\n\t\t\t\treturn rs.getTimestamp(pos, UTC_CALENDAR);\n\t\t\tdefault:\n\t\t\t\treturn rs.getTimestamp(pos);\n\t\t}\n\t}\n\n\t/**\n\t * Helper method to get timestamp from result set. Provides proper calendar when needed to adjust timestamps so that\n\t * they are stored in the database in proper time zone.\n\t */\n\tdefault Timestamp getTimestamp(ResultSet rs, String field) throws SQLException {\n\t\tswitch (getDatabaseType()) {\n\t\t\tcase jtds:\n\t\t\tcase sqlserver:\n\t\t\t\treturn rs.getTimestamp(field, UTC_CALENDAR);\n\t\t\tdefault:\n\t\t\t\treturn rs.getTimestamp(field);\n\t\t}\n\t}\n\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/db/DataRepositoryPool.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport tigase.db.jdbc.DataRepositoryImpl;\nimport tigase.db.util.DBInitForkJoinPoolCache;\nimport tigase.db.util.JDBCPasswordObfuscator;\nimport tigase.stats.StatisticsList;\nimport tigase.stats.StatisticsProviderIfc;\nimport tigase.util.Version;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.time.Duration;\nimport java.util.ArrayDeque;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Queue;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.concurrent.ForkJoinPool;\nimport java.util.concurrent.ForkJoinTask;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Sep 4, 2010 2:13:22 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Repository.Meta(supportedUris = {\"jdbc:[^:]+:.*\"})\npublic class DataRepositoryPool\n\t\timplements DataRepository, DataSourcePool<DataRepository>, StatisticsProviderIfc {\n\n\tprivate static final Logger log = Logger.getLogger(DataRepositoryPool.class.getName());\n\n\t// ~--- fields ---------------------------------------------------------------\n\tprivate dbTypes database = null;\n\tprivate CopyOnWriteArrayList<DataRepository> repoPool = new CopyOnWriteArrayList<DataRepository>();\n\tprivate String resource_uri = null;\n\n\tpublic void addRepo(DataRepository repo) {\n\t\trepoPool.addIfAbsent(repo);\n\t}\n\n\t@Override\n\tpublic boolean automaticSchemaManagement() {\n\t\tif (repoPool.isEmpty()) {\n\t\t\treturn true;\n\t\t}\n\t\treturn repoPool.get(0).automaticSchemaManagement();\n\t}\n\n\t@Override\n\tpublic void checkConnectivity(Duration watchdogTime) {\n\t\trepoPool.forEach(repo -> repo.checkConnectivity(watchdogTime));\n\t}\n\n\tpublic DataRepository takeRepo(BareJID user_id) {\n\t\tint idx = user_id != null ? Math.abs(user_id.hashCode() % repoPool.size()) : 0;\n\t\tDataRepository result = null;\n\t\ttry {\n\t\t\tresult = repoPool.get(idx);\n\t\t} catch (IndexOutOfBoundsException ioobe) {\n\t\t\tresult = repoPool.get(0);\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic DataRepository takeRepo(int hashCode) {\n\t\tint idx = Math.abs(hashCode % repoPool.size());\n\t\tDataRepository result = null;\n\t\ttry {\n\t\t\tresult = repoPool.get(idx);\n\t\t} catch (IndexOutOfBoundsException ioobe) {\n\t\t\tresult = repoPool.get(0);\n\t\t}\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic DataRepository takeRepoHandle(BareJID user_id) {\n\t\treturn takeRepo(user_id);\n\t}\n\n\t@Override\n\tpublic void releaseRepoHandle(DataRepository repo) {\n\t\t// addRepo(repo);\n\t}\n\n\t@Override\n\tpublic boolean checkSchemaVersion(DataSourceAware<? extends DataSource> datasource, boolean shutdownServer) {\n\t\tDataRepository repo = takeRepo(null);\n\n\t\tif (repo != null) {\n\t\t\treturn repo.checkSchemaVersion(datasource, shutdownServer);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic Optional<Version> getSchemaVersion(String component) {\n\t\tDataRepository repo = takeRepo(null);\n\n\t\tif (repo != null) {\n\t\t\treturn repo.getSchemaVersion(component);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t\treturn Optional.empty();\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean checkTable(String tableName) throws SQLException {\n\t\tDataRepository repo = takeRepo(null);\n\n\t\tif (repo != null) {\n\t\t\treturn repo.checkTable(tableName);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean checkTable(String tableName, String createTableQuery) throws SQLException {\n\t\tDataRepository repo = takeRepo(null);\n\n\t\tif (repo != null) {\n\t\t\treturn repo.checkTable(tableName, createTableQuery);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic Statement createStatement(BareJID user_id) throws SQLException {\n\t\tDataRepository repo = takeRepo(user_id);\n\n\t\tif (repo != null) {\n\t\t\treturn repo.createStatement(user_id);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic PreparedStatement getPreparedStatement(BareJID user_id, String stIdKey) throws SQLException {\n\t\tDataRepository repo = takeRepo(user_id);\n\n\t\tif (repo != null) {\n\t\t\treturn repo.getPreparedStatement(user_id, stIdKey);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic PreparedStatement getPreparedStatement(int hashCode, String stIdKey) throws SQLException {\n\t\tDataRepository repo = takeRepo(hashCode);\n\n\t\tif (repo != null) {\n\t\t\treturn repo.getPreparedStatement(hashCode, stIdKey);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getResourceUri() {\n\t\tif (resource_uri == null && !repoPool.isEmpty()) {\n\t\t\treturn takeRepo(null).getResourceUri();\n\t\t}\n\t\treturn resource_uri;\n\t}\n\n\t@Override\n\tpublic dbTypes getDatabaseType() {\n\t\treturn database;\n\t}\n\n\t@Override\n\tpublic void getStatistics(String compName, StatisticsList list) {\n\t\tlist.add(compName, \"uri\", JDBCPasswordObfuscator.obfuscatePassword(getResourceUri()), Level.FINE);\n\t\tlist.add(compName, \"connections count\", repoPool.size(), Level.FINE);\n\t\tfor (DataRepository repo : repoPool) {\n\t\t\tif (repo instanceof StatisticsProviderIfc) {\n\t\t\t\t((StatisticsProviderIfc) repo).getStatistics(compName, list);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void initPreparedStatement(String stIdKey, String query) throws SQLException {\n\t\texecuteForEachDataSource(dataRepository -> dataRepository.initPreparedStatement(stIdKey, query));\n\t}\n\n\t@Override\n\tpublic void initPreparedStatement(String stIdKey, String query, int autoGeneratedKeys) throws SQLException {\n\t\texecuteForEachDataSource(dataRepository -> dataRepository.initPreparedStatement(stIdKey, query, autoGeneratedKeys));\n\t}\n\n\tprivate void executeForEachDataSource(ForkWithSqlException taskToRun) throws SQLException {\n\t\tQueue<ForkJoinTask<DataRepository>> tasks = new ArrayDeque<>();\n\t\tfinal ForkJoinPool pool = DBInitForkJoinPoolCache.shared.pool(\"dbinit-\" + this.hashCode(), Math.min(repoPool.size(), 128));\n\t\tfor (DataRepository dataRepository : repoPool) {\n\t\ttasks.offer(pool.submit(() -> {\n\t\ttry {\n\t\t\ttaskToRun.run(dataRepository);\n\t\t\treturn dataRepository;\n\t\t} catch (SQLException ex) {\n\t\t\tthrow new RuntimeException(\n\t\t\t\t\t\"Could not initialize prepared statement\", ex);\n\t\t}\n\t\t}));\n\t\t}\n\t\ttry {\n\t\t\tForkJoinTask<DataRepository> task;\n\t\t\twhile ((task = tasks.poll()) != null) {\n\t\t\t\tDataRepository repo = task.join();\n\t\t\t}\n\t\t} catch (RuntimeException ex) {\n\t\t\tthrow new SQLException(\"Could not initialize prepared statement\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void initialize(String resource_uri) throws DBInitException {\n\t\tthis.resource_uri = resource_uri;\n\n\t\tif (this.database == null) {\n\t\t\tdatabase = DataRepositoryImpl.parseDatabaseType(resource_uri);\n\t\t}\n\t}\n\n\t@Override\n\t@Deprecated\n\tpublic void initRepository(String resource_uri, Map<String, String> params) throws DBInitException {\n\t\tinitialize(resource_uri);\n\n\t\tfor (DataRepository dataRepository : repoPool) {\n\t\t\tdataRepository.initRepository(resource_uri, params);\n\t\t\tthis.database = dataRepository.getDatabaseType();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void release(Statement stmt, ResultSet rs) {\n\t\tif (rs != null) {\n\t\t\ttry {\n\t\t\t\trs.close();\n\t\t\t} catch (SQLException sqlEx) {\n\t\t\t}\n\t\t}\n\n\t\tif (stmt != null) {\n\t\t\ttry {\n\t\t\t\tstmt.close();\n\t\t\t} catch (SQLException sqlEx) {\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void startTransaction() throws SQLException {\n\t\t// TODO Auto-generated method stub\n\t}\n\n\t@Override\n\tpublic void commit() throws SQLException {\n\t\t// TODO Auto-generated method stub\n\t}\n\n\t@Override\n\tpublic void rollback() throws SQLException {\n\t\t// TODO Auto-generated method stub\n\n\t}\n\n\t@Override\n\tpublic void endTransaction() throws SQLException {\n\t\t// TODO Auto-generated method stub\n\n\t}\n\n\t@Override\n\tpublic int getPoolSize() {\n\t\treturn repoPool.size();\n\t}\n\n\t@FunctionalInterface\n\tprivate interface ForkWithSqlException {\n\n\t\tvoid run(DataRepository dataRepository) throws SQLException;\n\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tfinal String url = getResourceUri() != null ? JDBCPasswordObfuscator.obfuscatePassword(getResourceUri()) : \"n/a\";\n\t\treturn \"DataRepositoryPool{\" + \"size=\" + getPoolSize() + \", uri='\" + url + '\\'' + '}';\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/DataSource.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.db.util.JDBCPasswordObfuscator;\nimport tigase.db.util.RepositoryVersionAware;\nimport tigase.db.util.SchemaVersionCheckerLogger;\nimport tigase.kernel.core.Kernel;\nimport tigase.sys.TigaseRuntime;\nimport tigase.util.Version;\nimport tigase.util.log.LogFormatter;\n\nimport java.io.ByteArrayInputStream;\nimport java.time.Duration;\nimport java.util.Arrays;\nimport java.util.Optional;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Interface implemented by every class providing access to data storage, ie. databases, files, key-value stores.\n * <br>\n * Created by andrzej on 09.03.2016.\n */\npublic interface DataSource\n\t\textends Repository {\n\n\tLogger log = Logger.getLogger(DataSource.class.getName());\n\n\t/**\n\t * This method is called by data source bean watchdog mechanism to ensure that there is proper connectivity to\n\t * underlying data storage.\n\t *\n\t * @param watchdogTime time which should pass between checks\n\t */\n\tdefault void checkConnectivity(Duration watchdogTime) {}\n\n\t/**\n\t * Method checks version of the particular DataSource stored in the defined source.\n\t *\n\t * @param datasource implementation of {@link DataSourceAware} interface\n\t * @param shutdownServer specifies whether server should be shutdown automatically if the version in the database\n\t * doesn't match required version.\n\t *\n\t * @return a {@code false} when the version doesn't match or there is no version information in the repository. if\n\t * {@code shutdownServer} is set to {@code true} and the component version is final it would force shutting down of\n\t * the server, otherwise (for non-final version) only a warning would be printed.\n\t *\n\t */\n\tdefault public boolean checkSchemaVersion(DataSourceAware<? extends DataSource> datasource, boolean shutdownServer) {\n\t\tboolean result = false;\n\n\t\tif (datasource == null) {\n\t\t\t//most likely shutdown, no need to check schema now\n\t\t\treturn true;\n\t\t}\n\t\tfinal Class<? extends DataSourceAware> datasourceClass = datasource.getClass();\n\t\tif (datasourceClass.isAnnotationPresent(SchemaId.class)\n\t\t\t\t&& RepositoryVersionAware.class.isAssignableFrom(datasourceClass)) {\n\t\t\tfinal String dataSourceID = datasourceClass.getAnnotation(SchemaId.class).id();\n\n\t\t\tif (!automaticSchemaManagement()) {\n\t\t\t\tif (log.isLoggable(Level.WARNING)) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Automatic schema management is disabled for \" + JDBCPasswordObfuscator.obfuscatePassword(this.getResourceUri()) +\n\t\t\t\t\t\t\t\", skipping version check for \" + dataSourceID + \"(\" + datasourceClass.getSimpleName() +\n\t\t\t\t\t\t\t\")\");\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tOptional<Version> dbVer = getSchemaVersion(dataSourceID);\n\n\t\t\tVersion implementationVersion;\n\t\t\ttry {\n\t\t\t\tfinal RepositoryVersionAware repositoryVersionAware = (RepositoryVersionAware) datasourceClass.newInstance();\n\t\t\t\timplementationVersion = repositoryVersionAware.getVersion();\n\t\t\t} catch (InstantiationException | IllegalAccessException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Error creating instance\", e);\n\t\t\t\timplementationVersion = Version.of(datasourceClass.getPackage().getImplementationVersion());\n\t\t\t}\n\n\t\t\timplementationVersion = new Version.Builder(implementationVersion).setCommit(null).build();\n\n\t\t\tif (!dbVer.isPresent() || !implementationVersion.getBaseVersion().equals(dbVer.get().getBaseVersion()) ||\n\t\t\t\t\t(!Version.TYPE.FINAL.equals(dbVer.get().getVersionType()) &&\n\t\t\t\t\t\t\tVersion.TYPE.FINAL.equals(implementationVersion.getVersionType()))) {\n\t\t\t\tresult = false;\n\n\t\t\t\tString[] errorMsg = new String[]{\n\t\t\t\t\t\t\t\"ERROR! Component \" + dataSourceID + \" (\" + datasourceClass.getSimpleName() + \") \" +  \"schema version is not loaded in the database or it is old!\",\n\t\t\t\t\t\t\t(dbVer.isPresent() ? (\"Version in database: \" + dbVer.get() + \". \") : \"\")\n\t\t\t\t\t\t\t\t\t+ \"Required version: \" + implementationVersion,\n\t\t\t\t\t\t\t\"Please upgrade the installation by running:\",\n\t\t\t\t\t\t\t\"\\t$ ./scripts/tigase.sh upgrade-schema etc/tigase.conf\"};\n\n\t\t\t\t\tif (shutdownServer) {\n\t\t\t\t\t\tTigaseRuntime.getTigaseRuntime().shutdownTigase(errorMsg);\n\t\t\t\t\t}\n\t\t\t\t} else if (implementationVersion.getBaseVersion().equals(dbVer.get().getBaseVersion()) &&\n\t\t\t\t\t\t!Version.TYPE.FINAL.equals(implementationVersion.getVersionType())) {\n\t\t\t\t\tresult = false;\n\n\t\t\t\tfinal SchemaVersionCheckerLogger.VersionCheckerSchemaInfo versionInfo = new SchemaVersionCheckerLogger.VersionCheckerSchemaInfo(\n\t\t\t\t\t\tdatasourceClass, dbVer, implementationVersion);\n\t\t\t\tSchemaVersionCheckerLogger.getInstance().logVersion(versionInfo);\n\n\t\t\t} else {\n\t\t\t\t\t// schema version present in DB and matches component version\n\t\t\t\t\tresult = true;\n\t\t\t\t}\n\t\t} else {\n\t\t\t// there is no annotation so we assume schema version is correct;\n\t\t\tresult = true;\n\t\t}\n\t\treturn result;\n\t}\n\n\tdefault boolean automaticSchemaManagement() {\n\t\treturn true;\n\t}\n\n\t/**\n\t * Method obtains version of the schema for particular component stored in the database.\n\t *\n\t * @param component name of the component for which we want to get the schema version\n\t *\n\t * @return an optional value of the version.\n\t */\n\tOptional<Version> getSchemaVersion(String component);\n\n\n\t/**\n\t * Returns a DB connection string or DB connection URI.\n\t *\n\t * @return a <code>String</code> value representing database connection string.\n\t */\n\tString getResourceUri();\n\n\t/**\n\t * The method is called to initialize the data repository.\n\t *\n\t * @param resource_uri value in most cases representing the database connection string.\n\t *\n\t * @throws RepositoryException if there was an error during initialization of data source. Some implementations,\n\t * though, perform so called lazy initialization so even though there is a problem with the underlying data source\n\t * it may not be signaled through this method call.\n\t */\n\tvoid initialize(String resource_uri) throws RepositoryException;\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/DataSourceAware.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport tigase.component.exceptions.RepositoryException;\n\n/**\n * Interface providing a generic way to access data sources by classes implementing it.\n * <br>\n * It is required to be implemented by classes requiring access to data sources and should be used with class extending\n * {@link tigase.db.beans.MDRepositoryBean} for ease of use and to provide support for usage of different data source\n * for different domains (vhosts).\n * <br>\n * Created by andrzej on 09.03.2016.\n */\npublic interface DataSourceAware<T extends DataSource> {\n\n\t/**\n\t * Method called to provide class with instance of a data source.\n\t */\n\tvoid setDataSource(T dataSource) throws RepositoryException;\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/DataSourceHelper.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport tigase.util.reflection.ClassUtilBean;\n\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.regex.Pattern;\n\n/**\n * Created by andrzej on 15.03.2016.\n */\npublic class DataSourceHelper {\n\n\tprivate static final Logger log = Logger.getLogger(DataSourceHelper.class.getCanonicalName());\n\n\tpublic static <T extends Class<?>> Set<T> getAnnotatedClasses(T cls) {\n\t\tSet<T> classes = new HashSet<>();\n\t\tfor (Class<?> clazz : ClassUtilBean.getInstance().getAllClasses()) {\n\t\t\tRepository.Meta annotation = clazz.getAnnotation(Repository.Meta.class);\n\t\t\tif (annotation == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (cls.isAssignableFrom(clazz)) {\n\t\t\t\tclasses.add((T) clazz);\n\t\t\t}\n\t\t}\n\t\treturn classes;\n\t}\n\n\tpublic static <T extends Class<?>> T getDefaultClass(T cls, String uri, Matcher matcher) throws DBInitException {\n\t\tSet<T> classes = getAnnotatedClasses(cls);\n\t\tSet<T> supported = new HashSet<T>();\n\t\tfor (T clazz : classes) {\n\t\t\tif (matcher != null && !matcher.matches(clazz)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tRepository.Meta annotation = (Repository.Meta) clazz.getAnnotation(Repository.Meta.class);\n\t\t\tif (annotation != null) {\n\t\t\t\tString[] supportedUris = annotation.supportedUris();\n\t\t\t\tif (supportedUris != null) {\n\t\t\t\t\tfor (String supportedUri : supportedUris) {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\t\t\"checking if {0} for {1} supports {2} while it supports {3} result = {4}\",\n\t\t\t\t\t\t\t\t\tnew Object[]{clazz.getCanonicalName(), cls.getCanonicalName(), uri, supportedUri,\n\t\t\t\t\t\t\t\t\t\t\t\t Pattern.matches(supportedUri, uri)});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (Pattern.matches(supportedUri, uri)) {\n\t\t\t\t\t\t\tsupported.add(clazz);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tsupported.add(clazz);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tsupported.add(clazz);\n\t\t\t}\n\t\t}\n\t\tif (supported.isEmpty()) {\n\t\t\tthrow new DBInitException(\"[DataSourceHelper] Not found class supporting uri = \" + uri);\n\t\t}\n\t\tT result = null;\n\t\tfor (T clazz : supported) {\n\t\t\tif (result == null) {\n\t\t\t\tresult = clazz;\n\t\t\t} else {\n\t\t\t\tRepository.Meta ar = (Repository.Meta) result.getAnnotation(Repository.Meta.class);\n\t\t\t\tRepository.Meta ac = (Repository.Meta) clazz.getAnnotation(Repository.Meta.class);\n\t\t\t\tif (ac == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif ((ar == null && ac != null) || (!ar.isDefault() &&\n\t\t\t\t\t\t((ar.supportedUris() == null && ac.supportedUris() != null) || ac.isDefault()))) {\n\t\t\t\t\tresult = clazz;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Method returns class which would be by default used as implementation of class\n\t */\n\tpublic static <T extends Class<?>> T getDefaultClass(T cls, String uri) throws DBInitException {\n\t\treturn getDefaultClass(cls, uri, null);\n\t}\n\n\tpublic static String getDefaultClassName(Class cls, String uri) throws DBInitException {\n\t\tClass result = getDefaultClass(cls, uri);\n\t\treturn result.getCanonicalName();\n\t}\n\n\tpublic static interface Matcher {\n\n\t\tboolean matches(Class cls);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/DataSourcePool.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\n/**\n * Generic interface for all implementations of a pool of data sources.\n * <br>\n * It is not required to implement and provide pool implementation. However, if underlying connection driver does not\n * contain internal connection pool and uses single connection to data storage it is highly recommended to provide\n * pooling implementation for data source to improve performance of access to data source.\n * <br>\n * Created by andrzej on 09.03.2016.\n */\npublic interface DataSourcePool<T extends DataSource>\n\t\textends RepositoryPool<T>, DataSource {\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/DummyRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.util.Version;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.time.Duration;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\n/**\n * DummyRepository is a class with all methods empty. They don't return anything and they don't throw exception.\n * SessionManager requires a user repository to work properly but in some installations there is no need for user\n * repository as authentication is done through external data source and user roster is pulled dynamically.\n * <br>\n * Created: Sat Nov  3 16:17:03 2007\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Repository.Meta(supportedUris = {\"dummy\"})\npublic class DummyRepository\n\t\timplements Repository, DataSource, UserRepository, AuthRepository {\n\n\t@Override\n\tpublic void addDataList(BareJID user, String subnode, String key, String[] list) {\n\t}\n\n\t@Override\n\tpublic void addUser(BareJID user) {\n\t}\n\n\t@Override\n\tpublic long getActiveUsersCountIn(Duration duration) {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void addUser(BareJID user, String password) throws UserExistsException, TigaseDBException {\n\t}\n\n\t// Implementation of tigase.db.UserRepository\n\n\t/**\n\t * {@inheritDoc}\n\t *\n\t * @return a <code>String</code> value of null always.\n\t */\n\t@Override\n\tpublic String getData(BareJID user, String subnode, String key, String def) {\n\t\treturn null;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t *\n\t * @return a <code>String</code> value of null always.\n\t */\n\t@Override\n\tpublic String getData(BareJID user, String subnode, String key) {\n\t\treturn null;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t * <br>\n\t *\n\t * @return a <code>String</code> value of null always.\n\t */\n\t@Override\n\tpublic String getData(BareJID user, String key) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String[] getDataList(BareJID user, String subnode, String key) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String[] getKeys(BareJID user, String subnode) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String[] getKeys(BareJID user) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Optional<Version> getSchemaVersion(String component) {\n\t\treturn Optional.empty();\n\t}\n\n\t@Override\n\tpublic String getResourceUri() {\n\t\treturn null;\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"Support for multi-level nodes will be removed\")\n\tpublic String[] getSubnodes(BareJID user, String subnode) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String[] getSubnodes(BareJID user) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic long getUserUID(BareJID user) throws TigaseDBException {\n\t\treturn -1;\n\t}\n\n\t@Override\n\tpublic List<BareJID> getUsers() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic long getUsersCount() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic long getUsersCount(String domain) {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void initialize(String connStr) throws RepositoryException {\n\t\t// nothing to do\n\t}\n\n\t@Override\n\t@Deprecated\n\tpublic void initRepository(String string, Map<String, String> params) {\n\t}\n\n\t@Override\n\tpublic void loggedIn(BareJID jid) throws TigaseDBException {\n\t}\n\n\t@Override\n\tpublic void logout(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t}\n\n\t@Override\n\tpublic boolean otherAuth(Map<String, Object> authProps)\n\t\t\tthrows UserNotFoundException, TigaseDBException, AuthorizationException {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void queryAuth(Map<String, Object> authProps) {\n\t}\n\n\t@Override\n\tpublic void removeData(BareJID user, String subnode, String key) {\n\t}\n\n\t@Override\n\tpublic void removeData(BareJID user, String key) {\n\t}\n\n\t@Override\n\tpublic void removeSubnode(BareJID user, String subnode) {\n\t}\n\n\t@Override\n\tpublic void removeUser(BareJID user) {\n\t}\n\n\t@Override\n\tpublic void setData(BareJID user, String subnode, String key, String value) {\n\t}\n\n\t@Override\n\tpublic void setData(BareJID user, String key, String value) {\n\t}\n\n\t@Override\n\tpublic void setDataList(BareJID user, String subnode, String key, String[] list) {\n\t}\n\n\t@Override\n\tpublic void updatePassword(BareJID user, String password) throws UserNotFoundException, TigaseDBException {\n\t}\n\n\t@Override\n\tpublic boolean userExists(BareJID user) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String getPassword(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\t// TODO Auto-generated method stub\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean isUserDisabled(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void setUserDisabled(BareJID user, Boolean value) throws UserNotFoundException, TigaseDBException {\n\t\tthrow new TigaseDBException(\"Feature not supported\");\n\t}\n\n\t@Override\n\tpublic void setAccountStatus(BareJID user, AccountStatus status) throws TigaseDBException {\n\t\tthrow new TigaseDBException(\"Feature not supported\");\n\t}\n\n\t@Override\n\tpublic AccountStatus getAccountStatus(BareJID user) throws TigaseDBException {\n\t\treturn AccountStatus.active;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/MsgRepositoryIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.server.amp.db.MsgRepository;\nimport tigase.xml.Element;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.concurrent.locks.Condition;\nimport java.util.concurrent.locks.ReentrantLock;\n\n/**\n * Created by andrzej on 13.03.2016.\n */\npublic interface MsgRepositoryIfc<T extends DataSource>\n\t\textends OfflineMsgRepositoryIfc, DataSourceAware<T> {\n\n\tMap<Enum, Long> getMessagesCount(JID to) throws UserNotFoundException, TigaseDBException;\n\n\tList<Element> getMessagesList(JID to) throws UserNotFoundException, TigaseDBException;\n\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\")\n\t@Deprecated\n\tvoid setCondition(ReentrantLock lock, Condition condition);\n\n\tQueue<Element> loadMessagesToJID(List<String> db_ids, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t\t\t boolean delete, MsgRepository.OfflineMessagesProcessor proc)\n\t\t\tthrows UserNotFoundException, TigaseDBException;\n\n\tint deleteMessagesToJID(List<String> db_ids, XMPPResourceConnection session)\n\t\t\tthrows UserNotFoundException;\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/NonAuthUserRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport tigase.xmpp.jid.BareJID;\n\n/**\n * Describe interface WriteOnlyUserRepository here.\n * <br>\n * Created: Sat Oct 14 20:42:30 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface NonAuthUserRepository {\n\n\tpublic static final String PUBLIC_DATA_NODE = \"public\";\n\n\tpublic static final String OFFLINE_DATA_NODE = \"offline\";\n\n\t/**\n\t * <code>addDataList</code> method adds mode entries to existing data list associated with given key in repository\n\t * under given node path. This method is very similar to <code>setDataList(...)</code> except it doesn't remove\n\t * existing data.\n\t *\n\t * @param user a <code>String</code> value of user ID for which data must be stored. User ID consists of user name\n\t * and domain name.\n\t * @param subnode a <code>String</code> value is a node path where data is stored. Node path has the same form as\n\t * directory path on file system:\n\t * <pre>/root/subnode1/subnode2</pre>.\n\t * @param key a <code>String</code> with which the specified values list is to be associated.\n\t * @param value a <code>String</code> is an array of values to be assosiated with the specified key.\n\t *\n\t * @throws UserNotFoundException if user id hasn't been found in reository.\n\t */\n\tvoid addOfflineData(BareJID user, String subnode, String key, String value)\n\t\t\tthrows UserNotFoundException, DataOverwriteException;\n\n\t/**\n\t * <code>addDataList</code> method adds mode entries to existing data list associated with given key in repository\n\t * under given node path. This method is very similar to <code>setDataList(...)</code> except it doesn't remove\n\t * existing data.\n\t *\n\t * @param user a <code>String</code> value of user ID for which data must be stored. User ID consists of user name\n\t * and domain name.\n\t * @param subnode a <code>String</code> value is a node path where data is stored. Node path has the same form as\n\t * directory path on file system:\n\t * <pre>/root/subnode1/subnode2</pre>.\n\t * @param key a <code>String</code> with which the specified values list is to be associated.\n\t * @param list a <code>String[]</code> is an array of values to be assosiated with the specified key.\n\t *\n\t * @throws UserNotFoundException if user id hasn't been found in reository.\n\t */\n\tvoid addOfflineDataList(BareJID user, String subnode, String key, String[] list) throws UserNotFoundException;\n\n\t/**\n\t * Retrieves and returns a value associated with given subnode and key from a publicly available space. The space is\n\t * specific to given virtual domain and is shared among all running cluster nodes. The data are stored in some\n\t * temporary space outside of the registered user data so no information for registered users can be retrieved.<br>\n\t *\n\t * @param domain is a DNS domain name with which the data is associated.\n\t * @param subnode a {@link String} value is a node path where data is stored. Node path has the same form as\n\t * directory path on file system:\n\t * <pre>/root/subnode1/subnode2</pre>.\n\t * @param key a {@link String} with which the needed value is associated.\n\t * @param def a {@link String} value which is returned in case if data for specified key does not exixist in\n\t * repository.\n\t *\n\t * @return a {@link String} value for a given subnode and key or {@code def]} if no entry has been found.\n\t *\n\t * @throws TigaseDBException if there was an error during reading data from the repository.\n\t */\n\tString getDomainTempData(BareJID domain, String subnode, String key, String def) throws TigaseDBException;\n\n\t/**\n\t * <code>getPublicData</code> method returns a value associated with given key for user repository in given subnode.\n\t * If key is not found in repository given default value is returned.\n\t *\n\t * @param user a {@link String} value of user ID for which data must be stored. User ID consists of user name and\n\t * domain name.\n\t * @param subnode a {@link String} value is a node path where data is stored. Node path has the same form as\n\t * directory path on file system:\n\t * <pre>/root/subnode1/subnode2</pre>.\n\t * @param key a {@link String} with which the needed value is associated.\n\t * @param def a {@link String} value which is returned in case if data for specified key does not exixist in\n\t * repository.\n\t *\n\t * @return a {@link String} value for a given subnode and key or {@code def} if no entry has been found.\n\t *\n\t * @throws UserNotFoundException if user id hasn't been found in reository.\n\t */\n\tString getPublicData(BareJID user, String subnode, String key, String def) throws UserNotFoundException;\n\n\t/**\n\t * <code>getPublicDataList</code> method returns array of values associated with given key or <code>null</code> if\n\t * given key does not exist for given user ID in given node path.\n\t *\n\t * @param user a {@link String} value of user ID for which data must be stored. User ID consists of user name and\n\t * domain name.\n\t * @param subnode a {@link String} value is a node path where data is stored. Node path has the same form as\n\t * directory path on file system:\n\t * <pre>/root/subnode1/subnode2</pre>.\n\t * @param key a {@link String} with which the needed values list is associated.\n\t *\n\t * @return a <code>String[]</code> value\n\t *\n\t * @throws UserNotFoundException if user id hasn't been found in reository.\n\t */\n\tString[] getPublicDataList(BareJID user, String subnode, String key) throws UserNotFoundException;\n\n\t/**\n\t * Retrieves and returns a value associated with given subnode and key from a publicly available space. The space is\n\t * specific for the Tigase instance and is not shared among different cluster nodes. The data is stored in some\n\t * temporary space outside of the registered user data. So no information for registered users can be\n\t * retrieved.<br>\n\t *\n\t * @param subnode a {@link String} value is a node path where data is stored. Node path has the same form as\n\t * directory path on file system:\n\t * <pre>/root/subnode1/subnode2</pre>.\n\t * @param key a {@link String} with which the needed value is associated.\n\t * @param def a {@link String} value which is returned in case if data for specified key does not exixist in\n\t * repository.\n\t *\n\t * @return a {@link String} value for a given subnode and key or <code>def</code> if no entry has been found.\n\t *\n\t * @throws TigaseDBException if there was an error during reading data from the repository.\n\t */\n\tString getTempData(String subnode, String key, String def) throws TigaseDBException;\n\n\t/**\n\t * The method allows to store some temporary data by the plugin in publicly available space. The space is specific\n\t * to given virtual domain and is shared among all running cluster nodes. The data is stored in some place outside\n\t * of the normal user space so no information for registered user can be overwriten.<br> If there is already a value\n\t * for a given subnode and key it will be overwritten otherwise a new entry will be created.\n\t *\n\t * @param domain is a DNS domain name with which the data is associated.\n\t * @param subnode a {@link String} value is a node path where data is stored. Node path has the same form as\n\t * directory path on file system:\n\t * <pre>/root/subnode1/subnode2</pre>.\n\t * @param key a {@link String} with which the specified values list is to be associated.\n\t * @param value a {@link String} is an array of values to be assosiated with the specified key.\n\t *\n\t * @throws TigaseDBException if there was an error during writing data to the repository.\n\t */\n\tvoid putDomainTempData(BareJID domain, String subnode, String key, String value) throws TigaseDBException;\n\n\t/**\n\t * The method allows to store some temporary data by the plugin in publicly available space. The space is specific\n\t * for the Tigase instance and is not shared among different cluster nodes. The data is stored in some place outside\n\t * of the normal user space so no information for registered user can be overwriten.<br> If there is already a value\n\t * for a given subnode and key it will be overwritten otherwise a new entry will be created.\n\t *\n\t * @param subnode a {@link String} value is a node path where data is stored. Node path has the same form as\n\t * directory path on file system:\n\t * <pre>/root/subnode1/subnode2</pre>.\n\t * @param key a {@link String} with which the specified values list is to be associated.\n\t * @param value a {@link String} is an array of values to be assosiated with the specified key.\n\t *\n\t * @throws TigaseDBException if there was an error during writing data to the repository.\n\t */\n\tvoid putTempData(String subnode, String key, String value) throws TigaseDBException;\n\n\t/**\n\t * The method allows to remove existing data stored in a temporary storage space associated with a given DNS\n\t * domain.\n\t *\n\t * @param domain is a DNS domain name with which the data is associated.\n\t * @param subnode a {@link String} value is a node path where data is stored. Node path has the same form as\n\t * directory path on file system:\n\t * <pre>/root/subnode1/subnode2</pre>.\n\t * @param key a {@link String} with which the specified values list is to be associated.\n\t *\n\t * @throws TigaseDBException if there was an error during writing data to the repository.\n\t */\n\tvoid removeDomainTempData(BareJID domain, String subnode, String key) throws TigaseDBException;\n\n\t/**\n\t * The method allows to remove existing data stored in the Tigase instance specific temporary storage.\n\t *\n\t * @param subnode a {@link String} value is a node path where data is stored. Node path has the same form as\n\t * directory path on file system:\n\t * <pre>/root/subnode1/subnode2</pre>.\n\t * @param key a {@link String} with which the specified values list is to be associated.\n\t *\n\t * @throws TigaseDBException if there was an error during writing data to the repository.\n\t */\n\tvoid removeTempData(String subnode, String key) throws TigaseDBException;\n}    // WriteOnlyUserRepository\n\n"
  },
  {
    "path": "src/main/java/tigase/db/NonAuthUserRepositoryImpl.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.core.Kernel;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentSkipListSet;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: May 3, 2010 1:23:45 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"nonAuthUserRepository\", parent = Kernel.class, active = true, exportable = true)\npublic class NonAuthUserRepositoryImpl\n\t\timplements NonAuthUserRepository {\n\n\tprivate static final Logger log = Logger.getLogger(NonAuthUserRepositoryImpl.class.getName());\n\n\tprivate final Set<BareJID> existing_domains = new ConcurrentSkipListSet<BareJID>();\n\t@ConfigField(alias = \"offline-user-autocreate\", desc = \"Autocreate offline users\")\n\tprivate boolean autoCreateOffline = false;\n\tprivate BareJID defDomain = null;\n\t@Inject\n\tprivate UserRepository rep;\n\n\t\tpublic NonAuthUserRepositoryImpl(UserRepository userRep, BareJID defDomain, boolean autoCreateOffline) {\n\t\trep = userRep;\n\t\tthis.defDomain = defDomain;\n\t\tthis.autoCreateOffline = autoCreateOffline;\n\t}\n\n\t/**\n\t * Constructor for bean\n\t */\n\tpublic NonAuthUserRepositoryImpl() {\n\n\t}\n\n\t@Override\n\tpublic void addOfflineData(BareJID user, String subnode, String key, String value)\n\t\t\tthrows UserNotFoundException, DataOverwriteException {\n\t\tString node = calcNode(OFFLINE_DATA_NODE, subnode);\n\n\t\ttry {\n\t\t\tString data = rep.getData(user, node, key);\n\n\t\t\tif (data == null) {\n\t\t\t\trep.setData(user, node, key, value);\n\t\t\t} else {\n\t\t\t\tthrow new DataOverwriteException(\"Not authorized attempt to overwrite data.\");\n\t\t\t}    // end of if (data == null) else\n\t\t} catch (TigaseDBException e) {\n\t\t\tlog.log(Level.SEVERE, \"Problem accessing repository data.\", e);\n\t\t}      // end of try-catch\n\t}\n\n\t@Override\n\tpublic void addOfflineDataList(BareJID user, String subnode, String key, String[] list)\n\t\t\tthrows UserNotFoundException {\n\t\ttry {\n\t\t\tif (autoCreateOffline || rep.userExists(user)) {\n\t\t\t\trep.addDataList(user, calcNode(OFFLINE_DATA_NODE, subnode), key, list);\n\t\t\t} else {\n\t\t\t\tthrow new UserNotFoundException(\"User: \" + user + \" has not been found inthe repository.\");\n\t\t\t}\n\t\t} catch (UserNotFoundException e) {\n\n\t\t\t// This is quite normal for anonymous users.\n\t\t\tlog.log(Level.CONFIG, \"User not found in repository: {0}\", user);\n\t\t} catch (TigaseDBException e) {\n\t\t\tlog.log(Level.SEVERE, \"Problem accessing repository data.\", e);\n\t\t}    // end of try-catch\n\t}\n\n\t@Override\n\tpublic String getDomainTempData(BareJID domain, String subnode, String key, String def) throws TigaseDBException {\n\t\tcheckDomain(domain);\n\n\t\treturn rep.getData(domain, subnode, key, def);\n\t}\n\n\t@Override\n\tpublic String getPublicData(BareJID user, String subnode, String key, String def) throws UserNotFoundException {\n\t\ttry {\n\t\t\treturn (rep.userExists(user) ? rep.getData(user, calcNode(PUBLIC_DATA_NODE, subnode), key, def) : null);\n\t\t} catch (TigaseDBException e) {\n\t\t\tlog.log(Level.SEVERE, \"Problem accessing repository data.\", e);\n\n\t\t\treturn null;\n\t\t}    // end of try-catch\n\t}\n\n\t@Override\n\tpublic String[] getPublicDataList(BareJID user, String subnode, String key) throws UserNotFoundException {\n\t\ttry {\n\t\t\treturn (rep.userExists(user) ? rep.getDataList(user, calcNode(PUBLIC_DATA_NODE, subnode), key) : null);\n\t\t} catch (TigaseDBException e) {\n\t\t\tlog.log(Level.SEVERE, \"Problem accessing repository data.\", e);\n\n\t\t\treturn null;\n\t\t}    // end of try-catch\n\t}\n\n\t@Override\n\tpublic String getTempData(String subnode, String key, String def) throws TigaseDBException {\n\t\tcheckDomain(defDomain);\n\n\t\treturn rep.getData(defDomain, subnode, key, def);\n\t}\n\n\t@Override\n\tpublic void putDomainTempData(BareJID domain, String subnode, String key, String value) throws TigaseDBException {\n\t\tcheckDomain(domain);\n\t\trep.setData(domain, subnode, key, value);\n\t}\n\n\t@Override\n\tpublic void putTempData(String subnode, String key, String value) throws TigaseDBException {\n\t\tcheckDomain(defDomain);\n\t\trep.setData(defDomain, subnode, key, value);\n\t}\n\n\t@Override\n\tpublic void removeDomainTempData(BareJID domain, String subnode, String key) throws TigaseDBException {\n\t\tcheckDomain(defDomain);\n\t\trep.removeData(domain, subnode, key);\n\t}\n\n\t@Override\n\tpublic void removeTempData(String subnode, String key) throws TigaseDBException {\n\t\tcheckDomain(defDomain);\n\t\trep.removeData(defDomain, subnode, key);\n\t}\n\n\tprivate String calcNode(String base, String subnode) {\n\t\tif (subnode == null) {\n\t\t\treturn base;\n\t\t}    // end of if (subnode == null)\n\n\t\treturn base + \"/\" + subnode;\n\t}\n\n\tprivate void checkDomain(BareJID domain) throws TigaseDBException {\n\t\tif (!existing_domains.contains(domain) && !rep.userExists(domain)) {\n\t\t\trep.addUser(domain);\n\t\t\texisting_domains.add(domain);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/OfflineMsgRepositoryIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.xml.Element;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Date;\nimport java.util.Queue;\n\n/**\n * Interface for storing and restoring offline Elements.\n * <br>\n * Created: May 11, 2010 6:56:14 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\npublic interface OfflineMsgRepositoryIfc\n\t\textends Repository {\n\n\t/**\n\t * Loads head of the payloads queue which holds items that would be expired after stated time with an option to\n\t * delete them from repository after being retrieved. This is blocking method, which means if there is not data to\n\t * return, implementation should block the call until data is available.\n\t *\n\t * @param time time in milliseconds representing time after which given message would be considered as expired\n\t * @param delete boolean parameter controlling whether messages should be removed from repository after they\n\t * retrieved.\n\t *\n\t * @return head of the payloads queue which holds items that would be expired after stated time with an option to\n\t * delete them from repository after being retrieved.\n\t */\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"Will be removed. MsgRepository pool beans \")\n\t@Deprecated\n\tElement getMessageExpired(long time, boolean delete);\n\n\t/**\n\t * Loads all payloads for the given user's {@link JID} from repository.\n\t *\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param delete boolean parameter controlling whether messages should be removed from repository after they\n\t * retrieved.\n\t *\n\t * @return a {@link Queue} of {@link Element} objects representing stored payloads for the given user's {@link JID}\n\t *\n\t */\n\tQueue<Element> loadMessagesToJID(XMPPResourceConnection session, boolean delete) throws UserNotFoundException, TigaseDBException;\n\n\t/**\n\t * Saves the massage to the repository\n\t *\n\t * @param from {@link JID} denotes address of the sender\n\t * @param to {@link JID} denotes address of the receiver\n\t * @param expired {@link Date} object denoting expiration date of the message\n\t * @param msg {@link Element} payload of the stanza to be saved\n\t * @param userRepo {@link NonAuthUserRepository} instance of non auth user repository to get user settings for\n\t * offline messages\n\t *\n\t * @return {@code true} if the packet was correctly saved to repository, {@code false} otherwise.\n\t *\n\t */\n\tboolean storeMessage(JID from, JID to, Date expired, Element msg, NonAuthUserRepository userRepo)\n\t\t\tthrows UserNotFoundException, TigaseDBException;\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/Repository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport tigase.annotations.TigaseDeprecated;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.Map;\n\n/**\n * Base interface which should be implemented by every repository to have one common interface\n *\n * @author andrzej\n */\npublic interface Repository {\n\n\t/**\n\t * Method is deprecated and should not be user any more.\n\t * <br>\n\t * The method is called to initialize the data repository. Depending on the implementation all the initialization\n\t * parameters can be passed either via <code>resource_uri</code> parameter as the database connection string or via\n\t * <code>params</code> map if the required repository parameters are more complex or both.\n\t *\n\t * @param resource_uri value in most cases representing the database connection string.\n\t * @param params is a <code>Map</code> with repository properties necessary to initialize and perform all the\n\t * functions. The initialization parameters are implementation dependent.\n\t *\n\t * @throws tigase.db.DBInitException if there was an error during repository initialization. Some implementations,\n\t * though, perform so called lazy initialization so even though there is a problem with the underlying repository it\n\t * may not be signaled through this method call.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tdefault void initRepository(String resource_uri, Map<String, String> params) throws DBInitException {\n\n\t}\n\n\t/**\n\t * Meta created to add possibility to retrieve information about implementation of repository (ie. supported\n\t * database URI)\n\t */\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target(ElementType.TYPE)\n\tpublic static @interface Meta {\n\n\t\t/**\n\t\t * Is it default implementation for supported URIs?\n\t\t *\n\t\t * @return true if yes\n\t\t */\n\t\tboolean isDefault() default false;\n\n\t\t/**\n\t\t * Method returns and array of strings with regexps matching URIs which are supported by annotated class.\n\t\t *\n\t\t * @return string array of regular expressions\n\t\t */\n\t\tString[] supportedUris();\n\t}\n\n\t/**\n\t * Annotation provides information about schema (ie. database schema) required by annotated class.\n\t */\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target(ElementType.TYPE)\n\tpublic static @interface SchemaId {\n\n\t\t/**\n\t\t * Method returns identifiers of a schema (ie. database schema) required by annotated class.\n\t\t *\n\t\t * @return id of a schema\n\t\t */\n\t\tString id();\n\n\t\t/**\n\t\t * Method returns name of a schema required by annotated class. * <br> Result of this method is intended to be\n\t\t * displayed to the user.\n\t\t *\n\t\t * @return name of a schema\n\t\t */\n\t\tString name();\n\n\t\t/**\n\t\t * Method indicates whether schema definition is stored externally to the class (for example in {@code .sql} files.\n\t\t *\n\t\t * @return true if the schema definitions are stored in {@code .sql} files.\n\t\t */\n\t\tboolean external() default true;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/RepositoryFactory.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.osgi.ModulesManagerImpl;\nimport tigase.util.reflection.ClassUtilBean;\n\nimport java.sql.SQLException;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.regex.Pattern;\n\n/**\n * Describe class RepositoryFactory here.\n * <br>\n * Created: Tue Oct 24 22:13:52 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Deprecated\n@TigaseDeprecated(since = \"8.0.0\")\npublic abstract class RepositoryFactory {\n\n\tpublic static final String AUTH_DOMAIN_POOL_CLASS = \"--auth-domain-repo-pool\";\n\n\tpublic static final String AUTH_DOMAIN_POOL_CLASS_PROP_KEY = \"auth-domain-repo-pool\";\n\n\tpublic static final String AUTH_DOMAIN_POOL_CLASS_PROP_VAL = \"tigase.db.AuthRepositoryMDImpl\";\n\n\tpublic static final String AUTH_REPO_CLASS_PROP_KEY = \"auth-repo-class\";\n\n\t@Deprecated\n\tpublic static final String AUTH_REPO_CLASS_PROP_VAL = \"tigase.db.jdbc.TigaseCustomAuth\";\n\n\tpublic static final String AUTH_REPO_DOMAINS_PROP_KEY = \"auth-repo-domains\";\n\n\tpublic static final String AUTH_REPO_PARAMS_NODE = \"auth-repo-params\";\n\n\tpublic static final String AUTH_REPO_POOL_CLASS = \"--auth-repo-pool\";\n\n\tpublic static final String AUTH_REPO_POOL_CLASS_PROP_DEF = \"tigase.db.AuthRepositoryPool\";\n\n\tpublic static final String AUTH_REPO_POOL_CLASS_PROP_KEY = \"auth-repo-pool\";\n\n\tpublic static final String AUTH_REPO_POOL_CLASS_PROP_VAL = null;\n\n\tpublic static final String AUTH_REPO_POOL_SIZE = \"--auth-repo-pool-size\";\n\n\tpublic static final String AUTH_REPO_POOL_SIZE_PROP_KEY = \"auth-repo-pool-size\";\n\n\tpublic static final int REPO_POOL_SIZE_FACTOR_PROP_VAL = 4;\n\n\t// AuthRepository properties\n\n\tpublic static final int AUTH_REPO_POOL_SIZE_PROP_VAL = Math.max(10, Runtime.getRuntime().availableProcessors() *\n\t\t\tREPO_POOL_SIZE_FACTOR_PROP_VAL);\n\n\tpublic static final String AUTH_REPO_URL_PROP_KEY = \"auth-repo-url\";\n\n\tpublic static final String DATA_REPO_CLASS_PROP_KEY = \"data-repo\";\n\n\t@Deprecated\n\tpublic static final String DATA_REPO_CLASS_PROP_VAL = \"tigase.db.jdbc.DataRepositoryImpl\";\n\n\tpublic static final String DATA_REPO_POOL_CLASS_PROP_KEY = \"data-repo-pool\";\n\n\tpublic static final String DATA_REPO_POOL_CLASS_PROP_VAL = \"tigase.db.DataRepositoryPool\";\n\n\tpublic static final String DATA_REPO_POOL_SIZE = \"--data-repo-pool-size\";\n\n\tpublic static final String DATA_REPO_POOL_SIZE_PROP_KEY = \"data-repo-pool-size\";\n\tpublic static final int DATA_REPO_POOL_SIZE_PROP_VAL = Math.max(10, Runtime.getRuntime().availableProcessors() *\n\t\t\tREPO_POOL_SIZE_FACTOR_PROP_VAL);\n\tpublic static final String DERBY_REPO_CLASS_PROP_VAL = \"tigase.db.jdbc.JDBCRepository\";\n\n\t// DataRepository properties\n\tpublic static final String DERBY_REPO_URL_PROP_VAL = \"jdbc:derby:tigase-derbydb;create=true\";\n\n\t// repositories classes and URLs\n\tpublic static final String DRUPAL_REPO_URL_PROP_VAL = \"jdbc:mysql://localhost/drupal?user=root&password=mypass\";\n\tpublic static final String DRUPALWP_REPO_CLASS_PROP_VAL = \"tigase.db.jdbc.DrupalWPAuth\";\n\tpublic static final String DUMMY_REPO_CLASS_PROP_VAL = \"tigase.db.DummyRepository\";\n\tpublic static final String GEN_AUTH_DB = \"--auth-db\";\n\tpublic static final String GEN_AUTH_DB_URI = \"--auth-db-uri\";\n\tpublic static final String GEN_USER_DB_PROP_KEY = \"user-db\";\n\tpublic static final String GEN_USER_DB = \"--\" + GEN_USER_DB_PROP_KEY;\n\tpublic static final String GEN_USER_DB_URI_PROP_KEY = \"user-db-uri\";\n\tpublic static final String GEN_USER_DB_URI = \"--\" + GEN_USER_DB_URI_PROP_KEY;\n\tpublic static final String LIBRESOURCE_REPO_CLASS_PROP_VAL = \"tigase.db.jdbc.LibreSourceAuth\";\n\tpublic static final String LIBRESOURCE_REPO_URL_PROP_VAL = \"jdbc:postgresql://localhost/libresource?user=demo\";\n\t/** Default MS SQL Server JDBC class */\n\tpublic static final String SQLSERVER_REPO_CLASS_PROP_VAL = \"tigase.db.jdbc.JDBCRepository\";\n\t/** Default MS SQL Server JDBC connection string */\n\tpublic static final String SQLSERVER_REPO_URL_PROP_VAL = \"jdbc:sqlserver://localhost:1433;databaseName=tigasedb;user=tigase;password=tigase;schema=dbo\";\n\tpublic static final String MYSQL_REPO_CLASS_PROP_VAL = \"tigase.db.jdbc.JDBCRepository\";\n\tpublic static final String MYSQL_REPO_URL_PROP_VAL = \"jdbc:mysql://localhost/tigase?user=root&password=mypass\";\n\tpublic static final String PGSQL_REPO_CLASS_PROP_VAL = \"tigase.db.jdbc.JDBCRepository\";\n\tpublic static final String PGSQL_REPO_URL_PROP_VAL = \"jdbc:postgresql://localhost/tigase?user=tigase\";\n\tpublic static final String SHARED_AUTH_REPO_PARAMS_PROP_KEY = \"shared-auth-repo-params\";\n\tpublic static final String SHARED_AUTH_REPO_PROP_KEY = \"shared-auth-repo\";\n\tpublic static final String SHARED_USER_REPO_PARAMS_PROP_KEY = \"shared-user-repo-params\";\n\tpublic static final String SHARED_USER_REPO_PROP_KEY = \"shared-user-repo\";\n\tpublic static final String TIGASE_AUTH_REPO_CLASS_PROP_VAL = \"tigase.db.jdbc.TigaseAuth\";\n\tpublic static final String TIGASE_AUTH_REPO_URL_PROP_VAL = \"jdbc:mysql://localhost/tigasedb?user=tigase_user&password=mypass\";\n\tpublic static final String TIGASE_CUSTOM_AUTH_REPO_CLASS_PROP_VAL = \"tigase.db.jdbc.TigaseCustomAuth\";\n\tpublic static final String USER_DOMAIN_POOL_CLASS = \"--user-domain-repo-pool\";\n\tpublic static final String USER_DOMAIN_POOL_CLASS_PROP_KEY = \"user-domain-repo-pool\";\n\tpublic static final String USER_DOMAIN_POOL_CLASS_PROP_VAL = \"tigase.db.UserRepositoryMDImpl\";\n\tpublic static final String USER_REPO_CLASS_PROP_KEY = \"user-repo-class\";\n\t@Deprecated\n\tpublic static final String USER_REPO_CLASS_PROP_VAL = \"tigase.db.jdbc.JDBCRepository\";\n\tpublic static final String USER_REPO_DOMAINS_PROP_KEY = \"user-repo-domains\";\n\tpublic static final String USER_REPO_PARAMS_NODE = \"user-repo-params\";\n\tpublic static final String USER_REPO_POOL_CLASS = \"--user-repo-pool\";\n\tpublic static final String USER_REPO_POOL_CLASS_PROP_DEF = \"tigase.db.UserRepositoryPool\";\n\tpublic static final String USER_REPO_POOL_CLASS_PROP_KEY = \"user-repo-pool\";\n\tpublic static final String USER_REPO_POOL_CLASS_PROP_VAL = null;\n\tpublic static final String USER_REPO_POOL_SIZE = \"--user-repo-pool-size\";\n\tpublic static final String USER_REPO_POOL_SIZE_PROP_KEY = \"user-repo-pool-size\";\n\tpublic static final int USER_REPO_POOL_SIZE_PROP_VAL = Math.max(10, Runtime.getRuntime().availableProcessors() *\n\t\t\tREPO_POOL_SIZE_FACTOR_PROP_VAL);\n\tpublic static final String USER_REPO_URL_PROP_KEY = \"user-repo-url\";\n\n\t// UserRepository properties\n\tpublic static final String XML_REPO_CLASS_PROP_VAL = \"tigase.db.xml.XMLRepository\";\n\tpublic static final String XML_REPO_URL_PROP_VAL = \"user-repository.xml\";\n\tpublic static final String DATABASE_TYPE_PROP_KEY = \"database-type\";\n\tprivate static final String REPO_POOL_SIZE_PROP_KEY = \"repo-pool-size\";\n\tprivate static final String DEF_REPO_POOL_SIZE_PROP_KEY = \"def-repo-pool-size\";\n\tprivate static final Logger log = Logger.getLogger(RepositoryFactory.class.getCanonicalName());\n\n\tprivate static final ConcurrentMap<String, UserRepository> user_repos = new ConcurrentHashMap<String, UserRepository>(\n\t\t\tUSER_REPO_POOL_SIZE_PROP_VAL);\n\tprivate static final ConcurrentMap<String, DataRepository> data_repos = new ConcurrentHashMap<String, DataRepository>(\n\t\t\tDATA_REPO_POOL_SIZE_PROP_VAL);\n\tprivate static final ConcurrentMap<String, AuthRepository> auth_repos = new ConcurrentHashMap<String, AuthRepository>(\n\t\t\tAUTH_REPO_POOL_SIZE_PROP_VAL);\n\n\tpublic static AuthRepository getAuthRepository(String class_name, String resource, Map<String, String> params)\n\t\t\tthrows ClassNotFoundException, InstantiationException, IllegalAccessException, DBInitException {\n\t\tString cls = class_name;\n\n\t\tif (cls == null) {\n\t\t\tcls = System.getProperty(AUTH_REPO_CLASS_PROP_KEY);\n\t\t\tif (cls == null) {\n\t\t\t\tcls = getRepoClassName(AuthRepository.class, resource);\n\t\t\t}\n\t\t}\n\t\tif (params == null) {\n\t\t\tparams = new LinkedHashMap<String, String>(AUTH_REPO_POOL_SIZE_PROP_VAL);\n\t\t}\n\t\tcls = getRepoClass(cls);\n\n\t\tAuthRepository repo = auth_repos.get(cls + resource);\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Getting AuthRepository using: {0} for: {1}; repository instance: \",\n\t\t\t\t\tnew Object[]{cls, resource, repo});\n\t\t}\n\n\t\tif (repo == null) {\n\t\t\tString repo_pool_cls = System.getProperty(AUTH_REPO_POOL_CLASS_PROP_KEY, AUTH_REPO_POOL_CLASS_PROP_VAL);\n\t\t\tint repo_pool_size;\n\n\t\t\tif (params.get(RepositoryFactory.AUTH_REPO_POOL_SIZE_PROP_KEY) != null) {\n\t\t\t\trepo_pool_size = Integer.parseInt(params.get(RepositoryFactory.AUTH_REPO_POOL_SIZE_PROP_KEY));\n\t\t\t\tparams.put(RepositoryFactory.REPO_POOL_SIZE_PROP_KEY, String.valueOf(repo_pool_size));\n\t\t\t} else if (Integer.getInteger(AUTH_REPO_POOL_SIZE_PROP_KEY) != null) {\n\t\t\t\trepo_pool_size = Integer.getInteger(AUTH_REPO_POOL_SIZE_PROP_KEY);\n\t\t\t\tparams.put(RepositoryFactory.REPO_POOL_SIZE_PROP_KEY, String.valueOf(repo_pool_size));\n\t\t\t} else {\n\t\t\t\trepo_pool_size = AUTH_REPO_POOL_SIZE_PROP_VAL;\n\t\t\t\tparams.put(RepositoryFactory.DEF_REPO_POOL_SIZE_PROP_KEY, String.valueOf(repo_pool_size));\n\t\t\t}\n\t\t\tparams.put(RepositoryFactory.DATABASE_TYPE_PROP_KEY, class_name);\n\t\t\tif (repo_pool_cls != null) {\n\t\t\t\tAuthRepositoryPool repo_pool = (AuthRepositoryPool) ModulesManagerImpl.getInstance()\n\t\t\t\t\t\t.forName(repo_pool_cls)\n\t\t\t\t\t\t.newInstance();\n\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"No AuthRepository, creating new; repo_pool_cls: {0}, repo_pool_size: {1}\",\n\t\t\t\t\t\t\tnew Object[]{repo_pool_cls, repo_pool_size});\n\t\t\t\t}\n\n\t\t\t\trepo_pool.initRepository(resource, params);\n\t\t\t\tfor (int i = 0; i < repo_pool_size; i++) {\n\t\t\t\t\trepo = (AuthRepository) ModulesManagerImpl.getInstance().forName(cls).newInstance();\n\t\t\t\t\trepo.initRepository(resource, params);\n\t\t\t\t\trepo_pool.addRepo(repo);\n\t\t\t\t}\n\t\t\t\trepo = repo_pool;\n\t\t\t} else {\n\t\t\t\trepo = (AuthRepository) ModulesManagerImpl.getInstance().forName(cls).newInstance();\n\t\t\t\trepo.initRepository(resource, params);\n\t\t\t}\n\t\t\tauth_repos.put(cls + resource, repo);\n\t\t}\n\n\t\treturn repo;\n\t}\n\n\tpublic static DataRepository getDataRepository(String class_name, String resource, Map<String, String> params)\n\t\t\tthrows ClassNotFoundException, InstantiationException, IllegalAccessException, DBInitException,\n\t\t\t\t   SQLException {\n\t\tString cls = class_name;\n\n\t\tif (cls == null) {\n\t\t\tcls = System.getProperty(DATA_REPO_CLASS_PROP_KEY);\n\t\t\tif (cls == null) {\n\t\t\t\tcls = getRepoClassName(DataRepository.class, resource);\n\t\t\t}\n\t\t}\n\t\tif (params == null) {\n\t\t\tparams = new LinkedHashMap<String, String>(DATA_REPO_POOL_SIZE_PROP_VAL);\n\t\t}\n\t\tcls = getRepoClass(cls);\n\n\t\tDataRepository repo = data_repos.get(cls + resource);\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Getting DataRepository: {0} for: {1}; repository instance: \",\n\t\t\t\t\tnew Object[]{cls, resource, repo});\n\t\t}\n\n\t\tif (repo == null) {\n\t\t\tint repo_pool_size;\n\n\t\t\tif (params.get(RepositoryFactory.DATA_REPO_POOL_SIZE_PROP_KEY) != null) {\n\t\t\t\trepo_pool_size = Integer.parseInt(params.get(RepositoryFactory.DATA_REPO_POOL_SIZE_PROP_KEY));\n\t\t\t} else if (params.get(RepositoryFactory.REPO_POOL_SIZE_PROP_KEY) != null) {\n\t\t\t\trepo_pool_size = Integer.parseInt(params.get(RepositoryFactory.REPO_POOL_SIZE_PROP_KEY));\n\t\t\t} else if (Integer.getInteger(DATA_REPO_POOL_SIZE_PROP_KEY) != null) {\n\t\t\t\trepo_pool_size = Integer.getInteger(DATA_REPO_POOL_SIZE_PROP_KEY);\n\t\t\t} else if (params.get(RepositoryFactory.DEF_REPO_POOL_SIZE_PROP_KEY) != null) {\n\t\t\t\trepo_pool_size = Integer.parseInt(params.get(RepositoryFactory.DEF_REPO_POOL_SIZE_PROP_KEY));\n\t\t\t} else {\n\t\t\t\trepo_pool_size = Integer.getInteger(DATA_REPO_POOL_SIZE_PROP_KEY, DATA_REPO_POOL_SIZE_PROP_VAL);\n\t\t\t}\n\t\t\tparams.put(RepositoryFactory.DATABASE_TYPE_PROP_KEY, class_name);\n\t\t\tString repo_pool_cls = System.getProperty(DATA_REPO_POOL_CLASS_PROP_KEY, DATA_REPO_POOL_CLASS_PROP_VAL);\n\n\t\t\tDataRepositoryPool repo_pool = (DataRepositoryPool) ModulesManagerImpl.getInstance()\n\t\t\t\t\t.forName(repo_pool_cls)\n\t\t\t\t\t.newInstance();\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"No DataRepository, creating new; repo_pool_cls: {0}, repo_pool_size: {1}\",\n\t\t\t\t\t\tnew Object[]{repo_pool_cls, repo_pool_size});\n\t\t\t}\n\n\t\t\trepo_pool.initRepository(resource, params);\n\t\t\tfor (int i = 0; i < repo_pool_size; i++) {\n\t\t\t\trepo = (DataRepository) ModulesManagerImpl.getInstance().forName(cls).newInstance();\n\t\t\t\trepo.initRepository(resource, params);\n\t\t\t\trepo_pool.addRepo(repo);\n\t\t\t}\n\t\t\trepo = repo_pool;\n\t\t\tdata_repos.put(cls + resource, repo);\n\t\t}\n\n\t\treturn repo;\n\t}\n\n\t/**\n\t * Method returns class which would be by default used as implementation of class\n\t */\n\tpublic static <T extends Class<? extends Repository>> T getRepoClass(T cls, String uri) throws DBInitException {\n\t\tSet<T> classes = getRepoInternalClasses(cls);\n\t\tSet<T> supported = new HashSet<T>();\n\t\tfor (T clazz : classes) {\n\t\t\tRepository.Meta annotation = clazz.getAnnotation(Repository.Meta.class);\n\t\t\tif (annotation != null) {\n\t\t\t\tString[] supportedUris = annotation.supportedUris();\n\t\t\t\tif (supportedUris != null) {\n\t\t\t\t\tfor (String supportedUri : supportedUris) {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\t\t\"checking if {0} for {1} supports {2} while it supports {3} result = {4}\",\n\t\t\t\t\t\t\t\t\tnew Object[]{clazz.getCanonicalName(), cls.getCanonicalName(), uri, supportedUri,\n\t\t\t\t\t\t\t\t\t\t\t\t Pattern.matches(supportedUri, uri)});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (Pattern.matches(supportedUri, uri)) {\n\t\t\t\t\t\t\tsupported.add(clazz);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tsupported.add(clazz);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tsupported.add(clazz);\n\t\t\t}\n\t\t}\n\t\tif (supported.isEmpty()) {\n\t\t\tthrow new DBInitException(\"[RepositoryFactory] Not found class supporting uri = \" + uri);\n\t\t}\n\t\tT result = null;\n\t\tfor (T clazz : supported) {\n\t\t\tif (result == null) {\n\t\t\t\tresult = clazz;\n\t\t\t} else {\n\t\t\t\tRepository.Meta ar = (Repository.Meta) result.getAnnotation(Repository.Meta.class);\n\t\t\t\tRepository.Meta ac = (Repository.Meta) clazz.getAnnotation(Repository.Meta.class);\n\t\t\t\tif (ac == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif ((ar == null && ac != null) || (!ar.isDefault() &&\n\t\t\t\t\t\t((ar.supportedUris() == null && ac.supportedUris() != null) || ac.isDefault()))) {\n\t\t\t\t\tresult = clazz;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic static String getRepoClass(String repo_name) {\n\t\tString result = repo_name;\n\n\t\tif (repo_name.equals(\"mysql\")) {\n\t\t\tresult = MYSQL_REPO_CLASS_PROP_VAL;\n\t\t}\n\t\tif (repo_name.equals(\"pgsql\")) {\n\t\t\tresult = PGSQL_REPO_CLASS_PROP_VAL;\n\t\t}\n\t\tif (repo_name.equals(\"derby\")) {\n\t\t\tresult = DERBY_REPO_CLASS_PROP_VAL;\n\t\t}\n\t\tif (repo_name.equals(\"tigase-custom-auth\") || repo_name.equals(\"tigase-custom\") ||\n\t\t\t\trepo_name.equals(\"custom-auth\")) {\n\t\t\tresult = TIGASE_CUSTOM_AUTH_REPO_CLASS_PROP_VAL;\n\t\t}\n\t\tif (repo_name.equals(\"tigase-auth\")) {\n\t\t\tresult = TIGASE_AUTH_REPO_CLASS_PROP_VAL;\n\t\t}\n\t\tif (repo_name.equals(\"drupal\") || repo_name.equals(\"wp\")) {\n\t\t\tresult = DRUPALWP_REPO_CLASS_PROP_VAL;\n\t\t}\n\t\tif (repo_name.equals(\"libresource\")) {\n\t\t\tresult = LIBRESOURCE_REPO_CLASS_PROP_VAL;\n\t\t}\n\t\tif (repo_name.equals(\"sqlserver\")) {\n\t\t\tresult = SQLSERVER_REPO_CLASS_PROP_VAL;\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Returns name of class which would be used as repository implementation\n\t *\n\t * @param cls - interface class needs to implement\n\t * @param uri - uri which needs to be supported by implementation\n\t */\n\tpublic static String getRepoClassName(Class cls, String uri) throws DBInitException {\n\t\tClass result = getRepoClass(cls, uri);\n\t\treturn result.getCanonicalName();\n\t}\n\n\t/**\n\t * Method returns internal (available in server classpath) implementation of classes extending or implementing class\n\t * passed as cls parameter.\n\t *\n\t * @param cls - class for which we look for implementations of extensions\n\t *\n\t * @return set of classes matching criteria\n\t */\n\tprivate static <T extends Class<? extends Repository>> Set<T> getRepoInternalClasses(T cls) {\n\t\tHashSet<T> result = new HashSet<T>();\n\t\tfor (Class<?> clazz : ClassUtilBean.getInstance().getAllClasses()) {\n\t\t\tif (cls.isAssignableFrom(clazz)) {\n\t\t\t\tresult.add((T) clazz);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic static UserRepository getUserRepository(String class_name, String resource, Map<String, String> params)\n\t\t\tthrows ClassNotFoundException, InstantiationException, IllegalAccessException, DBInitException {\n\t\tString cls = class_name;\n\n\t\tif (resource == null) {\n\t\t\tresource = System.getProperty(GEN_USER_DB_URI_PROP_KEY);\n\t\t}\n\n\t\tif (cls == null) {\n\t\t\tcls = System.getProperty(USER_REPO_CLASS_PROP_KEY);\n\t\t\tif (cls == null) {\n\t\t\t\tcls = getRepoClassName(UserRepository.class, resource);\n\t\t\t\tif (cls == null) {\n\t\t\t\t\tcls = System.getProperty(GEN_USER_DB_PROP_KEY);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (params == null) {\n\t\t\tparams = new LinkedHashMap<String, String>(USER_REPO_POOL_SIZE_PROP_VAL);\n\t\t}\n\t\tcls = getRepoClass(cls);\n\n\t\tUserRepository repo = user_repos.get(cls + resource);\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Getting UserRepository: {0} for: {1}; repository instance: \",\n\t\t\t\t\tnew Object[]{cls, resource, repo});\n\t\t}\n\n\t\tif (repo == null) {\n\t\t\tString repo_pool_cls = System.getProperty(USER_REPO_POOL_CLASS_PROP_KEY, USER_REPO_POOL_CLASS_PROP_VAL);\n\t\t\tint repo_pool_size;\n\n\t\t\tif (params.get(RepositoryFactory.USER_REPO_POOL_SIZE_PROP_KEY) != null) {\n\t\t\t\trepo_pool_size = Integer.parseInt(params.get(RepositoryFactory.USER_REPO_POOL_SIZE_PROP_KEY));\n\t\t\t\tparams.put(RepositoryFactory.REPO_POOL_SIZE_PROP_KEY, String.valueOf(repo_pool_size));\n\t\t\t} else if (Integer.getInteger(USER_REPO_POOL_SIZE_PROP_KEY) != null) {\n\t\t\t\trepo_pool_size = Integer.getInteger(USER_REPO_POOL_SIZE_PROP_KEY);\n\t\t\t\tparams.put(RepositoryFactory.REPO_POOL_SIZE_PROP_KEY, String.valueOf(repo_pool_size));\n\t\t\t} else {\n\t\t\t\trepo_pool_size = USER_REPO_POOL_SIZE_PROP_VAL;\n\t\t\t\tparams.put(RepositoryFactory.DEF_REPO_POOL_SIZE_PROP_KEY, String.valueOf(repo_pool_size));\n\t\t\t}\n\n\t\t\tparams.put(RepositoryFactory.DATABASE_TYPE_PROP_KEY, class_name);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"No UserRepository, creating new; repo_pool_cls: {0}, repo_pool_size: {1}\",\n\t\t\t\t\t\tnew Object[]{repo_pool_cls, repo_pool_size});\n\t\t\t}\n\n\t\t\tif (repo_pool_cls != null) {\n\t\t\t\tUserRepositoryPool repo_pool = (UserRepositoryPool) ModulesManagerImpl.getInstance()\n\t\t\t\t\t\t.forName(repo_pool_cls)\n\t\t\t\t\t\t.newInstance();\n\n\t\t\t\trepo_pool.initRepository(resource, params);\n\t\t\t\tfor (int i = 0; i < repo_pool_size; i++) {\n\t\t\t\t\trepo = (UserRepository) ModulesManagerImpl.getInstance().forName(cls).newInstance();\n\t\t\t\t\trepo.initRepository(resource, params);\n\t\t\t\t\trepo_pool.addRepo(repo);\n\t\t\t\t}\n\t\t\t\trepo = repo_pool;\n\t\t\t} else {\n\t\t\t\trepo = (UserRepository) ModulesManagerImpl.getInstance().forName(cls).newInstance();\n\t\t\t\trepo.initRepository(resource, params);\n\t\t\t}\n\t\t\tuser_repos.put(cls + resource, repo);\n\t\t}\n\n\t\treturn repo;\n\t}\n\n}    // RepositoryFactory\n\n"
  },
  {
    "path": "src/main/java/tigase/db/RepositoryPool.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\n/**\n * Generic interface of repository pool implementations.\n * <br>\n * Created by andrzej on 08.03.2016.\n */\npublic interface RepositoryPool<T>\n\t\textends Repository {\n\n\t/**\n\t * Method called to add instance of a repository to the repository pool.\n\t *\n\t * @param repo instance of a repository\n\t */\n\tvoid addRepo(T repo);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/Schema.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\n/**\n * Created by andrzej on 04.05.2017.\n */\npublic class Schema {\n\n\tpublic static final String SERVER_SCHEMA_ID = \"server\";\n\tpublic static final String SERVER_SCHEMA_NAME = \"Tigase XMPP Server (Core)\";\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/TigaseDBException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport tigase.component.exceptions.RepositoryException;\n\n/**\n * Describe class TigaseDBException here.\n * <br>\n * Created: Thu Oct 26 12:15:36 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class TigaseDBException\n\t\textends RepositoryException {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * Creates a new <code>TigaseDBException</code> instance.\n\t */\n\tpublic TigaseDBException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic TigaseDBException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n} // TigaseDBException\n"
  },
  {
    "path": "src/main/java/tigase/db/UserExistsException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport tigase.xmpp.jid.BareJID;\n\n/**\n * The <code>UserExistsException</code> is thrown when application tries to add new user with user ID which already\n * exists in repository. According to <code>UserRepository</code> specification there can be the only one registered\n * user with particular ID. <p> Created: Wed Oct 27 14:17:44 2004 </p>\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class UserExistsException\n\t\textends TigaseDBException {\n\n\tprivate static final long serialVersionUID = 1L;\n\tprivate BareJID userId = null;\n\n\tpublic UserExistsException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic UserExistsException(BareJID user, String message, Throwable cause) {\n\t\tsuper(message + \" (\" + user + \")\", cause);\n\t\tuserId = user;\n\t}\n\n\tpublic UserExistsException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n\tpublic BareJID getUserId() {\n\t\treturn userId;\n\t}\n} \n"
  },
  {
    "path": "src/main/java/tigase/db/UserNotFoundException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\n/**\n * The <code>UserNotFoundException</code> exception is thrown when application tries to access data for user which does\n * not exist in repository. <p> Created: Wed Oct 27 14:17:44 2004 </p>\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class UserNotFoundException\n\t\textends TigaseDBException {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * Creates a new <code>UserNotFoundException</code> instance.\n\t */\n\tpublic UserNotFoundException(String message) {\n\t\tsuper(message);\n\t}\n\n\t/**\n\t * Creates a new <code>UserNotFoundException</code> instance.\n\t */\n\tpublic UserNotFoundException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n} // UserNotFoundException\n"
  },
  {
    "path": "src/main/java/tigase/db/UserRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport org.jspecify.annotations.NonNull;\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.eventbus.EventBusEvent;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.function.Function;\n\n/**\n * <code>UserRepository</code> interface defines all functionalities required to store user data. It contains adding,\n * removing and searching methods. User repository is organized as hierarchical data base. It means you can add items to\n * repository on different levels like files in file systems. Instead, however of working with directories you work with\n * nodes. You can create many levels of nodes and store data on any level. It helps to organize data in more logical\n * order.\n * <br>\n * <p> Created: Tue Oct 26 15:09:28 2004 </p>\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface UserRepository\n\t\textends Repository {\n\n\t/**\n\t * <code>addDataList</code> method adds mode entries to existing data list associated with given key in repository\n\t * under given node path. This method is very similar to <code>setDataList(...)</code> except it doesn't remove\n\t * existing data.\n\t *\n\t * @param user a <code>BareJID</code> value of user ID for which data must be stored. User ID consists of user name\n\t * and domain name.\n\t * @param subnode a <code>String</code> value is a node path where data is stored. Node path has the same form as\n\t * directory path on file system:\n\t * <pre>/root/subnode1/subnode2</pre>.\n\t * @param key a <code>String</code> with which the specified values list is to be associated.\n\t * @param list a <code>String[]</code> is an array of values to be associated with the specified key.\n\t *\n\t * @throws UserNotFoundException if user id hasn't been found in repository.\n\t * @throws TigaseDBException if database backend error occurs.\n\t */\n\tvoid addDataList(BareJID user, String subnode, String key, String[] list)\n\t\t\tthrows UserNotFoundException, TigaseDBException;\n\n\t/**\n\t * This <code>addUser</code> method allows to add new user to repository. It <b>must</b> throw en exception\n\t * <code>UserExistsException</code> if such user already exists because user <b>must</b> be unique within user\n\t * repository data base.<br> As one <em>XMPP</em> server can support many virtual internet domains it is required\n\t * that <code>user</code> id consists of user name and domain address: <em>username@domain.address.net</em> for\n\t * example.\n\t *\n\t * @param user a <code>BareJID</code> value of user id consisting of user name and domain address.\n\t *\n\t * @throws UserExistsException if user with the same id already exists.\n\t * @throws TigaseDBException if database backend error occurs.\n\t */\n\tvoid addUser(BareJID user) throws UserExistsException, TigaseDBException;\n\n\t/**\n\t * <code>getData</code> method returns a value associated with given key for user repository in given subnode. If\n\t * key is not found in repository given default value is returned.\n\t *\n\t * @param user a <code>BareJID</code> value of user ID for which data must be stored. User ID consists of user name\n\t * and domain name.\n\t * @param subnode a <code>String</code> value is a node path where data is stored. Node path has the same form as\n\t * directory path on file system:\n\t * <pre>/root/subnode1/subnode2</pre>.\n\t * @param key a <code>String</code> with which the needed value is associated.\n\t * @param def a <code>String</code> value which is returned in case if data for specified key does not exixist in\n\t * repository.\n\t *\n\t * @return a <code>String</code> value\n\t *\n\t * @throws UserNotFoundException if user id hasn't been found in repository.\n\t * @throws TigaseDBException if database backend error occurs.\n\t */\n\tString getData(BareJID user, String subnode, String key, String def)\n\t\t\tthrows UserNotFoundException, TigaseDBException;\n\n\t/**\n\t * <code>getData</code> method returns a value associated with given key for user repository in given subnode. If\n\t * key is not found in repository <code>null</code> value is returned.\n\t *\n\t * @param user a <code>BareJID</code> value of user ID for which data must be stored. User ID consists of user name\n\t * and domain name.\n\t * @param subnode a <code>String</code> value is a node path where data is stored. Node path has the same form as\n\t * directory path on file system:\n\t * <pre>/root/subnode1/subnode2</pre>.\n\t * @param key a <code>String</code> with which the needed value is associated.\n\t *\n\t * @return a <code>String</code> value\n\t *\n\t * @throws UserNotFoundException if user id hasn't been found in repository.\n\t * @throws TigaseDBException if database backend error occurs.\n\t */\n\tString getData(BareJID user, String subnode, String key) throws UserNotFoundException, TigaseDBException;\n\n\t/**\n\t * <code>getData</code> method returns a value associated with given key for user repository in default subnode. If\n\t * key is not found in repository <code>null</code> value is returned.\n\t *\n\t * @param user a <code>BareJID</code> value of user ID for which data must be stored. User ID consists of user name\n\t * and domain name.\n\t * @param key a <code>String</code> with which the needed value is associated.\n\t *\n\t * @return a <code>String</code> value\n\t *\n\t * @throws UserNotFoundException if user id hasn't been found in repository.\n\t * @throws TigaseDBException if database backend error occurs.\n\t */\n\tString getData(BareJID user, String key) throws UserNotFoundException, TigaseDBException;\n\n\t/**\n\t * <code>getDataMap</code> method returns a values associated with each key for user repository in given subnode.\n\t *\n\t * @param user a <code>BareJID</code> value of user ID for which data must be stored. User ID consists of user name\n\t * and domain name.\n\t * @param subnode a <code>String</code> value is a node path where data is stored. Node path has the same form as\n\t * directory path on file system:\n\t * <pre>/root/subnode1/subnode2</pre>.\n\t *\n\t * @return a <code>Map</code> with values\n\t *\n\t * @throws UserNotFoundException if user id hasn't been found in repository.\n\t * @throws TigaseDBException if database backend error occurs.\n\t */\n\tdefault Map<String, String> getDataMap(BareJID user, String subnode) throws UserNotFoundException, TigaseDBException {\n\t\tString[] keys = getKeys(user, subnode);\n\t\tif (keys != null) {\n\t\t\tMap<String, String> data = new HashMap<>();\n\t\t\tfor (String key : keys) {\n\t\t\t\tdata.put(key, getData(user, subnode, key));\n\t\t\t}\n\t\t\treturn data;\n\t\t}\n\t\treturn Collections.emptyMap();\n\t}\n\n\t/**\n\t * @param key key for which return of map of users and values corresponding to the value\n\t *\n\t * @return a <code>Map</code> with user JID as key and value corresponding to the key passed as parameter\n\t *\n\t * @throws UserNotFoundException if user id hasn't been found in repository.\n\t * @throws TigaseDBException if database backend error occurs.\n\t */\n\tdefault Map<BareJID, String> getDataMap(@NonNull String key) throws UserNotFoundException, TigaseDBException {\n\t\tObjects.requireNonNull(key);\n\t\tMap<BareJID, String> result = new HashMap<>();\n\t\tfor (BareJID user : getUsers()) {\n\t\t\tvar userDataForKey = getData(user, key);\n\t\t\tif (userDataForKey != null) {\n\t\t\t\tresult.put(user, userDataForKey);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * <code>getDataMap</code> method returns a values associated with each key for user repository in given subnode.\n\t *\n\t * @param user a <code>BareJID</code> value of user ID for which data must be stored. User ID consists of user name\n\t * and domain name.\n\t * @param subnode a <code>String</code> value is a node path where data is stored. Node path has the same form as\n\t * directory path on file system:\n\t * <pre>/root/subnode1/subnode2</pre>.\n\t * @param converter a <code>Function</code> which takes value for a key and converts to expected value type\n\t *\n\t * @return a <code>Map</code> with values\n\t *\n\t * @throws UserNotFoundException if user id hasn't been found in repository.\n\t * @throws TigaseDBException if database backend error occurs.\n\t */\n\tdefault <T> Map<String, T> getDataMap(BareJID user, String subnode, Function<String,T> converter) throws UserNotFoundException, TigaseDBException {\n\t\tString[] keys = getKeys(user, subnode);\n\t\tif (keys != null) {\n\t\t\tMap<String, T> data = new HashMap<>();\n\t\t\tfor (String key : keys) {\n\t\t\t\tString value = getData(user, subnode, key);\n\t\t\t\tif (value != null) {\n\t\t\t\t\tdata.put(key, converter.apply(value));\n\t\t\t\t} else {\n\t\t\t\t\tdata.put(key, null);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn data;\n\t\t}\n\t\treturn Collections.emptyMap();\n\t}\n\n\t/**\n\t * <code>getDataList</code> method returns array of values associated with given key or <code>null</code> if given\n\t * key does not exist for given user ID in given node path.\n\t *\n\t * @param user a <code>BareJID</code> value of user ID for which data must be stored. User ID consists of user name\n\t * and domain name.\n\t * @param subnode a <code>String</code> value is a node path where data is stored. Node path has the same form as\n\t * directory path on file system:\n\t * <pre>/root/subnode1/subnode2</pre>.\n\t * @param key a <code>String</code> with which the needed values list is associated.\n\t *\n\t * @return a <code>String[]</code> value\n\t *\n\t * @throws UserNotFoundException if user id hasn't been found in repository.\n\t * @throws TigaseDBException if database backend error occurs.\n\t */\n\tString[] getDataList(BareJID user, String subnode, String key) throws UserNotFoundException, TigaseDBException;\n\n\t/**\n\t * <code>getKeys</code> method returns list of all keys stored in given subnode in user repository. There is a value\n\t * (or list of values) associated with each key. It is up to user (developer) to know what key keeps one value and\n\t * what key keeps list of values.\n\t *\n\t * @param user a <code>BareJID</code> value of user ID for which data must be stored. User ID consists of user name\n\t * and domain name.\n\t * @param subnode a <code>String</code> value is a node path where data is stored. Node path has the same form as\n\t * directory path on file system:\n\t * <pre>/root/subnode1/subnode2</pre>.\n\t *\n\t * @return a <code>String[]</code> value\n\t *\n\t * @throws UserNotFoundException if user id hasn't been found in repository.\n\t * @throws TigaseDBException if database backend error occurs.\n\t */\n\tString[] getKeys(BareJID user, String subnode) throws UserNotFoundException, TigaseDBException;\n\n\t/**\n\t * <code>getKeys</code> method returns list of all keys stored in default user repository node. There is some a\n\t * value (or list of values) associated with each key. It is up to user (developer) to know what key keeps one value\n\t * and what key keeps list of values.\n\t *\n\t * @param user a <code>BareJID</code> value of user ID for which data must be stored or retrieved. User ID consists\n\t * of user name and domain name.\n\t *\n\t * @return a <code>String[]</code> value\n\t *\n\t * @throws UserNotFoundException if user id hasn't been found in repository.\n\t * @throws TigaseDBException if database backend error occurs.\n\t */\n\tString[] getKeys(BareJID user) throws UserNotFoundException, TigaseDBException;\n\n\t/**\n\t * Returns a DB connection string or DB connection URI.\n\t *\n\t * @return a <code>String</code> value representing database connection string.\n\t */\n\tString getResourceUri();\n\n\t/**\n\t * <code>getSubnodes</code> method returns list of all direct subnodes from given node.\n\t *\n\t * @param user a <code>BareJID</code> value of user ID for which data must be stored. User ID consists of user name\n\t * and domain name.\n\t * @param subnode a <code>String</code> value is a node path where data is stored. Node path has the same form as\n\t * directory path on file system:\n\t * <pre>/root/subnode1/subnode2</pre>.\n\t *\n\t * @return a <code>String[]</code> value is an array of all direct subnodes.\n\t *\n\t * @throws UserNotFoundException if user id hasn't been found in repository.\n\t * @throws TigaseDBException if database backend error occurs.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"Support for multi-level nodes will be removed\")\n\tString[] getSubnodes(BareJID user, String subnode) throws UserNotFoundException, TigaseDBException;\n\n\t/**\n\t * <code>getSubnodes</code> method returns list of all <em>root</em> nodes for given user.\n\t *\n\t * @param user a <code>BareJID</code> value of user ID for which data must be stored. User ID consists of user name\n\t * and domain name.\n\t *\n\t * @return a <code>String[]</code> value is an array of all <em>root</em> nodes for given user.\n\t *\n\t * @throws UserNotFoundException if user id hasn't been found in repository.\n\t * @throws TigaseDBException if database backend error occurs.\n\t */\n\tString[] getSubnodes(BareJID user) throws UserNotFoundException, TigaseDBException;\n\n\t/**\n\t * Returns a user unique ID number within the given repository. Please note it is also possible that the ID number\n\t * is unique only for the user domain. The ID is a positive number if the user exists and negative if the user was\n\t * not found in the repository.\n\t *\n\t * @param user a <code>BareJID</code> value of user ID for which data must be stored or retrieved. User ID consists\n\t * of user name and domain name.\n\t *\n\t * @return a user inique ID number within the repository or domain. The ID is a positive number if the user exists\n\t * and negative if the user was not found in the repository.\n\t *\n\t * @throws TigaseDBException if there is a problem with accessing user repository.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tlong getUserUID(BareJID user) throws TigaseDBException;\n\n\t/**\n\t * This method is only used by the data conversion tools. They attempt to copy whole user repositories from one to\n\t * another database. Databases might not be compatible but as long as the API is implemented and they support adding\n\t * user the user database can be copied to a different data source.\n\t *\n\t * @return returns a collection of all user IDs (Jabber IDs) stored in the user repository.\n\t *\n\t */\n\tList<BareJID> getUsers() throws TigaseDBException;\n\n\t/**\n\t * This method is only used by the server statistics component to report number of registered users.\n\t *\n\t * @return a <code>long</code> number of registered users in the repository.\n\t */\n\tlong getUsersCount();\n\n\t/**\n\t * This method is only used by the server statistics component to report number of registered users for given\n\t * domain.\n\t * @return a <code>long</code> number of registered users in the repository.\n\t */\n\tlong getUsersCount(String domain);\n\n\t/**\n\t * <code>removeData</code> method removes pair (key, value) from user repository in given subnode. If the key exists\n\t * in user repository there is always a value associated with this key - even empty <code>String</code>. If key does\n\t * not exist the <code>null</code> value is returned from repository backend or given default value.\n\t *\n\t * @param user a <code>BareJID</code> value of user ID for which data must be stored. User ID consists of user name\n\t * and domain name.\n\t * @param subnode a <code>String</code> value is a node path where data is stored. Node path has the same form as\n\t * directory path on file system:\n\t * <pre>/root/subnode1/subnode2</pre>.\n\t * @param key a <code>String</code> for which the value is to be removed.\n\t *\n\t * @throws UserNotFoundException if user id hasn't been found in repository.\n\t * @throws TigaseDBException if database backend error occurs.\n\t */\n\tvoid removeData(BareJID user, String subnode, String key) throws UserNotFoundException, TigaseDBException;\n\n\t/**\n\t * <code>removeData</code> method removes pair (key, value) from user repository in default repository node. If the\n\t * key exists in user repository there is always a value associated with this key - even empty <code>String</code>.\n\t * If key does not exist the <code>null</code> value is returned from repository backend or given default value.\n\t *\n\t * @param user a <code>BareJID</code> value of user ID for which data must be stored. User ID consists of user name\n\t * and domain name.\n\t * @param key a <code>String</code> for which the value is to be removed.\n\t *\n\t * @throws UserNotFoundException if user id hasn't been found in repository.\n\t * @throws TigaseDBException if database backend error occurs.\n\t */\n\tvoid removeData(BareJID user, String key) throws UserNotFoundException, TigaseDBException;\n\n\t/**\n\t * <code>removeSubnode</code> method removes given subnode with all subnodes in this node and all data stored in\n\t * this node and in all subnodes. Effectively it removes entire repository tree starting from given node.\n\t *\n\t * @param user a <code>BareJID</code> value of user ID for which data must be stored. User ID consists of user name\n\t * and domain name.\n\t * @param subnode a <code>String</code> value is a node path to subnode which has to be removed. Node path has the\n\t * same form as directory path on file\n\t * system: <pre>/root/subnode1/subnode2</pre>.\n\t *\n\t * @throws UserNotFoundException if user id hasn't been found in repository.\n\t * @throws TigaseDBException if database backend error occurs.\n\t */\n\tvoid removeSubnode(BareJID user, String subnode) throws UserNotFoundException, TigaseDBException;\n\n\t/**\n\t * This <code>removeUser</code> method allows to remove user and all his data from user repository. If given user id\n\t * does not exist <code>UserNotFoundException</code> must be thrown. As one <em>XMPP</em> server can support many\n\t * virtual internet domains it is required that <code>user</code> id consists of user name and domain address:\n\t * <em>username@domain.address.net</em> for example.\n\t *\n\t * @param user a <code>BareJID</code> value of user id consisting of user name and domain address.\n\t *\n\t * @throws UserNotFoundException if user id hasn't been found in repository.\n\t * @throws TigaseDBException if database backend error occurs.\n\t */\n\tvoid removeUser(BareJID user) throws UserNotFoundException, TigaseDBException;\n\n\t/**\n\t * <code>setData</code> method sets data value for given user ID in repository under given node path and associates\n\t * it with given key. If there already exists value for given key in given node, old value is replaced with new\n\t * value. No warning or exception is thrown in case if methods overwrites old value.\n\t *\n\t * @param user a <code>BareJID</code> value of user ID for which data must be stored. User ID consists of user name\n\t * and domain name.\n\t * @param subnode a <code>String</code> value is a node path where data is stored. Node path has the same form as\n\t * directory path on file system:\n\t * <pre>/root/subnode1/subnode2</pre>.\n\t * @param key a <code>String</code> with which the specified value is to be associated.\n\t * @param value a <code>String</code> value to be associated with the specified key.\n\t *\n\t * @throws UserNotFoundException if user id hasn't been found in repository.\n\t * @throws TigaseDBException if database backend error occurs.\n\t */\n\tvoid setData(BareJID user, String subnode, String key, String value)\n\t\t\tthrows UserNotFoundException, TigaseDBException;\n\n\t/**\n\t * This <code>setData</code> method sets data value for given user ID associated with given key in default\n\t * repository node. Default node is dependent on implementation and usually it is root user node. If there already\n\t * exists value for given key in given node, old value is replaced with new value. No warning or exception is thrown\n\t * in case if methods overwrites old value.\n\t *\n\t * @param user a <code>BareJID</code> value of user ID for which data must be stored. User ID consists of user name\n\t * and domain name.\n\t * @param key a <code>String</code> with which the specified value is to be associated.\n\t * @param value a <code>String</code> value to be associated with the specified key.\n\t *\n\t * @throws UserNotFoundException if user id hasn't been found in repository.\n\t * @throws TigaseDBException if database backend error occurs.\n\t */\n\tvoid setData(BareJID user, String key, String value) throws UserNotFoundException, TigaseDBException;\n\n\t/**\n\t * <code>setDataList</code> method sets list of values for given user associated given key in repository under given\n\t * node path. If there already exist values for given key in given node, all old values are replaced with new\n\t * values. No warning or exception is thrown in case if methods overwrites old value.\n\t *\n\t * @param user a <code>BareJID</code> value of user ID for which data must be stored. User ID consists of user name\n\t * and domain name.\n\t * @param subnode a <code>String</code> value is a node path where data is stored. Node path has the same form as\n\t * directory path on file system:\n\t * <pre>/root/subnode1/subnode2</pre>.\n\t * @param key a <code>String</code> with which the specified values list is to be associated.\n\t * @param list a <code>String[]</code> is an array of values to be associated with the specified key.\n\t *\n\t * @throws UserNotFoundException if user id hasn't been found in repository.\n\t * @throws TigaseDBException if database backend error occurs.\n\t */\n\tvoid setDataList(BareJID user, String subnode, String key, String[] list)\n\t\t\tthrows UserNotFoundException, TigaseDBException;\n\n\t/**\n\t * Method <code>userExists</code> checks whether the user (or repository top node) exists in the database. The\n\t * method doesn't throw any exception nor it creates the user in case it is missing. It just checks whether the user\n\t * is already in the database.\n\t * <br>\n\t * Please don't overuse this method. All other methods throw <code>UserNotFoundException</code> exception in case\n\t * the user is missing for which you executed the method. The exception is thrown unless <code>userAutoCreate</code>\n\t * property is set to true. In such case the exception is never thrown and the methods are executed for given\n\t * parameters prior to creating user entry if it is missing.\n\t * <br>\n\t * Therefore this method should be used only to check whether the account exists without creating it.\n\t *\n\t * @param user a <code>BareJID</code> value\n\t *\n\t * @return a <code>boolean</code> value\n\t */\n\tboolean userExists(BareJID user);\n\n\tpublic static class UserAddedEvent implements EventBusEvent {\n\t\t\n\t\tprivate final BareJID jid;\n\n\t\tpublic UserAddedEvent(BareJID jid) {\n\t\t\tthis.jid = jid;\n\t\t}\n\n\t\tpublic BareJID getJid() {\n\t\t\treturn jid;\n\t\t}\n\t}\n\n\tpublic static class UserRemovedEvent implements EventBusEvent {\n\n\t\tpublic final BareJID jid;\n\n\t\tpublic UserRemovedEvent(BareJID jid) {\n\t\t\tthis.jid = jid;\n\t\t}\n\n\t\tpublic BareJID getJid() {\n\t\t\treturn jid;\n\t\t}\n\t}\n\n\tpublic static class UserBeforeRemovedEvent implements EventBusEvent {\n\n\t\tpublic final BareJID jid;\n\n\t\tpublic UserBeforeRemovedEvent(BareJID jid) {\n\t\t\tthis.jid = jid;\n\t\t}\n\n\t\tpublic BareJID getJid() {\n\t\t\treturn jid;\n\t\t}\n\t}\n}    // UserRepository\n"
  },
  {
    "path": "src/main/java/tigase/db/UserRepositoryMDImpl.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport org.jspecify.annotations.NonNull;\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.beans.MDPoolBeanWithStatistics;\nimport tigase.db.beans.UserRepositoryMDPoolBean;\nimport tigase.eventbus.EventBus;\nimport tigase.kernel.beans.Inject;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\n/**\n * Created: Mar 27, 2010 6:43:02 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic abstract class UserRepositoryMDImpl\n\t\textends MDPoolBeanWithStatistics<UserRepository, UserRepositoryMDPoolBean.UserRepositoryConfigBean>\n\t\timplements UserRepository {\n\n\tprivate static final Logger log = Logger.getLogger(UserRepositoryMDImpl.class.getName());\n\n\t@Inject\n\tprivate EventBus eventBus;\n\n\tpublic UserRepositoryMDImpl() {\n\t\tsuper(UserRepository.class);\n\t}\n\n\t@Override\n\tpublic void addDataList(BareJID user, String subnode, String key, String[] list)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\trepo.addDataList(user, subnode, key, list);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void addUser(BareJID user) throws UserExistsException, TigaseDBException {\n\t\tUserRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\trepo.addUser(user);\n\t\t\teventBus.fire(new UserAddedEvent(user));\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getData(BareJID user, String subnode, String key, String def)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\treturn repo.getData(user, subnode, key, def);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getData(BareJID user, String subnode, String key) throws UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\treturn repo.getData(user, subnode, key);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getData(BareJID user, String key) throws UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\treturn repo.getData(user, key);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Map<String, String> getDataMap(BareJID user, String subnode)\n\t\t\tthrows TigaseDBException {\n\t\tUserRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\treturn repo.getDataMap(user, subnode);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Map<BareJID, String> getDataMap(@NonNull String key) throws UserNotFoundException, TigaseDBException {\n\t\ttry {\n\t\t\treturn repositoriesStream().sequential().flatMap(userRepository -> {\n\t\t\t\ttry {\n\t\t\t\t\treturn userRepository.getDataMap(key).entrySet().stream();\n\t\t\t\t} catch (TigaseDBException e) {\n\t\t\t\t\tthrow new RuntimeException(e);\n\t\t\t\t}\n\t\t\t}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));\n\t\t} catch (RuntimeException ex) {\n\t\t\tThrowable cause = ex.getCause();\n\t\t\tif (cause instanceof TigaseDBException) {\n\t\t\t\tthrow new TigaseDBException(\"Could not retrieve list of users\", cause);\n\t\t\t}\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\t@Override\n\tpublic <T> Map<String, T> getDataMap(BareJID user, String subnode, Function<String, T> converter)\n\t\t\tthrows TigaseDBException {\n\t\tUserRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\treturn repo.getDataMap(user, subnode, converter);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String[] getDataList(BareJID user, String subnode, String key)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\treturn repo.getDataList(user, subnode, key);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String[] getKeys(BareJID user, String subnode) throws UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\treturn repo.getKeys(user, subnode);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String[] getKeys(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\treturn repo.getKeys(user);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getResourceUri() {\n\t\treturn getDefaultRepository().getResourceUri();\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"Support for multi-level nodes will be removed\")\n\tpublic String[] getSubnodes(BareJID user, String subnode) throws UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\treturn repo.getSubnodes(user, subnode);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String[] getSubnodes(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\treturn repo.getSubnodes(user);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic long getUserUID(BareJID user) throws TigaseDBException {\n\t\tUserRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\treturn repo.getUserUID(user);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\n\t\treturn -1;\n\t}\n\n\t@Override\n\tpublic List<BareJID> getUsers() throws TigaseDBException {\n\t\ttry {\n\t\t\treturn repositoriesStream().sequential().flatMap(userRepository -> {\n\t\t\t\ttry {\n\t\t\t\t\treturn userRepository.getUsers().stream();\n\t\t\t\t} catch (TigaseDBException e) {\n\t\t\t\t\tthrow new RuntimeException(e);\n\t\t\t\t}\n\t\t\t}).collect(Collectors.toList());\n\t\t} catch (RuntimeException ex) {\n\t\t\tThrowable cause = ex.getCause();\n\t\t\tif (cause instanceof TigaseDBException) {\n\t\t\t\tthrow new TigaseDBException(\"Could not retrieve list of users\", cause);\n\t\t\t}\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\t@Override\n\tpublic long getUsersCount() {\n\t\treturn repositoriesStream().mapToLong(UserRepository::getUsersCount).sum();\n\t}\n\n\t@Override\n\tpublic long getUsersCount(String domain) {\n\t\tUserRepository repo = getRepo(domain);\n\n\t\tif (repo != null) {\n\t\t\treturn repo.getUsersCount(domain);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"Couldn't obtain user repository for domain: \" + domain + \", not even default one!\");\n\t\t}\n\n\t\treturn -1;\n\t}\n\n\t@Override\n\t@Deprecated\n\tpublic void initRepository(String resource_uri, Map<String, String> params) throws DBInitException {\n\t\tlog.log(Level.CONFIG, \"Multi-domain repository pool initialized: \" + resource_uri + \", params: \" + params);\n\t}\n\n\t@Override\n\tpublic void removeData(BareJID user, String subnode, String key) throws UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\trepo.removeData(user, subnode, key);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void removeData(BareJID user, String key) throws UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\trepo.removeData(user, key);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void removeSubnode(BareJID user, String subnode) throws UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\trepo.removeSubnode(user, subnode);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void removeUser(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\teventBus.fire(new UserRepository.UserBeforeRemovedEvent(user));\n\t\t\trepo.removeUser(user);\n\n\t\t\teventBus.fire(new UserRemovedEvent(user));\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setData(BareJID user, String subnode, String key, String value)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\trepo.setData(user, subnode, key, value);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setData(BareJID user, String key, String value) throws UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\trepo.setData(user, key, value);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setDataList(BareJID user, String subnode, String key, String[] list)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\trepo.setDataList(user, subnode, key, list);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean userExists(BareJID user) {\n\t\tUserRepository repo = getRepo(user.getDomain());\n\n\t\tif (repo != null) {\n\t\t\treturn repo.userExists(user);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Couldn't obtain user repository for domain: \" + user.getDomain() + \", not even default one!\");\n\t\t}\n\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/UserRepositoryPool.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport org.jspecify.annotations.NonNull;\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.util.cache.SimpleCache;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.function.Function;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Pool for user repositories. * <br> This pool should be used if connection to user storage is blocking or synchronized,\n * ie. implemented using single connection.* <br> If implementation of <code>UserRepository</code> uses connection pool\n * or non blocking, concurrent access to user storage (ie. <code>DataSourcePool</code>), then this pool is not need.\n * <br>\n * Created: Jan 28, 2009 8:46:53 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class UserRepositoryPool\n\t\timplements UserRepository, RepositoryPool<UserRepository> {\n\n\tprivate static final Logger log = Logger.getLogger(UserRepositoryPool.class.getName());\n\n\tprivate IRepoCache<String, Object> cache = null;\n\tprivate LinkedBlockingQueue<UserRepository> repoPool = new LinkedBlockingQueue<UserRepository>();\n\n\t@Override\n\tpublic void addDataList(BareJID user, String subnode, String key, String[] list)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\trepo.addDataList(user, subnode, key, list);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\t}\n\n\tpublic void addRepo(UserRepository repo) {\n\t\trepoPool.offer(repo);\n\t}\n\n\t@Override\n\tpublic void addUser(BareJID user) throws UserExistsException, TigaseDBException {\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\trepo.addUser(user);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getData(BareJID user, String subnode, String key, String def)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\tString data = (String) cache.get(user + \"/\" + subnode + \"/\" + key);\n\n\t\tif (data != null) {\n\t\t\treturn data;\n\t\t}\n\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.getData(user, subnode, key, def);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getData(BareJID user, String subnode, String key) throws UserNotFoundException, TigaseDBException {\n\t\tString data = (String) cache.get(user + \"/\" + subnode + \"/\" + key);\n\n\t\tif (data != null) {\n\t\t\treturn data;\n\t\t}\n\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.getData(user, subnode, key);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getData(BareJID user, String key) throws UserNotFoundException, TigaseDBException {\n\t\tString data = (String) cache.get(user + \"/\" + key);\n\n\t\tif (data != null) {\n\t\t\treturn data;\n\t\t}\n\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.getData(user, key);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Map<String, String> getDataMap(BareJID user, String subnode)\n\t\t\tthrows TigaseDBException {\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.getDataMap(user, subnode);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Map<BareJID, String> getDataMap(@NonNull String key) throws UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.getDataMap(key);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic <T> Map<String, T> getDataMap(BareJID user, String subnode, Function<String, T> converter)\n\t\t\tthrows TigaseDBException {\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.getDataMap(user, subnode, converter);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String[] getDataList(BareJID user, String subnode, String key)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.getDataList(user, subnode, key);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String[] getKeys(BareJID user, String subnode) throws UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.getKeys(user, subnode);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String[] getKeys(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.getKeys(user);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getResourceUri() {\n\t\treturn null;\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"Support for multi-level nodes will be removed\")\n\tpublic String[] getSubnodes(BareJID user, String subnode) throws UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.getSubnodes(user, subnode);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String[] getSubnodes(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.getSubnodes(user);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic long getUserUID(BareJID user) throws TigaseDBException {\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.getUserUID(user);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\n\t\treturn -1;\n\t}\n\n\t@Override\n\tpublic List<BareJID> getUsers() throws TigaseDBException {\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.getUsers();\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic long getUsersCount() {\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.getUsersCount();\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic long getUsersCount(String domain) {\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.getUsersCount(domain);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void initRepository(String resource_uri, Map<String, String> params) throws DBInitException {\n\t\tif (resource_uri.contains(\"cacheRepo=off\")) {\n\t\t\tlog.fine(\"Disabling cache.\");\n\t\t\tcache = new RepoNoCache();\n\t\t} else {\n\t\t\tcache = new RepoCache(10000, 60 * 1000);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void removeData(BareJID user, String subnode, String key) throws UserNotFoundException, TigaseDBException {\n\t\tcache.remove(user + \"/\" + subnode + \"/\" + key);\n\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\trepo.removeData(user, subnode, key);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void removeData(BareJID user, String key) throws UserNotFoundException, TigaseDBException {\n\t\tcache.remove(user + \"/\" + key);\n\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\trepo.removeData(user, key);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void removeSubnode(BareJID user, String subnode) throws UserNotFoundException, TigaseDBException {\n\t\tcache.remove(user + \"/\" + subnode);\n\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\trepo.removeSubnode(user, subnode);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void removeUser(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\trepo.removeUser(user);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setData(BareJID user, String subnode, String key, String value)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\trepo.setData(user, subnode, key, value);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\n\t\tcache.put(user + \"/\" + subnode + \"/\" + key, value);\n\t}\n\n\t@Override\n\tpublic void setData(BareJID user, String key, String value) throws UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\trepo.setData(user, key, value);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\n\t\tcache.put(user + \"/\" + key, value);\n\t}\n\n\t@Override\n\tpublic void setDataList(BareJID user, String subnode, String key, String[] list)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\trepo.setDataList(user, subnode, key, list);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\t}\n\n\tpublic UserRepository takeRepo() {\n\t\ttry {\n\t\t\treturn repoPool.take();\n\t\t} catch (InterruptedException ex) {\n\t\t\tlog.log(Level.WARNING, \"Couldn't obtain user repository from the pool\", ex);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean userExists(BareJID user) {\n\t\tUserRepository repo = takeRepo();\n\n\t\tif (repo != null) {\n\t\t\ttry {\n\t\t\t\treturn repo.userExists(user);\n\t\t\t} finally {\n\t\t\t\taddRepo(repo);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"repo is NULL, pool empty? - {0}\", repoPool.size());\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tpublic interface IRepoCache<K,V> {\n\n\t\tV get(Object key);\n\n\t\tV put(K key, V value);\n\n\t\tV remove(Object key);\n\t}\n\n\tprivate class RepoNoCache implements IRepoCache<String, Object> {\n\n\t\t@Override\n\t\tpublic Object get(Object key) {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object put(String key, Object value) {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object remove(Object key) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate class RepoCache\n\t\t\textends SimpleCache<String, Object> implements IRepoCache<String, Object> {\n\n\t\tpublic RepoCache(int maxsize, long cache_time) {\n\t\t\tsuper(maxsize, cache_time);\n\t\t}\n\n\t\t@Override\n\t\tpublic Object get(Object key) {\n\t\t\tif (cache_off) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tsynchronized (this) {\n\t\t\t\treturn super.get(key);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic Object put(String key, Object value) {\n\t\t\tif (cache_off) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tsynchronized (this) {\n\t\t\t\treturn super.put(key, value);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic Object remove(Object key) {\n\t\t\tif (cache_off) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tsynchronized (this) {\n\t\t\t\tObject val = super.remove(key);\n\t\t\t\tString strk = key.toString();\n\t\t\t\tIterator<String> ks = keySet().iterator();\n\n\t\t\t\twhile (ks.hasNext()) {\n\t\t\t\t\tString k = ks.next().toString();\n\n\t\t\t\t\tif (k.startsWith(strk)) {\n\t\t\t\t\t\tks.remove();\n\t\t\t\t\t}    // end of if (k.startsWith(strk))\n\t\t\t\t}      // end of while (ks.hasNext())\n\n\t\t\t\treturn val;\n\t\t\t}\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/db/beans/AuthRepositoryMDPoolBean.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.beans;\n\nimport tigase.auth.CredentialsDecoderBean;\nimport tigase.auth.CredentialsEncoderBean;\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.db.AuthRepository;\nimport tigase.db.AuthRepositoryMDImpl;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.BasicComponent;\nimport tigase.stats.StatisticsCollector;\nimport tigase.stats.StatisticsList;\n\n/**\n * Class implements bean for multi domain pool for authentication repositories.\n * <br>\n * Created by andrzej on 08.03.2016.\n */\n@Bean(name = \"authRepository\", parent = Kernel.class, exportable = true, active = true)\n@ConfigType({ConfigTypeEnum.DefaultMode, ConfigTypeEnum.SessionManagerMode, ConfigTypeEnum.ConnectionManagersMode,\n\t\t\t ConfigTypeEnum.ComponentMode})\npublic class AuthRepositoryMDPoolBean\n\t\textends AuthRepositoryMDImpl {\n\n\t@Override\n\tpublic boolean belongsTo(Class<? extends BasicComponent> component) {\n\t\treturn StatisticsCollector.class.isAssignableFrom(component);\n\t}\n\n\t@Override\n\tpublic void getStatistics(String compName, StatisticsList list) {\n\t\tsuper.getStatistics(getName(), list);\n\t}\n\n\t@Override\n\tpublic Class<? extends AuthRepositoryConfigBean> getConfigClass() {\n\t\treturn AuthRepositoryConfigBean.class;\n\t}\n\n\t@Override\n\tpublic Class<?> getDefaultBeanClass() {\n\t\treturn AuthRepositoryConfigBean.class;\n\t}\n\n\tpublic static class AuthRepositoryConfigBean\n\t\t\textends AuthUserRepositoryConfigBean<AuthRepository, AuthRepositoryConfigBean> {\n\n\t\t@Inject\n\t\tprivate CredentialsDecoderBean credentialsDecoderBean;\n\t\t@Inject\n\t\tprivate CredentialsEncoderBean credentialsEncoderBean;\n\n\t\t@Override\n\t\tprotected Class<AuthRepository> getRepositoryIfc() {\n\t\t\treturn AuthRepository.class;\n\t\t}\n\n\t\t@Override\n\t\tprotected String getRepositoryPoolClassName() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tprotected void initRepository(AuthRepository repository) throws RepositoryException {\n\t\t\tsuper.initRepository(repository);\n\t\t\trepository.setCredentialsCodecs(credentialsEncoderBean, credentialsDecoderBean);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/beans/AuthUserRepositoryConfigBean.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.beans;\n\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.db.*;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.HandleEvent;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.beans.config.ConfigurationChangedAware;\n\nimport java.lang.reflect.Method;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Configuration bean for authentication repository per domain Created by andrzej on 09.03.2016.\n */\npublic abstract class AuthUserRepositoryConfigBean<T, U extends AuthUserRepositoryConfigBean<T, U>>\n\t\textends MDPoolConfigBean<T, U>\n\t\timplements ConfigurationChangedAware, Initializable, UnregisterAware {\n\n\tprivate static final Logger log = Logger.getLogger(AuthUserRepositoryConfigBean.class.getCanonicalName());\n\tprivate DataSource dataSource;\n\t@Inject\n\tprivate DataSourceBean dataSourceBean;\n\n\t@ConfigField(desc = \"Name of data source to use\", alias = \"data-source\")\n\tprivate String dataSourceName;\n\t@Inject\n\tprivate EventBus eventBus;\n\tprivate String repositoryUri;\n\n\t@Override\n\tpublic void beanConfigurationChanged(Collection<String> changedFields) {\n\t\tif (dataSourceBean != null) {\n\t\t\tif (uri == null) {\n\t\t\t\trepositoryUri = dataSourceName != null ? dataSourceName : name;\n\t\t\t\tdataSource = dataSourceBean.getRepository(repositoryUri);\n\t\t\t\tif (dataSource != null) {\n\t\t\t\t\trepositoryUri = dataSource.getResourceUri();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\trepositoryUri = uri;\n\t\t\t}\n\t\t}\n\t\tsuper.beanConfigurationChanged(changedFields);\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\teventBus.registerAll(this);\n\t\tsuper.initialize();\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\teventBus.unregisterAll(this);\n\t}\n\n\t@Override\n\tprotected String getUri() {\n\t\treturn repositoryUri;\n\t}\n\n\t/**\n\t * Returns class name of a repository to initialize for domain\n\t */\n\t@Override\n\tprotected String getRepositoryClassName() throws DBInitException {\n\t\tif (cls != null) {\n\t\t\treturn cls;\n\t\t}\n\t\treturn RepositoryFactory.getRepoClassName(getRepositoryIfc(), repositoryUri);\n\t}\n\n\t@Override\n\tprotected void initRepository(T repository) throws RepositoryException {\n\t\tboolean initialized = false;\n\t\tif (repository instanceof Repository) {\n\t\t\ttry {\n\t\t\t\tMethod m = repository.getClass().getDeclaredMethod(\"initRepository\", String.class, Map.class);\n\t\t\t\tif (m.getAnnotation(Deprecated.class) == null) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Class {0} is using deprecated initialization using method initRepository()\",\n\t\t\t\t\t\t\trepository.getClass().getCanonicalName());\n\t\t\t\t\t((Repository) repository).initRepository(getUri(), new HashMap<>());\n\t\t\t\t\tinitialized = true;\n\t\t\t\t}\n\t\t\t} catch (NoSuchMethodException | SecurityException ex) {\n\t\t\t\t// ignoring exception\n\t\t\t}\n\t\t}\n\t\tif (!initialized && repository instanceof DataSourceAware) {\n\t\t\tfinal DataSourceAware dsAwareRepository = (DataSourceAware) repository;\n\t\t\tdataSource.checkSchemaVersion(dsAwareRepository, true);\n\t\t\tdsAwareRepository.setDataSource(dataSource);\n\t\t}\n\t}\n\n\t/**\n\t * Returns class name of repository pool for domain\n\t */\n\t@Override\n\tprotected String getRepositoryPoolClassName() {\n\t\treturn null;\n\t}\n\n\t@HandleEvent\n\tprotected void onDataSourceChange(DataSourceBean.DataSourceChangedEvent event) {\n\t\tif (!event.isCorrectSender(dataSourceBean)) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (uri != null || (!event.getDomain().equals(name) && !event.getDomain().equals(dataSourceName))) {\n\t\t\treturn;\n\t\t}\n\n\t\tbeanConfigurationChanged(Collections.singleton(\"uri\"));\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/beans/DataSourceBean.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.beans;\n\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.db.DBInitException;\nimport tigase.db.DataSource;\nimport tigase.db.DataSourceHelper;\nimport tigase.db.DataSourcePool;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.EventBusEvent;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.BasicComponent;\nimport tigase.stats.ComponentStatisticsProvider;\nimport tigase.stats.StatisticsCollector;\nimport tigase.stats.StatisticsList;\nimport tigase.stats.StatisticsProviderIfc;\n\nimport java.time.Duration;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.db.beans.DataSourceBean.DataSourceMDConfigBean;\n\n/**\n * This is main bean responsible for managing and initialization of data sources. Created by andrzej on 09.03.2016.\n */\n@Bean(name = \"dataSource\", parent = Kernel.class, active = true, exportable = true)\n@ConfigType({ConfigTypeEnum.DefaultMode, ConfigTypeEnum.SessionManagerMode, ConfigTypeEnum.ConnectionManagersMode,\n\t\t\t ConfigTypeEnum.ComponentMode})\npublic class DataSourceBean\n\t\textends MDPoolBean<DataSource, DataSourceMDConfigBean>\n\t\timplements ComponentStatisticsProvider {\n\n\tprivate static final Logger log = Logger.getLogger(DataSourceBean.class.getCanonicalName());\n\n\tprivate final Map<String, DataSource> repositories = new ConcurrentHashMap<>();\n\t@Inject\n\tprivate EventBus eventBus;\n\tprivate ScheduledExecutorService executorService = null;\n\tprivate int watchdogs = 0;\n\n\t/**\n\t * Retrieves data source for provided name\n\t *\n\t * @param name of data source to retrieve\n\t *\n\t * @return instance of data source for name or default instance of data source\n\t */\n\tpublic DataSource getRepository(String name) {\n\t\tif (name == null) {\n\t\t\treturn repositories.get(\"default\");\n\t\t}\n\t\treturn repositories.get(name);\n\t}\n\n\t@Override\n\tpublic Class<? extends DataSourceMDConfigBean> getConfigClass() {\n\t\treturn DataSourceMDConfigBean.class;\n\t}\n\n\t/**\n\t * Add data source instance to the pool\n\t *\n\t * @param domain name of data source\n\t * @param repo instance of data source\n\t */\n\t@Override\n\tpublic void addRepo(String domain, DataSource repo) {\n\t\tDataSource oldRepo = this.repositories.put(domain, repo);\n\t\tfire(new DataSourceChangedEvent(this, domain, repo, oldRepo));\n\t}\n\n\t/**\n\t * Remove data source from the pool\n\t *\n\t * @param domain name of data source\n\t *\n\t * @return removed instance of data source\n\t */\n\t@Override\n\tpublic DataSource removeRepo(String domain) {\n\t\tDataSource oldRepo = repositories.remove(domain);\n\t\tfire(new DataSourceChangedEvent(this, domain, null, oldRepo));\n\t\treturn oldRepo;\n\t}\n\n\t/**\n\t * Retrieve list of all available data source names\n\t *\n\t * @return list of names\n\t */\n\tpublic Set<String> getDataSourceNames() {\n\t\treturn Collections.unmodifiableSet(repositories.keySet());\n\t}\n\n\t@Override\n\tpublic void setDefault(DataSource repo) {\n\t\t// here we do nothing\n\t}\n\n\t@Override\n\tpublic boolean belongsTo(Class<? extends BasicComponent> component) {\n\t\treturn StatisticsCollector.class.isAssignableFrom(component);\n\t}\n\n\t@Override\n\tpublic void everyHour() {\n\n\t}\n\n\t@Override\n\tpublic void everyMinute() {\n\n\t}\n\n\t@Override\n\tpublic void everySecond() {\n\n\t}\n\n\t@Override\n\tpublic void getStatistics(String compName, StatisticsList list) {\n\t\tString name = getName();\n\t\tlist.add(name, \"Number of data sources\", repositories.size(), Level.FINE);\n\t\trepositories.entrySet()\n\t\t\t\t.stream()\n\t\t\t\t.filter(e -> e.getValue() instanceof StatisticsProviderIfc)\n\t\t\t\t.forEach(e -> ((StatisticsProviderIfc) e.getValue()).getStatistics(name + \"/\" + e.getKey(), list));\n\t}\n\n\t@Override\n\tpublic Class<?> getDefaultBeanClass() {\n\t\treturn DataSourceMDConfigBean.class;\n\t}\n\n\tprotected ScheduledFuture addWatchdogTask(Runnable task, Duration frequency) {\n\t\tsynchronized (this) {\n\t\t\tif (executorService == null) {\n\t\t\t\texecutorService = Executors.newSingleThreadScheduledExecutor();\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"created watchdog executor\");\n\t\t\t\t}\n\t\t\t}\n\t\t\twatchdogs++;\n\t\t\treturn executorService.scheduleAtFixedRate(task, frequency.toMillis(), frequency.toMillis(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t   TimeUnit.MILLISECONDS);\n\t\t}\n\t}\n\n\tprotected void removeWatchdogTask(ScheduledFuture scheduledFuture) {\n\t\tsynchronized (this) {\n\t\t\tscheduledFuture.cancel(true);\n\t\t\twatchdogs--;\n\t\t\tif (watchdogs == 0) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"destroying watchdog executor\");\n\t\t\t\t}\n\t\t\t\texecutorService.shutdown();\n\t\t\t\texecutorService = null;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void fire(Object event) {\n\t\tif (eventBus != null) {\n\t\t\teventBus.fire(event);\n\t\t}\n\t}\n\n\t/**\n\t * Event emitted by {@link tigase.db.beans.DataSourceBean} using {@link tigase.eventbus.EventBus} when instance of\n\t * {@link tigase.db.DataSource} for some name changes.\n\t */\n\tpublic static class DataSourceChangedEvent implements EventBusEvent {\n\n\t\tprivate final DataSourceBean bean;\n\t\tprivate final String domain;\n\t\tprivate final DataSource newDataSource;\n\t\tprivate final DataSource oldDataSource;\n\n\t\tpublic DataSourceChangedEvent(DataSourceBean bean, String domain, DataSource newDataSource,\n\t\t\t\t\t\t\t\t\t  DataSource oldDataSource) {\n\t\t\tthis.bean = bean;\n\t\t\tthis.domain = domain;\n\t\t\tthis.newDataSource = newDataSource;\n\t\t\tthis.oldDataSource = oldDataSource;\n\t\t}\n\n\t\t/**\n\t\t * Check if event was emitted by provided instance of <code>DataSourceBean</code>\n\t\t */\n\t\tpublic boolean isCorrectSender(DataSourceBean bean) {\n\t\t\treturn this.bean == bean;\n\t\t}\n\n\t\t/**\n\t\t * Get name for which data source instance was changed\n\t\t *\n\t\t */\n\t\tpublic String getDomain() {\n\t\t\treturn domain;\n\t\t}\n\n\t\t/**\n\t\t * Get old instance of data source\n\t\t *\n\t\t */\n\t\tpublic DataSource getOldDataSource() {\n\t\t\treturn oldDataSource;\n\t\t}\n\n\t\t/**\n\t\t * Get new instance of data source\n\t\t *\n\t\t */\n\t\tpublic DataSource getNewDataSource() {\n\t\t\treturn newDataSource;\n\t\t}\n\n\t}\n\n\tpublic static class DataSourceMDConfigBean\n\t\t\textends MDPoolConfigBean<DataSource, DataSourceMDConfigBean>\n\t\t\timplements UnregisterAware {\n\n\t\tprivate ScheduledFuture future = null;\n\n\t\t@ConfigField(desc = \"Watchdog data source frequency\", alias = \"watchdog-frequency\")\n\t\tprivate Duration watchdogFrequency = Duration.ofHours(1);\n\n\t\tpublic void setWatchdogFrequency(Duration watchdogFrequency) {\n\t\t\tthis.watchdogFrequency = watchdogFrequency;\n\t\t\tupdateWatchdogTask();\n\t\t}\n\n\t\t@Override\n\t\tpublic void initialize() {\n\t\t\tsuper.initialize();\n\t\t\tupdateWatchdogTask();\n\t\t}\n\n\t\t@Override\n\t\tpublic void beforeUnregister() {\n\t\t\tif (future != null) {\n\t\t\t\tif (mdPool != null) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"unregistering watchdog for data source {0}\", name);\n\t\t\t\t\t}\n\t\t\t\t\t((DataSourceBean) mdPool).removeWatchdogTask(future);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Get interface to which all instances in this pool must conform.\n\t\t *\n\t\t * @return interface    `\n\t\t */\n\t\t@Override\n\t\tprotected Class<? extends DataSource> getRepositoryIfc() {\n\t\t\treturn DataSource.class;\n\t\t}\n\n\t\t/**\n\t\t * Finds and retrieves repository pool class name for data source defined in this config bean. * <br> Name of a\n\t\t * pool class will be retrieved from <code>poolCls</code> field or looked for instances of\n\t\t * <code>DataSourcePool</code> class annotated with {@link tigase.db.Repository.Meta} which supported uri\n\t\t * matches (regexp) of data source URI.\n\t\t *\n\t\t * @return name of a class\n\t\t *\n\t\t */\n\t\t@Override\n\t\tprotected String getRepositoryPoolClassName() throws DBInitException {\n\t\t\tif (poolCls != null) {\n\t\t\t\treturn poolCls;\n\t\t\t}\n\n\t\t\tClass<? extends DataSourcePool> poolClass = null;\n\t\t\ttry {\n\t\t\t\tpoolClass = DataSourceHelper.getDefaultClass(DataSourcePool.class, uri);\n\t\t\t} catch (DBInitException ex) {\n\t\t\t\t// ok, no problem - it maybe a data source without a pool\n\t\t\t}\n\t\t\treturn poolClass == null ? null : poolClass.getCanonicalName();\n\t\t}\n\n\t\t/**\n\t\t * Initializes instances of provided data source.\n\t\t *\n\t\t * @param repo instance of data source\n\t\t *\n\t\t */\n\t\t@Override\n\t\tprotected void initRepository(DataSource repo) throws RepositoryException {\n\t\t\trepo.initialize(getUri());\n\t\t}\n\n\t\tprivate void executeWatchdog() {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"execution of watchdog for data source {0}\", name);\n\t\t\t}\n\t\t\tthis.getRepository().checkConnectivity(watchdogFrequency);\n\t\t}\n\n\t\tprivate void updateWatchdogTask() {\n\t\t\tif (mdPool != null) {\n\t\t\t\tDataSourceBean dataSourceBean = (DataSourceBean) mdPool;\n\t\t\t\tif (future != null) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"unregistering watchdog for data source {0}\", name);\n\t\t\t\t\t}\n\t\t\t\t\tdataSourceBean.removeWatchdogTask(future);\n\t\t\t\t}\n\n\t\t\t\tif (!watchdogFrequency.isZero()) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"registering watchdog for data source {0} with frequency {1}\",\n\t\t\t\t\t\t\t\tnew Object[]{name, watchdogFrequency});\n\t\t\t\t\t}\n\t\t\t\t\tdataSourceBean.addWatchdogTask(this::executeWatchdog, watchdogFrequency);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/beans/MDPoolBean.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.beans;\n\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.RegistrarBeanWithDefaultBeanClass;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.core.Kernel;\n\n/**\n * Abstract class providing base part for implementation of pool for multiple domains.\n * <br>\n * Created by andrzej on 08.03.2016.\n */\npublic abstract class MDPoolBean<S, T extends MDPoolConfigBean<S, T>>\n\t\timplements RegistrarBeanWithDefaultBeanClass {\n\n\tpublic static final String REPO_URI = \"repo-uri\";\n\tpublic static final String REPO_CLASS = \"repo-class\";\n\tpublic static final String POOL_CLASS = \"pool-class\";\n\tpublic static final String POOL_SIZE = \"pool-size\";\n\t@Inject(nullAllowed = true)\n\tprivate MDPoolConfigBean[] configBeans;\n\tprivate Kernel kernel;\n\t@ConfigField(desc = \"Bean name\")\n\tprivate String name;\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\t\tthis.kernel = kernel;\n\t\tif (!kernel.isBeanClassRegistered(\"default\")) {\n\t\t\tregisterConfigBean(\"default\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\t\tthis.kernel = null;\n\t}\n\n\t/**\n\t * Default alias used if for provided domain then is no repo instance.\n\t *\n\t * @return default alias\n\t */\n\tpublic String getDefaultAlias() {\n\t\treturn \"default\";\n\t}\n\n\tprotected void registerConfigBean(String domain) {\n\t\tkernel.registerBean(domain).asClass(getConfigClass()).exec();\n\t}\n\n\t/**\n\t * Returns per domain configuration class\n\t *\n\t * @return class\n\t */\n\tprotected abstract Class<? extends T> getConfigClass();\n\n\t/**\n\t * Method called to add repo instance for domain\n\t *\n\t */\n\tprotected abstract void addRepo(String domain, S repo);\n\n\t/**\n\t * Method called to remove repo instance for domain\n\t * @return removed instance of repo\n\t */\n\tprotected abstract S removeRepo(String domain);\n\n\t/**\n\t * Method called to set default repo instance.\n\t *\n\t * @param repo instance of repo\n\t */\n\tprotected abstract void setDefault(S repo);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/beans/MDPoolBeanWithStatistics.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.beans;\n\nimport tigase.db.Repository;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.stats.ComponentStatisticsProvider;\nimport tigase.stats.StatisticsInvocationHandler;\nimport tigase.stats.StatisticsList;\n\nimport java.lang.reflect.Proxy;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.stream.Stream;\n\n/**\n * Class extends MDPoolBean class by adding support for statistics gathering for every managed repository.\n * <br>\n * Created by andrzej on 14.12.2016.\n */\npublic abstract class MDPoolBeanWithStatistics<S extends Repository, T extends MDPoolConfigBean<S, T>>\n\t\textends MDPoolBean<S, T>\n\t\timplements ComponentStatisticsProvider {\n\n\tprivate final Class<S> repoClazz;\n\tprivate S def;\n\tprivate S defProxy;\n\tprivate ConcurrentHashMap<String, StatisticsInvocationHandler<S>> handlers = new ConcurrentHashMap<>();\n\tprivate ConcurrentHashMap<String, S> repos = new ConcurrentHashMap<>();\n\tprivate ConcurrentHashMap<String, S> reposProxy = new ConcurrentHashMap<>();\n\t@ConfigField(desc = \"Enable statistics\", alias = \"statistics\")\n\tprivate boolean statisticsEnabled = true;\n\n\tpublic MDPoolBeanWithStatistics(Class<S> repoClazz) {\n\t\tthis.repoClazz = repoClazz;\n\t}\n\n\tpublic void addRepo(String name, S repo) {\n\t\tsynchronized (this) {\n\t\t\tif (statisticsEnabled) {\n\t\t\t\twrapInProxy(name, repo);\n\t\t\t}\n\t\t\trepos.put(name, repo);\n\t\t}\n\t}\n\n\tpublic S removeRepo(String domain) {\n\t\tsynchronized (this) {\n\t\t\tS repo = repos.remove(domain);\n\t\t\tif (statisticsEnabled) {\n\t\t\t\treposProxy.remove(domain);\n\t\t\t}\n\t\t\treturn repo;\n\t\t}\n\t}\n\n\tpublic Collection<String> getDomainsList() {\n\t\treturn Collections.unmodifiableCollection(repos.keySet());\n\t}\n\n\tpublic S getDefaultRepository() {\n\t\treturn statisticsEnabled ? defProxy : def;\n\t}\n\n\tpublic Stream<S> repositoriesStream() {\n\t\tif (statisticsEnabled) {\n\t\t\treturn reposProxy.values().stream();\n\t\t} else {\n\t\t\treturn repos.values().stream();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void everyHour() {\n\t\tif (statisticsEnabled) {\n\t\t\thandlers.values().forEach(StatisticsInvocationHandler::everyHour);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void everyMinute() {\n\t\tif (statisticsEnabled) {\n\t\t\thandlers.values().forEach(StatisticsInvocationHandler::everyMinute);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void everySecond() {\n\t\tif (statisticsEnabled) {\n\t\t\thandlers.values().forEach(StatisticsInvocationHandler::everySecond);\n\t\t}\n\t}\n\n\tpublic void setDefault(S repo) {\n\t\tdef = repo;\n\t\tdefProxy = reposProxy.get(\"default\");\n\t}\n\n\t@Override\n\tpublic void getStatistics(String compName, StatisticsList list) {\n\t\thandlers.values().forEach(handler -> {\n\t\t\thandler.getStatistics(compName, null, list);\n\t\t});\n\t}\n\n\tpublic void setStatisticsEnabled(boolean value) {\n\t\tif (this.statisticsEnabled != value) {\n\t\t\tsynchronized (this) {\n\t\t\t\tif (value) {\n\t\t\t\t\trepos.forEach(this::wrapInProxy);\n\t\t\t\t}\n\t\t\t\tthis.statisticsEnabled = value;\n\t\t\t\tif (!value) {\n\t\t\t\t\treposProxy.clear();\n\t\t\t\t\tdefProxy = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic S getRepo(String domain) {\n\t\tif (statisticsEnabled) {\n\t\t\tif (domain == null) {\n\t\t\t\treturn defProxy;\n\t\t\t}\n\t\t\tS result = reposProxy.get(domain);\n\n\t\t\tif (result == null) {\n\t\t\t\tresult = defProxy;\n\t\t\t}\n\n\t\t\treturn result;\n\n\t\t} else {\n\t\t\tif (domain == null) {\n\t\t\t\treturn def;\n\t\t\t}\n\t\t\tS result = repos.get(domain);\n\n\t\t\tif (result == null) {\n\t\t\t\tresult = def;\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t}\n\n\tpublic void wrapInProxy(String name, S repo) {\n\t\tStatisticsInvocationHandler handler = new StatisticsInvocationHandler(name, repo, repoClazz);\n\t\tS proxy = (S) Proxy.newProxyInstance(repo.getClass().getClassLoader(), new Class[]{repoClazz}, handler);\n\t\thandlers.put(name, handler);\n\t\treposProxy.put(name, proxy);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/beans/MDPoolConfigBean.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.beans;\n\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.db.DBInitException;\nimport tigase.db.RepositoryFactory;\nimport tigase.db.RepositoryPool;\nimport tigase.db.util.DBInitForkJoinPoolCache;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.RegistrarBean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.beans.config.ConfigFieldType;\nimport tigase.kernel.beans.config.ConfigurationChangedAware;\nimport tigase.kernel.core.BeanConfig;\nimport tigase.kernel.core.Kernel;\nimport tigase.osgi.ModulesManagerImpl;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.*;\nimport java.util.concurrent.ForkJoinPool;\nimport java.util.concurrent.ForkJoinTask;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.db.beans.MDPoolBean.*;\n\n/**\n * Base class for configuration beans of {@link tigase.db.beans.DataSourceBean}, {@link\n * tigase.db.beans.AuthRepositoryMDPoolBean} and {@link tigase.db.beans.UserRepositoryMDPoolBean}\n * <br>\n * Created by andrzej on 08.03.2016.\n */\npublic abstract class MDPoolConfigBean<A, B extends MDPoolConfigBean<A, B>>\n\t\timplements Initializable, ConfigurationChangedAware, RegistrarBean {\n\n\tprivate static final Logger log = Logger.getLogger(MDPoolConfigBean.class.getCanonicalName());\n\t@ConfigField(alias = REPO_CLASS, desc = \"Class implementing repository\", allowAliasFromParent = false)\n\tprotected String cls;\n\t@Inject\n\tprotected MDPoolBean<A, B> mdPool;\n\t@ConfigField(desc = \"Name (ie. domain)\")\n\tprotected String name;\n\t@ConfigField(alias = POOL_CLASS, desc = \"Class implementing repository pool\", allowAliasFromParent = false)\n\tprotected String poolCls;\n\t@ConfigField(alias = POOL_SIZE, desc = \"Pool size\", allowAliasFromParent = false)\n\tprotected int poolSize = RepositoryFactory.USER_REPO_POOL_SIZE_PROP_VAL;\n\t@ConfigField(alias = REPO_URI, desc = \"URI for repository\", allowAliasFromParent = false, type = ConfigFieldType.JdbcUrl)\n\tprotected String uri;\n\t@Inject(nullAllowed = true)\n\tprivate Set<A> instances;\n\tprivate Kernel kernel;\n\t@Inject(bean = \"instance\", nullAllowed = true)\n\tprivate A repository;\n\tprivate boolean skipInitializationErrors = false;\n\n\t@Override\n\tpublic void beanConfigurationChanged(Collection<String> changedFields) {\n\t\t// name not set yet - skip initialization\n\t\tif (name == null || getUri() == null || kernel == null) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tString cls = getRepositoryClassName();\n\t\t\tif (cls == null) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tClass<?> repoClass = ModulesManagerImpl.getInstance().forName(cls);\n\t\t\tString poolCls = getRepositoryPoolClassName();\n\n\t\t\tKernel.DelayedDependencyInjectionQueue queue = kernel.beginDependencyDelayedInjection();\n\n\t\t\tif (poolCls == null) {\n\t\t\t\tif (repository == null || changedFields.contains(\"poolCls\")) {\n\t\t\t\t\tkernel.registerBean(\"instance\").asClass(repoClass).exec();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (repository == null || changedFields.contains(\"poolCls\") || changedFields.contains(\"poolSize\")) {\n\t\t\t\t\tClass<?> poolClass = ModulesManagerImpl.getInstance().forName(poolCls);\n\n\t\t\t\t\tfor (int i = 0; i < poolSize; i++) {\n\t\t\t\t\t\tkernel.registerBean(\"repo-\" + i).asClass(repoClass).exec();\n\t\t\t\t\t}\n\n\t\t\t\t\tkernel.registerBean(\"instance\").asClass(poolClass).exec();\n\t\t\t\t}\n\t\t\t}\n\t\t\tkernel.finishDependecyDelayedInjection(queue);\n\t\t\tunloadOldBeans();\n\t\t} catch (DBInitException | ClassNotFoundException | InstantiationException | InvocationTargetException | IllegalAccessException ex) {\n\t\t\tthrow new RuntimeException(\n\t\t\t\t\t\"Could not initialize \" + getRepositoryIfc().getCanonicalName() + \" for name '\" + name + \"'\", ex);\n\t\t}\n\t}\n\n\tpublic void unloadOldBeans() {\n\t\tList<BeanConfig> beanConfigs = new ArrayList<>(kernel.getDependencyManager().getBeanConfigs());\n\t\tfor (BeanConfig bc : beanConfigs) {\n\t\t\tif (bc.getBeanName().startsWith(\"repo-\")) {\n\t\t\t\ttry {\n\t\t\t\t\tInteger pos = Integer.parseInt(bc.getBeanName().replace(\"repo-\", \"\"));\n\t\t\t\t\tif (getRepositoryPoolClassName() == null || pos >= poolSize) {\n\t\t\t\t\t\tkernel.unregister(bc.getBeanName());\n\t\t\t\t\t}\n\t\t\t\t} catch (NumberFormatException | DBInitException ex) {\n\t\t\t\t\t// this is not instance create by us, ignoring\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tbeanConfigurationChanged(Collections.singletonList(\"uri\"));\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\t\tthis.kernel = kernel;\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\n\t}\n\n\tpublic void setInstances(Set<A> instances) {\n\t\tif (instances != null) {\n\t\t\tfor (Iterator<A> iter = instances.iterator(); iter.hasNext(); ) {\n\t\t\t\tA it = iter.next();\n\t\t\t\tif (it instanceof MDPoolBean) {\n\t\t\t\t\titer.remove();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tHashSet<A> toInitialize = new HashSet<A>(instances);\n\t\tif (this.instances != null) {\n\t\t\ttoInitialize.removeAll(this.instances);\n\t\t}\n\n\t\tif (!toInitialize.isEmpty()) {\n\t\t\tQueue<ForkJoinTask<A>> tasks = new ArrayDeque<>();\n\t\t\tfinal ForkJoinPool pool = DBInitForkJoinPoolCache\n\t\t\t.shared.pool(repository == null ? \"dbinit\" : \"dbinit-\" + repository.hashCode(), Math.min(toInitialize.size(), 128));//new ForkJoinPool(Math.min(toInitialize.size(), 128));\n\t\t\tfor (A repo : toInitialize) {\n\t\t\t\ttasks.offer(pool.submit(() -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tinitRepository(repo);\n\t\t\t\t\t} catch (RepositoryException ex) {\n\t\t\t\t\t\tif (skipInitializationErrors) {\n\t\t\t\t\t\t\t// maybe we should not ignore this error but delay initialization and await successful database initialization?\n\t\t\t\t\t\t\t// or maybe we should unload this bean totally as it is not working as it should?\n\t\t\t\t\t\t\t// Two things to consider:\n\t\t\t\t\t\t\t// * initialization during bootstrap\n\t\t\t\t\t\t\t// * initialization during reconfiguration of server\n\t\t\t\t\t\t\tLogger.getLogger(this.getClass().getCanonicalName())\n\t\t\t\t\t\t\t\t\t.log(Level.WARNING,\n\t\t\t\t\t\t\t\t\t\t \"Could not initialize \" + repo.getClass().getCanonicalName() + \" for name '\" +\n\t\t\t\t\t\t\t\t\t\t\t\t name + \"'\", ex);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthrow new RuntimeException(\n\t\t\t\t\t\t\t\t\t\"Could not initialize \" + repo.getClass().getCanonicalName() + \" for name '\" +\n\t\t\t\t\t\t\t\t\t\t\tname + \"'\", ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn repo;\n\t\t\t\t}));\n\t\t\t}\n\t\t\tForkJoinTask<A> task;\n\t\t\twhile ((task = tasks.poll()) != null) {\n\t\t\t\tA repo = task.join();\n\t\t\t\tif (repository instanceof RepositoryPool && !(repo instanceof RepositoryPool)) {\n\t\t\t\t\t((RepositoryPool<A>) repository).addRepo(repo);\n\t\t\t\t}\n\t\t\t}\n\t\t\t//pool.shutdown();\n\t\t}\n\n\t\tthis.instances = instances;\n\t}\n\n\tpublic void setMdPool(MDPoolBean<A, B> mdPool) {\n\t\tif (mdPool != null && this.repository != null) {\n\t\t\tmdPool.addRepo(name, this.repository);\n\t\t\tif (\"default\".equals(name)) {\n\t\t\t\tmdPool.setDefault(this.repository);\n\t\t\t}\n\t\t}\n\t\tthis.mdPool = mdPool;\n\t}\n\n\t/**\n\t * Get interface to which instances initialized by this config bean must conform to.\n\t *\n\t * @return interface\n\t */\n\tprotected abstract Class<? extends A> getRepositoryIfc();\n\n\t/**\n\t * Get name of a pool which should be used if any.\n\t *\n\t * @return class name\n\t *\n\t */\n\tprotected abstract String getRepositoryPoolClassName() throws DBInitException;\n\n\t/**\n\t * Method used to initialize provided instance\n\t */\n\tprotected abstract void initRepository(A repo) throws RepositoryException;\n\n\t/**\n\t * Get class name to initialize as repository\n\t */\n\tprotected String getRepositoryClassName() throws DBInitException {\n\t\tif (cls != null) {\n\t\t\treturn cls;\n\t\t}\n\t\treturn RepositoryFactory.getRepoClassName(getRepositoryIfc(), uri);\n\t}\n\n\tprotected String getUri() {\n\t\treturn uri;\n\t}\n\n\tprotected A getRepository() {\n\t\treturn repository;\n\t}\n\n\tpublic void setRepository(A repo) {\n\t\tthis.repository = repo;\n\t\tif (repo != null) {\n\t\t\tif (instances != null) {\n\t\t\t\tfor (A instance : instances) {\n\t\t\t\t\tif (repository instanceof RepositoryPool && !(instance instanceof RepositoryPool)) {\n\t\t\t\t\t\t((RepositoryPool) repository).addRepo(instance);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (mdPool != null) {\n\t\t\tif (repo != null) {\n\t\t\t\tmdPool.addRepo(name, repo);\n\t\t\t} else {\n\t\t\t\tmdPool.removeRepo(name);\n\t\t\t}\n\t\t\tif (\"default\".equals(name)) {\n\t\t\t\tmdPool.setDefault(repo);\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/main/java/tigase/db/beans/MDRepositoryBean.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.beans;\n\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.db.DBInitException;\nimport tigase.db.DataSource;\nimport tigase.db.DataSourceAware;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.HandleEvent;\nimport tigase.kernel.beans.*;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.beans.config.ConfigurationChangedAware;\nimport tigase.kernel.core.Kernel;\nimport tigase.osgi.ModulesManagerImpl;\n\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Stream;\n\nimport static tigase.db.beans.MDPoolBean.REPO_CLASS;\n\n/**\n * Abstract class implementing bean to which should be used to create name aware repository pool. This class is\n * responsible for creation of correct repository instances for every DataSource configured.\n * <br>\n * Created by andrzej on 15.03.2016.\n */\npublic abstract class MDRepositoryBean<T extends DataSourceAware>\n\t\timplements Initializable, UnregisterAware, RegistrarBeanWithDefaultBeanClass {\n\n\tprivate static final Logger log = Logger.getLogger(MDRepositoryBean.class.getCanonicalName());\n\n\t/**\n\t * Determines behaviour of MDRepositoryBean.\n\t */\n\tpublic static enum SelectorType {\n\t\t/**\n\t\t * Repository instances will be created for default data source and for data sources listed in configuration.\n\t\t */\n\t\tList,\n\t\t/**\n\t\t * Repository instances will be created for every data source.\n\t\t */\n\t\tEveryDataSource,\n\t\t/**\n\t\t * Repository instances will be created for every data source for which user repository exists.\n\t\t */\n\t\tEveryUserRepository,\n\t}\n\tprivate final Map<String, T> repositories = new ConcurrentHashMap<>();\n\t@ConfigField(desc = \"Map of aliases for data sources to use\")\n\tprotected ConcurrentHashMap<String, String> aliases = new ConcurrentHashMap<>();\n\t@ConfigField(desc = \"Create repositories for: every UserRepository, every data source, listed data sources\")\n\tprotected SelectorType dataSourceSelection = SelectorType.List;\n\t@Inject(nullAllowed = true)\n\tprivate MDRepositoryConfigBean[] configBeans;\n\t@Inject\n\tprivate DataSourceBean dataSourceBean;\n\t@Inject\n\tprivate EventBus eventBus;\n\tprivate Kernel kernel;\n\t@ConfigField(desc = \"Bean name\")\n\tprivate String name;\n\n\t/**\n\t * Returns name of a bean\n\t *\n\t * @return name of a bean\n\t */\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setDataSourceBean(DataSourceBean dataSourceBean) {\n\t\tMap<String, DataSource> oldDataSources = new HashMap<>();\n\t\tString defAlias = \"default\";\n\t\tif (this.dataSourceBean != null) {\n\t\t\toldDataSources.put(defAlias, this.dataSourceBean.getRepository(defAlias));\n\t\t\tfor (String domain : this.dataSourceBean.getDataSourceNames()) {\n\t\t\t\toldDataSources.put(domain, this.dataSourceBean.getRepository(domain));\n\t\t\t}\n\t\t}\n\n\t\tthis.dataSourceBean = dataSourceBean;\n\n\t\tif (this.dataSourceBean != null) {\n\t\t\tswitch (dataSourceSelection) {\n\t\t\t\tcase EveryDataSource:\n\t\t\t\t\tfor (String name : dataSourceBean.getDataSourceNames()) {\n\t\t\t\t\t\tregisterIfNotExists(name);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase EveryUserRepository:\n\t\t\t\t\tregisterIfNotExists(\"default\");\n\n\t\t\t\t\tUserRepositoryMDPoolBean userRepositoryPool = kernel.getInstance(UserRepositoryMDPoolBean.class);\n\t\t\t\t\tfor (String name : userRepositoryPool.getDomainsList()) {\n\t\t\t\t\t\tregisterIfNotExists(name);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase List:\n\t\t\t\t\tregisterIfNotExists(\"default\");\n\n\t\t\t\t\t// manual registration is already possible by configuring new subbeans\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void registerIfNotExists(String name) {\n\t\tif (!kernel.isBeanClassRegistered(name)) {\n\t\t\tClass<?> cls = getDefaultBeanClass();\n\t\t\tkernel.registerBean(name).asClass(cls).exec();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\t\tthis.kernel = kernel;\n\t\tregisterIfNotExists(\"default\");\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\t\tthis.kernel = null;\n\t}\n\n\tpublic void initialize() {\n\t\teventBus.registerAll(this);\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\teventBus.unregisterAll(this);\n\t}\n\n\t/**\n\t * Method returns class implementing repository which supports data source instance provided in parameter.\n\t * @return repository class\n\t *\n\t */\n\tprotected abstract Class<? extends T> findClassForDataSource(DataSource dataSource) throws DBInitException;\n\n\t/**\n\t * Provides access to all available repository instances\n\t *\n\t * @return stream of repository instances\n\t */\n\tprotected Stream<T> repositoriesStream() {\n\t\treturn getRepositories().values().stream();\n\t}\n\n\t/**\n\t * Provides access to unmodifiable map domain to repository instance\n\t *\n\t * @return map of domain to repository instance\n\t */\n\tprotected Map<String, T> getRepositories() {\n\t\treturn Collections.unmodifiableMap(repositories);\n\t}\n\n\t/**\n\t * Provides repository instance for passed domain name\n\t * @return instance of repository\n\t */\n\tprotected T getRepository(String domain) {\n\t\tT repo = repositories.get(aliases.getOrDefault(domain, domain));\n\t\tif (repo == null) {\n\t\t\trepo = repositories.get(\"default\");\n\t\t}\n\t\treturn repo;\n\t}\n\n\t@HandleEvent\n\tprotected void onDataSourceChange(DataSourceBean.DataSourceChangedEvent event) {\n\t\tif (!event.isCorrectSender(dataSourceBean)) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (dataSourceSelection == SelectorType.EveryDataSource) {\n\t\t\tif (event.getNewDataSource() == null) {\n\t\t\t\tkernel.unregister(event.getDomain());\n\t\t\t} else {\n\t\t\t\tregisterIfNotExists(event.getDomain());\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Method called to initialized passed repository instance for passed domain. <br> Should be empty if no custom\n\t * initialization is required.\n\t *\n\t */\n\tprotected void initializeRepository(String domain, T repo) {\n\n\t}\n\n\t/**\n\t * Method called when repository instance for domain changes.\n\t *\n\t * @param domain name of domain\n\t * @param newRepo new instance of repository\n\t * @param oldRepo old instance of repository\n\t */\n\tprotected void updateDataSourceAware(String domain, T newRepo, T oldRepo) {\n\t\tif (newRepo != null) {\n\t\t\tthis.repositories.put(domain, newRepo);\n\t\t} else {\n\t\t\tthis.repositories.remove(domain, oldRepo);\n\t\t}\n\t}\n\n\t/**\n\t * MDRepositoryConfigBean is bean responsible for basic management and initialization of repository for domain.\n\t *\n\t *\t */\n\tpublic abstract static class MDRepositoryConfigBean<A extends DataSourceAware>\n\t\t\timplements Initializable, UnregisterAware, ConfigurationChangedAware, RegistrarBean {\n\n\t\t@Inject\n\t\tprotected DataSourceBean dataSourceBean;\n\t\t@ConfigField(alias = REPO_CLASS, desc = \"Class implementing repository\", allowAliasFromParent = false)\n\t\tprivate String cls;\n\t\tprivate DataSource dataSource;\n\t\t@Inject(bean = \"instance\", nullAllowed = true)\n\t\tprivate A dataSourceAware;\n\t\t@ConfigField(desc = \"Name of data source\", alias = \"data-source\")\n\t\tprivate String dataSourceName;\n\t\t@Inject\n\t\tprivate EventBus eventBus;\n\t\tprivate Kernel kernel;\n\t\t@Inject\n\t\tprivate MDRepositoryBean<A> mdRepositoryBean;\n\t\t@ConfigField(desc = \"Name (ie. domain)\")\n\t\tprivate String name;\n\n\t\t@Override\n\t\tpublic void beanConfigurationChanged(Collection<String> changedFields) {\n\t\t\tif (name == null || mdRepositoryBean == null || dataSourceBean == null) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tString name = this.name;\n\t\t\tif (dataSourceName != null && !dataSourceName.isEmpty()) {\n\t\t\t\tname = dataSourceName;\n\t\t\t}\n\n\t\t\tdataSource = dataSourceBean.getRepository(name);\n\n\t\t\tif (dataSource != null) {\n\t\t\t\ttry {\n\t\t\t\t\tClass<?> repoClass = getRepositoryClassName();\n\n\t\t\t\t\tkernel.registerBean(\"instance\").asClass(repoClass).exec();\n\t\t\t\t} catch (DBInitException | ClassNotFoundException ex) {\n\t\t\t\t\tthrow new RuntimeException(\"Could not initialize bean '\" + name + \"'\", ex);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (kernel.isBeanClassRegistered(\"instance\")) {\n\t\t\t\t\tkernel.unregister(\"instance\");\n\t\t\t\t}\n\t\t\t\tif (log.isLoggable(Level.WARNING)) {\n\t\t\t\t\tlog.log(Level.WARNING, \"There is no data source named '\" + Optional.ofNullable(dataSourceName)\n\t\t\t\t\t\t\t.orElse(name) +  \"'\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic void setDataSourceAware(A dataSourceAware) {\n\t\t\tif (mdRepositoryBean == null || this.dataSourceAware == dataSourceAware) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (dataSourceAware != null) {\n\t\t\t\tthis.mdRepositoryBean.initializeRepository(name, dataSourceAware);\n\t\t\t\ttry {\n\t\t\t\t\tdataSource.checkSchemaVersion(dataSourceAware, true);\n\t\t\t\t\tdataSourceAware.setDataSource(dataSource);\n\t\t\t\t} catch (RepositoryException ex) {\n\t\t\t\t\tthrow new RuntimeException(\"Failed to initialize repository\", ex);\n\t\t\t\t}\n\t\t\t}\n\t\t\tmdRepositoryBean.updateDataSourceAware(name, dataSourceAware, this.dataSourceAware);\n\t\t\tthis.dataSourceAware = dataSourceAware;\n\t\t}\n\n\t\t@Override\n\t\tpublic void register(Kernel kernel) {\n\t\t\tthis.kernel = kernel;\n\t\t\tif (kernel.getParent() != null) {\n\t\t\t\tString rootBean = kernel.getParent().getName();\n\t\t\t\tthis.kernel.getParent().ln(\"service\", kernel, rootBean);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void unregister(Kernel kernel) {\n\t\t\tkernel.unregister(\"instance\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void initialize() {\n\t\t\teventBus.registerAll(this);\n\t\t\tbeanConfigurationChanged(Collections.singleton(\"uri\"));\n\t\t\tif (dataSource == null) {\n\t\t\t\tthrow new RuntimeException(\"There is no data source named '\" + Optional.ofNullable(dataSourceName)\n\t\t\t\t\t\t.orElse(name) + \"'\");\n\t\t\t}\n\t\t\tsetDataSourceAware(kernel.getInstance(\"instance\"));\n\t\t}\n\n\t\t@Override\n\t\tpublic void beforeUnregister() {\n\t\t\teventBus.unregisterAll(this);\n\t\t\tkernel.unregister(\"instance\");\n\t\t}\n\n\t\t/**\n\t\t * Method returns class for instance repository matching data source or returning class specified in\n\t\t * <code>cls</code> config field.\n\t\t */\n\t\tprotected Class<?> getRepositoryClassName() throws DBInitException, ClassNotFoundException {\n\t\t\tif (cls == null) {\n\t\t\t\treturn mdRepositoryBean.findClassForDataSource(dataSource);\n\t\t\t}\n\t\t\treturn ModulesManagerImpl.getInstance().forName(cls);\n\t\t}\n\n\t\tprotected String getCls() {\n\t\t\treturn cls;\n\t\t}\n\n\t\t@HandleEvent\n\t\tprotected void onDataSourceChange(DataSourceBean.DataSourceChangedEvent event) {\n\t\t\tif (!event.isCorrectSender(dataSourceBean)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!event.getDomain().equals(name) && !event.getDomain().equals(dataSourceName)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tbeanConfigurationChanged(Collections.singleton(\"uri\"));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/beans/MDRepositoryBeanWithStatistics.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.beans;\n\nimport tigase.db.DataSourceAware;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.stats.ComponentStatisticsProvider;\nimport tigase.stats.StatisticsInvocationHandler;\nimport tigase.stats.StatisticsList;\n\nimport java.lang.reflect.Proxy;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.stream.Stream;\n\n/**\n * Extended version of MDRepositoryBean class with support for statistics gathering.\n * <br>\n * Created by andrzej on 15.12.2016.\n */\npublic abstract class MDRepositoryBeanWithStatistics<T extends DataSourceAware>\n\t\textends MDRepositoryBean<T>\n\t\timplements ComponentStatisticsProvider {\n\n\tprivate final Class<?>[] repoInterfaces;\n\tprivate ConcurrentHashMap<String, StatisticsInvocationHandler<T>> handlers = new ConcurrentHashMap<>();\n\tprivate ConcurrentHashMap<String, T> reposProxy = new ConcurrentHashMap<>();\n\t@ConfigField(desc = \"Enable statistics\", alias = \"statistics\")\n\tprivate boolean statisticsEnabled = true;\n\n\tpublic MDRepositoryBeanWithStatistics(Class<?>... repoClazz) {\n\t\tthis.repoInterfaces = repoClazz;\n\t}\n\n\t@Override\n\tpublic void everyHour() {\n\t\tif (statisticsEnabled) {\n\t\t\thandlers.values().forEach(StatisticsInvocationHandler::everyHour);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void everyMinute() {\n\t\tif (statisticsEnabled) {\n\t\t\thandlers.values().forEach(StatisticsInvocationHandler::everyMinute);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void everySecond() {\n\t\tif (statisticsEnabled) {\n\t\t\thandlers.values().forEach(StatisticsInvocationHandler::everySecond);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void getStatistics(String compName, StatisticsList list) {\n\t\thandlers.values().forEach(handler -> {\n\t\t\thandler.getStatistics(compName, getName(), list);\n\t\t});\n\t}\n\n\tpublic void setStatisticsEnabled(boolean value) {\n\t\tif (this.statisticsEnabled != value) {\n\t\t\tsynchronized (this) {\n\t\t\t\tif (value) {\n\t\t\t\t\tsuper.getRepositories().forEach(this::wrapInProxy);\n\t\t\t\t}\n\t\t\t\tthis.statisticsEnabled = value;\n\t\t\t\tif (!value) {\n\t\t\t\t\treposProxy.clear();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void wrapInProxy(String name, T repo) {\n\t\tClass<?>[] repoInterfaces = Stream.concat(Arrays.stream(this.repoInterfaces), Stream.of(DataSourceAware.class))\n\t\t\t\t.filter(cls -> cls.isAssignableFrom(repo.getClass()))\n\t\t\t\t.toArray(size -> new Class<?>[size]);\n\t\tStatisticsInvocationHandler handler = new StatisticsInvocationHandler(name, repo, repoInterfaces);\n\t\tT proxy = (T) Proxy.newProxyInstance(repo.getClass().getClassLoader(), repoInterfaces, handler);\n\t\thandlers.put(name, handler);\n\t\treposProxy.put(name, proxy);\n\t}\n\n\t@Override\n\tprotected T getRepository(String domain) {\n\t\tif (statisticsEnabled) {\n\t\t\tT repo = reposProxy.get(aliases.getOrDefault(domain, domain));\n\t\t\tif (repo == null) {\n\t\t\t\trepo = reposProxy.get(\"default\");\n\t\t\t}\n\t\t\treturn repo;\n\t\t}\n\t\treturn super.getRepository(domain);\n\t}\n\n\t@Override\n\tprotected Map<String, T> getRepositories() {\n\t\tif (statisticsEnabled) {\n\t\t\treturn Collections.unmodifiableMap(reposProxy);\n\t\t}\n\t\treturn super.getRepositories();\n\t}\n\n\t@Override\n\tprotected void updateDataSourceAware(String domain, T newRepo, T oldRepo) {\n\t\tif (statisticsEnabled && newRepo != null) {\n\t\t\twrapInProxy(domain, newRepo);\n\t\t} else {\n\t\t\treposProxy.remove(domain);\n\t\t}\n\t\tsuper.updateDataSourceAware(domain, newRepo, oldRepo);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/beans/SDRepositoryBean.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.beans;\n\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.db.DBInitException;\nimport tigase.db.DataSource;\nimport tigase.db.DataSourceAware;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.HandleEvent;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.RegistrarBean;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.beans.config.ConfigurationChangedAware;\nimport tigase.kernel.core.Kernel;\nimport tigase.osgi.ModulesManagerImpl;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Optional;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.db.beans.MDPoolBean.REPO_CLASS;\n\n/**\n * Abstract class implementing bean to which can be used to create name unaware repository pool. This class is\n * resposible for creation of correct repository instance for single specified data source.\n * <br>\n * Created by andrzej on 17.08.2016.\n */\npublic abstract class SDRepositoryBean<A extends DataSourceAware>\n\t\timplements Initializable, UnregisterAware, ConfigurationChangedAware, RegistrarBean {\n\n\tprivate static final Logger log = Logger.getLogger(SDRepositoryBean.class.getCanonicalName());\n\n\t@ConfigField(alias = REPO_CLASS, desc = \"Class implementing repository\", allowAliasFromParent = false)\n\tprivate String cls;\n\tprivate DataSource dataSource;\n\t@Inject\n\tprivate DataSourceBean dataSourceBean;\n\t@ConfigField(desc = \"Name of data source\", alias = \"data-source\")\n\tprivate String dataSourceName;\n\t@Inject\n\tprivate EventBus eventBus;\n\tprivate Kernel kernel;\n\t@ConfigField(desc = \"Bean name\")\n\tprivate String name;\n\t@Inject(bean = \"instance\", nullAllowed = true)\n\tprivate A repository;\n\n\tpublic String getDataSourceName() {\n\t\tif (dataSourceName == null) {\n\t\t\treturn \"default\";\n\t\t}\n\t\treturn dataSourceName;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic void beanConfigurationChanged(Collection<String> changedFields) {\n\t\tif (dataSourceBean == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tString name = \"default\";\n\t\tif (dataSourceName != null && !dataSourceName.isEmpty()) {\n\t\t\tname = dataSourceName;\n\t\t}\n\n\t\tdataSource = dataSourceBean.getRepository(name);\n\n\t\tif (dataSource != null) {\n\t\t\ttry {\n\t\t\t\tClass<?> repoClass = getRepositoryClassName();\n\n\t\t\t\tkernel.registerBean(\"instance\").asClass(repoClass).exec();\n\t\t\t} catch (DBInitException | ClassNotFoundException ex) {\n\t\t\t\tthrow new RuntimeException(\"Could not initialize bean '\" + name + \"'\", ex);\n\t\t\t}\n\t\t} else {\n\t\t\tif (kernel.isBeanClassRegistered(\"instance\")) {\n\t\t\t\tkernel.unregister(\"instance\");\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.WARNING)) {\n\t\t\t\tlog.log(Level.WARNING, \"There is no data source named '\" + Optional.ofNullable(dataSourceName)\n\t\t\t\t\t\t.orElse(name) +  \"'\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\t\tthis.kernel = kernel;\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\t\tkernel.unregister(\"instance\");\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\teventBus.registerAll(this);\n\t\tbeanConfigurationChanged(Collections.singleton(\"uri\"));\n\t\tif (dataSource == null) {\n\t\t\tthrow new RuntimeException(\"There is no data source named '\" + Optional.ofNullable(dataSourceName)\n\t\t\t\t\t.orElse(\"default\") + \"'\");\n\t\t}\n\t\tsetRepository(kernel.getInstance(\"instance\"));\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\teventBus.unregisterAll(this);\n\t\tkernel.unregister(\"instance\");\n\t}\n\n\tprotected abstract Class<?> findClassForDataSource(DataSource dataSource) throws DBInitException;\n\n\tprotected void initializeRepository(A repository) {\n\t}\n\n\tprotected A getRepository() {\n\t\treturn repository;\n\t}\n\n\tpublic void setRepository(A repository) {\n\t\tif (repository != null) {\n\t\t\tinitializeRepository(repository);\n\t\t\ttry {\n\t\t\t\tdataSource.checkSchemaVersion(repository, true);\n\t\t\t\trepository.setDataSource(dataSource);\n\t\t\t} catch (RepositoryException ex) {\n\t\t\t\tthrow new RuntimeException(\"Failed to initialize repository\", ex);\n\t\t\t}\n\t\t}\n\t\tthis.repository = repository;\n\t}\n\n\tprotected Class<?> getRepositoryClassName() throws DBInitException, ClassNotFoundException {\n\t\tif (cls == null) {\n\t\t\treturn findClassForDataSource(dataSource);\n\t\t}\n\t\treturn ModulesManagerImpl.getInstance().forName(cls);\n\t}\n\n\t@HandleEvent\n\tprotected void onDataSourceChange(DataSourceBean.DataSourceChangedEvent event) {\n\t\tif (!event.isCorrectSender(dataSourceBean)) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!event.getDomain().equals(\"default\") && !event.getDomain().equals(dataSourceName)) {\n\t\t\treturn;\n\t\t}\n\n\t\tbeanConfigurationChanged(Collections.singleton(\"uri\"));\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/beans/SDRepositoryBeanWithStatistics.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.beans;\n\nimport tigase.db.DataSourceAware;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.stats.ComponentStatisticsProvider;\nimport tigase.stats.StatisticsInvocationHandler;\nimport tigase.stats.StatisticsList;\n\nimport java.lang.reflect.Proxy;\n\n/**\n * Extended version of SDRepositoryBean class with support for statistics gathering.\n * <br>\n * Created by andrzej on 15.12.2016.\n */\npublic abstract class SDRepositoryBeanWithStatistics<T extends DataSourceAware>\n\t\textends SDRepositoryBean<T>\n\t\timplements ComponentStatisticsProvider {\n\n\tprivate final Class<T> repoClazz;\n\tprivate StatisticsInvocationHandler<T> handler;\n\tprivate T repoProxy;\n\t@ConfigField(desc = \"Enable statistics\", alias = \"statistics\")\n\tprivate boolean statisticsEnabled = false;\n\n\tpublic SDRepositoryBeanWithStatistics(Class<T> repoClazz) {\n\t\tthis.repoClazz = repoClazz;\n\t}\n\n\t@Override\n\tpublic void everyHour() {\n\t\tif (statisticsEnabled) {\n\t\t\thandler.everyHour();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void everyMinute() {\n\t\tif (statisticsEnabled) {\n\t\t\thandler.everyMinute();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void everySecond() {\n\t\tif (statisticsEnabled) {\n\t\t\thandler.everySecond();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void getStatistics(String compName, StatisticsList list) {\n\t\tif (handler != null) {\n\t\t\thandler.getStatistics(compName, getName(), list);\n\t\t}\n\t}\n\n\tpublic void setStatisticsEnabled(boolean value) {\n\t\tif (this.statisticsEnabled != value) {\n\t\t\tsynchronized (this) {\n\t\t\t\tif (value) {\n\t\t\t\t\twrapInProxy(super.getRepository());\n\t\t\t\t}\n\t\t\t\tthis.statisticsEnabled = value;\n\t\t\t\tif (!value) {\n\t\t\t\t\trepoProxy = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void wrapInProxy(T repo) {\n\t\thandler = new StatisticsInvocationHandler(getDataSourceName(), repo, repoClazz);\n\t\trepoProxy = (T) Proxy.newProxyInstance(repo.getClass().getClassLoader(), new Class[]{repoClazz}, handler);\n\t}\n\n\t@Override\n\tprotected T getRepository() {\n\t\tif (statisticsEnabled) {\n\t\t\treturn repoProxy;\n\t\t}\n\t\treturn super.getRepository();\n\t}\n\n\t@Override\n\tpublic void setRepository(T repository) {\n\t\tif (statisticsEnabled) {\n\t\t\twrapInProxy(repository);\n\t\t}\n\t\tsuper.setRepository(repository);\n\t}\n\n}"
  },
  {
    "path": "src/main/java/tigase/db/beans/UserRepositoryMDPoolBean.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.beans;\n\nimport tigase.db.UserRepository;\nimport tigase.db.UserRepositoryMDImpl;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.BasicComponent;\nimport tigase.stats.StatisticsCollector;\nimport tigase.stats.StatisticsList;\n\n/**\n * Class implements bean for multi domain pool for user repositories.\n * <br>\n * Created by andrzej on 07.03.2016.\n */\n@Bean(name = \"userRepository\", parent = Kernel.class, exportable = true, active = true)\n@ConfigType({ConfigTypeEnum.DefaultMode, ConfigTypeEnum.SessionManagerMode, ConfigTypeEnum.ConnectionManagersMode,\n\t\t\t ConfigTypeEnum.ComponentMode})\npublic class UserRepositoryMDPoolBean\n\t\textends UserRepositoryMDImpl {\n\n\t@Override\n\tpublic boolean belongsTo(Class<? extends BasicComponent> component) {\n\t\treturn StatisticsCollector.class.isAssignableFrom(component);\n\t}\n\n\t@Override\n\tpublic void getStatistics(String compName, StatisticsList list) {\n\t\tsuper.getStatistics(getName(), list);\n\t}\n\n\t@Override\n\tpublic Class<? extends UserRepositoryConfigBean> getConfigClass() {\n\t\treturn UserRepositoryConfigBean.class;\n\t}\n\n\t@Override\n\tpublic Class<?> getDefaultBeanClass() {\n\t\treturn UserRepositoryConfigBean.class;\n\t}\n\n\tpublic static class UserRepositoryConfigBean\n\t\t\textends AuthUserRepositoryConfigBean<UserRepository, UserRepositoryConfigBean> {\n\n\t\t@Override\n\t\tprotected Class<UserRepository> getRepositoryIfc() {\n\t\t\treturn UserRepository.class;\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/comp/AbstractSDComponentRepositoryBean.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.comp;\n\nimport tigase.db.DBInitException;\nimport tigase.db.DataSource;\nimport tigase.db.TigaseDBException;\nimport tigase.db.beans.SDRepositoryBean;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.Map;\n\n/**\n * Class implements ComponentRepository interfaces and extends SDRepositoryBean and is designed to be based bean used by\n * other classes responsible for loading proper implementation of ComponentRepository depending on used implementation\n * of DataSource.\n * <br>\n * Created by andrzej on 18.03.2016.\n */\npublic abstract class AbstractSDComponentRepositoryBean<Item extends RepositoryItem>\n\t\textends SDRepositoryBean<ComponentRepositoryDataSourceAware<Item, DataSource>>\n\t\timplements ComponentRepository<Item> {\n\n\t@Override\n\tpublic void setRepository(ComponentRepositoryDataSourceAware<Item, DataSource> repository) {\n\t\tComponentRepositoryDataSourceAware<Item, DataSource> oldRepo = getRepository();\n\t\tsuper.setRepository(repository);\n\t\tif (oldRepo != getRepository() && oldRepo != null) {\n\t\t\toldRepo.destroy();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void addRepoChangeListener(RepositoryChangeListenerIfc<Item> repoChangeListener) {\n\t\tgetRepository().addRepoChangeListener(repoChangeListener);\n\t}\n\n\t@Override\n\tpublic void removeRepoChangeListener(RepositoryChangeListenerIfc<Item> repoChangeListener) {\n\t\tgetRepository().removeRepoChangeListener(repoChangeListener);\n\n\t}\n\n\t@Override\n\tpublic void addItem(Item item) throws TigaseDBException {\n\t\tgetRepository().addItem(item);\n\t}\n\n\t@Override\n\tpublic void addItemNoStore(Item item) {\n\t\tgetRepository().addItemNoStore(item);\n\t}\n\n\t@Override\n\tpublic Collection<Item> allItems() throws TigaseDBException {\n\t\treturn getRepository() != null ? getRepository().allItems() : Collections.emptySet();\n\t}\n\n\t@Override\n\tpublic boolean contains(String key) {\n\t\treturn getRepository().contains(key);\n\t}\n\n\t@Override\n\tpublic void destroy() {\n\t\tgetRepository().destroy();\n\t}\n\n\t@Deprecated\n\t@Override\n\tpublic void getDefaults(Map<String, Object> defs, Map<String, Object> params) {\n\t\t//repo.getDefaults(defs, params);\n\t}\n\n\t@Override\n\tpublic Item getItem(String key) {\n\t\treturn getRepository().getItem(key);\n\t}\n\n\t@Override\n\tpublic Item getItemInstance() {\n\t\treturn getRepository().getItemInstance();\n\t}\n\n\t@Override\n\tpublic void reload() throws TigaseDBException {\n\t\tgetRepository().reload();\n\t}\n\n\t@Override\n\tpublic void removeItem(String key) throws TigaseDBException {\n\t\tgetRepository().removeItem(key);\n\t}\n\n\t@Override\n\tpublic void removeItemNoStore(String key) {\n\t\tgetRepository().removeItemNoStore(key);\n\t}\n\n\t@Deprecated\n\t@Override\n\tpublic void setProperties(Map<String, Object> properties) {\n\t\t//repo.setProperties(properties);\n\t}\n\n\t@Override\n\tpublic int size() {\n\t\treturn getRepository().size();\n\t}\n\n\t@Override\n\tpublic void store() throws TigaseDBException {\n\t\tgetRepository().store();\n\t}\n\n\t@Override\n\tpublic String validateItem(Item item) {\n\t\treturn getRepository().validateItem(item);\n\t}\n\n\t@Override\n\tpublic void setAutoloadTimer(long delay) {\n\t\t//repo.setAutoloadTimer(delay);\n\t}\n\n\t@Override\n\tpublic Iterator<Item> iterator() {\n\t\treturn getRepository().iterator();\n\t}\n\n\t@Override\n\tpublic void initRepository(String resource_uri, Map<String, String> params) throws DBInitException {\n\t\tgetRepository().initRepository(resource_uri, params);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tCollection<Item> items;\n\t\ttry {\n\t\t\titems = allItems();\n\t\t} catch (TigaseDBException ex) {\n\t\t\titems = Collections.emptyList();\n\t\t\t// don't need to log anything\n\t\t}\n\t\tfinal StringBuilder sb = new StringBuilder(\"RepoItems\");\n\t\tif (!items.isEmpty()) {\n\t\t\tsb.append(\", size=\").append(items.size());\n\t\t\tvar itemsString = items.toString();\n\t\t\tsb.append(\", items=\").append(itemsString.substring(0, Math.min(itemsString.length(), 1024)));\n\t\t\tif (itemsString.length() > 1024) {\n\t\t\t\tsb.append(\"...\");\n\t\t\t}\n\t\t}\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/comp/ComponentRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.comp;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.Repository;\nimport tigase.db.TigaseDBException;\n\nimport java.util.Collection;\nimport java.util.Map;\n\n/**\n * A convenience interface for a unified access to component specific repository data. This is not intended to keep huge\n * number of elements. Rather then it is more like for storing Component dynamic configuration data. In simple cases\n * this data can be stored in configuration file, in more complex cases it can be a database represented by\n * UserRepository or even something else. <br> The repository is intended to store elements of a single type only. Each\n * element is identified by a unique key. All elements are cached in memory for a fast retrieval so this kind of\n * repository is recommended for small data only when you need very fast and efficient access to all the\n * information.<br> Some implementations however may behave differently and not cache all the repository items in\n * memory. <br> Created: Oct 3, 2009 1:46:25 PM\n *\n * @param <Item> An element stored in the component repository.\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface ComponentRepository<Item extends RepositoryItem>\n\t\textends Iterable<Item>, Repository {\n\n\tpublic static final String COMP_REPO_BIND = \"comp_repo\";\n\n\t/**\n\t * Adds a listener for repository Item change.\n\t */\n\tvoid addRepoChangeListener(RepositoryChangeListenerIfc<Item> repoChangeListener);\n\n\t/**\n\t * Removes a listener for repository Item change.\n\t */\n\tvoid removeRepoChangeListener(RepositoryChangeListenerIfc<Item> repoChangeListener);\n\n\t/**\n\t * The method adds a new or updates existing Item in the repository. It needs to have all fields set correctly.\n\t * After this method call is finished a new added item must be available in the component repository. The method\n\t * adds the item to memory cache and permanent storage.\n\t *\n\t * @param item a <code>Item</code> with all it's configuration parameters.\n\t */\n\tvoid addItem(Item item) throws TigaseDBException;\n\n\t/**\n\t * The method adds a new or updates existing Item. It needs to have all fields set correctly. After this method call\n\t * is finished a new added item must be available in the component repository. The method adds the item to memory\n\t * cache but not to a permanent storage.\n\t *\n\t * @param item a <code>Item</code> with all it's configuration parameters.\n\t */\n\tvoid addItemNoStore(Item item);\n\n\t/**\n\t * Returns a collection with all items stored in the repository.\n\t */\n\tCollection<Item> allItems() throws TigaseDBException;\n\n\t/**\n\t * The method checks whether the item is stored in the repository.\n\t *\n\t * @param key a <code>String</code> with key to search for.\n\t *\n\t * @return a <code>boolean</code> value <code>true</code> if the item exists in the repository or <code>false</code>\n\t * of it does not.\n\t */\n\tboolean contains(String key);\n\n\t/**\n\t * Method destroys this instance of ComponentRepository releasing resources allocated for this instance of\n\t * ComponentRepository if possible\n\t */\n\tvoid destroy();\n\n\t/**\n\t * The method is called to obtain default configuration settings if there are any for this repository implementation\n\t * The configuration settings are implementation dependent and there are no defaults set by the server. Default\n\t * settings returned by this method are then saved in the configuration file and presented to the admin for further\n\t * adjustments.\n\t *\n\t * @param defs is a <code>Map</code> collection where all repository configuration defaults must be put.\n\t * @param params is a <code>Map</code> collection with some preset properties for the server. These settings can be\n\t * used to adjust repository defaults, for example they can contain database connection URL or initial list of\n\t * virtual domains.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tvoid getDefaults(Map<String, Object> defs, Map<String, Object> params);\n\n\t/**\n\t * The method returns all item configuration parameters for a key or <code>null</code> if the item does not exist in\n\t * the repository.\n\t *\n\t * @param key a <code>String</code> with item identifier to search for.\n\t *\n\t * @return a <code>Item</code> for a given key or <code>null</code> if the item is not in the repository.\n\t */\n\tItem getItem(String key);\n\n\t/**\n\t * Creates a new, uninitialized instance of the repository Item.\n\t *\n\t * @return a new, uninitialized instance of the repository Item.\n\t */\n\tItem getItemInstance();\n\n\t/**\n\t * This method is called to reload items from the database or other permanent storage. It is possible that items\n\t * list is modified externally by third-party system. When all modifications are done this method is called to\n\t * refresh the class cache. Whether the implementation load whole list or just last modifications is implementation\n\t * dependent.\n\t */\n\tvoid reload() throws TigaseDBException;\n\n\t/**\n\t * The method is called to remove given Item from the memory cache and permanent storage. After this method is\n\t * completed the item should no longer be available in the component repository.\n\t *\n\t * @param key a <code>String</code> with domain name to remove.\n\t */\n\tvoid removeItem(String key) throws TigaseDBException;\n\n\tvoid removeItemNoStore(String key);\n\n\t/**\n\t * The method is called to set configuration for this repository implementation. The configuration is repository\n\t * implementation dependent. There are no default settings for the repository.\n\t *\n\t * @param properties a <code>Map</code> with configuration settings. Content of this <code>Map</code> must not be\n\t * modified. This read-only collection.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tvoid setProperties(Map<String, Object> properties);\n\n\t/**\n\t * The method returns number of items in the repository.\n\t *\n\t * @return an <code>int</code> value with number of items in the repository.\n\t */\n\tint size();\n\n\t/**\n\t * The method is called to store all data in the database. It is used when the repository has been changed in some\n\t * way and the changes have to be put to a permanent storage for later retrieval.\n\t */\n\tvoid store() throws TigaseDBException;\n\n\t/**\n\t * Performs Item validation to check whether it meets the repository policy. If validation is successful the method\n\t * returns <code>null</code>, otherwise it returns an error description.\n\t *\n\t * @param item is an <code>Item</code> object to perform validation checking upon.\n\t *\n\t * @return <code>null</code> on success and an error message otherwise.\n\t */\n\tString validateItem(Item item);\n\n\t/**\n\t * Sets autoload task to periodically reload data from database.\n\t *\n\t * @param delay in seconds between each database reload.\n\t */\n\tvoid setAutoloadTimer(long delay);\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/db/comp/ComponentRepositoryDataSourceAware.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.comp;\n\nimport tigase.db.DataSource;\nimport tigase.db.DataSourceAware;\n\n/**\n * Interface should be implemented by <code>ComponentRepository</code> which are using <code>DataSource</code> to\n * load/store entries.\n * <br>\n * Interface created mainly for use by <code>AbstractSDComponentRepositoryBean</code>.\n * <br>\n * Created by andrzej on 18.03.2016.\n */\npublic interface ComponentRepositoryDataSourceAware<Item extends RepositoryItem, DS extends DataSource>\n\t\textends ComponentRepository<Item>, DataSourceAware<DS> {\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/comp/ConfigRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.comp;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.kernel.beans.config.ConfigField;\n\nimport java.util.*;\nimport java.util.concurrent.ConcurrentSkipListMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Oct 3, 2009 2:58:41 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\npublic abstract class ConfigRepository<Item extends RepositoryItem>\n\t\timplements ComponentRepository<Item>, Initializable, UnregisterAware {\n\n\tprivate static final Logger log = Logger.getLogger(ConfigRepository.class.getName());\n\n\t@ConfigField(desc = \"Automatic items load interval (in seconds)\", alias = \"repo-autoreload-interval\")\n\tprotected long autoReloadInterval = 0;\n\tprotected Map<String, Item> items = new ConcurrentSkipListMap<String, Item>(String.CASE_INSENSITIVE_ORDER);\n\tprotected int itemsHash = 0;\n\n\tprivate Timer autoLoadTimer = null;\n\tprivate boolean initialized = false;\n\tprivate RepositoryChangeListenerIfc<Item> repoChangeList = null;\n\n\tpublic ConfigRepository() {\n\t\tString propKey = getPropertyKey();\n\t\tif (propKey != null) {\n\t\t\tif (propKey.startsWith(\"--\")) {\n\t\t\t\tpropKey = propKey.substring(2);\n\t\t\t}\n\n\t\t\tString value = System.getProperty(propKey);\n\t\t\tString[] items = null;\n\t\t\tif (value != null) {\n\t\t\t\titems = value.split(\",\");\n\t\t\t\tfor (int i = 0; i < items.length; i++) {\n\t\t\t\t\titems[i] = items[i].trim();\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.setItemsOld(items);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setAutoloadTimer(long delay) {\n\t\tlong interval = delay * 1000;\n\n\t\tif (autoLoadTimer != null) {\n\t\t\tautoLoadTimer.cancel();\n\t\t\tautoLoadTimer = null;\n\t\t}\n\t\tif (interval > 0) {\n\t\t\tautoLoadTimer = new Timer(getConfigKey(), true);\n\t\t\tautoLoadTimer.schedule(new TimerTask() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\ttry {\n\t\t\t\t\t\treload();\n\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\tlog.log(Level.SEVERE, \"exception during reload of config repository items\", ex);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}, 5 * 1000, interval);\n\t\t}\n\t}\n\n\tpublic void setAutoReloadInterval(long autoLoadInterval) {\n\t\tthis.autoReloadInterval = autoLoadInterval;\n\t\tsetAutoloadTimer(autoLoadInterval);\n\t}\n\n\t@Override\n\tpublic void addRepoChangeListener(RepositoryChangeListenerIfc<Item> repoChangeListener) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Adding new repository listener: {0}\", repoChangeListener);\n\t\t}\n\t\tthis.repoChangeList = repoChangeListener;\n\t}\n\n\t@Override\n\tpublic void removeRepoChangeListener(RepositoryChangeListenerIfc<Item> repoChangeListener) {\n\t\tthis.repoChangeList = null;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tfinal StringBuilder sb = new StringBuilder(\"RepoItems\");\n\t\tsb.append(\", size=\").append(items.size());\n\t\tfinal String itemsString = items.keySet().toString();\n\t\tsb.append(\", items=\").append(itemsString.substring(0, Math.min(itemsString.length(), 1024)));\n\t\tif (itemsString.length() > 1024) {\n\t\t\tsb.append(\"...\");\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic abstract String getConfigKey();\n\n\tpublic Item[] getDefaultItems() {\n\t\treturn null;\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic String[] getDefaultPropetyItems() {\n\t\treturn null;\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic abstract String getPropertyKey();\n\n\t@Override\n\tpublic void addItem(Item item) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Adding item: {0}\", item);\n\t\t}\n\t\taddItemNoStore(item);\n\n\t\tstore();\n\t}\n\n\t@Override\n\tpublic void addItemNoStore(Item item) {\n\t\tItem old = items.put(item.getKey(), item);\n\n\t\tif (repoChangeList != null) {\n\t\t\tif (old == null) {\n\t\t\t\tlog.log(Level.CONFIG, \"Calling itemAdded for: {0}\", item);\n\t\t\t\trepoChangeList.itemAdded(item);\n\t\t\t} else {\n\t\t\t\tif (itemChanged(old, item)) {\n\t\t\t\t\tlog.log(Level.CONFIG, \"Calling itemUpadted for: {0}\", item);\n\t\t\t\t\trepoChangeList.itemUpdated(item);\n\t\t\t\t} else {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Not calling itemUpadted for: {0}, item unchanged.\", item);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"No repoChangeListener for: {0}\", item);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic boolean itemChanged(Item oldItem, Item newItem) {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic Collection<Item> allItems() {\n\t\treturn items.values();\n\t}\n\n\t@Override\n\tpublic boolean contains(String key) {\n\t\treturn items.keySet().contains(key);\n\t}\n\n\t@Deprecated\n\t@Override\n\tpublic void getDefaults(Map<String, Object> defs, Map<String, Object> params) {\n\t\tinitItemsMap();\n\t\tString[] items_arr = getDefaultPropetyItems();\n\n\t\tif (params.get(getPropertyKey()) != null) {\n\t\t\titems_arr = ((String) params.get(getPropertyKey())).split(\",\");\n\t\t\tfor (int i = 0; i < items_arr.length; i++) {\n\t\t\t\titems_arr[i] = items_arr[i].trim();\n\t\t\t}\n\t\t}\n\t\tdefs.put(getConfigKey(), items_arr);\n\t}\n\n\t@Override\n\tpublic Item getItem(String key) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Getting item: {0} of items: {1}\", new Object[]{key, items.keySet()});\n\t\t}\n\n\t\treturn items.get(key);\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic String[] getItemsOld() {\n\t\tList<String> itemsStrs = new ArrayList<>();\n\t\tfor (Item item : items.values()) {\n\t\t\titemsStrs.add(item.toPropertyString());\n\t\t}\n\t\treturn itemsStrs.toArray(new String[itemsStrs.size()]);\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic void setItemsOld(String[] items_arr) {\n\t\tif (items_arr != null) {\n\t\t\tfor (String it : items_arr) {\n\t\t\t\tlog.log(Level.CONFIG, \"Loading config item: {0}\", it);\n\n\t\t\t\tItem item = getItemInstance();\n\n\t\t\t\titem.initFromPropertyString(it);\n\t\t\t\tif (!items.containsKey(item.getKey())) {\n\t\t\t\t\taddItem(item);\n\t\t\t\t\tlog.log(Level.CONFIG, \"Loaded config item: {0}\", item);\n\t\t\t\t} else {\n\t\t\t\t\tlog.log(Level.CONFIG, \"Config item already loaded, skipping: {0}\", item);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic Iterator<Item> iterator() {\n\t\treturn items.values().iterator();\n\t}\n\n\t@Override\n\tpublic void reload() {\n\t}\n\n\t@Override\n\tpublic void removeItem(String key) {\n\t\tItem item = items.remove(key);\n\n\t\tif (item != null) {\n\t\t\tstore();\n\t\t\tif (repoChangeList != null) {\n\t\t\t\trepoChangeList.itemRemoved(item);\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Removing item: {0}\", item);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void removeItemNoStore(String key) {\n\t\tItem item = items.remove(key);\n\n\t\tif (item != null) {\n\t\t\tif (repoChangeList != null) {\n\t\t\t\trepoChangeList.itemRemoved(item);\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Removing item: {0}\", item);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Deprecated\n\t@Override\n\tpublic void setProperties(Map<String, Object> properties) {\n\t\tinitItemsMap();\n\t\tString[] items_arr = (String[]) properties.get(getConfigKey());\n\n\t\tif ((items_arr != null) && (items_arr.length > 0)) {\n\t\t\tsetItemsOld(items_arr);\n\t\t} else {\n\t\t\tlog.warning(\"Items list is not set in the configuration file!!\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic int size() {\n\t\treturn items.size();\n\t}\n\n\t@Override\n\tpublic void store() {\n\t}\n\n\t@Override\n\tpublic String validateItem(Item item) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\tsetAutoloadTimer(0);\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tthis.initialized = true;\n\t\tif (items.isEmpty()) {\n\t\t\tString[] itemsStr = getDefaultPropetyItems();\n\t\t\tif (itemsStr != null) {\n\t\t\t\tsetItemsOld(itemsStr);\n\t\t\t} else {\n\t\t\t\tOptional.ofNullable(getDefaultItems()).ifPresent(items -> Arrays.stream(items).forEach(this::addItem));\n\t\t\t}\n\t\t} else {\n\t\t\tstore();\n\t\t}\n\t\tsetAutoReloadInterval(autoReloadInterval);\n\t}\n\n\t/**\n\t * Method create instance of items Map. By overriding it it's possible to change implementation and it's settings.\n\t */\n\tprotected void initItemsMap() {\n\t\tif (null == items) {\n\t\t\titems = new ConcurrentSkipListMap<String, Item>(String.CASE_INSENSITIVE_ORDER);\n\t\t}\n\t}\n\n\tprotected boolean isInitialized() {\n\t\treturn initialized;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/comp/RepositoryChangeListenerIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.comp;\n\n/**\n * @author Artur Hefczyc Created May 23, 2011\n */\npublic interface RepositoryChangeListenerIfc<Item extends RepositoryItem> {\n\n\tvoid itemAdded(Item item);\n\n\tvoid itemUpdated(Item item);\n\n\tvoid itemRemoved(Item item);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/comp/RepositoryItem.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.comp;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\n\n/**\n * The interface defines a contract for a repository item handled by ComponentRepository implementation. Created: Oct 3,\n * 2009 2:35:58 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\npublic interface RepositoryItem {\n\n\t/**\n\t * The method is used for handling ad-hoc commands. The 'empty' ad-hoc command packet is provided and the Item\n\t * should fill it with fields for the user.\n\t *\n\t * @param packet with empty ad-hoc command to fill with fields\n\t */\n\tvoid addCommandFields(Packet packet);\n\n\t/**\n\t * Returns an array with the Item administrators, that is people IDs who can manage, configure and control less\n\t * critical elements of the Item, like changing less critical configuration settings. Administrators cannot remove\n\t * the Item or change the owner or add/remove administrators.\n\t *\n\t * @return an array with the Item administrators IDs.\n\t */\n\tString[] getAdmins();\n\n\t/**\n\t * Returns an array with the Item administrators, that is people IDs who can manage, configure and control less\n\t * critical elements of the Item, like changing less critical configuration settings. Administrators cannot remove\n\t * the Item or change the owner or add/remove administrators.\n\t *\n\t * @param admins is an array with the Item administrators IDs to set for the Item.\n\t */\n\tvoid setAdmins(String[] admins);\n\n\t/**\n\t * Returns a unique key for the item in the repository. All items are stored in a memory cache which is a Map. And\n\t * the key returned by this method is the item identifier in the Map.\n\t *\n\t * @return an Item key.\n\t */\n\tString getKey();\n\n\t/**\n\t * Returns the owner ID of the item.  This is used for a management to allow fine tuned service administration with\n\t * roles assigned to specific elements and items. Normally only owner can perform some critical actions like\n\t * removing the item, managing item administrators or changing owner.<br> There can be only one Item owner.\n\t *\n\t * @return an ID of the Item owner.\n\t */\n\tString getOwner();\n\n\t/**\n\t * Set the Item owner.  This is used for a management to allow fine tuned service administration with roles assigned\n\t * to specific elements and items. Normally only owner can perform some critical actions like removing the item,\n\t * managing item administrators or changing owner.<br> There can be only one Item owner.\n\t *\n\t * @param owner is the Item owner ID.\n\t */\n\tvoid setOwner(String owner);\n\n\t/**\n\t * The method used for handling ad-hoc commands. After a user fills all given field the ad-hoc command packet is\n\t * passed back to the item to initialize it with data. Similar method to initFromElement(), but the data source is\n\t * different.\n\t *\n\t * @param packet with ad-hoc command filled by the user.\n\t */\n\tvoid initFromCommand(Packet packet);\n\n\t/**\n\t * The item can be also initialized from a more complex repositories: XML repository or SQL database. In such a case\n\t * more complex representation is prefered, possibly carrying more information about the item. The method is called\n\t * to initialize the item with a data parsed from an XML representation of the repository.\n\t *\n\t * @param elem XML Element with all the item initialization data.\n\t */\n\tvoid initFromElement(Element elem);\n\n\t/**\n\t * The item can be initialized based on the data loaded from a configuration file. In such a case the item\n\t * representation is usually very simplified as a list of parameters separated by a marker. Please note, usually\n\t * each item is separated from another with a comma, therefore do not use a comma in the item property string.\n\t * Double collon is commonly used alternative.\n\t *\n\t * @param propString is a property string to initialize the RepositoryItem.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tvoid initFromPropertyString(String propString);\n\n\t/**\n\t * The method checks whether the given id is one of the administrators set for the Item.\n\t *\n\t * @param id is an ID of a person for which we check access permissions.\n\t *\n\t * @return true of the given ID is on a list of administrators and false otherwise.\n\t */\n\tboolean isAdmin(String id);\n\n\t/**\n\t * The method checks whether the person with given ID is the Item owner.\n\t *\n\t * @param id is an ID of a person for whom we check access permissions.\n\t *\n\t * @return true of the given ID is on the Item owner and false otherwise.\n\t */\n\tboolean isOwner(String id);\n\n\t/**\n\t * Item data can be stored in a more complex form than a simple property string. The XML Element can contain much\n\t * more detailed information about the element than the simplified form and is used to store the repository item in\n\t * more advanced repositories then just property file. XML repository or SQL database can keep many records for\n\t * repository item with as much detailed information as needed.\n\t *\n\t * @return an XML Element with all the item initialization data.\n\t */\n\tElement toElement();\n\n\t/**\n\t * The item can be initialized based on the data loaded from a configuration file. In such a case the item\n\t * representation is usually very simplified as a list of parameters separated by a marker. Please note, usually\n\t * each item is separated from another with a comma, therefore do not use a comma in the item property string.\n\t * Double collon is commonly used alternative.\n\t *\n\t * @return a property string representing the repository item in a simplified form.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tString toPropertyString();\n\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/db/comp/RepositoryItemAbstract.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.comp;\n\nimport tigase.kernel.BeanUtils;\nimport tigase.kernel.DefaultTypesConverter;\nimport tigase.kernel.TypesConverter;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Command;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\n\nimport java.lang.reflect.Field;\nimport java.util.Arrays;\nimport java.util.stream.Stream;\n\n/**\n * Created: Sep 23, 2010 6:53:14 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic abstract class RepositoryItemAbstract\n\t\timplements RepositoryItem {\n\n\tpublic static final String ADMINS_ATT = \"admins\";\n\n\tpublic static final String ADMINS_LABEL = \"Administrators\";\n\n\tpublic static final String OWNER_ATT = \"owner\";\n\n\tpublic static final String OWNER_LABEL = \"Owner\";\n\n\tprivate static final TypesConverter typesConverter = new DefaultTypesConverter();\n\n\tprivate String[] admins = null;\n\tprivate String owner = null;\n\n\tpublic abstract String getElemName();\n\n\tprotected abstract void setKey(String key);\n\n\t@Override\n\tpublic void addCommandFields(Packet packet) {\n\t\tCommand.addFieldValue(packet, OWNER_LABEL,\n\t\t\t\t\t\t\t  (owner != null) ? owner : packet.getStanzaTo().getBareJID().toString());\n\t\tCommand.addFieldValue(packet, ADMINS_LABEL, adminsToString(admins));\n\t}\n\n\t@Override\n\tpublic String[] getAdmins() {\n\t\treturn admins;\n\t}\n\n\t@Override\n\tpublic void setAdmins(String[] admins) {\n\t\tthis.admins = admins;\n\t}\n\n\t@Override\n\tpublic String getOwner() {\n\t\treturn owner;\n\t}\n\n\t@Override\n\tpublic void setOwner(String owner) {\n\t\tthis.owner = owner;\n\t}\n\n\t@Override\n\tpublic void initFromCommand(Packet packet) {\n\t\towner = Command.getFieldValue(packet, OWNER_LABEL);\n\t\tif ((owner == null) || owner.trim().isEmpty()) {\n\t\t\towner = packet.getStanzaFrom().getBareJID().toString();\n\t\t}\n\t\tadmins = adminsFromString(Command.getFieldValue(packet, ADMINS_LABEL));\n\t}\n\n\t@Override\n\tpublic void initFromElement(Element elem) {\n\t\towner = elem.getAttributeStaticStr(OWNER_ATT);\n\t\tadmins = adminsFromString(elem.getAttributeStaticStr(ADMINS_ATT));\n\t}\n\n\t@Override\n\tpublic boolean isAdmin(String id) {\n\t\tif (admins == null) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (String admin : admins) {\n\t\t\tif (admin.equals(id)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean isOwner(String id) {\n\t\treturn ((owner == null) ? false : owner.equals(id));\n\t}\n\n\t@Override\n\tpublic Element toElement() {\n\t\tElement elem = new Element(getElemName());\n\n\t\tif (owner != null) {\n\t\t\telem.addAttribute(OWNER_ATT, owner);\n\t\t}\n\t\tif (admins != null) {\n\t\t\telem.addAttribute(ADMINS_ATT, adminsToString(admins));\n\t\t}\n\n\t\treturn elem;\n\t}\n\n\tprivate String[] adminsFromString(String admins_m) {\n\t\tString[] result = null;\n\n\t\tif ((admins_m != null) && (admins_m.trim().length() > 0)) {\n\t\t\tString[] tmp = admins_m.split(\",\");\n\n\t\t\tresult = new String[tmp.length];\n\t\t\tfor (int i = 0; i < tmp.length; i++) {\n\t\t\t\tresult[i] = tmp[i].trim();\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate String adminsToString(String[] admins_m) {\n\t\tStringBuilder sb = new StringBuilder(100);\n\n\t\tif ((admins_m != null) && (admins_m.length > 0)) {\n\t\t\tfor (String adm : admins_m) {\n\t\t\t\tif (sb.length() == 0) {\n\t\t\t\t\tsb.append(adm);\n\t\t\t\t} else {\n\t\t\t\t\tsb.append(',').append(adm);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn sb.toString();\n\t}\n\n\tprivate Stream<Field> streamConfigFields() {\n\t\treturn Arrays.stream(BeanUtils.getAllFields(getClass()))\n\t\t\t\t.filter(field -> field.isAnnotationPresent(ConfigField.class));\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/db/comp/UserRepoRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.comp;\n\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserExistsException;\nimport tigase.db.UserRepository;\nimport tigase.kernel.beans.Inject;\nimport tigase.xml.DomBuilderHandler;\nimport tigase.xml.Element;\nimport tigase.xml.SimpleParser;\nimport tigase.xml.SingletonFactory;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.HashSet;\nimport java.util.Queue;\nimport java.util.Set;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Oct 3, 2009 3:55:27 PM\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic abstract class UserRepoRepository<Item extends RepositoryItem>\n\t\textends ConfigRepository<Item> {\n\n\tpublic static final String REPO_CLASS_PROP_KEY = \"repo-class\";\n\n\tpublic static final String REPO_URI_PROP_KEY = \"repo-uri\";\n\tprivate static final Logger log = Logger.getLogger(UserRepoRepository.class.getName());\n\n\tprivate String items_list_pkey = \"items-lists\";\n\t@Inject\n\tprotected UserRepository repo = null;\n\n\tpublic abstract BareJID getRepoUser();\n\n\tpublic String getItemsListPKey() {\n\t\treturn items_list_pkey;\n\t}\n\n\t@Override\n\tpublic void reload() {\n\t\tint hashCode = 0;\n\t\tsuper.reload();\n\t\ttry {\n\n\t\t\t// It is now time to load all Items' settings from the database:\n\t\t\tif (repo == null) {\n\t\t\t\tlog.log(Level.SEVERE, \"Repository is not initialised - skipping reload\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tString items_list = repo.getData(getRepoUser(), getItemsListPKey());\n\n\t\t\tif (items_list != null) {\n\t\t\t\thashCode = items_list.hashCode();\n\n\t\t\t\tif (hashCode != itemsHash) {\n\t\t\t\t\tSet<String> oldKeys = new HashSet<>(super.items.keySet());\n\n\t\t\t\t\tif (!items_list.isEmpty()) {\n\t\t\t\t\t\tDomBuilderHandler domHandler = new DomBuilderHandler();\n\t\t\t\t\t\tSimpleParser parser = SingletonFactory.getParserInstance();\n\n\t\t\t\t\t\tparser.parse(domHandler, items_list.toCharArray(), 0, items_list.length());\n\n\t\t\t\t\t\tQueue<Element> elems = domHandler.getParsedElements();\n\n\t\t\t\t\t\tif ((elems != null) && (elems.size() > 0)) {\n\t\t\t\t\t\t\tfor (Element elem : elems) {\n\t\t\t\t\t\t\t\tItem item = getItemInstance();\n\n\t\t\t\t\t\t\t\titem.initFromElement(elem);\n\t\t\t\t\t\t\t\taddItemNoStore(item);\n\t\t\t\t\t\t\t\toldKeys.remove(item.getKey());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tlog.log(Level.CONFIG, \"All loaded items: {0}\", items);\n\t\t\t\t\t}\n\t\t\t\t\titemsHash = hashCode;\n\n\t\t\t\t\tfor (String key : oldKeys) {\n\t\t\t\t\t\tremoveItemNoStore(key);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (hashCode != itemsHash) {\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (TigaseDBException ex) {\n\t\t\tlog.log(Level.SEVERE, \"Problem with loading items list from the database.\", ex);\n\t\t}\n\t}\n\n\tpublic void setRepo(UserRepository userRepository) {\n\t\tthis.repo = userRepository;\n\t\ttry {\n\t\t\tif (!repo.userExists(getRepoUser())) {\n\t\t\t\trepo.addUser(getRepoUser());\n\t\t\t}\n\t\t} catch (UserExistsException e) {\n\n\t\t\t// This is expected when the Items repository has been already running on\n\t\t\t// this databaseso and can be ignored.\n\t\t} catch (Exception e) {\n\n\t\t\t// This is not expected so let's signal an error:\n\t\t\tlog.log(Level.SEVERE, \"Problem with adding '\" + getRepoUser() + \"' user to the database\", e);\n\t\t}\n\n\t\treload();\n\t}\n\n\t@Override\n\tpublic void store() {\n\t\tsuper.store();\n\t\tif (repo != null && isInitialized()) {\n\t\t\tStringBuilder sb = new StringBuilder();\n\n\t\t\tfor (Item item : items.values()) {\n\t\t\t\tsb.append(item.toElement().toString());\n\t\t\t}\n\t\t\ttry {\n\t\t\t\trepo.setData(getRepoUser(), getItemsListPKey(), sb.toString());\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.SEVERE, \"Error storing items list in the repository\", e);\n\t\t\t}\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/db/derby/MsgBroadcastRepositoryStoredProcedures.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.derby;\n\nimport tigase.util.Algorithms;\n\nimport java.security.NoSuchAlgorithmException;\nimport java.sql.*;\n\n/**\n * Created by andrzej on 24.03.2017.\n */\npublic class MsgBroadcastRepositoryStoredProcedures {\n\n\tpublic static void addMessage(String msgId, Timestamp expired, String msg) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\tResultSet rs = null;\n\t\ttry {\n\t\t\tPreparedStatement stmt = conn.prepareStatement(\"select id from tig_broadcast_messages where id = ?\");\n\t\t\tstmt.setString(1, msgId);\n\t\t\trs = stmt.executeQuery();\n\n\t\t\tif (rs.next()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\trs.close();\n\n\t\t\tstmt = conn.prepareStatement(\"insert into tig_broadcast_messages (id, expired, msg) values (?,?,?)\");\n\t\t\tstmt.setString(1, msgId);\n\t\t\tstmt.setTimestamp(2, expired);\n\t\t\tstmt.setString(3, msg);\n\t\t\tstmt.executeUpdate();\n\t\t} finally {\n\t\t\tif (rs != null) {\n\t\t\t\trs.close();\n\t\t\t}\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void addMessageRecipient(String msgId, String jid) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\tResultSet rs = null;\n\t\ttry {\n\t\t\tString jidSha1 = Algorithms.hexDigest(jid.toString(), \"\", \"SHA\");\n\t\t\tPreparedStatement stmt = conn.prepareStatement(\"select jid_id from tig_broadcast_jids where jid_sha1 = ?\");\n\t\t\tstmt.setString(1, jidSha1);\n\t\t\trs = stmt.executeQuery();\n\t\t\tlong jidId = -1;\n\t\t\tif (rs.next()) {\n\t\t\t\tjidId = rs.getLong(1);\n\t\t\t}\n\t\t\trs.close();\n\n\t\t\tif (jidId < 0) {\n\t\t\t\tstmt = conn.prepareStatement(\"insert into tig_broadcast_jids (jid, jid_sha1) values (?,?)\");\n\t\t\t\tstmt.setString(1, jid);\n\t\t\t\tstmt.setString(2, jidSha1);\n\t\t\t\tstmt.executeUpdate();\n\t\t\t\trs = stmt.getGeneratedKeys();\n\t\t\t\tif (rs.next()) {\n\t\t\t\t\tjidId = rs.getLong(1);\n\t\t\t\t}\n\t\t\t\trs.close();\n\t\t\t}\n\n\t\t\tstmt = conn.prepareStatement(\"select 1 from tig_broadcast_recipients where \tmsg_id = ? and jid_id = ?\");\n\t\t\tstmt.setString(1, msgId);\n\t\t\tstmt.setLong(2, jidId);\n\t\t\trs = stmt.executeQuery();\n\t\t\tif (rs.next()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\trs.close();\n\n\t\t\tstmt = conn.prepareStatement(\"insert into tig_broadcast_recipients (msg_id, jid_id) values (?,?)\");\n\t\t\tstmt.setString(1, msgId);\n\t\t\tstmt.setLong(2, jidId);\n\n\t\t\tstmt.executeUpdate();\n\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\tthrow new SQLException(e);\n\t\t} finally {\n\t\t\tif (rs != null) {\n\t\t\t\trs.close();\n\t\t\t}\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void getMessageRecipients(String msgId, ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement stmt = conn.prepareStatement(\n\t\t\t\t\t\"select j.jid from tig_broadcast_recipients r inner join tig_broadcast_jids j on j.jid_id = r.jid_id where r.msg_id = ?\");\n\t\t\tstmt.setString(1, msgId);\n\t\t\tdata[0] = stmt.executeQuery();\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void getMessages(Timestamp expired, ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement stmt = conn.prepareStatement(\n\t\t\t\t\t\"select id, expired, msg from tig_broadcast_messages where expired >= ?\");\n\t\t\tstmt.setTimestamp(1, expired);\n\t\t\tdata[0] = stmt.executeQuery();\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/derby/MsgRepositoryStoredProcedures.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.derby;\n\nimport tigase.util.Algorithms;\n\nimport java.nio.charset.Charset;\nimport java.security.NoSuchAlgorithmException;\nimport java.sql.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created by andrzej on 22.03.2017.\n */\npublic class MsgRepositoryStoredProcedures {\n\n\tprivate static final Logger log = Logger.getLogger(StoredProcedures.class.getName());\n\n\tprivate static final Charset UTF8 = Charset.forName(\"UTF-8\");\n\n\tpublic static void addMessage(String receiver, String sender, Integer type, Timestamp ts, String message,\n\t\t\t\t\t\t\t\t  Timestamp expired, Long limit, ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tString receiverSha1 = Algorithms.hexDigest(receiver.toString(), \"\", \"SHA\");\n\t\t\tString senderSha1 = Algorithms.hexDigest(sender.toString(), \"\", \"SHA\");\n\t\t\tif (limit != 0) {\n\t\t\t\tPreparedStatement stmt = conn.prepareStatement(\n\t\t\t\t\t\t\"select count(1) from tig_offline_messages where receiver_sha1 = ? and sender_sha1 = ?\");\n\t\t\t\tstmt.setString(1, receiverSha1);\n\t\t\t\tstmt.setString(2, senderSha1);\n\t\t\t\tResultSet rs = stmt.executeQuery();\n\t\t\t\tif (rs.next()) {\n\t\t\t\t\tlong count = rs.getLong(1);\n\t\t\t\t\tif (count >= limit) {\n\t\t\t\t\t\trs.close();\n\t\t\t\t\t\tdata[0] = conn.createStatement().executeQuery(\"select 1 from sysibm.sysdummy1 where 1=0\");\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\trs.close();\n\t\t\t}\n\n\t\t\tPreparedStatement stmt = conn.prepareStatement(\n\t\t\t\t\t\"insert into tig_offline_messages (receiver, receiver_sha1, sender, sender_sha1, msg_type, ts, message, expired ) \" +\n\t\t\t\t\t\t\t\"values (?, ?, ?, ?, ?, ?, ?, ?)\", Statement.RETURN_GENERATED_KEYS);\n\t\t\tstmt.setString(1, receiver.toString());\n\t\t\tstmt.setString(2, receiverSha1);\n\t\t\tstmt.setString(3, sender.toString());\n\t\t\tstmt.setString(4, senderSha1);\n\t\t\tstmt.setInt(5, type.intValue());\n\t\t\tstmt.setTimestamp(6, ts);\n\t\t\tstmt.setString(7, message);\n\t\t\tif (expired == null) {\n\t\t\t\tstmt.setNull(8, Types.TIMESTAMP);\n\t\t\t} else {\n\t\t\t\tstmt.setTimestamp(8, expired);\n\t\t\t}\n\n\t\t\tstmt.execute();\n\t\t\tdata[0] = stmt.getGeneratedKeys();\n\n\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\tthrow new SQLException(e);\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void deleteMessage(Long msgId) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement stmt = conn.prepareStatement(\"delete from tig_offline_messages where msg_id = ?\");\n\t\t\tstmt.setLong(1, msgId);\n\t\t\tstmt.executeUpdate();\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void deleteMessages(String receiver, ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tString receiverSha1 = Algorithms.hexDigest(receiver.toString(), \"\", \"SHA\");\n\t\t\tPreparedStatement stmt = conn.prepareStatement(\"delete from tig_offline_messages where receiver_sha1 = ?\");\n\t\t\tstmt.setString(1, receiverSha1);\n\n\t\t\tint affectedRows = stmt.executeUpdate();\n\n\t\t\tStatement stmt1 = conn.createStatement();\n\t\t\tdata[0] = stmt1.executeQuery(\"select \" + affectedRows + \" from sysibm.sysdummy1\");\n\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\tthrow new SQLException(e);\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void deleteMessagesByIds(String receiver, String msgId1, String msgId2, String msgId3, String msgId4,\n\t\t\t\t\t\t\t\t\t\t   ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tString receiverSha1 = Algorithms.hexDigest(receiver.toString(), \"\", \"SHA\");\n\t\t\tPreparedStatement stmt = conn.prepareStatement(\n\t\t\t\t\t\"delete from tig_offline_messages\" + \" where receiver_sha1 = ?\" + \" and (\" +\n\t\t\t\t\t\t\t\" (? is not null and msg_id = ?)\\n\" + \" or (? is not null and msg_id = ?)\" +\n\t\t\t\t\t\t\t\" or (? is not null and msg_id = ?)\" + \" or (? is not null and msg_id = ?)\" + \" )\");\n\t\t\tstmt.setString(1, receiverSha1);\n\t\t\tstmt.setString(2, msgId1);\n\t\t\tstmt.setString(3, msgId1);\n\t\t\tstmt.setString(4, msgId2);\n\t\t\tstmt.setString(5, msgId2);\n\t\t\tstmt.setString(6, msgId3);\n\t\t\tstmt.setString(7, msgId3);\n\t\t\tstmt.setString(8, msgId4);\n\t\t\tstmt.setString(9, msgId4);\n\n\t\t\tint affectedRows = stmt.executeUpdate();\n\n\t\t\tStatement stmt1 = conn.createStatement();\n\t\t\tdata[0] = stmt1.executeQuery(\"select \" + affectedRows + \" from sysibm.sysdummy1\");\n\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\tthrow new SQLException(e);\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void getExpiredMessages(int limit, ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement stmt = conn.prepareStatement(\n\t\t\t\t\t\"select om.msg_id, om.expired, om.message\" + \" from tig_offline_messages om\" +\n\t\t\t\t\t\t\t\" where om.expired is not null\" + \" order by om.expired asc\");\n\t\t\tstmt.setMaxRows(limit);\n\n\t\t\tdata[0] = stmt.executeQuery();\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void getExpiredMessagesBefore(Timestamp before, ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement stmt = conn.prepareStatement(\n\t\t\t\t\t\"select om.msg_id, om.expired, om.message\" + \" from tig_offline_messages om\" +\n\t\t\t\t\t\t\t\" where om.expired is not null and (? is null or om.expired <= ?)\" +\n\t\t\t\t\t\t\t\" order by om.expired asc\");\n\t\t\tif (before == null) {\n\t\t\t\tstmt.setNull(1, Types.TIMESTAMP);\n\t\t\t\tstmt.setNull(2, Types.TIMESTAMP);\n\t\t\t} else {\n\t\t\t\tstmt.setTimestamp(1, before);\n\t\t\t\tstmt.setTimestamp(2, before);\n\t\t\t}\n\t\t\tdata[0] = stmt.executeQuery();\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void getMessages(String receiver, ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tString receiverSha1 = Algorithms.hexDigest(receiver.toString(), \"\", \"SHA\");\n\t\t\tPreparedStatement stmt = conn.prepareStatement(\n\t\t\t\t\t\"select om.message, om.msg_id\" + \" from tig_offline_messages om\" + \" where om.receiver_sha1 = ?\");\n\t\t\tstmt.setString(1, receiverSha1);\n\n\t\t\tdata[0] = stmt.executeQuery();\n\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\tthrow new SQLException(e);\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void getMessagesByIds(String receiver, String msgId1, String msgId2, String msgId3, String msgId4,\n\t\t\t\t\t\t\t\t\t\tResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tString receiverSha1 = Algorithms.hexDigest(receiver.toString(), \"\", \"SHA\");\n\t\t\tPreparedStatement stmt = conn.prepareStatement(\n\t\t\t\t\t\"select om.message, om.msg_id\" + \" from tig_offline_messages om\" + \" where om.receiver_sha1 = ?\" +\n\t\t\t\t\t\t\t\" and (\" + \" (? is not null and om.msg_id = ?)\\n\" +\n\t\t\t\t\t\t\t\" or (? is not null and om.msg_id = ?)\" + \" or (? is not null and om.msg_id = ?)\" +\n\t\t\t\t\t\t\t\" or (? is not null and om.msg_id = ?)\" + \" )\");\n\t\t\tstmt.setString(1, receiverSha1);\n\t\t\tstmt.setString(2, msgId1);\n\t\t\tstmt.setString(3, msgId1);\n\t\t\tstmt.setString(4, msgId2);\n\t\t\tstmt.setString(5, msgId2);\n\t\t\tstmt.setString(6, msgId3);\n\t\t\tstmt.setString(7, msgId3);\n\t\t\tstmt.setString(8, msgId4);\n\t\t\tstmt.setString(9, msgId4);\n\n\t\t\tdata[0] = stmt.executeQuery();\n\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\tthrow new SQLException(e);\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void getMessagesCount(String receiver, ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tString receiverSha1 = Algorithms.hexDigest(receiver.toString(), \"\", \"SHA\");\n\t\t\tPreparedStatement stmt = conn.prepareStatement(\n\t\t\t\t\t\"select om.msg_type, count(om.msg_type)\" + \" from tig_offline_messages om\" +\n\t\t\t\t\t\t\t\" where om.receiver_sha1 = ?\" + \" group by om.msg_type\");\n\t\t\tstmt.setString(1, receiverSha1);\n\n\t\t\tdata[0] = stmt.executeQuery();\n\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\tthrow new SQLException(e);\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void listMessages(String receiver, ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tString receiverSha1 = Algorithms.hexDigest(receiver.toString(), \"\", \"SHA\");\n\t\t\tPreparedStatement stmt = conn.prepareStatement(\n\t\t\t\t\t\"select om.msg_id, om.msg_type, om.sender\" + \" from tig_offline_messages om\" +\n\t\t\t\t\t\t\t\" where om.receiver_sha1 = ?\");\n\t\t\tstmt.setString(1, receiverSha1);\n\n\t\t\tdata[0] = stmt.executeQuery();\n\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\tthrow new SQLException(e);\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/derby/StoredProcedures.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.derby;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.auth.credentials.Credentials;\nimport tigase.util.Algorithms;\n\nimport java.nio.charset.Charset;\nimport java.security.MessageDigest;\nimport java.sql.*;\nimport java.util.Optional;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * @author Artur Hefczyc\n * @version 5.1.0, 2010.09.11 at 02:08:58 BST\n */\npublic class StoredProcedures {\n\n\tprivate static final Logger log = Logger.getLogger(StoredProcedures.class.getName());\n\n\tprivate static final Charset UTF8 = Charset.forName(\"UTF-8\");\n\n\tprivate static final String DEFAULT_USERNAME_SHA1 = sha1(Credentials.DEFAULT_CREDENTIAL_ID);\n\n\tprivate static final String GET_VERSION = \"select version from tig_schema_versions where (component = ?)\";\n\n\tprivate static String encodePassword(String encMethod, String userId, String userPw) {\n\t\tif ((encMethod != null) && \"MD5-PASSWORD\".equals(encMethod)) {\n\t\t\treturn md5(userPw);\n\t\t} else if ((encMethod != null) && \"MD5-USERID-PASSWORD\".equals(encMethod)) {\n\t\t\treturn md5(userId + userPw);\n\t\t} else if ((encMethod != null) && \"MD5-USERNAME-PASSWORD\".equals(encMethod)) {\n\t\t\treturn md5(userId.substring(0, userId.indexOf(\"@\")) + userPw);\n\t\t} else {\n\t\t\treturn userPw;\n\t\t}\n\n\t}\n\n\tprivate static String md5(String data) {\n\t\ttry {\n\t\t\tMessageDigest md = MessageDigest.getInstance(\"MD5\");\n\n\t\t\tif (data != null) {\n\t\t\t\tmd.update(data.getBytes(UTF8));\n\t\t\t}\n\n\t\t\tbyte[] digest = md.digest();\n\n\t\t\treturn Algorithms.bytesToHex(digest);\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Error on encoding password\", e);\n\t\t}\n\t}\n\n\tprivate static String sha1(String data) {\n\t\ttry {\n\t\t\tMessageDigest md = MessageDigest.getInstance(\"SHA1\");\n\n\t\t\tif (data != null) {\n\t\t\t\tmd.update(data.getBytes(UTF8));\n\t\t\t}\n\n\t\t\tbyte[] digest = md.digest();\n\n\t\t\treturn Algorithms.bytesToHex(digest);\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(\"Error on encoding password\", e);\n\t\t}\n\t}\n\n\tpublic static void tigAccountStatus(final String user, ResultSet[] data) throws SQLException {\n\t\ttry (Connection conn = DriverManager.getConnection(\"jdbc:default:connection\")) {\n\t\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\t\t\tPreparedStatement ps = conn.prepareStatement(\n\t\t\t\t\t\"SELECT account_status FROM tig_users WHERE lower(user_id) = ?\");\n\t\t\tps.setString(1, user);\n\t\t\tdata[0] = ps.executeQuery();\n\t\t}\n\t}\n\n\tpublic static void tigActiveAccounts(ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement ps = conn.prepareStatement(\n\t\t\t\t\t\"select user_id, last_login, last_logout, online_status, failed_logins, account_status from tig_users where account_status > 0\");\n\n\t\t\tdata[0] = ps.executeQuery();\n\t\t} catch (SQLException e) {\n\n\t\t\t// e.printStackTrace();\n\t\t\t// log.log(Level.SEVERE, \"SP error\", e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void tigAddNode(long parentNid, long uid, String node, ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement ps = conn.prepareStatement(\n\t\t\t\t\t\"insert into tig_nodes (parent_nid, uid, node) values (?, ?, ?)\", Statement.RETURN_GENERATED_KEYS);\n\n\t\t\tps.setLong(1, parentNid);\n\t\t\tps.setLong(2, uid);\n\t\t\tps.setString(3, node);\n\t\t\tps.executeUpdate();\n\t\t\tdata[0] = ps.getGeneratedKeys();\n\t\t} catch (SQLException e) {\n\n\t\t\t// e.printStackTrace();\n\t\t\t// log.log(Level.SEVERE, \"SP error\", e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void tigAddUser(String userId, String userPw, ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement ps = conn.prepareStatement(\"insert into tig_users (user_id) values (?)\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t Statement.RETURN_GENERATED_KEYS);\n\n\t\t\tps.setString(1, userId);\n\t\t\tps.executeUpdate();\n\n\t\t\tResultSet rs = ps.getGeneratedKeys();\n\n\t\t\trs.next();\n\n\t\t\tlong generatedKey = rs.getLong(1);\n\n\t\t\tif (userPw != null) {\n\t\t\t\ttigUpdatePasswordPlainPw(userId, userPw);\n\t\t\t}\n\n\t\t\tPreparedStatement ps3 = conn.prepareStatement(\"select uid from tig_users where uid=?\");\n\n\t\t\tps3.setLong(1, generatedKey);\n\t\t\tdata[0] = ps3.executeQuery();\n\n\t\t\tPreparedStatement ps2 = conn.prepareStatement(\n\t\t\t\t\t\"insert into tig_nodes (parent_nid, uid, node) values (NULL, ?, 'root')\");\n\n\t\t\tps2.setLong(1, generatedKey);\n\t\t\tps2.executeUpdate();\n\n\t\t\tif (null == userPw) {\n\t\t\t\tPreparedStatement ps4 = conn.prepareStatement(\"update tig_users set account_status = -1 where uid = ?\");\n\n\t\t\t\tps4.setLong(1, generatedKey);\n\t\t\t\tps4.executeUpdate();\n\t\t\t}\n\t\t} catch (SQLException e) {\n\n\t\t\t// e.printStackTrace();\n\t\t\t// log.log(Level.SEVERE, \"SP error\", e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void tigAddUserPlainPw(String userId, String userPw, ResultSet[] data) throws SQLException {\n\t\tString encMethod = tigGetDBProperty(\"password-encoding\");\n\t\tString encp = encodePassword(encMethod, userId, userPw);\n\n\t\ttigAddUser(userId, encp, data);\n\t}\n\n\tpublic static void tigAllUsers(ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement ps = conn.prepareStatement(\n\t\t\t\t\t\"select user_id, failed_logins, account_status from tig_users\");\n\n\t\t\tdata[0] = ps.executeQuery();\n\t\t} catch (SQLException e) {\n\n\t\t\t// e.printStackTrace();\n\t\t\t// log.log(Level.SEVERE, \"SP error\", e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void tigAllUsersCount(ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement ps = conn.prepareStatement(\"select count(*) as res_cnt from tig_users\");\n\n\t\t\tdata[0] = ps.executeQuery();\n\t\t} catch (SQLException e) {\n\n\t\t\t// e.printStackTrace();\n\t\t\t// log.log(Level.SEVERE, \"SP error\", e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void tigDisableAccount(final String userId) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement ps = conn.prepareStatement(\n\t\t\t\t\t\"update tig_users set account_status = 0 where lower(user_id) = ?\");\n\n\t\t\tps.setString(1, userId.toLowerCase());\n\t\t\tps.executeUpdate();\n\t\t} catch (SQLException e) {\n\n\t\t\t// e.printStackTrace();\n\t\t\t// log.log(Level.SEVERE, \"SP error\", e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void tigDisabledAccounts(ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement ps = conn.prepareStatement(\n\t\t\t\t\t\"select user_id, last_login, last_logout, online_status, failed_logins, account_status from tig_users where account_status = 0\");\n\n\t\t\tdata[0] = ps.executeQuery();\n\t\t} catch (SQLException e) {\n\n\t\t\t// e.printStackTrace();\n\t\t\t// log.log(Level.SEVERE, \"SP error\", e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void tigEnableAccount(final String userId) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement ps = conn.prepareStatement(\n\t\t\t\t\t\"update tig_users set account_status = 1 where lower(user_id) = ?\");\n\n\t\t\tps.setString(1, userId.toLowerCase());\n\t\t\tps.executeUpdate();\n\t\t} catch (SQLException e) {\n\n\t\t\t// e.printStackTrace();\n\t\t\t// log.log(Level.SEVERE, \"SP error\", e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void tigGetComponentVersion(final String component, ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tString result = null;\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Getting version of the component: \" + component);\n\t\t\t}\n\n\t\t\tPreparedStatement ps = conn.prepareStatement(GET_VERSION);\n\n\t\t\tps.setString(1, component.toLowerCase());\n\t\t\tdata[0] = ps.executeQuery();\n\n\t\t} catch (SQLException e) {\n\n\t\t\t// e.printStackTrace();\n\t\t\t// log.log(Level.SEVERE, \"SP error\", e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static String tigGetDBProperty(final String key) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tString result = null;\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"function tigGetDBProperty('\" + key + \"') called\");\n\t\t\t}\n\n\t\t\tPreparedStatement ps = conn.prepareStatement(\n\t\t\t\t\t\"select pval from tig_pairs, tig_users where (pkey = ?) AND (user_id = 'db-properties') AND (tig_pairs.uid = tig_users.uid)\");\n\t\t\tResultSet rs;\n\n\t\t\tps.setString(1, key.toLowerCase());\n\t\t\trs = ps.executeQuery();\n\n\t\t\tif (rs.next()) {\n\t\t\t\tresult = rs.getString(1);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} catch (SQLException e) {\n\n\t\t\t// e.printStackTrace();\n\t\t\t// log.log(Level.SEVERE, \"SP error\", e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void tigGetPassword(String userId, ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement ps = conn.prepareStatement(\n\t\t\t\t\t\"select c.value from tig_users u \" + \" inner join tig_user_credentials c on c.uid = u.uid \" +\n\t\t\t\t\t\t\t\" where \" + \" u.user_id = ? \" + \" and c.mechanism = 'PLAIN' \" +\n\t\t\t\t\t\t\t\" and c.username = 'default'\");\n\n\t\t\tps.setString(1, userId.toLowerCase());\n\t\t\tdata[0] = ps.executeQuery();\n\t\t} catch (SQLException e) {\n\n\t\t\t// e.printStackTrace();\n\t\t\t// log.log(Level.SEVERE, \"SP error\", e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void tigGetUserDBUid(String userId, ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement ps = conn.prepareStatement(\"select uid from tig_users where lower(user_id) = ?\");\n\n\t\t\tps.setString(1, userId.toLowerCase());\n\t\t\tdata[0] = ps.executeQuery();\n\t\t} catch (SQLException e) {\n\n\t\t\t// e.printStackTrace();\n\t\t\t// log.log(Level.SEVERE, \"SP error\", e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void tigInitdb() throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement ps = conn.prepareStatement(\"update tig_users set online_status = 0\");\n\n\t\t\tps.executeUpdate();\n\t\t} catch (SQLException e) {\n\n\t\t\t// e.printStackTrace();\n\t\t\t// log.log(Level.SEVERE, \"SP error\", e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void tigOfflineUsers(ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement ps = conn.prepareStatement(\n\t\t\t\t\t\"select user_id, last_login, last_logout, online_status, failed_logins, account_status from tig_users where online_status = 0\");\n\n\t\t\tdata[0] = ps.executeQuery();\n\t\t} catch (SQLException e) {\n\n\t\t\t// e.printStackTrace();\n\t\t\t// log.log(Level.SEVERE, \"SP error\", e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void tigOnlineUsers(ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement ps = conn.prepareStatement(\n\t\t\t\t\t\"select user_id, last_login, last_logout, online_status, failed_logins, account_status from tig_users where online_status > 0\");\n\n\t\t\tdata[0] = ps.executeQuery();\n\t\t} catch (SQLException e) {\n\n\t\t\t// e.printStackTrace();\n\t\t\t// log.log(Level.SEVERE, \"SP error\", e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void tigRemoveUser(final String userId) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement ps3 = conn.prepareStatement(\"select uid from tig_users where lower(user_id) = ?\");\n\n\t\t\tps3.setString(1, userId.toLowerCase());\n\n\t\t\tResultSet rs = ps3.executeQuery();\n\n\t\t\tif (!rs.next()) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlong uid = rs.getLong(1);\n\t\t\tPreparedStatement ps1 = conn.prepareStatement(\"delete from tig_pairs where uid = ?\");\n\n\t\t\tps1.setLong(1, uid);\n\t\t\tps1.executeUpdate();\n\n\t\t\tPreparedStatement ps2 = conn.prepareStatement(\"delete from tig_nodes where uid = ?\");\n\n\t\t\tps2.setLong(1, uid);\n\t\t\tps2.executeUpdate();\n\n\t\t\tPreparedStatement ps4 = conn.prepareStatement(\"delete from tig_user_credentials where uid = ?\");\n\t\t\tps4.setLong(1, uid);\n\t\t\tps4.executeUpdate();\n\n\t\t\tPreparedStatement ps = conn.prepareStatement(\"delete from tig_users where uid = ?\");\n\n\t\t\tps.setLong(1, uid);\n\t\t\tps.executeUpdate();\n\t\t} catch (SQLException e) {\n\n\t\t\t// e.printStackTrace();\n\t\t\t// log.log(Level.SEVERE, \"SP error\", e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void tigSetComponentVersion(final String name, final String version) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Setting component: {0} version to: {1}\", new Object[]{name, version});\n\t\t\t}\n\n\t\t\tint result;\n\n\t\t\tPreparedStatement psComp = conn.prepareStatement(GET_VERSION);\n\t\t\tpsComp.setString(1, name.toLowerCase());\n\n\t\t\tResultSet rs = psComp.executeQuery();\n\n\t\t\tif (rs.next()) {\n\t\t\t\tfinal String updateSql = \"update tig_schema_versions set version = ? where (component = ?)\";\n\t\t\t\tPreparedStatement ps = conn.prepareStatement(updateSql);\n\n\t\t\t\tps.setString(1, version);\n\t\t\t\tps.setString(2, name);\n\t\t\t\tresult = ps.executeUpdate();\n\t\t\t} else {\n\t\t\t\tfinal String insertSql = \"insert into tig_schema_versions (component, version, last_update) VALUES (?, ?, current timestamp) \";\n\t\t\t\tPreparedStatement ps = conn.prepareStatement(insertSql);\n\n\t\t\t\tps.setString(1, name);\n\t\t\t\tps.setString(2, version);\n\t\t\t\tresult = ps.executeUpdate();\n\t\t\t}\n\n\t\t\tif (result != 1) {\n\t\t\t\tlog.severe(\"Error on Setting version\");\n\t\t\t}\n\t\t} catch (SQLException e) {\n\n//\t\t\t e.printStackTrace();\n//\t\t\t log.log(Level.SEVERE, \"SP error\", e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void tigUpdateAccountStatus(final String user, final int status) throws SQLException {\n\t\ttry (Connection conn = DriverManager.getConnection(\"jdbc:default:connection\")) {\n\t\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\t\t\tPreparedStatement ps = conn.prepareStatement(\n\t\t\t\t\t\"UPDATE tig_users SET account_status = ? WHERE lower(user_id) = ?\");\n\t\t\tps.setInt(1, status);\n\t\t\tps.setString(2, user);\n\t\t\tps.executeUpdate();\n\t\t}\n\t}\n\n\tpublic static void tigUpdateLoginTime(final String userId) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement ps = conn.prepareStatement(\n\t\t\t\t\t\"update tig_users set last_used = current timestamp where lower(user_id) =  ?\");\n\n\t\t\tps.setString(1, userId.toLowerCase());\n\t\t\tps.executeUpdate();\n\t\t} catch (SQLException e) {\n\n\t\t\t// e.printStackTrace();\n\t\t\t// log.log(Level.SEVERE, \"SP error\", e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void tigUpdatePairs(long nid, long uid, String key, Clob value) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement ps = conn.prepareStatement(\n\t\t\t\t\t\"select 1 from tig_pairs where nid = ? and uid = ? and pkey = ?\");\n\n\t\t\tps.setLong(1, nid);\n\t\t\tps.setLong(2, uid);\n\t\t\tps.setString(3, key);\n\n\t\t\tResultSet rs = ps.executeQuery();\n\t\t\tif (rs.next()) {\n\t\t\t\tPreparedStatement ps1 = conn.prepareStatement(\n\t\t\t\t\t\t\"update tig_pairs set pval = ? where nid = ? and uid = ? and pkey = ?\");\n\n\t\t\t\tps1.setClob(1, value);\n\t\t\t\tps1.setLong(2, nid);\n\t\t\t\tps1.setLong(3, uid);\n\t\t\t\tps1.setString(4, key);\n\n\t\t\t\tps1.executeUpdate();\n\t\t\t} else {\n\t\t\t\tPreparedStatement ps1 = conn.prepareStatement(\n\t\t\t\t\t\t\"insert into tig_pairs (nid, uid, pkey, pval) values (?, ?, ?, ?)\");\n\n\t\t\t\tps1.setLong(1, nid);\n\t\t\t\tps1.setLong(2, uid);\n\t\t\t\tps1.setString(3, key);\n\t\t\t\tps1.setClob(4, value);\n\n\t\t\t\tps1.executeUpdate();\n\t\t\t}\n\n\t\t} catch (SQLException e) {\n\n\t\t\t// e.printStackTrace();\n\t\t\t// log.log(Level.SEVERE, \"SP error\", e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void tigUpdatePasswordPlainPw(String userId, String userPw) throws SQLException {\n\t\tString passwordEncoding = Optional.ofNullable(tigGetDBProperty(\"password-encoding\")).orElse(\"PLAIN\");\n\t\tString encodedPassword = encodePassword(passwordEncoding, userId, userPw);\n\t\ttigUserCredentialUpdate(userId, Credentials.DEFAULT_CREDENTIAL_ID, passwordEncoding, encodedPassword);\n\t}\n\n\tpublic static void tigUpdatePasswordPlainPwRev(String userPw, String userId) throws SQLException {\n\t\ttigUpdatePasswordPlainPw(userId, userPw);\n\t}\n\n\tpublic static void tigUserCredentialRemove(String userId, String username) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement ps = conn.prepareStatement(\"select uid from tig_users where lower(user_id) =  ?\");\n\n\t\t\tps.setString(1, userId.toLowerCase());\n\n\t\t\tResultSet rs = ps.executeQuery();\n\t\t\tif (rs.next()) {\n\t\t\t\tlong uid = rs.getLong(1);\n\n\t\t\t\tps = conn.prepareStatement(\"delete from tig_user_credentials where uid = ? and username = ?\");\n\t\t\t\tps.setLong(1, uid);\n\t\t\t\tps.setString(2, username);\n\t\t\t\tps.execute();\n\t\t\t}\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void tigUserCredentialUpdate(String userId, String username, String mechanism, String value)\n\t\t\tthrows SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement ps = conn.prepareStatement(\"select uid from tig_users where lower(user_id) =  ?\");\n\n\t\t\tps.setString(1, userId.toLowerCase());\n\n\t\t\tResultSet rs = ps.executeQuery();\n\t\t\tif (rs.next()) {\n\t\t\t\tlong uid = rs.getLong(1);\n\n\t\t\t\tps = conn.prepareStatement(\n\t\t\t\t\t\t\"select 1 from tig_user_credentials where uid = ? and username = ? and mechanism = ?\");\n\t\t\t\tps.setLong(1, uid);\n\t\t\t\tps.setString(2, username);\n\t\t\t\tps.setString(3, mechanism);\n\t\t\t\trs = ps.executeQuery();\n\t\t\t\tif (rs.next()) {\n\t\t\t\t\tps = conn.prepareStatement(\n\t\t\t\t\t\t\t\"update tig_user_credentials set value = ? where uid = ? and username = ? and mechanism = ?\");\n\t\t\t\t\tps.setString(1, value);\n\t\t\t\t\tps.setLong(2, uid);\n\t\t\t\t\tps.setString(3, username);\n\t\t\t\t\tps.setString(4, mechanism);\n\t\t\t\t\tps.executeUpdate();\n\t\t\t\t} else {\n\t\t\t\t\tps = conn.prepareStatement(\n\t\t\t\t\t\t\t\"insert into tig_user_credentials (uid, username, mechanism, value) values (?,?,?,?)\");\n\t\t\t\t\tps.setLong(1, uid);\n\t\t\t\t\tps.setString(2, username);\n\t\t\t\t\tps.setString(3, mechanism);\n\t\t\t\t\tps.setString(4, value);\n\t\t\t\t\tps.execute();\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (SQLException e) {\n\n\t\t\t// e.printStackTrace();\n\t\t\t// log.log(Level.SEVERE, \"SP error\", e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void tigUserCredentialsGet(String userId, String username, ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement ps = conn.prepareStatement(\n\t\t\t\t\t\"select c.mechanism, c.value, u.account_status from tig_users u \" +\n\t\t\t\t\t\t\t\" inner join tig_user_credentials c on c.uid = u.uid \" +\n\t\t\t\t\t\t\t\" where lower(u.user_id) = ? and c.username = ?\");\n\t\t\tps.setString(1, userId.toLowerCase());\n\t\t\tps.setString(2, username);\n\n\t\t\tdata[0] = ps.executeQuery();\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void tigUserLoginPlainPw(String userId, String userPw, ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tString passwordEncoding = Optional.ofNullable(tigGetDBProperty(\"password-encoding\")).orElse(\"PLAIN\");\n\t\t\tString encodedPassword = encodePassword(passwordEncoding, userId, userPw);\n\t\t\tPreparedStatement ps = conn.prepareStatement(\n\t\t\t\t\t\"select u.user_id from tig_users u inner join tig_user_credentials c on c.uid = u.uid where (u.account_status > 0) AND ( lower(u.user_id) = ?) \" +\n\t\t\t\t\t\t\t\" AND c.username = '\" + Credentials.DEFAULT_CREDENTIAL_ID +\n\t\t\t\t\t\t\t\"' AND c.mechanism = ? AND c.value = ?\");\n\n\t\t\tps.setString(1, userId.toLowerCase());\n\t\t\tps.setString(2, passwordEncoding);\n\t\t\tps.setString(3, userPw);\n\n\t\t\tResultSet rs = ps.executeQuery();\n\n\t\t\tif (rs.next()) {\n\t\t\t\tPreparedStatement x = conn.prepareStatement(\"values '\" + userId + \"'\");\n\n\t\t\t\tdata[0] = x.executeQuery();\n\n\t\t\t\tPreparedStatement flps = conn.prepareStatement(\n\t\t\t\t\t\t\"update tig_users set online_status = online_status + 1, last_login = current timestamp where lower(user_id) =  ?\");\n\n\t\t\t\tflps.setString(1, userId.toLowerCase());\n\t\t\t\tflps.executeUpdate();\n\t\t\t} else {\n\t\t\t\tPreparedStatement x = conn.prepareStatement(\"values '-'\");\n\n\t\t\t\tdata[0] = x.executeQuery();\n\n\t\t\t\tPreparedStatement flps = conn.prepareStatement(\n\t\t\t\t\t\t\"update tig_users set failed_logins = failed_logins + 1 where lower(user_id) = ?\");\n\n\t\t\t\tflps.setString(1, userId.toLowerCase());\n\t\t\t\tflps.executeUpdate();\n\t\t\t}\n\t\t} catch (SQLException e) {\n\n\t\t\t// e.printStackTrace();\n\t\t\t// log.log(Level.SEVERE, \"SP error\", e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void tigUserLogout(final String userId) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement ps = conn.prepareStatement(\n\t\t\t\t\t\"update tig_users set online_status = online_status - 1, last_logout = current timestamp where lower(user_id) =  ?\");\n\n\t\t\tps.setString(1, userId.toLowerCase());\n\t\t\tps.executeUpdate();\n\t\t} catch (SQLException e) {\n\n\t\t\t// e.printStackTrace();\n\t\t\t// log.log(Level.SEVERE, \"SP error\", e);\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n\n\tpublic static void tigUserUsernamesGet(String userId, ResultSet[] data) throws SQLException {\n\t\tConnection conn = DriverManager.getConnection(\"jdbc:default:connection\");\n\n\t\tconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);\n\n\t\ttry {\n\t\t\tPreparedStatement ps = conn.prepareStatement(\"select distinct c.username from tig_users u \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \" inner join tig_user_credentials c on c.uid = u.uid \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \" where lower(u.user_id) = ?\");\n\t\t\tps.setString(1, userId.toLowerCase());\n\n\t\t\tdata[0] = ps.executeQuery();\n\t\t} finally {\n\t\t\tconn.close();\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/db/jdbc/DataRepositoryImpl.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.jdbc;\n\nimport tigase.db.DBInitException;\nimport tigase.db.DataRepository;\nimport tigase.db.Repository;\nimport tigase.db.util.JDBCPasswordObfuscator;\nimport tigase.db.util.RepositoryVersionAware;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.stats.CounterValue;\nimport tigase.stats.StatisticsList;\nimport tigase.stats.StatisticsProviderIfc;\nimport tigase.util.Version;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.lang.reflect.Proxy;\nimport java.sql.*;\nimport java.time.Duration;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.concurrent.ConcurrentSkipListMap;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Sep 3, 2010 5:55:41 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Repository.Meta(isDefault = true, supportedUris = {\"jdbc:[^:]+:.*\"})\npublic class DataRepositoryImpl\n\t\timplements DataRepository, StatisticsProviderIfc, RepositoryVersionAware {\n\n\tpublic static final String DERBY_CONNVALID_QUERY = \"values 1\";\n\tpublic static final String JDBC_CONNVALID_QUERY = \"select 1\";\n\tpublic static final String JDBC_SCHEMA_VERSION_QUERY = \"{ call TigGetComponentVersion( ? ) }\";\n\tpublic static final String MYSQL_CHECK_TABLE_QUERY = \"select * from information_schema.tables where table_name = ? and table_schema = ?\";\n\tpublic static final String PGSQL_CHECK_TABLE_QUERY = \"select * from pg_tables where tablename = ? and schemaname = ?\";\n\tpublic static final String DERBY_CHECK_TABLE_QUERY = \"select * from SYS.SYSTABLES where tablename = UPPER(?) and ? is not null\";\n\tpublic static final String SQLSERVER_CHECK_TABLE_QUERY = \"SELECT * FROM INFORMATION_SCHEMA.TABLES where TABLE_TYPE = 'BASE TABLE' AND  TABLE_NAME = ? and TABLE_SCHEMA = ?\";\n\tpublic static final String OTHER_CHECK_TABLE_QUERY = \"\";\n\tpublic static final String SP_STARTS_WITH = \"{ call\";\n\tpublic static final String QUERY_TIMEOUT_PROP_KEY = \"sql-query-timeout\";\n\tpublic static final int QUERY_TIMEOUT = 10;\n\tpublic static final String DB_CONN_TIMEOUT_PROP_KEY = \"db-conn-timeout\";\n\tpublic static final int DB_CONN_TIMEOUT = 15;\n\tprivate static final Logger log = Logger.getLogger(DataRepositoryImpl.class.getName());\n\t@ConfigField(desc = \"Automatic schema management\", alias = \"schema-management\")\n\tprivate boolean automaticSchemaManagement = true;\n\tprivate String check_table_query = OTHER_CHECK_TABLE_QUERY;\n\tprivate Connection conn = null;\n\tprivate PreparedStatement conn_valid_st = null;\n\tprivate long connectionValidateInterval = TimeUnit.SECONDS.toMillis(60);\n\tprivate dbTypes database = null;\n\tprivate String db_conn = null;\n\t@ConfigField(desc = \"Database connection timeout\", alias = DB_CONN_TIMEOUT_PROP_KEY)\n\tprivate int db_conn_timeout = DB_CONN_TIMEOUT;\n\tprivate Map<String, DBQuery> db_queries = new ConcurrentSkipListMap<String, DBQuery>();\n\tprivate Map<String, PreparedStatement> db_statements = new ConcurrentSkipListMap<String, PreparedStatement>();\n\tprivate boolean derby_mode = false;\n\tprivate long lastConnectionValidated = 0;\n\t@ConfigField(desc = \"Query timeout\", alias = QUERY_TIMEOUT_PROP_KEY)\n\tprivate int query_timeout = QUERY_TIMEOUT;\n\tprivate CounterValue reconnectionCounter = null;\n\tprivate CounterValue reconnectionFailedCounter = null;\n\tprivate String table_schema = null;\n\t@ConfigField(desc = \"Use workaround for slow prepareStatement() in MySQL\", alias = \"useCallableMysqlWorkaround\")\n\tprivate boolean useCallableMysqlWorkaround = false;\n\t@ConfigField(desc = \"Use native JDBC driver connectivity check\", alias = \"use-native-driver-connectivity-check\")\n\tprivate boolean useNativeDriverConnectivityCheck = true;\n\t@ConfigField(desc = \"Check if database connection is read-only\", alias = \"test-if-connection-is-read-only\")\n\tprivate boolean testIfConnectionIsReadOnly = true;\n\t@ConfigField(desc = \"Query connection server and check if it is read-only\", alias = \"query-server-is-read-only-state\")\n\tprivate boolean queryServerIsReadOnlyState = false;\n\n\t@Override\n\tpublic boolean automaticSchemaManagement() {\n\t\treturn automaticSchemaManagement;\n\t}\n\n\t@Override\n\tpublic Optional<Version> getSchemaVersion(String component) {\n\t\tResultSet rs = null;\n\t\tString dbVersionStr = null;\n\t\ttry {\n\t\t\tPreparedStatement ps = getPreparedStatement(null, JDBC_SCHEMA_VERSION_QUERY);\n\t\t\tsynchronized (ps) {\n\t\t\t\tps.setString(1, component);\n\t\t\t\trs = ps.executeQuery();\n\t\t\t\tif (rs.next()) {\n\t\t\t\t\tdbVersionStr = rs.getString(1);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tlog.log(Level.FINE, \"Error getting schema version from the DB\", e);\n\t\t} finally {\n\t\t\trelease(null, rs);\n\t\t}\n\t\treturn dbVersionStr != null ? Optional.of(Version.of(dbVersionStr)) : Optional.empty();\n\t}\n\n\t@Override\n\tpublic boolean checkTable(String tableName) throws SQLException {\n\t\tPreparedStatement checkTableSt = getPreparedStatement(null, check_table_query);\n\n\t\tif (checkTableSt == null) {\n\t\t\treturn true;\n\t\t}\n\n\t\tboolean result = false;\n\t\tResultSet rs = null;\n\n\t\tsynchronized (checkTableSt) {\n\t\t\ttry {\n\t\t\t\tcheckTableSt.setString(1, tableName);\n\t\t\t\tcheckTableSt.setString(2, table_schema);\n\t\t\t\trs = checkTableSt.executeQuery();\n\n\t\t\t\tif (rs.next()) {\n\t\t\t\t\tresult = true;\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\trelease(null, rs);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean checkTable(String tableName, String createTableQuery) throws SQLException {\n\t\tResultSet rs = null;\n\t\tStatement st = null;\n\t\tboolean result = false;\n\n\t\ttry {\n\t\t\tlog.log(Level.CONFIG, \"Checking if table {0} exists in DB {1}.\", new Object[]{tableName, table_schema});\n\t\t\tif (!checkTable(tableName)) {\n\t\t\t\tlog.log(Level.CONFIG, \"Table {0} not found in database, creating: {1}\",\n\t\t\t\t\t\tnew Object[]{tableName, createTableQuery});\n\t\t\t\tst = createStatement(null);\n\t\t\t\tif (!db_conn.contains(\"derby\")) {\n\t\t\t\t\tst.executeUpdate(createTableQuery);\n\t\t\t\t} else {\n\t\t\t\t\tString[] queries = createTableQuery.split(\";\");\n\t\t\t\t\tfor (String query : queries) {\n\t\t\t\t\t\tquery = query.trim();\n\t\t\t\t\t\tif (query.isEmpty()) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tst.executeUpdate(query);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tresult = true;\n\t\t\t} else {\n\t\t\t\tlog.log(Level.CONFIG, \"OK table {0} found in database.\", tableName);\n\t\t\t}\n\t\t} finally {\n\t\t\trelease(st, rs);\n\t\t\trs = null;\n\t\t\tst = null;\n\n\t\t\t// stmt = null;\n\t\t}\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic void checkConnectivity(Duration watchdogTime) {\n\t\ttry {\n\t\t\tif (watchdogTime.toMillis() < (System.currentTimeMillis() - lastConnectionValidated)) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"checking connection status {0}, last query executed {1}ms ago\",\n\t\t\t\t\t\t\tnew Object[]{this, (System.currentTimeMillis() - lastConnectionValidated)});\n\t\t\t\t}\n\t\t\t\tcheckConnection();\n\t\t\t}\n\t\t} catch (SQLException ex) {\n\t\t\tlog.log(Level.FINER, \"Exception during repository reinitialization\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Statement createStatement(BareJID user_id) throws SQLException {\n\t\tcheckConnection();\n\t\t// This synchronization is used to prevent call when the connection and\n\t\t// all prepared statements are being recreated.\n\t\tsynchronized (db_statements) {\n\t\t\treturn conn.createStatement();\n\t\t}\n\t}\n\n\t@Override\n\tpublic PreparedStatement getPreparedStatement(BareJID user_id, String stIdKey) throws SQLException {\n\t\tcheckConnection();\n\n\t\t// This synchronization is used to prevent call when the connection and\n\t\t// all prepared statements are being recreated.\n\t\tsynchronized (db_statements) {\n\t\t\treturn db_statements.get(stIdKey);\n\t\t}\n\t}\n\n\t@Override\n\tpublic PreparedStatement getPreparedStatement(int hashCode, String stIdKey) throws SQLException {\n\t\tcheckConnection();\n\n\t\t// This synchronization is used to prevent call when the connection and\n\t\t// all prepared statements are being recreated.\n\t\tsynchronized (db_statements) {\n\t\t\treturn db_statements.get(stIdKey);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getResourceUri() {\n\t\treturn db_conn;\n\t}\n\n\t@Override\n\tpublic dbTypes getDatabaseType() {\n\t\treturn database;\n\t}\n\n\t@Override\n\tpublic void initPreparedStatement(String key, String query) throws SQLException {\n\t\tdb_queries.put(key, new DBQuery(query, Statement.NO_GENERATED_KEYS));\n\t\tinitStatement(key);\n\t}\n\n\t@Override\n\tpublic void initPreparedStatement(String key, String query, int autoGeneratedKeys) throws SQLException {\n\t\tdb_queries.put(key, new DBQuery(query, autoGeneratedKeys));\n\t\tinitStatement(key);\n\t}\n\n\t@Override\n\tpublic void initialize(String resource_uri) throws DBInitException {\n\t\tString driverClass = null;\n\n\t\tdatabase = parseDatabaseType(resource_uri);\n\n\t\tif (database == null) {\n\t\t\tthrow new DBInitException(\"Database not supported\");\n\t\t}\n\n\t\tswitch (database) {\n\t\t\tcase postgresql:\n\t\t\t\tdriverClass = \"org.postgresql.Driver\";\n\t\t\t\tcheck_table_query = PGSQL_CHECK_TABLE_QUERY;\n\t\t\t\tbreak;\n\t\t\tcase mysql:\n\t\t\t\tdriverClass = \"com.mysql.cj.jdbc.Driver\";\n\t\t\t\tcheck_table_query = MYSQL_CHECK_TABLE_QUERY;\n\t\t\t\tbreak;\n\t\t\tcase derby:\n\t\t\t\tdriverClass = \"org.apache.derby.jdbc.EmbeddedDriver\";\n\t\t\t\tcheck_table_query = DERBY_CHECK_TABLE_QUERY;\n\t\t\t\tbreak;\n\t\t\tcase jtds:\n\t\t\tcase sqlserver:\n\t\t\t\tdriverClass = \"com.microsoft.sqlserver.jdbc.SQLServerDriver\";\n\t\t\t\tcheck_table_query = SQLSERVER_CHECK_TABLE_QUERY;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tdriverClass = \"net.sf.log4jdbc.sql.jdbcapi.DriverSpy\";\n\t\t\t\tcheck_table_query = OTHER_CHECK_TABLE_QUERY;\n\t\t\t\tbreak;\n\t\t}\n\n\t\ttry {\n\t\t\tClass.forName(driverClass, true, this.getClass().getClassLoader());\n\t\t} catch (ClassNotFoundException ex) {\n\t\t\tLogger.getLogger(DataRepositoryImpl.class.getName()).log(Level.SEVERE, null, ex);\n\t\t}\n\n\t\tdb_conn = resource_uri;\n\n\t\tif (db_conn != null) {\n\n\t\t\tswitch (database) {\n\t\t\t\tcase jtds:\n\t\t\t\tcase sqlserver:\n\t\t\t\t\ttable_schema = \"dbo\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase postgresql:\n\t\t\t\t\ttable_schema = \"public\";\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tString[] slashes = db_conn.split(\"/\");\n\t\t\t\t\ttable_schema = slashes[slashes.length - 1].split(\"\\\\?\")[0];\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tlog.log(Level.CONFIG, \"Table schema found: {0}, database type: {1}, database driver: {2}\",\n\t\t\t\t\tnew Object[]{table_schema, database.toString(), driverClass});\n\t\t}\n\t\ttry {\n\t\t\treconnectionCounter = new CounterValue(\"reconnections\", Level.FINER);\n\t\t\treconnectionFailedCounter = new CounterValue(\"failed reconnections\", Level.FINER);\n\t\t\tinitRepo();\n\n\t\t\tif (!check_table_query.isEmpty()) {\n\t\t\t\tinitPreparedStatement(check_table_query, check_table_query);\n\t\t\t}\n\t\t\tinitPreparedStatement(JDBC_SCHEMA_VERSION_QUERY, JDBC_SCHEMA_VERSION_QUERY);\n\t\t} catch (SQLException ex) {\n\t\t\tthrow new DBInitException(\"Database initialization failed\", ex);\n\t\t}\n\n\t\tlog.log(Level.CONFIG, \"Initialized database connection: {0}\", JDBCPasswordObfuscator.obfuscatePassword(resource_uri));\n\t}\n\n\tpublic static dbTypes parseDatabaseType(String resource_uri) {\n\t\tdbTypes db = null;\n\t\tif (resource_uri.startsWith(\"jdbc:postgresql\")) {\n\t\t\tdb = dbTypes.postgresql;\n\t\t} else if (resource_uri.startsWith(\"jdbc:mysql\")) {\n\t\t\tdb = dbTypes.mysql;\n\t\t} else if (resource_uri.startsWith(\"jdbc:derby\")) {\n\t\t\tdb = dbTypes.derby;\n\t\t} else if (resource_uri.startsWith(\"jdbc:jtds:sqlserver\")) {\n\t\t\tdb = dbTypes.jtds;\n\t\t} else if (resource_uri.startsWith(\"jdbc:sqlserver\")) {\n\t\t\tdb = dbTypes.sqlserver;\n\t\t}\n\t\treturn db;\n\t}\n\n\t@Override\n\t@Deprecated\n\tpublic void initRepository(String resource_uri, Map<String, String> params) throws DBInitException {\n\n\t\tdb_conn_timeout = getParam(DB_CONN_TIMEOUT_PROP_KEY, params, DB_CONN_TIMEOUT);\n\t\tquery_timeout = getParam(QUERY_TIMEOUT_PROP_KEY, params, QUERY_TIMEOUT);\n\n\t\tinitialize(resource_uri);\n\t}\n\n\t@Override\n\tpublic void release(Statement stmt, ResultSet rs) {\n\t\tif (rs != null) {\n\t\t\ttry {\n\t\t\t\trs.close();\n\t\t\t} catch (SQLException sqlEx) {\n\t\t\t}\n\t\t}\n\n\t\tif (stmt != null) {\n\t\t\ttry {\n\t\t\t\tstmt.close();\n\t\t\t} catch (SQLException sqlEx) {\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic DataRepository takeRepoHandle(BareJID user_id) {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void startTransaction() throws SQLException {\n\t\tconn.setAutoCommit(false);\n\t}\n\n\t@Override\n\tpublic void commit() throws SQLException {\n\t\tconn.commit();\n\t}\n\n\t@Override\n\tpublic void rollback() throws SQLException {\n\t\tconn.rollback();\n\t}\n\n\t@Override\n\tpublic void endTransaction() throws SQLException {\n\t\tconn.setAutoCommit(true);\n\t}\n\n\t@Override\n\tpublic void releaseRepoHandle(DataRepository repo) {\n\t}\n\n\t@Override\n\tpublic void getStatistics(String compName, StatisticsList list) {\n\t\tlong reconnections =\n\t\t\t\tlist.getValue(compName, reconnectionCounter.getName(), 0L) + reconnectionCounter.getValue();\n\t\tlist.add(compName, reconnectionCounter.getName(), reconnections, Level.FINER);\n\t\tlong failedReconnections =\n\t\t\t\tlist.getValue(compName, reconnectionFailedCounter.getName(), 0L) + reconnectionFailedCounter.getValue();\n\t\tlist.add(compName, reconnectionFailedCounter.getName(), failedReconnections, Level.FINER);\n\t}\n\n\t@Override\n\tpublic int getPoolSize() {\n\t\treturn 1;\n\t}\n\n\tprotected int getParam(String key, Map<String, String> params, int def) {\n\t\tint result = def;\n\t\tString temp = System.getProperty(key);\n\t\tif (temp != null) {\n\t\t\ttry {\n\t\t\t\tresult = Integer.parseInt(temp);\n\t\t\t} catch (NumberFormatException e) {\n\t\t\t\tresult = def;\n\t\t\t}\n\t\t}\n\t\tif (params != null) {\n\t\t\ttemp = params.get(key);\n\t\t\tif (temp != null) {\n\t\t\t\ttry {\n\t\t\t\t\tresult = Integer.parseInt(temp);\n\t\t\t\t} catch (NumberFormatException e) {\n\t\t\t\t\tresult = def;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * <code>checkConnection</code> method checks database connection before any query. For some database servers (or\n\t * JDBC drivers) it happens the connection is dropped if not in use for a long time or after certain timeout passes.\n\t * This method allows us to detect the problem and reinitialize database connection. This method must not be called\n\t * concurrently, therefore it is synchronized.\n\t *\n\t * @return a <code>boolean</code> value if the database connection is working.\n\t *\n\t * @throws SQLException if an error occurs on database query.\n\t */\n\tprivate synchronized boolean checkConnection() throws SQLException {\n\t\tif (conn.isClosed()) {\n\t\t\tlog.log(Level.FINEST, \"Connection is in closed, reinitialising connection: \" + conn);\n\t\t\tinitRepo();\n\t\t\treturn false;\n\t\t}\n\t\tif (testIfConnectionIsReadOnly) {\n\t\t\tif (checkConnectionIsReadOnly()) {\n\t\t\t\tlog.log(Level.INFO, \"Connection is in read-only mode, reinitialising connection: \" + conn);\n\t\t\t\tinitRepo();\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tif (useNativeDriverConnectivityCheck) {\n\t\t\tlong tmp = System.currentTimeMillis();\n\n\t\t\tif ((tmp - lastConnectionValidated) >= connectionValidateInterval) {\n\t\t\t\tlastConnectionValidated = tmp;\n\t\t\t\ttry {\n\t\t\t\t\tif (!conn.isValid(db_conn_timeout)) {\n\t\t\t\t\t\tinitRepo();\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tinitRepo();\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tResultSet rs = null;\n\n\t\t\ttry {\n\t\t\t\tlong tmp = System.currentTimeMillis();\n\n\t\t\t\t// synchronized (conn_valid_st) {\n\t\t\t\tif ((tmp - lastConnectionValidated) >= connectionValidateInterval) {\n\t\t\t\t\tlastConnectionValidated = tmp;\n\t\t\t\t\trs = conn_valid_st.executeQuery();\n\t\t\t\t} // end of if ()\n\t\t\t\t// }\n\n\t\t\t\tif (((conn_valid_st == null) || conn_valid_st.isClosed()) && ((tmp - lastConnectionValidated) >= 1000)) {\n\t\t\t\t\tinitRepo();\n\t\t\t\t} // end of if ()\n\t\t\t} catch (Exception e) {\n\t\t\t\tinitRepo();\n\t\t\t} finally {\n\t\t\t\trelease(null, rs);\n\t\t\t} // end of try-catch\n\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate boolean checkConnectionIsReadOnly() {\n\t\ttry {\n\t\t\tsynchronized (conn) {\n\t\t\t\tif (queryServerIsReadOnlyState ? isConnectionServerReadOnly() : conn.isReadOnly()) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\t// FOR some reason MySQL driver throws either NPE or ArrayIndexOutOfBoundsException here…\n\t\t\tlog.log(Level.FINEST, \"Connection.isReadOnly threw exception: \" + conn, e);\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate boolean isConnectionServerReadOnly() {\n\t\ttry (Statement statement = conn.createStatement()) {\n\t\t\tOptional<String> query = switch (database) {\n\t\t\t\t// AWS RDS uses `innodb_read_only` variable on read-only replica\n\t\t\t\tcase mysql -> Optional.of(\"select @@innodb_read_only\");\n\t\t\t\tcase postgresql -> Optional.of(\"SELECT pg_is_in_recovery();\");\n\t\t\t\tdefault -> Optional.empty();\n\t\t\t};\n\t\t\tif (query.isPresent()) {\n\t\t\t\ttry (ResultSet resultSet = statement.executeQuery(query.get())) {\n\t\t\t\t\tif (resultSet.next() && !resultSet.getBoolean(1)) {\n\t\t\t\t\t\treturn true;\n\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tlog.log(Level.FINEST, \"Checking if database server is ReadOnly threw exception: \" + conn, e);\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * <code>initPreparedStatements</code> method initializes internal database connection variables such as prepared\n\t * statements.\n\t *\n\t * @throws SQLException if an error occurs on database query.\n\t */\n\tprivate void initPreparedStatements() throws SQLException {\n\t\tString query = (derby_mode ? DERBY_CONNVALID_QUERY : JDBC_CONNVALID_QUERY);\n\n\t\tconn_valid_st = prepareQuery(query, Statement.NO_GENERATED_KEYS);\n\t\ttry {\n\t\t\tconn_valid_st.setQueryTimeout(query_timeout);\n\t\t} catch (SQLException ex) {\n\t\t\t// Ignore for now, it seems that PostgreSQL does not support this method\n\t\t\t// call yet\n\t\t}\n\n\t\tfor (String key : db_queries.keySet()) {\n\t\t\tinitStatement(key);\n\t\t}\n\t}\n\n\tprivate void initStatement(String key) throws SQLException {\n\t\tDBQuery dbQuery = db_queries.get(key);\n\n\t\tPreparedStatement st = prepareQuery(dbQuery.query, dbQuery.autoGeneratedKeys);\n\n\t\tst = (PreparedStatement) Proxy.newProxyInstance(this.getClass().getClassLoader(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew Class[]{PreparedStatement.class},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew PreparedStatementInvocationHandler(st));\n\n\t\ttry {\n\t\t\tst.setQueryTimeout(query_timeout);\n\t\t} catch (SQLException ex) {\n\t\t\t// Ignore for now, it seems that PostgreSQL does not support this method\n\t\t\t// call yet\n\t\t}\n\t\tdb_statements.put(key, st);\n\t}\n\n\t/**\n\t * <code>initRepo</code> method initializes database connection and data repository.\n\t *\n\t * @throws SQLException if an error occurs on database query.\n\t */\n\tprivate void initRepo() throws SQLException {\n\n\t\tboolean failure = true;\n\t\ttry {\n\t\t\tif (conn != null) {\n\t\t\t\treconnectionCounter.inc();\n\t\t\t\tlog.log(Level.CONFIG, \"Reconnecting connection: {0}\", reconnectionCounter);\n\t\t\t}\n\t\t\tsynchronized (db_statements) {\n\t\t\t\tdb_statements.clear();\n\t\t\t\tDriverManager.setLoginTimeout(db_conn_timeout);\n\t\t\t\tconn = DriverManager.getConnection(db_conn);\n\t\t\t\tconn.setAutoCommit(true);\n\t\t\t\tderby_mode = db_conn.startsWith(\"jdbc:derby\");\n\t\t\t\tinitPreparedStatements();\n\n\t\t\t\tfailure = false;\n\t\t\t\t// stmt = conn.createStatement();\n\t\t\t}\n\t\t} finally {\n\t\t\trelease(null, null);\n\n\t\t\tif (failure) {\n\t\t\t\treconnectionFailedCounter.inc();\n\t\t\t\tlog.log(Level.CONFIG, \"Reconnecting connection failed: {0}\", reconnectionFailedCounter);\n\t\t\t}\n\t\t\t// release(stmt, rs);\n\t\t\t// stmt = null;\n//\t\t\trs = null;\n\t\t}\n\t}\n\n\tprivate PreparedStatement prepareQuery(String query, int autoGeneratedKeys) throws SQLException {\n\t\tif (query.startsWith(SP_STARTS_WITH)) {\n\t\t\tswitch (database) {\n\t\t\t\tcase mysql:\n\t\t\t\t\tif (useCallableMysqlWorkaround) {\n\t\t\t\t\t\tString tmp = query.replace(\"{ call\", \"CALL \").replace(\"}\", \"\");\n\t\t\t\t\t\treturn conn.prepareStatement(tmp);\n\t\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\treturn conn.prepareCall(query);\n\t\t\t}\n\t\t} else {\n\t\t\tswitch (database) {\n\t\t\t\tcase sqlserver:\n\t\t\t\t\treturn conn.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);\n\t\t\t\tdefault:\n\t\t\t\t\treturn conn.prepareStatement(query, autoGeneratedKeys);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tfinal String url = getResourceUri() != null ? JDBCPasswordObfuscator.obfuscatePassword(getResourceUri()) : \"n/a\";\n\t\treturn \"DataRepository{\" + url + \"}\";\n\t}\n\n\tprivate class DBQuery {\n\n\t\tfinal int autoGeneratedKeys;\n\t\tfinal String query;\n\n\t\tDBQuery(String query, int autoGeneratedKeys) {\n\t\t\tthis.query = query;\n\t\t\tthis.autoGeneratedKeys = autoGeneratedKeys;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/jdbc/JDBCRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.jdbc;\n\nimport org.jspecify.annotations.NonNull;\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.*;\nimport tigase.db.util.JDBCPasswordObfuscator;\nimport tigase.db.util.RepositoryVersionAware;\nimport tigase.util.cache.SimpleCache;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.io.StringReader;\nimport java.sql.*;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.*;\nimport java.util.function.Function;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Not synchronized implementation! Musn't be used by more than one thread at the same time. <p> Thanks to Daniele for\n * better unique IDs handling. Created: Thu Oct 26 11:48:53 2006 </p>\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n * @author <a href=\"mailto:piras@tiscali.com\">Daniele</a>\n*/\n@Repository.Meta(supportedUris = {\"jdbc:[^:]+:.*\"})\n@Repository.SchemaId(id = Schema.SERVER_SCHEMA_ID, name = Schema.SERVER_SCHEMA_NAME)\npublic class JDBCRepository\n\t\timplements AuthRepository, UserRepository, DataSourceAware<DataRepository>, RepositoryVersionAware {\n\n\tpublic static final String CURRENT_DB_SCHEMA_VER = \"8.0.0\";\n\n\tpublic static final String DEF_MAXIDS_TBL = \"tig_max_ids\";\n\n\tpublic static final String DEF_NODES_TBL = \"tig_nodes\";\n\n\tpublic static final String DEF_PAIRS_TBL = \"tig_pairs\";\n\n\tpublic static final String DEF_ROOT_NODE = \"root\";\n\n\tpublic static final String DEF_USERS_TBL = \"tig_users\";\n\n\tpublic static final String DERBY_GETSCHEMAVER_QUERY = \"values TigGetDBProperty('schema-version')\";\n\n\t/**\n\t * Stored procedure used to check version of the schema\n\t * <br>\n\t * neither MS SQL Server JDBC driver supports default schema prefix in connection string for stored functions\n\t */\n\tpublic static final String SQLSERVER_GETSCHEMAVER_QUERY = \"select dbo.TigGetDBProperty('schema-version')\";\n\n\tpublic static final String JDBC_GETSCHEMAVER_QUERY = \"select TigGetDBProperty('schema-version')\";\n\n\tpublic static final String SCHEMA_UPGRADE_LINK = \"Administration Guide > Tigase Server Schema v8.0 Updates (available locally in docs directory and online http://docs.tigase.org)\";\n\tprivate static final String ADD_NODE_QUERY = \"{ call TigAddNode(?, ?, ?) }\";\n\tprivate static final String ADD_USER_PLAIN_PW_QUERY = \"{ call TigAddUserPlainPw(?, ?) }\";\n\tprivate static final String COUNT_USERS_FOR_DOMAIN_QUERY = \"select count(*) from tig_users where user_id like ?\";\n\tprivate static final String COUNT_ACTIVE_USERS_QUERY = \"select count(*) from tig_users where last_used > ?\";\n\tprivate static final String DEF_GET_USERS_QUERY = \"{ call TigAllUsers() }\";\n\tprivate static final String GET_USER_DB_UID_QUERY = \"{ call TigGetUserDBUid(?) }\";\n\tprivate static final String GET_USERS_COUNT_QUERY = \"{ call TigAllUsersCount() }\";\n\tprivate static final Logger log = Logger.getLogger(JDBCRepository.class.getName());\n\tprivate static final String PGSQL_GET_USERS_QUERY = \"select TigAllUsers()\";\n\tprivate static final String REMOVE_USER_QUERY = \"{ call TigRemoveUser(?) }\";\n\tprivate static final String UPDATE_PAIRS_QUERY = \"{ call TigUpdatePairs(?, ?, ?, ?) }\";\n\tprivate static final String USER_STR = \"User: \";\n\tprivate static final String REMOVE_KEY_DATA_QUERY =\n\t\t\t\"delete from \" + DEF_PAIRS_TBL + \" where (nid = ?) AND (pkey = ?)\";\n\tprivate static final String NODES_FOR_NODE_QUERY =\n\t\t\t\"select nid, node from \" + DEF_NODES_TBL + \" where parent_nid = ?\";\n\tprivate static final String KEYS_FOR_NODE_QUERY = \"select pkey from \" + DEF_PAIRS_TBL + \" where (nid = ?)\";\n\tprivate static final String INSERT_KEY_VAL_QUERY =\n\t\t\t\"insert into \" + DEF_PAIRS_TBL + \" (nid, uid, pkey, pval) \" + \" values (?, ?, ?, ?)\";\n\tprivate static final String DATA_FOR_NODE_QUERY =\n\t\t\t\"select pval from \" + DEF_PAIRS_TBL + \" where (nid = ?) AND (pkey = ?)\";\n\tprivate static final String KEYS_DATA_FOR_NODE_QUERY =\n\t\t\t\"select pkey, pval from \" + DEF_PAIRS_TBL + \" where (nid = ?)\";\n\tprivate static final String USER_KEY_VALUE_MAP_QUERY =\n\t\t\t\"select \" + DEF_USERS_TBL + \".user_id, \" + DEF_PAIRS_TBL + \".pval from \" + DEF_PAIRS_TBL +\n\t\t\t\t\" left join \" + DEF_USERS_TBL + \" on \" + DEF_USERS_TBL + \".uid = \" + DEF_PAIRS_TBL + \".uid\" +\n\t\t\t\t\" where (pkey = ?)\";\n\n\tprivate static final String UPDATE_LAST_LOGIN_QUERY =\n\t\t\t\"update \" + DEF_USERS_TBL + \" set last_login=? where user_id=?\";\n\n\t// ~--- fields ---------------------------------------------------------------\n\tprivate AuthRepository auth = null;\n\tprivate boolean autoCreateUser = false;\n\t// Cache moved to connection pool\n\tprivate IRepoCache<String, Object> cache = null;\n\tprivate DataRepository data_repo = null;\n\tprivate String get_users_query = null;\n\n\t@Override\n\tpublic void addDataList(BareJID user_id, final String subnode, final String key, final String[] list)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\ttry {\n\t\t\taddDataList(null, user_id, subnode, key, list);\n\t\t} catch (SQLException ex) {\n\t\t\tthrow new TigaseDBException(\"Problem adding data list to repository\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void addUser(BareJID user_id) throws UserExistsException, TigaseDBException {\n\t\ttry {\n\t\t\taddUserRepo(null, user_id);\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Error adding user to repository: \", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void addUser(BareJID user, final String password) throws UserExistsException, TigaseDBException {\n\t\tauth.addUser(user, password);\n\t}\n\n\t@Override\n\tpublic String getData(BareJID user_id, final String subnode, final String key, final String def)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\n\t\ttry {\n\t\t\tlong nid = getNodeNID(null, user_id, subnode);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Loading data for key: {0}, user: {1}, node: {2}, def: {3}, found nid: {4}\",\n\t\t\t\t\t\tnew Object[]{key, user_id, subnode, def, nid});\n\t\t\t}\n\n\t\t\tif (nid > 0) {\n\t\t\t\tResultSet rs = null;\n\n\t\t\t\tPreparedStatement data_for_node_st = data_repo.getPreparedStatement(user_id, DATA_FOR_NODE_QUERY);\n\n\t\t\t\tsynchronized (data_for_node_st) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tString result = def;\n\n\t\t\t\t\t\tdata_for_node_st.setLong(1, nid);\n\t\t\t\t\t\tdata_for_node_st.setString(2, key);\n\t\t\t\t\t\trs = data_for_node_st.executeQuery();\n\t\t\t\t\t\tif (rs.next()) {\n\t\t\t\t\t\t\tresult = rs.getString(1);\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Found data: {0}\", result);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// cache.put(user_id+\"/\"+subnode+\"/\"+key, new String[] {result});\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn def;\n\t\t\t}    // end of if (nid > 0) else\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Error getting user data for: \" + user_id + \"/\" + subnode + \"/\" + key, e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getData(BareJID user_id, final String subnode, final String key)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\treturn getData(user_id, subnode, key, null);\n\t}\n\n\t@Override\n\tpublic String getData(BareJID user_id, final String key) throws UserNotFoundException, TigaseDBException {\n\t\treturn getData(user_id, null, key, null);\n\t}\n\n\t@Override\n\tpublic Map<BareJID, String> getDataMap(@NonNull String key) throws UserNotFoundException, TigaseDBException {\n\t\tObjects.requireNonNull(key);\n\n\t\tlog.log(Level.FINEST, \"Searching for key: {0}\", key);\n\t\ttry {\n\t\t\tResultSet rs = null;\n\n\t\t\tPreparedStatement user_key_value_map = data_repo.getPreparedStatement(null, USER_KEY_VALUE_MAP_QUERY);\n\n\t\t\tsynchronized (user_key_value_map) {\n\t\t\t\ttry {\n\t\t\t\t\tMap<BareJID, String> results = new HashMap<>();\n\n\t\t\t\t\tuser_key_value_map.setString(1, key);\n\t\t\t\t\trs = user_key_value_map.executeQuery();\n\t\t\t\t\twhile (rs.next()) {\n\t\t\t\t\t\tString user = rs.getString(1);\n\t\t\t\t\t\tString value = rs.getString(2);\n\t\t\t\t\t\tlog.log(Level.FINEST, () -> \"Found data: \" + user + \": \" + value);\n\t\t\t\t\t\tif (user != null) {\n\t\t\t\t\t\t\tresults.put(BareJID.bareJIDInstanceNS(user), value);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn results;\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Error getting user mapped data for: \" + key, e);\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic Map<String, String> getDataMap(BareJID user_id, String subnode)\n\t\t\tthrows TigaseDBException {\n\t\ttry {\n\t\t\tlong nid = getNodeNID(null, user_id, subnode);\n\t\t\t\n\t\t\tif (nid > 0) {\n\t\t\t\tResultSet rs = null;\n\n\t\t\t\tPreparedStatement data_for_node_st = data_repo.getPreparedStatement(user_id, KEYS_DATA_FOR_NODE_QUERY);\n\n\t\t\t\tsynchronized (data_for_node_st) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tMap<String, String> results = new HashMap<>();\n\n\t\t\t\t\t\tdata_for_node_st.setLong(1, nid);\n\t\t\t\t\t\trs = data_for_node_st.executeQuery();\n\t\t\t\t\t\twhile (rs.next()) {\n\t\t\t\t\t\t\tString key = rs.getString(1);\n\t\t\t\t\t\t\tString value = rs.getString(2);\n\t\t\t\t\t\t\tresults.put(key, value);\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Found data: {0}, {1}\", new String[] { key, value });\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn results;\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn Collections.emptyMap();\n\t\t\t}    // end of if (nid > 0) else\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Error getting data map for: \" + user_id + \"/\" + subnode, e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic <T> Map<String, T> getDataMap(BareJID user, String subnode, Function<String, T> converter)\n\t\t\tthrows TigaseDBException {\n\t\tMap<String, String> data = getDataMap(user, subnode);\n\t\tif (data.isEmpty()) {\n\t\t\treturn Collections.emptyMap();\n\t\t} else {\n\t\t\tMap<String, T> results = new HashMap<>();\n\t\t\tfor (Map.Entry<String, String> e : data.entrySet()) {\n\t\t\t\tString value = e.getValue();\n\t\t\t\tif (value != null) {\n\t\t\t\t\tresults.put(e.getKey(), converter.apply(value));\n\t\t\t\t} else {\n\t\t\t\t\tresults.put(e.getKey(), null);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn results;\n\t\t}\n\t}\n\n\t@Override\n\tpublic String[] getDataList(BareJID user_id, final String subnode, final String key)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\n\t\t// String[] cache_res = (String[])cache.get(user_id+\"/\"+subnode+\"/\"+key);\n\t\t// if (cache_res != null) {\n\t\t// return cache_res;\n\t\t// } // end of if (result != null)\n\n\t\ttry {\n\t\t\tlong nid = getNodeNID(null, user_id, subnode);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Loading data for key: {0}, user: {1}, node: {2}, found nid: {3}\",\n\t\t\t\t\t\tnew Object[]{key, user_id, subnode, nid});\n\t\t\t}\n\n\t\t\tif (nid > 0) {\n\t\t\t\tResultSet rs = null;\n\n\t\t\t\tPreparedStatement data_for_node_st = data_repo.getPreparedStatement(user_id, DATA_FOR_NODE_QUERY);\n\n\t\t\t\tsynchronized (data_for_node_st) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tList<String> results = new ArrayList<String>();\n\n\t\t\t\t\t\tdata_for_node_st.setLong(1, nid);\n\t\t\t\t\t\tdata_for_node_st.setString(2, key);\n\t\t\t\t\t\trs = data_for_node_st.executeQuery();\n\t\t\t\t\t\twhile (rs.next()) {\n\t\t\t\t\t\t\tresults.add(rs.getString(1));\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Found data: {0}\", rs.getString(1));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tString[] result = (results.size() == 0) ? null : results.toArray(new String[results.size()]);\n\n\t\t\t\t\t\t// cache.put(user_id+\"/\"+subnode+\"/\"+key, result);\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn null;\n\t\t\t}    // end of if (nid > 0) else\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Error getting data list for: \" + user_id + \"/\" + subnode + \"/\" + key, e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String[] getKeys(BareJID user_id, final String subnode) throws UserNotFoundException, TigaseDBException {\n\n\t\ttry {\n\t\t\tlong nid = getNodeNID(null, user_id, subnode);\n\n\t\t\tif (nid > 0) {\n\t\t\t\tResultSet rs = null;\n\t\t\t\tList<String> results = new ArrayList<String>();\n\t\t\t\tPreparedStatement keys_for_node_st = data_repo.getPreparedStatement(user_id, KEYS_FOR_NODE_QUERY);\n\n\t\t\t\tsynchronized (keys_for_node_st) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tkeys_for_node_st.setLong(1, nid);\n\t\t\t\t\t\trs = keys_for_node_st.executeQuery();\n\t\t\t\t\t\twhile (rs.next()) {\n\t\t\t\t\t\t\tresults.add(rs.getString(1));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn (results.size() == 0) ? null : results.toArray(new String[results.size()]);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn null;\n\t\t\t}    // end of if (nid > 0) else\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Error getting subnodes list.\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String[] getKeys(BareJID user_id) throws UserNotFoundException, TigaseDBException {\n\t\treturn getKeys(user_id, null);\n\t}\n\n\t@Override\n\tpublic String getResourceUri() {\n\t\treturn data_repo.getResourceUri();\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"Support for multi-level nodes will be removed\")\n\tpublic String[] getSubnodes(BareJID user_id, final String subnode) throws UserNotFoundException, TigaseDBException {\n\t\ttry {\n\t\t\tlong nid = getNodeNID(null, user_id, subnode);\n\t\t\tif (nid > 0) {\n\t\t\t\tResultSet rs = null;\n\t\t\t\tPreparedStatement nodes_for_node_st = data_repo.getPreparedStatement(user_id, NODES_FOR_NODE_QUERY);\n\n\t\t\t\tsynchronized (nodes_for_node_st) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tList<String> results = new ArrayList<String>();\n\n\t\t\t\t\t\tnodes_for_node_st.setLong(1, nid);\n\t\t\t\t\t\trs = nodes_for_node_st.executeQuery();\n\t\t\t\t\t\twhile (rs.next()) {\n\t\t\t\t\t\t\tresults.add(rs.getString(2));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn (results.size() == 0) ? null : results.toArray(new String[results.size()]);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn null;\n\t\t\t}    // end of if (nid > 0) else\n\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Error getting subnodes list.\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String[] getSubnodes(BareJID user_id) throws UserNotFoundException, TigaseDBException {\n\t\treturn getSubnodes(user_id, null);\n\t}\n\n\t@Override\n\tpublic long getUserUID(BareJID user_id) throws TigaseDBException {\n\t\ttry {\n\t\t\treturn getUserUID(null, user_id);\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Error retrieving user UID from repository: \", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic List<BareJID> getUsers() throws TigaseDBException {\n\t\tResultSet rs = null;\n\t\tList<BareJID> users = null;\n\n\t\ttry {\n\t\t\tPreparedStatement all_users_sp = data_repo.getPreparedStatement(null, get_users_query);\n\n\t\t\tsynchronized (all_users_sp) {\n\t\t\t\ttry {\n\t\t\t\t\t// Load all user ids from database\n\t\t\t\t\trs = all_users_sp.executeQuery();\n\t\t\t\t\tusers = new ArrayList<BareJID>(1000);\n\t\t\t\t\twhile (rs.next()) {\n\t\t\t\t\t\tusers.add(BareJID.bareJIDInstanceNS(rs.getString(1)));\n\t\t\t\t\t}    // end of while (rs.next())\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\trs = null;\n\t\t\t\t}\n\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Problem loading user list from repository\", e);\n\t\t}\n\t\treturn users;\n\t}\n\n\t@Override\n\tpublic long getActiveUsersCountIn(Duration duration) {\n\t\ttry {\n\t\t\tlong users = -1;\n\t\t\tResultSet rs = null;\n\t\t\tPreparedStatement active_users_count = data_repo.getPreparedStatement(null, COUNT_ACTIVE_USERS_QUERY);\n\n\t\t\tsynchronized (active_users_count) {\n\t\t\t\ttry {\n\t\t\t\t\t// Load all user count from database\n\t\t\t\t\tfinal Instant instant = Instant.now().minus(duration);\n\t\t\t\t\tactive_users_count.setTimestamp(1, Timestamp.from(instant));\n\t\t\t\t\trs = active_users_count.executeQuery();\n\n\t\t\t\t\tif (rs.next()) {\n\t\t\t\t\t\tusers = rs.getLong(1);\n\t\t\t\t\t} // end of while (rs.next())\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\trs = null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn users;\n\t\t} catch (SQLException e) {\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\t@Override\n\tpublic long getUsersCount() {\n\t\ttry {\n\t\t\tResultSet rs = null;\n\t\t\tlong users = -1;\n\t\t\tPreparedStatement users_count_sp = data_repo.getPreparedStatement(null, GET_USERS_COUNT_QUERY);\n\n\t\t\tsynchronized (users_count_sp) {\n\t\t\t\ttry {\n\t\t\t\t\t// Load all user count from database\n\t\t\t\t\trs = users_count_sp.executeQuery();\n\t\t\t\t\tif (rs.next()) {\n\t\t\t\t\t\tusers = rs.getLong(1);\n\t\t\t\t\t}    // end of while (rs.next())\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\trs = null;\n\n\t\t\t\t}\n\n\t\t\t\treturn users;\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\treturn -1;\n\n\t\t\t// throw new\n\t\t\t// TigaseDBException(\"Problem loading user list from repository\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic long getUsersCount(String domain) {\n\t\ttry {\n\t\t\tResultSet rs = null;\n\t\t\tlong users = -1;\n\t\t\tPreparedStatement users_domain_count_st = data_repo.getPreparedStatement(null,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t COUNT_USERS_FOR_DOMAIN_QUERY);\n\n\t\t\tsynchronized (users_domain_count_st) {\n\t\t\t\ttry {\n\t\t\t\t\t// Load all user count from database\n\t\t\t\t\tusers_domain_count_st.setString(1, \"%@\" + domain);\n\t\t\t\t\trs = users_domain_count_st.executeQuery();\n\t\t\t\t\tif (rs.next()) {\n\t\t\t\t\t\tusers = rs.getLong(1);\n\t\t\t\t\t}    // end of while (rs.next())\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\trs = null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn users;\n\t\t} catch (SQLException e) {\n\t\t\treturn -1;\n\n\t\t\t// throw new\n\t\t\t// TigaseDBException(\"Problem loading user list from repository\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setDataSource(DataRepository dataSource) throws DBInitException {\n\t\tdata_repo = dataSource;\n\n\t\tString connection_str = data_repo.getResourceUri();\n\t\ttry {\n\t\t\tif (connection_str.contains(\"autoCreateUser=true\")) {\n\t\t\t\tautoCreateUser = true;\n\t\t\t}    // end of if (db_conn.contains())\n\t\t\tif (connection_str.contains(\"cacheRepo=off\")) {\n\t\t\t\tlog.fine(\"Disabling cache.\");\n\t\t\t\tcache = new RepoNoCache();\n\t\t\t} else {\n\t\t\t\tcache = new RepoCache(10000, 60 * 1000);\n\t\t\t}\n\t\t\tdata_repo.initPreparedStatement(GET_USER_DB_UID_QUERY, GET_USER_DB_UID_QUERY);\n\t\t\tdata_repo.initPreparedStatement(GET_USERS_COUNT_QUERY, GET_USERS_COUNT_QUERY);\n\t\t\tif (connection_str.startsWith(\"jdbc:postgresql\")) {\n\t\t\t\tget_users_query = PGSQL_GET_USERS_QUERY;\n\t\t\t} else {\n\t\t\t\tget_users_query = DEF_GET_USERS_QUERY;\n\t\t\t}\n\t\t\tdata_repo.initPreparedStatement(get_users_query, get_users_query);\n\t\t\tdata_repo.initPreparedStatement(ADD_USER_PLAIN_PW_QUERY, ADD_USER_PLAIN_PW_QUERY);\n\t\t\tdata_repo.initPreparedStatement(REMOVE_USER_QUERY, REMOVE_USER_QUERY);\n\t\t\tdata_repo.initPreparedStatement(ADD_NODE_QUERY, ADD_NODE_QUERY);\n\t\t\tdata_repo.initPreparedStatement(COUNT_USERS_FOR_DOMAIN_QUERY, COUNT_USERS_FOR_DOMAIN_QUERY);\n\t\t\tdata_repo.initPreparedStatement(COUNT_ACTIVE_USERS_QUERY, COUNT_ACTIVE_USERS_QUERY);\n\t\t\tdata_repo.initPreparedStatement(DATA_FOR_NODE_QUERY, DATA_FOR_NODE_QUERY);\n\t\t\tdata_repo.initPreparedStatement(KEYS_FOR_NODE_QUERY, KEYS_FOR_NODE_QUERY);\n\t\t\tdata_repo.initPreparedStatement(KEYS_DATA_FOR_NODE_QUERY, KEYS_DATA_FOR_NODE_QUERY);\n\t\t\tdata_repo.initPreparedStatement(USER_KEY_VALUE_MAP_QUERY, USER_KEY_VALUE_MAP_QUERY);\n\t\t\tdata_repo.initPreparedStatement(NODES_FOR_NODE_QUERY, NODES_FOR_NODE_QUERY);\n\t\t\tdata_repo.initPreparedStatement(INSERT_KEY_VAL_QUERY, INSERT_KEY_VAL_QUERY);\n\t\t\tdata_repo.initPreparedStatement(REMOVE_KEY_DATA_QUERY, REMOVE_KEY_DATA_QUERY);\n\t\t\tdata_repo.initPreparedStatement(UPDATE_PAIRS_QUERY, UPDATE_PAIRS_QUERY);\n\t\t\tdata_repo.initPreparedStatement(UPDATE_LAST_LOGIN_QUERY, UPDATE_LAST_LOGIN_QUERY);\n\t\t\tauth = new AuthRepositoryImpl(this);\n\n\t\t\t// initRepo();\n\t\t\tlog.log(Level.CONFIG, \"Initialized database connection: {0}\", JDBCPasswordObfuscator.obfuscatePassword(connection_str));\n\t\t} catch (SQLException ex) {\n\t\t\tdata_repo = null;\n\t\t\tthrow new DBInitException(\"Could not initialize repository\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isMechanismSupported(String domain, String mechanism) {\n\t\treturn auth.isMechanismSupported(domain, mechanism);\n\t}\n\n\t@Override\n\t@Deprecated\n\tpublic void initRepository(final String connection_str, Map<String, String> params) throws DBInitException {\n\t\ttry {\n\t\t\tif (data_repo == null) {\n\t\t\t\tfinal DataRepository dataRepository = RepositoryFactory.getDataRepository(null, connection_str, params);\n\t\t\t\tdataRepository.checkSchemaVersion(this, true);\n\t\t\t\tsetDataSource(dataRepository);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new DBInitException(\"Problem initializing jdbc connection: \" + connection_str, e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void logout(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\tauth.logout(user);\n\t}\n\n\t@Override\n\tpublic void loggedIn(BareJID user) throws TigaseDBException {\n\t\tauth.loggedIn(user);\n\t}\n\n\t@Override\n\tpublic boolean otherAuth(final Map<String, Object> props)\n\t\t\tthrows UserNotFoundException, TigaseDBException, AuthorizationException {\n\t\treturn auth.otherAuth(props);\n\t}\n\n\t@Override\n\tpublic void queryAuth(Map<String, Object> authProps) {\n\t\tauth.queryAuth(authProps);\n\t}\n\n\t@Override\n\tpublic void removeData(BareJID user_id, final String subnode, final String key)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\tremoveData(null, user_id, subnode, key);\n\t}\n\n\t// Implementation of tigase.db.AuthRepository\n\n\t@Override\n\tpublic void removeData(BareJID user_id, final String key) throws UserNotFoundException, TigaseDBException {\n\t\tremoveData(user_id, null, key);\n\t}\n\n\t@Override\n\tpublic void removeSubnode(BareJID user_id, final String subnode) throws UserNotFoundException, TigaseDBException {\n\t\tif (subnode == null) {\n\t\t\treturn;\n\t\t}    // end of if (subnode == null)\n\t\ttry {\n\t\t\tString[] subnodes = getSubnodes(user_id, subnode);\n\t\t\tif (subnodes != null && subnodes.length > 0) {\n\t\t\t\tfor (String innerSubNode : subnodes) {\n\t\t\t\t\tremoveSubnode(user_id, subnode + \"/\" + innerSubNode);\n\t\t\t\t}\n\t\t\t}\n\t\t\tlong nid = getNodeNID(null, user_id, subnode);\n\n\t\t\tif (nid > 0) {\n\t\t\t\tdeleteSubnode(null, nid);\n\t\t\t\tcache.remove(user_id + \"/\" + subnode);\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Error getting subnodes list.\", e);\n\t\t}\n\t}\n\n\t/**\n\t * <code>removeUser</code> method is thread safe. It uses local variable for storing <code>Statement</code>.\n\t *\n\t * @param user_id a <code>String</code> value the user Jabber ID.\n\t *\n\t * @throws UserNotFoundException if an error occurs\n\t */\n\t@Override\n\tpublic void removeUser(BareJID user_id) throws UserNotFoundException, TigaseDBException {\n\t\tStatement stmt = null;\n\t\tString query = null;\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Removing user: {0}\", user_id);\n\t\t}\n\t\ttry {\n\t\t\tstmt = data_repo.createStatement(user_id);\n\n\t\t\t// Get user account uid\n\t\t\tlong uid = getUserUID(null, user_id, autoCreateUser);\n\n\t\t\t// Remove all user enrties from pairs table\n\t\t\tquery = \"delete from \" + DEF_PAIRS_TBL + \" where uid = \" + uid;\n\t\t\tstmt.executeUpdate(query);\n\n\t\t\t// Remove all user entries from nodes table\n\t\t\tquery = \"delete from \" + DEF_NODES_TBL + \" where uid = \" + uid;\n\t\t\tstmt.executeUpdate(query);\n\n\t\t\tPreparedStatement user_del_sp = data_repo.getPreparedStatement(user_id, REMOVE_USER_QUERY);\n\n\t\t\t// Remove user account from users table\n\t\t\tsynchronized (user_del_sp) {\n\t\t\t\tuser_del_sp.setString(1, user_id.toString());\n\t\t\t\tuser_del_sp.executeUpdate();\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Error removing user from repository: \" + query, e);\n\t\t} finally {\n\t\t\tdata_repo.release(stmt, null);\n\t\t\tstmt = null;\n\t\t\tcache.remove(user_id.toString());\n\n\t\t\t// cache.clear();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setData(BareJID user_id, final String subnode, final String key, final String value)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\tlong uid = -2;\n\t\tlong nid = -2;\n\t\tDataRepository repo = data_repo.takeRepoHandle(user_id);\n\n\t\tsynchronized (repo) {\n\t\t\ttry {\n\t\t\t\tuid = getUserUID(repo, user_id, autoCreateUser);\n\t\t\t\tnid = getNodeNID(repo, uid, subnode);\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Saving data setting data, user_id: {0}, subnode: {1}, key: {2}, \" +\n\t\t\t\t\t\t\t\"uid: {3}, nid: {4}, value: {5}\", new Object[]{user_id, subnode, key, uid, nid, value});\n\t\t\t\t}\n\t\t\t\tif (nid < 0) {\n\t\t\t\t\ttry {\n\n\t\t\t\t\t\t// OK\n\t\t\t\t\t\tnid = createNodePath(repo, user_id, subnode);\n\t\t\t\t\t} catch (SQLException e) {\n\n\t\t\t\t\t\t// This may happen in cluster node, when 2 nodes at the same\n\t\t\t\t\t\t// time write data to the same location, like offline messages....\n\t\t\t\t\t\t// Let's try to get the nid again.\n\t\t\t\t\t\t// OK\n\t\t\t\t\t\tnid = getNodeNID(repo, uid, subnode);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tPreparedStatement update_pairs_sp = repo.getPreparedStatement(user_id, UPDATE_PAIRS_QUERY);\n\n\t\t\t\tupdate_pairs_sp.setLong(1, nid);\n\t\t\t\tupdate_pairs_sp.setLong(2, uid);\n\t\t\t\tupdate_pairs_sp.setString(3, key);\n\t\t\t\tswitch (data_repo.getDatabaseType()) {\n\t\t\t\t\tcase derby:\n\t\t\t\t\t\t// When commit() is called on the connection all CLOB instances are freed so each next request\n\t\t\t\t\t\t// to them may throw NPE!!! Instead, if we set character stream, then CLOB is created inside\n\t\t\t\t\t\t// executeUpdate() by DerbyDB, which is inside block which is synchronized on the internal\n\t\t\t\t\t\t// connection instance.\n\n\t\t\t\t\t\tupdate_pairs_sp.setCharacterStream(4, new StringReader(value));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tupdate_pairs_sp.setString(4, value);\n\t\t\t\t}\n\t\t\t\tupdate_pairs_sp.executeUpdate();\n\t\t\t} catch (SQLException e) {\n\t\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\t\"Error setting data , user_id: \" + user_id + \", subnode: \" + subnode + \", key: \" + key +\n\t\t\t\t\t\t\t\t\", uid: \" + uid + \", nid: \" + nid + \", value: \" + value, e);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setData(BareJID user_id, final String key, final String value)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\tsetData(user_id, null, key, value);\n\t}\n\n\t@Override\n\tpublic void setDataList(BareJID user_id, final String subnode, final String key, final String[] list)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\n\t\t// Transactions may not yet work properly but at least let's make sure\n\t\t// both calls below are executed exclusively on the same DB connection\n\t\tDataRepository repo = data_repo.takeRepoHandle(user_id);\n\n\t\tsynchronized (repo) {\n\t\t\ttry {\n\t\t\t\tremoveData(repo, user_id, subnode, key);\n\t\t\t\ttry {\n\t\t\t\t\taddDataList(repo, user_id, subnode, key, list);\n\t\t\t\t} catch (SQLException ex) {\n\t\t\t\t\tthrow new TigaseDBException(\n\t\t\t\t\t\t\t\"Problem adding data to DB, user_id: \" + user_id + \", subnode: \" + subnode + \", key: \" +\n\t\t\t\t\t\t\t\t\tkey + \", list: \" + Arrays.toString(list), ex);\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tdata_repo.releaseRepoHandle(repo);\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void updateCredential(BareJID user, String credentialId, String mechanism, String data)\n\t\t\tthrows TigaseDBException {\n\t\tauth.updateCredential(user, credentialId, mechanism, data);\n\t}\n\n\t@Override\n\tpublic void updateCredential(BareJID user, String credentialId, String password) throws TigaseDBException {\n\t\tauth.updateCredential(user, credentialId, password);\n\t}\n\n\t@Override\n\tpublic void updatePassword(BareJID user, final String password) throws TigaseDBException {\n\t\tauth.updatePassword(user, password);\n\t}\n\n\t@Override\n\tpublic boolean userExists(BareJID user) {\n\t\ttry {\n\t\t\treturn getUserUID(null, user) > 0;\n\t\t} catch (SQLException e) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getPassword(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\t// TODO Auto-generated method stub\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean isUserDisabled(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\treturn auth.isUserDisabled(user);\n\t}\n\n\t@Override\n\tpublic void setAccountStatus(BareJID user, AccountStatus status) throws TigaseDBException {\n\t\tauth.setAccountStatus(user, status);\n\t}\n\n\t@Override\n\tpublic AccountStatus getAccountStatus(BareJID user) throws TigaseDBException {\n\t\treturn auth.getAccountStatus(user);\n\t}\n\n\t@Override\n\tpublic void setUserDisabled(BareJID user, Boolean value) throws UserNotFoundException, TigaseDBException {\n\t\tauth.setUserDisabled(user, value);\n\t}\n\n\tprotected DataRepository getRepository() {\n\t\treturn data_repo;\n\t}\n\n\tprotected boolean isExceptionKeyViolation(SQLException ex) {\n\t\t// sqlState may be NULL!!\n\t\tString sqlState = ex.getSQLState();\n\t\tboolean keyViolation = false;\n\t\tswitch (data_repo.getDatabaseType()) {\n\n\t\t\tcase derby:\n\t\t\t\tkeyViolation = (\"X0Y78\".equals(sqlState));\n\t\t\t\tbreak;\n\t\t\tcase postgresql:\n\t\t\t\tkeyViolation = (\"23505\".equals(sqlState) || \"23000\".equals(sqlState));\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tkeyViolation = \"23000\".equals(sqlState);\n\t\t\t\tbreak;\n\t\t}\n\t\treturn keyViolation;\n\t}\n\n\t// ~--- methods --------------------------------------------------------------\n\tprivate void addDataList(DataRepository repo, BareJID user_id, final String subnode, final String key,\n\t\t\t\t\t\t\t final String[] list) throws UserNotFoundException, SQLException, UserNotFoundException {\n\t\tlong uid = -2;\n\t\tlong nid = -2;\n\n\t\ttry {\n\n\t\t\t// OK\n\t\t\tuid = getUserUID(repo, user_id, autoCreateUser);\n\n\t\t\t// OK\n\t\t\tnid = getNodeNID(repo, uid, subnode);\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Saving data adding data list, user_id: {0}, subnode: {1}, key: {2}, \" +\n\t\t\t\t\t\t\t\t\"uid: {3}, nid: {4}, list: {5}\",\n\t\t\t\t\t\tnew Object[]{user_id, subnode, key, uid, nid, Arrays.toString(list)});\n\t\t\t}\n\t\t\tif (nid < 0) {\n\t\t\t\ttry {\n\n\t\t\t\t\t// OK\n\t\t\t\t\tnid = createNodePath(repo, user_id, subnode);\n\t\t\t\t} catch (SQLException e) {\n\n\t\t\t\t\t// This may happen in cluster node, when 2 nodes at the same\n\t\t\t\t\t// time write data to the same location, like offline messages....\n\t\t\t\t\t// Let's try to get the nid again.\n\t\t\t\t\t// OK\n\t\t\t\t\tnid = getNodeNID(repo, uid, subnode);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tPreparedStatement insert_key_val_st = null;\n\n\t\t\tif (repo == null) {\n\t\t\t\tinsert_key_val_st = data_repo.getPreparedStatement(user_id, INSERT_KEY_VAL_QUERY);\n\t\t\t} else {\n\t\t\t\tinsert_key_val_st = repo.getPreparedStatement(user_id, INSERT_KEY_VAL_QUERY);\n\t\t\t}\n\t\t\tsynchronized (insert_key_val_st) {\n\t\t\t\tinsert_key_val_st.setLong(1, nid);\n\t\t\t\tinsert_key_val_st.setLong(2, uid);\n\t\t\t\tinsert_key_val_st.setString(3, key);\n\t\t\t\tfor (String val : list) {\n\t\t\t\t\tinsert_key_val_st.setString(4, val);\n\t\t\t\t\tinsert_key_val_st.executeUpdate();\n\t\t\t\t}    // end of for (String val: list)\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Error adding data list, user_id: \" + user_id + \", subnode: \" + subnode + \", key: \" + key +\n\t\t\t\t\t\t\t\", uid: \" + uid + \", nid: \" + nid + \", list: \" + Arrays.toString(list), e);\n\n\t\t\tthrow e;\n\t\t}\n\n\t\t// cache.put(user_id+\"/\"+subnode+\"/\"+key, list);\n\t}\n\n\tprivate void removeData(DataRepository repo, BareJID user_id, final String subnode, final String key)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\n\t\t// cache.remove(user_id+\"/\"+subnode+\"/\"+key);\n\t\ttry {\n\t\t\tlong nid = getNodeNID(repo, user_id, subnode);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Removing data, user_id: {0}, subnode: {1}, key: {2}, nid: {3}\",\n\t\t\t\t\t\tnew Object[]{user_id, subnode, key, nid});\n\t\t\t}\n\n\t\t\tPreparedStatement remove_key_data_st = null;\n\n\t\t\tif (repo == null) {\n\t\t\t\tremove_key_data_st = data_repo.getPreparedStatement(user_id, REMOVE_KEY_DATA_QUERY);\n\t\t\t} else {\n\t\t\t\tremove_key_data_st = repo.getPreparedStatement(user_id, REMOVE_KEY_DATA_QUERY);\n\t\t\t}\n\t\t\tif (nid > 0) {\n\t\t\t\tsynchronized (remove_key_data_st) {\n\t\t\t\t\tremove_key_data_st.setLong(1, nid);\n\t\t\t\t\tremove_key_data_st.setString(2, key);\n\t\t\t\t\tremove_key_data_st.executeUpdate();\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Error getting subnodes list.\", e);\n\t\t}\n\t}\n\n\tprivate long addNode(DataRepository repo, long uid, long parent_nid, String node_name) throws SQLException {\n\t\tResultSet rs = null;\n\t\tPreparedStatement node_add_sp = null;\n\n\t\tif (repo == null) {\n\t\t\tnode_add_sp = data_repo.getPreparedStatement(null, ADD_NODE_QUERY);\n\t\t} else {\n\t\t\tnode_add_sp = repo.getPreparedStatement(null, ADD_NODE_QUERY);\n\t\t}\n\t\tsynchronized (node_add_sp) {\n\t\t\ttry {\n\t\t\t\tif (parent_nid < 0) {\n\t\t\t\t\tnode_add_sp.setNull(1, Types.BIGINT);\n\t\t\t\t} else {\n\t\t\t\t\tnode_add_sp.setLong(1, parent_nid);\n\t\t\t\t}    // end of else\n\t\t\t\tnode_add_sp.setLong(2, uid);\n\t\t\t\tnode_add_sp.setString(3, node_name);\n\n\t\t\t\tswitch (data_repo.getDatabaseType()) {\n//\t\t\t\t\tcase sqlserver:\n//\t\t\t\t\t\tnode_add_sp.executeUpdate();\n//\t\t\t\t\t\trs = node_add_sp.getGeneratedKeys();\n//\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\trs = node_add_sp.executeQuery();\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (rs.next()) {\n\t\t\t\t\treturn rs.getLong(1);\n\t\t\t\t} else {\n\t\t\t\t\tlog.warning(\"Missing NID after adding new node...\");\n\n\t\t\t\t\treturn -1;\n\n\t\t\t\t\t// throw new TigaseDBException(\"Propeblem adding new node. \"\n\t\t\t\t\t// + \"The SP should return nid or fail\");\n\t\t\t\t}    // end of if (isnext) else\n\t\t\t} finally {\n\t\t\t\tdata_repo.release(null, rs);\n\t\t\t}\n\t\t}\n\n\t\t// return new_nid;\n\t}\n\n\t/**\n\t * <code>addUserRepo</code> method is thread safe. It uses local variable for storing <code>Statement</code>.\n\t *\n\t * @param user_id a <code>String</code> value of the user ID.\n\t *\n\t * @return a <code>long</code> value of <code>uid</code> database user ID.\n\t *\n\t * @throws SQLException if an error occurs\n\t * @throws UserExistsException if the user already exists in repository\n\t */\n\tprivate long addUserRepo(DataRepository repo, BareJID user_id) throws SQLException, UserExistsException {\n\t\tResultSet rs = null;\n\t\tlong uid = -1;\n\t\tPreparedStatement user_add_sp = null;\n\n\t\tif (repo == null) {\n\t\t\tuser_add_sp = data_repo.getPreparedStatement(user_id, ADD_USER_PLAIN_PW_QUERY);\n\t\t} else {\n\t\t\tuser_add_sp = repo.getPreparedStatement(user_id, ADD_USER_PLAIN_PW_QUERY);\n\t\t}\n\t\tsynchronized (user_add_sp) {\n\t\t\ttry {\n\t\t\t\tuser_add_sp.setString(1, user_id.toString());\n\t\t\t\tuser_add_sp.setNull(2, Types.VARCHAR);\n\n\t\t\t\tlog.log(Level.FINEST, \"Adding non existing user to user-repository: \" + user_id.toString());\n\n\t\t\t\tswitch (data_repo.getDatabaseType()) {\n\t\t\t\t\tdefault:\n\t\t\t\t\t\trs = user_add_sp.executeQuery();\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (rs != null && rs.next()) {\n\t\t\t\t\tuid = rs.getLong(1);\n\n\t\t\t\t\t// addNode(uid, -1, root_node);\n\t\t\t\t} else {\n\t\t\t\t\tlog.warning(\"Missing UID after adding new user...\");\n\t\t\t\t}    // end of if (isnext) else\n\t\t\t} catch (SQLException ex) {\n\t\t\t\tif (isExceptionKeyViolation(ex)) {\n\t\t\t\t\t// remove entry from cache as if cache was enabled and we ended here it may have incorrect value\n\t\t\t\t\tcache.remove(user_id.toString());\n\t\t\t\t\tthrow new UserExistsException(user_id, \"User already exist in the database\", ex);\n\t\t\t\t} else {\n\t\t\t\t\tthrow ex;\n\t\t\t\t}\n\n\t\t\t} finally {\n\t\t\t\tdata_repo.release(null, rs);\n\t\t\t}\n\t\t}\n\t\tcache.put(user_id.toString(), Long.valueOf(uid));\n\n\t\treturn uid;\n\t}\n\n\tprivate String buildNodeQuery(long uid, String node_path) {\n\t\tString query = \"select nid as nid1 from \" + DEF_NODES_TBL + \" where (uid = \" + uid + \")\" +\n\t\t\t\t\" AND (parent_nid is null)\" + \" AND (node = '\" + DEF_ROOT_NODE + \"')\";\n\n\t\tif (node_path == null) {\n\t\t\treturn query;\n\t\t} else {\n\t\t\tStringTokenizer strtok = new StringTokenizer(node_path, \"/\", false);\n\t\t\tint cnt = 1;\n\t\t\tString subquery = query;\n\n\t\t\twhile (strtok.hasMoreTokens()) {\n\t\t\t\tString token = strtok.nextToken();\n\n\t\t\t\t++cnt;\n\t\t\t\tsubquery = \"select nid as nid\" + cnt + \", node as node\" + cnt + \" from \" + DEF_NODES_TBL + \", (\" +\n\t\t\t\t\t\tsubquery + \") nodes\" + (cnt - 1) + \" where (parent_nid = nid\" + (cnt - 1) + \")\" +\n\t\t\t\t\t\t\" AND (node = '\" + token + \"')\";\n\t\t\t}    // end of while (strtok.hasMoreTokens())\n\n\t\t\treturn subquery;\n\t\t}      // end of else\n\t}\n\n\tprivate long createNodePath(DataRepository repo, BareJID user_id, String node_path)\n\t\t\tthrows SQLException, UserNotFoundException {\n\t\tif (node_path == null) {\n\n\t\t\t// Or should I throw NullPointerException?\n\t\t\t// OK\n\t\t\treturn getNodeNID(repo, user_id, null);\n\t\t}    // end of if (node_path == null)\n\n\t\t// OK\n\t\tlong uid = getUserUID(repo, user_id, autoCreateUser);\n\n\t\t// OK\n\t\tlong nid = getNodeNID(repo, uid, null);\n\t\tStringTokenizer strtok = new StringTokenizer(node_path, \"/\", false);\n\t\tStringBuilder built_path = new StringBuilder();\n\n\t\twhile (strtok.hasMoreTokens()) {\n\t\t\tString token = strtok.nextToken();\n\n\t\t\tbuilt_path.append(\"/\").append(token);\n\n\t\t\t// OK\n\t\t\tlong cur_nid = getNodeNID(repo, uid, built_path.toString());\n\n\t\t\tif (cur_nid > 0) {\n\t\t\t\tnid = cur_nid;\n\t\t\t} else {\n\n\t\t\t\t// OK\n\t\t\t\tnid = addNode(repo, uid, nid, token);\n\t\t\t}    // end of if (cur_nid > 0) else\n\t\t}      // end of while (strtok.hasMoreTokens())\n\n\t\treturn nid;\n\t}\n\n\tprivate void deleteSubnode(DataRepository repo, long nid) throws SQLException {\n\t\tStatement stmt = null;\n\t\tResultSet rs = null;\n\t\tString query = null;\n\n\t\ttry {\n\t\t\tif (repo == null) {\n\t\t\t\tstmt = data_repo.createStatement(null);\n\t\t\t} else {\n\t\t\t\tstmt = repo.createStatement(null);\n\t\t\t}\n\t\t\tquery = \"delete from \" + DEF_PAIRS_TBL + \" where nid = \" + nid;\n\t\t\tstmt.executeUpdate(query);\n\t\t\tquery = \"delete from \" + DEF_NODES_TBL + \" where nid = \" + nid;\n\t\t\tstmt.executeUpdate(query);\n\t\t} finally {\n\t\t\tdata_repo.release(stmt, rs);\n\t\t}\n\t}\n\n\tprivate long getNodeNID(DataRepository repo, long uid, String node_path)\n\t\t\tthrows SQLException, UserNotFoundException {\n\t\tString query = buildNodeQuery(uid, node_path);\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(query);\n\t\t}\n\n\t\tStatement stmt = null;\n\t\tResultSet rs = null;\n\t\tlong nid = -1;\n\n\t\ttry {\n\t\t\tif (repo == null) {\n\t\t\t\tstmt = data_repo.createStatement(null);\n\t\t\t} else {\n\t\t\t\tstmt = repo.createStatement(null);\n\t\t\t}\n\t\t\trs = stmt.executeQuery(query);\n\t\t\tif (rs.next()) {\n\t\t\t\tnid = rs.getLong(1);\n\t\t\t} else {\n\t\t\t\tnid = -1;\n\t\t\t}    // end of if (isnext) else\n\t\t\tif (nid <= 0) {\n\t\t\t\tif (node_path == null) {\n\t\t\t\t\tlog.log(Level.CONFIG, \"Missing root node, database upgrade or bug in the code? Adding missing \" +\n\t\t\t\t\t\t\t\t\t \"root node now.\");\n\n\t\t\t\t\t// OK\n\t\t\t\t\tnid = addNode(repo, uid, -1, \"root\");\n\t\t\t\t} else {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Missing nid for node path: {0} and uid: {1}\",\n\t\t\t\t\t\t\t\tnew Object[]{node_path, uid});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn nid;\n\t\t} finally {\n\t\t\tdata_repo.release(stmt, rs);\n\t\t\tstmt = null;\n\t\t\trs = null;\n\t\t}\n\t}\n\n\tprivate long getNodeNID(DataRepository repo, BareJID user_id, String node_path)\n\t\t\tthrows SQLException, UserNotFoundException {\n\t\tLong cache_res = (Long) cache.get(user_id + \"/\" + node_path);\n\n\t\tif (cache_res != null) {\n\t\t\treturn cache_res.longValue();\n\t\t}    // end of if (result != null)\n\n\t\t// OK\n\t\tlong uid = getUserUID(repo, user_id, autoCreateUser);\n\n\t\t// OK\n\t\tlong result = getNodeNID(repo, uid, node_path);\n\n\t\tif (result > 0) {\n\t\t\tcache.put(user_id + \"/\" + node_path, Long.valueOf(result));\n\t\t}    // end of if (result > 0)\n\n\t\treturn result;\n\t}\n\n\tprivate long getUserUID(DataRepository repo, BareJID user_id) throws SQLException {\n\t\tLong cache_res = (Long) cache.get(user_id.toString());\n\n\t\tif (cache_res != null) {\n\t\t\treturn cache_res.longValue();\n\t\t}    // end of if (result != null)\n\n\t\tlong result = getUserUIDDirect(repo, user_id);\n\t\tcache.put(user_id.toString(), Long.valueOf(result));\n\n\t\treturn result;\n\t}\n\n\tprivate long getUserUIDDirect(DataRepository repo, BareJID user_id) throws SQLException {\n\t\tlong result = -1;\n\n\t\tResultSet rs = null;\n\t\tPreparedStatement uid_sp = null;\n\n\t\tif (repo == null) {\n\t\t\tuid_sp = data_repo.getPreparedStatement(user_id, GET_USER_DB_UID_QUERY);\n\t\t} else {\n\t\t\tuid_sp = repo.getPreparedStatement(user_id, GET_USER_DB_UID_QUERY);\n\t\t}\n\t\tsynchronized (uid_sp) {\n\t\t\ttry {\n\t\t\t\tuid_sp.setString(1, user_id.toString());\n\t\t\t\trs = uid_sp.executeQuery();\n\t\t\t\tif (rs.next()) {\n\t\t\t\t\tresult = rs.getLong(1);\n\t\t\t\t} else {\n\t\t\t\t\tresult = -1;\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tdata_repo.release(null, rs);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate long getUserUID(DataRepository repo, BareJID user_id, boolean autoCreate)\n\t\t\tthrows SQLException, UserNotFoundException {\n\n\t\t// OK\n\t\tlong result = getUserUID(repo, user_id);\n\n\t\tif (result <= 0) {\n\t\t\tif (autoCreate) {\n\n\t\t\t\t// OK\n\t\t\t\ttry {\n\t\t\t\t\tresult = addUserRepo(repo, user_id);\n\t\t\t\t} catch (UserExistsException ex) {\n\t\t\t\t\t// there was unique key violation which indicates that the user\n\t\t\t\t\t// already exists in the database therefore we can retrieve it's UID\n\t\t\t\t\tresult = getUserUID(repo, user_id);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthrow new UserNotFoundException(\"User does not exist: \" + user_id);\n\t\t\t}    // end of if (autoCreate) else\n\t\t}      // end of if (isnext) else\n\n\t\treturn result;\n\t}\n\n\tpublic interface IRepoCache<K,V> {\n\n\t\tV get(Object key);\n\n\t\tV put(K key, V value);\n\n\t\tV remove(Object key);\n\t}\n\n\tprivate class RepoNoCache implements IRepoCache<String, Object> {\n\n\t\t@Override\n\t\tpublic Object get(Object key) {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object put(String key, Object value) {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object remove(Object key) {\n\t\t\treturn null;\n\t\t}\n\t}\n\t// ~--- inner classes --------------------------------------------------------\n\tprivate class RepoCache\n\t\t\textends SimpleCache<String, Object> implements IRepoCache<String, Object> {\n\n\t\tpublic RepoCache(int maxsize, long cache_time) {\n\t\t\tsuper(maxsize, cache_time);\n\t\t}\n\n\t\t@Override\n\t\tpublic Object get(Object key) {\n\t\t\tif (cache_off) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tsynchronized (this) {\n\t\t\t\treturn super.get(key);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic Object put(String key, Object value) {\n\t\t\tif (cache_off) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tsynchronized (this) {\n\t\t\t\treturn super.put(key, value);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic Object remove(Object key) {\n\t\t\tif (cache_off) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tsynchronized (this) {\n\t\t\t\tObject val = super.remove(key);\n\t\t\t\tString strk = key.toString();\n\t\t\t\tIterator<String> ks = keySet().iterator();\n\n\t\t\t\twhile (ks.hasNext()) {\n\t\t\t\t\tString k = ks.next().toString();\n\n\t\t\t\t\tif (k.startsWith(strk)) {\n\t\t\t\t\t\tks.remove();\n\t\t\t\t\t}    // end of if (k.startsWith(strk))\n\t\t\t\t}      // end of while (ks.hasNext())\n\n\t\t\t\treturn val;\n\t\t\t}\n\t\t}\n\t}\n}    // JDBCRepository\n\n"
  },
  {
    "path": "src/main/java/tigase/db/jdbc/PreparedStatementInvocationHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.jdbc;\n\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.UndeclaredThrowableException;\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class PreparedStatementInvocationHandler\n\t\timplements InvocationHandler {\n\n\tprivate final static Logger log = Logger.getLogger(PreparedStatementInvocationHandler.class.getName());\n\n\tprivate final PreparedStatement ps;\n\n\tpublic PreparedStatementInvocationHandler(PreparedStatement ps) {\n\t\tthis.ps = ps;\n\t}\n\n\t@Override\n\tpublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n\t\ttry {\n\t\t\treturn method.invoke(ps, args);\n\t\t} catch (Throwable ex) {\n\t\t\tif (isReadOnlyException(ex)) {\n\t\t\t\tlog.log(Level.FINEST, \"Database connection threw read-only exception, marking it as such co reinitialise it: \" + ps.getConnection());\n\t\t\t\tps.getConnection().setReadOnly(true);\n\t\t\t}\n\t\t\tif (ex instanceof UndeclaredThrowableException) {\n\t\t\t\tex = ((UndeclaredThrowableException) ex).getUndeclaredThrowable();\n\t\t\t}\n\t\t\tif (ex instanceof InvocationTargetException) {\n\t\t\t\tthrow ((InvocationTargetException) ex).getTargetException();\n\t\t\t} else {\n\t\t\t\tthrow ex;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate boolean isMySqlReadOnly(SQLException sqlex) {\n\t\treturn sqlex.getErrorCode() == 1290 && \"HY000\".equals(sqlex.getSQLState());\n\t}\n\n\tprivate boolean isPostgresReadOnly(SQLException sqlex) {\n\t\treturn sqlex.getErrorCode() == 0 && \"25006\".equals(sqlex.getSQLState());\n\t}\n\n\tprivate boolean isReadOnlyException(Throwable ex) {\n\t\tif (ex instanceof InvocationTargetException) {\n\t\t\tex = ((InvocationTargetException) ex).getTargetException();\n\t\t}\n\t\tif (ex instanceof SQLException) {\n\t\t\tSQLException sqlex = (SQLException) ex;\n\t\t\treturn (isMySqlReadOnly(sqlex) || isPostgresReadOnly(sqlex));\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/jdbc/TigaseAuth.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.jdbc;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.*;\nimport tigase.db.util.RepositoryVersionAware;\nimport tigase.util.Base64;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.sql.*;\nimport java.time.Duration;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.db.AuthRepository.Meta;\n\n/**\n * Describe class TigaseAuth here.\n * <br>\n * Created: Sat Nov 11 22:22:04 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Meta(supportedUris = {\"jdbc:[^:]+:.*\"})\n@Repository.SchemaId(id = Schema.SERVER_SCHEMA_ID, name = Schema.SERVER_SCHEMA_NAME)\n@Deprecated\n@TigaseDeprecated(since = \"8.0.0\")\npublic class TigaseAuth\n\t\timplements AuthRepository, DataSourceAware<DataRepository>, RepositoryVersionAware {\n\n\tprivate static final Logger log = Logger.getLogger(\"tigase.db.jdbc.TigaseAuth\");\n\tprivate static final String[] non_sasl_mechs = {\"password\"};\n\tprivate static final String[] sasl_mechs = {\"PLAIN\"};\n\tprivate static final String INIT_DB_QUERY = \"{ call TigInitdb() }\";\n\tprivate static final String ADD_USER_PLAIN_PW_QUERY = \"{ call TigAddUserPlainPw(?, ?) }\";\n\tprivate static final String REMOVE_USER_QUERY = \"{ call TigRemoveUser(?) }\";\n\tprivate static final String GET_PASSWORD_QUERY = \"{ call TigGetPassword(?) }\";\n\tprivate static final String UPDATE_PASSWORD_PLAIN_PW_QUERY = \"{ call TigUpdatePasswordPlainPw(?, ?) }\";\n\tprivate static final String USER_LOGIN_PLAIN_PW_QUERY = \"{ call TigUserLoginPlainPw(?, ?) }\";\n\tprivate static final String USER_LOGOUT_QUERY = \"{ call TigUserLogout(?) }\";\n\tprivate static final String USERS_COUNT_QUERY = \"{ call TigAllUsersCount() }\";\n\tprivate static final String USERS_DOMAIN_COUNT_QUERY = \"select count(*) from tig_users where user_id like ?\";\n\tprivate static final String ACTIVE_USERS_COUNT_QUERY = \"select count(*) from tig_users where last_used >= now() - ?\";\n\tprivate static final String DEF_UPDATELOGINTIME_QUERY = \"{ call TigUpdateLoginTime(?) }\";\n\n\tprivate DataRepository data_repo = null;\n\n\t@Override\n\tpublic void addUser(BareJID user, final String password) throws UserExistsException, TigaseDBException {\n\t\tResultSet rs = null;\n\n\t\ttry {\n\t\t\tPreparedStatement add_user_plain_pw_sp = data_repo.getPreparedStatement(user, ADD_USER_PLAIN_PW_QUERY);\n\n\t\t\tsynchronized (add_user_plain_pw_sp) {\n\t\t\t\ttry {\n\t\t\t\t\tadd_user_plain_pw_sp.setString(1, user.toString());\n\t\t\t\t\tadd_user_plain_pw_sp.setString(2, password);\n\t\t\t\t\trs = add_user_plain_pw_sp.executeQuery();\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (SQLIntegrityConstraintViolationException e) {\n\t\t\tthrow new UserExistsException(\"Error while adding user to repository, user exists?\", e);\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Problem accessing repository.\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getResourceUri() {\n\t\treturn data_repo.getResourceUri();\n\t}\n\n\n\t@Override\n\tpublic long getActiveUsersCountIn(Duration duration) {\n\t\tResultSet rs = null;\n\n\t\ttry {\n\t\t\tlong users = -1;\n\t\t\tPreparedStatement active_users_count_st = data_repo.getPreparedStatement(null, USERS_DOMAIN_COUNT_QUERY);\n\n\t\t\tsynchronized (active_users_count_st) {\n\t\t\t\ttry {\n\t\t\t\t\t// Load all user count from database\n\t\t\t\t\tactive_users_count_st.setTime(1, Time.valueOf(\"0\"));\n\t\t\t\t\trs = active_users_count_st.executeQuery();\n\n\t\t\t\t\tif (rs.next()) {\n\t\t\t\t\t\tusers = rs.getLong(1);\n\t\t\t\t\t}    // end of while (rs.next())\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\trs = null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn users;\n\t\t} catch (SQLException e) {\n\t\t\treturn -1;\n\n\t\t\t// throw new TigaseDBException(\"Problem loading user list from repository\", e);\n\t\t}\n\t}\n\n\n\t/**\n\t * <code>getUsersCount</code> method is thread safe. It uses local variable for storing <code>Statement</code>.\n\t *\n\t * @return a <code>long</code> number of user accounts in database.\n\t */\n\t@Override\n\tpublic long getUsersCount() {\n\t\tResultSet rs = null;\n\n\t\ttry {\n\t\t\tlong users = -1;\n\t\t\tPreparedStatement users_count_sp = data_repo.getPreparedStatement(null, USERS_COUNT_QUERY);\n\n\t\t\tsynchronized (users_count_sp) {\n\t\t\t\ttry {\n\t\t\t\t\t// Load all user count from database\n\t\t\t\t\trs = users_count_sp.executeQuery();\n\n\t\t\t\t\tif (rs.next()) {\n\t\t\t\t\t\tusers = rs.getLong(1);\n\t\t\t\t\t}    // end of while (rs.next())\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\trs = null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn users;\n\t\t} catch (SQLException e) {\n\t\t\treturn -1;\n\n\t\t\t// throw new TigaseDBException(\"Problem loading user list from repository\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic long getUsersCount(String domain) {\n\t\tResultSet rs = null;\n\n\t\ttry {\n\t\t\tlong users = -1;\n\t\t\tPreparedStatement users_domain_count_st = data_repo.getPreparedStatement(null, USERS_DOMAIN_COUNT_QUERY);\n\n\t\t\tsynchronized (users_domain_count_st) {\n\t\t\t\ttry {\n\t\t\t\t\t// Load all user count from database\n\t\t\t\t\tusers_domain_count_st.setString(1, \"%@\" + domain);\n\t\t\t\t\trs = users_domain_count_st.executeQuery();\n\n\t\t\t\t\tif (rs.next()) {\n\t\t\t\t\t\tusers = rs.getLong(1);\n\t\t\t\t\t}    // end of while (rs.next())\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\trs = null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn users;\n\t\t} catch (SQLException e) {\n\t\t\treturn -1;\n\n\t\t\t// throw new TigaseDBException(\"Problem loading user list from repository\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setDataSource(DataRepository dataSource) throws DBInitException {\n\t\ttry {\n\t\t\tdata_repo = dataSource;\n\t\t\tdata_repo.initPreparedStatement(INIT_DB_QUERY, INIT_DB_QUERY);\n\t\t\tdata_repo.initPreparedStatement(ADD_USER_PLAIN_PW_QUERY, ADD_USER_PLAIN_PW_QUERY);\n\t\t\tdata_repo.initPreparedStatement(REMOVE_USER_QUERY, REMOVE_USER_QUERY);\n\t\t\tdata_repo.initPreparedStatement(GET_PASSWORD_QUERY, GET_PASSWORD_QUERY);\n\t\t\tdata_repo.initPreparedStatement(UPDATE_PASSWORD_PLAIN_PW_QUERY, UPDATE_PASSWORD_PLAIN_PW_QUERY);\n\t\t\tdata_repo.initPreparedStatement(USER_LOGIN_PLAIN_PW_QUERY, USER_LOGIN_PLAIN_PW_QUERY);\n\t\t\tdata_repo.initPreparedStatement(USER_LOGOUT_QUERY, USER_LOGOUT_QUERY);\n\t\t\tdata_repo.initPreparedStatement(USERS_COUNT_QUERY, USERS_COUNT_QUERY);\n\t\t\tdata_repo.initPreparedStatement(USERS_DOMAIN_COUNT_QUERY, USERS_DOMAIN_COUNT_QUERY);\n\t\t\tdata_repo.initPreparedStatement(DEF_UPDATELOGINTIME_QUERY, DEF_UPDATELOGINTIME_QUERY);\n\n\t\t\tdata_repo.getPreparedStatement(null, INIT_DB_QUERY).executeQuery();\n\t\t} catch (Exception ex) {\n\t\t\tthrow new DBInitException(\"Failed to initialize repository \" + this.getClass().getCanonicalName(), ex);\n\t\t}\n\t}\n\n\t@Override\n\t@Deprecated\n\tpublic void initRepository(final String connection_str, Map<String, String> params) throws DBInitException {\n\t\ttry {\n\t\t\tif (data_repo == null) {\n\t\t\t\tfinal DataRepository dataRepository = RepositoryFactory.getDataRepository(null, connection_str, params);\n\t\t\t\tdataRepository.checkSchemaVersion(this, true);\n\n\t\t\t\tsetDataSource(dataRepository);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tdata_repo = null;\n\n\t\t\tthrow new DBInitException(\"Problem initializing jdbc connection: \" + connection_str, e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void logout(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\ttry {\n\t\t\tPreparedStatement user_logout_sp = data_repo.getPreparedStatement(user, USER_LOGOUT_QUERY);\n\n\t\t\tsynchronized (user_logout_sp) {\n\t\t\t\tuser_logout_sp.setString(1, user.toString());\n\t\t\t\tuser_logout_sp.execute();\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Problem accessing repository.\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void loggedIn(BareJID user) throws TigaseDBException {\n\t\ttry {\n\t\t\tPreparedStatement ps = data_repo.getPreparedStatement(user, DEF_UPDATELOGINTIME_QUERY);\n\n\t\t\tsynchronized (ps) {\n\t\t\t\tps.setString(1, user.toString());\n\t\t\t\tps.execute();\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Problem accessing repository.\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean otherAuth(final Map<String, Object> props)\n\t\t\tthrows UserNotFoundException, TigaseDBException, AuthorizationException {\n\t\tString proto = (String) props.get(PROTOCOL_KEY);\n\n\t\tif (proto.equals(PROTOCOL_VAL_SASL)) {\n\t\t\tString mech = (String) props.get(MACHANISM_KEY);\n\n\t\t\tif (mech.equals(\"PLAIN\")) {\n\t\t\t\ttry {\n\t\t\t\t\treturn saslAuth(props);\n\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\tthrow new AuthorizationException(\"Stringprep failed for: \" + props, ex);\n\t\t\t\t}\n\t\t\t}    // end of if (mech.equals(\"PLAIN\"))\n\n\t\t\tthrow new AuthorizationException(\"Mechanism is not supported: \" + mech);\n\t\t}      // end of if (proto.equals(PROTOCOL_VAL_SASL))\n\n\t\tif (proto.equals(PROTOCOL_VAL_NONSASL)) {\n\t\t\tString password = (String) props.get(PASSWORD_KEY);\n\t\t\tBareJID user_id = (BareJID) props.get(USER_ID_KEY);\n\t\t\tif (password != null) {\n\t\t\t\treturn plainAuth(user_id, password);\n\t\t\t}\n\t\t\tString digest = (String) props.get(DIGEST_KEY);\n\t\t\tif (digest != null) {\n\t\t\t\tthrow new AuthorizationException(\"Not supported.\");\n\t\t\t}\n\t\t} // end of if (proto.equals(PROTOCOL_VAL_SASL))\n\n\t\tthrow new AuthorizationException(\"Protocol is not supported: \" + proto);\n\t}\n\n\t@Override\n\tpublic void queryAuth(final Map<String, Object> authProps) {\n\t\tString protocol = (String) authProps.get(PROTOCOL_KEY);\n\n\t\tif (protocol.equals(PROTOCOL_VAL_NONSASL)) {\n\t\t\tauthProps.put(RESULT_KEY, non_sasl_mechs);\n\t\t}    // end of if (protocol.equals(PROTOCOL_VAL_NONSASL))\n\n\t\tif (protocol.equals(PROTOCOL_VAL_SASL)) {\n\t\t\tauthProps.put(RESULT_KEY, sasl_mechs);\n\t\t}    // end of if (protocol.equals(PROTOCOL_VAL_NONSASL))\n\t}\n\n\t// Implementation of tigase.db.AuthRepository\n\n\t@Override\n\tpublic void removeUser(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\ttry {\n\t\t\tPreparedStatement remove_user_sp = data_repo.getPreparedStatement(user, REMOVE_USER_QUERY);\n\n\t\t\tsynchronized (remove_user_sp) {\n\t\t\t\tremove_user_sp.setString(1, user.toString());\n\t\t\t\tremove_user_sp.execute();\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Problem accessing repository.\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void updatePassword(BareJID user, final String password) throws UserNotFoundException, TigaseDBException {\n\t\ttry {\n\t\t\tPreparedStatement update_pass_plain_pw_sp = data_repo.getPreparedStatement(user,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   UPDATE_PASSWORD_PLAIN_PW_QUERY);\n\n\t\t\tsynchronized (update_pass_plain_pw_sp) {\n\t\t\t\tupdate_pass_plain_pw_sp.setString(1, user.toString());\n\t\t\t\tupdate_pass_plain_pw_sp.setString(2, password);\n\t\t\t\tupdate_pass_plain_pw_sp.execute();\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Problem accessing repository.\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getPassword(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\tResultSet rs = null;\n\n\t\ttry {\n\t\t\tPreparedStatement get_pass_sp = data_repo.getPreparedStatement(user, GET_PASSWORD_QUERY);\n\n\t\t\tsynchronized (get_pass_sp) {\n\t\t\t\ttry {\n\t\t\t\t\tget_pass_sp.setString(1, user.toString());\n\t\t\t\t\trs = get_pass_sp.executeQuery();\n\n\t\t\t\t\tif (rs.next()) {\n\t\t\t\t\t\treturn rs.getString(1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new UserNotFoundException(\"User does not exist: \" + user);\n\t\t\t\t\t}    // end of if (isnext) else\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Problem with retrieving user password.\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isUserDisabled(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void setUserDisabled(BareJID user, Boolean value) throws UserNotFoundException, TigaseDBException {\n\t\tthrow new TigaseDBException(\"Feature not supported\");\n\t}\n\n\t@Override\n\tpublic void setAccountStatus(BareJID user, AccountStatus status) throws TigaseDBException {\n\t\tthrow new TigaseDBException(\"Feature not supported\");\n\t}\n\n\t@Override\n\tpublic AccountStatus getAccountStatus(BareJID user) throws TigaseDBException {\n\t\treturn AccountStatus.active;\n\t}\n\n\tprivate boolean plainAuth(BareJID user, final String password)\n\t\t\tthrows UserNotFoundException, TigaseDBException, AuthorizationException {\n\t\tResultSet rs = null;\n\t\tString res_string = null;\n\n\t\ttry {\n\t\t\tPreparedStatement user_login_plain_pw_sp = data_repo.getPreparedStatement(user, USER_LOGIN_PLAIN_PW_QUERY);\n\n\t\t\tsynchronized (user_login_plain_pw_sp) {\n\t\t\t\ttry {\n\t\t\t\t\tuser_login_plain_pw_sp.setString(1, user.toString());\n\t\t\t\t\tuser_login_plain_pw_sp.setString(2, password);\n\t\t\t\t\tswitch (data_repo.getDatabaseType()) {\n\t\t\t\t\t\tcase jtds:\n\t\t\t\t\t\tcase sqlserver:\n\t\t\t\t\t\t\tuser_login_plain_pw_sp.executeUpdate();\n\t\t\t\t\t\t\trs = user_login_plain_pw_sp.getGeneratedKeys();\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\trs = user_login_plain_pw_sp.executeQuery();\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tboolean auth_result_ok = false;\n\n\t\t\t\t\tif (rs.next()) {\n\t\t\t\t\t\tres_string = rs.getString(1);\n\n\t\t\t\t\t\tif (res_string != null) {\n\t\t\t\t\t\t\tBareJID result = BareJID.bareJIDInstance(res_string);\n\n\t\t\t\t\t\t\tauth_result_ok = user.equals(result);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (auth_result_ok) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINE,\n\t\t\t\t\t\t\t\t\t\t\"Login failed, for user: ''{0}\" + \"''\" + \", password: ''\" + \"{1}\" + \"''\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\", from DB got: \" + \"{2}\", new Object[]{user, password, res_string});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t}\n\t\t\t\tthrow new UserNotFoundException(\"User does not exist: \" + user);\n\t\t\t}\n\t\t} catch (TigaseStringprepException ex) {\n\t\t\tthrow new AuthorizationException(\"Stringprep failed for: \" + res_string, ex);\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Problem accessing repository.\", e);\n\t\t}    // end of catch\n\t}\n\n\tprivate boolean saslAuth(final Map<String, Object> props)\n\t\t\tthrows UserNotFoundException, TigaseDBException, AuthorizationException, TigaseStringprepException {\n\t\tString data_str = (String) props.get(DATA_KEY);\n\t\tString domain = (String) props.get(REALM_KEY);\n\n\t\tprops.put(RESULT_KEY, null);\n\n\t\tbyte[] in_data = ((data_str != null) ? Base64.decode(data_str) : new byte[0]);\n\t\tint auth_idx = 0;\n\n\t\twhile ((in_data[auth_idx] != 0) && (auth_idx < in_data.length)) {\n\t\t\t++auth_idx;\n\t\t}\n\n\t\tString authoriz = new String(in_data, 0, auth_idx);\n\t\tint user_idx = ++auth_idx;\n\n\t\twhile ((in_data[user_idx] != 0) && (user_idx < in_data.length)) {\n\t\t\t++user_idx;\n\t\t}\n\n\t\tString user_name = new String(in_data, auth_idx, user_idx - auth_idx);\n\n\t\t++user_idx;\n\n\t\tBareJID jid = null;\n\n\t\tif (BareJID.parseJID(user_name)[0] == null) {\n\t\t\tjid = BareJID.bareJIDInstance(user_name, domain);\n\t\t} else {\n\t\t\tjid = BareJID.bareJIDInstance(user_name);\n\t\t}\n\n\t\tprops.put(USER_ID_KEY, jid);\n\n\t\tString passwd = new String(in_data, user_idx, in_data.length - user_idx);\n\n\t\treturn plainAuth(jid, passwd);\n\t}\n}    // TigaseAuth\n\n"
  },
  {
    "path": "src/main/java/tigase/db/jdbc/TigaseCustomAuth.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.jdbc;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.auth.XmppSaslException;\nimport tigase.auth.credentials.Credentials;\nimport tigase.db.*;\nimport tigase.db.util.RepositoryVersionAware;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.util.Algorithms;\nimport tigase.util.Base64;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xmpp.jid.BareJID;\n\nimport javax.security.auth.callback.*;\nimport javax.security.sasl.*;\nimport java.io.IOException;\nimport java.security.NoSuchAlgorithmException;\nimport java.sql.*;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.db.AuthRepository.Meta;\n\n/**\n * The user authentication connector allows for customized SQL queries to be used. Queries are defined in the\n * configuration file and they can be either plain SQL queries or stored procedures.\n * <br>\n * If the query starts with characters: <code>{ call</code> then the server assumes this is a stored procedure call,\n * otherwise it is executed as a plain SQL query. Each configuration value is stripped from white characters on both\n * ends before processing.\n * <br>\n * Please don't use semicolon <code>';'</code> at the end of the query as many JDBC drivers get confused and the query\n * may not work for unknown obvious reason.\n * <br>\n * Some queries take arguments. Arguments are marked by question marks <code>'?'</code> in the query. Refer to the\n * configuration parameters description for more details about what parameters are expected in each query.\n * <br>\n * Example configuration.\n * <br>\n * The first example shows how to put a stored procedure as a query with 2 required parameters.\n * <br>\n * <pre>\n * add-user-query={ call TigAddUserPlainPw(?, ?) }\n * </pre>\n * <br>\n * The same query with plain SQL parameters instead:\n * <br>\n * <pre>\n * add-user-query=insert into users (user_id, password) values (?, ?)\n * </pre>\n * <br>\n * Created: Sat Nov 11 22:22:04 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Meta(isDefault = true, supportedUris = {\"jdbc:[^:]+:.*\"})\n@Repository.SchemaId(id = Schema.SERVER_SCHEMA_ID, name = Schema.SERVER_SCHEMA_NAME)\npublic class TigaseCustomAuth\n\t\textends AbstractAuthRepositoryWithCredentials\n\t\timplements DataSourceAware<DataRepository>, RepositoryVersionAware {\n\n\t/**\n\t * Query executing periodically to ensure active connection with the database.\n\t * <br>\n\t * Takes no arguments.\n\t * <br>\n\t * Example query:\n\t * <br>\n\t * <pre>\n\t * select 1\n\t * </pre>\n\t */\n\tpublic static final String DEF_CONNVALID_KEY = \"conn-valid-query\";\n\t/**\n\t * Database initialization query which is run after the server is started.\n\t * <br>\n\t * Takes no arguments.\n\t * <br>\n\t * Example query:\n\t * <br>\n\t * <pre>\n\t * update tig_users set online_status = 0\n\t * </pre>\n\t */\n\tpublic static final String DEF_INITDB_KEY = \"init-db-query\";\n\t/**\n\t * Query adding a new user to the database.\n\t * <br>\n\t * Takes 2 arguments: <code>(user_id (JID), password)</code>\n\t * <br>\n\t * Example query:\n\t * <br>\n\t * <pre>\n\t * insert into tig_users (user_id, user_pw) values (?, ?)\n\t * </pre>\n\t */\n\tpublic static final String DEF_ADDUSER_KEY = \"add-user-query\";\n\t/**\n\t * Removes a user from the database.\n\t * <br>\n\t * Takes 1 argument: <code>(user_id (JID))</code>\n\t * <br>\n\t * Example query:\n\t * <br>\n\t * <pre>\n\t * delete from tig_users where user_id = ?\n\t * </pre>\n\t */\n\tpublic static final String DEF_DELUSER_KEY = \"del-user-query\";\n\t/**\n\t * Retrieves user password from the database for given user_id (JID).\n\t * <br>\n\t * Takes 1 argument: <code>(user_id (JID))</code>\n\t * <br>\n\t * Example query:\n\t * <br>\n\t * <pre>\n\t * select user_pw from tig_users where user_id = ?\n\t * </pre>\n\t */\n\tpublic static final String DEF_GETPASSWORD_KEY = \"get-password-query\";\n\t/**\n\t * Updates (changes) password for a given user_id (JID).\n\t * <br>\n\t * Takes 2 arguments: <code>(password, user_id (JID))</code>\n\t * <br>\n\t * Example query:\n\t * <br>\n\t * <pre>\n\t * update tig_users set user_pw = ? where user_id = ?\n\t * </pre>\n\t */\n\tpublic static final String DEF_UPDATEPASSWORD_KEY = \"update-password-query\";\n\t/**\n\t * Performs user login. Normally used when there is a special SP used for this purpose. This is an alternative way\n\t * to a method requiring retrieving user password. Therefore at least one of those queries must be defined:\n\t * <code>user-login-query</code> or <code>get-password-query</code>.\n\t * <br>\n\t * If both queries are defined then <code>user-login-query</code> is used. Normally this method should be only used\n\t * with plain text password authentication or sasl-plain.\n\t * <br>\n\t * The Tigase server expects a result set with user_id to be returned from the query if login is successful and\n\t * empty results set if the login is unsuccessful.\n\t * <br>\n\t * Takes 2 arguments: <code>(user_id (JID), password)</code>\n\t * <br>\n\t * Example query:\n\t * <br>\n\t * <pre>\n\t * select user_id from tig_users where (user_id = ?) AND (user_pw = ?)\n\t * </pre>\n\t */\n\tpublic static final String DEF_USERLOGIN_KEY = \"user-login-query\";\n\t/**\n\t * This query is called when user logs out or disconnects. It can record that event in the database.\n\t * <br>\n\t * Takes 1 argument: <code>(user_id (JID))</code>\n\t * <br>\n\t * Example query:\n\t * <br>\n\t * <pre>\n\t * update tig_users, set online_status = online_status - 1 where user_id = ?\n\t * </pre>\n\t */\n\tpublic static final String DEF_USERLOGOUT_KEY = \"user-logout-query\";\n\tpublic static final String DEF_UPDATELOGINTIME_KEY = \"update-login-time-query\";\n\tpublic static final String DEF_USERS_COUNT_KEY = \"users-count-query\";\n\tpublic static final String DEF_ACTIVE_USERS_COUNT_KEY = \"active-users-count-query\";\n\tpublic static final String DEF_USERS_DOMAIN_COUNT_KEY = \"\" + \"users-domain-count-query\";\n\tpublic static final String DEF_LISTDISABLEDACCOUNTS_KEY = \"users-list-disabled-accounts-query\";\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String DEF_DISABLEACCOUNT_KEY = \"user-disable-account-query\";\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String DEF_ENABLEACCOUNT_KEY = \"user-enable-account-query\";\n\tpublic static final String DEF_UPDATEACCOUNTSTATUS_KEY = \"user-update-account-status-query\";\n\tpublic static final String DEF_ACCOUNTSTATUS_KEY = \"user-account-status-query\";\n\t/**\n\t * Comma separated list of NON-SASL authentication mechanisms. Possible mechanisms are: <code>password</code> and\n\t * <code>digest</code>. <code>digest</code> mechanism can work only with <code>get-password-query</code> active and\n\t * only when password are stored in plain text format in the database.\n\t */\n\tpublic static final String DEF_NONSASL_MECHS_KEY = \"non-sasl-mechs\";\n\t/**\n\t * Comma separated list of SASL authentication mechanisms. Possible mechanisms are all mechanisms supported by Java\n\t * implementation. The most common are: <code>PLAIN</code>, <code>DIGEST-MD5</code>, <code>CRAM-MD5</code>.\n\t * <br>\n\t * \"Non-PLAIN\" mechanisms will work only with the <code>get-password-query</code> active and only when passwords are\n\t * stored in plain text format in the database.\n\t */\n\tpublic static final String DEF_SASL_MECHS_KEY = \"sasl-mechs\";\n\tpublic static final String NO_QUERY = \"none\";\n\tpublic static final String DEF_INITDB_QUERY = \"{ call TigInitdb() }\";\n\tpublic static final String DEF_ADDUSER_QUERY = \"{ call TigAddUserPlainPw(?, ?) }\";\n\tpublic static final String DEF_DELUSER_QUERY = \"{ call TigRemoveUser(?) }\";\n\tpublic static final String DEF_GETPASSWORD_QUERY = \"{ call TigGetPassword(?) }\";\n\tpublic static final String DEF_USERS_COUNT_QUERY = \"{ call TigAllUsersCount() }\";\n\tpublic static final String DEF_ACTIVE_USERS_COUNT_QUERY = \"select count(*) from tig_users where last_used > ?\";\n\tpublic static final String DEF_USERS_DOMAIN_COUNT_QUERY = \"select count(*) from tig_users where user_id like ?\";\n\tpublic static final String DEF_LISTDISABLEDACCOUNTS_QUERY = \"{ call TigDisabledAccounts() }\";\n\tpublic static final String DEF_UPDATEACCOUNTSTATUS_QUERY = \"{ call TigUpdateAccountStatus(?, ?) }\";\n\tpublic static final String DEF_ACCOUNTSTATUS_QUERY = \"{ call TigAccountStatus(?) }\";\n\tpublic static final String DEF_NONSASL_MECHS = \"password\";\n\tpublic static final String DEF_SASL_MECHS = \"PLAIN\";\n\tpublic static final String SP_STARTS_WITH = \"{ call\";\n\tprivate static final Logger log = Logger.getLogger(TigaseCustomAuth.class.getName());\n\tprivate static final String DEF_UPDATELOGINTIME_QUERY = \"{ call TigUpdateLoginTime(?) }\";\n\n\t// ~--- fields ---------------------------------------------------------------\n\t@ConfigField(desc = \"Checks account status\", alias = DEF_ACCOUNTSTATUS_KEY)\n\tprivate String accountstatus_query = DEF_ACCOUNTSTATUS_QUERY;\n\t@ConfigField(desc = \"Query adding a new user to the database\", alias = DEF_ADDUSER_KEY)\n\tprivate String adduser_query = DEF_ADDUSER_QUERY;\n\tprivate DataRepository data_repo = null;\n\t@ConfigField(desc = \"Removes a user from the database\", alias = DEF_DELUSER_KEY)\n\tprivate String deluser_query = DEF_DELUSER_QUERY;\n\t// credentials queries\n\t@ConfigField(desc = \"Select list of credentials for account and credential-id\", alias = \"get-account-credentials-query\")\n\tprivate String getaccountcredentials_query = \"{ call TigUserCredentials_Get(?,?) }\";\n\t@ConfigField(desc = \"Select list of credential IDs for account\", alias = \"get-account-credentialids-query\")\n\tprivate String getaccountcredentialids_query = \"{ call TigUserUsernames_Get(?) }\";\n\t@ConfigField(desc = \"Database initialization query which is run after the server is started\", alias = DEF_INITDB_KEY)\n\tprivate String initdb_query = null;\n\t@ConfigField(desc = \"Lists disabled accounts\", alias = DEF_LISTDISABLEDACCOUNTS_KEY)\n\tprivate String listdisabledaccounts_query = DEF_LISTDISABLEDACCOUNTS_QUERY;\n\t@ConfigField(desc = \"Comma separated list of NON-SASL authentication mechanisms\", alias = DEF_NONSASL_MECHS_KEY)\n\tprivate String[] nonsasl_mechs = DEF_NONSASL_MECHS.split(\",\");\n\t@ConfigField(desc = \"Remove credential for account and credential ID\", alias = \"remove-account-credential-query\")\n\tprivate String removeaccountcredential_query = \"{ call TigUserCredential_Remove(?,?) }\";\n\t// private String userlogout_query = DEF_USERLOGOUT_QUERY;\n\t@ConfigField(desc = \"Comma separated list of SASL authentication mechanisms\", alias = DEF_SASL_MECHS_KEY)\n\tprivate String[] sasl_mechs = DEF_SASL_MECHS.split(\",\");\n\t@ConfigField(desc = \"Update credential for account and credential ID\", alias = \"update-account-credential-query\")\n\tprivate String updateaccountcredential_query = \"{ call TigUserCredential_Update(?,?,?,?) }\";\n\t@ConfigField(desc = \"Updates (changes) account status\", alias = DEF_UPDATEACCOUNTSTATUS_KEY)\n\tprivate String updateaccountstatus_query = DEF_UPDATEACCOUNTSTATUS_QUERY;\n\t@ConfigField(desc = \"Updates last login/logout timestamps\", alias = DEF_UPDATELOGINTIME_KEY)\n\tprivate String updatelastlogin_query = DEF_UPDATELOGINTIME_QUERY;\n\t@ConfigField(desc = \"Count users for domain\", alias = DEF_USERS_DOMAIN_COUNT_KEY)\n\tprivate String userdomaincount_query = DEF_USERS_DOMAIN_COUNT_QUERY;\n\tprivate boolean userlogin_active = false;\n\t@ConfigField(desc = \"Performs user login\", alias = DEF_USERLOGIN_KEY)\n\tprivate String userlogin_query = null;\n\t// It is better just to not call the query if it is not defined by the user\n\t// By default it is null then and not called.\n\t@ConfigField(desc = \"Performs user logout\", alias = DEF_USERLOGOUT_KEY)\n\tprivate String userlogout_query = null;\n\t@ConfigField(desc = \"Counts users\", alias = DEF_USERS_COUNT_KEY)\n\tprivate String userscount_query = DEF_USERS_COUNT_QUERY;\n\t@ConfigField(desc = \"Counts active users in defined period of time\", alias = DEF_ACTIVE_USERS_COUNT_KEY)\n\tprivate String active_users_count_query = DEF_ACTIVE_USERS_COUNT_QUERY;\n\n\t@Override\n\tpublic void addUser(BareJID user, final String password) throws TigaseDBException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Adding account: {0}\", new Object[]{user});\n\t\t}\n\n\t\tif (adduser_query == null) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tResultSet rs = null;\n\t\t\tPreparedStatement add_user = data_repo.getPreparedStatement(user, adduser_query);\n\n\t\t\tsynchronized (add_user) {\n\t\t\t\ttry {\n\t\t\t\t\tadd_user.setString(1, user.toString());\n\t\t\t\t\tadd_user.setString(2, password);\n\n\t\t\t\t\tboolean is_result = add_user.execute();\n\n\t\t\t\t\tif (is_result) {\n\t\t\t\t\t\trs = add_user.getResultSet();\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tupdateCredential(user, Credentials.DEFAULT_CREDENTIAL_ID, password);\n\t\t} catch (SQLIntegrityConstraintViolationException e) {\n\t\t\tthrow new UserExistsException(\"Error while adding user to repository, user possibly exists: \" + user, e);\n\t\t} catch (SQLException e) {\n\t\t\tif (e.getMessage() != null && (e.getMessage().contains(\"Violation of UNIQUE KEY\") ||\n\t\t\t\t\te.getMessage().contains(\"violates unique constraint \\\"user_id\\\"\")) ||\n\t\t\t\t\te.getMessage().contains(\"DerbySQLIntegrityConstraintViolationException\")) {\n\t\t\t\t// This is a workaround SQL Server which just throws SLQ Exception\n\t\t\t\tthrow new UserExistsException(\"Error while adding user to repository, user possibly exists: \" + user,\n\t\t\t\t\t\t\t\t\t\t\t  e);\n\t\t\t} else {\n\t\t\t\tthrow new TigaseDBException(\"Problem accessing repository for user: \" + user, e);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate boolean digestAuth(BareJID user, final String digest, final String id, final String alg)\n\t\t\tthrows TigaseDBException, AuthorizationException {\n\t\tif (userlogin_active) {\n\t\t\tthrow new AuthorizationException(\"Not supported.\");\n\t\t} else {\n\t\t\tfinal String db_password = getPassword(user);\n\n\t\t\ttry {\n\t\t\t\tfinal String digest_db_pass = Algorithms.hexDigest(id, db_password, alg);\n\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Comparing passwords, given: {0}, db: {1}\",\n\t\t\t\t\t\t\tnew Object[]{digest, digest_db_pass});\n\t\t\t\t}\n\n\t\t\t\treturn digest.equals(digest_db_pass);\n\t\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\t\tthrow new AuthorizationException(\"No such algorithm.\", e);\n\t\t\t} // end of try-catch\n\t\t}\n\t}\n\n\t@Override\n\tpublic AccountStatus getAccountStatus(final BareJID user) throws TigaseDBException {\n\t\tif (accountstatus_query == null) {\n\t\t\treturn null;\n\t\t}\n\t\ttry {\n\t\t\tResultSet rs = null;\n\t\t\tPreparedStatement get_status = data_repo.getPreparedStatement(user, accountstatus_query);\n\n\t\t\tsynchronized (get_status) {\n\t\t\t\ttry {\n\t\t\t\t\tget_status.setString(1, user.toString());\n\t\t\t\t\trs = get_status.executeQuery();\n\n\t\t\t\t\tif (rs.next()) {\n\t\t\t\t\t\tint v = rs.getInt(1);\n\t\t\t\t\t\tfinal AccountStatus accountStatus = AccountStatus.byValue(v);\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Got account: {0} status: {1}\", new Object[]{user, accountStatus});\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn accountStatus;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new UserNotFoundException(\"User does not exist: \" + user);\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t}\n\t\t\t}\n\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Problem with retrieving account status.\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Credentials getCredentials(BareJID user, String credentialId) throws TigaseDBException {\n\t\tif (userlogin_active) {\n\t\t\treturn new SingleCredential(user, getAccountStatus(user), new Credentials.Entry() {\n\t\t\t\t@Override\n\t\t\t\tpublic String getMechanism() {\n\t\t\t\t\treturn \"STORED-PROCEDURE\";\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic boolean verifyPlainPassword(String password) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn userLoginAuth(user, password);\n\t\t\t\t\t} catch (TigaseDBException | AuthorizationException e) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"authorization failed with an error\", e);\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\ttry {\n\t\t\tPreparedStatement get_credentials = data_repo.getPreparedStatement(user, getaccountcredentials_query);\n\n\t\t\tList<DefaultCredentials.RawEntry> entries = new ArrayList<>();\n\t\t\tAccountStatus accountStatus = null;\n\t\t\tsynchronized (get_credentials) {\n\t\t\t\tResultSet rs = null;\n\t\t\t\ttry {\n\t\t\t\t\tget_credentials.setString(1, user.toString());\n\t\t\t\t\tget_credentials.setString(2, credentialId);\n\t\t\t\t\trs = get_credentials.executeQuery();\n\n\t\t\t\t\twhile (rs.next()) {\n\t\t\t\t\t\tString mechanism = rs.getString(1);\n\t\t\t\t\t\tString value = rs.getString(2);\n\t\t\t\t\t\taccountStatus = AccountStatus.byValue(rs.getInt(3));\n\n\t\t\t\t\t\t// TODO: if we were to add status of particular credentials we would\n\t\t\t\t\t\t// have include it here; currently we only use global status from tig_users;\n\t\t\t\t\t\tentries.add(new DefaultCredentials.RawEntry(mechanism, value));\n\t\t\t\t\t}\n\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (accountStatus == null && entries.isEmpty()) {\n\t\t\t\tthrow new UserNotFoundException(\n\t\t\t\t\t\t\"No credentials found for the user: \" + user + \" (credential ID: \" + credentialId + \")\");\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Got account: {0} credentials: {1}\", new Object[]{user, accountStatus});\n\t\t\t}\n\t\t\treturn new DefaultCredentials(user, accountStatus, entries, getCredentialsDecoder());\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\n\t\t\t\t\t\"Problem with retrieving credentials for account \" + user + \" and credential ID \" + credentialId, e);\n\t\t}\n\t}\n\n\tprotected String getParamWithDef(Map<String, String> params, String key, String def) {\n\t\tif (params == null) {\n\t\t\treturn def;\n\t\t}\n\n\t\tString result = params.get(key);\n\n\t\tif (result != null) {\n\t\t\tlog.log(Level.CONFIG, \"Custom query loaded for ''{0}'': ''{1}''\", new Object[]{key, result});\n\t\t} else {\n\t\t\tresult = def;\n\t\t\tlog.log(Level.CONFIG, \"Default query loaded for ''{0}'': ''{1}''\", new Object[]{key, def});\n\t\t}\n\n\t\tif (result != null) {\n\t\t\tresult = result.trim();\n\n\t\t\tif (result.isEmpty() || result.equals(NO_QUERY)) {\n\t\t\t\tresult = null;\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic String getResourceUri() {\n\t\treturn data_repo.getResourceUri();\n\t}\n\n\t@Override\n\tpublic Collection<String> getCredentialIds(BareJID user) throws TigaseDBException {\n\t\ttry {\n\t\t\tPreparedStatement credentialIdsStatement = data_repo.getPreparedStatement(user, getaccountcredentialids_query);\n\t\t\tList<String> result = new ArrayList<>();\n\t\t\tsynchronized (credentialIdsStatement) {\n\t\t\t\tResultSet rs = null;\n\t\t\t\ttry {\n\t\t\t\t\tcredentialIdsStatement.setString(1, user.toString());\n\t\t\t\t\trs = credentialIdsStatement.executeQuery();\n\n\t\t\t\t\twhile (rs.next()) {\n\t\t\t\t\t\tString credentialId = rs.getString(1);\n\t\t\t\t\t\tresult.add(credentialId);\n\t\t\t\t\t}\n\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Account: {0} has credentialId: {1}\", new Object[]{user, result});\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Problem with retrieving credential IDs for account \" + user, e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic long getActiveUsersCountIn(Duration duration) {\n\t\tif (active_users_count_query == null) {\n\t\t\treturn -1;\n\t\t}\n\n\t\ttry {\n\t\t\tlong users = -1;\n\t\t\tResultSet rs = null;\n\t\t\tPreparedStatement active_users_count = data_repo.getPreparedStatement(null, active_users_count_query);\n\n\t\t\tsynchronized (active_users_count) {\n\t\t\t\ttry {\n\t\t\t\t\t// Load all user count from database\n\t\t\t\t\tfinal Instant instant = Instant.now().minus(duration);\n\t\t\t\t\tactive_users_count.setTimestamp(1, Timestamp.from(instant));\n\t\t\t\t\trs = active_users_count.executeQuery();\n\n\t\t\t\t\tif (rs.next()) {\n\t\t\t\t\t\tusers = rs.getLong(1);\n\t\t\t\t\t} // end of while (rs.next())\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\trs = null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn users;\n\t\t} catch (SQLException e) {\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\t/**\n\t * <code>getUsersCount</code> method is thread safe. It uses local variable for storing <code>Statement</code>.\n\t *\n\t * @return a <code>long</code> number of user accounts in database.\n\t */\n\t@Override\n\tpublic long getUsersCount() {\n\t\tif (userscount_query == null) {\n\t\t\treturn -1;\n\t\t}\n\n\t\ttry {\n\t\t\tlong users = -1;\n\t\t\tResultSet rs = null;\n\t\t\tPreparedStatement users_count = data_repo.getPreparedStatement(null, userscount_query);\n\n\t\t\tsynchronized (users_count) {\n\t\t\t\ttry {\n\t\t\t\t\t// Load all user count from database\n\t\t\t\t\trs = users_count.executeQuery();\n\n\t\t\t\t\tif (rs.next()) {\n\t\t\t\t\t\tusers = rs.getLong(1);\n\t\t\t\t\t} // end of while (rs.next())\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn users;\n\t\t} catch (SQLException e) {\n\t\t\treturn -1;\n\n\t\t\t// throw new\n\t\t\t// TigaseDBException(\"Problem loading user list from repository\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic long getUsersCount(String domain) {\n\t\tif (userdomaincount_query == null) {\n\t\t\treturn -1;\n\t\t}\n\n\t\ttry {\n\t\t\tlong users = -1;\n\t\t\tResultSet rs = null;\n\t\t\tPreparedStatement users_domain_count = data_repo.getPreparedStatement(null, userdomaincount_query);\n\n\t\t\tsynchronized (users_domain_count) {\n\t\t\t\ttry {\n\t\t\t\t\t// Load all user count from database\n\t\t\t\t\tusers_domain_count.setString(1, \"%@\" + domain);\n\t\t\t\t\trs = users_domain_count.executeQuery();\n\n\t\t\t\t\tif (rs.next()) {\n\t\t\t\t\t\tusers = rs.getLong(1);\n\t\t\t\t\t} // end of while (rs.next())\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\trs = null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn users;\n\t\t} catch (SQLException e) {\n\t\t\treturn -1;\n\n\t\t\t// throw new\n\t\t\t// TigaseDBException(\"Problem loading user list from repository\", e);\n\t\t}\n\t}\n\n\tprivate void initDb() throws SQLException {\n\t\tif (initdb_query == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tPreparedStatement init_db = data_repo.getPreparedStatement(null, initdb_query);\n\n\t\tsynchronized (init_db) {\n\t\t\tinit_db.executeUpdate();\n\t\t}\n\t}\n\n\t@Override\n\t@Deprecated\n\tpublic void initRepository(final String connection_str, Map<String, String> params) throws DBInitException {\n\t\ttry {\n\t\t\tinitdb_query = getParamWithDef(params, DEF_INITDB_KEY, null);\n\n\t\t\tadduser_query = getParamWithDef(params, DEF_ADDUSER_KEY, DEF_ADDUSER_QUERY);\n\n\t\t\tdeluser_query = getParamWithDef(params, DEF_DELUSER_KEY, DEF_DELUSER_QUERY);\n\n\t\t\tuserlogin_query = getParamWithDef(params, DEF_USERLOGIN_KEY, null);\n\n\t\t\tuserlogout_query = getParamWithDef(params, DEF_USERLOGOUT_KEY, null);\n\n\t\t\tupdatelastlogin_query = getParamWithDef(params, DEF_UPDATELOGINTIME_KEY, DEF_UPDATELOGINTIME_QUERY);\n\n\t\t\tuserscount_query = getParamWithDef(params, DEF_USERS_COUNT_KEY, DEF_USERS_COUNT_QUERY);\n\t\t\tactive_users_count_query = getParamWithDef(params, DEF_ACTIVE_USERS_COUNT_KEY, DEF_ACTIVE_USERS_COUNT_QUERY);\n\n\t\t\tuserdomaincount_query = getParamWithDef(params, DEF_USERS_DOMAIN_COUNT_KEY, DEF_USERS_DOMAIN_COUNT_QUERY);\n\n\t\t\tlistdisabledaccounts_query = getParamWithDef(params, DEF_LISTDISABLEDACCOUNTS_KEY,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t DEF_LISTDISABLEDACCOUNTS_QUERY);\n\n\t\t\tupdateaccountstatus_query = getParamWithDef(params, DEF_UPDATEACCOUNTSTATUS_KEY,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tDEF_UPDATEACCOUNTSTATUS_QUERY);\n\n\t\t\taccountstatus_query = getParamWithDef(params, DEF_ACCOUNTSTATUS_KEY, DEF_ACCOUNTSTATUS_QUERY);\n\n\t\t\tnonsasl_mechs = getParamWithDef(params, DEF_NONSASL_MECHS_KEY, DEF_NONSASL_MECHS).split(\",\");\n\t\t\tsasl_mechs = getParamWithDef(params, DEF_SASL_MECHS_KEY, DEF_SASL_MECHS).split(\",\");\n\n\t\t\tif (data_repo == null) {\n\t\t\t\tDataRepository dataRepo = RepositoryFactory.getDataRepository(null, connection_str, params);\n\t\t\t\tdataRepo.checkSchemaVersion(this, true);\n\t\t\t\tsetDataSource(dataRepo);\n\t\t\t}\n\n\t\t\tif ((params != null) && (params.get(\"init-db\") != null)) {\n\t\t\t\tinitDb();\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tdata_repo = null;\n\n\t\t\tthrow new DBInitException(\"Problem initializing jdbc connection: \" + connection_str, e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isMechanismSupported(String domain, String mechanism) {\n\t\tif (\"PLAIN\".equals(mechanism)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (userlogin_active) {\n\t\t\treturn false;\n\t\t}\n\t\treturn super.isMechanismSupported(domain, mechanism);\n\t}\n\n\t@Override\n\tpublic void loggedIn(BareJID user) throws TigaseDBException {\n\t\tif (updatelastlogin_query == null) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tPreparedStatement ps = data_repo.getPreparedStatement(user, updatelastlogin_query);\n\n\t\t\tif (ps != null) {\n\t\t\t\tsynchronized (ps) {\n\t\t\t\t\tps.setString(1, user.toString());\n\t\t\t\t\tps.execute();\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Problem accessing repository.\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void logout(BareJID user) throws TigaseDBException {\n\t\tif (userlogout_query == null) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tPreparedStatement user_logout = data_repo.getPreparedStatement(user, userlogout_query);\n\n\t\t\tif (user_logout != null) {\n\t\t\t\tsynchronized (user_logout) {\n\t\t\t\t\tuser_logout.setString(1, user.toString());\n\t\t\t\t\tuser_logout.execute();\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Problem accessing repository.\", e);\n\t\t}\n\t}\n\n\t// Implementation of tigase.db.AuthRepository\n\n\t@Override\n\tpublic boolean otherAuth(final Map<String, Object> props)\n\t\t\tthrows TigaseDBException, AuthorizationException {\n\t\tString proto = (String) props.get(PROTOCOL_KEY);\n\n\t\tif (proto.equals(PROTOCOL_VAL_SASL)) {\n\t\t\tString mech = (String) props.get(MACHANISM_KEY);\n\n\t\t\tif (mech.equals(\"PLAIN\")) {\n\t\t\t\ttry {\n\t\t\t\t\tif (saslPlainAuth(props)) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new AuthorizationException(\"Authentication failed.\");\n\t\t\t\t\t}\n\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\tthrow new AuthorizationException(\"Stringprep failed for: \" + props, ex);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn saslAuth(props);\n\t\t\t}\n\t\t} // end of if (proto.equals(PROTOCOL_VAL_SASL))\n\n\t\tif (proto.equals(PROTOCOL_VAL_NONSASL)) {\n\t\t\tString password = (String) props.get(PASSWORD_KEY);\n\t\t\tBareJID user_id = (BareJID) props.get(USER_ID_KEY);\n\t\t\tif (password != null) {\n\t\t\t\treturn plainAuth(user_id, password);\n\t\t\t}\n\t\t\tString digest = (String) props.get(DIGEST_KEY);\n\t\t\tif (digest != null) {\n\t\t\t\tString digest_id = (String) props.get(DIGEST_ID_KEY);\n\t\t\t\treturn digestAuth(user_id, digest, digest_id, \"SHA\");\n\t\t\t}\n\t\t} // end of if (proto.equals(PROTOCOL_VAL_SASL))\n\n\t\tthrow new AuthorizationException(\"Protocol is not supported.\");\n\t}\n\n\tprivate boolean plainAuth(BareJID user, final String password)\n\t\t\tthrows TigaseDBException, AuthorizationException {\n\t\tif (userlogin_active) {\n\t\t\treturn userLoginAuth(user, password);\n\t\t} else {\n\t\t\tString db_password = getPassword(user);\n\n\t\t\treturn (password != null) && (db_password != null) && db_password.equals(password);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void queryAuth(final Map<String, Object> authProps) {\n\t\tString protocol = (String) authProps.get(PROTOCOL_KEY);\n\n\t\tif (protocol.equals(PROTOCOL_VAL_NONSASL)) {\n\t\t\tauthProps.put(RESULT_KEY, nonsasl_mechs);\n\t\t} // end of if (protocol.equals(PROTOCOL_VAL_NONSASL))\n\n\t\tif (protocol.equals(PROTOCOL_VAL_SASL)) {\n\t\t\tauthProps.put(RESULT_KEY, sasl_mechs);\n\t\t} // end of if (protocol.equals(PROTOCOL_VAL_NONSASL))\n\t}\n\n\t@Override\n\tpublic void removeCredential(BareJID user, String credentialId) throws TigaseDBException {\n\t\ttry {\n\t\t\tPreparedStatement removeCredential_stmt = data_repo.getPreparedStatement(user,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t removeaccountcredential_query);\n\t\t\tsynchronized (removeCredential_stmt) {\n\t\t\t\tremoveCredential_stmt.setString(1, user.toString());\n\t\t\t\tremoveCredential_stmt.setString(2, credentialId);\n\t\t\t\tremoveCredential_stmt.execute();\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Problem accessing repository.\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void removeUser(BareJID user) throws TigaseDBException {\n\t\tif (deluser_query == null) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tPreparedStatement remove_user = data_repo.getPreparedStatement(user, deluser_query);\n\n\t\t\tsynchronized (remove_user) {\n\t\t\t\tremove_user.setString(1, user.toString());\n\t\t\t\tremove_user.execute();\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Problem accessing repository.\", e);\n\t\t}\n\t}\n\n\tprivate boolean saslAuth(final Map<String, Object> props) throws AuthorizationException {\n\t\ttry {\n\t\t\tSaslServer ss = (SaslServer) props.get(\"SaslServer\");\n\n\t\t\tif (ss == null) {\n\t\t\t\tMap<String, String> sasl_props = new TreeMap<String, String>();\n\n\t\t\t\tsasl_props.put(Sasl.QOP, \"auth\");\n\t\t\t\tss = Sasl.createSaslServer((String) props.get(MACHANISM_KEY), \"xmpp\",\n\t\t\t\t\t\t\t\t\t\t   (String) props.get(SERVER_NAME_KEY), sasl_props,\n\t\t\t\t\t\t\t\t\t\t   new SaslCallbackHandler(props));\n\t\t\t\tprops.put(\"SaslServer\", ss);\n\t\t\t} // end of if (ss == null)\n\n\t\t\tString data_str = (String) props.get(DATA_KEY);\n\t\t\tbyte[] in_data = ((data_str != null) ? Base64.decode(data_str) : new byte[0]);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"response: {0}\", new String(in_data));\n\t\t\t}\n\n\t\t\tbyte[] challenge = ss.evaluateResponse(in_data);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"challenge: {0}\", ((challenge != null) ? new String(challenge) : \"null\"));\n\t\t\t}\n\n\t\t\tString challenge_str = (((challenge != null) && (challenge.length > 0)) ? Base64.encode(challenge) : null);\n\n\t\t\tprops.put(RESULT_KEY, challenge_str);\n\n\t\t\t// end of if (ss.isComplete()) else\n\t\t\treturn ss.isComplete();\n\t\t} catch (SaslException e) {\n\t\t\tthrow new AuthorizationException(\"Sasl exception.\", e);\n\t\t} // end of try-catch\n\t}\n\n\tprivate boolean saslPlainAuth(final Map<String, Object> props)\n\t\t\tthrows TigaseDBException, AuthorizationException, TigaseStringprepException {\n\t\tString data_str = (String) props.get(DATA_KEY);\n\t\tString domain = (String) props.get(REALM_KEY);\n\n\t\tprops.put(RESULT_KEY, null);\n\n\t\tbyte[] in_data = ((data_str != null) ? Base64.decode(data_str) : new byte[0]);\n\t\tint auth_idx = 0;\n\n\t\twhile ((in_data[auth_idx] != 0) && (auth_idx < in_data.length)) {\n\t\t\t++auth_idx;\n\t\t}\n\n\t\tString authoriz = new String(in_data, 0, auth_idx);\n\t\tint user_idx = ++auth_idx;\n\n\t\twhile ((in_data[user_idx] != 0) && (user_idx < in_data.length)) {\n\t\t\t++user_idx;\n\t\t}\n\n\t\tString user_name = new String(in_data, auth_idx, user_idx - auth_idx);\n\n\t\t++user_idx;\n\n\t\tBareJID jid = null;\n\n\t\tif (BareJID.parseJID(user_name)[0] == null) {\n\t\t\tjid = BareJID.bareJIDInstance(user_name, domain);\n\t\t} else {\n\t\t\tjid = BareJID.bareJIDInstance(user_name);\n\t\t}\n\n\t\tprops.put(USER_ID_KEY, jid);\n\n\t\tString passwd = new String(in_data, user_idx, in_data.length - user_idx);\n\n\t\treturn plainAuth(jid, passwd);\n\t}\n\n\t@Override\n\tpublic void setAccountStatus(BareJID user, AccountStatus value) throws TigaseDBException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Setting account: {0} status to: {1}\", new Object[]{user, value});\n\t\t}\n\t\ttry {\n\t\t\tPreparedStatement changeState = data_repo.getPreparedStatement(user, updateaccountstatus_query);\n\t\t\tsynchronized (changeState) {\n\t\t\t\tchangeState.setString(1, user.toString());\n\t\t\t\tchangeState.setInt(2, value.getValue());\n\t\t\t\tchangeState.execute();\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tlog.log(Level.FINEST, \"problem with changing user account state\", e);\n\t\t\tthrow new TigaseDBException(\"Problem with changing user account state\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setDataSource(DataRepository data_repo) throws DBInitException {\n\t\ttry {\n\t\t\tif (initdb_query != null) {\n\t\t\t\tdata_repo.initPreparedStatement(initdb_query, initdb_query);\n\t\t\t}\n\t\t\tif ((adduser_query != null)) {\n\t\t\t\tdata_repo.initPreparedStatement(adduser_query, adduser_query);\n\t\t\t}\n\t\t\tif ((deluser_query != null)) {\n\t\t\t\tdata_repo.initPreparedStatement(deluser_query, deluser_query);\n\t\t\t}\n\t\t\tif (userlogin_query != null) {\n\t\t\t\tdata_repo.initPreparedStatement(userlogin_query, userlogin_query);\n\t\t\t\tuserlogin_active = true;\n\t\t\t}\n\t\t\tif ((userlogout_query != null)) {\n\t\t\t\tdata_repo.initPreparedStatement(userlogout_query, userlogout_query);\n\t\t\t}\n\t\t\tif (updatelastlogin_query != null) {\n\t\t\t\tdata_repo.initPreparedStatement(updatelastlogin_query, updatelastlogin_query);\n\t\t\t}\n\t\t\tif ((userscount_query != null)) {\n\t\t\t\tdata_repo.initPreparedStatement(userscount_query, userscount_query);\n\t\t\t}\n\t\t\tif ((active_users_count_query != null)) {\n\t\t\t\tdata_repo.initPreparedStatement(active_users_count_query, active_users_count_query);\n\t\t\t}\n\t\t\tif ((userdomaincount_query != null)) {\n\t\t\t\tdata_repo.initPreparedStatement(userdomaincount_query, userdomaincount_query);\n\t\t\t}\n\t\t\tif (listdisabledaccounts_query != null) {\n\t\t\t\tdata_repo.initPreparedStatement(listdisabledaccounts_query, listdisabledaccounts_query);\n\t\t\t}\n\t\t\tif (updateaccountstatus_query != null) {\n\t\t\t\tdata_repo.initPreparedStatement(updateaccountstatus_query, updateaccountstatus_query);\n\t\t\t}\n\t\t\tif (accountstatus_query != null) {\n\t\t\t\tdata_repo.initPreparedStatement(accountstatus_query, accountstatus_query);\n\t\t\t}\n\t\t\tif (getaccountcredentials_query != null) {\n\t\t\t\tdata_repo.initPreparedStatement(getaccountcredentials_query, getaccountcredentials_query);\n\t\t\t}\n\t\t\tif (getaccountcredentialids_query != null) {\n\t\t\t\tdata_repo.initPreparedStatement(getaccountcredentialids_query, getaccountcredentialids_query);\n\t\t\t}\n\t\t\tif (removeaccountcredential_query != null) {\n\t\t\t\tdata_repo.initPreparedStatement(removeaccountcredential_query, removeaccountcredential_query);\n\t\t\t}\n\t\t\tif (updateaccountcredential_query != null) {\n\t\t\t\tdata_repo.initPreparedStatement(updateaccountcredential_query, updateaccountcredential_query);\n\t\t\t}\n\n\t\t\tthis.data_repo = data_repo;\n\n\t\t\tif (initdb_query != null) {\n\t\t\t\tinitDb();\n\t\t\t}\n\t\t} catch (SQLException ex) {\n\t\t\tdata_repo = null;\n\t\t\tthrow new DBInitException(\"Could not initialize TigaseCustomAuth instance\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void updateCredential(BareJID user, String credentialId, String password)\n\t\t\tthrows TigaseDBException {\n\t\tList<String[]> entries = getCredentialsEncoder().encodeForAllMechanisms(user, password);\n\t\ttry {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Updating credentials for user: {0}, credentialId: {1}\",\n\t\t\t\t\t\tnew Object[]{user, credentialId});\n\t\t\t}\n\t\t\tremoveCredential(user, credentialId);\n\n\t\t\tPreparedStatement updateCredential_stmt = data_repo.getPreparedStatement(user,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t updateaccountcredential_query);\n\t\t\tsynchronized (updateCredential_stmt) {\n\t\t\t\tfor (String[] entry : entries) {\n\t\t\t\t\tupdateCredential_stmt.setString(1, user.toString());\n\t\t\t\t\tupdateCredential_stmt.setString(2, credentialId);\n\t\t\t\t\tupdateCredential_stmt.setString(3, entry[0]);\n\t\t\t\t\tupdateCredential_stmt.setString(4, entry[1]);\n\n\t\t\t\t\tupdateCredential_stmt.addBatch();\n\t\t\t\t}\n\t\t\t\tupdateCredential_stmt.executeBatch();\n\t\t\t}\n\t\t} catch (SQLException ex) {\n\t\t\tthrow new TigaseDBException(\"Problem accessing repository.\", ex);\n\t\t}\n\t}\n\n\tpublic void updateCredential(BareJID user, String credentialId, String mechanism, String data) throws TigaseDBException {\n\t\ttry {\n\t\t\tPreparedStatement updateCredential_stmt = data_repo.getPreparedStatement(user,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t updateaccountcredential_query);\n\t\t\tsynchronized (updateCredential_stmt) {\n\t\t\t\tupdateCredential_stmt.setString(1, user.toString());\n\t\t\t\tupdateCredential_stmt.setString(2, credentialId);\n\t\t\t\tupdateCredential_stmt.setString(3, mechanism);\n\t\t\t\tupdateCredential_stmt.setString(4, data);\n\n\t\t\t\tupdateCredential_stmt.execute();\n\t\t\t}\n\t\t} catch (SQLException ex) {\n\t\t\tthrow new TigaseDBException(\"Problem accessing repository.\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void updatePassword(BareJID user, final String password) throws TigaseDBException {\n\t\tupdateCredential(user, \"default\", password);\n\t}\n\n\tprivate boolean userLoginAuth(BareJID user, final String password)\n\t\t\tthrows TigaseDBException, AuthorizationException {\n\t\tif (userlogin_query == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tString res_string = null;\n\n\t\ttry {\n\t\t\tResultSet rs = null;\n\t\t\tPreparedStatement user_login = data_repo.getPreparedStatement(user, userlogin_query);\n\n\t\t\tsynchronized (user_login) {\n\t\t\t\ttry {\n\t\t\t\t\tuser_login.setString(1, user.toString());\n\t\t\t\t\tuser_login.setString(2, password);\n\n\t\t\t\t\tswitch (data_repo.getDatabaseType()) {\n\t\t\t\t\t\tcase sqlserver:\n\t\t\t\t\t\t\tuser_login.executeUpdate();\n\t\t\t\t\t\t\trs = user_login.getGeneratedKeys();\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\trs = user_login.executeQuery();\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tboolean auth_result_ok = false;\n\n\t\t\t\t\tif (rs.next()) {\n\t\t\t\t\t\tres_string = rs.getString(1);\n\n\t\t\t\t\t\tif (res_string != null) {\n\t\t\t\t\t\t\tBareJID result = BareJID.bareJIDInstance(res_string);\n\n\t\t\t\t\t\t\tauth_result_ok = user.equals(result);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (auth_result_ok) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINE,\n\t\t\t\t\t\t\t\t\t\t\"Login failed, for user: ''{0}\" + \"''\" + \", password: ''\" + \"{1}\" + \"''\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\", from DB got: \" + \"{2}\", new Object[]{user, password, res_string});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t}\n\t\t\t\tthrow new UserNotFoundException(\"User does not exist: \" + user + \", in database: \" + getResourceUri());\n\t\t\t}\n\t\t} catch (TigaseStringprepException ex) {\n\t\t\tthrow new AuthorizationException(\"Stringprep failed for: \" + res_string, ex);\n\t\t} catch (SQLException e) {\n\t\t\tthrow new TigaseDBException(\"Problem accessing repository.\", e);\n\t\t} // end of catch\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tprivate class SaslCallbackHandler\n\t\t\timplements CallbackHandler {\n\n\t\tprivate Map<String, Object> options = null;\n\n\t\tprivate SaslCallbackHandler(final Map<String, Object> options) {\n\t\t\tthis.options = options;\n\t\t}\n\n\t\t// Implementation of javax.security.auth.callback.CallbackHandler\n\n\t\t@Override\n\t\tpublic void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException {\n\t\t\tBareJID jid = null;\n\n\t\t\tfor (int i = 0; i < callbacks.length; i++) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Callback: {0}\", callbacks[i].getClass().getSimpleName());\n\t\t\t\t}\n\n\t\t\t\tif (callbacks[i] instanceof RealmCallback) {\n\t\t\t\t\tRealmCallback rc = (RealmCallback) callbacks[i];\n\t\t\t\t\tString realm = (String) options.get(REALM_KEY);\n\n\t\t\t\t\tif (realm != null) {\n\t\t\t\t\t\trc.setText(realm);\n\t\t\t\t\t} // end of if (realm == null)\n\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"RealmCallback: {0}\", realm);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (callbacks[i] instanceof NameCallback) {\n\t\t\t\t\t\tNameCallback nc = (NameCallback) callbacks[i];\n\t\t\t\t\t\tString user_name = nc.getName();\n\n\t\t\t\t\t\tif (user_name == null) {\n\t\t\t\t\t\t\tuser_name = nc.getDefaultName();\n\t\t\t\t\t\t} // end of if (name == null)\n\n\t\t\t\t\t\tjid = BareJID.bareJIDInstanceNS(user_name, (String) options.get(REALM_KEY));\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tfinal AccountStatus accountStatus = getAccountStatus(jid);\n\t\t\t\t\t\t\tif (accountStatus.isInactive()) {\n\t\t\t\t\t\t\t\tthrow XmppSaslException.getExceptionFor(accountStatus);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (TigaseDBException e) {\n\t\t\t\t\t\t\tthrow new IOException(\"Account Status retrieving problem.\", e);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\toptions.put(USER_ID_KEY, jid);\n\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"NameCallback: {0}\", user_name);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (callbacks[i] instanceof PasswordCallback) {\n\t\t\t\t\t\t\tPasswordCallback pc = (PasswordCallback) callbacks[i];\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tString passwd = getPassword(jid);\n\n\t\t\t\t\t\t\t\tpc.setPassword(passwd.toCharArray());\n\n\t\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"PasswordCallback: {0}\", passwd);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\tthrow new IOException(\"Password retrieving problem.\", e);\n\t\t\t\t\t\t\t} // end of try-catch\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (callbacks[i] instanceof AuthorizeCallback) {\n\t\t\t\t\t\t\t\tAuthorizeCallback authCallback = ((AuthorizeCallback) callbacks[i]);\n\t\t\t\t\t\t\t\tString authenId = authCallback.getAuthenticationID();\n\n\t\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"AuthorizeCallback: authenId: {0}\", authenId);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tString authorId = authCallback.getAuthorizationID();\n\n\t\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"AuthorizeCallback: authorId: {0}\", authorId);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (authenId.equals(authorId)) {\n\t\t\t\t\t\t\t\t\tauthCallback.setAuthorized(true);\n\t\t\t\t\t\t\t\t} // end of if (authenId.equals(authorId))\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthrow new UnsupportedCallbackException(callbacks[i], \"Unrecognized Callback\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n} // TigaseCustomAuth\n\n"
  },
  {
    "path": "src/main/java/tigase/db/jdbc/TigaseSPAuth.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.jdbc;\n\nimport tigase.auth.mechanisms.AbstractSaslSCRAM;\nimport tigase.db.*;\nimport tigase.db.util.RepositoryVersionAware;\nimport tigase.util.Base64;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.security.InvalidKeyException;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.SecureRandom;\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Tigase Salted Password Auth.\n */\n@Repository.SchemaId(id = Schema.SERVER_SCHEMA_ID, name = Schema.SERVER_SCHEMA_NAME)\npublic class TigaseSPAuth\n\t\textends TigaseCustomAuth\n\t\timplements RepositoryVersionAware {\n\n\tprivate static final Logger log = Logger.getLogger(TigaseSPAuth.class.getName());\n\n\tprivate static final SecureRandom random = new SecureRandom();\n\n\tprivate static final String encode(String pwd) throws InvalidKeyException, NoSuchAlgorithmException {\n\t\tbyte[] salt = new byte[20];\n\t\trandom.nextBytes(salt);\n\t\treturn encode(pwd, salt);\n\t}\n\n\tprivate static final String encode(final String pwd, final byte[] salt)\n\t\t\tthrows InvalidKeyException, NoSuchAlgorithmException {\n\t\tbyte[] saltedPassword = AbstractSaslSCRAM.hi(\"SHA1\", AbstractSaslSCRAM.normalize(pwd), salt, 4096);\n\t\tbyte[] result = new byte[salt.length + saltedPassword.length];\n\n\t\tSystem.arraycopy(salt, 0, result, 0, salt.length);\n\t\tSystem.arraycopy(saltedPassword, 0, result, salt.length, saltedPassword.length);\n\n\t\treturn Base64.encode(result);\n\t}\n\n\t@Override\n\tpublic void addUser(BareJID user, String password) throws UserExistsException, TigaseDBException {\n\t\ttry {\n\t\t\tsuper.addUser(user, encode(password));\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Can't add user \" + user, e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean otherAuth(Map<String, Object> props)\n\t\t\tthrows UserNotFoundException, TigaseDBException, AuthorizationException {\n\t\tString password = (String) props.get(PASSWORD_KEY);\n\t\tBareJID user_id = (BareJID) props.get(USER_ID_KEY);\n\n\t\ttry {\n\t\t\tprops.put(PASSWORD_KEY, encodeWithUserSalt(user_id, password));\n\t\t\treturn super.otherAuth(props);\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Can't salt user password\", e);\n\t\t\tthrow new AuthorizationException(\"Can't salt user password\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void updatePassword(BareJID user, String password) throws UserNotFoundException, TigaseDBException {\n\t\ttry {\n\t\t\tsuper.updatePassword(user, encode(password));\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Can't update password for user \" + user, e);\n\t\t}\n\t}\n\n\tprivate String encodeWithUserSalt(final BareJID user, final String password)\n\t\t\tthrows UserNotFoundException, TigaseDBException, InvalidKeyException, NoSuchAlgorithmException {\n\t\tString pwd = getPassword(user);\n\t\tif (pwd == null) {\n\t\t\tthrow new UserNotFoundException(\"User \" + user + \" not found.\");\n\t\t}\n\t\tbyte[] buffer = Base64.decode(pwd);\n\t\tbyte[] salt = new byte[20];\n\t\tSystem.arraycopy(buffer, 0, salt, 0, salt.length);\n\n\t\treturn encode(password, salt);\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate boolean isPasswordValid(final BareJID user, final String password)\n\t\t\tthrows UserNotFoundException, TigaseDBException, InvalidKeyException, NoSuchAlgorithmException {\n\n\t\t// String saltedPassword = getPassword(user);\n\t\tString pwd = getPassword(user);\n\t\tif (pwd == null) {\n\t\t\tthrow new UserNotFoundException(\"User \" + user + \" not found.\");\n\t\t}\n\t\tbyte[] buffer = Base64.decode(pwd);\n\t\tbyte[] salt = new byte[20];\n\t\tbyte[] saltedPassword = new byte[20];\n\t\tSystem.arraycopy(buffer, 0, salt, 0, salt.length);\n\t\tSystem.arraycopy(buffer, salt.length, saltedPassword, 0, saltedPassword.length);\n\n\t\tbyte[] np = AbstractSaslSCRAM.hi(\"SHA1\", AbstractSaslSCRAM.normalize(password), salt, 4096);\n\n\t\treturn Arrays.equals(saltedPassword, np);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/ldap/LdapAuthProvider.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.ldap;\n\nimport tigase.auth.credentials.Credentials;\nimport tigase.db.*;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.util.Base64;\nimport tigase.xmpp.jid.BareJID;\n\nimport javax.naming.Context;\nimport javax.naming.directory.DirContext;\nimport javax.naming.directory.InitialDirContext;\nimport javax.security.sasl.SaslException;\nimport javax.security.sasl.SaslServer;\nimport java.time.Duration;\nimport java.util.Hashtable;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Repository.Meta(supportedUris = {\"ldaps?:.*\"})\npublic class LdapAuthProvider\n\t\timplements AuthRepository {\n\n\t/**\n\t * Example: <code>uid=%s,ou=people,dc=xmpp-test,dc=org</code>\n\t */\n\tpublic static final String USER_DN_PATTERN_KEY = \"user-dn-pattern\";\n\tprotected static final String[] non_sasl_mechs = {\"password\"};\n\tprotected static final String[] sasl_mechs = {\"PLAIN\"};\n\tprivate static final Logger log = Logger.getLogger(LdapAuthProvider.class.getName());\n\t@ConfigField(desc = \"LDAP provider URL\", alias = \"uri\")\n\tprivate String providerUrl;\n\t@ConfigField(desc = \"User DN pattern\", alias = USER_DN_PATTERN_KEY)\n\tprivate String userDnPattern;\n\n\t@Override\n\tpublic void addUser(BareJID user, String password) throws UserExistsException, TigaseDBException {\n\t\tthrow new TigaseDBException(\"Not available\");\n\t}\n\n\t@Override\n\tpublic Credentials getCredentials(BareJID user, String credentialId) throws TigaseDBException {\n\t\tCredentials.Entry entry = new Credentials.Entry() {\n\t\t\t@Override\n\t\t\tpublic String getMechanism() {\n\t\t\t\treturn \"PLAIN\";\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean verifyPlainPassword(String plain) {\n\t\t\t\ttry {\n\t\t\t\t\treturn LdapAuthProvider.this.doBindAuthentication(user, plain);\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Can''t authenticate user\", ex);\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t};\n\n\t\treturn new SingleCredential(user, getAccountStatus(user), entry);\n\t}\n\n\t@Override\n\tpublic String getResourceUri() {\n\t\treturn providerUrl;\n\t}\n\n\t@Override\n\tpublic long getActiveUsersCountIn(Duration duration) {\n\t\treturn -1;\n\t}\n\n\t@Override\n\tpublic long getUsersCount() {\n\t\treturn -1;\n\t}\n\n\t@Override\n\tpublic long getUsersCount(String domain) {\n\t\treturn -1;\n\t}\n\n\t@Override\n\t@Deprecated\n\tpublic void initRepository(String resource_uri, Map<String, String> params) throws DBInitException {\n\t\tthis.userDnPattern = params.get(USER_DN_PATTERN_KEY);\n\t\tthis.providerUrl = resource_uri;\n\t\tif (log.isLoggable(Level.CONFIG)) {\n\t\t\tlog.config(\"User DN Pattern: \" + this.userDnPattern);\n\t\t\tlog.config(\"LDAP URL: \" + this.providerUrl);\n\t\t}\n\t}\n\n\t;\n\n\t@Override\n\tpublic void logout(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t}\n\n\t@Override\n\tpublic void loggedIn(BareJID jid) throws TigaseDBException {\n\t}\n\n\t@Override\n\tpublic boolean otherAuth(Map<String, Object> props)\n\t\t\tthrows UserNotFoundException, TigaseDBException, AuthorizationException {\n\t\tString proto = (String) props.get(PROTOCOL_KEY);\n\n\t\tif (proto.equals(PROTOCOL_VAL_SASL)) {\n\t\t\tif (props.get(MACHANISM_KEY).equals(\"PLAIN\")) {\n\t\t\t\treturn saslAuth(props);\n\t\t\t}\n\t\t} else if (proto.equals(PROTOCOL_VAL_NONSASL)) {\n\t\t\tString password = (String) props.get(PASSWORD_KEY);\n\t\t\tBareJID user_id = (BareJID) props.get(USER_ID_KEY);\n\t\t\tboolean auth = doBindAuthentication(user_id, password);\n\t\t\tif (auth) {\n\t\t\t\tprops.put(USER_ID_KEY, user_id);\n\t\t\t}\n\t\t\treturn auth;\n\t\t}\n\n\t\tthrow new AuthorizationException(\"Protocol is not supported.\");\n\t}\n\n\t@Override\n\tpublic void queryAuth(Map<String, Object> authProps) {\n\t\tString protocol = (String) authProps.get(PROTOCOL_KEY);\n\n\t\tif (protocol.equals(PROTOCOL_VAL_NONSASL)) {\n\t\t\tauthProps.put(RESULT_KEY, non_sasl_mechs);\n\t\t}\n\n\t\tif (protocol.equals(PROTOCOL_VAL_SASL)) {\n\t\t\tauthProps.put(RESULT_KEY, sasl_mechs);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void removeUser(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\tthrow new TigaseDBException(\"Not available\");\n\t}\n\n\t@Override\n\tpublic void updatePassword(BareJID user, String password) throws UserNotFoundException, TigaseDBException {\n\t\tthrow new TigaseDBException(\"Not available\");\n\t}\n\n\t@Override\n\tpublic String getPassword(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\tthrow new TigaseDBException(\"Not available\");\n\t}\n\n\t@Override\n\tpublic boolean isUserDisabled(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void setUserDisabled(BareJID user, Boolean value) throws UserNotFoundException, TigaseDBException {\n\t\tthrow new TigaseDBException(\"Feature not supported\");\n\t}\n\n\t@Override\n\tpublic void setAccountStatus(BareJID user, AccountStatus status) throws TigaseDBException {\n\t\tthrow new TigaseDBException(\"Feature not supported\");\n\t}\n\n\t@Override\n\tpublic AccountStatus getAccountStatus(BareJID user) throws TigaseDBException {\n\t\treturn AccountStatus.active;\n\t}\n\n\tprivate boolean doBindAuthentication(BareJID userId, final String password)\n\t\t\tthrows UserNotFoundException, TigaseDBException, AuthorizationException {\n\t\ttry {\n\t\t\tHashtable<Object, Object> env = new Hashtable<Object, Object>();\n\t\t\tenv.put(Context.INITIAL_CONTEXT_FACTORY, \"com.sun.jndi.ldap.LdapCtxFactory\");\n\t\t\tenv.put(Context.PROVIDER_URL, this.providerUrl);\n\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.fine(\"Authenticating user '\" + userId + \"' with password ******\");\n\t\t\t}\n\n\t\t\tfinal String dn = String.format(this.userDnPattern, userId.getLocalpart(), userId.getDomain(),\n\t\t\t\t\t\t\t\t\t\t\tuserId.toString());\n\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.finer(\"Using DN:\" + dn);\n\t\t\t}\n\n\t\t\tenv.put(Context.SECURITY_AUTHENTICATION, \"simple\");\n\t\t\tenv.put(Context.SECURITY_PRINCIPAL, dn);\n\t\t\tenv.put(Context.SECURITY_CREDENTIALS, password);\n\n\t\t\t// Create the initial context\n\t\t\tDirContext ctx = new InitialDirContext(env);\n\t\t\tctx.close();\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.fine(\"User \" + userId + \" authenticated.\");\n\t\t\t}\n\t\t\treturn true;\n\t\t} catch (javax.naming.AuthenticationException e) {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"Authentication error: \" + e.getMessage());\n\t\t\t}\n\t\t\treturn false;\n\t\t} catch (Exception e) {\n\t\t\tif (log.isLoggable(Level.WARNING)) {\n\t\t\t\tlog.log(Level.WARNING, \"Can''t authenticate user\", e);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t}\n\n\tprivate boolean saslAuth(final Map<String, Object> props) throws AuthorizationException {\n\t\ttry {\n\t\t\tSaslPLAINLdap ss = new SaslPLAINLdap((String) props.get(SERVER_NAME_KEY));\n\n\t\t\tString data_str = (String) props.get(DATA_KEY);\n\t\t\tbyte[] in_data = ((data_str != null) ? Base64.decode(data_str) : new byte[0]);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"response: \" + new String(in_data));\n\t\t\t}\n\n\t\t\tbyte[] challenge = ss.evaluateResponse(in_data);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"challenge: \" + ((challenge != null) ? new String(challenge) : \"null\"));\n\t\t\t}\n\n\t\t\tString challenge_str = (((challenge != null) && (challenge.length > 0)) ? Base64.encode(challenge) : null);\n\n\t\t\tprops.put(RESULT_KEY, challenge_str);\n\n\t\t\tif (ss.isComplete()) {\n\t\t\t\tprops.put(USER_ID_KEY, ss.getUser_id());\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\treturn false;\n\t\t\t} // end of if (ss.isComplete()) else\n\t\t} catch (SaslException e) {\n\t\t\tthrow new AuthorizationException(\"Sasl exception.\", e);\n\t\t}\n\t}\n\n\tprivate class SaslPLAINLdap\n\t\t\timplements SaslServer {\n\n\t\tprivate final String serverName;\n\t\tprivate boolean authOk = false;\n\t\tprivate BareJID userId;\n\n\t\tpublic SaslPLAINLdap(String serverName) {\n\t\t\tthis.serverName = serverName;\n\t\t}\n\n\t\t@Override\n\t\tpublic void dispose() throws SaslException {\n\t\t}\n\n\t\t@Override\n\t\tpublic byte[] evaluateResponse(byte[] byteArray) throws SaslException {\n\t\t\tint auth_idx = 0;\n\n\t\t\twhile ((byteArray[auth_idx] != 0) && (auth_idx < byteArray.length)) {\n\t\t\t\t++auth_idx;\n\t\t\t}\n\n\t\t\tint user_idx = ++auth_idx;\n\n\t\t\twhile ((byteArray[user_idx] != 0) && (user_idx < byteArray.length)) {\n\t\t\t\t++user_idx;\n\t\t\t}\n\n\t\t\tfinal String user_id = new String(byteArray, auth_idx, user_idx - auth_idx);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"SASL userId: \" + user_id);\n\t\t\t}\n\n\t\t\t++user_idx;\n\n\t\t\tfinal String passwd = new String(byteArray, user_idx, byteArray.length - user_idx);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"SASL password: \" + passwd);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tuserId = BareJID.bareJIDInstance(user_id, serverName);\n\t\t\t\tauthOk = doBindAuthentication(userId, passwd);\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.WARNING, \"Can''t authenticate user\", e);\n\t\t\t\tauthOk = false;\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getAuthorizationID() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getMechanismName() {\n\t\t\treturn \"PLAIN\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getNegotiatedProperty(String propName) {\n\t\t\treturn null;\n\t\t}\n\n\t\tpublic BareJID getUser_id() {\n\t\t\treturn userId;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isComplete() {\n\t\t\treturn authOk;\n\t\t}\n\n\t\t@Override\n\t\tpublic byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException {\n\t\t\treturn null;\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/services/AccountExpirationService.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.services;\n\nimport org.jspecify.annotations.NonNull;\nimport tigase.db.AuthRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserRepository;\nimport tigase.eventbus.EventBus;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.xmppsession.DisconnectUserEBAction;\nimport tigase.xmpp.StreamError;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.time.Duration;\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.temporal.ChronoUnit;\nimport java.time.temporal.TemporalUnit;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Bean(name = \"account-expiration-service\", active = false, parent = Kernel.class, exportable = true)\npublic class AccountExpirationService\n\timplements Initializable {\n\n\tpublic static final String ACCOUNT_EXPIRATION_DATE = \"account-expiration-date\";\n\tprivate static final Logger log = Logger.getLogger(AccountExpirationService.class.getCanonicalName());\n\tprivate static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);\n\t@Inject\n\tprivate AuthRepository authRepository;\n\t@Inject\n\tprivate EventBus eventBus;\n\tprivate RemoveExpiredAccountsTask expiredMessagesRemovalTask = null;\n\tprivate LocalTime scheduledRemovalTimeLT = LocalTime.of(0, 5);\n\t@ConfigField(desc = \"Time at which removal of expired accounts should be executed\", alias = \"scheduled-expired-accounts-removal-time\")\n\tprivate String scheduledRemovalTime = scheduledRemovalTimeLT.toString();\n\t@Inject\n\tprivate UserRepository userRepository;\n\n\t@Override\n\tpublic void initialize() {\n\t\tfinal LocalTime now = LocalTime.now();\n\t\tDuration initialDelay = now.isAfter(scheduledRemovalTimeLT) ? Duration.ofHours(24)\n\t\t\t.minus(Duration.between(scheduledRemovalTimeLT, now)) : Duration.between(now, scheduledRemovalTimeLT);\n\n\t\tlog.log(Level.CONFIG,\n\t\t        \"Scheduling removing expired users accounts at: \" + scheduledRemovalTimeLT + \", next run in: \" +\n\t\t\t        initialDelay + \", sec: \" + initialDelay.getSeconds());\n\t\tscheduler.scheduleAtFixedRate(new RemoveExpiredAccountsTask(), initialDelay.getSeconds(),\n\t\t                              TimeUnit.DAYS.toSeconds(1), TimeUnit.SECONDS);\n\t}\n\n\tvoid removeExpiredAccounts() throws TigaseDBException {\n\t\tlog.log(Level.FINEST, \"Running expired accounts removal task\");\n\t\tfinal Map<BareJID, String> dataMap = userRepository.getDataMap(ACCOUNT_EXPIRATION_DATE);\n\t\tfinal LocalDate now = LocalDate.now();\n\t\tfor (Map.Entry<BareJID, String> jidExpirationEntry : dataMap.entrySet()) {\n\t\t\tif (jidExpirationEntry.getValue() != null && LocalDate.parse(jidExpirationEntry.getValue()).isBefore(now)) {\n\t\t\t\tlog.log(Level.FINE, \"Removing expired account: \" + jidExpirationEntry.getKey() + \", expiration date: \" + jidExpirationEntry.getValue());\n\t\t\t\tauthRepository.removeUser(jidExpirationEntry.getKey());\n\t\t\t\teventBus.fire(\n\t\t\t\t\tnew DisconnectUserEBAction(jidExpirationEntry.getKey(), StreamError.Reset, \"Account was deleted\"));\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void setScheduledRemovalTime(String scheduledRemovalTime) {\n\t\tthis.scheduledRemovalTime = scheduledRemovalTime;\n\t\tthis.scheduledRemovalTimeLT = LocalTime.parse(scheduledRemovalTime);\n\t}\n\n\tpublic void setUserExpiration(@NonNull BareJID userId, Integer expirationDays) throws TigaseDBException {\n\t\tObjects.requireNonNull(userId);\n\t\tlog.log(Level.FINE, \"Setting user expiration for \" + userId + \" to \" + expirationDays);\n\t\tif (expirationDays != null) {\n\n\t\t\tif (expirationDays <= 0) {\n\t\t\t\tuserRepository.removeData(userId, ACCOUNT_EXPIRATION_DATE);\n\t\t\t} else {\n\t\t\t\tvar localDate = LocalDate.now().plusDays(expirationDays);\n\t\t\t\tuserRepository.setData(userId, ACCOUNT_EXPIRATION_DATE, localDate.toString());\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic Long getUserExpirationDays(@NonNull BareJID userId) {\n\t\treturn getUserExpiration(userId).map(d -> LocalDate.now().until(d, ChronoUnit.DAYS)).orElse(0L);\n\t}\n\t/**\n\t * @param userId JID of the user for which remaining expiration time should\n\t *\n\t * @return expiration date if the expiration date is present\n\t *\n\t */\n\tpublic Optional<LocalDate> getUserExpiration(@NonNull BareJID userId) {\n\t\tObjects.requireNonNull(userId);\n\t\tlog.log(Level.FINE, \"Getting user expiration for \" + userId);\n\n\t\ttry {\n\t\t\tvar expirationDate = userRepository.getData(userId, ACCOUNT_EXPIRATION_DATE);\n\t\t\tif (expirationDate != null) {\n\t\t\t\treturn Optional.of(LocalDate.parse(expirationDate));\n\t\t\t}\n\t\t} catch (TigaseDBException e) {\n\t\t\tlog.log(Level.WARNING, \"Error obtaining user expiration date from repository\", e);\n\t\t}\n\t\treturn Optional.empty();\n\t}\n\n\n\tprivate class RemoveExpiredAccountsTask\n\t\timplements Runnable {\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\ttry {\n\t\t\t\tremoveExpiredAccounts();\n\t\t\t} catch (TigaseDBException e) {\n\t\t\t\tlog.log(Level.SEVERE, \"Removing expired accounts failed\", e);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/util/DBInitForkJoinPoolCache.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.util;\n\nimport java.util.HashMap;\nimport java.util.Timer;\nimport java.util.TimerTask;\nimport java.util.concurrent.ForkJoinPool;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class DBInitForkJoinPoolCache {\n\n\tprivate static final Logger log = Logger.getLogger(DBInitForkJoinPoolCache.class.getCanonicalName());\n\n\tpublic static final DBInitForkJoinPoolCache shared = new DBInitForkJoinPoolCache();\n\n\tprivate final HashMap<String, Item> cache = new HashMap<String, Item>();\n\tprivate Timer timer = null;\n\n\tpublic synchronized ForkJoinPool pool(String key, int concurrency) {\n\t\tItem item = cache.get(key);\n\t\tif (item == null) {\n\t\t\titem = new Item(key, concurrency);\n\t\t\tcache.put(key, item);\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"creating fork-join thread pool for \" + key);\n\t\t\t}\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"reusing fork-join thread pool for \" + key);\n\t\t\t}\n\t\t}\n\t\tif (timer == null) {\n\t\t\ttimer = new Timer(\"dbinit-fork-join-pool-cache-timer\", true);\n\t\t}\n\t\titem.restartTimer(timer);\n\t\treturn item.getPool();\n\t}\n\n\tprivate synchronized void release(Item item) {\n\t\tcache.remove(item.getKey());\n\t\tif (cache.isEmpty()) {\n\t\t\ttimer.cancel();\n\t\t\ttimer = null;\n\t\t}\n\t\titem.getPool().shutdown();\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"releasing fork-join thread pool for \" + item.getKey());\n\t\t}\n\t}\n\t\n\tprivate class Item {\n\t\tprivate final String key;\n\t\tprivate final ForkJoinPool pool;\n\t\tprivate TimerTask timerTask;\n\n\t\tpublic Item(String key, int concurrency) {\n\t\t\tthis.key = key;\n\t\t\tthis.pool = new ForkJoinPool(concurrency);\n\t\t}\n\n\t\tpublic String getKey() {\n\t\t\treturn key;\n\t\t}\n\n\t\tpublic ForkJoinPool getPool() {\n\t\t\treturn pool;\n\t\t}\n\n\t\tpublic void restartTimer(Timer timer) {\n\t\t\tif (timerTask != null) {\n\t\t\t\ttimerTask.cancel();\n\t\t\t\ttimerTask = null;\n\t\t\t}\n\t\t\ttimerTask = new TimerTask() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tsynchronized (DBInitForkJoinPoolCache.this) {\n\t\t\t\t\t\tif (Item.this.timerTask != this) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tDBInitForkJoinPoolCache.this.release(Item.this);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\t\t\ttimer.schedule(timerTask, 60l * 1000);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/util/DBSchemaLoader.java",
    "content": "/*\r\n * Tigase XMPP Server - The instant messaging server\r\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\r\n *\r\n * This program is free software: you can redistribute it and/or modify\r\n * it under the terms of the GNU Affero General Public License as published by\r\n * the Free Software Foundation, version 3 of the License.\r\n *\r\n * This program is distributed in the hope that it will be useful,\r\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n * GNU Affero General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU Affero General Public License\r\n * along with this program. Look for COPYING file in the top folder.\r\n * If not, see http://www.gnu.org/licenses/.\r\n */\r\npackage tigase.db.util;\r\n\r\nimport tigase.component.exceptions.RepositoryException;\r\nimport tigase.db.AuthRepository;\r\nimport tigase.db.DataRepository;\r\nimport tigase.db.DataSource;\r\nimport tigase.db.Schema;\r\nimport tigase.db.jdbc.DataRepositoryImpl;\r\nimport tigase.db.util.locker.ConnectionLock;\r\nimport tigase.server.extdisco.ExtServiceDiscoItem;\r\nimport tigase.server.extdisco.ExtServiceDiscoveryUserRepoRepository;\r\nimport tigase.util.Version;\r\nimport tigase.util.log.LogFormatter;\r\nimport tigase.util.ui.console.CommandlineParameter;\r\nimport tigase.util.ui.console.SystemConsole;\r\nimport tigase.xmpp.jid.BareJID;\r\n\r\nimport java.io.IOException;\r\nimport java.io.InputStream;\r\nimport java.nio.file.*;\r\nimport java.nio.file.attribute.BasicFileAttributes;\r\nimport java.sql.*;\r\nimport java.util.*;\r\nimport java.util.concurrent.atomic.AtomicReference;\r\nimport java.util.function.BiPredicate;\r\nimport java.util.function.Function;\r\nimport java.util.logging.ConsoleHandler;\r\nimport java.util.logging.Handler;\r\nimport java.util.logging.Level;\r\nimport java.util.logging.Logger;\r\nimport java.util.regex.Matcher;\r\nimport java.util.regex.Pattern;\r\nimport java.util.stream.Collectors;\r\nimport java.util.stream.Stream;\r\n\r\nimport static tigase.db.DataRepository.dbTypes;\r\nimport static tigase.db.jdbc.DataRepositoryImpl.JDBC_SCHEMA_VERSION_QUERY;\r\n\r\n/**\r\n * Simple utility class allowing various Database operations, including executing simple queries, loading specific\r\n * schema files or performing complete load of all Tigase schema required to run the server.\r\n * <br>\r\n * Following set of {@link Properties} is accepted: <ul> <li>{@code dbType} - type of the database, possible values are:\r\n * {@code mysql}, {@code postgresql}, {@code derby}, {@code sqlserver}; </li><li>{@code schemaVersion} - schema version to be\r\n * loaded, , possible values are: {@code 7-2}, {@code 7-1}, {@code 5-1}, {@code 5}, {@code 4}; </li><li>{@code dbName} - name\r\n * of the database to be created/used; </li><li>{@code dbHostname} - hostname of the database; </li><li>{@code dbUser} - username\r\n * of the regular user; </li><li>{@code dbPass} - password of the regular user; </li><li>{@code rootUser} - username of the\r\n * database administrator user; </li><li>{@code rootPass} - password of the database administrator user; </li><li>{@code query} -\r\n * simple, single query to be executed; </li><li>{@code file} - path to the single schema file to be loaded to the database;\r\n * </li><li>{@code adminJID} - JID address of the XMPP administrator account; </li><li>{@code adminJIDpass} - password of the XMPP\r\n * administrator account.</li></ul>\r\n *\r\n * @author wojtek\r\n */\r\npublic class DBSchemaLoader\r\n\t\textends SchemaLoader<DBSchemaLoader.Parameters> {\r\n\r\n\tprivate static final Logger log = Logger.getLogger(DBSchemaLoader.class.getCanonicalName());\r\n\tpublic enum PARAMETERS_ENUM {\r\n\t\tDATABASE_TYPE(\"dbType\", \"mysql\"),\r\n\t\tSCHEMA_VERSION(\"schemaVersion\", \"8-0\"),\r\n\t\tDATABASE_NAME(\"dbName\", \"tigasedb\"),\r\n\t\tDATABASE_HOSTNAME(\"dbHostname\", \"localhost\"),\r\n\t\tTIGASE_USERNAME(\"dbUser\", \"tigase_user\"),\r\n\t\tTIGASE_PASSWORD(\"dbPass\", \"tigase_pass\"),\r\n\t\tROOT_USERNAME(\"rootUser\", null),\r\n\t\tROOT_PASSWORD(\"rootPass\", null),\r\n\t\tROOT_ASK(\"rootAsk\", \"false\"),\r\n\t\tLOG_LEVEL(\"logLevel\", \"CONFIG\"),\r\n\t\tUSE_SSL(\"useSSL\", \"false\"),\r\n\t\tGET_URI(\"getURI\", \"false\"),\r\n\t\tQUERY(\"query\", null),\r\n\t\tFILE(\"file\", null),\r\n\t\tADMIN_JID(\"adminJID\", null),\r\n\t\tADMIN_JID_PASS(\"adminJIDpass\", null),\r\n\t\tIGNORE_MISSING_FILES(\"ignoreMissingFiles\", \"false\"),\r\n\t\tFORCE_RELOAD_ALL_SCHEMA_FILES(\"forceReloadAllSchemaFiles\", \"false\"),\r\n\t\tDATABASE_OPTIONS(\"dbOptions\", null),\r\n\t\tUSE_LEGACY_DATETIME_CODE(\"useLegacyDatetimeCode\", \"false\"),\r\n\t\tSERVER_TIMEZONE(\"serverTimezone\", null);\r\n\r\n\t\tprivate String defaultValue = null;\r\n\t\tprivate String name = null;\r\n\r\n\t\tPARAMETERS_ENUM(String name, String defaultValue) {\r\n\t\t\tthis.name = name;\r\n\t\t\tthis.defaultValue = defaultValue;\r\n\t\t}\r\n\r\n\t\tpublic String getName() {\r\n\t\t\treturn name;\r\n\t\t}\r\n\r\n\t\tpublic String getDefaultValue() {\r\n\t\t\treturn defaultValue;\r\n\t\t}\r\n\t}\r\n\t/**\r\n\t * Denotes whether there wasn't any problem establishing connection to the database\r\n\t */\r\n\tprivate boolean connection_ok = false;\r\n\t/** Denotes whether database exists */\r\n\tprivate boolean db_ok = false;\r\n\tprivate Parameters params;\r\n\t/** Holds map of all replacement variables */\r\n\tprivate Map<String, String> replacementMap = new HashMap<String, String>();\r\n\t/** Denotes whether schema has proper version */\r\n\tprivate boolean schema_ok = false;\r\n\tprivate Optional<ConnectionLock> connectionLock = null;\r\n\r\n\t/**\r\n\t * Main method allowing pass arguments to the class and setting all logging to be printed to console.\r\n\t *\r\n\t * @param args key-value (in the form of {@code \"-<variable> value\"}) parameters.\r\n\t */\r\n\tpublic static void main(String[] args) {\r\n\t\tSchemaLoader.main(args);\r\n\t}\r\n\r\n\tprivate static final List<TypeInfo> suppertedTypes = Stream.of(\r\n\t\t\tnew TypeInfo(10, \"derby\", \"Derby (built in database)\", \"org.apache.derby.jdbc.EmbeddedDriver\"),\r\n\t\t\tnew TypeInfo(20, \"mysql\", \"MySQL\", \"com.mysql.cj.jdbc.Driver\"),\r\n\t\t\tnew TypeInfo(30, \"postgresql\", \"PostgreSQL\", \"org.postgresql.Driver\"),\r\n\t\t\tnew TypeInfo(40, \"sqlserver\", \"SQLServer\", \"com.microsoft.sqlserver.jdbc.SQLServerDriver\"))\r\n\t\t\t.collect(Collectors.toList());\r\n\r\n\tpublic DBSchemaLoader() {\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void init(Parameters params, Optional<SchemaManager.RootCredentialsCache> rootCredentialsCache) {\r\n\t\tparams.init(rootCredentialsCache);\r\n\t\tfor (PARAMETERS_ENUM p : PARAMETERS_ENUM.values()) {\r\n\t\t\tString value = null;\r\n\t\t\tswitch (p) {\r\n\t\t\t\tcase DATABASE_TYPE:\r\n\t\t\t\t\tvalue = params.getDbType();\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase DATABASE_HOSTNAME:\r\n\t\t\t\t\tvalue = params.getDbHostname();\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase DATABASE_NAME:\r\n\t\t\t\t\tvalue = params.getDbName();\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase TIGASE_USERNAME:\r\n\t\t\t\t\tvalue = params.getDbUser();\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase TIGASE_PASSWORD:\r\n\t\t\t\t\tvalue = params.getDbPass();\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase ROOT_USERNAME:\r\n\t\t\t\t\tvalue = params.getDbRootUser();\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase ROOT_PASSWORD:\r\n\t\t\t\t\tvalue = params.getDbRootPass();\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tdefault:\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tif (value != null) {\r\n\t\t\t\treplacementMap.put(\"${\" + p.getName() + \"}\", value);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// configure logger\r\n\t\tLevel lvl = params.logLevel;\r\n\r\n\t\tLogger logSetup = Logger.getLogger(\"tigase.db.util\");\r\n\t\tlogSetup.setUseParentHandlers(false);\r\n\t\tlogSetup.setLevel(lvl);\r\n\r\n\t\tArrays.stream(logSetup.getHandlers())\r\n\t\t\t\t.filter((handler) -> handler instanceof ConsoleHandler)\r\n\t\t\t\t.findAny()\r\n\t\t\t\t.orElseGet(() -> {\r\n\t\t\t\t\tHandler handler = new ConsoleHandler();\r\n\t\t\t\t\thandler.setLevel(lvl);\r\n\t\t\t\t\thandler.setFormatter(new LogFormatter());\r\n\t\t\t\t\tlogSetup.addHandler(handler);\r\n\t\t\t\t\treturn handler;\r\n\t\t\t\t});\r\n\r\n\t\tlog.log(Level.CONFIG, \"Parameters: {0}\", new Object[]{params});\r\n\t\tthis.params = params;\r\n\t}\r\n\r\n\tpublic List<TypeInfo> getSupportedTypes() {\r\n\t\treturn suppertedTypes;\r\n\t}\r\n\r\n\tpublic Map<Version,Path> getSchemaFileNames(String schemaId) {\r\n\t\tPath databaseDirectory = Paths.get(params.getSchemaDirectory());\r\n\t\tString databaseType = params.getDbType();\r\n\r\n\t\tfinal BiPredicate<Path, BasicFileAttributes> predicate = (path, attributes) -> {\r\n\t\t\tfinal String regex = databaseType + \"-\" + schemaId + \"(-\\\\d+\\\\.\\\\d+\\\\.\\\\d+)(-b\\\\d+)?\\\\.sql\";\r\n\t\t\treturn path.getFileName().toString().matches(regex);\r\n\t\t};\r\n\r\n\t\ttry (final Stream<Path> pathStream = Files.find(databaseDirectory, 1, predicate, FileVisitOption.FOLLOW_LINKS)) {\r\n\t\t\treturn pathStream.map(Path::getFileName)\r\n\t\t\t\t\t.map(filename -> new AbstractMap.SimpleImmutableEntry<>(\r\n\t\t\t\t\t\t\tgetVersionFromSchemaFilename(filename, databaseType, schemaId), filename))\r\n\t\t\t\t\t.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> null, TreeMap::new));\r\n\t\t} catch (IOException e) {\r\n\t\t\tlog.log(Level.WARNING, \"Error while getting schema file list: {0}\", e.getMessage());\r\n\t\t}\r\n\r\n\t\treturn Collections.emptyMap();\r\n\t}\r\n\r\n\tstatic Map<Version, Path> getSchemaFileNamesInRange(Map<Version, Path> paths, Optional<Version> currentVersion,\r\n\t                                                    Version requiredVersion) {\r\n\r\n\t\tboolean isRequiredFinal = Version.TYPE.FINAL.equals(requiredVersion.getVersionType());\r\n\t\tboolean isCurrentFinal =\r\n\t\t\t\tcurrentVersion.isPresent() && Version.TYPE.FINAL.equals(currentVersion.get().getVersionType());\r\n\r\n\t\tboolean startInclusive = !isCurrentFinal;// || !isRequiredFinal;\r\n\t\tboolean endInclusive = true; //isCurrentFinal;// || !isRequiredFinal;\r\n\r\n\t\treturn paths.entrySet()\r\n\t\t\t\t.stream()\r\n\t\t\t\t.sorted(Comparator.comparing(Map.Entry::getKey, Version.VERSION_COMPARATOR))\r\n\t\t\t\t.filter(entry -> !currentVersion.isPresent()\r\n\t\t\t\t\t\t|| (startInclusive\r\n\t\t\t\t\t\t    ? entry.getKey().compareTo(currentVersion.get()) >= 0\r\n\t\t\t\t\t\t    : entry.getKey().compareTo(currentVersion.get()) > 0))\r\n\t\t\t\t.filter(entry -> endInclusive\r\n\t\t\t\t                 ? entry.getKey().compareTo(requiredVersion.getBaseVersion()) <= 0\r\n\t\t\t\t                 : entry.getKey().compareTo(requiredVersion) < 0)\r\n\t\t\t\t.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> null, TreeMap::new));\r\n\t}\r\n\r\n\tprivate static Version getVersionFromSchemaFilename(Path v, String dbType, String component) {\r\n\t\tString filename = v.getFileName().toString();\r\n\t\tint start = (dbType + \"-\" + component + \"-\").length();\r\n\t\tint end = filename.length()-4;\r\n\t\treturn Version.of(filename.substring(start,end));\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Parameters createParameters() {\r\n\t\treturn new Parameters();\r\n\t}\r\n\r\n\t/**\r\n\t * Executes set of {@link TigaseDBTask} tasks selected based on set on passed properties\r\n\t *\r\n\t * @param params set of configuration parameters.\r\n\t */\r\n\tpublic void execute(SchemaLoader.Parameters params) {\r\n\t\tif (params instanceof Parameters) {\r\n\t\t\t// Get list of appropriate task and execute them;\r\n\t\t\tTigaseDBTask[] tasks;\r\n\t\t\tParameters p = (Parameters) params;\r\n\t\t\tif (p.getQuery() != null) {\r\n\t\t\t\ttasks = Tasks.getQueryTasks();\r\n\t\t\t} else if (p.getFile() != null) {\r\n\t\t\t\ttasks = Tasks.getSchemaTasks();\r\n\t\t\t} else {\r\n\t\t\t\ttasks = Tasks.getTasksInOrder();\r\n\t\t\t}\r\n\t\t\tfor (TigaseDBTask task : tasks) {\r\n\t\t\t\ttask.execute(this, p);\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tthrow new RuntimeException(\"Invalid parameters type!\");\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Result validateDBConnection() {\r\n\t\tconnection_ok = false;\r\n\t\tString db_conn = getDBUri(false, hasRootCredentials(params));\r\n\t\tlog.log(Level.INFO, \"Validating DBConnection, URI: \" + getDBUri(false, hasRootCredentials(params), true));\r\n\t\tif (db_conn == null) {\r\n\t\t\tlog.log(Level.WARNING, \"Missing DB connection URL\");\r\n\t\t\treturn Result.ok;\r\n\t\t} else {\r\n\t\t\ttry (Connection conn = DriverManager.getConnection(db_conn)) {\r\n\t\t\t\tlogAvailableDrivers();\r\n\t\t\t\tconn.close();\r\n\t\t\t\tconnection_ok = true;\r\n\t\t\t\tlog.log(Level.INFO, \"Connection OK\");\r\n\t\t\t\tconnectionLock = ConnectionLock.getConnectionLocker(db_conn);\r\n\t\t\t\tif (connectionLock.isEmpty() || connectionLock.get().lock()) {\r\n\t\t\t\t\treturn Result.ok;\r\n\t\t\t\t} else {\r\n\t\t\t\t\tlog.log(Level.WARNING, \"Could not obtain lock, skipping\");\r\n\t\t\t\t\treturn Result.error;\r\n\t\t\t\t}\r\n\t\t\t} catch (Throwable e) {\r\n\t\t\t\tlog.log(Level.WARNING, \"Problem validating database connection: \" + e, e);\r\n\t\t\t\treturn Result.error;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tprivate static boolean hasRootCredentials(Parameters params) {\r\n\t\treturn params.getDbRootUser() != null && params.getDbRootPass() != null;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Result shutdown() {\r\n\t\tif (connectionLock != null) {\r\n\t\t\tconnectionLock.ifPresent(ConnectionLock::unlock);\r\n\t\t\tconnectionLock.ifPresent(ConnectionLock::cleanup);\r\n\t\t\tconnectionLock = Optional.empty();\r\n\t\t}\r\n\t\treturn shutdownDerby();\r\n\t}\r\n\r\n\tpublic Result shutdownDerby() {\r\n\t\tString db_conn = getDBUri(false, true);\r\n\t\tif (\"derby\".equals(params.getDbType())) {\r\n\t\t\tlog.log(Level.INFO, \"Validating DBConnection, URI: \" + getDBUri(false, true, true));\r\n\t\t\tif (db_conn == null) {\r\n\t\t\t\tlog.log(Level.WARNING, \"Missing DB connection URL\");\r\n\t\t\t} else {\r\n\t\t\t\tdb_conn += \";shutdown=true\";\r\n\t\t\t\treturn withConnection(db_conn, conn -> {\r\n\t\t\t\t\tconnection_ok = true;\r\n\t\t\t\t\tlog.log(Level.INFO, \"Connection OK\");\r\n\t\t\t\t\treturn Result.ok;\r\n\t\t\t\t});\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn Result.ok;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Result validateDBExists() {\r\n\t\tif (!connection_ok) {\r\n\t\t\tlog.log(Level.WARNING, \"Connection not validated\");\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\r\n\t\tdb_ok = false;\r\n\t\tString db_conn1 = getDBUri(true, false);\r\n\t\tlog.log(Level.INFO, \"Validating whether DB Exists, URI: \" + getDBUri(true, false, true));\r\n\t\tif (db_conn1 == null) {\r\n\t\t\tlog.log(Level.WARNING, \"Missing DB connection URL\");\r\n\t\t\treturn Result.error;\r\n\t\t} else {\r\n\t\t\treturn withConnection(db_conn1, conn -> {\r\n\t\t\t\tdb_ok = true;\r\n\t\t\t\tlog.log(Level.INFO, \"Exists OK\");\r\n\t\t\t\treturn Result.ok;\r\n\t\t\t}, e -> {\r\n\t\t\t\tif (!hasRootCredentials(params)) {\r\n\t\t\t\t\tlog.log(Level.WARNING, \"Database does not exist!\");\r\n\t\t\t\t\treturn Result.error;\r\n\t\t\t\t}\r\n\t\t\t\treturn withConnection(getDBUri(false, true), conn -> {\r\n\t\t\t\t\tResult result = Result.ok;\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tArrayList<String> queries = loadSQLQueries(\r\n\t\t\t\t\t\t\t\t\"database/\" + params.getDbType() + \"-installer-create-db.sql\");\r\n\t\t\t\t\t\tfor (String query : queries) {\r\n\t\t\t\t\t\t\tlog.log(Level.FINE, \"Executing query: \" + query);\r\n\t\t\t\t\t\t\tif (!query.isEmpty()) {\r\n\t\t\t\t\t\t\t\t// Some queries may fail and this is still fine\r\n\t\t\t\t\t\t\t\t// the user or the database may already exist\r\n\t\t\t\t\t\t\t\ttry (Statement stmt = conn.createStatement()) {\r\n\t\t\t\t\t\t\t\t\tstmt.execute(query);\r\n\t\t\t\t\t\t\t\t\tstmt.close();\r\n\t\t\t\t\t\t\t\t} catch (SQLException ex) {\r\n\t\t\t\t\t\t\t\t\tresult = Result.warning;\r\n\t\t\t\t\t\t\t\t\tlog.log(Level.WARNING, \"Query failed: \" + ex.getMessage());\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tlog.log(Level.INFO, \" OK\");\r\n\t\t\t\t\t\tdb_ok = true;\r\n\t\t\t\t\t} catch (IOException ex) {\r\n\t\t\t\t\t\tlog.log(Level.WARNING, ex.getMessage());\r\n\t\t\t\t\t\tresult = Result.error;\r\n\t\t\t\t\t}\r\n\t\t\t\t\treturn result;\r\n\t\t\t\t});\r\n\t\t\t});\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Method performs post-installation action using using {@code *-installer-post.sql} schema file substituting it's\r\n\t * variables with ones provided.\r\n\t */\r\n\t@Override\r\n\tpublic Result postInstallation() {\r\n\t\t// part 1, check db preconditions\r\n\t\tif (!connection_ok) {\r\n\t\t\tlog.log(Level.WARNING, \"Connection not validated\");\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\t\tif (!db_ok) {\r\n\t\t\tlog.log(Level.WARNING, \"Database not validated\");\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\r\n\t\tif (!schema_ok) {\r\n\t\t\tlog.log(Level.WARNING, \"Database schema is invalid\");\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\r\n\t\t// part 2, acquire reqired fields and validate them\r\n\t\tif (!hasRootCredentials(params)) {\r\n\t\t\tlog.log(Level.WARNING, \"No database root user credentials, skipping post database creation scripts.\");\r\n\t\t\treturn Result.skipped;\r\n\t\t}\r\n\t\tString db_conn = getDBUri(true, true);\r\n\t\tlog.log(Level.INFO, \"Post Installation, URI: \" + getDBUri(true, true,true));\r\n\t\treturn withStatement(db_conn, stmt -> {\r\n\t\t\tlog.log(Level.INFO, \"Finalizing...\");\r\n\t\t\tArrayList<String> queries = loadSQLQueries(\"database/\" + params.getDbType() + \"-installer-post.sql\");\r\n\t\t\tfor (String query : queries) {\r\n\t\t\t\tif (!query.isEmpty()) {\r\n\t\t\t\t\tlog.log(Level.FINEST, \"Executing query: \" + query);\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tstmt.execute(query);\r\n\t\t\t\t\t} catch (SQLException ex) {\r\n\t\t\t\t\t\tlog.log(Level.SEVERE, \"Failed to execute query: \" + query);\r\n\t\t\t\t\t\tthrow ex;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tlog.log(Level.INFO, \" completed OK\");\r\n\t\t\treturn Result.ok;\r\n\t\t});\r\n\t}\r\n\r\n\t/**\r\n\t * Method performs post-installation action using using {@code *-installer-post.sql} schema file substituting it's\r\n\t * variables with ones provided.\r\n\t */\r\n\t@Override\r\n\tpublic Result printInfo() {\r\n\t\t// part 1, check db preconditions\r\n\t\tif (!connection_ok) {\r\n\t\t\tlog.log(Level.WARNING, \"Connection not validated\");\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\t\tif (!db_ok) {\r\n\t\t\tlog.log(Level.WARNING, \"Database not validated\");\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\r\n\t\tif (!schema_ok) {\r\n\t\t\tlog.log(Level.WARNING, \"Database schema is invalid\");\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\r\n\t\treturn super.printInfo();\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Result addXmppAdminAccount(SchemaManager.SchemaInfo schemaInfo) {\r\n\t\t// part 1, check db preconditions\r\n\t\tif (!connection_ok) {\r\n\t\t\tlog.log(Level.WARNING, \"Connection not validated\");\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\t\tif (!db_ok) {\r\n\t\t\tlog.log(Level.WARNING, \"Database not validated\");\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\r\n\t\tif (!schema_ok) {\r\n\t\t\tlog.log(Level.WARNING, \"Database schema is invalid\");\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\r\n\t\t// part 2, acquire required fields and validate them\r\n\t\tList<BareJID> jids = params.getAdmins();\r\n\t\tif (jids.size() < 1) {\r\n\t\t\tlog.log(Level.WARNING, \"Error: No admin users entered\");\r\n\t\t\treturn Result.warning;\r\n\t\t}\r\n\r\n\t\tString pwd = params.getAdminPassword();\r\n\t\tif (pwd == null) {\r\n\t\t\tlog.log(Level.WARNING, \"Error: No admin password entered\");\r\n\t\t\treturn Result.warning;\r\n\t\t}\r\n\r\n\t\tString dbUri = getDBUri();\r\n\t\tlog.log(Level.INFO, \"Adding XMPP Admin Account, URI: \" + getDBUri(true));\r\n\r\n\t\ttry {\r\n\t\t\tDataRepository dataSource = new DataRepositoryImpl();\r\n\t\t\tdataSource.initialize(dbUri);\r\n\r\n\t\t\tResult result = addUsersToRepository(schemaInfo, dataSource, DataRepository.class, jids, pwd, log);\r\n\t\t\tif (result == Result.ok) {\r\n\t\t\t\tlog.log(Level.INFO, \"All users added\");\r\n\t\t\t}\r\n\t\t\treturn result;\r\n\t\t} catch (RepositoryException e) {\r\n\t\t\tlog.log(Level.WARNING, \"Error initializing DB\" + e);\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\t}\r\n\t\r\n\t@Override\r\n\tpublic Result addExternalServices(SchemaManager.SchemaInfo schemaInfo) {\r\n\t\t// part 1, check db preconditions\r\n\t\tif (!connection_ok) {\r\n\t\t\tlog.log(Level.WARNING, \"Connection not validated\");\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\t\tif (!db_ok) {\r\n\t\t\tlog.log(Level.WARNING, \"Database not validated\");\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\r\n\t\tif (!schema_ok) {\r\n\t\t\tlog.log(Level.WARNING, \"Database schema is invalid\");\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\r\n\t\t// part 2, acquire required fields and validate them\r\n\t\tList<ExtServiceDiscoItem> services = params.getExternalServices();\r\n\t\tif (services.size() < 1) {\r\n\t\t\tlog.log(Level.WARNING, \"Error: No external services entered\");\r\n\t\t\treturn Result.skipped;\r\n\t\t}\r\n\r\n\t\tString dbUri = getDBUri();\r\n\t\tlog.log(Level.INFO, \"Adding External Service Discovery Items, URI: \" + getDBUri(true));\r\n\r\n\t\ttry {\r\n\t\t\tDataRepository dataSource = new DataRepositoryImpl();\r\n\t\t\tdataSource.initialize(dbUri);\r\n\r\n\t\t\tResult result = addComponentRepositoryItems(schemaInfo, dataSource, DataRepository.class, ExtServiceDiscoveryUserRepoRepository.class, log, services);\r\n\r\n\t\t\tif (result == Result.ok) {\r\n\t\t\t\tlog.log(Level.INFO, \"All External Service Discovery Items added\");\r\n\t\t\t}\r\n\t\t\treturn result;\r\n\t\t} catch (RepositoryException e) {\r\n\t\t\tlog.log(Level.WARNING, \"Error initializing DB\" + e);\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Result setComponentVersion(String component, String version) {\r\n\t\t// part 1, check db preconditions\r\n\t\tif (!connection_ok) {\r\n\t\t\tlog.log(Level.INFO, \"Connection not validated\");\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\r\n\t\tif (component == null) {\r\n\t\t\tlog.log(Level.WARNING, \"Invalid component\");\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\r\n\t\tif (version == null) {\r\n\t\t\tlog.log(Level.WARNING, \"Invalid version\");\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\r\n\t\tString db_conn = getDBUri();\r\n\t\tlog.log(Level.INFO, \"Setting version of the component: {0} to: {1} for connection: {2}\",\r\n\t\t\t\tnew Object[]{component, version, getDBUri(true)});\r\n\r\n\t\tif (db_conn == null) {\r\n\t\t\tlog.log(Level.WARNING, \"Missing DB connection URL\");\r\n\t\t\treturn Result.error;\r\n\t\t} else {\r\n\t\t\treturn withConnection(db_conn, cmd -> {\r\n\t\t\t\tString procedure = \"{ call TigSetComponentVersion(?,?) }\";\r\n\t\t\t\ttry (PreparedStatement ps = cmd.prepareCall(procedure)) {\r\n\t\t\t\t\tps.setString(1, component);\r\n\t\t\t\t\tps.setString(2, version);\r\n\t\t\t\t\tps.executeUpdate();\r\n\t\t\t\t\tps.close();\r\n\t\t\t\t\treturn Result.ok;\r\n\t\t\t\t} catch (SQLException ex) {\r\n\t\t\t\t\tlog.log(Level.WARNING, \"Setting version failed: \" + procedure + \", \" + ex.getMessage());\r\n\t\t\t\t\treturn Result.warning;\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Optional<Version> getComponentVersionFromDb(String component) {\r\n\t\tif (component == null || component.trim().isEmpty()) {\r\n\t\t\tlog.log(Level.WARNING, \"Missing DB connection URL\");\r\n\t\t\tthrow new IllegalArgumentException(\"Wrong component name\");\r\n\t\t} else {\r\n\t\t\tString db_conn = getDBUri(true, false);\r\n\t\t\tfinal SQLCommand<Connection, Version> versionCommand = cmd -> {\r\n\r\n\t\t\t\ttry (PreparedStatement ps = cmd.prepareCall(JDBC_SCHEMA_VERSION_QUERY)) {\r\n\t\t\t\t\tps.setString(1, component);\r\n\t\t\t\t\tfinal ResultSet rs = ps.executeQuery();\r\n\t\t\t\t\tif (rs.next()) {\r\n\t\t\t\t\t\tfinal String versionString = rs.getString(1);\r\n\t\t\t\t\t\tif (versionString != null) {\r\n\t\t\t\t\t\t\treturn Version.of(versionString);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\tps.close();\r\n\t\t\t\t} catch (SQLException ex) {\r\n\t\t\t\t\tlog.log(Level.WARNING,\r\n\t\t\t\t\t\t\t\"Getting version failed: \" + JDBC_SCHEMA_VERSION_QUERY + \", \" + ex.getMessage());\r\n\t\t\t\t}\r\n\t\t\t\treturn null;\r\n\t\t\t};\r\n\r\n\t\t\treturn withConnectionGeneric(db_conn, versionCommand, null);\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Optional<Version> getMinimalRequiredComponentVersionForUpgrade(SchemaManager.SchemaInfo schema) {\r\n\t\tif (params.isForceReloadSchema()) {\r\n\t\t\treturn Optional.of(Version.ZERO);\r\n\t\t}\r\n\t\tMap<Version, Path> versions = getSchemaFileNames(schema.getId());\r\n\t\tif (versions.size() > 1) {\r\n\t\t\treturn versions.keySet().stream().sorted().findFirst();\r\n\t\t} else {\r\n\t\t\treturn Optional.ofNullable(Version.ZERO);\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Result loadSchema(SchemaManager.SchemaInfo schema, String version) {\r\n\r\n\t\tlog.log(Level.CONFIG, \"SchemaInfo:: id: {0}, repositories: {1}; version: {2}\",\r\n\t\t        new Object[]{schema.getId(), schema.getRepositories().size(), version});\r\n\r\n\t\tif (!connection_ok) {\r\n\t\t\tlog.log(Level.CONFIG, \"Connection not validated\");\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\r\n\t\tResult result;\r\n\t\tOptional<Version> currentVersion = getComponentVersionFromDb(schema.getId());\r\n\t\tVersion requiredVersion = Version.of(version);\r\n\r\n\t\tif (schema.isExternal()) {\r\n\t\t\tresult = loadSchemaFromSQLFiles(schema, currentVersion, requiredVersion);\r\n\t\t} else {\r\n\t\t\tresult = loadSchemaFromClass(schema, currentVersion, requiredVersion);\r\n\t\t}\r\n\r\n\t\t// particular repository is version aware therefore we store version:\r\n\t\t// * if no version was yet stored and the update was skipped (because no logic\r\n\t\t//   was yet defined to perform any update\r\n\t\t// * version was set and the update was correctly performed\r\n\t\tif (!currentVersion.isPresent() && Result.skipped.equals(result) || Result.ok.equals(result)) {\r\n\t\t\tVersion build = new Version.Builder(requiredVersion).setCommit(null).build();\r\n\t\t\tsetComponentVersion(schema.getId(), build.toString() );\r\n\t\t}\r\n\t\tif (Result.skipped.equals(result)) {\r\n\t\t\tlog.log(Level.INFO, \"Required schema: {0} is already loaded in correct version: {1}\",\r\n\t\t\t\t\tnew String[] {schema.getId(), version});\r\n\t\t}\r\n\r\n\t\t// logic to check if the schema version was correctly loaded\r\n\r\n\t\tOptional<String> passwordEncoding = schema.getRepositories()\r\n\t\t\t\t.stream()\r\n\t\t\t\t.filter(info -> AuthRepository.class.isAssignableFrom(info.getImplementation()))\r\n\t\t\t\t.map(this::getDataSourcePasswordEncoding)\r\n\t\t\t\t.filter(Objects::nonNull)\r\n\t\t\t\t.findFirst();\r\n\r\n\t\tpasswordEncoding.ifPresent(encoding -> {\r\n\t\t\tlog.log(Level.WARNING, \"You have 'password-encoding' property set to \" + encoding + \".\"\r\n\t\t\t        + \"\\n\" + \"This setting will no longer work out of the box with this version of Tigase XMPP Server.\"\r\n\t\t\t\t\t+ \"\\n\" + \"Please check Tigase XMPP Server Administration Guide, section \\\"Changes to Schema in v8.0.0\\\"\" +\r\n\t\t\t\t\t\" at http://docs.tigase.org/ for more details.\");\r\n\t\t});\r\n\r\n\t\treturn passwordEncoding.isPresent()\r\n\t\t\t   ? (result == Result.ok ? Result.warning : result)\r\n\t\t\t   : result;\r\n\t}\r\n\t\r\n\tprivate Result loadSchemaFromClass(SchemaManager.SchemaInfo schema, Optional<Version> currentVersion,\r\n\t                                   Version requiredVersion) {\r\n\t\tResult result;\r\n\t\ttry {\r\n\t\t\tString dbUri = getDBUri();\r\n\t\t\tlog.log(Level.CONFIG, \"Loading schema {0}, version: {1} into repo: {2}\",\r\n\t\t\t        new Object[]{schema.getId(), requiredVersion, getDBUri(true)});\r\n\t\t\tDataRepository dataSource = new DataRepositoryImpl();\r\n\t\t\tdataSource.initialize(dbUri);\r\n\r\n\t\t\tfinal Set<Result> collect = getInitializedDataSourceAwareForSchemaInfo(schema, DataSource.class,\r\n\t\t\t                                                                       dataSource, log).filter(\r\n\t\t\t\t\tclazz -> RepositoryVersionAware.class.isAssignableFrom(clazz.getClass()))\r\n\t\t\t\t\t.map(RepositoryVersionAware.class::cast)\r\n\t\t\t\t\t.map(updateSchemaFunction(currentVersion, requiredVersion))\r\n\t\t\t\t\t.collect(Collectors.toSet());\r\n\t\t\tresult = parseResultsSet(collect);\r\n\r\n\t\t} catch (RepositoryException e) {\r\n\t\t\tlog.log(Level.WARNING, e.getMessage());\r\n\t\t\tresult = Result.warning;\r\n\t\t}\r\n\t\treturn result;\r\n\t}\r\n\r\n\tprivate Result loadSchemaFromSQLFiles(SchemaManager.SchemaInfo schema, Optional<Version> currentVersion,\r\n\t                                      Version requiredVersion) {\r\n\t\tResult result;\r\n\t\tlog.log(Level.CONFIG, \"Loading schema {0}, version: {1} from files, current: {2}\",\r\n\t\t        new Object[]{schema.getId(), requiredVersion, currentVersion.orElse(Version.ZERO)});\r\n\r\n\t\tMap<Version, Path> schemaFileNames = getSchemaFileNames(schema.getId());\r\n\r\n\t\tif (!schemaFileNames.containsKey(requiredVersion.getBaseVersion())) {\r\n\t\t\tlog.log(Level.WARNING,\r\n\t\t\t\t\t\"Schema file for required version: {0} is missing in the list of available schema files: {1}\",\r\n\t\t\t\t\tnew Object[]{requiredVersion.getBaseVersion(), schemaFileNames.keySet()});\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\r\n\t\tif (!params.isForceReloadSchema()) {\r\n\t\t\tschemaFileNames = getSchemaFileNamesInRange(schemaFileNames, currentVersion, requiredVersion);\r\n\t\t}\r\n\r\n\t\tif (schemaFileNames.isEmpty()) {\r\n\t\t\tlog.log(Level.FINEST, \"Empty schema list\");\r\n\t\t\tresult = Result.skipped;\r\n\t\t} else {\r\n\t\t\tlog.log(Level.INFO, \"Schema files to load: {0}\", new Object[]{schemaFileNames});\r\n\t\t\tfinal Set<Result> collect = schemaFileNames.entrySet().stream()\r\n\t\t\t\t\t.map(file -> loadSchemaFile(file.getValue().toString()))\r\n\t\t\t\t\t.collect(Collectors.toSet());\r\n\r\n\t\t\tresult = parseResultsSet(collect);\r\n\t\t}\r\n\t\treturn result;\r\n\t}\r\n\r\n\t/**\r\n\t * Method parses provided collection and based on the collection items returns\r\n\t * single result. If there is single item then it will be return, otherwise\r\n\t * collection will be checked and if either {@code Result.error} or {@code Result.warning}\r\n\t * is present then it will be return. Otherwise {@code Result.ok} will be returned\r\n\t *\r\n\t * @param collect Collection of the {@link tigase.db.util.SchemaLoader.Result} to be processed\r\n\t *\r\n\t * @return single {@link tigase.db.util.SchemaLoader.Result} appropriate for the whole collection\r\n\t */\r\n\tprivate Result parseResultsSet(Set<Result> collect) {\r\n\t\tResult result;\r\n\t\tif (collect.size() == 1) {\r\n\t\t\tresult = collect.iterator().next();\r\n\t\t} else {\r\n\t\t\tresult = collect.contains(Result.error)\r\n\t\t\t         ? Result.error\r\n\t\t\t         : (collect.contains(Result.warning) ? Result.warning : Result.ok);\r\n\t\t}\r\n\t\treturn result;\r\n\t}\r\n\r\n\tprivate Function<RepositoryVersionAware, Result> updateSchemaFunction(Optional<Version> currentVersion,\r\n\t                                                                      Version requiredVersion) {\r\n\t\treturn versionAware -> {\r\n\t\t\ttry {\r\n\t\t\t\treturn versionAware.updateSchema(currentVersion, requiredVersion);\r\n\t\t\t} catch (Exception e) {\r\n\t\t\t\tlog.log(Level.WARNING, e.getMessage());\r\n\t\t\t\treturn Result.warning;\r\n\t\t\t}\r\n\t\t};\r\n\t}\r\n\r\n\tprivate String getDataSourcePasswordEncoding(SchemaManager.RepoInfo repoInfo) {\r\n\t\tAtomicReference<String> passwordEncoding = new AtomicReference<>();\r\n\t\twithStatement(repoInfo.getDataSource().getResourceUri(), stmt -> {\r\n\r\n\t\t\tString query = null;\r\n\r\n\t\t\tfinal dbTypes dbTypes = DataRepositoryImpl.parseDatabaseType(\r\n\t\t\t\t\trepoInfo.getDataSource().getResourceUri());\r\n\r\n\t\t\tswitch (dbTypes) {\r\n\t\t\t\tcase derby:\r\n\t\t\t\t\tquery = \"values TigGetDBProperty('password-encoding')\";\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase jtds:\r\n\t\t\t\tcase sqlserver:\r\n\t\t\t\t\tquery = \"select dbo.TigGetDBProperty('password-encoding')\";\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tdefault:\r\n\t\t\t\t\tquery = \"select TigGetDBProperty('password-encoding')\";\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tResultSet rs = stmt.executeQuery(query);\r\n\t\t\tif (rs.next()) {\r\n\t\t\t\tpasswordEncoding.set(rs.getString(1));\r\n\t\t\t}\r\n\t\t\trs.close();\r\n\t\t\treturn Result.ok;\r\n\t\t});\r\n\r\n\t\treturn passwordEncoding.get();\r\n\t}\r\n\r\n\tprivate Result loadSchema(String schemaId, String version) {\r\n\r\n\t\treturn Result.error;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Result loadSchemaFile(String fileName) {\r\n\t\t// part 1, check db preconditions\r\n\t\tif (!connection_ok) {\r\n\t\t\tlog.log(Level.INFO, \"Connection not validated\");\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\t\tif (!db_ok) {\r\n\t\t\tlog.log(Level.INFO, \"Database not validated\");\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\r\n\t\tif (fileName == null) {\r\n\t\t\tlog.log(Level.WARNING, \"Error: empty query\");\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\r\n\t\tString db_conn = getDBUri();\r\n\t\tlog.log(Level.INFO, String.format(\"Loading schema from file(s): %1$s, URI: %2$s\", fileName, getDBUri(true)));\r\n\r\n\t\treturn withStatement(db_conn, stmt -> {\r\n\t\t\tArrayList<String> queries = new ArrayList<>(loadSQLQueries(\"database/\" + fileName));\r\n\t\t\tfor (String query : queries) {\r\n\t\t\t\tif (!query.isEmpty()) {\r\n\t\t\t\t\tlog.log(Level.FINEST, \"Executing query: \" + query);\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tstmt.execute(query);\r\n\t\t\t\t\t} catch (SQLException ex) {\r\n\t\t\t\t\t\tif (\"derby\".equals(params.getDbType())) {\r\n\t\t\t\t\t\t\tString lowerQuery = query.toLowerCase().trim();\r\n\t\t\t\t\t\t\tif ((\"X0Y32\".equals(ex.getSQLState()) || \"X0Y68\".equals(ex.getSQLState())) &&\r\n\t\t\t\t\t\t\t\t\t(lowerQuery.startsWith(\"create \") || lowerQuery.startsWith(\"alter \"))) {\r\n\t\t\t\t\t\t\t\t// if object already exists then we should not consider creation failure as an error for DerbyDB\r\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Object already exists!\");\r\n\t\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\tif (\"42Y55\".equals(ex.getSQLState()) && lowerQuery.startsWith(\"drop \")) {\r\n\t\t\t\t\t\t\t\t// if object does not exists then wen should not consider drop failure as an error for DerbyDB\r\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Object already dropped!\");\r\n\t\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tlog.log(Level.SEVERE, \"Failed to execute query: \" + query);\r\n\t\t\t\t\t\tthrow ex;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tschema_ok = true;\r\n\t\t\tlog.log(Level.INFO, \" completed OK\");\r\n\t\t\treturn Result.ok;\r\n\t\t});\r\n\t}\r\n\r\n\tpublic Result destroyDataSource() {\r\n\t\tif (!connection_ok) {\r\n\t\t\tlog.log(Level.INFO, \"Connection not validated\");\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\r\n\t\tif (\"derby\".equals(params.getDbType())) {\r\n\t\t\tshutdown();\r\n\t\t\tPath path = Paths.get(params.getDbName());\r\n\t\t\ttry {\r\n\t\t\t\tFiles.walkFileTree(path, new SimpleFileVisitor<Path>() {\r\n\t\t\t\t\t@Override\r\n\t\t\t\t\tpublic FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {\r\n\t\t\t\t\t\tFiles.delete(file);\r\n\t\t\t\t\t\treturn FileVisitResult.CONTINUE;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t@Override\r\n\t\t\t\t\tpublic FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {\r\n\t\t\t\t\t\tFiles.delete(dir);\r\n\t\t\t\t\t\treturn FileVisitResult.CONTINUE;\r\n\t\t\t\t\t}\r\n\t\t\t\t});\r\n\t\t\t\treturn Result.ok;\r\n\t\t\t} catch (IOException ex) {\r\n\t\t\t\tlog.log(Level.SEVERE, \"Failed to remove path \" + path.toString(), ex);\r\n\t\t\t\treturn Result.error;\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tif (!hasRootCredentials(params)) {\r\n\t\t\t\tlog.log(Level.WARNING, \"Missing root database user credentials!\");\r\n\t\t\t\treturn Result.error;\r\n\t\t\t}\r\n\t\t\tString db_conn = getDBUri(false, true);\r\n\t\t\tlog.log(Level.INFO, \"Dropping database, URI: \" + getDBUri(false, true, true));\r\n\t\t\treturn withStatement(db_conn, stmt -> {\r\n\t\t\t\tString query = \"drop database \" + params.getDbName();\r\n\t\t\t\tlog.log(Level.FINEST, \"Executing query: \" + query);\r\n\t\t\t\ttry {\r\n\t\t\t\t\tstmt.execute(query);\r\n\t\t\t\t\tstmt.close();\r\n\t\t\t\t\treturn Result.ok;\r\n\t\t\t\t} catch (SQLException ex) {\r\n\t\t\t\t\tlog.log(Level.WARNING, \"Query failed: \" + query + \", \" + ex.getMessage());\r\n\t\t\t\t\treturn Result.warning;\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t}\r\n\t}\r\n\r\n\tpublic String getDBUri() {\r\n\t\treturn getDBUri(false);\r\n\t}\r\n\r\n\tpublic String getDBUri(boolean debug) {\r\n\t\treturn getDBUri(true, false, debug);\r\n\t}\r\n\r\n\tpublic List<CommandlineParameter> getCommandlineParameters() {\r\n\t\tList<CommandlineParameter> options = new ArrayList<>();\r\n\r\n\t\toptions.add(new CommandlineParameter.Builder(\"T\", PARAMETERS_ENUM.DATABASE_TYPE.getName()).description(\r\n\t\t\t\t\"Database server type\")\r\n\t\t\t\t\t\t\t.defaultValue(PARAMETERS_ENUM.DATABASE_TYPE.getDefaultValue())\r\n\t\t\t\t\t\t\t.required(true)\r\n\t\t\t\t\t\t\t.build());\r\n\t\toptions.addAll(getSetupOptions());\r\n\r\n\t\toptions.add(new CommandlineParameter.Builder(\"F\", PARAMETERS_ENUM.FILE.getName()).description(\r\n\t\t\t\t\"Comma separated list of SQL files that will be processed\").build());\r\n\t\toptions.add(new CommandlineParameter.Builder(\"Q\", PARAMETERS_ENUM.QUERY.getName()).description(\r\n\t\t\t\t\"Custom query to be executed\").build());\r\n\t\toptions.add(new CommandlineParameter.Builder(\"J\", PARAMETERS_ENUM.ADMIN_JID.getName()).description(\r\n\t\t\t\t\"Comma separated list of administrator JID(s)\").build());\r\n\t\toptions.add(new CommandlineParameter.Builder(\"N\", PARAMETERS_ENUM.ADMIN_JID_PASS.getName()).description(\r\n\t\t\t\t\"Password that will be used for the entered JID(s) - one for all configured administrators\")\r\n\t\t\t\t\t\t\t.secret()\r\n\t\t\t\t\t\t\t.build());\r\n\t\toptions.add(new CommandlineParameter.Builder(null, PARAMETERS_ENUM.GET_URI.getName()).description(\r\n\t\t\t\t\"Generate database URI\")\r\n\t\t\t\t\t\t\t.requireArguments(false)\r\n\t\t\t\t\t\t\t.defaultValue(PARAMETERS_ENUM.GET_URI.getDefaultValue())\r\n\t\t\t\t\t\t\t.build());\r\n\t\toptions.add(new CommandlineParameter.Builder(null, PARAMETERS_ENUM.IGNORE_MISSING_FILES.getName()).description(\r\n\t\t\t\t\"Force ignoring missing files errors\")\r\n\t\t\t\t\t\t\t.defaultValue(PARAMETERS_ENUM.IGNORE_MISSING_FILES.getDefaultValue())\r\n\t\t\t\t\t\t\t.build());\r\n\t\treturn options;\r\n\t}\r\n\r\n\tpublic List<CommandlineParameter> getSetupOptions() {\r\n\t\tList<CommandlineParameter> options = new ArrayList<>();\r\n\t\toptions.add(new CommandlineParameter.Builder(\"D\", PARAMETERS_ENUM.DATABASE_NAME.getName()).description(\r\n\t\t\t\t\"Name of the database that will be created and to which schema will be loaded\")\r\n\t\t\t\t\t\t\t.defaultValue(PARAMETERS_ENUM.DATABASE_NAME.getDefaultValue())\r\n\t\t\t\t\t\t\t.required(true)\r\n\t\t\t\t\t\t\t.build());\r\n\t\toptions.add(new CommandlineParameter.Builder(\"H\", PARAMETERS_ENUM.DATABASE_HOSTNAME.getName()).description(\r\n\t\t\t\t\"Address of the database instance\")\r\n\t\t\t\t\t\t\t.defaultValue(PARAMETERS_ENUM.DATABASE_HOSTNAME.getDefaultValue())\r\n\t\t\t\t\t\t\t.required(true)\r\n\t\t\t\t\t\t\t.build());\r\n\t\toptions.add(new CommandlineParameter.Builder(\"U\", PARAMETERS_ENUM.TIGASE_USERNAME.getName()).description(\r\n\t\t\t\t\"Name of the user that will be created specifically to access Tigase XMPP Server\")\r\n\t\t\t\t\t\t\t.defaultValue(PARAMETERS_ENUM.TIGASE_USERNAME.getDefaultValue())\r\n\t\t\t\t\t\t\t.required(true)\r\n\t\t\t\t\t\t\t.build());\r\n\t\toptions.add(new CommandlineParameter.Builder(\"P\", PARAMETERS_ENUM.TIGASE_PASSWORD.getName()).description(\r\n\t\t\t\t\"Password of the user that will be created specifically to access Tigase XMPP Server\")\r\n\t\t\t\t\t\t\t.defaultValue(PARAMETERS_ENUM.TIGASE_PASSWORD.getDefaultValue())\r\n\t\t\t\t\t\t\t.required(true)\r\n\t\t\t\t\t\t\t.secret()\r\n\t\t\t\t\t\t\t.build());\r\n\t\toptions.add(new CommandlineParameter.Builder(null, PARAMETERS_ENUM.ROOT_ASK.getName()).description(\r\n\t\t\t\t\t\t\"Ask for database root user credentials\")\r\n\t\t\t\t\t\t\t.defaultValue(PARAMETERS_ENUM.ROOT_ASK.getDefaultValue())\r\n\t\t\t\t\t\t\t.type(Boolean.class)\r\n\t\t\t\t\t\t\t.build());\r\n\t\toptions.add(new CommandlineParameter.Builder(\"R\", PARAMETERS_ENUM.ROOT_USERNAME.getName()).description(\r\n\t\t\t\t\"Database root account username used to create tigase user and database\")\r\n\t\t\t\t\t\t\t.defaultValue(PARAMETERS_ENUM.ROOT_USERNAME.getDefaultValue())\r\n\t\t\t\t\t\t\t.required(false)\r\n\t\t\t\t\t\t\t.build());\r\n\t\toptions.add(new CommandlineParameter.Builder(\"A\", PARAMETERS_ENUM.ROOT_PASSWORD.getName()).description(\r\n\t\t\t\t\"Database root account password used to create tigase user and database\")\r\n\t\t\t\t\t\t\t.defaultValue(PARAMETERS_ENUM.ROOT_PASSWORD.getDefaultValue())\r\n\t\t\t\t\t\t\t.secret()\r\n\t\t\t\t\t\t\t.required(false)\r\n\t\t\t\t\t\t\t.build());\r\n\t\toptions.add(new CommandlineParameter.Builder(\"S\", PARAMETERS_ENUM.USE_SSL.getName()).description(\r\n\t\t\t\t\"Enable SSL support for database connection (if database supports it)\")\r\n\t\t\t\t\t\t\t.requireArguments(false)\r\n\t\t\t\t\t\t\t.defaultValue(PARAMETERS_ENUM.USE_SSL.getDefaultValue())\r\n\t\t\t\t\t\t\t.type(Boolean.class)\r\n\t\t\t\t\t\t\t.build());\r\n\t\tswitch (this.getType()) {\r\n\t\t\tcase \"mysql\":\r\n\t\t\t\toptions.add(new CommandlineParameter.Builder(null,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t PARAMETERS_ENUM.USE_LEGACY_DATETIME_CODE.getName()).description(\r\n\t\t\t\t\t\t\"Use legacy datetime code for JDBC connection\")\r\n\t\t\t\t\t\t\t\t\t.requireArguments(true)\r\n\t\t\t\t\t\t\t\t\t.defaultValue(PARAMETERS_ENUM.USE_LEGACY_DATETIME_CODE.getDefaultValue())\r\n\t\t\t\t\t\t\t\t\t.type(Boolean.class)\r\n\t\t\t\t\t\t\t\t\t.build());\r\n\t\t\t\toptions.add(\r\n\t\t\t\t\t\tnew CommandlineParameter.Builder(null, PARAMETERS_ENUM.SERVER_TIMEZONE.getName()).description(\r\n\t\t\t\t\t\t\t\t\"Time zone of database server\")\r\n\t\t\t\t\t\t\t\t.defaultValue(PARAMETERS_ENUM.SERVER_TIMEZONE.getDefaultValue())\r\n\t\t\t\t\t\t\t\t.requireArguments(true)\r\n\t\t\t\t\t\t\t\t.build());\r\n\t\t\t\tbreak;\r\n\t\t\tdefault:\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t\treturn options;\r\n\t}\r\n\r\n\t/**\r\n\t * Method checks whether the connection to the database is possible and that database of specified name exists. If\r\n\t * yes then a single query is executed.\r\n\t *\r\n\t * @param query to execute\r\n\t */\r\n\tprotected Result executeSingleQuery(String query) {\r\n\t\t// part 1, check db preconditions\r\n\t\tif (!connection_ok) {\r\n\t\t\tlog.log(Level.INFO, \"Connection not validated\");\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\r\n\t\tif (query == null) {\r\n\t\t\tlog.log(Level.WARNING, \"Error: empty query\");\r\n\t\t\treturn Result.error;\r\n\t\t}\r\n\r\n\t\tString db_conn = getDBUri();\r\n\t\tlog.log(Level.INFO, \"Executing Simple Query, URI: \" + getDBUri(true));\r\n\t\tif (db_conn == null) {\r\n\t\t\tlog.log(Level.WARNING, \"Missing DB connection URL\");\r\n\t\t\treturn Result.error;\r\n\t\t} else {\r\n\t\t\treturn withStatement(db_conn, stmt -> {\r\n\t\t\t\tlog.log(Level.FINEST, \"Executing query: \" + query);\r\n\t\t\t\tif (!query.isEmpty()) {\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tstmt.execute(query);\r\n\t\t\t\t\t\tstmt.close();\r\n\t\t\t\t\t} catch (SQLException ex) {\r\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Query failed: \" + query + \", \" + ex.getMessage());\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\treturn Result.ok;\r\n\t\t\t});\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Performs actual read and parsing of queries from resource file. It skips comments and require proper syntax of\r\n\t * the file, i.e. each query enclosed in {@code \"-- QUERY START:\"} and {@code \"-- QUERY END:\"}. If the file\r\n\t * references/sources/import another schema file then it will also be read and parsed.\r\n\t *\r\n\t * @param resource name of the resource for which an {@link InputStream} should be created.\r\n\t */\r\n\tprivate ArrayList<String> loadSQLQueries(String resource) throws IOException {\r\n\t\tlog.log(Level.FINER, \"Loading queries, resource: {0}\", new Object[]{resource});\r\n\t\tArrayList<String> results = new ArrayList<>();\r\n\t\tPath p = Paths.get(resource);\r\n\r\n\t\tif (!Files.exists(p)) {\r\n\t\t\tPath srcPath = Paths.get(\"src/main/\" + resource);\r\n\t\t\tif (!Files.exists(srcPath)) {\r\n\t\t\t\tif (params.isIgnoreMissingFiles()) {\r\n\t\t\t\t\tlog.log(Level.WARNING, \"Provided path: {0} doesn''t exist, skipping!\", new Object[]{p.toString()});\r\n\t\t\t\t\treturn results;\r\n\t\t\t\t} else {\r\n\t\t\t\t\tthrow new IOException(\"Required file at \" + p.toString() + \" doesn't exist!\");\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\tp = srcPath;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tString sql_query = \"\";\r\n\t\tSQL_LOAD_STATE state = SQL_LOAD_STATE.INIT;\r\n\t\tfor (String line : Files.readAllLines(p)) {\r\n\t\t\tswitch (state) {\r\n\t\t\t\tcase INIT:\r\n\t\t\t\t\tif (line.startsWith(\"-- QUERY START:\")) {\r\n\t\t\t\t\t\tsql_query = \"\";\r\n\t\t\t\t\t\tstate = SQL_LOAD_STATE.IN_SQL;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (line.startsWith(\"-- LOAD FILE:\") && line.trim().contains(\"sql\")) {\r\n\t\t\t\t\t\tMatcher matcher = Pattern.compile(\"-- LOAD FILE:\\\\s*(.*\\\\.sql)\").matcher(line);\r\n\t\t\t\t\t\tif (matcher.find()) {\r\n\t\t\t\t\t\t\tresults.addAll(loadSQLQueries(matcher.group(1)));\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase IN_SQL:\r\n\t\t\t\t\tif (line.startsWith(\"-- QUERY END:\")) {\r\n\t\t\t\t\t\tstate = SQL_LOAD_STATE.INIT;\r\n\t\t\t\t\t\tsql_query = sql_query.trim();\r\n\t\t\t\t\t\tif (sql_query.endsWith(\";\")) {\r\n\t\t\t\t\t\t\tsql_query = sql_query.substring(0, sql_query.length() - 1);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tif (sql_query.endsWith(\"//\")) {\r\n\t\t\t\t\t\t\tsql_query = sql_query.substring(0, sql_query.length() - 2);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tfor (Map.Entry<String, String> entry : replacementMap.entrySet()) {\r\n\t\t\t\t\t\t\tsql_query = sql_query.replace(entry.getKey(), entry.getValue());\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tresults.add(sql_query);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif (line.isEmpty() || line.trim().startsWith(\"--\")) {\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\tsql_query += \" \" + line.trim();\r\n\t\t\t\t\t}\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tdefault:\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn results;\r\n\t}\r\n\r\n\tprivate Result withConnection(String db_conn, SQLCommand<Connection, Result> cmd) {\r\n\t\treturn withConnection(db_conn, cmd, null);\r\n\t}\r\n\r\n\tprivate <R> Optional<R> withConnectionGeneric(String db_conn, SQLCommand<Connection, R> cmd,\r\n\t\t\t\t\t\t\t\t\t\t\t\t  ExceptionHandler<Exception, R> exceptionHandler) {\r\n\t\tR result = null;\r\n\t\ttry (Connection conn = DriverManager.getConnection(db_conn)) {\r\n\t\t\tlogAvailableDrivers();\r\n\t\t\tresult = cmd.execute(conn);\r\n\t\t\tconn.close();\r\n\t\t} catch (SQLException | IOException e) {\r\n\r\n\t\t\t// Handle Derby shutdown - it throws exception even if the shutdown was correct…\r\n\t\t\tif (e instanceof SQLException) {\r\n\t\t\t\tSQLException se = (SQLException) e;\r\n\t\t\t\tif (((se.getErrorCode() == 50000) && (\"XJ015\".equals(se.getSQLState()))) ||\r\n\t\t\t\t\t\t((se.getErrorCode() == 45000) && (\"08006\".equals(se.getSQLState())))) {\r\n\t\t\t\t\tSystem.out.println(\"Derby shut down normally\");\r\n\t\t\t\t\tlog.log(Level.INFO, \"Derby shut down normally\");\r\n\t\t\t\t\treturn Optional.empty();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (exceptionHandler != null) {\r\n\t\t\t\treturn Optional.of(exceptionHandler.handleException(e));\r\n\t\t\t} else {\r\n\t\t\t\tlog.log(Level.SEVERE, \"\\n\\n\\n=====\\nFailure: \" + e.getMessage() + \"\\n=====\\n\\n\");\r\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\r\n\r\n\t\t\t\t\tlog.log(Level.SEVERE, \"Failure: \" + e.getMessage(), e);\r\n\t\t\t\t}\r\n\t\t\t\treturn Optional.empty();\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn Optional.ofNullable(result);\r\n\t}\r\n\r\n\tprivate void logAvailableDrivers() {\r\n\t\tString availableDrivers = Collections.list(DriverManager.getDrivers())\r\n\t\t\t\t.stream()\r\n\t\t\t\t.map(driver -> driver.getClass().getName() + \"[\" + driver.getMajorVersion() + \".\" +\r\n\t\t\t\t\t\tdriver.getMinorVersion() + \"]\")\r\n\t\t\t\t.collect(Collectors.joining(\" ,\"));\r\n\t\tlog.log(Level.FINE, \"DriverManager (available drivers): {0}\", availableDrivers);\r\n\t}\r\n\r\n\tprivate Result withConnection(String db_conn, SQLCommand<Connection, Result> cmd,\r\n\t\t\t\t\t\t\t\t  ExceptionHandler<Exception, Result> exceptionHandler) {\r\n\t\tfinal Optional<Result> result = withConnectionGeneric(db_conn, cmd, exceptionHandler);\r\n\t\treturn result.orElse(Result.error);\r\n\t}\r\n\r\n\tprivate Result withStatement(String dbConn, SQLCommand<Statement, Result> cmd) {\r\n\t\treturn withConnection(dbConn, conn -> {\r\n\t\t\ttry (Statement stmt = conn.createStatement()) {\r\n\t\t\t\treturn cmd.execute(stmt);\r\n\t\t\t}\r\n\t\t});\r\n\t}\r\n\r\n\t/**\r\n\t * Helper method used to generate proper database URI depending on properties.\r\n\t *\r\n\t * @param includeDbName configure whether to include database name in the URI\r\n\t * @param useRootCredentials whether to put in the URI credentials of database administrator ({@code true}) or\r\n\t * regular user.\r\n\t *\r\n\t */\r\n\tprivate String getDBUri(boolean includeDbName, boolean useRootCredentials) {\r\n\t\treturn getDBUri(includeDbName, useRootCredentials, false);\r\n\t}\r\n\r\n\tprivate String getDBUri(boolean includeDbName, boolean useRootCredentials, boolean debug) {\r\n\t\tStringBuilder db_uri = new StringBuilder(\"jdbc:\");\r\n\t\tString database = params.getDbType();\r\n\t\tString USERNAME = useRootCredentials ? params.getDbRootUser() : params.getDbUser();\r\n\t\tString PASSWORD = useRootCredentials ? params.getDbRootPass() : params.getDbPass();\r\n\r\n\t\tswitch (database) {\r\n\t\t\tcase \"sqlserver\":\r\n\t\t\t\tdb_uri.append(\"sqlserver:\");\r\n\t\t\t\tbreak;\r\n\t\t\tdefault:\r\n\t\t\t\tdb_uri.append(database).append(\":\");\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t\tswitch (database) {\r\n\t\t\tcase \"derby\":\r\n\t\t\t\tif (params.getOtherParameters() != null && !params.getOtherParameters().isEmpty()) {\r\n\t\t\t\t\tfor (Map.Entry<String, String> entry : params.getOtherParameters().entrySet()) {\r\n\t\t\t\t\t\tdb_uri.append(\";\").append(entry.getKey()).append(\"=\").append(entry.getValue());\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tdb_uri.append(params.getDbName()).append(\";create=true\");\r\n\t\t\t\tbreak;\r\n\t\t\tcase \"sqlserver\":\r\n\t\t\t\tdb_uri.append(\"//\").append(params.getDbHostname());\r\n\t\t\t\tif (includeDbName) {\r\n\t\t\t\t\tdb_uri.append(\";databaseName=\").append(params.getDbName());\r\n\t\t\t\t}\r\n\t\t\t\tdb_uri.append(\";user=\").append(USERNAME);\r\n\t\t\t\tif (PASSWORD != null && !PASSWORD.isEmpty()) {\r\n\t\t\t\t\tdb_uri.append(\";password=\").append(maskString(PASSWORD, debug));\r\n\t\t\t\t}\r\n\t\t\t\tdb_uri.append(\";schema=dbo\");\r\n\t\t\t\tdb_uri.append(\";lastUpdateCount=false\");\r\n\t\t\t\tdb_uri.append(\";cacheMetaData=false\");\r\n\t\t\t\tif (Boolean.TRUE.equals(params.isUseSSL())) {\r\n\t\t\t\t\tdb_uri.append(\";encrypt=true\");\r\n\t\t\t\t} else {\r\n\t\t\t\t\tdb_uri.append(\";encrypt=false\");\r\n\t\t\t\t}\r\n\t\t\t\tif (params.getOtherParameters() != null && !params.getOtherParameters().isEmpty()) {\r\n\t\t\t\t\tfor (Map.Entry<String, String> entry : params.getOtherParameters().entrySet()) {\r\n\t\t\t\t\t\tdb_uri.append(\";\").append(entry.getKey()).append(\"=\").append(entry.getValue());\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\tcase \"postgresql\":\r\n\t\t\t\tdb_uri.append(\"//\").append(params.getDbHostname()).append(\"/\");\r\n\t\t\t\tif (includeDbName) {\r\n\t\t\t\t\tdb_uri.append(params.getDbName());\r\n\t\t\t\t} else if (useRootCredentials) {\r\n\t\t\t\t\tdb_uri.append(\"postgres\");\r\n\t\t\t\t}\r\n\t\t\t\tdb_uri.append(\"?user=\").append(USERNAME);\r\n\t\t\t\tif (PASSWORD != null && !PASSWORD.isEmpty()) {\r\n\t\t\t\t\tdb_uri.append(\"&password=\").append(maskString(PASSWORD, debug));\r\n\t\t\t\t}\r\n\t\t\t\tif (Boolean.TRUE.equals(params.isUseSSL())) {\r\n\t\t\t\t\tdb_uri.append(\"&useSSL=true\");\r\n\t\t\t\t} else if (Boolean.FALSE.equals(params.isUseSSL())) {\r\n\t\t\t\t\t// explicitly disable SSL to avoid a warning by the driver\r\n\t\t\t\t\tdb_uri.append(\"&useSSL=false\");\r\n\t\t\t\t}\r\n\t\t\t\tif (params.getOtherParameters() != null && !params.getOtherParameters().isEmpty()) {\r\n\t\t\t\t\tfor (Map.Entry<String, String> entry : params.getOtherParameters().entrySet()) {\r\n\t\t\t\t\t\tdb_uri.append(\"&\").append(entry.getKey()).append(\"=\").append(entry.getValue());\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase \"mysql\":\r\n\t\t\t\tdb_uri.append(\"//\").append(params.getDbHostname()).append(\"/\");\r\n\t\t\t\tif (includeDbName) {\r\n\t\t\t\t\tdb_uri.append(params.getDbName());\r\n\t\t\t\t}\r\n\t\t\t\tdb_uri.append(\"?user=\").append(USERNAME);\r\n\t\t\t\tif (PASSWORD != null && !PASSWORD.isEmpty()) {\r\n\t\t\t\t\tdb_uri.append(\"&password=\").append(maskString(PASSWORD, debug));\r\n\t\t\t\t}\r\n\t\t\t\tif (Boolean.TRUE.equals(params.isUseSSL())) {\r\n\t\t\t\t\tdb_uri.append(\"&useSSL=true\");\r\n\t\t\t\t} else if (Boolean.FALSE.equals(params.isUseSSL())) {\r\n\t\t\t\t\t// explicitly disable SSL to avoid a warning by the driver\r\n\t\t\t\t\tdb_uri.append(\"&useSSL=false\");\r\n\t\t\t\t}\r\n\t\t\t\tif (Boolean.TRUE.equals(params.isUseLegacyDatetimeCode())) {\r\n\t\t\t\t\tdb_uri.append(\"&useLegacyDatetimeCode=\").append(params.isUseLegacyDatetimeCode());\r\n\t\t\t\t}\r\n\t\t\t\tif (params.getServerTimezone() != null) {\r\n\t\t\t\t\tdb_uri.append(\"&serverTimezone=\").append(params.getServerTimezone());\r\n\t\t\t\t}\r\n\t\t\t\tif (params.getOtherParameters() != null && !params.getOtherParameters().isEmpty()) {\r\n\t\t\t\t\tfor (Map.Entry<String, String> entry : params.getOtherParameters().entrySet()) {\r\n\t\t\t\t\t\tdb_uri.append(\"&\").append(entry.getKey()).append(\"=\").append(entry.getValue());\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tdb_uri.append(\"&allowPublicKeyRetrieval=true\");\r\n\t\t\t\tbreak;\r\n\t\t\tdefault:\r\n\t\t\t\tthrow new IllegalArgumentException(\"Unknown database type: \" + database);\r\n\t\t}\r\n\t\treturn db_uri.toString();\r\n\t}\r\n\r\n\tprivate String maskString(String PASSWORD, boolean debug) {\r\n\t\treturn debug ? PASSWORD.replaceAll(\".\", \"*\") : PASSWORD;\r\n\t}\r\n\r\n\tenum SQL_LOAD_STATE {\r\n\r\n\t\tINIT,\r\n\t\tIN_SQL;\r\n\t}\r\n\r\n\tenum Tasks\r\n\t\t\timplements TigaseDBTask {\r\n\r\n\t\tVALIDATE_CONNECTION(\"Checking connection to the database\") {\r\n\t\t\t@Override\r\n\t\t\tpublic void execute(DBSchemaLoader helper, Parameters params) {\r\n\t\t\t\thelper.validateDBConnection();\r\n\t\t\t}\r\n\t\t},\r\n\t\tVALIDATE_DB_EXISTS(\"Checking if the database exists\") {\r\n\t\t\t@Override\r\n\t\t\tpublic void execute(DBSchemaLoader helper, Parameters params) {\r\n\t\t\t\thelper.validateDBExists();\r\n\t\t\t}\r\n\t\t},\r\n\t\tVALIDATE_DB_SCHEMA(\"Checking the database schema\") {\r\n\t\t\t@Override\r\n\t\t\tpublic void execute(DBSchemaLoader helper, Parameters params) {\r\n\t\t\t\tSchemaManager.getDefaultDataSourceAndSchemas(helper.getDBUri()).values().stream().findAny().ifPresent( schemas -> {\r\n\t\t\t\t\tschemas.forEach(schema -> helper.loadSchema(schema, schema.getVersion().get().toString()));\r\n\t\t\t\t});\r\n\t\t\t}\r\n\t\t},\r\n\t\tADD_ADMIN_XMPP_ACCOUNT(\"Adding XMPP admin accounts\") {\r\n\t\t\t@Override\r\n\t\t\tpublic void execute(DBSchemaLoader helper, Parameters params) {\r\n\t\t\t\tSchemaManager.getDefaultDataSourceAndSchemas(helper.getDBUri()).values().stream().findAny().ifPresent( schemas -> {\r\n\t\t\t\t\tschemas.stream().filter(schema -> Schema.SERVER_SCHEMA_ID.equals(schema.getId())).findAny().ifPresent(helper::addXmppAdminAccount);\r\n\t\t\t\t});\r\n\t\t\t}\r\n\t\t},\r\n\t\tEXECUTE_SIMPLE_QUERY(\"Executing simple single query\") {\r\n\t\t\t@Override\r\n\t\t\tpublic void execute(DBSchemaLoader helper, Parameters params) {\r\n\t\t\t\thelper.executeSingleQuery(params.getQuery());\r\n\t\t\t}\r\n\t\t},\r\n\t\tLOAD_SCHEMA_FILE(\"Loading schema file from provided file\") {\r\n\t\t\t@Override\r\n\t\t\tpublic void execute(DBSchemaLoader helper, Parameters params) {\r\n\t\t\t\thelper.loadSchemaFile(params.getFile());\r\n\t\t\t}\r\n\t\t},\r\n\t\tPOST_INSTALLATION(\"Post installation actions\") {\r\n\t\t\t@Override\r\n\t\t\tpublic void execute(DBSchemaLoader helper, Parameters params) {\r\n\t\t\t\thelper.postInstallation();\r\n\t\t\t}\r\n\t\t},\r\n\t\tSHUTDOWN_DATABASE(\"Shutting Down Database\") {\r\n\t\t\t@Override\r\n\t\t\tpublic void execute(DBSchemaLoader helper, Parameters params) {\r\n\t\t\t\thelper.shutdownDerby();\r\n\t\t\t}\r\n\t\t},\r\n\t\tPRINT_INFO_TASK(\"Database Configuration Details\") {\r\n\t\t\t@Override\r\n\t\t\tpublic void execute(DBSchemaLoader helper, Parameters params) {\r\n\t\t\t\thelper.printInfo();\r\n\t\t\t}\r\n\t\t};\r\n\t\tprivate final String description;\r\n\r\n\t\tpublic static TigaseDBTask[] getQueryTasks() {\r\n\t\t\treturn new TigaseDBTask[]{VALIDATE_CONNECTION, EXECUTE_SIMPLE_QUERY, SHUTDOWN_DATABASE};\r\n\t\t}\r\n\r\n\t\tpublic static TigaseDBTask[] getSchemaTasks() {\r\n\t\t\treturn new TigaseDBTask[]{VALIDATE_CONNECTION, VALIDATE_DB_EXISTS, LOAD_SCHEMA_FILE, POST_INSTALLATION,\r\n\t\t\t\t\t\t\t\t\t  SHUTDOWN_DATABASE, PRINT_INFO_TASK};\r\n\t\t}\r\n\r\n\t\tpublic static TigaseDBTask[] getTasksInOrder() {\r\n\t\t\treturn new TigaseDBTask[]{VALIDATE_CONNECTION, VALIDATE_DB_EXISTS, VALIDATE_DB_SCHEMA,\r\n\t\t\t\t\t\t\t\t\t  ADD_ADMIN_XMPP_ACCOUNT, POST_INSTALLATION, SHUTDOWN_DATABASE, PRINT_INFO_TASK};\r\n\t\t}\r\n\r\n\t\tprivate Tasks(String description) {\r\n\t\t\tthis.description = description;\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic String getDescription() {\r\n\t\t\treturn description;\r\n\t\t}\r\n\t}\r\n\r\n\tpublic interface ExceptionHandler<T extends Exception, R> {\r\n\r\n\t\tR handleException(T ex);\r\n\t}\r\n\r\n\tpublic interface SQLCommand<C, R> {\r\n\r\n\t\tR execute(C conn) throws SQLException, IOException;\r\n\r\n\t}\r\n\r\n\tstatic interface TigaseDBTask {\r\n\r\n\t\tString getDescription();\r\n\r\n\t\tabstract void execute(DBSchemaLoader helper, Parameters variables);\r\n\t}\r\n\r\n\tpublic static class Parameters\r\n\t\t\timplements SchemaLoader.Parameters {\r\n\r\n\t\tprivate String adminPassword;\r\n\t\tprivate List<BareJID> admins;\r\n\t\tprivate String dbHostname = null;\r\n\t\tprivate String dbName = null;\r\n\t\tprivate String dbPass = null;\r\n\t\tprivate String dbRootPass;\r\n\t\tprivate String dbRootUser;\r\n\t\tprivate Boolean dbRootAsk = false;\r\n\t\tprivate String dbType;\r\n\t\tprivate String dbUser = null;\r\n\t\tprivate String file;\r\n\t\tprivate Boolean ingoreMissingFiles = false;\r\n\t\tprivate Level logLevel = Level.CONFIG;\r\n\t\tprivate String query;\r\n\t\tprivate String serverTimezone = null;\r\n\t\tprivate Boolean useLegacyDatetimeCode = false;\r\n\t\tprivate Boolean useSSL = null;\r\n\t\tprivate Boolean forceReloadSchema = false;\r\n\t\tprivate String schemaDirectory = \"database/\";\r\n\t\tprivate Map<String,String> otherParameters = new HashMap<>();\r\n\t\tprivate List<ExtServiceDiscoItem> externalServices = new ArrayList<>();\r\n\r\n\t\tprivate static String getProperty(Properties props, PARAMETERS_ENUM param) {\r\n\t\t\treturn props.getProperty(param.getName(), null); //param.getDefaultValue());\r\n\t\t}\r\n\r\n\t\tprivate static String getPropertyWithDefault(Properties props, DBSchemaLoader.PARAMETERS_ENUM param) {\r\n\t\t\treturn props.getProperty(param.getName(), param.getDefaultValue());\r\n\t\t}\r\n\r\n\t\tprivate static <T> T getProperty(Properties props, PARAMETERS_ENUM param, Function<String, T> converter) {\r\n\t\t\tString tmp = getProperty(props, param);\r\n\t\t\tif (tmp == null) {\r\n\t\t\t\treturn null;\r\n\t\t\t}\r\n\t\t\treturn converter.apply(tmp);\r\n\t\t}\r\n\r\n\t\tprivate static <T> T getPropertyWithDefault(Properties props, DBSchemaLoader.PARAMETERS_ENUM param, Function<String, T> converter) {\r\n\t\t\tString tmp = getPropertyWithDefault(props, param);\r\n\t\t\tif (tmp == null) {\r\n\t\t\t\treturn null;\r\n\t\t\t}\r\n\t\t\treturn converter.apply(tmp);\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic String getAdminPassword() {\r\n\t\t\treturn adminPassword;\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic List<BareJID> getAdmins() {\r\n\t\t\treturn admins == null ? Collections.emptyList() : admins;\r\n\t\t}\r\n\r\n\t\tpublic String getDbRootPass() {\r\n\t\t\treturn dbRootPass;\r\n\t\t}\r\n\r\n\t\tpublic String getDbRootUser() {\r\n\t\t\treturn dbRootUser;\r\n\t\t}\r\n\r\n\t\tpublic boolean isDbRootAsk() {\r\n\t\t\treturn (dbRootUser == null || dbRootPass == null) && dbRootAsk != null && dbRootAsk;\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic void setDbRootAsk(boolean dbRootAsk) {\r\n\t\t\tthis.dbRootAsk = dbRootAsk;\r\n\t\t}\r\n\r\n\t\tpublic String getDbType() {\r\n\t\t\treturn dbType;\r\n\t\t}\r\n\r\n\t\tpublic String getDbName() {\r\n\t\t\treturn dbName;\r\n\t\t}\r\n\r\n\t\tpublic String getDbHostname() {\r\n\t\t\treturn dbHostname;\r\n\t\t}\r\n\r\n\t\tpublic String getDbUser() {\r\n\t\t\treturn dbUser;\r\n\t\t}\r\n\r\n\t\tpublic String getDbPass() {\r\n\t\t\treturn dbPass;\r\n\t\t}\r\n\r\n\t\tpublic Map<String, String> getOtherParameters() {\r\n\t\t\treturn otherParameters;\r\n\t\t}\r\n\r\n\t\tpublic boolean isForceReloadSchema() {\r\n\t\t\treturn forceReloadSchema;\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic void setForceReloadSchema(boolean forceReloadSchema) {\r\n\t\t\tthis.forceReloadSchema = forceReloadSchema;\r\n\t\t}\r\n\r\n\t\tpublic boolean isIgnoreMissingFiles() {\r\n\t\t\treturn ingoreMissingFiles;\r\n\t\t}\r\n\r\n\t\tpublic Boolean isUseSSL() {\r\n\t\t\treturn useSSL;\r\n\t\t}\r\n\r\n\t\tpublic Boolean isUseLegacyDatetimeCode() {\r\n\t\t\treturn useLegacyDatetimeCode;\r\n\t\t}\r\n\r\n\t\tpublic String getServerTimezone() {\r\n\t\t\treturn serverTimezone;\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic List<ExtServiceDiscoItem> getExternalServices() {\r\n\t\t\treturn externalServices;\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic void setExternalServices(List<ExtServiceDiscoItem> externalServices) {\r\n\t\t\tthis.externalServices = externalServices;\r\n\t\t}\r\n\r\n\t\tpublic String getSchemaDirectory() {\r\n\t\t\treturn schemaDirectory;\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic void setSchemaDirectory(String schemaDirectory) {\r\n\t\t\tthis.schemaDirectory = schemaDirectory;\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic void parseUri(String uri) {\r\n\t\t\tint idx = uri.indexOf(\":\", 5);\r\n\t\t\tdbType = uri.substring(5, idx);\r\n\t\t\tif (\"jtds\".equals(dbType)) {\r\n\t\t\t\tdbType = \"sqlserver\";\r\n\t\t\t}\r\n\r\n\t\t\tString rest = null;\r\n\t\t\tswitch (dbType) {\r\n\t\t\t\tcase \"derby\":\r\n\t\t\t\t\tdbName = uri.substring(idx + 1, uri.indexOf(\";\"));\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase \"sqlserver\":\r\n\t\t\t\t\tidx = uri.indexOf(\"//\", idx) + 2;\r\n\t\t\t\t\trest = uri.substring(idx);\r\n\t\t\t\t\tfor (String x : rest.split(\";\")) {\r\n\t\t\t\t\t\tif (!x.contains(\"=\")) {\r\n\t\t\t\t\t\t\tdbHostname = x;\r\n\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\tString p[] = x.split(\"=\");\r\n\t\t\t\t\t\t\tif (\"databaseName\".equals(p[0])) {\r\n\t\t\t\t\t\t\t\tdbName = p[1];\r\n\t\t\t\t\t\t\t} else if (\"user\".equals(p[0])) {\r\n\t\t\t\t\t\t\t\tdbUser = p[1];\r\n\t\t\t\t\t\t\t} else if (\"password\".equals(p[0])) {\r\n\t\t\t\t\t\t\t\tdbPass = p[1];\r\n\t\t\t\t\t\t\t} else if (\"encrypt\".equals(p[0])) {\r\n\t\t\t\t\t\t\t\tuseSSL = Boolean.valueOf(p[1]);\r\n\t\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\t\totherParameters.put(p[0],p[1]);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tdefault:\r\n\t\t\t\t\tidx = uri.indexOf(\"//\", idx) + 2;\r\n\t\t\t\t\trest = uri.substring(idx);\r\n\t\t\t\t\tidx = rest.indexOf(\"/\");\r\n\t\t\t\t\tdbHostname = rest.substring(0, idx);\r\n\t\t\t\t\trest = rest.substring(idx + 1);\r\n\t\t\t\t\tidx = rest.indexOf(\"?\");\r\n\t\t\t\t\tdbName = rest.substring(0, idx);\r\n\t\t\t\t\trest = rest.substring(idx + 1);\r\n\t\t\t\t\tuseLegacyDatetimeCode = true;\r\n\t\t\t\t\tfor (String x : rest.split(\"&\")) {\r\n\t\t\t\t\t\tString p[] = x.split(\"=\");\r\n\t\t\t\t\t\tif (p.length < 2) {\r\n\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tif (\"user\".equals(p[0])) {\r\n\t\t\t\t\t\t\tdbUser = p[1];\r\n\t\t\t\t\t\t} else if (\"password\".equals(p[0])) {\r\n\t\t\t\t\t\t\tdbPass = p[1];\r\n\t\t\t\t\t\t} else if (\"useSSL\".equals(p[0])) {\r\n\t\t\t\t\t\t\tuseSSL = Boolean.valueOf(p[1]);\r\n\t\t\t\t\t\t} else if (\"useLegacyDatetimeCode\".equals(p[0])) {\r\n\t\t\t\t\t\t\tuseLegacyDatetimeCode = Boolean.valueOf(p[1]);\r\n\t\t\t\t\t\t} else if (\"serverTimezone\".equals(p[0])) {\r\n\t\t\t\t\t\t\tserverTimezone = p[1];\r\n\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\totherParameters.put(p[0],p[1]);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic void setProperties(Properties props) {\r\n\t\t\tlogLevel = getPropertyWithDefault(props, PARAMETERS_ENUM.LOG_LEVEL, Level::parse);\r\n\t\t\tingoreMissingFiles = getProperty(props, PARAMETERS_ENUM.IGNORE_MISSING_FILES, Boolean::valueOf);\r\n\t\t\tadmins = getProperty(props, PARAMETERS_ENUM.ADMIN_JID, tmp -> Arrays.stream(tmp.split(\",\"))\r\n\t\t\t\t\t.map(BareJID::bareJIDInstanceNS)\r\n\t\t\t\t\t.collect(Collectors.toList()));\r\n\t\t\tadminPassword = getProperty(props, PARAMETERS_ENUM.ADMIN_JID_PASS);\r\n\r\n\t\t\tdbType = getProperty(props, PARAMETERS_ENUM.DATABASE_TYPE);\r\n\t\t\tdbName = getProperty(props, PARAMETERS_ENUM.DATABASE_NAME);\r\n\t\t\tdbHostname = getProperty(props, PARAMETERS_ENUM.DATABASE_HOSTNAME);\r\n\t\t\tdbUser = getProperty(props, PARAMETERS_ENUM.TIGASE_USERNAME);\r\n\t\t\tdbPass = getProperty(props, PARAMETERS_ENUM.TIGASE_PASSWORD);\r\n\t\t\tuseSSL = getProperty(props, PARAMETERS_ENUM.USE_SSL, Boolean::valueOf);\r\n\t\t\tuseLegacyDatetimeCode = getProperty(props, PARAMETERS_ENUM.USE_LEGACY_DATETIME_CODE, Boolean::valueOf);\r\n\t\t\tserverTimezone = getProperty(props, PARAMETERS_ENUM.SERVER_TIMEZONE);\r\n\r\n\t\t\tdbRootUser = getProperty(props, PARAMETERS_ENUM.ROOT_USERNAME);\r\n\t\t\tdbRootPass = getProperty(props, PARAMETERS_ENUM.ROOT_PASSWORD);\r\n\t\t\tdbRootAsk = getProperty(props, PARAMETERS_ENUM.ROOT_ASK, Boolean::valueOf);\r\n\r\n\t\t\tfile = getProperty(props, PARAMETERS_ENUM.FILE);\r\n\t\t\tquery = getProperty(props, PARAMETERS_ENUM.QUERY);\r\n\t\t\tforceReloadSchema = getPropertyWithDefault(props, PARAMETERS_ENUM.FORCE_RELOAD_ALL_SCHEMA_FILES, Boolean::valueOf);\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic void setAdmins(List<BareJID> admins, String password) {\r\n\t\t\tthis.admins = admins;\r\n\t\t\tthis.adminPassword = password;\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic void setDbRootCredentials(String username, String password) {\r\n\t\t\tthis.dbRootUser = username;\r\n\t\t\tthis.dbRootPass = password;\r\n\t\t\tif (this.dbRootUser == null && this.dbRootPass == null) {\r\n\t\t\t\tthis.dbRootUser = this.dbUser;\r\n\t\t\t\tthis.dbRootPass = this.dbPass;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic Level getLogLevel() {\r\n\t\t\treturn this.logLevel;\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic void setLogLevel(Level level) {\r\n\t\t\tthis.logLevel = level;\r\n\t\t}\r\n\r\n\t\tpublic Boolean getIngoreMissingFiles() {\r\n\t\t\treturn ingoreMissingFiles;\r\n\t\t}\r\n\r\n\t\tpublic void setIngoreMissingFiles(Boolean ingoreMissingFiles) {\r\n\t\t\tthis.ingoreMissingFiles = ingoreMissingFiles;\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic String toString() {\r\n\t\t\treturn \"[\" + Arrays.stream(this.getClass().getDeclaredFields()).map(field -> {\r\n\t\t\t\tString result = field.getName() + \": \";\r\n\t\t\t\tObject value;\r\n\t\t\t\ttry {\r\n\t\t\t\t\tfield.setAccessible(true);\r\n\t\t\t\t\tvalue = field.get(this);\r\n\t\t\t\t\tif (field.getName().toLowerCase().contains(\"pass\")) {\r\n\t\t\t\t\t\tvalue = String.valueOf(value).replaceAll(\".\", \"*\");\r\n\t\t\t\t\t}\r\n\t\t\t\t} catch (Exception ex) {\r\n\t\t\t\t\tvalue = \"Error!\";\r\n\t\t\t\t}\r\n\t\t\t\treturn result + value;\r\n\t\t\t}).collect(Collectors.joining(\", \")) + \"]\";\r\n\t\t}\r\n\r\n\t\tprotected void init(Optional<SchemaManager.RootCredentialsCache> rootCredentialsCache) {\r\n\t\t\tif (dbRootUser == null || dbRootPass == null) {\r\n\t\t\t\tSchemaManager.RootCredentials credentials = rootCredentialsCache.isPresent()\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? rootCredentialsCache.get().get(dbHostname)\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: null;\r\n\t\t\t\tif (credentials != null) {\r\n\t\t\t\t\tdbRootUser = credentials.user;\r\n\t\t\t\t\tdbRootPass = credentials.password;\r\n\t\t\t\t} else {\r\n\t\t\t\t\tif (isDbRootAsk() && !\"derby\".equals(dbType)) {\r\n\t\t\t\t\t\tSystemConsole console = new SystemConsole();\r\n\t\t\t\t\t\tconsole.writeLine(\"\");\r\n\t\t\t\t\t\tif (dbRootUser == null) {\r\n\t\t\t\t\t\t\tdbRootUser = console.readLine(\r\n\t\t\t\t\t\t\t\t\t\"Database root account username used to create tigase user and database at \" +\r\n\t\t\t\t\t\t\t\t\t\t\tdbHostname + \" : \");\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tif (dbRootPass == null) {\r\n\t\t\t\t\t\t\tdbRootPass = new String(console.readPassword(\r\n\t\t\t\t\t\t\t\t\t\"Database root account password used to create tigase user and database at \" +\r\n\t\t\t\t\t\t\t\t\t\t\tdbHostname + \" : \"));\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\trootCredentialsCache.ifPresent(cache -> cache.set(dbHostname,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  new SchemaManager.RootCredentials(dbRootUser,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdbRootPass)));\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate String getFile() {\r\n\t\t\treturn file;\r\n\t\t}\r\n\r\n\t\tprivate String getQuery() {\r\n\t\t\treturn query;\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "src/main/java/tigase/db/util/JDBCPasswordObfuscator.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.db.util;\n\nimport tigase.db.DataRepository;\nimport tigase.db.jdbc.DataRepositoryImpl;\n\npublic class JDBCPasswordObfuscator {\n\n\tprivate static String getObfuscatedUrl(String url, char separatorCharacter) {\n\t\tString passwordParameter = \"password=\";\n\t\tfinal int passwordIndex = url.lastIndexOf(passwordParameter);\n\t\tfinal String urlReminder = url.substring(passwordIndex + passwordParameter.length());\n\t\tint nextSectionIndex = urlReminder.indexOf(separatorCharacter);\n\t\tint passwordLength = nextSectionIndex > 0 ? nextSectionIndex : urlReminder.length();\n\t\turl = url.substring(0, passwordIndex + passwordParameter.length()) + \"*\".repeat(passwordLength) +\n\t\t\t\turlReminder.substring(passwordLength);\n\t\treturn url;\n\t}\n\n\tpublic static String obfuscatePassword(String url) {\n\n\t\tfinal DataRepository.dbTypes dbType = DataRepositoryImpl.parseDatabaseType(url);\n\n\t\tswitch (dbType) {\n\t\t\tcase postgresql:\n\t\t\tcase mysql:\n\t\t\t\turl = getObfuscatedUrl(url, '&');\n\t\t\t\tbreak;\n\t\t\tcase jtds:\n\t\t\tcase sqlserver:\n\t\t\t\turl = getObfuscatedUrl(url, ';');\n\t\t\t\tbreak;\n\t\t}\n\n\t\treturn url;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/util/RepositoryVersionAware.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.util;\n\nimport tigase.util.Version;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.Optional;\n\n/**\n * Interface indicates that the implementation is aware of the version, can specify required version of the schema that should be present in the database and perform update\n * from current version in the database to the required version\n */\npublic interface RepositoryVersionAware {\n\n\t/**\n\t * Returns current required version of the repository implementing this interface\n\t *\n\t * (If we are version aware then we have to specify the version)\n\t *\n\t * @return current required version of the schema for the\n\t */\n\tdefault public Version getVersion() {\n\n\t\tif (this.getClass().isAnnotationPresent(SchemaVersion.class)) {\n\t\t\tSchemaVersion sv = this.getClass().getAnnotation(SchemaVersion.class);\n\t\t\treturn Version.of(sv.version());\n\t\t} else {\n\t\t\tString impl = this.getClass().getPackage().getImplementationVersion();\n\t\t\tif (impl != null && !impl.isEmpty()) {\n\t\t\t\treturn Version.of(impl);\n\t\t\t} else {\n\t\t\t\treturn Version.ZERO;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Method used to update schema in the database from the (optional) {@code oldVersion} to {@code newVersion}. If the\n\t * process was correct (i.e. return {@link tigase.db.util.SchemaLoader.Result#ok}) then new version will be stored\n\t * in the database.\n\t *\n\t * @param oldVersion optional version of the schema currently loaded in the database\n\t * @param newVersion version to which component schema should be updated\n\t *\n\t * @return result of the update process - if the process was correct then  {@link tigase.db.util.SchemaLoader.Result#ok}\n\t * should be returned\n\t *\n\t * @throws Exception when something unexpected happened\n\t */\n\tpublic default SchemaLoader.Result updateSchema(Optional<Version> oldVersion, Version newVersion) throws Exception {\n\t\t// by default we don't need to upgrade anything so we skip it\n\t\treturn SchemaLoader.Result.skipped;\n\t};\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target({ElementType.TYPE})\n\tpublic static @interface SchemaVersion {\n\n\t\tString version() default \"0.0.0-b0\";\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/util/SchemaLoader.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.util;\n\nimport tigase.auth.CredentialsDecoderBean;\nimport tigase.auth.CredentialsEncoderBean;\nimport tigase.component.DSLBeanConfigurator;\nimport tigase.conf.ConfigBuilder;\nimport tigase.conf.ConfigHolder;\nimport tigase.conf.ConfigWriter;\nimport tigase.db.*;\nimport tigase.db.comp.ComponentRepository;\nimport tigase.db.comp.RepositoryItem;\nimport tigase.db.comp.UserRepoRepository;\nimport tigase.kernel.DefaultTypesConverter;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.core.Kernel;\nimport tigase.osgi.util.ClassUtilBean;\nimport tigase.server.extdisco.ExtServiceDiscoItem;\nimport tigase.util.Version;\nimport tigase.util.ui.console.CommandlineParameter;\nimport tigase.util.ui.console.ParameterParser;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.io.IOException;\nimport java.io.StringWriter;\nimport java.lang.reflect.Modifier;\nimport java.lang.reflect.Type;\nimport java.util.*;\nimport java.util.function.Function;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport static tigase.db.util.SchemaManager.COMMON_SCHEMA_ID;\nimport static tigase.db.util.SchemaManager.COMMON_SCHEMA_VERSION;\nimport static tigase.util.reflection.ReflectionHelper.classMatchesClassWithParameters;\n\n/**\n * @author andrzej\n */\npublic abstract class SchemaLoader<P extends SchemaLoader.Parameters> {\n\n\tprotected static final Logger log = Logger.getLogger(SchemaLoader.class.getName());\n\n\tpublic static enum Result {\n\t\tok,\n\t\terror,\n\t\twarning,\n\t\tskipped\n\t}\n\tprivate String type;\n\n\tprivate static List<CommandlineParameter> getDbTypeDependentParameters(String type) {\n\t\tSchemaLoader loader = newInstance(type);\n\t\treturn loader.getCommandlineParameters();\n\t}\n\n\tpublic static List<CommandlineParameter> getMainCommandlineParameters(boolean forceNotRequired) {\n\t\tString[] supportedTypes = getAllSupportedTypesStream().map(TypeInfo::getName).sorted().toArray(String[]::new);\n\t\treturn Arrays.asList(new CommandlineParameter.Builder(\"T\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  DBSchemaLoader.PARAMETERS_ENUM.DATABASE_TYPE.getName()).description(\n\t\t\t\t\"Database server type\")\n\t\t\t\t\t\t\t\t\t .options(supportedTypes)\n\t\t\t\t\t\t\t\t\t //.defaultValue(DBSchemaLoader.PARAMETERS_ENUM.DATABASE_TYPE.getDefaultValue())\n\t\t\t\t\t\t\t\t\t .valueDependentParametersProvider(SchemaLoader::getDbTypeDependentParameters)\n\t\t\t\t\t\t\t\t\t .required(!forceNotRequired)\n\t\t\t\t\t\t\t\t\t .build());\n\t}\n\n\tpublic static Stream<TypeInfo> getAllSupportedTypesStream() {\n\t\treturn getSchemaLoaderInstances()\n\t\t\t\t.map(SchemaLoader::getSupportedTypes)\n\t\t\t\t.flatMap(List::stream)\n\t\t\t\t.map(TypeInfo.class::cast)\n\t\t\t\t.sorted();\n\t}\n\n\tpublic static List<TypeInfo> getAllSupportedTypes() {\n\t\tfinal List<TypeInfo> supportedTypes = getAllSupportedTypesStream().collect(Collectors.toList());\n\t\tlog.log(Level.WARNING, \"All supported types: \" + supportedTypes);\n\t\treturn supportedTypes;\n\t}\n\n\tpublic static Optional<TypeInfo> getSupportedTypeForName(String name) {\n\t\tif (name == null) {\n\t\t\treturn getDefaultSupportedTypeForName();\n\t\t}\n\t\treturn getAllSupportedTypesStream()\n\t\t\t\t.filter(type -> type.getName().equals(name))\n\t\t\t\t.findAny();\n\t}\n\n\tpublic static Optional<TypeInfo> getDefaultSupportedTypeForName() {\n\t\treturn getAllSupportedTypesStream().findFirst();\n\t}\n\n\tprivate static Stream<Class<?>> getSchemaLoaderClasses() {\n\t\treturn ClassUtilBean.getInstance()\n\t\t\t\t.getAllClasses()\n\t\t\t\t.stream()\n\t\t\t\t.filter(SchemaLoader.class::isAssignableFrom)\n\t\t\t\t.filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()));\n\t}\n\n\tprivate static Stream<SchemaLoader> getSchemaLoaderInstances() {\n\t\treturn getSchemaLoaderClasses().map(clazz -> {\n\t\t\tSchemaLoader loader = null;\n\t\t\ttry {\n\t\t\t\tloader = (SchemaLoader) clazz.newInstance();\n\t\t\t} catch (IllegalAccessException | InstantiationException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Error creating instance\", e);\n\t\t\t}\n\t\t\treturn loader;\n\t\t}).filter(Objects::nonNull);\n\t}\n\n\t/**\n\t * Main method allowing pass arguments to the class and setting all logging to be printed to console.\n\t *\n\t * @param args key-value (in the form of {@code \"-<variable> value\"}) parameters.\n\t */\n\tpublic static void main(String[] args) {\n\t\tParameterParser parser = new ParameterParser(true);\n\n\t\tparser.addOptions(getMainCommandlineParameters(false));\n\n\t\tProperties properties = null;\n\n\t\tif (null == args || args.length == 0 || (properties = parser.parseArgs(args)) == null) {\n\t\t\tSystem.out.println(parser.getHelp());\n\t\t\tSystem.exit(0);\n\t\t} else {\n\t\t\tSystem.out.println(\"properties: \" + properties);\n\t\t}\n\n\t\tString type = properties.getProperty(DBSchemaLoader.PARAMETERS_ENUM.DATABASE_TYPE.getName());\n\n\t\tSchemaLoader dbHelper = newInstance(type);\n\n\t\tParameters params = dbHelper.createParameters();\n\t\tparams.setProperties(properties);\n\n\t\tdbHelper.execute(params);\n\t}\n\n\tpublic static SchemaLoader newInstance(String type) {\n\t\tif (type == null) {\n\t\t\tthrow new RuntimeException(\"Missing dbType property\");\n\t\t}\n\t\tSchemaLoader schemaLoader = getSchemaLoaderInstances().filter(instance -> instance.isSupported(type))\n\t\t\t\t.findAny()\n\t\t\t\t.get();\n\t\tschemaLoader.setType(type);\n\t\treturn schemaLoader;\n\t}\n\n\tpublic static SchemaLoader newInstanceForURI(String uri) {\n\t\tint idx = uri.indexOf(\":\");\n\t\tif (idx < 0) {\n\t\t\tthrow new RuntimeException(\"Unsupported URI\");\n\t\t}\n\t\tString type = uri.substring(0, idx);\n\t\tif (\"jdbc\".equals(type) || \"jtds\".equals(type)) {\n\t\t\treturn newInstanceForURI(uri.substring(idx+1));\n\t\t}\n\t\treturn newInstance(type);\n\t}\n\n\tpublic abstract P createParameters();\n\n\tpublic abstract void execute(Parameters params);\n\n\tpublic abstract void init(P props, Optional<SchemaManager.RootCredentialsCache> rootCredentialsCache);\n\n\tpublic void init(P props) {\n\t\tinit(props, Optional.empty());\n\t}\n\n\tpublic abstract List<TypeInfo> getSupportedTypes();\n\n\tpublic boolean isSupported(String dbType) {\n\t\treturn getSupportedTypes().stream().map(TypeInfo::getName).anyMatch(typeName -> typeName.equals(dbType));\n\t}\n\n\tpublic abstract String getDBUri();\n\n\tpublic abstract List<CommandlineParameter> getSetupOptions();\n\n\tpublic abstract List<CommandlineParameter> getCommandlineParameters();\n\n\t/**\n\t * Method validates whether the connection can at least be eI stablished. If yes then appropriate flag is set.\n\t */\n\tpublic abstract Result validateDBConnection();\n\n\t/**\n\t * Method, if the connection is validated by {@code validateDBConnection}, checks whether desired database exists.\n\t * If not it creates such database using {@code *-installer-create-db.sql} schema file substituting it's variables\n\t * with ones provided.\n\t */\n\tpublic abstract Result validateDBExists();\n\n\tpublic abstract Result postInstallation();\n\n\tprotected String getConfigString() throws IOException {\n\t\tString dataSourceUri = getDBUri();\n\n\t\tConfigBuilder builder = new ConfigBuilder();\n\t\tbuilder.withBean(ds -> ds.name(\"dataSource\").withBean(def -> def.name(\"default\").with(\"uri\", dataSourceUri)));\n\n\t\ttry (StringWriter writer = new StringWriter()) {\n\t\t\tnew ConfigWriter().write(writer, builder.build());\n\t\t\treturn writer.toString();\n\t\t}\n\t}\n\n\tpublic Result printInfo() {\n\t\tString configStr = null;\n\t\ttry {\n\t\t\tconfigStr = getConfigString();\n\t\t} catch (IOException ex) {\n\t\t\t// should not happen\n\t\t\tconfigStr = \"Failure: \" + ex.getMessage();\n\t\t}\n\t\tLogger.getLogger(this.getClass().getCanonicalName())\n\t\t\t\t.log(Level.INFO, \"\\n\\nDatabase \" + ConfigHolder.TDSL_CONFIG_FILE_DEF + \" configuration:\\n{0}\\n\",\n\t\t\t\t\t new Object[]{configStr});\n\t\treturn Result.ok;\n\t}\n\n\t/**\n\t * Method attempts to add XMPP admin user account to the database using {@code AuthRepository}.\n\t */\n\tpublic abstract Result addXmppAdminAccount(SchemaManager.SchemaInfo schemaInfo);\n\n\tpublic Result addExternalServices(SchemaManager.SchemaInfo schemaInfo) {\n\t\treturn Result.skipped;\n\t}\n\n\t/**\n\t * Methods attempt to write to database loaded schema version for particular component\n\t *\n\t * @param component name of the component for which version should be set\n\t * @param version value which should be associated with the component\n\t *\n\t * @return a {@link Result} object indicating whether the call was successful\n\t */\n\tpublic abstract Result setComponentVersion(String component, String version);\n\n\tpublic abstract Optional<Version> getComponentVersionFromDb(String component);\n\n\t/**\n\t * Method checks whether the connection to the database is possible and that database of specified name exists. If\n\t * yes then a schema file from properties is loaded.\n\t *\n\t * @param fileName set of {@code String} with path to file\n\t */\n\tpublic abstract Result loadSchemaFile(String fileName);\n\n\tpublic abstract Result shutdown();\n\n\tpublic Result loadCommonSchema() {\n\t\treturn loadSchema(\n\t\t\t\tnew SchemaManager.SchemaInfo(COMMON_SCHEMA_ID, \"Common Schema\", true, Collections.emptyList()), COMMON_SCHEMA_VERSION);\n\t}\n\n\tpublic abstract Result loadSchema(SchemaManager.SchemaInfo schemaInfo, String version);\n\n\tpublic abstract Optional<Version> getMinimalRequiredComponentVersionForUpgrade(SchemaManager.SchemaInfo schema);\n\t\n\tpublic abstract Result destroyDataSource();\n\n\tprotected <T extends DataSource> Result addUsersToRepository(SchemaManager.SchemaInfo schemaInfo, T dataSource, Class<T> dataSourceClass, List<BareJID> jids, String password, Logger log) {\n\t\treturn getDataSourceAwareClassesForSchemaInfo(schemaInfo, dataSourceClass)\n\t\t\t\t.filter(AuthRepository.class::isAssignableFrom)\n\t\t\t\t.filter(AbstractAuthRepositoryWithCredentials.class::isAssignableFrom)\n\t\t\t\t.map(this::instantiateClass)\n\t\t\t\t.map(initializeDataSourceAwareFunction(dataSource, log))\n\t\t\t\t.map(AuthRepository.class::cast)\n\t\t\t\t.map(this::initializeAuthRepository)\n\t\t\t\t.filter(Objects::nonNull)\n\t\t\t\t.findAny()\n\t\t\t\t.map(addUsersToRepositoryFunction(jids, password, log))\n\t\t\t\t.orElse(Result.error);\n\t}\n\n\tprotected <X extends RepositoryItem, T extends DataSource, R extends ComponentRepository<X>> Result addComponentRepositoryItems(SchemaManager.SchemaInfo schemaInfo, T dataSource, Class<T> dataSourceClass, Class<R> componentRepositoryClass, Logger log, List<X> items) {\n\t\treturn withComponentRepository(schemaInfo, dataSource, dataSourceClass, componentRepositoryClass, log, repo -> {\n\t\t\ttry {\n\t\t\t\tfor (X item : items) {\n\t\t\t\t\trepo.addItem(item);\n\t\t\t\t}\n\t\t\t\treturn Result.ok;\n\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\tlog.log(Level.WARNING, \"Error adding component items for repository\", ex);\n\t\t\t\treturn Result.error;\n\t\t\t}\n\t\t});\n\t}\n\n\tprotected <T extends DataSource> Result withUserRepository(SchemaManager.SchemaInfo schemaInfo, T dataSource, Class<T> dataSourceClass, Logger log, Function<UserRepository, Result> function) {\n\t\treturn getDataSourceAwareClassesForSchemaInfo(schemaInfo, dataSourceClass)\n\t\t\t\t.filter(UserRepository.class::isAssignableFrom)\n\t\t\t\t.map(this::instantiateClass)\n\t\t\t\t.map(initializeDataSourceAwareFunction(dataSource, log))\n\t\t\t\t.map(UserRepository.class::cast)\n\t\t\t\t.filter(Objects::nonNull)\n\t\t\t\t.findAny()\n\t\t\t\t.map(function)\n\t\t\t\t.orElse(Result.error);\n\t}\n\n\tprotected <X extends RepositoryItem, T extends DataSource, R extends ComponentRepository<X>> Result withComponentRepository(SchemaManager.SchemaInfo schemaInfo, T dataSource, Class<T> dataSourceClass, Class<R> componentRepositoryClass, Logger log, Function<R, Result> function) {\n\t\tif (UserRepoRepository.class.isAssignableFrom(componentRepositoryClass)) {\n\t\t\treturn withUserRepository(schemaInfo, dataSource, dataSourceClass, log, userRepository -> {\n\t\t\t   try {\n\t\t\t\t   R repo = componentRepositoryClass.getConstructor().newInstance();\n\t\t\t\t   if (repo instanceof UserRepoRepository) {\n\t\t\t\t\t   ((UserRepoRepository<?>) repo).setRepo(userRepository);\n\t\t\t\t   }\n\t\t\t\t   if (repo instanceof Initializable) {\n\t\t\t\t\t   ((Initializable) repo).initialize();\n\t\t\t\t   }\n\t\t\t\t   repo.reload();\n\t\t\t\t   return function.apply(repo);\n\t\t\t   } catch (Throwable ex) {\n\t\t\t\t   log.log(Level.WARNING, \"Failure while trying to initialize \" + componentRepositoryClass.getName(), ex);\n\t\t\t\t   return Result.error;\n\t\t\t   }\n\t\t\t});\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"Invalid component repository class \" + componentRepositoryClass.getCanonicalName());\n\t\t\treturn Result.error;\n\t\t}\n\t}\n\n\tprotected <DS extends DataSource> Stream<Class<DataSourceAware<DS>>> getDataSourceAwareClassesForSchemaInfo(\n\t\t\tSchemaManager.SchemaInfo schema, Class<DS> dataSourceIfc) {\n\t\treturn schema.getRepositories()\n\t\t\t\t.stream()\n\t\t\t\t.map(SchemaManager.RepoInfo::getImplementation)\n\t\t\t\t.filter(DataSourceAware.class::isAssignableFrom)\n\t\t\t\t.filter(clazz -> classMatchesClassWithParameters(clazz, DataSourceAware.class,\n\t\t\t\t                                                 new Type[]{dataSourceIfc}))\n\t\t\t\t.map(clazz -> (Class<DataSourceAware<DS>>) clazz);\n\t}\n\n\tprotected <DSIFC extends DataSource, DS extends DSIFC> Stream<DataSourceAware> getInitializedDataSourceAwareForSchemaInfo(\n\t\t\tSchemaManager.SchemaInfo schema, Class<DSIFC> dataSourceIfc, DS dataSource, Logger log) {\n\t\treturn getDataSourceAwareClassesForSchemaInfo(schema, dataSourceIfc).map(this::instantiateClass)\n\t\t\t\t.map(initializeDataSourceAwareFunction(dataSource, log));\n\t}\n\n\tprotected AuthRepository initializeAuthRepository(AuthRepository authRepository) {\n\t\tif (authRepository instanceof AbstractAuthRepositoryWithCredentials) {\n\t\t\tAbstractAuthRepositoryWithCredentials repo = (AbstractAuthRepositoryWithCredentials) authRepository;\n\t\t\tKernel kernel = new Kernel(\"SchemaLoader\");\n\t\t\tkernel.registerBean(DefaultTypesConverter.class).exportable().exec();\n\t\t\tkernel.registerBean(DSLBeanConfigurator.class).exportable().exec();\n\t\t\tkernel.getInstance(DSLBeanConfigurator.class).setProperties(new HashMap<>());\n\t\t\tkernel.registerBean(CredentialsEncoderBean.class).exec();\n\t\t\tkernel.registerBean(CredentialsDecoderBean.class).exec();\n\t\t\tCredentialsEncoderBean encoderBean = kernel.getInstance(CredentialsEncoderBean.class);\n\t\t\tCredentialsDecoderBean decoderBean = kernel.getInstance(CredentialsDecoderBean.class);\n\t\t\trepo.setCredentialsCodecs(encoderBean, decoderBean);\n\t\t}\n\t\treturn authRepository;\n\t}\n\n\tprotected <T extends DataSource> Function<DataSourceAware<T>, DataSourceAware<T>> initializeDataSourceAwareFunction(T dataSource, Logger log) {\n\t\treturn repo -> {\n\t\t\ttry {\n\t\t\t\trepo.setDataSource(dataSource);\n\t\t\t\treturn repo;\n\t\t\t} catch (Exception ex) {\n\t\t\t\tlog.log(Level.WARNING, ex.getMessage());\n\t\t\t\treturn null;\n\t\t\t}\n\t\t};\n\t}\n\n\tprotected Function<AuthRepository, Result> addUsersToRepositoryFunction(List<BareJID> jids, String pwd,\n\t                                                                        Logger log) {\n\t\treturn authRepository -> {\n\t\t\tif (authRepository == null) {\n\t\t\t\treturn Result.error;\n\t\t\t}\n\t\t\treturn jids.stream().map(jid -> {\n\t\t\t\ttry {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tauthRepository.getAccountStatus(jid);\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Skipping user \" + jid + \" creation, user already exists!\");\n\t\t\t\t\t\treturn Result.skipped;\n\t\t\t\t\t} catch (UserNotFoundException ex) {\n\t\t\t\t\t\t// we are creating user only if it does not exist..\n\t\t\t\t\t\tauthRepository.addUser(jid, pwd);\n\t\t\t\t\t\treturn Result.ok;\n\t\t\t\t\t}\n\t\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\t\tlog.log(Level.WARNING, ex.getMessage());\n\t\t\t\t\treturn Result.warning;\n\t\t\t\t}\n\t\t\t}).reduce(Result.ok, (r1, r2) -> {\n\t\t\t\tif (r1 == Result.error || r2 == Result.error) {\n\t\t\t\t\treturn Result.error;\n\t\t\t\t}\n\t\t\t\tif (r1 == Result.warning || r2 == Result.warning) {\n\t\t\t\t\treturn Result.warning;\n\t\t\t\t}\n\t\t\t\tif (r1 == Result.skipped || r2 == Result.skipped) {\n\t\t\t\t\treturn Result.skipped;\n\t\t\t\t}\n\t\t\t\treturn Result.ok;\n\t\t\t});\n\t\t};\n\t}\n\n\tprotected <T> T instantiateClass(Class<T> clazz) {\n\t\ttry {\n\t\t\treturn clazz.newInstance();\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.WARNING, \"Failed to create instance of \" + clazz.getCanonicalName());\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprotected String getType() {\n\t\treturn type;\n\t}\n\n\tprivate void setType(String type) {\n\t\tTypeInfo info = getAllSupportedTypesStream().filter(typeInfo -> type.equals(typeInfo.getName())).findFirst().get();\n\t\tif (!info.isAvailable()) {\n\t\t\tthrow new RuntimeException(\"Driver for \" + info.getLabel() + \" (\" + info.getName() + \") is missing due to missing class: \" + info.getDriverClassName());\n\t\t}\n\t\tthis.type = type;\n\t}\n\n\tpublic interface Parameters {\n\n\t\tvoid parseUri(String uri);\n\n\t\tvoid setProperties(Properties props);\n\n\t\tList<BareJID> getAdmins();\n\n\t\tString getAdminPassword();\n\n\t\tvoid setAdmins(List<BareJID> admins, String password);\n\n\t\tvoid setDbRootCredentials(String username, String password);\n\n\t\tdefault boolean isDbRootAsk() {\n\t\t\treturn false;\n\t\t}\n\n\t\tdefault void setDbRootAsk(boolean value) {\n\t\t}\n\n\t\tdefault void setSchemaDirectory(String schemaDirectory) {\n\n\t\t}\n\n\t\tdefault List<ExtServiceDiscoItem> getExternalServices() {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\n\t\tdefault void setExternalServices(List<ExtServiceDiscoItem> externalServices) {\n\t\t}\n\n\t\tLevel getLogLevel();\n\n\t\tvoid setLogLevel(Level level);\n\n\t\tboolean isForceReloadSchema();\n\n\t\tvoid setForceReloadSchema(boolean forceReloadSchema);\n\t}\n\n\tpublic static class TypeInfo implements Comparable<TypeInfo> {\n\n\t\tprivate final String name;\n\t\tprivate final String label;\n\t\tprivate final String driverClassName;\n\t\tprivate final String warning;\n\t\tprivate final int ordinal;\n\t\tfinal Comparator<TypeInfo> typeInfoComparator = Comparator.comparing(TypeInfo::getOrdinal)\n\t\t\t\t.thenComparing(TypeInfo::getName);\n\n\t\tpublic TypeInfo(String name, String label, String driverClassName) {\n\t\t\tthis(name, label, driverClassName, null);\n\t\t}\n\n\t\tpublic TypeInfo(int ordinal, String name, String label, String driverClassName) {\n\t\t\tthis(ordinal, name, label, driverClassName, null);\n\t\t}\n\n\t\tpublic TypeInfo(String name, String label, String driverClassName, String warning) {\n\t\t\tthis(Integer.MAX_VALUE, name, label, driverClassName, warning);\n\t\t}\n\t\tpublic TypeInfo(int ordinal, String name, String label, String driverClassName, String warning) {\n\t\t\tthis.ordinal = ordinal;\n\t\t\tthis.name = name;\n\t\t\tthis.label = label;\n\t\t\tthis.driverClassName = driverClassName;\n\t\t\tthis.warning = warning;\n\t\t}\n\n\t\tpublic int getOrdinal() {\n\t\t\treturn ordinal;\n\t\t}\n\n\t\tpublic String getName() {\n\t\t\treturn name;\n\t\t}\n\n\t\tpublic String getLabel() {\n\t\t\treturn label;\n\t\t}\n\n\t\tpublic String getWarning() {\n\t\t\treturn warning;\n\t\t}\n\n\t\tpublic boolean isAvailable() {\n\t\t\ttry {\n\t\t\t\tthis.getClass().getClassLoader().loadClass(driverClassName);\n\t\t\t} catch (Exception ex) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic int compareTo(TypeInfo o) {\n\t\t\treturn typeInfoComparator.compare(this, o);\n\t\t}\n\n\t\tprotected String getDriverClassName() {\n\t\t\treturn driverClassName;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn name + \" (\" + ordinal + \")\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/util/SchemaManager.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.util;\n\nimport tigase.component.DSLBeanConfigurator;\nimport tigase.component.DSLBeanConfiguratorWithBackwardCompatibility;\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.conf.ConfigBuilder;\nimport tigase.conf.ConfigHolder;\nimport tigase.conf.ConfigReader;\nimport tigase.conf.ConfigWriter;\nimport tigase.db.*;\nimport tigase.db.beans.*;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.kernel.DefaultTypesConverter;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.RegistrarBean;\nimport tigase.kernel.beans.config.AbstractBeanConfigurator;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.beans.selector.ServerBeanSelector;\nimport tigase.kernel.core.BeanConfig;\nimport tigase.kernel.core.Kernel;\nimport tigase.kernel.core.RegistrarKernel;\nimport tigase.osgi.ModulesManagerImpl;\nimport tigase.server.XMPPServer;\nimport tigase.server.extdisco.ExtServiceDiscoItem;\nimport tigase.server.monitor.MonitorRuntime;\nimport tigase.sys.TigaseRuntime;\nimport tigase.util.Version;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.util.reflection.ClassUtilBean;\nimport tigase.util.setup.BeanDefinition;\nimport tigase.util.setup.SetupHelper;\nimport tigase.util.ui.console.CommandlineParameter;\nimport tigase.util.ui.console.ParameterParser;\nimport tigase.util.ui.console.Task;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.io.*;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Function;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * Created by andrzej on 02.05.2017.\n */\npublic class SchemaManager {\n\n\tprotected static final Class[] SUPPORTED_CLASSES = {MDPoolBean.class, MDRepositoryBean.class,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tSDRepositoryBean.class};\n\tpublic static final String COMMON_SCHEMA_ID = \"common\";\n\tpublic static final String COMMON_SCHEMA_VERSION = \"0.0.3\";\n\tprivate static final Logger log = Logger.getLogger(SchemaManager.class.getCanonicalName());\n\tprivate static final Comparator<SchemaInfo> SCHEMA_INFO_COMPARATOR = (o1, o2) -> {\n\t\tif (o1.getId().equals(\"<unknown>\") || o2.getId().equals(Schema.SERVER_SCHEMA_ID)) {\n\t\t\treturn 1;\n\t\t}\n\t\tif (o2.getId().equals(\"<unknown>\") || o1.getId().equals(Schema.SERVER_SCHEMA_ID)) {\n\t\t\treturn -1;\n\t\t}\n\t\treturn o1.getId().compareTo(o2.getId());\n\t};\n\tprivate CommandlineParameter COMPONENTS = new CommandlineParameter.Builder(\"C\", \"components\").description(\n\t\t\t\"List of enabled components identifiers (+/-)\")\n\t\t\t.defaultValue(getActiveNonCoreComponentNames().sorted().collect(Collectors.joining(\",\")))\n\t\t\t.options(getNonCoreComponentNames().sorted().toArray(String[]::new))\n\t\t\t.build();\n\tprivate CommandlineParameter PROPERTY_CONFIG_FILE = new CommandlineParameter.Builder(null,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t ConfigHolder.PROPERTIES_CONFIG_FILE_KEY\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t .replace(\"--\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"\")).defaultValue(\n\t\t\tConfigHolder.PROPERTIES_CONFIG_FILE_DEF)\n\t\t\t.description(\"Path to properties configuration file\")\n\t\t\t.requireArguments(true)\n\t\t\t.build();\n\tprivate CommandlineParameter ROOT_PASSWORD = new CommandlineParameter.Builder(\"A\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  DBSchemaLoader.PARAMETERS_ENUM.ROOT_PASSWORD\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  .getName()).description(\n\t\t\t\"Database root account password used to create/remove tigase user and database\").secret().build();\n\tprivate CommandlineParameter ROOT_USERNAME = new CommandlineParameter.Builder(\"R\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  DBSchemaLoader.PARAMETERS_ENUM.ROOT_USERNAME\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  .getName()).description(\n\t\t\t\"Database root account username used to create/remove tigase user and database\").build();\n\tprivate CommandlineParameter ROOT_ASK = new CommandlineParameter.Builder(null,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t DBSchemaLoader.PARAMETERS_ENUM.ROOT_ASK.getName()).description(\n\t\t\t\"Ask for database root account credentials.\").build();\n\tprivate CommandlineParameter TDSL_CONFIG_FILE = new CommandlineParameter.Builder(null,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t ConfigHolder.TDSL_CONFIG_FILE_KEY.replace(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"--\", \"\")).defaultValue(\n\t\t\tConfigHolder.TDSL_CONFIG_FILE_DEF)\n\t\t\t.description(\"Path to DSL configuration file\")\n\t\t\t.requireArguments(true)\n\t\t\t.build();\n\n\tprivate CommandlineParameter LOG_LEVEL = new CommandlineParameter.Builder(\"L\",\n\t                                                                          DBSchemaLoader.PARAMETERS_ENUM.LOG_LEVEL.getName())\n\t\t\t.description(\"Java Logger level during loading process\")\n\t\t\t.defaultValue(DBSchemaLoader.PARAMETERS_ENUM.LOG_LEVEL.getDefaultValue())\n\t\t\t.build();\n\n\tprivate CommandlineParameter FORCE_RELOAD_SCHEMA = new CommandlineParameter.Builder(null, DBSchemaLoader.PARAMETERS_ENUM.FORCE_RELOAD_ALL_SCHEMA_FILES.getName()).description(\n\t\t\t\t\"Force reloading all schema files\")\n\t\t\t\t\t\t\t.defaultValue(DBSchemaLoader.PARAMETERS_ENUM.FORCE_RELOAD_ALL_SCHEMA_FILES.getDefaultValue())\n\t\t\t.build();\n\tprivate CommandlineParameter ADMIN_JID = new CommandlineParameter.Builder(\"J\", DBSchemaLoader.PARAMETERS_ENUM.ADMIN_JID.getName()).description(\n\t\t\t\t\"Comma separated list of administrator JID(s)\").build();\n\tprivate CommandlineParameter ADMIN_JID_PASS = new CommandlineParameter.Builder(\"N\", DBSchemaLoader.PARAMETERS_ENUM.ADMIN_JID_PASS.getName()).description(\n\t\t\t\t\"Password that will be used for the entered JID(s) - one for all configured administrators\")\n\t\t\t\t\t\t\t.secret()\n\t\t\t\t\t\t\t.build();\n\n\tprivate Boolean dbRootAsk = false;\n\tprivate String adminPass = null;\n\tprivate List<BareJID> admins = null;\n\tprivate Map<String, Object> config;\n\tprivate Level logLevel = Level.CONFIG;\n\tprivate boolean forceReloadSchema = false;\n\tprivate RootCredentialsCache rootCredentialsCache = new RootCredentialsCache();\n\tprivate List<ExtServiceDiscoItem> externalServices = new ArrayList<>();\n\n\tprivate static Stream<String> getActiveNonCoreComponentNames() {\n\t\treturn SetupHelper.getAvailableComponents()\n\t\t\t\t.stream()\n\t\t\t\t.filter(BeanDefinition::isActive)\n\t\t\t\t.filter(def -> !def.isCoreComponent())\n\t\t\t\t.map(BeanDefinition::getName);\n\t}\n\n\tprivate static Stream<String> getNonCoreComponentNames() {\n\t\treturn SetupHelper.getAvailableComponents()\n\t\t\t\t.stream()\n\t\t\t\t.filter(def -> !def.isCoreComponent())\n\t\t\t\t.map(BeanDefinition::getName);\n\t}\n\n\tpublic static Optional<String> getProperty(Properties props, CommandlineParameter parameter) {\n\t\tOptional<String> value = Optional.ofNullable(props.getProperty(parameter.getFullName(false).get()));\n\t\tif (!value.isPresent()) {\n\t\t\treturn parameter.getDefaultValue();\n\t\t}\n\t\treturn value;\n\t}\n\n\tpublic static <T> Optional<T> getProperty(Properties props, CommandlineParameter parameter,\n\t\t\t\t\t\t\t\t\t\t\t  Function<String, T> converter) {\n\t\tOptional<String> value = getProperty(props, parameter);\n\t\tif (!value.isPresent()) {\n\t\t\treturn Optional.empty();\n\t\t}\n\t\tT result = converter.apply(value.get());\n\t\treturn Optional.ofNullable(result);\n\t}\n\n\tpublic static List<Class<?>> getRepositoryClasses() {\n\t\treturn ClassUtilBean.getInstance()\n\t\t\t\t.getAllClasses()\n\t\t\t\t.stream()\n\t\t\t\t.filter(clazz -> Arrays.stream(SUPPORTED_CLASSES)\n\t\t\t\t\t\t.anyMatch(supClazz -> supClazz.isAssignableFrom(clazz)))\n\t\t\t\t.filter(clazz -> {\n\t\t\t\t\tBean bean = clazz.getAnnotation(Bean.class);\n\t\t\t\t\treturn bean != null && (bean.parent() != Object.class || bean.parents().length > 0);\n\t\t\t\t})\n\t\t\t\t.filter(clazz -> !DataSourceBean.class.isAssignableFrom(clazz))\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\tpublic static void main(String args[]) throws IOException, ConfigReader.ConfigException {\n\t\ttry {\n\t\t\tSchemaManager schemaManager = new SchemaManager();\n\t\t\tschemaManager.execute(args);\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.SEVERE, \"Error while loading schema\", ex);\n\t\t} finally {\n\t\t\tSystem.exit(0);\n\t\t}\n\t}\n\n\tpublic SchemaManager() {\n\t\tList<Class<?>> repositoryClasses = getRepositoryClasses();\n\t\tlog.log(Level.FINE, \"found following data source related classes: {0}\", repositoryClasses);\n\t}\n\n\tpublic void execute(String args[]) throws Exception {\n\t\tString scriptName = System.getProperty(\"scriptName\");\n\t\tParameterParser parser = new ParameterParser(true);\n\n\t\tparser.setTasks(new Task[]{new Task.Builder().name(\"upgrade-schema\")\n\t\t\t\t\t\t\t\t\t\t   .description(\n\t\t\t\t\t\t\t\t\t\t\t\t   \"Upgrade schema of databases specified in your config file - it's not possible to specify parameters\")\n\t\t\t\t\t\t\t\t\t\t   .additionalParameterSupplier(this::upgradeSchemaParametersSupplier)\n\t\t\t\t\t\t\t\t\t\t   .function(this::upgradeSchema).build(),\n\t\t\t\t\t\t\t\t   new Task.Builder().name(\"install-schema\")\n\t\t\t\t\t\t\t\t\t\t   .description(\n\t\t\t\t\t\t\t\t\t\t\t\t   \"Install schema to database - it requires specifying database parameters where schema will be installed (config file will be ignored)\")\n\t\t\t\t\t\t\t\t\t\t   .additionalParameterSupplier(this::installSchemaParametersSupplier)\n\t\t\t\t\t\t\t\t\t\t   .function(this::installSchema).build(),\n\t\t\t\t\t\t\t\t   new Task.Builder().name(\"destroy-schema\")\n\t\t\t\t\t\t\t\t\t\t   .description(\"Destroy database and schemas (DANGEROUS)\")\n\t\t\t\t\t\t\t\t\t\t   .additionalParameterSupplier(this::destroySchemaParametersSupplier)\n\t\t\t\t\t\t\t\t\t\t   .function(this::destroySchema).build()\n\n\t\t});\n\n\t\tProperties props = null;\n\t\ttry {\n\t\t\tprops = parser.parseArgs(args);\n\t\t} catch (IllegalArgumentException ex) {\n\t\t\t// IllegalArgumentException is thrown if any of required parameters is missing\n\t\t\t// We can ignore it and just display help for this command.\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, ex.getMessage(), ex);\n\t\t\t}\n\t\t}\n\t\tOptional<Task> task = parser.getTask();\n\t\tif (props != null && task.isPresent()) {\n\t\t\ttask.get().execute(props);\n\t\t} else {\n\t\t\tString executionCommand = null;\n\t\t\tif (scriptName != null) {\n\t\t\t\texecutionCommand = \"$ \" + scriptName + \" [task] [params-file.conf] [options]\" + \"\\n\\t\\t\" +\n\t\t\t\t\t\t\"if the option defines default then <value> is optional\";\n\t\t\t}\n\n\t\t\tSystem.out.println(parser.getHelp(executionCommand));\n\t\t}\n\t}\n\n\tpublic void destroySchema(Properties props) throws IOException, ConfigReader.ConfigException {\n\t\tfixShutdownThreadIssue();\n\t\tString type = props.getProperty(DBSchemaLoader.PARAMETERS_ENUM.DATABASE_TYPE.getName());\n\t\tMap<String, Object> config = null;\n\t\tOptional<String[]> conversionMessages = Optional.empty();\n\t\tif (type != null) {\n\t\t\tSchemaLoader loader = SchemaLoader.newInstance(type);\n\t\t\tSchemaLoader.Parameters params = loader.createParameters();\n\t\t\tparams.setProperties(props);\n\t\t\tloader.init(params, Optional.ofNullable(rootCredentialsCache));\n\t\t\tString dbUri = loader.getDBUri();\n\n\t\t\tString vhost = DNSResolverFactory.getInstance().getDefaultHost();\n\t\t\tConfigBuilder configBuilder = SetupHelper.generateConfig(ConfigTypeEnum.DefaultMode, dbUri, false, false,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Optional.empty(), Optional.empty(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Optional.empty(), vhost, Optional.empty(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Optional.empty());\n\n\t\t\tconfig = configBuilder.build();\n\t\t} else {\n\t\t\tConfigHolder holder = new ConfigHolder();\n\t\t\tconversionMessages = holder.loadConfiguration(\n\t\t\t\t\tnew String[]{ConfigHolder.PROPERTIES_CONFIG_FILE_KEY, PROPERTY_CONFIG_FILE.getValue().get(),\n\t\t\t\t\t\t\t\t ConfigHolder.TDSL_CONFIG_FILE_KEY, TDSL_CONFIG_FILE.getValue().get()});\n\n\t\t\tconfig = holder.getProperties();\n\t\t}\n\n\t\tOptional<String> rootUser = getProperty(props, ROOT_USERNAME);\n\t\tOptional<String> rootPass = getProperty(props, ROOT_PASSWORD);\n\n\t\tsetConfig(config);\n\t\tif (rootUser.isPresent() && rootPass.isPresent()) {\n\t\t\tsetDbRootCredentials(rootUser.get(), rootPass.get());\n\t\t}\n\n\t\tgetProperty(props, LOG_LEVEL, Level::parse).ifPresent(result -> logLevel = result);\n\n\t\tMap<String, DataSourceInfo> result = getDataSources(config);\n\t\tlog.log(Level.INFO, \"found \" + result.size() + \" data sources to destroy...\");\n\t\tMap<DataSourceInfo, List<SchemaManager.ResultEntry>> results = destroySchemas(result.values());\n\t\tlog.log(Level.INFO, \"data sources  destruction finished!\");\n\t\tList<String> output = prepareOutput(\"Data source destruction finished\", results, conversionMessages);\n\n\t\tfinal int exitCode = isErrorPresent(results) ? 1 : 0;\n\t\tfinal String[] message = output.toArray(new String[output.size()]);\n\t\tTigaseRuntime.getTigaseRuntime().shutdownTigase(message, exitCode);\n\t}\n\n\tpublic void installSchema(Properties props) throws IOException, ConfigReader.ConfigException {\n\t\tString type = props.getProperty(DBSchemaLoader.PARAMETERS_ENUM.DATABASE_TYPE.getName());\n\t\tSchemaLoader loader = SchemaLoader.newInstance(type);\n\t\tSchemaLoader.Parameters params = loader.createParameters();\n\t\tparams.setProperties(props);\n\t\tloader.init(params, Optional.ofNullable(rootCredentialsCache));\n\t\tString dbUri = loader.getDBUri();\n\n\t\t// split list of components and group them according to enable/disable sign (and none translates to \"+\"\n\t\tfinal Function<String, List<String>> stringToListFunction = (listStr) -> Arrays.asList(listStr.split(\",\"));\n\n\t\tfinal Function<String, String> signRemovingFunction = (v) -> (v.startsWith(\"-\") || v.startsWith(\"+\"))\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t ? v.substring(1)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t : v;\n\t\tMap<String, Set<String>> changes = getProperty(props, COMPONENTS, stringToListFunction).orElse(\n\t\t\t\tCollections.emptyList())\n\t\t\t\t.stream()\n\t\t\t\t.collect(Collectors.groupingBy((v) -> v.startsWith(\"-\") ? \"-\" : \"+\",\n\t\t\t\t\t\t\t\t\t\t\t   Collectors.mapping(signRemovingFunction, Collectors.toSet())));\n\n\t\tSet<String> components = getActiveNonCoreComponentNames().collect(Collectors.toSet());\n\n\t\tchanges.forEach((k, v) -> {\n\t\t\tswitch (k) {\n\t\t\t\tcase \"+\":\n\t\t\t\t\tcomponents.addAll(v);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"-\":\n\t\t\t\t\tcomponents.removeAll(v);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t});\n\n\t\tadmins = params.getAdmins();\n\t\tadminPass = params.getAdminPassword();\n\t\tdbRootAsk = params.isDbRootAsk();\n\t\tlogLevel = params.getLogLevel();\n\n\t\tString vhost = DNSResolverFactory.getInstance().getDefaultHost();\n\t\tConfigBuilder configBuilder = SetupHelper.generateConfig(ConfigTypeEnum.DefaultMode, dbUri, false, false,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Optional.ofNullable(components),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Optional.ofNullable(changes.get(\"+\")),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Optional.empty(), vhost, Optional.of(params.getAdmins().toArray(new BareJID[params.getAdmins().size()])),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Optional.empty());\n\n\n\t\tMap<String, Object> config = configBuilder.build();\n\t\tMap<SchemaManager.DataSourceInfo, List<SchemaManager.ResultEntry>> results = loadSchemas(config, props);\n\n\t\tList<String> output = prepareOutput(\"Schema installation finished\", results, Optional.empty());\n\t\toutput.add(\"\");\n\t\toutput.add(\"Example \" + ConfigHolder.TDSL_CONFIG_FILE_DEF + \" configuration file:\");\n\t\toutput.add(\"\");\n\t\ttry (StringWriter writer = new StringWriter()) {\n\t\t\tnew ConfigWriter().write(writer, config);\n\t\t\toutput.addAll(Arrays.stream(writer.toString().split(\"\\n\")).collect(Collectors.toList()));\n\t\t}\n\t\tfinal int exitCode = isErrorPresent(results) ? 1 : 0;\n\t\tfinal String[] message = output.toArray(new String[output.size()]);\n\n\t\tTigaseRuntime.getTigaseRuntime().shutdownTigase(message, exitCode);\n\t}\n\n\tpublic void upgradeSchema(Properties props) throws IOException, ConfigReader.ConfigException {\n\t\tConfigHolder holder = new ConfigHolder();\n\t\tOptional<String[]> conversionMessages = holder.loadConfiguration(\n\t\t\t\tnew String[]{ConfigHolder.PROPERTIES_CONFIG_FILE_KEY, PROPERTY_CONFIG_FILE.getValue().get(),\n\t\t\t\t\t\t\t ConfigHolder.TDSL_CONFIG_FILE_KEY, TDSL_CONFIG_FILE.getValue().get()});\n\n\t\tMap<String, Object> config = holder.getProperties();\n\n\t\tadmins = getProperty(props, ADMIN_JID).map(\n\t\t\t\t\t\tstr -> Arrays.stream(str.split(\",\")).map(BareJID::bareJIDInstanceNS).collect(Collectors.toList()))\n\t\t\t\t.orElse(null);\n\t\tadminPass = getProperty(props, ADMIN_JID_PASS).orElse(null);\n\t\tdbRootAsk = getProperty(props, ROOT_ASK, Boolean::parseBoolean).orElse(false);\n\t\tgetProperty(props, LOG_LEVEL, Level::parse).ifPresent(result -> logLevel = result);\n\t\tgetProperty(props, FORCE_RELOAD_SCHEMA, Boolean::parseBoolean).ifPresent(result -> forceReloadSchema = result);\n\n\t\tMap<SchemaManager.DataSourceInfo, List<SchemaManager.ResultEntry>> results = loadSchemas(config, props);\n\t\tList<String> output = prepareOutput(\"Schema upgrade finished\", results, conversionMessages);\n\n\t\tfinal int exitCode = isErrorPresent(results) ? 1 : 0;\n\t\tfinal String[] message = output.toArray(new String[output.size()]);\n\n\t\tTigaseRuntime.getTigaseRuntime().shutdownTigase(message, exitCode);\n\n\t}\n\n\tpublic void readConfig(File file) throws IOException, ConfigReader.ConfigException {\n\t\tconfig = new ConfigReader().read(file);\n\t}\n\n\tpublic void readConfig(String configString) throws IOException, ConfigReader.ConfigException {\n\t\ttry (StringReader reader = new StringReader(configString)) {\n\t\t\treadConfig(reader);\n\t\t}\n\t}\n\n\tpublic void readConfig(Reader reader) throws IOException, ConfigReader.ConfigException {\n\t\tconfig = new ConfigReader().read(reader);\n\t}\n\n\tpublic void setAdmins(List<BareJID> admins, String adminPass) {\n\t\tthis.admins = admins;\n\t\tthis.adminPass = adminPass;\n\t}\n\n\tpublic void setConfig(Map<String, Object> config) {\n\t\tthis.config = config;\n\t}\n\n\tpublic void setDbRootCredentials(String user, String pass) {\n\t\trootCredentialsCache.set(null, new RootCredentials(user, pass));\n\t}\n\n\tpublic void setExternalServices(List<ExtServiceDiscoItem> externalServices) {\n\t\tthis.externalServices = externalServices;\n\t}\n\n\tpublic static Map<DataSourceInfo, List<SchemaInfo>> getDefaultDataSourceAndSchemas(String dbUri) {\n\t\treturn getDefaultDataSourceAndSchemas(dbUri, getActiveNonCoreComponentNames().collect(Collectors.toSet()));\n\t}\n\n\tprivate static Map<DataSourceInfo, List<SchemaInfo>> getDefaultDataSourceAndSchemas(String dbUri, Set<String> components) {\n\t\tConfigBuilder configBuilder = SetupHelper.generateConfig(ConfigTypeEnum.DefaultMode, dbUri, false, false,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Optional.ofNullable(components),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Optional.empty(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Optional.empty(), \"example.com\", Optional.empty(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Optional.empty());\n\n\n\t\tMap<String, Object> config = configBuilder.build();\n\t\treturn getDataSourcesAndSchemas(config);\n\t}\n\n\tpublic static Optional<SchemaInfo> getDefaultSchemaFor(String dbUri, String schemaId, Set<String> components) {\n\t\treturn getDefaultDataSourceAndSchemas(dbUri, components).values().stream().flatMap(value -> value.stream()).filter(schema -> schemaId.equals(schema.getId())).findFirst();\n\t}\n\n\tpublic static Map<DataSourceInfo, List<SchemaInfo>> getDataSourcesAndSchemas(Map<String, Object> config) {\n\t\tKernel kernel = prepareKernel(config);\n\t\tList<BeanConfig> repoBeans = getRepositoryBeans(kernel, getRepositoryClasses(), config);\n\t\tList<RepoInfo> repositories = getRepositories(kernel, repoBeans, config);\n\t\tMap<DataSourceInfo, List<RepoInfo>> repositoriesByDataSource = repositories.stream()\n\t\t\t\t.collect(Collectors.groupingBy(RepoInfo::getDataSource, Collectors.toList()));\n\n\t\treturn collectSchemasByDataSource(repositoriesByDataSource);\n\t}\n\n\tpublic Map<DataSourceInfo, List<ResultEntry>> destroySchemas(Collection<DataSourceInfo> dataSources) {\n\t\treturn dataSources.stream()\n\t\t\t\t.map(e -> new Pair<>(e, e.automaticSchemaManagement()\n\t\t\t\t\t\t\t\t\t\t? destroySchemas(e)\n\t\t\t\t\t\t\t\t\t\t: Collections.singletonList(\n\t\t\t\t\t\t\t\t\t\t\t\tnew ResultEntry(\"Destroying data source\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchemaLoader.Result.skipped,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"Automatic schema management is disabled for this data source!\"))))\n\t\t\t\t.collect(Collectors.toMap(Pair::getKey, Pair::getValue));\n\t}\n\n\tpublic List<ResultEntry> destroySchemas(DataSource ds) {\n\t\treturn executeWithSchemaLoader(ds, (schemaLoader, handler) -> {\n\t\t\tList<ResultEntry> results = new ArrayList<>();\n\t\t\tlog.log(Level.FINEST, \"removing database for data source \" + ds);\n\t\t\tresults.add(new ResultEntry(\"Destroying data source\", schemaLoader.destroyDataSource(), handler));\n\t\t\treturn results;\n\t\t});\n\t}\n\n\tpublic Map<DataSourceInfo, List<ResultEntry>> loadSchemas() {\n\t\tMap<DataSourceInfo, List<SchemaInfo>> dataSourceSchemas = getDataSourcesAndSchemas(config);\n\t\tMap<DataSourceInfo, List<ResultEntry>> upgradeSupportResults = dataSourceSchemas.entrySet()\n\t\t\t\t.stream()\n\t\t\t\t.filter(e -> e.getKey().automaticSchemaManagement())\n\t\t\t\t.map(e -> new Pair<DataSourceInfo, List<ResultEntry>>(e.getKey(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  checkUpgradeSupport(e.getKey(), e.getValue())))\n\t\t\t\t.collect(Collectors.toMap(Pair::getKey, Pair::getValue));\n\t\tif (upgradeSupportResults.values()\n\t\t\t\t.stream()\n\t\t\t\t.flatMap(List::stream)\n\t\t\t\t.filter(re -> re.result != SchemaLoader.Result.ok)\n\t\t\t\t.findAny()\n\t\t\t\t.isPresent()) {\n\t\t\t// we need to stop here!!\n\t\t\treturn upgradeSupportResults.entrySet()\n\t\t\t\t\t.stream()\n\t\t\t\t\t.map(e -> new Pair<DataSourceInfo, List<ResultEntry>>(e.getKey(), e.getValue().stream().map(re -> {\n\t\t\t\t\t\tif (re.result == SchemaLoader.Result.ok && !\"Checking connection to database\".equals(re.name)) {\n\t\t\t\t\t\t\treturn new ResultEntry(re.name, SchemaLoader.Result.skipped, \"Skipped due to other errors\");\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treturn re;\n\t\t\t\t\t\t}\n\t\t\t\t\t}).collect(Collectors.toList())))\n\t\t\t\t\t.collect(Collectors.toMap(Pair::getKey, Pair::getValue));\n\t\t}\n\t\treturn dataSourceSchemas.entrySet()\n\t\t\t\t.stream()\n\t\t\t\t.map(e -> new Pair<DataSourceInfo, List<ResultEntry>>(e.getKey(), e.getKey().automaticSchemaManagement()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  ? loadSchemas(e.getKey(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\te.getValue())\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  : e.getValue()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  .stream()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  .map(schema -> new ResultEntry(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"Loading schema: \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  schema.getName() +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \", version: \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  schema.getVersion()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  .map(Version::toString)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  .orElse(\"0.0.0\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  SchemaLoader.Result.skipped,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"Automatic schema management is disabled for this data source!\"))\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  .collect(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  Collectors.toList())))\n\t\t\t\t.collect(Collectors.toMap(Pair::getKey, Pair::getValue));\n\t}\n\n\tpublic List<ResultEntry> checkUpgradeSupport(DataSource ds, List<SchemaInfo> schemas) {\n\t\tfinal List<SchemaInfo> validSchemas = schemas.stream().filter(SchemaInfo::isValid).collect(Collectors.toList());\n\n\t\tif (validSchemas.isEmpty()) {\n\t\t\tlog.log(Level.FINER, \"no known schemas for data source \" + ds + \", skipping schema loading...\");\n\t\t\treturn Collections.emptyList();\n\t\t}\n\n\t\treturn executeWithSchemaLoader(ds, (schemaLoader, handler) -> {\n\t\t\tif (schemaLoader.validateDBExists() == SchemaLoader.Result.ok) {\n\t\t\t\treturn validSchemas.stream().map(schema -> {\n\t\t\t\t\tOptional<Version> versionFromDB = schemaLoader.getComponentVersionFromDb(schema.getId());\n\t\t\t\t\tOptional<Version> minimalSchemaVersionForUpgrade = schemaLoader.getMinimalRequiredComponentVersionForUpgrade(\n\t\t\t\t\t\t\tschema);\n\t\t\t\t\tif (!versionFromDB.isPresent() ||\n\t\t\t\t\t\t\tminimalSchemaVersionForUpgrade.map(version -> versionFromDB.get().compareTo(version) >= 0)\n\t\t\t\t\t\t\t\t\t.orElse(false)) {\n\t\t\t\t\t\treturn new ResultEntry(schema.getName(), SchemaLoader.Result.ok, \"Upgrade supported\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn new ResultEntry(schema.getName(), SchemaLoader.Result.error,\n\t\t\t\t\t\t\t\t\t\t\t   minimalSchemaVersionForUpgrade.map(\n\t\t\t\t\t\t\t\t\t\t\t\t\t   version -> \"Upgrade supported only from version \" + version + \" or higher\")\n\t\t\t\t\t\t\t\t\t\t\t\t\t   .orElse(\"Upgrade not supported!\"));\n\t\t\t\t\t}\n\t\t\t\t}).collect(Collectors.toList());\n\t\t\t} else {\n\t\t\t\treturn validSchemas.stream()\n\t\t\t\t\t\t.map(schema -> new ResultEntry(schema.getName(), SchemaLoader.Result.ok,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   \"Database do not exist\"))\n\t\t\t\t\t\t.collect(Collectors.toList());\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic List<ResultEntry> loadSchemas(DataSource ds, List<SchemaInfo> schemas) {\n\t\tfinal List<SchemaInfo> validSchemas = schemas.stream().filter(SchemaInfo::isValid).collect(Collectors.toList());\n\n\t\tif (validSchemas.isEmpty()) {\n\t\t\tlog.log(Level.FINER, \"no known schemas for data source \" + ds + \", skipping schema loading...\");\n\t\t\treturn Collections.emptyList();\n\t\t}\n\n\t\treturn executeWithSchemaLoader(ds, (schemaLoader, handler) -> {\n\t\t\tList<ResultEntry> results = new ArrayList<>();\n\t\t\tresults.add(new ResultEntry(\"Checking if database exists\", schemaLoader.validateDBExists(), handler));\n\t\t\tlog.log(Level.FINER, \"loading schemas for data source \" + ds);\n\t\t\tschemas.sort(SCHEMA_INFO_COMPARATOR);\n\n\t\t\tresults.add(new ResultEntry(\"Loading Common Schema Files\", schemaLoader.loadCommonSchema(), handler));\n\n\t\t\tfor (SchemaInfo schema : validSchemas) {\n\t\t\t\t// we filter validSchemas at the beginning of the method to only include Valid Schemas\n\t\t\t\tVersion version = schema.getVersion().get();\n\n\t\t\t\tfinal Optional<Version> componentVersionFromDb = schemaLoader.getComponentVersionFromDb(schema.getId());\n\n\t\t\t\tString dbVersionMsg = \" (database version: \" +\n\t\t\t\t\t\t(componentVersionFromDb.isPresent() ? componentVersionFromDb.get() : \"none\") + \")\";\n\n\t\t\t\tResultEntry schemaLoadResultEntry;\n\t\t\t\tif (!Version.TYPE.FINAL.equals(version.getVersionType())\n\t\t\t\t\t\t|| (!componentVersionFromDb.isPresent()\n\t\t\t\t\t\t|| (version.getBaseVersion().equals(componentVersionFromDb.get().getBaseVersion()) &&\n\t\t\t\t\t\t\t\t!Version.TYPE.FINAL.equals(componentVersionFromDb.get().getVersionType()))\n\t\t\t\t\t\t|| !version.getBaseVersion().equals(componentVersionFromDb.get().getBaseVersion()))) {\n\t\t\t\t\tlog.log(Level.FINER, \"loading schema with id ='\" + schema + \"'\");\n\t\t\t\t\tschemaLoadResultEntry = new ResultEntry(\n\t\t\t\t\t\t\t\"Loading schema: \" + schema.getName() + \", version: \" + version +\n\t\t\t\t\t\t\t\t\tdbVersionMsg,\n\t\t\t\t\t\t\tschemaLoader.loadSchema(schema, version.toString()), handler);\n\t\t\t\t} else {\n\t\t\t\t\tlog.log(Level.FINER, \"Skipped loading schema with id ='\" + schema + \"'\");\n\t\t\t\t\tlog.log(Level.INFO, \"Required schema is already loaded in correct version\");\n\t\t\t\t\tschemaLoadResultEntry = new ResultEntry(\n\t\t\t\t\t\t\t\"Skipping schema: \" + schema.getName() + \", version: \" + version + dbVersionMsg,\n\t\t\t\t\t\t\tSchemaLoader.Result.skipped, handler);\n\t\t\t\t}\n\t\t\t\tresults.add(schemaLoadResultEntry);\n\t\t\t}\n\n\t\t\tschemas.stream()\n\t\t\t\t\t.filter(schema -> Schema.SERVER_SCHEMA_ID.equals(schema.getId()) || (Schema.SERVER_SCHEMA_ID + \"-user\").equals(schema.getId()))\n\t\t\t\t\t.findAny()\n\t\t\t\t\t.ifPresent(schemaInfo -> {\n\t\t\t\t\t\tresults.add(\n\t\t\t\t\t\t\t\tnew ResultEntry(\"Adding XMPP admin accounts\", schemaLoader.addXmppAdminAccount(schemaInfo),\n\t\t\t\t\t\t\t\t\t\t\t\thandler));\n\t\t\t\t\t\tresults.add(\n\t\t\t\t\t\t\t\tnew ResultEntry(\"Adding external service discovery items\", schemaLoader.addExternalServices(schemaInfo), handler));\n\t\t\t\t\t});\n\n\t\t\tresults.add(new ResultEntry(\"Post installation action\", schemaLoader.postInstallation(), handler));\n\t\t\treturn results;\n\t\t});\n\t}\n\n\tprivate List<CommandlineParameter> destroySchemaParametersSupplier() {\n\t\tList<CommandlineParameter> options = new ArrayList<>();\n\t\toptions.addAll(Arrays.asList(ROOT_USERNAME, ROOT_PASSWORD, ROOT_ASK, TDSL_CONFIG_FILE, PROPERTY_CONFIG_FILE, LOG_LEVEL));\n\t\toptions.addAll(SchemaLoader.getMainCommandlineParameters(true));\n\t\treturn options;\n\t}\n\n\tprivate List<CommandlineParameter> installSchemaParametersSupplier() {\n\n\t\tList<CommandlineParameter> options = new ArrayList<>();\n\t\toptions.add(COMPONENTS);\n\t\toptions.add(LOG_LEVEL);\n\t\toptions.addAll(SchemaLoader.getMainCommandlineParameters(false));\n\t\treturn options;\n\t}\n\n\tprivate List<CommandlineParameter> upgradeSchemaParametersSupplier() {\n\t\treturn Arrays.asList(ROOT_USERNAME, ROOT_PASSWORD, ROOT_ASK, TDSL_CONFIG_FILE, PROPERTY_CONFIG_FILE, LOG_LEVEL, FORCE_RELOAD_SCHEMA, ADMIN_JID, ADMIN_JID_PASS);\n\t}\n\n\tprivate boolean isErrorPresent(Map<DataSourceInfo, List<ResultEntry>> results) {\n\t\treturn results.values()\n\t\t\t\t.stream()\n\t\t\t\t.flatMap(Collection::stream)\n\t\t\t\t.map(entry -> entry.result)\n\t\t\t\t.anyMatch(r -> r == SchemaLoader.Result.error);\n\t}\n\n\tprivate Map<SchemaManager.DataSourceInfo, List<SchemaManager.ResultEntry>> loadSchemas(Map<String, Object> config,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   Properties props)\n\t\t\tthrows IOException, ConfigReader.ConfigException {\n\t\tOptional<String> rootUser = getProperty(props, ROOT_USERNAME);\n\t\tOptional<String> rootPass = getProperty(props, ROOT_PASSWORD);\n\n\t\tsetConfig(config);\n\t\tif (rootUser.isPresent() && rootPass.isPresent()) {\n\t\t\tsetDbRootCredentials(rootUser.get(), rootPass.get());\n\t\t}\n\n\t\tgetProperty(props, LOG_LEVEL, Level::parse).ifPresent(result -> logLevel = result);\n\n\t\tlog.log(Level.INFO, \"beginning loading schema files...\");\n\t\tMap<SchemaManager.DataSourceInfo, List<SchemaManager.ResultEntry>> results = loadSchemas();\n\t\tlog.log(Level.INFO, \"schema loading finished!\");\n\t\treturn results;\n\t}\n\n\tprivate List<String> prepareOutput(String title, Map<DataSourceInfo, List<SchemaManager.ResultEntry>> results,\n\t\t\t\t\t\t\t\t\t   Optional<String[]> conversionMessages) {\n\t\tList<String> output = new ArrayList<>(Arrays.asList(\"\\t\" + title));\n\t\tconversionMessages.ifPresent(msgs -> {\n\t\t\toutput.add(\"\");\n\t\t\toutput.addAll(Arrays.asList(msgs));\n\t\t});\n\t\tresults.forEach((k, v) -> {\n\t\t\toutput.add(\"\");\n\t\t\toutput.add(\"Data source: \" + k.getName() + \" with uri \" + JDBCPasswordObfuscator.obfuscatePassword(k.getResourceUri()));\n\t\t\tv.forEach(r -> {\n\t\t\t\toutput.add(\"\\t\" + r.name + \"\\t\" + r.result);\n\t\t\t\tif (r.result != SchemaLoader.Result.ok && r.message != null) {\n\t\t\t\t\tString[] lines = r.message.split(\"\\n\");\n\t\t\t\t\tfor (int i = 0; i < lines.length; i++) {\n\t\t\t\t\t\tif (i == 0) {\n\t\t\t\t\t\t\toutput.add(\"\\t\\tMessage: \" + lines[0]);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\toutput.add(\"\\t\\t         \" + lines[i]);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t\treturn output;\n\t}\n\n\tprivate List<ResultEntry> executeWithSchemaLoader(DataSource ds, SchemaLoaderExecutor function) {\n\t\tSchemaLoader schemaLoader = SchemaLoader.newInstanceForURI(ds.getResourceUri());\n\t\tList<ResultEntry> results = new ArrayList<>();\n\n\t\tLogger logger = java.util.logging.Logger.getLogger(schemaLoader.getClass().getCanonicalName());\n\t\tSchemaManagerLogHandler handler = Arrays.stream(logger.getHandlers())\n\t\t\t\t.filter(h -> h instanceof SchemaManagerLogHandler)\n\t\t\t\t.map(h -> (SchemaManagerLogHandler) h)\n\t\t\t\t.findAny()\n\t\t\t\t.orElseGet(() -> {\n\t\t\t\t\tSchemaManagerLogHandler handler1 = new SchemaManagerLogHandler();\n\t\t\t\t\tlogger.addHandler(handler1);\n\t\t\t\t\treturn handler1;\n\t\t\t\t});\n\t\thandler.setLevel(java.util.logging.Level.FINEST);\n\t\tlogger.setLevel(java.util.logging.Level.FINEST);\n\n\t\tSchemaLoader.Parameters params = schemaLoader.createParameters();\n\t\tparams.parseUri(ds.getResourceUri());\n\t\tparams.setAdmins(admins, adminPass);\n\t\tparams.setLogLevel(logLevel);\n\t\tparams.setForceReloadSchema(forceReloadSchema);\n\t\tparams.setDbRootAsk(dbRootAsk);\n\t\tparams.setExternalServices(externalServices);\n\t\tschemaLoader.init(params, Optional.ofNullable(rootCredentialsCache));\n\n\t\tresults.add(new ResultEntry(\"Checking connection to database\", schemaLoader.validateDBConnection(), handler));\n\n\t\tresults.addAll(function.execute(schemaLoader, handler));\n\n\t\tschemaLoader.shutdown();\n\t\treturn results;\n\t}\n\n\tprivate static Map<String, DataSourceInfo> getDataSources(Map<String, Object> config) {\n\t\tfinal Optional<Map<String,Object>> dataSources = Optional.ofNullable(config.get(\"dataSource\")).map(obj -> (Map<String,Object>)obj);\n\t\tboolean automaticSchemaManagement = dataSources\n\t\t\t\t.map(map -> map.getOrDefault(\"automaticSchemaManagement\", map.get(\"schema-management\")))\n\t\t\t\t.map(Boolean.class::cast)\n\t\t\t\t.orElseGet(() -> (Boolean) config.getOrDefault(\"automaticSchemaManagement\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t config.getOrDefault(\"schema-management\", true)));\n\t\treturn dataSources.isEmpty()\n\t\t\t   ? Collections.emptyMap()\n\t\t\t   : dataSources.get()\n\t\t\t\t\t   .values()\n\t\t\t\t\t   .stream()\n\t\t\t\t\t   .filter(v -> v instanceof AbstractBeanConfigurator.BeanDefinition)\n\t\t\t\t\t   .map(v -> (AbstractBeanConfigurator.BeanDefinition) v)\n\t\t\t\t\t   .filter(AbstractBeanConfigurator.BeanDefinition::isActive)\n\t\t\t\t\t   .map(v -> SchemaManager.createDataSourceInfo(v, automaticSchemaManagement))\n\t\t\t\t\t   .collect(Collectors.toMap(DataSourceInfo::getName, Function.identity()));\n\t}\n\n\tprivate static DataSourceInfo createDataSourceInfo(AbstractBeanConfigurator.BeanDefinition def, boolean automaticSchemaManagement) {\n\t\tObject v = def.getOrDefault(\"uri\", def.get(\"repo-uri\"));\n\t\tDataSourceInfo info = new DataSourceInfo(def.getBeanName(),\n\t\t\t\t\t\t\t\t  v instanceof ConfigReader.Variable ? ((ConfigReader.Variable) v).calculateValue()\n\t\t\t\t\t\t\t\t\t\t  .toString() : v.toString());\n\t\tboolean schemaManagement = (Boolean) def.getOrDefault(\"automaticSchemaManagement\", def.getOrDefault(\"schema-management\", automaticSchemaManagement));\n\t\tinfo.setAutomaticSchemaManagement(schemaManagement);\n\t\treturn info;\n\t}\n\n\tpublic static List<RepoInfo> getRepositories(Kernel kernel, List<BeanConfig> repoBeans, Map<String, Object> config) {\n\t\tDSLBeanConfigurator configurator = kernel.getInstance(DSLBeanConfigurator.class);\n\t\tMap<String, DataSourceInfo> dataSources = getDataSources(config);\n\t\treturn dataSources.isEmpty()\n\t\t\t   ? Collections.emptyList()\n\t\t\t   : repoBeans.stream().filter(Objects::nonNull).flatMap(bc -> {\n\t\t\t\t   try {\n\t\t\t\tif (SDRepositoryBean.class.isAssignableFrom(bc.getClazz())) {\n\t\t\t\t\tString dataSourceName = getDataSourceNameOr(configurator, bc, \"default\");\n\t\t\t\t\tDataSourceInfo dataSource = dataSources.get(dataSourceName);\n\t\t\t\t\tClass<?> implementation = getRepositoryImplementation(configurator, dataSource, bc, null);\n\t\t\t\t\treturn Stream.of(new RepoInfo(bc, dataSource, implementation));\n\t\t\t\t} else {\n\t\t\t\t\tMDRepositoryBean mdRepositoryBean;\n\t\t\t\t\tMDRepositoryBean.SelectorType selectorType = Optional.ofNullable(configurator.getConfiguration(bc))\n\t\t\t\t\t\t\t.map(tmpConfig -> tmpConfig.get(\"dataSourceSelection\"))\n\t\t\t\t\t\t\t.map(val -> {\n\t\t\t\t\t\t\t\tif (val instanceof MDRepositoryBean.SelectorType) {\n\t\t\t\t\t\t\t\t\treturn (MDRepositoryBean.SelectorType) val;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\treturn MDRepositoryBean.SelectorType.valueOf(val.toString());\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.orElseGet(() -> {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tField f = MDRepositoryBean.class.getDeclaredField(\"dataSourceSelection\");\n\t\t\t\t\t\t\t\t\tf.setAccessible(true);\n\t\t\t\t\t\t\t\t\tObject instance = bc.getClazz().newInstance();\n\t\t\t\t\t\t\t\t\treturn (MDRepositoryBean.SelectorType) f.get(instance);\n\t\t\t\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\t\t\t\treturn MDRepositoryBean.SelectorType.List;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\tswitch (selectorType) {\n\t\t\t\t\t\tcase EveryDataSource:\n\t\t\t\t\t\t\tmdRepositoryBean = ((MDRepositoryBean) bc.getClazz().newInstance());\n\t\t\t\t\t\t\tmdRepositoryBean.register(bc.getKernel());\n\t\t\t\t\t\t\treturn getDataSources(config).entrySet().stream().map(e -> {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tDataSourceInfo dataSource = e.getValue();\n\t\t\t\t\t\t\t\t\tmdRepositoryBean.registerIfNotExists(e.getKey());\n\t\t\t\t\t\t\t\t\tBeanConfig bc2 = bc.getKernel().getDependencyManager().getBeanConfig(e.getKey());\n\t\t\t\t\t\t\t\t\tClass<?> implementation = getRepositoryImplementation(configurator, dataSource, bc2,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  bc);\n\t\t\t\t\t\t\t\t\treturn new RepoInfo(bc2, dataSource, implementation);\n\t\t\t\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\t\t\t\tlog.log(Level.WARNING, \"Error getting repository implementation\", ex);\n\t\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\tcase EveryUserRepository:\n\t\t\t\t\t\t\tmdRepositoryBean = ((MDRepositoryBean) bc.getClazz().newInstance());\n\t\t\t\t\t\t\tmdRepositoryBean.register(bc.getKernel());\n\t\t\t\t\t\t\treturn kernel.getDependencyManager()\n\t\t\t\t\t\t\t\t\t.getBeanConfigs()\n\t\t\t\t\t\t\t\t\t.stream()\n\t\t\t\t\t\t\t\t\t.filter(bc1 -> UserRepositoryMDPoolBean.class.isAssignableFrom(bc1.getClazz()))\n\t\t\t\t\t\t\t\t\t.map(BeanConfig::getKernel)\n\t\t\t\t\t\t\t\t\t.flatMap(k1 -> k1.getDependencyManager().getBeanConfigs().stream())\n\t\t\t\t\t\t\t\t\t.filter(bc1 -> !Kernel.class.isAssignableFrom(bc1.getClazz()))\n\t\t\t\t\t\t\t\t\t.filter(bc1 -> !Kernel.DelegatedBeanConfig.class.isAssignableFrom(bc1.getClass()))\n\t\t\t\t\t\t\t\t\t.map(bc1 -> {\n\t\t\t\t\t\t\t\t\t\tString dataSourceName = null;\n\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\tdataSourceName = getDataSourceNameOr(configurator, bc1, bc1.getBeanName());\n\t\t\t\t\t\t\t\t\t\t\tDataSourceInfo dataSource = dataSources.get(dataSourceName);\n\n\t\t\t\t\t\t\t\t\t\t\tmdRepositoryBean.registerIfNotExists(bc1.getBeanName());\n\t\t\t\t\t\t\t\t\t\t\tBeanConfig bc2 = bc.getKernel().getDependencyManager().getBeanConfig(bc1.getBeanName());\n\t\t\t\t\t\t\t\t\t\t\tClass<?> implementation = getRepositoryImplementation(configurator, dataSource, bc2,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  bc);\n\t\t\t\t\t\t\t\t\t\t\treturn new RepoInfo(bc2, dataSource, implementation);\n\t\t\t\t\t\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\t\t\t\t\t\tlog.log(Level.WARNING, \"Error getting repository implementation for: \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\tbc.getKernel().getParent().getName() +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\" bean and named repository: \" + dataSourceName +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\", available data sources:\\n\" + dataSources.values(), ex);\n\t\t\t\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\tcase List:\n\t\t\t\t\t\t\treturn bc.getKernel()\n\t\t\t\t\t\t\t\t\t.getDependencyManager()\n\t\t\t\t\t\t\t\t\t.getBeanConfigs()\n\t\t\t\t\t\t\t\t\t.stream()\n\t\t\t\t\t\t\t\t\t.filter(bc1 -> !Kernel.class.isAssignableFrom(bc1.getClazz()))\n\t\t\t\t\t\t\t\t\t.filter(bc1 -> !Kernel.DelegatedBeanConfig.class.isAssignableFrom(bc1.getClass()))\n\t\t\t\t\t\t\t\t\t.map(bc1 -> {\n\t\t\t\t\t\t\t\t\t\tString dataSourceName = null;\n\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\tdataSourceName = getDataSourceNameOr(configurator, bc1, bc1.getBeanName());\n\t\t\t\t\t\t\t\t\t\t\tDataSourceInfo dataSource = dataSources.get(dataSourceName);\n\t\t\t\t\t\t\t\t\t\t\tClass<?> implementation = getRepositoryImplementation(configurator, dataSource, bc1,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  bc);\n\t\t\t\t\t\t\t\t\t\t\treturn new RepoInfo(bc1, dataSource, implementation);\n\t\t\t\t\t\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\t\t\t\t\t\tlog.log(Level.WARNING, \"Error getting repository implementation for: \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\tbc.getKernel().getParent().getName() +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\" bean and named repository: \" + dataSourceName +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\", available data sources:\\n\" + dataSources.values(), ex);\n\t\t\t\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\treturn Stream.empty();\n\t\t\t\t}\n\t\t\t} catch (Exception ex) {\n\t\t\t\tlog.log(Level.WARNING, \"Error getting repository implementation\", ex);\n\t\t\t\treturn Stream.empty();\n\t\t\t}\n\t\t}).filter(Objects::nonNull).collect(Collectors.toList());\n\t}\n\n\tprivate static Map<DataSourceInfo, List<SchemaInfo>> collectSchemasByDataSource(\n\t\t\tMap<DataSourceInfo, List<RepoInfo>> repositoriesByDataSource) {\n\t\tMap<DataSourceInfo, List<SchemaInfo>> dataSourceSchemas = new HashMap<>();\n\t\tfor (Map.Entry<DataSourceInfo, List<RepoInfo>> entry : repositoriesByDataSource.entrySet()) {\n\n\t\t\tList<SchemaInfo> schemas = entry.getValue()\n\t\t\t\t\t.stream()\n\t\t\t\t\t.collect(Collectors.groupingBy(SchemaManager::getSchemaId, Collectors.toList()))\n\t\t\t\t\t.entrySet()\n\t\t\t\t\t.stream()\n\t\t\t\t\t.map(e -> {\n\t\t\t\t\t\tfinal Repository.SchemaId annotation = e.getValue()\n\t\t\t\t\t\t\t\t.iterator()\n\t\t\t\t\t\t\t\t.next()\n\t\t\t\t\t\t\t\t.getImplementation()\n\t\t\t\t\t\t\t\t.getAnnotation(Repository.SchemaId.class);\n\t\t\t\t\t\treturn new SchemaInfo(annotation, e.getValue());\n\t\t\t\t\t})\n\t\t\t\t\t.collect(Collectors.toList());\n\n\t\t\tdataSourceSchemas.put(entry.getKey(), schemas);\n\t\t}\n\n\t\treturn dataSourceSchemas;\n\t}\n\n\tprivate static String getSchemaId(RepoInfo repoInfo) {\n\t\tRepository.SchemaId schemaId = repoInfo.getImplementation().getAnnotation(Repository.SchemaId.class);\n\t\treturn schemaId == null ? \"<unknown>\" : schemaId.id();\n\t}\n\n\tprivate static String getDataSourceNameOr(DSLBeanConfigurator configurator, BeanConfig bc, String defValue) {\n\t\tMap<String, Object> cfg = configurator.getConfiguration(bc);\n\t\treturn (String) cfg.getOrDefault(\"dataSourceName\", cfg.getOrDefault(\"data-source\", defValue));\n\t}\n\n\tprivate static Class<?> getRepositoryImplementation(DSLBeanConfigurator configurator, DataSourceInfo dataSource,\n\t\t\t\t\t\t\t\t\t\t\t\t BeanConfig beanConfig, BeanConfig mdRepoBeanConfig)\n\t\t\tthrows ClassNotFoundException, DBInitException, IllegalAccessException, InstantiationException,\n\t\t\t\t   NoSuchMethodException, InvocationTargetException {\n\t\tif (dataSource == null) {\n\t\t\tthrow new RuntimeException(\"No dataSource configured!\");\n\t\t}\n\t\tMap<String, Object> cfg = configurator.getConfiguration(beanConfig);\n\t\tString cls = (String) cfg.getOrDefault(\"cls\", cfg.get(\"repo-cls\"));\n\t\tif (cls != null) {\n\t\t\treturn ModulesManagerImpl.getInstance().forName(cls);\n\t\t}\n\n\t\tObject bean = beanConfig.getClazz().newInstance();\n\t\tif (bean instanceof MDPoolConfigBean) {\n\t\t\tMethod m = MDPoolConfigBean.class.getDeclaredMethod(\"getRepositoryIfc\");\n\t\t\tm.setAccessible(true);\n\t\t\treturn DataSourceHelper.getDefaultClass((Class<?>) m.invoke(bean), dataSource.getResourceUri());\n\t\t}\n\t\tif (bean instanceof SDRepositoryBean) {\n\t\t\tMethod m = SDRepositoryBean.class.getDeclaredMethod(\"findClassForDataSource\", DataSource.class);\n\t\t\tm.setAccessible(true);\n\t\t\treturn (Class<?>) m.invoke(bean, dataSource);\n\t\t}\n\t\tif (mdRepoBeanConfig != null) {\n\t\t\tObject mdRepoBean = mdRepoBeanConfig.getClazz().newInstance();\n\t\t\tif (mdRepoBean instanceof MDRepositoryBean) {\n\t\t\t\tMethod m = MDRepositoryBean.class.getDeclaredMethod(\"findClassForDataSource\", DataSource.class);\n\t\t\t\tm.setAccessible(true);\n\t\t\t\treturn (Class<?>) m.invoke(mdRepoBean, dataSource);\n\t\t\t}\n\t\t}\n\t\tthrow new RuntimeException(\"Unknown repository!\");\n\t}\n\n\tpublic static Kernel prepareKernel(Map<String, Object> config) {\n\t\tKernel kernel = new Kernel(\"root\");\n\t\ttry {\n\t\t\tif (XMPPServer.isOSGi()) {\n\t\t\t\tkernel.registerBean(\"classUtilBean\")\n\t\t\t\t\t\t.asInstance(Class.forName(\"tigase.osgi.util.ClassUtilBean\").newInstance())\n\t\t\t\t\t\t.exportable()\n\t\t\t\t\t\t.exec();\n\t\t\t} else {\n\t\t\t\tkernel.registerBean(\"classUtilBean\")\n\t\t\t\t\t\t.asInstance(Class.forName(\"tigase.util.reflection.ClassUtilBean\").newInstance())\n\t\t\t\t\t\t.exportable()\n\t\t\t\t\t\t.exec();\n\t\t\t}\n\t\t} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\n\t\t// register default types converter and properties bean configurator\n\t\tkernel.registerBean(DefaultTypesConverter.class).exportable().exec();\n\t\tkernel.registerBean(DSLBeanConfiguratorWithBackwardCompatibility.class).exportable().exec();\n\t\tkernel.registerBean(\"eventBus\").asInstance(EventBusFactory.getInstance()).exportable().exec();\n\n\t\tDSLBeanConfigurator configurator = kernel.getInstance(DSLBeanConfigurator.class);\n\t\tconfigurator.setProperties(config);\n\t\tModulesManagerImpl.getInstance().setBeanConfigurator(configurator);\n\n\t\tkernel.registerBean(\"beanSelector\").asInstance(new ServerBeanSelector()).exportable().exec();\n\n\t\treturn kernel;\n\t}\n\n\tpublic static List<BeanConfig> getRepositoryBeans(Kernel kernel, List<Class<?>> repositoryClasses, Map<String, Object> config) {\n\t\tDSLBeanConfigurator configurator = kernel.getInstance(DSLBeanConfigurator.class);\n\t\tconfigurator.registerBeans(null, null, config);\n\n\t\tList<BeanConfig> repoBeans = crawlKernel(repositoryClasses, kernel, configurator, config);\n\t\tfixShutdownThreadIssue();\n\t\treturn repoBeans;\n\t}\n\t\n\tprivate static void fixShutdownThreadIssue() {\n\t\tMonitorRuntime.getMonitorRuntime();\n\t\ttry {\n\t\t\tField f = MonitorRuntime.class.getDeclaredField(\"mainShutdownThread\");\n\t\t\tf.setAccessible(true);\n\t\t\tMonitorRuntime monitorRuntime = MonitorRuntime.getMonitorRuntime();\n\t\t\tRuntime.getRuntime().removeShutdownHook((Thread) f.get(monitorRuntime));\n\t\t} catch (NoSuchFieldException | IllegalAccessException ex) {\n\t\t\tlog.log(Level.FINEST, \"There was an error with unregistration of shutdown hook\", ex);\n\t\t}\n\t}\n\n\tpublic static List<BeanConfig> crawlKernel(List<Class<?>> repositoryClasses, Kernel kernel,\n\t\t\t\t\t\t\t\t\t\t\t   DSLBeanConfigurator configurator, Map<String, Object> config) {\n\t\tList<BeanConfig> results = new ArrayList<>();\n\t\tkernel.getDependencyManager()\n\t\t\t\t.getBeanConfigs()\n\t\t\t\t.stream()\n\t\t\t\t.filter(bc -> bc.getState() == BeanConfig.State.registered)\n\t\t\t\t.filter(bc -> !Kernel.DelegatedBeanConfig.class.isAssignableFrom(bc.getClass()))\n\t\t\t\t.forEach(bc -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tClass<?> clazz = bc.getClazz();\n\t\t\t\t\t\tif (\"tigase.muc.cluster.MUCComponentClustered\".equals(clazz.getName())) {\n\t\t\t\t\t\t\tclazz = ModulesManagerImpl.getInstance().forName(\"tigase.muc.MUCComponent\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (\"tigase.pubsub.cluster.PubSubComponentClustered\".equals(clazz.getName())) {\n\t\t\t\t\t\t\tclazz = ModulesManagerImpl.getInstance().forName(\"tigase.pubsub.PubSubComponent\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tObject bean = clazz.newInstance();\n\t\t\t\t\t\tif (RegistrarBean.class.isAssignableFrom(clazz)) {\n\t\t\t\t\t\t\tRegistrarKernel k = new RegistrarKernel();\n\t\t\t\t\t\t\tk.setName(bc.getBeanName());\n\t\t\t\t\t\t\tbc.getKernel().registerBean(bc.getBeanName() + \"#KERNEL\").asInstance(k).exec();\n\t\t\t\t\t\t\tMethod m = bc.getClass().getDeclaredMethod(\"setKernel\", Kernel.class);\n\t\t\t\t\t\t\tm.setAccessible(true);\n\t\t\t\t\t\t\tm.invoke(bc, k);\n\n\t\t\t\t\t\t\tKernel parent = bc.getKernel().getParent();\n\t\t\t\t\t\t\t// without this line setBeanActive() fails\n\t\t\t\t\t\t\t//parent.ln(beanConfig.getBeanName(), beanConfig.getKernel(), beanConfig.getBeanName());\n\t\t\t\t\t\t\tparent.ln(bc.getBeanName(), bc.getKernel(), \"service\");\n\n\t\t\t\t\t\t\t((RegistrarBean) bean).register(bc.getKernel());\n\t\t\t\t\t\t\tMap<String, Object> cfg = (Map<String, Object>) config.getOrDefault(bc.getBeanName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew HashMap<>());\n\t\t\t\t\t\t\tconfigurator.registerBeans(bc, bean, cfg);\n\t\t\t\t\t\t\tresults.addAll(crawlKernel(repositoryClasses, bc.getKernel(), configurator, cfg));\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (repositoryClasses.stream()\n\t\t\t\t\t\t\t\t.anyMatch(repoClazz -> repoClazz.isAssignableFrom(bc.getClazz()))) {\n\t\t\t\t\t\t\tresults.add(bc);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} catch (ClassNotFoundException | InvocationTargetException | NoSuchMethodException | InstantiationException | IllegalAccessException ex) {\n\t\t\t\t\t\tlog.log(Level.SEVERE, \"Exception while crawling kernel\", ex);\n\t\t\t\t\t} catch (StackOverflowError ex) {\n\t\t\t\t\t\tex.printStackTrace();\n\t\t\t\t\t\tKernel k = bc.getKernel();\n\t\t\t\t\t\tList<String> list = new ArrayList<>();\n\t\t\t\t\t\tdo {\n\t\t\t\t\t\t\tlist.add(k.getName());\n\t\t\t\t\t\t} while ((k = k.getParent()) != null);\n\t\t\t\t\t\tlog.log(Level.SEVERE, \"exception in path \" + list);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\treturn results;\n\t}\n\n\t@FunctionalInterface\n\tpublic interface SchemaLoaderExecutor {\n\n\t\tList<ResultEntry> execute(SchemaLoader schemaLoader, SchemaManagerLogHandler handler);\n\t}\n\n\tpublic static class DataSourceInfo\n\t\t\timplements DataSource {\n\n\t\tprivate final String name;\n\t\tprivate final String uri;\n\t\tprivate boolean automaticSchemaManagement = true;\n\n\t\tprivate DataSourceInfo(String name, String uri) {\n\t\t\tthis.name = name;\n\t\t\tthis.uri = uri;\n\t\t}\n\n\t\tpublic String getName() {\n\t\t\treturn name;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<Version> getSchemaVersion(String component) {\n\t\t\treturn Optional.empty();\n\t\t}\n\n\t\t@Override\n\t\tpublic String getResourceUri() {\n\t\t\treturn uri;\n\t\t}\n\n\t\t@Override\n\t\tpublic void initialize(String connStr) throws RepositoryException {\n\t\t\t// nothing to do\n\t\t}\n\n\t\t@Override\n\t\t@Deprecated\n\t\tpublic void initRepository(String resource_uri, Map<String, String> params) throws DBInitException {\n\t\t\t// nothing to do\n\t\t}\n\n\t\tpublic boolean automaticSchemaManagement() {\n\t\t\treturn automaticSchemaManagement;\n\t\t}\n\n\t\tprotected void setAutomaticSchemaManagement(boolean value) {\n\t\t\tthis.automaticSchemaManagement = value;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn name + \"[uri=\" + uri + \"]\";\n\t\t}\n\t}\n\n\tpublic static class Pair<K, V> {\n\n\t\tprivate final K key;\n\t\tprivate final V value;\n\n\t\tpublic Pair(K key, V value) {\n\t\t\tthis.key = key;\n\t\t\tthis.value = value;\n\t\t}\n\n\t\tpublic K getKey() {\n\t\t\treturn key;\n\t\t}\n\n\t\tpublic V getValue() {\n\t\t\treturn value;\n\t\t}\n\t}\n\n\tpublic static class RepoInfo {\n\n\t\tprivate final BeanConfig beanConfig;\n\t\tprivate final DataSourceInfo dataSource;\n\t\tprivate final Class<?> implementation;\n\n\t\tpublic RepoInfo(BeanConfig beanConfig, DataSourceInfo dataSource, Class<?> implementation) {\n\t\t\tthis.beanConfig = beanConfig;\n\t\t\tthis.dataSource = dataSource;\n\t\t\tthis.implementation = implementation;\n\t\t}\n\n\t\tpublic DataSourceInfo getDataSource() {\n\t\t\treturn dataSource;\n\t\t}\n\n\t\tpublic Class<?> getImplementation() {\n\t\t\treturn implementation;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn beanConfig.getBeanName() + \"[dataSource=\" + dataSource.getName() + \", class=\" + implementation + \"]\";\n\t\t}\n\t}\n\n\tpublic static class ResultEntry {\n\n\t\tpublic final String message;\n\t\tpublic final String name;\n\t\tpublic final SchemaLoader.Result result;\n\n\t\tprivate ResultEntry(String name, SchemaLoader.Result result, SchemaManagerLogHandler logHandler) {\n\t\t\tthis.name = name;\n\t\t\tthis.result = result;\n\t\t\tthis.message = logHandler.getMessage().orElse(null);\n\t\t}\n\n\t\tprivate ResultEntry(String name, SchemaLoader.Result result, String message) {\n\t\t\tthis.name = name;\n\t\t\tthis.result = result;\n\t\t\tthis.message = message;\n\t\t}\n\t}\n\n\tpublic static class RootCredentials {\n\n\t\tpublic final String password;\n\t\tpublic final String user;\n\n\t\tpublic RootCredentials(String user, String password) {\n\t\t\tthis.user = user;\n\t\t\tthis.password = password;\n\t\t}\n\t}\n\n\tpublic static class RootCredentialsCache {\n\n\t\tprivate final Map<String, RootCredentials> cache = new ConcurrentHashMap<>();\n\n\t\tpublic RootCredentials get(String server) {\n\t\t\treturn cache.getOrDefault(createKey(server), cache.get(\"default\"));\n\t\t}\n\n\t\tpublic void set(String server, RootCredentials credentials) {\n\t\t\tcache.put(createKey(server), credentials);\n\t\t}\n\n\t\tprivate String createKey(String server) {\n\t\t\tif (server == null) {\n\t\t\t\treturn \"default\";\n\t\t\t}\n\t\t\treturn server;\n\t\t}\n\n\t}\n\n\tpublic static class SchemaInfo {\n\n\t\tprivate final List<RepoInfo> repositories;\n\t\tprivate final Optional<String> id;\n\t\tprivate final Optional<String> name;\n\t\tprivate final boolean external;\n\n\t\tpublic SchemaInfo(Repository.SchemaId schema, List<RepoInfo> repositories) {\n\t\t\tthis(schema == null ? null : schema.id(),\n\t\t\t     schema == null ? null : schema.name(),\n\t\t\t     schema == null || schema.external(),\n\t\t\t     repositories);\n\t\t}\n\n\t\tpublic SchemaInfo(String id, String name, boolean external, List<RepoInfo> repositories) {\n\t\t\tthis.id = Optional.ofNullable(id);\n\t\t\tthis.name = Optional.ofNullable(name);\n\t\t\tthis.external = external;\n\t\t\tthis.repositories = repositories;\n\t\t}\n\n\t\tpublic String getId() {\n\t\t\treturn id.orElse(\"<unknown>\");\n\t\t}\n\n\t\tpublic String getName() {\n\t\t\treturn name.orElse(\"\");\n\t\t}\n\n\t\tpublic List<RepoInfo> getRepositories() {\n\t\t\treturn repositories;\n\t\t}\n\n\t\tpublic Optional<Version> getVersion() {\n\t\t\tMap<Version, List<Version>> versions = repositories.stream()\n\t\t\t\t\t.map(RepoInfo::getImplementation)\n\t\t\t\t\t.filter(RepositoryVersionAware.class::isAssignableFrom)\n\t\t\t\t\t.map(SchemaManager::getInstance)\n\t\t\t\t\t.map(RepositoryVersionAware.class::cast)\n\t\t\t\t\t.filter(Objects::nonNull)\n\t\t\t\t\t.map(RepositoryVersionAware::getVersion)\n\t\t\t\t\t.filter(Objects::nonNull)\n\t\t\t\t\t.collect(Collectors.groupingBy(Function.identity()));\n\t\t\tif (versions.size() == 1) {\n\t\t\t\treturn Optional.of(versions.keySet().iterator().next());\n\t\t\t} else {\n\t\t\t\treturn Optional.empty();\n\t\t\t}\n\t\t}\n\n\t\tpublic boolean isExternal() {\n\t\t\treturn this.external;\n\t\t}\n\n\t\tpublic boolean isValid() {\n\t\t\treturn id.isPresent() && getVersion().isPresent();\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"SchemaInfo[id=\" + getId() + \", repositories=\" + repositories +\n\t\t\t\t\t\"]\";\n\t\t}\n\t}\n\n\tprivate static <T> T getInstance(Class<T> clazz) {\n\t\ttry {\n\t\t\treturn clazz.newInstance();\n\t\t} catch (InstantiationException | IllegalAccessException e) {\n\t\t\tlog.log(Level.WARNING, \"Error creating instance of: \" + clazz.getName(), e);\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/util/SchemaManagerLogHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.util;\n\nimport java.util.ArrayDeque;\nimport java.util.Optional;\nimport java.util.logging.*;\n\n/**\n * @author andrzej\n */\npublic class SchemaManagerLogHandler\n\t\textends Handler {\n\n\tprivate final ArrayDeque<LogRecord> queue = new ArrayDeque<LogRecord>();\n\n\tprivate static Formatter simpleLogFormatter = new SchemaLogFormatter();\n\n\tpublic SchemaManagerLogHandler() {\n\t}\n\n\t@Override\n\tpublic void publish(LogRecord record) {\n\t\tqueue.offer(record);\n\t}\n\n\t@Override\n\tpublic void flush() {\n\t\tqueue.clear();\n\t}\n\n\t@Override\n\tpublic void close() throws SecurityException {\n\t\tflush();\n\t}\n\n\tpublic LogRecord poll() {\n\t\treturn queue.poll();\n\t}\n\n\tpublic Optional<String> getMessage() {\n\t\tLogRecord rec;\n\t\tStringBuilder sb = null;\n\t\twhile ((rec = poll()) != null) {\n\t\t\tif (rec.getLevel().intValue() <= Level.CONFIG.intValue()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (rec.getMessage() == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (sb == null) {\n\t\t\t\tsb = new StringBuilder();\n\t\t\t} else {\n\t\t\t\tsb.append(\"\\n\");\n\t\t\t}\n\t\t\tsb.append(simpleLogFormatter.format(rec));\n\t\t}\n\t\treturn (sb == null ? Optional.empty() : Optional.of(sb.toString()));\n\t}\n\n\tprivate static class SchemaLogFormatter extends Formatter {\n\n\t\t@Override\n\t\tpublic String format(LogRecord record) {\n\t\t\treturn formatMessage(record);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/util/SchemaVersionCheckerLogger.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.util;\n\nimport tigase.db.DataSourceAware;\nimport tigase.db.Repository;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.eventbus.HandleEvent;\nimport tigase.eventbus.events.StartupFinishedEvent;\nimport tigase.util.Version;\nimport tigase.util.dns.DNSResolverFactory;\n\nimport java.util.Comparator;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentSkipListSet;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class SchemaVersionCheckerLogger {\n\n\tprivate final static Logger log = Logger.getLogger(SchemaVersionCheckerLogger.class.getName());\n\tprivate static final SchemaVersionCheckerLogger INSTANCE = new SchemaVersionCheckerLogger();\n\tprivate final EventBus eventBus;\n\tprivate Set<VersionCheckerSchemaInfo> components = new ConcurrentSkipListSet<>();\n\n\tpublic static SchemaVersionCheckerLogger getInstance() {\n\t\treturn INSTANCE;\n\t}\n\n\tprivate SchemaVersionCheckerLogger() {\n\t\teventBus = EventBusFactory.getInstance();\n\t\teventBus.registerAll(this);\n\t}\n\n\tpublic void logVersion(VersionCheckerSchemaInfo component) {\n\t\tcomponents.add(component);\n\t}\n\n\t@HandleEvent\n\tpublic void printSnapshotInformation(StartupFinishedEvent event) {\n\n\t\t// if not this node is being shutdown then do nothing\n\t\tif (event.getNode() == null || !DNSResolverFactory.getInstance().getDefaultHost().equals(event.getNode())) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!components.isEmpty()) {\n\n\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\tsb.append(\"\\n\\n\\tIt's possible that following data sources are out of date:\");\n\t\t\tcomponents.forEach(item -> sb.append(\"\\n\\t\\t* \").append(item));\n\t\t\tsb.append(\"\\n\\tPlease upgrade the installation by running:\");\n\t\t\tsb.append(\"\\n\\t\\t$ ./scripts/tigase.sh upgrade-schema etc/tigase.conf\").append(\"\\n\");\n\t\t\tsb.append(\"\\n\\t(this warning is printed each time SNAPSHOT version is started, you can ignore this\");\n\t\t\tsb.append(\"\\n\\tmessage if you've just run above command)\").append(\"\\n\");\n\n\t\t\tlog.log(Level.INFO, sb.toString());\n\t\t}\n\t}\n\n\tpublic static class VersionCheckerSchemaInfo\n\t\t\timplements Comparable<VersionCheckerSchemaInfo> {\n\n\t\tprivate static final Comparator<VersionCheckerSchemaInfo> VERSION_COMPARATOR = Comparator.comparing(\n\t\t\t\t(VersionCheckerSchemaInfo schemaInfo) -> schemaInfo.repositoryId)\n\t\t\t\t.thenComparing(ver -> ver.databaseVersion.orElse(null),\n\t\t\t\t               Comparator.nullsLast(Version.VERSION_COMPARATOR))\n\t\t\t\t.thenComparing(schemaInfo -> schemaInfo.requiredVersion);\n\t\tOptional<Version> databaseVersion;\n\t\tString datasourceClassName;\n\t\tString repositoryId;\n\t\tVersion requiredVersion;\n\n\t\tpublic VersionCheckerSchemaInfo(final Class<? extends DataSourceAware> datasourceClass,\n\t\t                                Optional<Version> databaseVersion, Version requiredVersion) {\n\t\t\tthis.datasourceClassName = datasourceClass.getSimpleName();\n\t\t\tthis.repositoryId = datasourceClass.getAnnotation(Repository.SchemaId.class).id();\n\t\t\tthis.databaseVersion = databaseVersion;\n\t\t\tthis.requiredVersion = requiredVersion;\n\t\t}\n\n\t\t@Override\n\t\tpublic int compareTo(VersionCheckerSchemaInfo that) {\n\t\t\treturn VERSION_COMPARATOR.compare(this, that);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object o) {\n\t\t\tif (this == o) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tVersionCheckerSchemaInfo that = (VersionCheckerSchemaInfo) o;\n\n\t\t\tif (repositoryId != null ? !repositoryId.equals(that.repositoryId) : that.repositoryId != null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (databaseVersion.isPresent()\n\t\t\t    ? !databaseVersion.equals(that.databaseVersion)\n\t\t\t    : that.databaseVersion.isPresent()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn requiredVersion != null\n\t\t\t       ? requiredVersion.equals(that.requiredVersion)\n\t\t\t       : that.requiredVersion == null;\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\tint result = repositoryId != null ? repositoryId.hashCode() : 0;\n\t\t\tresult = 31 * result + (databaseVersion != null ? databaseVersion.hashCode() : 0);\n\t\t\tresult = 31 * result + (requiredVersion != null ? requiredVersion.hashCode() : 0);\n\t\t\treturn result;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tfinal StringBuilder sb = new StringBuilder();\n\t\t\tsb.append(repositoryId).append(\" (\").append(datasourceClassName).append(\")\");\n\t\t\tString ver = databaseVersion.isPresent() ? databaseVersion.get().toString() : \"n/a\";\n\t\t\tsb.append(\" ~ version in database: \")\n\t\t\t\t\t.append(ver)\n\t\t\t\t\t.append(\", required version: \")\n\t\t\t\t\t.append(requiredVersion)\n\t\t\t\t\t.append(\")\");\n\t\t\treturn sb.toString();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/util/importexport/AbstractImporterExtension.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.util.importexport;\n\nimport java.util.Date;\n\npublic abstract class AbstractImporterExtension\n\t\timplements ImporterExtension {\n\t\n\tpublic Date parseTimestamp(String str) {\n\t\treturn Exporter.parseTimestamp(str);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/util/importexport/CredentialsExtension.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.util.importexport;\n\nimport tigase.auth.CredentialsDecoderBean;\nimport tigase.auth.credentials.Credentials;\nimport tigase.auth.credentials.entries.*;\nimport tigase.db.AbstractAuthRepositoryWithCredentials;\nimport tigase.db.AuthRepository;\nimport tigase.db.UserNotFoundException;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.core.Kernel;\nimport tigase.util.Base64;\nimport tigase.util.ClassUtil;\nimport tigase.util.ui.console.CommandlineParameter;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.io.Writer;\nimport java.lang.reflect.Field;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Path;\nimport java.security.InvalidParameterException;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport static tigase.db.util.importexport.RepositoryManager.isSet;\n\npublic class CredentialsExtension\n\t\textends RepositoryManagerExtensionBase {\n\n\tprivate static final Logger log = Logger.getLogger(CredentialsExtension.class.getSimpleName());\n\n\tprivate final CommandlineParameter EXPORT_PLAIN_CREDENTIALS = new CommandlineParameter.Builder(null,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"plain-credentials\").description(\n\t\t\t\t\t\"Export PLAIN credentials (if any exist)\")\n\t\t\t.type(Boolean.class)\n\t\t\t.requireArguments(false)\n\t\t\t.defaultValue(\"false\")\n\t\t\t.build();\n\n\tprivate final CommandlineParameter IMPORT_PLAIN_CREDENTIALS = new CommandlineParameter.Builder(null,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"plain-credentials\").description(\n\t\t\t\"Import PLAIN credentials\").type(Boolean.class).requireArguments(false).defaultValue(\"false\").build();\n\n\t@Override\n\tpublic void initialize(Kernel kernel, DataSourceHelper dataSourceHelper,\n\t\t\t\t\t\t   RepositoryHolder repositoryHolder, Path rootPath) {\n\t\trepositoryHolder.registerPrepFn(AbstractAuthRepositoryWithCredentials.class, this::prepareAuthRepo);\n\t\tsuper.initialize(kernel, dataSourceHelper, repositoryHolder, rootPath);\n\t}\n\n\t@Override\n\tpublic Stream<CommandlineParameter> getExportParameters() {\n\t\treturn Stream.concat(super.getExportParameters(), Stream.of(EXPORT_PLAIN_CREDENTIALS));\n\t}\n\n\t@Override\n\tpublic Stream<CommandlineParameter> getImportParameters() {\n\t\treturn Stream.concat(super.getImportParameters(), Stream.of(IMPORT_PLAIN_CREDENTIALS));\n\t}\n\n\t@Override\n\tpublic void exportDomainData(String domain, Writer writer) throws Exception {\n\t}\n\n\t@Override\n\tpublic void exportUserData(Path userDirPath, BareJID user, Writer writer) throws Exception {\n\t\tAuthRepository authRepository = getRepository(AbstractAuthRepositoryWithCredentials.class, user.getDomain());\n\t\ttry {\n\t\t\tCredentials credentials = authRepository.getCredentials(user, \"default\");\n\t\t\tif (credentials instanceof AuthRepository.DefaultCredentials) {\n\t\t\t\tField entriesField = AuthRepository.DefaultCredentials.class.getDeclaredField(\"entries\");\n\t\t\t\tentriesField.setAccessible(true);\n\t\t\t\tfor (AuthRepository.DefaultCredentials.RawEntry entry : (List<AuthRepository.DefaultCredentials.RawEntry>) entriesField.get(\n\t\t\t\t\t\tcredentials)) {\n\t\t\t\t\tString mechanism = entry.getMechanism();\n\t\t\t\t\tCredentials.Entry e1 = credentials.getEntryForMechanism(mechanism);\n\t\t\t\t\tif (e1 instanceof ScramCredentialsEntry saslEntry) {\n\t\t\t\t\t\twriteSCRAM(saslEntry, writer);\n\t\t\t\t\t} else if (e1 instanceof PlainCredentialsEntry plainEntry) {\n\t\t\t\t\t\tif (!isSet(EXPORT_PLAIN_CREDENTIALS)) {\n\t\t\t\t\t\t\twriteSCRAM(new ScramSha1CredentialsEntry(plainEntry), writer);\n\t\t\t\t\t\t\twriteSCRAM(new ScramSha256CredentialsEntry(plainEntry), writer);\n\t\t\t\t\t\t\twriteSCRAM(new ScramSha512CredentialsEntry(plainEntry), writer);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\twriter.append(\"<plain-credentials xmlns='tigase:xep-0227:sasl:0#plain' mechanism='\")\n\t\t\t\t\t\t\t\t\t.append(plainEntry.getMechanism())\n\t\t\t\t\t\t\t\t\t.append(\"'>\");\n\t\t\t\t\t\t\twriter.append(\"<password>\")\n\t\t\t\t\t\t\t\t\t.append(Base64.encode(plainEntry.getPassword().getBytes(StandardCharsets.UTF_8)))\n\t\t\t\t\t\t\t\t\t.append(\"</password>\");\n\t\t\t\t\t\t\twriter.append(\"</plain-credentials>\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (UserNotFoundException ex) {\n\t\t\tlog.log(Level.FINEST, \"No credentials for user \" + user);\n\t\t}\n\t}\n\n\tprotected void writeSCRAM(ScramCredentialsEntry saslEntry, Writer writer) throws Exception {\n\t\twriter.append(\"<scram-credentials xmlns='urn:xmpp:pie:0#scram' mechanism='\")\n\t\t\t\t.append(saslEntry.getMechanism())\n\t\t\t\t.append(\"'>\");\n\t\twriter.append(\"<iter-count>\").append(String.valueOf(saslEntry.getIterations())).append(\"</iter-count>\");\n\t\twriter.append(\"<salt>\").append(Base64.encode(saslEntry.getSalt())).append(\"</salt>\");\n\t\twriter.append(\"<server-key>\").append(Base64.encode(saslEntry.getServerKey())).append(\"</server-key>\");\n\t\twriter.append(\"<stored-key>\").append(Base64.encode(saslEntry.getStoredKey())).append(\"</stored-key>\");\n\t\twriter.append(\"</scram-credentials>\");\n\t}\n\n\tprotected AbstractAuthRepositoryWithCredentials prepareAuthRepo(AbstractAuthRepositoryWithCredentials authRepo) {\n\t\tCredentialsDecoderBean decoder = new CredentialsDecoderBean();\n\t\ttry {\n\t\t\tField f = CredentialsDecoderBean.class.getDeclaredField(\"decoders\");\n\t\t\tf.setAccessible(true);\n\t\t\tList<Credentials.Decoder> decoders = ClassUtil.getClassesImplementing(Credentials.Decoder.class)\n\t\t\t\t\t.stream()\n\t\t\t\t\t.map(clazz -> {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tCredentials.Decoder d = clazz.getConstructor().newInstance();\n\t\t\t\t\t\t\tBean bean = d.getClass().getAnnotation(Bean.class);\n\t\t\t\t\t\t\tif (bean != null) {\n\t\t\t\t\t\t\t\tClass x = d.getClass();\n\t\t\t\t\t\t\t\twhile (x != null) {\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tField f1 = x.getDeclaredField(\"name\");\n\t\t\t\t\t\t\t\t\t\tf1.setAccessible(true);\n\t\t\t\t\t\t\t\t\t\tf1.set(d, bean.name());\n\t\t\t\t\t\t\t\t\t\tx = null;\n\t\t\t\t\t\t\t\t\t} catch (NoSuchFieldException ex) {\n\t\t\t\t\t\t\t\t\t\tx = x.getSuperclass();\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn d;\n\t\t\t\t\t\t} catch (Throwable ex) {\n\t\t\t\t\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  \"failed to initialize credentials decoder \" + clazz, ex);\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.filter(Objects::nonNull)\n\t\t\t\t\t.collect(Collectors.toList());\n\t\t\tSystem.out.println(decoders);\n\t\t\tf.set(decoder, decoders);\n\t\t\tauthRepo.setCredentialsCodecs(null, decoder);\n\t\t} catch (Throwable ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t\treturn authRepo;\n\t}\n\n\t@Override\n\tpublic ImporterExtension startImportUserData(BareJID userJid, String name,\n\t\t\t\t\t\t\t\t\t\t\t\t Map<String, String> attrs) throws Exception {\n\t\treturn switch (name) {\n\t\t\tcase \"scram-credentials\" -> {\n\t\t\t\tif (\"urn:xmpp:pie:0#scram\".equals(attrs.get(\"xmlns\"))) {\n\t\t\t\t\tyield new SCRAMAuthImportExtension(\n\t\t\t\t\t\t\tgetRepository(AbstractAuthRepositoryWithCredentials.class, userJid.getDomain()), userJid,\n\t\t\t\t\t\t\tattrs.get(\"mechanism\"));\n\t\t\t\t}\n\t\t\t\tyield null;\n\t\t\t}\n\t\t\tcase \"plain-credentials\" -> {\n\t\t\t\tif (\"tigase:xep-0227:sasl:0#plain\".equals(attrs.get(\"xmlns\"))) {\n\t\t\t\t\tyield new PlainAuthImportExtension(\n\t\t\t\t\t\t\tgetRepository(AbstractAuthRepositoryWithCredentials.class, userJid.getDomain()), userJid,\n\t\t\t\t\t\t\tattrs.get(\"mechanism\"), isSet(IMPORT_PLAIN_CREDENTIALS));\n\t\t\t\t}\n\t\t\t\tyield null;\n\t\t\t}\n\t\t\tdefault -> null;\n\t\t};\n\t}\n\n\tpublic abstract static class AuthImportExtension\n\t\t\textends AbstractImporterExtension {\n\n\t\tprotected final AuthRepository authRepository;\n\t\tprivate final BareJID user;\n\t\tprivate final String mechanism;\n\n\t\tprotected AuthImportExtension(AuthRepository authRepository, BareJID user, String mechanism) {\n\t\t\tthis.authRepository = authRepository;\n\t\t\tthis.user = user;\n\t\t\tthis.mechanism = mechanism;\n\t\t}\n\n\t\tprotected void save(String data) throws Exception {\n\t\t\tsave(mechanism, data);\n\t\t}\n\t\tprotected void save(String mechanism, String data) throws Exception {\n\t\t\tlog.finest(\"importing user \" + user + \" credentials for \" + mechanism + \"...\");\n\t\t\tauthRepository.updateCredential(user, \"default\", mechanism, data);\n\t\t}\n\t}\n\n\tpublic static class SCRAMAuthImportExtension\n\t\t\textends AuthImportExtension {\n\n\t\tprivate byte[] salt;\n\t\tprivate int iterations;\n\t\tprivate byte[] storedKey;\n\t\tprivate byte[] serverKey;\n\n\t\tpublic SCRAMAuthImportExtension(AuthRepository authRepository, BareJID user, String mechanism) {\n\t\t\tsuper(authRepository, user, mechanism);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean handleElement(Element element) throws Exception {\n\t\t\treturn switch (element.getName()) {\n\t\t\t\tcase \"iter-count\" -> {\n\t\t\t\t\titerations = Integer.parseInt(element.getCData());\n\t\t\t\t\tyield true;\n\t\t\t\t}\n\t\t\t\tcase \"salt\" -> {\n\t\t\t\t\tsalt = Base64.decode(element.getCData());\n\t\t\t\t\tyield true;\n\t\t\t\t}\n\t\t\t\tcase \"server-key\" -> {\n\t\t\t\t\tserverKey = Base64.decode(element.getCData());\n\t\t\t\t\tyield true;\n\t\t\t\t}\n\t\t\t\tcase \"stored-key\" -> {\n\t\t\t\t\tstoredKey = Base64.decode(element.getCData());\n\t\t\t\t\tyield true;\n\t\t\t\t}\n\t\t\t\tdefault -> false;\n\t\t\t};\n\t\t}\n\n\t\t@Override\n\t\tpublic void close() throws Exception {\n\t\t\tif (iterations <= 0) {\n\t\t\t\tthrow new InvalidParameterException(\"Iterations cannot be less or equal 0!\");\n\t\t\t}\n\t\t\tif (salt == null) {\n\t\t\t\tthrow new InvalidParameterException(\"Salt cannot be null!\");\n\t\t\t}\n\t\t\tif (serverKey == null) {\n\t\t\t\tthrow new InvalidParameterException(\"ServerKey cannot be null!\");\n\t\t\t}\n\t\t\tif (storedKey == null) {\n\t\t\t\tthrow new InvalidParameterException(\"StoredKey cannot be null!\");\n\t\t\t}\n\t\t\tsave(ScramCredentialsEntry.Encoder.encode(salt, iterations, storedKey, serverKey));\n\t\t\tsuper.close();\n\t\t}\n\t}\n\n\tpublic static class PlainAuthImportExtension\n\t\t\textends AuthImportExtension {\n\n\t\tprivate String password = null;\n\t\tprivate boolean importPLAIN;\n\n\t\tprotected PlainAuthImportExtension(AuthRepository authRepository, BareJID user, String mechanism, boolean importPLAIN) {\n\t\t\tsuper(authRepository, user, mechanism);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean handleElement(Element element) throws Exception {\n\t\t\tif (\"password\".equals(element.getName())) {\n\t\t\t\tpassword = new String(Base64.decode(element.getCData()), StandardCharsets.UTF_8);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic void close() throws Exception {\n\t\t\tif (importPLAIN) {\n\t\t\t\tsave(password);\n\t\t\t} else {\n\t\t\t\tPlainCredentialsEntry plainEntry = new PlainCredentialsEntry(password);\n\t\t\t\tsave(\"SCRAM-SHA-1\", ScramCredentialsEntry.Encoder.encode(new ScramSha1CredentialsEntry(plainEntry)));\n\t\t\t\tsave(\"SCRAM-SHA-256\", ScramCredentialsEntry.Encoder.encode(new ScramSha256CredentialsEntry(plainEntry)));\n\t\t\t\tsave(\"SCRAM-SHA-512\", ScramCredentialsEntry.Encoder.encode(new ScramSha512CredentialsEntry(plainEntry)));\n\t\t\t}\n\t\t\tsuper.close();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/util/importexport/DataSourceHelper.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.util.importexport;\n\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.db.DataSource;\nimport tigase.db.DataSourceAware;\nimport tigase.db.jdbc.DataRepositoryImpl;\nimport tigase.db.util.SchemaManager;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\npublic class DataSourceHelper {\n\n\tprivate final Map<String, DataSource> dataSourceMap = new ConcurrentHashMap<>();\n\tprivate final Map<String, SchemaManager.DataSourceInfo> dataSourceInfos;\n\n\tpublic DataSourceHelper(List<SchemaManager.DataSourceInfo> dataSourceInfos) {\n\t\tthis.dataSourceInfos = dataSourceInfos.stream()\n\t\t\t\t.collect(Collectors.toMap(SchemaManager.DataSourceInfo::getName, Function.identity()));\n\t}\n\n\tpublic DataSource getDefault() throws RepositoryException {\n\t\treturn get(\"default\");\n\t}\n\n\tpublic DataSource get(String name) throws RepositoryException {\n\t\tSchemaManager.DataSourceInfo info = this.dataSourceInfos.get(name);\n\t\tif (info == null) {\n\t\t\tint idx = name.indexOf('.');\n\t\t\tif (idx > 0) {\n\t\t\t\treturn get(name.substring(idx + 1));\n\t\t\t}\n\t\t\treturn getDefault();\n\t\t} else {\n\t\t\treturn get(info);\n\t\t}\n\t}\n\n\tpublic DataSource get(SchemaManager.DataSourceInfo dataSourceInfo) throws RepositoryException {\n\t\tDataSource dataSource = dataSourceMap.get(dataSourceInfo.getName());\n\t\tif (dataSource == null) {\n\t\t\tdataSource = new DataRepositoryImpl();\n\t\t\tdataSource.initialize(dataSourceInfo.getResourceUri());\n\t\t\tdataSourceMap.put(dataSourceInfo.getName(), dataSource);\n\t\t}\n\t\treturn dataSource;\n\t}\n\n\tpublic <R> R createRepository(SchemaManager.RepoInfo repoInfo)\n\t\t\tthrows RepositoryException, InstantiationException, IllegalAccessException {\n\t\tDataSource dataSource = get(repoInfo.getDataSource().getName());\n\t\tR repo = ((Class<? extends R>) repoInfo.getImplementation()).newInstance();\n\t\t((DataSourceAware) repo).setDataSource(dataSource);\n\t\treturn repo;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/util/importexport/Exporter.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.util.importexport;\n\nimport tigase.db.AbstractAuthRepositoryWithCredentials;\nimport tigase.db.AuthRepository;\nimport tigase.db.UserRepository;\nimport tigase.util.datetime.TimestampHelper;\nimport tigase.util.ui.console.CommandlineParameter;\nimport tigase.vhosts.VHostItem;\nimport tigase.vhosts.VHostJDBCRepository;\nimport tigase.xml.Element;\nimport tigase.xml.XMLUtils;\nimport tigase.xmpp.impl.Privacy;\nimport tigase.xmpp.impl.VCardTemp;\nimport tigase.xmpp.impl.roster.RosterAbstract;\nimport tigase.xmpp.impl.roster.RosterElement;\nimport tigase.xmpp.impl.roster.RosterFlat;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.io.BufferedWriter;\nimport java.io.Writer;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\nimport java.time.ZoneOffset;\nimport java.time.format.DateTimeFormatter;\nimport java.util.*;\nimport java.util.logging.Logger;\n\npublic class Exporter {\n\n\tprivate static final Logger log = Logger.getLogger(Exporter.class.getSimpleName());\n\tprivate static final TimestampHelper TIMESTAMP_FORMATTER = new TimestampHelper();\n\n\tpublic static final CommandlineParameter EXPORT_MAM_SINCE = new CommandlineParameter.Builder(null, \"export-mam-since\").description(\"Export MAM archive since\").type(\n\t\t\tLocalDateTime.class).required(false).build();\n\tpublic static final CommandlineParameter EXPORT_MAM_BATCH_SIZE = new CommandlineParameter.Builder(null,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"export-mam-batch-size\").description(\n\t\t\t\"Export MAM archive batch size\").type(Integer.class).defaultValue(\"50000\").required(false).build();\n\n\tprivate static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ISO_LOCAL_DATE;\n\tprivate static final DateTimeFormatter DATETIME_FORMAT = DateTimeFormatter.ISO_LOCAL_DATE_TIME;\n\tpublic static Optional<Date> getExportMAMSinceValue() {\n\t\treturn EXPORT_MAM_SINCE.getValue().map(str -> {\n\t\t\ttry {\n\t\t\t\treturn TIMESTAMP_FORMATTER.parseTimestamp(str);\n\t\t\t} catch (Exception ex) {\n\t\t\t\ttry {\n\t\t\t\t\tLocalDateTime ts =  parseLocalDate(str);\n\t\t\t\t\treturn Date.from(ts.toInstant(ZoneOffset.UTC));\n\t\t\t\t} catch (Exception ex2) {\n\t\t\t\t\tthrow new RuntimeException(\"Could not parse \" + str + \" as timestamp\", ex);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic static Integer getExportMAMBatchSize() {\n\t\treturn EXPORT_MAM_BATCH_SIZE.getValue().map(Integer::parseInt).orElse(50000);\n\t}\n\n\tprivate static LocalDateTime parseLocalDate(String str) throws Exception {\n\t\ttry {\n\t\t\treturn LocalDateTime.parse(str, DATETIME_FORMAT);\n\t\t} catch (Exception ex1) {\n\t\t\treturn LocalDateTime.of(LocalDate.parse(str, DATE_FORMAT), LocalTime.MIDNIGHT);\n\t\t}\n\t}\n\n\tpublic static Date parseTimestamp(String str) {\n\t\ttry {\n\t\t\treturn TIMESTAMP_FORMATTER.parseTimestamp(str);\n\t\t} catch (Exception ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n\tpublic static void exportInclude(Writer parentWriter, Path rootPath, Path filePath, RepositoryManager.ThrowingConsumer<Writer> writerConsumer) throws Exception {\n\t\tFiles.createDirectories(filePath.getParent());\n\t\tparentWriter.append(\"<xi:include href='\").append(rootPath.relativize(filePath).toString()).append(\"'/>\\n\");\n\t\topenXmlFile(filePath, writerConsumer);\n\t}\n\n\tpublic static void openXmlFile(Path filePath, RepositoryManager.ThrowingConsumer<Writer> writerConsumer) throws Exception {\n\t\ttry (BufferedWriter writer = Files.newBufferedWriter(filePath)) {\n\t\t\twriter.write(\"<?xml version='1.0' encoding='UTF-8'?>\\n\");\n\t\t\twriterConsumer.accept(writer);\n\t\t}\n\t}\n\n\tprivate final RepositoryHolder repositoryHolder;\n\tprivate final VHostJDBCRepository vhostRepository;\n\tprivate final List<RepositoryManagerExtension> extensions;\n\tprivate final Path rootPath;\n\t\n\tpublic Exporter(RepositoryHolder repositoryHolder, VHostJDBCRepository vhostRepository, List<RepositoryManagerExtension> extensions, Path rootPath) {\n\t\tthis.repositoryHolder = repositoryHolder;\n\t\tthis.vhostRepository = vhostRepository;\n\t\tthis.extensions = extensions;\n\t\tthis.rootPath = rootPath;\n\n\t\tgetExportMAMSinceValue().ifPresent(date -> log.info(\"exporting MAM since: \" + date));\n\t\tlog.info(\"exporting MAM in batch size of \" + getExportMAMBatchSize() + \" messages\");\n\t}\n\n\tpublic void export(String fileName) throws Exception {\n\t\topenXmlFile(rootPath.resolve(fileName), rootWriter -> {\n\t\t\trootWriter.append(\"<server-data xmlns='urn:xmpp:pie:0' xmlns:xi='http://www.w3.org/2001/XInclude'>\\n\");\n\t\t\tfor (VHostItem item : vhostRepository.allItems()) {\n\t\t\t\tif (\"default\".equals(item.getKey())) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tPath domainFileName = rootPath.resolve(item.getKey() + \".xml\");\n\t\t\t\texportInclude(rootWriter, rootPath, domainFileName,\n\t\t\t\t\t\t\t  domainWriter -> exportDomain(domainFileName, item.getKey(), domainWriter));\n\t\t\t}\n\t\t\trootWriter.append(\"</server-data>\");\n\t\t});\n\t}\n\n\tprotected void exportDomain(Path domainFilePath, String domain, Writer writer) throws Exception {\n\t\tlog.info(\"exporting domain \" + domain + \" data...\");\n\t\twriter.append(\"<host xmlns='urn:xmpp:pie:0' xmlns:xi='http://www.w3.org/2001/XInclude' jid=\\\"\")\n\t\t\t\t.append(XMLUtils.escape(domain))\n\t\t\t\t.append(\"\\\">\\n\");\n\n\t\t// exporting domain users\n\t\tUserRepository userRepository = repositoryHolder.getRepository(UserRepository.class, domain);\n\t\tList<BareJID> users = userRepository.getUsers();\n\t\tint exportedUsers = 0;\n\t\tfor (BareJID user : users) {\n\t\t\tif (user.getLocalpart() == null || !domain.equals(user.getDomain())) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tPath userFilePath = domainFilePath.resolveSibling(domain).resolve(user.getLocalpart() + \".xml\");\n\t\t\texportInclude(writer, rootPath, userFilePath, userWriter -> exportUser(userRepository, userFilePath, user, userWriter));\n\t\t\texportedUsers++;\n\t\t}\n\n\t\t// exporting additional data, ie. components running on subdomains...\n\t\tfor (RepositoryManagerExtension extension : extensions) {\n\t\t\textension.exportDomainData(domain, writer);\n\t\t}\n\n\t\tlog.info(\"exported domain \" + domain + \" with \" + exportedUsers + \" users\");\n\n\t\twriter.append(\"</host>\");\n\t}\n\n\tprotected void exportUser(UserRepository userRepository, Path userFilePath, BareJID user, Writer writer) throws Exception {\n\t\tlog.info(\"exporting user \" + user + \" data...\");\n\t\twriter.append(\"<user xmlns='urn:xmpp:pie:0' xmlns:xi='http://www.w3.org/2001/XInclude' name=\\\"\")\n\t\t\t\t.append(XMLUtils.escape(user.getLocalpart()))\n\t\t\t\t.append(\"\\\"\");\n\n\n\t\tAuthRepository authRepository = repositoryHolder.getRepository(AbstractAuthRepositoryWithCredentials.class, user.getDomain());\n\t\ttry {\n\t\t\tAuthRepository.AccountStatus accountStatus = authRepository.getAccountStatus(user);\n\t\t\twriter.append(\" xmlns:tigase=\\\"tigase:xep-0227:user:0\\\"\");\n\t\t\twriter.append(\" tigase:status=\\\"\").append(accountStatus.name()).append(\"\\\"\");\n\t\t} catch (Exception ex) {\n\t\t\tlog.severe(\"error for \" + user + \" using \" + authRepository.getClass().getSimpleName());\n\t\t\tthrow ex;\n\t\t}\n\n\t\twriter.append(\">\");\n\t\t// roster\n\t\tString rosterStr = userRepository.getData(user, null, RosterAbstract.ROSTER, null);\n\t\tif (rosterStr != null) {\n\t\t\tMap<BareJID, RosterElement> roster = new LinkedHashMap<>();\n\t\t\tRosterFlat.parseRosterUtil(rosterStr, roster, null);\n\t\t\twriter.append(\"<query xmlns='jabber:iq:roster'>\");\n\t\t\tfor (RosterElement re : roster.values()) {\n\t\t\t\tElement rosterItem = re.getRosterItem();\n\t\t\t\tif (re.getMixParticipantId() != null) {\n\t\t\t\t\trosterItem.addChild(new Element(\"channel\", new String[] { \"xmlns\", \"participant-id\" }, new String[] { \"urn:xmpp:mix:roster:0\", re.getMixParticipantId() }));\n\t\t\t\t}\n\t\t\t\twriter.append(rosterItem.toString());\n\t\t\t}\n\t\t\twriter.append(\"</query>\");\n\t\t}\n\t\t// vcard-temp\n\t\tString vcardTemp = userRepository.getData(user, \"public/vcard-temp\", VCardTemp.VCARD_KEY);\n\t\tif (vcardTemp != null) {\n\t\t\twriter.append(vcardTemp);\n\t\t}\n\t\t// privacy lists\n\t\tString defListName = userRepository.getData(user, Privacy.PRIVACY, Privacy.DEFAULT);\n\t\tString[] subnodes = userRepository.getSubnodes(user, Privacy.PRIVACY);\n\t\tif (subnodes != null && subnodes.length > 0) {\n\t\t\twriter.append(\"<query xmlns='jabber:iq:privacy'>\");\n\t\t\tif (defListName != null) {\n\t\t\t\twriter.append(\"<default name=\\\"\").append(XMLUtils.escape(defListName)).append(\"\\\"/>\");\n\t\t\t\tfor (String node : subnodes) {\n\t\t\t\t\tString list = userRepository.getData(user, Privacy.PRIVACY + \"/\" + node, Privacy.PRIVACY_LIST);\n\t\t\t\t\tif (list != null) {\n\t\t\t\t\t\twriter.append(list);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\twriter.append(\"</query>\");\n\t\t}\n\n\t\tPath userDirPath = userFilePath.getParent().resolve(user.getLocalpart());\n\t\tfor (RepositoryManagerExtension extension : extensions) {\n\t\t\textension.exportUserData(userDirPath, user, writer);\n\t\t}\n\n\t\twriter.append(\"</user>\");\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/util/importexport/ImportXMLHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.util.importexport;\n\nimport tigase.xml.SimpleHandler;\n\nimport java.util.HashMap;\nimport java.util.logging.Logger;\n\npublic class ImportXMLHandler\n\t\timplements SimpleHandler {\n\tprivate static final Logger log = Logger.getLogger(ImportXMLHandler.class.getSimpleName());\n\n\tprivate final Importer importer;\n\n\tpublic ImportXMLHandler(Importer importer) {\n\t\tthis.importer = importer;\n\t}\n\n\t@Override\n\tpublic void error(String s) {\n\t\t// ignore\n\t\tlog.severe(\"error: \" + s);\n\t}\n\n\t@Override\n\tpublic void startElement(StringBuilder name, StringBuilder[] att_names, StringBuilder[] att_values) {\n\t\tHashMap<String, String> attrs = new HashMap<>();\n\t\tif (att_names != null && att_values != null) {\n\t\t\tfor (int i = 0; i < att_names.length; i++) {\n\t\t\t\tif (att_names[i] != null && att_values[i] != null) {\n\t\t\t\t\tattrs.put(att_names[i].toString(), att_values[i].toString());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\timporter.startElement(name.toString(), attrs);\n\t}\n\n\t@Override\n\tpublic void elementCData(StringBuilder stringBuilder) {\n\t\timporter.elementCData(stringBuilder.toString());\n\t}\n\n\t@Override\n\tpublic boolean endElement(StringBuilder stringBuilder) {\n\t\treturn importer.endElement(stringBuilder.toString());\n\t}\n\n\t@Override\n\tpublic void otherXML(StringBuilder other) {\n\t\tlog.fine(\"Other XML content: \" + other);\n\t}\n\n\tprivate Object parserState;\n\n\t@Override\n\tpublic void saveParserState(Object o) {\n\t\tparserState = o;\n\t}\n\n\t@Override\n\tpublic Object restoreParserState() {\n\t\treturn parserState;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/util/importexport/Importer.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.util.importexport;\n\nimport tigase.db.AbstractAuthRepositoryWithCredentials;\nimport tigase.db.AuthRepository;\nimport tigase.db.UserRepository;\nimport tigase.vhosts.VHostItem;\nimport tigase.vhosts.VHostJDBCRepository;\nimport tigase.xml.Element;\nimport tigase.xml.SimpleParser;\nimport tigase.xmpp.impl.VCardTemp;\nimport tigase.xmpp.impl.roster.RosterAbstract;\nimport tigase.xmpp.impl.roster.RosterElement;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.*;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\npublic class Importer {\n\tprivate static final Logger log = Logger.getLogger(Importer.class.getSimpleName());\n\tprivate static final SimpleParser parser = new SimpleParser();\n\tenum State {\n\t\tbegining,\n\t\tserverData,\n\t\thost,\n\t\tuser\n\t}\n\n\tprivate final RepositoryHolder repositoryHolder;\n\tprivate final VHostJDBCRepository vhostRepository;\n\tprivate final Path rootPath;\n\tprivate State state = State.begining;\n\tprivate String domain;\n\tprivate BareJID user;\n\tprivate Stack<Element> readElements = new Stack<>();\n\tprivate Stack<String> elementStack = new Stack<>();\n\tprivate final List<RepositoryManagerExtension> extensions;\n\tprivate ImporterExtension activeExtension = null;\n\tprivate int usersCount = 0;\n\n\tpublic Importer(RepositoryHolder repositoryHolder, VHostJDBCRepository vhostRepository, List<RepositoryManagerExtension> extensions, Path rootPath) {\n\t\tthis.repositoryHolder = repositoryHolder;\n\t\tthis.vhostRepository = vhostRepository;\n\t\tthis.rootPath = rootPath;\n\t\tthis.extensions = extensions;\n\t}\n\n\tprotected void startElement(String name, Map<String, String> attrs) {\n\t\telementStack.push(name);\n\t\tif (name.equals(\"xi:include\")) {\n\t\t\tString path = attrs.get(\"href\");\n\t\t\ttry {\n\t\t\t\tprocess(rootPath.resolve(path));\n\t\t\t} catch (Throwable ex) {\n\t\t\t\tthrow new RuntimeException(ex);\n\t\t\t}\n\t\t} else {\n\t\t\tboolean handled = switch (state) {\n\t\t\t\tcase begining -> switch (name) {\n\t\t\t\t\tcase \"server-data\" -> {\n\t\t\t\t\t\tstate = State.serverData;\n\t\t\t\t\t\tyield true;\n\t\t\t\t\t}\n\t\t\t\t\tdefault -> false;\n\t\t\t\t};\n\t\t\t\tcase serverData -> switch (name) {\n\t\t\t\t\tcase \"host\" -> {\n\t\t\t\t\t\tstate = State.host;\n\t\t\t\t\t\tdomain = attrs.get(\"jid\");\n\t\t\t\t\t\tusersCount = 0;\n\t\t\t\t\t\tlog.info(\"importing domain \" + domain + \" data...\");\n\t\t\t\t\t\tif (vhostRepository.getItem(domain) == null) {\n\t\t\t\t\t\t\tVHostItem item = vhostRepository.getItemInstance();\n\t\t\t\t\t\t\titem.setKey(domain);\n\t\t\t\t\t\t\tvhostRepository.addItem(item);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tyield true;\n\t\t\t\t\t}\n\t\t\t\t\tdefault -> false;\n\t\t\t\t};\n\t\t\t\tcase host -> switch (name) {\n\t\t\t\t\tcase \"user\" -> {\n\t\t\t\t\t\tstate = State.user;\n\t\t\t\t\t\tuser = BareJID.bareJIDInstanceNS(attrs.get(\"name\"), domain);\n\t\t\t\t\t\tlog.info(\"importing user \" + user + \" data...\");\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tUserRepository userRepository = repositoryHolder.getRepository(\n\t\t\t\t\t\t\t\t\tUserRepository.class, user.getDomain());\n\t\t\t\t\t\t\tif (!userRepository.userExists(user)) {\n\t\t\t\t\t\t\t\tuserRepository.addUser(user);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tAuthRepository.AccountStatus accountStatus = Optional.ofNullable(attrs.get(\"tigase:status\"))\n\t\t\t\t\t\t\t\t\t.map(AuthRepository.AccountStatus::valueOf)\n\t\t\t\t\t\t\t\t\t.orElse(AuthRepository.AccountStatus.active);\n\t\t\t\t\t\t\tAuthRepository authRepository = repositoryHolder.getRepository(AbstractAuthRepositoryWithCredentials.class, user.getDomain());\n\t\t\t\t\t\t\tauthRepository.setAccountStatus(user, accountStatus);\n\t\t\t\t\t\t} catch (Throwable ex) {\n\t\t\t\t\t\t\tthrow new RuntimeException(ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tyield true;\n\t\t\t\t\t}\n\t\t\t\t\tdefault -> false;\n\t\t\t\t};\n\t\t\t\tdefault -> false;\n\t\t\t};\n\t\t\tif (!handled) {\n\t\t\t\ttry {\n\t\t\t\t\tif (activeExtension == null) {\n\t\t\t\t\t\tfor (RepositoryManagerExtension extension : extensions) {\n\t\t\t\t\t\t\tactiveExtension = extensionStartImport(extension, name, attrs);\n\t\t\t\t\t\t\tif (activeExtension != null) {\n\t\t\t\t\t\t\t\thandled = true;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\thandled = activeExtension.startElement(name, attrs);\n\t\t\t\t\t}\n\t\t\t\t} catch (Throwable ex) {\n\t\t\t\t\tthrow new RuntimeException(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!handled) {\n\t\t\t\tElement el = new Element(name);\n\t\t\t\tel.setAttributes(attrs);\n\t\t\t\treadElements.push(el);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate ImporterExtension extensionStartImport(RepositoryManagerExtension extension, String name,\n\t\t\t\t\t\t\t\t\t\t\t\t   Map<String, String> attrs) throws Exception {\n\t\treturn switch (state) {\n\t\t\tcase host -> extension.startImportDomainData(domain, name, attrs);\n\t\t\tcase user -> extension.startImportUserData(user, name, attrs);\n\t\t\tdefault -> null;\n\t\t};\n\t}\n\n\tprotected void elementCData(String cdata) {\n\t\tif (!readElements.isEmpty()) {\n\t\t\treadElements.peek().addCData(cdata);\n\t\t}\n\t}\n\n\tinterface ThrowingFunction<IN, OUT> {\n\n\t\tOUT apply(IN in) throws Exception;\n\t}\n\n\tprivate boolean processReadElement(ThrowingFunction<Element, Boolean> func) throws Exception {\n\t\tif (readElements.size() == 1) {\n\t\t\tElement el = readElements.pop();\n\t\t\treturn func.apply(el);\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprotected boolean endElement(String name) {\n\t\ttry {\n\t\t\tif (name.equals(elementStack.peek())) {\n\t\t\t\tif (name.equals(\"xi:include\")) {\n\t\t\t\t\telementStack.pop();\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tif (activeExtension != null) {\n\t\t\t\t\tboolean handled = activeExtension.endElement(name);\n\t\t\t\t\tif (!handled) {\n\t\t\t\t\t\tif (!readElements.isEmpty()) {\n\t\t\t\t\t\t\tElement el = readElements.pop();\n\t\t\t\t\t\t\tif (readElements.isEmpty()) {\n\t\t\t\t\t\t\t\tif (!activeExtension.handleElement(el)) {\n\t\t\t\t\t\t\t\t\tlog.warning(\"read unknown element: \" + el);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\treadElements.peek().addChild(el);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tactiveExtension.close();\n\t\t\t\t\t\t\tactiveExtension = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tboolean handled = switch (state) {\n\t\t\t\t\t\tcase serverData -> switch (name) {\n\t\t\t\t\t\t\tcase \"server-data\" -> {\n\t\t\t\t\t\t\t\tstate = State.begining;\n\t\t\t\t\t\t\t\tyield true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tdefault -> false;\n\t\t\t\t\t\t};\n\t\t\t\t\t\tcase host -> switch (name) {\n\t\t\t\t\t\t\tcase \"host\" -> {\n\t\t\t\t\t\t\t\tstate = State.serverData;\n\t\t\t\t\t\t\t\tlog.info(\"imported domain \" + domain + \" with \" + usersCount + \" users\");\n\t\t\t\t\t\t\t\tusersCount = 0;\n\t\t\t\t\t\t\t\tdomain = null;\n\t\t\t\t\t\t\t\tyield true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tdefault -> false;\n\t\t\t\t\t\t};\n\t\t\t\t\t\tcase user -> switch (name) {\n\t\t\t\t\t\t\tcase \"user\" -> {\n\t\t\t\t\t\t\t\tstate = State.host;\n\t\t\t\t\t\t\t\tlog.fine(\"imported user \" + user);\n\t\t\t\t\t\t\t\tusersCount++;\n\t\t\t\t\t\t\t\tuser = null;\n\t\t\t\t\t\t\t\tyield true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcase \"query\" -> processReadElement(query -> switch (query.getXMLNS()) {\n\t\t\t\t\t\t\t\tcase \"jabber:iq:roster\" -> {\n\t\t\t\t\t\t\t\t\tsaveRoster(query);\n\t\t\t\t\t\t\t\t\tyield true;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tdefault -> false;\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tcase \"vCard\" -> processReadElement(vcard -> switch (vcard.getXMLNS()) {\n\t\t\t\t\t\t\t\tcase \"vcard-temp\" -> {\n\t\t\t\t\t\t\t\t\tUserRepository userRepository = repositoryHolder.getRepository(\n\t\t\t\t\t\t\t\t\t\t\tUserRepository.class, user.getDomain());\n\t\t\t\t\t\t\t\t\tuserRepository.setData(user, \"public/vcard-temp\", VCardTemp.VCARD_KEY,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   vcard.toString());\n\t\t\t\t\t\t\t\t\tyield true;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tdefault -> false;\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tdefault -> false;\n\t\t\t\t\t\t};\n\t\t\t\t\t\tdefault -> false;\n\t\t\t\t\t};\n\t\t\t\t\tif (!handled) {\n\t\t\t\t\t\tif (!readElements.isEmpty()) {\n\t\t\t\t\t\t\tElement el = readElements.pop();\n\t\t\t\t\t\t\tif (!readElements.isEmpty()) {\n\t\t\t\t\t\t\t\tElement parentEl = readElements.peek();\n\t\t\t\t\t\t\t\tif (parentEl != null) {\n\t\t\t\t\t\t\t\t\tparentEl.addChild(el);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tlog.warning(\"read unknown element: \" + el);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telementStack.pop();\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\tlog.severe(\n\t\t\t\t\t\t\"found close tag for \" + name + \" without start element at: \" + elementStack.stream().toList());\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} catch (Throwable ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n\tprivate void saveRoster(Element query) throws Exception {\n\t\tList<Element> children = query.findChildren(el -> \"item\".equals(el.getName()));\n\t\tList<RosterElement> roster = new ArrayList<>();\n\t\tif (children != null) {\n\t\t\tfor (Element child : children) {\n\t\t\t\tList<String> groups = child.mapChildren(el -> \"group\".equals(el.getName()),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tel -> el.getCData());\n\t\t\t\tRosterElement re = new RosterElement(\n\t\t\t\t\t\tJID.jidInstanceNS(child.getAttributeStaticStr(\"jid\")),\n\t\t\t\t\t\tchild.getAttributeStaticStr(\"name\"),\n\t\t\t\t\t\tgroups == null ? null : groups.toArray(String[]::new));\n\t\t\t\tre.setSubscription(subscriptionType(child));\n\t\t\t\tOptional.ofNullable(child.getChild(\"channel\", \"urn:xmpp:mix:roster:0\"))\n\t\t\t\t\t\t.map(el -> el.getAttributeStaticStr(\"participant-id\"))\n\t\t\t\t\t\t.ifPresent(re::setMixParticipantId);\n\t\t\t\troster.add(re);\n\t\t\t}\n\t\t}\n\t\tString rosterStr = roster.stream()\n\t\t\t\t.map(RosterElement::getRosterElement)\n\t\t\t\t.map(Element::toString)\n\t\t\t\t.collect(Collectors.joining());\n\t\tUserRepository userRepository = repositoryHolder.getRepository(\n\t\t\t\tUserRepository.class, user.getDomain());\n\t\tif (rosterStr.isEmpty()) {\n\t\t\tuserRepository.removeData(user, RosterAbstract.ROSTER);\n\t\t} else {\n\t\t\tuserRepository.setData(user, RosterAbstract.ROSTER, rosterStr);\n\t\t}\n\t\tlog.finest(\"importing user \" + user + \" roster...\");\n\t}\n\n\tprivate static RosterAbstract.SubscriptionType subscriptionType(Element rosterItem) {\n\t\treturn subscriptionType(rosterItem.getAttributeStaticStr(\"subscription\"),\n\t\t\t\t\t\t\t\trosterItem.getAttributeStaticStr(\"ask\"),\n\t\t\t\t\t\t\t\tBoolean.parseBoolean(rosterItem.getAttributeStaticStr(\"approved\")));\n\t}\n\n\tprivate static RosterAbstract.SubscriptionType subscriptionType(String subscr, String ask, boolean approved) {\n\t\tRosterAbstract.SubscriptionType type = RosterAbstract.SubscriptionType.none;\n\t\tif (subscr != null) {\n\t\t\ttry {\n\t\t\t\ttype = RosterAbstract.SubscriptionType.valueOf(subscr);\n\t\t\t\tboolean subscribe = \"subscribe\".equals(ask);\n\t\t\t\tif (approved) {\n\t\t\t\t\ttype = switch (type) {\n\t\t\t\t\t\tcase to -> RosterAbstract.SubscriptionType.to_pre_approved;\n\t\t\t\t\t\tcase none -> {\n\t\t\t\t\t\t\tif (subscribe) {\n\t\t\t\t\t\t\t\tyield RosterAbstract.SubscriptionType.none_pending_out_pre_approved;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tyield RosterAbstract.SubscriptionType.none_pre_approved;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdefault -> type;\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\ttype = switch (type) {\n\t\t\t\t\tcase none -> RosterAbstract.SubscriptionType.none_pending_out;\n\t\t\t\t\tcase from -> RosterAbstract.SubscriptionType.from_pending_out;\n\t\t\t\t\tdefault -> type;\n\t\t\t\t};\n\t\t\t} catch (IllegalArgumentException ex) {\n\t\t\t\t// in this case, lets assume that subscription is `none`\n\t\t\t}\n\t\t}\n\t\treturn type;\n\t}\n\t\n\tpublic void process(Path path) throws IOException {\n\t\ttry (BufferedReader reader = Files.newBufferedReader(path)) {\n\t\t\tprocess(reader);\n\t\t}\n\t}\n\n\tpublic void process(BufferedReader reader) throws IOException {\n\t\tImportXMLHandler importXMLHandler = new ImportXMLHandler(this);\n\t\twhile (reader.ready()) {\n\t\t\tString data = reader.readLine();\n\t\t\tparser.parse(importXMLHandler, data);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/util/importexport/ImporterExtension.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.util.importexport;\n\nimport tigase.xml.Element;\n\nimport java.util.Map;\n\npublic interface ImporterExtension {\n\n\tdefault boolean startElement(String name, Map<String, String> attrs) throws Exception {\n\t\treturn false;\n\t}\n\n\tboolean handleElement(Element element) throws Exception;\n\n\tdefault boolean endElement(String name) throws Exception {\n\t\treturn false;\n\t}\n\n\tdefault void close() throws Exception {\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/util/importexport/JabberIqPrivateExtension.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.util.importexport;\n\nimport tigase.db.UserRepository;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.io.Writer;\nimport java.nio.file.Path;\nimport java.util.Map;\nimport java.util.logging.Logger;\n\npublic class JabberIqPrivateExtension extends RepositoryManagerExtensionBase {\n\n\tprivate static final Logger log = Logger.getLogger(JabberIqPrivateExtension.class.getSimpleName());\n\n\t@Override\n\tpublic void exportDomainData(String domain, Writer writer) throws Exception {\n\t\t// nothing to do...\n\t}\n\n\t@Override\n\tpublic void exportUserData(Path userDirPath, BareJID user, Writer writer) throws Exception {\n\t\tUserRepository userRepository = getRepository(UserRepository.class, user.getDomain());\n\t\tString[] keys = userRepository.getKeys(user, \"jabber:iq:private\");\n\t\tif (keys != null) {\n\t\t\twriter.append(\"<query xmlns='jabber:iq:private'>\");\n\t\t\tfor (String key : keys) {\n\t\t\t\tString data = userRepository.getData(user, key);\n\t\t\t\tif (data != null) {\n\t\t\t\t\twriter.append(data);\n\t\t\t\t}\n\t\t\t}\n\t\t\twriter.append(\"</query>\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic ImporterExtension startImportUserData(BareJID userJid, String name,\n\t\t\t\t\t\t\t\t\t\t\t\t Map<String, String> attrs) throws Exception {\n\t\tif (\"query\".equals(name) && \"jabber:iq:private\".equals(attrs.get(\"xmlns\"))) {\n\t\t\tlog.finest(\"importing user \" + userJid + \" jabber:iq:private...\");\n\t\t\treturn new JabberIqPrivateImporterExtension(getRepository(UserRepository.class, userJid.getDomain()), userJid);\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static class JabberIqPrivateImporterExtension extends AbstractImporterExtension {\n\n\t\tprivate final UserRepository userRepository;\n\t\tprivate final BareJID user;\n\n\t\tpublic JabberIqPrivateImporterExtension(UserRepository userRepository, BareJID user) {\n\t\t\tthis.userRepository = userRepository;\n\t\t\tthis.user = user;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean handleElement(Element item) throws Exception {\n\t\t\tuserRepository.setData(user, \"jabber:iq:private\", item.getName() + item.getXMLNS(),\n\t\t\t\t\t item.toString());\n\t\t\treturn true;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/util/importexport/RepositoryHolder.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.util.importexport;\n\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.db.util.SchemaManager;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Function;\n\npublic class RepositoryHolder {\n\n\tprivate record RepoCacheKey(Class<?> ifc, String name) {\n\n\t}\n\n\tprivate final DataSourceHelper dataSourceHelper;\n\tprivate final List<SchemaManager.RepoInfo> allRepoInfos;\n\tprivate final Map<RepoCacheKey, Object> repoCache = new ConcurrentHashMap<>();\n\tprivate final Map<Class, Function> prepFuncs = new ConcurrentHashMap<>();\n\n\tpublic RepositoryHolder(DataSourceHelper dataSourceHelper,\n\t\t\t\t\t\t\tList<SchemaManager.RepoInfo> allRepoInfos) {\n\t\tthis.dataSourceHelper = dataSourceHelper;\n\t\tthis.allRepoInfos = allRepoInfos;\n\t}\n\n\tpublic <X> X getDefaultRepository(Class<X> ifc)\n\t\t\tthrows RepositoryException, InstantiationException, IllegalAccessException {\n\t\treturn getRepository(ifc, \"default\");\n\t}\n\n\tpublic <X> X getRepository(Class<X> ifc, String name)\n\t\t\tthrows RepositoryException, InstantiationException, IllegalAccessException {\n\t\tRepoCacheKey key = new RepoCacheKey(ifc, name);\n\t\tX repo = (X) repoCache.get(key);\n\t\tif (repo == null) {\n\t\t\tList<SchemaManager.RepoInfo> matchingClasses = allRepoInfos.stream()\n\t\t\t\t\t.filter(repoInfo -> ifc.isAssignableFrom(repoInfo.getImplementation()))\n\t\t\t\t\t.toList();\n\t\t\tSchemaManager.RepoInfo repoInfo = findRepoInfo(matchingClasses, name).or(\n\t\t\t\t\t() -> findRepoInfo(matchingClasses, \"default\")).orElseThrow();\n\t\t\trepo = prepareRepository((X) dataSourceHelper.createRepository(repoInfo));\n\t\t\trepoCache.put(key, repo);\n\t\t}\n\t\treturn repo;\n\t}\n\n\tpublic <X> void registerPrepFn(Class<X> ifc, Function<X, X> func) {\n\t\tprepFuncs.put(ifc, func);\n\t}\n\n\tprotected <X> X prepareRepository(X repo)\n\t\t\tthrows RepositoryException, InstantiationException, IllegalAccessException {\n\t\tfor (Map.Entry<Class, Function> e : prepFuncs.entrySet()) {\n\t\t\tif (e.getKey().isAssignableFrom(repo.getClass())) {\n\t\t\t\trepo = (X) e.getValue().apply(repo);\n\t\t\t}\n\t\t}\n\t\treturn repo;\n\t}\n\n\tprivate Optional<SchemaManager.RepoInfo> findRepoInfo(List<SchemaManager.RepoInfo> repoInfos, String name) {\n\t\treturn repoInfos.stream().filter(repoInfo -> name.equals(repoInfo.getDataSource().getName())).findFirst();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/util/importexport/RepositoryManager.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.util.importexport;\n\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.conf.ConfigHolder;\nimport tigase.conf.ConfigReader;\nimport tigase.db.UserRepository;\nimport tigase.db.comp.ConfigRepository;\nimport tigase.db.util.SchemaManager;\nimport tigase.kernel.core.BeanConfig;\nimport tigase.kernel.core.Kernel;\nimport tigase.util.ClassUtil;\nimport tigase.util.ui.console.CommandlineParameter;\nimport tigase.util.ui.console.ParameterParser;\nimport tigase.util.ui.console.Task;\nimport tigase.vhosts.VHostItemDefaults;\nimport tigase.vhosts.VHostItemExtensionManager;\nimport tigase.vhosts.VHostJDBCRepository;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.lang.reflect.Field;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.*;\nimport java.util.function.Function;\nimport java.util.logging.Level;\nimport java.util.logging.LogManager;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\npublic class RepositoryManager {\n\n\tpublic static boolean isSet(CommandlineParameter parameter) throws Exception {\n\t\tString value = parameter.getValue().or(parameter::getDefaultValue).orElseThrow();\n\t\treturn Boolean.parseBoolean(value);\n\t}\n\t\n\tprivate static final Logger log = Logger.getLogger(RepositoryManager.class.getSimpleName());\n\n\tprivate final CommandlineParameter EXPORT_TO = new CommandlineParameter.Builder(null, \"to\").description(\n\t\t\t\t\t\"Path to export data to\")\n\t\t\t.required(true)\n\t\t\t.build();\n\tprivate final CommandlineParameter IMPORT_FROM = new CommandlineParameter.Builder(null, \"from\").description(\n\t\t\t\t\t\"Path to import data from\")\n\t\t\t.required(true)\n\t\t\t.build();\n\tprivate final CommandlineParameter TDSL_CONFIG_FILE = new CommandlineParameter.Builder(null,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"config-file\").description(\n\t\t\t\"Path to Tigase XMPP Server config file\")\n\t\t\t.defaultValue(ConfigHolder.TDSL_CONFIG_FILE_DEF)\n\t\t\t.required(false)\n\t\t\t.build();\n\n\tprivate final CommandlineParameter DEBUG = new CommandlineParameter.Builder(null, \"debug\").description(\n\t\t\t\"Enable verbose logging\").defaultValue(\"false\").required(false).requireArguments(false).build();\n\n\tprivate static final String LOGGING_CONFIG = \"\"\"\nhandlers = java.util.logging.ConsoleHandler\n.level = ALL\njava.util.logging.ConsoleHandler.formatter = tigase.util.log.LogFormatter\n\t\t\t\"\"\";\n\tpublic void execute(String args[]) throws Exception {\n\t\tconfigureLogging(Level.SEVERE);\n\t\tString scriptName = System.getProperty(\"scriptName\");\n\t\tParameterParser parser = new ParameterParser(true);\n\n\t\textensions = ClassUtil.getClassesImplementing(RepositoryManagerExtension.class)\n\t\t\t\t.stream()\n\t\t\t\t.map(clazz -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn clazz.getConstructor().newInstance();\n\t\t\t\t\t} catch (Throwable ex) {\n\t\t\t\t\t\tlog.log(Level.WARNING, ex.getMessage(), ex);\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.filter(Objects::nonNull)\n\t\t\t\t.toList();\n\t\t\n\t\tparser.setTasks(new Task[]{new Task.Builder().name(\"export-data\")\n\t\t\t\t\t\t\t\t\t\t   .description(\"Export data to XML\")\n\t\t\t\t\t\t\t\t\t\t   .additionalParameterSupplier(() -> Stream.concat(Stream.of(TDSL_CONFIG_FILE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  EXPORT_TO, DEBUG),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\textensions.stream()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.flatMap(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRepositoryManagerExtension::getExportParameters))\n\t\t\t\t\t\t\t\t\t\t\t\t   .toList())\n\t\t\t\t\t\t\t\t\t\t   .function(this::exportData).build(), new Task.Builder().name(\"import-data\")\n\t\t\t\t\t\t\t\t\t\t   .description(\"Import data from XML\")\n\t\t\t\t\t\t\t\t\t\t   .additionalParameterSupplier(() -> Stream.concat(Stream.of(TDSL_CONFIG_FILE,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  IMPORT_FROM, DEBUG),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\textensions.stream()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.flatMap(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tRepositoryManagerExtension::getImportParameters))\n\t\t\t\t\t\t\t\t\t\t\t\t   .toList())\n\t\t\t\t\t\t\t\t\t\t   .function(this::importData).build()});\n\t\tProperties props = null;\n\t\ttry {\n\t\t\tprops = parser.parseArgs(args);\n\t\t} catch (IllegalArgumentException ex) {\n\t\t\t// IllegalArgumentException is thrown if any of required parameters is missing\n\t\t\t// We can ignore it and just display help for this command.\n\t\t\tex.printStackTrace();\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, ex.getMessage(), ex);\n\t\t\t}\n\t\t}\n\t\tOptional<Task> task = parser.getTask();\n\t\tif (props != null && task.isPresent()) {\n\t\t\ttask.get().execute(props);\n\t\t} else {\n\t\t\tString executionCommand = null;\n\t\t\tif (scriptName != null) {\n\t\t\t\texecutionCommand = \"$ \" + scriptName + \" [task] [params-file.conf] [options]\" + \"\\n\\t\\t\" +\n\t\t\t\t\t\t\"if the option defines default then <value> is optional\";\n\t\t\t}\n\n\t\t\tSystem.out.println(parser.getHelp(executionCommand));\n\t\t}\n\t}\n\n\tpublic static void configureLogging(Level level) throws IOException {\n\t\tString config =  \"\"\"\nhandlers = java.util.logging.ConsoleHandler\n.level = ALL\ntigase.xml.level = SEVERE\njava.util.logging.ConsoleHandler.level = \"\"\" + level.getName() + \"\"\"\n\njava.util.logging.ConsoleHandler.formatter = tigase.util.log.LogFormatter\"\"\";\n\t\tbyte[] data = config.getBytes(StandardCharsets.UTF_8);\n\t\ttry (ByteArrayInputStream in = new ByteArrayInputStream(data)) {\n\t\t\tLogManager.getLogManager().reset();\n\t\t\tLogManager.getLogManager()\n\t\t\t\t\t.updateConfiguration(in, (k) -> k.endsWith(\".handlers\")\n\t\t\t\t\t\t\t\t\t\t\t\t\t? ((o, n) -> (o == null ? n : o))\n\t\t\t\t\t\t\t\t\t\t\t\t\t: ((o, n) -> n));\n\t\t}\n\t}\n\n\tprivate Path rootPath;\n\tprivate Map<String, Object> config;\n\tprivate Kernel kernel;\n\tprivate DataSourceHelper dataSourceHelper;\n\tprivate RepositoryHolder repositoryHolder;\n\tprivate List<RepositoryManagerExtension> extensions = new ArrayList<>();\n\tprivate VHostJDBCRepository vhostRepository;\n\n\tprivate void initialize(CommandlineParameter fileParam, Properties properties)\n\t\t\tthrows ConfigReader.ConfigException, IOException, ClassNotFoundException, RepositoryException,\n\t\t\t\t   InstantiationException, IllegalAccessException, NoSuchFieldException {\n\t\trootPath = Paths.get(properties.getProperty(fileParam.getFullName().get()));\n\t\tConfigHolder holder = new ConfigHolder();\n\t\tholder.loadConfiguration(new String[] { ConfigHolder.TDSL_CONFIG_FILE_KEY,\n\t\t\t\t\t\t\t\t\t\t\t\tTDSL_CONFIG_FILE.getValue().orElseThrow()\n\t\t});\n\t\tconfig = holder.getProperties();\n\t\tconfig.remove(\"cluster-mode\");\n\t\tkernel = SchemaManager.prepareKernel(config);\n\t\tList<BeanConfig> repoBeans = SchemaManager.getRepositoryBeans(kernel, SchemaManager.getRepositoryClasses(), config);\n\n\t\tList<SchemaManager.RepoInfo> repositories = SchemaManager.getRepositories(kernel, repoBeans, config);\n\n\t\tMap<String, SchemaManager.RepoInfo> userRepoMap = repositories.stream()\n\t\t\t\t.filter(repoInfo -> UserRepository.class.isAssignableFrom(repoInfo.getImplementation()))\n\t\t\t\t.collect(Collectors.toMap(repoInfo -> repoInfo.getDataSource().getName(), Function.identity()));\n\n\t\tdataSourceHelper = new DataSourceHelper(\n\t\t\t\trepositories.stream().map(SchemaManager.RepoInfo::getDataSource).distinct().toList());\n\t\trepositoryHolder = new RepositoryHolder(dataSourceHelper, repositories);\n\n\t\tvhostRepository = new VHostJDBCRepository();\n\t\tvhostRepository.setRepo(repositoryHolder.getDefaultRepository(UserRepository.class));\n\t\tvhostRepository.setMainVHostName((String) config.get(\"default-virtual-host\"));\n\t\tvhostRepository.setExtensionManager(new VHostItemExtensionManager());\n\t\tvhostRepository.setVhostDefaultValues(new VHostItemDefaults());\n\t\tvhostRepository.reload();\n\t\t// mark vhostRepository as initialized to allow saving items (not enabled autoreload intentionally)\n\t\tField f = ConfigRepository.class.getDeclaredField(\"initialized\");\n\t\tf.setAccessible(true);\n\t\tf.set(vhostRepository, true);\n\t\t\n\t\tfor (RepositoryManagerExtension extension : extensions) {\n\t\t\textension.initialize(kernel, dataSourceHelper, repositoryHolder, rootPath);\n\t\t}\n\n\t\tLevel level = Boolean.parseBoolean(DEBUG.getValue().orElseThrow()) ? Level.FINEST : Level.INFO;\n\t\tconfigureLogging(level);\n\t\tlog.finest(\"extensions: \" + extensions);\n\t}\n\n\tprivate void importData(Properties properties) throws Exception {\n\t\tinitialize(IMPORT_FROM, properties);\n\t\tif (!Files.exists(rootPath)) {\n\t\t\tthrow new RuntimeException(\"Source directory does not exist\");\n\t\t}\n\n\t\tImporter importer = new Importer(repositoryHolder, vhostRepository, extensions, rootPath);\n\t\timporter.process(rootPath.resolve(\"server-data.xml\"));\n\t}\n\n\tprivate void exportData(Properties properties) throws Exception {\n\t\tinitialize(EXPORT_TO, properties);\n\n\t\tif (!Files.exists(rootPath)) {\n\t\t\tFiles.createDirectories(rootPath);\n\t\t}\n\n\t\tExporter exporter = new Exporter(repositoryHolder, vhostRepository, extensions, rootPath);\n\t\texporter.export(\"server-data.xml\");\n\t}\n\t\n\t@FunctionalInterface\n\tpublic interface ThrowingConsumer<X> {\n\t\tvoid accept(X x) throws Exception;\n\t}\n\n\tpublic static void main(String args[]) throws IOException, ConfigReader.ConfigException {\n\t\ttry {\n\t\t\tRepositoryManager repositoryManager = new RepositoryManager();\n\t\t\trepositoryManager.execute(args);\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.SEVERE, \"Error while executing task\", ex);\n\t\t} finally {\n\t\t\tSystem.exit(0);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/util/importexport/RepositoryManagerExtension.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.util.importexport;\n\nimport tigase.kernel.core.Kernel;\nimport tigase.util.ui.console.CommandlineParameter;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.io.Writer;\nimport java.nio.file.Path;\nimport java.util.Map;\nimport java.util.stream.Stream;\n\npublic interface RepositoryManagerExtension {\n\n\tdefault Stream<CommandlineParameter> getExportParameters() {\n\t\treturn Stream.empty();\n\t}\n\n\tdefault Stream<CommandlineParameter> getImportParameters() {\n\t\treturn Stream.empty();\n\t}\n\n\tdefault void initialize(Kernel kernel, DataSourceHelper dataSourceHelper,\n\t\t\t\t\t\t\tRepositoryHolder repositoryHolder, Path rootPath) {\n\t}\n\n\tvoid exportDomainData(String domain, Writer writer) throws Exception;\n\n\tvoid exportUserData(Path userDirPath, BareJID user, Writer writer) throws Exception;\n\n\tdefault ImporterExtension startImportDomainData(String domain, String name,\n\t\t\t\t\t\t\t\t\t\t\t\t\tMap<String, String> attrs) throws Exception {\n\t\treturn null;\n\t}\n\n\tdefault ImporterExtension startImportUserData(BareJID userJid, String name,\n\t\t\t\t\t\t\t\t\t\t\t\t  Map<String, String> attrs) throws Exception {\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/util/importexport/RepositoryManagerExtensionBase.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.util.importexport;\n\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.kernel.core.BeanConfig;\nimport tigase.kernel.core.Kernel;\n\nimport java.io.Writer;\nimport java.nio.file.Path;\nimport java.util.List;\nimport java.util.logging.Logger;\n\npublic abstract class RepositoryManagerExtensionBase\n\t\timplements RepositoryManagerExtension {\n\n\tprivate static final Logger log = Logger.getLogger(RepositoryManagerExtensionBase.class.getSimpleName());\n\n\tprivate Kernel kernel;\n\tprivate DataSourceHelper dataSourceHelper;\n\tprivate RepositoryHolder repositoryHolder;\n\tprivate Path rootPath;\n\n\t@Override\n\tpublic void initialize(Kernel kernel, DataSourceHelper dataSourceHelper,\n\t\t\t\t\t\t   RepositoryHolder repositoryHolder, Path rootPath) {\n\t\tthis.kernel = kernel;\n\t\tthis.dataSourceHelper = dataSourceHelper;\n\t\tthis.repositoryHolder = repositoryHolder;\n\t\tthis.rootPath = rootPath;\n\t}\n\n\tpublic <X> X getRepository(Class<X> ifc, String name)\n\t\t\tthrows RepositoryException, InstantiationException, IllegalAccessException {\n\t\treturn repositoryHolder.getRepository(ifc, name);\n\t}\n\n\tpublic List<String> getNamesOfComponent(Class<?> clazz) {\n\t\treturn kernel.getDependencyManager()\n\t\t\t\t.getBeanConfigs()\n\t\t\t\t.stream()\n\t\t\t\t.filter(beanConfig -> beanConfig.getState() != BeanConfig.State.inactive)\n\t\t\t\t.filter(beanConfig -> clazz.isAssignableFrom(beanConfig.getClazz()))\n\t\t\t\t.map(BeanConfig::getBeanName)\n\t\t\t\t.toList();\n\t}\n\n\tpublic Path getRootPath() {\n\t\treturn rootPath;\n\t}\n\n\tprotected void exportInclude(Writer parentWriter, Path filePath,\n\t\t\t\t\t\t\t\t RepositoryManager.ThrowingConsumer<Writer> consumer) throws Exception {\n\t\tExporter.exportInclude(parentWriter, rootPath, filePath, consumer);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/util/locker/ConnectionLock.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.db.util.locker;\n\nimport tigase.db.DataRepository;\nimport tigase.db.jdbc.DataRepositoryImpl;\nimport tigase.db.util.JDBCPasswordObfuscator;\n\nimport java.sql.*;\nimport java.util.Optional;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nabstract public class ConnectionLock {\n\n\tstatic final Logger log = Logger.getLogger(ConnectionLock.class.getCanonicalName());\n\tprotected boolean isLocked = false;\n\tprotected String jdbcConnection;\n\tprotected int lockAttemptDelay = 1000;\n\tprotected int lockAttemptsLimit = 10;\n\tString lockName = \"tigase\";\n\tprivate Connection connection;\n\n\tstatic public Optional<ConnectionLock> getConnectionLocker(String db_conn) throws IllegalArgumentException {\n\t\tDataRepository.dbTypes dbType = DataRepositoryImpl.parseDatabaseType(db_conn);\n\t\tif (dbType == null) {\n\t\t\treturn Optional.empty();\n\t\t}\n\t\tConnectionLock connectionLock = null;\n\t\tswitch (dbType) {\n\t\t\tcase postgresql:\n\t\t\t\tconnectionLock = new PostgresqlConnectionLock(db_conn);\n\t\t\t\tbreak;\n\t\t\tcase mysql:\n\t\t\t\tconnectionLock = new MysqlConnectionLock(db_conn);\n\t\t\t\tbreak;\n\t\t\tcase jtds:\n\t\t\tcase sqlserver:\n\t\t\t\tconnectionLock = new MssqlConnectionLock(db_conn);\n\t\t\t\tbreak;\n\t\t\tcase derby:\n\t\t\tdefault:\n\t\t\t\tlog.log(Level.WARNING, \"Database locking is not supported for: \" + dbType);\n\t\t\t\tbreak;\n\t\t}\n\t\treturn Optional.ofNullable(connectionLock);\n\t}\n\n\tprotected ConnectionLock(String db_conn) {\n\t\tthis.jdbcConnection = db_conn;\n\t\ttry {\n\t\t\tconnection = DriverManager.getConnection(db_conn);\n\t\t\tlog.log(Level.INFO, \"Prepared connection for locking\");\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Failed preparing connection for locking: \" + e.getMessage());\n\t\t}\n\t}\n\n\tpublic boolean lock() {\n\t\tlog.log(Level.INFO, \"Trying to get lock for database, current state: \" + isLocked);\n\n\t\tif (connection == null) {\n\t\t\tlog.log(Level.WARNING, \"No connection available!\");\n\t\t\treturn false;\n\t\t}\n\n\t\tif (isLocked) {\n\t\t\tlog.log(Level.WARNING, \"Connection already locked!\");\n\t\t\treturn false;\n\t\t}\n\n\t\tboolean gotLocked = false;\n\t\tint attempt = 0;\n\t\twhile (!gotLocked && attempt < lockAttemptsLimit) {\n\t\t\tlog.log(Level.FINEST, \"Trying to get lock for database, attempt {0} of {1}\",\n\t\t\t\t\tnew Object[]{attempt, lockAttemptsLimit});\n\t\t\tgotLocked = lockDatabase(connection);\n\t\t\tif (!isLocked && gotLocked) {\n\t\t\t\tisLocked = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tattempt++;\n\t\t\twait(lockAttemptDelay);\n\t\t}\n\n\t\tif (isLocked) {\n\t\t\tlog.log(Level.INFO, \"Obtained lock for connection: \" + JDBCPasswordObfuscator.obfuscatePassword(jdbcConnection));\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"FAILED to obtain lock for connection: \" + JDBCPasswordObfuscator.obfuscatePassword(jdbcConnection));\n\t\t}\n\t\treturn isLocked;\n\t}\n\n\tpublic boolean unlock() {\n\t\tboolean gotUnlocked = false;\n\n\t\tif (connection == null) {\n\t\t\tlog.log(Level.WARNING, \"No connection available!\");\n\t\t\treturn false;\n\t\t}\n\n\t\tif (isLocked) {\n\n\t\t\tlog.log(Level.INFO, \"Unlocking database\");\n\n\t\t\tint attempt = 0;\n\t\t\twhile (!gotUnlocked && attempt < lockAttemptsLimit) {\n\t\t\t\tlog.log(Level.FINEST, \"Trying to unlock the database, attempt {0} of {1}\",\n\t\t\t\t\t\tnew Object[]{attempt, lockAttemptsLimit});\n\t\t\t\tgotUnlocked = unlockDatabase(connection);\n\t\t\t\tif (isLocked && gotUnlocked) {\n\t\t\t\t\tisLocked = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tattempt++;\n\t\t\t\twait(lockAttemptDelay);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.INFO, \"Connection was not locked, skipping unlocking \");\n\t\t}\n\t\treturn gotUnlocked;\n\t}\n\n\tpublic void cleanup() {\n\t\tif (!isLocked && connection != null) {\n\t\t\ttry {\n\t\t\t\tlog.log(Level.INFO, \"Closing lock connection\");\n\t\t\t\tconnection.close();\n\t\t\t} catch (SQLException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Failed closing connection\", e);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic boolean isLocked() {\n\t\tlog.log(Level.FINE, \"Lock state: \" + isLocked);\n\t\treturn isLocked;\n\t}\n\n\t/**\n\t * @param connection {@link java.sql.Connection} which should hold the lock\n\t *\n\t * @return {@code true} if the locking was successful or {@code false} if it wasn't\n\t */\n\tprotected abstract boolean lockDatabase(Connection connection);\n\n\tprotected void release(Statement statement, ResultSet resultSet) {\n\t\ttry {\n\t\t\tif (resultSet != null) {\n\t\t\t\tresultSet.close();\n\t\t\t}\n\t\t\tif (statement != null) {\n\t\t\t\tstatement.close();\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tlog.log(Level.FINEST, \"Failed to release ResultSet or Statement\");\n\t\t}\n\t}\n\n\t/**\n\t * @param connection {@link java.sql.Connection} which holds the lock and which should be unlocked\n\t *\n\t * @return {@code true} if the unlocking was successful or {@code false} if it wasn't and the lock is still hold\n\t */\n\tprotected abstract boolean unlockDatabase(Connection connection);\n\n\tprotected boolean executeQuery(Connection connection, String query) {\n\t\tboolean isLocked = false;\n\t\tPreparedStatement statement = null;\n\t\tResultSet resultSet = null;\n\t\ttry {\n\t\t\tstatement = connection.prepareStatement(query);\n\t\t\tresultSet = statement.executeQuery();\n\t\t\tif (resultSet.next()) {\n\t\t\t\tfinal ResultSetMetaData metaData = resultSet.getMetaData();\n\t\t\t\tisLocked = resultSet.getBoolean(1);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, e.getMessage(), e);\n\t\t} finally {\n\t\t\trelease(statement, resultSet);\n\t\t}\n\t\treturn isLocked;\n\t}\n\n\tprivate void wait(int delay) {\n\t\ttry {\n\t\t\tTimeUnit.MILLISECONDS.sleep(delay);\n\t\t} catch (InterruptedException e) {\n\t\t\t//ignore\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/util/locker/MssqlConnectionLock.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.db.util.locker;\n\nimport java.sql.*;\nimport java.util.function.Consumer;\nimport java.util.logging.Level;\n\n/**\n * https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-getapplock-transact-sql\n * https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-releaseapplock-transact-sql\n * https://docs.microsoft.com/en-us/sql/connect/jdbc/using-a-stored-procedure-with-a-return-status\n */\nclass MssqlConnectionLock\n\t\textends ConnectionLock {\n\n\tpublic MssqlConnectionLock(String db_conn) {\n\t\tsuper(db_conn);\n\t}\n\n\t@Override\n\tprotected boolean lockDatabase(Connection connection) {\n\t\tString query = \"{ ? = call dbo.sp_getapplock(?, ?, ?, ?) }\";\n\t\treturn executeProcedure(connection, query, statement -> {\n\t\t\ttry {\n\t\t\t\tstatement.setString(2, lockName);\n\t\t\t\tstatement.setString(3, \"Exclusive\");\n\t\t\t\tstatement.setString(4, \"Session\");\n\t\t\t\tstatement.setInt(5, 1000);\n\t\t\t} catch (SQLException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Failed to set statement parameters\", e);\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tprotected boolean unlockDatabase(Connection connection) {\n\t\tString query = \"{ ? = call dbo.sp_releaseapplock(?, ?) }\";\n//\t\tString query = \"exec sp_getapplock @Resource = '\" + lockName + \"', @LockMode = 'Exclusive', @LockOwner='Session'\";\n\t\treturn executeProcedure(connection, query, statement -> {\n\t\t\ttry {\n\t\t\t\tstatement.setString(2, lockName);\n\t\t\t\tstatement.setString(3, \"Session\");\n\t\t\t} catch (SQLException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Failed to set statement parameters\", e);\n\t\t\t}\n\t\t});\n\t}\n\n\tprotected boolean executeProcedure(Connection connection, String query, Consumer<CallableStatement> consumer) {\n\t\tboolean isLocked = false;\n\t\tCallableStatement statement = null;\n\t\tResultSet resultSet = null;\n\t\ttry {\n\t\t\tstatement = connection.prepareCall(query);\n\t\t\tstatement.registerOutParameter(1, Types.INTEGER);\n\t\t\tconsumer.accept(statement);\n\t\t\tstatement.execute();\n\t\t\tfinal int returnCode = statement.getInt(1);\n\t\t\tlog.log(Level.FINEST, \"Stored procedure return code: \" + returnCode);\n\t\t\tswitch (returnCode) {\n\t\t\t\tcase 0:\n\t\t\t\tcase 1:\n\t\t\t\t\tisLocked = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase -1:\n\t\t\t\tcase -2:\n\t\t\t\tcase -3:\n\t\t\t\tcase -999:\n\t\t\t\tdefault:\n\t\t\t\t\tisLocked = false;\n\t\t\t\t\tbreak;\n\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, e.getMessage(), e);\n\t\t} finally {\n\t\t\trelease(statement, resultSet);\n\t\t}\n\t\treturn isLocked;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/util/locker/MysqlConnectionLock.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.db.util.locker;\n\nimport java.sql.Connection;\n\n/**\n * https://dev.mysql.com/doc/refman/8.0/en/locking-functions.html\n */\nclass MysqlConnectionLock\n\t\textends ConnectionLock {\n\n\tpublic MysqlConnectionLock(String db_conn) {\n\t\tsuper(db_conn);\n\t}\n\n\t@Override\n\tprotected boolean lockDatabase(Connection connection) {\n\t\tString query = \"SELECT GET_LOCK('\" + lockName + \"',1)\";\n\t\treturn executeQuery(connection, query);\n\t}\n\n\t@Override\n\tprotected boolean unlockDatabase(Connection connection) {\n\t\tString query = \"SELECT RELEASE_LOCK('\" + lockName + \"')\";\n\t\treturn executeQuery(connection, query);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/util/locker/PostgresqlConnectionLock.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.db.util.locker;\n\nimport java.sql.Connection;\nimport java.util.Objects;\n\n/**\n * Based on https://www.postgresql.org/docs/current/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS\n */\nclass PostgresqlConnectionLock\n\t\textends ConnectionLock {\n\n\tint lockName = Math.abs(Objects.hash(super.lockName));\n\n\tpublic PostgresqlConnectionLock(String db_conn) {\n\t\tsuper(db_conn);\n\t}\n\n\t@Override\n\tprotected boolean lockDatabase(Connection connection) {\n\t\tString query = \"SELECT pg_try_advisory_lock(\" + lockName + \")\";\n\t\treturn executeQuery(connection, query);\n\t}\n\n\t@Override\n\tprotected boolean unlockDatabase(Connection connection) {\n\t\tString query = \"SELECT  pg_advisory_unlock(\" + lockName + \")\";\n\t\treturn executeQuery(connection, query);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/xml/XMLDataSource.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.xml;\n\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.db.DBInitException;\nimport tigase.db.DataSource;\nimport tigase.db.Repository;\nimport tigase.util.Version;\nimport tigase.xml.db.XMLDB;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created by andrzej on 04.04.2017.\n */\n@Repository.Meta(supportedUris = {\"memory://.*\"})\npublic class XMLDataSource\n\t\timplements DataSource {\n\n\tprivate static final Logger log = Logger.getLogger(XMLDataSource.class.getCanonicalName());\n\n\tprivate String resource_uri;\n\tprivate XMLDB xmldb;\n\n\t@Override\n\tpublic Optional<Version> getSchemaVersion(String component) {\n\t\treturn Optional.empty();\n\t}\n\n\t@Override\n\tpublic String getResourceUri() {\n\t\treturn resource_uri;\n\t}\n\n\t@Override\n\tpublic void initialize(String file) throws RepositoryException {\n\t\tinitRepository(file, new HashMap<>());\n\t}\n\n\t@Override\n\t@Deprecated\n\tpublic void initRepository(String file, Map<String, String> params) throws DBInitException {\n\t\tthis.resource_uri = file;\n\t\tString file_name = file;\n\n\t\tlog.log(Level.FINEST, \"Initializing repository, file: {0}, params: {1}\", new Object[]{file, params});\n\n\t\ttry {\n\t\t\tint idx = file.indexOf(\"?\");\n\n\t\t\tif (idx > 0) {\n\t\t\t\tfile_name = file.substring(0, idx);\n\t\t\t}\n\n\t\t\txmldb = new XMLDB(file_name);\n\t\t} catch (Exception e) {\n\t\t\tlog.warning(\"Can not open existing user repository file, creating new one, \" + e);\n\t\t\txmldb = XMLDB.createDB(file_name, \"users\", \"user\");\n\t\t}      // end of try-catch\n\t}\n\n\tpublic XMLDB getXMLDB() {\n\t\treturn xmldb;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/xml/XMLMsgRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.xml;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.Repository;\nimport tigase.db.UserNotFoundException;\nimport tigase.server.amp.db.MsgRepository;\nimport tigase.xml.DomBuilderHandler;\nimport tigase.xml.Element;\nimport tigase.xml.db.NodeNotFoundException;\nimport tigase.xml.db.XMLDB;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created by andrzej on 04.04.2017.\n */\n@Repository.Meta(supportedUris = {\"memory://.*\"})\npublic class XMLMsgRepository\n\t\textends MsgRepository<String, XMLDataSource> {\n\n\tprivate static final Logger log = Logger.getLogger(XMLMsgRepository.class.getCanonicalName());\n\n\tprivate XMLDataSource dataSource;\n\tprivate XMLDB xmldb;\n\n\t@Override\n\tpublic void setDataSource(XMLDataSource dataSource) {\n\t\tthis.dataSource = dataSource;\n\t\tthis.xmldb = dataSource.getXMLDB();\n\t}\n\n\t@Override\n\tpublic Map<Enum, Long> getMessagesCount(JID to) throws UserNotFoundException {\n\t\tthrow new UnsupportedOperationException(\"Feature not implemented!\");\n\t}\n\n\t@Override\n\tpublic List<Element> getMessagesList(JID to) throws UserNotFoundException {\n\t\tthrow new UnsupportedOperationException(\"Feature not implemented!\");\n\t}\n\n\t@Override\n\tpublic Queue<Element> loadMessagesToJID(XMPPResourceConnection session, boolean delete)\n\t\t\tthrows UserNotFoundException {\n\t\treturn loadMessagesToJID(null, session, delete, null);\n\t}\n\n\t@Override\n\tpublic boolean storeMessage(JID from, JID to, Date expired, Element msg, NonAuthUserRepository userRepo)\n\t\t\tthrows UserNotFoundException {\n\t\tBareJID user = to.getBareJID();\n\t\ttry {\n\t\t\tString[] old_data = xmldb.getDataList(user.toString(), \"offline\", \"messages\");\n\t\t\tString[] all = null;\n\t\t\tString[] list = new String[]{msg.toString()};\n\n\t\t\tif (old_data != null) {\n\t\t\t\tall = new String[old_data.length + 1];\n\t\t\t\tSystem.arraycopy(old_data, 0, all, 0, old_data.length);\n\t\t\t\tSystem.arraycopy(list, 0, all, old_data.length, list.length);\n\t\t\t\txmldb.setData(user.toString(), \"offline\", \"messages\", all);\n\t\t\t} else {\n\t\t\t\txmldb.setData(user.toString(), \"offline\", \"messages\", list);\n\t\t\t}    // end of else\n\n\t\t} catch (NodeNotFoundException e) {\n\t\t\tthrow new UserNotFoundException(\"User not found \" + user, e);\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic Queue<Element> loadMessagesToJID(List<String> db_ids, XMPPResourceConnection session, boolean delete,\n\t\t\t\t\t\t\t\t\t\t\tOfflineMessagesProcessor proc) throws UserNotFoundException {\n\t\tBareJID user = null;\n\t\ttry {\n\t\t\tuser = session.getBareJID();\n\t\t\tString[] msgs = xmldb.getDataList(user.toString(), \"offline\", \"messages\");\n\t\t\tif (msgs != null) {\n\t\t\t\tif (delete) {\n\t\t\t\t\txmldb.removeData(user.toString(), \"offline\", \"messages\");\n\t\t\t\t}\n\t\t\t\tDomBuilderHandler domHandler = new DomBuilderHandler();\n\t\t\t\tStringBuilder sb = new StringBuilder();\n\n\t\t\t\tfor (String msg : msgs) {\n\t\t\t\t\tsb.append(msg);\n\t\t\t\t}\n\n\t\t\t\tchar[] data = sb.toString().toCharArray();\n\n\t\t\t\tparser.parse(domHandler, data, 0, data.length);\n\n\t\t\t\treturn domHandler.getParsedElements();\n\t\t\t}\n\t\t} catch (NotAuthorizedException e) {\n\t\t\tlog.log(Level.WARNING, \"Session not authorized yet!\", e);\n\t\t} catch (NodeNotFoundException e) {\n\t\t\tthrow new UserNotFoundException(\"User not found \" + user, e);\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic int deleteMessagesToJID(List<String> db_ids, XMPPResourceConnection session) throws UserNotFoundException {\n\t\tQueue<Element> msgs = loadMessagesToJID(null, session, true, null);\n\t\treturn msgs == null ? 0 : msgs.size();\n\t}\n\n\t@Override\n\tprotected void loadExpiredQueue(int max) {\n\t\t// nothing to do\n\t}\n\n\t@Override\n\tprotected void loadExpiredQueue(Date expired) {\n\t\t// nothing to do\n\t}\n\n\t@Override\n\tprotected void deleteMessage(String db_id) {\n\t\tthrow new UnsupportedOperationException(\"Removal of messages using id is not supported!\");\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/db/xml/XMLRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.xml;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.*;\nimport tigase.xml.db.NodeExistsException;\nimport tigase.xml.db.NodeNotFoundException;\nimport tigase.xml.db.XMLDB;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Class <code>XMLRepository</code> is a <em>XML</em> implementation of <code>UserRepository</code>. It uses\n * <code>tigase.xml.db</code> package as repository backend and uses <em>Bridge</em> design pattern to translate\n * <code>XMLDB</code> calls to <code>UserRepository</code> functions.\n * <br>\n * <p> Created: Tue Oct 26 15:27:33 2004 </p>\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\n@Repository.Meta(supportedUris = {\"memory://.*\"})\npublic class XMLRepository\n\t\timplements Repository, DataSourceAware<XMLDataSource>, AuthRepository, UserRepository {\n\n\tprivate static final String USER_STR = \"User: \";\n\tprivate static final String NOT_FOUND_STR = \" has not been found in repository.\";\n\tprivate static final Logger log = Logger.getLogger(\"tigase.db.xml.XMLRepository\");\n\tprivate AuthRepository auth = null;\n\tprivate boolean autoCreateUser = false;\n\tprivate XMLDB xmldb = null;\n\n\t@Override\n\tpublic synchronized void addDataList(BareJID user, final String subnode, final String key, final String[] list)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\tlog.log(Level.FINE, \"Adding data list, user: {0}, subnode: {1}, key: {2}, list: {3}\",\n\t\t\t\tnew Object[]{user, subnode, key, Arrays.asList(list)});\n\n\t\ttry {\n\t\t\tString[] old_data = getDataList(user, subnode, key);\n\t\t\tString[] all = null;\n\n\t\t\tif (old_data != null) {\n\t\t\t\tall = new String[old_data.length + list.length];\n\t\t\t\tSystem.arraycopy(old_data, 0, all, 0, old_data.length);\n\t\t\t\tSystem.arraycopy(list, 0, all, old_data.length, list.length);\n\t\t\t\txmldb.setData(user.toString(), subnode, key, all);\n\t\t\t} else {\n\t\t\t\txmldb.setData(user.toString(), subnode, key, list);\n\t\t\t}    // end of else\n\t\t} catch (NodeNotFoundException e) {\n\t\t\tthrow new UserNotFoundException(USER_STR + user + NOT_FOUND_STR, e);\n\t\t}      // end of try-catch\n\t}\n\n\t@Override\n\tpublic synchronized void addUser(BareJID user) throws UserExistsException {\n\t\tlog.log(Level.FINE, \"adding new user, user: {0}\", new Object[]{user});\n\t\ttry {\n\t\t\txmldb.addNode1(user.toString());\n\t\t} catch (NodeExistsException e) {\n\t\t\tthrow new UserExistsException(USER_STR + user + \" already exists.\", e);\n\t\t}    // end of try-catch\n\t}\n\n\t@Override\n\tpublic synchronized void addUser(BareJID user, final String password)\n\t\t\tthrows UserExistsException, TigaseDBException {\n\t\tauth.addUser(user, password);\n\t}\n\n\t@Override\n\tpublic synchronized String getData(BareJID user, final String subnode, final String key, final String def)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\tlog.log(Level.FINE, \"Getting data, user: {0}, subnode: {1}, key: {2}\", new Object[]{user, subnode, key});\n\n\t\ttry {\n\t\t\treturn (String) xmldb.getData(user.toString(), subnode, key, def);\n\t\t} catch (NodeNotFoundException e) {\n\t\t\tif (autoCreateUser) {\n\t\t\t\ttry {\n\t\t\t\t\taddUser(user);\n\n\t\t\t\t\treturn (String) xmldb.getData(user.toString(), subnode, key, def);\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tthrow new TigaseDBException(\"Unknown repository problem: \", ex);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthrow new UserNotFoundException(USER_STR + user + NOT_FOUND_STR, e);\n\t\t\t}\n\t\t}    // end of try-catch\n\t}\n\n\t@Override\n\tpublic String getData(BareJID user, final String subnode, final String key)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\treturn getData(user, subnode, key, null);\n\t}\n\n\t@Override\n\tpublic String getData(BareJID user, final String key) throws UserNotFoundException, TigaseDBException {\n\t\treturn getData(user, null, key, null);\n\t}\n\n\t@Override\n\tpublic synchronized String[] getDataList(BareJID user, final String subnode, final String key)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\tlog.log(Level.FINE, \"Getting data list, user: {0}, subnode: {1}, key: {2}\", new Object[]{user, subnode, key});\n\n\t\ttry {\n\t\t\treturn xmldb.getDataList(user.toString(), subnode, key);\n\t\t} catch (NodeNotFoundException e) {\n\t\t\tif (autoCreateUser) {\n\t\t\t\ttry {\n\t\t\t\t\taddUser(user);\n\n\t\t\t\t\treturn xmldb.getDataList(user.toString(), subnode, key);\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tthrow new TigaseDBException(\"Unknown repository problem: \", ex);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthrow new UserNotFoundException(USER_STR + user + NOT_FOUND_STR, e);\n\t\t\t}\n\t\t}    // end of try-catch\n\t}\n\n\t@Override\n\tpublic synchronized String[] getKeys(BareJID user, final String subnode)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\tlog.log(Level.FINE, \"Getting keys, user: {0}, subnode: {1}\", new Object[]{user, subnode});\n\n\t\ttry {\n\t\t\treturn xmldb.getKeys(user.toString(), subnode);\n\t\t} catch (NodeNotFoundException e) {\n\t\t\tif (autoCreateUser) {\n\t\t\t\ttry {\n\t\t\t\t\taddUser(user);\n\n\t\t\t\t\treturn xmldb.getKeys(user.toString(), subnode);\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tthrow new TigaseDBException(\"Unknown repository problem: \", ex);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthrow new UserNotFoundException(USER_STR + user + NOT_FOUND_STR, e);\n\t\t\t}\n\t\t}    // end of try-catch\n\t}\n\n\t@Override\n\tpublic String[] getKeys(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\treturn getKeys(user, null);\n\t}\n\n\t@Override\n\tpublic String getResourceUri() {\n\t\treturn xmldb.getDBFileName();\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"Support for multi-level nodes will be removed\")\n\tpublic synchronized String[] getSubnodes(BareJID user, final String subnode)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\tlog.log(Level.FINE, \"Getting subnodes, user: {0}, subnode: {1}\", new Object[]{user, subnode});\n\n\t\ttry {\n\t\t\treturn xmldb.getSubnodes(user.toString(), subnode);\n\t\t} catch (NodeNotFoundException e) {\n\t\t\tif (autoCreateUser) {\n\t\t\t\ttry {\n\t\t\t\t\taddUser(user);\n\n\t\t\t\t\treturn xmldb.getSubnodes(user.toString(), subnode);\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tthrow new TigaseDBException(\"Unknown repository problem: \", ex);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthrow new UserNotFoundException(USER_STR + user + NOT_FOUND_STR, e);\n\t\t\t}\n\t\t}    // end of try-catch\n\t}\n\n\t@Override\n\tpublic String[] getSubnodes(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\treturn getSubnodes(user, null);\n\t}\n\n\t@Override\n\tpublic long getUserUID(BareJID user) throws TigaseDBException {\n\t\treturn Math.abs(user.hashCode());\n\t}\n\n\t@Override\n\tpublic synchronized List<BareJID> getUsers() {\n\t\tList<String> users = xmldb.getAllNode1s();\n\t\tlog.log(Level.FINE, \"Getting users, users: {0}, xmldb: {1}\", new Object[]{users, xmldb});\n\n\t\tList<BareJID> result = new ArrayList<BareJID>();\n\n\t\tfor (String usr : users) {\n\t\t\tresult.add(BareJID.bareJIDInstanceNS(usr));\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic synchronized long getUsersCount(String domain) {\n\t\tlong res = 0;\n\t\tList<BareJID> jids = getUsers();\n\n\t\tfor (BareJID jid : jids) {\n\t\t\tif (jid.getDomain().equals(domain)) {\n\t\t\t\t++res;\n\t\t\t}\n\t\t}\n\n\t\treturn res;\n\t}\n\n\t@Override\n\tpublic long getActiveUsersCountIn(Duration duration) {\n\t\treturn -1;\n\t}\n\n\t@Override\n\tpublic synchronized long getUsersCount() {\n\t\treturn xmldb.getAllNode1sCount();\n\t}\n\n\t@Override\n\t@Deprecated\n\tpublic synchronized void initRepository(String file, Map<String, String> params) throws DBInitException {\n\t\tif (xmldb == null) {\n\t\t\tlog.log(Level.FINE, \"Initializing repository, file: {0}, params: {1}\", new Object[]{file, params});\n\t\t\tXMLDataSource dataSource = new XMLDataSource();\n\t\t\tdataSource.initRepository(file, params);\n\t\t\tsetDataSource(dataSource);\n\t\t}\n\t}\n\n\t@Override\n\tpublic synchronized void logout(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\tauth.logout(user);\n\t}\n\n\t@Override\n\tpublic void loggedIn(BareJID user) throws TigaseDBException {\n\t\tauth.loggedIn(user);\n\t}\n\n\t@Override\n\tpublic synchronized boolean otherAuth(final Map<String, Object> props)\n\t\t\tthrows UserNotFoundException, TigaseDBException, AuthorizationException {\n\t\treturn auth.otherAuth(props);\n\t}\n\n\t// Implementation of tigase.db.AuthRepository\n\n\t@Override\n\tpublic synchronized void queryAuth(Map<String, Object> authProps) {\n\t\tauth.queryAuth(authProps);\n\t}\n\n\t@Override\n\tpublic synchronized void removeData(BareJID user, final String subnode, final String key)\n\t\t\tthrows UserNotFoundException {\n\t\tlog.log(Level.FINE, \"Removing data, user: {0}, subnode: {1}, key: {2}\", new Object[]{user, subnode, key});\n\n\t\ttry {\n\t\t\txmldb.removeData(user.toString(), subnode, key);\n\t\t} catch (NodeNotFoundException e) {\n\t\t\tif (!autoCreateUser) {\n\t\t\t\tthrow new UserNotFoundException(USER_STR + user + NOT_FOUND_STR, e);\n\t\t\t}\n\t\t}    // end of try-catch\n\t}\n\n\t@Override\n\tpublic void removeData(BareJID user, final String key) throws UserNotFoundException {\n\t\tremoveData(user, null, key);\n\t}\n\n\t@Override\n\tpublic synchronized void removeSubnode(BareJID user, final String subnode) throws UserNotFoundException {\n\t\tlog.log(Level.FINE, \"Removing subnode, user: {0}, subnode: {1}\", new Object[]{user, subnode});\n\n\t\ttry {\n\t\t\txmldb.removeSubnode(user.toString(), subnode);\n\t\t} catch (NodeNotFoundException e) {\n\t\t\tif (!autoCreateUser) {\n\t\t\t\tthrow new UserNotFoundException(USER_STR + user + NOT_FOUND_STR, e);\n\t\t\t}\n\t\t}    // end of try-catch\n\t}\n\n\t@Override\n\tpublic synchronized void removeUser(BareJID user) throws UserNotFoundException {\n\t\ttry {\n\t\t\tlog.log(Level.FINE, \"Removing user: {0}\", new Object[]{user});\n\n\t\t\txmldb.removeNode1(user.toString());\n\t\t} catch (NodeNotFoundException e) {\n\t\t\tthrow new UserNotFoundException(USER_STR + user + NOT_FOUND_STR, e);\n\t\t}    // end of try-catch\n\t}\n\n\t@Override\n\tpublic synchronized void setData(BareJID user, final String subnode, final String key, final String value)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\tlog.log(Level.FINE, \"Setting data, user: {0}, subnode: {1}, key: {2}, value: {3}\",\n\t\t\t\tnew Object[]{user, subnode, key, value});\n\t\ttry {\n\t\t\txmldb.setData(user.toString(), subnode, key, value);\n\t\t} catch (NodeNotFoundException e) {\n\t\t\tif (autoCreateUser) {\n\t\t\t\ttry {\n\t\t\t\t\taddUser(user);\n\t\t\t\t\txmldb.setData(user.toString(), subnode, key, value);\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tthrow new TigaseDBException(\"Unknown repository problem: \", ex);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthrow new UserNotFoundException(USER_STR + user + NOT_FOUND_STR, e);\n\t\t\t}\n\t\t}    // end of try-catch\n\t}\n\n\t@Override\n\tpublic void setData(BareJID user, final String key, final String value)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\tsetData(user, null, key, value);\n\t}\n\n\t@Override\n\tpublic synchronized void setDataList(BareJID user, final String subnode, final String key, final String[] list)\n\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\tlog.log(Level.FINE, \"Setting data list, user: {0}, subnode: {1}, key: {2}, value: {3}\",\n\t\t\t\tnew Object[]{user, subnode, key, Arrays.asList(list)});\n\t\ttry {\n\t\t\txmldb.setData(user.toString(), subnode, key, list);\n\t\t} catch (NodeNotFoundException e) {\n\t\t\tif (autoCreateUser) {\n\t\t\t\ttry {\n\t\t\t\t\taddUser(user);\n\t\t\t\t\txmldb.setData(user.toString(), subnode, key, list);\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tthrow new TigaseDBException(\"Unknown repository problem: \", ex);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthrow new UserNotFoundException(USER_STR + user + NOT_FOUND_STR, e);\n\t\t\t}\n\t\t}    // end of try-catch\n\t}\n\n\t@Override\n\tpublic synchronized void updatePassword(BareJID user, final String password)\n\t\t\tthrows UserExistsException, TigaseDBException {\n\t\tauth.updatePassword(user, password);\n\t}\n\n\t@Override\n\tpublic synchronized boolean userExists(BareJID user) {\n\t\treturn xmldb.findNode1(user.toString()) != null;\n\t}\n\n\t@Override\n\tpublic String getPassword(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\treturn auth.getPassword(user);\n\t}\n\n\t@Override\n\tpublic boolean isUserDisabled(BareJID user) throws UserNotFoundException, TigaseDBException {\n\t\tfinal String disabled = getData(user, \"disabled\");\n\t\treturn disabled != null && Boolean.parseBoolean(disabled);\n\t}\n\n\t@Override\n\tpublic void setUserDisabled(BareJID user, Boolean value) throws UserNotFoundException, TigaseDBException {\n\t\tsetData(user, \"disabled\", value.toString());\n\t}\n\n\t@Override\n\tpublic void setAccountStatus(BareJID user, AccountStatus status) throws TigaseDBException {\n\t\tsetData(user, \"accountStatus\", status.toString());\n\t}\n\n\t@Override\n\tpublic AccountStatus getAccountStatus(BareJID user) throws TigaseDBException {\n\t\tfinal String accountStatus = getData(user, \"accountStatus\");\n\t\treturn accountStatus != null ? AccountStatus.valueOf(accountStatus) : AccountStatus.active;\n\t}\n\n\t@Override\n\tpublic void setDataSource(XMLDataSource dataSource) {\n\t\tString file = dataSource.getResourceUri();\n\t\tif (file.contains(\"autoCreateUser=true\")) {\n\t\t\tautoCreateUser = true;\n\t\t}    // end of if (db_conn.contains())\n\t\txmldb = dataSource.getXMLDB();\n\t\tauth = new AuthRepositoryImpl(this);\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/disco/ServiceEntity.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.disco;\n\nimport tigase.xml.Element;\nimport tigase.xmpp.impl.PresenceCapabilitiesManager;\n\nimport java.util.*;\nimport java.util.function.Function;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.xmpp.impl.PresenceCapabilitiesManager.CAPS_NODE;\nimport static tigase.xmpp.impl.PresenceCapabilitiesManager.generateVerificationString;\n\n/**\n * Describe class ServiceEntity here.\n * <br>\n * Created: Sat Feb 10 13:11:34 2007\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class ServiceEntity {\n\n\tprivate static Logger log = Logger.getLogger(ServiceEntity.class.getName());\n\n\tprivate boolean adminOnly = false;\n\tprivate Function<String, Element> extensionSupplier;\n\tprivate Set<String> features = null;\n\tprivate List<ServiceIdentity> identities = null;\n\tprivate Set<ServiceEntity> items = null;\n\tprivate String jid = null;\n\tprivate String name = null;\n\tprivate String node = null;\n\n\tpublic ServiceEntity(String jid, String node, String name) {\n\t\tthis(jid, node, name, null);\n\t}\n\t\n\tpublic ServiceEntity(String jid, String node, String name, Function<String, Element> extensionSupplier) {\n\t\tthis.jid = jid;\n\t\tthis.node = node;\n\t\tthis.name = name;\n\t\tthis.extensionSupplier = extensionSupplier;\n\t}\n\n\tpublic ServiceEntity(String jid, String node, String name, Function<String, Element> extensionSupplier, boolean adminOnly) {\n\t\tthis.jid = jid;\n\t\tthis.node = node;\n\t\tthis.name = name;\n\t\tthis.extensionSupplier = extensionSupplier;\n\t\tthis.adminOnly = adminOnly;\n\t}\n\n//\tpublic Element getExtensions() {\n//\t\treturn extensions.clone();\n//\t}\n//\n//\tpublic void setExtensions(Element extensions) {\n//\t\tthis.extensions = extensions;\n//\t}\n\n\tpublic void addFeatures(String... features) {\n\t\tif (this.features == null) {\n\t\t\tthis.features = new LinkedHashSet<String>();\n\t\t}\n\t\tCollections.addAll(this.features, features);\n\t}\n\n\tpublic void addIdentities(ServiceIdentity... identities) {\n\t\tif (this.identities == null) {\n\t\t\tthis.identities = new ArrayList<ServiceIdentity>();\n\t\t}\n\t\tCollections.addAll(this.identities, identities);\n\t}\n\n\tpublic void addItems(ServiceEntity... items) {\n\t\tif (this.items == null) {\n\t\t\tthis.items = new LinkedHashSet<ServiceEntity>();\n\t\t}\n\n\t\t// It may look very strange but look at equals() method....\n\t\t// So some items which might be the same from the Set point of\n\t\t// view are not really the same. They may have different name.\n\t\t// This is to allow \"update\" the service discovery with some changed\n\t\t// info.... So in particular the \"name\" may contain some additional\n\t\t// information which can change at runtime\n\t\tfor (ServiceEntity item : items) {\n\t\t\tif (this.items.contains(item)) {\n\t\t\t\tthis.items.remove(item);\n\t\t\t}\n\t\t\tthis.items.add(item);\n\t\t}\n\t}\n\n\t/**\n\t * 2 ServiceEntities are equal of JIDs are equal and NODEs are equal.\n\t *\n\t * @param obj an <code>Object</code> value\n\t *\n\t * @return a <code>boolean</code> value\n\t */\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (obj instanceof ServiceEntity) {\n\t\t\tServiceEntity se = (ServiceEntity) obj;\n\n\t\t\t// Assuming here that jid can never be NULL\n\t\t\t// Node can be NULL so we need to do more calculation on it\n\t\t\treturn ((se == null) ? false : jid.equals(se.jid)) &&\n\t\t\t\t\t((node == se.node) || ((node != null) ? node.equals(se.node) : se.node.equals(node)));\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tpublic ServiceEntity findNode(String node) {\n\n\t\t// System.out.println(\"Looking for a node: \" + node);\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Looking for a node: \" + node);\n\t\t}\n\t\tif ((this.node != null) && this.node.equals(node)) {\n\n\t\t\t// System.out.println(\"Looking for a node: \" + node);\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Found myself: \" + toString());\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\t\tif (items == null) {\n\t\t\treturn null;\n\t\t}\n\t\tfor (ServiceEntity item : items) {\n\t\t\tString n = item.getNode();\n\n\t\t\tif ((n != null) && node.equals(n)) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"Found child item: \" + item.toString());\n\t\t\t\t}\n\n\t\t\t\treturn item;\n\t\t\t}\n\t\t}\n\n\t\tint idx = node.indexOf('/');\n\n\t\tif (idx >= 0) {\n\t\t\tString top = node.substring(0, idx);\n\t\t\tServiceEntity current = findNode(top);\n\n\t\t\tif (current != null) {\n\t\t\t\tString rest = node.substring(idx + 1);\n\n\t\t\t\treturn current.findNode(rest);\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic int hashCode() {\n\t\treturn ((jid != null) ? jid.hashCode() : 0) + ((node != null) ? node.hashCode() : 0);\n\t}\n\n\tpublic void removeItems(ServiceEntity... items) {\n\t\tif (this.items == null) {\n\t\t\treturn;\n\t\t}\n\t\tfor (ServiceEntity item : items) {\n\t\t\tthis.items.remove(item);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getDiscoItem(null, null).toString();\n\t}\n\n\tpublic Optional<Element> getCaps(boolean admin, String domain) {\n\t\tif (adminOnly && !admin) {\n\t\t\treturn Optional.empty();\n\t\t}\n\t\tfinal String[] discoFeatures = getDiscoFeatures();\n\t\tList<String> list = new ArrayList<>();\n\t\tfor (ServiceIdentity serviceIdentity : getDiscoIdentities()) {\n\t\t\tString asCapsString = serviceIdentity.getAsCapsString();\n\t\t\tlist.add(asCapsString);\n\t\t}\n\t\tlist.sort(null);\n\t\tfinal String[] discoIdentities = list.toArray(new String[0]);\n\n\t\tElement extensions = extensionSupplier != null ? extensionSupplier.apply(domain) : null;\n\t\tfinal String caps = generateVerificationString(discoIdentities, discoFeatures, extensions);\n\n\t\tif (caps != null) {\n\t\t\tfinal String capsNode = CAPS_NODE + \"#\" + caps;\n\t\t\tPresenceCapabilitiesManager.setNodeFeatures(capsNode, getDiscoFeatures());\n\t\t\treturn Optional.of(PresenceCapabilitiesManager.getCapsElement(caps));\n\t\t} else {\n\t\t\treturn Optional.empty();\n\t\t}\n\t}\n\n\tpublic String[] getDiscoFeatures() {\n\t\treturn features == null ? new String[0] : features.toArray(new String[0]);\n\t}\n\n\tpublic List<ServiceIdentity> getDiscoIdentities() {\n\t\treturn identities == null ? Collections.emptyList() : Collections.unmodifiableList(identities);\n\t}\n\n\tpublic Element[] getDiscoFeatures(String node) {\n\t\tArrayList<Element> elFeatures = new ArrayList<Element>();\n\n\t\tif (features != null) {\n\t\t\tfor (String feature : features) {\n\t\t\t\telFeatures.add(new Element(\"feature\", new String[]{\"var\"}, new String[]{feature}));\n\t\t\t}\n\t\t}\n\n\t\treturn (elFeatures.size() > 0) ? elFeatures.toArray(new Element[elFeatures.size()]) : null;\n\t}\n\n\tpublic Element getDiscoInfo(String node) {\n\t\treturn getDiscoInfo(node, true);\n\t}\n\n\tpublic Element getDiscoInfo(String node, boolean admin) {\n\n\t\t// System.out.println(\"Node: \" + node);\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Node: \" + node);\n\t\t}\n\n\t\tElement query = null;\n\n\t\tif (node == null) {\n\n\t\t\t// If the node is for admins only and this is not admin return null\n\t\t\tif (adminOnly && !admin) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"It's me: \" + toString());\n\t\t\t}\n\t\t\tquery = new Element(\"query\", new String[]{\"xmlns\"}, new String[]{\"http://jabber.org/protocol/disco#info\"});\n\t\t\tif (identities != null) {\n\t\t\t\tfor (ServiceIdentity ident : identities) {\n\t\t\t\t\tquery.addChild(ident.getElement());\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (features != null) {\n\t\t\t\tfor (String feature : features) {\n\t\t\t\t\tquery.addChild(new Element(\"feature\", new String[]{\"var\"}, new String[]{feature}));\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tServiceEntity entity = findNode(node);\n\n\t\t\t// If the entity is for admins only and this is not admin return null\n\t\t\tif ((entity != null) && entity.adminOnly && !admin) {\n\t\t\t\tentity = null;\n\t\t\t}\n\t\t\tif (entity != null) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"Found child node: \" + entity.toString());\n\t\t\t\t}\n\t\t\t\tquery = entity.getDiscoInfo(null);\n\t\t\t\tquery.setAttribute(\"node\", node);\n\t\t\t}\n\t\t}\n\n\t\treturn query;\n\t}\n\n\tpublic Element getDiscoItem(String node, String jid) {\n\t\tElement item = new Element(\"item\");\n\n\t\tif (jid != null) {\n\t\t\titem.setAttribute(\"jid\", jid);\n\t\t} else {\n\t\t\tif (this.jid != null) {\n\t\t\t\titem.setAttribute(\"jid\", this.jid);\n\t\t\t}\n\t\t}\n\t\tif (node != null) {\n\t\t\titem.setAttribute(\"node\", node + ((this.node != null) ? \"/\" + this.node : \"\"));\n\t\t} else {\n\t\t\tif (this.node != null) {\n\t\t\t\titem.setAttribute(\"node\", this.node);\n\t\t\t}\n\t\t}\n\t\tif (name != null) {\n\t\t\titem.setAttribute(\"name\", name);\n\t\t}\n\n\t\treturn item;\n\t}\n\n\tpublic List<Element> getDiscoItems(String node, String jid) {\n\t\treturn getDiscoItems(node, jid, true);\n\t}\n\n\tpublic List<Element> getDiscoItems(String node, String jid, boolean admin) {\n\n\t\t// System.out.println(\"node: \" + node + \", jid: \" + jid);\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"node: \" + node + \", jid: \" + jid);\n\t\t}\n\n\t\tList<Element> result = null;\n\n\t\tif (node == null) {\n\t\t\tresult = getItems(null, jid, admin);\n\t\t} else {\n\t\t\tServiceEntity entity = findNode(node);\n\n\t\t\tif ((entity != null) && entity.adminOnly && !admin) {\n\t\t\t\tentity = null;\n\t\t\t}\n\n\t\t\t// System.out.println(\"Found disco entity: \" + entity.toString());\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Found disco entity: \" + ((entity != null) ? entity.toString() : null));\n\t\t\t}\n\t\t\tif (entity != null) {\n\t\t\t\tresult = entity.getItems(node, jid, admin);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic List<Element> getItems(String node, String jid) {\n\t\treturn getItems(node, jid, true);\n\t}\n\n\tpublic List<Element> getItems(String node, String jid, boolean admin) {\n\t\tList<Element> result = null;\n\n\t\tif (items != null) {\n\t\t\tresult = new ArrayList<Element>();\n\t\t\tfor (ServiceEntity item : items) {\n\t\t\t\tif (item.adminOnly && !admin) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tresult.add(item.getDiscoItem(node, jid));\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic String getJID() {\n\t\treturn jid;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic String getNode() {\n\t\treturn node;\n\t}\n\n\tpublic boolean isAdminOnly() {\n\t\treturn adminOnly;\n\t}\n\n\tpublic void setAdminOnly(boolean adminOnly) {\n\t\tthis.adminOnly = adminOnly;\n\t}\n\n\tpublic void setDescription(String description) {\n\t\tthis.name = description;\n\t}\n\n\tpublic void setFeatures(String... features) {\n\t\tthis.features = null;\n\t\taddFeatures(features);\n\t}\n\n\tpublic void setIdentities(ServiceIdentity... identities) {\n\t\tthis.identities = new ArrayList<ServiceIdentity>();\n\t\tCollections.addAll(this.identities, identities);\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/disco/ServiceIdentity.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.disco;\n\nimport tigase.xml.Element;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Describe class ServiceIdentity here.\n * <br>\n * Created: Sat Feb 10 13:34:54 2007\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class ServiceIdentity {\n\n\tprivate final String category;\n\tprivate final String lang;\n\tprivate final String name;\n\tprivate final String type;\n\n\tpublic static String[] getServiceIdentitiesCapsFromDiscoInfo(Element discoInfo) {\n\t\tList<String> list = new ArrayList<>();\n\t\tfor (ServiceIdentity serviceIdentity : getServiceIdentitiesFromDiscoInfo(discoInfo)) {\n\t\t\tString asCapsString = serviceIdentity.getAsCapsString();\n\t\t\tlist.add(asCapsString);\n\t\t}\n\t\treturn list.toArray(new String[0]);\n\t}\n\n\tpublic static ServiceIdentity[] getServiceIdentitiesFromDiscoInfo(Element discoInfo) {\n\t\tList<ServiceIdentity> list = new ArrayList<>();\n\t\tfinal List<Element> identityElements = discoInfo.findChildren(child -> child.getName().equals(\"identity\"));\n\t\tif (identityElements != null && !identityElements.isEmpty()) {\n\t\t\tfor (Element identityElement : identityElements) {\n\t\t\t\tServiceIdentity serviceIdentityFromElement = of(identityElement);\n\t\t\t\tlist.add(serviceIdentityFromElement);\n\t\t\t}\n\t\t}\n\t\treturn list.toArray(new ServiceIdentity[0]);\n\t}\n\n\tprivate static ServiceIdentity of(Element identity) throws IllegalArgumentException {\n\t\tfinal String lang = identity.getAttributeStaticStr(\"xml:lang\");\n\t\tfinal String category = identity.getAttributeStaticStr(\"category\");\n\t\tfinal String name = identity.getAttributeStaticStr(\"name\");\n\t\tfinal String type = identity.getAttributeStaticStr(\"type\");\n\n\t\tif (category == null || type == null) {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\tString.format(\"Neither category: %s nor type: %s can be null\", category, type));\n\t\t}\n\n\t\tif (name == null && lang == null) {\n\t\t\treturn new ServiceIdentity(category, type);\n\t\t} else if (lang == null) {\n\t\t\treturn new ServiceIdentity(category, type, name);\n\t\t} else {\n\t\t\treturn new ServiceIdentity(category, type, name, lang);\n\t\t}\n\n\t}\n\n\t/**\n\t * Creates a new <code>ServiceIdentity</code> instance.\n\t */\n\tpublic ServiceIdentity(String category, String type) {\n\t\tthis.category = category;\n\t\tthis.type = type;\n\t\tthis.name = \"\";\n\t\tthis.lang = \"\";\n\t}\n\n\tpublic ServiceIdentity(String category, String type, String name) {\n\t\tthis.category = category;\n\t\tthis.type = type;\n\t\tthis.name = name;\n\t\tthis.lang = \"\";\n\t}\n\n\tpublic ServiceIdentity(String category, String type, String name, String lang) {\n\t\tthis.category = category;\n\t\tthis.type = type;\n\t\tthis.name = name;\n\t\tthis.lang = lang;\n\t}\n\n\tpublic String getCategory() {\n\t\treturn category;\n\t}\n\n\tpublic String getType() {\n\t\treturn type;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic Element getElement() {\n\t\treturn new Element(\"identity\", new String[]{\"category\", \"type\", \"name\"}, new String[]{category, type, name});\n\t}\n\n\tpublic String getAsCapsString() {\n\t\treturn String.format(\"%s/%s/%s/%s\", category, type, lang, name);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tServiceIdentity that = (ServiceIdentity) o;\n\n\t\tif (!category.equals(that.category)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!name.equals(that.name)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!type.equals(that.type)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn lang.equals(that.lang);\n\n\t}\n\n\tpublic String getLang() {\n\t\treturn lang;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = category.hashCode();\n\t\tresult = 31 * result + name.hashCode();\n\t\tresult = 31 * result + type.hashCode();\n\t\tresult = 31 * result + lang.hashCode();\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/disco/XMPPService.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.disco;\n\nimport tigase.server.ServerComponent;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.List;\n\n/**\n * Interface XMPPService\n * <br>\n * Objects which implement this interface can respond to \"ServiceDiscovery\" requests. All such requests are managed by\n * MessageRouter instance.\n * <br>\n * Created: Tue Nov 22 07:07:11 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface XMPPService\n\t\textends ServerComponent {\n\n\t/**\n\t * A convenience constant with service discovery info xmlns string.\n\t */\n\tpublic static final String INFO_XMLNS = \"http://jabber.org/protocol/disco#info\";\n\n\t/**\n\t * A convenience constant with service discovery items xmlns string.\n\t */\n\tpublic static final String ITEMS_XMLNS = \"http://jabber.org/protocol/disco#items\";\n\n\t/**\n\t * A convenience constant with all stream features for service discovery.\n\t */\n\tpublic static final String[] DEF_FEATURES = {INFO_XMLNS, ITEMS_XMLNS};\n\n\t/**\n\t * A convenience constant with component features related to ad-hoc commands.\n\t */\n\tpublic static final String[] CMD_FEATURES = {\"http://jabber.org/protocol/commands\", \"jabber:x:data\"};\n\n\t/**\n\t * Returns service discovery info for the component. If the jid is null then this is info for the top level request.\n\t * SM may return disco info on the top level. Other components should not.\n\t *\n\t * @param node is service discovery node for which the request is made. Is normally null for the component top level\n\t * request.\n\t * @param jid is the jid to which the request has been made.\n\t * @param from is the request sender address. Some service discovery information is only meant for administrators.\n\t *\n\t * @return returns an XML Element with service discovery data.\n\t */\n\tElement getDiscoInfo(String node, JID jid, JID from);\n\n\t/**\n\t * Returns service discovery items for the component. If the JID is null then this is request for the top level\n\t * request. SM may return disco items on the top level, other components should just return it's top level service\n\t * discovery item for null node.\n\t *\n\t * @param node is a service discovery node for which the request has been made.\n\t * @param jid is the jid to which the request has been made.\n\t * @param from is the request sender address. Some service discovery information is only meant for administrators.\n\t *\n\t * @return a list of service discovery items for this component or the component itself disco item for the top level\n\t * request.\n\t */\n\tList<Element> getDiscoItems(String node, JID jid, JID from);\n\n\t/**\n\t * Returns features for top level disco info\n\t *\n\t * @param from a request sender address. Some service disco elements are meant to be available only to system\n\t * administrarors. The component is responsible to check whether the sender is the component administrator and\n\t * return results appropriate.\n\t *\n\t * @return a list of elements with service discovery features.\n\t */\n\tList<Element> getDiscoFeatures(JID from);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/disco/XMPPServiceCollector.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.disco;\n\nimport tigase.server.AbstractComponentRegistrator;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.ServerComponent;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.List;\nimport java.util.Queue;\nimport java.util.logging.Logger;\n\n/**\n * Class XMPPServiceCollector\n * <br>\n * Created: Tue Nov 22 07:07:11 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic abstract class XMPPServiceCollector\n\t\textends AbstractComponentRegistrator<XMPPService> {\n\n\t@SuppressWarnings(\"unused\")\n\tprivate static final Logger log = Logger.getLogger(XMPPServiceCollector.class.getName());\n\n\tprivate ServiceEntity serviceEntity = null;\n\n\tpublic XMPPServiceCollector() {\n\t\tserviceEntity = new ServiceEntity(\"Tigase\", \"server\", \"Session manager\");\n\t\tserviceEntity.addIdentities(new ServiceIdentity[]{new ServiceIdentity(\"server\", \"im\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  tigase.server.XMPPServer.NAME + \" ver. \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  tigase.server.XMPPServer.getImplementationVersion())});\n\t}\n\n\t@Override\n\tpublic void componentAdded(XMPPService component) {\n\t}\n\n\t@Override\n\tpublic void componentRemoved(XMPPService component) {\n\t}\n\n\t@Override\n\tpublic boolean isCorrectType(ServerComponent component) {\n\t\treturn component instanceof XMPPService;\n\t}\n\n\t@Override\n\tpublic void processPacket(final Packet packet, final Queue<Packet> results) {\n\t\tif (packet.isXMLNSStaticStr(Iq.IQ_QUERY_PATH, INFO_XMLNS) ||\n\t\t\t\tpacket.isXMLNSStaticStr(Iq.IQ_QUERY_PATH, ITEMS_XMLNS)) {\n\t\t\tJID jid = packet.getStanzaTo();\n\t\t\tJID from = packet.getStanzaFrom();\n\t\t\tString node = packet.getAttributeStaticStr(Iq.IQ_QUERY_PATH, \"node\");\n\t\t\tElement query = packet.getElement().getChild(\"query\").clone();\n\n\t\t\tif (packet.isXMLNSStaticStr(Iq.IQ_QUERY_PATH, INFO_XMLNS)) {\n\t\t\t\tfor (XMPPService comp : components.values()) {\n\t\t\t\t\tElement resp = comp.getDiscoInfo(node, jid, from);\n\n\t\t\t\t\tif (resp != null) {\n\t\t\t\t\t\tquery = resp;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}      // end of for ()\n\t\t\t}\n\t\t\tif (packet.isXMLNSStaticStr(Iq.IQ_QUERY_PATH, ITEMS_XMLNS)) {\n\t\t\t\tfor (XMPPService comp : components.values()) {\n\t\t\t\t\tList<Element> items = comp.getDiscoItems(node, jid, from);\n\n\t\t\t\t\tif ((items != null) && (items.size() > 0)) {\n\t\t\t\t\t\tquery.addChildren(items);\n\t\t\t\t\t}    // end of if (stats != null && stats.count() > 0)\n\t\t\t\t}      // end of for ()\n\t\t\t}\n\t\t\tresults.offer(packet.okResult(query, 0));\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/disteventbus/component/EventBusComponent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.disteventbus.component;\n\npublic class EventBusComponent\n\t\textends tigase.eventbus.component.EventBusComponent {\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/EventBus.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.xml.Element;\n\npublic interface EventBus {\n\n\t/**\n\t * Adds listener of event to EventBus.\n\t *\n\t * @param eventClass class of expected event.\n\t * @param listener listener.\n\t * @param <T> class of event.\n\t */\n\t<T> void addListener(Class<T> eventClass, EventListener<T> listener);\n\n\t/**\n\t * Adds listener of event to EventBus. If event matching to given packageName and eventName will be fired as Object\n\t * (not Element), then event will be converted to XML.\n\t *\n\t * @param packageName package of event to listen.\n\t * @param eventName name of event to listen. May be <code>null</code>, then listener is listening for all events\n\t * with specific package name.\n\t * @param listener listener.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"Class based events should be used\")\n\tvoid addListener(String packageName, String eventName, EventListener<Element> listener);\n\n\t/**\n\t * Fires event.\n\t *\n\t * @param event event to fire.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"Class based events should be used\")\n\tvoid fire(Object event);\n\n\t/**\n\t * Fires event.\n\t *\n\t * @param event event to fire.\n\t */\n\tvoid fire(EventBusEvent event);\n\n\t/**\n\t * Register all methods annotated with {@link HandleEvent @HandleEvent} as events handlers to EventBus.\n\t *\n\t * @param eventConsumer events consumer object.\n\t *\n\t * @throws RegistrationException if it is impossible to register all handlers method.\n\t */\n\tvoid registerAll(Object eventConsumer);\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"Class based events should be used\")\n\tvoid registerEvent(String event, String description, boolean privateEvent);\n\n\tpublic void registerEvent(Class<?> event, String description, boolean privateEvent);\n\n\t/**\n\t * Removes listener from Eventbus.\n\t *\n\t * @param listener listener to remove.\n\t */\n\t<T> void removeListener(EventListener<T> listener);\n\n\t/**\n\t * Unregister all methods annotated with {@link HandleEvent @HandleEvent} as events handlers from EventBus.\n\t *\n\t * @param eventConsumer events consumer object.\n\t */\n\tvoid unregisterAll(Object eventConsumer);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/EventBusAction.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.eventbus;\n\n/**\n * Interface declaring actions that should be distributed and handled by EventBus\n */\npublic interface EventBusAction {\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/EventBusEvent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.eventbus;\n\npublic interface EventBusEvent {\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/EventBusException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus;\n\npublic class EventBusException\n\t\textends RuntimeException {\n\n\tpublic EventBusException() {\n\t}\n\n\tpublic EventBusException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic EventBusException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n\tpublic EventBusException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n\tpublic EventBusException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {\n\t\tsuper(message, cause, enableSuppression, writableStackTrace);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/EventBusFactory.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus;\n\nimport tigase.eventbus.impl.EventBusImplementation;\nimport tigase.eventbus.impl.EventsRegistrar;\n\npublic class EventBusFactory {\n\n\tprivate final static EventBusImplementation eventBus = new EventBusImplementation();\n\n\tpublic static EventBus getInstance() {\n\t\treturn eventBus;\n\t}\n\n\tpublic static EventsRegistrar getRegistrar() {\n\t\treturn eventBus.getRegistrar();\n\t}\n\n\tprivate EventBusFactory() {\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/EventListener.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus;\n\npublic interface EventListener<E> {\n\n\tvoid onEvent(E event);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/EventRoutedTransientFiller.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus;\n\n/**\n * Interface implemented by classes responsible for filling transient fields of events after event is delivered on other\n * node in cluster\n *\n * @author andrzej\n */\npublic interface EventRoutedTransientFiller {\n\n\tboolean fillEvent(Object event);\n\n\tClass<?> getEventClass();\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/EventRoutingSelector.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus;\n\nimport tigase.eventbus.component.stores.Subscription;\n\nimport java.util.Collection;\n\n/**\n * This interface is required to be implemented by every class which wants to change routing of delivering events to\n * other machines.\n *\n * @author andrzej\n */\npublic interface EventRoutingSelector {\n\n\t/**\n\t * Returns class of event for which it modifies delivery\n\t */\n\tClass getEventClass();\n\n\t/**\n\t * Method responsible for actual modification of delivery by adding and removing items to Subscriptions collection\n\t *\n\t * @param event instance of event\n\t * @param subscriptions original list of subscriptions\n\t */\n\tCollection<Subscription> getSubscriptions(Object event, Collection<Subscription> subscriptions);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/EventSourceListener.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus;\n\npublic interface EventSourceListener<E> {\n\n\tvoid onEvent(E event, Object source);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/FillRoutedEvent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus;\n\nimport java.lang.annotation.*;\n\n/**\n * Annotation used to mark methods of consumer class as responsible for filling event transient fields\n *\n * @author andrzej\n */\n@Target({ElementType.METHOD})\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\npublic @interface FillRoutedEvent {\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/HandleEvent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus;\n\nimport java.lang.annotation.*;\n\n/**\n * Annotation to mark method as event handler. <br>\n * <br>\n * Example:\n * <br>\n * <pre>\n * <code>\n * public class Consumer {\n * &#64;HandleEvent\n * public void onCatchSomeNiceEvent(Event01 event) {\n * }\n * &#64;HandleEvent\n * public void onCatchSomeNiceEvent(Event02 event) {\n * }\n * }\n * </code>\n * </pre>\n * <br>\n * Handler method must have only one argument with type equals to expected event.\n */\n@Target({ElementType.METHOD})\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\npublic @interface HandleEvent {\n\n\tenum Type {\n\t\tremote,\n\t\tlocal,\n\t\tall\n\t}\n\n\tType filter() default Type.all;\n\n\tboolean sync() default false;\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/RegistrationException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus;\n\npublic class RegistrationException\n\t\textends RuntimeException {\n\n\tpublic RegistrationException() {\n\t}\n\n\tpublic RegistrationException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic RegistrationException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n\tpublic RegistrationException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n\tpublic RegistrationException(String message, Throwable cause, boolean enableSuppression,\n\t\t\t\t\t\t\t\t boolean writableStackTrace) {\n\t\tsuper(message, cause, enableSuppression, writableStackTrace);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/RouteEvent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus;\n\nimport java.lang.annotation.*;\n\n/**\n * Annotation used to mark methods of consumer class as responsible for routing of events\n *\n * @author andrzej\n */\n@Target({ElementType.METHOD})\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\npublic @interface RouteEvent {\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/TickMinuteEvent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus;\n\npublic class TickMinuteEvent implements EventBusEvent {\n\n\tprivate final long timestamp;\n\n\tpublic TickMinuteEvent(long timestamp) {\n\t\tthis.timestamp = timestamp;\n\t}\n\n\tpublic long getTimestamp() {\n\t\treturn timestamp;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tfinal StringBuffer sb = new StringBuffer(\"TickMinuteEvent{\");\n\t\tsb.append(\"timestamp=\").append(timestamp);\n\t\tsb.append('}');\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/XMLEventBusEvent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.eventbus;\n\nimport tigase.xml.Element;\n\npublic interface XMLEventBusEvent<E extends EventBusEvent> extends EventBusEvent {\n\n\tElement serialize();\n\n\tE deserialize(Element element);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/component/AbstractEventBusModule.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.component;\n\nimport tigase.component.modules.AbstractModule;\nimport tigase.kernel.beans.Inject;\nimport tigase.xmpp.jid.JID;\n\npublic abstract class AbstractEventBusModule\n\t\textends AbstractModule {\n\n\tprivate static long id = 0;\n\n\t@Inject\n\tprivate EventBusComponent component;\n\n\tprotected boolean isClusteredEventBus(final JID jid) {\n\t\treturn jid.getLocalpart().equals(\"eventbus\") && component.getNodesConnected().contains(jid);\n\t}\n\n\tprotected String nextStanzaID() {\n\n\t\tString prefix = component.getComponentId().getDomain();\n\n\t\tsynchronized (this) {\n\t\t\treturn prefix + \"-\" + (++id);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/component/ElemPathCriteria.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.component;\n\nimport tigase.criteria.Criteria;\nimport tigase.xml.Element;\n\npublic class ElemPathCriteria\n\t\timplements Criteria {\n\n\tprivate final String[] names;\n\tprivate final String[] xmlns;\n\n\tpublic ElemPathCriteria(String[] elemNames, String[] namespaces) {\n\t\tthis.names = elemNames;\n\t\tthis.xmlns = namespaces;\n\t}\n\n\t@Override\n\tpublic Criteria add(Criteria criteria) {\n\t\tthrow new RuntimeException(\"UNSUPPORTED!\");\n\t}\n\n\t@Override\n\tpublic boolean match(Element element) {\n\n\t\tboolean match = element.getName().equals(names[0]);\n\t\tif (match && xmlns[0] != null) {\n\t\t\tmatch &= xmlns[0].equals(element.getXMLNS());\n\t\t}\n\n\t\tElement child = element;\n\t\tint i = 1;\n\t\tfor (; i < names.length; i++) {\n\t\t\tString n = names[i];\n\t\t\tString x = xmlns[i];\n\n\t\t\tchild = child.getChildStaticStr(n, x);\n\n\t\t\tmatch &= child != null;\n\n\t\t\tif (!match) {\n\t\t\t\treturn match;\n\t\t\t}\n\n\t\t}\n\n\t\t// TODO Auto-generated method stub\n\t\treturn match;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/component/EventBusComponent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.component;\n\nimport tigase.cluster.api.ClusterControllerIfc;\nimport tigase.cluster.api.ClusteredComponentIfc;\nimport tigase.component.AbstractKernelBasedComponent;\nimport tigase.component.modules.Module;\nimport tigase.component.modules.impl.AdHocCommandModule;\nimport tigase.component.modules.impl.JabberVersionModule;\nimport tigase.component.modules.impl.XmppPingModule;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.eventbus.component.stores.Affiliation;\nimport tigase.eventbus.component.stores.AffiliationStore;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\nimport tigase.stats.StatisticsList;\nimport tigase.sys.TigaseRuntime;\nimport tigase.xmpp.jid.JID;\n\nimport javax.script.ScriptEngineManager;\nimport java.util.logging.Level;\n\n@Bean(name = \"eventbus\", parent = Kernel.class, active = true)\n@ConfigType({ConfigTypeEnum.DefaultMode, ConfigTypeEnum.SessionManagerMode, ConfigTypeEnum.ConnectionManagersMode,\n\t\t\t ConfigTypeEnum.ComponentMode})\npublic class EventBusComponent\n\t\textends AbstractKernelBasedComponent\n\t\timplements ClusteredComponentIfc {\n\n\tpublic EventBusComponent() {\n\t}\n\n\t@Override\n\tpublic String getDiscoCategory() {\n\t\treturn \"pubsub\";\n\t}\n\n\t@Override\n\tpublic String getDiscoCategoryType() {\n\t\treturn \"service\";\n\t}\n\n\t@Override\n\tpublic String getDiscoDescription() {\n\t\treturn \"Distributed EventBus\";\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t\tsuper.getStatistics(list);\n\t}\n\n\t@Override\n\tpublic boolean isDiscoNonAdmin() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean isSubdomain() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void onNodeDisconnected(JID jid) {\n\t\tsuper.onNodeDisconnected(jid);\n\n\t\tModule module = kernel.getInstance(SubscribeModule.ID);\n\t\tif (module != null && module instanceof SubscribeModule) {\n\t\t\t((SubscribeModule) module).clusterNodeDisconnected(jid);\n\t\t}\n\t\tkernel.getInstance(AffiliationStore.class).removeAffiliation(jid);\n\t}\n\n\t@Override\n\tpublic void processPacket(tigase.server.Packet packet) {\n\t\tsuper.processPacket(packet);\n\t}\n\n\t@Override\n\tpublic int processingInThreads() {\n\t\treturn TigaseRuntime.getTigaseRuntime().getCPUsNumber();\n\t}\n\n\t@Override\n\tpublic int processingOutThreads() {\n\t\treturn TigaseRuntime.getTigaseRuntime().getCPUsNumber();\n\t}\n\n\t@Override\n\tpublic void setClusterController(ClusterControllerIfc cl_controller) {\n\t}\n\n\t@Override\n\tprotected void onNodeConnected(JID jid) {\n\t\tsuper.onNodeConnected(jid);\n\n\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\tlog.fine(\"Cluster node \" + jid + \" added to Affiliation Store\");\n\t\t}\n\t\tkernel.getInstance(AffiliationStore.class).putAffiliation(jid, Affiliation.owner);\n\n\t\tModule module = kernel.getInstance(SubscribeModule.ID);\n\t\tif (module != null && module instanceof SubscribeModule) {\n\t\t\t((SubscribeModule) module).clusterNodeConnected(jid);\n\t\t}\n\n\t}\n\n\t@Override\n\tprotected void registerModules(Kernel kernel) {\n\t\tkernel.registerBean(\"scriptEngineManager\").asInstance(new ScriptEngineManager()).exec();\n\t\tkernel.registerBean(\"eventBusRegistrar\").asInstance(EventBusFactory.getRegistrar()).exec();\n\t\tkernel.registerBean(\"localEventBus\").asInstance(EventBusFactory.getInstance()).exec();\n\n\t\tkernel.registerBean(XmppPingModule.class).exec();\n\t\tkernel.registerBean(JabberVersionModule.class).exec();\n\t\tkernel.registerBean(AdHocCommandModule.class).exec();\n\t\tkernel.registerBean(EventbusDiscoveryModule.class).exec();\n\n\t\t// modules\n\t\tkernel.registerBean(SubscribeModule.class).exec();\n\t\tkernel.registerBean(UnsubscribeModule.class).exec();\n\t\tkernel.registerBean(EventPublisherModule.class).exec();\n\t\tkernel.registerBean(EventReceiverModule.class).exec();\n\n\t\t// beans\n\t\t// kernel.registerBean(ListenerScriptRegistrar.class).exec();\n//\t\tkernel.registerBean(AffiliationStore.class).exec();\n//\t\tkernel.registerBean(\"subscriptionStore\").asClass(SubscriptionStore.class).exec();\n\t\t// ad-hoc commands\n\t\t// kernel.registerBean(AddListenerScriptCommand.class).exec();\n\t\t// kernel.registerBean(RemoveListenerScriptCommand.class).exec();\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/component/EventPublisherModule.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.component;\n\nimport tigase.component.exceptions.ComponentException;\nimport tigase.criteria.Criteria;\nimport tigase.eventbus.EventRoutingSelector;\nimport tigase.eventbus.component.stores.Subscription;\nimport tigase.eventbus.component.stores.SubscriptionStore;\nimport tigase.eventbus.impl.AbstractHandler;\nimport tigase.eventbus.impl.EventBusImplementation;\nimport tigase.eventbus.impl.EventBusSerializer;\nimport tigase.eventbus.impl.EventName;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.server.Packet;\nimport tigase.server.Permissions;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.JID;\n\nimport java.io.Serializable;\nimport java.util.Collection;\nimport java.util.logging.Level;\n\n@Bean(name = EventPublisherModule.ID, active = true)\npublic class EventPublisherModule\n\t\textends AbstractEventBusModule\n\t\timplements Initializable, UnregisterAware {\n\n\tpublic final static String ID = \"publisher\";\n\t@Inject\n\tprivate EventBusComponent component;\n\t@Inject(nullAllowed = false, bean = \"localEventBus\")\n\tprivate EventBusImplementation localEventBus;\n\tprivate EventBusSerializer serializer = new EventBusSerializer();\n\t@Inject\n\tprivate SubscriptionStore subscriptionStore;\n\tprivate final AbstractHandler firedEventHandler = new AbstractHandler(null, null) {\n\t\t@Override\n\t\tpublic void dispatch(Object event, Object source, boolean remotelyGeneratedEvent) {\n\t\t\tif (remotelyGeneratedEvent) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (event instanceof Element) {\n\t\t\t\tpublishEvent((Element) event);\n\t\t\t} else if (!(event instanceof EventBusImplementation.InternalEventbusEvent)) {\n\t\t\t\tpublishObjectEvent(event);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic AbstractHandler.Type getRequiredEventType() {\n\t\t\treturn Type.asIs;\n\t\t}\n\t};\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\tlocalEventBus.removeHandler(firedEventHandler);\n\t}\n\n\t@Override\n\tpublic String[] getFeatures() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Criteria getModuleCriteria() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tlocalEventBus.addHandler(firedEventHandler);\n\n\t}\n\n\t@Override\n\tpublic void process(Packet packet) throws ComponentException, TigaseStringprepException {\n\t}\n\n\tpublic void publishEvent(final Element event) {\n\t\tEventName en = new EventName(event.getName());\n\n\t\tfinal String isRemote = event.getAttributeStaticStr(\"remote\");\n\t\tif (isRemote != null && (\"true\".equals(isRemote) || \"1\".equals(isRemote))) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Remote event. No need to redistribute this way. \" + event.toString());\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tfinal String isLocal = event.getAttributeStaticStr(\"local\");\n\t\tif (isLocal != null && (\"true\".equals(isLocal) || \"1\".equals(isLocal))) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Event for local subscribers only. Skipping. \" + event.toString());\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tfinal Collection<Subscription> subscribers = subscriptionStore.getSubscribersJIDs(en.getPackage(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  en.getName());\n\t\tpublishEvent(en.getPackage(), en.getName(), event, subscribers);\n\t}\n\n\tpublic void publishEvent(String eventPackage, String name, Element event, Collection<Subscription> subscribers) {\n\t\ttry {\n\t\t\tfinal Element eventElem = new Element(\"event\", new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{\"http://jabber.org/protocol/pubsub#event\"});\n\t\t\tfinal Element itemsElem = new Element(\"items\", new String[]{\"node\"},\n\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{EventName.toString(eventPackage, name)});\n\t\t\teventElem.addChild(itemsElem);\n\t\t\tfinal Element itemElem = new Element(\"item\");\n\t\t\titemElem.addChild(event);\n\t\t\titemsElem.addChild(itemElem);\n\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.log(Level.FINER, \"Sending event ({0}, {1}, {2}) to {3}\",\n\t\t\t\t\t\tnew Object[]{name, eventPackage, event, subscribers});\n\t\t\t}\n\n\t\t\tfor (Subscription subscriber : subscribers) {\n\n\t\t\t\tString from;\n\t\t\t\tif (subscriber.getServiceJID() == null) {\n\t\t\t\t\tfrom = component.getComponentId().toString();\n\t\t\t\t} else {\n\t\t\t\t\tfrom = subscriber.getServiceJID().toString();\n\t\t\t\t}\n\t\t\t\tJID toJID = subscriber.getJid();\n\n\t\t\t\tpublishEvent(eventElem, from, toJID);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.FINEST, \"Error publishing event\", e);\n\t\t}\n\t}\n\n\tpublic void publishObjectEvent(Object event) {\n\t\tif (!(event instanceof Serializable)) {\n\t\t\treturn;\n\t\t}\n\t\tClass<?> eventClass = event.getClass();\n\t\tfinal String packageName = eventClass.getPackage().getName();\n\t\tfinal String eventName = eventClass.getSimpleName();\n\n\t\tfinal Collection<Subscription> subscribers = getSubscribers(packageName, eventName, event);\n\t\tif (subscribers.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\n\t\tElement eventElement = serializer.serialize(event);\n\t\teventElement.setAttribute(\"serialized\", \"true\");\n\n\t\tpublishEvent(packageName, eventName, eventElement, subscribers);\n\t}\n\n\tprotected Collection<Subscription> getSubscribers(String packageName, String eventName, Object event) {\n\t\tCollection<Subscription> subscribers = subscriptionStore.getSubscribersJIDs(packageName, eventName);\n\n\t\tEventRoutingSelector selector = localEventBus.getEventRoutingSelector(event.getClass());\n\t\tif (selector != null) {\n\t\t\tsubscribers = selector.getSubscriptions(event, subscribers);\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"for event {0}, found subscribers using selector: {1}\",\n\t\t\t\t\t\tnew Object[]{event.getClass().getCanonicalName(), selector.getClass()});\n\t\t\t}\n\t\t}\n\n\t\treturn subscribers;\n\t}\n\n\tprivate void publishEvent(Element pubsubEventElem, String from, JID toJID) throws TigaseStringprepException {\n\t\tPacket message = Packet.packetInstance(new Element(\"message\", new String[]{\"to\", \"from\", \"id\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{toJID.toString(), from, nextStanzaID()}));\n\t\tmessage.getElement().addChild(pubsubEventElem);\n\t\tmessage.setXMLNS(Packet.CLIENT_XMLNS);\n\n\t\tmessage.setPermissions(Permissions.ADMIN);\n\n\t\twrite(message);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/component/EventReceiverModule.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.component;\n\nimport tigase.component.exceptions.ComponentException;\nimport tigase.criteria.Criteria;\nimport tigase.eventbus.EventRoutedTransientFiller;\nimport tigase.eventbus.component.stores.Affiliation;\nimport tigase.eventbus.component.stores.AffiliationStore;\nimport tigase.eventbus.component.stores.Subscription;\nimport tigase.eventbus.component.stores.SubscriptionStore;\nimport tigase.eventbus.impl.EventBusImplementation;\nimport tigase.eventbus.impl.EventBusSerializer;\nimport tigase.eventbus.impl.EventName;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Packet;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\n\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Bean(name = EventReceiverModule.ID, active = true)\npublic class EventReceiverModule\n\t\textends AbstractEventBusModule {\n\n\tpublic final static String ID = \"receiver\";\n\tprivate static final Logger log = Logger.getLogger(EventReceiverModule.class.getCanonicalName());\n\tprivate static final Criteria CRIT = new ElemPathCriteria(new String[]{\"message\", \"event\"}, new String[]{null,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"http://jabber.org/protocol/pubsub#event\"});\n\t@Inject\n\tprivate AffiliationStore affiliationStore;\n\n\t@Inject\n\tprivate EventPublisherModule eventPublisherModule;\n\n\t@Inject(nullAllowed = false, bean = \"localEventBus\")\n\tprivate EventBusImplementation localEventBus;\n\tprivate EventBusSerializer serializer = new EventBusSerializer();\n\t@Inject\n\tprivate SubscriptionStore subscriptionStore;\n\n\t@Override\n\tpublic String[] getFeatures() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Criteria getModuleCriteria() {\n\t\treturn CRIT;\n\t}\n\n\t@Override\n\tpublic void process(Packet packet) throws ComponentException, TigaseStringprepException {\n\t\tfinal Affiliation affiliation = affiliationStore.getAffiliation(packet.getStanzaFrom());\n\t\tif (!affiliation.isPublishItem()) {\n\t\t\tthrow new ComponentException(Authorization.FORBIDDEN);\n\t\t}\n\n\t\tfinal String type = packet.getElement().getAttributeStaticStr(\"type\");\n\n\t\tif (type != null && type.equals(\"error\")) {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.fine(\"Ignoring error message! \" + packet);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.finer(\"Received event stanza: \" + packet.toStringFull());\n\t\t}\n\n\t\tElement eventElem = packet.getElement().getChild(\"event\", \"http://jabber.org/protocol/pubsub#event\");\n\t\tElement itemsElem = eventElem.getChild(\"items\");\n\n\t\tfor (Element item : itemsElem.getChildren()) {\n\t\t\tif (!\"item\".equals(item.getName())) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (Element event : item.getChildren()) {\n\t\t\t\tEventName eventName = new EventName(event.getName());\n\n\t\t\t\tevent.setAttribute(\"remote\", \"true\");\n\n\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\tlog.finer(\"Received event \" + eventName + \": \" + event);\n\t\t\t\t}\n\n\t\t\t\tfireEventLocally(eventName, event);\n\n\t\t\t\t// forwarding event to _non cluster_ subscribers.\n\t\t\t\tfinal Collection<Subscription> subscribers = subscriptionStore.getSubscribersJIDs(\n\t\t\t\t\t\teventName.getPackage(), eventName.getName());\n\t\t\t\tIterator<Subscription> it = subscribers.iterator();\n\t\t\t\twhile (it.hasNext()) {\n\t\t\t\t\tSubscription subscription = it.next();\n\t\t\t\t\tif (subscription.isInClusterSubscription()) {\n\t\t\t\t\t\tit.remove();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\teventPublisherModule.publishEvent(eventName.getPackage(), eventName.getName(), event, subscribers);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate Object deserialize(final Element event) {\n\t\t// TODO: for now commented. This optimization will be enabled in the future,\n\t\t//  when attribute 'serialized' will be set for sure on all nodes.\n//\t\tfinal String serialized = event.getAttributeStaticStr(\"serialized\");\n//\t\tif (serialized == null || !serialized.equals(\"true\")) {\n//\t\t\treturn null;\n//\t\t}\n\t\treturn serializer.deserialize(event);\n\t}\n\n\tprivate void fireEventLocally(final EventName name, final Element event) {\n\t\tObject obj = deserialize(event);\n\t\tif (obj == null) {\n\t\t\tobj = event;\n\t\t} else {\n\t\t\tboolean ready = true;\n\t\t\tCollection<EventRoutedTransientFiller> fillers = localEventBus.getEventRoutedTransientFillers(\n\t\t\t\t\tobj.getClass());\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"for event = {0}, found following fillers: {1}\", new Object[]{name, fillers});\n\t\t\t}\n\t\t\tif (fillers != null) {\n\t\t\t\tfor (EventRoutedTransientFiller f : fillers) {\n\t\t\t\t\tready &= f.fillEvent(obj);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!ready) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tlocalEventBus.fire(obj, this, true);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/component/EventbusDiscoveryModule.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.component;\n\nimport tigase.component.exceptions.ComponentException;\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.component.modules.impl.DiscoveryModule;\nimport tigase.eventbus.component.stores.Affiliation;\nimport tigase.eventbus.component.stores.AffiliationStore;\nimport tigase.eventbus.impl.EventName;\nimport tigase.eventbus.impl.EventsRegistrar;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.PacketErrorTypeException;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\n\n@Bean(name = DiscoveryModule.ID, active = true)\npublic class EventbusDiscoveryModule\n\t\textends DiscoveryModule {\n\n\t@Inject\n\tprivate AffiliationStore affiliationStore;\n\t@Inject\n\tprivate EventsRegistrar registrar;\n\n\t@Override\n\tprotected void processDiscoInfo(Packet packet, JID jid, String node, JID senderJID)\n\t\t\tthrows ComponentException, RepositoryException {\n\t\tfinal Affiliation affiliation = affiliationStore.getAffiliation(packet.getStanzaFrom());\n\t\tif (node == null || !affiliation.isSubscribe()) {\n\t\t\tsuper.processDiscoInfo(packet, jid, node, senderJID);\n\t\t} else {\n\t\t\tfinal Map<String, Set<String>> tree = prepareEventsTree();\n\n\t\t\tElement resultQuery = new Element(\"query\", new String[]{\"xmlns\", \"node\"},\n\t\t\t\t\t\t\t\t\t\t\t  new String[]{\"http://jabber.org/protocol/disco#info\", node});\n\t\t\tPacket resultIq = packet.okResult(resultQuery, 0);\n\n\t\t\tif (tree.containsKey(node)) {\n\t\t\t\tresultQuery.addChild(new Element(\"identity\", new String[]{\"category\", \"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t new String[]{\"pubsub\", \"collection\"}));\n\t\t\t} else if (registrar.isRegistered(node)) {\n\t\t\t\tresultQuery.addChild(\n\t\t\t\t\t\tnew Element(\"identity\", new String[]{\"category\", \"type\"}, new String[]{\"pubsub\", \"leaf\"}));\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\twrite(Authorization.ITEM_NOT_FOUND.getResponseMessage(packet, \"Unknown event\", true));\n\t\t\t\t\treturn;\n\t\t\t\t} catch (PacketErrorTypeException e) {\n\t\t\t\t\tthrow new RuntimeException(e);\n\t\t\t\t}\n\t\t\t}\n\t\t\tresultQuery.addChild(\n\t\t\t\t\tnew Element(\"feature\", new String[]{\"var\"}, new String[]{\"http://jabber.org/protocol/pubsub\"}));\n\n\t\t\twrite(resultIq);\n\t\t}\n\n\t}\n\n\t@Override\n\tprotected void processDiscoItems(Packet packet, JID jid, String node, JID senderJID)\n\t\t\tthrows ComponentException, RepositoryException {\n\t\tfinal Affiliation affiliation = affiliationStore.getAffiliation(packet.getStanzaFrom());\n\t\tif (!affiliation.isSubscribe()) {\n\t\t\twrite(packet.okResult(new Element(\"query\", new String[]{Packet.XMLNS_ATT}, new String[]{DISCO_ITEMS_XMLNS}),\n\t\t\t\t\t\t\t\t  0));\n\t\t\treturn;\n\t\t}\n\n\t\tfinal Map<String, Set<String>> tree = prepareEventsTree();\n\n\t\tif (node == null) {\n\t\t\tList<Element> items = new ArrayList<Element>();\n\t\t\tElement resultQuery = new Element(\"query\", new String[]{Packet.XMLNS_ATT}, new String[]{DISCO_ITEMS_XMLNS});\n\t\t\tPacket result = packet.okResult(resultQuery, 0);\n\n\t\t\tfor (String n : tree.keySet()) {\n\t\t\t\titems.add(new Element(\"item\", new String[]{\"jid\", \"node\", \"name\"}, new String[]{jid.toString(), n, n}));\n\t\t\t}\n\n\t\t\t// for (String eventName : registrar.getRegisteredEvents()) {\n\t\t\t// String description = registrar.getDescription(eventName);\n\t\t\t// items.add(new Element(\"item\", new String[] { \"jid\", \"node\",\n\t\t\t// \"name\" }, new String[] { jid.toString(), eventName,\n\t\t\t// description == null || description.isEmpty() ? eventName :\n\t\t\t// description }));\n\t\t\t// }\n\n\t\t\tresultQuery.addChildren(items);\n\t\t\twrite(result);\n\n\t\t} else if (tree.containsKey(node)) {\n\t\t\tList<Element> items = new ArrayList<Element>();\n\t\t\tElement resultQuery = new Element(\"query\", new String[]{Packet.XMLNS_ATT}, new String[]{DISCO_ITEMS_XMLNS});\n\t\t\tPacket result = packet.okResult(resultQuery, 0);\n\n\t\t\tfor (String eventName : tree.get(node)) {\n\t\t\t\tString description = registrar.getDescription(eventName);\n\t\t\t\titems.add(new Element(\"item\", new String[]{\"jid\", \"node\", \"name\"},\n\t\t\t\t\t\t\t\t\t  new String[]{jid.toString(), eventName,\n\t\t\t\t\t\t\t\t\t\t\t\t   description == null || description.isEmpty()\n\t\t\t\t\t\t\t\t\t\t\t\t   ? eventName\n\t\t\t\t\t\t\t\t\t\t\t\t   : description}));\n\t\t\t}\n\t\t\tresultQuery.addChildren(items);\n\t\t\twrite(result);\n\t\t} else {\n\t\t\tElement resultQuery = new Element(\"query\", new String[]{Packet.XMLNS_ATT}, new String[]{DISCO_ITEMS_XMLNS});\n\t\t\twrite(packet.okResult(resultQuery, 0));\n\t\t}\n\t}\n\n\tprivate Map<String, Set<String>> prepareEventsTree() {\n\t\tMap<String, Set<String>> result = new HashMap<>();\n\n\t\tfor (String e : registrar.getRegisteredEvents()) {\n\t\t\tEventName name = new EventName(e);\n\n\t\t\tSet<String> nodes;\n\n\t\t\tfinal String pck = name.getPackage() + \".*\";\n\t\t\tif (result.containsKey(pck)) {\n\t\t\t\tnodes = result.get(pck);\n\t\t\t} else {\n\t\t\t\tnodes = new HashSet<>();\n\t\t\t\tresult.put(pck, nodes);\n\t\t\t}\n\n\t\t\tnodes.add(e);\n\t\t}\n\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/component/SubscribeModule.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.component;\n\nimport tigase.component.exceptions.ComponentException;\nimport tigase.component.responses.AsyncCallback;\nimport tigase.criteria.Criteria;\nimport tigase.eventbus.EventListener;\nimport tigase.eventbus.component.stores.Affiliation;\nimport tigase.eventbus.component.stores.AffiliationStore;\nimport tigase.eventbus.component.stores.Subscription;\nimport tigase.eventbus.component.stores.SubscriptionStore;\nimport tigase.eventbus.impl.EventBusImplementation;\nimport tigase.eventbus.impl.EventName;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.server.Packet;\nimport tigase.server.Permissions;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.logging.Level;\n\n@Bean(name = SubscribeModule.ID, active = true)\npublic class SubscribeModule\n\t\textends AbstractEventBusModule\n\t\timplements Initializable, UnregisterAware {\n\n\tpublic final static String ID = \"subscribe\";\n\tprivate static final Criteria CRIT = new ElemPathCriteria(new String[]{\"iq\", \"pubsub\", \"subscribe\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{null, \"http://jabber.org/protocol/pubsub\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   null});\n\t@Inject\n\tprivate AffiliationStore affiliationStore;\n\n\t@Inject\n\tprivate EventBusComponent component;\n\n\tprivate final EventListener<EventBusImplementation.ListenerAddedEvent> eventBusHandlerAddedHandler = new EventListener<EventBusImplementation.ListenerAddedEvent>() {\n\t\t@Override\n\t\tpublic void onEvent(final EventBusImplementation.ListenerAddedEvent event) {\n\t\t\tif (!event.getPackageName().startsWith(\"tigase.eventbus\")) {\n\t\t\t\tSubscribeModule.this.onAddHandler(event.getEventName(), event.getPackageName());\n\t\t\t}\n\t\t}\n\t};\n\n\t@Inject(nullAllowed = false, bean = \"localEventBus\")\n\tprivate EventBusImplementation localEventBus;\n\n\t@Inject\n\tprivate SubscriptionStore subscriptionStore;\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\tlocalEventBus.removeListener(eventBusHandlerAddedHandler);\n\t}\n\n\tpublic void clusterNodeConnected(JID node) {\n\t\tif (component.getComponentId().equals(node)) {\n\t\t\treturn;\n\t\t}\n\t\t// context.getSubscriptionStore().addSubscription(null,\n\t\t// \"tigase:eventbus\", JID.jidInstanceNS(\"eventbus\", node, null));\n\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.finer(\"Node \" + node + \" is connected. Preparing subscribe request.\");\n\t\t}\n\n\t\tSet<Element> pubsubNodes = new HashSet<>();\n\t\tfor (EventName eventName : localEventBus.getAllListenedEvents()) {\n\t\t\tpubsubNodes.add(prepareSubscribeElement(eventName, component.getComponentId(), null));\n\t\t}\n\n\t\tfor (EventName eventName : subscriptionStore.getSubscribedEvents()) {\n\t\t\tCollection<Subscription> subscriptions = subscriptionStore.getSubscribersJIDs(eventName.getPackage(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  eventName.getName());\n\t\t\tfor (Subscription subscription : subscriptions) {\n\t\t\t\tif (subscription.getServiceJID() != null) {\n\t\t\t\t\tpubsubNodes.add(prepareSubscribeElement(eventName, subscription.getJid(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsubscription.getServiceJID().toString()));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!pubsubNodes.isEmpty()) {\n\t\t\tsendSubscribeRequest(\"eventbus@\" + node.getDomain(), pubsubNodes);\n\t\t}\n\t}\n\n\tpublic void clusterNodeDisconnected(JID node) {\n\t\tif (component.getComponentId().equals(node)) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.finer(\"Node \" + node + \" is disconnected.\");\n\t\t}\n\t\tsubscriptionStore.remove(new Subscription(JID.jidInstanceNS(\"eventbus\", node.getDomain(), null)));\n\t}\n\n\t@Override\n\tpublic String[] getFeatures() {\n\t\treturn new String[]{\"http://jabber.org/protocol/pubsub#subscribe\"};\n\t}\n\n\t@Override\n\tpublic Criteria getModuleCriteria() {\n\t\treturn CRIT;\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tlocalEventBus.addListener(EventBusImplementation.ListenerAddedEvent.class, eventBusHandlerAddedHandler);\n\t}\n\n\tprotected void onAddHandler(String eventName, String eventPackage) {\n\t\tfor (JID node : component.getNodesConnected()) {\n\t\t\tif (component.getComponentId().equals(node)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tElement se = prepareSubscribeElement(new EventName(eventPackage, eventName), component.getComponentId(),\n\t\t\t\t\t\t\t\t\t\t\t\t null);\n\t\t\tsendSubscribeRequest(\"eventbus@\" + node.getDomain(), Collections.singleton(se));\n\t\t}\n\t}\n\n\tprivate Element prepareSubscribeElement(EventName event, JID jid, String service) {\n\t\tElement subscribeElem = new Element(\"subscribe\");\n\t\tsubscribeElem.addAttribute(\"node\", event.toString());\n\t\tsubscribeElem.addAttribute(\"jid\", jid.toString());\n\n\t\tif (service != null) {\n\t\t\tsubscribeElem.addChild(new Element(\"service\", service));\n\t\t}\n\n\t\treturn subscribeElem;\n\t}\n\n\t@Override\n\tpublic void process(Packet packet) throws ComponentException, TigaseStringprepException {\n\t\tif (packet.getType() == StanzaType.set) {\n\t\t\tprocessSet(packet);\n\t\t} else {\n\t\t\tthrow new ComponentException(Authorization.NOT_ALLOWED, \"Only type set is allowed.\");\n\t\t}\n\t}\n\n\tprotected Element processClusterSubscription(final Packet packet) throws TigaseStringprepException {\n\t\t// subscription from cluster node\n\t\tlog.finest(\"Processing cluster subscription request from \" + packet.getStanzaFrom());\n\t\tList<Element> subscribeElements = packet.getElemChildrenStaticStr(new String[]{\"iq\", \"pubsub\"});\n\n\t\tfor (Element subscribe : subscribeElements) {\n\t\t\tElement serviceItem = subscribe.getChild(\"service\");\n\n\t\t\tfinal EventName parsedName = new EventName(subscribe.getAttributeStaticStr(\"node\"));\n\t\t\tfinal JID jid = JID.jidInstance(subscribe.getAttributeStaticStr(\"jid\"));\n\t\t\tfinal String service;\n\n\t\t\tif (serviceItem != null && serviceItem.getCData() != null) {\n\t\t\t\tservice = serviceItem.getCData();\n\t\t\t} else {\n\t\t\t\tservice = null;\n\t\t\t}\n\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.fine(\"Node \" + jid + \" subscribed for events \" + parsedName);\n\t\t\t}\n\n\t\t\tSubscription subscription = new Subscription(jid);\n\t\t\tsubscription.setInClusterSubscription(true);\n\t\t\tsubscription.setServiceJID(JID.jidInstanceNS(service));\n\n\t\t\tsubscriptionStore.addSubscription(parsedName.getPackage(), parsedName.getName(), subscription);\n\n\t\t\tlocalEventBus.fire(new NewRemoteSubscriptionEvent(parsedName, subscription));\n\t\t}\n\t\treturn null;\n\t}\n\n\tprotected Element processNonClusterSubscription(final Packet packet)\n\t\t\tthrows TigaseStringprepException, ComponentException {\n\t\t// subscription from something out of cluster\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Processing noncluster subscription request from \" + packet.getStanzaFrom());\n\t\t}\n\t\tfinal Affiliation affiliation = affiliationStore.getAffiliation(packet.getStanzaFrom());\n\n\t\tif (!affiliation.isSubscribe()) {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.fine(\"Subscription rejected. Subscriber \" + packet.getStanzaFrom() + \" has bad affiliation: \" +\n\t\t\t\t\t\t\t\t affiliation);\n\t\t\t}\n\t\t\tthrow new ComponentException(Authorization.FORBIDDEN, \"Bad affiliation: \" + affiliation);\n\t\t}\n\n\t\tList<Element> subscribeElements = packet.getElemChildrenStaticStr(new String[]{\"iq\", \"pubsub\"});\n\t\tElement response = new Element(\"pubsub\", new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t   new String[]{\"http://jabber.org/protocol/pubsub\"});\n\n\t\tfinal Set<Element> subscribedNodes = new HashSet<>();\n\t\tfor (Element subscribe : subscribeElements) {\n\t\t\tEventName parsedName = new EventName(subscribe.getAttributeStaticStr(\"node\"));\n\t\t\tJID jid = JID.jidInstance(subscribe.getAttributeStaticStr(\"jid\"));\n\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.fine(\"Entity \" + jid + \" subscribed for events \" + parsedName);\n\t\t\t}\n\n\t\t\tSubscription subscription = new Subscription(jid, packet.getStanzaTo());\n\t\t\tsubscription.setInClusterSubscription(false);\n\n\t\t\tsubscriptionStore.addSubscription(parsedName.getPackage(), parsedName.getName(), subscription);\n\n\t\t\tsubscribedNodes.add(prepareSubscribeElement(parsedName, jid, packet.getStanzaTo().toString()));\n\n\t\t\tresponse.addChild(new Element(\"subscription\", new String[]{\"node\", \"jid\", \"subscription\"},\n\t\t\t\t\t\t\t\t\t\t  new String[]{parsedName.toString(), jid.toString(), \"subscribed\"}));\n\t\t\tlocalEventBus.fire(new NewRemoteSubscriptionEvent(parsedName, subscription));\n\t\t}\n\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.finer(\"Forwarding subscription to: \" + component.getNodesConnected());\n\t\t}\n\n\t\tfor (JID node : component.getNodesConnected()) {\n\t\t\tif (component.getComponentId().equals(node)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tsendSubscribeRequest(\"eventbus@\" + node.getDomain(), subscribedNodes);\n\t\t}\n\n\t\treturn response;\n\t}\n\n\tprivate void processSet(final Packet packet) throws TigaseStringprepException, ComponentException {\n\t\tElement subscriptionResponse;\n\t\tif (isClusteredEventBus(packet.getStanzaFrom())) {\n\t\t\tsubscriptionResponse = processClusterSubscription(packet);\n\t\t} else {\n\t\t\tsubscriptionResponse = processNonClusterSubscription(packet);\n\t\t}\n\n\t\tPacket response = packet.okResult(subscriptionResponse, 0);\n\t\tresponse.setPermissions(Permissions.ADMIN);\n\t\twrite(response);\n\t}\n\n\tprotected void sendSubscribeRequest(final String to, Collection<Element> subscriptionElements) {\n\t\ttry {\n\t\t\tfinal String id = nextStanzaID();\n\t\t\tElement iq = new Element(\"iq\", new String[]{\"from\", \"to\", \"type\", \"id\"},\n\t\t\t\t\t\t\t\t\t new String[]{component.getComponentId().toString(), to, \"set\", id});\n\n\t\t\tElement pubsubElem = new Element(\"pubsub\", new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t new String[]{\"http://jabber.org/protocol/pubsub\"});\n\t\t\tiq.addChild(pubsubElem);\n\n\t\t\tsubscriptionElements.forEach(pubsubElem::addChild);\n\n\t\t\tfinal Packet packet = Packet.packetInstance(iq);\n\t\t\tpacket.setPermissions(Permissions.ADMIN);\n\t\t\tpacket.setXMLNS(Packet.CLIENT_XMLNS);\n\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.finer(\"Sending subscribe request (id=\" + id + \") to node \" + to);\n\t\t\t}\n\n\t\t\twrite(packet, new AsyncCallback() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void onError(Packet responseStanza, String errorCondition) {\n\t\t\t\t\t// TODO Auto-generated method stub\n\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\tlog.fine(\"Subscription request was cancelled by node \" + to + \" with error \" + errorCondition);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void onSuccess(Packet responseStanza) {\n\t\t\t\t\t// TODO Auto-generated method stub\n\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\tlog.fine(\"Subscription request was accepted by node \" + to + \".\");\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void onTimeout() {\n\t\t\t\t\t// TODO Auto-generated method stub\n\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\tlog.fine(\"Subscription request timeout. Node \" + to + \" not answered.\");\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Why? Oh Why?\", e);\n\t\t}\n\t}\n\n\tpublic static class NewRemoteSubscriptionEvent\n\t\t\timplements EventBusImplementation.InternalEventbusEvent {\n\n\t\tpublic EventName getParsedName() {\n\t\t\treturn parsedName;\n\t\t}\n\n\t\tprivate final EventName parsedName;\n\t\tprivate Subscription subscription;\n\n\t\tpublic NewRemoteSubscriptionEvent(EventName parsedName, Subscription subscription) {\n\t\t\tthis.parsedName = parsedName;\n\t\t\tthis.subscription = subscription;\n\t\t}\n\n\t\tpublic Subscription getSubscription() {\n\t\t\treturn subscription;\n\t\t}\n\n\t\tpublic void setSubscription(Subscription subscription) {\n\t\t\tthis.subscription = subscription;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/component/UnsubscribeModule.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.component;\n\nimport tigase.component.exceptions.ComponentException;\nimport tigase.component.responses.AsyncCallback;\nimport tigase.criteria.Criteria;\nimport tigase.eventbus.component.stores.Subscription;\nimport tigase.eventbus.component.stores.SubscriptionStore;\nimport tigase.eventbus.impl.EventBusImplementation;\nimport tigase.eventbus.impl.EventName;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.server.Packet;\nimport tigase.server.Permissions;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.logging.Level;\n\n@Bean(name = UnsubscribeModule.ID, active = true)\npublic class UnsubscribeModule\n\t\textends AbstractEventBusModule\n\t\timplements Initializable, UnregisterAware {\n\n\tpublic final static String ID = \"unsubscribe\";\n\tprivate static final Criteria CRIT = new ElemPathCriteria(new String[]{\"iq\", \"pubsub\", \"unsubscribe\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{null, \"http://jabber.org/protocol/pubsub\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   null});\n\t@Inject\n\tprivate EventBusComponent component;\n\t@Inject(nullAllowed = false, bean = \"localEventBus\")\n\tprivate EventBusImplementation localEventBus;\n\n\tprivate final tigase.eventbus.EventListener<EventBusImplementation.ListenerRemovedEvent> eventBusHandlerRemovedHandler = new tigase.eventbus.EventListener<EventBusImplementation.ListenerRemovedEvent>() {\n\t\t@Override\n\t\tpublic void onEvent(final EventBusImplementation.ListenerRemovedEvent event) {\n\t\t\tif (!event.getPackageName().startsWith(\"tigase.eventbus\")) {\n\t\t\t\tUnsubscribeModule.this.onRemoveHandler(event.getPackageName(), event.getEventName());\n\t\t\t}\n\t\t}\n\t};\n\n\t@Inject\n\tprivate SubscriptionStore subscriptionStore;\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\tlocalEventBus.removeListener(eventBusHandlerRemovedHandler);\n\t}\n\n\t@Override\n\tpublic String[] getFeatures() {\n\t\treturn new String[]{\"http://jabber.org/protocol/pubsub#subscribe\"};\n\t}\n\n\t@Override\n\tpublic Criteria getModuleCriteria() {\n\t\treturn CRIT;\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tlocalEventBus.addListener(EventBusImplementation.ListenerRemovedEvent.class, eventBusHandlerRemovedHandler);\n\t}\n\n\t@Override\n\tpublic void process(Packet packet) throws ComponentException, TigaseStringprepException {\n\t\tif (packet.getType() == StanzaType.set) {\n\t\t\tprocessSet(packet);\n\t\t} else {\n\t\t\tthrow new ComponentException(Authorization.NOT_ALLOWED);\n\t\t}\n\t}\n\n\tprotected void onRemoveHandler(String eventPackage, String eventName) {\n\t\tboolean listenedByHandlers = localEventBus.isListened(eventPackage, eventName);\n\n\t\tif (!listenedByHandlers) {\n\t\t\tfor (JID node : component.getNodesConnected()) {\n\t\t\t\tElement se = prepareUnsubscribeElement(new EventName(eventPackage, eventName),\n\t\t\t\t\t\t\t\t\t\t\t\t\t   component.getComponentId(), null);\n\t\t\t\tsendUnsubscribeRequest(\"eventbus@\" + node.getDomain(), Collections.singleton(se));\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected void sendUnsubscribeRequest(String to, Collection<Element> subscriptionElement) {\n\t\ttry {\n\t\t\tElement iq = new Element(\"iq\", new String[]{\"from\", \"to\", \"type\", \"id\"},\n\t\t\t\t\t\t\t\t\t new String[]{component.getComponentId().toString(), to, \"set\", nextStanzaID()});\n\n\t\t\tElement pubsubElem = new Element(\"pubsub\", new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t new String[]{\"http://jabber.org/protocol/pubsub\"});\n\t\t\tiq.addChild(pubsubElem);\n\n\t\t\tfor (Element node : subscriptionElement) {\n\t\t\t\tpubsubElem.addChild(node);\n\t\t\t}\n\n\t\t\tfinal Packet packet = Packet.packetInstance(iq);\n\t\t\tpacket.setPermissions(Permissions.ADMIN);\n\t\t\tpacket.setXMLNS(Packet.CLIENT_XMLNS);\n\n\t\t\twrite(packet, new AsyncCallback() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void onError(Packet responseStanza, String errorCondition) {\n\t\t\t\t\t// TODO Auto-generated method stub\n\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void onSuccess(Packet responseStanza) {\n\t\t\t\t\t// TODO Auto-generated method stub\n\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void onTimeout() {\n\t\t\t\t\t// TODO Auto-generated method stub\n\n\t\t\t\t}\n\t\t\t});\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Error creating packet instance\", e);\n\t\t}\n\t}\n\n\tprivate Element prepareUnsubscribeElement(EventName event, JID jid, String service) {\n\t\tElement subscribeElem = new Element(\"unsubscribe\");\n\t\tsubscribeElem.addAttribute(\"node\", event.toString());\n\t\tsubscribeElem.addAttribute(\"jid\", jid.toString());\n\n\t\tif (service != null) {\n\t\t\tsubscribeElem.addChild(new Element(\"service\", service));\n\t\t}\n\n\t\treturn subscribeElem;\n\t}\n\n\tprivate void processSet(final Packet packet) throws TigaseStringprepException {\n\t\tList<Element> unsubscribeElements = packet.getElemChildrenStaticStr(new String[]{\"iq\", \"pubsub\"});\n\n\t\tif (isClusteredEventBus(packet.getStanzaFrom())) {\n\t\t\t// request from cluster node\n\t\t\tfor (Element unsubscribe : unsubscribeElements) {\n\t\t\t\tEventName parsedName = new EventName(unsubscribe.getAttributeStaticStr(\"node\"));\n\t\t\t\tJID jid = JID.jidInstance(unsubscribe.getAttributeStaticStr(\"jid\"));\n\n\t\t\t\tsubscriptionStore.removeSubscription(parsedName.getPackage(), parsedName.getName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t new Subscription(jid));\n\t\t\t}\n\t\t} else {\n\t\t\t// request from something out of cluster\n\t\t\tfinal Set<Element> subscribedNodes = new HashSet<>();\n\t\t\tfor (Element subscribe : unsubscribeElements) {\n\t\t\t\tEventName parsedName = new EventName(subscribe.getAttributeStaticStr(\"node\"));\n\t\t\t\tJID jid = JID.jidInstance(subscribe.getAttributeStaticStr(\"jid\"));\n\n\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\tlog.finer(\"Entity \" + jid + \" subscribed for events \" + parsedName);\n\t\t\t\t}\n\n\t\t\t\tsubscriptionStore.removeSubscription(parsedName.getPackage(), parsedName.getName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t new Subscription(jid, packet.getStanzaTo()));\n\n\t\t\t\tsubscribedNodes.add(prepareUnsubscribeElement(parsedName, jid, packet.getStanzaTo().toString()));\n\t\t\t}\n\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.finer(\"Forwarding unsubcribe to: \" + component.getNodesConnected());\n\t\t\t}\n\n\t\t\tfor (JID node : component.getNodesConnected()) {\n\t\t\t\tsendUnsubscribeRequest(\"eventbus@\" + node.getDomain(), subscribedNodes);\n\t\t\t}\n\t\t}\n\t\tPacket response = packet.okResult((Element) null, 0);\n\t\tresponse.setPermissions(Permissions.ADMIN);\n\t\twrite(response);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/component/stores/Affiliation.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.component.stores;\n\npublic enum Affiliation {\n\tmember(2, true, true, false, false, false, false, false),\n\tnone(1, false, false, false, false, false, false, false),\n\t/**\n\t * An entity that is disallowed from subscribing or publishing to a node.\n\t */\n\toutcast(0, false, false, false, false, false, false, false),\n\t/**\n\t * The manager of a node, of which there may be more than one; often but not necessarily the node creator.\n\t */\n\towner(4, true, true, true, true, true, true, true),\n\t/** An entity that is allowed to publish items to a node. */\n\tpublisher(3, true, true, true, true, false, false, false);\n\n\tprivate final boolean configureNode;\n\n\tprivate final boolean deleteItem;\n\n\tprivate final boolean deleteNode;\n\n\tprivate final boolean publishItem;\n\n\tprivate final boolean purgeNode;\n\n\tprivate final boolean retrieveItem;\n\n\tprivate final boolean subscribe;\n\n\tprivate final int weight;\n\n\tAffiliation(int weight, boolean subscribe, boolean retrieveItem, boolean publishItem, boolean deleteItem,\n\t\t\t\tboolean configureNode, boolean deleteNode, boolean purgeNode) {\n\t\tthis.subscribe = subscribe;\n\t\tthis.weight = weight;\n\t\tthis.retrieveItem = retrieveItem;\n\t\tthis.publishItem = publishItem;\n\t\tthis.deleteItem = deleteItem;\n\t\tthis.configureNode = configureNode;\n\t\tthis.deleteNode = deleteNode;\n\t\tthis.purgeNode = purgeNode;\n\t}\n\n\tpublic int getWeight() {\n\t\treturn weight;\n\t}\n\n\tpublic boolean isConfigureNode() {\n\t\treturn configureNode;\n\t}\n\n\tpublic boolean isDeleteItem() {\n\t\treturn deleteItem;\n\t}\n\n\tpublic boolean isDeleteNode() {\n\t\treturn deleteNode;\n\t}\n\n\tpublic boolean isPublishItem() {\n\t\treturn publishItem;\n\t}\n\n\tpublic boolean isPurgeNode() {\n\t\treturn purgeNode;\n\t}\n\n\tpublic boolean isRetrieveItem() {\n\t\treturn retrieveItem;\n\t}\n\n\tpublic boolean isSubscribe() {\n\t\treturn subscribe;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/component/stores/AffiliationStore.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.component.stores;\n\nimport tigase.eventbus.component.EventBusComponent;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n@Bean(name = \"affiliations\", parent = EventBusComponent.class, active = true)\npublic class AffiliationStore {\n\n\tprivate final Map<JID, Affiliation> affiliations = new ConcurrentHashMap<JID, Affiliation>();\n\n\t@ConfigField(desc = \"List of JIDs what can subscribe for events\")\n\tprivate JID[] allowedSubscribers;\n\n\tpublic Affiliation getAffiliation(final JID jid) {\n\t\tAffiliation a = affiliations.get(jid);\n\t\tif (a == null && allowedSubscribers != null) {\n\t\t\tfor (JID j : allowedSubscribers) {\n\t\t\t\tif (j.getResource() != null && j.equals(jid)) {\n\t\t\t\t\treturn Affiliation.member;\n\t\t\t\t} else if (j.getResource() == null && j.getBareJID().equals(jid.getBareJID())) {\n\t\t\t\t\treturn Affiliation.member;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn a == null ? Affiliation.none : a;\n\t}\n\n\tpublic JID[] getAllowedSubscribers() {\n\t\treturn allowedSubscribers;\n\t}\n\n\tpublic void setAllowedSubscribers(JID[] allowedSubscribers) {\n\t\tthis.allowedSubscribers = allowedSubscribers;\n\t}\n\n\tpublic void putAffiliation(JID jid, Affiliation affiliation) {\n\t\tthis.affiliations.put(jid, affiliation);\n\t}\n\n\tpublic void removeAffiliation(JID jid) {\n\t\tthis.affiliations.remove(jid);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/component/stores/Subscription.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.component.stores;\n\nimport tigase.xmpp.jid.JID;\n\npublic class Subscription {\n\n\tprivate final JID jid;\n\tprivate boolean inClusterSubscription;\n\tprivate JID serviceJID;\n\n\tpublic Subscription(JID jid) {\n\t\tsuper();\n\t\tthis.jid = jid;\n\t}\n\n\tpublic Subscription(JID jid, JID serviceJID) {\n\t\tsuper();\n\t\tthis.jid = jid;\n\t\tthis.serviceJID = serviceJID;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tSubscription other = (Subscription) obj;\n\t\tif (jid == null) {\n\t\t\tif (other.jid != null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else if (!jid.equals(other.jid)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic JID getJid() {\n\t\treturn jid;\n\t}\n\n\tpublic JID getServiceJID() {\n\t\treturn serviceJID;\n\t}\n\n\tpublic void setServiceJID(JID serviceJID) {\n\t\tthis.serviceJID = serviceJID;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tfinal int prime = 31;\n\t\tint result = 1;\n\t\tresult = prime * result + ((jid == null) ? 0 : jid.hashCode());\n\t\treturn result;\n\t}\n\n\tpublic boolean isInClusterSubscription() {\n\t\treturn inClusterSubscription;\n\t}\n\n\tpublic void setInClusterSubscription(boolean inClusterSubscription) {\n\t\tthis.inClusterSubscription = inClusterSubscription;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Subscription{\" + \"jid=\" + jid + \", serviceJID=\" + serviceJID + '}';\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/component/stores/SubscriptionStore.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.component.stores;\n\nimport tigase.eventbus.component.EventBusComponent;\nimport tigase.eventbus.impl.EventName;\nimport tigase.eventbus.impl.EventsNameMap;\nimport tigase.kernel.beans.Bean;\n\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.logging.Logger;\n\n@Bean(name = \"subscriptionStore\", parent = EventBusComponent.class, active = true)\npublic class SubscriptionStore {\n\n\tprotected final Logger log = Logger.getLogger(this.getClass().getName());\n\n\tprivate final EventsNameMap<Subscription> subscribers = new EventsNameMap<Subscription>();\n\n\tpublic void addSubscription(String eventPackage, String eventName, Subscription subscription) {\n\t\tsubscribers.put(eventPackage, eventName, subscription);\n\t}\n\n\tpublic Collection<Subscription> getAllData() {\n\t\treturn subscribers.getAllData();\n\t}\n\n\tpublic Set<EventName> getSubscribedEvents() {\n\t\treturn subscribers.getAllListenedEvents();\n\t}\n\n\tpublic Collection<Subscription> getSubscribersJIDs(String eventPackage, String eventName) {\n\t\tfinal HashSet<Subscription> handlers = new HashSet<Subscription>();\n\t\thandlers.addAll(subscribers.get(eventPackage, eventName));\n\t\thandlers.addAll(subscribers.get(eventPackage, null));\n\t\treturn handlers;\n\t}\n\n\tpublic boolean hasSubscriber(String eventPackage, String eventName) {\n\t\treturn subscribers.hasData(eventPackage, eventName);\n\t}\n\n\tpublic void remove(Subscription jid) {\n\t\tsubscribers.delete(jid);\n\t}\n\n\tpublic void removeSubscription(String eventPackage, String eventName, Subscription jidInstanceNS) {\n\t\tsubscribers.delete(eventPackage, eventName, jidInstanceNS);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/events/ShutdownEvent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.events;\n\nimport java.io.Serializable;\n\n/**\n * @author andrzej\n */\npublic class ShutdownEvent\n\t\timplements Serializable {\n\n\tprivate long delay = 0;\n\tprivate String msg;\n\tprivate String node;\n\n\tpublic ShutdownEvent() {\n\t}\n\n\tpublic ShutdownEvent(String node, long delay, String msg) {\n\t\tthis.node = node;\n\t\tthis.delay = delay;\n\t\tthis.msg = msg;\n\t}\n\n\tpublic long getDelay() {\n\t\treturn delay;\n\t}\n\n\tpublic String getMessage() {\n\t\treturn msg;\n\t}\n\n\tpublic String getNode() {\n\t\treturn node;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/events/StartupFinishedEvent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.events;\n\nimport tigase.eventbus.EventBusEvent;\n\nimport java.io.Serializable;\n\n/**\n * Created: Feb 19, 2009 12:17:03 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class StartupFinishedEvent\n\t\timplements Serializable, EventBusEvent {\n\n\tString node;\n\n\tpublic StartupFinishedEvent() {\n\t}\n\n\tpublic StartupFinishedEvent(String node) {\n\t\tthis.node = node;\n\t}\n\n\tpublic String getNode() {\n\t\treturn node;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/impl/AbstractHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\npublic abstract class AbstractHandler {\n\n\t/**\n\t * Defines what type of event is expected by Handler.\n\t */\n\tpublic enum Type {\n\t\t/**\n\t\t * Only non-XML events. XML events will be ignored.\n\t\t */\n\t\tobject,\n\t\t/**\n\t\t * Only XML events. Non-XML events will be converted to XML.\n\t\t */\n\t\telement,\n\t\t/**\n\t\t * As is. Without conversion.\n\t\t */\n\t\tasIs\n\t}\n\tprivate final String eventName;\n\tprivate final String packageName;\n\n\tpublic AbstractHandler(String packageName, String eventName) {\n\t\tthis.packageName = packageName;\n\t\tthis.eventName = eventName;\n\t}\n\n\tpublic abstract void dispatch(Object event, Object source, boolean remotelyGeneratedEvent);\n\n\tpublic String getEventName() {\n\t\treturn eventName;\n\t}\n\n\tpublic String getPackageName() {\n\t\treturn packageName;\n\t}\n\n\tpublic abstract Type getRequiredEventType();\n\n\tpublic boolean isSynchronous() {\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/impl/AbstractListenerHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\npublic abstract class AbstractListenerHandler<T>\n\t\textends AbstractHandler {\n\n\tprotected final T listener;\n\n\tprotected AbstractListenerHandler(final String packageName, final String eventName, T listener) {\n\t\tsuper(packageName, eventName);\n\t\tthis.listener = listener;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || !(o instanceof AbstractListenerHandler)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tAbstractListenerHandler that = (AbstractListenerHandler) o;\n\n\t\treturn listener.equals(that.listener);\n\n\t}\n\n\tpublic T getListener() {\n\t\treturn listener;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn listener.hashCode();\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/impl/ElementListenerHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.eventbus.EventListener;\nimport tigase.xml.Element;\n\n@Deprecated\n@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"Class based events should be used\")\nclass ElementListenerHandler\n\t\textends AbstractListenerHandler<EventListener<Element>> {\n\n\tpublic ElementListenerHandler(final String packageName, final String eventName, EventListener<Element> listener) {\n\t\tsuper(packageName, eventName, listener);\n\t}\n\n\t@Override\n\tpublic void dispatch(Object event, Object source, boolean remotelyGeneratedEvent) {\n\t\tlistener.onEvent((Element) event);\n\t}\n\n\t@Override\n\tpublic Type getRequiredEventType() {\n\t\treturn Type.element;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/impl/ElementSourceListenerHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.eventbus.EventSourceListener;\nimport tigase.xml.Element;\n\n@Deprecated\n@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"Class based events should be used\")\nclass ElementSourceListenerHandler\n\t\textends AbstractListenerHandler<EventSourceListener<Element>> {\n\n\tpublic ElementSourceListenerHandler(final String packageName, final String eventName,\n\t\t\t\t\t\t\t\t\t\tEventSourceListener<Element> listener) {\n\t\tsuper(packageName, eventName, listener);\n\t}\n\n\t@Override\n\tpublic void dispatch(Object event, Object source, boolean remotelyGeneratedEvent) {\n\t\tlistener.onEvent((Element) event, source);\n\t}\n\n\t@Override\n\tpublic Type getRequiredEventType() {\n\t\treturn Type.element;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/impl/EventBusImplementation.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.eventbus.*;\nimport tigase.xml.Element;\n\nimport java.util.*;\nimport java.util.concurrent.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class EventBusImplementation\n\t\timplements EventBus {\n\n\tprivate static final Logger log = Logger.getLogger(EventBusImplementation.class.getName());\n\tprivate final EventsNameMap<AbstractHandler> listeners = new EventsNameMap<>();\n\tprivate final ReflectEventListenerHandlerFactory reflectEventListenerFactory = new ReflectEventListenerHandlerFactory();\n\tprivate final ReflectEventRoutedTransientFillerFactory reflectEventRoutedTransientFillerFactory = new ReflectEventRoutedTransientFillerFactory();\n\tprivate final ReflectEventRoutingSelectorFactory reflectEventRoutingSelectorFactory = new ReflectEventRoutingSelectorFactory();\n\tprivate final EventsRegistrar registrar = new EventsRegistrar();\n\tprivate final Map<Class<?>, Set<EventRoutedTransientFiller>> routedTransientFillers = new ConcurrentHashMap<>();\n\tprivate final Map<Class<?>, EventRoutingSelector> routingSelectors = new ConcurrentHashMap<>();\n\tprivate final Serializer serializer = new EventBusSerializer();\n\tprivate boolean acceptOnlyRegisteredEvents = false;\n\tprivate Executor executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 4);\n\tprivate ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);\n\n\tpublic EventBusImplementation() {\n\t\tthis.scheduler.scheduleAtFixedRate(new Runnable() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tfire(new TickMinuteEvent(System.currentTimeMillis()));\n\t\t\t}\n\t\t}, 1, 1, TimeUnit.MINUTES);\n\t}\n\n\tpublic void addHandler(AbstractHandler listenerHandler) {\n\t\tlisteners.put(listenerHandler.getPackageName(), listenerHandler.getEventName(), listenerHandler);\n\t}\n\n\tpublic <T> void addListener(Class<T> eventClass, tigase.eventbus.EventListener<T> listener) {\n\t\tfinal String packageName = eventClass.getPackage().getName();\n\t\tfinal String eventName = eventClass.getSimpleName();\n\n\t\tAbstractListenerHandler handler = new ObjectEventsListenerHandler(packageName, eventName, listener);\n\t\taddHandler(handler);\n\t\tfireListenerAddedEvent(packageName, eventName);\n\t}\n\n\tpublic <T> void addListener(Class<T> eventClass, EventSourceListener<T> listener) {\n\t\tfinal String packageName = eventClass.getPackage().getName();\n\t\tfinal String eventName = eventClass.getSimpleName();\n\n\t\tAbstractListenerHandler handler = new ObjectEventsSourceListenerHandler(packageName, eventName, listener);\n\n\t\taddHandler(handler);\n\t\tfireListenerAddedEvent(packageName, eventName);\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"Class based events should be used\")\n\tpublic void addListener(String packageName, String eventName, tigase.eventbus.EventListener<Element> listener) {\n\t\tAbstractListenerHandler handler = new ElementListenerHandler(packageName, eventName, listener);\n\t\taddHandler(handler);\n\t\tfireListenerAddedEvent(packageName, eventName);\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"Class based events should be used\")\n\tpublic void addListener(String packageName, String eventName, EventSourceListener<Element> listener) {\n\t\tAbstractListenerHandler handler = new ElementSourceListenerHandler(packageName, eventName, listener);\n\t\taddHandler(handler);\n\t\tfireListenerAddedEvent(packageName, eventName);\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"Class based events should be used\")\n\tpublic void fire(Object event) {\n\t\tfire(event, null, false);\n\t}\n\n\tpublic void fire(EventBusEvent event) {\n\t\tfire(event, null, false);\n\t}\n\n\tpublic void fire(Object event, Object source) {\n\t\tfire(event, source, false);\n\t}\n\n\tpublic void fire(Object event, Object source, boolean remotelyGeneratedEvent) {\n\t\ttry {\n\t\t\tHashSet<AbstractHandler> listeners;\n\t\t\tif (event instanceof Element) {\n\t\t\t\tString eventFullName = ((Element) event).getName();\n\t\t\t\tint i = eventFullName.lastIndexOf(\".\");\n\t\t\t\tfinal String packageName = i >= 0 ? eventFullName.substring(0, i) : \"\";\n\t\t\t\tfinal String eventName = eventFullName.substring(i + 1);\n\t\t\t\tcheckIfEventIsRegistered(eventFullName);\n\t\t\t\tlisteners = getListenersForEvent(packageName, eventName);\n\t\t\t} else {\n\t\t\t\tcheckIfEventIsRegistered(event.getClass().getName());\n\t\t\t\tlisteners = getListenersForEvent(event.getClass());\n\t\t\t}\n\n\t\t\tdoFireThreadPerHandler(event, source, remotelyGeneratedEvent, listeners);\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Problem on firing event\", e);\n\t\t}\n\t}\n\n\tpublic Collection<AbstractHandler> getAllHandlers() {\n\t\treturn listeners.getAllData();\n\t}\n\n\tpublic Set<EventName> getAllListenedEvents() {\n\t\treturn listeners.getAllListenedEvents();\n\t}\n\n\tpublic Collection<EventRoutedTransientFiller> getEventRoutedTransientFillers(Class<?> eventClass) {\n\t\tfinal HashSet<EventRoutedTransientFiller> result = new HashSet<>();\n\t\tClass<?> tmp = eventClass;\n\t\tdo {\n\t\t\tCollection<EventRoutedTransientFiller> fillers = routedTransientFillers.get(tmp);\n\t\t\tif (fillers != null) {\n\t\t\t\tresult.addAll(fillers);\n\t\t\t}\n\t\t\ttmp = tmp.getSuperclass();\n\t\t} while (!tmp.equals(Object.class));\n\n\t\treturn result;\n\t}\n\n\tpublic EventRoutingSelector getEventRoutingSelector(Class<?> eventClass) {\n\t\tClass<?> tmp = eventClass;\n\t\tEventRoutingSelector handler = null;\n\t\tdo {\n\t\t\thandler = routingSelectors.get(tmp);\n\t\t\tif (handler != null) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\ttmp = tmp.getSuperclass();\n\t\t} while (!tmp.equals(Object.class));\n\n\t\treturn handler;\n\t}\n\n\tpublic Executor getExecutor() {\n\t\treturn executor;\n\t}\n\n\tpublic void setExecutor(Executor executor) {\n\t\tthis.executor = executor;\n\t}\n\n\tpublic EventsRegistrar getRegistrar() {\n\t\treturn registrar;\n\t}\n\n\tpublic Serializer getSerializer() {\n\t\treturn serializer;\n\t}\n\n\tpublic boolean isAcceptOnlyRegisteredEvents() {\n\t\treturn acceptOnlyRegisteredEvents;\n\t}\n\n\tpublic void setAcceptOnlyRegisteredEvents(boolean acceptOnlyRegisteredEvents) {\n\t\tthis.acceptOnlyRegisteredEvents = acceptOnlyRegisteredEvents;\n\t}\n\n\tpublic boolean isListened(String eventPackage, String eventName) {\n\t\treturn listeners.hasData(eventPackage, eventName);\n\t}\n\n\tpublic void registerAll(Object consumer) {\n\t\tCollection<AbstractHandler> listeners = this.reflectEventListenerFactory.create(consumer);\n\t\tfor (AbstractHandler l : listeners) {\n\t\t\taddHandler(l);\n\t\t}\n\t\tCollection<EventRoutedTransientFiller> fillers = this.reflectEventRoutedTransientFillerFactory.create(consumer);\n\t\tfor (EventRoutedTransientFiller f : fillers) {\n\t\t\tSet<EventRoutedTransientFiller> eventFillers = routedTransientFillers.computeIfAbsent(f.getEventClass(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  (Class<?> c) -> {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  return new CopyOnWriteArraySet<>();\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  });\n\t\t\teventFillers.add(f);\n\t\t}\n\t\tCollection<EventRoutingSelector> selectors = this.reflectEventRoutingSelectorFactory.create(consumer);\n\t\tfor (EventRoutingSelector s : selectors) {\n\t\t\troutingSelectors.put(s.getEventClass(), s);\n\t\t}\n\t}\n\n\tpublic void registerEvent(String event, String description, boolean privateEvent) {\n\t\tregistrar.register(event, description, privateEvent);\n\t}\n\n\tpublic void registerEvent(Class<?> event, String description, boolean privateEvent) {\n\t\tregistrar.register(event.getName(), description, privateEvent);\n\t}\n\n\tpublic void removeHandler(AbstractHandler listenerHandler) {\n\t\tlisteners.delete(listenerHandler);\n\t}\n\n\tpublic <T> void removeListener(EventSourceListener<T> listener) {\n\t\tAbstractListenerHandler handler = new ObjectEventsSourceListenerHandler(null, null, listener);\n\t\tremoveHandler(handler);\n\t}\n\n\tpublic <T> void removeListener(tigase.eventbus.EventListener<T> listener) {\n\t\tif (listener == null) {\n\t\t\treturn;\n\t\t}\n\t\tAbstractListenerHandler handler = new ObjectEventsListenerHandler(null, null, listener);\n\t\tremoveHandler(handler);\n\t}\n\n\tpublic void unregisterAll(Object consumer) {\n\t\tCollection<AbstractHandler> listeners = this.reflectEventListenerFactory.create(consumer);\n\t\tfor (AbstractHandler l : listeners) {\n\t\t\tremoveHandler(l);\n\t\t}\n\t\tCollection<EventRoutingSelector> selectors = this.reflectEventRoutingSelectorFactory.create(consumer);\n\t\tfor (EventRoutingSelector s : selectors) {\n\t\t\troutingSelectors.remove(s.getEventClass(), s);\n\t\t}\n\t\tCollection<EventRoutedTransientFiller> fillers = this.reflectEventRoutedTransientFillerFactory.create(consumer);\n\t\tfor (EventRoutedTransientFiller f : fillers) {\n\t\t\tSet<EventRoutedTransientFiller> eventFillers = routedTransientFillers.get(f.getEventClass());\n\t\t\tif (eventFillers != null) {\n\t\t\t\teventFillers.remove(f);\n\t\t\t}\n\t\t}\n\t}\n\n\tList<tigase.eventbus.EventListener> getEventListeners(final String packageName, final String eventName) {\n\t\tArrayList<tigase.eventbus.EventListener> result = new ArrayList<>();\n\t\tCollection ls = listeners.get(packageName, eventName);\n\t\tresult.addAll(ls);\n\n\t\tls = listeners.get(null, eventName);\n\t\tresult.addAll(ls);\n\n\t\treturn result;\n\t}\n\n\tHashSet<AbstractHandler> getListenersForEvent(final Class<?> eventClass) {\n\n\t\tfinal HashSet<AbstractHandler> result = new HashSet<>();\n\n\t\t// interface-based listeners\n\t\tfor (Class<?> cls : eventClass.getInterfaces()) {\n\t\t\tfillListenersForEvent(result, cls);\n\t\t}\n\n\t\t// class-based listeners\n\t\tClass<?> cls = eventClass;\n\t\twhile (!cls.equals(Object.class)) {\n\t\t\tfillListenersForEvent(result, cls);\n\t\t\tcls = cls.getSuperclass();\n\t\t}\n\t\tresult.addAll(listeners.get(null, null));\n\n\t\treturn result;\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"Class based events should be used\")\n\tHashSet<AbstractHandler> getListenersForEvent(final String packageName, final String eventName) {\n\t\tfinal HashSet<AbstractHandler> result = new HashSet<>();\n\t\tresult.addAll(listeners.get(packageName, eventName));\n\t\tresult.addAll(listeners.get(packageName, null));\n\t\tresult.addAll(listeners.get(null, null));\n\n\t\treturn result;\n\t}\n\n\tprotected void doFireThreadPerHandler(final Object event, final Object source, boolean remotelyGeneratedEvent,\n\t\t\t\t\t\t\t\t\t\t  HashSet<AbstractHandler> handlers) {\n\t\tElement eventConverted = null;\n\t\tfor (AbstractHandler listenerHandler : handlers) {\n\t\t\tObject eventObject;\n\n\t\t\tif (listenerHandler.getRequiredEventType() == AbstractListenerHandler.Type.asIs) {\n\t\t\t\teventObject = event;\n\t\t\t} else if (listenerHandler.getRequiredEventType() == AbstractListenerHandler.Type.element &&\n\t\t\t\t\t!(event instanceof Element)) {\n\t\t\t\tif (eventConverted == null) {\n\t\t\t\t\teventConverted = serializer.serialize(event);\n\t\t\t\t}\n\t\t\t\teventObject = eventConverted;\n\t\t\t} else if (listenerHandler.getRequiredEventType() != AbstractListenerHandler.Type.element &&\n\t\t\t\t\tevent instanceof Element) {\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\teventObject = event;\n\t\t\t}\n\n\t\t\tRunnable task = () -> {\n\t\t\t\ttry {\n\t\t\t\t\tlistenerHandler.dispatch(eventObject, source, remotelyGeneratedEvent);\n\t\t\t\t} catch (Throwable e) {\n\t\t\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\t\t\"Exception during execution of event: \" + event.getClass().getCanonicalName(), e);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tif (listenerHandler.isSynchronous()) {\n\t\t\t\ttask.run();\n\t\t\t} else {\n\t\t\t\texecutor.execute(task);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void checkIfEventIsRegistered(final String eventName) throws EventBusException {\n\t\tif (!registrar.isRegistered(eventName)) {\n\t\t\tif (this.acceptOnlyRegisteredEvents) {\n\t\t\t\tthrow new EventBusException(\"Event \" + eventName + \" is not registered.\");\n\t\t\t} else {\n\t\t\t\tlog.log(Level.FINEST, \"Event \" + eventName + \" in not registered.\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void fillListenersForEvent(HashSet<AbstractHandler> result, Class<?> cls) {\n\t\tfinal String packageName = cls.getPackage().getName();\n\t\tfinal String eventName = cls.getSimpleName();\n\n\t\tresult.addAll(listeners.get(packageName, eventName));\n\t\tresult.addAll(listeners.get(packageName, null));\n\t}\n\n\tprivate void fireListenerAddedEvent(String packageName, String eventName) {\n\t\tListenerAddedEvent event = new ListenerAddedEvent();\n\t\tevent.setEventName(eventName);\n\t\tevent.setPackageName(packageName);\n\n\t\tfire(event);\n\t}\n\n\tpublic interface InternalEventbusEvent {\n\n\t}\n\n\tpublic static class ListenerAddedEvent\n\t\t\timplements InternalEventbusEvent {\n\n\t\tprivate String eventName;\n\t\tprivate String packageName;\n\n\t\tpublic String getEventName() {\n\t\t\treturn eventName;\n\t\t}\n\n\t\tpublic void setEventName(String eventName) {\n\t\t\tthis.eventName = eventName;\n\t\t}\n\n\t\tpublic String getPackageName() {\n\t\t\treturn packageName;\n\t\t}\n\n\t\tpublic void setPackageName(String packageName) {\n\t\t\tthis.packageName = packageName;\n\t\t}\n\t}\n\n\tpublic static class ListenerRemovedEvent\n\t\t\timplements InternalEventbusEvent {\n\n\t\tprivate String eventName;\n\t\tprivate String packageName;\n\n\t\tpublic String getEventName() {\n\t\t\treturn eventName;\n\t\t}\n\n\t\tpublic void setEventName(String eventName) {\n\t\t\tthis.eventName = eventName;\n\t\t}\n\n\t\tpublic String getPackageName() {\n\t\t\treturn packageName;\n\t\t}\n\n\t\tpublic void setPackageName(String packageName) {\n\t\t\tthis.packageName = packageName;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/impl/EventBusSerializer.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\nimport tigase.kernel.BeanUtils;\nimport tigase.kernel.DefaultTypesConverter;\nimport tigase.kernel.TypesConverter;\nimport tigase.xml.Element;\nimport tigase.xml.XMLUtils;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Modifier;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class EventBusSerializer\n\t\timplements Serializer {\n\n\tprivate static final Logger log = Logger.getLogger(EventBusSerializer.class.getName());\n\tprivate static final String ELEM_NAME = \"event\";\n\tprivate static final String CLASS_ATTR_NAME = \"class\";\n\tprivate TypesConverter typesConverter = new DefaultTypesConverter();\n\n\tpublic <T> T deserialize(final Element element) {\n\t\ttry {\n\t\t\tif (!ELEM_NAME.equals(element.getName())) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tString className = element.getAttributeStaticStr(CLASS_ATTR_NAME);\n\t\t\tif (className == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tfinal Class<?> cls = Class.forName(className);\n\t\t\tfinal Object result = cls.newInstance();\n\n\t\t\tField[] fields = BeanUtils.getAllFields(cls);\n\t\t\tfor (final Field f : fields) {\n\t\t\t\tif (Modifier.isTransient(f.getModifiers())) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (Modifier.isFinal(f.getModifiers())) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (Modifier.isStatic(f.getModifiers())) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tObject value;\n\t\t\t\t\tElement v = element.getChild(f.getName());\n\t\t\t\t\tif (v == null) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (Element.class.isAssignableFrom(f.getType())) {\n\t\t\t\t\t\tif (v.getChildren().size() > 0) {\n\t\t\t\t\t\t\tvalue = v.getChildren().get(0);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tvalue = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvalue = typesConverter.convert(XMLUtils.unescape(v.getCData()), f.getType(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t   f.getGenericType());\n\t\t\t\t\t}\n\t\t\t\t\tBeanUtils.setValue(result, f, value);\n\t\t\t\t} catch (IllegalAccessException | InvocationTargetException caught) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Error while deserializing\", caught);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn (T) result;\n\t\t} catch (ClassNotFoundException e) {\n\t\t\treturn null;\n\t\t} catch (InstantiationException | IllegalAccessException e) {\n\t\t\tlog.log(Level.WARNING, \"Error while deserializing\", e);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic Element serialize(final Object object) {\n\t\tfinal Class<?> cls = object.getClass();\n\t\tElement e = new Element(ELEM_NAME);\n\t\te.setAttribute(CLASS_ATTR_NAME, cls.getName());\n\n\t\tField[] fields = BeanUtils.getAllFields(cls);\n\t\tfor (final Field f : fields) {\n\t\t\tif (Modifier.isTransient(f.getModifiers())) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (Modifier.isFinal(f.getModifiers())) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (Modifier.isStatic(f.getModifiers())) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tfinal Object value = BeanUtils.getValue(object, f);\n\n\t\t\t\tif (value == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tElement v = new Element(f.getName());\n\t\t\t\tif (Element.class.isAssignableFrom(f.getType())) {\n\t\t\t\t\tv.addChild((Element) value);\n\t\t\t\t} else {\n\t\t\t\t\tString x = typesConverter.toString(value);\n\t\t\t\t\tv.setCData(XMLUtils.escape(x));\n\t\t\t\t}\n\t\t\t\te.addChild(v);\n\t\t\t} catch (IllegalAccessException | InvocationTargetException caught) {\n\t\t\t\tlog.log(Level.WARNING, \"Error while serializing\", e);\n\t\t\t}\n\t\t}\n\n\t\treturn e;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/impl/EventName.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\npublic class EventName {\n\n\tprivate final String eventName;\n\n\tprivate final String eventPackage;\n\n\tpublic final static String toString(final String eventPackage, final String eventName) {\n\t\tString result = \"\";\n\t\tif (eventPackage == null) {\n\t\t\tresult += \"*\";\n\t\t} else {\n\t\t\tresult += eventPackage;\n\t\t}\n\n\t\tif (!result.isEmpty()) {\n\t\t\tresult += \".\";\n\t\t}\n\n\t\tif (eventName == null) {\n\t\t\tresult += \"*\";\n\t\t} else {\n\t\t\tresult += eventName;\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic EventName(final Class<?> eventClass) {\n\t\tthis(eventClass.getPackage().getName(), eventClass.getSimpleName());\n\t}\n\n\tpublic EventName(String eventName) {\n\t\tint i = eventName.lastIndexOf(\".\");\n\t\tString tmp = i >= 0 ? eventName.substring(0, i) : \"\";\n\t\tthis.eventPackage = tmp.equals(\"*\") ? null : tmp;\n\n\t\ttmp = eventName.substring(i + 1);\n\t\tthis.eventName = tmp.equals(\"*\") ? null : tmp;\n\t}\n\n\tpublic EventName(String eventPackage, String eventName) {\n\t\tsuper();\n\t\tthis.eventName = eventName;\n\t\tthis.eventPackage = eventPackage;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tEventName other = (EventName) obj;\n\t\tif (eventName == null) {\n\t\t\tif (other.eventName != null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else if (!eventName.equals(other.eventName)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (eventPackage == null) {\n\t\t\treturn other.eventPackage == null;\n\t\t} else return eventPackage.equals(other.eventPackage);\n\t}\n\n\tpublic String getName() {\n\t\treturn eventName;\n\t}\n\n\tpublic String getPackage() {\n\t\treturn eventPackage;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tfinal int prime = 31;\n\t\tint result = 1;\n\t\tresult = prime * result + ((eventName == null) ? 0 : eventName.hashCode());\n\t\tresult = prime * result + ((eventPackage == null) ? 0 : eventPackage.hashCode());\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn toString(eventPackage, eventName);\n\t}\n\n}"
  },
  {
    "path": "src/main/java/tigase/eventbus/impl/EventsNameMap.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\nimport java.util.*;\nimport java.util.Map.Entry;\nimport java.util.concurrent.ConcurrentHashMap;\n\npublic class EventsNameMap<M> {\n\n\tprivate final static String NULL_NAME = new String(new byte[]{0});\n\n\tprivate final Map<String, Map<String, Collection<M>>> dataMap = createMainDataMap();\n\n\tpublic void delete(M data) {\n\t\tIterator<Entry<String, Map<String, Collection<M>>>> namesIt = dataMap.entrySet().iterator();\n\t\twhile (namesIt.hasNext()) {\n\t\t\tMap<String, Collection<M>> datas = namesIt.next().getValue();\n\t\t\tIterator<Entry<String, Collection<M>>> dataIt = datas.entrySet().iterator();\n\t\t\twhile (dataIt.hasNext()) {\n\t\t\t\tCollection<M> d = dataIt.next().getValue();\n\t\t\t\td.remove(data);\n\n\t\t\t\tif (d.isEmpty()) {\n\t\t\t\t\tdataIt.remove();\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t}\n\n\tpublic void delete(String pckg, String name, M data) {\n\t\tfinal String eventName = name == null ? NULL_NAME : name;\n\t\tfinal String eventPackage = pckg == null ? NULL_NAME : pckg;\n\n\t\tMap<String, Collection<M>> namesData = dataMap.get(eventPackage);\n\t\tif (namesData == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tCollection<M> dataCollection = namesData.get(eventName);\n\t\tif (dataCollection == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tdataCollection.remove(data);\n\n\t\tif (dataCollection.isEmpty()) {\n\t\t\tnamesData.remove(eventName);\n\t\t}\n\n\t\tif (namesData.isEmpty()) {\n\t\t\tdataMap.remove(eventPackage);\n\t\t}\n\t}\n\n\tpublic Collection<M> get(String pckg, String name) {\n\t\tfinal String eventName = name == null ? NULL_NAME : name;\n\t\tfinal String eventPackage = pckg == null ? NULL_NAME : pckg;\n\n\t\tMap<String, Collection<M>> namesData = dataMap.get(eventPackage);\n\t\tif (namesData == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\n\t\tCollection<M> dataList = namesData.get(eventName);\n\t\tif (dataList == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\n\t\treturn dataList;\n\t}\n\n\tpublic Collection<M> getAllData() {\n\t\tHashSet<M> result = new HashSet<M>();\n\t\tfor (Map<String, Collection<M>> c1 : dataMap.values()) {\n\t\t\tfor (Collection<M> c2 : c1.values()) {\n\t\t\t\tresult.addAll(c2);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic Set<EventName> getAllListenedEvents() {\n\t\tHashSet<EventName> result = new HashSet<EventName>();\n\t\tIterator<Entry<String, Map<String, Collection<M>>>> packagesIt = dataMap.entrySet().iterator();\n\n\t\twhile (packagesIt.hasNext()) {\n\t\t\tEntry<String, Map<String, Collection<M>>> e = packagesIt.next();\n\t\t\tfinal String eventPackage = e.getKey();\n\n\t\t\tIterator<String> namesIt = e.getValue().keySet().iterator();\n\t\t\twhile (namesIt.hasNext()) {\n\t\t\t\tString n = namesIt.next();\n\t\t\t\tresult.add(new EventName(eventPackage == NULL_NAME ? null : eventPackage, n == NULL_NAME ? null : n));\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic boolean hasData(String pckg, String name) {\n\t\tfinal String eventName = name == null ? NULL_NAME : name;\n\t\tfinal String eventPackage = pckg == null ? NULL_NAME : pckg;\n\n\t\tMap<String, Collection<M>> namesData = dataMap.get(eventPackage);\n\t\tif (namesData == null || namesData.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tCollection<M> dataList = namesData.get(eventName);\n\t\treturn !(dataList == null || dataList.isEmpty());\n\t}\n\n\tpublic void put(String pckg, String name, M data) {\n\t\tfinal String eventName = name == null ? NULL_NAME : name;\n\t\tfinal String eventPackage = pckg == null ? NULL_NAME : pckg;\n\n\t\tMap<String, Collection<M>> namesData = dataMap.get(eventPackage);\n\t\tif (namesData == null) {\n\t\t\tnamesData = createNamesDataMap();\n\t\t\tdataMap.put(eventPackage, namesData);\n\t\t}\n\n\t\tCollection<M> dataList = namesData.get(eventName);\n\t\tif (dataList == null) {\n\t\t\tdataList = createDataList();\n\t\t\tnamesData.put(eventName, dataList);\n\t\t}\n\n\t\tdataList.add(data);\n\t}\n\n\tprotected Collection<M> createDataList() {\n\t\treturn new HashSet<M>();\n\t}\n\n\tprotected Map<String, Map<String, Collection<M>>> createMainDataMap() {\n\t\treturn new ConcurrentHashMap<String, Map<String, Collection<M>>>();\n\t}\n\n\tprotected Map<String, Collection<M>> createNamesDataMap() {\n\t\treturn new ConcurrentHashMap<String, Collection<M>>();\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/impl/EventsRegistrar.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.concurrent.ConcurrentHashMap;\n\npublic class EventsRegistrar {\n\n\tprivate final ConcurrentHashMap<String, EventInfo> events = new ConcurrentHashMap<>();\n\n\tpublic String getDescription(String eventName) {\n\t\tEventInfo info = events.get(eventName);\n\t\treturn info == null ? null : info.getDescription();\n\t}\n\n\tpublic Collection<String> getRegisteredEvents() {\n\t\tArrayList<String> result = new ArrayList<>();\n\t\tfor (EventInfo info : events.values()) {\n\t\t\tif (info.isPrivateEvent()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tresult.add(info.event);\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic boolean isRegistered(String eventName) {\n\t\treturn this.events.containsKey(eventName);\n\t}\n\n\tpublic boolean isRegistered(Class<?> eventClass) {\n\t\treturn this.events.containsKey(eventClass.getName());\n\t}\n\n\tpublic void register(String event, String description, boolean privateEvent) {\n\t\tEventInfo info = new EventInfo(event);\n\t\tinfo.setDescription(description);\n\t\tinfo.setPrivateEvent(privateEvent);\n\n\t\tthis.events.put(event, info);\n\t}\n\n\tprivate static class EventInfo {\n\n\t\tprivate final String event;\n\t\tprivate String description;\n\t\tprivate boolean privateEvent;\n\n\t\tpublic EventInfo(String event) {\n\t\t\tthis.event = event;\n\t\t}\n\n\t\tpublic String getDescription() {\n\t\t\treturn description;\n\t\t}\n\n\t\tpublic void setDescription(String description) {\n\t\t\tthis.description = description;\n\t\t}\n\n\t\tpublic boolean isPrivateEvent() {\n\t\t\treturn privateEvent;\n\t\t}\n\n\t\tpublic void setPrivateEvent(boolean privateEvent) {\n\t\t\tthis.privateEvent = privateEvent;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/impl/ObjectEventsListenerHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\nimport tigase.eventbus.EventListener;\n\npublic class ObjectEventsListenerHandler\n\t\textends AbstractListenerHandler<EventListener> {\n\n\tpublic ObjectEventsListenerHandler(final String packageName, final String eventName, EventListener listener) {\n\t\tsuper(packageName, eventName, listener);\n\t}\n\n\t@Override\n\tpublic void dispatch(Object event, Object source, boolean remotelyGeneratedEvent) {\n\t\tlistener.onEvent(event);\n\t}\n\n\t@Override\n\tpublic Type getRequiredEventType() {\n\t\treturn Type.object;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/impl/ObjectEventsSourceListenerHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\nimport tigase.eventbus.EventSourceListener;\n\npublic class ObjectEventsSourceListenerHandler\n\t\textends AbstractListenerHandler<EventSourceListener> {\n\n\tpublic ObjectEventsSourceListenerHandler(final String packageName, final String eventName,\n\t\t\t\t\t\t\t\t\t\t\t EventSourceListener listener) {\n\t\tsuper(packageName, eventName, listener);\n\t}\n\n\t@Override\n\tpublic void dispatch(Object event, Object source, boolean remotelyGeneratedEvent) {\n\t\tlistener.onEvent(event, source);\n\t}\n\n\t@Override\n\tpublic Type getRequiredEventType() {\n\t\treturn Type.object;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/impl/ReflectEventListenerHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\nimport tigase.eventbus.HandleEvent;\n\nimport java.lang.reflect.Method;\n\npublic class ReflectEventListenerHandler\n\t\textends AbstractHandler {\n\n\tprotected final Object consumerObject;\n\tprotected final HandleEvent.Type filter;\n\tprotected final Method handlerMethod;\n\tprotected final boolean synchronous;\n\n\tpublic ReflectEventListenerHandler(HandleEvent.Type filter, final String packageName, final String eventName,\n\t\t\t\t\t\t\t\t\t   Object consumerObject, Method handlerMethod) {\n\t\tthis(filter, false, packageName, eventName, consumerObject, handlerMethod);\n\t}\n\n\tpublic ReflectEventListenerHandler(HandleEvent.Type filter, boolean synchronous, final String packageName, final String eventName,\n\t\t\t\t\t\t\t\t\t   Object consumerObject, Method handlerMethod) {\n\t\tsuper(packageName, eventName);\n\t\tthis.synchronous = synchronous;\n\t\tthis.filter = filter;\n\t\tthis.consumerObject = consumerObject;\n\t\tthis.handlerMethod = handlerMethod;\n\t}\n\n\t@Override\n\tpublic void dispatch(final Object event, final Object source, boolean remotelyGeneratedEvent) {\n\t\tif (remotelyGeneratedEvent && filter == HandleEvent.Type.local ||\n\t\t\t\t!remotelyGeneratedEvent && filter == HandleEvent.Type.remote) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\thandlerMethod.invoke(consumerObject, event);\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tReflectEventListenerHandler that = (ReflectEventListenerHandler) o;\n\n\t\tif (!consumerObject.equals(that.consumerObject)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn handlerMethod.equals(that.handlerMethod);\n\n\t}\n\n\t@Override\n\tpublic Type getRequiredEventType() {\n\t\treturn Type.object;\n\t}\n\n\t@Override\n\tpublic boolean isSynchronous() {\n\t\treturn synchronous;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = consumerObject.hashCode();\n\t\tresult = 31 * result + handlerMethod.hashCode();\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/impl/ReflectEventListenerHandlerFactory.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\nimport tigase.eventbus.HandleEvent;\nimport tigase.eventbus.RegistrationException;\n\nimport java.lang.reflect.Method;\nimport java.util.Collection;\nimport java.util.logging.Logger;\n\nimport static tigase.util.reflection.ReflectionHelper.Handler;\nimport static tigase.util.reflection.ReflectionHelper.collectAnnotatedMethods;\n\npublic class ReflectEventListenerHandlerFactory {\n\n\tprivate static final Handler<HandleEvent, AbstractHandler> HANDLER = (Object consumer, Method method, HandleEvent annotation) -> {\n\t\tif (method.getParameterCount() < 1) {\n\t\t\tthrow new RegistrationException(\"Handler method must have parameter to receive event!\");\n\t\t}\n\n\t\tfinal Class eventType = method.getParameters()[0].getType();\n\t\tfinal String packageName = eventType.getPackage().getName();\n\t\tfinal String eventName = eventType.getSimpleName();\n\n\t\tif (annotation.sync() && annotation.filter() != HandleEvent.Type.local) {\n\t\t\tthrow new RegistrationException(\"Handler synchronous event listener may only listen local events!\");\n\t\t}\n\n\t\tReflectEventListenerHandler handler;\n\t\tswitch (method.getParameterCount()) {\n\t\t\tcase 1:\n\t\t\t\thandler = new ReflectEventListenerHandler(annotation.filter(), annotation.sync(), packageName, eventName, consumer,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  method);\n\t\t\t\tbreak;\n\t\t\tcase 2:\n\t\t\t\tfinal Class sourPar = method.getParameters()[1].getType();\n\t\t\t\tif (!sourPar.equals(Object.class)) {\n\t\t\t\t\tthrow new RegistrationException(\"Second parameter (event source) must be Object type.\");\n\t\t\t\t}\n\t\t\t\thandler = new ReflectEventSourceListenerHandler(annotation.filter(), annotation.sync(), packageName, eventName, consumer,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tmethod);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tthrow new RegistrationException(\"Handler method must have exactly one parameter!\");\n\t\t}\n\n\t\tmethod.setAccessible(true);\n\n\t\treturn handler;\n\t};\n\tprotected final Logger log = Logger.getLogger(this.getClass().getName());\n\n\tpublic Collection<AbstractHandler> create(final Object consumer) throws RegistrationException {\n\t\treturn collectAnnotatedMethods(consumer, HandleEvent.class, HANDLER);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/impl/ReflectEventRoutedTransientFiller.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\nimport tigase.eventbus.EventRoutedTransientFiller;\n\nimport java.lang.reflect.Method;\n\n/**\n * Class responsible for calling method on consumer instance which will fill event transient fields.\n *\n * @author andrzej\n */\npublic class ReflectEventRoutedTransientFiller\n\t\timplements EventRoutedTransientFiller {\n\n\tprivate final Object consumer;\n\tprivate final Class eventClass;\n\tprivate final Method method;\n\n\tpublic ReflectEventRoutedTransientFiller(Class eventClass, Object consumer, Method method) {\n\t\tthis.eventClass = eventClass;\n\t\tthis.consumer = consumer;\n\t\tthis.method = method;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (o == this) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || o.getClass() != getClass()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tReflectEventRoutedTransientFiller s = (ReflectEventRoutedTransientFiller) o;\n\t\tif (!consumer.equals(s.consumer)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn method.equals(s.method);\n\t}\n\n\t@Override\n\tpublic boolean fillEvent(Object event) {\n\t\ttry {\n\t\t\treturn (Boolean) method.invoke(consumer, event);\n\t\t} catch (Exception ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Class getEventClass() {\n\t\treturn eventClass;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = consumer.hashCode();\n\t\tresult = 31 * result + method.hashCode();\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/impl/ReflectEventRoutedTransientFillerFactory.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\nimport tigase.eventbus.EventRoutedTransientFiller;\nimport tigase.eventbus.FillRoutedEvent;\nimport tigase.eventbus.RegistrationException;\nimport tigase.util.reflection.ReflectionHelper.Handler;\n\nimport java.lang.reflect.Method;\nimport java.util.Collection;\n\nimport static tigase.util.reflection.ReflectionHelper.collectAnnotatedMethods;\n\n/**\n * Class responsible for creation of <code>ReflectEventRoutedTransientFiller</code> instances based on methods of\n * consumer class annotated with <code>FillRoutedEvent</code> annotation.\n *\n * @author andrzej\n */\npublic class ReflectEventRoutedTransientFillerFactory {\n\n\tprivate static final Handler<FillRoutedEvent, EventRoutedTransientFiller> HANDLER = (Object consumer, Method method, FillRoutedEvent annotation) -> {\n\t\tif (method.getParameterCount() < 1) {\n\t\t\tthrow new RegistrationException(\"Event routing selection method must have parameter to receive event!\");\n\t\t}\n\n\t\tfinal Class eventType = method.getParameters()[0].getType();\n\t\tmethod.setAccessible(true);\n\t\treturn new ReflectEventRoutedTransientFiller(eventType, consumer, method);\n\t};\n\n\tpublic Collection<EventRoutedTransientFiller> create(Object consumer) {\n\t\treturn collectAnnotatedMethods(consumer, FillRoutedEvent.class, HANDLER);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/impl/ReflectEventRoutingSelector.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\nimport tigase.eventbus.EventRoutingSelector;\nimport tigase.eventbus.component.stores.Subscription;\n\nimport java.lang.reflect.Method;\nimport java.util.Collection;\n\n/**\n * This class is implementation of <code>EventRoutingSelector</code> used when this selector is created based on\n * annotated method of consumer class.\n *\n * @author andrzej\n */\npublic class ReflectEventRoutingSelector\n\t\timplements EventRoutingSelector {\n\n\tprivate final Object consumer;\n\tprivate final Class eventClass;\n\tprivate final Method method;\n\n\tpublic ReflectEventRoutingSelector(Class eventClass, Object consumer, Method method) {\n\t\tthis.eventClass = eventClass;\n\t\tthis.consumer = consumer;\n\t\tthis.method = method;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (o == this) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || o.getClass() != getClass()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tReflectEventRoutingSelector s = (ReflectEventRoutingSelector) o;\n\t\tif (!consumer.equals(s.consumer)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn method.equals(s.method);\n\t}\n\n\t@Override\n\tpublic Class getEventClass() {\n\t\treturn eventClass;\n\t}\n\n\t@Override\n\tpublic Collection<Subscription> getSubscriptions(Object event, Collection<Subscription> subscriptions) {\n\t\ttry {\n\t\t\treturn (Collection<Subscription>) method.invoke(consumer, event, subscriptions);\n\t\t} catch (Exception ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = consumer.hashCode();\n\t\tresult = 31 * result + method.hashCode();\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/impl/ReflectEventRoutingSelectorFactory.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\nimport tigase.eventbus.EventRoutingSelector;\nimport tigase.eventbus.RegistrationException;\nimport tigase.eventbus.RouteEvent;\n\nimport java.lang.reflect.Method;\nimport java.util.Collection;\n\nimport static tigase.util.reflection.ReflectionHelper.Handler;\nimport static tigase.util.reflection.ReflectionHelper.collectAnnotatedMethods;\n\n/**\n * Class responsible for generation of <code>EventRoutingSelectors</code> based on methods of consumer class annotated\n * with <code>@RouteEvent</code>\n *\n * @author andrzej\n */\npublic class ReflectEventRoutingSelectorFactory {\n\n\tprivate static final Handler<RouteEvent, EventRoutingSelector> HANDLER = (Object consumer, Method method, RouteEvent annotation) -> {\n\t\tif (method.getParameterCount() < 1) {\n\t\t\tthrow new RegistrationException(\"Event routing selection method must have parameter to receive event!\");\n\t\t}\n\n\t\tfinal Class eventType = method.getParameters()[0].getType();\n\t\tmethod.setAccessible(true);\n\t\treturn new ReflectEventRoutingSelector(eventType, consumer, method);\n\t};\n\n\t/**\n\t * Method looks for methods of consumer class and returns list of <code>EventRoutingSelectors</code> created based\n\t * on methods annotated with <code>@RouteEvent</code>\n\t */\n\tpublic Collection<EventRoutingSelector> create(Object consumer) {\n\t\treturn collectAnnotatedMethods(consumer, RouteEvent.class, HANDLER);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/impl/ReflectEventSourceListenerHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\nimport tigase.eventbus.HandleEvent;\n\nimport java.lang.reflect.Method;\n\npublic class ReflectEventSourceListenerHandler\n\t\textends ReflectEventListenerHandler {\n\n\tpublic ReflectEventSourceListenerHandler(HandleEvent.Type filter, String packageName, String eventName,\n\t\t\t\t\t\t\t\t\t\t\t Object consumerObject, Method handlerMethod) {\n\t\tsuper(filter, packageName, eventName, consumerObject, handlerMethod);\n\t}\n\n\tpublic ReflectEventSourceListenerHandler(HandleEvent.Type filter, boolean synchronous, String packageName, String eventName,\n\t\t\t\t\t\t\t\t\t\t\t Object consumerObject, Method handlerMethod) {\n\t\tsuper(filter, synchronous, packageName, eventName, consumerObject, handlerMethod);\n\t}\n\n\t@Override\n\tpublic void dispatch(final Object event, final Object source, boolean remotelyGeneratedEvent) {\n\t\tif (remotelyGeneratedEvent && filter == HandleEvent.Type.local ||\n\t\t\t\t!remotelyGeneratedEvent && filter == HandleEvent.Type.remote) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\thandlerMethod.invoke(consumerObject, event, source);\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/eventbus/impl/Serializer.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\nimport tigase.xml.Element;\n\npublic interface Serializer {\n\n\t<T> T deserialize(final Element element);\n\n\tElement serialize(final Object object);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/examples/ExampleUsingDataRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.examples;\n\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.db.DataRepository;\nimport tigase.db.DataSourceAware;\nimport tigase.db.Repository;\nimport tigase.db.TigaseDBException;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\nimport static java.lang.System.Logger;\nimport static java.lang.System.getLogger;\n\n/**\n * This is only and example, sample implementation of DataSourceAware and should not be used!\n */\n@Repository.Meta(supportedUris = {\"jdbc:[^:]+:.*\"})\npublic class ExampleUsingDataRepository implements DataSourceAware<DataRepository> {\n\n    private static final Logger log = getLogger(ExampleUsingDataRepository.class.getName());\n\n    private static final String GET_DATA = \"SELECT field FROM my_custom_table WHERE user_id = ?\";\n\n    private DataRepository data_repo = null;\n\n    @Override\n    public void setDataSource(DataRepository dataSource) throws RepositoryException {\n        this.data_repo = dataSource;\n        try {\n            data_repo.initPreparedStatement(\"GET_DATA\", GET_DATA);\n        } catch (SQLException e) {\n            log.log(Logger.Level.WARNING, \"Failed to init prepared statement: \" + e.getMessage());\n        }\n    }\n\n\n    public void getDataFromRepo(String userID) throws TigaseDBException {\n        try {\n            var getDataQuery = data_repo.getPreparedStatement(null, GET_DATA);\n            getDataQuery.setString(1, userID);\n            ResultSet rs = null;\n            synchronized (getDataQuery) {\n                try {\n                    rs = getDataQuery.executeQuery();\n                    if (rs.next()) {\n                        log.log(Logger.Level.INFO, \"User data: \" + rs.getString(1));\n                    }\n                } finally {\n                    data_repo.release(null, rs);\n                }\n            }\n        } catch (SQLException e) {\n            throw new TigaseDBException(\"Failed to get prepared statement: \" + e.getMessage());\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/tigase/examples/SampleCustomAuthRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.examples;\n\nimport tigase.auth.credentials.Credentials;\nimport tigase.auth.credentials.entries.PlainCredentialsEntry;\nimport tigase.db.AuthRepository;\nimport tigase.db.AuthorizationException;\nimport tigase.db.TigaseDBException;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.time.Duration;\nimport java.util.Map;\n\n/**\n * This is only and example, sample implementation of AuthRepository and should not be used!\n */\npublic class SampleCustomAuthRepository implements AuthRepository {\n\n    @Override\n    public Credentials getCredentials(BareJID user, String credentialId) throws TigaseDBException {\n        final String passwordFromRepository = \"password\";\n        final PlainCredentialsEntry passwordEntry = new PlainCredentialsEntry(passwordFromRepository);\n        return new SingleCredential(user, getAccountStatus(user), passwordEntry);\n    }\n\n    @Override\n    public AccountStatus getAccountStatus(BareJID user) throws TigaseDBException {\n        return AccountStatus.active;\n    }\n\n    @Override\n    public void loggedIn(BareJID jid) throws TigaseDBException {\n\n    }\n\n    @Override\n    public void logout(BareJID user) throws TigaseDBException {\n\n    }\n\n\n    @Override\n    public void addUser(BareJID user, String password) throws TigaseDBException {\n        throw new UnsupportedOperationException(\"Not supported yet.\");\n    }\n\n    @Override\n    public String getPassword(BareJID user) throws TigaseDBException {\n        throw new UnsupportedOperationException(\"Not supported yet.\");\n    }\n\n    @Override\n    public String getResourceUri() {\n        throw new UnsupportedOperationException(\"Not supported yet.\");\n    }\n\n    @Override\n    public long getActiveUsersCountIn(Duration duration) {\n        throw new UnsupportedOperationException(\"Not supported yet.\");\n    }\n\n    @Override\n    public long getUsersCount() {\n        throw new UnsupportedOperationException(\"Not supported yet.\");\n    }\n\n    @Override\n    public long getUsersCount(String domain) {\n        throw new UnsupportedOperationException(\"Not supported yet.\");\n    }\n\n    @Override\n    public boolean otherAuth(Map<String, Object> authProps) throws TigaseDBException, AuthorizationException {\n        throw new UnsupportedOperationException(\"Not supported yet.\");\n    }\n\n    @Override\n    public void queryAuth(Map<String, Object> authProps) {\n        throw new UnsupportedOperationException(\"Not supported yet.\");\n    }\n\n    @Override\n    public void removeUser(BareJID user) throws TigaseDBException {\n        throw new UnsupportedOperationException(\"Not supported yet.\");\n    }\n\n    @Override\n    public void setAccountStatus(BareJID user, AccountStatus status) throws TigaseDBException {\n        throw new UnsupportedOperationException(\"Not supported yet.\");\n    }\n\n    @Override\n    public void updatePassword(BareJID user, String password) throws TigaseDBException {\n        throw new UnsupportedOperationException(\"Not supported yet.\");\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/io/BufferUnderflowException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.io;\n\nimport java.io.IOException;\n\n/**\n * Describe class BufferUnderflowException here.\n * <br>\n * Created: Sun Sep 24 22:32:37 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class BufferUnderflowException\n\t\textends IOException {\n\n\tprivate static final long serialVersionUID = 1L;\n}\n"
  },
  {
    "path": "src/main/java/tigase/io/CertFilesTrustManager.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.io;\n\nimport javax.net.ssl.X509TrustManager;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.InputStream;\nimport java.security.InvalidAlgorithmParameterException;\nimport java.security.cert.*;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * Class CertFilesTrustManager\n *\n * @author <a href=\"mailto:bartosz.malkowski@tigase.org\">Bartosz Malkowski</a>\n * @version $Rev: $\n */\npublic class CertFilesTrustManager\n\t\timplements X509TrustManager {\n\n\tprivate static CertificateFactory certificateFactory;\n\tprivate PKIXParameters parameters;\n\tprivate CertPathValidator validator;\n\n\tpublic static CertFilesTrustManager getInstance(String pathToCertsFiles) throws Exception {\n\t\tcertificateFactory = CertificateFactory.getInstance(\"X.509\");\n\t\tSet<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>();\n\n\t\tFile[] files = new File(pathToCertsFiles).listFiles();\n\n\t\tfor (File file : files) {\n\t\t\tif (!file.isFile()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tX509Certificate cert = loadCertificate(file);\n\t\t\t\tTrustAnchor ta = new TrustAnchor(cert, null);\n\t\t\t\ttrustAnchors.add(ta);\n\t\t\t} catch (CertificateParsingException e) {\n\t\t\t}\n\t\t}\n\n\t\tCertPathValidator val = CertPathValidator.getInstance(CertPathValidator.getDefaultType());\n\n\t\tPKIXParameters cpp = new PKIXParameters(trustAnchors);\n\t\tcpp.setRevocationEnabled(false);\n\t\tCertFilesTrustManager tm = new CertFilesTrustManager(val, cpp);\n\t\treturn tm;\n\t}\n\n\tprivate static X509Certificate loadCertificate(File certFile) throws Exception {\n\t\tInputStream inStream = null;\n\t\ttry {\n\t\t\tinStream = new FileInputStream(certFile);\n\t\t\tX509Certificate cert = (X509Certificate) certificateFactory.generateCertificate(inStream);\n\t\t\treturn cert;\n\t\t} finally {\n\t\t\tif (inStream != null) {\n\t\t\t\tinStream.close();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate CertFilesTrustManager(CertPathValidator val, PKIXParameters cpp) {\n\t\tthis.validator = val;\n\t\tthis.parameters = cpp;\n\t}\n\n\t@Override\n\tpublic void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {\n\t\tcheckServerTrusted(chain, authType);\n\t}\n\n\t@Override\n\tpublic void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {\n\t\t// TODO Auto-generated method stub\n\t\tList<X509Certificate> certs = new ArrayList<X509Certificate>();\n\t\tfor (X509Certificate certificate : chain) {\n\t\t\tcertificate.checkValidity();\n\t\t\tcerts.add(certificate);\n\t\t}\n\t\tCertPath cp = CertificateFactory.getInstance(\"X.509\").generateCertPath(certs);\n\t\ttry {\n\t\t\tCertPathValidatorResult result = validator.validate(cp, parameters);\n\t\t\tSystem.out.println(result);\n\t\t} catch (CertPathValidatorException e) {\n\t\t\t// e.printStackTrace();\n\t\t\tthrow new CertificateException(e);\n\t\t} catch (InvalidAlgorithmParameterException e) {\n\t\t\t// e.printStackTrace();\n\t\t\tthrow new CertificateException(e);\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic X509Certificate[] getAcceptedIssuers() {\n\t\treturn parameters.getTrustAnchors().toArray(new X509Certificate[]{});\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/io/CertificateContainer.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.io;\n\nimport tigase.cert.CertificateEntry;\nimport tigase.cert.CertificateUtil;\nimport tigase.db.comp.RepositoryChangeListenerIfc;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.EventBusEvent;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.eventbus.HandleEvent;\nimport tigase.io.repo.CertificateItem;\nimport tigase.io.repo.CertificateRepository;\nimport tigase.kernel.beans.*;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.core.Kernel;\n\nimport javax.net.ssl.*;\nimport java.io.*;\nimport java.net.Socket;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.nio.file.StandardCopyOption;\nimport java.security.*;\nimport java.security.cert.*;\nimport java.security.cert.Certificate;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentSkipListMap;\nimport java.util.concurrent.ConcurrentSkipListSet;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.io.SSLContextContainerIfc.*;\n\n/**\n * Class used to keep SSL certificates loaded in memory. To get instance use getter from TLSUtil class.\n * <br>\n * Created by andrzej on 29.02.2016.\n */\n@Bean(name = \"certificate-container\", parent = Kernel.class, active = true, exportable = true)\npublic class CertificateContainer\n\t\timplements CertificateContainerIfc,\n\t\t\t\t   Initializable,\n\t\t\t\t   UnregisterAware,\n\t\t\t\t   RegistrarBean,\n\t\t\t\t   RepositoryChangeListenerIfc<CertificateItem> {\n\n\tpublic final static String PER_DOMAIN_CERTIFICATE_KEY = \"virt-hosts-cert-\";\n\tpublic final static String SNI_DISABLE_KEY = \"sni-disable\";\n\tprivate static final Logger log = Logger.getLogger(CertificateContainer.class.getCanonicalName());\n\tprivate static final EventBus eventBus = EventBusFactory.getInstance();\n\t@Inject(nullAllowed = true)\n\tCertificateRepository repository;\n\tprivate Map<String, CertificateEntry> cens = new ConcurrentSkipListMap<>();\n\t@ConfigField(desc = \"Custom certificates\", alias = \"custom-certificates\")\n\tprivate Map<String, String> customCerts = new HashMap<>();\n\t@ConfigField(desc = \"Alias for default certificate\", alias = DEFAULT_DOMAIN_CERT_KEY)\n\tprivate String def_cert_alias = DEFAULT_DOMAIN_CERT_VAL;\n\tprivate File defaultCertDirectory = null;\n\tprivate String email = \"admin@tigase.org\";\n\tprivate char[] emptyPass = new char[0];\n\tprivate Map<String, KeyManagerFactory> kmfs = new ConcurrentSkipListMap<String, KeyManagerFactory>();\n\tprivate KeyManager[] kms = new KeyManager[]{new SniKeyManager()};\n\tprivate String o = \"Tigase.org\";\n\tprivate String ou = \"XMPP Service\";\n\t@ConfigField(desc = \"Remove root CA (efectively self-signed) certificate from chain\")\n\tprivate boolean removeRootCACertificate = true;\n\t@ConfigField(desc = \"Disable SNI support\", alias = SNI_DISABLE_KEY)\n\tprivate boolean sniDisable = false;\n\t@ConfigField(desc = \"Location of server SSL certificates\", alias = SERVER_CERTS_LOCATION_KEY)\n\tprivate String[] sslCertsLocation = {SERVER_CERTS_LOCATION_VAL};\n\tprivate X509TrustManager[] tms = new X509TrustManager[]{new FakeTrustManager()};\n\tprivate KeyStore trustKeyStore = null;\n\t@ConfigField(desc = \"Location of trusted certificates\", alias = TRUSTED_CERTS_DIR_KEY)\n\tprivate String[] trustedCertsDir = {TRUSTED_CERTS_DIR_VAL};\n\t@ConfigField(desc = \"Remove unused self-signed certificates\", alias = \"remove-self-signed-unused-certificates\")\n\tprivate boolean removeSelfSignedUnusedCerts = false;\n\n\tpublic void setRepository(CertificateRepository repository) {\n\t\tif (repository != null) {\n\t\t\tlog.log(Level.INFO,\n\t\t\t\t\t\"CertificateRepository configured! No certificate will be loaded from the local filesystem!\");\n\t\t}\n\t\tthis.repository = repository;\n\t}\n\n\t@Override\n//\t@Deprecated\n//\t@TigaseDeprecated(since = \"8.4.0\", removeIn = \"9.0.0\")\n\tpublic void addCertificates(Map<String, String> params) throws CertificateParsingException {\n\t\tString pemCert = params.get(PEM_CERTIFICATE_KEY);\n\t\tString saveToDiskVal = params.get(CERT_SAVE_TO_DISK_KEY);\n\t\tboolean saveToDisk = (saveToDiskVal != null) && saveToDiskVal.equalsIgnoreCase(\"true\");\n\n\t\tString useAsDefaultVal = params.get(DEFAULT_DOMAIN_CERT_KEY);\n\t\tboolean useAsDefault = (useAsDefaultVal != null) && useAsDefaultVal.equalsIgnoreCase(\"true\");\n\n\t\tfinal String alias = params.get(CERT_ALIAS_KEY);\n\n\t\tif (alias == null) {\n\t\t\tthrow new RuntimeException(\"Certificate alias must be specified\");\n\t\t}\n\n\t\tif (pemCert != null) {\n\t\t\taddCertificate(alias, pemCert, saveToDisk, true);\n\t\t\tif (useAsDefault) {\n\t\t\t\taddCertificate(def_cert_alias, pemCert, saveToDisk, true);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void addCertificates(CertificateEntity certificateEntity) throws CertificateParsingException {\n\t\taddCertificate(certificateEntity.alias(), certificateEntity.certificatePem(), certificateEntity.storePermanently(), true);\n\t\tif (certificateEntity.useAsDefault()) {\n\t\t\taddCertificate(def_cert_alias, certificateEntity.certificatePem(), certificateEntity.storePermanently(),\n\t\t\t               true);\n\t\t}\n\t}\n\n\t@Override\n\tpublic KeyManager[] createCertificate(String alias)\n\t\t\tthrows NoSuchAlgorithmException, CertificateException, SignatureException, NoSuchProviderException,\n\t\t\t\t   InvalidKeyException, IOException, UnrecoverableKeyException, KeyStoreException {\n\t\tfinal KeyManagerFactory keyManagerFactory = createCertificateKmf(alias);\n\t\tKeyManager[] kms = keyManagerFactory.getKeyManagers();\n\t\tlog.log(Level.WARNING, \"Auto-generated certificate for domain: {0}\", alias);\n\t\treturn kms;\n\t}\n\n\t@Override\n\tpublic String getDefCertAlias() {\n\t\treturn def_cert_alias;\n\t}\n\n\tpublic CertificateEntry getCertificateEntry(String hostname) {\n\t\tString alias = hostname == null ? getDefCertAlias() : hostname.toLowerCase();\n\t\treturn SSLContextContainerAbstract.find(cens, alias);\n\t}\n\n\t@Override\n\tpublic KeyManager[] getKeyManagers(String hostname) {\n\t\tlog.log(Level.FINEST, \"Looking up domain: {0} in collection: {1}\", new Object[] {hostname, kmfs.keySet()});\n\t\tif (hostname == null && !sniDisable) {\n\t\t\treturn kms;\n\t\t}\n\n\t\tString alias = hostname;\n\t\tif (alias == null) {\n\t\t\talias = getDefCertAlias();\n\t\t}\n\n\t\tKeyManagerFactory kmf = SSLContextContainerAbstract.find(kmfs, alias);\n\t\treturn (kmf == null) ? null : kmf.getKeyManagers();\n\t}\n\n\t@Override\n\tpublic TrustManager[] getTrustManagers() {\n\t\treturn tms;\n\t}\n\n\t@Override\n\tpublic KeyStore getTrustStore() {\n\t\treturn trustKeyStore;\n\t}\n\n\t@Override\n\tpublic void init(Map<String, Object> params) {\n\t\ttry {\n\t\t\tdef_cert_alias = (String) params.get(DEFAULT_DOMAIN_CERT_KEY);\n\n\t\t\tif (def_cert_alias == null) {\n\t\t\t\tdef_cert_alias = DEFAULT_DOMAIN_CERT_VAL;\n\t\t\t}\n\n\t\t\tif (params.containsKey(SNI_DISABLE_KEY)) {\n\t\t\t\tsniDisable = (Boolean) params.get(SNI_DISABLE_KEY);\n\t\t\t} else {\n\t\t\t\tsniDisable = false;\n\t\t\t}\n\n\t\t\tif (repository != null) {\n\t\t\t\tloadCertificatesFromRepository();\n\t\t\t} else {\n\n\t\t\t\tString pemD = (String) params.get(SERVER_CERTS_LOCATION_KEY);\n\n\t\t\t\tif (pemD == null) {\n\t\t\t\t\tpemD = SERVER_CERTS_LOCATION_VAL;\n\t\t\t\t}\n\n\t\t\t\tString[] pemDirs = pemD.split(\",\");\n\n\t\t\t\tdefaultCertDirectory = getDefaultCertDirectory(pemDirs);\n\n\t\t\t\t// we should first load available files and then override it with user configured mapping\n\t\t\t\tloadCertificatesFromDirectories(pemDirs, false);\n\n\t\t\t\tMap<String, String> predefined = findPredefinedCertificates(params);\n\t\t\t\tloadPredefinedCertificates(predefined, false);\n\t\t\t}\n\t\t} catch (Exception ex) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"There was a problem initializing SSL certificates.\", ex);\n\t\t\t}\n\t\t\tlog.log(Level.WARNING, \"There was a problem initializing SSL certificates.\");\n\t\t}\n\n\t\tString trustLoc = (String) params.get(TRUSTED_CERTS_DIR_KEY);\n\n\t\tif (trustLoc == null) {\n\t\t\ttrustLoc = TRUSTED_CERTS_DIR_VAL;\n\t\t}\n\n\t\tfinal String[] trustLocations = trustLoc.split(\",\");\n\n\t\t// It may take a while, let's do it in background\n\t\tnew Thread() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tloadTrustedCerts(trustLocations);\n\t\t\t}\n\t\t}.start();\n\t}\n\n\t@Override\n\tpublic void itemAdded(CertificateItem item) {\n\t\ttry {\n\t\t\taddCertificateEntry(item.getCertificateEntry(), item.getAlias(), false);\n\t\t\tif (item.isDefault()) {\n\t\t\t\taddCertificateEntry(item.getCertificateEntry(), \"default\", false);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Problem adding certificate while reloading from repository\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void itemUpdated(CertificateItem item) {\n\t\ttry {\n\t\t\taddCertificateEntry(item.getCertificateEntry(), item.getAlias(), false);\n\t\t\tif (item.isDefault()) {\n\t\t\t\taddCertificateEntry(item.getCertificateEntry(), \"default\", false);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Problem adding certificate while reloading from repository\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void itemRemoved(CertificateItem item) {\n\t\tkmfs.remove(item.getAlias());\n\t\tcens.remove(item.getAlias());\n\t\tif (item.isDefault()) {\n\t\t\tkmfs.remove(\"default\");\n\t\t\tcens.remove(\"default\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\teventBus.registerAll(this);\n\t\ttry {\n\t\t\tif (repository != null) {\n\t\t\t\tloadCertificatesFromRepository();\n\t\t\t\tif (repository.isMoveFromFilesystemToRepository()) {\n\t\t\t\t\tloadCertificatesFromDirectories(sslCertsLocation, true);\n\t\t\t\t\tloadPredefinedCertificates(customCerts, true);\n\t\t\t\t\tfor (Map.Entry<String, CertificateEntry> certificates : cens.entrySet()) {\n\t\t\t\t\t\trepository.addItem(new CertificateItem(certificates.getKey(), certificates.getValue()));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tString[] pemDirs = sslCertsLocation;\n\t\t\t\tdefaultCertDirectory = getDefaultCertDirectory(pemDirs);\n\t\t\t\t// we should first load available files and then override it with user configured mapping\n\t\t\t\tloadCertificatesFromDirectories(pemDirs, false);\n\t\t\t\tloadPredefinedCertificates(customCerts, false);\n\t\t\t}\n\t\t} catch (Exception ex) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"There was a problem initializing SSL certificates.\", ex);\n\t\t\t}\n\t\t\tlog.log(Level.WARNING, \"There was a problem initializing SSL certificates.\");\n\t\t}\n\n\t\t// It may take a while, let's do it in background\n\t\tnew Thread() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tloadTrustedCerts(trustedCertsDir);\n\t\t\t}\n\t\t}.start();\n\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\teventBus.unregisterAll(this);\n\t}\n\n\t@HandleEvent\n\tpublic void certificateChange(CertificateChange event) {\n\t\tif (event.isLocal()) {\n\t\t\treturn;\n\t\t}\n\t\tboolean saveToDisk = repository == null;\n\t\ttry {\n\t\t\taddCertificate(event.getAlias(), event.getPemCertificate(), saveToDisk, false);\n\t\t} catch (CertificateParsingException ex) {\n\t\t\tlog.log(Level.WARNING, \"Failed to update certificate for \" + event.getAlias(), ex);\n\t\t}\n\t\tif (repository != null) {\n\t\t\trepository.reload();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\n\t}\n\n\tprivate boolean shouldReplace(String alias, CertificateEntry update) {\n\t\tCertificateEntry current = cens.get(alias);\n\t\tif (current == null) {\n\t\t\treturn true;\n\t\t}\n\t\t// if current certificate is self-signed we override it (even with not valid)\n\t\tif (current.isSelfSigned()) {\n\t\t\t// if certificate is for \"exact\" domain, then it is more important that certificate for SAN\n\t\t\tif (alias.equals(current.getDomain()) && update.isSelfSigned() && !alias.equals(update.getDomain())) {\n\t\t\t\t// except current is not valid or is self-signed and update is not, then use a SAN\n\t\t\t\treturn !current.isValid();\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\t// if new certificate if self-signed but previous wasn't - do not update!\n\t\tif (update.isSelfSigned() && !current.isSelfSigned()) {\n\t\t\treturn false;\n\t\t}\n\t\t// both certificates are properly signed\n\t\t// if certificate is for \"exact\" domain, then it is more important that certificate for SAN\n\t\tif (alias.equals(current.getDomain()) && !alias.equals(update.getDomain())) {\n\t\t\t// except current is not valid, then use a SAN\n\t\t\treturn !current.isValid();\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic KeyManagerFactory addCertificateEntry(CertificateEntry entry, String alias, boolean store)\n\t\t\tthrows KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException,\n\t\t\t\t   UnrecoverableKeyException {\n\t\tlog.log(Level.FINEST, \"Adding certificate entry for alias: {0}. Saving to disk: {1}, entry: {2}\",\n\t\t\t\tnew Object[]{alias, store, entry != null ? entry.toString(true) : \"null\"});\n\n\t\tboolean wasUsed = false;\n\n\t\tKeyManagerFactory rootKmf =  getKeyManagerFactory(alias, entry);\n\t\tif (shouldReplace(alias, entry)) {\n\t\t\tlog.log(Level.FINEST, \"Certificate present with domain {0}, updating kmfs cens\", alias);\n\t\t\tkmfs.put(alias, rootKmf);\n\t\t\tCertificateEntry removed = cens.put(alias, entry);\n\t\t\tremoveSelfSignedWildcardIfMainWasRemoved(removed, entry);\n\t\t\twasUsed = true;\n\t\t}\n\n\t\tif (!def_cert_alias.equals(alias) && (wasUsed || !entry.isSelfSigned())) {\n\t\t\tSet<String> domains = entry.getAllDomains();\n\t\t\tdomains.removeIf(domain -> !shouldReplace(domain, entry));\n\t\t\tvar spareDomainNamesToRemoveFromKmfs = SSLContextContainerAbstract.getSpareDomainNamesToRemove(kmfs.keySet(), domains).stream().filter(domain -> shouldReplace(domain, entry)).toList();\n\t\t\tvar spareDomainNamesToRemoveFromCens = SSLContextContainerAbstract.getSpareDomainNamesToRemove(cens.keySet(), domains).stream().filter(domain -> shouldReplace(domain, entry)).toList();\n\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\"Certificate present with domains: {0}. Replacing in collections, kmfs domains: {1}, spare to remove: {2}; cens domains: {3}, spare to remove: {4}. Certificate: {5}\",\n\t\t\t\t\tnew Object[]{domains, kmfs.keySet(), spareDomainNamesToRemoveFromKmfs, cens.keySet(), spareDomainNamesToRemoveFromCens, entry.toString(true)});\n\n\t\t\tfor (String domain : domains) {\n\t\t\t\tKeyManagerFactory kmf = getKeyManagerFactory(domain, entry);\n\t\t\t\tkmfs.put(domain, kmf);\n\t\t\t\tCertificateEntry removed = cens.put(domain, entry);\n\t\t\t\tremoveSelfSignedWildcardIfMainWasRemoved(removed, entry);\n\t\t\t\twasUsed = true;\n\t\t\t}\n\t\t\tspareDomainNamesToRemoveFromKmfs.forEach(d -> kmfs.remove(d));\n\t\t\tspareDomainNamesToRemoveFromCens.forEach(d -> {\n\t\t\t\tvar removed = cens.remove(d);\n\t\t\t\tremoveSelfSignedWildcardIfMainWasRemoved(removed, entry);\n\t\t\t});\n\t\t}\n\n\t\tif (store) {\n\t\t\tif ((!wasUsed) && entry.isSelfSigned()) {\n\t\t\t\tif (repository != null && removeSelfSignedUnusedCerts) {\n\t\t\t\t\tlog.log(Level.FINER, \"Self-signed certificate entry for alias: {0} was not used, removing {0}\");\n\t\t\t\t\trepository.removeItem(alias);\n\t\t\t\t} else {\n\t\t\t\t\tlog.log(Level.FINER, \"Self-signed certificate entry for alias: {0} was not used\", alias);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (repository != null) {\n\t\t\t\t\tfinal CertificateItem item = new CertificateItem(alias, entry);\n\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\"Storing to repository, certificate entry for alias: {0} with SerialNumber: {1}\",\n\t\t\t\t\t\t\tnew Object[]{alias, item.getSerialNumber()});\n\t\t\t\t\trepository.addItem(item);\n\t\t\t\t} else {\n\t\t\t\t\tstoreCertificateToFile(entry, alias);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn rootKmf;\n\t}\n\n\tprivate void removeSelfSignedWildcardIfMainWasRemoved(CertificateEntry replaced, CertificateEntry updated) {\n\t\tif (replaced == null || replaced == updated) {\n\t\t\treturn;\n\t\t}\n\t\tlog.log(Level.FINEST, \"replaced certificate for \" + replaced.getDomain() + \", self-signed: \" + replaced.isSelfSigned() + \", \" + replaced + \" with \" + updated.getDomain() + \", self-signed: \" + updated.isSelfSigned() + \", \" + updated);\n\t\t// if we remove self-signed cert for domain, remove it for wildcard as well (if the same is used)\n\t\tif (replaced.isSelfSigned() && !replaced.getDomain().startsWith(\"*.\")) {\n\t\t\tlog.log(Level.FINEST, \"removing certificate for *.\" + replaced.getDomain() + \", self-signed: \" + replaced.isSelfSigned());\n\t\t\tif (cens.get(\"*.\" + replaced.getDomain()) == replaced) {\n\t\t\t\tkmfs.remove(\"*.\" + replaced.getDomain());\n\t\t\t\tcens.remove(\"*.\" + replaced.getDomain());\n\t\t\t\tif (!cens.containsValue(replaced)) {\n\t\t\t\t\tif (repository != null && removeSelfSignedUnusedCerts) {\n\t\t\t\t\t\tlog.log(Level.FINER, \"Self-signed certificate entry for alias: {0} was not used, removing {0}\", replaced.getDomain());\n\t\t\t\t\t\trepository.removeItem(replaced.getDomain());\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlog.log(Level.FINER, \"Self-signed certificate entry for alias: {0} was not used\", replaced.getDomain());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void loadCertificatesFromRepository() {\n\t\tif (repository != null) {\n\t\t\tList<CertificateItem> allSelfSignedItems = new ArrayList<>();\n\t\t\tfor (CertificateItem item : repository.allItems()) {\n\t\t\t\tCertificateEntry certificate = item.getCertificateEntry();\n\t\t\t\tString alias = item.getAlias();\n\t\t\t\ttry {\n\t\t\t\t\taddCertificateEntry(certificate, alias, false);\n\t\t\t\t\tif (certificate.isSelfSigned()) {\n\t\t\t\t\t\tallSelfSignedItems.add(item);\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Cannot load certficate from repository: \" + item.getKey(), ex);\n\t\t\t\t\t}\n\t\t\t\t\tlog.log(Level.WARNING, \"Cannot load certficate from repository: \" + item.getKey());\n\t\t\t\t}\n\t\t\t}\n\t\t\tList<String> skipped = new ArrayList<>();\n\t\t\tfor (CertificateItem item : allSelfSignedItems) {\n\t\t\t\tCertificateEntry certificate = item.getCertificateEntry();\n\t\t\t\tString alias = item.getAlias();\n\t\t\t\tif (!certificate.isSelfSigned()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (cens.containsValue(certificate)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tskipped.add(alias);\n\t\t\t\tif (removeSelfSignedUnusedCerts) {\n\t\t\t\t\tlog.log(Level.FINER, \"Self-signed certificate entry for alias: {0} was not used, removing {0}\",\n\t\t\t\t\t\t\talias);\n\t\t\t\t\trepository.removeItem(alias);\n\t\t\t\t}\n\t\t\t}\n\t\t\tlog.log(Level.FINER, \"Loaded \" + cens.size() + \" certificate entries from repository for domains (main): \" + cens.values().stream().map(CertificateEntry::getDomain).distinct().toList() + \", skipped self-signed: \" + skipped);\n\t\t}\n\t}\n\n\tprivate void loadCertificatesFromDirectories(String[] pemDirs, boolean moveFileToBackup) {\n\t\tfor (String pemDir : pemDirs) {\n\t\t\tlog.log(Level.CONFIG, \"Loading server certificates from PEM directory: {0}\", pemDir);\n\t\t\tfinal File directory = new File(pemDir);\n\t\t\tif (!directory.exists()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfinal File[] files = directory.listFiles(new PEMFileFilter());\n\t\t\t// let's add certificates from most top-level to most nested domains (with the assumption that\n\t\t\t// if there is a file for subdomain then there was an explicit effort to generat cert for that\n\t\t\t// subdomain.\n\t\t\tArrays.sort(files, Comparator.comparingInt(fn -> fn.getName().split(\"\\\\.\").length));\n\t\t\tfor (File file : files) {\n\t\t\t\tloadCertificateFromFile(file, moveFileToBackup);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void loadCertificateFromFile(File file, boolean moveFileToBackup) {\n\t\tString alias = file.getName();\n\t\tif (alias.endsWith(\".pem\")) {\n\t\t\talias = alias.substring(0, alias.length() - 4);\n\t\t}\n\n\t\tloadCertificateFromFile(file, alias, moveFileToBackup);\n\t}\n\n\tprivate void loadCertificateFromFile(File file, String alias, boolean moveFileToBackup) {\n\t\ttry {\n\t\t\tCertificateEntry certEntry = CertificateUtil.loadCertificate(file);\n\t\t\taddCertificateEntry(certEntry, alias, false);\n\t\t\tSet<String> domains = certEntry.getAllDomains();\n\t\t\tlog.log(Level.CONFIG, \"Loaded server certificate for domain: {0} (altCNames: {1}) from file: {2}\",\n\t\t\t\t\tnew Object[]{alias, String.join(\", \", domains), file});\n\t\t\tif (moveFileToBackup) {\n\t\t\t\tPath target = null;\n\t\t\t\ttry {\n\t\t\t\t\ttarget = file.toPath().resolveSibling(file.toPath().getFileName() + \".bak\");\n\t\t\t\t\tFiles.move(file.toPath(), target, StandardCopyOption.REPLACE_EXISTING);\n\t\t\t\t\tlog.log(Level.CONFIG, \"Made backup of file: {0} to: {1}\", new Object[]{file, target});\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.log(Level.INFO, \"Making certificate backup file from: {0} to: {1} failed!\", new Object[]{file, target, e});\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception ex) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Cannot load certficate from file: \" + file, ex);\n\t\t\t}\n\t\t\tlog.log(Level.WARNING, \"Cannot load certficate from file: \" + file + \", \" + ex.getMessage());\n\t\t}\n\t}\n\n\tprivate void loadPredefinedCertificates(Map<String, String> predefined, boolean moveFileToBackup) {\n\t\tlog.log(Level.CONFIG, \"Loading predefined server certificates\");\n\t\tfor (final Map.Entry<String, String> entry : predefined.entrySet()) {\n\t\t\tFile file = new File(entry.getValue());\n\t\t\tString alias = entry.getKey();\n\t\t\tloadCertificateFromFile(file, alias, moveFileToBackup);\n\t\t}\n\t}\n\n\tprivate File getDefaultCertDirectory(String[] pemDirs) {\n\t\tfinal File file = Arrays.stream(pemDirs)\n\t\t\t\t.map(Paths::get)\n\t\t\t\t.map(Path::toFile)\n\t\t\t\t.filter(File::exists)\n\t\t\t\t.findFirst()\n\t\t\t\t.orElse(Paths.get(pemDirs[0]).toFile());\n\t\tlog.log(Level.CONFIG, () -> \"Setting default directory for storing certificates to: \" + file.getAbsolutePath());\n\t\treturn file;\n\t}\n\n\tvoid storeCertificateToFile(CertificateEntry entry, String filename)\n\t\t\tthrows CertificateEncodingException, IOException {\n\t\tfinal String path = new File(defaultCertDirectory, filename + \".pem\").toString();\n\t\tCertificateUtil.storeCertificate(path, entry);\n\t}\n\n\tprivate KeyManagerFactory getKeyManagerFactory(String domain, CertificateEntry entry)\n\t\t\tthrows UnrecoverableKeyException, CertificateException, KeyStoreException, IOException,\n\t\t\t\t   NoSuchAlgorithmException {\n\t\treturn getKeyManagerFactory(domain, entry.getPrivateKey(), entry.getCertChain(removeRootCACertificate));\n\t}\n\n\tprivate KeyManagerFactory getKeyManagerFactory(String domain, PrivateKey privateKey, Certificate[] certChain)\n\t\t\tthrows KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException,\n\t\t\t\t   UnrecoverableKeyException {\n\t\tKeyStore keys = KeyStore.getInstance(\"JKS\");\n\t\tkeys.load(null, emptyPass);\n\t\tkeys.setKeyEntry(domain, privateKey, emptyPass, certChain);\n\n\t\tKeyManagerFactory kmf = KeyManagerFactory.getInstance(\"SunX509\");\n\t\tkmf.init(keys, emptyPass);\n\t\treturn kmf;\n\t}\n\n\tprivate void addCertificate(String alias, String pemCert, boolean saveToDisk, boolean notifyCluster)\n\t\t\tthrows CertificateParsingException {\n\t\ttry {\n\t\t\tlog.log(Level.FINEST, \"Adding new certificate with alias: {0}. Saving to disk: {1}, notify cluster: {2}\",\n\t\t\t\t\tnew Object[]{alias, saveToDisk, notifyCluster});\n\t\t\tCertificateEntry entry = CertificateUtil.parseCertificate(new StringReader(pemCert));\n\t\t\taddCertificateEntry(entry, alias, saveToDisk);\n\t\t\tif (notifyCluster) {\n\t\t\t\t// we notify whole cluster but the event should not propagate \"Save to disk\" state if we use repository\n\t\t\t\tboolean eventSaveToDisk = repository == null;\n\t\t\t\teventBus.fire(new CertificateChange(alias, pemCert, eventSaveToDisk));\n\t\t\t}\n\t\t\tSet<String> domains = entry.getAllDomains();\n\n\t\t\teventBus.fire(new CertificateChanged(alias, domains));\n\t\t\tlog.log(Level.CONFIG,\n\t\t\t\t\t\"Certificate with alias: {0} for domains: {1} added. Saving to disk: {2}, notify cluster: {3}\",\n\t\t\t\t\tnew Object[]{alias, domains, saveToDisk, notifyCluster});\n\t\t} catch (Exception ex) {\n\t\t\tthrow new CertificateParsingException(\"Problem adding a new certificate (\" + ex.getMessage() + \")\", ex);\n\t\t}\n\t}\n\n\tprivate KeyManagerFactory createCertificateKmf(String alias)\n\t\t\tthrows NoSuchAlgorithmException, CertificateException, IOException, InvalidKeyException,\n\t\t\t\t   NoSuchProviderException, SignatureException, KeyStoreException, UnrecoverableKeyException {\n\t\tCertificateEntry entry = CertificateUtil.createSelfSignedCertificate(email, alias, ou, o, null, null, null,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t () -> CertificateUtil.createKeyPair(1024,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"secret\"));\n\t\treturn addCertificateEntry(entry, alias, true);\n\t}\n\n\tprivate Map<String, String> findPredefinedCertificates(Map<String, Object> params) {\n\t\tfinal Map<String, String> result = new HashMap<>();\n\t\tif (params == null) {\n\t\t\treturn result;\n\t\t}\n\n\t\tfor (String t : params.keySet()) {\n\t\t\tif (t.startsWith(PER_DOMAIN_CERTIFICATE_KEY)) {\n\t\t\t\tString domainName = t.substring(PER_DOMAIN_CERTIFICATE_KEY.length());\n\t\t\t\tresult.put(domainName, params.get(t).toString());\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\n\t}\n\n\tprivate void loadTrustedCerts(String[] trustLocations) {\n\t\tint counter = 0;\n\t\tlong start = System.currentTimeMillis();\n\n\t\tList<X509Certificate> acceptedIssuers = new ArrayList<>(200);\n\t\ttry {\n\t\t\ttrustKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());\n\t\t\ttrustKeyStore.load(null, emptyPass);\n\n\t\t\tfinal File trustStoreFile = new File(\n\t\t\t\t\tSystem.getProperty(\"java.home\") + \"/lib/security/cacerts\".replace('/', File.separatorChar));\n\t\t\tfinal File userStoreFile = new File(\"~/.keystore\");\n\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"Looking for trusted certs in: {0}\", trustStoreFile);\n\t\t\t}\n\n\t\t\tif (trustStoreFile.exists()) {\n\t\t\t\tlog.log(Level.CONFIG, \"Loading trustKeyStore from location: {0}\", trustStoreFile);\n\t\t\t\tInputStream in = new FileInputStream(trustStoreFile);\n\t\t\t\ttrustKeyStore.load(in, null);\n\t\t\t\tin.close();\n\t\t\t}\n\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"Looking for trusted certs in: {0}\", userStoreFile);\n\t\t\t}\n\n\t\t\tif (userStoreFile.exists()) {\n\t\t\t\tlog.log(Level.CONFIG, \"Loading trustKeyStore from location: {0}\", userStoreFile);\n\t\t\t\tInputStream in = new FileInputStream(userStoreFile);\n\t\t\t\ttrustKeyStore.load(in, null);\n\t\t\t\tin.close();\n\t\t\t}\n\n\t\t\tlog.log(Level.CONFIG, \"Loading trustKeyStore from locations: {0}\", Arrays.toString(trustLocations));\n\t\t\tfor (String location : trustLocations) {\n\t\t\t\tFile root = new File(location);\n\t\t\t\tFile[] files = root.listFiles(new PEMFileFilter());\n\n\t\t\t\tif (files != null) {\n\t\t\t\t\tfor (File file : files) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tCertificateEntry certEntry = CertificateUtil.loadCertificate(file);\n\t\t\t\t\t\t\tCertificate[] chain = certEntry.getCertChain();\n\n\t\t\t\t\t\t\tif (chain != null) {\n\t\t\t\t\t\t\t\tfor (Certificate cert : chain) {\n\t\t\t\t\t\t\t\t\tif (cert instanceof X509Certificate) {\n\t\t\t\t\t\t\t\t\t\tX509Certificate crt = (X509Certificate) cert;\n\t\t\t\t\t\t\t\t\t\tString alias = crt.getSubjectX500Principal().getName();\n\n\t\t\t\t\t\t\t\t\t\ttrustKeyStore.setCertificateEntry(alias, crt);\n\t\t\t\t\t\t\t\t\t\tacceptedIssuers.add(crt);\n\t\t\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Imported certificate: {0}\", alias);\n\t\t\t\t\t\t\t\t\t\t++counter;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Problem loading certificate from file: \" + file, e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tlog.log(Level.WARNING, \"Problem loading certificate from file: {0}\", file);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t} catch (Exception ex) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"An error loading trusted certificates\", ex);\n\t\t\t}\n\t\t\tlog.log(Level.WARNING, \"An error loading trusted certificates\");\n\t\t}\n\n\t\ttry {\n\t\t\tif (!trustKeyStore.aliases().hasMoreElements()) {\n\t\t\t\tlog.log(Level.CONFIG, \"No Trusted Anchors!!! Creating temporary trusted CA cert!\");\n\t\t\t\tCertificateEntry entry = CertificateUtil.createSelfSignedCertificate(\"fake_local@tigase\", \"fake one\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"none\", \"none\", \"none\", \"none\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"US\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t () -> CertificateUtil.createKeyPair(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t 1024, \"secret\"));\n\t\t\t\ttrustKeyStore.setCertificateEntry(\"generated fake CA\", entry.getCertChain()[0]);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Can''t generate fake trusted CA certificate\", e);\n\t\t\t}\n\t\t\tlog.log(Level.WARNING, \"Can''t generate fake trusted CA certificate\");\n\t\t}\n\n\t\ttms = new X509TrustManager[]{\n\t\t\t\tnew FakeTrustManager(acceptedIssuers.toArray(new X509Certificate[acceptedIssuers.size()]))};\n\n\t\tlong seconds = (System.currentTimeMillis() - start) / 1000;\n\n\t\tlog.log(Level.CONFIG, \"Loaded {0} trust certificates, it took {1} seconds.\", new Object[]{counter, seconds});\n\t}\n\n\t/**\n\t * Event indicating certificate change that will be distributed in the cluster.\n\t */\n\tpublic static class CertificateChange\n\t\t\timplements Serializable, EventBusEvent {\n\n\t\tprivate String alias;\n\t\tprivate transient boolean local = false;\n\t\tprivate String pemCert;\n\t\tprivate boolean saveToDisk;\n\n\t\t/**\n\t\t * Empty constructor to be able to serialize/deserialize event\n\t\t */\n\t\tpublic CertificateChange() {\n\t\t}\n\n\t\tpublic CertificateChange(String alias, String pemCert, boolean saveToDisk) {\n\t\t\tthis.alias = alias;\n\t\t\tthis.pemCert = pemCert;\n\t\t\tthis.saveToDisk = saveToDisk;\n\t\t\tthis.local = true;\n\t\t}\n\n\t\tpublic String getAlias() {\n\t\t\treturn alias;\n\t\t}\n\n\t\tpublic String getPemCertificate() {\n\t\t\treturn pemCert;\n\t\t}\n\n\t\tpublic boolean isLocal() {\n\t\t\treturn local;\n\t\t}\n\n\t\tpublic boolean isSaveToDisk() {\n\t\t\treturn saveToDisk;\n\t\t}\n\t}\n\n\tprivate static class FakeTrustManager\n\t\t\timplements X509TrustManager {\n\n\t\tprivate X509Certificate[] issuers = null;\n\n\t\tFakeTrustManager() {\n\t\t\tthis(new X509Certificate[0]);\n\t\t}\n\n\t\tFakeTrustManager(X509Certificate[] ai) {\n\t\t\tissuers = ai;\n\t\t}\n\n\t\t@Override\n\t\tpublic void checkClientTrusted(final X509Certificate[] x509CertificateArray, final String string)\n\t\t\t\tthrows CertificateException {\n\t\t}\n\n\t\t@Override\n\t\tpublic void checkServerTrusted(final X509Certificate[] x509CertificateArray, final String string)\n\t\t\t\tthrows CertificateException {\n\t\t}\n\n\t\t@Override\n\t\tpublic X509Certificate[] getAcceptedIssuers() {\n\t\t\treturn issuers;\n\t\t}\n\t}\n\n\tpublic class CertificateChanged implements EventBusEvent {\n\n\t\tSet<String> domains = new ConcurrentSkipListSet<>();\n\t\tprivate String alias;\n\n\t\tpublic CertificateChanged(String alias, Set<String> domains) {\n\t\t\tthis.alias = alias;\n\t\t\tif (domains != null) {\n\t\t\t\tthis.domains.addAll(domains);\n\t\t\t}\n\t\t}\n\n\t\tpublic String getAlias() {\n\t\t\treturn alias;\n\t\t}\n\n\t\tpublic Set<String> getDomains() {\n\t\t\treturn domains;\n\t\t}\n\t}\n\n\tprivate class PEMFileFilter\n\t\t\timplements FileFilter {\n\n\t\t@Override\n\t\tpublic boolean accept(File pathname) {\n\t\t\treturn pathname.isFile() && (pathname.getName().endsWith(\".pem\") || pathname.getName().endsWith(\".PEM\") ||\n\t\t\t\t\tpathname.getName().endsWith(\".crt\") || pathname.getName().endsWith(\".CRT\") ||\n\t\t\t\t\tpathname.getName().endsWith(\".cer\") || pathname.getName().endsWith(\".CER\"));\n\n\t\t}\n\t}\n\n\tprivate class SniKeyManager\n\t\t\textends X509ExtendedKeyManager {\n\n\t\t@Override\n\t\tpublic String[] getClientAliases(String string, Principal[] prncpls) {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic String chooseClientAlias(String[] strings, Principal[] prncpls, Socket socket) {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic String[] getServerAliases(String string, Principal[] prncpls) {\n\t\t\tSet<String> aliases = kmfs.keySet();\n\t\t\treturn aliases.toArray(new String[aliases.size()]);\n\t\t}\n\n\t\t@Override\n\t\tpublic String chooseServerAlias(String string, Principal[] prncpls, Socket socket) {\n\t\t\tif (socket instanceof SSLSocket) {\n\t\t\t\tExtendedSSLSession session = (ExtendedSSLSession) ((SSLSocket) socket).getSession();\n\n\t\t\t\treturn chooseServerAlias(session);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) {\n\t\t\tExtendedSSLSession session = (ExtendedSSLSession) engine.getHandshakeSession();\n\n\t\t\treturn chooseServerAlias(session);\n\t\t}\n\n\t\t/**\n\t\t * Using passed alias method searches for proper KeyManagerFactory to return proper certificate chain for alias\n\t\t */\n\t\t@Override\n\t\tpublic X509Certificate[] getCertificateChain(String alias) {\n\t\t\tKeyManagerFactory kmf;\n\t\t\tif (alias == null) {\n\t\t\t\talias = def_cert_alias;\n\t\t\t}\n\t\t\tkmf = SSLContextContainerAbstract.find(kmfs, alias);\n\t\t\tif (kmf == null) {\n\t\t\t\talias = def_cert_alias;\n\t\t\t\tkmf = SSLContextContainer.find(kmfs, alias);\n\t\t\t}\n\t\t\t// we still don't have kmf so it's unknown domain, we should create and use default\n\t\t\tif (kmf == null) {\n\t\t\t\ttry {\n\t\t\t\t\tkmf = createCertificateKmf(alias);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Failed to create certificate for alias: \" + alias, e);\n\t\t\t\t\t}\n\t\t\t\t\tlog.log(Level.WARNING, \"Failed to create certificate for alias: \" + alias);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn kmf != null ? ((X509KeyManager) kmf.getKeyManagers()[0]).getCertificateChain(alias) : null;\n\t\t}\n\n\t\t/**\n\t\t * Using passed alias method searches for proper KeyManagerFactory to return proper private key for alias\n\t\t */\n\t\t@Override\n\t\tpublic PrivateKey getPrivateKey(String alias) {\n\t\t\tKeyManagerFactory kmf;\n\t\t\tif (alias == null) {\n\t\t\t\talias = def_cert_alias;\n\t\t\t}\n\t\t\tkmf = SSLContextContainerAbstract.find(kmfs, alias);\n\t\t\tif (kmf == null) {\n\t\t\t\talias = def_cert_alias;\n\t\t\t\tkmf = SSLContextContainer.find(kmfs, alias);\n\t\t\t}\n\t\t\t// we still don't have kmf so it's unknown domain, we should create and use default\n\t\t\tif (kmf == null) {\n\t\t\t\ttry {\n\t\t\t\t\tkmf = createCertificateKmf(alias);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Failed to create certificate for alias: \" + alias, e);\n\t\t\t\t\t}\n\t\t\t\t\tlog.log(Level.WARNING, \"Failed to create certificate for alias: \" + alias);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn kmf != null ? ((X509KeyManager) kmf.getKeyManagers()[0]).getPrivateKey(alias) : null;\n\t\t}\n\n\t\t/**\n\t\t * Method retrieves requested server name from ExtendedSSLSession and uses it to return proper alias for server\n\t\t * certificate\n\t\t */\n\t\tprivate String chooseServerAlias(ExtendedSSLSession session) {\n\t\t\t// Pick first SNIHostName in the list of SNI names.\n\t\t\tString hostname = null;\n\t\t\tfor (SNIServerName name : session.getRequestedServerNames()) {\n\t\t\t\tif (name.getType() == StandardConstants.SNI_HOST_NAME) {\n\t\t\t\t\thostname = ((SNIHostName) name).getAsciiName();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If we got given a hostname over SNI, check if we have a cert and\n\t\t\t// key for that hostname. If so, we use it.\n\t\t\t// Otherwise, we fall back to the default certificate.\n\t\t\tif (hostname != null && (getCertificateChain(hostname) != null && getPrivateKey(hostname) != null)) {\n\t\t\t\treturn hostname;\n\t\t\t} else {\n\t\t\t\treturn def_cert_alias;\n\t\t\t}\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/io/CertificateContainerIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.io;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.cert.CertificateEntry;\n\nimport javax.net.ssl.KeyManager;\nimport javax.net.ssl.TrustManager;\nimport java.io.IOException;\nimport java.security.*;\nimport java.security.cert.CertificateException;\nimport java.security.cert.CertificateParsingException;\nimport java.util.Map;\nimport java.util.Objects;\n\n/**\n * Interface implemented by classes responsible for keeping SSL certificates in memory\n * <br>\n * Created by andrzej on 29.02.2016.\n */\npublic interface CertificateContainerIfc {\n\n\tString CERTIFICATE_CONTAINER_CLASS_KEY = \"cert-container-class\";\n\n\tString CERTIFICATE_CONTAINER_CLASS_VAL = CertificateContainer.class.getCanonicalName();\n\n\t/**\n\t * Method <code>addCertificates</code> allows to add more certificates at run time after the container has bee\n\t * already initialized. This is to avoid server restart if there are certificates updates or new certificates for\n\t * new virtual domain. The method should add new certificates or replace existing one if there is already a\n\t * certificate for a domain.\n\t *\n\t * @param params a <code>Map</code> value with configuration parameters.\n\t *\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.4.0\", removeIn = \"9.0.0\", note = \"Method with tigase.io.CertificateContainerIfc.CertificateEntity should be used\")\n\tvoid addCertificates(Map<String, String> params) throws CertificateParsingException;\n\n\tvoid addCertificates(CertificateEntity certificateEntity) throws CertificateParsingException;\n\n\t/**\n\t * Method <code>createCertificate</code> allows to generate self-signed certificate for passed domain name.s\n\t *\n\t * @param domain domain for which certificate should be generated\n\t *\n\t * @return an array of <code>KeyManager</code> containing generated certificate\n\t *\n\t */\n\tKeyManager[] createCertificate(String domain)\n\t\t\tthrows NoSuchAlgorithmException, CertificateException, SignatureException, NoSuchProviderException,\n\t\t\t\t   InvalidKeyException, IOException, UnrecoverableKeyException, KeyStoreException;\n\n\t/**\n\t * Method to retrieve default alias of certificate to use when domain is <code>null</code>\n\t *\n\t * @return default alias\n\t */\n\tString getDefCertAlias();\n\n\tCertificateEntry getCertificateEntry(String hostname);\n\n\t/**\n\t * Method returns array of <code>KeyManager</code> with certificate for domain or <code>null</code> if there is no\n\t * certificate for domain\n\t */\n\tKeyManager[] getKeyManagers(String domain);\n\n\tTrustManager[] getTrustManagers();\n\n\tKeyStore getTrustStore();\n\n\t/**\n\t * Method used to pass parameters to initialize instance of class\n\t *\n\t */\n\tvoid init(Map<String, Object> params);\n\n\trecord CertificateEntity(String certificatePem, String alias, boolean storePermanently, boolean useAsDefault) {\n\n\t\tpublic CertificateEntity {\n\t\t\tObjects.requireNonNull(certificatePem);\n\t\t\tObjects.requireNonNull(alias);\n\t\t}\n\n\t\tCertificateEntity withAlias(String alias) {\n\t\t\treturn new CertificateEntity(certificatePem, alias, storePermanently, useAsDefault);\n\t\t}\n\t\tCertificateEntity withDefaultAlias() {\n\t\t\treturn new CertificateEntity(certificatePem, SSLContextContainerIfc.DEFAULT_DOMAIN_CERT_VAL, storePermanently, useAsDefault);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/io/IOInterface.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.io;\n\nimport tigase.stats.StatisticsList;\n\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.SocketChannel;\n\n/**\n * Describe interface IOInterface here.\n * <br>\n * Created: Sat May 14 08:07:38 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\npublic interface IOInterface {\n\n\tint bytesRead();\n\n\tboolean checkCapabilities(String caps);\n\n\tint getInputPacketSize() throws IOException;\n\n\tSocketChannel getSocketChannel();\n\n\tvoid getStatistics(StatisticsList list, boolean reset);\n\n\tlong getBytesSent(boolean reset);\n\n\tlong getTotalBytesSent();\n\n\tlong getBytesReceived(boolean reset);\n\n\tlong getTotalBytesReceived();\n\n\tlong getBuffOverflow(boolean reset);\n\n\tlong getTotalBuffOverflow();\n\n\tboolean isConnected();\n\n\tboolean isRemoteAddress(String addr);\n\n\tByteBuffer read(final ByteBuffer buff) throws IOException;\n\n\tvoid stop() throws IOException;\n\n\tboolean waitingToSend();\n\n\tint waitingToSendSize();\n\n\tint write(final ByteBuffer buff) throws IOException;\n\n\tvoid setLogId(String logId);\n\n}    // IOInterface\n\n"
  },
  {
    "path": "src/main/java/tigase/io/JcaTLSWrapper.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.io;\n\nimport tigase.cert.CertCheckResult;\nimport tigase.cert.CertificateUtil;\n\nimport javax.net.ssl.*;\nimport javax.net.ssl.SSLEngineResult.HandshakeStatus;\nimport javax.net.ssl.SSLEngineResult.Status;\nimport java.nio.ByteBuffer;\nimport java.security.cert.Certificate;\nimport java.util.Arrays;\nimport java.util.StringJoiner;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Describe class TLSWrapper here.\n * <br>\n * Created: Sat Mar 5 09:13:29 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class JcaTLSWrapper\n\t\timplements TLSWrapper {\n\n\tenum InternalHandshakeStatus {\n\t\thandshaking,\n\t\tfinished,\n\t\tnot_handshaking\n\t}\n\n\tprivate static final Logger log = Logger.getLogger(JcaTLSWrapper.class.getName());\n\n\tprivate int appBuffSize = 0;\n\tprivate String debugId = null;\n\n\t// private String protocol = null;\n\tprivate TLSEventHandler eventHandler = null;\n\tprivate int netBuffSize = 0;\n\tprotected SSLEngine tlsEngine = null;\n\tprivate SSLEngineResult tlsEngineResult = null;\n\tprivate InternalHandshakeStatus handshakeStatus = InternalHandshakeStatus.handshaking;\n\n\tpublic JcaTLSWrapper(SSLContext sslc, TLSEventHandler eventHandler, String hostname, int port,\n\t\t\t\t\t\t final boolean clientMode, final boolean wantClientAuth) {\n\t\tthis(sslc, eventHandler, hostname, port, clientMode, wantClientAuth, false);\n\t}\n\n\t/**\n\t * Creates a new <code>TLSWrapper</code> instance.\n\t *\n\t */\n\tpublic JcaTLSWrapper(SSLContext sslc, TLSEventHandler eventHandler, String hostname, int port,\n\t\t\t\t\t\t final boolean clientMode, final boolean wantClientAuth, final boolean needClientAuth) {\n\t\tthis(sslc, eventHandler, hostname, port, clientMode, wantClientAuth, needClientAuth, null, null);\n\t}\n\n\tpublic JcaTLSWrapper(SSLContext sslc, TLSEventHandler eventHandler, String remote_hostname, int port,\n\t\t\t\t\t\t final boolean clientMode, final boolean wantClientAuth, final boolean needClientAuth,\n\t\t\t\t\t\t String[] enabledCiphers, String[] enabledProtocols) {\n\t\tif (clientMode && remote_hostname != null) {\n\t\t\ttlsEngine = sslc.createSSLEngine(remote_hostname, port);\n\t\t} else {\n\t\t\ttlsEngine = sslc.createSSLEngine();\n\t\t}\n\t\ttlsEngine.setUseClientMode(clientMode);\n\n\t\tif (enabledCiphers != null) {\n\t\t\ttlsEngine.setEnabledCipherSuites(enabledCiphers);\n\t\t}\n\n\t\tif (enabledProtocols != null) {\n\t\t\ttlsEngine.setEnabledProtocols(enabledProtocols);\n\t\t}\n\n\t\tnetBuffSize = tlsEngine.getSession().getPacketBufferSize();\n\t\tappBuffSize = Math.min(eventHandler.getSocketInputSize(), tlsEngine.getSession().getApplicationBufferSize());\n\t\tthis.eventHandler = eventHandler;\n\n\t\tif (!clientMode && wantClientAuth) {\n\t\t\ttlsEngine.setWantClientAuth(true);\n\t\t}\n\t\tif (!clientMode && needClientAuth) {\n\t\t\ttlsEngine.setNeedClientAuth(true);\n\t\t}\n\n\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\tfinal String mode = clientMode ? \"client\" : \"server\";\n\t\t\tfinal String enabledCiphersDebug = tlsEngine.getEnabledCipherSuites() == null\n\t\t\t\t\t\t\t\t\t\t\t   ? \" default\"\n\t\t\t\t\t\t\t\t\t\t\t   : Arrays.toString(tlsEngine.getEnabledCipherSuites());\n\t\t\tfinal String enabledProtocolsDebug = tlsEngine.getEnabledProtocols() == null\n\t\t\t\t\t\t\t\t\t\t\t\t ? \" default\"\n\t\t\t\t\t\t\t\t\t\t\t\t : Arrays.toString(tlsEngine.getEnabledProtocols());\n\t\t\tfinal String sessionCipher =\n\t\t\t\t\ttlsEngine.getSession() == null ? \"n/a\" : tlsEngine.getSession().getCipherSuite();\n\t\t\tlog.log(Level.FINE, \"Created {0} TLSWrapper. Protocols: {1}; Ciphers: {2}; Session cipher: {3}\",\n\t\t\t\t\tnew Object[]{mode, enabledProtocolsDebug, enabledCiphersDebug, sessionCipher});\n\t\t}\n\n\t}\n\n\tprotected void tlsEngineHandshakeCompleted() {\n\t\tif (handshakeStatus == InternalHandshakeStatus.handshaking) {\n\t\t\thandshakeStatus = InternalHandshakeStatus.finished;\n\t\t} else {\n\t\t\tlog.log(Level.FINEST, \"Handshake completed, already reported [{0}]\", new Object[]{debugId});\n\t\t}\n\t}\n\n\t@Override\n\tpublic void notifyIfHandshakeFinished() {\n\t\tif (handshakeStatus == InternalHandshakeStatus.finished) {\n\t\t\thandshakeStatus = InternalHandshakeStatus.not_handshaking;\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tSSLSession session = null;\n\t\t\t\ttry {\n\t\t\t\t\tsession = tlsEngine.getHandshakeSession();\n\t\t\t\t} catch (UnsupportedOperationException ex) {\n\t\t\t\t}\n\t\t\t\tif (session != null && session.isValid()) {\n\t\t\t\t\tlog.log(Level.FINE, \"Handshake completed with: {1}, cipher: {2}, application protocol: {3} [{0}]\",\n\t\t\t\t\t\t\tnew Object[]{debugId, session.getProtocol(), session.getCipherSuite(),\n\t\t\t\t\t\t\t\t\t\t tlsEngine.getApplicationProtocol()});\n\t\t\t\t} else {\n\t\t\t\t\tlog.log(Level.FINEST, \"Handshake completed, but details are unavailable [{0}]\",\n\t\t\t\t\t\t\tnew Object[]{debugId});\n\t\t\t\t}\n\t\t\t}\n\t\t\teventHandler.handshakeCompleted(this);\n\t\t}\n\t}\n\n\t@Override\n\tpublic int bytesConsumed() {\n\t\treturn tlsEngineResult.bytesConsumed();\n\t}\n\n\t@Override\n\tpublic void close() throws SSLException {\n\t\ttlsEngine.closeOutbound();\n\t\ttlsEngine.getSession().invalidate();\n\t\t// tlsEngine.closeInbound();\n\t}\n\n\t@Override\n\tpublic int getAppBuffSize() {\n\t\treturn appBuffSize;\n\t}\n\n\t@Override\n\tpublic CertCheckResult getCertificateStatus(boolean revocationEnabled, SSLContextContainerIfc sslContextContainer) {\n\t\tCertificate[] peerChain = null;\n\n\t\ttry {\n\t\t\tpeerChain = tlsEngine.getSession().getPeerCertificates();\n\t\t} catch (SSLPeerUnverifiedException ex) {\n\n\t\t\t// This normally happens when the peer is in a client mode and does\n\t\t\t// not\n\t\t\t// send any certificate, even though we set: setWantClientAuth(true)\n\t\t\treturn CertCheckResult.none;\n\t\t}\n\n\t\ttry {\n\t\t\treturn CertificateUtil.validateCertificate(peerChain, sslContextContainer.getTrustStore(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t   revocationEnabled);\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.WARNING, \"Problem validating certificate\", ex);\n\t\t}\n\n\t\treturn CertCheckResult.invalid;\n\t}\n\n\t@Override\n\tpublic HandshakeStatus getHandshakeStatus() {\n\t\treturn tlsEngine.getHandshakeStatus();\n\t}\n\n\t@Override\n\tpublic Certificate[] getLocalCertificates() {\n\t\treturn tlsEngine.getSession().getLocalCertificates();\n\t}\n\n\t@Override\n\tpublic int getNetBuffSize() {\n\t\treturn netBuffSize;\n\t}\n\n\t@Override\n\tpublic int getPacketBuffSize() {\n\t\treturn tlsEngine.getSession().getPacketBufferSize();\n\t}\n\n\t@Override\n\tpublic Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {\n\t\treturn tlsEngine.getSession().getPeerCertificates();\n\t}\n\n\t@Override\n\tpublic TLSStatus getStatus() {\n\t\tTLSStatus status = null;\n\n\t\tif ((tlsEngineResult != null) && (tlsEngineResult.getStatus() == Status.BUFFER_UNDERFLOW)) {\n\t\t\tstatus = TLSStatus.UNDERFLOW;\n\n\t\t\t// status = TLSStatus.NEED_READ;\n\t\t} // end of if (tlsEngine.getStatus() == Status.BUFFER_UNDERFLOW)\n\t\telse {\n\t\t\tif ((tlsEngineResult != null) && (tlsEngineResult.getStatus() == Status.CLOSED)) {\n\t\t\t\tstatus = TLSStatus.CLOSED;\n\t\t\t} // end of if (tlsEngine.getStatus() == Status.BUFFER_UNDERFLOW)\n\t\t\telse {\n\t\t\t\tswitch (tlsEngine.getHandshakeStatus()) {\n\t\t\t\t\tcase NEED_WRAP:\n\t\t\t\t\t\tstatus = TLSStatus.NEED_WRITE;\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase NEED_UNWRAP:\n\t\t\t\t\t\tstatus = TLSStatus.NEED_READ;\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tstatus = TLSStatus.OK;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t} // end of switch (tlsEngine.getHandshakeStatus())\n\t\t\t}\n\t\t} // end of else\n\n\t\treturn status;\n\t}\n\n\t@Override\n\tpublic byte[] getTlsUniqueBindingData() {\n\t\t// Because of Java API limitations it always returns null.\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic byte[] getTlsExporterBindingData() {\n\t\t// Because of Java API limitations it always returns null.\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean isClientMode() {\n\t\treturn tlsEngine.getUseClientMode();\n\t}\n\n\t@Override\n\tpublic boolean isNeedClientAuth() {\n\t\treturn tlsEngine.getNeedClientAuth();\n\t}\n\n\t@Override\n\tpublic void setDebugId(String id) {\n\t\tdebugId = id;\n\t}\n\n\t@Override\n\tpublic ByteBuffer unwrap(ByteBuffer net, ByteBuffer app) throws SSLException {\n\t\tByteBuffer out = app;\n\n\t\tout.order(app.order());\n\t\ttlsEngineResult = tlsEngine.unwrap(net, out);\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\"unwrap() tlsEngineResult.getStatus() = {1}, tlsEngineResult.getHandshakeStatus() = {2} [{0}]\",\n\t\t\t\t\tnew Object[]{debugId, tlsEngineResult.getStatus(), tlsEngineResult.getHandshakeStatus()});\n\t\t}\n\n\t\tif (tlsEngineResult.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED) {\n\t\t\tthis.tlsEngineHandshakeCompleted();\n\t\t\tthis.notifyIfHandshakeFinished();\n\t\t}\n\n\t\tif (tlsEngineResult.getStatus() == Status.BUFFER_OVERFLOW) {\n\t\t\tout = resizeApplicationBuffer(net, out);\n\t\t\ttlsEngineResult = tlsEngine.unwrap(net, out);\n\t\t}\n\n\t\tif (tlsEngineResult.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {\n\t\t\tdoTasks();\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"unwrap() doTasks(), handshake: {1} [{0}]\",\n\t\t\t\t\t\tnew Object[]{ debugId, tlsEngine.getHandshakeStatus()});\n\t\t\t}\n\t\t}\n\n\t\treturn out;\n\t}\n\n\t@Override\n\tpublic boolean wantClientAuth() {\n\t\treturn tlsEngine.getWantClientAuth();\n\t}\n\n\t@Override\n\tpublic void wrap(ByteBuffer app, ByteBuffer net) throws SSLException {\n\t\ttlsEngineResult = tlsEngine.wrap(app, net);\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"tlsEngineRsult.getStatus() = {1}, tlsEngineRsult.getHandshakeStatus() = {2} [{0}]\",\n\t\t\t\t\tnew Object[]{debugId, tlsEngineResult.getStatus(), tlsEngineResult.getHandshakeStatus()});\n\t\t}\n\n\t\tif (tlsEngineResult.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED) {\n\t\t\t// this needs to be delayed until after net buffer is added to output queue or we can cause reordering of bytes on the TCP stream\n//\t\t\tif (eventHandler != null) {\n//\t\t\t\teventHandler.handshakeCompleted(this);\n//\t\t\t}\n\t\t\t// so set a flag and check it later on..\n\t\t\tthis.tlsEngineHandshakeCompleted();\n\t\t}\n\n\t\tif (tlsEngineResult.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {\n\t\t\tdoTasks();\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"doTasks(): {1} [{0}]\", new Object[]{debugId, tlsEngine.getHandshakeStatus()});\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void doTasks() {\n\t\tRunnable runnable = null;\n\n\t\twhile ((runnable = tlsEngine.getDelegatedTask()) != null) {\n\t\t\trunnable.run();\n\t\t} // end of while ((runnable = engine.getDelegatedTask()) != 0)\n\t}\n\n\t/**\n\t * Method <code>resizeApplicationBuffer</code> is used to perform buffer resizing\n\t */\n\tprivate ByteBuffer resizeApplicationBuffer(ByteBuffer net, ByteBuffer app) {\n\n\t\t// if (appBuffSize > app.remaining()) {\n\t\t// if (net.remaining() > app.remaining()) {\n\t\t// if (appBuffSize > app.capacity() - app.remaining()) {\n\t\t// if (log.isLoggable(Level.FINE)) {\n\t\t// log.fine(\"Resizing tlsInput to \" + (appBuffSize + app.capacity()) +\n\t\t// \" bytes.\");\n\t\t// }\n\t\t//\n\t\t// ByteBuffer bb = ByteBuffer.allocate(app.capacity() + appBuffSize);\n\t\tint newSize = app.capacity() * 2;\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.log(Level.FINER, \"Resizing tlsInput to {1} bytes [{0}]\", new Object[]{debugId, newSize});\n\t\t}\n\n\t\tByteBuffer bb = ByteBuffer.allocate(newSize);\n\n\t\t// bb.clear();\n\t\tbb.order(app.order());\n\t\tapp.flip();\n\t\tbb.put(app);\n\n\t\treturn bb;\n\n\t\t// } else {\n\t\t//\n\t\t// return app;\n\t\t// } // end of else\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tfinal StringJoiner joiner = new StringJoiner(\", \", JcaTLSWrapper.class.getSimpleName() + \"[\", \"]\")\n//\t\t\t\t.add(\"appBuffSize=\" + appBuffSize)\n//\t\t\t\t.add(\"netBuffSize=\" + netBuffSize)\n\t\t\t\t.add(\"WrapperStatus = \" + getStatus());\n//\t\t\t\t.add(\"CipherSuite = \" + tlsEngine.getSession().getCipherSuite())\n\t\tif (tlsEngineResult != null) {\n\t\t\tjoiner\n\t\t\t\t.add(\"TLSEngineStatus = \" + tlsEngineResult.getStatus())\n\t\t\t\t.add(\"HandshakeStatus = \" + tlsEngineResult.getHandshakeStatus());\n\t\t}\n\t\treturn joiner.toString();\n\t}\n}"
  },
  {
    "path": "src/main/java/tigase/io/ProxyIO.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.io;\n\nimport tigase.stats.StatisticsList;\n\nimport java.io.IOException;\nimport java.net.*;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.SocketChannel;\nimport java.nio.charset.StandardCharsets;\nimport java.util.function.BiConsumer;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.IntStream;\n\nimport static tigase.util.Algorithms.bytesToHex;\n\n/**\n * Implementation of Proxy (v1 and v2) protocol decoding to obtain remote client IP address\n */\npublic class ProxyIO implements IOInterface {\n\n\tprivate static final Logger log = Logger.getLogger(ProxyIO.class.getName());\n\n\tprivate static final byte[] SIGNATURE_1 = \"PROXY\".getBytes(StandardCharsets.UTF_8);\n\tprivate static final byte[] SIGNATURE_2 = new byte[] { 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A };\n\n\tprivate enum State {\n\t\tNEW,\n\t\tPROXY_1,\n\t\tPROXY_2,\n\t\tDONE\n\t}\n\n\tenum Family {\n\t\tUNSPECIFIED(-1),\n\t\tINET(4),\n\t\tINET6(16),\n\t\tUNIX(108);\n\n\t\tprivate int addrLen;\n\n\t\tprivate\tFamily(int addrLen) {\n\t\t\tthis.addrLen = addrLen;\n\t\t}\n\n\t\tpublic int getAddressLength() {\n\t\t\treturn addrLen;\n\t\t}\n\n\t\tpublic InetAddress getByAddress(byte[] addr) throws IOException {\n\t\t\treturn switch (this) {\n\t\t\t\tcase INET -> InetAddress.getByAddress(addr);\n\t\t\t\tcase INET6 -> Inet6Address.getByAddress(addr);\n\t\t\t\tdefault -> throw new IOException(\"Unsupported socket address\");\n\t\t\t};\n\t\t}\n\t}\n\tenum Transport {\n\t\tUNSPECIFIED,\n\t\tSTREAM,\n\t\tDATAGRAM\n\t}\n\n\tprivate static final int PROXY_1_FIELD_PROTOCOL = 1;\n\tprivate static final int PROXY_1_FIELD_SRC_IP = 2;\n\tprivate static final int PROXY_1_FIELD_DST_IP = 3;\n\tprivate static final int PROXY_1_FIELD_SRC_PORT = 4;\n\tprivate static final int PROXY_1_FIELD_DST_PORT = 5;\n\n\tprivate final IOInterface io;\n\tprivate State state = State.NEW;\n\tprivate byte[] partialData = null;\n\tprivate BiConsumer<String,String> addressConsumer = null;\n\n\t// Consumer<DestinationAddress,SourceAddress> for incoming connections\n\tpublic ProxyIO(IOInterface io, BiConsumer<String,String> addressConsumer) {\n\t\tthis.io = io;\n\t\tthis.partialData = null;\n\t\tthis.addressConsumer = addressConsumer;\n\t}\n\n\t@Override\n\tpublic int bytesRead() {\n\t\treturn io.bytesRead();\n\t}\n\n\t@Override\n\tpublic boolean checkCapabilities(String caps) {\n\t\treturn caps.contains(\"PROXY\") || io.checkCapabilities(caps);\n\t}\n\n\t@Override\n\tpublic int getInputPacketSize() throws IOException {\n\t\treturn io.getInputPacketSize();\n\t}\n\n\t@Override\n\tpublic SocketChannel getSocketChannel() {\n\t\treturn io.getSocketChannel();\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list, boolean reset) {\n\t\tio.getStatistics(list, reset);\n\t}\n\n\t@Override\n\tpublic long getBytesSent(boolean reset) {\n\t\treturn io.getBytesSent(reset);\n\t}\n\n\t@Override\n\tpublic long getTotalBytesSent() {\n\t\treturn io.getTotalBytesSent();\n\t}\n\n\t@Override\n\tpublic long getBytesReceived(boolean reset) {\n\t\treturn io.getBytesReceived(reset);\n\t}\n\n\t@Override\n\tpublic long getTotalBytesReceived() {\n\t\treturn io.getTotalBytesReceived();\n\t}\n\n\t@Override\n\tpublic long getBuffOverflow(boolean reset) {\n\t\treturn io.getBuffOverflow(reset);\n\t}\n\n\t@Override\n\tpublic long getTotalBuffOverflow() {\n\t\treturn io.getTotalBuffOverflow();\n\t}\n\n\t@Override\n\tpublic boolean isConnected() {\n\t\treturn io.isConnected();\n\t}\n\n\t@Override\n\tpublic boolean isRemoteAddress(String addr) {\n\t\treturn io.isRemoteAddress(addr);\n\t}\n\n\t@Override\n\tpublic ByteBuffer read(ByteBuffer buff) throws IOException {\n\t\tif (state != State.DONE) {\n\t\t\tByteBuffer tmpBuffer = io.read(buff);\n\t\t\tByteBuffer buf;\n\t\t\tif (partialData == null) {\n\t\t\t\tbuf = tmpBuffer;\n\t\t\t} else {\n\t\t\t\tbuf = ByteBuffer.allocate(partialData.length + tmpBuffer.remaining());\n\t\t\t\tbuf.put(partialData);\n\t\t\t\tbuf.put(tmpBuffer);\n\t\t\t\tbuf.flip();\n\t\t\t\ttmpBuffer.clear();\n\t\t\t\tpartialData = null;\n\t\t\t}\n\n\t\t\tbuf.mark();\n\t\t\tif (state == State.NEW) {\n\t\t\t\tboolean proxy1Checked = false;\n\t\t\t\tboolean proxy2Checked = false;\n\t\t\t\tif (buf.remaining() >= SIGNATURE_1.length) {\n\t\t\t\t\tproxy1Checked = true;\n\t\t\t\t\tif (IntStream.range(0, SIGNATURE_1.length).allMatch(i -> buf.get(i) == SIGNATURE_1[i])) {\n\t\t\t\t\t\tstate = State.PROXY_1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (buf.remaining() >= SIGNATURE_2.length) {\n\t\t\t\t\tproxy2Checked = true;\n\t\t\t\t\tif (IntStream.range(0, SIGNATURE_2.length).allMatch(i -> buf.get(i) == SIGNATURE_2[i])) {\n\t\t\t\t\t\tstate = State.PROXY_2;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (proxy1Checked && proxy2Checked) {\n\t\t\t\t\tif (state == State.NEW) {\n\t\t\t\t\t\tstate = State.DONE;\n\t\t\t\t\t\treturn buf;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tpartialData = new byte[buf.remaining()];\n\t\t\t\t\tbuf.get(partialData);\n\t\t\t\t\treturn tmpBuffer;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn switch (state) {\n\t\t\t\tcase PROXY_1 ->  {\n\t\t\t\t\tString[] fields = new String[6];\n\t\t\t\t\tint fieldIndex = 0;\n\t\t\t\t\tStringBuilder builder = new StringBuilder();\n\t\t\t\t\tboolean awaitLineFeed = false;\n\t\t\t\t\twhile (buf.hasRemaining()) {\n\t\t\t\t\t\tbyte b = buf.get();\n\t\t\t\t\t\tif (!awaitLineFeed) {\n\t\t\t\t\t\t\tif (b == ' ' || b == '\\r') {\n\t\t\t\t\t\t\t\tfields[fieldIndex] = builder.toString();\n\t\t\t\t\t\t\t\tfieldIndex++;\n\t\t\t\t\t\t\t\tbuilder.setLength(0);\n\t\t\t\t\t\t\t\tif (b == '\\r') {\n\t\t\t\t\t\t\t\t\tawaitLineFeed = true;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else if (b < ' ') {\n\t\t\t\t\t\t\t\tstate = State.DONE;\n\t\t\t\t\t\t\t\tbuf.reset();\n\t\t\t\t\t\t\t\tyield buf;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tbuilder.append((char) b);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (b == '\\n') {\n\t\t\t\t\t\t\t\t// upgrade state!!\n\t\t\t\t\t\t\t\tif (!\"PROXY\".equals(fields[0])) {\n\t\t\t\t\t\t\t\t\tlog.log(Level.FINE, () -> \"Not a PROXY protocol!\");\n\t\t\t\t\t\t\t\t\tstate = State.DONE;\n\t\t\t\t\t\t\t\t\tbuf.reset();\n\t\t\t\t\t\t\t\t\tyield buf;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (\"UNKNOWN\".equals(fields[PROXY_1_FIELD_PROTOCOL])) {\n\t\t\t\t\t\t\t\t\tstate = State.DONE;\n\t\t\t\t\t\t\t\t\tyield buf;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (addressConsumer != null) {\n\t\t\t\t\t\t\t\t\taddressConsumer.accept(fields[PROXY_1_FIELD_DST_IP], fields[PROXY_1_FIELD_SRC_IP]);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tyield buf;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tbuf.reset();\n\t\t\t\t\t\t\t\tpartialData = new byte[buf.remaining()];\n\t\t\t\t\t\t\t\tbuf.get(partialData);\n\t\t\t\t\t\t\t\tyield tmpBuffer;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// PROXY1 had incomplete data\n\t\t\t\t\tbuf.reset();\n\t\t\t\t\tpartialData = new byte[buf.remaining()];\n\t\t\t\t\tbuf.get(partialData);\n\t\t\t\t\tyield tmpBuffer;\n\t\t\t\t}\n\n\t\t\t\tcase PROXY_2 -> {\n\t\t\t\t\tif (proxy2Header == null) {\n\t\t\t\t\t\tbuf.position(buf.position() + SIGNATURE_2.length);\n\t\t\t\t\t\tif (buf.remaining() < 4) {\n\t\t\t\t\t\t\tbuf.reset();\n\t\t\t\t\t\t\tpartialData = new byte[buf.remaining()];\n\t\t\t\t\t\t\tbuf.get(partialData);\n\t\t\t\t\t\t\tyield tmpBuffer;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tint verAndCmd = 0xFF & buf.get();\n\t\t\t\t\t\tif ((verAndCmd & 0xF0) != 0x20) {\n\t\t\t\t\t\t\tlog.log(Level.FINE, () -> \"Bad Proxy v2 version\");\n\t\t\t\t\t\t\tstate = State.DONE;\n\t\t\t\t\t\t\tbuf.reset();\n\t\t\t\t\t\t\tyield buf;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tboolean isLocal = (verAndCmd & 0xF0) == 0x00;\n\n\t\t\t\t\t\tint transportAndFamily = 0xFF & buf.get();\n\t\t\t\t\t\tFamily family = switch (transportAndFamily >> 4) {\n\t\t\t\t\t\t\tcase 0 -> Family.UNSPECIFIED;\n\t\t\t\t\t\t\tcase 1 -> Family.INET;\n\t\t\t\t\t\t\tcase 2 -> Family.INET6;\n\t\t\t\t\t\t\tcase 3 -> Family.UNIX;\n\t\t\t\t\t\t\tdefault -> throw new IOException(\"Bad Proxy Family value\");\n\t\t\t\t\t\t};\n\t\t\t\t\t\tTransport transport = switch (transportAndFamily & 0xF) {\n\t\t\t\t\t\t\tcase 0 -> Transport.UNSPECIFIED;\n\t\t\t\t\t\t\tcase 1 -> Transport.STREAM;\n\t\t\t\t\t\t\tcase 2 -> Transport.DATAGRAM;\n\t\t\t\t\t\t\tdefault -> throw new IOException(\"Bad Proxy Transport value\");\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tif (!isLocal && (family == Family.UNSPECIFIED || transport != Transport.STREAM)) {\n\t\t\t\t\t\t\tthrow new IOException(\"Unsupported Proxy mode\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tint length = buf.getChar();\n\t\t\t\t\t\tif (length > 1024) {\n\t\t\t\t\t\t\tthrow new IOException(\"Unsupported Proxy header length - too long\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tproxy2Header = new Proxy2Header(transport, family, isLocal, length);\n\t\t\t\t\t\tbuf.mark();\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif (buf.remaining() < proxy2Header.length) {\n\t\t\t\t\t\tbuf.reset();\n\t\t\t\t\t\tpartialData = new byte[buf.remaining()];\n\t\t\t\t\t\tbuf.get(partialData);\n\t\t\t\t\t\tyield tmpBuffer;\n\t\t\t\t\t}\n\n\t\t\t\t\tint nonProxyRemaining = buf.remaining() - proxy2Header.length;\n\t\t\t\t\tif (proxy2Header.isLocal) {\n\t\t\t\t\t\tbuf.position(buf.position() + proxy2Header.length);\n\t\t\t\t\t\t// no content for local and remote address\n\t\t\t\t\t} else {\n\t\t\t\t\t\tint dataLength = proxy2Header.family.getAddressLength();\n\t\t\t\t\t\tif (dataLength < 0) {\n\t\t\t\t\t\t\tthrow new IOException(\"Unsupported socket address\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbyte[] data = new byte[dataLength];\n\t\t\t\t\t\tbuf.get(data);\n\t\t\t\t\t\tInetAddress srcAddr = proxy2Header.family.getByAddress(data);\n\t\t\t\t\t\tbuf.get(data);\n\t\t\t\t\t\tInetAddress dstAddr = proxy2Header.family.getByAddress(data);\n\t\t\t\t\t\tint srcPort = buf.getChar();\n\t\t\t\t\t\tint dstPort = buf.getChar();\n\n\t\t\t\t\t\tInetSocketAddress local = new InetSocketAddress(dstAddr, dstPort);\n\t\t\t\t\t\tInetSocketAddress remote = new InetSocketAddress(srcAddr, srcPort);\n\n\t\t\t\t\t\tif (addressConsumer != null) {\n\t\t\t\t\t\t\taddressConsumer.accept(local.getHostString(), remote.getHostString());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\twhile (buf.remaining() > nonProxyRemaining) {\n\t\t\t\t\t\tint type = 0xff & buf.get();\n\t\t\t\t\t\tint length = buf.getChar();\n\t\t\t\t\t\tbyte[] value = new byte[length];\n\t\t\t\t\t\tbuf.get(value);\n\n\t\t\t\t\t\tlog.log(Level.FINEST, () -> String.format(\"Proxy v2 T=%x L=%d V=%s for %s\", type, length, bytesToHex(value), this));\n\t\t\t\t\t}\n\n\t\t\t\t\tstate = State.DONE;\n\t\t\t\t\tyield buf;\n\t\t\t\t}\n\n\t\t\t\tdefault -> {\n\t\t\t\t\tyield buf;\n\t\t\t\t}\n\t\t\t};\n\t\t} else {\n\t\t\treturn io.read(buff);\n\t\t}\n\t}\n\n\tprivate Proxy2Header proxy2Header;\n\n\trecord Proxy2Header(Transport transport, Family family, boolean isLocal, int length) {\n\t\t\n\t}\n\t\n\t@Override\n\tpublic void stop() throws IOException {\n\t\tio.stop();\n\t}\n\n\t@Override\n\tpublic boolean waitingToSend() {\n\t\treturn io.waitingToSend();\n\t}\n\n\t@Override\n\tpublic int waitingToSendSize() {\n\t\treturn io.waitingToSendSize();\n\t}\n\n\t@Override\n\tpublic int write(ByteBuffer buff) throws IOException {\n\t\treturn io.write(buff);\n\t}\n\n\t@Override\n\tpublic void setLogId(String logId) {\n\t\tio.setLogId(logId);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/io/SSLContextContainer.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.io;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.cert.CertificateUtil;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.eventbus.HandleEvent;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Command;\nimport tigase.server.ConnectionManager;\nimport tigase.server.DataForm;\nimport tigase.server.Packet;\nimport tigase.vhosts.*;\nimport tigase.xml.Element;\n\nimport javax.net.ssl.SSLContext;\nimport javax.net.ssl.SSLEngine;\nimport javax.net.ssl.TrustManager;\nimport java.io.IOException;\nimport java.nio.ByteOrder;\nimport java.security.KeyStore;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.cert.CertificateException;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentSkipListMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * Created: Oct 15, 2010 2:40:49 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\n@Bean(name = \"sslContextContainer\", parent = ConnectionManager.class, active = true)\npublic class SSLContextContainer\n\t\textends SSLContextContainerAbstract\n\t\timplements Initializable {\n\n\t// Workaround for TLS/SSL bug in new JDK used with new version of\n\t// nss library see also:\n\t// http://stackoverflow.com/q/10687200/427545\n\t// http://bugs.sun.com/bugdatabase/view_bug.do;jsessionid=b509d9cb5d8164d90e6731f5fc44?bug_id=6928796\n\t/* @formatter:off */\n\tprivate static final String EPHEMERAL_DH_KEYSIZE_KEY = \"jdk.tls.ephemeralDHKeySize\";\n\tprivate static final int EPHEMERAL_DH_KEYSIZE_VALUE = 4096;\n\tprivate static final String REJECT_CLIENT_INITIATED_RENEGOTIATION_KEY = \"jdk.tls.rejectClientInitiatedRenegotiation\";\n\tprivate static final String MAX_TLS_HANDSHAKE_MESSAGE_SIZE_KEY = \"jdk.tls.maxHandshakeMessageSize\";\n\tprivate static final int MAX_TLS_HANDSHAKE_MESSAGE_SIZE_VALUE = (int)Math.pow(2, 16);\n\tprivate static final String[] TLS_WORKAROUND_CIPHERS = new String[]{\"SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"SSL_DHE_DSS_WITH_DES_CBC_SHA\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"SSL_DHE_RSA_WITH_DES_CBC_SHA\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"SSL_RSA_EXPORT_WITH_DES40_CBC_SHA\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"SSL_RSA_EXPORT_WITH_RC4_40_MD5\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"SSL_RSA_WITH_3DES_EDE_CBC_SHA\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"SSL_RSA_WITH_DES_CBC_SHA\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"SSL_RSA_WITH_RC4_128_MD5\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"SSL_RSA_WITH_RC4_128_SHA\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"TLS_DHE_DSS_WITH_AES_128_CBC_SHA\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"TLS_DHE_RSA_WITH_AES_128_CBC_SHA\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"TLS_EMPTY_RENEGOTIATION_INFO_SCSV\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"TLS_RSA_WITH_AES_128_CBC_SHA\"};\n\t/* @formatter:on */\n\n\tprivate static final String[] HARDENED_SECURE_FORBIDDEN_CIPHERS = new String[]{\"^.*(_(MD5|SHA1)$|RC4_.*$)\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"^(TLS_RSA_WITH_AES.*$)\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"^.*_CBC_.*$\"};\n\tprivate static final String[] HARDENED_STRICT_FORBIDDEN_CIPHERS = new String[]{\"^.*_AES_128_.*$\", \"^.*_CBC_.*$\"};\n\n\tprivate static final String[] HARDENED_SECURE_FORBIDDEN_PROTOCOLS = new String[]{\"SSL\", \"SSLv2\", \"SSLv3\"};\n\tprivate static final String[] HARDENED_STRICT_FORBIDDEN_PROTOCOLS = new String[]{\"SSLv2Hello\", \"TLSv1\", \"TLSv1.1\"};\n\tprivate static final Logger log = Logger.getLogger(SSLContextContainer.class.getName());\n\n\tpublic enum HARDENED_MODE {\n\t\tglobal(),\n\t\trelaxed(),\n\t\tsecure(),\n\t\tstrict();\n\n\t\tpublic static HARDENED_MODE getDefault() {\n\t\t\treturn secure;\n\t\t}\n\n\t\tstatic String[] stringValues() {\n\t\t\treturn EnumSet.allOf(HARDENED_MODE.class).stream().map(HARDENED_MODE::name).toArray(String[]::new);\n\t\t}\n\t}\n\n\t@Inject\n\tprotected EventBus eventBus = EventBusFactory.getInstance();\n\tprotected Map<String, SSLHolder> sslContexts = new ConcurrentSkipListMap<>();\n\t@Inject(nullAllowed = true)\n\tprotected VHostManagerIfc vHostManager = null;\n\tMap<String, String[]> enabledCiphersMap = new ConcurrentHashMap<>(3);\n\tMap<String, String[]> enabledProtocolsMap = new ConcurrentHashMap<>(6);\n\t@TigaseDeprecated(since = \"8.1.0\", removeIn = \"9.0.0\", note = \"(temporarily) disable TLS 1.3 due to compatibility issues\")\n\t@Deprecated\n\t@ConfigField(desc = \"Disable TLS 1.3\", alias = \"tls-disable-tls13\")\n\tprivate boolean disableTLS13 = false;\n\t@ConfigField(desc = \"Disabled TLS/SSL ciphers\", alias = \"tls-disabled-ciphers\")\n\tprivate String[] disabledCiphers;\n\t@ConfigField(desc = \"Disabled TLS/SSL protocols\", alias = \"tls-disabled-protocols\")\n\tprivate String[] disabledProtocols;\n\t@ConfigField(desc = \"Enabled TLS/SSL ciphers\", alias = \"tls-enabled-ciphers\")\n\t@TigaseDeprecated(since = \"8.1.0\", removeIn = \"9.0.0\", note = \"Control list of ciphers with `tls-disabled-ciphers`\")\n\t@Deprecated\n\tprivate String[] enabledCiphers;\n\t@TigaseDeprecated(since = \"8.1.0\", removeIn = \"9.0.0\", note = \"Control list of protocols with `tls-disabled-protocols`\")\n\t@Deprecated\n\t@ConfigField(desc = \"Enabled TLS/SSL protocols\", alias = \"tls-enabled-protocols\")\n\tprivate String[] enabledProtocols;\n\t@ConfigField(desc = \"Sets ephemeral DH Key Size\", alias = \"ephemeral-key-size\")\n\tprivate int ephemeralDHKeySize = EPHEMERAL_DH_KEYSIZE_VALUE;\n\t@ConfigField(desc = \"Sets max Handshake Message Size\", alias = \"max-handshake-message-size\")\n\tprivate int maxHandshakeMessageSize = MAX_TLS_HANDSHAKE_MESSAGE_SIZE_VALUE;\n\t@ConfigField(desc = \"TLS/SSL hardened mode\", alias = \"hardened-mode\")\n\tprivate HARDENED_MODE hardenedMode = HARDENED_MODE.secure;\n\t@Inject(bean = \"rootSslContextContainer\", type = Root.class, nullAllowed = true)\n\tprivate SSLContextContainerIfc parent;\n\t@ConfigField(desc = \"TLS/SSL\", alias = \"tls-jdk-nss-bug-workaround-active\")\n\tprivate boolean tlsJdkNssBugWorkaround = false;\n\n\tprivate static String getKey(SSLContextContainer.HARDENED_MODE mode, boolean client) {\n\t\treturn mode + (client ? \"_client\" : \"\");\n\t}\n\n\tprivate static String markEnabled(String[] enabled, String[] supported) {\n\t\tfinal List<String> en = enabled == null ? new ArrayList<String>() : Arrays.asList(enabled);\n\t\tString result = \"\";\n\n\t\tif (supported != null) {\n\t\t\tfor (int i = 0; i < supported.length; i++) {\n\t\t\t\tString t = supported[i];\n\t\t\t\tresult += (en.contains(t) ? \"(+)\" : \"(-)\");\n\t\t\t\tresult += t;\n\t\t\t\tif (i + 1 < supported.length) {\n\t\t\t\t\tresult += \",\";\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate static String[] subtractItemsFromCollection(String[] input, String[] itemsToRemove) {\n\t\treturn Arrays.stream(input)\n\t\t\t\t.filter(c -> !Arrays.stream(itemsToRemove)\n\t\t\t\t\t\t.map(Pattern::compile)\n\t\t\t\t\t\t.map(pat -> pat.matcher(c))\n\t\t\t\t\t\t.map(Matcher::matches)\n\t\t\t\t\t\t.anyMatch(e -> e))\n\t\t\t\t.toArray(String[]::new);\n\t}\n\n\t/**\n\t * Constructor for bean only\n\t */\n\tpublic SSLContextContainer() {\n\t\tthis(null, null);\n\t}\n\n\t/**\n\t * Constructor used to create root SSLContextContainer instance which should cache only SSLContext instances where\n\t * array of TrustManagers is not set - common for all ConnectionManagers. This instance is kept by TLSUtil class.\n\t */\n\tpublic SSLContextContainer(CertificateContainerIfc certContainer) {\n\t\tthis(certContainer, null);\n\t}\n\n\t/**\n\t * Constructor used to create instances for every ConnectionManager so that every connection manager can have\n\t * different TrustManagers and SSLContext instance will still be cached.\n\t */\n\tpublic SSLContextContainer(CertificateContainerIfc certContainer, SSLContextContainerIfc parent) {\n\t\tsuper(certContainer);\n\t\tthis.parent = parent;\n\t}\n\n\t@Override\n\tpublic IOInterface createIoInterface(String protocol, String local_hostname, String remote_hostname, int port,\n\t\t\t\t\t\t\t\t\t\t boolean clientMode, boolean wantClientAuth, boolean needClientAuth,\n\t\t\t\t\t\t\t\t\t\t ByteOrder byteOrder, TrustManager[] x509TrustManagers,\n\t\t\t\t\t\t\t\t\t\t TLSEventHandler eventHandler, IOInterface socketIO,\n\t\t\t\t\t\t\t\t\t\t CertificateContainerIfc certificateContainer) throws IOException {\n\t\tSSLContext sslContext = getSSLContext(protocol, local_hostname, clientMode, x509TrustManagers);\n\t\tTLSWrapper wrapper = new JcaTLSWrapper(sslContext, eventHandler, remote_hostname, port, clientMode,\n\t\t\t\t\t\t\t\t\t\t\t   wantClientAuth, needClientAuth, getEnabledCiphers(local_hostname),\n\t\t\t\t\t\t\t\t\t\t\t   getEnabledProtocols(local_hostname, clientMode));\n\t\treturn new TLSIO(socketIO, wrapper, byteOrder);\n\t}\n\n\t@Override\n\tpublic String[] getEnabledCiphers(String domain) {\n\t\tif (enabledCiphers != null && enabledCiphers.length != 0) {\n\t\t\treturn enabledCiphers;\n\t\t} else if (tlsJdkNssBugWorkaround) {\n\t\t\treturn TLS_WORKAROUND_CIPHERS;\n\t\t} else {\n\t\t\tHARDENED_MODE mode = getHardenedMode(domain);\n\t\t\tString key = getKey(mode, false);\n\t\t\treturn enabledCiphersMap.get(key);\n\t\t}\n\t}\n\n\tpublic void setEnabledCiphers(String[] enabledCiphers) {\n\t\tif (log.isLoggable(Level.CONFIG)) {\n\t\t\tlog.config(\"Enabled ciphers: \" + (enabledCiphers == null ? \"default\" : Arrays.toString(enabledCiphers)));\n\t\t}\n\t\tthis.enabledCiphers = enabledCiphers;\n\t}\n\n\t@Override\n\tpublic String[] getEnabledProtocols(String domain, boolean client) {\n\t\tif (enabledProtocols != null && enabledProtocols.length != 0) {\n\t\t\treturn enabledProtocols;\n\t\t} else {\n\t\t\tHARDENED_MODE mode = getHardenedMode(domain);\n\t\t\tString key = getKey(mode, client);\n\t\t\treturn enabledProtocolsMap.get(key);\n\t\t}\n\t}\n\n\tpublic void setEnabledProtocols(String[] enabledProtocols) {\n\t\tif (log.isLoggable(Level.CONFIG)) {\n\t\t\tlog.config(\n\t\t\t\t\t\"Enabled protocols: \" + (enabledProtocols == null ? \"default\" : Arrays.toString(enabledProtocols)));\n\t\t}\n\t\tthis.enabledProtocols = enabledProtocols;\n\t}\n\n\tpublic void setEphemeralDHKeySize(int ephemeralDHKeySize) {\n\t\tthis.ephemeralDHKeySize = ephemeralDHKeySize;\n\t}\n\n\t@Override\n\tpublic SSLContext getSSLContext(String protocol, String hostname, boolean clientMode, TrustManager[] tms) {\n\t\tSSLHolder holder = null;\n\n\t\tString alias = hostname;\n\n\t\ttry {\n\t\t\tif (tms == null) {\n\t\t\t\tif (parent != null) {\n\t\t\t\t\treturn parent.getSSLContext(protocol, hostname, clientMode, tms);\n\t\t\t\t} else {\n\t\t\t\t\ttms = getTrustManagers();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (alias == null) {\n\t\t\t\talias = getDefCertAlias();\n\t\t\t}\n\n\t\t\tholder = find(sslContexts, alias);\n\n\t\t\tif (!validateDomainCertificate(holder, alias)) {\n\t\t\t\tholder = null;\n\t\t\t}\n\n\t\t\tif (holder == null || !holder.isValid(tms)) {\n\t\t\t\tholder = createContextHolder(protocol, hostname, alias, clientMode, tms);\n\t\t\t\tif (clientMode) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Using SSLHolder: \" + holder);\n\t\t\t\t\t}\n\t\t\t\t\treturn holder.getSSLContext();\n\t\t\t\t}\n\n\t\t\t\tif (!validateDomainCertificate(holder, alias)) {\n\t\t\t\t\tholder = createContextHolder(protocol, hostname, alias, clientMode, tms);\n\t\t\t\t}\n\n\t\t\t\tsslContexts.put(alias, holder);\n\t\t\t}\n\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.SEVERE, \"Can not initialize SSLContext for domain: \" + alias + \", protocol: \" + protocol, e);\n\t\t\tholder = null;\n\t\t}\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Using SSLHolder: \" + holder);\n\t\t}\n\t\treturn holder != null ? holder.getSSLContext() : null;\n\t}\n\n\t@Override\n\tpublic KeyStore getTrustStore() {\n\t\tKeyStore trustStore = super.getTrustStore();\n\t\tif (trustStore == null && parent != null) {\n\t\t\ttrustStore = parent.getTrustStore();\n\t\t}\n\t\treturn trustStore;\n\t}\n\n\tpublic void setHardenedMode(HARDENED_MODE hardenedMode) {\n\t\tthis.hardenedMode = hardenedMode;\n\t\t// Don't clear DH key size property as it's global property and affects all connections.\n//\t\tif (HARDENED_MODE.relaxed.equals(hardenedMode)) {\n//\t\t\tSystem.clearProperty(EPHEMERAL_DH_KEYSIZE_KEY);\n//\t\t}\n\t}\n\n\n\tpublic void setParent(SSLContextContainerIfc parent) {\n\t\tlog.log(Level.FINE, \"setting root = \" + parent);\n\t\tthis.parent = parent;\n\t}\n\n\tpublic void setTlsJdkNssBugWorkaround(boolean value) {\n\t\tif (log.isLoggable(Level.CONFIG)) {\n\t\t\tlog.config(\"Workaround for TLS/SSL bug is \" + (value ? \"enabled\" : \"disabled\"));\n\t\t}\n\t\tthis.tlsJdkNssBugWorkaround = value;\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tSystem.setProperty(EPHEMERAL_DH_KEYSIZE_KEY, String.valueOf(ephemeralDHKeySize));\n\t\tSystem.setProperty(MAX_TLS_HANDSHAKE_MESSAGE_SIZE_KEY, String.valueOf(maxHandshakeMessageSize));\n\t\tSystem.setProperty(REJECT_CLIENT_INITIATED_RENEGOTIATION_KEY, String.valueOf(true));\n\t\ttry {\n\t\t\tfinal SSLContext sslContext = SSLContext.getDefault();\n\t\t\tSSLEngine tmpEngine = sslContext.createSSLEngine();\n\t\t\ttmpEngine.setUseClientMode(false);\n\t\t\tlog.config(\"Supported protocols: \" +\n\t\t\t\t\t\t\t   markEnabled(tmpEngine.getEnabledProtocols(), tmpEngine.getSupportedProtocols()));\n\t\t\tlog.config(\"Supported ciphers: \" +\n\t\t\t\t\t\t\t   markEnabled(tmpEngine.getEnabledCipherSuites(), tmpEngine.getSupportedCipherSuites()));\n\n\t\t\tString[] TMP;\n\t\t\t// TODO: we should temporarily disable TLS1.3 as an option?\n\t\t\tif (disableTLS13) {\n\t\t\t\tTMP = subtractItemsFromCollection(tmpEngine.getEnabledProtocols(), new String[]{\"TLSv1.3\"});\n\t\t\t} else {\n\t\t\t\tTMP = tmpEngine.getEnabledProtocols();\n\t\t\t}\n\n\t\t\tif (disabledProtocols != null && disabledProtocols.length > 0) {\n\t\t\t\tTMP = subtractItemsFromCollection(TMP, disabledProtocols);\n\t\t\t}\n\n\t\t\tlog.config(\"RELAXED protocols: \" + Arrays.toString(TMP));\n\t\t\tenabledProtocolsMap.put(getKey(HARDENED_MODE.relaxed, false), TMP);\n\t\t\tenabledProtocolsMap.put(getKey(HARDENED_MODE.relaxed, true),\n\t\t\t\t\t\t\t\t\tsubtractItemsFromCollection(TMP, new String[]{\"SSLv2Hello\"}));\n\n\t\t\tTMP = subtractItemsFromCollection(TMP, HARDENED_SECURE_FORBIDDEN_PROTOCOLS);\n\t\t\tlog.config(\"SECURE protocols: \" + Arrays.toString(TMP));\n\t\t\tenabledProtocolsMap.put(getKey(HARDENED_MODE.secure, false), TMP);\n\t\t\tenabledProtocolsMap.put(getKey(HARDENED_MODE.secure, true),\n\t\t\t\t\t\t\t\t\tsubtractItemsFromCollection(TMP, new String[]{\"SSLv2Hello\"}));\n\n\t\t\tTMP = subtractItemsFromCollection(TMP, HARDENED_STRICT_FORBIDDEN_PROTOCOLS);\n\t\t\tlog.config(\"STRICT protocols: \" + Arrays.toString(TMP));\n\t\t\tenabledProtocolsMap.put(getKey(HARDENED_MODE.strict, false), TMP);\n\t\t\tenabledProtocolsMap.put(getKey(HARDENED_MODE.strict, true),\n\t\t\t\t\t\t\t\t\tsubtractItemsFromCollection(TMP, new String[]{\"SSLv2Hello\"}));\n\n\t\t\tTMP = tmpEngine.getEnabledCipherSuites();\n\t\t\tif (disabledProtocols != null && disabledProtocols.length > 0) {\n\t\t\t\tTMP = subtractItemsFromCollection(TMP, disabledCiphers);\n\t\t\t}\n\n\t\t\tlog.config(\"RELAXED ciphers: \" + Arrays.toString(TMP));\n\t\t\tenabledCiphersMap.put(getKey(HARDENED_MODE.relaxed, false), TMP);\n\n\t\t\tTMP = subtractItemsFromCollection(TMP, HARDENED_SECURE_FORBIDDEN_CIPHERS);\n\t\t\tlog.config(\"SECURE ciphers: \" + Arrays.toString(TMP));\n\t\t\tenabledCiphersMap.put(getKey(HARDENED_MODE.secure, false), TMP);\n\n\t\t\tTMP = subtractItemsFromCollection(TMP, HARDENED_STRICT_FORBIDDEN_CIPHERS);\n\t\t\tlog.config(\"STRICT ciphers: \" + Arrays.toString(TMP));\n\t\t\tenabledCiphersMap.put(getKey(HARDENED_MODE.strict, false), TMP);\n\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\tlog.log(Level.WARNING, \"Can't determine supported protocols\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void start() {\n\t\teventBus.registerAll(this);\n\t}\n\n\t@Override\n\tpublic void stop() {\n\t\teventBus.unregisterAll(this);\n\t}\n\n\tprivate HARDENED_MODE getHardenedMode(String domain) {\n\t\tHARDENED_MODE mode = hardenedMode;\n\t\tif (domain != null && vHostManager != null) {\n\t\t\tfinal VHostItem vHostItem = vHostManager.getVHostItem(domain);\n\t\t\tif (vHostItem != null) {\n\t\t\t\tHardenedModeVHostItemExtension extension = vHostItem.getExtension(HardenedModeVHostItemExtension.class);\n\t\t\t\tif (extension != null) {\n\t\t\t\t\tmode = extension.getMode();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tmode = HARDENED_MODE.global.equals(mode) ? hardenedMode : mode;\n\t\tlog.log(Level.CONFIG, \"Using hardened-mode: {0} for domain: {1}\", new String[]{String.valueOf(mode), domain});\n\t\treturn mode;\n\t}\n\n\t/**\n\t * Method used to re-create self-signed certificate in case it's invalid (e.g. expired)\n\t */\n\tprivate void invalidateContextHolder(SSLHolder holder, String alias) throws Exception {\n\t\tsslContexts.remove(alias);\n\t\tcreateCertificate(alias);\n\t}\n\n\t/**\n\t * Method handles <code>CertificateChanged</code> event emitted by CertificateContainer and removes cached instance\n\t * of SSLContext for domain for which certificate has changed.\n\t */\n\t@HandleEvent\n\tprivate void onCertificateChange(CertificateContainer.CertificateChanged event) {\n\t\tsslContexts.remove(event.getAlias());\n\t\tremoveMatchedDomains(sslContexts, event.getDomains());\n\t}\n\n\tprivate boolean validateDomainCertificate(final SSLHolder holder, final String alias) throws Exception {\n\t\t// for self-signed certificates only\n\t\tif (holder != null && holder.domainCertificate != null && CertificateUtil.isSelfSigned(holder.domainCertificate)){\n\t\t\ttry {\n\t\t\t\tholder.domainCertificate.checkValidity();\n\t\t\t} catch (CertificateException e) {\n\t\t\t\tif (log.isLoggable(Level.INFO)) {\n\t\t\t\t\tlog.log(Level.INFO, \"Certificate for domain: {0} is not valid, exception: {1}, certificate: {2}\",\n\t\t\t\t\t\t\tnew String[]{alias, String.valueOf(e), String.valueOf(holder.domainCertificate)});\n\t\t\t\t}\n\t\t\t\tinvalidateContextHolder(holder, alias);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tpublic static class HardenedModeVHostItemExtension\n\t\t\textends AbstractVHostItemExtension<HardenedModeVHostItemExtension>\n\t\t\timplements VHostItemExtensionBackwardCompatible<HardenedModeVHostItemExtension> {\n\n\t\tpublic static final String ID = \"hardened-mode\";\n\n\t\tprivate HARDENED_MODE mode = HARDENED_MODE.secure;\n\n\t\tpublic static HARDENED_MODE parseHardenedModeFromString(String modeString) {\n\t\t\tHARDENED_MODE m = HARDENED_MODE.secure;\n\t\t\ttry {\n\t\t\t\tm = HARDENED_MODE.valueOf(modeString);\n\t\t\t} catch (IllegalArgumentException e) {\n\t\t\t\tString legacyOption = modeString.trim().toLowerCase();\n\t\t\t\tif (\"true\".equals(legacyOption)) {\n\t\t\t\t\tm = HARDENED_MODE.secure;\n\t\t\t\t} else if (\"false\".equals(legacyOption)) {\n\t\t\t\t\tm = HARDENED_MODE.relaxed;\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tm = HARDENED_MODE.global;\n\t\t\t}\n\t\t\treturn m;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getId() {\n\t\t\treturn ID;\n\t\t}\n\n\t\t@Override\n\t\tpublic void initFromElement(Element item) {\n\t\t\tmode = parseHardenedModeFromString(item.getAttributeStaticStr(getId()));\n\t\t}\n\n\t\t@Override\n\t\tpublic void initFromCommand(String prefix, Packet packet) throws IllegalArgumentException {\n\t\t\tfinal String fieldValue = Command.getFieldValue(packet, prefix);\n\t\t\tmode = fieldValue != null ? parseHardenedModeFromString(fieldValue) : HARDENED_MODE.global;\n\t\t}\n\n\t\tpublic HARDENED_MODE getMode() {\n\t\t\treturn mode;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toDebugString() {\n\t\t\treturn ID + \": \" + mode;\n\t\t}\n\n\t\t@Override\n\t\tpublic Element toElement() {\n\t\t\tif (mode == null || mode.equals(HARDENED_MODE.getDefault())) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tElement el = new Element(getId());\n\t\t\tel.setAttribute(getId(), String.valueOf(mode));\n\t\t\treturn el;\n\t\t}\n\n\t\t@Override\n\t\tpublic void addCommandFields(String prefix, Packet packet, boolean forDefault) {\n\t\t\tElement commandEl = packet.getElemChild(Command.COMMAND_EL, Command.XMLNS);\n\t\t\tDataForm.addFieldValue(commandEl, getId(), String.valueOf(getMode()), getId(), HARDENED_MODE.stringValues(),\n\t\t\t\t\t\t\t\t   HARDENED_MODE.stringValues(), DataForm.FieldType.ListSingle.value());\n\t\t}\n\n\t\t@Override\n\t\tpublic void initFromData(Map<String, Object> data) {\n\t\t\tHARDENED_MODE val = (HARDENED_MODE) data.remove(getId());\n\t\t\tif (val != null) {\n\t\t\t\tmode = val;\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic HardenedModeVHostItemExtension mergeWithDefaults(HardenedModeVHostItemExtension defaults) {\n\t\t\treturn mode == HARDENED_MODE.global ? defaults : this;\n\t\t}\n\t}\n\n\t@Bean(name = HardenedModeVHostItemExtension.ID, parent = VHostItemExtensionManager.class, active = true)\n\tpublic static class HardenedModeVHostItemExtensionProvider\n\t\t\timplements VHostItemExtensionProvider<HardenedModeVHostItemExtension> {\n\n\t\t@Override\n\t\tpublic String getId() {\n\t\t\treturn HardenedModeVHostItemExtension.ID;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<HardenedModeVHostItemExtension> getExtensionClazz() {\n\t\t\treturn HardenedModeVHostItemExtension.class;\n\t\t}\n\t}\n\n\t@Bean(name = \"rootSslContextContainer\", parent = Kernel.class, active = true, exportable = true)\n\tpublic static class Root\n\t\t\textends SSLContextContainer\n\t\t\timplements Initializable, UnregisterAware {\n\n\t\tpublic Root() {\n\t\t\tsuper();\n\t\t}\n\n\t\t@Override\n\t\tpublic void beforeUnregister() {\n\t\t\tstop();\n\t\t}\n\n\t\t@Override\n\t\tpublic void initialize() {\n\t\t\tstart();\n\t\t}\n\n\t\t// empty method to ensure that parent will not be injected to root instance\n\t\tpublic void setParent(SSLContextContainerIfc parent) {\n\t\t\tlog.log(Level.FINE, \"setting root = \" + parent);\n\t\t}\n\t}\n\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/io/SSLContextContainerAbstract.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.io;\n\nimport tigase.cert.CertificateUtil;\nimport tigase.kernel.beans.Inject;\n\nimport javax.net.ssl.KeyManager;\nimport javax.net.ssl.SSLContext;\nimport javax.net.ssl.TrustManager;\nimport javax.net.ssl.X509KeyManager;\nimport java.security.KeyStore;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.NoSuchProviderException;\nimport java.security.SecureRandom;\nimport java.security.cert.CertificateParsingException;\nimport java.security.cert.X509Certificate;\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\n/**\n * Created by andrzej on 29.02.2016.\n */\npublic abstract class SSLContextContainerAbstract\n\t\timplements SSLContextContainerIfc {\n\n\n\n\tprivate static final Logger log = Logger.getLogger(SSLContextContainerAbstract.class.getCanonicalName());\n\n\t@Inject\n\tprivate final CertificateContainerIfc certificateContainer;\n\n\tprivate SecureRandom secureRandom = new SecureRandom();\n\n\t/**\n\t * Generic method responsible for lookup of value in <code>Map</code> where passed key is domain name and in\n\t * <code>Map</code> wildcard name may be used as a key.\n\t */\n\tpublic static <T> T find(Map<String, T> lookupMap, String domain) {\n\t\tdomain = domain != null ? domain.toLowerCase() : domain;\n\t\tif (lookupMap.containsKey(domain)) {\n\t\t\treturn lookupMap.get(domain);\n\t\t}\n\n\t\tif (lookupMap.containsKey(\"*.\" + domain)) {\n\t\t\treturn lookupMap.get(\"*.\" + domain);\n\t\t}\n\n\t\t// should be faster than code commented below\n\t\t// in case when there is no value at all\n\t\tint idx = domain.indexOf(\".\");\n\t\tif (idx >= 0) {\n\t\t\tString wildcardDomain = \"*\" + domain.substring(idx);\n\t\t\tT cert = lookupMap.get(wildcardDomain);\n\t\t\tif (cert != null) {\n\t\t\t\tlookupMap.put(domain, cert);\n\t\t\t\treturn cert;\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tstatic void removeMatchedDomains(Map<String, ?> collection, Set<String> domains) {\n\t\tcollection.keySet().removeAll(domains);\n\t\tfinal Set<String> wildcardDomainsWithoutAsterisk = domains.stream()\n\t\t\t\t.filter(domain -> domain.startsWith(\"*.\"))\n\t\t\t\t.map(domain -> domain.substring(2))\n\t\t\t\t.collect(Collectors.toSet());\n\t\tcollection.keySet().removeIf(alias -> {\n\t\t\tString parentDomain = alias.substring(alias.indexOf(\".\") + 1);\n\t\t\treturn wildcardDomainsWithoutAsterisk.contains(parentDomain);\n\t\t});\n\t}\n\n\tstatic Set<String> getSpareDomainNamesToRemove(Set<String> collection, Set<String> domains) {\n\t\tvar wildcardDomainsWithoutAsterisk = domains.stream()\n\t\t\t\t.filter(domain -> domain.startsWith(\"*.\"))\n\t\t\t\t.map(domain -> domain.substring(2))\n\t\t\t\t.collect(Collectors.toSet());\n\t\treturn collection.stream().filter(alias -> {\n\t\t\tvar isWildcard = alias.startsWith(\"*.\");\n\t\t\tvar parentDomain = alias.indexOf(\".\") != -1 ? alias.substring(alias.indexOf(\".\") + 1) : null;\n\t\t\treturn !isWildcard && parentDomain != null && wildcardDomainsWithoutAsterisk.contains(parentDomain);\n\t\t}).collect(Collectors.toSet());\n\t}\n\n\tpublic SSLContextContainerAbstract(CertificateContainerIfc certContainer) {\n\t\tthis.certificateContainer = certContainer;\n\t}\n\n\t@Override\n\tpublic void addCertificates(Map<String, String> params) throws CertificateParsingException {\n\t\tthis.certificateContainer.addCertificates(params);\n\t}\n\n\t@Override\n\tpublic SSLContext getSSLContext(String protocol, String hostname, boolean clientMode) {\n\t\treturn getSSLContext(protocol, hostname, clientMode, null);\n\t}\n\n\t@Override\n\tpublic KeyStore getTrustStore() {\n\t\treturn (certificateContainer != null) ? certificateContainer.getTrustStore() : null;\n\t}\n\n\tprotected KeyManager[] createCertificate(String alias) throws Exception {\n\t\treturn certificateContainer.createCertificate(alias);\n\t}\n\n\tprotected SSLContext createSSLContext(String protocol) throws NoSuchAlgorithmException, NoSuchProviderException {\n\t\treturn SSLContext.getInstance(protocol);\n\t}\n\n\t/**\n\t * Common method used to create SSLContext instance based on provided parameters\n\t */\n\tprotected SSLHolder createContextHolder(String protocol, String hostname, String alias, boolean clientMode,\n\t\t\t\t\t\t\t\t\t\t\tTrustManager[] tms) throws Exception {\n\t\tSSLContext sslContext = null;\n\n\t\thostname = hostname != null ? hostname.toLowerCase() : hostname;\n\t\talias = alias != null ? alias.toLowerCase() : alias;\n\n\t\tKeyManager[] kms = getKeyManagers(hostname);\n\t\tif (kms == null) {\n\t\t\t// if there is no KeyManagerFactory for domain then we can create\n\t\t\t// new empty context as we have no certificate for this domain\n\t\t\tif (clientMode) {\n\t\t\t\tsslContext = createSSLContext(protocol);\n\t\t\t\tsslContext.init(null, tms, secureRandom);\n\t\t\t\tfinal SSLHolder sslHolder = new SSLHolder(tms, sslContext, null);\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Created new SSLHolder: {0} for domain: {1} (with alias: {2})\",\n\t\t\t\t\t\t\tnew String[]{String.valueOf(sslHolder), hostname, alias, String.valueOf(clientMode)});\n\t\t\t\t}\n\t\t\t\treturn sslHolder;\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.CONFIG)) {\n\t\t\t\tlog.log(Level.CONFIG, \"Key manager for hostname: {0} does not exist, generating new one\",\n\t\t\t\t\t\tnew String[]{hostname});\n\t\t\t}\n\n\t\t\tkms = createCertificate(alias);\n\t\t}\n\n\t\tX509Certificate crt = null;\n\t\tif (kms.length > 0 && kms[0] instanceof X509KeyManager) {\n\t\t\tX509KeyManager km = (X509KeyManager) kms[0];\n\t\t\tX509Certificate[] chain = km.getCertificateChain(alias);\n\t\t\tif (chain == null) {\n\t\t\t\tchain = km.getCertificateChain(\"*.\" + alias);\n\t\t\t}\n\t\t\tif (chain == null) {\n\t\t\t\tchain = km.getCertificateChain(getParentWildcardDomain(alias));\n\t\t\t}\n\n\t\t\t// Certificates are sorted so first one contain our certificate!\n\t\t\tcrt = chain == null || chain.length == 0 ? null : chain[0];\n\t\t}\n\n\t\tsslContext = createSSLContext(protocol);\n\t\tsslContext.init(kms, tms, secureRandom);\n\n\t\tfinal SSLHolder sslHolder = new SSLHolder(tms, sslContext, crt);\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Created new SSLHolder: {0} for domain: {1} (with alias: {2})\",\n\t\t\t\t\tnew String[]{String.valueOf(sslHolder), hostname, alias, String.valueOf(clientMode)});\n\t\t}\n\t\treturn sslHolder;\n\t}\n\n\tprotected String getDefCertAlias() {\n\t\treturn certificateContainer.getDefCertAlias();\n\t}\n\n\tprotected KeyManager[] getKeyManagers(String hostname) {\n\t\treturn certificateContainer.getKeyManagers(hostname);\n\t}\n\n\tprotected TrustManager[] getTrustManagers() {\n\t\treturn certificateContainer.getTrustManagers();\n\t}\n\n\tprivate String getParentWildcardDomain(String hostname) {\n\t\treturn hostname.indexOf('.') > 0 ? \"*\" + hostname.substring(hostname.indexOf('.')) : hostname;\n\t}\n\n\tprotected class SSLHolder {\n\n\t\tfinal X509Certificate domainCertificate;\n\t\tfinal SSLContext sslContext;\n\t\tfinal TrustManager[] tms;\n\n\t\tpublic SSLHolder(TrustManager[] tms, SSLContext sslContext, X509Certificate domainCertificate) {\n\t\t\tthis.tms = tms;\n\t\t\tthis.sslContext = sslContext;\n\t\t\tthis.domainCertificate = domainCertificate;\n\t\t}\n\n\t\tpublic SSLContext getSSLContext() {\n\t\t\treturn sslContext;\n\t\t}\n\n\t\tpublic boolean isValid(TrustManager[] tms) {\n\t\t\treturn tms == this.tms;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tfinal StringBuffer sb = new StringBuffer(\"SSLHolder{\");\n\t\t\tif (domainCertificate != null) {\n\t\t\t\tsb.append(\"domainCertificate=subject: \")\n\t\t\t\t\t\t.append(CertificateUtil.getCertCName(domainCertificate))\n\t\t\t\t\t\t.append(\", altNames: \")\n\t\t\t\t\t\t.append(CertificateUtil.getCertAltCName(domainCertificate))\n\t\t\t\t\t\t.append(\", issuer: \")\n\t\t\t\t\t\t.append(domainCertificate.getIssuerDN());\n\t\t\t}\n\t\t\tsb.append(\", sslContext=\").append(sslContext);\n\t\t\tsb.append(\", tms=\").append(tms == null ? \"null\" : Arrays.asList(tms).toString());\n\t\t\tsb.append('}');\n\t\t\treturn sb.toString();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/io/SSLContextContainerIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.io;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.server.Lifecycle;\n\nimport javax.net.ssl.SSLContext;\nimport javax.net.ssl.TrustManager;\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.ByteOrder;\nimport java.security.KeyStore;\nimport java.security.cert.CertificateParsingException;\nimport java.util.Map;\n\n/**\n * Describe interface SSLContextContainerIfc here.\n * <br>\n * Created: Tue Nov 20 11:43:32 2007\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface SSLContextContainerIfc\n\t\textends Lifecycle {\n\n\t/**\n\t * Constant <code>ALLOW_INVALID_CERTS_KEY</code> is a key pointing to a configuration parameters specyfying if\n\t * invalid certificates are acceptable by the server. Invalid certificates are expired ones or certificates issued\n\t * for a different domain. This should be really set to <code>false</code> in any real deployment and can be set ot\n\t * <code>true</code> in development invironment.\n\t */\n\tString ALLOW_INVALID_CERTS_KEY = \"allow-invalid-certs\";\n\n\t/**\n\t * Constant <code>ALLOW_INVALID_CERTS_VAL</code> is a default configuration parameter specifying if invalid\n\t * certificates are acceptable by the server.\n\t */\n\tString ALLOW_INVALID_CERTS_VAL = \"false\";\n\n\t/**\n\t * Constant <code>ALLOW_SELF_SIGNED_CERTS_KEY</code> is a key pointing to a configuration parameter specifying if\n\t * self-signed certificates are acceptable for the server.\n\t */\n\tString ALLOW_SELF_SIGNED_CERTS_KEY = \"allow-self-signed-certs\";\n\n\t/**\n\t * Constant <code>ALLOW_SELF_SIGNED_CERTS_VAL</code> is a default configuration value specifying if self-signed\n\t * certificates are allowed by the server.\n\t */\n\tString ALLOW_SELF_SIGNED_CERTS_VAL = \"true\";\n\n\tString CERT_ALIAS_KEY = \"cert-alias\";\n\n\tString CERT_SAVE_TO_DISK_KEY = \"cert-save-to-disk\";\n\n\t/**\n\t * Constant <code>DEFAULT_DOMAIN_CERT_KEY</code> is a key pointing to the domain with default certificate.\n\t */\n\tString DEFAULT_DOMAIN_CERT_KEY = \"ssl-def-cert-domain\";\n\n\t/**\n\t * Constant <code>DEFAULT_DOMAIN_CERT_VAL</code> keeps default value for a domain with default certificate.\n\t */\n\tString DEFAULT_DOMAIN_CERT_VAL = \"default\";\n\n\t/**\n\t * Constant <code>JKS_KEYSTORE_FILE_KEY</code> is a key pointing to a JKS keystore file.\n\t */\n\tString JKS_KEYSTORE_FILE_KEY = \"keys-store\";\n\n\t/**\n\t * Constant <code>JKS_KEYSTORE_FILE_VAL</code> keeps default value for a JKS keystore file.\n\t */\n\tString JKS_KEYSTORE_FILE_VAL = \"certs\" + File.separator + \"rsa-keystore\";\n\n\t/**\n\t * Constant <code>JKS_KEYSTORE_PWD_KEY</code> is a key pointing to a private key password,\n\t */\n\tString JKS_KEYSTORE_PWD_KEY = \"keys-store-password\";\n\n\t/**\n\t * Constant <code>JKS_KEYSTORE_PWD_VAL</code> is a default private key password.\n\t */\n\tString JKS_KEYSTORE_PWD_VAL = \"keystore\";\n\n\tString PEM_CERTIFICATE_KEY = \"pem-certificate\";\n\n\t/**\n\t * Constant <code>SERVER_CERTS_DIR_KEY</code> is a key pointing to a configuration parameter with directory names\n\t * where all server certificates are stored. This can be a comma separated list of directories, instead of a single\n\t * directory name. Certificates are stored in <code>*.pem</code> files where the first part of the file name is a\n\t * domain name i.e.: <code>yourdomain.com.pem</code>. There is one exception though. The file named\n\t * <code>default.pem</code> stores a certificate which is a default certificate for the server if certificate for\n\t * specific domain is missing.\n\t */\n\tString SERVER_CERTS_LOCATION_KEY = \"ssl-certs-location\";\n\n\t/**\n\t * Constant <code>SERVER_CERTS_DIR_VAL</code> is a default directory name where all certificate files are stored.\n\t */\n\tString SERVER_CERTS_LOCATION_VAL = \"certs/\";\n\n\t/**\n\t * Constant <code>SSL_CONTAINER_CLASS_KEY</code> is a key pointing to a container implementation class. The class is\n\t * loaded at startup time and initialized using configuration parameters. Some container implementations may accept\n\t * different parameters set. Please refer to the implementation for more details.\n\t */\n\tString SSL_CONTAINER_CLASS_KEY = \"ssl-container-class\";\n\n\t/**\n\t * Constant <code>SSL_CONTAINER_CLASS_VAL</code> keeps default container implementation class loaded if none is\n\t * specified in configuration file.\n\t */\n\tString SSL_CONTAINER_CLASS_VAL = SSLContextContainer.class.getName();\n\n\t/**\n\t * Constant <code>TRUSTED_CERTS_DIR_KEY</code> is a key pointing to a configuration parameter where all trusted\n\t * certificates are stored. This can be a comma separated list of directories.\n\t */\n\tString TRUSTED_CERTS_DIR_KEY = \"trusted-certs-dir\";\n\n\t/**\n\t * Constant <code>TRUSTED_CERTS_DIR_VAL</code> is a default directory name where all trusted certificates are\n\t * stored.\n\t */\n\tString TRUSTED_CERTS_DIR_VAL = \"/etc/ssl/certs\";\n\n\t/**\n\t * Constant <code>TRUSTSTORE_FILE_KEY</code> is a key pointing to a trust store file.\n\t */\n\tString TRUSTSTORE_FILE_KEY = \"trusts-store\";\n\n\t/**\n\t * Constant <code>TRUSTSTORE_FILE_VAL</code> is a default truststore file.\n\t */\n\tString TRUSTSTORE_FILE_VAL = \"certs\" + File.separator + \"truststore\";\n\n\t/**\n\t * Constant <code>TRUSTSTORE_PWD_KEY</code> is a key pointing to a trustore file password.\n\t */\n\tString TRUSTSTORE_PWD_KEY = \"trusts-store-password\";\n\n\t/**\n\t * Constant <code>TRUSTSTORE_PWD_VAL</code> is a default password for truststore file.\n\t */\n\tString TRUSTSTORE_PWD_VAL = \"truststore\";\n\n\t// ~--- methods\n\t// --------------------------------------------------------------\n\n\t/**\n\t * Method <code>addCertificates</code> allows to add more certificates at run time after the container has bee\n\t * already initialized. This is to avoid server restart if there are certificates updates or new certificates for\n\t * new virtual domain. The method should add new certificates or replace existing one if there is already a\n\t * certificate for a domain.\n\t *\n\t * @param params a <code>Map</code> value with configuration parameters.\n\t *\n\t */\n\tvoid addCertificates(Map<String, String> params) throws CertificateParsingException;\n\n\tIOInterface createIoInterface(String protocol, String local_hostname, String remote_hostname, int port, boolean clientMode,\n\t\t\t\t\t\t\t\t  boolean wantClientAuth, boolean needClientAuth, ByteOrder byteOrder,\n\t\t\t\t\t\t\t\t  TrustManager[] x509TrustManagers, TLSEventHandler eventHandler, IOInterface ioi,\n\t\t\t\t\t\t\t\t  CertificateContainerIfc certificateContainer) throws IOException;\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.1.0\", removeIn = \"9.0.0\")\n\tdefault IOInterface createIoInterface(String protocol, String tls_hostname, int port, boolean clientMode,\n\t\t\t\t\t\t\t\t  boolean wantClientAuth, boolean needClientAuth, ByteOrder byteOrder,\n\t\t\t\t\t\t\t\t  TrustManager[] x509TrustManagers, TLSEventHandler eventHandler, IOInterface ioi,\n\t\t\t\t\t\t\t\t  CertificateContainerIfc certificateContainer) throws IOException {\n\t\treturn this.createIoInterface(protocol, tls_hostname, tls_hostname, port, clientMode, wantClientAuth, needClientAuth,\n\t\t\t\t\t\t\t   byteOrder, x509TrustManagers, eventHandler, ioi, certificateContainer);\n\t};\n\n\t// ~--- get methods\n\t// ----------------------------------------------------------\n\n\t/**\n\t * Method <code>getSSLContext</code> creates and returns new SSLContext for a given domain (hostname). For creation\n\t * of the SSLContext a certificate associated with this domain (hostname) should be used. If there is no specific\n\t * certificate for a given domain then default certificate should be used.\n\t *\n\t * @param protocol a <code>String</code> is either 'SSL' or 'TLS' value.\n\t * @param hostname a <code>String</code> value keeps a hostname or domain for SSLContext.\n\t * @param clientMode if set SSLContext will be created for client mode (ie. creation of server certificate will be\n\t * skipped if there is no certificate)\n\t *\n\t * @return a <code>SSLContext</code> value\n\t */\n\tSSLContext getSSLContext(String protocol, String hostname, boolean clientMode);\n\n\t/**\n\t * Method <code>getSSLContext</code> creates and returns new SSLContext for a given domain (hostname). For creation\n\t * of the SSLContext a certificate associated with this domain (hostname) should be used. If there is no specific\n\t * certificate for a given domain then default certificate should be used.\n\t *\n\t * @param protocol a <code>String</code> is either 'SSL' or 'TLS' value.\n\t * @param hostname a <code>String</code> value keeps a hostname or domain for SSLContext.\n\t * @param clientMode if set SSLContext will be created for client mode (ie. creation of server certificate will be\n\t * skipped if there is no certificate)\n\t * @param tms array of TrustManagers which should be used to validate remote certificate\n\t *\n\t * @return a <code>SSLContext</code> value\n\t */\n\tSSLContext getSSLContext(String protocol, String hostname, boolean clientMode, TrustManager[] tms);\n\n\t/**\n\t * Returns a trust store with all trusted certificates.\n\t *\n\t * @return a KeyStore with all trusted certificates, the KeyStore can be empty but cannot be null.\n\t */\n\tKeyStore getTrustStore();\n\n\tString[] getEnabledCiphers(String domain);\n\n\tString[] getEnabledProtocols(String domain, boolean client);\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.1.0\")\n\tdefault String[] getEnabledCiphers() {\n\t\treturn getEnabledCiphers(null);\n\t};\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.1.0\")\n\tdefault String[] getEnabledProtocols() {\n\t\treturn getEnabledProtocols(null, true);\n\t};\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/io/SampleSocketThread.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.io;\n\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.nio.channels.SelectionKey;\nimport java.nio.channels.Selector;\nimport java.nio.channels.ServerSocketChannel;\nimport java.nio.channels.SocketChannel;\nimport java.util.Iterator;\nimport java.util.concurrent.ConcurrentLinkedQueue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Describe class SampleSocketThread here.\n * <br>\n * Created: Sun Aug  6 22:34:40 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class SampleSocketThread\n\t\textends Thread {\n\n\tprivate static final Logger log = Logger.getLogger(\"tigase.io.SampleSocketThread\");\n\tprivate final ConcurrentLinkedQueue<IOInterface> for_removal = new ConcurrentLinkedQueue<IOInterface>();\n\tprivate final ConcurrentLinkedQueue<IOInterface> waiting = new ConcurrentLinkedQueue<IOInterface>();\n\tprivate final ConcurrentLinkedQueue<InetSocketAddress> waiting_accept = new ConcurrentLinkedQueue<InetSocketAddress>();\n\tprivate Selector clientSel = null;\n\tprivate SocketHandler handler = null;\n\tprivate boolean stopping = false;\n\n\t/**\n\t * Creates a new <code>SampleSocketThread</code> instance.\n\t */\n\tpublic SampleSocketThread(SocketHandler handler) throws IOException {\n\t\tthis.handler = handler;\n\t\tclientSel = Selector.open();\n\t\tsetName(\"SampleSocketThread\");\n\t}\n\n\tpublic void addIOInterface(IOInterface s) {\n\t\twaiting.offer(s);\n\t\tclientSel.wakeup();\n\t}\n\n\tpublic void addForAccept(InetSocketAddress isa) {\n\t\twaiting_accept.offer(isa);\n\t\tclientSel.wakeup();\n\t}\n\n\tpublic void removeIOInterface(IOInterface s) {\n\t\tSelectionKey key = s.getSocketChannel().keyFor(clientSel);\n\t\tif (key != null && key.attachment() == s) {\n\t\t\tkey.cancel();\n\t\t} // end of if (key != null)\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\twhile (!stopping) {\n\t\t\ttry {\n\t\t\t\tclientSel.select();\n\t\t\t\tfor (Iterator i = clientSel.selectedKeys().iterator(); i.hasNext(); ) {\n\t\t\t\t\tSelectionKey sk = (SelectionKey) i.next();\n\t\t\t\t\ti.remove();\n\t\t\t\t\tif ((sk.readyOps() & SelectionKey.OP_ACCEPT) != 0) {\n\t\t\t\t\t\tServerSocketChannel nextReady = (ServerSocketChannel) sk.channel();\n\t\t\t\t\t\tSocketChannel sc = nextReady.accept();\n\t\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\t\tlog.finer(\"Registered new client socket: \" + sc);\n\t\t\t\t\t\t}\n\t\t\t\t\t\thandler.handleSocketAccept(sc);\n\t\t\t\t\t}\n\t\t\t\t\tif ((sk.readyOps() & SelectionKey.OP_CONNECT) != 0) {\n\t\t\t\t\t\t// Not implemented yet\n\t\t\t\t\t}\n\t\t\t\t\tif ((sk.readyOps() & SelectionKey.OP_READ) != 0) {\n\t\t\t\t\t\tIOInterface s = (IOInterface) sk.attachment();\n\t\t\t\t\t\tsk.cancel();\n\t\t\t\t\t\thandler.handleIOInterface(s);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Clean-up cancelled keys...\n\t\t\t\tclientSel.selectNow();\n\t\t\t\taddAllWaiting();\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.SEVERE, \"SampleSocketThread I/O error, can't continue my work.\", e);\n\t\t\t\tstopping = true;\n\t\t\t}\n\t\t}\n\t\tSystem.err.println(\"SampleSocketThread stopped!\");\n\t\tSystem.exit(2);\n\t}\n\n\t// Implementation of java.lang.Runnable\n\n\tprivate void addAllWaiting() throws IOException {\n\t\tIOInterface s = null;\n\t\twhile ((s = waiting.poll()) != null) {\n\t\t\tfinal SocketChannel sc = s.getSocketChannel();\n\t\t\ttry {\n\t\t\t\tsc.register(clientSel, SelectionKey.OP_READ, s);\n\t\t\t} catch (Exception e) {\n\t\t\t\t// Ignore such channel\n\t\t\t} // end of try-catch\n\t\t} // end of for ()\n\t\tInetSocketAddress isa = null;\n\t\twhile ((isa = waiting_accept.poll()) != null) {\n\t\t\tServerSocketChannel ssc = ServerSocketChannel.open();\n\t\t\tssc.configureBlocking(false);\n\t\t\tssc.socket().bind(isa);\n\t\t\tssc.register(clientSel, SelectionKey.OP_ACCEPT, null);\n\t\t} // end of while (isa = waiting_accept.poll() != null)\n\t}\n\n\tpublic interface SocketHandler {\n\n\t\tvoid handleIOInterface(IOInterface ioIfc) throws IOException;\n\n\t\tvoid handleSocketAccept(SocketChannel sc) throws IOException;\n\n\t}\n\n} // SampleSocketThread\n"
  },
  {
    "path": "src/main/java/tigase/io/SocketIO.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.io;\n\nimport jdk.net.ExtendedSocketOptions;\nimport tigase.net.ConnectionOpenListener;\nimport tigase.net.IOUtil;\nimport tigase.stats.StatisticsList;\n\nimport java.io.EOFException;\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.SocketChannel;\nimport java.util.Queue;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Describe class SocketIO here.\n * <br>\n * Created: Sat May 14 07:18:30 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\npublic class SocketIO\n\t\timplements IOInterface {\n\n\tprivate static final Logger log = Logger.getLogger(SocketIO.class.getName());\n\tprivate static final int MAX_USER_IO_QUEUE_SIZE_PROP_DEF = 1000;\n\n\t// ~--- fields ---------------------------------------------------------------\n\tprivate static final String MAX_USER_IO_QUEUE_SIZE_PROP_KEY = \"max-user-io-queue-size\";\n\tprivate static final boolean DEBUG_SOCKET_OVERFLOW = Boolean.getBoolean(\"socket-overflow-debug\");\n\tprivate static final Level LOG_SOCKET_OVERFLOW_LEVEL = DEBUG_SOCKET_OVERFLOW ? Level.INFO : Level.FINEST;\n\n\tprivate long buffOverflow = 0;\n\tprivate int bytesRead = 0;\n\tprivate long bytesReceived = 0;\n\tprivate long bytesSent = 0;\n\tprivate SocketChannel channel = null;\n\tprivate String channelToString = null;\n\tprivate Queue<ByteBuffer> dataToSend = null;\n\tprivate String logId = null;\n\tprivate String remoteAddress = null;\n\tprivate long totalBuffOverflow = 0;\n\tprivate long totalBytesReceived = 0;\n\tprivate long totalBytesSent = 0;\n\n\tpublic SocketIO(final SocketChannel sock) throws IOException {\n\t\tchannel = sock;\n\t\tif (channel != null && channel.socket() != null) {\n\t\t\tchannelToString = channel.socket().toString();\n\t\t}\n\t\tchannel.configureBlocking(false);\n\t\tchannel.socket().setSoLinger(false, 0);\n\t\tchannel.socket().setReuseAddress(true);\n\t\tchannel.socket().setKeepAlive(true);\n\n\t\tchannel.socket().setOption(ExtendedSocketOptions.TCP_KEEPIDLE, 60);\n\t\tchannel.socket().setOption(ExtendedSocketOptions.TCP_KEEPCOUNT, 3);\n\t\tchannel.socket().setOption(ExtendedSocketOptions.TCP_KEEPINTERVAL, 60);\n\n\t\tremoteAddress = channel.socket().getInetAddress().getHostAddress();\n\t\tif (channel.socket().getTrafficClass() == ConnectionOpenListener.IPTOS_THROUGHPUT) {\n\t\t\tdataToSend = new LinkedBlockingQueue<ByteBuffer>(100000);\n\t\t} else {\n\t\t\tint queue_size = Integer.getInteger(MAX_USER_IO_QUEUE_SIZE_PROP_KEY, MAX_USER_IO_QUEUE_SIZE_PROP_DEF);\n\t\t\tif (log.isLoggable(LOG_SOCKET_OVERFLOW_LEVEL)) {\n\t\t\t\tlog.log(LOG_SOCKET_OVERFLOW_LEVEL, \"SOCKET - Buffer to send limit: {0} [{1}]\",\n\t\t\t\t\t\tnew Object[]{queue_size, toString()});\n\t\t\t}\n\n\t\t\tdataToSend = new LinkedBlockingQueue<ByteBuffer>(queue_size);\n\t\t}\n\t}\n\n\t@Override\n\tpublic int bytesRead() {\n\t\treturn bytesRead;\n\t}\n\n\t@Override\n\tpublic boolean checkCapabilities(String caps) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int getInputPacketSize() throws IOException {\n\t\treturn channel.socket().getReceiveBufferSize();\n\t}\n\n\t@Override\n\tpublic SocketChannel getSocketChannel() {\n\t\treturn channel;\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list, boolean reset) {\n\t\tlist.add(\"socketio\", \"Bytes sent\", bytesSent, Level.FINE);\n\t\tlist.add(\"socketio\", \"Bytes received\", bytesReceived, Level.FINE);\n\t\tlist.add(\"socketio\", \"Buffers overflow\", buffOverflow, Level.FINE);\n\t\tlist.add(\"socketio\", \"Total bytes sent\", totalBytesSent, Level.FINE);\n\t\tlist.add(\"socketio\", \"Total bytes received\", totalBytesReceived, Level.FINE);\n\t\tlist.add(\"socketio\", \"Ttoal buffers overflow\", totalBuffOverflow, Level.FINE);\n\t\tif (reset) {\n\t\t\tbytesSent = 0;\n\t\t\tbytesReceived = 0;\n\t\t\tbuffOverflow = 0;\n\t\t}\n\t}\n\n\tpublic long getBytesSent(boolean reset) {\n\t\tlong tmp = bytesSent;\n\n\t\tif (reset) {\n\t\t\tbytesSent = 0;\n\t\t}\n\n\t\treturn tmp;\n\t}\n\n\tpublic long getTotalBytesSent() {\n\t\treturn totalBytesSent;\n\t}\n\n\tpublic long getBytesReceived(boolean reset) {\n\t\tlong tmp = bytesReceived;\n\n\t\tif (reset) {\n\t\t\tbytesReceived = 0;\n\t\t}\n\n\t\treturn tmp;\n\t}\n\n\tpublic long getTotalBytesReceived() {\n\t\treturn totalBytesReceived;\n\t}\n\n\t@Override\n\tpublic long getBuffOverflow(boolean reset) {\n\t\tlong tmp = buffOverflow;\n\n\t\tif (reset) {\n\t\t\tbuffOverflow = 0;\n\t\t}\n\n\t\treturn tmp;\n\t}\n\n\t@Override\n\tpublic long getTotalBuffOverflow() {\n\t\treturn totalBuffOverflow;\n\t}\n\n\t@Override\n\tpublic boolean isConnected() {\n\n\t\t// More correct would be calling both methods, however in the Tigase\n\t\t// all SocketChannels are connected before SocketIO is created.\n\t\t// return channel.isOpen() && channel.isConnected();\n\t\treturn channel.isOpen();\n\t}\n\n\t@Override\n\tpublic boolean isRemoteAddress(String addr) {\n\t\treturn remoteAddress.equals(addr);\n\t}\n\n\t@Override\n\tpublic ByteBuffer read(final ByteBuffer buff) throws IOException {\n\t\tByteBuffer tmp = IOUtil.getDirectBuffer(buff.remaining());\n\t\ttry {\n\t\t\tbytesRead = channel.read(tmp);\n\t\t\ttmp.flip();\n\t\t\tif (bytesRead > 0) {\n\t\t\t\tbuff.put(tmp);\n\t\t\t}\n\t\t} finally {\n\t\t\tIOUtil.returnDirectBuffer(tmp);\n\t\t}\n\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.log(Level.FINER, \"Read from channel {0} bytes [{1}]\", new Object[]{bytesRead, toString()});\n\t\t}\n\t\tif (bytesRead == -1) {\n\t\t\t// we need to close channel but we should not throw exception as\n\t\t\t// in other way we will lose data from higher level internal buffers\n\t\t\tchannel.close();\n//\t\t\tthrow new EOFException(\"Channel has been closed.\");\n\t\t}    // end of if (result == -1)\n\t\tif (bytesRead > 0) {\n\t\t\tbuff.flip();\n\t\t\tbytesReceived += bytesRead;\n\t\t\ttotalBytesReceived += bytesRead;\n\t\t}\n\n\t\treturn buff;\n\t}\n\n\t@Override\n\tpublic void stop() throws IOException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Stop called \" + toString());\n\t\t}\n\n\t\tchannel.close();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getClass().getSimpleName() + \", ID: \" + logId + ((channel == null)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? null\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: \", \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(channel.socket().isConnected()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t ? \"connected \" + channel.socket()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t : \"disconnected \" + channelToString));\n\t}\n\n\t@Override\n\tpublic boolean waitingToSend() {\n\t\treturn isConnected() && (dataToSend.size() > 0);\n\t}\n\n\t@Override\n\tpublic int waitingToSendSize() {\n\t\treturn dataToSend.size();\n\t}\n\n\t@Override\n\tpublic int write(final ByteBuffer buff) throws IOException {\n\n\t\t// int result = 0;\n\t\t// while (buff.hasRemaining()) {\n\t\t// final int res = channel.write(buff);\n\t\t// if (res == -1) {\n\t\t// throw new EOFException(\"Channel has been closed.\");\n\t\t// } // end of if (res == -1)\n\t\t// result += res;\n\t\t// } // end of while (out.hasRemaining())\n\t\t// log.finer(\"Wrote to channel \" + result + \" bytes.\");\n\t\t// return result;\n\t\tif ((buff != null) && buff.hasRemaining()) {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.log(Level.FINER, \"SOCKET - Writing data, remaining: {0} [{1}]\",\n\t\t\t\t\t\tnew Object[]{buff.remaining(), toString()});\n\t\t\t}\n\t\t\tif (!dataToSend.offer(buff)) {\n\t\t\t\t++buffOverflow;\n\t\t\t\t++totalBuffOverflow;\n\t\t\t\tif (log.isLoggable(LOG_SOCKET_OVERFLOW_LEVEL)) {\n\t\t\t\t\tlog.log(LOG_SOCKET_OVERFLOW_LEVEL, \"SOCKET - Buffer overflow: {0}, {1} [{2}]\",\n\t\t\t\t\t\t\tnew Object[]{totalBuffOverflow, dataToSend.size(), toString()});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tint result = 0;\n\t\tByteBuffer dataBuffer = null;\n\n\t\t// we are processing all buffers one by one to reduce need for direct \n\t\t// memory, and we use our own cache of DirectByteBuffers as cache from JDK\n\t\t// may keep up to 1024 buffers for single thread!!\n\t\twhile ((dataBuffer = dataToSend.peek()) != null) {\n\t\t\tint pos = dataBuffer.position();\n\t\t\tint lim = dataBuffer.limit();\n\t\t\tint rem = (pos <= lim ? lim - pos : 0);\n\t\t\tint res = 0;\n\n\t\t\tByteBuffer tmp = IOUtil.getDirectBuffer(rem);\n\t\t\ttry {\n\t\t\t\ttmp.put(dataBuffer);\n\t\t\t\ttmp.flip();\n\t\t\t\tdataBuffer.position(pos);\n\t\t\t\tres = channel.write(tmp);\n\t\t\t\tif (res > 0) {\n\t\t\t\t\tdataBuffer.position(pos + res);\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tIOUtil.returnDirectBuffer(tmp);\n\t\t\t}\n\n\t\t\tif (res == -1) {\n\t\t\t\tthrow new EOFException(\"Channel has been closed.\");\n\t\t\t} else {\n\t\t\t\tresult += res;\n\t\t\t}\n\n\t\t\tif (!dataBuffer.hasRemaining()) {\n\t\t\t\tdataToSend.poll();\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.log(Level.FINER, \"Wrote to channel {0} bytes [{1}]\", new Object[]{result, toString()});\n\t\t}\n\n\t\tbytesSent += result;\n\t\ttotalBytesSent += result;\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic void setLogId(String logId) {\n\t\tthis.logId = logId;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/io/TLSEventHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.io;\n\n/**\n * Describe interface TLSEventHandler here.\n * <br>\n * Created: Wed Feb  7 23:20:14 2007\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\npublic interface TLSEventHandler {\n\n\tvoid handshakeCompleted(TLSWrapper wrapper);\n\n\tint getSocketInputSize();\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/io/TLSIO.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.io;\n\nimport tigase.stats.StatisticsList;\n\nimport javax.net.ssl.SSLEngineResult;\nimport java.io.EOFException;\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.channels.SocketChannel;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Describe class TLSIO here.\n * <br>\n * Created: Sat May 14 07:43:30 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class TLSIO\n\t\timplements IOInterface, TLSIOIfc {\n\n\tpublic static final String TLS_CAPS = \"tls-caps\";\n\n\tprivate static final String TLS_WAIT_FOR_HANDSHAKE_NOT_READY_KEY = \"tls-wait-for-handshake\";\n\t\n\tprivate static final boolean TLS_WAIT_FOR_HANDSHAKE_NOT_READY = Boolean.getBoolean(TLS_WAIT_FOR_HANDSHAKE_NOT_READY_KEY);\n\n\tprivate static final Logger log = Logger.getLogger(TLSIO.class.getName());\n\n\tprivate IOInterface io = null;\n\n\t/**\n\t * <code>tlsInput</code> buffer keeps data decoded from tlsWrapper.\n\t */\n\tprivate ByteBuffer tlsInput = null;\n\n\t/**\n\t * <code>tlsWrapper</code> is a TLS wrapper for connections requiring TLS protocol.\n\t */\n\tprivate TLSWrapper tlsWrapper = null;\n\n\tint max_loop_runs = 1000;\n\n\t// /**\n\t// * Creates a new <code>TLSIO</code> instance.\n\t// *\n\t// */\n\t// public TLSIO(final SocketChannel sock) {\n\t// io = new SocketIO(sock);\n\t// tlsWrapper = new TLSWrapper(\"TLS\");\n\t// tlsInput = ByteBuffer.allocate(tlsWrapper.getAppBuffSize());\n\t// }\n\n\tpublic TLSIO(final IOInterface ioi, final TLSWrapper wrapper, final ByteOrder order) throws IOException {\n\t\tio = ioi;\n\t\ttlsWrapper = wrapper;\n\t\ttlsWrapper.setDebugId(toString());\n\t\ttlsInput = ByteBuffer.allocate(tlsWrapper.getAppBuffSize());\n\t\ttlsInput.order(order);\n\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.log(Level.FINER, \"TLS Socket created: {0}\", io.toString());\n\t\t}\n\n\t\tif (tlsWrapper.isClientMode()) {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.finer(\"TLS - client mode, starting handshaking now...\");\n\t\t\t}\n\n\t\t\twrite(ByteBuffer.allocate(0));\n\t\t} // end of if (tlsWrapper.isClientMode())\n\t}\n\n\t@Override\n\tpublic int bytesRead() {\n\t\treturn io.bytesRead();\n\t}\n\n\t@Override\n\tpublic long getBytesSent(boolean reset) {\n\t\treturn io.getBytesSent(reset);\n\t}\n\n\t@Override\n\tpublic long getTotalBytesSent() {\n\t\treturn io.getTotalBytesSent();\n\t}\n\n\t@Override\n\tpublic long getBytesReceived(boolean reset) {\n\t\treturn io.getBytesReceived(reset);\n\t}\n\n\t@Override\n\tpublic long getTotalBytesReceived() {\n\t\treturn io.getTotalBytesReceived();\n\t}\n\n\t@Override\n\tpublic long getBuffOverflow(boolean reset) {\n\t\treturn io.getBuffOverflow(reset);\n\t}\n\n\t@Override\n\tpublic long getTotalBuffOverflow() {\n\t\treturn io.getTotalBuffOverflow();\n\t}\n\n\t@Override\n\tpublic boolean checkCapabilities(String caps) {\n\t\treturn caps.contains(TLS_CAPS) || io.checkCapabilities(caps);\n\t}\n\n\t@Override\n\tpublic int getInputPacketSize() throws IOException {\n\t\treturn tlsWrapper.getPacketBuffSize();\n\t}\n\n\t@Override\n\tpublic SocketChannel getSocketChannel() {\n\t\treturn io.getSocketChannel();\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list, boolean reset) {\n\t\tif (io != null) {\n\t\t\tio.getStatistics(list, reset);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isConnected() {\n\t\treturn io.isConnected();\n\t}\n\n\t@Override\n\tpublic boolean isRemoteAddress(String addr) {\n\t\treturn io.isRemoteAddress(addr);\n\t}\n\n\t@Override\n\tpublic ByteBuffer read(ByteBuffer buff) throws IOException {\n\n\t\t// if (log.isLoggable(Level.FINER)) {\n\t\t// log.finer(\"input.capacity()=\" + buff.capacity());\n\t\t// log.finer(\"input.remaining()=\" + buff.remaining());\n\t\t// log.finer(\"input.limit()=\" + buff.limit());\n\t\t// log.finer(\"input.position()=\" + buff.position());\n\t\t// }\n\t\tByteBuffer tmpBuffer = io.read(buff);\n\n\t\tif (io.bytesRead() > 0) {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.log(Level.FINER, \"Read bytes: {0}, {1}\", new Object[]{io.bytesRead(), toString()});\n\t\t\t}\n\n\t\t\treturn decodeData(tmpBuffer);\n\t\t} else {\n\t\t\tif (tlsInput.capacity() > tlsWrapper.getAppBuffSize() && tlsInput.capacity() == tlsInput.remaining()) {\n\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\tlog.log(Level.FINE, \"Resizing tlsInput to {0} bytes, capacity: {1}, remaining: {2}; IO: {3}\",\n\t\t\t\t\t\t\tnew Object[]{tlsWrapper.getAppBuffSize(), tlsInput.capacity(), tlsInput.remaining(), toString()});\n\t\t\t\t}\n\t\t\t\tByteBuffer bb = ByteBuffer.allocate(tlsWrapper.getAppBuffSize());\n\n\t\t\t\tbb.order(tlsInput.order());\n\n\t\t\t\ttlsInput = bb;\n\t\t\t}\n\t\t\treturn null;\n\t\t} // end of else\n\t}\n\n\t@Override\n\tpublic void stop() throws IOException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Stop called...\" + toString());\n\n\t\t\t// Thread.dumpStack();\n\t\t}\n\n\t\tio.stop();\n\t\ttlsWrapper.close();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"TLS: \" + io.toString();\n\t}\n\n\t@Override\n\tpublic boolean waitingToSend() {\n\t\treturn io.waitingToSend();\n\t}\n\n\t@Override\n\tpublic int waitingToSendSize() {\n\t\treturn io.waitingToSendSize();\n\t}\n\n\t@Override\n\tpublic int write(ByteBuffer buff) throws IOException {\n\t\tTLSStatus stat = tlsWrapper.getStatus();\n\n\t\t// The loop below falls into infinite loop for some reason.\n\t\t// Let's try to detect it here and recover.\n\t\t// Looks like for some reason tlsWrapper.getStatus() sometimes starts to\n\t\t// return\n\t\t// NEED_READ status all the time and the loop never ends.\n\t\tint loop_cnt = 0;\n\n\t\tboolean breakNow = true;\n\n\t\twhile (((stat == TLSStatus.NEED_WRITE) || (stat == TLSStatus.NEED_READ)) && (++loop_cnt < max_loop_runs)) {\n\t\t\tswitch (stat) {\n\t\t\t\tcase NEED_WRITE:\n\t\t\t\t\twriteBuff(ByteBuffer.allocate(0), loop_cnt);\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase NEED_READ:\n\t\t\t\t\t// We can get NEED_READ TLS status while there are data awaiting to be\n\t\t\t\t\t// sent thru network connection - we need to force sending data and to break\n\t\t\t\t\t// from this loop\n\t\t\t\t\tif (io.waitingToSend()) {\n\t\t\t\t\t\tio.write(null);\n\n\t\t\t\t\t\t// it appears only during handshake so force break only in this case\n\t\t\t\t\t\tif (tlsWrapper.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING &&\n\t\t\t\t\t\t\t\t(buff == null || !buff.hasRemaining())) {\n\t\t\t\t\t\t\tbreakNow = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// I wonder if some real data can be read from the socket here (and we\n\t\t\t\t\t// would\n\t\t\t\t\t// loose the data) or this is just TLS stuff here.....\n\t\t\t\t\tByteBuffer rbuff = read(ByteBuffer.allocate(tlsWrapper.getNetBuffSize()));\n\n\t\t\t\t\tbreak;\n\n\t\t\t\t// case CLOSED:\n\t\t\t\t// if (tlsWrapper.getStatus() == TLSStatus.CLOSED) {\n\t\t\t\t// if (log.isLoggable(Level.FINER)) {\n\t\t\t\t// log.finer(\"TLS Socket closed...\");\n\t\t\t\t// }\n\t\t\t\t// throw new EOFException(\"Socket has been closed.\");\n\t\t\t\t// } // end of if (tlsWrapper.getStatus() == TLSStatus.CLOSED)\n\t\t\t\t// break;\n\t\t\t\tdefault:\n\t\t\t}\n\n\t\t\tstat = tlsWrapper.getStatus();\n\n\t\t\t// We can get NEED_READ TLS status while there are data awaiting to be\n\t\t\t// sent thru network connection - we need to force sending data and to break\n\t\t\t// from this loop\n\t\t\tif (breakNow) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (loop_cnt > (max_loop_runs / 2)) {\n\t\t\tlog.log(Level.FINE,\n\t\t\t\t\t\"Infinite loop detected in write(buff) TLS code, tlsWrapper.getStatus(): {0}, io: {1}\",\n\t\t\t\t\tnew Object[]{tlsWrapper.getStatus(), toString()});\n\n\t\t\t// Let's close the connection now\n\t\t\tthrow new EOFException(\"Socket has been closed due to TLS problems.\");\n\t\t}\n\n\t\tif (tlsWrapper.getStatus() == TLSStatus.CLOSED) {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.finer(\"TLS Socket closed...\");\n\t\t\t}\n\n\t\t\tthrow new EOFException(\"Socket has been closed.\");\n\t\t} // end of if (tlsWrapper.getStatus() == TLSStatus.CLOSED)\n\n\t\tint result = -1;\n\n\t\tif (buff == null) {\n\t\t\tresult = io.write(null);\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.log(Level.FINER, \"TLS - Writing data, remaining: {0}, loop_cnt: {1}, TLSIO: {2}, tlsWrapper: {3}\",\n\t\t\t\t\t\tnew Object[]{buff.remaining(), loop_cnt, toString(), String.valueOf(tlsWrapper)});\n\t\t\t}\n\n\t\t\tresult = writeBuff(buff, loop_cnt);\n\t\t}\n\n\t\t// if (isRemoteAddress(\"81.142.228.219\")) {\n\t\t// log.warning(\"TLS - Writing data, remaining: \" + buff.remaining());\n\t\t// }\n\t\t// if (isRemoteAddress(\"81.142.228.219\")) {\n\t\t// log.warning(\"TLS - written: \" + result);\n\t\t// }\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic void setLogId(String logId) {\n\t\tio.setLogId(logId);\n\t}\n\n\tpublic void processHandshake(byte[] data) throws IOException {\n\t\tdecodeData(ByteBuffer.wrap(data));\n\t}\n\n\tprivate ByteBuffer decodeData(ByteBuffer input) throws IOException {\n\t\tTLSStatus stat = null;\n\t\tboolean continueLoop = true;\n\n\t\t// input.flip();\n\t\t// do_loop:\n\t\tdo {\n\n\t\t\t// if (log.isLoggable(Level.FINER)) {\n\t\t\t// log.finer(\"Decoding data: \" + input.remaining());\n\t\t\t// log.finer(\"input.capacity()=\" + input.capacity());\n\t\t\t// log.finer(\"input.remaining()=\" + input.remaining());\n\t\t\t// log.finer(\"input.limit()=\" + input.limit());\n\t\t\t// log.finer(\"input.position()=\" + input.position());\n\t\t\t// log.finer(\"tlsInput.capacity()=\" + tlsInput.capacity());\n\t\t\t// log.finer(\"tlsInput.remaining()=\" + tlsInput.remaining());\n\t\t\t// log.finer(\"tlsInput.limit()=\" + tlsInput.limit());\n\t\t\t// log.finer(\"tlsInput.position()=\" + tlsInput.position());\n\t\t\t// }\n\t\t\ttlsInput = tlsWrapper.unwrap(input, tlsInput);\n\n\t\t\t// if (log.isLoggable(Level.FINEST)) {\n\t\t\t// int netSize = tlsWrapper.getPacketBuffSize();\n\t\t\t//\n\t\t\t// log.finer(\"tlsWrapper.getStatus() = \" + tlsWrapper.getStatus().name());\n\t\t\t// log.finer(\"PacketBuffSize=\" + netSize);\n\t\t\t// log.finer(\"input.capacity()=\" + input.capacity());\n\t\t\t// log.finer(\"input.remaining()=\" + input.remaining());\n\t\t\t// log.finer(\"input.limit()=\" + input.limit());\n\t\t\t// log.finer(\"input.position()=\" + input.position());\n\t\t\t// log.finer(\"tlsInput.capacity()=\" + tlsInput.capacity());\n\t\t\t// log.finer(\"tlsInput.remaining()=\" + tlsInput.remaining());\n\t\t\t// log.finer(\"tlsInput.limit()=\" + tlsInput.limit());\n\t\t\t// log.finer(\"tlsInput.position()=\" + tlsInput.position());\n\t\t\t// }\n\t\t\t// if (input.hasRemaining()) {\n\t\t\t// input.compact();\n\t\t\t// }// end of if (input.hasRemaining())\n\t\t\tswitch (tlsWrapper.getStatus()) {\n\t\t\t\tcase NEED_WRITE:\n\t\t\t\t\twriteBuff(ByteBuffer.allocate(0), 0);\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase UNDERFLOW:\n\n\t\t\t\t\t// if (log.isLoggable(Level.FINER) && !log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t// int netSize = tlsWrapper.getPacketBuffSize();\n\t\t\t\t\t// log.finer(\"tlsWrapper.getStatus() = UNDERFLOW\");\n\t\t\t\t\t// log.finer(\"PacketBuffSize=\" + netSize);\n\t\t\t\t\t// log.finer(\"input.capacity()=\" + input.capacity());\n\t\t\t\t\t// log.finer(\"input.remaining()=\" + input.remaining());\n\t\t\t\t\t// log.finer(\"input.limit()=\" + input.limit());\n\t\t\t\t\t// log.finer(\"input.position()=\" + input.position());\n\t\t\t\t\t// log.finer(\"tlsInput.capacity()=\" + tlsInput.capacity());\n\t\t\t\t\t// log.finer(\"tlsInput.remaining()=\" + tlsInput.remaining());\n\t\t\t\t\t// log.finer(\"tlsInput.limit()=\" + tlsInput.limit());\n\t\t\t\t\t// log.finer(\"tlsInput.position()=\" + tlsInput.position());\n\t\t\t\t\t// }\n\t\t\t\t\t// Obtain more inbound network data for src,\n\t\t\t\t\t// then retry the operation.\n\t\t\t\t\t// If there is some data ready to read, let's try to read it before we\n\t\t\t\t\t// increase\n\t\t\t\t\t// the buffer size\n\t\t\t\t\t// throw new BufferUnderflowException();\n\t\t\t\t\tif (tlsInput.capacity() == tlsInput.remaining()) {\n\t\t\t\t\t\tthrow new BufferUnderflowException();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tinput.compact();\n\t\t\t\t\t\tcontinueLoop = false;\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase CLOSED:\n\n\t\t\t\t\t// if (tlsWrapper.getStatus() == TLSStatus.CLOSED) {\n\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\tlog.finer(\"TLS Socket closed...\" + toString());\n\t\t\t\t\t}\n\n\t\t\t\t\t//throw new EOFException(\"Socket has been closed.\");\n\n\t\t\t\t\t// } // end of if (tlsWrapper.getStatus() == TLSStatus.CLOSED)\n\t\t\t\t\t// break do_loop;\n\t\t\t\t\t// break;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t} // end of switch (tlsWrapper.getStatus())\n\n\t\t\tstat = tlsWrapper.getStatus();\n\t\t} while (continueLoop && ((stat == TLSStatus.NEED_READ) || (stat == TLSStatus.OK)) && input.hasRemaining());\n\n\t\tif (continueLoop) {\n\t\t\tif (input.hasRemaining()) {\n\t\t\t\tinput.rewind();\n\t\t\t} else {\n\t\t\t\tinput.clear();\n\t\t\t}\n\t\t}\n\n\t\ttlsInput.flip();\n\n\t\treturn tlsInput;\n\t}\n\n\tprivate int writeBuff(ByteBuffer buff, int loop_cnt) throws IOException {\n\t\tint result = 0;\n\t\tint wr = 0;\n\n\t\t// The loop below falls into infinite loop for some reason.\n\t\t// Let's try to detect it here and recover.\n\t\t// -- After some tests....\n\t\t// Looks like the cause has been detected. Sometimes the loop\n\t\t// below is executed a few times for some reason. It happens that\n\t\t// the tlsWarpper.getStatus() returns NEED_READ and it doesn't\n\t\t// accept any more data from the input buffer.\n\t\t// The proper handling would need reading from the socket to\n\t\t// reset TLS to the correct state, but this involves another problems.\n\t\t// What to do with possible user data received in such a call?\n\t\t// It happens extremely rarely and is hard to diagnose. Let's leave it\n\t\t// as it is now which just causes such connections to be closed.\n\t\tdo {\n\t\t\tif (tlsWrapper.getStatus() == TLSStatus.NEED_READ) {\n\n\t\t\t\t// I wonder if some real data can be read from the socket here (and we\n\t\t\t\t// would\n\t\t\t\t// loose the data) or this is just TLS stuff here.....\n\t\t\t\tByteBuffer rbuff = read(ByteBuffer.allocate(tlsWrapper.getNetBuffSize()));\n\t\t\t\tif ((!TLS_WAIT_FOR_HANDSHAKE_NOT_READY) && loop_cnt > 2 && tlsWrapper.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP &&\n\t\t\t\t\t\ttlsWrapper.getStatus() == TLSStatus.NEED_READ) {\n\t\t\t\t\tthrow new IOException(\"TLS handshake not established!\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tByteBuffer tlsOutput = ByteBuffer.allocate(tlsWrapper.getNetBuffSize());\n\n\t\t\t// Not sure if this is really needed, I guess not...\n\t\t\ttlsOutput.clear();\n\t\t\ttlsWrapper.wrap(buff, tlsOutput);\n\n\t\t\tif (tlsWrapper.getStatus() == TLSStatus.CLOSED) {\n\t\t\t\tthrow new EOFException(\"Socket has been closed.\");\n\t\t\t} // end of if (tlsWrapper.getStatus() == TLSStatus.CLOSED)\n\n\t\t\ttlsOutput.flip();\n\t\t\twr = io.write(tlsOutput);\n\t\t\tresult += wr;\n\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.log(Level.FINER, \"TLS - Writing data, remaining: {0}, run {1} of {2}, TLSIO: {3}, tlsWrapper: {4}\",\n\t\t\t\t\t\tnew Object[]{buff.remaining(), loop_cnt, max_loop_runs, toString(), String.valueOf(tlsWrapper)});\n\t\t\t}\n\t\t\ttlsWrapper.notifyIfHandshakeFinished();\n\t\t} while (buff.hasRemaining() && (++loop_cnt < max_loop_runs));\n\n\t\tif (loop_cnt > (max_loop_runs / 2)) {\n\t\t\tlog.log(Level.CONFIG,\n\t\t\t\t\t\"Infinite loop detected in writeBuff(buff) TLS code, tlsWrapper.getStatus(): {0}, buff.remaining(): {1}, ran {2} times, io: {3}\",\n\t\t\t\t\tnew Object[]{tlsWrapper.getStatus(), buff.remaining(), loop_cnt, toString()});\n\n\t\t\t// Let's close the connection now\n\t\t\tthrow new EOFException(\"Socket has been closed due to TLS problems.\");\n\t\t}\n\n\t\tif (tlsWrapper.getStatus() == TLSStatus.NEED_WRITE && (loop_cnt * 10) < max_loop_runs) {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"TLS - Recursive: Writing data, remaining: {0}, buff capacity: {1}, run {2} of {3}, TLSIO: {4}, tlsWrapper: {5}\",\n\t\t\t\t\t\tnew Object[]{buff.remaining(), buff.capacity(), loop_cnt, max_loop_runs, toString(), String.valueOf(tlsWrapper)});\n\t\t\t}\n\n\t\t\twriteBuff(ByteBuffer.allocate(0), ++loop_cnt);\n\t\t} // end of if ()\n\n\t\treturn result;\n\t}\n} // TLSIO\n"
  },
  {
    "path": "src/main/java/tigase/io/TLSIOIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.io;\n\nimport java.io.IOException;\n\npublic interface TLSIOIfc {\n\n\tvoid processHandshake(byte[] data) throws IOException;\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/io/TLSStatus.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.io;\n\n/**\n * Describe class TLSStatus here.\n * <br>\n * Created: Sat Mar  5 09:28:45 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic enum TLSStatus {\n\n\tNEED_WRITE,\n\tNEED_READ,\n\tUNDERFLOW,\n\tCLOSED,\n\tOK;\n\n} // TLSStatus\n"
  },
  {
    "path": "src/main/java/tigase/io/TLSUtil.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.io;\n\nimport tigase.osgi.ModulesManagerImpl;\n\nimport java.security.KeyStore;\nimport java.security.cert.CertificateParsingException;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.io.CertificateContainerIfc.CERTIFICATE_CONTAINER_CLASS_KEY;\nimport static tigase.io.CertificateContainerIfc.CERTIFICATE_CONTAINER_CLASS_VAL;\nimport static tigase.io.SSLContextContainerIfc.SSL_CONTAINER_CLASS_KEY;\nimport static tigase.io.SSLContextContainerIfc.SSL_CONTAINER_CLASS_VAL;\n\n/**\n * Describe class TLSUtil here.\n * <br>\n * Created: Mon Jan 23 14:21:31 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic abstract class TLSUtil {\n\n\tprivate static final Logger log = Logger.getLogger(TLSUtil.class.getName());\n\tprivate static CertificateContainerIfc certificateContainer = null;\n\t//private static Map<String, SSLContextContainerIfc> sslContexts =\n//  new HashMap<String, SSLContextContainerIfc>();\n\tprivate static SSLContextContainerIfc sslContextContainer = null;\n\n\tpublic static void addCertificate(Map<String, String> params) throws CertificateParsingException {\n\t\tcertificateContainer.addCertificates(params);\n\t}\n\n\tpublic static void configureSSLContext(Map<String, Object> params) {\n\t\t// we should initialize this only once\n\t\tif (sslContextContainer != null) {\n\t\t\treturn;\n\t\t}\n\n\t\tString sslCC_class = (String) params.get(SSL_CONTAINER_CLASS_KEY);\n\t\tString certC_class = (String) params.get(CERTIFICATE_CONTAINER_CLASS_KEY);\n\n\t\tif (sslCC_class == null) {\n\t\t\tsslCC_class = SSL_CONTAINER_CLASS_VAL;\n\t\t}\n\t\tif (sslCC_class.equals(\"tigase.io.jdk18.SNISSLContextContainer\")) {\n\t\t\tlog.log(Level.WARNING, \"You are using '\" + sslCC_class + \"' as \" + SSL_CONTAINER_CLASS_KEY + \".\\n\" +\n\t\t\t\t\t\"This class is not available as SNI support was moved to SSLContextContainer\");\n\t\t\tsslCC_class = SSL_CONTAINER_CLASS_VAL;\n\t\t}\n\t\tif (sslCC_class.equals(\"tigase.extras.io.PEMSSLContextContainer\")) {\n\t\t\tlog.log(Level.WARNING, \"You are using '\" + sslCC_class + \"' as \" + SSL_CONTAINER_CLASS_KEY + \".\\n\" +\n\t\t\t\t\t\"This class is not available any more. To keep using this feature please replace configuration\\n\" +\n\t\t\t\t\t\"of \" + SSL_CONTAINER_CLASS_KEY + \" to \" + sslCC_class + \" with \" +\n\t\t\t\t\tCERTIFICATE_CONTAINER_CLASS_KEY + \"\\n\" + \"set to tigase.extras.io.PEMCertificateContainer\");\n\t\t\tsslCC_class = SSL_CONTAINER_CLASS_VAL;\n\t\t\tcertC_class = \"tigase.extras.io.PEMCertificateContainer\";\n\t\t}\n\t\tif (certC_class == null) {\n\t\t\tcertC_class = CERTIFICATE_CONTAINER_CLASS_VAL;\n\t\t}\n\n\t\ttry {\n\t\t\tcertificateContainer = (CertificateContainerIfc) ModulesManagerImpl.getInstance()\n\t\t\t\t\t.forName(certC_class)\n\t\t\t\t\t.newInstance();\n\t\t\tcertificateContainer.init(params);\n\t\t\tsslContextContainer = (SSLContextContainerIfc) ModulesManagerImpl.getInstance()\n\t\t\t\t\t.forName(sslCC_class)\n\t\t\t\t\t.getDeclaredConstructor(CertificateContainerIfc.class)\n\t\t\t\t\t.newInstance(certificateContainer);\n\t\t\t//sslContextContainer = (SSLContextContainerIfc) Class.forName(sslCC_class).newInstance();\n\t\t\tsslContextContainer.start();\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.SEVERE, \"Can not initialize SSL Container: \" + sslCC_class, e);\n\t\t\tsslContextContainer = null;\n\t\t}\n\t}\n\n\t/**\n\t * Method returns singleton instance of class implementing CertificateContainterIfc responsible for caching SSL\n\t * certificates in memory.\n\t *\n\t */\n\tpublic static CertificateContainerIfc getCertificateContainer() {\n\t\treturn certificateContainer;\n\t}\n\n\t/**\n\t * Method returns singleton instance of class implementing SSLContextContainerIfc responsible for caching SSLContext\n\t * instances.\n\t * <br>\n\t * This instance should be wrapped by new instance of SSLContextContainer if method getSSLContext will be used with\n\t * TrustManager array passed!\n\t *\n\t */\n\tpublic static SSLContextContainerIfc getRootSslContextContainer() {\n\t\treturn sslContextContainer;\n\t}\n\n\tpublic static KeyStore getTrustStore() {\n\t\treturn sslContextContainer.getTrustStore();\n\t}\n}    // TLSUtil\n\n"
  },
  {
    "path": "src/main/java/tigase/io/TLSWrapper.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.io;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.cert.CertCheckResult;\n\nimport javax.net.ssl.SSLEngineResult;\nimport javax.net.ssl.SSLException;\nimport javax.net.ssl.SSLPeerUnverifiedException;\nimport java.nio.ByteBuffer;\nimport java.security.cert.Certificate;\n\npublic interface TLSWrapper {\n\n\tint bytesConsumed();\n\n\tvoid close() throws SSLException;\n\n\tint getAppBuffSize();\n\n\tCertCheckResult getCertificateStatus(boolean revocationEnabled, SSLContextContainerIfc sslContextContainer);\n\n\tSSLEngineResult.HandshakeStatus getHandshakeStatus();\n\n\tCertificate[] getLocalCertificates();\n\n\tint getNetBuffSize();\n\n\tint getPacketBuffSize();\n\n\tCertificate[] getPeerCertificates() throws SSLPeerUnverifiedException;\n\n\tTLSStatus getStatus();\n\n\tbyte[] getTlsUniqueBindingData();\n\n\tbyte[] getTlsExporterBindingData();\n\n\tboolean isClientMode();\n\n\tboolean isNeedClientAuth();\n\n\tvoid setDebugId(String id);\n\n\tByteBuffer unwrap(ByteBuffer net, ByteBuffer app) throws SSLException;\n\n\tboolean wantClientAuth();\n\n\tvoid wrap(ByteBuffer app, ByteBuffer net) throws SSLException;\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"This method might be replaced by checking if handshake was finished on TLSIO level but that requires API changes.\")\n\tdefault void notifyIfHandshakeFinished() {}\n}\n"
  },
  {
    "path": "src/main/java/tigase/io/TelnetClient.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.io;\n\nimport tigase.util.log.LogFormatter;\n\nimport java.io.*;\nimport java.net.InetSocketAddress;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.CharBuffer;\nimport java.nio.channels.SocketChannel;\nimport java.nio.charset.Charset;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.logging.Level;\nimport java.util.logging.LogManager;\nimport java.util.logging.Logger;\n\nimport static tigase.io.SSLContextContainerIfc.JKS_KEYSTORE_FILE_KEY;\nimport static tigase.io.SSLContextContainerIfc.JKS_KEYSTORE_PWD_KEY;\n\n/**\n * This is sample class demonstrating how to use <code>tigase.io</code> library for TLS/SSL client connection. This is\n * simple telnet client class which can connect to remote server using plain connection or SSL.\n * <br>\n * Created: Sun Aug  6 15:14:49 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class TelnetClient\n\t\timplements SampleSocketThread.SocketHandler {\n\n\tprivate static final Logger log = Logger.getLogger(\"tigase.io.TelnetClient\");\n\tprivate static final Charset coder = Charset.forName(\"UTF-8\");\n\tprivate static boolean continuous = false;\n\tprivate static boolean debug = false;\n\tprivate static long delay = 100;\n\tprivate static String file = null;\n\tprivate static String hostname = \"localhost\";\n\tprivate static int port = 7777;\n\tprivate static boolean ssl = false;\n\n\tprivate IOInterface iosock = null;\n\tprivate SampleSocketThread reader = null;\n\n\tpublic static String help() {\n\t\treturn \"\\n\" + \"Parameters:\\n\" + \" -?                this help message\\n\" + \" -h hostname       host name\\n\" +\n\t\t\t\t\" -p port           port number\\n\" + \" -ssl              turn SSL on for all connections\\n\" +\n\t\t\t\t\" -f file           file with content to send to remote host\\n\" +\n\t\t\t\t\" -c                continuous sending file content\\n\" +\n\t\t\t\t\" -t millis         delay between sending file content\\n\" +\n\t\t\t\t\" -v                prints server version info\\n\" + \" -d [true|false]   turn on|off debug mode\\n\";\n\t}\n\n\tpublic static void main(final String[] args) throws Exception {\n\t\tparseParams(args);\n\n\t\tif (debug) {\n\t\t\tturnDebugOn();\n\t\t}    // end of if (debug)\n\n\t\tif (ssl) {\n\t\t\tMap<String, Object> tls_params = new LinkedHashMap<String, Object>();\n\n\t\t\ttls_params.put(JKS_KEYSTORE_FILE_KEY, \"certs/keystore\");\n\t\t\ttls_params.put(JKS_KEYSTORE_PWD_KEY, \"keystore\");\n\t\t\tTLSUtil.configureSSLContext(tls_params);\n\t\t}    // end of if (ssl)\n\n\t\tTelnetClient client = new TelnetClient(hostname, port);\n\t\tInputStreamReader str_reader = new InputStreamReader(System.in);\n\n\t\tif (file != null) {\n\t\t\tFileReader fr = new FileReader(file);\n\t\t\tchar[] file_buff = new char[64 * 1024];\n\t\t\tint res = -1;\n\n\t\t\twhile ((res = fr.read(file_buff)) != -1) {\n\t\t\t\tclient.writeData(new String(file_buff, 0, res));\n\t\t\t}    // end of while ((res = fr.read(buff)) != -1)\n\n\t\t\tfr.close();\n\t\t}      // end of if (file != null)\n\n\t\tchar[] buff = new char[1024];\n\n\t\tfor (; ; ) {\n\t\t\tint res = str_reader.read(buff);\n\n\t\t\tclient.writeData(new String(buff, 0, res));\n\t\t}    // end of for (;;)\n\t}\n\n\tpublic static void parseParams(final String[] args) throws Exception {\n\t\tif ((args != null) && (args.length > 0)) {\n\t\t\tfor (int i = 0; i < args.length; i++) {\n\t\t\t\tif (args[i].equals(\"-?\")) {\n\t\t\t\t\tSystem.out.print(help());\n\t\t\t\t\tSystem.exit(0);\n\t\t\t\t}      // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-v\")) {\n\t\t\t\t\tSystem.out.print(version());\n\t\t\t\t\tSystem.exit(0);\n\t\t\t\t}      // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-f\")) {\n\t\t\t\t\tif (i + 1 == args.length) {\n\t\t\t\t\t\tSystem.out.print(help());\n\t\t\t\t\t\tSystem.exit(1);\n\t\t\t\t\t}    // end of if (i+1 == args.length)\n\t\t\t\t\telse {\n\t\t\t\t\t\tfile = args[++i];\n\t\t\t\t\t}    // end of else\n\t\t\t\t}      // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-h\")) {\n\t\t\t\t\tif (i + 1 == args.length) {\n\t\t\t\t\t\tSystem.out.print(help());\n\t\t\t\t\t\tSystem.exit(1);\n\t\t\t\t\t}    // end of if (i+1 == args.length)\n\t\t\t\t\telse {\n\t\t\t\t\t\thostname = args[++i];\n\t\t\t\t\t}    // end of else\n\t\t\t\t}      // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-p\")) {\n\t\t\t\t\tif (i + 1 == args.length) {\n\t\t\t\t\t\tSystem.out.print(help());\n\t\t\t\t\t\tSystem.exit(1);\n\t\t\t\t\t}    // end of if (i+1 == args.length)\n\t\t\t\t\telse {\n\t\t\t\t\t\tport = Integer.decode(args[++i]);\n\t\t\t\t\t}    // end of else\n\t\t\t\t}      // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-d\")) {\n\t\t\t\t\tif ((i + 1 == args.length) || args[i + 1].startsWith(\"-\")) {\n\t\t\t\t\t\tdebug = true;\n\t\t\t\t\t}    // end of if (i+1 == args.length)\n\t\t\t\t\telse {\n\t\t\t\t\t\t++i;\n\t\t\t\t\t\tdebug = (args[i].charAt(0) != '-') && (args[i].equals(\"true\") || args[i].equals(\"yes\"));\n\t\t\t\t\t}    // end of else\n\t\t\t\t}      // end of if (args[i].equals(\"-d\"))\n\n\t\t\t\tif (args[i].equals(\"-c\")) {\n\t\t\t\t\tif ((i + 1 == args.length) || args[i + 1].startsWith(\"-\")) {\n\t\t\t\t\t\tcontinuous = true;\n\t\t\t\t\t}    // end of if (i+1 == args.length)\n\t\t\t\t\telse {\n\t\t\t\t\t\t++i;\n\t\t\t\t\t\tcontinuous = (args[i].charAt(0) != '-') && (args[i].equals(\"true\") || args[i].equals(\"yes\"));\n\t\t\t\t\t}    // end of else\n\t\t\t\t}      // end of if (args[i].equals(\"-c\"))\n\n\t\t\t\tif (args[i].equals(\"-ssl\")) {\n\t\t\t\t\tif ((i + 1 == args.length) || args[i + 1].startsWith(\"-\")) {\n\t\t\t\t\t\tssl = true;\n\t\t\t\t\t}    // end of if (i+1 == args.length)\n\t\t\t\t\telse {\n\t\t\t\t\t\t++i;\n\t\t\t\t\t\tssl = (args[i].charAt(0) != '-') && (args[i].equals(\"true\") || args[i].equals(\"yes\"));\n\t\t\t\t\t}    // end of else\n\t\t\t\t}      // end of if (args[i].equals(\"-ssl\"))\n\t\t\t}        // end of for (int i = 0; i < args.length; i++)\n\t\t}\n\t}\n\n\tpublic static void turnDebugOn() {\n\t\tMap<String, String> properties = new HashMap<String, String>();\n\n\t\tproperties.put(\".level\", \"ALL\");\n\t\tproperties.put(\"handlers\", \"java.util.logging.ConsoleHandler\");\n\t\tproperties.put(\"java.util.logging.ConsoleHandler.formatter\", LogFormatter.class.getName());\n\t\tproperties.put(\"java.util.logging.ConsoleHandler.level\", \"ALL\");\n\n\t\tSet<Map.Entry<String, String>> entries = properties.entrySet();\n\t\tStringBuilder buff = new StringBuilder();\n\n\t\tfor (Map.Entry<String, String> entry : entries) {\n\t\t\tbuff.append(entry.getKey() + \"=\" + entry.getValue() + \"\\n\");\n\t\t}\n\n\t\ttry {\n\t\t\tfinal ByteArrayInputStream bis = new ByteArrayInputStream(buff.toString().getBytes());\n\n\t\t\tLogManager.getLogManager().readConfiguration(bis);\n\t\t\tbis.close();\n\t\t} catch (IOException e) {\n\t\t\tlog.log(Level.SEVERE, \"Can not configure logManager\", e);\n\t\t}    // end of try-catch\n\t}\n\n\tpublic static String version() {\n\t\treturn \"\\n\" + \"-- \\n\" + \"Tigase XMPP Telnet, version: \" +\n\t\t\t\tTelnetClient.class.getPackage().getImplementationVersion() + \"\\n\" +\n\t\t\t\t\"Author:  Artur Hefczyc <artur.hefczyc@tigase.org>\\n\" + \"-- \\n\";\n\t}\n\n\t/**\n\t * Creates a new <code>TelnetClient</code> instance.\n\t */\n\tpublic TelnetClient(String hostname, int port) throws Exception {\n\t\treader = new SampleSocketThread(this);\n\t\treader.start();\n\n\t\tSocketChannel sc = SocketChannel.open(new InetSocketAddress(hostname, port));\n\n\t\t// Basic channel configuration\n\t\tiosock = new SocketIO(sc);\n\n\t\tif (ssl) {\n\t\t\tSSLContextContainerIfc sslContextContainer = TLSUtil.getRootSslContextContainer();\n\t\t\tiosock = new TLSIO(iosock,\n\t\t\t\t\t\t\t   new JcaTLSWrapper(sslContextContainer.getSSLContext(\"SSL\", null, true), null, null, 0,\n\t\t\t\t\t\t\t\t\t\t\t\t true, false), ByteOrder.BIG_ENDIAN);\n\t\t}    // end of if (ssl)\n\n\t\treader.addIOInterface(iosock);\n\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.finer(\"Registered new client socket: \" + sc);\n\t\t}\n\t}\n\n\tpublic void handleIOInterface(IOInterface ioifc) throws IOException {\n\t\tByteBuffer socketInput = ByteBuffer.allocate(ioifc.getSocketChannel().socket().getReceiveBufferSize());\n\t\tByteBuffer tmpBuffer = ioifc.read(socketInput);\n\n\t\tif (ioifc.bytesRead() > 0) {\n\t\t\ttmpBuffer.flip();\n\n\t\t\tCharBuffer cb = coder.decode(tmpBuffer);\n\n\t\t\ttmpBuffer.clear();\n\n\t\t\tif (cb != null) {\n\t\t\t\tSystem.out.print(new String(cb.array()));\n\t\t\t}    // end of if (cb != null)\n\t\t}      // end of if (socketIO.bytesRead() > 0)\n\n\t\tif (!ioifc.isConnected()) {\n\t\t\tthrow new EOFException(\"Channel has been closed.\");\n\t\t}\n\n\t\treader.addIOInterface(ioifc);\n\t}\n\n\tpublic void handleSocketAccept(SocketChannel sc) {\n\n\t\t// Empty, not needed any implementation for that\n\t}\n\n\tpublic void writeData(String data) throws IOException {\n\t\tByteBuffer dataBuffer = null;\n\n\t\tif ((data != null) && (data.length() > 0)) {\n\t\t\tdataBuffer = coder.encode(CharBuffer.wrap(data));\n\t\t\tiosock.write(dataBuffer);\n\t\t}    // end of if (data == null || data.equals(\"\")) else\n\t}\n}    // TelnetClient\n\n"
  },
  {
    "path": "src/main/java/tigase/io/TelnetServer.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.io;\n\nimport tigase.util.log.LogFormatter;\n\nimport java.io.*;\nimport java.net.InetSocketAddress;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.CharBuffer;\nimport java.nio.channels.SocketChannel;\nimport java.nio.charset.Charset;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.logging.Level;\nimport java.util.logging.LogManager;\nimport java.util.logging.Logger;\n\nimport static tigase.io.SSLContextContainerIfc.*;\n\n/**\n * This is sample class demonstrating how to use <code>tigase.io</code> library for TLS/SSL server connection. This is\n * simple telnet server class which can be run to receive plain connections or SSL connections.\n * <br>\n * Created: Sun Aug  6 22:27:13 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class TelnetServer\n\t\timplements SampleSocketThread.SocketHandler {\n\n\tprivate static final Logger log = Logger.getLogger(\"tigase.io.TelnetServer\");\n\tprivate static final Charset coder = Charset.forName(\"UTF-8\");\n\tprivate static boolean continuous = false;\n\tprivate static boolean debug = false;\n\tprivate static long delay = 100;\n\tprivate static String file = null;\n\tprivate static String hostname = \"localhost\";\n\tprivate static int port = 7777;\n\tprivate static boolean ssl = false;\n\n\tprivate IOInterface iosock = null;\n\tprivate SampleSocketThread reader = null;\n\n\tpublic static String help() {\n\t\treturn \"\\n\" + \"Parameters:\\n\" + \" -?                this help message\\n\" + \" -h hostname       host name\\n\" +\n\t\t\t\t\" -p port           port number\\n\" + \" -ssl              turn SSL on for all connections\\n\" +\n\t\t\t\t\" -f file           file with content to send to remote host\\n\" +\n\t\t\t\t\" -c                continuous sending file content\\n\" +\n\t\t\t\t\" -t millis         delay between sending file content\\n\" +\n\t\t\t\t\" -v                prints server version info\\n\" + \" -d [true|false]   turn on|off debug mode\\n\";\n\t}\n\n\tpublic static void main(final String[] args) throws Exception {\n\t\tparseParams(args);\n\n\t\tif (debug) {\n\t\t\tturnDebugOn();\n\t\t}    // end of if (debug)\n\n\t\tif (ssl) {\n\t\t\tMap<String, Object> tls_params = new LinkedHashMap<String, Object>();\n\n\t\t\ttls_params.put(JKS_KEYSTORE_FILE_KEY, \"certs/keystore\");\n\t\t\ttls_params.put(JKS_KEYSTORE_PWD_KEY, \"keystore\");\n\t\t\ttls_params.put(TRUSTSTORE_FILE_KEY, \"certs/truststore\");\n\t\t\ttls_params.put(TRUSTSTORE_PWD_KEY, \"truststore\");\n\t\t\tTLSUtil.configureSSLContext(tls_params);\n\t\t}    // end of if (ssl)\n\n\t\tTelnetServer server = new TelnetServer(port);\n\n\t\tserver.run();\n\t}\n\n\tpublic static void parseParams(final String[] args) throws Exception {\n\t\tif ((args != null) && (args.length > 0)) {\n\t\t\tfor (int i = 0; i < args.length; i++) {\n\t\t\t\tif (args[i].equals(\"-?\")) {\n\t\t\t\t\tSystem.out.print(help());\n\t\t\t\t\tSystem.exit(0);\n\t\t\t\t}      // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-v\")) {\n\t\t\t\t\tSystem.out.print(version());\n\t\t\t\t\tSystem.exit(0);\n\t\t\t\t}      // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-f\")) {\n\t\t\t\t\tif (i + 1 == args.length) {\n\t\t\t\t\t\tSystem.out.print(help());\n\t\t\t\t\t\tSystem.exit(1);\n\t\t\t\t\t}    // end of if (i+1 == args.length)\n\t\t\t\t\telse {\n\t\t\t\t\t\tfile = args[++i];\n\t\t\t\t\t}    // end of else\n\t\t\t\t}      // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-h\")) {\n\t\t\t\t\tif (i + 1 == args.length) {\n\t\t\t\t\t\tSystem.out.print(help());\n\t\t\t\t\t\tSystem.exit(1);\n\t\t\t\t\t}    // end of if (i+1 == args.length)\n\t\t\t\t\telse {\n\t\t\t\t\t\thostname = args[++i];\n\t\t\t\t\t}    // end of else\n\t\t\t\t}      // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-p\")) {\n\t\t\t\t\tif (i + 1 == args.length) {\n\t\t\t\t\t\tSystem.out.print(help());\n\t\t\t\t\t\tSystem.exit(1);\n\t\t\t\t\t}    // end of if (i+1 == args.length)\n\t\t\t\t\telse {\n\t\t\t\t\t\tport = Integer.decode(args[++i]);\n\t\t\t\t\t}    // end of else\n\t\t\t\t}      // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-d\")) {\n\t\t\t\t\tif ((i + 1 == args.length) || args[i + 1].startsWith(\"-\")) {\n\t\t\t\t\t\tdebug = true;\n\t\t\t\t\t}    // end of if (i+1 == args.length)\n\t\t\t\t\telse {\n\t\t\t\t\t\t++i;\n\t\t\t\t\t\tdebug = (args[i].charAt(0) != '-') && (args[i].equals(\"true\") || args[i].equals(\"yes\"));\n\t\t\t\t\t}    // end of else\n\t\t\t\t}      // end of if (args[i].equals(\"-d\"))\n\n\t\t\t\tif (args[i].equals(\"-c\")) {\n\t\t\t\t\tif ((i + 1 == args.length) || args[i + 1].startsWith(\"-\")) {\n\t\t\t\t\t\tcontinuous = true;\n\t\t\t\t\t}    // end of if (i+1 == args.length)\n\t\t\t\t\telse {\n\t\t\t\t\t\t++i;\n\t\t\t\t\t\tcontinuous = (args[i].charAt(0) != '-') && (args[i].equals(\"true\") || args[i].equals(\"yes\"));\n\t\t\t\t\t}    // end of else\n\t\t\t\t}      // end of if (args[i].equals(\"-c\"))\n\n\t\t\t\tif (args[i].equals(\"-ssl\")) {\n\t\t\t\t\tif ((i + 1 == args.length) || args[i + 1].startsWith(\"-\")) {\n\t\t\t\t\t\tssl = true;\n\t\t\t\t\t}    // end of if (i+1 == args.length)\n\t\t\t\t\telse {\n\t\t\t\t\t\t++i;\n\t\t\t\t\t\tssl = (args[i].charAt(0) != '-') && (args[i].equals(\"true\") || args[i].equals(\"yes\"));\n\t\t\t\t\t}    // end of else\n\t\t\t\t}      // end of if (args[i].equals(\"-ssl\"))\n\t\t\t}        // end of for (int i = 0; i < args.length; i++)\n\t\t}\n\t}\n\n\tpublic static void turnDebugOn() {\n\t\tMap<String, String> properties = new HashMap<String, String>();\n\n\t\tproperties.put(\".level\", \"ALL\");\n\t\tproperties.put(\"handlers\", \"java.util.logging.ConsoleHandler\");\n\t\tproperties.put(\"java.util.logging.ConsoleHandler.formatter\", LogFormatter.class.getName());\n\t\tproperties.put(\"java.util.logging.ConsoleHandler.level\", \"ALL\");\n\n\t\tSet<Map.Entry<String, String>> entries = properties.entrySet();\n\t\tStringBuilder buff = new StringBuilder();\n\n\t\tfor (Map.Entry<String, String> entry : entries) {\n\t\t\tbuff.append(entry.getKey() + \"=\" + entry.getValue() + \"\\n\");\n\t\t}\n\n\t\ttry {\n\t\t\tfinal ByteArrayInputStream bis = new ByteArrayInputStream(buff.toString().getBytes());\n\n\t\t\tLogManager.getLogManager().readConfiguration(bis);\n\t\t\tbis.close();\n\t\t} catch (IOException e) {\n\t\t\tlog.log(Level.SEVERE, \"Can not configure logManager\", e);\n\t\t}    // end of try-catch\n\t}\n\n\tpublic static String version() {\n\t\treturn \"\\n\" + \"-- \\n\" + \"Tigase XMPP Telnet, version: \" +\n\t\t\t\tTelnetServer.class.getPackage().getImplementationVersion() + \"\\n\" +\n\t\t\t\t\"Author:  Artur Hefczyc <artur.hefczyc@tigase.org>\\n\" + \"-- \\n\";\n\t}\n\n\t/**\n\t * Creates a new <code>TelnetServer</code> instance.\n\t */\n\tpublic TelnetServer(int port) throws IOException {\n\t\treader = new SampleSocketThread(this);\n\t\treader.start();\n\t\treader.addForAccept(new InetSocketAddress(port));\n\t}\n\n\t@Override\n\tpublic void handleIOInterface(IOInterface ioifc) throws IOException {\n\t\tByteBuffer socketInput = ByteBuffer.allocate(ioifc.getSocketChannel().socket().getReceiveBufferSize());\n\t\tByteBuffer tmpBuffer = ioifc.read(socketInput);\n\n\t\tif (ioifc.bytesRead() > 0) {\n\t\t\ttmpBuffer.flip();\n\n\t\t\tCharBuffer cb = coder.decode(tmpBuffer);\n\n\t\t\ttmpBuffer.clear();\n\n\t\t\tif (cb != null) {\n\t\t\t\tSystem.out.print(new String(cb.array()));\n\t\t\t}    // end of if (cb != null)\n\t\t}      // end of if (socketIO.bytesRead() > 0)\n\n\t\tif (!ioifc.isConnected()) {\n\t\t\tthrow new EOFException(\"Channel has been closed.\");\n\t\t}\n\n\t\treader.addIOInterface(ioifc);\n\t}\n\n\t@Override\n\tpublic void handleSocketAccept(SocketChannel sc) throws IOException {\n\t\tiosock = new SocketIO(sc);\n\n\t\tif (ssl) {\n\t\t\tSSLContextContainerIfc sslContextContainer = TLSUtil.getRootSslContextContainer();\n\t\t\tiosock = new TLSIO(iosock,\n\t\t\t\t\t\t\t   new JcaTLSWrapper(sslContextContainer.getSSLContext(\"SSL\", null, false), null, null, 0,\n\t\t\t\t\t\t\t\t\t\t\t\t false, false), ByteOrder.BIG_ENDIAN);\n\t\t}    // end of if (ssl)\n\n\t\treader.addIOInterface(iosock);\n\n\t\tif (file != null) {\n\t\t\tFileReader fr = new FileReader(file);\n\t\t\tchar[] file_buff = new char[64 * 1024];\n\t\t\tint res = -1;\n\n\t\t\twhile ((res = fr.read(file_buff)) != -1) {\n\t\t\t\tByteBuffer dataBuffer = coder.encode(CharBuffer.wrap(file_buff, 0, res));\n\n\t\t\t\tiosock.write(dataBuffer);\n\t\t\t}    // end of while ((res = fr.read(buff)) != -1)\n\n\t\t\tfr.close();\n\t\t}      // end of if (file != null)\n\t}\n\n\tpublic void run() throws IOException {\n\t\tInputStreamReader isr = new InputStreamReader(System.in);\n\t\tchar[] buff = new char[1024];\n\n\t\tfor (; ; ) {\n\t\t\tint res = isr.read(buff);\n\n\t\t\tif (iosock != null) {\n\t\t\t\tByteBuffer dataBuffer = coder.encode(CharBuffer.wrap(buff, 0, res));\n\n\t\t\t\tiosock.write(dataBuffer);\n\t\t\t}    // end of if (ioscok != null)\n\t\t\telse {\n\t\t\t\tSystem.err.println(\"Can't write to socket, no open socket.\");\n\t\t\t}    // end of if (ioscok != null) else\n\t\t}      // end of for (;;)\n\t}\n}    // Telnetserver\n\n"
  },
  {
    "path": "src/main/java/tigase/io/ZLibIO.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.io;\n\nimport tigase.stats.StatisticsList;\nimport tigase.util.IOListener;\nimport tigase.util.ZLibWrapper;\n\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.SocketChannel;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Jul 29, 2009 11:58:02 AM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class ZLibIO\n\t\timplements IOInterface {\n\n\tpublic static final String ZLIB_CAPS = \"zlib-caps\";\n\n\tprivate static Logger log = Logger.getLogger(ZLibIO.class.getName());\n\n\tprivate IOInterface io = null;\n\tprivate ZLibWrapper zlib = null;\n\n\tpublic ZLibIO(final IOInterface ioi, final int level) {\n\t\tthis.io = ioi;\n\t\tzlib = new ZLibWrapper();\n\t}\n\n\t@Override\n\tpublic int bytesRead() {\n\t\treturn io.bytesRead();\n\t}\n\n\t@Override\n\tpublic long getBytesSent(boolean reset) {\n\t\treturn io.getBytesSent(reset);\n\t}\n\n\t@Override\n\tpublic long getTotalBytesSent() {\n\t\treturn io.getTotalBytesSent();\n\t}\n\n\t@Override\n\tpublic long getBytesReceived(boolean reset) {\n\t\treturn io.getBytesReceived(reset);\n\t}\n\n\t@Override\n\tpublic long getTotalBytesReceived() {\n\t\treturn io.getTotalBytesReceived();\n\t}\n\n\t@Override\n\tpublic long getBuffOverflow(boolean reset) {\n\t\treturn io.getBuffOverflow(reset);\n\t}\n\n\t@Override\n\tpublic long getTotalBuffOverflow() {\n\t\treturn io.getTotalBuffOverflow();\n\t}\n\n\t@Override\n\tpublic boolean checkCapabilities(String caps) {\n\t\treturn caps.contains(ZLIB_CAPS) || io.checkCapabilities(caps);\n\t}\n\n\t@Override\n\tpublic int getInputPacketSize() throws IOException {\n\t\treturn io.getInputPacketSize();\n\t}\n\n\t@Override\n\tpublic SocketChannel getSocketChannel() {\n\t\treturn io.getSocketChannel();\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list, boolean reset) {\n\t\tif (io != null) {\n\t\t\tio.getStatistics(list, reset);\n\t\t}\n\n\t\tif (zlib != null) {\n\t\t\tlist.add(\"zlibio\", \"Average compression rate\", zlib.averageCompressionRate(), Level.FINE);\n\t\t\tlist.add(\"zlibio\", \"Average decompression rate\", zlib.averageDecompressionRate(), Level.FINE);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isConnected() {\n\t\treturn io.isConnected();\n\t}\n\n\t@Override\n\tpublic boolean isRemoteAddress(String addr) {\n\t\treturn io.isRemoteAddress(addr);\n\t}\n\n\t@Override\n\tpublic ByteBuffer read(ByteBuffer buff) throws IOException {\n\t\tByteBuffer tmpBuffer = io.read(buff);\n\n\t\tif (io.bytesRead() > 0) {\n\t\t\tByteBuffer decompressed_buff = zlib.decompress(tmpBuffer);\n\n\t\t\t// The buffer is reused to it needs to be cleared before it can be\n\t\t\t// used again.\n\t\t\ttmpBuffer.clear();\n\n\t\t\t// System.out.println(\"Decompression rate: \" + zlib.lastDecompressionRate());\n\t\t\treturn decompressed_buff;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void stop() throws IOException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Stop called...\" + toString());\n\t\t}\n\n\t\tio.stop();\n\t\tzlib.end();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"ZLIB: \" + io.toString();\n\t}\n\n\t@Override\n\tpublic boolean waitingToSend() {\n\t\treturn io.waitingToSend();\n\t}\n\n\t@Override\n\tpublic int waitingToSendSize() {\n\t\treturn io.waitingToSendSize();\n\t}\n\n\t@Override\n\tpublic int write(ByteBuffer buff) throws IOException {\n\t\tif (buff == null) {\n\t\t\treturn io.write(null);\n\t\t}\n\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.log(Level.FINER, \"ZLIB - Writing data, remaining: {0}\", buff.remaining());\n\t\t}\n\n\t\tByteBuffer compressed_buff = zlib.compress(buff);\n\n\t\t// System.out.println(\"Compression rate: \" + zlib.lastCompressionRate());\n\t\treturn io.write(compressed_buff);\n\t}\n\n\t@Override\n\tpublic void setLogId(String logId) {\n\t\tio.setLogId(logId);\n\t}\n\n\tpublic void setIOListener(IOListener listener) {\n\t\tzlib.setIOListener(listener);\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/io/repo/CertificateItem.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.io.repo;\n\nimport tigase.cert.CertificateEntry;\nimport tigase.cert.CertificateUtil;\nimport tigase.db.comp.RepositoryItemAbstract;\nimport tigase.server.Command;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\n\nimport java.io.CharArrayReader;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.cert.Certificate;\nimport java.security.cert.CertificateEncodingException;\nimport java.security.cert.X509Certificate;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class CertificateItem\n\t\textends RepositoryItemAbstract {\n\n\tpublic static final String PEM_CERTIFICATE_KEY = \"pem-certificate\";\n\tpublic static final String FINGERPRINT_KEY = \"fingerprint\";\n\tpublic static final String SERIALNUMBER_KEY = \"serial-number\";\n\tpublic static final String IS_DEFAULT_KEY = \"is-default\";\n\tpublic static final String ALIAS_KEY = \"alias\";\n\tpublic static final String REPO_ITEM_ELEM_NAME = \"certificate\";\n\n\tprivate static final Logger log = Logger.getLogger(CertificateItem.class.getName());\n\tprivate String alias;\n\tprivate CertificateEntry entry;\n\tprivate String fingerprint;\n\tprivate boolean isDefault;\n\tprivate String serialNumber;\n\n\tpublic CertificateItem() {\n\t}\n\n\tpublic CertificateItem(String alias, CertificateEntry entry) {\n\t\tObjects.requireNonNull(alias);\n\t\tObjects.requireNonNull(entry);\n\t\tif (\"default\".equals(alias)) {\n\t\t\tthis.isDefault = true;\n\t\t}\n\t\tthis.alias = CertificateUtil.getCertCName((X509Certificate) entry.getCertificate().get());\n\t\tthis.entry = entry;\n\t\ttry {\n\t\t\tif (entry.getCertificate().isPresent()) {\n\t\t\t\tfinal Certificate certificate = entry.getCertificate().get();\n\t\t\t\tfingerprint = CertificateUtil.getCertificateFingerprint(certificate);\n\t\t\t\tCertificateUtil.getCertificateSerialNumber(certificate)\n\t\t\t\t\t\t.ifPresent(serialNumber -> this.serialNumber = serialNumber.toString(16));\n\t\t\t}\n\t\t} catch (CertificateEncodingException | NoSuchAlgorithmException e) {\n\t\t\tlog.log(Level.WARNING, \"Failing creating Certificate item\", e);\n\t\t}\n\t}\n\n\tpublic Optional<String> getSerialNumber() {\n\t\treturn Optional.ofNullable(serialNumber);\n\t}\n\n\tpublic void setSerialNumber(String serialNumber) {\n\t\tthis.serialNumber = serialNumber;\n\t}\n\n\tpublic Optional<String> getFingerprint() {\n\t\treturn Optional.ofNullable(fingerprint);\n\t}\n\n\tpublic void setFingerprint(String fingerprint) {\n\t\tthis.fingerprint = fingerprint;\n\t}\n\n\t@Override\n\tpublic String getKey() {\n\t\treturn getAlias();\n\t}\n\n\t@Override\n\tprotected void setKey(String key) {\n\t\tsetAlias(key);\n\t}\n\n\t@Override\n\tpublic String getElemName() {\n\t\treturn REPO_ITEM_ELEM_NAME;\n\t}\n\n\tpublic CertificateEntry getCertificateEntry() {\n\t\treturn entry;\n\t}\n\n\tpublic String getAlias() {\n\t\treturn alias;\n\t}\n\n\tpublic void setAlias(String alias) {\n\t\tthis.alias = alias;\n\t}\n\n\t@Override\n\tpublic void initFromCommand(Packet packet) {\n\t\tlog.log(Level.FINEST, \"Initiating item from command\");\n\t\tsuper.initFromCommand(packet);\n\t\talias = Command.getFieldValue(packet, ALIAS_KEY);\n\t\tfingerprint = Command.getFieldValue(packet, FINGERPRINT_KEY);\n\t\tserialNumber = Command.getFieldValue(packet, SERIALNUMBER_KEY);\n\t\tisDefault = Boolean.parseBoolean(Command.getFieldValue(packet, IS_DEFAULT_KEY));\n\t\tfinal String pemCertificate = Command.getFieldValue(packet, PEM_CERTIFICATE_KEY);\n\t\ttry {\n\t\t\tentry = CertificateUtil.parseCertificate(new CharArrayReader(pemCertificate.toCharArray()));\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Error while loading certificate from PEM format\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void addCommandFields(Packet packet) {\n\t\tsuper.addCommandFields(packet);\n\t\tCommand.addFieldValue(packet, ALIAS_KEY, alias);\n\t\tif (getFingerprint().isPresent()) {\n\t\t\tCommand.addFieldValue(packet, FINGERPRINT_KEY, getFingerprint().get());\n\t\t}\n\t\tif (getSerialNumber().isPresent()) {\n\t\t\tCommand.addFieldValue(packet, SERIALNUMBER_KEY, getSerialNumber().get());\n\t\t}\n\t\tCommand.addFieldValue(packet, IS_DEFAULT_KEY, String.valueOf(isDefault));\n\t\ttry {\n\t\t\tfinal String pemCertificate = CertificateUtil.exportToPemFormat(entry);\n\t\t\tCommand.addFieldValue(packet, PEM_CERTIFICATE_KEY, pemCertificate);\n\t\t} catch (CertificateEncodingException e) {\n\t\t\tlog.log(Level.WARNING, \"Error converting certificate entry to PEM format\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void initFromElement(Element elem) {\n\t\tlog.log(Level.FINEST, \"Initiating item from element: \" + elem);\n\t\tif (elem.getName() != REPO_ITEM_ELEM_NAME) {\n\t\t\tthrow new IllegalArgumentException(\"Incorrect element name, expected: \" + REPO_ITEM_ELEM_NAME);\n\t\t}\n\t\tsuper.initFromElement(elem);\n\t\tsetAlias(elem.getAttributeStaticStr(ALIAS_KEY));\n\t\tsetFingerprint(elem.getAttributeStaticStr(FINGERPRINT_KEY));\n\t\tsetDefault(Boolean.parseBoolean(elem.getAttributeStaticStr(IS_DEFAULT_KEY)));\n\t\tString pemCertificate = elem.getCData();\n\t\tif (pemCertificate == null) {\n\t\t\t// handling of the short-lived case where certificate was stored as an attribute…\n\t\t\tpemCertificate = elem.getAttributeStaticStr(PEM_CERTIFICATE_KEY);\n\t\t}\n\t\tif (pemCertificate == null) {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"Certificate is missing - neither as element attribute or CData: \" + elem);\n\t\t}\n\t\ttry {\n\t\t\tentry = CertificateUtil.parseCertificate(new CharArrayReader(pemCertificate.toCharArray()));\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Error while loading certificate from PEM format: \" + elem, e);\n\t\t}\n\t\tString serialNumberVal = elem.getAttributeStaticStr(SERIALNUMBER_KEY);\n\t\tif (serialNumberVal != null) {\n\t\t\tsetSerialNumber(serialNumberVal);\n\t\t} else if (entry.getCertificate().isPresent()) {\n\t\t\tCertificateUtil.getCertificateSerialNumber(entry.getCertificate().get())\n\t\t\t\t\t.ifPresent(serialNumber -> setSerialNumber(serialNumber.toString(16)));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void initFromPropertyString(String propString) {\n\t\tthrow new UnsupportedOperationException(\"Configuring via property string is not supported\");\n\t}\n\n\tpublic boolean isDefault() {\n\t\treturn isDefault;\n\t}\n\n\tpublic void setDefault(boolean aDefault) {\n\t\tisDefault = aDefault;\n\t}\n\n\t@Override\n\tpublic Element toElement() {\n\t\tElement elem = super.toElement();\n\n\t\telem.addAttribute(ALIAS_KEY, alias);\n\t\tif (getFingerprint().isPresent()) {\n\t\t\telem.addAttribute(FINGERPRINT_KEY, getFingerprint().get());\n\t\t}\n\t\tif (getSerialNumber().isPresent()) {\n\t\t\telem.addAttribute(SERIALNUMBER_KEY, getSerialNumber().get());\n\t\t}\n\t\telem.addAttribute(IS_DEFAULT_KEY, String.valueOf(isDefault()));\n\t\ttry {\n\t\t\tfinal String pemCertificate = CertificateUtil.exportToPemFormat(entry);\n\t\t\telem.setCData(pemCertificate);\n\t\t} catch (CertificateEncodingException e) {\n\t\t\tlog.log(Level.WARNING, \"Error converting certificate entry to PEM format\", e);\n\t\t}\n\t\treturn elem;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"CertificateItem{\" + \"alias='\" + alias + '\\'' + \", fingerprint='\" + fingerprint + '\\'' +\n\t\t\t\t\", isDefault=\" + isDefault + \", serialNumber='\" + serialNumber + '\\'' + '}';\n\t}\n\n\t@Override\n\tpublic String toPropertyString() {\n\t\tthrow new UnsupportedOperationException(\"Configuring via property string is not supported\");\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/io/repo/CertificateRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.io.repo;\n\nimport tigase.db.TigaseDBException;\nimport tigase.db.comp.UserRepoRepository;\nimport tigase.io.CertificateContainer;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.xml.DomBuilderHandler;\nimport tigase.xml.Element;\nimport tigase.xml.SimpleParser;\nimport tigase.xml.SingletonFactory;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.*;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Bean(name = \"repository\", parent = CertificateContainer.class, active = false)\npublic class CertificateRepository\n\t\textends UserRepoRepository<CertificateItem> {\n\n\tprivate static final Logger log = Logger.getLogger(CertificateRepository.class.getName());\n\n\tprivate final static String CONFIG_KEY = \"vhost-certificates\";\n\n\tprivate final static BareJID REPO_USER_JID = BareJID.bareJIDInstanceNS(\"certificate-manager\");\n\n\t@ConfigField(desc = \"Automatically migrate certificates from filesystem to repository (and make backup)\", alias = \"move-from-filesystem-to-repository\")\n\tprotected boolean moveFromFilesystemToRepository = true;\n\n\tpublic CertificateRepository() {\n\t\tthis.autoReloadInterval = TimeUnit.HOURS.toSeconds(1);\n\t}\n\n\t@Override\n\tpublic void addItem(CertificateItem item) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Adding item: {0}\", item);\n\t\t}\n\t\taddItemNoStore(item);\n\n\t\t// store only single item for performance\n\t\tif (repo != null && isInitialized()) {\n\t\t\tstoreSingleItem(item);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean itemChanged(CertificateItem oldItem, CertificateItem newItem) {\n\t\treturn oldItem.getFingerprint().isPresent() && newItem.getFingerprint().isPresent() &&\n\t\t\t\toldItem.getFingerprint().get().equals(newItem.getFingerprint().get());\n\t}\n\n\t@Override\n\tpublic void destroy() {\n\t}\n\n\t@Override\n\tpublic String getConfigKey() {\n\t\treturn CONFIG_KEY;\n\t}\n\n\t@Override\n\tpublic CertificateItem getItemInstance() {\n\t\treturn new CertificateItem();\n\t}\n\n\t@Override\n\tpublic String getPropertyKey() {\n\t\treturn \"--\" + CONFIG_KEY;\n\t}\n\n\t@Override\n\tpublic BareJID getRepoUser() {\n\t\treturn REPO_USER_JID;\n\t}\n\n\tpublic boolean isMoveFromFilesystemToRepository() {\n\t\treturn moveFromFilesystemToRepository;\n\t}\n\n\t@Override\n\tpublic void reload() {\n\t\tint hashCode = 0;\n\t\ttry {\n\t\t\tif (repo == null) {\n\t\t\t\tlog.log(Level.SEVERE, \"Repository is not initialised - skipping reload\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfinal Map<String, String> itemsMap = repo.getDataMap(getRepoUser(), getItemsListPKey());\n\n\t\t\tif (itemsMap != null) {\n\t\t\t\thashCode = itemsMap.hashCode();\n\n\t\t\t\tif (hashCode != itemsHash) {\n\t\t\t\t\tSet<String> oldKeys = new HashSet<>(super.items.keySet());\n\n\t\t\t\t\titemsMap.forEach((key, value) -> parseElement(value).ifPresentOrElse(item -> {\n\t\t\t\t\t\taddItemNoStore(item);\n\t\t\t\t\t\toldKeys.remove(item.getKey());\n\t\t\t\t\t}, () -> oldKeys.remove(key)));\n\t\t\t\t\tlog.log(Level.CONFIG, \"All loaded items: {0}\", items);\n\n\t\t\t\t\titemsHash = hashCode;\n\n\t\t\t\t\toldKeys.forEach(this::removeItemNoStore);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (TigaseDBException ex) {\n\t\t\tlog.log(Level.SEVERE, \"Problem with loading items list from the database.\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void removeItem(String key) {\n\t\tsuper.removeItem(key);\n\n\t\tif (repo != null && isInitialized()) {\n\t\t\ttry {\n\t\t\t\trepo.removeData(getRepoUser(), getItemsListPKey(), key);\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.SEVERE, \"Error removing item from the repository\", e);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void store() {\n\t\tif (repo != null && isInitialized()) {\n\t\t\tfor (CertificateItem item : items.values()) {\n\t\t\t\tstoreSingleItem(item);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate Optional<CertificateItem> parseElement(String element) {\n\t\tDomBuilderHandler domHandler = new DomBuilderHandler();\n\t\tSimpleParser parser = SingletonFactory.getParserInstance();\n\n\t\tparser.parse(domHandler, element);\n\n\t\tQueue<Element> parsedElements = domHandler.getParsedElements();\n\n\t\tif (!parsedElements.isEmpty() && parsedElements.peek() != null) {\n\t\t\tCertificateItem item = getItemInstance();\n\t\t\titem.initFromElement(parsedElements.peek());\n\t\t\treturn Optional.of(item);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"Parsing certificate from element failed: \" + element);\n\t\t\treturn Optional.empty();\n\t\t}\n\t}\n\n\tprotected void storeSingleItem(CertificateItem item) {\n\t\ttry {\n\t\t\trepo.setData(getRepoUser(), getItemsListPKey(), item.getKey(), item.toElement().toString());\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.SEVERE, \"Error storing item in the repository\", e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/BeanUtils.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel;\n\nimport tigase.kernel.core.BeanConfig;\nimport tigase.kernel.core.DependencyManager;\n\nimport java.lang.reflect.*;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\npublic class BeanUtils {\n\n\tpublic static Field[] getAllFields(Class<?> klass) {\n\t\tList<Field> fields = new ArrayList<Field>();\n\t\tfields.addAll(Arrays.asList(klass.getDeclaredFields()));\n\t\tif (klass.getSuperclass() != null) {\n\t\t\tfields.addAll(Arrays.asList(getAllFields(klass.getSuperclass())));\n\t\t}\n\t\treturn fields.toArray(new Field[]{});\n\t}\n\n\tpublic static Method[] getAllMethods(Class<?> klass) {\n\t\tList<Method> fields = new ArrayList<Method>();\n\t\tfields.addAll(Arrays.asList(klass.getDeclaredMethods()));\n\t\tif (klass.getSuperclass() != null) {\n\t\t\tfields.addAll(Arrays.asList(getAllMethods(klass.getSuperclass())));\n\t\t}\n\t\treturn fields.toArray(new Method[]{});\n\t}\n\n\tpublic static java.lang.reflect.Field getField(BeanConfig bc, String fieldName) {\n\t\tfinal Class<?> cl = bc.getClazz();\n\t\tjava.lang.reflect.Field[] fields = DependencyManager.getAllFields(cl);\n\t\tfor (java.lang.reflect.Field field : fields) {\n\t\t\tif (field.getName().equals(fieldName)) {\n\t\t\t\treturn field;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static Type getGetterSetterMethodsParameterType(Field f) {\n\t\tMethod getter = prepareGetterMethod(f);\n\t\tif (getter == null) {\n\t\t\treturn null;\n\t\t}\n\t\tClass rt = getter.getReturnType();\n\t\tMethod setter = prepareSetterMethod(f, rt);\n\n\t\treturn (setter == null) ? null : getter.getGenericReturnType();\n\t}\n\n\tpublic static Object getValue(Object fromBean, Field field)\n\t\t\tthrows IllegalAccessException, IllegalArgumentException, InvocationTargetException {\n\t\tMethod setter = BeanUtils.prepareGetterMethod(field);\n\t\tif (setter != null) {\n\t\t\treturn setter.invoke(fromBean);\n\t\t} else {\n\t\t\tfield.setAccessible(true);\n\t\t\treturn field.get(fromBean);\n\t\t}\n\n\t}\n\n\tpublic static String prepareAccessorMainPartName(final String fieldName) {\n\t\tif (fieldName.length() == 1) {\n\t\t\treturn fieldName.toUpperCase();\n\t\t}\n\n\t\tString r;\n\t\tif (Character.isUpperCase(fieldName.charAt(1))) {\n\t\t\tr = fieldName.substring(0, 1);\n\t\t} else {\n\t\t\tr = fieldName.substring(0, 1).toUpperCase();\n\t\t}\n\n\t\tr += fieldName.substring(1);\n\n\t\treturn r;\n\t}\n\n\tpublic static Method prepareGetterMethod(Field f) {\n\t\tString t = prepareAccessorMainPartName(f.getName());\n\t\t@SuppressWarnings(\"unused\") String sm;\n\t\tString gm;\n\t\tif (f.getType().isPrimitive() && f.getType().equals(boolean.class)) {\n\t\t\tsm = \"set\" + t;\n\t\t\tgm = \"is\" + t;\n\t\t} else {\n\t\t\tsm = \"set\" + t;\n\t\t\tgm = \"get\" + t;\n\t\t}\n\n\t\ttry {\n\t\t\tMethod m = f.getDeclaringClass().getMethod(gm);\n\t\t\treturn m;\n\t\t} catch (NoClassDefFoundError ex) {\n\t\t\tthrow createExceptionForMissingClassForField(f, ex);\n\t\t} catch (NoSuchMethodException e) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic static Method prepareSetterMethod(Field f) {\n\t\treturn prepareSetterMethod(f, f.getType());\n\t}\n\n\tpublic static Method prepareSetterMethod(Field f, Class type) {\n\t\tString t = prepareAccessorMainPartName(f.getName());\n\t\tString sm;\n\t\t@SuppressWarnings(\"unused\") String gm;\n\t\tif (f.getType().isPrimitive() && f.getType().equals(boolean.class)) {\n\t\t\tsm = \"set\" + t;\n\t\t\tgm = \"is\" + t;\n\t\t} else {\n\t\t\tsm = \"set\" + t;\n\t\t\tgm = \"get\" + t;\n\t\t}\n\n\t\ttry {\n\t\t\tMethod m = f.getDeclaringClass().getMethod(sm, type);\n\t\t\treturn m;\n\t\t} catch (NoClassDefFoundError ex) {\n\t\t\tthrow createExceptionForMissingClassForField(f, ex);\n\t\t} catch (NoSuchMethodException e) {\n\t\t\treturn null;\n\t\t\t// throw new KernelException(\"Class \" +\n\t\t\t// f.getDeclaringClass().getName() + \" has no setter of field \" +\n\t\t\t// f.getName(), e);\n\t\t}\n\t}\n\n\tpublic static ArrayList<Method> prepareSetterMethods(Class<?> destination, String fieldName) {\n\t\tString t = prepareAccessorMainPartName(fieldName);\n\t\tArrayList<Method> result = new ArrayList<Method>();\n\t\ttry {\n\t\t\tfor (Method m : getAllMethods(destination)) {\n\t\t\t\tif (m.getName().equals(\"set\" + t)) {\n\t\t\t\t\tresult.add(m);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn result;\n\t\t} catch (Exception e) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic static void setValue(Object toBean, Field field, Object valueToSet)\n\t\t\tthrows IllegalAccessException, IllegalArgumentException, InvocationTargetException {\n\t\tMethod setter = BeanUtils.prepareSetterMethod(field);\n\t\tif (valueToSet != null && setter == null) {\n\t\t\tsetter = BeanUtils.prepareSetterMethod(field, valueToSet.getClass());\n\t\t}\n\t\tif (setter != null) {\n\t\t\tsetter.invoke(toBean, valueToSet);\n\t\t} else {\n\t\t\tfield.setAccessible(true);\n\t\t\tfield.set(toBean, valueToSet);\n\t\t}\n\t}\n\n\tpublic static void setValue(Object toBean, String fieldName, Object valueToSet)\n\t\t\tthrows IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException {\n\t\tArrayList<Method> setters = BeanUtils.prepareSetterMethods(toBean.getClass(), fieldName);\n\n\t\tif (setters == null || setters.isEmpty()) {\n\t\t\tthrow new NoSuchMethodException(\"No setter for property '\" + fieldName + \"'.\");\n\t\t}\n\n\t\tfor (final Method s : setters) {\n\t\t\ttry {\n\t\t\t\ts.invoke(toBean, valueToSet);\n\t\t\t\treturn;\n\t\t\t} catch (Exception e) {\n\t\t\t}\n\t\t}\n\n\t\tthrow new IllegalArgumentException(\n\t\t\t\t\"Cannot set value type \" + valueToSet.getClass().getName() + \" to property '\" + fieldName + \"'.\");\n\t}\n\n\tprivate static IllegalArgumentException createExceptionForMissingClassForField(Field f, Throwable cause) {\n\t\treturn new IllegalArgumentException(\n\t\t\t\t\"Missing class in classpath for field '\" + f + \"' in class \" + f.getDeclaringClass() + \" from \" +\n\t\t\t\t\t\tf.getDeclaringClass()\n\t\t\t\t\t\t\t\t.getResource(\"/\" + f.getDeclaringClass().getName().replace('.', '/') + \".class\"),\n\t\t\t\tcause);\n\t}\n\n\tprivate BeanUtils() {\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/DefaultTypesConverter.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel;\n\nimport tigase.conf.ConfigReader;\nimport tigase.kernel.beans.Bean;\nimport tigase.osgi.ModulesManagerImpl;\nimport tigase.server.CmdAcl;\nimport tigase.util.Base64;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.XMLUtils;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.io.File;\nimport java.lang.reflect.*;\nimport java.time.Duration;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n@Bean(name = \"defaultTypesConverter\", active = true)\npublic class DefaultTypesConverter\n\t\timplements TypesConverter {\n\n\tprivate static final String[] decoded = {\",\"};\n\tprivate static final String[] encoded = {\"\\\\,\"};\n\tprivate static final String[] decoded_1 = {\",\"};\n\tprivate static final String[] encoded_1 = {\"\\\\,\"};\n\n\tprivate final static String regex = \"(?<!\\\\\\\\)\" + Pattern.quote(\",\");\n\n\tpublic static String escape(String input) {\n\t\tif (input != null) {\n\t\t\treturn XMLUtils.translateAll(input, decoded, encoded);\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic static String unescape(String input) {\n\t\tif (input != null) {\n\t\t\treturn XMLUtils.translateAll(input, encoded_1, decoded_1);\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Converts value to expected type.\n\t *\n\t * @param value value to be converted.\n\t * @param expectedType class of expected type.\n\t * @param <T> expected type.\n\t *\n\t * @return converted value.\n\t */\n\tpublic <T> T convert(final Object value, final Class<T> expectedType) {\n\t\treturn convert(value, expectedType, null);\n\t}\n\n\tpublic <T> T convert(final Object value, final Type type) {\n\t\tif (type instanceof Class) {\n\t\t\treturn convert(value, (Class<T>) type);\n\t\t}\n\t\tif (type instanceof ParameterizedType) {\n\t\t\tParameterizedType pt = (ParameterizedType) type;\n\t\t\tif (pt.getRawType() instanceof Class) {\n\t\t\t\treturn convert(value, (Class<T>) pt.getRawType(), pt);\n\t\t\t}\n\t\t}\n\n\t\tthrow new RuntimeException(\"Cannot convert to \" + type);\n\t}\n\n\tpublic <T> T convert(Object value, final Class<T> expectedType, Type genericType) {\n\t\ttry {\n\t\t\tif (value == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tif (\"null\".equals(value)) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tif (value instanceof ConfigReader.Variable) {\n\t\t\t\tvalue = ((ConfigReader.Variable) value).calculateValue();\n\t\t\t}\n\n\t\t\tfinal Class<?> currentType = value.getClass();\n\n\t\t\tif (expectedType.isAssignableFrom(currentType) && genericType == null) {\n\t\t\t\treturn expectedType.cast(value);\n\t\t\t}\n\n\t\t\tT customResult = customConversion(value, expectedType, genericType);\n\t\t\tif (customResult != null) {\n\t\t\t\treturn customResult;\n\t\t\t} else if (expectedType.equals(Class.class)) {\n\t\t\t\ttry {\n\t\t\t\t\treturn expectedType.cast(ModulesManagerImpl.getInstance().forName(value.toString().trim()));\n\t\t\t\t} catch (ClassNotFoundException e) {\n\t\t\t\t\tthrow new RuntimeException(\"Cannot convert to \" + expectedType, e);\n\t\t\t\t}\n\t\t\t} else if (expectedType.equals(File.class)) {\n\t\t\t\tif (value instanceof Collection || value instanceof Map) {\n\t\t\t\t\tthrow new RuntimeException(\"Cannot convert \" + value + \" to \" + expectedType);\n\t\t\t\t}\n\t\t\t\treturn expectedType.cast(new File(value.toString().trim()));\n\t\t\t} else if (expectedType.equals(Level.class)) {\n\t\t\t\treturn expectedType.cast(Level.parse(value.toString().trim()));\n\t\t\t} else if (expectedType.isEnum()) {\n\t\t\t\tString trimmedValue = value.toString().trim();\n\t\t\t\tfinal Class<? extends Enum> enumType = (Class<? extends Enum>) expectedType;\n\t\t\t\ttry {\n\t\t\t\t\tfinal Enum<?> theOneAndOnly = Enum.valueOf(enumType, trimmedValue);\n\t\t\t\t\treturn expectedType.cast(theOneAndOnly);\n\t\t\t\t} catch (IllegalArgumentException ex) {\n\t\t\t\t\t// if there is no value for the exact type let's make case insensitive lookup\n\t\t\t\t\tOptional<Enum<?>> theOneAndOnly = EnumSet.allOf(enumType)\n\t\t\t\t\t\t\t.stream()\n\t\t\t\t\t\t\t.filter(val -> trimmedValue.equalsIgnoreCase(((Enum) val).name()))\n\t\t\t\t\t\t\t.findFirst();\n\t\t\t\t\tif (theOneAndOnly.isPresent()) {\n\t\t\t\t\t\treturn expectedType.cast(theOneAndOnly.get());\n\t\t\t\t\t}\n\t\t\t\t\tthrow new RuntimeException(\"Cannot convert to \" + expectedType, ex);\n\t\t\t\t}\n\t\t\t} else if (expectedType.equals(JID.class)) {\n\t\t\t\tif (value instanceof Collection || value instanceof Map) {\n\t\t\t\t\tthrow new RuntimeException(\"Cannot convert \" + value + \" to \" + expectedType);\n\t\t\t\t}\n\t\t\t\treturn expectedType.cast(JID.jidInstance(value.toString().trim()));\n\t\t\t} else if (expectedType.equals(BareJID.class)) {\n\t\t\t\tif (value instanceof Collection || value instanceof Map) {\n\t\t\t\t\tthrow new RuntimeException(\"Cannot convert \" + value + \" to \" + expectedType);\n\t\t\t\t}\n\t\t\t\treturn expectedType.cast(BareJID.bareJIDInstance(value.toString().trim()));\n\t\t\t} else if (expectedType.equals(CmdAcl.class)) {\n\t\t\t\treturn expectedType.cast(new CmdAcl(value.toString().trim()));\n\t\t\t} else if (expectedType.equals(String.class)) {\n\t\t\t\tif (value instanceof Collection || value instanceof Map) {\n\t\t\t\t\tthrow new RuntimeException(\"Cannot convert \" + value + \" to \" + expectedType);\n\t\t\t\t}\n\t\t\t\treturn expectedType.cast(String.valueOf(value.toString().trim()));\n\t\t\t} else if (expectedType.equals(Long.class)) {\n\t\t\t\treturn expectedType.cast(Long.valueOf(value.toString().trim()));\n\t\t\t} else if (expectedType.equals(Integer.class)) {\n\t\t\t\treturn expectedType.cast(Integer.valueOf(value.toString().trim()));\n\t\t\t} else if (expectedType.equals(Boolean.class)) {\n\t\t\t\tString val = value.toString().trim();\n\t\t\t\tboolean b = (val.equalsIgnoreCase(\"yes\") || val.equalsIgnoreCase(\"true\") || val.equalsIgnoreCase(\"on\") || val.equals(\"1\"));\n\t\t\t\treturn expectedType.cast(Boolean.valueOf(b));\n\t\t\t} else if (expectedType.equals(Float.class)) {\n\t\t\t\treturn expectedType.cast(Float.valueOf(value.toString().trim()));\n\t\t\t} else if (expectedType.equals(Double.class)) {\n\t\t\t\treturn expectedType.cast(Double.valueOf(value.toString().trim()));\n\t\t\t} else if (expectedType.equals(char.class)) {\n\t\t\t\tString v = value.toString().trim();\n\t\t\t\tif (v.length() == 1) {\n\t\t\t\t\treturn (T) Character.valueOf(v.charAt(0));\n\t\t\t\t} else {\n\t\t\t\t\tthrow new RuntimeException(\"Cannot convert '\" + v + \"' to char.\");\n\t\t\t\t}\n\t\t\t} else if (expectedType.equals(int.class)) {\n\t\t\t\treturn (T) Integer.valueOf(value.toString().trim());\n\t\t\t} else if (expectedType.equals(byte.class)) {\n\t\t\t\treturn (T) Byte.valueOf(value.toString().trim());\n\t\t\t} else if (expectedType.equals(long.class)) {\n\t\t\t\treturn (T) Long.valueOf(value.toString().trim());\n\t\t\t} else if (expectedType.equals(double.class)) {\n\t\t\t\treturn (T) Double.valueOf(value.toString().trim());\n\t\t\t} else if (expectedType.equals(short.class)) {\n\t\t\t\treturn (T) Short.valueOf(value.toString().trim());\n\t\t\t} else if (expectedType.equals(float.class)) {\n\t\t\t\treturn (T) Float.valueOf(value.toString().trim());\n\t\t\t} else if (expectedType.equals(boolean.class)) {\n\t\t\t\tString val = value.toString().trim();\n\t\t\t\tboolean b = (val.equalsIgnoreCase(\"yes\") || val.equalsIgnoreCase(\"true\") || val.equalsIgnoreCase(\"on\") || val.equals(\"1\"));\n\t\t\t\treturn (T) Boolean.valueOf(b);\n\t\t\t} else if (expectedType.equals(Duration.class)) {\n\t\t\t\treturn (T) Duration.parse(value.toString().trim());\n\t\t\t} else if (expectedType.equals(byte[].class) && value.toString().startsWith(\"string:\")) {\n\t\t\t\treturn (T) value.toString().substring(7).getBytes();\n\t\t\t} else if (expectedType.equals(byte[].class) && value.toString().startsWith(\"base64:\")) {\n\t\t\t\treturn (T) Base64.decode(value.toString().substring(7));\n\t\t\t} else if (expectedType.equals(char[].class) && value.toString().startsWith(\"string:\")) {\n\t\t\t\treturn (T) value.toString().substring(7).toCharArray();\n\t\t\t} else if (expectedType.equals(char[].class) && value.toString().startsWith(\"base64:\")) {\n\t\t\t\treturn (T) (new String(Base64.decode(value.toString().substring(7)))).toCharArray();\n\t\t\t} else if (expectedType.isArray()) {\n\t\t\t\tif (value instanceof Collection) {\n\t\t\t\t\tCollection col = (Collection) value;\n\t\t\t\t\tObject result = Array.newInstance(expectedType.getComponentType(), col.size());\n\t\t\t\t\tIterator it = col.iterator();\n\t\t\t\t\tint i = 0;\n\t\t\t\t\twhile (it.hasNext()) {\n\t\t\t\t\t\tObject v = it.next();\n\t\t\t\t\t\tif (v instanceof ConfigReader.Variable) {\n\t\t\t\t\t\t\tv = ((ConfigReader.Variable) v).calculateValue();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tArray.set(result, i, (v instanceof String)\n\t\t\t\t\t\t\t\t\t\t\t ? convert(unescape((String) v), expectedType.getComponentType())\n\t\t\t\t\t\t\t\t\t\t\t : v);\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\t\t\t\t\treturn (T) result;\n\t\t\t\t} else {\n\t\t\t\t\tString[] a_str = value.toString().split(regex);\n\t\t\t\t\tObject result = Array.newInstance(expectedType.getComponentType(), a_str.length);\n\t\t\t\t\tfor (int i = 0; i < a_str.length; i++) {\n\t\t\t\t\t\tArray.set(result, i, convert(unescape(a_str[i]), expectedType.getComponentType()));\n\t\t\t\t\t}\n\t\t\t\t\treturn (T) result;\n\t\t\t\t}\n\t\t\t} else if (Parcelable.class.isAssignableFrom(expectedType)) {\n\t\t\t\ttry {\n\t\t\t\t\tT obj = expectedType.newInstance();\n\t\t\t\t\tString[] v = convert(value, String[].class);\n\t\t\t\t\t((Parcelable) obj).fillFromString(v);\n\t\t\t\t\treturn obj;\n\t\t\t\t} catch (InstantiationException | IllegalAccessException ex) {\n\t\t\t\t\tthrow new RuntimeException(\"Unsupported conversion (parcel) to \" + expectedType, ex);\n\t\t\t\t}\n\t\t\t} else if (EnumSet.class.isAssignableFrom(expectedType) && genericType instanceof ParameterizedType) {\n\t\t\t\tParameterizedType pt = (ParameterizedType) genericType;\n\t\t\t\tType[] actualTypes = pt.getActualTypeArguments();\n\t\t\t\tif (actualTypes[0] instanceof Class) {\n\t\t\t\t\tString[] a_str = value.toString().split(regex);\n\t\t\t\t\tHashSet<Enum> result = new HashSet<>();\n\t\t\t\t\tfor (int i = 0; i < a_str.length; i++) {\n\t\t\t\t\t\tresult.add((Enum) convert(unescape(a_str[i]), (Class<?>) actualTypes[0]));\n\t\t\t\t\t}\n\n\t\t\t\t\treturn (T) EnumSet.copyOf(result);\n\t\t\t\t}\n\t\t\t} else if (Pattern.class.isAssignableFrom(expectedType)) {\n\t\t\t\treturn (T) Pattern.compile(value.toString());\n\t\t\t} else if (Collection.class.isAssignableFrom(expectedType) && genericType != null) {\n\t\t\t\tint mod = expectedType.getModifiers();\n\t\t\t\tif ((Modifier.isInterface(mod) || !Modifier.isAbstract(mod)) && genericType instanceof ParameterizedType) {\n\t\t\t\t\tParameterizedType pt = (ParameterizedType) genericType;\n\t\t\t\t\tType[] actualTypes = pt.getActualTypeArguments();\n\t\t\t\t\tif (actualTypes[0] instanceof Class) {\n\t\t\t\t\t\tStream<?> stream = valueToStream(value, (Class<?>) actualTypes[0]);\n\t\t\t\t\t\tif (stream == null) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (Modifier.isInterface(mod)) {\n\t\t\t\t\t\t\t\tif (expectedType.isAssignableFrom(List.class)) {\n\t\t\t\t\t\t\t\t\treturn (T) stream.collect(Collectors.toUnmodifiableList());\n\t\t\t\t\t\t\t\t} else if (expectedType.isAssignableFrom(Set.class)) {\n\t\t\t\t\t\t\t\t\treturn (T) stream.collect(Collectors.toUnmodifiableSet());\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tthrow new RuntimeException(\"Unsupported conversion to \" + expectedType);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tCollection result = (Collection) expectedType.getDeclaredConstructor().newInstance();\n\t\t\t\t\t\t\t\t\tstream.forEach(result::add);\n\t\t\t\t\t\t\t\t\treturn (T) result;\n\t\t\t\t\t\t\t\t} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException ex) {\n\t\t\t\t\t\t\t\t\tthrow new RuntimeException(\"Unsupported conversion to \" + expectedType, ex);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (Map.class.isAssignableFrom(expectedType) && genericType instanceof ParameterizedType && value instanceof Map) {\n\t\t\t\t// this is additional support for convertion to type of Map, however value needs to be instance of Map\n\t\t\t\t// Added mainly for BeanConfigurators to be able to configure Map fields\n\t\t\t\tint mod = expectedType.getModifiers();\n\t\t\t\tMap result = null;\n\t\t\t\tif (!Modifier.isAbstract(mod) && !Modifier.isInterface(mod)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tresult = (Map) expectedType.newInstance();\n\t\t\t\t\t} catch (InstantiationException | IllegalAccessException ex) {\n\t\t\t\t\t\tthrow new RuntimeException(\"Unsupported conversion to \" + expectedType, ex);\n\t\t\t\t\t}\n\t\t\t\t} else if (value instanceof Map) {\n\t\t\t\t\tresult = new HashMap<>();\n\t\t\t\t}\n\t\t\t\tif (result != null) {\n\t\t\t\t\tParameterizedType pt = (ParameterizedType) genericType;\n\t\t\t\t\tType[] actualTypes = pt.getActualTypeArguments();\n\t\t\t\t\tfor (Map.Entry<String, Object> e : ((Map<String, Object>) value).entrySet()) {\n\t\t\t\t\t\tObject k = convert(unescape(e.getKey()), actualTypes[0]);\n\t\t\t\t\t\tObject v = e.getValue() instanceof String\n\t\t\t\t\t\t\t\t   ? convert(unescape((String) e.getValue()), actualTypes[1])\n\t\t\t\t\t\t\t\t   : convert(e.getValue(), actualTypes[1]);\n\t\t\t\t\t\tresult.put(k, v);\n\t\t\t\t\t}\n\t\t\t\t\treturn (T) result;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// here we try to assign instances of class passed as paramter if possible\n\t\t\t\ttry {\n\t\t\t\t\tClass<?> cls = ModulesManagerImpl.getInstance().forName(value.toString());\n\t\t\t\t\tif (expectedType.isAssignableFrom(cls)) {\n\t\t\t\t\t\treturn (T) cls.newInstance();\n\t\t\t\t\t}\n\t\t\t\t} catch (ClassNotFoundException ex) {\n\t\t\t\t\t// ignoring this\n\t\t\t\t} catch (InstantiationException | IllegalAccessException ex) {\n\t\t\t\t\tthrow new RuntimeException(\"Could not instantiate instance of \" + value.toString());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t{\n\t\t\t\tthrow new RuntimeException(\"Unsupported conversion to \" + expectedType);\n\t\t\t}\n\t\t} catch (TigaseStringprepException e) {\n\t\t\tthrow new IllegalArgumentException(e);\n\t\t}\n\t}\n\n\tprivate <T> Stream<T> valueToStream(Object value, Class<T> actualType) {\n\t\tif (value == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (value.getClass().isArray()) {\n\t\t\tint size = Array.getLength(value);\n\t\t\treturn Stream.iterate(0, i -> i + 1).limit(size).map(i -> Array.get(value, i)).map(v -> convert(v, actualType));\n\t\t}\n\t\tif (value instanceof Collection) {\n\t\t\treturn ((Collection) value).stream().map(v -> convert(v, actualType));\n\t\t} else {\n\t\t\treturn valueToStream(value.toString().split(regex), actualType);\n\t\t}\n\t}\n\n\tprotected <T> T customConversion(final Object value, final Class<T> expectedType, Type genericType) {\n\t\treturn null;\n\t}\n\n\t/**\n\t * Converts object to String.\n\t *\n\t * @param value object to convert.\n\t *\n\t * @return text representation of value.\n\t */\n\tpublic String toString(final Object value) {\n\t\tif (value == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (value instanceof Parcelable) {\n\t\t\treturn toString(((Parcelable) value).encodeToStrings());\n\t\t} else if (value instanceof Class<?>) {\n\t\t\treturn ((Class<?>) value).getName();\n\t\t} else if (value.getClass().isEnum()) {\n\t\t\treturn ((Enum) value).name();\n\t\t} else if (value instanceof Collection) {\n\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\tIterator it = ((Collection) value).iterator();\n\t\t\twhile (it.hasNext()) {\n\t\t\t\tsb.append(escape(toString(it.next())));\n\t\t\t\tif (it.hasNext()) {\n\t\t\t\t\tsb.append(',');\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn sb.toString();\n\t\t} else if (value.getClass().isArray()) {\n\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\tfinal int l = Array.getLength(value);\n\t\t\tfor (int i = 0; i < l; i++) {\n\t\t\t\tObject o = Array.get(value, i);\n\t\t\t\tsb.append(escape(toString(o)));\n\t\t\t\tif (i + 1 < l) {\n\t\t\t\t\tsb.append(',');\n\t\t\t\t}\n\n\t\t\t}\n\t\t\treturn sb.toString();\n\t\t} else if (value instanceof ConfigReader.Variable) {\n\t\t\treturn toString(((ConfigReader.Variable) value).calculateValue());\n\t\t} else {\n\t\t\treturn value.toString();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/KernelException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel;\n\npublic class KernelException\n\t\textends RuntimeException {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic KernelException() {\n\t\tsuper();\n\t}\n\n\tpublic KernelException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic KernelException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n\tpublic KernelException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {\n\t\tsuper(message, cause, enableSuppression, writableStackTrace);\n\t}\n\n\tpublic KernelException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/TypesConverter.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel;\n\nimport java.lang.reflect.Type;\n\npublic interface TypesConverter {\n\n\t<T> T convert(final Object value, final Class<T> expectedType);\n\n\t<T> T convert(final Object value, final Class<T> expectedType, Type genericType);\n\n\tString toString(final Object value);\n\n\tinterface Parcelable {\n\n\t\tString[] encodeToStrings();\n\n\t\tvoid fillFromString(String[] encoded);\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/beans/Autostart.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.beans;\n\n/**\n * Created by andrzej on 22.10.2016.\n */\n\nimport java.lang.annotation.*;\n\n@Target({ElementType.TYPE})\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\npublic @interface Autostart {\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/beans/Bean.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.beans;\n\nimport java.lang.annotation.*;\n\n/**\n * Defines name of bean. <p> This annotation is not required, but each bean must be named! Instead of using annotation,\n * name of bean may be defined during registration. </p>\n */\n@Target({ElementType.TYPE})\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\npublic @interface Bean {\n\n\t/**\n\t * Name of bean. * <br> This name will be used to find bean and load configuration for the bean.\n\t *\n\t * @return name of bean.\n\t */\n\tString name();\n\n\t/**\n\t * Is active by default. * <br> <ul> <li><code>true</code> if annotated bean (if registered) should be active\n\t * (automatically loaded if required);</li> <li><code>false</code> if annotated bean will not be automatically\n\t * loaded, even if it will be registered. It will require manual activation in the configuration.</li> </ul>\n\t *\n\t * @return <code>true</code> if bean should be automatically loaded\n\t */\n\tboolean active();\n\n\t/**\n\t * Is bean exportable? * <br> Exportable beans are available not only in the context of a kernel in which they are\n\t * registered but are available also to all beans registered in subkernels.\n\t *\n\t * @return <code>true</code> if bean should be available also in subkernels\n\t */\n\tboolean exportable() default false;\n\n\t/**\n\t * Class of a parent bean. * <br> Returns parent class for which this bean should always be registered. * <br>\n\t * Following cases are supported: <ul> <li><code>Object</code> if bean should never be automatically registered</li>\n\t * <li>{@link tigase.kernel.core.Kernel} if bean should be automatically registered in the main/root\n\t * kernel</li> <li>class implementing {@link tigase.kernel.beans.RegistrarBean} if bean should be loaded\n\t * automatically for that class</li> </ul>\n\t *\n\t * @return parent class for which this class should be automatically registered\n\t */\n\tClass parent() default Object.class;\n\n\t/**\n\t * Classes of parent beans. * <br> In some cases same beans should be automatically registered for more than one\n\t * class. This method allows to return more than one class.\n\t *\n\t * @return array of classes for which this class should be automatically registered\n\t *\n\t * @see #parent()\n\t */\n\tClass[] parents() default {};\n\n\t/**\n\t * Automatic registration selectors. * <br> In some cases it is required/useful to decide if beans should be\n\t * registered automatically depending on more global configuration option. This method returns array of {@link\n\t * tigase.kernel.beans.BeanSelector} classes which provide a logic deciding if bean should be automatically loaded\n\t * or not. * <br> Will only work if {@link #parent()} or {@link #parents()} returns correct values.\n\t *\n\t * @return array of classes deciding if bean should be automatically loaded.\n\t */\n\tClass<? extends BeanSelector>[] selectors() default {};\n\n}"
  },
  {
    "path": "src/main/java/tigase/kernel/beans/BeanFactory.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.beans;\n\nimport tigase.kernel.KernelException;\n\n/**\n * Interface to create factories of beans.\n * <br>\n * Factory is responsible to create instance of bean and inject all dependencies!\n *\n * @param <T> type of created bean.\n */\npublic interface BeanFactory<T> {\n\n\t/**\n\t * Create instance of bean. <p> Remember, that dependencies will not be injected to this bean. Factory must do that!\n\t * </p>\n\t *\n\t * @return instancje of bean.\n\t *\n\t * @throws KernelException when something goes wrong.\n\t */\n\tT createInstance() throws KernelException;\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/beans/BeanSelector.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.beans;\n\nimport tigase.kernel.core.Kernel;\n\n/**\n * Interface used by bean configurators to detect is additional beans should be registered\n * <br>\n * Created by andrzej on 10.03.2016.\n */\npublic interface BeanSelector {\n\n\t/**\n\t * Method needs to return true if bean in which annotation class implementing this interface is specified and this\n\t * bean should be registered\n\t */\n\tboolean shouldRegister(Class clazz, Kernel kernel);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/beans/Converter.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.beans;\n\nimport tigase.kernel.TypesConverter;\n\nimport java.lang.annotation.*;\n\n@Target({ElementType.FIELD})\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\npublic @interface Converter {\n\n\tClass<? extends TypesConverter> converter();\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/beans/Initializable.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.beans;\n\n/**\n * If bean implements this interface, then if bean will be created and configured, Kernel calls method {@link\n * Initializable#initialize()}.\n */\npublic interface Initializable {\n\n\t/**\n\t * Method will be called, when bean will be created, configured and ready to use.\n\t */\n\tvoid initialize();\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/beans/Inject.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.beans;\n\nimport java.lang.annotation.*;\n\n/**\n * This annotation marks field in class that Kernel should inject dependency here.\n */\n@Target({ElementType.FIELD})\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\npublic @interface Inject {\n\n\t/**\n\t * Name of bean to be injected (optional).\n\t *\n\t * @return name of bean.\n\t */\n\tString bean() default \"\";\n\n\t/**\n\t * Specify if injection of dependency is required or not.\n\t *\n\t * @return <code>true</code> if <code>null</code> value is allowed to inject.\n\t */\n\tboolean nullAllowed() default false;\n\n\t/**\n\t * Type of bean to be injected (opiotnal).\n\t *\n\t * @return type of bean.\n\t */\n\tClass<?> type() default EMPTY.class;\n\n\tclass EMPTY {\n\n\t\tprivate EMPTY() {\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/beans/RegistrarBean.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.beans;\n\nimport tigase.kernel.core.Kernel;\n\n/**\n * Interface which needs to be implemented by bean classes which are also Registrars.\n * <br>\n * Normal implementations of Registrars cannot be same class as bean inside newly created kernel - with\n * <code>RegistrarBean</code> it is possible.\n * <br>\n * Created by andrzej on 05.03.2016.\n */\npublic interface RegistrarBean {\n\n\t/**\n\t * Method called when bean is being registered allowing developer to programatically register other beans.\n\t *\n\t * @param kernel - instance from local scope\n\t */\n\tvoid register(Kernel kernel);\n\n\t/**\n\t * Method called while bean is being unregistered.\n\t *\n\t * @param kernel - instance from local scope\n\t */\n\tvoid unregister(Kernel kernel);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/beans/RegistrarBeanWithDefaultBeanClass.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.beans;\n\n/**\n * Created by andrzej on 14.08.2016.\n */\npublic interface RegistrarBeanWithDefaultBeanClass\n\t\textends RegistrarBean {\n\n\t/**\n\t * Returns default class for all bean which are defined using configuration as subbeans of bean implementing this\n\t * interface.\n\t *\n\t * This allows users to have more convenient configuration file without the need to specify class for each of\n\t * subbbeans if most of them will have the same class.\n\t *\n\t */\n\tClass<?> getDefaultBeanClass();\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/beans/UnregisterAware.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.beans;\n\n/**\n * If bean implements this interface then just before unregistering bean Kernel calls method {@link\n * UnregisterAware#beforeUnregister()}.\n */\npublic interface UnregisterAware {\n\n\t/**\n\t * Method called before bean unregister.\n\t */\n\tvoid beforeUnregister();\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/beans/config/AbstractBeanConfigurator.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.beans.config;\n\nimport tigase.conf.AbstractConfigBuilder;\nimport tigase.db.util.SchemaManager;\nimport tigase.kernel.BeanUtils;\nimport tigase.kernel.KernelException;\nimport tigase.kernel.TypesConverter;\nimport tigase.kernel.beans.*;\nimport tigase.kernel.core.BeanConfig;\nimport tigase.kernel.core.BeanConfigBuilder;\nimport tigase.kernel.core.DependencyManager;\nimport tigase.kernel.core.Kernel;\nimport tigase.osgi.ModulesManagerImpl;\nimport tigase.osgi.util.ClassUtilBean;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\npublic abstract class AbstractBeanConfigurator\n\t\timplements BeanConfigurator {\n\n\tprivate static final Logger log = Logger.getLogger(AbstractBeanConfigurator.class.getCanonicalName());\n\n\tprivate final ConcurrentHashMap<BeanConfig, HashMap<Field, Object>> defaultFieldValues = new ConcurrentHashMap<BeanConfig, HashMap<Field, Object>>();\n\t@Inject(bean = \"defaultTypesConverter\")\n\tprotected TypesConverter defaultTypesConverter;\n\t@Inject(bean = \"kernel\", nullAllowed = false)\n\tprotected Kernel kernel;\n\tprivate boolean accessToAllFields = false;\n\n\t/**\n\t * Method looks for bean classes (classes annotated with <code>@Bean</code> which has <code>parent</code> property\n\t * set to passed class.\n\t *\n\t * @param kernel instance of the Kernel\n\t * @param requiredClass class to look for as <code>parent</code> property value of <code>@Bean</code> annotation\n\t * @return map of bean classes in for of \"bean name\" - \"class\"\n\t */\n\tpublic static Map<String, Class<?>> getBeanClassesFromAnnotations(Kernel kernel, Class<?> requiredClass) {\n\t\tSet<Class<?>> classes = ClassUtilBean.getInstance().getAllClasses();\n\t\tList<Class<?>> toRegister = registerBeansForBeanOfClassGetBeansToRegister(kernel, requiredClass, classes);\n\n\t\tMap<String, Class<?>> result = new HashMap<>();\n\t\tfor (Class<?> cls : toRegister) {\n\t\t\tBean annotation = cls.getAnnotation(Bean.class);\n\t\t\tresult.put(annotation.name(), cls);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Method checkes if bean is already registered in parent kernel.\n\t * @param kernel kernel instance to check\n\t * @param name name of the bean\n\t * @param clazz expected class of the bean\n\t */\n\tprotected static boolean isBeanClassRegisteredInParentKernel(Kernel kernel, String name, Class<?> clazz) {\n\t\tif (kernel == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tBeanConfig bc = kernel.getDependencyManager().getBeanConfig(name);\n\t\tif (bc == null) {\n\t\t\treturn isBeanClassRegisteredInParentKernel(kernel.getParent(), name, clazz);\n\t\t}\n\n\t\tif (bc.getClazz().equals(clazz)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tBean annotation = clazz.getAnnotation(Bean.class);\n\t\tfor (Class<?> ifc : clazz.getInterfaces()) {\n\t\t\tif (ifc.isAssignableFrom(bc.getClazz()) && !ifc.equals(RegistrarBean.class) && !ifc.equals(Initializable.class) && !ifc.equals(UnregisterAware.class)) {\n\t\t\t\tBean existingBeanAnnotation = bc.getClazz().getAnnotation(Bean.class);\n\t\t\t\tif (existingBeanAnnotation == null ||\n\t\t\t\t\t\tannotation.parent().isAssignableFrom(existingBeanAnnotation.parent())) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\t\n\tprotected static Map<String, BeanDefinition> mergeWithBeansPropertyValue(\n\t\t\tMap<String, BeanDefinition> beanPropConfigMap, Map<String, Object> values) {\n\t\tList<String> beansProp = null;\n\t\tObject beansValue = values.get(\"beans\");\n\t\tif (beansValue instanceof String) {\n\t\t\tbeansProp = Arrays.asList(((String) beansValue).split(\",\"));\n\t\t} else if (beansValue instanceof List) {\n\t\t\tbeansProp = (List<String>) beansValue;\n\t\t}\n\t\tif (beansProp != null) {\n\t\t\tfor (String beanStr : beansProp) {\n\t\t\t\tString beanName = beanStr;\n\t\t\t\tboolean active = true;\n\t\t\t\tif (beanStr.startsWith(\"-\")) {\n\t\t\t\t\tbeanName = beanStr.substring(1);\n\t\t\t\t\tactive = false;\n\t\t\t\t} else if (beanStr.startsWith(\"+\")) {\n\t\t\t\t\tbeanName = beanStr.substring(1);\n\t\t\t\t}\n\n\t\t\t\tif (beanPropConfigMap.get(beanName) != null) {\n\t\t\t\t\tthrow new RuntimeException(\n\t\t\t\t\t\t\t\"Invalid 'beans' property value - duplicated entry for bean \" + beanName + \"! in \" +\n\t\t\t\t\t\t\t\t\tbeansProp);\n\t\t\t\t}\n\t\t\t\tBeanDefinition cfg = new BeanDefinition();\n\t\t\t\tcfg.setBeanName(beanName);\n\t\t\t\tcfg.setActive(active);\n\t\t\t\tbeanPropConfigMap.put(beanName, cfg);\n\t\t\t}\n\t\t}\n\t\treturn beanPropConfigMap;\n\t}\n\n\t/**\n\t * Method registers beans which classes are annotated with <code>@Bean</code> and have <code>parent</code> set to\n\t * the class passed as parameter.\n\t */\n\tpublic static void registerBeansForBeanOfClass(Kernel kernel, Class<?> cls) {\n\t\tSet<Class<?>> classes = ClassUtilBean.getInstance().getAllClasses();\n\t\tregisterBeansForBeanOfClass(kernel, cls, classes);\n\t}\n\n\tprotected static void registerBeansForBeanOfClass(Kernel kernel, Class<?> requiredClass, Set<Class<?>> classes) {\n\t\tList<Class<?>> toRegister = registerBeansForBeanOfClassGetBeansToRegister(kernel, requiredClass, classes);\n\t\tfor (Class<?> cls : toRegister) {\n\t\t\tBean annotation = cls.getAnnotation(Bean.class);\n\t\t\tif (isBeanClassRegisteredInParentKernel(kernel.getParent(), annotation.name(), cls)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tkernel.registerBean(cls).execWithoutInject();\n\t\t}\n\t}\n\n\tprotected static List<Class<?>> registerBeansForBeanOfClassGetBeansToRegister(Kernel kernel, Class<?> requiredClass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  Set<Class<?>> classes) {\n\t\tMap<Class<?>, Bean> matching = new HashMap<>();\n\t\tfor (Class<?> cls : classes) {\n\t\t\tBean bean = registerBeansForBeanOfClassShouldRegister(kernel, requiredClass, cls);\n\t\t\tif (bean != null) {\n\t\t\t\tmatching.put(cls, bean);\n\t\t\t}\n\t\t}\n\n\t\tMap<String, SchemaManager.Pair<Class<?>,Class>> map = new HashMap<>();\n\n\t\t//List<Class<?>> toRegister = new ArrayList<>();\n\t\tClass<?> req = requiredClass;\n\t\tdo {\n\t\t\tOptional<List<Class<?>>> interfaces = Optional.ofNullable(req.getInterfaces()).map(Arrays::asList);\n\t\t\tIterator<Map.Entry<Class<?>, Bean>> it = matching.entrySet().iterator();\n\t\t\twhile (it.hasNext()) {\n\t\t\t\tMap.Entry<Class<?>, Bean> e = it.next();\n\t\t\t\tClass expParent = e.getValue().parent();\n\t\t\t\tif (expParent.equals(req)) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.finest(\"comparing exp parent \" + expParent.getCanonicalName() + \" with \" + req.getCanonicalName() + \" for \" + e.getKey().getCanonicalName());\n\t\t\t\t\t}\n\t\t\t\t\tputInMap(map, e.getValue().name(), e.getKey(), req);\n\t\t\t\t\t//toRegister.add(0, e.getKey());\n\t\t\t\t\tit.remove();\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (interfaces.isPresent()) {\n\t\t\t\t\tboolean found = false;\n\t\t\t\t\tfor(Class<?> reqIfc : interfaces.get()) {\n\t\t\t\t\t\tif (reqIfc.equals(expParent)) {\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.finest(\"comparing required interface \" + reqIfc.getCanonicalName() + \" with exp parent\" + expParent.getCanonicalName() + \" for \" + e.getKey().getCanonicalName());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tputInMap(map, e.getValue().name(), e.getKey(), req);\n//\t\t\t\t\t\t\ttoRegister.add(0, e.getKey());\n\t\t\t\t\t\t\tit.remove();\n\t\t\t\t\t\t\tfound = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (found) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tClass[] exParents = e.getValue().parents();\n\t\t\t\tfor (Class exp : exParents) {\n\t\t\t\t\tif (exp.equals(req)) {\n\t\t\t\t\t\tputInMap(map, e.getValue().name(), e.getKey(), req);\n//\t\t\t\t\t\ttoRegister.add(0, e.getKey());\n\t\t\t\t\t\tit.remove();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} while ((req = req.getSuperclass()) != null && !req.equals(Object.class) && !matching.isEmpty());\n\n\t\tList<Class<?>> toRegister = map.values().stream().map(SchemaManager.Pair::getKey).collect(Collectors.toList());\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\n\t\t\t\t\t\"for class \" + requiredClass.getCanonicalName() + \" we need to register: \" + toRegister);\n\t\t}\n\t\treturn toRegister;\n\t}\n\n\tprivate static void putInMap(Map<String, SchemaManager.Pair<Class<?>,Class>> map, String key, Class<?> value, Class parent) {\n\t\tSchemaManager.Pair<Class<?>, Class> oldValue = map.get(key);\n\t\tif (oldValue == null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"setting class \" + value.getCanonicalName() + \" for \" + key + \" for parent \" + parent.getCanonicalName());\n\t\t\t}\n\t\t\tmap.put(key, new SchemaManager.Pair<>(value, parent));\n\t\t} else {\n\t\t\t// if newly found bean is for subclass of previous bean, then replace it..\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"checking class \" + value.getCanonicalName() + \" for \" + key + \" for parent \" + parent.getCanonicalName());\n\t\t\t}\n\t\t\tif (oldValue.getValue().isAssignableFrom(parent)) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"replacing with class \" + value.getCanonicalName() + \" for \" + key + \" for parent \" + parent.getCanonicalName());\n\t\t\t\t}\n\t\t\t\tmap.put(key, new SchemaManager.Pair<>(value, parent));\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected static Bean registerBeansForBeanOfClassShouldRegister(Kernel kernel, Class<?> requiredClass,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tClass<?> cls) {\n\t\tBean annotation = cls.getDeclaredAnnotation(Bean.class);\n\t\tif (annotation == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tClass parent = annotation.parent();\n\t\tif (parent == Object.class) {\n\t\t\tClass[] parents = annotation.parents();\n\t\t\tboolean matches = false;\n\n\t\t\tfor (Class p : parents) {\n\t\t\t\tmatches |= p.isAssignableFrom(requiredClass);\n\t\t\t}\n\n\t\t\tif (!matches) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t} else if (!parent.isAssignableFrom(requiredClass)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tClass<? extends BeanSelector>[] selectors = annotation.selectors();\n\t\tif (selectors.length > 0) {\n\t\t\tfor (Class<? extends BeanSelector> selectorCls : selectors) {\n\t\t\t\ttry {\n\t\t\t\t\tBeanSelector selector = selectorCls.newInstance();\n\t\t\t\t\tif (!selector.shouldRegister(cls, kernel)) {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t} catch (InstantiationException | IllegalAccessException e) {\n\t\t\t\t\tlog.log(Level.SEVERE,\n\t\t\t\t\t\t\t\"could not instantiate BeanSelector \" + selectorCls.getCanonicalName() + \" for \" +\n\t\t\t\t\t\t\t\t\tcls.getCanonicalName(), e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tBeanSelector selector = kernel.getInstance(BeanSelector.class);\n\t\t\tif (selector != null) {\n\t\t\t\tif (!selector.shouldRegister(cls, kernel)) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (KernelException ex) {\n\t\t\tlog.log(Level.FINEST, \"Could not find implementation of bean selector, skipping bean selection...\");\n\t\t}\n\t\treturn annotation;\n\t}\n\n\t/**\n\t * Method returns current configuration map.\n\t */\n\tpublic abstract Map<String, Object> getProperties();\n\n\t/**\n\t * Method configures passed bean with provided values.\n\t */\n\tpublic void configure(final BeanConfig beanConfig, final Object bean, final Map<String, Object> values) {\n\t\tif (values == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (log.isLoggable(Level.CONFIG)) {\n\t\t\tlog.config(\"Configuring bean '\" + beanConfig.getBeanName() + \"'...\");\n\t\t}\n\n\t\tregisterBeans(beanConfig, bean, values);\n\n\t\tfinal HashMap<Field, Object> valuesToSet = new HashMap<>();\n\n\t\t// should we invert it? for every field get value, not for every value find field?\n\t\tfor (Map.Entry<String, Object> entry : values.entrySet()) {\n\t\t\tfinal String property = entry.getKey();\n\t\t\tfinal Object value = entry.getValue();\n\n\t\t\ttry {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"Preparing property '\" + property + \"' of bean '\" + beanConfig.getBeanName() + \"'...\");\n\t\t\t\t}\n\n\t\t\t\tfinal Field field = BeanUtils.getField(beanConfig, property);\n\t\t\t\tif (field == null) {\n\t\t\t\t\tswitch (property) {\n\t\t\t\t\t\tcase \"name\":\n\t\t\t\t\t\tcase \"class\":\n\t\t\t\t\t\tcase \"beans\":\n\t\t\t\t\t\t\t// ignoring as this properties are handled by configurator and kernel\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t// ignoring if property contains \"/\" as this mean it is configuration property for subbean\n\t\t\t\t\t\t\tif (!property.contains(\"/\") && !(value instanceof BeanDefinition) &&\n\t\t\t\t\t\t\t\t\tkernel.getDependencyManager().getBeanConfig(property) == null &&\n\t\t\t\t\t\t\t\t\t(!(bean instanceof RegistrarBean) || (kernel.getDependencyManager()\n\t\t\t\t\t\t\t\t\t\t\t.getBeanConfig(beanConfig.getBeanName() + \"#KERNEL\") != null &&\n\t\t\t\t\t\t\t\t\t\t\t((Kernel) kernel.getInstance(\n\t\t\t\t\t\t\t\t\t\t\t\t\tbeanConfig.getBeanName() + \"#KERNEL\")).getDependencyManager()\n\t\t\t\t\t\t\t\t\t\t\t\t\t.getBeanConfig(property) == null))) {\n\t\t\t\t\t\t\t\tlog.config(\"Field '\" + property + \"' does not exists in bean '\" +\n\t\t\t\t\t\t\t\t\t\t\t\t   beanConfig.getBeanName() + \"'. Ignoring!\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tConfigField cf = field.getAnnotation(ConfigField.class);\n\t\t\t\tif (!accessToAllFields && cf == null) {\n\t\t\t\t\tlog.fine(\"Field '\" + property + \"' of bean '\" + beanConfig.getBeanName() +\n\t\t\t\t\t\t\t\t\t \"' Can''t be configured (missing @ConfigField). Ignoring!\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tTypesConverter converter = defaultTypesConverter;\n\t\t\t\tConverter cAnn = field.getAnnotation(Converter.class);\n\t\t\t\tif (cAnn != null) {\n\t\t\t\t\tconverter = kernel.getInstance(cAnn.converter());\n\t\t\t\t}\n\n\t\t\t\tType expType = BeanUtils.getGetterSetterMethodsParameterType(field);\n\t\t\t\tClass clazz = null;\n\t\t\t\tif (expType != null) {\n\t\t\t\t\tif (expType instanceof Class) {\n\t\t\t\t\t\tclazz = (Class) expType;\n\t\t\t\t\t} else if (expType instanceof ParameterizedType) {\n\t\t\t\t\t\tType type = ((ParameterizedType) expType).getRawType();\n\t\t\t\t\t\tif (type instanceof Class) {\n\t\t\t\t\t\t\tclazz = (Class) type;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\texpType = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else{\n\t\t\t\t\t\texpType = null;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (expType != null) {\n\t\t\t\t\tObject v = converter.convert(value, clazz, expType);\n\t\t\t\t\tvaluesToSet.put(field, v);\n\t\t\t\t} else {\n\t\t\t\t\tObject v = converter.convert(value, field.getType(), field.getGenericType());\n\t\t\t\t\tvaluesToSet.put(field, v);\n\t\t\t\t}\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\t\"Can''t prepare value of property '\" + property + \"' of bean '\" + beanConfig.getBeanName() +\n\t\t\t\t\t\t\t\t\"': '\" + value + \"'\", e);\n\t\t\t\tthrow new RuntimeException(\n\t\t\t\t\t\t\"Can''t prepare value of property '\" + property + \"' of bean '\" + beanConfig.getBeanName() +\n\t\t\t\t\t\t\t\t\"': '\" + value + \"'\");\n\t\t\t}\n\t\t}\n\n\t\tfinal HashSet<String> changedFields = new HashSet<>();\n\n\t\tfor (Map.Entry<Field, Object> item : valuesToSet.entrySet()) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Setting property '\" + item.getKey().getName() + \"' of bean '\" + beanConfig.getBeanName() +\n\t\t\t\t\t\t\t\t   \"'...\");\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tObject oldValue = BeanUtils.getValue(bean, item.getKey());\n\t\t\t\tObject newValue = item.getValue();\n\n\t\t\t\tif (!equals(oldValue, newValue)) {\n\t\t\t\t\tBeanUtils.setValue(bean, item.getKey(), newValue);\n\t\t\t\t\tchangedFields.add(item.getKey().getName());\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.finest(\"Property '\" + item.getKey().getName() + \"' of bean '\" + beanConfig.getBeanName() +\n\t\t\t\t\t\t\t\t\t\t   \"' has been set to \" + item.getValue());\n\t\t\t\t\t}\n\t\t\t\t} else if (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"Property '\" + item.getKey().getName() + \"' of bean '\" + beanConfig.getBeanName() +\n\t\t\t\t\t\t\t\t\t   \"' has NOT been set to \" + item.getValue() +\n\t\t\t\t\t\t\t\t\t   \" because is identical with previous value.\");\n\t\t\t\t}\n\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\t\"Can''t set property '\" + item.getKey().getName() + \"' of bean '\" + beanConfig.getBeanName() +\n\t\t\t\t\t\t\t\t\"' with value '\" + item.getValue() + \"'\", e);\n\t\t\t\tthrow new RuntimeException(\n\t\t\t\t\t\t\"Can''t set property '\" + item.getKey().getName() + \"' of bean '\" + beanConfig.getBeanName() +\n\t\t\t\t\t\t\t\t\"' with value '\" + item.getValue() + \"'\");\n\t\t\t}\n\t\t}\n\n\t\tif (bean instanceof ConfigurationChangedAware) {\n\t\t\t((ConfigurationChangedAware) bean).beanConfigurationChanged(\n\t\t\t\t\tCollections.unmodifiableCollection(changedFields));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void configure(BeanConfig beanConfig, Object bean) throws KernelException {\n\t\ttry {\n\t\t\tgrabDefaultConfig(beanConfig, bean);\n\t\t\tMap<String, Object> ccc = getConfiguration(beanConfig);\n\t\t\tconfigure(beanConfig, bean, ccc);\n\t\t} catch (Exception e) {\n\t\t\tthrow new KernelException(\"Cannot inject configuration to bean \" + beanConfig.getBeanName(), e);\n\t\t}\n\t}\n\n\t/**\n\t * Method returns default types converter used by bean configurator.\n\t */\n\tpublic TypesConverter getDefaultTypesConverter() {\n\t\treturn defaultTypesConverter;\n\t}\n\n\tpublic void setDefaultTypesConverter(TypesConverter defaultTypesConverter) {\n\t\tthis.defaultTypesConverter = defaultTypesConverter;\n\t}\n\t\n\tpublic Kernel getKernel() {\n\t\treturn kernel;\n\t}\n\n\tpublic void setKernel(Kernel kernel) {\n\t\tthis.kernel = kernel;\n\t}\n\n\tpublic boolean isAccessToAllFields() {\n\t\treturn accessToAllFields;\n\t}\n\n\tpublic void setAccessToAllFields(boolean accessToAllFields) {\n\t\tthis.accessToAllFields = accessToAllFields;\n\t}\n\n\t/**\n\t * Method registers all beans which are annotated by <code>@Bean</code> and which <code>parent</code> property is\n\t * set to class which instance is passed. Additionally this method registers beans which definitions are passed\n\t * in the configuration.\n\t * \n\t * @param beanConfig bean config of initializing bean\n\t * @param bean instance of initializing bean\n\t * @param values configuration of a bean\n\t */\n\t@Override\n\tpublic void registerBeans(BeanConfig beanConfig, Object bean, Map<String, Object> values) {\n\t\tif (beanConfig != null && Kernel.class.isAssignableFrom(beanConfig.getClazz())) {\n\t\t\treturn;\n\t\t}\n\n\t\tKernel kernel = beanConfig == null ? this.getKernel() : beanConfig.getKernel();\n\n\t\tSet<String> toUnregister = new ArrayList<>(kernel.getDependencyManager().getBeanConfigs()).stream()\n\t\t\t\t.filter(bc -> bc.getSource() == BeanConfig.Source.configuration)\n\t\t\t\t.filter(bc -> beanConfig == null || bc.getRegisteredBy().contains(beanConfig))\n\t\t\t\t.map(bc -> bc.getBeanName())\n\t\t\t\t.collect(Collectors.toSet());\n\n\t\tfinal Map<String, Class<?>> beansFromAnnotations = getBeanClassesFromAnnotations(kernel, beanConfig == null\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t ? Kernel.class\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t : beanConfig.getClazz());\n\t\tfinal Map<String, BeanDefinition> beanDefinitionsFromConfig =\n\t\t\t\tvalues == null ? new HashMap<>() : mergeWithBeansPropertyValue(getBeanDefinitions(values), values);\n\n\t\tbeansFromAnnotations.forEach((name, cls) -> {\n\t\t\tif (beanDefinitionsFromConfig != null) {\n\t\t\t\tBeanDefinition definition = beanDefinitionsFromConfig.get(name);\n\t\t\t\tif (definition != null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (isBeanClassRegisteredInParentKernel(kernel.getParent(), name, cls)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tBeanConfig bc = kernel.getDependencyManager().getBeanConfig(name);\n\t\t\tif (bc != null && bc.getSource() == BeanConfig.Source.annotation && bc.getClazz().equals(cls)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (beanConfig != null && beanConfig.getState() == BeanConfig.State.initialized) {\n\t\t\t\tkernel.registerBean(cls).setSource(BeanConfig.Source.annotation).registeredBy(beanConfig).exec();\n\t\t\t} else {\n\t\t\t\tkernel.registerBean(cls)\n\t\t\t\t\t\t.setSource(BeanConfig.Source.annotation)\n\t\t\t\t\t\t.registeredBy(beanConfig)\n\t\t\t\t\t\t.execWithoutInject();\n\t\t\t}\n\n\t\t\tbc = kernel.getDependencyManager().getBeanConfig(name);\n\t\t\tif (bc != null && bc.getState() == BeanConfig.State.inactive && hasDirectConfiguration(bc)) {\n\t\t\t\tlog.log(Level.CONFIG, \"bean \" + bc.getBeanName() + \" is disabled but configuration is specified\");\n\t\t\t}\n\t\t});\n\n\t\tfor (BeanDefinition cfg : beanDefinitionsFromConfig.values()) {\n\t\t\ttry {\n\t\t\t\tClass<?> clazz = cfg.getClazzName() == null\n\t\t\t\t\t\t\t\t ? beansFromAnnotations.get(cfg.getBeanName())\n\t\t\t\t\t\t\t\t : ModulesManagerImpl.getInstance().forName(cfg.getClazzName());\n\t\t\t\tBeanConfig oldBc = kernel.getDependencyManager().getBeanConfig(cfg.getBeanName());\n\t\t\t\tif (clazz == null) {\n\t\t\t\t\tif (bean != null && bean instanceof RegistrarBeanWithDefaultBeanClass) {\n\t\t\t\t\t\tclazz = ((RegistrarBeanWithDefaultBeanClass) bean).getDefaultBeanClass();\n\t\t\t\t\t} else if (oldBc != null) {\n\t\t\t\t\t\tclazz = oldBc.getClazz();\n\t\t\t\t\t}\n\n\t\t\t\t\tif (clazz == null) {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"unknown class {0} for bean {1} (from: {2}), skipping registration of a bean\",\n\t\t\t\t\t\t\t\tnew Object[]{cfg.getClazzName(), cfg.getBeanName(),\n\t\t\t\t\t\t\t\t             (beanConfig != null ? beanConfig.getBeanName() : \"n/a\")});\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!tigase.util.reflection.ClassUtilBean.getInstance().getAllClasses().contains(clazz)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\ttoUnregister.remove(cfg.getBeanName());\n\n\t\t\t\tif (oldBc != null && oldBc.getClazz().equals(clazz) &&\n\t\t\t\t\t\t(oldBc.isExportable() || cfg.isExportable() == oldBc.isExportable())) {\n\t\t\t\t\tkernel.setBeanActive(cfg.getBeanName(), cfg.isActive());\n\t\t\t\t} else {\n\t\t\t\t\tBean ba = clazz.getAnnotation(Bean.class);\n\t\t\t\t\tBeanConfigBuilder cfgBuilder = kernel.registerBean(cfg.getBeanName()).asClass(clazz);\n\t\t\t\t\tcfgBuilder.setActive(cfg.isActive()).setSource(BeanConfig.Source.configuration);\n\t\t\t\t\tif (cfg.isExportable()) {\n\t\t\t\t\t\tcfgBuilder.exportable();\n\t\t\t\t\t}\n\t\t\t\t\tif (ba != null) {\n\t\t\t\t\t\tif (ba.exportable()) {\n\t\t\t\t\t\t\tcfgBuilder.exportable();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tcfgBuilder.registeredBy(beanConfig);\n\n\t\t\t\t\tif (beanConfig != null && beanConfig.getState() == BeanConfig.State.initialized) {\n\t\t\t\t\t\tcfgBuilder.exec();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcfgBuilder.execWithoutInject();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (ClassNotFoundException ex) {\n\t\t\t\tlog.log(Level.FINER,\n\t\t\t\t\t\t\"could not register bean '\" + cfg.getBeanName() + \"' as class '\" + cfg.getClazzName() +\n\t\t\t\t\t\t\t\t\"' is not available\", ex);\n\t\t\t}\n\t\t}\n\n\t\ttoUnregister.forEach(beanName -> kernel.unregister(beanName));\n\t}\n\n\t/**\n\t * Method applies configuration changes to bean. Should be called after configuration is updated.\n\t */\n\tpublic void configurationChanged() {\n\t\trefreshConfiguration(kernel);\n\t}\n\n\t/**\n\t * Method restors default configuration of a bean\n\t */\n\tpublic void restoreDefaults(String beanName) {\n\t\tBeanConfig beanConfig = kernel.getDependencyManager().getBeanConfig(beanName);\n\t\tObject bean = kernel.getInstance(beanName);\n\n\t\ttry {\n\t\t\tHashMap<Field, Object> defaultConfig = defaultFieldValues.get(beanConfig);\n\t\t\tif (defaultConfig == null) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfinal Field[] fields = DependencyManager.getAllFields(beanConfig.getClazz());\n\t\t\tfor (Field field : fields) {\n\n\t\t\t\tConfigField configField = field.getAnnotation(ConfigField.class);\n\n\t\t\t\tif (configField == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (!defaultConfig.containsKey(field)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tObject valueToSet = defaultConfig.get(field);\n\t\t\t\tBeanUtils.setValue(bean, field, valueToSet);\n\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new KernelException(\"Cannot inject configuration to bean \" + beanConfig.getBeanName(), e);\n\t\t}\n\n\t}\n\n\t/**\n\t * Method returns configuration of a bean.\n\t */\n\tprotected abstract Map<String, Object> getConfiguration(BeanConfig beanConfig);\n\n\tprotected Map<Field, Object> grabDefaultConfig(final BeanConfig beanConfig, final Object bean) {\n\t\ttry {\n\t\t\tHashMap<Field, Object> defaultConfig = defaultFieldValues.get(beanConfig);\n\t\t\tif (defaultConfig == null) {\n\t\t\t\tdefaultConfig = new HashMap<Field, Object>();\n\t\t\t\tdefaultFieldValues.put(beanConfig, defaultConfig);\n\t\t\t}\n\t\t\tfinal Field[] fields = DependencyManager.getAllFields(beanConfig.getClazz());\n\t\t\tfor (Field field : fields) {\n\n\t\t\t\tConfigField configField = field.getAnnotation(ConfigField.class);\n\n\t\t\t\tif (configField == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t} else {\n\t\t\t\t\tObject currentValue = BeanUtils.getValue(bean, field);\n\t\t\t\t\tif (!defaultConfig.containsKey(field)) {\n\t\t\t\t\t\tdefaultConfig.put(field, currentValue);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn defaultConfig;\n\t\t} catch (Exception e) {\n\t\t\tthrow new KernelException(\"Cannot grab default values of bean \" + beanConfig.getBeanName(), e);\n\t\t}\n\t}\n\n\tprotected Map<Field, Object> grabCurrentConfig(final Object bean, String beanName) {\n\t\tMap<Field, Object> config = new HashMap<>();\n\t\ttry {\n\t\t\tfinal Field[] fields = DependencyManager.getAllFields(bean.getClass());\n\n\t\t\tfor (Field field : fields) {\n\n\t\t\t\tConfigField configField = field.getAnnotation(ConfigField.class);\n\n\t\t\t\tif (configField == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t} else {\n\t\t\t\t\tObject currentValue = BeanUtils.getValue(bean, field);\n\t\t\t\t\tif (!config.containsKey(field)) {\n\t\t\t\t\t\tconfig.put(field, currentValue);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.FINEST, \"retrieval of configuration for bean \" + beanName + \" failed\", ex);\n\t\t}\n\t\treturn config;\n\t}\n\n\t/**\n\t * Method returns map of bean definitions found in the configuration\n\t * @param values configuration map\n\t */\n\tprotected Map<String, BeanDefinition> getBeanDefinitions(Map<String, Object> values) {\n\t\treturn new HashMap<>();\n\t}\n\n\t/**\n\t * Method returns queue of kernel and bean names to find bean config (path to the bean config from root of\n\t * the config).\n\t */\n\tprotected ArrayDeque<String> getBeanConfigPath(BeanConfig beanConfig) {\n\t\tKernel kernel = beanConfig.getKernel();\n\t\tArrayDeque<String> path = new ArrayDeque<>();\n\n\t\tif (!beanConfig.getBeanName().equals(beanConfig.getKernel().getName())) {\n\t\t\tpath.push(beanConfig.getBeanName());\n\t\t}\n\n\t\twhile (kernel.getParent() != null && kernel != this.kernel) {\n\t\t\tpath.push(kernel.getName());\n\t\t\tkernel = kernel.getParent();\n\t\t}\n\n\t\treturn path;\n\t}\n\n\tprotected abstract boolean hasDirectConfiguration(BeanConfig bc);\n\n\tprotected void refreshConfiguration(final Kernel kernel) {\n\t\t// TODO\n\t\t//kernel.beginDelayedInjection();\n\t\trefreshConfiguration_removeUndefinedBeans(kernel);\n\t\tregisterBeans(null, null, getProperties());\n\t\trefreshConfiguration_updateConfiguration(kernel);\n\t\t// TODO\n\t\t//kernel.finishDelayedInjection();\n\t}\n\n\tprotected void refreshConfiguration_removeUndefinedBeans(Kernel kernel) {\n\t\tSet<Class<?>> classes = tigase.util.reflection.ClassUtilBean.getInstance().getAllClasses();\n\t\tSet<BeanConfig> toRemove = kernel.getDependencyManager()\n\t\t\t\t.getBeanConfigs()\n\t\t\t\t.stream()\n\t\t\t\t.filter(bc -> bc.getSource() != BeanConfig.Source.hardcoded)\n\t\t\t\t.filter(bc -> !classes.contains(bc.getClazz()))\n\t\t\t\t.filter(bc -> {\n\t\t\t\t\tString name = bc.getClazz().getCanonicalName();\n\t\t\t\t\treturn (!name.startsWith(\"java.\")) && (!name.startsWith(\"javax.\")) &&\n\t\t\t\t\t\t\t(!name.startsWith(\"com.sun.\"));\n\t\t\t\t})\n\t\t\t\t.collect(Collectors.toSet());\n\t\ttoRemove.forEach(bc -> kernel.unregister(bc.getBeanName()));\n\n\t\tfor (String name : kernel.getNamesOf(Kernel.class)) {\n\t\t\tKernel subkernel = kernel.getInstance(name);\n\t\t\tif (subkernel == null || subkernel == kernel) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\trefreshConfiguration_removeUndefinedBeans(subkernel);\n\t\t}\n\t}\n\n\tprotected void refreshConfiguration_updateConfiguration(Kernel kernel) {\n\t\tSet<BeanConfig> toReconfigure = kernel.getDependencyManager()\n\t\t\t\t.getBeanConfigs()\n\t\t\t\t.stream()\n\t\t\t\t.filter(bc -> bc.getState() == BeanConfig.State.initialized)\n\t\t\t\t.filter(bc -> !(bc instanceof Kernel.DelegatedBeanConfig))\n\t\t\t\t.filter(bc -> !Kernel.class.isAssignableFrom(bc.getClazz()))\n\t\t\t\t.collect(Collectors.toSet());\n\t\ttoReconfigure.forEach(bc -> {\n\t\t\tif (kernel.isBeanClassRegistered(bc.getBeanName())) {\n\t\t\t\tAbstractBeanConfigurator.this.configure(bc, kernel.getInstance(bc.getBeanName()));\n\t\t\t}\n\t\t});\n\n\t\tfor (String name : kernel.getNamesOf(Kernel.class)) {\n\t\t\tKernel subkernel = kernel.getInstance(name);\n\t\t\tif (subkernel == null || subkernel == kernel) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\trefreshConfiguration_updateConfiguration(subkernel);\n\t\t}\n\t}\n\n\tprivate final boolean equals(Object o1, Object o2) {\n\t\treturn (o1 == null ? o2 == null : o1.equals(o2));\n\t}\n\n\tpublic static class BeanDefinition\n\t\t\textends HashMap {\n\n\t\tprivate boolean active = true;\n\t\tprivate String beanName;\n\t\tprivate String clazzName;\n\t\tprivate boolean exportable = false;\n\n\t\tpublic BeanDefinition() {\n\t\t}\n\n\t\tpublic BeanDefinition(BeanDefinition def) {\n\t\t\tthis.beanName = def.getBeanName();\n\t\t\tthis.clazzName = def.getClazzName();\n\t\t\tthis.active = def.isActive();\n\t\t\tthis.exportable = def.isExportable();\n\t\t\tthis.putAll(def);\n\t\t}\n\n\t\tpublic String getBeanName() {\n\t\t\treturn beanName;\n\t\t}\n\n\t\tpublic void setBeanName(String beanName) {\n\t\t\tthis.beanName = beanName;\n\t\t}\n\n\t\tpublic String getClazzName() {\n\t\t\treturn clazzName;\n\t\t}\n\n\t\tpublic void setClazzName(String clazzName) {\n\t\t\tthis.clazzName = clazzName;\n\t\t}\n\n\t\tpublic boolean isActive() {\n\t\t\treturn active;\n\t\t}\n\n\t\tpublic void setActive(boolean active) {\n\t\t\tthis.active = active;\n\t\t}\n\n\t\tpublic boolean isExportable() {\n\t\t\treturn exportable;\n\t\t}\n\n\t\tpublic void setExportable(boolean exportable) {\n\t\t\tthis.exportable = exportable;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object o) {\n\t\t\tif (o instanceof AbstractBeanConfigurator.BeanDefinition) {\n\t\t\t\tAbstractBeanConfigurator.BeanDefinition o1 = (AbstractBeanConfigurator.BeanDefinition) o;\n\t\t\t\tif (!beanName.equals(o1.beanName)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tif (clazzName == null) {\n\t\t\t\t\tif (o1.clazzName != null) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (!clazzName.equals(o1.clazzName)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (exportable != o1.exportable) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tif (active != o1.active) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn super.equals(o);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn super.hashCode() * 21 + Objects.hash(beanName, clazzName, exportable, active);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tfinal StringBuilder sb = new StringBuilder(\"BeanConfig{\");\n\t\t\tsb.append(\"beanName='\").append(beanName).append('\\'');\n\t\t\tsb.append(\", clazz=\").append(clazzName);\n\t\t\tsb.append(\", exportable=\").append(exportable);\n\t\t\tsb.append(\", active=\").append(active);\n\t\t\tsb.append(\", props=[\");\n\t\t\tIterator<Map.Entry> it = entrySet().iterator();\n\t\t\twhile (it.hasNext()) {\n\t\t\t\tEntry e = it.next();\n\t\t\t\tsb.append(\"\" + e.getKey()).append(\"=\").append(\"\" + e.getValue());\n\t\t\t\tif (it.hasNext()) {\n\t\t\t\t\tsb.append(\", \");\n\t\t\t\t}\n\t\t\t}\n\t\t\tsb.append('}');\n\t\t\treturn sb.toString();\n\t\t}\n\n\t\tpublic static class Builder\n\t\t\t\textends AbstractConfigBuilder<BeanDefinition, Builder> {\n\n\t\t\tprivate final Map<String, Object> parent;\n\n\t\t\tpublic Builder(Map<String, Object> parent) {\n\t\t\t\tsuper(new AbstractBeanConfigurator.BeanDefinition());\n\t\t\t\tthis.parent = parent;\n\t\t\t}\n\n\t\t\tpublic Builder() {\n\t\t\t\tthis(null);\n\t\t\t}\n\n\t\t\tpublic Builder active(boolean active) {\n\t\t\t\tmap.setActive(active);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tpublic Builder name(String name) {\n\t\t\t\tmap.setBeanName(name);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tpublic Builder clazz(Class<?> clazz) {\n\t\t\t\tmap.setClazzName(clazz == null ? null : clazz.getCanonicalName());\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tpublic Builder clazz(String clazz) {\n\t\t\t\tmap.setClazzName(clazz);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tpublic BeanDefinition build() {\n\t\t\t\tif (parent != null) {\n\t\t\t\t\tparent.put(map.getBeanName(), map);\n\t\t\t\t}\n\t\t\t\treturn map;\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/beans/config/BeanConfigurator.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.beans.config;\n\nimport tigase.kernel.core.BeanConfig;\n\nimport java.util.Map;\n\n/**\n * Bean configurator. <p> Newly created beans should be configured: it means put specific values to fields, etc.\n * Configurator no need to inject dependencies. This interface allows to create any kind of configurator for beans. </p>\n * <p> Note, that {@link BeanConfig} parameter is just internal metadata used to identify and keep dependencies, etc.\n * </p>\n */\npublic interface BeanConfigurator {\n\n\t/**\n\t * Name of default configurator. It will be used by default during creating new beans by Kernel.\n\t */\n\tString DEFAULT_CONFIGURATOR_NAME = \"defaultBeanConfigurator\";\n\n\t/**\n\t * Notify bean configurator that configuration was changed and beans needs to be reconfigured\n\t */\n\tvoid configurationChanged();\n\n\t/**\n\t * Configure bean.\n\t *\n\t * @param beanConfig internal bean configuration.\n\t * @param bean bean to configure.\n\t */\n\tvoid configure(BeanConfig beanConfig, Object bean);\n\n\t/**\n\t * Looks for and registers beans which should be registered due to initialization of passed bean.\n\t *\n\t * List of beans to register may come from config (<code>values</code>), annotations, etc.\n\t *\n\t * @param beanConfig bean config of initializing bean\n\t * @param bean instance of initializing bean\n\t * @param valeus configuration for the initializing bean\n\t */\n\tvoid registerBeans(BeanConfig beanConfig, Object bean, Map<String, Object> valeus);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/beans/config/ConfigAlias.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.beans.config;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n/**\n * Annotation to add additional aliases to the fields. * <br> Useful if field annotated with {@link\n * tigase.kernel.beans.config.ConfigField} is inaccessible direcly, ie. is defined in extended class.\n * <br>\n * Created by andrzej on 05.08.2016.\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\npublic @interface ConfigAlias {\n\n\t/**\n\t * Name of the field\n\t *\n\t */\n\tString field();\n\n\t/**\n\t * Alias for the field\n\t *\n\t */\n\tString alias();\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/beans/config/ConfigAliases.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.beans.config;\n\nimport java.lang.annotation.*;\n\n/**\n * Annotation allows to add multiple {@link tigase.kernel.beans.config.ConfigAlias} aliases for multiple configuration\n * fields\n * <br>\n * Created by andrzej on 05.08.2016.\n */\n@Target({ElementType.TYPE})\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\npublic @interface ConfigAliases {\n\n\tConfigAlias[] value();\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/beans/config/ConfigField.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.beans.config;\n\nimport java.lang.annotation.*;\n\n/**\n * Annotation to define configurable field.\n */\n@Target({ElementType.FIELD})\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\npublic @interface ConfigField {\n\n\t/**\n\t * Description of field. May be used in all human readable forms.\n\t *\n\t * @return description of field.\n\t */\n\tString desc();\n\n\t/**\n\t * Makes alias of \"component root level\" property in config file. <p> Not only {@code component/bean/property=value}\n\t * will be used but also {@code component/alias=value}. </p>\n\t *\n\t * @return alias of config field.\n\t */\n\tString alias() default \"\";\n\n\t/**\n\t * Allows config to be set on one of parent levels of configuration using alias.\n\t * If not set to <code>true</code>, it is possible to use alias only on the config level of the bean.\n\t */\n\tboolean allowAliasFromParent() default true;\n\n\tConfigFieldType type() default ConfigFieldType.Plain;\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/beans/config/ConfigFieldType.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.kernel.beans.config;\n\npublic enum ConfigFieldType {\n\tPlain,\n\tPassword,\n\tJdbcUrl;\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/beans/config/ConfigurationChangedAware.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.beans.config;\n\nimport java.util.Collection;\n\n/**\n * Interface which should be implemented by beans which want to get notified when all configuration changes were applied\n * and get full list of changed fields in a single place.\n */\npublic interface ConfigurationChangedAware {\n\n\t/**\n\t * Method called when configuration was applied to bean.\n\t *\n\t * @param changedFields collection of field names which were changed\n\t */\n\tvoid beanConfigurationChanged(Collection<String> changedFields);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/beans/selector/ClusterModeRequired.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.beans.selector;\n\nimport java.lang.annotation.*;\n\n/**\n * Marks bean as it required cluster mode value to be equal to value of active property.\n */\n@Target({ElementType.TYPE})\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\npublic @interface ClusterModeRequired {\n\n\tboolean active();\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/beans/selector/ConfigType.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.beans.selector;\n\nimport java.lang.annotation.*;\n\n/**\n * Defines value of 'config-type' for which bean should be loaded.\n */\n@Target({ElementType.TYPE})\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\npublic @interface ConfigType {\n\n\tConfigTypeEnum[] value();\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/beans/selector/ConfigTypeEnum.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.beans.selector;\n\nimport java.util.Arrays;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * Created by andrzej on 26.04.2017.\n */\npublic enum ConfigTypeEnum {\n\tDefaultMode(\"default\"),\n\tSessionManagerMode(\"session-manager\"),\n\tConnectionManagersMode(\"connection-managers\"),\n\tComponentMode(\"component\"),\n\tSetupMode(\"setup\");\n\n\tprivate static final ConcurrentHashMap<String, ConfigTypeEnum> values = new ConcurrentHashMap();\n\n\tstatic {\n\t\tArrays.asList(values()).forEach(val -> values.put(val.id(), val));\n\t}\n\n\tprivate final String id;\n\n\tpublic static ConfigTypeEnum valueForId(String id) {\n\t\treturn values.get(id);\n\t}\n\n\tConfigTypeEnum(String id) {\n\t\tthis.id = id;\n\t}\n\n\tpublic String id() {\n\t\treturn id;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/beans/selector/ServerBeanSelector.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.beans.selector;\n\nimport tigase.kernel.beans.BeanSelector;\nimport tigase.kernel.beans.config.AbstractBeanConfigurator;\nimport tigase.kernel.beans.config.BeanConfigurator;\nimport tigase.kernel.core.Kernel;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * Created by andrzej on 26.04.2017.\n */\npublic class ServerBeanSelector\n\t\timplements BeanSelector {\n\n\tprivate static boolean checkClusterMode(Class clazz, Kernel kernel) {\n\t\tClusterModeRequired clusterModeRequired = (ClusterModeRequired) clazz.getAnnotation(ClusterModeRequired.class);\n\t\treturn clusterModeRequired == null || clusterModeRequired.active() == getClusterMode(kernel);\n\t}\n\n\tprivate static boolean checkConfigType(Class clazz, Kernel kernel) {\n\t\tConfigType configType = (ConfigType) clazz.getAnnotation(ConfigType.class);\n\t\tif (configType == null) {\n\t\t\treturn true;\n\t\t}\n\t\tList<ConfigTypeEnum> supportedTypes = Arrays.asList(configType.value());\n\n\t\tConfigTypeEnum activeConfigType = getConfigType(kernel);\n\n\t\treturn supportedTypes.contains(activeConfigType);\n\t}\n\n\tpublic static boolean getClusterMode(Kernel kernel) {\n\t\tObject val = getProperty(kernel, \"cluster-mode\", false);\n\t\tif (val instanceof Boolean) {\n\t\t\treturn (Boolean) val;\n\t\t} else {\n\t\t\treturn Boolean.valueOf((String) val);\n\t\t}\n\n\t}\n\n\tpublic static ConfigTypeEnum getConfigType(Kernel kernel) {\n\t\twhile (kernel.getParent() != null) {\n\t\t\tkernel = kernel.getParent();\n\t\t}\n\n\t\tString type = getProperty(kernel, \"config-type\", \"default\");\n\t\tswitch (type) {\n\t\t\tcase \"--gen-config-def\":\n\t\t\tcase \"--gen-config-default\":\n\t\t\t\ttype = \"default\";\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t\treturn ConfigTypeEnum.valueForId(type);\n\t}\n\n\tprotected static <T> T getProperty(Kernel kernel, String name, T defValue) {\n\t\tif (kernel.isBeanClassRegistered(BeanConfigurator.DEFAULT_CONFIGURATOR_NAME)) {\n\t\t\treturn (T) kernel.getInstance(AbstractBeanConfigurator.class).getProperties().getOrDefault(name, defValue);\n\t\t}\n\t\treturn defValue;\n\t}\n\n\t@Override\n\tpublic boolean shouldRegister(Class clazz, Kernel kernel) {\n\t\treturn checkClusterMode(clazz, kernel) && checkConfigType(clazz, kernel);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/core/BeanConfig.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.core;\n\nimport java.lang.reflect.Field;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * This is internal configuration of each bean. It stores name of bean, dependencies, state of bean etc.\n */\npublic class BeanConfig {\n\n\tpublic enum Source {\n\t\thardcoded,\n\t\tannotation,\n\t\tconfiguration\n\t}\n\t/**\n\t * State of bean.\n\t */\n\tpublic enum State {\n\t\t/**\n\t\t * Bean is initialized and ready to use.\n\t\t */\n\t\tinitialized,\n\t\t/**\n\t\t * Instance of bean is created, but bean isn't initialized.\n\t\t */\n\t\tinstanceCreated,\n\t\t/**\n\t\t * Bean class is registered, but instance of bean isn't created yet.\n\t\t */\n\t\tregistered,\n\t\t/**\n\t\t * Bean class is registered, but it CANNOT be used!!! Should be treated as not registered at all.\n\t\t */\n\t\tinactive,\n\t}\n\tprivate final String beanName;\n\tprivate final Class<?> clazz;\n\tprivate final Map<Field, Dependency> fieldDependencies = new HashMap<Field, Dependency>();\n\tprivate String beanInstanceName = null;\n\tprivate boolean exportable;\n\tprivate BeanConfig factory;\n\tprivate Kernel kernel;\n\tprivate boolean pinned = true;\n\tprivate Set<BeanConfig> registeredBeans = new HashSet<>();\n\tprivate Set<BeanConfig> registeredBy = new HashSet<>();\n\tprivate Source source = Source.hardcoded;\n\tprivate State state;\n\n\tBeanConfig(String id, Class<?> clazz) {\n\t\tsuper();\n\t\tthis.beanName = id;\n\t\tthis.clazz = clazz;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tBeanConfig other = (BeanConfig) obj;\n\t\tif (beanName == null) {\n\t\t\tif (other.beanName != null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else if (!beanName.equals(other.beanName)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Returns name of bean.\n\t *\n\t * @return name of bean.\n\t */\n\tpublic String getBeanName() {\n\t\treturn beanName;\n\t}\n\n\t/**\n\t * Returns class of bean.\n\t *\n\t * @return class of bean.\n\t */\n\tpublic Class<?> getClazz() {\n\t\treturn clazz;\n\t}\n\n\t/**\n\t * Return factory of bean.\n\t *\n\t * @return factory of bean. It may return <code>null</code> if default factory is used.\n\t */\n\tpublic BeanConfig getFactory() {\n\t\treturn factory;\n\t}\n\n\tvoid setFactory(final BeanConfig bfc) {\n\t\tthis.factory = bfc;\n\t}\n\n\t/**\n\t * Returns map of dependencies. Note that Kernel has field-based-dependency model, it means that each dependency\n\t * must be related to field in class.\n\t *\n\t * @return map of dependencies.\n\t */\n\tpublic Map<Field, Dependency> getFieldDependencies() {\n\t\treturn fieldDependencies;\n\t}\n\n\t/**\n\t * Returns {@link Kernel} managing this bean.\n\t *\n\t * @return {@link Kernel}.\n\t */\n\tpublic Kernel getKernel() {\n\t\treturn kernel;\n\t}\n\n\tvoid setKernel(Kernel kernel) {\n\t\tthis.kernel = kernel;\n\t}\n\n\t/**\n\t * Returns state of bean.\n\t *\n\t * @return state of bean.\n\t */\n\tpublic State getState() {\n\t\treturn state;\n\t}\n\n\tvoid setState(State state) {\n\t\tthis.state = state;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tfinal int prime = 31;\n\t\tint result = 1;\n\t\tresult = prime * result + ((beanName == null) ? 0 : beanName.hashCode());\n\t\treturn result;\n\t}\n\n\t/**\n\t * Checks if bean may be visible in child Kernels.\n\t *\n\t * @return <code>true</code> if beans will be visible in child Kernel (other Kernels deployed as beans to current\n\t * Kernel).\n\t */\n\tpublic boolean isExportable() {\n\t\treturn exportable;\n\t}\n\n\tvoid setExportable(boolean value) {\n\t\tthis.exportable = value;\n\t}\n\n\t/**\n\t * Returns information if bean in pinned. If bean is pinned it will not be unloaded even if no other bean uses it.\n\t */\n\tpublic boolean isPinned() {\n\t\treturn pinned;\n\t}\n\n\tpublic void setPinned(boolean pinned) {\n\t\tthis.pinned = pinned;\n\t}\n\n\t/**\n\t * Returns information about source of the bean registration (annotation, code, config, etc.)\n\t */\n\tpublic Source getSource() {\n\t\treturn source;\n\t}\n\n\tvoid setSource(Source source) {\n\t\tthis.source = source;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tfinal StringBuilder sb = new StringBuilder(\"BeanConfig{\");\n\t\tsb.append(\"beanName=\").append(beanName);\n\t\tsb.append(\", class=\").append(clazz.getName());\n\t\tif (exportable) {\n\t\t\tsb.append(\", exportable=\").append(exportable);\n\t\t}\n\t\tsb.append(\", pinned=\").append(pinned);\n\t\tif (factory != null) {\n\t\t\tsb.append(\", factory=\").append(factory);\n\t\t}\n\t\tsb.append(\", kernel=\").append(kernel.getName());\n\t\tsb.append(\", source=\").append(source);\n\t\tsb.append(\", state=\").append(state);\n\t\tsb.append('}');\n\t\treturn sb.toString();\n\t}\n\n\t/**\n\t * List of beans registered by registration of this bean - related to <code>Bean::parent</code>.\n\t */\n\tpublic Set<BeanConfig> getRegisteredBeans() {\n\t\treturn registeredBeans;\n\t}\n\t\n\tpublic void addRegisteredBean(BeanConfig beanConfig) {\n\t\tregisteredBeans.add(beanConfig);\n\t}\n\n\tpublic void removeRegisteredBean(BeanConfig beanConfig) {\n\t\tregisteredBeans.remove(beanConfig);\n\t}\n\n\tpublic void addRegisteredBy(BeanConfig beanConfig) {\n\t\tregisteredBy.add(beanConfig);\n\t}\n\n\tpublic boolean removeRegisteredBy(BeanConfig beanConfig) {\n\t\tregisteredBy.remove(beanConfig);\n\t\treturn registeredBy.isEmpty();\n\t}\n\n\t/**\n\t * Set of beans which caused registration of this bean - related to <code>Bean::parent</code>.\n\t */\n\tpublic Set<BeanConfig> getRegisteredBy() {\n\t\treturn registeredBy;\n\t}\n\n\tprotected String getBeanInstanceName() {\n\t\treturn beanInstanceName == null ? getBeanName() : beanInstanceName;\n\t}\n\n\tprotected void setBeanInstanceName(String beanInstanceName) {\n\t\tthis.beanInstanceName = beanInstanceName;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/core/BeanConfigBuilder.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.core;\n\nimport tigase.kernel.KernelException;\nimport tigase.kernel.beans.BeanFactory;\n\nimport java.util.logging.Logger;\n\n/**\n * Builder to help register beans in Kernel. <p> Usage: </p>\n * <pre>\n * {@code\n *\n *  // If Bean1.class is annotated by @Bean annotation.\n *  registerBean(Bean1.class).exec();\n *\n *  // If Bean2 isn't annotated or should be registered with different name.\n *  krnl.registerBean(\"bean2\").asClass(Bean2.class).exec();\n *\n *  // To register already created variable bean4 as bean \"bean4\".\n *  krnl.registerBean(\"bean4\").asInstance(bean4).exec();\n *\n *  // If Bean5 have to been created by Bean5Factory.\n *  krnl.registerBean(\"bean5\").asClass(Bean5.class).withFactory(Bean5Factory.class).exec();\n * }\n * </pre>\n */\npublic class BeanConfigBuilder {\n\n\tprotected final Logger log = Logger.getLogger(this.getClass().getName());\n\tprivate final String beanName;\n\tprivate final DependencyManager dependencyManager;\n\tprivate final Kernel kernel;\n\tprivate BeanConfig beanConfig;\n\tprivate Object beanInstance;\n\tprivate Class<?> clazz;\n\tprivate BeanConfig factoryBeanConfig;\n\n\tBeanConfigBuilder(Kernel kernel, DependencyManager dependencyManager, String beanName) {\n\t\tthis.kernel = kernel;\n\t\tthis.dependencyManager = dependencyManager;\n\t\tthis.beanName = beanName;\n\t}\n\n\t/**\n\t * Registers bean as type to be created when it will be required.\n\t *\n\t * @param cls class of bean.\n\t *\n\t * @return {@link BeanConfigBuilder}.\n\t */\n\tpublic BeanConfigBuilder asClass(Class<?> cls) {\n\t\tthis.clazz = cls;\n\t\tif (this.beanConfig != null) {\n\t\t\tthrowException(new KernelException(\"Class or instance is already defined for bean '\" + beanName + \"'\"));\n\t\t}\n\n\t\tthis.beanConfig = dependencyManager.createBeanConfig(kernel, beanName, cls);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Registers class instance as bean.\n\t *\n\t * @param bean instance of bean.\n\t *\n\t * @return {@link BeanConfigBuilder}.\n\t */\n\tpublic BeanConfigBuilder asInstance(Object bean) {\n\t\tif (this.beanConfig != null) {\n\t\t\tthrowException(new KernelException(\"Class or instance is already defined for bean '\" + beanName + \"'\"));\n\t\t}\n\n\t\tthis.beanConfig = dependencyManager.createBeanConfig(kernel, beanName, bean.getClass());\n\t\tthis.beanInstance = bean;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Finishing registration of bean.\n\t */\n\tpublic void exec() {\n\t\texecWithoutInject();\n\t\tkernel.registerLinks(beanName);\n\t\tkernel.injectIfRequired(beanConfig);\n\t}\n\n\tpublic BeanConfig execWithoutInject() {\n\t\tif (beanConfig == null) {\n\t\t\tlog.warning(\"Bean \" + clazz +\n\t\t\t\t\t\t\t\t\" cannot be registered, because Kernel cannot create configuration for this bean.\");\n\t\t\tkernel.currentlyUsedConfigBuilder = null;\n\t\t\treturn null;\n\t\t}\n\n\t\tbeanConfig = kernel.registerBean(beanConfig, factoryBeanConfig, beanInstance);\n\n\t\treturn beanConfig;\n\t}\n\n\t/**\n\t * Mark bean as 'exportable'. It means that bean will be visible for all child Kernels registered in current\n\t * Kernel.\n\t *\n\t * @return {@link BeanConfigBuilder}.\n\t */\n\tpublic BeanConfigBuilder exportable() {\n\t\tif (beanConfig == null) {\n\t\t\tlog.warning(\"Bean \" + clazz +\n\t\t\t\t\t            \" cannot be configured as exportable, because Kernel cannot create configuration for this bean.\" +\n\t\t\t\t\t            this);\n\t\t}\n\t\tbeanConfig.setExportable(true);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Returns name of bean.\n\t *\n\t * @return name of bean.\n\t */\n\tpublic String getBeanName() {\n\t\treturn beanName;\n\t}\n\n\tpublic BeanConfigBuilder setActive(boolean active) {\n\t\tif (beanConfig != null) {\n\t\t\tif (active) {\n\t\t\t\tbeanConfig.setState(null);\n\t\t\t} else {\n\t\t\t\tbeanConfig.setState(BeanConfig.State.inactive);\n\t\t\t}\n\t\t}\n\t\treturn this;\n\t}\n\n\tpublic BeanConfigBuilder setPinned(boolean pinned) {\n\t\tif (beanConfig != null) {\n\t\t\tbeanConfig.setPinned(pinned);\n\t\t}\n\t\treturn this;\n\t}\n\n\tpublic BeanConfigBuilder setSource(BeanConfig.Source source) {\n\t\tif (beanConfig != null) {\n\t\t\tbeanConfig.setSource(source);\n\t\t}\n\t\treturn this;\n\t}\n\n\tpublic BeanConfigBuilder registeredBy(BeanConfig parent) {\n\t\tif (parent != null) {\n\t\t\tif (beanConfig != null) {\n\t\t\t\tbeanConfig.addRegisteredBy(parent);\n\t\t\t}\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Defines factory for currently registered bean.\n\t *\n\t * @param beanFactoryClass bean factory class.\n\t *\n\t * @return {@link BeanConfigBuilder}.\n\t */\n\tpublic BeanConfigBuilder withFactory(Class<? extends BeanFactory> beanFactoryClass) {\n\t\tif (beanInstance != null) {\n\t\t\tthrowException(\n\t\t\t\t\tnew KernelException(\"Cannot register factory to bean '\" + beanName + \"' registered as instance.\"));\n\t\t}\n\t\tif (factoryBeanConfig != null) {\n\t\t\tthrowException(new KernelException(\"Factory for bean '\" + beanName + \"' is already registered.\"));\n\t\t}\n\n\t\tthis.factoryBeanConfig = dependencyManager.createBeanConfig(kernel, beanName + \"#FACTORY\", beanFactoryClass);\n\t\tbeanConfig.setFactory(factoryBeanConfig);\n\n\t\treturn this;\n\t}\n\n\tprotected void throwException(KernelException e) {\n\t\tkernel.currentlyUsedConfigBuilder = null;\n\t\tthrow e;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/core/Dependency.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.core;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Type;\n\npublic class Dependency {\n\n\tprivate BeanConfig beanConfig;\n\n\tprivate String beanName;\n\n\tprivate Field field;\n\tprivate Type genericType;\n\tprivate boolean nullAllowed;\n\tprivate Class<?> type;\n\n\t/**\n\t * Creates instance of class.\n\t *\n\t * @param beanConfig definition of bean.\n\t */\n\tpublic Dependency(BeanConfig beanConfig) {\n\t\tthis.beanConfig = beanConfig;\n\t}\n\n\t/**\n\t * Returns definition of bean.\n\t *\n\t * @return definition of bean.\n\t */\n\tpublic BeanConfig getBeanConfig() {\n\t\treturn beanConfig;\n\t}\n\n\t/**\n\t * Returns name of dependent bean.\n\t *\n\t * @return name of dependent bean, or <code>null</code> if name is not specified.\n\t */\n\tpublic String getBeanName() {\n\t\treturn beanName;\n\t}\n\n\tpublic void setBeanName(String beanId) {\n\t\tthis.beanName = beanId;\n\t}\n\n\t/**\n\t * Returns field to be filled by dependency.\n\t *\n\t * @return field.\n\t */\n\tpublic Field getField() {\n\t\treturn field;\n\t}\n\n\tpublic void setField(Field field) {\n\t\tthis.field = field;\n\t}\n\n\tpublic Type getGenericType() {\n\t\treturn genericType;\n\t}\n\n\tpublic void setGenericType(Type genericType) {\n\t\tthis.genericType = genericType;\n\t}\n\n\t/**\n\t * Returns type of wanted bean.\n\t *\n\t * @return type of bean.\n\t */\n\tpublic Class<?> getType() {\n\t\treturn type;\n\t}\n\n\tpublic void setType(Class<?> type) {\n\t\tthis.type = type;\n\t}\n\n\t/**\n\t * Checks if empty value may be injected.\n\t *\n\t * @return <code>true</code> if dependency is optional.\n\t */\n\tpublic boolean isNullAllowed() {\n\t\treturn nullAllowed;\n\t}\n\n\tpublic void setNullAllowed(boolean nullAllowed) {\n\t\tthis.nullAllowed = nullAllowed;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tif (beanName != null) {\n\t\t\treturn \"bean:\" + beanName;\n\t\t} else {\n\t\t\treturn \"type:\" + type.getName();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/core/DependencyGrapher.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.core;\n\nimport tigase.kernel.core.Kernel.DelegatedBeanConfig;\n\nimport java.util.HashSet;\n\n/**\n * Creates graph of beans dependency in <a href=\"www.graphviz.org\">Graphviz</a> format.\n */\npublic class DependencyGrapher {\n\n\tprivate Kernel kernel;\n\n\tpublic DependencyGrapher() {\n\t}\n\n\tpublic DependencyGrapher(Kernel krnl) {\n\t\tsetKernel(krnl);\n\t}\n\n\t/**\n\t * Returns dependency graph in Graphviz format.\n\t *\n\t * @return graph.\n\t */\n\tpublic String getDependencyGraph() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"digraph \").append(\"Context\").append(\" {\\n\");\n\t\tsb.append(\"label=\").append(\"\\\"\").append(kernel.getName()).append(\"\\\"\\n\");\n\t\tsb.append(\"node[shape=record,style=filled,fillcolor=khaki1, color=brown]\\n\");\n\t\tsb.append(\"edge[color=brown]\\n\");\n\n\t\t// sb.append(\"rank=same\\n\");\n\n\t\tHashSet<String> connections = new HashSet<String>();\n\t\tdrawContext(sb, connections, kernel);\n\n\t\tfor (String string : connections) {\n\t\t\tsb.append(string).append('\\n');\n\t\t}\n\t\tsb.append(\"}\\n\");\n\t\treturn sb.toString();\n\t}\n\n\tpublic Kernel getKernel() {\n\t\treturn kernel;\n\t}\n\n\t/**\n\t * Sets Kernel instance.\n\t *\n\t * @param kernel instance of Kernel.\n\t */\n\tpublic void setKernel(Kernel kernel) {\n\t\tthis.kernel = kernel;\n\t}\n\n\tprivate void drawContext(StringBuilder structureSB, HashSet<String> connections, Kernel kernel) {\n\n\t\tfinal DependencyManager dependencyManager = kernel.getDependencyManager();\n\t\tstructureSB.append(\"subgraph \").append(\" {\\n\");\n\n\t\tfor (BeanConfig bc : dependencyManager.getBeanConfigs()) {\n\t\t\tif (bc.getClazz().equals(Kernel.class)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tstructureSB.append('\"').append(bc.getKernel().getName() + \".\" + bc.getBeanName()).append('\"').append(\"[\");\n\n\t\t\tif (bc instanceof DelegatedBeanConfig) {\n\t\t\t\tstructureSB.append(\"label=\\\"\");\n\t\t\t\tstructureSB.append(bc.getBeanName());\n\t\t\t\tstructureSB.append(\"\\\"\");\n\t\t\t\tstructureSB.append(\"shape=oval\");\n\t\t\t} else {\n\t\t\t\tstructureSB.append(\"label=\\\"{\");\n\t\t\t\tstructureSB.append(bc.getBeanName())\n\t\t\t\t\t\t.append(\"\\\\n\")\n\t\t\t\t\t\t.append(\"(\")\n\t\t\t\t\t\t.append(bc.getClazz().getName())\n\t\t\t\t\t\t.append(\")\");\n\t\t\t\tstructureSB.append(\"}\\\"\");\n\t\t\t}\n\t\t\tstructureSB.append(\"];\\n\");\n\t\t}\n\t\tstructureSB.append(\"}\\n\");\n\n\t\tint c = 0;\n\t\tfor (final BeanConfig bc : dependencyManager.getBeanConfigs()) {\n\t\t\t++c;\n\n\t\t\tif (bc.getFactory() != null) {\n\t\t\t\tBeanConfig dBean = bc.getFactory();\n\t\t\t\tStringBuilder sbi = new StringBuilder();\n\t\t\t\tsbi.append('\"').append(bc.getKernel().getName() + \".\" + bc.getBeanName()).append('\"');\n\t\t\t\t// sb.append(':').append(dp.getField().getName());\n\t\t\t\tsbi.append(\"->\");\n\n\t\t\t\tsbi.append('\"')\n\t\t\t\t\t\t.append(dBean.getKernel().getName() + \".\" + dBean.getBeanName())\n\t\t\t\t\t\t.append('\"')\n\t\t\t\t\t\t.append(\"[style=\\\"dashed\\\"]\");\n\n\t\t\t\tconnections.add(sbi.toString());\n\t\t\t}\n\n\t\t\tif (bc instanceof DelegatedBeanConfig) {\n\t\t\t\tfinal BeanConfig oryginal = ((DelegatedBeanConfig) bc).getOriginal();\n\t\t\t\tStringBuilder sbi = new StringBuilder();\n\t\t\t\tsbi.append('\"').append(oryginal.getKernel().getName() + \".\" + oryginal.getBeanName()).append('\"');\n\t\t\t\tsbi.append(\"->\");\n\t\t\t\tsbi.append('\"')\n\t\t\t\t\t\t.append(bc.getKernel().getName() + \".\" + bc.getBeanName())\n\t\t\t\t\t\t.append('\"')\n\t\t\t\t\t\t.append(\"[style=dotted,arrowtail=none,arrowhead=none]\");\n\t\t\t\tconnections.add(sbi.toString());\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (Dependency dp : bc.getFieldDependencies().values()) {\n\n\t\t\t\tBeanConfig[] dBeans = dependencyManager.getBeanConfig(dp);\n\t\t\t\tfor (BeanConfig dBean : dBeans) {\n\t\t\t\t\tBeanConfig fromBC = bc;\n\t\t\t\t\tStringBuilder sbi = new StringBuilder();\n\t\t\t\t\tif (dBean != null && dBean.getKernel() != bc.getKernel()) {\n\t\t\t\t\t\tsbi.append(\"/* inne kernele */ \");\n\t\t\t\t\t\tfromBC = findDelegateIn(bc, dBean.getKernel().getDependencyManager());\n\t\t\t\t\t}\n\n\t\t\t\t\tif (dBean == null) {\n\t\t\t\t\t\tsbi.append('\"').append(fromBC.getKernel().getName() + \".\" + fromBC.getBeanName()).append('\"');\n\t\t\t\t\t\tsbi.append(\"->\");\n\t\t\t\t\t\tsbi.append(\"{UNKNOWN_\")\n\t\t\t\t\t\t\t\t.append(c)\n\t\t\t\t\t\t\t\t.append(\"[label=\\\"\")\n\t\t\t\t\t\t\t\t.append(dp)\n\t\t\t\t\t\t\t\t.append(\"\\\", fillcolor=red, style=filled, shape=box]}\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsbi.append('\"').append(fromBC.getKernel().getName() + \".\" + fromBC.getBeanName()).append('\"');\n\t\t\t\t\t\tsbi.append(\"->\");\n\t\t\t\t\t\tsbi.append('\"').append(dBean.getKernel().getName() + \".\" + dBean.getBeanName()).append('\"');\n\t\t\t\t\t}\n\t\t\t\t\tconnections.add(sbi.toString());\n\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (BeanConfig kc : dependencyManager.getBeanConfigs(Kernel.class, null, null)) {\n\t\t\tKernel ki = kernel.getInstance(kc.getBeanName());\n\t\t\tstructureSB.append(\"subgraph \").append(\"cluster_\").append(ki.hashCode()).append(\" {\\n\");\n\t\t\tstructureSB.append(\"label=\").append(\"\\\"\").append(ki.getName()).append(\"\\\"\\n\");\n\t\t\tif (ki != kernel) {\n\t\t\t\tdrawContext(structureSB, connections, ki);\n\t\t\t}\n\t\t\tstructureSB.append(\"}\\n\");\n\t\t}\n\n\t}\n\n\tprivate BeanConfig findDelegateIn(BeanConfig dBean, DependencyManager dependencyManager) {\n\t\tfor (BeanConfig bc : dependencyManager.getBeanConfigs()) {\n\t\t\tif (bc instanceof DelegatedBeanConfig) {\n\t\t\t\tBeanConfig orig = ((DelegatedBeanConfig) bc).getOriginal();\n\t\t\t\tif (orig == dBean) {\n\t\t\t\t\treturn bc;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn dBean;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/core/DependencyManager.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.core;\n\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.core.BeanConfig.State;\nimport tigase.util.reflection.ReflectionHelper;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.lang.reflect.TypeVariable;\nimport java.util.*;\nimport java.util.Map.Entry;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class DependencyManager {\n\n\tprotected final Logger log = Logger.getLogger(this.getClass().getName());\n\tprivate final Map<String, BeanConfig> beanConfigs = new ConcurrentHashMap<>();\n\tprivate DependencyManager parent;\n\t/**\n\t * if <code>true</code> then DependencyManager will throw exception if it can't create beanConfig. If\n\t * <code>false</code> then {@link DependencyManager#createBeanConfig(Kernel, String, Class)} will return null\n\t * instead of BeanConfig.\n\t */\n\tprivate boolean throwExceptionIfCannotCreate = false;\n\n\tpublic static Field[] getAllFields(Class<?> klass) {\n\t\tList<Field> fields = new ArrayList<Field>();\n\t\tfields.addAll(Arrays.asList(klass.getDeclaredFields()));\n\t\tif (klass.getSuperclass() != null) {\n\t\t\tfields.addAll(Arrays.asList(getAllFields(klass.getSuperclass())));\n\t\t}\n\t\treturn fields.toArray(new Field[]{});\n\t}\n\n\tpublic static boolean match(Dependency dependency, BeanConfig beanConfig) {\n\t\tif (dependency.getBeanName() != null) {\n\t\t\treturn beanConfig.getBeanName().equals(dependency.getBeanName());\n\t\t} else if (dependency.getType() != null) {\n\t\t\tClass<?> type = dependency.getType();\n\t\t\tif (Collection.class.isAssignableFrom(type)) {\n\t\t\t\tMap<TypeVariable<?>, Type> expectedTypes = ReflectionHelper.createGenericsTypeMap(\n\t\t\t\t\t\tdependency.getBeanConfig().getClazz());\n\t\t\t\treturn ReflectionHelper.compareTypes(ReflectionHelper.getCollectionParamter(dependency.getGenericType(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdependency.getBeanConfig()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.getClazz()),\n\t\t\t\t\t\t\t\t\t\t\t\t\t beanConfig.getClazz(), expectedTypes, null);\n\t\t\t\t//type = ReflectionHelper.getCollectionParamter(dependency.getGenericType(), dependency.getBeanConfig().getClazz());\n\t\t\t}\n\t\t\treturn type.isAssignableFrom(beanConfig.getClazz());\n\t\t} else {\n\t\t\tthrow new RuntimeException(\"Unsupported dependecy type.\");\n\t\t}\n\t}\n\n\tpublic BeanConfig[] getBeanConfig(Dependency dependency) {\n\t\tArrayList<BeanConfig> bcs = new ArrayList<BeanConfig>();\n\t\tif (this.parent != null && this.parent != this) {\n\t\t\tBeanConfig[] pds = this.parent.getBeanConfig(dependency);\n\t\t\tfor (BeanConfig beanConfig : pds) {\n\t\t\t\tif (beanConfig != null && beanConfig.isExportable() && beanConfig.getState() != State.inactive) {\n\t\t\t\t\tbcs.add(beanConfig);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (dependency.getBeanName() != null) {\n\t\t\tBeanConfig b = beanConfigs.get(dependency.getBeanName());\n\t\t\tif (b != null && b.getState() != State.inactive) {\n\t\t\t\t// if there is a named bean in a parent kernel we need to override it!\n\t\t\t\tbcs.clear();\n\t\t\t\tbcs.add(b);\n\t\t\t}\n\t\t\tif (bcs.isEmpty()) {\n\t\t\t\tbcs.add(null);\n\t\t\t}\n\t\t} else if (dependency.getType() != null) {\n\t\t\tClass<?> type = dependency.getType();\n\t\t\tif (Collection.class.isAssignableFrom(type)) {\n\t\t\t\tType fieldGenericType = dependency.getGenericType();\n\t\t\t\tif (fieldGenericType instanceof ParameterizedType) {\n\t\t\t\t\tParameterizedType pt = (ParameterizedType) fieldGenericType;\n\t\t\t\t\tif (pt.getActualTypeArguments().length == 1) {\n\t\t\t\t\t\tfieldGenericType = pt.getActualTypeArguments()[0];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (fieldGenericType instanceof Class) {\n\t\t\t\t\ttype = (Class<?>) fieldGenericType;\n\t\t\t\t} else if (fieldGenericType instanceof ParameterizedType) {\n\t\t\t\t\ttype = (Class<?>) ((ParameterizedType) fieldGenericType).getRawType();\n\t\t\t\t} else if (fieldGenericType instanceof TypeVariable) {\n\t\t\t\t\tTypeVariable tv = (TypeVariable) fieldGenericType;\n\t\t\t\t\tif (tv.getBounds().length == 1) {\n\t\t\t\t\t\ttype = (Class<?>) tv.getBounds()[0];\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttype = Object.class;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbcs.addAll(getBeanConfigs(type, fieldGenericType, dependency.getBeanConfig().getClazz()));\n\t\t\t} else {\n\t\t\t\tbcs.addAll(getBeanConfigs(type, dependency.getGenericType(), dependency.getBeanConfig().getClazz()));\n\t\t\t}\n\t\t} else {\n\t\t\tthrow new RuntimeException(\"Unsupported dependecy type.\");\n\t\t}\n\t\treturn bcs.toArray(new BeanConfig[]{});\n\t}\n\n\tpublic BeanConfig getBeanConfig(String beanName) {\n\t\treturn this.beanConfigs.get(beanName);\n\t}\n\n\tpublic Collection<BeanConfig> getBeanConfigs() {\n\t\treturn Collections.unmodifiableCollection(beanConfigs.values());\n\t}\n\n\tpublic List<BeanConfig> getBeanConfigs(Class<?> type, Type genericType, Class<?> ownerClass) {\n\t\treturn getBeanConfigs(type, genericType, ownerClass, true);\n\t}\n\n\tpublic List<BeanConfig> getBeanConfigs(final Class<?> type, Type genericType, Class<?> ownerClass,\n\t\t\t\t\t\t\t\t\t\t   final boolean allowNonExportable) {\n\t\tArrayList<BeanConfig> result = new ArrayList<BeanConfig>();\n\t\tfor (BeanConfig bc : beanConfigs.values()) {\n\t\t\tif (bc.getState() != State.inactive && type.isAssignableFrom(bc.getClazz()) &&\n\t\t\t\t\t(allowNonExportable || bc.isExportable())) {\n\t\t\t\tif (genericType == null) {\n\t\t\t\t\tresult.add(bc);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tMap<TypeVariable<?>, Type> map = ReflectionHelper.createGenericsTypeMap(ownerClass);\n\t\t\t\tif (ReflectionHelper.compareTypes(genericType, bc.getClazz(), map, null)) {\n\t\t\t\t\tresult.add(bc);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic Collection<Dependency> getDependenciesTo(BeanConfig destination) {\n\t\tHashSet<Dependency> result = new HashSet<Dependency>();\n\t\tfor (BeanConfig candidate : beanConfigs.values()) {\n\t\t\tfor (Dependency dp : candidate.getFieldDependencies().values()) {\n\t\t\t\tList<BeanConfig> bcs = Arrays.asList(getBeanConfig(dp));\n\t\t\t\tif (bcs.contains(destination)) {\n\t\t\t\t\tresult.add(dp);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic HashSet<BeanConfig> getDependentBeans(final BeanConfig beanConfig) {\n\t\tHashSet<BeanConfig> result = new HashSet<BeanConfig>();\n\t\tfor (BeanConfig candidate : beanConfigs.values()) {\n\t\t\tif (candidate.getState() == State.inactive) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (Dependency dp : candidate.getFieldDependencies().values()) {\n\t\t\t\tList<BeanConfig> bcs = Arrays.asList(getBeanConfig(dp));\n\t\t\t\tif (bcs.contains(beanConfig)) {\n\t\t\t\t\tresult.add(candidate);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic boolean isBeanClassRegistered(String beanName) {\n\t\treturn beanConfigs.containsKey(beanName);\n\t}\n\n\tpublic boolean isThrowExceptionIfCannotCreate() {\n\t\treturn throwExceptionIfCannotCreate;\n\t}\n\n\tpublic void setThrowExceptionIfCannotCreate(boolean throwExceptionIfCannotCreate) {\n\t\tthis.throwExceptionIfCannotCreate = throwExceptionIfCannotCreate;\n\t}\n\n\tpublic BeanConfig unregister(String beanName) {\n\t\treturn beanConfigs.remove(beanName);\n\t}\n\n\tBeanConfig[] findDelegationTo(final BeanConfig beanConfig) {\n\t\treturn beanConfigs.values()\n\t\t\t\t.stream()\n\t\t\t\t.filter(beanConfig1 -> beanConfig1 instanceof Kernel.DelegatedBeanConfig &&\n\t\t\t\t\t\t((Kernel.DelegatedBeanConfig) beanConfig1).getOriginal().equals(beanConfig))\n\t\t\t\t.toArray(BeanConfig[]::new);\n\t}\n\n\tDependencyManager getParent() {\n\t\treturn parent;\n\t}\n\n\tvoid setParent(DependencyManager parent) {\n\t\tthis.parent = parent;\n\t}\n\n\tvoid register(BeanConfig beanConfig) {\n\t\tbeanConfigs.put(beanConfig.getBeanName(), beanConfig);\n\t\tif (beanConfig.getState() != State.inactive) {\n\t\t\tbeanConfig.setState(State.registered);\n\t\t}\n\t}\n\n\tprotected BeanConfig createBeanConfig(final Kernel kernel, final String beanName, final Class<?> beanClass) {\n\t\ttry {\n\t\t\tBeanConfig result = new BeanConfig(beanName, beanClass);\n\t\t\tresult.setKernel(kernel);\n\t\t\tprepareDependencies(result);\n\t\t\treturn result;\n\t\t} catch (java.lang.NoClassDefFoundError e) {\n\t\t\tlog.log(Level.WARNING, \"Cannot create bean config '\" + beanName + \"', type=\" + beanClass.getName() +\n\t\t\t\t\t\". Bean requires unknown class \" + e.getMessage());\n\n\t\t\tif (throwExceptionIfCannotCreate) {\n\t\t\t\tthrow e;\n\t\t\t} else {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected void prepareDependencies(BeanConfig beanConfig) {\n\t\tfinal String id = beanConfig.getBeanName();\n\t\tfinal Class<?> cls = beanConfig.getClazz();\n\n\t\tMap<Field, Inject> deps = createFieldsDependencyList(cls);\n\t\tfor (Entry<Field, Inject> e : deps.entrySet()) {\n\t\t\tField f = e.getKey();\n\t\t\tDependency d = new Dependency(beanConfig);\n\t\t\td.setField(f);\n\t\t\td.setNullAllowed(e.getValue().nullAllowed());\n\t\t\tif (!e.getValue().bean().isEmpty()) {\n\t\t\t\td.setBeanName(e.getValue().bean());\n\t\t\t} else if (e.getValue().type() != Inject.EMPTY.class) {\n\t\t\t\td.setType(e.getValue().type());\n\t\t\t} else if (f.getType().isArray()) {\n\t\t\t\td.setType(f.getType().getComponentType());\n\t\t\t} else {\n\t\t\t\tClass<?> type = f.getType();\n\t\t\t\td.setType(type);\n\t\t\t\td.setGenericType(f.getGenericType());\n\t\t\t}\n\n\t\t\tbeanConfig.getFieldDependencies().put(e.getKey(), d);\n\t\t}\n\t}\n\n\tprivate Map<Field, Inject> createFieldsDependencyList(final Class<?> cls) {\n\t\tMap<Field, Inject> deps = new HashMap<Field, Inject>();\n\t\tfor (Field field : getAllFields(cls)) {\n\t\t\tInject injectAnnotation = field.getAnnotation(Inject.class);\n\t\t\tif (injectAnnotation != null) {\n\t\t\t\tdeps.put(field, injectAnnotation);\n\t\t\t}\n\t\t}\n\t\treturn deps;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/core/Kernel.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.core;\n\nimport tigase.kernel.BeanUtils;\nimport tigase.kernel.KernelException;\nimport tigase.kernel.beans.*;\nimport tigase.kernel.beans.config.AbstractBeanConfigurator;\nimport tigase.kernel.beans.config.BeanConfigurator;\nimport tigase.kernel.core.BeanConfig.State;\nimport tigase.sys.TigaseRuntime;\nimport tigase.util.ExceptionUtilities;\nimport tigase.util.reflection.ReflectionHelper;\n\nimport java.lang.reflect.*;\nimport java.util.*;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * Main class of Kernel.\n */\npublic class Kernel {\n\n\tprotected final static Logger log = Logger.getLogger(Kernel.class.getName());\n\n\tprivate static final ThreadLocal<DelayedDependencyInjectionQueue> DELAYED_DEPENDENCY_INJECTION = new ThreadLocal<>();\n\n\tprivate final Map<String, Object> beanInstances = new HashMap<>();\n\n\tprivate final DependencyManager dependencyManager = new DependencyManager();\n\n\tBeanConfigBuilder currentlyUsedConfigBuilder;\n\n\tprivate boolean forceAllowNull;\n\n\tprivate String name;\n\n\tprivate Kernel parent;\n\tprivate Map<String, Link> registeredLinks = new HashMap<>();\n\tprivate boolean shutdown = false;\n\t\n\tprotected void initBean(BeanConfig tmpBC, Set<BeanConfig> createdBeansConfig, int deep)\n\t\t\tthrows IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {\n\t\tfinal BeanConfig beanConfig = tmpBC instanceof DelegatedBeanConfig\n\t\t\t\t\t\t\t\t\t  ? ((DelegatedBeanConfig) tmpBC).original\n\t\t\t\t\t\t\t\t\t  : tmpBC;\n\n\t\tif (log.isLoggable(Level.CONFIG)) {\n\t\t\tlog.log(Level.CONFIG, \"[{0}] Initialising bean, config: {1}, createdBeansConfigs={2}, deep={3}\",\n\t\t\t\t\tnew Object[]{tmpBC.getBeanName(), tmpBC, createdBeansConfig.size(), deep});\n\t\t}\n\n\t\tif (beanConfig.getState() == State.initialized) {\n\t\t\treturn;\n\t\t}\n\n\t\tDelayedDependencyInjectionQueue queue = beanConfig.getKernel().beginDependencyDelayedInjection();\n\n\t\ttry {\n\t\t\tObject bean;\n\t\t\tif (beanConfig.getState() == State.registered) {\n\t\t\t\tbeanConfig.setState(State.instanceCreated);\n\t\t\t\tif (beanConfig.getFactory() != null && beanConfig.getFactory().getState() != State.initialized) {\n\t\t\t\t\tinitBean(beanConfig.getFactory(), new HashSet<BeanConfig>(), 0);\n\t\t\t\t}\n\t\t\t\tif (RegistrarBean.class.isAssignableFrom(beanConfig.getClazz())) {\n\t\t\t\t\tRegistrarKernel k = new RegistrarKernel();\n\t\t\t\t\tk.setName(beanConfig.getBeanName());\n\t\t\t\t\tbeanConfig.getKernel().registerBean(beanConfig.getBeanName() + \"#KERNEL\").asInstance(k).exec();\n\t\t\t\t\tbeanConfig.setKernel(k);\n\t\t\t\t\tbeanConfig.setBeanInstanceName(\"service\");\n\t\t\t\t}\n\t\t\t\tbean = beanConfig.getKernel().createNewInstance(beanConfig);\n\t\t\t\tbeanConfig.getKernel().putBeanInstance(beanConfig.getBeanInstanceName(), bean);\n\t\t\t\tcreatedBeansConfig.add(beanConfig);\n\t\t\t\tif (RegistrarBean.class.isAssignableFrom(beanConfig.getClazz())) {\n\t\t\t\t\tKernel parent = beanConfig.getKernel().getParent();\n\t\t\t\t\t// without this line setBeanActive() fails\n\t\t\t\t\t//parent.ln(beanConfig.getBeanName(), beanConfig.getKernel(), beanConfig.getBeanName());\n\t\t\t\t\tparent.ln(beanConfig.getBeanName(), beanConfig.getKernel(), \"service\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tbean = beanConfig.getKernel().getInstance(beanConfig);\n\t\t\t}\n\n\t\t\tif (bean instanceof RegistrarBean) {\n\t\t\t\t((RegistrarBean) bean).register(beanConfig.getKernel());\n\t\t\t}\n\n\t\t\tBeanConfigurator beanConfigurator;\n\t\t\ttry {\n\t\t\t\tif (beanConfig.getKernel().isBeanClassRegistered(BeanConfigurator.DEFAULT_CONFIGURATOR_NAME) &&\n\t\t\t\t\t\t!beanConfig.getBeanName().equals(BeanConfigurator.DEFAULT_CONFIGURATOR_NAME)) {\n\t\t\t\t\tbeanConfigurator = beanConfig.getKernel().getInstance(BeanConfigurator.DEFAULT_CONFIGURATOR_NAME);\n\t\t\t\t} else {\n\t\t\t\t\tbeanConfigurator = null;\n\t\t\t\t}\n\t\t\t} catch (KernelException e) {\n\t\t\t\tbeanConfigurator = null;\n\t\t\t}\n\n\t\t\tif (beanConfigurator != null) {\n\t\t\t\tbeanConfigurator.configure(beanConfig, bean);\n\t\t\t} else {\n\t\t\t\tAbstractBeanConfigurator.registerBeansForBeanOfClass(beanConfig.getKernel(), bean.getClass());\n\t\t\t}\n\n\t\t\tbeanConfig.getKernel().finishDependecyDelayedInjection(queue);\n\n\t\t\tfor (final Dependency dep : beanConfig.getFieldDependencies().values()) {\n\t\t\t\tbeanConfig.getKernel().injectDependencies(bean, dep, createdBeansConfig, deep, false);\n\t\t\t}\n\n\t\t\t// there is no need to wait to initialize parent beans, it there any?\n\t\t\tif (bean instanceof Initializable && beanConfig.getState() != State.initialized) {\n\t\t\t\t((Initializable) bean).initialize();\n\t\t\t}\n\t\t} catch (Throwable ex) {\n\t\t\tif (beanConfig.getState() == State.instanceCreated) {\n\t\t\t\t// initialization of a bean failed!!\n\t\t\t\tObject i = beanConfig.getKernel().beanInstances.remove(beanConfig.getBeanInstanceName());\n\t\t\t\tif (i != null) {\n\t\t\t\t\tfireUnregisterAware(i);\n\t\t\t\t\tif (i instanceof RegistrarBean) {\n\t\t\t\t\t\tbeanConfig.getKernel().shutdown = true;\n\t\t\t\t\t\t((RegistrarBean) i).unregister(beanConfig.getKernel());\n\t\t\t\t\t\tKernel parent = beanConfig.getKernel().getParent();\n\t\t\t\t\t\tparent.unregister(beanConfig.getBeanName() + \"#KERNEL\");\n\t\t\t\t\t\tbeanConfig.setKernel(parent);\n\t\t\t\t\t\tbeanConfig.setBeanInstanceName(null);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbeanConfig.setState(State.registered);\n\t\t\t}\n\t\t\tthrow ex;\n\t\t}\n\t\ttmpBC.setState(State.initialized);\n//\t\tif (deep == 0) {\n//\t\t\tfor (BeanConfig bc : createdBeansConfig) {\n//\t\t\t\tObject bi = bc.getKernel().getInstance(bc);\n//\t\t\t\tbc.setState(State.initialized);\n//\t\t\t\tif (bi instanceof Initializable) {\n//\t\t\t\t\t((Initializable) bi).initialize();\n//\t\t\t\t}\n//\t\t\t}\n//\n////\t\t\tif (bean instanceof RegistrarBean) {\n////\t\t\t\tKernel parent = beanConfig.getKernel().getParent();\n////\t\t\t\tparent.ln(beanConfig.getBeanName(), beanConfig.getKernel(), beanConfig.getBeanName());\n////\t\t\t}\n//\n//\t\t}\n\t}\n\n\t/**\n\t * Creates instance of Kernel.\n\t */\n\tpublic Kernel() {\n\t\tthis(\"<unknown>\");\n\t}\n\n\t/**\n\t * Creates instance of kernel.\n\t *\n\t * @param name kernel name.\n\t */\n\tpublic Kernel(String name) {\n\t\tthis.name = name;\n\n\t\tBeanConfig bc = dependencyManager.createBeanConfig(this, \"kernel\", Kernel.class);\n\t\tbc.setPinned(true);\n\t\tdependencyManager.register(bc);\n\t\tputBeanInstance(bc, this);\n\t\tbc.setState(State.initialized);\n\t}\n\n\tpublic void gc() {\n\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\tlog.log(Level.FINE, \"Start GC for unused beans.\");\n\t\t}\n\n\t\tdependencyManager.getBeanConfigs()\n\t\t\t\t.stream()\n\t\t\t\t.filter(beanConfig -> beanConfig.getState() == State.instanceCreated)\n\t\t\t\t.forEach(beanConfig -> {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Removing instance of unused bean \" + beanConfig.getBeanName());\n\t\t\t\t\t}\n\t\t\t\t\tbeanConfig.getKernel().beanInstances.remove(beanConfig.getBeanInstanceName());\n\t\t\t\t\tbeanConfig.setState(State.registered);\n\t\t\t\t});\n\n\t\tint count;\n\t\tdo {\n\t\t\tCollection<BeanConfig> injectedBeans = gc_getInjectedBeans();\n\t\t\tCollection<BeanConfig> bcs = dependencyManager.getBeanConfigs();\n\t\t\tcount = 0;\n\t\t\tfor (BeanConfig bc : bcs) {\n\t\t\t\tif (bc.getState() == State.initialized && !bc.isPinned() && !injectedBeans.contains(bc)) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Removing instance of unused bean \" + bc.getBeanName());\n\t\t\t\t\t}\n\t\t\t\t\tbc.setState(State.registered);\n\t\t\t\t\tObject i = bc.getKernel().beanInstances.remove(bc.getBeanInstanceName());\n\t\t\t\t\tfireUnregisterAware(i);\n\t\t\t\t\tif (i instanceof RegistrarBean) {\n\t\t\t\t\t\t((RegistrarBean) i).unregister(bc.getKernel());\n\t\t\t\t\t}\n\t\t\t\t\t++count;\n\t\t\t\t}\n\t\t\t}\n\t\t} while (count > 0);\n\n\t\tdependencyManager.getBeanConfigs()\n\t\t\t\t.stream()\n\t\t\t\t.filter(beanConfig -> Kernel.class.isAssignableFrom(beanConfig.getClazz()) &&\n\t\t\t\t\t\tbeanConfig.getState() == State.initialized)\n\t\t\t\t.forEach(new Consumer<BeanConfig>() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void accept(BeanConfig beanConfig) {\n\t\t\t\t\t\tKernel k = getInstance(beanConfig);\n\t\t\t\t\t\tif (k != Kernel.this) {\n\t\t\t\t\t\t\tk.gc();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t}\n\n\t/**\n\t * Returns {@link DependencyManager} used in Kernel.\n\t *\n\t * @return {@link DependencyManager depenency manager}.\n\t */\n\tpublic DependencyManager getDependencyManager() {\n\t\treturn dependencyManager;\n\t}\n\n\t/**\n\t * Returns instance of bean.\n\t *\n\t * @param beanClass type of requested bean. Note that if more than one instance of bean will match, then Kernel\n\t * throws exception.\n\t * @param <T> type of bean to be returned.\n\t *\n\t * @return instance of bean if bean exists and there is only single instance of it.\n\t *\n\t * @throws KernelException when more than one instance of matching beans will be found or none of matching beans is\n\t * registered.\n\t */\n\tpublic <T> T getInstance(Class<T> beanClass) throws KernelException {\n\t\treturn getInstance(beanClass, true);\n\t}\n\n\t/**\n\t * Returns instance of bean. It creates bean if it is required.\n\t *\n\t * @param beanName name of bean to be returned.\n\t * @param <T> type of bean to be returned.\n\t *\n\t * @return instance of bean if bean exists and there is only single instance of it.\n\t *\n\t * @throws KernelException when bean with given name doesn't exists.\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic <T> T getInstance(String beanName) {\n\t\tBeanConfig bc = dependencyManager.getBeanConfig(beanName);\n\n\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\tlog.log(Level.FINE, \"[{0}] Creating instance of bean ''{1}'': config={2}, parent={3}, state={4}\",\n\t\t\t\t\tnew Object[]{getName(), beanName, bc, parent, (bc != null ? bc.getState() : \"n/a\")});\n\t\t}\n\n\t\tif (bc == null && parent != null && parent.isBeanClassRegistered(beanName)) {\n\t\t\treturn parent.getInstance(beanName);\n\t\t}\n\n\t\tif (bc == null) {\n\t\t\tthrow new KernelException(\"Unknown bean '\" + beanName + \"'.\");\n\t\t}\n\n\t\tif (bc.getState() != State.initialized) {\n\t\t\ttry {\n\t\t\t\tbc.getKernel().initBean(bc, new HashSet<BeanConfig>(), 0);\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.SEVERE, \"Exception getting instance\", e);\n\t\t\t\tthrow new KernelException(e);\n\t\t\t}\n\t\t\tinjectIfRequired(bc);\n\t\t}\n\n\t\tObject result = bc.getKernel().getInstance(bc);\n\n\t\treturn (T) result;\n\t}\n\n\t/**\n\t * Returns instance of bean if instance exists already or calls passed function.\n\t *\n\t * @param beanName name of bean to be returned.\n\t * @param function function to call if instance does not exist.\n\t * @param <T> type of bean to be returned.\n\t *\n\t * @return instance of bean if bean exists and there is only single instance of it or .\n\t *\n\t * @throws KernelException when bean with given name doesn't exists.\n\t */\n\tpublic <T> T getInstanceIfExistsOr(String beanName, Function<BeanConfig, T> function) {\n\t\tBeanConfig bc = dependencyManager.getBeanConfig(beanName);\n\n\t\tif (bc == null && parent != null && parent.isBeanClassRegistered(beanName)) {\n\t\t\treturn parent.getInstanceIfExistsOr(beanName, function);\n\t\t}\n\n\t\tif (bc == null) {\n\t\t\tthrow new KernelException(\"Unknown bean '\" + beanName + \"'.\");\n\t\t}\n\n\t\tObject result = bc.getKernel().getInstance(bc);\n\t\tif (result == null) {\n\t\t\tresult = function.apply(bc);\n\t\t}\n\n\t\treturn (T) result;\n\t}\n\n\t/**\n\t * Returns name of Kernel.\n\t *\n\t * @return name of Kernel.\n\t */\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t/**\n\t * Set name of the Kernel.\n\t * @param name to set\n\t */\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\t/**\n\t * Register links for bean of the passed name.\n\t */\n\tpublic void registerLinks(String beanName) {\n\t\tLink l = this.registeredLinks.get(beanName);\n\t\tif (l != null) {\n\t\t\tlnInternal(l.exportingBeanName, l.destinationKernel, l.destinationName);\n\t\t}\n\t}\n\n\t/**\n\t * Returns name of beans matching to given type.\n\t *\n\t * @param beanType type of searched beans.\n\t *\n\t * @return collection of matching bean names.\n\t */\n\tpublic Collection<String> getNamesOf(Class<?> beanType) {\n\t\tArrayList<String> result = new ArrayList<String>();\n\t\tList<BeanConfig> bcs = dependencyManager.getBeanConfigs(beanType, null, null);\n\t\tfor (BeanConfig beanConfig : bcs) {\n\t\t\tresult.add(beanConfig.getBeanName());\n\t\t}\n\t\treturn Collections.unmodifiableCollection(result);\n\t}\n\n\t/**\n\t * Returns parent Kernel.\n\t *\n\t * @return parent Kernel or <code>null</code> if there is no parent Kernel.\n\t */\n\tpublic Kernel getParent() {\n\t\treturn parent;\n\t}\n\n\tvoid setParent(Kernel parent) {\n\t\tthis.dependencyManager.setParent(parent.getDependencyManager());\n\t\tthis.parent = parent;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tfinal StringBuilder sb = new StringBuilder(\"Kernel{\");\n\t\tsb.append(\"name=\").append(name);\n\t\tsb.append(\", parent=\").append(parent);\n\t\tsb.append('}');\n\t\treturn sb.toString();\n\t}\n\n\t/**\n\t * Forces initiate all registered beans.\n\t */\n\tpublic void initAll() {\n\t\ttry {\n\t\t\tfor (BeanConfig bc : dependencyManager.getBeanConfigs()) {\n\t\t\t\tif (bc.getState() != State.initialized) {\n\t\t\t\t\tinitBean(bc, new HashSet<BeanConfig>(), 0);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new KernelException(\"Can''t initialize all beans\", e);\n\t\t}\n\t}\n\n\t/**\n\t * Checks if bean with given name is registered in Kernel.\n\t *\n\t * @param beanName name of bean to check.\n\t *\n\t * @return <code>true</code> if bean is registered (it may be not initialized!).\n\t */\n\tpublic boolean isBeanClassRegistered(final String beanName) {\n\t\treturn isBeanClassRegistered(beanName, true);\n\t}\n\n\t/**\n\t * Checks if bean with given name is registered in Kernel.\n\t * @param beanName name of bean to check.\n\t * @param checkInParent should check in parent kernel if not found in the current Kernel.\n\t *\n\t * @return <code>true</code> if bean is registered (it may be not initialized!).\n\t */\n\tpublic boolean isBeanClassRegistered(final String beanName, boolean checkInParent) {\n\t\tboolean x = dependencyManager.isBeanClassRegistered(beanName);\n\t\tif (x == false && parent != null) {\n\t\t\tx = parent.isBeanClassRegistered(beanName);\n\t\t}\n\t\treturn x;\n\t}\n\n\t/**\n\t * Makes symlink to bean in another Kernel.\n\t *\n\t * @param exportingBeanName name bean to be linked.\n\t * @param destinationKernel destination Kernel.\n\t * @param destinationName name of bean in destination Kernel.\n\t */\n\tpublic void ln(String exportingBeanName, Kernel destinationKernel, String destinationName) {\n\t\tLink link = new Link();\n\t\tlink.exportingBeanName = exportingBeanName;\n\t\tlink.destinationKernel = destinationKernel;\n\t\tlink.destinationName = destinationName;\n\t\tthis.registeredLinks.put(exportingBeanName, link);\n\n\t\tBeanConfig dbc = lnInternal(exportingBeanName, destinationKernel, destinationName);\n\t\t//destinationKernel.injectIfRequired(dbc);\n\t}\n\n\t/**\n\t * Registers bean as class in Kernel. Class must be annotated with {@link Bean} annotation. <p> For example: </p>\n\t * <pre>\n\t * {@code\n\t *\n\t *  // If Bean1.class is annotated by @Bean annotation.\n\t *  registerBean(Bean1.class).exec();\n\t * }\n\t * </pre>\n\t *\n\t * @param beanClass class of bean to register.\n\t *\n\t * @return {@link BeanConfigBuilder config builder} what allows to finish bean registering.\n\t */\n\tpublic BeanConfigBuilder registerBean(Class<?> beanClass) {\n\t\tif (currentlyUsedConfigBuilder != null) {\n\t\t\tthrow new KernelException(\n\t\t\t\t\t\"Registration of bean '\" + currentlyUsedConfigBuilder.getBeanName() + \"' is not finished yet!\");\n\t\t}\n\n\t\tBean annotation = beanClass.getAnnotation(Bean.class);\n\t\tif (annotation == null || annotation.name() == null || annotation.name().isEmpty()) {\n\t\t\tthrow new KernelException(\"Name of bean class \" + beanClass.getName() + \" is not defined.\");\n\t\t}\n\n\t\tBeanConfigBuilder builder = new BeanConfigBuilder(this, dependencyManager, annotation.name());\n\t\tthis.currentlyUsedConfigBuilder = builder;\n\t\tbuilder.asClass(beanClass);\n\t\tbuilder.setActive(annotation.active());\n\t\tif (annotation.exportable()) {\n\t\t\tbuilder.exportable();\n\t\t}\n\t\treturn builder;\n\t}\n\n\t/**\n\t * Registers bean with given name. Class or instance of bean must be defined in returned {@link BeanConfigBuilder\n\t * config builder}. <p> For example: </p>\n\t * <pre>\n\t * {@code\n\t *\n\t *  // To register already created variable bean4 as bean \"bean4\".\n\t *  krnl.registerBean(\"bean4\").asInstance(bean4).exec();\n\t *\n\t *  // If Bean5 have to been created by Bean5Factory.\n\t *  krnl.registerBean(\"bean5\").asClass(Bean5.class).withFactory(Bean5Factory.class).exec();\n\t * }\n\t * </pre>\n\t *\n\t * @param beanName name of bean.\n\t *\n\t * @return {@link BeanConfigBuilder config builder} what allows to finish bean registering.\n\t */\n\tpublic BeanConfigBuilder registerBean(String beanName) {\n\t\tif (currentlyUsedConfigBuilder != null) {\n\t\t\tthrow new KernelException(\n\t\t\t\t\t\"Registration of bean '\" + currentlyUsedConfigBuilder.getBeanName() + \"' is not finished yet!\");\n\t\t}\n\t\tBeanConfigBuilder builder = new BeanConfigBuilder(this, dependencyManager, beanName);\n\t\tthis.currentlyUsedConfigBuilder = builder;\n\t\treturn builder;\n\t}\n\n\t/**\n\t * Calling this method instructs Kernel to delay dependency injection until\n\t * <code>finishDependecyDelayedInjection()</code> method is called.\n\t *\n\t * @return instance of a queue with delayed dependency injections\n\t */\n\tpublic DelayedDependencyInjectionQueue beginDependencyDelayedInjection() {\n\t\tDelayedDependencyInjectionQueue queue = DELAYED_DEPENDENCY_INJECTION.get();\n\t\tif (queue == null) {\n\t\t\tqueue = new DelayedDependencyInjectionQueue();\n\t\t\tDELAYED_DEPENDENCY_INJECTION.set(queue);\n\t\t\treturn queue;\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Calling this method instructs Kernel to end delaying dependency injection and inject all queued items.\n\t */\n\tpublic void finishDependecyDelayedInjection(DelayedDependencyInjectionQueue queue)\n\t\t\tthrows IllegalAccessException, InstantiationException, InvocationTargetException {\n\t\tif (log.isLoggable(Level.CONFIG)) {\n\t\t\tlog.log(Level.CONFIG, \"[{0}] Finishing injecting dependencies, queue: {1}\", new Object[]{getName(), queue});\n\t\t}\n\n\t\tif (queue == null) {\n\t\t\treturn;\n\t\t}\n\t\tDELAYED_DEPENDENCY_INJECTION.remove();\n\n\t\tfor (DelayedDependenciesInjection item : queue.getQueue()) {\n\t\t\titem.inject();\n\t\t}\n\t}\n\n\t/**\n\t * Change state of a bean (activate/deactivate).\n\t *\n\t * @param beanName name of a bean\n\t * @param value new state of a bean\n\t */\n\tpublic void setBeanActive(String beanName, boolean value) {\n\t\tBeanConfig beanConfig = dependencyManager.getBeanConfig(beanName);\n\n\t\tif (beanConfig == null) {\n\t\t\tthrow new KernelException(\"Unknown bean '\" + beanName + \"'.\");\n\t\t}\n\n\t\twhile (beanConfig instanceof DelegatedBeanConfig) {\n\t\t\tbeanConfig = ((DelegatedBeanConfig) beanConfig).getOriginal();\n\t\t}\n\n\t\tif (beanConfig.getKernel() != this) {\n\t\t\tif (RegistrarBean.class.isAssignableFrom(beanConfig.getClazz()) &&\n\t\t\t\t\t(beanConfig.getState() == State.initialized || beanConfig.getState() == State.instanceCreated)) {\n\t\t\t\tbeanConfig.getKernel().setBeanActive(\"service\", value);\n\t\t\t} else {\n\t\t\t\tbeanConfig.getKernel().setBeanActive(beanConfig.getBeanName(), value);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (value && beanConfig.getState() == State.inactive) {\n\t\t\t// activing bean\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.finer(\"[\" + getName() + \"] Making bean \" + beanName + \" active\");\n\t\t\t}\n\t\t\tbeanConfig.setState(State.registered);\n\t\t\ttry {\n\t\t\t\tinjectIfRequired(beanConfig);\n\t\t\t} catch (KernelException e) {\n\t\t\t\tlog.fine(\"Cannot initialize \" + beanConfig.getBeanName() + \". Leaving in state \" +\n\t\t\t\t\t\t\t\t beanConfig.getState());\n\t\t\t}\n\t\t}\n\t\tif (!value && beanConfig.getState() != State.inactive) {\n\t\t\t// deactiving bean\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.finer(\"[\" + getName() + \"] Making bean \" + beanName + \" inactive\");\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tif (beanConfig instanceof DelegatedBeanConfig) {\n\t\t\t\t\tbeanConfig = ((DelegatedBeanConfig) beanConfig).getOriginal();\n\t\t\t\t}\n\t\t\t\tObject i = beanConfig.getKernel().beanInstances.remove(beanConfig.getBeanInstanceName());\n\t\t\t\tfireUnregisterAware(i);\n\t\t\t\tif (i instanceof RegistrarBean) {\n\t\t\t\t\t((RegistrarBean) i).unregister(beanConfig.getKernel());\n\t\t\t\t\tKernel parent = beanConfig.getKernel().getParent();\n\t\t\t\t\tparent.unregister(beanConfig.getBeanName() + \"#KERNEL\");\n\t\t\t\t\tbeanConfig.setKernel(parent);\n\t\t\t\t\tbeanConfig.setBeanInstanceName(null);\n\t\t\t\t}\n\t\t\t\tbeanConfig.setState(State.inactive);\n\t\t\t\tbeanConfig.getKernel().unloadInjectedBean(beanConfig);\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new KernelException(\"Can''t unload bean \" + beanName + \" from depenent beans\", e);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Force injection of nulls in all dependency injection fields of all beans if required bean for injections are not\n\t * available.\n\t */\n\tpublic void setForceAllowNull(boolean forceAllowNull) {\n\t\tthis.forceAllowNull = forceAllowNull;\n\t}\n\n\t/**\n\t * Shutdown kernel.\n\t */\n\tpublic void shutdown() {\n\t    shutdown(null);\n\t}\n\n\t/**\n\t * Shutdown kernel with passed comparator to define order in which bean will be stopped.\n\t * @param shutdownOrder comparator defining order of beans for shutdown\n\t */\n\tpublic void shutdown(Comparator<BeanConfig> shutdownOrder) {\n\t\tinitiateShutdown();\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Shutting down kernel and beans...\");\n\t\t}\n\t\tStream<BeanConfig> beanConfigStream = getDependencyManager().getBeanConfigs()\n\t\t\t\t.stream()\n\t\t\t\t.filter(bc -> !bc.getBeanName().endsWith(\"#KERNEL\"));\n\t\tif (shutdownOrder != null) {\n\t\t\tbeanConfigStream = beanConfigStream.sorted(shutdownOrder);\n\t\t}\n\t\tList<String> beanNames = beanConfigStream\n\t\t\t\t.map(bc -> bc.getBeanName())\n\t\t\t\t.collect(Collectors.toList());\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"found \" + beanNames.size() + \" to stop: \" + beanNames);\n\t\t}\n\t\tfor (String beanName : beanNames) {\n\t\t\ttry {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"disabling bean \" + beanName + \"...\");\n\t\t\t\t}\n\t\t\t\tBeanConfig bc = getDependencyManager().getBeanConfig(beanName);\n\t\t\t\tif (bc != null && bc.getState() != BeanConfig.State.inactive) {\n\t\t\t\t\tsetBeanActive(beanName, false);\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"bean \" + beanName + \" stopped.\");\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"bean \" + beanName + \" was already stopped!\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (Throwable ex) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"failed to disable bean \" + beanName, ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void initiateShutdown() {\n\t\tthis.shutdown = true;\n\t\tbeanInstances.values()\n\t\t\t\t.stream()\n\t\t\t\t.filter(o -> o instanceof Kernel)\n\t\t\t\t.map(o -> (Kernel) o)\n\t\t\t\t.filter(o -> o != this)\n\t\t\t\t.forEach(Kernel::initiateShutdown);\n\t}\n\n\t/**\n\t * Removes bean from Kernel.\n\t *\n\t * @param beanName name of bean to be removed.\n\t */\n\tpublic void unregister(final String beanName) {\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.finer(\"[\" + getName() + \"] Unregistering bean \" + beanName);\n\t\t}\n\t\tBeanConfig unregisteredBeanConfig = dependencyManager.getBeanConfig(beanName);\n\t\t// bean can be unregistered already\n\t\tif (unregisteredBeanConfig == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!(unregisteredBeanConfig instanceof DelegatedBeanConfig) && unregisteredBeanConfig.getKernel() != this) {\n\t\t\tif (!RegistrarBean.class.isAssignableFrom(unregisteredBeanConfig.getClazz())) {\n\t\t\t\tunregisteredBeanConfig.getKernel().unregister(beanName);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tMap<Kernel, ArrayList<BeanConfig>> lklklk = new HashMap<>();\n\t\tgetDependencyManager().getBeanConfigs(Kernel.class, null, null)\n\t\t\t\t.stream()\n\t\t\t\t.filter(beanConfig -> beanConfig.getState() == State.initialized)\n\t\t\t\t.map(new Function<BeanConfig, Kernel>() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic Kernel apply(BeanConfig beanConfig) {\n\t\t\t\t\t\treturn getInstance(beanConfig);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.forEach(kernel -> {\n\t\t\t\t\tCollection<Dependency> links = kernel.getDependencyManager()\n\t\t\t\t\t\t\t.getDependenciesTo(unregisteredBeanConfig);\n\t\t\t\t\tArrayList<BeanConfig> toRemove = new ArrayList<BeanConfig>();\n\t\t\t\t\tfor (Dependency link : links) {\n\t\t\t\t\t\tBeanConfig[] bc = kernel.getDependencyManager().getBeanConfig(link);\n\t\t\t\t\t\ttoRemove.addAll(Arrays.asList(bc));\n\t\t\t\t\t}\n\n\t\t\t\t\tlklklk.put(kernel, toRemove);\n\t\t\t\t});\n\n\t\tunregisterInt(beanName);\n\t\ttry {\n\t\t\tunloadInjectedBean(unregisteredBeanConfig);\n\t\t} catch (Exception e) {\n//\t\t\te.printStackTrace();\n\t\t\tlog.log(Level.SEVERE, \"Exception during unregistering\", e);\n\t\t\tthrow new KernelException(\"Can''t unload bean \" + beanName + \" from depenent beans\", e);\n\t\t} finally {\n\t\t\tdependencyManager.unregister(beanName);\n\t\t}\n\n\t\tgetDependencyManager().getBeanConfigs(Kernel.class, null, null)\n\t\t\t\t.stream()\n\t\t\t\t.filter(beanConfig -> beanConfig.getState() == State.initialized)\n\t\t\t\t.map(new Function<BeanConfig, Kernel>() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic Kernel apply(BeanConfig beanConfig) {\n\t\t\t\t\t\treturn getInstance(beanConfig);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.forEach(kernel -> {\n\t\t\t\t\tBeanConfig[] links = kernel.getDependencyManager().findDelegationTo(unregisteredBeanConfig);\n\t\t\t\t\tfor (BeanConfig link : links) {\n\t\t\t\t\t\tkernel.unregister(link.getBeanName());\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\tif (parent != null) {\n\t\t\tBeanConfig[] links = parent.getDependencyManager().findDelegationTo(unregisteredBeanConfig);\n\t\t\tif (links != null) {\n\t\t\t\tfor (BeanConfig link : links) {\n\t\t\t\t\tparent.unregister(link.getBeanName());\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t\tfor (Map.Entry<Kernel, ArrayList<BeanConfig>> en : lklklk.entrySet()) {\n\t\t\tKernel kernel = en.getKey();\n\t\t\tfor (BeanConfig beanConfig : en.getValue()) {\n\t\t\t\ttry {\n\t\t\t\t\tkernel.unloadInjectedBean(beanConfig);\n\t\t\t\t} catch (Exception e) {\n//\t\t\t\t\te.printStackTrace();\n\t\t\t\t\tlog.log(Level.SEVERE, \"Exception during un-registering\", e);\n\t\t\t\t\tthrow new KernelException(\n\t\t\t\t\t\t\t\"Can''t unload bean \" + beanConfig.getBeanName() + \" from depenent beans in kernel \" +\n\t\t\t\t\t\t\t\t\tkernel.getName(), e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic String toPrintable() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\ttoPrintable(sb, 0);\n\t\treturn sb.toString();\n\t}\n\n\tprivate void toPrintable(StringBuilder sb, int level) {\n\t\tdependencyManager.getBeanConfigs().stream().filter(bc -> !bc.getBeanName().endsWith(\"#KERNEL\")).sorted((bc1, bc2) -> {\n\t\t\treturn bc1.getBeanName().compareTo(bc2.getBeanName());\n\t\t}).forEach(bc -> {\n\t\t\tfor (int i=0; i<level; i++) {\n\t\t\t\tsb.append(\" \");\n\t\t\t}\n\t\t\tsb.append(bc.getBeanName());\n\t\t\tsb.append(\"(state: \").append(bc.getState()).append(\", class: \").append(Optional.ofNullable(bc.getClazz()).map(Class::getCanonicalName).orElse(null)).append(\")\");\n\t\t\tif (RegistrarBean.class.isAssignableFrom(bc.getClazz())) {\n\t\t\t\tsb.append(\" {\\n\");\n\t\t\t\tKernel k = (Kernel) beanInstances.get(bc.getBeanName()+\"#KERNEL\");\n\t\t\t\tif (k != null) {\n\t\t\t\t\tk.toPrintable(sb, level+1);\n\t\t\t\t}\n\t\t\t\tfor (int i=0; i<level; i++) {\n\t\t\t\t\tsb.append(\" \");\n\t\t\t\t}\n\t\t\t\tsb.append(\"}\\n\");\n\t\t\t} else {\n\t\t\t\tsb.append(\"\\n\");\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Returns instance of bean.\n\t *\n\t * @param beanConfig definition of bean to be returned.\n\t * @param <T> type of bean.\n\t *\n\t * @return bean or <code>null</code> if instance of bean is not created.\n\t */\n\t<T> T getInstance(BeanConfig beanConfig) {\n\t\twhile (beanConfig instanceof DelegatedBeanConfig) {\n\t\t\tbeanConfig = ((DelegatedBeanConfig) beanConfig).original;\n\t\t}\n//\t\treturn (T) beanConfig.getKernel().beanInstances.get(beanConfig);\n\t\tString beanInstanceName = beanConfig.getBeanInstanceName();\n\t\treturn (T) beanConfig.getKernel().beanInstances.get(beanInstanceName);\n\t}\n\n\tvoid injectDependency(Dependency dep)\n\t\t\tthrows IllegalAccessException, InstantiationException, InvocationTargetException {\n\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\tlog.log(Level.FINE, \"[{0}] Injecting dependency, dep: {1}\", new Object[]{getName(), dep});\n\t\t}\n\n\t\tBeanConfig depbc = dep.getBeanConfig();\n\t\tif (depbc.getState() == State.initialized || depbc.getState() == State.instanceCreated) {\n\t\t\tObject bean = depbc.getKernel().getInstance(depbc);\n\t\t\tif (bean == null) {\n\t\t\t\tlog.log(Level.FINEST, \"skipping injection of dependencies to \" + dep + \" as there is no bean instance\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tinjectDependencies(bean, dep, new HashSet<BeanConfig>(), 0, false);\n\t\t}\n\t}\n\n\tvoid injectDependencies(Collection<Dependency> dps) {\n\n\t\tif (log.isLoggable(Level.CONFIG)) {\n\t\t\tlog.log(Level.CONFIG, \"[{0}] Injecting dependencies, dps: {1}\", new Object[]{getName(), dps});\n\t\t}\n\n\t\tfor (Dependency dep : dps) {\n\t\t\tBeanConfig depbc = dep.getBeanConfig();\n\n\t\t\ttry {\n\t\t\t\tinjectDependency(dep);\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\t\"Can''t inject dependency to bean \" + depbc.getBeanName() + \" (class: \" + depbc.getClazz() +\n\t\t\t\t\t\t\t\t\")\" + \" unloading bean \" + depbc.getBeanName() +\n\t\t\t\t\t\t\t\tExceptionUtilities.getExceptionRootCause(e, true));\n\n\t\t\t\tlog.log(Level.CONFIG,\n\t\t\t\t\t\t\"Can''t inject dependency to bean \" + depbc.getBeanName() + \" (class: \" + depbc.getClazz() +\n\t\t\t\t\t\t\t\t\")\" + \" unloading bean \" + depbc.getBeanName(), e);\n\t\t\t\ttry {\n\t\t\t\t\tObject i = depbc.getKernel().beanInstances.remove(depbc);\n\t\t\t\t\tState oldState = depbc.getState();\n\t\t\t\t\tdepbc.setState(State.inactive);\n\t\t\t\t\tif (oldState == State.initialized) {\n\t\t\t\t\t\tfireUnregisterAware(i);\n\t\t\t\t\t}\n\t\t\t\t\tunloadInjectedBean(depbc);\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tthrow new KernelException(\"Can''t unload bean \" + depbc.getBeanName(), ex);\n\t\t\t\t} finally {\n\t\t\t\t\tdepbc.setState(State.registered);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tBeanConfig lnInternal(String exportingBeanName, Kernel destinationKernel, String destinationName) {\n\t\tfinal BeanConfig sbc = dependencyManager.getBeanConfig(exportingBeanName);\n\t\t// Object bean = getInstance(sbc.getBeanName());\n\t\tif (sbc == null) {\n\t\t\tthrow new KernelException(\"Can''t export bean \" + exportingBeanName + \" as there is no such bean\");\n\t\t}\n\n\t\tBeanConfig dbc = new DelegatedBeanConfig(destinationName, sbc);\n\n\t\tdestinationKernel.dependencyManager.register(dbc);\n\n\t\treturn dbc;\n\t}\n\n\tvoid putBeanInstance(String beanName, Object beanInstance) {\n\t\tObject oldBeanInstance = this.beanInstances.put(beanName, beanInstance);\n\t\tif (oldBeanInstance instanceof UnregisterAware && oldBeanInstance != beanInstance) {\n\t\t\t((UnregisterAware) oldBeanInstance).beforeUnregister();\n\t\t}\n\t\tif (beanInstance instanceof Kernel && beanInstance != this) {\n\t\t\t((Kernel) beanInstance).setParent(this);\n\t\t}\n\t\t//beanConfig.setState(State.initialized);\n\t}\n\n\tvoid putBeanInstance(BeanConfig beanConfig, Object beanInstance) {\n\t\tputBeanInstance(beanConfig.getBeanName(), beanInstance);\n\t}\n\n\tvoid unregisterInt(String beanName) {\n\t\tif (dependencyManager.isBeanClassRegistered(beanName)) {\n\t\t\t// unregistering\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.finer(\"[\" + getName() + \"] Found registred bean \" + beanName + \". Unregistering...\");\n\t\t\t}\n\n\t\t\tBeanConfig oldBeanConfig = dependencyManager.unregister(beanName);\n\t\t\tObject i = oldBeanConfig.getKernel().beanInstances.remove(oldBeanConfig.getBeanInstanceName());\n\t\t\toldBeanConfig.setBeanInstanceName(null);\n\t\t\tif (oldBeanConfig.getState() == State.initialized) {\n\t\t\t\tfireUnregisterAware(i);\n\t\t\t}\n\n\t\t\tfor (BeanConfig bc : oldBeanConfig.getRegisteredBeans()) {\n\t\t\t\tif (bc.removeRegisteredBy(oldBeanConfig)) {\n\t\t\t\t\tbc.getKernel().unregisterInt(bc.getBeanName());\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (RegistrarBean.class.isAssignableFrom(oldBeanConfig.getClazz())) {\n\t\t\t\tif (oldBeanConfig.getKernel().getParent() != null) {         // removing from the wrong kernel???\n\t\t\t\t\toldBeanConfig.getKernel().getParent().unregister(oldBeanConfig.getBeanName());\n\t\t\t\t\toldBeanConfig.getKernel().getParent().unregister(oldBeanConfig.getBeanName() + \"#KERNEL\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprotected <T> T getInstance(Class<T> beanClass, boolean allowNonExportable) {\n\t\t//TODO - check if null should be passed here\n\t\tfinal List<BeanConfig> bcs = dependencyManager.getBeanConfigs(beanClass, null, null, allowNonExportable);\n\n\t\tif (bcs.size() > 1) {\n\t\t\tthrow new KernelException(\"Too many beans implemented class \" + beanClass);\n\t\t} else if (bcs.isEmpty() && this.parent != null && this.parent != this) {\n\t\t\treturn this.parent.getInstance(beanClass, false);\n\t\t}\n\n\t\tif (bcs.isEmpty()) {\n\t\t\tthrow new KernelException(\"Can''t find bean implementing \" + beanClass);\n\t\t}\n\n\t\tBeanConfig bc = bcs.get(0);\n\n\t\tif (bc.getState() != State.initialized) {\n\t\t\ttry {\n\t\t\t\tinitBean(bc, new HashSet<BeanConfig>(), 0);\n\t\t\t} catch (Exception e) {\n//\t\t\t\te.printStackTrace();\n\t\t\t\tlog.log(Level.SEVERE, \"Exception getting instance\", e);\n\t\t\t\tthrow new KernelException(e);\n\t\t\t}\n\t\t}\n\n\t\tObject result = bc.getKernel().getInstance(bc);\n\n\t\treturn (T) result;\n\t}\n\n\tprotected void injectIfRequired(final BeanConfig beanConfig) {\n\t\ttry {\n\t\t\tif (!isThereSomethingWaitingFor(beanConfig)) {\n\t\t\t\t// nothing is waiting for this bean. Skipping initialization.\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tCollection<Dependency> dps = dependencyManager.getDependenciesTo(beanConfig);\n\n\t\t\tfor (Dependency dep : dps) {\n\t\t\t\tBeanConfig depbc = dep.getBeanConfig();\n\n\t\t\t\tif (depbc.getState() != State.initialized && depbc.getState() != State.inactive) {\n\t\t\t\t\t//depbc.getState() != State.inactive && depbc.getState() != State.instanceCreated\n\t\t\t\t\t//if (isThereSomethingWaitingFor(depbc)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (depbc.getState() != State.instanceCreated) {\n\t\t\t\t\t\t\tinitBean(depbc, new HashSet<BeanConfig>(), 0);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tinjectIfRequired(depbc);\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tlog.log(Level.SEVERE, \"Exception injecting bean if required\", e);\n\t\t\t\t\t}\n\t\t\t\t\t//}\n\t\t\t\t}\n\n\t\t\t\tif (depbc.getState() == State.initialized) {\n\t\t\t\t\tif (beanConfig.getState() != State.initialized) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tinitBean(beanConfig, new HashSet<BeanConfig>(), 0);\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t// cannot initialize beanconfig -- skipping injecting\n\t\t\t\t\t\t\tlog.log(Level.SEVERE, \"Exception injecting bean if required\", e);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tObject bean = depbc.getKernel().getInstance(depbc);\n\n\t\t\t\t\tinjectDependencies(bean, dep, new HashSet<BeanConfig>(), 0, false);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (beanConfig.isExportable()) {\n\t\t\t\tgetDependencyManager().getBeanConfigs()\n\t\t\t\t\t\t.stream()\n\t\t\t\t\t\t.filter(bc -> Kernel.class.isAssignableFrom(bc.getClazz()) &&\n\t\t\t\t\t\t\t\tbc.getState() == State.initialized)\n\t\t\t\t\t\t.map(p -> (Kernel) getInstance(p))\n\t\t\t\t\t\t.filter(k -> !k.equals(Kernel.this))\n\t\t\t\t\t\t.forEach(k -> {\n\t\t\t\t\t\t\tk.injectIfRequired(beanConfig);\n\t\t\t\t\t\t});\n\n\t\t\t}\n\t\t} catch (Exception e) {\n//\t\t\te.printStackTrace();\n\t\t\tlog.log(Level.SEVERE, \"Exception\", e);\n\t\t\tthrow new KernelException(\"Can''t inject bean \" + beanConfig + \" to dependend beans.\", e);\n\t\t}\n\t}\n\n\tprotected BeanConfig registerBean(BeanConfig beanConfig, BeanConfig factoryBeanConfig, Object beanInstance) {\n\t\tBeanConfig parent = null;\n\t\tif (beanConfig.getSource() == BeanConfig.Source.annotation && !beanConfig.getRegisteredBy().isEmpty()) {\n\t\t\tBeanConfig bc = dependencyManager.getBeanConfig(beanConfig.getBeanName());\n\t\t\tparent = beanConfig.getRegisteredBy().iterator().next();\n\t\t\tif (bc != null && bc.getClazz().equals(beanConfig.getClazz())) {\n\t\t\t\tbc.addRegisteredBy(parent);\n\t\t\t\tparent.addRegisteredBean(bc);\n\t\t\t\tcurrentlyUsedConfigBuilder = null;\n\t\t\t\treturn bc;\n\t\t\t}\n\t\t}\n\n\t\tif (factoryBeanConfig != null) {\n\t\t\tfactoryBeanConfig.setPinned(beanConfig.isPinned());\n\t\t\tfactoryBeanConfig.setState(beanConfig.getState());\n\t\t\tunregisterInt(factoryBeanConfig.getBeanName());\n\t\t\tdependencyManager.register(factoryBeanConfig);\n\t\t}\n\n\t\tBeanConfig oldBeanConfig = dependencyManager.getBeanConfig(beanConfig.getBeanName());\n\t\tCollection<Dependency> oldDeps =\n\t\t\t\toldBeanConfig == null ? null : dependencyManager.getDependenciesTo(oldBeanConfig);\n\n\t\tunregisterInt(beanConfig.getBeanName());\n\t\tdependencyManager.register(beanConfig);\n\t\tif (parent != null) {\n\t\t\tparent.addRegisteredBean(beanConfig);\n\t\t}\n\n\t\tif (beanInstance != null) {\n\t\t\tputBeanInstance(beanConfig, beanInstance);\n\t\t\tbeanConfig.setState(State.initialized);\n\t\t}\n\n\t\tCollection<Dependency> deps = dependencyManager.getDependenciesTo(beanConfig);\n\t\tif (oldDeps != null) {\n\t\t\tdeps.addAll(oldDeps.stream().filter(od -> {\n\t\t\t\tField f = od.getField();\n\t\t\t\treturn !deps.stream().anyMatch(nd -> nd.getField().equals(f));\n\t\t\t}).collect(Collectors.toSet()));\n\t\t}\n\n\t\tcurrentlyUsedConfigBuilder = null;\n\n\t\tif (!queueForDelayedDependencyInjection(deps)) {\n\t\t\tinjectDependencies(deps);\n\t\t}\n\n\t\treturn beanConfig;\n\t}\n\n\tprivate Object createNewInstance(BeanConfig beanConfig) {\n\t\ttry {\n\t\t\tif (beanConfig.getFactory() != null) {\n\t\t\t\tBeanFactory<?> factory = beanConfig.getKernel().getInstance(beanConfig.getFactory());\n\t\t\t\treturn factory.createInstance();\n\t\t\t} else {\n\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\tlog.finer(\"[\" + getName() + \"] Creating instance of bean \" + beanConfig.getBeanName());\n\t\t\t\t}\n\t\t\t\tClass<?> clz = beanConfig.getClazz();\n\n\t\t\t\treturn clz.newInstance();\n\t\t\t}\n\t\t} catch (NoClassDefFoundError e) {\n\t\t\tif (e.getMessage() != null && e.getMessage().contains(\"licence\")) {\n\t\t\t\tfinal String[] msg = {\"ERROR! ACS strategy was enabled with following class configuration\",\n\t\t\t\t\t\t\t\t\t  \"--sm-cluster-strategy-class=tigase.server.cluster.strategy.OnlineUsersCachingStrategy\",\n\t\t\t\t\t\t\t\t\t  \"but required libraries are missing!\", \"\",\n\t\t\t\t\t\t\t\t\t  \"Please make sure that all tigase-acs*.jar and licence-lib.jar\",\n\t\t\t\t\t\t\t\t\t  \"files are available in the classpath or disable ACS strategy!\",\n\t\t\t\t\t\t\t\t\t  \"(by commenting out above line)\", \"\",\n\t\t\t\t\t\t\t\t\t  \"For more information please peruse ACS documentation.\",};\n\t\t\t\tTigaseRuntime.getTigaseRuntime().shutdownTigase(msg);\n\t\t\t}\n\t\t\tthrow new KernelException(\"Can''t create instance of bean '\" + beanConfig.getBeanName() + \"' (class: \" +\n\t\t\t\t\t\t\t\t\t\t\t  beanConfig.getClazz() + \")\", e);\n\t\t} catch (Exception e) {\n\t\t\tthrow new KernelException(\"Can''t create instance of bean '\" + beanConfig.getBeanName() + \"' (class: \" +\n\t\t\t\t\t\t\t\t\t\t\t  beanConfig.getClazz() + \")\", e);\n\t\t}\n\t}\n\n\tprivate void fireUnregisterAware(Object i) {\n\t\tif (i != null && i instanceof UnregisterAware) {\n\t\t\ttry {\n\t\t\t\t((UnregisterAware) i).beforeUnregister();\n\t\t\t} catch (Exception e) {\n//\t\t\t\te.printStackTrace();\n\t\t\t\tlog.log(Level.WARNING, \"Problem during unregistering bean\", e);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate Collection<BeanConfig> gc_getInjectedBeans() {\n\t\tHashSet<BeanConfig> injectedBeans = new HashSet<>();\n\t\tCollection<BeanConfig> bcs = dependencyManager.getBeanConfigs();\n\t\tfor (BeanConfig bc : bcs) {\n\t\t\tif (bc.getState() != State.initialized) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (Dependency dp : bc.getFieldDependencies().values()) {\n\t\t\t\tBeanConfig[] xxx = dependencyManager.getBeanConfig(dp);\n\t\t\t\tfor (BeanConfig beanConfig : xxx) {\n\t\t\t\t\tif (beanConfig != null && beanConfig.getState() == State.initialized) {\n\t\t\t\t\t\tinjectedBeans.add(beanConfig);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn injectedBeans;\n\t}\n\n\t/**\n\t * Injects data to bean.\n\t *\n\t * @param data data to be injected.\n\t * @param dependency dependency definition.\n\t * @param toBean destination bean.\n\t * @param forceNullInjection if <code>true</code> then null will be injected even if null is not allowed for this\n\t * dependency definition. In this case, Exception \"Can't inject <null>\" will not be throwed.\n\t *\n\t * @return <code>true</code> if injection was successfull, <code>false</code> only in case of forcing null injection\n\t * on not-null dependency.\n\t *\n\t */\n\t@SuppressWarnings({\"unchecked\", \"rawtypes\"})\n\tprivate boolean inject(Object[] data, Dependency dependency, Object toBean, final boolean forceNullInjection)\n\t\t\tthrows IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {\n\n\t\tif (!forceNullInjection && !this.forceAllowNull && !dependency.isNullAllowed() &&\n\t\t\t\t(data == null || data.length == 0)) {\n\t\t\tthrow new KernelException(\n\t\t\t\t\t\"Can't inject <null> to field \" + dependency.getField().getDeclaringClass().getName() + \".\" +\n\t\t\t\t\t\t\tdependency.getField().getName());\n\t\t}\n\n\t\tObject valueToSet;\n\t\tif (data == null) {\n\t\t\tvalueToSet = null;\n\t\t} else if (Collection.class.isAssignableFrom(dependency.getField().getType())) {\n\t\t\tCollection o;\n\n\t\t\tif (!dependency.getField().getType().isInterface()) {\n\t\t\t\to = (Collection) dependency.getField().getType().newInstance();\n\t\t\t} else if (dependency.getField().getType().isAssignableFrom(Set.class)) {\n\t\t\t\to = new HashSet();\n\t\t\t} else {\n\t\t\t\to = new ArrayList();\n\t\t\t}\n\n\t\t\to.addAll(Arrays.asList(data));\n\n\t\t\tvalueToSet = o;\n\t\t} else {\n\t\t\tObject o;\n\t\t\tif (data != null && dependency.getField().getType().equals(data.getClass())) {\n\t\t\t\to = data;\n\t\t\t} else {\n\t\t\t\tint l = Array.getLength(data);\n\t\t\t\tif (l > 1) {\n\t\t\t\t\tthrow new KernelException(\"Can''t put many objects to single field \" + dependency.getField());\n\t\t\t\t}\n\t\t\t\tif (l == 0) {\n\t\t\t\t\to = null;\n\t\t\t\t} else {\n\t\t\t\t\to = Array.get(data, 0);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvalueToSet = o;\n\t\t}\n\n\t\tBeanUtils.setValue(toBean, dependency.getField(), valueToSet);\n\n\t\treturn !(forceNullInjection && !this.forceAllowNull && !dependency.isNullAllowed() &&\n\t\t\t\t(data == null || data.length == 0));\n\t}\n\n\tprivate boolean injectDependencies(Object bean, Dependency dep, Set<BeanConfig> createdBeansConfig, int deep,\n\t\t\t\t\t\t\t\t\t   boolean forceNullInjection)\n\t\t\tthrows IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {    \n\t\tif (shutdown) {\n\t\t\t// we are shutting down, no more dependency injection..\n\t\t\treturn false;\n\t\t}\n\n\t\tBeanConfig[] dependentBeansConfigs = dependencyManager.getBeanConfig(dep);\n\t\tArrayList<Object> dataToInject = new ArrayList<Object>();\n\n\t\tif (log.isLoggable(Level.CONFIG)) {\n\t\t\tlog.log(Level.CONFIG,\n\t\t\t\t\t\"[{0}] Injecting dependencies, bean: {1}, dep: {2}, createdBeansConfig: {3}, deep: {4}\",\n\t\t\t\t\tnew Object[]{getName(), bean, dep, createdBeansConfig.size(), deep});\n\t\t}\n\n\t\tfor (BeanConfig b : dependentBeansConfigs) {\n\t\t\tif (b == null) {\n\t\t\t\tcontinue;\n\t\t\t} else {\n\t\t\t\tObject beanToInject = b.getKernel().getInstance(b);\n\t\t\t\tif (beanToInject == null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tinitBean(b, createdBeansConfig, deep + 1);\n\t\t\t\t\t} catch (InvocationTargetException | RuntimeException | InstantiationException ex) {\n\t\t\t\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\t\t\t\"\\n\\n\\n=====================\\n\"\n\t\t\t\t\t\t\t\t+ \"Could not initialize bean \" + b.getBeanName() + \" (class: \" + b.getClazz() + \")\" +\n\t\t\t\t\t\t\t\t\t\t\", skipping injection of this bean\" +\n\t\t\t\t\t\t\t\t\t\tExceptionUtilities.getExceptionRootCause(ex, true)\n\t\t\t\t\t\t\t\t+ \"\\n=====================\\n\\n\\n\"\n\t\t\t\t\t\t);\n\t\t\t\t\t\tlog.log(Level.CONFIG,\n\t\t\t\t\t\t\t\t\"Could not initialize bean \" + b.getBeanName() + \" (class: \" + b.getClazz() + \")\" +\n\t\t\t\t\t\t\t\t\t\t\", skipping injection of this bean\", ex);\n\t\t\t\t\t\tObject i = b.getKernel().beanInstances.remove(b);\n\t\t\t\t\t\tif (i instanceof RegistrarBean) {\n\t\t\t\t\t\t\t((RegistrarBean) i).unregister(b.getKernel());\n\t\t\t\t\t\t\tKernel parent = b.getKernel().getParent();\n\t\t\t\t\t\t\tparent.unregister(b.getBeanName() + \"#KERNEL\");\n\t\t\t\t\t\t\tb.setKernel(parent);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tb.setState(State.registered);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tbeanToInject = b.getKernel().getInstance(b);\n\t\t\t\t}\n\t\t\t\tif (beanToInject ==\n\t\t\t\t\t\tnull) // && dep.getType() != null && (Collection.class.isAssignableFrom(dep.getType()) || dep.getType().isArray()))\n\t\t\t\t{\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// it may happen that we create link in parent kernel to bean in subkernel and both are marked\n\t\t\t\t// as exportable - then we have 2 bean configs pointing to same instance - so we need it to\n\t\t\t\t// detect this - remove duplicated instances from dataToInject list\n\t\t\t\tif (!dataToInject.contains(beanToInject)) {\n\t\t\t\t\tdataToInject.add(beanToInject);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tObject[] d;\n\t\tif (dataToInject.isEmpty()) {\n\t\t\td = new Object[]{};\n\t\t} else if (dep.getType() != null) {\n\t\t\tClass<?> type = dep.getType();\n\t\t\tif (Collection.class.isAssignableFrom(type)) {\n\t\t\t\tType t = ReflectionHelper.getCollectionParamter(dep.getGenericType(), dep.getBeanConfig().getClazz());\n\t\t\t\tif (t instanceof ParameterizedType) {\n\t\t\t\t\ttype = (Class) ((ParameterizedType) t).getRawType();\n\t\t\t\t} else if (t instanceof TypeVariable) {\n\t\t\t\t\ttype = (Class) ((TypeVariable) t).getBounds()[0];\n\t\t\t\t} else {\n\t\t\t\t\ttype = (Class) t;\n\t\t\t\t}\n\t\t\t}\n\t\t\tObject[] z = (Object[]) Array.newInstance(type, 1);\n\t\t\td = dataToInject.toArray(z);\n\t\t} else {\n\t\t\td = dataToInject.toArray();\n\t\t}\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.finer(\"[\" + getName() + \"] Injecting \" + Arrays.toString(d) + \" to \" + dep.getBeanConfig() + \"#\" + dep);\n\t\t}\n\n\t\treturn inject(d, dep, bean, forceNullInjection);\n\t}\n\n\tprivate boolean isThereSomethingWaitingFor(final BeanConfig beanConfig) {\n\t\t// current kernel\n//\t\tCollection<Dependency> dps = dependencyManager.getDependenciesTo(beanConfig);\n//\t\tfor (Dependency dp : dps) {\n//\t\t\tif(dp.getBeanConfig().getState()==State.initialized){\n//\t\t\t\t// initialized bean is waiting of beanConfig.\n//\t\t\t\treturn true;\n//\t\t\t} else {\n//\t\t\t\tboolean x = isThereSomethingWaitingFor(dp.getBeanConfig());\n//\t\t\t\tif(x) return true;\n//\t\t\t}\n//\t\t}\n\n\t\tfinal Set<BeanConfig> related = dependencyManager.getDependenciesTo(beanConfig)\n\t\t\t\t.stream()\n\t\t\t\t.map(d -> d.getBeanConfig())\n\t\t\t\t.collect(Collectors.toSet());\n\t\twhile (true) {\n\t\t\tHashSet<BeanConfig> toAdd = new HashSet<>();\n\n\t\t\tfor (BeanConfig config : related) {\n\t\t\t\tfor (Dependency dependency : dependencyManager.getDependenciesTo(config)) {\n\t\t\t\t\tif (!related.contains(dependency.getBeanConfig())) {\n\t\t\t\t\t\ttoAdd.add(dependency.getBeanConfig());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (toAdd.size() == 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\trelated.addAll(toAdd);\n\t\t}\n\n\t\tfor (BeanConfig config : related) {\n\t\t\tif (config.getState() == State.initialized) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n//\t\tif (dps.stream().filter(dependency -> dependency.getBeanConfig().getState() == State.initialized).count() > 0) {\n//\t\t\treturn true;\n//\t\t}\n\n\t\tif (beanConfig.isExportable()) {\n\t\t\tlong r = getDependencyManager().getBeanConfigs()\n\t\t\t\t\t.stream()\n\t\t\t\t\t.filter(bc -> Kernel.class.isAssignableFrom(bc.getClazz()) && bc.getState() == State.initialized)\n\t\t\t\t\t.map(p -> (Kernel) getInstance(p))\n\t\t\t\t\t.filter(k -> !k.equals(Kernel.this))\n\t\t\t\t\t.map(kernel -> (Boolean) kernel.isThereSomethingWaitingFor(beanConfig))\n\t\t\t\t\t.filter(Boolean::booleanValue)\n\t\t\t\t\t.count();//\t\t\t\t\t.forEach(k -> {\n//\t\t\t\t\t\tboolean r = k.isThereSomethingWaitingFor(beanConfig);\n//\t\t\t\t\t\tif(r) return true;\n//\t\t\t\t\t})\n\t\t\treturn r > 0;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate boolean queueForDelayedDependencyInjection(Collection<Dependency> deps) {\n\t\tDelayedDependencyInjectionQueue queue = DELAYED_DEPENDENCY_INJECTION.get();\n\t\tif (queue == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (deps.isEmpty()) {\n\t\t\treturn true;\n\t\t}\n\n\t\tqueue.offer(new DelayedDependenciesInjection(deps));\n\t\treturn true;\n\t}\n\n\t/**\n\t * Unload given bean from all previously injected objects.\n\t *\n\t */\n\tprivate void unloadInjectedBean(BeanConfig beanConfig)\n\t\t\tthrows IllegalAccessException, InstantiationException, InvocationTargetException {\n\t\tfinal HashSet<BeanConfig> beansToRemove = new HashSet<>();\n\n\t\tfor (BeanConfig bc : dependencyManager.getBeanConfigs()) {\n\t\t\tif (bc.getState() != State.initialized) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tObject ob = bc.getKernel().getInstance(bc);\n\t\t\tif (ob == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfor (Dependency d : bc.getFieldDependencies().values()) {\n\t\t\t\tif (DependencyManager.match(d, beanConfig)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tBeanConfig[] cbcs = dependencyManager.getBeanConfig(d);\n\t\t\t\t\t\tif (cbcs.length == 0) {\n\t\t\t\t\t\t\tboolean r = inject(null, d, ob, false);\n\t\t\t\t\t\t\tif (!r) {\n\t\t\t\t\t\t\t\tbeansToRemove.add(bc);\n\t\t\t\t\t\t\t}\n//\t\t\t\t\t\t} else if (cbcs.length == 1) {// Clearing single-instance\n//\t\t\t\t\t\t\t// dependency. Like single field.\n//\t\t\t\t\t\t\t// BeanConfig cbc = cbcs[0];\n//\t\t\t\t\t\t\t// if (cbc != null && cbc.equals(removingBC)) {\n//\t\t\t\t\t\t\tinject(null, d, ob);\n//\t\t\t\t\t\t\t// }\n//\t\t\t\t\t\t} else if (cbcs.length > 1) { // Clearing multi-instance\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// dependiency. Like\n\t\t\t\t\t\t\t// collections and arrays.\n\n\t\t\t\t\t\t\tboolean r = injectDependencies(ob, d, new HashSet<BeanConfig>(), 0, false);\n\t\t\t\t\t\t\tif (!r) {\n\t\t\t\t\t\t\t\tbeansToRemove.add(bc);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (KernelException ex) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Can''t set null to \" + d + \" unloading bean \" + d.getBeanName(), ex);\n\t\t\t\t\t\tbeansToRemove.add(bc);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (BeanConfig config : beansToRemove) {\n\t\t\tlog.log(Level.INFO, \"Removing \" + config.getBeanName() + \" because of dependency violation\");\n\t\t\tif (dependencyManager.getBeanConfig(config.getBeanName()) != null) {\n\t\t\t\tsetBeanActive(config.getBeanName(), false);\n\t\t\t}\n//\t\t\tsetBeanActive(config.getBeanName(), true);\n\t\t}\n\n\t\tif (!shutdown) {\n\t\t\t// We reenable beans which were stopped in case they were stopped due to reload of one of dependencies.\n\t\t\t// However, in case of shutdown there is no point of reenabling beans as they will be stopped anyway.\n\t\t\tfor (BeanConfig config : beansToRemove) {\n\t\t\t\t// we need to check if changing state using setState() will be enough, but there should be no need\n\t\t\t\t// to reinstantiate bean at this time.\n//\t\t\t\tif (dependencyManager.getBeanConfig(config.getBeanName()) != null) {\n//\t\t\t\t\tconfig.getKernel().setBeanActive(config.getBeanName(), true);\n//\t\t\t\t}\n\t\t\t\tif (config.getState() == State.inactive) {\n\t\t\t\t\tconfig.setState(State.registered);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * Class used for delegating beans from one kernel to the other kernel.\n\t * It is used internally for exporting/linking bean to the other kernels.\n\t */\n\tpublic static class DelegatedBeanConfig\n\t\t\textends BeanConfig {\n\n\t\tprivate final BeanConfig original;\n\n\t\tDelegatedBeanConfig(String localName, BeanConfig src) {\n\t\t\tsuper(localName, src.getClazz());\n\t\t\toriginal = src;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getClazz() {\n\t\t\treturn original.getClazz();\n\t\t}\n\n\t\t@Override\n\t\tpublic BeanConfig getFactory() {\n\t\t\treturn original.getFactory();\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<Field, Dependency> getFieldDependencies() {\n\t\t\treturn original.getFieldDependencies();\n\t\t}\n\n\t\t@Override\n\t\tpublic Kernel getKernel() {\n\t\t\treturn original.getKernel();\n\t\t}\n\n\t\tpublic BeanConfig getOriginal() {\n\t\t\treturn original;\n\t\t}\n\n\t\t@Override\n\t\tpublic State getState() {\n\t\t\treturn original.getState();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isExportable() {\n\t\t\treturn original.isExportable();\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn original.toString();\n\t\t}\n\t}\n\n\tprivate class DelayedDependenciesInjection {\n\n\t\tprivate final Collection<Dependency> dependencies;\n\n\t\tpublic DelayedDependenciesInjection(Collection<Dependency> deps) {\n\t\t\tthis.dependencies = deps;\n\t\t}\n\n\t\tpublic void inject() throws IllegalAccessException, InvocationTargetException, InstantiationException {\n\t\t\tfor (Dependency dep : dependencies) {\n\t\t\t\tKernel.this.injectDependency(dep);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\tif (obj instanceof DelayedDependenciesInjection) {\n\t\t\t\tDelayedDependenciesInjection o = (DelayedDependenciesInjection) obj;\n\t\t\t\tif (o.dependencies.size() != dependencies.size()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn o.dependencies.containsAll(dependencies) && dependencies.containsAll(o.dependencies);\n\t\t\t}\n\t\t\treturn super.equals(obj);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"dependencies=\" + dependencies;\n\t\t}\n\t}\n\n\t/**\n\t * Class implements a queue for delayed dependency injection.\n\t */\n\tpublic class DelayedDependencyInjectionQueue {\n\n\t\tprivate final ArrayDeque<DelayedDependenciesInjection> queue = new ArrayDeque<>();\n\n\t\tpublic boolean offer(DelayedDependenciesInjection item) {\n\t\t\tsynchronized (queue) {\n\t\t\t\tDelayedDependenciesInjection last = queue.peekLast();\n\t\t\t\tif (last != null && last.equals(item)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn queue.offer(item);\n\t\t\t}\n\t\t}\n\n\t\tpublic Queue<DelayedDependenciesInjection> getQueue() {\n\t\t\treturn queue;\n\t\t}\n\n\t\tpublic boolean checkStartingKernel(Kernel kernel) {\n\t\t\treturn Kernel.this == kernel;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tfinal StringBuilder sb = new StringBuilder(\"DelayedDependencyInjectionQueue{\");\n\t\t\tsb.append(queue.stream().flatMap(queue -> queue.dependencies.stream()).collect(Collectors.toList()));\n\t\t\tsb.append('}');\n\t\t\treturn sb.toString();\n\t\t}\n\t}\n\n\tprivate class Link {\n\n\t\tKernel destinationKernel;\n\t\tString destinationName;\n\t\tString exportingBeanName;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/core/PlantUMLGrapher.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.core;\n\npublic class PlantUMLGrapher {\n\n\tprivate Kernel kernel;\n\n\tprivate static String n(BeanConfig beanConfig) {\n\t\tKernel bk = beanConfig.getKernel();\n\t\tString kernelName = bk.getName();\n\t\tString beanName = beanConfig.getBeanName();\n\t\treturn (kernelName + \".\" + beanName).replace(\"#\", \"_\").replace(\"<\", \"_\").replace(\">\", \"_\");\n\t}\n\n\tpublic PlantUMLGrapher() {\n\t}\n\n\tpublic PlantUMLGrapher(Kernel krnl) {\n\t\tsetKernel(krnl);\n\t}\n\n\tpublic String getDependencyGraph() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"@startuml\").append('\\n');\n\n\t\tsb.append(makePackage(kernel));\n\n\t\tsb.append(\"@enduml\").append('\\n');\n\t\treturn sb.toString();\n\t}\n\n\tpublic Kernel getKernel() {\n\t\treturn kernel;\n\t}\n\n\tpublic void setKernel(Kernel kernel) {\n\t\tthis.kernel = kernel;\n\t}\n\n\tprivate StringBuilder makeObject(BeanConfig bc) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"object \").append(\"\\\"\").append(bc.getBeanName()).append(\"\\\" as \").append(n(bc));\n\t\tif (true) {\n\t\t\tsb.append(\"{\\n\");\n\t\t\tfor (Dependency d : bc.getFieldDependencies().values()) {\n\t\t\t\tsb.append(d.getField().getName());\n\t\t\t\tsb.append('\\n');\n\t\t\t}\n\n\t\t\tsb.append(\"}\");\n\t\t}\n\t\tsb.append('\\n');\n\n//\t\tsb.append(\"note bottom of [\")\n//\t\t\t\t.append(n(bc.getKernel().getName(), bc.getBeanName()))\n//\t\t\t\t.append(\"]\\n\")\n//\t\t\t\t.append(\"State: \")\n//\t\t\t\t.append(bc.getState())\n//\t\t\t\t.append('\\n')\n//\t\t\t\t.append(\"end note\\n\");\n\n\t\treturn sb;\n\t}\n\n\tprivate StringBuilder makePackage(Kernel k) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"package \").append(k.getName()).append(\" {\\n\");\n\n\t\tfor (BeanConfig bc : k.getDependencyManager().getBeanConfigs()) {\n\n\t\t\tif (Kernel.class.isAssignableFrom(bc.getClazz())) {\n\t\t\t\tKernel sk = k.getInstance(bc);\n\t\t\t\tif (sk != k) {\n\t\t\t\t\tsb.append(makePackage(sk));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsb.append(makeObject(bc));\n\n\t\t\tfor (Dependency dp : bc.getFieldDependencies().values()) {\n\t\t\t\tBeanConfig[] dBeans = k.getDependencyManager().getBeanConfig(dp);\n\n\t\t\t\tfor (BeanConfig dBean : dBeans) {\n\t\t\t\t\tif (dBean != null) {\n\t\t\t\t\t\tsb.append(n(dBean)).append(\" *- \").append(n(bc)).append('\\n');\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif (bc instanceof Kernel.DelegatedBeanConfig) {\n\t\t\t\tfinal BeanConfig orginal = ((Kernel.DelegatedBeanConfig) bc).getOriginal();\n\t\t\t\tsb.append(n(orginal)).\n\t\t\t\t\t\tappend(\" .. \").append(n(bc)).append('\\n');\n\n\t\t\t}\n\n\t\t}\n\n\t\tsb.append(\"}\\n\");\n\t\treturn sb;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/kernel/core/RegistrarKernel.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.core;\n\nimport tigase.kernel.beans.UnregisterAware;\n\npublic class RegistrarKernel\n\t\textends Kernel\n\t\timplements UnregisterAware {\n\n\tpublic RegistrarKernel() {\n\t\tsuper(\"RegistrarKernel\");\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\tfor (BeanConfig bc : this.getDependencyManager().getBeanConfigs()) {\n\t\t\tBeanConfig bc1 = bc;\n\t\t\twhile (bc1 instanceof DelegatedBeanConfig) {\n\t\t\t\tbc1 = ((DelegatedBeanConfig) bc1).getOriginal();\n\t\t\t}\n\t\t\tbc1.getKernel().unregister(bc1.getBeanName());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/map/ClusterMapFactory.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.map;\n\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.EventBusEvent;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.eventbus.HandleEvent;\nimport tigase.eventbus.impl.EventName;\nimport tigase.kernel.DefaultTypesConverter;\nimport tigase.kernel.TypesConverter;\n\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class ClusterMapFactory {\n\n\tprivate final static EventName NEWMAP_EVENT_NAME = new EventName(NewMapCreatedEvent.class);\n\tprivate static ClusterMapFactory instance;\n\tprivate final Logger log = Logger.getLogger(this.getClass().getName());\n\tprivate final ConcurrentHashMap<String, DMap> maps = new ConcurrentHashMap<>();\n\tprivate final TypesConverter typesConverter = new DefaultTypesConverter();\n\tprivate EventBus eventBus;\n\tprivate final DMap.DMapListener mapListener = new DMap.DMapListener() {\n\t\t@Override\n\t\tpublic void onClear(DMap map) {\n\t\t\tMapClearEvent event = new MapClearEvent();\n\t\t\tevent.setUid(map.getUid());\n\t\t\teventBus.fire(event);\n\t\t}\n\n\t\t@Override\n\t\tpublic void onPut(DMap map, Object key, Object value) {\n\t\t\tElementAddEvent event = new ElementAddEvent();\n\t\t\tevent.setUid(map.getUid());\n\t\t\tevent.setKey(typesConverter.toString(key));\n\t\t\tevent.setValue(typesConverter.toString(value));\n\t\t\teventBus.fire(event);\n\t\t}\n\n\t\t@Override\n\t\tpublic void onPutAll(DMap map, Map<?, ?> m) {\n\t\t\tfor (Map.Entry<?, ?> en : m.entrySet()) {\n\t\t\t\tElementAddEvent event = new ElementAddEvent();\n\t\t\t\tevent.setUid(map.getUid());\n\t\t\t\tevent.setKey(typesConverter.toString(en.getKey()));\n\t\t\t\tevent.setValue(typesConverter.toString(en.getValue()));\n\t\t\t\teventBus.fire(event);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void onRemove(DMap map, Object key) {\n\t\t\tElementRemoveEvent event = new ElementRemoveEvent();\n\t\t\tevent.setUid(map.getUid());\n\t\t\tevent.setKey(typesConverter.toString(key));\n\t\t\teventBus.fire(event);\n\t\t}\n\t};\n\n\tpublic static final ClusterMapFactory get() {\n\t\tif (instance == null) {\n\t\t\tinstance = new ClusterMapFactory();\n\t\t}\n\t\treturn instance;\n\t}\n\n\tClusterMapFactory() {\n\t\tthis.eventBus = EventBusFactory.getInstance();\n\t\tthis.eventBus.registerAll(this);\n\t}\n\n\tpublic <K, V> Map<K, V> createMap(final String uid, final Class<K> keyClass, final Class<V> valueClass,\n\t\t\t\t\t\t\t\t\t  final String... params) {\n\n\t\tNewMapCreatedEvent event = new NewMapCreatedEvent();\n\t\tevent.setUid(uid);\n\t\tevent.setKeyClass(keyClass);\n\t\tevent.setValueClass(valueClass);\n\t\tevent.setParams(params);\n\t\teventBus.fire(event);\n\n\t\tDMap<K, V> map = maps.computeIfAbsent(uid, (u) -> new DMap<K, V>(uid, this.mapListener, keyClass, valueClass));\n\n\t\treturn map;\n\t}\n\n\tpublic void destroyMap(Map map) {\n\t\tif (map instanceof DMap) {\n\t\t\tMapDestroyEvent event = new MapDestroyEvent();\n\t\t\tevent.setUid(((DMap) map).getUid());\n\n\t\t\teventBus.fire(event);\n\t\t\tthis.maps.remove(((DMap) map).getUid(), map);\n\t\t}\n\t}\n\n\tprivate void fireOnMapCreated(Map map, String uid, String... parameters) {\n\t\tMapCreatedEvent event = new MapCreatedEvent(map, uid, parameters);\n\t\teventBus.fire(event);\n\t}\n\n\tprivate void fireOnMapDestroyed(final Map map, final String uid) {\n\t\tMapDestroyedEvent event = new MapDestroyedEvent(map, uid);\n\t\teventBus.fire(event);\n\t}\n\n\tpublic EventBus getEventBus() {\n\t\treturn eventBus;\n\t}\n\n\tpublic void setEventBus(EventBus eventBus) {\n\t\tthis.eventBus = eventBus;\n\t}\n\n\tpublic <K, V> Map<K, V> getMap(String uid) {\n\t\treturn this.maps.get(uid);\n\t}\n\n\t@HandleEvent(filter = HandleEvent.Type.remote)\n\tvoid onMapClear(MapClearEvent event) {\n\t\tfinal String uid = event.getUid();\n\t\tDMap map = this.maps.get(uid);\n\t\tif (map == null) {\n\t\t\tlog.log(Level.FINE, \"No map '\" + uid + \"' created on this node! Ignoring MapClear event.\");\n\t\t\treturn;\n\t\t}\n\t\tmap.clearNoEvent();\n\t}\n\n\t@HandleEvent(filter = HandleEvent.Type.remote)\n\tvoid onMapDestroyed(MapDestroyEvent event) {\n\t\tfinal String uid = event.getUid();\n\t\tDMap map = this.maps.remove(uid);\n\t\tif (map != null) {\n\t\t\tfireOnMapDestroyed(map, map.uid);\n\t\t}\n\t}\n\n\t@HandleEvent(filter = HandleEvent.Type.remote)\n\tvoid onMapElementAdd(ElementAddEvent event) {\n\t\tfinal String uid = event.getUid();\n\t\tfinal DMap map = this.maps.get(uid);\n\n\t\tif (map == null) {\n\t\t\tlog.log(Level.FINE, \"No map '\" + uid + \"' created on this node! Ignoring ElementAdd item event.\");\n\t\t\treturn;\n\t\t}\n\n\t\tString k = event.getKey();\n\t\tString v = event.getValue();\n\n\t\tObject key = typesConverter.convert(k, map.keyClass);\n\t\tObject value = typesConverter.convert(v, map.valueClass);\n\n\t\tmap.putNoEvent(key, value);\n\t}\n\n\t@HandleEvent(filter = HandleEvent.Type.remote)\n\tvoid onMapElementRemove(ElementRemoveEvent event) {\n\t\tfinal String uid = event.getUid();\n\t\tDMap map = this.maps.get(uid);\n\n\t\tif (map == null) {\n\t\t\tlog.log(Level.FINE, \"No map '\" + uid + \"' created on this node! Ignoring ElementRemove item event.\");\n\t\t\treturn;\n\t\t}\n\n\t\tString k = event.getKey();\n\t\tObject key = typesConverter.convert(k, map.keyClass);\n\t\tmap.removeNoEvent(key);\n\t}\n\n\t@HandleEvent(filter = HandleEvent.Type.remote)\n\tvoid onNewMapCreated(final NewMapCreatedEvent event) {\n\t\tfinal String uid = event.getUid();\n\t\tif (!maps.containsKey(uid)) {\n\t\t\tfinal Class keyClass = event.getKeyClass();\n\t\t\tfinal Class valueClass = event.getValueClass();\n\n\t\t\tString[] parameters = event.getParams();\n\n\t\t\tDMap map = new DMap(uid, mapListener, keyClass, valueClass);\n\t\t\tmaps.put(uid, map);\n\t\t\tfireOnMapCreated(map, uid, parameters);\n\t\t} else {\n\t\t\tDMap map = this.maps.get(uid);\n\t\t\tmapListener.onPutAll(map, map);\n\t\t}\n\t}\n\n\tpublic static class ElementAddEvent\n\t\t\timplements Serializable, EventBusEvent {\n\n\t\tprivate String key;\n\t\tprivate String uid;\n\t\tprivate String value;\n\n\t\tpublic String getKey() {\n\t\t\treturn key;\n\t\t}\n\n\t\tpublic void setKey(String key) {\n\t\t\tthis.key = key;\n\t\t}\n\n\t\tpublic String getUid() {\n\t\t\treturn uid;\n\t\t}\n\n\t\tpublic void setUid(String uid) {\n\t\t\tthis.uid = uid;\n\t\t}\n\n\t\tpublic String getValue() {\n\t\t\treturn value;\n\t\t}\n\n\t\tpublic void setValue(String value) {\n\t\t\tthis.value = value;\n\t\t}\n\t}\n\n\tpublic static class ElementRemoveEvent\n\t\t\timplements Serializable, EventBusEvent {\n\n\t\tprivate String key;\n\t\tprivate String uid;\n\n\t\tpublic String getKey() {\n\t\t\treturn key;\n\t\t}\n\n\t\tpublic void setKey(String key) {\n\t\t\tthis.key = key;\n\t\t}\n\n\t\tpublic String getUid() {\n\t\t\treturn uid;\n\t\t}\n\n\t\tpublic void setUid(String uid) {\n\t\t\tthis.uid = uid;\n\t\t}\n\t}\n\n\tpublic static class MapClearEvent\n\t\t\timplements Serializable, EventBusEvent {\n\n\t\tprivate String uid;\n\n\t\tpublic String getUid() {\n\t\t\treturn uid;\n\t\t}\n\n\t\tpublic void setUid(String uid) {\n\t\t\tthis.uid = uid;\n\t\t}\n\t}\n\n\tpublic static class MapDestroyEvent\n\t\t\timplements Serializable, EventBusEvent {\n\n\t\tprivate String uid;\n\n\t\tpublic String getUid() {\n\t\t\treturn uid;\n\t\t}\n\n\t\tpublic void setUid(String uid) {\n\t\t\tthis.uid = uid;\n\t\t}\n\t}\n\n\tpublic static class NewMapCreatedEvent\n\t\t\timplements Serializable, EventBusEvent {\n\n\t\tprivate Class keyClass;\n\t\tprivate String[] params;\n\t\tprivate String uid;\n\t\tprivate Class valueClass;\n\n\t\tpublic Class getKeyClass() {\n\t\t\treturn keyClass;\n\t\t}\n\n\t\tpublic void setKeyClass(Class keyClass) {\n\t\t\tthis.keyClass = keyClass;\n\t\t}\n\n\t\tpublic String[] getParams() {\n\t\t\treturn params;\n\t\t}\n\n\t\tpublic void setParams(String[] params) {\n\t\t\tthis.params = params;\n\t\t}\n\n\t\tpublic String getUid() {\n\t\t\treturn uid;\n\t\t}\n\n\t\tpublic void setUid(String uid) {\n\t\t\tthis.uid = uid;\n\t\t}\n\n\t\tpublic Class getValueClass() {\n\t\t\treturn valueClass;\n\t\t}\n\n\t\tpublic void setValueClass(Class valueClass) {\n\t\t\tthis.valueClass = valueClass;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/map/CollectionWrapper.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.map;\n\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.Spliterator;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\nclass CollectionWrapper<E>\n\t\timplements Collection<E> {\n\n\tprivate final Collection<E> collection;\n\n\tCollectionWrapper(Collection<E> collection) {\n\t\tthis.collection = collection;\n\t}\n\n\tpublic boolean add(E e) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\tpublic boolean addAll(Collection<? extends E> c) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\tpublic void clear() {\n\t\tcollection.clear();\n\t}\n\n\tpublic boolean contains(Object o) {\n\t\treturn collection.contains(o);\n\t}\n\n\tpublic boolean containsAll(Collection<?> c) {\n\t\treturn collection.containsAll(c);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\treturn collection.equals(o);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn collection.hashCode();\n\t}\n\n\tpublic boolean isEmpty() {\n\t\treturn collection.isEmpty();\n\t}\n\n\tpublic Iterator<E> iterator() {\n\t\treturn new IteratorWrapper<>(collection.iterator());\n\t}\n\n\tpublic Stream<E> parallelStream() {\n\t\treturn collection.parallelStream();\n\t}\n\n\tpublic boolean remove(Object o) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\tpublic boolean removeAll(Collection<?> c) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\tpublic boolean removeIf(Predicate<? super E> filter) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\tpublic boolean retainAll(Collection<?> c) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\tpublic int size() {\n\t\treturn collection.size();\n\t}\n\n\tpublic Spliterator<E> spliterator() {\n\t\treturn collection.spliterator();\n\t}\n\n\tpublic Stream<E> stream() {\n\t\treturn collection.stream();\n\t}\n\n\tpublic <T> T[] toArray(T[] a) {\n\t\treturn collection.toArray(a);\n\t}\n\n\tpublic Object[] toArray() {\n\t\treturn collection.toArray();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn collection.toString();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/map/DMap.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.map;\n\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\nclass DMap<K, V>\n\t\timplements Map<K, V> {\n\n\tfinal Class<K> keyClass;\n\tfinal DMapListener listener;\n\tfinal ConcurrentHashMap<K, V> map = new ConcurrentHashMap<K, V>();\n\tfinal String uid;\n\tfinal Class<V> valueClass;\n\n\tpublic DMap(String uid, DMapListener listener, final Class<K> keyClass, final Class<V> valueClass) {\n\t\tthis.listener = listener;\n\t\tthis.uid = uid;\n\t\tthis.keyClass = keyClass;\n\t\tthis.valueClass = valueClass;\n\t}\n\n\t@Override\n\tpublic void clear() {\n\t\tthis.listener.onClear(this);\n\t\tmap.clear();\n\t}\n\n\tpublic void clearNoEvent() {\n\t\tmap.clear();\n\t}\n\n\t@Override\n\tpublic boolean containsKey(Object key) {\n\t\treturn map.contains(key);\n\t}\n\n\t@Override\n\tpublic boolean containsValue(Object value) {\n\t\treturn map.containsValue(value);\n\t}\n\n\t@Override\n\tpublic Set<Entry<K, V>> entrySet() {\n\t\treturn new SetWrapper<>(map.entrySet());\n\t}\n\n\t@Override\n\tpublic V get(Object key) {\n\t\treturn map.get(key);\n\t}\n\n\tpublic String getUid() {\n\t\treturn uid;\n\t}\n\n\t@Override\n\tpublic boolean isEmpty() {\n\t\treturn map.isEmpty();\n\t}\n\n\t@Override\n\tpublic Set<K> keySet() {\n\t\treturn new SetWrapper<>(map.keySet());\n\t}\n\n\t@Override\n\tpublic V put(K key, V value) {\n\t\tthis.listener.onPut(this, key, value);\n\t\treturn map.put(key, value);\n\t}\n\n\t@Override\n\tpublic void putAll(Map<? extends K, ? extends V> m) {\n\t\tthis.listener.onPutAll(this, m);\n\t\tmap.putAll(m);\n\t}\n\n\tpublic void putNoEvent(K key, V value) {\n\t\tmap.put(key, value);\n\t}\n\n\t@Override\n\tpublic V remove(Object key) {\n\t\tlistener.onRemove(this, key);\n\t\treturn map.remove(key);\n\t}\n\n\tpublic V removeNoEvent(Object key) {\n\t\treturn map.remove(key);\n\t}\n\n\t@Override\n\tpublic int size() {\n\t\treturn map.size();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn map.toString();\n\t}\n\n\t@Override\n\tpublic Collection<V> values() {\n\t\treturn new CollectionWrapper<>(map.values());\n\t}\n\n\tinterface DMapListener {\n\n\t\tvoid onClear(DMap map);\n\n\t\tvoid onPut(DMap map, Object key, Object value);\n\n\t\tvoid onPutAll(DMap map, Map<?, ?> m);\n\n\t\tvoid onRemove(DMap map, Object key);\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/map/DMapListener.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.map;\n\n/**\n * Created by bmalkow on 04.12.2015.\n */\npublic interface DMapListener<K, V> {\n\n\tvoid onAddItem(K key, V value);\n\n\tvoid onClear();\n\n\tvoid onRemove(K key, V value);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/map/IteratorWrapper.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.map;\n\nimport java.util.Iterator;\nimport java.util.function.Consumer;\n\n/**\n * Created by bmalkow on 04.12.2015.\n */\nclass IteratorWrapper<E>\n\t\timplements Iterator<E> {\n\n\tprivate final Iterator<E> iterator;\n\n\tIteratorWrapper(Iterator<E> iterator) {\n\t\tthis.iterator = iterator;\n\t}\n\n\tpublic void forEachRemaining(Consumer<? super E> action) {\n\t\titerator.forEachRemaining(action);\n\t}\n\n\tpublic boolean hasNext() {\n\t\treturn iterator.hasNext();\n\t}\n\n\tpublic E next() {\n\t\treturn iterator.next();\n\t}\n\n\tpublic void remove() {\n\t\tthrow new UnsupportedOperationException();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/map/MapCreatedEvent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.map;\n\nimport java.util.Map;\n\npublic class MapCreatedEvent {\n\n\tprivate final Map map;\n\tprivate final String[] parameters;\n\tprivate final String uid;\n\n\tpublic MapCreatedEvent(Map map, String uid, String... parameters) {\n\t\tthis.map = map;\n\t\tthis.uid = uid;\n\t\tthis.parameters = parameters;\n\t}\n\n\tpublic Map getMap() {\n\t\treturn map;\n\t}\n\n\tpublic String[] getParameters() {\n\t\treturn parameters;\n\t}\n\n\tpublic String getUid() {\n\t\treturn uid;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/map/MapDestroyedEvent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.map;\n\nimport tigase.eventbus.EventBusEvent;\n\nimport java.util.Map;\n\npublic class MapDestroyedEvent implements EventBusEvent {\n\n\tprivate final Map map;\n\tprivate final String uid;\n\n\tpublic MapDestroyedEvent(Map map, String uid) {\n\t\tthis.map = map;\n\t\tthis.uid = uid;\n\t}\n\n\tpublic Map getMap() {\n\t\treturn map;\n\t}\n\n\tpublic String getUid() {\n\t\treturn uid;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/map/SetWrapper.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.map;\n\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.Set;\nimport java.util.Spliterator;\n\nclass SetWrapper<E>\n\t\timplements Set<E> {\n\n\tprivate final Set<E> set;\n\n\tSetWrapper(Set<E> set) {\n\t\tthis.set = set;\n\t}\n\n\tpublic boolean add(E e) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\tpublic boolean addAll(Collection<? extends E> c) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\tpublic void clear() {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\tpublic boolean contains(Object o) {\n\t\treturn set.contains(o);\n\t}\n\n\tpublic boolean containsAll(Collection<?> c) {\n\t\treturn set.containsAll(c);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\treturn set.equals(o);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn set.hashCode();\n\t}\n\n\tpublic boolean isEmpty() {\n\t\treturn set.isEmpty();\n\t}\n\n\tpublic Iterator<E> iterator() {\n\t\treturn new IteratorWrapper<>(set.iterator());\n\t}\n\n\tpublic boolean remove(Object o) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\tpublic boolean removeAll(Collection<?> c) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\tpublic boolean retainAll(Collection<?> c) {\n\t\treturn set.retainAll(c);\n\t}\n\n\tpublic int size() {\n\t\treturn set.size();\n\t}\n\n\tpublic Spliterator<E> spliterator() {\n\t\treturn set.spliterator();\n\t}\n\n\tpublic <T> T[] toArray(T[] a) {\n\t\treturn set.toArray(a);\n\t}\n\n\tpublic Object[] toArray() {\n\t\treturn set.toArray();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn set.toString();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/AdHocTask.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor;\n\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Collection;\n\npublic interface AdHocTask\n\t\textends MonitorTask {\n\n\tCollection<Element> getAdHocCommands(JID toJID, JID senderJID);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/ConfigurableTask.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor;\n\nimport tigase.form.Form;\n\npublic interface ConfigurableTask {\n\n\tForm getCurrentConfiguration();\n\n\tvoid setNewConfiguration(Form form);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/InfoTask.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor;\n\nimport tigase.form.Form;\n\npublic interface InfoTask {\n\n\tForm getTaskInfo();\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/MonitorComponent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor;\n\nimport tigase.component.AbstractKernelBasedComponent;\nimport tigase.component.modules.impl.JabberVersionModule;\nimport tigase.component.modules.impl.XmppPingModule;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\nimport tigase.monitor.modules.*;\nimport tigase.server.monitor.MonitorRuntime;\nimport tigase.util.common.TimerTask;\n\nimport javax.script.ScriptEngineManager;\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Bean(name = \"monitor\", parent = Kernel.class, active = true)\n@ConfigType({ConfigTypeEnum.DefaultMode, ConfigTypeEnum.SessionManagerMode, ConfigTypeEnum.ConnectionManagersMode,\n\t\t\t ConfigTypeEnum.ComponentMode})\npublic class MonitorComponent\n\t\textends AbstractKernelBasedComponent {\n\n\tprivate final TimerTaskService timerTaskService = new TimerTaskService() {\n\n\t\t@Override\n\t\tpublic void addTimerTask(TimerTask task, long delay) {\n\t\t\tMonitorComponent.this.addTimerTask(task, delay);\n\t\t}\n\n\t\t@Override\n\t\tpublic void addTimerTask(TimerTask task, long initialDelay, long period) {\n\t\t\tMonitorComponent.this.addTimerTask(task, initialDelay, period);\n\t\t}\n\t};\n\t@Inject(nullAllowed = true)\n\tprivate List<MonitorExtension> extensions = new ArrayList<>();\n\n\tpublic MonitorComponent() {}\n\n\t@Override\n\tpublic String getDiscoCategory() {\n\t\treturn \"component\";\n\t}\n\n\t@Override\n\tpublic String getDiscoCategoryType() {\n\t\treturn \"monitor\";\n\t}\n\n\t@Override\n\tpublic String getDiscoDescription() {\n\t\treturn \"Monitor Component\";\n\t}\n\n\t@Override\n\tpublic boolean isDiscoNonAdmin() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void start() {\n\t\tsuper.start();\n\t\t((TasksScriptRegistrar) kernel.getInstance(TasksScriptRegistrar.ID)).load();\n\t}\n\n\t@Override\n\tprotected void registerModules(Kernel kernel) {\n\t\tkernel.registerBean(\"runtime\").asInstance(MonitorRuntime.getMonitorRuntime()).exec();\n\t\tkernel.registerBean(TasksScriptRegistrar.class).exec();\n\n\t\tkernel.registerBean(XmppPingModule.class).exec();\n\t\tkernel.registerBean(JabberVersionModule.class).exec();\n\t\tkernel.registerBean(AdHocCommandMonitorModule.class).exec();\n\t\tkernel.registerBean(DiscoveryMonitorModule.class).exec();\n\n\t\tkernel.registerBean(AddScriptTaskCommand.class).exec();\n\t\tkernel.registerBean(AddTimerScriptTaskCommand.class).exec();\n\t\tkernel.registerBean(DeleteScriptTaskCommand.class).exec();\n\n\t\tScriptEngineManager scriptEngineManager = new ScriptEngineManager();\n\t\tkernel.registerBean(\"scriptEngineManager\").asInstance(scriptEngineManager).exec();\n\t\tkernel.registerBean(\"bindings\").asInstance(scriptEngineManager.getBindings()).exec();\n\n\t\tkernel.registerBean(\"timerTaskService\").asInstance(timerTaskService).exec();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/MonitorExtension.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor;\n\nimport java.util.Map;\n\npublic interface MonitorExtension {\n\n\tvoid setProperties(Map<String, Object> props);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/MonitorTask.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor;\n\npublic interface MonitorTask {\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/TaskConfigItem.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor;\n\nimport tigase.db.comp.RepositoryItemAbstract;\nimport tigase.form.Form;\nimport tigase.util.Base64;\nimport tigase.xml.Element;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class TaskConfigItem\n\t\textends RepositoryItemAbstract\n\t\timplements Comparable<TaskConfigItem> {\n\n\tpublic static final String CLASS_ELEM = \"class\";\n\tpublic static final String ELEM_NAME = \"monitor-task\";\n\tpublic static final String SCRIPT_ELEM = \"script\";\n\tpublic static final String SCRIPT_EXT_ATT = \"scriptExtension\";\n\tpublic static final String TASK_CLASS_ATT = \"taskClass\";\n\tpublic static final String TASK_NAME_ATT = \"taskName\";\n\tpublic static final String TASK_TYPE_ATT = \"type\";\n\tprotected static final String[] TASK_CLASS_PATH = {ELEM_NAME, CLASS_ELEM};\n\tprotected static final String[] TASK_SCRIPT_PATH = {ELEM_NAME, SCRIPT_ELEM};\n\n\tprivate static final Logger log = Logger.getLogger(TaskConfigItem.class.getName());\n\n\tpublic static enum Type {\n\t\tscriptTask,\n\t\tscriptTimerTask,\n\t\t/**\n\t\t * Default task, built with standard java class.\n\t\t */\n\t\ttask\n\t}\n\tprivate Form configuration;\n\n\tprivate String scriptExtension;\n\n\tprivate Class<? extends MonitorTask> taskClass;\n\n\tprivate String taskName;\n\n\tprivate String taskScript;\n\n\tprivate Type type;\n\n\tpublic TaskConfigItem() {\n\t}\n\n\tpublic TaskConfigItem(String taskName, Class<? extends MonitorTask> taskClass) {\n\t\tthis.taskName = taskName;\n\t\tthis.taskClass = taskClass;\n\t\tthis.type = Type.task;\n\t}\n\n\t@Override\n\tpublic int compareTo(TaskConfigItem o) {\n\t\treturn taskName.compareTo(o.taskName);\n\t}\n\n\tpublic Form getConfiguration() {\n\t\treturn configuration;\n\t}\n\n\tpublic void setConfiguration(Form configuration) {\n\t\tthis.configuration = configuration;\n\t}\n\n\t@Override\n\tpublic String getElemName() {\n\t\treturn ELEM_NAME;\n\t}\n\n\t@Override\n\tpublic String getKey() {\n\t\treturn taskName;\n\t}\n\n\t@Override\n\tprotected void setKey(String key) {\n\t\tsetTaskName(key);\n\t}\n\n\tpublic String getScriptExtension() {\n\t\treturn scriptExtension;\n\t}\n\n\tpublic void setScriptExtension(String scriptExtension) {\n\t\tthis.scriptExtension = scriptExtension;\n\t}\n\n\tpublic Class<? extends MonitorTask> getTaskClass() {\n\t\treturn taskClass;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate void setTaskClass(String tmp) {\n\t\tif (tmp == null) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tthis.taskClass = (Class<? extends MonitorTask>) Class.forName(tmp);\n\t\t} catch (ClassNotFoundException e) {\n\t\t\tlog.log(Level.WARNING, \"Error creating instance\", e);\n\t\t}\n\t}\n\n\tpublic String getTaskName() {\n\t\treturn taskName;\n\t}\n\n\tpublic void setTaskName(String taskName) {\n\t\tthis.taskName = taskName;\n\t}\n\n\tpublic String getTaskScript() {\n\t\treturn taskScript;\n\t}\n\n\tpublic void setTaskScript(String taskScript) {\n\t\tthis.taskScript = taskScript;\n\t}\n\n\tpublic Type getType() {\n\t\treturn type;\n\t}\n\n\tpublic void setType(Type type) {\n\t\tthis.type = type;\n\t}\n\n\t@Override\n\tpublic void initFromElement(Element elem) {\n\t\tif (elem.getName() != ELEM_NAME) {\n\t\t\tthrow new IllegalArgumentException(\"Incorrect element name, expected: \" + ELEM_NAME);\n\t\t}\n\t\tsuper.initFromElement(elem);\n\n\t\tthis.taskName = elem.getAttributeStaticStr(TASK_NAME_ATT);\n\t\ttry {\n\t\t\tthis.type = Type.valueOf(elem.getAttributeStaticStr(TASK_TYPE_ATT));\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\tthis.scriptExtension = elem.getAttributeStaticStr(SCRIPT_EXT_ATT);\n\n\t\tsetTaskClass(elem.getAttributeStaticStr(TASK_CLASS_ATT));\n\t\tsetTaskScriptEncoded(elem.getCDataStaticStr(TASK_SCRIPT_PATH));\n\n\t\tElement form = elem.getChild(\"x\", \"jabber:x:data\");\n\t\tif (form != null) {\n\t\t\tthis.configuration = new Form(form);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void initFromPropertyString(String propString) {\n\t\t// TODO Auto-generated method stub\n\n\t}\n\n\tpublic void setTaskClass(Class<? extends MonitorTask> taskClass) {\n\t\tthis.taskClass = taskClass;\n\t}\n\n\t@Override\n\tpublic Element toElement() {\n\t\tfinal Element elem = super.toElement();\n\n\t\telem.addAttribute(TASK_NAME_ATT, this.taskName);\n\t\telem.addAttribute(TASK_TYPE_ATT, this.type.name());\n\n\t\tif (this.scriptExtension != null) {\n\t\t\telem.addAttribute(SCRIPT_EXT_ATT, this.scriptExtension);\n\t\t}\n\n\t\tif (this.taskScript != null) {\n\t\t\telem.addChild(new Element(SCRIPT_ELEM, Base64.encode(this.taskScript.getBytes())));\n\t\t}\n\n\t\tif (this.taskClass != null) {\n\t\t\telem.addChild(new Element(CLASS_ELEM, this.taskClass.getName()));\n\t\t}\n\n\t\tif (this.configuration != null) {\n\t\t\telem.addChild(this.configuration.getElement());\n\t\t}\n\n\t\treturn elem;\n\t}\n\n\t@Override\n\tpublic String toPropertyString() {\n\t\t// TODO Auto-generated method stub\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"taskName=\" + taskName + \", taskClass=\" + taskClass + \", type=\" + type + \", configuration=\" +\n\t\t\t\tconfiguration;\n\t}\n\n\tprivate void setTaskScriptEncoded(String tmp) {\n\t\tif (tmp == null) {\n\t\t\treturn;\n\t\t}\n\t\tthis.taskScript = new String(Base64.decode(tmp));\n\t}\n}"
  },
  {
    "path": "src/main/java/tigase/monitor/TaskConfigItemJDBCRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor;\n\nimport tigase.db.DBInitException;\nimport tigase.db.comp.UserRepoRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.Map;\n\n@Bean(name = \"configItemRepository\", parent = MonitorComponent.class, active = true)\npublic class TaskConfigItemJDBCRepository\n\t\textends UserRepoRepository<TaskConfigItem> {\n\n\tprivate final static String CONFIG_KEY = \"monitor-tasks\";\n\n\tprivate final static BareJID REPO_USER_JID = BareJID.bareJIDInstanceNS(\"tigase-monitor\");\n\n\t@Override\n\tpublic void destroy() {\n\t\t// Nothing to do\n\t}\n\n\t@Override\n\tpublic String getConfigKey() {\n\t\treturn CONFIG_KEY;\n\t}\n\n\t@Override\n\tpublic String[] getDefaultPropetyItems() {\n\t\t// TODO Auto-generated method stub\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic TaskConfigItem getItemInstance() {\n\t\treturn new TaskConfigItem();\n\t}\n\n\t@Override\n\tpublic String getPropertyKey() {\n\t\treturn \"--monitor-tasks\";\n\t}\n\n\t@Override\n\tpublic BareJID getRepoUser() {\n\t\treturn REPO_USER_JID;\n\t}\n\n\t@Override\n\t@Deprecated\n\tpublic void initRepository(String resource_uri, Map<String, String> params) throws DBInitException {\n\t\t// Nothing to do\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/TasksScriptRegistrar.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor;\n\nimport tigase.db.TigaseDBException;\nimport tigase.db.comp.ComponentRepository;\nimport tigase.db.comp.RepositoryChangeListenerIfc;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.core.Kernel;\nimport tigase.monitor.TaskConfigItem.Type;\nimport tigase.monitor.tasks.ScriptTask;\nimport tigase.monitor.tasks.ScriptTimerTask;\n\nimport javax.script.ScriptException;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Bean(name = TasksScriptRegistrar.ID, active = true)\npublic class TasksScriptRegistrar {\n\n\tpublic static final String ID = \"TasksScriptRegistrar\";\n\tprotected final Logger log = Logger.getLogger(this.getClass().getName());\n\t@Inject(bean = \"kernel\")\n\tprivate Kernel kernel;\n\t@Inject(nullAllowed = false)\n\tprivate ComponentRepository<TaskConfigItem> repo;\n\n\tpublic void delete(String taskName) {\n\t\ttry {\n\t\t\trepo.removeItem(taskName);\n\t\t} catch (TigaseDBException e) {\n\t\t\tlog.log(Level.WARNING, \"Error accessing repository\", e);\n\t\t}\n\t}\n\n\tpublic Kernel getKernel() {\n\t\treturn kernel;\n\t}\n\n\tpublic void setKernel(Kernel kernel) {\n\t\tthis.kernel = kernel;\n\t}\n\n\tpublic ComponentRepository<TaskConfigItem> getRepo() {\n\t\treturn repo;\n\t}\n\n\tpublic void setRepo(ComponentRepository<TaskConfigItem> repo) {\n\t\tthis.repo = repo;\n\t\tthis.repo.addRepoChangeListener(new RepositoryChangeListenerIfc<TaskConfigItem>() {\n\n\t\t\t@Override\n\t\t\tpublic void itemAdded(TaskConfigItem item) {\n\t\t\t\tif (!kernel.isBeanClassRegistered(item.getTaskName())) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tinitTaskFromTaskConfig(item);\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Problem during initializing task\", e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void itemRemoved(TaskConfigItem item) {\n\t\t\t\ttry {\n\t\t\t\t\tkernel.unregister(item.getTaskName());\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Problem during unregistering task\", e);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void itemUpdated(TaskConfigItem item) {\n\t\t\t\ttry {\n\t\t\t\t\tMonitorTask task = kernel.getInstance(item.getTaskName());\n\t\t\t\t\tif (task instanceof ConfigurableTask) {\n\t\t\t\t\t\t((ConfigurableTask) task).setNewConfiguration(item.getConfiguration());\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Problem during configuring task\", e);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic void load() {\n\t\ttry {\n\t\t\tfor (TaskConfigItem item : repo.allItems()) {\n\t\t\t\tinitTaskFromTaskConfig(item);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Error loading task\", e);\n\t\t}\n\t}\n\n\tpublic void registerScript(String scriptName, String scriptExtension, String scriptContent) throws ScriptException {\n\t\tScriptTask task = runScriptTask(scriptName, scriptExtension, scriptContent);\n\t\tsaveScript(Type.scriptTask, scriptName, scriptExtension, scriptContent, task.getCurrentConfiguration());\n\t}\n\n\tpublic void registerTimerScript(String scriptName, String scriptExtension, String scriptContent, Long delay)\n\t\t\tthrows ScriptException {\n\t\tScriptTimerTask task = runScriptTimerTask(scriptName, scriptExtension, scriptContent, delay);\n\t\tsaveScript(Type.scriptTimerTask, scriptName, scriptExtension, scriptContent, task.getCurrentConfiguration());\n\t}\n\n\tpublic void updateConfig(String taskName, Form form) {\n\t\tMonitorTask task = kernel.getInstance(taskName);\n\n\t\tTaskConfigItem item = repo.getItem(taskName);\n\t\tif (item == null) {\n\t\t\titem = new TaskConfigItem();\n\t\t\titem.setTaskName(taskName);\n\t\t\tif (task instanceof ScriptTimerTask) {\n\t\t\t\titem.setType(Type.scriptTimerTask);\n\t\t\t\titem.setScriptExtension(((ScriptTimerTask) task).getScriptExtension());\n\t\t\t\titem.setTaskScript(((ScriptTimerTask) task).getScript());\n\t\t\t} else if (task instanceof ScriptTask) {\n\t\t\t\titem.setType(Type.scriptTask);\n\t\t\t\titem.setScriptExtension(((ScriptTask) task).getScriptExtension());\n\t\t\t\titem.setTaskScript(((ScriptTask) task).getScript());\n\t\t\t} else {\n\t\t\t\titem.setType(Type.task);\n\t\t\t\titem.setTaskClass(task.getClass());\n\t\t\t}\n\t\t}\n\t\titem.setConfiguration(form);\n\n\t\ttry {\n\t\t\trepo.addItem(item);\n\t\t} catch (TigaseDBException e) {\n\t\t\tlog.log(Level.WARNING, \"Error accessing repository\", e);\n\t\t}\n\t}\n\n\tprivate void initTaskFromTaskConfig(final TaskConfigItem item) throws ScriptException, TigaseDBException {\n\t\tType type;\n\t\ttry {\n\t\t\ttype = item.getType();\n\t\t\tif (type == null) {\n\t\t\t\trepo.removeItem(item.getKey());\n\t\t\t\treturn;\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\trepo.removeItem(item.getKey());\n\t\t\treturn;\n\t\t}\n\t\tString taskName = item.getTaskName();\n\t\tString scriptExtension = item.getScriptExtension();\n\t\tString scriptContent = item.getTaskScript();\n\n\t\tswitch (type) {\n\t\t\tcase task:\n\t\t\t\tif (kernel.isBeanClassRegistered(item.getTaskName())) {\n\t\t\t\t\tMonitorTask task = kernel.getInstance(item.getTaskName());\n\t\t\t\t\tif (task instanceof ConfigurableTask) {\n\t\t\t\t\t\t((ConfigurableTask) task).setNewConfiguration(item.getConfiguration());\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\trepo.removeItem(item.getKey());\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase scriptTask:\n\t\t\t\trunScriptTask(taskName, scriptExtension, scriptContent, item.getConfiguration());\n\t\t\tcase scriptTimerTask:\n\t\t\t\trunScriptTimerTask(taskName, scriptExtension, scriptContent, item.getConfiguration());\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\n\t}\n\n\tprivate ScriptTask runScriptTask(String scriptName, String scriptExtension, String scriptContent)\n\t\t\tthrows ScriptException {\n\t\tkernel.registerBean(scriptName).asClass(ScriptTask.class).exec();\n\t\tScriptTask scriptTask = kernel.getInstance(scriptName);\n\t\tscriptTask.setScript(scriptContent, scriptExtension);\n\t\tscriptTask.setEnabled(true);\n\t\treturn scriptTask;\n\t}\n\n\tprivate ScriptTask runScriptTask(String scriptName, String scriptExtension, String scriptContent, Form config)\n\t\t\tthrows ScriptException {\n\t\tkernel.registerBean(scriptName).asClass(ScriptTask.class).exec();\n\t\tScriptTask scriptTask = kernel.getInstance(scriptName);\n\t\tscriptTask.setScript(scriptContent, scriptExtension);\n\t\tscriptTask.setNewConfiguration(config);\n\t\treturn scriptTask;\n\t}\n\n\tprivate ScriptTimerTask runScriptTimerTask(String scriptName, String scriptExtension, String scriptContent,\n\t\t\t\t\t\t\t\t\t\t\t   Form config) throws ScriptException {\n\t\tkernel.registerBean(scriptName).asClass(ScriptTimerTask.class).exec();\n\t\tScriptTimerTask scriptTask = kernel.getInstance(scriptName);\n\t\tscriptTask.setScript(scriptContent, scriptExtension);\n\t\tscriptTask.setNewConfiguration(config);\n\t\treturn scriptTask;\n\t}\n\n\tprivate ScriptTimerTask runScriptTimerTask(String scriptName, String scriptExtension, String scriptContent,\n\t\t\t\t\t\t\t\t\t\t\t   Long delay) throws ScriptException {\n\t\tkernel.registerBean(scriptName).asClass(ScriptTimerTask.class).exec();\n\t\tScriptTimerTask scriptTask = kernel.getInstance(scriptName);\n\t\tscriptTask.setScript(scriptContent, scriptExtension);\n\t\tscriptTask.setPeriod(delay);\n\t\tscriptTask.setEnabled(true);\n\t\treturn scriptTask;\n\t}\n\n\tprivate void saveScript(Type type, String scriptName, String scriptExtension, String scriptContent,\n\t\t\t\t\t\t\tForm configuration) {\n\t\tTaskConfigItem item = new TaskConfigItem();\n\t\titem.setTaskName(scriptName);\n\t\titem.setConfiguration(configuration);\n\t\titem.setScriptExtension(scriptExtension);\n\t\titem.setTaskScript(scriptContent);\n\t\titem.setType(type);\n\n\t\ttry {\n\t\t\trepo.addItem(item);\n\t\t} catch (TigaseDBException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/TimerTaskService.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor;\n\nimport tigase.util.common.TimerTask;\n\npublic interface TimerTaskService {\n\n\tvoid addTimerTask(TimerTask task, long delay);\n\n\tvoid addTimerTask(TimerTask task, long initialDelay, long period);\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/modules/AdHocCommandMonitorModule.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor.modules;\n\nimport tigase.component.adhoc.AdHocCommand;\nimport tigase.component.adhoc.AdHocCommandException;\nimport tigase.component.adhoc.AdHocCommandManager;\nimport tigase.component.exceptions.ComponentException;\nimport tigase.component.modules.impl.AdHocCommandModule;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.core.Kernel;\nimport tigase.monitor.MonitorComponent;\nimport tigase.server.Command;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.jid.JID;\n\n@Bean(name = AdHocCommandModule.ID, parent = MonitorComponent.class, active = true)\npublic class AdHocCommandMonitorModule\n\t\textends AdHocCommandModule\n\t\timplements Initializable {\n\n\tprivate final AdHocCommandManager customCommandsManager = new AdHocCommandManager();\n\t@Inject\n\tprivate ConfigureTaskCommand configCommand;\n\t@Inject\n\tprivate InfoTaskCommand infoCommand;\n\n\t@Inject(bean = \"kernel\")\n\tprivate Kernel kernel;\n\n\tpublic AdHocCommandMonitorModule() {\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tsuper.initialize();\n\t}\n\n\t@Override\n\tpublic void process(Packet packet) throws ComponentException {\n\t\tfinal JID jid = packet.getStanzaTo();\n\n\t\tfinal Object taskInstance = jid.getResource() != null ? kernel.getInstance(jid.getResource()) : null;\n\n\t\tif (jid.getResource() != null && taskInstance != null) {\n\t\t\tprocessCommand(packet, taskInstance);\n\t\t} else if (jid.getResource() != null) {\n\t\t\tthrow new ComponentException(Authorization.ITEM_NOT_FOUND);\n\t\t} else if (jid.getResource() == null) {\n\t\t\tsuper.process(packet);\n\t\t} else {\n\t\t\tthrow new ComponentException(Authorization.NOT_ACCEPTABLE);\n\t\t}\n\t}\n\n\tprivate AdHocCommand getCommand(final Object taskInstance, final String node) {\n\t\tif (node.equals(InfoTaskCommand.NODE)) {\n\t\t\treturn infoCommand;\n\t\t} else if (node.equals(ConfigureTaskCommand.NODE)) {\n\t\t\treturn configCommand;\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate void processCommand(Packet packet, Object taskInstance) throws ComponentException {\n\t\tfinal Element element = packet.getElement();\n\t\tfinal JID senderJid = packet.getStanzaFrom();\n\t\tfinal Element command = element.getChild(Command.COMMAND_EL, Command.XMLNS);\n\t\tfinal String node = command.getAttributeStaticStr(\"node\");\n\t\tfinal String action = command.getAttributeStaticStr(\"action\");\n\t\tfinal String sessionId = command.getAttributeStaticStr(\"sessionid\");\n\n\t\tAdHocCommand adHocCommand = getCommand(taskInstance, node);\n\n\t\ttry {\n\t\t\tcustomCommandsManager.process(packet, command, node, action, sessionId, adHocCommand, writer::write);\n\t\t} catch (AdHocCommandException e) {\n\t\t\tthrow new ComponentException(e.getErrorCondition(), e.getMessage());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/modules/AddScriptTaskCommand.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor.modules;\n\nimport tigase.component.adhoc.AdHocCommand;\nimport tigase.component.adhoc.AdHocCommandException;\nimport tigase.component.adhoc.AdHocResponse;\nimport tigase.component.adhoc.AdhHocRequest;\nimport tigase.form.Field;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.core.Kernel;\nimport tigase.monitor.MonitorComponent;\nimport tigase.monitor.TasksScriptRegistrar;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.jid.JID;\n\nimport javax.script.ScriptEngineFactory;\nimport javax.script.ScriptEngineManager;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Bean(name = \"x-add-task\", parent = MonitorComponent.class, active = true)\npublic class AddScriptTaskCommand\n\t\timplements AdHocCommand {\n\n\tprivate final static Logger log = Logger.getLogger(AddScriptTaskCommand.class.getName());\n\tprivate static final Optional<String> GROUP = Optional.of(\"Monitor\");\n\n\t@Inject\n\tprivate MonitorComponent component;\n\t@Inject(bean = \"kernel\")\n\tprivate Kernel kernel;\n\n\tpublic AddScriptTaskCommand() {\n\t}\n\n\t@Override\n\tpublic void execute(AdhHocRequest request, AdHocResponse response) throws AdHocCommandException {\n\t\ttry {\n\t\t\tfinal Element data = request.getCommand().getChild(\"x\", \"jabber:x:data\");\n\n\t\t\tif (request.getAction() != null && \"cancel\".equals(request.getAction())) {\n\t\t\t\tresponse.cancelSession();\n\t\t\t} else if (data == null) {\n\t\t\t\tForm form = new Form(\"form\", \"Add monitor script\", null);\n\n\t\t\t\tList<ScriptEngineFactory> sef = kernel.getInstance(ScriptEngineManager.class).getEngineFactories();\n\t\t\t\tArrayList<String> labels = new ArrayList<String>();\n\t\t\t\tArrayList<String> values = new ArrayList<String>();\n\t\t\t\tfor (ScriptEngineFactory scriptEngineFactory : sef) {\n\t\t\t\t\tlabels.add(scriptEngineFactory.getLanguageName());\n\t\t\t\t\tvalues.add(scriptEngineFactory.getExtensions().get(0));\n\t\t\t\t}\n\n\t\t\t\tform.addField(Field.fieldTextSingle(\"scriptName\", \"\", \"Script name\"));\n\t\t\t\tform.addField(\n\t\t\t\t\t\tField.fieldListSingle(\"scriptExtension\", \"\", \"Script engine\", labels.toArray(new String[]{}),\n\t\t\t\t\t\t\t\t\t\t\t  values.toArray(new String[]{})));\n\t\t\t\tform.addField(Field.fieldTextMulti(\"scriptContent\", \"\", \"Script\"));\n\n\t\t\t\tresponse.getElements().add(form.getElement());\n\t\t\t\tresponse.startSession();\n\t\t\t} else {\n\t\t\t\tForm form = new Form(data);\n\n\t\t\t\tif (\"submit\".equals(form.getType())) {\n\t\t\t\t\tString scriptName = form.getAsString(\"scriptName\");\n\t\t\t\t\tString scriptExtension = form.getAsString(\"scriptExtension\");\n\t\t\t\t\tString scriptContent = form.getAsString(\"scriptContent\");\n\n\t\t\t\t\t((TasksScriptRegistrar) kernel.getInstance(TasksScriptRegistrar.ID)).registerScript(scriptName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tscriptExtension,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tscriptContent);\n\n\t\t\t\t}\n\t\t\t\tform = new Form(\"form\", \"Completed\", null);\n\t\t\t\tform.addField(Field.fieldFixed(\"Script added.\"));\n\t\t\t\tresponse.getElements().add(form.getElement());\n\n\t\t\t\tresponse.completeSession();\n\t\t\t}\n\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.FINEST, \"Error adding script\", e);\n\t\t\tthrow new AdHocCommandException(Authorization.INTERNAL_SERVER_ERROR, e.getMessage());\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"Add monitor task\";\n\t}\n\n\t@Override\n\tpublic String getNode() {\n\t\treturn \"x-add-task\";\n\t}\n\n\t@Override\n\tpublic Optional<String> getGroup() {\n\t\treturn GROUP;\n\t}\n\n\t@Override\n\tpublic boolean isAllowedFor(JID jid) {\n\t\treturn component.isAdmin(jid);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/modules/AddTimerScriptTaskCommand.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor.modules;\n\nimport tigase.component.adhoc.AdHocCommand;\nimport tigase.component.adhoc.AdHocCommandException;\nimport tigase.component.adhoc.AdHocResponse;\nimport tigase.component.adhoc.AdhHocRequest;\nimport tigase.form.Field;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.core.Kernel;\nimport tigase.monitor.MonitorComponent;\nimport tigase.monitor.TasksScriptRegistrar;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.jid.JID;\n\nimport javax.script.ScriptEngineFactory;\nimport javax.script.ScriptEngineManager;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Bean(name = \"x-add-timer-task\", parent = MonitorComponent.class, active = true)\npublic class AddTimerScriptTaskCommand\n\t\timplements AdHocCommand {\n\n\tprivate final static Logger log = Logger.getLogger(AddTimerScriptTaskCommand.class.getName());\n\t@Inject\n\tprivate MonitorComponent component;\n\t@Inject(bean = \"kernel\")\n\tprivate Kernel kernel;\n\n\tpublic AddTimerScriptTaskCommand() {\n\t}\n\n\t@Override\n\tpublic void execute(AdhHocRequest request, AdHocResponse response) throws AdHocCommandException {\n\t\ttry {\n\t\t\tfinal Element data = request.getCommand().getChild(\"x\", \"jabber:x:data\");\n\n\t\t\tif (request.getAction() != null && \"cancel\".equals(request.getAction())) {\n\t\t\t\tresponse.cancelSession();\n\t\t\t} else if (data == null) {\n\t\t\t\tForm form = new Form(\"form\", \"Add monitor script\", null);\n\n\t\t\t\tList<ScriptEngineFactory> sef = kernel.getInstance(ScriptEngineManager.class).getEngineFactories();\n\t\t\t\tArrayList<String> labels = new ArrayList<String>();\n\t\t\t\tArrayList<String> values = new ArrayList<String>();\n\t\t\t\tfor (ScriptEngineFactory scriptEngineFactory : sef) {\n\t\t\t\t\tlabels.add(scriptEngineFactory.getLanguageName());\n\t\t\t\t\tvalues.add(scriptEngineFactory.getExtensions().get(0));\n\t\t\t\t}\n\n\t\t\t\tform.addField(Field.fieldTextSingle(\"scriptName\", \"\", \"Script name\"));\n\t\t\t\tform.addField(Field.fieldTextSingle(\"delay\", \"1000\", \"Delay\"));\n\t\t\t\tform.addField(\n\t\t\t\t\t\tField.fieldListSingle(\"scriptExtension\", \"\", \"Script engine\", labels.toArray(new String[]{}),\n\t\t\t\t\t\t\t\t\t\t\t  values.toArray(new String[]{})));\n\t\t\t\tform.addField(Field.fieldTextMulti(\"scriptContent\", \"\", \"Script\"));\n\n\t\t\t\tresponse.getElements().add(form.getElement());\n\t\t\t\tresponse.startSession();\n\t\t\t} else {\n\t\t\t\tForm form = new Form(data);\n\n\t\t\t\tif (\"submit\".equals(form.getType())) {\n\t\t\t\t\tString scriptName = form.getAsString(\"scriptName\");\n\t\t\t\t\tString scriptExtension = form.getAsString(\"scriptExtension\");\n\t\t\t\t\tString scriptContent = form.getAsString(\"scriptContent\");\n\t\t\t\t\tLong delay = form.getAsLong(\"delay\");\n\n\t\t\t\t\t((TasksScriptRegistrar) kernel.getInstance(TasksScriptRegistrar.ID)).registerTimerScript(scriptName,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t scriptExtension,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t scriptContent,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t delay);\n\t\t\t\t}\n\n\t\t\t\tform = new Form(\"form\", \"Completed\", null);\n\t\t\t\tform.addField(Field.fieldFixed(\"Script added.\"));\n\t\t\t\tresponse.getElements().add(form.getElement());\n\n\t\t\t\tresponse.completeSession();\n\t\t\t}\n\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.FINEST, \"Error adding script\", e);\n\t\t\tthrow new AdHocCommandException(Authorization.INTERNAL_SERVER_ERROR, e.getMessage());\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"Add monitor timer task\";\n\t}\n\n\t@Override\n\tpublic String getNode() {\n\t\treturn \"x-add-timer-task\";\n\t}\n\n\t@Override\n\tpublic boolean isAllowedFor(JID jid) {\n\t\treturn component.isAdmin(jid);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/modules/ConfigureTaskCommand.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor.modules;\n\nimport tigase.component.adhoc.AdHocCommand;\nimport tigase.component.adhoc.AdHocCommandException;\nimport tigase.component.adhoc.AdHocResponse;\nimport tigase.component.adhoc.AdhHocRequest;\nimport tigase.form.Field;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.core.Kernel;\nimport tigase.monitor.ConfigurableTask;\nimport tigase.monitor.MonitorComponent;\nimport tigase.monitor.TasksScriptRegistrar;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.monitor.modules.ConfigureTaskCommand.NODE;\n\n@Bean(name = NODE, parent = MonitorComponent.class, active = true)\npublic class ConfigureTaskCommand\n\t\timplements AdHocCommand {\n\n\tprivate final static Logger log = Logger.getLogger(ConfigureTaskCommand.class.getName());\n\tpublic static final String NODE = \"x-config\";\n\n\t@Inject\n\tprivate MonitorComponent component;\n\t@Inject(bean = \"kernel\")\n\tprivate Kernel kernel;\n\t@Inject\n\tprivate TasksScriptRegistrar registrar;\n\n\tpublic ConfigureTaskCommand() {\n\t}\n\n\t@Override\n\tpublic void execute(AdhHocRequest request, AdHocResponse response) throws AdHocCommandException {\n\t\ttry {\n\t\t\tfinal Element data = request.getCommand().getChild(\"x\", \"jabber:x:data\");\n\n\t\t\tif (request.getAction() != null && \"cancel\".equals(request.getAction())) {\n\t\t\t\tresponse.cancelSession();\n\t\t\t} else if (data == null) {\n\t\t\t\tfinal ConfigurableTask taskInstance = kernel.getInstance(request.getIq().getStanzaTo().getResource());\n\t\t\t\tForm form = taskInstance.getCurrentConfiguration();\n\t\t\t\tform.setType(\"form\");\n\t\t\t\tresponse.getElements().add(form.getElement());\n\t\t\t\tresponse.startSession();\n\t\t\t} else {\n\t\t\t\tForm form = new Form(data);\n\t\t\t\tif (\"submit\".equals(form.getType())) {\n\t\t\t\t\tfinal ConfigurableTask taskInstance = kernel.getInstance(\n\t\t\t\t\t\t\trequest.getIq().getStanzaTo().getResource());\n\n\t\t\t\t\tregistrar.updateConfig(request.getIq().getStanzaTo().getResource(), form);\n\n\t\t\t\t\tform = new Form(\"form\", \"Completed\", null);\n\t\t\t\t\tform.addField(Field.fieldFixed(\"Script configured\"));\n\t\t\t\t\tresponse.getElements().add(form.getElement());\n\n\t\t\t\t\tresponse.completeSession();\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.FINEST, \"Error configuring task\", e);\n\t\t\tthrow new AdHocCommandException(Authorization.INTERNAL_SERVER_ERROR, e.getMessage());\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"Task config\";\n\t}\n\n\t@Override\n\tpublic String getNode() {\n\t\treturn NODE;\n\t}\n\n\t@Override\n\tpublic boolean isAllowedFor(JID jid) {\n\t\treturn component.isAdmin(jid);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/modules/DeleteScriptTaskCommand.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor.modules;\n\nimport tigase.component.adhoc.AdHocCommand;\nimport tigase.component.adhoc.AdHocCommandException;\nimport tigase.component.adhoc.AdHocResponse;\nimport tigase.component.adhoc.AdhHocRequest;\nimport tigase.form.Field;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.core.Kernel;\nimport tigase.monitor.MonitorComponent;\nimport tigase.monitor.MonitorTask;\nimport tigase.monitor.TasksScriptRegistrar;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Collection;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Bean(name = \"x-delete-task\", parent = MonitorComponent.class, active = true)\npublic class DeleteScriptTaskCommand\n\t\timplements AdHocCommand {\n\n\tprivate final static Logger log = Logger.getLogger(DeleteScriptTaskCommand.class.getName());\n\t@Inject\n\tprivate MonitorComponent component;\n\t@Inject(bean = \"kernel\")\n\tprivate Kernel kernel;\n\n\tpublic DeleteScriptTaskCommand() {\n\t}\n\n\t@Override\n\tpublic void execute(AdhHocRequest request, AdHocResponse response) throws AdHocCommandException {\n\t\ttry {\n\t\t\tfinal Element data = request.getCommand().getChild(\"x\", \"jabber:x:data\");\n\n\t\t\tif (request.getAction() != null && \"cancel\".equals(request.getAction())) {\n\t\t\t\tresponse.cancelSession();\n\t\t\t} else if (data == null) {\n\t\t\t\tForm form = new Form(\"form\", \"Delete monitor task\", null);\n\n\t\t\t\tCollection<String> taskNames = kernel.getNamesOf(MonitorTask.class);\n\n\t\t\t\tform.addField(\n\t\t\t\t\t\tField.fieldListSingle(\"delete_task\", \"\", \"Task to delete\", taskNames.toArray(new String[]{}),\n\t\t\t\t\t\t\t\t\t\t\t  taskNames.toArray(new String[]{})));\n\n\t\t\t\tresponse.getElements().add(form.getElement());\n\t\t\t\tresponse.startSession();\n\t\t\t} else {\n\t\t\t\tForm form = new Form(data);\n\n\t\t\t\tif (\"submit\".equals(form.getType())) {\n\t\t\t\t\tString taskName = form.getAsString(\"delete_task\");\n\n\t\t\t\t\tObject i = kernel.getInstance(taskName);\n\t\t\t\t\tif (i instanceof MonitorTask) {\n\t\t\t\t\t\t((TasksScriptRegistrar) kernel.getInstance(TasksScriptRegistrar.ID)).delete(taskName);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new RuntimeException(\"Are you kidding me?\");\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tform = new Form(\"form\", \"Completed\", null);\n\t\t\t\tform.addField(Field.fieldFixed(\"Script removed\"));\n\t\t\t\tresponse.getElements().add(form.getElement());\n\n\t\t\t\tresponse.completeSession();\n\t\t\t}\n\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.FINEST, \"Error executing script\", e);\n\t\t\tthrow new AdHocCommandException(Authorization.INTERNAL_SERVER_ERROR, e.getMessage());\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"Delete monitor task\";\n\t}\n\n\t@Override\n\tpublic String getNode() {\n\t\treturn \"x-delete-task\";\n\t}\n\n\t@Override\n\tpublic boolean isAllowedFor(JID jid) {\n\t\treturn component.isAdmin(jid);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/modules/DiscoveryMonitorModule.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor.modules;\n\nimport tigase.component.exceptions.ComponentException;\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.component.modules.impl.DiscoveryModule;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.core.Kernel;\nimport tigase.monitor.*;\nimport tigase.server.Command;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\n@Bean(name = DiscoveryModule.ID, parent = MonitorComponent.class, active = true)\npublic class DiscoveryMonitorModule\n\t\textends DiscoveryModule {\n\n\t@Inject(bean = \"kernel\")\n\tprivate Kernel kernel;\n\n\t@Inject(nullAllowed = true)\n\tprivate MonitorTask[] takss;\n\n\t@Override\n\tprotected void processAdHocCommandItems(Packet packet, JID jid, String node, JID senderJID)\n\t\t\tthrows ComponentException, RepositoryException {\n\t\tif (jid.getResource() != null && isAdHocCompatible(kernel.getInstance(jid.getResource()))) {\n\t\t\tfinal Object taskInstance = kernel.getInstance(jid.getResource());\n\n\t\t\tList<Element> items = new ArrayList<Element>();\n\t\t\tElement resultQuery = new Element(\"query\", new String[]{Packet.XMLNS_ATT}, new String[]{DISCO_ITEMS_XMLNS});\n\t\t\tPacket result = packet.okResult(resultQuery, 0);\n\n\t\t\tif (taskInstance instanceof InfoTask) {\n\t\t\t\titems.add(new Element(\"item\", new String[]{\"jid\", \"node\", \"name\"},\n\t\t\t\t\t\t\t\t\t  new String[]{jid.toString(), InfoTaskCommand.NODE, \"Task Info\"}));\n\t\t\t}\n\t\t\tif (taskInstance instanceof ConfigurableTask) {\n\t\t\t\titems.add(new Element(\"item\", new String[]{\"jid\", \"node\", \"name\"},\n\t\t\t\t\t\t\t\t\t  new String[]{jid.toString(), ConfigureTaskCommand.NODE, \"Task config\"}));\n\t\t\t}\n\n\t\t\tif (taskInstance instanceof AdHocTask) {\n\t\t\t\titems.addAll(((AdHocTask) taskInstance).getAdHocCommands(jid, senderJID));\n\t\t\t}\n\n\t\t\tresultQuery.addChildren(items);\n\t\t\twrite(result);\n\n\t\t} else if (jid.getResource() != null) {\n\t\t\tthrow new ComponentException(Authorization.ITEM_NOT_FOUND);\n\t\t} else {\n\t\t\tsuper.processAdHocCommandItems(packet, jid, node, senderJID);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void processDiscoInfo(Packet packet, JID jid, String node, JID senderJID)\n\t\t\tthrows ComponentException, RepositoryException {\n\t\tif (jid.getResource() == null) {\n\t\t\tsuper.processDiscoInfo(packet, jid, node, senderJID);\n\t\t} else if (jid.getResource() != null && kernel.getInstance(jid.getResource()) != null) {\n\t\t\tfinal Object taskInstance = kernel.getInstance(jid.getResource());\n\n\t\t\tElement resultQuery = new Element(\"query\", new String[]{\"xmlns\"}, new String[]{DISCO_INFO_XMLNS});\n\t\t\tPacket resultIq = packet.okResult(resultQuery, 0);\n\n\t\t\tresultQuery.addChild(new Element(\"identity\", new String[]{\"category\", \"type\", \"name\"},\n\t\t\t\t\t\t\t\t\t\t\t new String[]{\"automation\", \"task\", \"Task \" + jid.getResource()}));\n\n\t\t\tif (isAdHocCompatible(taskInstance)) {\n\t\t\t\tresultQuery.addChild(new Element(\"feature\", new String[]{\"var\"}, new String[]{Command.XMLNS}));\n\t\t\t}\n\n\t\t\twrite(resultIq);\n\t\t} else {\n\t\t\tthrow new ComponentException(Authorization.ITEM_NOT_FOUND);\n\t\t}\n\n\t}\n\n\t@Override\n\tprotected void processDiscoItems(Packet packet, JID jid, String node, JID senderJID)\n\t\t\tthrows ComponentException, RepositoryException {\n\t\tif (node == null && jid.getResource() == null) {\n\t\t\tElement resultQuery = new Element(\"query\", new String[]{Packet.XMLNS_ATT}, new String[]{DISCO_ITEMS_XMLNS});\n\n\t\t\tCollection<String> taskNames = kernel.getNamesOf(MonitorTask.class);\n\t\t\tfor (String taskName : taskNames) {\n\t\t\t\tresultQuery.addChild(new Element(\"item\", new String[]{\"jid\", \"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t new String[]{jid.toString() + \"/\" + taskName, \"Task \" + taskName}));\n\t\t\t}\n\n\t\t\twrite(packet.okResult(resultQuery, 0));\n\t\t} else {\n\t\t\tElement resultQuery = new Element(\"query\", new String[]{Packet.XMLNS_ATT}, new String[]{DISCO_ITEMS_XMLNS});\n\t\t\twrite(packet.okResult(resultQuery, 0));\n\t\t}\n\t}\n\n\tprivate boolean isAdHocCompatible(Object taskInstance) {\n\t\treturn taskInstance != null && (taskInstance instanceof AdHocTask || taskInstance instanceof InfoTask ||\n\t\t\t\ttaskInstance instanceof ConfigurableTask);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/modules/InfoTaskCommand.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor.modules;\n\nimport tigase.component.adhoc.AdHocCommand;\nimport tigase.component.adhoc.AdHocCommandException;\nimport tigase.component.adhoc.AdHocResponse;\nimport tigase.component.adhoc.AdhHocRequest;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.core.Kernel;\nimport tigase.monitor.InfoTask;\nimport tigase.monitor.MonitorComponent;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.monitor.modules.InfoTaskCommand.NODE;\n\n@Bean(name = NODE, parent = MonitorComponent.class, active = true)\npublic class InfoTaskCommand\n\t\timplements AdHocCommand {\n\n\tprivate final static Logger log = Logger.getLogger(InfoTaskCommand.class.getName());\n\tpublic static final String NODE = \"x-info\";\n\n\t@Inject\n\tprivate MonitorComponent component;\n\t@Inject(bean = \"kernel\")\n\tprivate Kernel kernel;\n\n\tpublic InfoTaskCommand() {\n\t}\n\n\t@Override\n\tpublic void execute(AdhHocRequest request, AdHocResponse response) throws AdHocCommandException {\n\t\ttry {\n\t\t\tif (request.getAction() != null && \"cancel\".equals(request.getAction())) {\n\t\t\t\tresponse.cancelSession();\n\t\t\t} else {\n\t\t\t\tfinal InfoTask taskInstance = kernel.getInstance(request.getIq().getStanzaTo().getResource());\n\n\t\t\t\tForm form = taskInstance.getTaskInfo();\n\n\t\t\t\tresponse.getElements().add(form.getElement());\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.FINEST, \"Error executing script\", e);\n\t\t\tthrow new AdHocCommandException(Authorization.INTERNAL_SERVER_ERROR, e.getMessage());\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"Task info\";\n\t}\n\n\t@Override\n\tpublic String getNode() {\n\t\treturn NODE;\n\t}\n\n\t@Override\n\tpublic boolean isAllowedFor(JID jid) {\n\t\treturn component.isAdmin(jid);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/tasks/AbstractConfigurableTask.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor.tasks;\n\nimport tigase.form.Field;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.monitor.ConfigurableTask;\nimport tigase.monitor.MonitorTask;\n\npublic abstract class AbstractConfigurableTask\n\t\timplements MonitorTask, ConfigurableTask, UnregisterAware, Initializable {\n\n\tprivate final static String ENABLED_VAR = \"x-task#enabled\";\n\n\t@ConfigField(desc = \"Enable task\")\n\tprivate boolean enabled = false;\n\tprivate boolean initialized = false;\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\tsetEnabled(false);\n\t}\n\n\t@Override\n\tpublic Form getCurrentConfiguration() {\n\t\tForm f = new Form(\"\", \"Task Configuration\", \"\");\n\n\t\tf.addField(Field.fieldBoolean(ENABLED_VAR, enabled, \"Enabled\"));\n\t\t// f.addField(Field.fieldTextSingle(\"period\", \"\" + period,\n\t\t// \"Period [ms]\"));\n\n\t\treturn f;\n\t}\n\n\tpublic boolean isEnabled() {\n\t\treturn enabled;\n\t}\n\n\tpublic void setEnabled(boolean value) {\n\t\tif (enabled && !value) {\n\t\t\t// turning off\n\t\t\tthis.enabled = value;\n\t\t\tif (initialized) {\n\t\t\t\tdisable();\n\t\t\t}\n\t\t} else if (!enabled && value) {\n\t\t\t// turning on\n\t\t\tthis.enabled = value;\n\t\t\tif (initialized) {\n\t\t\t\tenable();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tthis.initialized = true;\n\t\tif (isEnabled()) {\n\t\t\tenable();\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void setNewConfiguration(Form form) {\n\t\tField f = form.get(ENABLED_VAR);\n\t\tif (f != null) {\n\t\t\tboolean value = Field.getAsBoolean(f);\n\t\t\tsetEnabled(value);\n\t\t}\n\t}\n\n\tprotected void disable() {\n\t}\n\n\tprotected void enable() {\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/tasks/AbstractConfigurableTimerTask.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor.tasks;\n\nimport tigase.form.Field;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.monitor.TimerTaskService;\nimport tigase.util.common.TimerTask;\n\npublic abstract class AbstractConfigurableTimerTask\n\t\textends AbstractConfigurableTask\n\t\timplements UnregisterAware {\n\n\tprivate final static String PERIOD_VAR = \"x-task#period\";\n\tprivate final TimerTask worker = new TimerTask() {\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tAbstractConfigurableTimerTask.this.run();\n\t\t}\n\t};\n\t@ConfigField(desc = \"Task execute period [ms]\")\n\tprivate long period = 1000l;\n\t@Inject(bean = \"timerTaskService\")\n\tprivate TimerTaskService timerTaskService;\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\tsetEnabled(false);\n\t}\n\n\t@Override\n\tpublic Form getCurrentConfiguration() {\n\t\tForm f = super.getCurrentConfiguration();\n\n\t\tf.addField(Field.fieldTextSingle(PERIOD_VAR, \"\" + period, \"Period [ms]\"));\n\n\t\treturn f;\n\t}\n\n\tpublic long getPeriod() {\n\t\treturn period;\n\t}\n\n\tpublic void setPeriod(long value) {\n\t\tif (this.period != value) {\n\t\t\tthis.period = value;\n\t\t\tif (isEnabled()) {\n\t\t\t\tworker.cancel();\n\t\t\t\ttimerTaskService.addTimerTask(worker, 1000l, period);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic TimerTaskService getTimerTaskService() {\n\t\treturn timerTaskService;\n\t}\n\n\tpublic void setTimerTaskService(TimerTaskService timerTaskService) {\n\t\tthis.timerTaskService = timerTaskService;\n\t}\n\n\t@Override\n\tpublic void setNewConfiguration(Form form) {\n\t\tField f = form.get(PERIOD_VAR);\n\t\tif (f != null) {\n\t\t\tlong value = Long.parseLong(f.getValue());\n\t\t\tsetPeriod(value);\n\t\t}\n\n\t\tsuper.setNewConfiguration(form);\n\t}\n\t\n\t@Override\n\tprotected void disable() {\n\t\tsuper.disable();\n\t\tworker.cancel();\n\t}\n\n\t@Override\n\tprotected void enable() {\n\t\tsuper.enable();\n\t\tif (timerTaskService != null) {\n\t\t\ttimerTaskService.addTimerTask(worker, 1000l, period);\n\t\t}\n\t}\n\n\tprotected abstract void run();\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/tasks/ConnectionsTask.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor.tasks;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.eventbus.EventBus;\nimport tigase.form.Field;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.monitor.InfoTask;\nimport tigase.monitor.MonitorComponent;\nimport tigase.server.XMPPServer;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.util.datetime.TimestampHelper;\nimport tigase.xml.Element;\n\nimport java.util.Date;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Bean(name = \"connections-task\", parent = MonitorComponent.class, active = true)\npublic class ConnectionsTask\n\t\textends AbstractConfigurableTimerTask\n\t\timplements InfoTask, Initializable {\n\n\tprotected final static TimestampHelper dtf = new TimestampHelper();\n\tprotected static final Logger log = Logger.getLogger(ConnectionsTask.class.getName());\n\tprivate static final String USERS_DISCONNECTEED_EVENT_NAME = \"tigase.monitor.tasks.UsersDisconnected\";\n\t@Inject\n\tprotected MonitorComponent component;\n\t@Inject\n\tprotected EventBus eventBus;\n\tprivate int lastOnlineUsers;\n\t@ConfigField(desc = \"Percent of disconnected users\")\n\tprivate int threshold = 80;\n\t@ConfigField(desc = \"Minimal amount of disconnected users\")\n\tprivate int thresholdMinimal = 10;\n\n\t/**\n\t * Creates alarm event if required. Event will be created only if both conditions will met.\n\t *\n\t * @param currentOnlineUsers current amount of online users.\n\t * @param lastOnlineUsers previous amount of online users.\n\t * @param thresholdMinimal minimal amount of disconnected users to create alarm event.\n\t * @param threshold percent of disconnected users to create alarm event.\n\t *\n\t * @return event or <code>null</code>.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"Class based events should be used\")\n\tpublic static Element createAlarmEvent(int currentOnlineUsers, int lastOnlineUsers, int thresholdMinimal,\n\t\t\t\t\t\t\t\t\t\t   int threshold) {\n\t\tfinal int delta = currentOnlineUsers - lastOnlineUsers;\n\t\tfinal float percent = (lastOnlineUsers == 0 ? 1 : ((float) delta) / (float) lastOnlineUsers) * 100;\n\n\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\tlog.fine(\"Data: lastOnlineUsers=\" + lastOnlineUsers + \"; currentOnlineUsers=\" + currentOnlineUsers +\n\t\t\t\t\t\t\t \"; delta=\" + delta + \"; percent=\" + percent + \"; thresholdMinimal=\" + thresholdMinimal +\n\t\t\t\t\t\t\t \"; threshold=\" + threshold);\n\t\t}\n\n\t\tif (-1 * delta >= thresholdMinimal && -1 * percent >= threshold) {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.fine(\"Creating event!\");\n\t\t\t}\n\n\t\t\tElement event = new Element(USERS_DISCONNECTEED_EVENT_NAME);\n\t\t\tevent.addChild(new Element(\"timestamp\", \"\" + dtf.format(new Date())));\n\t\t\tevent.addChild(new Element(\"disconnections\", \"\" + (-1 * delta)));\n\t\t\tevent.addChild(new Element(\"disconnectionsPercent\", \"\" + (-1 * percent)));\n\n\t\t\treturn event;\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic static Optional<UserDisconnectedEvent> createUserDisconnectedEvent(int currentOnlineUsers,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  int lastOnlineUsers, int thresholdMinimal,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  int threshold) {\n\t\tfinal int delta = currentOnlineUsers - lastOnlineUsers;\n\t\tfinal float percent = (lastOnlineUsers == 0 ? 1 : ((float) delta) / (float) lastOnlineUsers) * 100;\n\n\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\tlog.fine(\"Data: lastOnlineUsers=\" + lastOnlineUsers + \"; currentOnlineUsers=\" + currentOnlineUsers +\n\t\t\t\t\t\t\t \"; delta=\" + delta + \"; percent=\" + percent + \"; thresholdMinimal=\" + thresholdMinimal +\n\t\t\t\t\t\t\t \"; threshold=\" + threshold);\n\t\t}\n\n\t\tif (-1 * delta >= thresholdMinimal && -1 * percent >= threshold) {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.fine(\"Creating event!\");\n\t\t\t}\n\n\t\t\treturn Optional.of(new UserDisconnectedEvent(delta, percent));\n\t\t} else {\n\t\t\treturn Optional.empty();\n\t\t}\n\t}\n\n\tpublic ConnectionsTask() {\n\t}\n\n\t@Override\n\tpublic Form getCurrentConfiguration() {\n\t\tForm x = super.getCurrentConfiguration();\n\t\tx.addField(Field.fieldTextSingle(\"threshold\", \"\" + threshold, \"Percent of disconnected users\"));\n\t\tx.addField(Field.fieldTextSingle(\"thresholdMinimal\", \"\" + thresholdMinimal,\n\t\t\t\t\t\t\t\t\t\t \"Minimal amount of disconnected users\"));\n\t\treturn x;\n\t}\n\n\t@Override\n\tpublic Form getTaskInfo() {\n\t\tForm x = new Form(\"\", \"Task Info\", \"\");\n\t\tx.addField(Field.fieldTextSingle(\"lastUsersOnline\", \"\" + lastOnlineUsers, \"Last measured online users\"));\n\t\treturn x;\n\t}\n\n\tpublic int getThreshold() {\n\t\treturn threshold;\n\t}\n\n\tpublic void setThreshold(int threshold) {\n\t\tthis.threshold = threshold;\n\t}\n\n\tpublic int getThresholdMinimal() {\n\t\treturn thresholdMinimal;\n\t}\n\n\tpublic void setThresholdMinimal(int thresholdMinimal) {\n\t\tthis.thresholdMinimal = thresholdMinimal;\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tsuper.initialize();\n\t\teventBus.registerEvent(UserDisconnectedEvent.class,\n\t\t\t\t\t\t\t   \"Fired when too many users disconnected in the same time\", false);\n\t\teventBus.registerAll(this);\n\t}\n\n\t@Override\n\tpublic void setNewConfiguration(Form form) {\n\t\tField thresholdPercent = form.get(\"threshold\");\n\t\tif (thresholdPercent != null) {\n\t\t\tthis.threshold = Integer.parseInt(thresholdPercent.getValue());\n\t\t}\n\n\t\tField thresholdNetto = form.get(\"thresholdMinimal\");\n\t\tif (thresholdNetto != null) {\n\t\t\tthis.thresholdMinimal = Integer.parseInt(thresholdNetto.getValue());\n\t\t}\n\n\t\tsuper.setNewConfiguration(form);\n\t}\n\n\t@Override\n\tprotected void run() {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Running task...\");\n\t\t}\n\n\t\tSessionManager sess = XMPPServer.getComponent(SessionManager.class);\n\n\t\tfinal int currentOnlineUsers = sess.getOpenUsersConnectionsAmount();\n\n\t\tOptional<UserDisconnectedEvent> event = createUserDisconnectedEvent(currentOnlineUsers, lastOnlineUsers,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tthresholdMinimal, threshold);\n\t\tevent.ifPresent(userDisconnectedEvent -> eventBus.fire(userDisconnectedEvent));\n\n\t\tthis.lastOnlineUsers = currentOnlineUsers;\n\t}\n\n\tstatic class UserDisconnectedEvent\n\t\t\textends TasksEvent {\n\n\t\tprivate int disconnections;\n\t\tprivate float disconnectionsPercent;\n\n\t\t/**\n\t\t * Empty constructor to be able to serialize/deserialize event\n\t\t */\n\t\tpublic UserDisconnectedEvent(String name, String description) {\n\t\t\tsuper(name, description);\n\t\t}\n\n\t\tpublic UserDisconnectedEvent(int delta, float percent) {\n\t\t\tsuper(\"UserDisconnectedEvent\", \"Fired when too many users disconnected in the same time\");\n\t\t\tthis.disconnections = -1 * delta;\n\t\t\tthis.disconnectionsPercent = -1 * percent;\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, String> getAdditionalData() {\n\t\t\t// @formatter:off\n\t\t\treturn Map.of(\"disconnections\", \"\" + (disconnections),\n\t\t\t\t\t\t  \"disconnectionsPercent\", \"\" + (disconnectionsPercent)\n\t\t\t);\n\t\t\t// @formatter:onn\n\t\t}\n\n\t\tpublic int getDisconnections() {\n\t\t\treturn disconnections;\n\t\t}\n\n\t\tpublic float getDisconnectionsPercent() {\n\t\t\treturn disconnectionsPercent;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/tasks/CpuTempTask.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor.tasks;\n\nimport tigase.eventbus.EventBus;\nimport tigase.form.Field;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.monitor.MonitorComponent;\nimport tigase.util.datetime.TimestampHelper;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileReader;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Bean(name = \"cpu-temp-task\", parent = MonitorComponent.class, active = true)\npublic class CpuTempTask\n\t\textends AbstractConfigurableTimerTask\n\t\timplements Initializable {\n\n\tpublic static final String CPU_TEMP_MONITOR_EVENT_NAME = \"tigase.monitor.tasks.CPUTempMonitorEvent\";\n\n\tprivate final static TimestampHelper dtf = new TimestampHelper();\n\n\tprivate static final File FREQ_FILE = new File(\"/proc/cpuinfo\");\n\n\tprivate static final Logger log = Logger.getLogger(CpuTempTask.class.getName());\n\n\tprivate static final File TEMP_FILE = new File(\"/proc/acpi/thermal_zone/TZ01/temperature\");\n\n\tprivate static final String THROTT_DIR = \"/proc/acpi/processor/CPU\";\n\tprivate static final String THROTT_FILE = \"/throttling\";\n\tprivate boolean triggered = false;\n\t@Inject\n\tprivate MonitorComponent component;\n\t@ConfigField(desc = \"CPU Temperature threshold\")\n\tprivate int cpuTempThreshold = 90;\n\tprivate float[] cpu_freq = new float[Runtime.getRuntime().availableProcessors()];\n\tprivate int cpu_temp;\n\tprivate int[] cpu_thrott_pr = new int[Runtime.getRuntime().availableProcessors()];\n\tprivate int[] cpu_thrott_st = new int[Runtime.getRuntime().availableProcessors()];\n\t@Inject\n\tprivate EventBus eventBus;\n\n\tpublic CpuTempTask() {\n\t\tsetPeriod(1000 * 10);\n\t}\n\n\tpublic int getCpuTempThreshold() {\n\t\treturn cpuTempThreshold;\n\t}\n\n\tpublic void setCpuTempThreshold(Integer cpuTempThreshold) {\n\t\tthis.cpuTempThreshold = cpuTempThreshold;\n\t}\n\n\t@Override\n\tpublic Form getCurrentConfiguration() {\n\t\tForm x = super.getCurrentConfiguration();\n\t\tx.addField(Field.fieldTextSingle(\"cpuTempThreshold\", \"\" + cpuTempThreshold, \"CPU Temp threshold\"));\n\t\t// x.addField(Field.fieldTextSingle(\"N270#cpuTemp\", \"\" +\n\t\t// cpuTempThreshold, \"CPU Temp threshold\"));\n\t\treturn x;\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tsuper.initialize();\n\t\teventBus.registerEvent(CpuTempEvent.class, \"Fired when CPU temperature is too high\", false);\n\t}\n\n\t@Override\n\tpublic void setNewConfiguration(Form form) {\n\t\tField cpuTempField = form.get(\"cpuTempThreshold\");\n\t\tif (cpuTempField != null) {\n\t\t\tthis.cpuTempThreshold = Integer.parseInt(cpuTempField.getValue());\n\t\t}\n\n\t\tsuper.setNewConfiguration(form);\n\t}\n\n\t@Override\n\tprotected void run() {\n\t\tcheckCPUTemperature();\n\t\t// checkCPUFrequency();\n\t\t// checkCPUThrottling();\n\n\t\tif (cpu_temp >= cpuTempThreshold) {\n\t\t\tfinal CpuTempEvent event = new CpuTempEvent(cpu_temp);\n\n\t\t\tif (!triggered) {\n\t\t\t\teventBus.fire(event);\n\t\t\t\ttriggered = true;\n\t\t\t}\n\n\t\t} else {\n\t\t\ttriggered = false;\n\t\t}\n\t}\n\n\tprivate void checkCPUFrequency() {\n\t\ttry {\n\t\t\tint cpu = 0;\n\t\t\tBufferedReader buffr = new BufferedReader(new FileReader(FREQ_FILE));\n\t\t\tString line = null;\n\t\t\twhile ((line = buffr.readLine()) != null) {\n\t\t\t\tif (line.startsWith(\"cpu MHz\")) {\n\t\t\t\t\tint idx = line.indexOf(':');\n\t\t\t\t\tcpu_freq[cpu++] = Float.parseFloat(line.substring(idx + 1).trim());\n\t\t\t\t}\n\t\t\t}\n\t\t\tbuffr.close();\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.WARNING, \"Can''t read file: \" + FREQ_FILE, ex);\n\t\t}\n\t}\n\n\tprivate void checkCPUTemperature() {\n\t\ttry {\n\t\t\tBufferedReader buffr = new BufferedReader(new FileReader(TEMP_FILE));\n\t\t\tString line = buffr.readLine();\n\t\t\tif (line != null) {\n\t\t\t\tcpu_temp = Integer.parseInt(line.substring(\"temperature:\".length(), line.length() - 1).trim());\n\t\t\t} else {\n\t\t\t\tlog.warning(\"Empty file: \" + TEMP_FILE);\n\t\t\t}\n\t\t\tbuffr.close();\n\t\t} catch (FileNotFoundException ex) {\n\t\t\tlog.log(Level.WARNING, \"File contains temperature doesn't exists. Disabling task cpu-temp-task\");\n\t\t\tsetEnabled(false);\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.WARNING, \"Can''t read file: \" + TEMP_FILE, ex);\n\t\t}\n\t}\n\n\tprivate void checkCPUThrottling() {\n\t\tfor (int i = 0; i < cpu_thrott_st.length; i++) {\n\t\t\ttry {\n\t\t\t\tFile file = new File(THROTT_DIR + i + THROTT_FILE);\n\t\t\t\tBufferedReader buffr = new BufferedReader(new FileReader(file));\n\t\t\t\tString line = null;\n\t\t\t\twhile ((line = buffr.readLine()) != null) {\n\t\t\t\t\tString line_trimmed = line.trim();\n\t\t\t\t\tif (line_trimmed.startsWith(\"*\")) {\n\t\t\t\t\t\tint idx = line_trimmed.indexOf(':');\n\t\t\t\t\t\tcpu_thrott_st[i] = Integer.parseInt(line_trimmed.substring(2, idx));\n\t\t\t\t\t\tString line_pr = line_trimmed.substring(idx + 1, line_trimmed.length() - 1).trim();\n\t\t\t\t\t\tcpu_thrott_pr[i] = Integer.parseInt(line_pr);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbuffr.close();\n\t\t\t} catch (Exception ex) {\n\t\t\t\tlog.log(Level.WARNING, \"Can''t read file: \" + THROTT_DIR + i + THROTT_FILE, ex);\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class CpuTempEvent extends TasksEvent {\n\n\t\tprivate int cpu_temp;\n\n\t\t/**\n\t\t * Empty constructor to be able to serialize/deserialize event\n\t\t */\n\t\tpublic CpuTempEvent(String name, String description) {\n\t\t\tsuper(name, description);\n\t\t}\n\n\t\tpublic CpuTempEvent(int cpu_temp) {\n\t\t\tsuper(\"CpuTempEvent\", \"Fired when CPU temperature is too high\");\n\t\t\tthis.cpu_temp = cpu_temp;\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, String> getAdditionalData() {\n\t\t\t// @formatter:off\n\t\t\treturn Map.of(\"cpuTemp\", \"\" + cpu_temp);\n\t\t\t// @formatter:onn\n\t\t}\n\n\t\tpublic int getCpu_temp() {\n\t\t\treturn cpu_temp;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/tasks/DiskTask.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor.tasks;\n\nimport tigase.eventbus.EventBus;\nimport tigase.form.Field;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.monitor.MonitorComponent;\nimport tigase.util.common.OSUtils;\nimport tigase.util.datetime.TimestampHelper;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileFilter;\nimport java.io.FileReader;\nimport java.util.ArrayList;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Bean(name = \"disk-task\", parent = MonitorComponent.class, active = true)\npublic class DiskTask\n\t\textends AbstractConfigurableTimerTask\n\t\timplements Initializable {\n\n\tpublic static final String DISK_USAGE_MONITOR_EVENT_NAME = \"tigase.monitor.tasks.DiskUsageMonitorEvent\";\n\n\tprotected final static TimestampHelper dtf = new TimestampHelper();\n\n\tprivate static final Logger log = Logger.getLogger(DiskTask.class.getName());\n\t@Inject\n\tprotected MonitorComponent component;\n\t@Inject\n\tprotected EventBus eventBus;\n\t@ConfigField(desc = \"Disk usage threshold\")\n\tprotected float threshold = 0.8F;\n\tprivate File[] roots;\n\tprivate boolean triggered = false;\n\n\tpublic DiskTask() {\n\t\tsetPeriod(1000 * 60);\n\t}\n\n\t@Override\n\tpublic Form getCurrentConfiguration() {\n\t\tForm x = super.getCurrentConfiguration();\n\t\tx.addField(Field.fieldTextSingle(\"threshold\", \"\" + threshold, \"Disk usage ratio threshold\"));\n\t\treturn x;\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tsuper.initialize();\n\t\teventBus.registerEvent(DiskUsageEvent.class, \"Fired if disk usage is too high\", false);\n\t\tfindAllRoots();\n\t}\n\n\t@Override\n\tpublic void setNewConfiguration(Form form) {\n\t\tField diskUsageField = form.get(\"threshold\");\n\t\tif (diskUsageField != null) {\n\t\t\tthis.threshold = Float.parseFloat(diskUsageField.getValue());\n\t\t}\n\n\t\tsuper.setNewConfiguration(form);\n\t}\n\n\tpublic void setThreshold(Float threshold) {\n\t\tthis.threshold = threshold;\n\t}\n\n\t@Override\n\tprotected void run() {\n\t\tfor (File file : roots) {\n\t\t\tif (file.getUsableSpace() < file.getTotalSpace() * (1 - threshold)) {\n\n\t\t\t\tfinal DiskUsageEvent event = new DiskUsageEvent(file.toString(), file.getUsableSpace(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfile.getTotalSpace());\n\n\t\t\t\tif (!triggered) {\n\t\t\t\t\teventBus.fire(event);\n\t\t\t\t\ttriggered = true;\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\ttriggered = false;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void findAllRoots() {\n\t\tswitch (OSUtils.getOSType()) {\n\t\t\tcase windows:\n\t\t\t\tFile[] winRoots = File.listRoots();\n\t\t\t\troots = winRoots;\n\t\t\t\tbreak;\n\t\t\tcase linux:\n\t\t\t\tFile[] linRoots = getLinuxRoots();\n\t\t\t\troots = linRoots;\n\t\t\t\tbreak;\n\t\t\tcase sunos:\n\t\t\tcase solaris:\n\t\t\t\tFile[] solRoots = getSolarisRoots();\n\t\t\t\troots = solRoots;\n\t\t\t\tbreak;\n\t\t\tcase mac:\n\t\t\t\tFile[] macRoots = getMacRoots();\n\t\t\t\troots = macRoots;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tFile[] otherRoots = File.listRoots();\n\t\t\t\tif (otherRoots.length == 1) {\n\t\t\t\t\tFile[] mtabRoots = getLinuxRoots();\n\t\t\t\t\tif (mtabRoots != null && mtabRoots.length > 1) {\n\t\t\t\t\t\totherRoots = mtabRoots;\n\t\t\t\t\t}\n\t\t\t\t\troots = otherRoots;\n\t\t\t\t}\n\t\t}\n\t}\n\n\tprivate File[] getLinuxRoots() {\n\t\ttry {\n\t\t\tString mtab = \"/etc/mtab\";\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Reading mtab: \" + mtab);\n\t\t\t}\n\t\t\tBufferedReader buffr = new BufferedReader(new FileReader(mtab));\n\t\t\tString line = null;\n\t\t\tArrayList<File> results = new ArrayList<File>();\n\t\t\twhile ((line = buffr.readLine()) != null) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"Analyzing line: \" + line);\n\t\t\t\t}\n\t\t\t\tif (line.contains(\"proc\") || line.contains(\"devfs\") || line.contains(\"tmpfs\") ||\n\t\t\t\t\t\tline.contains(\"sysfs\") || line.contains(\"devpts\") || line.contains(\"securityfs\")) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.finest(\"Found virtual fs line, omitting...\");\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"Splitting line...\");\n\t\t\t\t}\n\t\t\t\tString[] parts = line.split(\"\\\\s\");\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"Found file system: \" + parts[1]);\n\t\t\t\t}\n\t\t\t\tresults.add(new File(parts[1]));\n\t\t\t}\n\t\t\treturn results.toArray(new File[results.size()]);\n\t\t} catch (Exception e) {\n\t\t\tlog.warning(\"Can not read filesystems from /etc/mtab file\" + e);\n\t\t\treturn File.listRoots();\n\t\t}\n\n\t}\n\n\tprivate File[] getMacRoots() {\n\t\tFile volumes = new File(\"/Volumes\");\n\t\treturn volumes.listFiles(new FileFilter() {\n\t\t\t@Override\n\t\t\tpublic boolean accept(File path) {\n\t\t\t\treturn path.isDirectory();\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate File[] getSolarisRoots() {\n\t\treturn File.listRoots();\n\t}\n\n\tstatic class DiskUsageEvent\n\t\t\textends TasksEvent {\n\n\t\tString root;\n\t\tlong totalSpace;\n\t\tlong usableSpace;\n\n\t\t/**\n\t\t * Empty constructor to be able to serialize/deserialize event\n\t\t */\n\t\tpublic DiskUsageEvent(String name, String description) {\n\t\t\tsuper(name, description);\n\t\t}\n\n\t\tpublic DiskUsageEvent(String root, long usableSpace, long totalSpace) {\n\t\t\tsuper(\"DiskUsageEvent\", \"Fired if disk usage is too high\");\n\t\t\tthis.root = root;\n\t\t\tthis.usableSpace = usableSpace;\n\t\t\tthis.totalSpace = totalSpace;\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, String> getAdditionalData() {\n\t\t\t// @formatter:off\n\t\t\t\treturn Map.of(\"root\", root,\n\t\t\t\t\"usableSpace\", \"\" + usableSpace,\n\t\t\t\t\"totalSpace\", \"\" + totalSpace);\n\t\t\t// @formatter:onn\n\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/tasks/LoadCheckerTask.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor.tasks;\n\nimport tigase.eventbus.EventBus;\nimport tigase.form.Field;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.monitor.InfoTask;\nimport tigase.monitor.MonitorComponent;\nimport tigase.server.monitor.MonitorRuntime;\nimport tigase.util.datetime.TimestampHelper;\n\nimport java.util.Map;\n\n@Bean(name = \"load-checker-task\", parent = MonitorComponent.class, active = true)\npublic class LoadCheckerTask\n\t\textends AbstractConfigurableTimerTask\n\t\timplements InfoTask, Initializable {\n\n\tpublic static final String MONITOR_EVENT_NAME = \"tigase.monitor.tasks.LoadAverageMonitorEvent\";\n\tprivate final static TimestampHelper dtf = new TimestampHelper();\n\tprivate boolean triggered = false;\n\t@ConfigField(desc = \"Average Load Threshold\")\n\tprivate long averageLoadThreshold = 10;\n\t@Inject\n\tprivate MonitorComponent component;\n\t@Inject\n\tprivate EventBus eventBus;\n\t@Inject\n\tprivate MonitorRuntime runtime;\n\n\tpublic long getAverageLoadThreshold() {\n\t\treturn averageLoadThreshold;\n\t}\n\n\tpublic void setAverageLoadThreshold(Long averageLoadThreshold) {\n\t\tthis.averageLoadThreshold = averageLoadThreshold;\n\t}\n\n\t@Override\n\tpublic Form getCurrentConfiguration() {\n\t\tForm form = super.getCurrentConfiguration();\n\n\t\tform.addField(Field.fieldTextSingle(\"averageLoadThreshold\", String.valueOf(averageLoadThreshold),\n\t\t\t\t\t\t\t\t\t\t\t\"Alarm when AverageLoad is bigger than\"));\n\n\t\treturn form;\n\t}\n\n\t@Override\n\tpublic Form getTaskInfo() {\n\t\tForm result = new Form(\"\", \"Load Information\", \"\");\n\t\tresult.addField(\n\t\t\t\tField.fieldTextSingle(\"averageLoad\", Double.toString(runtime.getLoadAverage()), \"Load Average\"));\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tsuper.initialize();\n\t\teventBus.registerEvent(LoadCheckerTaskEvent.class, \"Fired when load is too high\", false);\n\t}\n\n\t@Override\n\tpublic void setNewConfiguration(Form form) {\n\t\tField tmp = form.get(\"averageLoadThreshold\");\n\t\tif (tmp != null) {\n\t\t\tthis.averageLoadThreshold = Long.parseLong(tmp.getValue());\n\t\t}\n\n\t\tsuper.setNewConfiguration(form);\n\t}\n\n\t@Override\n\tprotected void run() {\n\t\tdouble curAverageLoad = runtime.getLoadAverage();\n\t\tif (curAverageLoad >= averageLoadThreshold) {\n\t\t\tfinal LoadCheckerTaskEvent event = new LoadCheckerTaskEvent(curAverageLoad);\n\t\t\tif (!triggered) {\n\t\t\t\teventBus.fire(event);\n\t\t\t\ttriggered = true;\n\t\t\t}\n\t\t} else {\n\t\t\ttriggered = false;\n\t\t}\n\t}\n\n\tstatic class LoadCheckerTaskEvent\n\t\t\textends TasksEvent {\n\n\t\tdouble averageLoad;\n\n\t\t/**\n\t\t * Empty constructor to be able to serialize/deserialize event\n\t\t */\n\t\tpublic LoadCheckerTaskEvent(String name, String description) {\n\t\t\tsuper(name, description);\n\t\t}\n\n\t\tpublic LoadCheckerTaskEvent(double averageLoad) {\n\t\t\tsuper(\"DiskUsageEvent\", \"Fired when load is too high\");\n\t\t\tthis.averageLoad = averageLoad;\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, String> getAdditionalData() {\n\t\t\t// @formatter:off\n\t\t\treturn Map.of(\"averageLoad\", \"\" + averageLoad);\n\t\t\t// @formatter:onn\n\t\t}\n\n\t\tpublic double getAverageLoad() {\n\t\t\treturn averageLoad;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/tasks/LoggerTask.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor.tasks;\n\nimport tigase.eventbus.EventBus;\nimport tigase.form.Field;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.monitor.MonitorComponent;\nimport tigase.util.StringUtilities;\nimport tigase.util.datetime.TimestampHelper;\nimport tigase.util.log.LogFormatter;\nimport tigase.xml.XMLUtils;\n\nimport java.util.LinkedList;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.*;\n\n@Bean(name = \"logger-task\", parent = MonitorComponent.class, active = true)\npublic class LoggerTask\n\t\textends AbstractConfigurableTask\n\t\timplements Initializable {\n\n\tpublic static final Logger log = Logger.getLogger(LoggerTask.class.getName());\n\tprotected final static TimestampHelper dtf = new TimestampHelper();\n\tprivate static final String LOGGER_MONITOR_EVENT_NAME = \"tigase.monitor.tasks.LoggerMonitorEvent\";\n\t@Inject\n\tprotected MonitorComponent component;\n\t@Inject\n\tprotected EventBus eventBus;\n\tprivate long lastWarningSent = 0;\n\t@ConfigField(desc = \"Log Level Threshold\")\n\tprivate Level levelTreshold = Level.WARNING;\n\tprivate long logWarings = 0;\n\tprivate int loggerSize = 50;\n\t// We only allow 50k of text by default\n\t@ConfigField(desc = \"Log size limit\")\n\tprivate int maxLogBuffer = 1000 * 50;\n\t@ConfigField(desc = \"Minimum notification interval limit\")\n\tprivate final int minimumIntervalInMinutes = 5;\n\tprivate MemoryHandlerFlush memoryHandler = null;\n\tprivate MonitorHandler monitorHandler = null;\n\n\t@Override\n\tpublic Form getCurrentConfiguration() {\n\t\tForm f = super.getCurrentConfiguration();\n\n\t\tField x = Field.fieldListSingle(\"levelTreshold\", levelTreshold.getName(), \"Log level threshold\",\n\t\t\t\t\t\t\t\t\t\tnew String[]{Level.SEVERE.getName(), Level.WARNING.getName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t Level.INFO.getName(), Level.CONFIG.getName(), Level.FINE.getName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t Level.FINER.getName(), Level.FINEST.getName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t Level.ALL.getName()},\n\t\t\t\t\t\t\t\t\t\tnew String[]{Level.SEVERE.getName(), Level.WARNING.getName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t Level.INFO.getName(), Level.CONFIG.getName(), Level.FINE.getName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t Level.FINER.getName(), Level.FINEST.getName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t Level.ALL.getName()});\n\t\tf.addField(x);\n\n\t\treturn f;\n\t}\n\n\tpublic Level getLevelTreshold() {\n\t\treturn levelTreshold;\n\t}\n\n\tpublic void setLevelTreshold(String levelTreshold) {\n\t\tboolean reregister = false;\n\t\tif (levelTreshold != null) {\n\t\t\tLevel v = Level.parse(levelTreshold);\n\t\t\treregister |= !v.equals(this.levelTreshold);\n\t\t\tthis.levelTreshold = v;\n\t\t}\n\n\t\tif (reregister) {\n\t\t\tregisterHandler();\n\t\t}\n\n\t\tlog.log(Level.FINEST, \"Set log level treshold to \" + this.levelTreshold);\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tsuper.initialize();\n\t\teventBus.registerEvent(LOGGER_MONITOR_EVENT_NAME, \"Fired when logger receives with specific level\", false);\n\t}\n\n\tpublic void sendWarningOut(String logBuff) {\n\t\tfinal LoggerTaskEvent event = new LoggerTaskEvent(logBuff);\n\t\teventBus.fire(event);\n\t}\n\n\t@Override\n\tpublic void setNewConfiguration(Form form) {\n\t\tField f = form.get(\"levelTreshold\");\n\t\tif (f != null) {\n\t\t\tsetLevelTreshold(f.getValue());\n\t\t}\n\t\tsuper.setNewConfiguration(form);\n\t}\n\n\t@Override\n\tprotected void disable() {\n\t\tremoveHandler();\n\t\tsuper.disable();\n\t}\n\n\t@Override\n\tprotected void enable() {\n\t\tregisterHandler();\n\t\tsuper.enable();\n\t}\n\n\tprivate void registerHandler() {\n\t\tremoveHandler();\n\t\tif (monitorHandler == null) {\n\t\t\tmonitorHandler = new MonitorHandler();\n\t\t\tmonitorHandler.setLevel(Level.ALL);\n\t\t}\n\t\tmemoryHandler = new MemoryHandlerFlush(monitorHandler, loggerSize, levelTreshold);\n\t\tmemoryHandler.setLevel(Level.ALL);\n\t\tLogger.getLogger(\"\").addHandler(memoryHandler);\n\t}\n\n\tprivate void removeHandler() {\n\t\tif (memoryHandler != null) {\n\t\t\tLogger.getLogger(\"\").removeHandler(memoryHandler);\n\t\t}\n\t}\n\n\tprivate class MemoryHandlerFlush\n\t\t\textends MemoryHandler {\n\n\t\tMonitorHandler monHandle = null;\n\n\t\tpublic MemoryHandlerFlush(MonitorHandler target, int size, Level pushLevel) {\n\t\t\tsuper(target, size, pushLevel);\n\t\t\tmonHandle = target;\n\t\t}\n\n\t\t@Override\n\t\tpublic void push() {\n\t\t\tsuper.push();\n\t\t\tflush();\n\t\t}\n\n\t\tpublic String pushToString() {\n\t\t\tsuper.push();\n\t\t\treturn monHandle.logsToString();\n\t\t}\n\t}\n\n\tprivate class MonitorHandler\n\t\t\textends Handler {\n\n\t\tprivate Formatter formatter = new LogFormatter(false);\n\t\tprivate LinkedList<String> logs = new LinkedList<String>();\n\n\t\t@Override\n\t\tpublic void close() throws SecurityException {\n\t\t}\n\n\t\t@Override\n\t\tpublic synchronized void flush() {\n\t\t\t++logWarings;\n\n\t\t\tif (System.currentTimeMillis() - lastWarningSent > TimeUnit.MINUTES.toMillis(minimumIntervalInMinutes)) {\n\t\t\t\tString logBuff = logsToString();\n\t\t\t\t// We don't want to flood the system with this in case of\n\t\t\t\t// some frequent error....\n\t\t\t\tsendWarningOut(logBuff);\n\t\t\t\tlastWarningSent = System.currentTimeMillis();\n\t\t\t}\n\t\t}\n\n\t\tpublic synchronized String logsToString() {\n\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\tString logEntry = null;\n\t\t\twhile (((logEntry = logs.pollLast()) != null) && (sb.length() < maxLogBuffer)) {\n\t\t\t\tsb.insert(0, logEntry);\n\t\t\t}\n\t\t\tlogs.clear();\n\t\t\tString result = sb.length() <= maxLogBuffer ? sb.toString() : sb.substring(sb.length() - maxLogBuffer);\n\t\t\treturn result;\n\t\t}\n\n\t\t@Override\n\t\tpublic synchronized void publish(LogRecord record) {\n\t\t\tString logEntry = XMLUtils.escape(formatter.format(record));\n\t\t\tString unicodeLiteral = StringUtilities.convertNonPrintableCharactersToLiterals(logEntry, true);\n\t\t\tlogs.add(unicodeLiteral);\n\t\t}\n\t}\n\n\tstatic class LoggerTaskEvent\n\t\t\textends TasksEvent {\n\n\t\tString log;\n\n\t\t/**\n\t\t * Empty constructor to be able to serialize/deserialize event\n\t\t */\n\t\tpublic LoggerTaskEvent(String name, String description) {\n\t\t\tsuper(name, description);\n\t\t}\n\n\t\tpublic LoggerTaskEvent(String log) {\n\t\t\tsuper(\"LoggerEvent\", \"Fired when logger receives with specific level\");\n\t\t\tthis.log = log;\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, String> getAdditionalData() {\n\t\t\t// @formatter:off\n\t\t\treturn Map.of(\"log\", \"\" + log);\n\t\t\t// @formatter:onn\n\t\t}\n\n\t\tpublic String getLog() {\n\t\t\treturn log;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/tasks/MemoryCheckerTask.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor.tasks;\n\nimport tigase.eventbus.EventBus;\nimport tigase.form.Field;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.monitor.InfoTask;\nimport tigase.monitor.MonitorComponent;\nimport tigase.server.monitor.MonitorRuntime;\nimport tigase.util.datetime.TimestampHelper;\nimport tigase.xml.Element;\n\nimport java.util.Date;\nimport java.util.HashSet;\nimport java.util.Map;\n\n@Bean(name = \"memory-checker-task\", parent = MonitorComponent.class, active = true)\npublic class MemoryCheckerTask\n\t\textends AbstractConfigurableTimerTask\n\t\timplements InfoTask, Initializable {\n\n\tpublic final static String HEAP_MEMORY_MONITOR_EVENT_NAME = \"tigase.monitor.tasks.HeapMemoryMonitorEvent\";\n\tpublic final static String NONHEAP_MEMORY_MONITOR_EVENT_NAME = \"tigase.monitor.tasks.NonHeapMemoryMonitorEvent\";\n\tprivate final static TimestampHelper dtf = new TimestampHelper();\n\tprivate final HashSet<String> triggeredEvents = new HashSet<>();\n\t@Inject\n\tprivate MonitorComponent component;\n\t@Inject\n\tprivate EventBus eventBus;\n\t/**\n\t * Percent\n\t */\n\t@ConfigField(desc = \"Max Heap Mem Usage Threshold [%]\")\n\tprivate int maxHeapMemUsagePercentThreshold = 90;\n\t/**\n\t * Percent\n\t */\n\t@ConfigField(desc = \"Max Non-Heap Mem Usage Threshold [%]\")\n\tprivate int maxNonHeapMemUsagePercentThreshold = 90;\n\t@Inject\n\tprivate MonitorRuntime runtime;\n\n\t@Override\n\tpublic Form getCurrentConfiguration() {\n\t\tForm form = super.getCurrentConfiguration();\n\n\t\tform.addField(Field.fieldTextSingle(\"maxHeapMemUsagePercentThreshold\",\n\t\t\t\t\t\t\t\t\t\t\tString.valueOf(maxHeapMemUsagePercentThreshold),\n\t\t\t\t\t\t\t\t\t\t\t\"Alarm when heap mem usage is bigger than [%]\"));\n\n\t\tform.addField(Field.fieldTextSingle(\"maxNonHeapMemUsagePercentThreshold\",\n\t\t\t\t\t\t\t\t\t\t\tString.valueOf(maxNonHeapMemUsagePercentThreshold),\n\t\t\t\t\t\t\t\t\t\t\t\"Alarm when non-heap mem usage is bigger than [%]\"));\n\n\t\treturn form;\n\t}\n\n\tpublic int getMaxHeapMemUsagePercentThreshold() {\n\t\treturn maxHeapMemUsagePercentThreshold;\n\t}\n\n\tpublic void setMaxHeapMemUsagePercentThreshold(Integer maxHeapMemUsagePercentThreshold) {\n\t\tthis.maxHeapMemUsagePercentThreshold = maxHeapMemUsagePercentThreshold;\n\t}\n\n\tpublic int getMaxNonHeapMemUsagePercentThreshold() {\n\t\treturn maxNonHeapMemUsagePercentThreshold;\n\t}\n\n\tpublic void setMaxNonHeapMemUsagePercentThreshold(Integer maxNonHeapMemUsagePercentThreshold) {\n\t\tthis.maxNonHeapMemUsagePercentThreshold = maxNonHeapMemUsagePercentThreshold;\n\t}\n\n\t@Override\n\tpublic Form getTaskInfo() {\n\t\tForm result = new Form(\"\", \"Memory Information\", \"\");\n\t\tresult.addField(Field.fieldTextSingle(\"heapMemMax\", Long.toString(runtime.getHeapMemMax()), \"Heap Memory Max\"));\n\t\tresult.addField(\n\t\t\t\tField.fieldTextSingle(\"heapMemUsed\", Long.toString(runtime.getHeapMemUsed()), \"Heap Memory Used\"));\n\t\tresult.addField(Field.fieldTextSingle(\"heapMemUsedPercentage\", Float.toString(runtime.getHeapMemUsage()),\n\t\t\t\t\t\t\t\t\t\t\t  \"Heap Memory Used [%]\"));\n\t\tresult.addField(Field.fieldTextSingle(\"nonHeapMemMax\", Long.toString(runtime.getNonHeapMemMax()),\n\t\t\t\t\t\t\t\t\t\t\t  \"Non-Heap Memory Max\"));\n\t\tresult.addField(Field.fieldTextSingle(\"nonHeapMemUsed\", Long.toString(runtime.getNonHeapMemUsed()),\n\t\t\t\t\t\t\t\t\t\t\t  \"Non-Heap Memory Used\"));\n\t\tresult.addField(Field.fieldTextSingle(\"nonHeapMemUsedPercentage\", Float.toString(runtime.getNonHeapMemUsage()),\n\t\t\t\t\t\t\t\t\t\t\t  \"Non-Heap Memory Used [%]\"));\n\t\tresult.addField(Field.fieldTextSingle(\"directMemUsed\", Long.toString(runtime.getDirectMemUsed()),\n\t\t\t\t\t\t\t\t\t\t\t  \"Direct Memory Used\"));\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tsuper.initialize();\n\t\teventBus.registerEvent(MemoryCheckerTaskEvent.class, \"Fired when HEAP memory is too low\", false);\n\t}\n\n\t@Override\n\tpublic void setNewConfiguration(Form form) {\n\t\tField heapMemUsage = form.get(\"maxHeapMemUsagePercentThreshold\");\n\t\tif (heapMemUsage != null) {\n\t\t\tthis.maxHeapMemUsagePercentThreshold = Integer.parseInt(heapMemUsage.getValue());\n\t\t}\n\t\tField nonHeapMemUsage = form.get(\"maxNonHeapMemUsagePercentThreshold\");\n\t\tif (nonHeapMemUsage != null) {\n\t\t\tthis.maxNonHeapMemUsagePercentThreshold = Integer.parseInt(nonHeapMemUsage.getValue());\n\t\t}\n\t\tsuper.setNewConfiguration(form);\n\t}\n\n\t@Override\n\tprotected void run() {\n\t\tfloat curHeapMemUsagePercent = runtime.getHeapMemUsage();\n\t\tfloat curNonHeapMemUsagePercent = runtime.getNonHeapMemUsage();\n\t\tif (curHeapMemUsagePercent >= maxHeapMemUsagePercentThreshold) {\n\t\t\tMemoryCheckerTaskEvent event = new MemoryCheckerTaskEvent(HEAP_MEMORY_MONITOR_EVENT_NAME,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"Fired when HEAP memory is too low\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  curHeapMemUsagePercent, curNonHeapMemUsagePercent,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  runtime.getHeapMemMax(), runtime.getHeapMemUsed(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  runtime.getNonHeapMemMax(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  runtime.getNonHeapMemUsed(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  runtime.getDirectMemUsed(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"Heap memory usage is higher than \" + maxHeapMemUsagePercentThreshold +\n\t\t\t\t\t\t\t\t\t\t\t  \t\t\t\t\t\t\t\t \" and it equals \" + curHeapMemUsagePercent);\n\n\t\t\tif (!triggeredEvents.contains(event.getName())) {\n\t\t\t\teventBus.fire(event);\n\t\t\t\ttriggeredEvents.add(event.getName());\n\t\t\t}\n\t\t} else {\n\t\t\ttriggeredEvents.remove(HEAP_MEMORY_MONITOR_EVENT_NAME);\n\t\t}\n\n\t\tif (curNonHeapMemUsagePercent >= maxNonHeapMemUsagePercentThreshold) {\n\t\t\tMemoryCheckerTaskEvent event = new MemoryCheckerTaskEvent(NONHEAP_MEMORY_MONITOR_EVENT_NAME,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"Fired when Non-HEAP memory is too low\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  curHeapMemUsagePercent, curNonHeapMemUsagePercent,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  runtime.getHeapMemMax(), runtime.getHeapMemUsed(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  runtime.getNonHeapMemMax(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  runtime.getNonHeapMemUsed(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  runtime.getDirectMemUsed(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"Heap memory usage is higher than \" + maxNonHeapMemUsagePercentThreshold +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \" and it equals \" + curNonHeapMemUsagePercent);\n\t\t\tif (!triggeredEvents.contains(event.getName())) {\n\t\t\t\teventBus.fire(event);\n\t\t\t\ttriggeredEvents.add(event.getName());\n\t\t\t}\n\t\t} else {\n\t\t\ttriggeredEvents.remove(NONHEAP_MEMORY_MONITOR_EVENT_NAME);\n\t\t}\n\t}\n\tstatic class MemoryCheckerTaskEvent\n\t\t\textends TasksEvent {\n\n\t\tfloat heapMemUsage;\n\t\tfloat nonHeapMemUsage;\n\t\tlong heapMemMax;\n\t\tlong heapMemUsed;\n\t\tlong nonHeapMemMax;\n\t\tlong nonHeapMemUsed;\n\t\tlong directMemUsed;\n\t\tString message;\n\n\t\t/**\n\t\t * Empty constructor to be able to serialize/deserialize event\n\t\t */\n\t\tpublic MemoryCheckerTaskEvent(String name, String description) {\n\t\t\tsuper(name, description);\n\t\t}\n\n\t\tpublic MemoryCheckerTaskEvent(String name, String description, float heapMemUsage, float nonHeapMemUsage,\n\t\t\t\t\t\t\t\t\t  long heapMemMax, long heapMemUsed, long nonHeapMemMax, long nonHeapMemUsed,\n\t\t\t\t\t\t\t\t\t  long directMemUsed, String message) {\n\t\t\tsuper(name, description);\n\t\t\tthis.heapMemUsage = heapMemUsage;\n\t\t\tthis.nonHeapMemUsage = nonHeapMemUsage;\n\t\t\tthis.heapMemMax = heapMemMax;\n\t\t\tthis.heapMemUsed = heapMemUsed;\n\t\t\tthis.nonHeapMemMax = nonHeapMemMax;\n\t\t\tthis.nonHeapMemUsed = nonHeapMemUsed;\n\t\t\tthis.directMemUsed = directMemUsed;\n\t\t\tthis.message = message;\n\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, String> getAdditionalData() {\n\t\t\t// @formatter:off\n\t\t\treturn Map.of(\n\t\t\t\t\"heapMemUsage\", Float.toString(heapMemUsage),\n\t\t\t\t\"nonHeapMemUsage\", Float.toString(nonHeapMemUsage),\n\t\t\t\t\"heapMemMax\", Long.toString(heapMemMax),\n\t\t\t\t\"heapMemUsed\", Long.toString(heapMemUsed),\n\t\t\t\t\"nonHeapMemMax\", Long.toString(nonHeapMemMax),\n\t\t\t\t\"nonHeapMemUsed\", Long.toString(nonHeapMemUsed),\n\t\t\t\t\"directMemUsed\", Long.toString(directMemUsed),\n\t\t\t\t\"message\", message\n\t\t\t);\n\t\t\t// @formatter:onn\n\t\t}\n\n\t\tpublic float getHeapMemUsage() {\n\t\t\treturn heapMemUsage;\n\t\t}\n\n\t\tpublic float getNonHeapMemUsage() {\n\t\t\treturn nonHeapMemUsage;\n\t\t}\n\n\t\tpublic long getHeapMemMax() {\n\t\t\treturn heapMemMax;\n\t\t}\n\n\t\tpublic long getHeapMemUsed() {\n\t\t\treturn heapMemUsed;\n\t\t}\n\n\t\tpublic long getNonHeapMemMax() {\n\t\t\treturn nonHeapMemMax;\n\t\t}\n\n\t\tpublic long getNonHeapMemUsed() {\n\t\t\treturn nonHeapMemUsed;\n\t\t}\n\n\t\tpublic long getDirectMemUsed() {\n\t\t\treturn directMemUsed;\n\t\t}\n\n\t\tpublic String getMessage() {\n\t\t\treturn message;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/tasks/SampleTask.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor.tasks;\n\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.XMLEventBusEvent;\nimport tigase.form.Field;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.monitor.MonitorComponent;\nimport tigase.xml.Element;\n\nimport java.util.Date;\nimport java.util.Map;\n\n@Bean(name = \"sample-task\", parent = MonitorComponent.class, active = true)\npublic class SampleTask\n\t\textends AbstractConfigurableTimerTask\n\t\timplements Initializable {\n\n\t@Inject\n\tprivate EventBus eventBus;\n\tprivate String message = \"<->\";\n\n\t@Override\n\tpublic Form getCurrentConfiguration() {\n\t\tForm x = super.getCurrentConfiguration();\n\t\tx.addField(Field.fieldTextSingle(\"message\", \"\", \"Event message\"));\n\t\treturn x;\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tsuper.initialize();\n\t\teventBus.registerEvent(SampleTaskEvent.class, \"Sample task\", false);\n\t}\n\n\t@Override\n\tpublic void setNewConfiguration(Form form) {\n\t\tField m = form.get(\"message\");\n\t\tif (m == null) {\n\t\t\tthis.message = \"<not found>\";\n\t\t} else {\n\t\t\tthis.message = m.getValue();\n\t\t}\n\n\t\tsuper.setNewConfiguration(form);\n\t}\n\n\t@Override\n\tprotected void enable() {\n\t\tsuper.enable();\n\n\t\tfinal SampleTaskEvent event = new SampleTaskEvent(this.message);\n\t\tthis.message = \"<->\";\n\t\teventBus.fire(event);\n\n\t\tsetEnabled(false);\n\t}\n\n\t@Override\n\tprotected void run() {\n\t}\n\n\tpublic static class SampleTaskEvent\n\t\t\textends TasksEvent {\n\n\t\tString message;\n\n\t\t/**\n\t\t * Empty constructor to be able to serialize/deserialize event\n\t\t */\n\t\tpublic SampleTaskEvent(String name, String description) {\n\t\t\tsuper(name, description);\n\t\t}\n\n\t\tpublic SampleTaskEvent(String message) {\n\t\t\tsuper(\"SampleTaskEvent\", \"Sample task\");\n\t\t\tthis.message = message;\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, String> getAdditionalData() {\n\t\t\t// @formatter:off\n\t\t\treturn Map.of(\"log\", \"\" + message);\n\t\t\t// @formatter:onn\n\t\t}\n\n\t\tpublic String getMessage() {\n\t\t\treturn message;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/tasks/ScriptTask.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor.tasks;\n\nimport tigase.kernel.beans.Inject;\n\nimport javax.script.Bindings;\nimport javax.script.ScriptEngine;\nimport javax.script.ScriptEngineManager;\nimport javax.script.ScriptException;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class ScriptTask\n\t\textends AbstractConfigurableTask {\n\n\tprivate static final Logger log = Logger.getLogger(ScriptTask.class.getName());\n\n\t@Inject\n\tprotected Bindings bindings;\n\t@Inject\n\tprotected ScriptEngineManager scriptEngineManager;\n\tprivate ScriptEngine engine;\n\tprivate String script;\n\tprivate String scriptExtension;\n\n\tpublic Bindings getBindings() {\n\t\treturn bindings;\n\t}\n\n\tpublic void setBindings(Bindings bindings) {\n\t\tthis.bindings = bindings;\n\t}\n\n\tpublic String getScript() {\n\t\treturn script;\n\t}\n\n\tpublic ScriptEngineManager getScriptEngineManager() {\n\t\treturn scriptEngineManager;\n\t}\n\n\tpublic void setScriptEngineManager(ScriptEngineManager scriptEngineManager) {\n\t\tthis.scriptEngineManager = scriptEngineManager;\n\t}\n\n\tpublic String getScriptExtension() {\n\t\treturn scriptExtension;\n\t}\n\n\tpublic void setScript(String script, String scriptExtension) {\n\t\tthis.engine = scriptEngineManager.getEngineByExtension(scriptExtension);\n\t\tthis.script = script;\n\t\tthis.scriptExtension = scriptExtension;\n\t}\n\n\t@Override\n\tprotected void enable() {\n\t\tsuper.enable();\n\n\t\ttry {\n\t\t\tengine.eval(script, bindings);\n\t\t} catch (ScriptException e) {\n\t\t\tlog.log(Level.WARNING, \"Execution failed for the monitoring script: {0}\", new Object[]{getScript()});\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Execution failed for the monitoring script: \" + getScript(), e);\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/tasks/ScriptTimerTask.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor.tasks;\n\nimport tigase.kernel.beans.Inject;\nimport tigase.monitor.ConfigurableTask;\n\nimport javax.script.Bindings;\nimport javax.script.ScriptEngine;\nimport javax.script.ScriptEngineManager;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class ScriptTimerTask\n\t\textends AbstractConfigurableTimerTask\n\t\timplements ConfigurableTask {\n\n\tprivate static final Logger log = Logger.getLogger(ScriptTimerTask.class.getName());\n\n\t@Inject\n\tprotected Bindings bindings;\n\t@Inject\n\tprotected ScriptEngineManager scriptEngineManager;\n\tprivate ScriptEngine engine;\n\tprivate String script;\n\tprivate String scriptExtension;\n\n\tpublic Bindings getBindings() {\n\t\treturn bindings;\n\t}\n\n\tpublic void setBindings(Bindings bindings) {\n\t\tthis.bindings = bindings;\n\t}\n\n\tpublic String getScript() {\n\t\treturn script;\n\t}\n\n\tpublic ScriptEngineManager getScriptEngineManager() {\n\t\treturn scriptEngineManager;\n\t}\n\n\tpublic void setScriptEngineManager(ScriptEngineManager scriptEngineManager) {\n\t\tthis.scriptEngineManager = scriptEngineManager;\n\t}\n\n\tpublic String getScriptExtension() {\n\t\treturn scriptExtension;\n\t}\n\n\tpublic void setScript(String script, String scriptExtension) {\n\t\tthis.engine = scriptEngineManager.getEngineByExtension(scriptExtension);\n\t\tthis.script = script;\n\t\tthis.scriptExtension = scriptExtension;\n\t}\n\n\t@Override\n\tprotected void run() {\n\t\ttry {\n\t\t\tengine.eval(script, bindings);\n\t\t} catch (Throwable e) {\n\t\t\tlog.log(Level.WARNING, \"Error while executing SimpleTimerTask\", e);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/monitor/tasks/TasksEvent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.monitor.tasks;\n\nimport tigase.eventbus.EventBusEvent;\nimport tigase.util.datetime.TimestampHelper;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.util.dns.DNSResolverIfc;\n\nimport java.io.Serializable;\nimport java.util.Date;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\npublic abstract class TasksEvent\n\t\timplements EventBusEvent {\n\n\tprivate final static TimestampHelper dtf = new TimestampHelper();\n\tprivate final DNSResolverIfc dnsResolver = DNSResolverFactory.getInstance();\n\tString description;\n\tString external_hostname;\n\tString hostname;\n\tString name;\n\tString timestamp;\n\n\n\n\tpublic TasksEvent(String name, String description) {\n\t\tObjects.requireNonNull(name);\n\t\tObjects.requireNonNull(description);\n\t\tthis.name = name;\n\t\tthis.description = description;\n\t\tthis.timestamp = \"\" + dtf.format(new Date());\n\t\tthis.hostname = dnsResolver.getDefaultHost();\n\t\tthis.external_hostname = dnsResolver.getSecondaryHost();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tTasksEvent that = (TasksEvent) o;\n\n\t\tif (!name.equals(that.name)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!description.equals(that.description)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!timestamp.equals(that.timestamp)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!hostname.equals(that.hostname)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn external_hostname.equals(that.external_hostname);\n\t}\n\n\tpublic String asString() {\n\t\treturn getAdditionalData().entrySet()\n\t\t\t\t.stream()\n\t\t\t\t.map(entry -> entry.getKey() + \": \" + entry.getValue())\n\t\t\t\t.collect(Collectors.joining(\"\\n\"));\n\t}\n\n\tabstract public Map<String, String> getAdditionalData();\n\n\tpublic String getDescription() {\n\t\treturn description;\n\t}\n\n\tpublic String getExternal_hostname() {\n\t\treturn external_hostname;\n\t}\n\n\tpublic String getHostname() {\n\t\treturn hostname;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic String getTimestamp() {\n\t\treturn timestamp;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = name.hashCode();\n\t\tresult = 31 * result + description.hashCode();\n\t\tresult = 31 * result + timestamp.hashCode();\n\t\tresult = 31 * result + hostname.hashCode();\n\t\tresult = 31 * result + external_hostname.hashCode();\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/net/Accept.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.net;\n\nimport java.io.IOException;\nimport java.nio.channels.SocketChannel;\n\n/**\n * Describe interface Accept here.\n * <br>\n * Created: Sat May 14 07:00:16 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface Accept {\n\n\tvoid accept(final SocketChannel sc) throws IOException;\n\n} // Accept\n"
  },
  {
    "path": "src/main/java/tigase/net/ConnectionOpenListener.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.net;\n\nimport java.net.InetSocketAddress;\nimport java.nio.channels.SocketChannel;\n\n/**\n * Describe interface ConnectionOpenListener here.\n * <br>\n * Created: Thu Jan 26 00:00:39 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface ConnectionOpenListener {\n\n\t/**\n\t * <code>RECEIVE_BUFFER_SIZE</code> defines a size for TCP/IP packets. XMPP data packets are quite small usually,\n\t * below 1kB so we don't need big TCP/IP data buffers.\n\t */\n\tpublic static final int DEF_RECEIVE_BUFFER_SIZE = 2 * 1024;\n\n\tpublic static final int IPTOS_LOWCOST = 0x02;\n\n\tpublic static final int IPTOS_LOWDELAY = 0x10;\n\n\tpublic static final int IPTOS_RELIABILITY = 0x04;\n\n\tpublic static final int IPTOS_THROUGHPUT = 0x08;\n\n\tpublic static final int DEF_TRAFFIC_CLASS = IPTOS_LOWCOST;\n\n\tvoid accept(SocketChannel sc);\n\n\tint getPort();\n\n\tString[] getIfcs();\n\n\tString getSRVType();\n\n\tString getRemoteHostname();\n\n\tInetSocketAddress getRemoteAddress();\n\n\tConnectionType getConnectionType();\n\n\tSocketType getSocketType();\n\n\tint getReceiveBufferSize();\n\n\tint getTrafficClass();\n\n\tdefault long getNewConnectionsThrottling() {\n\t\treturn ConnectionOpenThread.def_5222_throttling;\n\t}\n\n\tdefault void release() {}\n}    // ConnectionOpenListener\n\n"
  },
  {
    "path": "src/main/java/tigase/net/ConnectionOpenThread.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.net;\n\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.net.NoRouteToHostException;\nimport java.net.SocketException;\nimport java.nio.channels.*;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentLinkedQueue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Describe class ConnectionOpenThread here.\n * <br>\n * Created: Wed Jan 25 23:51:28 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class ConnectionOpenThread\n\t\timplements Runnable {\n\n\tpublic static final long def_5222_throttling = 200;\n\n\tpublic static final long def_5223_throttling = 50;\n\n\tpublic static final long def_5269_throttling = 100;\n\n\tpublic static final long def_5280_throttling = 1000;\n\tprivate static final Logger log = Logger.getLogger(ConnectionOpenThread.class.getName());\n\tpublic static Map<Integer, PortThrottlingData> throttling = new ConcurrentHashMap<Integer, PortThrottlingData>(10);\n\tprivate static ConnectionOpenThread acceptThread = null;\n\n\tprotected long accept_counter = 0;\n\tprivate Selector selector = null;\n\tprivate boolean stopping = false;\n\tprivate Timer timer = null;\n\tprivate ConcurrentLinkedQueue<Task> waiting = new ConcurrentLinkedQueue<>();\n\n\tpublic static ConnectionOpenThread getInstance() {\n\n\t\t// Long new_throttling = Long.getLong(\"new-connections-throttling\");\n//  if (new_throttling != null) {\n//    throttling = new_throttling;\n//    log.log(Level.WARNING, \"New connections throttling set to: {0}\", throttling);\n//  }\n\t\tif (acceptThread == null) {\n\t\t\tacceptThread = new ConnectionOpenThread();\n\n\t\t\tThread thrd = new Thread(acceptThread);\n\n\t\t\tthrd.setName(\"ConnectionOpenThread\");\n\t\t\tthrd.start();\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.finer(\"ConnectionOpenThread started.\");\n\t\t\t}\n\t\t}    // end of if (acceptThread == null)\n\n\t\treturn acceptThread;\n\t}\n\n\tprivate ConnectionOpenThread() {\n\t\ttimer = new Timer(\"Connections open timer\", true);\n\t\ttimer.scheduleAtFixedRate(new TimerTask() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tfor (PortThrottlingData portData : throttling.values()) {\n\t\t\t\t\tportData.lastSecondConnections = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}, 1000, 1000);\n\t\ttry {\n\t\t\tselector = Selector.open();\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.SEVERE, \"Server I/O error, can''t continue my work.\", e);\n\t\t\tstopping = true;\n\t\t}    // end of try-catch\n\t}\n\n\tpublic void addConnectionOpenListener(ConnectionOpenListener al) {\n\t\twaiting.offer(new Task(al, Task.Action.Add));\n\t\tselector.wakeup();\n\t}\n\n\tpublic void removeConnectionOpenListener(ConnectionOpenListener al) {\n\t\twaiting.offer(new Task(al, Task.Action.Remove));\n\t\tselector.wakeup();\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\twhile (!stopping) {\n\t\t\ttry {\n\t\t\t\tint select = selector.select();\n\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Selected: \" + select + \" from selector: \" + selector);\n\t\t\t\t}\n\n\t\t\t\tfor (Iterator i = selector.selectedKeys().iterator(); i.hasNext(); ) {\n\t\t\t\t\tSelectionKey sk = (SelectionKey) i.next();\n\n\t\t\t\t\ti.remove();\n\n\t\t\t\t\tSocketChannel sc = null;\n\t\t\t\t\tboolean throttled = false;\n\t\t\t\t\tint port_no = 0;\n\n\t\t\t\t\tif ((sk.readyOps() & SelectionKey.OP_ACCEPT) != 0) {\n\t\t\t\t\t\tServerSocketChannel nextReady = (ServerSocketChannel) sk.channel();\n\n\t\t\t\t\t\tport_no = nextReady.socket().getLocalPort();\n\t\t\t\t\t\tsc = nextReady.accept();\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.finest(\"OP_ACCEPT\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tPortThrottlingData port_throttling = throttling.get(port_no);\n\n\t\t\t\t\t\tif (port_throttling != null) {\n\t\t\t\t\t\t\t++port_throttling.lastSecondConnections;\n\t\t\t\t\t\t\tif (port_throttling.lastSecondConnections > port_throttling.throttling) {\n\t\t\t\t\t\t\t\tif (log.isLoggable(Level.INFO)) {\n\t\t\t\t\t\t\t\t\tlog.log(Level.INFO, \"New connections throttling level {0} exceeded limit of {1}, closing: {2}\",\n\t\t\t\t\t\t\t\t\t\t\tnew Object[]{port_throttling.lastSecondConnections, port_throttling.throttling, sc});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tsc.close();\n\t\t\t\t\t\t\t\tsc = null;\n\t\t\t\t\t\t\t\tthrottled = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t// Hm, this should not happen actually\n\t\t\t\t\t\t\tlog.log(Level.WARNING, \"Throttling not configured for port: {0}\", port_no);\n\t\t\t\t\t\t}\n\t\t\t\t\t}    // end of if (sk.readyOps() & SelectionKey.OP_ACCEPT)\n\t\t\t\t\tif ((sk.readyOps() & SelectionKey.OP_CONNECT) != 0) {\n\t\t\t\t\t\tsk.cancel();\n\t\t\t\t\t\tsc = (SocketChannel) sk.channel();\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.finest(\"OP_CONNECT\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}    // end of if (sk.readyOps() & SelectionKey.OP_ACCEPT)\n\t\t\t\t\tif (sc != null) {\n\n\t\t\t\t\t\t// We have to catch exception here as sometimes socket is closed\n\t\t\t\t\t\t// or connection is broken before we start configuring it here\n\t\t\t\t\t\t// then whatever we do on the socket it throws an exception\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tsc.configureBlocking(false);\n\t\t\t\t\t\t\tsc.socket().setSoLinger(false, 0);\n\t\t\t\t\t\t\tsc.socket().setReuseAddress(true);\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINER, \"Registered new client socket: {0}\", sc);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tConnectionOpenListener al = (ConnectionOpenListener) sk.attachment();\n\n\t\t\t\t\t\t\tsc.socket().setTrafficClass(al.getTrafficClass());\n\t\t\t\t\t\t\tsc.socket().setReceiveBufferSize(al.getReceiveBufferSize());\n\t\t\t\t\t\t\tal.accept(sc);\n\t\t\t\t\t\t} catch (java.net.SocketException e) {\n\t\t\t\t\t\t\tlog.log(Level.CONFIG, \"Socket closed instantly after it had been opened?\", e);\n\n\t\t\t\t\t\t\tConnectionOpenListener al = (ConnectionOpenListener) sk.attachment();\n\n\t\t\t\t\t\t\tal.accept(sc);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlog.log(Level.CONFIG,\n\t\t\t\t\t\t\t\t\"Can not obtain socket channel from selection key, throttling activated = {0}, for port: {1}\",\n\t\t\t\t\t\t\t\tnew Object[]{throttled, port_no});\n\t\t\t\t\t}    // end of if (sc != null) else\n\t\t\t\t\t++accept_counter;\n\t\t\t\t}\n\t\t\t\tprocessWaiting();\n\t\t\t} catch (IOException e) {\n\t\t\t\tlog.log(Level.SEVERE, \"Server I/O error.\", e);\n\n\t\t\t\t// stopping = true;\n\t\t\t}        // end of catch\n\t\t\tcatch (Exception e) {\n\t\t\t\tlog.log(Level.SEVERE, \"Other service exception.\", e);\n\n\t\t\t\t// stopping = true;\n\t\t\t}        // end of catch\n\t\t}\n\t}\n\n\tpublic void start() {\n\t\tThread t = new Thread(this);\n\n\t\tt.setName(\"ConnectionOpenThread\");\n\t\tt.start();\n\t}\n\n\tpublic void stop() {\n\t\tstopping = true;\n\t\tselector.wakeup();\n\t}\n\n\tprivate void processWaiting() throws IOException {\n\t\tTask task = null;\n\n\t\twhile ((task = waiting.poll()) != null) {\n\t\t\tConnectionOpenListener al = task.openListener;\n\n\t\t\tswitch (task.action) {\n\t\t\t\tcase Add:\n\t\t\t\t\ttry {\n\t\t\t\t\t\taddPort(al);\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tif (((e instanceof SocketException && e.getMessage() != null &&\n\t\t\t\t\t\t\t\te.getMessage().contains(\"Network is unreachable\")) ||\n\t\t\t\t\t\t\t\t(e instanceof NoRouteToHostException && e.getMessage() != null &&\n\t\t\t\t\t\t\t\t\t\te.getMessage().equals(\"No route to host\"))) &&\n\t\t\t\t\t\t\t\tal.getConnectionType() == ConnectionType.connect && al.getIfcs() != null &&\n\t\t\t\t\t\t\t\tArrays.stream(al.getIfcs()).filter(ifc -> ifc.contains(\":\")).findFirst().isPresent()) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Error: creating IPv6 connection (\" + e + \") for: \" + al);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlog.log(Level.WARNING, \"Error: creating connection for: \" + al, e);\n\n\t\t\t\t\t\t\t// check for existing bindings\n\t\t\t\t\t\t\tfor (SelectionKey key : selector.keys()) {\n\t\t\t\t\t\t\t\tConnectionOpenListener al1 = (ConnectionOpenListener) key.attachment();\n\t\t\t\t\t\t\t\tif (al != null) {\n\t\t\t\t\t\t\t\t\tif (al.getPort() == al1.getPort()) {\n\t\t\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"port \" + al.getPort() + \" still bound!!\");\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tal.accept(null);\n\t\t\t\t\t}    // end of try-catch\n\t\t\t\t\tbreak;\n\t\t\t\tcase Remove:\n\t\t\t\t\tfor (SelectionKey key : selector.keys()) {\n\n\t\t\t\t\t\tif (al == key.attachment()) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tSelectableChannel channel = key.channel();\n\t\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"removing binding for port:\" + al.getPort());\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tchannel.close();\n\t\t\t\t\t\t\t\tkey.cancel();\n\t\t\t\t\t\t\t\tselector.selectNow();\n\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\tlog.log(Level.WARNING, \"Exception during removing connection listener.\", e);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\t\n\tprivate void addISA(InetSocketAddress isa, ConnectionOpenListener al) throws IOException {\n\t\tswitch (al.getConnectionType()) {\n\t\t\tcase accept:\n\t\t\t\tlong port_throttling = al.getNewConnectionsThrottling();\n\n\t\t\t\tthrottling.put(isa.getPort(), new PortThrottlingData(port_throttling));\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\"Setting up throttling for the port {0} to {1} connections per second. isa: {2}\",\n\t\t\t\t\t\t\tnew Object[]{isa.getPort(), port_throttling, isa});\n\t\t\t\t}\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"Setting up 'accept' channel...\");\n\t\t\t\t}\n\n\t\t\t\tServerSocketChannel ssc = ServerSocketChannel.open();\n\n\t\t\t\tssc.socket().setReceiveBufferSize(al.getReceiveBufferSize());\n\t\t\t\tssc.configureBlocking(false);\n\t\t\t\tssc.bind(isa, (int) (port_throttling));\n\t\t\t\tssc.register(selector, SelectionKey.OP_ACCEPT, al);\n\n\t\t\t\tbreak;\n\n\t\t\tcase connect:\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Setting up ''connect'' channel for: {0}/{1}\",\n\t\t\t\t\t\t\tnew Object[]{isa.getAddress(), isa.getPort()});\n\t\t\t\t}\n\n\t\t\t\tSocketChannel sc = SocketChannel.open();\n\n\t\t\t\tsc.socket().setReceiveBufferSize(al.getReceiveBufferSize());\n\t\t\t\tsc.socket().setTrafficClass(al.getTrafficClass());\n\t\t\t\tsc.configureBlocking(false);\n\t\t\t\tsc.connect(isa);\n\t\t\t\tsc.register(selector, SelectionKey.OP_CONNECT, al);\n\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tlog.log(Level.WARNING, \"Unknown connection type: {0}\", al.getConnectionType());\n\n\t\t\t\tbreak;\n\t\t}    // end of switch (al.getConnectionType())\n\t}\n\n\tprivate void addPort(ConnectionOpenListener al) throws IOException {\n\t\tif ((al.getConnectionType() == ConnectionType.connect) && (al.getRemoteAddress() != null)) {\n\t\t\taddISA(al.getRemoteAddress(), al);\n\t\t} else if ((al.getIfcs() == null) || (al.getIfcs().length == 0) || al.getIfcs()[0].equals(\"ifc\") ||\n\t\t\t\tal.getIfcs()[0].equals(\"*\")) {\n\t\t\taddISA(new InetSocketAddress(al.getPort()), al);\n\t\t} else {\n\t\t\tfor (String ifc : al.getIfcs()) {\n\t\t\t\taddISA(new InetSocketAddress(ifc, al.getPort()), al);\n\t\t\t}    // end of for ()\n\t\t}      // end of if (ip == null || ip.equals(\"\")) else\n\t}\n\n\tprivate static class Task {\n\n\t\tprivate final ConnectionOpenListener openListener;\n\t\tprivate final Action action;\n\n\t\tprivate Task(ConnectionOpenListener openListener, Action action) {\n\t\t\tthis.action = action;\n\t\t\tthis.openListener = openListener;\n\t\t}\n\n\t\tprivate enum Action {\n\t\t\tAdd,\n\t\t\tRemove\n\t\t}\n\t}\n\n\tprivate class PortThrottlingData {\n\n\t\t\tprotected long lastSecondConnections = 0;\n\n\t\t\tprotected long throttling;\n\n\n\t\tprivate PortThrottlingData(long throttling_prop) {\n\t\t\tthrottling = throttling_prop;\n\t\t}\n\t}\n}    // ConnectionOpenThread\n\n"
  },
  {
    "path": "src/main/java/tigase/net/ConnectionType.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.net;\n\nimport java.util.Arrays;\n\n/**\n * Describe class ConnectionType here.\n * <br>\n * Created: Sun Feb  5 09:26:44 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic enum ConnectionType {\n\n\taccept,\n\tconnect;\n\n\tpublic static String[] names() {\n\t\tString[] names = new String[values().length];\n\t\tConnectionType[] values = values();\n\t\tArrays.setAll(names, i -> values[i].toString());\n\t\treturn names;\n\t}\n\n\tpublic String toStringPretty() {\n\t\tswitch (this) {\n\t\t\tcase accept:\n\t\t\t\treturn \"<- incoming (\" + name() + \")\";\n\t\t\tcase connect:\n\t\t\t\treturn \"-> outgoing (\" + name() + \")\";\n\t\t\tdefault:\n\t\t\t\treturn name();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/net/IOService.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.net;\n\nimport org.jspecify.annotations.Nullable;\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.cert.CertCheckResult;\nimport tigase.cert.CertificateUtil;\nimport tigase.io.*;\nimport tigase.stats.StatisticsList;\nimport tigase.util.IOListener;\nimport tigase.xmpp.jid.JID;\n\nimport javax.net.ssl.SSLHandshakeException;\nimport javax.net.ssl.SSLPeerUnverifiedException;\nimport javax.net.ssl.SSLProtocolException;\nimport javax.net.ssl.TrustManager;\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.net.Socket;\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.CharBuffer;\nimport java.nio.channels.SocketChannel;\nimport java.nio.charset.*;\nimport java.security.KeyStore;\nimport java.security.cert.Certificate;\nimport java.security.cert.X509Certificate;\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.concurrent.locks.ReentrantLock;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.cert.CertificateUtil.validateCertificate;\nimport static tigase.server.ConnectionManager.PORT_PROXY_PROTOCOL_PROP_KEY;\n\n/**\n * <code>IOService</code> offers thread safe <code>call()</code> method execution, however you must be prepared that\n * other methods can be called simultaneously like <code>stop()</code>, <code>getProtocol()</code> or\n * <code>isConnected()</code>. <br> It is recommended that developers extend <code>AbsractServerService</code> rather\n * then implement <code>ServerService</code> interface directly. <p> If you directly implement\n * <code>ServerService</code> interface you must take care about <code>SocketChannel</code> I/O, queuing tasks,\n * processing results and thread safe execution of <code>call()</code> method. If you however extend\n * <code>IOService</code> class all this basic operation are implemented and you have only to take care about parsing\n * data received from network socket. Parsing data is expected to be implemented in <code>parseData(char[] data)</code>\n * method. </p>\n * <br>\n * <p> Created: Tue Sep 28 23:00:34 2004 </p>\n *\n * @param <RefObject> is a reference object stored by this service. This is e reference to higher level data object\n * keeping more information about the connection.\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\npublic abstract class IOService<RefObject>\n\t\timplements Callable<IOService<?>>, TLSEventHandler, IOListener {\n\n\tpublic static final String CERT_CHECK_RESULT = \"cert-check-result\";\n\n\tpublic static final String LOCAL_CERT_CHECK_RESULT = \"local-cert-check-result\";\n\n\tpublic static final String CERT_REQUIRED_DOMAIN = \"cert-required-domain\";\n\n\tpublic static final String HOSTNAME_KEY = \"hostname-key\";\n\n\tpublic static final String PORT_TYPE_PROP_KEY = \"type\";\n\n\t/**\n\t * This is key used to store session ID in temporary session data storage. As it is used by many components it is\n\t * required that all components access session ID with this constant.\n\t */\n\tpublic static final String SESSION_ID_KEY = \"sessionID\";\n\n\tpublic static final String SSL_PROTOCOLS_KEY = \"ssl-protocols\";\n\n\tprivate static final Logger log = Logger.getLogger(IOService.class.getName());\n\tprivate static final long MAX_ALLOWED_EMPTY_CALLS = 1000;\n\n\tprivate final ReentrantLock readInProgress = new ReentrantLock();\n\tprivate final ReentrantLock writeInProgress = new ReentrantLock();\n\tprotected CharBuffer cb = CharBuffer.allocate(2048);\n\tprotected CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();\n\tprotected CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();\n\t/**\n\t * The saved partial bytes for multi-byte UTF-8 characters between reads\n\t */\n\tprotected byte[] partialCharacterBytes = null;\n\tprivate int bufferLimit = 0;\n\tprivate CertificateContainerIfc certificateContainer;\n\n\t/**\n\t * Intended for low-level logging to contain user connection ID to easier track particular user connection and troubleshoot issues.\n\t */\n\tprivate JID connectionId = null;\n\t@Nullable\n\tprivate ConnectionType connectionType = null;\n\tprivate JID dataReceiver = null;\n\tprivate long empty_read_call_count = 0;\n\tprivate String id = null;\n\t/**\n\t * This variable keeps the time of last transfer in any direction it is used to help detect dead connections.\n\t */\n\tprivate long lastTransferTime = 0;\n\tprivate Certificate localCertificate;\n\tprivate String local_address = null;\n\tprivate Certificate peerCertificate;\n\tprivate long[] rdData = new long[60];\n\tprivate RefObject refObject = null;\n\n\t// properties from block below should not be used without proper knowledge\n\t// ----- BEGIN ---------------------------------------------------------------\n\tprivate String remote_address = null;\n\tprivate IOServiceListener<IOService<RefObject>> serviceListener = null;\n\tprivate ConcurrentMap<String, Object> sessionData = new ConcurrentHashMap<String, Object>(4, 0.75f, 4);\n\tprivate IOInterface socketIO = null;\n\t/**\n\t * <code>socketInput</code> buffer keeps data read from socket.\n\t */\n\tprivate ByteBuffer socketInput = null;\n\tprivate int socketInputSize = 2048;\n\tprivate boolean socketServiceReady = false;\n\tprivate SSLContextContainerIfc sslContextContainer;\n\tprivate boolean stopping = false;\n\tprivate byte[] tlsUniqueId;\n\tprivate byte[] tlsExporter;\n\tprivate long[] wrData = new long[60];\n\n\tprivate TrustManager[] x509TrustManagers;\n\n\tprivate static String getRemoteHostname(IOService ios) {\n\t\tString tls_hostname = (String) ios.getSessionData().get(HOSTNAME_KEY);\n\t\tString tls_remote_hostname = (String) ios.getSessionData().get(\"remote-host\");\n\t\tif (tls_remote_hostname == null) {\n\t\t\ttls_remote_hostname = (String) ios.getSessionData().get(\"remote-hostname\");\n\t\t\tif (tls_remote_hostname == null) {\n\t\t\t\ttls_remote_hostname = tls_hostname;\n\t\t\t}\n\t\t}\n\t\treturn tls_remote_hostname;\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.3.0\", removeIn = \"9.0.0\", note = \"Please use version with 'socketInputSize' parameter\")\n\tpublic void accept(final SocketChannel socketChannel) throws IOException {\n\t\taccept(socketChannel, socketChannel.socket().getReceiveBufferSize());\n\t}\n\n\tpublic void accept(final SocketChannel socketChannel, Integer socketInputSize) throws IOException {\n\t\ttry {\n\t\t\tif (socketChannel.isConnectionPending()) {\n\t\t\t\tsocketChannel.finishConnect();\n\t\t\t}    // end of if (socketChannel.isConnecyionPending())\n\t\t\tsocketIO = new SocketIO(socketChannel);\n\t\t\tif (connectionType == ConnectionType.accept && sessionData != null &&\n\t\t\t\t\t(Boolean) sessionData.getOrDefault(PORT_PROXY_PROTOCOL_PROP_KEY, false)) {\n\t\t\t\tsocketIO = new ProxyIO(socketIO, (local, remote) -> {\n\t\t\t\t\tif (local != null) {\n\t\t\t\t\t\tlocal_address = local;\n\t\t\t\t\t}\n\t\t\t\t\tif (remote != null) {\n\t\t\t\t\t\tremote_address = remote;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (IOException e) {\n\t\t\tString host = (String) sessionData.get(\"remote-hostname\");\n\n\t\t\tif (host == null) {\n\t\t\t\thost = (String) sessionData.get(\"remote-host\");\n\t\t\t}\n\n\t\t\tString sock_str = null;\n\n\t\t\ttry {\n\t\t\t\tsock_str = socketChannel.socket().toString();\n\t\t\t} catch (Exception ex) {\n\t\t\t\tsock_str = ex.toString();\n\t\t\t}\n\t\t\tlog.log(Level.FINER,\n\t\t\t\t\t\"Problem connecting to remote host: {0}, address: {1}, socket: {2} - exception: {3}, session data: {4}\",\n\t\t\t\t\tnew Object[]{host, remote_address, sock_str, e, sessionData});\n\n\t\t\tthrow e;\n\t\t}\n\t\tthis.socketInputSize = socketInputSize;\n\t\tsocketInput = ByteBuffer.allocate(socketInputSize);\n\t\tsocketInput.order(byteOrder());\n\n\t\tSocket sock = socketIO.getSocketChannel().socket();\n\n\t\tlocal_address = sock.getLocalAddress().getHostAddress();\n\t\tremote_address = sock.getInetAddress().getHostAddress();\n\t\tid = local_address + \"_\" + sock.getLocalPort() + \"_\" + remote_address + \"_\" + sock.getPort();\n\t\tsetLastTransferTime();\n\t}\n\n\t@Override\n\tpublic IOService<?> call() throws IOException {\n\t\twriteData(null);\n\n\t\tboolean readLock = true;\n\n\t\tif (stopping) {\n\t\t\tstop();\n\t\t} else {\n\t\t\treadLock = readInProgress.tryLock();\n\t\t\tif (readLock) {\n\t\t\t\ttry {\n\t\t\t\t\tprocessSocketData();\n\t\t\t\t\tif ((receivedPackets() > 0) && (serviceListener != null)) {\n\t\t\t\t\t\tserviceListener.packetsReady(this);\n\t\t\t\t\t}    // end of if (receivedPackets.size() > 0)\n\t\t\t\t} finally {\n\t\t\t\t\treadInProgress.unlock();\n\t\t\t\t\tif (!isConnected()) {\n\t\t\t\t\t\t// added to sooner detect disconnection of peer - ie. client\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\t\t\"Stopping connection due to the fact that it was disconnected, forceStop() [{0}]\",\n\t\t\t\t\t\t\t\t\ttoString());\n\t\t\t\t\t\t}\n\t\t\t\t\t\tforceStop();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn readLock && socketServiceReady ? this : null;\n\t}\n\n\t@Override\n\tpublic boolean checkBufferLimit(int bufferSize) {\n\t\treturn (bufferLimit == 0 || bufferSize <= bufferLimit);\n\t}\n\n\tpublic ConnectionType connectionType() {\n\t\treturn this.connectionType;\n\t}\n\n\tpublic void forceStop() {\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.log(Level.FINER, \"Force stop called... Socket: {0}, \", socketIO);\n\t\t}\n\t\ttry {\n\t\t\tif ((socketIO != null) && socketIO.isConnected()) {\n\t\t\t\tsynchronized (socketIO) {\n\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\tlog.log(Level.FINER, \"Calling stop on: {0}\", socketIO);\n\t\t\t\t\t}\n\t\t\t\t\tsocketIO.stop();\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\n\t\t\t// Well, do nothing, we are closing the connection anyway....\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Socket: \" + socketIO + \", Exception while stopping service: \" + connectionId, e);\n\t\t\t}\n\t\t} finally {\n\t\t\tif (serviceListener != null) {\n\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\tlog.log(Level.FINER, \"Calling stop on the listener: {0}\", serviceListener);\n\t\t\t\t}\n\n\t\t\t\tIOServiceListener<IOService<RefObject>> tmp = serviceListener;\n\n\t\t\t\tserviceListener = null;\n\n\t\t\t\t// The temp can still be null if the forceStop is called concurrently\n\t\t\t\tif (tmp != null) {\n\t\t\t\t\ttmp.serviceStopped(this);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\tlog.log(Level.FINER, \"Service listener is null: {0}\", socketIO);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void handshakeCompleted(TLSWrapper wrapper) {\n\t\tString reqCertDomain = (String) getSessionData().get(CERT_REQUIRED_DOMAIN);\n\t\tCertCheckResult certCheckResult = wrapper.getCertificateStatus(false, sslContextContainer);\n\t\tif (reqCertDomain != null) {\n\t\t\t// if reqCertDomain is set then verify if certificate got from server\n\t\t\t// is allowed for reqCertDomain\n\t\t\ttry {\n\t\t\t\tCertificate[] certs = wrapper.getPeerCertificates();\n\t\t\t\tif (certs != null && certs.length > 0) {\n\t\t\t\t\tCertificate peerCert = certs[0];\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\t\"TLS handshake verifying if certificate from connection matches domain {1} [{0}]\",\n\t\t\t\t\t\t\t\tnew Object[]{this, reqCertDomain});\n\t\t\t\t\t}\n\t\t\t\t\tif (!CertificateUtil.verifyCertificateForDomain((X509Certificate) peerCert, reqCertDomain)) {\n\t\t\t\t\t\tcertCheckResult = CertCheckResult.invalid;\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"TLS handshake: certificate doesn't match domain) [{0}]\",\n\t\t\t\t\t\t\t\t\tnew Object[]{this});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tcertCheckResult = CertCheckResult.invalid;\n\t\t\t\tif (log.isLoggable(Level.CONFIG)) {\n\t\t\t\t\tlog.log(Level.CONFIG,\n\t\t\t\t\t\t\t\"Certificate validation failed, CertCheckResult: \" + certCheckResult + \") [\" + this + \"]\", e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tsessionData.put(CERT_CHECK_RESULT, certCheckResult);\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"TLS handshake completed: {1} [{0}]\", new Object[]{this, certCheckResult});\n\t\t}\n\n\t\tif (!wrapper.isClientMode()) {\n\t\t\tthis.tlsUniqueId = wrapper.getTlsUniqueBindingData();\n\t\t}\n\t\tif (!wrapper.isClientMode()) {\n\t\t\tthis.tlsExporter = wrapper.getTlsExporterBindingData();\n\t\t}\n\n\t\t// we want to have local SSL certificate validated all the time, ie. for outgoing S2S connection\n\t\ttry {\n\t\t\tCertificate[] certs = wrapper.getLocalCertificates();\n\t\t\tthis.localCertificate = certs == null || certs.length == 0 ? null : certs[0];\n\t\t\tif (certs != null) {\n\t\t\t\tKeyStore trustStore = sslContextContainer.getTrustStore();\n\t\t\t\tCertCheckResult checkResult = validateCertificate(certs, trustStore, false);\n\t\t\t\tsessionData.put(LOCAL_CERT_CHECK_RESULT, checkResult);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthis.localCertificate = null;\n\t\t\tlog.log(Level.WARNING, \"Cannot get local certificate\", e);\n\t\t}\n\t\tif (!wrapper.isClientMode() && (wrapper.wantClientAuth() || wrapper.isNeedClientAuth())) {\n\t\t\ttry {\n\t\t\t\tCertificate[] certs = wrapper.getPeerCertificates();\n\t\t\t\tthis.peerCertificate = certs[0];\n\n\t\t\t} catch (SSLPeerUnverifiedException e) {\n\t\t\t\tthis.peerCertificate = null;\n\t\t\t} catch (Exception e) {\n\t\t\t\tthis.peerCertificate = null;\n\t\t\t\tlog.log(Level.WARNING, \"Problem with extracting subjectAltName\", e);\n\t\t\t}\n\t\t}\n\t\tserviceListener.tlsHandshakeCompleted(this);\n\t}\n\n\tpublic abstract void processWaitingPackets() throws IOException;\n\n\tpublic void startSSL(boolean clientMode, boolean wantClientAuth, boolean needClientAuth) throws IOException {\n\t\tif (socketIO instanceof TLSIO) {\n\t\t\tthrow new IllegalStateException(\"SSL mode is already activated.\");\n\t\t}\n\t\tif (sslContextContainer == null) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\"SSL cannot be activated - sslContextContainer is not set for \" + connectionId);\n\t\t}\n\n\t\tString tls_hostname = null;\n\t\tint port = 0;\n\t\tString tls_remote_hostname = null;\n\t\tif (clientMode) {\n\t\t\ttls_remote_hostname = getRemoteHostname(this);\n\t\t\tport = ((InetSocketAddress) socketIO.getSocketChannel().getRemoteAddress()).getPort();\n\t\t}\n\n\t\tsocketIO = sslContextContainer.createIoInterface(\"SSL\", tls_hostname, tls_remote_hostname, port, clientMode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t wantClientAuth, needClientAuth, byteOrder(), x509TrustManagers,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t this, socketIO, certificateContainer);\n//\t\tif (!clientMode && useBouncyCastle) {\n//\t\t\tsocketIO = new BcTLSIO(certificateContainer, this, socketIO, tls_hostname, byteOrder(), wantClientAuth,\n//\t\t\t\t\t\t\t\t   needClientAuth, sslContextContainer.getEnabledCiphers(),\n//\t\t\t\t\t\t\t\t   sslContextContainer.getEnabledProtocols(),x509TrustManagers);\n//\t\t} else {\n//\t\t\tSSLContext sslContext = sslContextContainer.getSSLContext(\"SSL\", tls_hostname, clientMode,\n//\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  x509TrustManagers);\n//\t\t\tTLSWrapper wrapper = new JcaTLSWrapper(sslContext, this, tls_hostname, port, clientMode, wantClientAuth,\n//\t\t\t\t\t\t\t\t\t\t\t\t   needClientAuth, sslContextContainer.getEnabledCiphers(),\n//\t\t\t\t\t\t\t\t\t\t\t\t   sslContextContainer.getEnabledProtocols());\n//\t\t\tsocketIO = new TLSIO(socketIO, wrapper, byteOrder());\n//\t\t}\n\n\t\tsetLastTransferTime();\n\t\tencoder.reset();\n\t\tdecoder.reset();\n\t}\n\n\tpublic CertificateContainerIfc getCertificateContainer() {\n\t\treturn certificateContainer;\n\t}\n\n\tpublic void setCertificateContainer(CertificateContainerIfc certificateContainer) {\n\t\tthis.certificateContainer = certificateContainer;\n\t}\n\n\tpublic void startTLS(boolean clientMode, boolean wantClientAuth, boolean needClientAuth) throws IOException {\n\t\tif (socketIO.checkCapabilities(TLSIO.TLS_CAPS)) {\n\t\t\tthrow new IllegalStateException(\"TLS mode is already activated \" + connectionId);\n\t\t}\n\t\tif (sslContextContainer == null) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\"TLS cannot be activated - sslContextContainer is not set for \" + connectionId);\n\t\t}\n\n\t\t// This should not take more then 100ms\n\t\tint counter = 0;\n\n\t\twhile (isConnected() && waitingToSend() && (++counter < 10)) {\n\t\t\twriteData(null);\n\t\t\ttry {\n\t\t\t\tThread.sleep(10);\n\t\t\t} catch (InterruptedException ex) {\n\t\t\t}\n\t\t}\n\t\tif (counter >= 10) {\n\t\t\tstop();\n\t\t} else {\n\t\t\tString tls_hostname = (String) sessionData.get(HOSTNAME_KEY);\n\t\t\tString tls_remote_hostname = null;\n\t\t\tint port = 0;\n\t\t\tif (clientMode) {\n\t\t\t\tport = ((InetSocketAddress) socketIO.getSocketChannel().getRemoteAddress()).getPort();\n\t\t\t\ttls_remote_hostname = getRemoteHostname(this);\n\t\t\t}\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Starting TLS for domain: {1} [{0}]\", new Object[]{this, tls_hostname});\n\t\t\t}\n\n\t\t\tsocketIO = sslContextContainer.createIoInterface(\"TLS\", tls_hostname, tls_remote_hostname, port, clientMode,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t wantClientAuth, needClientAuth, byteOrder(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t x509TrustManagers, this, socketIO, certificateContainer);\n\t\t\tsetLastTransferTime();\n\t\t\tencoder.reset();\n\t\t\tdecoder.reset();\n\t\t}\n\t}\n\n\tpublic void startZLib(int level) {\n\t\tif (socketIO.checkCapabilities(ZLibIO.ZLIB_CAPS)) {\n\t\t\tthrow new IllegalStateException(\"ZLIB mode is already activated.\");\n\t\t}\n\t\tsocketIO = new ZLibIO(socketIO, level);\n\t\t((ZLibIO) socketIO).setIOListener(this);\n\t}\n\n\tpublic void stop() {\n\t\tif ((socketIO != null) && socketIO.waitingToSend()) {\n\t\t\tstopping = true;\n\t\t} else {\n\t\t\tforceStop();\n\t\t}\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// there is no need to include connectionId here as it's passed to socket in\n\t\t// tigase.net.IOService.setConnectionId and included from there\n\t\treturn getClass().getSimpleName() + \", UniqueId: \" + getUniqueId() + \", type: \" + (connectionType != null ? connectionType.toStringPretty() : null) + \", \" + socketIO;\n\t}\n\n\tpublic boolean waitingToRead() {\n\t\treturn true;\n\t}\n\n\tpublic boolean waitingToSend() {\n\t\treturn socketIO.waitingToSend();\n\t}\n\n\tpublic int waitingToSendSize() {\n\t\treturn socketIO.waitingToSendSize();\n\t}\n\n\tpublic long getBuffOverflow(boolean reset) {\n\t\treturn socketIO.getBuffOverflow(reset);\n\t}\n\n\tpublic long getBytesReceived(boolean reset) {\n\t\treturn socketIO.getBytesReceived(reset);\n\t}\n\n\tpublic long getBytesSent(boolean reset) {\n\t\treturn socketIO.getBytesSent(reset);\n\t}\n\n\tpublic JID getConnectionId() {\n\t\treturn connectionId;\n\t}\n\n\tpublic void setConnectionId(JID connectionId) {\n\t\tthis.connectionId = connectionId;\n\t\tsocketIO.setLogId(connectionId.toString());\n\t}\n\n\tpublic JID getDataReceiver() {\n\t\treturn this.dataReceiver;\n\t}\n\n\tpublic void setDataReceiver(JID address) {\n\t\tthis.dataReceiver = address;\n\t}\n\n\t/**\n\t * This method returns the time of last transfer in any direction through this service. It is used to help detect\n\t * dead connections.\n\t */\n\tpublic long getLastTransferTime() {\n\t\treturn lastTransferTime;\n\t}\n\n\tpublic String getLocalAddress() {\n\t\treturn local_address;\n\t}\n\n\tpublic byte[] getTlsUniqueId() {\n\t\treturn tlsUniqueId;\n\t}\n\n\tpublic byte[] getTlsExporter() {\n\t\treturn tlsExporter;\n\t}\n\n\t/**\n\t * Method returns local port of opened socket\n\t */\n\tpublic int getLocalPort() {\n\t\tSocket sock = socketIO.getSocketChannel().socket();\n\t\treturn sock.getLocalPort();\n\t}\n\n\tpublic long[] getReadCounters() {\n\t\treturn rdData;\n\t}\n\n\tpublic RefObject getRefObject() {\n\t\treturn refObject;\n\t}\n\n\tpublic void setRefObject(RefObject refObject) {\n\t\tthis.refObject = refObject;\n\t}\n\n\t/**\n\t * Returns a remote IP address for the TCP/IP connection.\n\t *\n\t * @return a remote IP address for the TCP/IP connection.\n\t */\n\tpublic String getRemoteAddress() {\n\t\treturn remote_address;\n\t}\n\n\tpublic ConcurrentMap<String, Object> getSessionData() {\n\t\treturn sessionData;\n\t}\n\n\tpublic void setSessionData(Map<String, Object> props) {\n\n\t\t// Sometimes, some values are null which is allowed in the original Map\n\t\t// however, ConcurrentHashMap does not allow nulls as value so we have\n\t\t// to copy Maps carefully.\n\t\tsessionData = new ConcurrentHashMap<String, Object>(props.size());\n\t\tfor (Map.Entry<String, Object> entry : props.entrySet()) {\n\t\t\tif (entry.getValue() != null) {\n\t\t\t\tsessionData.put(entry.getKey(), entry.getValue());\n\t\t\t}\n\t\t}\n\t\tconnectionType = ConnectionType.valueOf(String.valueOf(sessionData.get(PORT_TYPE_PROP_KEY)));\n\t}\n\n\tpublic int getSocketInputSize() {\n\t\treturn socketInputSize;\n\t}\n\n\tpublic SocketChannel getSocketChannel() {\n\t\treturn socketIO.getSocketChannel();\n\t}\n\n\tpublic void getStatistics(StatisticsList list, boolean reset) {\n\t\tif (socketIO != null) {\n\t\t\tsocketIO.getStatistics(list, reset);\n\t\t}\n\t}\n\n\tpublic long getTotalBuffOverflow() {\n\t\treturn socketIO.getTotalBuffOverflow();\n\t}\n\n\tpublic long getTotalBytesReceived() {\n\t\treturn socketIO.getTotalBytesReceived();\n\t}\n\n\tpublic long getTotalBytesSent() {\n\t\treturn socketIO.getTotalBytesSent();\n\t}\n\n\tpublic String getUniqueId() {\n\t\treturn id;\n\t}\n\n\tpublic long[] getWriteCounters() {\n\t\treturn wrData;\n\t}\n\n\tpublic boolean isConnected() {\n\t\tboolean result = (socketIO != null) && socketIO.isConnected();\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Connected: {1} [{0}]\", new Object[]{socketIO, result});\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic void setBufferLimit(int bufferLimit) {\n\t\tthis.bufferLimit = bufferLimit;\n\t}\n\n\tpublic void setIOServiceListener(IOServiceListener<IOService<RefObject>> sl) {\n\t\tthis.serviceListener = sl;\n\t}\n\n\tpublic void setSslContextContainer(SSLContextContainerIfc sslContextContainer) {\n\t\tthis.sslContextContainer = sslContextContainer;\n\t}\n\n\tpublic void setX509TrustManagers(TrustManager[] trustManager) {\n\t\tthis.x509TrustManagers = trustManager;\n\t}\n\n\tpublic Certificate getPeerCertificate() {\n\t\treturn peerCertificate;\n\t}\n\n\tpublic Certificate getLocalCertificate() {\n\t\treturn localCertificate;\n\t}\n\n\tprotected ByteOrder byteOrder() {\n\t\treturn ByteOrder.BIG_ENDIAN;\n\t}\n\n\tprotected boolean debug(final char[] msg) {\n\t\tif (msg != null) {\n\t\t\tSystem.out.print(new String(msg));\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprotected boolean debug(final String msg, final String prefix) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tif ((msg != null) && (msg.trim().length() > 0)) {\n\t\t\t\tString log_msg =\n\t\t\t\t\t\t\"\\n\" + ((connectionType() != null) ? connectionType().toString() : \"null-type\") + \" \" + prefix +\n\t\t\t\t\t\t\t\t\"\\n\" + msg + \"\\n\";\n\n\t\t\t\t// System.out.print(log_msg);\n\t\t\t\tlog.finest(log_msg);\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprotected abstract void processSocketData() throws IOException;\n\n\tprotected ByteBuffer readBytes() throws IOException {\n\t\tsetLastTransferTime();\n\t\tif (log.isLoggable(Level.FINEST) && (empty_read_call_count > 10)) {\n\t\t\tThrowable thr = new Throwable();\n\n\t\t\tthr.fillInStackTrace();\n\t\t\tlog.log(Level.FINEST, \"Socket: \" + socketIO, thr);\n\t\t}\n\t\ttry {\n\t\t\tByteBuffer tmpBuffer = socketIO.read(socketInput);\n\n\t\t\tif (socketIO.bytesRead() > 0) {\n\t\t\t\tempty_read_call_count = 0;\n\n\t\t\t\treturn tmpBuffer;\n\t\t\t} else {\n\t\t\t\tif ((++empty_read_call_count) > MAX_ALLOWED_EMPTY_CALLS && (!writeInProgress.isLocked())) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Max allowed empty calls exceeded, closing connection. [{0}]\",\n\t\t\t\t\t\t\tsocketIO);\n\t\t\t\t\tforceStop();\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (BufferUnderflowException ex) {\n\t\t\tresizeInputBuffer();\n\n\t\t\treturn readBytes();\n\t\t} catch (SSLHandshakeException e) {\n\t\t\tif (log.isLoggable(Level.CONFIG)) {\n\t\t\t\tlog.log(Level.CONFIG, \"Exception starting connection [\" + socketIO + \"]: \" + e);\n\t\t\t}\n\t\t\tforceStop();\n\t\t} catch (Exception eof) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Exception reading data [\" + socketIO + \"]: \" + eof);\n\t\t\t}\n\t\t\tforceStop();\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprotected void readCompleted() {\n\t\tdecoder.reset();\n\t}\n\n\tprotected char[] readData() throws IOException {\n\t\tsetLastTransferTime();\n\t\tif (log.isLoggable(Level.FINEST) && (empty_read_call_count > 10)) {\n\t\t\tThrowable thr = new Throwable();\n\n\t\t\tthr.fillInStackTrace();\n\t\t\tlog.log(Level.FINEST, \"Socket: \" + socketIO, thr);\n\t\t}\n\n\t\t// Generally it should not happen as it is called only in\n\t\t// call() which has concurrent call protection.\n\t\t// synchronized (socketIO) {\n\t\ttry {\n\n\t\t\t// resizeInputBuffer();\n\t\t\t// Maybe we can shrink the input buffer??\n\t\t\tif ((socketInput.capacity() > socketInputSize) && (socketInput.remaining() == socketInput.capacity())) {\n\n\t\t\t\t// Yes, looks like we can\n\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\tlog.log(Level.FINE, \"Resizing socketInput down to {1} bytes. [{0}]\",\n\t\t\t\t\t\t\tnew Object[]{socketIO, socketInputSize});\n\t\t\t\t}\n\t\t\t\tsocketInput = ByteBuffer.allocate(socketInputSize);\n\t\t\t\tsocketInput.order(byteOrder());\n\t\t\t\tcb = CharBuffer.allocate(socketInputSize * 4);\n\t\t\t}\n\n\t\t\t// if (log.isLoggable(Level.FINEST)) {\n\t\t\t// log.finer(\"Before read from socket.\");\n\t\t\t// log.finer(\"socketInput.capacity()=\" + socketInput.capacity());\n\t\t\t// log.finer(\"socketInput.remaining()=\" + socketInput.remaining());\n\t\t\t// log.finer(\"socketInput.limit()=\" + socketInput.limit());\n\t\t\t// log.finer(\"socketInput.position()=\" + socketInput.position());\n\t\t\t// }\n\t\t\tByteBuffer tmpBuffer = socketIO.read(socketInput);\n\n\t\t\tif (socketIO.bytesRead() > 0) {\n\t\t\t\tempty_read_call_count = 0;\n\n\t\t\t\tchar[] result = null;\n\n\t\t\t\t// There might be some characters read from the network\n\t\t\t\t// but the buffer may still be null or empty because there might\n\t\t\t\t// be not enough data to decode TLS or compressed buffer.\n\t\t\t\tif (tmpBuffer != null) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Reading network binary data: {1} [{0}]\",\n\t\t\t\t\t\t\t\tnew Object[]{socketIO, socketIO.bytesRead()});\n\t\t\t\t\t}\n\n\t\t\t\t\t// Restore the partial bytes for multibyte UTF8 characters\n\t\t\t\t\tif (partialCharacterBytes != null) {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Reloading partial bytes: {1} [{0}]\",\n\t\t\t\t\t\t\t\t\tnew Object[]{socketIO, partialCharacterBytes.length});\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tByteBuffer oldTmpBuffer = tmpBuffer;\n\n\t\t\t\t\t\ttmpBuffer = ByteBuffer.allocate(partialCharacterBytes.length + oldTmpBuffer.remaining() + 2);\n\t\t\t\t\t\ttmpBuffer.order(byteOrder());\n\t\t\t\t\t\ttmpBuffer.put(partialCharacterBytes);\n\t\t\t\t\t\ttmpBuffer.put(oldTmpBuffer);\n\t\t\t\t\t\ttmpBuffer.flip();\n\t\t\t\t\t\toldTmpBuffer.clear();\n\t\t\t\t\t\tpartialCharacterBytes = null;\n\t\t\t\t\t}\n\n\t\t\t\t\t// if (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t// log.finer(\"Before decoding data\");\n\t\t\t\t\t// log.finer(\"socketInput.capacity()=\" + socketInput.capacity());\n\t\t\t\t\t// log.finer(\"socketInput.remaining()=\" + socketInput.remaining());\n\t\t\t\t\t// log.finer(\"socketInput.limit()=\" + socketInput.limit());\n\t\t\t\t\t// log.finer(\"socketInput.position()=\" + socketInput.position());\n\t\t\t\t\t// log.finer(\"tmpBuffer.capacity()=\" + tmpBuffer.capacity());\n\t\t\t\t\t// log.finer(\"tmpBuffer.remaining()=\" + tmpBuffer.remaining());\n\t\t\t\t\t// log.finer(\"tmpBuffer.limit()=\" + tmpBuffer.limit());\n\t\t\t\t\t// log.finer(\"tmpBuffer.position()=\" + tmpBuffer.position());\n\t\t\t\t\t// log.finer(\"cb.capacity()=\" + cb.capacity());\n\t\t\t\t\t// log.finer(\"cb.remaining()=\" + cb.remaining());\n\t\t\t\t\t// log.finer(\"cb.limit()=\" + cb.limit());\n\t\t\t\t\t// log.finer(\"cb.position()=\" + cb.position());\n\t\t\t\t\t// }\n\t\t\t\t\t// tmpBuffer.flip();\n\t\t\t\t\tif (cb.capacity() < tmpBuffer.remaining() * 4) {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Resizing character buffer to: {1} [{0}]\",\n\t\t\t\t\t\t\t\t\tnew Object[]{socketIO, tmpBuffer.remaining()});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcb = CharBuffer.allocate(tmpBuffer.remaining() * 4);\n\t\t\t\t\t}\n\n\t\t\t\t\tCoderResult cr = decoder.decode(tmpBuffer, cb, false);\n\n\t\t\t\t\tif (cr.isMalformed()) {\n\t\t\t\t\t\tif (!handleMalformedInput(tmpBuffer, cb)) {\n\t\t\t\t\t\t\tthrow new MalformedInputException(tmpBuffer.remaining());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (cb.remaining() > 0) {\n\t\t\t\t\t\tcb.flip();\n\t\t\t\t\t\tresult = new char[cb.remaining()];\n\t\t\t\t\t\tcb.get(result);\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Decoded character data: {1} [{0}]\",\n\t\t\t\t\t\t\t\t\tnew Object[]{socketIO, new String(result)});\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// if (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t// log.finer(\"Just after decoding.\");\n\t\t\t\t\t\t// log.finer(\"tmpBuffer.capacity()=\" + tmpBuffer.capacity());\n\t\t\t\t\t\t// log.finer(\"tmpBuffer.remaining()=\" + tmpBuffer.remaining());\n\t\t\t\t\t\t// log.finer(\"tmpBuffer.limit()=\" + tmpBuffer.limit());\n\t\t\t\t\t\t// log.finer(\"tmpBuffer.position()=\" + tmpBuffer.position());\n\t\t\t\t\t\t// log.finer(\"cb.capacity()=\" + cb.capacity());\n\t\t\t\t\t\t// log.finer(\"cb.remaining()=\" + cb.remaining());\n\t\t\t\t\t\t// log.finer(\"cb.limit()=\" + cb.limit());\n\t\t\t\t\t\t// log.finer(\"cb.position()=\" + cb.position());\n\t\t\t\t\t\t// }\n\t\t\t\t\t}\n\t\t\t\t\tif (cr.isUnderflow() && (tmpBuffer.remaining() > 0)) {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"UTF-8 decoder data underflow: {1} [{0}]\",\n\t\t\t\t\t\t\t\t\tnew Object[]{socketIO, tmpBuffer.remaining()});\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Save the partial bytes of a multibyte character such that they\n\t\t\t\t\t\t// can be restored on the next read.\n\t\t\t\t\t\tpartialCharacterBytes = new byte[tmpBuffer.remaining()];\n\t\t\t\t\t\ttmpBuffer.get(partialCharacterBytes);\n\t\t\t\t\t}\n\t\t\t\t\ttmpBuffer.clear();\n\t\t\t\t\tcb.clear();\n\n\t\t\t\t\t// if (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t// log.finer(\"Before return from method.\");\n\t\t\t\t\t// log.finer(\"tmpBuffer.capacity()=\" + tmpBuffer.capacity());\n\t\t\t\t\t// log.finer(\"tmpBuffer.remaining()=\" + tmpBuffer.remaining());\n\t\t\t\t\t// log.finer(\"tmpBuffer.limit()=\" + tmpBuffer.limit());\n\t\t\t\t\t// log.finer(\"tmpBuffer.position()=\" + tmpBuffer.position());\n\t\t\t\t\t// log.finer(\"cb.capacity()=\" + cb.capacity());\n\t\t\t\t\t// log.finer(\"cb.remaining()=\" + cb.remaining());\n\t\t\t\t\t// log.finer(\"cb.limit()=\" + cb.limit());\n\t\t\t\t\t// log.finer(\"cb.position()=\" + cb.position());\n\t\t\t\t\t// }\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\t\t\t} else {\n\n\t\t\t\t// Detecting infinite read 0 bytes\n\t\t\t\t// sometimes it happens that the connection has been lost\n\t\t\t\t// and the select thinks there are some bytes waiting for reading\n\t\t\t\t// and 0 bytes are read\n\t\t\t\tif ((++empty_read_call_count) > MAX_ALLOWED_EMPTY_CALLS && (!writeInProgress.isLocked())) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Max allowed empty calls exceeded, closing connection. [{0}]\",\n\t\t\t\t\t\t\tsocketIO);\n\t\t\t\t\tforceStop();\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (BufferUnderflowException ex) {\n\n\t\t\t// Obtain more inbound network data for src,\n\t\t\t// then retry the operation.\n\t\t\tresizeInputBuffer();\n\n\t\t\treturn null;\n\n\t\t\t// } catch (MalformedInputException ex) {\n\t\t\t//// This happens after TLS initialization sometimes, maybe reset helps\n\t\t\t// decoder.reset();\n\t\t} catch (SSLProtocolException | SSLHandshakeException e) {\n\t\t\tif (log.isLoggable(Level.CONFIG)) {\n\t\t\t\tlog.log(Level.CONFIG, \"Exception starting connection [\"  + socketIO + \"] \" + e);\n\t\t\t}\n\t\t\tforceStop();\n\t\t} catch (Exception eof) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Exception reading data [\"  + socketIO + \"] \" + eof);\n\t\t\t}\n\t\t\tforceStop();\n\t\t}    // end of try-catch\n\t\treturn null;\n\t}\n\n\tprotected abstract int receivedPackets();\n\n\tprotected void writeBytes(ByteBuffer data) {\n\n\t\t// Try to lock the data writing method\n\t\tboolean locked = writeInProgress.tryLock();\n\n\t\t// If cannot lock and nothing to send, just leave\n\t\tif (!locked && (data == null)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Otherwise wait.....\n\t\tif (!locked) {\n\t\t\twriteInProgress.lock();\n\t\t}\n\n\t\t// Avoid concurrent calls here (one from call() and another from\n\t\t// application)\n\t\ttry {\n\t\t\tif ((data != null) && data.hasRemaining()) {\n\t\t\t\tint length = data.remaining();\n\n\t\t\t\tsocketIO.write(data);\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Wrote: {1} [{0}]\", new Object[]{socketIO, length});\n\t\t\t\t}\n\t\t\t\tsetLastTransferTime();\n\t\t\t\tempty_read_call_count = 0;\n\t\t\t} else {\n\t\t\t\tif (socketIO.waitingToSend()) {\n\t\t\t\t\tsocketIO.write(null);\n\t\t\t\t\tsetLastTransferTime();\n\t\t\t\t\tempty_read_call_count = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.log(Level.FINER, \"Data writing exception [\"  + socketIO + \"] \" + e);\n\t\t\t}\n\t\t\tforceStop();\n\t\t} finally {\n\t\t\twriteInProgress.unlock();\n\t\t}\n\t}\n\n\tprotected void writeData(final String data) {\n\n\t\t// Try to lock the data writing method\n\t\tboolean locked = writeInProgress.tryLock();\n\n\t\t// If cannot lock and nothing to send, just leave\n\t\tif (!locked && (data == null)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Otherwise wait.....\n\t\tif (!locked) {\n\t\t\twriteInProgress.lock();\n\t\t}\n\n\t\t// Avoid concurrent calls here (one from call() and another from\n\t\t// application)\n\t\ttry {\n\t\t\tif ((data != null) && (data.length() > 0)) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tif (data.length() < 256) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Writing data ({1}): {2} [{0}]\",\n\t\t\t\t\t\t\t\tnew Object[]{socketIO, data.length(), data});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Writing data: {1} [{0}]\", new Object[]{socketIO, data.length()});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tByteBuffer dataBuffer = null;\n\n\t\t\t\t// int out_buff_size = data.length();\n\t\t\t\t// int idx_start = 0;\n\t\t\t\t// int idx_offset = Math.min(idx_start + out_buff_size, data.length());\n\t\t\t\t//\n\t\t\t\t// while (idx_start < data.length()) {\n\t\t\t\t// String data_str = data.substring(idx_start, idx_offset);\n\t\t\t\t// if (log.isLoggable(Level.FINEST)) {\n\t\t\t\t// log.finest(\"Writing data_str (\" + data_str.length() + \"), idx_start=\"\n\t\t\t\t// + idx_start + \", idx_offset=\" + idx_offset + \": \" + data_str);\n\t\t\t\t// }\n\t\t\t\tencoder.reset();\n\n\t\t\t\t// dataBuffer = encoder.encode(CharBuffer.wrap(data, idx_start,\n\t\t\t\t// idx_offset));\n\t\t\t\tdataBuffer = encoder.encode(CharBuffer.wrap(data));\n\t\t\t\tencoder.flush(dataBuffer);\n\t\t\t\tsocketIO.write(dataBuffer);\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Wrote: {1} [{0}]\", new Object[]{socketIO, data.length()});\n\t\t\t\t}\n\n\t\t\t\t// idx_start = idx_offset;\n\t\t\t\t// idx_offset = Math.min(idx_start + out_buff_size, data.length());\n\t\t\t\t// }\n\t\t\t\tsetLastTransferTime();\n\n\t\t\t\t// addWritten(data.length());\n\t\t\t\tempty_read_call_count = 0;\n\t\t\t} else {\n\t\t\t\tif (socketIO.waitingToSend()) {\n\t\t\t\t\tsocketIO.write(null);\n\t\t\t\t\tsetLastTransferTime();\n\t\t\t\t\tempty_read_call_count = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (SSLHandshakeException e) {\n\t\t\tif (log.isLoggable(Level.CONFIG)) {\n\t\t\t\tlog.log(Level.CONFIG,  \"Exception starting connection [\" + socketIO + \"]\" + e);\n\t\t\t}\n\t\t\tforceStop();\n\t\t} catch (Exception e) {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.log(Level.FINER,  \"Data [\" + data + \"] writing exception [\" + socketIO + \"]\" + e);\n\t\t\t}\n\t\t\tforceStop();\n\t\t} finally {\n\t\t\twriteInProgress.unlock();\n\t\t}\n\t}\n\n\tprotected boolean isSocketServiceReady() {\n\t\treturn socketServiceReady;\n\t}\n\n\tprotected void setSocketServiceReady(boolean value) {\n\t\tthis.socketServiceReady = value;\n\t}\n\n\tprotected boolean handleMalformedInput(ByteBuffer buffer, CharBuffer cb) {\n\t\treturn false;\n\t}\n\n\tprotected boolean isInputBufferEmpty() {\n\t\treturn (socketInput != null) && (socketInput.remaining() == socketInput.capacity());\n\t}\n\n\tprotected IOInterface getIO() {\n\t\treturn socketIO;\n\t}\n\n\tprivate void resizeInputBuffer() throws IOException {\n\t\tint netSize = socketIO.getInputPacketSize();\n\n\t\t// Resize buffer if needed.\n\t\t// if (netSize > socketInput.remaining()) {\n\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\tlog.log(Level.FINE, \"Resize? netSize: {1}, capacity: {2}, remaining: {3}. [{0}]\",\n\t\t\t\t\tnew Object[]{socketIO, netSize, socketInput.capacity(), socketInput.remaining()});\n\t\t}\n\n\t\tif (netSize > socketInput.capacity() - socketInput.remaining()) {\n\n\t\t\t// int newSize = netSize + socketInput.capacity();\n\t\t\tint newSize = socketInput.capacity() * 2;\n\t\t\tif (bufferLimit > 0 && newSize > bufferLimit && socketInput.capacity() < bufferLimit) {\n\t\t\t\tnewSize = bufferLimit;\n\t\t\t}\n\t\t\tif (!checkBufferLimit(bufferLimit)) {\n\t\t\t\tthrow new IOException(\"Input buffer size limit exceeded\");\n\t\t\t}\n\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"Resizing socketInput to {1} bytes. [{0}]\", new Object[]{socketIO, newSize});\n\t\t\t}\n\n\t\t\tByteBuffer b = ByteBuffer.allocate(newSize);\n\n\t\t\tb.order(byteOrder());\n\t\t\tb.put(socketInput);\n\t\t\tsocketInput = b;\n\t\t} else {\n\n\t\t\t// if (log.isLoggable(Level.FINEST)) {\n\t\t\t// log.finer(\"     Before flip()\");\n\t\t\t// log.finer(\"input.capacity()=\" + socketInput.capacity());\n\t\t\t// log.finer(\"input.remaining()=\" + socketInput.remaining());\n\t\t\t// log.finer(\"input.limit()=\" + socketInput.limit());\n\t\t\t// log.finer(\"input.position()=\" + socketInput.position());\n\t\t\t// }\n\t\t\t// socketInput.flip();\n\t\t\t// if (log.isLoggable(Level.FINEST)) {\n\t\t\t// log.finer(\"     Before compact()\");\n\t\t\t// log.finer(\"input.capacity()=\" + socketInput.capacity());\n\t\t\t// log.finer(\"input.remaining()=\" + socketInput.remaining());\n\t\t\t// log.finer(\"input.limit()=\" + socketInput.limit());\n\t\t\t// log.finer(\"input.position()=\" + socketInput.position());\n\t\t\t// }\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"Compacting socketInput. [{0}]\", socketIO);\n\t\t\t}\n\t\t\tsocketInput.compact();\n\n\t\t\t// if (log.isLoggable(Level.FINEST)) {\n\t\t\t// log.finer(\"     After compact()\");\n\t\t\t// log.finer(\"input.capacity()=\" + socketInput.capacity());\n\t\t\t// log.finer(\"input.remaining()=\" + socketInput.remaining());\n\t\t\t// log.finer(\"input.limit()=\" + socketInput.limit());\n\t\t\t// log.finer(\"input.position()=\" + socketInput.position());\n\t\t\t// }\n\t\t}\n\t}\n\n\tprivate void setLastTransferTime() {\n\t\tlastTransferTime = System.currentTimeMillis();\n\t}\n\n}    // IOService\n\n"
  },
  {
    "path": "src/main/java/tigase/net/IOServiceListener.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.net;\n\nimport java.io.IOException;\n\n/**\n * Describe interface IOServiceListener here.\n * <br>\n * Created: Mon Jan 30 22:37:51 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface IOServiceListener<IO extends IOService<?>> {\n\n\tvoid packetsReady(IO service) throws IOException;\n\n\tboolean serviceStopped(IO service);\n\n\tvoid tlsHandshakeCompleted(IO service);\n}    // IOServiceListener\n\n"
  },
  {
    "path": "src/main/java/tigase/net/IOUtil.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.net;\n\nimport java.nio.ByteBuffer;\n\n/**\n * @author andrzej\n */\npublic class IOUtil {\n\n\tstatic {\n\t}\n\n\tpublic static ByteBuffer getDirectBuffer(int size) {\n\t\treturn ByteBuffer.allocateDirect(size);\n\t}\n\n\tpublic static void returnDirectBuffer(ByteBuffer buf) {\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/net/ServiceCommand.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.net;\n\n/**\n * <code>ServiceCommand</code> is enumerated type defining all possible commands related to <code>ServiceData</code>\n * instances. These commands are processed by <code>MessageDispatcher</code> implementations. Some commands are related\n * to data encapsulated in <code>ServiceData</code> instance like <code>SEND_MESSAGE</code> or <code>BROADCAST</code>\n * others are related to <code>ServerService</code> sending or receiving this message like: <code>STOP</code>,\n * <code>CONNECTED</code> and so on. Please refer to detailed API documentation for more information.\n * <br>\n * <p> Created: Sun Oct 17 22:32:22 2004 </p>\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic enum ServiceCommand {\n\n\tSTOP,\n\tSTOPPED,\n\tCONNECT,\n\tCONNECTED,\n\tSEND_MESSAGE,\n\tBROADCAST;\n\n} // ServiceCommand\n"
  },
  {
    "path": "src/main/java/tigase/net/SocketThread.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.net;\n\nimport tigase.annotations.TODO;\n\nimport java.io.IOException;\nimport java.nio.channels.CancelledKeyException;\nimport java.nio.channels.SelectionKey;\nimport java.nio.channels.Selector;\nimport java.nio.channels.SocketChannel;\nimport java.util.Comparator;\nimport java.util.Set;\nimport java.util.concurrent.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Describe class SocketThread here.\n * <br>\n * Created: Mon Jan 30 12:01:17 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class SocketThread\n\t\timplements Runnable {\n\n\tpublic static final int DEF_MAX_THREADS_PER_CPU = 8;\n\tprivate static final Logger log = Logger.getLogger(SocketThread.class.getName());\n\tprivate static final int MAX_EMPTY_SELECTIONS = 10;\n\t/**\n\t * Variable <code>completionService</code> keeps reference to server thread pool. There is only one thread pool used\n\t * by all server modules. Each module requiring separate threads for tasks processing must have access to server\n\t * thread pool.\n\t */\n\tprivate static CompletionService<IOService<?>> completionService = null;\n\tprivate static int cpus = Runtime.getRuntime().availableProcessors();\n\tprivate static ThreadPoolExecutor executor = null;\n\tprivate static SocketThread[] socketReadThread = null;\n\tprivate static SocketThread[] socketWriteThread = null;\n\n\t//private static int threadNo = 0;\n//private static final int READ_ONLY = SelectionKey.OP_READ;\n//private static final int READ_WRITE = SelectionKey.OP_READ | SelectionKey.OP_WRITE;\n\tstatic {\n\t\tif (socketReadThread == null) {\n\t\t\tint nThreads = (cpus * DEF_MAX_THREADS_PER_CPU) / 2 + 1;\n\n\t\t\texecutor = new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,\n\t\t\t\t\t\t\t\t\t\t\t  new LinkedBlockingQueue<Runnable>());\n\t\t\tcompletionService = new ExecutorCompletionService<IOService<?>>(executor);\n\t\t\tsocketReadThread = new SocketThread[nThreads];\n\t\t\tsocketWriteThread = new SocketThread[nThreads];\n\n\t\t\tfor (int i = 0; i < socketReadThread.length; i++) {\n\t\t\t\tsocketReadThread[i] = new SocketThread(\"socketReadThread-\" + i);\n\t\t\t\tsocketReadThread[i].reading = true;\n\n\t\t\t\tThread thrd = new Thread(socketReadThread[i]);\n\n\t\t\t\tthrd.setName(\"socketReadThread-\" + i);\n\t\t\t\tthrd.start();\n\t\t\t}\n\n\t\t\tlog.log(Level.INFO, \"{0} socketReadThreads started.\", socketReadThread.length);\n\n\t\t\tfor (int i = 0; i < socketWriteThread.length; i++) {\n\t\t\t\tsocketWriteThread[i] = new SocketThread(\"socketWriteThread-\" + i);\n\t\t\t\tsocketWriteThread[i].writing = true;\n\n\t\t\t\tThread thrd = new Thread(socketWriteThread[i]);\n\n\t\t\t\tthrd.setName(\"socketWriteThread-\" + i);\n\t\t\t\tthrd.start();\n\t\t\t}\n\n\t\t\tlog.log(Level.INFO, \"{0} socketWriteThreads started.\", socketWriteThread.length);\n\t\t}    // end of if (acceptThread == null)\n\t}\n\n\tprivate Selector clientsSel = null;\n\n\t// private boolean selecting = false;\n\tprivate int empty_selections = 0;\n\t// IOServices must be added to thread pool after they are removed from\n\t// the selector and the selector and key is cleared, otherwise we have\n\t// dead-lock somewhere down in the:\n\t// java.nio.channels.spi.AbstractSelectableChannel.removeKey(AbstractSelectableChannel.java:111)\n\tprivate ConcurrentSkipListSet<IOService<?>> forCompletion = new ConcurrentSkipListSet<IOService<?>>(\n\t\t\tnew IOServiceComparator());\n\tprivate boolean reading = false;\n\tprivate boolean stopping = false;\n\tprivate ConcurrentSkipListSet<IOService<?>> waiting = new ConcurrentSkipListSet<IOService<?>>(\n\t\t\tnew IOServiceComparator());\n\tprivate boolean writing = false;\n\n\tpublic static void addSocketService(IOService<?> s) {\n\t\ts.setSocketServiceReady(true);\n\t\t// Due to a delayed SelectionKey cancelling deregistering\n\t\t// nature this distribution doesn't work well, it leads to\n\t\t// dead-lock. Let's make sure the service is always processed\n\t\t// by the same thread thus the same Selector.\n\t\t// socketReadThread[incrementAndGet()].addSocketServicePriv(s);\n\t\tif (s.waitingToRead()) {\n\t\t\tsocketReadThread[s.hashCode() % socketReadThread.length].addSocketServicePriv(s);\n\t\t}\n\n\t\tif (s.waitingToSend()) {\n\t\t\tsocketWriteThread[s.hashCode() % socketWriteThread.length].addSocketServicePriv(s);\n\t\t}\n\t}\n\n\n\tpublic static void removeSocketService(IOService<Object> s) {\n\t\ts.setSocketServiceReady(false);\n\t\tsocketReadThread[s.hashCode() % socketReadThread.length].removeSocketServicePriv(s);\n\t\tsocketWriteThread[s.hashCode() % socketWriteThread.length].removeSocketServicePriv(s);\n\t}\n\n\t/**\n\t * Creates a new <code>SocketThread</code> instance.\n\t */\n\tprivate SocketThread(String name) {\n\t\ttry {\n\t\t\tclientsSel = Selector.open();\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.SEVERE, \"Server I/O error, can't continue my work.\", e);\n\t\t\tstopping = true;\n\t\t}    // end of try-catch\n\n\t\tnew ResultsListener(\"ResultsListener-\" + name).start();\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void addSocketServicePriv(IOService<?> s) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Adding to waiting: {0}\", s);\n\t\t}\n\n\t\twaiting.add((IOService<Object>) s);\n\n\t\t// Calling lazy wakeup to avoid multiple wakeup calls\n\t\t// when lots of new services are added....\n\t\tclientsSel.wakeup();\n\n\t\t// wakeupHelper.wakeup();\n\t}\n\n\tpublic void removeSocketServicePriv(IOService<?> s) {\n\t\twaiting.remove(s);\n\n\t\tSelectionKey key = s.getSocketChannel().keyFor(clientsSel);\n\t\tif ((key != null) && (key.attachment() == s)) {\n\t\t\tkey.cancel();\n\t\t}\n\t}\n\n\t@SuppressWarnings({\"unchecked\"})\n\t@Override\n\tpublic void run() {\n\t\twhile (!stopping) {\n\t\t\ttry {\n\t\t\t\tclientsSel.select();\n\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Selector AWAKE: {0}\", clientsSel);\n\t\t\t\t}\n\n\t\t\t\tSet<SelectionKey> selected = clientsSel.selectedKeys();\n\t\t\t\tint selectedKeys = selected.size();\n\n\t\t\t\tif ((selectedKeys == 0) && (waiting.size() == 0)) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.finest(\"Selected keys = 0!!! a bug again?\");\n\t\t\t\t\t}\n\n\t\t\t\t\tif ((++empty_selections) > MAX_EMPTY_SELECTIONS) {\n\t\t\t\t\t\trecreateSelector();\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tempty_selections = 0;\n\n\t\t\t\t\tif (selectedKeys > 0) {\n\n\t\t\t\t\t\t// This is dirty but selectNow() causes concurrent modification exception\n\t\t\t\t\t\t// and the selectNow() is needed because of a bug in JVM mentioned below\n\t\t\t\t\t\tfor (SelectionKey sk : selected) {\n\n\t\t\t\t\t\t\t// According to most guides we should use below code\n\t\t\t\t\t\t\t// removing SelectionKey from iterator, however a little later\n\t\t\t\t\t\t\t// we do cancel() on this key so removing is somehow redundant\n\t\t\t\t\t\t\t// and causes concurrency exception if a few calls are performed\n\t\t\t\t\t\t\t// at the same time.\n\t\t\t\t\t\t\t// selected_keys.remove(sk);\n\t\t\t\t\t\t\tIOService s = (IOService) sk.attachment();\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\t\tStringBuilder sb = new StringBuilder(\"AWAKEN: \" + s.getIO());\n\n\t\t\t\t\t\t\t\t\tif (sk.isWritable()) {\n\t\t\t\t\t\t\t\t\t\tsb.append(\", ready for WRITING\");\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tif (sk.isReadable()) {\n\t\t\t\t\t\t\t\t\t\tsb.append(\", ready for READING\");\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tsb.append(\", readyOps() = \").append(sk.readyOps());\n\t\t\t\t\t\t\t\t\tlog.finest(sb.toString());\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Set<SelectionKey> selected_keys = clientsSel.selectedKeys();\n\t\t\t\t\t\t\t\t// for (SelectionKey sk : selected_keys) {\n\t\t\t\t\t\t\t\t// Handling a bug or not a bug described in the\n\t\t\t\t\t\t\t\t// last comment to this issue:\n\t\t\t\t\t\t\t\t// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4850373\n\t\t\t\t\t\t\t\t// and\n\t\t\t\t\t\t\t\t// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6403933\n\t\t\t\t\t\t\t\tsk.cancel();\n\t\t\t\t\t\t\t\tforCompletion.add(s);\n\n\t\t\t\t\t\t\t\t// IOServices must be added to thread pool after they are removed from\n\t\t\t\t\t\t\t\t// the selector and the selector and key is cleared, otherwise we have\n\t\t\t\t\t\t\t\t// dead-lock somewhere down in the:\n\t\t\t\t\t\t\t\t// java.nio.channels.spi.AbstractSelectableChannel.\n\t\t\t\t\t\t\t\t// removeKey(AbstractSelectableChannel.java:111)\n\t\t\t\t\t\t\t\t// completionService.submit(s);\n\t\t\t\t\t\t\t} catch (CancelledKeyException e) {\n\t\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"CancelledKeyException, stopping the connection: {0}\",\n\t\t\t\t\t\t\t\t\t\t\ts.getIO());\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\ts.forceStop();\n\t\t\t\t\t\t\t\t} catch (Exception ex2) {\n\t\t\t\t\t\t\t\t\tif (log.isLoggable(Level.WARNING)) {\n\t\t\t\t\t\t\t\t\t\tlog.log(Level.WARNING, \"got exception during forceStop: {0}\", e);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Clean-up cancelled keys...\n\t\t\t\t\tclientsSel.selectNow();\n\t\t\t\t}\n\n\t\t\t\taddAllWaiting();\n\n\t\t\t\tIOService serv = null;\n\n\t\t\t\twhile ((serv = forCompletion.pollFirst()) != null) {\n\t\t\t\t\tcompletionService.submit(serv);\n\t\t\t\t}\n\n\t\t\t\t// clientsSel.selectNow();\n\t\t\t} catch (CancelledKeyException brokene) {\n\n\t\t\t\t// According to Java API that should not happen.\n\t\t\t\t// I think it happens only on the broken Java implementation\n\t\t\t\t// from Apple.\n\t\t\t\tlog.log(Level.WARNING, \"Ups, broken JDK, Apple? \", brokene);\n\n\t\t\t\ttry {\n\t\t\t\t\trecreateSelector();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.log(Level.SEVERE, \"Serious problem, can't recreate selector: \", e);\n\n\t\t\t\t\t// stopping = true;\n\t\t\t\t}\n\t\t\t} catch (IOException ioe) {\n\n\t\t\t\t// According to Java API that should not happen.\n\t\t\t\t// I think it happens only on the broken Java implementation from Apple\n\t\t\t\t// and due to a bug: http://bugs.sun.com/view_bug.do?bug_id=6693490\n\t\t\t\tlog.log(Level.WARNING, \"Problem with the network connection: \", ioe);\n\n\t\t\t\ttry {\n\t\t\t\t\trecreateSelector();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.log(Level.SEVERE, \"Serious problem, can't recreate selector: \", e);\n\n\t\t\t\t\t// stopping = true;\n\t\t\t\t}\n\t\t\t} catch (Exception exe) {\n\t\t\t\tlog.log(Level.SEVERE, \"Server I/O error: \", exe);\n\n\t\t\t\ttry {\n\t\t\t\t\trecreateSelector();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.log(Level.SEVERE, \"Serious problem, can't recreate selector: \", e);\n\n\t\t\t\t\t// stopping = true;\n\t\t\t\t}\n\n\t\t\t\t// stopping = true;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void setMaxThread(int threads) {\n\t\texecutor.setCorePoolSize(threads);\n\t\texecutor.setMaximumPoolSize(threads);\n\t}\n\n\tpublic void setMaxThreadPerCPU(int threads) {\n\t\tsetMaxThread(threads * cpus);\n\t}\n\n\tprivate void addAllWaiting() throws IOException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"waiting.size(): {0}\", waiting.size());\n\t\t}\n\n\t\tIOService s = null;\n\n\t\t// boolean added = false;\n\t\twhile ((s = waiting.pollFirst()) != null) {\n\t\t\tSocketChannel sc = s.getSocketChannel();\n\n\t\t\ttry {\n\t\t\t\tif (sc.isConnected()) {\n\t\t\t\t\tif (reading) {\n\t\t\t\t\t\tsc.register(clientsSel, SelectionKey.OP_READ, s);\n\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"ADDED OP_READ: {0}\", s.getIO());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (writing) {\n\t\t\t\t\t\tsc.register(clientsSel, SelectionKey.OP_WRITE, s);\n\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"ADDED OP_WRITE: {0}\", s.getIO());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// added = true;\n\t\t\t\t} else {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Socket not connected: {0}\", s.getIO());\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\t\tlog.log(Level.FINER, \"Forcing stopping the service: {0}\", s.getIO());\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ts.forceStop();\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Exception while stopping service: \" + s.getIO(), e);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\tlog.log(Level.FINER, \"Forcing stopping the service: \" + s.getIO(), e);\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\ts.forceStop();\n\t\t\t\t} catch (Exception ez) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Exception while stopping service: \" + s.getIO(), ez);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}    // end of try-catch\n\t\t}      // end of for ()\n\n//  if (added) {\n//    clientsSel.wakeup();\n//  }\n\t}\n\n\t// Implementation of java.lang.Runnable\n\tprivate synchronized void recreateSelector() throws IOException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Recreating selector, opened channels: {0}\", clientsSel.keys().size());\n\t\t}\n\n\t\tempty_selections = 0;\n\n\t\t// Handling a bug or not a bug described in the\n\t\t// last comment to this issue:\n\t\t// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4850373\n\t\t// and\n\t\t// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6403933\n\t\t// Recreating the selector and registering all channles with\n\t\t// the new selector\n\t\t// Selector tempSel = clientsSel;\n\t\t// clientsSel = Selector.open();\n\t\tboolean cancelled = false;\n\n\t\t// Sometimes this is just a broken connection which causes\n\t\t// selector spin... this is the cheaper solution....\n\t\tfor (SelectionKey sk : clientsSel.keys()) {\n\t\t\tIOService<?> serv = (IOService<?>) sk.attachment();\n\t\t\tSocketChannel sc = serv.getSocketChannel();\n\n\t\t\tif ((sc == null) || !sc.isConnected()) {\n\t\t\t\tcancelled = true;\n\t\t\t\tsk.cancel();\n\n\t\t\t\ttry {\n\t\t\t\t\tlog.log(Level.CONFIG, \"Forcing stopping the service: {0}\", serv.getIO());\n\t\t\t\t\tserv.forceStop();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// waiting.offer(serv);\n\t\t}\n\n\t\tif (cancelled) {\n\t\t\tclientsSel.selectNow();\n\t\t} else {\n\n\t\t\t// Unfortunately must be something wrong with the selector\n\t\t\t// itself, now more expensive calculations...\n\t\t\tSelector tempSel = clientsSel;\n\n\t\t\tclientsSel = Selector.open();\n\n\t\t\tfor (SelectionKey sk : tempSel.keys()) {\n\t\t\t\tIOService<?> serv = (IOService<?>) sk.attachment();\n\n\t\t\t\tsk.cancel();\n\t\t\t\twaiting.add(serv);\n\t\t\t}\n\n\t\t\ttempSel.close();\n\t\t}\n\t}\n\n\tprivate class IOServiceComparator\n\t\t\timplements Comparator<IOService<?>> {\n\n\t\t@Override\n\t\tpublic int compare(IOService<?> o1, IOService<?> o2) {\n\t\t\treturn o1.getUniqueId().compareTo(o2.getUniqueId());\n\t\t}\n\t}\n\n\t@TODO(note = \"ExecutionException is poorly implemented.\")\n\tprotected class ResultsListener\n\t\t\textends Thread {\n\n\t\tpublic ResultsListener(String name) {\n\t\t\tsuper();\n\t\t\tsetName(name);\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tfor (; ; ) {\n\t\t\t\ttry {\n\t\t\t\t\tIOService<?> service = completionService.take().get();\n\n\t\t\t\t\tif (service != null) {\n\t\t\t\t\t\tif (service.isConnected()) {\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"COMPLETED: {0}\", service.getIO());\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\taddSocketService(service);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"REMOVED: {0}\", service.getIO());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}    // end of else\n\t\t\t\t\t}\n\t\t\t\t} catch (ExecutionException e) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Protocol execution exception.\", e.getCause());\n\n\t\t\t\t\t// TODO: Do something with this\n\t\t\t\t}        // end of catch\n\t\t\t\tcatch (InterruptedException e) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Protocol execution interrupted.\", e);\n\t\t\t\t}        // end of try-catch\n\t\t\t\tcatch (Exception e) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Protocol execution unknown exception.\", e);\n\t\t\t\t}        // end of catch\n\t\t\t}          // end of for ()\n\t\t}\n\t}\n}    // SocketThread\n\n"
  },
  {
    "path": "src/main/java/tigase/net/SocketType.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.net;\n\nimport java.util.Arrays;\n\n/**\n * Describe class SocketType here.\n * <br>\n * Created: Sun Feb  5 09:27:34 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic enum SocketType {\n\n\tplain,\n\tssl,\n\ttls;\n\n\tpublic static String[] names() {\n\t\tString[] names = new String[values().length];\n\t\tSocketType[] values = values();\n\t\tArrays.setAll(names, i -> values[i].toString());\n\t\treturn names;\n\t}\n} // SocketType\n"
  },
  {
    "path": "src/main/java/tigase/osgi/AbstractActivator.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.osgi;\n\nimport org.osgi.framework.*;\nimport tigase.osgi.util.ClassUtil;\n\nimport java.lang.reflect.Modifier;\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\n/**\n * Common activator which should be extended by any OSGi module which will be used by Tigase XMPP Server in OSGi mode.\n * <br>\n * Created by andrzej on 08.09.2016.\n */\npublic abstract class AbstractActivator\n\t\timplements BundleActivator, ServiceListener {\n\n\tprivate static final Logger log = Logger.getLogger(Activator.class.getCanonicalName());\n\tprivate static Predicate<Class> PUBLIC_AND_NOT_ABSTRACT = (cls -> {\n\t\tint mod = cls.getModifiers();\n\t\treturn !Modifier.isAbstract(mod) && Modifier.isPublic(mod);\n\t});\n\tprotected Set<Class<?>> classesToExport = null;\n\tprivate BundleContext context = null;\n\tprivate ModulesManager serviceManager = null;\n\tprivate ServiceReference serviceReference = null;\n\n\t@Override\n\tpublic void start(BundleContext bc) throws Exception {\n\t\tsynchronized (this) {\n\t\t\tcontext = bc;\n\t\t\tbc.addServiceListener(this, \"(&(objectClass=\" + ModulesManager.class.getName() + \"))\");\n\t\t\tserviceReference = bc.getServiceReference(ModulesManager.class.getName());\n\t\t\tif (serviceReference != null) {\n\t\t\t\tserviceManager = (ModulesManager) bc.getService(serviceReference);\n\t\t\t\tregisterAddons();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void stop(BundleContext bc) throws Exception {\n\t\tsynchronized (this) {\n\t\t\tif (serviceManager != null) {\n\t\t\t\tunregisterAddons();\n\t\t\t\tcontext.ungetService(serviceReference);\n\t\t\t\tserviceManager = null;\n\t\t\t\tserviceReference = null;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void serviceChanged(ServiceEvent event) {\n\t\tif (event.getType() == ServiceEvent.REGISTERED) {\n\t\t\tif (serviceReference == null) {\n\t\t\t\tserviceReference = event.getServiceReference();\n\t\t\t\tserviceManager = (ModulesManager) context.getService(serviceReference);\n\t\t\t\tregisterAddons();\n\t\t\t}\n\t\t} else if (event.getType() == ServiceEvent.UNREGISTERING) {\n\t\t\tif (serviceReference == event.getServiceReference()) {\n\t\t\t\tunregisterAddons();\n\t\t\t\tcontext.ungetService(serviceReference);\n\t\t\t\tserviceManager = null;\n\t\t\t\tserviceReference = null;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void registerAddons() {\n\t\tif (serviceManager != null) {\n\t\t\tif (classesToExport == null) {\n\t\t\t\tclassesToExport = ClassUtil.getClassesFromBundle(context.getBundle())\n\t\t\t\t\t\t.stream()\n\t\t\t\t\t\t.filter(PUBLIC_AND_NOT_ABSTRACT)\n\t\t\t\t\t\t.collect(Collectors.toSet());\n\t\t\t}\n\t\t\tclassesToExport.forEach(cls -> serviceManager.registerClass(cls));\n\t\t\tserviceManager.update();\n\t\t}\n\t}\n\n\tprivate void unregisterAddons() {\n\t\tif (serviceManager != null) {\n\t\t\tif (classesToExport != null) {\n\t\t\t\tclassesToExport.forEach(cls -> serviceManager.unregisterClass(cls));\n\t\t\t}\n\t\t\tserviceManager.update();\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/main/java/tigase/osgi/Activator.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.osgi;\n\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.BundleActivator;\nimport org.osgi.framework.BundleContext;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@tigase.annotations.TigaseDeprecated(since = \"8.1.0\", removeIn = \"9.0.0\", note = \"Remove OSGi support which is disabled since 8.1.0\")\n@Deprecated\npublic class Activator\n\t\timplements BundleActivator {\n\n\tprivate static final Logger log = Logger.getLogger(Activator.class.getCanonicalName());\n\n\tprivate static Bundle bundle = null;\n\n\tpublic static Bundle getBundle() {\n\t\treturn bundle;\n\t}\n\n\t@Override\n\tpublic void start(BundleContext bc) throws Exception {\n\t\ttry {\n\t\t\tlog.severe(\"Tigase XMPP Server does not support OSGi any more! Please consider the usage of Tigase XMPP Server in standalone mode with HTTP API for required integration. If you have additional questions or you would like support please contact us by sending an email to support@tigase.net.\");\n\t\t\tthrow new RuntimeException(\"OSGi mode not supported!\");\n//\t\t\tMBeanServer mbs = ManagementFactory.getPlatformMBeanServer();\n//\t\t\tbc.registerService(MBeanServer.class.getName(), mbs, null);\n//\n//\t\t\tbundle = bc.getBundle();\n//\n//\t\t\tif (!SLF4JBridgeHandler.isInstalled()) {\n//\t\t\t\tSLF4JBridgeHandler.install();\n//\t\t\t}\n//\n//\t\t\tXMPPServer.setOSGi(true);\n//\n//\t\t\t// we need to export this before we start, so if start will fail due to missing\n//\t\t\t// dependencies we would be able to add them later and recorver from this\n//\t\t\tModulesManagerImpl.getInstance().setActive(true);\n//\t\t\tbc.registerService(ModulesManager.class.getName(), ModulesManagerImpl.getInstance(), new Hashtable());\n//\n//\t\t\tXMPPServer.start(new String[0]);\n//\n//\t\t\t// if it is not too late\n//\t\t\tSLF4JBridgeHandler.install();\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.SEVERE, \"Error starting bundle: \", ex);\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void stop(BundleContext bc) throws Exception {\n//\t\ttry {\n//\t\t\tModulesManagerImpl.getInstance().setActive(false);\n//\t\t\tXMPPServer.stop();\n//\t\t} catch (Exception ex) {\n//\t\t\tlog.log(Level.SEVERE, \"Error stopping bundle: \", ex);\n//\t\t\tthrow ex;\n//\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/osgi/ModulesManager.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.osgi;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.conf.Configurable;\nimport tigase.xmpp.XMPPImplIfc;\n\n/**\n * @author andrzej\n */\npublic interface ModulesManager {\n\n\tvoid registerClass(Class<?> cls);\n\n\tvoid unregisterClass(Class<?> cls);\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tvoid registerPluginClass(Class<? extends XMPPImplIfc> pluginCls);\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tvoid unregisterPluginClass(Class<? extends XMPPImplIfc> pluginCls);\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tvoid registerServerComponentClass(Class<? extends Configurable> compCls);\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tvoid unregisterServerComponentClass(Class<? extends Configurable> compCls);\n\n\tvoid update();\n\n\tClass<?> forName(String className) throws ClassNotFoundException;\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/osgi/ModulesManagerImpl.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.osgi;\n\nimport tigase.conf.Configurable;\nimport tigase.kernel.beans.config.AbstractBeanConfigurator;\nimport tigase.xmpp.XMPPImplIfc;\n\nimport java.util.Collection;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.logging.Logger;\n\n/**\n * @author andrzej\n */\npublic class ModulesManagerImpl\n\t\timplements ModulesManager {\n\n\tprivate static final Logger log = Logger.getLogger(ModulesManagerImpl.class.getCanonicalName());\n\n\tprivate static ModulesManagerImpl instance = null;\n\tprivate boolean active = false;\n\tprivate AbstractBeanConfigurator beanConfigurator;\n\tprivate ConcurrentHashMap<String, Class<?>> classes = null;\n\n\tpublic static ModulesManagerImpl getInstance() {\n\t\tif (instance == null) {\n\t\t\tinstance = new ModulesManagerImpl();\n\t\t}\n\t\treturn instance;\n\t}\n\n\tprivate ModulesManagerImpl() {\n\t\tclasses = new ConcurrentHashMap<String, Class<?>>();\n\t}\n\n\tpublic Collection<Class<?>> getClasses() {\n\t\treturn classes.values();\n\t}\n\n\t@Override\n\tpublic void registerPluginClass(Class<? extends XMPPImplIfc> pluginCls) {\n\t\tregisterClass(pluginCls);\n\t}\n\n\t@Override\n\tpublic void unregisterPluginClass(Class<? extends XMPPImplIfc> pluginClass) {\n\t\tunregisterClass(pluginClass);\n\t}\n\n\t@Override\n\tpublic void registerServerComponentClass(Class<? extends Configurable> compCls) {\n\t\tregisterClass(compCls);\n\t}\n\n\t@Override\n\tpublic void unregisterServerComponentClass(Class<? extends Configurable> compCls) {\n\t\tunregisterClass(compCls);\n\t}\n\n\t@Override\n\tpublic void registerClass(Class<?> cls) {\n\t\tsynchronized (this) {\n\t\t\tString clsName = cls.getCanonicalName();\n\t\t\tclasses.put(clsName, cls);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void unregisterClass(Class<?> cls) {\n\t\tsynchronized (this) {\n\t\t\tString clsName = cls.getCanonicalName();\n\t\t\tclasses.remove(clsName, cls);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Class<?> forName(String className) throws ClassNotFoundException {\n\t\tif (\"tigase.cluster.strategy.OnlineUsersCachingStrategy\".equals(className)) {\n\t\t\tlog.warning(\"You are using old name for SM clustering strategy in property \" +\n\t\t\t\t\t\t\t\t\"--sm-cluster-strategy-class\\nYou are using name: \" + className + \"\\n\" +\n\t\t\t\t\t\t\t\t\" while name: tigase.server.cluster.strategy.OnlineUsersCachingStrategy\" +\n\t\t\t\t\t\t\t\t\" should be used.\");\n\t\t\tclassName = \"tigase.server.cluster.strategy.OnlineUsersCachingStrategy\";\n\t\t}\n\t\tClass<?> cls = classes.get(className);\n\t\tif (cls == null) {\n\t\t\tcls = this.getClass().getClassLoader().loadClass(className);\n\t\t}\n\t\treturn cls;\n\t}\n\n\t@Override\n\tpublic void update() {\n\t\t//synchronized (this) {\n\t\tif (active && beanConfigurator != null) {\n\t\t\tbeanConfigurator.configurationChanged();\n\t\t}\n\t\t//}\n\t}\n\n\tpublic void setBeanConfigurator(AbstractBeanConfigurator beanConfigurator) {\n\t\tthis.beanConfigurator = beanConfigurator;\n\t}\n\n\tpublic void setActive(boolean active) {\n\t\tthis.active = active;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/osgi/OSGiScriptEngine.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.osgi;\n\nimport javax.script.*;\nimport java.io.Reader;\n\npublic class OSGiScriptEngine\n\t\timplements ScriptEngine {\n\n\tprivate ScriptEngine engine;\n\tprivate OSGiScriptEngineFactory factory;\n\n\tpublic OSGiScriptEngine(ScriptEngine engine, OSGiScriptEngineFactory factory) {\n\t\tthis.engine = engine;\n\t\tthis.factory = factory;\n\t}\n\n\tpublic Bindings createBindings() {\n\t\treturn engine.createBindings();\n\t}\n\n\tpublic Object eval(Reader reader, Bindings n) throws ScriptException {\n\t\treturn engine.eval(reader, n);\n\t}\n\n\tpublic Object eval(Reader reader, ScriptContext context) throws ScriptException {\n\t\treturn engine.eval(reader, context);\n\t}\n\n\tpublic Object eval(Reader reader) throws ScriptException {\n\t\treturn engine.eval(reader);\n\t}\n\n\tpublic Object eval(String script, Bindings n) throws ScriptException {\n\t\treturn engine.eval(script, n);\n\t}\n\n\tpublic Object eval(String script, ScriptContext context) throws ScriptException {\n\t\treturn engine.eval(script, context);\n\t}\n\n\tpublic Object eval(String script) throws ScriptException {\n\t\treturn engine.eval(script);\n\t}\n\n\tpublic Object get(String key) {\n\t\treturn engine.get(key);\n\t}\n\n\tpublic Bindings getBindings(int scope) {\n\t\treturn engine.getBindings(scope);\n\t}\n\n\tpublic ScriptContext getContext() {\n\t\treturn engine.getContext();\n\t}\n\n\tpublic void setContext(ScriptContext context) {\n\t\tengine.setContext(context);\n\t}\n\n\tpublic ScriptEngineFactory getFactory() {\n\t\treturn factory;\n\t}\n\n\tpublic void put(String key, Object value) {\n\t\tengine.put(key, value);\n\t}\n\n\tpublic void setBindings(Bindings bindings, int scope) {\n\t\tengine.setBindings(bindings, scope);\n\t}\n\n}"
  },
  {
    "path": "src/main/java/tigase/osgi/OSGiScriptEngineFactory.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.osgi;\n\nimport javax.script.ScriptEngine;\nimport javax.script.ScriptEngineFactory;\nimport java.util.List;\n\n/**\n * This is a wrapper class for the ScriptEngineFactory class that deals with context class loader issues It is necessary\n * because engines (at least ruby) use the context classloader to find their resources (i.e., their \"native\" classes)\n */\npublic class OSGiScriptEngineFactory\n\t\timplements ScriptEngineFactory {\n\n\tprivate ClassLoader contextClassLoader;\n\tprivate ScriptEngineFactory factory;\n\n\tpublic OSGiScriptEngineFactory(ScriptEngineFactory factory, ClassLoader contextClassLoader) {\n\t\tthis.factory = factory;\n\t\tthis.contextClassLoader = contextClassLoader;\n\t}\n\n\tpublic String getEngineName() {\n\t\treturn factory.getEngineName();\n\t}\n\n\tpublic String getEngineVersion() {\n\t\treturn factory.getEngineVersion();\n\t}\n\n\tpublic List<String> getExtensions() {\n\t\treturn factory.getExtensions();\n\t}\n\n\tpublic String getLanguageName() {\n\t\treturn factory.getLanguageName();\n\t}\n\n\tpublic String getLanguageVersion() {\n\t\treturn factory.getLanguageVersion();\n\t}\n\n\tpublic String getMethodCallSyntax(String obj, String m, String... args) {\n\t\treturn factory.getMethodCallSyntax(obj, m, args);\n\t}\n\n\tpublic List<String> getMimeTypes() {\n\t\treturn factory.getMimeTypes();\n\t}\n\n\tpublic List<String> getNames() {\n\t\treturn factory.getNames();\n\t}\n\n\tpublic String getOutputStatement(String toDisplay) {\n\t\treturn factory.getOutputStatement(toDisplay);\n\t}\n\n\tpublic Object getParameter(String key) {\n\t\treturn factory.getParameter(key);\n\t}\n\n\tpublic String getProgram(String... statements) {\n\t\treturn factory.getProgram(statements);\n\t}\n\n\tpublic ScriptEngine getScriptEngine() {\n\t\tScriptEngine engine = null;\n\t\tif (contextClassLoader != null) {\n\t\t\tClassLoader old = Thread.currentThread().getContextClassLoader();\n\t\t\tThread.currentThread().setContextClassLoader(contextClassLoader);\n\t\t\tengine = factory.getScriptEngine();\n\t\t\tThread.currentThread().setContextClassLoader(old);\n\t\t} else {\n\t\t\tengine = factory.getScriptEngine();\n\t\t}\n\t\treturn engine;\n\t}\n\n}"
  },
  {
    "path": "src/main/java/tigase/osgi/OSGiScriptEngineManager.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.osgi;\n\nimport org.osgi.framework.Bundle;\nimport org.osgi.framework.BundleContext;\n\nimport javax.script.*;\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.net.URL;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * This class acts as a delegate for all the available ScriptEngineManagers. Unluckily, the standard did not define it\n * as an interface, so we need to extend it to allow polymorphism. However, no calls to super are used. It wraps all\n * available ScriptEngineManagers in the OSGi ServicePlatform into a merged ScriptEngineManager.\n * <br>\n * Internally, what this class does is creating ScriptEngineManagers for each bundle that contains a ScriptEngineFactory\n * and includes a META-INF/services/javax.script.ScriptEngineFactory file. It assumes that the file contains a list of\n * {@link} ScriptEngineFactory classes. For each bundle, it creates a ScriptEngineManager, then merges them. @link\n * ScriptEngineFactory objects are wrapped into @link OSGiScriptEngineFactory objects to deal with problems of context\n * class loader: Those scripting engines that rely on the ContextClassloader for finding resources need to use this\n * wrapper and the @link OSGiScriptFactory. Mainly, jruby does.\n * <br>\n * Note that even if no context classloader issues arose, it would still be needed to search manually for the factories\n * and either use them directly (losing the mimeType/extension/shortName mechanisms for finding engines or manually\n * registering them) or still use this class, which would be smarter. In the latter case, it would only be needed to\n * remove the hack that temporarily sets the context classloader to the appropriate, bundle-related, class loader.\n * <br>\n * Caveats: <ul><li> All factories are wrapped with an {@link OSGiScriptEngineFactory}. As Engines are not wrapped,\n * calls like <code> ScriptEngineManager osgiManager=new OSGiScriptEngineManager(context);<br> ScriptEngine\n * engine=osgiManager.getEngineByName(\"ruby\"); ScriptEngineFactory factory=engine.getFactory() //this does not return\n * the OSGiFactory wrapper factory.getScriptEngine(); //this might fail, as it does not use OSGiScriptEngineFactory\n * wrapper </code> might result in unexpected errors. Future versions may wrap the ScriptEngine with a OSGiScriptEngine\n * to solve this issue, but for the moment it is not needed. </li> </ul>\n */\npublic class OSGiScriptEngineManager\n\t\textends ScriptEngineManager {\n\n\tprivate static final Logger log = Logger.getLogger(OSGiScriptEngineManager.class.getCanonicalName());\n\n\tprivate Bindings bindings;\n\tprivate Map<ScriptEngineManager, ClassLoader> classLoaders;\n\tprivate BundleContext context;\n\n\tpublic OSGiScriptEngineManager() {\n\t\tthis(Activator.getBundle().getBundleContext());\n\t}\n\n\tpublic OSGiScriptEngineManager(BundleContext context) {\n\t\tthis.context = context;\n\t\tbindings = new SimpleBindings();\n\t\tthis.classLoaders = findManagers(context);\n\t}\n\n\t/**\n\t * This method is the only one that is visible and not part of the ScriptEngineManager class. Its purpose is to find\n\t * new managers that weren't available before, but keeping the globalScope bindings set. If you want to clean the\n\t * bindings you can either get a fresh instance of OSGiScriptManager or setting up a new bindings object. This can\n\t * be done with: <code> ScriptEngineManager manager=new OSGiScriptEngineManager(context); (...)//do stuff\n\t * osgiManager=(OSGiScriptEngineManager)manager;//cast to ease reading osgiManager.reloadManagers();\n\t * <br>\n\t * manager.setBindings(new OSGiBindings());//or you can use your own bindings implementation\n\t * <br>\n\t * </code>\n\t */\n\tpublic void reloadManagers() {\n\t\tthis.classLoaders = findManagers(context);\n\t}\n\n\tpublic Object get(String key) {\n\t\treturn bindings.get(key);\n\t}\n\n\tpublic Bindings getBindings() {\n\t\treturn bindings;\n\t}\n\n\t/**\n\t * Follows the same behavior of @link javax.script.ScriptEngineManager#setBindings(Bindings) This means that the\n\t * same bindings are applied to all the underlying managers.\n\t *\n\t */\n\tpublic void setBindings(Bindings bindings) {\n\t\tthis.bindings = bindings;\n\t\tfor (ScriptEngineManager manager : classLoaders.keySet()) {\n\t\t\tmanager.setBindings(bindings);\n\t\t}\n\n\t}\n\n\tpublic ScriptEngine getEngineByExtension(String extension) {\n\t\t//TODO this is a hack to deal with context class loader issues\n\t\tScriptEngine engine = null;\n\t\tfor (ScriptEngineManager manager : classLoaders.keySet()) {\n\t\t\tClassLoader old = Thread.currentThread().getContextClassLoader();\n\t\t\tThread.currentThread().setContextClassLoader(classLoaders.get(manager));\n\t\t\tengine = manager.getEngineByExtension(extension);\n\t\t\tThread.currentThread().setContextClassLoader(old);\n\t\t\tif (engine != null) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn engine;\n\t}\n\n\tpublic ScriptEngine getEngineByMimeType(String mimeType) {\n\t\t//TODO this is a hack to deal with context class loader issues\n\t\tScriptEngine engine = null;\n\t\tfor (ScriptEngineManager manager : classLoaders.keySet()) {\n\t\t\tClassLoader old = Thread.currentThread().getContextClassLoader();\n\t\t\tThread.currentThread().setContextClassLoader(classLoaders.get(manager));\n\t\t\tengine = manager.getEngineByMimeType(mimeType);\n\t\t\tThread.currentThread().setContextClassLoader(old);\n\t\t\tif (engine != null) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn engine;\n\t}\n\n\tpublic ScriptEngine getEngineByName(String shortName) {\n\t\t//TODO this is a hack to deal with context class loader issues\n\t\tfor (ScriptEngineManager manager : classLoaders.keySet()) {\n\t\t\tClassLoader old = Thread.currentThread().getContextClassLoader();\n\t\t\tThread.currentThread().setContextClassLoader(classLoaders.get(manager));\n\t\t\tScriptEngine engine = manager.getEngineByName(shortName);\n\t\t\tThread.currentThread().setContextClassLoader(old);\n\t\t\tif (engine != null) {\n\t\t\t\treturn new OSGiScriptEngine(engine, new OSGiScriptEngineFactory(engine.getFactory(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassLoaders.get(manager)));\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic List<ScriptEngineFactory> getEngineFactories() {\n\t\tList<ScriptEngineFactory> osgiFactories = new ArrayList<ScriptEngineFactory>();\n\t\tfor (ScriptEngineManager engineManager : classLoaders.keySet()) {\n\t\t\tfor (ScriptEngineFactory factory : engineManager.getEngineFactories()) {\n\t\t\t\tosgiFactories.add(new OSGiScriptEngineFactory(factory, classLoaders.get(engineManager)));\n\t\t\t}\n\t\t}\n\t\treturn osgiFactories;\n\t}\n\n\tpublic void put(String key, Object value) {\n\t\tbindings.put(key, value);\n\t}\n\n\tpublic void registerEngineExtension(String extension, ScriptEngineFactory factory) {\n\t\tfor (ScriptEngineManager engineManager : classLoaders.keySet()) {\n\t\t\tengineManager.registerEngineExtension(extension, factory);\n\t\t}\n\t}\n\n\tpublic void registerEngineMimeType(String type, ScriptEngineFactory factory) {\n\t\tfor (ScriptEngineManager engineManager : classLoaders.keySet()) {\n\t\t\tengineManager.registerEngineMimeType(type, factory);\n\t\t}\n\t}\n\n\tpublic void registerEngineName(String name, ScriptEngineFactory factory) {\n\t\tfor (ScriptEngineManager engineManager : classLoaders.keySet()) {\n\t\t\tengineManager.registerEngineName(name, factory);\n\t\t}\n\t}\n\n\tprivate Map<ScriptEngineManager, ClassLoader> findManagers(BundleContext context) {\n\t\tMap<ScriptEngineManager, ClassLoader> managers = new HashMap<ScriptEngineManager, ClassLoader>();\n\t\ttry {\n\t\t\tfor (String factoryName : findFactoryCandidates(context)) {\n\t\t\t\t//We do not really need the class, but we need the classloader \n\t\t\t\tClassLoader factoryLoader = Class.forName(factoryName).getClassLoader();\n\t\t\t\tfactoryLoader = new CustomClassLoader(\n\t\t\t\t\t\tnew ClassLoader[]{factoryLoader, this.getClass().getClassLoader()}, factoryLoader);\n\t\t\t\tScriptEngineManager manager = new ScriptEngineManager(factoryLoader);\n\t\t\t\tmanager.setBindings(bindings);\n\t\t\t\tmanagers.put(manager, factoryLoader);\n\t\t\t}\n\t\t\treturn managers;\n\t\t} catch (IOException ioe) {\n\t\t\tthrow new RuntimeException(ioe);\n\t\t} catch (ClassNotFoundException cnfe) {\n\t\t\tthrow new RuntimeException(cnfe);\n\t\t}\n\t}\n\n\t/**\n\t * Iterates through all bundles to get the available @link ScriptEngineFactory classes\n\t *\n\t * @return the names of the available ScriptEngineFactory classes\n\t *\n\t */\n\tprivate List<String> findFactoryCandidates(BundleContext context) throws IOException {\n\t\tBundle[] bundles = context.getBundles();\n\t\tList<String> factoryCandidates = new ArrayList<String>();\n\t\tfor (Bundle bundle : bundles) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"checking bundle for script engine manager - Id: {0} Bundle: {1}  Version: {2}\",\n\t\t\t\t\t\tnew Object[]{bundle.getBundleId(), bundle.getSymbolicName(), bundle.getVersion().toString()});\n\t\t\t}\n\t\t\tif (bundle.getSymbolicName() == null || bundle.getSymbolicName().equals(\"system.bundle\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tEnumeration urls = bundle.findEntries(\"META-INF/services\", \"javax.script.ScriptEngineFactory\", false);\n\t\t\tif (urls == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\twhile (urls.hasMoreElements()) {\n\t\t\t\tURL u = (URL) urls.nextElement();\n\t\t\t\tBufferedReader reader = new BufferedReader(new InputStreamReader(u.openStream()));\n\t\t\t\tString line;\n\t\t\t\twhile ((line = reader.readLine()) != null) {\n\t\t\t\t\tfactoryCandidates.add(line.trim());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn factoryCandidates;\n\t}\n\n\tprivate class CustomClassLoader\n\t\t\textends ClassLoader {\n\n\t\tprivate final ClassLoader[] loaders;\n\n\t\tpublic CustomClassLoader(ClassLoader[] loaders, ClassLoader mainLoader) {\n\t\t\tsuper(mainLoader);\n\t\t\tthis.loaders = loaders;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> loadClass(String name) throws ClassNotFoundException {\n\t\t\treturn findClass(name);\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {\n\t\t\treturn findClass(name);\n\t\t}\n\n\t\t@Override\n\t\tprotected Class<?> findClass(String name) throws ClassNotFoundException {\n\t\t\tClass<?> cls = null;\n\t\t\tfor (ClassLoader cl : loaders) {\n\t\t\t\ttry {\n\t\t\t\t\tcls = cl.loadClass(name);\n\t\t\t\t\tif (cls != null) {\n\t\t\t\t\t\treturn cls;\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (cls == null) {\n\t\t\t\treturn findSystemClass(name);\n\t\t\t}\n\n\t\t\treturn cls;\n\t\t}\n\n\t}\n}"
  },
  {
    "path": "src/main/java/tigase/osgi/util/ClassUtil.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.osgi.util;\n\nimport org.osgi.framework.Bundle;\nimport tigase.osgi.Activator;\nimport tigase.util.ClassComparator;\nimport tigase.util.ObjectComparator;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Modifier;\nimport java.net.URL;\nimport java.util.*;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarFile;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * <code>ClassUtil</code> file contains code used for loading all implementations of specified <em>interface</em> or\n * <em>abstract class</em> found in classpath. As a result of calling some functions you can have <code>Set</code>\n * containing all required classes.\n * <br>\n * <p> Created: Wed Oct  6 08:25:52 2004 </p>\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n * @version $Rev: 609 $\n */\npublic class ClassUtil {\n\n\tprivate static final Logger log = Logger.getLogger(ClassUtil.class.getName());\n\n\tprivate static final String[] SKIP_CONTAINS = {\".ui.\", \".swing\", \".awt\", \".sql.\", \".xml.\", \".terracotta.\"};\n\tprivate static final String[] SKIP_STARTS = {\"com.mysql\", \"tigase.pubsub.Utils\", \"org.apache.derby\",\n\t\t\t\t\t\t\t\t\t\t\t\t \"org.apache.xml\", \"org.postgresql\", \"com.sun\", \"groovy\",\n\t\t\t\t\t\t\t\t\t\t\t\t \"org.codehaus.groovy\", \"org.netbeans\", \"org.python\", \"com.google.gwt\",\n\t\t\t\t\t\t\t\t\t\t\t\t \"xtigase.http.client.BasePanel\", \"org.osgi\"};\n\n\tpublic static String getClassNameFromFileName(String fileName) {\n\t\tString class_name = null;\n\t\tif (fileName.endsWith(\".class\")) {\n//       class_name = fileName.substring(0,\n//         fileName.length()-6).replace(File.separatorChar, '.');\n\t\t\t// Above code does not works on MS Windows if we load\n\t\t\t// files from jar file. Jar manipulation code always returns\n\t\t\t// file names with unix style separators\n\t\t\tint off = fileName.charAt(0) == '/' ? 1 : 0;\n\t\t\tString tmp_class_name = fileName.substring(off, fileName.length() - 6).replace('\\\\', '.');\n\t\t\tclass_name = tmp_class_name.replace('/', '.');\n\t\t} // end of if (entry_name.endsWith(\".class\"))\n\t\treturn class_name;\n\t}\n\n\tpublic static Set<String> getClassNamesFromDir(File dir) {\n\t\tSet<String> tmp_set = getFileListDeep(dir);\n\t\tSet<String> result = new TreeSet<String>();\n\t\tfor (String elem : tmp_set) {\n\t\t\tString class_name = getClassNameFromFileName(elem);\n\t\t\tif (class_name != null) {\n\t\t\t\tresult.add(class_name);\n\t\t\t\tif (class_name.contains(\"Plugin\")) {\n\t\t\t\t\tSystem.out.println(\"class name: \" + class_name);\n\t\t\t\t}\n\t\t\t} // end of if (class_name != null)\n\t\t} // end of for ()\n\t\treturn result;\n\t}\n\n\tpublic static Set<String> getClassNamesFromJar(File jarFile) throws IOException {\n\t\tSet<String> result = new TreeSet<String>();\n\t\tJarFile jar = new JarFile(jarFile);\n\t\tEnumeration<JarEntry> jar_entries = jar.entries();\n\t\twhile (jar_entries.hasMoreElements()) {\n\t\t\tJarEntry jar_entry = jar_entries.nextElement();\n\t\t\tString class_name = getClassNameFromFileName(jar_entry.getName());\n\t\t\tif (class_name != null) {\n\t\t\t\tresult.add(class_name);\n\t\t\t\t//        System.out.println(\"class name: \"+class_name);\n\t\t\t} // end of if (entry_name.endsWith(\".class\"))\n\t\t} // end of while (jar_entries.hasMoreElements())\n\n\t\treturn result;\n\t}\n\n\tpublic static Set<String> getClassNamesFromWar(File jarFile) throws IOException {\n\t\tSet<String> result = new TreeSet<String>();\n\t\tJarFile jar = new JarFile(jarFile);\n\t\tEnumeration<JarEntry> jar_entries = jar.entries();\n\t\twhile (jar_entries.hasMoreElements()) {\n\t\t\tJarEntry jar_entry = jar_entries.nextElement();\n\t\t\tString name = jar_entry.getName();\n\t\t\tif (!name.startsWith(\"WEB-INF/classes/\")) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tname = name.substring(16);\n\t\t\tString class_name = getClassNameFromFileName(name);\n\t\t\tif (class_name != null) {\n\t\t\t\tresult.add(class_name);\n\t\t\t\t//        System.out.println(\"class name: \"+class_name);\n\t\t\t} // end of if (entry_name.endsWith(\".class\"))\n\t\t} // end of while (jar_entries.hasMoreElements())\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Scans file for classes annotated with annotation T\n\t *\n\t *\t *\t * @param loader class loader used to load classes\n\t * @param f file to scan for classes\n\t * @param cls annotation class\n\t *\n\t * @return set of annotated classes\n\t *\n\t */\n\t@SuppressWarnings({\"unchecked\"})\n\tpublic static <T extends Class, S extends Class> Set<S> getClassesAnnotated(ClassLoader loader, File f, T cls)\n\t\t\tthrows IOException, ClassNotFoundException {\n\n\t\tSet<String> class_names = (f.getName().endsWith(\".war\"))\n\t\t\t\t\t\t\t\t  ? ClassUtil.getClassNamesFromWar(f)\n\t\t\t\t\t\t\t\t  : ClassUtil.getClassNamesFromJar(f);\n\t\tSet<Class> classes = ClassUtil.getClassesFromNames(loader, class_names);\n\t\tSet<S> classes_set = new TreeSet<S>(new ClassComparator());\n\n\t\tfor (Class c : classes) {\n\t\t\tAnnotation[] annots = c.getAnnotations();\n\t\t\tif (c.isAnnotationPresent(cls)) {\n\t\t\t\tint mod = c.getModifiers();\n\t\t\t\tif (!Modifier.isAbstract(mod) && !Modifier.isInterface(mod)) {\n\t\t\t\t\tclasses_set.add((S) c);\n\t\t\t\t} // end of if (!Modifier.isAbstract(mod) && !Modifier.isInterface(mod))\n\t\t\t} // end of if (cls.isAssignableFrom(c))\n\t\t} // end of for ()\n\n\t\treturn classes_set;\n\t}\n\n\t/**\n\t * Scans OSGI bundle for classes annotated with annotation T\n\t *\n\t *\t *\t * @param bundle bundle to scan for annotated classes\n\t * @param cls annotation class\n\t *\n\t * @return set of annotated classes\n\t *\n\t */\n\t@SuppressWarnings({\"unchecked\"})\n\tpublic static <T extends Class, S extends Class> Set<S> getClassesAnnotated(Bundle bundle, T cls)\n\t\t\tthrows IOException, ClassNotFoundException {\n\n\t\tSet<S> classes_set = new TreeSet<S>(new ClassComparator());\n\t\tEnumeration e = bundle.findEntries(\"/\", \"*.class\", true);\n\t\tif (e != null) {\n\t\t\twhile (e.hasMoreElements()) {\n\t\t\t\tURL clsUrl = (URL) e.nextElement();\n\t\t\t\tString clsName = getClassNameFromFileName(clsUrl.getPath());\n\n\t\t\t\tboolean skip_class = false;\n\t\t\t\tfor (String prefix : SKIP_STARTS) {\n\t\t\t\t\tif (clsName.startsWith(prefix)) {\n\t\t\t\t\t\tskip_class = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (skip_class) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tfor (String part : SKIP_CONTAINS) {\n\t\t\t\t\tif (clsName.contains(part)) {\n\t\t\t\t\t\tskip_class = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (skip_class) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tClass c = bundle.loadClass(clsName);\n\t\t\t\t\tAnnotation[] annots = c.getAnnotations();\n\t\t\t\t\tif (c.isAnnotationPresent(cls)) {\n\t\t\t\t\t\tint mod = c.getModifiers();\n\t\t\t\t\t\tif (!Modifier.isAbstract(mod) && !Modifier.isInterface(mod)) {\n\t\t\t\t\t\t\tclasses_set.add((S) c);\n\t\t\t\t\t\t} // end of if (!Modifier.isAbstract(mod) && !Modifier.isInterface(mod))\n\t\t\t\t\t} // end of if (cls.isAssignableFrom(c))\n\t\t\t\t} catch (ClassNotFoundException ex) {\n\t\t\t\t\tLogger.getLogger(ClassUtil.class.getCanonicalName()).warning(\"Could not find class = \" + clsName);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn classes_set;\n\t}\n\n\tpublic static Set<Class<?>> getClassesFromBundle(Bundle bundle) {\n\t\tSet<Class<?>> classes_set = new TreeSet<>(new ClassComparator());\n\t\tEnumeration e = bundle.findEntries(\"/\", \"*.class\", true);\n\t\tif (e != null) {\n\t\t\twhile (e.hasMoreElements()) {\n\t\t\t\tURL clsUrl = (URL) e.nextElement();\n\t\t\t\tString clsName = getClassNameFromFileName(clsUrl.getPath());\n\n\t\t\t\tboolean skip_class = false;\n\t\t\t\tfor (String prefix : SKIP_STARTS) {\n\t\t\t\t\tif (clsName.startsWith(prefix)) {\n\t\t\t\t\t\tskip_class = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (skip_class) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tfor (String part : SKIP_CONTAINS) {\n\t\t\t\t\tif (clsName.contains(part)) {\n\t\t\t\t\t\tskip_class = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (skip_class) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tClass c = bundle.loadClass(clsName);\n\t\t\t\t\tint mod = c.getModifiers();\n\t\t\t\t\tif (!Modifier.isAbstract(mod) && !Modifier.isInterface(mod)) {\n\t\t\t\t\t\tclasses_set.add(c);\n\t\t\t\t\t} // end of if (!Modifier.isAbstract(mod) && !Modifier.isInterface(mod))\n\t\t\t\t} catch (ClassNotFoundException | NoClassDefFoundError ex) {\n\t\t\t\t\tLogger.getLogger(ClassUtil.class.getCanonicalName()).warning(\"Could not find class = \" + clsName);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn classes_set;\n\t}\n\n\tpublic static Set<Class> getClassesFromClassPath() throws IOException, ClassNotFoundException {\n\n\t\tSet<Class> classes_set = new TreeSet<Class>(new ClassComparator());\n\n\t\tString classpath = System.getProperty(\"java.class.path\");\n\t\tSystem.out.println(\"classpath: \" + classpath);\n\t\tClassLoader loader = ClassUtil.class.getClassLoader();\n\t\tStringTokenizer stok = new StringTokenizer(classpath, File.pathSeparator, false);\n\t\twhile (stok.hasMoreTokens()) {\n\t\t\tString path = stok.nextToken();\n\t\t\tFile file = new File(path);\n\t\t\tif (file.exists()) {\n\t\t\t\tif (file.isDirectory()) {\n\t\t\t\t\tSystem.out.println(\"directory: \" + path);\n\t\t\t\t\tSet<String> class_names = getClassNamesFromDir(file);\n\t\t\t\t\tclasses_set.addAll(getClassesFromNames(loader, class_names));\n\t\t\t\t} // end of if (file.isDirectory())\n\t\t\t\tif (file.isFile()) {\n\t\t\t\t\t//System.out.println(\"jar file: \"+path);\n\t\t\t\t\tSet<String> class_names = getClassNamesFromJar(file);\n\t\t\t\t\tclasses_set.addAll(getClassesFromNames(loader, class_names));\n\t\t\t\t\t//System.out.println(\"Loaded jar file: \"+path);\n\t\t\t\t} // end of if (file.isFile())\n\t\t\t} // end of if (file.exists())\n\t\t} // end of while (stok.hasMoreTokens())\n\n\t\treturn classes_set;\n\t}\n\n\tpublic static Set<Class> getClassesFromNames(ClassLoader loader, Set<String> names) throws ClassNotFoundException {\n\t\tSet<Class> classes = new TreeSet<Class>(new ClassComparator());\n\t\tfor (String name : names) {\n\t\t\ttry {\n\t\t\t\tboolean skip_class = false;\n\t\t\t\tfor (String test_str : SKIP_CONTAINS) {\n\t\t\t\t\tskip_class = name.contains(test_str);\n\t\t\t\t\tif (skip_class) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!skip_class) {\n\t\t\t\t\tfor (String test_str : SKIP_STARTS) {\n\t\t\t\t\t\tskip_class = name.startsWith(test_str);\n\t\t\t\t\t\tif (skip_class) {\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!skip_class) {\n\t\t\t\t\t//Class cls = Class.forName(name);\n\t\t\t\t\tClass cls = loader.loadClass(name);\n\t\t\t\t\tclasses.add(cls);\n\t\t\t\t}\n\t\t\t} catch (UnsupportedClassVersionError e) {\n\t\t\t\tlog.log(Level.WARNING, \"Class: \" + name + \" compiled using newer JDK version. Please upgrade your JDK!\");\n\t\t\t} catch (NoClassDefFoundError e) {\n\t\t\t\tSystem.out.println(\"Class not found name: \" + name);\n\t\t\t} catch (UnsatisfiedLinkError e) {\n\t\t\t\tSystem.out.println(\"Class unsatisfied name: \" + name);\n\t\t\t} catch (Throwable e) {\n\t\t\t\tThrowable cause = e.getCause();\n\t\t\t\tSystem.out.println(\"Class name: \" + name);\n\t\t\t\te.printStackTrace();\n\t\t\t\tif (cause != null) {\n\t\t\t\t\tcause.printStackTrace();\n\t\t\t\t}\n\t\t\t}\n\t\t} // end of for ()\n\t\treturn classes;\n\t}\n\n\t@SuppressWarnings({\"unchecked\"})\n\tpublic static <T extends Class> Set<T> getClassesImplementing(Collection<Class> classes, T cls) {\n\n\t\tSet<T> classes_set = new TreeSet<T>(new ClassComparator());\n\n\t\tfor (Class c : classes) {\n\t\t\tif (c.getName().contains(\"Plugin\")) {\n\t\t\t\tSystem.out.println(c.getName() + \" \" + cls.isAssignableFrom(c));\n\t\t\t}\n\t\t\tif (cls.isAssignableFrom(c)) {\n\t\t\t\tint mod = c.getModifiers();\n\t\t\t\tif (!Modifier.isAbstract(mod) && !Modifier.isInterface(mod)) {\n\t\t\t\t\tclasses_set.add((T) c);\n\t\t\t\t} // end of if (!Modifier.isAbstract(mod) && !Modifier.isInterface(mod))\n\t\t\t} // end of if (cls.isAssignableFrom(c))\n\t\t} // end of for ()\n\n\t\treturn classes_set;\n\t}\n\n\tpublic static <T extends Class> Set<T> getClassesImplementing(T cls) throws IOException, ClassNotFoundException {\n\t\t// @TODO: Temporary?? fix for xmpp processors loading\n\t\treturn getClassesImplementing(Activator.getBundle(), cls);\n\t}\n\n\t@SuppressWarnings({\"unchecked\"})\n\tpublic static <T extends Class> Set<T> getClassesImplementing(ClassLoader loader, File f, T cls)\n\t\t\tthrows IOException, ClassNotFoundException {\n\n\t\tSet<String> class_names = (f.getName().endsWith(\".war\"))\n\t\t\t\t\t\t\t\t  ? ClassUtil.getClassNamesFromWar(f)\n\t\t\t\t\t\t\t\t  : ClassUtil.getClassNamesFromJar(f);\n\t\tSet<Class> classes = ClassUtil.getClassesFromNames(loader, class_names);\n\n\t\treturn ClassUtil.getClassesImplementing(classes, cls);\n\t}\n\n\t/**\n\t * Scans OSGI bundle for classes implementing class T\n\t *\n\t * @param <T> return type param\n\t * @param bundle bundle to scan\n\t * @param cls base class\n\t *\n\t * @return returns set of implementations\n\t *\n\t */\n\t@SuppressWarnings({\"unchecked\"})\n\tpublic static <T extends Class> Set<T> getClassesImplementing(Bundle bundle, T cls)\n\t\t\tthrows IOException, ClassNotFoundException {\n\n\t\tSet<T> classes_set = new TreeSet<T>(new ClassComparator());\n\t\tEnumeration e = bundle.findEntries(\"/\", \"*.class\", true);\n\t\tif (e != null) {\n\t\t\twhile (e.hasMoreElements()) {\n\t\t\t\tURL clsUrl = (URL) e.nextElement();\n\t\t\t\tString clsName = getClassNameFromFileName(clsUrl.getPath());\n\n\t\t\t\tboolean skip_class = false;\n\t\t\t\tfor (String prefix : SKIP_STARTS) {\n\t\t\t\t\tif (clsName.startsWith(prefix)) {\n\t\t\t\t\t\tskip_class = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (skip_class) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tfor (String part : SKIP_CONTAINS) {\n\t\t\t\t\tif (clsName.contains(part)) {\n\t\t\t\t\t\tskip_class = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (skip_class) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tClass c = bundle.loadClass(clsName);\n\t\t\t\t\tif (cls.isAssignableFrom(c)) {\n\t\t\t\t\t\tint mod = c.getModifiers();\n\t\t\t\t\t\tif (!Modifier.isAbstract(mod) && !Modifier.isInterface(mod)) {\n\t\t\t\t\t\t\tclasses_set.add((T) c);\n\t\t\t\t\t\t} // end of if (!Modifier.isAbstract(mod) && !Modifier.isInterface(mod))\n\t\t\t\t\t} // end of if (cls.isAssignableFrom(c))\n\t\t\t\t} catch (ClassNotFoundException ex) {\n\t\t\t\t\tLogger.getLogger(ClassUtil.class.getCanonicalName()).warning(\"Could not find class = \" + clsName);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn classes_set;\n\t\t//return ClassUtil.getClassesImplementing(classes, cls);\n\t}\n\n\tpublic static Set<String> getFileListDeep(File path) {\n\t\tSet<String> set = new TreeSet<String>();\n\t\tif (path.isDirectory()) {\n\t\t\tString[] files = path.list();\n\t\t\tfor (String file : files) {\n\t\t\t\twalkInDirForFiles(path, file, set);\n\t\t\t} // end of for ()\n\t\t} else {\n\t\t\tset.add(path.toString());\n\t\t} // end of if (file.isDirectory()) else\n\t\treturn set;\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static <T> Set<T> getImplementations(Class<T> obj)\n\t\t\tthrows IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {\n\n\t\tSet<T> result = new TreeSet<T>(new ObjectComparator());\n\n\t\tfor (Class cls : getClassesImplementing(obj)) {\n\t\t\tresult.add((T) cls.newInstance());\n\t\t} // end of for ()\n\t\treturn result;\n\t}\n\n\tpublic static void walkInDirForFiles(File base_dir, String path, Set<String> set) {\n\t\tFile tmp_file = new File(base_dir, path);\n\t\tif (tmp_file.isDirectory()) {\n\t\t\tString[] files = tmp_file.list();\n\t\t\tfor (String file : files) {\n\t\t\t\twalkInDirForFiles(base_dir, new File(path, file).toString(), set);\n\t\t\t} // end of for ()\n\t\t} else {\n\t\t\tif (path.toString().contains(\"Plugin\")) {\n\t\t\t\tSystem.out.println(\"File: \" + path.toString());\n\t\t\t}\n\t\t\tset.add(path);\n\t\t} // end of if (file.isDirectory()) else\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/osgi/util/ClassUtilBean.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.osgi.util;\n\nimport tigase.osgi.Activator;\nimport tigase.osgi.ModulesManagerImpl;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * Created by andrzej on 08.09.2016.\n */\npublic class ClassUtilBean\n\t\textends tigase.util.reflection.ClassUtilBean {\n\n\tpublic ClassUtilBean() {\n\t\tclasses.addAll(ClassUtil.getClassesFromBundle(Activator.getBundle()));\n\t}\n\n\t@Override\n\tpublic Set<Class<?>> getAllClasses() {\n\t\tModulesManagerImpl modulesManager = ModulesManagerImpl.getInstance();\n\t\tSet<Class<?>> classes = new HashSet<>(super.getAllClasses());\n\t\tclasses.addAll(modulesManager.getClasses());\n\t\treturn classes;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/AbstractComponentRegistrator.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport tigase.conf.ConfigurationException;\n\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * This is an archetype of a special types of classes which collect some data from Tigase components or provide these\n * data to components. They normally do not process normall packets and are usually accessed by admins via ad-hoc\n * commands. Good examples of such components are <code>StatisticsCollector</code> or <code>Configurator</code>.<br>\n * Extensions of these class can process packets addresses to the component via <code>processPacket(Packet packet,\n * Queue&lt;Packet&gt; results)</code> method. Alternatively scripting API can be used via ad-hoc commands.<br> The\n * class does not have any queues buffering packets or separate threads for packets processing. All packets are\n * processed from <code>MessageRouter</code> threads via <code>processPacket(Packet packet, Queue&lt;Packet&gt;\n * results)</code> method. Hence this is important that processing implemented in extensions to the class does not take\n * long time. In particular no DB processing is expected.\n * <br>\n * Created: Tue Nov 22 22:57:44 2005\n *\n* @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic abstract class AbstractComponentRegistrator<E extends ServerComponent>\n\t\textends BasicComponent\n\t\timplements ComponentRegistrator {\n\n\t/**\n\t * A collection of server components which implement special interface, related to the functionality provided by\n\t * extension of the class.\n\t */\n\tprotected Map<String, E> components = new ConcurrentHashMap<>();\n\tprivate long packetId = 0;\n\n\t/**\n\t * Creates a new <code>AbstractComponentRegistrator</code> instance.\n\t */\n\tpublic AbstractComponentRegistrator() {\n\t}\n\n\t/**\n\t * Method provides a callback mechanism signaling that a new component implementing special interface has been added\n\t * to the internal <code>components</code> collection.\n\t *\n\t * @param component is a reference to the component just added to the collection.\n\t */\n\tpublic abstract void componentAdded(E component) throws ConfigurationException;\n\n\t/**\n\t * Method provides a callback mechanism signaling that a component implementing special interface has been removed\n\t * from the internal <code>components</code> collection.\n\t *\n\t * @param component is a reference to the component removed from the collection.\n\t */\n\tpublic abstract void componentRemoved(E component);\n\n\t/**\n\t * Method checks whether the component provides as method parameter is correct type that is implements special\n\t * interface or extends special class. Result of the method determines whether the component can be added to the\n\t * internal <code>components</code> collection.\n\t *\n\t * @param component is a reference to the component being checked.\n\t *\n\t * @return a <code>boolean</code> value of <code>true</code> if the component is of a correct type and\n\t * <code>false</code> otherwise.\n\t */\n\tpublic abstract boolean isCorrectType(ServerComponent component);\n\n\t/**\n\t * Method checks whether the component is of a correct type, adds it to the internal <code>components</code>\n\t * collection and calls <code>componentAdded(...)</code> callback.\n\t *\n\t * @param component a reference to the component which is being added to the intenal collection.\n\t *\n\t * @return a <code>boolean</code> value of <code>true</code> if the component has been added to the internal\n\t * collection and <code>false</code> otherwise.\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic boolean addComponent(ServerComponent component) throws ConfigurationException {\n\t\tif (isCorrectType(component)) {\n\t\t\tcomponents.put(component.getName(), (E) component);\n\t\t\tcomponentAdded((E) component);\n\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Method removes specified component from the internal <code>components</code> collection and calls\n\t * <code>componentRemoved(...)</code> callback method.\n\t *\n\t * @param component is a reference to the component being removed.\n\t *\n\t * @return a <code>boolean</code> value of <code>true</code> if the component has been removed from the internal\n\t * collection and <code>false</code> otherwise.\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic boolean deleteComponent(ServerComponent component) {\n\t\tif (isCorrectType(component)) {\n\t\t\tcomponents.remove(component.getName());\n\t\t\tcomponentRemoved((E) component);\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Method returns a component for a specified component name from internal <code>components</code> collection or\n\t * <code>null</code> of there is no such component in the collection.\n\t *\n\t * @param name is a <code>String</code> value of the component name.\n\t *\n\t * @return a reference to the component found in the internal collection or <code>null</code> if no component has\n\t * been found.\n\t */\n\tpublic E getComponent(String name) {\n\t\treturn components.get(name);\n\t}\n\n\t/**\n\t * Method generates and returns an unique packet ID. The ID is unique within running Tigase instance. The method can\n\t * be overwritten to change the generation of the packet ID.\n\t *\n\t * @param prefix is a <code>String</code> value of the ID profix or <code>null</code> if no prefix is necessary.\n\t *\n\t * @return a <code>String</code> instance of a new packet ID.\n\t */\n\tpublic String newPacketId(String prefix) {\n\t\tStringBuilder sb = new StringBuilder(32);\n\n\t\tif (prefix != null) {\n\t\t\tsb.append(prefix).append(\"-\");\n\t\t}\n\n\t\tsb.append(getName()).append(++packetId);\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic void release() {\n\t}\n}    // AbstractComponentRegistrator\n"
  },
  {
    "path": "src/main/java/tigase/server/AbstractMessageReceiver.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport tigase.component.ScheduledTask;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.filters.PacketFiltersBean;\nimport tigase.stats.StatisticType;\nimport tigase.stats.StatisticsContainer;\nimport tigase.stats.StatisticsList;\nimport tigase.sys.TigaseRuntime;\nimport tigase.util.Algorithms;\nimport tigase.util.routing.PatternComparator;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.util.workqueue.PriorityQueueAbstract;\nimport tigase.util.workqueue.PriorityQueueRelaxed;\nimport tigase.xmpp.jid.JID;\n\nimport java.text.DecimalFormat;\nimport java.time.Duration;\nimport java.util.*;\nimport java.util.concurrent.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.regex.Pattern;\n\n/**\n * This is an archetype for all classes processing user-level packets. The implementation is designed for a heavy\n * packets processing with internal queues and number of separate threads depending on number of CPUs. Extensions of the\n * class can process normall user packets and administrator packets via ad-hoc commands. Good examples of such\n * components are <code>MUC</code>, <code>PubSub</code>, <code>SessionManager</code>. <br> The class offers scripting\n * API for administrator ad-hoc commands. <br> By default it internally uses priority queues which in some rare cases\n * may lead to packets reordering. When this happens and it is unacceptable for the deployment non-priority queues can\n * be used. The queues size is limited and depends on the available memory size. <br> Packets are processed by\n * <code>processPacket(Packet packet)</code> method which is concurrently called from multiple threads.\n * <br>\n * Created: Tue Nov 22 07:07:11 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic abstract class AbstractMessageReceiver\n\t\textends BasicComponent\n\t\timplements StatisticsContainer, MessageReceiver, PacketWriterWithTimeout {\n\n\t/**\n\t * Configuration property key for setting incoming packets filters on the component level.\n\t */\n\tpublic static final String INCOMING_FILTERS_PROP_KEY = \"incoming-filters\";\n\n\t/**\n\t * Configuration property default vakue with a default incoming packet filter loaded by Tigase server. <br> This is\n\t * a comma-separated list of classes which should be loaded as packet filters. The classes must implement\n\t * <code>PacketFilterIfc</code> interface.\n\t */\n\tpublic static final String INCOMING_FILTERS_PROP_VAL = \"tigase.server.filters.PacketCounter\";\n\n\t/**\n\t * Configuration property key allowing to overwrite a default (memory size dependent) size for the component\n\t * internal queues. By default the queue size is adjusted to the available memory size to avoid out of memory\n\t * errors.\n\t */\n\tpublic static final String MAX_QUEUE_SIZE_PROP_KEY = \"max-queue-size\";\n\n\t/**\n\t * A default value for max queue size property. The value is calculated at the server startup time using following\n\t * formula: <br> <code>Runtime.getRuntime().maxMemory() / 400000L</code> You can change the default queue size by\n\t * setting a different value for the <code>MAX_QUEUE_SIZE_PROP_KEY</code> property in the server configuration.\n\t */\n\tpublic static final Integer MAX_QUEUE_SIZE_PROP_VAL = new Long(\n\t\t\tRuntime.getRuntime().maxMemory() / 400000L).intValue();\n\n\t/**\n\t * Configuration property key for setting outgoing packets filters on the component level. This is a comma-separated\n\t * list of classes which should be loaded as packet filters. The classes must implement <code>PacketFilterIfc</code>\n\t * interface.\n\t */\n\tpublic static final String OUTGOING_FILTERS_PROP_KEY = \"outgoing-filters\";\n\n\t/**\n\t * Configuration property default vakue with a default outgoing packet filter loaded by Tigase server. <br> This is\n\t * a comma-separated list of classes which should be loaded as packet filters. The classes must implement\n\t * <code>PacketFilterIfc</code> interface.\n\t */\n\tpublic static final String OUTGOING_FILTERS_PROP_VAL = \"tigase.server.filters.PacketCounter\";\n\n\tpublic static final String PACKET_DELIVERY_RETRY_COUNT_PROP_KEY = \"packet-delivery-retry-count\";\n\n\t/**\n\t * Configuration property key for setting number of threads used by component ScheduledExecutorService.\n\t */\n\tpublic static final String SCHEDULER_THREADS_PROP_KEY = \"scheduler-threads\";\n\n\t/**\n\t * Constant used in time calculation procedures. Indicates a second that is 1000 milliseconds.\n\t */\n\tprotected static final long SECOND = 1000;\n\n\t/**\n\t * Constant used in time calculation procedures. Indicates a minute that is 60 <code>SECOND</code>s.\n\t */\n\tprotected static final long MINUTE = 60 * SECOND;\n\n\t/**\n\t * Constant used in time calculation procedures. Indicates a hour that is 60 <code>MINUTE</code>s.\n\t */\n\tprotected static final long HOUR = 60 * MINUTE;\n\n\t// String added intentionally!!\n\t// Don't change to AbstractMessageReceiver.class.getName()\n\n\tprivate static final Logger log = Logger.getLogger(\"tigase.debug.AbstractMessageReceiver\");\n\n\t// PriorityQueueAbstract.getPriorityQueue(pr_cache.length, maxQueueSize);\n\t@Inject\n\tprivate PacketFiltersBean.IncomingPacketFiltersBean incoming_filters;\n\t@Inject\n\tprivate PacketFiltersBean.OutgoingPacketFiltersBean outgoing_filters;\n\t// Array cache to speed processing up....\n\tprivate final Priority[] pr_cache = Priority.values();\n\tprivate final List<PriorityQueueAbstract<Packet>> out_queues = new ArrayList<PriorityQueueAbstract<Packet>>(\n\t\t\tpr_cache.length);\n\tprivate final List<PriorityQueueAbstract<Packet>> in_queues = new ArrayList<>(pr_cache.length);\n\tprivate final long[] processPacketTimings = new long[100];\n\tprivate final Set<Pattern> regexRoutings = new ConcurrentSkipListSet<Pattern>(new PatternComparator());\n\tprivate final ThreadFactory threadFactory = new ThreadFactory() {\n\n\t\tprivate final ThreadFactory internal = Executors.defaultThreadFactory();\n\n\t\t@Override\n\t\tpublic Thread newThread(Runnable r) {\n\t\t\tThread th = internal.newThread(r);\n\t\t\tth.setName(\"scheduler_\" + th.getName() + \"-\" + getName());\n\t\t\treturn th;\n\t\t}\n\n\t};\n\tprivate final ConcurrentHashMap<String, PacketReceiverTaskIfc> waitingTasks = new ConcurrentHashMap<>(\n\t\t\t16, 0.75f, 4);\n\tprotected int maxInQueueSize = MAX_QUEUE_SIZE_PROP_VAL;\n\tprotected int maxOutQueueSize = MAX_QUEUE_SIZE_PROP_VAL;\n\t@ConfigField(desc = \"Maximum size of internal queues\", alias = \"max-queue-size\")\n\tprotected int maxQueueSize = MAX_QUEUE_SIZE_PROP_VAL;\n\tprivate int in_queues_size = processingInThreads();\n\tprivate long last_hour_packets = 0;\n\tprivate long last_minute_packets = 0;\n\tprivate long last_second_packets = 0;\n\tprivate int out_queues_size = processingOutThreads();\n\tprivate QueueListener out_thread = null;\n\t@ConfigField(desc = \"Packet delivery retry count\", alias = PACKET_DELIVERY_RETRY_COUNT_PROP_KEY)\n\tprivate int packetDeliveryRetryCount = 15;\n\tprivate long packetId = 0;\n\tprivate long packets_per_hour = 0;\n\tprivate long packets_per_minute = 0;\n\tprivate long packets_per_second = 0;\n\tprivate MessageReceiver parent = null;\n\tprivate int pptIdx = 0;\n\t@ConfigField(desc = \"Priority queue class\", alias = \"priority-queue-implementation\")\n\tprivate Class<? extends PriorityQueueAbstract> priorityQueueClass = PriorityQueueRelaxed.class;\n\t// ~--- fields ---------------------------------------------------------------\n\t// private static final TigaseTracer tracer =\n\t// TigaseTracer.getTracer(\"abstract\");\n\t@ConfigField(desc = \"Number of threads processing incoming packages\", alias = \"processing-in-threads\")\n\tprivate int processingInThreads = processingInThreads();\n\t@ConfigField(desc = \"Number of threads processing outgoing packages\", alias = \"processing-out-threads\")\n\tprivate int processingOutThreads = processingOutThreads();\n\tprivate ScheduledExecutorService receiverScheduler = null;\n\tprivate Timer receiverTasks = null;\n\tprivate String resourceForPacketWithTimeout = null;\n\t@Inject(nullAllowed = true)\n\tprivate Set<ScheduledTask> scheduledTasks;\n\t@ConfigField(desc = \"Number of threads for scheduler\", alias = SCHEDULER_THREADS_PROP_KEY)\n\tprivate int schedulerThreads_size = 1;\n\t/**\n\t * Variable <code>statAddedMessagesEr</code> keeps counter of unsuccessfuly added messages due to queue overflow.\n\t */\n\tprivate long statReceivedPacketsEr = 0;\n\t/**\n\t * Variable <code>statAddedMessagesOk</code> keeps counter of successfuly added messages to queue.\n\t */\n\tprivate long statReceivedPacketsOk = 0;\n\tprivate long statSentPacketsEr = 0;\n\tprivate long statSentPacketsOk = 0;\n\tprivate Queue<Runnable> tasksAwaitingReceiver = new LinkedList<>();\n\tprivate ArrayDeque<QueueListener> threadsQueueIn = null;\n\tprivate ArrayDeque<QueueListener> threadsQueueOut = null;\n\n\tprivate static final DecimalFormat df = new DecimalFormat(\"#0.00\");\n\n\t/**\n\t * Helper method used in statistics to find uneven distribution of packet processing across processing threads\n\t *\n\t * @param array is an resizable array of {@code QueueListener} containing all processing threads\n\t *\n\t * @return string containing average value, variance as well as comma separated list of outlier threads and number\n\t * of processed packets; returns empty string if passed array is empty or null\n\t */\n\tprivate static String calculateOutliers(ArrayDeque<AbstractMessageReceiver.QueueListener> array) {\n\n\t\tif (array == null || array.size() == 0) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\tlong[] allNumbers = new long[array.size()];\n\t\tint idx = 0;\n\t\tlong sum = 0;\n\t\tfor (QueueListener queueListener : array) {\n\t\t\tsum += queueListener.packetCounter;\n\t\t\tallNumbers[idx++] = queueListener.packetCounter;\n\t\t}\n\n\t\tdouble mean = sum / allNumbers.length;\n\n\t\tdouble tmp_sum_variance = 0;\n\t\tfor (long allNumber : allNumbers) {\n\t\t\ttmp_sum_variance += Math.pow(allNumber - mean, 2);\n\t\t}\n\n\t\tdouble deviation = Math.sqrt(tmp_sum_variance / allNumbers.length);\n\n\t\tList<String> outliers = new ArrayList<>();\n\t\tfor (QueueListener queueListener : array) {\n\t\t\tif (Math.abs(queueListener.packetCounter - mean) > (2 * deviation)) {\n\t\t\t\toutliers.add(queueListener.getName() + \":\" + queueListener.packetCounter + \":x\" +\n\t\t\t\t\t\t\t\t\t df.format(queueListener.packetCounter / mean));\n\t\t\t}\n\t\t}\n\n\t\treturn \"mean: \" + df.format(mean) + \", deviation: \" + df.format(deviation) +\n\t\t\t\t(!outliers.isEmpty() ? \", outliers: \" + outliers.toString() : \"\");\n\t}\n\n\tpublic AbstractMessageReceiver() {\n\t}\n\n\t/**\n\t * Method adds a <code>Packet</code> object to the internal input queue. Packets from the input queue are later\n\t * passed to the <code>processPacket(Packet)</code> method. This is a blocking method waiting if necessary for the\n\t * room if the queue is full. <br> The method returns a <code>boolean</code> value of <code>true</code> if the\n\t * packet has been successfully added to the queue and <code>false</code> otherwise. <br> There can be many queues\n\t * and many threads processing packets for the component, however the method makes the best effort to guarantee that\n\t * packets are later processed in the correct order. For example that packets for a single user always end up in the\n\t * same exact queue. You can tweak the packets distribution among threads by overwriting\n\t * <code>hashCodeForPacket(Packet)</code> method.<br> If there is <code>N</code> threads the packets are distributed\n\t * among thread using following logic:\n\t * <br>\n\t * <pre>\n\t * int threadNo = Math.abs(hashCodeForPacket(packet) % N);\n\t * </pre>\n\t * <br>\n\t * This is a preferred method to be used by most Tigase components. If the queues are full the component should stop\n\t * and wait for more room. The blocking methods aim to prevent from the system overloading or wasting resources for\n\t * generating packets which can't be processed anyway.\n\t *\n\t * @param packet is a <code>Packet</code> instance being put to the component internal input queue.\n\t *\n\t * @return a <code>boolean</code> value of <code>true</code> if the packet has been successfully added to the queue\n\t * and <code>false</code> otherwise.\n\t */\n\t@Override\n\tpublic boolean addPacket(Packet packet) {\n\t\tint queueIdx = Math.abs(hashCodeForPacket(packet) % in_queues_size);\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"[{0}] queueIdx={1}, {2}\",\n\t\t\t\t\tnew Object[]{getName(), queueIdx, packet.toStringSecure()});\n\t\t}\n\t\ttry {\n\t\t\tin_queues.get(queueIdx).put(packet, packet.getPriority().ordinal());\n\t\t\t++statReceivedPacketsOk;\n\t\t} catch (InterruptedException e) {\n\t\t\t++statReceivedPacketsEr;\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Packet dropped for unknown reason: {0}\", packet);\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}    // end of try-catch\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * This is a variant of <code>addPacket(Packet)</code> method which adds <code>Packet</code> to in the internal\n\t * input queue without blocking. <br> The method returns a <code>boolean</code> value of <code>true</code> if the\n\t * packet has been successful added to the queue and <code>false</code> otherwise. <br> Use of the non-blocking\n\t * methods is not recommended for most of the components implementations. The only component which is allowed to use\n\t * them is the server <code>MessageRouter</code> implementation which can not hang on any method. This would cause a\n\t * dead-lock in the application. All other components must use blocking methods and wait if the system is under so\n\t * high load that it's queues are full. <br> See <code>addPacket(Packet)</code> method's documentation for some more\n\t * details.\n\t *\n\t * @param packet is a <code>Packet</code> instance being put to the component internal input queue.\n\t *\n\t * @return a <code>boolean</code> value of <code>true</code> if the packet has been successfully added to the queue\n\t * and <code>false</code> otherwise.\n\t *\n\t * @see AbstractMessageReceiver#addPacket(Packet packet)\n\t */\n\t@Override\n\tpublic boolean addPacketNB(Packet packet) {\n\t\tint queueIdx = Math.abs(hashCodeForPacket(packet) % in_queues_size);\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"[{0}] queueIdx={1}, {2}\",\n\t\t\t\t\tnew Object[]{getName(), queueIdx, packet.toStringSecure()});\n\t\t}\n\n\t\tboolean result = in_queues.get(queueIdx).offer(packet, packet.getPriority().ordinal());\n\n\t\tif (result) {\n\t\t\t++statReceivedPacketsOk;\n\t\t} else {\n\n\t\t\t// Queue overflow!\n\t\t\t++statReceivedPacketsEr;\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Packet dropped due to queue overflow: {0}\", packet);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * This is a convenience method for adding all packets stored in given queue to the component's internal input\n\t * queue.<br> The method calls <code>addPacket(Packet)</code> in a loop for each packet in the queue. If the call\n\t * returns <code>true</code> then the packet is removed from the given queue, otherwise the methods ends the loop\n\t * and returns <code>false</code>. <br> Please note, if the method returns <code>true</code> it means that all the\n\t * packets from the queue passed as a parameter have been successfuly run through the <code>addPacket(Packet)</code>\n\t * method and the queue passed as a parameter should be empty. If the method returns false then at least one packet\n\t * from the parameter queue wasn't successfully run through the <code>addPacket(Packet)</code> method. If the method\n\t * returns <code>false</code> then the queue passed as a parameter is not empty and it contains packet which was\n\t * unseccessfully run through the <code>addPacket(Packet)</code> method and all the packets which were not run at\n\t * all.\n\t *\n\t * @param packets is a <code>Queue</code> of packets for adding to the component internal input queue. All the\n\t * packets are later processed by <code>processPacket(Packet)</code> method in the same exact order if they are\n\t * processed by the same thread. See documentation <code>hashCodeForPacket(Packet)</code> method how to control\n\t * assiging packets to particular threads.\n\t *\n\t * @return a <code>boolean</code> value of <code>true</code> if all packets has been successfully added to the\n\t * component's internal input queue and <code>false</code> otherwise.\n\t *\n\t * @see AbstractMessageReceiver#hashCodeForPacket(Packet packet)\n\t */\n\t@Override\n\tpublic boolean addPackets(Queue<Packet> packets) {\n\t\tboolean result = true;\n\t\tPacket p = packets.peek();\n\n\t\twhile (p != null) {\n\t\t\tresult = addPacket(p);\n\t\t\tif (result) {\n\t\t\t\tpackets.poll();\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}    // end of if (result) else\n\t\t\tp = packets.peek();\n\t\t}      // end of while ()\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Method adds a new routing address for the component. Routing addresses are used by the <code>MessageRouter</code>\n\t * to calculate packet's destination. If the packet's destination address matches one of the component's routing\n\t * addresses the packet is added to the component's internal input queue. <br> By default all components accept\n\t * packets addressed to the componentId and to:\n\t * <br>\n\t * <pre>\n\t * component.getName() + '@' + any virtual domain\n\t * </pre>\n\t * <br>\n\t * TODO: The future implementation most likely accept packets addressed to:\n\t * <br>\n\t * <pre>\n\t * any virtual domain + '/' + component.getName()\n\t * </pre>\n\t * <br>\n\t * instead. <br> The routings are passed as Java regular expression strings are the extra addresses accepted by the\n\t * component. In most cases this is used by the external component protocol implementations which can dynamically\n\t * change accepted addresses depending on the connected external components.\n\t *\n\t * @param address is a Java regular expression string for the packet's destination address accepted by this\n\t * component.\n\t */\n\tpublic void addRegexRouting(String address) {\n\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\tlog.log(Level.FINE, \"{0} - attempt to add regex routing: {1}\", new Object[]{getName(), address});\n\t\t}\n\t\tregexRoutings.add(Pattern.compile(address, Pattern.CASE_INSENSITIVE));\n\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\tlog.log(Level.FINE, \"{0} - success adding regex routing: {1}\", new Object[]{getName(), address});\n\t\t}\n\t}\n\n\t/**\n\t * Method queues and executes timer tasks using ScheduledExecutorService which allows using more than one thread for\n\t * executing tasks.\n\t */\n\tpublic void addTimerTask(tigase.util.common.TimerTask task, long delay) {\n\t\tif (task.isCancelled()) {\n\t\t\treturn;\n\t\t}\n\t\tif (receiverScheduler == null) {\n\t\t\ttasksAwaitingReceiver.offer(() -> this.addTimerTask(task, delay));\n\t\t\treturn;\n\t\t}\n\t\tScheduledFuture<?> future = receiverScheduler.schedule(task, delay, TimeUnit.MILLISECONDS);\n\n\t\ttask.setScheduledFuture(future);\n\t}\n\n\tpublic void addTimerTask(tigase.util.common.TimerTask task, long initialDelay, long period) {\n\t\tif (task.isCancelled()) {\n\t\t\treturn;\n\t\t}\n\t\tif (receiverScheduler == null) {\n\t\t\ttasksAwaitingReceiver.offer(() -> this.addTimerTask(task, initialDelay, period));\n\t\t\treturn;\n\t\t}\n\t\tScheduledFuture<?> future = receiverScheduler.scheduleAtFixedRate(task, initialDelay, period,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  TimeUnit.MILLISECONDS);\n\t\ttask.setScheduledFuture(future);\n\t}\n\n/**\n\t * Method queues and executes timer tasks using ScheduledExecutorService which allows using more than one thread for\n\t * executing tasks. It allows to set a timeout to cancel long running tasks\n\t *\n\t * @param task a task implementing {@link tigase.util.common.TimerTask}\n\t * @param delay in milliseconds delay after which task will be started\n\t * @param timeout in milliseconds after which task will be cancelled disregarding whether it has finished or not\n\t */\n\tpublic void addTimerTaskWithTimeout(final tigase.util.common.TimerTask task, long delay, long timeout) {\n\t\tif (task.isCancelled()) {\n\t\t\treturn;\n\t\t}\n\t\tif (receiverScheduler == null) {\n\t\t\ttasksAwaitingReceiver.offer(() -> this.addTimerTaskWithTimeout(task, delay, timeout));\n\t\t\treturn;\n\t\t}\n\t\treceiverScheduler.schedule(new tigase.util.common.TimerTask() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Cancelling tigase task (timeout): \" + task);\n\t\t\t\t}\n\t\t\t\tif (task != null) {\n\t\t\t\t\ttask.cancel(true);\n\t\t\t\t}\n\t\t\t}\n\t\t}, timeout, TimeUnit.MILLISECONDS);\n\n\t\taddTimerTask(task, delay);\n\t}\n\n\t/**\n\t * Creates and executes a periodic action that becomes enabled first after the given initial delay, and subsequently\n\t * with the given period; please refer to {@link ScheduledExecutorService#scheduleAtFixedRate} javadoc for details.\n\t * It utilizes Tigase {@link tigase.util.common.TimerTask} and allows setting a timeout to cancel long running\n\t * tasks\n\t *\n\t * @param task a task implementing {@link tigase.util.common.TimerTask}\n\t * @param delay in milliseconds, the time to delay first execution\n\t * @param period in milliseconds, the period between successive executions\n\t * @param timeout in milliseconds after which task will be cancelled disregarding whether it has finished or not\n\t */\n\tpublic void addTimerTaskWithTimeout(final tigase.util.common.TimerTask task, long delay, long period,\n\t\t\t\t\t\t\t\t\t\tlong timeout) {\n\t\tif (task.isCancelled()) {\n\t\t\treturn;\n\t\t}\n\t\tif (receiverScheduler == null) {\n\t\t\ttasksAwaitingReceiver.offer(() -> this.addTimerTaskWithTimeout(task, delay, period, timeout));\n\t\t\treturn;\n\t\t}\n\t\treceiverScheduler.schedule(new tigase.util.common.TimerTask() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Cancelling tigase task (timeout): \" + task);\n\t\t\t\t}\n\t\t\t\tif (task != null) {\n\t\t\t\t\ttask.cancel(true);\n\t\t\t\t}\n\t\t\t}\n\t\t}, timeout, TimeUnit.MILLISECONDS);\n\n\t\taddTimerTask(task, delay, period);\n\t}\n\n\t/**\n\t * Method clears, removes all the component routing addresses. After this method call the component accepts only\n\t * packets addressed to default routings that is component ID or the component name + '@' + virtual domains\n\t */\n\tpublic void clearRegexRoutings() {\n\t\tregexRoutings.clear();\n\t}\n\n\t/**\n\t * Utility method executed precisely every hour. A component can overwrite the method to put own code to be executed\n\t * at the regular intervals of time. <br> Note, no extensive calculations should happen in this method nor long\n\t * lasting operations. It is essential that the method processing does not exceed 1 hour. The overriding method must\n\t * call the the super method first and only then run own code.\n\t */\n\t@Override\n\tpublic synchronized void everyHour() {\n\t\tpackets_per_hour = statReceivedPacketsOk - last_hour_packets;\n\t\tlast_hour_packets = statReceivedPacketsOk;\n\t\tsuper.everyHour();\n\t}\n\n\t/**\n\t * Utility method executed precisely every minute. A component can overwrite the method to put own code to be\n\t * executed at the regular intervals of time. <br> Note, no extensive calculations should happen in this method nor\n\t * long lasting operations. It is essential that the method processing does not exceed 1 minute. The overriding\n\t * method must call the the super method first and only then run own code.\n\t */\n\t@Override\n\tpublic synchronized void everyMinute() {\n\t\tpackets_per_minute = statReceivedPacketsOk - last_minute_packets;\n\t\tlast_minute_packets = statReceivedPacketsOk;\n\t\treceiverTasks.purge();\n\t\tsuper.everyMinute();\n\t}\n\n\t/**\n\t * Utility method executed precisely every second. A component can overwrite the method to put own code to be\n\t * executed at the regular intervals of time. <br> Note, no extensive calculations should happen in this method nor\n\t * long lasting operations. It is essential that the method processing does not exceed 1 second. The overriding\n\t * method must call the the super method first and only then run own code.\n\t */\n\t@Override\n\tpublic synchronized void everySecond() {\n\t\tpackets_per_second = statReceivedPacketsOk - last_second_packets;\n\t\tlast_second_packets = statReceivedPacketsOk;\n\t\tsuper.everySecond();\n\t}\n\n\t/**\n\t * This method decides how incoming packets are distributed among processing threads. Different components needs\n\t * different distribution to efficient use all threads and avoid packets re-ordering. <br> If there are N processing\n\t * threads, packets are distributed among threads using following code:\n\t * <br>\n\t * <pre>\n\t * int threadNo = Math.abs(hashCodeForPacket(packet) % N);\n\t * </pre>\n\t * <br>\n\t * For a PubSub component, for example, a better packets distribution would be based on the PubSub channel name, for\n\t * SM a better distribution is based on the destination address, etc....\n\t *\n\t * @param packet is a <code>Packet</code> which needs to be processed by some thread.\n\t *\n\t * @return a hash code generated for the input thread.\n\t */\n\tpublic int hashCodeForPacket(Packet packet) {\n\t\t// use of getPacketFrom was moved to SM as it worked OK only in use case of SM \n\t\t// where packet may came from connection manager\n\t\tif ((packet.getPacketTo() != null) && !getComponentId().equals(packet.getPacketTo())) {\n\t\t\treturn packet.getPacketTo().hashCode();\n\t\t}\n\n\t\t// If not, then a better way is to get hashCode from the elemTo address\n\t\t// as this would be by the destination address user name:\n\t\tif (packet.getStanzaTo() != null) {\n\t\t\treturn packet.getStanzaTo().getBareJID().hashCode();\n\t\t}\n\n\t\treturn 1;\n\t}\n\n\tpublic String newPacketId(String prefix) {\n\t\tfinal String uuid = UUID.randomUUID().toString();\n\t\treturn prefix != null ? prefix + \"-\" + uuid : uuid;\n\t}\n\n\t/**\n\t * Concurrency control method. Returns preferable number of threads set for this component.\n\t *\n\t * @return preferable number of threads set for this component.\n\t */\n\tpublic int processingInThreads() {\n\t\treturn 1;\n\t}\n\n\t/**\n\t * Concurrency control method. Returns preferable number of threads set for this component.\n\t *\n\t * @return preferable number of threads set for this component.\n\t */\n\tpublic int processingOutThreads() {\n\t\treturn 1;\n\t}\n\t/**\n\t * By default this method just copies the given packet between queue. This method operates on packets which have\n\t * been already processed somehow by the component so usually the default action is the best one, however some\n\t * components in rare cases may choose to process packets differently. In most cases this method should not be\n\t * overridden.\n\t *\n\t * @param packet is an output packet which normally has to go to other component for further processing.\n\t */\n\tpublic void processOutPacket(Packet packet) {\n\t\tif (parent != null) {\n\t\t\tparent.addPacket(packet);\n\t\t} else {\n\n\t\t\t// It may happen for MessageRouter and this is intentional\n\t\t\taddPacketNB(packet);\n\n\t\t\t// log.warning(\"[\" + getName() + \"]  \" + \"No parent!\");\n\t\t}    // end of else\n\t}\n\n\t/**\n\t * This is the main <code>Packet</code> processing method. It is called concurrently from many threads so\n\t * implementing it in thread save manner is essential. The method is called for each packet addressed to the\n\t * component. <br> Please note, the <code>Packet</code> instance may be processed by different parts of the server,\n\t * different components or plugins at the same time. Therefore this is very important to tread the\n\t * <code>Packet</code> instance as unmodifiable object. <br> Processing in this method is asynchronous, therefore\n\t * there is no result value. If there are some 'result' packets generated during processing, they should be passed\n\t * back using <code>addOutPacket(Packet)</code> method.\n\t *\n\t * @param packet is an instance of the <code>Packet</code> class passed for processing.\n\t */\n\tpublic abstract void processPacket(Packet packet);\n\n\t@Override\n\tpublic final void processPacket(final Packet packet, final Queue<Packet> results) {\n\t\taddPacketNB(packet);\n\t}\n\n\t@Override\n\tpublic void release() {\n\t\tstop();\n\t}\n\n\tpublic boolean removeRegexRouting(String address) {\n\t\treturn regexRoutings.remove(Pattern.compile(address, Pattern.CASE_INSENSITIVE));\n\t}\n\n\t/**\n\t * Method returns default number of threads used by SchedulerExecutorService\n\t *\n\t * @return a value of <code>int</code>\n\t */\n\tpublic int schedulerThreads() {\n\t\treturn 2;\n\t}\n\n\t@Override\n\tpublic void start() {\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.log(Level.FINER, \"{0}: starting queue management threads ...\", getName());\n\t\t}\n\t\tstartThreads();\n\n\t\tif (scheduledTasks != null) {\n\t\t\tfor (ScheduledTask task : scheduledTasks) {\n\t\t\t\ttask.initialize();\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void stop() {\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.log(Level.FINER, \"{0}: stopping queue management threads ...\", getName());\n\t\t}\n\t\tstopThreads();\n\t}\n\n\t/**\n\t * Method returns a <code>Set</code> with all component's routings as a compiled regular expression patterns. The\n\t * <code>Set</code> can be empty but it can not be null.\n\t *\n\t * @return a <code>Set</code> with all component's routings as a compiled regular expression patterns.\n\t */\n\tpublic Set<Pattern> getRegexRoutings() {\n\t\treturn regexRoutings;\n\t}\n\n\t/**\n\t * Method returns component statistics. Please note, the method can be called every second by the server monitoring\n\t * system therefore no extensive or lengthy calculations are allowed. If there are some statistics requiring lengthy\n\t * operations like database access they must have <code>Level.FINEST</code> assigned and must be put inside the\n\t * level guard to prevent generating them by the system monitor. The system monitor does not collect\n\t * <code>FINEST</code> statistics. <br> Level guard code looks like the example below:\n\t * <br>\n\t * <pre>\n\t * if (list.checkLevel(Level.FINEST)) {\n\t *   // Some CPU intensive calculations or lengthy operations\n\t *   list.add(getName(), \"Statistic description\", stat_value, Level.FINEST);\n\t * }\n\t *\n\t * </pre>\n\t * This way you make sure your extensive operation is not executed every second by the monitoring system and does\n\t * not affect the server performance.\n\t *\n\t * @param list is a <code>StatistcsList</code> where all statistics are stored.\n\t */\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t\tlist.add(getName(), \"Last second packets\", packets_per_second, Level.FINE);\n\t\tlist.add(getName(), \"Last minute packets\", packets_per_minute, Level.FINE);\n\t\tlist.add(getName(), \"Last hour packets\", packets_per_hour, Level.FINE);\n\t\tlist.add(getName(), \"Processing threads\", processingInThreads(), Level.FINER);\n\t\tlist.add(getName(), StatisticType.MSG_RECEIVED_OK.getDescription(), statReceivedPacketsOk, Level.FINE);\n\t\tlist.add(getName(), StatisticType.MSG_SENT_OK.getDescription(), statSentPacketsOk, Level.FINE);\n\t\tif (list.checkLevel(Level.FINEST)) {\n\t\t\tint[] in_priority_sizes = in_queues.get(0).size();\n\n\t\t\tfor (int i = 1; i < in_queues.size(); i++) {\n\t\t\t\tint[] tmp_pr_sizes = in_queues.get(i).size();\n\n\t\t\t\tfor (int j = 0; j < tmp_pr_sizes.length; j++) {\n\t\t\t\t\tin_priority_sizes[j] += tmp_pr_sizes[j];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tint[] out_priority_sizes = out_queues.get(0).size();\n\n\t\t\tfor (int i = 1; i < out_queues.size(); i++) {\n\t\t\t\tint[] tmp_pr_sizes = out_queues.get(i).size();\n\n\t\t\t\tfor (int j = 0; j < tmp_pr_sizes.length; j++) {\n\t\t\t\t\tout_priority_sizes[j] += tmp_pr_sizes[j];\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (int i = 0; i < in_priority_sizes.length; i++) {\n\t\t\t\tPriority queue = Priority.values()[i];\n\n\t\t\t\tlist.add(getName(), \"In queue wait: \" + queue.name(), in_priority_sizes[queue.ordinal()], Level.FINEST);\n\t\t\t}\n\t\t\tfor (int i = 0; i < out_priority_sizes.length; i++) {\n\t\t\t\tPriority queue = Priority.values()[i];\n\n\t\t\t\tlist.add(getName(), \"Out queue wait: \" + queue.name(), out_priority_sizes[queue.ordinal()],\n\t\t\t\t\t\t Level.FINEST);\n\t\t\t}\n\t\t}\n\n\t\tint in_queue_size = 0;\n\n\t\tfor (PriorityQueueAbstract<Packet> total_size : in_queues) {\n\t\t\tin_queue_size += total_size.totalSize();\n\t\t}\n\n\t\tint out_queue_size = 0;\n\n\t\tfor (PriorityQueueAbstract<Packet> total_size : out_queues) {\n\t\t\tout_queue_size += total_size.totalSize();\n\t\t}\n\t\tlist.add(getName(), \"Total In queues wait\", in_queue_size, Level.INFO);\n\t\tlist.add(getName(), \"Total Out queues wait\", out_queue_size, Level.INFO);\n\t\tlist.add(getName(), \"Total queues wait\", (in_queue_size + out_queue_size), Level.INFO);\n\t\tlist.add(getName(), StatisticType.MAX_QUEUE_SIZE.getDescription(), (maxInQueueSize * processingInThreads()),\n\t\t\t\t Level.FINEST);\n\t\tlist.add(getName(), StatisticType.IN_QUEUE_OVERFLOW.getDescription(), statReceivedPacketsEr, Level.INFO);\n\t\tlist.add(getName(), StatisticType.OUT_QUEUE_OVERFLOW.getDescription(), statSentPacketsEr, Level.INFO);\n\t\tlist.add(getName(), \"Total queues overflow\", (statReceivedPacketsEr + statSentPacketsEr), Level.INFO);\n\n\t\tlong res = 0;\n\n\t\tfor (long ppt : processPacketTimings) {\n\t\t\tres += ppt;\n\t\t}\n\n\t\tlong prcessingTime = res / processPacketTimings.length;\n\n\t\tlist.add(getName(), \"Average processing time on last \" + processPacketTimings.length + \" runs [ms]\",\n\t\t\t\t prcessingTime, Level.FINE);\n\t\tfor (PacketFilterIfc packetFilter : incoming_filters.getFilters()) {\n\t\t\tpacketFilter.getStatistics(list);\n\t\t}\n\t\tfor (PacketFilterIfc packetFilter : outgoing_filters.getFilters()) {\n\t\t\tpacketFilter.getStatistics(list);\n\t\t}\n\t\tif (list.checkLevel(Level.FINEST)) {\n\t\t\tlist.add(getName(), \"Processed packets thread IN\", threadsQueueIn.toString(), Level.FINEST);\n\t\t\tlist.add(getName(), \"Processed packets thread OUT\", threadsQueueOut.toString(), Level.FINEST);\n\t\t\tlist.add(getName(), \"Processed packets thread (outliers) IN\", calculateOutliers(threadsQueueIn),\n\t\t\t\t\t Level.FINEST);\n\t\t\tlist.add(getName(), \"Processed packets thread (outliers) OUT\", calculateOutliers(threadsQueueOut),\n\t\t\t\t\t Level.FINEST);\n\t\t}\n\t\tsuper.getStatistics(list);\n\t}\n\n\t@Override\n\tpublic boolean isInRegexRoutings(String address) {\n\n\t\t// log.finest(getName() + \" looking for regex routings: \" + address);\n\t\tfor (Pattern pat : regexRoutings) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"{0} matching: {1} against {2}\",\n\t\t\t\t\t\tnew Object[]{getName(), address, pat.toString()});\n\t\t\t}\n\t\t\tif (pat.matcher(address).matches()) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t// log.finest(getName() + \" matching failed against pattern: \" +\n\t\t\t// pat.toString());\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tpublic void setIncoming_filters(PacketFiltersBean.IncomingPacketFiltersBean incoming_filters) {\n\t\tthis.incoming_filters = incoming_filters;\n\t\tupdateFiltersName();\n\t}\n\n\tpublic void setOutgoing_filters(PacketFiltersBean.OutgoingPacketFiltersBean outgoing_filters) {\n\t\tthis.outgoing_filters = outgoing_filters;\n\t\tupdateFiltersName();\n\t}\n\n\tprotected void updateFiltersName() {\n\t\tOptional.ofNullable(incoming_filters).ifPresent(filters -> filters.setName(getName()));\n\t\tOptional.ofNullable(outgoing_filters).ifPresent(filters -> filters.setName(getName()));\n\t}\n\n\t@Override\n\tpublic void beanConfigurationChanged(Collection<String> changedFields) {\n\t\tsuper.beanConfigurationChanged(changedFields);\n\n\t\tboolean recreate = ((maxInQueueSize != (maxQueueSize / processingInThreads) * 2) ||\n\t\t\t\t(maxOutQueueSize != (maxQueueSize / processingOutThreads) * 2));\n\n\t\tif (in_queues.isEmpty() || !priorityQueueClass.equals(in_queues.get(0).getClass())) {\n\t\t\trecreate = true;\n\t\t\tthis.in_queues.clear();\n\t\t\tthis.out_queues.clear();\n\t\t}\n\n\t\tif (processingInThreads != in_queues_size || processingOutThreads == out_queues_size) {\n\t\t\trecreate = true;\n\t\t\tthis.in_queues_size = processingInThreads;\n\t\t\tthis.out_queues_size = processingOutThreads;\n\t\t\tthis.in_queues.clear();\n\t\t\tthis.out_queues.clear();\n\t\t}\n\n\t\tif (recreate) {\n\t\t\trecreateProcessingQueues(maxQueueSize);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setCompId(JID jid) {\n\t\tsuper.setCompId(jid);\n\t\tif (jid != null) {\n\t\t\tresourceForPacketWithTimeout = Algorithms.sha256(jid.getDomain());\n\t\t} else {\n\t\t\tresourceForPacketWithTimeout = null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\tsuper.setName(name);\n\t\tin_queues_size = processingInThreads();\n\t\tout_queues_size = processingOutThreads();\n\t\tschedulerThreads_size = schedulerThreads();\n\t\tupdateFiltersName();\n\t}\n\n\t@Override\n\tpublic void setParent(MessageReceiver parent) {\n\t\tthis.parent = parent;\n\t}\n\n\tpublic void setSchedulerThreads_size(int size) {\n\t\tlog.log(Level.FINE, \"Setting scheduler size to: {0}\", new Object[]{size});\n\t\tif (schedulerThreads_size != size) {\n\t\t\tthis.schedulerThreads_size = size;\n\n\t\t\tScheduledExecutorService scheduler = receiverScheduler;\n\t\t\treceiverScheduler = Executors.newScheduledThreadPool(size, threadFactory);\n\t\t\tscheduler.shutdown();\n\t\t}\n\t}\n\n\tpublic boolean addOutPacketWithTimeout(Packet packet, ReceiverTimeoutHandler handler, long delay, TimeUnit unit) {\n\n\t\t// It is automatically added to collections and the Timer\n\t\tnew PacketReceiverTask(handler, delay, unit, packet);\n\n\t\treturn addOutPacket(packet);\n\t}\n\n\tpublic boolean addOutPacketWithTimeout(Packet packet, Duration timeout, PacketWriterWithTimeout.Handler handler) {\n\t\tnew SimplePacketReceiverTask(handler, timeout, packet);\n\t\treturn addOutPacket(packet);\n\t}\n\t\n\tprotected boolean addOutPacket(Packet packet) {\n\t\tint queueIdx = Math.abs(hashCodeForPacket(packet) % out_queues_size);\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"[{0}]  queueIdx={1}, {2}\",\n\t\t\t\t\tnew Object[]{getName(), queueIdx, packet.toStringSecure()});\n\t\t}\n\t\ttry {\n\t\t\tout_queues.get(queueIdx).put(packet, packet.getPriority().ordinal());\n\t\t\t++statSentPacketsOk;\n\t\t} catch (InterruptedException e) {\n\t\t\t++statSentPacketsEr;\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Packet dropped for unknown reason: {0}\", packet);\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}    // end of try-catch\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Non blocking version of <code>addOutPacket</code>.\n\t *\n\t * @param packet a <code>Packet</code> value\n\t *\n\t * @return a <code>boolean</code> value\n\t */\n\tprotected boolean addOutPacketNB(Packet packet) {\n\t\tint queueIdx = Math.abs(hashCodeForPacket(packet) % out_queues_size);\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"[{0}]  queueIdx={1}, {2}\",\n\t\t\t\t\tnew Object[]{getName(), queueIdx, packet.toStringSecure()});\n\t\t}\n\n\t\tboolean result = false;\n\n\t\tresult = out_queues.get(queueIdx).offer(packet, packet.getPriority().ordinal());\n\t\tif (result) {\n\t\t\t++statSentPacketsOk;\n\t\t} else {\n\n\t\t\t// Queue overflow!\n\t\t\t++statSentPacketsEr;\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Packet dropped due to queue overflow: {0}\", packet);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprotected boolean addOutPackets(Queue<Packet> packets) {\n\t\tPacket p = null;\n\t\tboolean result = true;\n\n\t\twhile ((p = packets.peek()) != null) {\n\t\t\tresult = addOutPacket(p);\n\t\t\tif (result) {\n\t\t\t\tpackets.poll();\n\t\t\t} else {\n\t\t\t\treturn false;\n\t\t\t}    // end of if (result) else\n\t\t}      // end of while ()\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Method queues and executes timer tasks using ScheduledExecutorService which allows using more than one thread for\n\t * executing tasks.\n\t */\n\tprotected void addTimerTask(tigase.util.common.TimerTask task, long delay, TimeUnit unit) {\n\t\tif (task.isCancelled()) {\n\t\t\treturn;\n\t\t}\n\t\tif (receiverScheduler == null) {\n\t\t\ttasksAwaitingReceiver.offer(() -> this.addTimerTask(task, delay, unit));\n\t\t\treturn;\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"adding timer, task: {0}, delay: {1}, TimeUnit: {2}, receiverScheduler: {3}\",\n\t\t\t\t\tnew Object[]{task, delay, unit, receiverScheduler});\n\t\t}\n\t\tScheduledFuture<?> future = receiverScheduler.schedule(task, delay, unit);\n\n\t\ttask.setScheduledFuture(future);\n\t}\n\n\tprotected Integer getMaxQueueSize(int def) {\n\t\treturn def;\n\t}\n\n\tprivate void recreateProcessingQueues(int maxQueueSize) {\n\t\t// Processing threads number is split to incoming and outgoing queues...\n\t\t// So real processing threads number of in_queues is processingThreads()/2\n\t\tthis.maxInQueueSize = (maxQueueSize / processingInThreads) * 2;\n\t\tthis.maxOutQueueSize = (maxQueueSize / processingOutThreads) * 2;\n\t\tlog.log(Level.FINEST, \"{0} maxQueueSize: {1}, maxInQueueSize: {2}, maxOutQueueSize: {3}\",\n\t\t\t\tnew Object[]{getName(), maxQueueSize, maxInQueueSize, maxOutQueueSize});\n\t\tlog.log(Level.FINEST, \"{0} maxQueueSize: {1}, maxInQueueSize: {2}, maxOutQueueSize: {3}\",\n\t\t\t\tnew Object[]{getName(), maxQueueSize, maxInQueueSize, maxOutQueueSize});\n\n\t\tif (maxInQueueSize <= 15 || maxQueueSize <= 15) {\n\t\t\tTigaseRuntime.getTigaseRuntime()\n\t\t\t\t\t.shutdownTigase(new String[]{\"You configured component of class \" + getClass().getCanonicalName() +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t \" with packet queues total size set to \" + maxQueueSize,\n\t\t\t\t\t\t\t\t\t\t\t\t \"Component uses \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t Math.max(processingInThreads, processingOutThreads) +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t \" queues which would give a limit of \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t Math.min(maxInQueueSize, maxOutQueueSize) +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t \" packets per queue.\",\n\t\t\t\t\t\t\t\t\t\t\t\t \"This value is too low for Tigase XMPP Server to run properly. Please adjust the configuration of the server.\",\n\t\t\t\t\t\t\t\t\t\t\t\t \"max-queue-size should be set to at least \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t (Math.max(processingInThreads, processingOutThreads) / 2) *\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t 16});\n\t\t}\n\n\t\tif (in_queues.size() == 0) {\n\t\t\tfor (int i = 0; i < in_queues_size; i++) {\n\t\t\t\tPriorityQueueAbstract<Packet> queue = PriorityQueueAbstract.getPriorityQueue(pr_cache.length,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t maxInQueueSize,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t priorityQueueClass);\n\n\t\t\t\tin_queues.add(queue);\n\t\t\t}\n\t\t} else {\n\t\t\tfor (int i = 0; i < in_queues.size(); i++) {\n\t\t\t\tin_queues.get(i).setMaxSize(maxInQueueSize);\n\t\t\t}\n\t\t}\n\t\tif (out_queues.size() == 0) {\n\t\t\tfor (int i = 0; i < out_queues_size; i++) {\n\t\t\t\tPriorityQueueAbstract<Packet> queue = PriorityQueueAbstract.getPriorityQueue(pr_cache.length,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t maxOutQueueSize,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t priorityQueueClass);\n\n\t\t\t\tout_queues.add(queue);\n\t\t\t}\n\t\t} else {\n\t\t\tfor (int i = 0; i < out_queues.size(); i++) {\n\t\t\t\tout_queues.get(i).setMaxSize(maxOutQueueSize);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate Packet filterPacket(Packet packet, List<PacketFilterIfc> filters) {\n\t\tPacket result = packet;\n\n\t\tfor (PacketFilterIfc packetFilterIfc : filters) {\n\t\t\tresult = packetFilterIfc.filter(result);\n\t\t\tif (result == null) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate void startThreads() {\n\t\tif (log.isLoggable(Level.CONFIG)) {\n\t\t\tlog.log(Level.CONFIG,\n\t\t\t\t\t\"Starting threads, in_queues_size: {0}, out_queues_size: {1}, schedulerThreads_size: {2}\",\n\t\t\t\t\tnew Object[]{in_queues_size, out_queues_size, schedulerThreads_size});\n\t\t}\n\t\tif (threadsQueueIn == null) {\n\t\t\tthreadsQueueIn = new ArrayDeque<>(8);\n\t\t\tfor (int i = 0; i < in_queues_size; i++) {\n\t\t\t\tQueueListener in_thread = new QueueListener(in_queues.get(i), QueueType.IN_QUEUE);\n\n\t\t\t\tin_thread.setName(\"in_\" + i + \"-\" + getName());\n\t\t\t\tin_thread.start();\n\t\t\t\tthreadsQueueIn.add(in_thread);\n\t\t\t}\n\t\t}\n\t\tif (threadsQueueOut == null) {\n\t\t\tthreadsQueueOut = new ArrayDeque<>(8);\n\t\t\tfor (int i = 0; i < out_queues_size; i++) {\n\t\t\t\tQueueListener out_thread = new QueueListener(out_queues.get(i), QueueType.OUT_QUEUE);\n\n\t\t\t\tout_thread.setName(\"out_\" + i + \"-\" + getName());\n\t\t\t\tout_thread.start();\n\t\t\t\tthreadsQueueOut.add(out_thread);\n\t\t\t}\n\t\t}    // end of if (thread == null || ! thread.isAlive())\n\n\t\t// if ((out_thread == null) ||!out_thread.isAlive()) {\n\t\t// out_thread = new QueueListener(out_queue, QueueType.OUT_QUEUE);\n\t\t// out_thread.setName(\"out_\" + getName());\n\t\t// out_thread.start();\n\t\t// } // end of if (thread == null || ! thread.isAlive())\n\t\treceiverScheduler = Executors.newScheduledThreadPool(schedulerThreads_size, threadFactory);\n\t\treceiverTasks = new Timer(getName() + \" tasks\", true);\n\t\treceiverTasks.scheduleAtFixedRate(new TimerTask() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\teverySecond();\n\t\t\t}\n\t\t}, SECOND, SECOND);\n\t\treceiverTasks.scheduleAtFixedRate(new TimerTask() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\teveryMinute();\n\t\t\t}\n\t\t}, MINUTE, MINUTE);\n\t\treceiverTasks.scheduleAtFixedRate(new TimerTask() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\teveryHour();\n\t\t\t}\n\t\t}, HOUR, HOUR);\n\n\t\tRunnable task;\n\t\twhile ((task = tasksAwaitingReceiver.poll()) != null) {\n\t\t\ttask.run();\n\t\t}\n\t}\n\n\tprivate void stopThreads() {\n\t\t// stopped = true;\n\t\ttry {\n\t\t\tArrayDeque<QueueListener> threads = new ArrayDeque<>();\n\t\t\tif (threadsQueueIn != null) {\n\t\t\t\tthreads.addAll(threadsQueueIn);\n\t\t\t}\n\t\t\tif (threadsQueueOut != null) {\n\t\t\t\tthreads.addAll(threadsQueueOut);\n\t\t\t}\n\t\t\tif (out_thread != null) {\n\t\t\t\tthreads.add(out_thread);\n\t\t\t}\n\t\t\tstopThread(threads);\n\n\t\t} catch (InterruptedException e) {\n\t\t}\n\t\tthreadsQueueIn = null;\n\t\tthreadsQueueOut = null;\n\t\tout_thread = null;\n\t\tif (receiverTasks != null) {\n\t\t\treceiverTasks.cancel();\n\t\t\treceiverTasks = null;\n\t\t}\n\t\tif (receiverScheduler != null) {\n\t\t\treceiverScheduler.shutdownNow();\n\t\t\treceiverScheduler = null;\n\t\t}\n\t}\n\n\tprivate void stopThread(ArrayDeque<QueueListener> threadsQueue) throws InterruptedException {\n\t\tif (threadsQueue != null) {\n\t\t\tSet<QueueListener> awaiting = new HashSet<>();\n\t\t\tfor (QueueListener in_thread : threadsQueue) {\n\t\t\t\tin_thread.threadStopped = true;\n\t\t\t\tin_thread.interrupt();\n\t\t\t\tawaiting.add(in_thread);\n\t\t\t}\n\t\t\twhile (!awaiting.isEmpty()) {\n\t\t\t\tfor (QueueListener in_thread : threadsQueue) {\n\t\t\t\t\tif (!in_thread.isAlive()) {\n\t\t\t\t\t\tawaiting.remove(in_thread);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tThread.sleep(10);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate interface PacketReceiverTaskIfc {\n\n\t\tvoid handleResponse(Packet response);\n\n\t\tvoid handleTimeout();\n\n\t}\n\t\n\tprotected String getResourceForPacketWithTimeout() {\n\t\treturn resourceForPacketWithTimeout;\n\t}\n\n\tprivate class SimplePacketReceiverTask extends tigase.util.common.TimerTask implements PacketReceiverTaskIfc {\n\n\t\tprivate final PacketWriterWithTimeout.Handler handler;\n\t\tprivate final String id;\n\n\t\tSimplePacketReceiverTask(PacketWriterWithTimeout.Handler handler, Duration timeout, Packet packet) {\n\t\t\tthis.handler = handler;\n\n\t\t\tJID from = packet.getStanzaFrom();\n\t\t\tif (from.getResource() == null && isLocalDomainOrComponent(from.toString())) {\n\t\t\t\tfrom = from.copyWithResourceNS(getResourceForPacketWithTimeout());\n\t\t\t}\n\n\t\t\tif (packet.getStanzaId() != null) {\n\t\t\t\tthrow new IllegalArgumentException(\"Packet cannot have `id` set as it is required to be unique and will be set internally.\");\n\t\t\t}\n\t\t\tpacket.getElement().setAttribute(\"id\", UUID.randomUUID().toString());\n\n\t\t\tpacket.initVars(from, packet.getStanzaTo());\n\n\t\t\t// we need to generate id which will make this packet routable in the cluster!\n\t\t\tthis.id = packet.getStanzaFrom().toString() + packet.getStanzaId();\n\n\t\t\twaitingTasks.put(id, this);\n\n\t\t\taddTimerTask(this, timeout.getSeconds(), TimeUnit.SECONDS);\n\t\t}\n\n\t\tpublic void handleResponse(Packet response) {\n\t\t\tcancel();\n\t\t\tthis.handler.handle(response);\n\t\t}\n\n\t\tpublic void handleTimeout() {\n\t\t\twaitingTasks.remove(id);\n\t\t\tthis.handler.handle(null);\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\thandleTimeout();\n\t\t}\n\t\t\n\t}\n\n\tprivate class PacketReceiverTask\n\t\t\textends tigase.util.common.TimerTask implements PacketReceiverTaskIfc {\n\n\t\tprivate ReceiverTimeoutHandler handler = null;\n\t\tprivate String id = null;\n\t\tprivate Packet packet = null;\n\t\tprivate int retryCount = packetDeliveryRetryCount;\n\n\t\tprivate PacketReceiverTask(ReceiverTimeoutHandler handler, long delay, TimeUnit unit, Packet packet) {\n\t\t\tsuper();\n\t\t\tthis.handler = handler;\n\t\t\tthis.packet = packet;\n\t\t\tthis.id = packet.getFrom().toString() + packet.getStanzaId();\n\n\t\t\tString countStr = packet.getElement().getAttributeStaticStr(\"retryCount\");\n\t\t\tif (countStr != null) {\n\t\t\t\tretryCount = Byte.valueOf(countStr);\n\t\t\t\tretryCount--;\n\t\t\t}\n\t\t\tthis.packet.getElement().setAttribute(\"retryCount\", Integer.toString(retryCount));\n\n\t\t\tif (retryCount < 0) {\n\t\t\t\tif (log.isLoggable(Level.WARNING)) {\n\t\t\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\t\t\"Dropping command packet! Retry limit reached for packet with ID: {0}, retryCount: {1}, packet: {2}\",\n\t\t\t\t\t\t\tnew Object[]{id, retryCount, this.packet});\n\t\t\t\t}\n\t\t\t\tPacketReceiverTaskIfc remove = waitingTasks.remove(id);\n\t\t\t\tif (remove instanceof tigase.util.common.TimerTask) {\n\t\t\t\t\t((tigase.util.common.TimerTask) remove).cancel();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tString delayStr = packet.getElement().getAttributeStaticStr(\"delay\");\n\t\t\tif (delayStr != null) {\n\t\t\t\tdelay = (long) (Long.valueOf(delayStr) * 1.5);\n\t\t\t}\n\n\t\t\tthis.packet.getElement().setAttribute(\"delay\", Long.toString(delay));\n\n\t\t\twaitingTasks.put(id, this);\n\n\t\t\taddTimerTask(this, delay, unit);\n\n\t\t\ttry {\n\t\t\t\tthis.packet.initVars();\n\t\t\t} catch (TigaseStringprepException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Reinitializing packet failed\", e);\n\t\t\t}\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"[{0}] Added timeout task for ID: {1}, delay: {2}, retryCount: {3}, packet: {4}\",\n\t\t\t\t\t\tnew Object[]{getName(), id, delay, retryCount, this.packet});\n\t\t\t}\n\t\t}\n\n\t\tpublic void handleResponse(Packet response) {\n\t\t\tthis.cancel();\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"[{0}] Response received for id: {1}\", new Object[]{getName(), id});\n\t\t\t}\n\t\t\thandler.responseReceived(packet, response);\n\t\t}\n\n\t\tpublic void handleTimeout() {\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"[{0}] Fired timeout for id: {1}\", new Object[]{getName(), id});\n\t\t\t}\n\t\t\twaitingTasks.remove(id);\n\t\t\thandler.timeOutExpired(packet);\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\thandleTimeout();\n\t\t}\n\t}\n\n\tprivate class QueueListener\n\t\t\textends Thread {\n\n\t\tprivate String compName = null;\n\t\tprivate long packetCounter = 0;\n\t\tprivate PriorityQueueAbstract<Packet> queue;\n\t\tprivate boolean threadStopped = false;\n\t\tprivate QueueType type = null;\n\n\t\tprivate QueueListener(PriorityQueueAbstract<Packet> q, QueueType type) {\n\t\t\tthis.queue = q;\n\t\t\tthis.type = type;\n\t\t\tcompName = AbstractMessageReceiver.this.getName();\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"{0} starting queue processing.\", getName());\n\t\t\t}\n\n\t\t\tPacket packet = null;\n\t\t\tQueue<Packet> results = new ArrayDeque<Packet>(2);\n\n\t\t\twhile (!threadStopped) {\n\t\t\t\ttry {\n\n\t\t\t\t\t// Now process next waiting packet\n\t\t\t\t\t// log.finest(\"[\" + getName() + \"] before take... \" + type);\n\t\t\t\t\t// packet = queue.take(getName() + \":\" + type);\n\t\t\t\t\tpacket = queue.take();\n\t\t\t\t\t++packetCounter;\n\n\t\t\t\t\t// if (log.isLoggable(Level.INFO)) {\n\t\t\t\t\t// log.log(Level.INFO, (\"[\" + getName() + \"] packet from \" + type + \" queue: \" +\n\t\t\t\t\t// packet);\n\t\t\t\t\t// }\n\t\t\t\t\tswitch (type) {\n\t\t\t\t\t\tcase IN_QUEUE:\n\t\t\t\t\t\t\tlong startPPT = System.currentTimeMillis();\n\n\t\t\t\t\t\t\t// tracer.trace(null, packet.getElemTo(), packet.getElemFrom(),\n\t\t\t\t\t\t\t// packet.getFrom(), getName(), type.name(), null, packet);\n\t\t\t\t\t\t\tPacketReceiverTaskIfc task = null;\n\n\t\t\t\t\t\t\tif (packet.getTo() != null) {\n\t\t\t\t\t\t\t\tString id = packet.getTo().toString() + packet.getStanzaId();\n\n\t\t\t\t\t\t\t\ttask = waitingTasks.remove(id);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (task == null && packet.getStanzaTo() != null) {\n\t\t\t\t\t\t\t\tString id = packet.getStanzaTo().toString() + packet.getStanzaId();\n\n\t\t\t\t\t\t\t\ttask = waitingTasks.remove(id);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (task != null) {\n\t\t\t\t\t\t\t\ttask.handleResponse(packet);\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t// log.finest(\"[\" + getName() + \"]  \" +\n\t\t\t\t\t\t\t\t// \"No task found for id: \" + id);\n\t\t\t\t\t\t\t\t// Maybe this is a command for local processing...\n\t\t\t\t\t\t\t\tboolean processed = false;\n\n\t\t\t\t\t\t\t\tif (packet.isCommand() && (packet.getStanzaTo() != null) &&\n\t\t\t\t\t\t\t\t\t\tcompName.equals(packet.getStanzaTo().getLocalpart()) &&\n\t\t\t\t\t\t\t\t\t\tisLocalDomain(packet.getStanzaTo().getDomain())) {\n\t\t\t\t\t\t\t\t\tprocessed = processScriptCommand(packet, results);\n\t\t\t\t\t\t\t\t\tif (processed) {\n\t\t\t\t\t\t\t\t\t\tPacket result = null;\n\n\t\t\t\t\t\t\t\t\t\twhile ((result = results.poll()) != null) {\n\t\t\t\t\t\t\t\t\t\t\taddOutPacket(result);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (!processed && ((packet = filterPacket(packet, incoming_filters.getFilters())) != null)) {\n\t\t\t\t\t\t\t\t\tprocessPacket(packet);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// It is all concurrent so we have to use a local index variable\n\t\t\t\t\t\t\t\tint idx = pptIdx;\n\n\t\t\t\t\t\t\t\tpptIdx = (pptIdx + 1) % processPacketTimings.length;\n\n\t\t\t\t\t\t\t\tlong timing = System.currentTimeMillis() - startPPT;\n\n\t\t\t\t\t\t\t\tprocessPacketTimings[idx] = timing;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase OUT_QUEUE:\n\n\t\t\t\t\t\t\t// tracer.trace(null, packet.getElemTo(), packet.getElemFrom(),\n\t\t\t\t\t\t\t// packet.getTo(), getName(), type.name(), null, packet);\n\t\t\t\t\t\t\tif ((packet = filterPacket(packet, outgoing_filters.getFilters())) != null) {\n\t\t\t\t\t\t\t\tprocessOutPacket(packet);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tlog.log(Level.SEVERE, \"Unknown queue element type: {0}\", type);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}    // end of switch (qel.type)\n\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\tSystem.out.println(\"interrupted \" + getName());\n\t\t\t\t\t// log.log(Level.SEVERE, \"Exception during packet processing: \", e);\n\t\t\t\t\t// stopped = true;\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tif (!threadStopped) {\n\t\t\t\t\t\tlog.log(Level.SEVERE, \"[\" + getName() + \"] Exception during packet processing: \" + packet, e);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t//log.log(Level.FINEST, \"[\" + getName() + \"] Stopping processing thread\");\n\t\t\t\t\t}\n\t\t\t\t}    // end of try-catch\n\t\t\t}      // end of while (! threadStopped)\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn String.valueOf(packetCounter);\n\t\t}\n\t}\n}    // AbstractMessageReceiver\n"
  },
  {
    "path": "src/main/java/tigase/server/BasicComponent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.cluster.api.ClusterControllerIfc;\nimport tigase.cluster.api.ClusteredComponentIfc;\nimport tigase.conf.Configurable;\nimport tigase.conf.ConfigurationException;\nimport tigase.disco.ServiceEntity;\nimport tigase.disco.ServiceIdentity;\nimport tigase.disco.XMPPService;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.beans.config.ConfigurationChangedAware;\nimport tigase.osgi.ModulesManagerImpl;\nimport tigase.osgi.OSGiScriptEngineManager;\nimport tigase.server.script.AddScriptCommand;\nimport tigase.server.script.CommandIfc;\nimport tigase.server.script.RemoveScriptCommand;\nimport tigase.stats.ComponentStatisticsProvider;\nimport tigase.stats.StatisticsList;\nimport tigase.util.common.DependencyChecker;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.vhosts.*;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport javax.script.Bindings;\nimport javax.script.ScriptEngineFactory;\nimport javax.script.ScriptEngineManager;\nimport javax.script.ScriptException;\nimport java.io.*;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentSkipListSet;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.concurrent.CopyOnWriteArraySet;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * Created: Oct 17, 2009 7:49:05 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class BasicComponent\n\t\timplements Configurable,\n\t\t\t\t   XMPPService,\n\t\t\t\t   VHostListener,\n\t\t\t\t   ClusteredComponentIfc,\n\t\t\t\t   Initializable,\n\t\t\t\t   ConfigurationChangedAware {\n\n\tpublic static final String ALL_PROP_KEY = \"ALL\";\n\n\tpublic static final String COMMAND_PROP_NODE = \"command\";\n\n\tpublic static final String SCRIPTS_DIR_PROP_DEF = \"scripts/admin\";\n\n\tpublic static final String SCRIPTS_DIR_PROP_KEY = \"scripts-dir\";\n\n\tprivate static final Logger log = Logger.getLogger(BasicComponent.class.getName());\n\n\tprivate final CopyOnWriteArrayList<JID> connectedNodes = new CopyOnWriteArrayList<JID>();\n\tprivate final CopyOnWriteArrayList<JID> connectedNodesWithLocal = new CopyOnWriteArrayList<JID>();\n\tprivate final List<JID> connectedNodesWithLocal_ro = Collections.unmodifiableList(connectedNodesWithLocal);\n\tprivate final List<JID> connectedNodes_ro = Collections.unmodifiableList(connectedNodes);\n\t@ConfigField(desc = \"List of admins JIDs\", alias = \"admins\")\n\tprotected ConcurrentSkipListSet<BareJID> admins = new ConcurrentSkipListSet<BareJID>();\n\tprotected Map<String, CommandIfc> scriptCommands = new ConcurrentHashMap<String, CommandIfc>(20);\n\tprotected ConcurrentSkipListSet<String> trusted = new ConcurrentSkipListSet<String>();\n\t@Inject(nullAllowed = true)\n\tprotected VHostManagerIfc vHostManager = null;\n\tprivate String DEF_HOSTNAME_PROP_VAL = null;\n\tprivate ComponentInfo cmpInfo = null;\n\t@ConfigField(alias = \"commands\", desc = \"Commands ACL\")\n\tprivate ConcurrentHashMap<String, CopyOnWriteArraySet<CmdAcl>> commandsACL = new ConcurrentHashMap<>(20);\n\t@ConfigField(desc = \"Component JID\")\n\tprivate JID compId = null;\n\t@ConfigField(desc = \"Default hostname\")\n\tprivate BareJID defHostname = null;\n\t@ConfigField(desc = \"Service Discovery Extensions\", alias = \"disco-extensions\")\n\tprivate Map<String, ArrayList<String>> discoExtensions = new HashMap<>();\n\tprivate boolean initializationCompleted = false;\n\t@ConfigField(desc = \"Component name\")\n\tprivate String name = null;\n\tprivate boolean nonAdminCommands = false;\n\tprotected ScriptEngineManager scriptEngineManager = null;\n\t@ConfigField(desc = \"Base directory for scripts\", alias = SCRIPTS_DIR_PROP_KEY)\n\tprivate String scriptsBaseDir = SCRIPTS_DIR_PROP_DEF;\n\tprivate String scriptsCompDir = null;\n\tprivate ServiceEntity serviceEntity = null;\n\t@Inject(nullAllowed = true)\n\tprivate List<ComponentStatisticsProvider> statisticsProviders;\n\t@ConfigField(alias = \"trusted\", desc = \"List of trusted JIDs\")\n\tprivate String[] trustedProp = null;\n\n\tpublic BasicComponent() {\n\t\tDependencyChecker.checkDependencies(getClass());\n\n\t\tDEF_HOSTNAME_PROP_VAL = DNSResolverFactory.getInstance().getDefaultHost();\n\t\tdefHostname = BareJID.bareJIDInstanceNS(DEF_HOSTNAME_PROP_VAL);\n\t}\n\n\tpublic void addComponentDomain(String domain) {\n\t\tvHostManager.addComponentDomain(domain);\n\t}\n\n\t@Override\n\tpublic void beanConfigurationChanged(Collection<String> changedFields) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Bean: {0} configuration changed: {1}\",\n\t\t\t\t\tnew Object[]{this.getClass().getName(), changedFields});\n\t\t}\n\t\tif (changedFields.contains(\"trusted\")) {\n\t\t\trefreshTrustedJids();\n\t\t}\n\t}\n\n\t/**\n\t * Method checks if following adhoc command can execute from this JID\n\t *\n\t * @param jid - JID of entity which wants to execute command\n\t * @param commandId - ID of an adhoc command\n\t *\n\t * @return a value of <code>boolean</code>\n\t */\n\tpublic boolean canCallCommand(JID jid, String commandId) {\n\t\treturn canCallCommand(jid, null, commandId);\n\t}\n\n\tpublic boolean canCallCommand(JID jid, String domain, String commandId) {\n\t\tif (jid == null) {\n\t\t\treturn false;\n\t\t}\n\t\tboolean result = isAdmin(jid) || isTrusted(jid);\n\n\t\tif (result) {\n\t\t\treturn true;\n\t\t}\n\n\t\tSet<CmdAcl> acl = commandsACL.get(ALL_PROP_KEY);\n\n\t\tif (acl != null) {\n\t\t\tresult = checkCommandAcl(jid, domain, acl);\n\t\t}\n\t\tif (!result) {\n\t\t\tacl = commandsACL.get(commandId);\n\t\t\tif (acl != null) {\n\t\t\t\tresult = checkCommandAcl(jid, domain, acl);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Check if entity with JID is allowed ot execute command with passed access control list.\n\t *\n\t * @param jid - entity JID\n\t * @param acl - access control list\n\t *\n\t * @return a value of <code>boolean</code>\n\t */\n\tpublic boolean checkCommandAcl(JID jid, Set<CmdAcl> acl) {\n\t\treturn checkCommandAcl(jid, null, acl);\n\t}\n\n\t/**\n\t * Check if entity with JID is allowed ot execute command with passed access control list.\n\t *\n\t * @param jid - entity JID\n\t * @param domain - domain for which check permission\n\t * @param acl - access control list\n\t *\n\t * @return a value of <code>boolean</code>\n\t */\n\tpublic boolean checkCommandAcl(JID jid, String domain, Set<CmdAcl> acl) {\n\t\tfor (CmdAcl cmdAcl : acl) {\n\t\t\tswitch (cmdAcl.getType()) {\n\t\t\t\tcase ALL:\n\t\t\t\t\treturn true;\n\n\t\t\t\tcase ADMIN:\n\t\t\t\t\tif (isAdmin(jid)) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase LOCAL:\n\t\t\t\t\tif (isLocalDomain(jid.getDomain())) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase NONE:\n\t\t\t\t\treturn false;\n\n\t\t\t\tcase DOMAIN_ADMIN:\n\t\t\t\t\tif (isLocalDomain(jid.getDomain())) {\n\t\t\t\t\t\tif (domain == null) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tVHostItem vHostItem = getVHostItem(domain);\n\t\t\t\t\t\tif (vHostItem == null) {\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (vHostItem.isAdmin(jid.getBareJID().toString())) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DOMAIN_OWNER:\n\t\t\t\t\tif (isLocalDomain(jid.getDomain())) {\n\t\t\t\t\t\tif (domain == null) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tVHostItem vHostItem = getVHostItem(domain);\n\t\t\t\t\t\tif (vHostItem == null) {\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (vHostItem.isOwner(jid.getBareJID().toString())) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase DOMAIN:\n\t\t\t\t\tif (cmdAcl.isDomainAllowed(jid.getDomain())) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase JID:\n\t\t\t\tdefault:\n\t\t\t\t\tif (cmdAcl.isJIDAllowed(jid.getBareJID())) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tpublic void everyHour() {\n\t\tfor (CommandIfc comm : scriptCommands.values()) {\n\t\t\tcomm.everyHour();\n\t\t}\n\t\tif (statisticsProviders != null) {\n\t\t\tstatisticsProviders.forEach(ComponentStatisticsProvider::everyHour);\n\t\t}\n\t}\n\n\tpublic void everyMinute() {\n\t\tfor (CommandIfc comm : scriptCommands.values()) {\n\t\t\tcomm.everyMinute();\n\t\t}\n\t\tif (statisticsProviders != null) {\n\t\t\tstatisticsProviders.forEach(ComponentStatisticsProvider::everyMinute);\n\t\t}\n\t}\n\n\tpublic void everySecond() {\n\t\tfor (CommandIfc comm : scriptCommands.values()) {\n\t\t\tcomm.everySecond();\n\t\t}\n\t\tif (statisticsProviders != null) {\n\t\t\tstatisticsProviders.forEach(ComponentStatisticsProvider::everySecond);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean handlesLocalDomains() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean handlesNameSubdomains() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean handlesNonLocalDomains() {\n\t\treturn false;\n\t}\n\n\t/**\n\t * Initialize a mapping of key/value pairs which can be used in scripts loaded by the server\n\t *\n\t * @param binds A mapping of key/value pairs, all of whose keys are Strings.\n\t */\n\tpublic void initBindings(Bindings binds) {\n\t\tbinds.put(CommandIfc.VHOST_MANAGER, vHostManager);\n\t\tbinds.put(CommandIfc.ADMINS_SET, admins);\n\t\tbinds.put(CommandIfc.COMMANDS_ACL, commandsACL);\n\t\tbinds.put(CommandIfc.SCRI_MANA, scriptEngineManager);\n\t\tbinds.put(CommandIfc.ADMN_CMDS, scriptCommands);\n\t\tbinds.put(CommandIfc.ADMN_DISC, serviceEntity);\n\t\tbinds.put(CommandIfc.SCRIPT_BASE_DIR, scriptsBaseDir);\n\t\tbinds.put(CommandIfc.SCRIPT_COMP_DIR, scriptsCompDir);\n\t\tbinds.put(CommandIfc.CONNECTED_NODES, connectedNodes);\n\t\tbinds.put(CommandIfc.CONNECTED_NODES_WITH_LOCAL, connectedNodesWithLocal);\n\t\tbinds.put(CommandIfc.COMPONENT_NAME, getName());\n\t\tbinds.put(CommandIfc.COMPONENT, this);\n\t\tbinds.put(CommandIfc.EVENTBUS, EventBusFactory.getInstance());\n\t}\n\n\t@Override\n\tpublic void initializationCompleted() {\n\t\tinitializationCompleted = true;\n\t}\n\n\t@Override\n\tpublic void nodeConnected(String node) {\n\t\tJID jid = JID.jidInstanceNS(getName(), node, null);\n\t\tboolean added = false;\n\n\t\tsynchronized (connectedNodesWithLocal) {\n\t\t\tif (!connectedNodesWithLocal.contains(jid)) {\n\t\t\t\tJID[] tmp = connectedNodesWithLocal.toArray(new JID[connectedNodesWithLocal.size() + 1]);\n\t\t\t\ttmp[tmp.length - 1] = jid;\n\t\t\t\tArrays.sort(tmp);\n\t\t\t\tint pos = Arrays.binarySearch(tmp, jid);\n\t\t\t\tconnectedNodesWithLocal.add(pos, jid);\n\t\t\t\tadded = true;\n\t\t\t}\n\t\t}\n\n\t\tsynchronized (connectedNodes) {\n\t\t\tif (!connectedNodes.contains(jid) && !getComponentId().equals(jid)) {\n\t\t\t\tJID[] tmp = connectedNodes.toArray(new JID[connectedNodes.size() + 1]);\n\t\t\t\ttmp[tmp.length - 1] = jid;\n\t\t\t\tArrays.sort(tmp);\n\t\t\t\tint pos = Arrays.binarySearch(tmp, jid);\n\t\t\t\tconnectedNodes.add(pos, jid);\n\t\t\t\tadded = true;\n\t\t\t}\n\t\t}\n\n\t\tif (added) {\n\t\t\tlog.log(Level.FINE, \"Node connected: {0}\", node);\n\t\t\tonNodeConnected(jid);\n\t\t\trefreshTrustedJids();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void nodeDisconnected(String node) {\n\t\tJID jid = JID.jidInstanceNS(getName(), node, null);\n\t\tboolean removed = false;\n\n\t\tsynchronized (connectedNodesWithLocal) {\n\t\t\tremoved |= connectedNodesWithLocal.remove(jid);\n\t\t}\n\n\t\tsynchronized (connectedNodes) {\n\t\t\tremoved |= connectedNodes.remove(jid);\n\t\t}\n\n\t\tif (removed) {\n\t\t\tlog.log(Level.FINE, \"Node disonnected: {0}\", node);\n\t\t\tonNodeDisconnected(jid);\n\t\t\trefreshTrustedJids();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processPacket(Packet packet, Queue<Packet> results) {\n\t\tif (packet.isCommand() && getName().equals(packet.getStanzaTo().getLocalpart()) &&\n\t\t\t\tisLocalDomain(packet.getStanzaTo().getDomain())) {\n\t\t\tprocessScriptCommand(packet, results);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void release() {\n\t}\n\n\tpublic void removeComponentDomain(String domain) {\n\t\tvHostManager.removeComponentDomain(domain);\n\t}\n\n\tpublic void removeServiceDiscoveryItem(String jid, String node, String description) {\n\t\tServiceEntity item = new ServiceEntity(jid, node, description, null);\n\n\t\t// item.addIdentities(new ServiceIdentity(\"component\", identity_type,\n\t\t// name));\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Modifying service-discovery info, removing: {0}\", item);\n\t\t}\n\t\tserviceEntity.removeItems(item);\n\t}\n\n\t@Override\n\tpublic void setClusterController(ClusterControllerIfc cl_controller) {\n\n\t}\n\n\tpublic void updateServiceDiscoveryItem(String jid, String node, String description, boolean admin) {\n\t\tupdateServiceDiscoveryItem(jid, node, description, admin, (String[]) null);\n\t}\n\n\tpublic void updateServiceDiscoveryItem(String jid, String node, String description, boolean admin,\n\t\t\t\t\t\t\t\t\t\t   String... features) {\n\t\tupdateServiceDiscoveryItem(jid, node, description, null, null, admin, features);\n\t}\n\n\tpublic void updateServiceDiscoveryItem(String jid, String node, String description, String category, String type,\n\t\t\t\t\t\t\t\t\t\t   boolean admin, String... features) {\n\t\tif (serviceEntity.getJID().equals(jid) && (serviceEntity.getNode() == node)) {\n\t\t\tserviceEntity.setAdminOnly(admin);\n\t\t\tserviceEntity.setDescription(description);\n\t\t\tif ((category != null) || (type != null)) {\n\t\t\t\tserviceEntity.addIdentities(new ServiceIdentity(category, type, description));\n\t\t\t}\n\t\t\tif (features != null) {\n\t\t\t\tserviceEntity.setFeatures(\"http://jabber.org/protocol/commands\");\n\t\t\t\tserviceEntity.addFeatures(features);\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Modifying service-discovery info: {0}\", serviceEntity);\n\t\t\t}\n\t\t} else {\n\t\t\tServiceEntity item = new ServiceEntity(jid, node, description, this::getDiscoExtensionsForm, admin);\n\n\t\t\tif ((category != null) || (type != null)) {\n\t\t\t\titem.addIdentities(new ServiceIdentity(category, type, description));\n\t\t\t}\n\t\t\tif (features != null) {\n\t\t\t\titem.addFeatures(features);\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Adding new item: {0}\", item);\n\t\t\t}\n\t\t\tserviceEntity.addItems(item);\n\t\t}\n\t}\n\n\tpublic void updateServiceEntity() {\n\t\tserviceEntity = new ServiceEntity(name, null, getDiscoDescription(), this::getDiscoExtensionsForm, true);\n\t\tserviceEntity.addIdentities(\n\t\t\t\tnew ServiceIdentity(getDiscoCategory(), getDiscoCategoryType(), getDiscoDescription()));\n\t\tserviceEntity.addFeatures(\"http://jabber.org/protocol/commands\");\n//\t\tfinal Element discoExtensionsForm = getDiscoExtensionsForm();\n//\t\tif (discoExtensionsForm != null) {\n//\t\t\tserviceEntity.setExtensions(discoExtensionsForm);\n//\t\t}\n\t}\n\n\t@Override\n\tpublic JID getComponentId() {\n\t\treturn compId;\n\t}\n\n\tpublic void setCompId(JID jid) {\n\t\tthis.compId = jid;\n\t}\n\n\t@Override\n\tpublic ComponentInfo getComponentInfo() {\n\t\tif (cmpInfo == null) {\n\t\t\tcmpInfo = new ComponentInfo(getName(), this.getClass());\n\t\t}\n\n\t\treturn cmpInfo;\n\t}\n\n\t@Override\n\t@Deprecated\n\tpublic Map<String, Object> getDefaults(Map<String, Object> params) {\n\t\tMap<String, Object> defs = new LinkedHashMap<String, Object>(50);\n\n\t\tdefs.put(COMPONENT_ID_PROP_KEY, compId.toString());\n\t\tDEF_HOSTNAME_PROP_VAL = DNSResolverFactory.getInstance().getDefaultHost();\n\t\tdefs.put(DEF_HOSTNAME_PROP_KEY, DEF_HOSTNAME_PROP_VAL);\n\n\t\treturn defs;\n\t}\n\n\tpublic BareJID getDefHostName() {\n\t\treturn defHostname;\n\t}\n\n\tpublic BareJID getDefVHostItem() {\n\t\treturn (vHostManager != null) ? vHostManager.getDefVHostItem() : getDefHostName();\n\t}\n\n\t/**\n\t * Method returns category of a component used for service discovery responses.\n\t *\n\t * @return category of a component\n\t */\n\tpublic String getDiscoCategory() {\n\t\treturn \"component\";\n\t}\n\n\t/**\n\t * Method returns component category type used for service discovery responses.\n\t *\n\t * @return category type of a component\n\t */\n\tpublic String getDiscoCategoryType() {\n\t\treturn \"generic\";\n\t}\n\n\t/**\n\t * Method returns description used for service discovery responses.\n\t *\n\t * @return description of a component\n\t */\n\tpublic String getDiscoDescription() {\n\t\treturn \"Undefined description\";\n\t}\n\n\t/**\n\t * Method returns list of features provided by this component.\n\t *\n\t * @return list of features\n\t */\n\tpublic List<Element> getDiscoFeatures() {\n\t\treturn null;\n\t}\n\n\t/**\n\t * Method returns list of features provided by this component for provided JID.\n\t *\n\t * @return list of features\n\t */\n\t@Override\n\tpublic List<Element> getDiscoFeatures(JID from) {\n\t\treturn getDiscoFeatures();\n\t}\n\n\t@Override\n\tpublic Element getDiscoInfo(String node, JID jid, JID from) {\n\t\tif (getName().equals(jid.getLocalpart()) || jid.toString().startsWith(getName() + \".\")) {\n\t\t\tElement queryEl = serviceEntity.getDiscoInfo(node, isAdmin(from) || hasNonAdminCommands());\n\t\t\tif (queryEl != null) {\n\t\t\t\tElement form = getDiscoExtensionsForm(jid.getDomain());\n\t\t\t\tif (form != null) {\n\t\t\t\t\tqueryEl.addChild(form);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn queryEl;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprotected boolean hasNonAdminCommands() {\n\t\treturn nonAdminCommands;\n\t}\n\n\tprivate static final List<String> DISCO_EXTENSION_ADDRESSES = Arrays.asList(\"abuse-addresses\", \"admin-addresses\", \"feedback-addresses\", \"sales-addresses\", \"security-addresses\", \"support-addresses\");\n\n\tpublic Element getDiscoExtensionsForm(String domain) {\n\t\tVHostItem vHostItem = this.vHostManager.getVHostItemDomainOrComponent(domain);\n\t\tElement form = null;\n\n\t\tif (vHostItem != null) {\n\t\t\tServerInfoVHostItemExtension extension = vHostItem.getExtension(ServerInfoVHostItemExtension.class);\n\t\t\tFunction<String, Supplier<List<String>>> addressesFromVHost = field -> {\n\t\t\t\tif (extension == null) {\n\t\t\t\t\treturn Collections::emptyList;\n\t\t\t\t}\n\t\t\t\tswitch (field) {\n\t\t\t\t\tcase \"abuse-addresses\":\n\t\t\t\t\t\treturn extension::getAbuseAddresses;\n\t\t\t\t\tcase \"admin-addresses\":\n\t\t\t\t\t\treturn extension::getAdminAddresses;\n\t\t\t\t\tcase \"feedback-addresses\":\n\t\t\t\t\t\treturn extension::getFeedbackAddresses;\n\t\t\t\t\tcase \"sales-addresses\":\n\t\t\t\t\t\treturn extension::getSalesAddresses;\n\t\t\t\t\tcase \"security-addresses\":\n\t\t\t\t\t\treturn extension::getSecurityAddresses;\n\t\t\t\t\tcase \"support-addresses\":\n\t\t\t\t\t\treturn extension::getSupportAddresses;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn Collections::emptyList;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tfor (String field : DISCO_EXTENSION_ADDRESSES) {\n\t\t\t\tList<String> vhostAddresses = addressesFromVHost.apply(field).get();\n\t\t\t\tList<String> globalAddresses = discoExtensions.get(field);\n\n\t\t\t\tif (vhostAddresses.isEmpty() && (globalAddresses == null || globalAddresses.isEmpty())) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tList<String> addresses = globalAddresses == null\n\t\t\t\t\t\t\t\t\t\t ? vhostAddresses\n\t\t\t\t\t\t\t\t\t\t : Stream.concat(vhostAddresses.stream(), globalAddresses.stream()).collect(Collectors.toList());\n\n\t\t\t\tif (form == null) {\n\t\t\t\t\tform = DataForm.createDataForm(Command.DataType.result);\n\t\t\t\t\tDataForm.addHiddenField(form, \"FORM_TYPE\", \"http://jabber.org/network/serverinfo\");\n\t\t\t\t}\n\t\t\t\tDataForm.addFieldListMultiValue(form, field, addresses);\n\t\t\t}\n\t\t}\n\n\t\tif (!discoExtensions.isEmpty()) {\n\t\t\tif (form == null) {\n\t\t\t\tform = DataForm.createDataForm(Command.DataType.result);\n\t\t\t\tDataForm.addHiddenField(form, \"FORM_TYPE\", \"http://jabber.org/network/serverinfo\");\n\t\t\t}\n\n\t\t\tfor (Map.Entry<String, ArrayList<String>> item : discoExtensions.entrySet()) {\n\t\t\t\tif (DISCO_EXTENSION_ADDRESSES.contains(item.getKey())) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tDataForm.addFieldListMultiValue(form, item.getKey(), item.getValue());\n\t\t\t}\n\t\t\treturn form;\n\t\t}\n\t\treturn form;\n\t}\n\n\t@Override\n\tpublic List<Element> getDiscoItems(String node, JID jid, JID from) {\n\t\tList<Element> result = null;\n\t\tboolean isAdminFrom = isAdmin(from);\n\n\t\tif (getName().equals(jid.getLocalpart()) || jid.toString().startsWith(getName() + \".\")) {\n\t\t\tif (node != null) {\n\t\t\t\tif (node.equals(\"http://jabber.org/protocol/commands\") && (isAdminFrom || hasNonAdminCommands())) {\n\t\t\t\t\tresult = getScriptItems(node, jid, from);\n\t\t\t\t} else {\n\t\t\t\t\tresult = serviceEntity.getDiscoItems(node, jid.toString(), (isAdminFrom || hasNonAdminCommands()));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tresult = serviceEntity.getDiscoItems(null, jid.toString(), (isAdminFrom || hasNonAdminCommands()));\n\t\t\t\tif (result != null) {\n\t\t\t\t\tfor (Iterator<Element> it = result.iterator(); it.hasNext(); ) {\n\t\t\t\t\t\tElement element = it.next();\n\n\t\t\t\t\t\tif (element.getAttributeStaticStr(\"node\") == null) {\n\t\t\t\t\t\t\tit.remove();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Element result = serviceEntity.getDiscoItem(null, getName() + \".\" + jid);\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Found disco items: {0}\", ((result != null) ? result.toString() : null));\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"{0} General disco items request, node: {1}\", new Object[]{getName(), node});\n\t\t\t}\n\t\t\tif (node == null) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"{0} Disco items request for null node\", new Object[]{getName()});\n\t\t\t\t}\n\n\t\t\t\tElement res = null;\n\n\t\t\t\tif (!serviceEntity.isAdminOnly() || isAdmin(from) || hasNonAdminCommands()) {\n\t\t\t\t\tres = serviceEntity.getDiscoItem(null, isSubdomain()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   ? (getName() + \".\" + jid)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   : getName() + \"@\" + jid.toString());\n\t\t\t\t}\n\t\t\t\tresult = serviceEntity.getDiscoItems(null, null, (isAdminFrom || hasNonAdminCommands()));\n\t\t\t\tif (res != null) {\n\t\t\t\t\tif (result != null) {\n\t\t\t\t\t\tfor (Iterator<Element> it = result.iterator(); it.hasNext(); ) {\n\t\t\t\t\t\t\tElement element = it.next();\n\n\t\t\t\t\t\t\tif (element.getAttributeStaticStr(\"node\") != null) {\n\t\t\t\t\t\t\t\tit.remove();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresult.add(0, res);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult = Arrays.asList(res);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\n\t\ttry {\n\t\t\tsetCompId(JID.jidInstance(name, defHostname.getDomain(), null));\n\t\t} catch (TigaseStringprepException ex) {\n\t\t\tlog.log(Level.WARNING, \"Problem setting component ID: \", ex);\n\t\t}\n\t}\n\n\tpublic void getStatistics(StatisticsList list) {\n\t\tString compName = getName();\n\t\tfor (CommandIfc comm : scriptCommands.values()) {\n\t\t\tcomm.getStatistics(compName, list);\n\t\t}\n\t\tif (connectedNodes.size() > 0) {\n\t\t\tlist.add(getName(), \"Known cluster nodes\", connectedNodes.size(), Level.FINEST);\n\t\t}\n\n\t\tif (statisticsProviders != null) {\n\t\t\tstatisticsProviders.stream()\n\t\t\t\t\t.filter(provider -> provider.belongsTo(this.getClass()))\n\t\t\t\t\t.forEach(provider -> provider.getStatistics(compName, list));\n\t\t}\n\t}\n\n\tpublic List<Element> getScriptItems(String node, JID jid, JID from) {\n\t\tLinkedList<Element> result = null;\n\t\tboolean isAdminFrom = isAdmin(from);\n\n\t\tif (node.equals(\"http://jabber.org/protocol/commands\") && (isAdminFrom || hasNonAdminCommands())) {\n\t\t\tresult = new LinkedList<Element>();\n\t\t\tfor (CommandIfc comm : scriptCommands.values()) {\n\t\t\t\tif (!comm.isAdminOnly() || isAdminFrom) {\n\t\t\t\t\tElement item = new Element(\"item\", new String[]{\"node\", \"name\", \"jid\"},\n\t\t\t\t\t\t\t\t\t\t\t   new String[]{comm.getCommandId(), comm.getDescription(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tjid.toString()});\n\t\t\t\t\tif (comm.getGroup() != null) {\n\t\t\t\t\t\titem.setAttribute(\"group\", comm.getGroup());\n\t\t\t\t\t}\n\n\t\t\t\t\tresult.add(item);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic VHostItem getVHostItem(String domain) {\n\t\treturn (vHostManager != null) ? vHostManager.getVHostItem(domain) : null;\n\t}\n\n\tpublic boolean isAdmin(JID jid) {\n\t\treturn admins.contains(jid.getBareJID());\n\t}\n\n\t@Override\n\tpublic boolean isInitializationComplete() {\n\t\treturn initializationCompleted;\n\t}\n\n\tpublic boolean isLocalDomain(String domain) {\n\t\treturn (vHostManager != null) ? vHostManager.isLocalDomain(domain) : false;\n\t}\n\n\tpublic boolean isLocalDomainOrComponent(String domain) {\n\t\treturn (vHostManager != null) ? vHostManager.isLocalDomainOrComponent(domain) : false;\n\t}\n\n\t@TigaseDeprecated(note = \"This method will be removed. All components should use a subdomain addressing\", removeIn = \"9.0.0\", since = \"8.5.0\")\n\t@Deprecated\n\tpublic boolean isSubdomain() {\n\t\treturn false;\n\t}\n\n\tpublic boolean isTrusted(JID jid) {\n\t\tif (trusted.contains(jid.getBareJID().toString())) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn isAdmin(jid);\n\t}\n\n\tpublic boolean isTrusted(String jid) {\n\t\treturn trusted.contains(jid);\n\t}\n\n\tpublic void setAdmins(Set<BareJID> admins) {\n\t\tthis.admins.addAll(admins);\n\t\tthis.admins.retainAll(admins);\n\t}\n\n\t@Override\n\t@Deprecated\n\tpublic void setProperties(Map<String, Object> props) throws ConfigurationException {\n\t}\n\n\tprotected Map<String, CopyOnWriteArraySet<CmdAcl>>getCommandACL() {\n\t\treturn Map.copyOf(commandsACL);\n\t}\n\n\tpublic void setCommandsACL(ConcurrentHashMap<String, CopyOnWriteArraySet<CmdAcl>> commandsACL) {\n\t\tthis.commandsACL = commandsACL;\n\t\tthis.nonAdminCommands = commandsACL.entrySet()\n\t\t\t\t.stream()\n\t\t\t\t.filter(e -> (!e.getValue().contains(CmdAcl.ADMIN)) || e.getValue().size() > 1)\n\t\t\t\t.findAny()\n\t\t\t\t.isPresent();\n\t}\n\n\tpublic void setScriptsBaseDir(String scriptsBaseDir) {\n\t\tthis.scriptsBaseDir = scriptsBaseDir;\n\t\tthis.scriptsCompDir = scriptsBaseDir + \"/\" + getName();\n\t\tif (scriptEngineManager != null) {\n\t\t\treloadScripts();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setVHostManager(VHostManagerIfc manager) {\n\t\tthis.vHostManager = manager;\n\t}\n\n\tpublic List<JID> getNodesConnected() {\n\t\treturn connectedNodes_ro;\n\t}\n\n\tpublic List<JID> getNodesConnectedWithLocal() {\n\t\treturn connectedNodesWithLocal_ro;\n\t}\n\n\tpublic boolean processScriptCommand(Packet pc, Queue<Packet> results) {\n\n\t\t// TODO: test if this is right\n\t\t// It is not, the packet should actually have packetFrom set at all times\n\t\t// to ensure the error can be sent back to the original sender.\n\t\t// if ((pc.getStanzaFrom() == null) || (pc.getPacketFrom() != null)) {\n\t\t//\n\t\t//// The packet has not gone through session manager yet\n\t\t// return false;\n\t\t// }\n\t\t// This test is more correct as it says whether the packet went through\n\t\t// session manager checking.\n\t\t// TODO: test if commands still work for users from different XMPP servers\n\t\t// with the right permission set.\n\t\tif (pc.getPermissions() == Permissions.NONE) {\n\t\t\treturn false;\n\t\t}\n\n\t\tIq iqc = (Iq) pc;\n\t\tCommand.Action action = Command.getAction(iqc);\n\n\t\tif (action == Command.Action.cancel) {\n\t\t\tPacket result = iqc.commandResult(Command.DataType.result);\n\n\t\t\tCommand.addTextField(result, \"Note\", \"Command canceled.\");\n\t\t\tresults.offer(result);\n\n\t\t\treturn true;\n\t\t}\n\n\t\tString strCommand = iqc.getStrCommand();\n\t\tCommandIfc com = scriptCommands.get(strCommand);\n\n\t\tif ((strCommand != null) && (com != null)) {\n\t\t\tboolean allowed = false;\n\n\t\t\ttry {\n\t\t\t\tallowed = canCallCommand(iqc.getStanzaFrom(), strCommand);\n\t\t\t\tif (allowed) {\n\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\tlog.log(Level.FINER, \"Processing admin command: {0}\", pc);\n\t\t\t\t\t}\n\n\t\t\t\t\tBindings binds = com.getBindings();\n\n\t\t\t\t\tif (binds == null) {\n\t\t\t\t\t\tbinds = scriptEngineManager.getBindings();\n\t\t\t\t\t}\n\n\t\t\t\t\t// Bindings binds = scriptEngineManager.getBindings();\n\t\t\t\t\tinitBindings(binds);\n\n\t\t\t\t\tFunction<String, Boolean> isAllowedForDomain = (domain) -> canCallCommand(iqc.getStanzaFrom(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  domain, strCommand);\n\t\t\t\t\tbinds.put(\"isAllowedForDomain\", isAllowedForDomain);\n\n\t\t\t\t\tcom.runCommand(iqc, binds, results);\n\t\t\t\t} else {\n\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\tlog.log(Level.FINER, \"Command rejected non-admin detected: {0}\", pc.getStanzaFrom());\n\t\t\t\t\t}\n\t\t\t\t\tresults.offer(\n\t\t\t\t\t\t\tAuthorization.FORBIDDEN.getResponseMessage(pc, \"Only Administrator can call the command.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   true));\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.WARNING, \"Unknown admin command processing exception: \" + pc, e);\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tnodeConnected(defHostname.getDomain());\n\t\tif (scriptEngineManager == null) {\n\t\t\tscriptEngineManager = createScriptEngineManager();\n\t\t}\n\n\t\tupdateServiceEntity();\n\n\t\tsetScriptsBaseDir(scriptsBaseDir);\n\t\treloadScripts();\n\t\tcmpInfo = new ComponentInfo(getName(), this.getClass());\n\n\t\tlog.log(Level.INFO, \"Loading component: \" + cmpInfo);\n\n\t\tinitializationCompleted();\n\t}\n\n\tpublic Optional<Element> getServiceEntityCaps(JID fromJid) {\n\t\treturn getServiceEntity().getCaps(isAdmin(fromJid) || hasNonAdminCommands(), fromJid.getDomain());\n\t}\n\n\tprotected ScriptEngineManager createScriptEngineManager() {\n\t\tif (XMPPServer.isOSGi()) {\n\t\t\treturn new OSGiScriptEngineManager();\n\t\t} else {\n\t\t\treturn new ScriptEngineManager();\n\t\t}\n\t}\n\n\tprotected void onNodeConnected(JID jid) {\n\n\t}\n\n\tprotected void onNodeDisconnected(JID jid) {\n\n\t}\n\n\tprotected Map<String, CommandIfc> getScriptCommands() {\n\t\treturn scriptCommands;\n\t}\n\n\tprotected ServiceEntity getServiceEntity() {\n\t\treturn serviceEntity;\n\t}\n\n\tprotected boolean isNonAdminCommands() {\n\t\treturn hasNonAdminCommands();\n\t}\n\n\tprotected void reloadScripts() {\n\t\tlog.log(Level.CONFIG, \"Reloading admin scripts for component: {0}.\", new Object[]{getName()});\n\t\tscriptCommands.clear();\n\t\tCommandIfc command = new AddScriptCommand();\n\n\t\tcommand.init(CommandIfc.ADD_SCRIPT_CMD, \"New command script\", \"Scripts\");\n\t\tscriptCommands.put(command.getCommandId(), command);\n\t\tcommand = new RemoveScriptCommand();\n\t\tcommand.init(CommandIfc.DEL_SCRIPT_CMD, \"Remove command script\", \"Scripts\");\n\t\tscriptCommands.put(command.getCommandId(), command);\n\n\t\tloadScripts();\n\t}\n\n\tprivate void loadScripts() {\n\t\tlog.log(Level.CONFIG, \"Loading admin scripts for component: {0}.\", new Object[]{getName()});\n\n\t\tFile file = null;\n\t\tAddScriptCommand addCommand = new AddScriptCommand();\n\t\tBindings binds = scriptEngineManager.getBindings();\n\t\tList<String> extensions = new ArrayList<>();\n\t\tfor (ScriptEngineFactory engineFactory : scriptEngineManager.getEngineFactories()) {\n\t\t\textensions.addAll(engineFactory.getExtensions());\n\t\t}\n\n\t\tinitBindings(binds);\n\n\t\tString[] dirs = new String[]{scriptsBaseDir, scriptsCompDir};\n\n\t\t// check class only from main directory\n\n\t\tfor (String scriptsPath : dirs) {\n\t\t\tlog.log(Level.CONFIG, \"{0}: Loading scripts from directory: {1}\", new Object[]{getName(), scriptsPath});\n\t\t\ttry {\n\t\t\t\tFile adminDir = new File(scriptsPath);\n\n\t\t\t\tif ((adminDir != null) && adminDir.exists()) {\n\t\t\t\t\tfor (File f : adminDir.listFiles(new ExtFilter(extensions))) {\n\n\t\t\t\t\t\t// Just regular files here....\n\t\t\t\t\t\tif (f.isFile() && !f.toString().endsWith(\"~\") && !f.isHidden()) {\n\t\t\t\t\t\t\tString cmdId = null;\n\t\t\t\t\t\t\tString cmdDescr = null;\n\t\t\t\t\t\t\tString cmdGroup = null;\n\t\t\t\t\t\t\tString comp = null;\n\t\t\t\t\t\t\tString compClass = null;\n\n\t\t\t\t\t\t\tfile = f;\n\n\t\t\t\t\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\t\t\t\t\tBufferedReader buffr = new BufferedReader(new FileReader(file));\n\t\t\t\t\t\t\tString line = null;\n\n\t\t\t\t\t\t\twhile ((line = buffr.readLine()) != null) {\n\t\t\t\t\t\t\t\tsb.append(line).append(\"\\n\");\n\n\t\t\t\t\t\t\t\tint idx = line.indexOf(CommandIfc.SCRIPT_DESCRIPTION);\n\n\t\t\t\t\t\t\t\tif (idx >= 0) {\n\t\t\t\t\t\t\t\t\tcmdDescr = line.substring(idx + CommandIfc.SCRIPT_DESCRIPTION.length()).trim();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tidx = line.indexOf(CommandIfc.SCRIPT_ID);\n\t\t\t\t\t\t\t\tif (idx >= 0) {\n\t\t\t\t\t\t\t\t\tcmdId = line.substring(idx + CommandIfc.SCRIPT_ID.length()).trim();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tidx = line.indexOf(CommandIfc.SCRIPT_COMPONENT);\n\t\t\t\t\t\t\t\tif (idx >= 0) {\n\t\t\t\t\t\t\t\t\tcomp = line.substring(idx + CommandIfc.SCRIPT_COMPONENT.length()).trim();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tidx = line.indexOf(CommandIfc.SCRIPT_CLASS);\n\t\t\t\t\t\t\t\tif (idx >= 0) {\n\t\t\t\t\t\t\t\t\tcompClass = line.substring(idx + CommandIfc.SCRIPT_CLASS.length()).trim();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tidx = line.indexOf(CommandIfc.SCRIPT_GROUP);\n\t\t\t\t\t\t\t\tif (idx >= 0) {\n\t\t\t\t\t\t\t\t\tcmdGroup = line.substring(idx + CommandIfc.SCRIPT_GROUP.length()).trim();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbuffr.close();\n\t\t\t\t\t\t\tif ((cmdId == null) || (cmdDescr == null)) {\n\t\t\t\t\t\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\t\t\t\t\t\"Admin script found but it has no command ID or command\" + \"description: \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\"{0}\", file);\n\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tboolean found = false;\n\n\t\t\t\t\t\t\tif (comp != null) {\n\t\t\t\t\t\t\t\t// Which components should load the script\n\t\t\t\t\t\t\t\tString[] comp_names = comp.split(\",\");\n\n\t\t\t\t\t\t\t\t// check component names\n\t\t\t\t\t\t\t\tfor (String cmp : comp_names) {\n\t\t\t\t\t\t\t\t\tcmp = cmp.trim();\n\t\t\t\t\t\t\t\t\tfound |= getName().equals(cmp);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// check component classes\n\t\t\t\t\t\t\tif (null != compClass) {\n\t\t\t\t\t\t\t\t// do we need this check? it blocks us from loading adhoc\n\t\t\t\t\t\t\t\t// commands from component named directory if it is marked\n\t\t\t\t\t\t\t\t// as for a specific component class - this should be allowed\n//\t\t\t\t\t\t\t\tif ( scriptsPath.endsWith( getName() ) ){\n//\t\t\t\t\t\t\t\t\t// ok, this is script for component of particular name, skip\n//\t\t\t\t\t\t\t\t\t// loading based on class\n//\t\t\t\t\t\t\t\t\tcontinue;\n//\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tString[] comp_classes = compClass.split(\",\");\n\t\t\t\t\t\t\t\tfor (String cmp : comp_classes) {\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t// we also check whether script is loaded for particular class or it's subclasses\n\t\t\t\t\t\t\t\t\t\tClass<?> loadClass = ModulesManagerImpl.getInstance().forName(cmp);\n\t\t\t\t\t\t\t\t\t\tfound |= loadClass.isAssignableFrom(this.getClass());\n\n\t\t\t\t\t\t\t\t\t} catch (NoClassDefFoundError ex) {\n\t\t\t\t\t\t\t\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\t\t\t\t\t\t\t\"Tried loading script with class defined as: {0} for class: {1}\",\n\t\t\t\t\t\t\t\t\t\t\t\tnew String[]{cmp, this.getClass().getCanonicalName()});\n\t\t\t\t\t\t\t\t\t} catch (ClassNotFoundException ex) {\n\t\t\t\t\t\t\t\t\t\t// just ignore\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!found) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\t\t\t\"{0}: skipping admin script {1}, id: {2}, descr: {3}, group: {4} for component: {5} or class: {6}\",\n\t\t\t\t\t\t\t\t\t\tnew Object[]{getName(), file, cmdId, cmdDescr, cmdGroup, comp, compClass});\n\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tint idx = file.toString().lastIndexOf('.');\n\t\t\t\t\t\t\tString ext = file.toString().substring(idx + 1);\n\n\t\t\t\t\t\t\tif (cmdGroup != null && cmdGroup.contains(\"${componentName}\")) {\n\t\t\t\t\t\t\t\tcmdGroup = cmdGroup.replace(\"${componentName}\", this.getDiscoDescription());\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\taddCommand.addAdminScript(cmdId, cmdDescr, cmdGroup, sb.toString(), null, ext, binds);\n\t\t\t\t\t\t\tlog.log(Level.CONFIG,\n\t\t\t\t\t\t\t\t\t\"{0}: Loaded admin command from file: {1}, id: {2}, ext: {3}, descr: {4}\",\n\t\t\t\t\t\t\t\t\tnew Object[]{getName(), file, cmdId, ext, cmdDescr});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (IOException | ScriptException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Can''t load the admin script file: \" + file, e);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void refreshTrustedJids() {\n\t\tsynchronized (connectedNodesWithLocal) {\n\t\t\ttrusted.clear();\n\t\t\tif (trustedProp != null) {\n\t\t\t\tfor (String trustedStr : trustedProp) {\n\t\t\t\t\tif (trustedStr.contains(\"{clusterNode}\")) {\n\t\t\t\t\t\tfor (JID nodeJid : connectedNodesWithLocal) {\n\t\t\t\t\t\t\tString node = nodeJid.getDomain();\n\t\t\t\t\t\t\tString jid = trustedStr.replace(\"{clusterNode}\", node);\n\t\t\t\t\t\t\ttrusted.add(jid);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttrusted.add(trustedStr);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"component {0} got trusted jids set as {1}\", new Object[]{getName(), trusted});\n\t\t}\n\t}\n\n\tprivate class ExtFilter\n\t\t\timplements FileFilter {\n\n\t\tList<String> extensions;\n\n\t\tpublic ExtFilter(List<String> extensions) {\n\t\t\tthis.extensions = extensions;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean accept(File file) {\n\n\t\t\tboolean matched = false;\n\t\t\tfor (String extension : extensions) {\n\t\t\t\tmatched |= file.isFile() && file.getName().toLowerCase().endsWith(extension);\n\t\t\t}\n\t\t\treturn matched;\n\t\t}\n\t}\n\n\tpublic static class ServerInfoVHostItemExtension\n\t\t\textends AbstractVHostItemExtension<ServerInfoVHostItemExtension> {\n\n\t\tpublic static final String ID = \"disco-server-info\";\n\n\t\tprivate List<String> abuseAddresses = Collections.EMPTY_LIST;\n\t\tprivate List<String> adminAddresses = Collections.EMPTY_LIST;\n\t\tprivate List<String> feedbackAddresses = Collections.EMPTY_LIST;\n\t\tprivate List<String> salesAddresses = Collections.EMPTY_LIST;\n\t\tprivate List<String> securityAddresses = Collections.EMPTY_LIST;\n\t\tprivate List<String> supportAddresses = Collections.EMPTY_LIST;\n\n\t\tpublic List<String> getAbuseAddresses() {\n\t\t\treturn abuseAddresses;\n\t\t}\n\n\t\tpublic List<String> getAdminAddresses() {\n\t\t\treturn adminAddresses;\n\t\t}\n\n\t\tpublic List<String> getFeedbackAddresses() {\n\t\t\treturn feedbackAddresses;\n\t\t}\n\n\t\tpublic List<String> getSalesAddresses() {\n\t\t\treturn salesAddresses;\n\t\t}\n\n\t\tpublic List<String> getSecurityAddresses() {\n\t\t\treturn securityAddresses;\n\t\t}\n\n\t\tpublic List<String> getSupportAddresses() {\n\t\t\treturn supportAddresses;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getId() {\n\t\t\treturn ID;\n\t\t}\n\n\t\t@Override\n\t\tpublic void initFromElement(Element item) {\n\t\t\tabuseAddresses = childrenToList(item, \"abuse\");\n\t\t\tadminAddresses = childrenToList(item, \"admin\");\n\t\t\tfeedbackAddresses = childrenToList(item, \"feedback\");\n\t\t\tsalesAddresses = childrenToList(item, \"sales\");\n\t\t\tsecurityAddresses = childrenToList(item, \"security\");\n\t\t\tsupportAddresses = childrenToList(item, \"support\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void initFromCommand(String prefix, Packet packet) throws IllegalArgumentException {\n\t\t\tabuseAddresses = fromCommandField(packet, prefix + \"-abuse\");\n\t\t\tadminAddresses = fromCommandField(packet, prefix + \"-admin\");\n\t\t\tfeedbackAddresses = fromCommandField(packet, prefix + \"-feedback\");\n\t\t\tsalesAddresses = fromCommandField(packet, prefix + \"-sales\");\n\t\t\tsecurityAddresses = fromCommandField(packet, prefix + \"-security\");\n\t\t\tsupportAddresses = fromCommandField(packet, prefix + \"-support\");\n\t\t}\n\n\t\t@Override\n\t\tpublic String toDebugString() {\n\t\t\treturn \"abuse: \" + adminAddresses + \", admin: \" + adminAddresses + \", feedback: \" + feedbackAddresses + \", sales: \" + salesAddresses + \", security: \" + securityAddresses + \", support: \" + supportAddresses;\n\t\t}\n\n\t\t@Override\n\t\tpublic Element toElement() {\n\t\t\tElement el = new Element(getId());\n\t\t\telementsFromList(\"abuse\", abuseAddresses).forEach(el::addChild);\n\t\t\telementsFromList(\"admin\", adminAddresses).forEach(el::addChild);\n\t\t\telementsFromList(\"feedback\", feedbackAddresses).forEach(el::addChild);\n\t\t\telementsFromList(\"sales\", salesAddresses).forEach(el::addChild);\n\t\t\telementsFromList(\"security\", securityAddresses).forEach(el::addChild);\n\t\t\telementsFromList(\"support\", supportAddresses).forEach(el::addChild);\n\t\t\treturn el.getChildren() != null ? el : null;\n\t\t}\n\n\t\t@Override\n\t\tpublic void addCommandFields(String prefix, Packet packet, boolean forDefault) {\n\t\t\tElement command = packet.getElemChild(Command.COMMAND_EL, Command.XMLNS);\n\t\t\t// Usage of `addFieldMultiValue()` was intentional and should result in `text-multi` fields.\n\t\t\t// Those fields are for editing list of JIDs in VHost configuration and usage\n\t\t\t// of `list-multi` will not work as it does not allow editing those JIDs - allows only selection.\n\t\t\tDataForm.addFieldMultiValue(command, prefix + \"-abuse\", abuseAddresses, \"Abuse reporting addresses\");\n\t\t\tDataForm.addFieldMultiValue(command, prefix + \"-admin\", adminAddresses, \"Admin addresses\");\n\t\t\tDataForm.addFieldMultiValue(command, prefix + \"-feedback\", feedbackAddresses, \"Feedback addresses\");\n\t\t\tDataForm.addFieldMultiValue(command, prefix + \"-sales\", salesAddresses, \"Sales addresses\");\n\t\t\tDataForm.addFieldMultiValue(command, prefix + \"-security\", securityAddresses, \"Security addresses\");\n\t\t\tDataForm.addFieldMultiValue(command, prefix + \"-support\", supportAddresses, \"Support addresses\");\n\t\t}\n\n\t\t@Override\n\t\tpublic ServerInfoVHostItemExtension mergeWithDefaults(ServerInfoVHostItemExtension defaults) {\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate static List<String> fromCommandField(Packet packet, String field) {\n\t\t\tList<String> values = ((Stream<String>) Optional.ofNullable(Command.getFieldValues(packet, field))\n\t\t\t\t\t.map(Arrays::asList)\n\t\t\t\t\t.orElse(Collections.EMPTY_LIST)\n\t\t\t\t\t.stream()).map(String::trim).filter(s -> !s.isEmpty()).collect(Collectors.toList());\n\t\t\treturn values.isEmpty() ? Collections.EMPTY_LIST : values;\n\t\t}\n\n\t\tprivate static List<String> childrenToList(Element el, String name) {\n\t\t\treturn Optional.ofNullable(el.mapChildren(child -> child.getName() == name, Element::getCData))\n\t\t\t\t\t.orElse(Collections.EMPTY_LIST);\n\t\t}\n\n\t\tprivate static Stream<Element> elementsFromList(String name, List<String> values) {\n\t\t\treturn values.stream().map(v -> new Element(name, v));\n\t\t}\n\n\t\t@Bean(name = ID, parent = VHostItemExtensionManager.class, active = true)\n\t\tpublic static class ServerInfoVHostItemExtensionProvider\n\t\t\t\timplements VHostItemExtensionProvider<ServerInfoVHostItemExtension> {\n\n\t\t\t@Override\n\t\t\tpublic String getId() {\n\t\t\t\treturn ID;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Class<ServerInfoVHostItemExtension> getExtensionClazz() {\n\t\t\t\treturn ServerInfoVHostItemExtension.class;\n\t\t\t}\n\t\t}\n\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/Bootstrap.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport tigase.component.DSLBeanConfigurator;\nimport tigase.component.DSLBeanConfiguratorWithBackwardCompatibility;\nimport tigase.conf.ConfigHolder;\nimport tigase.conf.ConfigReader;\nimport tigase.conf.ConfiguratorAbstract;\nimport tigase.conf.LoggingBean;\nimport tigase.db.beans.DataSourceBean;\nimport tigase.db.beans.MDPoolBean;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.kernel.DefaultTypesConverter;\nimport tigase.kernel.KernelException;\nimport tigase.kernel.beans.Autostart;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.beans.selector.ServerBeanSelector;\nimport tigase.kernel.core.BeanConfig;\nimport tigase.kernel.core.DependencyGrapher;\nimport tigase.kernel.core.Kernel;\nimport tigase.net.ConnectionOpenThread;\nimport tigase.osgi.ModulesManagerImpl;\nimport tigase.server.monitor.MonitorRuntime;\nimport tigase.sys.ShutdownHook;\nimport tigase.util.dns.DNSResolverDefault;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.util.dns.DNSResolverIfc;\nimport tigase.util.log.LogFormatter;\nimport tigase.util.reflection.ClassUtilBean;\nimport tigase.xmpp.impl.roster.RosterFactory;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.conf.Configurable.GEN_DEBUG;\nimport static tigase.conf.Configurable.GEN_DEBUG_PACKAGES;\nimport static tigase.conf.ConfiguratorAbstract.LOGGING_KEY;\n\n/**\n * Bootstrap class is responsible for initialization of Kernel to start Tigase XMPP Server.\n * <br>\n * Created by andrzej on 05.03.2016.\n */\npublic class Bootstrap {\n\n\tprivate static final Logger log = Logger.getLogger(Bootstrap.class.getCanonicalName());\n\n\tprivate final Kernel kernel;\n\tprivate final ShutdownHook shutdownHook = new BootstrapShutdownHook();\n\tprivate ConfigHolder config = new ConfigHolder();\n\t// Common logging setup\n\tprivate Map<String, String> loggingSetup = new LinkedHashMap<String, String>(10);\n\n\tpublic Bootstrap() {\n\t\tkernel = new Kernel(\"root\");\n\t}\n\n\tprivate void configureLogManager() {\n\t\tMap<String, Object> cfg = prepareLogManagerConfiguration(config.getProperties());\n\t\tsetupLogManager(cfg);\n\t}\n\n\tpublic <T> T getInstance(String beanName) {\n\t\treturn kernel.getInstance(beanName);\n\t}\n\n\tpublic <T> T getInstance(Class<T> clazz) {\n\t\treturn kernel.getInstance(clazz);\n\t}\n\n\tprotected Kernel getKernel() {\n\t\treturn kernel;\n\t}\n\n\tpublic void init(String[] args) throws ConfigReader.ConfigException, IOException {\n\t\tconfig.loadConfiguration(args);\n\t\tconfigureLogManager();\n\t}\n\n\tprivate void initializeAutostartBeans(Kernel kernel) {\n\t\tlog.config(\"Starting 'autostart' beans in kernel \" + kernel.getName());\n\t\tfor (BeanConfig bc : kernel.getDependencyManager().getBeanConfigs()) {\n\t\t\tif (Kernel.class.isAssignableFrom(bc.getClazz())) {\n\t\t\t\tKernel sk = kernel.getInstance(bc.getBeanName());\n\t\t\t\tif (sk != kernel) {\n\t\t\t\t\tinitializeAutostartBeans(sk);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tAutostart autostart = bc.getClazz().getAnnotation(Autostart.class);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Checking for autostart \" + bc.getKernel().getName() + \".\" + bc.getBeanName() + \":: state=\" + bc.getState() +\n\t\t\t\t\t\t\t\t   \"; \" + \"autostart=\" + (autostart != null));\n\t\t\t}\n\n\t\t\tif ((bc.getState() == BeanConfig.State.registered || bc.getState() == BeanConfig.State.initialized) &&\n\t\t\t\t\tautostart != null) {\n\t\t\t\tlog.config(\"Autostarting bean \" + bc);\n\t\t\t\tkernel.getInstance(bc.getBeanName());\n\t\t\t}\n\t\t}\n\t}\n\n\t// moved to AbstractBeanConfigurator\n//\tpublic void registerBeans() {\n//\t}\n//\n//\tprotected void registerBeans(Set<Class<?>> classes) {\n//\t\tfor (Class<?> cls : classes) {\n//\t\t\tBean annotation = shouldRegister(cls, this.getClass());\n//\t\t\tif (annotation != null) {\n//\t\t\t\tkernel.registerBean(cls);\n//\t\t\t}\n//\t\t}\n//\t}\n//\n//\tprotected Bean shouldRegister(Class<?> cls, Class<?> requiredClass) {\n//\t\tBean annotation = cls.getAnnotation(Bean.class);\n//\t\tif (annotation == null)\n//\t\t\treturn null;\n//\n//\t\tClass parent = annotation.parent();\n//\t\tif (parent == Object.class)\n//\t\t\treturn null;\n//\n//\t\treturn parent.isAssignableFrom(requiredClass) ? annotation : null;\n//\t}\n\n\tprivate void initializeDnsResolver() {\n\t\tMap<String, Object> resolverConfig = (Map<String, Object>) config.getProperties().get(\"dns-resolver\");\n\t\tif (resolverConfig != null) {\n\t\t\tString resolverClass = (String) resolverConfig.get(DNSResolverFactory.TIGASE_RESOLVER_CLASS);\n\t\t\tif (resolverClass != null) {\n\t\t\t\tDNSResolverFactory.setDnsResolverClassName(resolverClass);\n\t\t\t}\n\t\t\tDNSResolverIfc resolver = DNSResolverFactory.getInstance();\n\t\t\tif (resolver instanceof DNSResolverDefault) {\n\t\t\t\tObject config = resolverConfig.get(DNSResolverIfc.TIGASE_PRIMARY_ADDRESS);\n\t\t\t\tif (config instanceof ConfigReader.AbstractEnvironmentPropertyVariable) {\n\t\t\t\t\tconfig = ((ConfigReader.AbstractEnvironmentPropertyVariable) config).calculateValue();\n\t\t\t\t}\n\t\t\t\tString host = (String) config;\n\t\t\t\tif (host != null && !host.isEmpty()) {\n\t\t\t\t\t((DNSResolverDefault) resolver).setPrimaryHost(host);\n\t\t\t\t}\n\t\t\t\tconfig = resolverConfig.get(DNSResolverIfc.TIGASE_SECONDARY_ADDRESS);\n\t\t\t\tif (config instanceof ConfigReader.AbstractEnvironmentPropertyVariable) {\n\t\t\t\t\tconfig = ((ConfigReader.AbstractEnvironmentPropertyVariable) config).calculateValue();\n\t\t\t\t}\n\t\t\t\thost = (String) config;\n\t\t\t\tif (host != null && !host.isEmpty()) {\n\t\t\t\t\t((DNSResolverDefault) resolver).setSecondaryHost(host);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate Map<String, Object> prepareLogManagerConfiguration(Map<String, Object> params) {\n\t\tMap<String, Object> defaults = new HashMap<>();\n\t\tString levelStr = \".level\";\n\n//\t\tif ((Boolean) params.get(\"test\")) {\n//\t\t\tdefaults.put(LOGGING_KEY + levelStr, \"WARNING\");\n//\t\t} else {\n\t\tdefaults.put(LOGGING_KEY + levelStr, \"CONFIG\");\n//\t\t}\n\t\tdefaults.put(LOGGING_KEY + \"handlers\", \"java.util.logging.ConsoleHandler java.util.logging.FileHandler\");\n\t\tdefaults.put(LOGGING_KEY + \"java.util.logging.ConsoleHandler.formatter\", LogFormatter.class.getName());\n\t\tdefaults.put(LOGGING_KEY + \"java.util.logging.ConsoleHandler.level\", \"INFO\");\n\t\tdefaults.put(LOGGING_KEY + \"java.util.logging.FileHandler.append\", \"true\");\n\t\tdefaults.put(LOGGING_KEY + \"java.util.logging.FileHandler.count\", \"5\");\n\t\tdefaults.put(LOGGING_KEY + \"java.util.logging.FileHandler.formatter\", LogFormatter.class.getName());\n\t\tdefaults.put(LOGGING_KEY + \"java.util.logging.FileHandler.limit\", \"10000000\");\n\t\tdefaults.put(LOGGING_KEY + \"java.util.logging.FileHandler.pattern\", \"logs/tigase.log\");\n\t\tdefaults.put(LOGGING_KEY + \"tigase.useParentHandlers\", \"true\");\n\t\tdefaults.put(LOGGING_KEY + \"java.util.logging.FileHandler.level\", \"ALL\");\n\t\tdefaults.put(LOGGING_KEY + \"tigase.kernel.core.Kernel.level\", \"CONFIG\");\n\t\tif (params.get(GEN_DEBUG) != null) {\n\t\t\tString[] packs = ((String) params.get(GEN_DEBUG)).split(\",\");\n\n\t\t\tfor (String pack : packs) {\n\t\t\t\tdefaults.put(LOGGING_KEY + \"tigase.\" + pack + \".level\", \"ALL\");\n\t\t\t}    // end of for (String pack: packs)\n\t\t}\n\t\tif (params.get(GEN_DEBUG_PACKAGES) != null) {\n\t\t\tString[] packs = ((String) params.get(GEN_DEBUG_PACKAGES)).split(\",\");\n\n\t\t\tfor (String pack : packs) {\n\t\t\t\tdefaults.put(LOGGING_KEY + pack + \".level\", \"ALL\");\n\t\t\t}    // end of for (String pack: packs)\n\t\t}\n\n\t\treturn defaults;\n\t}\n\n\tpublic void setProperties(Map<String, Object> props) {\n\t\tthis.config.setProperties(props);\n\t}\n\n\tprivate void setupLogManager(Map<String, Object> properties) {\n\t\tSet<Map.Entry<String, Object>> entries = properties.entrySet();\n\t\tStringBuilder buff = new StringBuilder(200);\n\n\t\tfor (Map.Entry<String, Object> entry : entries) {\n\t\t\tif (entry.getKey().startsWith(LOGGING_KEY)) {\n\t\t\t\tString key = entry.getKey().substring(LOGGING_KEY.length());\n\t\t\t\tloggingSetup.put(key, entry.getValue().toString());\n\t\t\t}\n\t\t}\n\n\t\tfor (String key : loggingSetup.keySet()) {\n\t\t\tString entry = loggingSetup.get(key);\n\t\t\tbuff.append(key).append(\"=\").append(entry).append(\"\\n\");\n\t\t\tif (key.equals(\"java.util.logging.FileHandler.pattern\")) {\n\t\t\t\tFile log_path = new File(entry).getParentFile();\n\t\t\t\tif (!log_path.exists()) {\n\t\t\t\t\tlog_path.mkdirs();\n\t\t\t\t}\n\t\t\t}    // end of if (key.equals())\n\t\t}      // end of if (entry.getKey().startsWith(LOGGING_KEY))\n\n\t\t// System.out.println(\"Setting logging: \\n\" + buff.toString());\n\t\tConfiguratorAbstract.loadLogManagerConfig(buff.toString());\n\t\tlog.config(\"DONE\");\n\t}\n\n\tpublic void start() {\n\t\tinitializeDnsResolver();\n\t\tObject clusterMode = config.getProperties()\n\t\t\t\t.getOrDefault(\"cluster-mode\", config.getProperties().getOrDefault(\"--cluster-mode\", Boolean.FALSE));\n\t\tif (clusterMode instanceof ConfigReader.Variable) {\n\t\t\tclusterMode = ((ConfigReader.Variable) clusterMode).calculateValue();\n\t\t\tif (clusterMode == null) {\n\t\t\t\tclusterMode = Boolean.FALSE;\n\t\t\t}\n\t\t}\n\t\tif (clusterMode instanceof String) {\n\t\t\tclusterMode = Boolean.parseBoolean((String) clusterMode);\n\t\t}\n\t\tif ((Boolean) clusterMode) {\n\t\t\tSystem.setProperty(\"tigase.cache\", \"false\");\n\t\t\tlog.log(Level.INFO, \"Tigase cache turned off\");\n\t\t}\n\t\tconfig.getProperties().put(\"cluster-mode\", clusterMode);\n\n\t\tOptional.ofNullable((String) config.getProperties().get(\"stringprep-processor\"))\n\t\t\t\t.ifPresent(val -> BareJID.useStringprepProcessor(val));\n\n\t\tfor (Map.Entry<String, Object> e : config.getProperties().entrySet()) {\n\t\t\tif (e.getKey().startsWith(\"--\")) {\n\t\t\t\tString key = e.getKey().substring(2);\n\t\t\t\tObject value = e.getValue();\n\t\t\t\tif (value instanceof ConfigReader.Variable) {\n\t\t\t\t\tvalue = ((ConfigReader.Variable) value).calculateValue();\n\t\t\t\t}\n\t\t\t\tSystem.setProperty(key, value.toString());\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tClassUtilBean classUtilBean = null;\n\t\t\tif (XMPPServer.isOSGi()) {\n\t\t\t\tclassUtilBean = (ClassUtilBean) Class.forName(\"tigase.osgi.util.ClassUtilBean\").newInstance();\n\t\t\t} else {\n\t\t\t\tclassUtilBean = (ClassUtilBean) Class.forName(\"tigase.util.reflection.ClassUtilBean\").newInstance();\n\t\t\t}\n\n\t\t\tclassUtilBean.initialize(ClassUtilBean.getPackagesToSkip(null));\n\t\t\tkernel.registerBean(\"classUtilBean\").asInstance(classUtilBean).exportable().exec();\n\t\t} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\n\t\t// register default types converter and properties bean configurator\n\t\tkernel.registerBean(DefaultTypesConverter.class).exportable().exec();\n\t\tkernel.registerBean(DSLBeanConfiguratorWithBackwardCompatibility.class).exportable().exec();\n\t\tkernel.registerBean(\"eventBus\").asInstance(EventBusFactory.getInstance()).exportable().exec();\n\n\t\tDSLBeanConfigurator configurator = kernel.getInstance(DSLBeanConfigurator.class);\n\t\tconfigurator.setConfigHolder(config);\n\t\tModulesManagerImpl.getInstance().setBeanConfigurator(configurator);\n\n\t\tkernel.registerBean(\"logging\").asClass(LoggingBean.class).setActive(true).setPinned(true).exec();\n\t\tkernel.getInstance(\"logging\");\n\n\t\tkernel.registerBean(\"beanSelector\").asInstance(new ServerBeanSelector()).exportable().exec();\n\n\t\tkernel.registerBean(RosterFactory.Bean.class).setPinned(true).exec();\n\t\tkernel.getInstance(RosterFactory.Bean.class);\n\n\t\t// if null then we register global subbeans\n\t\tconfigurator.registerBeans(null, null, config.getProperties());\n\n\t\tDependencyGrapher dg = new DependencyGrapher();\n\t\tdg.setKernel(kernel);\n\n\t\tlog.log(Level.CONFIG, dg.getDependencyGraph());\n\n\t\t// this is called to make sure that data sources are properly initialized\n\t\tif (ServerBeanSelector.getConfigType(kernel) != ConfigTypeEnum.SetupMode) {\n\t\t\tDataSourceBean dataSource = kernel.getInstance(DataSourceBean.class);\n\t\t\tif (dataSource == null || dataSource.getDataSourceNames().isEmpty()) {\n\t\t\t\tthrow new KernelException(\"Failed to initialize data sources!\");\n\t\t\t}\n\t\t}\n\t\tMessageRouter mr = kernel.getInstance(\"message-router\");\n\t\tlog.log(Level.INFO, \"Starting MessageRouter\");\n\t\tmr.start();\n\n//\t\tStringBuilder sb = new StringBuilder(\"\\n======\");\n//\t\tsb.append(\"\\n\");\n//\t\tfinal Collection<BeanConfig> beanConfigs = kernel.getDependencyManager().getBeanConfigs();\n//\t\tfor (BeanConfig beanConfig : beanConfigs) {\n//\t\t\tsb.append(\"bean config: \").append(beanConfig).append(\"\\n\");\n//\t\t\tfinal Set<BeanConfig> registeredBeans = beanConfig.getRegisteredBeans();\n//\t\t\tfor (BeanConfig registeredBean : registeredBeans) {\n//\t\t\t\tsb.append(\"  -> registered bean: \").append(registeredBean).append(\"\\n\");\n//\t\t\t\tfinal Set<BeanConfig> registeredBeans1 = registeredBean.getRegisteredBeans();\n//\t\t\t\tfor (BeanConfig beanConfig1 : registeredBeans1) {\n//\t\t\t\t\tsb.append(\"    -> registered bean1: \").append(beanConfig1).append(\"\\n\");\n//\t\t\t\t}\n//\t\t\t}\n//\t\t}\n//\t\tsb.append(\"======\\n\");\n//\t\tSystem.out.println(sb);\n\n\t\ttry {\n\t\t\tlog.fine(\"Dump configuration\");\n\t\t\tFile f = new File(\"etc/config-dump.properties\");\n\t\t\tif (f.exists()) {\n\t\t\t\tf.delete();\n\t\t\t}\n\t\t\tconfigurator.dumpConfiguration(f);\n\t\t} catch (IOException ex) {\n\t\t\tlog.log(Level.FINE, \"failed to dump configuration to file etc/config-dump.properties\");\n\t\t}\n\n\t\tMonitorRuntime.getMonitorRuntime().addShutdownHook(shutdownHook);\n\n\t\tinitializeAutostartBeans(kernel);\n\t}\n\n\tpublic void stop() {\n\t\tkernel.shutdown((bc1, bc2) -> {\n\t\t\tif (MDPoolBean.class.isAssignableFrom(bc1.getClazz())) {\n\t\t\t\treturn Integer.MIN_VALUE;\n\t\t\t}\n\t\t\tif (MDPoolBean.class.isAssignableFrom(bc2.getClazz())) {\n\t\t\t\treturn Integer.MIN_VALUE;\n\t\t\t}\n\t\t\treturn 0;\n\t\t});\n\t}\n\n\tpublic class BootstrapShutdownHook\n\t\t\timplements ShutdownHook {\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn \"bootstrap-shutdown\";\n\t\t}\n\n\t\t@Override\n\t\tpublic String shutdown() {\n\t\t\tConnectionOpenThread.getInstance().stop();\n\n\t\t\tlong start = System.currentTimeMillis();\n\t\t\ttry {\n\t\t\t\tBootstrap.this.stop();\n\t\t\t} catch (Throwable ex) {\n\t\t\t\tSystem.out.println(\"Warning: failed to shutdown Tigase Kernel with error: \" + ex.getMessage());\n\t\t\t\tex.printStackTrace();\n\t\t\t\treturn ex.getMessage();\n\n\t\t\t}\n\n\t\t\tlong end = System.currentTimeMillis();\n\n\t\t\treturn \"Tigase Kernel stopped in \" + (end - start) + \"ms\";\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/CmdAcl.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.Objects;\n\npublic class CmdAcl {\n\n\tpublic static final CmdAcl ADMIN = new CmdAcl(Type.ADMIN.name());\n\n\tpublic enum Type {\n\t\t/**\n\t\t * Everybody can execute the command, even users from a different servers.\n\t\t */\n\t\tALL,\n\t\t/**\n\t\t * Only local server administrators can execute command.\n\t\t */\n\t\tADMIN,\n\t\t/**\n\t\t * Only users who have accounts on this local server can execute the command.\n\t\t */\n\t\tLOCAL,\n\t\t/**\n\t\t * Only users who have an account within the given domain can execute the command.\n\t\t */\n\t\tDOMAIN,\n\t\t/**\n\t\t * Only user who is an admin of the given domain can execute the command.\n\t\t */\n\t\tDOMAIN_ADMIN,\n\t\t/**\n\t\t * Only user who is an owner of the given domain can execute the command.\n\t\t */\n\t\tDOMAIN_OWNER,\n\t\t/**\n\t\t * Comma separated list of JIDs of users who can execute the command.\n\t\t */\n\t\tJID,\n\t\t/**\n\t\t * No one is allowed to execute the command, even server administrators!\n\t\t */\n\t\tNONE;\n\t}\n\tprivate final BareJID jid;\n\tprivate final Type type;\n\n\tpublic CmdAcl(String value) {\n\t\tswitch (value) {\n\t\t\tcase \"ALL\":\n\t\t\t\ttype = Type.ALL;\n\t\t\t\tjid = null;\n\t\t\t\tbreak;\n\t\t\tcase \"ADMIN\":\n\t\t\t\ttype = Type.ADMIN;\n\t\t\t\tjid = null;\n\t\t\t\tbreak;\n\t\t\tcase \"LOCAL\":\n\t\t\t\ttype = Type.LOCAL;\n\t\t\t\tjid = null;\n\t\t\t\tbreak;\n\t\t\tcase \"DOMAIN_OWNER\":\n\t\t\t\ttype = Type.DOMAIN_OWNER;\n\t\t\t\tjid = null;\n\t\t\t\tbreak;\n\t\t\tcase \"DOMAIN_ADMIN\":\n\t\t\t\ttype = Type.DOMAIN_ADMIN;\n\t\t\t\tjid = null;\n\t\t\t\tbreak;\n\t\t\tcase \"NONE\":\n\t\t\t\ttype = Type.NONE;\n\t\t\t\tjid = null;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tjid = BareJID.bareJIDInstanceNS(value);\n\t\t\t\ttype = jid == null ? Type.NONE : (jid.getLocalpart() == null ? Type.DOMAIN : Type.JID);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tpublic Type getType() {\n\t\treturn type;\n\t}\n\n\tpublic boolean isJIDAllowed(BareJID jid) {\n\t\treturn this.jid.equals(jid);\n\t}\n\n\tpublic boolean isDomainAllowed(String domain) {\n\t\treturn domain.equals(domain);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (obj instanceof CmdAcl) {\n\t\t\tCmdAcl o = (CmdAcl) obj;\n\t\t\treturn type == o.type && (jid == null || jid.equals(jid));\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(type, jid);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tswitch (type) {\n\t\t\tcase ALL:\n\t\t\tcase ADMIN:\n\t\t\tcase DOMAIN_OWNER:\n\t\t\tcase DOMAIN_ADMIN:\n\t\t\tcase LOCAL:\n\t\t\t\treturn type.name();\n\t\t\tdefault:\n\t\t\t\treturn jid.toString();\n\t\t}\n\t}\n\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/Command.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport tigase.xml.Element;\nimport tigase.xml.XMLUtils;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Helper enum to make it easier to operate on packets with ad-hoc commands. It allows to create a packet with command,\n * add and retrieve command data field values, set actions and so on.\n * <br>\n * It contains predefined set of commands used internally by the Tigase server and also 'OTHER' command which refers all\n * other not predefined commands.\n * <br>\n * Most of the implementation details, constants and parameters is based on the <a\n * href=\"http://xmpp.org/extensions/xep-0050.html\">XEP-0050</a> for ad-hoc commands protocol. Please refer to the XEP\n * for more details.\n * <br>\n * Created: Thu Feb 9 20:52:02 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic enum Command {\n\n\t/**\n\t * Command sent from a connection manager to the session manager when a new stream from the client has been opened.\n\t */\n\tSTREAM_OPENED(Priority.SYSTEM),\n\n\t/**\n\t * Command sent from connection manager to the session manager after TLS handshake if client sent certificate.\n\t */\n\tTLS_HANDSHAKE_COMPLETE(Priority.SYSTEM),\n\n\t/**\n\t * Command sent from session manager to the connection manager after successful user login.\n\t */\n\tUSER_LOGIN(Priority.SYSTEM),\n\n\t/**\n\t * Command sent from a connection manager to the session manager when a connection or stream has been closed.\n\t */\n\tSTREAM_CLOSED(Priority.SYSTEM),\n\tSTREAM_CLOSED_UPDATE(Priority.SYSTEM),\n\n\t/**\n\t * Command sent from a connection manager to the session manager after last packet from closed connection stream has\n\t * been sent.\n\t */\n\tSTREAM_FINISHED(Priority.NORMAL),\n\n\t/**\n\t * Sends a command from SM to the connection holder to confirm whether the connection is still active. Expects\n\t * result for ok, error or timeout if the connection is no longer active.\n\t */\n\tCHECK_USER_CONNECTION(Priority.SYSTEM),\n\n\t/**\n\t * Command sent from the session manager to a connection manager to start TLS handshaking over the client\n\t * connection.\n\t */\n\tSTARTTLS(Priority.NORMAL),\n\n\t/**\n\t * Command sent from the session manager to a connection manager to start zlib compression on the connection\n\t * stream.\n\t */\n\tSTARTZLIB(Priority.NORMAL),\n\n\t/**\n\t * Command sent between a connection manager and the session manager to retrieve stream features.\n\t */\n\tGETFEATURES(Priority.HIGH),\n\n\t/**\n\t * This is depreciated command sent between components in the Tigase server for service discovery handling.\n\t */\n\tGETDISCO(Priority.HIGH),\n\n\t/**\n\t * Command sent from the session manager to a client manager to close the client connection.\n\t */\n\tCLOSE(Priority.SYSTEM),\n\n\t/**\n\t * Command used by the StatisticsCollector to provide server statistics through ad-hoc command.\n\t */\n\tGETSTATS(Priority.HIGH),\n\n\t/**\n\t * Command sent to the session manager from an external entity to activate a user session with the connection\n\t * end-point at the given address.\n\t */\n\tUSER_STATUS(Priority.NORMAL),\n\n\t/**\n\t * Command used to set a broadcast message to all online users.\n\t */\n\tBROADCAST_TO_ONLINE(Priority.NORMAL),\n\n\t/**\n\t * Command used to set a broadcast message to all registered local users.\n\t */\n\tBROADCAST_TO_ALL(Priority.NORMAL),\n\n\t/**\n\t * Command used to redirect packets from a connection manager to other than default session manager. (Mostly used in\n\t * the clustering.)\n\t */\n\tREDIRECT(Priority.SYSTEM),\n\n\t/**\n\t * Command sent to the VHostManager to reload virtual hosts from the database.\n\t */\n\tVHOSTS_RELOAD(Priority.NORMAL),\n\n\t/**\n\t * Command sent to the VHostManager to add or update existing virtual host.\n\t */\n\tVHOSTS_UPDATE(Priority.NORMAL),\n\n\t/**\n\t * Command sent to the VHostManager to remove existing virtual host.\n\t */\n\tVHOSTS_REMOVE(Priority.NORMAL),\n\n\t/**\n\t * Command sent to SessionManager to change connectionId of existing session.\n\t */\n\tSTREAM_MOVED(Priority.NORMAL),\n\n\t/**\n\t * Identifies all other, not predefined commands.\n\t */\n\tOTHER(Priority.NORMAL);\n\n\tpublic static final String COMMAND_EL = \"command\";\n\n\tpublic static final String XMLNS = \"http://jabber.org/protocol/commands\";\n\n\tprivate static final Logger log = Logger.getLogger(\"tigase.server.Command\");\n\n\t/**\n\t * Ad-hoc command actions ad defined in the XEP-0050.\n\t */\n\tpublic enum Action {\n\n\t\t/**\n\t\t * The command should be executed or continue to be executed. This is the default value.\n\t\t */\n\t\texecute,\n\n\t\t/**\n\t\t * The command should be canceled.\n\t\t */\n\t\tcancel,\n\n\t\t/**\n\t\t * The command should be digress to the previous stage of execution.\n\t\t */\n\t\tprev,\n\n\t\t/**\n\t\t * The command should progress to the next stage of execution.\n\t\t */\n\t\tnext,\n\n\t\t/**\n\t\t * The command should be completed (if possible).\n\t\t */\n\t\tcomplete,\n\n\t\t/**\n\t\t * Other, not recognized command action.\n\t\t */\n\t\tother;\n\t}\n\n\t/**\n\t * Data form-types as defined in the XEP-0050.\n\t */\n\tpublic enum DataType {\n\n\t\t/**\n\t\t * This is a form with ad-hoc command result data.\n\t\t */\n\t\tresult,\n\n\t\t/**\n\t\t * This is a form querying for more data from the user.\n\t\t */\n\t\tform,\n\n\t\t/**\n\t\t * Form filled with data sent as a response to 'form' request.\n\t\t */\n\t\tsubmit;\n\t}\n\n\t/**\n\t * Ad-hoc command statuses as defined in the XEP-0050.\n\t */\n\tpublic enum Status {\n\n\t\t/**\n\t\t * The command is being executed.\n\t\t */\n\t\texecuting,\n\n\t\t/**\n\t\t * The command has completed. The command session has ended.\n\t\t */\n\t\tcompleted,\n\n\t\t/**\n\t\t * The command has been canceled. The command session has ended.\n\t\t */\n\t\tcanceled,\n\n\t\t/**\n\t\t * Other, not recognized command status.\n\t\t */\n\t\tother;\n\t}\n\tprivate Priority priority = Priority.NORMAL;\n\n\tpublic static void addAction(final Packet packet, final Action action) {\n\t\taddActionEl(packet.getElement(), action);\n\t}\n\n\tprivate static void addActionElToCommand(final Element command, Action action) {\n\t\tElement actions = command.getChild(\"actions\");\n\n\t\tif (actions == null) {\n\t\t\tactions = new Element(\"actions\", new String[]{Action.execute.toString()}, new String[]{action.toString()});\n\t\t\tcommand.addChild(actions);\n\t\t}\n\t\tactions.addChild(new Element(action.toString()));\n\t}\n\n\tprivate static void addActionEl(Element iq, Action action) {\n\t\tElement command = iq.getChild(COMMAND_EL);\n\t\taddActionElToCommand(command, action);\n\t}\n\n\tpublic static void addCheckBoxField(Packet packet, String f_name, boolean f_value) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL);\n\n\t\tDataForm.addCheckBoxField(command, f_name, f_value);\n\t}\n\n\tprivate static Element addDataForm(Element command, DataType data_type) {\n\t\tElement x = new Element(\"x\", new String[]{\"xmlns\", \"type\"}, new String[]{\"jabber:x:data\", data_type.name()});\n\n\t\tcommand.addChild(x);\n\n\t\treturn x;\n\t}\n\n\t/**\n\t * A simple method for adding a multi-line (text-multi) data field to the command data form. Only field name\n\t * (variable name) and field default value can be set.\n\t *\n\t * @param packet is a <code>Packet</code> instance of the ad-hoc command request to be modified.\n\t * @param f_name is a <code>String</code> instance with the field name. In ad-hoc command terms this is a variable\n\t * name. This field name (variable name) will be also displayed as the field label.\n\t * @param f_value is a list with lines of text to be displayed as a multi-line field content.\n\t */\n\tpublic static void addFieldMultiValue(final Packet packet, final String f_name, final List<String> f_value) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL);\n\t\tDataForm.addFieldMultiValue(command, f_name, f_value);\n\n\t}\n\n\tpublic static void addFieldMultiValue(final Packet packet, final String f_name, final List<String> f_value,\n\t\t\t\t\t\t\t\t\t\t  String label) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL);\n\t\tDataForm.addFieldMultiValue(command, f_name, f_value, label);\n\n\t}\n\n\tpublic static void addFieldMultiValue(final Packet packet, final String f_name, final Throwable ex) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL);\n\t\tDataForm.addFieldMultiValue(command, f_name, ex);\n\n\t}\n\n\t/**\n\t * Simple method for adding a new field to the command data form. Only field name (variable name) and field default\n\t * value can be set.\n\t *\n\t * @param packet is a <code>Packet</code> instance of the ad-hoc command request to be modified.\n\t * @param f_name is a <code>String</code> instance with the field name. In ad-hoc command terms this is a variable\n\t * name. This field name (variable name) will be also displayed as the field label.\n\t * @param f_value is a <code>String</code> instance with the field default value.\n\t */\n\tpublic static void addFieldValue(final Packet packet, final String f_name, final String f_value) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL);\n\t\tDataForm.addFieldValue(command, f_name, f_value);\n\t}\n\n\t/**\n\t * This method allows to add a new multi-option-select-one data field to the command data form. This is much more\n\t * complex implementation allowing to set a field label and labels for all provided field options. It allows the\n\t * end-user to select a single option from a given list.\n\t *\n\t * @param packet is a <code>Packet</code> instance of the ad-hoc command request to be modified.\n\t * @param f_name is a <code>String</code> instance with the field name. In ad-hoc command terms this is a variable\n\t * name.\n\t * @param f_value is a <code>String</code> instance with the field default value. It must match one of the options\n\t * vaulues provided as a list in 'options' parameter.\n\t * @param label is a <code>String</code> instance with the field label. This time a label set here is displayed to\n\t * the user instead of the field name (variable name). This is useful if the variable name is not suitable or clear\n\t * enough to the end-user.\n\t * @param labels is an array with options labels which are displayed to the end-user upon presenting the selection\n\t * options.\n\t * @param options is an array with options values to be selected by the end-user. Normally these values are not\n\t * displayed to the end-user. Only options labels are.\n\t */\n\tpublic static void addFieldValue(final Packet packet, final String f_name, final String f_value, final String label,\n\t\t\t\t\t\t\t\t\t final String[] labels, final String[] options) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL);\n\t\tDataForm.addFieldValue(command, f_name, f_value, label, labels, options);\n\t}\n\n\t/**\n\t * This method allows to add a new multi-option-select-many data field to the command data form. This is much more\n\t * complex implementation allowing to set a field label and labels for all provided field options. It allows the\n\t * end-user to select many options from the given list.\n\t *\n\t * @param packet is a <code>Packet</code> instance of the ad-hoc command request to be modified.\n\t * @param f_name is a <code>String</code> instance with the field name. In ad-hoc command terms this is a variable\n\t * name.\n\t * @param f_values is an array of default values which are presented to the end user as preselected options. They\n\t * must match options vaulues provided as a list in 'options' parameter.\n\t * @param label is a <code>String</code> instance with the field label. This time a label set here is displayed to\n\t * the user instead of the field name (variable name). This is useful if the variable name is not suitable or clear\n\t * enough to the end-user.\n\t * @param labels is an array with options labels which are displayed to the end-user upon presenting the selection\n\t * options.\n\t * @param options is an array with options values to be selected by the end-user. Normally these values are not\n\t * displayed to the end-user. Only options labels are.\n\t */\n\tpublic static void addFieldValue(final Packet packet, final String f_name, final String[] f_values,\n\t\t\t\t\t\t\t\t\t final String label, final String[] labels, final String[] options) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL);\n\t\tDataForm.addFieldValue(command, f_name, f_values, label, labels, options);\n\t}\n\n\tpublic static void addFieldValue(final Packet packet, final String f_name, final String f_value, final String label,\n\t\t\t\t\t\t\t\t\t final String[] labels, final String[] options, final String type) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL);\n\t\tDataForm.addFieldValue(command, f_name, f_value, label, labels, options, type);\n\t}\n\n\tpublic static void addFieldValue(final Packet packet, final String f_name, final String f_value,\n\t\t\t\t\t\t\t\t\t final String type) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL);\n\t\tDataForm.addFieldValue(command, f_name, f_value, type);\n\t}\n\n\tpublic static void addFieldValue(final Packet packet, final String f_name, final String f_value, final String type,\n\t\t\t\t\t\t\t\t\t final String label) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL);\n\t\tDataForm.addFieldValue(command, f_name, f_value, type, label);\n\t}\n\n\tpublic static void addHiddenField(Packet packet, String f_name, String f_value) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL);\n\t\tDataForm.addFieldValue(command, f_name, f_value, \"hidden\");\n\t}\n\n\tpublic static void addInstructions(final Packet packet, final String instructions) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL);\n\t\tDataForm.addInstructions(command, instructions);\n\t}\n\n\tpublic static void addNote(final Packet packet, final String note) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL);\n\t\tElement notes = command.getChild(\"note\");\n\n\t\tif (notes == null) {\n\t\t\tnotes = new Element(\"note\", new String[]{\"type\"}, new String[]{\"info\"});\n\t\t\tcommand.addChild(notes);\n\t\t}\n\t\tnotes.setCData(XMLUtils.escape(note));\n\t}\n\n\tpublic static void addTextField(Packet packet, String f_name, String f_value) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL);\n\t\tDataForm.addFieldValue(command, f_name, f_value, \"fixed\");\n\t}\n\n\tpublic static void addTitle(final Packet packet, final String title) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL);\n\t\tDataForm.addTitle(command, title);\n\t}\n\n\tprotected static Element createCommandEl(String node, DataType data_type) {\n\t\tElement command = new Element(COMMAND_EL, new String[]{\"xmlns\", \"node\"}, new String[]{XMLNS, node});\n\n\t\tif (data_type != null) {\n\t\t\taddDataForm(command, data_type);\n\t\t\tif (data_type == DataType.result) {\n\t\t\t\tcommand.setAttribute(\"status\", Status.completed.name());\n\t\t\t}\n\t\t\tif (data_type == DataType.form) {\n\t\t\t\tcommand.setAttribute(\"status\", Status.executing.name());\n\t\t\t\taddActionElToCommand(command, Action.complete);\n\t\t\t}\n\t\t}\n\t\treturn command;\n\t}\n\n\tprivate static Element createIqCommandEl(JID from, JID to, StanzaType type, String id, String node,\n\t\t\t\t\t\t\t\t\t\t   DataType data_type, String xmlns) {\n\t\tElement iq = new Element(\"iq\", new String[]{\"type\"}, new String[]{type.toString()});\n\n\t\tif (xmlns != null) {\n\t\t\tiq.setAttribute(\"xmlns\", xmlns);\n\t\t}\n\n\t\tif (from != null) {\n\t\t\tiq.setAttribute(\"from\", from.toString());\n\t\t}\n\n\t\t// The IQ packet (and command for that matter) should have the id attribute\n\t\t// therefore if it is not set then NPE should be thrown.\n\t\t// if (id != null) {\n\t\tiq.setAttribute(\"id\", id);\n\n\t\t// }\n\t\tif (to != null) {\n\t\t\tiq.setAttribute(\"to\", to.toString());\n\t\t}\n\n\t\tiq.addChild(createCommandEl(node, data_type));\n\n\t\treturn iq;\n\t}\n\n\tpublic static Element createIqCommand(JID from, JID to, final StanzaType type, final String id, final String node,\n\t\t\t\t\t\t\t\t\t\t  final DataType data_type) {\n\t\tElement iq = createIqCommand(from, to, type, id, node, data_type, Iq.CLIENT_XMLNS);\n\n\t\treturn iq;\n\t}\n\n\tprivate static Element createIqCommand(JID from, JID to, final StanzaType type, final String id, final String node,\n\t\t\t\t\t\t\t\t\t\t  final DataType data_type, String xmlns) {\n\t\tElement iq = createIqCommandEl(from, to, type, id, node, data_type, xmlns);\n\n\t\treturn iq;\n\t}\n\n\tpublic static Action getAction(final Packet packet) {\n\t\tString action = packet.getAttributeStaticStr(Iq.IQ_COMMAND_PATH, \"action\");\n\n\t\ttry {\n\t\t\treturn Action.valueOf(action);\n\t\t} catch (Exception e) {\n\t\t\treturn Action.other;\n\t\t}\n\t}\n\n\tpublic static boolean getCheckBoxFieldValue(Packet packet, String f_name) {\n\t\tString result = getFieldValue(packet, f_name);\n\n\t\tif (result == null) {\n\t\t\treturn false;\n\t\t}\n\t\tresult = result.trim();\n\n\t\treturn result.equalsIgnoreCase(\"true\") || result.equals(\"1\");\n\t}\n\n\tpublic static List<Element> getData(final Packet packet) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL);\n\n\t\treturn command.getChildren();\n\t}\n\n\tpublic static Element getData(final Packet packet, final String el_name, final String xmlns) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL);\n\n\t\treturn command.getChild(el_name, xmlns);\n\t}\n\n\tpublic static String getFieldKeyStartingWith(Packet packet, String f_name) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL, XMLNS);\n\n\t\treturn DataForm.getFieldKeyStartingWith(command, f_name);\n\t}\n\n\tpublic static String getFieldValue(Packet packet, String f_name) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL, XMLNS);\n\t\treturn DataForm.getFieldValue(command, f_name);\n\t}\n\n\tpublic static String getFieldValue(final Packet packet, final String f_name, boolean debug) {\n\t\tElement iq = packet.getElement();\n\n\t\tlog.log(Level.FINEST, \"Command iq: \" + iq.toString());\n\n\t\tElement command = iq.getChild(COMMAND_EL, XMLNS);\n\n\t\tlog.log(Level.FINEST, \"Command command: \" + command.toString());\n\n\t\tElement x = command.getChild(\"x\", \"jabber:x:data\");\n\n\t\tif (x == null) {\n\t\t\tlog.log(Level.FINEST, \"Command x: NULL\");\n\n\t\t\treturn null;\n\t\t}\n\t\tlog.log(Level.FINEST, \"Command x: \" + x.toString());\n\n\t\tList<Element> children = x.getChildren();\n\n\t\tfor (Element child : children) {\n\t\t\tlog.log(Level.FINEST, \"Command form child: \" + child.toString());\n\t\t\tif (child.getName().equals(DataForm.FIELD_EL) && child.getAttributeStaticStr(\"var\").equals(f_name)) {\n\t\t\t\tString value = child.getChildCDataStaticStr(DataForm.FIELD_VALUE_PATH);\n\n\t\t\t\tlog.log(Level.FINEST, \"Command found: field=\" + f_name + \", value=\" + value);\n\t\t\t\tif (value != null) {\n\t\t\t\t\treturn XMLUtils.unescape(value);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tlog.log(Level.FINEST, \"Command not found: field=\" + f_name + \", value=\" +\n\t\t\t\t\t\t\t\t child.getChildCDataStaticStr(DataForm.FIELD_VALUE_PATH));\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic static String[] getFieldValues(final Packet packet, final String f_name) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL, XMLNS);\n\n\t\treturn DataForm.getFieldValues(command, f_name);\n\t}\n\n\tpublic static boolean removeFieldValue(final Packet packet, final String f_name) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL, XMLNS);\n\n\t\treturn DataForm.removeFieldValue(command, f_name);\n\t}\n\n\tpublic static void setData(final Packet packet, final Element data) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL);\n\n\t\tcommand.addChild(data);\n\t}\n\n\tpublic static void setData(final Packet packet, final List<Element> data) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL);\n\n\t\tcommand.addChildren(data);\n\t}\n\n\tpublic static void setStatus(final Packet packet, final Status status) {\n\t\tElement iq = packet.getElement();\n\t\tElement command = iq.getChild(COMMAND_EL);\n\n\t\tcommand.setAttribute(\"status\", status.toString());\n\t}\n\n\tprivate static void setStatusEl(Element iq, Status status) {\n\t\tElement command = iq.getChild(COMMAND_EL);\n\n\t\tcommand.setAttribute(\"status\", status.name());\n\t}\n\n\tpublic static Command valueof(String cmd) {\n\t\ttry {\n\t\t\treturn Command.valueOf(cmd);\n\t\t} catch (Exception e) {\n\t\t\treturn OTHER;\n\t\t}    // end of try-catch\n\t}\n\n\tprivate Command(Priority priority) {\n\t\tthis.priority = priority;\n\t}\n\n\t/**\n\t * Method returns instance of a Packet with command element added.\n\t *\n\t * WARNING: Returned packet will not have any XMLNS set!\n\t *\n\t */\n\tpublic Packet getPacket(JID from, JID to, final StanzaType type, final String id) {\n\t\tElement elem = createIqCommand(from, to, type, id, this.toString(), null, null);\n\t\tPacket result = Packet.packetInstance(elem, from, to);\n\n\t\t// Added to ensure that command will be executed in proper thread\n\t\t// and to ensure that multiple threads will be used for processing\n\t\t// command packets\n\t\tresult.setPacketFrom(from);\n\t\tresult.setPacketTo(to);\n\n\t\tresult.setPriority(priority);\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Method returns instance of a Packet with command element added.\n\t *\n\t * WARNING: Returned packet will not have any XMLNS set!\n\t * \n\t */\n\tpublic Packet getPacket(JID from, JID to, StanzaType type, String id, DataType data_type) {\n\t\tElement elem = createIqCommand(from, to, type, id, this.toString(), data_type, null);\n\t\tPacket result = Packet.packetInstance(elem, from, to);\n\n\t\tresult.setPriority(priority);\n\n\t\treturn result;\n\t}\n\n\tpublic static class Builder {\n\n\t\tprivate final Element command;\n\n\t\tpublic Builder(Packet packet) {\n\t\t\tcommand = Optional.ofNullable(packet.getElemChild(COMMAND_EL)).orElseGet(() -> createCommandEl(packet));\n\t\t}\n\n\t\tpublic Builder addAction(Action action) {\n\t\t\taddActionElToCommand(command, action);\n\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic DataForm.Builder addDataForm(DataType type) {\n\t\t\treturn new DataForm.Builder(command, type);\n\t\t}\n\n\t\tprivate static Element createCommandEl(Packet packet) {\n\t\t\tElement commandEl = new Element(COMMAND_EL, XMLNS);\n\t\t\tpacket.getElement().addChild(commandEl);\n\t\t\treturn commandEl;\n\t\t}\n\n\t}\n}    // Command\n\n"
  },
  {
    "path": "src/main/java/tigase/server/ComponentInfo.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport tigase.cluster.api.ClusteredComponentIfc;\nimport tigase.util.Version;\nimport tigase.xml.Element;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Helper class for storing and handling additional informations about components\n *\n * @author Wojciech Kapcia\n */\npublic class ComponentInfo {\n\n\tprivate static final Logger log = Logger.getLogger(ComponentInfo.class.getName());\n\tprivate String cls;\n\tprivate HashMap<String, Object> cmpData;\n\tprivate String name = null;\n\tprivate String title;\n\tprivate String version;\n\n\t/**\n\t * Allows retrieving implementation package (obtained from jar package) for a given class\n\t *\n\t * @param c class for which package is to be retrieved\n\t *\n\t * @return package containing given class\n\t */\n\tpublic static Package getImplementation(Class<?> c) {\n\t\treturn c.getPackage() == null ? XMPPServer.class.getPackage() : c.getPackage();\n\t}\n\n\t/**\n\t * Allows retrieving implementation information (obtained from jar package) for a given class\n\t *\n\t * @param c class for which Package information is to be retrieved\n\t *\n\t * @return title and version of the Package holding class\n\t */\n\tpublic static String getImplementationInfo(Class<?> c) {\n\t\treturn getImplementationTitle(c) + \", version: \" + getImplementationVersion(c);\n\t}\n\n\t/**\n\t * Allows retrieving implementation title (obtained from jar package) for a given class\n\t *\n\t * @param c class for which Package title is to be retrieved\n\t *\n\t * @return Package title of the given class\n\t */\n\tpublic static String getImplementationTitle(Class<?> c) {\n\t\tPackage p = getImplementation(c);\n\n\t\tString title = p == null ? null : p.getImplementationTitle();\n\n\t\treturn title == null ? \"\" : title;\n\t}\n\n\tpublic static Optional<Version> getImplementationVersion(String... classes) {\n\t\treturn Arrays.stream(classes).map(clz -> {\n\t\t\ttry {\n\t\t\t\treturn Class.forName(clz);\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.FINE, \"Problem obtaining version for class: \" + clz + \", exception: \" + e.getMessage());\n\t\t\t}\n\t\t\treturn null;\n\t\t}).filter(Objects::nonNull).map(clz -> {\n\t\t\tVersion version = null;\n\t\t\ttry {\n\t\t\t\tversion = Version.of(ComponentInfo.getImplementationVersion(clz));\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.WARNING, \"Problem obtaining current version information\");\n\t\t\t\tversion = Version.ZERO;\n\t\t\t}\n\t\t\treturn version;\n\t\t}).max(Version.VERSION_COMPARATOR);\n\t}\n\n\t/**\n\t * Allows retrieving implementation version (obtained from jar package) for a given class\n\t *\n\t * @param c class for which Package version is to be retrieved\n\t *\n\t * @return Package version of the given class\n\t */\n\tpublic static String getImplementationVersion(Class<?> c) {\n\t\tPackage p = getImplementation(c);\n\n\t\tString version = p == null ? null : p.getImplementationVersion();\n\n\t\tif (ClusteredComponentIfc.class.isAssignableFrom(c)) {\n\t\t\tClass<?> superClass = c.getSuperclass();\n\t\t\tPackage superPackage = getImplementation(superClass);\n\t\t\tif (p != superPackage && superPackage != null && !p.equals(superPackage)) {\n\t\t\t\tString superVersion = superPackage.getImplementationVersion();\n\t\t\t\tif (superVersion != null && version != null && !version.equals(superVersion)) {\n\t\t\t\t\tversion += \"-\" + superVersion;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn (version == null) ? \"\" : version;\n\t}\n\n\tpublic static Optional<ComponentInfo> of(String className) {\n\t\tComponentInfo componentInfo = null;\n\t\ttry {\n\t\t\tfinal Class<?> clazz = Class.forName(className);\n\t\t\tcomponentInfo = new ComponentInfo(clazz);\n\t\t} catch (Exception e) {\n\t\t\t// if absent don't print anything\n\t\t}\n\t\treturn Optional.ofNullable(componentInfo);\n\t}\n\n\t/**\n\t * Creates ComponentInfo object with initial data\n\t *\n\t * @param cmpTitle title of the component\n\t * @param cmpVersion version of the component\n\t * @param cmpCls class of the component\n\t */\n\tpublic ComponentInfo(String cmpTitle, String cmpVersion, String cmpCls) {\n\t\tthis(null, cmpTitle, cmpVersion, cmpCls);\n\t}\n\n\t/**\n\t * Creates ComponentInfo object with initial data\n\t *\n\t * @param cmpName name of the component\n\t * @param cmpTitle title of the component\n\t * @param cmpVersion version of the component\n\t * @param cmpCls class of the component\n\t */\n\tpublic ComponentInfo(String cmpName, String cmpTitle, String cmpVersion, String cmpCls) {\n\t\tthis(null, cmpTitle, cmpVersion, cmpCls, new HashMap<String, Object>());\n\t}\n\n\t/**\n\t * Creates ComponentInfo object with initial data\n\t *\n\t * @param cmpName name of the component\n\t * @param cmpTitle title of the component\n\t * @param cmpVersion version of the component\n\t * @param cmpCls class of the component\n\t * @param cmpData additional information about component\n\t */\n\tpublic ComponentInfo(String cmpName, String cmpTitle, String cmpVersion, String cmpCls,\n\t\t\t\t\t\t HashMap<String, Object> cmpData) {\n\t\tthis.name = cmpName;\n\t\tthis.title = cmpTitle;\n\t\tthis.version = cmpVersion;\n\t\tthis.cls = cmpCls;\n\t\tthis.cmpData = cmpData;\n\t}\n\n\t/**\n\t * Creates ComponentInfo object with initial data\n\t *\n\t * @param c class of the component\n\t */\n\tpublic ComponentInfo(Class<?> c) {\n\t\tthis(null, c);\n\t}\n\n\t/**\n\t * Creates ComponentInfo object with initial data\n\t *\n\t * @param cmpName name of the component\n\t * @param c class of the component\n\t */\n\tpublic ComponentInfo(String cmpName, Class<?> c) {\n\t\tthis.name = cmpName;\n\t\tthis.title = getImplementationTitle(c);\n\t\tthis.version = getImplementationVersion(c);\n\t\tthis.cls = c.getName();\n\t\tthis.cmpData = new HashMap<>();\n\t}\n\n\t/**\n\t * Allows retrieving of component's name\n\t *\n\t * @return component name\n\t */\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t/**\n\t * Allows retrieving of component's title\n\t *\n\t * @return component title\n\t */\n\tpublic String getComponentTitle() {\n\t\treturn title;\n\t}\n\n\t/**\n\t * Allows retrieving of component's version\n\t *\n\t * @return component version\n\t */\n\tpublic String getComponentVersion() {\n\t\treturn version;\n\t}\n\n\t/**\n\t * Allows retrieving of component's class\n\t *\n\t * @return component class\n\t */\n\tpublic String getComponentClass() {\n\t\treturn cls;\n\t}\n\n\t/**\n\t * Allows retrieving of component's additional data\n\t *\n\t * @return component additional data\n\t */\n\tpublic HashMap<String, Object> getComponentData() {\n\t\treturn cmpData;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn (name == null ? \"\" : name + \" :: \") + \"componentInfo{\" +\n\t\t\t\t(title.isEmpty() ? \"\" : \"Title=\" + title + \", \") +\n\t\t\t\t(version.isEmpty() ? \"\" : \"Version=\" + version + \", \") + \"Class=\" + cls +\n\t\t\t\t(cmpData.isEmpty() ? \"\" : \", componentData=\" + cmpData) + '}';\n\t}\n\n\t/**\n\t * Allows retrieving of component's information as Element\n\t *\n\t * @return component information as Element\n\t */\n\tpublic Element toElement() {\n\t\tElement cmpInfo = new Element(\"cmpInfo\");\n\t\tif (name != null) {\n\t\t\tcmpInfo.addChild(new Element(\"name\", name));\n\t\t}\n\t\tif (!title.isEmpty()) {\n\t\t\tcmpInfo.addChild(new Element(\"title\", title));\n\t\t}\n\t\tif (!version.isEmpty()) {\n\t\t\tcmpInfo.addChild(new Element(\"version\", version));\n\t\t}\n\t\tif (!cls.isEmpty()) {\n\t\t\tcmpInfo.addChild(new Element(\"class\", cls));\n\t\t}\n\t\tif (!cmpData.isEmpty()) {\n\t\t\tElement data = new Element(\"cmpData\");\n\t\t\tfor (String key : cmpData.keySet()) {\n\t\t\t\tdata.addChild(new Element(key, cmpData.get(key).toString()));\n\t\t\t}\n\t\t\tcmpInfo.addChild(data);\n\t\t}\n\t\treturn cmpInfo;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/ComponentRegistrator.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport tigase.conf.ConfigurationException;\n\n/**\n * Interface ComponentRegistrator\n * <br>\n * Collects information about all ServerComponents connected to MessageRouter\n * <br>\n * Created: Tue Nov 22 07:07:11 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface ComponentRegistrator\n\t\textends ServerComponent {\n\n\t/**\n\t * @param component to be registered\n\t *\n\t * @return a <code>boolean</code> value indicating whether component has been successfully added or not.\n\t */\n\tboolean addComponent(ServerComponent component) throws ConfigurationException;\n\n\t/**\n\t * @param component to be registered\n\t *\n\t * @return a <code>boolean</code> value indicating whether component has been successfully removed or not.\n\t */\n\tboolean deleteComponent(ServerComponent component);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/ConnectionManager.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport tigase.annotations.TODO;\nimport tigase.io.CertificateContainerIfc;\nimport tigase.io.SSLContextContainerIfc;\nimport tigase.kernel.beans.*;\nimport tigase.kernel.beans.config.AbstractBeanConfigurator;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.beans.config.ConfigurationChangedAware;\nimport tigase.kernel.core.Kernel;\nimport tigase.net.*;\nimport tigase.server.script.CommandIfc;\nimport tigase.server.xmppclient.XMPPIOProcessor;\nimport tigase.stats.StatisticsList;\nimport tigase.util.common.TimerTask;\nimport tigase.util.repository.DataTypes;\nimport tigase.xml.Element;\nimport tigase.xmpp.StreamError;\nimport tigase.xmpp.XMPPDomBuilderHandler;\nimport tigase.xmpp.XMPPIOService;\nimport tigase.xmpp.XMPPIOServiceListener;\nimport tigase.xmpp.jid.JID;\n\nimport javax.script.Bindings;\nimport java.io.BufferedReader;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.nio.channels.SocketChannel;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.xmpp.XMPPIOService.DOM_HANDLER;\n\n/**\n * Describe class ConnectionManager here.\n * <br>\n * Created: Sun Jan 22 22:52:58 2006\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic abstract class ConnectionManager<IO extends XMPPIOService<?>>\n\t\textends AbstractMessageReceiver\n\t\timplements XMPPIOServiceListener<IO>, RegistrarBean {\n\n\tpublic static final String HT_TRAFFIC_THROTTLING_PROP_KEY = \"--cm-ht-traffic-throttling\";\n\n\tpublic static final String HT_TRAFFIC_THROTTLING_PROP_VAL = \"xmpp:50k:0:disc,bin:400m:0:disc\";\n\n\tpublic static final String NET_BUFFER_HT_PROP_KEY = \"--net-buff-high-throughput\";\n\n\tpublic static final String NET_BUFFER_ST_PROP_KEY = \"--net-buff-standard\";\n\n\tpublic static final String PORT_LOCAL_HOST_PROP_KEY = \"local-host\";\n\n\tpublic static final String ST_TRAFFIC_THROTTLING_PROP_KEY = \"--cm-traffic-throttling\";\n\n\tpublic static final String ST_TRAFFIC_THROTTLING_PROP_VAL = \"xmpp:2500:0:disc,bin:20m:0:disc\";\n\n\tpublic static final String TRAFFIC_THROTTLING_PROP_KEY = \"traffic-throttling\";\n\t/**\n\t * Key name of the system property for configuration protection from system overload and DOS attack.\n\t */\n\tpublic static final String ELEMENTS_NUMBER_LIMIT_PROP_KEY = \"elements-number-limit\";\n\tpublic static final long LAST_MINUTE_BIN_LIMIT_PROP_VAL = 20000000L;\n\tpublic static final long LAST_MINUTE_PACKETS_LIMIT_PROP_VAL = 2500L;\n\tpublic static final String MAX_INACTIVITY_TIME = \"max-inactivity-time\";\n\tpublic static final String MAX_RECONNECTS_PROP_KEY = \"max-reconnects\";\n\tpublic static final int NET_BUFFER_HT_PROP_VAL = 64 * 1024;\n\tpublic static final int NET_BUFFER_ST_PROP_VAL = 2 * 1024;\n\tpublic static final int NET_BUFFER_LIMIT_HT_PROP_VAL = 20 * 1024 * 1024;\n\tpublic static final int NET_BUFFER_LIMIT_ST_PROP_VAL = 2 * 1024 * 1024;\n\tpublic static final String PORT_CLASS_PROP_KEY = \"class\";\n\tpublic static final String PORT_IFC_PROP_KEY = \"ifc\";\n\tpublic static final String PORT_LISTENING_DELAY_KEY = \"port-delay-listening\";\n\tpublic static final boolean PORT_LISTENING_DELAY_DEF = false;\n\tpublic static final String PORT_KEY = \"port-no\";\n\tpublic static final String PORT_NEW_CONNECTIONS_THROTTLING_KEY = \"new-connections-throttling\";\n\tpublic static final String PORT_PROXY_PROTOCOL_PROP_KEY = \"proxy-protocol\";\n\tpublic static final String PORT_REMOTE_HOST_PROP_KEY = \"remote-host\";\n\tpublic static final String PORT_REMOTE_HOST_PROP_VAL = \"localhost\";\n\tpublic static final String PORT_SOCKET_PROP_KEY = \"socket\";\n\tpublic static final String PORT_TYPE_PROP_KEY = \"type\";\n\tpublic static final String PROP_KEY = \"connections/\";\n\n\tpublic static final int SOCKET_BUFFER_ST_PROP_VAL  = 4 * 1024;\n\n\tpublic static final int SOCKET_BUFFER_HT_PROP_VAL = NET_BUFFER_HT_PROP_VAL;\n\n\tpublic static final long TOTAL_BIN_LIMIT_PROP_VAL = 0L;\n\tpublic static final long TOTAL_PACKETS_LIMIT_PROP_VAL = 0L;\n\tpublic static final String WHITE_CHAR_ACK_PROP_KEY = \"white-char-ack\";\n\tpublic static final String XMPP_ACK_PROP_KEY = \"xmpp-ack\";\n\tpublic static final boolean XMPP_ACK_PROP_VAL = false;\n\tpublic static final boolean WHITE_CHAR_ACK_PROP_VAL = false;\n\tpublic static final String PORTS_PROP_KEY = PROP_KEY + \"ports\";\n\tpublic static final String WATCHDOG_DELAY = \"watchdog_delay\";\n\tpublic static final String WATCHDOG_TIMEOUT = \"watchdog_timeout\";\n\tpublic static final String WATCHDOG_PING_TYPE_KEY = \"watchdog_ping_type\";\n\n\tprotected static final Element pingElement = new Element(\"iq\", new Element[]{\n\t\t\tnew Element(\"ping\", new String[]{\"xmlns\"}, new String[]{\"urn:xmpp:ping\"})}, new String[]{\"type\", \"id\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t new String[]{\"get\", \"tigase-ping\"});\n\tprivate static final Logger log = Logger.getLogger(ConnectionManager.class.getName());\n\tpublic enum LIMIT_ACTION {\n\t\tDISCONNECT,\n\t\tDROP_PACKETS\n\t}\n\n\t/**\n\t * Holds possible types of ping to be used in watchdog service for detection of broken connections\n\t */\n\tpublic enum WATCHDOG_PING_TYPE {\n\t\tWHITESPACE,\n\t\tXMPP\n\t}\n\n\t/**\n\t * Default value for the system property for configuration protection from system overload and DOS attack.\n\t */\n\tpublic static int ELEMENTS_NUMBER_LIMIT_PROP_VAL = 1000;\n\tprivate static ConnectionOpenThread connectThread = ConnectionOpenThread.getInstance();\n\t@ConfigField(desc = \"Interfaces to listen on\", alias = PORT_IFC_PROP_KEY)\n\tpublic String[] PORT_IFC_PROP_VAL = {\"*\"};\n\t@ConfigField(desc = \"Delay before connection is established\")\n\tprotected long connectionDelay = 2 * SECOND;\n\tprotected boolean delayPortListening = PORT_LISTENING_DELAY_DEF;\n\t/**\n\t * Protection from the system overload and DOS attack. We want to limit number of elements created within a single\n\t * XMPP stanza.\n\t */\n\t@ConfigField(desc = \"Limit of elements for single XMPP stanza\", alias = ELEMENTS_NUMBER_LIMIT_PROP_KEY)\n\tprotected int elements_number_limit = ELEMENTS_NUMBER_LIMIT_PROP_VAL;\n\tprotected Kernel kernel;\n\t@ConfigField(desc = \"Default size of a network buffer\", alias = \"net-buffer\")\n\tprotected int net_buffer = isHighThroughput() ? NET_BUFFER_HT_PROP_VAL : NET_BUFFER_ST_PROP_VAL;\n\t@ConfigField(desc = \"Size of TCP receive socket buffer for connection\", alias = \"socket-buffer-size\")\n\tprivate int socketBufferSize = isHighThroughput() ? SOCKET_BUFFER_HT_PROP_VAL : SOCKET_BUFFER_ST_PROP_VAL;\n\t@Inject(nullAllowed = true)\n\tprotected XMPPIOProcessor[] processors = new XMPPIOProcessor[0];\n\t@ConfigField(desc = \"Traffic throttling\")\n\tprotected String trafficThrottling = null;\n\t@ConfigField(desc = \"Watchdog delay\", alias = \"watchdog-delay\")\n\tprotected long watchdogDelay = 10 * MINUTE; // 600 000\n\t@ConfigField(desc = \"Watchdog ping type\", alias = \"watchdog-ping-type\")\n\tprotected WATCHDOG_PING_TYPE watchdogPingType = WATCHDOG_PING_TYPE.WHITESPACE;\n\t@ConfigField(desc = \"Watchdog timeout\", alias = \"watchdog-timeout\")\n\tprotected long watchdogTimeout = 29 * MINUTE; // 1 740 000\n\tprivate long bytesReceived = 0;\n\tprivate long bytesSent = 0;\n\t@Inject\n\tprivate CertificateContainerIfc certificateContainer;\n\t@ConfigField(desc = \"Flash cross domain policy file path\", alias = XMPPIOService.CROSS_DOMAIN_POLICY_FILE_PROP_KEY)\n\tprivate String flashCrossDomainPolicyFile = XMPPIOService.CROSS_DOMAIN_POLICY_FILE_PROP_VAL;\n\tprivate String flassCrossDomainPolicy = null;\n\tprivate IOServiceStatisticsGetter ioStatsGetter = new IOServiceStatisticsGetter();\n\t@ConfigField(desc = \"Limit of bytes per minute for connection\")\n\tprivate long last_minute_bin_limit = LAST_MINUTE_BIN_LIMIT_PROP_VAL;\n\t@ConfigField(desc = \"Limit of packets per minute for connection\")\n\tprivate long last_minute_packets_limit = LAST_MINUTE_PACKETS_LIMIT_PROP_VAL;\n\t@ConfigField(desc = \"Maximal allowed time of inactivity of connection\")\n\tprivate long maxInactivityTime = getMaxInactiveTime();\n\t@ConfigField(desc = \"Limit of size for network buffer for connection\", alias = \"net-buffer-limit\")\n\tprivate int net_buffer_limit = 0;\n\tprivate Set<ConnectionListenerImpl> pending_open = Collections.synchronizedSet(\n\t\t\tnew HashSet<ConnectionListenerImpl>());\n\t@Inject\n\tprivate PortsConfigBean portsConfigBean;\n\tprivate ConcurrentHashMap<String, IO> services = new ConcurrentHashMap<String, IO>();\n\tprivate int services_size = 0;\n\tprivate long socketOverflow = 0;\n\t@Inject(bean = \"sslContextContainer\")\n\tprivate SSLContextContainerIfc sslContextContainer;\n\tprivate boolean started = false;\n\t@ConfigField(desc = \"Limit of total numer of bytes per connection\")\n\tprivate long total_bin_limit = TOTAL_BIN_LIMIT_PROP_VAL;\n\t@ConfigField(desc = \"Limit of total number of packets per connection\")\n\tprivate long total_packets_limit = TOTAL_PACKETS_LIMIT_PROP_VAL;\n\tprivate LinkedList<Map<String, Object>> waitingTasks = new LinkedList<Map<String, Object>>();\n\tprivate Watchdog watchdog = null;\n\tprivate long watchdogRuns = 0;\n\tprivate long watchdogStopped = 0;\n\tprivate long watchdogTests = 0;\n\tprivate boolean white_char_ack = WHITE_CHAR_ACK_PROP_VAL;\n\n\t@ConfigField(desc = \"Action taken if XMPP limit is exceeded\")\n\tprivate LIMIT_ACTION xmppLimitAction = LIMIT_ACTION.DISCONNECT;\n\tprivate boolean xmpp_ack = XMPP_ACK_PROP_VAL;\n\n\t@ConfigField(desc = \"Service connection timeout\", alias = \"service-connected-timeout\")\n\tprotected int serviceConnectedTimeout = 60;\n\t@ConfigField(desc = \"Whitelisted IPs allowed to connect\", alias = \"ip-whitelist\")\n\tprivate List<String> whitelistedIPs;\n\n\tpublic ConnectionManager() {\n\t}\n\n\tprotected ConnectionManager(int socketBufferSize) {\n\t\tthis.socketBufferSize = socketBufferSize;\n\t}\n\n\tprotected boolean enableServiceConnectedTimeout(IO service) {\n\t\treturn service != null && service.connectionType() == ConnectionType.accept;\n\t}\n\n\t@Override\n\tpublic void beanConfigurationChanged(Collection<String> changedFields) {\n\t\tsuper.beanConfigurationChanged(changedFields);\n\t}\n\n\tpublic boolean checkTrafficLimits(IO serv) {\n\t\tboolean xmppLimitHit = false;\n\n\t\tif (last_minute_packets_limit > 0) {\n\t\t\txmppLimitHit = (serv.getPacketsReceived(false) >= last_minute_packets_limit);\n\t\t}\n\t\tif (!xmppLimitHit && (total_packets_limit > 0)) {\n\t\t\txmppLimitHit = (serv.getTotalPacketsReceived() >= total_packets_limit);\n\t\t}\n\t\tif (xmppLimitHit) {\n\t\t\tLevel level = Level.FINER;\n\n\t\t\tif (isHighThroughput()) {\n\t\t\t\tlevel = Level.WARNING;\n\t\t\t}\n\t\t\tswitch (xmppLimitAction) {\n\t\t\t\tcase DROP_PACKETS:\n\t\t\t\t\tif (log.isLoggable(level)) {\n\t\t\t\t\t\tlog.log(level, \"[[{0}]] XMPP Limits exceeded on connection {1}\" + \" dropping packets: {2}\",\n\t\t\t\t\t\t\t\tnew Object[]{getName(), serv, serv.getReceivedPackets()});\n\t\t\t\t\t}\n\t\t\t\t\twhile (serv.getReceivedPackets().poll() != null) {\n\t\t\t\t\t\t;\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tif (log.isLoggable(level)) {\n\t\t\t\t\t\tlog.log(level,\n\t\t\t\t\t\t\t\t\"[[{0}]] XMPP Limits exceeded on connection {1}\" + \" stopping, packets dropped: {2}\",\n\t\t\t\t\t\t\t\tnew Object[]{getName(), serv, serv.getReceivedPackets()});\n\t\t\t\t\t}\n\t\t\t\t\txmppStreamError(serv, Collections.singletonList(StreamError.PolicyViolation.prepareStreamErrorElement(\"XMPP Limits exceeded on connection\")));\n\t\t\t\t\tserv.forceStop();\n\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\tboolean binLimitHit = false;\n\t\tlong bytesSent = serv.getBytesSent(false);\n\t\tlong bytesReceived = serv.getBytesReceived(false);\n\n\t\tif (last_minute_bin_limit > 0) {\n\t\t\tbinLimitHit = (bytesSent >= last_minute_bin_limit) || (bytesReceived >= last_minute_bin_limit);\n\t\t}\n\n\t\tlong totalSent = serv.getTotalBytesSent();\n\t\tlong totalReceived = serv.getTotalBytesReceived();\n\n\t\tif (!binLimitHit && (total_bin_limit > 0)) {\n\t\t\tbinLimitHit = (totalReceived >= total_bin_limit) || (totalSent >= total_bin_limit);\n\t\t}\n\t\tif (binLimitHit) {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"[[{0}]] Binary Limits exceeded ({1}:{2}:{3}:{4}) on\" +\n\t\t\t\t\t\t\t\t\" connection {5} stopping, packets dropped: {6}\",\n\t\t\t\t\t\tnew Object[]{getName(), bytesSent, bytesReceived, totalSent, totalReceived, serv,\n\t\t\t\t\t\t\t\t\t serv.getReceivedPackets()});\n\t\t\t}\n\t\t\txmppStreamError(serv, Collections.singletonList(StreamError.PolicyViolation.prepareStreamErrorElement(\"XMPP Limits exceeded on connection\")));\n\n\t\t\tserv.forceStop();\n\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic synchronized void everyMinute() {\n\t\tsuper.everyMinute();\n\n\t\t// This variable used to provide statistics gets off on a busy\n\t\t// services as it is handled in methods called concurrently by\n\t\t// many threads. While accuracy of this variable is not critical\n\t\t// for the server functions, statistics should be as accurate as\n\t\t// possible to provide valuable metrics data.\n\t\t// So in the watchdog thread we re-synchronize this number\n\t\tint tmp = services.size();\n\n\t\tservices_size = tmp;\n\t\tdoForAllServices(ioStatsGetter);\n\t}\n\n\t@Override\n\tpublic int hashCodeForPacket(Packet packet) {\n\t\tif (packet.getStanzaTo() != null) {\n\t\t\treturn packet.getStanzaTo().hashCode();\n\t\t}\n\t\tif (packet.getTo() != null) {\n\t\t\treturn packet.getTo().hashCode();\n\t\t}\n\n\t\treturn super.hashCodeForPacket(packet);\n\t}\n\n\t@Override\n\tpublic void initBindings(Bindings binds) {\n\t\tsuper.initBindings(binds);\n\t\tbinds.put(CommandIfc.SERVICES_MAP, services);\n\t}\n\n\t@Override\n\tpublic void initializationCompleted() {\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.log(Level.FINER, \"isInitializationComplete(): {0}\", new Object[]{isInitializationComplete()});\n\t\t}\n\n\t\tif (isInitializationComplete()) {\n\n\t\t\t// Do we really need to do this again?\n\t\t\treturn;\n\t\t}\n\t\tsuper.initializationCompleted();\n\t\t//started = true;\n\t}\n\n\t@Override\n\tpublic void packetsReady(IO serv) throws IOException {\n\n\t\t// Under a high load data, especially lots of packets on a single\n\t\t// connection it may happen that one threads started processing\n\t\t// socketData and then another thread reads more packets which\n\t\t// may take over earlier data depending on a thread scheduler used.\n\t\t// synchronized (serv) {\n\t\tif (checkTrafficLimits(serv)) {\n\t\t\twritePacketsToSocket(serv, processSocketData(serv));\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic int processingInThreads() {\n\t\treturn Runtime.getRuntime().availableProcessors() * 4;\n\t}\n\n\t@Override\n\tpublic int processingOutThreads() {\n\t\treturn Runtime.getRuntime().availableProcessors() * 4;\n\t}\n\n\t@Override\n\tpublic void processPacket(Packet packet) {\n\t\twritePacketToSocket(packet);\n\t}\n\n\tpublic abstract Queue<Packet> processSocketData(IO serv);\n\n\t/**\n\t * Processes undelivered packets\n\t *\n\t * @param stamp - timestamp when packet was received to be written to XMPPIOService\n\t */\n\tpublic abstract boolean processUndeliveredPacket(Packet packet, Long stamp, String errorMessage);\n\n\tpublic abstract void reconnectionFailed(Map<String, Object> port_props);\n\n\tpublic HashSet<Integer> getDefPorts() {\n\t\tHashSet<Integer> result = new HashSet<>();\n\t\tint[] ports = getDefPlainPorts();\n\t\tint[] sslPorts = getDefSSLPorts();\n\t\tif (ports != null) {\n\t\t\tfor (int port : ports) {\n\t\t\t\tresult.add(port);\n\t\t\t}\n\t\t}\n\n\t\tif (sslPorts != null) {\n\t\t\tfor (int port : sslPorts) {\n\t\t\t\tresult.add(port);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\t\tthis.kernel = kernel;\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\t\tthis.kernel = null;\n\t}\n\n\t@Override\n\tpublic void release() {\n\n\t\t// delayedTasks.cancel();\n\t\treleaseListeners();\n\t\tsuper.release();\n\t}\n\n\tprotected void serviceConnected(IO service) {\n\t\tServiceConnectedTimer.cancel(service);\n\t}\n\n\t@TODO(note = \"Do something if service with the same unique ID is already started, \" +\n\t\t\t\"possibly kill the old one...\")\n\tpublic void serviceStarted(final IO service) {\n\n\t\t// synchronized(services) {\n\t\tString id = getUniqueId(service);\n\n\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\tlog.log(Level.FINE, \"[[{0}]] Connection started: {1}\", new Object[]{getName(), service});\n\t\t}\n\n\t\tIO serv = services.get(id);\n\n\t\tif (serv != null) {\n\t\t\tif (serv == service) {\n\t\t\t\tlog.log(Level.WARNING, \"That would explain a lot, adding the same service twice, ID: {1} [{0}]\",\n\t\t\t\t\t\tnew Object[]{getName(), serv});\n\t\t\t} else {\n\n\t\t\t\t// Is it at all possible to happen???\n\t\t\t\t// let's log it for now....\n\t\t\t\tlog.log(Level.FINE, \"Attempt to add different service with the same ID: {1}; old: {2} (stopped)  [{0}]\",\n\t\t\t\t\t\tnew Object[]{getName(), service, serv});\n\t\t\t\t// And stop the old service....\n\t\t\t\tserv.stop();\n\t\t\t}\n\t\t}\n\t\tservices.put(id, service);\n\t\t++services_size;\n\n\t\tif (enableServiceConnectedTimeout(service)) {\n\t\t\tServiceConnectedTimer startTimer = new ServiceConnectedTimer(service);\n\t\t\taddTimerTask(startTimer, serviceConnectedTimeout, TimeUnit.SECONDS);\n\t\t}\n\t\t// }\n\t}\n\n\t@Override\n\tpublic boolean serviceStopped(IO service) {\n\n\t\t// Hopefuly there is no exception at this point, but just in case...\n\t\t// This is a very fresh code after all\n\t\ttry {\n\t\t\tioStatsGetter.check(service);\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.CONFIG, \"Nothing serious to worry about but please notify the developer.\", e);\n\t\t}\n\n\t\tServiceConnectedTimer.cancel(service);\n\n\t\t// synchronized(service) {\n\t\tString id = getUniqueId(service);\n\n\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\tlog.log(Level.FINE, \"[[{0}]] Connection stopped: {1}\", new Object[]{getName(), service});\n\t\t}\n\n\t\t// id might be null if service is stopped in accept method due to\n\t\t// an exception during establishing TCP/IP connection\n\t\t// IO serv = (id != null ? services.get(id) : null);\n\t\tif (id != null) {\n\t\t\tboolean result = services.remove(id, service);\n\n\t\t\tif (result) {\n\t\t\t\t--services_size;\n\n\t\t\t\tredeliverWaitingPackets(service);\n\t\t\t} else if (log.isLoggable(Level.FINER)) {\n\n\t\t\t\t// Is it at all possible to happen???\n\t\t\t\t// let's log it for now....\n\t\t\t\tlog.log(Level.FINER, \"[[{0}]] Attempt to stop incorrect service: {1}\",\n\t\t\t\t\t\tnew Object[]{getName(), service});\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\n\t\treturn false;\n\n\t}\n\n\tprotected boolean shouldRedeliverWaitingPackets(IO service) {\n\t\treturn true;\n\t}\n\n\tprotected boolean isAddressWhitelisted(String address) {\n\t\tif (whitelistedIPs == null) {\n\t\t\treturn true;\n\t\t}\n\t\treturn whitelistedIPs.contains(address);\n\t}\n\n\tprotected void redeliverWaitingPackets(IO service) {\n\t\tif (!shouldRedeliverWaitingPackets(service)){\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tQueue<Packet> undeliveredPackets = service.getWaitingPackets();\n\t\tlog.log(Level.FINEST, \"[[{0}]] processing undelivered packets: {1}\",\n\t\t\t\tnew Object[]{getName(), undeliveredPackets.size()});\n\n\t\tPacket p = null;\n\t\twhile ((p = undeliveredPackets.poll()) != null) {\n\t\t\tprocessUndeliveredPacket(p, null, null);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void start() {\n\t\tsslContextContainer.start();\n\t\tsuper.start();\n\n\t\tstarted = true;\n\t\tif (!delayPortListening) {\n\t\t\tconnectWaitingTasks();\n\t\t} else {\n\t\t\tlog.log(Level.INFO, \"Delaying opening ports of component: {0}\", getName());\n\t\t}\n\n\t\tsetupWatchdogThread();\n\t\tif (null != watchdog) {\n\t\t\twatchdog.start();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void stop() {\n\t\tif (null != watchdog) {\n\t\t\twatchdog.shutdown();\n\t\t}\n\t\tstarted = false;\n\t\tthis.releaseListeners();\n\n\t\t// when stopping connection manager we need to stop all active connections as well\n\t\tfor (IO service : services.values()) {\n\t\t\tservice.forceStop();\n\t\t}\n\t\tportsConfigBean.stop();\n\t\tsuper.stop();\n\t\tsslContextContainer.stop();\n\t}\n\n\tpublic void updateConnectionDetails(Map<String, Object> port_props) {\n\t}\n\n\tpublic void writePacketsToSocket(IO serv, Queue<Packet> packets) {\n\t\tif (serv != null) {\n\n\t\t\t// synchronized (serv) {\n\t\t\tif ((packets != null) && (packets.size() > 0)) {\n\t\t\t\tPacket p = null;\n\n\t\t\t\twhile ((p = packets.poll()) != null) {\n\t\t\t\t\tif (log.isLoggable(Level.FINER) && !log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINER, \"Processing packet: {1}, type: {2} [{0}]\",\n\t\t\t\t\t\t\t\tnew Object[]{serv, p.getElemName(), p.getType()});\n\t\t\t\t\t}\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Writing packet: {1} [{0}]\", new Object[]{serv, p});\n\t\t\t\t\t}\n\t\t\t\t\tserv.addPacketToSend(p);\n\t\t\t\t}      // end of for ()\n\t\t\t\ttry {\n\t\t\t\t\tserv.processWaitingPackets();\n\t\t\t\t\tSocketThread.addSocketService(serv);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Exception during writing packets [\" + serv + \"]: \", e);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tserv.stop();\n\t\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Exception stopping XMPPIOService [\" + serv + \"[: \", e1);\n\t\t\t\t\t}    // end of try-catch\n\t\t\t\t}      // end of try-catch\n\t\t\t}\n\n\t\t\t// }\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"Can''t find service for packets: [{0}] \", packets);\n\t\t\t}\n\t\t}          // end of if (ios != null) else\n\t}\n\n\tpublic boolean writePacketToSocket(IO ios, Packet p) {\n\t\tif (ios != null) {\n\t\t\tif (log.isLoggable(Level.FINER) && !log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINER, \"Processing packet: {1}, type: {2} [{0}]\",\n\t\t\t\t\t\tnew Object[]{ios, p.getElemName(), p.getType()});\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Writing packet: {1} [{0}]\", new Object[]{ios, p});\n\t\t\t}\n\n\t\t\t// if packet is added to waiting packets queue then we can assume it is sent\n\t\t\t// as if it will fail it will be returned as error by serviceStopped method\n\t\t\tios.addPacketToSend(p);\n\t\t\tif (ios.writeInProgress.tryLock()) {\n\t\t\t\ttry {\n\t\t\t\t\tios.processWaitingPackets();\n\t\t\t\t\tSocketThread.addSocketService(ios);\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t// if there was IOException we need to forceStop this service!\n\t\t\t\t\tlog.log(Level.WARNING, \"Exception during writing packets [\" + ios + \"[: \", e);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tios.forceStop();\n\t\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Exception stopping XMPPIOService [\" + ios + \"]: \", e1);\n\t\t\t\t\t}    // end of try-catch\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Exception during writing packets [\" + ios + \"]: \", e);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tios.stop();\n\t\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Exception stopping XMPPIOService [\" + ios + \"]: \", e1);\n\t\t\t\t\t}    // end of try-catch\n\t\t\t\t} finally {\n\t\t\t\t\tios.writeInProgress.unlock();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"Can''t find service for packet: <{0}> {1}, service id: {2}\",\n\t\t\t\t\t\tnew Object[]{p.getElemName(), p.getTo(), getServiceId(p)});\n\t\t\t}\n\t\t}    // end of if (ios != null) else\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String xmppStreamError(IO serv, List<Element> err_el) {\n\t\tStreamError streamError = StreamError.getByCondition(err_el.get(0).getName());\n\n\t\tfor (XMPPIOProcessor proc : processors) {\n\t\t\tproc.streamError(serv, streamError);\n\t\t}\n\t\treturn \"<stream:error xmlns:stream=\\\"http://etherx.jabber.org/streams\\\">\" + err_el.get(0).toString() + \"</stream:error>\";\n\t}\n\n\tpublic PortsConfigBean getPortsConfigBean() {\n\t\treturn portsConfigBean;\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t\tsuper.getStatistics(list);\n\t\tlist.add(getName(), \"Open connections\", services_size, Level.INFO);\n\t\tif (list.checkLevel(Level.FINEST) || (services.size() < 1000)) {\n\t\t\tint waitingToSendSize = 0;\n\n\t\t\tfor (IO serv : services.values()) {\n\t\t\t\twaitingToSendSize += serv.waitingToSendSize();\n\t\t\t}\n\t\t\tlist.add(getName(), \"Waiting to send\", waitingToSendSize, Level.FINE);\n\t\t}\n\t\tlist.add(getName(), \"Bytes sent\", bytesSent, Level.FINE);\n\t\tlist.add(getName(), \"Bytes received\", bytesReceived, Level.FINE);\n\t\tlist.add(getName(), \"Socket overflow\", socketOverflow, Level.FINE);\n\t\tlist.add(getName(), \"Watchdog runs\", watchdogRuns, Level.FINER);\n\t\tlist.add(getName(), \"Watchdog tests\", watchdogTests, Level.FINE);\n\t\tlist.add(getName(), \"Watchdog stopped\", watchdogStopped, Level.FINE);\n\t\tfor (XMPPIOProcessor proc : processors) {\n\t\t\tproc.getStatistics(list);\n\t\t}\n\t}\n\n\tpublic IO getXMPPIOService(String serviceId) {\n\t\treturn services.get(serviceId);\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\tsuper.setName(name);\n\t}\n\n\tpublic String getFlashCrossDomainPolicy() {\n\t\treturn flassCrossDomainPolicy;\n\t}\n\n\tpublic void setFlashCrossDomainPolicyFile(String file) {\n\t\tthis.flashCrossDomainPolicyFile = file;\n\t\tif (flashCrossDomainPolicyFile != null) {\n\t\t\ttry {\n\t\t\t\tBufferedReader br = new BufferedReader(new FileReader(flashCrossDomainPolicyFile));\n\t\t\t\tString line = br.readLine();\n\t\t\t\tStringBuilder sb = new StringBuilder();\n\n\t\t\t\twhile (line != null) {\n\t\t\t\t\tsb.append(line);\n\t\t\t\t\tline = br.readLine();\n\t\t\t\t}\n\t\t\t\tsb.append('\\0');\n\t\t\t\tbr.close();\n\t\t\t\tflassCrossDomainPolicy = sb.toString();\n\t\t\t} catch (Exception ex) {\n\t\t\t\tlog.log(Level.WARNING, \"Problem reading cross domain poicy file: \" + flashCrossDomainPolicyFile, ex);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic int getNet_buffer_limit() {\n\t\tif (net_buffer_limit == 0) {\n\t\t\tAbstractBeanConfigurator configurator = kernel.getInstance(AbstractBeanConfigurator.class);\n\t\t\tif (isHighThroughput()) {\n\t\t\t\tnet_buffer_limit = (Integer) configurator.getProperties()\n\t\t\t\t\t\t.getOrDefault(\"net-buffer-high-throughput\", NET_BUFFER_LIMIT_HT_PROP_VAL);\n\t\t\t} else {\n\t\t\t\tnet_buffer_limit = (Integer) configurator.getProperties()\n\t\t\t\t\t\t.getOrDefault(\"net-buffer-standard\", NET_BUFFER_LIMIT_ST_PROP_VAL);\n\t\t\t}\n\t\t}\n\t\treturn net_buffer_limit;\n\t}\n\n\tpublic void setNet_buffer_limit(int value) {\n\t\tthis.net_buffer_limit = value;\n\t}\n\n\tpublic void setProcessors(XMPPIOProcessor[] processors) {\n\t\tif (processors == null) {\n\t\t\tprocessors = new XMPPIOProcessor[0];\n\t\t}\n\t\tthis.processors = processors;\n\t}\n\n\tpublic String getTrafficThrottling() {\n\t\tif (trafficThrottling == null) {\n\t\t\tAbstractBeanConfigurator configurator = kernel.getInstance(AbstractBeanConfigurator.class);\n\t\t\tString value = null;\n\t\t\tif (isHighThroughput()) {\n\t\t\t\tvalue = (String) configurator.getProperties()\n\t\t\t\t\t\t.getOrDefault(\"cm-ht-traffic-throttling\", HT_TRAFFIC_THROTTLING_PROP_VAL);\n\t\t\t} else {\n\t\t\t\tvalue = (String) configurator.getProperties()\n\t\t\t\t\t\t.getOrDefault(\"cm-traffic-throttling\", ST_TRAFFIC_THROTTLING_PROP_VAL);\n\t\t\t}\n\t\t\tsetTrafficThrottling(value);\n\t\t}\n\t\treturn trafficThrottling;\n\t}\n\n\tpublic void setTrafficThrottling(String trafficThrottling) {\n\t\tthis.trafficThrottling = trafficThrottling;\n\n\t\tString tmp = trafficThrottling;\n\t\tfor (String tmp_s : tmp.split(\",\")) {\n\t\t\tString[] tmp_thr = tmp_s.split(\":\");\n\n\t\t\tif (tmp_thr[0].equalsIgnoreCase(\"xmpp\")) {\n\t\t\t\tlast_minute_packets_limit = DataTypes.parseNum(tmp_thr[1], Long.class,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   LAST_MINUTE_PACKETS_LIMIT_PROP_VAL);\n\t\t\t\tlog.finest(getName() + \" last_minute_packets_limit = \" + last_minute_packets_limit);\n\t\t\t\ttotal_packets_limit = DataTypes.parseNum(tmp_thr[2], Long.class, TOTAL_PACKETS_LIMIT_PROP_VAL);\n\t\t\t\tlog.finest(getName() + \" total_packets_limit = \" + total_packets_limit);\n\t\t\t\tif (tmp_thr[3].equalsIgnoreCase(\"disc\")) {\n\t\t\t\t\txmppLimitAction = LIMIT_ACTION.DISCONNECT;\n\t\t\t\t}\n\t\t\t\tif (tmp_thr[3].equalsIgnoreCase(\"drop\")) {\n\t\t\t\t\txmppLimitAction = LIMIT_ACTION.DROP_PACKETS;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (tmp_thr[0].equalsIgnoreCase(\"bin\")) {\n\t\t\t\tlast_minute_bin_limit = DataTypes.parseNum(tmp_thr[1], Long.class, LAST_MINUTE_BIN_LIMIT_PROP_VAL);\n\t\t\t\tlog.finest(getName() + \" last_minute_bin_limit = \" + last_minute_bin_limit);\n\t\t\t\ttotal_bin_limit = DataTypes.parseNum(tmp_thr[2], Long.class, TOTAL_BIN_LIMIT_PROP_VAL);\n\t\t\t\tlog.finest(getName() + \" total_bin_limit = \" + total_bin_limit);\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected void connectWaitingTasks() {\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.log(Level.FINER, \"Connecting waitingTasks: {0}\", new Object[]{waitingTasks});\n\t\t}\n\n\t\tfor (Map<String, Object> params : waitingTasks) {\n\t\t\treconnectService(params, connectionDelay);\n\t\t}\n\t\twaitingTasks.clear();\n\t\tif (null != watchdog && Thread.State.NEW.equals(watchdog.getState())) {\n\t\t\twatchdog.start();\n\t\t}\n\t\tdelayPortListening = false;\n\t\tportsConfigBean.start();\n\t}\n\n\tprotected void setupWatchdogThread() {\n\t\twatchdog = newWatchdog();\n\t\twatchdog.setName(\"Watchdog - \" + getName());\n\t\twatchdog.setDaemon(true);\n\t}\n\n\tpublic void setWatchdogPingType(WATCHDOG_PING_TYPE watchdogPingType) {\n\t\tthis.watchdogPingType = watchdogPingType;\n\t}\n\n\tprotected Watchdog newWatchdog() {\n\t\treturn new Watchdog();\n\t}\n\n\tprotected void addWaitingTask(Map<String, Object> conn) {\n\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.log(Level.FINER, \"Adding waiting task: {0}, started: {1}, delayPortListening: {2}, to: {3}\",\n\t\t\t\t\tnew Object[]{conn, started, delayPortListening, waitingTasks});\n\t\t}\n\n\t\tif (started && !delayPortListening) {\n\t\t\treconnectService(conn, connectionDelay);\n\t\t} else {\n\t\t\twaitingTasks.add(conn);\n\t\t}\n\t}\n\n\tprotected <T> void checkHighThroughputProperty(String ht_def_key, T ht_dev_val, String st_def_key, T st_def_val,\n\t\t\t\t\t\t\t\t\t\t\t\t   String prop_key, Class<T> prop_val_class, Map<String, Object> params,\n\t\t\t\t\t\t\t\t\t\t\t\t   Map<String, Object> props) {\n\t\tT tmp = st_def_val;\n\t\tString str_tmp = null;\n\n\t\tif (isHighThroughput()) {\n\t\t\ttmp = ht_dev_val;\n\t\t\tstr_tmp = (String) params.get(ht_def_key);\n\t\t} else {\n\t\t\ttmp = st_def_val;\n\t\t\tstr_tmp = (String) params.get(st_def_key);\n\t\t}\n\t\tif (tmp == null) {\n\t\t\ttmp = st_def_val;\n\t\t}\n\n\t\tif (str_tmp != null) {\n\t\t\tif (prop_val_class.isAssignableFrom(Integer.class)) {\n\t\t\t\ttmp = prop_val_class.cast(DataTypes.parseNum(str_tmp, Integer.class, (Integer) tmp));\n\t\t\t}\n\t\t\tif (prop_val_class.isAssignableFrom(Long.class)) {\n\t\t\t\ttmp = prop_val_class.cast(DataTypes.parseNum(str_tmp, Long.class, (Long) tmp));\n\t\t\t}\n\t\t\tif (prop_val_class.isAssignableFrom(String.class)) {\n\t\t\t\ttmp = prop_val_class.cast(str_tmp);\n\t\t\t}\n\t\t}\n\t\tprops.put(prop_key, tmp);\n\n\t}\n\n\t/**\n\t * Returns number of active network connections (IOServices).\n\t *\n\t * @return number of active network connections (IOServices).\n\t */\n\tprotected int countIOServices() {\n\t\treturn services.size();\n\t}\n\n\t/**\n\t * Perform a given action defined by ServiceChecker for all active IOService objects (active network connections).\n\t *\n\t * @param checker is a <code>ServiceChecker</code> instance defining an action to perform for all IOService\n\t * objects.\n\t */\n\tprotected void doForAllServices(ServiceChecker<IO> checker) {\n\t\tfor (IO service : services.values()) {\n\t\t\tchecker.check(service);\n\t\t}\n\t}\n\n\tprotected boolean writePacketToSocket(Packet p) {\n\t\tIO ios = getXMPPIOService(p);\n\n\t\tif (ios != null) {\n\t\t\treturn writePacketToSocket(ios, p);\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprotected boolean writePacketToSocket(Packet p, String serviceId) {\n\t\tIO ios = getXMPPIOService(serviceId);\n\n\t\tif (ios != null) {\n\t\t\treturn writePacketToSocket(ios, p);\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprotected void writeRawData(IO ios, String data) {\n\t\ttry {\n\t\t\tios.writeRawData(data);\n\t\t\tSocketThread.addSocketService(ios);\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Exception during writing data: \" + data + \" [\" + ios + \"]\", e);\n\t\t\ttry {\n\t\t\t\tios.stop();\n\t\t\t} catch (Exception e1) {\n\t\t\t\tlog.log(Level.WARNING, \"Exception stopping XMPPIOService [\" + ios + \"]\", e1);\n\t\t\t}    // end of try-catch\n\t\t}\n\t}\n\n\tprotected int[] getDefPlainPorts() {\n\t\treturn null;\n\t}\n\n\tprotected int[] getDefSSLPorts() {\n\t\treturn null;\n\t}\n\n\tprotected String getDefTrafficThrottling() {\n\t\tString result = ST_TRAFFIC_THROTTLING_PROP_VAL;\n\n\t\tif (isHighThroughput()) {\n\t\t\tresult = HT_TRAFFIC_THROTTLING_PROP_VAL;\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprotected abstract long getMaxInactiveTime();\n\t\n\tprotected Map<String, Object> getParamsForPort(int port) {\n\t\treturn null;\n\t}\n\n\tprotected String getServiceId(Packet packet) {\n\t\treturn getServiceId(packet.getTo());\n\t}\n\n\tprotected String getServiceId(JID jid) {\n\t\treturn jid.getResource();\n\t}\n\n\tprotected String getUniqueId(IO serv) {\n\t\treturn serv.getUniqueId();\n\t}\n\n\tprotected IO getXMPPIOService(Packet p) {\n\t\tString id = getServiceId(p);\n\n\t\tif (id != null) {\n\t\t\treturn services.get(id);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprotected abstract IO getXMPPIOServiceInstance();\n\n\tprotected boolean isHighThroughput() {\n\t\treturn false;\n\t}\n\n\tprotected void socketAccepted(IO serv, SocketType type) {\n\t}\n\n\tprotected void releaseListener(ConnectionOpenListener toStop) {\n\t\ttoStop.release();\n\t\tpending_open.remove(toStop);\n\t\tconnectThread.removeConnectionOpenListener(toStop);\n\t}\n\n\tprivate void reconnectService(final Map<String, Object> port_props, long delay) {\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tString cid = \"\" + port_props.get(\"local-hostname\") + \"@\" + port_props.get(\"remote-hostname\");\n\n\t\t\tlog.log(Level.FINER, \"Reconnecting service for: {0}, scheduling next try in {1} seconds, cid: {2}, props: {3}\",\n\t\t\t\t\tnew Object[]{getName(), delay / 1000, cid, port_props});\n\t\t}\n\t\taddTimerTask(new TimerTask() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tString host = (String) port_props.get(PORT_REMOTE_HOST_PROP_KEY);\n\n\t\t\t\tif (host == null) {\n\t\t\t\t\thost = (String) port_props.get(\"remote-hostname\");\n\t\t\t\t}\n\n\t\t\t\tint port = (Integer) port_props.get(PORT_KEY);\n\n\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\tlog.log(Level.FINE,\n\t\t\t\t\t\t\t\"Reconnecting service for component: {0}, to remote host: {1} on port: {2,number,#}\",\n\t\t\t\t\t\t\tnew Object[]{getName(), host, port});\n\t\t\t\t}\n\t\t\t\tstartService(port_props);\n\t\t\t}\n\t\t}, delay);\n\t}\n\n\tprivate void releaseListeners() {\n\t\tfor (ConnectionListenerImpl cli : pending_open) {\n\t\t\tconnectThread.removeConnectionOpenListener(cli);\n\t\t}\n\t\tpending_open.clear();\n\t}\n\n\tprotected ConnectionListenerImpl startService(Map<String, Object> port_props) {\n\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\tlog.log(Level.FINE, \"Starting service: {0}\", new Object[]{port_props});\n\t\t}\n\t\tif (port_props == null) {\n\t\t\tthrow new NullPointerException(\"port_props cannot be null.\");\n\t\t}\n\n\t\tConnectionListenerImpl cli = new ConnectionListenerImpl(port_props);\n\n\t\tif (cli.getConnectionType() == ConnectionType.accept) {\n\t\t\tpending_open.add(cli);\n\t\t}\n\t\tconnectThread.addConnectionOpenListener(cli);\n\t\treturn cli;\n\t}\n\n\tpublic static class PortConfigBean\n\t\t\timplements ConfigurationChangedAware, Initializable, UnregisterAware {\n\n\t\t@ConfigField(desc = \"Interface to listen on\")\n\t\tprotected String[] ifc = null;\n\t\t@ConfigField(desc = \"New connections throttling\", alias = \"new-connections-throttling\")\n\t\tprotected long newConnectionsThrottling = -1;\n\t\t@ConfigField(desc = \"Socket type\")\n\t\tprotected SocketType socket = SocketType.plain;\n\t\t@ConfigField(desc = \"Port type\")\n\t\tprotected ConnectionType type = ConnectionType.accept;\n\t\t@ConfigField(desc = \"Enable Proxy protocol support\")\n\t\tprotected boolean enableProxy = false;\n\t\t@Inject\n\t\tprivate ConnectionManager connectionManager;\n\t\tprivate ConnectionOpenListener connectionOpenListener = null;\n\t\t@ConfigField(desc = \"Port\")\n\t\tprivate Integer name;\n\n\t\tpublic PortConfigBean() {\n\n\t\t}\n\n\t\tpublic int getPort() {\n\t\t\treturn name;\n\t\t}\n\n\t\tpublic boolean isSecure() {\n\t\t\treturn socket == SocketType.tls || socket == SocketType.ssl;\n\t\t}\n\n\t\t@Override\n\t\tpublic void beanConfigurationChanged(Collection<String> changedFields) {\n\t\t\tif (connectionManager == null || !connectionManager.isInitializationComplete() ||\n\t\t\t\t\tconnectionManager.delayPortListening) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsynchronized (this) {\n\t\t\t\tif (connectionOpenListener != null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconnectionManager.releaseListener(connectionOpenListener);\n\t\t\t\t\t\tconnectionOpenListener = null;\n\t\t\t\t\t} catch (Throwable ex) {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Error while processing handshake\", ex);\n\t\t\t\t\t\tthrow ex;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"connectionManager: {0}, changedFields: {1}, props: {2}\",\n\t\t\t\t\t\t\tnew Object[]{connectionManager, changedFields, getProps()});\n\t\t\t\t}\n\n\t\t\t\tconnectionOpenListener = connectionManager.startService(getProps());\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void beforeUnregister() {\n\t\t\tsynchronized (this) {\n\t\t\t\tif (connectionOpenListener != null) {\n\t\t\t\t\tconnectionManager.releaseListener(connectionOpenListener);\n\t\t\t\t\tconnectionOpenListener = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void initialize() {\n\t\t\tif (newConnectionsThrottling == -1) {\n\t\t\t\tswitch (name) {\n\t\t\t\t\tcase 5223:\n\t\t\t\t\t\tnewConnectionsThrottling = ConnectionOpenThread.def_5223_throttling;\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 5269:\n\t\t\t\t\t\tnewConnectionsThrottling = ConnectionOpenThread.def_5269_throttling;\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase 5280:\n\t\t\t\t\t\tnewConnectionsThrottling = ConnectionOpenThread.def_5280_throttling;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tnewConnectionsThrottling = ConnectionOpenThread.def_5222_throttling;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tbeanConfigurationChanged(Collections.emptyList());\n\t\t}\n\n\t\tprotected Map<String, Object> getProps() {\n\t\t\tMap<String, Object> props = new HashMap<>();\n\t\t\tprops.put(PORT_KEY, name);\n\t\t\tprops.put(PORT_TYPE_PROP_KEY, type);\n\t\t\tprops.put(PORT_SOCKET_PROP_KEY, socket);\n\t\t\tif (ifc == null) {\n\t\t\t\tprops.put(PORT_IFC_PROP_KEY, connectionManager.PORT_IFC_PROP_VAL);\n\t\t\t} else {\n\t\t\t\tprops.put(PORT_IFC_PROP_KEY, ifc);\n\t\t\t}\n\t\t\tprops.put(PORT_REMOTE_HOST_PROP_KEY, PORT_REMOTE_HOST_PROP_VAL);\n\t\t\tprops.put(PORT_NEW_CONNECTIONS_THROTTLING_KEY, newConnectionsThrottling);\n//\t\t\tprops.put(TLS_REQUIRED_PROP_KEY, TLS_REQUIRED_PROP_VAL);\n\t\t\tprops.put(PORT_PROXY_PROTOCOL_PROP_KEY, enableProxy);\n\t\t\treturn props;\n\t\t}\n\t}\n\n\t@Bean(name = \"connections\", parent = ConnectionManager.class, active = true, exportable = true)\n\tpublic static class PortsConfigBean\n\t\t\timplements RegistrarBeanWithDefaultBeanClass, Initializable {\n\n\t\t@Inject\n\t\tprivate ConnectionManager connectionManager;\n\t\tprivate Kernel kernel;\n\t\t@ConfigField(desc = \"Ports to enable\", alias = \"ports\")\n\t\tprivate HashSet<Integer> ports;\n\t\t@Inject(nullAllowed = true)\n\t\tprivate PortConfigBean[] portsBeans;\n\n\t\tpublic PortsConfigBean() {\n\n\t\t}\n\n\t\tpublic PortConfigBean[] getPortsBeans() {\n\t\t\treturn portsBeans;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getDefaultBeanClass() {\n\t\t\treturn PortConfigBean.class;\n\t\t}\n\n\t\t@Override\n\t\tpublic void register(Kernel kernel) {\n\t\t\tthis.kernel = kernel;\n\t\t\tif (kernel.getParent() != null) {\n\t\t\t\tString connManagerBean = kernel.getParent().getName();\n\t\t\t\tthis.kernel.getParent().ln(\"service\", kernel, connManagerBean);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void unregister(Kernel kernel) {\n\t\t\tthis.kernel = null;\n\t\t}\n\n\t\t@Override\n\t\tpublic void initialize() {\n\t\t\tif (ports == null) {\n\t\t\t\tports = connectionManager.getDefPorts();\n\t\t\t}\n\n\t\t\tHashSet<Integer> sslPorts = new HashSet<>();\n\t\t\tif (connectionManager.getDefSSLPorts() != null) {\n\t\t\t\tfor (int port : connectionManager.getDefSSLPorts()) {\n\t\t\t\t\tsslPorts.add(port);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (Integer port : ports) {\n\t\t\t\tString name = String.valueOf(port);\n\t\t\t\tif (kernel.getDependencyManager().getBeanConfig(name) == null) {\n\t\t\t\t\tClass cls = sslPorts.contains(port.intValue()) ? SecPortConfigBean.class : PortConfigBean.class;\n\t\t\t\t\tkernel.registerBean(name).asClass(cls).exec();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic void start() {\n\t\t\tif (portsBeans != null) {\n\t\t\t\tArrays.stream(portsBeans).forEach(portBean -> portBean.initialize());\n\t\t\t}\n\t\t}\n\n\t\tpublic void stop() {\n\t\t\t// nothing to do for now\n\t\t}\n\n\t}\n\n\tpublic static class SecPortConfigBean\n\t\t\textends PortConfigBean {\n\n\t\tpublic SecPortConfigBean() {\n\t\t\tsocket = SocketType.ssl;\n\t\t}\n\n\t}\n\n\tprivate class ConnectionListenerImpl\n\t\t\timplements ConnectionOpenListener {\n\n\t\tprivate Map<String, Object> port_props = null;\n\n\t\tprivate ConnectionListenerImpl(Map<String, Object> port_props) {\n\t\t\tthis.port_props = port_props;\n\t\t}\n\n\t\t@Override\n\t\tpublic void accept(SocketChannel sc) {\n\t\t\tString cid = \"\" + port_props.get(\"local-hostname\") + \"@\" + port_props.get(\"remote-hostname\");\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Accept called for service: {0}, port_props: {1}\", new Object[]{cid, port_props});\n\t\t\t}\n\n\t\t\tif (!isAddressWhitelisted(sc.socket().getInetAddress().getHostAddress())) {\n\t\t\t\tlog.log(Level.FINE, \"Rejecting service: {0}, port_props: {1}, due to IP not being whitelisted: {2}\", new Object[]{cid, port_props, sc.socket().getInetAddress().getHostAddress()});\n\t\t\t\ttry {\n\t\t\t\t\tsc.close();\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Problem stopping service: {0}, port_props: {1}\", new Object[]{cid, port_props});\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tIO serv = getXMPPIOServiceInstance();\n\t\t\tserv.setSslContextContainer(sslContextContainer);\n\t\t\tserv.setBufferLimit(net_buffer_limit);\n\t\t\tserv.setCertificateContainer(certificateContainer);\n\n\t\t\t((XMPPDomBuilderHandler) serv.getSessionData().get(DOM_HANDLER)).setElementsLimit(elements_number_limit);\n\n\t\t\tserv.setIOServiceListener(ConnectionManager.this);\n\t\t\tserv.setSessionData(port_props);\n\t\t\ttry {\n\t\t\t\tserv.accept(sc, net_buffer);\n\t\t\t\tsocketAccepted(serv, getSocketType());\n\t\t\t\tif (getSocketType() == SocketType.ssl) {\n\t\t\t\t\tserv.startSSL(false, false, false);\n\t\t\t\t}    // end of if (socket == SocketType.ssl)\n\t\t\t\tserviceStarted(serv);\n\t\t\t\tSocketThread.addSocketService(serv);\n\t\t\t} catch (Exception e) {\n\t\t\t\tif (getConnectionType() == ConnectionType.connect) {\n\n\t\t\t\t\t// Accept side for component service is not ready yet?\n\t\t\t\t\t// Let's wait for a few secs and try again.\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tif (sc != null || !(e instanceof Exception)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\t\tString.format(\"Problem reconnecting the service: %1$s, port_props: %2$s\", serv,\n\t\t\t\t\t\t\t\t\t\t\t\t  port_props), e);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tupdateConnectionDetails(port_props);\n\n\t\t\t\t\tboolean reconnect = false;\n\t\t\t\t\tInteger reconnects = (Integer) port_props.get(MAX_RECONNECTS_PROP_KEY);\n\n\t\t\t\t\tif (reconnects != null) {\n\t\t\t\t\t\tint recon = reconnects.intValue();\n\n\t\t\t\t\t\tif (recon != 0) {\n\t\t\t\t\t\t\tport_props.put(MAX_RECONNECTS_PROP_KEY, (--recon));\n\t\t\t\t\t\t\treconnect = true;\n\t\t\t\t\t\t}    // end of if (recon != 0)\n\t\t\t\t\t}\n\t\t\t\t\tif (reconnect) {\n\t\t\t\t\t\treconnectService(port_props, connectionDelay);\n\t\t\t\t\t} else {\n\t\t\t\t\t\treconnectionFailed(port_props);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tserv.forceStop();\n\t\t\t\t}\n\t\t\t}          // end of try-catch\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn port_props.toString();\n\t\t}\n\n\t\t@Override\n\t\tpublic ConnectionType getConnectionType() {\n\t\t\tString type = null;\n\n\t\t\tif (port_props.get(PORT_TYPE_PROP_KEY) == null) {\n\t\t\t\tlog.warning(getName() + \": connection type is null: \" + port_props.get(PORT_KEY).toString());\n\t\t\t} else {\n\t\t\t\ttype = port_props.get(PORT_TYPE_PROP_KEY).toString();\n\t\t\t}\n\n\t\t\treturn ConnectionType.valueOf(type);\n\t\t}\n\n\t\t@Override\n\t\tpublic String[] getIfcs() {\n\t\t\treturn (String[]) port_props.get(PORT_IFC_PROP_KEY);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getPort() {\n\t\t\treturn (Integer) port_props.get(PORT_KEY);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getReceiveBufferSize() {\n\t\t\treturn socketBufferSize;\n\t\t}\n\n\t\t@Override\n\t\tpublic InetSocketAddress getRemoteAddress() {\n\t\t\treturn (InetSocketAddress) port_props.get(\"remote-address\");\n\t\t}\n\n\t\t@Override\n\t\tpublic String getRemoteHostname() {\n\t\t\tif (port_props.containsKey(PORT_REMOTE_HOST_PROP_KEY)) {\n\t\t\t\treturn (String) port_props.get(PORT_REMOTE_HOST_PROP_KEY);\n\t\t\t}\n\n\t\t\treturn (String) port_props.get(\"remote-hostname\");\n\t\t}\n\n\t\t@Override\n\t\tpublic SocketType getSocketType() {\n\t\t\treturn SocketType.valueOf(port_props.get(PORT_SOCKET_PROP_KEY).toString());\n\t\t}\n\n\t\t@Override\n\t\tpublic String getSRVType() {\n\t\t\tString type = (String) this.port_props.get(\"srv-type\");\n\n\t\t\tif ((type == null) || type.isEmpty()) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn type;\n\t\t}\n\n\t\t@Override\n\t\tpublic int getTrafficClass() {\n\t\t\tif (isHighThroughput()) {\n\t\t\t\treturn IPTOS_THROUGHPUT;\n\t\t\t} else {\n\t\t\t\treturn DEF_TRAFFIC_CLASS;\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic long getNewConnectionsThrottling() {\n\t\t\treturn (Long) port_props.getOrDefault(PORT_NEW_CONNECTIONS_THROTTLING_KEY,\n\t\t\t\t\t\t\t\t\t\t\t\t  ConnectionOpenThread.def_5222_throttling);\n\t\t}\n\n\t\t@Override\n\t\tpublic void release() {\n\t\t\tport_props.remove(MAX_RECONNECTS_PROP_KEY);\n\t\t}\n\t}\n\n\tprivate class IOServiceStatisticsGetter\n\t\t\timplements ServiceChecker<IO> {\n\n\t\tprivate StatisticsList list = new StatisticsList(Level.ALL);\n\n\t\t@Override\n\t\tpublic synchronized void check(IO service) {\n\t\t\tbytesReceived += service.getBytesReceived(true);\n\t\t\tbytesSent += service.getBytesSent(true);\n\t\t\tsocketOverflow += service.getBuffOverflow(true);\n\t\t\tservice.getPacketsReceived(true);\n\t\t\tservice.getPacketsSent(true);\n\n\t\t\t// service.getStatistics(list, true);\n\t\t\t// bytesReceived += list.getValue(\"socketio\", \"Bytes received\", -1l);\n\t\t\t// bytesSent += list.getValue(\"socketio\", \"Bytes sent\", -1l);\n\t\t\t// socketOverflow += list.getValue(\"socketio\", \"Buffers overflow\", -1l);\n\t\t}\n\t}\n\n\t/**\n\t * Class looks in all established connections and checks whether any of them is dead by performing either whitspace\n\t * or XMPP ping. If client fails to respond within defined time then the service is stopped.\n\t */\n\tprotected class Watchdog\n\t\t\textends Thread {\n\n\t\tPacket pingPacket;\n\t\tprivate boolean shutdown = false;\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\twhile (!shutdown) {\n\t\t\t\ttry {\n\n\t\t\t\t\t// Sleep...\n\t\t\t\t\tThread.sleep(watchdogDelay);\n\t\t\t\t\t++watchdogRuns;\n\n\t\t\t\t\texecuteWatchdog();\n\t\t\t\t} catch (InterruptedException e) {    /* Do nothing here */\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t\tpublic void shutdown() {\n\t\t\tshutdown = true;\n\t\t}\n\n\t\tprotected long getDurationSinceLastTransfer(final XMPPIOService service) {\n\t\t\tlong curr_time = System.currentTimeMillis();\n\t\t\tlong lastTransfer;\n\t\t\tswitch (watchdogPingType) {\n\t\t\t\tcase XMPP:\n\t\t\t\t\tlastTransfer = service.getLastXmppPacketReceiveTime();\n\t\t\t\t\tbreak;\n\t\t\t\tcase WHITESPACE:\n\t\t\t\tdefault:\n\t\t\t\t\tlastTransfer = service.getLastTransferTime();\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\treturn curr_time - lastTransfer;\n\t\t}\n\n\t\tprivate void executeWatchdog() {\n\t\t\t/** Walk through all connections and check whether they are really\n\t\t\t * alive. Depending on the configuration send either whitespace or\n\t\t\t * XMPP ping if the service is inactive for the configured period of\n\t\t\t * time\n\t\t\t */\n\t\t\tdoForAllServices(new ServiceChecker<IO>() {\n\t\t\t\t@Override\n\t\t\t\tpublic void check(final XMPPIOService service) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (null != service) {\n\t\t\t\t\t\t\tlong sinceLastTransfer = getDurationSinceLastTransfer(service);\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\t\t\t\"Testing service: {0}, sinceLastTransfer: {1}, maxInactivityTime: {2}, watchdogTimeout: {3}, watchdogDelay: {4}, watchdogPingType: {5} \",\n\t\t\t\t\t\t\t\t\t\tnew Object[]{service, sinceLastTransfer, maxInactivityTime,\n\t\t\t\t\t\t\t\t\t\t\t\t\t watchdogTimeout, watchdogDelay, watchdogPingType});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (sinceLastTransfer >= maxInactivityTime) {\n\n\t\t\t\t\t\t\t\t// Stop the service if max keep-alive time is exceeded\n\t\t\t\t\t\t\t\t// for non-active connections.\n\t\t\t\t\t\t\t\tif (log.isLoggable(Level.CONFIG)) {\n\t\t\t\t\t\t\t\t\tlog.log(Level.CONFIG,\n\t\t\t\t\t\t\t\t\t\t\t\"Max inactive time exceeded, stopping: {1} ( sinceLastTransfer: {2}, maxInactivityTime: {3}, watchdogTimeout: {4}, watchdogDelay: {5}, watchdogPingType: {6} ) [{0}]\",\n\t\t\t\t\t\t\t\t\t\t\tnew Object[]{getName(), service, sinceLastTransfer,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t maxInactivityTime, watchdogTimeout, watchdogDelay,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t watchdogPingType});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t++watchdogStopped;\n\t\t\t\t\t\t\t\tservice.forceStop();\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tif (sinceLastTransfer >= (watchdogTimeout)) {\n\n\t\t\t\t\t\t\t\t\t/** At least once every configured timings check if the\n\t\t\t\t\t\t\t\t\t * connection is still alive with the use of configured\n\t\t\t\t\t\t\t\t\t * ping type. */\n\t\t\t\t\t\t\t\t\tswitch (watchdogPingType) {\n\t\t\t\t\t\t\t\t\t\tcase XMPP:\n\t\t\t\t\t\t\t\t\t\t\tpingPacket = Iq.packetInstance(pingElement.clone(), JID.jidInstanceNS(\n\t\t\t\t\t\t\t\t\t\t\t\t\t(String) service.getSessionData().get(XMPPIOService.HOSTNAME_KEY)),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   JID.jidInstanceNS(service.getUserJid()));\n\t\t\t\t\t\t\t\t\t\t\tif (pingPacket.getStanzaFrom() != null && pingPacket.getStanzaTo() != null) {\n\t\t\t\t\t\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Sending XMPP ping {1} [{0}]\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew Object[]{service, pingPacket});\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\tif (!writePacketToSocket((IO) service, pingPacket)) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t// writing failed, stopp service\n\t\t\t\t\t\t\t\t\t\t\t\t\t++watchdogStopped;\n\t\t\t\t\t\t\t\t\t\t\t\t\tservice.forceStop();\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\t\tif (log.isLoggable(Level.WARNING)) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"Could not sent watchdog XMPP ping - missing to or from: {0}, service: {1} \",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew Object[]{pingPacket, String.valueOf(service)});\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\t\t\t\t\tcase WHITESPACE:\n\t\t\t\t\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Sending whitespace ping for service {0}\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew Object[]{service});\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tservice.writeRawData(\" \");\n\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t++watchdogTests;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (IOException e) {\n\n\t\t\t\t\t\t// Close the service\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tif (service != null) {\n\t\t\t\t\t\t\t\tlog.log(Level.CONFIG, getName() + \": Found dead connection, stopping: \" + service);\n\t\t\t\t\t\t\t\t++watchdogStopped;\n\t\t\t\t\t\t\t\tservice.forceStop();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (Exception ignore) {\n\t\t\t\t\t\t\t// Do nothing here as we expect Exception to be thrown here...\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate static class ServiceConnectedTimer<IO extends XMPPIOService> extends TimerTask {\n\n\t\tprivate final IO service;\n\n\t\tprivate ServiceConnectedTimer(final IO service) {\n\t\t\tthis.service = service;\n\t\t\tservice.getSessionData().put(\"ServiceConnectedTimer\", this);\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tservice.forceStop();\n\t\t}\n\n\t\t@Override\n\t\tpublic void cancel(boolean mayInterruptIfRunning) {\n\t\t\tservice.getSessionData().remove(\"ServiceConnectedTimer\");\n\t\t\tsuper.cancel(mayInterruptIfRunning);\n\t\t}\n\n\t\tpublic static void cancel(XMPPIOService service) {\n\t\t\tServiceConnectedTimer timer = (ServiceConnectedTimer) service.getSessionData().get(\"ServiceConnectedTimer\");\n\t\t\tif (timer != null) {\n\t\t\t\ttimer.cancel();\n\t\t\t} else {\n\t\t\t\tlog.log(Level.FINEST, \"Missing service connected timer task: {0}\", service);\n\t\t\t}\n\t\t}\n\t}\n}    // ConnectionManager\n\n"
  },
  {
    "path": "src/main/java/tigase/server/DataForm.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport tigase.server.Command.DataType;\nimport tigase.xml.Element;\nimport tigase.xml.XMLUtils;\n\nimport java.util.*;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.logging.Logger;\n\n/**\n * @author Wojciech Kapcia\n */\npublic class DataForm {\n\n\tpublic static final String FIELD_EL = \"field\";\n\tpublic static final String VALUE_EL = \"value\";\n\tpublic static final String FORM_TYPE = \"FORM_TYPE\";\n\tprotected static final String[] FIELD_VALUE_PATH = {FIELD_EL, VALUE_EL};\n\tprivate static final Logger log = Logger.getLogger(DataForm.class.getName());\n\n\tpublic enum FieldType {\n\t\tBoolean,\n\t\tFixed,\n\t\tHidden,\n\t\tJidMulti,\n\t\tJidSingle,\n\t\tListMulti,\n\t\tListSingle,\n\t\tTextMulti,\n\t\tTextPrivate,\n\t\tTextSingle;\n\n\t\tpublic String value() {\n\t\t\tswitch (this) {\n\t\t\t\tcase Boolean:\n\t\t\t\t\treturn \"boolean\";\n\t\t\t\tcase Fixed:\n\t\t\t\t\treturn \"fixed\";\n\t\t\t\tcase Hidden:\n\t\t\t\t\treturn \"hidden\";\n\t\t\t\tcase JidMulti:\n\t\t\t\t\treturn \"jid-multi\";\n\t\t\t\tcase JidSingle:\n\t\t\t\t\treturn \"jid-single\";\n\t\t\t\tcase ListMulti:\n\t\t\t\t\treturn \"list-multi\";\n\t\t\t\tcase ListSingle:\n\t\t\t\t\treturn \"list-single\";\n\t\t\t\tcase TextMulti:\n\t\t\t\t\treturn \"text-multi\";\n\t\t\t\tcase TextPrivate:\n\t\t\t\t\treturn \"text-private\";\n\t\t\t\tcase TextSingle:\n\t\t\t\t\treturn \"text-single\";\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Data form-types as defined in the XEP-0050.\n\t */\n\n\tpublic static void addCheckBoxField(final Element el, String f_name, boolean f_value) {\n\t\tDataForm.addFieldValue(el, f_name, Boolean.toString(f_value), \"boolean\");\n\t}\n\n\tpublic static Element addDataForm(Element el, DataType data_type) {\n\t\tElement x = createDataForm(data_type);\n\n\t\tel.addChild(x);\n\n\t\treturn x;\n\t}\n\n\tpublic static void addField(final Element el, final String f_name, final String f_label, final String type) {\n\t\tElement x = getXElement(el);\n\n\t\tElement field = new Element(FIELD_EL, new String[]{\"var\", \"type\", \"label\"},\n\t\t\t\t\t\t\t\t\tnew String[]{XMLUtils.escape(f_name), type, f_label});\n\n\t\tx.addChild(field);\n\t}\n\n\tpublic static void addFieldMultiValue(final Element el, final String f_name, final List<String> f_value) {\n\t\taddFieldMultiValue(el, f_name, f_value, null);\n\t}\n\n\tpublic static void addFieldMultiValue(final Element el, final String f_name, final List<String> f_value,\n\t\t\t\t\t\t\t\t\t\t  final String label) {\n\t\taddFieldMultiValue(el, f_name, f_value, label, \"text-multi\");\n\t}\n\n\tpublic static void addFieldListMultiValue(final Element el, final String f_name, final List<String> f_value) {\n\t\taddFieldMultiValue(el, f_name, f_value, null, \"list-multi\");\n\t}\n\n\tpublic static void addFieldListMultiValue(final Element el, final String f_name, final List<String> f_value,\n\t\t\t\t\t\t\t\t\t\t\t  final String label) {\n\t\taddFieldMultiValue(el, f_name, f_value, label, \"list-multi\");\n\t}\n\n\tpublic static void addFieldMultiValue(final Element el, final String f_name, final List<String> f_value,\n\t\t\t\t\t\t\t\t\t\t  final String label, final String type) {\n\t\tElement x = getXElement(el);\n\n\t\tif (x == null) {\n\t\t\tx = addDataForm(el, DataType.result);\n\t\t}\n\t\tif (f_value != null) {\n\t\t\tElement field = new Element(FIELD_EL, new String[]{\"var\", \"type\"},\n\t\t\t\t\t\t\t\t\t\tnew String[]{XMLUtils.escape(f_name), type});\n\n\t\t\tif (label != null) {\n\t\t\t\tfield.addAttribute(\"label\", label);\n\t\t\t}\n\n\t\t\tfor (String val : f_value) {\n\t\t\t\tif (val != null) {\n\t\t\t\t\tElement value = new Element(VALUE_EL, XMLUtils.escape(val));\n\n\t\t\t\t\tfield.addChild(value);\n\t\t\t\t}\n\t\t\t}\n\t\t\tx.addChild(field);\n\t\t}\n\t}\n\n\tpublic static void addFieldMultiValue(final Element el, final String f_name, final Throwable ex) {\n\t\tElement x = getXElement(el);\n\n\t\tList<String> f_value = null;\n\n\t\tif (ex != null) {\n\t\t\tf_value = new ArrayList<String>(100);\n\t\t\tf_value.add(ex.getLocalizedMessage());\n\t\t\tfor (StackTraceElement ste : ex.getStackTrace()) {\n\t\t\t\tf_value.add(\"  \" + ste.toString());\n\t\t\t}\n\t\t}\n\t\tif (f_value != null) {\n\t\t\tElement field = new Element(FIELD_EL, new String[]{\"var\", \"type\"},\n\t\t\t\t\t\t\t\t\t\tnew String[]{XMLUtils.escape(f_name), \"text-multi\"});\n\n\t\t\tfor (String val : f_value) {\n\t\t\t\tif (val != null) {\n\t\t\t\t\tElement value = new Element(VALUE_EL, XMLUtils.escape(val));\n\n\t\t\t\t\tfield.addChild(value);\n\t\t\t\t}\n\t\t\t}\n\t\t\tx.addChild(field);\n\t\t}\n\t}\n\n\tpublic static void addFieldValue(final Element el, final String f_name, final String f_value) {\n\t\tElement x = getXElement(el);\n\n\t\tElement field = new Element(FIELD_EL, new Element[]{new Element(VALUE_EL, XMLUtils.escape(f_value))},\n\t\t\t\t\t\t\t\t\tnew String[]{\"var\"}, new String[]{XMLUtils.escape(f_name)});\n\n\t\tx.addChild(field);\n\t}\n\n\tpublic static void addFieldValue(final Element el, final String f_name, final String f_value, final String label,\n\t\t\t\t\t\t\t\t\t final String[] labels, final String[] options) {\n\t\tElement x = getXElement(el);\n\n\t\tElement field = new Element(FIELD_EL, new Element[]{new Element(VALUE_EL, XMLUtils.escape(f_value))},\n\t\t\t\t\t\t\t\t\tnew String[]{\"var\", \"type\", \"label\"},\n\t\t\t\t\t\t\t\t\tnew String[]{XMLUtils.escape(f_name), \"list-single\", XMLUtils.escape(label)});\n\n\t\taddOptions(labels, options, x, field);\n\t}\n\n\tprivate static void addOptions(String[] labels, String[] options, Element x, Element field) {\n\t\tfor (int i = 0; i < labels.length; i++) {\n\t\t\tfield.addChild(new Element(\"option\", new Element[]{new Element(VALUE_EL, XMLUtils.escape(options[i]))},\n\t\t\t\t\t\t\t\t\t   new String[]{\"label\"}, new String[]{XMLUtils.escape(labels[i])}));\n\t\t}\n\t\tx.addChild(field);\n\t}\n\n\tpublic static void addFieldValue(final Element el, final String f_name, final String[] f_values, final String label,\n\t\t\t\t\t\t\t\t\t final String[] labels, final String[] options) {\n\t\tElement x = getXElement(el);\n\n\t\tElement field = new Element(FIELD_EL, new String[]{\"var\", \"type\", \"label\"},\n\t\t\t\t\t\t\t\t\tnew String[]{XMLUtils.escape(f_name), \"list-multi\", XMLUtils.escape(label)});\n\n\t\tfor (int i = 0; i < labels.length; i++) {\n\t\t\tfield.addChild(new Element(\"option\", new Element[]{new Element(VALUE_EL, XMLUtils.escape(options[i]))},\n\t\t\t\t\t\t\t\t\t   new String[]{\"label\"}, new String[]{XMLUtils.escape(labels[i])}));\n\t\t}\n\t\tfor (int i = 0; i < f_values.length; i++) {\n\t\t\tfield.addChild(new Element(VALUE_EL, XMLUtils.escape(f_values[i])));\n\t\t}\n\t\tx.addChild(field);\n\t}\n\n\tpublic static void addFieldValue(final Element el, final String f_name, final String f_value, final String label,\n\t\t\t\t\t\t\t\t\t final String[] labels, final String[] options, final String type) {\n\t\tElement x = getXElement(el);\n\n\t\tElement field = new Element(FIELD_EL, new Element[]{new Element(VALUE_EL, XMLUtils.escape(f_value))},\n\t\t\t\t\t\t\t\t\tnew String[]{\"var\", \"type\", \"label\"},\n\t\t\t\t\t\t\t\t\tnew String[]{XMLUtils.escape(f_name), type, XMLUtils.escape(label)});\n\n\t\taddOptions(labels, options, x, field);\n\t}\n\n\tprivate static Element getXElement(Element el) {\n\t\tif (el != null) {\n\t\t\tif (\"x\".equals(el.getName()) && \"jabber:x:data\".equals(el.getXMLNS())) {\n\t\t\t\treturn el;\n\t\t\t} else {\n\t\t\t\tElement x = el.getChild(\"x\", \"jabber:x:data\");\n\n\t\t\t\tif (x == null) {\n\t\t\t\t\tx = addDataForm(el, DataType.submit);\n\t\t\t\t}\n\t\t\t\treturn x;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static void addFieldValue(final Element el, final String f_name, final String f_value, final String type) {\n\t\tElement x = getXElement(el);\n\n\t\tElement field = new Element(FIELD_EL, new Element[]{new Element(VALUE_EL, XMLUtils.escape(f_value))},\n\t\t\t\t\t\t\t\t\tnew String[]{\"var\", \"type\"}, new String[]{XMLUtils.escape(f_name), type});\n\n\t\tx.addChild(field);\n\t}\n\n\tpublic static void addFieldValue(final Element el, final String f_name, final String f_value, final String type,\n\t\t\t\t\t\t\t\t\t final String label) {\n\t\tElement x = getXElement(el);\n\n\t\tElement field = new Element(FIELD_EL, new Element[]{new Element(VALUE_EL, XMLUtils.escape(f_value))},\n\t\t\t\t\t\t\t\t\tnew String[]{\"var\", \"type\", \"label\"},\n\t\t\t\t\t\t\t\t\tnew String[]{XMLUtils.escape(f_name), type, XMLUtils.escape(label)});\n\n\t\tx.addChild(field);\n\t}\n\n\tpublic static void addHiddenField(final Element el, String f_name, String f_value) {\n\t\taddFieldValue(el, f_name, f_value, \"hidden\");\n\t}\n\n\tpublic static void addInstructions(final Element el, final String instructions) {\n\t\tElement x = getXElement(el);\n\t\tx.addChild(new Element(\"instructions\", instructions));\n\t}\n\n\tpublic static void addTextField(final Element el, String f_name, String f_value) {\n\t\taddFieldValue(el, f_name, f_value, \"fixed\");\n\t}\n\n\tpublic static void addTitle(final Element el, final String title) {\n\t\tElement x = getXElement(el);\n\t\tx.addChild(new Element(\"title\", title));\n\t}\n\n\tpublic static Element createDataForm(DataType data_type) {\n\t\treturn new Element(\"x\", new String[]{\"xmlns\", \"type\"}, new String[]{\"jabber:x:data\", data_type.name()});\n\t}\n\n\tpublic static String getFieldKeyStartingWith(final Element el, String f_name) {\n\t\tElement x = getXElement(el);\n\n\t\tif (x != null) {\n\t\t\tList<Element> children = x.getChildren();\n\n\t\t\tif (children != null) {\n\t\t\t\tfor (Element child : children) {\n\t\t\t\t\tif (child.getName().equals(FIELD_EL) && child.getAttributeStaticStr(\"var\").startsWith(f_name)) {\n\t\t\t\t\t\treturn child.getAttributeStaticStr(\"var\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic static String getFieldValue(final Element el, String f_name) {\n\t\tElement x = getXElement(el);\n\n\t\tif (x != null) {\n\t\t\tList<Element> children = x.getChildren();\n\n\t\t\tif (children != null) {\n\t\t\t\tfor (Element child : children) {\n\t\t\t\t\tif (child.getName().equals(FIELD_EL) && child.getAttributeStaticStr(\"var\").equals(f_name)) {\n\t\t\t\t\t\tString value = child.getChildCDataStaticStr(FIELD_VALUE_PATH);\n\n\t\t\t\t\t\tif (value != null) {\n\t\t\t\t\t\t\treturn XMLUtils.unescape(value);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic static boolean getFieldBoolValue(final Element el, final String f_name) {\n\t\tString value = getFieldValue(el, f_name);\n\t\treturn \"true\".equals(value) || \"1\".equals(value);\n\t}\n\n\tpublic static String[] getFieldValues(final Element el, final String f_name) {\n\t\tElement x = getXElement(el);\n\n\t\tif (x != null) {\n\t\t\tList<Element> children = x.getChildren();\n\n\t\t\tif (children != null) {\n\t\t\t\tfor (Element child : children) {\n\t\t\t\t\tif (child.getName().equals(FIELD_EL) && child.getAttributeStaticStr(\"var\").equals(f_name)) {\n\t\t\t\t\t\tList<String> values = new LinkedList<String>();\n\t\t\t\t\t\tList<Element> val_children = child.getChildren();\n\n\t\t\t\t\t\tif (val_children != null) {\n\t\t\t\t\t\t\tfor (Element val_child : val_children) {\n\t\t\t\t\t\t\t\tif (val_child.getName().equals(VALUE_EL)) {\n\t\t\t\t\t\t\t\t\tString value = val_child.getCData();\n\n\t\t\t\t\t\t\t\t\tif (value != null) {\n\t\t\t\t\t\t\t\t\t\tvalues.add(XMLUtils.unescape(value));\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn values.toArray(new String[0]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic static Set<String> getFields(Element el) {\n\t\tElement x = getXElement(el);\n\n\t\tif (x != null) {\n\t\t\tList<Element> children = x.getChildren();\n\t\t\tSet<String> set = new HashSet<>();\n\t\t\tfor (Element child : children) {\n\t\t\t\tString varName = child.getAttributeStaticStr(\"var\");\n\t\t\t\tif (varName != null) {\n\t\t\t\t\tif (!varName.equals(FORM_TYPE)) {\n\t\t\t\t\t\tset.add(varName);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn set;\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static String getFormType(Element form) {\n\t\treturn getFieldValue(form, FORM_TYPE);\n\t}\n\n\tpublic static boolean removeFieldValue(final Element el, final String f_name) {\n\t\tElement x = getXElement(el);\n\n\t\tif (x != null) {\n\t\t\tList<Element> children = x.getChildren();\n\n\t\t\tif (children != null) {\n\t\t\t\tfor (Element child : children) {\n\t\t\t\t\tif (child.getName().equals(FIELD_EL) && child.getAttributeStaticStr(\"var\").equals(f_name)) {\n\t\t\t\t\t\treturn x.removeChild(child);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tpublic static class Builder {\n\n\t\tprivate final Element x;\n\n\t\tprivate static Element createDataEl(Element parent) {\n\t\t\tElement dataEl = new Element(\"x\");\n\t\t\tdataEl.setXMLNS(\"jabber:x:data\");\n\t\t\tparent.addChild(dataEl);\n\t\t\treturn dataEl;\n\t\t}\n\n\t\tpublic Builder(DataType type) {\n\t\t\tx = new Element(\"x\");\n\t\t\tx.setXMLNS(\"jabber:x:data\");\n\t\t\tx.setAttribute(\"type\", type.name());\n\t\t}\n\n\t\tpublic Builder(Element parent, DataType type) {\n\t\t\tx = Optional.ofNullable(parent.getChild(\"x\", \"jabber:x:data\")).orElseGet(() -> createDataEl(parent));\n\t\t\tx.setAttribute(\"type\", type.name());\n\t\t}\n\n\t\tpublic Builder addTitle(String title) {\n\t\t\tElement old;\n\t\t\twhile ((old = x.getChild(\"title\")) != null) {\n\t\t\t\tx.removeChild(old);\n\t\t\t}\n\t\t\tif (title != null) {\n\t\t\t\tx.addChild(new Element(\"title\", title));\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder addInstructions(String[] instructions) {\n\t\t\tList<Element> oldValues = x.mapChildren(el -> el.getName() == \"instructions\", Function.identity());\n\t\t\tif (oldValues != null) {\n\t\t\t\tfor (Element oldValue : oldValues) {\n\t\t\t\t\tx.removeChild(oldValue);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (instructions != null) {\n\t\t\t\tfor (String instruction : instructions) {\n\t\t\t\t\tx.addChild(new Element(\"instructions\", instruction));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Field.Builder addField(FieldType type, String var) {\n\t\t\treturn new Field.Builder(x, type, var);\n\t\t}\n\n\t\tpublic Builder withFields(Consumer<Builder> consumer) {\n\t\t\tconsumer.accept(this);\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder withField(FieldType type, String var, Consumer<Field.Builder> consumer) {\n\t\t\tField.Builder builder = addField(type, var);\n\t\t\tconsumer.accept(builder);\n\t\t\tbuilder.build();\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder withReported(Consumer<Reported.Builder> consumer) {\n\t\t\tReported.Builder builder = new Reported.Builder(x);\n\t\t\tconsumer.accept(builder);\n\t\t\tbuilder.build();\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder withItem(Consumer<Item.Builder> consumer) {\n\t\t\tItem.Builder builder = new Item.Builder(x);\n\t\t\tconsumer.accept(builder);\n\t\t\tbuilder.build();\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Element build() {\n\t\t\treturn x;\n\t\t}\n\t}\n\n\tpublic static class Field {\n\n\t\tpublic static class Builder {\n\n\t\t\tprivate final Element el;\n\t\t\tprivate final Element parent;\n\t\t\tprivate final FieldType type;\n\n\t\t\tpublic Builder(Element form, FieldType type, String var) {\n\t\t\t\tthis.parent = form;\n\t\t\t\tthis.type = Optional.ofNullable(type).orElse(FieldType.TextSingle);\n\t\t\t\tthis.el = new Element(\"field\");\n\t\t\t\tel.setAttribute(\"var\", var);\n\t\t\t\tif (type != null) {\n\t\t\t\t\tthis.el.setAttribute(\"type\", type.value());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpublic Builder setLabel(String label) {\n\t\t\t\tif (label == null) {\n\t\t\t\t\tel.removeAttribute(\"label\");\n\t\t\t\t} else {\n\t\t\t\t\tel.setAttribute(\"label\", label);\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tpublic Builder setDesc(String desc) {\n\t\t\t\tremoveChildren(\"desc\");\n\t\t\t\tif (desc != null) {\n\t\t\t\t\tel.addChild(new Element(\"desc\", desc));\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tpublic Builder setRequired(boolean required) {\n\t\t\t\tremoveChildren(\"required\");\n\t\t\t\tif (required) {\n\t\t\t\t\tel.addChild(new Element(\"required\"));\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tpublic Builder addOption(String value) {\n\t\t\t\tthis.addOption(value, null);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tpublic Builder addOption(String value, String label) {\n\t\t\t\tswitch (type) {\n\t\t\t\t\tcase ListMulti:\n\t\t\t\t\tcase ListSingle:\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthrow new UnsupportedOperationException(\"Invalid field type!\");\n\t\t\t\t}\n\t\t\t\tElement option = new Element(\"option\");\n\t\t\t\tif (label != null) {\n\t\t\t\t\toption.setAttribute(\"label\", label);\n\t\t\t\t}\n\t\t\t\toption.addChild(new Element(\"value\", value));\n\t\t\t\tel.addChild(option);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tpublic Builder setOptions(String[] values) {\n\t\t\t\treturn setOptions(values, null);\n\t\t\t}\n\n\t\t\tpublic Builder setOptions(String[] values, String[] labels) {\n\t\t\t\tfor (int i = 0; i < values.length; i++) {\n\t\t\t\t\taddOption(values[i], labels == null ? null : labels[i]);\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tpublic Builder setValue(Boolean value) {\n\t\t\t\treturn setValue(value == null ? null : (value ? \"true\" : \"false\"));\n\t\t\t}\n\n\t\t\tpublic Builder setValue(String value) {\n\t\t\t\tswitch (type) {\n\t\t\t\t\tcase JidMulti:\n\t\t\t\t\tcase ListMulti:\n\t\t\t\t\tcase TextMulti:\n\t\t\t\t\t\tthrow new UnsupportedOperationException(\"Invalid field type!\");\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tremoveOldValues();\n\t\t\t\tif (value != null) {\n\t\t\t\t\tel.addChild(new Element(\"value\", value));\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tpublic Builder setValues(String[] values) {\n\t\t\t\tswitch (type) {\n\t\t\t\t\tcase Boolean:\n\t\t\t\t\tcase Fixed:\n\t\t\t\t\tcase Hidden:\n\t\t\t\t\tcase JidSingle:\n\t\t\t\t\tcase ListSingle:\n\t\t\t\t\tcase TextSingle:\n\t\t\t\t\tcase TextPrivate:\n\t\t\t\t\t\tthrow new UnsupportedOperationException(\"Invalid field type!\");\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tremoveOldValues();\n\t\t\t\tif (values != null) {\n\t\t\t\t\tfor (String value : values) {\n\t\t\t\t\t\tel.addChild(new Element(\"value\", value));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tpublic Element build() {\n\t\t\t\tif (parent != null) {\n\t\t\t\t\tparent.addChild(el);\n\t\t\t\t}\n\t\t\t\treturn el;\n\t\t\t}\n\n\t\t\tprivate void removeOldValues() {\n\t\t\t\tremoveChildren(FIELD_EL);\n\t\t\t}\n\n\t\t\tprivate void removeChildren(String name) {\n\t\t\t\tList<Element> oldValues = el.mapChildren(el -> el.getName() == name, Function.identity());\n\t\t\t\tif (oldValues != null) {\n\t\t\t\t\tfor (Element oldValue : oldValues) {\n\t\t\t\t\t\tel.removeChild(oldValue);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tpublic static class Item {\n\n\t\tpublic static class Builder {\n\n\t\t\tprivate final Element item;\n\t\t\tprivate final Element x;\n\n\t\t\tpublic Builder(Element x) {\n\t\t\t\tthis.x = x;\n\t\t\t\tthis.item = new Element(\"item\");\n\t\t\t}\n\n\t\t\tpublic Field.Builder addField(String var) {\n\t\t\t\treturn new Field.Builder(item, null, var);\n\t\t\t}\n\n\t\t\tpublic Builder withFields(Consumer<Item.Builder> consumer) {\n\t\t\t\tconsumer.accept(this);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tpublic Element build() {\n\t\t\t\tif (x != null) {\n\t\t\t\t\tx.addChild(item);\n\t\t\t\t}\n\t\t\t\treturn item;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static class Reported {\n\n\t\tpublic static class Builder {\n\n\t\t\tprivate final Element reported;\n\t\t\tprivate final Element x;\n\n\t\t\tpublic Builder(Element x) {\n\t\t\t\tthis.x = x;\n\t\t\t\tthis.reported = new Element(\"reported\");\n\t\t\t}\n\n\t\t\tpublic Field.Builder addField(FieldType type, String var) {\n\t\t\t\treturn new Field.Builder(reported, type, var);\n\t\t\t}\n\n\t\t\tpublic Builder withFields(Consumer<Reported.Builder> consumer) {\n\t\t\t\tconsumer.accept(this);\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\tpublic Element build() {\n\t\t\t\tif (x != null) {\n\t\t\t\t\tx.addChild(reported);\n\t\t\t\t}\n\t\t\t\treturn reported;\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/DisableDisco.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\n/**\n * Disables service discovery processing for a component implementing this interface.\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n * @version 13/03/04\n */\npublic interface DisableDisco {\n\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/Iq.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport tigase.disco.XMPPService;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.impl.roster.RosterAbstract;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.List;\n\n/**\n * Created: Dec 31, 2009 8:43:21 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class Iq\n\t\textends Packet {\n\n\tpublic static final String ELEM_NAME = \"iq\";\n\n\tpublic static final String QUERY_NAME = \"query\";\n\n\tpublic static final String[] IQ_QUERY_PATH = {ELEM_NAME, QUERY_NAME};\n\n\tpublic static final String[] IQ_PUBSUB_PATH = {ELEM_NAME, \"pubsub\"};\n\n\tpublic static final String[] IQ_ERROR_PATH = {ELEM_NAME, \"error\"};\n\n\tpublic static final String[] IQ_COMMAND_PATH = {ELEM_NAME, \"command\"};\n\n\tpublic static final String[] IQ_CHAT_PATH = {ELEM_NAME, \"chat\"};\n\n\tpublic static final String[] IQ_BIND_RESOURCE_PATH = {ELEM_NAME, \"bind\", \"resource\"};\n\n\tpublic static final String[] IQ_BIND_PATH = {ELEM_NAME, \"bind\"};\n\n\tprivate boolean cmd = false;\n\tprivate Command command = null;\n\tprivate String iqQueryXMLNS = null;\n\tprivate boolean serviceDisco = false;\n\tprivate String strCommand = null;\n\n\tpublic static Packet commandResultForm(Iq packet) throws TigaseStringprepException {\n\t\tPacket result = packet.commandResult(Command.DataType.form);\n\n\t\treturn result;\n\t}\n\n\tpublic static Packet commandResultResult(Iq packet) throws TigaseStringprepException {\n\t\tPacket result = packet.commandResult(Command.DataType.result);\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Method creates a new <code>Packet</code> instance or <code>Iq</code> instance more specificly with a\n\t * roster entry content. TODO: Remove dependency on RosterAbstract class, possibly move the method again to more\n\t * proper location but it needs to be accessible from all parts of the application.\n\t *\n\t * @param iq_type is a <code>String</code> value with the stanza type: 'set', 'get', 'result'.\n\t * @param iq_id is a <code>String</code> value with the stanza unique id.\n\t * @param from is a <code>JID</code> instance with the packet source address.\n\t * @param to is a <code>JID</code> instance with the packet destination address.\n\t * @param item_jid is a <code>JID</code> instance with the roster item JID, note in most cases the jid should not\n\t * have a resource part, but this method does not cut it off. This is because there are cases when we want to have a\n\t * resource part in the roster item.\n\t * @param item_name is a <code>String</code> vakue with the roster item name.\n\t * @param item_groups is a <code>String[]</code> array with all groups the item belongs to.\n\t * @param subscription is a <code>String</code> instance with the item subscription state.\n\t * @param item_type is a <code>String</code> of the user item type. This is <code>null</code> in most cases as this\n\t * is not part of the XMPP RFC. Some deployments needs some extra information about the roster item type though.\n\t *\n\t * @return a new <code>Packet</code> instance or <code>Iq</code> instance more specificly with a roster entry\n\t * content.\n\t */\n\tpublic static Iq createRosterPacket(String iq_type, String iq_id, JID from, JID to, JID item_jid, String item_name,\n\t\t\t\t\t\t\t\t\t\tString[] item_groups, String subscription, String item_type) {\n\t\tElement iq = new Element(\"iq\", new String[]{\"type\", \"id\"}, new String[]{iq_type, iq_id});\n\n\t\tiq.setXMLNS(CLIENT_XMLNS);\n\t\tif (from != null) {\n\t\t\tiq.addAttribute(\"from\", from.toString());\n\t\t}\n\t\tif (to != null) {\n\t\t\tiq.addAttribute(\"to\", to.toString());\n\t\t}\n\n\t\tElement query = new Element(\"query\");\n\n\t\tquery.setXMLNS(RosterAbstract.XMLNS);\n\t\tiq.addChild(query);\n\n\t\tElement item = new Element(\"item\", new String[]{\"jid\"}, new String[]{item_jid.toString()});\n\n\t\tif (item_type != null) {\n\t\t\titem.addAttribute(\"type\", item_type);\n\t\t}\n\t\tif (item_name != null) {\n\t\t\titem.addAttribute(RosterAbstract.NAME, item_name);\n\t\t}\n\t\tif (subscription != null) {\n\t\t\titem.addAttribute(RosterAbstract.SUBSCRIPTION, subscription);\n\t\t}\n\t\tif (item_groups != null) {\n\t\t\tfor (String gr : item_groups) {\n\t\t\t\tElement group = new Element(RosterAbstract.GROUP, gr);\n\n\t\t\t\titem.addChild(group);\n\t\t\t}\n\t\t}\n\t\tquery.addChild(item);\n\n\t\treturn new Iq(iq, from, to);\n\t}\n\n\tpublic Iq(Element elem) throws TigaseStringprepException {\n\t\tsuper(elem);\n\t\tinit();\n\t}\n\n\tpublic Iq(Element elem, JID stanzaFrom, JID stanzaTo) {\n\t\tsuper(elem, stanzaFrom, stanzaTo);\n\t\tinit();\n\t}\n\n\tpublic Packet commandResult(Command.DataType cmd_type) {\n\t\treturn okResult(command.createCommandEl(strCommand, cmd_type), 0);\n\t}\n\n\t@Override\n\tpublic Command getCommand() {\n\t\treturn command;\n\t}\n\n\tpublic String getIQChildName() {\n\t\tList<Element> children = elem.getChildren();\n\n\t\tif ((children != null) && (children.size() > 0)) {\n\t\t\treturn children.get(0).getName();\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic String getIQXMLNS() {\n\t\tif (iqQueryXMLNS == null) {\n\t\t\tiqQueryXMLNS = elem.getXMLNSStaticStr(IQ_QUERY_PATH);\n\t\t}\n\n\t\treturn iqQueryXMLNS;\n\t}\n\n\tpublic String getStrCommand() {\n\t\treturn strCommand;\n\t}\n\n\t@Override\n\tpublic boolean isCommand() {\n\t\treturn cmd;\n\t}\n\n\t@Override\n\tpublic boolean isServiceDisco() {\n\t\treturn serviceDisco;\n\t}\n\n\t@Override\n\tprotected String[] getElNameErrorPath() {\n\t\treturn IQ_ERROR_PATH;\n\t}\n\n\tprivate void init() {\n\t\tElement child = elem.getChild(\"command\", Command.XMLNS);\n\n\t\tif (child != null) {\n\t\t\tcmd = true;\n\t\t\tstrCommand = child.getAttributeStaticStr(\"node\");\n\t\t\tcommand = Command.valueof(strCommand);\n\t\t}\n\t\tserviceDisco = (isXMLNSStaticStr(IQ_QUERY_PATH, XMPPService.INFO_XMLNS) ||\n\t\t\t\tisXMLNSStaticStr(IQ_QUERY_PATH, XMPPService.ITEMS_XMLNS));\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/Lifecycle.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\n/**\n * Generic inteface which should be implemented by every class which can be started/stopped during runtime it Tigase\n * XMPP Server (especially for components, processors, etc. which instances can be replaced and they need information\n * that it's lifecycle has ended).\n *\n * @author andrzej\n */\npublic interface Lifecycle {\n\n\tvoid start();\n\n\tvoid stop();\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/Message.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.JID;\n\n/**\n * Created: Dec 31, 2009 8:38:38 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class Message\n\t\textends Packet {\n\n\tpublic static final String ELEM_NAME = \"message\";\n\n\tpublic static final String[] MESSAGE_BODY_PATH = {ELEM_NAME, \"body\"};\n\n\tpublic static final String[] MESSAGE_SUBJECT_PATH = {ELEM_NAME, \"subject\"};\n\n\tpublic static final String[] MESSAGE_ERROR_PATH = {ELEM_NAME, \"error\"};\n\n\tpublic static final String[] MESSAGE_DELAY_PATH = {ELEM_NAME, \"delay\"};\n\n\t/**\n\t * Creates a packet with message stanza.\n\t *\n\t * @param from is a <code>JID</code> instance with message source address.\n\t * @param to is a <code>JID</code> instance with message destination address.\n\t * @param type is a <code>StanzaType</code> object with the message type.\n\t * @param body is a <code>String</code> object with message body content.\n\t * @param subject is a <code>String</code> object with message subject.\n\t * @param thread is a <code>String</code> object with message thread.\n\t * @param id is a <code>String</code> object with packet id value. Normally we do not set packet IDs for messages\n\t * but in some cases this might be useful.\n\t *\n\t * @return a new <code>Packet</code> instance (more specifically <code>Message</code> instance) with the message\n\t * stanza.\n\t */\n\tpublic static Packet getMessage(JID from, JID to, StanzaType type, String body, String subject, String thread,\n\t\t\t\t\t\t\t\t\tString id) {\n\t\tElement message = new Element(\"message\", null, null);\n\n\t\tmessage.setXMLNS(CLIENT_XMLNS);\n\t\tif (body != null) {\n\t\t\tmessage.addChild(new Element(\"body\", body));\n\t\t}\n\t\tif (from != null) {\n\t\t\tmessage.addAttribute(\"from\", from.toString());\n\t\t}\n\t\tif (to != null) {\n\t\t\tmessage.addAttribute(\"to\", to.toString());\n\t\t}\n\t\tif (type != null) {\n\t\t\tmessage.addAttribute(\"type\", type.name());\n\t\t}\n\t\tif (id != null) {\n\t\t\tmessage.addAttribute(\"id\", id);\n\t\t}\n\t\tif (subject != null) {\n\t\t\tmessage.addChild(new Element(\"subject\", subject));\n\t\t}\n\t\tif (thread != null) {\n\t\t\tmessage.addChild(new Element(\"thread\", thread));\n\t\t}\n\n\t\treturn packetInstance(message, from, to);\n\t}\n\n\tpublic Message(Element elem) throws TigaseStringprepException {\n\t\tsuper(elem);\n\t}\n\n\tpublic Message(Element elem, JID stanzaFrom, JID stanzaTo) {\n\t\tsuper(elem, stanzaFrom, stanzaTo);\n\t}\n\n\t@Override\n\tprotected String[] getElNameErrorPath() {\n\t\treturn MESSAGE_ERROR_PATH;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/MessageReceiver.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.Queue;\n\n/**\n * Interface MessageReceiver\n * <br>\n * Objects of this type can receive messages. They can be in fact routing destination depending on target address.\n * Message are routed to proper destination in MessageRouter class.\n * <br>\n * Created: Tue Nov 22 07:07:11 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface MessageReceiver\n\t\textends ServerComponent {\n\n\tboolean addPacket(Packet packet);\n\n\tboolean addPacketNB(Packet packet);\n\n\tboolean addPackets(Queue<Packet> packets);\n\n\tBareJID getDefHostName();\n\n\t///**\n// * Returns array of Strings. Each String should be a regular expression\n// * defining destination addresses for which this receiver can process\n// * messages. There can be more than one message receiver for each messages.\n// *\n// * @return a <code>String[]</code> value\n// */\n//String[] getLocalAddresses();\n\t// Set<String> getRoutings();\n\tboolean isInRegexRoutings(String address);\n\n\tvoid setParent(MessageReceiver msg_rec);\n\n\tvoid start();\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/MessageRouter.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport tigase.conf.ConfigurationException;\nimport tigase.conf.ConfiguratorAbstract;\nimport tigase.conf.MonitoringBeanIfc;\nimport tigase.conf.SetLoggingCommand;\nimport tigase.disco.XMPPService;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.RegistrarBean;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.script.CommandIfc;\nimport tigase.stats.StatisticsList;\nimport tigase.sys.NMTScope;\nimport tigase.sys.NativeMemoryTracking;\nimport tigase.sys.TigaseRuntime;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.util.updater.UpdatesChecker;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.PresenceCapabilitiesManager;\nimport tigase.xmpp.jid.JID;\n\nimport javax.script.Bindings;\nimport java.lang.management.BufferPoolMXBean;\nimport java.lang.management.ManagementFactory;\nimport java.lang.management.MemoryPoolMXBean;\nimport java.lang.management.MemoryUsage;\nimport java.text.DecimalFormat;\nimport java.text.NumberFormat;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentSkipListSet;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.server.MessageRouterConfig.DISCO_NAME_PROP_VAL;\nimport static tigase.server.MessageRouterConfig.DISCO_SHOW_VERSION_PROP_VAL;\nimport static tigase.xmpp.impl.PresenceCapabilitiesManager.CAPS_NODE;\n\n/**\n * Class MessageRouter\n * <br>\n * Created: Tue Nov 22 07:07:11 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\n@Bean(name = \"message-router\", parent = Kernel.class, active = true)\npublic class MessageRouter\n\t\textends AbstractMessageReceiver\n\t\timplements MessageRouterIfc, RegistrarBean, UnregisterAware {\n\n\t// implements XMPPService {\n\t// public static final String INFO_XMLNS =\n\t// \"http://jabber.org/protocol/disco#info\";\n\t// public static final String ITEMS_XMLNS =\n\t// \"http://jabber.org/protocol/disco#items\";\n\n\t// TODO: use default logger with TRACE level after switching to slf4j\n\tprivate static final Logger debugLog = Logger.getLogger(\"tigase.debug.MessageRouter\");\n\tprivate static final Logger log = Logger.getLogger(MessageRouter.class.getName());\n\n\tprivate static final String JVM_STATS_GC_STATISTICS = \"JVM/GC-statistics\";\n\tprivate static final String JVM_STATS_HEAP_TOTAL = \"JVM/HEAP Total \";\n\tprivate static final String JVM_STATS_HEAP_POOLS = \"JVM/MemoryPools/HeapMemory/\";\n\tprivate static final String JVM_STATS_BUFFER_POOLS = \"JVM/BufferPools/\";\n\n\tprivate Map<String, ServerComponent> components = new ConcurrentHashMap<>();\n\t@Inject\n\tprivate Set<ServerComponent> componentsAll;\n\tprivate Map<JID, ServerComponent> components_byId = new ConcurrentHashMap<>();\n\tprivate ConfiguratorAbstract config = null;\n\tprivate Set<String> connectionManagerNames = new ConcurrentSkipListSet<>();\n\t// private static final long startupTime = System.currentTimeMillis();\n\t// private Set<String> localAddresses = new CopyOnWriteArraySet<String>();\n\t@ConfigField(desc = \"Name of server software presented in discovery\")\n\tprivate String disco_name = DISCO_NAME_PROP_VAL;\n\t@ConfigField(desc = \"Show version of server software in discovery\")\n\tprivate boolean disco_show_version = DISCO_SHOW_VERSION_PROP_VAL;\n\tprivate boolean inProperties = false;\n\t@Inject(bean = \"kernel\")\n\tprivate Kernel kernel;\n\t@Inject(nullAllowed = true)\n\tprivate MonitoringBeanIfc monitoringBean;\n\tprivate Map<String, MessageReceiver> receivers = new ConcurrentHashMap<>();\n\tprivate ConcurrentHashMap<String, ComponentRegistrator> registrators = new ConcurrentHashMap<>();\n\t@Inject\n\tprivate UpdatesChecker updates_checker = null;\n\tprivate Map<String, XMPPService> xmppServices = new ConcurrentHashMap<>();\n\n\t@ConfigField(desc = \"Enabled detailed memory usage statistics from NMT in metrics\", alias = \"detailed-memory-statistics\")\n\tprivate boolean detailedMemoryStatistics = false;\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\n\t}\n\n\tpublic Set<ServerComponent> getComponentsAll() {\n\t\treturn componentsAll;\n\t}\n\n\tpublic void setComponentsAll(Set<ServerComponent> components) {\n\t\tif (components == null) {\n\t\t\treturn;\n\t\t}\n\t\tcomponentsAll = components;\n\n\t\tHashSet<ServerComponent> removeComponents = new HashSet<>(this.components.values());\n\t\tremoveComponents.removeAll(components);\n\t\tfor (ServerComponent comp : removeComponents) {\n\t\t\tif (comp instanceof ComponentRegistrator) {\n\t\t\t\tremoveRegistrator((ComponentRegistrator) comp);\n\t\t\t} else if (comp instanceof MessageReceiver) {\n\t\t\t\tremoveRouter((MessageReceiver) comp);\n\t\t\t} else {\n\t\t\t\tremoveComponent(comp);\n\t\t\t}\n\t\t\tif (comp instanceof ConnectionManager) {\n\t\t\t\tconnectionManagerNames.remove(comp.getName());\n\t\t\t}\n\t\t\tcomp.release();\n\t\t}\n\n\t\tHashSet<ServerComponent> newComponents = new HashSet<>(components);\n\t\tnewComponents.removeAll(this.components.values());\n\t\tfor (ServerComponent comp : newComponents) {\n\t\t\ttry {\n\t\t\t\tif (comp instanceof MessageReceiver) {\n\t\t\t\t\tMessageReceiver mr = (MessageReceiver) comp;\n\t\t\t\t\tmr.setParent(this);\n\t\t\t\t\tmr.start();\n\t\t\t\t}\n\t\t\t\tif (comp instanceof ConnectionManager) {\n\t\t\t\t\tconnectionManagerNames.add(comp.getName());\n\t\t\t\t}\n\t\t\t\tif (comp instanceof ComponentRegistrator) {\n\t\t\t\t\taddRegistrator((ComponentRegistrator) comp);\n\t\t\t\t} else if (comp instanceof MessageReceiver) {\n\t\t\t\t\taddRouter((MessageReceiver) comp);\n\t\t\t\t} else {\n\t\t\t\t\taddComponent(comp);\n\t\t\t\t}\n\t\t\t} catch (ConfigurationException ex) {\n\t\t\t\t// TODO - most likely this will no longer happen as configuration will not be done in this method\n\t\t\t\tlog.log(Level.WARNING, \"component \" + comp.getName() + \" was not configured properly\", ex);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void addComponent(ServerComponent component) throws ConfigurationException {\n\t\tlog.log(Level.CONFIG, \"Adding component: \", component.getClass().getSimpleName());\n\t\tcomponents.put(component.getName(), component);\n\t\tcomponents_byId.put(component.getComponentId(), component);\n\t\tif (component instanceof XMPPService) {\n\t\t\txmppServices.put(component.getName(), (XMPPService) component);\n\t\t}\n\t\tfor (ComponentRegistrator registr : registrators.values()) {\n\t\t\tif (registr != component) {\n\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\tlog.log(Level.FINER, \"Adding: {0} component to {1} registrator.\",\n\t\t\t\t\t\t\tnew Object[]{component.getName(), registr.getName()});\n\t\t\t\t}\n\t\t\t\tregistr.addComponent(component);\n\t\t\t}    // end of if (reg != component)\n\t\t}      // end of for ()\n\t}\n\n\tpublic void addRegistrator(ComponentRegistrator registr) throws ConfigurationException {\n\t\tlog.log(Level.CONFIG, \"Adding registrator: {0}\", registr.getClass().getSimpleName());\n\t\tregistrators.put(registr.getName(), registr);\n\t\taddComponent(registr);\n\t\tfor (ServerComponent comp : components.values()) {\n\n\t\t\t// if (comp != registr) {\n\t\t\tregistr.addComponent(comp);\n\n\t\t\t// } // end of if (comp != registr)\n\t\t}    // end of for (ServerComponent comp : components)\n\t}\n\n\tpublic void addRouter(MessageReceiver receiver) throws ConfigurationException {\n\t\tlog.log(Level.CONFIG, \"Adding receiver: \" + receiver.getClass().getSimpleName());\n\t\taddComponent(receiver);\n\t\treceivers.put(receiver.getName(), receiver);\n\t}\n\n\t@Override\n\tpublic void beanConfigurationChanged(Collection<String> changedFields) {\n\t\tsuper.beanConfigurationChanged(changedFields);\n\t\tif (getServiceEntity() != null &&\n\t\t\t\t(changedFields.contains(\"disco_name\") || changedFields.contains(\"disco_show_version\"))) {\n\t\t\tupdateServiceDiscoveryItem(getName(), null, getDiscoDescription(), \"server\", \"im\", false);\n\t\t}\n\t}\n\n\t@Override\n\tpublic int hashCodeForPacket(Packet packet) {\n\n\t\t// This is actually quite tricky part. We want to both avoid\n\t\t// packet reordering and also even packets distribution among\n\t\t// different threads.\n\t\t// If packet comes from a connection manager we must use packetFrom\n\t\t// address. However if the packet comes from SM, PubSub or other similar\n\t\t// component all packets would end-up in the same queue.\n\t\t// So, kind of a workaround here....\n\t\t// TODO: develop a proper solution discovering which components are\n\t\t// connection managers and use their names here instead of static names.\n\t\tif ((packet.getPacketFrom() != null) && (packet.getPacketFrom().getLocalpart() != null)) {\n\t\t\tif (connectionManagerNames.contains(packet.getPacketFrom().getLocalpart())) {\n\t\t\t\treturn packet.getPacketFrom().hashCode();\n\t\t\t}\n\t\t}\n\t\tif ((packet.getPacketTo() != null) && (packet.getPacketTo().getLocalpart() != null)) {\n\t\t\tif (connectionManagerNames.contains(packet.getPacketTo().getLocalpart())) {\n\t\t\t\treturn packet.getPacketTo().hashCode();\n\t\t\t}\n\t\t}\n\t\tif (packet.getStanzaTo() != null) {\n\t\t\treturn packet.getStanzaTo().getBareJID().hashCode();\n\t\t}\n\t\tif ((packet.getPacketFrom() != null) && !getComponentId().equals(packet.getPacketFrom())) {\n\n\t\t\t// This comes from connection manager so the best way is to get hashcode\n\t\t\t// by the connectionId, which is in the getFrom()\n\t\t\treturn packet.getPacketFrom().hashCode();\n\t\t}\n\t\tif ((packet.getPacketTo() != null) && !getComponentId().equals(packet.getPacketTo())) {\n\t\t\treturn packet.getPacketTo().hashCode();\n\t\t}\n\n\t\t// If not, then a better way is to get hashCode from the elemTo address\n\t\t// as this would be by the destination address user name:\n\t\treturn 1;\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tsuper.initialize();\n\t\tupdateServiceDiscoveryItem(getName(), null, getDiscoDescription(), \"server\", \"im\", false);\n\t}\n\n\t@Override\n\tpublic void initBindings(Bindings binds) {\n\t\tsuper.initBindings(binds);\n\t\tbinds.put(\"kernel\", kernel);\n\t}\n\n\t@Override\n\tpublic int processingInThreads() {\n\t\treturn Runtime.getRuntime().availableProcessors() * 4;\n\t}\n\n\t@Override\n\tpublic int processingOutThreads() {\n\t\treturn 1;\n\t}\n\n\t@Override\n\tpublic void processPacket(Packet packet) {\n\n\t\t// We do not process packets with not destination address\n\t\t// Just dropping them and writing a log message\n\t\tif (packet.getTo() == null) {\n\t\t\tlog.log(Level.WARNING, \"Packet with TO attribute set to NULL: {0}\", packet);\n\n\t\t\treturn;\n\t\t}    // end of if (packet.getTo() == null)\n\n\t\t// Intentionally comparing to static, final String\n\t\t// This is kind of a hack to handle packets addressed specifically for the\n\t\t// SessionManager\n\t\t// TODO: Replace the hack with a proper solution\n\t\t// if (packet.getTo() == NULL_ROUTING) {\n\t\t// log.log(Level.INFO, (\"NULL routing, it is normal if server doesn't know how to\"\n\t\t// + \" process packet: \" + packet.toStringSecure());\n\t\t//\n\t\t// try {\n\t\t// Packet error =\n\t\t// Authorization.FEATURE_NOT_IMPLEMENTED.getResponseMessage(packet,\n\t\t// \"Feature not supported yet.\", true);\n\t\t//\n\t\t// addOutPacketNB(error);\n\t\t// } catch (PacketErrorTypeException e) {\n\t\t// log.warning(\"Packet processing exception: \" + e);\n\t\t// }\n\t\t//\n\t\t// return;\n\t\t// }\n\t\t// if (log.isLoggable(Level.FINER)) {\n\t\t// log.finer(\"Processing packet: \" + packet.getElemName()\n\t\t// + \", type: \" + packet.getType());\n\t\t// }\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Processing packet: {0}\", packet);\n\t\t}\n\n\t\t// Detect inifinite loop if from == to\n\t\t// Maybe it is not needed anymore...\n\t\t// There is a need to process packets with the same from and to address\n\t\t// let't try to relax restriction and block all packets with error type\n\t\t// 2008-06-16\n\t\tif (((packet.getType() == StanzaType.error) && (packet.getFrom() != null) &&\n\t\t\t\tpacket.getFrom().equals(packet.getTo()) &&\n\t\t\t\t(packet.getStanzaFrom() == null || packet.getStanzaFrom().equals(packet.getStanzaTo())))) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Possible infinite loop, dropping packet: {0}\", packet);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\t\tif (isLocalDiscoRequest(packet)) {\n\t\t\tQueue<Packet> results = new ArrayDeque<>();\n\n\t\t\tprocessDiscoQuery(packet, results);\n\t\t\tif (results.size() > 0) {\n\t\t\t\tfor (Packet res : results) {\n\n\t\t\t\t\t// No more recurrential calls!!\n\t\t\t\t\taddOutPacketNB(res);\n\t\t\t\t}    // end of for ()\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\t// It is not a service discovery packet, we have to find a component to\n\t\t// process\n\t\t// the packet. The below block of code is to \"quickly\" find a component if\n\t\t// the\n\t\t// the packet is addressed to the component ID where the component ID is\n\t\t// either\n\t\t// of one below:\n\t\t// 1. component name + \"@\" + default domain name\n\t\t// 2. component name + \"@\" + any virtual host name\n\t\t// 3. component name + \".\" + default domain name\n\t\t// 4. component name + \".\" + any virtual host name\n\t\t// TODO: check the efficiency for packets addressed to c2s component\n\t\tServerComponent comp = getLocalComponent(packet.getTo());\n\n\t\tif (comp != null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"1. Packet will be processed by: {0}, {1}\",\n\t\t\t\t\t\tnew Object[]{comp.getComponentId(), packet});\n\t\t\t}\n\n\t\t\tQueue<Packet> results = new ArrayDeque<Packet>();\n\n\t\t\tif (comp == this) {\n\n\t\t\t\t// This is addressed to the MessageRouter itself. Has to be processed\n\t\t\t\t// separately to avoid recurential calls by the packet processing\n\t\t\t\t// method.\n\t\t\t\tprocessPacketMR(packet, results);\n\t\t\t} else {\n\n\t\t\t\t// All other components process the packet the same way.\n\t\t\t\tcomp.processPacket(packet, results);\n\t\t\t}\n\t\t\tif (results.size() > 0) {\n\t\t\t\tfor (Packet res : results) {\n\n\t\t\t\t\t// No more recurrential calls!!\n\t\t\t\t\taddOutPacketNB(res);\n\n\t\t\t\t\t// processPacket(res);\n\t\t\t\t}    // end of for ()\n\t\t\t}\n\n\t\t\t// If the component is found the processing ends here as there can be\n\t\t\t// only one component with specific ID.\n\t\t\treturn;\n\t\t}\n\n\t\t// This packet is not processed yet\n\t\t// The packet can be addressed to just a domain, one of the virtual hosts\n\t\t// The code below finds all components which handle packets addressed\n\t\t// to a virtual domains (implement VHostListener and return 'true' from\n\t\t// handlesLocalDomains() method call)\n\t\tString host = packet.getTo().getDomain();\n\t\tServerComponent[] comps = getComponentsForLocalDomain(host);\n\n\t\tif (comps == null) {\n\n\t\t\t// Still no component found, now the most expensive lookup.\n\t\t\t// Checking regex routings provided by the component.\n\t\t\tcomps = getServerComponentsForRegex(packet.getTo().getBareJID().toString());\n\t\t}\n\t\tif ((comps == null) && !isLocalDomain(host)) {\n\n\t\t\t// None of the component want to process the packet.\n\t\t\t// If the packet is addressed to non-local domain then it is processed by\n\t\t\t// all components dealing with external world, like s2s\n\t\t\tcomps = getComponentsForNonLocalDomain(host);\n\t\t}\n\n\t\t// Ok, if any component has been found then process the packet in a standard\n\t\t// way\n\t\tif (comps != null) {\n\n\t\t\t// Processing packet and handling results out\n\t\t\tQueue<Packet> results = new ArrayDeque<Packet>();\n\n\t\t\tfor (ServerComponent serverComponent : comps) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"2. Packet will be processed by: {0}, {1}\",\n\t\t\t\t\t\t\tnew Object[]{serverComponent.getComponentId(), packet});\n\t\t\t\t}\n\t\t\t\tserverComponent.processPacket(packet, results);\n\t\t\t\tif (results.size() > 0) {\n\t\t\t\t\tfor (Packet res : results) {\n\n\t\t\t\t\t\t// No more recurrential calls!!\n\t\t\t\t\t\taddOutPacketNB(res);\n\n\t\t\t\t\t\t// processPacket(res);\n\t\t\t\t\t}    // end of for ()\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\n\t\t\t// No components for the packet, sending an error back\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"There is no component for the packet, sending it back\");\n\t\t\t}\n\t\t\ttry {\n\t\t\t\taddOutPacketNB(Authorization.SERVICE_UNAVAILABLE.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"There is no service found to process your request.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttrue));\n\t\t\t} catch (PacketErrorTypeException e) {\n\n\t\t\t\t// This packet is to local domain, we don't want to send it out\n\t\t\t\t// drop packet :-(\n\t\t\t\tlog.warning(\"Can't process packet to local domain, dropping...\" + packet.toStringSecure());\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void processPacketMR(Packet packet, Queue<Packet> results) {\n\t\tIq iq = null;\n\n\t\tif (packet instanceof Iq) {\n\t\t\tiq = (Iq) packet;\n\t\t} else {\n\n\t\t\t// Not a command for sure...\n\t\t\tlog.log(Level.FINE, \"I expect command (Iq) packet here, instead I got: \" + packet.toString());\n\t\t\ttry {\n\t\t\t\tPacket res = Authorization.FEATURE_NOT_IMPLEMENTED.getResponseMessage(packet, null, true);\n\t\t\t\tresults.offer(res);\n\t\t\t} catch (PacketInvalidTypeException e) {\n\t\t\t\tlog.log(Level.FINE, \"Packet: {0} processing exception (I expect command (Iq) packet here): {1}\",\n\t\t\t\t\t\tnew Object[]{packet.toString(), e});\n\t\t\t} catch (PacketInvalidAddressException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Packet processing exception (I expect command (Iq) packet here): \", e);\n\t\t\t} catch (PacketErrorTypeException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Packet processing exception (I expect command (Iq) packet here): \", e);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\t\tif (packet.getPermissions() != Permissions.ADMIN) {\n\t\t\ttry {\n\t\t\t\tPacket res = Authorization.NOT_AUTHORIZED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"You are not authorized for this action.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true);\n\n\t\t\t\tresults.offer(res);\n\n\t\t\t\t// processPacket(res);\n\t\t\t} catch (PacketInvalidTypeException e) {\n\t\t\t\tlog.log(Level.FINE, \"Packet: {0} processing exception (not authorised): {1}\",\n\t\t\t\t\t\tnew Object[]{packet.toString(), e});\n\t\t\t} catch (PacketInvalidAddressException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Packet processing exception (not authorised): \", e);\n\t\t\t} catch (PacketErrorTypeException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Packet processing exception (not authorised): \", e);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Command received: \" + iq.toString() + \", but no commands available!\");\n\t\t}\n\t}\n\n\tpublic void removeComponent(ServerComponent component) {\n\t\tfor (ComponentRegistrator registr : registrators.values()) {\n\t\t\tif (registr != component) {\n\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\tlog.log(Level.FINER, \"Removing: {0} component from {1} registrator.\",\n\t\t\t\t\t\t\tnew Object[]{component.getName(), registr.getName()});\n\t\t\t\t}\n\t\t\t\tregistr.deleteComponent(component);\n\t\t\t}    // end of if (reg != component)\n\t\t}      // end of for ()\n\t\tcomponents.remove(component.getName());\n\t\tcomponents_byId.remove(component.getComponentId());\n\t\tif (component instanceof XMPPService) {\n\t\t\txmppServices.remove(component.getName());\n\t\t}\n\t}\n\n\tpublic void removeRegistrator(ComponentRegistrator registr) {\n\t\tlog.log(Level.CONFIG, \"Removing registrator: {0}\", registr.getClass().getSimpleName());\n\t\tregistrators.remove(registr.getName(), registr);\n\t\tremoveComponent(registr);\n\t\tfor (ServerComponent comp : components.values()) {\n\n\t\t\t// if (comp != registr) {\n\t\t\tregistr.deleteComponent(comp);\n\n\t\t\t// } // end of if (comp != registr)\n\t\t}    // end of for (ServerComponent comp : components)\n\t}\n\n\tpublic void removeRouter(MessageReceiver receiver) {\n\t\tlog.log(Level.CONFIG, \"Removing receiver: \" + receiver.getClass().getSimpleName());\n\t\treceivers.remove(receiver.getName());\n\t\tremoveComponent(receiver);\n\t}\n\n\t@Override\n\tpublic void start() {\n\t\tsuper.start();\n\t}\n\n\t@Override\n\tpublic void stop() {\n\t\tSet<String> comp_names = new TreeSet<String>();\n\n\t\tcomp_names.addAll(components.keySet());\n\t\tfor (String name : comp_names) {\n\t\t\tServerComponent comp = components.remove(name);\n\n\t\t\tif ((comp != this) && (comp != null)) {\n\t\t\t\tcomp.release();\n\t\t\t}\n\t\t}\n\t\tsuper.stop();\n\t}\n\n\t@Override\n\tpublic String getDiscoCategoryType() {\n\t\treturn \"router\";\n\t}\n\n\t@Override\n\tpublic String getDiscoDescription() {\n\t\treturn disco_name + (disco_show_version ? (\" ver. \" + XMPPServer.getImplementationVersion()) : \"\");\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t\tsuper.getStatistics(list);\n\t\tlist.add(getName(), \"Local hostname\", getDefHostName().getDomain(), Level.INFO);\n\n\t\tTigaseRuntime runtime = TigaseRuntime.getTigaseRuntime();\n\n\t\tlist.add(getName(), \"Uptime\", runtime.getUptimeString(), Level.INFO);\n\n\t\tNumberFormat format = NumberFormat.getNumberInstance();\n\n\t\tlist.add(getName(), \"Version\", XMPPServer.getImplementationVersion(), Level.INFO);\n\n\t\tformat.setMaximumFractionDigits(4);\n\t\tlist.add(getName(), \"Load average\", format.format(runtime.getLoadAverage()), Level.FINE);\n\t\tlist.add(getName(), \"CPUs no\", runtime.getCPUsNumber(), Level.FINEST);\n\t\tlist.add(getName(), \"Threads count\", runtime.getThreadsNumber(), Level.FINEST);\n\n\t\tfloat cpuUsage = runtime.getCPUUsage();\n\t\tfloat heapUsage = runtime.getHeapMemUsage();\n\t\tfloat nonHeapUsage = runtime.getNonHeapMemUsage();\n\n\t\tlist.add(getName(), \"CPU usage [%]\", cpuUsage, Level.FINE);\n\t\tlist.add(getName(), \"HEAP usage [%]\", heapUsage, Level.FINE);\n\t\tlist.add(getName(), \"NONHEAP usage [%]\", nonHeapUsage, Level.FINE);\n\t\tformat = NumberFormat.getNumberInstance();\n\t\tformat.setMaximumFractionDigits(1);\n\n\t\tlist.add(getName(), \"CPU usage\", format.format(cpuUsage) + \"%\", Level.INFO);\n\n\t\tformat = NumberFormat.getIntegerInstance();\n\t\tif (format instanceof DecimalFormat) {\n\t\t\tDecimalFormat decf = (DecimalFormat) format;\n\n\t\t\tdecf.applyPattern(decf.toPattern() + \" KB\");\n\t\t}\n\t\tlist.add(getName(), \"Max Heap mem\", format.format(runtime.getHeapMemMax() / 1024), Level.INFO);\n\t\tlist.add(getName(), \"Used Heap\", format.format(runtime.getHeapMemUsed() / 1024), Level.INFO);\n\t\tlist.add(getName(), \"Free Heap\", format.format(\n\t\t\t\t\t\t runtime.getHeapMemMax() == -1 ? -1.0 : (runtime.getHeapMemMax() - runtime.getHeapMemUsed()) / 1024),\n\t\t\t\t Level.FINE);\n\t\tlist.add(getName(), \"Max NonHeap mem\", format.format(runtime.getNonHeapMemMax() / 1024), Level.FINE);\n\t\tlist.add(getName(), \"Used NonHeap\", format.format(runtime.getNonHeapMemUsed() / 1024), Level.FINE);\n\t\tlist.add(getName(), \"Free NonHeap\", format.format(runtime.getNonHeapMemMax() == -1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  ? -1.0\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  : (runtime.getNonHeapMemMax() - runtime.getNonHeapMemUsed()) /\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  1024), Level.FINE);\n\n\t\t// general JVM/GC info\n\t\tlist.add(getName(), \"Heap region name\", runtime.getOldGenName(), Level.FINE);\n\t\tlist.add(getName(), JVM_STATS_GC_STATISTICS, runtime.getGcStatistics(), Level.FINER);\n\n\t\t// Total HEAP usage\n\t\tLevel statLevel = Level.FINER;\n\t\tMemoryUsage heapMemoryUsage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();\n\t\tString JVM_STATS_USED = \"Used [KB]\";\n\t\tString JVM_STATS_FREE = \"Free [KB]\";\n\t\tString JVM_STATS_MAX = \"Max [KB]\";\n\t\tint factor = 1024;\n\t\tlist.add(getName(), JVM_STATS_HEAP_TOTAL + JVM_STATS_USED, heapMemoryUsage.getUsed() / factor, statLevel);\n\t\tlist.add(getName(), JVM_STATS_HEAP_TOTAL + JVM_STATS_MAX, heapMemoryUsage.getMax() / factor, statLevel);\n\t\tlist.add(getName(), JVM_STATS_HEAP_TOTAL + JVM_STATS_FREE,\n\t\t\t\t (heapMemoryUsage.getMax() - heapMemoryUsage.getUsed()) / factor, statLevel);\n\t\tlist.add(getName(), JVM_STATS_HEAP_TOTAL + \"Usage [%]\",\n\t\t\t\t heapMemoryUsage.getUsed() * 100F / heapMemoryUsage.getMax(), statLevel);\n\n\t\t// per-heap-pool metrics\n\t\tfor (Map.Entry<String, MemoryPoolMXBean> entry : runtime.getMemoryPoolMXBeans().entrySet()) {\n\t\t\tlist.add(getName(), JVM_STATS_HEAP_POOLS + entry.getKey() + \"/Name\", entry.getValue().getName(), statLevel);\n\t\t\tlist.add(getName(), JVM_STATS_HEAP_POOLS + entry.getKey() + \"/Usage/\" + JVM_STATS_USED,\n\t\t\t\t\t entry.getValue().getUsage().getUsed() / factor, statLevel);\n\t\t\tlist.add(getName(), JVM_STATS_HEAP_POOLS + entry.getKey() + \"/Usage/\" + JVM_STATS_MAX,\n\t\t\t\t\t entry.getValue().getUsage().getMax() / factor, statLevel);\n\n\t\t\tlist.add(getName(), JVM_STATS_HEAP_POOLS + entry.getKey() + \"/Peak Usage/\" + JVM_STATS_USED,\n\t\t\t\t\t entry.getValue().getPeakUsage().getUsed() / factor, statLevel);\n\t\t\tlist.add(getName(), JVM_STATS_HEAP_POOLS + entry.getKey() + \"/Peak Usage/\" + JVM_STATS_MAX,\n\t\t\t\t\t entry.getValue().getPeakUsage().getMax() / factor, statLevel);\n\n\t\t\tlist.add(getName(), JVM_STATS_HEAP_POOLS + entry.getKey() + \"/Collection Usage/\" + JVM_STATS_USED,\n\t\t\t\t\t entry.getValue().getCollectionUsage().getUsed() / factor, statLevel);\n\t\t\tlist.add(getName(), JVM_STATS_HEAP_POOLS + entry.getKey() + \"/Collection Usage/\" + JVM_STATS_MAX,\n\t\t\t\t\t entry.getValue().getCollectionUsage().getMax() / factor, statLevel);\n\n\t\t}\n\n\t\t// per-buffer-pool metrics\n\t\tfor (BufferPoolMXBean bufferMXBean : ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class)) {\n\t\t\tlist.add(getName(), JVM_STATS_BUFFER_POOLS + bufferMXBean.getName() + \"/Used [B]\",\n\t\t\t\t\t bufferMXBean.getMemoryUsed(), statLevel);\n\t\t\tlist.add(getName(), JVM_STATS_BUFFER_POOLS + bufferMXBean.getName() + \"/Capacity [B]\",\n\t\t\t\t\t bufferMXBean.getTotalCapacity(), statLevel);\n\t\t\tlist.add(getName(), JVM_STATS_BUFFER_POOLS + bufferMXBean.getName() + \"/Count\", bufferMXBean.getCount(),\n\t\t\t\t\t statLevel);\n\t\t}\n\n\t\tif (detailedMemoryStatistics) {\n\t\t\t// Native Memory Tracking statistics - require NMT to be enabled with -XX:NativeMemoryTracking=summary JVM option\n\t\t\t// see `JVM_MEMORY` in etc/tigase.conf!\n\t\t\tfinal Optional<NativeMemoryTracking> nativeMemoryTracking = TigaseRuntime.getNativeMemoryTracking();\n\t\t\tnativeMemoryTracking.ifPresent(nmt -> {\n\t\t\t\tlist.add(getName(), \"JVM/NativeMemoryTracking\", nmt.toString(), Level.FINE);\n\t\t\t\tfinal TreeSet<NMTScope> scopes = new TreeSet<>(Comparator.comparing(NMTScope::getCommitted).reversed());\n\t\t\t\tscopes.addAll(nmt.getScopes().values());\n\t\t\t\tfor (NMTScope scope : scopes) {\n\t\t\t\t\tnmt.getScale();\n\t\t\t\t\tfinal String keyPrefix = \"JVM/NativeMemoryTracking/\" + scope.getScopeType() + \"/\";\n\t\t\t\t\tlist.add(getName(), keyPrefix + \"commited [\" + nmt.getScale() + \"]\", scope.getCommitted(), Level.FINE);\n\t\t\t\t\tlist.add(getName(), keyPrefix + \"reserved [\" + nmt.getScale() + \"]\", scope.getReserved(), Level.FINE);\n\t\t\t\t\tscope.getArena().ifPresent(val -> list.add(getName(), keyPrefix + \"arena [\" + nmt.getScale() + \"]\", val, Level.FINER));\n\t\t\t\t\tscope.getMalloc().ifPresent(val -> list.add(getName(), keyPrefix + \"malloc [\" + nmt.getScale() + \"]\", val, Level.FINER));\n//\t\t\t\t\tscope.getMmapCommitted()\n//\t\t\t\t\t\t\t.ifPresent(val -> list.add(getName(), keyPrefix + \"mmapCommitted [\" + nmt.getScale() + \"]\", val, Level.FINEST));\n//\t\t\t\t\tscope.getMmapReserved()\n//\t\t\t\t\t\t\t.ifPresent(val -> list.add(getName(), keyPrefix + \"mmapReserved [\" + nmt.getScale() + \"]\", val, Level.FINEST));\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setConfig(ConfiguratorAbstract config) throws ConfigurationException {\n\t\tcomponents.put(getName(), this);\n\t\tthis.config = config;\n\t\taddRegistrator(config);\n\t}\n\n\tpublic void setDetailedMemoryStatistics(boolean detailedMemoryStatistics) {\n\t\tthis.detailedMemoryStatistics = detailedMemoryStatistics;\n\t\tif (detailedMemoryStatistics && TigaseRuntime.getNativeMemoryTracking().isEmpty()) {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"Detailed memory statistics (NMT) enabled in config.tdsl but not enabled on JVM level - please enable in etc/tigase.conf!\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\tstop();\n\t}\n\n\t@Override\n\tpublic Optional<Element> getServiceEntityCaps(JID fromJid) {\n\t\tif (fromJid != null) {\n\t\t\tfinal Element discoInfo = getDiscoInfo(JID.jidInstanceNS(fromJid.getDomain()), fromJid, null);\n\t\t\tfinal String caps = PresenceCapabilitiesManager.generateVerificationStringFromDiscoInfo(discoInfo);\n\t\t\tfinal String capsNode = CAPS_NODE + \"#\" + caps;\n\t\t\tString[] features = PresenceCapabilitiesManager.getFeaturesFromDiscoInfo(discoInfo);\n\t\t\tPresenceCapabilitiesManager.setNodeFeatures(capsNode, features);\n\n\t\t\tfinal Element capsElement = PresenceCapabilitiesManager.getCapsElement(caps);\n\t\t\treturn Optional.of(capsElement);\n\t\t} else {\n\t\t\treturn Optional.empty();\n\t\t}\n\t}\n\n\tpublic Element getDiscoInfo(JID toJid, JID fromJid, String node) {\n\t\tElement discoInfoResult = new Element(\"query\");\n\t\tdiscoInfoResult.setXMLNS(\"http://jabber.org/protocol/disco#info\");\n\t\tif (isLocalDomain(toJid.toString()) && (node == null)) {\n\t\t\ttry {\n\t\t\t\tfinal Element discoInfo = getDiscoInfo(node, (toJid.getLocalpart() == null)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t ? JID.jidInstance(getName(), toJid.toString(), null)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t : toJid, fromJid);\n\t\t\t\tdiscoInfoResult.addChildren(discoInfo.getChildren());\n\t\t\t} catch (TigaseStringprepException e) {\n\t\t\t\tlog.log(Level.WARNING, \"processing by stringprep throw exception for = {0}@{1}\",\n\t\t\t\t\t\tnew Object[]{getName(), toJid.toString()});\n\t\t\t}\n\t\t\tfor (XMPPService comp : xmppServices.values()) {\n\n\t\t\t\t// Buggy custom component may throw exceptions here (NPE most likely)\n\t\t\t\t// which may cause service disco problems for well-behaving components\n\t\t\t\t// too\n\t\t\t\t// So this is kind of a protection\n\t\t\t\ttry {\n\t\t\t\t\tList<Element> features = comp.getDiscoFeatures(fromJid);\n\n\t\t\t\t\tif (features != null) {\n\t\t\t\t\t\tdiscoInfoResult.addChildren(features);\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Component service disco problem: \" + comp.getName() + \"\", e);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor (XMPPService comp : xmppServices.values()) {\n\n\t\t\t\t// Buggy custom component may throw exceptions here (NPE most likely)\n\t\t\t\t// which may cause service disco problems for well-behaving components\n\t\t\t\t// too\n\t\t\t\t// So this is kind of a protection\n\t\t\t\ttry {\n\n\t\t\t\t\t// if (jid.startsWith(comp.getName() + \".\")) {\n\t\t\t\t\tElement resp = comp.getDiscoInfo(node, toJid, fromJid);\n\n\t\t\t\t\tif (resp != null) {\n\t\t\t\t\t\tdiscoInfoResult.addChildren(resp.getChildren());\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Component service disco problem: \" + comp.getName(), e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn discoInfoResult;\n\t}\n\n\t@Override\n\tprotected Integer getMaxQueueSize(int def) {\n\t\treturn def * 10;\n\t}\n\n\t@Override\n\tprotected void reloadScripts() {\n\t\tsuper.reloadScripts();\n\t\tCommandIfc cmd = new SetLoggingCommand(kernel);\n\t\tscriptCommands.put(cmd.getCommandId(), cmd);\n\t}\n\n\tprivate void processDiscoQuery(final Packet packet, final Queue<Packet> results) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Processing disco query by: {0}\", packet.toStringSecure());\n\t\t}\n\n\t\tJID toJid = packet.getStanzaTo();\n\t\tJID fromJid = packet.getStanzaFrom();\n\t\tString node = packet.getAttributeStaticStr(Iq.IQ_QUERY_PATH, \"node\");\n\t\tElement query = packet.getElement().getChild(\"query\").clone();\n\n\t\tif (node != null && PresenceCapabilitiesManager.getNodeFeatures(node) != null) {\n\t\t\tnode = null;\n\t\t}\n\n\t\tif (packet.isXMLNSStaticStr(Iq.IQ_QUERY_PATH, INFO_XMLNS)) {\n\t\t\tquery.addChildren(getDiscoInfo(toJid, fromJid, node).getChildren());\n\t\t}\n\t\tif (packet.isXMLNSStaticStr(Iq.IQ_QUERY_PATH, ITEMS_XMLNS)) {\n\t\t\tquery.addChildren(getDiscoItems(toJid, fromJid, node));\n\t\t}\n\t\tresults.offer(packet.okResult(query, 0));\n\t}\n\n\tprivate List<Element> getDiscoItems(JID toJid, JID fromJid, String node) {\n\t\tboolean localDomain = isLocalDomain(toJid.toString());\n\n\t\tList<Element> items = new ArrayList<>();\n\n\t\tif (localDomain) {\n\t\t\tfor (XMPPService comp : xmppServices.values()) {\n\n\t\t\t\t// Buggy custom component may throw exceptions here (NPE most likely)\n\t\t\t\t// which may cause service disco problems for well-behaving components\n\t\t\t\t// too\n\t\t\t\t// So this is kind of a protection\n\t\t\t\ttry {\n\n\t\t\t\t\t// if (localDomain || (nick != null && comp.getName().equals(nick)))\n\t\t\t\t\t// {\n\t\t\t\t\tfinal List<Element> discoItems = comp.getDiscoItems(node, toJid, fromJid);\n\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Localdomain: {0}, DiscoItems processed by: {1}, items: {2}\",\n\t\t\t\t\t\t\t\tnew Object[]{toJid, comp.getComponentId(), (items == null) ? null : items.toString()});\n\t\t\t\t\t}\n\t\t\t\t\tif (discoItems != null && !discoItems.isEmpty()) {\n\t\t\t\t\t\titems.addAll(discoItems);\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Component service disco problem: \" + comp.getName(), e);\n\t\t\t\t}\n\t\t\t}    // end of for ()\n\t\t} else {\n\t\t\tServerComponent comp = getLocalComponent(toJid);\n\n\t\t\tif ((comp != null) && (comp instanceof XMPPService)) {\n\t\t\t\titems = ((XMPPService) comp).getDiscoItems(node, toJid, fromJid);\n\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"DiscoItems processed by: {0}, items: {1}\",\n\t\t\t\t\t\t\tnew Object[]{comp.getComponentId(), (items == null) ? null : items.toString()});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn items;\n\t}\n\n\tprivate ServerComponent[] getComponentsForLocalDomain(String domain) {\n\t\treturn vHostManager.getComponentsForLocalDomain(domain);\n\t}\n\n\tprivate ServerComponent[] getComponentsForNonLocalDomain(String domain) {\n\t\treturn vHostManager.getComponentsForNonLocalDomain(domain);\n\t}\n\n\tprivate ServerComponent getLocalComponent(JID jid) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Called for : {0}\", jid);\n\t\t}\n\n\t\t// Fast lookup in the server components to find a candidate\n\t\t// by the component ID (JID). If the packet is addressed directly\n\t\t// to the component ID then this is where the processing must happen.\n\t\t// Normally the component id is: component name + \"@\" + default hostname\n\t\t// However the component may \"choose\" to have any ID.\n\t\tServerComponent comp = components_byId.get(jid);\n\n\t\tif (comp != null) {\n\t\t\treturn comp;\n\t\t}\n\t\tif (debugLog.isLoggable(Level.FINEST)) {\n\t\t\t// TODO: use default logger with TRACE level after switching to slf4j\n\t\t\tdebugLog.log(Level.FINEST, \"No componentID matches (fast lookup against exact address): \" +\n\t\t\t\t\t\"{0}, for map: {1}; trying VHost lookup\", new Object[]{jid, components_byId.keySet()});\n\t\t}\n\n\t\t// Note, component ID consists of the component name + default hostname\n\t\t// which can be different from a virtual host. There might be many\n\t\t// virtual hosts and the packet can be addressed to the component by\n\t\t// the component name + virtual host name\n\t\t// Code below, tries to find a destination by the component name + any\n\t\t// active virtual hostname.\n\t\tif (jid.getLocalpart() != null) {\n\t\t\tcomp = components.get(jid.getLocalpart());\n\t\t\tif ((comp != null) &&\n\t\t\t\t\t(isLocalDomain(jid.getDomain()) || jid.getDomain().equals(getDefHostName().getDomain()))) {\n\t\t\t\treturn comp;\n\t\t\t}\n\t\t}\n\t\tif (debugLog.isLoggable(Level.FINEST)) {\n\t\t\t// TODO: use default logger with TRACE level after switching to slf4j\n\t\t\tdebugLog.log(Level.FINEST, \"No component name matches (VHost lookup against component name): \" +\n\t\t\t\t\t\t\t\"{0}, for map: {1}, for all VHosts: {2}; trying other forms of addressing\",\n\t\t\t\t\tnew Object[]{jid, components.keySet(), vHostManager.getAllVHosts().size() > 20 ? \"[hosts count: \" +\n\t\t\t\t\t\t\tvHostManager.getAllVHosts().size() + \"]\" : String.valueOf(vHostManager.getAllVHosts())});\n\t\t}\n\n\t\t// Instead of a component ID built of: component name + \"@\" domain name\n\t\t// Some components have an ID of: component name + \".\" domain name\n\t\t// Code below tries to find a packet receiver if the address have the other\n\t\t// type of form.\n\t\tint idx = jid.getDomain().indexOf('.');\n\n\t\tif (idx > 0) {\n\t\t\tString cmpName = jid.getDomain().substring(0, idx);\n\t\t\tString basename = jid.getDomain().substring(idx + 1);\n\n\t\t\tcomp = components.get(cmpName);\n\t\t\tif ((comp != null) && (isLocalDomain(basename) || basename.equals(getDefHostName().getDomain()))) {\n\t\t\t\tif (debugLog.isLoggable(Level.FINEST)) {\n\t\t\t\t\t// TODO: use default logger with TRACE level after switching to slf4j\n\t\t\t\t\tdebugLog.log(Level.FINEST, \"Component matched: {0}, for comp: {1}, basename: {3}\",\n\t\t\t\t\t\t\tnew Object[]{jid, components.keySet(), comp, basename});\n\t\t\t\t}\n\t\t\t\treturn comp;\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Component match failed: {0}, for comp: {1}, basename: {3}\",\n\t\t\t\t\t\tnew Object[]{jid, components.keySet(), comp, basename});\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate ServerComponent[] getServerComponentsForRegex(String id) {\n\t\tLinkedHashSet<ServerComponent> comps = new LinkedHashSet<ServerComponent>();\n\n\t\tfor (MessageReceiver mr : receivers.values()) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Checking routings for: \" + mr.getName());\n\t\t\t}\n\t\t\tif (mr.isInRegexRoutings(id)) {\n\t\t\t\tcomps.add(mr);\n\t\t\t}\n\t\t}\n\t\tif (comps.size() > 0) {\n\t\t\treturn comps.toArray(new ServerComponent[comps.size()]);\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate boolean isDiscoDisabled(ServerComponent comp, JID to) {\n\t\tif (comp != null) {\n\t\t\treturn (comp instanceof DisableDisco);\n\t\t} else {\n\t\t\tServerComponent[] comps = getComponentsForLocalDomain(to.getDomain());\n\n\t\t\tif (comps != null) {\n\t\t\t\tfor (ServerComponent c : comps) {\n\t\t\t\t\tif (c instanceof DisableDisco) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate boolean isLocalDiscoRequest(Packet packet) {\n\t\tboolean result = false;\n\t\tJID to = packet.getStanzaTo();\n\t\tServerComponent comp = (to == null) ? null : getLocalComponent(to);\n\n\t\tresult = packet.isServiceDisco() && (packet.getType() == StanzaType.get) && (packet.getStanzaFrom() != null) &&\n\t\t\t\t((packet.getStanzaTo() == null) ||\n\t\t\t\t\t\t(((comp != null) || isLocalDomain(packet.getStanzaTo().toString())) &&\n\t\t\t\t\t\t\t\t!isDiscoDisabled(comp, to)));\n\n//  .getStanzaFrom() != null) && ((packet.getStanzaTo() == null) || ((comp != null) &&\n//  !(comp instanceof DisableDisco)) || isLocalDomain(packet.getStanzaTo()\n//  .toString()))) {\n\t\treturn result;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/MessageRouterConfig.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport java.util.Map;\n\n/**\n * Describe class MessageRouterConfig here.\n * <br>\n * Created: Fri Jan  6 14:54:21 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class MessageRouterConfig {\n\n\tpublic static final String DISCO_NAME_PROP_KEY = \"disco-name\";\n\n\tpublic static final String DISCO_SHOW_VERSION_PROP_KEY = \"disco-show-version\";\n\n\tpublic static final String LOCAL_ADDRESSES_PROP_KEY = \"hostnames\";\n\n\tpublic static final String MSG_RECEIVERS_PROP_KEY = \"components/msg-receivers/\";\n\n\tpublic static final String REGISTRATOR_PROP_KEY = \"components/registrators/\";\n\n\tpublic static final String UPDATES_CHECKING_INTERVAL_PROP_KEY = \"updates-checking-interval\";\n\n\tpublic static final long UPDATES_CHECKING_INTERVAL_PROP_VAL = 7;\n\n\tpublic static final String UPDATES_CHECKING_PROP_KEY = \"updates-checking\";\n\tpublic static final String MSG_RECEIVERS_NAMES_PROP_KEY = MSG_RECEIVERS_PROP_KEY + \"id-names\";\n\tpublic static final Boolean UPDATES_CHECKING_PROP_VAL = true;\n\n\tpublic static final String REGISTRATOR_NAMES_PROP_KEY = REGISTRATOR_PROP_KEY + \"id-names\";\n\n\tpublic static final boolean DISCO_SHOW_VERSION_PROP_VAL = true;\n\n\tpublic static final String DISCO_NAME_PROP_VAL = tigase.server.XMPPServer.NAME;\n\n\tprivate Map<String, Object> props = null;\n\n\tprivate static boolean isTrue(String val) {\n\t\tif (val == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tString value = val.toLowerCase();\n\n\t\treturn (value.equals(\"true\") || value.equals(\"yes\") || value.equals(\"on\") || value.equals(\"1\"));\n\t}\n\n\n\tpublic MessageRouterConfig(Map<String, Object> props) {\n\t\tthis.props = props;\n\n\t\t// System.out.println(\"MessageRouterConfig() properties: \" + props.toString());\n\t}\n}    // MessageRouterConfig\n\n"
  },
  {
    "path": "src/main/java/tigase/server/MessageRouterIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport tigase.conf.ConfigurationException;\nimport tigase.conf.ConfiguratorAbstract;\n\n/**\n * Created: Dec 7, 2009 5:20:56 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface MessageRouterIfc\n\t\textends ServerComponent {\n\n\tvoid setConfig(ConfiguratorAbstract config) throws ConfigurationException;\n\n\tvoid start();\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/Packet.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.impl.annotation.Handle;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\n\n/**\n * Objects of this class carry a single XMPP packet (stanza). The XMPP stanza is carried as an XML element in DOM\n * structure by the Packet object which contains some extra information and convenience methods to quickly access the\n * most important stanza information.<br> The stanza is accessible directly through the <code>getElement()</code> method\n * and then it can be handles as an XML object. <br> <strong>Please note! Even though the <code>Packet</code> object and\n * carried the stanza <code>Element</code> is not unmodifiable it should be treated as such. This particular\n * <code>Packet</code> can be processed concurrently at the same time in different components or plugins of the Tigase\n * server. Modifying it may lead to unexpected and hard to diagnose behaviors. Every time you want to change or update\n * the object you should obtaina a copy of it using one of the utility methods: <code>copyElementOnly()</code>,\n * <code>swapFromTo(...)</code>, <code>errorResult(...)</code>, <code>okResult(...)</code>,\n * <code>swapStanzaFromTo(...)</code></strong><br> There are no public constructors for the class, instead you have to\n * use factory methods: <code>packetInstance(...)</code> which return instance of one of the classes: <code>Iq</code>,\n * <code>Message</code> or <code>Presence</code>. While creating a new <code>Packet</code> instance JIDs are parsed and\n * processed through the stringprep. Hence some of the factory methods may throw <code>TigaseStringprepException</code>\n * exception. You can avoid this by using the methods which accept preparsed JIDs. Reusing preparsed JIDs is highly\n * recommended. <br> There are 3 kinds of addresses available from the <code>Packet</code> object:\n * <em>PacketFrom/To</em>, <em>StanzaFrom/To</em> and <em>From/To</em>.<br> <em>Stanza</em> addresses are the normal\n * XMPP addresses parsed from the XML stanza and as a convenience are available through methods as JID objects. This is\n * not only convenient to the developer but also this is important for performance reasons as parsing JID and processing\n * it through stringprep is quite expensive operation so it is better to do it once and reuse the parsed objects. Please\n * note that any of them can be null. Note also. You should avoid parsing stanza JIDs from the XML element in your code\n * as this may impact the server performance. Reuse the JIDs provided from the <code>Packet</code> methods.<br>\n * <em>Packet</em> addresses are also JID objects but they may contain a different values from the <em>Stanza</em>\n * addresses. These are the Tigase internal addresses used by the server and they usually contain Tigase component\n * source and destination address. In most cases they are used between connection managers and session managers and can\n * be ignored by other code. One advantage of setting <code>PacketFrom</code> address to address of your component\n * (<code>getComponentId()</code>) address is that if there is a packet delivery problem it will be returned back to the\n * sender with apropriate error message.<br> <em>Simple From/To</em> addresses contains values following the logic: If\n * PacketFrom/To is not null then it contains PacketFrom/To values otherwise it contains StanzaFrom/To values. This is\n * because the Tigase server tries always to deliver and process the <code>Packet</code> using PacketFrom/To addresses\n * if they are null then Stanza addresses are used instead. So these are just convenience methods which allow avoiding\n * extra <code>IFs</code> in the program code and also save some CPU cycles.\n * <br>\n * Created: Tue Nov 22 07:07:11 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class Packet {\n\n\tpublic static final String CLIENT_XMLNS = \"jabber:client\";\n\n\tpublic static final String FROM_ATT = \"from\";\n\n\tpublic static final String ID_ATT = \"id\";\n\n\tpublic static final String PERM_ATT = \"perm\";\n\n\tpublic static final String PRIORITY_ATT = \"pr\";\n\n\tpublic static final String TO_ATT = \"to\";\n\n\tpublic static final String TYPE_ATT = \"type\";\n\n\tpublic static final String XMLNS_ATT = \"xmlns\";\n\n\tpublic static final String ERROR_NS = \"urn:ietf:params:xml:ns:xmpp-stanzas\";\n\n\t/**\n\t * The variable control whether the toStringSecure() hides all the CData information from stanzas printed to logs or\n\t * logs the full, detailed stanza content. By default the variable is set to 'false' to protect users' privacy and\n\t * not reveal chat content. This is the value to be used in all production/live systems. For the debug purposes on\n\t * the test or development system it can be set to 'true' to help diagnose run-time problems.<br> You can change\n\t * value of the field by setting system property: <code>'packet.debug.full'</code> to <code>'true'</code>.\n\t */\n\tpublic static boolean FULL_DEBUG = Boolean.getBoolean(\"packet.debug.full\");\n\n\tprotected Element elem;\n\tprivate JID packetFrom = null;\n\tprivate JID packetTo = null;\n\tprivate String packetToString = null;\n\tprivate String packetToStringSecure = null;\n\tprivate Permissions permissions = Permissions.NONE;\n\tprivate Priority priority = Priority.NORMAL;\n\tprivate Set<String> processorsIds = new LinkedHashSet<String>(4, 0.9f);\n\tprivate boolean routed;\n\tprivate LinkedHashSet<String> skippedProcessorsIds = new LinkedHashSet<String>(4, 0.9f);\n\tprivate JID stanzaFrom = null;\n\tprivate String stanzaId = null;\n\tprivate JID stanzaTo = null;\n\tprivate StanzaType type;\n\tprivate String stableId = null;\n\tprivate Optional<JID> serverAuthorisedStanzaFrom = Optional.empty();\n\n\t/**\n\t * Method trims {@link Element} stanza to 1024 characters and returns String representation of the element\n\t *\n\t * @param el Element which should be converted\n\t *\n\t */\n\tpublic static String elemToString(Element el) {\n\t\tString elemData = el.toString();\n\t\tint size = elemData.length();\n\n\t\tif (size > 1024) {\n\t\t\telemData = elemData.substring(0, 1024) + \" ... \";\n\t\t}\n\n\t\treturn elemData;\n\t}\n\n\t/**\n\t * Method trims {@link Element} stanza to 1024 characters and returns String representation of the element. This\n\t * version uses secure representation of CData of the elements.\n\t *\n\t * @param el Element which should be converted\n\t *\n\t */\n\tpublic static String elemToStringSecure(Element el) {\n\t\tString elemData = el.toStringSecure();\n\t\tint size = elemData.length();\n\n\t\tif (size > 1024) {\n\t\t\telemData = elemData.substring(0, 1024) + \" ... \";\n\t\t}\n\n\t\treturn elemData;\n\t}\n\n\t/**\n\t * The method returns <code>Packet</code> instance. More specifically it returns instance of one of the following\n\t * classes: <code>Iq</code>, <code>Message</code> or <code>Presence</code>. It takes stanza XML element as an\n\t * arguments, parses some the most commonly used data and created an object. Pre-parsed information are: stanza\n\t * from/to addresses, stanza id, type and presets the <code>Packet</code> priority.<br> If there is a stringprep\n\t * processing error for either the stanza source or destination address <code>TigaseStringprepException</code>\n\t * exception is thrown.\n\t *\n\t * @param elem is a stanza XML <code>Element</code>\n\t *\n\t * @return a <code>Packet</code> instance, more specifically instance of one of the following classes:\n\t * <code>Iq</code>, <code>Message</code> or <code>Presence</code>.\n\t *\n\t * @throws TigaseStringprepException if there is stanza from or to address parsing error.\n\t */\n\tpublic static Packet packetInstance(Element elem) throws TigaseStringprepException {\n\t\tPacket result = null;\n\n\t\tif (elem.getName() == Message.ELEM_NAME) {\n\t\t\tresult = new Message(elem);\n\t\t}\n\t\tif (elem.getName() == Presence.ELEM_NAME) {\n\t\t\tresult = new Presence(elem);\n\t\t}\n\t\tif (elem.getName() == Iq.ELEM_NAME) {\n\t\t\tresult = new Iq(elem);\n\t\t}\n\t\tif (result == null) {\n\t\t\tresult = new Packet(elem);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * The method returns <code>Packet</code> instance. More specifically it returns instance of one of the following\n\t * classes: <code>Iq</code>, <code>Message</code> or <code>Presence</code>. It takes stanza XML element as an\n\t * arguments and pre-parsed stanza from and to addresses. The method parses some other, the most commonly used data\n\t * and created an object. Pre-parsed information are: stanza id, type and presets the <code>Packet</code>\n\t * priority.<br> This method does not parses stanza from and stanza to address from the given XML document, hence it\n\t * does not throw <code>TigaseStringprepException</code>. Even though reusing parsed from and to address is highly\n\t * recommended an extra care is needed to pass correct parameters as stanza JIDs or the packet may be incorrectly\n\t * routed or processed.\n\t *\n\t * @param elem is the stanza XML <code>Element</code>\n\t * @param stanzaFrom is a pre-parsed <code>JID</code> instance from the given stanza XML element.\n\t * @param stanzaTo is a pre-parsed <code>JID</code> instance from the given stanza XML element.\n\t *\n\t * @return a <code>Packet</code> instance, more specifically instance of one of the following classes:\n\t * <code>Iq</code>, <code>Message</code> or <code>Presence</code>.\n\t */\n\tpublic static Packet packetInstance(Element elem, JID stanzaFrom, JID stanzaTo) {\n\t\tPacket result = null;\n\n\t\tif (elem.getName() == Message.ELEM_NAME) {\n\t\t\tresult = new Message(elem, stanzaFrom, stanzaTo);\n\t\t}\n\t\tif (elem.getName() == Presence.ELEM_NAME) {\n\t\t\tresult = new Presence(elem, stanzaFrom, stanzaTo);\n\t\t}\n\t\tif (elem.getName() == Iq.ELEM_NAME) {\n\t\t\tresult = new Iq(elem, stanzaFrom, stanzaTo);\n\t\t}\n\t\tif (result == null) {\n\t\t\tresult = new Packet(elem, stanzaFrom, stanzaTo);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * The method creates XML stanza from given parameters and returns <code>Packet</code> instance for this XML stanza.\n\t * More specifically it returns instance of one of the following classes: <code>Iq</code>, <code>Message</code> or\n\t * <code>Presence</code>. <br> The method first builds an XML stanza from given parameters: element name, from and\n\t * to addresses and stanza type, then it creates a Packet instance for the stanza. It also runs all the parsing and\n\t * stringprep processing, hence it throws an exception if any error is found.\n\t *\n\t * @param el_name XML stanza element name as <code>String</code>.\n\t * @param from is the stanza <strong>from</strong> address as <code>String</code>\n\t * @param to is the stanza <strong>to</strong> address as <code>String</code>.\n\t * @param type is one of the stanza types: <strong>set</strong>, <strong>get</strong>, <strong>result</strong>, ....\n\t * as <code>StanzaType</code> instance.\n\t *\n\t * @return a <code>Packet</code> instance, more specifically instance of one of the following classes:\n\t * <code>Iq</code>, <code>Message</code> or <code>Presence</code>.\n\t *\n\t * @throws TigaseStringprepException if there is stanza from or to address parsing error.\n\t */\n\tpublic static Packet packetInstance(String el_name, String from, String to, StanzaType type)\n\t\t\tthrows TigaseStringprepException {\n\t\tElement elem = new Element(el_name, new String[]{FROM_ATT, TO_ATT, TYPE_ATT},\n\t\t\t\t\t\t\t\t   new String[]{from, to, type.toString()});\n\n\t\treturn packetInstance(elem);\n\t}\n\n\t/**\n\t * A constructor creating the <code>Packet</code> instance. This is not part of the public API, please use\n\t * <code>packetInstance(...)</code> instead.\n\t *\n\t * @param elem is XML element with a single XMPP stanza.\n\t *\n\t * @throws TigaseStringprepException exception is thrown if the stanza source or destination address stringprep\n\t * processing failed.\n\t */\n\tprotected Packet(final Element elem) throws TigaseStringprepException {\n\t\tsetElem(elem);\n\t\tinitVars();\n\t}\n\n\t/**\n\t * A constructor creating the <code>Packet</code> instance. This is not part of the public API, please use\n\t * <code>packetInstance(...)</code> instead.\n\t *\n\t * @param elem is XML element with a single XMPP stanza.\n\t * @param stanzaFrom is a source JID address of the stanza passed as the constructor parameter.\n\t * @param stanzaTo is a destination JID address of the stanza passed as the constructor parameter.\n\t */\n\tprotected Packet(final Element elem, JID stanzaFrom, JID stanzaTo) {\n\t\tsetElem(elem);\n\t\tinitVars(stanzaFrom, stanzaTo);\n\t}\n\n\t/**\n\t * <code>copyElementOnly</code> method creates a copy of the packet with stanza information copied only. The\n\t * <code>Packet</code> specific information stays blank (NULL): (packetFrom, packetTo, etc...).<br> This method\n\t * should be used to obtain a copy of the packet without setting packet specific fields (packetFrom or packetTo).\n\t * The method reuses preparsed stanza JIDs and does not throw any exception.\n\t *\n\t * @return a new copy of the packet with packet specific fields set to NULL.\n\t */\n\tpublic Packet copyElementOnly() {\n\t\tElement res_elem = elem.clone();\n\t\tPacket result = packetInstance(res_elem, getStanzaFrom(), getStanzaTo());\n\n\t\tresult.setPriority(priority);\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Method returns a string representation of all the data enclosed by the <code>Packet</code> instance. All stanza\n\t * XML element and all fields are converted to the <code>String</code> representation for debugging. Please note,\n\t * this may be resources consuming process so use it only when experiencing problems with <code>Packet</code>\n\t * content.\n\t *\n\t * @return <code>String</code> representation of the packet with all its fields.\n\t */\n\tpublic String debug() {\n\t\treturn toString() + \", stanzaFrom=\" + stanzaFrom + \", stanzaTo=\" + stanzaTo;\n\t}\n\n\t/**\n\t * Method returns a modified copy of the <code>Packet</code> with its stanza as stanza error used for reporting\n\t * errors. It is recommended not to use this method directly as there is a utility class which makes generating\n\t * error responses much simpler. An example call (which uses this method underneath) looks like this example:\n\t * <pre>\n\t * import tigase.xmpp.Authorization;\n\t * Authorization.BAD_REQUEST.getResponseMessage(packet, \"Error message\", true/false);\n\t * </pre>\n\t * This utility class and it's method acts not only as a convenience but also provides some additional checking and\n\t * control.\n\t *\n\t * @param errorType is a <code>String</code> representation of the error type defined in the XMPP RFC-3920.\n\t * @param errorCode is an integer error code defined in the XMPP RFC for backward compatibility with old Jabber\n\t * implementations.\n\t * @param errorCondition is a <code>String</code> representation of the error condition defined in the XMPP\n\t * RFC-3920.\n\t * @param errorText human readable error message.\n\t * @param includeOriginalXML a boolean parameter indicating whether stanza top element children should be included\n\t * in the error message.\n\t *\n\t * @return a new <code>Packet</code> instance with an error type stanza which is a response to this\n\t * <code>Packet</code> instance.\n\t */\n\tpublic Packet errorResult(final String errorType, final Integer errorCode, final String errorCondition,\n\t\t\t\t\t\t\t  final String errorText, final boolean includeOriginalXML) {\n\t\tElement reply = new Element(elem.getName());\n\n\t\treply.setAttribute(TYPE_ATT, StanzaType.error.toString());\n\t\tif (getStanzaId() != null) {\n\t\t\treply.setAttribute(ID_ATT, getStanzaId());\n\t\t}    // end of if (getElemId() != null)\n\t\tif (includeOriginalXML) {\n\t\t\treply.addChildren(elem.getChildren());\n\t\t}    // end of if (includeOriginalXML)\n\t\tif (getXMLNS() != null) {\n\t\t\treply.setXMLNS(getXMLNS());\n\t\t}\n\n\t\tElement error = new Element(\"error\");\n\n\t\tif (errorCode != null) {\n\t\t\terror.setAttribute(\"code\", errorCode.toString());\n\t\t}\n\t\terror.setAttribute(TYPE_ATT, errorType);\n\n\t\tElement cond = new Element(errorCondition);\n\n\t\tcond.setXMLNS(ERROR_NS);\n\t\terror.addChild(cond);\n\t\tif (errorText != null) {\n\t\t\tElement t = new Element(\"text\", errorText, new String[]{\"xml:lang\", \"xmlns\"}, new String[]{\"en\", ERROR_NS});\n\n\t\t\terror.addChild(t);\n\t\t}    // end of if (text != null && text.length() > 0)\n\t\treply.addChild(error);\n\n\t\treturn swapFromTo(reply, getStanzaTo(), getStanzaFrom());\n\t}\n\n\tpublic Element getElemChild(String name) {\n\t\treturn elem.getChild(name);\n\t}\n\n\tpublic Element getElemChild(String name, String xmlns) {\n\t\treturn elem.getChild(name, xmlns);\n\t}\n\n\t/**\n\t * A convenience method for accessing stanza top element attributes. This call is equal to the call:\n\t * <pre>\n\t * packet.getElement().getAttribute(key);\n\t * </pre>\n\t *\n\t * @param key is an attribute key.\n\t *\n\t * @return an attribute value or NULL if there is no such attribute.\n\t */\n\tpublic String getAttributeStaticStr(String key) {\n\t\treturn elem.getAttributeStaticStr(key);\n\t}\n\n\t/**\n\t * A convenience method for accessing stanza top level or any of it's children attribute. This call is equal to the\n\t * call:\n\t * <pre>\n\t * packet.getElement().getAttribute(xmlPath, key);\n\t * </pre>\n\t * <strong>Please note! This method can only be used with static strings or with strings processed through\n\t * <code>String.intern()</code> call. It uses \"==\" for string comparison for performance reasons.</strong>\n\t *\n\t * @param path is XML path for the stanza element or stanza child for which attribute is retrieved.\n\t * @param key is an attribute key.\n\t *\n\t * @return value of the requested attribute or NULL if the attribute is not set.\n\t */\n\tpublic String getAttributeStaticStr(String[] path, String key) {\n\t\treturn elem.getAttributeStaticStr(path, key);\n\t}\n\n\t/**\n\t * The method always returns NULL. It is overwritten in the {@link Iq} class where it returns a command identifier\n\t * if the {@code iq} stanza represents an ad-hoc command. It is provided here is a convenience so the developer does\n\t * not have to cast the packet to IQ before retrieving the command id.\n\t *\n\t * @return the method always returns a NULL.\n\t */\n\tpublic Command getCommand() {\n\t\treturn null;\n\t}\n\n\t/**\n\t * Method returns character data from the enclosed stanza for a given stanza element or child pointed by the\n\t * <code>xmlPath</code> parameter. This call is equal to the call:\n\t * <pre>\n\t * packet.getElement().getCData(xmlPath);\n\t * </pre>\n\t * <strong>Please note! This method can only be used with static strings or with strings processed through\n\t * <code>String.intern()</code> call. It uses \"==\" for string comparison for performance reasons.</strong>\n\t *\n\t * @param xmlPath is an XML path to the stanza element for which CData is retrieved.\n\t *\n\t * @return CData for a given element or NULL if the element does not exist or there is no CData for the element.\n\t */\n\tpublic String getElemCDataStaticStr(String[] xmlPath) {\n\t\treturn elem.getCDataStaticStr(xmlPath);\n\t}\n\n\t/**\n\t * Method return character data for the stanza top element. This call is equal to the call:\n\t * <pre>\n\t * packet.getElement().getCData();\n\t * </pre>\n\t *\n\t * @return CData or from the stanza top element or NULL if there is no CData for the element.\n\t */\n\tpublic String getElemCData() {\n\t\treturn elem.getCData();\n\t}\n\n\t/**\n\t * Method returns a list of all XML children from the enclosed stanza for a given stanza element or child pointed by\n\t * the <code>xmlPath</code> parameter. This call is equal to the call:\n\t * <pre>\n\t * packet.getElement().getChildrenStaticStr(xmlPath);\n\t * </pre>\n\t * <strong>Please note! This method can only be used with static strings or with strings processed through\n\t * <code>String.intern()</code> call. It uses \"==\" for string comparison for performance reasons.</strong>\n\t *\n\t * @param xmlPath is an XML path to the stanza element for which children are retrieved.\n\t *\n\t * @return children list for a given element or NULL if the element does not exist or there is no children for the\n\t * element.\n\t */\n\tpublic List<Element> getElemChildrenStaticStr(String[] xmlPath) {\n\t\treturn elem.getChildrenStaticStr(xmlPath);\n\t}\n\n\t/**\n\t * Convenience method for retrieving the stanza top element name. This call is equal to the call:\n\t * <pre>\n\t * packet.getElement().getName();\n\t * </pre>\n\t *\n\t * @return the stanza top element name.\n\t */\n\tpublic String getElemName() {\n\t\treturn elem.getName();\n\t}\n\n\t/**\n\t * Method returns the stanza XML element in DOM format.\n\t *\n\t * @return the stanza XML element in DOM format.\n\t */\n\tpublic Element getElement() {\n\t\treturn elem;\n\t}\n\n\t/**\n\t * Method parses the stanza and returns the error condition if there is any.\n\t *\n\t * @return parsed stanza error condition or NULL if there is not error condition.\n\t */\n\tpublic String getErrorCondition() {\n\t\tList<Element> children = elem.getChildrenStaticStr(getElNameErrorPath());\n\n\t\tif (children != null) {\n\t\t\tfor (Element cond : children) {\n\t\t\t\tif (!cond.getName().equals(\"text\")) {\n\t\t\t\t\treturn cond.getName();\n\t\t\t\t}    // end of if (!cond.getName().equals(\"text\"))\n\t\t\t}      // end of for (Element cond: children)\n\t\t}        // end of if (children == null) else\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Returns the packet source address. The method works as a following code:\n\t * <pre>\n\t * return (packetFrom != null) ? packetFrom : stanzaFrom;\n\t * </pre>\n\t *\n\t * @return a <code>JID</code> instance of the packet source address or NULL if neither the packet source address is\n\t * set nor the stanza source address is set.\n\t */\n\tpublic JID getFrom() {\n\t\treturn (packetFrom != null) ? packetFrom : stanzaFrom;\n\t}\n\n\t/**\n\t * Returns the packet internal source address.\n\t *\n\t * @return a {@link JID} instance of the packet internal source address or NULL if the packet internal source\n\t * address has not been set\n\t */\n\tpublic JID getPacketFrom() {\n\t\treturn this.packetFrom;\n\t}\n\n\t/**\n\t * The method sets a source address for the <code>Packet</code> instance.\n\t *\n\t * @param from is a <code>JID</code> instance of the packet new source address.\n\t */\n\tpublic void setPacketFrom(JID from) {\n\t\tthis.packetFrom = from;\n\t}\n\n\t/**\n\t * Returns the packet internal destination address.\n\t *\n\t * @return a {@link JID} instance of the packet internal destination address or NULL if the packet internal\n\t * destination address has not been set.\n\t */\n\tpublic JID getPacketTo() {\n\t\treturn this.packetTo;\n\t}\n\n\t/**\n\t * The method sets a destination address for the <code>Packet</code> instance.\n\t *\n\t * @param to is a <code>JID</code> instance of the packet new destination address.\n\t */\n\tpublic void setPacketTo(JID to) {\n\t\tthis.packetTo = to;\n\t}\n\n\t/**\n\t * Method returns permissions set of the user who has sent the packet. Some packets carry ad-hoc commands which can\n\t * change server parameters, configuration or can contains other administration commands. Such commands are not\n\t * executed if the packet sender does not have enough permissions.\n\t *\n\t * @return a sender permissions set.\n\t */\n\tpublic Permissions getPermissions() {\n\t\treturn permissions;\n\t}\n\n\t/**\n\t * The method sets permissions for the packet of a user who sent the stanza.\n\t *\n\t * @param perm is <code>Permissions</code> instance of the stanza sender permissions calculated by the session\n\t * manager.\n\t */\n\tpublic void setPermissions(Permissions perm) {\n\t\tpacketToString = null;\n\t\tpacketToStringSecure = null;\n\t\tpermissions = perm;\n\t}\n\n\t/**\n\t * Method returns the packet priority. For more details please refer to {@link Priority} enumeration.\n\t *\n\t * @return the packet priority.\n\t */\n\tpublic Priority getPriority() {\n\t\treturn priority;\n\t}\n\n\t/**\n\t * The method sets the packet priority. Depending on the priority the packet is put to a queue with corresponding\n\t * priority. This matter only on system which experience overload and some packets may be delivered with a delay if\n\t * they are low priority packets.\n\t *\n\t * @param priority is a new <code>Priority</code> instance set for the packet.\n\t */\n\tpublic void setPriority(Priority priority) {\n\t\tthis.priority = priority;\n\t}\n\n\t/**\n\t * Method returns a set of all processor IDs which processed the packet. Each session manager processor which\n\t * handles the packet can mark the packet as processed. This is used internally by the session manager to detect\n\t * packets which hasn't been processed by any processor, hence a default action is applied to the packet if\n\t * possible.\n\t *\n\t * @return a <code>Set</code> of stanza processor IDs which handled the packet.\n\t */\n\tpublic Set<String> getProcessorsIds() {\n\t\treturn processorsIds;\n\t}\n\n\t/**\n\t * Method returns JID from-address that was authenticated and bound to user session.\n\t *\n\t * This is a helper method that facilitates and improve maintaining correct stanza from when\n\t * communicating between ClientConnectionManager and SessionManager.\n\t *\n\t * This is a temporary solution! In version 9.0, after reviewing the APIs and correcting clustering strategy\n\t * packets incomming in (Client) Connection Managers should already have correct 'from' attribute in stanzas\n\t * set in {@code tigase.server.xmppclient.ClientConnectionManager#processSocketData(tigase.xmpp.XMPPIOService)}.\n\t *\n\t * @return {@code Optional} that may contain user JID if the session was already authorised.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\")\n\tpublic Optional<JID> getServerAuthorisedStanzaFrom() {\n\t\treturn serverAuthorisedStanzaFrom;\n\t}\n\n\t/**\n\t * Method used to set JID from-address that was authenticated and bound to user session.\n\t *\n\t * This is a helper method that facilitates and improve maintaining correct stanza from when\n\t * communicating between ClientConnectionManager and SessionManager.\n\t *\n\t * This is a temporary solution! In version 9.0, after reviewing the APIs and correcting clustering strategy\n\t * packets incomming in (Client) Connection Managers should already have correct 'from' attribute in stanzas\n\t * set in {@code tigase.server.xmppclient.ClientConnectionManager#processSocketData(tigase.xmpp.XMPPIOService)}.\n\t *\n\t * @param serverAuthorisedStanzaFrom\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\")\n\tpublic void setServerAuthorisedStanzaFrom(JID serverAuthorisedStanzaFrom) {\n\t\tthis.serverAuthorisedStanzaFrom = Optional.ofNullable(serverAuthorisedStanzaFrom);\n\t\tthis.packetToString = null;\n\t\tthis.packetToStringSecure = null;\n\t}\n\n\t/**\n\t * Method returns a set of all processor IDs which skipped processing packets.\n\t *\n\t * @return a <code>Set</code> of stanza processor IDs which skipped the packet.\n\t */\n\tpublic Set<String> getSkippedProcessorsIds() {\n\t\treturn skippedProcessorsIds;\n\t}\n\n\t/**\n\t * Method returns source address of the stanza enclosed by this packet.\n\t *\n\t * @return a <code>JID</code> instance of the stanza source address or NULL if the source address has not been set\n\t * for the stanza.\n\t */\n\tpublic JID getStanzaFrom() {\n\t\treturn stanzaFrom;\n\t}\n\n\tpublic String getStableId() {\n\t\treturn stableId;\n\t}\n\n\tpublic void setStableId(String stableId) {\n\t\tthis.stableId = stableId;\n\t\tpacketToString = null;\n\t\tpacketToStringSecure = null;\n\t}\n\n\t/**\n\t * Method returns the stanza ID if set.\n\t *\n\t * @return a <code>String</code> representation of the stanza ID or NULL if the ID has not been set for the stanza.\n\t */\n\tpublic String getStanzaId() {\n\t\treturn stanzaId;\n\t}\n\n\t/**\n\t * Method returns destination address of the stanza enclosed by this packet.\n\t *\n\t * @return a <code>JID</code> instance of the stanza destination address or NULL if the destination address has not\n\t * been set for the stanza.\n\t */\n\tpublic JID getStanzaTo() {\n\t\treturn stanzaTo;\n\t}\n\n\t/**\n\t * Returns the packet destination address. The method works as a following code:\n\t * <pre>\n\t * return (packetTo != null) ? packetTo : stanzaTo;\n\t * </pre>\n\t *\n\t * @return a <code>JID</code> instance of the packet destination address or NULL if neither the packet destination\n\t * address is set nor the stanza destination address is set.\n\t */\n\tpublic JID getTo() {\n\t\treturn (packetTo != null) ? packetTo : stanzaTo;\n\t}\n\n\t/**\n\t * Method returns the stanza type parsed from the top XML element of the enclosed stanza.\n\t *\n\t * @return a <code>StanzaType</code> instance of the stanza type parsed from the top XML element of the enclosed\n\t * stanza or NULL of the type has not been set.\n\t */\n\tpublic StanzaType getType() {\n\t\treturn type;\n\t}\n\n\t/**\n\t * Returns the enclosed stanza top element XMLNS. This call is equal to the call:\n\t * <pre>\n\t * packet.getElement().getXMLNS();\n\t * </pre>\n\t *\n\t * @return a <code>String</code> instance of the stanza top element XMLNS.\n\t */\n\tpublic String getXMLNS() {\n\t\treturn elem.getXMLNS();\n\t}\n\n\t/**\n\t * Method allows to set-force XMLNS for the element. This is mostly used in cases where there is no XMLNS provided\n\t * for the element (by the client for example) and then a default one is used. However, in some contexts a default\n\t * XMLNS might be confusing such as when the packet is passed between s2s to c2s connection and the default XMLNS\n\t * changes.\n\t *\n\t * @param xmlns a valid XMLNS string for the element.\n\t */\n\tpublic void setXMLNS(String xmlns) {\n\t\telem.setXMLNS(xmlns);\n\t\tpacketToString = null;\n\t\tpacketToStringSecure = null;\n\t}\n\n\t/**\n\t * The method allows for re-syncing stanza JIDs stored in the packet with the attributes of the stanza if they have\n\t * been changed for any reason. <strong>Method mostly used internally only.</strong> Normally stanza carried by this\n\t * Packet instance must not be changed, however there are rare occasions when it has to be changed. RFC requires\n\t * that the server adds missing <em>'from'</em> attribute to every packet sent by the user. It would be highly\n\t * inefficient to create a new instance of the data just to add the missing from address. In such a case SM adds\n\t * missing attribute but then stanza preparsed JIDs stored in the packet are out of sync with the enclosed stanza.\n\t * This method allows for setting correct stanza JIDs for the packet fields without a need to reparse the stanza.\n\t *\n\t * @param stanzaFrom is a parsed source address JID from the stanza enclosed by this packet.\n\t * @param stanzaTo is a parsed destination address JID from the stanza enclosed by this packet.\n\t */\n\tpublic void initVars(JID stanzaFrom, JID stanzaTo) {\n\t\tif (this.stanzaFrom != stanzaFrom) {\n\t\t\tthis.stanzaFrom = stanzaFrom;\n\t\t\tif (stanzaFrom == null) {\n\t\t\t\telem.removeAttribute(FROM_ATT);\n\t\t\t} else {\n\t\t\t\telem.setAttribute(FROM_ATT, stanzaFrom.toString());\n\t\t\t}\n\t\t}\n\t\tif (this.stanzaTo != stanzaTo) {\n\t\t\tthis.stanzaTo = stanzaTo;\n\t\t\tif (stanzaTo == null) {\n\t\t\t\telem.removeAttribute(TO_ATT);\n\t\t\t} else {\n\t\t\t\telem.setAttribute(TO_ATT, stanzaTo.toString());\n\t\t\t}\n\t\t}\n\t\tstanzaId = elem.getAttributeStaticStr(ID_ATT);\n\t\tpacketToString = null;\n\t\tpacketToStringSecure = null;\n\t}\n\n\t/**\n\t * The method allows for re-syncing/parsing stanza JIDs stored in the packet with the attributes of the stanza if\n\t * they have been changed for any reason. <strong>Method mostly used internally only.</strong> Normally stanza\n\t * carried by this Packet instance must not be changed, however there are rare occasions when it is needed. RFC\n\t * requires that the server adds missing <em>'from'</em> attribute to every packet sent by the user. It would be\n\t * highly inefficient to create a new instance of the data just to add the missing from address. In such a case SM\n\t * adds missing attribute but then stanza pre-parsed JIDs stored in the packet are out of sync with the enclosed\n\t * stanza. This method causes stanza JIDs re-parsing and setting the packet variables.\n\t *\n\t * @throws TigaseStringprepException if the stringprep error occurs during the stanza JIDs parsing.\n\t */\n\tpublic void initVars() throws TigaseStringprepException {\n\t\tString tmp = elem.getAttributeStaticStr(TO_ATT);\n\n\t\tif (tmp != null) {\n\t\t\tstanzaTo = JID.jidInstance(tmp);\n\t\t} else {\n\t\t\tstanzaTo = null;\n\t\t}\n\t\ttmp = elem.getAttributeStaticStr(FROM_ATT);\n\t\tif (tmp != null) {\n\t\t\tstanzaFrom = JID.jidInstance(tmp);\n\t\t} else {\n\t\t\tstanzaFrom = null;\n\t\t}\n\t\tstanzaId = elem.getAttributeStaticStr(ID_ATT);\n\t\tpacketToString = null;\n\t\tpacketToStringSecure = null;\n\t\ttmp = elem.getAttributeStaticStr(PRIORITY_ATT);\n\t\tif (tmp != null) {\n\t\t\tpriority = Priority.valueOf(tmp);\n\t\t}\n\t\ttmp = elem.getAttributeStaticStr(PERM_ATT);\n\t\tif (tmp != null) {\n\t\t\tpermissions = Permissions.valueOf(tmp);\n\t\t}\n\t}\n\n\t/**\n\t * The method checks whether the stanza enclosed by this <code>Packet</code> instance is an ad-hoc command. This is\n\t * a generic method which in fact always returns <code>false</code>. It is overwritten in the <code>Iq</code> class\n\t * where the real checking is performed. This class has been provided as a convenience method to perform the check\n\t * without a need for casting the <code>Packet</code> instance to the <code>Iq</code> class.\n\t *\n\t * @return a <code>boolean</code> value <code>true</code> if the stanza is an ad-hoc command and <code>false</code>\n\t * otherwise.\n\t */\n\tpublic boolean isCommand() {\n\t\treturn false;\n\t}\n\n\t/**\n\t * The method checks whether the enclosed stanza is a specific XML element. That is, it checks whether the stanza\n\t * element name and XMLNS is exactly the same as given parameters. This is a convenience method which logic is equal\n\t * to the code below: {@code return packet.getElement().getName() == name && packet.getElement().getXMLNS() ==\n\t * xmlns; }\n\t *\n\t * @param name is a <code>String</code> representing the XML element name.\n\t * @param xmlns is a <code>String</code> representing the XML xmlns value.\n\t *\n\t * @return {@code true} if stanza element name and XMLNS is exactly the same as given parameters, {@code false}\n\t * otherwise.\n\t */\n\tpublic boolean isElement(String name, String xmlns) {\n\t\treturn (elem.getName() == name) && (xmlns == elem.getXMLNS());\n\t}\n\n\t/**\n\t * Method determines whether the stanza represents so called <em>routed</em> packet. A routed packet is a packet\n\t * created by a component responsible for Communication with external components. In certain work mode it can send\n\t * over the link the whole packet information with all internal states and addresses. Such a packet also encloses\n\t * original stanza with all it's attributes.\n\t *\n\t * @return a <code>boolean</code> value of <code>true</code> if the packet is routed and <code>false</code>\n\t * otherwise.\n\t */\n\tpublic boolean isRouted() {\n\t\treturn routed;\n\t}\n\n\t/**\n\t * A convenience method which checks whether the enclosed stanza is a service discovery query. This is a generic\n\t * method which in fact always returns <code>false</code>. It is overwritten in the <code>Iq</code> class where the\n\t * real checking is performed. This class has been provided as a convenience method to perform the check without a\n\t * need for casting the <code>Packet</code> instance to the <code>Iq</code> class.\n\t *\n\t * @return a <code>boolean</code> value <code>true</code> if the stanza is a a service discovery query and\n\t * <code>false</code> otherwise.\n\t */\n\tpublic boolean isServiceDisco() {\n\t\treturn false;\n\t}\n\n\t/**\n\t * The method checks whether the enclosed stanza contains an XML element and XML child element for a given element\n\t * path and XMLNS. The <code>elementPath</code> is directory path like string. <strong>Please note! This method can\n\t * only be used with static strings or with strings processed through <code>String.intern()</code> call. It uses\n\t * \"==\" for string comparison for performance reasons.</strong>\n\t *\n\t * @param elementPath is a <code>String[]</code> value which represents XML element path to a desired child\n\t * element.\n\t * @param xmlns is a <code>String</code> value which represents XML XMLNS.\n\t *\n\t * @return a <code>true</code> is element given in parameters is found in the packet stanza, otherwise\n\t * <code>false</code>\n\t */\n\tpublic boolean isXMLNSStaticStr(String[] elementPath, String xmlns) {\n\t\tif (Handle.ANY_XMLNS.equals(xmlns)) {\n\t\t\treturn true;\n\t\t}\n\t\tString this_xmlns = elem.getXMLNSStaticStr(elementPath);\n\n\t\treturn (this_xmlns == xmlns);\n\t}\n\n\t/**\n\t * The method marks that the packet has NOT been processed by a packet processor with a given ID.\n\t *\n\t * @param id is a <code>String</code> instance of the packet processer identifier.\n\t */\n\tpublic void notProcessedBy(String id) {\n\t\tskippedProcessorsIds.add(id);\n\t}\n\n\t/**\n\t * Method returns a modified copy of the <code>Packet</code> with its stanza as stanza <code>result</code> used for\n\t * reporting <em>IQ</em> stanza results. The method preserves all the attributes of the original stanza, swaps\n\t * stanza source and destination addresses and can optionally add more child XML elements and can preserve existing\n\t * children elements up to given depth.\n\t *\n\t * @param includeXML is an XML content serialized to <code>String</code> or just character data as\n\t * <code>String</code> which has to be added to response stanza.\n\t * @param originalXML parameter specified whether and if so to what depth the original stanza child elements have to\n\t * be preserved in the response packet.\n\t *\n\t * @return a new <code>Packet</code> instance with an OK (result) type stanza which is a response to this\n\t * <code>Packet</code> instance.\n\t */\n\tpublic Packet okResult(final String includeXML, final int originalXML) {\n\t\tElement reply = new Element(elem.getName());\n\n\t\tif (getXMLNS() != null) {\n\t\t\treply.setXMLNS(getXMLNS());\n\t\t}\n\t\treply.setAttribute(TYPE_ATT, StanzaType.result.toString());\n\t\tif (getStanzaId() != null) {\n\t\t\treply.setAttribute(ID_ATT, getStanzaId());\n\t\t}    // end of if (getElemId() != null)\n\n\t\tElement old_child = elem;\n\t\tElement new_child = reply;\n\n\t\tfor (int i = 0; i < originalXML; i++) {\n\t\t\tfinal List<Element> old_children = old_child.getChildren();\n\t\t\tif (old_children != null && old_children.size() > 0) {\n\t\t\t\told_child = old_children.get(0);\n\t\t\t\tElement tmp = new Element(old_child.getName());\n\t\t\t\ttmp.setAttributes(old_child.getAttributes());\n\t\t\t\tnew_child.addChild(tmp);\n\t\t\t\tnew_child = tmp;\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}    // end of for (int i = 0; i < originalXML; i++)\n\t\tif (includeXML != null) {\n\t\t\tnew_child.setCData(includeXML);\n\t\t}    // end of if (includeOriginalXML)\n\n\t\tPacket result = swapFromTo(reply, getStanzaTo(), getStanzaFrom());\n\n\t\tresult.setPriority(priority);\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Method returns a modified copy of the <code>Packet</code> with its stanza as stanza <code>result</code> used for\n\t * reporting <em>IQ</em> stanza results. The method preserves all the attributes of the original stanza, swaps\n\t * stanza source and destination addresses and can optionally add more child XML elements and can preserve existing\n\t * children elements up to given depth.\n\t *\n\t * @param includeXML is an XML content which has to be added to the response stanza.\n\t * @param originalXML parameter specified whether and if so to what depth the original stanza child elements have to\n\t * be preserved in the response packet.\n\t *\n\t * @return a new <code>Packet</code> instance with an OK (result) type stanza which is a response to this\n\t * <code>Packet</code> instance.\n\t */\n\tpublic Packet okResult(Element includeXML, int originalXML) {\n\t\tElement reply = new Element(elem.getName());\n\n\t\tif (getXMLNS() != null) {\n\t\t\treply.setXMLNS(getXMLNS());\n\t\t}\n\t\treply.setAttribute(TYPE_ATT, StanzaType.result.toString());\n\t\tif (getStanzaId() != null) {\n\t\t\treply.setAttribute(ID_ATT, getStanzaId());\n\t\t}    // end of if (getElemId() != null)\n\n\t\tElement old_child = elem;\n\t\tElement new_child = reply;\n\n\t\tfor (int i = 0; i < originalXML; i++) {\n\t\t\tfinal List<Element> old_children = old_child.getChildren();\n\t\t\tif (old_children != null && old_children.size() > 0) {\n\t\t\t\told_child = old_children.get(0);\n\t\t\t\tElement tmp = new Element(old_child.getName());\n\t\t\t\ttmp.setAttributes(old_child.getAttributes());\n\t\t\t\tnew_child.addChild(tmp);\n\t\t\t\tnew_child = tmp;\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}    // end of for (int i = 0; i < originalXML; i++)\n\t\tif (includeXML != null) {\n\t\t\tnew_child.addChild(includeXML);\n\t\t}    // end of if (includeOriginalXML)\n\n\t\tPacket result = swapFromTo(reply, getStanzaTo(), getStanzaFrom());\n\n\t\tresult.setPriority(priority);\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Returns a new <code>Packet</code> instance with stanza <em>routed</em> which means an original stanza has been\n\t * enclosed inside a <code>route</code> XML element which contains additional information taken from\n\t * <code>Packet</code> packet instance internal attributes.\n\t *\n\t * @return a new <code>Packet</code> instance with <code>route</code> stanza.\n\t */\n\tpublic Packet packRouted() {\n\t\tElement routedp = new Element(\"route\", new String[]{TO_ATT, FROM_ATT, PRIORITY_ATT, PERM_ATT},\n\t\t\t\t\t\t\t\t\t  new String[]{getTo().toString(), getFrom().toString(), priority.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t   permissions.toString()});\n\n\t\troutedp.addChild(elem);\n\n\t\treturn packetInstance(routedp, getFrom(), getTo());\n\t}\n\n\t/**\n\t * The method marks that the packet has been processed by a packet processor with a given ID.\n\t *\n\t * @param id is a <code>String</code> instance of the packet processer identifier.\n\t */\n\tpublic void processedBy(String id) {\n\t\tprocessorsIds.add(id);\n\t}\n\n\t/**\n\t * The method creates a new instance of the <code>Packet</code> class with the packet source and destination\n\t * addresses swapped and sets the given stanza element plus source and destination addresses for the new stanza.\n\t * This method gives you slightly more flexibility as you can set any source and destination address for the new\n\t * stanza. This method is rarely used in packet processors which don't sent a simple \"ok result\" response. Some data\n\t * flow requires a completely new packet to be send as a response to the original call, but the response has to be\n\t * delivered to the original sends. As an example are the SASL authentication and TLS handshaking.\n\t *\n\t * @param el is an XML element set for the new packet.\n\t * @param stanzaFrom is the stanza source address\n\t * @param stanzaTo is the stanza destination address\n\t *\n\t * @return a new <code>Packet</code> instance.\n\t */\n\tpublic Packet swapFromTo(Element el, JID stanzaFrom, JID stanzaTo) {\n\t\tPacket packet = packetInstance(el, stanzaFrom, stanzaTo);\n\n\t\tpacket.setPacketTo(getFrom());\n\t\tpacket.setPacketFrom(getTo());\n\t\tpacket.setPriority(priority);\n\n\t\treturn packet;\n\t}\n\n\t/**\n\t * Creates a new <code>Packet</code> instance with swapped packet source and destination addresses. Please note the\n\t * new packet contains unchanged copy of the original stanza. Stanza source and destination addresses are no\n\t * swapped.\n\t *\n\t * @return a new {@link Packet} instance.\n\t */\n\tpublic Packet swapFromTo() {\n\t\tElement el = elem.clone();\n\t\tPacket packet = packetInstance(el, getStanzaFrom(), getStanzaTo());\n\n\t\tpacket.setPacketTo(getFrom());\n\t\tpacket.setPacketFrom(getTo());\n\t\tpacket.setPriority(priority);\n\n\t\treturn packet;\n\t}\n\n\t/**\n\t * The method creates a new <code>Packet</code> instance with a stanza copy with swapped source and destination\n\t * addresses. The packet source and destination addresses are set to null.\n\t *\n\t * @return a new <code>Packet</code> instance.\n\t */\n\tpublic Packet swapStanzaFromTo() {\n\t\tElement copy = elem.clone();\n\t\tPacket result = packetInstance(copy, getStanzaTo(), getStanzaFrom());\n\n\t\tresult.setPriority(priority);\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * The method creates a new <code>Packet</code> instance with a stanza copy with swapped source and destination\n\t * addresses and the given type set. The packet source and destination addresses are set to null.\n\t *\n\t * @param type is a new type for the stanza copy to set.\n\t *\n\t * @return a new <code>Packet</code> instance.\n\t */\n\tpublic Packet swapStanzaFromTo(final StanzaType type) {\n\t\tElement copy = elem.clone();\n\n\t\tcopy.setAttribute(TYPE_ATT, type.toString());\n\n\t\tPacket result = packetInstance(copy, getStanzaTo(), getStanzaFrom());\n\n\t\tresult.setPriority(priority);\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * The method converts the <code>Packet</code> instance to a <code>String</code> representation. The stanza XML\n\t * element is presented as the string and all packet attributes are also added to the string. The method is for a\n\t * debugging purposes to log the whole packet content to the debug file for further analysis. It is recommended to\n\t * use <code>toStringSecure()</code> instead as it removes all the CData from the stanza avoiding exposing user chat\n\t * message content. The secure method also preserves you from flooding your log files in case of a huge chunks of\n\t * data are sent in packets (user photos in vCards or files).\n\t *\n\t * @return a <code>String</code> representation of the packet instance.\n\t */\n\tpublic String toStringFull() {\n\t\tif (packetToString == null) {\n\t\t\tString elemData = elemToString(elem);\n\n\t\t\tpacketToString = calcToString(elemData);\n\t\t}\n\n\t\treturn \"from=\" + packetFrom + \", to=\" + packetTo + \", serverAuthorisedStanzaFrom=\" + getServerAuthorisedStanzaFrom() + packetToString;\n\t}\n\n\t/**\n\t * Provides human-readable string presentation of the <code>Packet</code> object. It is not a XMPP stanza only, it\n\t * also contains some Tigase specific meta-data.\n\t *\n\t * @return human-readable string presentation of the <code>Packet</code> object.\n\t */\n\t@Override\n\tpublic String toString() {\n\t\treturn toString(!FULL_DEBUG);\n\t}\n\n\t/**\n\t * Is a convenience method which allows you to call always the same method but change (configure) whether you want\n\t * to get a secure packet string representation or full representation.\n\t *\n\t * @param secure parameter specifies whether the secure packet representation should be returned (<code>true</code>\n\t * value) or the full one (<code>false</code>).\n\t *\n\t * @return a <code>String</code> representation of the packet instance.\n\t */\n\tpublic String toString(boolean secure) {\n\t\tString result;\n\n\t\tif (secure) {\n\t\t\tresult = toStringSecure();\n\t\t} else {\n\t\t\tresult = toStringFull();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * The method returns a <code>String</code> representation of the packet with all CData content replaced with text:\n\t * <em>\"CData size: NN\"</em>. This is a preferable method to log the packets for debuging purposes.\n\t *\n\t * @return a <code>String</code> representation of the packet instance.\n\t */\n\tpublic String toStringSecure() {\n\t\tif (FULL_DEBUG) {\n\t\t\treturn toStringFull();\n\t\t} else {\n\t\t\tif (packetToStringSecure == null) {\n\t\t\t\tString elemData = elemToStringSecure(elem);\n\n\t\t\t\tpacketToStringSecure = calcToString(elemData);\n\t\t\t}\n\n\t\t\treturn \"from=\" + packetFrom + \", to=\" + packetTo + \", serverAuthorisedStanzaFrom=\" + getServerAuthorisedStanzaFrom() + packetToStringSecure;\n\t\t}\n\t}\n\n\t/**\n\t * The method unpacks the original packet and stanza from <code>route</code> stanza. This is the opposite action to\n\t * the <code>packRouted()</code> method.\n\t *\n\t * @return a new instance of the <code>Packet</code> class with unpacket packet and stanza from <code>route</code>\n\t * stanza.\n\t *\n\t * @throws TigaseStringprepException if there was a problem with addresses stringprep processing.\n\t */\n\tpublic Packet unpackRouted() throws TigaseStringprepException {\n\t\tPacket result = packetInstance(elem.getChildren().get(0));\n\n\t\tresult.setPacketTo(getTo());\n\t\tresult.setPacketFrom(getFrom());\n\t\tresult.setPriority(priority);\n\t\tresult.setPermissions(permissions);\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * The method determines whether the packet has been processed by any of the packet processors. In fact it says\n\t * whether there has been called method <code>processedBy(...)</code> on the packet.\n\t *\n\t * @return a <code>boolean</code> value of <code>true</code> of the packet was processed by any processor and\n\t * <code>false</code> otherwise.\n\t */\n\tpublic boolean wasProcessed() {\n\t\treturn processorsIds.size() > 0;\n\t}\n\n\t/**\n\t * The method determines whether the packet was directed to processing by any packet processor, but it wasn't\n\t * processed by them because of internal queue full.\n\t *\n\t * @return <code>true</code> if packet was skipped by any processor.\n\t */\n\tpublic boolean wasSkipped() {\n\t\treturn skippedProcessorsIds.size() > 0;\n\t}\n\n\t/**\n\t * The method checks whether the packet has been processed by a packet processor with the specified ID.\n\t *\n\t * @param id is a <code>String</code> instance of the packet processor identifier.\n\t *\n\t * @return a <code>boolean</code> value of <code>true</code> of the packet was processed by a processor with\n\t * specified ID and <code>false</code> otherwise.\n\t */\n\tpublic boolean wasProcessedBy(String id) {\n\t\treturn processorsIds.contains(id);\n\t}\n\n\t/**\n\t * A convenience method to provide XML path as <code>String[]</code> to error element. This method should be\n\t * override by all classes extending this class.\n\t *\n\t * @return XML path to error element.\n\t */\n\tprotected String[] getElNameErrorPath() {\n\t\treturn new String[]{elem.getName(), \"error\"};\n\t}\n\n\t/**\n\t * Common method for creating debugging string representation of {@code Packet} objects pre-processed either by\n\t * regular or secure {@code toString()} {@link Element} methods\n\t *\n\t * @param elemData string representation of the Element\n\t *\n\t * @return debug string representation with additional data.\n\t */\n\tprivate String calcToString(String elemData) {\n\t\treturn \", DATA=\" + elemData + \", SIZE=\" + elem.toString().length() + \", XMLNS=\" + elem.getXMLNS() +\n\t\t\t\t\", PRIORITY=\" + priority + \", PERMISSION=\" + permissions + \", TYPE=\" + type + \", STABLE_ID=\" + stableId;\n\t}\n\n\tprivate void setElem(Element elem) {\n\t\tif (elem == null) {\n\t\t\tthrow new NullPointerException();\n\t\t}    // end of if (elem == null)\n\t\tthis.elem = elem;\n\t\tif (elem.getAttributeStaticStr(TYPE_ATT) != null) {\n\t\t\ttype = StanzaType.valueof(elem.getAttributeStaticStr(TYPE_ATT));\n\t\t} else {\n\t\t\ttype = null;\n\t\t}    // end of if (elem.getAttribute(\"type\") != null) else\n\t\tif (elem.getName() == \"cluster\") {\n\t\t\tsetPriority(Priority.CLUSTER);\n\t\t} else {\n\t\t\tif ((elem.getName() == \"presence\") &&\n\t\t\t\t\t((type == null) || (type == StanzaType.available) || (type == StanzaType.unavailable) ||\n\t\t\t\t\t\t\t(type == StanzaType.probe))) {\n\t\t\t\tsetPriority(Priority.PRESENCE);\n\t\t\t} else {\n\t\t\t\tif (elem.getName() == \"route\") {\n\t\t\t\t\trouted = true;\n\t\t\t\t} else {\n\t\t\t\t\trouted = false;\n\t\t\t\t}    // end of if (elem.getName().equals(\"route\")) else\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/PacketFilterIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport tigase.stats.StatisticsList;\n\n/**\n * An interface for loadable packet filters to the Tigase server. Every Tigase component can have an independent list of\n * packet filters for outgoing and incoming traffic. A filter can make any change to the processed packet or can block\n * the packet from further processing. Please refer to the <code>filter()</code> method for more details.\n * <br>\n * Created: Jun 8, 2009 1:29:49 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface PacketFilterIfc {\n\n\t/**\n\t * The method initializes the filter. It is always called only once after an instance of the filter has been\n\t * created.\n\t *\n\t * @param name is a component name which loaded and initialized the filter. This is the name of the component which\n\t * uses the filter.\n\t * @param qType is a packet queue type, differnt one for outgoing traffic and different for incoming. A filter may\n\t * want to treat the traffic differently depending on the direction it flows.\n\t */\n\tvoid init(String name, QueueType qType);\n\n\t/**\n\t * This is the actual packet filtering method. It receives a packet as a parameter and may make any change to the\n\t * packet it wishes, remove or add specific payloads or redirect the packet to specific destination. <strong>Please\n\t * note!</strong> it is recommended not to modify the actual packet itself. If the filter needs to make any changes\n\t * to the packet it should create a copy of the object, then make any changes on the copy and return the copy as the\n\t * result. It may also optionally block the packet from further processing. This means that the packet is\n\t * effectivelly dropped and forgotten.\n\t * <br>\n\t * If the method returns a <code>Packet</code> as a result. It is normally recommended not to modify the existing\n\t * packet as it maybe processed simultanuously by other components/threads at the same time. Modifying packet while\n\t * it is being processed may lead to unpredictable results. Therefore, if the filter wants to modify the packet it\n\t * should create a copy of the packet and return modified copy from the method. If the filter decided to block the\n\t * packet it just has to return null. In most cases, however the method returns the packet it received as a\n\t * parameter to method call.\n\t *\n\t * @param packet for the filter processing.\n\t * <br>\n\t * Please note, the packet filtering may affect performance significantly therefore this method should be carefully\n\t * tested and optimized under a high load.\n\t *\n\t * @return a Packet object which is further processed by the system. If the method decided to block the packet it\n\t * returns null. If the method want the packet to be processed without any modifications it returns the same object\n\t * it received as a parameter. It may also return a modified copy of the Packet.\n\t */\n\tPacket filter(Packet packet);\n\n\t/**\n\t * A filter may optionally return some processing statistics. Please note the method may be called quite frequently\n\t * (once a second) therefore no expensive calculation should be performed inside the method.\n\t *\n\t * @param list of statistics created by the master object. The packet instance should add its statistics to the\n\t * list.\n\t */\n\tvoid getStatistics(StatisticsList list);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/PacketWriterWithTimeout.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport java.time.Duration;\n\npublic interface PacketWriterWithTimeout {\n\n\tboolean addOutPacketWithTimeout(Packet packet, Duration timeout, Handler handler);\n\n\t@FunctionalInterface\n\tinterface Handler {\n\n\t\tvoid handle(Packet result);\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/Permissions.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\n/**\n * Describe class Permissions here.\n * <br>\n * Created: Tue Jan 23 22:52:45 2007\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic enum Permissions {\n\n\tNONE,\n\t// Unknown user JID\n\tANONYM,\n\t// Anonymous user\n\tREMOTE,\n\t// Packet from a user from a different XMPP installation\n\tLOCAL,\n\t// This is local user JID but not authenticated yet\n\tAUTH,\n\t// Local authenticated and authorized user\n\tTRUSTED,\n\t// Trusted account, can broadcast packets\n\tADMIN;    // Admin account already authenticated\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/PolicyViolationException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\npublic class PolicyViolationException\n\t\textends Exception {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic PolicyViolationException() {\n\t\tsuper();\n\t}\n\n\tpublic PolicyViolationException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic PolicyViolationException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n\tpublic PolicyViolationException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/Presence.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.JID;\n\n/**\n * Created: Dec 31, 2009 8:42:05 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class Presence\n\t\textends Packet {\n\n\tpublic static final String ELEM_NAME = \"presence\";\n\n\tpublic static final String[] PRESENCE_ERROR_PATH = {ELEM_NAME, \"error\"};\n\n\tpublic static final String[] PRESENCE_PRIORITY_PATH = {ELEM_NAME, \"priority\"};\n\tpublic static final String[] PRESENCE_SHOW_PATH = {ELEM_NAME, \"show\"};\n\n\tpublic Presence(Element elem) throws TigaseStringprepException {\n\t\tsuper(elem);\n\t}\n\n\tpublic Presence(Element elem, JID stanzaFrom, JID stanzaTo) {\n\t\tsuper(elem, stanzaFrom, stanzaTo);\n\t}\n\n\t@Override\n\tprotected String[] getElNameErrorPath() {\n\t\treturn PRESENCE_ERROR_PATH;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/Priority.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\n/**\n * Created: Feb 13, 2009 9:44:53 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic enum Priority {\n\n\tSYSTEM,\n\tCLUSTER,\n\tHIGH,\n\tNORMAL,\n\tLOW,\n\tPRESENCE,\n\tLOWEST;\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/QueueType.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\n/**\n * Created: Jun 8, 2009 1:53:38 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic enum QueueType {\n\tIN_QUEUE,\n\tOUT_QUEUE;\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/ReceiverTimeoutHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\n/**\n * Created: Feb 16, 2009 1:44:46 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface ReceiverTimeoutHandler {\n\n\tvoid timeOutExpired(Packet data);\n\n\tvoid responseReceived(Packet data, Packet response);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/ServerComponent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Queue;\n\n/**\n * Interface ServerComponent\n * <br>\n * Object of this type can be managed by MessageRouter. All classes which are loaded by MessageRouter must inherit this\n * interface.\n * <br>\n * Created: Tue Nov 22 07:07:11 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface ServerComponent {\n\n\t/**\n\t * Method is called by <code>MessageRouter</code> when all the startup components of the server have been loaded and\n\t * configured through setProperties(...) call. At this point the whole server should be loaded and functional,\n\t * except initializations taking place in this routine.\n\t */\n\tvoid initializationCompleted();\n\n\t/**\n\t * <code>processPacket</code> is a blocking processing method implemented by all components. This method processes\n\t * packet and returns results instantly without waiting for any resources.\n\t *\n\t * @param packet a <code>Packet</code> value\n\t */\n\tvoid processPacket(Packet packet, Queue<Packet> results);\n\n\t/**\n\t * Method called when component is being stopped and unloaded.\n\t */\n\tvoid release();\n\n\t/**\n\t * Method returns component jid in form of the component name followed by server hostname as a domain.\n\t * \n\t * @return jid \n\t */\n\tJID getComponentId();\n\n\t/**\n\t * Allows to obtain various informations about components\n\t *\n\t * @return information about particular component\n\t */\n\tComponentInfo getComponentInfo();\n\n\t/**\n\t * Method returns name of the component.\n\t *\n\t * @return name of the component\n\t */\n\tString getName();\n\n\t/**\n\t * Method used to assign component name (localpart of the component)\n\t * \n\t * @param name to be assigned\n\t */\n\tvoid setName(String name);\n\n\t/**\n\t * Method returns information about whether the initialization process (initializationCompleted()) method has been\n\t * called.\n\t *\n\t * @return <code>true</code> if initialization of the object has been completed <code>false</code> otherwise\n\t */\n\tboolean isInitializationComplete();\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/ServiceChecker.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport tigase.xmpp.XMPPIOService;\n\n/**\n * Describe interface ServiceChecker here.\n * <br>\n * Created: Sat Jun 21 22:45:52 2008\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface ServiceChecker<IO extends XMPPIOService<?>> {\n\n\tvoid check(IO service);\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/StanzaSourceChecker.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.server;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.xmppclient.ClientConnectionManager;\n\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\n@Bean(name = \"stanza-source-checker\", parent = Kernel.class, active = true, exportable = true)\npublic class StanzaSourceChecker {\n\n\t@Inject(nullAllowed = true)\n\tprivate Set<ClientConnectionManager> clientConnectionManagers;\n\tprivate Set<String> clientConnectionManagersIds = new HashSet<>(3);\n\n\tpublic void setClientConnectionManagers(Set<ClientConnectionManager> clientConnectionManagers) {\n\t\tthis.clientConnectionManagers = clientConnectionManagers;\n\t\tif (clientConnectionManagers != null) {\n\t\t\tthis.clientConnectionManagersIds = clientConnectionManagers.stream()\n\t\t\t\t\t.map(BasicComponent::getName)\n\t\t\t\t\t.collect(Collectors.toSet());\n\t\t}\n\t}\n\n\tpublic boolean isPacketFromConnectionManager(Packet packet) {\n\t\treturn packet.getPacketFrom() != null && packet.getPacketFrom().getLocalpart() != null &&\n\t\t\t\tclientConnectionManagersIds.contains(packet.getPacketFrom().getLocalpart());\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/ThreadExceptionHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Class ThreadExceptionHandler.java is responsible helper class used to catch all unhandled exception from all threads.\n * This is default handler which sends exception stack trace to log system. If necessary other server packages can use\n * own custom handlers and do something else with unhandled exceptions. This handler is only implemented to avoid hidden\n * exception causing bugs.\n * <br>\n * <p> Created: Thu Sep 30 22:24:24 2004 </p>\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n\npublic class ThreadExceptionHandler\n\t\timplements Thread.UncaughtExceptionHandler {\n\n\tprivate static final Logger log = Logger.getLogger(\"tigase.server.ThreadExceptionHandler\");\n\n\tpublic void uncaughtException(final Thread t, final Throwable e) {\n\t\tlog.log(Level.SEVERE, \"Uncaught thread: \\\"\" + t.getName() + \"\\\" exception\", e);\n\t}\n\n}// ThreadExceptionHandler\n"
  },
  {
    "path": "src/main/java/tigase/server/XMPPServer.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport tigase.component.DSLBeanConfigurator;\nimport tigase.conf.ConfigHolder;\nimport tigase.conf.ConfigReader;\nimport tigase.conf.ConfigWriter;\nimport tigase.conf.ConfiguratorAbstract;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.eventbus.events.StartupFinishedEvent;\nimport tigase.kernel.KernelException;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.beans.selector.ServerBeanSelector;\nimport tigase.kernel.core.BeanConfig;\nimport tigase.sys.TigaseRuntime;\nimport tigase.util.ClassUtil;\nimport tigase.util.ExceptionUtilities;\nimport tigase.util.Version;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.util.log.LogFormatter;\nimport tigase.xml.XMLUtils;\n\nimport java.io.StringWriter;\nimport java.io.Writer;\nimport java.text.SimpleDateFormat;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Stream;\n\n/**\n * Describe class XMPPServer here.\n * <br>\n * Created: Wed Nov 23 07:04:18 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic final class XMPPServer {\n\n\t@SuppressWarnings(\"PMD\")\n\t/** property allowing setting up configurator implementation of\n\t * {@link tigase.conf.ConfiguratorAbstract} used in Tigase.\n\t */ public static final String CONFIGURATOR_PROP_KEY = \"tigase-configurator\";\n\tpublic static final String NAME = \"Tigase\";\n\t/**\n\t * default configurator implementation of {@link tigase.conf.ConfiguratorAbstract} used in Tigase, which is\n\t * tigase.conf.Configurator.\n\t */\n\tprivate static final String DEF_CONFIGURATOR = \"tigase.conf.Configurator\";\n\tprivate final static String[] serverVersionCandidates = new String[]{\"tigase.dist.XmppServerDist\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t XMPPServer.class.getCanonicalName()};\n\tprivate static Bootstrap bootstrap;\n\tprivate static boolean inOSGi = false;\n\tprivate static String serverName = \"message-router\";\n\tprivate final static Logger log = Logger.getLogger(XMPPServer.class.getName());\n\tprivate static final String defaultVersion = \"0.0.0-0\";\n\n\t/**\n\t * Allows obtaining {@link tigase.conf.ConfiguratorAbstract} implementation used by Tigase to handle all\n\t * configuration of the server.\n\t *\n\t * @return implementation of {@link tigase.conf.ConfiguratorAbstract} interface.\n\t */\n//\t@Deprecated\n//\tpublic static ConfiguratorAbstract getConfigurator() {\n//\t\treturn config;\n//\t}\n\tpublic static <T> T getComponent(String name) {\n\t\ttry {\n\t\t\treturn bootstrap.getInstance(name);\n\t\t} catch (KernelException ex) {\n\t\t\tLogger.getLogger(XMPPServer.class.getCanonicalName())\n\t\t\t\t\t.log(Level.FINEST, \"failed to retrieve instance of \" + name, ex);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic static <T> T getComponent(Class<T> clazz) {\n\t\ttry {\n\t\t\treturn bootstrap.getInstance(clazz);\n\t\t} catch (KernelException ex) {\n\t\t\tLogger.getLogger(XMPPServer.class.getCanonicalName())\n\t\t\t\t\t.log(Level.FINEST, \"failed to retrieve instance of \" + clazz, ex);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic static <T> Stream<T> getComponents(Class<T> clazz) {\n\t\treturn bootstrap.getKernel()\n\t\t\t\t.getDependencyManager()\n\t\t\t\t.getBeanConfigs()\n\t\t\t\t.stream()\n\t\t\t\t.filter(bc -> clazz.isAssignableFrom(bc.getClazz()) && bc.getState() == BeanConfig.State.initialized)\n\t\t\t\t.map(bc -> (T) bootstrap.getInstance(bc.getBeanName()));\n\t}\n\n\tpublic static String getImplementationVersion() {\n\t\tOptional<Version> version = ComponentInfo.getImplementationVersion(serverVersionCandidates);\n\t\tif (version.isEmpty() & log.isLoggable(Level.INFO)) {\n\t\t\tlog.log(Level.INFO,\n\t\t\t\t\t\"Problem obtaining version for classes: \" + Arrays.toString(serverVersionCandidates) + \", setting it to default: \" +\n\t\t\t\t\t\t\tdefaultVersion);\n\t\t}\n\t\treturn (version.isPresent() ? version.get().toString() : defaultVersion);\n\t}\n\n\tpublic static Version getVersion() {\n\t\treturn ComponentInfo.getImplementationVersion(serverVersionCandidates).orElse(Version.ZERO);\n\t}\n\n\t/**\n\t * Returns help regarding command line parameters\n\t */\n\tpublic static String help() {\n\t\treturn \"\\n\" + \"Parameters:\\n\" + \" -h               this help message\\n\" +\n\t\t\t\t\" -v               prints server version info\\n\" + \" -n server-name    sets server name\\n\";\n\t}\n\n\tpublic static boolean isOSGi() {\n\t\treturn inOSGi;\n\t}\n\n\tpublic static void setOSGi(boolean val) {\n\t\tinOSGi = val;\n\t}\n\n\t@SuppressWarnings(\"PMD\")\n\tpublic static void main(final String[] args) {\n\n\t\tparseParams(args);\n\n\t\tSystem.out.println((new ComponentInfo(XMLUtils.class)).toString());\n\t\tSystem.out.println((new ComponentInfo(ClassUtil.class)).toString());\n\t\tSystem.out.println((new ComponentInfo(XMPPServer.class)).toString());\n\t\tComponentInfo.of(\"tigase.dist.XmppServerDist\").ifPresent(System.out::println);\n\t\tstart(args);\n\t}\n\n\t@SuppressWarnings(\"PMD\")\n\tpublic static void parseParams(final String[] args) {\n\t\tif ((args != null) && (args.length > 0)) {\n\t\t\tfor (int i = 0; i < args.length; i++) {\n\t\t\t\tif (args[i].equals(\"-h\")) {\n\t\t\t\t\tSystem.out.print(help());\n\t\t\t\t\tSystem.exit(0);\n\t\t\t\t}      // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-v\")) {\n\t\t\t\t\tSystem.out.print(version());\n\t\t\t\t\tSystem.exit(0);\n\t\t\t\t}      // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-n\")) {\n\t\t\t\t\tif (i + 1 == args.length) {\n\t\t\t\t\t\tSystem.out.print(help());\n\t\t\t\t\t\tSystem.exit(1);\n\t\t\t\t\t} // end of if (i+1 == args.length)\n\t\t\t\t\telse {\n\t\t\t\t\t\tserverName = args[++i];\n\t\t\t\t\t}    // end of else\n\t\t\t\t}      // end of if (args[i].equals(\"-h\"))\n\n\t\t\t}        // end of for (int i = 0; i < args.length; i++)\n\t\t}\n\t}\n\n\tpublic static void start(String[] args) {\n\t\tfinal long start = System.currentTimeMillis();\n\t\tThread.setDefaultUncaughtExceptionHandler(new ThreadExceptionHandler());\n\n\t\tif (!isOSGi()) {\n\t\t\tString initial_config =\n\t\t\t\t\t\"tigase.level=ALL\\n\" + \"tigase.xml.level=INFO\\n\" + \"handlers=java.util.logging.ConsoleHandler\\n\" +\n\t\t\t\t\t\t\t\"java.util.logging.ConsoleHandler.level=ALL\\n\" +\n\t\t\t\t\t\t\t\"java.util.logging.ConsoleHandler.formatter=\" + LogFormatter.class.getName() + \"\\n\";\n\n\t\t\tConfiguratorAbstract.loadLogManagerConfig(initial_config);\n\t\t}\n\n\t\ttry {\n\t\t\tbootstrap = new Bootstrap();\n\t\t\tbootstrap.init(args);\n\t\t\tbootstrap.start();\n\t\t\tSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd' 'HH:mm:ss.SSS\");\n\t\t\tif (ServerBeanSelector.getConfigType(bootstrap.getKernel()) == ConfigTypeEnum.SetupMode) {\n\t\t\t\tlogTdslConfigWithSetupCredentials();\n\t\t\t\tlog.log(Level.INFO, \"Please setup server at http://localhost:8080/\\n\");\n\t\t\t} else {\n\t\t\t\tlog.log(Level.INFO, \"Server finished starting up in (\" +\n\t\t\t\t\t\t\t\t\t\t   (System.currentTimeMillis() - start) / 1000 +\n\t\t\t\t\t\t\t\t\t\t   \"s) and (if there wasn't any error) is ready to use\\n\");\n\t\t\t}\n\t\t\tEventBusFactory.getInstance()\n\t\t\t\t\t.fire(new StartupFinishedEvent(DNSResolverFactory.getInstance().getDefaultHost()));\n\t\t} catch (ConfigReader.UnsupportedOperationException e) {\n\t\t\tTigaseRuntime.getTigaseRuntime()\n\t\t\t\t\t.shutdownTigase(new String[]{\"ERROR! Terminating the server process.\",\n\t\t\t\t\t\t\t\t\t\t\t\t e.getMessage() + \" at line \" + e.getLine() + \" position \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t e.getPosition(), \"Line: \" + e.getLineContent(),\n\t\t\t\t\t\t\t\t\t\t\t\t \"Please fix the problem and start the server again.\"});\n\t\t} catch (ConfigReader.ConfigException e) {\n\t\t\tTigaseRuntime.getTigaseRuntime()\n\t\t\t\t\t.shutdownTigase(new String[]{\"ERROR! Terminating the server process.\",\n\t\t\t\t\t\t\t\t\t\t\t\t \"Issue with configuration file: \" + e,\n\t\t\t\t\t\t\t\t\t\t\t\t \"Please fix the problem and start the server again.\"});\n\t\t} catch (Exception e) {\n\t\t\tString cause = ExceptionUtilities.getExceptionRootCause(e, true);\n\t\t\tTigaseRuntime.getTigaseRuntime()\n\t\t\t\t\t.shutdownTigase(new String[]{\"ERROR! Terminating the server process.\",\n\t\t\t\t\t\t\t\t\t\t\t\t \"Problem initializing the server: \" + cause,\n\t\t\t\t\t\t\t\t\t\t\t\t \"Please fix the problem and start the server again.\"});\n\t\t}\n\t}\n\n\tprivate static void logTdslConfigWithSetupCredentials() {\n\t\tDSLBeanConfigurator configurator = bootstrap.getInstance(DSLBeanConfigurator.class);\n\t\tfinal ConfigHolder configHolder = configurator.getConfigHolder();\n\t\tif (configHolder != null) {\n\t\t\tfinal Map<String, Object> properties = configHolder.getProperties();\n\t\t\ttry (Writer w = new StringWriter()) {\n\t\t\t\tnew ConfigWriter().resolveVariables().write(w, properties);\n\t\t\t\tlog.log(Level.INFO, \"Setup configuration:\\n\" + w.toString());\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.WARNING, \"Problem showing configuration\");\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static void stop() {\n\t\tif (bootstrap != null) {\n\t\t\tbootstrap.stop();\n\t\t}\n\t}\n\n\tpublic static String version() {\n\t\treturn \"\\n\" + \"-- \\n\" + NAME + \" XMPP Server, version: \" + getImplementationVersion() + \"\\n\" +\n\t\t\t\t\"Author:  Artur Hefczyc <artur.hefczyc@tigase.org>\\n\" + \"-- \\n\";\n\t}\n\n\tprivate XMPPServer() {\n\t}\n}    // XMPPServer\n\n"
  },
  {
    "path": "src/main/java/tigase/server/amp/ActionAbstract.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.UserRepository;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigAlias;\nimport tigase.kernel.beans.config.ConfigAliases;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.PacketErrorTypeException;\nimport tigase.xmpp.impl.roster.RosterAbstract;\nimport tigase.xmpp.impl.roster.RosterElement;\nimport tigase.xmpp.impl.roster.RosterFlat;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: May 1, 2010 7:44:17 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@ConfigAliases({@ConfigAlias(field = \"security\", alias = \"amp-security-level\")})\npublic abstract class ActionAbstract\n\t\timplements ActionIfc {\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String AMP_SECURITY_LEVEL = \"--amp-security-level\";\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static final String AMP_SECURITY_LEVEL_DEFAULT = \"STRICT\";\n\n\tpublic static final String SECURITY_PROP_KEY = \"security-level\";\n\tprivate static Logger log = Logger.getLogger(ActionAbstract.class.getName());\n\n\tprotected ActionResultsHandlerIfc resultsHandler = null;\n\tRosterFlat rosterUtil = new RosterFlat();\n\t@ConfigField(alias = \"security\", desc = \"Security level\")\n\tprivate SECURITY security = SECURITY.STRICT;\n\t@Inject\n\tprivate UserRepository user_repository = null;\n\n\t@Override\n\tpublic void setActionResultsHandler(ActionResultsHandlerIfc resultsHandler) {\n\t\tthis.resultsHandler = resultsHandler;\n\t}\n\n\tprotected Packet prepareAmpPacket(Packet packet, Element rule) throws PacketErrorTypeException {\n\t\tboolean error_result = false;\n\n\t\tswitch (security) {\n\t\t\tcase NONE:\n\t\t\t\tbreak;\n\n\t\t\tcase PERFORMANCE:\n\t\t\t\terror_result = true;\n\n\t\t\t\tbreak;\n\n\t\t\tcase STRICT:\n\t\t\t\terror_result = !checkUserRoster(packet.getStanzaTo(), packet.getStanzaFrom());\n\n\t\t\t\tbreak;\n\t\t}\n\n\t\tPacket result = null;\n\n\t\tif (error_result) {\n\t\t\tresult = Authorization.NOT_ACCEPTABLE.getResponseMessage(packet, \"Subscription between users not valid\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t false);\n\t\t} else {\n\t\t\tJID old_from = packet.getStanzaFrom();\n\t\t\tJID old_to = packet.getStanzaTo();\n\t\t\tString from_conn_id = packet.getAttributeStaticStr(FROM_CONN_ID);\n\t\t\tJID new_from = null;\n\n\t\t\tif (from_conn_id != null) {\n\t\t\t\tnew_from = JID.jidInstanceNS(old_from.getDomain());\n\t\t\t} else {\n\t\t\t\tnew_from = JID.jidInstanceNS(old_to.getDomain());\n\t\t\t}\n\n\t\t\t// Packet result = Packet.packetInstance(packet.getElement(), new_from, old_from);\n\t\t\tresult = packet.copyElementOnly();\n\t\t\tresult.setStableId(packet.getStableId());\n\t\t\tresult.initVars(new_from, old_from);\n\n\t\t\tElement amp = result.getElement().getChild(\"amp\", AMP_XMLNS);\n\n\t\t\tresult.getElement().removeChild(amp);\n\t\t\tamp = new Element(\"amp\", new Element[]{rule}, new String[]{\"from\", \"to\", \"xmlns\", \"status\"},\n\t\t\t\t\t\t\t  new String[]{old_from.toString(), old_to.toString(), AMP_XMLNS, getName()});\n\t\t\tresult.getElement().addChild(amp);\n\t\t\tremoveTigasePayload(result);\n\t\t\tif (from_conn_id != null) {\n\t\t\t\tresult.setPacketTo(JID.jidInstanceNS(from_conn_id));\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprotected void removeTigasePayload(Packet packet) {\n\t\tpacket.getElement().removeAttribute(TO_CONN_ID);\n\t\tpacket.getElement().removeAttribute(TO_RES);\n\t\tpacket.getElement().removeAttribute(OFFLINE);\n\t\tpacket.getElement().removeAttribute(FROM_CONN_ID);\n\t\tpacket.getElement().removeAttribute(SESSION_JID);\n\t\tpacket.getElement().removeAttribute(EXPIRED);\n\t}\n\n\tprivate boolean checkUserRoster(JID user, JID contact) {\n\n\t\tif (user.getBareJID().equals(contact.getBareJID())) {\n\t\t\t// this is the same user, no point in checking sub\n\t\t\treturn true;\n\t\t}\n\n\t\ttry {\n\t\t\tString roster_str = user_repository.getData(user.getBareJID(), RosterAbstract.ROSTER);\n\n\t\t\tif (roster_str != null) {\n\t\t\t\tMap<BareJID, RosterElement> roster = new LinkedHashMap<BareJID, RosterElement>();\n\n\t\t\t\tRosterFlat.parseRosterUtil(roster_str, roster, null);\n\n\t\t\t\tRosterElement re = roster.get(contact.getBareJID());\n\n\t\t\t\tif (re != null) {\n\t\t\t\t\treturn rosterUtil.isSubscribedFrom(re.getSubscription());\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.CONFIG, \"Problem retrieving user roster: \" + user, ex);\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate enum SECURITY {\n\n\t\tNONE,\n\t\tPERFORMANCE,\n\t\tSTRICT\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/amp/ActionIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp;\n\nimport tigase.server.Packet;\nimport tigase.xml.Element;\n\n/**\n * Created: Apr 26, 2010 5:05:58 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface ActionIfc\n\t\textends AmpFeatureIfc {\n\n\tboolean execute(Packet packet, Element rule);\n\n\tvoid setActionResultsHandler(ActionResultsHandlerIfc resultsHandler);\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/amp/ActionResultsHandlerIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp;\n\nimport tigase.server.Packet;\n\nimport java.util.Queue;\n\n/**\n * Created: May 1, 2010 3:26:50 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface ActionResultsHandlerIfc {\n\n\tboolean addOutPacket(Packet packet);\n\n\tboolean addOutPackets(Queue<Packet> packets);\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/amp/AmpComponent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp;\n\nimport tigase.disco.XMPPService;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.RegistrarBean;\nimport tigase.kernel.beans.selector.ClusterModeRequired;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.AbstractMessageReceiver;\nimport tigase.server.Packet;\nimport tigase.server.amp.action.Broadcast;\nimport tigase.server.amp.cond.Deliver;\nimport tigase.server.amp.cond.ExpireAt;\nimport tigase.server.amp.cond.MatchResource;\nimport tigase.sys.TigaseRuntime;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Queue;\nimport java.util.concurrent.ConcurrentSkipListMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Apr 26, 2010 3:22:06 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"amp\", parent = Kernel.class, active = true)\n@ConfigType({ConfigTypeEnum.DefaultMode, ConfigTypeEnum.SessionManagerMode, ConfigTypeEnum.ConnectionManagersMode,\n\t\t\t ConfigTypeEnum.ComponentMode})\n@ClusterModeRequired(active = false)\npublic class AmpComponent\n\t\textends AbstractMessageReceiver\n\t\timplements ActionResultsHandlerIfc, RegistrarBean {\n\n\tprivate static final String AMP_NODE = \"http://jabber.org/protocol/amp\";\n\tprivate static final Logger log = Logger.getLogger(AmpComponent.class.getName());\n\tprivate static final String AMP_XMLNS = AMP_NODE;\n\tprivate static final Element top_feature = new Element(\"feature\", new String[]{\"var\"}, new String[]{AMP_NODE});\n\n\t@Inject\n\tprotected Broadcast broadcast = null;\n\t// ~--- fields ---------------------------------------------------------------\n\tprivate ConcurrentSkipListMap<String, ActionIfc> actions = new ConcurrentSkipListMap<String, ActionIfc>();\n\t@Inject\n\tprivate List<ActionIfc> allActions = new ArrayList<>();\n\tprivate ConcurrentSkipListMap<String, ConditionIfc> conditions = new ConcurrentSkipListMap<String, ConditionIfc>();\n\n\tpublic AmpComponent() {\n\t\tConditionIfc condition = new Deliver();\n\t\tconditions.put(condition.getName(), condition);\n\t\tcondition = new ExpireAt();\n\t\tconditions.put(condition.getName(), condition);\n\t\tcondition = new MatchResource();\n\t\tconditions.put(condition.getName(), condition);\n\n\t}\n\n\t@Override\n\tpublic boolean addOutPacket(Packet packet) {\n\t\treturn super.addOutPacket(packet);\n\t}\n\n\t@Override\n\tpublic boolean addOutPackets(Queue<Packet> packets) {\n\t\treturn super.addOutPackets(packets);\n\t}\n\n\t@Override\n\tpublic String getDiscoCategoryType() {\n\t\treturn \"generic\";\n\t}\n\n\t@Override\n\tpublic String getDiscoDescription() {\n\t\treturn \"IM AMP Support\";\n\t}\n\n\t@Override\n\tpublic Element getDiscoInfo(String node, JID jid, JID from) {\n\t\tElement query = super.getDiscoInfo(node, jid, from);\n\n\t\tif ((jid != null) && (getName().equals(jid.getLocalpart()) || isLocalDomain(jid.toString())) &&\n\t\t\t\t(AMP_NODE.equals(node))) {\n\t\t\tif (query == null) {\n\t\t\t\tquery = new Element(\"query\");\n\t\t\t\tquery.setXMLNS(XMPPService.INFO_XMLNS);\n\t\t\t}\n\t\t\tquery.addChild(new Element(\"identity\", new String[]{\"name\", \"category\", \"type\"},\n\t\t\t\t\t\t\t\t\t   new String[]{getDiscoDescription(), \"im\", getDiscoCategoryType()}));\n\t\t\tquery.addChild(top_feature);\n\t\t\tfor (ActionIfc action : actions.values()) {\n\t\t\t\tquery.addChild(new Element(\"feature\", new String[]{\"var\"},\n\t\t\t\t\t\t\t\t\t\t   new String[]{AMP_NODE + \"?action=\" + action.getName()}));\n\t\t\t}\n\t\t\tfor (ConditionIfc cond : conditions.values()) {\n\t\t\t\tquery.addChild(new Element(\"feature\", new String[]{\"var\"},\n\t\t\t\t\t\t\t\t\t\t   new String[]{AMP_NODE + \"?condition=\" + cond.getName()}));\n\t\t\t}\n\n\t\t\t// for (ProcessingThreads<ProcessorWorkerThread> proc_t :\n\t\t\t// processors.values()) {\n\t\t\t// Element[] discoFeatures =\n\t\t\t// proc_t.getWorkerThread().processor.supDiscoFeatures(null);\n\t\t\t//\n\t\t\t// if (discoFeatures != null) {\n\t\t\t// query.addChildren(Arrays.asList(discoFeatures));\n\t\t\t// } // end of if (discoFeatures != null)\n\t\t\t// }\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Found disco info: \" + ((query != null) ? query.toString() : null));\n\t\t}\n\n\t\treturn query;\n\t}\n\n\t@Override\n\tpublic int processingInThreads() {\n\t\treturn TigaseRuntime.getTigaseRuntime().getCPUsNumber() * 4;\n\t}\n\n\t@Override\n\tpublic int processingOutThreads() {\n\t\treturn TigaseRuntime.getTigaseRuntime().getCPUsNumber() * 4;\n\t}\n\n\t@Override\n\tpublic void processPacket(Packet packet) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"My packet: \" + packet);\n\t\t}\n\n\t\tif (broadcast.preprocess(packet)) {\n\t\t\treturn;\n\t\t}\n\n\t\tActionIfc def = null;\n\n\t\tif (packet.getAttributeStaticStr(AmpFeatureIfc.OFFLINE) == null) {\n\t\t\tdef = actions.get(\"deliver\");\n\t\t} else {\n\t\t\tdef = actions.get(\"store\");\n\t\t}\n\n\t\tboolean exec_def = true;\n\t\tElement amp = packet.getElement().getChild(\"amp\", AMP_XMLNS);\n\n\t\tif (amp != null) {\n\t\t\tList<Element> rules = amp.getChildren();\n\n\t\t\tif ((rules != null) && (rules.size() > 0)) {\n\t\t\t\tfor (Element rule : rules) {\n\t\t\t\t\tif (matchCondition(packet, rule)) {\n\t\t\t\t\t\texec_def = executeAction(packet, rule);\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tlog.warning(\"AMP packet but empty rule-set! \" + packet);\n\n\t\t\t\t// In case of such error, let's just drop the packet\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"Not an AMP packet! \" + packet);\n\n\t\t\t// In case of such error, let's just drop the packet\n\t\t\treturn;\n\t\t}\n\t\tif (exec_def) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Executing default action: \" + def.getName() + \", packet: \" + packet);\n\t\t\t}\n\t\t\tdef.execute(packet, null);\n\t\t}\n\t}\n\n\tpublic void setAllActions(List<ActionIfc> actions) {\n\t\tConcurrentSkipListMap<String, ActionIfc> map = new ConcurrentSkipListMap<>();\n\t\tfor (ActionIfc action : actions) {\n\t\t\taction.setActionResultsHandler(this);\n\t\t\tmap.put(action.getName(), action);\n\t\t}\n\t\tthis.actions = map;\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\n\t}\n\n\t// ~--- methods --------------------------------------------------------------\n\tprivate boolean executeAction(Packet packet, Element rule) {\n\t\tString act = rule.getAttributeStaticStr(AmpFeatureIfc.ACTION_ATT);\n\n\t\tif (act != null) {\n\t\t\tActionIfc action = actions.get(act);\n\n\t\t\tif (action != null) {\n\t\t\t\tboolean result = action.execute(packet, rule);\n\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"Matched action: \" + action.getName() + \", result: \" + result + \", packet: \" + packet);\n\t\t\t\t}\n\n\t\t\t\treturn result;\n\t\t\t} else {\n\t\t\t\tlog.fine(\"No action found for act: \" + act);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.fine(\"No actionset for rule: \" + rule);\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprivate boolean matchCondition(Packet packet, Element rule) {\n\t\tString cond = rule.getAttributeStaticStr(AmpFeatureIfc.CONDITION_ATT);\n\n\t\tif (cond != null) {\n\t\t\tConditionIfc condition = conditions.get(cond);\n\n\t\t\tif (condition != null) {\n\t\t\t\tboolean result = condition.match(packet, rule);\n\t\t\t\t;\n\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"Matched condition: \" + condition.getName() + \", result: \" + result + \", packet: \" + packet);\n\t\t\t\t}\n\n\t\t\t\treturn result;\n\t\t\t} else {\n\t\t\t\tlog.fine(\"No condition found for cond: \" + cond);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.fine(\"No condition set for rule: \" + rule);\n\t\t}\n\n\t\treturn false;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/amp/AmpFeatureIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp;\n\n/**\n * Created: Apr 28, 2010 5:38:24 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface AmpFeatureIfc {\n\n\tpublic static final String AMP_MSG_REPO_CLASS_PROP_KEY = \"amp-repo-class\";\n\n\tpublic static final String AMP_MSG_REPO_CLASS_PARAM = \"--amp-repo-class\";\n\n\tpublic static final String AMP_MSG_REPO_URI_PARAM = \"--amp-repo-uri\";\n\n\tpublic static final String AMP_MSG_REPO_URI_PROP_KEY = \"amp-repo-uri\";\n\n\tpublic static final String AMP_XMLNS = \"http://jabber.org/protocol/amp\";\n\n\tpublic static final String FROM_CONN_ID = \"from-conn-id\";\n\n\tpublic static final String SESSION_JID = \"from-session-jid\";\n\n\tpublic static final String TO_CONN_ID = \"to-conn-id\";\n\n\tpublic static final String TO_RES = \"to-res\";\n\n\tpublic static final String EXPIRED = \"expired\";\n\n\tpublic static final String OFFLINE = \"offline\";\n\n\tpublic static final String MSG_OFFLINE_PROP_KEY = \"msg-offline\";\n\n\tpublic static final String CONDITION_ATT = \"condition\";\n\n\tpublic static final String ACTION_ATT = \"action\";\n\n\tString getName();\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/amp/ConditionIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp;\n\nimport tigase.server.Packet;\nimport tigase.xml.Element;\n\n/**\n * Created: Apr 26, 2010 5:06:13 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface ConditionIfc\n\t\textends AmpFeatureIfc {\n\n\tboolean match(Packet packet, Element rule);\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/amp/JidResourceMap.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp;\n\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * Generic utility class to allow storage of any value for particular JID (including resource).\n *\n * *\n * @author andrzej\n */\npublic class JidResourceMap<T> {\n\n\tprivate final ConcurrentHashMap<BareJID, Map<String, T>> usersMap = new ConcurrentHashMap<BareJID, Map<String, T>>();\n\n\tpublic JidResourceMap() {\n\n\t}\n\n\tpublic boolean containsKey(BareJID jid) {\n\t\treturn usersMap.containsKey(jid);\n\t}\n\n\tpublic boolean containsKey(JID jid) {\n\t\tMap<String, T> resources = usersMap.get(jid.getBareJID());\n\t\treturn resources != null && resources.containsKey(jid.getResource());\n\t}\n\n\tpublic T get(JID jid) {\n\t\tMap<String, T> resources = usersMap.get(jid.getBareJID());\n\t\tif (resources == null) {\n\t\t\treturn null;\n\t\t} else {\n\t\t\tsynchronized (resources) {\n\t\t\t\treturn resources.get(jid.getResource());\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic T put(JID jid, T value) {\n\t\tif (value == null) {\n\t\t\treturn remove(jid);\n\t\t}\n\n\t\tMap<String, T> resources = usersMap.get(jid.getBareJID());\n\n\t\tif (resources == null) {\n\t\t\tresources = new HashMap<String, T>();\n\n\t\t\tMap<String, T> oldResources = usersMap.putIfAbsent(jid.getBareJID(), resources);\n\n\t\t\tif (oldResources != null) {\n\t\t\t\tresources = oldResources;\n\t\t\t}\n\t\t}\n\t\tif (jid.getResource() != null) {\n\t\t\tsynchronized (resources) {\n\t\t\t\treturn resources.put(jid.getResource(), value);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic T remove(JID jid) {\n\t\tMap<String, T> resources = usersMap.get(jid.getBareJID());\n\n\t\tif (resources == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tsynchronized (resources) {\n\t\t\treturn resources.remove(jid.getResource());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/amp/action/Alert.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp.action;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Packet;\nimport tigase.server.amp.ActionAbstract;\nimport tigase.server.amp.AmpComponent;\nimport tigase.xml.Element;\nimport tigase.xmpp.PacketErrorTypeException;\n\n/**\n * Created: May 1, 2010 1:39:07 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"alert\", parent = AmpComponent.class, active = true)\npublic class Alert\n\t\textends ActionAbstract {\n\n\tprivate static final String name = \"alert\";\n\n\t@Override\n\tpublic boolean execute(Packet packet, Element rule) {\n\t\ttry {\n\t\t\tPacket result = prepareAmpPacket(packet, rule);\n\n\t\t\tresultsHandler.addOutPacket(result);\n\t\t} catch (PacketErrorTypeException ex) {\n\n\t\t\t// Ignore....\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/amp/action/Broadcast.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp.action;\n\nimport tigase.db.RepositoryFactory;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Command;\nimport tigase.server.Packet;\nimport tigase.server.Presence;\nimport tigase.server.amp.ActionResultsHandlerIfc;\nimport tigase.server.amp.AmpComponent;\nimport tigase.server.amp.AmpFeatureIfc;\nimport tigase.server.amp.db.MsgBroadcastRepository;\nimport tigase.server.amp.db.MsgBroadcastRepositoryIfc;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.JID;\n\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.TimeZone;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.server.amp.cond.ExpireAt.NAME;\n\n/**\n * @author andrzej\n */\n@Bean(name = \"broadcast\", parent = AmpComponent.class, active = true)\npublic class Broadcast\n\t\timplements AmpFeatureIfc {\n\n\tprivate static final Logger log = Logger.getLogger(Broadcast.class.getName());\n\tprivate static final String name = \"broadcast\";\n\tprivate final SimpleDateFormat formatter;\n\tprivate final SimpleDateFormat formatter2;\n\tprivate MsgBroadcastRepositoryIfc repo = null;\n\tprivate ActionResultsHandlerIfc resultsHandler;\n\n\t{\n\t\tformatter = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'\");\n\t\tformatter.setTimeZone(TimeZone.getTimeZone(\"UTC\"));\n\t\tformatter2 = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss'Z'\");\n\t\tformatter2.setTimeZone(TimeZone.getTimeZone(\"UTC\"));\n\t}\n\n\tpublic boolean preprocess(Packet packet) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"processing packet = {0}\", packet.toString());\n\t\t}\n\t\tif (packet.getElemName() == Presence.ELEM_NAME) {\n\t\t\tsendBroadcastMessage(packet.getStanzaFrom());\n\t\t\treturn true;\n\t\t}\n\n\t\tElement broadcast = packet.getElement().getChild(\"broadcast\", \"http://tigase.org/protocol/broadcast\");\n\t\tif (broadcast == null || packet.getAttributeStaticStr(FROM_CONN_ID) != null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"processing broadcast packet = {0}\", packet);\n\t\t}\n\n\t\tif (repo != null) {\n\t\t\tif (packet.getStanzaTo().getResource() == null) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"setting broadcast request for user {0}\", packet.getStanzaTo());\n\t\t\t\t}\n\t\t\t\tElement amp = packet.getElement().getChild(\"amp\", AMP_XMLNS);\n\t\t\t\tElement rule = null;\n\t\t\t\tfor (Element elem : amp.getChildren()) {\n\t\t\t\t\tif (\"rule\".equals(elem.getName()) &&\n\t\t\t\t\t\t\t\"expire-at\".equals(elem.getAttributeStaticStr(CONDITION_ATT))) {\n\t\t\t\t\t\trule = elem;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (rule != null) {\n\t\t\t\t\tString value = rule.getAttributeStaticStr(\"value\");\n\t\t\t\t\tDate expire = null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (value != null) {\n\t\t\t\t\t\t\tif (value.contains(\".\")) {\n\t\t\t\t\t\t\t\tsynchronized (formatter) {\n\t\t\t\t\t\t\t\t\texpire = formatter.parse(value);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tsynchronized (formatter2) {\n\t\t\t\t\t\t\t\t\texpire = formatter2.parse(value);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tpacket.getElement().removeAttribute(TO_CONN_ID);\n\t\t\t\t\t\t\tpacket.getElement().removeAttribute(TO_RES);\n\t\t\t\t\t\t\tpacket.getElement().removeAttribute(OFFLINE);\n\t\t\t\t\t\t\tpacket.getElement().removeAttribute(FROM_CONN_ID);\n\t\t\t\t\t\t\tpacket.getElement().removeAttribute(EXPIRED);\n\n\t\t\t\t\t\t\tElement msg = packet.getElement().clone();\n\t\t\t\t\t\t\tmsg.removeAttribute(\"to\");\n\n\t\t\t\t\t\t\tString msgId = packet.getAttributeStaticStr(\"id\");\n\n\t\t\t\t\t\t\tMsgBroadcastRepository.BroadcastMsg bmsg = repo.getBroadcastMsg(msgId);\n\t\t\t\t\t\t\tboolean needToBroadcast = bmsg == null || !bmsg.needToSend(packet.getStanzaTo());\n\n\t\t\t\t\t\t\trepo.updateBroadcastMessage(msgId, msg, expire, packet.getStanzaTo().getBareJID());\n\n\t\t\t\t\t\t\tif (needToBroadcast) {\n\t\t\t\t\t\t\t\tPacket broadcastCmd = Command.BROADCAST_TO_ONLINE.getPacket(packet.getPacketTo(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tJID.jidInstanceNS(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"sess-man\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpacket.getPacketTo()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.getDomain(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnull),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tStanzaType.get, name);\n\t\t\t\t\t\t\t\tCommand.addFieldValue(broadcastCmd, \"to\", packet.getStanzaTo().toString());\n\t\t\t\t\t\t\t\tmsg = packet.getElement().clone();\n\t\t\t\t\t\t\t\tmsg.removeAttribute(\"to\");\n\t\t\t\t\t\t\t\tmsg.setAttribute(\"xmlns\", \"http://tigase.org/protocol/broadcast\");\n\t\t\t\t\t\t\t\tbroadcastCmd.getElement().addChild(msg);\n\n\t\t\t\t\t\t\t\tresultsHandler.addOutPacket(broadcastCmd);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (ParseException ex) {\n\t\t\t\t\t\tlog.log(Level.CONFIG, \"Incorrect \" + NAME + \" condition value for rule: \" + rule);\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tString msgId = packet.getAttributeStaticStr(\"id\");\n\t\t\t\tMsgBroadcastRepository.BroadcastMsg msg = repo.getBroadcastMsg(msgId);\n\t\t\t\tif (msg != null) {\n\t\t\t\t\tpacket.getElement().removeChild(broadcast);\n\t\t\t\t\tmsg.markAsSent(packet.getStanzaTo());\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"marking broadcast of message = {0} for user {1} as done, result = {2}\",\n\t\t\t\t\t\t\t\tnew Object[]{msgId, packet.getStanzaTo(), msg.needToSend(packet.getStanzaTo())});\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"not found broadcast request with id = {0} for user {1}, keys = {2}\",\n\t\t\t\t\t\t\t\tnew Object[]{msgId, packet.getStanzaTo(), repo.dumpBroadcastMessageKeys()});\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t\treturn packet.getPacketTo() == null ||\n\t\t\t\t\t\t!packet.getPacketTo().getDomain().equals(packet.getPacketFrom().getDomain());\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.FINEST, \"repository is NULL !!\");\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic void sendBroadcastMessage(JID jid) {\n\t\tif (repo != null) {\n\t\t\tfor (Object o : repo.getBroadcastMessages()) {\n\t\t\t\tMsgBroadcastRepository.BroadcastMsg msg = (MsgBroadcastRepository.BroadcastMsg) o;\n\t\t\t\tif (msg.getDelay(TimeUnit.MILLISECONDS) > 0 && msg.needToSend(jid)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tsendBroadcastMessage(jid, msg);\n\t\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"should not happen, contact developer\", ex);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void sendBroadcastMessage(JID jid, MsgBroadcastRepository.BroadcastMsg msg)\n\t\t\tthrows TigaseStringprepException {\n\t\tElement msgEl = msg.msg.clone();\n\t\tmsgEl.setAttribute(\"to\", jid.toString());\n\t\tPacket p = Packet.packetInstance(msgEl);\n\t\tresultsHandler.addOutPacket(p);\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic Map<String, Object> getDefaults(Map<String, Object> params) {\n\t\tMap<String, Object> defs = new HashMap<String, Object>();\n\t\tString db_uri = (String) params.get(AMP_MSG_REPO_URI_PARAM);\n\t\tString db_cls = (String) params.get(AMP_MSG_REPO_CLASS_PARAM);\n\n\t\tif (db_uri == null) {\n\t\t\tdb_uri = (String) params.get(RepositoryFactory.GEN_USER_DB_URI);\n\t\t}\n\t\tif (db_uri != null) {\n\t\t\tdefs.put(AMP_MSG_REPO_URI_PROP_KEY, db_uri);\n\t\t}\n\t\tif (db_cls != null) {\n\t\t\tdefs.put(AMP_MSG_REPO_CLASS_PROP_KEY, db_cls);\n\t\t}\n\n\t\treturn defs;\n\t}\n\n\tpublic void setRepo(MsgBroadcastRepositoryIfc repo) {\n\t\trepo.loadMessagesToBroadcast();\n\t\tthis.repo = repo;\n\t}\n\n\tpublic void setActionResultsHandler(ActionResultsHandlerIfc handler) {\n\t\tthis.resultsHandler = handler;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/amp/action/Deliver.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp.action;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Packet;\nimport tigase.server.amp.ActionAbstract;\nimport tigase.server.amp.AmpComponent;\nimport tigase.xml.Element;\n\n/**\n * Created: May 1, 2010 11:28:40 AM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"deliver\", parent = AmpComponent.class, active = true)\npublic class Deliver\n\t\textends ActionAbstract {\n\n\tprivate static final String name = \"deliver\";\n\n\t@Override\n\tpublic boolean execute(Packet packet, Element rule) {\n\t\tPacket result = packet.copyElementOnly();\n\t\tif (packet.getAttributeStaticStr(FROM_CONN_ID) == null) {\n\t\t\tresult.setPacketFrom(packet.getPacketTo());\n\t\t}\n\t\tresult.setStableId(packet.getStableId());\n\t\tremoveTigasePayload(result);\n\t\tresultsHandler.addOutPacket(result);\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/amp/action/Drop.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp.action;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Packet;\nimport tigase.server.amp.ActionAbstract;\nimport tigase.server.amp.AmpComponent;\nimport tigase.xml.Element;\n\n/**\n * Created: Apr 27, 2010 5:35:33 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"drop\", parent = AmpComponent.class, active = true)\npublic class Drop\n\t\textends ActionAbstract {\n\n\tprivate static final String name = \"drop\";\n\n\t@Override\n\tpublic boolean execute(Packet packet, Element rule) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/amp/action/Error.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp.action;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Packet;\nimport tigase.server.amp.ActionAbstract;\nimport tigase.server.amp.AmpComponent;\nimport tigase.xml.Element;\nimport tigase.xmpp.PacketErrorTypeException;\n\n/**\n * Created: Apr 27, 2010 5:35:45 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"error\", parent = AmpComponent.class, active = true)\npublic class Error\n\t\textends ActionAbstract {\n\n\tprivate static final String name = \"error\";\n\tprivate static final String FAILED_RULES_PATH = \"error/failed-rules\";\n\tprivate static final Element UNDEF_CONDITION = new Element(\"undefined-condition\", new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{\"urn:ietf:params:xml:ns:xmpp-stanzas\"});\n\tprivate static final Element FAILED_RULES = new Element(\"failed-rules\", new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew String[]{\"http://jabber.org/protocol/amp#errors\"});\n\tprivate static final Element ERROR_TEMPLATE = new Element(\"error\", new Element[]{UNDEF_CONDITION},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{\"type\", \"code\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{\"modify\", \"500\"});\n\n\t@Override\n\tpublic boolean execute(Packet packet, Element rule) {\n\t\ttry {\n\t\t\tPacket result = prepareAmpPacket(packet, rule);\n\t\t\tElement error = ERROR_TEMPLATE.clone();\n\t\t\tElement failed_rules = FAILED_RULES.clone();\n\n\t\t\tfailed_rules.addChild(rule);\n\t\t\terror.addChild(failed_rules);\n\t\t\tresult.getElement().addChild(error);\n\t\t\tresultsHandler.addOutPacket(result);\n\t\t} catch (PacketErrorTypeException ex) {\n\n\t\t\t// Ignore\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/amp/action/Notify.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp.action;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Packet;\nimport tigase.server.amp.ActionAbstract;\nimport tigase.server.amp.AmpComponent;\nimport tigase.xml.Element;\nimport tigase.xmpp.PacketErrorTypeException;\n\n/**\n * Created: Apr 27, 2010 5:36:03 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"notify\", parent = AmpComponent.class, active = true)\npublic class Notify\n\t\textends ActionAbstract {\n\n\tprivate static final String name = \"notify\";\n\n\t@Override\n\tpublic boolean execute(Packet packet, Element rule) {\n\t\ttry {\n\t\t\tif (packet.getStanzaFrom() != null && packet.getStanzaFrom().getBareJID().getLocalpart() != null) {\n\t\t\t\tPacket result = prepareAmpPacket(packet, rule);\n\t\t\t\tresultsHandler.addOutPacket(result);\n\t\t\t}\n\n\t\t} catch (PacketErrorTypeException ex) {\n\n\t\t\t// Ignore\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/amp/action/Store.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp.action;\n\nimport tigase.db.MsgRepositoryIfc;\nimport tigase.db.NonAuthUserRepositoryImpl;\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserNotFoundException;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.server.Packet;\nimport tigase.server.amp.ActionAbstract;\nimport tigase.server.amp.AmpComponent;\nimport tigase.server.amp.cond.ExpireAt;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.PacketErrorTypeException;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.TimeZone;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: May 1, 2010 11:32:59 AM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"store\", parent = AmpComponent.class, active = true)\npublic class Store\n\t\textends ActionAbstract\n\t\timplements Initializable, UnregisterAware {\n\n\tprivate static final Logger log = Logger.getLogger(Store.class.getName());\n\tprivate static final String name = \"store\";\n\n\tprivate final SimpleDateFormat formatter;\n\tprivate final SimpleDateFormat formatter2;\n\t// ~--- fields ---------------------------------------------------------------\n\tprivate Thread expiredProcessor = null;\n\t@Inject\n\tprivate NonAuthUserRepositoryImpl nonAuthUserRepo;\n\t@Inject\n\tprivate MsgRepositoryIfc repo = null;\n\n\t{\n\t\tformatter = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'\");\n\t\tformatter.setTimeZone(TimeZone.getTimeZone(\"UTC\"));\n\t\tformatter2 = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss'Z'\");\n\t\tformatter2.setTimeZone(TimeZone.getTimeZone(\"UTC\"));\n\t}\n\n\t@Override\n\tpublic boolean execute(Packet packet, Element rule) {\n\t\tif (repo != null) {\n\t\t\tDate expired = null;\n\t\t\tString stamp = null;\n\n\t\t\tif (packet.getAttributeStaticStr(EXPIRED) == null) {\n\t\t\t\tif (rule == null) {\n\t\t\t\t\trule = getExpireAtRule(packet);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tremoveExpireAtRule(packet);\n\t\t\t\trule = null;\n\t\t\t}\n\t\t\tif (rule != null) {\n\t\t\t\ttry {\n\t\t\t\t\tString value = rule.getAttributeStaticStr(\"value\");\n\t\t\t\t\tif (value != null) {\n\t\t\t\t\t\tif (value.contains(\".\")) {\n\t\t\t\t\t\t\tsynchronized (formatter) {\n\t\t\t\t\t\t\t\texpired = formatter.parse(value);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tsynchronized (formatter2) {\n\t\t\t\t\t\t\t\texpired = formatter2.parse(value);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.log(Level.CONFIG, \"Incorrect expire-at value: \" + rule.getAttributeStaticStr(\"value\"), e);\n\t\t\t\t\texpired = null;\n\t\t\t\t}\n\t\t\t}\n\t\t\tsynchronized (formatter) {\n\t\t\t\tstamp = formatter.format(new Date());\n\t\t\t}\n\t\t\tremoveTigasePayload(packet);\n\t\t\ttry {\n\t\t\t\tElement elem = packet.getElement();\n\n\t\t\t\tif (elem.getChild(\"delay\", \"urn:xmpp:delay\") == null) {\n\t\t\t\t\tElement x = new Element(\"delay\", \"Offline Storage\", new String[]{\"from\", \"stamp\", \"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\tnew String[]{packet.getStanzaTo().getDomain(), stamp, \"urn:xmpp:delay\"});\n\n\t\t\t\t\telem.addChild(x);\n\t\t\t\t}\n\t\t\t\trepo.storeMessage(packet.getStanzaFrom(), packet.getStanzaTo(), expired, elem, nonAuthUserRepo);\n\t\t\t} catch (UserNotFoundException ex) {\n\t\t\t\tlog.log(Level.CONFIG, \"User not found for offline message: \" + packet);\n\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Could not save packet for offline user \" + packet, ex);\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tresultsHandler.addOutPacket(Authorization.INTERNAL_SERVER_ERROR.getResponseMessage(packet, null, true));\n\t\t\t\t} catch (PacketErrorTypeException e) {\n\t\t\t\t\tlog.finest(\"Could not sent error for unsaved packet for offline user \" + packet);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tif ((repo != null) && (expiredProcessor == null)) {\n\t\t\texpiredProcessor = new Thread(\"expired-processor\") {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tThread.sleep(90 * 1000);\n\t\t\t\t\t\twhile (true) {\n\t\t\t\t\t\t\tElement elem = repo.getMessageExpired(0, true);\n\n\t\t\t\t\t\t\tif (elem != null) {\n\t\t\t\t\t\t\t\telem.addAttribute(OFFLINE, \"1\");\n\t\t\t\t\t\t\t\telem.addAttribute(EXPIRED, \"1\");\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tresultsHandler.addOutPacket(Packet.packetInstance(elem));\n\t\t\t\t\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\t\t\t\t\tlog.log(Level.CONFIG, \"Stringprep error for offline message loaded from DB: \" + elem);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (Thread.interrupted()) {\n\t\t\t\t\t\t\t\tlog.log(Level.INFO, \"stopping expired-processor\");\n\t\t\t\t\t\t\t\texpiredProcessor = null;\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Could not initialize expired processor\", e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\t\t\texpiredProcessor.setDaemon(true);\n\t\t\texpiredProcessor.start();\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\tif (expiredProcessor != null) {\n\t\t\texpiredProcessor.interrupt();\n\t\t}\n\t}\n\n\t// ~--- get methods ----------------------------------------------------------\n\tprivate Element getExpireAtRule(Packet packet) {\n\t\tElement amp = packet.getElement().getChild(\"amp\", AMP_XMLNS);\n\t\tList<Element> rules = amp.getChildren();\n\t\tElement rule = null;\n\n\t\tif ((rules != null) && (rules.size() > 0)) {\n\t\t\tfor (Element r : rules) {\n\t\t\t\tString cond = r.getAttributeStaticStr(CONDITION_ATT);\n\n\t\t\t\tif ((cond != null) && cond.equals(ExpireAt.NAME)) {\n\t\t\t\t\trule = r;\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn rule;\n\t}\n\n\t// ~--- methods --------------------------------------------------------------\n\tprivate void removeExpireAtRule(Packet packet) {\n\t\tElement amp = packet.getElement().getChild(\"amp\", AMP_XMLNS);\n\t\tList<Element> rules = amp.getChildren();\n\n\t\tif ((rules != null) && (rules.size() > 0)) {\n\t\t\tfor (Element r : rules) {\n\t\t\t\tString cond = r.getAttributeStaticStr(CONDITION_ATT);\n\n\t\t\t\tif ((cond != null) && cond.equals(ExpireAt.NAME)) {\n\t\t\t\t\tamp.removeChild(r);\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\trules = amp.getChildren();\n\t\tif ((rules == null) || (rules.size() == 0)) {\n\t\t\tpacket.getElement().removeChild(amp);\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/amp/cond/Deliver.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp.cond;\n\nimport tigase.server.Packet;\nimport tigase.server.amp.ConditionIfc;\nimport tigase.xml.Element;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Apr 27, 2010 5:36:27 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class Deliver\n\t\timplements ConditionIfc {\n\n\tprivate static final String name = \"deliver\";\n\tprivate static Logger log = Logger.getLogger(Deliver.class.getName());\n\n\tprivate boolean offline_storage = true;\n\n\n\tpublic Deliver() {\n\t\tString off_val = System.getProperty(MSG_OFFLINE_PROP_KEY);\n\n\t\toffline_storage = (off_val == null) || Boolean.parseBoolean(off_val);\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic boolean match(Packet packet, Element rule) {\n\t\tString value = rule.getAttributeStaticStr(\"value\");\n\t\tboolean result = false;\n\n\t\tif (value != null) {\n\t\t\ttry {\n\t\t\t\tMatchValue m_val = MatchValue.valueOf(value);\n\n\t\t\t\tswitch (m_val) {\n\t\t\t\t\tcase direct:\n\t\t\t\t\t\tresult = (packet.getAttributeStaticStr(OFFLINE) == null) &&\n\t\t\t\t\t\t\t\t(packet.getAttributeStaticStr(FROM_CONN_ID) == null);\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase forward:\n\n\t\t\t\t\t\t// Forwarding not supported in Tigase yet\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase gateway:\n\n\t\t\t\t\t\t// This can be only determined by the gateway itself\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase none:\n\t\t\t\t\t\tresult = (packet.getAttributeStaticStr(OFFLINE) != null) && !offline_storage;\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase stored:\n\t\t\t\t\t\tresult = (packet.getAttributeStaticStr(OFFLINE) != null) && offline_storage;\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.CONFIG, \"Incorrect \" + name + \" condition value for rule: \" + rule);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.CONFIG, \"No value set for rule: \" + rule);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate enum MatchValue {\n\t\tdirect,\n\t\tforward,\n\t\tgateway,\n\t\tnone,\n\t\tstored;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/amp/cond/ExpireAt.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp.cond;\n\nimport tigase.server.Packet;\nimport tigase.server.amp.ConditionIfc;\nimport tigase.xml.Element;\n\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.TimeZone;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Apr 27, 2010 5:36:39 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class ExpireAt\n\t\timplements ConditionIfc {\n\n\tpublic static final String NAME = \"expire-at\";\n\tprivate static Logger log = Logger.getLogger(ExpireAt.class.getName());\n\n\tprivate final SimpleDateFormat formatter;\n\tprivate final SimpleDateFormat formatter2;\n\n\t{\n\t\tformatter = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'\");\n\t\tformatter.setTimeZone(TimeZone.getTimeZone(\"UTC\"));\n\t\tformatter2 = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss'Z'\");\n\t\tformatter2.setTimeZone(TimeZone.getTimeZone(\"UTC\"));\n\t}\n\t@Override\n\tpublic String getName() {\n\t\treturn NAME;\n\t}\n\n\t@Override\n\tpublic boolean match(Packet packet, Element rule) {\n\t\tString value = rule.getAttributeStaticStr(\"value\");\n\n\t\tif (value != null) {\n\t\t\ttry {\n\t\t\t\tDate val_date = null;\n\n\t\t\t\tif (value.contains(\".\")) {\n\t\t\t\t\tsynchronized (formatter) {\n\t\t\t\t\t\tval_date = formatter.parse(value);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tsynchronized (formatter2) {\n\t\t\t\t\t\tval_date = formatter2.parse(value);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn val_date.before(new Date());\n\t\t\t} catch (ParseException ex) {\n\t\t\t\tlog.log(Level.CONFIG, \"Incorrect \" + NAME + \" condition value for rule: \" + rule);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.CONFIG, \"No value set for rule: \" + rule);\n\t\t}\n\n\t\treturn false;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/amp/cond/MatchResource.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp.cond;\n\nimport tigase.server.Packet;\nimport tigase.server.amp.ConditionIfc;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Apr 27, 2010 5:36:54 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class MatchResource\n\t\timplements ConditionIfc {\n\n\tprivate static final String name = \"match-resource\";\n\tprivate static Logger log = Logger.getLogger(MatchResource.class.getName());\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic boolean match(Packet packet, Element rule) {\n\t\tString value = rule.getAttributeStaticStr(\"value\");\n\t\tboolean result = false;\n\n\t\tif (value != null) {\n\t\t\ttry {\n\t\t\t\tMatchValue m_val = MatchValue.valueOf(value);\n\t\t\t\tString jid_resource = (packet.getStanzaTo() != null) ? packet.getStanzaTo().getResource() : null;\n\t\t\t\tString target_resource = packet.getAttributeStaticStr(TO_RES);\n\t\t\t\tString from_session_jid_string = packet.getAttributeStaticStr(SESSION_JID);\n\t\t\t\tJID from_original_jid =\n\t\t\t\t\t\tfrom_session_jid_string != null ? JID.jidInstance(from_session_jid_string) : null;\n\n\t\t\t\tswitch (m_val) {\n\t\t\t\t\tcase any:\n\t\t\t\t\t\tresult = true;\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase other:\n\t\t\t\t\t\tresult = ((jid_resource != null) && (target_resource != null) &&\n\t\t\t\t\t\t\t\t!jid_resource.equals(target_resource))\n\n\t\t\t\t\t\t\t\t|| (from_original_jid == null && target_resource == null);\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase exact:\n\t\t\t\t\t\tresult = (jid_resource != null) && jid_resource.equals(target_resource);\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.CONFIG, \"Incorrect \" + name + \" condition value for rule: \" + rule);\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.CONFIG, \"No value set for rule: \" + rule);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate enum MatchValue {\n\t\tany,\n\t\texact,\n\t\tother;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/amp/db/JDBCMsgBroadcastRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp.db;\n\nimport tigase.db.DataRepository;\nimport tigase.db.Repository;\nimport tigase.db.Schema;\nimport tigase.db.util.RepositoryVersionAware;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.xml.DomBuilderHandler;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Timestamp;\nimport java.util.Date;\nimport java.util.HashSet;\nimport java.util.Queue;\nimport java.util.Set;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.server.amp.db.JDBCMsgRepository.Meta;\n\n/**\n * Created by andrzej on 15.03.2016.\n */\n@Meta(isDefault = true, supportedUris = {\"jdbc:[^:]+:.*\"})\n@Repository.SchemaId(id = Schema.SERVER_SCHEMA_ID, name = Schema.SERVER_SCHEMA_NAME)\npublic class JDBCMsgBroadcastRepository\n\t\textends MsgBroadcastRepository<Long, DataRepository>\n\t\timplements RepositoryVersionAware {\n\n\tprivate static final Logger log = Logger.getLogger(JDBCMsgBroadcastRepository.class.getCanonicalName());\n\n\t@ConfigField(desc = \"Query to add message to broadcast messsages\", alias = \"add-message\")\n\tprivate String BROADCAST_ADD_MESSAGE = \"{ call Tig_BroadcastMessages_AddMessage(?,?,?) }\";\n\t@ConfigField(desc = \"Query to add message recipient to broadcast message\", alias = \"add-message-recipient\")\n\tprivate String BROADCAST_ADD_MESSAGE_RECIPIENT = \"{ call Tig_BroadcastMessages_AddMessageRecipient(?,?) }\";\n\t@ConfigField(desc = \"Query to load not expired broadcast messages\", alias = \"get-messages\")\n\tprivate String BROADCAST_GET_MESSAGES = \"{ call Tig_BroadcastMessages_GetMessages(?) }\";\n\t@ConfigField(desc = \"Query to load recipients of broadcast message\", alias = \"get-message-recipients\")\n\tprivate String BROADCAST_GET_MESSAGE_RECIPIENTS = \"{ call Tig_BroadcastMessages_GetMessageRecipients(?) }\";\n\n\tprivate DataRepository data_repo = null;\n\n\t@Override\n\tpublic void setDataSource(DataRepository data_repo) {\n\t\ttry {\n\t\t\tdata_repo.initPreparedStatement(BROADCAST_ADD_MESSAGE, BROADCAST_ADD_MESSAGE);\n\t\t\tdata_repo.initPreparedStatement(BROADCAST_ADD_MESSAGE_RECIPIENT, BROADCAST_ADD_MESSAGE_RECIPIENT);\n\t\t\tdata_repo.initPreparedStatement(BROADCAST_GET_MESSAGES, BROADCAST_GET_MESSAGES);\n\t\t\tdata_repo.initPreparedStatement(BROADCAST_GET_MESSAGE_RECIPIENTS, BROADCAST_GET_MESSAGE_RECIPIENTS);\n\t\t} catch (SQLException ex) {\n\t\t\tlog.log(Level.WARNING, \"MsgRepository not initialized due to exception\", ex);\n\t\t}\n\n\t\tthis.data_repo = data_repo;\n\t}\n\n\t@Override\n\tpublic void loadMessagesToBroadcast() {\n\t\ttry {\n\t\t\tSet<String> oldMessages = new HashSet<String>(broadcastMessages.keySet());\n\n\t\t\tResultSet rs = null;\n\t\t\tPreparedStatement stmt = data_repo.getPreparedStatement(null, BROADCAST_GET_MESSAGES);\n\n\t\t\tsynchronized (stmt) {\n\t\t\t\ttry {\n\t\t\t\t\tTimestamp ts = new Timestamp(System.currentTimeMillis());\n\t\t\t\t\tlog.log(Level.FINEST, \"loading expiring after \" + ts);\n\t\t\t\t\tdata_repo.setTimestamp(stmt, 1, ts);\n\t\t\t\t\trs = stmt.executeQuery();\n\n\t\t\t\t\tDomBuilderHandler domHandler = new DomBuilderHandler();\n\t\t\t\t\twhile (rs.next()) {\n\t\t\t\t\t\tString msgId = rs.getString(1);\n\t\t\t\t\t\tlog.log(Level.FINEST, \"loaded msg with id = \" + msgId);\n\t\t\t\t\t\toldMessages.remove(msgId);\n\t\t\t\t\t\tif (broadcastMessages.containsKey(msgId)) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tDate expire = data_repo.getTimestamp(rs, 2);\n\t\t\t\t\t\tchar[] msgChars = rs.getString(3).toCharArray();\n\n\t\t\t\t\t\tparser.parse(domHandler, msgChars, 0, msgChars.length);\n\n\t\t\t\t\t\tQueue<Element> elems = domHandler.getParsedElements();\n\t\t\t\t\t\tElement msg = elems.poll();\n\t\t\t\t\t\tif (msg == null) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"not adding - msg is null!\");\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbroadcastMessages.put(msgId, new BroadcastMsg(null, msg, expire));\n\t\t\t\t\t}\n\t\t\t\t\tlog.log(Level.FINEST, \"message loading finished!\");\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (String id : oldMessages) {\n\t\t\t\tbroadcastMessages.remove(id);\n\t\t\t}\n\n\t\t\trs = null;\n\n\t\t\tfor (String id : broadcastMessages.keySet()) {\n\t\t\t\tBroadcastMsg bmsg = broadcastMessages.get(id);\n\t\t\t\tstmt = data_repo.getPreparedStatement(null, BROADCAST_GET_MESSAGE_RECIPIENTS);\n\t\t\t\tsynchronized (stmt) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tstmt.setString(1, id);\n\t\t\t\t\t\trs = stmt.executeQuery();\n\t\t\t\t\t\twhile (rs.next()) {\n\t\t\t\t\t\t\tBareJID jid = BareJID.bareJIDInstanceNS(rs.getString(1));\n\t\t\t\t\t\t\tbmsg.addRecipient(jid);\n\t\t\t\t\t\t}\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (SQLException ex) {\n\t\t\tlog.log(Level.WARNING, \"Problem with retrieving broadcast messages\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void insertBroadcastMessage(String id, Element msg, Date expire, BareJID recipient) {\n\t\ttry {\n\t\t\tPreparedStatement stmt = data_repo.getPreparedStatement(recipient, BROADCAST_ADD_MESSAGE);\n\t\t\tsynchronized (stmt) {\n\t\t\t\tstmt.setString(1, id);\n\t\t\t\tdata_repo.setTimestamp(stmt, 2, new Timestamp(expire.getTime()));\n\t\t\t\tstmt.setString(3, msg.toString());\n\t\t\t\tstmt.executeUpdate();\n\t\t\t}\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.WARNING, \"Problem with updating broadcast message\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void ensureBroadcastMessageRecipient(String id, BareJID recipient) {\n\t\ttry {\n\t\t\tPreparedStatement stmt = data_repo.getPreparedStatement(recipient, BROADCAST_ADD_MESSAGE_RECIPIENT);\n\t\t\tsynchronized (stmt) {\n\t\t\t\tstmt.setString(1, id);\n\t\t\t\tstmt.setString(2, recipient.toString());\n\t\t\t\tstmt.executeUpdate();\n\t\t\t}\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.WARNING, \"Problem with updating broadcast message\", ex);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/amp/db/JDBCMsgRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp.db;\n\nimport tigase.db.*;\nimport tigase.db.util.JDBCPasswordObfuscator;\nimport tigase.db.util.RepositoryVersionAware;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Packet;\nimport tigase.util.ExceptionUtilities;\nimport tigase.xml.DomBuilderHandler;\nimport tigase.xml.Element;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.sql.*;\nimport java.util.Date;\nimport java.util.*;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.IntStream;\n\n/**\n * Created: May 3, 2010 5:28:02 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Repository.Meta(isDefault = true, supportedUris = {\"jdbc:[^:]+:.*\"})\n@Repository.SchemaId(id = Schema.SERVER_SCHEMA_ID, name = Schema.SERVER_SCHEMA_NAME)\npublic class JDBCMsgRepository\n\t\textends MsgRepository<Long, DataRepository>\n\t\timplements RepositoryVersionAware {\n\n\tprivate static final Logger log = Logger.getLogger(JDBCMsgRepository.class.getName());\n\tprotected DataRepository data_repo = null;\n\t@ConfigField(desc = \"Query to add message\", alias = \"add-message-query\")\n\tprivate String MSGS_ADD_MESSAGE = \"{ call Tig_OfflineMessages_AddMessage(?,?,?,?,?,?,?) }\";\n\t@ConfigField(desc = \"Query to count messages\", alias = \"count-messages-query\")\n\tprivate String MSGS_COUNT_MESSAGES = \"{ call Tig_OfflineMessages_GetMessagesCount(?) }\";\n\t@ConfigField(desc = \"Query to delete message\", alias = \"delete-message-query\")\n\tprivate String MSGS_DELETE_MESSAGE = \"{ call Tig_OfflineMessages_DeleteMessage(?) }\";\n\t@ConfigField(desc = \"Query to delete messages\", alias = \"delete-messages-query\")\n\tprivate String MSGS_DELETE_MESSAGES = \"{ call Tig_OfflineMessages_DeleteMessages(?) }\";\n\t@ConfigField(desc = \"Query to delete messages by ids\", alias = \"delete-messages-by-ids-query\")\n\tprivate String MSGS_DELETE_MESSAGES_BY_IDS = \"{ call Tig_OfflineMessages_DeleteMessagesByIds(?,?,?,?,?) }\";\n\t@ConfigField(desc = \"Query to select expired messages\", alias = \"get-expired-messages-query\")\n\tprivate String MSGS_GET_EXPIRED_MESSAGES = \"{ call Tig_OfflineMessages_GetExpiredMessages(?) }\";\n\t@ConfigField(desc = \"Query to select expired messages before passed time\", alias = \"get-expired-messages-before-query\")\n\tprivate String MSGS_GET_EXPIRED_MESSAGES_BEFORE = \"{ call Tig_OfflineMessages_GetExpiredMessagesBefore(?) }\";\n\t@ConfigField(desc = \"Query to load messages\", alias = \"get-messages-query\")\n\tprivate String MSGS_GET_MESSAGES = \"{ call Tig_OfflineMessages_GetMessages(?) }\";\n\t@ConfigField(desc = \"Query to load messages by ids\", alias = \"get-messages-by-ids-query\")\n\tprivate String MSGS_GET_MESSAGES_BY_IDS = \"{ call Tig_OfflineMessages_GetMessagesByIds(?,?,?,?,?) }\";\n\n//\tprivate static final Map<String, JDBCMsgRepository> repos =\n//\t\t\tnew ConcurrentSkipListMap<String, JDBCMsgRepository>();\n\n\t// ~--- fields ---------------------------------------------------------------\n\t@ConfigField(desc = \"Query to list messages\", alias = \"list-messages-query\")\n\tprivate String MSGS_LIST_MESSAGES = \"{ call Tig_OfflineMessages_ListMessages(?) }\";\n\tprivate boolean initialized = false;\n\n\t@Override\n\tpublic void setDataSource(DataRepository data_repo) {\n\t\ttry {\n\t\t\tdata_repo.initPreparedStatement(MSGS_ADD_MESSAGE, MSGS_ADD_MESSAGE);\n\t\t\tdata_repo.initPreparedStatement(MSGS_COUNT_MESSAGES, MSGS_COUNT_MESSAGES);\n\t\t\tdata_repo.initPreparedStatement(MSGS_LIST_MESSAGES, MSGS_LIST_MESSAGES);\n\t\t\tdata_repo.initPreparedStatement(MSGS_GET_MESSAGES, MSGS_GET_MESSAGES);\n\t\t\tdata_repo.initPreparedStatement(MSGS_GET_MESSAGES_BY_IDS, MSGS_GET_MESSAGES_BY_IDS);\n\t\t\tdata_repo.initPreparedStatement(MSGS_DELETE_MESSAGE, MSGS_DELETE_MESSAGE);\n\t\t\tdata_repo.initPreparedStatement(MSGS_DELETE_MESSAGES, MSGS_DELETE_MESSAGES);\n\t\t\tdata_repo.initPreparedStatement(MSGS_DELETE_MESSAGES_BY_IDS, MSGS_DELETE_MESSAGES_BY_IDS);\n\t\t\tdata_repo.initPreparedStatement(MSGS_GET_EXPIRED_MESSAGES, MSGS_GET_EXPIRED_MESSAGES);\n\t\t\tdata_repo.initPreparedStatement(MSGS_GET_EXPIRED_MESSAGES_BEFORE, MSGS_GET_EXPIRED_MESSAGES_BEFORE);\n\t\t} catch (SQLException ex) {\n\t\t\tlog.log(Level.WARNING, \"MsgRepository not initialized due to exception\",\n\t\t\t\t\tExceptionUtilities.getExceptionRootCause(ex, true));\n\t\t\tthrow new RuntimeException(\n\t\t\t\t\t\"Could not initialize JDBCMsgRepository instance for \" + JDBCPasswordObfuscator.obfuscatePassword(data_repo.getResourceUri()), ex);\n\t\t}\n\n\t\tthis.data_repo = data_repo;\n\t}\n\n\t@Override\n\t@Deprecated\n\tpublic void initRepository(String conn_str, Map<String, String> map) throws DBInitException {\n\t\tif (initialized) {\n\t\t\treturn;\n\t\t}\n\n\t\tinitialized = true;\n\t\tlog.log(Level.CONFIG, \"Initializing dbAccess for db connection url: {0}\", conn_str);\n\n\t\tsuper.initRepository(conn_str, map);\n\n\t\ttry {\n\t\t\tdata_repo = RepositoryFactory.getDataRepository(null, conn_str, map);\n\t\t\tdata_repo.checkSchemaVersion(this, true);\n\t\t\tsetDataSource(data_repo);\n\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"MsgRepository not initialized due to exception\", e);\n\t\t\t// Ignore for now....\n\t\t}\n\t}\n\n\t@Override\n\tpublic Map<Enum, Long> getMessagesCount(JID to) {\n\n\t\tMap<Enum, Long> result = new HashMap<>(MSG_TYPES.values().length);\n\n\t\ttry {\n\n\t\t\tResultSet rs = null;\n\n\t\t\t// get number of messages\n\t\t\tPreparedStatement number_of_messages = data_repo.getPreparedStatement(to.getBareJID(), MSGS_COUNT_MESSAGES);\n\n\t\t\tsynchronized (number_of_messages) {\n\t\t\t\tnumber_of_messages.setString(1, to.getBareJID().toString());\n\t\t\t\trs = number_of_messages.executeQuery();\n\n\t\t\t\twhile (rs.next()) {\n\t\t\t\t\tint msgType = rs.getInt(1);\n\t\t\t\t\tlong msgCount = rs.getLong(2);\n\t\t\t\t\tresult.put(MSG_TYPES.getFromInt(msgType), msgCount);\n\t\t\t\t}\n\t\t\t}\n\n\t\t} catch (SQLException e) {\n\t\t\tlog.log(Level.WARNING, \"Problem getting offline messages for user: \" + to, e);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic List<Element> getMessagesList(JID to) {\n\t\tList<Element> result = new LinkedList<Element>();\n\t\tResultSet rs = null;\n\n\t\ttry {\n\t\t\tPreparedStatement select_messages_list = data_repo.getPreparedStatement(to.getBareJID(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tMSGS_LIST_MESSAGES);\n\n\t\t\tsynchronized (select_messages_list) {\n\t\t\t\ttry {\n\t\t\t\t\tselect_messages_list.setString(1, to.getBareJID().toString());\n\n\t\t\t\t\trs = select_messages_list.executeQuery();\n\n\t\t\t\t\twhile (rs.next()) {\n\t\t\t\t\t\tlong msgId = rs.getLong(1);\n\t\t\t\t\t\tint mType = rs.getInt(2);\n\t\t\t\t\t\tMSG_TYPES messageType = MSG_TYPES.getFromInt(mType);\n\t\t\t\t\t\tString sender = rs.getString(3);\n\n\t\t\t\t\t\tif (msgId != 0 && messageType != MSG_TYPES.none && sender != null) {\n\t\t\t\t\t\t\tElement item = new Element(\"item\", new String[]{\"jid\", \"node\", \"type\", \"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{to.getBareJID().toString(), String.valueOf(msgId),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tmessageType.name(), sender});\n\t\t\t\t\t\t\tresult.add(item);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t}\n\t\t\t}\n\n\t\t} catch (SQLException e) {\n\t\t\tlog.log(Level.WARNING, \"Problem getting offline messages for user: \" + to, e);\n\t\t}\n\t\treturn result;\n\n\t}\n\n\t@Override\n\tpublic Queue<Element> loadMessagesToJID(List<String> db_ids, XMPPResourceConnection session, boolean delete,\n\t\t\t\t\t\t\t\t\t\t\tOfflineMessagesProcessor proc) throws UserNotFoundException {\n\n\t\tQueue<Element> result = null;\n\t\tBareJID to = null;\n\n\t\ttry {\n\t\t\tto = session.getBareJID();\n\n\t\t\tif (db_ids == null || db_ids.size() == 0) {\n\t\t\t\t// fetch\n\t\t\t\treturn loadMessagesToJID(session, delete, proc);\n\t\t\t} else {\n\t\t\t\tResultSet rs = null;\n\t\t\t\tresult = new LinkedList<Element>();\n\n\t\t\t\tIterator<String> ids = db_ids.iterator();\n\n\t\t\t\twhile (ids.hasNext()) {\n\t\t\t\t\tPreparedStatement select_ids_to_jid_st = data_repo.getPreparedStatement(to,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tMSGS_GET_MESSAGES_BY_IDS);\n\t\t\t\t\tsynchronized (select_ids_to_jid_st) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tselect_ids_to_jid_st.setString(1, to.toString());\n\t\t\t\t\t\t\tfor (int j = 0; j < 4; j++) {\n\t\t\t\t\t\t\t\tString id = ids.hasNext() ? ids.next() : null;\n\t\t\t\t\t\t\t\tselect_ids_to_jid_st.setString(j + 2, id);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\trs = select_ids_to_jid_st.executeQuery();\n\t\t\t\t\t\t\tresult.addAll(parseLoadedMessages(proc, rs));\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (delete) {\n\t\t\t\tdeleteMessagesToJID(null, session);\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tlog.log(Level.WARNING, \"Problem getting offline messages for user: \" + to, e);\n\t\t} catch (NotAuthorizedException ex) {\n\t\t\tlog.log(Level.WARNING, \"Session not authorized yet!\", ex);\n\t\t}\n\t\treturn result;\n\n\t}\n\n\t@Override\n\tpublic int deleteMessagesToJID(List<String> db_ids, XMPPResourceConnection session) throws UserNotFoundException {\n\n\t\tint affectedRows = 0;\n\t\tBareJID to = null;\n\n\t\ttry {\n\t\t\tto = session.getBareJID();\n\n\t\t\tif (db_ids == null || db_ids.size() == 0) {\n\t\t\t\t// purge\n\t\t\t\tPreparedStatement delete_to_jid_st = data_repo.getPreparedStatement(to, MSGS_DELETE_MESSAGES);\n\n\t\t\t\tResultSet rs = null;\n\t\t\t\tsynchronized (delete_to_jid_st) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tdelete_to_jid_st.setString(1, to.toString());\n\t\t\t\t\t\tdelete_to_jid_st.executeUpdate();\n\t\t\t\t\t\t// we return 1 if messages were removed, but we not check if any message was actually removed\n\t\t\t\t\t\taffectedRows += 1;\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tIterator<String> ids = db_ids.iterator();\n\n\t\t\t\twhile (ids.hasNext()) {\n\t\t\t\t\tPreparedStatement delete_to_jid_st = data_repo.getPreparedStatement(to,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tMSGS_DELETE_MESSAGES_BY_IDS);\n\t\t\t\t\tResultSet rs = null;\n\t\t\t\t\tsynchronized (delete_to_jid_st) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tdelete_to_jid_st.setString(1, to.toString());\n\t\t\t\t\t\t\tfor (int j = 0; j < 4; j++) {\n\t\t\t\t\t\t\t\tString id = ids.hasNext() ? ids.next() : null;\n\t\t\t\t\t\t\t\tdelete_to_jid_st.setString(j + 2, id);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\trs = delete_to_jid_st.executeQuery();\n\t\t\t\t\t\t\tif (rs.next()) {\n\t\t\t\t\t\t\t\taffectedRows += rs.getInt(1);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tlog.log(Level.WARNING, \"Problem getting offline messages for user: \" + to, e);\n\t\t} catch (NotAuthorizedException ex) {\n\t\t\tlog.log(Level.WARNING, \"Session not authorized yet!\", ex);\n\t\t}\n\n\t\treturn affectedRows;\n\t}\n\n\t@Override\n\tpublic Queue<Element> loadMessagesToJID(XMPPResourceConnection session, boolean delete)\n\t\t\tthrows UserNotFoundException {\n\t\treturn loadMessagesToJID(session, delete, null);\n\t}\n\n\tprivate final ReentrantReadWriteLock locks[] = IntStream.range(0, 128)\n\t\t\t.mapToObj(i -> new ReentrantReadWriteLock())\n\t\t\t.toArray(ReentrantReadWriteLock[]::new);\n\n\tprivate ReentrantReadWriteLock getLock(BareJID jid) {\n\t\tif (jid == null) {\n\t\t\treturn locks[0];\n\t\t}\n\t\treturn locks[Math.abs(jid.hashCode() % locks.length)];\n\t}\n\n\tpublic Queue<Element> loadMessagesToJID(XMPPResourceConnection session, boolean delete,\n\t\t\t\t\t\t\t\t\t\t\tOfflineMessagesProcessor proc) throws UserNotFoundException {\n\t\tQueue<Element> result = null;\n\t\tBareJID to = null;\n\n\t\tReentrantReadWriteLock.WriteLock lock = null;\n\t\ttry {\n\t\t\tto = session.getBareJID();\n\t\t\tlock = getLock(to).writeLock();\n\t\t\tlock.lock();\n\n\t\t\tResultSet rs = null;\n\t\t\tPreparedStatement select_to_jid_st = data_repo.getPreparedStatement(to, MSGS_GET_MESSAGES);\n\n\t\t\tsynchronized (select_to_jid_st) {\n\t\t\t\ttry {\n\t\t\t\t\tselect_to_jid_st.setString(1, to.toString());\n\t\t\t\t\trs = select_to_jid_st.executeQuery();\n\n\t\t\t\t\tresult = parseLoadedMessages(proc, rs);\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (delete) {\n\t\t\t\trs = null;\n\t\t\t\ttry {\n\t\t\t\t\tPreparedStatement delete_to_jid_st = data_repo.getPreparedStatement(to, MSGS_DELETE_MESSAGES);\n\n\t\t\t\t\tsynchronized (delete_to_jid_st) {\n\t\t\t\t\t\tdelete_to_jid_st.setString(1, to.toString());\n\t\t\t\t\t\tdelete_to_jid_st.executeUpdate();\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tlog.log(Level.WARNING, \"Problem getting offline messages for user: \" + to, e);\n\t\t} catch (NotAuthorizedException ex) {\n\t\t\tlog.log(Level.WARNING, \"Session not authorized yet!\", ex);\n\t\t} finally {\n\t\t\tif (lock != null) {\n\t\t\t\tlock.unlock();\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean storeMessage(JID from, JID to, Date expired, Element msg, NonAuthUserRepository userRepo)\n\t\t\tthrows UserNotFoundException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Storring expired: {0} message: {1}\",\n\t\t\t\t\tnew Object[]{expired, Packet.elemToString(msg)});\n\t\t}\n\n\t\tboolean result = false;\n\n\t\tReentrantReadWriteLock.ReadLock lock = getLock(to == null ? null : to.getBareJID()).readLock();\n\t\tlock.lock();\n\t\ttry {\n\t\t\tlong msgs_store_limit = getMsgsStoreLimit(to.getBareJID(), userRepo);\n\n\t\t\tPreparedStatement insert_msg_st = data_repo.getPreparedStatement(to.getBareJID(), MSGS_ADD_MESSAGE);\n\n\t\t\tsynchronized (insert_msg_st) {\n\t\t\t\tinsert_msg_st.setString(1, to.getBareJID().toString());\n\t\t\t\tinsert_msg_st.setString(2, from.getBareJID().toString());\n\t\t\t\tint msg_type;\n\t\t\t\ttry {\n\t\t\t\t\tfinal String name = msg.getName();\n\t\t\t\t\tfinal MSG_TYPES valueOf = MSG_TYPES.valueOf(name);\n\t\t\t\t\tmsg_type = valueOf.ordinal();\n\t\t\t\t} catch (IllegalArgumentException e) {\n\t\t\t\t\tmsg_type = Integer.MAX_VALUE;\n\t\t\t\t}\n\n\t\t\t\tinsert_msg_st.setInt(3, msg_type);\n\t\t\t\tdata_repo.setTimestamp(insert_msg_st, 4, new Timestamp(System.currentTimeMillis()));\n\t\t\t\tinsert_msg_st.setString(5, msg.toString());\n\n\t\t\t\tif (expired == null) {\n\t\t\t\t\tinsert_msg_st.setNull(6, Types.TIMESTAMP);\n\t\t\t\t} else {\n\t\t\t\t\tTimestamp time = new Timestamp(expired.getTime());\n\n\t\t\t\t\tdata_repo.setTimestamp(insert_msg_st, 6, time);\n\t\t\t\t}\n\t\t\t\tinsert_msg_st.setLong(7, msgs_store_limit);\n\n\t\t\t\ttry (ResultSet rs = insert_msg_st.executeQuery()) {\n\t\t\t\t\tif (rs.next()) {\n\t\t\t\t\t\tresult = rs.getLong(1) != 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (expired != null) {\n\t\t\t\tif (expired.getTime() < earliestOffline) {\n\t\t\t\t\tearliestOffline = expired.getTime();\n\t\t\t\t}\n\n\t\t\t\tif (awaitingInExpiredQueue.get() == 0) {\n\t\t\t\t\tloadExpiredQueue(1);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (DataTruncation dte) {\n\t\t\tlog.log(Level.FINE, \"Data truncated for message from {0} to {1}\", new Object[]{from, to});\n\t\t} catch (SQLException e) {\n\t\t\tif (e.getErrorCode() == 1366 || e.getMessage() != null && e.getMessage().startsWith(\"Incorrect string value\")) {\n\t\t\t\tlog.log(Level.WARNING, \"Your MySQL configuration can't handle extended Unicode (for example emoji) correctly. Please refer to <Support for emoji and other icons> section of the server documentation\");\n\t\t\t} else {\n\t\t\t\tlog.log(Level.WARNING, \"Problem adding new entry to DB: \", e);\n\t\t\t}\n\t\t} finally {\n\t\t\tlock.unlock();\n\t\t}\n\t\treturn result;\n\t}\n\n\tprotected Queue<Element> parseLoadedMessages(OfflineMessagesProcessor proc, ResultSet rs) throws SQLException {\n\t\tStringBuilder sb = new StringBuilder(1000);\n\t\tQueue<Element> result = new LinkedList<Element>();\n\t\tif (proc == null) {\n\t\t\twhile (rs.next()) {\n\t\t\t\tsb.append(rs.getString(1));\n\t\t\t}\n\n\t\t\tif (sb.length() > 0) {\n\t\t\t\tDomBuilderHandler domHandler = new DomBuilderHandler();\n\n\t\t\t\tparser.parse(domHandler, sb.toString().toCharArray(), 0, sb.length());\n\t\t\t\tresult = domHandler.getParsedElements();\n\t\t\t}\n\t\t} else {\n\t\t\tresult = new LinkedList<Element>();\n\t\t\twhile (rs.next()) {\n\t\t\t\tfinal String msg = rs.getString(1);\n\t\t\t\tfinal long msgId = rs.getLong(2);\n\n\t\t\t\tif (msg != null) {\n\t\t\t\t\tDomBuilderHandler domHandler = new DomBuilderHandler();\n\n\t\t\t\t\tparser.parse(domHandler, msg.toCharArray(), 0, msg.length());\n\t\t\t\t\tfinal Queue<Element> parsedElements = domHandler.getParsedElements();\n\t\t\t\t\tElement msgEl = parsedElements.poll();\n\t\t\t\t\tif (msgEl != null && msgId > 0) {\n\n\t\t\t\t\t\tproc.stamp(msgEl, String.valueOf(msgId));\n\n\t\t\t\t\t\tresult.add(msgEl);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t@Override\n\tprotected void deleteMessage(Long msg_id) {\n\t\ttry {\n\t\t\tPreparedStatement delete_id_st = data_repo.getPreparedStatement(null, MSGS_DELETE_MESSAGE);\n\n\t\t\tsynchronized (delete_id_st) {\n\t\t\t\tdelete_id_st.setLong(1, msg_id);\n\t\t\t\tdelete_id_st.executeUpdate();\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tlog.log(Level.WARNING, \"Problem removing entry from DB: \", e);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void loadExpiredQueue(int min_elements) {\n\t\ttry {\n\t\t\tResultSet rs = null;\n\t\t\tPreparedStatement select_expired_st = data_repo.getPreparedStatement(null, MSGS_GET_EXPIRED_MESSAGES);\n\n\t\t\tsynchronized (select_expired_st) {\n\t\t\t\ttry {\n\t\t\t\t\tselect_expired_st.setInt(1, min_elements);\n\t\t\t\t\trs = select_expired_st.executeQuery();\n\n\t\t\t\t\tDomBuilderHandler domHandler = new DomBuilderHandler();\n\t\t\t\t\tint counter = 0;\n\n\t\t\t\t\twhile (rs.next() && ((expiredQueue.size() < MAX_QUEUE_SIZE) || (counter++ < min_elements))) {\n\t\t\t\t\t\tMsgDBItem item = parseExpiredMessage(domHandler, rs);\n\t\t\t\t\t\tif (item != null) {\n\t\t\t\t\t\t\texpiredQueue.offer(item);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tlog.log(Level.WARNING, \"Problem getting offline messages from db: \", e);\n\t\t}\n\n\t\tearliestOffline = Long.MAX_VALUE;\n\t}\n\n\t@Override\n\tprotected void loadExpiredQueue(Date expired) {\n\t\ttry {\n\t\t\tif (expiredQueue.size() > 100 * MAX_QUEUE_SIZE) {\n\t\t\t\texpiredQueue.clear();\n\t\t\t\tawaitingInExpiredQueue.set(0);\n\t\t\t}\n\n\t\t\tResultSet rs = null;\n\t\t\tPreparedStatement select_expired_before_st = data_repo.getPreparedStatement(null,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tMSGS_GET_EXPIRED_MESSAGES_BEFORE);\n\n\t\t\tsynchronized (select_expired_before_st) {\n\t\t\t\ttry {\n\t\t\t\t\tdata_repo.setTimestamp(select_expired_before_st, 1, new Timestamp(expired.getTime()));\n\t\t\t\t\trs = select_expired_before_st.executeQuery();\n\n\t\t\t\t\tDomBuilderHandler domHandler = new DomBuilderHandler();\n\t\t\t\t\tint counter = 0;\n\n\t\t\t\t\twhile (rs.next() && (counter++ < MAX_QUEUE_SIZE)) {\n\t\t\t\t\t\tMsgDBItem item = parseExpiredMessage(domHandler, rs);\n\t\t\t\t\t\tif (item != null) {\n\t\t\t\t\t\t\texpiredQueue.offer(item);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tlog.log(Level.WARNING, \"Problem getting offline messages from db: \", e);\n\t\t}\n\n\t\tearliestOffline = Long.MAX_VALUE;\n\t}\n\n\tprotected MsgDBItem parseExpiredMessage(DomBuilderHandler domHandler, ResultSet rs) throws SQLException {\n\t\tString msg_str = rs.getString(3);\n\n\t\tparser.parse(domHandler, msg_str.toCharArray(), 0, msg_str.length());\n\n\t\tQueue<Element> elems = domHandler.getParsedElements();\n\t\tElement msg = elems.poll();\n\n\t\tif (msg == null) {\n\t\t\tlog.log(Level.CONFIG, \"Something wrong, loaded offline message from DB but parsed no \" + \"XML elements: {0}\",\n\t\t\t\t\tmsg_str);\n\t\t\treturn null;\n\t\t} else {\n\t\t\tTimestamp ts = data_repo.getTimestamp(rs, 2);\n\t\t\treturn new MsgDBItem(rs.getLong(1), msg, ts);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/amp/db/MsgBroadcastRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp.db;\n\nimport tigase.db.DBInitException;\nimport tigase.db.DataSource;\nimport tigase.db.DataSourceAware;\nimport tigase.db.DataSourceHelper;\nimport tigase.db.beans.MDRepositoryBeanWithStatistics;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.amp.AmpComponent;\nimport tigase.server.amp.JidResourceMap;\nimport tigase.xml.Element;\nimport tigase.xml.SimpleParser;\nimport tigase.xml.SingletonFactory;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Created by andrzej on 15.03.2016.\n */\npublic abstract class MsgBroadcastRepository<T, S extends DataSource>\n\t\timplements DataSourceAware<S> {\n\n\tprotected Map<String, BroadcastMsg> broadcastMessages = new ConcurrentHashMap<String, BroadcastMsg>();\n\tprotected long broadcastMessagesLastCleanup = 0;\n\tprotected SimpleParser parser = SingletonFactory.getParserInstance();\n\n\tpublic abstract void loadMessagesToBroadcast();\n\n\tpublic BroadcastMsg getBroadcastMsg(String id) {\n\t\treturn broadcastMessages.get(id);\n\t}\n\n\tpublic String dumpBroadcastMessageKeys() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (String key : broadcastMessages.keySet()) {\n\t\t\tif (sb.length() == 0) {\n\t\t\t\tsb.append(\"[\");\n\t\t\t} else {\n\t\t\t\tsb.append(\",\");\n\t\t\t}\n\t\t\tsb.append(key);\n\t\t}\n\t\treturn sb.append(\"]\").toString();\n\t}\n\n\tpublic Collection<BroadcastMsg> getBroadcastMessages() {\n\t\tlong now = System.currentTimeMillis();\n\t\tif (now - broadcastMessagesLastCleanup > 60 * 1000) {\n\t\t\tbroadcastMessagesLastCleanup = now;\n\t\t\tList<String> toRemove = new ArrayList<String>();\n\t\t\tfor (Map.Entry<String, BroadcastMsg> e : broadcastMessages.entrySet()) {\n\t\t\t\tif (e.getValue().getDelay(TimeUnit.MILLISECONDS) < 0) {\n\t\t\t\t\ttoRemove.add(e.getKey());\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (String key : toRemove) {\n\t\t\t\tbroadcastMessages.remove(key);\n\t\t\t}\n\t\t}\n\t\treturn Collections.unmodifiableCollection(broadcastMessages.values());\n\t}\n\n\tpublic boolean updateBroadcastMessage(String id, Element msg, Date expire, BareJID recipient) {\n\t\tboolean isNew = false;\n\t\tsynchronized (broadcastMessages) {\n\t\t\tBroadcastMsg bmsg = broadcastMessages.get(id);\n\t\t\tif (bmsg == null) {\n\t\t\t\tbmsg = new BroadcastMsg(null, msg, expire);\n\t\t\t\tbroadcastMessages.put(id, bmsg);\n\t\t\t\tisNew = true;\n\t\t\t\tinsertBroadcastMessage(id, msg, expire, recipient);\n\t\t\t}\n\t\t\tif (bmsg.addRecipient(recipient)) {\n\t\t\t\tensureBroadcastMessageRecipient(id, recipient);\n\t\t\t}\n\t\t\treturn isNew;\n\t\t}\n\t}\n\n\tprotected abstract void ensureBroadcastMessageRecipient(String id, BareJID recipient);\n\n\tprotected abstract void insertBroadcastMessage(String id, Element msg, Date expire, BareJID recipient);\n\n\t@Bean(name = \"msgBroadcastRepository\", parent = AmpComponent.class, active = true)\n\tpublic static class MsgBroadcastRepositoryBean\n\t\t\textends MDRepositoryBeanWithStatistics<MsgBroadcastRepository>\n\t\t\timplements MsgBroadcastRepositoryIfc {\n\n\t\tpublic MsgBroadcastRepositoryBean() {\n\t\t\tsuper(MsgBroadcastRepositoryIfc.class);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setDataSource(DataSource dataSource) {\n\t\t\t// Nothing to do\n\t\t}\n\n\t\t@Override\n\t\tpublic void loadMessagesToBroadcast() {\n\t\t\tgetRepository(\"default\").loadMessagesToBroadcast();\n\t\t}\n\n\t\t@Override\n\t\tpublic MsgBroadcastRepository.BroadcastMsg getBroadcastMsg(String id) {\n\t\t\treturn getRepository(\"default\").getBroadcastMsg(id);\n\t\t}\n\n\t\t@Override\n\t\tpublic String dumpBroadcastMessageKeys() {\n\t\t\treturn getRepository(\"default\").dumpBroadcastMessageKeys();\n\t\t}\n\n\t\t@Override\n\t\tpublic Collection<MsgBroadcastRepository.BroadcastMsg> getBroadcastMessages() {\n\t\t\treturn getRepository(\"default\").getBroadcastMessages();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean updateBroadcastMessage(String id, Element msg, Date expire, BareJID recipient) {\n\t\t\treturn getRepository(\"default\").updateBroadcastMessage(id, msg, expire, recipient);\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getDefaultBeanClass() {\n\t\t\treturn MsgBroadcastRepositoryConfigBean.class;\n\t\t}\n\n\t\t@Override\n\t\tprotected Class<? extends MsgBroadcastRepository> findClassForDataSource(DataSource dataSource)\n\t\t\t\tthrows DBInitException {\n\t\t\treturn DataSourceHelper.getDefaultClass(MsgBroadcastRepository.class, dataSource.getResourceUri());\n\t\t}\n\n\t\tpublic static class MsgBroadcastRepositoryConfigBean\n\t\t\t\textends MDRepositoryConfigBean<MsgBroadcastRepository> {\n\n\t\t}\n\t}\n\n\tpublic class BroadcastMsg<T>\n\t\t\textends MsgRepository.MsgDBItem<T> {\n\n\t\tprivate JidResourceMap<Boolean> recipients = new JidResourceMap<Boolean>();\n\n\t\tpublic BroadcastMsg(T db_id, Element msg, Date expired) {\n\t\t\tsuper(db_id, msg, expired);\n\t\t}\n\n\t\tpublic boolean needToSend(JID jid) {\n\t\t\treturn recipients.containsKey(jid.getBareJID()) &&\n\t\t\t\t\t(jid.getResource() == null || !recipients.containsKey(jid));\n\t\t}\n\n\t\tpublic void markAsSent(JID jid) {\n\t\t\trecipients.put(jid, Boolean.TRUE);\n\t\t}\n\n\t\tprotected boolean addRecipient(BareJID jid) {\n\t\t\tif (recipients.containsKey(jid)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\trecipients.put(JID.jidInstance(jid), Boolean.TRUE);\n\t\t\treturn true;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/amp/db/MsgBroadcastRepositoryIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp.db;\n\nimport tigase.db.DataSource;\nimport tigase.db.DataSourceAware;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.Collection;\nimport java.util.Date;\n\n/**\n * Created by andrzej on 15.03.2016.\n */\npublic interface MsgBroadcastRepositoryIfc<T extends DataSource>\n\t\textends DataSourceAware<T> {\n\n\tvoid loadMessagesToBroadcast();\n\n\tMsgBroadcastRepository.BroadcastMsg getBroadcastMsg(String id);\n\n\tString dumpBroadcastMessageKeys();\n\n\tCollection<MsgBroadcastRepository.BroadcastMsg> getBroadcastMessages();\n\n\tboolean updateBroadcastMessage(String id, Element msg, Date expire, BareJID recipient);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/amp/db/MsgRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp.db;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.*;\nimport tigase.db.beans.MDRepositoryBeanWithStatistics;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\nimport tigase.osgi.ModulesManagerImpl;\nimport tigase.server.BasicComponent;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.Element;\nimport tigase.xml.SimpleParser;\nimport tigase.xml.SingletonFactory;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.concurrent.ConcurrentSkipListMap;\nimport java.util.concurrent.DelayQueue;\nimport java.util.concurrent.Delayed;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.locks.Condition;\nimport java.util.concurrent.locks.ReentrantLock;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * @author andrzej\n */\npublic abstract class MsgRepository<T, S extends DataSource>\n\t\timplements MsgRepositoryIfc<S> {\n\n\tpublic static final String OFFLINE_MSGS_KEY = \"offline-msgs\";\n\tpublic static final String MSGS_STORE_LIMIT_KEY = \"store-limit\";\n\tprotected static final int MAX_QUEUE_SIZE = 1000;\n\tprivate static final long MSGS_STORE_LIMIT_VAL = 100;\n\tprivate static final String MSGS_USER_STORE_LIMIT_ENABLE_KEY = \"user-store-limit-enable\";\n\tprivate static final String NULL_STR = \"NULL\";\n\tprivate static final Map<String, MsgRepositoryIfc> repos = new ConcurrentSkipListMap<String, MsgRepositoryIfc>();\n\tpublic enum MSG_TYPES {\n\t\tnone(0),\n\t\tmessage(1),\n\t\tpresence(2);\n\n\t\tprivate final int numVal;\n\n\t\tpublic static MSG_TYPES getFromInt(int type) {\n\t\t\tswitch (type) {\n\t\t\t\tcase 1:\n\t\t\t\t\treturn message;\n\t\t\t\tcase 2:\n\t\t\t\t\treturn presence;\n\t\t\t\tcase 0:\n\t\t\t\tdefault:\n\t\t\t\t\treturn none;\n\t\t\t}\n\t\t}\n\n\t\tMSG_TYPES(int numVal) {\n\t\t\tthis.numVal = numVal;\n\t\t}\n\n\t\tpublic int getNumVal() {\n\t\t\treturn numVal;\n\t\t}\n\n\t}\n\tprotected AtomicInteger awaitingInExpiredQueue = new AtomicInteger(0);\n\tprotected long earliestOffline = Long.MAX_VALUE;\n\tprotected SimpleParser parser = SingletonFactory.getParserInstance();\n\tprotected DelayQueue<MsgDBItem<T>> expiredQueue = new DelayQueue<MsgDBItem<T>>() {\n\t\t@Override\n\t\tpublic boolean offer(MsgDBItem<T> tMsgDBItem) {\n\t\t\tboolean result = false;\n\t\t\tif (msgRepositoryIfc != null) {\n\t\t\t\tresult = msgRepositoryIfc.offerExpired(MsgRepository.this, tMsgDBItem.db_id, tMsgDBItem.msg, tMsgDBItem.expired);\n\t\t\t} else {\n\t\t\t\tresult = super.offer(tMsgDBItem);\n\t\t\t}\n\t\t\tif (result) {\n\t\t\t\tawaitingInExpiredQueue.incrementAndGet();\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\t};\n\t@ConfigField(desc = \"Limit of offline messages\", alias = \"store-limit\")\n\tprivate long msgs_store_limit = MSGS_STORE_LIMIT_VAL;\n\t@ConfigField(desc = \"Support limits of offline messages set by users\", alias = \"user-store-limit-enable\")\n\tprivate boolean msgs_user_store_limit = false;\n\t@Inject\n\tprivate UserRepository userRepository;\n\t@Inject(nullAllowed = true)\n\tprivate MsgRepositoryPoolBean msgRepositoryIfc;\n\n\tpublic static MsgRepositoryIfc getInstance(String cls, String id_string) throws TigaseDBException {\n\t\ttry {\n\t\t\tif (cls == null) {\n\t\t\t\tcls = RepositoryFactory.getRepoClassName(MsgRepositoryIfc.class, id_string);\n\t\t\t}\n\t\t\tString key = cls + \"#\" + id_string;\n\t\t\tMsgRepositoryIfc result = repos.get(key);\n\n\t\t\tif (result == null) {\n\t\t\t\tresult = (MsgRepositoryIfc) ModulesManagerImpl.getInstance().forName(cls).newInstance();\n\t\t\t\trepos.put(key, result);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} catch (Exception ex) {\n\t\t\tthrow new TigaseDBException(\"Could not create instance of \" + cls + \" for uri \" + id_string, ex);\n\t\t}\n\t}\n\n\tpublic abstract Queue<Element> loadMessagesToJID(List<String> db_ids, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t\t\t boolean delete, OfflineMessagesProcessor proc)\n\t\t\tthrows UserNotFoundException;\n\n\tpublic abstract int deleteMessagesToJID(List<String> db_ids, XMPPResourceConnection session)\n\t\t\tthrows UserNotFoundException;\n\n\t@Override\n\t@Deprecated\n\tpublic void initRepository(String conn_str, Map<String, String> map) throws DBInitException {\n\n\t\tif (map != null) {\n\t\t\tString msgs_store_limit_str = map.get(MSGS_STORE_LIMIT_KEY);\n\n\t\t\tif (msgs_store_limit_str != null) {\n\t\t\t\tmsgs_store_limit = Long.parseLong(msgs_store_limit_str);\n\t\t\t}\n\n\t\t\tString msgs_user_store_limit_enable = map.get(MSGS_USER_STORE_LIMIT_ENABLE_KEY);\n\t\t\tif (msgs_user_store_limit_enable != null) {\n\t\t\t\tmsgs_user_store_limit = Boolean.parseBoolean(msgs_user_store_limit_enable);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\t@Deprecated\n\tpublic Element getMessageExpired(long time, boolean delete) {\n\t\tif (expiredQueue.size() == 0) {\n\n\t\t\t// If the queue is empty load it with some elements\n\t\t\tloadExpiredQueue(MAX_QUEUE_SIZE);\n\t\t} else {\n\n\t\t\t// If the queue is not empty, check whether recently saved off-line\n\t\t\t// message\n\t\t\t// is due to expire sooner then the head of the queue.\n\t\t\tMsgDBItem item = expiredQueue.peek();\n\n\t\t\tif ((item != null) && (earliestOffline < item.expired.getTime())) {\n\n\t\t\t\t// There is in fact off-line message due to expire sooner then the head\n\t\t\t\t// of the\n\t\t\t\t// queue. Load all off-line message due to expire sooner then the first\n\t\t\t\t// element\n\t\t\t\t// in the queue.\n\t\t\t\tloadExpiredQueue(item.expired);\n\t\t\t}\n\t\t}\n\n\t\tMsgDBItem<T> item = expiredQueue.poll();\n\n\t\tif (item == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tawaitingInExpiredQueue.decrementAndGet();\n\t\tif (delete) {\n\t\t\tdeleteMessage(item.db_id);\n\t\t}\n\n\t\treturn item.msg;\n\t}\n\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\")\n\t@Deprecated\n\t@Override\n\tpublic void setCondition(ReentrantLock lock, Condition condition) {\n\t}\n\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"Will be replaced by method in MsgRepositoryIfc returning loaded items\")\n\t@Deprecated\n\tprotected abstract void loadExpiredQueue(int max);\n\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"Will be replaced by method in MsgRepositoryIfc returning loaded items\")\n\t@Deprecated\n\tprotected abstract void loadExpiredQueue(Date expired);\n\n\tprotected abstract void deleteMessage(T db_id);\n\n\tprotected long getMsgsStoreLimit(BareJID userJid, NonAuthUserRepository userRepo) throws UserNotFoundException {\n\t\tif (msgs_user_store_limit) {\n\t\t\tString limitStr = userRepo.getPublicData(userJid, OFFLINE_MSGS_KEY, MSGS_STORE_LIMIT_KEY, NULL_STR);\n\t\t\tif (limitStr == null) {\n\t\t\t\tthrow new UserNotFoundException(\"User \" + userJid + \" not found in user repository\");\n\t\t\t}\n\t\t\tif (NULL_STR != limitStr) {\n\t\t\t\tlong limit = Long.parseLong(limitStr);\n\t\t\t\t// in case of 0 we need to disable offline storage - not to save all as in case of store-limit\n\t\t\t\tif (limit == 0) {\n\t\t\t\t\tlimit = -1;\n\t\t\t\t}\n\t\t\t\treturn limit;\n\t\t\t}\n\t\t} else if (!userRepository.userExists(userJid)) {\n\t\t\tthrow new UserNotFoundException(\"User \" + userJid + \" not found in user repository\");\n\t\t}\n\t\treturn msgs_store_limit;\n\t}\n\n\tpublic interface OfflineMessagesProcessor {\n\n\t\tpublic void stamp(Element msg, String msgID);\n\t}\n\n\tpublic static class MsgDBItem<T>\n\t\t\timplements Delayed {\n\n\t\tpublic final T db_id;\n\t\tpublic final Date expired;\n\t\tpublic final Element msg;\n\n\t\tpublic MsgDBItem(T db_id, Element msg, Date expired) {\n\t\t\tthis.db_id = db_id;\n\t\t\tthis.msg = msg;\n\t\t\tthis.expired = expired;\n\t\t}\n\n\t\t@Override\n\t\tpublic int compareTo(Delayed o) {\n\t\t\treturn (int) (getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS));\n\t\t}\n\n\t\t@Override\n\t\tpublic long getDelay(TimeUnit unit) {\n\t\t\treturn unit.convert(expired.getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS);\n\t\t}\n\t}\n\n\t/**\n\t * Bean used to provide MsgRepository implementations\n\t */\n\t@Bean(name = \"msgRepository\", parent = Kernel.class, active = true, exportable = true)\n\t@ConfigType({ConfigTypeEnum.DefaultMode, ConfigTypeEnum.SessionManagerMode, ConfigTypeEnum.ConnectionManagersMode,\n\t\t\t\t ConfigTypeEnum.ComponentMode})\n\tpublic static class MsgRepositoryMDBean\n\t\t\textends MDRepositoryBeanWithStatistics<MsgRepositoryIfc>\n\t\t\timplements MsgRepositoryIfc, MsgRepositoryPoolBean {\n\n\t\tprivate static final Logger log = Logger.getLogger(MsgRepositoryMDBean.class.getCanonicalName());\n\n\t\tprivate DelayQueue<RepoAwareMsgDBItem> expiredQueue = new DelayQueue<RepoAwareMsgDBItem>();\n\t\tprivate long earliestOffline = Long.MAX_VALUE;\n\n\t\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\")\n\t\t@Deprecated\n\t\tprivate final transient ReentrantLock lock = new ReentrantLock();\n\t\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\")\n\t\t@Deprecated\n\t\tprivate final Condition expiredMessagesCondition = lock.newCondition();\n\n\t\tpublic MsgRepositoryMDBean() {\n\t\t\tsuper(MsgRepositoryIfc.class, OfflineMsgRepositoryIfc.class);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean belongsTo(Class<? extends BasicComponent> component) {\n\t\t\treturn SessionManager.class.isAssignableFrom(component);\n\t\t}\n\n\t\t@Override\n\t\tpublic Element getMessageExpired(long time, boolean delete) {\n\t\t\t// what if some queue has entries one repo (far in the future)\n\t\t\t// but the other repo loaded only part of his queue and some remained in the database?\n\n\t\t\tif (expiredQueue.size() == 0) {\n\n\t\t\t\t// If the queue is empty load it with some elements\n\t\t\t\tloadExpiredQueue(MAX_QUEUE_SIZE);\n\t\t\t} else {\n\t\t\t\t// Check if any repository in the poll has empty expiredQueue and if so, try to load for them..\n\t\t\t\tfor (MsgRepositoryIfc repo : getRepositories().values()) {\n\t\t\t\t\tif (repo instanceof MsgRepository && ((MsgRepository) repo).awaitingInExpiredQueue.get() == 0) {\n\t\t\t\t\t\t((MsgRepository) repo).loadExpiredQueue(MAX_QUEUE_SIZE);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// If the queue is not empty, check whether recently saved off-line\n\t\t\t\t// message\n\t\t\t\t// is due to expire sooner then the head of the queue.\n\t\t\t\tRepoAwareMsgDBItem item = expiredQueue.peek();\n\n\t\t\t\tif ((item != null) && (earliestOffline < item.expired.getTime())) {\n\n\t\t\t\t\t// There is in fact off-line message due to expire sooner then the head\n\t\t\t\t\t// of the\n\t\t\t\t\t// queue. Load all off-line message due to expire sooner then the first\n\t\t\t\t\t// element\n\t\t\t\t\t// in the queue.\n\t\t\t\t\tloadExpiredQueue(item.expired);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tRepoAwareMsgDBItem item = null;\n\t\t\twhile (item == null) {\n\t\t\t\ttry {\n\t\t\t\t\titem = expiredQueue.take();\n\t\t\t\t} catch (InterruptedException ex) {\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (item.getRepo() instanceof MsgRepository) {\n\t\t\t\t((MsgRepository) item.getRepo()).awaitingInExpiredQueue.decrementAndGet();\n\t\t\t\tif (delete) {\n\t\t\t\t\t((MsgRepository) item.getRepo()).deleteMessage(item.db_id);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn item.msg;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean offerExpired(MsgRepositoryIfc repo, Object id, Element element, Date expired) {\n\t\t\treturn expiredQueue.offer(new RepoAwareMsgDBItem(repo, id, element, expired));\n\t\t}\n\n\t\tprotected void loadExpiredQueue(int min_elements) {\n\t\t\tint max = Math.max(min_elements / getRepositories().size(), 1);\n\t\t\tfor (MsgRepositoryIfc repo : getRepositories().values()) {\n\t\t\t\tif (repo instanceof MsgRepository) {\n\t\t\t\t\t((MsgRepository) repo).loadExpiredQueue(max);\n\t\t\t\t}\n\t\t\t}\n\t\t\tearliestOffline = Long.MAX_VALUE;\n\t\t}\n\n\t\tprotected void loadExpiredQueue(Date expired) {\n\t\t\tif (expiredQueue.size() > 100 * MAX_QUEUE_SIZE) {\n\t\t\t\texpiredQueue.clear();\n\t\t\t\tfor (MsgRepositoryIfc repo : getRepositories().values()) {\n\t\t\t\t\tif (repo instanceof MsgRepository) {\n\t\t\t\t\t\t((MsgRepository) repo).awaitingInExpiredQueue.set(0);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tfor (MsgRepositoryIfc repo : getRepositories().values()) {\n\t\t\t\tif (repo instanceof MsgRepository) {\n\t\t\t\t\t((MsgRepository) repo).loadExpiredQueue(expired);\n\t\t\t\t}\n\t\t\t}\n\t\t\tearliestOffline = Long.MAX_VALUE;\n\t\t}\n\n\t\t@Override\n\t\tpublic Queue<Element> loadMessagesToJID(XMPPResourceConnection session, boolean delete)\n\t\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\t\tQueue<Element> result = null;\n\t\t\ttry {\n\t\t\t\tMsgRepositoryIfc repo = getRepository(session.getBareJID().getDomain());\n\t\t\t\tresult = repo.loadMessagesToJID(session, delete);\n\t\t\t} catch (NotAuthorizedException ex) {\n\t\t\t\tlog.log(Level.WARNING, \"Session not authorized yet!\", ex);\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean storeMessage(JID from, JID to, Date expired, Element msg, NonAuthUserRepository userRepo)\n\t\t\t\tthrows UserNotFoundException, TigaseDBException {\n\t\t\tMsgRepositoryIfc repo = getRepository(to.getDomain());\n\t\t\tboolean result = repo.storeMessage(from, to, expired, msg, userRepo);\n\t\t\tif (result && expired != null) {\n\t\t\t\tif (expired.getTime() < earliestOffline) {\n\t\t\t\t\tearliestOffline = expired.getTime();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\t\t@Override\n\t\t@Deprecated\n\t\tpublic void initRepository(String resource_uri, Map<String, String> params) throws DBInitException {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<Enum, Long> getMessagesCount(JID to) throws UserNotFoundException, TigaseDBException {\n\t\t\treturn getRepository(to.getDomain()).getMessagesCount(to);\n\t\t}\n\n\t\t@Override\n\t\tpublic List<Element> getMessagesList(JID to) throws UserNotFoundException, TigaseDBException {\n\t\t\treturn getRepository(to.getDomain()).getMessagesList(to);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setCondition(ReentrantLock lock, Condition condition) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic int deleteMessagesToJID(List db_ids, XMPPResourceConnection session) throws UserNotFoundException {\n\t\t\treturn getRepository(session.getDomainAsJID().getDomain()).deleteMessagesToJID(db_ids, session);\n\t\t}\n\n\t\t@Override\n\t\tpublic Queue<Element> loadMessagesToJID(List db_ids, XMPPResourceConnection session, boolean delete,\n\t\t\t\t\t\t\t\t\t\t\t\tOfflineMessagesProcessor proc) throws UserNotFoundException, TigaseDBException {\n\t\t\treturn getRepository(session.getDomainAsJID().getDomain()).loadMessagesToJID(db_ids, session, delete, proc);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setDataSource(DataSource dataSource) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getDefaultBeanClass() {\n\t\t\treturn MsgRepositoryConfigBean.class;\n\t\t}\n\n\t\t@Override\n\t\tprotected Class<? extends MsgRepositoryIfc> findClassForDataSource(DataSource dataSource)\n\t\t\t\tthrows DBInitException {\n\t\t\treturn DataSourceHelper.getDefaultClass(MsgRepository.class, dataSource.getResourceUri());\n\t\t}\n\n\t\t@Override\n\t\tprotected void initializeRepository(String domain, MsgRepositoryIfc repo) {\n\t\t\tsuper.initializeRepository(domain, repo);\n\t\t\trepo.setCondition(lock, expiredMessagesCondition);\n\t\t}\n\n\t\tprotected <T> T getValueForDomain(Map<String, T> map, String domain) {\n\t\t\tT value = map.get(domain);\n\t\t\tif (value == null) {\n\t\t\t\tvalue = map.get(\"default\");\n\t\t\t}\n\t\t\treturn value;\n\t\t}\n\n\t\tpublic static class MsgRepositoryConfigBean\n\t\t\t\textends MDRepositoryConfigBean<MsgRepositoryIfc> {\n\n\t\t}\n\n\t\tpublic static class RepoAwareMsgDBItem extends MsgDBItem {\n\n\t\t\tprivate final MsgRepositoryIfc repo;\n\n\t\t\tpublic RepoAwareMsgDBItem(MsgRepositoryIfc repo, Object db_id, Element msg, Date expired) {\n\t\t\t\tsuper(db_id, msg, expired);\n\t\t\t\tthis.repo = repo;\n\t\t\t}\n\n\t\t\tpublic MsgRepositoryIfc getRepo() {\n\t\t\t\treturn repo;\n\t\t\t}\n\t\t}\n\t}\n\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"It is expected to be moved to MsgRepositoryIfc\")\n\t@Deprecated\n\tpublic interface MsgRepositoryPoolBean<T> {\n\t\tboolean offerExpired(MsgRepositoryIfc repo, T id, Element element, Date expired);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/bosh/BoshConnectionManager.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.bosh;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.beans.selector.ClusterModeRequired;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Command;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.ReceiverTimeoutHandler;\nimport tigase.server.xmppclient.ClientConnectionManager;\nimport tigase.server.xmppclient.SeeOtherHostIfc.Phase;\nimport tigase.stats.StatisticsList;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport javax.script.Bindings;\nimport java.io.BufferedReader;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentSkipListMap;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.*;\n\nimport static tigase.server.bosh.Constants.*;\n\n/**\n * Describe class BoshConnectionManager here.\n * <br>\n * Created: Sat Jun 2 12:24:29 2007\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"bosh\", parent = Kernel.class, active = true)\n@ConfigType({ConfigTypeEnum.DefaultMode, ConfigTypeEnum.ConnectionManagersMode})\n@ClusterModeRequired(active = false)\npublic class BoshConnectionManager\n\t\textends ClientConnectionManager\n\t\timplements BoshSessionTaskHandler, BoshIOService.ConfigProvider {\n\n\tpublic static final String BOSH_CLOSE_CONNECTION_PROP_KEY = \"bosh-close-connection\";\n\tpublic static final String BOSH_EXTRA_HEADERS_FILE_PROP_KEY = \"bosh-extra-headers-file\";\n\tpublic static final String BOSH_EXTRA_HEADERS_FILE_PROP_VAL = \"etc/bosh-extra-headers.txt\";\n\tpublic static final String CLIENT_ACCESS_POLICY_FILE_PROP_KEY = \"client-access-policy-file\";\n\tpublic static final String CLIENT_ACCESS_POLICY_FILE_PROP_VAL = \"etc/client-access-policy.xml\";\n\tprivate static final Logger log = Logger.getLogger(BoshConnectionManager.class.getName());\n\tprivate static final int DEF_PORT_NO = 5280;\n\n\tprivate static java.util.logging.Handler sidFilehandler;\n\tprotected final Map<UUID, BoshSession> sessions = new ConcurrentSkipListMap<UUID, BoshSession>();\n\tprivate int[] PORTS = {DEF_PORT_NO};\n\t@ConfigField(desc = \"Batch queue timeout\", alias = BATCH_QUEUE_TIMEOUT_KEY)\n\tprivate long batch_queue_timeout = BATCH_QUEUE_TIMEOUT_VAL;\n\t@ConfigField(desc = \"Delay before closing BOSH session\", alias = BOSH_SESSION_CLOSE_DELAY_PROP_KEY)\n\tprivate long bosh_session_close_delay = BOSH_SESSION_CLOSE_DELAY_DEF_VAL;\n\tprivate String clientAccessPolicy = null;\n\t@ConfigField(desc = \"Client access policy file\", alias = CLIENT_ACCESS_POLICY_FILE_PROP_KEY)\n\tprivate String clientAccessPolicyFile = CLIENT_ACCESS_POLICY_FILE_PROP_VAL;\n\t@ConfigField(desc = \"Close BOSH connections\", alias = BOSH_CLOSE_CONNECTION_PROP_KEY)\n\tprivate boolean closeConnections = false;\n\t@ConfigField(desc = \"Maximal number of concurrent quests\", alias = CONCURRENT_REQUESTS_PROP_KEY)\n\tprivate int concurrent_requests = CONCURRENT_REQUESTS_PROP_VAL;\n\tprivate String extraHeaders = null;\n\t@ConfigField(desc = \"Extra headers file\", alias = BOSH_EXTRA_HEADERS_FILE_PROP_KEY)\n\tprivate String extraHeadersFile = BOSH_EXTRA_HEADERS_FILE_PROP_VAL;\n\t@ConfigField(desc = \"Maximal number of hold requests\", alias = HOLD_REQUESTS_PROP_KEY)\n\tprivate int hold_requests = HOLD_REQUESTS_PROP_VAL;\n\t@ConfigField(desc = \"Limit of number of packets waiting to send for a single session\", alias = MAX_SESSION_WAITING_PACKETS_KEY)\n\tprivate int maxSessionWaitingPackets = MAX_SESSION_WAITING_PACKETS_VAL;\n\t@ConfigField(desc = \"Maximal size of batch\", alias = MAX_BATCH_SIZE_KEY)\n\tprivate int max_batch_size = MAX_BATCH_SIZE_VAL;\n\t@ConfigField(desc = \"Maximal allowed time of inactivity\", alias = MAX_INACTIVITY_PROP_KEY)\n\tprivate long max_inactivity = MAX_INACTIVITY_PROP_VAL;\n\t@ConfigField(desc = \"Maximal allowed pause time\", alias = MAX_PAUSE_PROP_KEY)\n\tprivate long max_pause = MAX_PAUSE_PROP_VAL;\n\t@ConfigField(desc = \"Maximal allowed wait time\", alias = MAX_WAIT_DEF_PROP_KEY)\n\tprivate long max_wait = MAX_WAIT_DEF_PROP_VAL;\n\t@ConfigField(desc = \"Minimal allowed polling time\", alias = MIN_POLLING_PROP_KEY)\n\tprivate long min_polling = MIN_POLLING_PROP_VAL;\n\t@ConfigField(desc = \"Should send node hostname a BOSH element attribute\", alias = SEND_NODE_HOSTNAME_KEY)\n\tprivate boolean sendNodeHostname = SEND_NODE_HOSTNAME_VAL;\n\t@ConfigField(desc = \"SID logger level\", alias = SID_LOGGER_KEY)\n\tprivate String sidLoggerLevel = SID_LOGGER_VAL;\n\tprivate ReceiverTimeoutHandler startedHandler = newStartedHandler();\n\n\t;\n\tprivate ReceiverTimeoutHandler stoppedHandler = newStoppedHandler();\n\n\t// This should be actually a multi-thread save variable.\n\t// Changing it to\n\n\tprotected static void setupSidlogger(Level lvl) {\n\t\tif (!Level.OFF.equals(lvl)) {\n\t\t\tFilter bslf = new BoshSidLoggerFilter();\n\n\t\t\tLogger BoshConnectionManagerLogger = Logger.getLogger(BoshConnectionManager.class.getName());\n\t\t\tLogger BoshSessionLogger = Logger.getLogger(BoshSession.class.getName());\n\n\t\t\tif (BoshConnectionManagerLogger.getLevel() == null || BoshSessionLogger.getLevel() == null ||\n\t\t\t\t\tBoshConnectionManagerLogger.getLevel().intValue() < lvl.intValue()) {\n\t\t\t\tBoshConnectionManagerLogger.setLevel(lvl);\n\t\t\t\tBoshConnectionManagerLogger.setFilter(bslf);\n\t\t\t\tBoshSessionLogger.setLevel(lvl);\n\t\t\t\tBoshSessionLogger.setFilter(bslf);\n\n\t\t\t\tBoshConnectionManagerLogger.getParent().setFilter(bslf);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tif (null == sidFilehandler) {\n\t\t\t\t\tsidFilehandler = new FileHandler(\"logs/bosh_sid.log\", 10000000, 5, false);\n\t\t\t\t\tsidFilehandler.setLevel(lvl);\n\t\t\t\t\tsidFilehandler.setFilter(bslf);\n\t\t\t\t\tBoshConnectionManagerLogger.getParent().addHandler(sidFilehandler);\n\t\t\t\t}\n\t\t\t} catch (IOException ex) {\n\t\t\t\tlog.log(Level.CONFIG, \"Error creating BOSH SID logger\" + ex);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean addOutStreamClosed(Packet packet, BoshSession bs, boolean withTimeout) {\n\t\tpacket.setPacketFrom(getFromAddress(bs.getSid().toString()));\n\t\tpacket.setPacketTo(bs.getDataReceiver());\n\t\tpacket.initVars(packet.getPacketFrom(), packet.getPacketTo());\n\t\tbs.close();\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"{0} : {1} ({2})\",\n\t\t\t\t\tnew Object[]{BOSH_OPERATION_TYPE.REMOVE, bs.getSid(), \"Closing bosh session\"});\n\t\t}\n\n\t\tsessions.remove(bs.getSid());\n\n\t\tif (withTimeout) {\n\t\t\treturn addOutPacketWithTimeout(packet, stoppedHandler, 15l, TimeUnit.SECONDS);\n\t\t} else {\n\t\t\treturn addOutPacket(packet);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean addOutStreamOpen(Packet packet, BoshSession bs) {\n\t\tpacket.initVars(getFromAddress(bs.getSid().toString()), bs.getDataReceiver());\n\n\t\treturn addOutPacketWithTimeout(packet, startedHandler, 15l, TimeUnit.SECONDS);\n\t}\n\n\t@Override\n\tpublic void cancelSendQueueTask(BoshSendQueueTask tt) {\n\t\ttt.cancel();\n\t}\n\n\t@Override\n\tpublic void cancelTask(BoshTask tt) {\n\t\ttt.cancel();\n\t}\n\n\t@Override\n\tprotected boolean enableServiceConnectedTimeout(XMPPIOService<Object> service) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void processPacket(final Packet packet) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Processing packet: {0}\", packet.toString());\n\t\t}\n\t\tsuper.processPacket(packet);\n\t}\n\n\t@Override\n\tpublic Queue<Packet> processSocketData(XMPPIOService<Object> srv) {\n\t\tBoshIOService serv = (BoshIOService) srv;\n\t\tPacket p = null;\n\n\t\twhile ((p = serv.getReceivedPackets().poll()) != null) {\n\t\t\tQueue<Packet> out_results = new ArrayDeque<Packet>(2);\n\t\t\tBoshSession bs = null;\n\t\t\tString sid_str = null;\n\n\t\t\tsynchronized (sessions) {\n\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\tlog.log(Level.FINER, \"Processing packet: {0}, type: {1}\",\n\t\t\t\t\t\t\tnew Object[]{p.getElemName(), p.getType()});\n\t\t\t\t}\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Processing socket data: {0}\", p);\n\t\t\t\t}\n\t\t\t\tsid_str = p.getAttributeStaticStr(SID_ATTR);\n\n\t\t\t\tUUID sid = null;\n\n\t\t\t\tif (sid_str == null) {\n\t\t\t\t\tString hostname = p.getAttributeStaticStr(Packet.TO_ATT);\n\n\t\t\t\t\tif ((hostname != null) && isLocalDomain(hostname)) {\n\t\t\t\t\t\tif (!isAllowed(srv, hostname)) {\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINE, \"Policy violation. Closing connection: {0}\", p);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tserv.sendErrorAndStop(Authorization.NOT_ALLOWED, StreamError.PolicyViolation, p,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  \"Policy violation.\");\n\t\t\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\t\t\tlog.log(Level.WARNING, \"Problem sending invalid hostname error for sid =  \" + sid, e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tbs = new BoshSession(getDefVHostItem().getDomain(),\n\t\t\t\t\t\t\t\t\t\t\t\t JID.jidInstanceNS(routings.computeRouting(hostname)), this,\n\t\t\t\t\t\t\t\t\t\t\t\t sendNodeHostname ? getDefHostName().getDomain() : null,\n\t\t\t\t\t\t\t\t\t\t\t\t maxSessionWaitingPackets);\n\t\t\t\t\t\t\tsid = bs.getSid();\n\t\t\t\t\t\t\tsessions.put(sid, bs);\n\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINE, \"{0} : {1} ({2})\",\n\t\t\t\t\t\t\t\t\t\tnew Object[]{BOSH_OPERATION_TYPE.CREATE, sid, \"Socket bosh session\"});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tserv.sendErrorAndStop(Authorization.NOT_ALLOWED, hostname == null\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t ? StreamError.ImproperAddressing\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t : StreamError.HostUnknown, p,\n\t\t\t\t\t\t\t\t\t\t\t\t  \"Invalid hostname.\");\n\t\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\t\tlog.log(Level.WARNING, \"Problem sending invalid hostname error for sid =  \" + sid, e);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tsid = UUID.fromString(sid_str);\n\t\t\t\t\t\tbs = sessions.get(sid);\n\t\t\t\t\t} catch (IllegalArgumentException e) {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Problem processing socket data, sid =  \" + sid_str +\n\t\t\t\t\t\t\t\t\" does not conform to the UUID string representation.\", e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tif (bs != null) {\n\t\t\t\t\tsynchronized (bs) {\n\t\t\t\t\t\tif (sid_str == null) {\n\t\t\t\t\t\t\tbs.init(p, serv, max_wait, min_polling, max_inactivity, concurrent_requests, hold_requests,\n\t\t\t\t\t\t\t\t\tmax_pause, max_batch_size, batch_queue_timeout, out_results);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tbs.processSocketPacket(p, serv, out_results);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\tlog.log(Level.FINE, \"{0} : {1} ({2})\",\n\t\t\t\t\t\t\t\tnew Object[]{BOSH_OPERATION_TYPE.INVALID_SID, sid_str, \"Invalid SID\"});\n\t\t\t\t\t}\n\t\t\t\t\tserv.sendErrorAndStop(Authorization.ITEM_NOT_FOUND, null, p, \"Invalid SID\");\n\t\t\t\t}\n\t\t\t\taddOutPackets(out_results, bs);\n\t\t\t} catch (IOException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Problem processing socket data for sid =  \" + sid_str, e);\n\t\t\t}\n\n\t\t\t// addOutPackets(out_results);\n\t\t}    // end of while ()\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic BoshSendQueueTask scheduleSendQueueTask(final BoshSession bs, long delay) {\n\t\tBoshSendQueueTask bt = new BoshSendQueueTask(bs);\n\n\t\taddTimerTask(bt, delay);\n\n\t\t// boshTasks.schedule(bt, delay);\n\t\treturn bt;\n\t}\n\n\t@Override\n\tpublic BoshTask scheduleTask(BoshSession bs, long delay) {\n\t\tBoshTask bt = new BoshTask(bs, this);\n\n\t\taddTimerTask(bt, delay);\n\n\t\t// boshTasks.schedule(bt, delay);\n\t\treturn bt;\n\t}\n\n\t@Override\n\tpublic void serviceStarted(XMPPIOService<Object> service) {\n\t\tsuper.serviceStarted(service);\n\t}\n\n\t@Override\n\tpublic boolean serviceStopped(XMPPIOService<Object> xmppService) {\n\t\tBoshIOService service = (BoshIOService) xmppService;\n\t\tboolean result = super.serviceStopped(service);\n\n\t\tUUID sid = service.getSid();\n\n\t\tif (sid != null) {\n\t\t\tBoshSession bs = sessions.get(sid);\n\n\t\t\tif (bs != null) {\n\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\tlog.log(Level.FINE, \"{0} : {1} ({2})\",\n\t\t\t\t\t\t\tnew Object[]{BOSH_OPERATION_TYPE.REMOVE, bs.getSid(), \"Closing bosh session\"});\n\t\t\t\t}\n\n\t\t\t\tbs.disconnected(service);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic void writeRawData(BoshIOService ios, String data) {\n\t\tsuper.writeRawData(ios, data);\n\t}\n\n\t@Override\n\tpublic void xmppStreamClosed(XMPPIOService<Object> serv) {\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.finer(\"Stream closed.\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic String[] xmppStreamOpened(XMPPIOService<Object> serv, Map<String, String> attribs) {\n\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\tlog.fine(\"Ups, what just happened? Stream open. Hey, this is a Bosh connection manager.\" +\n\t\t\t\t\t\t\t \" c2s and s2s are not supported on the same port as Bosh yet.\");\n\t\t}\n\n\t\treturn new String[] { \"<?xml version='1.0'?><stream:stream\" + \" xmlns='jabber:client'\" +\n\t\t\t\t\" xmlns:stream='http://etherx.jabber.org/streams'\" + \" id='1'\" + \" from='\" + getDefVHostItem() + \"'\" +\n\t\t\t\t\" version='1.0' xml:lang='en'>\" + \"<stream:error>\" +\n\t\t\t\t\"<invalid-namespace xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>\" +\n\t\t\t\t\"<text xmlns='urn:ietf:params:xml:ns:xmpp-streams' xml:lang='langcode'>\" +\n\t\t\t\t\"Ups, what just happened? Stream open. Hey, this is a Bosh connection manager. \" +\n\t\t\t\t\"c2s and s2s are not supported on the same port... yet.\" + \"</text>\" + \"</stream:error>\" +\n\t\t\t\t\"</stream:stream>\" };\n\t}\n\n\t@Override\n\tpublic String getDiscoCategoryType() {\n\t\treturn \"c2s\";\n\t}\n\n\t@Override\n\tpublic String getDiscoDescription() {\n\t\treturn \"Bosh connection manager\";\n\t}\n\n\t/**\n\t * Returns full jid of passed BoshSession instance\n\t *\n\t * @param bs {@link BoshSession} for which JID should be retrieved\n\t *\n\t * @return JID address related to particular {@link BoshSession}\n\t */\n\t@Override\n\tpublic JID getJidForBoshSession(BoshSession bs) {\n\t\treturn getFromAddress(bs.getSid().toString());\n\t}\n\n\t@Override\n\tpublic Element getSeeOtherHostError(Packet packet, BareJID destination) {\n\n\t\tXMPPIOService<Object> xmppioService = getXMPPIOService(packet);\n\n\t\tInteger redirect_port = (Integer) xmppioService.getSessionData().get(FORCE_REDIRECT_TO_KEY);\n\n\t\treturn see_other_host_strategy.getStreamError(\"urn:ietf:params:xml:ns:xmpp-streams\", destination,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  redirect_port);\n\n\t}\n\n\t@Override\n\tpublic BareJID getSeeOtherHostForJID(Packet packet, BareJID fromJID, Phase ph) {\n\t\tif (see_other_host_strategy == null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"no see-other-host implementation set\");\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\t\tif (!see_other_host_strategy.isEnabled(vHostManager.getVHostItem(fromJID.getDomain()), ph)) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"see-other-host not enabled for the Phase: \" + ph.toString());\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tBareJID see_other_host = see_other_host_strategy.findHostForJID(fromJID, getDefHostName());\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"using = \" + see_other_host_strategy.getClass().getCanonicalName() + \"for jid = \" +\n\t\t\t\t\t\t\t   fromJID.toString() + \" got = \" +\n\t\t\t\t\t\t\t   ((see_other_host != null) ? see_other_host.toString() : \"null\") + \" in phase: \" +\n\t\t\t\t\t\t\t   ph.toString());\n\t\t}\n\n\t\tXMPPIOService<Object> xmppioService = getXMPPIOService(packet);\n\t\tInteger redirect_port = xmppioService != null\n\t\t\t\t\t\t\t\t? (Integer) xmppioService.getSessionData().getOrDefault(FORCE_REDIRECT_TO_KEY, -1)\n\t\t\t\t\t\t\t\t: -1;\n\n\t\treturn ((see_other_host != null) &&\n\t\t\t\t(redirect_port > 0 || see_other_host_strategy.isRedirectionRequired(getDefHostName(), see_other_host)))\n\t\t\t   ? see_other_host\n\t\t\t   : null;\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t\tsuper.getStatistics(list);\n\t\tif (list.checkLevel(Level.FINEST)) {\n\n\t\t\t// Be careful here, the size() for this map is expensive to count\n\t\t\tlist.add(getName(), \"Bosh sessions\", sessions.size(), Level.FINEST);\n\t\t}\n\t}\n\n\tpublic void setSidLoggerLevel(String loggerLevel) {\n\t\tLevel level = Level.OFF;\n\t\tif (loggerLevel != null) {\n\t\t\tlevel = Level.parse(loggerLevel);\n\t\t}\n\t\tthis.sidLoggerLevel = level.getName();\n\t\tsetupSidlogger(level);\n\t}\n\n\t@Override\n\tpublic void initBindings(Bindings binds) {\n\t\tsuper.initBindings(binds);\n\t\tbinds.put(\"boshCM\", this);\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tif (extraHeaders == null && extraHeadersFile != null) {\n\t\t\tsetExtraHeadersFile(extraHeadersFile);\n\t\t}\n\t\tif (clientAccessPolicy == null && clientAccessPolicyFile != null) {\n\t\t\tsetClientAccessPolicyFile(clientAccessPolicyFile);\n\t\t}\n\t\tsuper.initialize();\n\t}\n\n\t@Override\n\tpublic boolean isCloseConnections() {\n\t\treturn closeConnections;\n\t}\n\n\t@Override\n\tpublic String getClientAccessPolicy() {\n\t\treturn clientAccessPolicy;\n\t}\n\n\tpublic void setClientAccessPolicyFile(String clientAccessPolicyFile) {\n\t\tthis.clientAccessPolicyFile = clientAccessPolicyFile;\n\t\ttry {\n\t\t\tBufferedReader br = new BufferedReader(new FileReader(clientAccessPolicyFile));\n\t\t\tString line = br.readLine();\n\t\t\tStringBuilder sb = new StringBuilder();\n\n\t\t\twhile (line != null) {\n\t\t\t\tsb.append(line).append(BoshIOService.EOL);\n\t\t\t\tline = br.readLine();\n\t\t\t}\n\t\t\tbr.close();\n\t\t\tclientAccessPolicy = sb.toString();\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.WARNING, \"Problem reading client access policy file: \" + clientAccessPolicyFile, ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getExtraHeaders() {\n\t\treturn extraHeaders;\n\t}\n\n\tpublic void setExtraHeadersFile(String extraHeadersFile) {\n\t\tthis.extraHeadersFile = extraHeadersFile;\n\t\ttry {\n\t\t\tBufferedReader br = new BufferedReader(new FileReader(extraHeadersFile));\n\t\t\tString line = br.readLine();\n\t\t\tStringBuilder sb = new StringBuilder();\n\n\t\t\twhile (line != null) {\n\t\t\t\tsb.append(line).append(BoshIOService.EOL);\n\t\t\t\tline = br.readLine();\n\t\t\t}\n\t\t\tbr.close();\n\t\t\textraHeaders = sb.toString();\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.WARNING, \"Problem reading Bosh extra headers file: \" + extraHeadersFile, ex);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void setupWatchdogThread() {\n\t\t// having watchdog for bosh connections is not needed\n\t}\n\n\tprotected Map<String, String> preBindSession(Map<String, String> attr) {\n\t\tString hostname = attr.get(TO_ATTR);\n\n\t\tQueue<Packet> out_results = new ArrayDeque<Packet>(2);\n\n\t\tBoshSession bs = new BoshSession(getDefVHostItem().getDomain(),\n\t\t\t\t\t\t\t\t\t\t JID.jidInstanceNS(routings.computeRouting(hostname)), this,\n\t\t\t\t\t\t\t\t\t\t sendNodeHostname ? getDefHostName().getDomain() : null,\n\t\t\t\t\t\t\t\t\t\t maxSessionWaitingPackets);\n\n\t\tString jid = attr.get(FROM_ATTR);\n\t\tString uuid = UUID.randomUUID().toString();\n\t\tJID userId = JID.jidInstanceNS(jid);\n\t\tif (null == userId.getResource()) {\n\t\t\tuserId = userId.copyWithResourceNS(uuid);\n\t\t\tattr.put(FROM_ATTR, userId.toString());\n\t\t\tbs.setUserJid(jid);\n\t\t}\n\t\tlong rid = (long) (Math.random() * 10000000);\n\n\t\tattr.put(RID_ATTR, Long.toString(rid));\n\n\t\tUUID sid = bs.getSid();\n\t\tsessions.put(sid, bs);\n\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\tlog.log(Level.FINE, \"{0} : {1} ({2})\", new Object[]{BOSH_OPERATION_TYPE.CREATE, bs.getSid(), \"Pre-bind\"});\n\t\t}\n\n\t\tattr.put(SID_ATTR, sid.toString());\n\n\t\tPacket p = null;\n\t\ttry {\n\t\t\tElement el = new Element(\"body\");\n\t\t\tel.setAttributes(attr);\n\t\t\tp = Packet.packetInstance(el);\n\t\t\tp.setPacketTo(getComponentId().copyWithResource(sid.toString()));\n\t\t} catch (TigaseStringprepException ex) {\n\t\t\tLogger.getLogger(BoshConnectionManager.class.getName()).log(Level.SEVERE, null, ex);\n\t\t}\n\t\tbs.init(p, null, max_wait, min_polling, max_inactivity, concurrent_requests, hold_requests, max_pause,\n\t\t\t\tmax_batch_size, batch_queue_timeout, out_results, true);\n\t\taddOutPackets(out_results, bs);\n\n\t\tattr.put(\"hostname\", getDefHostName().toString());\n\n\t\treturn attr;\n\t}\n\n\t/**\n\t * Method adds packets to the output queue stamping it with the appropriate {@link BoshSession} data\n\t *\n\t * @param out_results collection of {@link Packet} objects to be added to queue\n\t * @param bs related {@link BoshSession}\n\t */\n\tprotected void addOutPackets(Queue<Packet> out_results, BoshSession bs) {\n\t\tfor (Packet res : out_results) {\n\t\t\tres.setPacketFrom(getFromAddress(bs.getSid().toString()));\n\t\t\tres.setPacketTo(bs.getDataReceiver());\n\t\t\tif (res.getCommand() != null) {\n\t\t\t\tswitch (res.getCommand()) {\n\t\t\t\t\tcase STREAM_CLOSED:\n\t\t\t\t\tcase GETFEATURES:\n\t\t\t\t\t\tres.initVars(res.getPacketFrom(), res.getPacketTo());\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\n\t\t\t\t\t\t// Do nothing...\n\t\t\t\t}\n\t\t\t}\n\t\t\taddOutPacket(res);\n\t\t}\n\t\tout_results.clear();\n\t}\n\n\t@Override\n\tprotected JID changeDataReceiver(Packet packet, JID newAddress, String command_sessionId,\n\t\t\t\t\t\t\t\t\t XMPPIOService<Object> serv) {\n\t\tBoshSession session = getBoshSession(packet.getTo());\n\n\t\tif (session != null) {\n\t\t\tString sessionId = session.getSessionId();\n\n\t\t\tif (sessionId.equals(command_sessionId)) {\n\t\t\t\tJID old_receiver = session.getDataReceiver();\n\n\t\t\t\tsession.setDataReceiver(newAddress);\n\n\t\t\t\treturn old_receiver;\n\t\t\t} else {\n\t\t\t\tlog.log(Level.CONFIG, \"Incorrect session ID, ignoring data redirect for: \" + newAddress);\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tprotected ReceiverTimeoutHandler newStartedHandler() {\n\t\treturn new StartedHandler();\n\t}\n\n\t@Override\n\tprotected void processCommand(Packet packet) {\n\t\tBoshSession session = getBoshSession(packet.getTo());\n\n\t\tswitch (packet.getCommand()) {\n\t\t\tcase USER_LOGIN:\n\t\t\t\tString jid = Command.getFieldValue(packet, \"user-jid\");\n\n\t\t\t\tif (jid != null) {\n\t\t\t\t\tif (session != null) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tBareJID fromJID = BareJID.bareJIDInstance(jid);\n\t\t\t\t\t\t\tBareJID hostJid = getSeeOtherHostForJID(packet, fromJID, Phase.LOGIN);\n\n\t\t\t\t\t\t\tif (hostJid != null) {\n\n\t\t\t\t\t\t\t\tXMPPIOService<Object> xmppioService = getXMPPIOService(packet);\n\t\t\t\t\t\t\t\tInteger port = (Integer) xmppioService.getSessionData().get(FORCE_REDIRECT_TO_KEY);\n\n\t\t\t\t\t\t\t\tElement streamErrorElement = see_other_host_strategy.getStreamError(\n\t\t\t\t\t\t\t\t\t\t\"urn:ietf:params:xml:ns:xmpp-streams\", hostJid, port);\n\t\t\t\t\t\t\t\tPacket redirectPacket = Packet.packetInstance(streamErrorElement);\n\n\t\t\t\t\t\t\t\tredirectPacket.setPacketTo(packet.getTo());\n\t\t\t\t\t\t\t\twritePacketToSocket(redirectPacket);\n\t\t\t\t\t\t\t\tsession.sendWaitingPackets();\n\t\t\t\t\t\t\t\tsession.close();\n\t\t\t\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\t\t\t\tlog.log(Level.FINE, \"{0} : {1} ({2})\",\n\t\t\t\t\t\t\t\t\t\t\tnew Object[]{BOSH_OPERATION_TYPE.REMOVE, session.getSid(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"See other host\"});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tsessions.remove(session.getSid());\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tsession.setUserJid(jid);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\t\t\tlog.log(Level.SEVERE, \"user JID violates RFC6122 (XMPP:Address Format): \", ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\t\tlog.log(Level.FINE, \"Missing XMPPIOService for USER_LOGIN command: {0}\", packet);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tlog.log(Level.WARNING, \"Missing user-jid for USER_LOGIN command: {0}\", packet);\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\n\t\t\tcase CLOSE:\n\t\t\t\tif (session != null) {\n\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\tlog.log(Level.FINER, \"Closing session for command CLOSE: {0}\", session.getSid());\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tList<Element> err_el = packet.getElement().getChildrenStaticStr(Iq.IQ_COMMAND_PATH);\n\n\t\t\t\t\t\tif ((err_el != null) && (err_el.size() > 0)) {\n\t\t\t\t\t\t\tElement error = new Element(\"stream:error\");\n\n\t\t\t\t\t\t\terror.addChild(err_el.get(0));\n\n\t\t\t\t\t\t\tPacket condition = Packet.packetInstance(error);\n\n\t\t\t\t\t\t\tcondition.setPacketTo(packet.getTo());\n\t\t\t\t\t\t\twritePacketToSocket(condition);\n\t\t\t\t\t\t\tsession.sendWaitingPackets();\n\t\t\t\t\t\t\tbosh_session_close_delay = 100;\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\t\tLogger.getLogger(BoshConnectionManager.class.getName()).log(Level.SEVERE, null, ex);\n\t\t\t\t\t}\n\t\t\t\t\tif (bosh_session_close_delay > 0) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tThread.sleep(bosh_session_close_delay);\n\t\t\t\t\t\t} catch (InterruptedException ex) {\n\n\t\t\t\t\t\t\t// Intentionally left blank\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tsession.close();\n\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\tlog.log(Level.FINE, \"{0} : {1} ({2})\",\n\t\t\t\t\t\t\t\tnew Object[]{BOSH_OPERATION_TYPE.REMOVE, session.getSid(),\n\t\t\t\t\t\t\t\t\t\t\t \"Closing session for command CLOSE\"});\n\t\t\t\t\t}\n\t\t\t\t\tsessions.remove(session.getSid());\n\t\t\t\t} else {\n\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\tlog.log(Level.FINE, \"Session does not exist for packet: {0}\", packet);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\n\t\t\tcase CHECK_USER_CONNECTION:\n\t\t\t\tif (session != null) {\n\n\t\t\t\t\t// It's ok, the session has been found, respond with OK.\n\t\t\t\t\taddOutPacket(packet.okResult((String) null, 0));\n\t\t\t\t} else {\n\n\t\t\t\t\t// Session is no longer active, respond with an error.\n\t\t\t\t\ttry {\n\t\t\t\t\t\taddOutPacket(\n\t\t\t\t\t\t\t\tAuthorization.ITEM_NOT_FOUND.getResponseMessage(packet, \"Connection gone.\", false));\n\t\t\t\t\t} catch (PacketErrorTypeException e) {\n\n\t\t\t\t\t\t// Hm, error already, ignoring...\n\t\t\t\t\t\tlog.log(Level.CONFIG, \"Error packet is not really expected here: {0}\", packet);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tsuper.processCommand(packet);\n\n\t\t\t\tbreak;\n\t\t}    // end of switch (pc.getCommand())\n\t}\n\n\t@Override\n\tprotected boolean writePacketToSocket(Packet packet) {\n\t\tBoshSession session = getBoshSession(packet.getTo());\n\n\t\tif (session != null) {\n\t\t\tsynchronized (session) {\n\t\t\t\tQueue<Packet> out_results = new ArrayDeque<Packet>();\n\n\t\t\t\tsession.processPacket(packet, out_results);\n\t\t\t\taddOutPackets(out_results, session);\n\t\t\t}\n\n\t\t\treturn true;\n\t\t} else {\n\t\t\tlog.log(Level.CONFIG, \"Session does not exist for packet: \" + packet.toString());\n\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Method retrieves {@link BoshSession} related to the particular user address\n\t *\n\t * @param jid address for which {@link BoshSession} should be returned\n\t *\n\t * @return a value of {@link BoshSession}\n\t */\n\tprotected BoshSession getBoshSession(JID jid) {\n\t\tString res = jid.getResource();\n\n\t\tif (res != null) {\n\t\t\tUUID sid = UUID.fromString(res);\n\n\t\t\treturn sessions.get(sid);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tprotected int[] getDefPlainPorts() {\n\t\treturn PORTS;\n\t}\n\n\t@Override\n\tprotected int[] getDefSSLPorts() {\n\t\treturn null;\n\t}\n\n\t/**\n\t * Method <code>getMaxInactiveTime</code> returns max keep-alive time for inactive connection. For Bosh it does not\n\t * make sense to keep the idle connection longer than 10 minutes.\n\t *\n\t * @return a <code>long</code> value\n\t */\n\t@Override\n\tprotected long getMaxInactiveTime() {\n\t\treturn 10 * MINUTE;\n\n\t}\n\n\t@Override\n\tprotected BoshIOService getXMPPIOServiceInstance() {\n\t\treturn new BoshIOService(this);\n\t}\n\n\t// ~--- get methods ----------------------------------------------------------\n\tprivate JID getFromAddress(String id) {\n\t\treturn JID.jidInstanceNS(getName(), getDefHostName().getDomain(), id);\n\t}\n\n\tprotected enum BOSH_OPERATION_TYPE {\n\n\t\tCREATE,\n\t\tREMOVE,\n\t\tINVALID_SID,\n\t\tTIMER;\n\n\t\tprivate static final Map<String, BOSH_OPERATION_TYPE> nameToValueMap = new HashMap<String, BOSH_OPERATION_TYPE>();\n\n\t\tstatic {\n\t\t\tfor (BOSH_OPERATION_TYPE value : EnumSet.allOf(BOSH_OPERATION_TYPE.class)) {\n\t\t\t\tnameToValueMap.put(value.name(), value);\n\t\t\t}\n\t\t}\n\n\t\tpublic static BOSH_OPERATION_TYPE forName(String name) {\n\t\t\treturn nameToValueMap.get(name);\n\t\t}\n\n\t}\n\n\t// ~--- inner classes --------------------------------------------------------\n\tprivate class StartedHandler\n\t\t\timplements ReceiverTimeoutHandler {\n\n\t\t@Override\n\t\tpublic void responseReceived(Packet packet, Packet response) {\n\t\t\tString pb = Command.getFieldValue(packet, PRE_BIND_ATTR);\n\t\t\tboolean prebind = Boolean.valueOf(pb);\n\n\t\t\tString sessionId = Command.getFieldValue(packet, SESSION_ID_ATTR);\n\t\t\tString userID = Command.getFieldValue(packet, USER_ID_ATTR);\n\n\t\t\tif (prebind) {\n\t\t\t\t// we are doing pre-bind, send user-login command, bind resource\n\t\t\t\tPacket packetOut = Command.USER_STATUS.getPacket(packet.getFrom(), packet.getTo(), StanzaType.get,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t UUID.randomUUID().toString());\n\n//\t\t\t\tElement presence = new Element( \"presence\" );\n\t\t\t\tCommand.addFieldValue(packetOut, USER_ID_ATTR, userID);\n\t\t\t\tif (null != sessionId) {\n\t\t\t\t\tCommand.addFieldValue(packetOut, SESSION_ID_ATTR, sessionId);\n\t\t\t\t}\n\t\t\t\tCommand.addFieldValue(packetOut, PRE_BIND_ATTR, String.valueOf(prebind));\n\n\t\t\t\taddOutPacket(packetOut);\n\t\t\t} else {\n\n\t\t\t\t// We are now ready to ask for features....\n\t\t\t\taddOutPacket(Command.GETFEATURES.getPacket(packet.getFrom(), packet.getTo(), StanzaType.get,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   UUID.randomUUID().toString(), null));\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void timeOutExpired(Packet packet) {\n\n\t\t\t// If we still haven't received confirmation from the SM then\n\t\t\t// the packet either has been lost or the server is overloaded\n\t\t\t// In either case we disconnect the connection.\n\t\t\tlog.warning(\"No response within time limit received for a packet: \" + packet.toString());\n\n\t\t\tBoshSession session = getBoshSession(packet.getFrom());\n\n\t\t\tif (session != null) {\n\t\t\t\tlog.fine(\"Closing session for timeout: \" + session.getSid());\n\t\t\t\tsession.close();\n\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\tlog.log(Level.FINE, \"{0} : {1} ({2})\",\n\t\t\t\t\t\t\tnew Object[]{BOSH_OPERATION_TYPE.REMOVE, session.getSid(), \"Closing session for timeout\"});\n\t\t\t\t}\n\t\t\t\tsessions.remove(session.getSid());\n\t\t\t} else {\n\t\t\t\tlog.log(Level.CONFIG, \"Session does not exist for packet: \" + packet.toString());\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/bosh/BoshIOService.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.bosh;\n\nimport tigase.server.Packet;\nimport tigase.server.xmppclient.XMPPIOProcessor;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.PacketErrorTypeException;\nimport tigase.xmpp.StreamError;\nimport tigase.xmpp.XMPPIOService;\n\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.util.UUID;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.util.StringUtilities.checkIfArrayContainsString;\n\n/**\n * Describe class BoshIOService here.\n * <br>\n * Created: Tue Jun 5 22:33:18 2007\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class BoshIOService\n\t\textends XMPPIOService<Object> {\n\n\tpublic static final String EOL = \"\\r\\n\";\n\tprivate static final String CONNECTION = \"Connection: \";\n\tprivate static final String CONTENT_TYPE_HEADER = \"Content-Type: \";\n\tprivate static final String CONTENT_TYPE_LENGTH = \"Content-Length: \";\n\tprivate static final Logger log = Logger.getLogger(BoshIOService.class.getName());\n\tprivate static final String HTTP_OK_RESPONSE = \"HTTP/1.1 200 OK\" + EOL;\n\tprivate static final String SERVER = \"Server: Tigase Bosh/\" + tigase.server.XMPPServer.getImplementationVersion();\n\tprivate static final char[] HTTP_CLIENT_ACCESS_POLICY_REQUEST_HEADER = \"GET /clientaccesspolicy.xml\".toCharArray();\n\tprivate static final char[] HTTP_CLIENT_ROOT_REQUEST_HEADER = \"GET /\".toCharArray();\n\tprivate static final char[] HTTP_CLIENT_GET_REQUEST_HEADER = \"GET\".toCharArray();\n\tprivate static final char[] HTTP_CLIENT_OPTIONS_REQUEST_HEADER = \"OPTIONS\".toCharArray();\n\n\tprivate final ConfigProvider configProvider;\n\tprivate String content_type = \"text/xml; charset=utf-8\";\n\tprivate boolean firstPassCORS = true;\n\tprivate boolean firstPassClientAccessPolicy = true;\n\tprivate long rid = -1;\n\tprivate UUID sid = null;\n\tprivate BoshTask waitTimer = null;\n\n\tpublic BoshIOService(ConfigProvider configProvider) {\n\t\tsuper();\n\t\tthis.configProvider = configProvider;\n\t}\n\n\tpublic long getRid() {\n\t\treturn this.rid;\n\t}\n\n\tpublic void setRid(long rid) {\n\t\tthis.rid = rid;\n\t}\n\n\tpublic UUID getSid() {\n\t\treturn this.sid;\n\t}\n\n\tpublic void setSid(UUID sid) {\n\t\tthis.sid = sid;\n\t}\n\n\tpublic BoshTask getWaitTimer() {\n\t\treturn waitTimer;\n\t}\n\n\tpublic void setWaitTimer(BoshTask timer) {\n\t\twaitTimer = timer;\n\t}\n\n\tpublic void sendErrorAndStop(Authorization errorCode, StreamError streamError, Packet packet, String errorMsg)\n\t\t\tthrows IOException {\n\t\tfor (XMPPIOProcessor proc : processors) {\n\t\t\tproc.streamError(this, streamError);\n\t\t}\n\t\tString code = \"<body type='terminate'\" + \" condition='\" +\n\t\t\t\t(streamError != null ? streamError.getCondition() : errorCode.getCondition()) + \"'\" +\n\t\t\t\t\" xmlns='http://jabber.org/protocol/httpbind'/>\";\n\n\t\ttry {\n\t\t\tPacket error = errorCode.getResponseMessage(packet, errorMsg, false);\n\n\t\t\tcode = error.getElement().toString();\n\t\t} catch (PacketErrorTypeException e) {\n\n\t\t\t// ignore\n\t\t}\n\n\t\tStringBuilder sb = new StringBuilder(200);\n\n\t\tsb.append(\"HTTP/1.1 \").append(errorCode.getErrorCode()).append(\" \");\n\t\tsb.append(errorMsg).append(EOL);\n\t\tsb.append(CONTENT_TYPE_HEADER).append(content_type).append(EOL);\n\t\tsb.append(CONTENT_TYPE_LENGTH).append(code.getBytes().length).append(EOL);\n\t\tString extra_headers = configProvider.getExtraHeaders();\n\t\tif (extra_headers != null) {\n\t\t\tsb.append(extra_headers);\n\t\t}\n\n\t\t// sb.append(\"X-error-code\").append(code).append(EOL);\n\t\tsb.append(CONNECTION + \"close\" + EOL);\n\t\tsb.append(SERVER).append(EOL);\n\t\tsb.append(EOL);\n\t\tsb.append(code);\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Writing to socket:\\n{0}\", sb.toString());\n\t\t}\n\t\tsuper.writeRawData(sb.toString());\n\t\tstop();\n\t}\n\n\tpublic void setContentType(String ct) {\n\t\tthis.content_type = ct;\n\t}\n\n\tpublic StringBuilder prepareHeaders(String data)  {\n\t\tStringBuilder sb = new StringBuilder(200);\n\n\t\tsb.append(HTTP_OK_RESPONSE);\n\t\tsb.append(CONTENT_TYPE_HEADER).append(content_type).append(EOL);\n\t\tif (data != null) {\n\t\t\tsb.append(CONTENT_TYPE_LENGTH).append(getDataLength(data, content_type)).append(EOL);\n\t\t} else {\n\t\t\tsb.append(CONTENT_TYPE_LENGTH).append(\"0\").append(EOL);\n\t\t}\n\t\tString extra_headers = configProvider.getExtraHeaders();\n\t\tif (extra_headers != null) {\n\t\t\tsb.append(extra_headers);\n\t\t}\n\t\tsb.append(SERVER).append(EOL);\n\t\tsb.append(EOL);\n\n\t\treturn sb;\n\t}\n\n\tprotected int getDataLength(String data, String content_type) {\n\t\tString charset = getCharset(content_type);\n\t\tif (charset != null) {\n\t\t\ttry {\n\t\t\t\treturn data.getBytes(charset).length;\n\t\t\t} catch (UnsupportedEncodingException e) {\n\t\t\t\tlog.fine(\"invalid charset:\" + charset);\n\t\t\t}\n\t\t}\n\n\t\treturn data.getBytes().length;\n\t}\n\n\tprotected String getCharset(String content_type) {\n\t\tif (content_type != null) {\n\t\t\tString[] props = content_type.split(\";\");\n\t\t\tfor (String prop : props) {\n\t\t\t\tint i;\n\t\t\t\tString trimmed = prop != null ? prop.trim() : null;\n\t\t\t\tif (trimmed != null && (i = trimmed.indexOf('=')) != -1 &&\n\t\t\t\t\t\t\"charset\".equalsIgnoreCase(trimmed.substring(0, i))) {\n\t\t\t\t\treturn trimmed.substring(i + 1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void writeRawData(String data) throws IOException {\n\t\tif ((data != null) && data.startsWith(\"<body\")) {\n\t\t\tStringBuilder sb = prepareHeaders(data);\n\n\t\t\tsb.append(data);\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Writing to socket:\\n{0}\", sb.toString());\n\t\t\t}\n\t\t\tsuper.writeRawData(sb.toString());\n\t\t} else {\n\t\t\tsuper.writeRawData(data);\n\t\t}\n\t\tif (configProvider.isCloseConnections()) {\n\t\t\tstop();\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean checkData(char[] data) throws IOException {\n\t\t// we need to check this every time as Webkit based browser are reusing\n\t\t// existing connections and repeat this CORS request every 10 minutes\n\t\t//if (firstPassCORS && \n\t\tfinal boolean isOptionsRequst = checkIfArrayContainsString(data, HTTP_CLIENT_OPTIONS_REQUEST_HEADER);\n\t\tif (isOptionsRequst) {\n\t\t\t// responding with headers - needed for Chrome browser\n\t\t\tthis.writeRawData(prepareHeaders(null).toString());\n\n\t\t\t// connection needs to be closed as in other case data headers are not sent to browser\n\t\t\t// until connection is closed and for OPTIONS request we are not sending any data\n\t\t\t//firstPassCORS = false;\n\n\t\t\treturn false;\n\t\t}\n\t\tfinal boolean isGetRequest = checkIfArrayContainsString(data, HTTP_CLIENT_GET_REQUEST_HEADER);\n\t\tif (firstPassClientAccessPolicy && isGetRequest) {\n\t\t\tfinal boolean isClientAccessPolicyRequest = checkIfArrayContainsString(data,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   HTTP_CLIENT_ACCESS_POLICY_REQUEST_HEADER);\n\t\t\tfinal boolean isClientRootRequest = checkIfArrayContainsString(data, HTTP_CLIENT_ROOT_REQUEST_HEADER);\n\t\t\tif (isClientAccessPolicyRequest || isClientRootRequest) {\n\t\t\t\tif (isClientAccessPolicyRequest) {\n\t\t\t\t\tString client_access_policy = configProvider.getClientAccessPolicy();\n\t\t\t\t\tthis.writeRawData(prepareHeaders(client_access_policy).toString() + client_access_policy);\n\t\t\t\t} else if (isClientRootRequest) {\n\t\t\t\t\tString notice = \"This is BOSH (XEP-0124: Bidirectional-streams Over Synchronous HTTP (BOSH)) endpoint, if you want to use it use POST method as defined in it's specification: https://xmpp.org/extensions/xep-0124.html\";\n\t\t\t\t\tthis.writeRawData(prepareHeaders(notice).toString() + notice);\n\t\t\t\t}\n\t\t\t\t// connection needs to be closed as in other case data headers are not sent to browser\n\t\t\t\t// until connection is closed\n\t\t\t\tfirstPassClientAccessPolicy = false;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tfirstPassClientAccessPolicy = false;\n\t\t}\n\n\t\t// by default do nothing and return false\n\t\treturn false;\n\t}\n\n\tpublic interface ConfigProvider {\n\n\t\tboolean isCloseConnections();\n\n\t\tString getExtraHeaders();\n\n\t\tString getClientAccessPolicy();\n\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/bosh/BoshSendQueueTask.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.bosh;\n\nimport tigase.util.common.TimerTask;\n\n/**\n * @author andrzej\n */\npublic class BoshSendQueueTask\n\t\textends TimerTask {\n\n\tprivate final BoshSession bs;\n\n\tpublic BoshSendQueueTask(BoshSession bs) {\n\t\tthis.bs = bs;\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\tbs.sendWaitingPackets();\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/bosh/BoshSession.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.bosh;\n\nimport tigase.server.Command;\nimport tigase.server.Iq;\nimport tigase.server.Message;\nimport tigase.server.Packet;\nimport tigase.server.bosh.Constants.*;\nimport tigase.server.xmppclient.SeeOtherHostIfc.Phase;\nimport tigase.util.common.TimerTask;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.PacketErrorTypeException;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.concurrent.ConcurrentSkipListMap;\nimport java.util.concurrent.ConcurrentSkipListSet;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.regex.Pattern;\n\nimport static tigase.server.bosh.Constants.*;\n\n/**\n * Describe class BoshSession here.\n * <br>\n * Created: Tue Jun 5 18:07:23 2007\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class BoshSession {\n\n\tprivate static final String IQ_ELEMENT_NAME = \"iq\";\n\n\tprivate static final Logger log = Logger.getLogger(BoshSession.class.getName());\n\tprivate static final String MESSAGE_ELEMENT_NAME = \"message\";\n\tprivate static final String PRESENCE_ELEMENT_NAME = \"presence\";\n\tprivate static final long SECOND = 1000;\n\tprivate static final TimerTaskComparator timerTaskComparator = new TimerTaskComparator();\n\n\tprivate long batch_queue_timeout = BATCH_QUEUE_TIMEOUT_VAL;\n\t// ~--- fields ---------------------------------------------------------------\n\tprivate BoshSessionCache cache = null;\n\tprivate boolean cache_on = false;\n\tprivate long cache_reload_counter = 0;\n\tprivate int concurrent_requests = CONCURRENT_REQUESTS_PROP_VAL;\n\t// Active connections with pending requests received\n\tprivate ConcurrentSkipListMap<BoshTask, BoshIOService> connections = new ConcurrentSkipListMap<BoshTask, BoshIOService>(\n\t\t\ttimerTaskComparator);\n\tprivate String content_type = CONTENT_TYPE_DEF;\n\t/**\n\t * <code>current_rid</code> is the table with body rids which are waiting for replies.\n\t */\n\tprivate long[] currentRids = null;\n\tprivate JID dataReceiver = null;\n\tprivate String domain = null;\n\tprivate BoshSessionTaskHandler handler = null;\n\tprivate int[] hashCodes = null;\n\tprivate int hold_requests = HOLD_REQUESTS_PROP_VAL;\n\tprivate String hostname = null;\n\tprivate BoshTask inactivityTimer = null;\n\tprivate long last_send_time;\n\tprivate Pattern[] links_regexs = {Pattern.compile(\"([^>/\\\";]|^)(www\\\\.[^ ]+)\", Pattern.CASE_INSENSITIVE),\n\t\t\t\t\t\t\t\t\t  Pattern.compile(\"([^\\\">;]|^)(http://[^ ]+)\", Pattern.CASE_INSENSITIVE),};\n\tprivate int max_batch_size = MAX_BATCH_SIZE_VAL;\n\tprivate long max_inactivity = MAX_INACTIVITY_PROP_VAL;\n\tprivate long max_pause = MAX_PAUSE_PROP_VAL;\n\tprivate long max_wait = MAX_WAIT_DEF_PROP_VAL;\n\tprivate long min_polling = MIN_POLLING_PROP_VAL;\n\t// Old connections which might be reused in keep-alive mode.\n\t// Requests have been responded to so in most cases the connection should\n\t// be closed unless it is reused in keep-alive mode.\n\t// Normally there should be no more than max 2 elements in the queue.\n\tprivate Queue<BoshIOService> old_connections = new LinkedBlockingQueue<BoshIOService>(4);\n\tprivate long previous_received_rid = -1;\n\tprivate BoshSendQueueTask queueTask = null;\n\tprivate String[] replace_with = {\"$1&lt;a href=\\\"http://$2\\\" target=\\\"_blank\\\"&gt;$2&lt;/a&gt;\",\n\t\t\t\t\t\t\t\t\t \"$1&lt;a href=\\\"$2\\\" target=\\\"_blank\\\"&gt;$2&lt;/a&gt;\",};\n\tprivate int rids_head = 0;\n\tprivate int rids_tail = 0;\n\tprivate String sessionId = null;\n\tprivate UUID sid = null;\n\tprivate boolean terminate = false;\n\tprivate JID userJid = null;\n\t// private enum TimedTask { EMPTY_RESP, STOP };\n\t// private Map<TimerTask, TimedTask> task_enum =\n\t// new LinkedHashMap<TimerTask, TimedTask>();\n\t// private EnumMap<TimedTask, TimerTask> enum_task =\n\t// new EnumMap<TimedTask, TimerTask>(TimedTask.class);\n\tprivate Set<BoshTask> waitTimerSet = new ConcurrentSkipListSet<BoshTask>(timerTaskComparator);\n\tprivate Queue<Element> waiting_packets = null;//new ConcurrentLinkedQueue<Element>();\n\n\t/**\n\t * Creates a new <code>BoshSession</code> instance.\n\t */\n\tpublic BoshSession(String def_domain, JID dataReceiver, BoshSessionTaskHandler handler, String hostname,\n\t\t\t\t\t   int maxWaitingPackets) {\n\t\tthis.sid = UUID.randomUUID();\n\t\tthis.domain = def_domain;\n\t\tthis.dataReceiver = dataReceiver;\n\t\tthis.handler = handler;\n\t\tthis.last_send_time = System.currentTimeMillis();\n\t\tthis.hostname = hostname;\n\t\tthis.waiting_packets = new LinkedBlockingQueue(maxWaitingPackets);\n\t}\n\n\tpublic void close() {\n\t\tterminate = true;\n\t\tprocessPacket(null, null);\n\t\tcloseAllConnections();\n\t}\n\n\tpublic void disconnected(BoshIOService bios) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Disconnected called for: \" + bios.getUniqueId());\n\t\t}\n\t\tif ((bios != null) && (bios.getWaitTimer() != null)) {\n\t\t\thandler.cancelTask(bios.getWaitTimer());\n\t\t\tconnections.remove(bios.getWaitTimer());\n\t\t}\n\t\tif (inactivityTimer != null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"{0} : {1} ({2})\",\n\t\t\t\t\t\tnew Object[]{BoshConnectionManager.BOSH_OPERATION_TYPE.TIMER, getSid(),\n\t\t\t\t\t\t\t\t\t \"Canceling inactivityTimer: \" + bios != null ? bios.getUniqueId() : \"n/a\"});\n\t\t\t}\n\t\t}\n\t\tif (connections.isEmpty()) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"{0} : {1} ({2})\",\n\t\t\t\t\t\tnew Object[]{BoshConnectionManager.BOSH_OPERATION_TYPE.TIMER, getSid(),\n\t\t\t\t\t\t\t\t\t \"Setting inactivityTimer for \" + max_inactivity + \" on: \" + bios != null\n\t\t\t\t\t\t\t\t\t ? bios.getUniqueId()\n\t\t\t\t\t\t\t\t\t : \"n/a\"});\n\t\t\t}\n\n\t\t\tinactivityTimer = handler.scheduleTask(this, max_inactivity * SECOND);\n\t\t}\n\t}\n\n\tpublic void init(Packet packet, BoshIOService service, long max_wait, long min_polling, long max_inactivity,\n\t\t\t\t\t int concurrent_requests, int hold_requests, long max_pause, int max_batch_size,\n\t\t\t\t\t long batch_queue_timeout, Queue<Packet> out_results) {\n\n\t\tinit(packet, service, max_wait, min_polling, max_inactivity, concurrent_requests, hold_requests, max_pause,\n\t\t\t max_batch_size, batch_queue_timeout, out_results, false);\n\t}\n\n\tpublic synchronized void processPacket(Packet packet, Queue<Packet> out_results) {\n\t\tif (packet != null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"[\" + connections.size() + \"] Processing packet: \" + packet.toString());\n\t\t\t}\n\t\t\tif (filterInPacket(packet)) {\n\t\t\t\tif (!waiting_packets.offer(packet.getElement())) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"waiting_packets queue exceeded, dropping packet: \" + packet.toString());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"[\" + connections.size() + \"] In packet filtered: \" + packet.toString());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif ((!connections.isEmpty()) && ((!waiting_packets.isEmpty()) || terminate)) {\n\t\t\tlong currentTime = System.currentTimeMillis();\n\n\t\t\tif (terminate || (waiting_packets.size() >= max_batch_size) ||\n\t\t\t\t\t(currentTime - last_send_time) > batch_queue_timeout) {\n\t\t\t\tMap.Entry<BoshTask, BoshIOService> entry = connections.pollFirstEntry();\n\t\t\t\tBoshIOService serv = entry.getValue();\n\n\t\t\t\tsendBody(serv, null);\n\t\t\t} else {\n\n\t\t\t\t// @todo - change it to use value from settings\n\t\t\t\tif (queueTask == null) {\n\t\t\t\t\tqueueTask = handler.scheduleSendQueueTask(this, batch_queue_timeout);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic synchronized void processSocketPacket(Packet packet, BoshIOService service, Queue<Packet> out_results) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"[\" + connections.size() + \"] Processing socket packet: \" + packet.toString());\n\t\t}\n\n\t\tBoshTask waitTimer = service.getWaitTimer();\n\n\t\tif (waitTimer != null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"{0} : {1} ({2})\",\n\t\t\t\t\t\tnew Object[]{BoshConnectionManager.BOSH_OPERATION_TYPE.TIMER, getSid(),\n\t\t\t\t\t\t\t\t\t \"Canceling waitTimer: \" + service.getUniqueId()});\n\t\t\t}\n\t\t\thandler.cancelTask(waitTimer);\n\t\t}\n\t\tif (inactivityTimer != null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"{0} : {1} ({2})\",\n\t\t\t\t\t\tnew Object[]{BoshConnectionManager.BOSH_OPERATION_TYPE.TIMER, getSid(),\n\t\t\t\t\t\t\t\t\t \"Canceling inactivityTimer: \" + service.getUniqueId()});\n\t\t\t}\n\t\t\thandler.cancelTask(inactivityTimer);\n\t\t}\n\t\tif ((packet.getElemName() == BODY_EL_NAME) && (packet.getXMLNS() == BOSH_XMLNS)) {\n\t\t\tList<Element> children = packet.getElemChildrenStaticStr(BODY_EL_PATH);\n\t\t\tboolean duplicate = false;\n\n\t\t\tif (packet.getAttributeStaticStr(RID_ATTR) != null) {\n\t\t\t\ttry {\n\t\t\t\t\tlong rid = Long.parseLong(packet.getAttributeStaticStr(RID_ATTR));\n\n\t\t\t\t\tif (isDuplicateRid(rid, children)) {\n\t\t\t\t\t\tlog.log(Level.CONFIG, \"Discovered duplicate client connection, trying to close the \" + \"old one with RID: \" +\n\t\t\t\t\t\t\t\t\t\t rid);\n\n\t\t\t\t\t\tElement body = getBodyElem();\n\n\t\t\t\t\t\tbody.setAttribute(\"type\", StanzaType.terminate.toString());\n\t\t\t\t\t\tsendBody(service, body);\n\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tservice.setRid(rid);\n\t\t\t\t\tduplicate = isDuplicateMessage(rid, children);\n\t\t\t\t\tif (!duplicate) {\n\t\t\t\t\t\tprocessRid(rid, children);\n\t\t\t\t\t}\n\t\t\t\t} catch (NumberFormatException e) {\n\t\t\t\t\tlog.warning(\"Incorrect RID value: \" + packet.getAttributeStaticStr(RID_ATTR));\n\t\t\t\t}\n\t\t\t}\n\t\t\tservice.setContentType(content_type);\n\t\t\tservice.setSid(sid);\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Setting waitTimer for \" + max_wait + \": \" + getSid());\n\t\t\t}\n\t\t\twaitTimer = handler.scheduleTask(this, max_wait * SECOND);\n\t\t\tservice.setWaitTimer(waitTimer);\n\t\t\tconnections.put(waitTimer, service);\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"{0} : {1} ({2})\",\n\t\t\t\t\t\tnew Object[]{BoshConnectionManager.BOSH_OPERATION_TYPE.TIMER, getSid(),\n\t\t\t\t\t\t\t\t\t \"Scheduling waitTimer: \" + service.getUniqueId()});\n\t\t\t}\n\t\t\tif (!duplicate) {\n\t\t\t\tif ((packet.getType() != null) && (packet.getType() == StanzaType.terminate)) {\n\n\t\t\t\t\t// We are preparing for session termination.\n\t\t\t\t\t// Some client send IQ stanzas with private data to store some\n\t\t\t\t\t// settings so some confirmation stanzas might be sent back\n\t\t\t\t\t// let's give the client a few secs for session termination\n\t\t\t\t\tmax_inactivity = 2;    // Max pause changed to 2 secs\n\t\t\t\t\tterminate = true;\n\n\t\t\t\t\tPacket command = Command.STREAM_CLOSED.getPacket(handler.getJidForBoshSession(this),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t getDataReceiver(), StanzaType.set,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t UUID.randomUUID().toString());\n\t\t\t\t\tif (userJid != null) {\n\t\t\t\t\t\tCommand.addFieldValue(command, \"user-jid\", userJid.toString());\n\t\t\t\t\t}\n\t\t\t\t\thandler.addOutStreamClosed(command, this, true);\n\n\t\t\t\t\t// out_results.offer(command);\n\t\t\t\t}\n\t\t\t\tif ((packet.getAttributeStaticStr(RESTART_ATTR) != null) &&\n\t\t\t\t\t\tpacket.getAttributeStaticStr(RESTART_ATTR).equals(\"true\")) {\n\t\t\t\t\tlog.fine(\"Found stream restart instruction: \" + packet.toString());\n\t\t\t\t\tout_results.offer(Command.GETFEATURES.getPacket(null, null, StanzaType.get, \"restart1\", null));\n\t\t\t\t}\n\t\t\t\tif (packet.getAttributeStaticStr(CACHE_ATTR) != null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tCacheAction action = CacheAction.valueOf(packet.getAttributeStaticStr(CACHE_ATTR));\n\n\t\t\t\t\t\tif (cache_on || (action == CacheAction.on)) {\n\t\t\t\t\t\t\tprocessCache(action, packet);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (IllegalArgumentException e) {\n\t\t\t\t\t\tlog.warning(\"Incorrect cache action: \" + packet.getAttributeStaticStr(CACHE_ATTR));\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (children != null) {\n\t\t\t\t\t\tfor (Element el : children) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tif (el.getXMLNS().equals(BOSH_XMLNS)) {\n\t\t\t\t\t\t\t\t\tel.setXMLNS(XMLNS_CLIENT_VAL);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tPacket result = Packet.packetInstance(el);\n\n\t\t\t\t\t\t\t\tif (filterOutPacket(result)) {\n\t\t\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\t\t\tlog.finest(\"Sending out packet: \" + result.toString());\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tout_results.offer(result);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\t\t\tlog.finest(\"Out packet filtered: \" + result.toString());\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\t\t\t\tlog.warning(\"Packet addressing problem, stringprep processing failed, dropping: \" + el);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (terminate) {\n\t\t\t\t\tPacket command = Command.STREAM_CLOSED.getPacket(handler.getJidForBoshSession(this),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t getDataReceiver(), StanzaType.set,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t UUID.randomUUID().toString());\n\t\t\t\t\tif (userJid != null) {\n\t\t\t\t\t\tCommand.addFieldValue(command, \"user-jid\", userJid.toString());\n\t\t\t\t\t}\n\t\t\t\t\thandler.addOutStreamClosed(command, this, true);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tlog.log(Level.CONFIG, \"Duplicated packet: \" + packet.toString());\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"[\" + connections.size() + \"] Unexpected packet from the network: \" + packet.toString());\n\n\t\t\tString er_msg = \"Invalid body element\";\n\n\t\t\tif (packet.getElemName() != BODY_EL_NAME) {\n\t\t\t\ter_msg += \", incorrect root element name, use \" + BODY_EL_NAME;\n\t\t\t}\n\t\t\tif (packet.getXMLNS() != BOSH_XMLNS) {\n\t\t\t\ter_msg += \", incorrect xmlns, use \" + BOSH_XMLNS;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tPacket error = Authorization.BAD_REQUEST.getResponseMessage(packet, er_msg, true);\n\n\t\t\t\tif (!waiting_packets.offer(error.getElement())) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"waiting_packets queue exceeded, dropping packet: \" + error.toString());\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tterminate = true;\n\n\t\t\t\tPacket command = Command.STREAM_CLOSED.getPacket(handler.getJidForBoshSession(this), getDataReceiver(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t StanzaType.set, UUID.randomUUID().toString());\n\t\t\t\tif (userJid != null) {\n\t\t\t\t\tCommand.addFieldValue(command, \"user-jid\", userJid.toString());\n\t\t\t\t}\n\t\t\t\thandler.addOutStreamClosed(command, this, true);\n\n\t\t\t\tcommand = Command.STREAM_FINISHED.getPacket(handler.getJidForBoshSession(this), getDataReceiver(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tStanzaType.set, UUID.randomUUID().toString());\n\t\t\t\tif (userJid != null) {\n\t\t\t\t\tCommand.addFieldValue(command, \"user-jid\", userJid.toString());\n\t\t\t\t}\n\t\t\t\thandler.addOutStreamClosed(command, this, false);\n\n\t\t\t\t// out_results.offer(command);\n\t\t\t} catch (PacketErrorTypeException e) {\n\t\t\t\tlog.log(Level.CONFIG, \"Error type and incorrect from bosh client? Ignoring...\");\n\t\t\t}\n\t\t}\n\n\t\t// Send packets waiting in queue...\n\t\tprocessPacket(null, out_results);\n\t\tif (connections.size() > hold_requests) {\n\t\t\tBoshIOService serv = connections.pollFirstEntry().getValue();\n\n\t\t\tsendBody(serv, null);\n\t\t}\n\t}\n\n\tpublic synchronized void sendWaitingPackets() {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"trying to send waiting packets from queue of \" + getSid() + \" after timer = \" +\n\t\t\t\t\t\t\t   waiting_packets.size());\n\t\t}\n\t\tif (!waiting_packets.isEmpty()) {\n\t\t\tMap.Entry<BoshTask, BoshIOService> entry = connections.pollFirstEntry();\n\n\t\t\tif (entry == null) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tBoshIOService serv = entry.getValue();\n\n\t\t\tsendBody(serv, null);\n\t\t}\n\t}\n\n\tpublic boolean task(Queue<Packet> out_results, TimerTask tt) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"task called for {0}, inactivityTimer = {1}, tt = {2}\",\n\t\t\t\t\tnew Object[]{getSid(), inactivityTimer, tt});\n\t\t}\n\t\tif (tt == inactivityTimer) {\n\t\t\tif (connections.size() > 0) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tString conns = \"\";\n\t\t\t\t\tfor (BoshIOService serv : connections.values()) {\n\t\t\t\t\t\tif (!conns.isEmpty()) {\n\t\t\t\t\t\t\tconns += \", \";\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconns += \"[\" + serv.toString() + \"]\";\n\t\t\t\t\t}\n\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"{0} : {1} ({2})\",\n\t\t\t\t\t\t\t\tnew Object[]{BoshConnectionManager.BOSH_OPERATION_TYPE.TIMER, getSid(),\n\t\t\t\t\t\t\t\t\t\t\t \"ignoring inactivityTimer\"});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"{0} : {1} ({2})\",\n\t\t\t\t\t\tnew Object[]{BoshConnectionManager.BOSH_OPERATION_TYPE.TIMER, getSid(),\n\t\t\t\t\t\t\t\t\t \"inactivityTimer fired\"});\n\t\t\t}\n\t\t\tfor (BoshTask waitTimer : waitTimerSet) {\n\t\t\t\tif (waitTimer != null) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"{0} : {1} ({2})\",\n\t\t\t\t\t\t\t\tnew Object[]{BoshConnectionManager.BOSH_OPERATION_TYPE.TIMER, getSid(),\n\t\t\t\t\t\t\t\t\t\t\t \"Canceling waitTimer\"});\n\t\t\t\t\t}\n\n\t\t\t\t\thandler.cancelTask(waitTimer);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"{0} : {1} ({2})\",\n\t\t\t\t\t\tnew Object[]{BoshConnectionManager.BOSH_OPERATION_TYPE.REMOVE, getSid(),\n\t\t\t\t\t\t\t\t\t \"Closing session, inactivity timeout expired\"});\n\t\t\t}\n\t\t\t// we need to set packetFrom as it is later used as packet from and to\n\t\t\t// pick thread on which it will be processed\n\t\t\tPacket command = Command.STREAM_CLOSED.getPacket(handler.getJidForBoshSession(this), getDataReceiver(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t StanzaType.set, UUID.randomUUID().toString());\n\t\t\tif (userJid != null) {\n\t\t\t\tCommand.addFieldValue(command, \"user-jid\", userJid.toString());\n\t\t\t}\n\n\t\t\thandler.addOutStreamClosed(command, this, true);\n\n\t\t\tfor (Element packet : waiting_packets) {\n\t\t\t\ttry {\n\n\t\t\t\t\t// Do not send stream:features back with an error\n\t\t\t\t\tif (packet.getName() != \"stream:features\") {\n//\t\t\t\t\t\tout_results.offer(Authorization.RECIPIENT_UNAVAILABLE.getResponseMessage(\n//\t\t\t\t\t\t\t\tPacket.packetInstance(packet), \"Bosh = disconnected\", true));\n\t\t\t\t\t\tPacket p = Packet.packetInstance(packet);\n\t\t\t\t\t\t// we need to set packetTo as it is later used as packet from and to\n\t\t\t\t\t\t// pick thread on which it will be processed\n\t\t\t\t\t\tp.setPacketTo(handler.getJidForBoshSession(this));\n\t\t\t\t\t\tp.setPacketFrom(getDataReceiver());\n\t\t\t\t\t\thandler.processUndeliveredPacket(p, null, \"Bosh = disconnected\");\n\t\t\t\t\t}\n\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\tlog.warning(\"Packet addressing problem, stringprep processing failed, dropping: \" + packet);\n//\t\t\t\t} catch (PacketErrorTypeException e) {\n//\t\t\t\t\tlog.log(Level.CONFIG, (\"Packet processing exception: \" + e);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcommand = Command.STREAM_FINISHED.getPacket(handler.getJidForBoshSession(this), getDataReceiver(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tStanzaType.set, UUID.randomUUID().toString());\n\t\t\tif (userJid != null) {\n\t\t\t\tCommand.addFieldValue(command, \"user-jid\", userJid.toString());\n\t\t\t}\n\n\t\t\thandler.addOutStreamClosed(command, this, false);\n\n\t\t\tcloseAllConnections();\n\n\t\t\t// out_results.offer(command);\n\t\t\treturn true;\n\t\t}\n\n\t\tBoshIOService serv = connections.remove(tt);\n\n\t\tif (serv != null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"waitTimer fired: \" + getSid());\n\t\t\t}\n\t\t\tsendBody(serv, null);\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tpublic void terminateBoshSession() {\n\t\tterminate = true;\n\t}\n\n\tpublic JID getDataReceiver() {\n\t\treturn dataReceiver;\n\t}\n\n\tpublic void setDataReceiver(JID dataReceiver) {\n\t\tthis.dataReceiver = dataReceiver;\n\t}\n\n\tpublic String getDomain() {\n\t\treturn domain;\n\t}\n\n\tpublic String getSessionId() {\n\t\treturn sessionId;\n\t}\n\n\tpublic UUID getSid() {\n\t\treturn sid;\n\t}\n\n\tpublic void setUserJid(String jid) {\n\t\tuserJid = JID.jidInstanceNS(jid);\n\t}\n\n\tprotected void init(Packet packet, BoshIOService service, long max_wait, long min_polling, long max_inactivity,\n\t\t\t\t\t\tint concurrent_requests, int hold_requests, long max_pause, int max_batch_size,\n\t\t\t\t\t\tlong batch_queue_timeout, Queue<Packet> out_results, boolean preBindEnabled) {\n\t\tString cache_action = packet.getAttributeStaticStr(CACHE_ATTR);\n\n\t\tif ((cache_action != null) && cache_action.equals(CacheAction.on.toString())) {\n\t\t\tcache = new BoshSessionCache();\n\t\t\tcache_on = true;\n\t\t\tlog.fine(\"BoshSessionCache set to ON\");\n\t\t}\n\t\thashCodes = new int[(this.concurrent_requests + 1) * 5];\n\t\tcurrentRids = new long[(this.concurrent_requests + 1) * 5];\n\t\tfor (int i = 0; i < currentRids.length; i++) {\n\t\t\tcurrentRids[i] = -1;\n\t\t\thashCodes[i] = -1;\n\t\t}\n\n\t\tlong wait_l = max_wait;\n\t\tString wait_s = packet.getAttributeStaticStr(WAIT_ATTR);\n\n\t\tif (wait_s != null) {\n\t\t\ttry {\n\t\t\t\twait_l = Long.parseLong(wait_s);\n\t\t\t} catch (NumberFormatException e) {\n\t\t\t\twait_l = max_wait;\n\t\t\t}\n\t\t}\n\t\tthis.max_wait = Math.min(wait_l, max_wait);\n\n\t\t// this.max_wait = wait_l;\n\t\tint hold_i = hold_requests;\n\t\tString tmp_str = packet.getAttributeStaticStr(HOLD_ATTR);\n\n\t\tif (tmp_str != null) {\n\t\t\ttry {\n\t\t\t\thold_i = Integer.parseInt(tmp_str);\n\t\t\t} catch (NumberFormatException e) {\n\t\t\t\thold_i = hold_requests;\n\t\t\t}\n\t\t}\n\t\ttmp_str = packet.getAttributeStaticStr(RID_ATTR);\n\t\tif (tmp_str != null) {\n\t\t\ttry {\n\t\t\t\tprevious_received_rid = Long.parseLong(tmp_str);\n\t\t\t\tcurrentRids[rids_head++] = previous_received_rid;\n\t\t\t} catch (NumberFormatException e) {\n\t\t\t}\n\t\t}\n\t\tthis.hold_requests = Math.max(hold_i, hold_requests);\n\t\tif (packet.getAttributeStaticStr(TO_ATTR) != null) {\n\t\t\tthis.domain = packet.getAttributeStaticStr(TO_ATTR);\n\t\t}\n\t\tthis.max_batch_size = max_batch_size;\n\t\tthis.batch_queue_timeout = batch_queue_timeout;\n\t\tthis.min_polling = min_polling;\n\t\tthis.max_inactivity = max_inactivity;\n\t\tthis.concurrent_requests = concurrent_requests;\n\t\tthis.max_pause = max_pause;\n\t\tif (packet.getAttributeStaticStr(CONTENT_ATTR) != null) {\n\t\t\tcontent_type = packet.getAttributeStaticStr(CONTENT_ATTR);\n\t\t}\n\n\t\tString lang = packet.getAttributeStaticStr(LANG_ATTR);\n\n\t\tif (lang == null) {\n\t\t\tlang = \"en\";\n\t\t}\n\n\t\tElement body = new Element(BODY_EL_NAME,\n\t\t\t\t\t\t\t\t   new String[]{WAIT_ATTR, INACTIVITY_ATTR, POLLING_ATTR, REQUESTS_ATTR, HOLD_ATTR,\n\t\t\t\t\t\t\t\t\t\t\t\tMAXPAUSE_ATTR, SID_ATTR, VER_ATTR, FROM_ATTR, SECURE_ATTR,\n\t\t\t\t\t\t\t\t\t\t\t\t\"xmpp:version\", \"xmlns:xmpp\", \"xmlns:stream\"},\n\t\t\t\t\t\t\t\t   new String[]{Long.toString(this.max_wait), Long.toString(this.max_inactivity),\n\t\t\t\t\t\t\t\t\t\t\t\tLong.toString(this.min_polling),\n\t\t\t\t\t\t\t\t\t\t\t\tInteger.toString(this.concurrent_requests),\n\t\t\t\t\t\t\t\t\t\t\t\tInteger.valueOf(this.hold_requests).toString(),\n\t\t\t\t\t\t\t\t\t\t\t\tLong.valueOf(this.max_pause).toString(), this.sid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\tBOSH_VERSION, this.domain, \"true\", \"1.0\", \"urn:xmpp:xbosh\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"http://etherx.jabber.org/streams\"});\n\n\t\tif (this.hostname != null) {\n\t\t\tbody.addAttribute(HOST_ATTR, this.hostname);\n\t\t}\n\n\t\tsessionId = UUID.randomUUID().toString();\n\t\tbody.setAttribute(AUTHID_ATTR, sessionId);\n\t\tif (getCurrentRidTail() > 0) {\n\t\t\tbody.setAttribute(ACK_ATTR, \"\" + takeCurrentRidTail());\n\t\t}\n\t\tJID userId = null;\n\t\ttry {\n\t\t\tuserId = (packet.getAttributeStaticStr(Packet.FROM_ATT) != null) ? JID.jidInstance(\n\t\t\t\t\tpacket.getAttributeStaticStr(Packet.FROM_ATT)) : null;\n\n\t\t\tif (userId != null) {\n\t\t\t\tBareJID hostJid = handler.getSeeOtherHostForJID(packet, userId.getBareJID(), Phase.OPEN);\n\n\t\t\t\tif (hostJid != null) {\n\t\t\t\t\tElement error = new Element(\"stream:error\");\n\t\t\t\t\tElement seeOtherHost = handler.getSeeOtherHostError(packet, hostJid);\n\n\t\t\t\t\tseeOtherHost.setXMLNS(\"urn:ietf:params:xml:ns:xmpp-streams\");\n\t\t\t\t\terror.addChild(seeOtherHost);\n\t\t\t\t\tbody.addChild(error);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (TigaseStringprepException ex) {\n\t\t\tLogger.getLogger(BoshSession.class.getName()).log(Level.SEVERE, null, ex);\n\t\t}\n\t\tbody.setXMLNS(BOSH_XMLNS);\n\t\t// we are just doing session pre-bind, ignore sending back\n\n\t\t// service.writeRawData(body.toString());\n\t\tPacket streamOpen = Command.STREAM_OPENED.getPacket(handler.getJidForBoshSession(this), null, StanzaType.set,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tUUID.randomUUID().toString(), Command.DataType.submit);\n\n\t\tCommand.addFieldValue(streamOpen, SESSION_ID_ATTR, sessionId);\n\t\tCommand.addFieldValue(streamOpen, \"hostname\", domain);\n\t\tCommand.addFieldValue(streamOpen, LANG_ATTR, lang);\n\n\t\tif (null != service) {\n\t\t\tservice.setContentType(content_type);\n\t\t\tsendBody(service, body);\n\t\t}\n\t\tif (preBindEnabled) {\n\n\t\t\tinactivityTimer = handler.scheduleTask(this, max_inactivity * SECOND);\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"{0} : {1} ({2})\",\n\t\t\t\t\t\tnew Object[]{BoshConnectionManager.BOSH_OPERATION_TYPE.TIMER, getSid(),\n\t\t\t\t\t\t\t\t\t \"Setting inactivityTimer for \" + max_inactivity + \" on \" + service});\n\t\t\t}\n\n\t\t\tif (null != userId) {\n\t\t\t\tCommand.addFieldValue(streamOpen, USER_ID_ATTR, userId.toString());\n\t\t\t}\n\t\t\tString ridString = packet.getAttributeStaticStr(RID_ATTR);\n\t\t\tif (null != ridString) {\n\t\t\t\tlong rid = Long.valueOf(ridString);\n\t\t\t\tprocessRid(rid, null);\n\t\t\t}\n\t\t\tCommand.addFieldValue(streamOpen, PRE_BIND_ATTR, String.valueOf(preBindEnabled));\n\t\t}\n\n\t\thandler.addOutStreamOpen(streamOpen, this);\n\n\t\t// out_results.offer(streamOpen);\n\t}\n\n\tprivate Element applyFilters(Element packet) {\n\t\tElement result = packet.clone();\n\n\t\tif (result.getName() == MESSAGE_ELEMENT_NAME) {\n\t\t\tString body = result.getCDataStaticStr(Message.MESSAGE_BODY_PATH);\n\n\t\t\tif (body != null) {\n\t\t\t\tint count = 0;\n\n\t\t\t\t// for (Pattern reg: links_regexs) {\n\t\t\t\t// body = reg.matcher(body).replaceAll(replace_with[count++]);\n\t\t\t\t// }\n\t\t\t\tresult.getChild(\"body\").setCData(body);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate void closeAllConnections() {\n\t\tfor (BoshIOService conn : old_connections) {\n\t\t\tconn.stop();\n\t\t}\n\t\tfor (BoshIOService conn : connections.values()) {\n\t\t\tconn.stop();\n\t\t}\n\t}\n\n\tprivate boolean filterInPacket(Packet packet) {\n\t\tif (cache_on) {\n\t\t\tprocessAutomaticCache(packet);\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprivate boolean filterOutPacket(Packet packet) {\n\t\tif (cache_on && (packet.getElemName() == MESSAGE_ELEMENT_NAME)) {\n\t\t\tcache.addToMessage(packet.getElement());\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprivate void processAutomaticCache(Packet packet) {\n\t\tif (packet.getElemName() == PRESENCE_ELEMENT_NAME) {\n\t\t\tcache.addPresence(packet.getElement());\n\t\t}\n\t\tif (packet.getElemName() == MESSAGE_ELEMENT_NAME) {\n\t\t\tcache.addFromMessage(packet.getElement());\n\t\t}\n\t\tif (packet.isXMLNSStaticStr(Iq.IQ_QUERY_PATH, \"jabber:iq:roster\")) {\n\t\t\tcache.addRoster(packet.getElement());\n\t\t}\n\t\tif (packet.isXMLNSStaticStr(Iq.IQ_BIND_PATH, \"urn:ietf:params:xml:ns:xmpp-bind\")) {\n\t\t\tcache.set(BoshSessionCache.RESOURCE_BIND_ID, Collections.singletonList(packet.getElement()));\n\t\t}\n\t}\n\n\tprivate void processCache(CacheAction action, Packet packet) {\n\t\t++cache_reload_counter;\n\n\t\tint packet_counter = 0;\n\t\tList<Element> children = packet.getElemChildrenStaticStr(BODY_EL_PATH);\n\t\tString cache_id = packet.getAttributeStaticStr(CACHE_ID_ATTR);\n\t\tList<Element> cache_res = null;\n\n\t\tswitch (action) {\n\t\t\tcase on:\n\t\t\t\tif (cache == null) {\n\t\t\t\t\tcache = new BoshSessionCache();\n\t\t\t\t}\n\t\t\t\tcache_on = true;\n\t\t\t\tlog.fine(\"BoshSessionCache set to ON\");\n\n\t\t\t\tbreak;\n\n\t\t\tcase off:\n\t\t\t\tcache_on = false;\n\t\t\t\tlog.fine(\"BoshSessionCache set to OFF\");\n\n\t\t\t\tbreak;\n\n\t\t\tcase set:\n\t\t\t\tcache.set(cache_id, children);\n\n\t\t\t\tbreak;\n\n\t\t\tcase add:\n\t\t\t\tcache.add(cache_id, children);\n\n\t\t\t\tbreak;\n\n\t\t\tcase get:\n\t\t\t\tcache_res = cache.get(cache_id);\n\n\t\t\t\tbreak;\n\n\t\t\tcase remove:\n\t\t\t\tcache.remove(cache_id);\n\n\t\t\t\tbreak;\n\n\t\t\tcase get_all:\n\t\t\t\tcache_res = cache.getAll();\n\t\t\t\tretireAllOldConnections();\n\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tlog.warning(\"Unknown cache action: \" + action.toString());\n\n\t\t\t\tbreak;\n\t\t}\n\t\tif (cache_res != null) {\n\t\t\tfor (Element elem : cache_res) {\n\t\t\t\telem.addAttribute(\"reload-counter\", \"\" + cache_reload_counter);\n\t\t\t\telem.addAttribute(\"packet-counter\", \"\" + (++packet_counter));\n\t\t\t\tif (!waiting_packets.offer(elem)) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"waiting_packets queue exceeded, dropping packet: \" + elem.toString());\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void processRid(long rid, List<Element> packets) {\n\t\tsynchronized (currentRids) {\n\t\t\tif ((previous_received_rid + 1) != rid) {\n\t\t\t\tlog.log(Level.FINER, \"Incorrect packet order, last_rid={0}, current_rid={1}\",\n\t\t\t\t\t\tnew Object[]{previous_received_rid, rid});\n\t\t\t}\n\t\t\tif ((packets != null) && (!packets.isEmpty())) {\n\t\t\t\tStringBuilder sb = new StringBuilder();\n\n\t\t\t\tfor (Element elem : packets) {\n\t\t\t\t\tsb.append(elem.toString());\n\t\t\t\t}\n\t\t\t\thashCodes[rids_head] = sb.toString().hashCode();\n\t\t\t} else {\n\t\t\t\thashCodes[rids_head] = -1;\n\t\t\t}\n\t\t\tprevious_received_rid = rid;\n\t\t\tcurrentRids[rids_head++] = rid;\n\t\t\tif (rids_head >= currentRids.length) {\n\t\t\t\trids_head = 0;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void retireAllOldConnections() {\n\t\twhile (connections.size() > 1) {\n\t\t\tMap.Entry<BoshTask, BoshIOService> entry = connections.pollFirstEntry();\n\n\t\t\thandler.cancelTask(entry.getKey());\n\n\t\t\tBoshIOService serv = entry.getValue();\n\n\t\t\tif (serv != null) {\n\t\t\t\tretireConnectionService(serv);\n\t\t\t} else {\n\t\t\t\tif (log.isLoggable(Level.WARNING)) {\n\t\t\t\t\tlog.warning(\"connections queue size is greater than 1 but poll returns null\" + getSid());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void retireConnectionService(BoshIOService serv) {\n\t\tif (!old_connections.contains(serv)) {\n\t\t\twhile (!old_connections.offer(serv)) {\n\t\t\t\tBoshIOService old_serv = old_connections.poll();\n\n\t\t\t\tif (old_serv != null) {\n\t\t\t\t\told_serv.stop();\n\t\t\t\t} else {\n\t\t\t\t\tif (log.isLoggable(Level.WARNING)) {\n\t\t\t\t\t\tlog.warning(\"old_connections queue is empty but can not add new element!: \" + getSid());\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tserv.setSid(null);\n\t\tdisconnected(serv);\n\t}\n\n\tprivate synchronized void sendBody(BoshIOService serv, Element body_par) {\n\t\tif (queueTask != null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Canceling queue timer: \" + getSid());\n\t\t\t}\n\t\t\thandler.cancelSendQueueTask(queueTask);\n\t\t\tqueueTask = null;\n\t\t}\n\t\tlast_send_time = System.currentTimeMillis();\n\n\t\tBoshTask timer = serv.getWaitTimer();\n\n\t\tif (timer != null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Canceling waitTimer: \" + getSid());\n\t\t\t}\n\t\t\thandler.cancelTask(timer);\n\t\t} else {\n\t\t\tlog.fine(\"No waitTimer for the Bosh connection! \" + serv);\n\t\t}\n\n\t\tElement body = body_par;\n\n\t\tif (body == null) {\n\t\t\tbody = getBodyElem();\n\n\t\t\tlong rid = takeCurrentRidTail();\n\n\t\t\tif (rid > 0) {\n\t\t\t\tbody.setAttribute(ACK_ATTR, \"\" + rid);\n\t\t\t}\n\t\t\tif (!waiting_packets.isEmpty()) {\n\n\t\t\t\t// body.addChild(applyFilters(waiting_packets.poll()));\n\t\t\t\t// Make sure the XMLNS is set correctly for all stanzas to avoid\n\t\t\t\t// namespace confusion:\n\t\t\t\t// http://forum.ag-software.de/thread/969\n\t\t\t\tElement stanza = waiting_packets.poll();\n\n\t\t\t\tif (stanza.getXMLNS() == null) {\n\t\t\t\t\tstanza.setXMLNS(XMLNS_CLIENT_VAL);\n\t\t\t\t}\n\t\t\t\tbody.addChild(stanza);\n\t\t\t\twhile ((!waiting_packets.isEmpty()) && (body.getChildren().size() < max_batch_size)) {\n\n\t\t\t\t\t// body.addChild(applyFilters(waiting_packets.poll()));\n\t\t\t\t\tstanza = waiting_packets.poll();\n\t\t\t\t\tif (stanza.getXMLNS() == null) {\n\t\t\t\t\t\tstanza.setXMLNS(XMLNS_CLIENT_VAL);\n\t\t\t\t\t}\n\t\t\t\t\tbody.addChild(stanza);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (body.getChild(\"stream:error\") != null) {\n\t\t\tbody.addAttribute(\"condition\", \"remote-stream-error\");\n\t\t\tbody.addAttribute(\"type\", \"terminate\");\n\t\t\tbody.addAttribute(\"xmlns:stream\", \"http://etherx.jabber.org/streams\");\n\t\t\tthis.terminate = true;\n\t\t}\n\n\t\ttry {\n\t\t\tif (terminate) {\n\t\t\t\tbody.setAttribute(\"type\", StanzaType.terminate.toString());\n\t\t\t}\n\t\t\thandler.writeRawData(serv, body.toString());\n\t\t\tretireConnectionService(serv);\n\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"[\" + connections.size() + \"] Exception during writing to socket\", e);\n\t\t}\n\t}\n\n\tprivate long takeCurrentRidTail() {\n\t\tsynchronized (currentRids) {\n\t\t\tint idx = rids_tail++;\n\n\t\t\tif (rids_tail >= currentRids.length) {\n\t\t\t\trids_tail = 0;\n\t\t\t}\n\n\t\t\treturn currentRids[idx];\n\t\t}\n\t}\n\n\tprivate Element getBodyElem() {\n\t\tElement body = new Element(BODY_EL_NAME,\n\t\t\t\t\t\t\t\t   new String[]{FROM_ATTR, SECURE_ATTR, \"xmpp:version\", \"xmlns:xmpp\", \"xmlns:stream\"},\n\t\t\t\t\t\t\t\t   new String[]{this.domain, \"true\", \"1.0\", \"urn:xmpp:xbosh\",\n\t\t\t\t\t\t\t\t\t\t\t\t\"http://etherx.jabber.org/streams\"});\n\n\t\tif (this.hostname != null) {\n\t\t\tbody.addAttribute(HOST_ATTR, this.hostname);\n\t\t}\n\n\t\tbody.setXMLNS(BOSH_XMLNS);\n\n\t\treturn body;\n\t}\n\n\tprivate long getCurrentRidTail() {\n\t\tsynchronized (currentRids) {\n\t\t\treturn currentRids[rids_tail];\n\t\t}\n\t}\n\n\tprivate boolean isDuplicateMessage(long rid, List<Element> packets) {\n\t\tsynchronized (currentRids) {\n\t\t\tint hashCode = -1;\n\n\t\t\tif ((packets != null) && (!packets.isEmpty())) {\n\t\t\t\tStringBuilder sb = new StringBuilder();\n\n\t\t\t\tfor (Element elem : packets) {\n\t\t\t\t\tsb.append(elem.toString());\n\t\t\t\t}\n\t\t\t\thashCode = sb.toString().hashCode();\n\t\t\t}\n\t\t\tif (hashCode == -1) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tfor (int i = 0; i < currentRids.length; ++i) {\n\t\t\t\tif (rid == currentRids[i]) {\n\t\t\t\t\treturn hashCode == hashCodes[i];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate boolean isDuplicateRid(long rid, List<Element> packets) {\n\t\tsynchronized (currentRids) {\n\t\t\tint hashCode = -1;\n\n\t\t\tif ((packets != null) && (!packets.isEmpty())) {\n\t\t\t\tStringBuilder sb = new StringBuilder();\n\n\t\t\t\tfor (Element elem : packets) {\n\t\t\t\t\tsb.append(elem.toString());\n\t\t\t\t}\n\t\t\t\thashCode = sb.toString().hashCode();\n\t\t\t}\n\t\t\tfor (int i = 0; i < currentRids.length; ++i) {\n\t\t\t\tif (rid == currentRids[i]) {\n\t\t\t\t\treturn hashCode != hashCodes[i];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate static class TimerTaskComparator\n\t\t\timplements Comparator<BoshTask> {\n\n\t\t@Override\n\t\tpublic int compare(BoshTask o1, BoshTask o2) {\n\t\t\tif (o1.timerOrder > o2.timerOrder) {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif (o1.timerOrder < o2.timerOrder) {\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\treturn 0;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/bosh/BoshSessionCache.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.bosh;\n\nimport tigase.server.Iq;\nimport tigase.server.Message;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\n\nimport java.text.SimpleDateFormat;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Describe class BoshSessionCache here.\n * <br>\n * Created: Mon Feb 25 23:54:57 2008\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class BoshSessionCache {\n\n\tpublic static final String DEF_ID = \"\";\n\n\tpublic static final String MESSAGE_ID = \"bosh-message\";\n\n\tpublic static final String RESOURCE_BIND_ID = \"bosh-resource-bind\";\n\n\tpublic static final String ROSTER_ID = \"bosh-roster\";\n\tprivate static final Logger log = Logger.getLogger(\"tigase.server.bosh.BoshSessionCache\");\n\tprivate static final SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss.SSSZ\");\n\n\t/**\n\t * Cached time of the first message to/from some jid to speedup message caching processing\n\t */\n\tprotected Map<String, Long> jid_msg_start = null;\n\n\t/**\n\t * Cache elements stored by the Bosh client. The cache elements are grouped by IDs. There can be any number of\n\t * Elements under each ID.\n\t */\n\tprivate Map<String, List<Element>> id_cache = null;\n\n\t/**\n\t * Cached presence elements automaticaly stored by the Bosh component. There is only 1 presence element stored for\n\t * each JID which means the cache stores the last presence element for each JID.\n\t */\n\tprivate Map<String, Element> jid_presence = null;\n\n\t/**\n\t * Creates a new <code>BoshSessionCache</code> instance.\n\t */\n\tpublic BoshSessionCache() {\n\t\tid_cache = new LinkedHashMap<String, List<Element>>();\n\t\tjid_presence = new LinkedHashMap<String, Element>();\n\t\tjid_msg_start = new LinkedHashMap<String, Long>();\n\t}\n\n\tpublic void add(String id, List<Element> data) {\n\t\tif (id == null) {\n\t\t\tid = DEF_ID;\n\t\t}\n\n\t\tList<Element> cached_data = id_cache.get(id);\n\n\t\tif (cached_data == null) {\n\t\t\tcached_data = new ArrayList<Element>();\n\t\t\tid_cache.put(id, cached_data);\n\t\t}\n\t\tcached_data.addAll(data);\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"ADD, id = \" + id + \", DATA: \" + data.toString());\n\t\t}\n\t}\n\n\tpublic void addFromMessage(Element message) {\n\t\tElement body = message.findChildStaticStr(Message.MESSAGE_BODY_PATH);\n\n\t\tif (body == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tString jid = message.getAttributeStaticStr(Packet.FROM_ATT);\n\n\t\taddMsgBody(jid, Packet.FROM_ATT, body);\n\t}\n\n\tpublic void addPresence(Element presence) {\n\t\tString from = presence.getAttributeStaticStr(Packet.FROM_ATT);\n\n\t\tjid_presence.put(from, presence);\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"ADD_PRESENCE, from = \" + from + \", PRESENCE: \" + presence.toString());\n\t\t}\n\t}\n\n\tpublic void addRoster(Element roster) {\n\n\t\t// Pushing roster with 'result' packet type will not work\n\t\tElement roster_mod = roster.clone();\n\n\t\troster_mod.setAttribute(Packet.TYPE_ATT, \"set\");\n\t\tadd(ROSTER_ID, Arrays.asList(roster_mod));\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"ADD_ROSTER, ROSTER: \" + roster_mod.toString());\n\t\t}\n\t}\n\n\tpublic void addToMessage(Element message) {\n\t\tElement body = message.findChildStaticStr(Message.MESSAGE_BODY_PATH);\n\n\t\tif (body == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tString jid = message.getAttributeStaticStr(Packet.TO_ATT);\n\n\t\taddMsgBody(jid, Packet.TO_ATT, body);\n\t}\n\n\tpublic List<Element> get(String id) {\n\t\tif (id == null) {\n\t\t\tid = DEF_ID;\n\t\t}\n\n\t\tList<Element> data = id_cache.get(id);\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"GET, id = \" + id + \", DATA: \" + data.toString());\n\t\t}\n\n\t\treturn data;\n\t}\n\n\tpublic List<Element> getAll() {\n\t\tList<Element> result = new ArrayList<Element>();\n\n\t\tfor (List<Element> cache_data : id_cache.values()) {\n\t\t\tresult.addAll(cache_data);\n\t\t}\n\t\tresult.addAll(jid_presence.values());\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"GET_ALL, DATA: \" + result.toString());\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic List<Element> getAllPresences() {\n\t\treturn new ArrayList<Element>(jid_presence.values());\n\t}\n\n\tpublic List<Element> getPresence(String... from) {\n\t\tList<Element> result = new ArrayList<Element>();\n\n\t\tfor (String f : from) {\n\t\t\tElement presence = jid_presence.get(f);\n\n\t\t\tif (presence != null) {\n\t\t\t\tresult.add(presence);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic List<Element> remove(String id) {\n\t\tif (id == null) {\n\t\t\tid = DEF_ID;\n\t\t}\n\n\t\tList<Element> data = id_cache.remove(id);\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"REMOVED, id = \" + id + \", DATA: \" + data.toString());\n\t\t}\n\n\t\treturn data;\n\t}\n\n\tpublic void set(String id, List<Element> data) {\n\t\tif (id == null) {\n\t\t\tid = DEF_ID;\n\t\t}\n\n\t\tList<Element> cached_data = new ArrayList<Element>();\n\n\t\tid_cache.put(id, cached_data);\n\t\tcached_data.addAll(data);\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"SET, id = \" + id + \", DATA: \" + data.toString());\n\t\t}\n\t}\n\n\tprivate void addMsgBody(String jid, String direction, Element body) {\n\t\tlong start_time = getMsgStartTime(jid);\n\t\tList<Element> msg_history_l = id_cache.get(MESSAGE_ID + jid);\n\t\tElement msg_history = null;\n\n\t\tif (msg_history_l == null) {\n\t\t\tmsg_history = createMessageHistory(jid);\n\t\t\tadd(MESSAGE_ID + jid, Arrays.asList(msg_history));\n\t\t} else {\n\t\t\tmsg_history = msg_history_l.get(0);\n\t\t}\n\n\t\tlong current_secs = (System.currentTimeMillis() / 1000) - start_time;\n\n\t\tmsg_history.findChildStaticStr(Iq.IQ_CHAT_PATH)\n\t\t\t\t.addChild(new Element(direction, new Element[]{body}, new String[]{\"secs\"},\n\t\t\t\t\t\t\t\t\t  new String[]{\"\" + current_secs}));\n\t}\n\n\tprivate Element createMessageHistory(String jid) {\n\t\tString sdf_string = null;\n\n\t\tsynchronized (sdf) {\n\t\t\tsdf_string = sdf.format(new Date());\n\t\t}\n\n\t\treturn new Element(\"iq\", new Element[]{new Element(\"chat\", new String[]{\"xmlns\", \"with\", \"start\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{\"urn:xmpp:tmp:archive\", jid, sdf_string})},\n\t\t\t\t\t\t   new String[]{\"type\", \"id\"}, new String[]{\"set\", \"\" + System.currentTimeMillis()});\n\t}\n\n\tprivate long getMsgStartTime(String jid) {\n\t\tLong start_time = jid_msg_start.get(jid);\n\n\t\tif (start_time == null) {\n\t\t\tstart_time = (System.currentTimeMillis() / 1000);\n\t\t\tjid_msg_start.put(jid, start_time);\n\t\t}\n\n\t\treturn start_time;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/bosh/BoshSessionTaskHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.bosh;\n\nimport tigase.server.Packet;\nimport tigase.server.xmppclient.SeeOtherHostIfc;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\n/**\n * Describe interface BoshSessionTaskHandler here.\n * <br>\n * Created: Sat Aug  4 10:39:21 2007\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface BoshSessionTaskHandler {\n\n\tJID getJidForBoshSession(BoshSession bs);\n\n\tBoshTask scheduleTask(BoshSession bs, long delay);\n\n\tBoshSendQueueTask scheduleSendQueueTask(BoshSession tt, long delay);\n\n\tvoid cancelTask(BoshTask bs);\n\n\tvoid cancelSendQueueTask(BoshSendQueueTask bt);\n\n\tvoid writeRawData(BoshIOService ios, String data);\n\n\tboolean addOutStreamOpen(Packet packet, BoshSession bs);\n\n\tboolean addOutStreamClosed(Packet packet, BoshSession bs, boolean withTimeout);\n\n\tBareJID getDefHostName();\n\n\tBareJID getSeeOtherHostForJID(Packet packet, BareJID userId, SeeOtherHostIfc.Phase ph);\n\n\tElement getSeeOtherHostError(Packet packet, BareJID destination);\n\n\tboolean processUndeliveredPacket(Packet packet, Long stamp, String errorMessage);\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/bosh/BoshSidLoggerFilter.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.bosh;\n\nimport tigase.server.bosh.BoshConnectionManager.BOSH_OPERATION_TYPE;\n\nimport java.util.logging.Filter;\nimport java.util.logging.LogRecord;\n\n/**\n * @author Wojciech Kapcia\n */\n\npublic class BoshSidLoggerFilter\n\t\timplements Filter {\n\n\t@Override\n\tpublic boolean isLoggable(LogRecord record) {\n\t\tboolean matchTracker = false;\n\n\t\tObject[] parameters = record.getParameters();\n\t\tif (parameters != null && parameters.length > 0 && parameters[0] != null &&\n\t\t\t\tBOSH_OPERATION_TYPE.forName(parameters[0].toString()) != null) {\n\t\t\tmatchTracker = true;\n\t\t}\n\t\treturn matchTracker;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/bosh/BoshTask.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.bosh;\n\nimport tigase.server.Packet;\nimport tigase.util.common.TimerTask;\n\nimport java.util.ArrayDeque;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Describe class BoshWaitTimer here.\n * <br>\n * Created: Tue Oct 30 16:38:15 2012\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n * @version $Rev: $\n */\npublic class BoshTask\n\t\textends TimerTask {\n\n\tprivate static final Logger log = Logger.getLogger(BoshTask.class.getName());\n\n\tprotected long timerOrder = System.currentTimeMillis();\n\tprivate BoshSession bs = null;\n\tprivate BoshConnectionManager manager = null;\n\n\n\tpublic BoshTask(BoshSession bs, BoshConnectionManager manager) {\n\t\tthis.bs = bs;\n\t\tthis.manager = manager;\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\tQueue<Packet> out_results = new ArrayDeque<Packet>();\n\n\t\tif (bs.task(out_results, this)) {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.finer(\"Closing session for BS task: \" + bs.getSid());\n\t\t\t}\n\t\t\tmanager.sessions.remove(bs.getSid());\n\t\t}\n\n\t\tmanager.addOutPackets(out_results, bs);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/bosh/Constants.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.bosh;\n\nimport java.util.logging.Level;\n\n/**\n * Describe class Constants here.\n * <br>\n * Created: Tue Jun  5 22:22:09 2007\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic abstract class Constants {\n\n\tpublic static final String BOSH_VERSION = \"1.6\";\n\n\tprotected static final String ACCEPT_ATTR = \"accept\";\n\n\tprotected static final String ACK_ATTR = \"ack\";\n\n\tprotected static final String AUTHID_ATTR = \"authid\";\n\n\tprotected static final String BATCH_QUEUE_TIMEOUT_KEY = \"batch-queue-timeout\";\n\n\tprotected static final long BATCH_QUEUE_TIMEOUT_VAL = 100;\n\n\tprotected static final String BODY_EL_NAME = \"body\";\n\n\tprotected static final String[] BODY_EL_PATH = {\"body\"};\n\n\tprotected static final long BOSH_SESSION_CLOSE_DELAY_DEF_VAL = 0;\n\n\tprotected static final String BOSH_SESSION_CLOSE_DELAY_PROP_KEY = \"bosh-session-close-delay\";\n\n\tprotected static final String BOSH_XMLNS = \"http://jabber.org/protocol/httpbind\";\n\n\tprotected static final String CACHE_ATTR = \"cache\";\n\n\tprotected static final String CACHE_ID_ATTR = \"cache-id\";\n\n\tprotected static final String CHARSETS_ATTR = \"charsets\";\n\n\tprotected static final String CONCURRENT_REQUESTS_PROP_KEY = \"concurrent-requests\";\n\n\tprotected static final int CONCURRENT_REQUESTS_PROP_VAL = 2;\n\n\tprotected static final String CONTENT_ATTR = \"content\";\n\n\tprotected static final String CONTENT_TYPE_DEF = \"text/xml; charset=utf-8\";\n\n\tprotected static final String FROM_ATTR = \"from\";\n\n\tprotected static final String HOLD_ATTR = \"hold\";\n\n\tprotected static final String HOLD_REQUESTS_PROP_KEY = \"hold-requests\";\n\n\tprotected static final int HOLD_REQUESTS_PROP_VAL = 1;\n\n\t/**\n\t * Name of custom attribute to pass name of host to which BOSH connection is established\n\t */\n\tprotected static final String HOST_ATTR = \"host\";\n\n\tprotected static final String INACTIVITY_ATTR = \"inactivity\";\n\n\tprotected static final String LANG_ATTR = \"xml:lang\";\n\n\tprotected static final String PRE_BIND_ATTR = \"prebind\";\n\tprotected static final String SESSION_ID_ATTR = \"session-id\";\n\tprotected static final String USER_ID_ATTR = \"jid\";\n\n\tprotected static final String MAX_BATCH_SIZE_KEY = \"max-batch-size\";\n\n\tprotected static final String MAX_INACTIVITY_PROP_KEY = \"max-inactivity\";\n\n\tprotected static final long MAX_INACTIVITY_PROP_VAL = 10;\n\n\tprotected static final int MAX_PACKETS = 15;\n\n\tprotected static final String MAX_PAUSE_PROP_KEY = \"max-pause\";\n\n\tprotected static final long MAX_PAUSE_PROP_VAL = 10;\n\n\tprotected static final String MAX_SESSION_WAITING_PACKETS_KEY = \"max-session-waiting-packets\";\n\n\tprotected static final int MAX_SESSION_WAITING_PACKETS_VAL = 100;\n\n\tprotected static final String MAX_WAIT_DEF_PROP_KEY = \"max-wait\";\n\n\tprotected static final long MAX_WAIT_DEF_PROP_VAL = 30;\n\n\tprotected static final String MAXPAUSE_ATTR = \"maxpause\";\n\n\tprotected static final String MIN_POLLING_PROP_KEY = \"min-polling\";\n\n\tprotected static final long MIN_POLLING_PROP_VAL = 10;\n\n\tprotected static final String POLLING_ATTR = \"polling\";\n\n\tprotected static final String REQUESTS_ATTR = \"requests\";\n\n\tprotected static final String RESTART_ATTR = \"xmpp:restart\";\n\n\tprotected static final String RID_ATTR = \"rid\";\n\n\tprotected static final String ROUTE_ATTR = \"route\";\n\n\tprotected static final String SECURE_ATTR = \"secure\";\n\n\tprotected static final String SEND_NODE_HOSTNAME_KEY = \"send-node-hostname\";\n\n\tprotected static final boolean SEND_NODE_HOSTNAME_VAL = true;\n\n\tprotected static final String SID_ATTR = \"sid\";\n\n\tprotected static final String TO_ATTR = \"to\";\n\n\tprotected static final String VER_ATTR = \"ver\";\n\n\tprotected static final String WAIT_ATTR = \"wait\";\n\n\tprotected static final String XMLNS_CLIENT_VAL = \"jabber:client\";\n\n\tprotected static final int MAX_BATCH_SIZE_VAL = MAX_PACKETS;\n\n\tprotected static final String SID_LOGGER_KEY = \"sid-logger-level\";\n\tprotected static final String SID_LOGGER_VAL = Level.OFF.toString();\n\n\tprotected enum CacheAction {\n\t\ton,\n\t\toff,\n\t\tset,\n\t\tadd,\n\t\tget,\n\t\tget_all,\n\t\tremove;\n\t}\n\n//protected static final String CACHE_ON = \"on\";\n//protected static final String CACHE_OFF = \"off\";\n//protected static final String CACHE_SET = \"set\";\n//protected static final String CACHE_ADD = \"add\";\n//protected static final String CACHE_GET = \"get\";\n//protected static final String CACHE_GET_ALL = \"get-all\";\n//protected static final String CACHE_REMOVE = \"remove\";\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/AbstractCompDBRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext;\n\nimport tigase.db.DBInitException;\nimport tigase.db.comp.UserRepoRepository;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.AbstractMessageReceiver;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Oct 24, 2009 3:55:41 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\npublic class AbstractCompDBRepository\n\t\textends UserRepoRepository<CompRepoItem> {\n\n\tpublic static final String ITEMS_IMPORT_FILE = \"etc/externalComponentItems\";\n\n\tprivate static final Logger log = Logger.getLogger(AbstractCompDBRepository.class.getCanonicalName());\n\n\t@Inject(bean = \"service\")\n\tprivate AbstractMessageReceiver component;\n\n\t@ConfigField(desc = \"ID of the external components group\", alias = \"external-components-group\")\n\tprivate String extenalComponentsGroup;\n\n\tprotected AbstractCompDBRepository(String extenalComponentsGroup) {\n\t\tthis.extenalComponentsGroup = extenalComponentsGroup;\n\t\tthis.autoReloadInterval = 30;\n\t}\n\n\t@Override\n\tpublic void destroy() {\n\t\t// Nothing to destroy here\n\t}\n\n\t@Override\n\tpublic String getConfigKey() {\n\t\treturn CompRepoDefaults.getConfigKey();\n\t}\n\n\t@Override\n\tpublic String[] getDefaultPropetyItems() {\n\t\treturn CompRepoDefaults.getDefaultPropetyItems();\n\t}\n\n\t@Override\n\tpublic CompRepoItem getItemInstance() {\n\t\treturn CompRepoDefaults.getItemInstance();\n\t}\n\n\t@Override\n\tpublic String getItemsListPKey() {\n\t\treturn extenalComponentsGroup;\n\t}\n\n\t@Override\n\tpublic String getPropertyKey() {\n\t\treturn CompRepoDefaults.getPropertyKey();\n\t}\n\n\t@Override\n\tpublic BareJID getRepoUser() {\n\t\treturn CompRepoDefaults.getRepoUser();\n\t}\n\n\t@Deprecated\n\t@Override\n\tpublic void initRepository(String resource_uri, Map<String, String> params) throws DBInitException {\n\t\t// Nothing to do here\n\t}\n\n\t@Override\n\tpublic String validateItem(CompRepoItem item) {\n\t\tString result = super.validateItem(item);\n\t\tif (result == null) {\n\t\t\tresult = item.validate();\n\t\t}\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tloadItemsFromFile();\n\t\tsuper.initialize();\n\t}\n\n\tpublic void loadItemsFromFile() {\n\t\tFile f = new File(ITEMS_IMPORT_FILE);\n\t\tif (f.exists()) {\n\t\t\ttry (BufferedReader reader = new BufferedReader(new FileReader(f))) {\n\t\t\t\treader.lines()\n\t\t\t\t\t\t.flatMap(list -> Arrays.stream(list.split(\",\")))\n\t\t\t\t\t\t.map(this::newItemFromPropertyString)\n\t\t\t\t\t\t.forEach(this::addItemNoStore);\n\t\t\t\tf.delete();\n\t\t\t} catch (IOException ex) {\n\t\t\t\tlog.log(Level.WARNING, \"could not load external component items from the import file\", ex);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate CompRepoItem newItemFromPropertyString(String str) {\n\t\tCompRepoItem item = this.getItemInstance();\n\t\titem.initFromPropertyString(str);\n\t\treturn item;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/CompCompDBRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\n\n@Bean(name = \"repository\", parent = ComponentProtocol.class, active = true)\n@ConfigType({ConfigTypeEnum.ComponentMode})\npublic class CompCompDBRepository extends AbstractCompDBRepository {\n\n\tpublic CompCompDBRepository() {\n\t\tsuper(\"component-default-items-list\");\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/CompConfigRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.DBInitException;\nimport tigase.db.comp.ConfigRepository;\n\nimport java.util.Map;\n\n/**\n * Created: Oct 3, 2009 2:00:30 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Deprecated\n@TigaseDeprecated(since=\"8.0.0\")\npublic class CompConfigRepository\n\t\textends ConfigRepository<CompRepoItem> {\n\n\t@Override\n\tpublic void destroy() {\n\t\t// Nothing to do\n\t}\n\n\t@Override\n\tpublic String[] getDefaultPropetyItems() {\n\t\treturn CompRepoDefaults.getDefaultPropetyItems();\n\t}\n\n\t@Override\n\tpublic String getPropertyKey() {\n\t\treturn CompRepoDefaults.getPropertyKey();\n\t}\n\n\t@Override\n\tpublic String getConfigKey() {\n\t\treturn CompRepoDefaults.getConfigKey();\n\t}\n\n\t@Override\n\tpublic CompRepoItem getItemInstance() {\n\t\treturn CompRepoDefaults.getItemInstance();\n\t}\n\n\t@Deprecated\n\t@Override\n\tpublic void initRepository(String resource_uri, Map<String, String> params) throws DBInitException {\n\t\t// Nothing to do\n\t}\n\n\t@Override\n\tpublic String validateItem(CompRepoItem item) {\n\t\tString result = super.validateItem(item);\n\t\tif (result == null) {\n\t\t\tresult = item.validate();\n\t\t}\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/CompRepoDefaults.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext;\n\nimport tigase.xmpp.jid.BareJID;\n\n/**\n * Created: Oct 24, 2009 3:57:36 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic abstract class CompRepoDefaults {\n\n\tprivate static final BareJID comp_user = BareJID.bareJIDInstanceNS(\"ext-comp-manager\");\n\tprivate static final String comp_list_pkey = \"ext-comp-lists\";\n\n\tpublic static String getConfigKey() {\n\t\treturn \"comp-items\";\n\t}\n\n\tpublic static String[] getDefaultPropetyItems() {\n\t\treturn null;\n\t}\n\n\tpublic static CompRepoItem getItemInstance() {\n\t\treturn new CompRepoItem();\n\t}\n\n\tpublic static String getItemsListPKey() {\n\t\treturn comp_list_pkey;\n\t}\n\n\tpublic static String getPropertyKey() {\n\t\treturn \"--external\";\n\t}\n\n\tpublic static BareJID getRepoUser() {\n\t\treturn comp_user;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/CompRepoItem.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext;\n\nimport tigase.db.comp.RepositoryItemAbstract;\nimport tigase.net.ConnectionType;\nimport tigase.net.SocketType;\nimport tigase.server.Command;\nimport tigase.server.DataForm;\nimport tigase.server.Packet;\nimport tigase.server.ext.lb.LoadBalancerIfc;\nimport tigase.server.ext.lb.ReceiverBareJidLB;\nimport tigase.xml.Element;\n\nimport java.util.Arrays;\nimport java.util.Optional;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\n/**\n * Created: Oct 3, 2009 4:39:51 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class CompRepoItem\n\t\textends RepositoryItemAbstract {\n\n\tpublic static final String CONN_TYPE_ATTR = \"type\";\n\n\tpublic static final String CONNECTION_TYPE_LABEL = \"Connection type\";\n\n\tpublic static final String DOMAIN_ATTR = \"domain\";\n\n\tpublic static final String DOMAIN_NAME_LABEL = \"Domain name\";\n\n\tpublic static final String DOMAIN_PASS_LABEL = \"Domain password\";\n\n\tpublic static final String LB_CLASS_LABEL = \"Load balancer class\";\n\n\tpublic static final String LB_NAME_ATTR = \"lb-class\";\n\n\tpublic static final String PASSWORD_ATTR = \"pass\";\n\n\tpublic static final String PORT_NO_ATTR = \"port\";\n\n\tpublic static final String PORT_NO_LABEL = \"Port number\";\n\n\tpublic static final String PROTO_XMLNS_ATTR = \"proto-xmlns\";\n\n\tpublic static final String PROTO_XMLNS_LABEL = \"Protocol\";\n\n\tpublic static final String REMOTE_HOST_ATTR = \"remote\";\n\n\tpublic static final String REMOTE_HOST_LABEL = \"Remote host\";\n\n\tpublic static final String REPO_ITEM_ELEM_NAME = \"item\";\n\n\tpublic static final String ROUTINGS_ATTR = \"routings\";\n\n\tpublic static final String ROUTINGS_LABEL = \"(Optional) Routings\";\n\n\tpublic static final String SOCKET_ATTR = \"socket\";\n\n\tpublic static final String SOCKET_LABEL = \"(Optional) Socket type\";\n\n\tpublic static final LoadBalancerIfc DEF_LB_CLASS = new ReceiverBareJidLB();\n\tprivate static final Logger log = Logger.getLogger(CompRepoItem.class.getName());\n\n\t// ~--- fields ---------------------------------------------------------------\n\tprivate String auth_pass = null;\n\n\t// \"accept:muc.domain.tld:5277:user:passwd\"\n\tprivate String domain = null;\n\tprivate LoadBalancerIfc lb = DEF_LB_CLASS;\n\tprivate int port = -1;\n\tprivate String prop_xmlns = null;\n\tprivate String remoteHost = null;\n\tprivate String[] routings = null;\n\tprivate ConnectionType type = ConnectionType.accept;\n\tprivate String xmlns = null;\n\tprivate SocketType socket  = SocketType.plain;\n\n\t@Override\n\tpublic void addCommandFields(Packet packet) {\n\t\tnew Command.Builder(packet).addDataForm(Command.DataType.form).withFields(builder -> {\n\t\t\tbuilder.addField(domain == null ? DataForm.FieldType.TextSingle : DataForm.FieldType.Fixed,\n\t\t\t\t\t\t\t DOMAIN_NAME_LABEL).setRequired(true).setValue(domain).build();\n\t\t\tbuilder.addField(DataForm.FieldType.TextSingle, DOMAIN_PASS_LABEL)\n\t\t\t\t\t.setRequired(true)\n\t\t\t\t\t.setValue(auth_pass)\n\t\t\t\t\t.build();\n\n\t\t\tbuilder.addField(DataForm.FieldType.ListSingle, CONNECTION_TYPE_LABEL)\n\t\t\t\t\t.setLabel(CONNECTION_TYPE_LABEL)\n\t\t\t\t\t.setRequired(true)\n\t\t\t\t\t.setValue(type.name())\n\t\t\t\t\t.setOptions(ConnectionType.names())\n\t\t\t\t\t.build();\n\t\t\tbuilder.addField(DataForm.FieldType.TextSingle, PORT_NO_LABEL)\n\t\t\t\t\t.setRequired(true)\n\t\t\t\t\t.setValue(port > 0 ? String.valueOf(port) : null)\n\t\t\t\t\t.build();\n\t\t\tbuilder.addField(DataForm.FieldType.TextSingle, REMOTE_HOST_LABEL).setValue(remoteHost).build();\n\t\t\tbuilder.addField(DataForm.FieldType.ListSingle, PROTO_XMLNS_LABEL)\n\t\t\t\t\t.addOption(\"\", \"Autodetect\")\n\t\t\t\t\t.addOption(\"accept\", \"XEP-0114: Jabber Component Protocol (accept)\")\n//\t\t\t\t\t.addOption(\"connect\", \"Connect\")\n\t\t\t\t\t.addOption(\"client\", \"XEP-0225: Component Connections\")\n\t\t\t\t\t.setValue(prop_xmlns)\n\t\t\t\t\t.setDesc(\"For 'accept' connection type you may use 'Autodetect' but for 'connect' type you need to select exact protocol\")\n\t\t\t\t\t.build();\n\t\t\tbuilder.addField(DataForm.FieldType.TextSingle, LB_CLASS_LABEL)\n\t\t\t\t\t.setValue(lb != null ? lb.getClass().getName() : null)\n\t\t\t\t\t.build();\n\t\t\tbuilder.addField(DataForm.FieldType.TextSingle, ROUTINGS_LABEL)\n\t\t\t\t\t.setValue(Optional.ofNullable(routings)\n\t\t\t\t\t\t\t\t\t  .map(Arrays::stream)\n\t\t\t\t\t\t\t\t\t  .map(stream -> stream.collect(Collectors.joining(\",\")))\n\t\t\t\t\t\t\t\t\t  .orElse(null));\n\t\t\tbuilder.addField(DataForm.FieldType.ListSingle, SOCKET_LABEL)\n\t\t\t\t\t.setValue(socket.name())\n\t\t\t\t\t.setLabel(SOCKET_LABEL)\n\t\t\t\t\t.setOptions(SocketType.names())\n\t\t\t\t\t.build();\n\t\t}).build();\n\t\tsuper.addCommandFields(packet);\n\t}\n\n\tpublic String getAuthPasswd() {\n\t\treturn auth_pass;\n\t}\n\n\tpublic ConnectionType getConnectionType() {\n\t\treturn type;\n\t}\n\n\tvoid setConnectionType(String connection_type) {\n\t\tthis.type = parseConnectionType(connection_type);\n\t}\n\n\tpublic LoadBalancerIfc getLoadBalancer() {\n\t\treturn lb;\n\t}\n\n\tpublic String getLb() {\n\t\tif (lb != null) {\n\t\t\treturn lb.getClass().getCanonicalName();\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic void setLb(String clazz) {\n\t\tif (clazz != null) {\n\t\t\tlb = lbInstance(clazz);\n\t\t}\n\t}\n\n\tpublic String getDomain() {\n\t\treturn domain;\n\t}\n\n\tpublic void setDomain(String domain) {\n\t\tthis.domain = domain;\n\t\troutings = new String[]{domain, \".*@\" + domain, \".*\\\\.\" + domain};\n\t}\n\n\t@Override\n\tpublic String getElemName() {\n\t\treturn REPO_ITEM_ELEM_NAME;\n\t}\n\n\t@Override\n\tpublic String getKey() {\n\t\treturn domain;\n\t}\n\n\t@Override\n\tprotected void setKey(String key) {\n\t\tsetDomain(key);\n\t}\n\n\tpublic int getPort() {\n\t\treturn port;\n\t}\n\n\tvoid setPort(int port) {\n\t\tthis.port = port;\n\t}\n\n\tpublic String getRemoteHost() {\n\t\treturn remoteHost;\n\t}\n\n\tpublic String[] getRoutings() {\n\t\treturn routings;\n\t}\n\n\tpublic String getXMLNS() {\n\t\treturn xmlns;\n\t}\n\n\tpublic SocketType getSocket() {\n\t\treturn socket;\n\t}\n\n\t@Override\n\tpublic void initFromCommand(Packet packet) {\n\t\tsuper.initFromCommand(packet);\n\t\tdomain = Command.getFieldValue(packet, DOMAIN_NAME_LABEL);\n\t\troutings = new String[]{domain, \".*@\" + domain, \".*\\\\.\" + domain};\n\t\tauth_pass = Command.getFieldValue(packet, DOMAIN_PASS_LABEL);\n\n\t\tString tmp = Command.getFieldValue(packet, REMOTE_HOST_LABEL);\n\n\t\tif ((tmp != null) && !tmp.isEmpty()) {\n\t\t\tremoteHost = tmp;\n\t\t}\n\t\ttmp = Command.getFieldValue(packet, CONNECTION_TYPE_LABEL);\n\t\tif ((tmp != null) && !tmp.isEmpty()) {\n\t\t\ttype = parseConnectionType(tmp);\n\t\t}\n\t\ttmp = Command.getFieldValue(packet, PORT_NO_LABEL);\n\t\tif ((tmp != null) && !tmp.isEmpty()) {\n\t\t\tport = parsePortNo(tmp);\n\t\t}\n\t\ttmp = Command.getFieldValue(packet, PROTO_XMLNS_LABEL);\n\t\tif ((tmp != null) && !tmp.isEmpty()) {\n\t\t\tprop_xmlns = tmp;\n\t\t\txmlns = parseProtoXMLNS(prop_xmlns);\n\t\t}\n\t\ttmp = Command.getFieldValue(packet, LB_CLASS_LABEL);\n\t\tif ((tmp != null) && !tmp.trim().isEmpty()) {\n\t\t\tlb = lbInstance(tmp);\n\t\t}\n\t\ttmp = Command.getFieldValue(packet, ROUTINGS_LABEL);\n\t\tif ((tmp != null) && !tmp.isEmpty()) {\n\t\t\troutings = tmp.split(\",\");\n\t\t}\n\t\ttmp = Command.getFieldValue(packet, SOCKET_LABEL);\n\t\tif ((tmp != null) &&!tmp.isEmpty()) {\n\t\t\tsocket = parseSocket(tmp);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void initFromElement(Element elem) {\n\t\tif (elem.getName() != REPO_ITEM_ELEM_NAME) {\n\t\t\tthrow new IllegalArgumentException(\"Incorrect element name, expected: \" + REPO_ITEM_ELEM_NAME);\n\t\t}\n\t\tsuper.initFromElement(elem);\n\t\tsetDomain(elem.getAttributeStaticStr(DOMAIN_ATTR));\n\t\tauth_pass = elem.getAttributeStaticStr(PASSWORD_ATTR);\n\t\tremoteHost = elem.getAttributeStaticStr(REMOTE_HOST_ATTR);\n\n\t\tString tmp = elem.getAttributeStaticStr(CONN_TYPE_ATTR);\n\n\t\tif (tmp != null) {\n\t\t\tsetConnectionType(tmp);\n\t\t}\n\t\ttmp = elem.getAttributeStaticStr(PORT_NO_ATTR);\n\t\tif (tmp != null) {\n\t\t\tport = parsePortNo(tmp);\n\t\t}\n\t\ttmp = elem.getAttributeStaticStr(PROTO_XMLNS_ATTR);\n\t\tif (tmp != null) {\n\t\t\tsetProtocol(tmp);\n\t\t}\n\t\ttmp = elem.getAttributeStaticStr(LB_NAME_ATTR);\n\t\tif (tmp != null) {\n\t\t\tlb = lbInstance(tmp);\n\t\t}\n\t\ttmp = elem.getAttributeStaticStr(ROUTINGS_ATTR);\n\t\tif (tmp != null) {\n\t\t\troutings = tmp.split(\",\");\n\t\t}\n\t\ttmp = elem.getAttributeStaticStr(SOCKET_ATTR);\n\t\tif (tmp != null) {\n\t\t\tsocket = parseSocket(tmp);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void initFromPropertyString(String propString) {\n\t\tString[] props = propString.split(\":\");\n\n\t\tif (props.length > 0) {\n\t\t\tsetDomain(props[0]);\n\t\t}\n\t\tif (props.length > 1) {\n\t\t\tauth_pass = props[1];\n\t\t}\n\t\tif (props.length > 2 && !props[2].trim().isEmpty()) {\n\t\t\tsetConnectionType(props[2]);\n\t\t}\n\t\tif (props.length > 3 && !props[3].trim().isEmpty()) {\n\t\t\tport = parsePortNo(props[3]);\n\t\t}\n\t\tif (props.length > 4 && !props[4].trim().isEmpty()) {\n\t\t\tremoteHost = props[4];\n\t\t}\n\t\tif (props.length > 5 && !props[5].trim().isEmpty()) {\n\t\t\tsetProtocol(props[5]);\n\t\t}\n\t\tif (props.length > 6 && !props[6].trim().isEmpty()) {\n\t\t\tlb = lbInstance(props[6]);\n\t\t}\n\t\tif (props.length > 7 && !props[7].trim().isEmpty()) {\n\t\t\tsocket = parseSocket(props[7]);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Element toElement() {\n\t\tElement elem = super.toElement();\n\n\t\telem.addAttribute(DOMAIN_ATTR, domain);\n\t\telem.addAttribute(PASSWORD_ATTR, auth_pass);\n\t\tif ((remoteHost != null) && !remoteHost.isEmpty()) {\n\t\t\telem.addAttribute(REMOTE_HOST_ATTR, remoteHost);\n\t\t}\n\t\telem.addAttribute(CONN_TYPE_ATTR, type.name());\n\t\tif (port > 0) {\n\t\t\telem.addAttribute(PORT_NO_ATTR, \"\" + port);\n\t\t}\n\t\tif (prop_xmlns != null) {\n\t\t\telem.addAttribute(PROTO_XMLNS_ATTR, prop_xmlns);\n\t\t}\n\t\telem.addAttribute(LB_NAME_ATTR, lb.getClass().getName());\n\n\t\tStringBuilder route = new StringBuilder();\n\n\t\tfor (String r : routings) {\n\t\t\tif (route.length() > 0) {\n\t\t\t\troute.append(',');\n\t\t\t}\n\t\t\troute.append(r);\n\t\t}\n\t\telem.addAttribute(ROUTINGS_ATTR, route.toString());\n\t\telem.addAttribute(SOCKET_ATTR, socket.name());\n\n\t\treturn elem;\n\t}\n\n\t@Override\n\tpublic String toPropertyString() {\n\t\treturn domain + \":\" + auth_pass + \":\" + type.name() + \":\" + port + \":\" + remoteHost + \":\" + prop_xmlns + \":\" +\n\t\t\t\tlb.getClass().getName();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn toPropertyString();\n\t}\n\n\tvoid setPassword(String password) {\n\t\tthis.auth_pass = password;\n\t}\n\n\tvoid setProtocol(String protocol) {\n\t\tthis.prop_xmlns = protocol;\n\t\tthis.xmlns = parseProtoXMLNS(protocol);\n\t}\n\n\tvoid setRemoteDomain(String remote_domain) {\n\t\tthis.remoteHost = remote_domain;\n\t}\n\n\tString validate() {\n\t\tif (domain == null) {\n\t\t\treturn \"Domain name is required\";\n\t\t}\n\t\tif (auth_pass == null) {\n\t\t\treturn \"Password is required\";\n\t\t}\n\t\tif (prop_xmlns != null && \"connect\".equals(prop_xmlns)) {\n\t\t\treturn \"Feature not implemented. Please use 'accept' protocol\";\n\t\t}\n\t\tif (type == ConnectionType.connect && prop_xmlns == null) {\n\t\t\treturn \"It is required to select protocol for connecting socket!\";\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate LoadBalancerIfc lbInstance(String cls_name) {\n\t\tString class_name = cls_name;\n\n//  if (!class_name.endsWith(\".class\")) {\n//    class_name = class_name + \".class\";\n//  }\n\t\tlog.log(Level.CONFIG, \"Activating load-balancer for domain: {0}, class: {1}\", new Object[]{domain, class_name});\n\n\t\tLoadBalancerIfc result = null;\n\n\t\ttry {\n\t\t\tresult = (LoadBalancerIfc) Class.forName(class_name).newInstance();\n\t\t} catch (Exception ex1) {\n\t\t\tclass_name = \"tigase.server.ext.lb.\" + class_name;\n\t\t\tlog.log(Level.CONFIG, \"Cannot active load balancer for class: {0}, trying: {1}\",\n\t\t\t\t\tnew Object[]{cls_name, class_name});\n\t\t\ttry {\n\t\t\t\tresult = (LoadBalancerIfc) Class.forName(class_name).newInstance();\n\t\t\t} catch (Exception ex2) {\n\t\t\t\tlog.log(Level.WARNING, \"Cannot active load balancer for class:\" +\n\t\t\t\t\t\t\t\t\" {0}, or: {1}, errors: {2} or {3}, using default LB: {4}\",\n\t\t\t\t\t\tnew Object[]{cls_name, class_name, ex1, ex2, DEF_LB_CLASS.getClass().getName()});\n\t\t\t\tresult = DEF_LB_CLASS;\n\t\t\t}\n\t\t}\n\t\tlog.log(Level.CONFIG, \"Activated load-balancer for domain: {0}, class: {1}\",\n\t\t\t\tnew Object[]{domain, result.getClass().getName()});\n\n\t\treturn result;\n\t}\n\n\t// ~--- methods --------------------------------------------------------------\n\tprivate ConnectionType parseConnectionType(String input) {\n\t\tConnectionType result = ConnectionType.accept;\n\n\t\tif (input.equals(\"connect\")) {\n\t\t\tresult = ConnectionType.connect;\n\t\t}\n\t\tif (input.equals(\"accept\") || input.equals(\"listen\")) {\n\t\t\tresult = ConnectionType.accept;\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate int parsePortNo(String input) {\n\t\tint result = -1;\n\n\t\ttry {\n\t\t\tresult = Integer.parseInt(input);\n\t\t} catch (Exception e) {\n\t\t\tresult = 5277;\n\t\t\tlog.warning(\"Incorrect port number, can''t parse: \" + input);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate String parseProtoXMLNS(String input) {\n\t\tString result = input;\n\n\t\tif (input.equals(\"accept\")) {\n\t\t\tresult = \"jabber:component:accept\";\n\t\t}\n\t\tif (input.equals(\"client\")) {\n\t\t\tresult = \"jabber:client\";\n\t\t}\n\t\tif (input.equals(\"connect\")) {\n\t\t\tresult = \"jabber:component:connect\";\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate SocketType parseSocket(String socket) {\n\t\tSocketType result = SocketType.plain;\n\t\ttry {\n\t\t\treturn SocketType.valueOf(socket.trim().toLowerCase());\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t        \"Error parsing external connection type: \" + socket + \", using default: \" + SocketType.plain, e);\n\t\t}\n\t\treturn result;\n\t}\n\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/CompSQLRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.*;\nimport tigase.db.beans.DataSourceBean;\nimport tigase.db.comp.ComponentRepository;\nimport tigase.db.comp.RepositoryChangeListenerIfc;\nimport tigase.eventbus.EventBus;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.xml.DomBuilderHandler;\nimport tigase.xml.Element;\nimport tigase.xml.SimpleParser;\nimport tigase.xml.SingletonFactory;\n\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Types;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Nov 7, 2009 11:26:10 AM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Deprecated\n@TigaseDeprecated(since=\"8.0.0\")\npublic class CompSQLRepository\n\t\timplements ComponentRepository<CompRepoItem>, DataSourceAware<DataRepository>, Initializable, UnregisterAware {\n\n\tpublic static final String REPO_URI_PROP_KEY = \"repo-uri\";\n\n\tpublic static final String TABLE_NAME = \"external_component\";\n\tprivate static final String CONNECTION_TYPE_COLUMN = \"connection_type\";\n\tprivate static final String DOMAIN_COLUMN = \"domain\";\n\tprivate static final Logger log = Logger.getLogger(CompSQLRepository.class.getName());\n\tprivate static final String OTHER_DATA_COLUMN = \"other_data\";\n\tprivate static final String PASSWORD_COLUMN = \"password\";\n\tprivate static final String PORT_COLUMN = \"port\";\n\tprivate static final String PROTOCOL_COLUMN = \"protocol\";\n\tprivate static final String REMOTE_DOMAIN_COLUMN = \"remote_domain\";\n\tprivate static final String GET_ITEM_QUERY = \"select * from \" + TABLE_NAME + \" where \" + DOMAIN_COLUMN + \" = ?\";\n\tprivate static final String GET_ALL_ITEMS_QUERY = \"select * from \" + TABLE_NAME;\n\tprivate static final String DELETE_ITEM_QUERY = \"delete from \" + TABLE_NAME + \" where (\" + DOMAIN_COLUMN + \" = ?)\";\n\tprivate static final String CREATE_TABLE_QUERY =\n\t\t\t\"create table \" + TABLE_NAME + \" (\" + \"  \" + DOMAIN_COLUMN + \" varchar(255) NOT NULL,\" + \"  \" +\n\t\t\t\t\tPASSWORD_COLUMN + \" varchar(255) NOT NULL,\" + \"  \" + CONNECTION_TYPE_COLUMN + \" varchar(127),\" +\n\t\t\t\t\t\"  \" + PORT_COLUMN + \" int,\" + \"  \" + REMOTE_DOMAIN_COLUMN + \" varchar(255),\" + \"  \" +\n\t\t\t\t\tPROTOCOL_COLUMN + \" varchar(127),\" + \"  \" + OTHER_DATA_COLUMN + \" TEXT,\" + \"  primary key(\" +\n\t\t\t\t\tDOMAIN_COLUMN + \"))\";\n\tprivate static final String CHECK_TABLE_QUERY = \"select count(*) from \" + TABLE_NAME;\n\tprivate static final String ADD_ITEM_QUERY =\n\t\t\t\"insert into \" + TABLE_NAME + \" (\" + DOMAIN_COLUMN + \", \" + PASSWORD_COLUMN + \", \" +\n\t\t\t\t\tCONNECTION_TYPE_COLUMN + \", \" + PORT_COLUMN + \", \" + REMOTE_DOMAIN_COLUMN + \", \" + PROTOCOL_COLUMN +\n\t\t\t\t\t\", \" +\n//\t\t\tOTHER_DATA_COLUMN + \") \" + \" values (?, ?, ?, ?, ?, ?, ?)\";\n\t\t\t\t\tOTHER_DATA_COLUMN + \") \" + \" (select ?, ?, ?, ?, ?, ?, ? from \" + TABLE_NAME + \" where \" +\n\t\t\t\t\tDOMAIN_COLUMN + \" = ? HAVING count(*)=0) \";\n\n\tprivate CompConfigRepository configRepo = new CompConfigRepository();\n\t@Inject\n\tprivate DataSourceBean dataSourceBean;\n\t@ConfigField(desc = \"Name of data source to use\")\n\tprivate String dataSourceName = \"default\";\n\tprivate DataRepository data_repo = null;\n\t@Inject\n\tprivate EventBus eventBus;\n\tprivate String tableName = TABLE_NAME;\n\n\tpublic void setDataSourceBean(DataSourceBean dataSourceBean) {\n\t\tthis.dataSourceBean = dataSourceBean;\n\t\tDataSource ds = dataSourceBean.getRepository(dataSourceName);\n\t\tif (ds != null && ds instanceof DataRepository) {\n\t\t\tds.checkSchemaVersion(this, true);\n\t\t\tsetDataSource((DataRepository) ds);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"Could not retrieve data source named ''{0}''\", new Object[]{dataSourceName});\n\t\t}\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\teventBus.registerAll(this);\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\teventBus.unregisterAll(this);\n\t}\n\n\tpublic void onDataSourceChange(DataSourceBean.DataSourceChangedEvent event) {\n\t\tif (!event.isCorrectSender(dataSourceBean)) {\n\t\t\treturn;\n\t\t}\n\n\t\tDataSource ds = event.getNewDataSource();\n\t\tif (ds != null && ds instanceof DataRepository) {\n\t\t\tds.checkSchemaVersion(this, true);\n\t\t\tsetDataSource((DataRepository) ds);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"Could not retrieve data source named ''{0}''\", new Object[]{dataSourceName});\n\t\t}\n\t}\n\n\t@Override\n\tpublic void addRepoChangeListener(RepositoryChangeListenerIfc<CompRepoItem> repoChangeListener) {\n\t\tconfigRepo.addRepoChangeListener(repoChangeListener);\n\t}\n\n\t@Override\n\tpublic void removeRepoChangeListener(RepositoryChangeListenerIfc<CompRepoItem> repoChangeListener) {\n\t\tconfigRepo.removeRepoChangeListener(repoChangeListener);\n\t}\n\n\t@Override\n\tpublic void addItemNoStore(CompRepoItem item) {\n\n\t}\n\n\t@Override\n\tpublic void addItem(CompRepoItem item) {\n\t\ttry {\n\t\t\tPreparedStatement addItemSt = data_repo.getPreparedStatement(null, ADD_ITEM_QUERY);\n\n\t\t\tsynchronized (addItemSt) {\n\t\t\t\tif ((item.getDomain() != null) && !item.getDomain().isEmpty()) {\n\t\t\t\t\taddItemSt.setString(1, item.getDomain());\n\t\t\t\t\taddItemSt.setString(8, item.getDomain());\n\t\t\t\t} else {\n\t\t\t\t\tthrow new NullPointerException(\"Null or empty domain name is not allowed\");\n\t\t\t\t}\n\t\t\t\tif (item.getAuthPasswd() != null) {\n\t\t\t\t\taddItemSt.setString(2, item.getAuthPasswd());\n\t\t\t\t} else {\n\t\t\t\t\tthrow new NullPointerException(\"Null password is not allowed\");\n\t\t\t\t}\n\t\t\t\tif (item.getConnectionType() != null) {\n\t\t\t\t\taddItemSt.setString(3, item.getConnectionType().name());\n\t\t\t\t} else {\n\t\t\t\t\taddItemSt.setNull(3, Types.VARCHAR);\n\t\t\t\t}\n\t\t\t\tif (item.getPort() > 0) {\n\t\t\t\t\taddItemSt.setInt(4, item.getPort());\n\t\t\t\t} else {\n\t\t\t\t\taddItemSt.setNull(4, Types.INTEGER);\n\t\t\t\t}\n\t\t\t\tif ((item.getRemoteHost() != null) && !item.getRemoteHost().isEmpty()) {\n\t\t\t\t\taddItemSt.setString(5, item.getRemoteHost());\n\t\t\t\t} else {\n\t\t\t\t\taddItemSt.setNull(5, Types.VARCHAR);\n\t\t\t\t}\n\t\t\t\tif (item.getXMLNS() != null) {\n\t\t\t\t\taddItemSt.setString(6, item.getXMLNS());\n\t\t\t\t} else {\n\t\t\t\t\taddItemSt.setNull(6, Types.VARCHAR);\n\t\t\t\t}\n\n\t\t\t\tString other_data = item.toElement().toString();\n\n\t\t\t\tif (other_data != null) {\n\t\t\t\t\taddItemSt.setString(7, other_data);\n\t\t\t\t} else {\n\t\t\t\t\taddItemSt.setNull(7, Types.VARCHAR);\n\t\t\t\t}\n\t\t\t\taddItemSt.executeUpdate();\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tlog.log(Level.WARNING, \"Problem adding a new item to DB: \" + item.toElement(), e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Collection<CompRepoItem> allItems() {\n\t\tList<CompRepoItem> result = new ArrayList<CompRepoItem>();\n\n\t\tresult.addAll(configRepo.allItems());\n\n\t\ttry {\n\t\t\tResultSet rs = null;\n\t\t\tPreparedStatement getAllItemsSt = data_repo.getPreparedStatement(null, GET_ALL_ITEMS_QUERY);\n\n\t\t\tsynchronized (getAllItemsSt) {\n\t\t\t\ttry {\n\t\t\t\t\trs = getAllItemsSt.executeQuery();\n\t\t\t\t\twhile (rs.next()) {\n\t\t\t\t\t\tresult.add(createItemFromRS(rs));\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tlog.log(Level.WARNING, \"Problem getting elements from DB: \", e);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean contains(String key) {\n\t\tboolean result = configRepo.contains(key);\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic void destroy() {\n\t\t// This implementation of CompSQLRepository is using shared connection\n\t\t// pool to database which is cached by RepositoryFactory and maybe be used\n\t\t// in other places, so we can not destroy it.\n\t}\n\n\t@Deprecated\n\t@Override\n\tpublic void getDefaults(Map<String, Object> defs, Map<String, Object> params) {\n\t\tconfigRepo.getDefaults(defs, params);\n\n\t\tString repo_uri = RepositoryFactory.DERBY_REPO_URL_PROP_VAL;\n\n\t\tif (params.get(RepositoryFactory.GEN_USER_DB_URI) != null) {\n\t\t\trepo_uri = (String) params.get(RepositoryFactory.GEN_USER_DB_URI);\n\t\t}\n\t\tdefs.put(REPO_URI_PROP_KEY, repo_uri);\n\t}\n\n\t@Override\n\tpublic CompRepoItem getItem(String key) {\n\t\tCompRepoItem result = configRepo.getItem(key);\n\n\t\tif (result == null) {\n\t\t\ttry {\n\t\t\t\tResultSet rs = null;\n\t\t\t\tPreparedStatement getItemSt = data_repo.getPreparedStatement(null, GET_ITEM_QUERY);\n\n\t\t\t\tsynchronized (getItemSt) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tgetItemSt.setString(1, key);\n\t\t\t\t\t\trs = getItemSt.executeQuery();\n\t\t\t\t\t\tif (rs.next()) {\n\t\t\t\t\t\t\tresult = createItemFromRS(rs);\n\t\t\t\t\t\t}\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (SQLException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Problem getting element from DB for domain: \" + key, e);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic CompRepoItem getItemInstance() {\n\t\treturn configRepo.getItemInstance();\n\t}\n\n\tpublic void setDataSource(DataRepository data_repo) {\n\t\ttry {\n\t\t\tcheckDB();\n\t\t\tdata_repo.initPreparedStatement(CHECK_TABLE_QUERY, CHECK_TABLE_QUERY);\n\t\t\tdata_repo.initPreparedStatement(GET_ITEM_QUERY, GET_ITEM_QUERY);\n\t\t\tdata_repo.initPreparedStatement(GET_ALL_ITEMS_QUERY, GET_ALL_ITEMS_QUERY);\n\t\t\tdata_repo.initPreparedStatement(ADD_ITEM_QUERY, ADD_ITEM_QUERY);\n\t\t\tdata_repo.initPreparedStatement(DELETE_ITEM_QUERY, DELETE_ITEM_QUERY);\n\t\t} catch (SQLException e) {\n\t\t\tthrow new RuntimeException(\"Could not initialize database: \", e);\n\t\t}\n\t}\n\n\t@Deprecated\n\t@Override\n\tpublic void initRepository(String conn_str, Map<String, String> params) throws DBInitException {\n\t\ttry {\n\t\t\tdata_repo = RepositoryFactory.getDataRepository(null, conn_str, params);\n\t\t\tdata_repo.checkSchemaVersion(this, true);\n\t\t\tsetDataSource(data_repo);\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Problem initializing database: \", e);\n\t\t} finally {\n\n\t\t\t// Check if DB is correctly setup and contains all required tables.\n\t\t}\n\t}\n\n\t@Override\n\tpublic Iterator<CompRepoItem> iterator() {\n\t\treturn allItems().iterator();\n\t}\n\n\t@Override\n\tpublic void reload() {\n\n\t\t// Do nothing, no caching, everything is read on demand from DB\n\t}\n\n\t@Override\n\tpublic void removeItem(String key) {\n\t\tconfigRepo.removeItem(key);\n\t\ttry {\n\t\t\tPreparedStatement deleteItemSt = data_repo.getPreparedStatement(null, DELETE_ITEM_QUERY);\n\n\t\t\tsynchronized (deleteItemSt) {\n\t\t\t\tdeleteItemSt.setString(1, key);\n\t\t\t\tdeleteItemSt.executeUpdate();\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tlog.log(Level.WARNING, \"Can''t remove item: \" + key, e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void removeItemNoStore(String key) {\n\t\t\n\t}\n\n\t@Deprecated\n\t@Override\n\tpublic void setProperties(Map<String, Object> properties) {\n\t\tconfigRepo.setProperties(properties);\n\n\t\tString repo_uri = (String) properties.get(REPO_URI_PROP_KEY);\n\n\t\ttry {\n\t\t\tinitRepository(repo_uri, null);\n\t\t} catch (DBInitException ex) {\n\t\t\tlog.log(Level.WARNING, \"Problem initializing database.\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic int size() {\n\t\tint result = configRepo.size();\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic void store() {\n\n\t\t// Do nothing everything is written on demand to DB\n\t}\n\n\t@Override\n\tpublic String validateItem(CompRepoItem item) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void setAutoloadTimer(long delay) {\n\t}\n\n\t/**\n\t * Performs database check, creates missing schema if necessary\n\t */\n\tprivate void checkDB() throws SQLException {\n\t\tdata_repo.checkTable(tableName, CREATE_TABLE_QUERY);\n\t}\n\n\tprivate CompRepoItem createItemFromRS(ResultSet rs) throws SQLException {\n\t\tCompRepoItem result = getItemInstance();\n\n\t\t// First init from other parameters, some fixed fields may\n\t\t// overwrite fields initialized from other parametrs\n\t\tString other = rs.getString(OTHER_DATA_COLUMN);\n\n\t\tif ((other != null) && !other.isEmpty()) {\n\t\t\tElement elem_item = parseElement(other);\n\n\t\t\tif (elem_item != null) {\n\t\t\t\tresult.initFromElement(elem_item);\n\t\t\t}\n\t\t}\n\n\t\tString domain = rs.getString(DOMAIN_COLUMN);\n\n\t\tif ((domain != null) && !domain.isEmpty()) {\n\t\t\tresult.setDomain(domain);\n\t\t}\n\n\t\tString password = rs.getString(PASSWORD_COLUMN);\n\n\t\tif ((password != null) && !password.isEmpty()) {\n\t\t\tresult.setPassword(password);\n\t\t}\n\n\t\tint port = rs.getInt(PORT_COLUMN);\n\n\t\tif (port > 0) {\n\t\t\tresult.setPort(port);\n\t\t}\n\n\t\tString remote_domain = rs.getString(REMOTE_DOMAIN_COLUMN);\n\n\t\tif ((remote_domain != null) && !remote_domain.isEmpty()) {\n\t\t\tresult.setRemoteDomain(remote_domain);\n\t\t}\n\n\t\tString protocol = rs.getString(PROTOCOL_COLUMN);\n\n\t\tif ((protocol != null) && !protocol.isEmpty()) {\n\t\t\tresult.setProtocol(protocol);\n\t\t}\n\n\t\tString connection_type = rs.getString(CONNECTION_TYPE_COLUMN);\n\n\t\tif ((connection_type != null) && !connection_type.isEmpty()) {\n\t\t\tresult.setConnectionType(connection_type);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate Element parseElement(String data) {\n\t\tDomBuilderHandler domHandler = new DomBuilderHandler();\n\t\tSimpleParser parser = SingletonFactory.getParserInstance();\n\n\t\tparser.parse(domHandler, data.toCharArray(), 0, data.length());\n\n\t\tQueue<Element> elems = domHandler.getParsedElements();\n\n\t\tif ((elems != null) && (elems.size() > 0)) {\n\t\t\treturn elems.poll();\n\t\t}\n\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/ComponentConnection.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext;\n\n/**\n * Created: Sep 30, 2009 9:20:22 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class ComponentConnection\n\t\timplements Comparable<ComponentConnection> {\n\n\tprivate String domain = null;\n\n\t//private List<String> resources     = new ArrayList<String>();\n\tprivate ComponentIOService service = null;\n\n\tpublic ComponentConnection(String domain, ComponentIOService service) {\n\t\tthis.domain = domain;\n\t\tthis.service = service;\n\t}\n\n\t@Override\n\tpublic int compareTo(ComponentConnection o) {\n\t\tif (o == null) {\n\t\t\treturn -1;\n\t\t}\n\n\t\treturn service.getRemoteAddress().compareTo(o.service.getRemoteAddress());\n\n\t\t// return service.getUniqueId().compareTo(o.service.getUniqueId());\n\t}\n\n\tpublic String getDomain() {\n\t\treturn domain;\n\t}\n\n\tpublic ComponentIOService getService() {\n\t\treturn service;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/ComponentIOService.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext;\n\nimport tigase.net.ConnectionType;\nimport tigase.util.cache.SizedCache;\nimport tigase.xmpp.XMPPIOService;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.List;\n\n/**\n * Created: Jun 14, 2010 12:05:41 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class ComponentIOService\n\t\textends XMPPIOService<List<ComponentConnection>> {\n\n\tprivate static final int MAX_RECENT_JIDS = 10000;\n\tprivate static final long MAX_CACHE_TIME = 100000;\n\n\tprivate boolean authenticated = false;\n\tprivate SizedCache<JID, JID> recentJIDs = new SizedCache<JID, JID>(MAX_RECENT_JIDS);\n\tprivate String routings = null;\n\n\tpublic boolean isAuthenticated() {\n\t\treturn authenticated;\n\t}\n\n\tpublic void setAuthenticated(boolean authenticated) {\n\t\tthis.authenticated = authenticated;\n\t}\n\n\tpublic String getRoutings() {\n\t\treturn routings;\n\t}\n\n\tpublic void setRoutings(String r) {\n\t\troutings = r;\n\t}\n\n\tpublic void addRecentJID(JID jid) {\n\t\t// We only save recent JIDs on the external component side\n\t\tif (connectionType() == ConnectionType.connect) {\n\t\t\trecentJIDs.put(jid, jid);\n\t\t}\n\t}\n\n\tpublic boolean isRecentJID(JID jid) {\n\t\treturn jid != null && recentJIDs.get(jid) != null;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/ComponentProtocol.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext;\n\nimport tigase.db.comp.ComponentRepository;\nimport tigase.db.comp.RepositoryChangeListenerIfc;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.core.Kernel;\nimport tigase.net.ConnectionOpenListener;\nimport tigase.net.ConnectionType;\nimport tigase.server.ConnectionManager;\nimport tigase.server.Packet;\nimport tigase.server.ext.handlers.*;\nimport tigase.server.ext.lb.LoadBalancerIfc;\nimport tigase.stats.StatisticsList;\nimport tigase.util.common.TimerTask;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.PacketErrorTypeException;\n\nimport javax.script.Bindings;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Sep 30, 2009 8:28:13 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"ext\", parent = Kernel.class, active = false)\npublic class ComponentProtocol\n\t\textends ConnectionManager<ComponentIOService>\n\t\timplements ComponentProtocolHandler, UnregisterAware, RepositoryChangeListenerIfc<CompRepoItem> {\n\n\tpublic static final String AUTHENTICATION_TIMEOUT_PROP_KEY = \"auth-timeout\";\n\n\tpublic static final String CLOSE_ON_SEQUENCE_ERROR_PROP_KEY = \"close-on-seq-error\";\n\n\tpublic static final String EXTCOMP_BIND_HOSTNAMES = \"--bind-ext-hostnames\";\n\n\tpublic static final String EXTCOMP_REPO_CLASS_PROP_KEY = \"repository-class\";\n\n\tpublic static final String EXTCOMP_REPO_CLASS_PROP_VAL = \"tigase.server.ext.ServerCompDBRepository\";\n\n\tpublic static final String EXTCOMP_REPO_CLASS_PROPERTY = \"--extcomp-repo-class\";\n\n\tpublic static final String IDENTITY_TYPE_KEY = \"identity-type\";\n\n\tpublic static final String IDENTITY_TYPE_VAL = \"generic\";\n\n\tpublic static final String MAX_AUTH_ATTEMPTS_PROP_KEY = \"max-auth-attempts\";\n\n\tpublic static final String PACK_ROUTED_KEY = \"pack-routed\";\n\n\tpublic static final String RETURN_SERVICE_DISCO_KEY = \"service-disco\";\n\tpublic static final boolean RETURN_SERVICE_DISCO_VAL = true;\n\tprivate static final Logger log = Logger.getLogger(ComponentProtocol.class.getName());\n\n\t// In seconds\n\t@ConfigField(desc = \"Authentication timeout\", alias = AUTHENTICATION_TIMEOUT_PROP_KEY)\n\tprivate long authenticationTimeOut = 15;\n\t// private ServiceEntity serviceEntity = null;\n\t@ConfigField(desc = \"Close stream on sequence error\", alias = CLOSE_ON_SEQUENCE_ERROR_PROP_KEY)\n\tprivate boolean closeOnSequenceError = true;\n\t/**\n\t * A map keeping all active connections by a connection JID or domain name. Since for each domain we can have 1..N\n\t * connections the Map value is a List of connections.\n\t */\n\tprivate Map<String, CopyOnWriteArrayList<ComponentConnection>> connections = new ConcurrentHashMap<String, CopyOnWriteArrayList<ComponentConnection>>();\n\t@ConfigField(desc = \"Enable experimental features\")\n\tprivate boolean experimental = false;\n\t@ConfigField(desc = \"Hostnames to bind\", alias = EXTCOMP_BIND_HOSTNAMES_PROP_KEY)\n\tprivate String[] hostnamesToBind = new String[0];\n\t@ConfigField(desc = \"Identitiy type\", alias = IDENTITY_TYPE_KEY)\n\tprivate String identity_type = IDENTITY_TYPE_VAL;\n\t@ConfigField(desc = \"Max number of authentication attempts\", alias = MAX_AUTH_ATTEMPTS_PROP_KEY)\n\tprivate int maxAuthenticationAttempts = 1;\n\t/**\n\t * List of processors which should handle all traffic incoming from the network. In most cases if not all, these\n\t * processors handle just protocol traffic, all the rest traffic should be passed on to MR.\n\t */\n\tprivate Map<String, ExtProcessor> processors = new LinkedHashMap<String, ExtProcessor>(10);\n\t@Inject\n\tprivate ComponentRepository<CompRepoItem> repo = null;\n\tprivate Map<String, StreamOpenHandler> streamOpenHandlers = new LinkedHashMap<String, StreamOpenHandler>();\n\tprivate UnknownXMLNSStreamOpenHandler unknownXMLNSHandler = new UnknownXMLNSStreamOpenHandler();\n\n\tprivate ConcurrentHashMap<String, List<ConnectionOpenListener>> activeConnections = new ConcurrentHashMap<>();\n\n\tpublic ComponentProtocol() {\n\t\tsuper();\n\n\t\tStreamOpenHandler handler = new JabberClientStreamOpenHandler();\n\n\t\tif (handler.getXMLNSs() != null) {\n\t\t\tfor (String xmlns : handler.getXMLNSs()) {\n\t\t\t\tstreamOpenHandlers.put(xmlns, handler);\n\t\t\t}\n\t\t}\n\t\thandler = new ComponentAcceptStreamOpenHandler();\n\t\tif (handler.getXMLNSs() != null) {\n\t\t\tfor (String xmlns : handler.getXMLNSs()) {\n\t\t\t\tstreamOpenHandlers.put(xmlns, handler);\n\t\t\t}\n\t\t}\n\t\thandler = new ComponentConnectStreamOpenHandler();\n\t\tif (handler.getXMLNSs() != null) {\n\t\t\tfor (String xmlns : handler.getXMLNSs()) {\n\t\t\t\tstreamOpenHandlers.put(xmlns, handler);\n\t\t\t}\n\t\t}\n\n\t\tprocessors = new LinkedHashMap<String, ExtProcessor>();\n\n\t\tExtProcessor proc = new HandshakeProcessor();\n\n\t\tprocessors.put(proc.getId(), proc);\n\t\tproc = new StreamFeaturesProcessor();\n\t\tprocessors.put(proc.getId(), proc);\n\t\tproc = new StartTLSProcessor();\n\t\tprocessors.put(proc.getId(), proc);\n\t\tproc = new SASLProcessor();\n\t\tprocessors.put(proc.getId(), proc);\n\t\tproc = new BindProcessor();\n\t\tprocessors.put(proc.getId(), proc);\n\t}\n\n\t@Override\n\tpublic void authenticated(ComponentIOService serv) {\n\t\tserv.setAuthenticated(true);\n\n\t\tsuper.serviceConnected(serv);\n\n\t\tString hostname = (String) serv.getSessionData().get(ComponentIOService.HOSTNAME_KEY);\n\n\t\tbindHostname(hostname, serv);\n\t\tif (hostnamesToBind != null) {\n\t\t\tserv.getSessionData().put(EXTCOMP_BIND_HOSTNAMES_PROP_KEY, hostnamesToBind);\n\n\t\t\tExtProcessor proc = getProcessor(\"bind\");\n\n\t\t\tif (proc != null) {\n\t\t\t\tQueue<Packet> results = new ArrayDeque<Packet>();\n\n\t\t\t\tproc.startProcessing(null, serv, this, results);\n\t\t\t\twritePacketsToSocket(serv, results);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void authenticationFailed(ComponentIOService serv, Packet packet) {\n\t\twritePacketToSocket(serv, packet);\n\n\t\tInteger fails = (Integer) serv.getSessionData().get(\"auth-fails\");\n\n\t\tif (fails == null) {\n\t\t\tfails = 1;\n\t\t} else {\n\t\t\tfails += 1;\n\t\t}\n\t\tif (fails >= maxAuthenticationAttempts) {\n\t\t\tserv.stop();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void bindHostname(String hostname, ComponentIOService serv) {\n\t\tString[] routings = new String[]{hostname, \".*@\" + hostname, \".*\\\\.\" + hostname};\n\n\t\tif (serv.connectionType() == ConnectionType.connect) {\n\n\t\t\t// Most likely we have an external component here which doesn't have any\n\t\t\t// connections managers. In such a case the best routings settings would\n\t\t\t// be: .*\n\t\t\troutings = new String[]{\".*\"};\n\t\t}\n\t\tserv.setRoutings(routings[0]);\n\t\tupdateRoutings(routings, true);\n\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\tlog.fine(\"Authenticated: \" + hostname);\n\t\t}\n\t\tupdateServiceDiscoveryItem(hostname, null, \"ext-comp connected\", false);\n\n\t\t// Now kind of trick to allow access to the external component in a more\n\t\t// direct way. However, we have to careful here to avoid disaster.\n\t\tif (experimental) {\n\t\t\tupdateServiceDiscoForConnection(hostname, serv);\n\t\t}\n\t\taddComponentConnection(hostname, serv);\n\t\taddComponentDomain(hostname);\n\t}\n\n\t@Override\n\tprotected boolean enableServiceConnectedTimeout(ComponentIOService service) {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic CompRepoItem getCompRepoItem(String hostname) {\n\t\treturn repo.getItem(hostname);\n\t}\n\n\t@Override\n\tpublic String getDiscoCategoryType() {\n\t\treturn identity_type;\n\t}\n\n\t@Override\n\tpublic String getDiscoDescription() {\n\t\treturn \"External component\";\n\t}\n\n\t@Override\n\tpublic ExtProcessor getProcessor(String key) {\n\t\treturn processors.get(key);\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t\tsuper.getStatistics(list);\n\n\t\t// Warning size() for ConcurrentHashMap is very slow\n\t\t// unless we have a huge number of domains this should not be a problem\n\t\t// though.\n\t\tlist.add(getName(), \"Number of external domains\", connections.size(), Level.FINE);\n\n\t\tint size = 0;\n\n\t\tfor (CopyOnWriteArrayList<ComponentConnection> conns : connections.values()) {\n\t\t\tsize += conns.size();\n\t\t}\n\t\tlist.add(getName(), \"Number of external component connections\", size, Level.FINER);\n\t}\n\n\t@Override\n\tpublic List<Element> getStreamFeatures(ComponentIOService serv) {\n\t\tList<Element> results = new LinkedList<Element>();\n\n\t\tfor (ExtProcessor proc : processors.values()) {\n\t\t\tList<Element> proc_res = proc.getStreamFeatures(serv, this);\n\n\t\t\tif (proc_res != null) {\n\t\t\t\tresults.addAll(proc_res);\n\t\t\t}\n\t\t}\n\n\t\treturn results;\n\t}\n\n\t@Override\n\tpublic StreamOpenHandler getStreamOpenHandler(String xmlns) {\n\t\treturn streamOpenHandlers.get(xmlns);\n\t}\n\n\t@Override\n\tpublic void initBindings(Bindings binds) {\n\t\tsuper.initBindings(binds);\n\t\tbinds.put(ComponentRepository.COMP_REPO_BIND, repo);\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\t\tsuper.register(kernel);\n\t//\tthis.kernel.getParent().setBeanActive(\"ext-man\", true);\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tsuper.initialize();\n\t\t//this.kernel.getParent().registerBean(ComponentProtocolManager.class).setActive(true).exec();\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t//\tthis.kernel.getParent().in\n\t}\n\n\t@Override\n\tpublic Queue<Packet> processSocketData(ComponentIOService serv) {\n\t\tQueue<Packet> packets = serv.getReceivedPackets();\n\t\tPacket p = null;\n\t\tQueue<Packet> results = new ArrayDeque<Packet>(2);\n\n\t\twhile ((p = packets.poll()) != null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Processing socket data: {0}, from socket: {1}\", new Object[]{p, serv});\n\t\t\t}\n\n\t\t\tboolean processed = false;\n\n\t\t\tfor (ExtProcessor proc : processors.values()) {\n\t\t\t\tprocessed |= proc.process(p, serv, this, results);\n\t\t\t\twritePacketsToSocket(serv, results);\n\t\t\t}\n\t\t\tif (!processed) {\n\n\t\t\t\t// This might be a bit slow, need to be tested.\n\t\t\t\t// Possibly a local variable in XMPPIOService might be needed\n\t\t\t\t// to improve performance\n\t\t\t\tif (serv.isAuthenticated()) {\n\t\t\t\t\tPacket result = p;\n\n\t\t\t\t\tif (p.isRouted()) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tresult = p.unpackRouted();\n\t\t\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\t\t\tlog.log(Level.WARNING, \"Packet stringprep addressing problem, dropping packet: {0}\", p);\n\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\t\t\t\t\t}    // end of if (p.isRouted())\n\t\t\t\t\tresult.getElement().setXMLNS(\"jabber:client\");\n\t\t\t\t\tif (result.getStanzaFrom() != null) {\n\t\t\t\t\t\tserv.addRecentJID(result.getStanzaFrom());\n\t\t\t\t\t}\n\t\t\t\t\taddOutPacket(result);\n\t\t\t\t} else {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tPacket error = Authorization.NOT_AUTHORIZED.getResponseMessage(p,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"Connection not yet authorized to send this packet.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   true);\n\n\t\t\t\t\t\twritePacketToSocket(serv, error);\n\t\t\t\t\t} catch (PacketErrorTypeException ex) {\n\n\t\t\t\t\t\t// Already error packet, just ignore to prevent infinite loop\n\t\t\t\t\t\tlog.log(Level.FINE, \"Received an error packet from unauthorized connection: {0}\", p);\n\t\t\t\t\t}\n\t\t\t\t\tif (closeOnSequenceError) {\n\t\t\t\t\t\tserv.stop();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}    // end of while ()\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean processUndeliveredPacket(Packet packet, Long stamp, String errorMessage) {\n\t\t// readd packet - this may be good as we would retry to send packet\n\t\t// which delivery failed due to IO error\n\t\taddPacket(packet);\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void reconnectionFailed(Map<String, Object> port_props) {\n\n\t\t// TODO: handle this somehow\n\t}\n\t\n\t@Override\n\tpublic void serviceStarted(ComponentIOService serv) {\n\t\tsuper.serviceStarted(serv);\n\t\taddTimerTask(new AuthenticationTimerTask(serv), authenticationTimeOut, TimeUnit.SECONDS);\n\n\t\tString xmlns = ((CompRepoItem) serv.getSessionData().get(REPO_ITEM_KEY)).getXMLNS();\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Connection started: \" + serv.getRemoteAddress() + \", xmlns: \" + xmlns + \", type: \" +\n\t\t\t\t\t\t\t   serv.connectionType().toString() + \", id=\" + serv.getUniqueId());\n\t\t}\n\n\t\tStreamOpenHandler handler = streamOpenHandlers.get(xmlns);\n\t\tString result = null;\n\n\t\tif (handler == null) {\n\n\t\t\t// Well, that's a but, we should not be here...\n\t\t\tlog.fine(\"XMLNS not set, accepting a new connection with xmlns auto-detection.\");\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"cid: {0}, sending: {1}, sessionData: {2}\",\n\t\t\t\t\t\tnew Object[]{serv.getSessionData().get(\"cid\"), result, serv.getSessionData()});\n\t\t\t}\n\t\t\tresult = handler.serviceStarted(serv);\n\t\t}\n\t\tif (result != null) {\n\t\t\tserv.xmppStreamOpen(result);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean serviceStopped(ComponentIOService service) {\n\t\tboolean result = super.serviceStopped(service);\n\n\t\tif (result) {\n\t\t\tMap<String, Object> sessionData = service.getSessionData();\n\t\t\tString hostname = (String) sessionData.get(ComponentIOService.HOSTNAME_KEY);\n\n\t\t\tif ((hostname != null) && !hostname.isEmpty()) {\n\t\t\t\tList<ComponentConnection> conns = service.getRefObject();\n\n\t\t\t\tif (conns != null) {\n\t\t\t\t\tfor (ComponentConnection conn : conns) {\n\t\t\t\t\t\tboolean moreConnections = removeComponentConnection(conn.getDomain(), conn);\n\n\t\t\t\t\t\tif (!moreConnections) {\n\t\t\t\t\t\t\tremoveRoutings(conn.getDomain());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\n\t\t\t\t\t// Nothing to do, let's log this however.\n\t\t\t\t\tlog.finer(\"Closing XMPPIOService has not yet set ComponentConnection as RefObject: \" + hostname +\n\t\t\t\t\t\t\t\t\t  \", id: \" + service.getUniqueId());\n\t\t\t\t}\n\t\t\t} else {\n\n\t\t\t\t// Stopped service which hasn't sent initial stream open yet\n\t\t\t\tlog.finer(\"Stopped service which hasn't sent initial stream open yet\" + service.getUniqueId());\n\t\t\t}\n\n\t\t\tConnectionType type = service.connectionType();\n\n\t\t\tif (type == ConnectionType.connect) {\n\t\t\t\tCompRepoItem connItem = (CompRepoItem) sessionData.get(REPO_ITEM_KEY);\n\t\t\t\tif (connItem == null) {\n\t\t\t\t\taddWaitingTask(sessionData);\n\t\t\t\t} else {\n\t\t\t\t\tCompRepoItem newItem = repo.getItem(connItem.getKey());\n\t\t\t\t\tif (newItem == connItem) {\n\t\t\t\t\t\taddWaitingTask(sessionData);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// reconnectService(sessionData, connectionDelay);\n\t\t\t}    // end of if (type == ConnectionType.connect)\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic void setRepo(ComponentRepository<CompRepoItem> repo) {\n\t\tthis.repo = repo;\n\t\trepo.addRepoChangeListener(this);\n\t}\n\n\t@Override\n\tpublic void start() {\n\t\tsuper.start();\n\n\t\t// Activate all connections for which parameters are defined in the\n\t\t// repository\n\t\tfor (CompRepoItem repoItem : repo) {\n\t\t\titemUpdated(repoItem);\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void tlsHandshakeCompleted(ComponentIOService service) {\n\t}\n\n\t@Override\n\tpublic void unbindHostname(String hostname, ComponentIOService serv) {\n\t\tCopyOnWriteArrayList<ComponentConnection> conns = connections.get(hostname);\n\n\t\tif (conns != null) {\n\t\t\tComponentConnection conn = null;\n\n\t\t\tfor (ComponentConnection componentConnection : conns) {\n\t\t\t\tif (componentConnection.getService() == serv) {\n\t\t\t\t\tconn = componentConnection;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (conn != null) {\n\t\t\t\tboolean moreConnections = removeComponentConnection(conn.getDomain(), conn);\n\n\t\t\t\tif (!moreConnections) {\n\t\t\t\t\tremoveRoutings(conn.getDomain());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean writePacketToSocket(ComponentIOService ios, Packet p) {\n\n\t\t// String xmlns = (String)ios.getSessionData().get(\"xmlns\");\n\t\t// if (xmlns != null) {\n\t\t// p.getElement().setXMLNS(xmlns);\n\t\t// }\n\t\tp.getElement().removeAttribute(\"xmlns\");\n\n\t\treturn super.writePacketToSocket(ios, p);\n\t}\n\n\t@Override\n\tpublic void xmppStreamClosed(ComponentIOService serv) {\n\t}\n\n\t@Override\n\tpublic String[] xmppStreamOpened(ComponentIOService serv, Map<String, String> attribs) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Stream opened: \" + serv.getRemoteAddress() + \", xmlns: \" + attribs.get(\"xmlns\") + \", type: \" +\n\t\t\t\t\t\t\t   serv.connectionType().toString() + \", uniqueId=\" + serv.getUniqueId() + \", to=\" +\n\t\t\t\t\t\t\t   attribs.get(\"to\"));\n\t\t}\n\n\t\tString s_xmlns = attribs.get(\"xmlns\");\n\t\tString result = null;\n\t\tStreamOpenHandler handler = streamOpenHandlers.get(s_xmlns);\n\n\t\tif ((handler == null) || (s_xmlns == null)) {\n\t\t\tlog.finest(\"unknownXMLNSHandler is processing request\");\n\t\t\tresult = unknownXMLNSHandler.streamOpened(serv, attribs, this);\n\t\t} else {\n\t\t\tlog.finest(handler.getClass().getName() + \" is processing request\");\n\t\t\tresult = handler.streamOpened(serv, attribs, this);\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Sending back: \" + result);\n\t\t}\n\n\t\treturn result == null ? null : new String[] { result };\n\t}\n\n\t@Override\n\tpublic void itemAdded(CompRepoItem repoItem) {\n\t\tif (repoItem.getPort() > 0) {\n\t\t\tString[] remote_host = PORT_IFC_PROP_VAL;\n\t\t\tString remote_domain = repoItem.getRemoteHost();\n\n\t\t\tif (repoItem.getRemoteHost() != null) {\n\t\t\t\tremote_host = repoItem.getRemoteHost().split(\";\");\n\n\t\t\t\t// The first item on the list is always the remote domain name, if\n\t\t\t\t// there are\n\t\t\t\t// more entries, the rest is just addresses to connect to for this\n\t\t\t\t// domain\n\t\t\t\tremote_domain = remote_host[0];\n\t\t\t\tif (remote_host.length > 1) {\n\n\t\t\t\t\t// Remove the first entry as this is domain name, whereas the rest\n\t\t\t\t\t// is the\n\t\t\t\t\t// address to connect to.\n\t\t\t\t\tString[] remote_host_copy = new String[remote_host.length - 1];\n\n\t\t\t\t\tSystem.arraycopy(remote_host, 1, remote_host_copy, 0, remote_host_copy.length);\n\t\t\t\t\tremote_host = remote_host_copy;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tList<ConnectionOpenListener> listeners = new ArrayList<>();\n\t\t\tfor (String r_host : remote_host) {\n\t\t\t\tMap<String, Object> port_props = new LinkedHashMap<String, Object>();\n\n\t\t\t\tport_props.put(PORT_KEY, repoItem.getPort());\n\t\t\t\tif (repoItem.getDomain() != null) {\n\t\t\t\t\tport_props.put(PORT_LOCAL_HOST_PROP_KEY, repoItem.getDomain());\n\t\t\t\t}\n\t\t\t\tport_props.put(PORT_REMOTE_HOST_PROP_KEY, remote_domain);\n\t\t\t\tport_props.put(ComponentIOService.HOSTNAME_KEY, remote_domain);\n\t\t\t\tport_props.put(PORT_TYPE_PROP_KEY, repoItem.getConnectionType());\n\t\t\t\tport_props.put(PORT_SOCKET_PROP_KEY, repoItem.getSocket());\n\t\t\t\tport_props.put(PORT_IFC_PROP_KEY, new String[]{r_host});\n\t\t\t\tport_props.put(MAX_RECONNECTS_PROP_KEY, (int) (120 * MINUTE));\n\t\t\t\tport_props.put(REPO_ITEM_KEY, repoItem);\n\t\t\t\tlog.config(\"Starting connection: \" + port_props);\n\t\t\t\tConnectionOpenListener connectionOpenListener = startService(port_props);\n\t\t\t\tif (connectionOpenListener != null) {\n\t\t\t\t\tlisteners.add(connectionOpenListener);\n\t\t\t\t}\n\t\t\t}\n\t\t\tactiveConnections.put(repoItem.getKey(), listeners);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void itemUpdated(CompRepoItem item) {\n\t\titemRemoved(item);\n\t\titemAdded(item);\n\t}\n\n\t@Override\n\tpublic void itemRemoved(CompRepoItem item) {\n\t\tList<ConnectionOpenListener> listeners = activeConnections.get(item.getKey());\n\t\tif (listeners != null) {\n\t\t\tfor (ConnectionOpenListener listener : listeners) {\n\t\t\t\treleaseListener(listener);\n\t\t\t}\n\t\t}\n\n\t\t// key is remote host name! so we need to find connections which are actually related\n\t\tfor (List<ComponentConnection> componentConnections : connections.values()) {\n\t\t\tfor (ComponentConnection connection : componentConnections) {\n\t\t\t\tCompRepoItem connItem = (CompRepoItem) connection.getService().getSessionData().get(REPO_ITEM_KEY);\n\t\t\t\tif (connItem.getKey().equals(item.getKey())) {\n\t\t\t\t\tconnection.getService().stop();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t@Override\n\tprotected String getDefTrafficThrottling() {\n\t\treturn \"xmpp:25m:0:disc,bin:20000m:0:disc\";\n\t}\n\n\t@Override\n\tprotected long getMaxInactiveTime() {\n\t\treturn 1000 * 24 * HOUR;\n\t}\n\n\t@Override\n\tprotected Integer getMaxQueueSize(int def) {\n\t\treturn def * 10;\n\t}\n\n\t@Override\n\tprotected ComponentIOService getXMPPIOService(Packet p) {\n\t\tif (p.getStanzaTo() == null) {\n\n\t\t\t// This is a bad packet actually\n\t\t\treturn null;\n\t\t}\n\n\t\tComponentIOService result = null;\n\t\tString hostname = p.getStanzaTo().getDomain();\n\t\tCopyOnWriteArrayList<ComponentConnection> conns = connections.get(hostname);\n\n\t\t// If there is no connections list for this domain and routings are set to *\n\t\t// we use the first available list.\n\t\tfor (CopyOnWriteArrayList<ComponentConnection> c : connections.values()) {\n\n\t\t\t// Is there a better way to take the first available element?\n\t\t\tif ((c.size() > 0) && \".*\".equals(c.get(0).getService().getRoutings())) {\n\t\t\t\tconns = c;\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (conns != null) {\n\n\t\t\t// First we check whether the receiver has sent to us a packet through one\n\t\t\t// of connections as this would be wise to send response on the same connection\n\t\t\tfor (ComponentConnection componentConnection : conns) {\n\t\t\t\tComponentIOService serv = componentConnection.getService();\n\n\t\t\t\tif ((serv != null) && serv.isConnected() && serv.isRecentJID(p.getStanzaTo())) {\n\t\t\t\t\tresult = serv;\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Now, load balancer selects the best connection to send the packet\n\t\t\tif ((result == null) && (conns.size() > 1)) {\n\t\t\t\tCompRepoItem cmp_repo_item = getCompRepoItem(hostname);\n\n\t\t\t\tif (cmp_repo_item == null) {\n\t\t\t\t\tcmp_repo_item = repo.getItem(p.getStanzaFrom().getDomain());\n\t\t\t\t}\n\n\t\t\t\tLoadBalancerIfc lb = cmp_repo_item.getLoadBalancer();\n\n\t\t\t\tresult = lb.selectConnection(p, conns);\n\t\t\t}\n\n\t\t\t// The above algorithm did not work for some reason. Now trying\n\t\t\t// traditional way to send a packet to the first available and working\n\t\t\t// connection\n\t\t\tif (result == null) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\n\t\t\t\t\t\t\t\"LB could not select connection, or there is only one connection, trying traditional way\");\n\t\t\t\t}\n\t\t\t\tfor (ComponentConnection componentConnection : conns) {\n\t\t\t\t\tComponentIOService serv = componentConnection.getService();\n\n\t\t\t\t\tif (serv != null) {\n\t\t\t\t\t\tif (serv.isConnected()) {\n\t\t\t\t\t\t\tresult = serv;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlog.log(Level.CONFIG, \"Service is not connected for connection for hostname: \" + hostname);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlog.log(Level.CONFIG, \"Service is null for connection for hostname: \" + hostname);\n\t\t\t\t\t}\n\t\t\t\t\tif (result != null) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.CONFIG, \"No ext connection for hostname: \" + hostname);\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Selected connection: \" + result);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tprotected ComponentIOService getXMPPIOServiceInstance() {\n\t\treturn new ComponentIOService();\n\t}\n\n\t@Override\n\tprotected boolean isHighThroughput() {\n\t\treturn true;\n\t}\n\n\tprivate void updateServiceDiscoForConnection(String hostname, ComponentIOService serv) {\n\n\t\t// Cut off the first, component part\n\t\tint idx = hostname.indexOf(\".\");\n\t\tString newhostname = hostname.substring(idx + 1);\n\n\t\tif (!isLocalDomain(newhostname)) {\n\t\t\tupdateServiceDiscoveryItem(newhostname, \"ext\", serv.getUniqueId(), true);\n\t\t} else {\n\n\t\t\t// We don't do the trick because this would break stuff\n\t\t}\n\t}\n\n\tprivate synchronized void addComponentConnection(String hostname, ComponentIOService s) {\n\t\tComponentConnection conn = new ComponentConnection(hostname, s);\n\t\tList<ComponentConnection> refObject = s.getRefObject();\n\n\t\tif (refObject == null) {\n\t\t\trefObject = new CopyOnWriteArrayList<ComponentConnection>();\n\t\t}\n\n\t\t// keep all connections sorted, fix for #983\n\t\tsynchronized (refObject) {\n\t\t\trefObject.add(conn);\n\n\t\t\t// workaround to sort CopyOnWriteArrayList\n\t\t\tComponentConnection[] arr_list = refObject.toArray(new ComponentConnection[refObject.size()]);\n\n\t\t\tArrays.sort(arr_list);\n\t\t\trefObject = new CopyOnWriteArrayList<ComponentConnection>(arr_list);\n\t\t}\n\t\ts.setRefObject(refObject);\n\n\t\tCopyOnWriteArrayList<ComponentConnection> conns = connections.get(hostname);\n\n\t\tif (conns == null) {\n\t\t\tconns = new CopyOnWriteArrayList<ComponentConnection>();\n\t\t}\n\n\t\t// Not very optimal, however this does not happen (should not) very often\n\t\t// and the data collections is optimized for fast object retrieval\n\t\t// by index (round robin balance for example)\n\t\t// keep all connections sorted, fix for #983\n\t\tboolean result;\n\n\t\tsynchronized (conns) {\n\t\t\tresult = conns.add(conn);\n\n\t\t\t// workaround to sort CopyOnWriteArrayList\n\t\t\tComponentConnection[] arr_list = conns.toArray(new ComponentConnection[conns.size()]);\n\n\t\t\tArrays.sort(arr_list);\n\t\t\tconns = new CopyOnWriteArrayList<ComponentConnection>(arr_list);\n\t\t}\n\t\tconnections.put(hostname, conns);\n\t\tif (result) {\n\t\t\tlog.finer(\"A new component connection added for: \" + hostname);\n\t\t} else {\n\t\t\tlog.fine(\"A new component connection NOT added for: \" + hostname);\n\t\t}\n\t}\n\n\tprivate synchronized boolean removeComponentConnection(String hostname, ComponentConnection conn) {\n\t\tboolean result = false;\n\t\tCopyOnWriteArrayList<ComponentConnection> conns = connections.get(hostname);\n\n\t\tif (conns != null) {\n\n\t\t\t// This is slow, however this does not happen (should not) very often\n\t\t\t// and the data collections is optimized for fast object retrieval\n\t\t\t// by index (round robin balance for example)\n\t\t\tboolean removed = conns.remove(conn);\n\n\t\t\tif (removed) {\n\t\t\t\tlog.finer(\"A component connection removed for: \" + hostname);\n\t\t\t} else {\n\t\t\t\tlog.fine(\"A component connection NOT removed for: \" + hostname);\n\t\t\t}\n\t\t\tfor (ComponentConnection compCon : conns) {\n\t\t\t\tComponentIOService serv = compCon.getService();\n\n\t\t\t\tif ((serv != null) && serv.isConnected()) {\n\n\t\t\t\t\t// There is still an active connection for this host\n\t\t\t\t\tresult = true;\n\t\t\t\t} else {\n\t\t\t\t\tlog.warning(\"Null or disconnected service for ComponentConnection for host: \" + hostname);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\n\t\t\t\t\t\"That should not happen, ComponentConnection is not null but \" + \"the collection is: \" + hostname);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate void removeRoutings(String hostname) {\n\t\tString[] routings = new String[]{hostname, \".*@\" + hostname, \".*\\\\.\" + hostname};\n\n\t\tupdateRoutings(routings, false);\n\n\t\t// removeRouting(serv.getRemoteHost());\n\t\t// String addr = (String)sessionData.get(PORT_REMOTE_HOST_PROP_KEY);\n\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\tlog.fine(\"Disonnected from: \" + hostname);\n\t\t}\n\t\tupdateServiceDiscoveryItem(hostname, null, \"XEP-0114 disconnected\", false);\n\t\tremoveComponentDomain(hostname);\n\t}\n\n\tprivate void updateRoutings(String[] routings, boolean add) {\n\t\tif (add) {\n\t\t\tfor (String route : routings) {\n\t\t\t\ttry {\n\t\t\t\t\taddRegexRouting(route);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.warning(\"Can not add regex routing '\" + route + \"' : \" + e);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor (String route : routings) {\n\t\t\t\ttry {\n\t\t\t\t\tremoveRegexRouting(route);\n\t\t\t\t\tlog.fine(\"Removed routings: \" + route);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.warning(\"Can not remove regex routing '\" + route + \"' : \" + e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tlog.finest(\"All regex routings: \" + getRegexRoutings().toString());\n\t}\n\n\tprivate class AuthenticationTimerTask\n\t\t\textends TimerTask {\n\n\t\tprivate ComponentIOService serv = null;\n\n\t\tprivate AuthenticationTimerTask(ComponentIOService serv) {\n\t\t\tthis.serv = serv;\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tif (!serv.isAuthenticated()) {\n\t\t\t\tserv.stop();\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/ComponentProtocolHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext;\n\nimport tigase.server.Packet;\nimport tigase.xml.Element;\n\nimport java.util.List;\n\n/**\n * Created: Oct 7, 2009 5:54:56 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface ComponentProtocolHandler {\n\n\tpublic static final String REPO_ITEM_KEY = \"repo-item\";\n\n//public static final String AUTHENTICATED_KEY = \"authenticated\";\n\n\tpublic static final String EXTCOMP_BIND_HOSTNAMES_PROP_KEY = \"bind-ext-hostnames\";\n\n\tvoid authenticated(ComponentIOService serv);\n\n\tvoid authenticationFailed(ComponentIOService serv, Packet packet);\n\n\tvoid bindHostname(String hostname, ComponentIOService serv);\n\n\tCompRepoItem getCompRepoItem(String hostname);\n\n\tExtProcessor getProcessor(String string);\n\n\tList<Element> getStreamFeatures(ComponentIOService serv);\n\n\tStreamOpenHandler getStreamOpenHandler(String xmlns);\n\n\tString newPacketId(String prefix);\n\n\tvoid unbindHostname(String hostname, ComponentIOService serv);\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/ComponentProtocolManager.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext;\n\nimport tigase.component.AbstractKernelBasedComponent;\nimport tigase.component.ComponenScriptCommandProcessor;\nimport tigase.component.adhoc.AdHocCommandManager;\nimport tigase.component.modules.impl.AdHocCommandModule;\nimport tigase.component.modules.impl.DiscoveryModule;\nimport tigase.db.comp.ComponentRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.DisableDisco;\n\nimport javax.script.Bindings;\n\n@Bean(name=\"ext-man\", parent = Kernel.class, active = false)\n@ConfigType({ConfigTypeEnum.DefaultMode})\npublic class ComponentProtocolManager extends AbstractKernelBasedComponent implements DisableDisco {\n\n\t@Inject\n\tprivate CompCompDBRepository repo;\n\n\t@Override\n\tpublic String getComponentVersion() {\n\t\tString version = this.getClass().getPackage().getImplementationVersion();\n\t\treturn version == null ? \"0.0.0\" : version;\n\t}\n\t\n\t@Override\n\tpublic String getDiscoCategoryType() {\n\t\treturn \"generic\";\n\t}\n\n\t@Override\n\tpublic String getDiscoDescription() {\n\t\treturn \"External Component Manager\";\n\t}\n\n\t@Override\n\tpublic boolean isDiscoNonAdmin() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tprotected void registerModules(Kernel kernel) {\n\t\tkernel.registerBean(CompCompDBRepository.class).setActive(true).exec();\n\t\tkernel.registerBean(DiscoveryModule.class).setActive(true).exec();\n\t\tkernel.registerBean(ComponenScriptCommandProcessor.class).setActive(true).exec();\n\t\tkernel.registerBean(AdHocCommandManager.class).setActive(true).exec();\n\t\tkernel.registerBean(AdHocCommandModule.class).setActive(true).exec();\n\t}\n\n\t@Override\n\tpublic void initBindings(Bindings binds) {\n\t\tsuper.initBindings(binds);\n\t\tbinds.put(ComponentRepository.COMP_REPO_BIND, repo);\n\t\tbinds.put(\"kernel\", kernel);\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tsuper.initialize();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/ExtProcessor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext;\n\nimport tigase.server.Packet;\nimport tigase.xml.Element;\n\nimport java.util.List;\nimport java.util.Queue;\n\n/**\n * Created: Oct 1, 2009 8:40:36 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface ExtProcessor {\n\n\tString getId();\n\n\tList<Element> getStreamFeatures(ComponentIOService serv, ComponentProtocolHandler handler);\n\n\tboolean process(Packet p, ComponentIOService serv, ComponentProtocolHandler handler, Queue<Packet> results);\n\n\tvoid startProcessing(Packet p, ComponentIOService serv, ComponentProtocolHandler handler, Queue<Packet> results);\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/ManagerCompDBRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\n\n@Bean(name = \"repository\", parent = ComponentProtocolManager.class, active = true)\n@ConfigType({ConfigTypeEnum.DefaultMode})\npublic class ManagerCompDBRepository extends CompCompDBRepository {\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/ServerCompDBRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\n\n@Bean(name = \"repository\", parent = ComponentProtocol.class, active = true)\n@ConfigType({ConfigTypeEnum.DefaultMode})\npublic class ServerCompDBRepository\n\t\textends AbstractCompDBRepository {\n\n\tpublic ServerCompDBRepository() {\n\t\tsuper(\"server-default-items-list\");\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/StreamOpenHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext;\n\nimport java.util.Map;\n\n/**\n * Created: Oct 2, 2009 12:15:43 AM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface StreamOpenHandler {\n\n\tpublic static final String XMLNS_KEY = \"xmlns\";\n\n\tString[] getXMLNSs();\n\n\tString serviceStarted(ComponentIOService s);\n\n\tString streamOpened(ComponentIOService s, Map<String, String> attribs, ComponentProtocolHandler handler);\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/handlers/BindProcessor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext.handlers;\n\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.ext.ComponentIOService;\nimport tigase.server.ext.ComponentProtocolHandler;\nimport tigase.server.ext.ExtProcessor;\nimport tigase.xml.Element;\nimport tigase.xmpp.StanzaType;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.server.ext.ComponentProtocolHandler.EXTCOMP_BIND_HOSTNAMES_PROP_KEY;\n\n/**\n * Created: Nov 2, 2009 2:37:18 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class BindProcessor\n\t\timplements ExtProcessor {\n\n\tprivate static final String EL_NAME = \"bind\";\n\tprivate static final String[] IQ_BIND_HOSTNAME_PATH = {\"iq\", \"bind\", \"hostname\"};\n\tprivate static final String[] IQ_UNBIND_PATH = {\"iq\", \"unbind\"};\n\n\tprivate static final Logger log = Logger.getLogger(BindProcessor.class.getName());\n\tprivate static final String XMLNS = \"urn:xmpp:component:0\";\n\tprivate static final String ID = EL_NAME;\n\tprivate static final Element FEATURES = new Element(EL_NAME, new String[]{\"xmlns\"}, new String[]{XMLNS});\n\n\t@Override\n\tpublic String getId() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic List<Element> getStreamFeatures(ComponentIOService serv, ComponentProtocolHandler handler) {\n\t\treturn Arrays.asList(FEATURES);\n\t}\n\n\t@Override\n\tpublic boolean process(Packet p, ComponentIOService serv, ComponentProtocolHandler handler, Queue<Packet> results) {\n\t\tif (p.isXMLNSStaticStr(Iq.IQ_BIND_PATH, XMLNS)) {\n\t\t\tif ((p.getType() == StanzaType.set) && serv.isAuthenticated()) {\n\t\t\t\tString hostname = p.getElemCDataStaticStr(IQ_BIND_HOSTNAME_PATH);\n\n\t\t\t\thandler.bindHostname(hostname, serv);\n\t\t\t\tresults.offer(Packet.packetInstance(okResult(p.getElement()), null, null));\n\t\t\t} else {\n\t\t\t\tlog.fine(\"Ok result received: \" + p.toString());\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\t\tif (p.isXMLNSStaticStr(IQ_UNBIND_PATH, XMLNS)) {\n\t\t\tif ((p.getType() == StanzaType.set) && serv.isAuthenticated()) {\n\t\t\t\tString hostname = p.getElemCDataStaticStr(IQ_BIND_HOSTNAME_PATH);\n\n\t\t\t\thandler.unbindHostname(hostname, serv);\n\t\t\t\tresults.offer(Packet.packetInstance(okResult(p.getElement()), null, null));\n\t\t\t} else {\n\t\t\t\tlog.fine(\"Ok result received: \" + p.toString());\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void startProcessing(Packet p, ComponentIOService serv, ComponentProtocolHandler handler,\n\t\t\t\t\t\t\t\tQueue<Packet> results) {\n\t\tString[] hostnames = (String[]) serv.getSessionData().get(EXTCOMP_BIND_HOSTNAMES_PROP_KEY);\n\n\t\tif (hostnames != null) {\n\t\t\tfor (String host : hostnames) {\n\t\t\t\tif (!host.isEmpty()) {\n\t\t\t\t\tPacket bind_p = Packet.packetInstance(newBindElement(host, handler), null, null);\n\n\t\t\t\t\tlog.log(Level.CONFIG, \"Generating hostname bind packet: \" + bind_p.toString());\n\t\t\t\t\tresults.offer(bind_p);\n\t\t\t\t} else {\n\t\t\t\t\tlog.warning(\"Empty hostname set for bind...\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate Element newBindElement(String host, ComponentProtocolHandler handler) {\n\t\tElement result = new Element(\"iq\", new String[]{\"type\", \"id\"},\n\t\t\t\t\t\t\t\t\t new String[]{\"set\", handler.newPacketId(\"bind\")});\n\t\tElement bind = new Element(EL_NAME, new Element[]{new Element(\"hostname\", host)}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t   new String[]{XMLNS});\n\n\t\tresult.addChild(bind);\n\n\t\treturn result;\n\t}\n\n\tprivate Element okResult(Element elem) {\n\t\tElement result = elem.clone();\n\n\t\tresult.setAttribute(\"type\", \"result\");\n\n\t\treturn result;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/handlers/ComponentAcceptStreamOpenHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext.handlers;\n\nimport tigase.server.Packet;\nimport tigase.server.ext.*;\n\nimport java.util.ArrayDeque;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.UUID;\nimport java.util.logging.Logger;\n\nimport static tigase.server.ext.ComponentProtocolHandler.REPO_ITEM_KEY;\n\n/**\n * Created: Oct 7, 2009 5:51:47 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class ComponentAcceptStreamOpenHandler\n\t\timplements StreamOpenHandler {\n\n\tpublic static final String XMLNS = \"jabber:component:accept\";\n\tprivate static final Logger log = Logger.getLogger(ComponentAcceptStreamOpenHandler.class.getName());\n\n\tprivate String[] xmlnss = new String[]{XMLNS};\n\n\t@Override\n\tpublic String[] getXMLNSs() {\n\t\treturn xmlnss;\n\t}\n\n\t@Override\n\tpublic String serviceStarted(ComponentIOService serv) {\n\t\tswitch (serv.connectionType()) {\n\t\t\tcase connect:\n\t\t\t\tCompRepoItem repoItem = (CompRepoItem) serv.getSessionData().get(REPO_ITEM_KEY);\n\t\t\t\tString r_host = (String) serv.getSessionData().get(\"remote-host\");\n\n\t\t\t\t// Send init xmpp stream here\n\t\t\t\tserv.getSessionData().put(ComponentIOService.HOSTNAME_KEY, r_host);\n\n\t\t\t\t// This should be done only, after authentication is completed\n\t\t\t\t// addComponentConnection(hostname, serv);\n\t\t\t\tString data = \"<stream:stream\" + \" xmlns='\" + XMLNS + \"'\" +\n\t\t\t\t\t\t\" xmlns:stream='http://etherx.jabber.org/streams'\" + \" to='\" + repoItem.getDomain() + \"'\" + \">\";\n\n\t\t\t\treturn data;\n\n\t\t\tdefault:\n\n\t\t\t\t// Do nothing, more/some data should come soon...\n\t\t\t\tbreak;\n\t\t}    // end of switch (service.connectionType())\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String streamOpened(ComponentIOService serv, Map<String, String> attribs, ComponentProtocolHandler handler) {\n\t\tserv.getSessionData().put(XMLNS_KEY, XMLNS);\n\n\t\tswitch (serv.connectionType()) {\n\t\t\tcase connect: {\n\t\t\t\tString id = attribs.get(\"id\");\n\n\t\t\t\tif (id == null) {\n\t\t\t\t\tserv.stop();\n\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\n\t\t\t\tserv.getSessionData().put(ComponentIOService.SESSION_ID_KEY, id);\n\n\t\t\t\tExtProcessor proc = handler.getProcessor(\"handshake\");\n\n\t\t\t\tif (proc != null) {\n\t\t\t\t\tQueue<Packet> results = new ArrayDeque<Packet>();\n\n\t\t\t\t\tproc.startProcessing(null, serv, handler, results);\n\n\t\t\t\t\tif (results != null) {\n\t\t\t\t\t\tStringBuilder sb = new StringBuilder();\n\n\t\t\t\t\t\tfor (Packet p : results) {\n\t\t\t\t\t\t\tsb.append(p.getElement().toString());\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn sb.toString();\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tlog.warning(\"Required processor is not available: 'handshake'\");\n\t\t\t\t}\n\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tcase accept: {\n\t\t\t\tString hostname = attribs.get(\"to\");\n\t\t\t\tCompRepoItem repoItem = handler.getCompRepoItem(hostname);\n\n\t\t\t\tif (repoItem != null) {\n\t\t\t\t\tserv.getSessionData().put(REPO_ITEM_KEY, repoItem);\n\t\t\t\t\tserv.getSessionData().put(ComponentIOService.HOSTNAME_KEY, hostname);\n\t\t\t\t\tlog.finest(\"CompRepoItem for \" + hostname + \" set: \" + repoItem.toString());\n\n\t\t\t\t\tString id = UUID.randomUUID().toString();\n\n\t\t\t\t\tserv.getSessionData().put(ComponentIOService.SESSION_ID_KEY, id);\n\t\t\t\t\tlog.finest(\"ID generated and set: \" + id);\n\n\t\t\t\t\t// This should be done only, after authentication is completed\n\t\t\t\t\t// addComponentConnection(hostname, serv);\n\t\t\t\t\treturn \"<stream:stream\" + \" xmlns='\" + XMLNS + \"'\" +\n\t\t\t\t\t\t\t\" xmlns:stream='http://etherx.jabber.org/streams'\" + \" from='\" + hostname + \"'\" + \" id='\" +\n\t\t\t\t\t\t\tid + \"'\" + \">\";\n\t\t\t\t} else {\n\t\t\t\t\treturn \"<stream:stream\" + \" xmlns='\" + XMLNS + \"'\" +\n\t\t\t\t\t\t\t\" xmlns:stream='http://etherx.jabber.org/streams'\" + \" from='\" + hostname + \"'>\" +\n\t\t\t\t\t\t\t\"<stream:error>\" + \"<host-unknown xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>\" +\n\t\t\t\t\t\t\t\"</stream:error></stream:stream>\";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdefault:\n\n\t\t\t\t// Do nothing, more data should come soon...\n\t\t\t\tbreak;\n\t\t}    // end of switch (service.connectionType())\n\n\t\treturn null;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/handlers/ComponentConnectStreamOpenHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext.handlers;\n\nimport tigase.server.ext.ComponentIOService;\nimport tigase.server.ext.ComponentProtocolHandler;\nimport tigase.server.ext.StreamOpenHandler;\n\nimport java.util.Map;\n\n/**\n * Created: Oct 7, 2009 5:50:34 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class ComponentConnectStreamOpenHandler\n\t\timplements StreamOpenHandler {\n\n\tpublic static final String XMLNS = \"jabber:component:connect\";\n\n\tprivate String[] xmlnss = new String[]{XMLNS};\n\n\t@Override\n\tpublic String[] getXMLNSs() {\n\t\treturn xmlnss;\n\t}\n\n\t@Override\n\tpublic String serviceStarted(ComponentIOService s) {\n\t\tthrow new UnsupportedOperationException(\"Not supported yet.\");\n\t}\n\n\t@Override\n\tpublic String streamOpened(ComponentIOService serv, Map<String, String> attribs, ComponentProtocolHandler handler) {\n\n\t\t// Not sure if this is really used. For sure it is not well documented\n\t\t// and perhaps it is not worth implementing, unless someone requests it\n\t\tthrow new UnsupportedOperationException(\"Not supported yet.\");\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/handlers/HandshakeProcessor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext.handlers;\n\nimport tigase.server.Packet;\nimport tigase.server.ext.CompRepoItem;\nimport tigase.server.ext.ComponentIOService;\nimport tigase.server.ext.ComponentProtocolHandler;\nimport tigase.server.ext.ExtProcessor;\nimport tigase.util.Algorithms;\nimport tigase.xml.Element;\n\nimport java.security.NoSuchAlgorithmException;\nimport java.util.List;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.server.ext.ComponentProtocolHandler.REPO_ITEM_KEY;\n\n/**\n * Created: Oct 21, 2009 1:58:56 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class HandshakeProcessor\n\t\timplements ExtProcessor {\n\n\tprivate static final Logger log = Logger.getLogger(HandshakeProcessor.class.getName());\n\tprivate static final String EL_NAME = \"handshake\";\n\tprivate static final String ID = EL_NAME;\n\n\t@Override\n\tpublic String getId() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic List<Element> getStreamFeatures(ComponentIOService serv, ComponentProtocolHandler handler) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean process(Packet p, ComponentIOService serv, ComponentProtocolHandler handler, Queue<Packet> results) {\n\t\tboolean result = false;\n\n\t\tif (p.getElemName() == EL_NAME) {\n\t\t\tresult = true;\n\n\t\t\tswitch (serv.connectionType()) {\n\t\t\t\tcase connect: {\n\t\t\t\t\tString data = p.getElemCData();\n\n\t\t\t\t\tif (data == null) {\n\n\t\t\t\t\t\t// According to XEP-0114 the authentication was successful\n\t\t\t\t\t\thandler.authenticated(serv);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlog.warning(\"Incorrect packet received: \" + p);\n\t\t\t\t\t\tserv.stop();\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase accept: {\n\t\t\t\t\tString digest = p.getElemCData();\n\t\t\t\t\tCompRepoItem comp = (CompRepoItem) serv.getSessionData()\n\t\t\t\t\t\t\t.get(ComponentProtocolHandler.REPO_ITEM_KEY);\n\t\t\t\t\tString id = (String) serv.getSessionData().get(ComponentIOService.SESSION_ID_KEY);\n\t\t\t\t\tString secret = comp.getAuthPasswd();\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tString loc_digest = Algorithms.hexDigest(id, secret, \"SHA\");\n\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.finest(\n\t\t\t\t\t\t\t\t\t\"Calculating digest: id=\" + id + \", secret=\" + secret + \", digest=\" + loc_digest);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Password digest matches, authentication OK\n\t\t\t\t\t\tif ((digest != null) && digest.equals(loc_digest)) {\n\t\t\t\t\t\t\thandler.authenticated(serv);\n\n\t\t\t\t\t\t\tPacket resp = Packet.packetInstance(new Element(\"handshake\"), null, null);\n\n\t\t\t\t\t\t\tresults.offer(resp);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlog.log(Level.CONFIG, \"Handshaking passwords don't match, disconnecting...\");\n\t\t\t\t\t\t\tserv.stop();\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tlog.log(Level.SEVERE, \"Handshaking error.\", e);\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tdefault:\n\n\t\t\t\t\t// Do nothing, more data should come soon...\n\t\t\t\t\tbreak;\n\t\t\t}    // end of switch (service.connectionType())\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic void startProcessing(Packet p, ComponentIOService serv, ComponentProtocolHandler handler,\n\t\t\t\t\t\t\t\tQueue<Packet> results) {\n\t\tString secret = ((CompRepoItem) serv.getSessionData().get(REPO_ITEM_KEY)).getAuthPasswd();\n\n\t\ttry {\n\t\t\tString id = (String) serv.getSessionData().get(ComponentIOService.SESSION_ID_KEY);\n\t\t\tString digest = Algorithms.hexDigest(id, secret, \"SHA\");\n\t\t\tPacket result = Packet.packetInstance(new Element(EL_NAME, digest), null, null);\n\n\t\t\tresults.offer(result);\n\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\tlog.log(Level.SEVERE, \"Can not generate digest for pass phrase.\", e);\n\t\t\tserv.stop();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/handlers/JabberClientStreamOpenHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext.handlers;\n\nimport tigase.server.ext.CompRepoItem;\nimport tigase.server.ext.ComponentIOService;\nimport tigase.server.ext.ComponentProtocolHandler;\nimport tigase.server.ext.StreamOpenHandler;\nimport tigase.xml.Element;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.logging.Logger;\n\nimport static tigase.server.ext.ComponentProtocolHandler.REPO_ITEM_KEY;\n\n/**\n * Created: Oct 7, 2009 3:17:09 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class JabberClientStreamOpenHandler\n\t\timplements StreamOpenHandler {\n\n\tpublic static final String XMLNS = \"jabber:client\";\n\tprivate static final Logger log = Logger.getLogger(JabberClientStreamOpenHandler.class.getName());\n\n\tprivate String[] xmlnss = new String[]{XMLNS};\n\n\t@Override\n\tpublic String[] getXMLNSs() {\n\t\treturn xmlnss;\n\t}\n\n\t@Override\n\tpublic String serviceStarted(ComponentIOService serv) {\n\t\tswitch (serv.connectionType()) {\n\t\t\tcase connect:\n\t\t\t\tCompRepoItem repoItem = (CompRepoItem) serv.getSessionData().get(REPO_ITEM_KEY);\n\n\t\t\t\tString r_host = (String) serv.getSessionData().get(\"remote-host\");\n\t\t\t\t// Send init xmpp stream here\n\t\t\t\tserv.getSessionData().put(ComponentIOService.HOSTNAME_KEY, r_host);\n\n\t\t\t\t// This should be done only, after authentication is completed\n\t\t\t\t// addComponentConnection(hostname, serv);\n\t\t\t\tString data = \"<stream:stream\" + \" xmlns='\" + XMLNS + \"'\" +\n\t\t\t\t\t\t\" xmlns:stream='http://etherx.jabber.org/streams'\" + \" version ='1.0'\" + \" xml:lang='en'\" +\n\t\t\t\t\t\t\" from='\" + repoItem.getDomain() + \"'\" + \" to ='\" + r_host + \"'\" + \">\";\n\n\t\t\t\treturn data;\n\n\t\t\tdefault:\n\n\t\t\t\t// Do nothing, more/some data should come soon...\n\t\t\t\tbreak;\n\t\t}    // end of switch (service.connectionType())\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String streamOpened(ComponentIOService serv, Map<String, String> attribs, ComponentProtocolHandler handler) {\n\t\tserv.getSessionData().put(XMLNS_KEY, XMLNS);\n\n\t\tswitch (serv.connectionType()) {\n\t\t\tcase connect: {\n\t\t\t\tString id = attribs.get(\"id\");\n\n\t\t\t\tif (id != null) {\n\t\t\t\t\tserv.getSessionData().put(ComponentIOService.SESSION_ID_KEY, id);\n\t\t\t\t}\n\n\t\t\t\t// Do nothing, stream features should come first\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tcase accept: {\n\t\t\t\tString from_hostname = attribs.get(\"from\");\n\t\t\t\tString to_hostname = attribs.get(\"to\");\n\t\t\t\tCompRepoItem repoItem = handler.getCompRepoItem(from_hostname);\n\n\t\t\t\tserv.getSessionData().put(REPO_ITEM_KEY, repoItem);\n\t\t\t\tserv.getSessionData().put(ComponentIOService.HOSTNAME_KEY, from_hostname);\n\n\t\t\t\tString id = UUID.randomUUID().toString();\n\n\t\t\t\tserv.getSessionData().put(ComponentIOService.SESSION_ID_KEY, id);\n\n\t\t\t\t// This should be done only, after authentication is completed\n\t\t\t\t// addComponentConnection(hostname, serv);\n\t\t\t\tStringBuilder sb = new StringBuilder();\n\n\t\t\t\tsb.append(\"<stream:stream\" + \" xmlns='\" + XMLNS + \"'\" +\n\t\t\t\t\t\t\t\t  \" xmlns:stream='http://etherx.jabber.org/streams'\" + \" version ='1.0'\" +\n\t\t\t\t\t\t\t\t  \" xml:lang='en'\" + \" from='\" + to_hostname + \"'\" + \" to='\" + from_hostname + \"'\" +\n\t\t\t\t\t\t\t\t  \" id='\" + id + \"'\" + \"><stream:features>\");\n\n\t\t\t\tList<Element> features = handler.getStreamFeatures(serv);\n\n\t\t\t\tfor (Element element : features) {\n\t\t\t\t\tsb.append(element.toString());\n\t\t\t\t}\n\n\t\t\t\tsb.append(\"</stream:features>\");\n\n\t\t\t\treturn sb.toString();\n\t\t\t}\n\n\t\t\tdefault:\n\n\t\t\t\t// Do nothing, more data should come soon...\n\t\t\t\tbreak;\n\t\t}    // end of switch (service.connectionType())\n\n\t\treturn null;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/handlers/SASLProcessor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext.handlers;\n\nimport tigase.server.Packet;\nimport tigase.server.ext.CompRepoItem;\nimport tigase.server.ext.ComponentIOService;\nimport tigase.server.ext.ComponentProtocolHandler;\nimport tigase.server.ext.ExtProcessor;\nimport tigase.util.Base64;\nimport tigase.xml.Element;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Queue;\nimport java.util.logging.Logger;\n\nimport static tigase.server.ext.ComponentProtocolHandler.REPO_ITEM_KEY;\n\n/**\n * Created: Oct 31, 2009 11:06:57 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class SASLProcessor\n\t\timplements ExtProcessor {\n\n\tprivate static final Logger log = Logger.getLogger(SASLProcessor.class.getName());\n\tprivate static final String ID = \"sasl\";\n\tprivate static final String XMLNS = \"urn:ietf:params:xml:ns:xmpp-sasl\";\n\tprivate static final Element FEATURES = new Element(\"mechanisms\", new Element[]{new Element(\"mechanism\", \"PLAIN\")},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew String[]{\"urn:ietf:params:xml:ns:xmpp-sasl\"});\n\n\t@Override\n\tpublic String getId() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic List<Element> getStreamFeatures(ComponentIOService serv, ComponentProtocolHandler handler) {\n\t\tif (serv.getSessionData().get(ID) != null) {\n\t\t\treturn null;\n\t\t} else {\n\t\t\treturn Arrays.asList(FEATURES);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean process(Packet p, ComponentIOService serv, ComponentProtocolHandler handler, Queue<Packet> results) {\n\t\tif (p.isElement(\"auth\", XMLNS)) {\n\t\t\tString cdata = p.getElemCData();\n\t\t\tString[] credentials = decodeMessage(cdata);\n\n\t\t\tlog.fine(\"External credentials: \" + Arrays.toString(credentials));\n\n\t\t\tCompRepoItem repo_item = handler.getCompRepoItem(credentials[1]);\n\t\t\tboolean auth_ok = false;\n\n\t\t\tif (repo_item != null) {\n\t\t\t\tString local_password = repo_item.getAuthPasswd();\n\n\t\t\t\tif (local_password.equals(credentials[2])) {\n\t\t\t\t\tauth_ok = true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (auth_ok) {\n\t\t\t\tElement success = new Element(\"success\", new String[]{\"xmlns\"}, new String[]{XMLNS});\n\n\t\t\t\tresults.offer(Packet.packetInstance(success, null, null));\n\t\t\t\thandler.authenticated(serv);\n\t\t\t} else {\n\t\t\t\tElement failure = new Element(\"failure\", new Element[]{new Element(\"not-authorized\")},\n\t\t\t\t\t\t\t\t\t\t\t  new String[]{\"xmlns\"}, new String[]{XMLNS});\n\n\t\t\t\thandler.authenticationFailed(serv, Packet.packetInstance(failure, null, null));\n\t\t\t}\n\n\t\t\tserv.getSessionData().put(ID, ID);\n\n\t\t\treturn true;\n\t\t}\n\n\t\tif (p.isElement(\"success\", XMLNS)) {\n\t\t\thandler.authenticated(serv);\n\t\t\tserv.getSessionData().put(ID, ID);\n\n\t\t\treturn true;\n\t\t}\n\n\t\tif (p.isElement(\"abort\", XMLNS)) {\n\t\t\tserv.stop();\n\n\t\t\treturn true;\n\t\t}\n\n\t\tif (p.isElement(\"failure\", XMLNS)) {\n\t\t\tserv.stop();\n\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void startProcessing(Packet p, ComponentIOService serv, ComponentProtocolHandler handler,\n\t\t\t\t\t\t\t\tQueue<Packet> results) {\n\t\tCompRepoItem comp_item = (CompRepoItem) serv.getSessionData().get(REPO_ITEM_KEY);\n\t\tString domain = comp_item.getDomain();\n\t\tString secret = comp_item.getAuthPasswd();\n\t\tString challenge = encodeMessage(null, domain, secret);\n\n//  <auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl'\n//       mechanism='PLAIN'>AGp1bGlldAByMG0zMG15cjBtMzA=</auth>\n\t\tElement auth = new Element(\"auth\", challenge, new String[]{\"xmlns\", \"mechanism\"}, new String[]{XMLNS, \"PLAIN\"});\n\n\t\tresults.offer(Packet.packetInstance(auth, null, null));\n\t}\n\n\tprivate String[] decodeMessage(String input) {\n\n\t\t// Container for authoriz, user_id and password\n\t\tString[] result = new String[3];\n\t\tbyte[] challenge = Base64.decode(input);\n\t\tint auth_idx = 0;\n\n\t\twhile ((challenge[auth_idx] != 0) && (auth_idx < challenge.length)) {\n\t\t\t++auth_idx;\n\t\t}\n\n\t\tString authoriz = new String(challenge, 0, auth_idx);\n\t\tint user_idx = ++auth_idx;\n\n\t\twhile ((challenge[user_idx] != 0) && (user_idx < challenge.length)) {\n\t\t\t++user_idx;\n\t\t}\n\n\t\tString user_id = new String(challenge, auth_idx, user_idx - auth_idx);\n\n\t\t++user_idx;\n\n\t\tString passwd = new String(challenge, user_idx, challenge.length - user_idx);\n\n\t\tresult[0] = ((authoriz.length() > 0) ? authoriz : null);\n\t\tresult[1] = ((user_id.length() > 0) ? user_id : null);\n\t\tresult[2] = ((passwd.length() > 0) ? passwd : null);\n\n\t\treturn result;\n\t}\n\n\tprivate String encodeMessage(String authoriz, String user_id, String password) {\n\t\tint authoriz_size = ((authoriz != null) ? authoriz.getBytes().length : 0);\n\t\tint user_id_size = ((user_id != null) ? user_id.getBytes().length : 0);\n\t\tint password_size = ((password != null) ? password.getBytes().length : 0);\n\n\t\t// 2 NUL U+0000 characters\n\t\tint size = 2;\n\n\t\tsize += authoriz_size + user_id_size + password_size;\n\n\t\tbyte[] result = new byte[size];\n\n\t\tif ((authoriz != null) && (authoriz_size > 0)) {\n\t\t\tSystem.arraycopy(authoriz.getBytes(), 0, result, 0, authoriz_size);\n\t\t}\n\n\t\tresult[authoriz_size] = 0;\n\n\t\tif ((user_id != null) && (user_id_size > 0)) {\n\t\t\tSystem.arraycopy(user_id.getBytes(), 0, result, authoriz_size + 1, user_id_size);\n\t\t}\n\n\t\tresult[authoriz_size + 1 + user_id_size] = 0;\n\n\t\tif ((password != null) && (password_size > 0)) {\n\t\t\tSystem.arraycopy(password.getBytes(), 0, result, authoriz_size + 1 + user_id_size + 1, password_size);\n\t\t}\n\n\t\treturn Base64.encode(result);\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/handlers/StartTLSProcessor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext.handlers;\n\nimport tigase.net.SocketType;\nimport tigase.server.Packet;\nimport tigase.server.ext.ComponentIOService;\nimport tigase.server.ext.ComponentProtocolHandler;\nimport tigase.server.ext.ExtProcessor;\nimport tigase.server.ext.StreamOpenHandler;\nimport tigase.xml.Element;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Queue;\nimport java.util.logging.Logger;\n\n/**\n * Created: Oct 31, 2009 4:54:39 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class StartTLSProcessor\n\t\timplements ExtProcessor {\n\n\tprivate static final Logger log = Logger.getLogger(StartTLSProcessor.class.getName());\n\tprivate static final String EL_NAME = \"starttls\";\n\tprivate static final String ID = EL_NAME;\n\tprivate static final Element FEATURES = new Element(EL_NAME, new String[] { \"xmlns\" },\n\t                                                    new String[] { \"urn:ietf:params:xml:ns:xmpp-tls\" });\n\tprivate static final Element FEATURES_REQUIRED = new Element(EL_NAME,\n\t                                                             new Element[] { new Element(\"required\") }, new String[] { \"xmlns\" },\n\t                                                             new String[] { \"urn:ietf:params:xml:ns:xmpp-tls\" });\n\n\t@Override\n\tpublic String getId() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic List<Element> getStreamFeatures(ComponentIOService serv, ComponentProtocolHandler handler) {\n\t\tif (serv.getSessionData().get(ID) != null) {\n\t\t\treturn null;\n\t\t} else {\n\t\t\tswitch ((SocketType) serv.getSessionData().get(\"socket\")) {\n\t\t\t\tcase ssl:\n\t\t\t\t\treturn null;\n\t\t\t\tcase plain:\n\t\t\t\t\treturn Collections.singletonList(FEATURES);\n\t\t\t\tcase tls:\n\t\t\t\t\treturn Collections.singletonList(FEATURES_REQUIRED);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean process(Packet p, ComponentIOService serv, ComponentProtocolHandler handler, Queue<Packet> results) {\n\t\tif (p.getElemName() == EL_NAME) {\n\t\t\tserv.getSessionData().put(ID, ID);\n\n\t\t\tString data = \"<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\";\n\n\t\t\tinitTLS(serv, data, false);\n\t\t\tlog.fine(\"Started server side TLS.\");\n\n\t\t\treturn true;\n\t\t}\n\n\t\tif (p.getElemName() == \"proceed\") {\n\t\t\tserv.getSessionData().put(ID, ID);\n\t\t\tinitTLS(serv, null, true);\n\t\t\tlog.fine(\"Started client side TLS.\");\n\n\t\t\tStreamOpenHandler soh = handler.getStreamOpenHandler(\"jabber:client\");\n\t\t\tString data = soh.serviceStarted(serv);\n\n\t\t\tserv.xmppStreamOpen(data);\n\t\t\tlog.fine(\"New stream opened: \" + data);\n\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void startProcessing(Packet p, ComponentIOService serv, ComponentProtocolHandler handler,\n\t\t\t\t\t\t\t\tQueue<Packet> results) {\n\t\tresults.offer(Packet.packetInstance(\n\t\t\t\tnew Element(EL_NAME, new String[]{\"xmlns\"}, new String[]{\"urn:ietf:params:xml:ns:xmpp-tls\"}), null,\n\t\t\t\tnull));\n\t}\n\n\tprivate void initTLS(ComponentIOService serv, String data, boolean client) {\n\t\ttry {\n\t\t\tserv.writeRawData(data);\n\t\t\tThread.sleep(10);\n\n\t\t\twhile (serv.waitingToSend()) {\n\t\t\t\tserv.writeRawData(null);\n\t\t\t\tThread.sleep(10);\n\t\t\t}\n\n\t\t\tserv.startTLS(client, false, false);\n\t\t} catch (Exception e) {\n\t\t\tlog.warning(\"TLS mode start failed: \" + e.getMessage());\n\t\t\tserv.forceStop();\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/handlers/StreamFeaturesProcessor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext.handlers;\n\nimport tigase.server.Packet;\nimport tigase.server.ext.ComponentIOService;\nimport tigase.server.ext.ComponentProtocolHandler;\nimport tigase.server.ext.ExtProcessor;\nimport tigase.xml.Element;\n\nimport java.util.List;\nimport java.util.Queue;\nimport java.util.logging.Logger;\n\n/**\n * Created: Oct 31, 2009 3:51:09 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class StreamFeaturesProcessor\n\t\timplements ExtProcessor {\n\n\tprivate static final Logger log = Logger.getLogger(StreamFeaturesProcessor.class.getName());\n\tprivate static final String EL_NAME = \"stream:features\";\n\tprivate static final String ID = EL_NAME;\n\tprivate static final String STARTTLS = \"starttls\";\n\tprivate static final String SASL = \"sasl\";\n\n\t@Override\n\tpublic String getId() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic List<Element> getStreamFeatures(ComponentIOService serv, ComponentProtocolHandler handler) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean process(Packet p, ComponentIOService serv, ComponentProtocolHandler handler, Queue<Packet> results) {\n\t\tif (p.isElement(\"features\", \"http://etherx.jabber.org/streams\")) {\n\t\t\tlog.fine(\"Received stream features: \" + p.toString());\n\n\t\t\tElement elem = p.getElement();\n\n\t\t\tif (elem.getChild(STARTTLS) != null) {\n\t\t\t\tExtProcessor proc = handler.getProcessor(STARTTLS);\n\n\t\t\t\tproc.startProcessing(null, serv, handler, results);\n\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (elem.getChild(\"mechanisms\") != null) {\n\t\t\t\tExtProcessor proc = handler.getProcessor(SASL);\n\n\t\t\t\tproc.startProcessing(null, serv, handler, results);\n\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void startProcessing(Packet p, ComponentIOService serv, ComponentProtocolHandler handler,\n\t\t\t\t\t\t\t\tQueue<Packet> results) {\n\t\tthrow new UnsupportedOperationException(\"Not supported yet.\");\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/handlers/UnknownXMLNSStreamOpenHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext.handlers;\n\nimport tigase.server.ext.ComponentIOService;\nimport tigase.server.ext.ComponentProtocolHandler;\nimport tigase.server.ext.StreamOpenHandler;\n\nimport java.util.Map;\n\n/**\n * Created: Oct 2, 2009 5:18:44 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class UnknownXMLNSStreamOpenHandler\n\t\timplements StreamOpenHandler {\n\n\t@Override\n\tpublic String[] getXMLNSs() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String serviceStarted(ComponentIOService s) {\n\t\tthrow new UnsupportedOperationException(\"Not supported yet.\");\n\t}\n\n\t@Override\n\tpublic String streamOpened(ComponentIOService serv, Map<String, String> attribs, ComponentProtocolHandler handler) {\n\t\tString xmlns = attribs.get(\"xmlns\");\n\t\tStringBuilder sb = new StringBuilder(\n\t\t\t\t\"<stream:stream xmlns:stream='http://etherx.jabber.org/streams'\" + \" version='1.0'\" + \" xml:lang='en'\");\n\n\t\tif (xmlns != null) {\n\t\t\tsb.append(\" xmlns='\").append(xmlns).append(\"'\");\n\t\t}\n\n\t\tif (attribs.get(\"to\") != null) {\n\t\t\tsb.append(\" from='\").append(attribs.get(\"to\")).append(\"'\");\n\t\t}\n\n\t\tif (attribs.get(\"from\") != null) {\n\t\t\tsb.append(\" to='\").append(attribs.get(\"from\")).append(\"'\");\n\t\t}\n\n\t\tif (attribs.get(\"id\") != null) {\n\t\t\tsb.append(\" id='\").append(attribs.get(\"id\")).append(\"'\");\n\t\t}\n\n\t\tsb.append('>');\n\t\tsb.append(\"<stream:error>\" + \"<invalid-namespace xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>\" +\n\t\t\t\t\t\t  \"</stream:error>\" + \"</stream:stream>\");\n\n\t\treturn sb.toString();\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/lb/LoadBalancerIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext.lb;\n\nimport tigase.server.Packet;\nimport tigase.server.ext.ComponentConnection;\nimport tigase.server.ext.ComponentIOService;\n\nimport java.util.List;\n\n/**\n * @author Artur Hefczyc Created Jul 9, 2011\n */\npublic interface LoadBalancerIfc {\n\n\tComponentIOService selectConnection(Packet p, List<ComponentConnection> conns);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/lb/ReceiverBareJidLB.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext.lb;\n\nimport tigase.server.Packet;\nimport tigase.server.ext.ComponentConnection;\nimport tigase.server.ext.ComponentIOService;\n\nimport java.util.List;\n\n/**\n * @author Artur Hefczyc Created Jul 9, 2011\n */\npublic class ReceiverBareJidLB\n\t\timplements LoadBalancerIfc {\n\n\t@Override\n\tpublic ComponentIOService selectConnection(Packet p, List<ComponentConnection> conns) {\n\t\tComponentIOService result = null;\n\t\tint idx = Math.abs(p.getStanzaTo().getBareJID().hashCode() % conns.size());\n\t\tComponentConnection conn = conns.get(idx);\n\n\t\tif ((conn.getService() != null) && conn.getService().isConnected()) {\n\t\t\tresult = conn.getService();\n\t\t}\n\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/lb/ReceiverFullJidLB.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext.lb;\n\nimport tigase.server.Packet;\nimport tigase.server.ext.ComponentConnection;\nimport tigase.server.ext.ComponentIOService;\n\nimport java.util.List;\n\n/**\n * @author Artur Hefczyc Created Jul 9, 2011\n */\npublic class ReceiverFullJidLB\n\t\timplements LoadBalancerIfc {\n\n\t@Override\n\tpublic ComponentIOService selectConnection(Packet p, List<ComponentConnection> conns) {\n\t\tComponentIOService result = null;\n\t\tint idx = Math.abs(p.getStanzaTo().hashCode() % conns.size());\n\t\tComponentConnection conn = conns.get(idx);\n\t\tif (conn.getService() != null && conn.getService().isConnected()) {\n\t\t\tresult = conn.getService();\n\t\t}\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/lb/SenderBareJidLB.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext.lb;\n\nimport tigase.server.Packet;\nimport tigase.server.ext.ComponentConnection;\nimport tigase.server.ext.ComponentIOService;\n\nimport java.util.List;\n\n/**\n * @author Artur Hefczyc Created Jul 9, 2011\n */\npublic class SenderBareJidLB\n\t\timplements LoadBalancerIfc {\n\n\t@Override\n\tpublic ComponentIOService selectConnection(Packet p, List<ComponentConnection> conns) {\n\t\tComponentIOService result = null;\n\t\tint idx = Math.abs(p.getStanzaFrom().getBareJID().hashCode() % conns.size());\n\t\tComponentConnection conn = conns.get(idx);\n\t\tif (conn.getService() != null && conn.getService().isConnected()) {\n\t\t\tresult = conn.getService();\n\t\t}\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/ext/lb/SenderFullJidLB.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.ext.lb;\n\nimport tigase.server.Packet;\nimport tigase.server.ext.ComponentConnection;\nimport tigase.server.ext.ComponentIOService;\n\nimport java.util.List;\n\n/**\n * @author Artur Hefczyc Created Jul 9, 2011\n */\npublic class SenderFullJidLB\n\t\timplements LoadBalancerIfc {\n\n\t@Override\n\tpublic ComponentIOService selectConnection(Packet p, List<ComponentConnection> conns) {\n\t\tComponentIOService result = null;\n\t\tint idx = Math.abs(p.getStanzaFrom().hashCode() % conns.size());\n\t\tComponentConnection conn = conns.get(idx);\n\t\tif (conn.getService() != null && conn.getService().isConnected()) {\n\t\t\tresult = conn.getService();\n\t\t}\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/extdisco/ExtServiceDiscoItem.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.extdisco;\n\nimport tigase.db.comp.RepositoryItemAbstract;\nimport tigase.server.Command;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\n\n/**\n * Created by andrzej on 06.09.2016.\n */\npublic class ExtServiceDiscoItem\n\t\textends RepositoryItemAbstract {\n\n\tprivate static final String KEY_LABEL = \"Service\";\n\tprivate static final String HOST_LABEL = \"Host\";\n\tprivate static final String NAME_LABEL = \"Service name\";\n\tprivate static final String PORT_LABEL = \"Port\";\n\tprivate static final String TRANSPORT_LABEL = \"Transport\";\n\tprivate static final String TYPE_LABEL = \"Type\";\n\tprivate static final String RESTRICTED_LABEL = \"Requires username and password\";\n\tprivate static final String USERNAME_LABEL = \"Username\";\n\tprivate static final String PASSWORD_LABEL = \"Password\";\n\tprivate String host;\n\tprivate String key;\n\tprivate String name;\n\tprivate String password;\n\tprivate Integer port;\n\tprivate boolean restricted;\n\tprivate String transport;\n\tprivate String type;\n\tprivate String username;\n\n\t@Override\n\tpublic void addCommandFields(Packet packet) {\n\t\tCommand.addFieldValue(packet, KEY_LABEL, key != null ? key : \"\");\n\t\tCommand.addFieldValue(packet, NAME_LABEL, name != null ? name : \"\");\n\t\tCommand.addFieldValue(packet, HOST_LABEL, host != null ? host : \"\");\n\t\tCommand.addFieldValue(packet, PORT_LABEL, port != null ? String.valueOf(port) : \"\");\n\t\tCommand.addFieldValue(packet, TYPE_LABEL, type != null ? type : \"\");\n\t\tCommand.addFieldValue(packet, TRANSPORT_LABEL, transport != null ? transport : \"\");\n\t\tCommand.addCheckBoxField(packet, RESTRICTED_LABEL, restricted);\n\t\tCommand.addFieldValue(packet, USERNAME_LABEL, username != null ? username : \"\");\n\t\tCommand.addFieldValue(packet, PASSWORD_LABEL, password != null ? password : \"\");\n\t}\n\n\t@Override\n\tpublic String[] getAdmins() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void setAdmins(String[] admins) {\n\n\t}\n\n\t@Override\n\tpublic String getElemName() {\n\t\treturn \"service\";\n\t}\n\t\n\t@Override\n\tpublic String getKey() {\n\t\treturn key;\n\t}\n\n\t@Override\n\tpublic void setKey(String key) {\n\t\tthis.key = key;\n\t}\n\t\n\t@Override\n\tpublic String getOwner() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void setOwner(String owner) {\n\n\t}\n\n\tpublic String getType() {\n\t\treturn type;\n\t}\n\n\tpublic void init(String key, String name, String host, int port, String type, String transport, boolean restricted, String username, String password) {\n\t\tthis.key = key;\n\t\tthis.name = name;\n\t\tthis.host = host;\n\t\tthis.port = port;\n\t\tthis.type = type;\n\t\tthis.transport = transport;\n\t\tthis.restricted = restricted;\n\t\tthis.username = username;\n\t\tthis.password = password;\n\t}\n\n\t@Override\n\tpublic void initFromCommand(Packet packet) {\n\t\tString tmp = Command.getFieldValue(packet, KEY_LABEL);\n\t\tif (tmp != null && !(tmp = tmp.trim()).isEmpty()) {\n\t\t\tkey = tmp;\n\t\t} else {\n\t\t\tkey = null;\n\t\t}\n\t\ttmp = Command.getFieldValue(packet, NAME_LABEL);\n\t\tif (tmp != null && !(tmp = tmp.trim()).isEmpty()) {\n\t\t\tname = tmp;\n\t\t} else {\n\t\t\tname = null;\n\t\t}\n\t\ttmp = Command.getFieldValue(packet, HOST_LABEL);\n\t\tif (tmp != null && !(tmp = tmp.trim()).isEmpty()) {\n\t\t\thost = tmp;\n\t\t} else {\n\t\t\thost = null;\n\t\t}\n\t\ttmp = Command.getFieldValue(packet, PORT_LABEL);\n\t\tif (tmp != null && !(tmp = tmp.trim()).isEmpty()) {\n\t\t\tport = Integer.parseInt(tmp);\n\t\t} else {\n\t\t\tport = null;\n\t\t}\n\t\ttmp = Command.getFieldValue(packet, TYPE_LABEL);\n\t\tif (tmp != null && !(tmp = tmp.trim()).isEmpty()) {\n\t\t\ttype = tmp;\n\t\t} else {\n\t\t\ttype = null;\n\t\t}\n\t\ttmp = Command.getFieldValue(packet, TRANSPORT_LABEL);\n\t\tif (tmp != null && !(tmp = tmp.trim()).isEmpty()) {\n\t\t\ttransport = tmp;\n\t\t} else {\n\t\t\ttransport = null;\n\t\t}\n\t\trestricted = Command.getCheckBoxFieldValue(packet, RESTRICTED_LABEL);\n\t\ttmp = Command.getFieldValue(packet, USERNAME_LABEL);\n\t\tif (tmp != null && !(tmp = tmp.trim()).isEmpty()) {\n\t\t\tusername = tmp;\n\t\t} else {\n\t\t\tusername = null;\n\t\t}\n\t\ttmp = Command.getFieldValue(packet, PASSWORD_LABEL);\n\t\tif (tmp != null && !(tmp = tmp.trim()).isEmpty()) {\n\t\t\tpassword = tmp;\n\t\t} else {\n\t\t\tpassword = null;\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void initFromElement(Element elem) {\n\t\tkey = elem.getAttributeStaticStr(\"key\");\n\t\thost = elem.getAttributeStaticStr(\"host\");\n\t\ttype = elem.getAttributeStaticStr(\"type\");\n\n\t\tString tmp = elem.getAttributeStaticStr(\"port\");\n\t\tif (tmp != null) {\n\t\t\tport = Integer.parseInt(tmp);\n\t\t} else {\n\t\t\tport = null;\n\t\t}\n\n\t\tname = elem.getAttributeStaticStr(\"name\");\n\t\ttransport = elem.getAttributeStaticStr(\"transport\");\n\n\t\trestricted = \"true\".equals(elem.getAttributeStaticStr(\"restricted\"));\n\t\tusername = elem.getAttributeStaticStr(\"username\");\n\t\tpassword = elem.getAttributeStaticStr(\"password\");\n\t}\n\n\t@Override\n\tpublic void initFromPropertyString(String propString) {\n\t\tString[] tmp = propString.split(\":\");\n\t\tkey = tmp[0];\n\t\tfor (String part : tmp) {\n\t\t\tint idx = part.indexOf(\"=\");\n\t\t\tString key = part;\n\t\t\tString val = null;\n\t\t\tif (idx > -1) {\n\t\t\t\tval = part.substring(idx + 1);\n\t\t\t\tkey = part.substring(0, idx);\n\t\t\t}\n\n\t\t\tswitch (key) {\n\t\t\t\tcase \"host\":\n\t\t\t\t\thost = val;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"type\":\n\t\t\t\t\ttype = val;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"port\":\n\t\t\t\t\tport = (val != null && !val.isEmpty()) ? Integer.parseInt(val) : null;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"name\":\n\t\t\t\t\tname = val;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"transport\":\n\t\t\t\t\ttransport = val;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"restricted\":\n\t\t\t\t\trestricted = \"true\".equals(val);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"username\":\n\t\t\t\t\tusername = val;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"password\":\n\t\t\t\t\tpassword = val;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isAdmin(String id) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean isOwner(String id) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic Element toElement() {\n\t\tElement service = new Element(\"service\");\n\t\tservice.setAttribute(\"key\", key);\n\t\tservice.setAttribute(\"host\", host);\n\t\tservice.setAttribute(\"type\", type);\n\t\tif (port != null) {\n\t\t\tservice.setAttribute(\"port\", String.valueOf(port));\n\t\t}\n\t\tif (name != null) {\n\t\t\tservice.setAttribute(\"name\", name);\n\t\t}\n\t\tif (transport != null) {\n\t\t\tservice.setAttribute(\"transport\", transport);\n\t\t}\n\n\t\t// Options for credentials to authenticate for external service\n\t\tif (restricted) {\n\t\t\tservice.setAttribute(\"restricted\", \"true\");\n\t\t}\n\t\tif (username != null) {\n\t\t\tservice.setAttribute(\"username\", username);\n\t\t}\n\t\tif (password != null) {\n\t\t\tservice.setAttribute(\"password\", password);\n\t\t}\n\n\t\treturn service;\n\t}\n\n\t@Override\n\tpublic String toPropertyString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(key);\n\t\tif (host != null) {\n\t\t\tsb.append(\":host=\").append(host);\n\t\t}\n\t\tif (type != null) {\n\t\t\tsb.append(\":type=\").append(type);\n\t\t}\n\t\tif (port != null) {\n\t\t\tsb.append(\":port=\").append(String.valueOf(port));\n\t\t}\n\t\tif (name != null) {\n\t\t\tsb.append(\":name=\").append(name);\n\t\t}\n\t\tif (transport != null) {\n\t\t\tsb.append(\":transport=\").append(transport);\n\t\t}\n\t\tif (restricted) {\n\t\t\tsb.append(\":restricted=true\");\n\t\t}\n\t\tif (username != null) {\n\t\t\tsb.append(\":username=\").append(username);\n\t\t}\n\t\tif (password != null) {\n\t\t\tsb.append(\":password=\").append(password);\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/extdisco/ExtServiceDiscoveryUserRepoRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.extdisco;\n\nimport tigase.db.DBInitException;\nimport tigase.db.comp.UserRepoRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.Map;\n\n/**\n * Created by andrzej on 06.09.2016.\n */\n@Bean(name = \"externalServiceDiscoveryRepository\", parent = ExternalServiceDiscoveryComponent.class, active = true)\npublic class ExtServiceDiscoveryUserRepoRepository\n\t\textends UserRepoRepository<ExtServiceDiscoItem> {\n\n\tprivate static final BareJID repoUser = BareJID.bareJIDInstanceNS(\"external-service-discovery\");\n\n\t@Override\n\tpublic BareJID getRepoUser() {\n\t\treturn repoUser;\n\t}\n\n\t@Override\n\t@Deprecated\n\tpublic void initRepository(String resource_uri, Map<String, String> params) throws DBInitException {\n\n\t}\n\n\t@Override\n\tpublic String getConfigKey() {\n\t\treturn repoUser.getDomain();\n\t}\n\n\t@Override\n\tpublic String[] getDefaultPropetyItems() {\n\t\treturn new String[0];\n\t}\n\n\t@Override\n\tpublic String getPropertyKey() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void destroy() {\n\n\t}\n\n\t@Override\n\tpublic ExtServiceDiscoItem getItemInstance() {\n\t\treturn new ExtServiceDiscoItem();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/extdisco/ExternalServiceDiscoveryComponent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.extdisco;\n\nimport tigase.component.AbstractKernelBasedComponent;\nimport tigase.component.modules.impl.AdHocCommandModule;\nimport tigase.component.modules.impl.DiscoveryModule;\nimport tigase.db.comp.ComponentRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\n\nimport javax.script.Bindings;\n\n/**\n * Created by andrzej on 06.09.2016.\n */\n@Bean(name = \"ext-disco\", parent = Kernel.class, active = true)\n@ConfigType({ConfigTypeEnum.DefaultMode})\npublic class ExternalServiceDiscoveryComponent\n\t\textends AbstractKernelBasedComponent {\n\n\t@Inject\n\tprivate ComponentRepository<ExtServiceDiscoItem> repo;\n\n\t@Override\n\tpublic String getDiscoDescription() {\n\t\treturn \"External Service Discovery component\";\n\t}\n\n\t@Override\n\tpublic void initBindings(Bindings binds) {\n\t\tsuper.initBindings(binds);\n\t\tbinds.put(ComponentRepository.COMP_REPO_BIND, repo);\n\t}\n\n\t@Override\n\tpublic boolean isDiscoNonAdmin() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tprotected void registerModules(Kernel kernel) {\n\t\tkernel.registerBean(AdHocCommandModule.class).exec();\n\t\tkernel.registerBean(DiscoveryModule.class).exec();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/extdisco/ExternalServiceDiscoveryModule.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.extdisco;\n\nimport tigase.component.PacketWriter;\nimport tigase.component.exceptions.ComponentException;\nimport tigase.component.modules.Module;\nimport tigase.criteria.Criteria;\nimport tigase.criteria.ElementCriteria;\nimport tigase.db.TigaseDBException;\nimport tigase.db.comp.ComponentRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.StanzaType;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created by andrzej on 06.09.2016.\n */\n@Bean(name = \"extDiscoModule\", parent = ExternalServiceDiscoveryComponent.class, active = true)\npublic class ExternalServiceDiscoveryModule\n\t\timplements Module {\n\n\tprivate static final Logger log = Logger.getLogger(ExternalServiceDiscoveryModule.class.getCanonicalName());\n\n\tprivate static final String XMLNS = \"urn:xmpp:extdisco:2\";\n\n\tprivate static final Criteria CRITERIA = ElementCriteria.name(Iq.ELEM_NAME)\n\t\t\t.add(ElementCriteria.name(\"services\", XMLNS));\n\n\t@Inject\n\tprivate PacketWriter packetWriter;\n\n\t@Inject\n\tprivate ComponentRepository<ExtServiceDiscoItem> repo;\n\n\t@Override\n\tpublic String[] getFeatures() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Criteria getModuleCriteria() {\n\t\treturn CRITERIA;\n\t}\n\n\t@Override\n\tpublic void process(Packet packet) throws ComponentException, TigaseStringprepException {\n\t\tif (packet.getType() == StanzaType.error) {\n\t\t\tlog.log(Level.FINEST, \"Received packet of type ''error'', dropping packet = {0}\", packet);\n\t\t\treturn;\n\t\t}\n\t\tif (packet.getType() != StanzaType.get) {\n\t\t\tthrow new ComponentException(Authorization.BAD_REQUEST, \"Invalid packet type\");\n\t\t}\n\n\t\tString type = packet.getElement().getChild(\"services\", XMLNS).getAttributeStaticStr(\"type\");\n\n\t\tList<ExtServiceDiscoItem> services = getServices(type);\n\n\t\tElement servicesEl = new Element(\"services\");\n\t\tservicesEl.setXMLNS(XMLNS);\n\n\t\tservices.forEach(service -> {\n\t\t\tElement item = service.toElement();\n\t\t\tString host = item.getAttributeStaticStr(\"host\");\n\t\t\tif (host != null && host.contains(\"{clusterNode}\")) {\n\t\t\t\titem.setAttribute(\"host\", host.replace(\"{clusterNode}\", DNSResolverFactory.getInstance().getSecondaryHost()));\n\t\t\t}\n\t\t\titem.removeAttribute(\"key\");\n\t\t\tservicesEl.addChild(item);\n\t\t});\n\n\t\tpacketWriter.write(packet.okResult(servicesEl, 0));\n\t}\n\n\tprotected List<ExtServiceDiscoItem> getServices(String type) {\n\t\ttry {\n\t\t\tList<ExtServiceDiscoItem> items = new ArrayList<>(repo.allItems());\n\n\t\t\tif (type != null) {\n\t\t\t\titems.removeIf((item) -> !type.equals(item.getType()));\n\t\t\t}\n\n\t\t\treturn items;\n\t\t} catch (TigaseDBException ex) {\n\t\t\tthrow new RuntimeException(\"Exception reading items from repository\", ex);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/extdisco/ExternalServiceDiscoveryProcessor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.extdisco;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.xmpp.XMPPException;\nimport tigase.xmpp.XMPPProcessorIfc;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.impl.annotation.*;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Map;\nimport java.util.Queue;\n\nimport static tigase.server.extdisco.ExternalServiceDiscoveryProcessor.ID;\nimport static tigase.server.extdisco.ExternalServiceDiscoveryProcessor.XMLNS;\n\n/**\n * Created by andrzej on 06.09.2016.\n */\n@Bean(name = ID, parent = SessionManager.class, active = true)\n@Id(ID)\n@DiscoFeatures({XMLNS})\n@Handles({@Handle(path = {Iq.ELEM_NAME, \"services\"}, xmlns = XMLNS)})\npublic class ExternalServiceDiscoveryProcessor\n\t\textends AnnotatedXMPPProcessor\n\t\timplements XMPPProcessorIfc {\n\n\tprotected static final String XMLNS = \"urn:xmpp:extdisco:2\";\n\tprotected static final String ID = XMLNS;\n\n\t@ConfigField(desc = \"JID of External Service Discovery Component\", alias = \"ext-service-disco-jid\")\n\tprivate JID extServiceDiscoJid = JID.jidInstanceNS(\"ext-disco\", DNSResolverFactory.getInstance().getDefaultHost());\n\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\t\tif (extServiceDiscoJid.equals(packet.getPacketFrom())) {\n\t\t\t// No session, skipping processing result\n\t\t\tif (session == null) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tPacket forward = packet.copyElementOnly();\n\t\t\tforward.setPacketTo(session.getConnectionId(packet.getStanzaTo()));\n\n\t\t\tresults.offer(forward);\n\t\t} else {\n\t\t\tPacket forward = packet.copyElementOnly();\n\t\t\tforward.setPacketTo(extServiceDiscoJid);\n\n\t\t\tresults.offer(forward);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/filters/PacketCounter.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.filters;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.PacketFilterIfc;\nimport tigase.server.QueueType;\nimport tigase.stats.StatisticsList;\n\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicLong;\nimport java.util.logging.Level;\n\n@Bean(name = \"packetCounter\", parents = {PacketFiltersBean.IncomingPacketFiltersBean.class,\n\t\t\t\t\t\t\t\t\t\t PacketFiltersBean.OutgoingPacketFiltersBean.class}, active = true)\npublic class PacketCounter\n\t\timplements PacketFilterIfc {\n\n\tprivate final static String DETAILED_OTHER_STATISTICS_KEY = \"detailed-other-statistics\";\n\tprivate final TypeCounter iqCounter = new TypeCounter(\"IQ\");\n\tprivate final Map<String, TypeCounter> otherCounters = new ConcurrentHashMap<>();\n\tprivate long clusterCounter = 0;\n\tprivate boolean detailedOtherStat = true;\n\tprivate long msgCounter = 0;\n\tprivate String name = null;\n\tprivate long otherCounter = 0;\n\tprivate long presCounter = 0;\n\tprivate QueueType queueType = null;\n\tprivate long total = 0;\n\n\tpublic PacketCounter() {\n\t\tfinal String tmp = System.getProperty(DETAILED_OTHER_STATISTICS_KEY);\n\t\tif (null != tmp) {\n\t\t\tdetailedOtherStat = Boolean.valueOf(tmp);\n\t\t}\n\t}\n\n\tpublic PacketCounter(boolean detailedOtherStat) {\n\t\tthis.detailedOtherStat = detailedOtherStat;\n\t}\n\n\t@Override\n\tpublic Packet filter(Packet packet) {\n\t\ttotal++;\n\t\tfinal String elemName = packet.getElemName();\n\t\tif (elemName == \"message\") {\n\t\t\t++msgCounter;\n\t\t} else if (elemName == \"presence\") {\n\t\t\t++presCounter;\n\t\t} else if (elemName == \"cluster\") {\n\t\t\t++clusterCounter;\n\t\t} else if (elemName == \"iq\") {\n\t\t\tString xmlns = ((Iq) packet).getIQXMLNS();\n\t\t\tiqCounter.incrementCounter((xmlns != null) ? xmlns : ((Iq) packet).getIQChildName());\n\t\t} else {\n\t\t\t++otherCounter;\n\n\t\t\tif (detailedOtherStat) {\n\t\t\t\tString xmlns = packet.getXMLNS() != null ? packet.getXMLNS() : \"no XMLNS\";\n\t\t\t\totherCounters.computeIfAbsent(xmlns, s -> new TypeCounter(\"other \" + xmlns)).incrementCounter(elemName);\n\t\t\t}\n\t\t}\n\t\treturn packet;\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t\tlist.add(name, queueType.name() + \" processed\", total, Level.FINER);\n\t\tlist.add(name, queueType.name() + \" processed messages\", msgCounter, Level.FINER);\n\t\tlist.add(name, queueType.name() + \" processed presences\", presCounter, Level.FINER);\n\t\tlist.add(name, queueType.name() + \" processed cluster\", clusterCounter, Level.FINER);\n\t\tlist.add(name, queueType.name() + \" processed other\", otherCounter, Level.FINER);\n\n\t\tiqCounter.getStatistics(list, Level.FINER);\n\n\t\tfinal Level finer = Level.FINER;\n\t\tif (detailedOtherStat & list.checkLevel(finer)) {\n\t\t\totherCounters.values().forEach(typeCounter -> typeCounter.getStatistics(list, finer));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void init(String name, QueueType queueType) {\n\t\tthis.name = name;\n\t\tthis.queueType = queueType;\n\t}\n\n\tprivate class TypeCounter {\n\n\t\tprivate final Map<String, AtomicLong> counter = new ConcurrentHashMap<>();\n\t\tprivate final String counterName;\n\t\tprivate final AtomicLong total = new AtomicLong();\n\n\t\tpublic TypeCounter(String name) {\n\t\t\tthis.counterName = name;\n\t\t}\n\n\t\tpublic Map<String, AtomicLong> getCounter() {\n\t\t\treturn counter;\n\t\t}\n\n\t\tpublic void getStatistics(StatisticsList list) {\n\t\t\tgetStatistics(list, Level.FINEST);\n\t\t}\n\n\t\tpublic void getStatistics(StatisticsList list, Level level) {\n\t\t\tlist.add(name, queueType.name() + \" processed \" + counterName, total.get(), level);\n\t\t\tfor (Entry<String, AtomicLong> xmlnsValues : counter.entrySet()) {\n\t\t\t\tlist.add(name, queueType.name() + \" processed \" + counterName + \" \" + xmlnsValues.getKey(),\n\t\t\t\t\t\t xmlnsValues.getValue().get(), level);\n\t\t\t}\n\t\t}\n\n\t\tpublic long getTotal() {\n\t\t\treturn total.get();\n\t\t}\n\n\t\tsynchronized public void incrementCounter(String param) {\n\t\t\ttotal.incrementAndGet();\n\t\t\tparam = param == null ? \"[no XMLNS/child]\" : param;\n\t\t\tcounter.computeIfAbsent(param, s -> new AtomicLong()).getAndIncrement();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/filters/PacketFiltersBean.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.filters;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.RegistrarBean;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.AbstractMessageReceiver;\nimport tigase.server.PacketFilterIfc;\nimport tigase.server.QueueType;\n\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.concurrent.CopyOnWriteArrayList;\n\npublic class PacketFiltersBean implements RegistrarBean {\n\n\tprivate final QueueType queueType;\n\n\tprivate String name;\n\t\n\t@Inject\n\tprivate CopyOnWriteArrayList<PacketFilterIfc> filters = new CopyOnWriteArrayList<>();\n\n\tprotected PacketFiltersBean(QueueType queueType) {\n\t\tthis.queueType = queueType;\n\t}\n\t\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t\tinitializeFilters();\n\t}\n\n\tpublic void setFilters(List<PacketFilterIfc> filters) {\n\t\tthis.filters = new CopyOnWriteArrayList<>(filters);\n\t\tinitializeFilters();\n\t}\n\n\tpublic List<PacketFilterIfc> getFilters() {\n\t\treturn filters;\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\t\t// nothing to do, we just need a scope\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\t\t// nothing to do, we just need a scope\n\t}\n\n\tprotected void initializeFilters() {\n\t\tthis.filters.forEach(filter -> filter.init(Optional.ofNullable(name).orElse(\"UNKNOWN\"), queueType));\n\t}\n\n\t@Bean(name = \"incomingFilters\", parent = AbstractMessageReceiver.class, active = true)\n\tpublic static class IncomingPacketFiltersBean extends PacketFiltersBean {\n\n\t\tpublic IncomingPacketFiltersBean() {\n\t\t\tsuper(QueueType.IN_QUEUE);\n\t\t}\n\n\t}\n\n\t@Bean(name = \"outgoingFilters\", parent = AbstractMessageReceiver.class, active = true)\n\tpublic static class OutgoingPacketFiltersBean extends PacketFiltersBean {\n\n\t\tpublic OutgoingPacketFiltersBean() {\n\t\t\tsuper(QueueType.OUT_QUEUE);\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/monitor/MonitorComponent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.monitor;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.conf.ConfigurationException;\nimport tigase.server.AbstractMessageReceiver;\nimport tigase.server.Packet;\nimport tigase.stats.StatisticsList;\n\nimport javax.script.Bindings;\nimport java.util.Map;\nimport java.util.logging.Logger;\n\n/**\n * Created: Jun 17, 2010 10:14:23 AM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n* @deprecated Use {@link  tigase.monitor.MonitorComponent} instead.\n */\n@Deprecated\n@TigaseDeprecated(since = \"8.0.0\")\npublic class MonitorComponent\n\t\textends AbstractMessageReceiver {\n\n\tprivate static final Logger log = Logger.getLogger(MonitorComponent.class.getName());\n\n\t@Override\n\tpublic Map<String, Object> getDefaults(Map<String, Object> params) {\n\t\tMap<String, Object> defs = super.getDefaults(params);\n\n\t\treturn defs;\n\t}\n\n\t@Override\n\tpublic String getDiscoCategoryType() {\n\t\treturn \"generic\";\n\t}\n\n\t@Override\n\tpublic String getDiscoDescription() {\n\t\treturn \"Monitor\";\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t\tsuper.getStatistics(list);\n\t}\n\n\t@Override\n\tpublic void initBindings(Bindings binds) {\n\t\tsuper.initBindings(binds);\n\t}\n\n\t@Override\n\tpublic void processPacket(Packet packet) {\n\t}\n\n\t@Override\n\tpublic int processingInThreads() {\n\t\treturn Runtime.getRuntime().availableProcessors();\n\t}\n\n\t@Override\n\tpublic int processingOutThreads() {\n\t\treturn Runtime.getRuntime().availableProcessors();\n\t}\n\n\t@Override\n\tpublic void setProperties(Map<String, Object> props) throws ConfigurationException {\n\t\tsuper.setProperties(props);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/monitor/MonitorPluginIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.monitor;\n\nimport tigase.server.Packet;\nimport tigase.stats.StatisticsList;\n\nimport java.util.Queue;\n\n/**\n * Created: Jun 17, 2010 11:59:13 AM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface MonitorPluginIfc {\n\n\tvoid check10Secs(Queue<Packet> results);\n\n\tvoid check1Day(Queue<Packet> results);\n\n\tvoid check1Hour(Queue<Packet> results);\n\n\tvoid check1Min(Queue<Packet> results);\n\n\tString commandsHelp();\n\n\tvoid destroy();\n\n\tboolean isMonitorCommand(String command);\n\n\tString runCommand(String[] command);\n\n\tString getState();\n\n\tvoid getStatistics(StatisticsList list);\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/monitor/MonitorRuntime.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.monitor;\n\nimport tigase.server.Bootstrap;\nimport tigase.sys.*;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.io.IOException;\nimport java.lang.management.ManagementFactory;\nimport java.lang.management.ThreadInfo;\nimport java.lang.management.ThreadMXBean;\nimport java.time.LocalDateTime;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.LinkedHashSet;\nimport java.util.LinkedList;\nimport java.util.logging.*;\n\n/**\n * Created: Feb 19, 2009 12:31:14 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class MonitorRuntime\n\t\textends TigaseRuntime {\n\n\tprivate static final Logger log = Logger.getLogger(MonitorRuntime.class.getName());\n\n\tprivate static MonitorRuntime runtime = null;\n\tprivate final LinkedList<OnlineJidsReporter> onlineJidsReporters = new LinkedList<OnlineJidsReporter>();\n\tprivate final LinkedHashSet<ShutdownHook> shutdownHooks = new LinkedHashSet<ShutdownHook>();\n\tprivate Thread mainShutdownThread;\n\tprivate boolean shutdownThreadDump = true;\n\n\tpublic static MonitorRuntime getMonitorRuntime() {\n\t\tif (runtime == null) {\n\t\t\truntime = new MonitorRuntime();\n\t\t}\n\t\treturn runtime;\n\t}\n\n\tprivate MonitorRuntime() {\n\t\tsuper();\n\t\tmainShutdownThread = new MainShutdownThread();\n\t\tRuntime.getRuntime().addShutdownHook(mainShutdownThread);\n\t}\n\n\t@Override\n\tpublic synchronized void addShutdownHook(ShutdownHook hook) {\n\t\tshutdownHooks.add(hook);\n\t}\n\n\t@Override\n\tpublic synchronized void addMemoryChangeListener(MemoryChangeListener memListener) {\n\t\tthrow new UnsupportedOperationException(\"Not supported yet.\");\n\t}\n\n\t@Override\n\tpublic synchronized void addCPULoadListener(CPULoadListener cpuListener) {\n\t\tthrow new UnsupportedOperationException(\"Not supported yet.\");\n\t}\n\n\t@Override\n\tpublic synchronized void addOnlineJidsReporter(OnlineJidsReporter onlineReporter) {\n\t\tonlineJidsReporters.add(onlineReporter);\n\t}\n\n\t@Override\n\tpublic boolean hasCompleteJidsInfo() {\n\t\tif (onlineJidsReporters.size() == 1) {\n\t\t\treturn onlineJidsReporters.getFirst().hasCompleteJidsInfo();\n\t\t} else {\n\t\t\tfor (OnlineJidsReporter onlineJidsReporter : onlineJidsReporters) {\n\t\t\t\tif (!onlineJidsReporter.hasCompleteJidsInfo()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean isJidOnline(JID jid) {\n\t\tif (onlineJidsReporters.size() == 1) {\n\t\t\treturn onlineJidsReporters.getFirst().containsJid(jid.getBareJID());\n\t\t} else {\n\t\t\tfor (OnlineJidsReporter onlineJidsReporter : onlineJidsReporters) {\n\t\t\t\tif (onlineJidsReporter.containsJid(jid.getBareJID())) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean isJidOnlineLocally(BareJID jid) {\n\t\tif (onlineJidsReporters.size() == 1) {\n\t\t\treturn onlineJidsReporters.getFirst().containsJidLocally(jid);\n\t\t} else {\n\t\t\tfor (OnlineJidsReporter onlineJidsReporter : onlineJidsReporters) {\n\t\t\t\tif (onlineJidsReporter.containsJidLocally(jid)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean isJidOnlineLocally(JID jid) {\n\t\tif (onlineJidsReporters.size() == 1) {\n\t\t\treturn onlineJidsReporters.getFirst().containsJidLocally(jid);\n\t\t} else {\n\t\t\tfor (OnlineJidsReporter onlineJidsReporter : onlineJidsReporters) {\n\t\t\t\tif (onlineJidsReporter.containsJidLocally(jid)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic JID[] getConnectionIdsForJid(JID jid) {\n\t\tif (onlineJidsReporters.size() == 1) {\n\t\t\treturn onlineJidsReporters.getFirst().getConnectionIdsForJid(jid.getBareJID());\n\t\t} else {\n\t\t\tfor (OnlineJidsReporter onlineJidsReporter : onlineJidsReporters) {\n\t\t\t\tJID[] connIds = onlineJidsReporter.getConnectionIdsForJid(jid.getBareJID());\n\t\t\t\tif (connIds != null) {\n\t\t\t\t\treturn connIds;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic synchronized void removeShutdownHook(ShutdownHook hook) {\n\t\tshutdownHooks.remove(hook);\n\t}\n\n\tpublic boolean isShutdownThreadDump() {\n\t\treturn shutdownThreadDump;\n\t}\n\n\tpublic void setShutdownThreadDump(boolean shutdownThreadDump) {\n\t\tthis.shutdownThreadDump = shutdownThreadDump;\n\t}\n\n\tprivate class MainShutdownThread\n\t\t\textends Thread {\n\n\t\tpublic MainShutdownThread() {\n\t\t\tsuper();\n\t\t\tsetName(\"MainShutdownThread\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tfinal String shutMsg = \"ShutdownThread started... \" + LocalDateTime.now();\n\t\t\tSystem.out.println(shutMsg);\n\t\t\tlog.warning(shutMsg);\n\n\t\t\tdetectThreadLocks();\n\n\t\t\tif (shutdownThreadDump) {\n\t\t\t\ttry {\n\t\t\t\t\tcreateThreadDump();\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\tSystem.out.println(\"exception while initialization\");\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t\tlog.log(Level.WARNING, \"Failed creating thread dumper logger\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\texecuteShutdownHooks();\n\n\t\t\tSystem.out.println(\"ShutdownThread finished...\");\n\t\t\tlog.warning(\"ShutdownThread finished...\");\n\t\t}\n\n\t\tprivate void createThreadDump() throws IOException {\n\t\t\t// we have to configure logger here\n\t\t\tLogger THREAD_DUMP_LOGGER = Logger.getLogger(\"ThreadDumpLogger\");\n\t\t\tString threadDumpPath = \"logs/thread-dump.log\";\n\t\t\tFileHandler fileHandler = new FileHandler(threadDumpPath, 10000000, 5, true);\n\t\t\tfileHandler.setLevel(Level.ALL);\n\t\t\tfileHandler.setFormatter(new Formatter() {\n\t\t\t\t@Override\n\t\t\t\tpublic String format(LogRecord record) {\n\t\t\t\t\treturn (new Date(record.getMillis()) + \": \" + record.getMessage());\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tTHREAD_DUMP_LOGGER.addHandler(fileHandler);\n\t\t\tTHREAD_DUMP_LOGGER.setLevel(Level.ALL);\n\t\t\tTHREAD_DUMP_LOGGER.setUseParentHandlers(false);\n\n\t\t\tStringBuilder threadDumpBuilder = new StringBuilder(\"All threads information:\\n\");\n\t\t\tfor (ThreadInfo threadInfo : ManagementFactory.getThreadMXBean().dumpAllThreads(true, true)) {\n\t\t\t\tthreadDumpBuilder.append(threadInfo);\n\t\t\t}\n\t\t\tthreadDumpBuilder.append(\"\\n===========\\n\\n\");\n\t\t\tTHREAD_DUMP_LOGGER.log(Level.INFO, threadDumpBuilder.toString());\n\n\t\t\tString msg = \"Save thread-dump to file: \" + threadDumpPath + \", size: \" + threadDumpBuilder.length();\n\t\t\tSystem.out.println(msg);\n\t\t\tlog.warning(msg);\n\t\t}\n\n\t\tprivate void detectThreadLocks() {\n\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\tThreadMXBean thBean = ManagementFactory.getThreadMXBean();\n\t\t\tsb.append(\"\\nTotal number of threads: \" + thBean.getThreadCount()).append('\\n');\n\t\t\tlong[] tids = thBean.findDeadlockedThreads();\n\t\t\tif (tids != null && tids.length > 0) {\n\t\t\t\tsb.append(\"Locked threads:\\n\");\n\t\t\t\tThreadInfo[] lockedThreads = thBean.getThreadInfo(tids);\n\t\t\t\tfor (ThreadInfo threadInfo : lockedThreads) {\n\t\t\t\t\tsb.append(\"Locked thread \")\n\t\t\t\t\t\t\t.append(threadInfo.getThreadName())\n\t\t\t\t\t\t\t.append(\" on \")\n\t\t\t\t\t\t\t.append(threadInfo.getLockInfo().toString())\n\t\t\t\t\t\t\t.append(\", locked synchronizes: \")\n\t\t\t\t\t\t\t.append(Arrays.toString(threadInfo.getLockedSynchronizers()))\n\t\t\t\t\t\t\t.append(\", locked monitors: \")\n\t\t\t\t\t\t\t.append(Arrays.toString(threadInfo.getLockedMonitors()))\n\t\t\t\t\t\t\t.append(\" by [\")\n\t\t\t\t\t\t\t.append(threadInfo.getLockOwnerId())\n\t\t\t\t\t\t\t.append(\"] \")\n\t\t\t\t\t\t\t.append(threadInfo.getLockOwnerName())\n\t\t\t\t\t\t\t.append('\\n');\n\t\t\t\t\tStackTraceElement[] ste = threadInfo.getStackTrace();\n\t\t\t\t\tfor (StackTraceElement stackTraceElement : ste) {\n\t\t\t\t\t\tsb.append(stackTraceElement.toString()).append('\\n');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tsb.append(\"No locked threads.\\n\");\n\t\t\t}\n\t\t\tif (sb.length() > 0) {\n\t\t\t\tSystem.out.println(sb.toString());\n\t\t\t\tlog.warning(sb.toString());\n\t\t\t}\n\t\t}\n\n\t\tprivate void executeShutdownHooks() {\n\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\tLinkedList<ShutdownHandlerThread> thlist = new LinkedList<>();\n\t\t\tThreadGroup threads = new ThreadGroup(Thread.currentThread().getThreadGroup(), \"Tigase Shutdown\");\n\t\t\tshutdownHooks.stream().sorted((o1, o2) -> {\n\t\t\t\tif (o1 instanceof Bootstrap.BootstrapShutdownHook) {\n\t\t\t\t\treturn Integer.MAX_VALUE;\n\t\t\t\t}\n\t\t\t\tif (o2 instanceof Bootstrap.BootstrapShutdownHook) {\n\t\t\t\t\treturn Integer.MIN_VALUE;\n\t\t\t\t}\n\t\t\t\treturn 0;\n\t\t\t}).forEach(shutdownHook -> {\n\t\t\t\tShutdownHandlerThread thr = new ShutdownHandlerThread(threads, shutdownHook);\n\t\t\t\tthr.start();\n\t\t\t\tthlist.add(thr);\n\t\t\t});\n\t\t\t// We allow for max 20 secs for the shutdown code to run...\n\t\t\tlong shutdownStart = System.currentTimeMillis();\n\t\t\twhile (threads.activeCount() > 0 && (System.currentTimeMillis() - shutdownStart) < 20000) {\n\t\t\t\ttry {\n\t\t\t\t\tsleep(100);\n\n\t\t\t\t\tif (thlist.stream().noneMatch(th -> th.isAlive())) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (ShutdownHandlerThread shutdownHandlerThread : thlist) {\n\t\t\t\tif (shutdownHandlerThread.getResultMessage() != null) {\n\t\t\t\t\tsb.append(shutdownHandlerThread.getResultMessage());\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (sb.length() > 0) {\n\t\t\t\tSystem.out.println(sb.toString());\n\t\t\t\tlog.warning(sb.toString());\n\t\t\t}\n\t\t}\n\n\t}\n\n\tprivate class ShutdownHandlerThread\n\t\t\textends Thread {\n\n\t\tprivate ShutdownHook hook = null;\n\t\tprivate String result = null;\n\n\t\tpublic ShutdownHandlerThread(ThreadGroup group, ShutdownHook hook) {\n\t\t\tsuper(group, hook.getName());\n\t\t\tthis.hook = hook;\n\t\t\tsetDaemon(true);\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tresult = hook.shutdown();\n\t\t}\n\n\t\tpublic String getResultMessage() {\n\t\t\treturn result;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/monitor/StatusReportGenerator.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.monitor;\n\nimport groovy.lang.Writable;\nimport groovy.text.GStringTemplateEngine;\nimport groovy.text.Template;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.HandleEvent;\nimport tigase.eventbus.TickMinuteEvent;\nimport tigase.kernel.beans.*;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.XMPPServer;\nimport tigase.sys.TigaseRuntime;\n\nimport java.io.*;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Bean(name = \"status-report-generator\", parent = Kernel.class, active = true)\n@Autostart\npublic class StatusReportGenerator\n\t\timplements Initializable, UnregisterAware {\n\n\tprivate static final Logger log = Logger.getLogger(StatusReportGenerator.class.getName());\n\t@Inject\n\tprivate EventBus eventBus;\n\t@ConfigField(desc = \"Status report generator enabled\")\n\tprivate boolean reportGeneratorEnabled = true;\n\tTemplate template = null;\n\n\tprivate static StringBuilder append(StringBuilder sb, String name, String value) {\n\t\tsb.append(\"'\").append(name).append(\"': \");\n\t\tsb.append(\"'\").append(value).append(\"'\");\n\t\treturn sb;\n\t}\n\n\tprivate static StringBuilder append(StringBuilder sb, String name, int value) {\n\t\tsb.append(\"'\").append(name).append(\"': \");\n\t\tsb.append(value);\n\t\treturn sb;\n\t}\n\n\tprivate static StringBuilder append(StringBuilder sb, String name, double value) {\n\t\tsb.append(\"'\").append(name).append(\"': \");\n\t\tsb.append(String.format(Locale.ROOT, \"%.2f\", value));\n\t\treturn sb;\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\teventBus.registerAll(this);\n\t\tfinal GStringTemplateEngine templateEngine = new GStringTemplateEngine();\n\t\ttry (InputStream in = StatusReportGenerator.class.getResourceAsStream(\"/templates/StatusReportTemplate.html\")) {\n\t\t\ttemplate = templateEngine.createTemplate(new InputStreamReader(in));\n\t\t} catch (Exception e) {\n\t\t\t//pass\n\t\t}\n\t\tif (reportGeneratorEnabled) {\n\t\t\twriteServerStatusFile();\n\t\t}\n\t}\n\n\tpublic void writeServerStatusFile() {\n\t\ttry (Writer w = new FileWriter(\"logs/server-info.html\")) {\n\t\t\tprocessTemplate(w);\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Cannot write server-info.html file\", e);\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\teventBus.unregisterAll(this);\n\t}\n\n\t@HandleEvent\n\tvoid handleTickEvent(final TickMinuteEvent event) {\n\t\tif (reportGeneratorEnabled) {\n\t\t\twriteServerStatusFile();\n\t\t}\n\t}\n\n\tprivate String prepareJSON() {\n\t\tfinal TigaseRuntime runtime = TigaseRuntime.getTigaseRuntime();\n\n\t\tfinal StringBuilder sb = new StringBuilder();\n\t\tsb.append(\"{\");\n\n\t\tappend(sb, \"data-uptime\", runtime.getUptimeString()).append(\",\");\n\t\tappend(sb, \"data-load-average\", runtime.getLoadAverage()).append(\",\");\n\t\tappend(sb, \"data-cpus-no\", runtime.getCPUsNumber()).append(\",\");\n\t\tappend(sb, \"data-threads-count\", runtime.getThreadsNumber()).append(\",\");\n\n\t\tappend(sb, \"data-cpu-usage-proc\", runtime.getCPUUsage()).append(\",\");\n\t\tappend(sb, \"data-heap-usage-proc\", runtime.getHeapMemUsage()).append(\",\");\n\t\tappend(sb, \"data-nonheap-usage-proc\", runtime.getNonHeapMemUsage()).append(\",\");\n\n\t\tSimpleDateFormat dtf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n\t\tappend(sb, \"version\", XMPPServer.getImplementationVersion()).append(\",\");\n\t\tappend(sb, \"report-creation-timstamp\", dtf.format(new Date()));\n\t\tsb.append(\"}\");\n\t\treturn sb.toString();\n\t}\n\n\tprivate void processTemplate(final Writer writer) throws IOException, ClassNotFoundException {\n//\t\tfinal GStringTemplateEngine templateEngine = new GStringTemplateEngine();\n//\t\ttry (InputStream in = StatusReportGenerator.class.getResourceAsStream(\"/templates/StatusReportTemplate.html\")) {\n//\t\t\tfinal Template template = templateEngine.createTemplate(new InputStreamReader(in));\n\t\t\tMap context = new HashMap();\n\t\t\tcontext.put(\"dataJson\", prepareJSON());\n\n\t\t\tWritable result = template.make(context);\n\t\t\tresult.writeTo(writer);\n//\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/package-info.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/**\n * The main package with top level API for server side components.\n */\npackage tigase.server;\n"
  },
  {
    "path": "src/main/java/tigase/server/rtbl/RTBL.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.rtbl;\n\nimport tigase.util.Algorithms;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Set;\nimport java.util.concurrent.CopyOnWriteArraySet;\nimport java.util.logging.Logger;\n\npublic class RTBL {\n\n\tprivate static final Logger logger = Logger.getLogger(RTBL.class.getCanonicalName());\n\tprivate final RTBLRepository.Key key;\n\tprivate final String hash;\n\n\tprivate final CopyOnWriteArraySet<String> blockedHashes;\n\n\tpublic RTBL(RTBLRepository.Key key, String hash) {\n\t\tthis(key, hash, new CopyOnWriteArraySet<>());\n\t}\n\t\n\tpublic RTBL(RTBLRepository.Key key, String hash, Set<String> hashes) {\n\t\tthis.key = key;\n\t\tthis.hash = hash;\n\t\tthis.blockedHashes = new CopyOnWriteArraySet<>(hashes);\n\t}\n\n\tpublic RTBL(BareJID jid, String node, String hash, Set<String> hashes) {\n\t\tthis(new RTBLRepository.Key(jid, node), hash, hashes);\n\t}\n\n\tpublic BareJID getJID() {\n\t\treturn key.getJid();\n\t}\n\n\tpublic String getNode() {\n\t\treturn key.getNode();\n\t}\n\n\tpublic RTBLRepository.Key getKey() {\n\t\treturn key;\n\t}\n\n\tpublic String getHash() {\n\t\treturn hash;\n\t}\n\n\tpublic boolean isBlocked(BareJID jid) {\n\t\ttry {\n\t\t\tMessageDigest md = MessageDigest.getInstance(hash);\n\t\t\tif (isBlocked(jid.getDomain(), md)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tmd.reset();\n\t\t\tif (isBlocked(jid.toString(), md)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\tlogger.warning(\"No hashing mechanism \" + hash + \" required by RTBL (\" + key + \")\");\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate boolean isBlocked(String jid, MessageDigest md) {\n\t\tString hash = Algorithms.bytesToHex(md.digest(jid.getBytes(StandardCharsets.UTF_8)));\n\t\treturn blockedHashes.contains(hash);\n\t}\n\n\tpublic Set<String> getBlocked() {\n\t\treturn blockedHashes;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn key.toString() + \", hash: \" + hash + \", blocked: \" + blockedHashes;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/rtbl/RTBLComponent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.rtbl;\n\nimport tigase.cluster.ClusterConnectionManager;\nimport tigase.component.AbstractKernelBasedComponent;\nimport tigase.component.modules.impl.AdHocCommandModule;\nimport tigase.component.modules.impl.DiscoveryModule;\nimport tigase.eventbus.HandleEvent;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\n\nimport java.util.List;\n\n@Bean(name = \"rtbl-component\", parent = Kernel.class, active = true, exportable = true)\n@ConfigType({ConfigTypeEnum.DefaultMode})\npublic class RTBLComponent extends AbstractKernelBasedComponent {\n\n\t@Inject\n\tprivate RTBLRepository repository;\n\t@Inject(nullAllowed = true)\n\tprivate RTBLSubscribeModule subscribeModule;\n\t@Inject(nullAllowed = true)\n\tprivate RTBLFetchModule fetchModule;\n\n\t@Override\n\tpublic boolean isDiscoNonAdmin() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tprotected void registerModules(Kernel kernel) {\n\t\tkernel.registerBean(AdHocCommandModule.class).exec();\n\t\tkernel.registerBean(DiscoveryModule.class).exec();\n\t}\n\n\t@Override\n\tpublic void start() {\n\t\tsuper.start();\n\t\teventBus.registerAll(this);\n\t}\n\n\t@Override\n\tpublic void stop() {\n\t\tsuper.stop();\n\t\teventBus.unregisterAll(this);\n\t}\n\n\t@Override\n\tpublic boolean isSubdomain() {\n\t\treturn true;\n\t}\n\n\t@HandleEvent\n\tpublic void serverInitialized(ClusterConnectionManager.ClusterInitializedEvent event) {\n\t\tif (subscribeModule != null && fetchModule != null) {\n\t\t\tList<RTBL> blocklists = repository.getBlockLists();\n\n\t\t\tfor (RTBL rtbl : blocklists) {\n\t\t\t\tsubscribeModule.subscribe(rtbl.getJID(), rtbl.getNode());\n\t\t\t\tfetchModule.fetch(rtbl.getJID(), rtbl.getNode());\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warning(\"failed to initialize RTBLComponent - missing subscribeModule: \" + subscribeModule + \", fetchModule: \" +  fetchModule);\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/rtbl/RTBLEventModule.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.rtbl;\n\nimport tigase.component.exceptions.ComponentException;\nimport tigase.component.modules.AbstractModule;\nimport tigase.criteria.Criteria;\nimport tigase.criteria.ElementCriteria;\nimport tigase.criteria.Or;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Packet;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.List;\n\n@Bean(name = \"rtblEventModule\", parent = RTBLComponent.class, active = true)\npublic class RTBLEventModule\n\t\textends AbstractModule {\n\n\tprivate static final String PUBSUB_XMLNS = \"http://jabber.org/protocol/pubsub\";\n\tprivate static final String PUBSUB_EVENT_XMLNS = PUBSUB_XMLNS + \"#event\";\n\n\tpublic Criteria criteria = ElementCriteria.name(\"message\")\n\t\t\t.add(ElementCriteria.name(\"event\", PUBSUB_EVENT_XMLNS).add(new Or(ElementCriteria.name(\"items\"),ElementCriteria.name(\"purge\"))));\n\n\t@Inject\n\tprivate RTBLRepository repository;\n\n\t@Override\n\tpublic Criteria getModuleCriteria() {\n\t\treturn criteria;\n\t}\n\n\t@Override\n\tpublic void process(Packet packet) throws ComponentException, TigaseStringprepException {\n\t\tElement event = packet.getElemChild(\"event\", PUBSUB_EVENT_XMLNS);\n\t\tJID from = packet.getStanzaFrom();\n\t\tif (from == null) {\n\t\t\treturn;\n\t\t}\n\t\tElement items = event.getChild(\"items\");\n\t\tif (items == null) {\n\t\t\tElement purge = event.getChild(\"purge\");\n\t\t\tif (purge == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tString node = event.getAttributeStaticStr(\"node\");\n\t\t\tif (node != null) {\n\t\t\t\trepository.purge(from.getBareJID(), node);\n\t\t\t}\n\t\t}\n\t\tString node = items.getAttributeStaticStr(\"node\");\n\t\tif (node == null) {\n\t\t\treturn;\n\t\t}\n\t\tList<Element> notifications = items.getChildren();\n\t\tif (notifications == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tfor (Element notification : notifications) {\n\t\t\tString id = notification.getAttributeStaticStr(\"id\");\n\t\t\tif (id == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tswitch (notification.getName()) {\n\t\t\t\tcase \"retract\" -> repository.update(from.getBareJID(), node, RTBLRepository.Action.remove, id);\n\t\t\t\tcase \"item\" -> repository.update(from.getBareJID(), node, RTBLRepository.Action.add, id);\n\t\t\t\tdefault -> {}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/rtbl/RTBLFetchModule.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.rtbl;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Iq;\nimport tigase.util.common.TimerTask;\nimport tigase.xml.Element;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n@Bean(name = \"rtblFetchModule\", parent = RTBLComponent.class, active = true)\npublic class RTBLFetchModule\n\t\textends RTBLIqModule<String> {\n\n\tprivate static final String PUBSUB_XMLNS = \"http://jabber.org/protocol/pubsub\";\n\n\t@Inject\n\tprivate RTBLRepository repository;\n\n\tpublic RTBLFetchModule() {\n\t\tsuper(\"fetch-\");\n\t}\n\n\tpublic void fetch(BareJID jid, String node) {\n\t\tsendIq(jid, StanzaType.get, iqEl -> {\n\t\t\tiqEl.withElement(\"pubsub\", PUBSUB_XMLNS, pubsubEl -> {\n\t\t\t\tpubsubEl.withElement(\"items\", itemsEl -> {\n\t\t\t\t\titemsEl.withAttribute(\"node\", node);\n\t\t\t\t});\n\t\t\t});\n\t\t}, () -> node);\n\t}\n\n\t@Override\n\tprotected void handleResult(ResultEvent event, String node) {\n\t\tswitch (event.getResult()) {\n\t\t\tcase failureRetry -> getComponent().addTimerTask(new TimerTask() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tfetch(event.getJid(), node);\n\t\t\t\t}\n\t\t\t}, 10000);\n\t\t\tcase success, failureNoRetry -> {}\n\t\t}\n\n\t}\n\n\t@Override\n\tprotected void handleSuccess(Iq iq) {\n\t\tElement pubsubEl = iq.getElemChild(\"pubsub\", PUBSUB_XMLNS);\n\t\tif (pubsubEl == null) {\n\t\t\treturn;\n\t\t}\n\t\tElement itemsEl = pubsubEl.getChild(\"items\");\n\t\tif (itemsEl == null) {\n\t\t\treturn;\n\t\t}\n\t\tString node = itemsEl.getAttributeStaticStr(\"node\");\n\t\tList<Element> items = itemsEl.findChildren(el -> \"item\".equals(el.getName()));\n\t\tif (node == null || items == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tRTBL existingRTBL = repository.getBlockList(iq.getStanzaFrom().getBareJID(), node);\n\t\tif (existingRTBL != null) {\n\t\t\tRTBL rtbl = new RTBL(iq.getStanzaFrom().getBareJID(), node, existingRTBL.getHash(),\n\t\t\t\t\t\t\t\t items.stream().map(it -> it.getAttributeStaticStr(\"id\")).collect(Collectors.toSet()));\n\t\t\trepository.update(rtbl);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/rtbl/RTBLIqModule.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.rtbl;\n\nimport tigase.component.exceptions.ComponentException;\nimport tigase.component.modules.AbstractModule;\nimport tigase.criteria.Criteria;\nimport tigase.criteria.ElementCriteria;\nimport tigase.criteria.Or;\nimport tigase.eventbus.EventBusEvent;\nimport tigase.eventbus.HandleEvent;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.vhosts.VHostManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.io.Serializable;\nimport java.util.Objects;\nimport java.util.UUID;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Consumer;\nimport java.util.function.Supplier;\nimport java.util.stream.Stream;\n\npublic abstract class RTBLIqModule<RCTX> extends AbstractModule implements Initializable, UnregisterAware {\n\n\tprivate final Criteria criteria = new Or(ElementCriteria.nameType(\"iq\", \"result\"),\n\t\t\t\t\t\t\t\t\t  ElementCriteria.nameType(\"iq\", \"error\"));\n\t@Inject(bean = \"rtbl-component\")\n\tprivate RTBLComponent component;\n\t@Inject\n\tprivate VHostManager vHostManager;\n\n\tprivate final String requestPrefix;\n\tprivate ConcurrentHashMap<RequestKey,RCTX> activeRequests = new ConcurrentHashMap<>();\n\n\tpublic RTBLIqModule(String requestPrefix) {\n\t\tthis.requestPrefix = requestPrefix;\n\t}\n\n\tpublic RTBLComponent getComponent() {\n\t\treturn component;\n\t}\n\n\tpublic Criteria getModuleCriteria() {\n\t\treturn criteria;\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\teventBus.registerAll(this);\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\tif (eventBus != null) {\n\t\t\teventBus.unregisterAll(this);\n\t\t}\n\t}\n\n\tprotected JID getOwnJID() {\n\t\treturn JID.jidInstanceNS(component.getName(), vHostManager.getDefVHostItem().getDomain());\n\t}\n\n\tprotected <T> void sendIq(BareJID to, StanzaType type, Consumer<Element> consumer, Supplier<RCTX> handleContextSupplier) {\n\t\tString id = requestPrefix + UUID.randomUUID().toString();\n\t\tElement iqEl = new Element(\"iq\").withAttribute(\"type\", type.name()).withAttribute(\"id\", id);\n\t\tconsumer.accept(iqEl);\n\t\tRequestKey requestKey = new RequestKey(to, id);\n\t\tactiveRequests.put(requestKey, handleContextSupplier.get());\n\t\ttry {\n\t\t\twrite(new Iq(iqEl, getOwnJID(), JID.jidInstance(to)));\n\t\t} catch (Throwable ex) {\n\t\t\tactiveRequests.remove(requestKey);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void process(Packet packet) throws ComponentException, TigaseStringprepException {\n\t\tJID from = packet.getStanzaFrom();\n\t\tif (from == null) {\n\t\t\treturn;\n\t\t}\n\t\tString id = packet.getAttributeStaticStr(\"id\");\n\t\tif (id == null) {\n\t\t\tthrow new ComponentException(Authorization.BAD_REQUEST);\n\t\t}\n\t\tif (!id.startsWith(requestPrefix)) {\n\t\t\treturn;\n\t\t}\n\n\t\tResultEvent.Result result = ResultEvent.Result.fromPacket(packet);\n\t\tif (result == ResultEvent.Result.success) {\n\t\t\thandleSuccess((Iq) packet);\n\t\t}\n\t\tgetEventBus().fire(new ResultEvent(getClass(), from.getBareJID(), id, result));\n\t}\n\n\tprotected void handleSuccess(Iq iq) {};\n\n\t@HandleEvent\n\tpublic void handleResultEvent(ResultEvent event) {\n\t\tif (event.isForClass(getClass())) {\n\t\t\tRCTX ctx = activeRequests.remove(new RequestKey(event.getJid(), event.getId()));\n\t\t\tif (ctx != null) {\n\t\t\t\thandleResult(event, ctx);\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected abstract void handleResult(ResultEvent event, RCTX ctx);\n\n\tpublic static class RequestKey {\n\t\tprivate final BareJID jid;\n\t\tprivate final String id;\n\n\t\tpublic RequestKey(BareJID jid, String id) {\n\t\t\tthis.jid = jid;\n\t\t\tthis.id = id;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object o) {\n\t\t\tif (this == o) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (!(o instanceof RequestKey that)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn Objects.equals(jid, that.jid) && Objects.equals(id, that.id);\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Objects.hash(jid, id);\n\t\t}\n\t}\n\n\tpublic static class ResultEvent\n\t\t\timplements Serializable, EventBusEvent {\n\n\t\tpublic enum Result {\n\t\t\tsuccess,\n\t\t\tfailureRetry,\n\t\t\tfailureNoRetry;\n\n\t\t\tpublic static ResultEvent.Result fromPacket(Packet packet) throws ComponentException {\n\t\t\t\tStanzaType type = packet.getType();\n\t\t\t\tif (type == null) {\n\t\t\t\t\tthrow new ComponentException(Authorization.BAD_REQUEST);\n\t\t\t\t}\n\t\t\t\treturn switch (type) {\n\t\t\t\t\tcase result -> Result.success;\n\t\t\t\t\tcase error -> {\n\t\t\t\t\t\tElement error = packet.getElemChild(\"error\");\n\t\t\t\t\t\tif (error != null) {\n\t\t\t\t\t\t\tyield Stream.of(Authorization.INTERNAL_SERVER_ERROR, Authorization.REMOTE_SERVER_NOT_FOUND,\n\t\t\t\t\t\t\t\t\t\t\tAuthorization.REMOTE_SERVER_TIMEOUT)\n\t\t\t\t\t\t\t\t\t\t  .map(Authorization::getCondition)\n\t\t\t\t\t\t\t\t\t\t  .anyMatch(name -> error.getChild(name,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"urn:ietf:params:xml:ns:xmpp-stanzas\") !=\n\t\t\t\t\t\t\t\t\t\t\t\t  null) ? Result.failureRetry : Result.failureNoRetry;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tyield Result.failureNoRetry;\n\t\t\t\t\t}\n\t\t\t\t\tdefault -> throw new ComponentException(Authorization.BAD_REQUEST);\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\tprivate BareJID jid;\n\t\tprivate String id;\n\n\t\tprivate Result result;\n\t\tprivate String className;\n\n\n\t\tpublic ResultEvent() {}\n\t\tpublic ResultEvent(Class<? extends RTBLIqModule> forClazz, BareJID jid, String id, ResultEvent.Result result) {\n\t\t\tthis.className = forClazz.getSimpleName();\n\t\t\tthis.jid = jid;\n\t\t\tthis.id = id;\n\t\t\tthis.result = result;\n\t\t}\n\n\t\tpublic BareJID getJid() {\n\t\t\treturn jid;\n\t\t}\n\n\t\tpublic void setJid(BareJID jid) {\n\t\t\tthis.jid = jid;\n\t\t}\n\n\t\tpublic String getId() {\n\t\t\treturn id;\n\t\t}\n\n\t\tpublic void setId(String id) {\n\t\t\tthis.id = id;\n\t\t}\n\n\t\tpublic ResultEvent.Result getResult() {\n\t\t\treturn result;\n\t\t}\n\n\t\tpublic void setResult(ResultEvent.Result result) {\n\t\t\tthis.result = result;\n\t\t}\n\n\t\tpublic boolean isForClass(Class<? extends RTBLIqModule> clazz) {\n\t\t\treturn clazz.getSimpleName().equals(className);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/rtbl/RTBLRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.rtbl;\n\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserRepository;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.EventBusEvent;\nimport tigase.eventbus.HandleEvent;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.core.Kernel;\nimport tigase.stats.StatisticsContainerIfc;\nimport tigase.stats.StatisticsList;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.io.Serializable;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CopyOnWriteArraySet;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\n@Bean(name = \"rtblRepository\", parent = Kernel.class, active = true, exportable = true)\npublic class RTBLRepository implements Initializable, UnregisterAware, StatisticsContainerIfc {\n\t\n\tprivate static final Logger logger = Logger.getLogger(RTBLRepository.class.getCanonicalName());\n\n\t@Inject\n\tprivate UserRepository userRepository;\n\t@Inject(bean = \"eventBus\")\n\tprivate EventBus eventBus;\n\n\t@ConfigField(desc = \"Reload interval\")\n\tprivate long reloadInterval = TimeUnit.MINUTES.toMillis(10);\n\n\tprivate BareJID repoUser = BareJID.bareJIDInstanceNS(\"rtbl\");\n\n\tprivate ConcurrentHashMap<Key, RTBL> cache = new ConcurrentHashMap<>();\n\n\tprivate Timer timer;\n\n\tprivate String name = \"rtblRepository\";\n\t\n\tpublic RTBLRepository() {\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\tif (timer != null) {\n\t\t\ttimer.cancel();\n\t\t}\n\t\tif (eventBus != null) {\n\t\t\teventBus.unregisterAll(this);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t\tlist.add(getName(), \"No. of lists\", cache.size(), Level.FINE);\n\t\tcache.values().stream().sorted(Comparator.comparing(RTBL::getJID).thenComparing(RTBL::getNode)).forEachOrdered(rtbl -> {\n\t\t\tlist.add(getName(), \"No. of items in \" + rtbl.getJID() + \"/\" + rtbl.getNode(), rtbl.getBlocked().size(), Level.FINEST);\n\t\t});\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\teventBus.registerAll(this);\n\t\treload();\n\t\tif (reloadInterval > 0) {\n\t\t\ttimer = new Timer(true);\n\t\t\ttimer.scheduleAtFixedRate(new TimerTask() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\treload();\n\t\t\t\t}\n\t\t\t}, reloadInterval, reloadInterval);\n\t\t}\n\t}\n\n\tpublic List<RTBL> getBlockLists() {\n\t\treturn new ArrayList<>(cache.values());\n\t}\n\n\tpublic RTBL getBlockList(BareJID jid, String node) {\n\t\treturn cache.get(new Key(jid, node));\n\t}\n\n\tpublic boolean isBlocked(BareJID jid) {\n\t\tfor (RTBL rtbl : cache.values()) {\n\t\t\tif (rtbl.isBlocked(jid)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic void update(RTBL rtbl) {\n\t\tKey key = rtbl.getKey();\n\t\tif (!cache.containsKey(key)) {\n\t\t\treturn;\n\t\t}\n\t\tcache.put(key, rtbl);\n\t\ttry {\n\t\t\tif (!userRepository.userExists(repoUser)) {\n\t\t\t\tuserRepository.addUser(repoUser);\n\t\t\t}\n\t\t} catch (TigaseDBException ex) {\n\t\t\t// ignore, possible race condition..\n\t\t}\n\t\ttry {\n\t\t\tRTBL oldList = loadList(key);\n\t\t\tSet<String> prevBlocked = Optional.ofNullable(oldList).map(RTBL::getBlocked).orElse(Collections.emptySet());\n\t\t\tList<String> newToAdd = rtbl.getBlocked().stream().filter(it -> !prevBlocked.contains(it)).toList();\n\n\t\t\tfor (String item : newToAdd) {\n\t\t\t\tupdateStore(key, Action.add, item);\n\t\t\t}\n\t\t\tList<String> oldToRemove = prevBlocked.stream().filter(it -> !rtbl.getBlocked().contains(it)).collect(\n\t\t\t\t\tCollectors.toList());\n\t\t\tfor (String item : oldToRemove) {\n\t\t\t\tupdateStore(key, Action.remove, item);\n\t\t\t}\n\t\t} catch (TigaseDBException ex) {\n\t\t\tlogger.log(Level.WARNING, \"failed to save updated RTBL \" + rtbl);\n\t\t}\n\t}\n\n\tpublic enum Action {\n\t\tadd,\n\t\tremove\n\t}\n\n\tpublic void add(BareJID pubsubJid, String node, String hash) throws TigaseDBException {\n\t\tKey key = new Key(pubsubJid, node);\n\t\tif (cache.get(key) == null) {\n\t\t\tuserRepository.setData(repoUser, key.getSubnode(), \"jid\", pubsubJid.toString());\n\t\t\tuserRepository.setData(repoUser, key.getSubnode(), \"node\", node);\n\t\t\tuserRepository.setData(repoUser, key.getSubnode(), \"hash\", hash);\n\t\t\teventBus.fire(new RTBLAdded(pubsubJid, node, hash));\n\t\t}\n\t}\n\n\t@HandleEvent\n\tpublic void handleAdded(RTBLAdded event) {\n\t\tKey key = event.getKey();\n\t\tcache.putIfAbsent(key, new RTBL(key, event.getHash()));\n\t}\n\n\tpublic void remove(BareJID pubsubJid, String node) throws TigaseDBException {\n\t\tKey key = new Key(pubsubJid, node);\n\t\tuserRepository.removeSubnode(repoUser, key.getSubnode());\n\t\teventBus.fire(new RTBLRemoved(pubsubJid, node));\n\t}\n\n\t@HandleEvent\n\tpublic void handleRemoved(RTBLRemoved event) {\n\t\tKey key = event.getKey();\n\t\tcache.remove(key);\n\t}\n\n\tpublic void update(BareJID pubsubJid, String node, Action action, String id) {\n\t\tKey key = new Key(pubsubJid, node);\n\t\tupdateStore(key, action, id);\n\t\teventBus.fire(new RTBLChange(pubsubJid, node, action, id));\n\t}\n\n\t@HandleEvent\n\tpublic void handleChange(RTBLChange event) {\n\t\tKey key = event.getKey();\n\t\tRTBL rtbl = cache.get(key);\n\t\tif (rtbl != null) {\n\t\t\tswitch (event.getAction()) {\n\t\t\t\tcase add -> {\n\t\t\t\t\trtbl.getBlocked().add(event.getId());\n\t\t\t\t}\n\t\t\t\tcase remove -> {\n\t\t\t\t\trtbl.getBlocked().remove(event.getId());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void purge(BareJID pubsubJid, String node) {\n\t\tKey key = new Key(pubsubJid, node);\n\t\tRTBL rtbl = cache.get(key);\n\t\tif (rtbl != null) {\n\t\t\tSet<String> toRemove = new HashSet<>(rtbl.getBlocked());\n\t\t\trtbl.getBlocked().clear();\n\t\t\tfor (String item : toRemove) {\n\t\t\t\tupdateStore(key, Action.remove, item);\n\t\t\t}\n\t\t\teventBus.fire(new RTBLReload(pubsubJid, node));\n\t\t}\n\t}\n\n\tpublic void reload(BareJID pubsubJid, String node) {\n\t\teventBus.fire(new RTBLRemoved(pubsubJid, node));\n\t}\n\n\t@HandleEvent\n\tpublic void handleReload(RTBLReload event) {\n\t\ttry {\n\t\t\tRTBL rtbl = loadList(event.getKey());\n\t\t\tif (rtbl != null) {\n\t\t\t\tcache.replace(rtbl.getKey(), rtbl);\n\t\t\t}\n\t\t} catch (TigaseDBException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tprivate void updateStore(Key key, Action action, String id) {\n\t\tswitch (action) {\n\t\t\tcase add -> {\n\t\t\t\ttry {\n\t\t\t\t\tuserRepository.setData(repoUser, key.getSubnode(), id, id);\n\t\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\t\tlogger.log(Level.WARNING, \"failed to save updated RTBL \" + key + \", adding \" + id + \" failed\", ex);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcase remove -> {\n\t\t\t\ttry {\n\t\t\t\t\tuserRepository.removeData(repoUser, key.getSubnode(), id);\n\t\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\t\tlogger.log(Level.WARNING, \"failed to save updated RTBL \" + key + \", removing \" + id + \" failed\", ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\tprivate RTBL loadList(Key key) throws TigaseDBException {\n\t\tif (userRepository.userExists(repoUser)) {\n\t\t\ttry {\n\t\t\t\tMap<String, String> data = userRepository.getDataMap(repoUser, key.getSubnode());\n\t\t\t\tBareJID jid = BareJID.bareJIDInstanceNS(data.remove(\"jid\"));\n\t\t\t\tString node = data.remove(\"node\");\n\t\t\t\tString hash = data.remove(\"hash\");\n\t\t\t\tif (jid == null || hash == null || node == null) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\treturn new RTBL(jid, node, hash, new CopyOnWriteArraySet<>(data.keySet()));\n\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\tlogger.log(Level.WARNING, \"could not load RTBL for \" + key, ex);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprotected void reload() {\n\t\ttry {\n\t\t\tif (userRepository.userExists(repoUser)) {\n\t\t\t\tString[] subnodes = userRepository.getSubnodes(repoUser);\n\t\t\t\tif (subnodes != null) {\n\t\t\t\t\tfor (String subnode : subnodes) {\n\t\t\t\t\t\tKey key = Key.parse(subnode);\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tif (key != null) {\n\t\t\t\t\t\t\t\tRTBL rtbl = loadList(key);\n\t\t\t\t\t\t\t\tif (rtbl != null) {\n\t\t\t\t\t\t\t\t\tcache.put(key, rtbl);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tcache.remove(key);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\t\t\t\tlogger.log(Level.WARNING, \"could not load RTBL \" + key, ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (TigaseDBException ex) {\n\t\t\tlogger.log(Level.WARNING, \"could not load list of RTBLs\", ex);\n\t\t}\n\t}\n\n\tpublic static class Key {\n\t\tprivate final BareJID jid;\n\t\tprivate final String node;\n\n\t\tpublic Key(BareJID jid, String node) {\n\t\t\tthis.jid = jid;\n\t\t\tthis.node = node;\n\t\t}\n\n\t\tpublic String getNode() {\n\t\t\treturn node;\n\t\t}\n\n\t\tpublic BareJID getJid() {\n\t\t\treturn jid;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object o) {\n\t\t\tif (this == o) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (!(o instanceof Key key)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn Objects.equals(jid, key.jid) && Objects.equals(node, key.node);\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Objects.hash(jid, node);\n\t\t}\n\n\t\tpublic String getSubnode() {\n\t\t\treturn jid.toString() + \"|\" + node.replaceAll(\"/\", \"|\");\n\t\t}\n\n\t\tpublic static Key parse(String data) {\n\t\t\tString[] parts = data.split(\"\\\\|\");\n\t\t\tif (parts != null && parts.length > 1) {\n\t\t\t\treturn new Key(BareJID.bareJIDInstanceNS(parts[0]),\n\t\t\t\t\t\t\t   Arrays.stream(parts).skip(1).collect(Collectors.joining(\"|\")));\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"jid: \" + jid + \", node: \" + node;\n\t\t}\n\t}\n\n\tpublic static abstract class RTBLEvent implements Serializable, EventBusEvent {\n\t\tprivate BareJID jid;\n\t\tprivate String node;\n\n\t\tpublic RTBLEvent() {}\n\n\t\tpublic RTBLEvent(BareJID jid, String node) {\n\t\t\tthis.jid = jid;\n\t\t\tthis.node = node;\n\t\t}\n\n\t\tpublic BareJID getJid() {\n\t\t\treturn jid;\n\t\t}\n\n\t\tpublic void setJid(BareJID jid) {\n\t\t\tthis.jid = jid;\n\t\t}\n\n\t\tpublic String getNode() {\n\t\t\treturn node;\n\t\t}\n\n\t\tpublic void setNode(String node) {\n\t\t\tthis.node = node;\n\t\t}\n\n\t\tpublic Key getKey() {\n\t\t\treturn new Key(jid, node);\n\t\t}\n\n\t}\n\n\tpublic static class RTBLAdded extends RTBLEvent implements Serializable, EventBusEvent {\n\n\t\tprivate String hash;\n\n\t\tpublic RTBLAdded() {}\n\n\t\tpublic RTBLAdded(BareJID jid, String node, String hash) {\n\t\t\tsuper(jid, node);\n\t\t\tthis.hash = hash;\n\t\t}\n\n\t\tpublic String getHash() {\n\t\t\treturn hash;\n\t\t}\n\n\t\tpublic void setHash(String hash) {\n\t\t\tthis.hash = hash;\n\t\t}\n\t\t\n\t}\n\n\tpublic static class RTBLRemoved extends RTBLEvent implements Serializable, EventBusEvent {\n\n\t\tpublic RTBLRemoved() {}\n\n\t\tpublic RTBLRemoved(BareJID jid, String node) {\n\t\t\tsuper(jid, node);\n\t\t}\n\n\t}\n\n\tpublic static class RTBLReload extends RTBLEvent implements Serializable, EventBusEvent {\n\n\t\tpublic RTBLReload() {}\n\n\t\tpublic RTBLReload(BareJID jid, String node) {\n\t\t\tsuper(jid, node);\n\t\t}\n\n\t}\n\n\tpublic static class RTBLChange extends RTBLEvent implements Serializable, EventBusEvent {\n\t\tprivate Action action;\n\t\tprivate String id;\n\n\t\tpublic RTBLChange() {}\n\n\t\tpublic RTBLChange(BareJID jid, String node, Action action, String id) {\n\t\t\tsuper(jid, node);\n\t\t\tthis.action = action;\n\t\t\tthis.id = id;\n\t\t}\n\t\t\n\t\tpublic Action getAction() {\n\t\t\treturn action;\n\t\t}\n\n\t\tpublic void setAction(Action action) {\n\t\t\tthis.action = action;\n\t\t}\n\n\t\tpublic String getId() {\n\t\t\treturn id;\n\t\t}\n\n\t\tpublic void setId(String id) {\n\t\t\tthis.id = id;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/rtbl/RTBLSubscribeModule.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.rtbl;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.util.common.TimerTask;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.BareJID;\n\n@Bean(name = \"rtblSubscribeModule\", parent = RTBLComponent.class, active = true)\npublic class RTBLSubscribeModule\n\t\textends RTBLIqModule<RTBLSubscribeModule.Context> {\n\n\tprivate static final String PUBSUB_XMLNS = \"http://jabber.org/protocol/pubsub\";\n\n\tpublic RTBLSubscribeModule() {\n\t\tsuper(\"sub-\");\n\t}\n\n\tpublic void subscribe(BareJID jid, String node) {\n\t\tsendIq(jid, StanzaType.set, iqEl -> {\n\t\t\tiqEl.withElement(\"pubsub\", PUBSUB_XMLNS, pubsubEl -> {\n\t\t\t\tpubsubEl.withElement(\"subscribe\", subscribeEl -> {\n\t\t\t\t\tsubscribeEl.withAttribute(\"node\", node).withAttribute(\"jid\", getOwnJID().toString());\n\t\t\t\t});\n\t\t\t});\n\t\t}, () -> new Context(Action.subscribe, node));\n\t}\n\n\tpublic void unsubscribe(BareJID jid, String node) {\n\t\tsendIq(jid, StanzaType.set, iqEl -> {\n\t\t\tiqEl.withElement(\"pubsub\", PUBSUB_XMLNS, pubsubEl -> {\n\t\t\t\tpubsubEl.withElement(\"unsubscribe\", unsubscribeEl -> {\n\t\t\t\t\tunsubscribeEl.withAttribute(\"node\", node).withAttribute(\"jid\", getOwnJID().toString());\n\t\t\t\t});\n\t\t\t});\n\t\t}, () -> new Context(Action.unsubscribe, node));\n\t}\n\t\n\t@Override\n\tprotected void handleResult(ResultEvent event, Context ctx) {\n\t\tif (event.getResult() == ResultEvent.Result.failureRetry) {\n\t\t\tgetComponent().addTimerTask(new TimerTask() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tswitch (ctx.action()) {\n\t\t\t\t\t\tcase subscribe -> subscribe(event.getJid(), ctx.node());\n\t\t\t\t\t\tcase unsubscribe -> unsubscribe(event.getJid(), ctx.node());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}, 10000);\n\t\t}\n\t}\n\n\tprivate enum Action {\n\t\tsubscribe,\n\t\tunsubscribe\n\t}\n\n\tpublic record Context(Action action, String node) {}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/rtbl/adhoc/AbstractAdHocCommand.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.rtbl.adhoc;\n\nimport tigase.component.adhoc.AdHocCommand;\nimport tigase.component.adhoc.AdHocCommandException;\nimport tigase.component.adhoc.AdHocResponse;\nimport tigase.component.adhoc.AdhHocRequest;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.rtbl.RTBLComponent;\nimport tigase.server.rtbl.RTBLRepository;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic abstract class AbstractAdHocCommand implements AdHocCommand {\n\n\tprivate static final Logger log = Logger.getLogger(AbstractAdHocCommand.class.getCanonicalName());\n\n\t@Inject\n\tprivate RTBLComponent component;\n\t@Inject\n\tprivate RTBLRepository repository;\n\t@Override\n\tpublic void execute(AdhHocRequest request, AdHocResponse response) throws AdHocCommandException {\n\t\ttry {\n\t\t\tfinal Element data = request.getCommand().getChild(\"x\", \"jabber:x:data\");\n\n\t\t\tif (request.isAction(\"cancel\")) {\n\t\t\t\tresponse.cancelSession();\n\t\t\t} else {\n\t\t\t\tif (data == null) {\n\t\t\t\t\tresponse.getElements().add(prepareForm(request, response).getElement());\n\t\t\t\t\tresponse.startSession();\n\t\t\t\t} else {\n\t\t\t\t\tForm form = new Form(data);\n\t\t\t\t\tif (form.isType(\"submit\")) {\n\t\t\t\t\t\tForm responseForm = submitForm(request, response, form);\n\t\t\t\t\t\tif (responseForm != null) {\n\t\t\t\t\t\t\tresponse.getElements().add(responseForm.getElement());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (AdHocCommandException ex) {\n\t\t\tthrow ex;\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.FINE, \"Exception during execution of adhoc command \" + getNode(), e);\n\t\t\tthrow new AdHocCommandException(Authorization.INTERNAL_SERVER_ERROR, e.getMessage());\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isAllowedFor(JID jid) {\n\t\treturn component.isAdmin(jid);\n\t}\n\n\tprotected RTBLRepository getRepository() {\n\t\treturn repository;\n\t}\n\n\tprotected abstract Form prepareForm(AdhHocRequest request, AdHocResponse response) throws AdHocCommandException;\n\n\tprotected abstract Form submitForm(AdhHocRequest request, AdHocResponse response, Form form)\n\t\t\tthrows AdHocCommandException;\n\n\tprotected String assertNotEmpty(String input, String message) throws AdHocCommandException {\n\t\tif (input == null || input.isBlank()) {\n\t\t\tthrow new AdHocCommandException(Authorization.BAD_REQUEST, message);\n\t\t}\n\t\treturn input.trim();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/rtbl/adhoc/RTBLAddCommand.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.rtbl.adhoc;\n\nimport tigase.component.adhoc.AdHocCommandException;\nimport tigase.component.adhoc.AdHocResponse;\nimport tigase.component.adhoc.AdhHocRequest;\nimport tigase.db.TigaseDBException;\nimport tigase.form.Field;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.rtbl.RTBL;\nimport tigase.server.rtbl.RTBLComponent;\nimport tigase.server.rtbl.RTBLFetchModule;\nimport tigase.server.rtbl.RTBLSubscribeModule;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Bean(name = \"rtbl-command-add\", parent = RTBLComponent.class, active = true)\npublic class RTBLAddCommand\n\t\textends AbstractAdHocCommand {\n\n\tprivate static final Logger log = Logger.getLogger(RTBLAddCommand.class.getCanonicalName());\n\n\t@Inject\n\tprivate RTBLSubscribeModule subscribeModule;\n\t@Inject\n\tprivate RTBLFetchModule fetchModule;\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"Add real-time blocklist\";\n\t}\n\n\t@Override\n\tpublic String getNode() {\n\t\treturn \"rtbl-add\";\n\t}\n\t\n\t@Override\n\tprotected Form prepareForm(AdhHocRequest request, AdHocResponse response) throws AdHocCommandException {\n\t\tForm form = new Form(\"form\", \"Add real-time blocklist\", \"Fill out and submit this form to add a new real-time blocklist\");\n\t\tform.addField(Field.fieldJidSingle(\"pubsubJid\", \"\", \"Service address (JID)\"));\n\t\tform.addField(Field.fieldTextSingle(\"node\", \"\", \"Node\"));\n\t\tform.addField(Field.fieldTextSingle(\"hash\", \"SHA-256\", \"Hashing algorithm\"));\n\t\treturn form;\n\t}\n\n\t@Override\n\tprotected Form submitForm(AdhHocRequest request, AdHocResponse response, Form form) throws AdHocCommandException {\n\t\ttry {\n\t\t\tBareJID jid = BareJID.bareJIDInstance(assertNotEmpty(form.getAsString(\"pubsubJid\"), \"Service address is required!\"));\n\t\t\tString node = assertNotEmpty(form.getAsString(\"node\"), \"Node is required!\");\n\t\t\tString hash = assertNotEmpty(form.getAsString(\"hash\"), \"Hash algorithm is required!\");\n\t\t\tMessageDigest.getInstance(hash);\n\t\t\tRTBL rtbl = getRepository().getBlockList(jid, node);\n\t\t\tif (rtbl != null) {\n\t\t\t\tthrow new AdHocCommandException(Authorization.CONFLICT, \"This blocklist was already added.\");\n\t\t\t}\n\t\t\tgetRepository().add(jid, node, hash);\n\t\t\tsubscribeModule.subscribe(jid, node);\n\t\t\tfetchModule.fetch(jid, node);\n\t\t\treturn null;\n\t\t} catch (TigaseStringprepException e) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"could not parse JID\", e);\n\t\t\t}\n\t\t\tthrow new AdHocCommandException(Authorization.BAD_REQUEST, \"Invalid service address\");\n\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"could not find hash algorithm\", e);\n\t\t\t}\n\t\t\tthrow new AdHocCommandException(Authorization.BAD_REQUEST, \"Unsupported hashing algorithm\");\n\t\t} catch (TigaseDBException e) {\n\t\t\tif (log.isLoggable(Level.WARNING)) {\n\t\t\t\tlog.log(Level.WARNING, \"failed to update database\", e);\n\t\t\t}\n\t\t\tthrow new AdHocCommandException(Authorization.INTERNAL_SERVER_ERROR);\n\t\t}\n\t}\n\t\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/rtbl/adhoc/RTBLDeleteCommand.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.rtbl.adhoc;\n\nimport tigase.component.adhoc.AdHocCommandException;\nimport tigase.component.adhoc.AdHocResponse;\nimport tigase.component.adhoc.AdhHocRequest;\nimport tigase.db.TigaseDBException;\nimport tigase.form.Field;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.rtbl.RTBL;\nimport tigase.server.rtbl.RTBLComponent;\nimport tigase.server.rtbl.RTBLSubscribeModule;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Bean(name = \"rtbl-command-delete\", parent = RTBLComponent.class, active = true)\npublic class RTBLDeleteCommand extends AbstractAdHocCommand {\n\n\tprivate static final Logger log = Logger.getLogger(RTBLDeleteCommand.class.getCanonicalName());\n\n\t@Inject\n\tprivate RTBLSubscribeModule subscribeModule;\n\t\n\t@Override\n\tpublic String getName() {\n\t\treturn \"Delete real-time blocklist\";\n\t}\n\n\t@Override\n\tpublic String getNode() {\n\t\treturn \"rtbl-delete\";\n\t}\n\n\t@Override\n\tprotected Form prepareForm(AdhHocRequest request, AdHocResponse response) throws AdHocCommandException {\n\t\tForm form = new Form(\"form\", \"Delete real-time blocklist\", \"Select blocklist to delete\");\n\t\tList<RTBL> blocklists = getRepository().getBlockLists();\n\t\tblocklists.sort(Comparator.comparing(RTBL::getJID).thenComparing(RTBL::getNode));\n\t\tString[] ids = blocklists.stream()\n\t\t\t\t.map(rtbl -> rtbl.getJID().toString() + \"/\" + rtbl.getNode())\n\t\t\t\t.toArray(String[]::new);\n\t\tform.addField(Field.fieldListSingle(\"blocklist\", \"\", \"Blocklist\", ids, ids));\n\t\treturn form;\n\t}\n\n\t@Override\n\tprotected Form submitForm(AdhHocRequest request, AdHocResponse response, Form form) throws AdHocCommandException {\n\t\tString blocklistId = assertNotEmpty(form.getAsString(\"blocklist\"), \"You need to select blocklist!\");\n\t\tJID tmp = JID.jidInstanceNS(blocklistId);\n\t\ttry {\n\t\t\tgetRepository().remove(tmp.getBareJID(), tmp.getResource());\n\t\t\tsubscribeModule.unsubscribe(tmp.getBareJID(), tmp.getResource());\n\t\t} catch (TigaseDBException e) {\n\t\t\tif (log.isLoggable(Level.WARNING)) {\n\t\t\t\tlog.log(Level.WARNING, \"failed to update database\", e);\n\t\t\t}\n\t\t\tthrow new AdHocCommandException(Authorization.INTERNAL_SERVER_ERROR);\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/script/AbstractScriptCommand.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.script;\n\nimport tigase.stats.StatisticHolderImpl;\n\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\n/**\n * Created: Jan 2, 2009 2:32:17 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic abstract class AbstractScriptCommand\n\t\textends StatisticHolderImpl\n\t\timplements CommandIfc {\n\n\tpublic static final Map<String, String> lineCommentStart = new LinkedHashMap<String, String>(20);\n\n\tstatic {\n\t\tlineCommentStart.put(\"groovy\", \"//\");\n\t\tlineCommentStart.put(\"scala\", \"//\");\n\t\tlineCommentStart.put(\"python\", \"#\");\n\t\tlineCommentStart.put(\"py\", \"#\");\n\t\tlineCommentStart.put(\"js\", \"//\");\n\t\tlineCommentStart.put(\"ruby\", \"#\");\n\t\tlineCommentStart.put(\"rb\", \"#\");\n\t\tlineCommentStart.put(\"perl\", \"#\");\n\t\tlineCommentStart.put(\"pl\", \"#\");\n\t\tlineCommentStart.put(\"awk\", \"//\");\n\t\tlineCommentStart.put(\"lisp\", \";\");\n\t\tlineCommentStart.put(\"el\", \";\");\n\t\tlineCommentStart.put(\"cl\", \";\");\n\t\tlineCommentStart.put(\"gc1\", \";\");\n\t\tlineCommentStart.put(\"gc3\", \";\");\n\t\tlineCommentStart.put(\"java\", \"//\");\n\t}\n\n\tprivate boolean adminOnly = true;\n\tprivate String commandId = null;\n\tprivate String description = null;\n\tprivate String group = null;\n\n\t@Override\n\tpublic String getCommandId() {\n\t\treturn this.commandId;\n\t}\n\n\t@Override\n\tpublic String getDescription() {\n\t\treturn this.description;\n\t}\n\n\t@Override\n\tpublic String getGroup() {\n\t\treturn this.group;\n\t}\n\n\t@Override\n\tpublic void init(String id, String description, String group) {\n\t\tthis.commandId = id;\n\t\tthis.description = description;\n\t\tthis.group = group;\n\n\t\tsetStatisticsPrefix(\"adhoc-command/\" + id);\n\t}\n\n\t@Override\n\tpublic boolean isAdminOnly() {\n\t\treturn adminOnly;\n\t}\n\n\t@Override\n\tpublic void setAdminOnly(boolean adminOnly) {\n\t\tthis.adminOnly = adminOnly;\n\t}\n\n\tprotected boolean isEmpty(String val) {\n\t\treturn (val == null) || val.isEmpty();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/script/AddScriptCommand.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.script;\n\nimport tigase.server.*;\n\nimport javax.script.Bindings;\nimport javax.script.ScriptEngineFactory;\nimport javax.script.ScriptEngineManager;\nimport javax.script.ScriptException;\nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Jan 2, 2009 2:29:48 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class AddScriptCommand\n\t\textends AbstractScriptCommand {\n\n\tprivate static final Logger log = Logger.getLogger(AddScriptCommand.class.getName());\n\n\t@SuppressWarnings({\"unchecked\"})\n\tpublic Script addAdminScript(String cmdId, String cmdDescr, String cmdGroup, String script, String lang, String ext,\n\t\t\t\t\t\t\t\t Bindings binds) throws ScriptException {\n\t\tScript as = new Script();\n\n\t\tas.init(cmdId, cmdDescr, cmdGroup, script, lang, ext, binds);\n\n\t\tMap<String, CommandIfc> adminCommands = (Map<String, CommandIfc>) binds.get(ADMN_CMDS);\n\n\t\tadminCommands.put(as.getCommandId(), as);\n\n\t\tMap<String, Set<CmdAcl>> commandsACL = (Map<String, Set<CmdAcl>>) binds.get(COMMANDS_ACL);\n\t\tSet<CmdAcl> acl = commandsACL.get(as.getCommandId());\n\n\t\tif (acl != null) {\n\t\t\tfor (CmdAcl cmdAcl : acl) {\n\t\t\t\tif (cmdAcl.getType() != CmdAcl.Type.ADMIN) {\n\t\t\t\t\tas.setAdminOnly(false);\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn as;\n\t}\n\n\t@Override\n\tpublic Bindings getBindings() {\n\t\treturn null;\n\t}\n\n\t@Override\n\t@SuppressWarnings({\"unchecked\"})\n\tpublic void runCommand(Iq packet, Bindings binds, Queue<Packet> results) {\n\t\tString language = Command.getFieldValue(packet, LANGUAGE);\n\t\tString commandId = Command.getFieldValue(packet, COMMAND_ID);\n\t\tString description = Command.getFieldValue(packet, DESCRIPT);\n\t\tString group = Command.getFieldValue(packet, GROUP);\n\t\tString[] scriptText = Command.getFieldValues(packet, SCRIPT_TEXT);\n\t\tboolean saveToDisk = Command.getCheckBoxFieldValue(packet, SAVE_TO_DISK);\n\n\t\tif (isEmpty(language) || isEmpty(commandId) || isEmpty(description) || (scriptText == null)) {\n\t\t\tresults.offer(prepareScriptCommand(packet, binds));\n\t\t} else {\n\t\t\tStringBuilder sb = new StringBuilder(1024);\n\n\t\t\tfor (String string : scriptText) {\n\t\t\t\tif (string != null) {\n\t\t\t\t\tsb.append(string).append(\"\\n\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tString originalGroup = group;\n\t\t\t\tif (group != null && group.contains(\"${componentName}\")) {\n\t\t\t\t\tBasicComponent component = (BasicComponent) binds.get(\"component\");\n\t\t\t\t\tif (component != null) {\n\t\t\t\t\t\tgroup = group.replace(\"${componentName}\", component.getDiscoDescription());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tScript s = addAdminScript(commandId, description, group, sb.toString(), language, null, binds);\n\t\t\t\tPacket result = packet.commandResult(Command.DataType.result);\n\n\t\t\t\tCommand.addTextField(result, \"Note\", \"Script loaded successfuly.\");\n\t\t\t\tresults.offer(result);\n\n\t\t\t\tif (saveToDisk) {\n\t\t\t\t\tsaveCommandToDisk(commandId, description, originalGroup, sb, s.getFileExtension(), binds);\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.WARNING, \"Can''t initialize script: \", e);\n\n\t\t\t\tPacket result = packet.commandResult(Command.DataType.result);\n\n\t\t\t\tCommand.addTextField(result, \"Note\", \"Script initialization error.\");\n\n\t\t\t\tStackTraceElement[] ste = e.getStackTrace();\n\t\t\t\tString[] error = new String[ste.length + 2];\n\n\t\t\t\terror[0] = e.getMessage();\n\t\t\t\terror[1] = e.toString();\n\n\t\t\t\tfor (int i = 0; i < ste.length; i++) {\n\t\t\t\t\terror[i + 2] = ste[i].toString();\n\t\t\t\t}\n\n\t\t\t\tCommand.addTextField(result, \"Error message\", e.getMessage());\n\t\t\t\tCommand.addFieldMultiValue(result, \"Debug info\", Arrays.asList(error));\n\t\t\t\tresults.offer(result);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate Packet prepareScriptCommand(Iq packet, Bindings binds) {\n\t\tPacket result = packet.commandResult(Command.DataType.form);\n\n\t\tCommand.addFieldValue(result, DESCRIPT, \"Short description\");\n\t\tCommand.addFieldValue(result, COMMAND_ID, \"new-command\");\n\t\tCommand.addFieldValue(result, GROUP, \"group\");\n\n\t\tScriptEngineManager scriptEngineManager = (ScriptEngineManager) binds.get(SCRI_MANA);\n\t\tList<ScriptEngineFactory> scriptFactories = scriptEngineManager.getEngineFactories();\n\n\t\tif (scriptFactories != null) {\n\t\t\tString[] langs = new String[scriptFactories.size()];\n\t\t\tint idx = 0;\n\t\t\tString def = null;\n\n\t\t\tfor (ScriptEngineFactory scriptEngineFactory : scriptFactories) {\n\t\t\t\tlangs[idx++] = scriptEngineFactory.getLanguageName();\n\n\t\t\t\tif (scriptEngineFactory.getLanguageName().equals(\"groovy\")) {\n\t\t\t\t\tdef = \"groovy\";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (def == null) {\n\t\t\t\tdef = langs[0];\n\t\t\t}\n\n\t\t\tCommand.addFieldValue(result, LANGUAGE, def, LANGUAGE, langs, langs);\n\t\t}\n\n\t\tCommand.addFieldMultiValue(result, SCRIPT_TEXT, Collections.nCopies(1, \"\"));\n\t\tCommand.addCheckBoxField(result, SAVE_TO_DISK, true);\n\n\t\treturn result;\n\t}\n\n\tprivate void saveCommandToDisk(String commandId, String description, String group, StringBuilder sb,\n\t\t\t\t\t\t\t\t   String fileExtension, Bindings binds) throws IOException {\n\t\tFile fileName = new File((String) binds.get(SCRIPT_COMP_DIR), commandId + \".\" + fileExtension);\n\n\t\tFile parentDirectory = fileName.getParentFile();\n\n\t\tif ((parentDirectory != null) && !parentDirectory.exists()) {\n\t\t\tlog.log(Level.CONFIG, \"Admin scripts directory is missing: {0}, creating...\", parentDirectory);\n\t\t\ttry {\n\t\t\t\tparentDirectory.mkdirs();\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.WARNING, \"Can''t create scripts directory , read-only filesystem: \" + parentDirectory, e);\n\t\t\t}\n\t\t}\n\n\t\tlog.log(Level.CONFIG, \"Saving command: {0} to disk file: {1}\", new Object[]{commandId, fileName.toString()});\n\n\t\tFileWriter fw = new FileWriter(fileName, false);\n\t\tString comment = lineCommentStart.get(fileExtension);\n\n\t\tif (comment == null) {\n\t\t\tcomment = \"//\";\n\t\t}\n\n\t\tfw.write(comment + \" \" + SCRIPT_DESCRIPTION + \" \" + description + '\\n');\n\t\tfw.write(comment + \" \" + SCRIPT_ID + \" \" + commandId + '\\n');\n\t\tfw.write(comment + \" \" + SCRIPT_COMPONENT + \" \" + binds.get(COMPONENT_NAME) + '\\n');\n\t\tif (group != null) {\n\t\t\tfw.write(comment + \" \" + SCRIPT_GROUP + \" \" + group + '\\n');\n\t\t}\n\t\tfw.write(sb.toString());\n\t\tfw.close();\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/script/CommandIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.script;\n\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.stats.StatisticHolder;\n\nimport javax.script.Bindings;\nimport java.util.Queue;\n\n/**\n * Created: Jan 2, 2009 1:20:16 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface CommandIfc\n\t\textends StatisticHolder {\n\n\tpublic static final String VHOST_MANAGER = \"vhostMan\";\n\n\tpublic static final String ADMINS_SET = \"adminsSet\";\n\n\tpublic static final String COMMANDS_ACL = \"cmdsAcl\";\n\n\tpublic static final String ADMN_CMDS = \"adminCommands\";\n\n\tpublic static final String USER_REPO = \"userRepository\";\n\n\tpublic static final String AUTH_REPO = \"authRepository\";\n\n\tpublic static final String USER_SESS = \"userSessions\";\n\n\tpublic static final String USER_CONN = \"userConnections\";\n\n\tpublic static final String ADMN_DISC = \"adminDisco\";\n\n\tpublic static final String SCRI_MANA = \"scriptManager\";\n\n\tpublic static final String SCRIPT_BASE_DIR = \"scriptBaseDir\";\n\n\tpublic static final String SCRIPT_COMP_DIR = \"scriptCompDir\";\n\n\tpublic static final String COMPONENT_NAME = \"componentName\";\n\n\tpublic static final String COMPONENT = \"component\";\n\n\tpublic static final String CONNECTED_NODES = \"connectedNodes\";\n\n\tpublic static final String CONNECTED_NODES_WITH_LOCAL = \"connectedNodesWithLocal\";\n\n\tpublic static final String EVENTBUS = \"eventBus\";\n\n\tpublic static final String SERVICES_MAP = \"servicesMap\";\n\n\tpublic static final String SCRIPT_DESCRIPTION = \"AS:Description:\";\n\n\tpublic static final String SCRIPT_ID = \"AS:CommandId:\";\n\n\tpublic static final String SCRIPT_COMPONENT = \"AS:Component:\";\n\n\tpublic static final String SCRIPT_CLASS = \"AS:ComponentClass:\";\n\n\tpublic static final String SCRIPT_GROUP = \"AS:Group:\";\n\n\tpublic static final String LANGUAGE = \"Language\";\n\n\tpublic static final String COMMAND_ID = \"Command Id\";\n\n\tpublic static final String SCRIPT_TEXT = \"Script text\";\n\n\tpublic static final String SCRIPT_RESULT = \"Script result\";\n\n\tpublic static final String DESCRIPT = \"Description\";\n\n\tpublic static final String GROUP = \"Group\";\n\n\tpublic static final String SAVE_TO_DISK = \"Save to disk\";\n\n\tpublic static final String REMOVE_FROM_DISK = \"Remove from disk\";\n\n\tpublic static final String PACKET = \"packet\";\n\n\tpublic static final String ADD_SCRIPT_CMD = \"add-script\";\n\n\tpublic static final String DEL_SCRIPT_CMD = \"del-script\";\n\n\tBindings getBindings();\n\n\tString getCommandId();\n\n\tString getDescription();\n\n\tString getGroup();\n\n\tvoid init(String id, String description, String group);\n\n\tboolean isAdminOnly();\n\n\tvoid setAdminOnly(boolean adminOnly);\n\n\tvoid runCommand(Iq packet, Bindings binds, Queue<Packet> results);\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/script/RemoveScriptCommand.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.script;\n\nimport tigase.disco.ServiceEntity;\nimport tigase.server.Command;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\n\nimport javax.script.Bindings;\nimport java.io.File;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.Set;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Jan 2, 2009 2:30:41 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class RemoveScriptCommand\n\t\textends AbstractScriptCommand {\n\n\tprivate static final Logger log = Logger.getLogger(RemoveScriptCommand.class.getName());\n\n\t@Override\n\t@SuppressWarnings({\"unchecked\"})\n\tpublic void runCommand(Iq packet, Bindings binds, Queue<Packet> results) {\n\t\tString commandId = Command.getFieldValue(packet, COMMAND_ID);\n\t\tif (isEmpty(commandId)) {\n\t\t\tresults.offer(prepareScriptCommand(packet, binds));\n\t\t} else {\n\t\t\tboolean removeFromDisk = Command.getCheckBoxFieldValue(packet, REMOVE_FROM_DISK);\n\n\t\t\tMap<String, CommandIfc> adminCommands = (Map<String, CommandIfc>) binds.get(ADMN_CMDS);\n\t\t\tScript command = (Script) adminCommands.remove(commandId);\n\t\t\tServiceEntity serviceEntity = (ServiceEntity) binds.get(ADMN_DISC);\n\t\t\tServiceEntity item = serviceEntity.findNode(\"http://jabber.org/protocol/admin#\" + commandId);\n\t\t\tserviceEntity.removeItems(item);\n\n\t\t\tif (removeFromDisk) {\n\t\t\t\tdeleteCommandFromDisk(command.getCommandId(), command.getFileExtension(), binds);\n\t\t\t}\n\n\t\t\tPacket result = packet.commandResult(Command.DataType.result);\n\t\t\tCommand.addTextField(result, \"Note\", \"Script removed correctly\");\n\t\t\tresults.offer(result);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Bindings getBindings() {\n\t\treturn null;\n\t}\n\n\t@SuppressWarnings({\"unchecked\"})\n\tprivate Packet prepareScriptCommand(Iq packet, Bindings binds) {\n\t\tPacket result = null;\n\t\tMap<String, CommandIfc> adminCommands = (Map<String, CommandIfc>) binds.get(ADMN_CMDS);\n\t\tif (adminCommands.size() > 2) {\n\t\t\tresult = packet.commandResult(Command.DataType.form);\n\t\t\tSet<String> ids = new LinkedHashSet<String>(adminCommands.keySet());\n\t\t\tids.remove(ADD_SCRIPT_CMD);\n\t\t\tids.remove(DEL_SCRIPT_CMD);\n\t\t\tString[] commandIds = ids.toArray(new String[ids.size()]);\n\t\t\tCommand.addFieldValue(result, COMMAND_ID, commandIds[0], \"Command Id\", commandIds, commandIds);\n\t\t\tCommand.addCheckBoxField(result, REMOVE_FROM_DISK, true);\n\t\t} else {\n\t\t\tresult = packet.commandResult(Command.DataType.result);\n\t\t\tCommand.addTextField(result, \"Note\", \"There is no command script to remove\");\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate void deleteCommandFromDisk(String commandId, String fileExtension, Bindings binds) {\n\t\tFile fileName = new File((String) binds.get(SCRIPT_COMP_DIR), commandId + \".\" + fileExtension);\n\n\t\tif (fileName.exists()) {\n\t\t\tlog.log(Level.CONFIG, \"Deleting file: {0}\", fileName);\n\t\t\tfileName.delete();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/script/Script.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.script;\n\nimport tigase.server.Command;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.util.log.LogFormatter;\n\nimport javax.script.*;\nimport java.io.StringWriter;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Jan 2, 2009 1:21:55 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class Script\n\t\textends AbstractScriptCommand {\n\n\tprivate static final Logger log = Logger.getLogger(Script.class.getName());\n\n\tprivate CompiledScript compiledScript = null;\n\tprivate String ext = null;\n\tprivate String language = null;\n\tprivate String script = null;\n\tprivate ScriptEngine scriptEngine = null;\n\n\t@Override\n\tpublic Bindings getBindings() {\n\t\treturn scriptEngine.createBindings();\n\t}\n\n\tpublic String getFileExtension() {\n\t\treturn ext;\n\t}\n\n\tpublic String getLanguageName() {\n\t\treturn language;\n\t}\n\n\tpublic void init(String id, String description, String group, String script, String lang, String ext,\n\t\t\t\t\t Bindings binds) throws ScriptException {\n\t\tsuper.init(id, description, group);\n\t\tthis.script = script;\n\t\tthis.language = lang;\n\t\tthis.ext = ext;\n\n\t\tScriptEngineManager scriptEngineManager = (ScriptEngineManager) binds.get(SCRI_MANA);\n\n\t\tlog.log(Level.FINEST, \"Trying to load admin command: {0}, description: {1}, language: {2}, ext: {3}\",\n\t\t\t\tnew Object[]{id, description, this.language, this.ext});\n\n\t\tif (language != null) {\n\t\t\tscriptEngine = scriptEngineManager.getEngineByName(language);\n\t\t}\n\n\t\tif (ext != null) {\n\t\t\tscriptEngine = scriptEngineManager.getEngineByExtension(ext);\n\t\t}\n\n\t\tif (scriptEngine instanceof Compilable) {\n\t\t\tcompiledScript = ((Compilable) scriptEngine).compile(script);\n\t\t}\n\n\t\tif (this.language == null && scriptEngine != null) {\n\t\t\tthis.language = scriptEngine.getFactory().getLanguageName();\n\t\t}\n\n\t\tif (this.ext == null && scriptEngine != null) {\n\t\t\tthis.ext = scriptEngine.getFactory().getExtensions().get(0);\n\t\t}\n\t\tString THREADING = scriptEngine != null ? String.valueOf(scriptEngine.getFactory().getParameter(\"THREADING\")) : null;\n\n\t\tlog.log(Level.FINE, \"Initialized script command, id: {0}, lang: {1}, ext: {2}, threading: {3}\",\n\t\t\t\tnew Object[]{id, this.language, this.ext, THREADING});\n\n\t}\n\n\t@Override\n\t@SuppressWarnings({\"unchecked\"})\n\tpublic void runCommand(Iq packet, Bindings binds, Queue<Packet> results) {\n\t\tScriptContext context = null;\n\t\tStringWriter writer = null;\n\n\t\ttry {\n\t\t\tlong start = System.currentTimeMillis();\n\t\t\t// Bindings localBinds = scriptEngine.createBindings();\n\t\t\tbinds.put(PACKET, packet);\n\n\t\t\t// Workaround for Python which doesn't return values and can overwrite\n\t\t\t// values only if type is correct\n\t\t\tObject res = \"\";\n\n\t\t\tbinds.put(\"result\", res);\n\t\t\tcontext = new SimpleScriptContext(); //scriptEngine.getContext(); returns global context!\n\t\t\tcontext.setBindings(binds, ScriptContext.ENGINE_SCOPE);\n\t\t\twriter = new StringWriter();\n\t\t\tcontext.setErrorWriter(writer);\n\n\t\t\tif (compiledScript != null) {\n\t\t\t\tres = compiledScript.eval(context);\n\t\t\t} else {\n\t\t\t\tres = scriptEngine.eval(script, context);\n\t\t\t}\n\n\t\t\tif (res == null) {\n\n\t\t\t\t// Yes, Python doesn't return results normally\n\t\t\t\t// (or I don't know how to do it)\n\t\t\t\t// Python can either return a Packet as 'packet' or string as 'result'\n\t\t\t\tres = binds.get(\"result\");\n\n\t\t\t\tif (res.toString().isEmpty()) {\n\t\t\t\t\tres = binds.get(PACKET);\n\n\t\t\t\t\tif (res == packet) {\n\n\t\t\t\t\t\t// Ups, apparently the script returned no results, to avoid infinite loop\n\t\t\t\t\t\t// we have to handle this somehow...\n\t\t\t\t\t\tres = \"Script finished with no errors but returned no results.\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (res instanceof Packet) {\n\t\t\t\tresults.offer((Packet) res);\n\t\t\t} else {\n\t\t\t\tif (res instanceof Queue) {\n\t\t\t\t\tresults.addAll((Queue<Packet>) res);\n\t\t\t\t} else {\n\t\t\t\t\tPacket result = packet.commandResult(Command.DataType.result);\n\n\t\t\t\t\t// Command.addTextField(result, \"Note\", \"Script execution result\");\n\t\t\t\t\tString[] text = null;\n\n\t\t\t\t\tif (res != null) {\n\t\t\t\t\t\ttext = res.toString().split(\"\\n\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext = new String[]{\"Script returned no results.\"};\n\t\t\t\t\t}\n\n\t\t\t\t\tCommand.addFieldMultiValue(result, SCRIPT_RESULT, Arrays.asList(text));\n\t\t\t\t\tresults.offer(result);\n\t\t\t\t}\n\t\t\t}\n\t\t\tlong end = System.currentTimeMillis();\n\t\t\tstatisticExecutedIn(end - start);\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Script execution error while processing packet: \" + packet, e);\n\t\t\tPacket result = packet.commandResult(Command.DataType.result);\n\n\t\t\tCommand.addTextField(result, \"Note\", \"Script execution error.\");\n\n\t\t\tif (e.getMessage() != null) {\n\t\t\t\tCommand.addTextField(result, \"Error message\", e.getMessage());\n\t\t\t}\n\n\t\t\tfinal String stackTrace = LogFormatter.fillThrowable(e);\n\t\t\tCommand.addFieldMultiValue(result, \"Debug info\", Collections.singletonList(stackTrace));\n\t\t\tresults.offer(result);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tfinal StringBuffer sb = new StringBuffer(\"Script{\");\n\t\tsb.append(\"'\")\n\t\t\t\t.append(getGroup())\n\t\t\t\t.append(':')\n\t\t\t\t.append(getCommandId())\n\t\t\t\t.append(':')\n\t\t\t\t.append(language)\n\t\t\t\t.append(':')\n\t\t\t\t.append(getDescription())\n\t\t\t\t.append('\\'');\n\t\tsb.append(\", adminOnly=\").append(isAdminOnly());\n\t\tsb.append(\", stats='\").append(getName()).append(':').append(getValue());\n\t\tsb.append('}');\n\t\treturn sb.toString();\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/test/EchoComponent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.test;\n\nimport tigase.server.AbstractMessageReceiver;\nimport tigase.server.Packet;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Sep 30, 2010 1:07:13 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class EchoComponent\n\t\textends AbstractMessageReceiver {\n\n\tprivate static final Logger log = Logger.getLogger(EchoComponent.class.getName());\n\n\t@Override\n\tpublic void processPacket(Packet packet) {\n\t\tlog.log(Level.FINEST, \"Received: {0}\", packet);\n\n\t\tPacket result = packet.swapStanzaFromTo();\n\n\t\taddOutPacket(result);\n\t\tlog.log(Level.FINEST, \"Sent back: {0}\", result);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/test/TestComponent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.test;\n\nimport tigase.component.AbstractKernelBasedComponent;\nimport tigase.component.modules.impl.DiscoveryModule;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Packet;\nimport tigase.stats.StatisticsList;\n\nimport javax.script.Bindings;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * A test component used to demonstrate API and for running different kinds of tests on the Tigase server - generate\n * local traffic for performance and load tests. Created: Nov 28, 2009 9:22:36 PM\n */\n@Bean(name = \"test\", parent = Kernel.class, active = false)\npublic class TestComponent\n\t\textends AbstractKernelBasedComponent {\n\n\tprivate static final Logger log = Logger.getLogger(TestComponent.class.getName());\n\n\t@Inject(nullAllowed = true)\n\tprivate TestSpamModule spamTestModule;\n\n\t@Override\n\tpublic String getDiscoCategoryType() {\n\t\treturn \"spam\";\n\t}\n\n\t@Override\n\tpublic String getDiscoDescription() {\n\t\treturn \"Spam filtering\";\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t\tsuper.getStatistics(list);\n\t\tif (null != spamTestModule) {\n\t\t\tlist.add(getName(), \"Spam messages found\", spamTestModule.getTotalSpamCounter(), Level.INFO);\n\t\t\tlist.add(getName(), \"All messages processed\", spamTestModule.getMessagesCounter(), Level.FINE);\n\t\t}\n\t\tif (list.checkLevel(Level.FINEST)) {\n\t\t\t// Some very expensive statistics generation code...\n\t\t}\n\t}\n\n\t@Override\n\tpublic int hashCodeForPacket(Packet packet) {\n\t\tif (packet.getStanzaTo() != null) {\n\t\t\treturn packet.getStanzaTo().hashCode();\n\t\t}\n\t\t// This should not happen, every packet must have a destination\n\t\t// address, but maybe our SPAM checker is used for checking\n\t\t// strange kind of packets too....\n\t\tif (packet.getStanzaFrom() != null) {\n\t\t\treturn packet.getStanzaFrom().hashCode();\n\t\t}\n\t\t// If this really happens on your system you should look carefully\n\t\t// at packets arriving to your component and decide a better way\n\t\t// to calculate hashCode\n\t\treturn 1;\n\t}\n\n\t@Override\n\tpublic void initBindings(Bindings binds) {\n\t\tsuper.initBindings(binds);\n\t\tif (null != spamTestModule) {\n\t\t\tspamTestModule.initBindings(binds);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isDiscoNonAdmin() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int processingInThreads() {\n\t\treturn Runtime.getRuntime().availableProcessors();\n\t}\n\n\t@Override\n\tpublic int processingOutThreads() {\n\t\treturn Runtime.getRuntime().availableProcessors();\n\t}\n\n\t@Override\n\tprotected void registerModules(Kernel kernel) {\n\t\tkernel.registerBean(\"disco\").asClass(DiscoveryModule.class).exec();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/test/TestGeneratorModule.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.test;\n\nimport tigase.component.exceptions.ComponentException;\nimport tigase.component.modules.AbstractModule;\nimport tigase.criteria.Criteria;\nimport tigase.criteria.ElementCriteria;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Message;\nimport tigase.server.Packet;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.logging.Logger;\n\n@Bean(name = \"test-generator-module\", parent = TestComponent.class, active = true)\npublic class TestGeneratorModule\n\t\textends AbstractModule {\n\n\tprivate static final Logger log = Logger.getLogger(TestGeneratorModule.class.getCanonicalName());\n\n\tprivate Criteria CRITERIA = ElementCriteria.name(\"message\");\n\n\t@Inject\n\tprivate TestComponent component;\n\n\t@Override\n\tpublic Criteria getModuleCriteria() {\n\t\treturn CRITERIA;\n\t}\n\n\t@Override\n\tpublic void process(Packet packet) throws ComponentException, TigaseStringprepException {\n\t\tif (isPostCommand(packet)) {\n\t\t\trunCommand(packet);\n\t\t} else {\n\t\t\tString body = packet.getElemCDataStaticStr(Message.MESSAGE_BODY_PATH);\n\n\t\t\twrite(Message.getMessage(packet.getStanzaTo(), packet.getStanzaFrom(), StanzaType.normal,\n\t\t\t\t\t\t\t\t\t \"This is response to your message: [\" + body + \"]\", \"Response\", null,\n\t\t\t\t\t\t\t\t\t packet.getStanzaId()));\n\t\t}\n\t}\n\n\tprivate boolean isPostCommand(Packet packet) {\n\t\tString body = packet.getElemCDataStaticStr(Message.MESSAGE_BODY_PATH);\n\n\t\tif (body != null) {\n\t\t\tfor (command comm : command.values()) {\n\t\t\t\tif (body.startsWith(\"//\" + comm.toString())) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate void runCommand(Packet packet) {\n\t\tString body = packet.getElemCDataStaticStr(Message.MESSAGE_BODY_PATH);\n\t\tString[] body_split = body.split(\" |\\n|\\r\");\n\t\tcommand comm = command.valueOf(body_split[0].substring(2));\n\n\t\tswitch (comm) {\n\t\t\tcase genn:\n\t\t\t\ttry {\n\t\t\t\t\tint number = Integer.parseInt(body_split[1]);\n\t\t\t\t\tString domain = packet.getStanzaFrom().getDomain();\n\n\t\t\t\t\tfor (int i = 0; i < number; i++) {\n\t\t\t\t\t\twrite(Message.getMessage(packet.getStanzaTo(), JID.jidInstance(\"nonename_\" + i + \"@\" + domain),\n\t\t\t\t\t\t\t\t\t\t\t\t StanzaType.normal, \"Traffic generattion: \" + number,\n\t\t\t\t\t\t\t\t\t\t\t\t \"Internal load test\", null, packet.getStanzaId()));\n\t\t\t\t\t}\n\t\t\t\t\twrite(Message.getMessage(packet.getStanzaTo(), packet.getStanzaFrom(), StanzaType.normal,\n\t\t\t\t\t\t\t\t\t\t\t \"Completed \" + number, \"Response\", null, packet.getStanzaId()));\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\twrite(Message.getMessage(packet.getStanzaTo(), packet.getStanzaFrom(), StanzaType.normal,\n\t\t\t\t\t\t\t\t\t\t\t \"Incorrect command parameter: \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t ((body_split.length > 1) ? body_split[1] : null) +\n\t\t\t\t\t\t\t\t\t\t\t\t\t \", expecting Integer.\", \"Response\", null, packet.getStanzaId()));\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate enum command {\n\t\tgenn\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/test/TestSpamModule.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.test;\n\nimport tigase.component.exceptions.ComponentException;\nimport tigase.component.modules.AbstractModule;\nimport tigase.criteria.Criteria;\nimport tigase.criteria.ElementCriteria;\nimport tigase.criteria.Or;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Message;\nimport tigase.server.Packet;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.JID;\n\nimport javax.script.Bindings;\nimport java.util.Arrays;\nimport java.util.logging.Logger;\n\n@Bean(name = \"test-spam-module\", parent = TestComponent.class, active = true)\npublic class TestSpamModule\n\t\textends AbstractModule {\n\n\tprivate static final Logger log = Logger.getLogger(TestSpamModule.class.getCanonicalName());\n\tprivate static final String BAD_WORDS_KEY = \"bad-words\";\n\tprivate static final String WHITELIST_KEY = \"white-list\";\n\t/**\n\t * This might be changed in one threads while it is iterated in processPacket(...) in another thread. We expect that\n\t * changes are very rare and small, most of operations are just iterations.\n\t */\n\t@ConfigField(desc = \"Bad words\", alias = \"bad-words\")\n\tprotected String[] badWords = {\"word1\", \"word2\", \"word3\"};\n\t@ConfigField(desc = \"White listed addresses\", alias = \"white-list\")\n\tprotected String[] whiteList = {\"admin@localhost\"};\n\tprivate Criteria CRITERIA = ElementCriteria.name(\"message\");\n\t@ConfigField(desc = \"Abuse notification address\", alias = \"abuse-address\")\n\tprivate JID abuseAddress = JID.jidInstanceNS(\"abuse@locahost\");\n\t@Inject\n\tprivate TestComponent component;\n\tprivate int delayCounter = 0;\n\tprivate long messagesCounter = 0;\n\t@ConfigField(desc = \"Frequency of notification\", alias = \"notification-frequency\")\n\tprivate int notificationFrequency = 10;\n\t@ConfigField(desc = \"Logged packet types\", alias = \"packet-types\")\n\tprivate String[] packetTypes = {\"message\", \"presence\", \"iq\"};\n\t@ConfigField(desc = \"Prefix\", alias = \"log-prepend\")\n\tprivate String prependText = \"Spam detected: \";\n\t@ConfigField(desc = \"Secure logging\", alias = \"secure-logging\")\n\tprivate boolean secureLogging = false;\n\tprivate long spamCounter = 0;\n\tprivate long totalSpamCounter = 0;\n\n\tpublic void everyMinute() {\n\t\tif ((++delayCounter) >= notificationFrequency) {\n\t\t\twrite(Message.getMessage(abuseAddress, component.getComponentId(), StanzaType.chat,\n\t\t\t\t\t\t\t\t\t \"Detected spam messages: \" + spamCounter, \"Spam counter\", null,\n\t\t\t\t\t\t\t\t\t component.newPacketId(\"spam-\")));\n\t\t\tdelayCounter = 0;\n\t\t\tspamCounter = 0;\n\t\t}\n\t}\n\n\t@Override\n\tpublic Criteria getModuleCriteria() {\n\t\treturn CRITERIA;\n\t}\n\n\t@Override\n\tpublic void process(Packet packet) throws ComponentException, TigaseStringprepException {\n\t\t// Is this packet a message?\n\t\tif (\"message\" == packet.getElemName()) {\n\t\t\tmessagesCounter++;\n\t\t\tString from = packet.getStanzaFrom().toString();\n\t\t\t// Is sender on the whitelist?\n\t\t\tif (Arrays.binarySearch(whiteList, from) < 0) {\n\t\t\t\t// The sender is not on whitelist so let's check the content\n\t\t\t\tString body = packet.getElemCDataStaticStr(Message.MESSAGE_BODY_PATH);\n\t\t\t\tif (body != null && !body.isEmpty()) {\n\t\t\t\t\tbody = body.toLowerCase();\n\t\t\t\t\tfor (String word : badWords) {\n\t\t\t\t\t\tif (body.contains(word)) {\n\t\t\t\t\t\t\tlog.finest(prependText + packet.toString(secureLogging));\n\t\t\t\t\t\t\t++spamCounter;\n\t\t\t\t\t\t\tcomponent.updateServiceDiscoveryItem(component.getName(), \"spam\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Spam caught: [\" + (++totalSpamCounter) + \"]\", true);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Not a SPAM, return it for further processing\n\t\tPacket result = packet.swapFromTo();\n\t\twrite(result);\n\t}\n\n\tpublic long getMessagesCounter() {\n\t\treturn messagesCounter;\n\t}\n\n\tpublic long getTotalSpamCounter() {\n\t\treturn totalSpamCounter;\n\t}\n\n\tpublic void setPacketTypes(String[] packetTypes) {\n\t\tthis.packetTypes = packetTypes;\n\t\tCriteria crit = new Or();\n\t\tfor (String packetType : packetTypes) {\n\t\t\tcrit.add(ElementCriteria.name(packetType));\n\t\t}\n\t\tCRITERIA = crit;\n\t}\n\n\tpublic void initBindings(Bindings binds) {\n\t\tbinds.put(BAD_WORDS_KEY, badWords);\n\t\tbinds.put(WHITELIST_KEY, whiteList);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/websocket/WebSocketClientConnectionManager.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.websocket;\n\nimport org.jspecify.annotations.Nullable;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.selector.ClusterModeRequired;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\nimport tigase.net.SocketType;\nimport tigase.server.xmppclient.XMPPIOProcessor;\nimport tigase.xml.Element;\nimport tigase.xmpp.StreamError;\nimport tigase.xmpp.XMPPIOService;\nimport tigase.xmpp.impl.StartTLS;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.List;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\n/**\n * Class implements basic support allowing clients to connect using WebSocket protocol\n *\n * @author andrzej\n */\n@Bean(name = \"ws2s\", parent = Kernel.class, active = true)\n@ConfigType({ConfigTypeEnum.DefaultMode, ConfigTypeEnum.ConnectionManagersMode})\n@ClusterModeRequired(active = false)\npublic class WebSocketClientConnectionManager\n\t\textends tigase.server.xmppclient.ClientConnectionManager {\n\n\tprivate static final Logger log = Logger.getLogger(WebSocketClientConnectionManager.class.getName());\n\tprivate static final String XMLNS_FRAMING = \"urn:ietf:params:xml:ns:xmpp-framing\";\n\n\tpublic WebSocketClientConnectionManager() {\n\t\tsuper();\n\t\twatchdogPingType = WATCHDOG_PING_TYPE.XMPP;\n\t}\n\n\t@Override\n\tpublic void setWatchdogPingType(WATCHDOG_PING_TYPE watchdogPingType) {\n\t\tsuper.setWatchdogPingType(watchdogPingType);\n\t\tif (watchdogPingType.equals(WATCHDOG_PING_TYPE.WHITESPACE)) {\n\t\t\tlog.log(Level.SEVERE, \"Setting watchdog ping type as WHITESPACE for WebSocket connection manager violates RFC7395 specification and can break numerous clients\");\n\t\t}\n\t}\n\n\t@Inject\n\tprivate WebSocketProtocolIfc[] enabledProtocolVersions;\n\n\t@Override\n\tpublic String getDiscoDescription() {\n\t\treturn \"Websocket connection manager\";\n\t}\n\n\t@Override\n\tprotected int[] getDefPlainPorts() {\n\t\treturn new int[]{5290};\n\t}\n\n\t@Override\n\tprotected int[] getDefSSLPorts() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tprotected XMPPIOService<Object> getXMPPIOServiceInstance() {\n\t\treturn new WebSocketXMPPIOService<Object>(enabledProtocolVersions);\n\t}\n\n\t@Override\n\tprotected String prepareStreamClose(XMPPIOService<Object> serv) {\n\t\tif (isPreRFC(serv)) {\n\t\t\treturn super.prepareStreamClose(serv);\n\t\t}\n\t\treturn \"<close xmlns='urn:ietf:params:xml:ns:xmpp-framing' />\";\n\t}\n\n\t@Override\n\tprotected String prepareStreamOpen(XMPPIOService<Object> serv, String id, String hostname, @Nullable String to) {\n\t\tif (isPreRFC(serv)) {\n\t\t\treturn super.prepareStreamOpen(serv, id, hostname, to);\n\t\t}\n\t\treturn \"<open\" + \" xmlns='\" + XMLNS_FRAMING + \"'\" + \" from='\" + hostname + \"'\" + (to == null ? \"\" : (\" to='\" + to + \"'\")) + \" id='\" + id + \"'\" +\n\t\t\t\t\" version='1.0' xml:lang='en' />\";\n\t}\n\n\t@Override\n\tprotected String prepareStreamError(XMPPIOService<Object> serv, List<Element> err_el) {\n\t\tif (isPreRFC(serv)) {\n\t\t\treturn super.prepareStreamError(serv, err_el);\n\t\t}\n\t\tStreamError streamError = StreamError.getByCondition(err_el.get(0).getName());\n\n\t\tfor (XMPPIOProcessor proc : processors) {\n\t\t\tproc.streamError(serv, streamError);\n\t\t}\n\n\t\treturn \"<stream:error xmlns:stream=\\\"http://etherx.jabber.org/streams\\\">\" + err_el.stream().map(Element::toString).collect(\n\t\t\t\tCollectors.joining()) +\n\t\t\t\t\"</stream:error>\";\n\t}\n\n\t@Override\n\tprotected String[] prepareStreamError(XMPPIOService<Object> serv, StreamError streamError, String hostname) {\n\t\tif (isPreRFC(serv)) {\n\t\t\treturn super.prepareStreamError(serv, streamError, hostname);\n\t\t}\n\t\tfor (XMPPIOProcessor proc : processors) {\n\t\t\tproc.streamError(serv, streamError);\n\t\t}\n\t\treturn new String[]{\n\t\t\t\t\"<open\" + \" xmlns='\" + XMLNS_FRAMING + \"'\" + \" from='\" + hostname + \"'\" + \" id='tigase-error-tigase'\" +\n\t\t\t\t\t\t\" version='1.0' xml:lang='en' />\",\n\t\t\t\t\"<stream:error xmlns:stream=\\\"http://etherx.jabber.org/streams\\\">\" + \"<\" + streamError.getCondition() +\n\t\t\t\t\t\t\" xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>\" + \"</stream:error>\",\n\t\t\t\t\"<close xmlns='\" + XMLNS_FRAMING + \"'/>\"};\n\t}\n\n\t@Override\n\tprotected String[] prepareSeeOtherHost(XMPPIOService<Object> serv, String hostname, BareJID see_other_host) {\n\t\tif (isPreRFC(serv)) {\n\t\t\treturn super.prepareSeeOtherHost(serv, hostname, see_other_host);\n\t\t}\n\t\tfor (XMPPIOProcessor proc : processors) {\n\t\t\tproc.streamError(serv, StreamError.SeeOtherHost);\n\t\t}\n\t\tboolean ssl = SocketType.ssl == ((SocketType) serv.getSessionData().get(\"socket\"));\n\t\tint localPort = serv.getLocalPort();\n\t\tString see_other_uri = (ssl ? \"wss://\" : \"ws://\") + see_other_host + \":\" + localPort + \"/\";\n\t\treturn new String[]{\"<open\" + \" xmlns='\" + XMLNS_FRAMING + \"'\" + \" from='\" +\n\t\t\t\t\t\t\t\t\t(hostname != null ? hostname : getDefVHostItem()) + \"'\" +\n\t\t\t\t\t\t\t\t\t\" id='tigase-error-tigase'\" + \" version='1.0' xml:lang='en' />\",\n\t\t\t\t\t\t\t\"<close xmlns='urn:ietf:params:xml:ns:xmpp-framing' see-other-uri='\" + see_other_uri +\n\t\t\t\t\t\t\t\t\t\"' />\"};\n\t}\n\n\t@Override\n\tprotected void preprocessStreamFeatures(XMPPIOService<Object> serv, Element elem_features) {\n\t\tif (!isPreRFC(serv)) {\n\t\t\telem_features.setAttribute(\"xmlns:stream\", \"http://etherx.jabber.org/streams\");\n\t\t}\n\t\tElement starttlsEl = elem_features.findChild(el -> {\n\t\t\treturn StartTLS.EL_NAME == el.getName() && \"urn:ietf:params:xml:ns:xmpp-tls\" == el.getXMLNS();\n\t\t});\n\t\tif (starttlsEl != null) {\n\t\t\telem_features.removeChild(starttlsEl);\n\t\t}\n\t}\n\n\tprivate boolean isPreRFC(XMPPIOService<Object> serv) {\n\t\treturn serv == null || ((WebSocketXMPPIOService<Object>) serv).getWebSocketXMPPSpec() ==\n\t\t\t\tWebSocketXMPPIOService.WebSocketXMPPSpec.hybi;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/websocket/WebSocketHixie76.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.websocket;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.net.SocketType;\n\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Class implements Hixie-76 version of WebSocket protocol specification which is used in connection handshaking as well\n * as in frameing/deframing of data sent over WebSocket connection\n *\n * @author andrzej\n * @see <a href=\"https://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76\">WebSocket Hixie-76 specification</a>\n */\n@Bean(name = \"hyxie76Protocol\", parent = WebSocketClientConnectionManager.class, active = false)\npublic class WebSocketHixie76\n\t\timplements WebSocketProtocolIfc {\n\n\tpublic static final String ID = \"hixie-76\";\n\tprivate static final Logger log = Logger.getLogger(WebSocketHixie76.class.getCanonicalName());\n\tprivate static final String RESPONSE_HEADER =\n\t\t\t\"HTTP/1.1 101 Switching Protocols\\r\\n\" + \"Upgrade: WebSocket\\r\\n\" + \"Connection: Upgrade\\r\\n\" +\n\t\t\t\t\t\"Access-Control-Allow-Origin: *\\r\\n\" + \"Access-Control-Allow-Methods: GET, POST, OPTIONS\\r\\n\" +\n\t\t\t\t\t\"Access-Control-Allow-Headers: Content-Type\\r\\n\" + \"Access-Control-Max-Age: 86400\\r\\n\";\n\n\tprivate static final String HOST_KEY = \"Host\";\n\tprivate static final String ORIGIN_KEY = \"Origin\";\n\tprivate static final String WS_KEY1_KEY = \"Sec-WebSocket-Key1\";\n\tprivate static final String WS_KEY2_KEY = \"Sec-WebSocket-Key2\";\n\tprivate static final String WS_ORIGIN_KEY = \"Sec-WebSocket-Origin\";\n\tprivate static final String WS_LOCATION_KEY = \"Sec-WebSocket-Location\";\n\n\tprivate static final byte[] FRAME_HEADER = new byte[]{(byte) 0x00};\n\tprivate static final byte[] FRAME_FOOTER = new byte[]{(byte) 0xFF};\n\n\t@Override\n\tpublic String getId() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic boolean handshake(WebSocketXMPPIOService service, Map<String, String> headers, byte[] buf)\n\t\t\tthrows NoSuchAlgorithmException, IOException {\n\t\tif (headers.containsKey(WS_VERSION_KEY.toUpperCase())) {\n\t\t\treturn false;\n\t\t}\n\n\t\tbyte[] secBufArr = new byte[16];\n\t\tLong secKey1 = decodeHyxie76SecKey(headers.get(WS_KEY1_KEY.toUpperCase()));\n\t\tLong secKey2 = decodeHyxie76SecKey(headers.get(WS_KEY2_KEY.toUpperCase()));\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"WS-KEY1 = {0}\", secKey1);\n\t\t\tlog.log(Level.FINEST, \"WS-KEY2 = {0}\", secKey2);\n\t\t}\n\t\tuintToBytes(secBufArr, 0, secKey1);\n\t\tuintToBytes(secBufArr, 4, secKey2);\n\t\tif (buf[buf.length - 9] != '\\n') {\n\t\t\tthrow new IOException(\"buf[len-9] != \\\\n!!\");\n\t\t}\n\t\tfor (int j = 8; j > 0; j--) {\n\t\t\tsecBufArr[8 + 8 - j] = buf[buf.length - j];\n\t\t}\n\n\t\tMessageDigest md = MessageDigest.getInstance(\"MD5\");\n\t\tbyte[] resp = md.digest(secBufArr);\n\n\t\tStringBuilder response = new StringBuilder(RESPONSE_HEADER.length() * 2);\n\t\tresponse.append(RESPONSE_HEADER);\n\n\t\tresponse.append(\"Content-Length: \").append(resp.length).append(\"\\r\\n\");\n\t\tresponse.append(WS_PROTOCOL_KEY).append(\": \");\n\t\tif (headers.get(WS_PROTOCOL_KEY.toUpperCase()).contains(\"xmpp-framing\")) {\n\t\t\tresponse.append(\"xmpp-framing\");\n\t\t} else {\n\t\t\tresponse.append(\"xmpp\");\n\t\t}\n\t\tresponse.append(\"\\r\\n\");\n\t\tif (headers.containsKey(ORIGIN_KEY.toUpperCase())) {\n\t\t\tresponse.append(WS_ORIGIN_KEY).append(\": \").append(headers.get(ORIGIN_KEY.toUpperCase())).append(\"\\r\\n\");\n\t\t}\n\n\t\tboolean ssl = SocketType.ssl == ((SocketType) service.getSessionData().get(\"socket\"));\n\t\tint localPort = service.getLocalPort();\n\t\tString location = (ssl ? \"wss://\" : \"ws://\") + headers.get(HOST_KEY.toUpperCase()) +\n\t\t\t\t(((ssl && localPort == 443) || (!ssl && localPort == 80) || headers.get(HOST_KEY.toUpperCase()).contains(\":\"))\n\t\t\t\t ? \"\"\n\t\t\t\t : (\":\" + localPort)) + \"/\";\n\t\tresponse.append(WS_LOCATION_KEY).append(\": \").append(location).append(\"\\r\\n\");\n\n\t\tresponse.append(\"\\r\\n\");\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"sending response = \\n{0}\", response.toString());\n\t\t}\n\n\t\tbyte[] respBytes = response.toString().getBytes();\n\t\tByteBuffer out = ByteBuffer.allocate(respBytes.length + 16);\n\t\tout.put(respBytes);\n\t\tout.put(resp);\n\t\tout.flip();\n\t\tservice.writeBytes(out);\n\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic ByteBuffer decodeFrame(WebSocketXMPPIOService service, ByteBuffer buf) {\n\t\tif (!buf.hasRemaining()) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"no content remainging to process\");\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tint position = buf.position();\n\t\tbyte type = buf.get();\n\n\t\tlog.finest(\"read type = \" + type);\n\n\t\tif ((type & 0x80) != 0x80) {\n\t\t\tint idx = position + 1;\n\t\t\tint remaining = buf.remaining();\n\t\t\tlog.finest(\"remaining = \" + remaining + \" on position \" + position);\n\t\t\twhile ((remaining - ((idx - position))) >= 0) {\n\t\t\t\tlog.finest(\"checking byte at \" + idx + \" = \" + buf.get(idx));\n\t\t\t\tif (buf.get(idx) != ((byte) 0xFF)) {\n\t\t\t\t\tidx++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tlog.finest(\"found data of \" + ((idx - position) - 1) + \" bytes\");\n\t\t\t\tbyte[] data = new byte[(idx - position) - 1];\n\t\t\t\tbuf.get(data);\n\t\t\t\tbuf.position(buf.position() + 1);\n\t\t\t\tlog.finest(\"read data = \" + new String(data));\n\t\t\t\treturn ByteBuffer.wrap(data);\n\t\t\t}\n\t\t\tbuf.position(position);\n\t\t\treturn null;\n\t\t} else {\n\t\t\tlong len = 0;\n\t\t\tbyte b = 0;\n\t\t\twhile (((b = buf.get()) & 0x80) == 0x80) {\n\t\t\t\tlen = (len * 128) + (b & 0x7f);\n\t\t\t}\n\t\t\tlen = (len * 128) + (b & 0x7f);\n\n\t\t\tif (len == 0) {\n\t\t\t\t// close request\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"closing connection due to client request\");\n\t\t\t\t}\n\t\t\t\tservice.setState(WebSocketXMPPIOService.State.closed);\n\t\t\t\t//service.forceStop();\n\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tif (buf.remaining() < len) {\n\t\t\t\tbuf.position(position);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tbyte[] data = new byte[(int) len];\n\t\t\tbuf.get(data);\n\t\t\treturn ByteBuffer.wrap(data);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void encodeFrameAndWrite(WebSocketXMPPIOService service, ByteBuffer buf) throws IOException {\n\t\tservice.writeBytes(ByteBuffer.wrap(FRAME_HEADER));\n\t\tservice.writeBytes(buf);\n\t\tservice.writeBytes(ByteBuffer.wrap(FRAME_FOOTER));\n\t}\n\n\t@Override\n\tpublic void closeConnection(WebSocketXMPPIOService service) {\n\t\tif (!service.isConnected()) {\n\t\t\treturn;\n\t\t}\n\n\t\tservice.setState(WebSocketXMPPIOService.State.closed);\n\t\tservice.writeBytes(ByteBuffer.wrap(new byte[]{(byte) 0xFF, (byte) 0x00}));\n\t}\n\n\tprivate void uintToBytes(byte[] arr, int offset, long val) {\n\t\tfor (int i = 3; i >= 0; i--) {\n\t\t\tarr[offset + i] = (byte) (val % 256);\n\t\t\tval = val / 256;\n\t\t}\n\t}\n\n\tprivate Long decodeHyxie76SecKey(String data) {\n\t\tlong result = 0;\n\t\tint spaces = 0;\n\n\t\tfor (char ch : data.trim().toCharArray()) {\n\t\t\tswitch (ch) {\n\t\t\t\tcase '0':\n\t\t\t\t\tresult = result * 10 + 0;\n\t\t\t\t\tbreak;\n\t\t\t\tcase '1':\n\t\t\t\t\tresult = result * 10 + 1;\n\t\t\t\t\tbreak;\n\t\t\t\tcase '2':\n\t\t\t\t\tresult = result * 10 + 2;\n\t\t\t\t\tbreak;\n\t\t\t\tcase '3':\n\t\t\t\t\tresult = result * 10 + 3;\n\t\t\t\t\tbreak;\n\t\t\t\tcase '4':\n\t\t\t\t\tresult = result * 10 + 4;\n\t\t\t\t\tbreak;\n\t\t\t\tcase '5':\n\t\t\t\t\tresult = result * 10 + 5;\n\t\t\t\t\tbreak;\n\t\t\t\tcase '6':\n\t\t\t\t\tresult = result * 10 + 6;\n\t\t\t\t\tbreak;\n\t\t\t\tcase '7':\n\t\t\t\t\tresult = result * 10 + 7;\n\t\t\t\t\tbreak;\n\t\t\t\tcase '8':\n\t\t\t\t\tresult = result * 10 + 8;\n\t\t\t\t\tbreak;\n\t\t\t\tcase '9':\n\t\t\t\t\tresult = result * 10 + 9;\n\t\t\t\t\tbreak;\n\t\t\t\tcase ' ':\n\t\t\t\t\tspaces++;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn result / spaces;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/websocket/WebSocketHybi.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.websocket;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.util.Base64;\n\nimport java.io.IOException;\nimport java.nio.BufferUnderflowException;\nimport java.nio.ByteBuffer;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.server.websocket.WebSocketXMPPIOService.State.closing;\n\n/**\n * Class implements Hybi (RFC compatible) version of WebSocket protocol specification which is used in connection\n * handshaking as well as in frameing/deframing of data sent over WebSocket connection\n *\n * @author andrzej\n * @see <a href=\"http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-13\">WebSocket HyBi specification</a>\n */\n@Bean(name = \"hybiProtocol\", parent = WebSocketClientConnectionManager.class, active = true)\npublic class WebSocketHybi\n\t\timplements WebSocketProtocolIfc {\n\n\tpublic static final String ID = \"hybi\";\n\tprivate static final Logger log = Logger.getLogger(WebSocketHybi.class.getCanonicalName());\n\tprivate static final String GUID = \"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\";\n\tprivate static final String RESPONSE_HEADER =\n\t\t\t\"HTTP/1.1 101 Switching Protocols\\r\\n\" + \"Upgrade: websocket\\r\\n\" + \"Connection: Upgrade\\r\\n\" +\n\t\t\t\t\t\"Access-Control-Allow-Origin: *\\r\\n\" +\n\t\t\t\t\t// Removed header below as it creates issues with connectivity using IE11\n//\t\t\"Access-Control-Allow-Methods: GET, POST, OPTIONS\\r\\n\" +\n\t\t\t\t\t\"Access-Control-Allow-Headers: Content-Type\\r\\n\" + \"Access-Control-Max-Age: 86400\\r\\n\";\n\n\tprivate static final String WS_ACCEPT_KEY = \"Sec-WebSocket-Accept\";\n\tprivate static final String WS_KEY_KEY = \"Sec-WebSocket-Key\";\n\n\tprivate static final String CLOSE_CODE = \"close-code\";\n\tprivate static final int PROTOCOL_ERROR = 1003;\n\tprivate static byte[] EMPTY = new byte[0];\n\t@ConfigField(desc = \"Allow for unmasked frames send from client\", alias = \"ws-allow-unmasked-frames\")\n\tprivate boolean allowUnmaskedFromClient = false;\n\n\t@Override\n\tpublic String getId() {\n\t\treturn ID;\n\t}\n\n\tstatic String calculateWsAcceptKey(final String webSocketKey) throws NoSuchAlgorithmException {\n\t\tfinal MessageDigest md = MessageDigest.getInstance(\"SHA1\");\n\t\tif (webSocketKey != null) {\n\t\t\tmd.update(webSocketKey.getBytes());\n\t\t}\n\t\tmd.update(GUID.getBytes());\n\t\treturn Base64.encode(md.digest());\n\t}\n\n\t@Override\n\tpublic boolean handshake(WebSocketXMPPIOService service, Map<String, String> headers, byte[] buf)\n\t\t\tthrows NoSuchAlgorithmException, IOException {\n\t\tif (!headers.containsKey(WS_VERSION_KEY.toUpperCase())) {\n\t\t\treturn false;\n\t\t}\n\n\t\tStringBuilder response = new StringBuilder(RESPONSE_HEADER.length() * 2);\n\t\tresponse.append(RESPONSE_HEADER);\n\n\t\tint version = Integer.parseInt(headers.get(WS_VERSION_KEY.toUpperCase()));\n\t\tString wsAccept = calculateWsAcceptKey(headers.get(WS_KEY_KEY.toUpperCase()));\n\n\t\tresponse.append(WS_PROTOCOL_KEY).append(\": \");\n\t\tif (headers.get(WS_PROTOCOL_KEY.toUpperCase()).contains(\"xmpp-framing\")) {\n\t\t\tresponse.append(\"xmpp-framing\");\n\t\t} else {\n\t\t\tresponse.append(\"xmpp\");\n\t\t}\n\t\tresponse.append(\"\\r\\n\");\n\t\tresponse.append(WS_ACCEPT_KEY + \": \");\n\t\tresponse.append(wsAccept);\n\t\tresponse.append(\"\\r\\n\");\n\t\tresponse.append(\"\\r\\n\");\n\t\tservice.maskingKey = new byte[4];\n\t\tservice.writeRawData(response.toString());\n\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic ByteBuffer decodeFrame(WebSocketXMPPIOService service, ByteBuffer buf) {\n\t\tif (!buf.hasRemaining()) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Socket: {0}, no content remainging to process\", new Object[]{service});\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tboolean masked = false;\n\t\tbyte type = 0x00;\n\t\tint position = buf.position();\n\t\tByteBuffer unmasked = null;\n\n\t\ttry {\n\t\t\tif (service.frameLength == -1) {\n\t\t\t\ttype = buf.get();\n\t\t\t\tif ((type & 0x0F) == 0x08) {\n\n\t\t\t\t\t// close request\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Socket: {0}, closing connection due to client request {1}\",\n\t\t\t\t\t\t\t\tnew Object[]{service, String.format(\"%02X \", type)});\n\t\t\t\t\t}\n\t\t\t\t\tservice.setState(closing);\n\t\t\t\t\tcloseConnection(service, null);\n\t\t\t\t\t//service.forceStop();\n\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\n\t\t\t\tbyte b2 = buf.get();\n\n\t\t\t\t// check if content is masked\n\t\t\t\tmasked = (b2 & 0x80) == 0x80;\n\t\t\t\tif (!masked && !allowUnmaskedFromClient) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\t\"Socket: {0}, closing connection due to protocol error - unmasked frame sent by client {1}\",\n\t\t\t\t\t\t\t\tnew Object[]{service, String.format(\"%02X \", type)});\n\t\t\t\t\t}\n\t\t\t\t\tcloseConnection(service, PROTOCOL_ERROR);\n\t\t\t\t\t//service.forceStop();\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\n\t\t\t\t// ignore sign bit\n\t\t\t\tservice.frameLength = (b2 & 0x7F);\n\t\t\t\tif (service.frameLength > 125) {\n\t\t\t\t\t// if frame length is bigger than 125 then\n\t\t\t\t\t// if is 126 - size is short (unsigned short)\n\t\t\t\t\t// is is 127 - size is long\n\t\t\t\t\tservice.frameLength = (service.frameLength == 126) ? (buf.getShort() & 0xffff) : buf.getLong();\n\t\t\t\t}\n\t\t\t\tif (masked) {\n\n\t\t\t\t\t// if content is masked get masking key\n\t\t\t\t\tbuf.get(service.maskingKey);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (buf.remaining() >= service.frameLength) {\n\t\t\t\tbyte[] data = new byte[(int) service.frameLength];\n\n\t\t\t\tbuf.get(data);\n\n\t\t\t\t// if content is masked then unmask content\n\t\t\t\tif (masked) {\n\t\t\t\t\tbyte[] maskingKey = service.maskingKey;\n\t\t\t\t\tfor (int i = 0; i < data.length; i++) {\n\t\t\t\t\t\tdata[i] = (byte) (data[i] ^ maskingKey[i % 4]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tunmasked = ByteBuffer.wrap(data);\n\t\t\t\tservice.frameLength = -1;\n\t\t\t} else {\n\t\t\t\t// not enought data so reset buffer position\n\t\t\t\tbuf.position(position);\n\t\t\t\tservice.frameLength = -1;\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tif (service.frameLength == -1) {\n\n\t\t\t\t// we need to ignore pong frame\n\t\t\t\tif ((type & 0x0A) == 0x0A) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Socket: {0}, ignoring pong frame\", new Object[]{service});\n\t\t\t\t\t}\n\t\t\t\t\t// We are returning empty byte buffer to make sure other frames remaining in buffer will be processed\n\t\t\t\t\tunmasked = ByteBuffer.wrap(EMPTY);\n\t\t\t\t} // if it ping request send pong response\n\t\t\t\telse if ((type & 0x09) == 0x09) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Socket: {0}, sending response on ping frame\", new Object[]{service});\n\t\t\t\t\t}\n\t\t\t\t\ttype = (byte) (((byte) (type ^ 0x09)) | 0x0A);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tByteBuffer header = createFrameHeader(type, unmasked.remaining());\n\n\t\t\t\t\t\tservice.writeInProgress.lock();\n\t\t\t\t\t\tservice.writeBytes(header);\n\t\t\t\t\t\tservice.writeBytes(unmasked);\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tservice.writeInProgress.unlock();\n\t\t\t\t\t}\n\t\t\t\t\t// We are returning empty byte buffer to make sure other frames remaining in buffer will be processed\n\t\t\t\t\tunmasked = ByteBuffer.wrap(EMPTY);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (BufferUnderflowException ex) {\n\t\t\t// if for some reason we do not have full frame header then we need to \n\t\t\t// reset buffer to original position and wait for the rest of data\n\t\t\tbuf.position(position);\n\t\t\tservice.frameLength = -1;\n\t\t\tunmasked = null;\n\t\t}\n\n\t\treturn unmasked;\n\t}\n\n\t@Override\n\tpublic void encodeFrameAndWrite(WebSocketXMPPIOService service, ByteBuffer buf) throws IOException {\n\t\tint size = buf.remaining();\n\n\t\t// set type as finally part (0x80) of message of type text (0x01)\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Socket: {0}, sending encoded data size = {1}\", new Object[]{service, size});\n\t\t}\n\n\t\tByteBuffer bbuf = createFrameHeader((byte) 0x81, size);\n\n\t\t// send frame header\n\t\tservice.writeBytes(bbuf);\n\n\t\tservice.writeBytes(buf);\n\t}\n\n\t@Override\n\tpublic void closeConnection(WebSocketXMPPIOService service) {\n\t\tif (!service.isConnected()) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Socket: {0}, sending close frame\", service);\n\t\t}\n\n\t\tservice.setState(WebSocketXMPPIOService.State.closed);\n\t\tInteger code = (Integer) service.getSessionData().get(CLOSE_CODE);\n\t\tint len = 0;\n\t\tif (code != null) {\n\t\t\tlen += 2;\n\t\t}\n\t\tByteBuffer bbuf = createFrameHeader((byte) 0x88, len);\n\t\tservice.writeBytes(bbuf);\n\t\tif (code != null) {\n\t\t\tByteBuffer buf = ByteBuffer.allocate(2);\n\t\t\tbuf.putShort(code.shortValue());\n\t\t\tbuf.flip();\n\t\t\tservice.writeBytes(buf);\n\t\t}\n\t}\n\n\tprivate void closeConnection(WebSocketXMPPIOService service, Integer code) {\n\t\tif (code != null) {\n\t\t\tservice.getSessionData().put(CLOSE_CODE, code);\n\t\t}\n\t\tswitch (service.getState()) {\n\t\t\tcase closing:\n\t\t\t\tservice.setState(WebSocketXMPPIOService.State.closed);\n\t\t\t\tbreak;\n\t\t\tcase handshaked:\n\t\t\t\tservice.setState(WebSocketXMPPIOService.State.closing);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Create WebSocket frame header with specific type and size\n\t *\n\t */\n\tprivate ByteBuffer createFrameHeader(byte type, int size) {\n\t\tByteBuffer bbuf = ByteBuffer.allocate(12);\n\n\t\tbbuf.put(type);\n\t\tif (size <= 125) {\n\t\t\tbbuf.put((byte) size);\n\t\t} else if (size <= 0xFFFF) {\n\t\t\tbbuf.put((byte) 0x7E);\n\t\t\tbbuf.putShort((short) size);\n\t\t} else {\n\t\t\tbbuf.put((byte) 0x7F);\n\t\t\tbbuf.putLong(size);\n\t\t}\n\t\tbbuf.flip();\n\n\t\treturn bbuf;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/websocket/WebSocketProtocolIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.websocket;\n\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Map;\n\n/**\n * Interface which needs to be implemented by any implemention of version of WebSocket protocol.\n * <br>\n * Currently we have stable version but there were older not compatible with current so it may be that new will come in\n * future - also not compatible.\n *\n * @author andrzej\n */\npublic interface WebSocketProtocolIfc {\n\n\t/**\n\t * HTTP header used by WebSocket to pass used version of WebSocket protocol from client to server\n\t */\n\tpublic static final String WS_VERSION_KEY = \"Sec-WebSocket-Version\";\n\n\t/**\n\t * HTTP header which contains name of subprotocol which should be used over established WebSocket connection\n\t */\n\tstatic final String WS_PROTOCOL_KEY = \"Sec-WebSocket-Protocol\";\n\n\t/**\n\t * Method to retrieve string identifier of implementation of protcol version\n\t */\n\tString getId();\n\n\t/**\n\t * Method responsible for handshaking of WebSocket using proper version of protocol.\n\t *\n\t * @return false - if implementation is not able to handshake using this version of protocol, in other case return\n\t * true\n\t */\n\tboolean handshake(WebSocketXMPPIOService service, Map<String, String> headers, byte[] buf)\n\t\t\tthrows NoSuchAlgorithmException, IOException;\n\n\t/**\n\t * Method responsible for decoding data received from socket and returning data after extracting it from WebSocket\n\t * frame.\n\t *\n\t * @return decoded data or null if not full frame is available in input buffer\n\t */\n\tByteBuffer decodeFrame(WebSocketXMPPIOService service, ByteBuffer buf);\n\n\t/**\n\t * Method encodes data into WebSocket frame and writes it to passed service\n\t */\n\tvoid encodeFrameAndWrite(WebSocketXMPPIOService service, ByteBuffer buf) throws IOException;\n\n\t/**\n\t * Method closes connection by sending close frame\n\t */\n\tvoid closeConnection(WebSocketXMPPIOService service);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/websocket/WebSocketXMPPIOService.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.websocket;\n\nimport tigase.server.Packet;\nimport tigase.xmpp.XMPPIOService;\n\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.CharBuffer;\nimport java.nio.charset.CharacterCodingException;\nimport java.nio.charset.CoderResult;\nimport java.nio.charset.MalformedInputException;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Class implements basic support for WebSocket protocol. It extends XMPPIOService so it can be used instead of\n * XMPPIOService in ClientConnectionManager to allow web clients to connect to it without using BOSH extension.\n *\n */\npublic class WebSocketXMPPIOService<RefObject>\n\t\textends XMPPIOService<RefObject> {\n\n\tprivate static final String BAD_REQUEST = \"HTTP/1.0 400 Bad request\\r\\n\\r\\n\";\n\tprivate static final String CONNECTION_KEY = \"Connection\";\n\tprivate static final Logger log = Logger.getLogger(WebSocketXMPPIOService.class.getCanonicalName());\n\n\tprivate static final String CLOSE_EL = \"close\";\n\tprivate static final String OPEN_EL = \"open\";\n\tprivate static final String XMLNS_FRAMING = \"urn:ietf:params:xml:ns:xmpp-framing\";\n\n\tpublic enum State {\n\t\t// begining state - used when we retrieve data from\n\t\t// connection to detect if connection is able to be\n\t\t// upgraded to WebSocket\n\t\thandshaking,\n\t\t// after WebSocket handshake is completed - can send data\n\t\thandshaked,\n\t\t// when server sent close request\n\t\tclosing,\n\t\t// when server sent close request and received\n\t\t// close request from client\n\t\tclosed\n\t}\n\n\tpublic static enum WebSocketXMPPSpec {\n\t\thybi,\n\t\txmpp\n\t}\n\n\t/* static variables used by WebSocket protocol */\n\n\tprivate final WebSocketProtocolIfc[] protocols;\n\tprotected long frameLength = -1;\n\tprotected byte[] maskingKey = null;\n\tprivate byte[] partialData = null;\n\tprivate WebSocketProtocolIfc protocol = null;\n\tprivate boolean started = false;\n\t// internal properties\n\t//private boolean websocket = false;\n\tprivate State state = State.handshaking;\n\tprivate WebSocketXMPPSpec webSocketXMPPSpec = WebSocketXMPPSpec.hybi;\n\n\tpublic WebSocketXMPPIOService(WebSocketProtocolIfc[] enabledProtocols) {\n\t\tthis.protocols = enabledProtocols;\n\t}\n\n\t@Override\n\tpublic void stop() {\n\t\tprotocol.closeConnection(this);\n\t\tsuper.stop(); //To change body of generated methods, choose Tools | Templates.\n\t}\n\n\tpublic void dumpHeaders(Map<String, String> headers) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tStringBuilder builder = new StringBuilder(1000);\n\t\t\tfor (Map.Entry<String, String> entry : headers.entrySet()) {\n\t\t\t\tbuilder.append(\"KEY = \");\n\t\t\t\tbuilder.append(entry.getKey());\n\t\t\t\tbuilder.append(\"VALUE = \");\n\t\t\t\tbuilder.append(entry.getValue());\n\t\t\t\tbuilder.append('\\n');\n\t\t\t}\n\n\t\t\tlog.log(Level.FINEST, \"received headers = \\n{0}\", builder.toString());\n\t\t}\n\t}\n\n\tprotected State getState() {\n\t\treturn state;\n\t}\n\n\tprotected void setState(State state) {\n\t\tthis.state = state;\n\t}\n\n\t@Override\n\tprotected void addReceivedPacket(Packet packet) {\n\t\tif (packet.getXMLNS() == XMLNS_FRAMING) {\n\t\t\t// it is framing packet, so it should be <open/> or <close/>\n\t\t\tif (packet.getElemName() == OPEN_EL) {\n\t\t\t\twebSocketXMPPSpec = WebSocketXMPPSpec.xmpp;\n\t\t\t\txmppStreamOpened(packet.getElement().getAttributes());\n\t\t\t\treturn;\n\t\t\t} else if (packet.getElemName() == CLOSE_EL) {\n\t\t\t\txmppStreamClosed();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tsuper.addReceivedPacket(packet); //To change body of generated methods, choose Tools | Templates.\n\t}\n\n\t@Override\n\tprotected void processSocketData() throws IOException {\n\t\tState oldState = state;\n\t\tsuper.processSocketData();\n\t\tif (state != oldState && oldState == State.handshaked) {\n\t\t\tprotocol.closeConnection(this);\n\t\t}\n\t}\n\n\tprotected WebSocketXMPPSpec getWebSocketXMPPSpec() {\n\t\treturn webSocketXMPPSpec;\n\t}\n\n\t@Override\n\tprotected String prepareStreamClose() {\n\t\tif (webSocketXMPPSpec == WebSocketXMPPSpec.hybi) {\n\t\t\treturn \"</stream:stream>\";\n\t\t}\n\t\treturn \"<close xmlns='urn:ietf:params:xml:ns:xmpp-framing' />\";\n\t}\n\n\t@Override\n\tprotected char[] readData() throws IOException {\n\t\tByteBuffer cb = super.readBytes();\n\n\t\tif (cb == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// handling partialy decoded frame\n\t\tif (partialData != null) {\n\t\t\tByteBuffer oldtmp = cb;\n\t\t\tcb = ByteBuffer.allocate(partialData.length + oldtmp.remaining());\n\t\t\tcb.order(oldtmp.order());\n\t\t\tcb.put(partialData);\n\t\t\tcb.put(oldtmp);\n\t\t\tcb.flip();\n\t\t\toldtmp.clear();\n\t\t\tpartialData = null;\n\t\t}\n\n\t\tif (state != State.handshaking) {\n\n\t\t\t// data needs to be decoded fully not just first frame!!\n\t\t\tByteBuffer tmp = ByteBuffer.allocate(cb.remaining());\n\t\t\t// here we got buffer overflow\n\t\t\tByteBuffer decoded = null;\n\t\t\twhile (cb.hasRemaining() && (decoded = decodeFrame(cb)) != null) {\n\t\t\t\t//decoded = decodeFrame(cb);\n\t\t\t\tif (decoded != null && decoded.hasRemaining()) {\n\t\t\t\t\ttmp.put(decoded);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// handling data which were not decoded - not complete data\n\t\t\tif (cb.hasRemaining()) {\n\t\t\t\tpartialData = new byte[cb.remaining()];\n\t\t\t\tcb.get(partialData);\n\t\t\t}\n\n\t\t\t// compact buffer after reading all frames\n\t\t\tcb.compact();\n\n\t\t\tif (tmp.capacity() > 0) {\n\t\t\t\ttmp.flip();\n\t\t\t}\n\t\t\tcb = tmp;\n\t\t}\n\t\tif (started) {\n\t\t\treturn decode(cb);\n\t\t}\n\n\t\tif (!cb.hasRemaining()) {\n\t\t\treturn null;\n\t\t}\n\n\t\ttry {\n/*\t\t\tif (!started && (cb.get(0) != (byte) 'G')) {\n\t\t\t\tstarted = true;\n\n\t\t\t\treturn decode(cb);\n\t\t\t}*/\n\n\t\t\tint remaining = cb.remaining();\n\t\t\tbyte[] buf = new byte[remaining];\n\n\t\t\tcb.get(buf, 0, remaining);\n\t\t\t//pos += read;\n\t\t\tcb.compact();\n\t\t\tint pos = remaining;\n\t\t\tif ((pos > 100) && (((buf[pos - 1] == '\\n') && (buf[pos - 1] == buf[pos - 3])) ||\n\t\t\t\t\t((buf[pos - 9] == '\\n') && (buf[pos - 9] == buf[pos - 11])))) {\n\t\t\t\tstarted = true;\n\t\t\t\tprocessWebSocketHandshake(buf);\n\t\t\t\t//websocket = true;\n\t\t\t\tif (protocol != null) {\n\t\t\t\t\tstate = State.handshaked;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tpartialData = buf;\n\t\t\t}\n\t\t} catch (Exception ex) {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"exception processing websocket handshake\", ex);\n\t\t\t}\n\t\t\tthis.forceStop();\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Custom implementation of writeData function which encodes data in WebSocket protocol frames\n\t *\n\t */\n\t@Override\n\tprotected void writeData(final String data) {\n\n\t\t// Try to lock the data writing method\n\t\t// If cannot lock and nothing to send, just leave\n\t\tboolean locked = writeInProgress.tryLock();\n\n\t\t// Otherwise wait.....\n\t\tif (!locked) {\n\t\t\tif (data == null) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\twriteInProgress.lock();\n\t\t}\n\t\ttry {\n\t\t\tif (state != State.handshaking) {\n\t\t\t\ttry {\n\t\t\t\t\tif (data != null) {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"sending data = {0}\", data);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tByteBuffer buf = encode(data);\n\t\t\t\t\t\tprotocol.encodeFrameAndWrite(this, buf);\n\n\t\t\t\t\t\t//buf.compact();\n\t\t\t\t\t} else {\n\t\t\t\t\t\twriteBytes(null);\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\tlog.log(Level.FINE, \"exception writing data\", ex);\n\t\t\t\t\t}\n\t\t\t\t\tforceStop();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tsuper.writeData(data);\n\t\t\t}\n\t\t} finally {\n\t\t\twriteInProgress.unlock();\n\t\t}\n\t}\n\n\tprotected int parseHttpHeaders(byte[] buf, Map<String, String> headers) {\n\t\tint i = 0;\n\n\t\twhile (buf[i] != '\\n') {\n\t\t\ti++;\n\t\t}\n\t\ti++;\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"parsing request = \\n{0}\", new String(buf));\n\t\t}\n\n\t\tStringBuilder builder = new StringBuilder(64);\n\t\tString key = null;\n\n\t\tboolean skipWhitespace = false;\n\t\tfor (; i < buf.length; i++) {\n\t\t\tswitch (buf[i]) {\n\t\t\t\tcase ':':\n\t\t\t\t\tif (key == null) {\n\t\t\t\t\t\tkey = builder.toString().trim().toUpperCase();\n\t\t\t\t\t\tbuilder = new StringBuilder(64);\n\t\t\t\t\t\tskipWhitespace = true;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbuilder.append((char) buf[i]);\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase '\\r':\n\t\t\t\t\theaders.put(key, builder.toString().trim());\n\t\t\t\t\tkey = null;\n\t\t\t\t\tbuilder = new StringBuilder(64);\n\t\t\t\t\tif (buf[i + 2] == '\\r') {\n\t\t\t\t\t\ti += 3;\n\t\t\t\t\t} else {\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase ' ':\n\t\t\t\tcase '\\t':\n\t\t\t\t\tif (!skipWhitespace) {\n\t\t\t\t\t\tbuilder.append((char) buf[i]);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tskipWhitespace = false;\n\t\t\t\t\tbuilder.append((char) buf[i]);\n\t\t\t}\n\t\t}\n\t\treturn i;\n\t}\n\n\t@Override\n\tprotected void writeBytes(ByteBuffer data) {\n\t\tsuper.writeBytes(data);\n\t}\n\n\t/**\n\t * Process data from internal temporary buffer used to decode HTTP request used by WebSocket protocol to switch\n\t * protocol to WebSocket protocol\n\t *\n\t */\n\tprivate void processWebSocketHandshake(byte[] buf) throws NoSuchAlgorithmException, IOException {\n\t\tHashMap<String, String> headers = new HashMap<String, String>();\n\t\tint i = parseHttpHeaders(buf, headers);\n\n\t\tString connectionHeader = headers.get(CONNECTION_KEY.toUpperCase());\n\t\tif (connectionHeader != null) {\n\t\t\tString[] values = connectionHeader.split(\",\");\n\t\t\tboolean contains = false;\n\t\t\tfor (String value : values) {\n\t\t\t\tcontains |= \"Upgrade\".equalsIgnoreCase(value.trim());\n\t\t\t\tif (contains) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!contains) {\n\t\t\t\twriteRawData(BAD_REQUEST);\n\n\t\t\t\tlog.log(Level.FINEST, \"WS, Connection: Upgrade not found, closing connection: {0}\", this);\n\t\t\t\tdumpHeaders(headers);\n\t\t\t\tforceStop();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (!headers.containsKey(WebSocketProtocolIfc.WS_PROTOCOL_KEY.toUpperCase()) ||\n\t\t\t\t!headers.get(WebSocketProtocolIfc.WS_PROTOCOL_KEY.toUpperCase()).contains(\"xmpp\")) {\n\t\t\twriteRawData(BAD_REQUEST);\n\n\t\t\tlog.log(Level.FINEST, WebSocketProtocolIfc.WS_PROTOCOL_KEY + \" not found, closing connection: {0}\", this);\n\t\t\tdumpHeaders(headers);\n\t\t\tforceStop();\n\t\t\treturn;\n\t\t}\n\n\t\ti = 0;\n\t\twhile (protocol == null && i < protocols.length) {\n\t\t\tif (protocols[i].handshake(this, headers, buf)) {\n\t\t\t\tprotocol = protocols[i];\n\t\t\t} else {\n\t\t\t\ti++;\n\t\t\t}\n\t\t}\n\n\t\tif (protocol == null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"could not find implementation for WebSocket protocol for {0}\", this);\n\t\t\t}\n\t\t\tdumpHeaders(headers);\n\t\t\tforceStop();\n\t\t}\n\t}\n\n\t/**\n\t * Decode data encoded in WebSocket frames from buffer\n\t *\n\t */\n\tprivate ByteBuffer decodeFrame(ByteBuffer buf) {\n\t\treturn protocol.decodeFrame(this, buf);\n\t}\n\n\t/**\n\t * Decode data from buffer to chars array\n\t */\n\tprivate char[] decode(ByteBuffer tmpBuffer) throws MalformedInputException {\n\t\tif (tmpBuffer == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tchar[] result = null;\n\n\t\t// Restore the partial bytes for multibyte UTF8 characters\n\t\tif (partialCharacterBytes != null) {\n\t\t\tByteBuffer oldTmpBuffer = tmpBuffer;\n\n\t\t\ttmpBuffer = ByteBuffer.allocate(partialCharacterBytes.length + oldTmpBuffer.remaining() + 2);\n\t\t\ttmpBuffer.put(partialCharacterBytes);\n\t\t\ttmpBuffer.put(oldTmpBuffer);\n\t\t\ttmpBuffer.flip();\n\t\t\toldTmpBuffer.clear();\n\t\t\tpartialCharacterBytes = null;\n\t\t}\n\t\tif (cb.capacity() < tmpBuffer.remaining() * 4) {\n\t\t\tcb = CharBuffer.allocate(tmpBuffer.remaining() * 4);\n\t\t}\n\n\t\tCoderResult cr = decoder.decode(tmpBuffer, cb, false);\n\n\t\tif (cr.isMalformed()) {\n\t\t\tthrow new MalformedInputException(tmpBuffer.remaining());\n\t\t}\n\t\tif (cb.remaining() > 0) {\n\t\t\tcb.flip();\n\t\t\tresult = new char[cb.remaining()];\n\t\t\tcb.get(result);\n\t\t}\n\t\tif (cr.isUnderflow() && (tmpBuffer.remaining() > 0)) {\n\n\t\t\t// Save the partial bytes of a multibyte character such that they\n\t\t\t// can be restored on the next read.\n\t\t\tpartialCharacterBytes = new byte[tmpBuffer.remaining()];\n\t\t\ttmpBuffer.get(partialCharacterBytes);\n\t\t}\n\t\ttmpBuffer.clear();\n\t\tcb.clear();\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Encode string into buffer\n\t */\n\tprivate ByteBuffer encode(String data) throws CharacterCodingException {\n\t\tByteBuffer dataBuffer = null;\n\n\t\tencoder.reset();\n\n\t\t// dataBuffer = encoder.encode(CharBuffer.wrap(data, idx_start,\n\t\t// idx_offset));\n\t\tdataBuffer = encoder.encode(CharBuffer.wrap(data));\n\t\tencoder.flush(dataBuffer);\n\n\t\t// dataBuffer.flip();\n\t\treturn dataBuffer;\n\t}\n\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppclient/C2SIOService.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppclient;\n\nimport tigase.io.TLSIOIfc;\nimport tigase.net.IOService;\nimport tigase.net.SocketThread;\nimport tigase.server.Packet;\nimport tigase.util.Algorithms;\nimport tigase.xmpp.XMPPIOService;\n\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.CharBuffer;\nimport java.util.Arrays;\nimport java.util.Queue;\nimport java.util.concurrent.ConcurrentLinkedQueue;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class C2SIOService<RefObject>\n\t\textends XMPPIOService<RefObject> {\n\n\tprivate static final Logger log = Logger.getLogger(C2SIOService.class.getCanonicalName());\n\n\tprivate AtomicInteger waitForResponse = new AtomicInteger(0);\n\tprivate boolean pipelining = true;\n\n\tprivate Queue<Runnable> tasks = new ConcurrentLinkedQueue<>();\n\n\tprivate byte[] tlsData = null;\n\n\t@Override\n\tprotected void addReceivedPacket(Packet packet) {\n\t\tif (pipelining) {\n\t\t\tsynchronized (tasks) {\n\t\t\t\tif (isWaitingForResponse()) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"queuing received packet as a task \" + packet);\n\t\t\t\t\t}\n\t\t\t\t\tboolean result = tasks.offer(() -> {\n\t\t\t\t\t\twaitForResponse.incrementAndGet();\n\t\t\t\t\t\tsuper.addReceivedPacket(packet);\n\t\t\t\t\t});\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"queued (\" + result + \") received packet as a task \" + packet);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tsuper.addReceivedPacket(packet);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tsuper.addReceivedPacket(packet);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean waitingToRead() {\n\t\treturn tlsData == null && super.waitingToRead();\n\t}\n\n\t@Override\n\tpublic void processWaitingPackets() throws IOException {\n\t\tif (pipelining) {\n\t\t\tboolean hadPackets = !this.getWaitingPackets().isEmpty();\n\t\t\tsuper.processWaitingPackets();\n\t\t\tif (hadPackets && this.getWaitingPackets().isEmpty()) {\n\t\t\t\trunQueuedTaskIfExists();\n\t\t\t}\n\t\t} else {\n\t\t\tsuper.processWaitingPackets();\n\t\t}\n\t}\n\t\n\tpublic void waitForResponse() {\n\t\twaitForResponse.incrementAndGet();\n\t}\n\n\tpublic boolean isWaitingForResponse() {\n\t\treturn !tasks.isEmpty() || waitForResponse.get() > 0;\n\t}\n\n\tpublic void queueTask(Runnable run) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"queuing task \" + run);\n\t\t}\n\t\tmoveParsedPacketsToReceived(false);\n\t\ttasks.offer(run);\n\t}\n\n//\t@Override\n//\tpublic void setUserJid(String jid) {\n//\t\tsuper.setUserJid(jid);\n//\t\tif (jid != null) {\n//\t\t\t// in this case user is authenticated and we can finish pipelining\n//\t\t\tthis.pipelining = false;\n//\t\t\t//this.waitForResponse = false;\n//\t\t}\n//\t}\n\n\tpublic boolean shouldQueueStreamOpened() {\n\t\tboolean value = hasParsedElements();\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"checking if should queue stream opened = \" + value);\n\t\t}\n\t\treturn value;\n\t}\n\n\t@Override\n\tprotected boolean handleMalformedInput(ByteBuffer buffer, CharBuffer cb) {\n\t\tint i = 0;\n\t\twhile (cb.position() + i >= 0 && cb.get(cb.position()+i) != '>') {\n\t\t\ti--;\n\t\t}\n\t\tif (cb.position() + i < 0) {\n\t\t\tcb.position(0);\n\t\t} else {\n\t\t\tcb.position(cb.position() + i + 1);\n\t\t}\n\n\t\tfor (i = 0; i < buffer.limit(); i++) {\n\t\t\tbyte b = buffer.get(i);\n\t\t\tswitch (b) {\n\t\t\t\tcase 0x16:\n\t\t\t\t\t// may be SSL 3.0 or newer header\n\t\t\t\t\tif (i + 5 < buffer.limit() && buffer.get(i+5) == 0x01) {\n\t\t\t\t\t\t// found SSL 3.0 header!!\n\t\t\t\t\t\tsynchronized (this) {\n\t\t\t\t\t\t\textractTlsHandshakeData(buffer, i);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\tcase 0x01:\n\t\t\t\t\tif (i >= 2 && (buffer.get(i-2) & 0x80) == 0x80) {\n\t\t\t\t\t\t// found SSL 2.0 header!!\n\t\t\t\t\t\t// check if length is less than or equal 16709\n\t\t\t\t\t\tif (((((int) (buffer.get(i-2) & 0x7f)) << 8) | buffer.get(i-1)) > 16709) {\n\t                        return false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsynchronized (this) {\n\t\t\t\t\t\t\textractTlsHandshakeData(buffer, i - 2);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tlog.log(Level.FINER, \"Tried Not found SSL/TLS handshake, bb: {0}, contents: {1} , cb: {2}\",\n\t\t\t\tnew String[]{String.valueOf(buffer), Arrays.toString(buffer.array()), String.valueOf(cb.array())});\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void startTLS(boolean clientMode, boolean wantClientAuth, boolean needClientAuth) throws IOException {\n\t\tsuper.startTLS(clientMode, wantClientAuth, needClientAuth);\n\t\tif (tlsData != null && getIO() instanceof TLSIOIfc) {\n\t\t\tsynchronized (this) {\n\t\t\t\t((TLSIOIfc) getIO()).processHandshake(tlsData);\n\t\t\t\ttlsData = null;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void extractTlsHandshakeData(ByteBuffer buffer, int i) {\n\t\tbuffer.position(i);\n\t\ttlsData = new byte[buffer.limit() - (i)];\n\t\tbuffer.get(tlsData);\n\t\tif (getIO() instanceof TLSIOIfc) {\n\t\t\ttry {\n\t\t\t\t((TLSIOIfc) getIO()).processHandshake(tlsData);\n\t\t\t} catch (Exception ex) {\n\t\t\t\tif (log.isLoggable(Level.WARNING)) {\n\t\t\t\t\tbyte[] data = Arrays.copyOf(tlsData, Math.min(tlsData.length, 10));\n\t\t\t\t\tlog.log(Level.FINE,\n\t\t\t\t\t\t\t\"Error while processing handshake, SSL packet header \" + Algorithms.bytesToHex(data) + \"...\", ex);\n\t\t\t\t}\n\t\t\t}\n\t\t\ttlsData = null;\n\t\t} else {\n\t\t\tSocketThread.removeSocketService((IOService<Object>) this);\n\t\t}\n\t}\n\n\tprivate void runQueuedTaskIfExists() {\n\t\tRunnable run;\n\t\tsynchronized (tasks) {\n\t\t\twaitForResponse.decrementAndGet();\n\t\t\trun = tasks.poll();\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"got task \" + run + \" to execute\");\n\t\t}\n\t\tif (run != null) {\n\t\t\trun.run();\n\t\t\trun = tasks.peek();\n\t\t}\n\t\t\n\t\tif (run == null) {\n\t\t\ttry {\n\t\t\t\t//waitForResponse = false;\n\t\t\t\tif (getUserJid() != null) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"finished stream initiation, disabling pipelining...\");\n\t\t\t\t\t}\n\t\t\t\t\tthis.pipelining = false;\n\t\t\t\t}\n\t\t\t\tif (serviceListener != null) {\n\t\t\t\t\tserviceListener.packetsReady(this);\n\t\t\t\t}\n\t\t\t\t//SocketThread.addSocketService(this);\n\t\t\t} catch (IOException ex) {\n\t\t\t\tforceStop();\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppclient/ClientConnectionManager.java",
    "content": "/*\r\n * Tigase XMPP Server - The instant messaging server\r\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\r\n *\r\n * This program is free software: you can redistribute it and/or modify\r\n * it under the terms of the GNU Affero General Public License as published by\r\n * the Free Software Foundation, version 3 of the License.\r\n *\r\n * This program is distributed in the hope that it will be useful,\r\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n * GNU Affero General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU Affero General Public License\r\n * along with this program. Look for COPYING file in the top folder.\r\n * If not, see http://www.gnu.org/licenses/.\r\n */\r\npackage tigase.server.xmppclient;\r\n\r\nimport org.jspecify.annotations.Nullable;\r\nimport tigase.annotations.TigaseDeprecated;\r\nimport tigase.eventbus.EventBus;\r\nimport tigase.eventbus.HandleEvent;\r\nimport tigase.eventbus.events.ShutdownEvent;\r\nimport tigase.kernel.beans.Bean;\r\nimport tigase.kernel.beans.Inject;\r\nimport tigase.kernel.beans.config.ConfigAlias;\r\nimport tigase.kernel.beans.config.ConfigAliases;\r\nimport tigase.kernel.beans.config.ConfigField;\r\nimport tigase.kernel.beans.selector.ClusterModeRequired;\r\nimport tigase.kernel.beans.selector.ConfigType;\r\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\r\nimport tigase.kernel.core.Kernel;\r\nimport tigase.net.IOService;\r\nimport tigase.net.SocketThread;\r\nimport tigase.net.SocketType;\r\nimport tigase.server.*;\r\nimport tigase.util.Base64;\r\nimport tigase.util.common.TimerTask;\r\nimport tigase.util.routing.RoutingsContainer;\r\nimport tigase.util.stringprep.TigaseStringprepException;\r\nimport tigase.vhosts.VHostItem;\r\nimport tigase.xml.Element;\r\nimport tigase.xmpp.*;\r\nimport tigase.xmpp.impl.C2SDeliveryErrorProcessor;\r\nimport tigase.xmpp.jid.BareJID;\r\nimport tigase.xmpp.jid.JID;\r\n\r\nimport javax.net.ssl.TrustManager;\r\nimport java.io.IOException;\r\nimport java.security.cert.CertificateEncodingException;\r\nimport java.util.*;\r\nimport java.util.concurrent.TimeUnit;\r\nimport java.util.logging.Level;\r\nimport java.util.logging.Logger;\r\nimport java.util.stream.Collectors;\r\nimport java.util.zip.Deflater;\r\n\r\n/**\r\n * Class ClientConnectionManager Created: Tue Nov 22 07:07:11 2005\r\n *\r\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\r\n*/\r\n@Bean(name = \"c2s\", parent = Kernel.class, active = true)\r\n@ConfigType({ConfigTypeEnum.DefaultMode, ConfigTypeEnum.ConnectionManagersMode})\r\n@ClusterModeRequired(active = false)\r\n@ConfigAliases({@ConfigAlias(field = \"delayPortListening\", alias = \"client-port-delay-listening\")})\r\npublic class ClientConnectionManager\r\n\t\textends ConnectionManager<XMPPIOService<Object>> {\r\n\r\n\tprotected static final String FORCE_REDIRECT_TO_KEY = \"force-redirect-to\";\r\n\tprivate static final Element FEATURE_PIPELINING = new Element(\"pipelining \", new String[]{\"xmlns\"},\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{\"urn:xmpp:features:pipelining\"});\r\n\tprivate static final Logger log = Logger.getLogger(ClientConnectionManager.class.getName());\r\n\tprivate static final String ROUTING_ENTRY_PROP_KEY = \".+\";\r\n\tprivate static final String ROUTING_MODE_PROP_KEY = \"multi-mode\";\r\n\tprivate static final String ROUTINGS_PROP_KEY = \"routings\";\r\n\tprivate static final long SOCKET_CLOSE_WAIT_PROP_DEF = 5;\r\n\tprivate static final String SOCKET_CLOSE_WAIT_PROP_KEY = \"socket-close-wait\";\r\n\tprivate static final String TLS_WANT_CLIENT_AUTH_ENABLED_KEY = \"tls-want-client-auth-enabled\";\r\n\tprivate static final String XMLNS = \"jabber:client\";\r\n\tprivate static final boolean TLS_WANT_CLIENT_AUTH_ENABLED_DEF = false;\r\n\tprivate static final boolean ROUTING_MODE_PROP_VAL = true;\r\n\r\n\tprivate final ShutdownTask shutdownTask = new ShutdownTask();\r\n\tprivate final ReceiverTimeoutHandler startedHandler = newStartedHandler();\r\n\t//private final Map<String, XMPPProcessorIfc> processors = new ConcurrentHashMap<String,\r\n//    XMPPProcessorIfc>();\r\n\tprivate final ReceiverTimeoutHandler stoppedHandler = newStoppedHandler();\r\n\t@Inject\r\n\tprotected EventBus eventBus;\r\n\t@Inject\r\n\tprotected RoutingsContainer.RoutingComputer routings = null;\r\n\t@Inject(nullAllowed = true)\r\n\tprotected SeeOtherHostIfc see_other_host_strategy = null;\r\n\t@Inject\r\n\tprivate ClientTrustManagerFactory clientTrustManagerFactory;\r\n\t@ConfigField(desc = \"Support for pipelining\")\r\n\tprivate boolean pipelining = false;\r\n\t/**\r\n\t * This is mostly for testing purpose. We want to investigate massive (10k per node) connections drops at the same\r\n\t * time during tests with Tsung. I suspect this might be due to problems with one of the tsung VMs working in the\r\n\t * cluster generating load. If I am right then all disconnects should come from only one or just a few machines. If\r\n\t * I am not right disconnects should be distributed evenly among all Tsung IPs.\r\n\t */\r\n\tprivate IPMonitor ipMonitor = new IPMonitor();\r\n\t@Inject(bean = RegistrationThrottling.ID, nullAllowed = true)\r\n\tprivate RegistrationThrottling registrationThrottling;\r\n\tprivate long socket_close_wait_time = SOCKET_CLOSE_WAIT_PROP_DEF;\r\n\r\n\tpublic ClientConnectionManager() {\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int hashCodeForPacket(Packet packet) {\r\n\t\tif ((packet.getPacketFrom() != null) &&\r\n\t\t\t\tgetComponentId().getBareJID().equals(packet.getPacketFrom().getBareJID())) {\r\n\t\t\treturn packet.getPacketFrom().hashCode();\r\n\t\t} else {\r\n\t\t\treturn packet.getTo().hashCode();\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void processPacket(final Packet packet) {\r\n\t\tif (log.isLoggable(Level.FINEST)) {\r\n\t\t\tlog.log(Level.FINEST, \"Processing packet: {0}\", packet.toStringSecure());\r\n\t\t}\r\n\t\tif (packet.isCommand() && (packet.getCommand() != Command.OTHER)) {\r\n\t\t\tprocessCommand(packet);\r\n\t\t} else {\r\n\t\t\tif (!writePacketToSocket(packet)) {\r\n\r\n\t\t\t\t// Connection closed or broken, send message back to the SM\r\n\t\t\t\t// if this is not IQ result...\r\n\t\t\t\t// Ignore also all presence packets with available, unavailble\r\n\t\t\t\tprocessUndeliveredPacket(packet, null, \"The user connection is no longer active.\");\r\n\r\n\t\t\t\t// In case the SessionManager lost synchronization for any\r\n\t\t\t\t// reason, let's\r\n\t\t\t\t// notify it that the user connection no longer exists.\r\n\t\t\t\t// But in case of mass-disconnects we might have lot's of\r\n\t\t\t\t// presences\r\n\t\t\t\t// floating around, so just skip sending stream_close for all\r\n\t\t\t\t// the\r\n\t\t\t\t// offline presences\r\n\t\t\t\tif ((packet.getType() != StanzaType.unavailable) && (packet.getPacketFrom() != null)) {\r\n\t\t\t\t\tif (packet.getStanzaTo() != null) {\r\n\t\t\t\t\t\tPacket command = Command.STREAM_CLOSED_UPDATE.getPacket(packet.getStanzaTo(),\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpacket.getPacketFrom(), StanzaType.set,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tUUID.randomUUID().toString());\r\n\r\n\t\t\t\t\t\tcommand.setPacketFrom(packet.getPacketTo());\r\n\t\t\t\t\t\tcommand.setPacketTo(packet.getPacketFrom());\r\n\r\n\t\t\t\t\t\t// Note! we don't want to receive response to this\r\n\t\t\t\t\t\t// request,\r\n\t\t\t\t\t\t// thus STREAM_CLOSED_UPDATE instead of STREAM_CLOSED\r\n\t\t\t\t\t\taddOutPacket(command);\r\n\r\n\t\t\t\t\t\t// addOutPacketWithTimeout(command, stoppedHandler, 15l,\r\n\t\t\t\t\t\t// TimeUnit.SECONDS);\r\n\t\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\r\n\t\t\t\t\t\t\tlog.log(Level.FINE,\r\n\t\t\t\t\t\t\t\t\t\"Sending a command to close the remote session for non-existen {0} connection: {1}\",\r\n\t\t\t\t\t\t\t\t\tnew Object[]{getName(), command.toStringSecure()});\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\tif (log.isLoggable(Level.WARNING)) {\r\n\t\t\t\t\t\t\tlog.log(Level.FINE, \"Stream close update without an user JID, skipping for packet: {0}\",\r\n\t\t\t\t\t\t\t\t\tnew Object[]{packet});\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}    // end of else\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Queue<Packet> processSocketData(XMPPIOService<Object> serv) {\r\n\r\n\t\t// String id = getUniqueId(serv);\r\n\t\tJID id = serv.getConnectionId();\r\n\r\n\t\t// String hostname =\r\n\t\t// (String)serv.getSessionData().get(serv.HOSTNAME_KEY);\r\n\t\tPacket p = null;\r\n\r\n\t\twhile ((p = serv.getReceivedPackets().poll()) != null) {\r\n\t\t\tif (log.isLoggable(Level.FINEST)) {\r\n\t\t\t\tlog.log(Level.FINEST, \"Processing socket data: {0} from connection: {1}\",\r\n\t\t\t\t\t\tnew Object[]{p.toStringSecure(), id});\r\n\t\t\t}\r\n\r\n\t\t\t// Sometimes xmlns is not set for the packet. Usually it does not\r\n\t\t\t// cause any problems but when the packet is sent over the s2s, ext\r\n\t\t\t// or cluster connection it may be quite problematic.\r\n\t\t\t// Let's force jabber:client xmlns for all packets received from c2s\r\n\t\t\t// connection\r\n\t\t\t// Ups, some packets like starttls or sasl-auth have own XMLNS,\r\n\t\t\t// overwriting it here is not really a good idea. We have to check\r\n\t\t\t// first\r\n\t\t\t// if the xmlns is not set and then force it to jabber:client\r\n\t\t\tif (p.getAttributeStaticStr(Packet.XMLNS_ATT) == null) {\r\n\t\t\t\tp.setXMLNS(XMLNS);\r\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\r\n\t\t\t\t\tlog.log(Level.FINEST, \"XMLNS set for packet: {0} from connection: {1}\",\r\n\t\t\t\t\t\t\tnew Object[]{p.toStringSecure(), id});\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// If client is sending packet with 'from' attribute set then packets\r\n\t\t\t// are being duplicated in clustered environment, so best it would be\r\n\t\t\t// to remove 'from' attribute as it will be set later during processing\r\n\t\t\t// by SessionManager\r\n\t\t\tif (p.getStanzaFrom() != null) {\r\n\t\t\t\tp.initVars(null, p.getStanzaTo());\r\n\t\t\t}\r\n\t\t\tif (serv.getAuthorisedUserJid().isPresent()) {\r\n\t\t\t\tp.setServerAuthorisedStanzaFrom(serv.getAuthorisedUserJid().get());\r\n\r\n\t\t\t\t// In the future, in version 9.0.0, after resolving issue with cluster packet duplication,\r\n\t\t\t\t// we should stamp stanza directly\r\n\t\t\t}\r\n\r\n\t\t\t// p.setPacketFrom(getFromAddress(id));\r\n\t\t\tp.setPacketFrom(id);\r\n\r\n\t\t\tJID receiver = serv.getDataReceiver();\r\n\r\n\t\t\tif (receiver != null) {\r\n\t\t\t\tp.setPacketTo(serv.getDataReceiver());\r\n\t\t\t\taddOutPacket(p);\r\n\t\t\t} else {\r\n\r\n\t\t\t\t// Hm, receiver is not set yet..., ignoring\r\n\t\t\t\tif (log.isLoggable(Level.FINE)) {\r\n\t\t\t\t\tlog.log(Level.FINE,\r\n\t\t\t\t\t\t\t\"Hm, receiver is not set yet stream open was not send by a client or server misconfiguration..., ignoring: {0}, connection: {1}\",\r\n\t\t\t\t\t\t\tnew Object[]{p.toStringSecure(), serv});\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// TODO: Implement sending 'req' attributes by the server too\r\n\t\t}    // end of while ()\r\n\r\n\t\treturn null;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean processUndeliveredPacket(Packet packet, Long stamp, String errorMessage) {\r\n\t\ttry {\r\n\t\t\t// is there a point in trying to redeliver stanza of type error?\r\n\t\t\tif (packet.getType() == StanzaType.error || packet.getType() == StanzaType.result) {\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\r\n\t\t\t// we should not send errors for presences as Presence module does not\r\n\t\t\t// allow to send presence with type error from users and presences\r\n\t\t\t// with type error resulting from presences sent to barejid are\r\n\t\t\t// messing up a lot on client side. moreover presences with type\r\n\t\t\t// unavailable will be send by Presence plugin from SessionManager\r\n\t\t\t// when session will be closed just after sending this errors\r\n\t\t\tif (packet.getElemName() == Presence.ELEM_NAME) {\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\r\n\t\t\tif (packet.getElemName() == Message.ELEM_NAME) {\r\n\t\t\t\t// we should mark this message packet so that SM will know that it is\r\n\t\t\t\t// resent from here due to connection failure\r\n\t\t\t\tPacket result = C2SDeliveryErrorProcessor.makeDeliveryError(packet, stamp);\r\n\r\n\t\t\t\tprocessOutPacket(result);\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\r\n\t\t\tprocessOutPacket(Authorization.RECIPIENT_UNAVAILABLE.getResponseMessage(packet, errorMessage, true));\r\n\t\t} catch (PacketErrorTypeException ex) {\r\n\t\t\tlog.log(Level.FINER, \"exception preparing request for returning error, data = {0}\", packet);\r\n\t\t}\r\n\t\treturn true;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void reconnectionFailed(Map<String, Object> port_props) {\r\n\t}\r\n\r\n//\t@Override\r\n//\tpublic void register(Kernel kernel) {\r\n//\t\tsuper.register(kernel);\r\n//\t\tkernel.registerBean(\"seeOtherHost\").asClass(SeeOtherHost.class).exec();;\r\n//\t}\r\n\r\n\t@Override\r\n\tpublic void serviceStarted(XMPPIOService<Object> service) {\r\n\t\tsuper.serviceStarted(service);\r\n\r\n\t\tString id = getUniqueId(service);\r\n\t\tJID connectionId = getFromAddress(id);\r\n\r\n\t\tservice.setConnectionId(connectionId);\r\n\t\tservice.setProcessors(processors);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean serviceStopped(XMPPIOService<Object> service) {\r\n\t\tboolean result = super.serviceStopped(service);\r\n\r\n\t\txmppStreamClosed(service);\r\n\r\n\t\treturn result;\r\n\t}\r\n\r\n\tpublic void setRegistrationThrottling(RegistrationThrottling throttling) {\r\n\t\tif (registrationThrottling != null) {\r\n\t\t\tthis.registrationThrottling.stopFor(kernel);\r\n\t\t}\r\n\t\tif (throttling != null) {\r\n\t\t\tthrottling.startFor(kernel);\r\n\t\t}\r\n\t\tthis.registrationThrottling = throttling;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void start() {\r\n\t\tsuper.start();\r\n\t\tipMonitor = new IPMonitor();\r\n\t\tipMonitor.start();\r\n\t\teventBus.registerAll(this);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void stop() {\r\n\t\teventBus.unregisterAll(this);\r\n\t\tsuper.stop();\r\n\t\tipMonitor.stopThread();\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void tlsHandshakeCompleted(XMPPIOService<Object> serv) {\r\n\t\tserv.getSessionData().put(\"SEND_TLS_COMPLETED\", true);\r\n\t}\r\n\r\n\t@Override\r\n\tprotected boolean shouldRedeliverWaitingPackets(XMPPIOService<Object> service) {\r\n\t\treturn super.shouldRedeliverWaitingPackets(service) && service.getSessionData().containsKey(\"stream-closed\");\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void xmppStreamClosed(XMPPIOService<Object> serv) {\r\n\t\tif (log.isLoggable(Level.FINER)) {\r\n\t\t\tlog.log(Level.FINER, \"Stream closed: {0}\", serv.getConnectionId());\r\n\t\t}\r\n\r\n\t\t// It might be a Bosh service in which case it is ignored here.\r\n\t\t// The method may be called more than one time for a single\r\n\t\t// connection but we want to send a notification just once\r\n\t\tif ((serv.getXMLNS() == XMLNS) && (serv.getSessionData().get(\"stream-closed\") == null)) {\r\n\t\t\tserv.getSessionData().put(\"stream-closed\", \"stream-closed\");\r\n\t\t\tipMonitor.addDisconnect(serv.getRemoteAddress());\r\n\t\t\tif (serv.getDataReceiver() != null) {\r\n\t\t\t\tPacket command = Command.STREAM_CLOSED.getPacket(serv.getConnectionId(), serv.getDataReceiver(),\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t StanzaType.set, UUID.randomUUID().toString());\r\n\t\t\t\tString userJid = serv.getUserJid();\r\n\r\n\t\t\t\tif (userJid != null) {\r\n\t\t\t\t\tCommand.addFieldValue(command, \"user-jid\", userJid);\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// In case of mass-disconnects, adjust the timeout properly\r\n\t\t\t\taddOutPacketWithTimeout(command, stoppedHandler, 120l, TimeUnit.SECONDS);\r\n\t\t\t\tlog.log(Level.FINE, \"Service stopped, sending packet: {0}\", command);\r\n\r\n\t\t\t\t//// For testing only.\r\n\t\t\t\t// System.out.println(\"Service stopped: \" +\r\n\t\t\t\t// service.getUniqueId());\r\n\t\t\t\t// Thread.dumpStack();\r\n\t\t\t\t//// For testing only.\r\n\t\t\t\t// System.out.println(\"Service stopped: \" +\r\n\t\t\t\t// service.getUniqueId());\r\n\t\t\t\t// Thread.dumpStack();\r\n\t\t\t\tprocessSocketData(serv);\r\n\r\n\t\t\t\tif (userJid != null) {\r\n\t\t\t\t\tcommand = Command.STREAM_FINISHED.getPacket(serv.getConnectionId(), serv.getDataReceiver(),\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tStanzaType.set, UUID.randomUUID().toString());\r\n\t\t\t\t\taddOutPacket(command);\r\n\t\t\t\t}\r\n\t\t\t\tredeliverWaitingPackets(serv);\r\n\t\t\t} else {\r\n\t\t\t\tlog.fine(\"Service stopped, before stream:stream received\");\r\n\t\t\t}\r\n\t\t\tserv.stop();\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String[] xmppStreamOpened(XMPPIOService<Object> serv, Map<String, String> attribs) {\r\n\t\tif (log.isLoggable(Level.FINER)) {\r\n\t\t\tlog.log(Level.FINER, \"Stream opened: {0}\", attribs);\r\n\t\t}\r\n\r\n\t\tString lang = attribs.get(\"xml:lang\");\r\n\t\tfinal String hostname = attribs.get(\"to\");\r\n\t\tfinal String from = attribs.get(\"from\");\r\n\t\tBareJID fromJID = null;\r\n\r\n\t\tif (from != null) {\r\n\t\t\ttry {\r\n\t\t\t\tfromJID = BareJID.bareJIDInstance(from);\r\n\t\t\t} catch (TigaseStringprepException ex) {\r\n\t\t\t\tlog.log(Level.CONFIG, \"From JID violates RFC6122 (XMPP:Address Format): \", ex);\r\n\r\n\t\t\t\treturn prepareStreamError(serv, StreamError.ImproperAddressing, null);\r\n\t\t\t}    // end of: try-catch\r\n\t\t}      // end of: if (from != null) {\r\n\t\tif (lang == null) {\r\n\t\t\tlang = \"en\";\r\n\t\t}\r\n\t\tif (hostname == null) {\r\n\t\t\treturn prepareStreamError(serv, StreamError.ImproperAddressing, null);\r\n\t\t}    // end of if (hostname == null)\r\n\t\tif (!isLocalDomain(hostname)) {\r\n\t\t\treturn prepareStreamError(serv, StreamError.HostUnknown, hostname);\r\n\t\t}    // end of if (!hostnames.contains(hostname))\r\n\t\tif (!isAllowed(serv, hostname)) {\r\n\t\t\treturn prepareStreamError(serv, StreamError.PolicyViolation, hostname);\r\n\t\t}\r\n\t\tInteger redirect_port = (Integer) serv.getSessionData().get(FORCE_REDIRECT_TO_KEY);\r\n\r\n\t\tif ((fromJID != null) && (see_other_host_strategy != null) &&\r\n\t\t\t\tsee_other_host_strategy.isEnabled(vHostManager.getVHostItem(fromJID.getDomain()),\r\n\t\t\t\t\t\t\t\t\t\t\t\t  SeeOtherHostIfc.Phase.OPEN)) {\r\n\t\t\tBareJID see_other_host = see_other_host_strategy.findHostForJID(fromJID, getDefHostName());\r\n\r\n\t\t\tif ((see_other_host != null) && (redirect_port != null ||\r\n\t\t\t\t\tsee_other_host_strategy.isRedirectionRequired(getDefHostName(), see_other_host))) {\r\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\r\n\t\t\t\t\tlog.log(Level.FINEST, \"Sending redirect for {0} to host {1}, connection {2}.\",\r\n\t\t\t\t\t\t\tnew Object[]{fromJID, see_other_host, serv});\r\n\t\t\t\t}\r\n\r\n\t\t\t\treturn prepareSeeOtherHost(serv, fromJID.getDomain(), see_other_host);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tString id = (String) serv.getSessionData().get(IOService.SESSION_ID_KEY);\r\n\r\n\t\tif (id == null) {\r\n\t\t\tid = UUID.randomUUID().toString();\r\n\t\t\tif (log.isLoggable(Level.FINER)) {\r\n\t\t\t\tlog.log(Level.FINER, \"No Session ID, generating a new one: {0}\", id);\r\n\t\t\t}\r\n\t\t\tserv.getSessionData().put(IOService.SESSION_ID_KEY, id);\r\n\t\t\tserv.setXMLNS(XMLNS);\r\n\t\t\tserv.getSessionData().put(IOService.HOSTNAME_KEY, hostname);\r\n\t\t\tserv.setDataReceiver(JID.jidInstanceNS(routings.computeRouting(hostname)));\r\n\r\n\t\t\tString streamOpenData = prepareStreamOpen(serv, id, hostname, from);\r\n\r\n\t\t\tif (log.isLoggable(Level.FINER)) {\r\n\t\t\t\tlog.log(Level.FINER, \"Writing raw data to the socket: {0}\", streamOpenData);\r\n\t\t\t}\r\n\t\t\twriteRawData(serv, streamOpenData);\r\n\t\t\tif (log.isLoggable(Level.FINER)) {\r\n\t\t\t\tlog.log(Level.FINER, \"DONE\");\r\n\t\t\t}\r\n\r\n\t\t\tPacket streamOpen = Command.STREAM_OPENED.getPacket(serv.getConnectionId(), serv.getDataReceiver(),\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tStanzaType.set, this.newPacketId(\"c2s-\"),\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCommand.DataType.submit);\r\n\r\n\t\t\tCommand.addFieldValue(streamOpen, \"session-id\", id);\r\n\t\t\tCommand.addFieldValue(streamOpen, \"hostname\", hostname);\r\n\t\t\tCommand.addFieldValue(streamOpen, \"xml:lang\", lang);\r\n\t\t\tif (fromJID != null) {\r\n\t\t\t\tCommand.addFieldValue(streamOpen, \"from\", fromJID.toString());\r\n\t\t\t}\r\n\t\t\tif (log.isLoggable(Level.FINER)) {\r\n\t\t\t\tlog.log(Level.FINER, \"Sending a system command to SM: {0}\", streamOpen);\r\n\t\t\t}\r\n\t\t\tif (serv instanceof C2SIOService) {\r\n\t\t\t\t((C2SIOService) serv).waitForResponse();\r\n\t\t\t}\r\n\t\t\taddOutPacketWithTimeout(streamOpen, startedHandler, 45l, TimeUnit.SECONDS);\r\n\r\n\t\t\tserviceConnected(serv);\r\n\t\t\tlog.log(Level.FINER, \"DONE 2\");\r\n\t\t} else {\r\n\t\t\tif (log.isLoggable(Level.FINER)) {\r\n\t\t\t\tlog.log(Level.FINER, \"Session ID is: {0}\", id);\r\n\t\t\t}\r\n\r\n\t\t\tif (serv instanceof C2SIOService && ((C2SIOService) serv).shouldQueueStreamOpened()) {\r\n\t\t\t\tfinal String localId = id;\r\n\t\t\t\t((C2SIOService) serv).queueTask(() -> {\r\n\t\t\t\t\twriteRawData(serv, prepareStreamOpen(serv, localId, hostname, from));\r\n\t\t\t\t\tfinal SocketType socket = (SocketType) serv.getSessionData().get(\"socket\");\r\n\t\t\t\t\tboolean ssl = socket.equals(SocketType.ssl);\r\n\t\t\t\t\t((C2SIOService) serv).waitForResponse();\r\n\t\t\t\t\tif ((Boolean) serv.getSessionData().getOrDefault(\"SEND_TLS_COMPLETED\", false)) {\r\n\t\t\t\t\t\tClientConnectionManager.this.sendTlsHandshakeCompletedToSessionManager(serv);\r\n\t\t\t\t\t}\r\n\t\t\t\t\taddOutPacket(Command.GETFEATURES.getPacket(serv.getConnectionId(), serv.getDataReceiver(), StanzaType.get, (ssl ? \"ssl_\" : \"\") + UUID.randomUUID().toString(), null));\r\n\t\t\t\t});\r\n\t\t\t} else {\r\n\t\t\t\twriteRawData(serv, prepareStreamOpen(serv, id, hostname, from));\r\n\r\n\t\t\t\tfinal SocketType socket = (SocketType) serv.getSessionData().get(\"socket\");\r\n\t\t\t\tboolean ssl = socket.equals(SocketType.ssl);\r\n\t\t\t\tif (serv instanceof C2SIOService) {\r\n\t\t\t\t\t((C2SIOService) serv).waitForResponse();\r\n\t\t\t\t}\r\n\t\t\t\tif ((Boolean) serv.getSessionData().getOrDefault(\"SEND_TLS_COMPLETED\", false)) {\r\n\t\t\t\t\tClientConnectionManager.this.sendTlsHandshakeCompletedToSessionManager(serv);\r\n\t\t\t\t}\r\n\t\t\t\taddOutPacket(Command.GETFEATURES.getPacket(serv.getConnectionId(), serv.getDataReceiver(), StanzaType.get,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   (ssl ? \"ssl_\" : \"\") + UUID.randomUUID().toString(), null));\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn null;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String xmppStreamError(XMPPIOService<Object> serv, List<Element> err_el) {\r\n\t\treturn prepareStreamError(serv, err_el);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getDiscoCategoryType() {\r\n\t\treturn \"c2s\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getDiscoDescription() {\r\n\t\treturn \"Client connection manager\";\r\n\t}\r\n\r\n\t/**\r\n\t * Method retrieves object of particular class implementing {@link SeeOtherHostIfc}\r\n\t *\r\n\t * @param see_other_host_class class of {@link SeeOtherHostIfc} implementation\r\n\t *\r\n\t * @return a value of <code>SeeOtherHostIfc</code>\r\n\t */\r\n\tpublic SeeOtherHostIfc getSeeOtherHostInstance(String see_other_host_class) {\r\n\t\tif (log.isLoggable(Level.FINEST)) {\r\n\t\t\tlog.finest(\"Configuring see_other_host strategy for: \" + see_other_host_class);\r\n\t\t}\r\n\t\tif (see_other_host_class == null) {\r\n\t\t\tsee_other_host_class = SeeOtherHostIfc.CM_SEE_OTHER_HOST_CLASS_PROP_DEF_VAL;\r\n\t\t}\r\n\t\tif (see_other_host_class.equals(\"none\")) {\r\n\t\t\treturn null;\r\n\t\t}\r\n\t\ttry {\r\n\t\t\tsee_other_host_strategy = (SeeOtherHostIfc) Class.forName(see_other_host_class).newInstance();\r\n\t\t\tsetSee_other_host_strategy(see_other_host_strategy);\r\n\t\t} catch (Exception e) {\r\n\t\t\tlog.log(Level.SEVERE, \"Can not instantiate see_other_host strategy for class: \" + see_other_host_class, e);\r\n\t\t}\r\n\r\n\t\treturn see_other_host_strategy;\r\n\t}\r\n\r\n\tpublic void setSee_other_host_strategy(SeeOtherHostIfc see_other_host_strategy) {\r\n\t\tif (see_other_host_strategy != null) {\r\n\t\t\tthis.see_other_host_strategy = see_other_host_strategy;\r\n\t\t\tsee_other_host_strategy.setNodes(getNodesConnectedWithLocal());\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tpublic int schedulerThreads() {\r\n\t\treturn 2;\r\n\t}\r\n\r\n\tpublic ClientTrustManagerFactory getClientTrustManagerFactory() {\r\n\t\treturn clientTrustManagerFactory;\r\n\t}\r\n\r\n\tprotected JID changeDataReceiver(Packet packet, JID newAddress, String command_sessionId,\r\n\t\t\t\t\t\t\t\t\t XMPPIOService<Object> serv) {\r\n\t\tif (serv != null) {\r\n\t\t\tString serv_sessionId = (String) serv.getSessionData().get(IOService.SESSION_ID_KEY);\r\n\r\n\t\t\tif (serv_sessionId.equals(command_sessionId)) {\r\n\t\t\t\tJID old_receiver = serv.getDataReceiver();\r\n\r\n\t\t\t\tserv.setDataReceiver(newAddress);\r\n\r\n\t\t\t\treturn old_receiver;\r\n\t\t\t} else {\r\n\t\t\t\tlog.log(Level.WARNING,\r\n\t\t\t\t\t\t\"Incorrect session ID, ignoring data redirect for: {0}, expected: {1}, received: {2}\",\r\n\t\t\t\t\t\tnew Object[]{newAddress, serv_sessionId, command_sessionId});\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn null;\r\n\t}\r\n\r\n\tprotected boolean isAllowed(XMPPIOService<Object> serv, String hostname) {\r\n\t\tVHostItem vhost = this.vHostManager.getVHostItem(hostname);\r\n\t\tif (vhost != null) {\r\n\t\t\tint[] allowedPorts = vhost.getC2SPortsAllowed();\r\n\t\t\tif (allowedPorts != null && Arrays.binarySearch(allowedPorts, serv.getLocalPort()) < 0) {\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn true;\r\n\t}\r\n\r\n\tprotected ReceiverTimeoutHandler newStartedHandler() {\r\n\t\treturn new StartedHandler();\r\n\t}\r\n\r\n\tprotected ReceiverTimeoutHandler newStoppedHandler() {\r\n\t\treturn new StoppedHandler();\r\n\t}\r\n\r\n\t@HandleEvent\r\n\tprotected void nodeShutdown(ShutdownEvent event) {\r\n\t\tif (event.getNode() == null || !getComponentId().getDomain().equals(event.getNode())) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\taddTimerTask(shutdownTask, event.getDelay() * SECOND);\r\n\t}\r\n\r\n\tprotected void processCommand(Packet packet) {\r\n\t\tXMPPIOService<Object> serv = getXMPPIOService(packet);\r\n\t\tIq iqc = (Iq) packet;\r\n\r\n\t\tswitch (iqc.getCommand()) {\r\n\t\t\tcase GETFEATURES:\r\n\t\t\t\tif (iqc.getType() == StanzaType.result) {\r\n\t\t\t\t\tList<Element> features = getFeatures(serv);\r\n\t\t\t\t\tElement elem_features = new Element(\"stream:features\");\r\n\r\n\t\t\t\t\telem_features.addChildren(features);\r\n\t\t\t\t\telem_features.addChildren(Command.getData(iqc));\r\n\r\n\t\t\t\t\tpreprocessStreamFeatures(serv, elem_features);\r\n\r\n\t\t\t\t\tPacket result = Packet.packetInstance(elem_features, null, null);\r\n\r\n\t\t\t\t\t// Is it actually needed?? Yes, it is needed, IOService is\r\n\t\t\t\t\t// looked up based on this.\r\n\t\t\t\t\tresult.setPacketTo(iqc.getTo());\r\n\t\t\t\t\twritePacketToSocket(result);\r\n\t\t\t\t}    // end of if (packet.getType() == StanzaType.get)\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase USER_LOGIN:\r\n\t\t\t\tString jid = Command.getFieldValue(iqc, \"user-jid\");\r\n\r\n\t\t\t\tif (jid != null) {\r\n\t\t\t\t\tif (serv != null) {\r\n\t\t\t\t\t\tsuper.serviceConnected(serv);\r\n\t\t\t\t\t\tBareJID fromJID = null;\r\n\r\n\t\t\t\t\t\ttry {\r\n\t\t\t\t\t\t\tfromJID = BareJID.bareJIDInstance(jid);\r\n\t\t\t\t\t\t} catch (TigaseStringprepException ex) {\r\n\t\t\t\t\t\t\tlog.log(Level.SEVERE, null, ex);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tif ((fromJID != null) && ((see_other_host_strategy != null) &&\r\n\t\t\t\t\t\t\t\tsee_other_host_strategy.isEnabled(vHostManager.getVHostItem(fromJID.getDomain()),\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  SeeOtherHostIfc.Phase.LOGIN))) {\r\n\t\t\t\t\t\t\tBareJID see_other_host = see_other_host_strategy.findHostForJID(fromJID, getDefHostName());\r\n\r\n\t\t\t\t\t\t\tInteger redirect_port = (Integer) serv.getSessionData().get(FORCE_REDIRECT_TO_KEY);\r\n\r\n\t\t\t\t\t\t\tif ((see_other_host != null) && (redirect_port != null ||\r\n\t\t\t\t\t\t\t\t\tsee_other_host_strategy.isRedirectionRequired(getDefHostName(), see_other_host))) {\r\n\t\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\r\n\t\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Sending redirect for {0} to host {1}, connection {2}.\",\r\n\t\t\t\t\t\t\t\t\t\t\tnew Object[]{fromJID, see_other_host, serv});\r\n\t\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\t\tString[] redirectMessages = prepareSeeOtherHost(serv, fromJID.getDomain(), see_other_host);\r\n\r\n\t\t\t\t\t\t\t\ttry {\r\n\t\t\t\t\t\t\t\t\tSocketThread.removeSocketService(serv);\r\n\t\t\t\t\t\t\t\t\tfor (String redirectMessage : redirectMessages) {\r\n\t\t\t\t\t\t\t\t\t\tserv.writeRawData(redirectMessage);\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\tserv.processWaitingPackets();\r\n\t\t\t\t\t\t\t\t\tThread.sleep(socket_close_wait_time);\r\n\t\t\t\t\t\t\t\t\tserv.stop();\r\n\t\t\t\t\t\t\t\t} catch (Exception e) {\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\t\tserv.setUserJid(jid);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\tserv.setUserJid(jid);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\r\n\t\t\t\t\t\t\tlog.log(Level.FINE, \"Missing XMPPIOService for USER_LOGIN command: {0}\", iqc);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t} else {\r\n\t\t\t\t\tlog.log(Level.WARNING, \"Missing user-jid for USER_LOGIN command: {0}\", iqc);\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase STARTZLIB:\r\n\t\t\t\tif (serv != null) {\r\n\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\r\n\t\t\t\t\t\tlog.log(Level.FINER, \"Starting zlib compression: {0}\", serv);\r\n\t\t\t\t\t}\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tElement compressed = Command.getData(iqc, \"compressed\", null);\r\n\t\t\t\t\t\tPacket p_compressed = Packet.packetInstance(compressed, null, null);\r\n\r\n\t\t\t\t\t\t// SocketThread readThread = SocketThread.getInstance();\r\n\t\t\t\t\t\tSocketThread.removeSocketService(serv);\r\n\r\n\t\t\t\t\t\t// writePacketToSocket(serv, p_proceed);\r\n\t\t\t\t\t\tserv.addPacketToSend(p_compressed);\r\n\t\t\t\t\t\tserv.processWaitingPackets();\r\n\t\t\t\t\t\tserv.startZLib(Deflater.BEST_COMPRESSION);\r\n\r\n\t\t\t\t\t\t// serv.call();\r\n\t\t\t\t\t\tSocketThread.addSocketService(serv);\r\n\t\t\t\t\t} catch (IOException ex) {\r\n\t\t\t\t\t\tlog.log(Level.CONFIG, \"Problem enabling zlib compression on the connection: \", ex);\r\n\t\t\t\t\t}\r\n\t\t\t\t} else {\r\n\t\t\t\t\tlog.log(Level.CONFIG, \"Can not find service for STARTZLIB command: {0}\", iqc);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase STARTTLS:\r\n\t\t\t\tif (serv != null) {\r\n\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\r\n\t\t\t\t\t\tlog.log(Level.FINER, \"Starting TLS for connection: {0}\", serv);\r\n\t\t\t\t\t}\r\n\t\t\t\t\ttry {\r\n\r\n\t\t\t\t\t\t// Note:\r\n\t\t\t\t\t\t// If you send <proceed> packet to client you must expect\r\n\t\t\t\t\t\t// instant response from the client with TLS handshaking\r\n\t\t\t\t\t\t// data before you will call startTLS() on server side.\r\n\t\t\t\t\t\t// So the initial handshaking data might be lost as they\r\n\t\t\t\t\t\t// will be processed in another thread reading data from the\r\n\t\t\t\t\t\t// socket.\r\n\t\t\t\t\t\t// That's why below code first removes service from reading\r\n\t\t\t\t\t\t// threads pool and then sends <proceed> packet and starts\r\n\t\t\t\t\t\t// TLS.\r\n\t\t\t\t\t\tElement proceed = Command.getData(iqc, \"proceed\", null);\r\n\t\t\t\t\t\tPacket p_proceed = Packet.packetInstance(proceed, null, null);\r\n\r\n\t\t\t\t\t\t// SocketThread readThread = SocketThread.getInstance();\r\n\t\t\t\t\t\tSocketThread.removeSocketService(serv);\r\n\r\n\t\t\t\t\t\tString hostname = (String) serv.getSessionData().get(IOService.HOSTNAME_KEY);\r\n\t\t\t\t\t\tVHostItem vhost = getVHostItem(hostname);\r\n\r\n\t\t\t\t\t\tTrustManager[] trustManagers = clientTrustManagerFactory.getManager(vhost);\r\n\t\t\t\t\t\tboolean wantClientAuth = clientTrustManagerFactory.isTlsWantClientAuthEnabled(vhost);\r\n\t\t\t\t\t\tboolean needClientAuth = clientTrustManagerFactory.isTlsNeedClientAuthEnabled(vhost);\r\n\r\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\r\n\t\t\t\t\t\t\tlog.log(Level.FINEST,\r\n\t\t\t\t\t\t\t\t\t\"TLS: wantClientAuth={0}, needClientAuth={1}, trustManagers={2}; for connection {3}\",\r\n\t\t\t\t\t\t\t\t\tnew Object[]{wantClientAuth, needClientAuth,\r\n\t\t\t\t\t\t\t\t\t\t\t\t (trustManagers != null ? Arrays.asList(trustManagers) : \"null\"), serv});\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tserv.setX509TrustManagers(trustManagers);\r\n\r\n\t\t\t\t\t\tserv.addPacketToSend(p_proceed);\r\n\t\t\t\t\t\tserv.processWaitingPackets();\r\n\r\n\t\t\t\t\t\tserv.startTLS(false, wantClientAuth, needClientAuth);\r\n\t\t\t\t\t\tSocketThread.addSocketService(serv);\r\n\t\t\t\t\t} catch (IllegalStateException | IOException e) {\r\n\t\t\t\t\t\tlog.log(Level.FINE, \"Error starting TLS: \" + e.getMessage(), e);\r\n\t\t\t\t\t\tserv.forceStop();\r\n\t\t\t\t\t}    // end of try-catch\r\n\t\t\t\t} else {\r\n\t\t\t\t\tlog.log(Level.CONFIG, \"Can not find service for STARTTLS command: {0}\", iqc);\r\n\t\t\t\t}      // end of else\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase REDIRECT:\r\n\t\t\t\tString command_sessionId = Command.getFieldValue(iqc, \"session-id\");\r\n\t\t\t\tJID newAddress = iqc.getFrom();\r\n\t\t\t\tJID old_receiver = changeDataReceiver(iqc, newAddress, command_sessionId, serv);\r\n\r\n\t\t\t\tif (old_receiver != null) {\r\n\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\r\n\t\t\t\t\t\tlog.log(Level.FINE, \"Redirecting data for sessionId: {0}, to: {1}\",\r\n\t\t\t\t\t\t\t\tnew Object[]{command_sessionId, newAddress});\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tPacket response = null;\r\n\r\n\t\t\t\t\tresponse = iqc.commandResult(null);\r\n\t\t\t\t\tCommand.addFieldValue(response, \"session-id\", command_sessionId);\r\n\t\t\t\t\tCommand.addFieldValue(response, \"action\", \"activate\");\r\n\t\t\t\t\tresponse.getElement().setAttribute(\"to\", newAddress.toString());\r\n\t\t\t\t\taddOutPacket(response);\r\n\t\t\t\t} else {\r\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\r\n\t\t\t\t\t\tlog.log(Level.FINEST,\r\n\t\t\t\t\t\t\t\t\"Connection for REDIRECT command does not exist, ignoring \" + \"packet: \" + \"{0}\",\r\n\t\t\t\t\t\t\t\tiqc.toStringSecure());\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase STREAM_CLOSED:\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase GETDISCO:\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase CLOSE:\r\n\t\t\t\tif (serv != null) {\r\n\t\t\t\t\tString streamClose = prepareStreamClose(serv);\r\n\t\t\t\t\tList<Element> err_el = packet.getElement().getChildrenStaticStr(Iq.IQ_COMMAND_PATH);\r\n\t\t\t\t\tboolean moreToSend = false;\r\n\r\n\t\t\t\t\tif ((err_el != null) && (err_el.size() > 0)) {\r\n\t\t\t\t\t\tstreamClose = prepareStreamError(serv, err_el) + streamClose;\r\n\t\t\t\t\t\tmoreToSend = true;\r\n\t\t\t\t\t}\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\r\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Sending stream close to the client: {0}\", streamClose);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tserv.getSessionData().put(XMPPIOService.STREAM_CLOSING, true);\r\n\t\t\t\t\t\tserv.writeRawData(streamClose);\r\n\t\t\t\t\t\tif (moreToSend) {\r\n\r\n\t\t\t\t\t\t\t// This is kind of a workaround. serv.stop() is supposed\r\n\t\t\t\t\t\t\t// to wait\r\n\t\t\t\t\t\t\t// until all data are sent to the client, however, even\r\n\t\t\t\t\t\t\t// then there\r\n\t\t\t\t\t\t\t// is still a chance, that the connection is closed\r\n\t\t\t\t\t\t\t// before data\r\n\t\t\t\t\t\t\t// reached the client\r\n\t\t\t\t\t\t\tThread.sleep(socket_close_wait_time);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t} catch (Exception e) {\r\n\t\t\t\t\t}\r\n\t\t\t\t\tserv.stop();\r\n\t\t\t\t\tfor (XMPPIOProcessor processor : processors) {\r\n\t\t\t\t\t\tprocessor.serviceStopped(serv, true);\r\n\t\t\t\t\t}\r\n\t\t\t\t} else {\r\n\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\r\n\t\t\t\t\t\tlog.log(Level.FINE,\r\n\t\t\t\t\t\t\t\t\"Attempt to stop non-existen service for packet: {0}, Service already stopped?\", iqc);\r\n\t\t\t\t\t}\r\n\t\t\t\t}    // end of if (serv != null) else\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase CHECK_USER_CONNECTION:\r\n\t\t\t\tif (serv != null) {\r\n\r\n\t\t\t\t\t// It's ok, the session has been found, respond with OK.\r\n\t\t\t\t\taddOutPacket(iqc.okResult((String) null, 0));\r\n\t\t\t\t} else {\r\n\r\n\t\t\t\t\t// Session is no longer active, respond with an error.\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\taddOutPacket(Authorization.ITEM_NOT_FOUND.getResponseMessage(iqc, \"Connection gone.\", false));\r\n\t\t\t\t\t} catch (PacketErrorTypeException e) {\r\n\r\n\t\t\t\t\t\t// Hm, error already, ignoring...\r\n\t\t\t\t\t\tlog.log(Level.CONFIG, \"Error packet is not really expected here: {0}\", iqc.toStringSecure());\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase STREAM_MOVED:\r\n\t\t\t\tif (processors != null) {\r\n\t\t\t\t\tfor (XMPPIOProcessor processor : processors) {\r\n\r\n\t\t\t\t\t\t// handled |= processor.processCommand(packet);\r\n\t\t\t\t\t\tprocessor.processCommand(serv, packet);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tdefault:\r\n\t\t\t\twritePacketToSocket(iqc);\r\n\r\n\t\t\t\tbreak;\r\n\t\t}    // end of switch (pc.getCommand())\r\n\t}\r\n\r\n\t@Override\r\n\tprotected int[] getDefPlainPorts() {\r\n\t\treturn new int[]{5222};\r\n\t}\r\n\r\n\t@Override\r\n\tprotected int[] getDefSSLPorts() {\r\n\t\treturn new int[]{5223};\r\n\t}\r\n\r\n\t/**\r\n\t * {@inheritDoc}\r\n\t * <br>\r\n\t * Let's assume user should send something at least once every 24 hours....\r\n\t */\r\n\t@Override\r\n\tprotected long getMaxInactiveTime() {\r\n\t\treturn 24 * HOUR;\r\n\t}\r\n\r\n\t@Override\r\n\tprotected Integer getMaxQueueSize(int def) {\r\n\t\treturn def * 10;\r\n\t}\r\n\r\n\t@Override\r\n\tprotected XMPPIOService<Object> getXMPPIOServiceInstance() {\r\n\t\tif (pipelining) {\r\n\t\t\treturn new C2SIOService<Object>();\r\n\t\t}\r\n\t\treturn new XMPPIOService<Object>();\r\n\t}\r\n\r\n\tprotected String prepareStreamClose(XMPPIOService<Object> serv) {\r\n\t\treturn \"</stream:stream>\";\r\n\t}\r\n\r\n\t@TigaseDeprecated(note = \"Use method with 'to' parameter (pass null if not known)\", since = \"8.5.0\")\r\n\t@Deprecated\r\n\tprotected String prepareStreamOpen(XMPPIOService<Object> serv, String id, String hostname) {\r\n\t\treturn prepareStreamOpen(serv, id, hostname, null);\r\n\t}\r\n\r\n\tprotected String prepareStreamOpen(XMPPIOService<Object> serv, String id, String hostname, @Nullable String to) {\r\n\t\treturn \"<?xml version='1.0'?><stream:stream\" + \" xmlns='\" + XMLNS + \"'\" +\r\n\t\t\t\t\" xmlns:stream='http://etherx.jabber.org/streams'\" + \" from='\" + hostname + \"'\" + (to == null ? \"\" : (\" to='\" + to + \"'\")) + \" id='\" + id + \"'\" +\r\n\t\t\t\t\" version='1.0' xml:lang='en'>\";\r\n\t}\r\n\r\n\tprotected String prepareStreamError(XMPPIOService<Object> serv, List<Element> err_el) {\r\n\t\tStreamError streamError = StreamError.getByCondition(err_el.get(0).getName());\r\n\r\n\t\tfor (XMPPIOProcessor proc : processors) {\r\n\t\t\tproc.streamError(serv, streamError);\r\n\t\t}\r\n\t\treturn \"<stream:error xmlns:stream=\\\"http://etherx.jabber.org/streams\\\">\" + err_el.stream().map(Element::toString).collect(Collectors.joining()) + \"</stream:error>\";\r\n\t}\r\n\r\n\tprotected String[] prepareStreamError(XMPPIOService<Object> serv, StreamError streamError, String hostname) {\r\n\t\tfor (XMPPIOProcessor proc : processors) {\r\n\t\t\tproc.streamError(serv, streamError);\r\n\t\t}\r\n\t\treturn new String[] { \"<?xml version='1.0'?><stream:stream\" + \" xmlns='\" + XMLNS + \"'\" +\r\n\t\t\t\t\" xmlns:stream='http://etherx.jabber.org/streams'\" + \" id='tigase-error-tigase'\" + \" from='\" +\r\n\t\t\t\t(hostname != null ? hostname : getDefVHostItem()) + \"'\" + \" version='1.0' xml:lang='en'>\" +\r\n\t\t\t\t\"<stream:error>\" + \"<\" + streamError.getCondition() + \" xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>\" +\r\n\t\t\t\t\"</stream:error>\" + \"</stream:stream>\" };\r\n\t}\r\n\r\n\tprotected String[] prepareSeeOtherHost(XMPPIOService<Object> serv, String hostname, BareJID see_other_host) {\r\n\t\tfor (XMPPIOProcessor proc : processors) {\r\n\t\t\tproc.streamError(serv, StreamError.SeeOtherHost);\r\n\t\t}\r\n\r\n\t\tInteger redirect_port = (Integer) serv.getSessionData().get(FORCE_REDIRECT_TO_KEY);\r\n\r\n\t\treturn new String[] { \"<stream:stream\" + \" xmlns='\" + XMLNS + \"'\" + \" xmlns:stream='http://etherx.jabber.org/streams'\" +\r\n\t\t\t\t\" id='tigase-error-tigase'\" + \" from='\" + (hostname != null ? hostname : getDefVHostItem()) + \"'\" +\r\n\t\t\t\t\" version='1.0' xml:lang='en'>\" +\r\n\t\t\t\tsee_other_host_strategy.getStreamError(\"urn:ietf:params:xml:ns:xmpp-streams\", see_other_host,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t   redirect_port).toString() + \"</stream:stream>\" };\r\n\t}\r\n\r\n\tprotected void preprocessStreamFeatures(XMPPIOService<Object> serv, Element elem_features) {\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tprotected void socketAccepted(XMPPIOService serv, SocketType type) {\r\n\t\tif (type == SocketType.ssl) {\r\n\t\t\tClientTrustManagerFactory factory = getClientTrustManagerFactory();\r\n\t\t\tTrustManager[] x = factory.getManager((XMPPIOService<Object>) serv);\r\n\t\t\tserv.setX509TrustManagers(x);\r\n\t\t}\r\n\t}\r\n\r\n\tprivate void sendTlsHandshakeCompletedToSessionManager(XMPPIOService<Object> serv) {\r\n\t\tfinal String id = (String) serv.getSessionData().get(IOService.SESSION_ID_KEY);\r\n\r\n\t\tif (id == null) {\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tserv.getSessionData().remove(\"SEND_TLS_COMPLETED\");\r\n\r\n\t\tboolean send = false;\r\n\r\n\t\tPacket command = Command.TLS_HANDSHAKE_COMPLETE.getPacket(serv.getConnectionId(), serv.getDataReceiver(),\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  StanzaType.set, this.newPacketId(\"c2s-\"),\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  Command.DataType.submit);\r\n\r\n\t\tCommand.addFieldValue(command, \"session-id\", id);\r\n\r\n\t\tif ((serv.getLocalCertificate() != null)) {\r\n\t\t\ttry {\r\n\t\t\t\tString encodedLocalCertificate = Base64.encode(serv.getLocalCertificate().getEncoded());\r\n\t\t\t\tCommand.addFieldValue(command, \"local-certificate\", encodedLocalCertificate);\r\n\t\t\t\tsend = true;\r\n\t\t\t} catch (CertificateEncodingException e) {\r\n\t\t\t\tlog.log(Level.WARNING, \"Can''t encode certificate\", e);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif (serv.getTlsUniqueId() != null) {\r\n\t\t\tString data = Base64.encode(serv.getTlsUniqueId());\r\n\t\t\tCommand.addFieldValue(command, \"tls-unique-id\", data);\r\n\t\t\tsend = true;\r\n\t\t}\r\n\r\n\t\tif (serv.getTlsExporter() != null) {\r\n\t\t\tString data = Base64.encode(serv.getTlsExporter());\r\n\t\t\tCommand.addFieldValue(command, \"tls-exporter\", data);\r\n\t\t\tsend = true;\r\n\t\t}\r\n\r\n\t\tif ((serv.getPeerCertificate() != null)) {\r\n\t\t\ttry {\r\n\t\t\t\tString encodedPeerCertificate = Base64.encode(serv.getPeerCertificate().getEncoded());\r\n\t\t\t\tCommand.addFieldValue(command, \"peer-certificate\", encodedPeerCertificate);\r\n\t\t\t\tsend = true;\r\n\t\t\t} catch (CertificateEncodingException e) {\r\n\t\t\t\tlog.log(Level.WARNING, \"Can''t encode certificate\", e);\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (send) {\r\n\t\t\taddOutPacket(command);\r\n\t\t}\r\n\t}\r\n\r\n\tprivate List<Element> getFeatures(XMPPIOService service) {\r\n\t\tList<Element> results = new LinkedList<Element>();\r\n\r\n\t\tfor (XMPPIOProcessor proc : processors) {\r\n\t\t\tElement[] features = proc.supStreamFeatures(service);\r\n\r\n\t\t\tif (features != null) {\r\n\t\t\t\tresults.addAll(Arrays.asList(features));\r\n\t\t\t}    // end of if (features != null)\r\n\t\t}      // end of for ()\r\n\r\n\t\tif (pipelining) {\r\n\t\t\tresults.add(FEATURE_PIPELINING);\r\n\t\t}\r\n\r\n\t\treturn results;\r\n\t}\r\n\r\n\tprivate JID getFromAddress(String id) {\r\n\t\treturn JID.jidInstanceNS(getName(), getDefHostName().getDomain(), id);\r\n\t}\r\n\r\n\tprivate XMPPResourceConnection getXMPPSession(Packet p) {\r\n\t\tXMPPIOService<Object> serv = getXMPPIOService(p);\r\n\r\n\t\treturn (serv == null) ? null : (XMPPResourceConnection) serv.getSessionData().get(\"xmpp-session\");\r\n\t}\r\n\r\n\tprivate class ShutdownTask\r\n\t\t\textends TimerTask {\r\n\r\n\t\t@Override\r\n\t\tpublic void run() {\r\n\t\t\tElement shudownError = new Element(\"system-shutdown\", new String[]{\"xmlns\"},\r\n\t\t\t\t\t\t\t\t\t\t\t   new String[]{\"urn:ietf:params:xml:ns:xmpp-streams\"});\r\n\t\t\tdoForAllServices((XMPPIOService<Object> service) -> {\r\n\t\t\t\tif (service.getUserJid() == null) {\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tBareJID userJid = BareJID.bareJIDInstanceNS(service.getUserJid());\r\n\t\t\t\tBareJID seeHost = see_other_host_strategy.findHostForJID(userJid, getDefHostName());\r\n\r\n\t\t\t\tElement error = null;\r\n\t\t\t\tif (seeHost == null || seeHost.getDomain().equals(getComponentId().getDomain())) {\r\n\t\t\t\t\t// if we cannot redirect user notify that this is shutdown\r\n\t\t\t\t\terror = shudownError.clone();\r\n\t\t\t\t} else {\r\n\t\t\t\t\t// in other case send redirection\r\n\t\t\t\t\tInteger redirect_port = (Integer) service.getSessionData().get(FORCE_REDIRECT_TO_KEY);\r\n\t\t\t\t\terror = see_other_host_strategy.getStreamError(\"urn:ietf:params:xml:ns:xmpp-streams\", seeHost,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   redirect_port).getChild(\"see-other-host\");\r\n\t\t\t\t}\r\n\t\t\t\tPacket packet = Command.CLOSE.getPacket(getComponentId(), service.getConnectionId(), StanzaType.set,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"shutdown\");\r\n\t\t\t\tElement command = packet.getElement().findChild(Iq.IQ_COMMAND_PATH);\r\n\t\t\t\tcommand.addChild(error);\r\n\t\t\t\taddPacket(packet);\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t}\r\n\r\n\tprivate class StartedHandler\r\n\t\t\timplements ReceiverTimeoutHandler {\r\n\r\n\t\t@Override\r\n\t\tpublic void responseReceived(Packet packet, Packet response) {\r\n\r\n\t\t\t// We are now ready to ask for features....\r\n\t\t\tXMPPIOService<Object> serv = getXMPPIOService(response);\r\n\t\t\tif (serv != null) {\r\n\t\t\t\tif (packet.getType() == StanzaType.error && Authorization.SERVICE_UNAVAILABLE.getCondition().equals(packet.getErrorCondition())) {\r\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\r\n\t\t\t\t\t\tlog.log(Level.FINEST, \"could not contact SessionManager, stopping client connection {0}...\", serv);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tserv.forceStop();\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t\tif ((Boolean) serv.getSessionData().getOrDefault(\"SEND_TLS_COMPLETED\", false)) {\r\n\t\t\t\t\tClientConnectionManager.this.sendTlsHandshakeCompletedToSessionManager(serv);\r\n\t\t\t\t}\r\n\t\t\t\tSocketType socket = (SocketType) serv.getSessionData().get(\"socket\");\r\n\t\t\t\tboolean ssl = socket.equals(SocketType.ssl);\r\n\t\t\t\taddOutPacket(Command.GETFEATURES.getPacket(packet.getFrom(), packet.getTo(), StanzaType.get,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   (ssl ? \"ssl_\" : \"\") + UUID.randomUUID().toString(), null));\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic void timeOutExpired(Packet packet) {\r\n\r\n\t\t\t// If we still haven't received confirmation from the SM then\r\n\t\t\t// the packet either has been lost or the server is overloaded\r\n\t\t\t// In either case we disconnect the connection.\r\n\t\t\tlog.log(Level.CONFIG, \"No response within time limit received for a packet: {0}\", packet.toStringSecure());\r\n\r\n\t\t\tXMPPIOService<Object> serv = getXMPPIOService(packet.getFrom().toString());\r\n\r\n\t\t\tif (serv != null) {\r\n\t\t\t\tserv.stop();\r\n\t\t\t} else {\r\n\t\t\t\tlog.log(Level.FINE, \"Attempt to stop non-existen service for packet: {0}, Service already stopped?\",\r\n\t\t\t\t\t\tpacket);\r\n\t\t\t}    // end of if (serv != null) else\r\n\t\t}\r\n\t}\r\n\r\n\tprivate class StoppedHandler\r\n\t\t\timplements ReceiverTimeoutHandler {\r\n\r\n\t\t@Override\r\n\t\tpublic void responseReceived(Packet packet, Packet response) {\r\n\r\n\t\t\t// Great, nothing to worry about.\r\n\t\t\tif (log.isLoggable(Level.FINEST)) {\r\n\t\t\t\tlog.finest(\"Response for stop received...\");\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic void timeOutExpired(Packet packet) {\r\n\r\n\t\t\t// Ups, doesn't look good, the server is either oveloaded or lost\r\n\t\t\t// a packet.\r\n\t\t\tlog.log(Level.CONFIG, \"No response within time limit received for a packet: {0}; RETRYING\",\r\n\t\t\t\t\tpacket.toStringSecure());\r\n\t\t\taddOutPacketWithTimeout(packet, stoppedHandler, 60L, TimeUnit.SECONDS);\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppclient/ClientTrustManagerFactory.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppclient;\n\nimport tigase.cert.CertificateEntry;\nimport tigase.cert.CertificateUtil;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Command;\nimport tigase.server.DataForm;\nimport tigase.server.Packet;\nimport tigase.vhosts.*;\nimport tigase.xml.Element;\nimport tigase.xmpp.XMPPIOService;\n\nimport javax.net.ssl.TrustManager;\nimport javax.net.ssl.TrustManagerFactory;\nimport javax.net.ssl.X509TrustManager;\nimport java.security.KeyStore;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.cert.Certificate;\nimport java.security.cert.CertificateException;\nimport java.security.cert.X509Certificate;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Bean(name = \"client-trust-manager-factory\", parent = ClientConnectionManager.class, active = true)\npublic class ClientTrustManagerFactory {\n\n\tpublic static final String CA_CERT_PATH = \"clientCertCA\";\n\n\tpublic static final String CERT_REQUIRED_KEY = \"clientCertRequired\";\n\n\tprivate final static char[] EMPTY_PASS = new char[0];\n\n\tprivate static final Logger log = Logger.getLogger(ClientTrustManagerFactory.class.getName());\n\n\tprivate final ArrayList<X509Certificate> acceptedIssuers = new ArrayList<X509Certificate>();\n\n\tprotected final TrustManager[] emptyTrustManager;\n\tprivate final KeyStore keystore;\n\tprivate final ConcurrentHashMap<VHostItem, TrustManager[]> trustManagers = new ConcurrentHashMap<>();\n\t@ConfigField(desc = \"CA for client certificate\", alias = \"clientCertCA\")\n\tprivate String clientCertCA;\n\t@ConfigField(desc = \"Is client certificate required\")\n\tprivate boolean clientCertRequired = false;\n\tprotected TrustManager[] defaultTrustManagers;\n\tprivate TrustManagerFactory tmf;\n\n\tpublic ClientTrustManagerFactory() {\n\t\tthis.emptyTrustManager = new TrustManager[]{new X509TrustManager() {\n\n\t\t\t@Override\n\t\t\tpublic void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic X509Certificate[] getAcceptedIssuers() {\n\t\t\t\treturn new X509Certificate[0];\n\t\t\t}\n\t\t}};\n\t\ttry {\n\t\t\tkeystore = KeyStore.getInstance(KeyStore.getDefaultType());\n\t\t\tkeystore.load(null, EMPTY_PASS);\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\n\t\ttry {\n\t\t\tthis.tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());\n\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\n\t}\n\n\tpublic void setClientCertCA(String clientCertCA) {\n\t\tthis.clientCertCA = clientCertCA;\n\t\tif (clientCertCA != null) {\n\t\t\tdefaultTrustManagers = loadTrustedCert(clientCertCA);\n\t\t} else {\n\t\t\tdefaultTrustManagers = null;\n\t\t}\n\t}\n\n\tpublic TrustManager[] getManager(final VHostItem vHost) {\n\t\tTrustManager[] result = trustManagers.get(vHost);\n\n\t\tfinal String vhostKey = vHost != null ? vHost.getKey() : \"null\";\n\t\tif (result == null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Creating new TrustManager for VHost \" + vhostKey);\n\t\t\t}\n\n\t\t\tresult = defaultTrustManagers;\n\t\t\tClientTrustVHostItemExtension extension = vHost.getExtension(ClientTrustVHostItemExtension.class);\n\t\t\tString path = extension != null ? extension.getCaCertPath() : null;\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"CA cert path=\" + path + \" for VHost \" + vhostKey);\n\t\t\t}\n\t\t\tif (path != null) {\n\t\t\t\tTrustManager[] tmp = loadTrustedCert(path);\n\t\t\t\tif (tmp != null) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.finest(\"Using custom TrustManager for VHost \" + vhostKey);\n\t\t\t\t\t}\n\t\t\t\t\tresult = tmp;\n\t\t\t\t\ttrustManagers.put(vHost, result);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Found TrustManager for VHost \" + vhostKey);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic TrustManager[] getManager(final XMPPIOService<Object> serv) {\n\t\treturn isActive() ? emptyTrustManager : null;\n\t}\n\n\tpublic boolean isActive() {\n\t\treturn acceptedIssuers.size() > 0;\n\t}\n\n\tpublic boolean isTlsNeedClientAuthEnabled(final VHostItem vhost) {\n\t\tClientTrustVHostItemExtension extension = vhost.getExtension(ClientTrustVHostItemExtension.class);\n\t\tif (extension == null || extension.isCertRequired() == null) {\n\t\t\treturn clientCertRequired;\n\t\t}\n\t\treturn extension.isCertRequired();\n\t}\n\n\tpublic boolean isTlsWantClientAuthEnabled(final VHostItem vhost) {\n\t\tTrustManager[] tmp = getManager(vhost);\n\t\treturn tmp != null && tmp.length > 0;\n\t}\n\n\tprotected X509Certificate[] getAcceptedIssuers() {\n\t\treturn acceptedIssuers.toArray(new X509Certificate[]{});\n\t}\n\n\tprotected TrustManager[] loadTrustedCert(String caCertFile) {\n\t\ttry {\n\t\t\tCertificateEntry certEntry = CertificateUtil.loadCertificate(caCertFile);\n\t\t\tCertificate[] chain = certEntry.getCertChain();\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Loaded certificate from file \" + caCertFile + \" : \" + certEntry);\n\t\t\t}\n\n\t\t\tif (chain != null) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"Loaded cert chain: \" + Arrays.toString(chain));\n\t\t\t\t}\n\t\t\t\tfor (Certificate cert : chain) {\n\t\t\t\t\tif (cert instanceof X509Certificate) {\n\t\t\t\t\t\tX509Certificate crt = (X509Certificate) cert;\n\t\t\t\t\t\tString alias = crt.getSubjectX500Principal().getName();\n\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.finest(\"Adding certificate to keystore: alias=\" + alias + \"; cert=\" + crt);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tkeystore.setCertificateEntry(alias, crt);\n\t\t\t\t\t\tacceptedIssuers.add(crt);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\ttmf.init(keystore);\n\t\t\treturn tmf.getTrustManagers();\n\t\t\t// this.saslExternalAvailable = true;\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Can''t create TrustManager with certificate from file.\", e);\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\t@Bean(name = \"client-trust-extension\", parent = VHostItemExtensionManager.class, active = true)\n\tpublic static class ClientTrustVHostItemExtensionProvider\n\t\t\timplements VHostItemExtensionProvider<ClientTrustVHostItemExtension> {\n\n\t\t@Override\n\t\tpublic String getId() {\n\t\t\treturn ClientTrustVHostItemExtension.ID;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<ClientTrustVHostItemExtension> getExtensionClazz() {\n\t\t\treturn ClientTrustVHostItemExtension.class;\n\t\t}\n\t}\n\n\tpublic static class ClientTrustVHostItemExtension\n\t\t\textends AbstractVHostItemExtension<ClientTrustVHostItemExtension>\n\t\t\timplements VHostItemExtensionBackwardCompatible<ClientTrustVHostItemExtension> {\n\n\t\tprotected static final String ID = \"client-trust-extension\";\n\n\t\tpublic static final String CA_CERT_PATH = \"ca-cert-path\";\n\t\tpublic static final String CERT_REQUIRED = \"cert-required\";\n\n\t\tprivate String caCertPath;\n\t\tprivate Boolean certRequired = null;\n\n\t\t@Override\n\t\tpublic String getId() {\n\t\t\treturn ID;\n\t\t}\n\n\t\tpublic String getCaCertPath() {\n\t\t\treturn caCertPath;\n\t\t}\n\n\t\tpublic Boolean isCertRequired() {\n\t\t\treturn certRequired;\n\t\t}\n\n\t\t@Override\n\t\tpublic void initFromElement(Element item) {\n\t\t\tcaCertPath = item.getAttributeStaticStr(CA_CERT_PATH);\n\t\t\tcertRequired = Optional.ofNullable(item.getAttributeStaticStr(CERT_REQUIRED))\n\t\t\t\t\t.map(Boolean::parseBoolean)\n\t\t\t\t\t.orElse(null);\n\t\t}\n\n\t\t@Override\n\t\tpublic void initFromCommand(String prefix, Packet packet) throws IllegalArgumentException {\n\t\t\tcaCertPath = Optional.ofNullable(Command.getFieldValue(packet, prefix + \"-\" + CA_CERT_PATH))\n\t\t\t\t\t.filter(s -> !s.isEmpty())\n\t\t\t\t\t.orElse(null);\n\t\t\tcertRequired = Optional.ofNullable(Command.getFieldValue(packet, prefix + \"-\" + CERT_REQUIRED))\n\t\t\t\t\t.map(s -> s.isEmpty() ? null : s)\n\t\t\t\t\t.map(Boolean::parseBoolean)\n\t\t\t\t\t.orElse(null);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toDebugString() {\n\t\t\treturn \"caCertPath: \" + caCertPath + \", certRequired: \" + certRequired;\n\t\t}\n\n\t\t@Override\n\t\tpublic Element toElement() {\n\t\t\tif ((caCertPath != null && !caCertPath.isEmpty()) || certRequired != null) {\n\t\t\t\tElement el = new Element(getId());\n\t\t\t\tif (caCertPath != null) {\n\t\t\t\t\tel.addAttribute(CA_CERT_PATH, caCertPath);\n\t\t\t\t}\n\t\t\t\tif (certRequired != null) {\n\t\t\t\t\tel.addAttribute(CERT_REQUIRED, String.valueOf(certRequired));\n\t\t\t\t}\n\t\t\t\treturn el;\n\t\t\t} else {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void addCommandFields(String prefix, Packet packet, boolean forDefault) {\n\t\t\tElement commandEl = packet.getElemChild(Command.COMMAND_EL, Command.XMLNS);\n\t\t\tDataForm.addFieldValue(commandEl, prefix + \"-\" + CA_CERT_PATH, caCertPath, \"text-single\",\n\t\t\t\t\t\t\t\t   \"Client Certificate CA\");\n\t\t\taddBooleanFieldWithDefaultToCommand(commandEl, prefix + \"-\" + CERT_REQUIRED, \"Client Certificate Required\",\n\t\t\t\t\t\t\t\t\t\t\t\tcertRequired, forDefault);\n\t\t}\n\n\t\t@Override\n\t\tpublic void initFromData(Map<String, Object> data) {\n\t\t\tcaCertPath = (String) data.remove(ClientTrustManagerFactory.CA_CERT_PATH);\n\t\t\tcertRequired = (Boolean) data.remove(ClientTrustManagerFactory.CERT_REQUIRED_KEY);\n\t\t}\n\n\t\t@Override\n\t\tpublic ClientTrustVHostItemExtension mergeWithDefaults(ClientTrustVHostItemExtension defaults) {\n\t\t\treturn this;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppclient/IPMonitor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppclient;\n\nimport java.util.LinkedHashSet;\nimport java.util.Timer;\nimport java.util.TimerTask;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Logger;\n\n/**\n * Created: Sep 11, 2009 12:39:04 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class IPMonitor\n\t\textends Thread {\n\n\tprivate static final Logger log = Logger.getLogger(IPMonitor.class.getName());\n\tprivate static final int MAX_SIZE = 50;\n\tprivate static final long CLEANUP_RATE = 10000;\n\tprivate static final long DISC_THRESHOLD = 200;\n\tprivate long[] ip_cnts = new long[MAX_SIZE];\n\tprivate LinkedHashSet<String> ips = new LinkedHashSet<String>();\n\tprivate LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<String>();\n\tprivate boolean stopped = false;\n\tprivate Timer timer = new Timer(\"IPMonitor Timer\", true);\n\n\tpublic IPMonitor() {\n\t\tsuper();\n\t\tsetName(IPMonitor.class.getSimpleName());\n\t\tsetDaemon(true);\n\t\tfor (int i = 0; i < ip_cnts.length; i++) {\n\t\t\tip_cnts[i] = 0;\n\t\t}\n\t}\n\n\tpublic void addDisconnect(String ip) {\n\t\tqueue.offer(ip);\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\ttimer.scheduleAtFixedRate(new TimerTask() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tfor (String ip : ips) {\n\t\t\t\t\tint idx = Math.abs(ip.hashCode() % ip_cnts.length);\n\t\t\t\t\tif (ip_cnts[idx] > DISC_THRESHOLD) {\n\t\t\t\t\t\tlog.warning(\"Many disconnects for IP: \" + ip + \" - \" + ip_cnts[idx]);\n\t\t\t\t\t}\n\t\t\t\t\tip_cnts[idx] = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}, CLEANUP_RATE, CLEANUP_RATE);\n\t\twhile (!stopped) {\n\t\t\ttry {\n\t\t\t\tString ip = queue.poll(10, TimeUnit.SECONDS);\n\t\t\t\tif (ip != null) {\n\t\t\t\t\tint idx = Math.abs(ip.hashCode()) % ip_cnts.length;\n\t\t\t\t\t++ip_cnts[idx];\n\t\t\t\t\tif (ips.size() < MAX_SIZE) {\n\t\t\t\t\t\tips.add(ip);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.warning(\"Error processing queue: \" + e);\n\t\t\t}\n\t\t}\n\t\ttimer.cancel();\n\t}\n\n\tpublic void stopThread() {\n\t\tstopped = true;\n\t\ttimer.cancel();\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppclient/RegistrationThrottling.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppclient;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Packet;\nimport tigase.xmpp.XMPPIOService;\n\nimport java.time.Duration;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\n/**\n * Created by andrzej on 19.11.2016.\n */\n@Bean(name = RegistrationThrottling.ID, parent = Kernel.class, active = false, exportable = true)\npublic class RegistrationThrottling\n\t\timplements UnregisterAware {\n\n\tpublic static final String ID = \"registration-throttling\";\n\t@ConfigField(desc = \"Limit of allowed account registrations for IP in specified period\")\n\tprotected Integer limit = 4;\n\t@ConfigField(desc = \"Period for which limit is set\")\n\tprotected Duration period = Duration.ofDays(1);\n\tprivate AtomicBoolean cleanUpScheduled = new AtomicBoolean(false);\n\tprivate ConcurrentHashMap<String, List<Long>> registrations = new ConcurrentHashMap<>();\n\tprivate Timer timer = new Timer(\"registration-timer\", true);\n\n\tpublic void startFor(Kernel kernel) {\n\t\tkernel.registerBean(RegistrationThrottlingProcessor.class).exec();\n\t}\n\n\tpublic void stopFor(Kernel kernel) {\n\t\tkernel.unregister(RegistrationThrottlingProcessor.ID);\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\ttimer.cancel();\n\t}\n\n\tprotected boolean checkLimits(XMPPIOService service) {\n\t\tList<Long> registrationTimes = registrations.computeIfAbsent(service.getRemoteAddress(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t (k) -> new ArrayList<Long>());\n\t\tsynchronized (registrationTimes) {\n\t\t\tcleanUp(registrationTimes);\n\n\t\t\tif (registrationTimes.size() <= limit) {\n\t\t\t\tregistrationTimes.add(System.currentTimeMillis());\n\t\t\t}\n\n\t\t\treturn registrationTimes.size() <= limit;\n\t\t}\n\t}\n\n\tprotected boolean checkLimits(XMPPIOService service, Packet packet) {\n\t\tboolean result = checkLimits(service);\n\t\tscheduleCleanUpIfNeeded();\n\t\treturn result;\n\t}\n\n\tprotected void cleanUp(List<Long> registrationTimes) {\n\t\t// Five seconds added to improve performance\n\t\tlong oldestAllowed = (System.currentTimeMillis() - period.toMillis()) + 5000;\n\t\tregistrationTimes.removeIf((ts) -> ts < oldestAllowed);\n\t}\n\n\tprotected void cleanUpFromTimer() {\n\t\tIterator<Map.Entry<String, List<Long>>> it = registrations.entrySet().iterator();\n\t\twhile (it.hasNext()) {\n\t\t\tMap.Entry<String, List<Long>> e = it.next();\n\t\t\tList<Long> registrationTimes = e.getValue();\n\t\t\tsynchronized (registrationTimes) {\n\t\t\t\tcleanUp(registrationTimes);\n\t\t\t\tif (registrationTimes.isEmpty()) {\n\t\t\t\t\tit.remove();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tOptional<Long> earliest = registrations.values().stream().flatMap(times -> times.stream()).min(Long::compare);\n\t\tif (earliest.isPresent()) {\n\t\t\ttimer.schedule(new CleanUpTask(), System.currentTimeMillis() - earliest.get());\n\t\t} else {\n\t\t\tcleanUpScheduled.compareAndSet(true, false);\n\t\t}\n\t}\n\n\tprotected void scheduleCleanUpIfNeeded() {\n\t\tif (cleanUpScheduled.compareAndSet(false, true)) {\n\t\t\ttimer.schedule(new CleanUpTask(), period.toMillis());\n\t\t}\n\t}\n\n\tprotected class CleanUpTask\n\t\t\textends TimerTask {\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tRegistrationThrottling.this.cleanUpFromTimer();\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppclient/RegistrationThrottlingProcessor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppclient;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.stats.StatisticsList;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.JID;\n\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created by andrzej on 16.11.2016.\n */\n@Bean(name = RegistrationThrottlingProcessor.ID, active = true)\npublic class RegistrationThrottlingProcessor\n\t\timplements XMPPIOProcessor {\n\n\tpublic static final String ID = RegistrationThrottling.ID + \"-processor\";\n\tprivate static final Logger log = Logger.getLogger(RegistrationThrottlingProcessor.class.getCanonicalName());\n\tprivate static final String[] REGISTER_PATH = Iq.IQ_QUERY_PATH;\n\tprivate static final String[] REMOVE_PATH = new String[]{Iq.ELEM_NAME, Iq.QUERY_NAME, \"remove\"};\n\tprivate static final String[] USERNAME_PATH = new String[]{Iq.ELEM_NAME, Iq.QUERY_NAME, \"username\"};\n\tprivate static final String XMLNS = \"jabber:iq:register\";\n\n\t@Inject(bean = \"service\")\n\tprivate ClientConnectionManager connectionManager;\n\n\t@Inject(bean = RegistrationThrottling.ID)\n\tprivate RegistrationThrottling throttler;\n\n\t@Override\n\tpublic String getId() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\n\t}\n\n\t@Override\n\tpublic Element[] supStreamFeatures(XMPPIOService service) {\n\t\treturn new Element[0];\n\t}\n\n\t@Override\n\tpublic boolean processIncoming(XMPPIOService service, Packet packet) {\n\t\tif (packet.getType() != StanzaType.set || !XMLNS.equals(packet.getAttributeStaticStr(REGISTER_PATH, \"xmlns\"))) {\n\t\t\treturn false;\n\t\t}\n\n\t\tJID to = packet.getStanzaTo();\n\t\tif (to != null && (to.getLocalpart() != null || !connectionManager.isLocalDomain(to.getDomain()))) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (packet.getElement().findChild(REMOVE_PATH) != null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (throttler.checkLimits(service, packet)) {\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"User from IP {0} exceeded registration limit trying to register account {1}\",\n\t\t\t\t\t\tnew Object[]{service.getRemoteAddress(), packet.getElemCDataStaticStr(USERNAME_PATH)});\n\t\t\t}\n\t\t\tPacket errorPacket = Authorization.POLICY_VIOLATION.getResponseMessage(packet, \"Policy violation\", true);\n\n\t\t\tElement streamError = new Element(\"policy-violation\");\n\t\t\tstreamError.setXMLNS(\"urn:ietf:params:xml:ns:xmpp-stanzas\");\n\t\t\tString result = connectionManager.xmppStreamError(service, Arrays.asList(streamError));\n\t\t\tservice.writeRawData(errorPacket.getElement().toString() + result + \"</stream:stream>\");\n\t\t} catch (PacketErrorTypeException | IOException ex) {\n\t\t\tlog.log(Level.FINEST, \"Exception while registration request to check policy violation\");\n\t\t}\n\t\tservice.stop();\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean processOutgoing(XMPPIOService service, Packet packet) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void packetsSent(XMPPIOService service) throws IOException {\n\n\t}\n\n\t@Override\n\tpublic void processCommand(XMPPIOService service, Packet packet) {\n\n\t}\n\n\t@Override\n\tpublic boolean serviceStopped(XMPPIOService service, boolean streamClosed) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void streamError(XMPPIOService service, StreamError streamError) {\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppclient/SeeOtherHost.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppclient;\n\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.HandleEvent;\nimport tigase.eventbus.events.ShutdownEvent;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.beans.selector.ClusterModeRequired;\nimport tigase.server.Command;\nimport tigase.server.DataForm;\nimport tigase.server.Packet;\nimport tigase.vhosts.*;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.concurrent.CopyOnWriteArraySet;\nimport java.util.logging.Logger;\n\n/**\n * Default and basic implementation of SeeOtherHost returning same host as the initial one\n *\n * @author Wojtek\n */\n@Bean(name = \"seeOtherHost\", parent = ClientConnectionManager.class, active = true)\n@ClusterModeRequired(active = false)\npublic class SeeOtherHost\n\t\timplements SeeOtherHostIfc, Initializable {\n\n\tpublic static final String REDIRECTION_ENABLED = \"see-other-host-redirect-enabled\";\n\tprivate static final Logger log = Logger.getLogger(SeeOtherHost.class.getName());\n\t@ConfigField(desc = \"Default host to redirect to\")\n\tprotected ArrayList<BareJID> defaultHost = null;\n\t@Inject\n\tprotected EventBus eventBus;\n\t@Inject\n\tprotected VHostManagerIfc vHostManager = null;\n\t@ConfigField(desc = \"Active phases\", alias = \"phases\")\n\tprivate ArrayList<Phase> active = new ArrayList<Phase>(Arrays.asList(Phase.OPEN));\n\tprivate Set<String> shutdownNodes = new CopyOnWriteArraySet<String>();\n\n\t@Override\n\tpublic BareJID findHostForJID(BareJID jid, BareJID host) {\n\t\tif (defaultHost != null && !defaultHost.isEmpty()) {\n\t\t\treturn defaultHost.get(0);\n\t\t} else {\n\t\t\treturn host;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t}\n\n\tpublic void setDefaultHost(ArrayList<BareJID> defaultHost) {\n\t\tif (defaultHost != null) {\n\t\t\tCollections.sort(defaultHost);\n\t\t}\n\t\tthis.defaultHost = defaultHost;\n\t}\n\n\t@Override\n\tpublic void setNodes(List<JID> nodes) {\n\t\t// log.log(Level.CONFIG, \"Action invalid for current implementation.\");\n\t\tsynchronized (this) {\n\t\t\tList<String> toRemove = new ArrayList<String>();\n\t\t\tIterator<String> it = shutdownNodes.iterator();\n\t\t\twhile (it.hasNext()) {\n\t\t\t\tString shutdownNode = it.next();\n\t\t\t\tboolean found = false;\n\t\t\t\tfor (JID node : nodes) {\n\t\t\t\t\tfound |= shutdownNode.equals(node.getDomain());\n\t\t\t\t}\n\t\t\t\t// remove node from nodes during shutdown if nodes was disconnected \n\t\t\t\t// as it probably was shutdown and should reconnect after restart\n\t\t\t\tif (!found) {\n\t\t\t\t\ttoRemove.add(shutdownNode);\n\t\t\t\t}\n\t\t\t}\n\t\t\tshutdownNodes.removeAll(toRemove);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isEnabled(VHostItem vHost, Phase ph) {\n\t\tSeeOtherHostVHostItemExtension extension = vHost.getExtension(SeeOtherHostVHostItemExtension.class);\n\t\tif (extension == null) {\n\t\t\treturn active.contains(ph);\n\t\t}\n\t\treturn extension.isEnabled() && active.contains(ph);\n\t}\n\n\t@Override\n\tpublic void start() {\n\t\teventBus.registerAll(this);\n\t}\n\n\t@Override\n\tpublic void stop() {\n\t\teventBus.unregisterAll(this);\n\t}\n\n\tprotected boolean isNodeShutdown(BareJID jid) {\n\t\treturn jid != null && shutdownNodes.contains(jid.getDomain());\n\t}\n\n\t@HandleEvent\n\tprotected void nodeShutdown(ShutdownEvent event) {\n\t\tsynchronized (this) {\n\t\t\tshutdownNodes.add(event.getNode());\n\t\t}\n\t}\n\n\t@Bean(name = SeeOtherHostVHostItemExtension.ID, parent = VHostItemExtensionManager.class, active = true)\n\tpublic static class SeeOtherHostVHostItemExtensionProvider implements VHostItemExtensionProvider<SeeOtherHostVHostItemExtension> {\n\n\t\t@Override\n\t\tpublic String getId() {\n\t\t\treturn SeeOtherHostVHostItemExtension.ID;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<SeeOtherHostVHostItemExtension> getExtensionClazz() {\n\t\t\treturn SeeOtherHostVHostItemExtension.class;\n\t\t}\n\t}\n\n\tpublic static class SeeOtherHostVHostItemExtension extends AbstractVHostItemExtension<SeeOtherHostVHostItemExtension> implements VHostItemExtensionBackwardCompatible<SeeOtherHostVHostItemExtension> {\n\n\t\tpublic static final String ID = \"see-other-host\";\n\n\t\tprivate boolean enabled = true;\n\n\t\t@Override\n\t\tpublic String getId() {\n\t\t\treturn ID;\n\t\t}\n\n\t\t@Override\n\t\tpublic void initFromElement(Element item) {\n\t\t\tenabled = Boolean.parseBoolean(item.getAttributeStaticStr(\"enabled\"));\n\t\t}\n\n\t\t@Override\n\t\tpublic void initFromCommand(String prefix, Packet packet) throws IllegalArgumentException {\n\t\t\tenabled = Command.getCheckBoxFieldValue(packet, prefix + \"-\" + REDIRECTION_ENABLED);\n\t\t}\n\n\t\tpublic boolean isEnabled() {\n\t\t\treturn enabled;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toDebugString() {\n\t\t\treturn \"enabled: \" + enabled;\n\t\t}\n\n\t\t@Override\n\t\tpublic Element toElement() {\n\t\t\tif (enabled) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tElement el = new Element(getId());\n\t\t\tel.setAttribute(\"enabled\", String.valueOf(enabled));\n\t\t\treturn el;\n\t\t}\n\n\t\t@Override\n\t\tpublic void addCommandFields(String prefix, Packet packet, boolean forDefault) {\n\t\t\tElement commandEl = packet.getElemChild(Command.COMMAND_EL, Command.XMLNS);\n\t\t\tDataForm.addFieldValue(commandEl, prefix + \"-\" + REDIRECTION_ENABLED, String.valueOf(enabled), \"boolean\", \"see-other-host redirection enabled\");\n\t\t}\n\n\t\t@Override\n\t\tpublic SeeOtherHostVHostItemExtension mergeWithDefaults(SeeOtherHostVHostItemExtension defaults) {\n\t\t\tif (isEnabled()) {\n\t\t\t\treturn this;\n\t\t\t} else if (defaults.isEnabled()) {\n\t\t\t\treturn defaults;\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic void initFromData(Map<String, Object> data) {\n\t\t\tBoolean val = (Boolean) data.remove(REDIRECTION_ENABLED);\n\t\t\tif (val != null) {\n\t\t\t\tenabled = val;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppclient/SeeOtherHostDB.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppclient;\n\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.db.*;\nimport tigase.db.beans.SDRepositoryBean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.RegistrarBean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.core.Kernel;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentSkipListMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Extended implementation of SeeOtherHost using redirect information from database\n */\npublic class SeeOtherHostDB\n\t\textends SeeOtherHostHashed\n\t\timplements RegistrarBean {\n\n\tpublic static final String SEE_OTHER_HOST_TABLE = \"tig_see_other_hosts\";\n\tpublic static final String SEE_OTHER_HOST_DB_URL_KEY = CM_SEE_OTHER_HOST_CLASS_PROP_KEY + \"/\" + \"db-url\";\n\tpublic static final String SEE_OTHER_HOST_DB_QUERY_KEY = CM_SEE_OTHER_HOST_CLASS_PROP_KEY + \"/\" + \"get-host-query\";\n\tpublic static final String DB_GET_ALL_DATA_DB_QUERY_KEY =\n\t\t\tCM_SEE_OTHER_HOST_CLASS_PROP_KEY + \"/\" + \"get-all-data-query\";\n\tpublic static final String GET_ALL_QUERY_TIMEOUT_QUERY_KEY =\n\t\t\tCM_SEE_OTHER_HOST_CLASS_PROP_KEY + \"/\" + \"get-all-query-timeout\";\n\tpublic static final String SERIAL_ID = \"sid\";\n\tpublic static final String USER_ID = \"uid\";\n\tpublic static final String NODE_ID = \"node_id\";\n\tprivate static final Logger log = Logger.getLogger(SeeOtherHostDB.class.getName());\n\t@Inject\n\tprivate SeeOtherHostRepository repo;\n\n\t// Methods\n\n\t@Override\n\tpublic BareJID findHostForJID(BareJID jid, BareJID host) {\n\n\t\tBareJID see_other_host = repo.getHostFor(jid);\n\n\t\tif (see_other_host != null && !isNodeShutdown(see_other_host)) {\n\t\t\treturn see_other_host;\n//\t\t} else {\n//\t\t\tsee_other_host = host;\n\t\t}\n\n\t\ttry {\n\t\t\tsee_other_host = repo.queryDBFor(jid);\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.SEVERE, \"DB lookup failed, fallback to SeeOtherHostHashed: \", ex);\n\t\t}\n\n\t\tif (see_other_host == null || isNodeShutdown(see_other_host)) {\n\t\t\tlog.log(Level.FINE,\n\t\t\t\t\t\"DB lookup failed or selected node is being stopped, fallback to SeeOtherHostHashed for {0}\", jid);\n\t\t\tsee_other_host = super.findHostForJID(jid, host);\n\t\t}\n\n\t\treturn see_other_host;\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\n\t}\n\n\t/**\n\t * Performs database check, creates missing schema if necessary\n\t *\n\t */\n\tprivate void checkDB() throws SQLException {\n\n\t}\n\n\tpublic interface SeeOtherHostRepository<DS extends DataSource>\n\t\t\textends DataSourceAware<DS> {\n\n\t\tBareJID getHostFor(BareJID jid);\n\n\t\tBareJID queryDBFor(BareJID jid) throws UserNotFoundException, SQLException, TigaseStringprepException;\n\n\t}\n\n\t@Repository.Meta(supportedUris = {\"jdbc:[^:]+:.*\"})\n\tpublic static class JDBCSeeOtherHostRepository\n\t\t\timplements SeeOtherHostRepository<DataRepository> {\n\n\t\tpublic static final String DEF_DB_GET_HOST_QUERY =\n\t\t\t\t\" select * from tig_users, \" + SEE_OTHER_HOST_TABLE + \" where tig_users.uid = \" + SEE_OTHER_HOST_TABLE +\n\t\t\t\t\t\t\".\" + USER_ID + \" and user_id = ?\";\n\n\t\tprivate static final String DEF_DB_GET_ALL_DATA_QUERY =\n\t\t\t\t\"select user_id, node_id from tig_users, \" + SEE_OTHER_HOST_TABLE + \" where tig_users.uid = \" +\n\t\t\t\t\t\tSEE_OTHER_HOST_TABLE + \".\" + USER_ID;\n\n\t\tprivate static final String CREATE_STATS_TABLE =\n\t\t\t\t\"create table \" + SEE_OTHER_HOST_TABLE + \" ( \" + SERIAL_ID + \" serial,\" + USER_ID +\n\t\t\t\t\t\t\" bigint unsigned NOT NULL, \" + NODE_ID + \" varchar(2049) NOT NULL, \" + \" primary key (\" +\n\t\t\t\t\t\tSERIAL_ID + \"), \" + \" constraint tig_see_other_host_constr foreign key (\" + USER_ID +\n\t\t\t\t\t\t\") references tig_users (\" + USER_ID + \")\" + \")\";\n\n\t\tprivate static final String DERBY_CREATE_STATS_TABLE =\n\t\t\t\t\"create table \" + SEE_OTHER_HOST_TABLE + \" ( \" + SERIAL_ID +\n\t\t\t\t\t\t\" bigint generated by default as identity not null,\" + USER_ID + \" bigint  NOT NULL, \" +\n\t\t\t\t\t\tNODE_ID + \" varchar(2049) NOT NULL, \" + \" primary key (\" + SERIAL_ID + \"), \" +\n\t\t\t\t\t\t\" constraint tig_see_other_host_constr foreign key (\" + USER_ID + \") references tig_users (\" +\n\t\t\t\t\t\tUSER_ID + \")\" + \")\";\n\n\t\tprivate static final String SQLSERVER_CREATE_STATS_TABLE =\n\t\t\t\t\"create table \" + SEE_OTHER_HOST_TABLE + \" ( \" + SERIAL_ID + \" [bigint] IDENTITY(1,1),\" + USER_ID +\n\t\t\t\t\t\t\" bigint NOT NULL, \" + NODE_ID + \" nvarchar(2049) NOT NULL, \" + \" primary key (\" + SERIAL_ID +\n\t\t\t\t\t\t\"), \" + \" constraint tig_see_other_host_constr foreign key (\" + USER_ID +\n\t\t\t\t\t\t\") references tig_users (\" + USER_ID + \")\" + \")\";\n\n\t\tprivate static final int DEF_QUERY_TIME_OUT = 0;\n\t\tprivate DataRepository data_repo;\n\t\t@ConfigField(desc = \"Query to load mapping data\", alias = \"get-all-data-query\")\n\t\tprivate String get_all_data_query = DEF_DB_GET_ALL_DATA_QUERY;\n\t\t@ConfigField(desc = \"Query to find host for JID\", alias = \"get-host-query\")\n\t\tprivate String get_host_query = DEF_DB_GET_HOST_QUERY;\n\t\tprivate Map<BareJID, BareJID> redirectsMap = new ConcurrentSkipListMap<BareJID, BareJID>();\n\n\t\t@Override\n\t\tpublic BareJID getHostFor(BareJID jid) {\n\t\t\treturn redirectsMap.get(jid);\n\t\t}\n\n\t\t@Override\n\t\tpublic BareJID queryDBFor(BareJID user) throws UserNotFoundException, SQLException, TigaseStringprepException {\n\n\t\t\tPreparedStatement get_host = data_repo.getPreparedStatement(user, get_host_query);\n\n\t\t\tResultSet rs = null;\n\n\t\t\tsynchronized (get_host) {\n\t\t\t\ttry {\n\t\t\t\t\tget_host.setString(1, user.toString());\n\n\t\t\t\t\trs = get_host.executeQuery();\n\n\t\t\t\t\tif (rs.next()) {\n\t\t\t\t\t\treturn BareJID.bareJIDInstance(rs.getString(NODE_ID));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new UserNotFoundException(\"Item does not exist for user: \" + user);\n\t\t\t\t\t} // end of if (isnext) else\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void setDataSource(DataRepository data_repo) throws RepositoryException {\n\t\t\ttry {\n\t\t\t\tDataRepository.dbTypes databaseType = data_repo.getDatabaseType();\n\t\t\t\tswitch (databaseType) {\n\t\t\t\t\tcase derby:\n\t\t\t\t\t\tdata_repo.checkTable(SEE_OTHER_HOST_TABLE, DERBY_CREATE_STATS_TABLE);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase jtds:\n\t\t\t\t\tcase sqlserver:\n\t\t\t\t\t\tdata_repo.checkTable(SEE_OTHER_HOST_TABLE, SQLSERVER_CREATE_STATS_TABLE);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase postgresql:\n\t\t\t\t\tcase mysql:\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdata_repo.checkTable(SEE_OTHER_HOST_TABLE, CREATE_STATS_TABLE);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tdata_repo.initPreparedStatement(get_host_query, get_host_query);\n\t\t\t\tdata_repo.initPreparedStatement(get_all_data_query, get_all_data_query);\n\n\t\t\t\tthis.data_repo = data_repo;\n\t\t\t\tqueryAllDB();\n\t\t\t} catch (SQLException ex) {\n\t\t\t\tthrow new TigaseDBException(\"Could not initialize repository\", ex);\n\t\t\t}\n\t\t}\n\n\t\tprivate void queryAllDB() throws SQLException {\n\t\t\tPreparedStatement get_all = data_repo.getPreparedStatement(null, get_all_data_query);\n\t\t\tget_all.setQueryTimeout(DEF_QUERY_TIME_OUT);\n\n\t\t\tResultSet rs = null;\n\n\t\t\tsynchronized (get_all) {\n\t\t\t\ttry {\n\t\t\t\t\trs = get_all.executeQuery();\n\n\t\t\t\t\twhile (rs.next()) {\n\t\t\t\t\t\tString user_jid = rs.getString(\"user_id\");\n\t\t\t\t\t\tString node_jid = rs.getString(NODE_ID);\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tBareJID user = BareJID.bareJIDInstance(user_jid);\n\t\t\t\t\t\t\tBareJID node = BareJID.bareJIDInstance(node_jid);\n\t\t\t\t\t\t\tredirectsMap.put(user, node);\n\t\t\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\t\t\tlog.warning(\"Invalid user's or node's JID: \" + user_jid + \", \" + node_jid);\n\t\t\t\t\t\t}\n\t\t\t\t\t} // end of if (isnext) else\n\t\t\t\t} finally {\n\t\t\t\t\tdata_repo.release(null, rs);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlog.log(Level.CONFIG, \"Loaded \" + redirectsMap.size() + \" redirect definitions from database.\");\n\t\t}\n\t}\n\n\tpublic static class SeeOtherHostDBSDRepositoryBean\n\t\t\textends SDRepositoryBean<SeeOtherHostRepository> {\n\n\t\t@Override\n\t\tprotected Class<?> findClassForDataSource(DataSource dataSource) throws DBInitException {\n\t\t\treturn DataSourceHelper.getDefaultClass(SeeOtherHostRepository.class, dataSource.getResourceUri());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppclient/SeeOtherHostDualIP.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppclient;\n\nimport tigase.cluster.ClusterConnectionManager.REPO_ITEM_UPDATE_TYPE;\nimport tigase.cluster.repo.ClusterRepoItem;\nimport tigase.cluster.repo.ClusterRepoItemEvent;\nimport tigase.db.DBInitException;\nimport tigase.db.DataSource;\nimport tigase.db.DataSourceAware;\nimport tigase.db.DataSourceHelper;\nimport tigase.db.beans.MDRepositoryBeanWithStatistics;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.HandleEvent;\nimport tigase.kernel.beans.*;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.beans.selector.ClusterModeRequired;\nimport tigase.kernel.core.Kernel;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.sql.SQLException;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Extended implementation of SeeOtherHost using redirect information from database based on cluster_nodes table.\n */\n@Bean(name = \"seeOtherHost\", parent = ClientConnectionManager.class, active = true)\n@ClusterModeRequired(active = true)\npublic class SeeOtherHostDualIP\n\t\textends SeeOtherHostHashed\n\t\timplements Initializable, RegistrarBean, UnregisterAware {\n\n\tpublic static final String SEE_OTHER_HOST_FALLBACK_REDIRECTION_KEY =\n\t\t\tCM_SEE_OTHER_HOST_CLASS_PROP_KEY + \"/\" + \"fallback-redirection-host\";\n\tpublic static final String SEE_OTHER_HOST_DATA_SOURCE_KEY = CM_SEE_OTHER_HOST_CLASS_PROP_KEY + \"/\" + \"data-source\";\n\tpublic static final String SEE_OTHER_HOST_DATA_SOURCE_VALUE = SeeOtherHostDualIPSQLRepository.class.getName();\n\tpublic static final String SEE_OTHER_HOST_DB_URL_KEY = CM_SEE_OTHER_HOST_CLASS_PROP_KEY + \"/\" + \"db-url\";\n\tprivate static final Logger log = Logger.getLogger(SeeOtherHostDualIP.class.getName());\n\tprivate final Map<BareJID, BareJID> redirectsMap = Collections.synchronizedMap(new HashMap());\n\t@Inject\n\tprivate EventBus eventBus;\n\t@ConfigField(desc = \"Failback host\", alias = \"failbackHost\")\n\tprivate BareJID fallback_host = null;\n\t@Inject(bean = \"dualIPRepository\")\n\tprivate DualIPRepository repo = null;\n\n\t@Override\n\tpublic BareJID findHostForJID(BareJID jid, BareJID host) {\n\n\t\t// get default host identification\n\t\tBareJID see_other_host = super.findHostForJID(jid, host);\n\n\t\t// lookup resolution in redirection map\n\t\tBareJID redirection;\n\t\tredirection = redirectsMap.get(see_other_host);\n\n\t\tif (redirection == null) {\n\t\t\t// let's try querying the table again\n\t\t\treloadRedirection();\n\t\t\tredirection = redirectsMap.get(see_other_host);\n\t\t}\n\n\t\tif (redirection == null && fallback_host != null) {\n\t\t\t// let's use default fallback redirection if present\n\t\t\tredirection = fallback_host;\n\t\t}\n\n\t\treturn redirection;\n\t}\n\n\t@HandleEvent\n\tpublic void clusterRepoItemEvent(ClusterRepoItemEvent event) {\n\n\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\tlog.log(Level.FINE, \"Procesing ClusterRepoItemEvent: {0}\", new Object[]{event});\n\t\t}\n\n\t\tif (event.getItem() == null || event.getAction() == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tREPO_ITEM_UPDATE_TYPE action = event.getAction();\n\n\t\tClusterRepoItem item = event.getItem();\n\n\t\tBareJID hostname;\n\t\tString hostnameStr = item.getHostname();\n\t\tif (null != hostnameStr) {\n\t\t\thostname = BareJID.bareJIDInstanceNS(hostnameStr);\n\t\t} else {\n\t\t\treturn;\n\t\t}\n\n\t\tBareJID secondary = null;\n\t\tString secondaryStr = item.getSecondaryHostname();\n\t\tif (null != secondaryStr && !secondaryStr.trim().isEmpty()) {\n\t\t\tsecondary = BareJID.bareJIDInstanceNS(secondaryStr);\n\t\t}\n\n\t\tBareJID oldItem;\n\t\tswitch (action) {\n\t\t\tcase ADDED:\n\t\t\tcase UPDATED:\n\t\t\t\toldItem = redirectsMap.put(hostname, secondary);\n\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\tlog.log(Level.FINE,\n\t\t\t\t\t\t\t\"Redirection item :: hostname: {0}, secondary: {1}, added/updated! Replaced: {2}\",\n\t\t\t\t\t\t\tnew Object[]{hostname, secondary, oldItem});\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\n\t\t\tcase REMOVED:\n\t\t\t\toldItem = redirectsMap.remove(hostname);\n\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\tlog.log(Level.FINE, \"Redirection item :: hostname: {0}, {1}\", new Object[]{hostname,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   (oldItem != null\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? \"removed\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: \"was not present in redirection map\")});\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setNodes(List<JID> connectedNodes) {\n\t\tsuper.setNodes(connectedNodes);\n\n\t\treloadRedirection();\n\t}\n\n\t@Override\n\tpublic boolean isRedirectionRequired(BareJID defaultHost, BareJID redirectionHost) {\n\t\treturn redirectsMap.get(defaultHost) != null ? !redirectsMap.get(defaultHost).equals(redirectionHost) : false;\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tsuper.initialize();\n\n\t\teventBus.registerAll(this);\n\n\t\treloadRedirection();\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\teventBus.unregisterAll(this);\n\t}\n\n\tprotected void reloadRedirection() {\n\t\t// reload redirections from\n\t\tif (null == repo) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tfinal Map<BareJID, BareJID> queryAllDB = repo.queryAllDB();\n\t\t\tif (null != queryAllDB) {\n\t\t\t\tredirectsMap.clear();\n\t\t\t\tredirectsMap.putAll(queryAllDB);\n\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\tlog.log(Level.FINE, \"Reloaded redirection items: \" + Arrays.asList(redirectsMap));\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.SEVERE, \"Reloading redirection items failed: \", ex);\n\t\t}\n\t}\n\n\tpublic interface DualIPRepository<T extends DataSource>\n\t\t\textends DataSourceAware<T> {\n\n\t\tpublic static final String HOSTNAME_ID = \"hostname\";\n\n\t\tpublic static final String SECONDARY_HOSTNAME_ID = \"secondary\";\n\n\t\tMap<BareJID, BareJID> queryAllDB() throws SQLException;\n\n\t}\n\n\t@Bean(name = \"dualIPRepository\", parent = SeeOtherHostDualIP.class, active = true)\n\tpublic static class DualIPRepositoryWrapper\n\t\t\textends MDRepositoryBeanWithStatistics<DualIPRepository>\n\t\t\timplements DualIPRepository<DataSource> {\n\n\t\tpublic DualIPRepositoryWrapper() {\n\t\t\tsuper(DualIPRepository.class);\n\t\t}\n\n\t\tpublic Map<BareJID, BareJID> queryAllDB() throws SQLException {\n\t\t\treturn getRepository(\"\").queryAllDB();\n\t\t}\n\n\t\t@Override\n\t\tpublic void setDataSource(DataSource dataSource) {\n\t\t\t// nothing to do\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getDefaultBeanClass() {\n\t\t\treturn DualIPRepositoryWrapperConfigBean.class;\n\t\t}\n\n\t\t@Override\n\t\tprotected Class<? extends DualIPRepository> findClassForDataSource(DataSource dataSource)\n\t\t\t\tthrows DBInitException {\n\t\t\treturn DataSourceHelper.getDefaultClass(DualIPRepository.class, dataSource.getResourceUri());\n\t\t}\n\n\t\tpublic static class DualIPRepositoryWrapperConfigBean\n\t\t\t\textends MDRepositoryConfigBean<DualIPRepository> {\n\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppclient/SeeOtherHostDualIPSQLRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppclient;\n\nimport tigase.db.DataRepository;\nimport tigase.db.Repository;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentSkipListMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * @author Wojtek\n */\n@Repository.Meta(supportedUris = {\"jdbc:[^:]+:.*\"})\npublic class SeeOtherHostDualIPSQLRepository\n\t\timplements SeeOtherHostDualIP.DualIPRepository<DataRepository> {\n\n\tpublic static final String CLUSTER_NODES_TABLE = \"tig_cluster_nodes\";\n\tpublic static final String DB_GET_ALL_DATA_DB_QUERY_KEY =\n\t\t\tSeeOtherHostIfc.CM_SEE_OTHER_HOST_CLASS_PROP_KEY + \"/\" + \"get-all-data-query\";\n\tpublic static final String GET_ALL_QUERY_TIMEOUT_QUERY_KEY =\n\t\t\tSeeOtherHostIfc.CM_SEE_OTHER_HOST_CLASS_PROP_KEY + \"/\" + \"get-all-query-timeout\";\n\tprivate static final String DEF_DB_GET_ALL_DATA_QUERY = \"select * from \" + CLUSTER_NODES_TABLE;\n\tprivate static final int DEF_QUERY_TIME_OUT = 10;\n\tprivate static final Logger log = Logger.getLogger(SeeOtherHostDualIPSQLRepository.class.getName());\n\tprivate DataRepository data_repo = null;\n\t@ConfigField(desc = \"SQL query to retrieve data\")\n\tprivate String get_all_data_query = DEF_DB_GET_ALL_DATA_QUERY;\n\t@ConfigField(desc = \"SQL query timeout\")\n\tprivate int query_timeout = DEF_QUERY_TIME_OUT;\n\n\t@Override\n\tpublic void setDataSource(DataRepository dataSource) {\n\t\tdata_repo = dataSource;\n\t\ttry {\n\t\t\tcheckDB();\n\t\t\tdata_repo.initPreparedStatement(get_all_data_query, get_all_data_query);\n\t\t} catch (SQLException ex) {\n\t\t\tthrow new RuntimeException(\"Repository initialization failed\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Map<BareJID, BareJID> queryAllDB() throws SQLException {\n\t\tMap<BareJID, BareJID> result = new ConcurrentSkipListMap<BareJID, BareJID>();\n\t\tif (data_repo == null) {\n\t\t\treturn null;\n\t\t}\n\t\tPreparedStatement get_all = data_repo.getPreparedStatement(null, get_all_data_query);\n\t\tget_all.setQueryTimeout(DEF_QUERY_TIME_OUT);\n\t\tResultSet rs = null;\n\t\tsynchronized (get_all) {\n\t\t\ttry {\n\t\t\t\trs = get_all.executeQuery();\n\t\t\t\twhile (rs.next()) {\n\t\t\t\t\tString user_jid = rs.getString(HOSTNAME_ID);\n\t\t\t\t\tString node_jid = rs.getString(SECONDARY_HOSTNAME_ID);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tBareJID hostname_hid = BareJID.bareJIDInstance(user_jid);\n\t\t\t\t\t\tBareJID secondary = BareJID.bareJIDInstance(node_jid);\n\t\t\t\t\t\tresult.put(hostname_hid, secondary);\n\t\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\t\tlog.warning(\"Invalid host or secondary hostname JID: \" + user_jid + \", \" + node_jid);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tdata_repo.release(null, rs);\n\t\t\t}\n\t\t}\n\t\tlog.log(Level.CONFIG, \"Loaded \" + result.size() + \" redirect definitions from database.\");\n\t\treturn result;\n\t}\n\n\t/**\n\t * Performs database check\n\t *\n\t */\n\tprivate void checkDB() throws SQLException {\n\t\tif (!data_repo.checkTable(CLUSTER_NODES_TABLE)) {\n\t\t\tthrow new SQLException(\"Nodes redirection table: \" + CLUSTER_NODES_TABLE + \" doesn't exits!\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppclient/SeeOtherHostHashed.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppclient;\n\nimport tigase.eventbus.events.ShutdownEvent;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.selector.ClusterModeRequired;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Default implementation for cluster environment of SeeOtherHostIfc returning redirect host based on the hash value of\n * the user's JID\n *\n * @author Wojtek\n */\n@Bean(name = \"seeOtherHost\", parent = ClientConnectionManager.class, active = true)\n@ClusterModeRequired(active = true)\npublic class SeeOtherHostHashed\n\t\textends SeeOtherHost {\n\n\tprivate static final Logger log = Logger.getLogger(SeeOtherHostHashed.class.getName());\n\t//\tprotected List<BareJID> defaultHost = null;\n\tprotected List<BareJID> connectedNodes = new CopyOnWriteArrayList<BareJID>();\n\n\t@Override\n\tpublic BareJID findHostForJID(BareJID jid, BareJID host) {\n\t\tint hash = Math.abs(jid.hashCode());\n\t\tif (defaultHost != null && !defaultHost.isEmpty() &&\n\t\t\t\tconnectedNodes.contains(defaultHost.get(hash % defaultHost.size()))) {\n\t\t\treturn defaultHost.get(hash % defaultHost.size());\n\t\t} else if (connectedNodes.size() > 0) {\n\t\t\treturn connectedNodes.get(hash % connectedNodes.size());\n\t\t} else {\n\t\t\treturn host;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setNodes(List<JID> connectedNodes) {\n\t\tsynchronized (this) {\n\t\t\tJID[] arr_in = connectedNodes.toArray(new JID[connectedNodes.size()]);\n\t\t\tList<BareJID> list_out = new ArrayList<BareJID>();\n\n\t\t\tfor (int i = 0; i < arr_in.length; i++) {\n\t\t\t\tBareJID jid = BareJID.bareJIDInstanceNS(null, arr_in[i].getDomain());\n\t\t\t\tlist_out.add(jid);\n\t\t\t}\n\n\t\t\tsetConnectedNodes(list_out);\n\t\t}\n\t\tsuper.setNodes(connectedNodes);\n\t}\n\n\t@Override\n\tprotected void nodeShutdown(ShutdownEvent event) {\n\t\tsuper.nodeShutdown(event);\n\t\tsynchronized (this) {\n\t\t\tsetConnectedNodes(new ArrayList<>(this.connectedNodes));\n\t\t}\n\t}\n\n\tprivate void setConnectedNodes(List<BareJID> connectedNodes) {\n\t\tconnectedNodes = filterNodes(connectedNodes);\n\t\tsynchronized (this) {\n\t\t\tCollections.sort(connectedNodes);\n\t\t\tthis.connectedNodes = new CopyOnWriteArrayList<>(connectedNodes);\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"setting list of connected nodes: {0}\", this.connectedNodes);\n\t\t}\n\t}\n\n\tprivate List<BareJID> filterNodes(List<BareJID> list) {\n\t\tIterator<BareJID> it = list.iterator();\n\t\twhile (it.hasNext()) {\n\t\t\tBareJID jid = it.next();\n\t\t\tif (isNodeShutdown(jid)) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"removing node {0} from see-other-host list as it is during shutdown\", jid);\n\t\t\t\t}\n\t\t\t\tit.remove();\n\t\t\t}\n\t\t}\n\t\treturn list;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppclient/SeeOtherHostIfc.java",
    "content": "/*\r\n * Tigase XMPP Server - The instant messaging server\r\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\r\n *\r\n * This program is free software: you can redistribute it and/or modify\r\n * it under the terms of the GNU Affero General Public License as published by\r\n * the Free Software Foundation, version 3 of the License.\r\n *\r\n * This program is distributed in the hope that it will be useful,\r\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n * GNU Affero General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU Affero General Public License\r\n * along with this program. Look for COPYING file in the top folder.\r\n * If not, see http://www.gnu.org/licenses/.\r\n */\r\npackage tigase.server.xmppclient;\r\n\r\nimport tigase.server.Lifecycle;\r\nimport tigase.vhosts.VHostItem;\r\nimport tigase.xml.Element;\r\nimport tigase.xmpp.jid.BareJID;\r\nimport tigase.xmpp.jid.JID;\r\n\r\nimport java.util.List;\r\n\r\n/**\r\n * @author Wojtek\r\n */\r\npublic interface SeeOtherHostIfc\r\n\t\textends Lifecycle {\r\n\r\n\tpublic static final String CM_SEE_OTHER_HOST_CLASS_PROPERTY = \"--cm-see-other-host\";\r\n\r\n\tpublic static final String CM_SEE_OTHER_HOST_CLASS_PROP_KEY = \"cm-see-other-host\";\r\n\r\n\tpublic static final String CM_SEE_OTHER_HOST_CLASS_PROP_DEF_VAL = \"tigase.server.xmppclient.SeeOtherHost\";\r\n\r\n\tpublic static final String CM_SEE_OTHER_HOST_CLASS_PROP_DEF_VAL_CLUSTER = \"tigase.server.xmppclient.SeeOtherHostHashed\";\r\n\r\n\t// default properties\r\n\tpublic static final String CM_SEE_OTHER_HOST_DEFAULT_HOST = CM_SEE_OTHER_HOST_CLASS_PROP_KEY + \"/\" + \"default-host\";\r\n\r\n\tpublic static final String CM_SEE_OTHER_HOST_DEFAULT_PORT = CM_SEE_OTHER_HOST_CLASS_PROP_KEY + \"/\" + \"default-port\";\r\n\r\n\tpublic static final String CM_SEE_OTHER_HOST_ACTIVE = CM_SEE_OTHER_HOST_CLASS_PROP_KEY + \"/\" + \"active\";\r\n\r\n\tpublic static enum Phase {\r\n\t\tOPEN,\r\n\t\tLOGIN,\r\n\t\tOTHER\r\n\t}\r\n\r\n\t/**\r\n\t * Finds an appropriate host for a given JID\r\n\t *\r\n\t * @param jid is a user JID extracted from the stream open attributes\r\n\t * @param host is \"this\" host to which the user is now connected and which calls the method\r\n\t *\r\n\t * @return BareJID of possible host to which the user should connect or NULL\r\n\t */\r\n\tBareJID findHostForJID(BareJID jid, BareJID host);\r\n\r\n\t/**\r\n\t * Sets list of available nodes in cluster environment\r\n\t *\r\n\t * @param nodes current list of nodes\r\n\t */\r\n\tvoid setNodes(List<JID> nodes);\r\n\r\n\t/**\r\n\t * Returns Element object containing stream:error message\r\n\t *\r\n\t * @param xmlns xml namespace of the element\r\n\t * @param destination BareJID address of the redirect destination\r\n\t *\r\n\t * @return element containing stream:error message\r\n\t */\r\n\tdefault Element getStreamError(String xmlns, BareJID destination, Integer port) {\r\n\t\tElement error = new Element(\"stream:error\");\r\n\t\tElement seeOtherHost = new Element(\"see-other-host\", destination.toString() + (port != null ? \":\" + port : \"\"));\r\n\r\n\t\tseeOtherHost.setXMLNS(xmlns);\r\n\t\terror.addChild(seeOtherHost);\r\n\r\n\t\treturn error;\r\n\t}\r\n\r\n\t/**\r\n\t * Performs check whether redirect is enabled in the given phase by default see-other-host redirect is only active\r\n\t * in stream:open phase\r\n\t *\r\n\t * @param vHost vHost for which redirection should be performed\r\n\t * @param ph phase for which the check should be performed\r\n\t *\r\n\t * @return boolean value indicating whether to perform or not redirect for the phase passed as argument\r\n\t */\r\n\tboolean isEnabled(VHostItem vHost, Phase ph);\r\n\r\n\t/**\r\n\t * Method validates whether a redirection for a particular hostname and resulting redirection hastname is required\r\n\t *\r\n\t * @param defaultHost default hostname of the particular machine\r\n\t * @param redirectionHost destination hostname\r\n\t *\r\n\t * @return {@code true} if the redirection is required, otherwise {@code false}\r\n\t */\r\n\tdefault boolean isRedirectionRequired(BareJID defaultHost, BareJID redirectionHost) {\r\n\t\treturn !defaultHost.equals(redirectionHost);\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppclient/StreamErrorCounterIOProcessor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppclient;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.ConnectionManager;\nimport tigase.server.Packet;\nimport tigase.stats.CounterValue;\nimport tigase.stats.StatisticsList;\nimport tigase.xml.Element;\nimport tigase.xmpp.StreamError;\nimport tigase.xmpp.XMPPIOService;\n\nimport java.io.IOException;\nimport java.util.logging.Level;\n\n/**\n * @author andrzej\n */\n@Bean(name = StreamErrorCounterIOProcessor.ID, parent = ClientConnectionManager.class, active = false)\npublic class StreamErrorCounterIOProcessor\n\t\timplements XMPPIOProcessor {\n\n\tpublic static final String ID = \"stream-error-counter\";\n\tprivate String compName;\n\t@Inject\n\tprivate ConnectionManager connectionManager;\n\tprivate ErrorStatisticsHolder holder = new ErrorStatisticsHolder();\n\n\tpublic void setConnectionManager(ConnectionManager connectionManager) {\n\t\tthis.connectionManager = connectionManager;\n\t\tcompName = connectionManager.getName();\n\t}\n\n\t@Override\n\tpublic String getId() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t\tholder.getStatistics(compName, list);\n\t}\n\n\t@Override\n\tpublic Element[] supStreamFeatures(XMPPIOService service) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean processIncoming(XMPPIOService service, Packet packet) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean processOutgoing(XMPPIOService service, Packet packet) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void packetsSent(XMPPIOService service) throws IOException {\n\t}\n\n\t@Override\n\tpublic void processCommand(XMPPIOService service, Packet packet) {\n\t}\n\n\t@Override\n\tpublic boolean serviceStopped(XMPPIOService service, boolean streamClosed) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void streamError(XMPPIOService service, StreamError streamError) {\n\t\tif (streamError == null) {\n\t\t\tstreamError = StreamError.UndefinedCondition;\n\t\t}\n\n\t\tholder.count(streamError);\n\t}\n\n\tpublic static class ErrorStatisticsHolder {\n\n\t\tprivate static final String[] ERROR_NAMES;\n\n\t\tstatic {\n\t\t\tint count = StreamError.values().length;\n\t\t\tERROR_NAMES = new String[count];\n\t\t\tStreamError[] vals = StreamError.values();\n\t\t\tfor (int i = 0; i < vals.length; i++) {\n\t\t\t\tString name = vals[i].getCondition();\n\t\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\t\tfor (String part : name.split(\"-\")) {\n\t\t\t\t\tsb.append(Character.toUpperCase(part.charAt(0)));\n\t\t\t\t\tsb.append(part.substring(1));\n\t\t\t\t}\n\t\t\t\tERROR_NAMES[i] = sb.toString();\n\t\t\t}\n\t\t}\n\n\t\tprivate final CounterValue[] counters;\n\n\t\tpublic static String[] getErrorNames() {\n\t\t\treturn ERROR_NAMES;\n\t\t}\n\n\t\tpublic ErrorStatisticsHolder() {\n\t\t\tcounters = new CounterValue[ERROR_NAMES.length];\n\t\t\tfor (int i = 0; i < counters.length; i++) {\n\t\t\t\tcounters[i] = new CounterValue(ERROR_NAMES[i], Level.FINER);\n\t\t\t}\n\t\t}\n\n\t\tpublic void count(StreamError val) {\n\t\t\tcounters[val.ordinal()].inc();\n\t\t}\n\n\t\tpublic void getStatistics(String compName, StatisticsList list) {\n\t\t\tfor (CounterValue c : counters) {\n\t\t\t\tlist.add(compName, \"StreamErrorStats/\" + c.getName() + \"ErrorsNumber\", c.getValue(), c.getLevel());\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppclient/StreamManagementCommand.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppclient;\n\nimport tigase.server.Command;\nimport tigase.server.Packet;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\npublic enum StreamManagementCommand {\n\tENABLE,\n\tENABLED,\n\tMOVE_STREAM,\n\tSTREAM_MOVED;\n\n\tpublic static StreamManagementCommand fromPacket(Packet packet) {\n\t\treturn valueof(Command.getFieldValue(packet, \"cmd\"));\n\t}\n\n\tpublic static StreamManagementCommand valueof(String cmdId) {\n\t\treturn COMMANDS.get(cmdId);\n\t}\n\t\n\tprivate static final Map<String, StreamManagementCommand> COMMANDS = Arrays.stream(StreamManagementCommand.values()).collect(\n\t\t\tCollectors.toMap(StreamManagementCommand::getId, Function.identity()));\n\n\tprivate final String cmdId;\n\n\tprivate StreamManagementCommand() {\n\t\tthis.cmdId = name().toLowerCase().replace('_', '-');\n\t}\n\n\tpublic Packet create(JID from, JID to) {\n\t\tPacket packet = Command.STREAM_MOVED.getPacket(from, to, StanzaType.set, UUID.randomUUID().toString());\n\t\tCommand.addFieldValue(packet, \"cmd\", getId());\n\t\treturn packet;\n\t}\n\n\tpublic String getId() {\n\t\treturn cmdId;\n\t}\n\t\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppclient/StreamManagementIOProcessor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppclient;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.component.exceptions.ComponentException;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.net.IOServiceListener;\nimport tigase.net.SocketThread;\nimport tigase.server.*;\nimport tigase.stats.StatisticsList;\nimport tigase.util.common.TimerTask;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.MessageCarbons;\nimport tigase.xmpp.jid.JID;\n\nimport java.io.IOException;\nimport java.text.SimpleDateFormat;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.*;\nimport java.util.function.LongSupplier;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Class implements XEP-0198 Stream Management\n *\n * @author andrzej\n */\n@Bean(name = StreamManagementIOProcessor.XMLNS, parent = ClientConnectionManager.class, active = true)\npublic class StreamManagementIOProcessor\n\t\timplements XMPPIOProcessor {\n\n\tpublic static final String XMLNS = \"urn:xmpp:sm:3\";\n\tprivate static final Logger log = Logger.getLogger(StreamManagementIOProcessor.class.getCanonicalName());\n\t// used tag names\n\tprivate static final String ACK_NAME = \"a\";\n\tprivate static final String ENABLE_NAME = \"enable\";\n\tprivate static final String ENABLED_NAME = \"enabled\";\n\tprivate static final String REQ_NAME = \"r\";\n\tprivate static final String RESUME_NAME = \"resume\";\n\tprivate static final String RESUMED_NAME = \"resumed\";\n\n\t// used attribute names\n\tprivate static final String H_ATTR = \"h\";\n\tprivate static final String LOCATION_ATTR = \"location\";\n\tprivate static final String RESUME_ATTR = \"resume\";\n\tprivate static final String MAX_ATTR = \"max\";\n\tprivate static final String PREVID_ATTR = \"previd\";\n\n\t// various strings used as key to store data in maps\n\tprivate static final String ACK_REQUEST_COUNT_KEY = \"ack-request-count\";\n\tprivate static final String ACK_REQUEST_MIN_DELAY_KEY = \"ack-request-min-delay\";\n\tprivate static final int DEF_ACK_REQUEST_COUNT_VAL = 10;\n\tprivate static final String[] DELAY_PATH = {Message.ELEM_NAME, \"delay\"};\n\tprivate static final String DELAY_XMLNS = \"urn:xmpp:delay\";\n\tprivate static final String IGNORE_UNDELIVERED_PRESENCE_KEY = \"ignore-undelivered-presence\";\n\tprivate static final String IN_COUNTER_KEY = XMLNS + \"_in\";\n\tprivate static final String MAX_RESUMPTION_TIMEOUT_KEY = XMLNS + \"_resumption-timeout\";\n\tprivate static final String MAX_RESUMPTION_TIMEOUT_PROP_KEY = \"max-resumption-timeout\";\n\tprivate static final String OUT_COUNTER_KEY = XMLNS + \"_out\";\n\tprivate static final String RESUMPTION_TASK_KEY = XMLNS + \"_resumption-task\";\n\tprivate static final String RESUMPTION_TIMEOUT_PROP_KEY = \"resumption-timeout\";\n\tprivate static final String RESUMPTION_TIMEOUT_START_KEY = \"resumption-timeout-start\";\n\tprivate static final String STREAM_ID_KEY = XMLNS + \"_stream_id\";\n\n\tprivate static final Element[] FEATURES = {new Element(\"sm\", new String[]{\"xmlns\"}, new String[]{XMLNS})};\n\n\tprivate static final SimpleDateFormat formatter;\n\n\tstatic {\n\t\tformatter = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'\");\n\t\tformatter.setTimeZone(TimeZone.getTimeZone(\"UTC\"));\n\t}\n\n\tprivate final ConcurrentHashMap<String, XMPPIOService> services = new ConcurrentHashMap<String, XMPPIOService>();\n\t@ConfigField(desc = \"Number of sent packets after should ask for confirmation of delivery\", alias = ACK_REQUEST_COUNT_KEY)\n\tprivate int ack_request_count = DEF_ACK_REQUEST_COUNT_VAL;\n\t@Inject(bean = \"service\")\n\tprivate ConnectionManager connectionManager;\n\t@ConfigField(desc = \"Ignore undelivered presence packets\", alias = IGNORE_UNDELIVERED_PRESENCE_KEY)\n\tprivate boolean ignoreUndeliveredPresence = true;\n\t@ConfigField(desc = \"Maximal allowed time for session resumption\", alias = MAX_RESUMPTION_TIMEOUT_PROP_KEY)\n\tprivate int max_resumption_timeout = 15 * 60;\n\t@ConfigField(desc = \"Default resumption timeout\", alias = RESUMPTION_TIMEOUT_PROP_KEY)\n\tprivate int resumption_timeout = 60;\n\t@ConfigField(desc = \"Max allowed queue size of unacked packets\", alias = \"max-resumption-queue-size\")\n\tprivate int max_queue_size = 2000;\n\t@ConfigField(desc = \"Maximal burst period for queue size\", alias = \"max-resumption-queue-size-burst-period\")\n\tprivate int max_resumption_queue_burst_period = 60;\n\t@ConfigField(desc = \"Maximal burst queue size ratio\", alias = \"max-resumption-queue-size-burst-ratio\")\n\tprivate int max_resumption_queue_burst_ratio = 20;\n\t@ConfigField(desc = \"Time since last ack received or ack request sent before which ack request should not be sent\", alias = ACK_REQUEST_MIN_DELAY_KEY)\n\tprivate long ack_request_min_delay = 200l;\n\n\tstatic AtomicLong totalQueueSize = new AtomicLong();\n\t/**\n\t * Method returns true if XMPPIOService has enabled SM.\n\t *\n\t */\n\tpublic static boolean isEnabled(XMPPIOService service) {\n\t\treturn service.getSessionData().containsKey(IN_COUNTER_KEY);\n\t}\n\n\tprivate static boolean isResumptionEnabled(XMPPIOService service) {\n\t\treturn service.getSessionData().containsKey(STREAM_ID_KEY);\n\t}\n\n\tpublic StreamManagementIOProcessor() {\n\t}\n\n\t@Override\n\tpublic String getId() {\n\t\treturn XMLNS;\n\t}\n\n\t@Override\n\tpublic Element[] supStreamFeatures(XMPPIOService service) {\n\t\t// user jid is set after authentication and then after resource bind,\n\t\t// so it should be available after authentication\n\t\t// but in rare cases service can be null if connection was already closed..\n\t\tif (service == null || service.getUserJid() == null)\n\t\t\treturn null;\n\t\tif (isEnabled(service)) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn FEATURES;\n\t}\n\t\n\tprivate String enable(XMPPIOService service, boolean withResumption, Integer maxTimeout) {\n\t\tOutQueue outQueue = newOutQueue();\n\t\toutQueue.setAckRequestCount(ack_request_count);\n\t\tservice.getSessionData().putIfAbsent(OUT_COUNTER_KEY, outQueue);\n\t\tservice.getSessionData().putIfAbsent(IN_COUNTER_KEY, newCounter());\n\n\t\tString id = null;\n\t\tint timeout = resumption_timeout;\n\t\tif (resumption_timeout > 0 && withResumption) {\n\t\t\toutQueue.setResumptionEnabled(true);\n\t\t\tif (maxTimeout != null) {\n\t\t\t\ttimeout = Math.min(max_resumption_timeout, maxTimeout);\n\t\t\t}\n\t\t\tid = UUID.randomUUID().toString();\n\t\t\tservice.getSessionData().putIfAbsent(STREAM_ID_KEY, id);\n\t\t\tservice.getSessionData().put(MAX_RESUMPTION_TIMEOUT_KEY, timeout);\n\n\t\t\tservices.put(id, service);\n\t\t}\n\n\t\tPacket cmd = StreamManagementCommand.ENABLED.create(service.getConnectionId(), service.getDataReceiver());\n\t\tcmd.setPacketFrom(service.getConnectionId());\n\t\tcmd.setPacketTo(service.getDataReceiver());\n\t\tCommand.addFieldValue(cmd, \"resumption-id\", id);\n\t\tconnectionManager.processOutPacket(cmd);\n\n\t\treturn id;\n\t}\n\n\t@Override\n\tpublic boolean processIncoming(XMPPIOService service, Packet packet) {\n\t\tif (!isEnabled(service)) {\n\t\t\tif (packet.getXMLNS() != XMLNS) {\n\t\t\t\treturn false;\n\t\t\t} else if (packet.getElemName() == ENABLE_NAME) {\n\t\t\t\tString maxStr = packet.getElement().getAttributeStaticStr(MAX_ATTR);\n\t\t\t\tString id = enable(service, packet.getElement().getAttributeStaticStr(RESUME_ATTR) != null,\n\t\t\t\t\t\t\t\t   maxStr != null ? Integer.parseInt(maxStr) : null);\n\t\t\t\tString location = id != null ? DNSResolverFactory.getInstance().getSecondaryHost() : null;\n\t\t\t\tInteger timeout = (Integer) service.getSessionData().get(MAX_RESUMPTION_TIMEOUT_KEY);\n\t\t\t\t\n\t\t\t\ttry {\n\t\t\t\t\tservice.writeRawData(\"<\" + ENABLED_NAME + \" xmlns='\" + XMLNS + \"'\" +\n\t\t\t\t\t\t\t\t\t\t\t\t (id != null ? \" id='\" + id + \"' \" + RESUME_ATTR + \"='true' \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t MAX_ATTR + \"='\" + timeout + \"'\" : \"\") +\n\t\t\t\t\t\t\t\t\t\t\t\t (location != null ? \" \" + LOCATION_ATTR + \"='\" + location + \"'\" : \"\") +\n\t\t\t\t\t\t\t\t\t\t\t\t \" />\");\n\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\tlog.log(Level.FINE, \"Started StreamManagement with resumption timeout set to = {1} [{0}]\",\n\t\t\t\t\t\t\t\tnew Object[]{service.toString(), (id != null ? resumption_timeout : null)});\n\t\t\t\t\t}\n\t\t\t\t} catch (IOException ex) {\n\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\tlog.log(Level.FINE, service.toString() + \", exception during sending <enabled/>, stopping...\",\n\t\t\t\t\t\t\t\tex);\n\t\t\t\t\t}\n\t\t\t\t\tservice.forceStop();\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t} else if (packet.getElemName() == RESUME_NAME) {\n\t\t\t\tString h = packet.getElement().getAttributeStaticStr(H_ATTR);\n\t\t\t\tString id = packet.getElement().getAttributeStaticStr(PREVID_ATTR);\n\n\t\t\t\ttry {\n\t\t\t\t\tresumeStream(service, id, Integer.parseInt(h));\n\t\t\t\t} catch (IOException ex) {\n\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\tlog.log(Level.FINE, service.toString() + \", exception while resuming stream for user \" +\n\t\t\t\t\t\t\t\tservice.getUserJid() + \" with id \" + id, ex);\n\t\t\t\t\t}\n\n\t\t\t\t\tservice.forceStop();\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\tif (packet.getXMLNS() == XMLNS) {\n\t\t\tif (packet.getElemName() == ACK_NAME) {\n\t\t\t\tString hStr = packet.getAttributeStaticStr(H_ATTR);\n\n\t\t\t\tint h = Integer.parseInt(hStr);\n\t\t\t\tOutQueue outQueue = (OutQueue) service.getSessionData().get(OUT_COUNTER_KEY);\n\t\t\t\tif (outQueue != null) {\n\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\tlog.log(Level.FINE, \"Acking {2} packets, in outQueue: {3} while processing: {1} [{0}]\",\n\t\t\t\t\t\t\t\tnew Object[]{service, packet, h, outQueue.waitingForAck()});\n\t\t\t\t\t}\n\t\t\t\t\toutQueue.ack(h);\n\t\t\t\t} else {\n\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\tlog.log(Level.FINE, \"outQueue already null while processing: {1} [{0}]\",\n\t\t\t\t\t\t\t\tnew Object[]{service, packet});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (packet.getElemName() == REQ_NAME) {\n\t\t\t\tint value = ((Counter) service.getSessionData().get(IN_COUNTER_KEY)).get();\n\n\t\t\t\ttry {\n\t\t\t\t\tservice.writeRawData(\n\t\t\t\t\t\t\t\"<\" + ACK_NAME + \" xmlns='\" + XMLNS + \"' \" + H_ATTR + \"='\" + String.valueOf(value) + \"'/>\");\n\t\t\t\t} catch (IOException ex) {\n\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\tlog.log(Level.FINE, service.toString() + \", exception during sending <a/> as \" +\n\t\t\t\t\t\t\t\t\"response for <r/>, not stopping serivce as it \" +\n\t\t\t\t\t\t\t\t\"will be stopped after processing all incoming data...\", ex);\n\t\t\t\t\t}\n\t\t\t\t\t//service.forceStop();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!isStanza(packet)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t((Counter) service.getSessionData().get(IN_COUNTER_KEY)).inc();\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean processOutgoing(XMPPIOService service, Packet packet) {\n\t\tif (!isEnabled(service) || packet.getXMLNS() == XMLNS) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// stream resumption should queue only stanzas (iq, presence, message)\n\t\tif (!isStanza(packet)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (service.getAuthorisedUserJid().isPresent() && packet.getStanzaTo() == null) {\n\t\t\tpacket.initVars(packet.getStanzaFrom(), (JID)service.getAuthorisedUserJid().get());\n\t\t}\n\n\t\tOutQueue outQueue = (OutQueue) service.getSessionData().get(OUT_COUNTER_KEY);\n\t\tif (outQueue == null) {\n\t\t\tOutQueue.Entry e = new OutQueue.Entry(packet);\n\t\t\tconnectionManager.processUndeliveredPacket(e.getPacketWithStamp(), e.stamp, null);\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Queuing StreamManagement packet: {1}, queue size: {2} [{0}]\", new Object[]{service, packet, outQueue.waitingForAck()});\n\t\t\t}\n\t\t\tif (!outQueue.append(packet, max_queue_size, max_resumption_timeout, max_resumption_queue_burst_period,\n\t\t\t\t\t\t\t\t max_resumption_queue_burst_ratio)) {\n\t\t\t\t// it is too long without confirmation or queue is too big, we need to cancel this connection.\n\t\t\t\ttry {\n\t\t\t\t\tservice.getSessionData().put(RESUMPTION_TIMEOUT_START_KEY, 0L);\n\t\t\t\t\tservice.writeRawData(\"<stream:error xmlns:stream=\\\"http://etherx.jabber.org/streams\\\">\" +\n\t\t\t\t\t\t\t\t\t\t\t\t \"<policy-violation xmlns=\\\"urn:ietf:params:xml:ns:xmpp-streams\\\"/>\" +\n\t\t\t\t\t\t\t\t\t\t\t\t \"<text xmlns=\\\"urn:ietf:params:xml:ns:xmpp-streams\\\">Too many unacked stanzas</text>\" +\n\t\t\t\t\t\t\t\t\t\t\t\t \"</stream:error>\");\n\t\t\t\t} catch (IOException ex) {\n\t\t\t\t\t// we do not care if exception happened as we already are closing this connection\n\t\t\t\t}\n\t\t\t\t// if we will be polite using stop() queues can still grow.. so let's stop being polite..\n\t\t\t\t// in worst case if queue was too large error may not be sent\n\t\t\t\tservice.forceStop();\n\t\t\t}\n\t\t}\n\n\t\treturn service.getSessionData().containsKey(RESUMPTION_TASK_KEY);\n\t}\n\n\t@Override\n\tpublic void packetsSent(XMPPIOService service) throws IOException {\n\t\tif (!isEnabled(service)) {\n\t\t\treturn;\n\t\t}\n\n\t\tOutQueue outQueue = (OutQueue) service.getSessionData().get(OUT_COUNTER_KEY);\n\t\tif (outQueue != null && shouldRequestAck(service, outQueue)) {\n\t\t\toutQueue.sendingRequest();\n\t\t\tservice.writeRawData(\"<\" + REQ_NAME + \" xmlns='\" + XMLNS + \"' />\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processCommand(XMPPIOService service, Packet pc) {\n\t\tif (pc.getType() == StanzaType.error || pc.getType() == StanzaType.result) {\n\t\t\treturn;\n\t\t}\n\t\tStreamManagementCommand cmd = StreamManagementCommand.fromPacket(pc);\n\t\tswitch (cmd) {\n\t\t\tcase STREAM_MOVED:\n\t\t\t\tif (service == null) {\n\t\t\t\t\t// it looks like the sender of this packet already was disconnected, ignoring request\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tString newConn = Command.getFieldValue(pc, \"new-conn-jid\");\n\n\t\t\t\tString id = (String) service.getSessionData().get(STREAM_ID_KEY);\n\n\t\t\t\tJID newConnJid = JID.jidInstanceNS(newConn);\n\t\t\t\tXMPPIOService newService = connectionManager.getXMPPIOService(newConnJid.getResource());\n\n\t\t\t\t// if connection was closed during resumption, then close\n\t\t\t\t// old connection as it would not be able to resume\n\t\t\t\tif (newService != null) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"stream for user {2} moved from {0} to {1}\",\n\t\t\t\t\t\t\t\tnew Object[]{service.getConnectionId(), newService.getConnectionId(),\n\t\t\t\t\t\t\t\t\t\t\t newService.getUserJid()});\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tnewService.setUserJid(service.getUserJid());\n\t\t\t\t\t\tif (!\"false\".equals(Command.getFieldValue(pc, \"send-response\"))) {\n\t\t\t\t\t\t\tCounter inCounter = (Counter) newService.getSessionData().get(IN_COUNTER_KEY);\n\t\t\t\t\t\t\tnewService.writeRawData(\n\t\t\t\t\t\t\t\t\t\"<\" + RESUMED_NAME + \" xmlns='\" + XMLNS + \"' \" + PREVID_ATTR + \"='\" + id + \"' \" + H_ATTR +\n\t\t\t\t\t\t\t\t\t\t\t\"='\" + inCounter.get() + \"' />\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tservice.getSessionData().put(\"stream-closed\", \"stream-closed\");\n\t\t\t\t\t\tservices.put(id, newService);\n\n\t\t\t\t\t\t// resending packets thru new connection\n\t\t\t\t\t\tOutQueue outQueue = (OutQueue) newService.getSessionData().get(OUT_COUNTER_KEY);\n\t\t\t\t\t\tList<OutQueue.Entry> packetsToResend = new ArrayList<OutQueue.Entry>(outQueue.getQueue());\n\t\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\t\tlog.log(Level.FINE, \"resuming stream with id = {1} resending unacked packets = {2} [{0}]\",\n\t\t\t\t\t\t\t\t\tnew Object[]{service, id, outQueue.waitingForAck()});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (OutQueue.Entry entry : packetsToResend) {\n\t\t\t\t\t\t\tPacket packetToResend = entry.getPacketWithStamp();\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"resuming stream with id = {1} resending unacked packet = {2} [{0}]\",\n\t\t\t\t\t\t\t\t\t\tnew Object[]{service, id, packetToResend});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tnewService.addPacketToSend(packetToResend);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// if there is any packet waiting we need to write them to socket\n\t\t\t\t\t\t// and to do that we need to call processWaitingPackets();\n\t\t\t\t\t\tif (!packetsToResend.isEmpty()) {\n\t\t\t\t\t\t\tif (newService.writeInProgress.tryLock()) {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tnewService.processWaitingPackets();\n\t\t\t\t\t\t\t\t\tSocketThread.addSocketService(newService);\n\t\t\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\t\t\tlog.log(Level.WARNING, newService + \"Exception during writing packets: \", e);\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tnewService.stop();\n\t\t\t\t\t\t\t\t\t} catch (Exception e1) {\n\t\t\t\t\t\t\t\t\t\tlog.log(Level.WARNING, newService + \"Exception stopping XMPPIOService: \", e1);\n\t\t\t\t\t\t\t\t\t}    // end of try-catch\n\t\t\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\t\t\tnewService.writeInProgress.unlock();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (IOException ex) {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\t\t\"could not confirm session resumption for user = \" + newService.getUserJid(), ex);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// remove new connection if resumption failed\n\t\t\t\t\t\tservices.remove(id, service);\n\t\t\t\t\t\tservices.remove(id, newService);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\t\"no new service available for user {0} to resume from {1},\" + \" already closed?\",\n\t\t\t\t\t\t\t\tnew Object[]{service.getUserJid(), service});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"closing old service {0} for user {1}\",\n\t\t\t\t\t\t\tnew Object[]{service, service.getUserJid()});\n\t\t\t\t}\n\n\t\t\t\t// stopping old service\n\t\t\t\tconnectionManager.serviceStopped(service);\n\t\t\t\tbreak;\n\t\t\tcase MOVE_STREAM: {\n\t\t\t\tString resumptionId = Command.getFieldValue(pc, \"resumption-id\");\n\t\t\t\tint h = Integer.parseInt(Command.getFieldValue(pc, \"h\"));\n\t\t\t\ttry {\n\t\t\t\t\tString oldConnId = moveStream(service, resumptionId, h);\n\n\t\t\t\t\tPacket response = pc.okResult(new Element(Command.COMMAND_EL, new String[]{\"xmlns\"}, new String[]{Command.XMLNS}), 0);\n\t\t\t\t\tCounter inCounter = (Counter) service.getSessionData().get(IN_COUNTER_KEY);\n\t\t\t\t\tCommand.addFieldValue(response, \"h\", String.valueOf(inCounter.counter));\n\t\t\t\t\tconnectionManager.processOutPacket(response);\n\n\t\t\t\t\tresponse = StreamManagementCommand.STREAM_MOVED.create(service.getConnectionId(), service.getDataReceiver());\n\t\t\t\t\tresponse.setPacketFrom(service.getConnectionId());\n\t\t\t\t\tresponse.setPacketTo(service.getDataReceiver());\n\t\t\t\t\tCommand.addFieldValue(response, \"old-conn-jid\", oldConnId);\n\t\t\t\t\tCommand.addFieldValue(response, \"send-response\", \"false\");\n\t\t\t\t\tconnectionManager.processOutPacket(response);\n\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconnectionManager.processOutPacket(\n\t\t\t\t\t\t\t\tAuthorization.ITEM_NOT_FOUND.getResponseMessage(pc, ex.getMessage(), false));\n\t\t\t\t\t} catch (PacketErrorTypeException e) {\n\t\t\t\t\t\t// nothing to do..\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t\tcase ENABLE: {\n\t\t\t\tString maxStr = Command.getFieldValue(pc, \"max\");\n\t\t\t\tInteger max = maxStr != null ? Integer.parseInt(maxStr) : null;\n\t\t\t\tboolean withResumption = Command.getCheckBoxFieldValue(pc, \"resume\");\n\t\t\t\tString resumptionId = enable(service, withResumption, max);\n\n\t\t\t\tPacket response = pc.okResult(new Element(Command.COMMAND_EL, new String[]{\"xmlns\"}, new String[]{Command.XMLNS}),0);\n\t\t\t\tif (resumptionId != null) {\n\t\t\t\t\tCommand.addFieldValue(response, \"id\", resumptionId);\n\n\t\t\t\t\tCommand.addFieldValue(response, \"location\", DNSResolverFactory.getInstance().getSecondaryHost());\n\t\t\t\t\tInteger timeout = (Integer) service.getSessionData().get(MAX_RESUMPTION_TIMEOUT_KEY);\n\t\t\t\t\tif (timeout != null) {\n\t\t\t\t\t\tCommand.addFieldValue(response,\"max\", String.valueOf(timeout));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconnectionManager.processOutPacket(response);\n\t\t\t}\n\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean serviceStopped(XMPPIOService service, boolean streamClosed) {\n\t\tif (!isEnabled(service)) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Service stopped - StreamManagement disabled [{0}]\", new Object[]{service});\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tString id = (String) service.getSessionData().get(STREAM_ID_KEY);\n\n\t\tif (streamClosed) {\n\t\t\tservice.getSessionData().remove(STREAM_ID_KEY);\n\t\t}\n\n//\t\ttry {\n//\t\t\tthrow new Exception();\n//\t\t} catch (Throwable ex) {\n//\t\t\tlog.log(Level.WARNING, \"resumption timeout started, stream close = \" + streamClosed, ex);\n//\t\t\tex.printStackTrace();\n//\t\t}\n\t\tLong resumptionTimeoutStart = (Long) service.getSessionData().get(RESUMPTION_TIMEOUT_START_KEY);\n\t\tif (resumptionTimeoutStart != null) {\n\t\t\tfinal boolean isBeyondResumptionTimeout = (System.currentTimeMillis() - resumptionTimeoutStart) > (1 * resumption_timeout * 1000);\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\"Service stopped - checking resumption timeout, resumptionTimeoutStart: {1}, resumption_timeout: {2}, streamClosed: {3} [{0}]\",\n\t\t\t\t\t\tnew Object[]{service, resumptionTimeoutStart, resumption_timeout, streamClosed});\n\t\t\t}\n\t\t\t// if resumptionTimeoutStart is set let's check if resumption was\n\t\t\t// not started for longer time than twice value of resumption_timeout\n\t\t\t// wojtek@2021-09-03: after discussion with andrzej we decided to use \"once\" times resumption timeout\n\t\t\t// as there wasn't sufficient reason to use double the value - if the resumption was not finished within\n\t\t\t// established timeout then it's considered failed.\n\t\t\tif (isBeyondResumptionTimeout || streamClosed) {\n\n\t\t\t\tif (id == null) {\n\t\t\t\t\t// connection already removed by another thread. It will take care of the rest\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t// if so we should assume that resumption failed so we should\n\t\t\t\t// send errors, remove reference to service and stop this service\n\t\t\t\tservices.remove(id, service);\n\t\t\t\tservice.clearWaitingPackets();\n\t\t\t\tconnectionManager.serviceStopped(service);\n\t\t\t\t// for case in which task was started but later </stream:stream> was found in remaining data\n\t\t\t\tTimerTask timerTask = (TimerTask) service.getSessionData().get(RESUMPTION_TASK_KEY);\n\t\t\t\tif (timerTask != null) {\n\t\t\t\t\ttimerTask.cancel();\n\t\t\t\t}\n\t\t\t\tsendErrorsForQueuedPackets(service);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t// some buggy client (ie. Psi) may close stream without sending stream\n\t\t// close which forces us to thread this stream as broken and waiting for\n\t\t// resumption but those clients are not compatible with XEP-0198 and\n\t\t// resumption so this should not happen\n\t\tif (isResumptionEnabled(service)) {\n\t\t\tif (service.getSessionData().getOrDefault(XMPPIOService.STREAM_CLOSING, false) == Boolean.TRUE) {\n\t\t\t\tservices.remove(id, service);\n\t\t\t\tconnectionManager.serviceStopped(service);\n\t\t\t\tsendErrorsForQueuedPackets(service);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (!services.containsKey(id)) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Service stopped - resumption enabled but service not available [{0}]\",\n\t\t\t\t\t\t\tnew Object[]{service});\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// ConnectionManager must not be notified about closed connection\n\t\t\t// but connection needs to be closed so this is this case we still\n\t\t\t// return false to call forceStop but we remove IOServiceListener\n\t\t\tservice.setIOServiceListener((IOServiceListener) null);\n\n\t\t\tint resumptionTimeout = (Integer) service.getSessionData().get(MAX_RESUMPTION_TIMEOUT_KEY);\n\t\t\tsynchronized (service) {\n\t\t\t\tif (!service.getSessionData().containsKey(RESUMPTION_TASK_KEY)) {\n\t\t\t\t\tTimerTask timerTask = new ResumptionTimeoutTask(service);\n\t\t\t\t\tservice.getSessionData().put(RESUMPTION_TASK_KEY, timerTask);\n\t\t\t\t\tconnectionManager.addTimerTask(timerTask, resumptionTimeout * 1000);\n\n\t\t\t\t\t// set timestamp of begining of resumption to be able to detect\n\t\t\t\t\t// if something went wrong during resumption and service is\n\t\t\t\t\t// still kept in connection manager services as active service\n\t\t\t\t\t// after twice as long as resumption timeout\n\t\t\t\t\tservice.getSessionData().put(RESUMPTION_TIMEOUT_START_KEY, System.currentTimeMillis());\n\t\t\t\t\tservice.clearWaitingPackets();\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Service stopped - resumption enabled and timeout started [{0}]\",\n\t\t\t\t\t\t\t\tnew Object[]{service});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false;\n\t\t} else if (id != null) {\n\t\t\tservices.remove(id, service);\n\t\t}\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Service stopped - resumption disabled, sending unacked packets [{0}]\",\n\t\t\t\t\tnew Object[]{service});\n\t\t}\n\n\t\tservice.clearWaitingPackets();\n\t\tconnectionManager.serviceStopped(service);\n\t\tsendErrorsForQueuedPackets(service);\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t\tif (list.checkLevel(Level.FINEST)) {\n\t\t\tlist.add(connectionManager.getName() + \"/\" + getId(), \"Number of resume services\", services.size(), Level.FINEST);\n\t\t\tlist.add(connectionManager.getName() + \"/\" + getId(), \"Total size of all queues\", totalQueueSize.get(), Level.FINEST);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void streamError(XMPPIOService service, StreamError streamErrorName) {\n\t}\n\n\t/**\n\t * Override this method to define a custom behaviour for request ack. The default implementation will request an ack\n\t * if there are more than {@link #ack_request_count} packets waiting since last request for ack and last ack request\n\t * was not sent in last X ms.\n\t */\n\tprotected boolean shouldRequestAck(XMPPIOService service, OutQueue outQueue) {\n\t\t// send request for ack if there is at least X message since last ack or request for ack\n\t\tif (Math.min(outQueue.unackedSinceLastRequest(), outQueue.waitingForAck()) >= ack_request_count) {\n\t\t\t// do not send ack request if there was less than X ms since previous request\n\t\t\tif (outQueue.gotAckOrSentRequestSince(System.currentTimeMillis() - ack_request_min_delay)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprotected Counter newCounter() {\n\t\treturn new Counter();\n\t}\n\n\tprotected OutQueue newOutQueue() {\n\t\treturn new OutQueue();\n\t}\n\n\tprotected boolean isStanza(Packet packet) {\n\t\treturn switch (packet.getElemName()) {\n\t\t\tcase Iq.ELEM_NAME, Message.ELEM_NAME, Presence.ELEM_NAME -> true;\n\t\t\tdefault -> false;\n\t\t};\n\t}\n\n\tpublic static class ResumptionException extends ComponentException {\n\n\t\tpublic ResumptionException(Authorization errorCondition) {\n\t\t\tsuper(errorCondition);\n\t\t}\n\t}\n\n\tprivate String moveStream(XMPPIOService service, String id, int h) throws IOException, ResumptionException {\n\t\tXMPPIOService oldService = services.get(id);\n\t\tif (oldService == null || !isSameUser(oldService, service)) {\n\t\t\t// should send failed!\n\t\t\tthrow new ResumptionException(Authorization.ITEM_NOT_FOUND);\n\t\t}\n\n\t\t// if stream has resource binded then we should not resume\n\t\tif (service.getUserJid() != null && JID.jidInstanceNS(service.getUserJid()).getResource() != null) {\n\t\t\tthrow new ResumptionException(Authorization.UNEXPECTED_REQUEST);\n\t\t}\n\n\t\tif (services.remove(id, oldService)) {\n\t\t\tsynchronized (oldService) {\n\t\t\t\t// we should cancel the timer but not remove it as in other case Watchdog can \"recreate\" timer task\n\t\t\t\t// and restart resumption process\n\t\t\t\tTimerTask timerTask = (TimerTask) oldService.getSessionData().get(RESUMPTION_TASK_KEY);\n\t\t\t\tif (timerTask != null) {\n\t\t\t\t\ttimerTask.cancel();\n\t\t\t\t}\n\t\t\t\toldService.clearWaitingPackets();\n\t\t\t}\n\n\t\t\t// get old out queue\n\t\t\tOutQueue outQueue = (OutQueue) oldService.getSessionData().get(OUT_COUNTER_KEY);\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE,\n\t\t\t\t\t\t\"Resuming stream with id = {1} with {2} packets waiting for ack, local h = {3} and remote h = {4} [{0}]\",\n\t\t\t\t\t\tnew Object[]{service, id, outQueue.waitingForAck(), outQueue.get(), h});\n\t\t\t}\n\t\t\toutQueue.ack(h);\n\n\t\t\t// move required data from old XMPPIOService session data to new service session data\n\t\t\tservice.getSessionData().put(OUT_COUNTER_KEY, outQueue);\n\t\t\tservice.getSessionData()\n\t\t\t\t\t.put(MAX_RESUMPTION_TIMEOUT_KEY, oldService.getSessionData().get(MAX_RESUMPTION_TIMEOUT_KEY));\n\t\t\tservice.getSessionData().put(IN_COUNTER_KEY, oldService.getSessionData().get(IN_COUNTER_KEY));\n\t\t\tservice.getSessionData().put(STREAM_ID_KEY, oldService.getSessionData().get(STREAM_ID_KEY));\n\n\t\t\treturn oldService.getConnectionId().toString();\n\t\t} else {\n\t\t\tthrow new ResumptionException(Authorization.ITEM_NOT_FOUND);\n\t\t}\n\t}\n\n\t/**\n\t * Method responsible for starting process of stream resumption\n\t */\n\tprivate void resumeStream(XMPPIOService service, String id, int h) throws IOException {\n\t\ttry {\n\t\t\tString oldConnId = moveStream(service, id,h);\n\t\t\t// send notification to session manager about change of connection\n\t\t\t// used for session\n\t\t\tPacket cmd = StreamManagementCommand.STREAM_MOVED.create(service.getConnectionId(), service.getDataReceiver());\n\t\t\tcmd.setPacketFrom(service.getConnectionId());\n\t\t\tcmd.setPacketTo(service.getDataReceiver());\n\t\t\tCommand.addFieldValue(cmd, \"old-conn-jid\", oldConnId);\n\t\t\tCommand.addFieldValue(cmd, \"send-response\", \"true\");\n\t\t\tconnectionManager.processOutPacket(cmd);\n\t\t} catch (ResumptionException ex) {\n\t\t\tservice.writeRawData(\"<failed xmlns='\" + XMLNS + \"'>\" + \"<\" + ex.getName() + \" xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>\" +\n\t\t\t\t\t\t\t\t\t\t \"</failed>\");\n\t\t}\n\t}\n\n\t/**\n\t * Verifies if connections are authenticate for same bare jid\n\t * @return true - only when bare jids are the same\n\t */\n\tprivate boolean isSameUser(XMPPIOService oldService, XMPPIOService newService) {\n\t\tif (oldService.getUserJid() == null || newService.getUserJid() == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tJID oldUserJid = JID.jidInstanceNS(oldService.getUserJid());\n\t\tJID newUserJid = JID.jidInstanceNS(newService.getUserJid());\n\n\t\treturn oldUserJid.getBareJID().equals(newUserJid.getBareJID());\n\t}\n\n\t/**\n\t * Method responsible for sending recipient-unavailable error for all not acked packets\n\t *\n\t */\n\tprivate void sendErrorsForQueuedPackets(XMPPIOService service) {\n\t\tservice.clearWaitingPackets();\n\n\t\tOutQueue outQueue = (OutQueue) service.getSessionData().remove(OUT_COUNTER_KEY);\n\t\tif (outQueue != null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\"Service stopped - returning errors for {1} packets in queue [{0}]\",\n\t\t\t\t\t\tnew Object[]{service, outQueue.waitingForAck()});\n\t\t\t}\n\t\t\tOutQueue.Entry e = null;\n\n\t\t\twhile ((e = outQueue.queue.poll()) != null) {\n\t\t\t\tconnectionManager.processUndeliveredPacket(e.getPacketWithStamp(), e.stamp, null);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Counter class implements proper counter with overflow from 2^32-1 to 0\n\t */\n\tpublic static class Counter {\n\n\t\tprivate int counter = 0;\n\n\t\t/**\n\t\t * Increment counter\n\t\t */\n\t\tpublic void inc() {\n\t\t\tcounter++;\n\t\t\tif (counter < 0) {\n\t\t\t\tcounter = 0;\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Get value of counter.\n\t\t */\n\t\tpublic int get() {\n\t\t\treturn counter;\n\t\t}\n\n\t\t/**\n\t\t * Sets value of a counter - use only for testing!\n\t\t *\n\t\t */\n\t\tprotected void setCounter(int value) {\n\t\t\tthis.counter = value;\n\t\t}\n\t}\n\n\t/**\n\t * OutQueue class implements queue of outgoing packets waiting for ack with implementation of removing acked\n\t * elements when id of acked packet is passed\n\t */\n\tpublic static class OutQueue\n\t\t\textends Counter {\n\n\t\tprivate final ArrayDeque<Entry> queue = new ArrayDeque<Entry>();\n\t\tprivate long lastConfirmationAt = 0;\n\t\tprivate long lastRequestSentAt = 0;\n\t\tprivate int lastRequestSentFor = 0;\n\t\tprivate int ackRequestCount = DEF_ACK_REQUEST_COUNT_VAL;\n\t\tprivate final LongSupplier timestampSupplier;\n\n\t\tpublic OutQueue() {\n\t\t\tthis(System::currentTimeMillis);\n\t\t}\n\n\t\tprotected OutQueue(LongSupplier timestampSupplier) {\n\t\t\tthis.timestampSupplier = timestampSupplier;\n\t\t}\n\t\t\n\t\t/**\n\t\t * Method determines if we should check packets for falling within timeout. Currently we only check\n\t\t * the timeout if the queue size is bigger if the maximum count of packets that can be send\n\t\t * to the server without the server requiring the ack. Otherwise, it could happen that idle or low\n\t\t * traffic clients could be disconnected if the packets waiting for the ack were very old thus falling\n\t\t * outside timeout but still few enough that wouldn't trigger ack request.\n\t\t *\n\t\t * @return {@code true} if there are fewer packets than maximum allow packet count that doesn't trigger\n\t\t * server ack request, {@code false} otherwise.\n\t\t */\n\t\tprivate boolean shouldCheckTimeout() {\n\t\t\treturn queue.size() > (ackRequestCount + 3);\n\t\t}\n\n\t\t/**\n\t\t * Append packet to waiting for ack queue\n\t\t *\n\t\t */\n\t\t@Deprecated\n\t\t@TigaseDeprecated(removeIn = \"9.0.0\", since = \"8.2.0\", note = \"Use method with maxQueueSize\")\n\t\tpublic boolean append(Packet packet, int timeoutInSec) {\n\t\t\treturn append(packet, Integer.MAX_VALUE, timeoutInSec);\n\t\t}\n\n\t\t/**\n\t\t * Append packet to waiting for ack queue\n\t\t *\n\t\t */\n\t\t@Deprecated\n\t\t@TigaseDeprecated(removeIn = \"9.0.0\", since = \"8.2.0\", note = \"Use method with maxQueueSize\")\n\t\tpublic boolean append(Packet packet, int maxQueueSize, int timeoutInSec) {\n\t\t\treturn append(packet, maxQueueSize, timeoutInSec, 0, 1);\n\t\t}\n\n\t\t/**\n\t\t * Append packet to waiting for ack queue\n\t\t *\n\t\t */\n\t\tpublic boolean append(Packet packet, int maxQueueSize, int timeoutInSec, int burstPeriodInSec, int burstRatio) {\n\t\t\tif (!packet.wasProcessedBy(XMLNS)) {\n\n\t\t\t\tlong currentTimeMillis = timestampSupplier.getAsLong();\n\t\t\t\t// check if queue size does not exceed limit\n\t\t\t\tif (queue.size() >= maxQueueSize) {\n\t\t\t\t\tEntry first = queue.peekFirst();\n\t\t\t\t\tLong period = first != null ? currentTimeMillis - first.stamp : null;\n\t\t\t\t\t// queue is exceeded, but we should allow burst, ie. during reconnection\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.finest(() -> \"Queue size exceeded maxQueueSize: \" + maxQueueSize + \", current size: \" + queue.size() + \", period: \" + period + \"ms\" + \", burst period: \" + burstPeriodInSec + \", burst ratio: \" + burstRatio);\n\t\t\t\t\t}\n\t\t\t\t\tif (period == null || period > ((long) burstPeriodInSec * 1000) || queue.size() >= (maxQueueSize * burstRatio)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (shouldCheckTimeout()) {\n\t\t\t\t\tEntry first = queue.peekFirst();\n\t\t\t\t\tif (first != null && (currentTimeMillis - first.stamp > ((long)timeoutInSec * 1000))) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tpacket.processedBy(XMLNS);\n\t\t\t\tqueue.offer(new Entry(packet, timestampSupplier));\n\t\t\t\ttotalQueueSize.incrementAndGet();\n\t\t\t\tinc();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t/**\n\t\t * Confirm delivery of packets up to count passed as value\n\t\t *\n\t\t */\n\t\tpublic void ack(int value) {\n\t\t\tint count = get() - value;\n\n\t\t\tif (count < 0) {\n\t\t\t\tcount = (Integer.MAX_VALUE - value) + get() + 1;\n\t\t\t}\n\n\t\t\tlastConfirmationAt = timestampSupplier.getAsLong();\n\n\t\t\twhile (count < queue.size()) {\n\t\t\t\tqueue.poll();\n\t\t\t\ttotalQueueSize.decrementAndGet();\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Method notifies class that request for ack is being sent\n\t\t */\n\t\tpublic void sendingRequest() {\n\t\t\tlastRequestSentAt = timestampSupplier.getAsLong();\n\t\t\tlastRequestSentFor = get();\n\t\t}\n\n\t\t/**\n\t\t * Sets ack request count value\n\t\t * @param ackRequestCount\n\t\t */\n\t\tpublic void setAckRequestCount(int ackRequestCount) {\n\t\t\tthis.ackRequestCount = ackRequestCount;\n\t\t}\n\n\t\t@Deprecated\n\t\t@TigaseDeprecated(removeIn = \"9.0.0\", since = \"8.3.0\", note = \"Method will not be called any more\")\n\t\tpublic void setResumptionEnabled(boolean enabled) {\n\t\t}\n\n\t\t/**\n\t\t * Returns size of queue containing packets waiting for ack\n\t\t */\n\t\tpublic int waitingForAck() {\n\t\t\treturn queue.size();\n\t\t}\n\n\t\t/**\n\t\t * Method returns internal queue with packets waiting for ack - use testing only!\n\t\t */\n\t\tprotected ArrayDeque<Entry> getQueue() {\n\t\t\treturn queue;\n\t\t}\n\n\t\t/**\n\t\t * Method returns timestamp of the last received ack.\n\t\t * @return\n\t\t */\n\t\tprotected long getLastConfirmationAt() {\n\t\t\treturn lastConfirmationAt;\n\t\t}\n\n\t\t/**\n\t\t * Method returns timestamp of the last request for ack being sent.\n\t\t * @return\n\t\t */\n\t\tprotected long getLastRequestSentAt() {\n\t\t\treturn lastRequestSentAt;\n\t\t}\n\n\t\t/**\n\t\t * Method checks if any ack was received or request for ack was sent since passed timestamp.\n\t\t * @param since\n\t\t * @return\n\t\t */\n\t\tprotected boolean gotAckOrSentRequestSince(long since) {\n\t\t\treturn Math.max(lastConfirmationAt, lastRequestSentAt) > since;\n\t\t}\n\n\t\t/**\n\t\t * Method returns no. of unacked stanzas since last request for ack was sent.\n\t\t * @return\n\t\t */\n\t\tprotected int unackedSinceLastRequest() {\n\t\t\tint count = get();\n\t\t\tif (count >= lastRequestSentFor) {\n\t\t\t\treturn count - lastRequestSentFor;\n\t\t\t} else {\n\t\t\t\treturn count + (Integer.MAX_VALUE - lastRequestSentFor);\n\t\t\t}\n\t\t}\n\n\t\tpublic static class Entry {\n\n\t\t\tprivate final Packet packet;\n\t\t\tprivate final long stamp;\n\n\t\t\tpublic Entry(Packet packet) {\n\t\t\t\tthis(packet, System::currentTimeMillis);\n\t\t\t}\n\n\t\t\tprotected Entry(Packet packet, LongSupplier stampSupplier) {\n\t\t\t\tthis.packet = packet;\n\t\t\t\tthis.stamp = stampSupplier.getAsLong();\n\t\t\t}\n\n\t\t\tpublic Packet getPacketWithStamp() {\n\t\t\t\tPacket result = packet.copyElementOnly();\n\t\t\t\tif (result.getElemName() != Iq.ELEM_NAME && !result.isXMLNSStaticStr(DELAY_PATH, DELAY_XMLNS)) {\n\t\t\t\t\tString stamp = null;\n\t\t\t\t\tsynchronized (formatter) {\n\t\t\t\t\t\tstamp = formatter.format(this.stamp);\n\t\t\t\t\t}\n\t\t\t\t\tString from = null;\n\t\t\t\t\tif (packet.getStanzaTo() != null) {\n\t\t\t\t\t\tfrom = packet.getStanzaTo().getDomain();\n\t\t\t\t\t} else if (packet.getPacketTo() != null) {\n\t\t\t\t\t\tfrom = packet.getPacketTo().getDomain();\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// if we still do not have anything just set from to the cluster node name\n\t\t\t\t\t\t// (same as result.getPacket().getDomain())\n\t\t\t\t\t\tfrom = DNSResolverFactory.getInstance().getDefaultHost();\n\t\t\t\t\t\tif (log.isLoggable(Level.WARNING)) {\n\t\t\t\t\t\t\tlog.log(Level.WARNING, \"unacked packet without stanzaTo: {0}, and packetTo: {1}; setting from to: {2}; packet: {3} \",\n\t\t\t\t\t\t\t\t\tnew Object[]{packet.getStanzaTo(), packet.getPacketTo(), from, packet.toString()});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tElement x = new Element(\"delay\", new String[]{\"from\", \"stamp\", \"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\tnew String[]{from, stamp, \"urn:xmpp:delay\"});\n\t\t\t\t\tElement carbon = result.getElement().findChild(e -> e.getXMLNS() == MessageCarbons.XMLNS);\n\t\t\t\t\tif (carbon == null) {\n\t\t\t\t\t\tresult.getElement().addChild(x);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tElement forwarded = carbon.getChild(\"forwarded\", \"urn:xmpp:forward:0\");\n\t\t\t\t\t\tif (forwarded != null) {\n\t\t\t\t\t\t\tElement message = forwarded.getChild(\"message\");\n\t\t\t\t\t\t\tif (message != null) {\n\t\t\t\t\t\t\t\tmessage.addChild(x);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * ResumptionTimeoutTask class is used for handing of timeout used during session resumption\n\t */\n\tprivate class ResumptionTimeoutTask\n\t\t\textends TimerTask {\n\n\t\tprivate final XMPPIOService service;\n\n\t\tpublic ResumptionTimeoutTask(XMPPIOService service) {\n\t\t\tthis.service = service;\n\t\t}\n\n\t\t@Override\n\t\tpublic void cancel(boolean mayInterruptIfRunning) {\n\t\t\tif (!isCancelled()) {\n\t\t\t\tsuper.cancel(mayInterruptIfRunning);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tString id = (String) service.getSessionData().get(STREAM_ID_KEY);\n\t\t\tif (services.remove(id, service)) {\n\t\t\t\t//service.getSessionData().put(SERVICE_STOP_ALLOWED_KEY, true);\n\t\t\t\tservice.clearWaitingPackets();\n\t\t\t\tconnectionManager.serviceStopped(service);\n\t\t\t\tsendErrorsForQueuedPackets(service);\n\t\t\t}\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppclient/XMPPIOProcessor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppclient;\n\nimport tigase.server.Packet;\nimport tigase.stats.StatisticsList;\nimport tigase.xml.Element;\nimport tigase.xmpp.StreamError;\nimport tigase.xmpp.XMPPIOService;\n\nimport java.io.IOException;\n\n/**\n * @author andrzej\n */\npublic interface XMPPIOProcessor {\n\n\t/**\n\t * Returns identifier of processor\n\t */\n\tString getId();\n\n\t/**\n\t * Returns statistics generated by this processor\n\t *\n\t */\n\tvoid getStatistics(StatisticsList list);\n\n\t/**\n\t * Returns array of features added by this processor\n\t *\n\t */\n\tElement[] supStreamFeatures(XMPPIOService service);\n\n\t/**\n\t * Process packets read from socket as they are sent to SessionManager.\n\t * @return true if packet should not be forwarded\n\t */\n\tboolean processIncoming(XMPPIOService service, Packet packet);\n\n\t/**\n\t * Process outgoing packets as they are added to XMPPIOService outgoing packets queue.\n\t * @return true if packet should be removed\n\t */\n\tboolean processOutgoing(XMPPIOService service, Packet packet);\n\n\t/**\n\t * Method is called when all waiting data was written to socket.\n\t */\n\tvoid packetsSent(XMPPIOService service) throws IOException;\n\n\t/**\n\t * Process command execution which may be sent from other component and should be processed by processor\n\t *\n\t */\n\tvoid processCommand(XMPPIOService service, Packet packet);\n\n\t/**\n\t * Method called when XMPPIOService is closed.\n\t * @return true if connecton manager should not be notified about stopping of this service\n\t */\n\tboolean serviceStopped(XMPPIOService service, boolean streamClosed);\n\n\t/**\n\t * Method called when XMPP stream error is about to be sent\n\t *\n\t */\n\tvoid streamError(XMPPIOService service, StreamError streamError);\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppcomponent/ComponentConnectionManager.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppcomponent;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.conf.ConfigurationException;\nimport tigase.disco.ServiceEntity;\nimport tigase.disco.ServiceIdentity;\nimport tigase.disco.XMPPService;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\nimport tigase.net.ConnectionType;\nimport tigase.net.SocketType;\nimport tigase.server.ConnectionManager;\nimport tigase.server.Packet;\nimport tigase.util.Algorithms;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.PacketErrorTypeException;\nimport tigase.xmpp.XMPPIOService;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.security.NoSuchAlgorithmException;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Class ComponentConnectionManager\n * <br>\n * Created: Tue Nov 22 07:07:11 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"ext-comp\", parent = Kernel.class, active = false)\n@ConfigType({ConfigTypeEnum.ComponentMode})\n@Deprecated\n@TigaseDeprecated(since = \"8.0.0\")\npublic class ComponentConnectionManager\n\t\textends ConnectionManager<XMPPIOService<Object>>\n\t\timplements XMPPService {\n\n\tpublic static final String SECRET_PROP_KEY = \"secret\";\n\n\tpublic static final String PORT_ROUTING_TABLE_PROP_KEY = \"routing-table\";\n\n\tpublic static final String PACK_ROUTED_KEY = \"pack-routed\";\n\n\tpublic static final String RETURN_SERVICE_DISCO_KEY = \"service-disco\";\n\n\tpublic static final boolean RETURN_SERVICE_DISCO_VAL = true;\n\n\tpublic static final String IDENTITY_TYPE_KEY = \"identity-type\";\n\n\tpublic static final String IDENTITY_TYPE_VAL = \"generic\";\n\n\tprivate static final Logger log = Logger.getLogger(ComponentConnectionManager.class.getName());\n\n\tpublic boolean PACK_ROUTED_VAL = false;\n\tpublic int[] PORTS = {5555};\n\tpublic String PORT_LOCAL_HOST_PROP_VAL = \"localhost\";\n\tpublic String PORT_REMOTE_HOST_PROP_VAL = \"comp-1.localhost\";\n\tpublic String[] PORT_ROUTING_TABLE_PROP_VAL = {PORT_REMOTE_HOST_PROP_VAL, \".*@\" + PORT_REMOTE_HOST_PROP_VAL,\n\t\t\t\t\t\t\t\t\t\t\t\t   \".*\\\\.\" + PORT_REMOTE_HOST_PROP_VAL};\n\tpublic SocketType PORT_SOCKET_PROP_VAL = SocketType.plain;\n\tpublic ConnectionType PORT_TYPE_PROP_VAL = ConnectionType.accept;\n\tpublic String SECRET_PROP_VAL = \"someSecret\";\n\t// private boolean service_disco = RETURN_SERVICE_DISCO_VAL;\n\tprivate String identity_type = IDENTITY_TYPE_VAL;\n\tprivate boolean pack_routed = PACK_ROUTED_VAL;\n\tprivate ServiceEntity serviceEntity = null;\n\tprivate String service_id = \"it doesn't matter\";\n\n\t@Override\n\tpublic Map<String, Object> getDefaults(Map<String, Object> params) {\n\t\tString config_type = (String) params.get(\"config-type\");\n\n\t\tif (config_type.equals(GEN_CONFIG_SM)) {\n\t\t\tPACK_ROUTED_VAL = true;\n\t\t\tPORT_TYPE_PROP_VAL = ConnectionType.accept;\n\t\t}\n\n\t\tif (config_type.equals(GEN_CONFIG_CS)) {\n\t\t\tPACK_ROUTED_VAL = true;\n\t\t\tPORT_TYPE_PROP_VAL = ConnectionType.connect;\n\t\t\tPORT_IFC_PROP_VAL = new String[]{\"localhost\"};\n\t\t}\n\n\t\tboolean def_found = false;\n\n\t\tfor (String key : params.keySet()) {\n\t\t\tString gen_ext_comp = GEN_EXT_COMP;\n\n\t\t\tif (key.startsWith(GEN_EXT_COMP)) {\n\t\t\t\tString end = key.substring(GEN_EXT_COMP.length());\n\n\t\t\t\tif (getName().endsWith(end)) {\n\t\t\t\t\tgen_ext_comp = key;\n\t\t\t\t}    // end of if (getName().endsWith(end))\n\n\t\t\t\tif (params.get(gen_ext_comp) != null) {\n\t\t\t\t\tdef_found = true;\n\t\t\t\t\tlog.config(\"Found default configuration: \" + (String) params.get(gen_ext_comp));\n\n\t\t\t\t\tString[] comp_params = ((String) params.get(gen_ext_comp)).split(\",\");\n\t\t\t\t\tint idx = 0;\n\n\t\t\t\t\tif (comp_params.length >= idx + 1) {\n\t\t\t\t\t\tPORT_LOCAL_HOST_PROP_VAL = comp_params[idx++];\n\t\t\t\t\t\tlog.config(\"Setting PORT_LOCAL_HOST_PROP_VAL to \" + PORT_LOCAL_HOST_PROP_VAL);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (comp_params.length >= idx + 1) {\n\t\t\t\t\t\tPORT_REMOTE_HOST_PROP_VAL = comp_params[idx++];\n\t\t\t\t\t\tlog.config(\"Setting PORT_REMOTE_HOST_PROP_VAL to \" + PORT_REMOTE_HOST_PROP_VAL);\n\t\t\t\t\t\tPORT_ROUTING_TABLE_PROP_VAL = new String[]{PORT_REMOTE_HOST_PROP_VAL};\n\n\t\t\t\t\t\tif (config_type.equals(GEN_CONFIG_CS)) {\n\t\t\t\t\t\t\tPORT_IFC_PROP_VAL = new String[]{PORT_REMOTE_HOST_PROP_VAL};\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (comp_params.length >= idx + 1) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tPORTS[0] = Integer.decode(comp_params[idx++]);\n\t\t\t\t\t\t} catch (NumberFormatException e) {\n\t\t\t\t\t\t\tlog.warning(\"Incorrect component port number: \" + comp_params[idx - 1]);\n\t\t\t\t\t\t\tPORTS[0] = 5555;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlog.config(\"Setting PORTS[0] to \" + PORTS[0]);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (comp_params.length >= idx + 1) {\n\t\t\t\t\t\tSECRET_PROP_VAL = comp_params[idx++];\n\t\t\t\t\t\tlog.config(\"Setting SECRET_PROP_VAL to \" + SECRET_PROP_VAL);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (comp_params.length >= idx + 1) {\n\t\t\t\t\t\tif (comp_params[idx++].equals(\"ssl\")) {\n\t\t\t\t\t\t\tPORT_SOCKET_PROP_VAL = SocketType.ssl;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlog.config(\"Setting PORT_SOCKET_PROP_VAL to \" + PORT_SOCKET_PROP_VAL);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (comp_params.length >= idx + 1) {\n\t\t\t\t\t\tif (comp_params[idx++].equals(\"accept\")) {\n\t\t\t\t\t\t\tPORT_TYPE_PROP_VAL = ConnectionType.accept;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tPORT_TYPE_PROP_VAL = ConnectionType.connect;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlog.config(\"Setting PORT_TYPE_PROP_VAL to \" + PORT_TYPE_PROP_VAL);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (comp_params.length >= idx + 1) {\n\t\t\t\t\t\tPORT_ROUTING_TABLE_PROP_VAL = new String[]{comp_params[idx++]};\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (config_type.equals(GEN_CONFIG_COMP)) {\n\n\t\t\t\t\t\t\t// This is specialized configuration for a single\n\t\t\t\t\t\t\t// external component so all traffic should go through\n\t\t\t\t\t\t\t// the external component (it acts as like s2s component)\n\t\t\t\t\t\t\tPORT_ROUTING_TABLE_PROP_VAL = new String[]{\".*\"};\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tString regex_host = PORT_REMOTE_HOST_PROP_VAL.replace(\".\", \"\\\\.\");\n\n\t\t\t\t\t\t\tPORT_ROUTING_TABLE_PROP_VAL = new String[]{regex_host, \".*@\" + regex_host,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \".*\\\\.\" + regex_host};\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!def_found) {\n\t\t\tPORT_LOCAL_HOST_PROP_VAL = \"localhost\";\n\t\t\tPORT_REMOTE_HOST_PROP_VAL = getName() + \".localhost\";\n\n\t\t\tString regex_host = PORT_REMOTE_HOST_PROP_VAL.replace(\".\", \"\\\\.\");\n\n\t\t\tPORT_ROUTING_TABLE_PROP_VAL = new String[]{regex_host, \".*@\" + regex_host, \".*\\\\.\" + regex_host};\n\t\t}    // end of if (!def_found)\n\n\t\tMap<String, Object> props = super.getDefaults(params);\n\n\t\tprops.put(PACK_ROUTED_KEY, PACK_ROUTED_VAL);\n\t\tprops.put(RETURN_SERVICE_DISCO_KEY, RETURN_SERVICE_DISCO_VAL);\n\t\tprops.put(IDENTITY_TYPE_KEY, IDENTITY_TYPE_VAL);\n\n\t\treturn props;\n\t}\n\n\t@Override\n\tpublic List<Element> getDiscoFeatures(JID from) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Element getDiscoInfo(String node, JID jid, JID from) {\n\t\tif ((jid != null) && getName().equals(jid.getLocalpart())) {\n\t\t\treturn serviceEntity.getDiscoInfo(node);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic List<Element> getDiscoItems(String node, JID jid, JID from) {\n\t\tif (getName().equals(jid.getLocalpart())) {\n\t\t\treturn serviceEntity.getDiscoItems(node, null);\n\t\t} else {\n\t\t\treturn Arrays.asList(serviceEntity.getDiscoItem(null, BareJID.toString(getName(), jid.toString())));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processPacket(Packet packet) {\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.finer(\"Processing packet: \" + packet.getElemName() + \", type: \" + packet.getType());\n\t\t}\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Processing packet: \" + packet);\n\t\t}\n\n\t\tif ((packet.getStanzaTo() != null) && packet.getStanzaTo().equals(getComponentId())) {\n\t\t\ttry {\n\t\t\t\taddOutPacket(Authorization.FEATURE_NOT_IMPLEMENTED.getResponseMessage(packet, \"Not implemented\", true));\n\t\t\t} catch (PacketErrorTypeException e) {\n\t\t\t\tlog.warning(\"Packet processing exception: \" + e);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tif (pack_routed && packet.getElemName() != \"route\") {\n\t\t\twritePacketToSocket(packet.packRouted());\n\t\t} else {\n\t\t\twritePacketToSocket(packet);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Queue<Packet> processSocketData(XMPPIOService<Object> serv) {\n\t\tPacket p = null;\n\n\t\twhile ((p = serv.getReceivedPackets().poll()) != null) {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.finer(\"Processing packet: \" + p.getElemName() + \", type: \" + p.getType());\n\t\t\t}\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Processing socket data: \" + p);\n\t\t\t}\n\n\t\t\tif (p.getElemName().equals(\"handshake\")) {\n\t\t\t\tprocessHandshake(p, serv);\n\t\t\t} else {\n\t\t\t\tPacket result = p;\n\n\t\t\t\tif (p.isRouted()) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tresult = p.unpackRouted();\n\t\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\t\tlog.warning(\"Packet stringprep addressing problem, dropping packet: \" + p);\n\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t}    // end of if (p.isRouted())\n\n\t\t\t\taddOutPacket(result);\n\t\t\t}\n\t\t}        // end of while ()\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean processUndeliveredPacket(Packet packet, Long stamp, String errorMessage) {\n\t\t// readd packet - this may be good as we would retry to send packet \n\t\t// which delivery failed due to IO error\n\t\taddPacket(packet);\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void reconnectionFailed(Map<String, Object> port_props) {\n\n\t\t// TODO: handle this somehow\n\t}\n\n\t@Override\n\tpublic void serviceStarted(XMPPIOService<Object> serv) {\n\t\tsuper.serviceStarted(serv);\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"c2c connection opened: \" + serv.getRemoteAddress() + \", type: \" +\n\t\t\t\t\t\t\t   serv.connectionType().toString() + \", id=\" + serv.getUniqueId());\n\t\t}\n\n//  String addr =\n//    (String)service.getSessionData().get(PORT_REMOTE_HOST_PROP_KEY);\n//  addRouting(addr);\n\t\t// addRouting(serv.getRemoteHost());\n\t\tswitch (serv.connectionType()) {\n\t\t\tcase connect:\n\n\t\t\t\t// Send init xmpp stream here\n\t\t\t\t// XMPPIOService serv = (XMPPIOService)service;\n\t\t\t\tString compName = (String) serv.getSessionData().get(PORT_LOCAL_HOST_PROP_KEY);\n\t\t\t\tString data = \"<stream:stream\" + \" xmlns='jabber:component:accept'\" +\n\t\t\t\t\t\t\" xmlns:stream='http://etherx.jabber.org/streams'\" + \" to='\" + compName + \"'\" + \">\";\n\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"cid: \" + (String) serv.getSessionData().get(\"cid\") + \", sending: \" + data);\n\t\t\t\t}\n\n\t\t\t\tserv.xmppStreamOpen(data);\n\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\n\t\t\t\t// Do nothing, more data should come soon...\n\t\t\t\tbreak;\n\t\t}    // end of switch (service.connectionType())\n\t}\n\n\t@Override\n\tpublic boolean serviceStopped(XMPPIOService<Object> service) {\n\t\tboolean result = super.serviceStopped(service);\n\n\t\tif (result) {\n\t\t\tMap<String, Object> sessionData = service.getSessionData();\n\t\t\tString[] routings = (String[]) sessionData.get(PORT_ROUTING_TABLE_PROP_KEY);\n\n\t\t\tupdateRoutings(routings, false);\n\n\t\t\tConnectionType type = service.connectionType();\n\n\t\t\tif (type == ConnectionType.connect) {\n\t\t\t\taddWaitingTask(sessionData);\n\n\t\t\t\t// reconnectService(sessionData, connectionDelay);\n\t\t\t}    // end of if (type == ConnectionType.connect)\n\n\t\t\t// removeRouting(serv.getRemoteHost());\n\t\t\tString addr = (String) sessionData.get(PORT_REMOTE_HOST_PROP_KEY);\n\n\t\t\tremoveComponentDomain(addr);\n\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.fine(\"Disonnected from: \" + addr);\n\t\t\t}\n\n\t\t\tupdateServiceDiscovery(addr, \"XEP-0114 disconnected\");\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic void setProperties(Map<String, Object> props) throws ConfigurationException {\n\t\tsuper.setProperties(props);\n\t\tpack_routed = (Boolean) props.get(PACK_ROUTED_KEY);\n\n\t\t// service_disco = (Boolean)props.get(RETURN_SERVICE_DISCO_KEY);\n\t\tidentity_type = (String) props.get(IDENTITY_TYPE_KEY);\n\n\t\t// serviceEntity = new ServiceEntity(getName(), \"external\", \"XEP-0114\");\n\t\tserviceEntity = new ServiceEntity(\"XEP-0114 \" + getName(), null, \"XEP-0114\");\n\t\tserviceEntity.addIdentities(new ServiceIdentity(\"component\", identity_type, \"XEP-0114 \" + getName()));\n\t}\n\n\t@Override\n\tpublic void tlsHandshakeCompleted(XMPPIOService<Object> service) {\n\t}\n\n\t@Override\n\tpublic void xmppStreamClosed(XMPPIOService<Object> serv) {\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.finer(\"Stream closed.\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic String[] xmppStreamOpened(XMPPIOService<Object> service, Map<String, String> attribs) {\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.finer(\"Stream opened: \" + attribs.toString());\n\t\t}\n\n\t\tswitch (service.connectionType()) {\n\t\t\tcase connect: {\n\t\t\t\tString id = attribs.get(\"id\");\n\n\t\t\t\tservice.getSessionData().put(XMPPIOService.SESSION_ID_KEY, id);\n\n\t\t\t\tString secret = (String) service.getSessionData().get(SECRET_PROP_KEY);\n\n\t\t\t\ttry {\n\t\t\t\t\tString digest = Algorithms.hexDigest(id, secret, \"SHA\");\n\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.finest(\"Calculating digest: id=\" + id + \", secret=\" + secret + \", digest=\" + digest);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn new String[] { \"<handshake>\" + digest + \"</handshake>\" };\n\t\t\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\t\t\tlog.log(Level.SEVERE, \"Can not generate digest for pass phrase.\", e);\n\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcase accept: {\n\t\t\t\tString hostname = attribs.get(\"to\");\n\n\t\t\t\tservice.getSessionData().put(XMPPIOService.HOSTNAME_KEY, hostname);\n\n\t\t\t\tString id = UUID.randomUUID().toString();\n\n\t\t\t\tservice.getSessionData().put(XMPPIOService.SESSION_ID_KEY, id);\n\n\t\t\t\treturn new String[] { \"<stream:stream\" + \" xmlns='jabber:component:accept'\" +\n\t\t\t\t\t\t\" xmlns:stream='http://etherx.jabber.org/streams'\" + \" from='\" + hostname + \"'\" + \" id='\" + id +\n\t\t\t\t\t\t\"'\" + \">\" };\n\t\t\t}\n\n\t\t\tdefault:\n\n\t\t\t\t// Do nothing, more data should come soon...\n\t\t\t\tbreak;\n\t\t}    // end of switch (service.connectionType())\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tprotected int[] getDefPlainPorts() {\n\t\treturn PORTS;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t * <br>\n\t * We should not really close external component connection at all, so let's say something like: 1000 days...\n\t */\n\t@Override\n\tprotected long getMaxInactiveTime() {\n\t\treturn 1000 * 24 * HOUR;\n\t}\n\n\t@Override\n\tprotected Map<String, Object> getParamsForPort(int port) {\n\t\tMap<String, Object> defs = new TreeMap<String, Object>();\n\n\t\tdefs.put(SECRET_PROP_KEY, SECRET_PROP_VAL);\n\t\tdefs.put(PORT_LOCAL_HOST_PROP_KEY, PORT_LOCAL_HOST_PROP_VAL);\n\t\tdefs.put(PORT_ROUTING_TABLE_PROP_KEY, PORT_ROUTING_TABLE_PROP_VAL);\n\t\tdefs.put(PORT_TYPE_PROP_KEY, PORT_TYPE_PROP_VAL);\n\t\tdefs.put(PORT_SOCKET_PROP_KEY, PORT_SOCKET_PROP_VAL);\n\t\tdefs.put(PORT_REMOTE_HOST_PROP_KEY, PORT_REMOTE_HOST_PROP_VAL);\n\t\tdefs.put(PORT_IFC_PROP_KEY, PORT_IFC_PROP_VAL);\n\t\tdefs.put(MAX_RECONNECTS_PROP_KEY, (int) (30 * MINUTE));\n\n\t\treturn defs;\n\t}\n\n\t@Override\n\tprotected String getServiceId(Packet packet) {\n\t\treturn service_id;\n\t}\n\n\t@Override\n\tprotected String getUniqueId(XMPPIOService<Object> serv) {\n\n\t\t// return (String)serv.getSessionData().get(PORT_REMOTE_HOST_PROP_KEY);\n\t\treturn service_id;\n\t}\n\n\t@Override\n\tprotected XMPPIOService<Object> getXMPPIOServiceInstance() {\n\t\treturn new XMPPIOService<Object>();\n\t}\n\n\tprivate void processHandshake(Packet p, XMPPIOService<Object> serv) {\n\t\tswitch (serv.connectionType()) {\n\t\t\tcase connect: {\n\t\t\t\tString data = p.getElemCData();\n\n\t\t\t\tif (data == null) {\n\t\t\t\t\tString[] routings = (String[]) serv.getSessionData().get(PORT_ROUTING_TABLE_PROP_KEY);\n\n\t\t\t\t\tupdateRoutings(routings, true);\n\n\t\t\t\t\tString addr = (String) serv.getSessionData().get(PORT_REMOTE_HOST_PROP_KEY);\n\n\t\t\t\t\taddComponentDomain(addr);\n\n\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\tlog.fine(\"Connected to: \" + addr);\n\t\t\t\t\t}\n\n\t\t\t\t\tupdateServiceDiscovery(addr, \"XEP-0114 connected\");\n\t\t\t\t} else {\n\t\t\t\t\tlog.warning(\"Incorrect packet received: \" + p);\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase accept: {\n\t\t\t\tString digest = p.getElemCData();\n\t\t\t\tString id = (String) serv.getSessionData().get(XMPPIOService.SESSION_ID_KEY);\n\t\t\t\tString secret = (String) serv.getSessionData().get(SECRET_PROP_KEY);\n\n\t\t\t\ttry {\n\t\t\t\t\tString loc_digest = Algorithms.hexDigest(id, secret, \"SHA\");\n\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.finest(\"Calculating digest: id=\" + id + \", secret=\" + secret + \", digest=\" + loc_digest);\n\t\t\t\t\t}\n\n\t\t\t\t\tif ((digest != null) && digest.equals(loc_digest)) {\n\t\t\t\t\t\tPacket resp = Packet.packetInstance(new Element(\"handshake\"));\n\n\t\t\t\t\t\twritePacketToSocket(serv, resp);\n\n\t\t\t\t\t\tString[] routings = (String[]) serv.getSessionData().get(PORT_ROUTING_TABLE_PROP_KEY);\n\n\t\t\t\t\t\tupdateRoutings(routings, true);\n\n\t\t\t\t\t\tString addr = (String) serv.getSessionData().get(XMPPIOService.HOSTNAME_KEY);\n\n\t\t\t\t\t\taddComponentDomain(addr);\n\n\t\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\t\tlog.fine(\"Connected to: \" + addr);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tupdateServiceDiscovery(addr, \"XEP-0114 connected\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlog.log(Level.CONFIG, \"Handshaking passwords don't match, disconnecting...\");\n\t\t\t\t\t\tserv.stop();\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.log(Level.SEVERE, \"Handshaking error.\", e);\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\n\t\t\t\t// Do nothing, more data should come soon...\n\t\t\t\tbreak;\n\t\t}    // end of switch (service.connectionType())\n\t}\n\n\tprivate void updateRoutings(String[] routings, boolean add) {\n\t\tif (add) {\n\t\t\tlog.log(Level.CONFIG, \"Adding routings: \" + Arrays.toString(routings));\n\n\t\t\tfor (String route : routings) {\n\t\t\t\ttry {\n\t\t\t\t\taddRegexRouting(route);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.warning(\"Can not add regex routing '\" + route + \"' : \" + e);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.CONFIG, \"Removing routings: \" + Arrays.toString(routings));\n\n\t\t\tfor (String route : routings) {\n\t\t\t\ttry {\n\t\t\t\t\tremoveRegexRouting(route);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.warning(\"Can not remove regex routing '\" + route + \"' : \" + e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void updateServiceDiscovery(String jid, String name) {\n\t\tServiceEntity item = new ServiceEntity(jid, null, name);\n\n\t\t// item.addIdentities(new ServiceIdentity(\"component\", identity_type, name));\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Modifing service-discovery info: \" + item.toString());\n\t\t}\n\n\t\tserviceEntity.addItems(item);\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/CID.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver;\n\nimport java.util.logging.Logger;\n\n/**\n * Created: Jan 7, 2010 12:51:33 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class CID {\n\n\tprivate static final Logger log = Logger.getLogger(CID.class.getName());\n\n\tprivate int hash = 3;\n\tprivate String localHost = null;\n\tprivate String remoteHost = null;\n\tprivate String to_string = null;\n\n\tpublic CID(String cid) {\n\t\tString[] cid_a = cid.split(\"@\");\n\n\t\tthis.localHost = cid_a[0].intern();\n\t\tthis.remoteHost = cid_a[1].intern();\n\t\tupdateToString();\n\t}\n\n\tpublic CID(String localHost, String remoteHost) {\n\t\tthis.localHost = ((localHost == null) ? null : localHost.intern());\n\t\tthis.remoteHost = ((remoteHost == null) ? null : remoteHost.intern());\n\t\tupdateToString();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (o instanceof CID) {\n\t\t\treturn (localHost == ((CID) o).localHost) && (remoteHost == ((CID) o).remoteHost);\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic String getLocalHost() {\n\t\treturn localHost;\n\t}\n\n\tpublic String getRemoteHost() {\n\t\treturn remoteHost;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn hash;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn to_string;\n\t}\n\n\tprivate void updateToString() {\n\t\tthis.to_string = \"\" + this.localHost + \"@\" + this.remoteHost;\n\t\thash = 47 * hash + ((this.localHost != null) ? this.localHost.hashCode() : 0);\n\t\thash = 47 * hash + ((this.remoteHost != null) ? this.remoteHost.hashCode() : 0);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/CIDConnections.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.net.ConnectionType;\nimport tigase.net.SocketType;\nimport tigase.server.Packet;\nimport tigase.util.dns.DNSEntry;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.PacketErrorTypeException;\nimport tigase.xmpp.PacketInvalidTypeException;\n\nimport java.net.UnknownHostException;\nimport java.util.*;\nimport java.util.concurrent.*;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.locks.ReentrantLock;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Stream;\n\nimport static tigase.server.ConnectionManager.*;\n\n/**\n * Created: Jun 14, 2010 12:32:49 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class CIDConnections {\n\n\tprivate static final Logger log = Logger.getLogger(CIDConnections.class.getName());\n\tprivate static final Random random = new Random();\n\n\tprivate CID cid = null;\n\tprivate S2SConnectionSelector connectionSelector = null;\n\tprivate CIDConnectionsOpenerService connectionsOpenerService = null;\n\t/**\n\t * (SessionID, dbKey) pairs\n\t */\n\tprivate Map<String, String> dbKeys = new ConcurrentSkipListMap<String, String>();\n\tprivate long firstWaitingTime = 0;\n\tprivate S2SConnectionHandlerIfc<S2SIOService> handler = null;\n\tprivate Set<S2SConnection> incoming = new ConcurrentSkipListSet<S2SConnection>();\n\tprivate int max_in_conns = 4;\n\tprivate int max_out_conns = 4;\n\tprivate int max_out_conns_per_ip = 2;\n\tprivate long max_waiting_time = 15 * 60 * 1000;\n\tprivate boolean oneWayAuthentication;\n\tprivate Set<S2SConnection> outgoing = new ConcurrentSkipListSet<S2SConnection>();\n\tprivate AtomicBoolean outgoingOpenInProgress = new AtomicBoolean(false);\n\tprivate Set<S2SConnection> outgoing_handshaking = new ConcurrentSkipListSet<S2SConnection>();\n\tprivate ReentrantLock sendInProgress = new ReentrantLock();\n\tprivate boolean testMode = Boolean.getBoolean(\"test\");\n\tprivate ConcurrentLinkedQueue<Packet> waitingPackets = new ConcurrentLinkedQueue<Packet>();\n\n\tpublic CIDConnections(CID cid, S2SConnectionHandlerIfc<S2SIOService> handler, S2SConnectionSelector selector,\n\t\t\t\t\t\t  int maxInConns, int maxOutConns, int maxOutConnsPerIP, long max_waiting_time) {\n\t\tthis(cid,handler,selector,maxInConns,maxOutConns,maxOutConnsPerIP,max_waiting_time, false);\n\t}\n\tpublic CIDConnections(CID cid, S2SConnectionHandlerIfc<S2SIOService> handler, S2SConnectionSelector selector,\n\t\t\t\t\t\t  int maxInConns, int maxOutConns, int maxOutConnsPerIP, long max_waiting_time, boolean oneWayAuthentication) {\n\t\tthis.cid = cid;\n\t\tthis.handler = handler;\n\t\tthis.connectionsOpenerService = handler.getConnectionOpenerService();\n\t\tthis.connectionSelector = selector;\n\t\tthis.max_in_conns = maxInConns;\n\t\tthis.max_out_conns = maxOutConns;\n\t\tthis.max_out_conns_per_ip = maxOutConnsPerIP;\n\t\tthis.max_waiting_time = max_waiting_time;\n\t\tthis.oneWayAuthentication = oneWayAuthentication;\n\t}\n\n\tpublic void resetOutgoingInProgress() {\n\t\toutgoingOpenInProgress.set(false);\n\t}\n\n\tpublic boolean getOutgoingInProgress() {\n\t\treturn outgoingOpenInProgress.get();\n\t}\n\n\tpublic void addDBKey(String sessId, String key) {\n\t\tdbKeys.put(sessId, key);\n\t}\n\n\tpublic void addIncoming(S2SIOService serv) {\n\t\tS2SConnection s2s_conn = serv.getS2SConnection();\n\n\t\tif (s2s_conn == null) {\n\t\t\ts2s_conn = new S2SConnection(handler, serv.getRemoteAddress());\n\t\t\ts2s_conn.setS2SIOService(serv);\n\t\t\tserv.setS2SConnection(s2s_conn);\n\t\t}\n\n\t\tCID cid = (CID) serv.getSessionData().get(S2SConnectionManager.CID_KEY);\n\t\tif (cid != null) {\n\t\t\t// using additional mapping/masking of incoming connections to allow \n\t\t\t// usage of intermediate server also for incoming connections\n\t\t\tString serverName = handler.getServerNameForDomain(cid.getRemoteHost());\n\t\t\tserv.getSessionData().put(S2SIOService.CERT_REQUIRED_DOMAIN, serverName);\n\t\t}\n\n\t\t// TODO: check if this should be moved inside the IF\n\t\tincoming.add(s2s_conn);\n\t}\n\n\tpublic void connectionAuthenticated(S2SIOService serv, CID cid) {\n\t\tfinal boolean isOutgoing = serv.connectionType() == ConnectionType.connect;\n\t\tS2SIOService.DIRECTION direction;\n\t\tif (oneWayAuthentication) {\n\t\t\tdirection = isOutgoing ? S2SIOService.DIRECTION.OUT : S2SIOService.DIRECTION.IN;\n\t\t} else {\n\t\t\tdirection = S2SIOService.DIRECTION.BOTH;\n\t\t}\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.log(Level.FINER, \"Connection is authenticated. Direction: {1} [{0}]\", new Object[]{serv, direction});\n\t\t}\n\t\tserv.addCID(cid, direction);\n\t\tif (serv.isAuthenticated()) {\n\t\t\thandler.serviceConnected(serv);\n\t\t}\n\t}\n\n\tpublic void connectionStopped(S2SIOService serv) {\n\t\tS2SConnection s2s_conn = serv.getS2SConnection();\n\n\t\tif (s2s_conn == null) {\n\t\t\tlog.log(Level.CONFIG, \"s2s_conn not set for serv: {0}\", serv);\n\n\t\t\treturn;\n\t\t}\n\t\tif (serv.getSessionId() != null) {\n\t\t\tdbKeys.remove(serv.getSessionId());\n\t\t}\n\t\tswitch (serv.connectionType()) {\n\t\t\tcase connect:\n\n\t\t\t\t// Release the 'lock'\n\t\t\t\toutgoingOpenInProgress.set(false);\n\t\t\t\toutgoing.remove(s2s_conn);\n\t\t\t\toutgoing_handshaking.remove(s2s_conn);\n\t\t\t\tif (!waitingPackets.isEmpty()) {\n\t\t\t\t\tcheckOpenConnections();\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\n\t\t\tcase accept:\n\t\t\t\tincoming.remove(s2s_conn);\n\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t}\n\t}\n\n\tpublic String getDBKey(String key_sessionId) {\n\t\treturn dbKeys.get(key_sessionId);\n\t}\n\n\tpublic int getDBKeysCount() {\n\t\treturn dbKeys.size();\n\t}\n\n\tpublic int getIncomingCount() {\n\t\tint result = 0;\n\n\t\tfor (S2SConnection s2SConnection : incoming) {\n\t\t\tif (s2SConnection.isConnected()) {\n\t\t\t\t++result;\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic int getIncomingTLSCount() {\n\t\tint result = 0;\n\n\t\tfor (S2SConnection s2SConnection : incoming) {\n\t\t\tS2SIOService serv = s2SConnection.getS2SIOService();\n\n\t\t\tif (serv.isConnected() && (serv.getSessionData().get(S2SIOService.CERT_CHECK_RESULT) != null)) {\n\t\t\t\t++result;\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic int getMaxOutConns() {\n\t\treturn this.max_out_conns;\n\t}\n\n\tpublic int getMaxOutConnsPerIP() {\n\t\treturn this.max_out_conns_per_ip;\n\t}\n\n\tpublic int getOutgoingCount() {\n\t\tint result = 0;\n\n\t\tfor (S2SConnection s2SConnection : outgoing) {\n\t\t\tif (s2SConnection.isConnected()) {\n\t\t\t\t++result;\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic int getOutgoingHandshakingCount() {\n\t\tint result = 0;\n\n\t\tfor (S2SConnection s2SConnection : outgoing_handshaking) {\n\t\t\tif (s2SConnection.isConnected()) {\n\t\t\t\t++result;\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic int getOutgoingTLSCount() {\n\t\tint result = 0;\n\n\t\tfor (S2SConnection s2SConnection : outgoing) {\n\t\t\tS2SIOService serv = s2SConnection.getS2SIOService();\n\n\t\t\tif (serv.isConnected() && (serv.getSessionData().get(S2SIOService.CERT_CHECK_RESULT) != null)) {\n\t\t\t\t++result;\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic S2SConnection getS2SConnectionForSessionId(String sessionId) {\n\t\tS2SConnection s2s_conn = null;\n\n\t\tfor (S2SConnection s2sc : incoming) {\n\t\t\tif ((s2sc.getS2SIOService() != null) && sessionId.equals(s2sc.getS2SIOService().getSessionId())) {\n\t\t\t\ts2s_conn = s2sc;\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (s2s_conn == null) {\n\t\t\tfor (S2SConnection s2sc : outgoing) {\n\t\t\t\tif ((s2sc.getS2SIOService() != null) && sessionId.equals(s2sc.getS2SIOService().getSessionId())) {\n\t\t\t\t\ts2s_conn = s2sc;\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (s2s_conn == null) {\n\t\t\tfor (S2SConnection s2sc : outgoing_handshaking) {\n\t\t\t\tif ((s2sc.getS2SIOService() != null) && sessionId.equals(s2sc.getS2SIOService().getSessionId())) {\n\t\t\t\t\ts2s_conn = s2sc;\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn s2s_conn;\n\t}\n\n\tpublic int getWaitingControlCount() {\n\t\tint result = 0;\n\n\t\tfor (S2SConnection s2sc : incoming) {\n\t\t\tresult += s2sc.getWaitingControlCount();\n\t\t}\n\t\tfor (S2SConnection s2sc : outgoing) {\n\t\t\tresult += s2sc.getWaitingControlCount();\n\t\t}\n\t\tfor (S2SConnection s2sc : outgoing_handshaking) {\n\t\t\tresult += s2sc.getWaitingControlCount();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic int getWaitingCount() {\n\t\treturn waitingPackets.size();\n\t}\n\n\tpublic void reconnectionFailed(Map<String, Object> port_props) {\n\t\tS2SConnection s2s_conn = (S2SConnection) port_props.get(S2SIOService.S2S_CONNECTION_KEY);\n\n\t\tif (s2s_conn == null) {\n\t\t\tlog.log(Level.CONFIG, \"s2s_conn not set for serv: {0}\", port_props);\n\n\t\t\treturn;\n\t\t}\n\n\t\tConnectionType type = (ConnectionType) port_props.get(\"type\");\n\n\t\tif (type != null) {\n\t\t\tswitch (type) {\n\t\t\t\tcase connect:\n\n\t\t\t\t\t// Release the 'lock'\n\t\t\t\t\toutgoingOpenInProgress.set(false);\n\t\t\t\t\toutgoing.remove(s2s_conn);\n\t\t\t\t\toutgoing_handshaking.remove(s2s_conn);\n\t\t\t\t\tif (!this.waitingPackets.isEmpty()) {\n\t\t\t\t\t\tcheckOpenConnections();\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase accept:\n\t\t\t\t\tthis.incoming.remove(s2s_conn);\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.CONFIG, \"ConnectionType not set for serv: {0}\", port_props);\n\t\t}\n\t}\n\n\tpublic boolean sendControlPacket(String sessionId, Packet packet) {\n\n\t\t// Seraching for a correct connection\n\t\t// TODO: speed it up somehow, maybe verify can be only sent to incoming\n\t\t// and result to outgoing? Check it out\n\t\tS2SConnection s2s_conn = getS2SConnectionForSessionId(sessionId);\n\n\t\tif (s2s_conn != null) {\n\t\t\ts2s_conn.addControlPacket(packet);\n\t\t\ts2s_conn.sendAllControlPackets();\n\n\t\t\treturn true;\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE,\n\t\t\t\t\t\t\"Control packet: {0} could not be sent as there is no connection \" + \"for the session id: {1}\",\n\t\t\t\t\t\tnew Object[]{packet, sessionId});\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic void sendHandshakingOnly(final Packet verify_req) {\n\t\tconnectionsOpenerService.schedule(new Runnable() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\ttry {\n\t\t\t\t\t// using additional domain name mapping to allow usage of intermediate server\n\t\t\t\t\tString serverName = handler.getServerNameForDomain(cid.getRemoteHost());\n\n\t\t\t\t\tDNSEntry dns_entry = DNSResolverFactory.getInstance().getHostSRV_Entry(serverName);\n\n\t\t\t\t\tboolean hasIPv6 = Stream.concat(incoming.stream(), outgoing.stream())\n\t\t\t\t\t\t\t.filter(conn -> conn.isConnected())\n\t\t\t\t\t\t\t.filter(conn -> conn.getIPAddress().contains(\":\"))\n\t\t\t\t\t\t\t.findFirst()\n\t\t\t\t\t\t\t.isPresent();\n\n\t\t\t\t\tString[] ips = dns_entry.getIps();\n\t\t\t\t\tif (!hasIPv6) {\n\t\t\t\t\t\tips = Arrays.stream(ips).filter(ip -> !ip.contains(\":\")).toArray(String[]::new);\n\t\t\t\t\t}\n\n\t\t\t\t\tString ip = ips.length == 1 ? ips[0] : ips[random.nextInt(ips.length)];\n\n\t\t\t\t\tS2SConnection s2s_conn = new S2SConnection(handler, ip);\n\n\t\t\t\t\ts2s_conn.addControlPacket(verify_req);\n\n\t\t\t\t\tMap<String, Object> port_props = new TreeMap<String, Object>();\n\t\t\t\t\tport_props.put(S2SIOService.CERT_REQUIRED_DOMAIN, serverName);\n\n\t\t\t\t\tport_props.put(S2SIOService.HANDSHAKING_ONLY_KEY, S2SIOService.HANDSHAKING_ONLY_KEY);\n\n\t\t\t\t\t// it looks like we are sending verify requests only on handshaking-only\n\t\t\t\t\t// connection so there is only one domain for verification\n\t\t\t\t\tport_props.put(S2SIOService.HANDSHAKING_DOMAIN_KEY, verify_req.getStanzaTo().toString());\n\t\t\t\t\tinitNewConnection(ip, dns_entry.getPort(), s2s_conn, port_props);\n\t\t\t\t} catch (UnknownHostException ex) {\n\t\t\t\t\tlog.log(Level.CONFIG, \"Remote host not found: \" + cid.getRemoteHost(), ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}, 0, TimeUnit.MILLISECONDS);\n\t}\n\n\tpublic void sendPacket(Packet packet) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Sending packet: \" + packet);\n\t\t}\n\t\tif (packet != null) {\n\t\t\tif ((firstWaitingTime == 0) || waitingPackets.isEmpty()) {\n\t\t\t\tfirstWaitingTime = System.currentTimeMillis();\n\t\t\t}\n\t\t\twaitingPackets.offer(packet);\n\t\t}\n\t\tif (sendInProgress.tryLock()) {\n\t\t\ttry {\n\t\t\t\tboolean packetSent = false;\n\t\t\t\tPacket waiting = null;\n\n\t\t\t\twhile ((waiting = waitingPackets.peek()) != null) {\n\t\t\t\t\tS2SConnection s2s_conn = getOutgoingConnection(waiting);\n\n\t\t\t\t\tif (s2s_conn != null) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tif (s2s_conn.isConnected()) {\n\t\t\t\t\t\t\t\tpacketSent = s2s_conn.sendPacket(waiting);\n\t\t\t\t\t\t\t\twaitingPackets.poll();\n\t\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Packet: {0} sent over connection: {1}\",\n\t\t\t\t\t\t\t\t\t\t\tnew Object[]{waiting, s2s_conn.getS2SIOService()});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"There was a closed connection available - removing \" +\n\t\t\t\t\t\t\t\t\t\t\t\"connection {0} from set of active connections\", s2s_conn);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\toutgoing.remove(s2s_conn);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\t\tlog.log(Level.FINE, \"A problem sending packet, connection broken? Retrying later. {0}\",\n\t\t\t\t\t\t\t\t\twaiting);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"There is no connection available to send the packet: {0}\", waiting);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!packetSent) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"No packet could be sent, trying to open more connections: {0}\", cid);\n\t\t\t\t\t}\n\t\t\t\t\tcheckOpenConnections();\n\t\t\t\t} else {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Some packets were sent, not trying to open more connections: {0}\", cid);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tsendInProgress.unlock();\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void streamNegotiationCompleted(S2SIOService serv) {\n\n\t\tlog.log(Level.FINER, \"Marking the stream as negotiated, sending packets. [{0}]\", cid);\n\t\tfinal boolean isOutgoing = serv.connectionType() == ConnectionType.connect;\n\t\tif (serv.isAuthenticated()) {\n\t\t\thandler.serviceConnected(serv);\n\t\t}\n\t\tif (isOutgoing) {\n\n\t\t\t// Release the 'lock'\n\t\t\toutgoingOpenInProgress.set(false);\n\n\t\t\tS2SConnection s2s_conn = serv.getS2SConnection();\n\n\t\t\toutgoing_handshaking.remove(s2s_conn);\n\t\t\toutgoing.add(s2s_conn);\n\t\t\tsendPacket(null);\n\t\t}\n\t}\n\n\tprivate void checkOpenConnections() {\n\t\tif (outgoingOpenInProgress.compareAndSet(false, true)) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Scheduling task for opening a new connection for: {0}\", cid);\n\t\t\t}\n\t\t\tconnectionsOpenerService.schedule(new Runnable() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tboolean result = false;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Running scheduled task for openning a new connection for: {0}\", cid);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresult = openOutgoingConnections();\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"uncaughtException in the connection opening thread: \", e);\n\t\t\t\t\t} finally {\n\t\t\t\t\t}\n\t\t\t\t\tif (!result) {\n\t\t\t\t\t\toutgoingOpenInProgress.set(false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}, 0, TimeUnit.MILLISECONDS);\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Outgoing open in progress, skipping for: {0}\", cid);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate int getOpenForIP(String ip) {\n\t\tint result = 0;\n\n\t\tfor (S2SConnection s2SConnection : outgoing) {\n\t\t\tif (ip.equals(s2SConnection.getIPAddress())) {\n\t\t\t\t++result;\n\t\t\t}\n\t\t}\n\t\tfor (S2SConnection s2SConnection : outgoing_handshaking) {\n\t\t\tif (ip.equals(s2SConnection.getIPAddress())) {\n\t\t\t\t++result;\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate S2SConnection getOutgoingConnection(Packet packet) {\n\t\treturn connectionSelector.selectConnection(packet, outgoing);\n\t}\n\n\tvoid initNewConnection(String ip, int port, S2SConnection s2s_conn, Map<String, Object> port_props) {\n\t\toutgoing_handshaking.add(s2s_conn);\n\t\tport_props.put(S2SIOService.S2S_CONNECTION_KEY, s2s_conn);\n\t\tport_props.put(\"remote-ip\", ip);\n\t\tport_props.put(\"local-hostname\", cid.getLocalHost());\n\t\tport_props.put(\"remote-hostname\", cid.getRemoteHost());\n\t\tport_props.put(PORT_IFC_PROP_KEY, new String[]{ip});\n\t\tport_props.put(PORT_SOCKET_PROP_KEY, SocketType.plain);\n\t\tport_props.put(PORT_TYPE_PROP_KEY, ConnectionType.connect);\n\t\tport_props.put(\"srv-type\", \"_xmpp-server._tcp\");\n\t\tport_props.put(PORT_KEY, port);\n\t\tport_props.put(S2SConnectionManager.CID_KEY, cid);\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"STARTING new connection: {0}, params: {1}\", new Object[]{cid, port_props});\n\t\t}\n\t\thandler.initNewConnection(port_props);\n\t}\n\n\tprivate boolean openOutgoingConnections() {\n\t\tboolean result = false;\n\n\t\ttry {\n\n\t\t\t// Check whether all active connections are still active\n\t\t\tfor (S2SConnection out_conn : outgoing) {\n\t\t\t\tif (!out_conn.isConnected()) {\n\t\t\t\t\toutgoing.remove(out_conn);\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Removing inactive connection: {0}\", out_conn);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (hasExceededMaxWaitingTime()) {\n\t\t\t\tsendPacketsBack();\n\t\t\t\tfirstWaitingTime = 0;\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"S2S Timeout expired, sending back: {0}\", waitingPackets);\n\t\t\t\t}\n\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tint all_outgoing = outgoing.size() + outgoing_handshaking.size();\n\n\t\t\tif (all_outgoing >= max_out_conns) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Exceeded max number of outgoing connections, not doing anything: {0}\",\n\t\t\t\t\t\t\tall_outgoing);\n\t\t\t\t}\n\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Checking DNS for host: {0} for: {1}\", new Object[]{cid.getRemoteHost(), cid});\n\t\t\t}\n\n\t\t\t// During TTS automated tests we send ping for 200 non-existen domains. On some\n\t\t\t// configurations DNS check for 200 non-existen domains takes forever, so here we\n\t\t\t// have a shortcut to speed the test up.\n\t\t\t// To be sure we do this only for vhosts without a '.' character which are used\n\t\t\t// during TTS tests.\n\t\t\tif (testMode) {\n\t\t\t\tif (cid.getRemoteHost().startsWith(\"vhost-\") && !cid.getRemoteHost().contains(\".\")) {\n\t\t\t\t\tthrow new UnknownHostException(cid.getRemoteHost());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// using additional domain name mapping to allow usage of intermediate server\n\t\t\tString serverName = handler.getServerNameForDomain(cid.getRemoteHost());\n\n\t\t\t// Check DNS entries\n\t\t\tDNSEntry[] dns_entries = DNSResolverFactory.getInstance().getHostSRV_Entries(serverName);\n\n\t\t\t// Activate 'missing' connections\n\t\t\tfor (DNSEntry dNSEntry : dns_entries) {\n\t\t\t\tfor (String ip : dNSEntry.getIps()) {\n\t\t\t\t\tint openForIP = getOpenForIP(ip);\n\n\t\t\t\t\tfor (int i = openForIP; i < max_out_conns_per_ip; i++) {\n\t\t\t\t\t\tif (ip.equals(\"127.0.0.1\")) {\n\n\t\t\t\t\t\t\t// DNS misconfiguration for the remote server (icq.jabber.cz for\n\t\t\t\t\t\t\t// example)\n\t\t\t\t\t\t\t// Now we assume: UnknownHostException\n\t\t\t\t\t\t\tif (log.isLoggable(Level.CONFIG)) {\n\t\t\t\t\t\t\t\tlog.log(Level.CONFIG, \"DNS misconfiguration for domain: {0}, for: {1}\",\n\t\t\t\t\t\t\t\t\t\tnew Object[]{cid.getRemoteHost(), cid});\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tthrow new UnknownHostException(\"DNS misconfiguration for domain: \" + cid.getRemoteHost());\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Create a new connection\n\t\t\t\t\t\tS2SConnection s2s_conn = new S2SConnection(handler, ip);\n\t\t\t\t\t\tMap<String, Object> port_props = new TreeMap<String, Object>();\n\t\t\t\t\t\tport_props.put(S2SIOService.CERT_REQUIRED_DOMAIN, serverName);\n\n\t\t\t\t\t\tinitNewConnection(ip, dNSEntry.getPort(), s2s_conn, port_props);\n\t\t\t\t\t\tresult = true;\n\t\t\t\t\t\tif (++all_outgoing >= max_out_conns) {\n\t\t\t\t\t\t\treturn result;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (UnknownHostException ex) {\n\t\t\tlog.log(Level.CONFIG, \"Remote host not found: \" + cid.getRemoteHost() + \", for: \" + cid, ex);\n\t\t\tsendPacketsBack();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprotected boolean hasExceededMaxWaitingTime() {\n\t\treturn firstWaitingTime + max_waiting_time <= System.currentTimeMillis();\n\t}\n\n\tprivate void sendPacketsBack() {\n\t\tPacket p = null;\n\n\t\twhile ((p = waitingPackets.poll()) != null) {\n\t\t\ttry {\n\t\t\t\thandler.addOutPacket(\n\t\t\t\t\t\tAuthorization.REMOTE_SERVER_NOT_FOUND.getResponseMessage(p, \"S2S - destination host not found\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true));\n\t\t\t} catch (PacketInvalidTypeException e) {\n\t\t\t\tlog.log(Level.FINE, \"Packet: {0} processing exception: {1}\", new Object[]{p.toString(), e});\n\t\t\t} catch (PacketErrorTypeException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Packet: {0} processing exception: {1}\", new Object[]{p.toString(), e});\n\t\t\t}\n\t\t}\n\t}\n\n\t@Bean(name = \"cidConnectionsOpenerService\", parent = S2SConnectionManager.class, active = true)\n\tpublic static class CIDConnectionsOpenerService {\n\n\t\t// TODO: #1195 - estimate proper default value\n\t\t@ConfigField(desc = \"Numer of threads for opening outgoing connections\")\n\t\tprivate int outgoingOpenThreads = Runtime.getRuntime().availableProcessors();\n\n\t\tprivate ScheduledExecutorService outgoingOpenTasks = Executors.newScheduledThreadPool(outgoingOpenThreads);\n\n\t\tpublic void setOutgoingOpenThreads(int size) {\n\t\t\tif (outgoingOpenThreads != size) {\n\t\t\t\toutgoingOpenThreads = size;\n\t\t\t\tScheduledExecutorService scheduler = outgoingOpenTasks;\n\t\t\t\toutgoingOpenTasks = Executors.newScheduledThreadPool(outgoingOpenThreads);\n\t\t\t\tscheduler.shutdown();\n\t\t\t}\n\t\t}\n\n\t\tpublic void schedule(Runnable r, long delay, TimeUnit unit) {\n\t\t\toutgoingOpenTasks.schedule(r, delay, unit);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tfinal StringBuffer sb = new StringBuffer(\"CIDConnections{\");\n\t\tsb.append(cid);\n\t\tsb.append(\", incoming=\").append(getIncomingCount());\n\t\tsb.append(\", outgoing=\").append(getOutgoingCount());\n\t\tsb.append('}');\n\t\treturn sb.toString();\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/KnownDomainsListProvider.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.server.xmppserver;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.core.Kernel;\nimport tigase.vhosts.DefaultAwareVHostManagerIfc;\nimport tigase.vhosts.VHostManagerIfc;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.concurrent.CopyOnWriteArraySet;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\n@Bean(name = \"KnownDomainsListProvider\", parent = Kernel.class, active = true, exportable = true)\npublic class KnownDomainsListProvider {\n\n\tprivate static final Logger log = Logger.getLogger(KnownDomainsListProvider.class.getCanonicalName());\n\n\t@ConfigField(desc = \"Static list of domains that are to be provided instead of dynamic ones\")\n\tprivate HashSet<String> staticDomainsSet = new HashSet<>();\n\n\t@ConfigField(desc = \"Use only static list of domains for both local and remote domains\")\n\tprivate boolean useOnlyStaticDomainsList = false;\n\n\t@Inject\n\tprivate DefaultAwareVHostManagerIfc vHostManagerIfc;\n\n\tprotected Set<String> authenticatedRemoteDomains = new CopyOnWriteArraySet<>();\n\n\n\tpublic void addRemoteDomain(String remoteHost) {\n\t\tif (staticDomainsSet.isEmpty()) {\n\t\t\tauthenticatedRemoteDomains.add(remoteHost);\n\t\t}\n\t}\n\n\n\tpublic Set<String> getAuthenticatedRemoteDomains() {\n\t\tif (!staticDomainsSet.isEmpty()) {\n\t\t\treturn Collections.unmodifiableSet(staticDomainsSet);\n\t\t}\n\t\treturn Collections.unmodifiableSet(authenticatedRemoteDomains);\n\t}\n\n\tpublic Set<String> getAllLocalDomains() {\n\t\tif (useOnlyStaticDomainsList) {\n\t\t\treturn Collections.unmodifiableSet(staticDomainsSet);\n\t\t}\n\t\treturn vHostManagerIfc.getAllVHosts(false).stream().map(JID::toString).collect(Collectors.toSet());\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/LocalhostException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver;\n\nimport tigase.xmpp.XMPPException;\n\n/**\n * Created: Dec 7, 2010 5:25:55 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class LocalhostException\n\t\textends XMPPException {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\n\tpublic LocalhostException() {\n\t\tsuper();\n\t}\n\n\n\tpublic LocalhostException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\n\tpublic LocalhostException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n\tpublic LocalhostException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/NotLocalhostException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver;\n\nimport tigase.xmpp.XMPPException;\n\n/**\n * Created: Sep 2, 2010 4:11:34 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class NotLocalhostException\n\t\textends XMPPException {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\n\tpublic NotLocalhostException() {\n\t\tsuper();\n\t}\n\n\n\tpublic NotLocalhostException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\n\tpublic NotLocalhostException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n\tpublic NotLocalhostException(String msg, Throwable cause) {\n\t\tsuper(msg, cause);\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/OutgoingState.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver;\n\npublic enum OutgoingState {\n\n\tNULL,\n\tCONNECTING,\n\tHANDSHAKING,\n\tOK\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/S2SConnection.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver;\n\nimport tigase.server.Packet;\n\nimport java.io.IOException;\nimport java.util.concurrent.ConcurrentLinkedQueue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Jun 14, 2010 1:19:55 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class S2SConnection\n\t\timplements Comparable<S2SConnection> {\n\n\tprivate static final Logger log = Logger.getLogger(S2SConnection.class.getName());\n\n\tprivate OutgoingState conn_state = OutgoingState.NULL;\n\tprivate S2SConnectionHandlerIfc<S2SIOService> handler = null;\n\tprivate String ipAddress = null;\n\tprivate S2SIOService service = null;\n\t/**\n\t * Control packets for s2s connection establishing\n\t */\n\tprivate ConcurrentLinkedQueue<Packet> waitingControlPackets = new ConcurrentLinkedQueue<Packet>();\n\n\tpublic S2SConnection(S2SConnectionHandlerIfc<S2SIOService> handler, String ip) {\n\t\tthis.handler = handler;\n\t\tthis.ipAddress = ip;\n\t}\n\n\tpublic void addControlPacket(Packet packet) {\n\t\twaitingControlPackets.add(packet);\n\t}\n\n\t@Override\n\tpublic int compareTo(S2SConnection o) {\n\t\treturn hashCode() - o.hashCode();\n\t}\n\n\tpublic String getIPAddress() {\n\t\treturn ipAddress;\n\t}\n\n\tpublic S2SIOService getS2SIOService() {\n\t\treturn service;\n\t}\n\n\tpublic void setS2SIOService(S2SIOService serv) {\n\t\tthis.service = serv;\n\t}\n\n\tpublic int getWaitingControlCount() {\n\t\treturn waitingControlPackets.size();\n\t}\n\n\tpublic boolean isConnected() {\n\t\treturn (service != null) && service.isConnected();\n\t}\n\n\tpublic void sendAllControlPackets() {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tfor (Packet packet : waitingControlPackets) {\n\t\t\t\tlog.log(Level.FINEST, \"Sending on connection: {0} control packet: {1}\", new Object[]{service, packet});\n\t\t\t}\n\t\t}\n\n\t\thandler.writePacketsToSocket(service, waitingControlPackets);\n\t}\n\n\tpublic boolean sendPacket(Packet packet) throws IOException {\n\t\treturn handler.writePacketToSocket(service, packet);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"S2S: \" + service;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/S2SConnectionHandlerIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver;\n\nimport tigase.server.Packet;\nimport tigase.util.common.TimerTask;\nimport tigase.xml.Element;\nimport tigase.xmpp.XMPPIOService;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Created: Dec 9, 2010 11:40:28 PM\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface S2SConnectionHandlerIfc<IO extends XMPPIOService<?>> {\n\n\t/**\n\t * Returns stream features available for particular S2S connection.\n\t *\n\t * @param serv {@link S2SIOService} for which stream features should be retrieved\n\t *\n\t * @return list of stream features\n\t */\n\tpublic List<Element> getStreamFeatures(S2SIOService serv);\n\n\tboolean addOutPacket(Packet packet);\n\n\tvoid addTimerTask(TimerTask task, long delay, TimeUnit unit);\n\n\tCIDConnections getCIDConnections(CID cid, boolean createNew) throws NotLocalhostException, LocalhostException;\n\n\tvoid validateCIDConnection(CID cid) throws NotLocalhostException, LocalhostException;\n\n\tCIDConnections.CIDConnectionsOpenerService getConnectionOpenerService();\n\n\tBareJID getDefHostName();\n\n\t/**\n\t * Returns secret used for particular domain\n\t *\n\t * @param domain for which secret should be returned\n\t *\n\t * @return for particular domain\n\t *\n\t * @throws NotLocalhostException if the domain is not local\n\t */\n\tString getSecretForDomain(String domain) throws NotLocalhostException;\n\n\tString getServerNameForDomain(String domain);\n\n\tvoid initNewConnection(Map<String, Object> port_props);\n\n\t/**\n\t * Checks if TLS is required for particular domain\n\t *\n\t * @param domain for which secret should be returned\n\t *\n\t * @return boolean indicating whether TLS is required\n\t */\n\tboolean isTlsRequired(String domain);\n\n\tboolean isTlsWantClientAuthEnabled();\n\n\tboolean isTlsNeedClientAuthEnabled();\n\n\tboolean sendVerifyResult(String elem_name, CID connCid, CID keyCid, Boolean valid, String key_sessionId,\n\t\t\t\t\t\t\t String serv_sessionId, String cdata, boolean handshakingOnly);\n\n\tboolean sendVerifyResult(String elem_name, CID connCid, CID keyCid, Boolean valid, String key_sessionId,\n\t\t\t\t\t\t\t String serv_sessionId, String cdata, boolean handshakingOnly, Element errorElem);\n\n\tvoid serviceConnected(IO service);\n\n\tboolean writePacketToSocket(IO serv, Packet packet);\n\n\tvoid writePacketsToSocket(IO serv, Queue<Packet> packets);\n\n\tvoid writeRawData(IO serv, String strError);\n\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/S2SConnectionManager.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver;\n\nimport tigase.cert.CertCheckResult;\nimport tigase.cert.CertificateUtil;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\nimport tigase.net.ConnectionType;\nimport tigase.server.ConnectionManager;\nimport tigase.server.Packet;\nimport tigase.server.Permissions;\nimport tigase.stats.StatisticsList;\nimport tigase.stats.StatisticsProviderIfc;\nimport tigase.vhosts.VHostItem;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.PacketErrorTypeException;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.JID;\n\nimport javax.script.Bindings;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Jun 14, 2010 11:59:38 AM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\n@Bean(name = \"s2s\", parent = Kernel.class, active = true)\n@ConfigType({ConfigTypeEnum.DefaultMode, ConfigTypeEnum.ConnectionManagersMode})\npublic class S2SConnectionManager\n\t\textends ConnectionManager<S2SIOService>\n\t\timplements S2SConnectionHandlerIfc<S2SIOService> {\n\n\tpublic static final String CID_CONNECTIONS_BIND = \"cidConnections\";\n\n\tpublic static final String CID_KEY = \"cid\";\n\n\tpublic static final String CID_CONNECTIONS_TASKS_THREADS_KEY = \"cid-connections-tasks-threads\";\n\n\tpublic static final String MAX_CONNECTION_INACTIVITY_TIME_PROP_KEY = \"max-inactivity-time\";\n\n\tpublic static final String MAX_INCOMING_CONNECTIONS_PROP_KEY = \"max-in-conns\";\n\n\tpublic static final int MAX_INCOMING_CONNECTIONS_PROP_VAL = 4;\n\n\tpublic static final String MAX_OUT_PER_IP_CONNECTIONS_PROP_KEY = \"max-out-per-ip-conns\";\n\n\tpublic static final int MAX_OUT_PER_IP_CONNECTIONS_PROP_VAL = 1;\n\n\tpublic static final String MAX_OUT_TOTAL_CONNECTIONS_PROP_KEY = \"max-out-total-conns\";\n\n\tpublic static final int MAX_OUT_TOTAL_CONNECTIONS_PROP_VAL = 1;\n\n\tpublic static final String MAX_PACKET_WAITING_TIME_PROP_KEY = \"max-packet-waiting-time\";\n\n\tpublic static final String S2S_CONNECTION_SELECTOR_PROP_KEY = \"s2s-conn-selector\";\n\n\tpublic static final String S2S_CONNECTION_SELECTOR_PROP_VAL = \"tigase.server.xmppserver.S2SRandomSelector\";\n\n\tpublic static final String S2S_DOMAIN_MAPPING_PROP_KEY = \"s2s-domain-mapping\";\n\n\tpublic static final String S2S_DOMAIN_MAPPING_PROP_VAL = \"\";\n\n\tpublic static final String S2S_HT_TRAFFIC_THROTTLING_PROP_VAL = \"xmpp:15k:0:disc,bin:120m:0:disc\";\n\tpublic static final long MAX_PACKET_WAITING_TIME_PROP_VAL = 7 * MINUTE;\n\n\t// TODO: #1195 - estimate proper default value\n\tpublic static final int CID_CONNECTIONS_TASKS_THREADS_VAL = Runtime.getRuntime().availableProcessors();\n\tpublic static final String XMLNS_SERVER_VAL = \"jabber:server\";\n\tprotected static final String DB_RESULT_EL_NAME = \"db:result\";\n\tprotected static final String DB_VERIFY_EL_NAME = \"db:verify\";\n\tprivate static final Logger log = Logger.getLogger(S2SConnectionManager.class.getName());\n\tprivate static final String PROCESSORS_CONF_PROP_KEY = \"processors-conf\";\n\tprivate static final String XMLNS_CLIENT_VAL = \"jabber:client\";\n\t/**\n\t * Outgoing and incoming connections for a given domains pair (localdomain, remotedomain)\n\t */\n\tprotected Map<CID, CIDConnections> cidConnections = new ConcurrentHashMap<CID, CIDConnections>(10000);\n\t@Inject\n\tprivate CIDConnections.CIDConnectionsOpenerService cidConnectionsOpenerService;\n\t// ~--- fields ---------------------------------------------------------------\n\t@Inject\n\tprivate S2SConnectionSelector connSelector;\n\t/**\n\t * Holds list of manually entered mappings which provide substitutions for domains matching pattens with names of\n\t * servers to which we should connect.\n\t */\n\t@Inject\n\tprivate DomainServerNameMapper domainServerNameMapper;\n\t@Inject\n\tprivate List<S2SFilterIfc> filters = Collections.emptyList();\n\tprivate int maxINConnections = MAX_INCOMING_CONNECTIONS_PROP_VAL;\n\tprivate int maxOUTPerIPConnections = MAX_OUT_PER_IP_CONNECTIONS_PROP_VAL;\n\tprivate int maxOUTTotalConnections = MAX_OUT_TOTAL_CONNECTIONS_PROP_VAL;\n\t/**\n\t * <code>maxPacketWaitingTime</code> keeps the maximum time packets can wait for sending in ServerPacketQueue.\n\t * Packets are put in the queue only when connection to remote server is not established so effectively this timeout\n\t * specifies the maximum time for connecting to remote server. If this time is exceeded then no more reconnecting\n\t * attempts are performed and packets are sent back with error information.\n\t * <br>\n\t * Default TCP/IP timeout is 300 seconds so we can follow this convention but administrator can set different\n\t * timeout in server configuration.\n\t */\n\tprivate long maxPacketWaitingTime = MAX_PACKET_WAITING_TIME_PROP_VAL;\n\n\t@ConfigField(desc = \"Accept self-signed certificates for outgoing S2S connections\")\n\tprivate boolean acceptSelfSignedSslCertificates = false;\n\t@ConfigField(desc = \"Whether s2s connection is required to be authenticated both ways before allowing transmission\", alias = \"one-way-authentication\")\n\tprivate boolean oneWayAuthentication = false;\n\t/**\n\t * List of processors which should handle all traffic incoming from the network. In most cases if not all, these\n\t * processors handle just protocol traffic, all the rest traffic should be passed on to MR.\n\t */\n\t@Inject\n\tprivate List<S2SProcessor> processors = Collections.emptyList();\n\n\t@Override\n\tpublic boolean addOutPacket(Packet packet) {\n\t\tif (packet.getPacketFrom() == null) {\n\t\t\tpacket.setPacketFrom(getComponentId());\n\t\t}\n\t\treturn super.addOutPacket(packet);\n\t}\n\n\t@Override\n\tpublic void addTimerTask(tigase.util.common.TimerTask task, long delay, TimeUnit unit) {\n\t\tsuper.addTimerTask(task, delay, unit);\n\t}\n\n\t@Override\n\tpublic boolean handlesNonLocalDomains() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int hashCodeForPacket(Packet packet) {\n\n\t\t// Calculate hash code from the destination domain name to make sure packets\n\t\t// for\n\t\t// a single domain are processed by the same thread to avoid race condition\n\t\t// creating new connection data structures for a destination domain\n\t\tif (packet.getStanzaTo() != null) {\n\t\t\treturn packet.getStanzaTo().getDomain().hashCode();\n\t\t}\n\n\t\t// Otherwise, it might be a control packet which can be processed by single\n\t\t// thread\n\t\treturn 1;\n\t}\n\n\t@Override\n\tpublic void initBindings(Bindings binds) {\n\t\tsuper.initBindings(binds);\n\t\tbinds.put(CID_CONNECTIONS_BIND, cidConnections);\n\t}\n\n\t@Override\n\tpublic void initNewConnection(Map<String, Object> port_props) {\n\t\taddWaitingTask(port_props);\n\t}\n\n\t@Override\n\tpublic void processPacket(Packet packet) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Processing packet: {0}\", packet);\n\t\t}\n\t\tif ((packet.getStanzaTo() == null) || packet.getStanzaTo().getDomain().trim().isEmpty()) {\n\t\t\tlog.log(Level.WARNING, \"Missing ''to'' attribute, ignoring packet...{0}\" +\n\t\t\t\t\t\"\\n This most likely happens due to missconfiguration of components\" + \" domain names.\", packet);\n\n\t\t\treturn;\n\t\t}\n\t\tif ((packet.getStanzaFrom() == null) || packet.getStanzaFrom().getDomain().trim().isEmpty()) {\n\t\t\tlog.log(Level.WARNING, \"Missing ''from'' attribute, ignoring packet...{0}\", packet);\n\n\t\t\treturn;\n\t\t}\n\n\t\tString to_hostname = packet.getStanzaTo().getDomain();\n\n\t\ttry {\n\t\t\tString from_hostname = packet.getStanzaFrom().getDomain();\n\t\t\tCID cid = new CID(from_hostname, to_hostname);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Connection ID is: {0}\", cid);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tCIDConnections cid_conns = getCIDConnections(cid, true);\n\t\t\t\tPacket server_packet = packet.copyElementOnly();\n\n\t\t\t\tserver_packet.getElement().removeAttribute(\"xmlns\");\n\t\t\t\tcid_conns.sendPacket(server_packet);\n\t\t\t} catch (NotLocalhostException e) {\n\t\t\t\taddOutPacket(Authorization.NOT_ACCEPTABLE.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"S2S - Incorrect source address (\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t from_hostname +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \") - none of any local virtual hosts or components.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true));\n\t\t\t} catch (LocalhostException e) {\n\t\t\t\taddOutPacket(Authorization.NOT_ACCEPTABLE.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"S2S - Incorrect destination address \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t to_hostname +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \" - one of local virtual hosts or components.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true));\n\t\t\t}\n\t\t} catch (PacketErrorTypeException e) {\n\t\t\tlog.log(Level.WARNING, \"Packet processing exception\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Queue<Packet> processSocketData(S2SIOService serv) {\n\t\tQueue<Packet> packets = serv.getReceivedPackets();\n\t\tPacket p = null;\n\t\tQueue<Packet> results = new ArrayDeque<Packet>(2);\n\n\t\twhile ((p = packets.poll()) != null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Processing socket data: {0} [{1}]\", new Object[]{p, serv});\n\t\t\t}\n\t\t\tif (p.getXMLNS() == null) {\n\t\t\t\tp.setXMLNS(XMLNS_SERVER_VAL);\n\t\t\t}\n\n\t\t\tboolean processed = false;\n\n\t\t\tfor (S2SProcessor proc : processors) {\n\t\t\t\tprocessed |= proc.process(p, serv, results);\n\t\t\t\twritePacketsToSocket(serv, results);\n\t\t\t}\n\n\t\t\tif (!processed) {\n\t\t\t\tfor (S2SFilterIfc filter : filters) {\n\t\t\t\t\tprocessed |= filter.filter(p, serv, results);\n\t\t\t\t\twritePacketsToSocket(serv, results);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!processed) {\n\n\t\t\t\t// Sometimes xmlns is not set for the packet. Usually it does not\n\t\t\t\t// cause any problems but when the packet is sent over the s2s, ext\n\t\t\t\t// or cluster connection it may be quite problematic.\n\t\t\t\t// Let's force jabber:client xmlns for all packets received from s2s\n\t\t\t\t// connection\n\t\t\t\t// In theory null does not hurt, but if the packet goes through the\n\t\t\t\t// cluster\n\t\t\t\t// connection is gets cluster XMLNS\n\t\t\t\tif ((p.getXMLNS() == XMLNS_SERVER_VAL) || (p.getXMLNS() == null)) {\n\t\t\t\t\tp.setXMLNS(XMLNS_CLIENT_VAL);\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tif (isLocalDomainOrComponent(p.getStanzaTo().getDomain())) {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Adding packet out: {0} [{1}]\", new Object[]{p, serv});\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// TODO: not entirely sure if this is a good idea....\n\t\t\t\t\t\t// Let's check it out.\n\t\t\t\t\t\tp.setPermissions(Permissions.REMOTE);\n\t\t\t\t\t\taddOutPacket(p);\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tserv.addPacketToSend(Authorization.NOT_ACCEPTABLE.getResponseMessage(p,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Not a local virtual domain or component\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true));\n\t\t\t\t\t\t} catch (PacketErrorTypeException ex) {\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.log(Level.CONFIG, \"Unexpected exception for packet: \" + p, e);\n\t\t\t\t}\n\t\t\t}\n\t\t}    // end of while ()\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean processUndeliveredPacket(Packet packet, Long stamp, String errorMessage) {\n\t\t// re-add packet - this may be good as we would retry to send packet\n\t\t// which delivery failed due to IO error\n\n\t\tfor (S2SProcessor proc : processors) {\n\t\t\tif (proc.shouldSkipUndelivered(packet)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\taddPacket(packet);\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void reconnectionFailed(Map<String, Object> port_props) {\n\t\tCID cid = (CID) port_props.get(CID_KEY);\n\n\t\tif (cid == null) {\n\t\t\tlog.log(Level.WARNING, \"Protocol error cid not set for outgoing connection: {0}\", port_props);\n\n\t\t\treturn;\n\t\t}\n\n\t\tCIDConnections cid_conns = getCIDConnections(cid);\n\n\t\tif (cid_conns == null) {\n\t\t\tlog.log(Level.WARNING, \"Protocol error cid_conns not found for outgoing connection: {0}\", port_props);\n\n\t\t\treturn;\n\t\t} else {\n\t\t\tcid_conns.reconnectionFailed(port_props);\n\t\t}\n\t}\n\n\t@Override\n\tpublic int schedulerThreads() {\n\n\t\t// TODO: #1195 - estimate proper default value\n\t\treturn Runtime.getRuntime().availableProcessors();\n\t}\n\n\t@Override\n\tpublic boolean sendVerifyResult(String elem_name, CID connCid, CID keyCid, Boolean valid, String key_sessionId,\n\t\t\t\t\t\t\t\t\tString serv_sessionId, String cdata, boolean handshakingOnly) {\n\t\treturn this.sendVerifyResult(elem_name, connCid, keyCid, valid, key_sessionId, serv_sessionId, cdata,\n\t\t\t\t\t\t\t\t\t handshakingOnly, null);\n\t}\n\n\t@Override\n\tpublic boolean sendVerifyResult(String elem_name, CID connCid, CID keyCid, Boolean valid, String key_sessionId,\n\t\t\t\t\t\t\t\t\tString serv_sessionId, String cdata, boolean handshakingOnly, Element errorElem) {\n\t\tCIDConnections cid_conns = getCIDConnections(connCid);\n\t\tlog.log(Level.FINEST,\n\t\t\t\t\"Sending verification result: {1}, session: {2}, handshaking: {3}, cdata: {4}, error: {5} [{0}]\",\n\t\t\t\tnew Object[]{cid_conns, valid, serv_sessionId, handshakingOnly, cdata, errorElem});\n\n\t\tif (cid_conns != null) {\n\t\t\tStanzaType type = null;\n\t\t\tif (valid != null) {\n\t\t\t\tif (valid) {\n\t\t\t\t\ttype = StanzaType.valid;\n\t\t\t\t} else {\n\t\t\t\t\ttype = StanzaType.invalid;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (errorElem != null) {\n\t\t\t\ttype = StanzaType.error;\n\t\t\t}\n\t\t\tPacket verify_valid = getValidResponse(elem_name, keyCid, key_sessionId, type, cdata);\n\n\t\t\tif (errorElem != null) {\n\t\t\t\tverify_valid.getElement().addChild(errorElem);\n\t\t\t}\n\n\t\t\tif (handshakingOnly) {\n\t\t\t\tcid_conns.sendHandshakingOnly(verify_valid);\n\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\treturn cid_conns.sendControlPacket(serv_sessionId, verify_valid);\n\t\t\t}\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"Can''t find CID connections for cid: {0}, can''t send verify response.\", keyCid);\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void serviceConnected(S2SIOService service) {\n\t\tsuper.serviceConnected(service);\n\t}\n\n\t@Override\n\tpublic void serviceStarted(S2SIOService serv) {\n\t\tsuper.serviceStarted(serv);\n\t\tfinal CID cid = (CID)serv.getSessionData().get(CID_KEY);\n\t\tif (cid != null) {\n\t\t\tserv.setUserJid(cid.toString());\n\t\t\tserv.setConnectionId(JID.jidInstanceNS(cid.getLocalHost(), cid.getRemoteHost(), UUID.randomUUID().toString()));\n\t\t} else {\n\t\t\tserv.setConnectionId(JID.jidInstanceNS(null, serv.getUniqueId(), UUID.randomUUID().toString()));\n\t\t}\n\t\tlog.log(Level.CONFIG, \"s2s connection opened: {0}\", serv);\n\t\tfor (S2SProcessor proc : processors) {\n\t\t\tproc.serviceStarted(serv);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean serviceStopped(S2SIOService serv) {\n\t\tboolean result = super.serviceStopped(serv);\n\n\t\tif (result) {\n\t\t\tfor (S2SProcessor proc : processors) {\n\t\t\t\tproc.serviceStopped(serv);\n\t\t\t}\n\t\t}\n\n\t\tif (log.isLoggable(Level.CONFIG)) {\n\t\t\tlog.log(Level.CONFIG, \"[[{0}]] S2S Connection stopped: {1}\", new Object[]{getName(), serv});\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic void tlsHandshakeCompleted(S2SIOService serv) {\n\t\tif ((!acceptSelfSignedSslCertificates) && serv.connectionType() == ConnectionType.connect) {\n\t\t\tif (serv.getSessionData().get(S2SIOService.CERT_CHECK_RESULT) != CertCheckResult.trusted) {\n\t\t\t\tserv.stop();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tfor (S2SProcessor proc : processors) {\n\t\t\tproc.serviceStarted(serv);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void writeRawData(S2SIOService ios, String data) {\n\t\tsuper.writeRawData(ios, data);\n\t}\n\n\t@Override\n\tpublic void xmppStreamClosed(S2SIOService serv) {\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.log(Level.FINER, \"Stream closed. {0}\", new Object[]{serv});\n\t\t}\n\t\tfor (S2SProcessor proc : processors) {\n\t\t\tproc.streamClosed(serv);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String[] xmppStreamOpened(S2SIOService serv, Map<String, String> attribs) {\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.log(Level.FINER, \"Stream opened: {1} [{0}]\", new Object[]{serv, attribs});\n\t\t}\n\n\t\tStringBuilder sb = new StringBuilder(256);\n\n\t\tfor (S2SProcessor proc : processors) {\n\t\t\tString res = proc.streamOpened(serv, attribs);\n\n\t\t\tif (res != null) {\n\t\t\t\tsb.append(res);\n\t\t\t}\n\t\t}\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.log(Level.FINER, \"Sending stream open: {1} [{0}]\", new Object[]{serv, sb});\n\t\t}\n\n\t\treturn (sb.length() == 0) ? null : new String[]{sb.toString()};\n\t}\n\n\t@Override\n\tpublic CIDConnections getCIDConnections(CID cid, boolean createNew)\n\t\t\tthrows NotLocalhostException, LocalhostException {\n\t\tCIDConnections result = getCIDConnections(cid);\n\n\t\tif ((result == null) && createNew && (cid != null)) {\n\t\t\tresult = createNewCIDConnections(cid);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic void validateCIDConnection(CID cid) throws NotLocalhostException, LocalhostException {\n\t\tif (cid.getLocalHost() == null || !isLocalDomainOrComponent(cid.getLocalHost())) {\n\t\t\tthrow new NotLocalhostException(\"This is not a valid local domain: \" + cid.getLocalHost());\n\t\t}\n\t\tif (cid.getRemoteHost() != null && isLocalDomainOrComponent(cid.getRemoteHost())) {\n\t\t\tthrow new LocalhostException(\"This is not a valid remotehost: \" + cid.getRemoteHost());\n\t\t}\n\t}\n\n\t@Override\n\tpublic CIDConnections.CIDConnectionsOpenerService getConnectionOpenerService() {\n\t\treturn cidConnectionsOpenerService;\n\t}\n\n\t@Override\n\tpublic String getDiscoCategoryType() {\n\t\treturn \"s2s\";\n\t}\n\n\t@Override\n\tpublic String getDiscoDescription() {\n\t\treturn \"S2S connection manager\";\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t * <br>\n\t * Secret is used in generation of dialback key\n\t */\n\t@Override\n\tpublic String getSecretForDomain(String domain) throws NotLocalhostException {\n\t\tVHostItem item = vHostManager.getVHostItem(domain);\n\t\tif (item == null) {\n\t\t\tif (this.isLocalDomainOrComponent(domain)) {\n\t\t\t\tint idx = domain.indexOf('.');\n\t\t\t\tif (idx > 0) {\n\t\t\t\t\tString basedomain = domain.substring(idx + 1);\n\t\t\t\t\titem = vHostManager.getVHostItem(basedomain);\n\t\t\t\t}\n\n\t\t\t\tif (item == null) {\n\t\t\t\t\titem = vHostManager.getVHostItem(vHostManager.getDefVHostItem().toString());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (item == null) {\n\t\t\tthrow new NotLocalhostException(\"This is not a valid localhost: \" + domain);\n\t\t}\n\n\t\treturn item.getS2sSecret();\n\t}\n\n\t@Override\n\tpublic String getServerNameForDomain(String domain) {\n\t\treturn domainServerNameMapper.getServerNameForDomain(domain);\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t\tsuper.getStatistics(list);\n\t\tlist.add(getName(), \"CIDs number\", cidConnections.size(), Level.INFO);\n\t\tif (list.checkLevel(Level.FINEST)) {\n\t\t\tlong total_outgoing = 0;\n\t\t\tlong total_outgoing_tls = 0;\n\t\t\tlong total_outgoing_handshaking = 0;\n\t\t\tlong total_incoming = 0;\n\t\t\tlong total_incoming_tls = 0;\n\t\t\tlong total_dbKeys = 0;\n\t\t\tlong total_waiting = 0;\n\t\t\tlong total_waiting_control = 0;\n\n\t\t\tfor (Map.Entry<CID, CIDConnections> cid_conn : cidConnections.entrySet()) {\n\t\t\t\tint outgoing = cid_conn.getValue().getOutgoingCount();\n\t\t\t\tint outgoing_tls = cid_conn.getValue().getOutgoingTLSCount();\n\t\t\t\tint outgoing_handshaking = cid_conn.getValue().getOutgoingHandshakingCount();\n\t\t\t\tint incoming = cid_conn.getValue().getIncomingCount();\n\t\t\t\tint incoming_tls = cid_conn.getValue().getIncomingTLSCount();\n\t\t\t\tint dbKeys = cid_conn.getValue().getDBKeysCount();\n\t\t\t\tint waiting = cid_conn.getValue().getWaitingCount();\n\t\t\t\tint waiting_control = cid_conn.getValue().getWaitingControlCount();\n\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\n\t\t\t\t\t// Throwable thr = new Throwable();\n\t\t\t\t\t//\n\t\t\t\t\t// thr.fillInStackTrace();\n\t\t\t\t\t// log.log(Level.FINEST, \"Called from: \", thr);\n\t\t\t\t\tlog.log(Level.FINEST, \"CID: {0}, OUT: {1}, OUT_HAND: {2}, IN: {3}, dbKeys: {4}, \" +\n\t\t\t\t\t\t\t\t\t\"waiting: {5}, waiting_control: {6}\",\n\t\t\t\t\t\t\tnew Object[]{cid_conn.getKey(), outgoing, outgoing_handshaking, incoming, dbKeys, waiting,\n\t\t\t\t\t\t\t\t\t\t waiting_control});\n\t\t\t\t}\n\t\t\t\ttotal_outgoing += outgoing;\n\t\t\t\ttotal_outgoing_tls += outgoing_tls;\n\t\t\t\ttotal_outgoing_handshaking += outgoing_handshaking;\n\t\t\t\ttotal_incoming += incoming;\n\t\t\t\ttotal_incoming_tls += incoming_tls;\n\t\t\t\ttotal_dbKeys += dbKeys;\n\t\t\t\ttotal_waiting += waiting;\n\t\t\t\ttotal_waiting_control += waiting_control;\n\t\t\t}\n\t\t\tlist.add(getName(), \"Total outgoing\", total_outgoing, Level.FINEST);\n\t\t\tlist.add(getName(), \"Total outgoing TLS\", total_outgoing_tls, Level.FINEST);\n\t\t\tlist.add(getName(), \"Total outgoing handshaking\", total_outgoing_handshaking, Level.FINEST);\n\t\t\tlist.add(getName(), \"Total incoming\", total_incoming, Level.FINEST);\n\t\t\tlist.add(getName(), \"Total incoming TLS\", total_incoming_tls, Level.FINEST);\n\t\t\tlist.add(getName(), \"Total DB keys\", total_dbKeys, Level.FINEST);\n\t\t\tlist.add(getName(), \"Total waiting\", total_waiting, Level.FINEST);\n\t\t\tlist.add(getName(), \"Total control waiting\", total_waiting_control, Level.FINEST);\n\t\t}\n\t\tfor (S2SProcessor processor : processors) {\n\t\t\t((StatisticsProviderIfc)processor).getStatistics(getName(), list);\n\t\t}\n\t}\n\n\t@Override\n\tpublic List<Element> getStreamFeatures(S2SIOService serv) {\n\t\tList<Element> results = new ArrayList<Element>(10);\n\n\t\tfor (S2SProcessor proc : processors) {\n\t\t\tproc.streamFeatures(serv, results);\n\t\t}\n\n\t\treturn results;\n\t}\n\n\t@Override\n\tpublic boolean isTlsRequired(String domain) {\n\t\tVHostItem item = vHostManager.getVHostItemDomainOrComponent(domain);\n\t\treturn item != null && item.isTlsRequired();\n\t}\n\n\t@Override\n\tpublic boolean isTlsWantClientAuthEnabled() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean isTlsNeedClientAuthEnabled() {\n\t\treturn false;\n\t}\n\n\tpublic void setProcessors(List<S2SProcessor> processors) {\n\t\tList<S2SProcessor> tmp_processors = new ArrayList<>(processors);\n\t\tCollections.sort(tmp_processors);\n\t\tthis.processors = Collections.unmodifiableList(tmp_processors);\n\t}\n\n\t@Override\n\tprotected int[] getDefPlainPorts() {\n\t\treturn new int[]{5269};\n\t}\n\n\t/**\n\t * Method from ConnectionManager is overriden as it uses local value S2S_HT_TRAFFIC_THROTTLING_PROP_VAL\n\t */\n\t@Override\n\tprotected String getDefTrafficThrottling() {\n\t\tString result = ST_TRAFFIC_THROTTLING_PROP_VAL;\n\n\t\tif (isHighThroughput()) {\n\t\t\tresult = S2S_HT_TRAFFIC_THROTTLING_PROP_VAL;\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tprotected long getMaxInactiveTime() {\n\t\treturn 120 * MINUTE;\n\t}\n\n\t@Override\n\tprotected S2SIOService getXMPPIOServiceInstance() {\n\t\treturn new S2SIOService();\n\t}\n\n\t@Override\n\tprotected boolean isHighThroughput() {\n\t\treturn true;\n\t}\n\n\tprotected CIDConnections createNewCIDConnections(CID cid) throws NotLocalhostException, LocalhostException {\n\t\tif (!isLocalDomainOrComponent(cid.getLocalHost())) {\n\t\t\tthrow new NotLocalhostException(\"This is not a valid localhost: \" + cid.getLocalHost());\n\t\t}\n\t\tif (isLocalDomainOrComponent(cid.getRemoteHost())) {\n\t\t\tthrow new LocalhostException(\"This is not a valid remotehost: \" + cid.getRemoteHost());\n\t\t}\n\n\t\tCIDConnections cid_conns = new CIDConnections(cid, this, connSelector, maxINConnections, maxOUTTotalConnections,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  maxOUTPerIPConnections, maxPacketWaitingTime, oneWayAuthentication);\n\n\t\tcidConnections.put(cid, cid_conns);\n\n\t\treturn cid_conns;\n\t}\n\n\t// ~--- get methods ----------------------------------------------------------\n\tprivate CIDConnections getCIDConnections(CID cid) {\n\t\tif (cid == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn cidConnections.get(cid);\n\t}\n\n\tprivate Packet getValidResponse(String elem_name, CID cid, String id, StanzaType type, String cdata) {\n\t\tElement elem = new Element(elem_name);\n\n\t\tif (cdata != null) {\n\t\t\telem.setCData(cdata);\n\t\t}\n\t\tif (type != null) {\n\t\t\telem.addAttribute(\"type\", type.name());\n\t\t}\n\t\tif (id != null) {\n\t\t\telem.addAttribute(\"id\", id);\n\t\t}\n\n\t\tPacket result = Packet.packetInstance(elem, JID.jidInstanceNS(cid.getLocalHost()),\n\t\t\t\t\t\t\t\t\t\t\t  JID.jidInstanceNS(cid.getRemoteHost()));\n\n\t\treturn result;\n\t}\n\n\t@Bean(name = \"domainServerNameMapper\", parent = S2SConnectionManager.class, active = true)\n\tpublic static class DomainServerNameMapper {\n\n\t\t@ConfigField(desc = \"Rules for mapping domains\")\n\t\tprivate List<Entry> entries = new ArrayList<Entry>();\n\n\t\tpublic DomainServerNameMapper() {\n\t\t}\n\n\t\tpublic String getServerNameForDomain(String domain) {\n\t\t\tfor (Entry e : entries) {\n\t\t\t\tif (e.matches(domain)) {\n\t\t\t\t\treturn e.getServerName();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn domain;\n\t\t}\n\n\t\tpublic Map<String, String> getEntries() {\n\t\t\tMap<String, String> result = new HashMap<>();\n\t\t\tfor (Entry e : entries) {\n\t\t\t\tresult.put(e.pattern, e.getServerName());\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\t\tpublic void setEntries(Map<String, String> entries) {\n\t\t\tentries.clear();\n\t\t\tfor (Map.Entry<String, String> e : entries.entrySet()) {\n\t\t\t\taddEntry(e.getKey(), e.getValue());\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\tsb.append(getClass().getName()).append(\"[\");\n\t\t\tboolean first = true;\n\t\t\tfor (Entry e : entries) {\n\t\t\t\tif (!first) {\n\t\t\t\t\tsb.append(\",\");\n\t\t\t\t}\n\t\t\t\tsb.append(e.pattern);\n\t\t\t\tsb.append(\"=\");\n\t\t\t\tsb.append(e.serverName);\n\t\t\t\tfirst = false;\n\t\t\t}\n\t\t\tsb.append(\"]\");\n\t\t\treturn sb.toString();\n\t\t}\n\n\t\tprotected void addEntry(String pattern, String serverName) {\n\t\t\t// clone list to fix possible concurrency issues with collection\n\t\t\t// could use CopyOnWriteArrayList but sorting this collection\n\t\t\t// is not possible on JDK7\n\t\t\tsynchronized (this) {\n\t\t\t\tList<Entry> entries = new ArrayList<Entry>(this.entries);\n\t\t\t\tEntry e = new Entry(pattern, serverName);\n\t\t\t\tentries.add(e);\n\t\t\t\tCollections.sort(entries);\n\t\t\t\tthis.entries = entries;\n\t\t\t}\n\t\t}\n\n\t\tprivate class Entry\n\t\t\t\timplements Comparable<Entry> {\n\n\t\t\tprivate final String pattern;\n\t\t\tprivate final String serverName;\n\n\t\t\tpublic Entry(String pattern, String serverName) {\n\t\t\t\tthis.pattern = pattern.toLowerCase();\n\t\t\t\tthis.serverName = serverName;\n\t\t\t}\n\n\t\t\tpublic String getServerName() {\n\t\t\t\treturn serverName;\n\t\t\t}\n\n\t\t\tpublic boolean matches(String domain) {\n\t\t\t\tif (\"*\".equals(pattern)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn CertificateUtil.match(domain, pattern);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean equals(Object obj) {\n\t\t\t\tif (obj instanceof Entry) {\n\t\t\t\t\treturn pattern.equals(((Entry) obj).pattern);\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic int hashCode() {\n\t\t\t\treturn pattern.hashCode();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic int compareTo(Entry o) {\n\t\t\t\tif (o.pattern.contains(\"*\")) {\n\t\t\t\t\tif (!pattern.contains(\"*\")) {\n\t\t\t\t\t\treturn -1;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (pattern.contains(\"*\")) {\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tint val = (pattern.split(\"\\\\.\").length - o.pattern.split(\"\\\\.\").length) * -1;\n\t\t\t\tif (val != 0) {\n\t\t\t\t\treturn val;\n\t\t\t\t}\n\t\t\t\treturn o.pattern.length() - pattern.length();\n\t\t\t}\n\t\t}\n\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/S2SConnectionSelector.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver;\n\nimport tigase.server.Packet;\n\nimport java.util.Set;\n\n/**\n * Created: Jun 26, 2010 9:38:19 AM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface S2SConnectionSelector {\n\n\tS2SConnection selectConnection(Packet packet, Set<S2SConnection> outgoing);\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/S2SFilterIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver;\n\nimport tigase.server.Packet;\n\nimport java.util.Map;\nimport java.util.Queue;\n\n/**\n * Created by andrzej on 19.05.2017.\n */\npublic interface S2SFilterIfc {\n\n\tvoid init(S2SConnectionHandlerIfc<S2SIOService> handler, Map<String, Object> props);\n\n\tboolean filter(Packet p, S2SIOService serv, Queue<Packet> results);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/S2SIOService.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver;\n\nimport tigase.xmpp.XMPPIOService;\n\nimport java.util.Set;\nimport java.util.concurrent.CopyOnWriteArraySet;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Jun 14, 2010 12:30:53 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class S2SIOService\n\t\textends XMPPIOService<Object> {\n\n\tpublic static final String S2S_CONNECTION_KEY = \"s2s-connection-key\";\n\tpublic static final String HANDSHAKING_DOMAIN_KEY = \"handshaking-domain-key\";\n\tprotected static final String HANDSHAKING_ONLY_KEY = \"handshaking-only-key\";\n\tprivate static final Logger log = Logger.getLogger(S2SIOService.class.getName());\n\n\t/**\n\t * This structure keeps a set of all CIDs reusing this connection. If the connection goes down all CIDs must be\n\t * notified.\n\t */\n\tprivate Set<CID> authenticatedCIDsOUT = new CopyOnWriteArraySet<CID>();\n\tprivate Set<CID> authenticatedCIDsIN = new CopyOnWriteArraySet<CID>();\n\tprivate boolean streamNegotiationCompleted = false;\n\tprivate CIDConnections cid_conns = null;\n\tprivate String dbKey = null;\n\tprivate S2SConnection s2s_conn = null;\n\n\tprivate String session_id = null;\n\n\tenum DIRECTION {\n\t\tIN,\n\t\tOUT,\n\t\tBOTH,\n\t\tANY\n\t}\n\n\t/**\n\t * Adds another connection id (CID) to the authenticated list for this connection\n\t *\n\t */\n\tpublic void addCID(CID cid) {\n\t\taddCID(cid, DIRECTION.BOTH);\n\t}\n\n\tpublic void addCID(CID cid, DIRECTION direction) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Adding CID to authenticated: {1} [{0}]\", new Object[]{this, cid});\n\t\t}\n\n\t\tswitch (direction) {\n\t\t\tcase IN:\n\t\t\t\tauthenticatedCIDsIN.add(cid);\n\t\t\t\tbreak;\n\t\t\tcase OUT:\n\t\t\t\tauthenticatedCIDsOUT.add(cid);\n\t\t\t\tbreak;\n\t\t\tcase BOTH:\n\t\t\tcase ANY:\n\t\t\tdefault:\n\t\t\t\tauthenticatedCIDsIN.add(cid);\n\t\t\t\tauthenticatedCIDsOUT.add(cid);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tpublic Set<CID> getCIDs() {\n\t\tfinal CopyOnWriteArraySet<CID> cids = new CopyOnWriteArraySet<>();\n\t\tcids.addAll(authenticatedCIDsIN);\n\t\tcids.retainAll(authenticatedCIDsOUT);\n\t\treturn cids;\n\t}\n\n\tpublic S2SConnection getS2SConnection() {\n\t\treturn s2s_conn;\n\t}\n\n\tpublic void setS2SConnection(S2SConnection s2s_conn) {\n\t\tthis.s2s_conn = s2s_conn;\n\t}\n\n\tpublic String getSessionId() {\n\t\treturn session_id;\n\t}\n\n\tpublic void setSessionId(String session_id) {\n\t\tthis.session_id = session_id;\n\t}\n\n\tpublic boolean isAuthenticated(CID cid) {\n\t\treturn authenticatedCIDsOUT.contains(cid) && authenticatedCIDsIN.contains(cid);\n\t}\n\n\tpublic boolean isAuthenticated() {\n\t\treturn authenticatedCIDsOUT.size() > 0 && authenticatedCIDsIN.size() > 0;\n\t}\n\n\tpublic boolean isHandshakingOnly() {\n\t\treturn getSessionData().get(HANDSHAKING_ONLY_KEY) != null;\n\t}\n\n\tpublic boolean isStreamNegotiationCompleted() {\n\t\treturn streamNegotiationCompleted;\n\t}\n\n\tpublic void streamNegotiationCompleted() {\n\t\tlog.log(Level.FINEST, \"Marking the service as negotiated: \" + this);\n\t\tthis.streamNegotiationCompleted = true;\n\t}\n\n\tpublic void setDBKey(String key) {\n\t\tdbKey = key;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tCID cid = (CID) getSessionData().get(S2SConnectionManager.CID_KEY);\n\n\t\treturn \"CID: \" + cid + \", IN: \" + authenticatedCIDsIN.size() + \", OUT: \" + authenticatedCIDsOUT.size() +\n\t\t\t\t\", authenticated: \" + isAuthenticated() + \", remote-session-id: \" + getSessionId()\n\t\t\t\t+ \", streamNegotiationCompleted: \" + streamNegotiationCompleted + \", \" + super.toString();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/S2SProcessor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver;\n\nimport tigase.server.Packet;\nimport tigase.stats.StatisticsProviderIfc;\nimport tigase.xml.Element;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Queue;\n\n/**\n * Created: Dec 9, 2010 1:50:09 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\npublic interface S2SProcessor\n\t\textends Comparable<S2SProcessor>, StatisticsProviderIfc {\n\n\t/**\n\t * Returns order of processor which is used to set order in which processors will be processing packet\n\t */\n\tint order();\n\n\tvoid serviceStarted(S2SIOService serv);\n\n\tvoid serviceStopped(S2SIOService serv);\n\n\tvoid streamFeatures(S2SIOService serv, List<Element> results);\n\n\tvoid init(S2SConnectionHandlerIfc<S2SIOService> handler, Map<String, Object> props);\n\n\tboolean process(Packet p, S2SIOService serv, Queue<Packet> results);\n\n\tdefault boolean stopProcessing() {\n\t\treturn false;\n\t}\n\n\tvoid streamClosed(S2SIOService serv);\n\n\tString streamOpened(S2SIOService serv, Map<String, String> attribs);\n\n\t/**\n\t * Method determines whether not delivered packet (due to closed connection) should be skipped and not\n\t * added for re-delivery. We should not re-add certain packets such as stream features or SASL as those are related\n\t * to particular connection and if it got broken then there's no point in trying to re-deliver them.\n\t *\n\t * @param packet which was not delivered and is to be re-delivered\n\t *\n\t * @return {@code true} if the packet should be skipped/ignored or {@code false} if it is to be re-delivered.\n\t */\n\tdefault boolean shouldSkipUndelivered(Packet packet) {\n\t\treturn false;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/S2SRandomSelector.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Packet;\n\nimport java.util.Random;\nimport java.util.Set;\nimport java.util.logging.Logger;\n\n/**\n * Created: Jun 26, 2010 9:40:04 AM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"s2sRandomSelector\", parent = S2SConnectionManager.class, active = true)\npublic class S2SRandomSelector\n\t\timplements S2SConnectionSelector {\n\n\tprivate static final Logger log = Logger.getLogger(S2SRandomSelector.class.getName());\n\n\tprivate Random rand = new Random();\n\n\t@Override\n\tpublic S2SConnection selectConnection(Packet packet, Set<S2SConnection> outgoing) {\n\t\tint size = outgoing.size();\n\n\t\tif (size == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tint pos = rand.nextInt(size);\n\t\tS2SConnection result = null;\n\t\tint i = -1;\n\n\t\tfor (S2SConnection s2SConnection : outgoing) {\n\t\t\tif (++i == pos) {\n\t\t\t\tresult = s2SConnection;\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/proc/AuthenticationProcessor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.server.xmppserver.proc;\n\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Packet;\nimport tigase.server.xmppserver.S2SIOService;\nimport tigase.util.common.TimerTask;\n\nimport java.util.Queue;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic abstract class AuthenticationProcessor\n\t\textends S2SAbstractProcessor {\n\n\tprivate final static Logger log = Logger.getLogger(AuthenticationProcessor.class.getName());\n\tprivate final static String AUTHENTICATION_TIMER_KEY = \"AUTHENTICATION_TIMER_KEY\";\n\t@Inject\n\tprotected static AuthenticatorSelectorManager authenticatorSelectorManager;\n\t@ConfigField(desc = \"Authentication timeout for S2S connections\")\n\tprivate long authenticationTimeOut = 30;\n\n\t@Override\n\tpublic void serviceStarted(S2SIOService serv) {\n\t\tlog.log(Level.FINEST, \"s2s connection opened, isHandshaking: {1} [{0}]\",\n\t\t\t\tnew Object[]{serv, serv.isHandshakingOnly()});\n\n\t\tif (serv.getSessionData().get(AUTHENTICATION_TIMER_KEY) == null) {\n\t\t\tfinal AuthenticationTimer task = new AuthenticationTimer(serv);\n\t\t\thandler.addTimerTask(task, authenticationTimeOut, TimeUnit.SECONDS);\n\t\t\tserv.getSessionData().put(AUTHENTICATION_TIMER_KEY, task);\n\t\t}\n\t}\n\n\tabstract String getMethodName();\n\n\tabstract void restartAuth(Packet packet, S2SIOService serv, Queue<Packet> results);\n\n\t/**\n\t * Method intends to determine if authenticator can handle received packet/features\n\t */\n\tabstract boolean canHandle(Packet packet, S2SIOService serv, Queue<Packet> results);\n\n\tstatic class AuthenticationTimer\n\t\t\textends TimerTask {\n\n\t\tprivate S2SIOService serv = null;\n\n\t\tprivate AuthenticationTimer(S2SIOService serv) {\n\t\t\tthis.serv = serv;\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tif (!serv.isAuthenticated() && serv.isConnected()) {\n\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\tlog.log(Level.FINE, \"Connection not authenticated within timeout, stopping: {0}\", serv);\n\t\t\t\t}\n\t\t\t\tauthenticatorSelectorManager.markConnectionAsFailed(\"timeout\", serv);\n\t\t\t\tserv.stop();\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/proc/AuthenticatorSelectorManager.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.server.xmppserver.proc;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.net.ConnectionType;\nimport tigase.server.Packet;\nimport tigase.server.xmppserver.*;\nimport tigase.stats.StatisticsList;\nimport tigase.stats.StatisticsProviderIfc;\n\nimport java.io.IOException;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentSkipListSet;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Bean(name = \"authenticator-selector-manager\", parent = S2SConnectionManager.class, active = true)\npublic class AuthenticatorSelectorManager\n\t\timplements StatisticsProviderIfc {\n\n\tpublic static final String S2S_METHOD_USED = \"S2S_METHOD_USED\";\n\tpublic static final String S2S_METHODS_ADVERTISED = \"S2S_METHODS_ADVERTISED\";\n\tpublic static final String S2S_METHODS_AVAILABLE = \"S2S_METHODS_AVAILABLE\";\n\tprivate static final Logger log = Logger.getLogger(AuthenticatorSelectorManager.class.getName());\n\t@Inject\n\tpublic List<AuthenticationProcessor> authenticationProcessors;\n\n\tprivate Map<String, AtomicInteger> failedAuthenticationDomains = new ConcurrentHashMap<>();\n\n\t@Inject\n\tprivate KnownDomainsListProvider knownDomainsListProvider;\n\n\tpublic AuthenticatorSelectorManager() {}\n\n\t/**\n\t * Method determines if given authenticator is allowed to proceed: takes into consideration authenticators\n\t * priority, currently used authenticator as well as received stream futures\n\t */\n\tpublic boolean isAllowed(Packet p, S2SIOService serv, AuthenticationProcessor processor, Queue<Packet> results) {\n\n\t\tfinal boolean authenticated = serv.isAuthenticated();\n\t\tfinal SortedSet<AuthenticationProcessor> methodsAvailable = getAuthenticationProcessors(serv);\n\t\tfinal Optional<AuthenticationProcessor> currentAuthenticationProcessor = getCurrentAuthenticationProcessor(\n\t\t\t\tserv);\n\t\tif (authenticated) {\n\t\t\tlog.log(Level.FINE,\n\t\t\t\t\t\"Connection already authenticated, skipping processor: {1}, methodsAvailable: {2}, checking packet: {3} [{0}]\",\n\t\t\t\t\tnew Object[]{serv, processor, methodsAvailable, p});\n\t\t\treturn false;\n\t\t}\n\t\tlog.log(Level.FINE, \"Processor {1}, methodsAvailable: {2}, checking packet: {3} [{0}]\",\n\t\t\t\tnew Object[]{serv, processor.getMethodName(), methodsAvailable, p});\n\n\t\tboolean canHandle = processor.canHandle(p, serv, results);\n\t\tif (canHandle) {\n\t\t\tmethodsAvailable.add(processor);\n\t\t}\n\n\t\tfinal boolean result = !currentAuthenticationProcessor.isPresent() && canHandle;\n\t\tlog.log(Level.FINEST,\n\t\t\t\t\"Processor {1}, canHandle: {2}, currentAuthenticationProcessor: {3}, result: {4}, methodsAvailable: {5}, packet: {6} [{0}]\",\n\t\t\t\tnew Object[]{serv, processor.getMethodName(), canHandle, currentAuthenticationProcessor, result,\n\t\t\t\t\t\t\t methodsAvailable, p});\n\t\tif (result) {\n\t\t\tmethodsAvailable.remove(processor);\n\t\t\tserv.getSessionData().put(S2S_METHOD_USED, processor);\n\t\t\tlog.log(Level.FINE, \"Allowing auth for: {1}, remaining: {2} [{0}]\",\n\t\t\t\t\tnew Object[]{serv, processor.getMethodName(), methodsAvailable});\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic void authenticateConnection(String sessionId, CIDConnections cid_conns, CID cidPacket) {\n\t\tS2SConnection s2s_conn = cid_conns.getS2SConnectionForSessionId(sessionId);\n\n\t\tif (s2s_conn != null) {\n\t\t\tauthenticateConnection(s2s_conn.getS2SIOService(), cid_conns, cidPacket);\n\t\t}\n\t}\n\n\tpublic void authenticateConnection(S2SIOService serv, CIDConnections cid_conns, CID cidPacket) {\n\t\tlog.log(Level.FINE, \"Authenticating connection [{0}]\", new Object[]{serv});\n\t\tserv.getSessionData().remove(S2S_METHOD_USED);\n\t\tcid_conns.connectionAuthenticated(serv, cidPacket);\n\n\t\tknownDomainsListProvider.addRemoteDomain(cidPacket.getRemoteHost());\n\t}\n\n\t@Override\n\tpublic void getStatistics(String compName, StatisticsList list) {\n\t\tfinal String keyName = compName + \"/AuthenticationFailures\";\n\t\tfor (Map.Entry<String, AtomicInteger> entry : failedAuthenticationDomains.entrySet()) {\n\t\t\tlist.add(keyName, entry.getKey(), entry.getValue().intValue(), Level.FINER);\n\t\t}\n\t}\n\n\tpublic void authenticationFailed(Packet packet, S2SIOService serv, AuthenticationProcessor processor,\n\t\t\t\t\t\t\t\t\t Queue<Packet> results) {\n\n\t\tmarkConnectionAsFailed(processor.getMethodName(), serv);\n\t\tserv.getSessionData().remove(S2S_METHOD_USED);\n\t\tfinal SortedSet<AuthenticationProcessor> methodsAvailable = getAuthenticationProcessors(serv);\n\t\tmethodsAvailable.remove(processor);\n\t\tlog.log(Level.FINE, \"Authentication failed for: {1}, remaining methodsAvailable: {2} [{0}]\",\n\t\t\t\tnew Object[]{serv, processor.getMethodName(), methodsAvailable});\n\n\t\tif (methodsAvailable.isEmpty()) {\n\t\t\tlog.log(Level.WARNING, \"All authentication methods failed, stopping connection [{0}]\", new Object[]{serv});\n\t\t\tflushRemainingPackets(serv, results);\n\t\t\tserv.forceStop();\n\t\t}\n\n\t\tif (serv.connectionType() == ConnectionType.connect) {\n\t\t\tOptional<AuthenticationProcessor> nextAuthenticationProcessor = methodsAvailable.stream().findFirst();\n\t\t\tif (nextAuthenticationProcessor.isPresent()) {\n\t\t\t\tlog.log(Level.FINE, \"Restarting authentication with: {1} [{0}]\",\n\t\t\t\t\t\tnew Object[]{serv, nextAuthenticationProcessor.get().getMethodName()});\n\t\t\t\tmethodsAvailable.remove(nextAuthenticationProcessor.get());\n\t\t\t\tnextAuthenticationProcessor.get().restartAuth(packet, serv, results);\n\t\t\t} else {\n\t\t\t\tlog.log(Level.WARNING, \"No more authenticators for outgoing connections, stopping [{0}]\",\n\t\t\t\t\t\tnew Object[]{serv});\n\t\t\t\tflushRemainingPackets(serv, results);\n\t\t\t\tserv.forceStop();\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void markConnectionAsFailed(String prefix, S2SIOService serv) {\n\t\tCID cid = (CID) serv.getSessionData().get(S2SConnectionManager.CID_KEY);\n\t\tlog.log(Level.FINEST, () -> \"Adding entry to stats, prefix: \" + prefix + \", cid: \" + cid);\n\t\tfailedAuthenticationDomains.computeIfAbsent(prefix + \"/\" + cid, s -> new AtomicInteger()).incrementAndGet();\n\t}\n\n\tSortedSet<AuthenticationProcessor> getAuthenticationProcessors(S2SIOService serv) {\n\t\tSortedSet<AuthenticationProcessor> mechanisms = (SortedSet<AuthenticationProcessor>) serv.getSessionData()\n\t\t\t\t.get(S2S_METHODS_AVAILABLE);\n\t\tif (mechanisms == null) {\n\t\t\tmechanisms = getAuthenticationProcessors();\n\t\t\tserv.getSessionData().put(S2S_METHODS_AVAILABLE, mechanisms);\n\t\t}\n\t\treturn mechanisms;\n\t}\n\n\tprivate void flushRemainingPackets(S2SIOService serv, Queue<Packet> results) {\n\t\tfor (Packet result : results) {\n\t\t\tserv.addPacketToSend(result);\n\t\t}\n\t\ttry {\n\t\t\tserv.processWaitingPackets();\n\t\t} catch (IOException e) {\n\t\t\tlog.log(Level.WARNING, \"Error while writing packets before closing the stream [\" + serv + \"]\", e);\n\t\t}\n\t}\n\n\tprivate SortedSet<AuthenticationProcessor> getAuthenticationProcessors() {\n\t\tlog.log(Level.FINEST, \"preparing empty processor list!\");\n\t\treturn new ConcurrentSkipListSet<>();\n\t}\n\n\tpublic void setAuthenticationProcessors(List<AuthenticationProcessor> authenticationProcessors) {\n\t\tthis.authenticationProcessors = new CopyOnWriteArrayList<>(authenticationProcessors);\n\t}\n\n\tprivate Optional<AuthenticationProcessor> getCurrentAuthenticationProcessor(S2SIOService serv) {\n\t\treturn Optional.ofNullable((AuthenticationProcessor) serv.getSessionData().get(S2S_METHOD_USED));\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/proc/Dialback.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver.proc;\n\nimport tigase.cert.CertCheckResult;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.net.ConnectionType;\nimport tigase.server.Packet;\nimport tigase.server.xmppserver.*;\nimport tigase.stats.StatisticsList;\nimport tigase.util.Algorithms;\nimport tigase.xml.Element;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.Set;\nimport java.util.concurrent.CopyOnWriteArraySet;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Dec 9, 2010 2:00:52 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\n@Bean(name = \"dialback\", parent = S2SConnectionManager.class, active = true)\npublic class Dialback\n\t\textends AuthenticationProcessor {\n\n\tprivate static final String METHOD_NAME = \"DIALBACK\";\n\tprivate static final Logger log = Logger.getLogger(Dialback.class.getName());\n\tprivate static final Element features_required = new Element(\"dialback\", new Element[]{new Element(\"required\")},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t new String[]{\"urn:xmpp:features:dialback\"});\n\tprivate static final Element features = new Element(\"dialback\", new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew String[]{\"urn:xmpp:features:dialback\"});\n\tprivate static final String REQUESTED_RESULT_DOMAINS_KEY = \"requested-result-domains-key\";\n\t// Ejabberd does not request dialback after TLS (at least some versions don't)\n\t@ConfigField(desc = \"Workaround for TLS dialback issue in Ejabberd\", alias = \"ejabberd-bug-workaround\")\n\tprivate boolean ejabberd_bug_workaround_active = true;\n\n\tpublic Dialback() {\n\t\tsuper();\n\t}\n\n\t@Override\n\tpublic String getMethodName() {\n\t\treturn METHOD_NAME;\n\t}\n\n\t@Override\n\tpublic int order() {\n\t\treturn Order.Dialback.ordinal();\n\t}\n\n\t@Override\n\tpublic boolean process(Packet p, S2SIOService serv, Queue<Packet> results) {\n\t\t// If this is a dialback packet, process it accordingly\n\t\tif (p.getXMLNS() == XMLNS_DB_VAL) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Processing dialback packet: {1} [{0}]\", new Object[]{serv, p});\n\t\t\t}\n\t\t\tprocessDialback(p, serv);\n\n\t\t\treturn true;\n\t\t}\n\n\t\tif (authenticatorSelectorManager.isAllowed(p, serv, this, results)) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Initializing dialback, packet: {1} [{0}]\", new Object[]{serv, p});\n\t\t\t}\n\t\t\tinitDialback(serv, serv.getSessionId());\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void streamFeatures(S2SIOService serv, List<Element> results) {\n\t\tif (!serv.isAuthenticated()) {\n\t\t\tresults.add(features);\n\t\t\tauthenticatorSelectorManager.getAuthenticationProcessors(serv).add(this);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String streamOpened(S2SIOService serv, Map<String, String> attribs) {\n\t\tif (attribs.containsKey(\"version\")) {\n\n\t\t\t// Let's wait for stream features\n\t\t\treturn null;\n\t\t}\n\t\tswitch (serv.connectionType()) {\n\t\t\tcase connect:\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Initializing dialback after stream opened: {1} [{0}]\",\n\t\t\t\t\t\t\tnew Object[]{serv, attribs.get(\"id\")});\n\t\t\t\t}\n\t\t\t\tinitDialback(serv, attribs.get(\"id\"));\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\n\t\t\t\t// Ignore\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void restartAuth(Packet packet, S2SIOService serv, Queue<Packet> results) {\n\t\tinitDialback(serv, serv.getSessionId());\n\t}\n\n\t@Override\n\tpublic boolean canHandle(Packet p, S2SIOService serv, Queue<Packet> results) {\n\t\tCID cid = (CID) serv.getSessionData().get(S2SConnectionManager.CID_KEY);\n\t\tboolean skipTLS = (cid != null) && skipTLSForHost(cid.getRemoteHost());\n\n\t\tif (p.isElement(FEATURES_EL, FEATURES_NS) && p.getElement().getChildren() != null &&\n\t\t\t\t!p.getElement().getChildren().isEmpty()) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Stream features received packet: {1} [{0}]\", new Object[]{serv, p});\n\t\t\t}\n\n\t\t\tCertCheckResult certCheckResult = (CertCheckResult) serv.getSessionData()\n\t\t\t\t\t.get(S2SIOService.CERT_CHECK_RESULT);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"TLS Certificate check: {1}, packet: {2} [{0}]\",\n\t\t\t\t\t\tnew Object[]{serv, certCheckResult, p});\n\t\t\t}\n\n\t\t\t// If TLS is not yet started (announced in stream features) then it is not\n\t\t\t// the right time for dialback yet\n\t\t\t// Some servers send starttls in stream features, even if TLS is already\n\t\t\t// initialized....\n\t\t\tif (p.isXMLNSStaticStr(FEATURES_STARTTLS_PATH, START_TLS_NS) && (certCheckResult == null) && !skipTLS) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Waiting for starttls, packet: {1} [{0}]\", new Object[]{serv, p});\n\t\t\t\t}\n\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// If TLS has been started and it is a trusted peer, we do not need\n\t\t\t// dialback here but... sometimes the remote server may request dialback anyway,\n\t\t\t// especially if they do not trust us.\n\t\t\tif ((certCheckResult == CertCheckResult.trusted) &&\n\t\t\t\t\t!(p.isXMLNSStaticStr(FEATURES_DIALBACK_PATH, DIALBACK_NS))) {\n\t\t\t\tif (ejabberd_bug_workaround_active) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\t\"Ejabberd bug workaround active, proceeding to dialback anyway, packet: {1} [{0}]\",\n\t\t\t\t\t\t\t\tnew Object[]{serv, p});\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"TLS trusted peer, no dialback needed or requested, packet: {1} [{0}]\",\n\t\t\t\t\t\t\t\tnew Object[]{serv, p});\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tfinal CIDConnections cid_conns = handler.getCIDConnections(cid, true);\n\t\t\t\t\t\tauthenticatorSelectorManager.authenticateConnection(serv, cid_conns, cid);\n\t\t\t\t\t} catch (NotLocalhostException ex) {\n\n\t\t\t\t\t\t// Should not happen....\n\t\t\t\t\t\tlog.log(Level.CONFIG, \"Incorrect local hostname, packet: {1} [{0}]\", new Object[]{serv, p});\n\t\t\t\t\t\tauthenticatorSelectorManager.authenticationFailed(p, serv, this, results);\n\t\t\t\t\t} catch (LocalhostException ex) {\n\n\t\t\t\t\t\t// Should not happen....\n\t\t\t\t\t\tlog.log(Level.CONFIG, \"Incorrect remote hostname name, packet: {1} [{0}]\", new Object[]{serv, p});\n\t\t\t\t\t\tauthenticatorSelectorManager.authenticationFailed(p, serv, this, results);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// we need to check if TLS is required\n\t\t\tif (!skipTLS && cid != null && !serv.getSessionData().containsKey(\"TLS\") &&\n\t\t\t\t\thandler.isTlsRequired(cid.getLocalHost())) {\n\t\t\t\tlog.log(Level.FINER, \"TLS is required for domain {1} but STARTTLS was not offered by {2} - policy-violation [{0}]\",\n\t\t\t\t\t\tnew Object[]{serv, cid.getLocalHost(), cid.getRemoteHost()});\n//\t\t\t\tauthenticatorSelectorManager.authenticationFailed(p, serv, this, results);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn true;\n\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void getStatistics(String compName, StatisticsList list) {\n\t\tsuper.getStatistics(compName, list);\n\t\tauthenticatorSelectorManager.getStatistics(compName, list);\n\t}\n\n\t/**\n\t * Checks if result request for received domain was sent by service\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tprotected boolean wasResultRequested(S2SIOService serv, String domain) {\n\t\tSet<String> requested = (Set<String>) serv.getSessionData().get(REQUESTED_RESULT_DOMAINS_KEY);\n\n\t\treturn (requested != null) && requested.contains(domain);\n\t}\n\n\t/**\n\t * Checks if verify request for received domain was sent by service\n\t *\n\t * @see CIDConnections#sendHandshakingOnly\n\t */\n\tprotected boolean wasVerifyRequested(S2SIOService serv, String domain) {\n\t\tString requested = (String) serv.getSessionData().get(S2SIOService.HANDSHAKING_DOMAIN_KEY);\n\n\t\treturn (requested != null) && requested.contains(domain);\n\t}\n\n\tprotected void initDialback(S2SIOService serv, String remote_id) {\n\n\t\ttry {\n\t\t\tif (remote_id == null) {\n\t\t\t\tgenerateStreamError(false, \"bad-request\", serv);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tCID cid = (CID) serv.getSessionData().get(S2SConnectionManager.CID_KEY);\n\t\t\tif (cid == null) {\n\t\t\t\t// can't process such request\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tString secret = handler.getSecretForDomain(cid.getLocalHost());\n\t\t\tString key = Algorithms.generateDialbackKey(cid.getLocalHost(), cid.getRemoteHost(), secret, remote_id);\n\n\t\t\tif (!serv.isHandshakingOnly()) {\n\t\t\t\tElement elem = new Element(DB_RESULT_EL_NAME, key, new String[]{XMLNS_DB_ATT},\n\t\t\t\t\t\t\t\t\t\t   new String[]{XMLNS_DB_VAL});\n\n\t\t\t\taddToResultRequested(serv, cid.getRemoteHost());\n\t\t\t\tserv.getS2SConnection()\n\t\t\t\t\t\t.addControlPacket(Packet.packetInstance(elem, JID.jidInstanceNS(cid.getLocalHost()),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tJID.jidInstanceNS(cid.getRemoteHost())));\n\t\t\t}\n\t\t\tserv.getS2SConnection().sendAllControlPackets();\n\t\t} catch (NotLocalhostException ex) {\n\t\t\tgenerateStreamError(false, \"host-unknown\", serv, ex);\n\t\t}\n\t}\n\n\tprivate void processDialback(Packet p, S2SIOService serv) {\n\n\t\t// Get the cid for which the connection has been created, the cid calculated\n\t\t// from the packet may be different though if the remote server tries to\n\t\t// multiplexing\n\t\tCID cid_main = (CID) serv.getSessionData().get(S2SConnectionManager.CID_KEY);\n\t\tCID cid_packet = new CID(p.getStanzaTo().getDomain(), p.getStanzaFrom().getDomain());\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"DIALBACK packet: {1}, CID_packet: {2} [{0}]\", new Object[]{serv, p, cid_packet});\n\t\t}\n\n\t\tCIDConnections cid_conns = null;\n\n\t\t// Some servers (ejabberd) do not send from/to attributes in the stream:open\n\t\t// which\n\t\t// violates the spec, they seem not to care though, so here we handle the\n\t\t// case.\n\t\tif (cid_main == null) {\n\n\t\t\t// This actually can only happen for 'accept' connection type\n\t\t\t// what we did not get in stream open we can get from here\n\t\t\tcid_main = cid_packet;\n\t\t\tserv.getSessionData().put(S2SConnectionManager.CID_KEY, cid_main);\n\n\t\t\t// For debuging purposes only....\n\t\t\tserv.getSessionData().put(\"local-hostname\", cid_main.getLocalHost());\n\t\t\tserv.getSessionData().put(\"remote-hostname\", cid_main.getRemoteHost());\n\t\t}\n\t\ttry {\n\t\t\tcid_conns = handler.getCIDConnections(cid_main, true);\n\t\t} catch (NotLocalhostException ex) {\n\t\t\tlog.log(Level.FINER, \"Incorrect local hostname: {1} [{0}]\", new Object[]{serv, p});\n\t\t\tgenerateStreamError(false, \"host-unknown\", serv, ex);\n\n\t\t\treturn;\n\t\t} catch (LocalhostException ex) {\n\t\t\tlog.log(Level.FINER, \"Incorrect remote hostname: {1} [{0}]\", new Object[]{serv, p});\n\t\t\tgenerateStreamError(false, \"invalid-from\", serv, ex);\n\n\t\t\treturn;\n\t\t}\n\t\tif (serv.connectionType() == ConnectionType.accept) {\n\t\t\tcid_conns.addIncoming(serv);\n\t\t}\n\n\t\tString remote_key = p.getElemCData();\n\n\t\t// Dummy dialback implementation for now....\n\t\tif ((p.getElemName() == RESULT_EL_NAME) || (p.getElemName() == DB_RESULT_EL_NAME)) {\n\t\t\tif (p.getType() == null) {\n\t\t\t\tCID cid = (CID) serv.getSessionData().get(S2SConnectionManager.CID_KEY);\n\t\t\t\tboolean skipTls = this.skipTLSForHost(cid.getRemoteHost());\n\t\t\t\tif (!skipTls && !serv.getSessionData().containsKey(\"TLS\") &&\n\t\t\t\t\t\thandler.isTlsRequired(cid.getLocalHost())) {\n\t\t\t\t\tlog.log(Level.FINER,\n\t\t\t\t\t\t\t\"Rejecting S2S connection from {1} to {2} due to policy violation - STARTTLS is required [{0}]\",\n\t\t\t\t\t\t\tnew Object[]{serv, cid.getRemoteHost(), cid.getLocalHost()});\n\t\t\t\t\thandler.sendVerifyResult(DB_RESULT_EL_NAME, cid_main, cid_packet, false, null, serv.getSessionId(),\n\t\t\t\t\t\t\t\t\t\t\t null, false, new Element(\"error\", new Element[]{\n\t\t\t\t\t\t\t\t\tnew Element(\"policy-violation\", new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t\tnew String[]{\"urn:ietf:params:xml:ns:xmpp-stanzas\"})},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{\"type\"}, new String[]{\"cancel\"}));\n\t\t\t\t} else {\n\t\t\t\t\tString conn_sessionId = serv.getSessionId();\n\t\t\t\t\thandler.sendVerifyResult(DB_VERIFY_EL_NAME, cid_main, cid_packet, null, conn_sessionId, null,\n\t\t\t\t\t\t\t\t\t\t\t p.getElemCData(), true);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (p.getType() == StanzaType.valid) {\n\t\t\t\t\tif (wasResultRequested(serv, p.getStanzaFrom().toString())) {\n\t\t\t\t\t\tauthenticatorSelectorManager.authenticateConnection(serv, cid_conns, cid_packet);\n\n\t\t\t\t\t\t// As per specification:\n\t\t\t\t\t\t// If the value of the 'type' attribute is \"valid\", then the connection between the domain pair\n\t\t\t\t\t\t// is considered verified and the Initiating Server can send any outbound stanzas\n\t\t\t\t\t\t// it has queued up for routing to the Receiving Server for the domain pair\n\t\t\t\t\t\tcid_conns.streamNegotiationCompleted(serv);\n\t\t\t\t\t\tserv.streamNegotiationCompleted();\n\t\t\t\t\t} else if (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\tlog.log(Level.FINE, \"Received result with type valid for {0} but it was not requested!\",\n\t\t\t\t\t\t\t\tp.getStanzaFrom());\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\tlog.log(Level.FINE, \"Invalid result for DB authentication: {0}, stopping connection: {1}\",\n\t\t\t\t\t\t\t\tnew Object[]{cid_packet, serv});\n\t\t\t\t\t}\n\t\t\t\t\tauthenticatorSelectorManager.markConnectionAsFailed(getMethodName(), serv);\n\t\t\t\t\tserv.stop();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif ((p.getElemName() == VERIFY_EL_NAME) || (p.getElemName() == DB_VERIFY_EL_NAME)) {\n\t\t\tif (p.getType() == null) {\n\t\t\t\tboolean result;\n\t\t\t\ttry {\n\t\t\t\t\tString secret = handler.getSecretForDomain(cid_packet.getLocalHost());\n\t\t\t\t\tString local_key = Algorithms.generateDialbackKey(cid_packet.getLocalHost(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  cid_packet.getRemoteHost(), secret,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  p.getStanzaId());\n\n\t\t\t\t\tif (local_key == null) {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\t\tlog.log(Level.FINER,\n\t\t\t\t\t\t\t\t\t\"The key is not available for connection CID: {0}, \" + \"or the packet CID: {1} \",\n\t\t\t\t\t\t\t\t\tnew Object[]{cid_main, cid_packet});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tresult = local_key != null && local_key.equals(remote_key);\n\t\t\t\t} catch (NotLocalhostException ex) {\n\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\tlog.log(Level.FINER, \"Could not retreive secret for \" + cid_packet.getLocalHost(), ex);\n\t\t\t\t\t}\n\t\t\t\t\tresult = false;\n\t\t\t\t}\n\t\t\t\thandler.sendVerifyResult(DB_VERIFY_EL_NAME, cid_main, cid_packet, result, p.getStanzaId(),\n\t\t\t\t\t\t\t\t\t\t serv.getSessionId(), null, false);\n\t\t\t} else {\n\t\t\t\tif (wasVerifyRequested(serv, p.getStanzaFrom().toString())) {\n\t\t\t\t\thandler.sendVerifyResult(DB_RESULT_EL_NAME, cid_main, cid_packet, (p.getType() == StanzaType.valid),\n\t\t\t\t\t\t\t\t\t\t\t null, p.getStanzaId(), null, false);\n\t\t\t\t\tif (p.getType() == StanzaType.valid) {\n\t\t\t\t\t\tauthenticatorSelectorManager.authenticateConnection(p.getStanzaId(), cid_conns, cid_packet);\n\n\t\t\t\t\t\t// As per specification:\n\t\t\t\t\t\t// If the value of the 'type' attribute is \"valid\", then the connection between the domain pair\n\t\t\t\t\t\t// is considered verified and the Initiating Server can send any outbound stanzas\n\t\t\t\t\t\t// it has queued up for routing to the Receiving Server for the domain pair\n\t\t\t\t\t\tcid_conns.streamNegotiationCompleted(serv);\n\t\t\t\t\t\tserv.streamNegotiationCompleted();\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\tlog.log(Level.FINE, \"Received verify for {0} but it was not requested!\", p.getStanzaFrom());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (serv.isHandshakingOnly()) {\n\t\t\t\t\tserv.stop();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Adds domain to list of domains requested for result by service\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tprivate void addToResultRequested(S2SIOService serv, String domain) {\n\t\tSet<String> requested = (Set<String>) serv.getSessionData().get(REQUESTED_RESULT_DOMAINS_KEY);\n\n\t\tif (requested == null) {\n\t\t\tSet<String> requested_tmp = new CopyOnWriteArraySet<String>();\n\n\t\t\trequested = (Set<String>) serv.getSessionData().putIfAbsent(REQUESTED_RESULT_DOMAINS_KEY, requested_tmp);\n\t\t\tif (requested == null) {\n\t\t\t\trequested = requested_tmp;\n\t\t\t}\n\t\t}\n\t\trequested.add(domain);\n\t}\n\n\t@Override\n\tpublic boolean shouldSkipUndelivered(Packet packet) {\n\t\treturn packet.getElemName() == DB_VERIFY_EL_NAME\n\t\t\t\t|| packet.getElemName() == DB_RESULT_EL_NAME;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/proc/PacketChecker.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver.proc;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Packet;\nimport tigase.server.xmppserver.CID;\nimport tigase.server.xmppserver.S2SConnectionHandlerIfc;\nimport tigase.server.xmppserver.S2SConnectionManager;\nimport tigase.server.xmppserver.S2SIOService;\nimport tigase.util.dns.DNSEntry;\nimport tigase.util.dns.DNSResolverFactory;\n\nimport java.net.UnknownHostException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.server.xmppserver.S2SConnectionManager.XMLNS_SERVER_VAL;\n\n/**\n * Created: Dec 10, 2010 5:53:57 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\n@Bean(name = \"packetChecker\", parent = S2SConnectionManager.class, active = true)\npublic class PacketChecker\n\t\textends S2SAbstractFilter {\n\n\tprivate static final Logger log = Logger.getLogger(PacketChecker.class.getName());\n\n\tprivate static final String ALLOW_PACKETS_FROM_OTHER_DOMAINS_MAP_KEY = \"allow-packets-from-other-domains-map\";\n\tprivate static final String ALLOW_PACKETS_FROM_OTHER_DOMAINS_WITH_SAME_IP_KEY = \"allow-packets-from-other-domains-with-same-ip\";\n\tprivate static final String ALLOW_PACKETS_FROM_OTHER_DOMAINS_WITH_SAME_IP_WHITELIST_KEY = \"allow-packets-from-other-domains-with-same-ip-whitelist\";\n\t@ConfigField(desc = \"Allow packets from other domains with same IP\")\n\tprivate boolean allowOtherDomainsWithSameIp = false;\n\t@ConfigField(desc = \"Allow packets from other domains on connections from domain\")\n\tprivate Map<String, String[]> allowedOtherDomainsMap = new ConcurrentHashMap<String, String[]>();\n\t@ConfigField(desc = \"Whitelist to allow packets from other domains with same IP\")\n\tprivate String[] allowedOtherDomainsWithSameIpWhitelist = null;\n\n\t@Override\n\tpublic void init(S2SConnectionHandlerIfc<S2SIOService> handler, Map<String, Object> props) {\n\t\tsuper.init(handler, props);\n\n\t\tString allowOtherDomainsStr = (String) props.get(ALLOW_PACKETS_FROM_OTHER_DOMAINS_WITH_SAME_IP_KEY);\n\t\tif (allowOtherDomainsStr != null) {\n\t\t\tallowOtherDomainsWithSameIp = Boolean.parseBoolean(allowOtherDomainsStr);\n\t\t}\n\n\t\tString allowedOtherDomainsWhitelistStr = (String) props.get(\n\t\t\t\tALLOW_PACKETS_FROM_OTHER_DOMAINS_WITH_SAME_IP_WHITELIST_KEY);\n\t\tif (allowedOtherDomainsWhitelistStr != null) {\n\t\t\tallowedOtherDomainsWithSameIpWhitelist = allowedOtherDomainsWhitelistStr.split(\",\");\n\t\t\tArrays.sort(allowedOtherDomainsWithSameIpWhitelist);\n\t\t} else {\n\t\t\tallowedOtherDomainsWithSameIpWhitelist = new String[0];\n\t\t}\n\n\t\tString allowedOtherDomainsMapStr = (String) props.get(ALLOW_PACKETS_FROM_OTHER_DOMAINS_MAP_KEY);\n\t\tallowedOtherDomainsMap.clear();\n\t\tif (allowedOtherDomainsMapStr != null) {\n\t\t\tfor (String listOfDomainsStr : allowedOtherDomainsMapStr.split(\",\")) {\n\t\t\t\tString[] listOfDomains = listOfDomainsStr.split(\":\");\n\t\t\t\tArrays.sort(listOfDomains);\n\t\t\t\tfor (int i = 0; i < listOfDomains.length; i++) {\n\t\t\t\t\tallowedOtherDomainsMap.put(listOfDomains[i], listOfDomains);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean filter(Packet p, S2SIOService serv, Queue<Packet> results) {\n\t\tif ((p.getXMLNS() == XMLNS_SERVER_VAL) || (p.getXMLNS() == XMLNS_CLIENT_VAL)) {\n\t\t\tif ((p.getStanzaFrom() == null) || (p.getStanzaFrom().getDomain().trim().isEmpty()) ||\n\t\t\t\t\t(p.getStanzaTo() == null) || p.getStanzaTo().getDomain().trim().isEmpty()) {\n\t\t\t\tgenerateStreamError(false, \"improper-addressing\", serv);\n\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tCID cid = new CID(p.getStanzaTo().getDomain(), p.getStanzaFrom().getDomain());\n\n\t\t\t// String remote_hostname = (String) serv.getSessionData().get(\"remote-hostname\");\n\t\t\tif (!isAllowed(p, serv, cid)) {\n\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\tlog.log(Level.FINER, \"{0}, Invalid hostname from the remote server for packet: \" +\n\t\t\t\t\t\t\t\t\t\"{1}, authenticated domains for this connection: {2}\",\n\t\t\t\t\t\t\tnew Object[]{serv, p, serv.getCIDs()});\n\t\t\t\t}\n\n\t\t\t\tgenerateStreamError(false, \"invalid-from\", serv);\n\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} else {\n//\t\t\t// if there is a stream features and we are authenticated we should ignore it..\n//\t\t\t// this case should be handled by StreamFeature plugin now\n\t\t\tif (p.isElement(FEATURES_EL, FEATURES_NS) && serv.isAuthenticated()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.log(Level.FINER, \"{0}, Invalid namespace for packet: {1}\", new Object[]{serv, p});\n\t\t\t}\n\n\t\t\tgenerateStreamError(false, \"invalid-namespace\", serv);\n\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Check if incoming packet is allowed on this connection\n\t */\n\tprotected boolean isAllowed(Packet p, S2SIOService serv, CID cid) {\n\t\tboolean allowed = serv.isAuthenticated(cid);\n\n\t\t// Some servers (e.g. Google) are sending packets from other domains than authenticated domain\n\t\t// we should check it if we need to connect to that incompatible domain\n\t\tif (!allowed && serv.isAuthenticated()) {\n\t\t\tString domain = p.getStanzaFrom().getDomain();\n\n\t\t\t// here we use mapping as DNS solution will not work in all cases\n\t\t\tString[] allowedOtherDomainsMapValue = allowedOtherDomainsMap.get(domain);\n\t\t\tif (allowedOtherDomainsMapValue != null) {\n\t\t\t\tArrayList<CID> authenticatedCids = new ArrayList<CID>(serv.getCIDs());\n\t\t\t\tfor (CID acid : authenticatedCids) {\n\t\t\t\t\tif (Arrays.binarySearch(allowedOtherDomainsMapValue, acid.getRemoteHost()) >= 0) {\n\t\t\t\t\t\tserv.addCID(cid);\n\t\t\t\t\t\tallowed = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!allowed && allowOtherDomainsWithSameIp) {\n\t\t\t\t// we can enable it for all domains or only for whitelisted domains\n\t\t\t\tif (allowedOtherDomainsWithSameIpWhitelist.length == 0 ||\n\t\t\t\t\t\tArrays.binarySearch(allowedOtherDomainsWithSameIpWhitelist, domain) >= 0) {\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tDNSEntry[] entries = DNSResolverFactory.getInstance().getHostSRV_Entries(domain);\n\t\t\t\t\t\tif (entries != null) {\n\t\t\t\t\t\t\tString remoteAddress = serv.getRemoteAddress();\n\t\t\t\t\t\t\tfor (DNSEntry entry : entries) {\n\t\t\t\t\t\t\t\tif (remoteAddress.equals(entry.getIp())) {\n\t\t\t\t\t\t\t\t\tserv.addCID(cid);\n\t\t\t\t\t\t\t\t\tallowed = true;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (UnknownHostException ex) {\n\t\t\t\t\t\tlog.log(Level.FINE, \"Unknown host for domain: {0}\", domain);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn allowed;\n\t}\n\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/proc/S2SAbstract.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver.proc;\n\nimport tigase.kernel.beans.Inject;\nimport tigase.server.xmppserver.S2SConnectionHandlerIfc;\nimport tigase.server.xmppserver.S2SConnectionManager;\nimport tigase.server.xmppserver.S2SIOService;\n\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created by andrzej on 19.05.2017.\n */\npublic class S2SAbstract {\n\n\tprotected static final String DB_RESULT_EL_NAME = \"db:result\";\n\n\tprotected static final String DB_VERIFY_EL_NAME = \"db:verify\";\n\n\tprotected static final String DIALBACK_EL = \"dialback\";\n\n\tprotected static final String DIALBACK_NS = \"urn:xmpp:features:dialback\";\n\n\tprotected static final String FEATURES_EL = \"features\";\n\n\tprotected static final String FEATURES_NS = \"http://etherx.jabber.org/streams\";\n\n\tprotected static final String PROCEED_TLS_EL = \"proceed\";\n\n\tprotected static final String RESULT_EL_NAME = \"result\";\n\n\tprotected static final String START_TLS_EL = \"starttls\";\n\n\tprotected static final String START_TLS_NS = \"urn:ietf:params:xml:ns:xmpp-tls\";\n\n\tpublic static final String STREAM_FEATURES_EL = \"stream:features\";\n\n\tprotected static final String VERIFY_EL_NAME = \"verify\";\n\n\tprotected static final String VERSION_ATT_NAME = \"version\";\n\n\tprotected static final String XMLNS_CLIENT_VAL = \"jabber:client\";\n\n\tprotected static final String XMLNS_DB_ATT = \"xmlns:db\";\n\n\tprotected static final String XMLNS_DB_VAL = \"jabber:server:dialback\";\n\n\tprotected static final String[] FEATURES_STARTTLS_PATH = {FEATURES_EL, START_TLS_EL};\n\n\tprotected static final String[] FEATURES_DIALBACK_PATH = {FEATURES_EL, DIALBACK_EL};\n\tprivate static final Logger log = Logger.getLogger(S2SAbstract.class.getName());\n\tpublic static boolean FORCE_VERSION = false;\n\n\t@Inject(nullAllowed = true)\n\tprotected S2SConnectionHandlerIfc<S2SIOService> handler = null;\n\n\tpublic void init(S2SConnectionHandlerIfc<S2SIOService> handler, Map<String, Object> props) {\n\t\tthis.handler = handler;\n\t}\n\n\tpublic void generateStreamError(boolean initStream, String error_el, S2SIOService serv) {\n\t\tThrowable thr = new Throwable();\n\t\tthr.fillInStackTrace();\n\t\tgenerateStreamError(initStream,error_el,serv,thr);\n\t}\n\n\tpublic void generateStreamError(boolean initStream, String error_el, S2SIOService serv, Throwable throwable) {\n\t\tfinal StringBuilder strError = new StringBuilder();\n\n\t\tif (initStream) {\n\t\t\tstrError.append(\"<?xml version='1.0'?><stream:stream\" + \" xmlns='\" + S2SConnectionManager.XMLNS_SERVER_VAL + \"'\" +\n\t\t\t\t\t\t\t\" xmlns:stream='http://etherx.jabber.org/streams'\" + \" id='tigase-server-error'\" +\n\t\t\t\t\t\t\t\" from='\" + handler.getDefHostName() + \"'\" + \" xml:lang='en'>\");\n\t\t}\n\t\tstrError.append(\"<stream:error>\" + \"<\" + error_el + \" xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>\" +\n\t\t\t\t\"</stream:error>\" + \"</stream:stream>\");\n\t\ttry {\n\t\t\tlog.log(Level.FINEST, throwable, () -> String.format(\"Sending stream error: %1$s: %2$s [%3$s]\", error_el, strError.toString(), serv));\n\t\t\thandler.writeRawData(serv, strError.toString());\n\t\t\tserv.stop();\n\t\t} catch (Exception e) {\n\t\t\tserv.forceStop();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/proc/S2SAbstractFilter.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver.proc;\n\nimport tigase.server.xmppserver.S2SFilterIfc;\n\n/**\n * Created by andrzej on 19.05.2017.\n */\npublic abstract class S2SAbstractFilter\n\t\textends S2SAbstract\n\t\timplements S2SFilterIfc {\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/proc/S2SAbstractProcessor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver.proc;\n\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Packet;\nimport tigase.server.xmppserver.S2SIOService;\nimport tigase.server.xmppserver.S2SProcessor;\nimport tigase.stats.StatisticsList;\nimport tigase.xml.Element;\n\nimport java.util.*;\nimport java.util.logging.Logger;\n\n/**\n * Created: Dec 10, 2010 3:32:11 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\npublic abstract class S2SAbstractProcessor\n\t\textends S2SAbstract\n\t\timplements S2SProcessor {\n\n\tprotected static final Comparator<S2SProcessor> processorsComparator = Comparator.comparingInt(S2SProcessor::order);\n\n\tprivate static final Logger log = Logger.getLogger(S2SAbstractProcessor.class.getName());\n\t@ConfigField(desc = \"Skip StartTLS for domains\", alias = \"skip-tls-hostnames\")\n\tprivate String[] skipTlsHosts;\n\n\tpublic void setSkipTlsHosts(String[] skipTlsHosts) {\n\t\tthis.skipTlsHosts = skipTlsHosts != null\n\t\t\t\t\t\t\t? Arrays.stream(skipTlsHosts).map(String::toLowerCase).toArray(String[]::new)\n\t\t\t\t\t\t\t: null;\n\t}\n\n\t@Override\n\tpublic boolean process(Packet p, S2SIOService serv, Queue<Packet> results) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void serviceStarted(S2SIOService serv) {\n\t}\n\n\t@Override\n\tpublic void serviceStopped(S2SIOService serv) {\n\t}\n\n\tpublic boolean skipTLSForHost(String hostname) {\n\t\t// Workaround for buggy servers having problems with establishing TLS over s2s\n\t\t// http://community.igniterealtime.org/thread/36206\n\t\t// http://community.igniterealtime.org/thread/30578\n\t\treturn skipTlsHosts != null && (Arrays.binarySearch(skipTlsHosts, hostname.toLowerCase()) >= 0);\n\t}\n\n\t@Override\n\tpublic void streamClosed(S2SIOService serv) {\n\t}\n\n\t@Override\n\tpublic void streamFeatures(S2SIOService serv, List<Element> results) {\n\t}\n\n\t@Override\n\tpublic String streamOpened(S2SIOService serv, Map<String, String> attribs) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic int compareTo(S2SProcessor proc) {\n\t\treturn processorsComparator.compare(this, proc);\n\t}\n\n\t@Override\n\tpublic void getStatistics(String compName, StatisticsList list) {\n\n\t}\n\n\t// Order of enum values is important as it is an order in which packet\n\t// is processed by processors\n\tprotected enum Order {\n\t\t// 0\n\t\tStreamOpen,\n\t\t// 1\n\t\tStreamError,\n\t\t// 2\n\t\tStreamFeatures,\n\t\t// 3\n\t\tStartTLS,\n\t\t// 4\n\t\tSaslExternal,\n\t\t// 5\n\t\tDialback,\n\t\t// 6\n\t\tStartZlib,\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/proc/SaslExternal.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver.proc;\n\nimport tigase.cert.CertCheckResult;\nimport tigase.cert.CertificateUtil;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Packet;\nimport tigase.server.xmppserver.*;\nimport tigase.stats.StatisticsList;\nimport tigase.util.Base64;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.cert.CertificateParsingException;\nimport java.security.cert.X509Certificate;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Queue;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Bean(name = \"sasl-external\", parent = S2SConnectionManager.class, active = true)\npublic class SaslExternal\n\t\textends AuthenticationProcessor {\n\n\tprotected static final String[] FEATURES_SASL_PATH = {FEATURES_EL, \"mechanisms\"};\n\tprivate static final String METHOD_NAME = \"SASL-EXTERNAL\";\n\tprivate static final String SASL_SUCCESS_ELEMENT_NAME = \"success\";\n\tprivate static final String SASL_FAILURE_ELEMENT_NAME = \"failure\";\n\tprivate final static String XMLNS_SASL = \"urn:ietf:params:xml:ns:xmpp-sasl\";\n\tprivate static final Logger log = Logger.getLogger(SaslExternal.class.getName());\n\tprivate static Element successElement = new Element(SASL_SUCCESS_ELEMENT_NAME, new String[]{\"xmlns\"}, new String[]{XMLNS_SASL});\n\t@ConfigField(desc = \"Enable compatibility with legacy servers\", alias = \"legacy-compat\")\n\tprivate boolean legacyCompat = true;\n\t@ConfigField(desc = \"Skip SASL-EXTERNAL for defined domains\", alias = \"skip-for-domains\")\n\tprivate String[] skipForDomains;\n\n\tprivate static boolean isAnyMechanismsPresent(Packet p) {\n\t\tfinal List<Element> childrenStaticStr = p.getElement().getChildrenStaticStr(FEATURES_SASL_PATH);\n\t\treturn p.isXMLNSStaticStr(FEATURES_SASL_PATH, XMLNS_SASL) && childrenStaticStr != null &&\n\t\t\t\t!childrenStaticStr.isEmpty();\n\t}\n\n\tprivate static boolean isTlsEstablished(final CertCheckResult certCheckResult) {\n\t\treturn (certCheckResult == CertCheckResult.trusted || certCheckResult == CertCheckResult.untrusted ||\n\t\t\t\tcertCheckResult == CertCheckResult.self_signed);\n\t}\n\n\t@Override\n\tpublic String getMethodName() {\n\t\treturn METHOD_NAME;\n\t}\n\n\tpublic void setSkipForDomains(String[] skipForDomains) {\n\t\tthis.skipForDomains = skipForDomains != null\n\t\t\t\t\t\t\t  ? Arrays.stream(skipForDomains).map(String::toLowerCase).toArray(String[]::new)\n\t\t\t\t\t\t\t  : null;\n\t}\n\n\t@Override\n\tpublic void streamFeatures(S2SIOService serv, List<Element> results) {\n\t\tElement mechanisms = new Element(\"mechanisms\", new Element[]{new Element(\"mechanism\", \"EXTERNAL\")},\n\t\t\t\t\t\t\t\t\t\t new String[]{\"xmlns\"}, new String[]{XMLNS_SASL});\n\n\t\tfinal boolean canAddSaslToFeatures = canAddSaslToFeatures(serv);\n\n\t\tif (canAddSaslToFeatures) {\n\t\t\tresults.add(mechanisms);\n\t\t\tauthenticatorSelectorManager.getAuthenticationProcessors(serv).add(this);\n\t\t}\n\t}\n\n\tpublic int order() {\n\t\treturn Order.SaslExternal.ordinal();\n\t}\n\n\t@Override\n\tpublic void restartAuth(Packet p, S2SIOService serv, Queue<Packet> results) {\n\t\ttry {\n\t\t\tsendAuthRequest(serv, results);\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, e, () -> String.format(\"%s, Error while restarting authentication\", serv));\n\t\t\tresults.add(failurePacket(null));\n\t\t\tauthenticatorSelectorManager.authenticationFailed(p, serv, this, results);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean canHandle(Packet p, S2SIOService serv, Queue<Packet> results) {\n\t\tfinal CID cid = (CID) serv.getSessionData().get(S2SConnectionManager.CID_KEY);\n\t\tfinal boolean skipTLS = (cid != null) && skipTLSForHost(cid.getRemoteHost());\n\n\t\tif (cid != null && (isSkippedDomain(cid.getLocalHost()) || isSkippedDomain(cid.getRemoteHost()))) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Skipping SASL-EXTERNAL for domain: {1} because it was ignored [{0}]\",\n\t\t\t\t\t\tnew Object[]{serv, cid});\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tif (p.isElement(FEATURES_EL, FEATURES_NS) && p.getElement().getChildren() != null &&\n\t\t\t\t!p.getElement().getChildren().isEmpty()) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Stream features received packet: {1} [{0}]\", new Object[]{serv, p});\n\t\t\t}\n\n\t\t\t// Some servers send empty SASL list!\n\t\t\tif (!isAnyMechanismsPresent(p)) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"No SASL mechanisms found in features. Skipping SASL. [{0}]\",\n\t\t\t\t\t\t\tnew Object[]{serv, p});\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tCertCheckResult certCheckResult = (CertCheckResult) serv.getSessionData()\n\t\t\t\t\t.get(S2SIOService.CERT_CHECK_RESULT);\n\n\t\t\tif (p.isXMLNSStaticStr(FEATURES_STARTTLS_PATH, START_TLS_NS) && (certCheckResult == null) && !skipTLS) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Waiting for starttls, packet: {1} [{0}]\", new Object[]{serv, p});\n\t\t\t\t}\n\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// it is reasonable to skip SASL EXTERNAL for handshaking only connections\n\t\t\tif (certCheckResult == CertCheckResult.invalid || serv.isHandshakingOnly()) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Connection is handshaking only: {1}, certCheckResult: {2}, packet: {3} [{0}]\",\n\t\t\t\t\t\t\tnew Object[]{serv, serv.isHandshakingOnly(), certCheckResult, p});\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (!canAddSaslToFeatures(serv)) {\n\t\t\t\t// if we have not trusted SSL certificate (local), then there is no point in trying SASL EXTERNAL\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Skipping SASL-EXTERNAL: local certificate for {1} is not trusted (self-signed or expired)  [{0}]\",\n\t\t\t\t\t\t\tnew Object[]{serv, cid.getLocalHost()});\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn true;\n\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void getStatistics(String compName, StatisticsList list) {\n\t\tsuper.getStatistics(compName, list);\n\t\tauthenticatorSelectorManager.getStatistics(compName, list);\n\t}\n\n\t@Override\n\tpublic boolean process(Packet p, S2SIOService serv, Queue<Packet> results) {\n\t\ttry {\n\t\t\tif (authenticatorSelectorManager.isAllowed(p, serv, this, results)) {\n\t\t\t\tsendAuthRequest(serv, results);\n\t\t\t\treturn true;\n\t\t\t} else if (p.isElement(\"auth\", XMLNS_SASL)) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Received auth request: {1} [{0}]\", new Object[]{serv, p});\n\t\t\t\t}\n\t\t\t\tprocessAuth(p, serv, results);\n\t\t\t\treturn true;\n\t\t\t} else if (p.isElement(SASL_SUCCESS_ELEMENT_NAME, XMLNS_SASL)) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Received success response: {1} [{0}]\", new Object[]{serv, p});\n\t\t\t\t}\n\t\t\t\tprocessSuccess(p, serv, results);\n\t\t\t\treturn true;\n\t\t\t} else if (p.isElement(SASL_FAILURE_ELEMENT_NAME, XMLNS_SASL)) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Received failure response: {1} [{0}]\", new Object[]{serv, p});\n\t\t\t\t}\n\n\t\t\t\tauthenticatorSelectorManager.authenticationFailed(p, serv, this, results);\n\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, e, () -> String.format(\"%s, Error while processing packet: %s\", serv, p));\n\t\t\tresults.add(failurePacket(null));\n\t\t\tauthenticatorSelectorManager.authenticationFailed(p, serv, this, results);\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate boolean isSkippedDomain(String domain) {\n\t\treturn domain != null && skipForDomains != null &&\n\t\t\t\tArrays.binarySearch(skipForDomains, domain.toLowerCase()) >= 0;\n\t}\n\n\t/**\n\t * \"Server2 advertises SASL mechanisms. If the 'from' attribute of the stream header sent by Server1 can be\n\t * matched against one of the identifiers provided in the certificate following the matching rules from\n\t * RFC 6125, Server2 SHOULD advertise the SASL EXTERNAL mechanism. If no match is found, Server2 MAY either\n\t * close Server1's TCP connection or continue with a Server Dialback (XEP-0220) [8] negotiation.\"\n\t * If there was no `from` in the incomming stream then we should not advertise SASL-EXTERNAL and let\n\t * other party possibly continue with Diallback\n\t *\n\t * @param serv for which to determine if SASL-EXTERNAL can be added to features\n\t *\n\t * @return {@code true} if TLS is established, local certificate is valid and domains have not been excluded\n\t */\n\tprivate boolean canAddSaslToFeatures(S2SIOService serv) {\n\t\tfinal ConcurrentMap<String, Object> sessionData = serv.getSessionData();\n\t\tCID cid = (CID) sessionData.get(S2SConnectionManager.CID_KEY);\n\t\tboolean skipDomain =\n\t\t\t\tcid != null && (isSkippedDomain(cid.getLocalHost()) || isSkippedDomain(cid.getRemoteHost()));\n\t\tCertCheckResult certCheckResult = (CertCheckResult) sessionData.get(S2SIOService.CERT_CHECK_RESULT);\n\t\tboolean tlsEstablished = isTlsEstablished(certCheckResult);\n\t\tCertCheckResult localCertCheckResult = (CertCheckResult) sessionData.get(S2SIOService.LOCAL_CERT_CHECK_RESULT);\n\t\tboolean localCertTrusted = localCertCheckResult == CertCheckResult.trusted;\n\t\tboolean canAddSaslToFeatures =\n\t\t\t\ttlsEstablished && localCertTrusted && !serv.isAuthenticated() && !serv.isHandshakingOnly() &&\n\t\t\t\t\t\t!skipDomain;\n\n\t\tif (!canAddSaslToFeatures && log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\"Not adding SASL-EXTERNAL feature, tlsEstablished: {1} (result: {2}), skipDomain: {3}, localCertTrusted: {4} (result: {5}) [{0}]\",\n\t\t\t\t\tnew Object[]{serv, tlsEstablished, certCheckResult, skipDomain, localCertTrusted,\n\t\t\t\t\t\t\t\t localCertCheckResult});\n\t\t}\n\t\treturn canAddSaslToFeatures;\n\t}\n\n\tprivate void sendAuthRequest(S2SIOService serv, Queue<Packet> results) throws TigaseStringprepException {\n\t\tString cdata = \"=\";\n\t\tCID cid = (CID) serv.getSessionData().get(S2SConnectionManager.CID_KEY);\n\t\tif (cid != null && legacyCompat) {\n\t\t\tcdata = Base64.encode(cid.getLocalHost().getBytes(StandardCharsets.UTF_8));\n\t\t}\n\t\tElement auth = new Element(\"auth\", cdata, new String[]{\"xmlns\", \"mechanism\"},\n\t\t\t\t\t\t\t\t   new String[]{XMLNS_SASL, \"EXTERNAL\"});\n\t\tresults.add(Packet.packetInstance(auth));\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Starting SASL EXTERNAL: {1} [{0}]\", new Object[]{serv, auth});\n\t\t}\n\t}\n\n\tprivate void processSuccess(Packet p, S2SIOService serv, Queue<Packet> results)\n\t\t\tthrows TigaseStringprepException, LocalhostException, NotLocalhostException {\n\t\tfinal CID cid = (CID) serv.getSessionData().get(S2SConnectionManager.CID_KEY);\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Sending new stream [{0}]\", new Object[]{serv});\n\t\t}\n\t\t// old ejabberd has problem if we first send `xmlns` and then `xmlns:stream` so we have to do it reversed...\n\t\tString data = \"<stream:stream\" + \" xmlns:stream='http://etherx.jabber.org/streams'\" + \" xmlns='jabber:server'\" +\n\t\t\t\t\" from='\" + cid.getLocalHost() + \"'\" + \" to='\" + cid.getRemoteHost() + \"'\" + \" version='1.0'>\";\n\n\t\tserv.xmppStreamOpen(data);\n\n\t\tCIDConnections cid_conns = handler.getCIDConnections(cid, true);\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Making connection authenticated. cid={1}  [{0}]\", new Object[]{serv, cid});\n\t\t}\n\t\tauthenticatorSelectorManager.authenticateConnection(serv, cid_conns, cid);\n\t}\n\n\tprivate void processAuth(Packet p, S2SIOService serv, Queue<Packet> results)\n\t\t\tthrows TigaseStringprepException, LocalhostException, NotLocalhostException {\n\t\tfinal X509Certificate peerCertificate = (X509Certificate) serv.getPeerCertificate();\n\n\t\tif (peerCertificate == null) {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"No peer certificate! [{0}]\", new Object[]{serv});\n\t\t\t}\n\t\t\tresults.add(failurePacket(\"No peer certificate\"));\n\t\t\tauthenticatorSelectorManager.authenticationFailed(p, serv, this, results);\n\t\t\treturn;\n\t\t}\n\n\t\tCertCheckResult certCheckResult = (CertCheckResult) serv.getSessionData().get(S2SIOService.CERT_CHECK_RESULT);\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Trust: {1} for peer certificate: {2}, AltNames: {3} [{0}]\",\n\t\t\t\t\tnew Object[]{serv, certCheckResult, peerCertificate.getSubjectDN(),\n\t\t\t\t\t\t\t\t CertificateUtil.getCertAltCName(peerCertificate)});\n\t\t}\n\n\t\tif (certCheckResult != CertCheckResult.trusted && certCheckResult != CertCheckResult.self_signed) {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"Certificate is not trusted [{0}]\", new Object[]{serv});\n\t\t\t}\n\t\t\tresults.add(failurePacket(\"Certificate is not trusted\"));\n\t\t\tauthenticatorSelectorManager.authenticationFailed(p, serv, this, results);\n\t\t\treturn;\n\t\t}\n\n\t\tfinal CID cid = (CID) serv.getSessionData().get(S2SConnectionManager.CID_KEY);\n\t\tif (cid == null) {\n\t\t\t// can't process such request\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"CID is unknown, can''t proceed [{0}]\", new Object[]{serv});\n\t\t\t}\n\t\t\tresults.add(failurePacket(\"Unknown origin hostname (lack of `from` element)\"));\n\t\t\tauthenticatorSelectorManager.authenticationFailed(p, serv, this, results);\n\t\t\treturn;\n\t\t}\n\n\t\tboolean nameValid;\n\t\ttry {\n\t\t\tnameValid = CertificateUtil.verifyCertificateForDomain(peerCertificate, cid.getRemoteHost());\n\t\t} catch (CertificateParsingException e) {\n\t\t\tnameValid = false;\n\t\t}\n\n\t\tif (!nameValid) {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"Certificate name doesn't match to '{1}' [{0}]\",\n\t\t\t\t\t\tnew Object[]{serv, cid.getRemoteHost()});\n\t\t\t}\n\t\t\tresults.add(failurePacket(\"Certificate name doesn't match to domain name\"));\n\t\t\tauthenticatorSelectorManager.authenticationFailed(p, serv, this, results);\n\t\t\treturn;\n\t\t}\n\n\t\tCIDConnections cid_conns = handler.getCIDConnections(cid, true);\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Making connection authenticated. cid={1} [{0}]\", new Object[]{serv, cid});\n\t\t}\n\t\tauthenticatorSelectorManager.authenticateConnection(serv, cid_conns, cid);\n\n\t\tresults.add(Packet.packetInstance(successElement));\n\t}\n\n\tprivate Packet failurePacket(String description) {\n\t\tElement result = new Element(SASL_FAILURE_ELEMENT_NAME, new Element[]{new Element(\"invalid-authzid\")}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t new String[]{XMLNS_SASL});\n\n\t\tif (description != null) {\n\t\t\tresult.addChild(new Element(\"text\", description));\n\t\t}\n\n\t\treturn Packet.packetInstance(result, null, null);\n\t}\n\n\t@Override\n\tpublic boolean shouldSkipUndelivered(Packet packet) {\n\t\treturn packet.getElemName() == SASL_SUCCESS_ELEMENT_NAME\n\t\t\t\t|| packet.getXMLNS() == XMLNS_SASL;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/proc/StartTLS.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver.proc;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Packet;\nimport tigase.server.xmppserver.CID;\nimport tigase.server.xmppserver.S2SConnectionManager;\nimport tigase.server.xmppserver.S2SIOService;\nimport tigase.xml.Element;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Dec 9, 2010 2:01:01 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"startTLS\", parent = S2SConnectionManager.class, active = true)\npublic class StartTLS\n\t\textends S2SAbstractProcessor {\n\n\tprivate static final Logger log = Logger.getLogger(StartTLS.class.getName());\n\tprivate static final Element features = new Element(START_TLS_EL, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew String[]{START_TLS_NS});\n\tprivate static final Element features_required = new Element(START_TLS_EL, new Element[]{new Element(\"required\")},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t new String[]{\"xmlns\"}, new String[]{START_TLS_NS});\n\tprivate static final Element starttls_el = new Element(START_TLS_EL, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{START_TLS_NS});\n\tprivate static final Element proceed_el = new Element(PROCEED_TLS_EL, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{START_TLS_NS});\n\n\t@Override\n\tpublic int order() {\n\t\treturn Order.StartTLS.ordinal();\n\t}\n\n\t@Override\n\tpublic boolean process(Packet p, S2SIOService serv, Queue<Packet> results) {\n\t\tif (p.isElement(START_TLS_EL, START_TLS_NS)) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Sending packet: {1} [{0}]\", new Object[]{serv, proceed_el});\n\t\t\t}\n\t\t\thandler.writeRawData(serv, proceed_el.toString());\n\t\t\ttry {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Starting TLS handshaking server side. [{0}]\", serv);\n\t\t\t\t}\n\t\t\t\tserv.getSessionData().put(\"TLS\", \"TLS\");\n\t\t\t\tserv.startTLS(false, handler.isTlsWantClientAuthEnabled(), handler.isTlsNeedClientAuthEnabled());\n\t\t\t} catch (IOException ex) {\n\t\t\t\tlog.log(Level.CONFIG, \"Problem with TLS initialization.\", ex);\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\t\tif (p.isElement(PROCEED_TLS_EL, START_TLS_NS)) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Received TLS proceed. [{0}]\", serv);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Starting TLS handshaking client side. [{0}]\", serv);\n\t\t\t\t}\n\t\t\t\tserv.getSessionData().put(\"TLS\", \"TLS\");\n\t\t\t\tserv.startTLS(true, handler.isTlsWantClientAuthEnabled(), handler.isTlsNeedClientAuthEnabled());\n\t\t\t} catch (IOException ex) {\n\t\t\t\tlog.log(Level.CONFIG, \"Problem with TLS initialization.\", ex);\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\t\tif (p.isElement(FEATURES_EL, FEATURES_NS) && p.getElement().getChildren() != null && !p.getElement().getChildren().isEmpty()) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Stream features received: {1} [{0}]\", new Object[]{serv, p});\n\t\t\t}\n\n\t\t\tCID cid = (CID) serv.getSessionData().get(S2SConnectionManager.CID_KEY);\n\t\t\tboolean skipTLS = (cid != null) && skipTLSForHost(cid.getRemoteHost());\n\n\t\t\tif (p.isXMLNSStaticStr(FEATURES_STARTTLS_PATH, START_TLS_NS) && !skipTLS) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Sending packet: {1} [{0}]\", new Object[]{serv, starttls_el});\n\t\t\t\t}\n\t\t\t\thandler.writeRawData(serv, starttls_el.toString());\n\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void streamFeatures(S2SIOService serv, List<Element> results) {\n\t\tif (!serv.getSessionData().containsKey(\"TLS\")) {\n\t\t\tCID cid = (CID) serv.getSessionData().get(S2SConnectionManager.CID_KEY);\n\t\t\tif (cid != null && !skipTLSForHost(cid.getRemoteHost()) && handler.isTlsRequired(cid.getLocalHost())) {\n\t\t\t\tresults.add(features_required);\n\t\t\t} else {\n\t\t\t\tresults.add(features);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean shouldSkipUndelivered(Packet packet) {\n\t\treturn packet.getElemName() == START_TLS_EL || packet.getElemName() == PROCEED_TLS_EL;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/proc/StartZlib.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver.proc;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Packet;\nimport tigase.server.xmppserver.S2SConnectionManager;\nimport tigase.server.xmppserver.S2SIOService;\nimport tigase.xml.Element;\n\nimport java.util.List;\nimport java.util.logging.Logger;\n\n/**\n * Created: Dec 9, 2010 2:01:12 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"startZlib\", parent = S2SConnectionManager.class, active = true)\npublic class StartZlib\n\t\textends S2SAbstractProcessor {\n\n\tprivate static final Logger log = Logger.getLogger(StartZlib.class.getName());\n\tprivate static final String START_ZLIB_XMLNS = \"http://jabber.org/features/compress\";\n\tprivate static final String START_ZLIB_ELEMENT_NAME = \"compression\";\n\tprivate static final Element features = new Element(START_ZLIB_ELEMENT_NAME, new Element[]{new Element(\"method\", \"zlib\")},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew String[]{START_ZLIB_XMLNS});\n\n\t@Override\n\tpublic int order() {\n\t\treturn Order.StartZlib.ordinal();\n\t}\n\n\t@Override\n\tpublic void streamFeatures(S2SIOService serv, List<Element> results) {\n\n\t\t// results.add(features);\n\t}\n\n\t@Override\n\tpublic boolean shouldSkipUndelivered(Packet packet) {\n\t\treturn packet.getElemName() == START_ZLIB_ELEMENT_NAME\n\t\t\t\t|| packet.getXMLNS() == START_ZLIB_XMLNS;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/proc/StreamError.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver.proc;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Packet;\nimport tigase.server.xmppserver.S2SConnectionManager;\nimport tigase.server.xmppserver.S2SIOService;\n\nimport java.util.Queue;\nimport java.util.logging.Logger;\n\n/**\n * Created: Dec 9, 2010 2:00:08 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"streamError\", parent = S2SConnectionManager.class, active = true)\npublic class StreamError\n\t\textends S2SAbstractProcessor {\n\n\tprivate static final Logger log = Logger.getLogger(StreamError.class.getName());\n\n\t@Override\n\tpublic int order() {\n\t\treturn Order.StreamError.ordinal();\n\t}\n\n\t@Override\n\tpublic boolean process(Packet p, S2SIOService serv, Queue<Packet> results) {\n\t\tif (p.getElemName() == \"error\") {\n\t\t\tserv.stop();\n\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/proc/StreamFeatures.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver.proc;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.net.ConnectionType;\nimport tigase.server.Packet;\nimport tigase.server.xmppserver.*;\nimport tigase.xml.Element;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Dec 9, 2010 2:00:25 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"streamFeatures\", parent = S2SConnectionManager.class, active = true)\npublic class StreamFeatures\n\t\textends S2SAbstractProcessor\n\t\timplements S2SFilterIfc {\n\n\tprivate static final Logger log = Logger.getLogger(StreamFeatures.class.getName());\n\n\tprivate static boolean hasEmptyOrOnlyObligatoryFeatures(Packet p) {\n\t\treturn hasEmptyOrOnlyObligatoryFeatures(p.getElement());\n\t}\n\n\t@Override\n\tpublic boolean shouldSkipUndelivered(Packet packet) {\n\t\treturn packet.getElemName() == STREAM_FEATURES_EL;\n\t}\n\n\tprivate static boolean hasEmptyOrOnlyObligatoryFeatures(Element featuresElement) {\n\t\treturn featuresElement.getChildren() == null || featuresElement.getChildren().isEmpty() ||\n\t\t\t\tfeaturesElement.findChildren(item -> item.getChild(\"required\") != null).isEmpty();\n\t}\n\n\t@Override\n\tpublic boolean filter(Packet p, S2SIOService serv, Queue<Packet> results) {\n\t\t// https://xmpp.org/rfcs/rfc6120.html#streams-negotiation-complete\n\t\t// The receiving entity indicates completion of the stream negotiation process by sending to the initiating entity\n\t\t// either an empty `<features/>` element or a `<features/>` element that contains only voluntary-to-negotiate features.\n\t\t//\n\t\t// if there is a stream features packet and we are authenticated and either it doesn't have any features or only have\n\t\t// non-mandatory features we should mark the service as \"completely negotiated\" as no other Processor handled the packet\n\t\t//\n\t\t// This should be done in filter to make sure there aren't any other processors that would want to negotiate more features.\n\n\t\tfinal boolean hasEmptyOrNonObligatoryFeatures = hasEmptyOrOnlyObligatoryFeatures(p);\n\t\tlog.log(Level.FINEST,\n\t\t\t\t\"Processing stream features and verifying if steam negotiation is complete; authenticated: {0}, completed: {1}, hasEmptyOrNonObligatoryFeatures: {2}, packet: {3} [{4}]\",\n\t\t\t\tnew Object[]{serv.isAuthenticated(), serv.isStreamNegotiationCompleted(),\n\t\t\t\t\t\t\t hasEmptyOrNonObligatoryFeatures, p, serv});\n\n\t\tif (p.isElement(FEATURES_EL, FEATURES_NS) && serv.isAuthenticated() && hasEmptyOrNonObligatoryFeatures) {\n\t\t\tstremNegotiationComplete(serv);\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int order() {\n\t\treturn Order.StreamFeatures.ordinal();\n\t}\n\n\t@Override\n\tpublic boolean process(Packet p, S2SIOService serv, Queue<Packet> results) {\n//\t\tif (p.isElement(FEATURES_EL, FEATURES_NS) && p.getElement().getChildren() != null && !p.getElement().getChildren().isEmpty()) {\n//\t\t\tif (log.isLoggable(Level.FINEST)) {\n//\t\t\t\tlog.log(Level.FINEST, \"Stream features received: {1} [{0}]\", new Object[]{serv, p});\n//\t\t\t}\n//\n//\t\t\treturn true;\n//\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String streamOpened(S2SIOService serv, Map<String, String> attribs) {\n\t\tif (attribs.containsKey(\"version\")) {\n\n\t\t\t// A version=1.0,  sending features\n\t\t\tif (serv.connectionType() == ConnectionType.accept) {\n\n\t\t\t\t// Send features only for accept connections\n\t\t\t\tList<Element> features = handler.getStreamFeatures(serv);\n\t\t\t\tElement featuresElement = new Element(STREAM_FEATURES_EL);\n\n\t\t\t\tfeaturesElement.addChildren(features);\n\n\t\t\t\t// do not send stattls feature to hosts in skip tls list\n\t\t\t\tif (attribs.containsKey(\"from\")) {\n\t\t\t\t\tif (skipTLSForHost(attribs.get(\"from\"))) {\n\t\t\t\t\t\tElement startTls = featuresElement.getChild(START_TLS_EL, START_TLS_NS);\n\t\t\t\t\t\tfeaturesElement.removeChild(startTls);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Sending stream features: {1} [{0}]\", new Object[]{serv, featuresElement});\n\t\t\t\t}\n\n\t\t\t\tserv.addPacketToSend(Packet.packetInstance(featuresElement, null, null));\n\n\t\t\t\tfinal boolean hasEmptyOrNonObligatoryFeatures = hasEmptyOrOnlyObligatoryFeatures(featuresElement);\n\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\"Sending stream features and verifying if steam negotiation is complete; authenticated: {0}, completed: {1}, hasEmptyOrNonObligatoryFeatures: {2}, features: {3} [{4}]\",\n\t\t\t\t\t\tnew Object[]{serv.isAuthenticated(), serv.isStreamNegotiationCompleted(),\n\t\t\t\t\t\t\t\t\t hasEmptyOrNonObligatoryFeatures, featuresElement, serv});\n\n\t\t\t\tif (serv.isAuthenticated() && hasEmptyOrNonObligatoryFeatures) {\n\t\t\t\t\tstremNegotiationComplete(serv);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate void stremNegotiationComplete(S2SIOService serv) {\n\t\tif (!serv.isStreamNegotiationCompleted()) {\n\t\t\tCID cid = (CID) serv.getSessionData().get(S2SConnectionManager.CID_KEY);\n\t\t\tif (cid != null) {\n\t\t\t\tCIDConnections cid_conns = null;\n\t\t\t\ttry {\n\t\t\t\t\tcid_conns = handler.getCIDConnections(cid, false);\n\t\t\t\t\tif (cid_conns != null) {\n\t\t\t\t\t\tcid_conns.streamNegotiationCompleted(serv);\n\t\t\t\t\t}\n\t\t\t\t} catch (NotLocalhostException | LocalhostException e) {\n\t\t\t\t\t//can be ignored\n\t\t\t\t}\n\t\t\t}\n\t\t\tserv.streamNegotiationCompleted();\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppserver/proc/StreamOpen.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver.proc;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.net.ConnectionType;\nimport tigase.server.xmppserver.*;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Dec 9, 2010 1:59:56 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"streamOpen\", parent = S2SConnectionManager.class, active = true)\npublic class StreamOpen\n\t\textends S2SAbstractProcessor {\n\n\tprivate static final Logger log = Logger.getLogger(StreamOpen.class.getName());\n\n\t@Override\n\tpublic int order() {\n\t\treturn Order.StreamOpen.ordinal();\n\t}\n\n\t@Override\n\tpublic void serviceStarted(S2SIOService serv) {\n\t\tswitch (serv.connectionType()) {\n\t\t\tcase connect:\n\t\t\t\tCID cid = (CID) serv.getSessionData().get(S2SConnectionManager.CID_KEY);\n\n\t\t\t\tserv.getSessionData().put(S2SIOService.HOSTNAME_KEY, cid.getLocalHost());\n\n\t\t\t\t// Send init xmpp stream here\n\t\t\t\t// XMPPIOService serv = (XMPPIOService)service;\n\t\t\t\tString data = \"<stream:stream\" + \" xmlns:stream='http://etherx.jabber.org/streams'\" +\n\t\t\t\t\t\t\" xmlns='jabber:server'\" + \" xmlns:db='jabber:server:dialback'\" + \" from='\" +\n\t\t\t\t\t\tcid.getLocalHost() + \"'\" + \" to='\" + cid.getRemoteHost() + \"'\" + \" version='1.0'>\";\n\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Sending: {1} [{0}]\", new Object[]{serv, data});\n\t\t\t\t}\n\n\t\t\t\tS2SConnection s2s_conn = (S2SConnection) serv.getSessionData().get(S2SIOService.S2S_CONNECTION_KEY);\n\n\t\t\t\tif (s2s_conn == null) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Protocol error s2s_connection not set for outgoing connection: {0}\", serv);\n\t\t\t\t\tserv.stop();\n\t\t\t\t} else {\n\t\t\t\t\ts2s_conn.setS2SIOService(serv);\n\t\t\t\t\tserv.setS2SConnection(s2s_conn);\n\t\t\t\t}\n\n\t\t\t\tserv.xmppStreamOpen(data);\n\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\n\t\t\t\t// Do nothing, more data should come soon...\n\t\t\t\tbreak;\n\t\t} // end of switch (service.connectionType())\n\t}\n\n\t@Override\n\tpublic void serviceStopped(S2SIOService serv) {\n\t\tCID cid = (CID) serv.getSessionData().get(S2SConnectionManager.CID_KEY);\n\n\t\tif (cid == null) {\n\t\t\tif (serv.connectionType() == ConnectionType.connect) {\n\t\t\t\tlog.log(Level.WARNING, \"Protocol error cid not set for outgoing connection: {0}\", serv);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tCIDConnections cid_conns = handler.getCIDConnections(cid, false);\n\n\t\t\tif (cid_conns == null) {\n\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\tlog.log(Level.FINE, \"Protocol error cid_conns not found for outgoing connection: {0}\", serv);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\tcid_conns.connectionStopped(serv);\n\t\t\t}\n\t\t} catch (NotLocalhostException ex) {\n\t\t\tlog.log(Level.WARNING, \"Not a local host for cid: {0}\", cid);\n\t\t} catch (LocalhostException ex) {\n\t\t\tlog.log(Level.WARNING, \"A local host for cid: {0}\", cid);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String streamOpened(S2SIOService serv, Map<String, String> attribs) {\n\t\tCID cid = (CID) serv.getSessionData().get(S2SConnectionManager.CID_KEY);\n\t\tString remote_hostname = attribs.get(\"from\");\n\t\tString local_hostname = attribs.get(\"to\");\n\t\tString version = attribs.get(VERSION_ATT_NAME);\n\t\tif (version != null) {\n\t\t\tserv.getSessionData().put(VERSION_ATT_NAME, version);\n\t\t}\n\n\t\tif (cid == null) {\n\t\t\tif ((remote_hostname != null) && (local_hostname != null)) {\n\t\t\t\tcid = new CID(local_hostname, remote_hostname);\n\t\t\t}\n\t\t}\n\n\n\t\ttry {\n\t\t\tif (serv.connectionType() == ConnectionType.accept) {\n\t\t\t\thandler.validateCIDConnection(new CID(local_hostname, remote_hostname));\n\t\t\t}\n\n\t\t\tCIDConnections cid_conns = handler.getCIDConnections(cid, false);\n\n\t\t\tswitch (serv.connectionType()) {\n\t\t\t\tcase connect: {\n\n\t\t\t\t\t// It must be always set for connect connection type\n\t\t\t\t\tString remote_id = attribs.get(\"id\");\n\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Connect Stream opened for: {1}, session id: {2} [{0}]\",\n\t\t\t\t\t\t\t\tnew Object[]{serv, cid, remote_id});\n\t\t\t\t\t}\n\n\t\t\t\t\tif (cid_conns == null) {\n\n\t\t\t\t\t\t// This should actually not happen. Let's be clear here about\n\t\t\t\t\t\t// handling unexpected\n\t\t\t\t\t\t// cases.\n\t\t\t\t\t\tlog.log(Level.WARNING, \"This might be a bug in s2s code, should not happen.\" +\n\t\t\t\t\t\t\t\t\" Missing CIDConnections for stream open to ''connect'' service type. [{0}]\", serv);\n\t\t\t\t\t\tgenerateStreamError(false, \"internal-server-error\", serv);\n\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Stream open for cid: {1}, outgoint: {2}, incoming: {3} [{0}]\",\n\t\t\t\t\t\t\t\tnew Object[]{serv, cid, cid_conns.getOutgoingCount(), cid_conns.getIncomingCount()});\n\t\t\t\t\t}\n\n\t\t\t\t\tserv.setSessionId(remote_id);\n\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\n\t\t\t\tcase accept: {\n\t\t\t\t\tif (local_hostname != null) {\n\t\t\t\t\t\tserv.getSessionData().put(S2SIOService.HOSTNAME_KEY, local_hostname);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Unknown local hostname. [{0}]\", serv);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tString id = UUID.randomUUID().toString();\n\n\t\t\t\t\tserv.setSessionId(id);\n\n\t\t\t\t\tString stream_open = \"<stream:stream\" + \" xmlns:stream='http://etherx.jabber.org/streams'\" +\n\t\t\t\t\t\t\t\" xmlns='jabber:server'\" + \" xmlns:db='jabber:server:dialback'\" + \" id='\" + id + \"'\";\n\n\t\t\t\t\tif (cid != null) {\n\t\t\t\t\t\tstream_open += \" from='\" + cid.getLocalHost() + \"'\" + \" to='\" + cid.getRemoteHost() + \"'\";\n\n\t\t\t\t\t\tif (cid_conns == null) {\n\t\t\t\t\t\t\tcid_conns = handler.getCIDConnections(cid, true);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Accept Stream opened for: {1}, session id: {2} [{0}]\",\n\t\t\t\t\t\t\t\t\tnew Object[]{serv, cid, id});\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tserv.getSessionData().put(S2SConnectionManager.CID_KEY, cid);\n\t\t\t\t\t\tserv.setConnectionId(JID.jidInstanceNS(cid.getLocalHost(), cid.getRemoteHost(), serv.getConnectionId().getResource()));\n\t\t\t\t\t\tcid_conns.addIncoming(serv);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Accept Stream opened for unknown CID, session id: {1} [{0}]\",\n\t\t\t\t\t\t\t\t\tnew Object[]{serv, id});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Spec examples show that the version should always be included but\n\t\t\t\t\t// this seems to break some servers.\n\t\t\t\t\tif (FORCE_VERSION || attribs.containsKey(\"version\")) {\n\t\t\t\t\t\tstream_open += \" version='1.0'\";\n\t\t\t\t\t}\n\t\t\t\t\tstream_open += \">\";\n\n\t\t\t\t\treturn stream_open;\n\t\t\t\t}\n\n\t\t\t\tdefault:\n\t\t\t\t\tlog.log(Level.SEVERE, \"Warning, program shouldn't reach that point. [{0}]\", serv);\n\n\t\t\t\t\tbreak;\n\t\t\t} // end of switch (serv.connectionType())\n\t\t} catch (NotLocalhostException ex) {\n\t\t\tgenerateStreamError(false, \"host-unknown\", serv, ex);\n\t\t} catch (LocalhostException ex) {\n\t\t\tgenerateStreamError(false, \"invalid-from\", serv, ex);\n\t\t}\n\n\t\treturn null;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppsession/DisconnectUserEBAction.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppsession;\n\nimport tigase.eventbus.EventBusAction;\nimport tigase.xmpp.StreamError;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\n\npublic class DisconnectUserEBAction\n\t\timplements Serializable, EventBusAction {\n\n\tprivate StreamError error;\n\tprivate String message;\n\tprivate List<String> resources;\n\tprivate BareJID userJid;\n\n\tpublic DisconnectUserEBAction() {\n\t}\n\n\tpublic DisconnectUserEBAction(BareJID userJid, List<String> resources, StreamError error, String message) {\n\t\tthis.userJid = userJid;\n\t\tthis.resources = Objects.requireNonNullElse(resources, Collections.emptyList());\n\t\tthis.error = error;\n\t\tthis.message = message;\n\t}\n\n\tpublic DisconnectUserEBAction(BareJID userJid, StreamError error, String message) {\n\t\tthis(userJid, Collections.emptyList(), error, message);\n\t}\n\n\tpublic StreamError getError() {\n\t\treturn error;\n\t}\n\n\tpublic void setError(StreamError error) {\n\t\tthis.error = error;\n\t}\n\n\tpublic String getMessage() {\n\t\treturn message;\n\t}\n\n\tpublic void setMessage(String message) {\n\t\tthis.message = message;\n\t}\n\n\tpublic List<String> getResources() {\n\t\treturn resources;\n\t}\n\n\tpublic void setResources(List<String> resources) {\n\t\tthis.resources = resources;\n\t}\n\n\tpublic BareJID getUserJid() {\n\t\treturn userJid;\n\t}\n\n\tpublic void setUserJid(BareJID userJid) {\n\t\tthis.userJid = userJid;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"DisconnectUserSessions{\" + \"userJid=\" + getUserJid() + \", resources=\" + getResources() + \", error=\" +\n\t\t\t\tgetError() + \", message='\" + getMessage() + '\\'' + '}';\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppsession/PacketDefaultHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppsession;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.server.Packet;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Describe class PacketDefaultHandler here.\n * <br>\n * Created: Fri Feb 2 15:08:58 2007\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class PacketDefaultHandler {\n\n\tprivate static final Logger log = Logger.getLogger(PacketDefaultHandler.class.getName());\n\n\t// private static TigaseRuntime runtime = TigaseRuntime.getTigaseRuntime();\n\t// private RosterAbstract roster_util =\n\t// RosterFactory.getRosterImplementation(true);\n\tprivate String[] IGNORE_PACKETS = {\"stream:features\"};\n\tprivate StanzaType[] IGNORE_TYPES = {StanzaType.error};\n\n\t/**\n\t * Creates a new <code>PacketDefaultHandler</code> instance.\n\t */\n\tpublic PacketDefaultHandler() {\n\t}\n\n\t/**\n\t * Method checks if packet can be processed by this handler\n\t *\n\t */\n\tpublic boolean canHandle(Packet packet, XMPPResourceConnection session) {\n\t\tif (session == null) {\n\t\t\treturn false;\n\t\t}    // end of if (session == null)\n\n\t\t// Cannot forward packet if there is no destination address\n\t\tif (packet.getStanzaTo() == null) {\n\n\t\t\t// If this is simple <iq type=\"result\"/> then ignore it\n\t\t\t// and consider it OK\n\t\t\tif ((packet.getElemName() == \"iq\") && (packet.getType() == StanzaType.result)) {\n\n\t\t\t\t// Nothing to do....\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"No ''to'' address, can''t deliver packet: {0}\", packet);\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tpublic boolean forward(final Packet packet, final XMPPResourceConnection session, final NonAuthUserRepository repo,\n\t\t\t\t\t\t   final Queue<Packet> results) {\n\n\t\t// Processing of the packets which needs to be processed as quickly\n\t\t// as possible, direct presences from unsubscribed entities apparently\n\t\t// have high priority as they may come from MUC and must be delivered\n\t\t// before room history\n\t\t// if (packet.getElemName() == \"presence\") {\n\t\t// PresenceType pres_type = roster_util.getPresenceType(session, packet);\n\t\t// if ((prese_type == PresenceType.in_initial)\n\t\t// && (packet.getElemFrom() != null)\n\t\t// && (roster_util.isSubscribedTo(session, packet.getElemFrom())\n\t\t// || (DynamicRoster.getBuddyItem(session, settings,\n\t\t// packet.getElemFrom()) != null))) {\n\t\t// }\n\t\t// }\n\t\treturn false;\n\t}\n\n\tpublic boolean preprocess(final Packet packet, final XMPPResourceConnection session,\n\t\t\t\t\t\t\t  final NonAuthUserRepository repo, final Queue<Packet> results) {\n\t\tif (session != null) {\n\t\t\tsession.incPacketsCounter();\n\n\t\t\tXMPPSession parent = session.getParentSession();\n\n\t\t\tif (parent != null) {\n\t\t\t\tparent.incPacketsCounter();\n\t\t\t}\n\t\t}\n\t\tfor (int i = 0; i < IGNORE_PACKETS.length; i++) {\n\t\t\tif ((packet.getElemName() == IGNORE_PACKETS[i]) && (packet.getType() == IGNORE_TYPES[i])) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results) throws XMPPException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Processing packet: {0}\", packet.toStringSecure());\n\t\t}\n\t\ttry {\n\t\t\tJID to = packet.getStanzaTo();\n\t\t\tJID from = packet.getStanzaFrom();\n\n\t\t\t// If this is simple <iq type=\"result\"/> then ignore it\n\t\t\t// and consider it OK\n\t\t\tif ((to == null) && (packet.getElemName() == \"iq\") && (packet.getType() == StanzaType.result)) {\n\n\t\t\t\t// Nothing to do....\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (session.isUserId(to.getBareJID()) && (from == null || !session.isUserId(from.getBareJID()) ||\n\t\t\t\t\t!session.getConnectionId().equals(packet.getPacketFrom()))) {\n\n\t\t\t\t// Yes this is message to 'this' client\n\t\t\t\tPacket result;\n\n\t\t\t\t// This is where and how we set the address of the component\n\t\t\t\t// which should rceive the result packet for the final delivery\n\t\t\t\t// to the end-user. In most cases this is a c2s or Bosh component\n\t\t\t\t// which keep the user connection.\n\t\t\t\tString resource = packet.getStanzaTo().getResource();\n\n\t\t\t\tif (resource == null) {\n\n\t\t\t\t\t// In default packet handler we deliver packets to a specific resource only\n\t\t\t\t\tresult = Authorization.FEATURE_NOT_IMPLEMENTED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"The feature is not supported yet.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  true);\n\t\t\t\t\tresult.setPacketFrom(null);\n\t\t\t\t\tresult.setPacketTo(null);\n\t\t\t\t} else {\n\n\t\t\t\t\t// Otherwise only to the given resource or sent back as error.\n\t\t\t\t\tXMPPResourceConnection con = session.getParentSession().getResourceForResource(resource);\n\n\t\t\t\t\tif (con != null) {\n\t\t\t\t\t\tresult = packet.copyElementOnly();\n\t\t\t\t\t\tresult.setPacketTo(con.getConnectionId());\n\n\t\t\t\t\t\t// In most cases this might be skept, however if there is a\n\t\t\t\t\t\t// problem during packet delivery an error might be sent back\n\t\t\t\t\t\tresult.setPacketFrom(packet.getTo());\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Delivering message, packet: {0}, to session: {1}\",\n\t\t\t\t\t\t\t\t\tnew Object[]{packet, con});\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult = Authorization.RECIPIENT_UNAVAILABLE.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"The recipient is no longer available.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttrue);\n\t\t\t\t\t\tresult.setPacketFrom(null);\n\t\t\t\t\t\tresult.setPacketTo(null);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Don't forget to add the packet to the results queue or it\n\t\t\t\t// will be lost.\n\t\t\t\tresults.offer(result);\n\n\t\t\t\treturn;\n\t\t\t}    // end of else\n\t\t\tif (from != null) {\n\t\t\t\tif (session.isUserId(from.getBareJID())) {\n\t\t\t\t\tPacket result = packet.copyElementOnly();\n\n\t\t\t\t\tresults.offer(result);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (NotAuthorizedException e) {\n\t\t\ttry {\n\t\t\t\tresults.offer(\n\t\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(packet, \"You must authorize session first.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttrue));\n\t\t\t\tlog.log(Level.FINE, \"NotAuthorizedException for packet: {0}\", packet.toString());\n\t\t\t} catch (PacketErrorTypeException e2) {\n\t\t\t\tlog.log(Level.FINE, \"Packet processing exception: {0}\", e2);\n\t\t\t}\n\t\t}    // end of try-catch\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppsession/SMResourceConnection.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppsession;\n\nimport tigase.db.AuthRepository;\nimport tigase.db.UserRepository;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.vhosts.VHostItemImpl;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Feb 27, 2010 8:02:11 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class SMResourceConnection\n\t\textends XMPPResourceConnection {\n\n\tpublic SMResourceConnection(JID connectionId, UserRepository rep, AuthRepository authRepo,\n\t\t\t\t\t\t\t\tSessionManagerHandler loginHandler) {\n\t\tsuper(connectionId, rep, authRepo, loginHandler);\n\n\t\ttry {\n\t\t\tsetDomain(new VHostItemImpl(loginHandler.getComponentId().getDomain()));\n\t\t} catch (TigaseStringprepException ex) {\n\t\t\tLogger.getLogger(SMResourceConnection.class.getName()).log(Level.SEVERE, null, ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isServerSession() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean isUserId(BareJID bareJID) {\n\t\treturn isLocalDomain(bareJID.toString(), false);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppsession/SessionManager.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppsession;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.auth.mechanisms.AbstractSaslSCRAM;\nimport tigase.auth.mechanisms.SaslEXTERNAL;\nimport tigase.component.ComponenScriptCommandProcessor;\nimport tigase.component.PacketWriter;\nimport tigase.component.adhoc.AdHocCommand;\nimport tigase.component.adhoc.AdHocCommandManager;\nimport tigase.component.exceptions.ComponentException;\nimport tigase.component.modules.impl.AdHocCommandModule;\nimport tigase.component.responses.AsyncCallback;\nimport tigase.conf.Configurable;\nimport tigase.db.AuthRepository;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserRepository;\nimport tigase.disco.XMPPService;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.eventbus.HandleEvent;\nimport tigase.eventbus.events.ShutdownEvent;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.RegistrarBean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.beans.selector.ClusterModeRequired;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.*;\nimport tigase.server.script.CommandIfc;\nimport tigase.server.xmppclient.StreamManagementCommand;\nimport tigase.stats.MaxDailyCounterQueue;\nimport tigase.stats.StatisticsList;\nimport tigase.sys.OnlineJidsReporter;\nimport tigase.sys.TigaseRuntime;\nimport tigase.util.Base64;\nimport tigase.util.common.TimerTask;\nimport tigase.util.processing.ProcessingThreads;\nimport tigase.util.processing.QueueItem;\nimport tigase.util.processing.WorkerThread;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.vhosts.VHostItem;\nimport tigase.vhosts.VHostItemImpl;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.C2SDeliveryErrorProcessor;\nimport tigase.xmpp.impl.PresenceCapabilitiesManager;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport javax.script.Bindings;\nimport java.io.ByteArrayInputStream;\nimport java.lang.reflect.Method;\nimport java.security.cert.Certificate;\nimport java.security.cert.CertificateFactory;\nimport java.time.Duration;\nimport java.util.*;\nimport java.util.concurrent.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.server.xmppsession.SessionManagerConfig.*;\nimport static tigase.xmpp.StreamError.ResourceConstraint;\nimport static tigase.xmpp.impl.StreamManagementInline.SESSION_RESUMPTION_ID_KEY;\n\n/**\n * Class SessionManager\n * <br>\n * Created: Tue Nov 22 07:07:11 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"sess-man\", parent = Kernel.class, active = true, exportable = true)\n@ConfigType({ConfigTypeEnum.DefaultMode, ConfigTypeEnum.SessionManagerMode})\n@ClusterModeRequired(active = false)\npublic class SessionManager\n\t\textends AbstractMessageReceiver\n\t\timplements Configurable, SessionManagerHandler, OnlineJidsReporter, RegistrarBean {\n\n\tprotected static final String ADMIN_COMMAND_NODE = \"http://jabber.org/protocol/admin\";\n\n\tprivate static final Logger log = Logger.getLogger(SessionManager.class.getName());\n\n\tprivate static final String SESSION_CLOSE_TIMER_KEY = \"session-close-timer\";\n\n\t/**\n\t * A Map with connectionID as a key and an object with all the user connection data as a value\n\t */\n\tprotected ConcurrentHashMap<JID, XMPPResourceConnection> connectionsByFrom = new ConcurrentHashMap<JID, XMPPResourceConnection>(\n\t\t\t100000);\n\t/**\n\t * A Map with bare user JID as a key and a user session object as a value.\n\t */\n\tprotected ConcurrentHashMap<BareJID, XMPPSession> sessionsByNodeId = new ConcurrentHashMap<BareJID, XMPPSession>(\n\t\t\t100000);\n\tprivate int activeUserNumber = 0;\n\t@ConfigField(desc = \"ActiveUsers timeframe\", alias = SessionManagerConfig.ACTIVE_USER_TIMEFRAME_KEY)\n\tprivate long activeUserTimeframe = 5 * 60 * 1000;\n\t@Inject\n\tprivate AdHocCommandModule adHocCommandModule;\n\t@Inject\n\tprivate ConcurrentSkipListSet<XMPPImplIfc> allPlugins = new ConcurrentSkipListSet<XMPPImplIfc>();\n\t@ConfigField(desc = \"Authentication timeout\", alias = SessionManagerConfig.AUTH_TIMEOUT_PROP_KEY)\n\tprivate long authTimeout = 120;\n\tprivate long authTimeouts = 0;\n\t@Inject\n\tprivate AuthRepository auth_repository = null;\n\tprivate long closedConnections = 0;\n\tprivate ConnectionCheckCommandHandler connectionCheckCommandHandler = new ConnectionCheckCommandHandler();\n\t@ConfigField(desc = \"Period after which connection may be checked when authenticating a new session\")\n\tprivate long connectionCheckPeriod = 30 * 1000;\n\t@Inject\n\tprivate DefaultHandlerProc defHandlerProc = null;\n\tprivate PacketDefaultHandler defPacketHandler = new PacketDefaultHandler();\n\tprivate String defPluginsThreadsPool = \"default-threads-pool\";\n\t// Can not inject eventBus as it is used before injection is done\n\t// TODO - Maybe we should allow autoregistration for event bus so that every\n\t// annotated bean instance would be registered to eventbus?\n\t//@Inject\n\tprivate EventBus eventBus = EventBusFactory.getInstance();\n\t@ConfigField(desc = \"Force detail check of stale connections\", alias = SessionManagerConfig.STALE_CONNECTION_CLOSER_QUEUE_SIZE_KEY)\n\tprivate boolean forceDetailStaleConnectionCheck = true;\n\tprivate Kernel kernel = null;\n\t/*\n\t * Date of moment where daily stats was resetted.\n\t */\n\tprivate Calendar lastDailyStatsReset = Calendar.getInstance();\n\tprivate int maxDailyUsersConnectionsWithinLastWeek = 0;\n\tprivate MaxDailyCounterQueue<Integer> maxDailyUsersSessions = new MaxDailyCounterQueue<>(31);\n\tprivate int maxIdx = 100;\n\tprivate int maxUserConnections = 0;\n\tprivate int maxUserSessions = 0;\n\tprivate int maxUserSessionsDaily = 0;\n\tprivate int maxUserSessionsYesterday = 0;\n\tprivate Long activeUsersLastDay = null;\n\tprivate Long activeUsersLastWeek = null;\n\tprivate Long activeUsersLast30Days = null;\n\t@Inject\n\tprivate NonAuthUserRepository naUserRepository;\n\tprivate NodeShutdownTask nodeShutdownTask = new NodeShutdownTask();\n\tprivate Map<String, XMPPPacketFilterIfc> outFilters = new ConcurrentHashMap<String, XMPPPacketFilterIfc>(10);\n\t// This is not used any more as plugins settings are passed by annotation and configuration injection\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tprivate Map<String, Map<String, Object>> plugin_config = new ConcurrentHashMap<String, Map<String, Object>>(20);\n\t@ConfigField(desc = \"Factor for number of threads per plugin\", alias = SessionManagerConfig.SM_THREADS_FACTOR_PROP_KEY)\n\tprivate int pluginsThreadFactor = 1;\n\tprivate Map<String, XMPPPostprocessorIfc> postProcessors = new ConcurrentHashMap<String, XMPPPostprocessorIfc>(10);\n\t// private long[] defPrepTime = new long[maxIdx];\n\t// private long[] prepTime = new long[maxIdx];\n\t// private long[] defForwTime = new long[maxIdx];\n\t// private long[] walkTime = new long[maxIdx];\n\t// private long[] postTime = new long[maxIdx];\n\tprivate Map<String, long[]> postTimes = new ConcurrentSkipListMap<String, long[]>();\n\tprivate Map<String, XMPPPreprocessorIfc> preProcessors = new ConcurrentHashMap<String, XMPPPreprocessorIfc>(10);\n\tprivate Map<String, XMPPProcessorIfc> processors = new ConcurrentHashMap<String, XMPPProcessorIfc>(32);\n\t@Inject(nullAllowed = true)\n\tprivate MessageRouter router;\n\t@Inject\n\tprivate SessionCloseProc sessionCloseProc = null;\n\t@Inject\n\tprivate SessionOpenProc sessionOpenProc = null;\n\t@ConfigField(desc = \"Include CAPS in stream features\")\n\tprivate boolean includeCapsInStream = true;\n\t@ConfigField(desc = \"Skip privacy check\", alias = SessionManagerConfig.SKIP_PRIVACY_PROP_KEY)\n\tprivate boolean skipPrivacy = false;\n\t@ConfigField(desc = \"Max no. of connections single user can user\", alias = \"user-connections-limit\")\n\tprivate Integer singleUserConnectionsLimit = null;\n\tprivate SMResourceConnection smResourceConnection = null;\n\t@ConfigField(desc = \"Default processors threads pool size\", alias = SessionManagerConfig.SM_THREADS_POOL_PROP_KEY)\n\tprivate String smThreadsPool = SessionManagerConfig.SM_THREADS_POOL_PROP_VAL;\n\t@Inject(nullAllowed = true)\n\tprotected MessageArchive messageArchive = null;\n\tprivate StaleConnectionCloser staleConnectionCloser = new StaleConnectionCloser();\n\tprivate Map<String, XMPPStopListenerIfc> stopListeners = new ConcurrentHashMap<String, XMPPStopListenerIfc>(10);\n\tprivate int tIdx = 0;\n\tprivate long totalUserConnections = 0;\n\tprivate long totalUserSessions = 0;\n\t@Inject\n\tprivate UserRepository user_repository = null;\n\n\tprivate Map<String, ProcessingThreads<ProcessorWorkerThread>> workerThreads = new ConcurrentHashMap<String, ProcessingThreads<ProcessorWorkerThread>>(\n\t\t\t32);\n\n\t@Override\n\tpublic boolean addOutPacket(Packet packet) {\n\n\t\t// We actually have to set packetFrom address to the session manager ID\n\t\t// to make sure the connection manager for instance can report problems back\n\t\t// This cause other problems with packets processing which have to be\n\t\t// resolved anyway\n\t\tif (packet.getPacketFrom() == null) {\n\t\t\tpacket.setPacketFrom(getComponentId());\n\t\t}\n\n\t\treturn super.addOutPacket(packet);\n\t}\n\n\tpublic XMPPImplIfc addPlugin(XMPPImplIfc proc)\n\t\t\tthrows ClassNotFoundException, InstantiationException, IllegalAccessException, TigaseDBException {\n\t\tString version;\n\n\t\tboolean loaded = false;\n\t\tif (proc instanceof XMPPProcessorIfc) {\n\t\t\tint threadsNo = proc.concurrentQueuesNo();\n\t\t\tint queueSize = maxQueueSize / threadsNo;\n\n\t\t\tboolean requireNewPool = false;\n\t\t\tif (proc instanceof XMPPProcessorConcurrencyAwareIfc) {\n\t\t\t\tXMPPProcessorConcurrencyAwareIfc procca = (XMPPProcessorConcurrencyAwareIfc) proc;\n\t\t\t\tif (threadsNo != procca.getThreadsNo()) {\n\t\t\t\t\tthreadsNo = procca.getThreadsNo();\n\t\t\t\t\tlog.log(Level.CONFIG, \"Concurrency for plugin: {0} set to: {1}\",\n\t\t\t\t\t\t\tnew Object[]{proc.id(), threadsNo});\n\t\t\t\t\trequireNewPool = true;\n\t\t\t\t}\n\t\t\t\tif (procca.getQueueSize() != null) {\n\t\t\t\t\tqueueSize = procca.getQueueSize();\n\t\t\t\t\tlog.log(Level.CONFIG, \"Queue for plugin: {0} set to: {1} per thread\",\n\t\t\t\t\t\t\tnew Object[]{proc.id(), queueSize});\n\t\t\t\t\trequireNewPool = true;\n\t\t\t\t} else {\n\t\t\t\t\tqueueSize = maxQueueSize / threadsNo;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthreadsNo = threadsNo * pluginsThreadFactor;\n\n\t\t\t// If there is not default processors thread pool or the processor does\n\t\t\t// have thread pool specific settings create a separate thread pool\n\t\t\t// for the processor\n\t\t\tif ((workerThreads.get(defPluginsThreadsPool) == null) || requireNewPool) {\n\n\t\t\t\t// Added to make sure that there will be only one thread pool for plugin\n\t\t\t\t// so if one exits we will keep it and not create another one\n\t\t\t\tif (!workerThreads.containsKey(proc.id())) {\n\t\t\t\t\tProcessorWorkerThread worker = new ProcessorWorkerThread();\n\t\t\t\t\tProcessingThreads<ProcessorWorkerThread> pt = new ProcessingThreads<ProcessorWorkerThread>(worker,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   threadsNo,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   queueSize,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   proc.id());\n\n\t\t\t\t\tworkerThreads.put(proc.id(), pt);\n\t\t\t\t\tlog.log(Level.CONFIG, \"Created thread pool: {0}, queue per thread: {1} for plugin id: {2}\",\n\t\t\t\t\t\t\tnew Object[]{threadsNo, queueSize, proc.id()});\n\t\t\t\t}\n\t\t\t}\n\t\t\tprocessors.put(proc.id(), (XMPPProcessorIfc) proc);\n\t\t\tlog.log(Level.CONFIG, \"Added processor: {0} for plugin id: {1}\",\n\t\t\t\t\tnew Object[]{proc.getClass().getSimpleName(), proc.id()});\n\t\t\tloaded = true;\n\t\t\tversion = proc.getComponentInfo().getComponentVersion();\n\t\t\tlog.log(Level.INFO, \"Loading plugin: \" + proc.id() + \"=\" + threadsNo + \":\" + queueSize + \" ... \" +\n\t\t\t\t\t\t\t\t\t   (version.isEmpty() ? \"\" : \"\\t, version: \" + version));\n\t\t}\n\n\t\tif (proc instanceof XMPPPreprocessorIfc) {\n\t\t\tpreProcessors.put(proc.id(), (XMPPPreprocessorIfc) proc);\n\t\t\tlog.log(Level.CONFIG, \"Added preprocessor: {0} for plugin id: {1}\",\n\t\t\t\t\tnew Object[]{proc.getClass().getSimpleName(), proc.id()});\n\t\t\tloaded = true;\n\t\t}\n\n\t\tif (proc instanceof XMPPPostprocessorIfc) {\n\t\t\tpostProcessors.put(proc.id(), (XMPPPostprocessorIfc) proc);\n\t\t\tlog.log(Level.CONFIG, \"Added postprocessor: {0} for plugin id: {1}\",\n\t\t\t\t\tnew Object[]{proc.getClass().getSimpleName(), proc});\n\t\t\tloaded = true;\n\t\t}\n\n\t\tif (proc instanceof XMPPStopListenerIfc) {\n\t\t\tstopListeners.put(proc.id(), (XMPPStopListenerIfc) proc);\n\t\t\tlog.log(Level.CONFIG, \"Added stopped processor: {0} for plugin id: {1}\",\n\t\t\t\t\tnew Object[]{proc.getClass().getSimpleName(), proc.id()});\n\t\t\tloaded = true;\n\t\t}\n\n\t\tif (proc instanceof XMPPPacketFilterIfc) {\n\t\t\toutFilters.put(proc.id(), (XMPPPacketFilterIfc) proc);\n\t\t\tlog.log(Level.CONFIG, \"Added packet filter: {0} for plugin id: {1}\",\n\t\t\t\t\tnew Object[]{proc.getClass().getSimpleName(), proc.id()});\n\t\t\tloaded = true;\n\t\t}\n\t\tif (!loaded) {\n\t\t\tlog.log(Level.WARNING, \"No implementation found for plugin id: {0}\", proc.id());\n\t\t}    // end of if (!loaded)\n\t\tif (proc != null) {\n\t\t\tif (allPlugins.add(proc)) {\n\t\t\t\tMap<String, Object> settings = new HashMap<>();\n\t\t\t\ttry {\n\t\t\t\t\tMethod m = proc.getClass().getDeclaredMethod(\"init\", Map.class);\n\t\t\t\t\tif (m.getAnnotation(Deprecated.class) == null) {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"processor \" + proc.getClass().getCanonicalName() +\n\t\t\t\t\t\t\t\t\" is using deprecated init() method!\");\n\t\t\t\t\t\tproc.init(settings);\n\t\t\t\t\t}\n\t\t\t\t} catch (NoSuchMethodException | SecurityException ex) {\n\t\t\t\t\t// ignoring...\n\t\t\t\t}\n\t\t\t\t//settings.put(\"sm-jid\", getComponentId());\n\t\t\t\teventBus.registerAll(proc);\n\t\t\t}\n\t\t\tif (proc instanceof PresenceCapabilitiesManager.PresenceCapabilitiesListener) {\n\t\t\t\tPresenceCapabilitiesManager.registerPresenceHandler(\n\t\t\t\t\t\t(PresenceCapabilitiesManager.PresenceCapabilitiesListener) proc);\n\t\t\t}\n\t\t}\n\n\t\treturn proc;\n\t}\n\n\t@Override\n\tpublic boolean containsJid(BareJID jid) {\n\t\treturn sessionsByNodeId.containsKey(jid);\n\t}\n\n\t@Override\n\tpublic boolean containsJidLocally(BareJID jid) {\n\t\treturn sessionsByNodeId.containsKey(jid);\n\t}\n\n\t@Override\n\tpublic boolean containsJidLocally(JID jid) {\n\t\tXMPPSession session = sessionsByNodeId.get(jid.getBareJID());\n\t\treturn session != null && session.getResourceForJID(jid) != null;\n\t}\n\n\tpublic void handleLocalPacket(Packet packet, XMPPResourceConnection conn) {\n\n\t\t// Do nothing here. Maybe we will attach some handlers later\n\t}\n\n\t@Override\n\tpublic void handleLogin(BareJID userId, XMPPResourceConnection conn) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"handleLogin called for: {0}, conn_id: {1}\", new Object[]{userId, conn});\n\t\t}\n\t\tregisterNewSession(userId, conn);\n\t\tAuthenticationTimer.cancel(conn);\n\t}\n\n\t@Override\n\tpublic void handleLogout(BareJID userId, XMPPResourceConnection conn) {\n\t\tcloseSession(conn, false);\n\t\ttry {\n\t\t\tconnectionsByFrom.remove(conn.getConnectionId());\n\n\t\t\tPacket cmd = Command.CLOSE.getPacket(getComponentId(), conn.getConnectionId(), StanzaType.set,\n\t\t\t\t\t\t\t\t\t\t\t\t conn.nextStanzaId());\n\t\t\tString error = (String) conn.getSessionData(XMPPResourceConnection.ERROR_KEY);\n\n\t\t\tif (error != null) {\n\t\t\t\tElement err_el = new Element(error);\n\n\t\t\t\terr_el.setXMLNS(\"urn:ietf:params:xml:ns:xmpp-streams\");\n\t\t\t\tcmd.getElement().getChild(\"command\").addChild(err_el);\n\t\t\t}\n\n\t\t\tvar errorElement = (Element)conn.getSessionData(XMPPResourceConnection.ERROR_ELEMENT_KEY);\n\t\t\tif (errorElement != null) {\n\t\t\t\tcmd.getElement().getChild(\"command\").addChild(errorElement);\n\t\t\t}\n\n\t\t\tfastAddOutPacket(cmd);\n\t\t} catch (NoConnectionIdException ex) {\n\t\t\tlog.log(Level.WARNING, \"Connection ID not set for session: {0}\", conn);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void handlePresenceSet(XMPPResourceConnection conn) {\n\t\tXMPPSession parentSession = conn.getParentSession();\n\n\t\tif (parentSession == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tElement presence = conn.getPresence();\n\n\t\tthis.processPresenceUpdate(parentSession, presence);\n\t}\n\t\n\t@Override\n\tpublic void handleResourceBind(XMPPResourceConnection conn) {\n\t\tif (!conn.isServerSession() && (!\"USER_STATUS\".equals(conn.getSessionId())) && !conn.isTmpSession()) {\n\t\t\ttry {\n\t\t\t\tPacket user_login_cmd = Command.USER_LOGIN.getPacket(getComponentId(), conn.getConnectionId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t StanzaType.set, conn.nextStanzaId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Command.DataType.submit);\n\n\t\t\t\tCommand.addFieldValue(user_login_cmd, \"user-jid\", conn.getjid().toString());\n\t\t\t\taddOutPacket(user_login_cmd);\n\t\t\t\teventBus.fire(new UserConnectedEvent(conn.getjid()));\n\t\t\t} catch (NoConnectionIdException ex) {\n\n\t\t\t\t// This actually should not happen... might be a bug:\n\t\t\t\tlog.log(Level.WARNING, \"This should not happen, check it out!, \", ex);\n\t\t\t}\n\n\t\t\tcheckSingleUserConnectionsLimit(conn);\n\t\t}\n\t}\n\n\t@HandleEvent\n\tpublic void handleDisconnectUser(DisconnectUserEBAction event) {\n\t\tlog.log(Level.FINEST, \"Handling event to disconnect!: \" + event);\n\t\tlogoutAllUserConnections(event.getUserJid(), event.getError(), event.getMessage());\n\t}\n\n\tprivate void logoutAllUserConnections(BareJID bareJID, StreamError error, String message) {\n\t\tvar errorElement = error != null ? error.prepareStreamErrorElement(message) : null;\n\t\tfinal XMPPSession session = getSession(bareJID);\n\t\tif (session != null) {\n\t\t\tfor (XMPPResourceConnection xmppResourceConnection : session.getActiveResources()) {\n\t\t\t\ttry {\n\t\t\t\t\tlog.log(Level.FINEST, \"Logging out user session: \" + xmppResourceConnection);\n\t\t\t\t\tif (errorElement != null) {\n\t\t\t\t\t\txmppResourceConnection.putSessionData(XMPPResourceConnection.ERROR_ELEMENT_KEY, errorElement);\n\t\t\t\t\t}\n\t\t\t\t\txmppResourceConnection.logout();\n\t\t\t\t} catch (NotAuthorizedException e) {\n\t\t\t\t\tthrow new RuntimeException(e);\n\t\t\t\t}\n\t\t\t\tsession.removeResourceConnection(xmppResourceConnection);\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected void checkSingleUserConnectionsLimit(XMPPResourceConnection conn) {\n\t\tif (getSingleUserConnectionsLimit() != null) {\n\t\t\tXMPPSession session = conn.getParentSession();\n\t\t\tif (session != null) {\n\t\t\t\tint overlimit = session.getActiveResourcesSize() - getSingleUserConnectionsLimit();\n\t\t\t\tif (overlimit > 0) {\n\t\t\t\t\tsession.getActiveResources()\n\t\t\t\t\t\t.stream()\n\t\t\t\t\t\t.sorted(Comparator.comparing(XMPPResourceConnection::getCreationTime))\n\t\t\t\t\t\t.limit(overlimit)\n\t\t\t\t\t\t.forEach(connToStop -> {\n\t\t\t\t\t\t\tvar errorElement = ResourceConstraint.prepareStreamErrorElement(\n\t\t\t\t\t\t\t\t\"Exceeded the limit of allowed connections\");\n\t\t\t\t\t\t\tconnToStop.putSessionData(XMPPResourceConnection.ERROR_ELEMENT_KEY, errorElement);\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tconnToStop.logout();\n\t\t\t\t\t\t\t} catch (NotAuthorizedException ex) {\n\t\t\t\t\t\t\t\t// if it is not authorized, there is nothing we can do, but this should not happen\n\t\t\t\t\t\t\t\tlog.log(Level.CONFIG, \"Exception during closing old connection, ignoring.\", ex);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tsession.removeResourceConnection(connToStop);\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean handlesLocalDomains() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic int hashCodeForPacket(Packet packet) {\n\t\t// moved this check from AbstractMessageReceiver as it is related only to SM\n\t\t// and in other components it causes issues as SM sending packet send packetFrom\n\t\t// to SM address which in fact forced other components to process all packets\n\t\t// from SM on single thread !!\n\t\tif ((packet.getPacketFrom() != null) && !getComponentId().equals(packet.getPacketFrom())) {\n\n\t\t\t// This comes from connection manager so the best way is to get hashcode\n\t\t\t// by the connectionId, which is in the getFrom()\n\t\t\treturn packet.getPacketFrom().hashCode();\n\t\t}\n\n\t\treturn super.hashCodeForPacket(packet);\n\t}\n\n\t@Override\n\tpublic void initBindings(Bindings binds) {\n\t\tsuper.initBindings(binds);\n\t\tbinds.put(CommandIfc.AUTH_REPO, auth_repository);\n\t\tbinds.put(CommandIfc.USER_CONN, connectionsByFrom);\n\t\tbinds.put(CommandIfc.USER_REPO, user_repository);\n\t\tbinds.put(CommandIfc.USER_SESS, sessionsByNodeId);\n\t\tbinds.put(\"kernel\", kernel);\n\t\tbinds.put(\"eventBus\", eventBus);\n\t}\n\n\t@Override\n\tpublic int processingInThreads() {\n\t\treturn Runtime.getRuntime().availableProcessors() * 8;\n\t}\n\n\t@Override\n\tpublic int processingOutThreads() {\n\t\treturn Runtime.getRuntime().availableProcessors() * 8;\n\t}\n\n\t@Override\n\tpublic void processPacket(final Packet packet) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Received packet: {0}\", packet.toStringSecure());\n\t\t}\n\t\tif (packet.isCommand() && processCommand(packet)) {\n\t\t\tpacket.processedBy(\"SessionManager\");\n\n\t\t\t// No more processing is needed for command packet\n\t\t\treturn;\n\t\t}    // end of if (pc.isCommand())\n\n\t\tif (messageArchive != null) {\n\t\t\tmessageArchive.generateStableId(packet);\n\t\t}\n\n\t\tXMPPResourceConnection conn = getXMPPResourceConnection(packet);\n\n\t\tif ((conn == null) && (isBrokenPacket(packet)) || processAdminsOrDomains(packet)) {\n\t\t\treturn;\n\t\t}\n\t\tprocessPacket(packet, conn);\n\t}\n\n\tpublic void removePlugin(XMPPImplIfc proc) {\n\t\tString plug_id = proc.id();\n\t\tremovePlugin(plug_id);\n\t}\n\n\tpublic void removePlugin(String plug_id) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Removing plugin {0}\", plug_id);\n\t\t}\n\n\t\tXMPPImplIfc p = null;\n\t\tProcessingThreads<ProcessorWorkerThread> pt = workerThreads.remove(plug_id);\n\n\t\tif (pt != null) {\n\t\t\tp = processors.remove(plug_id);\n\t\t\tpt.shutdown();\n\t\t\tif (p != null) {\n\t\t\t\tallPlugins.remove(p);\n\t\t\t}\n\t\t}\n\t\tif (preProcessors.get(plug_id) != null) {\n\t\t\tp = preProcessors.remove(plug_id);\n\t\t\tallPlugins.remove(p);\n\t\t}\n\t\tif (postProcessors.get(plug_id) != null) {\n\t\t\tp = postProcessors.remove(plug_id);\n\t\t\tallPlugins.remove(p);\n\t\t}\n\t\tif (stopListeners.get(plug_id) != null) {\n\t\t\tp = stopListeners.remove(plug_id);\n\t\t\tallPlugins.remove(p);\n\t\t}\n\t\tif (p != null) {\n\t\t\teventBus.unregisterAll(p);\n\t\t\tif (p instanceof PresenceCapabilitiesManager.PresenceCapabilitiesListener) {\n\t\t\t\tPresenceCapabilitiesManager.unregisterPresenceHandler(\n\t\t\t\t\t\t(PresenceCapabilitiesManager.PresenceCapabilitiesListener) p);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic boolean skipPrivacy() {\n\t\treturn skipPrivacy;\n\t}\n\n\t@Override\n\tpublic void start() {\n\t\tsuper.start();\n\t\tif (!staleConnectionCloser.isScheduled()) {\n\t\t\taddTimerTask(staleConnectionCloser, staleConnectionCloser.getTimeout());\n\t\t}\n\t\teventBus.registerAll(this);\n\t}\n\n\t@Override\n\tpublic void stop() {\n\t\teventBus.unregisterAll(this);\n\t\tsuper.stop();\n\t\tList<String> pluginsToStop = new ArrayList<String>(workerThreads.keySet());\n\t\tfor (String plugin_id : pluginsToStop) {\n\t\t\ttry {\n\t\t\t\tremovePlugin(plugin_id);\n\t\t\t} catch (Exception ex) {\n\t\t\t\tlog.log(Level.WARNING, \"Exception while stopping plugin\", ex);\n\t\t\t}\n\n\t\t}\n\t}\n\n\t@Override\n\tpublic JID[] getConnectionIdsForJid(BareJID jid) {\n\t\tif (skipPrivacy()) {\n\t\t\tXMPPSession session = sessionsByNodeId.get(jid);\n\n\t\t\tif (session != null) {\n\t\t\t\treturn session.getConnectionIds();\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getDiscoCategoryType() {\n\t\treturn \"sm\";\n\t}\n\n\t@Override\n\tpublic String getDiscoDescription() {\n\t\treturn \"Session manager\";\n\t}\n\n\t@Override\n\tpublic List<Element> getDiscoFeatures(JID from) {\n\t\tList<Element> features = new LinkedList<Element>();\n\t\tList<Element> tmp = super.getDiscoFeatures(from);\n\n\t\tif (tmp != null) {\n\t\t\tfeatures.addAll(tmp);\n\t\t}\n\t\tfor (XMPPProcessorIfc proc_t : processors.values()) {\n\t\t\tElement[] discoFeatures = proc_t.supDiscoFeatures(null);\n\n\t\t\tif (discoFeatures != null) {\n\t\t\t\tfeatures.addAll(Arrays.asList(discoFeatures));\n\t\t\t}    // end of if (discoFeatures != null)\n\t\t}\n\n\t\treturn features;\n\t}\n\n\t@Override\n\tpublic Element getDiscoInfo(String node, JID jid, JID from) {\n\t\tif ((jid != null) && (getName().equals(jid.getLocalpart()) || isLocalDomain(jid.toString()))) {\n\t\t\tElement query = super.getDiscoInfo(node, jid, from);\n\n\t\t\tif (query == null) {\n\t\t\t\tquery = new Element(\"query\");\n\t\t\t\tquery.setXMLNS(XMPPService.INFO_XMLNS);\n\t\t\t}\n\t\t\tif (node == null) {\n\t\t\t\tfor (XMPPProcessorIfc proc_t : processors.values()) {\n\t\t\t\t\tElement[] discoFeatures = proc_t.supDiscoFeatures(null);\n\n\t\t\t\t\tif (discoFeatures != null) {\n\t\t\t\t\t\tquery.addChildren(Arrays.asList(discoFeatures));\n\t\t\t\t\t}    // end of if (discoFeatures != null)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Found disco info: {0}\", ((query != null) ? query.toString() : null));\n\t\t\t}\n\n\t\t\treturn query;\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Not found disco info for node: {0}, jid: {1}\", new Object[]{node, jid});\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic List<Element> getDiscoItems(String node, JID jid, JID from) {\n\t\tif (\"http://jabber.org/protocol/commands\".equals(node)) {\n\t\t\treturn adHocCommandModule.getScriptItems(node, jid, from);\n\t\t} else {\n\t\t\treturn super.getDiscoItems(node, jid, from);\n\t\t}\n\t}\n\n\tpublic XMPPResourceConnection getResourceConnection(JID jid) {\n\t\tXMPPSession session = getSession(jid.getBareJID());\n\n\t\tif (session != null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Session not null, searching session for jid: {0}\", jid);\n\t\t\t}\n\n\t\t\tXMPPResourceConnection res = session.getResourceConnection(jid);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Found session: {0}, for jid: {1}\", new Object[]{res, jid});\n\t\t\t}\n\n\t\t\treturn res;\n\t\t}    // end of if (session != null)\n\n\t\t// Maybe this is a call for the server session?\n\t\tif (isLocalDomain(jid.toString(), false)) {\n\t\t\treturn smResourceConnection;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic int getOpenUsersConnectionsAmount() {\n\t\treturn connectionsByFrom.size();\n\t}\n\n\tpublic Integer getSingleUserConnectionsLimit() {\n\t\treturn singleUserConnectionsLimit;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void getStatistics(StatisticsList list) {\n\t\tsuper.getStatistics(list);\n\t\tif (list.checkLevel(Level.FINEST)) {\n\t\t\tlist.add(getName(), \"Registered accounts\", user_repository.getUsersCount(), Level.FINEST);\n\t\t}\n\t\tlist.add(getName(), \"Open user connections\", connectionsByFrom.size(), Level.INFO);\n\t\tlist.add(getName(), \"Maximum user connections\", maxUserConnections, Level.INFO);\n\t\tlist.add(getName(), \"Total user connections\", totalUserConnections, Level.FINER);\n\t\tlist.add(getName(), \"Closed user connections\", closedConnections, Level.FINER);\n\t\tlist.add(getName(), \"Open user sessions\", sessionsByNodeId.size(), Level.INFO);\n\t\tlist.add(getName(), \"Maximum user sessions\", maxUserSessions, Level.FINE);\n\t\tlist.add(getName(), \"Total user sessions\", totalUserSessions, Level.FINER);\n\t\tlist.add(getName(), \"Active user connections\", activeUserNumber, Level.FINER);\n\t\tlist.add(getName(), \"Authentication timouts\", authTimeouts, Level.INFO);\n\t\tif (list.checkLevel(Level.INFO)) {\n\t\t\tint totalQueuesWait = list.getValue(getName(), \"Total queues wait\", 0);\n\t\t\tlong totalQueuesOverflow = list.getValue(getName(), \"Total queues overflow\", 0l);\n\n\t\t\tfor (Map.Entry<String, ProcessingThreads<ProcessorWorkerThread>> procent : workerThreads.entrySet()) {\n\t\t\t\tProcessingThreads<ProcessorWorkerThread> proc = procent.getValue();\n\n\t\t\t\ttotalQueuesWait += proc.getTotalQueueSize();\n\t\t\t\ttotalQueuesOverflow += proc.getDroppedPackets();\n\t\t\t\tif (list.checkLevel(Level.INFO, proc.getTotalQueueSize() + proc.getDroppedPackets())) {\n\t\t\t\t\tlist.add(getName(), \"Processor: \" + procent.getKey(),\n\t\t\t\t\t\t\t \", Queue: \" + proc.getTotalQueueSize() + \", AvTime: \" + proc.getAverageProcessingTime() +\n\t\t\t\t\t\t\t\t\t \", Runs: \" + proc.getTotalRuns() + \", Lost: \" + proc.getDroppedPackets(),\n\t\t\t\t\t\t\t Level.INFO);\n\t\t\t\t}\n\t\t\t}\n\t\t\tlist.add(getName(), \"Total queues wait\", totalQueuesWait, Level.INFO);\n\t\t\tlist.add(getName(), \"Total queues overflow\", totalQueuesOverflow, Level.INFO);\n\t\t}\n\t\tif (list.checkLevel(Level.FINE)) {\n\t\t\tfor (Map.Entry tmEntry : postTimes.entrySet()) {\n\n\t\t\t\t// This line is only temporary because JIndent cannot parse it inside the above\n\t\t\t\t// for statement\n\t\t\t\tMap.Entry<String, long[]> entry = tmEntry;\n\n\t\t\t\tlist.add(getName(),\n\t\t\t\t\t\t \"Average \" + tmEntry.getKey() + \" on last \" + entry.getValue().length + \" runs [ms]\",\n\t\t\t\t\t\t calcAverage(entry.getValue()), Level.FINE);\n\t\t\t}\n\t\t}\n\t\tlist.add(getName(), \"Maximum user sessions today\", maxUserSessionsDaily, Level.INFO);\n\t\tlist.add(getName(), \"Maximum user sessions yesterday\", maxUserSessionsYesterday, Level.INFO);\n\n\t\tlist.add(getName(), \"Max daily users sessions count last month\", maxDailyUsersSessions, Level.INFO);\n\t\tlist.add(getName(), \"Max users sessions within last week\", maxDailyUsersConnectionsWithinLastWeek, Level.INFO);\n\n\t\tif (list.checkLevel(Level.FINE)) {\n\t\t\tif (activeUsersLastDay == null || activeUsersLastWeek == null || activeUsersLast30Days == null) {\n\t\t\t\tupdateActiveUsersStatistics();\n\t\t\t}\n\t\t\tif (activeUsersLastDay != null) {\n\t\t\t\tlist.add(getName(), \"Active users within last 24h\", activeUsersLastDay, Level.FINE);\n\t\t\t}\n\t\t\tif (activeUsersLastWeek != null) {\n\t\t\t\tlist.add(getName(), \"Active users within last 7 days\", activeUsersLastWeek, Level.FINE);\n\t\t\t}\n\t\t\tif (activeUsersLast30Days != null) {\n\t\t\t\tlist.add(getName(), \"Active users within last 30 days\", activeUsersLast30Days, Level.FINE);\n\t\t\t}\n\t\t}\n\n\t\tfor (XMPPImplIfc plugin : allPlugins) {\n\t\t\tplugin.getStatistics(list);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean hasCompleteJidsInfo() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic boolean isLocalDomain(String domain, boolean includeComponents) {\n\t\tif (includeComponents) {\n\t\t\treturn isLocalDomainOrComponent(domain);\n\t\t} else {\n\t\t\treturn isLocalDomain(domain);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\tsuper.setName(name);\n\t\tTigaseRuntime.getTigaseRuntime().addOnlineJidsReporter(this);\n\t}\n\n\tpublic void setAllPlugins(ConcurrentSkipListSet<XMPPImplIfc> allPlugins) {\n\t\tConcurrentSkipListSet<XMPPImplIfc> oldPlugins = this.allPlugins;\n\t\tHashSet<XMPPImplIfc> removed = new HashSet<>(oldPlugins);\n\t\tremoved.removeAll(allPlugins);\n\t\tfor (XMPPImplIfc proc : removed) {\n\t\t\tremovePlugin(proc);\n\t\t}\n\t\tHashSet<XMPPImplIfc> added = new HashSet<>(allPlugins);\n\t\tadded.removeAll(oldPlugins);\n\t\tfor (XMPPImplIfc proc : added) {\n\t\t\ttry {\n\t\t\t\taddPlugin(proc);\n\t\t\t} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | TigaseDBException e) {\n\t\t\t\tlog.log(Level.SEVERE, \"Failed initialization of processor \" + proc.id(), e);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void setSmThreadsPool(String val) {\n\t\tthis.smThreadsPool = val;\n\t\tif (!SM_THREADS_POOL_PROP_VAL.equals(val)) {\n\t\t\tString[] threads_pool_params = val.split(\":\");\n\t\t\tint size = 100;\n\t\t\tif (threads_pool_params.length > 1) {\n\t\t\t\ttry {\n\t\t\t\t\tsize = Integer.parseInt(threads_pool_params[1]);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Incorrect threads pool size: {0}, setting default to 100\",\n\t\t\t\t\t\t\tthreads_pool_params[1]);\n\t\t\t\t\tsize = 100;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tProcessorWorkerThread worker = new ProcessorWorkerThread();\n\t\t\t\tProcessingThreads<ProcessorWorkerThread> pt = new ProcessingThreads<>(worker, size, maxQueueSize,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  defPluginsThreadsPool);\n\t\t\t\tworkerThreads.put(defPluginsThreadsPool, pt);\n\t\t\t\tif (isInitializationComplete()) {\n\t\t\t\t\tlog.log(Level.CONFIG, \"Created a default thread pool: {0}\", size);\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.SEVERE, \"\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tConcurrentHashMap<String, CopyOnWriteArraySet<CmdAcl>> commandACL = new ConcurrentHashMap<>(getCommandACL());\n\t\tfor (AdHocCommand command : adHocCommandModule.getCommandsManager().getAllCommands()) {\n\t\t\tif (!commandACL.containsKey(command.getNode())) {\n\t\t\t\tif (command.getDefaultACL() != null) {\n\t\t\t\t\tcommandACL.put(command.getNode(), new CopyOnWriteArraySet<>(Set.of(command.getDefaultACL())));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tsetCommandsACL(commandACL);\n\n\t\tsuper.initialize();\n\n\t\tsmResourceConnection = new SMResourceConnection(null, user_repository, auth_repository, this);\n\t\tregisterNewSession(getComponentId().getBareJID(), smResourceConnection);\n\t}\n\n\t@Override\n\tpublic void setSchedulerThreads_size(int size) {\n\t\tsuper.setSchedulerThreads_size(size);\n\t\tif (!staleConnectionCloser.isScheduled()) {\n\t\t\taddTimerTask(staleConnectionCloser, staleConnectionCloser.getTimeout());\n\t\t}\n\t}\n\n\t@Override\n\tpublic int schedulerThreads() {\n\t\treturn 2;\n\t}\n\n\tpublic void register(Kernel kernel) {\n\t\tthis.kernel = kernel;\n\t\tkernel.registerBean(\"writer\").asClass(SMPacketWriter.class).exportable().exec();\n\t\tkernel.registerBean(\"adHocCommandManager\").asClass(AdHocCommandManager.class).exec();\n\t\tkernel.registerBean(\"scriptCommandProcessor\").asClass(ComponenScriptCommandProcessor.class).exec();\n\t\tkernel.registerBean(\"adHocCommandModule\").asClass(AdHocCommandModule.class).exec();\n\t}\n\n\tpublic void unregister(Kernel kernel) {\n\t\tthis.kernel = null;\n\t}\n\n\tpublic Map<String, XMPPProcessorIfc> getProcessors() {\n\t\treturn Collections.unmodifiableMap(processors);\n\t}\n\n\tpublic Map<String, XMPPPreprocessorIfc> getPreProcessors() {\n\t\treturn Collections.unmodifiableMap(preProcessors);\n\t}\n\n\tpublic Map<String, XMPPPostprocessorIfc> getPostProcessors() {\n\t\treturn Collections.unmodifiableMap(postProcessors);\n\t}\n\n\tpublic Map<String, XMPPPacketFilterIfc> getOutFilters() {\n\t\treturn Collections.unmodifiableMap(outFilters);\n\t}\n\n\t@Override\n\tpublic synchronized void everySecond() {\n\t\tsuper.everySecond();\n\t}\n\n\t@Override\n\tpublic synchronized void everyMinute() {\n\t\tsuper.everyMinute();\n\t\tcalculateActiveUsers();\n\n\t\tfinal Calendar now = Calendar.getInstance();\n\t\tif (now.get(Calendar.YEAR) != lastDailyStatsReset.get(Calendar.YEAR) ||\n\t\t\t\tnow.get(Calendar.DAY_OF_YEAR) != lastDailyStatsReset.get(Calendar.DAY_OF_YEAR)) {\n\t\t\tlastDailyStatsReset = Calendar.getInstance();\n\n\t\t\tmaxUserSessionsYesterday = maxUserSessionsDaily;\n\t\t\tmaxUserSessionsDaily = sessionsByNodeId.size();\n\t\t}\n\n\t\tmaxDailyUsersSessions.add(maxUserSessionsDaily - 1);\n\t\tmaxDailyUsersConnectionsWithinLastWeek = maxDailyUsersSessions.getMaxValueInRange(7).orElse(-1);\n\t}\n\n\t@Override\n\tpublic synchronized void everyHour() {\n\t\tsuper.everyHour();\n\t\tupdateActiveUsersStatistics();\n\t}\n\n\tprivate void updateActiveUsersStatistics() {\n\t\tactiveUsersLastDay = auth_repository.getActiveUsersCountIn(Duration.ofDays(1));\n\t\tactiveUsersLastWeek = auth_repository.getActiveUsersCountIn(Duration.ofDays(7));\n\t\tactiveUsersLast30Days = auth_repository.getActiveUsersCountIn(Duration.ofDays(30));\n\t}\n\n\t@Override\n\tpublic void handleDomainChange(final String domain, final XMPPResourceConnection conn) {\n\t\ttry {\n\t\t\tVHostItem vHostItem = getVHostItem(domain);\n\t\t\tif (vHostItem == null) {\n\t\t\t\tif (log.isLoggable(Level.CONFIG)) {\n\t\t\t\t\tlog.log(Level.CONFIG, \"Can''t get VHostItem for domain: {0}, using default one instead: {1}\",\n\t\t\t\t\t\t\tnew Object[]{domain, getDefHostName()});\n\t\t\t\t}\n\t\t\t\tvHostItem = new VHostItemImpl(getDefHostName().getDomain());\n\t\t\t}\n\t\t\tconn.setDomain(vHostItem);//.getUnmodifiableVHostItem());\n\t\t} catch (TigaseStringprepException ex) {\n\t\t\tlog.log(Level.CONFIG, \"Stringprep problem for resource connection: {0}\", conn);\n\t\t\t// handleLogout(userId, conn);\n\t\t}\n\t}\n\n\tprotected void addOutPackets(Packet packet, XMPPResourceConnection conn, Queue<Packet> results) {\n\t\tfor (XMPPPacketFilterIfc outfilter : outFilters.values()) {\n\t\t\toutfilter.filter(packet, conn, naUserRepository, results);\n\t\t}    // end of for (XMPPPostprocessorIfc postproc: postProcessors)\n\t\taddOutPackets(results);\n\t}\n\n\tprotected boolean addTrusted(JID jid) {\n\t\treturn trusted.add(jid.getBareJID().toString());\n\t}\n\n\tprotected void closeConnection(XMPPResourceConnection connection, JID connectionId, String userId,\n\t\t\t\t\t\t\t\t   boolean closeOnly) {\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.log(Level.FINER, \"Stream closed from: {0}\", connectionId);\n\t\t}\n\n\t\t// for test let's assume connection is not found\n\t\tif (connection == null) {\n\t\t\tconnection = connectionsByFrom.remove(connectionId);\n\t\t}\n\n\t\tif (connection != null) {\n\n\t\t\t// Make sure no other stuff happen on the connection while it is being\n\t\t\t// closed. The best example is handleLogin, it happens they are called\n\t\t\t// concurrently and this is where things go wrong....\n\t\t\tsynchronized (connection) {\n\t\t\t\tconnection.putSessionData(XMPPResourceConnection.CLOSING_KEY, XMPPResourceConnection.CLOSING_KEY);\n\t\t\t\tcloseSession(connection, closeOnly);\n\t\t\t}\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"Can not find resource connection for connectionId: {0}\", connectionId);\n\t\t\t}\n\t\t\tif (userId != null) {\n\n\t\t\t\t// check using userId if we can find stale XMPPResourceConnection\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Found trying to find stale XMPPResourceConnection by userId {0}...\", userId);\n\t\t\t\t}\n\n\t\t\t\tJID userJid = JID.jidInstanceNS(userId);\n\t\t\t\tXMPPSession sessionByUserId = getSession(userJid.getBareJID());\n\n\t\t\t\tif (sessionByUserId != null) {\n\t\t\t\t\tconnection = sessionByUserId.getResourceForConnectionId(connectionId);\n\t\t\t\t\tif (connection != null) {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Found stale XMPPResourceConnection {0} by userId {1}, removing...\",\n\t\t\t\t\t\t\t\t\tnew Object[]{connection, userId});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsessionByUserId.removeResourceConnection(connection);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Maybe we should move this loop based check to separe thread for performance reason\n\t\t\t// Check if our Set<JID> of not found sessions contains each of available connections from each session\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE,\n\t\t\t\t\t\t\"queuing connection {0} for user {1} for detail stale connection check - should not happen!!\",\n\t\t\t\t\t\tnew Object[]{connectionId, userId});\n\t\t\t}\n\t\t\tif (!forceDetailStaleConnectionCheck) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Let's make sure there is no stale XMPPResourceConnection in some\n\t\t\t// XMPPSession\n\t\t\t// object which may cause problems and packets sent to nowhere.\n\t\t\t// This might an expensive operation though.... add item to queue\n\t\t\t// executed in other thread\n\t\t\tstaleConnectionCloser.queueForClose(connectionId);\n\n\t\t\t// code below is original loop for finding stale XMPPResourceConnections\n//    log.log(Level.CONFIG, \"Trying to find and remove stale XMPPResourceConnection: {0}\",\n//        connectionId);\n//\n//    for (XMPPSession session : sessionsByNodeId.values()) {\n//      connection = session.getResourceForConnectionId(connectionId);\n//      if (connection != null) {\n//        log.log(Level.WARNING, \"Found stale XMPPResourceConnection: {0}, removing...\",\n//            connection);\n//        session.removeResourceConnection(connection);\n//\n//        break;\n//      }\n//    }\n\t\t}    // end of if (conn != null) else\n\t}\n\n\tprotected void closeSession(XMPPResourceConnection conn, boolean closeOnly) {\n\t\tif (!closeOnly) {\n\t\t\tQueue<Packet> results = new ArrayDeque<Packet>(50);\n\n\t\t\tfor (XMPPStopListenerIfc stopProc : stopListeners.values()) {\n\t\t\t\tstopProc.stopped(conn, results, plugin_config.get(stopProc.id()));\n\t\t\t}    // end of for ()\n\t\t\taddOutPackets(null, conn, results);\n\t\t}\n\t\ttry {\n\t\t\tif (conn.isAuthorized()) {\n\t\t\t\tJID userJid = conn.getJID();\n\n\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\tlog.log(Level.FINE, \"Closing connection for: {0}, conn: {1}\", new Object[]{userJid, conn});\n\t\t\t\t}\n\n\t\t\t\tXMPPSession sessionParent = conn.getParentSession();\n\n\t\t\t\tif (sessionParent != null) {\n\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\tlog.log(Level.FINE, \"Found parent session for: {0}\", userJid);\n\t\t\t\t\t}\n\t\t\t\t\t// as we are closing this connection we should ensure that it is removed\n\t\t\t\t\t// from list of active resources before going any further\n\t\t\t\t\tsessionParent.removeResourceConnection(conn);\n\t\t\t\t\tif (sessionParent.getActiveResourcesSize() <= 1) {\n\n\t\t\t\t\t\t// we should check if other this is the only connection on session\n\t\t\t\t\t\t// as in some cases connection can be already removed from active\n\t\t\t\t\t\t// resources but there might be other connection which is active\n\t\t\t\t\t\tif ((sessionParent.getActiveResourcesSize() > 0) &&\n\t\t\t\t\t\t\t\t!sessionParent.getActiveResources().contains(conn)) {\n\t\t\t\t\t\t\tlog.log(Level.FINE, \"Session contains connection for other \" +\n\t\t\t\t\t\t\t\t\t\"resource than: {0}, not removing session\", userJid);\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\t\t\tStringBuilder sb = new StringBuilder(100);\n\n\t\t\t\t\t\t\t\tfor (XMPPResourceConnection res_con : sessionParent.getActiveResources()) {\n\t\t\t\t\t\t\t\t\tsb.append(\", res=\").append(res_con.getResource());\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tlog.log(Level.FINER, \"Number of connections is {0} for the user: {1}{2}\",\n\t\t\t\t\t\t\t\t\t\tnew Object[]{sessionParent.getActiveResourcesSize(), userJid.getBareJID(), sb.toString()});\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tXMPPSession sessionFromMap = getSession(userJid.getBareJID());\n\n\t\t\t\t\t\tif (sessionParent.equals(sessionFromMap) && sessionFromMap.getActiveResources().isEmpty()) {\n\t\t\t\t\t\t\tsessionParent = sessionsByNodeId.remove(userJid.getBareJID());\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (sessionParent == null) {\n\t\t\t\t\t\t\tlog.log(Level.CONFIG, \"UPS can''t remove, session not found in map: {0}\", userJid);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINER, \"Number of user sessions: {0}\", sessionsByNodeId.size());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}    // end of else\n\t\t\t\t\t\tauth_repository.logout(userJid.getBareJID());\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\t\tStringBuilder sb = new StringBuilder(100);\n\n\t\t\t\t\t\t\tfor (XMPPResourceConnection res_con : sessionParent.getActiveResources()) {\n\t\t\t\t\t\t\t\tsb.append(\", res=\").append(res_con.getResource());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tlog.log(Level.FINER, \"Number of connections is {0} for the user: {1}{2}\",\n\t\t\t\t\t\t\t\t\tnew Object[]{sessionParent.getActiveResourcesSize(), userJid, sb.toString()});\n\t\t\t\t\t\t}\n\t\t\t\t\t}    // end of else\n\t\t\t\t}      // end of if (session.getActiveResourcesSize() == 0)\n\t\t\t} else {\n\t\t\t\tAuthenticationTimer.cancel(conn);\n\t\t\t}\n\t\t} catch (NotAuthorizedException e) {\n\t\t\tlog.log(Level.CONFIG, \"Closed not authorized session: {0}\", e);\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Exception closing session... \", e);\n\t\t}\n\t\t++closedConnections;\n\t\tconn.streamClosed();\n\t}\n\n\tprotected XMPPResourceConnection createUserSession(JID conn_id, String domain) throws TigaseStringprepException {\n\t\tXMPPResourceConnection connection = new XMPPResourceConnection(conn_id, user_repository, auth_repository, this);\n\t\tVHostItem vitem = null;\n\n\t\tif (domain != null) {\n\t\t\tvitem = getVHostItem(domain);\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Setting hostname {0} for connection: {1}, VHostItem: {2}\",\n\t\t\t\t\t\tnew Object[]{domain, conn_id, vitem});\n\t\t\t}\n\t\t}\n\t\tif (vitem == null) {\n\n\t\t\t// This shouldn't generally happen. Must mean misconfiguration.\n\t\t\tif (log.isLoggable(Level.CONFIG)) {\n\t\t\t\tlog.log(Level.CONFIG, \"Can''t get VHostItem for domain: {0}, using default one instead: {1}\",\n\t\t\t\t\t\tnew Object[]{domain, getDefHostName()});\n\t\t\t}\n\t\t\tvitem = new VHostItemImpl(getDefHostName().getDomain());\n\t\t}\n\t\tconnection.setDomain(vitem);//.getUnmodifiableVHostItem());\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Domain set for connectionId {0}\", conn_id);\n\t\t}\n\t\tconnectionsByFrom.put(conn_id, connection);\n\n\t\tint currSize = connectionsByFrom.size();\n\n\t\tif (currSize > maxUserConnections) {\n\t\t\tmaxUserConnections = currSize;\n\t\t}\n\t\t++totalUserConnections;\n\n\t\treturn connection;\n\t}\n\n\tprotected boolean delTrusted(JID jid) {\n\t\treturn trusted.remove(jid.getBareJID().toString());\n\t}\n\n\tprotected boolean fastAddOutPacket(Packet packet) {\n\t\treturn addOutPacket(packet);\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\tpublic XMPPResourceConnection loginUserSession(JID conn_id, String domain, BareJID user_id, String resource,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  String xmpp_sessionId, boolean tmpSession) {\n\t\ttry {\n\t\t\tXMPPResourceConnection conn = createUserSession(conn_id, domain);\n\n\t\t\tconn.setTmpSession(tmpSession);\n\t\t\tconn.setSessionId(xmpp_sessionId);\n\n\t\t\t// user_repository.setData(user_id, \"tokens\", xmpp_sessionId, conn_id.toString());\n\t\t\t// Authorization auth = conn.loginToken(user_id, xmpp_sessionId, conn_id.toString());\n\t\t\tconn.authorizeJID(user_id, false);\n\t\t\tif (conn.isAuthorized()) {\n\t\t\t\thandleLogin(user_id, conn);\n\t\t\t\tif (resource == null) {\n\t\t\t\t\tresource = UUID.randomUUID().toString();\n\t\t\t\t}\n\t\t\t\tconn.setResource(resource);\n\t\t\t} else {\n\t\t\t\tconnectionsByFrom.remove(conn_id);\n\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn conn;\n\t\t} catch (TigaseStringprepException | NotAuthorizedException ex) {\n\t\t\tlog.log(Level.WARNING, \"Problem logging user: \" + user_id + \"/\" + resource, ex);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprotected boolean processAdminsOrDomains(Packet packet) {\n\t\tif ((packet.getStanzaFrom() == null) && (packet.getPacketFrom() != null)) {\n\n\t\t\t// The packet, probably did not went through the first state of processing\n\t\t\t// yet.\n\t\t\treturn false;\n\t\t}\n\n\t\tJID to = packet.getStanzaTo();\n\n\t\tif ((to != null) && isLocalDomain(to.getBareJID().toString())) {\n\t\t\tif (packet.getElemName() == \"message\") {\n\n\t\t\t\t// Yes this packet is for admin....\n\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\tlog.log(Level.FINER, \"Packet for admin: {0}\", packet);\n\t\t\t\t}\n\t\t\t\tsendToAdmins(packet);\n\t\t\t\tpacket.processedBy(\"admins-or-domains\");\n\n\t\t\t\treturn true;\n\t\t\t} else if (packet.getElemName() == \"iq\" && packet.getType() == StanzaType.result) {\n\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\tlog.log(Level.FINER,\n\t\t\t\t\t\t\t\"IQ result packet addressed directly to server and not handle by any plugin: {0}\", packet);\n\t\t\t\t}\n\t\t\t\tpacket.processedBy(\"iq-result-to-server\");\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\tlog.log(Level.FINER, \"Packet for hostname, should be handled elsewhere: {0}\", packet);\n\t\t\t\t}\n\t\t\t}\n\t\t}    // end of if (isInRoutings(to))\n\n\t\treturn false;\n\t}\n\n\tprotected boolean processCommand(Packet pc) {\n\t\tif ((pc.getStanzaTo() == null) || !(getComponentId().equals(pc.getStanzaTo()) ||\n\t\t\t\t(isLocalDomain(pc.getStanzaTo().getDomain()) && (pc.getStanzaTo().getLocalpart() == null ||\n\t\t\t\t\t\tgetName().equals(pc.getStanzaTo().getLocalpart()))))) {\n\t\t\treturn false;\n\t\t}\n\n\t\tIq iqc = (Iq) pc;\n\t\tboolean processing_result = false;\n\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.log(Level.FINER, \"{0} command from: {1}\", new Object[]{iqc.getCommand().toString(), iqc.getFrom()});\n\t\t}\n\n\t\tXMPPResourceConnection connection = connectionsByFrom.get(iqc.getFrom());\n\n\t\tswitch (iqc.getCommand()) {\n\t\t\tcase CLOSE: {\n\t\t\t\tlog.log(Level.WARNING, \"Unexpected packet: {0}\", pc);\n\t\t\t\tprocessing_result = true;\n\t\t\t}\n\n\t\t\tbreak;\n\n\t\t\tcase STREAM_OPENED: {\n\n\t\t\t\t// Response is sent from the thread when opening user session is\n\t\t\t\t// completed.\n\t\t\t\t// fastAddOutPacket(pc.okResult((String) null, 0));\n\t\t\t\tProcessingThreads<ProcessorWorkerThread> pt = workerThreads.get(sessionOpenProc.id());\n\n\t\t\t\tif (pt == null) {\n\t\t\t\t\tpt = workerThreads.get(defPluginsThreadsPool);\n\t\t\t\t}\n\t\t\t\tpt.addItem(sessionOpenProc, iqc, connection);\n\t\t\t\tprocessing_result = true;\n\t\t\t}\n\n\t\t\tbreak;\n\n\t\t\tcase GETFEATURES: {\n\t\t\t\tif (iqc.getType() == StanzaType.get) {\n\n\t\t\t\t\tboolean ssl = iqc.getStanzaId().startsWith(\"ssl_\");\n\n\t\t\t\t\tconnection = connectionsByFrom.get(iqc.getStanzaFrom());\n\t\t\t\t\tif (connection != null && ssl) {\n\t\t\t\t\t\tconnection.putSessionData(\"SSL\", ssl);\n\t\t\t\t\t}\n\n\t\t\t\t\tList<Element> features = getFeatures(connection);\n\t\t\t\t\tPacket result = iqc.commandResult(null);\n\n\t\t\t\t\tCommand.setData(result, features);\n\t\t\t\t\taddOutPacket(result);\n\t\t\t\t}    // end of if (pc.getType() == StanzaType.get)\n\t\t\t\tprocessing_result = true;\n\t\t\t}\n\n\t\t\tbreak;\n\n\t\t\tcase STREAM_CLOSED: {\n\t\t\t\tfastAddOutPacket(iqc.okResult((String) null, 0));\n\n\t\t\t\t//try {\n//\t\t\tProcessingThreads<ProcessorWorkerThread> pt = workerThreads.get(sessionCloseProc\n//\t\t\t\t\t.id());\n//\n//\t\t\tif (pt == null) {\n//\t\t\t\tpt = workerThreads.get(defPluginsThreadsPool);\n//\t\t\t}\n//\t\t\tpt.addItem(sessionCloseProc, iqc, connection);\n\t\t\t\t// Replaced code above with new code below to execute STREAM_CLOSE in same\n\t\t\t\t// thread as other packets from connection so next packets will know there\n\t\t\t\t// is no session available after STREAM_CLOSE\n\t\t\t\t// This should not have bigger impact on performance as SessionCloseProc was\n\t\t\t\t// reimplemented to speed up process of closing connections (using maps instead\n\t\t\t\t// of list, etc.)\n\t\t\t\t// Updated by Artur: It does have a big impact. In most cases a DB is accessed during the\n\t\t\t\t// session close processing. If this is done in the main SM thread everything is slowed down.\n\t\t\t\t// If we work under load of 100 logins/logouts per second this bring down the whole system.\n\t\t\t\t// see below!!\n\t\t\t\t//sessionCloseProc.process(iqc, connection, naUserRepository, packetWriterQueue, plugin_config.get(sessionCloseProc.id()));\n\t\t\t\t//\t\t\t} catch (XMPPException ex) {\n\t\t\t\t//\t\t\t\tlog.log(Level.WARNING, \"Exception while processing STREAM_CLOSE command\", ex);\n\t\t\t\t//\t\t\t}\n\t\t\t\t// closeConnection(pc.getFrom(), false);\n\t\t\t\t//\n\t\t\t\t// we need to use other aproach then, as we need to remove session ASAP,\n\t\t\t\t// so let's at first remove XMPPResourceConnection in this thread and later add packet to\n\t\t\t\t// queue to close it later on\n\t\t\t\tif (connection != null) {\n\t\t\t\t\tif (!connection.isAuthorized()) {\n\t\t\t\t\t\t// first remove connection from connections map\n\t\t\t\t\t\t// only if connection is not authorized as in other case\n\t\t\t\t\t\t// it will be removed on end of stream in STREAM_FINISHED\n\t\t\t\t\t\tconnectionsByFrom.remove(iqc.getFrom(), connection);\n\t\t\t\t\t}\n\n\t\t\t\t\t// ok, now remove connection from session\n\t\t\t\t\tXMPPSession session = connection.getParentSession();\n\t\t\t\t\tif (session != null) {\n\t\t\t\t\t\tsession.removeResourceConnection(connection);\n\t\t\t\t\t\t// now set parent session to let processors properly close XMPPResourceConnection\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconnection.setParentSession(session);\n\t\t\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\t\t\tlog.log(Level.FINE, \"this should not happen as JID was already created once\", ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (connection == null || !connection.isAuthorized()) {\n\t\t\t\t\t// now we add packet to processing thread to let it close properly in separate thread\n\t\t\t\t\tProcessingThreads<ProcessorWorkerThread> pt = workerThreads.get(sessionCloseProc.id());\n\n\t\t\t\t\tif (pt == null) {\n\t\t\t\t\t\tpt = workerThreads.get(defPluginsThreadsPool);\n\t\t\t\t\t}\n\t\t\t\t\tpt.addItem(sessionCloseProc, iqc, connection);\n\t\t\t\t} else {\n\t\t\t\t\tTimerTask task = new SessionCloseTimer(iqc.getFrom(), connection.getSessionId());\n\t\t\t\t\taddTimerTask(task, 10, TimeUnit.SECONDS);\n\t\t\t\t\tconnection.putSessionData(XMPPResourceConnection.CLOSING_KEY, XMPPResourceConnection.CLOSING_KEY);\n\t\t\t\t\tconnection.putSessionData(SESSION_CLOSE_TIMER_KEY, task);\n\t\t\t\t}\n\t\t\t\tprocessing_result = true;\n\t\t\t}\n\n\t\t\tbreak;\n\n\t\t\tcase STREAM_CLOSED_UPDATE: {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"{0} processing comment, connection: {1}\",\n\t\t\t\t\t\t\tnew Object[]{iqc.getCommand(), ((connection != null) ? connection : \" is null\")});\n\t\t\t\t}\n\n\t\t\t\t// Note! We don't send response to this packet....\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"{0} adding to the processor: {1}\",\n\t\t\t\t\t\t\tnew Object[]{iqc.getCommand(), ((connection != null) ? connection : \" is null\")});\n\t\t\t\t}\n\t\t\t\tif (connection == null) {\n\n\t\t\t\t\t// Hm, the user connection does not exist here but\n\t\t\t\t\t// the XMPPSession thinks it still does, a quick fix should\n\t\t\t\t\t// be enough.\n\t\t\t\t\t// TODO: investigate why this happens at all, an exception\n\t\t\t\t\t// during connection close processing????\n\t\t\t\t\tJID stanzaFrom = iqc.getStanzaFrom();\n\n\t\t\t\t\tif (stanzaFrom == null) {\n\n\t\t\t\t\t\t// This is wrong\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Stream close update without an user JID: {0}\", iqc);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tXMPPSession xs = sessionsByNodeId.get(stanzaFrom.getBareJID());\n\n\t\t\t\t\t\tif (xs == null) {\n\t\t\t\t\t\t\tlog.log(Level.CONFIG, \"Stream close for the user session which does not exist: {0}\", iqc);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tXMPPResourceConnection xcr = xs.getResourceForConnectionId(iqc.getPacketFrom());\n\n\t\t\t\t\t\t\tif (xcr == null) {\n\t\t\t\t\t\t\t\tlog.log(Level.CONFIG, \"Stream close for the resource connection which does not exist\",\n\t\t\t\t\t\t\t\t\t\tiqc);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\txs.removeResourceConnection(xcr);\n\t\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"{0} removed resource connection: {1}\",\n\t\t\t\t\t\t\t\t\t\t\tnew Object[]{iqc.getCommand(), xcr});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tProcessingThreads<ProcessorWorkerThread> pt = workerThreads.get(sessionCloseProc.id());\n\n\t\t\t\t\tif (pt == null) {\n\t\t\t\t\t\tpt = workerThreads.get(defPluginsThreadsPool);\n\t\t\t\t\t}\n\t\t\t\t\tpt.addItem(sessionCloseProc, iqc, connection);\n\t\t\t\t}\n\n\t\t\t\t// closeConnection(pc.getFrom(), false);\n\t\t\t\tprocessing_result = true;\n\t\t\t}\n\n\t\t\tbreak;\n\n\t\t\tcase STREAM_FINISHED:\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"{0} processing command, connection: {1}\",\n\t\t\t\t\t\t\tnew Object[]{iqc.getCommand(), ((connection != null) ? connection : \" is null\")});\n\t\t\t\t}\n\t\t\t\tif (connection != null) {\n\t\t\t\t\tTimerTask task = (TimerTask) connection.getSessionData(SESSION_CLOSE_TIMER_KEY);\n\t\t\t\t\tif (task != null) {\n\t\t\t\t\t\t// cancel existing timer task as it will not be needed\n\t\t\t\t\t\ttask.cancel();\n\t\t\t\t\t}\n\t\t\t\t\t// first remove connection from connections map\n\t\t\t\t\tconnectionsByFrom.remove(iqc.getFrom(), connection);\n\t\t\t\t}\n\n\t\t\t\t// now we add packet to processing thread to let it close properly in separate thread\n\t\t\t\tProcessingThreads<ProcessorWorkerThread> pt = workerThreads.get(sessionCloseProc.id());\n\n\t\t\t\tif (pt == null) {\n\t\t\t\t\tpt = workerThreads.get(defPluginsThreadsPool);\n\t\t\t\t}\n\t\t\t\tpt.addItem(sessionCloseProc, iqc, connection);\n\n\t\t\t\tprocessing_result = true;\n\n\t\t\t\tbreak;\n\n\t\t\tcase USER_STATUS:\n\t\t\t\ttry {\n\t\t\t\t\tfinal boolean isTrusted =\n\t\t\t\t\t\t\tisTrusted(iqc.getStanzaFrom()) || isTrusted(iqc.getStanzaFrom().getDomain());\n\t\t\t\t\tString pb = Command.getFieldValue(pc, \"prebind\");\n\t\t\t\t\tboolean prebind = ((pb != null) && pb.equalsIgnoreCase(\"true\"));\n\n\t\t\t\t\tif (prebind || isTrusted) {\n\t\t\t\t\t\tString av = Command.getFieldValue(pc, \"available\");\n\t\t\t\t\t\tboolean available = !((av != null) && av.equalsIgnoreCase(\"false\"));\n\n\t\t\t\t\t\tJID user_jid = JID.jidInstance(Command.getFieldValue(iqc, \"jid\"));\n\n\t\t\t\t\t\tif (prebind) {\n\t\t\t\t\t\t\tString id = Command.getFieldValue(pc, \"session-id\");\n\t\t\t\t\t\t\tif (id == null) {\n\t\t\t\t\t\t\t\tid = UUID.randomUUID().toString();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tloginUserSession(iqc.getStanzaFrom(), user_jid.getDomain(), user_jid.getBareJID(),\n\t\t\t\t\t\t\t\t\t\t\t user_jid.getResource(), id, false);\n\t\t\t\t\t\t\tfastAddOutPacket(iqc.okResult((String) null, 0));\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (available) {\n\t\t\t\t\t\t\tPacket presence = null;\n\t\t\t\t\t\t\tElement p = iqc.getElement().getChild(\"command\").getChild(\"presence\");\n\n\t\t\t\t\t\t\tif (p != null) {\n\n\t\t\t\t\t\t\t\t// + // use this hack to break XMLNS\n\t\t\t\t\t\t\t\t// + Element el = new Element(\"presence\");\n\t\t\t\t\t\t\t\t// + el.setChildren(p.getChildren());\n\t\t\t\t\t\t\t\tElement elem = p.clone();\n\n\t\t\t\t\t\t\t\telem.setXMLNS(\"jabber:client\");\n\t\t\t\t\t\t\t\tpresence = Packet.packetInstance(elem);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconnection = connectionsByFrom.get(iqc.getStanzaFrom());\n\t\t\t\t\t\t\tif (!prebind && connection == null) {\n\t\t\t\t\t\t\t\tconnection = loginUserSession(iqc.getStanzaFrom(), user_jid.getDomain(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  user_jid.getBareJID(), user_jid.getResource(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"USER_STATUS\", false);\n\t\t\t\t\t\t\t\tconnection.putSessionData(\"jingle\", \"active\");\n\t\t\t\t\t\t\t\tfastAddOutPacket(iqc.okResult((String) null, 0));\n\t\t\t\t\t\t\t\tif (presence == null) {\n\t\t\t\t\t\t\t\t\tpresence = Packet.packetInstance(new Element(\"presence\", new Element[]{\n\t\t\t\t\t\t\t\t\t\t\tnew Element(\"priority\", \"-1\"),\n\t\t\t\t\t\t\t\t\t\t\tnew Element(\"c\", new String[]{\"node\", \"ver\", \"ext\", \"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew String[]{\"http://www.google.com/xmpp/client/caps\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t XMPPServer.getImplementationVersion(), \"voice-v1\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"http://jabber.org/protocol/caps\"})}, null, null));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\n\t\t\t\t\t\t\t\t// addOutPacket(Authorization.CONFLICT.getResponseMessage(pc,\n\t\t\t\t\t\t\t\t// \"The user resource already exists.\", true));\n\t\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\t\tlog.finest(\"USER_STATUS set to true for user who is already available: \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t   iqc.toStringSecure());\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tfastAddOutPacket(iqc.okResult((String) null, 0));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (presence != null) {\n\t\t\t\t\t\t\t\tpresence.setPacketFrom(iqc.getStanzaFrom());\n\t\t\t\t\t\t\t\tpresence.setPacketTo(getComponentId());\n\t\t\t\t\t\t\t\taddOutPacket(presence);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconnection = connectionsByFrom.remove(iqc.getStanzaFrom());\n\t\t\t\t\t\t\tif (connection != null) {\n\t\t\t\t\t\t\t\tcloseSession(connection, false);\n\t\t\t\t\t\t\t\taddOutPacket(iqc.okResult((String) null, 0));\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\taddOutPacket(Authorization.ITEM_NOT_FOUND.getResponseMessage(iqc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"The user resource you want to remove does not exist.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true));\n\t\t\t\t\t\t\t\tlog.log(Level.CONFIG, \"Can not find resource connection for packet: \" + iqc.toStringSecure());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\taddOutPacket(\n\t\t\t\t\t\t\t\t\tAuthorization.FORBIDDEN.getResponseMessage(iqc, \"Only trusted entity can do it.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   true));\n\t\t\t\t\t\t} catch (PacketErrorTypeException e) {\n\t\t\t\t\t\t\tlog.warning(\"Packet error type when not expected: \" + iqc.toStringSecure());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\taddOutPacket(Authorization.UNDEFINED_CONDITION.getResponseMessage(iqc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"Unexpected error occured during the request: \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  e, true));\n\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Error creating response packet\", ex);\n\t\t\t\t\t}\n\t\t\t\t\tlog.log(Level.WARNING, \"USER_STATUS session creation error: \", e);\n\t\t\t\t}\n\t\t\t\tprocessing_result = true;\n\n\t\t\t\tbreak;\n\n\t\t\tcase OTHER:\n\t\t\t\t//#2682: Commands addressed to domain should be processed by sess-man\n\t\t\t\tif (iqc.isCommand() && isLocalDomain(iqc.getStanzaTo().getDomain())) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (iqc.getStanzaFrom() == null && connection != null &&\n\t\t\t\t\t\t\t\tconnection.getConnectionId().equals(iqc.getPacketFrom())) {\n\t\t\t\t\t\t\tiqc.initVars(connection.getjid(), iqc.getStanzaTo());\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (iqc.getPermissions() == Permissions.NONE || iqc.getPermissions() == Permissions.REMOTE) {\n\t\t\t\t\t\t\tsetPermissions(connection, iqc);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (connection != null && connection.isAuthorized()) {\n\t\t\t\t\t\t\tArrayDeque<Packet> results = new ArrayDeque<>();\n\t\t\t\t\t\t\tfor (XMPPPreprocessorIfc preproc : preProcessors.values()) {\n\t\t\t\t\t\t\t\tif (preproc.preProcess(pc, connection, naUserRepository, results,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   plugin_config.get(preproc.id()))) {\n\t\t\t\t\t\t\t\t\taddOutPackets(pc, connection, results);\n\t\t\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Packet blocked by: {0}, packet{1}\",\n\t\t\t\t\t\t\t\t\t\t\t\tnew Object[]{preproc.id(), pc});\n\n\t\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\taddOutPackets(pc, connection, results);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}    // end of for (XMPPPreprocessorIfc preproc: preProcessors)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tswitch (iqc.getPermissions()) {\n\t\t\t\t\t\t\tcase AUTH:\n\t\t\t\t\t\t\tcase LOCAL:\n\t\t\t\t\t\t\tcase ADMIN:\n\t\t\t\t\t\t\tcase TRUSTED:\n\t\t\t\t\t\t\t\tadHocCommandModule.process(iqc);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\taddOutPacket(Authorization.NOT_AUTHORIZED.getResponseMessage(pc, \"Not authorized\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t false));\n\t\t\t\t\t\t\t\t} catch (PacketErrorTypeException ex1) {\n\t\t\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"packet already of type = error, \" + iqc);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (NoConnectionIdException ex) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\taddOutPacket(Authorization.INTERNAL_SERVER_ERROR.getResponseMessage(iqc, null, true));\n\t\t\t\t\t\t} catch (PacketErrorTypeException ex1) {\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"packet already of type = error, \" + iqc);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (ComponentException ex) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\taddOutPacket(ex.makeElement(iqc, true));\n\t\t\t\t\t\t} catch (PacketErrorTypeException ex1) {\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"packet already of type = error, \" + iqc);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tprocessing_result = true;\n\t\t\t\t}\n\n\t\t\t\tif (getComponentId().equals(iqc.getStanzaTo()) && getComponentId().equals(iqc.getPacketFrom())) {\n\n\t\t\t\t\t// No such command available. This prevents from an infinite loop in\n\t\t\t\t\t// case there is no implementation to hadle such a command\n\t\t\t\t\ttry {\n\t\t\t\t\t\taddOutPacket(Authorization.FEATURE_NOT_IMPLEMENTED.getResponseMessage(iqc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"There is no implementation for such command on the server.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  true));\n\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\tex.printStackTrace();\n\t\t\t\t\t}\n\t\t\t\t\tlog.log(Level.WARNING, \"There is no implementation for such command on the server: \" + iqc);\n\t\t\t\t\tprocessing_result = true;\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\n\t\t\tcase TLS_HANDSHAKE_COMPLETE:\n\t\t\t\tif (connection != null) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Handshake details received. connection: {0}\", connection);\n\t\t\t\t\t}\n\t\t\t\t\tString tlsUniqueId = Command.getFieldValue(pc, \"tls-unique-id\");\n\t\t\t\t\tif (tlsUniqueId != null) {\n\t\t\t\t\t\tbyte[] bytes = Base64.decode(tlsUniqueId);\n\t\t\t\t\t\tconnection.putSessionData(AbstractSaslSCRAM.TLS_UNIQUE_ID_KEY, bytes);\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"tls-unique-id {0} stored in session-data. connection: {1}\",\n\t\t\t\t\t\t\t\t\tnew Object[]{tlsUniqueId, connection});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tString tlsExporter = Command.getFieldValue(pc, \"tls-exporter\");\n\t\t\t\t\tif (tlsExporter != null) {\n\t\t\t\t\t\tbyte[] bytes = Base64.decode(tlsExporter);\n\t\t\t\t\t\tconnection.putSessionData(AbstractSaslSCRAM.TLS_EXPORTER_KEY, bytes);\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"tls-exporter {0} stored in session-data. connection: {1}\",\n\t\t\t\t\t\t\t\t\tnew Object[]{tlsUniqueId, connection});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tString encodedCertificate = Command.getFieldValue(pc, \"peer-certificate\");\n\t\t\t\t\tif (encodedCertificate != null) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tbyte[] bytes = Base64.decode(encodedCertificate);\n\n\t\t\t\t\t\t\tByteArrayInputStream bais = new ByteArrayInputStream(bytes);\n\t\t\t\t\t\t\tCertificateFactory cf = CertificateFactory.getInstance(\"X.509\");\n\t\t\t\t\t\t\tCertificate certificate = cf.generateCertificate(bais);\n\n\t\t\t\t\t\t\tconnection.putSessionData(SaslEXTERNAL.PEER_CERTIFICATE_KEY, certificate);\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"peer-certificate {0} stored in session-data. connection: {1}\",\n\t\t\t\t\t\t\t\t\t\tnew Object[]{certificate, connection});\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"could not decode peer certificate\", ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tencodedCertificate = Command.getFieldValue(pc, \"local-certificate\");\n\t\t\t\t\tif (encodedCertificate != null) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tbyte[] bytes = Base64.decode(encodedCertificate);\n\n\t\t\t\t\t\t\tByteArrayInputStream bais = new ByteArrayInputStream(bytes);\n\t\t\t\t\t\t\tCertificateFactory cf = CertificateFactory.getInstance(\"X.509\");\n\t\t\t\t\t\t\tCertificate certificate = cf.generateCertificate(bais);\n\n\t\t\t\t\t\t\tconnection.putSessionData(AbstractSaslSCRAM.LOCAL_CERTIFICATE_KEY, certificate);\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"local-certificate {0} stored in session-data. connection: {1}\",\n\t\t\t\t\t\t\t\t\t\tnew Object[]{certificate, connection});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"could not decode local certificate\", ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"Handshake details received, but no connection is related.\");\n\t\t\t\t}\n\t\t\t\tprocessing_result = true;\n\t\t\t\tbreak;\n\t\t\tcase STREAM_MOVED:\n\t\t\t\tif (pc.getType() == StanzaType.error || pc.getType() == StanzaType.result) {\n\n\t\t\t\t} else {\n\t\t\t\t\tStreamManagementCommand cmd = StreamManagementCommand.fromPacket(pc);\n\t\t\t\t\tswitch (cmd) {\n\t\t\t\t\t\tcase ENABLED:\n\t\t\t\t\t\t\tif (connection != null && connection.isAuthorized()) {\n\t\t\t\t\t\t\t\tString resumptionId = Command.getFieldValue(pc, \"resumption-id\");\n\t\t\t\t\t\t\t\tif (resumptionId != null) {\n\t\t\t\t\t\t\t\t\tconnection.putSessionData(SESSION_RESUMPTION_ID_KEY, resumptionId);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase STREAM_MOVED:\n\t\t\t\t\t\t\tif (connection != null && connection.isAuthorized()) {\n\t\t\t\t\t\t\t\tString oldConnectionJidStr = Command.getFieldValue(pc, \"old-conn-jid\");\n\t\t\t\t\t\t\t\tJID oldConnJid = JID.jidInstanceNS(oldConnectionJidStr);\n\n\t\t\t\t\t\t\t\ttry {\n\n\t\t\t\t\t\t\t\t\t// get old session and replace it's connection id to redirect packets\n\t\t\t\t\t\t\t\t\t// to new connection\n\t\t\t\t\t\t\t\t\tXMPPResourceConnection oldConn = connectionsByFrom.remove(oldConnJid);\n\n\t\t\t\t\t\t\t\t\tif (oldConn != null) {\n\t\t\t\t\t\t\t\t\t\t// move resumption id from old to the new session\n\t\t\t\t\t\t\t\t\t\tString resumptionId = (String) oldConn.getSessionData(SESSION_RESUMPTION_ID_KEY);\n\t\t\t\t\t\t\t\t\t\tif (resumptionId != null) {\n\t\t\t\t\t\t\t\t\t\t\toldConn.removeSessionData(SESSION_RESUMPTION_ID_KEY);\n\t\t\t\t\t\t\t\t\t\t\tconnection.putSessionData(SESSION_RESUMPTION_ID_KEY, resumptionId);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t// Move presence and priority from old session to the new one\n\t\t\t\t\t\t\t\t\t\t//connection.setPresence(oldConn.getPresence());\n\t\t\t\t\t\t\t\t\t\tElement oldPresent = oldConn.getPresence();\n\t\t\t\t\t\t\t\t\t\tif (oldPresent != null) {\n\t\t\t\t\t\t\t\t\t\t\tconnection.putSessionData(XMPPResourceConnection.PRESENCE_KEY, oldPresent);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tconnection.setPriority(oldConn.getPriority());\n\n\n\t\t\t\t\t\t\t\t\t\t// remove current connection from list of active connections as\n\t\t\t\t\t\t\t\t\t\t// this connection will be used with other already authenticated connection\n\t\t\t\t\t\t\t\t\t\tsessionsByNodeId.get(oldConn.getBareJID()).removeResourceConnection(oldConn);\n\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\t// set resource, to add a new connection\n\t\t\t\t\t\t\t\t\t\t\tconnection.setResource(oldConn.getResource());\n\t\t\t\t\t\t\t\t\t\t} catch (Throwable ex) {\n\t\t\t\t\t\t\t\t\t\t\tlog.log(Level.WARNING, \"Could not set resource during resumption\", ex);\n\t\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t\txmppStreamMoved(connection, oldConnJid, connection.getConnectionId(), Command.getFieldValue(pc, \"send-response\"));\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\taddOutPacket(Authorization.ITEM_NOT_FOUND.getResponseMessage(pc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Previous session missing\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t false));\n\t\t\t\t\t\t\t\t\t\t} catch (PacketErrorTypeException e) {\n\t\t\t\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"could not send error, packet already of type error\",\n\t\t\t\t\t\t\t\t\t\t\t\t\te);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} catch (XMPPException ex) {\n\t\t\t\t\t\t\t\t\tlog.log(Level.SEVERE, \"exception while replacing old connection id = \" + oldConnJid +\n\t\t\t\t\t\t\t\t\t\t\t\" with new connection id = \" + pc.getPacketFrom().toString(), ex);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tif (pc.getType() != StanzaType.error) {\n\t\t\t\t\t\t\t\t\t\taddOutPacket(\n\t\t\t\t\t\t\t\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(pc, \"Not authorized\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfalse));\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} catch (PacketErrorTypeException e) {\n\t\t\t\t\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\t\t\t\t\"could not send not-authorized error, packet already of type error\", e);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tprocessing_result = true;\n\n\t\t\t\tbreak;\n\n\t\t\tcase BROADCAST_TO_ONLINE: {\n\t\t\t\ttry {\n\t\t\t\t\tif ((connection != null && connection.isAuthorized() && isAdmin(connection.getJID())) ||\n\t\t\t\t\t\t\t(iqc.getStanzaTo() != null && getName().equals(iqc.getStanzaTo().getLocalpart()))) {\n\t\t\t\t\t\tElement packetToBroadcast = null;\n\t\t\t\t\t\tfor (Element elem : pc.getElement().getChildren()) {\n\t\t\t\t\t\t\tif (elem.getXMLNS() == \"http://tigase.org/protocol/broadcast\") {\n\t\t\t\t\t\t\t\tpacketToBroadcast = elem;\n\t\t\t\t\t\t\t\tpacketToBroadcast.setAttribute(\"xmlns\", Packet.CLIENT_XMLNS);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tString to = Command.getFieldValue(pc, \"to\");\n\t\t\t\t\t\tif (to == null) {\n\t\t\t\t\t\t\tfor (XMPPSession session : sessionsByNodeId.values()) {\n\t\t\t\t\t\t\t\tJID[] jids = session.getJIDs();\n\n\t\t\t\t\t\t\t\tif (jids == null) {\n\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tfor (JID jid : jids) {\n\t\t\t\t\t\t\t\t\tElement msg = packetToBroadcast.clone();\n\t\t\t\t\t\t\t\t\tmsg.setAttribute(\"to\", jid.toString());\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tPacket toSend = Packet.packetInstance(msg);\n\t\t\t\t\t\t\t\t\t\t// it is better to send by addOutPacket as in other case results\n\t\t\t\t\t\t\t\t\t\t// collection could be very large!!\n\t\t\t\t\t\t\t\t\t\taddOutPacket(toSend);\n\t\t\t\t\t\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"could not create packet for message to broadcast\", ex);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tBareJID userJid = BareJID.bareJIDInstanceNS(to);\n\t\t\t\t\t\t\tXMPPSession session = sessionsByNodeId.get(userJid);\n\t\t\t\t\t\t\tif (session != null) {\n\t\t\t\t\t\t\t\tJID[] jids = session.getJIDs();\n\n\t\t\t\t\t\t\t\tif (jids != null) {\n\t\t\t\t\t\t\t\t\tfor (JID jid : jids) {\n\t\t\t\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"broadcasting packet to {0}\", jid);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tElement msg = packetToBroadcast.clone();\n\t\t\t\t\t\t\t\t\t\tmsg.setAttribute(\"to\", jid.toString());\n\t\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\t\tPacket toSend = Packet.packetInstance(msg);\n\t\t\t\t\t\t\t\t\t\t\t// it is better to send by addOutPacket as in other case results\n\t\t\t\t\t\t\t\t\t\t\t// collection could be very large!!\n\t\t\t\t\t\t\t\t\t\t\taddOutPacket(toSend);\n\t\t\t\t\t\t\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"could not create packet for message to broadcast\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tex);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (pc.getType() != StanzaType.error) {\n\t\t\t\t\t\t\taddOutPacket(Authorization.NOT_AUTHORIZED.getResponseMessage(pc, \"Not authorized\", false));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (NotAuthorizedException e) {\n\t\t\t\t\tif (pc.getType() != StanzaType.error) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\taddOutPacket(Authorization.NOT_AUTHORIZED.getResponseMessage(pc, \"Not authorized\", false));\n\t\t\t\t\t\t} catch (PacketErrorTypeException ex) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"could not send not-authorized error, packet already of type error\",\n\t\t\t\t\t\t\t\t\tex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (PacketErrorTypeException e) {\n\t\t\t\t\tlog.log(Level.FINEST, \"could not send not-authorized error, packet already of type error\", e);\n\t\t\t\t}\n\t\t\t}\n\t\t\tprocessing_result = true;\n\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tif (getComponentId().equals(iqc.getStanzaTo()) && getComponentId().equals(iqc.getPacketFrom())) {\n\n\t\t\t\t\t// No such command available. This prevents from an infinite loop in\n\t\t\t\t\t// case there is no implementation to hadle such a command\n\t\t\t\t\ttry {\n\t\t\t\t\t\taddOutPacket(Authorization.FEATURE_NOT_IMPLEMENTED.getResponseMessage(iqc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"There is no implementation for such command on the server.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  true));\n\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Error creating instance\", ex);\n\t\t\t\t\t}\n\t\t\t\t\tlog.log(Level.WARNING, \"There is no implementation for such command on the server: \" + iqc);\n\t\t\t\t\tprocessing_result = true;\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t}    // end of switch (pc.getCommand())\n\n\t\treturn processing_result;\n\t}\n\n\tprotected void processPacket(Packet packet, XMPPResourceConnection conn) {\n\t\tlong startTime = System.currentTimeMillis();\n\t\tint idx = tIdx;\n\n\t\ttIdx = (tIdx + 1) % maxIdx;\n\n\t\t// long defPrepTm = 0;\n\t\t// long prepTm = 0;\n\t\t// long defForwTm = 0;\n\t\t// long walkTm = 0;\n\t\t// long postTm = 0;\n\t\t// TODO: check if this is really necessary, seems to be even harmful in some\n\t\t// cases like when the error is generated as a response to a bad packet.\n\t\tpacket.setPacketTo(getComponentId());\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"processing packet: {0}, connection: {1}\",\n\t\t\t\t\tnew Object[]{packet.toStringSecure(), conn});\n\t\t}\n\n\t\tQueue<Packet> results = new ArrayDeque<Packet>(2);\n\t\tboolean stop = false;\n\n\t\tif (!stop) {\n\t\t\tif (defPacketHandler.preprocess(packet, conn, naUserRepository, results)) {\n\t\t\t\tpacket.processedBy(\"filter-foward\");\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Packet preprocessed: {0}\", packet.toStringSecure());\n\t\t\t\t\tif (results.size() > 0) {\n\t\t\t\t\t\tfor (Packet p : results) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Preprocess result: {0}\", p.toStringSecure());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\taddOutPackets(packet, conn, results);\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// defPrepTm = System.currentTimeMillis() - startTime;\n\t\t// Preprocess..., all preprocessors get all messages to look at.\n\t\t// I am not sure if this is correct for now, let's try to do it this\n\t\t// way and maybe change it later.\n\t\t// If any of them returns true - it means processing should stop now.\n\t\t// That is needed for preprocessors like privacy lists which should\n\t\t// block certain packets.\n\t\tif (!stop) {\n\t\t\tfor (XMPPPreprocessorIfc preproc : preProcessors.values()) {\n\t\t\t\tstop |= preproc.preProcess(packet, conn, naUserRepository, results, plugin_config.get(preproc.id()));\n\t\t\t\tif (stop && log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Packet blocked by: {0}, packet{1}\", new Object[]{preproc.id(), packet});\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}    // end of for (XMPPPreprocessorIfc preproc: preProcessors)\n\t\t}\n\n\t\tsetPermissions(conn, packet);\n\n\t\t// prepTm = System.currentTimeMillis() - startTime;\n\t\tif (!stop) {\n\t\t\tif (defPacketHandler.forward(packet, conn, naUserRepository, results)) {\n\t\t\t\tpacket.processedBy(\"filter-foward\");\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Packet forwarded: {0}\", packet);\n\t\t\t\t}\n\t\t\t\taddOutPackets(packet, conn, results);\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// defForwTm = System.currentTimeMillis() - startTime;\n\t\tif (!stop) {\n\t\t\twalk(packet, conn);\n\t\t\ttry {\n\t\t\t\tif ((conn != null) && conn.getConnectionId().equals(packet.getPacketFrom())) {\n\t\t\t\t\thandleLocalPacket(packet, conn);\n\t\t\t\t}\n\t\t\t} catch (NoConnectionIdException ex) {\n\n\t\t\t\t// Ignore, this should not happen at this point, or even at all.\n\t\t\t\tlog.log(Level.CONFIG, \"Impossible happened, please report to developer packet: {0}, connection: {1}.\",\n\t\t\t\t\t\tnew Object[]{packet, conn});\n\t\t\t}\n\t\t}\n\n\t\t// walkTm = System.currentTimeMillis() - startTime;\n\t\tif (!stop) {\n\t\t\tfor (XMPPPostprocessorIfc postproc : postProcessors.values()) {\n\t\t\t\tString plug_id = postproc.id();\n\t\t\t\tlong[] postProcTime = null;\n\n\t\t\t\tsynchronized (postTimes) {\n\t\t\t\t\tpostProcTime = postTimes.get(plug_id);\n\t\t\t\t\tif (postProcTime == null) {\n\t\t\t\t\t\tpostProcTime = new long[maxIdx];\n\t\t\t\t\t\tpostTimes.put(plug_id, postProcTime);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tlong stTime = System.currentTimeMillis();\n\n\t\t\t\tpostproc.postProcess(packet, conn, naUserRepository, results, plugin_config.get(postproc.id()));\n\t\t\t\tpostProcTime[idx] = System.currentTimeMillis() - stTime;\n\t\t\t}    // end of for (XMPPPostprocessorIfc postproc: postProcessors)\n\t\t}      // end of if (!stop)\n\n\t\t// postTm = System.currentTimeMillis() - startTime;\n\t\tif (!stop && !packet.wasProcessed() &&\n\t\t\t\t((packet.getStanzaTo() == null) || (!isLocalDomain(packet.getStanzaTo().toString())))) {\n\t\t\tif (defPacketHandler.canHandle(packet, conn)) {\n\t\t\t\tProcessingThreads<ProcessorWorkerThread> pt = workerThreads.get(defHandlerProc.id());\n\n\t\t\t\tif (pt == null) {\n\t\t\t\t\tpt = workerThreads.get(defPluginsThreadsPool);\n\t\t\t\t}\n\t\t\t\tpt.addItem(defHandlerProc, packet, conn);\n\t\t\t\tpacket.processedBy(defHandlerProc.id());\n\t\t\t}\n\t\t}\n\t\tsetPermissions(conn, results);\n\t\taddOutPackets(packet, conn, results);\n\t\tif (packet.wasProcessed() || processAdminsOrDomains(packet)) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Packet processed by: {0}\", packet.getProcessorsIds().toString());\n\t\t\t}\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Packet not processed: {0}\", packet.toStringSecure());\n\t\t\t}\n\n\t\t\tPacket error = null;\n\n\t\t\tif (stop || ((conn == null) && (packet.getStanzaFrom() != null) && (packet.getStanzaTo() != null) &&\n\t\t\t\t\t!packet.getStanzaTo().equals(getComponentId()) &&\n\t\t\t\t\t((packet.getElemName() == Iq.ELEM_NAME) || (packet.getElemName() == Message.ELEM_NAME)))) {\n\t\t\t\ttry {\n\t\t\t\t\terror = Authorization.SERVICE_UNAVAILABLE.getResponseMessage(packet, \"Service not available.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true);\n\t\t\t\t} catch (PacketErrorTypeException e) {\n\t\t\t\t\tlog.log(Level.FINE, \"Service not available. Packet is error type already: {0}\",\n\t\t\t\t\t\t\tpacket.toStringSecure());\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (((packet.getStanzaFrom() != null) || (conn != null)) && packet.wasSkipped()) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\terror = Authorization.RESOURCE_CONSTRAINT.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Server subsystem overloaded, service temporarily unavailable.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true);\n\t\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\t\tlog.log(Level.FINE,\n\t\t\t\t\t\t\t\t\t\"Server subsystem overloaded. Packet {0} not processed by processors {1}\",\n\t\t\t\t\t\t\t\t\tnew Object[]{packet.toStringSecure(), packet.getSkippedProcessorsIds()});\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (PacketErrorTypeException e) {\n\t\t\t\t\t\tlog.log(Level.FINE, \"Internal queues full. Packet is error type already: {0}\",\n\t\t\t\t\t\t\t\tpacket.toStringSecure());\n\t\t\t\t\t}\n\t\t\t\t} else if ((packet.getStanzaFrom() != null) || (conn != null)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\terror = Authorization.FEATURE_NOT_IMPLEMENTED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Feature not supported yet.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true);\n\t\t\t\t\t} catch (PacketErrorTypeException e) {\n\t\t\t\t\t\tlog.log(Level.FINE, \"Feature not supported yet. Packet is error type already: {0}\",\n\t\t\t\t\t\t\t\tpacket.toStringSecure());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (error != null) {\n\t\t\t\tif (error.getStanzaTo() != null) {\n\t\t\t\t\tconn = getResourceConnection(error.getStanzaTo());\n\t\t\t\t}      // end of if (error.getElemTo() != null)\n\t\t\t\ttry {\n\t\t\t\t\tif (conn != null) {\n\t\t\t\t\t\terror.setPacketTo(conn.getConnectionId());\n\t\t\t\t\t}    // end of if (conn != null)\n\t\t\t\t\taddOutPacket(error);\n\t\t\t\t} catch (NoConnectionIdException e) {\n\n\t\t\t\t\t// Hm, strange, SM own session?\n\t\t\t\t\tlog.log(Level.WARNING, \"Error packet to the SM''s own session: {0}\", error);\n\t\t\t\t}\n\t\t\t}\n\t\t}    // end of else\n\n\t\t// defPrepTime[idx] = defPrepTm;\n\t\t// prepTime[idx] = prepTm;\n\t\t// defForwTime[idx] = defForwTm;\n\t\t// walkTime[idx] = walkTm;\n\t\t// postTime[idx] = postTm;\n\t}\n\n\tprotected void processPresenceUpdate(XMPPSession session, Element packet) {\n\t\ttry {\n\t\t\tPacket presence = Packet.packetInstance(packet);\n\t\t\teventBus.fire(new UserPresenceChangedEvent(session, presence));\n\t\t} catch (TigaseStringprepException ex) {\n\n\t\t\t// should not happen\n\t\t\tlog.log(Level.SEVERE,\n\t\t\t\t\t\"exception processing presence update for session = \" + session + \" and packet = \" + packet, ex);\n\t\t}\n\t}\n\n\tprotected void registerNewSession(BareJID userId, XMPPResourceConnection conn) {\n\t\tsynchronized (conn) {\n\t\t\tif (conn.getSessionData(XMPPResourceConnection.CLOSING_KEY) != null) {\n\n\t\t\t\t// The user just closed the connection, ignore....\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tXMPPSession session = getSession(userId);\n\n\t\t\tif (session == null) {\n\t\t\t\tsession = new XMPPSession(userId.getLocalpart());\n\t\t\t\tsessionsByNodeId.put(userId, session);\n\n\t\t\t\tint currSize = sessionsByNodeId.size();\n\n\t\t\t\tif (currSize > maxUserSessions) {\n\t\t\t\t\tmaxUserSessions = currSize;\n\t\t\t\t}\n\t\t\t\tif (currSize > maxUserSessionsDaily) {\n\t\t\t\t\tmaxUserSessionsDaily = currSize;\n\t\t\t\t}\n\n\t\t\t\t++totalUserSessions;\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Created new XMPPSession for: {0}\", userId);\n\t\t\t\t}\n\t\t\t} else {\n\n\t\t\t\t// Check all other connections whether they are still alive....\n\t\t\t\tList<XMPPResourceConnection> connections = session.getActiveResources();\n\n\t\t\t\tif (connections != null) {\n\t\t\t\t\tfor (XMPPResourceConnection connection : connections) {\n\t\t\t\t\t\tif (connection != conn && connection.getSessionData(XMPPResourceConnection.CLOSING_KEY) == null) {\n\t\t\t\t\t\t\tLong lastCheck = (Long) connection.getSessionData(XMPPResourceConnection.CONNECTION_CHECK_TIMESTAMP_KEY);\n\t\t\t\t\t\t\tif (lastCheck != null && (System.currentTimeMillis() - lastCheck) < this.connectionCheckPeriod) {\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconnection.putSessionData(XMPPResourceConnection.CONNECTION_CHECK_TIMESTAMP_KEY, System.currentTimeMillis());\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Checking connection: {0}\", connection);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tPacket command = Command.CHECK_USER_CONNECTION.getPacket(getComponentId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t connection.getConnectionId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t StanzaType.get,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t UUID.randomUUID().toString());\n\n\t\t\t\t\t\t\t\tCommand.addFieldValue(command, \"user-jid\", userId.toString());\n\t\t\t\t\t\t\t\taddOutPacketWithTimeout(command, connectionCheckCommandHandler, 30l, TimeUnit.SECONDS);\n\t\t\t\t\t\t\t} catch (NoConnectionIdException ex) {\n\n\t\t\t\t\t\t\t\t// This actually should not happen... might be a bug:\n\t\t\t\t\t\t\t\tlog.log(Level.WARNING, \"This should not happen, check it out!, \", ex);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tsession.addResourceConnection(conn);\n\t\t\t\tif ((!\"USER_STATUS\".equals(conn.getSessionId())) && !conn.isServerSession() && !conn.isTmpSession()) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tPacket user_login_cmd = Command.USER_LOGIN.getPacket(getComponentId(), conn.getConnectionId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t StanzaType.set, conn.nextStanzaId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Command.DataType.submit);\n\n\t\t\t\t\t\tCommand.addFieldValue(user_login_cmd, \"user-jid\", userId.toString());\n\t\t\t\t\t\taddOutPacket(user_login_cmd);\n\t\t\t\t\t} catch (NoConnectionIdException ex) {\n\n\t\t\t\t\t\t// This actually should not happen... might be a bug:\n\t\t\t\t\t\tlog.log(Level.WARNING, \"This should not happen, check it out!, \", ex);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\tlog.log(Level.CONFIG, \"Stringprep problem for resource connection: {0}\", conn);\n\t\t\t\thandleLogout(userId, conn);\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected void sendToAdmins(Packet packet) {\n\t\tfor (BareJID admin : admins) {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.log(Level.FINER, \"Sending packet to admin: {0}\", admin);\n\t\t\t}\n\n\t\t\tPacket admin_pac = packet.copyElementOnly();\n\n\t\t\tadmin_pac.initVars(packet.getStanzaFrom(), JID.jidInstance(admin));\n\t\t\tprocessPacket(admin_pac);\n\t\t}\n\t}\n\n\t@HandleEvent\n\tprotected void nodeShutdown(ShutdownEvent event) {\n\t\t// if not this node is being shutdown then do nothing\n\t\tif (!event.getNode().equals(getComponentId().getDomain())) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (event.getMessage() != null) {\n\t\t\tElement msgEl = new Element(\"message\", new String[]{Packet.XMLNS_ATT}, new String[]{Packet.CLIENT_XMLNS});\n\t\t\tmsgEl.addChild(new Element(\"body\", event.getMessage()));\n\t\t\tfor (XMPPResourceConnection conn : connectionsByFrom.values()) {\n\t\t\t\ttry {\n\t\t\t\t\tElement packetEl = msgEl.clone();\n\t\t\t\t\tpacketEl.setAttribute(\"from\", conn.getDomainAsJID().getDomain());\n\t\t\t\t\tpacketEl.setAttribute(\"to\", conn.getJID().toString());\n\t\t\t\t\tPacket packet = Packet.packetInstance(msgEl, conn.getDomainAsJID(), conn.getJID());\n\t\t\t\t\taddPacket(packet);\n\t\t\t\t} catch (NotAuthorizedException ex) {\n\t\t\t\t\tlog.log(Level.FINEST, \"could not deliver notification about shutdown as session is not authorized\",\n\t\t\t\t\t\t\tex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// schedule close of existing session after 30 seconds to make sure that\n\t\t// other components will be aware that we are stopping this server\n\t\taddTimerTask(nodeShutdownTask, event.getDelay() * SECOND, 1 * SECOND);\n\t}\n\n\tprotected void xmppStreamMoved(XMPPResourceConnection conn, JID oldConnId, JID newConnId, String sendResponse) {\n\t\tPacket cmd = StreamManagementCommand.STREAM_MOVED.create(getComponentId(), oldConnId);\n\n\t\tCommand.addFieldValue(cmd, \"cmd\", \"stream-moved\");\n\t\tCommand.addFieldValue(cmd, \"new-conn-jid\", newConnId.toString());\n\t\tCommand.addFieldValue(cmd, \"send-response\", sendResponse);\n\t\tcmd.setPacketFrom(getComponentId());\n\t\tcmd.setPacketTo(oldConnId);\n\t\taddOutPacket(cmd);\n\t}\n\n\t@Override\n\tprotected Integer getMaxQueueSize(int def) {\n\t\treturn def * 10;\n\t}\n\n\tprotected XMPPSession getSession(BareJID jid) {\n\t\treturn sessionsByNodeId.get(jid);\n\t}\n\n\tprotected XMPPResourceConnection getXMPPResourceConnection(JID connId) {\n\t\treturn connectionsByFrom.get(connId);\n\t}\n\n\tprotected XMPPResourceConnection getXMPPResourceConnection(Packet p) {\n\t\tXMPPResourceConnection conn = null;\n\t\tJID from = p.getPacketFrom();\n\n\t\tif (from != null) {\n\t\t\tconn = connectionsByFrom.get(from);\n\t\t\tif (conn != null) {\n\t\t\t\treturn conn;\n\t\t\t}\n\t\t}\n\n\t\t// It might be a message _to_ some user on this server\n\t\t// so let's look for established session for this user...\n\t\tJID to = p.getStanzaTo();\n\n\t\tif (to != null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Searching for resource connection for: \" + to);\n\t\t\t}\n\t\t\tconn = getResourceConnection(to);\n\t\t} else {\n\n\t\t\t// Hm, not sure what should I do now....\n\t\t\t// Maybe I should treat it as message to admin....\n\t\t\tlog.log(Level.CONFIG, \"Message without TO attribute set, don''t know what to do wih this: {0}\", p);\n\t\t}    // end of else\n\n\t\treturn conn;\n\t}\n\n\tprotected boolean isBrokenPacket(Packet p) {\n\n\t\t// TODO: check this out to make sure it does not lead to an infinite\n\t\t// processing loop These are most likely packets generated inside the SM to\n\t\t// other users who are offline, like presence updates.\n\t\tif (getComponentId().equals(p.getPacketFrom()) && (p.getPacketTo() == null)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (p.getFrom() == null) {\n\n\t\t\t// This is actually a broken packet and we can't even return an error\n\t\t\t// for it, so just log it and drop it.\n\t\t\tlog.log(Level.FINE, \"Broken packet: {0}\", p.toStringSecure());\n\n\t\t\treturn true;\n\t\t}\n\t\tif (!p.getFrom().equals(p.getStanzaFrom()) &&\n\t\t\t\t(!p.isCommand() || (p.isCommand() && (p.getCommand() == Command.OTHER)))) {\n\n\t\t\t// Sometimes (Bosh) connection is gone and this is an error packet\n\t\t\t// sent back to the original sender. This original sender might be\n\t\t\t// not local....\n\t\t\tif ((p.getStanzaFrom() != null) && !isLocalDomain(p.getStanzaFrom().getDomain())) {\n\t\t\t\t// needed to add following condition as we filtered out and dropped packets from S2S to users bare JID!!\n\t\t\t\tif (p.getStanzaTo() == null || !isLocalDomain(p.getStanzaTo().getDomain(), false)) {\n\t\t\t\t\t// ok just forward it there....\n\t\t\t\t\tp.setPacketFrom(null);\n\t\t\t\t\tp.setPacketTo(null);\n\t\t\t\t\tfastAddOutPacket(p);\n\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\t// maybe we should assume that if packetTo == null the it was routed correctly?\n\t\t\t\tif (p.getPacketTo() == null) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// this is special case in which we know and expect that there will be\n\t\t\t// no session for this packet but we still need to process it\n\t\t\tif (C2SDeliveryErrorProcessor.isDeliveryError(p)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// if this is packet to bare jid then we need to process it on behalf of a user\n\t\t\t// even if there is no session for this user\n\t\t\tif (p.getStanzaTo() != null && p.getStanzaTo().getResource() == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// if this is packet is a response from service disco#info on behalf of the user we need to forward it\n\t\t\tif (p.isServiceDisco() && p.getStanzaFrom() != null && p.getStanzaFrom().getResource() == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// It doesn't look good, there should really be a connection for\n\t\t\t// this packet....\n\t\t\t// returning error back...\n\t\t\tlog.log(Level.FINE, \"Broken packet: {0}\", p.toStringSecure());\n\n\t\t\t// we do not want to send presence error packets here...\n\t\t\tif ((p.getElemName() == Iq.ELEM_NAME) || (p.getElemName() == Message.ELEM_NAME)) {\n\t\t\t\ttry {\n\t\t\t\t\tPacket error = Authorization.SERVICE_UNAVAILABLE.getResponseMessage(p, \"Service not available.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttrue);\n\n\t\t\t\t\terror.setPacketTo(p.getFrom());\n\t\t\t\t\tfastAddOutPacket(error);\n\t\t\t\t} catch (PacketErrorTypeException e) {\n\t\t\t\t\tlog.log(Level.FINE, \"Packet is error type already: {0}\", p.toStringSecure());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate void calculateActiveUsers() {\n\t\tint count = 0;\n\n\t\tfor (BareJID bareJID : sessionsByNodeId.keySet()) {\n\t\t\tif (!bareJID.toString().startsWith(\"sess-man\")) {\n\t\t\t\tXMPPSession session = sessionsByNodeId.get(bareJID);\n\t\t\t\t// check if session is still there as it could be closed\n\t\t\t\t// if sessionsByNodeId is big collection\n\t\t\t\tif (session != null) {\n\t\t\t\t\tfor (XMPPResourceConnection xMPPResourceConnection : session.getActiveResources()) {\n\t\t\t\t\t\tif (System.currentTimeMillis() - xMPPResourceConnection.getLastAccessed() <\n\t\t\t\t\t\t\t\tactiveUserTimeframe) {\n\t\t\t\t\t\t\tcount++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tactiveUserNumber = count;\n\t}\n\n\tprivate long calcAverage(long[] timings) {\n\t\tlong res = 0;\n\n\t\tfor (long ppt : timings) {\n\t\t\tres += ppt;\n\t\t}\n\n\t\tlong processingTime = res / timings.length;\n\n\t\treturn processingTime;\n\t}\n\n\tprivate void walk(final Packet packet, final XMPPResourceConnection connection) {\n\n\t\t// final Element elem, final Queue<Packet> results) {\n\t\tfor (XMPPProcessorIfc proc_t : processors.values()) {\n\t\t\tXMPPProcessorIfc processor = proc_t;\n\t\t\tAuthorization result = processor.canHandle(packet, connection);\n\n\t\t\tif (result == Authorization.AUTHORIZED) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"XMPPProcessorIfc: {0} ({1}\" + \")\" + \"Request: \" + \"{2}, conn: {3}\",\n\t\t\t\t\t\t\tnew Object[]{processor.getClass().getSimpleName(), processor.id(), packet, connection});\n\t\t\t\t}\n\n\t\t\t\tProcessingThreads<ProcessorWorkerThread> pt = workerThreads.get(processor.id());\n\n\t\t\t\tif (pt == null) {\n\t\t\t\t\tpt = workerThreads.get(defPluginsThreadsPool);\n\t\t\t\t}\n\t\t\t\tif (pt.addItem(processor, packet, connection)) {\n\t\t\t\t\tpacket.processedBy(processor.id());\n\t\t\t\t} else {\n\t\t\t\t\tpacket.notProcessedBy(processor.id());\n\t\t\t\t\t// proc_t.debugQueue();\n\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\tlog.log(Level.FINE, \"Can not add packet: {0} to processor: {1} internal queue full.\",\n\t\t\t\t\t\t\t\tnew Object[]{packet.toStringSecure(), pt.getName()});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (result != null) {\n\n\t\t\t\t\t// TODO: A plugin returned an error, the packet should be bounced back\n\t\t\t\t\t// with an appropriate error\n\t\t\t\t}\n\t\t\t}\n\t\t}    // end of for ()\n\t}\n\n\tprivate List<Element> getFeatures(XMPPResourceConnection session) {\n\t\tList<Element> results = new LinkedList<Element>();\n\n\t\tfor (XMPPProcessorIfc proc_t : processors.values()) {\n\t\t\tElement[] features = proc_t.supStreamFeatures(session);\n\n\t\t\tif (features != null) {\n\t\t\t\tresults.addAll(Arrays.asList(features));\n\t\t\t}    // end of if (features != null)\n\t\t}      // end of for ()\n\n\t\tif (includeCapsInStream && router != null && session != null && session.isAuthorized()) {\n\t\t\trouter.getServiceEntityCaps(session.getjid()).ifPresent(results::add);\n\t\t}\n\n\t\treturn results;\n\t}\n\n\tprivate Map<String, Object> getPluginSettings(String plug_id, Map<String, Object> props) {\n\t\tMap<String, Object> plugin_settings = new ConcurrentHashMap<String, Object>(10);\n\n\t\t// First set all options common for all plugins and then set all options\n\t\t// specific to the\n\t\t// plugin to make sure specific options can overwrite common options\n\t\tfor (Map.Entry<String, Object> entry : props.entrySet()) {\n\t\t\tif (entry.getKey().startsWith(PLUGINS_CONF_PROP_KEY)) {\n\n\t\t\t\t// Split the key to configuration nodes separated with '/'\n\t\t\t\tString[] nodes = entry.getKey().split(\"/\");\n\n\t\t\t\t// Settings option for all plugins\n\t\t\t\tif (nodes.length == 2) {\n\t\t\t\t\tplugin_settings.put(nodes[1], entry.getValue());\n\t\t\t\t\tlog.log(Level.CONFIG, \"Adding a common plugins option: {0} = {1}\",\n\t\t\t\t\t\t\tnew Object[]{nodes[1], entry.getValue()});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Now set plugin specific options\n\t\tfor (Map.Entry<String, Object> entry : props.entrySet()) {\n\t\t\tif (entry.getKey().startsWith(PLUGINS_CONF_PROP_KEY)) {\n\n\t\t\t\t// Workaround for plugin id containing \"/\" in id\n\t\t\t\tString key = entry.getKey();\n\t\t\t\tif (plug_id.contains(\"/\")) {\n\t\t\t\t\tif (!key.contains(plug_id)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tkey = key.replace(plug_id, \"plugin-id\");\n\t\t\t\t}\n\t\t\t\t// Split the key to configuration nodes separated with '/'\n\t\t\t\tString[] nodes = key.split(\"/\");\n\n\t\t\t\t// The plugin ID part may contain many IDs separated with comma ','\n\t\t\t\tif (nodes.length > 2) {\n\t\t\t\t\tString[] ids = nodes[1].split(\",\");\n\n\t\t\t\t\tArrays.sort(ids);\n\t\t\t\t\tif (Arrays.binarySearch(ids, plug_id) >= 0 || Arrays.binarySearch(ids, \"plugin-id\") >= 0) {\n\t\t\t\t\t\tplugin_settings.put(nodes[2], entry.getValue());\n\t\t\t\t\t\tlog.log(Level.CONFIG, \"Adding a specific plugins option [{0}]: {1} = {2}\",\n\t\t\t\t\t\t\t\tnew Object[]{plug_id, nodes[2], entry.getValue()});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t//plugin_settings.put(\"sm-jid\", getComponentId());\n\n\t\treturn plugin_settings;\n\t}\n\n\tprivate void setPermissions(XMPPResourceConnection conn, Packet packet) {\n\t\tPermissions perms = getPermissionForConnection(conn);\n\t\tpacket.setPermissions(perms);\n\t}\n\n\tprivate void setPermissions(XMPPResourceConnection conn, Queue<Packet> results) {\n\t\tPermissions perms = getPermissionForConnection(conn);\n\t\tfor (Packet res : results) {\n\t\t\tres.setPermissions(perms);\n\t\t}\n\t}\n\n\tprivate Permissions getPermissionForConnection(XMPPResourceConnection conn) {\n\t\tPermissions perms = Permissions.NONE;\n\t\tif (conn != null) {\n\t\t\tperms = Permissions.LOCAL;\n\t\t\tif (conn.isAuthorized()) {\n\t\t\t\tperms = Permissions.AUTH;\n\t\t\t\tif (conn.isAnonymous()) {\n\t\t\t\t\tperms = Permissions.ANONYM;\n\t\t\t\t} else {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tJID id = conn.getJID();\n\n\t\t\t\t\t\tif (isTrusted(id)) {\n\t\t\t\t\t\t\tperms = Permissions.TRUSTED;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (isAdmin(id)) {\n\t\t\t\t\t\t\tperms = Permissions.ADMIN;\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (NotAuthorizedException e) {\n\t\t\t\t\t\tperms = Permissions.NONE;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn perms;\n\t}\n\n\tpublic interface ProcessorResultWriter {\n\n\t\tvoid write(Packet packet, XMPPResourceConnection session, Queue<Packet> results);\n\n\t}\n\n\tprivate static class AuthenticationTimer\n\t\t\textends TimerTask {\n\n\t\tpublic static final String AUTH_TIMER_KEY = \"authTimerKey\";\n\n\t\tpublic static void cancel(XMPPResourceConnection session) {\n\t\t\tif (session != null) {\n\t\t\t\tAuthenticationTimer timer = (AuthenticationTimer) session.getSessionData(AUTH_TIMER_KEY);\n\t\t\t\tif (timer != null) {\n\t\t\t\t\tsession.removeSessionData(AUTH_TIMER_KEY);\n\t\t\t\t\ttimer.cancel();\n\t\t\t\t\tlog.log(Level.FINEST, \"Authentication timer, cancelled for connection: {0}\", session);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate final SessionManager sm;\n\t\tprivate JID connId = null;\n\n\t\tprivate AuthenticationTimer(SessionManager sm, JID connId) {\n\t\t\tthis.sm = sm;\n\t\t\tthis.connId = connId;\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tXMPPResourceConnection conn = sm.connectionsByFrom.get(connId);\n\n\t\t\tif (conn != null) {\n\t\t\t\tsynchronized (conn) {\n\t\t\t\t\tif (!conn.isAuthorized()) {\n\t\t\t\t\t\tconn.putSessionData(XMPPResourceConnection.AUTHENTICATION_TIMEOUT_KEY,\n\t\t\t\t\t\t\t\t\t\t\tXMPPResourceConnection.AUTHENTICATION_TIMEOUT_KEY);\n\t\t\t\t\t\tsm.connectionsByFrom.remove(connId);\n\t\t\t\t\t\t++sm.authTimeouts;\n\t\t\t\t\t\tlog.log(Level.FINE, \"Authentication timeout expired, closing connection: {0}\", connId);\n\t\t\t\t\t\tsm.fastAddOutPacket(Command.CLOSE.getPacket(sm.getComponentId(), connId, StanzaType.set,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconn.nextStanzaId()));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Bean(name = defaultHandlerProcId, parent = SessionManager.class, active = true)\n\tpublic static class DefaultHandlerProc\n\t\t\textends XMPPProcessor\n\t\t\timplements XMPPProcessorIfc {\n\n\t\t@Inject\n\t\tSessionManager sm;\n\n\t\t@Override\n\t\tpublic int concurrentQueuesNo() {\n\t\t\treturn Runtime.getRuntime().availableProcessors() * 4;\n\t\t}\n\n\t\t@Override\n\t\tpublic String id() {\n\t\t\treturn defaultHandlerProcId;\n\t\t}\n\n\t\t@Override\n\t\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Executing default packet handler for: {0}\", packet);\n\t\t\t}\n\t\t\tsm.defPacketHandler.process(packet, session, repo, results);\n\t\t}\n\t}\n\n\t@Bean(name = \"writer\", active = true)\n\tpublic static class SMPacketWriter\n\t\t\timplements PacketWriter {\n\n\t\t@Inject(bean = \"service\", nullAllowed = false)\n\t\tprivate SessionManager component;\n\n\t\t@Override\n\t\tpublic void write(Collection<Packet> packets) {\n\t\t\tif (packets != null) {\n\t\t\t\tpackets.forEach(this::write);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void write(Packet packet) {\n\t\t\tcomponent.addOutPacket(packet);\n\t\t}\n\n\t\t@Override\n\t\tpublic void write(Packet packet, AsyncCallback callback) {\n\t\t\tthrow new UnsupportedOperationException(\"writing packets with AsyncCallback is not supported in SM!\");\n\t\t}\n\t}\n\n\t@Bean(name = sessionCloseProcId, parent = SessionManager.class, active = true)\n\tpublic static class SessionCloseProc\n\t\t\textends XMPPProcessor\n\t\t\timplements XMPPProcessorIfc {\n\n\t\t@Inject\n\t\tSessionManager sm;\n\n\t\t@Override\n\t\tpublic int concurrentQueuesNo() {\n\t\t\treturn super.concurrentQueuesNo() * 4;\n\t\t}\n\n\t\t@Override\n\t\tpublic String id() {\n\t\t\treturn sessionCloseProcId;\n\t\t}\n\n\t\t@Override\n\t\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Executing connection close for: {0}\", packet);\n\t\t\t}\n\n\t\t\tString userJid = Command.getFieldValue(packet, \"user-jid\");\n\n\t\t\tsm.closeConnection(session, packet.getFrom(), userJid, false);\n\t\t}\n\t}\n\n\t@Bean(name = sessionOpenProcId, parent = SessionManager.class, active = true)\n\tpublic static class SessionOpenProc\n\t\t\textends XMPPProcessor\n\t\t\timplements XMPPProcessorIfc {\n\n\t\t@Inject\n\t\tSessionManager sm;\n\n\t\t@Override\n\t\tpublic int concurrentQueuesNo() {\n\t\t\treturn super.concurrentQueuesNo() * 2;\n\t\t}\n\n\t\t@Override\n\t\tpublic String id() {\n\t\t\treturn sessionOpenProcId;\n\t\t}\n\n\t\t@Override\n\t\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\t\t\tXMPPResourceConnection conn = session;\n\n\t\t\t// It might be existing opened stream after TLS/SASL authorization\n\t\t\t// If not, it means this is new stream\n\t\t\tif (conn == null) {\n\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\tlog.log(Level.FINER, \"Adding resource connection for: {0}\", packet.getFrom());\n\t\t\t\t}\n\n\t\t\t\tfinal String hostname = Command.getFieldValue(packet, \"hostname\");\n\n\t\t\t\ttry {\n\t\t\t\t\tconn = sm.createUserSession(packet.getFrom(), hostname);\n\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Incrrect hostname, did not pass stringprep processing: {0}\", hostname);\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tAuthenticationTimer authTimer = new AuthenticationTimer(sm, packet.getFrom());\n\t\t\t\tconn.putSessionData(AuthenticationTimer.AUTH_TIMER_KEY, authTimer);\n\t\t\t\tsm.addTimerTask(authTimer, sm.authTimeout, TimeUnit.SECONDS);\n\t\t\t} else {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Stream opened for existing session, authorized: {0}\", conn.isAuthorized());\n\t\t\t\t}\n\t\t\t}    // end of else\n\t\t\tconn.setSessionId(Command.getFieldValue(packet, \"session-id\"));\n\t\t\tconn.setDefLang(Command.getFieldValue(packet, \"xml:lang\"));\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Setting session-id {0} for connection: {1}\",\n\t\t\t\t\t\tnew Object[]{conn.getSessionId(), conn});\n\t\t\t}\n\t\t\tsm.fastAddOutPacket(packet.okResult((String) null, 0));\n\t\t}\n\t}\n\n\tprivate class ConnectionCheckCommandHandler\n\t\t\timplements ReceiverTimeoutHandler {\n\n\t\t@Override\n\t\tpublic void responseReceived(Packet packet, Packet response) {\n\t\t\tif (response.getType() == StanzaType.error) {\n\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\tlog.log(Level.FINER, \"Connection checker error received, closing connection: {0}\", packet.getTo());\n\t\t\t\t}\n\n\t\t\t\t// The connection is not longer active, closing the user session here.\n\t\t\t\tString userJid = Command.getFieldValue(packet, \"user-jid\");\n\n\t\t\t\tcloseConnection(null, packet.getTo(), userJid, false);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void timeOutExpired(Packet packet) {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.log(Level.FINER, \"Connection checker timeout expired, closing connection: {0}\", packet.getTo());\n\t\t\t}\n\n\t\t\tString userJid = Command.getFieldValue(packet, \"user-jid\");\n\n\t\t\tcloseConnection(null, packet.getTo(), userJid, false);\n\t\t}\n\t}\n\n\tprivate class NodeShutdownTask\n\t\t\textends TimerTask {\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\t// we are stopping server so let's check if all session are closed\n\t\t\tif (sessionsByNodeId.isEmpty() ||\n\t\t\t\t\t(sessionsByNodeId.size() == 1 && sessionsByNodeId.get(getComponentId().getBareJID()) != null)) {\n\t\t\t\tlog.log(Level.CONFIG, \"shutdown - stopping JVM\");\n\t\t\t\tSystem.exit(0);\n\t\t\t} else {\n\t\t\t\tlog.log(Level.CONFIG, \"shutdown - still waiting for {0} to be closed\", sessionsByNodeId.size());\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\t\t\tfor (XMPPSession session : sessionsByNodeId.values()) {\n\t\t\t\t\t\tsb.append(\"\\n\\t\");\n\t\t\t\t\t\tsb.append(session.toString());\n\t\t\t\t\t}\n\t\t\t\t\tlog.log(Level.FINEST, \"shutdown - waiting for following sessions:{0}\", sb.toString());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n\tprivate class ProcessorWorkerThread\n\t\t\textends WorkerThread {\n\n\t\tprivate ArrayDeque<Packet> local_results = new ArrayDeque<Packet>(100);\n\n\t\t@Override\n\t\tpublic void process(QueueItem item) {\n\t\t\tXMPPProcessorIfc processor = item.getProcessor();\n\n\t\t\ttry {\n\t\t\t\tprocessor.process(item.getPacket(), item.getConn(), naUserRepository, local_results,\n\t\t\t\t\t\t\t\t  plugin_config.get(processor.id()));\n\t\t\t\tif (item.getConn() != null) {\n\t\t\t\t\tsetPermissions(item.getConn(), local_results);\n\t\t\t\t}\n\t\t\t\taddOutPackets(item.getPacket(), item.getConn(), local_results);\n\t\t\t} catch (InvalidPacketException e) {\n\t\t\t\tlog.log(Level.CONFIG, \"Invalid packet! Error: {0}, packet: {1}\",\n\t\t\t\t\t\tnew String[]{e.getLocalizedMessage(), item.getPacket().toStringSecure()});\n\t\t\t} catch (NotAuthorizedException e) {\n\t\t\t\tlog.log(Level.CONFIG, \"Session hasn't been authorised yet! Error: {0}, packet: {1}\",\n\t\t\t\t\t\tnew String[]{e.getLocalizedMessage(), item.getPacket().toStringSecure()});\n\t\t\t\tsendErrorBack(item.getPacket(), Authorization.NOT_AUTHORIZED, null);\n\t\t\t} catch (XMPPProcessorException e) {\n\t\t\t\tlog.log(Level.FINEST, \"Exception during packet processing: \" + item.getPacket().toStringSecure(), e);\n\t\t\t\tsendErrorBack(item.getPacket(), e.getErrorCondition(), e.getMessage());\n\t\t\t} catch (XMPPException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Exception during packet processing: \" + item.getPacket().toStringSecure(), e);\n\t\t\t\tsendErrorBack(item.getPacket(), Authorization.INTERNAL_SERVER_ERROR, null);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic WorkerThread getNewInstance() {\n\t\t\tProcessorWorkerThread worker = new ProcessorWorkerThread();\n\n\t\t\treturn worker;\n\t\t}\n\n\t\tprivate void sendErrorBack(Packet packet, Authorization errorCondition, String message) {\n\t\t\tif (packet.getType() != StanzaType.error) {\n\t\t\t\ttry {\n\t\t\t\t\taddOutPacket(errorCondition.getResponseMessage(packet, message, true));\n\t\t\t\t} catch (PacketErrorTypeException ex) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Problem during generate error response\", ex);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Class implements timer which will be scheduled on STREAM_CLOSED to ensure that session is properly closed, even\n\t * if STREAM_FINISHED would not be received\n\t */\n\tprivate class SessionCloseTimer\n\t\t\textends TimerTask {\n\n\t\tprivate JID connId = null;\n\t\tprivate String sessId = null;\n\n\t\tprivate SessionCloseTimer(JID connId, String sessId) {\n\t\t\tthis.connId = connId;\n\t\t\tthis.sessId = sessId;\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tXMPPResourceConnection conn = connectionsByFrom.get(connId);\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\"session closed timer executed for connId = {0}, \" + \"sessionId = {1}, conn = {2}\",\n\t\t\t\t\t\tnew Object[]{connId, sessId, conn});\n\t\t\t}\n\t\t\t// if connection still exists then close it\n\t\t\tif (conn != null && (sessId == null || sessId.equals(conn.getSessionId()))) {\n\t\t\t\tconnectionsByFrom.remove(connId, conn);\n\n\t\t\t\tcloseConnection(conn, connId, null, false);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate class StaleConnectionCloser\n\t\t\textends TimerTask {\n\n\t\tpublic static final int DEF_QUEUE_SIZE = 1000;\n\n\t\tpublic static final long DEF_TIMEOUT = 30 * 1000;\n\n\t\tprivate int maxQueueSize;\n\t\tprivate Set<JID> queueSet;\n\t\tprivate Thread thread;\n\t\tprivate long timeout;\n\t\tprivate Set<JID> workingSet;\n\n\t\tpublic StaleConnectionCloser() {\n\t\t\tthis(DEF_QUEUE_SIZE, DEF_TIMEOUT);\n\t\t}\n\n\t\tpublic StaleConnectionCloser(int queueSize, long timeout) {\n\t\t\tthis.timeout = timeout;\n\t\t\tthis.maxQueueSize = queueSize;\n\t\t\tworkingSet = new HashSet<JID>(queueSize);\n\t\t\tqueueSet = new HashSet<JID>(queueSize);\n\t\t}\n\n\t\tpublic void closeConnections() {\n\n\t\t\t// nothing waiting to remove\n\t\t\tif (workingSet.isEmpty()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlog.log(Level.CONFIG, \"Trying to find and remove stale XMPPResourceConnections\");\n\n\t\t\tLinkedList<XMPPResourceConnection> staleConnections = new LinkedList<XMPPResourceConnection>();\n\n\t\t\tfor (XMPPSession session : sessionsByNodeId.values()) {\n\t\t\t\tList<XMPPResourceConnection> connections = session.getActiveResources();\n\n\t\t\t\tfor (XMPPResourceConnection connection : connections) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tJID connectionId = connection.getConnectionId(false);\n\n\t\t\t\t\t\tif (workingSet.contains(connectionId)) {\n\n\t\t\t\t\t\t\t// queue connection for removal\n\t\t\t\t\t\t\tstaleConnections.offer(connection);\n\n\t\t\t\t\t\t\t// remove from working set\n\t\t\t\t\t\t\tworkingSet.remove(connectionId);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (NoConnectionIdException ex) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"found connection without proper connection id = {0}\",\n\t\t\t\t\t\t\t\tconnection.toString());\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// remove queued connections\n\t\t\t\tXMPPResourceConnection connection;\n\n\t\t\t\twhile ((connection = staleConnections.poll()) != null) {\n\t\t\t\t\tlog.log(Level.FINE, \"Found stale XMPPResourceConnection: {0}, removing...\", connection);\n\t\t\t\t\tsession.removeResourceConnection(connection);\n\t\t\t\t}\n\n\t\t\t\t// working set is empty so break iteration now\n\t\t\t\tif (workingSet.isEmpty()) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic boolean queueForClose(JID connectionId) {\n\t\t\tboolean result;\n\n\t\t\tsynchronized (this) {\n\t\t\t\tif (queueSet.size() > maxQueueSize) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tresult = queueSet.add(connectionId);\n\t\t\t}\n\t\t\tif (!result && log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\"connection with id {0} already queued for removing as stale\" + \" XMPPResourceConnection\",\n\t\t\t\t\t\tconnectionId);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tif ((thread != null) && thread.isAlive()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthread = new Thread() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tprocess();\n\t\t\t\t\tthread = null;\n\t\t\t\t}\n\t\t\t};\n\t\t\tthread.start();\n\t\t}\n\n\t\tpublic int getMaxQueueSize() {\n\t\t\treturn maxQueueSize;\n\t\t}\n\n\t\tpublic void setMaxQueueSize(int queueSize) {\n\t\t\tthis.maxQueueSize = queueSize;\n\t\t}\n\n\t\tpublic long getTimeout() {\n\t\t\treturn timeout;\n\t\t}\n\n\t\tprivate void process() {\n\t\t\ttry {\n\t\t\t\twhile (swapSets()) {\n\t\t\t\t\tcloseConnections();\n\t\t\t\t}\n\t\t\t} catch (Throwable th) {\n\t\t\t\tlog.log(Level.SEVERE, \"exception closing stale connections\", th);\n\t\t\t}\n\t\t\taddTimerTask(this, timeout);\n\t\t}\n\n\t\tprivate boolean swapSets() {\n\t\t\tsynchronized (this) {\n\t\t\t\tSet<JID> tmp = workingSet;\n\n\t\t\t\tworkingSet = queueSet;\n\t\t\t\tqueueSet = tmp;\n\t\t\t\tqueueSet.clear();\n\n\t\t\t\treturn !workingSet.isEmpty();\n\t\t\t}\n\t\t}\n\t}\n\t\n\tpublic interface MessageArchive {\n\n\t\tvoid generateStableId(Packet packet);\n\n\t\tboolean willArchive(Packet packet, XMPPResourceConnection session) throws NotAuthorizedException;\n\n\t\tvoid addStableId(Packet packet, XMPPResourceConnection session);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppsession/SessionManagerConfig.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppsession;\n\n/**\n * Describe class SessionManagerConfig here.\n * <br>\n * Created: Tue Oct 24 23:07:57 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic abstract class SessionManagerConfig {\n\n\tpublic static final String defaultHandlerProcId = \"default-handler\";\n\n\tpublic static final String PLUGINS_CONCURRENCY_PROP_KEY = \"plugins-concurrency\";\n\n\tpublic static final String PLUGINS_CONF_PROP_KEY = \"plugins-conf\";\n\n\tpublic static final String PLUGINS_PROP_KEY = \"plugins\";\n\n\tpublic static final String sessionCloseProcId = \"session-close\";\n\n\tpublic static final String sessionOpenProcId = \"session-open\";\n\tpublic static final String SM_THREADS_POOL_PROP_KEY = \"sm-threads-pool\";\n\tprotected static final String ADMIN_SCRIPTS_PROP_KEY = \"admin-scripts-dir\";\n\tprotected static final String ADMIN_SCRIPTS_PROP_VAL = \"scripts/admin/\";\n\tprotected static final String AUTO_CREATE_OFFLINE_USER_PROP_KEY = \"offline-user-autocreate\";\n\tprotected static final String AUTO_CREATE_OFFLINE_USER_PROP_VAL = \"false\";\n\tprotected static final String FORCE_DETAIL_STALE_CONNECTION_CHECK = \"force-detail-stale-connection-check\";\n\tprotected static final String SKIP_PRIVACY_PROP_KEY = \"skip-privacy\";\n\tprotected static final String SM_THREADS_POOL_CUSTOM_PROP_VAL = \"custom\";\n\tprotected static final String SM_THREADS_POOL_PROP_VAL = \"default\";\n\n\tprotected static final String SM_THREADS_FACTOR_PROP_KEY = \"sm-threads-factor\";\n\tprotected static final int SM_THREADS_FACTOR_PROP_VAL = 1;\n\n\tprotected static final String ACTIVE_USER_TIMEFRAME_KEY = \"active-user-timeframe\";\n\tprotected static final long ACTIVE_USER_TIMEFRAME_VAL = 5 * 60 * 1000;\n\n\tprotected static final String AUTH_TIMEOUT_PROP_KEY = \"auth-timeout\";\n\tprotected static final long AUTH_TIMEOUT_PROP_VAL = 120;\n\n\tprotected static final String STALE_CONNECTION_CLOSER_QUEUE_SIZE_KEY = \"stale-connection-closer-queue-size\";\n\n}    // SessionManagerConfig\n\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppsession/SessionManagerHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppsession;\n\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\n/**\n * Describe interface SessionManagerHandler here.\n * <br>\n * Created: Sat Feb 18 13:27:58 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface SessionManagerHandler {\n\n\tpublic static final String COMMIT_HANDLER_KEY = \"LoginHandlerKey\";\n\n\tJID getComponentId();\n\n\tvoid handleLogin(BareJID userId, XMPPResourceConnection conn);\n\n\tvoid handleDomainChange(String domain, XMPPResourceConnection conn);\n\n\tvoid handleLogout(BareJID userId, XMPPResourceConnection conn);\n\n\tvoid handlePresenceSet(XMPPResourceConnection conn);\n\n\tvoid handleResourceBind(XMPPResourceConnection conn);\n\n\tboolean isLocalDomain(String domain, boolean includeComponents);\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppsession/UserConnectedEvent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppsession;\n\nimport tigase.eventbus.EventBusEvent;\nimport tigase.xmpp.jid.JID;\n\nimport java.io.Serializable;\n\npublic class UserConnectedEvent\n\t\timplements Serializable, EventBusEvent {\n\n\tprivate JID userJid;\n\n\tpublic UserConnectedEvent() {}\n\n\tpublic UserConnectedEvent(JID userJid) {\n\t\tthis.userJid = userJid;\n\t}\n\n\tpublic JID getUserJid() {\n\t\treturn userJid;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppsession/UserPresenceChangedEvent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppsession;\n\nimport tigase.eventbus.EventBusEvent;\nimport tigase.server.Packet;\nimport tigase.xmpp.XMPPSession;\n\n/**\n * UserPresenceChangeEvent is local event (called on local node), which is fired when user changes presence.\n * <br>\n * This event is local only as SessionManagerClustered will forward information to other cluster nodes that presence is\n * changed and on that nodes this event also will be called locally, if and only if on that node is at least one\n * XMPPResouceConnection for same bare jid as client which changed presence.\n *\n * @author andrzej\n */\npublic class UserPresenceChangedEvent implements EventBusEvent {\n\n\t/**\n\t * Packet containing new presence with \"from\" attribute set to full jid of connection which changed presence.\n\t */\n\tprivate final Packet presence;\n\t/**\n\t * Instance of XMPPSesssion for client which bare jid is same as bare jid of from attribute of changed presence.\n\t */\n\tprivate final XMPPSession session;\n\n\tpublic UserPresenceChangedEvent(XMPPSession session, Packet presence) {\n\t\tthis.session = session;\n\t\tthis.presence = presence;\n\t}\n\n\tpublic Packet getPresence() {\n\t\treturn presence;\n\t}\n\n\tpublic XMPPSession getSession() {\n\t\treturn session;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppsession/UserSessionEvent.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppsession;\n\nimport tigase.eventbus.EventBusEvent;\nimport tigase.xmpp.XMPPSession;\nimport tigase.xmpp.jid.JID;\n\nimport java.io.Serializable;\n\n/**\n * Base class for implementation of events related to user session. For this event exists additional routing mechanism\n * which will optimize delivery of this event in clustered environment.\n *\n * @author andrzej\n */\npublic class UserSessionEvent\n\t\timplements Serializable, EventBusEvent {\n\n\tprivate JID sender;\n\tprivate transient XMPPSession session;\n\t// this is destination to which event will be routed\n\tprivate JID userJid;\n\n\tpublic UserSessionEvent() {\n\t}\n\n\tpublic UserSessionEvent(JID sender, JID userJid, XMPPSession session) {\n\t\tthis.sender = sender;\n\t\tthis.session = session;\n\t\tthis.userJid = userJid;\n\t}\n\n\tpublic XMPPSession getSession() {\n\t\treturn session;\n\t}\n\n\tpublic void setSession(XMPPSession session) {\n\t\tthis.session = session;\n\t}\n\n\tpublic JID getUserJid() {\n\t\treturn userJid;\n\t}\n\n\tpublic JID getSender() {\n\t\treturn sender;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppsession/UserSessionEventWithProcessorResultWriter.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppsession;\n\nimport tigase.xmpp.XMPPSession;\nimport tigase.xmpp.jid.JID;\n\npublic class UserSessionEventWithProcessorResultWriter\n\t\textends UserSessionEvent {\n\n\tprivate transient SessionManager.ProcessorResultWriter packetWriter;\n\n\tpublic UserSessionEventWithProcessorResultWriter() {\n\t}\n\n\tpublic UserSessionEventWithProcessorResultWriter(JID sender, JID userJid, XMPPSession session,\n\t\t\t\t\t\t\t\t\t\t\t\t\t SessionManager.ProcessorResultWriter writer) {\n\t\tsuper(sender, userJid, session);\n\t\tpacketWriter = writer;\n\t}\n\n\tpublic SessionManager.ProcessorResultWriter getPacketWriter() {\n\t\treturn packetWriter;\n\t}\n\n\tpublic void setPacketWriter(SessionManager.ProcessorResultWriter packetConsumer) {\n\t\tthis.packetWriter = packetConsumer;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/server/xmppsession/adhoc/SuggestedDomainsListAdhoc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.server.xmppsession.adhoc;\n\nimport tigase.component.adhoc.AdHocCommand;\nimport tigase.component.adhoc.AdHocCommandException;\nimport tigase.component.adhoc.AdHocResponse;\nimport tigase.component.adhoc.AdhHocRequest;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.xmppserver.KnownDomainsListProvider;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.form.Field.fieldTextMulti;\nimport static tigase.server.xmppsession.adhoc.SuggestedDomainsListAdhoc.XMLNS;\n\n/**\n * AdHoc command to retrieve list of all know domains (local and remote)\n *\n * <pre>\n *  {@code\n *  <iq type='set'>\n * \t  <command xmlns='http://jabber.org/protocol/commands' node='tigase:instance:details#domains' action='execute'/>\n * \t</iq>\n *  }\n * \t</pre>\n */\n@Bean(name = XMLNS, parent = SessionManager.class, active = true, exportable = true)\npublic class SuggestedDomainsListAdhoc\n\timplements AdHocCommand {\n\n\tprotected static final String XMLNS = \"tigase:instance:details#domains\";\n\n\tprivate static final Logger log = Logger.getLogger(SuggestedDomainsListAdhoc.class.getCanonicalName());\n\n\t@Inject\n\tprivate KnownDomainsListProvider knownDomainsListProvider;\n\n\tpublic void execute(AdhHocRequest request, AdHocResponse response) throws AdHocCommandException {\n\t\ttry {\n\t\t\tresponse.getElements().add(this.prepareForm().getElement());\n\t\t\tresponse.completeSession();\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.FINE, \"Exception during execution of adhoc command \" + this.getNode(), e);\n\t\t\tthrow new AdHocCommandException(Authorization.INTERNAL_SERVER_ERROR, e.getMessage());\n\t\t}\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn \"Information about domains on this instance\";\n\t}\n\n\t@Override\n\tpublic String getNode() {\n\t\treturn XMLNS;\n\t}\n\n\t@Override\n\tpublic boolean isAllowedFor(JID jid) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean isAllowedFor(JID from, JID to) {\n\t\treturn to == null || from.getBareJID().equals(to.getBareJID());\n\t}\n\n\t@Override\n\tpublic boolean isForSelf() {\n\t\treturn true;\n\t}\n\n\tprotected Form prepareForm() {\n\t\tvar form = new Form(\"form\", \"Know local and remote domains\",\n\t\t                    \"Form used to obtain list of all know local and remote domains\");\n\t\tvar localDomains = knownDomainsListProvider.getAllLocalDomains().toArray(String[]::new);\n\t\tvar knownRemoteDomains = knownDomainsListProvider.getAuthenticatedRemoteDomains().toArray(String[]::new);\n\n\t\tform.addField(fieldTextMulti(\"knownRemoteDomains\", knownRemoteDomains, \"List of known remote domains\"));\n\n\t\tform.addField(fieldTextMulti(\"localDomains\", localDomains, \"List of local instance domains\"));\n\t\treturn form;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/stats/ComponentStatisticsProvider.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\nimport tigase.server.BasicComponent;\n\n/**\n * Created by andrzej on 15.12.2016.\n */\npublic interface ComponentStatisticsProvider\n\t\textends StatisticsProviderIfc {\n\n\tvoid everyHour();\n\n\tvoid everyMinute();\n\n\tvoid everySecond();\n\n\tdefault boolean belongsTo(Class<? extends BasicComponent> component) {\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/stats/Counter.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\nimport java.util.logging.Level;\n\n/**\n * @author andrzej\n */\npublic class Counter\n\t\textends CounterValue {\n\n\tprivate long last_hour_counter = 0;\n\tprivate long last_minute_counter = 0;\n\tprivate long last_second_counter = 0;\n\n\tprivate long per_hour = 0;\n\tprivate long per_minute = 0;\n\tprivate long per_second = 0;\n\n\tpublic Counter(String name, Level level) {\n\t\tsuper(name, level);\n\t}\n\n\tpublic synchronized void everyHour() {\n\t\tper_hour = counter - last_hour_counter;\n\t\tlast_hour_counter = counter;\n\t}\n\n\tpublic synchronized void everyMinute() {\n\t\tper_minute = counter - last_minute_counter;\n\t\tlast_minute_counter = counter;\n\t}\n\n\tpublic synchronized void everySecond() {\n\t\tper_second = counter - last_second_counter;\n\t\tlast_second_counter = counter;\n\t}\n\n\tpublic long getPerHour() {\n\t\treturn per_hour;\n\t}\n\n\tpublic long getPerMinute() {\n\t\treturn per_minute;\n\t}\n\n\tpublic long getPerSecond() {\n\t\treturn per_second;\n\t}\n\n\tpublic void getStatistics(String compName, StatisticsList list) {\n\t\tif (list.checkLevel(level)) {\n\t\t\tlist.add(compName, name + \" total\", counter, level);\n\t\t\tlist.add(compName, name + \" last hour\", per_hour, level);\n\t\t\tlist.add(compName, name + \" last minute\", per_minute, level);\n\t\t\tlist.add(compName, name + \" last second\", per_second, level);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/stats/CounterDataArchivizer.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\nimport tigase.db.DBInitException;\nimport tigase.db.DataRepository;\nimport tigase.db.RepositoryFactory;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.beans.config.ConfigurationChangedAware;\nimport tigase.server.XMPPServer;\nimport tigase.sys.TigaseRuntime;\n\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.text.NumberFormat;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.db.RepositoryFactory.DATA_REPO_POOL_SIZE_PROP_KEY;\n\n/**\n * Created: Mar 25, 2010 8:55:11 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class CounterDataArchivizer\n\t\timplements StatisticsArchivizerIfc, ConfigurationChangedAware, Initializable {\n\n\tpublic static final String DB_URL_PROP_KEY = \"db-url\";\n\tpublic static final String KEY_FIELD_PROP_KEY = \"key-field\";\n\tpublic static final String TABLE_NAME_PROP_KEY = \"table-name\";\n\tpublic static final String VAL_FIELD_PROP_KEY = \"val-field\";\n\tprivate static final String CPU_USAGE_TEXT = \"Usage CPU [%]: \";\n\tprivate static final String DEF_KEY_FIELD_NAME = \"counter_name\";\n\tprivate static final String DEF_TABLE_NAME = \"counter_data\";\n\tprivate static final String DEF_VALUE_FIELD_NAME = \"counter_value\";\n\tprivate static final String MEM_USAGE_TEXT = \"Usage RAM [%]: \";\n\tprivate static final Logger log = Logger.getLogger(CounterDataArchivizer.class.getName());\n\tprivate static final String SERVER_CONNECTIONS_TEXT = \"Connections s2s: \";\n\tprivate static final String UPTIME_TEXT = \"Uptime: \";\n\tprivate static final String USER_CONNECTIONS_TEXT = \"Connections c2s: \";\n\tprivate static final String USER_REGISTERED_TEXT = \"Registered user: \";\n\tprivate static final String VERSION_TEXT = \"Version: \";\n\tprivate static final String VHOSTS_TEXT = \"VHosts: \";\n\n\tprivate String create_table_query = null;\n\tprivate DataRepository data_repo = null;\n\t@ConfigField(desc = \"Database URL\", alias = DB_URL_PROP_KEY)\n\tprivate String databaseUrl = null;\n\t@ConfigField(desc = \"Frequency\")\n\tprivate long frequency = -1;\n\tprivate String init_entry_query = null;\n\t@ConfigField(desc = \"Key field\", alias = KEY_FIELD_PROP_KEY)\n\tprivate String keyField = DEF_KEY_FIELD_NAME;\n\t// private PreparedStatement initEntry = null;\n\t@ConfigField(desc = \"Table name\", alias = TABLE_NAME_PROP_KEY)\n\tprivate String tableName = DEF_TABLE_NAME;\n\tprivate String update_entry_query = null;\n\t// private PreparedStatement updateEntry = null;\n\t@ConfigField(desc = \"Value field\", alias = VAL_FIELD_PROP_KEY)\n\tprivate String valueField = DEF_VALUE_FIELD_NAME;\n\n\t@Override\n\tpublic void execute(StatisticsProvider sp) {\n\t\tNumberFormat format = NumberFormat.getNumberInstance();\n\n\t\tformat.setMaximumFractionDigits(2);\n\t\tinitData(CPU_USAGE_TEXT, format.format(sp.getCPUUsage()));\n\t\tinitData(MEM_USAGE_TEXT, format.format(sp.getHeapMemUsage()));\n\t\tformat = NumberFormat.getIntegerInstance();\n\t\tinitData(USER_REGISTERED_TEXT, format.format(sp.getRegistered()));\n\t\tinitData(USER_CONNECTIONS_TEXT, format.format(sp.getConnectionsNumber()));\n\t\tinitData(SERVER_CONNECTIONS_TEXT, format.format(sp.getServerConnections()));\n\t\tinitData(UPTIME_TEXT, TigaseRuntime.getTigaseRuntime().getUptimeString());\n\t\tinitData(VHOSTS_TEXT, format.format(sp.getStats(\"vhost-man\", \"Number of VHosts\", 0)));\n\t}\n\n\tpublic void initData(String key, String value) {\n\t\ttry {\n\t\t\tPreparedStatement updateEntry = data_repo.getPreparedStatement(null, update_entry_query);\n\t\t\tPreparedStatement initEntry = data_repo.getPreparedStatement(null, init_entry_query);\n\n\t\t\tsynchronized (updateEntry) {\n\t\t\t\tupdateEntry.setString(1, value);\n\t\t\t\tupdateEntry.setString(2, key);\n\t\t\t\tupdateEntry.executeUpdate();\n\t\t\t}\n\t\t\tsynchronized (initEntry) {\n\t\t\t\tinitEntry.setString(1, key);\n\t\t\t\tinitEntry.setString(2, value);\n\t\t\t\tinitEntry.setString(3, key);\n\t\t\t\tinitEntry.executeUpdate();\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tlog.log(Level.WARNING, \"Problem adding new entry to DB: \", e);\n\t\t}\n\t}\n\n\tpublic void initRepository(String conn_str, Map<String, String> params)\n\t\t\tthrows SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException,\n\t\t\t\t   DBInitException {\n\t\tdata_repo = RepositoryFactory.getDataRepository(null, conn_str, params);\n\t\tcheckDB();\n\t\tdata_repo.initPreparedStatement(init_entry_query, init_entry_query);\n\t\tdata_repo.initPreparedStatement(update_entry_query, update_entry_query);\n\t}\n\n\t@Override\n\tpublic void release() {\n\n\t\t// Do nothing for now....\n\t}\n\n\tpublic void updateData(String key, String value) {\n\t\ttry {\n\t\t\tPreparedStatement updateEntry = data_repo.getPreparedStatement(null, update_entry_query);\n\n\t\t\tsynchronized (updateEntry) {\n\t\t\t\tupdateEntry.setString(1, value);\n\t\t\t\tupdateEntry.setString(2, key);\n\t\t\t\tupdateEntry.executeUpdate();\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tlog.log(Level.WARNING, \"Problem adding new entry to DB: \", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void beanConfigurationChanged(Collection<String> changedFields) {\n\t\tlog.log(Level.SEVERE, \"Initialize stats archive, table: {0} \", tableName);\n\t\tinit_entry_query =\n\t\t\t\t\"insert into \" + tableName + \" (\" + keyField + \", \" + valueField + \") \" + \" (select ?, ? from \" +\n\t\t\t\t\t\ttableName + \" where \" + keyField + \" = ? HAVING count(*)=0)\";\n\t\tupdate_entry_query = \"update \" + tableName + \" set \" + valueField + \" = ? where \" + keyField + \" = ?\";\n\t\tcreate_table_query =\n\t\t\t\t\"CREATE TABLE \" + tableName + \" ( \" + keyField + \" varchar(255) NOT NULL DEFAULT '0', \" + valueField +\n\t\t\t\t\t\t\" varchar(255) NOT NULL DEFAULT '0',\" + \"  PRIMARY KEY ( \" + keyField + \" ));\";\n\t\ttry {\n\t\t\tMap<String, String> params = new HashMap<>();\n\t\t\tparams.put(DATA_REPO_POOL_SIZE_PROP_KEY, \"1\");\n\t\t\tinitRepository(databaseUrl, params);\n\t\t\tinitData(VERSION_TEXT, XMPPServer.getImplementationVersion());\n\t\t\tinitData(CPU_USAGE_TEXT, \"0\");\n\t\t\tinitData(MEM_USAGE_TEXT, \"0\");\n\t\t\tinitData(USER_CONNECTIONS_TEXT, \"0\");\n\t\t\tinitData(SERVER_CONNECTIONS_TEXT, \"0\");\n\t\t\tinitData(VHOSTS_TEXT, \"0\");\n\t\t\tinitData(UPTIME_TEXT, TigaseRuntime.getTigaseRuntime().getUptimeString());\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.SEVERE, \"Cannot initialize connection to database: \", ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic long getFrequency() {\n\t\treturn frequency;\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tbeanConfigurationChanged(Collections.emptyList());\n\t}\n\n\tprivate void checkDB() throws SQLException {\n\t\tdata_repo.checkTable(tableName, create_table_query);\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/stats/CounterDataFileLogger.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\nimport tigase.collections.CircularFifoQueue;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.beans.config.ConfigurationChangedAware;\n\nimport java.io.BufferedWriter;\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.*;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\n/**\n * Class responsible for dumping server statistics to a file\n *\n * @author wojtek\n */\npublic class CounterDataFileLogger\n\t\timplements StatisticsArchivizerIfc, ConfigurationChangedAware, Initializable {\n\n\t/* logger instance */\n\tprivate static final Logger log = Logger.getLogger(CounterDataFileLogger.class.getName());\n\tprivate final static ExecutorService service = Executors.newSingleThreadScheduledExecutor();\n\tprivate final AtomicBoolean collectionReady = new AtomicBoolean(false);\n\tprivate Charset charset = StandardCharsets.UTF_8;\n\t/* Field holding datetime format configuration */\n\t@ConfigField(desc = \"Format of a date time\", alias = \"stats-datetime-format\")\n\tprivate String dateTimeFormat = \"yyyy-MM-dd_HH:mm:ss\";\n\tprivate DateTimeFormatter dateTimeFormatter;\n\t/* Field holding directory path configuration */\n\t@ConfigField(desc = \"Directory path\", alias = \"stats-directory\")\n\tprivate String directory = \"logs/stats\";\n\t/* Field holding file prefix configuration */\n\t@ConfigField(desc = \"Name of a file\", alias = \"stats-filename\")\n\tprivate String filename = \"stats\";\n\t@ConfigField(desc = \"Remaining space in MB resulting in shrinking the collection\", alias = \"automatically-prune-resize-mb\")\n\tprivate int freeSpaceMB = 100;\n\t@ConfigField(desc = \"Remaining space in % resulting in shrinking the collection\", alias = \"automatically-prune-resize-percent\")\n\tprivate int freeSpacePerCent = 5;\n\t@ConfigField(desc = \"Frequency\")\n\tprivate long frequency = -1;\n\t/* Field holding configuration whether include or not datetime timestamp into filename */\n\t@ConfigField(desc = \"Should include date time\", alias = \"stats-datetime\")\n\tprivate boolean includeDateTime = true;\n\t/* Field holding configuration whether include or not unixtime into filename */\n\t@ConfigField(desc = \"Should include unix time\", alias = \"stats-unixtime\")\n\tprivate boolean includeUnixTime = true;\n\t@ConfigField(desc = \"Whether old entries should be pruned automatically\", alias = \"automatically-prune-limit\")\n\tprivate int limit = 60 * 60 * 24;\n\tprivate CircularFifoQueue<Path> pathsQueue = null;\n\t@ConfigField(desc = \"Whether old entries should be pruned automatically\", alias = \"automatically-prune-old\")\n\tprivate boolean pruneOldEntries = true;\n\t@ConfigField(desc = \"Factor by which collection will be shrunk\", alias = \"automatically-prune-resize-factor\")\n\tprivate double shrinkFactor = 0.75;\n\t/* Field holding level of the statistics configuration */\n\t@ConfigField(desc = \"Statistics detail level\", alias = \"stats-level\")\n\tprivate Level statsLevel = Level.ALL;\n\n\tprivate static void deleteFile(Path file) {\n\t\ttry {\n\t\t\tif (Files.exists(file)) {\n\t\t\t\tFiles.delete(file);\n\t\t\t}\n\t\t} catch (IOException e) {\n\t\t\tlog.log(Level.FINEST, \"Error deleting file \" + file, e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void execute(StatisticsProvider sp) {\n\n\t\tif (pruneOldEntries && !collectionReady.get()) {\n\t\t\treturn;\n\t\t}\n\n\t\tfinal ZonedDateTime time = ZonedDateTime.now();\n\n\t\tPath path = Paths.get(getPath(time));\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Dumping server statistics to: {0}\", path);\n\t\t}\n\n\t\tMap<String, String> stats = sp.getAllStats(statsLevel.intValue());\n\t\tstats.put(\"Statistics time\", dateTimeFormatter.format(time));\n\t\tstats.put(\"Statistics time (linux)\", Long.toString(time.toInstant().toEpochMilli()));\n\n\t\ttry (BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {\n\t\t\tString result = stats.entrySet()\n\t\t\t\t\t.stream()\n\t\t\t\t\t.map((e) -> e.getKey() + \"\\t\" + e.getValue())\n\t\t\t\t\t.collect(Collectors.joining(\"\\n\"));\n\t\t\twriter.write(result);\n\t\t} catch (IOException ex) {\n\t\t\tlog.log(Level.SEVERE, \"Error dumping server statistics to file\", ex);\n\t\t}\n\n\t\tif (pruneOldEntries) {\n\t\t\tpathsQueue.offer(path);\n\n\t\t\tfinal File file = path.toFile();\n\t\t\tif (pathsQueue.limit() > 0 && (file.getUsableSpace() / 1024 / 1024 < freeSpaceMB ||\n\t\t\t\t\t((file.getUsableSpace() * 100) / file.getTotalSpace() < freeSpacePerCent))) {\n\t\t\t\tint newLimit = (int) (pathsQueue.limit() * shrinkFactor);\n\n\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\"Shrinking stats file history from {0} to {1} (usable space: {2}, total space: {3}\",\n\t\t\t\t\t\tnew Object[]{limit, newLimit, file.getUsableSpace(), file.getTotalSpace()});\n\t\t\t\tlimit = newLimit;\n\t\t\t\tpathsQueue.setLimit(limit);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void release() {\n\t\t// pass\n\t}\n\n\t@Override\n\tpublic void beanConfigurationChanged(Collection<String> changedFields) {\n\t\tPaths.get(directory).toFile().mkdirs();\n\t\tdateTimeFormatter = DateTimeFormatter.ofPattern(dateTimeFormat);\n\n\t\tif (pruneOldEntries) {\n\t\t\tif (pathsQueue == null) {\n\t\t\t\tpathsQueue = new CircularFifoQueue<>(limit, CounterDataFileLogger::deleteFile);\n\n\t\t\t\tfinal Thread thread = new Thread(() -> {\n\t\t\t\t\tfinal long start = System.currentTimeMillis();\n\t\t\t\t\tlog.log(Level.FINE, \"Started collecting existing statistics files\");\n\t\t\t\t\ttry {\n\t\t\t\t\t\tfinal File[] files = Paths.get(directory).toFile().listFiles();\n\t\t\t\t\t\tif (files != null && files.length > 0) {\n\t\t\t\t\t\t\tfinal List<Path> existingFilesPaths = Arrays.stream(files)\n\t\t\t\t\t\t\t\t\t.sorted(Comparator.comparing(File::lastModified))\n\t\t\t\t\t\t\t\t\t.map(File::toPath)\n\t\t\t\t\t\t\t\t\t.collect(Collectors.toList());\n\t\t\t\t\t\t\tpathsQueue.addAll(existingFilesPaths);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Reading statistics files list\", e);\n\t\t\t\t\t}\n\t\t\t\t\tlog.log(Level.CONFIG, \"Statistics files collection finished in: {0}s \",\n\t\t\t\t\t\t\tnew Object[]{(System.currentTimeMillis() - start) / 1000});\n\n\t\t\t\t\tcollectionReady.set(true);\n\t\t\t\t});\n\t\t\t\tthread.setName(\"stats-files-reader\");\n\t\t\t\tthread.start();\n\n\t\t\t} else {\n\t\t\t\tpathsQueue.setLimit(limit);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic long getFrequency() {\n\t\treturn frequency;\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tbeanConfigurationChanged(Collections.emptyList());\n\t}\n\n\tprivate String getPath(ZonedDateTime time) {\n\t\treturn directory + \"/\" + filename + (includeUnixTime ? \"_\" + time.toInstant().toEpochMilli() : \"\") +\n\t\t\t\t(includeDateTime ? \"_\" + dateTimeFormatter.format(time) : \"\") + \".txt\";\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/stats/CounterDataLogger.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\nimport tigase.db.util.RepositoryVersionAware;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.RegistrarBean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.beans.config.ConfigurationChangedAware;\nimport tigase.kernel.core.Kernel;\nimport tigase.stats.db.CounterDataLoggerRepoBean;\nimport tigase.util.dns.DNSResolverFactory;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.logging.Logger;\n\n/**\n * Created: Apr 20, 2010 6:39:05 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\n@Bean(name = \"counter-data-logger\", parent = StatisticsCollector.class, active = false)\npublic class CounterDataLogger\n\t\timplements StatisticsArchivizerIfc, ConfigurationChangedAware, Initializable, RegistrarBean {\n\n\tprivate static final Logger log = Logger.getLogger(CounterDataLogger.class.getName());\n\tprotected static String defaultHostname;\n\t@ConfigField(desc = \"Frequency\")\n\tprivate long frequency = -1;\n\n\tprivate long last_c2s_packets = 0;\n\tprivate long last_ext_packets = 0;\n\tprivate long last_iqs = 0;\n\tprivate long last_messages = 0;\n\tprivate long last_muc_packets = 0;\n\tprivate long last_presences = 0;\n\tprivate long last_pubsub_packets = 0;\n\tprivate long last_s2s_packets = 0;\n\tprivate long last_sm_packets = 0;\n\tprivate long last_ws2s_packets = 0;\n\t@Inject\n\tprivate CounterDataLoggerRepoBean repository;\n\n\t@Override\n\tpublic void beanConfigurationChanged(Collection<String> changedFields) {\n\t\tdefaultHostname = DNSResolverFactory.getInstance().getDefaultHost();\n\t}\n\n\t@Override\n\tpublic void execute(StatisticsProvider sp) {\n\t\tlong c2s_packets = sp.getCompPackets(\"c2s\");\n\t\tlong ws2s_packets = sp.getCompPackets(\"ws2s\");\n\t\tlong ext_packets = sp.getCompPackets(\"ext\");\n\t\tlong iqs = sp.getCompIqs(\"sess-man\");\n\t\tlong messages = sp.getCompMessages(\"sess-man\");\n\t\tlong muc_packets = sp.getCompPackets(\"muc\");\n\t\tlong presences = sp.getCompPresences(\"sess-man\");\n\t\tlong pubsub_packets = sp.getCompPackets(\"pubsub\");\n\t\tlong s2s_packets = sp.getCompPackets(\"s2s\");\n\t\tlong sm_packets = sp.getSMPacketsNumber();\n\t\tint sm_connections = sp.getStats(\"sess-man\", \"Open user connections\", 0);\n\t\tint sm_sessions = sp.getStats(\"sess-man\", \"Open user sessions\", 0);\n\n\t\trepository.addStatsLogEntry(defaultHostname,\n\t\t                            sp.getCPUUsage(),\n\t\t                            sp.getHeapMemUsage(),\n\t\t                            sp.getUptime(),\n\t\t                            sp.getStats(\"vhost-man\", \"Number of VHosts\", 0),\n\t\t                            sm_packets - last_sm_packets,\n\t\t                            muc_packets - last_muc_packets,\n\t\t                            pubsub_packets - last_pubsub_packets,\n\t\t                            c2s_packets - last_c2s_packets,\n\t\t                            ws2s_packets - last_ws2s_packets,\n\t\t                            s2s_packets - last_s2s_packets,\n\t\t                            ext_packets - last_ext_packets,\n\t\t                            presences - last_presences,\n\t\t                            messages - last_messages, iqs - last_iqs,\n\t\t                            sp.getRegistered(),\n\t\t                            sp.getCompConnections(\"c2s\"),\n\t\t                            sp.getCompConnections(\"ws2s\"),\n\t\t                            sp.getCompConnections(\"bosh\"),\n\t\t                            sp.getCompConnections(\"s2s\"),\n\t\t                            sm_connections,\n\t\t                            sm_sessions);\n\t\tlast_c2s_packets = c2s_packets;\n\t\tlast_ws2s_packets = ws2s_packets;\n\t\tlast_ext_packets = ext_packets;\n\t\tlast_iqs = iqs;\n\t\tlast_messages = messages;\n\t\tlast_muc_packets = muc_packets;\n\t\tlast_presences = presences;\n\t\tlast_pubsub_packets = pubsub_packets;\n\t\tlast_s2s_packets = s2s_packets;\n\t\tlast_sm_packets = sm_packets;\n\t}\n\n\t@Override\n\tpublic long getFrequency() {\n\t\treturn frequency;\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tbeanConfigurationChanged(Collections.emptyList());\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\n\t}\n\n\t@Override\n\tpublic void release() {\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/stats/CounterPerSecond.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\nimport java.util.logging.Level;\n\n/**\n * @author andrzej\n */\npublic class CounterPerSecond\n\t\textends CounterValue {\n\n\tprivate long last_second_counter = 0;\n\n\tprivate long per_second = 0;\n\n\tpublic CounterPerSecond(String name, Level level) {\n\t\tsuper(name, level);\n\t}\n\n\tpublic synchronized void everySecond() {\n\t\tper_second = counter - last_second_counter;\n\t\tlast_second_counter = counter;\n\t}\n\n\tpublic long getPerSecond() {\n\t\treturn per_second;\n\t}\n\n\tpublic void getStatistics(String compName, StatisticsList list) {\n\t\tif (list.checkLevel(level)) {\n\t\t\tlist.add(compName, name + \" last second\", per_second, level);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/stats/CounterValue.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\nimport java.util.logging.Level;\n\n/**\n * @author andrzej\n */\npublic class CounterValue {\n\n\tprotected final Level level;\n\tprotected long counter = 0;\n\tprotected String name;\n\n\tpublic CounterValue(String name, Level level) {\n\t\tthis.name = name;\n\t\tthis.level = level;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic void inc() {\n\t\t++counter;\n\t}\n\n\tpublic Level getLevel() {\n\t\treturn level;\n\t}\n\n\tpublic long getValue() {\n\t\treturn counter;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"CounterValue{\" + \"name='\" + name + '\\'' + \", counter=\" + counter + '}';\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/stats/ErrorsStatisticsProvider.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\nimport tigase.server.xmppclient.StreamErrorCounterIOProcessor;\nimport tigase.xmpp.impl.ErrorCounter;\n\nimport javax.management.*;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * @author andrzej\n */\npublic class ErrorsStatisticsProvider\n\t\timplements DynamicMBean {\n\n\tprivate static final String ERRORS_NUMBER = \"ErrorsNumber\";\n\tprivate static final String PER_SECOND = \"PerSecond\";\n\tprivate static final String TOTAL = \"Total\";\n\n\tprivate static final String TYPE_FLOAT = \"java.lang.Float\";\n\tprivate static final String TYPE_LONG = \"java.lang.Long\";\n\tprivate final Map<String, String> attrToKey = new HashMap<>();\n\t// internal variables describing the MBean\n\tprivate final String dClassName = this.getClass().getName();\n\tprivate final MBeanConstructorInfo[] dConstructors = new MBeanConstructorInfo[0];\n\tprivate final String dDescription = \"Error statistics MBean\";\n\tprivate final MBeanOperationInfo[] dOperations = new MBeanOperationInfo[1];\n\tprivate final Map<String, Holder> stats = new ConcurrentHashMap<>();\n\tprivate final Map<String, String> statsKeyToKey = new HashMap<>();\n\tprivate MBeanAttributeInfo[] dAttributes;\n\tprivate MBeanInfo dMBeanInfo = null;\n\tprivate String[] statsKeys;\n\n\tpublic ErrorsStatisticsProvider() {\n\t\tbuildDynamicMBeanInfo();\n\t}\n\n\tpublic void update(StatisticsProvider sp) {\n\t\t// retrieval of statistics data for keys listed in statsKeys\n\t\tMap<String, Object> data = sp.getCurStats(statsKeys);\n\n\t\t// refreshing totals and per second values using retrieved data\n\t\tfor (String statKey : statsKeys) {\n\t\t\tString key = statsKeyToKey.get(statKey);\n\t\t\tlong value = (long) data.getOrDefault(statKey, 0);\n\n\t\t\tHolder holder = stats.get(key);\n\t\t\tif (holder == null) {\n\t\t\t\tholder = new Holder();\n\t\t\t\tstats.put(key, holder);\n\t\t\t}\n\n\t\t\tholder.updateTotal(value);\n\t\t}\n\n\t\tfor (String errorName : StreamErrorCounterIOProcessor.ErrorStatisticsHolder.getErrorNames()) {\n\t\t\tlong total = 0;\n\t\t\tfor (String compName : sp.getCompNames()) {\n\t\t\t\tString key = \"StreamErrorStats/\" + errorName + \"ErrorsNumber\";\n\t\t\t\ttotal += sp.getStats(compName, key, 0L);\n\t\t\t}\n\n\t\t\tHolder holder = stats.get(errorName);\n\t\t\tif (holder == null) {\n\t\t\t\tholder = new Holder();\n\t\t\t\tstats.put(errorName, holder);\n\t\t\t}\n\n\t\t\tholder.updateTotal(total);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Object getAttribute(String attribute)\n\t\t\tthrows AttributeNotFoundException, MBeanException, ReflectionException {\n\t\tboolean total = attribute.endsWith(TOTAL);\n\t\tboolean perSec = attribute.endsWith(PER_SECOND);\n\n\t\tattribute = attrToKey.get(attribute);\n\t\tHolder holder = stats.get(attribute);\n\n\t\tif (holder != null) {\n\t\t\tif (total) {\n\t\t\t\treturn holder.getTotal();\n\t\t\t} else if (perSec) {\n\t\t\t\treturn holder.getPerSecond();\n\t\t\t}\n\t\t}\n\n\t\tthrow new RuntimeOperationsException(new IllegalArgumentException(\"Unknown attribute name \" + attribute),\n\t\t\t\t\t\t\t\t\t\t\t \"Cannot invoke a getter of \" + dClassName);\n\t}\n\n\t@Override\n\tpublic void setAttribute(Attribute attribute)\n\t\t\tthrows AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException {\n\t\tthrow new UnsupportedOperationException(\"Not supported yet.\");\n\t}\n\n\t@Override\n\tpublic AttributeList getAttributes(String[] attributeNames) {\n\t\tif (attributeNames == null) {\n\t\t\tthrow new RuntimeOperationsException(new IllegalArgumentException(\"attributeNames[] cannot be null\"),\n\t\t\t\t\t\t\t\t\t\t\t\t \"Cannot invoke a getter of \" + dClassName);\n\t\t}\n\t\tAttributeList resultList = new AttributeList();\n\n\t\tif (attributeNames.length == 0) {\n\t\t\treturn resultList;\n\t\t}\n\n\t\tfor (String attributeName : attributeNames) {\n\t\t\ttry {\n\t\t\t\tObject value = getAttribute((String) attributeName);\n\t\t\t\tresultList.add(new Attribute(attributeName, value));\n\t\t\t} catch (Exception e) {\n\t\t\t\t// ignoring this exception\n\t\t\t}\n\t\t}\n\t\treturn resultList;\n\t}\n\n\t@Override\n\tpublic AttributeList setAttributes(AttributeList attributes) {\n\t\tthrow new UnsupportedOperationException(\n\t\t\t\t\"Not supported yet.\"); //To change body of generated methods, choose Tools | Templates.\n\t}\n\n\t@Override\n\tpublic Object invoke(String actionName, Object[] params, String[] signature)\n\t\t\tthrows MBeanException, ReflectionException {\n\t\tswitch (actionName) {\n\t\t\tcase \"getAllStats\":\n\t\t\t\treturn getAllStats();\n\t\t\tdefault:\n\t\t\t\tthrow new RuntimeOperationsException(new IllegalArgumentException(\"Unknown method \" + actionName),\n\t\t\t\t\t\t\t\t\t\t\t\t\t \"Cannot invoke a method \" + actionName);\n\t\t}\n\t}\n\n\tpublic Map<String, String> getAllStats() throws MBeanException, ReflectionException {\n\t\tLinkedHashMap<String, String> result = new LinkedHashMap<>();\n\t\tfor (String attr : attrToKey.keySet()) {\n\t\t\ttry {\n\t\t\t\tObject value = getAttribute(attr);\n\t\t\t\tif (value != null) {\n\t\t\t\t\tresult.put(attr, String.valueOf(value));\n\t\t\t\t}\n\t\t\t} catch (AttributeNotFoundException ex) {\n\t\t\t\t// should not happens\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic MBeanInfo getMBeanInfo() {\n\t\treturn dMBeanInfo;\n\t}\n\n\tprivate void buildDynamicMBeanInfo() {\n\t\tList<MBeanAttributeInfo> attrs = new ArrayList<MBeanAttributeInfo>();\n\n\t\tfor (String errorName : ErrorCounter.ErrorStatisticsHolder.getErrorNames()) {\n\t\t\tString attrName = errorName + ERRORS_NUMBER + PER_SECOND;\n\t\t\tattrs.add(\n\t\t\t\t\tnew MBeanAttributeInfo(attrName, TYPE_FLOAT, \"Number of errors \" + errorName + \" per second\", true,\n\t\t\t\t\t\t\t\t\t\t   false, false));\n\t\t\tattrToKey.put(attrName, errorName);\n\t\t\tattrName = errorName + ERRORS_NUMBER + TOTAL;\n\t\t\tattrs.add(new MBeanAttributeInfo(attrName, TYPE_LONG, \"Total number of errors \" + errorName, true, false,\n\t\t\t\t\t\t\t\t\t\t\t false));\n\t\t\tattrToKey.put(attrName, errorName);\n\t\t}\n\n\t\tfor (String errorName : StreamErrorCounterIOProcessor.ErrorStatisticsHolder.getErrorNames()) {\n\t\t\tString attrName = errorName + ERRORS_NUMBER + PER_SECOND;\n\t\t\tattrs.add(\n\t\t\t\t\tnew MBeanAttributeInfo(attrName, TYPE_FLOAT, \"Number of errors \" + errorName + \" per second\", true,\n\t\t\t\t\t\t\t\t\t\t   false, false));\n\t\t\tattrToKey.put(attrName, errorName);\n\t\t\tattrName = errorName + ERRORS_NUMBER + TOTAL;\n\t\t\tattrs.add(new MBeanAttributeInfo(attrName, TYPE_LONG, \"Total number of errors \" + errorName, true, false,\n\t\t\t\t\t\t\t\t\t\t\t false));\n\t\t\tattrToKey.put(attrName, errorName);\n\t\t}\n\n\t\tdAttributes = attrs.toArray(new MBeanAttributeInfo[attrs.size()]);\n\n\t\tdOperations[0] = new MBeanOperationInfo(\"getAllStats\", \"Provides errors statistics\", new MBeanParameterInfo[0],\n\t\t\t\t\t\t\t\t\t\t\t\t\"java.util.Map\", MBeanOperationInfo.INFO);\n\n\t\tdMBeanInfo = new MBeanInfo(dClassName, dDescription, dAttributes, dConstructors, dOperations,\n\t\t\t\t\t\t\t\t   new MBeanNotificationInfo[0]);\n\n\t\tString[] errorNames = ErrorCounter.ErrorStatisticsHolder.getErrorNames();\n\t\tstatsKeys = new String[errorNames.length];\n\t\tfor (int i = 0; i < errorNames.length; i++) {\n\t\t\tString key = \"sess-man/ErrorStats/\" + errorNames[i] + \"ErrorsNumber[L]\";\n\t\t\tstatsKeys[i] = key;\n\t\t\tstatsKeyToKey.put(key, errorNames[i]);\n\t\t}\n\t}\n\n\tprivate static class Holder {\n\n\t\tprivate float perSec = 0;\n\t\tprivate float prevPerSec = 0;\n\t\tprivate long total = 0;\n\n\t\tpublic void updateTotal(long newValue) {\n\t\t\tfloat temp = perSec;\n\t\t\tlong prevTotal = total;\n\t\t\ttotal = newValue;\n\t\t\tperSec = (prevPerSec + (temp * 2f) + (total - prevTotal)) / 4f;\n\t\t\tprevPerSec = temp;\n\t\t}\n\n\t\tpublic long getTotal() {\n\t\t\treturn total;\n\t\t}\n\n\t\tpublic float getPerSecond() {\n\t\t\treturn perSec;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/stats/JMXProxyListener.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\n/**\n * Created: Aug 24, 2009 1:02:08 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface JMXProxyListener {\n\n\tvoid connected(String id, StatisticsProviderMBean bean);\n\n\tvoid disconnected(String id);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/stats/JMXProxyListenerOpt.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\n/**\n * @author Artur Hefczyc Created Jun 3, 2011\n */\npublic interface JMXProxyListenerOpt {\n\n\tvoid connected(String id, JavaJMXProxyOpt bean);\n\n\tvoid disconnected(String id);\n\n\tString[] getDataIds();\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/stats/JavaJMXProxyOpt.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\nimport tigase.util.repository.DataTypes;\n\nimport javax.management.*;\nimport javax.management.remote.JMXConnectionNotification;\nimport javax.management.remote.JMXConnector;\nimport javax.management.remote.JMXConnectorFactory;\nimport javax.management.remote.JMXServiceURL;\nimport java.io.IOException;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * @author Artur Hefczyc Created Jun 3, 2011\n */\npublic class JavaJMXProxyOpt\n\t\timplements NotificationListener {\n\n\tprivate static final Logger log = Logger.getLogger(JavaJMXProxyOpt.class.getName());\n\n\tprivate int cpuNo = 0;\n\tprivate long delay = -1;\n\tprivate Map<String, LinkedList<Object>> history = null;\n\tprivate String hostname = null;\n\tprivate String id = null;\n\tprivate boolean initialized = false;\n\tprivate long interval = -1;\n\tprivate JMXServiceURL jmxUrl = null;\n\tprivate JMXConnector jmxc = null;\n\tprivate Date lastDisconnectTime = null;\n\tprivate List<JMXProxyListenerOpt> listeners = new LinkedList<JMXProxyListenerOpt>();\n\tprivate boolean loadHistory = false;\n\tprivate Set<String> metrics = new LinkedHashSet<String>();\n\tprivate String password = null;\n\tprivate int port = -1;\n\tprivate MBeanServerConnection server = null;\n\tprivate String sysDetails = \"No data yet...\";\n\tprivate StatisticsProviderMBean tigBean = null;\n\tprivate StatisticsUpdater updater = null;\n\tprivate String urlPath = null;\n\tprivate String userName = null;\n\n\tpublic JavaJMXProxyOpt(String id, String hostname, int port, String userName, String password, long delay,\n\t\t\t\t\t\t   long interval, boolean loadHistory) {\n\t\tthis.id = id;\n\t\tthis.hostname = hostname;\n\t\tthis.port = port;\n\t\tthis.userName = userName;\n\t\tthis.password = password;\n\t\tthis.delay = delay;\n\t\tthis.interval = interval;\n\t\tthis.urlPath = \"/jndi/rmi://\" + this.hostname + \":\" + this.port + \"/jmxrmi\";\n\t\tthis.loadHistory = loadHistory;\n\t\tSystem.out.println(\"Created: \" + id + \":\" + hostname + \":\" + port);\n\t}\n\n\tpublic void addJMXProxyListener(JMXProxyListenerOpt listener) {\n\t\tlisteners.add(listener);\n\n\t\tString[] dataIds = listener.getDataIds();\n\n\t\tif ((dataIds != null) && (dataIds.length > 0)) {\n\t\t\tfor (String did : dataIds) {\n\t\t\t\tmetrics.add(did);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void connect() throws Exception {\n\t\tthis.jmxUrl = new JMXServiceURL(\"rmi\", \"\", 0, this.urlPath);\n\n\t\tString[] userCred = new String[]{userName, password};\n\t\tHashMap<String, Object> env = new HashMap<String, Object>();\n\n\t\tenv.put(JMXConnector.CREDENTIALS, userCred);\n\t\tjmxc = JMXConnectorFactory.newJMXConnector(jmxUrl, env);\n\t\tjmxc.addConnectionNotificationListener(this, null, null);\n\t\tjmxc.connect();\n\t}\n\n\t@Override\n\tpublic void handleNotification(Notification notification, Object handback) {\n\t\tif (notification.getType().equals(JMXConnectionNotification.OPENED)) {\n\t\t\tSystem.out.println(\"Connected: \" + id + \":\" + hostname + \":\" + port);\n\t\t\ttry {\n\t\t\t\tserver = jmxc.getMBeanServerConnection();\n\n\t\t\t\tObjectName obn = new ObjectName(StatisticsCollector.STATISTICS_MBEAN_NAME);\n\n\t\t\t\ttigBean = MBeanServerInvocationHandler.newProxyInstance(server, obn, StatisticsProviderMBean.class,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfalse);\n\t\t\t\tif (history == null) {\n\t\t\t\t\tif (loadHistory) {\n\t\t\t\t\t\tString[] metrics_arr = metrics.toArray(new String[metrics.size()]);\n\n\t\t\t\t\t\thistory = tigBean.getStatsHistory(metrics_arr);\n\t\t\t\t\t\tSystem.out.println(hostname + \" loaded history, size: \" +\n\t\t\t\t\t\t\t\t\t\t\t\t   (((history != null) && (history.get(metrics_arr[0]) != null))\n\t\t\t\t\t\t\t\t\t\t\t\t\t? history.get(metrics_arr[0]).size()\n\t\t\t\t\t\t\t\t\t\t\t\t\t: \"null\"));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tSystem.out.println(hostname + \" loading history switched off.\");\n\t\t\t\t\t}\n\t\t\t\t\tif (history == null) {\n\t\t\t\t\t\thistory = new LinkedHashMap<String, LinkedList<Object>>();\n\t\t\t\t\t\tfor (String m : metrics) {\n\t\t\t\t\t\t\tLinkedList<Object> list = new LinkedList<Object>();\n\n\t\t\t\t\t\t\thistory.put(m, list);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tSystem.out.println(hostname + \" history already loaded, skipping.\");\n\t\t\t\t}\n\t\t\t\tfor (JMXProxyListenerOpt jMXProxyListener : listeners) {\n\t\t\t\t\tjMXProxyListener.connected(id, this);\n\t\t\t\t}\n\t\t\t\tstart();\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.WARNING, \"Error handling notification\", e);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\t\tif (notification.getType().equals(JMXConnectionNotification.CLOSED)) {\n\t\t\tserver = null;\n\t\t\ttigBean = null;\n\t\t\tlastDisconnectTime = new Date();\n\t\t\tfor (JMXProxyListenerOpt jMXProxyListener : listeners) {\n\t\t\t\tjMXProxyListener.disconnected(id);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\t\tif (notification.getType().equals(JMXConnectionNotification.FAILED)) {\n\t\t\tSystem.out.println(\"Reconnection to {hostName} failed...\");\n\n\t\t\treturn;\n\t\t}\n\t\tSystem.out.println(\"Unsupported JMX notification: {notification.getType()}\");\n\t}\n\n\tpublic void start() {\n\t\tif (updater == null) {\n\t\t\tupdater = new StatisticsUpdater();\n\t\t\tSystem.out.println(\"Started: \" + id + \":\" + hostname + \":\" + port);\n\t\t}\n\t}\n\n\tpublic void update() {\n\t\tif (tigBean != null) {\n\n\t\t\t// This doesn't ever change so it is enough to query it once\n\t\t\tif (cpuNo == 0) {\n\t\t\t\tcpuNo = tigBean.getCPUsNumber();\n\t\t\t}\n\n\t\t\tMap<String, Object> curMetrics = tigBean.getCurStats(metrics.toArray(new String[metrics.size()]));\n\n\t\t\tif (null == history) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfor (Map.Entry<String, Object> e : curMetrics.entrySet()) {\n\t\t\t\tLinkedList<Object> list = history.get(e.getKey());\n\n\t\t\t\tif (list != null) {\n\t\t\t\t\tlist.add(e.getValue());\n\t\t\t\t\tif (list.size() > 1) {\n\t\t\t\t\t\tlist.removeFirst();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tsysDetails = tigBean.getSystemDetails();\n\t\t\tinitialized = true;\n\t\t}\n\t}\n\n\tpublic Map<String, String> getAllStats(int level) {\n\t\tif (tigBean != null) {\n\t\t\treturn tigBean.getAllStats(level);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic List<String> getComponentsNames() {\n\t\tif (tigBean != null) {\n\t\t\treturn tigBean.getComponentsNames();\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic Map<String, String> getComponentStats(String compName, int level) {\n\t\tif (tigBean != null) {\n\t\t\treturn tigBean.getComponentStats(compName, level);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic String getHostname() {\n\t\treturn hostname;\n\t}\n\n\tpublic String getId() {\n\t\treturn id;\n\t}\n\n\tpublic Object getMetricData(String key) {\n\t\tif (null == history) {\n\t\t\treturn null;\n\t\t}\n\t\tLinkedList<Object> h = history.get(key);\n\n\t\tif (h != null && h.size() > 0) {\n\t\t\treturn h.getLast();\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic Object[] getMetricHistory(String key) {\n\t\tif (null == history) {\n\t\t\treturn null;\n\t\t}\n\t\tList<Object> result = history.get(key);\n\n\t\tif (result != null) {\n\t\t\tswitch (DataTypes.decodeTypeIdFromName(key)) {\n\t\t\t\tcase 'I':\n\t\t\t\t\treturn result.toArray(new Integer[result.size()]);\n\n\t\t\t\tcase 'L':\n\t\t\t\t\treturn result.toArray(new Long[result.size()]);\n\n\t\t\t\tcase 'F':\n\t\t\t\t\treturn result.toArray(new Float[result.size()]);\n\n\t\t\t\tcase 'D':\n\t\t\t\t\treturn result.toArray(new Double[result.size()]);\n\n\t\t\t\tdefault:\n\t\t\t\t\treturn result.toArray(new String[result.size()]);\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic String getSystemDetails() {\n\t\treturn sysDetails;\n\t}\n\n\tpublic boolean isConnected() {\n\t\treturn tigBean != null;\n\t}\n\n\tpublic boolean isInitialized() {\n\t\treturn isConnected() && initialized;\n\t}\n\n\tprivate class StatisticsUpdater {\n\n\t\tprivate Timer updateTimer = null;\n\n\t\tprivate StatisticsUpdater() {\n\t\t\tupdateTimer = new Timer(\"stats-updater\", true);\n\n\t\t\t// updateTimer.scheduleAtFixedRate(new TimerTask() {\n\t\t\tupdateTimer.schedule(new TimerTask() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (server == null) {\n\t\t\t\t\t\t\tconnect();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (server != null) {\n\t\t\t\t\t\t\tupdate();\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (IOException e) {\n\t\t\t\t\t\tThrowable cause = e;\n\n\t\t\t\t\t\twhile (cause.getCause() != null) {\n\t\t\t\t\t\t\tcause = cause.getCause();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tString disconnected = \"\";\n\n\t\t\t\t\t\tif (lastDisconnectTime != null) {\n\t\t\t\t\t\t\tlong disconnectedInterval =\n\t\t\t\t\t\t\t\t\t(System.currentTimeMillis() - lastDisconnectTime.getTime()) / (1000 * 60);\n\n\t\t\t\t\t\t\tdisconnected = \", disconnected: \" + lastDisconnectTime + \", \" + disconnectedInterval +\n\t\t\t\t\t\t\t\t\t\" minutes ago.\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\tlog.log(Level.WARNING, \"{0}, {1}, retrying in {2} seconds{3}\",\n\t\t\t\t\t\t\t\tnew Object[]{cause.getMessage(), hostname, interval / 1000, disconnected});\n\n\t\t\t\t\t\t// log.log(Level.FINEST, e.getMessage(), e);\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Problem retrieving statistics: \", e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}, delay, interval);\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/stats/MaxDailyCounterQueue.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\nimport java.time.LocalDate;\nimport java.util.AbstractQueue;\nimport java.util.ArrayDeque;\nimport java.util.Iterator;\nimport java.util.Optional;\nimport java.util.concurrent.ConcurrentLinkedDeque;\n\n/**\n * A queue implementation which stores highest added value on a given day\n * and has limited size.\n */\npublic class MaxDailyCounterQueue<E extends Number & Comparable<E>>\n\t\textends AbstractQueue<E> {\n\n\tprivate final int maxQueueLength;\n\tprivate ConcurrentLinkedDeque<E> deque = new ConcurrentLinkedDeque<>();\n\tprivate LocalDate lastDailyStatsReset = LocalDate.now();\n\tprivate String toString = \"[]\";\n\n\tpublic MaxDailyCounterQueue(int maxQueueLength) {\n\t\tthis.maxQueueLength = maxQueueLength;\n\t}\n\n\tpublic Optional<E> getMaxValue() {\n\t\treturn getMaxValueInRange(maxQueueLength);\n\t}\n\n\tpublic Optional<E> getMaxValueInRange(int range) {\n\t\trange = Math.min(range, maxQueueLength);\n\n\t\tE result = null;\n\t\tfinal Iterator<E> iterator = deque.descendingIterator();\n\t\twhile (iterator.hasNext() && range > 0) {\n\t\t\trange--;\n\n\t\t\tfinal E next = iterator.next();\n\t\t\tif (result == null || next.compareTo(result) > 0) {\n\t\t\t\tresult = next;\n\t\t\t}\n\t\t}\n\t\treturn Optional.ofNullable(result);\n\t}\n\n\t/**\n\t * Check if <b>any</b> item in the collection surpass the limit\n\t *\n\t * @param limit against which items should be check\n\t *\n\t * @return indicating whether <b>any</b> item in the collection surpass the limit\n\t */\n\tpublic boolean isLimitSurpassed(E limit) {\n\t\treturn isLimitSurpassed(maxQueueLength, limit);\n\t}\n\n\t/**\n\t * Check if <b>any</b> item within range surpass the limit\n\t *\n\t * @param range number of items to check\n\t * @param limit against which items should be check\n\t *\n\t * @return indicating whether <b>any</b> item within range surpass the limit\n\t */\n\tpublic boolean isLimitSurpassed(int range, E limit) {\n\t\treturn getMaxValueInRange(range).filter(e -> e.compareTo(limit) > 0).isPresent();\n\t}\n\n\t/**\n\t * Check if all and every item in the collection surpass the limit\n\t *\n\t * @param limit against which items should be check\n\t *\n\t * @return indicating whether all items in the collection surpass the limit\n\t */\n\tpublic boolean isLimitSurpassedAllItems(E limit) {\n\t\treturn isLimitSurpassedAllItems(maxQueueLength, limit);\n\t}\n\n\t/**\n\t * Check if all and every item within range surpass the limit\n\t *\n\t * @param range number of items to check\n\t * @param limit against which items should be check\n\t *\n\t * @return indicating whether all items <b>within range</b> surpass the limit\n\t */\n\tpublic boolean isLimitSurpassedAllItems(int range, E limit) {\n\t\tboolean result = true;\n\t\trange = Math.min(range, maxQueueLength);\n\n\t\tfinal Iterator<E> iter = deque.descendingIterator();\n\t\twhile (iter.hasNext() && range > 0) {\n\t\t\trange--;\n\n\t\t\tfinal E next = iter.next();\n\t\t\tif (next.compareTo(limit) <= 0) {\n\t\t\t\tresult &= false;\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic Iterator<E> iterator() {\n\t\treturn deque.iterator();\n\t}\n\n\t@Override\n\tpublic boolean offer(E added) {\n\t\tif (isNextItem() || deque.peekLast() == null) {\n\t\t\tdeque.offer(added);\n\t\t} else if (deque.peekLast().compareTo(added) < 0) {\n\t\t\tdeque.pollLast();\n\t\t\tdeque.offer(added);\n\t\t}\n\t\twhile (deque.size() > maxQueueLength) {\n\t\t\tdeque.remove();\n\t\t}\n\t\ttoString = deque.toString();\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic E peek() {\n\t\treturn deque.peek();\n\t}\n\n\t@Override\n\tpublic E poll() {\n\t\treturn deque.poll();\n\t}\n\n\t@Override\n\tpublic int size() {\n\t\treturn deque.size();\n\t}\n\n\tpublic ArrayDeque<E> subQueue(int range) {\n\t\tfinal ArrayDeque<E> result = new ArrayDeque<E>(range);\n\t\trange = Math.min(range, maxQueueLength);\n\n\t\tfinal Iterator<E> iter = deque.descendingIterator();\n\t\twhile (iter.hasNext() && range > 0) {\n\t\t\trange--;\n\t\t\tresult.add(iter.next());\n\t\t}\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn toString;\n\t}\n\n\tprotected boolean isNextItem() {\n\t\tLocalDate now = LocalDate.now();\n\t\tif (now.getYear() != lastDailyStatsReset.getYear() ||\n\t\t\t\tnow.getDayOfYear() != lastDailyStatsReset.getDayOfYear()) {\n\t\t\tlastDailyStatsReset = LocalDate.now();\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/stats/StatRecord.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\nimport tigase.annotations.TigaseDeprecated;\n\nimport java.util.Collection;\nimport java.util.logging.Level;\n\n/**\n * Describe class StatRecord here.\n * <br>\n * Created: Wed Nov 23 21:28:53 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Deprecated\n@TigaseDeprecated(since = \"8.4.0\", note = \"Will be replaced by more efficient statistics storage\")\npublic class StatRecord<E extends Number> {\n\n\tprivate Collection<E> collection = null;\n\tprivate String component = null;\n\tprivate String description = null;\n\tprivate float floatValue = -1f;\n\tprivate int intValue = -1;\n\tprivate Level level = Level.INFO;\n\tprivate long longValue = -1;\n\tprivate boolean nonZero = false;\n\tprivate StatisticType type = StatisticType.OTHER;\n\tprivate String value = null;\n\n\tpublic StatRecord(String comp, String description, String value, Level level) {\n\t\tthis.description = description.intern();\n\t\tif (value != null) {\n\t\t\tthis.value = value.intern();\n\t\t\tthis.nonZero = !value.isEmpty();\n\t\t}\n\t\tthis.level = level;\n\t\tthis.component = comp.intern();\n\t}\n\n\tpublic StatRecord(String comp, String description, int value, Level level) {\n\t\tthis(comp, description, \"\" + value, level);\n\t\tthis.intValue = value;\n\t\tthis.nonZero = (value > 0);\n\t}\n\n\tpublic StatRecord(String comp, StatisticType type, long value, Level level) {\n\t\tthis(comp, type.getDescription(), \"\" + value, level);\n\t\tthis.type = type;\n\t\tthis.longValue = value;\n\t\tthis.nonZero = (value > 0);\n\t}\n\n\tpublic StatRecord(String comp, StatisticType type, int value, Level level) {\n\t\tthis(comp, type.getDescription(), \"\" + value, level);\n\t\tthis.type = type;\n\t\tthis.intValue = value;\n\t\tthis.nonZero = (value > 0);\n\t}\n\n\tpublic StatRecord(String comp, String description, long value, Level level) {\n\t\tthis(comp, description, \"\" + value, level);\n\t\tthis.longValue = value;\n\t\tthis.nonZero = (value > 0);\n\t}\n\n\tStatRecord(String comp, String description, float value, Level level) {\n\t\tthis(comp, description, \"\" + value, level);\n\t\tthis.floatValue = value;\n\t\tthis.nonZero = (value > 0f);\n\t}\n\n\tStatRecord(String comp, String description, Collection<E> value, Level level) {\n\t\tthis(comp, description, (value != null ? value.toString() : \"\"), level);\n\t\tthis.collection = value;\n\t\tthis.nonZero = isCollectionNonZero(collection);\n\t}\n\n\tpublic String getDescription() {\n\t\treturn description;\n\t}\n\n\tpublic String getValue() {\n\t\treturn value;\n\t}\n\n\tpublic StatisticType getType() {\n\t\treturn type;\n\t}\n\n\tpublic Level getLevel() {\n\t\treturn level;\n\t}\n\n\tpublic String getComponent() {\n\t\treturn component;\n\t}\n\n\tpublic long getLongValue() {\n\t\treturn this.longValue;\n\t}\n\n\tpublic int getIntValue() {\n\t\treturn this.intValue;\n\t}\n\n\tpublic Collection<E> getCollection() {\n\t\treturn this.collection;\n\t}\n\n\t;\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(component).append('/').append(description);\n\n\t\tsb.append('[');\n\t\tif (longValue > -1) {\n\t\t\tsb.append('L');\n\t\t} else if (intValue > -1) {\n\t\t\tsb.append('I');\n\t\t} else if (floatValue > -1f) {\n\t\t\tsb.append('F');\n\t\t} else if (collection != null) {\n\t\t\tsb.append('C');\n\t\t} else {\n\t\t\tsb.append('S');\n\t\t}\n\t\tsb.append(']');\n\n\t\tsb.append(\" = \").append(value);\n\t\treturn sb.toString();\n\t}\n\n\tboolean isNonZero() {\n\t\treturn nonZero;\n\t}\n\n\tfloat getFloatValue() {\n\t\treturn this.floatValue;\n\t}\n\n\tprivate boolean isCollectionNonZero(Collection<E> collection) {\n\t\tfor (E e : collection) {\n\t\t\tif (e.byteValue() > 0) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n} // StatRecord\n"
  },
  {
    "path": "src/main/java/tigase/stats/StatisticHolder.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\n/**\n * @author andrzej\n */\npublic interface StatisticHolder {\n\n\tvoid statisticExecutedIn(long executionTime);\n\n\tvoid everyHour();\n\n\tvoid everyMinute();\n\n\tvoid everySecond();\n\n\tvoid getStatistics(String compName, StatisticsList list);\n\n\tvoid setStatisticsPrefix(String prefix);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/stats/StatisticHolderImpl.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\nimport java.util.logging.Level;\n\n/**\n * @author andrzej\n */\npublic class StatisticHolderImpl\n\t\textends Counter\n\t\timplements StatisticHolder {\n\n\t//\tprivate String prefix = null;\n//\t\n//\tprivate long last_hour_packets = 0;\n//\tprivate long last_minute_packets = 0;\n//\tprivate long last_second_packets = 0;\n//\tprivate long packets_per_hour = 0;\n//\tprivate long packets_per_minute = 0;\n//\tprivate long packets_per_second = 0;\n//\tprivate long requestsOk = 0;\n\tprivate long avgProcessingTime = 0;\n\n\tpublic StatisticHolderImpl() {\n\t\tsuper(\"NULL\", Level.FINEST);\n\t}\n\n\tpublic StatisticHolderImpl(String name) {\n\t\tsuper(name, Level.FINEST);\n\t}\n\n\t@Override\n\tpublic void statisticExecutedIn(long executionTime) {\n\t\tavgProcessingTime = (avgProcessingTime + executionTime) / 2;\n\t\tinc();\n\t}\n\n\t@Override\n\tpublic void getStatistics(String compName, StatisticsList list) {\n\t\tsuper.getStatistics(compName, list);\n\t\tlist.add(compName, getName() + \"/Average processing time\", avgProcessingTime, Level.FINE);\n\t}\n\n\t@Override\n\tpublic void setStatisticsPrefix(String prefix) {\n\t\tsetName(prefix);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/stats/StatisticType.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\n/**\n * Describe class StatisticType here.\n * <br>\n * Created: Wed Nov 23 21:20:20 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic enum StatisticType {\n\n\tQUEUE_WAITING(\"Total waiting packets\"),\n\tMAX_QUEUE_SIZE(\"Max queue size\"),\n\tMSG_RECEIVED_OK(\"Packets received\"),\n\tMSG_SENT_OK(\"Packets sent\"),\n\tIN_QUEUE_OVERFLOW(\"IN Queue overflow\"),\n\tOUT_QUEUE_OVERFLOW(\"OUT Queue overflow\"),\n\tOTHER(null);\n\n\tprivate String description = null;\n\n\tprivate StatisticType(String description) {\n\t\tthis.description = description;\n\t}\n\n\tpublic String getDescription() {\n\t\treturn description;\n\t}\n\n} // StatisticType\n"
  },
  {
    "path": "src/main/java/tigase/stats/StatisticsArchivizerIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\n/**\n * Created: Mar 25, 2010 5:10:28 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface StatisticsArchivizerIfc {\n\n\tlong getFrequency();\n\n\tvoid execute(StatisticsProvider sp);\n\n\tvoid release();\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/stats/StatisticsCollector.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.conf.ConfiguratorAbstract;\nimport tigase.disco.ServiceEntity;\nimport tigase.disco.ServiceIdentity;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.RegistrarBean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.*;\nimport tigase.sys.ShutdownHook;\nimport tigase.sys.TigaseRuntime;\nimport tigase.xml.Element;\nimport tigase.xml.XMLUtils;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport javax.management.ObjectName;\nimport java.lang.management.ManagementFactory;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Class StatisticsCollector\n * <br>\n * Created: Tue Nov 22 07:07:11 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"stats\", parent = Kernel.class, active = true)\n@ConfigType({ConfigTypeEnum.DefaultMode, ConfigTypeEnum.SessionManagerMode, ConfigTypeEnum.ConnectionManagersMode,\n\t\t\t ConfigTypeEnum.ComponentMode})\n@Deprecated\n@TigaseDeprecated(since = \"8.4.0\", note = \"Will be replaced by more efficient statistics storage\")\npublic class StatisticsCollector\n\t\textends AbstractComponentRegistrator<StatisticsContainer>\n\t\timplements ShutdownHook, RegistrarBean {\n\n\tpublic static final String ERRORS_STATISTICS_MBEAN_NAME = \"tigase.stats:type=ErrorStatistics\";\n\tpublic static final String STATISTICS_MBEAN_NAME = \"tigase.stats:type=StatisticsProvider\";\n\n\tpublic static final String STATS_HISTORY_SIZE_PROP_KEY = \"stats-history-size\";\n\n\tpublic static final int STATS_HISTORY_SIZE_PROP_VAL = 8640;\n\n\tpublic static final String STATS_UPDATE_INTERVAL_PROP_KEY = \"stats-update-interval\";\n\n\tpublic static final String STATS_HIGH_MEMORY_LEVEL_KEY = \"stats-high-memory-level\";\n\n\tprivate static final String STATS_XMLNS = \"http://jabber.org/protocol/stats\";\n\tprivate static final Logger log = Logger.getLogger(StatisticsCollector.class.getName());\n\n\tprivate final ArchivizerRunner arch_runner = new ArchivizerRunner();\n\tprivate final Timer everyX = new Timer(\"stats-timer\", true);\n\tprivate final Timer statsArchivTasks = new Timer(\"stats-archivizer-tasks\", true);\n\tprivate Map<StatisticsArchivizerIfc, TimerTask> archiverTasks = new ConcurrentHashMap<>();\n\t@Inject(nullAllowed = true)\n\tprivate StatisticsArchivizerIfc[] archivizers = new StatisticsArchivizerIfc[0];\n\tprivate ErrorsStatisticsProvider esp = null;\n\t@ConfigField(desc = \"High memory level\", alias = STATS_HIGH_MEMORY_LEVEL_KEY)\n\tprivate int highMemoryLevel = 95;\n\t@ConfigField(desc = \"History size\", alias = STATS_HISTORY_SIZE_PROP_KEY)\n\tprivate int historySize = 0;\n\tprivate TimerTask initializationCompletedTask = null;\n\tprivate ServiceEntity serviceEntity = null;\n\tprivate StatisticsProvider sp = null;\n\t// private ServiceEntity stats_modules = null;\n\tprivate Level statsLevel = Level.INFO;\n\t@ConfigField(desc = \"Update interval\", alias = STATS_UPDATE_INTERVAL_PROP_KEY)\n\tprivate long updateInterval = 10;\n\n\t@Override\n\tpublic void componentAdded(StatisticsContainer component) {\n\t\tServiceEntity item = serviceEntity.findNode(component.getName());\n\n\t\tif (item == null) {\n\t\t\titem = new ServiceEntity(getName(), component.getName(), \"Component: \" + component.getName());\n\t\t\titem.addFeatures(CMD_FEATURES);\n\t\t\titem.addIdentities(new ServiceIdentity(\"automation\", \"command-node\", \"Component: \" + component.getName()));\n\t\t\tserviceEntity.addItems(item);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void componentRemoved(StatisticsContainer component) {\n\t}\n\n\t@Override\n\tpublic void initializationCompleted() {\n\t\tif (isInitializationComplete()) {\n\n\t\t\t// Do we really need to do this again?\n\t\t\treturn;\n\t\t}\n\t\tsuper.initializationCompleted();\n\t\ttry {\n\t\t\tsp = new StatisticsProvider(this, historySize, updateInterval, highMemoryLevel);\n\n\t\t\tString objName = STATISTICS_MBEAN_NAME;\n\t\t\tObjectName on = new ObjectName(objName);\n\n\t\t\tManagementFactory.getPlatformMBeanServer().registerMBean(sp, on);\n\t\t\tConfiguratorAbstract.putMXBean(objName, sp);\n\n\t\t\tesp = new ErrorsStatisticsProvider();\n\n\t\t\tobjName = ERRORS_STATISTICS_MBEAN_NAME;\n\t\t\ton = new ObjectName(objName);\n\n\t\t\tManagementFactory.getPlatformMBeanServer().registerMBean(esp, on);\n\t\t\tConfiguratorAbstract.putMXBean(objName, esp);\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.SEVERE, \"Can not install Statistics MXBean: \", ex);\n\t\t}\n\t\tTigaseRuntime.getTigaseRuntime().addShutdownHook(this);\n\t\tif (initializationCompletedTask != null) {\n\t\t\tinitializationCompletedTask.run();\n\t\t}\n\t\teveryX.schedule(new TimerTask() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\teverySecond();\n\t\t\t}\n\t\t}, 1000, 1000);\n\t\teveryX.schedule(new TimerTask() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\teveryMinute();\n\t\t\t}\n\t\t}, 1000 * 60, 1000 * 60);\n\t\teveryX.schedule(new TimerTask() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\teveryHour();\n\t\t\t}\n\t\t}, 1000 * 60 * 60, 1000 * 60 * 60);\n\t}\n\n\t@Override\n\tpublic void processPacket(final Packet packet, final Queue<Packet> results) {\n\t\tif (!packet.isCommand() || (packet.getType() == StanzaType.result)) {\n\t\t\treturn;\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"{0} command received: {1}\", new Object[]{packet.getCommand().name(), packet});\n\t\t}\n\n\t\tIq iqc = (Iq) packet;\n\t\tBareJID stanzaFromBare = iqc.getStanzaFrom().getBareJID();\n\t\tJID stanzaFrom = JID.jidInstance(stanzaFromBare);\n\n\t\tif (!isAdmin(stanzaFrom)) {\n\t\t\tPacket result = iqc.commandResult(Command.DataType.result);\n\n\t\t\tCommand.addTextField(result, \"Error\", \"You do not have enough permissions to manage this domain\");\n\t\t\tresults.offer(result);\n\n\t\t\treturn;\n\t\t}\n\t\tswitch (iqc.getCommand()) {\n\t\t\tcase GETSTATS: {\n\n\t\t\t\tElement query = new Element(\"query\", STATS_XMLNS);\n\t\t\t\tStatisticsList stats = getAllStats();\n\n\t\t\t\tif (stats != null) {\n\t\t\t\t\tfor (StatRecord record : stats) {\n\t\t\t\t\t\tElement item = new Element(\"stat\");\n\n\t\t\t\t\t\titem.addAttribute(\"name\", record.getComponent() + \"/\" + record.getDescription());\n\t\t\t\t\t\titem.addAttribute(\"value\", record.getValue());\n\t\t\t\t\t\tquery.addChild(item);\n\t\t\t\t\t}    // end of for ()\n\t\t\t\t}      // end of if (stats != null && stats.count() > 0)\n\n\t\t\t\tPacket result = iqc.okResult(query, 0);\n\n\t\t\t\t// Command.setData(result, statistics);\n\t\t\t\tresults.offer(result);\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase OTHER: {\n\t\t\t\tif (iqc.getStrCommand() == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tString nick = iqc.getTo().getLocalpart();\n\n\t\t\t\tif (!getName().equals(nick)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tCommand.Action action = Command.getAction(iqc);\n\n\t\t\t\tif (action == Command.Action.cancel) {\n\t\t\t\t\tPacket result = iqc.commandResult(null);\n\n\t\t\t\t\tresults.offer(result);\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tString tmp_val = Command.getFieldValue(iqc, \"Stats level\");\n\n\t\t\t\t// copying default value of stats level to local variable to not override default value\n\t\t\t\tLevel statsLevel = this.statsLevel;\n\t\t\t\tif (tmp_val != null) {\n\t\t\t\t\tstatsLevel = Level.parse(tmp_val);\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"statsLevel parsed to: {0}\", statsLevel.getName());\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tStatisticsList list = new StatisticsList(statsLevel);\n\n\t\t\t\tif (iqc.getStrCommand().equals(\"stats\")) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Getting all stats for level: {0}\", statsLevel.getName());\n\t\t\t\t\t}\n\t\t\t\t\tgetAllStats(list);\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"All stats for level loaded: {0}\", statsLevel.getName());\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tString[] spl = iqc.getStrCommand().split(\"/\");\n\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Getting stats for component: {0}, level: {1}\",\n\t\t\t\t\t\t\t\tnew Object[]{spl[1], statsLevel.getName()});\n\t\t\t\t\t}\n\t\t\t\t\tgetComponentStats(spl[1], list);\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Stats loaded for component: {0}, level: {1}\",\n\t\t\t\t\t\t\t\tnew Object[]{spl[1], statsLevel.getName()});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tPacket result = iqc.commandResult(Command.DataType.form);\n\n\t\t\t\tfor (StatRecord rec : list) {\n\t\t\t\t\tCommand.addFieldValue(result, XMLUtils.escape(rec.getComponent() + \"/\" + rec.getDescription()),\n\t\t\t\t\t\t\t\t\t\t  XMLUtils.escape(rec.getValue()));\n\t\t\t\t}\n\n\t\t\t\tCommand.addFieldValue(result, \"Stats level\", statsLevel.getName(), \"Stats level\",\n\t\t\t\t\t\t\t\t\t  new String[]{Level.INFO.getName(), Level.FINE.getName(), Level.FINER.getName(),\n\t\t\t\t\t\t\t\t\t\t\t\t   Level.FINEST.getName()},\n\t\t\t\t\t\t\t\t\t  new String[]{Level.INFO.getName(), Level.FINE.getName(), Level.FINER.getName(),\n\t\t\t\t\t\t\t\t\t\t\t\t   Level.FINEST.getName()});\n\t\t\t\tresults.offer(result);\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Returning stats result: {0}\", result);\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}    // end of switch (packet.getCommand())\n\t}\n\n\t@Override\n\tpublic void release() {\n\t\tsuper.release();\n\t\tsp.stop();\n\t\tstatsArchivTasks.cancel();\n\t}\n\n\t@Override\n\tpublic String shutdown() {\n\t\tStatisticsList allStats = getAllStats();\n\t\tStringBuilder sb = new StringBuilder(4096);\n\n\t\tfor (StatRecord statRecord : allStats) {\n\t\t\tsb.append(statRecord.toString()).append('\\n');\n\t\t}\n\n\t\treturn sb.toString();\n\t}\n\n\tpublic StatisticsList getAllStats() {\n\t\tStatisticsList list = new StatisticsList(Level.ALL);\n\n\t\tgetAllStats(list);\n\n\t\treturn list;\n\t}\n\n\tpublic void getAllStats(StatisticsList list) {\n\t\tfor (StatisticsContainer comp : components.values()) {\n\t\t\tgetComponentStats(comp.getName(), list);\n\t\t}\n\n\t\tgetStatistics(list);\n\n\t\tint totalQueuesWait = 0;\n\t\tlong totalQueuesOverflow = 0;\n\n\t\tfor (StatisticsContainer comp : components.values()) {\n\t\t\ttotalQueuesWait += list.getValue(comp.getName(), \"Total queues wait\", 0);\n\t\t\ttotalQueuesOverflow += list.getValue(comp.getName(), \"Total queues overflow\", 0L);\n\t\t}\n\t\tlist.add(\"total\", \"Total queues wait\", totalQueuesWait, Level.INFO);\n\t\tlist.add(\"total\", \"Total queues overflow\", totalQueuesOverflow, Level.INFO);\n\t}\n\n\tpublic List<String> getComponentsNames() {\n\t\treturn new ArrayList<String>(components.keySet());\n\t}\n\n\tpublic void getComponentStats(String name, StatisticsList list) {\n\t\tStatisticsContainer stats = components.get(name);\n\n\t\tif (stats != null) {\n\t\t\tstats.getStatistics(list);\n\t\t}\n\t}\n\n\t@Override\n\tpublic List<Element> getDiscoFeatures(JID from) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Element getDiscoInfo(String node, JID jid, JID from) {\n\t\tif ((jid != null) && getName().equals(jid.getLocalpart()) && isAdmin(from)) {\n\t\t\treturn serviceEntity.getDiscoInfo(node);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic List<Element> getDiscoItems(String node, JID jid, JID from) {\n\t\tif (isAdmin(from)) {\n\t\t\tif (getName().equals(jid.getLocalpart()) || getComponentId().equals(jid)) {\n\t\t\t\tList<Element> items = serviceEntity.getDiscoItems(node, jid.toString());\n\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Processing discoItems for node: {0}, result: {1}\",\n\t\t\t\t\t\t\tnew Object[]{node, (items == null) ? null : items.toString()});\n\t\t\t\t}\n\n\t\t\t\treturn items;\n\t\t\t} else {\n\t\t\t\tif (node == null) {\n\t\t\t\t\tElement item = serviceEntity.getDiscoItem(null, BareJID.toString(getName(), jid.toString()));\n\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Processing discoItems, result: {0}\",\n\t\t\t\t\t\t\t\t((item == null) ? null : item.toString()));\n\t\t\t\t\t}\n\n\t\t\t\t\treturn Arrays.asList(item);\n\t\t\t\t} else {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn super.getName();\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\tsuper.setName(name);\n\t\tserviceEntity = new ServiceEntity(name, \"stats\", \"Server statistics\");\n\t\tserviceEntity.addIdentities(new ServiceIdentity(\"component\", \"stats\", \"Server statistics\"),\n\t\t\t\t\t\t\t\t\tnew ServiceIdentity(\"automation\", \"command-node\", \"All statistics\"),\n\t\t\t\t\t\t\t\t\tnew ServiceIdentity(\"automation\", \"command-list\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"Statistics retrieving commands\"));\n\t\tserviceEntity.addFeatures(DEF_FEATURES);\n\t\tserviceEntity.addFeatures(CMD_FEATURES);\n\t}\n\n\t@Override\n\tpublic boolean isCorrectType(ServerComponent component) {\n\t\treturn component instanceof StatisticsContainer;\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\n\t}\n\n\tpublic void setArchivizers(StatisticsArchivizerIfc[] archivizers) {\n\t\tif (archivizers == null) {\n\t\t\tarchivizers = new StatisticsArchivizerIfc[0];\n\t\t}\n\t\tList<StatisticsArchivizerIfc> newArchivizers = Arrays.asList(archivizers);\n\t\tArrays.stream(this.archivizers).filter(it -> !newArchivizers.contains(it)).forEach(it -> {\n\t\t\tTimerTask tt = this.archiverTasks.get(it);\n\t\t\tif (tt != null) {\n\t\t\t\ttt.cancel();\n\t\t\t}\n\t\t});\n\t\tList<StatisticsArchivizerIfc> oldArchivizers = Arrays.asList(this.archivizers);\n\t\tthis.archivizers = archivizers;\n\t\tArrays.stream(this.archivizers)\n\t\t\t\t.filter(it -> !oldArchivizers.contains(it))\n\t\t\t\t.filter(it -> it.getFrequency() > 0)\n\t\t\t\t.forEach(it -> {\n\t\t\t\t\tTimerTask tt = new TimerTask() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\tit.execute(sp);\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t\tstatsArchivTasks.schedule(tt, it.getFrequency() * 1000, it.getFrequency() * 1000);\n\t\t\t\t\tthis.archiverTasks.put(it, tt);\n\t\t\t\t});\n\t}\n\n\tprotected void statsUpdated() {\n\t\tsynchronized (arch_runner) {\n\t\t\tarch_runner.notifyAll();\n\t\t}\n\t\tesp.update(sp);\n\t}\n\n\tprivate class ArchivizerRunner\n\t\t\textends Thread {\n\n\t\tprivate boolean stopped = false;\n\n\t\tprivate ArchivizerRunner() {\n\t\t\tsuper(\"stats-archivizer\");\n\t\t\tsetDaemon(true);\n\t\t\tstart();\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\twhile (!stopped) {\n\t\t\t\ttry {\n\t\t\t\t\tsynchronized (this) {\n\t\t\t\t\t\tthis.wait();\n\t\t\t\t\t}\n\t\t\t\t\tArrays.stream(archivizers)\n\t\t\t\t\t\t\t.filter(archiv -> archiv.getFrequency() <= 0)\n\t\t\t\t\t\t\t.forEach(archiv -> archiv.execute(sp));\n\t\t\t\t} catch (InterruptedException ex) {\n\n\t\t\t\t\t// Ignore...\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/stats/StatisticsContainer.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\nimport tigase.server.ServerComponent;\n\npublic interface StatisticsContainer\n\t\textends StatisticsContainerIfc, ServerComponent {\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/stats/StatisticsContainerIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\n/**\n * Interface StatisticsContainerIfc\n * <br>\n * Objects which inherits this type can return runtime statistics. Any object can collect job statistics and\n * implementing this interface guarantees that statistics will be presented in configured way to user who wants to see\n * them.\n * <br>\n * Created: Tue Nov 22 07:07:11 2005\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface StatisticsContainerIfc {\n\n\tpublic String getName();\n\n\tpublic void getStatistics(StatisticsList list);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/stats/StatisticsInvocationHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.UndeclaredThrowableException;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.logging.Level;\n\n/**\n * Generic class used by MD connection pools and repositories to wrap and measure method execution times.\n * <br>\n * Created by andrzej on 15.12.2016.\n */\npublic class StatisticsInvocationHandler<S>\n\t\timplements InvocationHandler {\n\n\tprivate final S instance;\n\tprivate final Class[] monitoredIfcs;\n\tprivate final String name;\n\tprivate final ConcurrentHashMap<Method, MethodStatistics> statistics = new ConcurrentHashMap<>();\n\n\tpublic StatisticsInvocationHandler(String name, S instance, Class... monitoredIfcs) {\n\t\tthis.name = name;\n\t\tthis.instance = instance;\n\t\tthis.monitoredIfcs = monitoredIfcs;\n\t\tfor (Class clazz : this.monitoredIfcs) {\n\t\t\tfor (Method m : clazz.getDeclaredMethods()) {\n\t\t\t\tstatistics.put(m, new MethodStatistics(m));\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void everyHour() {\n\t\tstatistics.values().forEach(MethodStatistics::everyHour);\n\t}\n\n\tpublic void everyMinute() {\n\t\tstatistics.values().forEach(MethodStatistics::everyMinute);\n\t}\n\n\tpublic void everySecond() {\n\t\tstatistics.values().forEach(MethodStatistics::everySecond);\n\t}\n\n\tpublic void getStatistics(String compName, String prefix, StatisticsList list) {\n\t\tString subprefix = (prefix != null) ? (prefix + \"/\" + name) : name;\n\t\tstatistics.values().forEach(methodStatistics -> methodStatistics.getStatistics(compName, subprefix, list));\n\t}\n\n\t@Override\n\tpublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n\t\tMethodStatistics methodStatistics = statistics.get(method);\n\t\tif (methodStatistics != null) {\n\t\t\tlong start = System.currentTimeMillis();\n\n\t\t\ttry {\n\t\t\t\treturn method.invoke(this.instance, args);\n\t\t\t} catch (Throwable ex) {\n\t\t\t\tmethodStatistics.executionFailed();\n\t\t\t\tif (ex instanceof UndeclaredThrowableException) {\n\t\t\t\t\tex = ((UndeclaredThrowableException) ex).getUndeclaredThrowable();\n\t\t\t\t}\n\t\t\t\tif (ex instanceof InvocationTargetException) {\n\t\t\t\t\tthrow ((InvocationTargetException) ex).getTargetException();\n\t\t\t\t} else {\n\t\t\t\t\tthrow ex;\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tmethodStatistics.updateExecutionTime(System.currentTimeMillis() - start);\n\t\t\t}\n\t\t} else {\n\t\t\ttry {\n\t\t\t\treturn method.invoke(this.instance, args);\n\t\t\t} catch (Throwable ex) {\n\t\t\t\tif (ex instanceof UndeclaredThrowableException) {\n\t\t\t\t\tex = ((UndeclaredThrowableException) ex).getUndeclaredThrowable();\n\t\t\t\t}\n\t\t\t\tif (ex instanceof InvocationTargetException) {\n\t\t\t\t\tthrow ((InvocationTargetException) ex).getTargetException();\n\t\t\t\t} else {\n\t\t\t\t\tthrow ex;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static class Statistics {\n\n\t\tprivate final String name;\n\t\tprivate long avgProcessingTime = 0;\n\t\tprivate long exceptions_counter = 0;\n\t\tprivate long executions_counter = 0;\n\t\tprivate long last_hour_counter = 0;\n\t\tprivate long last_minute_counter = 0;\n\t\tprivate long last_second_counter = 0;\n\t\tprivate long per_hour = 0;\n\t\tprivate long per_minute = 0;\n\t\tprivate long per_second = 0;\n\n\t\tpublic Statistics(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\tpublic String getName() {\n\t\t\treturn name;\n\t\t}\n\n\t\tpublic synchronized void everyHour() {\n\t\t\tper_hour = executions_counter - last_hour_counter;\n\t\t\tlast_hour_counter = executions_counter;\n\t\t}\n\n\t\tpublic synchronized void everyMinute() {\n\t\t\tper_minute = executions_counter - last_minute_counter;\n\t\t\tlast_minute_counter = executions_counter;\n\t\t}\n\n\t\tpublic synchronized void everySecond() {\n\t\t\tper_second = executions_counter - last_second_counter;\n\t\t\tlast_second_counter = executions_counter;\n\t\t}\n\n\t\tpublic void getStatistics(String compName, String prefix, StatisticsList list) {\n\t\t\tif (list.checkLevel(Level.FINEST)) {\n\t\t\t\tlist.add(compName, prefix + \"/\" + getName() + \"/Excutions last hour\", per_hour, Level.FINEST);\n\t\t\t\tlist.add(compName, prefix + \"/\" + getName() + \"/Excutions last minute\", per_minute, Level.FINEST);\n\t\t\t\tlist.add(compName, prefix + \"/\" + getName() + \"/Excutions last second\", per_second, Level.FINEST);\n\t\t\t}\n\t\t\tlist.add(compName, prefix + \"/\" + getName() + \"/Average processing time\", avgProcessingTime, Level.FINE);\n\t\t\tlist.add(compName, prefix + \"/\" + getName() + \"/Executions\", executions_counter, Level.FINE);\n\t\t\tlist.add(compName, prefix + \"/\" + getName() + \"/Exceptions during execution\", exceptions_counter, Level.FINE);\n\t\t}\n\n\t\tpublic void updateExecutionTime(long executionTime) {\n\t\t\texecutions_counter++;\n\t\t\tavgProcessingTime = (avgProcessingTime + executionTime) / 2;\n\t\t}\n\n\t\tpublic void executionFailed() {\n\t\t\texceptions_counter++;\n\t\t}\n\t}\n\n\tpublic static class MethodStatistics extends Statistics {\n\n\t\tprivate static String generateMethodName(Method method) {\n\t\t\tStringBuilder sb = new StringBuilder(method.getName());\n\t\t\tsb.append(\"(\");\n\t\t\tboolean first = true;\n\t\t\tfor (Class<?> parameter : method.getParameterTypes()) {\n\t\t\t\tif (first) {\n\t\t\t\t\tfirst = false;\n\t\t\t\t} else {\n\t\t\t\t\tsb.append(\",\");\n\t\t\t\t}\n\t\t\t\tsb.append(parameter.getSimpleName());\n\t\t\t}\n\t\t\tsb.append(\")\");\n\t\t\treturn sb.toString();\n\t\t}\n\n\t\tprivate final Method method;\n\n\t\tpublic MethodStatistics(Method method) {\n\t\t\tsuper(generateMethodName(method));\n\t\t\tthis.method = method;\n\n\t\t}\n\t\t\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/stats/StatisticsList.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.server.QueueType;\nimport tigase.util.repository.DataTypes;\n\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Jul 10, 2009 3:23:23 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Deprecated\n@TigaseDeprecated(since = \"8.4.0\", note = \"Will be replaced by more efficient statistics storage\")\npublic class StatisticsList\n\t\timplements Iterable<StatRecord> {\n\n\tprivate static final Logger log = Logger.getLogger(StatisticsList.class.getName());\n\tprivate final LinkedHashMap<String, LinkedHashMap<String, StatRecord>> stats = new LinkedHashMap<String, LinkedHashMap<String, StatRecord>>();\n\tprivate Level statLevel = Level.ALL;\n\n\tpublic StatisticsList(Level level) {\n\t\tthis.statLevel = level;\n\t}\n\n\tpublic boolean add(String comp, String description, long value, Level recordLevel) {\n\t\treturn addEntry(comp, description, recordLevel, new StatRecord(comp, description, value, recordLevel));\n\t}\n\n\tpublic boolean add(String comp, String description, int value, Level recordLevel) {\n\t\treturn addEntry(comp, description, recordLevel, new StatRecord(comp, description, value, recordLevel));\n\t}\n\n\tpublic boolean add(String comp, String description, String value, Level recordLevel) {\n\t\treturn addEntry(comp, description, recordLevel, new StatRecord(comp, description, value, recordLevel));\n\t}\n\n\tpublic boolean add(String comp, String description, float value, Level recordLevel) {\n\t\treturn addEntry(comp, description, recordLevel, new StatRecord(comp, description, value, recordLevel));\n\t}\n\n\tpublic <E extends Number> boolean add(String comp, String description, Collection<E> value, Level recordLevel) {\n\t\treturn addEntry(comp, description, recordLevel, new StatRecord(comp, description, value, recordLevel));\n\t}\n\n\tpublic LinkedHashMap<String, StatRecord> addCompStats(String comp) {\n\t\tLinkedHashMap<String, StatRecord> compStats = new LinkedHashMap<String, StatRecord>();\n\n\t\tstats.put(comp, compStats);\n\n\t\treturn compStats;\n\t}\n\n\tpublic boolean checkLevel(Level recordLevel) {\n\t\treturn recordLevel.intValue() >= statLevel.intValue();\n\t}\n\n\tpublic boolean checkLevel(Level recordLevel, long value) {\n\t\treturn checkLevel(recordLevel) && (value != 0 || checkLevel(Level.FINEST));\n\n\t}\n\n\tpublic boolean checkLevel(Level recordLevel, StatRecord record) {\n\t\treturn checkLevel(recordLevel) && (record.isNonZero() || checkLevel(Level.FINEST));\n\t}\n\n\tpublic boolean checkLevel(Level recordLevel, int value) {\n\t\treturn checkLevel(recordLevel) && (value != 0 || checkLevel(Level.FINEST));\n\n\t}\n\n\tpublic int getCompConnections(String comp) {\n\t\treturn getValue(comp, \"Open connections\", 0);\n\t}\n\n\tpublic long getCompIq(String comp) {\n\t\treturn getCompIqSent(comp) + getCompIqReceived(comp);\n\t}\n\n\tpublic long getCompIqReceived(String comp) {\n\t\treturn getValue(comp, QueueType.IN_QUEUE.name() + \" processed IQ\", 0L);\n\t}\n\n\tpublic long getCompIqSent(String comp) {\n\t\treturn getValue(comp, QueueType.OUT_QUEUE.name() + \" processed IQ\", 0L);\n\t}\n\n\t/**\n\t * Returns names of every component for which statistics are stored in <code>stats</code> variable\n\t *\n\t */\n\tpublic Set<String> getCompNames() {\n\t\treturn stats.keySet();\n\t}\n\n\tpublic long getCompMsg(String comp) {\n\t\treturn getCompMsgSent(comp) + getCompMsgReceived(comp);\n\t}\n\n\tpublic long getCompMsgReceived(String comp) {\n\t\treturn getValue(comp, QueueType.IN_QUEUE.name() + \" processed messages\", 0L);\n\t}\n\n\tpublic long getCompMsgSent(String comp) {\n\t\treturn getValue(comp, QueueType.OUT_QUEUE.name() + \" processed messages\", 0L);\n\t}\n\n\tpublic long getCompPackets(String comp) {\n\t\treturn getCompSentPackets(comp) + getCompReceivedPackets(comp);\n\t}\n\n\tpublic long getCompPres(String comp) {\n\t\treturn getCompPresSent(comp) + getCompPresReceived(comp);\n\t}\n\n\tpublic long getCompPresReceived(String comp) {\n\t\treturn getValue(comp, QueueType.IN_QUEUE.name() + \" processed presences\", 0L);\n\t}\n\n\tpublic long getCompPresSent(String comp) {\n\t\treturn getValue(comp, QueueType.OUT_QUEUE.name() + \" processed presences\", 0L);\n\t}\n\n\tpublic long getCompReceivedPackets(String comp) {\n\t\treturn getValue(comp, StatisticType.MSG_RECEIVED_OK.getDescription(), 0L);\n\t}\n\n\tpublic long getCompSentPackets(String comp) {\n\t\treturn getValue(comp, StatisticType.MSG_SENT_OK.getDescription(), 0L);\n\t}\n\n\tpublic LinkedHashMap<String, StatRecord> getCompStats(String comp) {\n\t\treturn stats.get(comp);\n\t}\n\n\tpublic long getValue(String comp, String description, long def) {\n\t\tlong result = def;\n\t\tLinkedHashMap<String, StatRecord> compStats = stats.get(comp);\n\n\t\tif (compStats != null) {\n\t\t\tStatRecord rec = compStats.get(description);\n\n\t\t\tif (rec != null) {\n\t\t\t\tresult = rec.getLongValue();\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic float getValue(String comp, String description, float def) {\n\t\tfloat result = def;\n\t\tLinkedHashMap<String, StatRecord> compStats = stats.get(comp);\n\n\t\tif (compStats != null) {\n\t\t\tStatRecord rec = compStats.get(description);\n\n\t\t\tif (rec != null) {\n\t\t\t\tresult = rec.getFloatValue();\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic int getValue(String comp, String description, int def) {\n\t\tint result = def;\n\t\tLinkedHashMap<String, StatRecord> compStats = stats.get(comp);\n\n\t\tif (compStats != null) {\n\t\t\tStatRecord rec = compStats.get(description);\n\n\t\t\tif (rec != null) {\n\t\t\t\tresult = rec.getIntValue();\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic String getValue(String comp, String description, String def) {\n\t\tString result = def;\n\t\tLinkedHashMap<String, StatRecord> compStats = stats.get(comp);\n\n\t\tif (compStats != null) {\n\t\t\tStatRecord rec = compStats.get(description);\n\n\t\t\tif (rec != null) {\n\t\t\t\tresult = rec.getValue();\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic <E> Collection<E> getValue(String comp, String description, Collection<E> def) {\n\t\tCollection<E> result = def;\n\t\tLinkedHashMap<String, StatRecord> compStats = stats.get(comp);\n\n\t\tif (compStats != null) {\n\t\t\tStatRecord rec = compStats.get(description);\n\n\t\t\tif (rec != null) {\n\t\t\t\tresult = rec.getCollection();\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic <E> Collection<E> getCollectionValue(String dataId) {\n\t\tString dataName = DataTypes.stripNameFromTypeId(dataId);\n\t\tint idx = dataName.indexOf('/');\n\t\tString comp = dataName.substring(0, idx);\n\t\tString descr = dataName.substring(idx + 1);\n\t\treturn getCollectionValue(comp, descr, null);\n\t}\n\n\tpublic <E> Collection<E> getCollectionValue(String comp, String description, Collection<E> def) {\n\t\tCollection<E> result = def;\n\t\tLinkedHashMap<String, StatRecord> compStats = stats.get(comp);\n\n\t\tif (compStats != null) {\n\t\t\tStatRecord rec = compStats.get(description);\n\n\t\t\tif (rec != null) {\n\t\t\t\tresult = rec.getCollection();\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic Object getValue(String dataId) {\n\t\tchar dataType = DataTypes.decodeTypeIdFromName(dataId);\n\t\tString dataName = DataTypes.stripNameFromTypeId(dataId);\n\t\tint idx = dataName.indexOf('/');\n\t\tString comp = dataName.substring(0, idx);\n\t\tString descr = dataName.substring(idx + 1);\n//\t\tlog.log(Level.FINEST,\n//\t\t\t\t\"Returning metrics for component: {0}, description: {1} and type: {2}\",\n//\t\t\t\tnew Object[] { comp, descr, dataType });\n\t\tswitch (dataType) {\n\t\t\tcase 'L':\n\t\t\t\treturn getValue(comp, descr, 0l);\n\t\t\tcase 'I':\n\t\t\t\treturn getValue(comp, descr, 0);\n\t\t\tcase 'F':\n\t\t\t\treturn getValue(comp, descr, 0f);\n\t\t\tcase 'C':\n\t\t\t\treturn getCollectionValue(comp, descr, null);\n\t\t\tdefault:\n\t\t\t\treturn getValue(comp, descr, \" \");\n\t\t}\n\t}\n\n\t@Override\n\tpublic Iterator<StatRecord> iterator() {\n\t\treturn new StatsIterator();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn stats.toString();\n\t}\n\n\tprivate boolean addEntry(String comp, String description, Level recordLevel, StatRecord statRecord) {\n\t\tdescription = description.intern();\n\t\tif (checkLevel(recordLevel, statRecord)) {\n\t\t\tLinkedHashMap<String, StatRecord> compStats = stats.get(comp);\n\n\t\t\tif (compStats == null) {\n\t\t\t\tcompStats = addCompStats(comp);\n\t\t\t}\n\n\t\t\tcompStats.put(description, statRecord);\n\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate class StatsIterator\n\t\t\timplements Iterator<StatRecord> {\n\n\t\tIterator<LinkedHashMap<String, StatRecord>> compsIt = stats.values().iterator();\n\t\tIterator<StatRecord> recIt = null;\n\n\t\t@Override\n\t\tpublic boolean hasNext() {\n\t\t\tif ((recIt == null) || !recIt.hasNext()) {\n\t\t\t\tif (compsIt.hasNext()) {\n\t\t\t\t\trecIt = compsIt.next().values().iterator();\n\t\t\t\t} else {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn recIt.hasNext();\n\t\t}\n\n\t\t@Override\n\t\tpublic StatRecord next() throws NoSuchElementException {\n\t\t\tif ((recIt == null) || !recIt.hasNext()) {\n\t\t\t\tif (compsIt.hasNext()) {\n\t\t\t\t\trecIt = compsIt.next().values().iterator();\n\t\t\t\t} else {\n\t\t\t\t\tthrow new NoSuchElementException(\"No more statistics.\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn recIt.next();\n\t\t}\n\n\t\t@Override\n\t\tpublic void remove() {\n\t\t\tthrow new UnsupportedOperationException(\"Not supported yet.\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/stats/StatisticsProvider.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\nimport tigase.server.QueueType;\nimport tigase.sys.TigaseRuntime;\nimport tigase.util.historyCache.AllHistoryCache;\nimport tigase.util.historyCache.FloatHistoryCache;\nimport tigase.util.historyCache.IntHistoryCache;\nimport tigase.util.historyCache.LongHistoryCache;\n\nimport javax.management.*;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Class StatisticsProvider\n *\n * @author kobit\n */\npublic class StatisticsProvider\n\t\textends StandardMBean\n\t\timplements StatisticsProviderMBean {\n\n\tprivate static final Logger log = Logger.getLogger(StatisticsProvider.class.getName());\n\n\t// ~--- fields ---------------------------------------------------------------\n\tprivate StatisticsCache cache = null;\n\tprivate StatisticsCollector theRef;\n\n\tpublic StatisticsProvider(StatisticsCollector theRef, int historySize, long updateInterval, int highMemoryLevel)\n\t\t\tthrows NotCompliantMBeanException {\n\n\t\t// WARNING Uncomment the following call to super() to make this class\n\t\t// compile (see BUG ID 122377)\n\t\tsuper(StatisticsProviderMBean.class, false);\n\t\tthis.theRef = theRef;\n\t\tcache = new StatisticsCache(historySize, updateInterval, highMemoryLevel);\n\t}\n\n\tpublic void stop() {\n\t\tif (cache != null) {\n\t\t\tcache.stop();\n\t\t}\n\t}\n\n\t@Override\n\tpublic Map<String, String> getAllStats(int level) {\n//\t\tif (log.isLoggable(Level.FINEST)) {\n//\t\t\tlog.log(Level.FINEST, \"Get all stats called for level: {0}\", level);\n//\t\t}\n\n\t\tStatisticsList list = new StatisticsList(Level.parse(\"\" + level));\n\n\t\ttheRef.getAllStats(list);\n\n\t\treturn getMapFromList(list);\n\t}\n\n\t@Override\n\tpublic int getCLIOQueueSize() {\n\t\treturn cache.clIOQueue;\n\t}\n\n\t@Override\n\tpublic float[] getCLPacketsPerSecHistory() {\n\t\treturn (cache.clpacks_history != null) ? cache.clpacks_history.getCurrentHistory() : null;\n\t}\n\n\t@Override\n\tpublic int getCLQueueSize() {\n\t\treturn cache.clQueue;\n\t}\n\n\t@Override\n\tpublic int getClusterCacheSize() {\n\n\t\t// cache.updateIfOlder(1000);\n\t\treturn cache.clusterCache;\n\t}\n\n\t@Override\n\tpublic float getClusterCompressionRatio() {\n\t\treturn cache.clusterCompressionRatio;\n\t}\n\n\t@Override\n\tpublic long getClusterNetworkBytes() {\n\t\treturn cache.clusterNetworkBytes;\n\t}\n\n\t@Override\n\tpublic float getClusterNetworkBytesPerSecond() {\n\t\treturn cache.clusterNetworkBytesPerSecond;\n\t}\n\n\t@Override\n\tpublic long getClusterPackets() {\n\n\t\t// cache.updateIfOlder(1000);\n\t\treturn cache.clusterPackets;\n\t}\n\n\t@Override\n\tpublic float getClusterPacketsPerSec() {\n\t\treturn cache.clusterPacketsPerSec;\n\t}\n\n\tpublic int getCompConnections(String comp) {\n\t\treturn cache.allStats.getCompConnections(comp);\n\t}\n\n\tpublic long getCompIqs(String comp) {\n\t\treturn cache.allStats.getCompIq(comp);\n\t}\n\n\tpublic long getCompMessages(String comp) {\n\t\treturn cache.allStats.getCompMsg(comp);\n\t}\n\n\t@Override\n\tpublic List<String> getComponentsNames() {\n\t\treturn theRef.getComponentsNames();\n\t}\n\n\t@Override\n\tpublic Map<String, String> getComponentStats(String compName, int level) {\n\t\tStatisticsList list = new StatisticsList(Level.parse(\"\" + level));\n\n\t\ttheRef.getComponentStats(compName, list);\n\n\t\treturn getMapFromList(list);\n\t}\n\n\tpublic long getCompPackets(String comp) {\n\t\treturn cache.allStats.getCompPackets(comp);\n\t}\n\n\tpublic long getCompPresences(String comp) {\n\t\treturn cache.allStats.getCompPres(comp);\n\t}\n\n\t@Override\n\tpublic int getConnectionsNumber() {\n\n\t\t// cache.updateIfOlder(1000);\n\t\treturn cache.clientConnections;\n\t}\n\n\t@Override\n\tpublic int[] getConnectionsNumberHistory() {\n\t\treturn (cache.conns_history != null) ? cache.conns_history.getCurrentHistory() : null;\n\t}\n\n\t@Override\n\tpublic int getCPUsNumber() {\n\t\treturn TigaseRuntime.getTigaseRuntime().getCPUsNumber();\n\t}\n\n\t@Override\n\tpublic float getCPUUsage() {\n\t\treturn cache.cpuUsage;\n\t}\n\n\t@Override\n\tpublic float[] getCPUUsageHistory() {\n\t\treturn (cache.cpu_usage_history != null) ? cache.cpu_usage_history.getCurrentHistory() : null;\n\t}\n\n\tpublic Map<String, Object> getCurStats(String[] statsKeys) {\n\t\tMap<String, Object> result = new LinkedHashMap<String, Object>();\n\n\t\tfor (String key : statsKeys) {\n\t\t\tresult.put(key, cache.allStats.getValue(key));\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic long getDirectMemUsed() {\n\t\treturn TigaseRuntime.getTigaseRuntime().getDirectMemUsed();\n\t}\n\n\t@Override\n\tpublic long[] getDirectMemUsedHistory() {\n\t\treturn (cache.direct_used_history != null) ? cache.direct_used_history.getCurrentHistory() : null;\n\t}\n\n\t@Override\n\tpublic float getHeapMemUsage() {\n\t\treturn TigaseRuntime.getTigaseRuntime().getHeapMemUsage();\n\t}\n\n\t@Override\n\tpublic float[] getHeapUsageHistory() {\n\t\treturn (cache.heap_usage_history != null) ? cache.heap_usage_history.getCurrentHistory() : null;\n\t}\n\n\t@Override\n\tpublic long getIQAuthNumber() {\n\n\t\t// cache.updateIfOlder(1000);\n\t\treturn cache.iqAuthNumber;\n\t}\n\n\t@Override\n\tpublic long getIQOtherNumber() {\n\n\t\t// cache.updateIfOlder(1000);\n\t\treturn cache.iqOtherNumber;\n\t}\n\n\t@Override\n\tpublic float getIQOtherNumberPerSec() {\n\t\treturn cache.iqOtherNumberPerSec;\n\t}\n\n\t@Override\n\tpublic MBeanInfo getMBeanInfo() {\n\t\tMBeanInfo mbinfo = super.getMBeanInfo();\n\n\t\treturn new MBeanInfo(mbinfo.getClassName(), mbinfo.getDescription(), mbinfo.getAttributes(),\n\t\t\t\t\t\t\t mbinfo.getConstructors(), mbinfo.getOperations(), getNotificationInfo());\n\t}\n\n\t@Override\n\tpublic long getMessagesNumber() {\n\n\t\t// cache.updateIfOlder(1000);\n\t\treturn cache.messagesNumber;\n\t}\n\n\t@Override\n\tpublic float getMessagesNumberPerSec() {\n\t\treturn cache.messagesPerSec;\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn theRef.getName();\n\t}\n\n\t@Override\n\tpublic float getNonHeapMemUsage() {\n\t\treturn TigaseRuntime.getTigaseRuntime().getNonHeapMemUsage();\n\t}\n\n\tpublic MBeanNotificationInfo[] getNotificationInfo() {\n\t\treturn new MBeanNotificationInfo[]{};\n\t}\n\n\t@Override\n\tpublic long getPresencesNumber() {\n\n\t\t// cache.updateIfOlder(1000);\n\t\treturn cache.presencesNumber;\n\t}\n\n\t@Override\n\tpublic float getPresencesNumberPerSec() {\n\t\treturn cache.presencesPerSec;\n\t}\n\n\t@Override\n\tpublic long getProcesCPUTime() {\n\t\treturn TigaseRuntime.getTigaseRuntime().getProcessCPUTime();\n\t}\n\n\t@Override\n\tpublic long getQueueOverflow() {\n\n\t\t// cache.updateIfOlder(1000);\n\t\treturn cache.queueOverflow;\n\t}\n\n\t@Override\n\tpublic int getQueueSize() {\n\n\t\t// cache.updateIfOlder(1000);\n\t\treturn cache.queueSize;\n\t}\n\n\tpublic long getRegistered() {\n\t\treturn cache.registered;\n\t}\n\n\t@Override\n\tpublic int getServerConnections() {\n\n\t\t// cache.updateIfOlder(1000);\n\t\treturn cache.serverConnections;\n\t}\n\n\t@Override\n\tpublic int[] getServerConnectionsHistory() {\n\t\treturn (cache.server_conns_history != null) ? cache.server_conns_history.getCurrentHistory() : null;\n\t}\n\n\t@Override\n\tpublic long getSMPacketsNumber() {\n\t\treturn cache.smPackets;\n\t}\n\n\t@Override\n\tpublic float getSMPacketsNumberPerSec() {\n\t\treturn cache.smPacketsPerSec;\n\t}\n\n\t@Override\n\tpublic float[] getSMPacketsPerSecHistory() {\n\t\treturn (cache.smpacks_history != null) ? cache.smpacks_history.getCurrentHistory() : null;\n\t}\n\n\t@Override\n\tpublic int getSMQueueSize() {\n\t\treturn cache.smQueue;\n\t}\n\n\tpublic long getStats(String cmp_name, String stat, long def) {\n\t\treturn cache.allStats.getValue(cmp_name, stat, def);\n\t}\n\n\tpublic float getStats(String cmp_name, String stat, float def) {\n\t\treturn cache.allStats.getValue(cmp_name, stat, def);\n\t}\n\n\tpublic String getStats(String cmp_name, String stat, String def) {\n\t\treturn cache.allStats.getValue(cmp_name, stat, def);\n\t}\n\n\tpublic int getStats(String cmp_name, String stat, int def) {\n\t\treturn cache.allStats.getValue(cmp_name, stat, def);\n\t}\n\n\tpublic Map<String, LinkedList<Object>> getStatsHistory(String[] statsKeys) {\n\t\tlog.log(Level.CONFIG, \"Generating history for metrics: {0}\", Arrays.toString(statsKeys));\n\n\t\tMap<String, LinkedList<Object>> result = null;\n\n\t\tif (cache.allHistory != null) {\n\t\t\tresult = new LinkedHashMap<String, LinkedList<Object>>();\n\t\t\tfor (StatisticsList stHist : cache.allHistory.getCurrentHistory()) {\n\t\t\t\tfor (String key : statsKeys) {\n\t\t\t\t\tLinkedList<Object> statsForKey = result.get(key);\n\n\t\t\t\t\tif (statsForKey == null) {\n\t\t\t\t\t\tstatsForKey = new LinkedList<Object>();\n\t\t\t\t\t\tresult.put(key, statsForKey);\n\t\t\t\t\t}\n\t\t\t\t\tstatsForKey.add(stHist.getValue(key));\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.CONFIG, \"The server does not keep metrics history.\");\n\t\t}\n\n\t\t// log.log(Level.INFO, \"History generated: {0}\", result);\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic String getSystemDetails() {\n\n\t\t// cache.updateIfOlder(1000);\n\t\treturn cache.systemDetails;\n\t}\n\n\t@Override\n\tpublic long getUptime() {\n\t\treturn TigaseRuntime.getTigaseRuntime().getUptime();\n\t}\n\n\t@Override\n\tprotected String getDescription(MBeanInfo info) {\n\t\treturn \"Provides the Tigase server statistics\";\n\t}\n\n\t@Override\n\tprotected String getDescription(MBeanAttributeInfo info) {\n\t\tString description = null;\n\n\t\tif (info.getName().equals(\"AllStats\")) {\n\t\t\tdescription = \"Collection of statistics from all components.\";\n\t\t} else {\n\t\t\tif (info.getName().equals(\"ComponentsNames\")) {\n\t\t\t\tdescription = \"List of components names for which statistics are available\";\n\t\t\t} else {\n\t\t\t\tif (info.getName().equals(\"Name\")) {\n\t\t\t\t\tdescription = \"This is a component name - name of the statistics collector component,\";\n\t\t\t\t} else {\n\t\t\t\t\tif (info.getName().equals(\"getUptime\")) {\n\t\t\t\t\t\tdescription = \"Returns JVM uptime.\";\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (info.getName().equals(\"getProcesCPUTime\")) {\n\t\t\t\t\t\t\tdescription = \"Returns JMV process CPU time.\";\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn description;\n\t}\n\n\t@Override\n\tprotected String getDescription(MBeanOperationInfo op, MBeanParameterInfo param, int sequence) {\n\t\tif (op.getName().equals(\"getAllStats\")) {\n\t\t\tswitch (sequence) {\n\t\t\t\tcase 0:\n\t\t\t\t\treturn \"Statistics level, 0 - All, 500 - Medium, 800 - Minimal\";\n\n\t\t\t\tdefault:\n\t\t\t\t\treturn null;\n\t\t\t}\n\t\t} else {\n\t\t\tif (op.getName().equals(\"getComponentStats\")) {\n\t\t\t\tswitch (sequence) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\treturn \"The component name to provide statistics for\";\n\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\treturn \"Statistics level, 0 - All, 500 - Medium, 800 - Minimal\";\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tprotected String getDescription(MBeanOperationInfo info) {\n\t\tString description = null;\n\t\tMBeanParameterInfo[] params = info.getSignature();\n\t\tString[] signature = new String[params.length];\n\n\t\tfor (int i = 0; i < params.length; i++) {\n\t\t\tsignature[i] = params[i].getType();\n\t\t}\n\n\t\tString[] methodSignature;\n\n\t\tmethodSignature = new String[]{java.lang.Integer.TYPE.getName()};\n\t\tif (info.getName().equals(\"getAllStats\") && Arrays.equals(signature, methodSignature)) {\n\t\t\tdescription = \"Provides statistics for all components for a given level.\";\n\t\t}\n\t\tmethodSignature = new String[]{java.lang.String.class.getName(), java.lang.Integer.TYPE.getName()};\n\t\tif (info.getName().equals(\"getComponentStats\") && Arrays.equals(signature, methodSignature)) {\n\t\t\tdescription = \"Provides statistics for a given component name and statistics level.\";\n\t\t}\n\n\t\treturn description;\n\t}\n\n\t@Override\n\tprotected String getParameterName(MBeanOperationInfo op, MBeanParameterInfo param, int sequence) {\n\t\tif (op.getName().equals(\"getAllStats\")) {\n\t\t\tswitch (sequence) {\n\t\t\t\tcase 0:\n\t\t\t\t\treturn \"level\";\n\n\t\t\t\tdefault:\n\t\t\t\t\treturn null;\n\t\t\t}\n\t\t} else {\n\t\t\tif (op.getName().equals(\"getComponentStats\")) {\n\t\t\t\tswitch (sequence) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\treturn \"compName\";\n\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\treturn \"level\";\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprotected Set<String> getCompNames() {\n\t\treturn cache.allStats.getCompNames();\n\t}\n\n\tprivate Map<String, String> getMapFromList(StatisticsList list) {\n\t\tif (list != null) {\n\t\t\tMap<String, String> result = new LinkedHashMap<String, String>(300);\n\n\t\t\tfor (StatRecord rec : list) {\n\t\t\t\tString key = rec.getComponent() + \"/\" + rec.getDescription();\n\t\t\t\tString value = rec.getValue();\n\n\t\t\t\tresult.put(key, value);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t// ~--- inner classes --------------------------------------------------------\n\tprivate class StatisticsCache {\n\n\t\tprivate static final String WS2S_COMP = \"ws2s\";\n\t\tprivate static final String BOSH_COMP = \"bosh\";\n\t\tprivate static final String C2S_COMP = \"c2s\";\n\t\tprivate static final String CL_COMP = \"cl-comp\";\n\n\t\t// private static final int HISTORY_SIZE = 8640;\n\t\tprivate static final int HISTORY_SIZE = 30;\n\t\tprivate static final String S2S_COMP = \"s2s\";\n\t\tprivate static final long SECOND = 1000;\n\t\tprivate static final String SM_COMP = \"sess-man\";\n\t\tprivate static final long MINUTE = 60 * SECOND;\n\t\tprivate static final long HOUR = 60 * MINUTE;\n\n\t\tprivate AllHistoryCache allHistory = null;\n\t\t// ~--- fields -------------------------------------------------------------\n\t\tprivate int clIOQueue = 0;\n\t\tprivate int clQueue = 0;\n\t\tprivate int clientConnections = 0;\n\t\tprivate FloatHistoryCache clpacks_history = null;\n\t\tprivate int clusterCache = 0;\n\t\tprivate float clusterCompressionRatio = 0f;\n\t\tprivate long clusterNetworkBytes = 0L;\n\t\tprivate float clusterNetworkBytesPerSecond = 0L;\n\t\tprivate long clusterNetworkBytesReceived = 0L;\n\t\tprivate long clusterNetworkBytesSent = 0L;\n\t\tprivate long clusterPackets = 0L;\n\t\tprivate float clusterPacketsPerSec = 0;\n\t\tprivate long clusterPacketsReceived = 0L;\n\t\tprivate long clusterPacketsSent = 0L;\n\t\tprivate int cnt = 0;\n\t\tprivate IntHistoryCache conns_history = null;\n\t\tprivate float cpuUsage = 0f;\n\t\tprivate FloatHistoryCache cpu_usage_history = null;\n\t\tprivate LongHistoryCache direct_used_history = null;\n\t\tprivate FloatHistoryCache heap_usage_history = null;\n\t\tprivate int inter = 10;\n\t\tprivate long iqAuthNumber = 0;\n\t\tprivate long iqOtherNumber = 0;\n\t\tprivate float iqOtherNumberPerSec = 0;\n\t\tprivate String largeQueues = \"\";\n\t\tprivate long lastPresencesReceived = 0;\n\t\tprivate long lastPresencesSent = 0;\n\t\t// private long lastUpdate = 0;\n\t\tprivate Level level = Level.FINER;\n\t\t//\t\tprivate Level level = Level.FINEST;\n\t\tprivate StatisticsList allStats = new StatisticsList(level);\n\t\tprivate long messagesNumber = 0;\n\t\tprivate float messagesPerSec = 0;\n\t\tprivate long presencesNumber = 0;\n\t\tprivate float presencesPerSec = 0;\n\t\tprivate long presences_received_per_update = 0;\n\t\tprivate long presences_sent_per_update = 0;\n\t\tprivate long prevClusterNetworkBytes = 0L;\n\t\tprivate float prevClusterNetworkBytesPerSecond = 0L;\n\t\tprivate long prevClusterPackets = 0L;\n\t\tprivate float prevClusterPacketsPerSec = 0;\n\t\tprivate float prevCpuUsage = 0f;\n\t\tprivate float prevIqOtherNumber = 0;\n\t\tprivate float prevIqOtherNumberPerSec = 0;\n\t\tprivate long prevMessagesNumber = 0;\n\t\tprivate float prevMessagesPerSec = 0;\n\t\tprivate long prevPresencesNumber = 0;\n\t\tprivate float prevPresencesPerSec = 0;\n\t\tprivate long prevSmPackets = 0;\n\t\tprivate float prevSmPacketsPerSec = 0;\n\t\tprivate long queueOverflow = 0;\n\t\tprivate int queueSize = 0;\n\t\tprivate long registered = 0;\n\t\tprivate int runs_counter = 100;\n\t\tprivate int serverConnections = 0;\n\t\tprivate IntHistoryCache server_conns_history = null;\n\t\tprivate long smPackets = 0;\n\t\tprivate float smPacketsPerSec = 0;\n\t\tprivate int smQueue = 0;\n\t\tprivate FloatHistoryCache smpacks_history = null;\n\t\tprivate String systemDetails = \"\";\n\t\tprivate Timer updateTimer = null;\n\n\t\t// ~--- constructors -------------------------------------------------------\n\t\tprivate StatisticsCache(int historySize, long cacheUpdate, int highMemoryLevel) {\n\t\t\tif (historySize > 0) {\n\t\t\t\tsmpacks_history = new FloatHistoryCache(historySize);\n\t\t\t\tserver_conns_history = new IntHistoryCache(historySize);\n\t\t\t\theap_usage_history = new FloatHistoryCache(historySize);\n\t\t\t\tcpu_usage_history = new FloatHistoryCache(historySize);\n\t\t\t\tconns_history = new IntHistoryCache(historySize);\n\t\t\t\tclpacks_history = new FloatHistoryCache(historySize);\n\t\t\t\tdirect_used_history = new LongHistoryCache(historySize);\n\t\t\t\tallHistory = new AllHistoryCache(historySize, highMemoryLevel);\n\t\t\t}\n\t\t\tupdateTimer = new Timer(\"stats-cache\", true);\n\t\t\tupdateTimer.scheduleAtFixedRate(new TimerTask() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tupdate();\n\t\t\t\t\t\tupdateSystemDetails();\n\t\t\t\t\t\ttheRef.statsUpdated();\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Problem retrieving statistics: \", e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}, 10 * 1000, cacheUpdate * 1000);\n\t\t}\n\n\t\tpublic void stop() {\n\t\t\tupdateTimer.cancel();\n\t\t}\n\n\t\tprivate void update() {\n//\t\t\tif (log.isLoggable(Level.FINEST)) {\n//\t\t\t\tlog.log(Level.FINEST, \"Update started\");\n//\t\t\t}\n\n\t\t\tfloat temp = cpuUsage;\n\n\t\t\tcpuUsage = (prevCpuUsage + (temp * 2) + TigaseRuntime.getTigaseRuntime().getCPUUsage()) / 4;\n\t\t\tif (cpu_usage_history != null) {\n\t\t\t\tcpu_usage_history.addItem(cpuUsage);\n\t\t\t}\n\t\t\tprevCpuUsage = temp;\n\t\t\tif (heap_usage_history != null) {\n\t\t\t\theap_usage_history.addItem(getHeapMemUsage());\n\t\t\t}\n\t\t\tif (direct_used_history != null) {\n\t\t\t\tdirect_used_history.addItem(getDirectMemUsed());\n\t\t\t}\n\t\t\tif (++runs_counter >= 100) {\n\t\t\t\tallStats = new StatisticsList(Level.FINEST);\n\t\t\t\truns_counter = 0;\n\t\t\t} else {\n\t\t\t\tallStats = new StatisticsList(level);\n\t\t\t}\n\t\t\ttheRef.getAllStats(allStats);\n\t\t\tif (allHistory != null) {\n\t\t\t\tallHistory.addItem(allStats);\n\t\t\t}\n\n\t\t\tlong tmp_reg = allStats.getValue(SM_COMP, \"Registered accounts\", -1L);\n\n\t\t\tif (tmp_reg > 0) {\n\t\t\t\tregistered = tmp_reg;\n\t\t\t}\n\n\t\t\t// System.out.println(allStats.toString());\n\t\t\tclusterCompressionRatio = (allStats.getValue(CL_COMP, \"Average compression ratio\", -1f) +\n\t\t\t\t\tallStats.getValue(CL_COMP, \"Average decompression ratio\", -1f)) / 2f;\n\t\t\tclusterPacketsReceived = allStats.getCompReceivedPackets(CL_COMP);\n\t\t\tclusterPacketsSent = allStats.getCompSentPackets(CL_COMP);\n\t\t\tclusterPackets = clusterPacketsSent + clusterPacketsReceived;\n\t\t\ttemp = clusterPacketsPerSec;\n\t\t\tclusterPacketsPerSec =\n\t\t\t\t\t(prevClusterPacketsPerSec + (temp * 2f) + (clusterPackets - prevClusterPackets)) / 4f;\n\t\t\tif (clpacks_history != null) {\n\t\t\t\tclpacks_history.addItem(clusterPacketsPerSec);\n\t\t\t}\n\t\t\tprevClusterPacketsPerSec = temp;\n\t\t\tprevClusterPackets = clusterPackets;\n\t\t\tsmPackets = allStats.getCompPackets(SM_COMP);\n\t\t\ttemp = smPacketsPerSec;\n\t\t\tsmPacketsPerSec = (prevSmPacketsPerSec + (temp * 2f) + (smPackets - prevSmPackets)) / 4f;\n\t\t\tif (smpacks_history != null) {\n\t\t\t\tsmpacks_history.addItem(smPacketsPerSec);\n\t\t\t}\n\t\t\tprevSmPacketsPerSec = temp;\n\t\t\tprevSmPackets = smPackets;\n\t\t\tclientConnections = allStats.getCompConnections(C2S_COMP) + allStats.getCompConnections(BOSH_COMP) +\n\t\t\t\t\tallStats.getCompConnections(WS2S_COMP);\n\t\t\tif (conns_history != null) {\n\t\t\t\tconns_history.addItem(clientConnections);\n\t\t\t}\n\t\t\tserverConnections = allStats.getCompConnections(S2S_COMP);\n\t\t\tif (server_conns_history != null) {\n\t\t\t\tserver_conns_history.addItem(serverConnections);\n\t\t\t}\n\t\t\tclIOQueue = allStats.getValue(CL_COMP, \"Waiting to send\", 0);\n\t\t\tclusterCache = allStats.getValue(\"cl-caching-strat\", \"Cached JIDs\", 0);\n\t\t\tmessagesNumber = allStats.getCompMsg(SM_COMP);\n\t\t\ttemp = messagesPerSec;\n\t\t\tmessagesPerSec = (prevMessagesPerSec + (temp * 2f) + (messagesNumber - prevMessagesNumber)) / 4f;\n\t\t\tprevMessagesPerSec = temp;\n\t\t\tprevMessagesNumber = messagesNumber;\n\n\t\t\tiqAuthNumber = allStats.getValue(SM_COMP, QueueType.OUT_QUEUE.name() + \" processed IQ jabber:iq:auth\", 0L) +\n\t\t\t\t\tallStats.getValue(SM_COMP, QueueType.IN_QUEUE.name() + \" processed IQ jabber:iq:auth\", 0L) +\n\t\t\t\t\tallStats.getValue(SM_COMP,\n\t\t\t\t\t\t\t\t\t  QueueType.OUT_QUEUE.name() + \" processed other urn:ietf:params:xml:ns:xmpp-sasl\",\n\t\t\t\t\t\t\t\t\t  0L) + allStats.getValue(SM_COMP, QueueType.IN_QUEUE.name() +\n\t\t\t\t\t\" processed other urn:ietf:params:xml:ns:xmpp-sasl\", 0L);\n\n\t\t\tiqOtherNumber = allStats.getCompIq(SM_COMP);\n\n\t\t\ttemp = iqOtherNumberPerSec;\n\t\t\tiqOtherNumberPerSec = (prevIqOtherNumberPerSec + (temp * 2f) + (iqOtherNumber - prevIqOtherNumber)) / 4f;\n\t\t\tprevIqOtherNumberPerSec = temp;\n\t\t\tprevIqOtherNumber = iqOtherNumber;\n\n\t\t\tclusterNetworkBytesSent = allStats.getValue(CL_COMP, \"Bytes sent\", 0L);\n\t\t\tclusterNetworkBytesReceived = allStats.getValue(CL_COMP, \"Bytes received\", 0L);\n\t\t\tclusterNetworkBytes = clusterNetworkBytesSent + clusterNetworkBytesReceived;\n\t\t\ttemp = clusterNetworkBytesPerSecond;\n\t\t\tclusterNetworkBytesPerSecond =\n\t\t\t\t\t(prevClusterNetworkBytesPerSecond + (temp * 2f) + (clusterNetworkBytes - prevClusterNetworkBytes)) /\n\t\t\t\t\t\t\t4f;\n\t\t\tprevClusterNetworkBytesPerSecond = temp;\n\t\t\tprevClusterNetworkBytes = clusterNetworkBytes;\n\n\t\t\tlong currPresencesReceived = allStats.getCompPresReceived(SM_COMP);\n\t\t\tlong currPresencesSent = allStats.getCompPresSent(SM_COMP);\n\n\t\t\tpresencesNumber = currPresencesReceived + currPresencesSent;\n\t\t\ttemp = presencesPerSec;\n\t\t\tpresencesPerSec = (prevPresencesPerSec + (temp * 2f) + (presencesNumber - prevPresencesNumber)) / 4f;\n\t\t\tprevPresencesPerSec = temp;\n\t\t\tprevPresencesNumber = presencesNumber;\n\t\t\tif (++cnt >= inter) {\n\t\t\t\tpresences_sent_per_update = (currPresencesSent - lastPresencesSent) / 10;\n\t\t\t\tpresences_received_per_update = (currPresencesReceived - lastPresencesReceived) / 10;\n\t\t\t\tlastPresencesSent = currPresencesSent;\n\t\t\t\tlastPresencesReceived = currPresencesReceived;\n\t\t\t\tcnt = 0;\n\t\t\t}\n\t\t\tqueueSize = 0;\n\t\t\tqueueOverflow = 0;\n\t\t\tsmQueue = 0;\n\t\t\tclQueue = 0;\n\t\t\tlargeQueues = \"\";\n\t\t\tfor (StatRecord rec : allStats) {\n\t\t\t\tif ((rec.getDescription() == StatisticType.IN_QUEUE_OVERFLOW.getDescription()) ||\n\t\t\t\t\t\t(rec.getDescription() == StatisticType.OUT_QUEUE_OVERFLOW.getDescription())) {\n\t\t\t\t\tqueueOverflow += rec.getLongValue();\n\t\t\t\t}\n\t\t\t\tif ((rec.getDescription() == \"Total In queues wait\") ||\n\t\t\t\t\t\t(rec.getDescription() == \"Total Out queues wait\")) {\n\t\t\t\t\tqueueSize += rec.getIntValue();\n\t\t\t\t\tif (rec.getComponent().equals(SM_COMP)) {\n\t\t\t\t\t\tsmQueue += rec.getIntValue();\n\t\t\t\t\t}\n\t\t\t\t\tif (rec.getComponent().equals(CL_COMP)) {\n\t\t\t\t\t\tclQueue += rec.getIntValue();\n\t\t\t\t\t}\n\t\t\t\t\tif (rec.getIntValue() > 10000) {\n\t\t\t\t\t\tlargeQueues += rec.getComponent() + \" - queue size: \" + rec.getIntValue() + \"\\n\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void updateSystemDetails() {\n\t\t\tStringBuilder sb = new StringBuilder();\n\n\t\t\tsb.append(\"Uptime: \").append(TigaseRuntime.getTigaseRuntime().getUptimeString());\n\n\t\t\tint cpu_temp = allStats.getValue(\"cpu-mon\", \"CPU temp\", 0);\n\n\t\t\tif (cpu_temp > 0) {\n\t\t\t\tsb.append(\",      Temp: \").append(cpu_temp).append(\" C\");\n\t\t\t}\n\n\t\t\tString cpu_freq = allStats.getValue(\"cpu-mon\", \"CPU freq\", \"\");\n\n\t\t\tif (cpu_freq != null && !cpu_freq.isEmpty()) {\n\t\t\t\tsb.append(\"\\nFreq: \").append(cpu_freq);\n\t\t\t}\n\n\t\t\tString cpu_throt = allStats.getValue(\"cpu-mon\", \"CPU throt\", \"\");\n\n\t\t\tif (cpu_throt != null && !cpu_throt.isEmpty()) {\n\t\t\t\tsb.append(\"\\nThrott: \").append(cpu_throt);\n\t\t\t}\n\t\t\tsb.append(\"\\nTop threads:\");\n\n\t\t\tString cpu_thread = allStats.getValue(\"cpu-mon\", \"1st max CPU thread\", \"\");\n\n\t\t\tif (cpu_thread != null && !cpu_thread.isEmpty()) {\n\t\t\t\tsb.append(\"\\n   \").append(cpu_thread);\n\t\t\t}\n\t\t\tcpu_thread = allStats.getValue(\"cpu-mon\", \"2nd max CPU thread\", \"\");\n\t\t\tif (cpu_thread != null && !cpu_thread.isEmpty()) {\n\t\t\t\tsb.append(\"\\n   \").append(cpu_thread);\n\t\t\t}\n\t\t\tcpu_thread = allStats.getValue(\"cpu-mon\", \"3rd max CPU thread\", \"\");\n\t\t\tif (cpu_thread != null && !cpu_thread.isEmpty()) {\n\t\t\t\tsb.append(\"\\n   \").append(cpu_thread);\n\t\t\t}\n\t\t\tcpu_thread = allStats.getValue(\"cpu-mon\", \"4th max CPU thread\", \"\");\n\t\t\tif (cpu_thread != null && !cpu_thread.isEmpty()) {\n\t\t\t\tsb.append(\"\\n   \").append(cpu_thread);\n\t\t\t}\n\t\t\tcpu_thread = allStats.getValue(\"cpu-mon\", \"5th max CPU thread\", \"\");\n\t\t\tif (cpu_thread != null && !cpu_thread.isEmpty()) {\n\t\t\t\tsb.append(\"\\n   \").append(cpu_thread);\n\t\t\t}\n\n\t\t\tLinkedHashMap<String, StatRecord> compStats = allStats.getCompStats(SM_COMP);\n\n\t\t\tif (compStats != null) {\n\t\t\t\tfor (StatRecord rec : compStats.values()) {\n\t\t\t\t\tif (rec.getDescription().startsWith(\"Processor:\")) {\n\t\t\t\t\t\tsb.append(\"\\n\").append(rec.getDescription()).append(rec.getValue());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tsb.append(\"\\nSM presences rec Tot: \").append(lastPresencesReceived);\n\t\t\tsb.append(\" / \").append(presences_received_per_update).append(\" last sec\");\n\t\t\tsb.append(\"\\nSM presences sent Tot: \").append(lastPresencesSent);\n\t\t\tsb.append(\" / \").append(presences_sent_per_update).append(\" last sec\");\n\t\t\tsb.append(\"\\nCluster bytes/sec: \").append(clusterNetworkBytesPerSecond);\n\t\t\tsb.append(\", compress: \").append(clusterCompressionRatio);\n\t\t\tsb.append(\"\\nCluster bytes: [S] \").append(clusterNetworkBytesSent);\n\t\t\tsb.append(\" / [R] \").append(clusterNetworkBytesReceived);\n\t\t\tsb.append(\"\\nCluster packets: [S] \").append(clusterPacketsSent);\n\t\t\tsb.append(\" / [R] \").append(clusterPacketsReceived);\n\t\t\tif (!largeQueues.isEmpty()) {\n\t\t\t\tsb.append(\"\\n\").append(largeQueues);\n\t\t\t}\n\t\t\tsystemDetails = sb.toString();\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/stats/StatisticsProviderIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\n/**\n * Created by andrzej on 28.04.2016.\n */\npublic interface StatisticsProviderIfc {\n\n\tvoid getStatistics(String compName, StatisticsList list);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/stats/StatisticsProviderMBean.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Interface StatisticsProviderMBean\n *\n * @author kobit\n */\npublic interface StatisticsProviderMBean {\n\n\t/**\n\t * Operation exposed for management\n\t *\n\t * @param level Statistics level, 0 - All, 500 - Medium, 800 - Minimal\n\t *\n\t * @return {@code java.util.Map<String, String>}\n\t */\n\tpublic Map<String, String> getAllStats(int level);\n\n\tpublic int getCLIOQueueSize();\n\n\tpublic float[] getCLPacketsPerSecHistory();\n\n\tpublic int getCLQueueSize();\n\n\tpublic int getClusterCacheSize();\n\n\tpublic float getClusterCompressionRatio();\n\n\tpublic long getClusterNetworkBytes();\n\n\tpublic float getClusterNetworkBytesPerSecond();\n\n\tpublic long getClusterPackets();\n\n\tpublic float getClusterPacketsPerSec();\n\n\t/**\n\t * Get Attribute exposed for management\n\t *\n\t * @return a value of {@code List<String>}\n\t */\n\tpublic List<String> getComponentsNames();\n\n\t/**\n\t * Operation exposed for management\n\t *\n\t * @param compName The component name to provide statistics for\n\t * @param level Statistics level, 0 - All, 500 - Medium, 800 - Minimal\n\t *\n\t * @return {@code java.util.Map<String, String>}\n\t */\n\tpublic Map<String, String> getComponentStats(String compName, int level);\n\n\tpublic int getConnectionsNumber();\n\n\tpublic int[] getConnectionsNumberHistory();\n\n\tpublic int getCPUsNumber();\n\n\tpublic float getCPUUsage();\n\n\tpublic float[] getCPUUsageHistory();\n\n\tpublic Map<String, Object> getCurStats(String[] statsKeys);\n\n\tpublic long getDirectMemUsed();\n\n\tpublic long[] getDirectMemUsedHistory();\n\n\tpublic float getHeapMemUsage();\n\n\tpublic float[] getHeapUsageHistory();\n\n\tpublic long getIQAuthNumber();\n\n\tpublic long getIQOtherNumber();\n\n\tpublic float getIQOtherNumberPerSec();\n\n\tpublic long getMessagesNumber();\n\n\tpublic float getMessagesNumberPerSec();\n\n\tpublic String getName();\n\n\tpublic float getNonHeapMemUsage();\n\n\tpublic long getPresencesNumber();\n\n\tpublic float getPresencesNumberPerSec();\n\n\tpublic long getProcesCPUTime();\n\n\tpublic long getQueueOverflow();\n\n\tpublic int getQueueSize();\n\n\tpublic int getServerConnections();\n\n\tpublic int[] getServerConnectionsHistory();\n\n\tpublic long getSMPacketsNumber();\n\n\tpublic float getSMPacketsNumberPerSec();\n\n\tpublic float[] getSMPacketsPerSecHistory();\n\n\tpublic int getSMQueueSize();\n\n\tpublic Map<String, LinkedList<Object>> getStatsHistory(String[] statsKeys);\n\n\tpublic String getSystemDetails();\n\n\tpublic long getUptime();\n}\n"
  },
  {
    "path": "src/main/java/tigase/stats/db/CounterDataLoggerRepoBean.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats.db;\n\nimport tigase.db.DBInitException;\nimport tigase.db.DataSource;\nimport tigase.db.DataSourceHelper;\nimport tigase.db.beans.MDRepositoryBean;\nimport tigase.db.beans.MDRepositoryBeanWithStatistics;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.core.Kernel;\nimport tigase.stats.CounterDataLogger;\n\n@Bean(name = \"repository\", active = true, parent = CounterDataLogger.class)\npublic class CounterDataLoggerRepoBean\n\t\textends MDRepositoryBeanWithStatistics<CounterDataLoggerRepositoryIfc>\n\t\timplements CounterDataLoggerRepositoryIfc {\n\n\tpublic CounterDataLoggerRepoBean() {\n\t\tsuper(CounterDataLoggerRepositoryIfc.class\n\t\t);\n\t}\n\n\t@Override\n\tpublic void addStatsLogEntry(String hostname, float cpu_usage, float mem_usage, long uptime, int vhosts,\n\t                             long sm_packets, long muc_packets, long pubsub_packets, long c2s_packets,\n\t                             long ws2s_packets, long s2s_packets, long ext_packets, long presences, long messages,\n\t                             long iqs, long registered, int c2s_conns, int ws2s_conns, int bosh_conns,\n\t                             int s2s_conns, int sm_sessions, int sm_connections) {\n\t\tgetRepository(\"default\").addStatsLogEntry(hostname, cpu_usage, mem_usage, uptime, vhosts, sm_packets,\n\t\t                                          muc_packets, pubsub_packets, c2s_packets, ws2s_packets, s2s_packets,\n\t\t                                          ext_packets, presences, messages, iqs, registered, c2s_conns,\n\t\t                                          ws2s_conns, bosh_conns, s2s_conns, sm_sessions, sm_connections);\n\t}\n\n\t@Override\n\tprotected Class<? extends CounterDataLoggerRepositoryIfc> findClassForDataSource(DataSource dataSource)\n\t\t\tthrows DBInitException {\n\t\treturn DataSourceHelper.getDefaultClass(CounterDataLoggerRepositoryIfc.class, dataSource.getResourceUri());\n\t}\n\n\t@Override\n\tpublic Class<?> getDefaultBeanClass() {\n\t\treturn CounterDataLoggerConfigBean.class;\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\t\tsuper.register(kernel);\n\t}\n\n\t@Override\n\tpublic void setDataSource(DataSource ds) {\n\t\t// nothing to do..\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\t\tsuper.unregister(kernel);\n\t}\n\n\tpublic static class CounterDataLoggerConfigBean\n\t\t\textends MDRepositoryConfigBean<CounterDataLoggerRepositoryIfc> {\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/stats/db/CounterDataLoggerRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats.db;\n\nimport tigase.db.DataRepository;\nimport tigase.db.Repository;\nimport tigase.db.util.RepositoryVersionAware;\n\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.util.Objects;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Repository.Meta(supportedUris = {\"jdbc:[^:]+:.*\"})\n@Repository.SchemaId(id = \"counter_data_logger\", name = \"DB Counter Data Logger\")\n@RepositoryVersionAware.SchemaVersion(version = \"0.0.1\")\npublic class CounterDataLoggerRepository\n\t\timplements CounterDataLoggerRepositoryIfc<DataRepository>, RepositoryVersionAware {\n\n\tprivate static final String STATS_INSERT = \"insert into tig_stats_log (hostname,cpu_usage,mem_usage,uptime,vhosts,sm_packets,muc_packets,pubsub_packets,c2s_packets,ws2s_packets,s2s_packets,ext_packets,presences,messages,iqs,registered,c2s_conns,ws2s_conns,bosh_conns,s2s_conns,sm_sessions,sm_connections) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)\";\n\n\tprivate final Logger log = Logger.getLogger(CounterDataLoggerRepository.class.getName());\n\tprivate DataRepository repository;\n\n\t@Override\n\tpublic void addStatsLogEntry(String hostname, float cpu_usage, float mem_usage, long uptime, int vhosts,\n\t                             long sm_packets, long muc_packets, long pubsub_packets, long c2s_packets,\n\t                             long ws2s_packets, long s2s_packets, long ext_packets, long presences,\n\t                             long messages, long iqs, long registered, int c2s_conns, int ws2s_conns,\n\t                             int bosh_conns, int s2s_conns, int sm_sessions, int sm_connections) {\n\n\t\tObjects.requireNonNull(hostname);\n\n\t\ttry {\n\t\t\tPreparedStatement insert_stats = repository.getPreparedStatement(null, STATS_INSERT);\n\n\t\t\tsynchronized (insert_stats) {\n\t\t\t\tinsert_stats.setString(1, hostname);\n\t\t\t\tinsert_stats.setFloat(2, ((cpu_usage >= 0f) ? cpu_usage : 0f));\n\t\t\t\tinsert_stats.setFloat(3, mem_usage);\n\t\t\t\tinsert_stats.setLong(4, uptime);\n\t\t\t\tinsert_stats.setInt(5, vhosts);\n\t\t\t\tinsert_stats.setLong(6, sm_packets);\n\t\t\t\tinsert_stats.setLong(7, muc_packets);\n\t\t\t\tinsert_stats.setLong(8, pubsub_packets);\n\t\t\t\tinsert_stats.setLong(9, c2s_packets);\n\t\t\t\tinsert_stats.setLong(10, ws2s_packets);\n\t\t\t\tinsert_stats.setLong(11, s2s_packets);\n\t\t\t\tinsert_stats.setLong(12, ext_packets);\n\t\t\t\tinsert_stats.setLong(13, presences);\n\t\t\t\tinsert_stats.setLong(14, messages);\n\t\t\t\tinsert_stats.setLong(15, iqs);\n\t\t\t\tinsert_stats.setLong(16, registered);\n\t\t\t\tinsert_stats.setInt(17, c2s_conns);\n\t\t\t\tinsert_stats.setInt(18, ws2s_conns);\n\t\t\t\tinsert_stats.setInt(19, bosh_conns);\n\t\t\t\tinsert_stats.setInt(20, s2s_conns);\n\t\t\t\tinsert_stats.setInt(21, sm_sessions);\n\t\t\t\tinsert_stats.setInt(22, sm_connections);\n\t\t\t\tinsert_stats.executeUpdate();\n\t\t\t}\n\t\t} catch (SQLException e) {\n\t\t\tlog.log(Level.WARNING, \"Problem adding new entry to DB: \", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setDataSource(DataRepository repository) {\n\t\ttry {\n\t\t\trepository.initPreparedStatement(STATS_INSERT, STATS_INSERT);\n\t\t} catch (SQLException e) {\n\t\t\tlog.log(Level.WARNING, \"Could not initialize repository\", e);\n\t\t}\n\t\tthis.repository = repository;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/stats/db/CounterDataLoggerRepositoryIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats.db;\n\nimport tigase.db.DataSource;\nimport tigase.db.DataSourceAware;\n\npublic interface CounterDataLoggerRepositoryIfc<DS extends DataSource>\n\t\textends DataSourceAware<DS> {\n\n\tpublic static final String BOSH_CONNS_COL = \"bosh_conns\";\n\tpublic static final String C2S_CONNS_COL = \"c2s_conns\";\n\tpublic static final String WS2S_CONNS_COL = \"ws2s_conns\";\n\tpublic static final String C2S_PACKETS_COL = \"c2s_packets\";\n\tpublic static final String CPU_USAGE_COL = \"cpu_usage\";\n\tpublic static final String EXT_PACKETS_COL = \"ext_packets\";\n\tpublic static final String HOSTNAME_COL = \"hostname\";\n\tpublic static final String IQS_COL = \"iqs\";\n\tpublic static final String MEM_USAGE_COL = \"mem_usage\";\n\tpublic static final String MESSAGES_COL = \"messages\";\n\tpublic static final String MUC_PACKETS_COL = \"muc_packets\";\n\tpublic static final String PRESENCES_COL = \"presences\";\n\tpublic static final String PUBSUB_PACKETS_COL = \"pubsub_packets\";\n\tpublic static final String S2S_CONNS_COL = \"s2s_conns\";\n\tpublic static final String S2S_PACKETS_COL = \"s2s_packets\";\n\tpublic static final String WS2S_PACKETS_COL = \"ws2s_packets\";\n\tpublic static final String SM_PACKETS_COL = \"sm_packets\";\n\tpublic static final String SM_SESSIONS_COL = \"sm_sessions\";\n\tpublic static final String SM_CONNECTIONS_COL = \"sm_connections\";\n\tpublic static final String STATS_TABLE = \"tig_stats_log\";\n\tpublic static final String UPTIME_COL = \"uptime\";\n\tpublic static final String VHOSTS_COL = \"vhosts\";\n\tpublic static final String REGISTERED_COL = \"registered\";\n\n\tvoid addStatsLogEntry(String hostname, float cpu_usage, float mem_usage, long uptime, int vhosts, long sm_packets,\n\t                      long muc_packets, long pubsub_packets, long c2s_packets, long ws2s_packets, long s2s_packets,\n\t                      long ext_packets, long presences, long messages, long iqs, long registered, int c2s_conns,\n\t                      int ws2s_conns, int bosh_conns, int s2s_conns, int sm_sessions, int sm_connections);\n}\n"
  },
  {
    "path": "src/main/java/tigase/sys/CPULoadListener.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.sys;\n\n/**\n * Created: Feb 19, 2009 12:21:15 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface CPULoadListener {\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/sys/MemoryChangeListener.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.sys;\n\n/**\n * Created: Feb 19, 2009 12:19:57 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface MemoryChangeListener {\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/sys/NMTScope.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.sys;\n\nimport java.util.Comparator;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.StringJoiner;\n\npublic class NMTScope {\n\n\tLong arena;\n\tLong committed;\n\tLong malloc;\n\tLong mmapCommitted;\n\tLong mmapReserved;\n\tLong reserved;\n\tString scopeType;\n\n\tNMTScope(String scopeType, Long reserved, Long committed) {\n\t\tthis(scopeType, reserved, committed, null, null, null, null);\n\t}\n\n\tNMTScope(String scopeType, Long reserved, Long committed, Long malloc, Long mmapReserved, Long mmapCommitted,\n\t\t\t Long arena) {\n\t\tObjects.nonNull(scopeType);\n\t\tthis.scopeType = scopeType;\n\t\tObjects.nonNull(reserved);\n\t\tthis.reserved = reserved;\n\t\tObjects.nonNull(committed);\n\t\tthis.committed = committed;\n\t\tthis.malloc = malloc;\n\t\tthis.mmapReserved = mmapReserved;\n\t\tthis.mmapCommitted = mmapCommitted;\n\t\tthis.arena = arena;\n\t}\n\n\tpublic String getScopeType() {\n\t\treturn scopeType;\n\t}\n\n\tpublic Long getReserved() {\n\t\treturn reserved;\n\t}\n\n\tpublic Long getCommitted() {\n\t\treturn committed;\n\t}\n\n\tpublic Optional<Long> getMalloc() {\n\t\treturn Optional.ofNullable(malloc);\n\t}\n\n\tpublic Optional<Long> getMmapReserved() {\n\t\treturn Optional.ofNullable(mmapReserved);\n\t}\n\n\tpublic Optional<Long> getMmapCommitted() {\n\t\treturn Optional.ofNullable(mmapCommitted);\n\t}\n\n\tpublic Optional<Long> getArena() {\n\t\treturn Optional.ofNullable(arena);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tfinal StringJoiner joiner = new StringJoiner(\", \", scopeType + \" (KB): \", \"\").add(\"reserved=\" + reserved)\n\t\t\t\t.add(\"committed=\" + committed);\n\t\tgetMalloc().ifPresent(val -> joiner.add(\"malloc=\" + val));\n\t\tgetMmapReserved().ifPresent(val -> joiner.add(\"mmapReserved=\" + val));\n\t\tgetMmapCommitted().ifPresent(val -> joiner.add(\"mmapCommitted=\" + val));\n\t\tgetArena().ifPresent(val -> joiner.add(\"arena=\" + val));\n\t\treturn joiner.toString();\n\t}\n\n\tenum COMMON_SCOPES {\n\t\tTOTAL(\"Total\"),\n\t\tJAVA_HEAP(\"Java Heap\"),\n\t\tCLASS(\"Class\"),\n\t\tTHREAD(\"Thread\"),\n\t\tCODE(\"Code\"),\n\t\tGC(\"GC\"),\n\t\tCOMPILER(\"Compiler\"),\n\t\tINTERNAL(\"Internal\"),\n\t\tOTHER(\"Other\"),\n\t\tSYMBOL(\"Symbol\"),\n\t\tNATIVE_MEMORY_TRACKING(\"Native Memory Tracking\"),\n\t\tARENA_CHUNK(\"Arena Chunk\"),\n\t\tLOGGING(\"Logging\"),\n\t\tARGUMENTS(\"Arguments\"),\n\t\tMODULE(\"Module\"),\n\t\tSYNCHRONIZER(\"Synchronizer\"),\n\t\tSAFEPOINT(\"Safepoint\");\n\n\t\tString name;\n\n\t\tCOMMON_SCOPES(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\tpublic String getName() {\n\t\t\treturn name;\n\t\t}\n\t}\n\n\tstatic final class NMTScopeBuilder {\n\n\t\tLong arena;\n\t\tLong committed;\n\t\tLong malloc;\n\t\tLong mmapCommitted;\n\t\tLong mmapReserved;\n\t\tLong reserved;\n\t\tString scopeType;\n\n\t\tstatic NMTScopeBuilder aNMTScope(String scopeType, Long reserved, Long committed) {\n\t\t\treturn new NMTScopeBuilder(scopeType, reserved, committed);\n\t\t}\n\n\t\tprivate NMTScopeBuilder(String scopeType, Long reserved, Long committed) {\n\t\t\tObjects.nonNull(scopeType);\n\t\t\tthis.scopeType = scopeType;\n\t\t\tObjects.nonNull(reserved);\n\t\t\tthis.reserved = reserved;\n\t\t\tObjects.nonNull(committed);\n\t\t\tthis.committed = committed;\n\n\t\t}\n\n\t\tNMTScopeBuilder withMalloc(Long malloc) {\n\t\t\tthis.malloc = malloc;\n\t\t\treturn this;\n\t\t}\n\n\t\tNMTScopeBuilder withMmapReserved(Long mmapReserved) {\n\t\t\tthis.mmapReserved = mmapReserved;\n\t\t\treturn this;\n\t\t}\n\n\t\tNMTScopeBuilder withMmapCommitted(Long mmapCommitted) {\n\t\t\tthis.mmapCommitted = mmapCommitted;\n\t\t\treturn this;\n\t\t}\n\n\t\tNMTScopeBuilder withArena(Long arena) {\n\t\t\tthis.arena = arena;\n\t\t\treturn this;\n\t\t}\n\n\t\tNMTScope build() {\n\t\t\treturn new NMTScope(scopeType, reserved, committed, malloc, mmapReserved, mmapCommitted, arena);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/sys/NativeMemoryTracking.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.sys;\n\nimport tigase.annotations.TigaseDeprecated;\n\nimport javax.management.JMException;\nimport javax.management.ObjectName;\nimport java.lang.management.ManagementFactory;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\npublic class NativeMemoryTracking {\n\n\tprivate static final Logger log = Logger.getLogger(NativeMemoryTracking.class.getName());\n\tprivate static final Pattern subScopeHeaderPattern = Pattern.compile(\n\t\t\t\"^-\\\\s+([\\\\w\\\\s]+)\\\\(reserved=(\\\\d+)[GKM]B, committed=(\\\\d+)[GKM]B\\\\).*\");\n\tprivate static final Pattern mmapPattern = Pattern.compile(\n\t\t\t\"^\\\\s+\\\\(mmap: reserved=(\\\\d+)[GKM]B, committed=(\\\\d+)[GKM]B\\\\).*\");\n\tprivate static final Pattern mallocArenaPattern = Pattern.compile(\"^\\\\s*\\\\((malloc|arena)=(\\\\d+)[GKM]B.*\\\\).*\");\n\tprivate final SCALE scale;\n\tprivate final Map<String, NMTScope> scopes;\n\n\tenum SCALE {\n\t\tKB,\n\t\tMB,\n\t\tGB\n\t}\n\n\tprivate static String executeMBeanCommand(String command, String... args) throws JMException {\n\t\treturn (String) ManagementFactory.getPlatformMBeanServer()\n\t\t\t\t.invoke(new ObjectName(\"com.sun.management:type=DiagnosticCommand\"), command, new Object[]{args},\n\t\t\t\t\t\tnew String[]{\"[Ljava.lang.String;\"});\n\t}\n\n\tprivate static Map<String, NMTScope> getNMTScopesFrom(String summary) {\n\t\tMap<String, NMTScope> scopes = new ConcurrentHashMap<>();\n\t\tfinal String[] summaryLines = summary.split(\"\\\\r?\\\\n\");\n\t\tfor (int currentLine = 0; currentLine < summaryLines.length; currentLine++) {\n\t\t\tString summaryLine = summaryLines[currentLine];\n\t\t\tif (summaryLine.startsWith(\"Total\")) {\n\t\t\t\tprocessTotalScope(scopes, summaryLine);\n\t\t\t} else if (summaryLine.startsWith(\"-\")) {\n\t\t\t\tcurrentLine = processSubScope(scopes, summaryLines, currentLine);\n\t\t\t}\n\t\t}\n\t\treturn scopes;\n\t}\n\n\tstatic Optional<NativeMemoryTracking> getNativeMemoryTracking() {\n\t\treturn getNativeMemoryTracking(SCALE.MB);\n\t}\n\n\tstatic Optional<NativeMemoryTracking> getNativeMemoryTracking(SCALE scale) {\n\t\tfinal Optional<String> nativeMemoryTrackingSummary = getNativeMemoryTrackingTextSummary(scale);\n\t\treturn nativeMemoryTrackingSummary.flatMap((String summary) -> parse(summary, scale));\n\t}\n\n\tprivate static Optional<String> getNativeMemoryTrackingTextSummary(SCALE scale) {\n\t\tString result = null;\n\t\ttry {\n\t\t\tresult = executeMBeanCommand(\"vmNativeMemory\", \"summary\", \"scale=\" + scale);\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.FINER, e, () -> \"There was a problem obtaining NMT summary\");\n\t\t}\n\t\treturn Optional.ofNullable(result);\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tSystem.out.println(NativeMemoryTracking.getNativeMemoryTracking(SCALE.MB));\n\t}\n\n\tstatic Optional<NativeMemoryTracking> parse(String summary) {\n\t\treturn parse(summary, SCALE.KB);\n\t}\n\n\tstatic Optional<NativeMemoryTracking> parse(String summary, SCALE scale) {\n\t\tMap<String, NMTScope> scopes = getNMTScopesFrom(summary);\n\t\treturn scopes != null && !scopes.isEmpty()\n\t\t\t   ? Optional.of(new NativeMemoryTracking(scopes, scale))\n\t\t\t   : Optional.empty();\n\t}\n\n\tprivate static void processMallocAndArena(NMTScope.NMTScopeBuilder scopeBuilder, String subSummaryLine,\n\t\t\t\t\t\t\t\t\t\t\t  Matcher mallocArenaMatcher) {\n\t\tString type = mallocArenaMatcher.group(1);\n\t\tswitch (type) {\n\t\t\tcase \"malloc\":\n\t\t\t\ttryParsingMatched(mallocArenaMatcher.group(2), subSummaryLine).ifPresent(scopeBuilder::withMalloc);\n\t\t\t\tbreak;\n\t\t\tcase \"arena\":\n\t\t\t\ttryParsingMatched(mallocArenaMatcher.group(2), subSummaryLine).ifPresent(scopeBuilder::withArena);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate static void processMmap(NMTScope.NMTScopeBuilder scopeBuilder, String subSummaryLine, Matcher mmapMatcher) {\n\t\ttryParsingMatched(mmapMatcher.group(1), subSummaryLine).ifPresent(scopeBuilder::withMmapReserved);\n\t\ttryParsingMatched(mmapMatcher.group(2), subSummaryLine).ifPresent(scopeBuilder::withMmapCommitted);\n\t}\n\n\tprivate static int processSubScope(Map<String, NMTScope> scopes, String[] summaryLines, int currentLine) {\n\t\t//- GC (reserved=207337KB, committed=61417KB)\n\t\t//     (malloc=17861KB #2347)\n\t\t//     (mmap: reserved=189476KB, committed=43556KB)\n\t\tfinal Matcher subScopeHeaderMatcher = subScopeHeaderPattern.matcher(summaryLines[currentLine]);\n\t\tif (subScopeHeaderMatcher.matches()) {\n\t\t\tString scope = subScopeHeaderMatcher.group(1).trim();\n\t\t\tLong reserverd = Long.valueOf(subScopeHeaderMatcher.group(2));\n\t\t\tLong commited = Long.valueOf(subScopeHeaderMatcher.group(3));\n\t\t\tfinal NMTScope.NMTScopeBuilder scopeBuilder = NMTScope.NMTScopeBuilder.aNMTScope(scope, reserverd,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t commited);\n\t\t\twhile (currentLine + 1 < summaryLines.length && !summaryLines[currentLine + 1].startsWith(\"-\")) {\n\t\t\t\tfinal String subSummaryLine = summaryLines[currentLine++];\n\t\t\t\tfinal Matcher mmapMatcher = mmapPattern.matcher(subSummaryLine);\n\t\t\t\tif (mmapMatcher.matches()) {\n\t\t\t\t\tprocessMmap(scopeBuilder, subSummaryLine, mmapMatcher);\n\t\t\t\t}\n\t\t\t\tfinal Matcher mallocArenaMatcher = mallocArenaPattern.matcher(subSummaryLine);\n\t\t\t\tif (mallocArenaMatcher.matches()) {\n\t\t\t\t\tprocessMallocAndArena(scopeBuilder, subSummaryLine, mallocArenaMatcher);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfinal NMTScope tmpScope = scopeBuilder.build();\n\t\t\tscopes.put(tmpScope.getScopeType(), tmpScope);\n\t\t}\n\t\treturn currentLine;\n\t}\n\n\tprivate static void processTotalScope(Map<String, NMTScope> scopes, String summaryLine) {\n\t\tfinal Pattern compile = Pattern.compile(\"Total: reserved=(\\\\d+)[GKM]B, committed=(\\\\d+)[GKM]B\");\n\t\tfinal Matcher matcher = compile.matcher(summaryLine);\n\t\tif (matcher.matches()) {\n\t\t\tfinal NMTScope total = new NMTScope(\"Total\", Long.valueOf(matcher.group(1)),\n\t\t\t\t\t\t\t\t\t\t\t\tLong.valueOf(matcher.group(2)));\n\t\t\tscopes.put(total.getScopeType(), total);\n\t\t}\n\t}\n\n\tprivate static Optional<Long> tryParsingMatched(String matched, String line) {\n\t\ttry {\n\t\t\treturn Optional.of(Long.valueOf(matched));\n\t\t} catch (NumberFormatException e) {\n\t\t\tlog.log(Level.WARNING, e, () -> \"Can''t parse string: \" + matched + \" from line: \" + line);\n\t\t}\n\t\treturn Optional.empty();\n\t}\n\n\tpublic NativeMemoryTracking(Map<String, NMTScope> scopes) {\n\t\tthis(scopes, SCALE.MB);\n\t}\n\n\tpublic NativeMemoryTracking(Map<String, NMTScope> scopes, SCALE scale) {\n\t\tObjects.nonNull(scopes);\n\t\tthis.scopes = scopes;\n\t\tthis.scale = scale;\n\t}\n\n\tpublic Map<String, NMTScope> getScopes() {\n\t\treturn Collections.unmodifiableMap(scopes);\n\t}\n\n\tpublic SCALE getScale() {\n\t\treturn scale;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder(\"scopes: \").append(scopes.size());\n\t\tfinal NMTScope totalScope = scopes.get(NMTScope.COMMON_SCOPES.TOTAL.name);\n\t\tif (totalScope != null) {\n\t\t\tsb.append(\", total reserved: \")\n\t\t\t\t\t.append(totalScope.getReserved())\n\t\t\t\t\t.append(\", total commited: \")\n\t\t\t\t\t.append(totalScope.getCommitted())\n\t\t\t\t\t.append(\", scale: \")\n\t\t\t\t\t.append(scale)\n\t\t\t;\n\t\t}\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/sys/OnlineJidsReporter.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.sys;\n\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\n/**\n * Created: Apr 19, 2009 12:15:07 AM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\npublic interface OnlineJidsReporter {\n\n\t/**\n\t * Method checks whether the clustering strategy has a complete JIDs info. That is whether the strategy knows about\n\t * all users connected to all nodes. Some strategies may choose not to share this information among nodes, hence the\n\t * methods returns false. Other may synchronize this information and can provide it to further optimize cluster\n\t * traffic.\n\t *\n\t * @return a true boolean value if the strategy has a complete information about all users connected to all cluster\n\t * nodes.\n\t */\n\tboolean hasCompleteJidsInfo();\n\n\t/**\n\t * The method checks whether the given JID is known to the installation, either user connected to local machine or\n\t * any of the cluster nodes. False result does not mean the user is not connected. It means the method does not know\n\t * anything about the JID. Some clustering strategies may not cache online users information.\n\t *\n\t * @param jid a user's JID for whom we query information.\n\t *\n\t * @return true if the user is known as online to the installation, false if the method does not know.\n\t */\n\tboolean containsJid(BareJID jid);\n\n\t/**\n\t * The method checks whether the given JID is known to local cluster node as connected user. False result means that\n\t * given JID is not connected to local cluster node but it may be connected to other cluster node. Result of this\n\t * method should be independent of used clustering strategy.\n\t *\n\t * @param jid a user's JID for whom we query information\n\t *\n\t * @return true if user is known as connected to local cluster node, false if it is not connected to local node\n\t */\n\tboolean containsJidLocally(BareJID jid);\n\n\t/**\n\t * The method checks whether the given JID is known to local cluster node as connected user. False result means that\n\t * given JID is not connected to local cluster node but it may be connected to other cluster node. Result of this\n\t * method should be independent of used clustering strategy.\n\t *\n\t * @param jid a user's JID for whom we query information\n\t *\n\t * @return true if user is known as connected to local cluster node, false if it is not connected to local node\n\t */\n\tboolean containsJidLocally(JID jid);\n\n\t/**\n\t * Retrieve all connection IDs (CIDs) for the given user.\n\t *\n\t * @param jid id of the user for which we want to retrieve the list.\n\t *\n\t * @return an array of {@link JID} containing all Connection IDs (CIDs) for the given user.\n\t */\n\tJID[] getConnectionIdsForJid(BareJID jid);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/sys/ResourceState.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.sys;\n\n/**\n * Created: Feb 19, 2009 12:22:55 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic enum ResourceState {\n\tGREEN,\n\tYELLOW,\n\tRED;\n}\n"
  },
  {
    "path": "src/main/java/tigase/sys/ShutdownHook.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.sys;\n\n/**\n * Created: Feb 19, 2009 12:17:03 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface ShutdownHook {\n\n\tString getName();\n\n\tString shutdown();\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/sys/TigaseRuntime.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.sys;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.server.XMPPServer;\nimport tigase.server.monitor.MonitorRuntime;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.lang.management.*;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Feb 19, 2009 12:15:02 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\npublic abstract class TigaseRuntime {\n\n\tprotected static final long SECOND = 1000;\n\tprotected static final long MINUTE = 60 * SECOND;\n\tprotected static final long HOUR = 60 * MINUTE;\n\tprivate static final Logger log = Logger.getLogger(TigaseRuntime.class.getName());\n\n\tprivate float cpuUsage = 0F;\n\tprivate int cpus = Runtime.getRuntime().availableProcessors();\n\tprivate Map<String, MemoryPoolMXBean> memoryPoolMXBeans = null;\n\tprivate MemoryPoolMXBean oldMemPool = null;\n\tprivate long prevCputime = 0;\n\tprivate long prevUptime = 0;\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.3.0\", removeIn = \"9.0.0\")\n\tpublic static Optional<NativeMemoryTracking> getNativeMemoryTracking() {\n\t\treturn NativeMemoryTracking.getNativeMemoryTracking(NativeMemoryTracking.SCALE.MB);\n\t}\n\n\tpublic static Optional<NativeMemoryTracking> getNativeMemoryTracking(NativeMemoryTracking.SCALE scale) {\n\t\treturn NativeMemoryTracking.getNativeMemoryTracking(scale);\n\t}\n\n\tpublic static TigaseRuntime getTigaseRuntime() {\n\t\treturn MonitorRuntime.getMonitorRuntime();\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tfinal TigaseRuntime tigaseRuntime = getTigaseRuntime();\n\t\tSystem.out.println(tigaseRuntime);\n\t\tSystem.out.println(getNativeMemoryTracking(NativeMemoryTracking.SCALE.MB));\n\t\ttigaseRuntime.shutdownTigase(new String[]{\"there\", \"was\", \"an\", \"error\"});\n\t}\n\n\tprotected TigaseRuntime() {\n\t\tList<MemoryPoolMXBean> memPools = ManagementFactory.getMemoryPoolMXBeans();\n\n\t\tmemoryPoolMXBeans = new LinkedHashMap<>(3);\n\n\t\tfor (MemoryPoolMXBean memoryPoolMXBean : memPools) {\n\t\t\tif (memoryPoolMXBean.getName().toLowerCase().contains(\"old\")) {\n\t\t\t\toldMemPool = memoryPoolMXBean;\n\n\t\t\t\tmemoryPoolMXBeans.put(\"old\", memoryPoolMXBean);\n\t\t\t\tlog.log(Level.CONFIG, \"Using {0} memory pool for reporting (old) memory usage.\",\n\t\t\t\t\t\tmemoryPoolMXBean.getName());\n\t\t\t}\n\t\t\tif (memoryPoolMXBean.getName().toLowerCase().contains(\"survivor\")) {\n\t\t\t\tmemoryPoolMXBeans.put(\"survivor\", memoryPoolMXBean);\n\t\t\t\tlog.log(Level.CONFIG, \"Using {0} memory pool for reporting survivor memory usage.\",\n\t\t\t\t\t\tmemoryPoolMXBean.getName());\n\t\t\t}\n\t\t\tif (memoryPoolMXBean.getName().toLowerCase().contains(\"eden\")) {\n\t\t\t\tmemoryPoolMXBeans.put(\"eden\", memoryPoolMXBean);\n\t\t\t\tlog.log(Level.CONFIG, \"Using {0} memory pool for reporting eden memory usage.\",\n\t\t\t\t\t\tmemoryPoolMXBean.getName());\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic Map<String, MemoryPoolMXBean> getMemoryPoolMXBeans() {\n\t\treturn memoryPoolMXBeans;\n\t}\n\n\tpublic MemoryPoolMXBean getOldMemPool() {\n\t\treturn oldMemPool;\n\t}\n\n\tpublic abstract void addCPULoadListener(CPULoadListener cpuListener);\n\n\tpublic abstract void addMemoryChangeListener(MemoryChangeListener memListener);\n\n\tpublic abstract void addOnlineJidsReporter(OnlineJidsReporter onlineReporter);\n\n\tpublic abstract void addShutdownHook(ShutdownHook hook);\n\n\tpublic abstract JID[] getConnectionIdsForJid(JID jid);\n\n\tpublic int getCPUsNumber() {\n\t\treturn cpus;\n\t}\n\n\tpublic ResourceState getCPUState() {\n\t\treturn ResourceState.GREEN;\n\t}\n\n\tpublic float getCPUUsage() {\n\t\tlong currCputime = -1;\n\t\tlong elapsedCpu = -1;\n\t\tlong currUptime = getUptime();\n\t\tlong elapsedTime = currUptime - prevUptime;\n\n\t\tif ((prevUptime > 0L) && (elapsedTime > 500L)) {\n\t\t\tcurrCputime = getProcessCPUTime();\n\t\t\telapsedCpu = currCputime - prevCputime;\n\t\t\tcpuUsage = Math.min(99.99F, elapsedCpu / (elapsedTime * 10000F * cpus));\n\t\t}\n\t\tif (elapsedTime > 500L) {\n\t\t\tprevUptime = currUptime;\n\t\t\tprevCputime = currCputime;\n\t\t}\n\n\t\treturn cpuUsage;\n\t}\n\n\tpublic long getDirectMemUsed() {\n\t\tlong result = -1;\n\t\tList<MemoryPoolMXBean> memPools = ManagementFactory.getMemoryPoolMXBeans();\n\n\t\tfor (MemoryPoolMXBean memoryPoolMXBean : memPools) {\n\t\t\tif (memoryPoolMXBean.getName().toLowerCase().contains(\"direct\")) {\n\t\t\t\tresult = memoryPoolMXBean.getUsage().getUsed();\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic String getGcStatistics() {\n\n\t\t// As this is variable and the collectors may change over time we\n\t\t// need to re-do it each time\n\t\tStringBuilder sb = new StringBuilder();\n\t\tList<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();\n\t\tfor (GarbageCollectorMXBean gcBean : gcBeans) {\n\t\t\tif (sb.length() > 0) {\n\t\t\t\tsb.append('|');\n\t\t\t}\n\t\t\tsb.append('{');\n\t\t\tsb.append(\"name=\");\n\t\t\tsb.append(gcBean.getName()).append(';');\n\t\t\tsb.append(\"count=\").append(gcBean.getCollectionCount()).append(';');\n\t\t\tsb.append(\"time=\").append(gcBean.getCollectionTime()).append(';');\n\t\t\tif (gcBean.getCollectionCount() > 0) {\n\t\t\t\tsb.append(\"avgTime=\").append(gcBean.getCollectionTime() / gcBean.getCollectionCount()).append(';');\n\t\t\t} else {\n\t\t\t\tsb.append(\"avgTime=\").append(0).append(';');\n\t\t\t}\n\t\t\tsb.append(\"pools=\").append(Arrays.asList(gcBean.getMemoryPoolNames()));\n\t\t\tsb.append('}');\n\t\t}\n\t\treturn sb.toString();\n\n\t}\n\n\t/**\n\t * We try to return OLD memory pool size as this is what is the most interesting to us. If this is not possible then\n\t * we return total Heap size.\n\t *\n\t * @return a value of <code>long</code>\n\t */\n\tpublic long getHeapMemMax() {\n\t\tif (oldMemPool != null) {\n\t\t\tMemoryUsage memUsage = oldMemPool.getUsage();\n\n\t\t\treturn memUsage.getMax();\n\t\t}\n\n\t\treturn ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getMax();\n\t}\n\n\tpublic float getHeapMemUsage() {\n\t\treturn getHeapMemMax() == -1 ? -1.0F : (getHeapMemUsed() * 100F) / getHeapMemMax();\n\t}\n\n\t/**\n\t * We try to return OLD memory pool size as this is what is the most interesting to us. If this is not possible then\n\t * we return total Heap used.\n\t *\n\t * @return a value of <code>long</code>\n\t */\n\tpublic long getHeapMemUsed() {\n\t\tif (oldMemPool != null) {\n\t\t\tMemoryUsage memUsage = oldMemPool.getUsage();\n\n\t\t\treturn memUsage.getUsed();\n\t\t}\n\n\t\treturn ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed();\n\t}\n\n\tpublic double getLoadAverage() {\n\t\treturn ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage();\n\t}\n\n\tpublic ResourceState getMemoryState() {\n\t\treturn ResourceState.GREEN;\n\t}\n\n\tpublic long getNonHeapMemMax() {\n\t\treturn ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage().getMax();\n\t}\n\n\tpublic float getNonHeapMemUsage() {\n\t\treturn getNonHeapMemMax() == -1 ? -1.0F : (getNonHeapMemUsed() * 100F) / getNonHeapMemMax();\n\t}\n\n\tpublic long getNonHeapMemUsed() {\n\t\treturn ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage().getUsed();\n\t}\n\n\tpublic long getProcessCPUTime() {\n\t\tlong result = 0;\n\t\tOperatingSystemMXBean osMXBean = ManagementFactory.getOperatingSystemMXBean();\n\n\t\tif (osMXBean instanceof com.sun.management.OperatingSystemMXBean) {\n\n\t\t\t// The easy way if possible\n\t\t\tcom.sun.management.OperatingSystemMXBean sunOSMXBean = (com.sun.management.OperatingSystemMXBean) osMXBean;\n\n\t\t\tresult = sunOSMXBean.getProcessCpuTime();\n\t\t} else {\n\n\t\t\t// The hard way...\n\t\t\tThreadMXBean thBean = ManagementFactory.getThreadMXBean();\n\n\t\t\tfor (long thid : thBean.getAllThreadIds()) {\n\t\t\t\tresult += thBean.getThreadCpuTime(thid);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic int getThreadsNumber() {\n\t\treturn ManagementFactory.getThreadMXBean().getThreadCount();\n\t}\n\n\tpublic long getUptime() {\n\t\treturn ManagementFactory.getRuntimeMXBean().getUptime();\n\t}\n\n\tpublic String getUptimeString() {\n\t\tlong uptime = ManagementFactory.getRuntimeMXBean().getUptime();\n\t\tlong days = uptime / (24 * HOUR);\n\t\tlong hours = (uptime - (days * 24 * HOUR)) / HOUR;\n\t\tlong minutes = (uptime - (days * 24 * HOUR + hours * HOUR)) / MINUTE;\n\t\tlong seconds = (uptime - (days * 24 * HOUR + hours * HOUR + minutes * MINUTE)) / SECOND;\n\t\tStringBuilder sb = new StringBuilder();\n\n\t\tsb.append((days > 0) ? days + ((days == 1) ? \" day\" : \" days\") : \"\");\n\t\tif (hours > 0) {\n\t\t\tif (sb.length() > 0) {\n\t\t\t\tsb.append(\", \");\n\t\t\t}\n\t\t\tsb.append(hours + ((hours == 1) ? \" hour\" : \" hours\"));\n\t\t}\n\t\tif ((days == 0) && (minutes > 0)) {\n\t\t\tif (sb.length() > 0) {\n\t\t\t\tsb.append(\", \");\n\t\t\t}\n\t\t\tsb.append(minutes + ((minutes == 1) ? \" min\" : \" mins\"));\n\t\t}\n\t\tif ((days == 0) && (hours == 0) && (seconds > 0)) {\n\t\t\tif (sb.length() > 0) {\n\t\t\t\tsb.append(\", \");\n\t\t\t}\n\t\t\tsb.append(seconds + \" sec\");\n\t\t}\n\n\t\treturn sb.toString();\n\t}\n\n\tpublic abstract boolean hasCompleteJidsInfo();\n\n\tpublic abstract boolean isJidOnline(JID jid);\n\n\tpublic abstract boolean isJidOnlineLocally(BareJID jid);\n\n\tpublic abstract boolean isJidOnlineLocally(JID jid);\n\n\tpublic abstract void removeShutdownHook(ShutdownHook hook);\n\n\tpublic String getOldGenName() {\n\t\treturn (oldMemPool != null ? oldMemPool.getName() : \"n/a\");\n\t}\n\n\tpublic void shutdownTigase(String[] msg) {\n\t\tshutdownTigase(msg, 1);\n\t}\n\n\tpublic void shutdownTigase(String[] msg, int exitCode) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"\\n\");\n\t\tsb.append(\"\\n\");\n\t\tsb.append(\"\\n\");\n\t\tsb.append(\"\\n\");\n\t\tsb.append(\"  =============================================================================\").append(\"\\n\");\n\t\tfor (String line : msg) {\n\t\t\tsb.append(\"  \").append(line).append(\"\\n\");\n\t\t}\n\t\tsb.append(\"  =============================================================================\").append(\"\\n\");\n\t\tsb.append(\"\\n\");\n\t\tsb.append(\"\\n\");\n\t\tsb.append(\"\\n\");\n\t\tsb.append(\"\\n\");\n\n\t\tif (XMPPServer.isOSGi()) {\n\t\t\t// for some reason System.out.println is not working in OSGi\n\t\t\tlog.log(Level.SEVERE, sb.toString());\n\t\t} else {\n\t\t\tSystem.out.println(sb.toString());\n\t\t}\n\n\t\tSystem.exit(exitCode);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/util/common/DependencyChecker.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.common;\n\nimport tigase.server.XMPPServer;\nimport tigase.sys.TigaseRuntime;\nimport tigase.util.Version;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.util.Enumeration;\nimport java.util.jar.JarFile;\nimport java.util.jar.Manifest;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class DependencyChecker {\n\n\tprivate static final Logger log = Logger.getLogger(DependencyChecker.class.getCanonicalName());\n\n\tpublic static void checkDependencies(Class clazz) {\n\t\tif (XMPPServer.isOSGi()) {\n\t\t\t// OSGi has its own dependency checking mechanism which will work just fine\n\t\t\treturn;\n\t\t}\n\n\t\t// In other case we need to verify availability of required packages and versions\n\t\ttry {\n\t\t\tEnumeration<URL> manifestUrls = clazz.getClassLoader().getResources(JarFile.MANIFEST_NAME);\n\t\t\twhile (manifestUrls.hasMoreElements()) {\n\t\t\t\tURL url = manifestUrls.nextElement();\n\t\t\t\ttry (InputStream is = url.openStream()) {\n\t\t\t\t\tManifest manifest = new Manifest(is);\n\t\t\t\t\tString dependencies = manifest.getMainAttributes().getValue(\"Tigase-Required-Dependencies\");\n\t\t\t\t\tif (dependencies != null) {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Found required dependencies \" + dependencies + \" in \" + url);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (String dependency : dependencies.split(\",\")) {\n\t\t\t\t\t\t\tString[] parts = dependency.split(\"=\");\n\t\t\t\t\t\t\tString name = parts[0];\n\t\t\t\t\t\t\tVersion version = Version.of(parts[1]);\n\n\t\t\t\t\t\t\tPackage p = Package.getPackage(name);\n\t\t\t\t\t\t\tif (p == null) {\n\t\t\t\t\t\t\t\tTigaseRuntime.getTigaseRuntime()\n\t\t\t\t\t\t\t\t\t\t.shutdownTigase(new String[]{\"Required package \" + name +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \" is inaccessible. Make sure that all required jars are available in your classpath.\"});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (p.getImplementationVersion() == null) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINE, \"could not check \" + name +\n\t\t\t\t\t\t\t\t\t\t\" dependency version as package version is not set\");\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"looking for \" + name + \" in version \" + version + \" and found \" +\n\t\t\t\t\t\t\t\t\t\tp.getImplementationVersion());\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t//check support for more than one requirement!\n\t\t\t\t\t\t\t// should be only >\n\t\t\t\t\t\t\tif (version.compareTo(Version.of(p.getImplementationVersion())) > 0) {\n\t\t\t\t\t\t\t\tTigaseRuntime.getTigaseRuntime()\n\t\t\t\t\t\t\t\t\t\t.shutdownTigase(new String[]{\n\t\t\t\t\t\t\t\t\t\t\t\tp.getImplementationTitle() + \" is available in version \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tp.getImplementationVersion() + \" while \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclazz.getPackage().getImplementationTitle() + \" \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclazz.getPackage().getImplementationVersion() +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\" requires version >= \" + version});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t} catch (IOException ex) {\n\t\t\tthrow new RuntimeException(\"Failed to read \" + JarFile.MANIFEST_NAME, ex);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/util/common/OSUtils.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.common;\n\n/**\n * Created: Dec 11, 2008 10:26:00 AM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class OSUtils {\n\n\tpublic enum OS {\n\t\twindows,\n\t\tlinux,\n\t\tsunos,\n\t\tsolaris,\n\t\tmac,\n\t\tunix,\n\t\tother;\n\t}\n\n\tpublic static OS getOSType() {\n\t\tString osName = System.getProperty(\"os.name\").toLowerCase();\n\t\tfor (OS os : OS.values()) {\n\t\t\tif (osName.contains(os.toString())) {\n\t\t\t\treturn os;\n\t\t\t}\n\t\t}\n\t\treturn OS.other;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/util/common/TimerTask.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.common;\n\nimport java.util.concurrent.ScheduledFuture;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * TimerTask class is basic implementation of java.util.TimerTask class which is used with ScheduledExecutorService in\n * AbstractMessageRecevier.\n */\npublic abstract class TimerTask\n\t\timplements Runnable {\n\n\tprivate static final Logger log = Logger.getLogger(TimerTask.class.getName());\n\tprivate boolean cancelled = false;\n\tprivate ScheduledFuture<?> future = null;\n\n\tpublic void setScheduledFuture(ScheduledFuture<?> future) {\n\t\tthis.future = future;\n\t}\n\n\tpublic boolean isScheduled() {\n\t\treturn future != null && !future.isCancelled() && !future.isDone();\n\t}\n\n\tpublic boolean isCancelled() {\n\t\treturn cancelled;\n\t}\n\n\tpublic void cancel() {\n\t\tcancel(false);\n\t}\n\n\tpublic void cancel(boolean mayInterruptIfRunning) {\n\t\tcancelled = true;\n\t\tif (future != null && log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\"Cancelling tigase task, mayInterruptIfRunning: {0}, done: {1}, cancelled: {2}, future: {3}\",\n\t\t\t\t\tnew Object[]{mayInterruptIfRunning, future.isDone(), future.isCancelled(), future});\n\t\t}\n\n\t\tif (future != null && !future.isDone()) {\n\t\t\tfuture.cancel(mayInterruptIfRunning);\n\t\t}\n\t}\n\n\tpublic void reset(boolean mayInterruptIfRunning) {\n\t\tif (future != null && !future.isDone()) {\n\t\t\tfuture.cancel(mayInterruptIfRunning);\n\t\t}\n\t\tfuture = null;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/util/historyCache/AllHistoryCache.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.historyCache;\n\nimport tigase.stats.StatisticsList;\nimport tigase.sys.TigaseRuntime;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * @author Artur Hefczyc Created May 28, 2011\n */\npublic class AllHistoryCache {\n\n\tprivate static final Logger log = Logger.getLogger(AllHistoryCache.class.getName());\n\tprivate StatisticsList[] buffer = null;\n\tprivate int count = 0;\n\tprivate int highMemoryLevel = 95;\n\tprivate int highMemoryUsageCount = 0;\n\tprivate int start = 0;\n\n\tpublic AllHistoryCache(int limit, int highMemoryLevel) {\n\t\tbuffer = new StatisticsList[limit];\n\t\tthis.highMemoryLevel = highMemoryLevel;\n\t}\n\n\tpublic synchronized void addItem(StatisticsList item) {\n\t\tint ix = (start + count) % buffer.length;\n\t\tbuffer[ix] = item;\n\t\tif (count < buffer.length) {\n\t\t\tcount++;\n\t\t} else {\n\t\t\tstart++;\n\t\t\tstart %= buffer.length;\n\t\t}\n\t\tif (isHighMemoryUsage()) {\n\t\t\thighMemoryUsageCount++;\n\t\t\tint minimalSize = count / 2;\n\t\t\tif (minimalSize < 5) {\n\t\t\t\tminimalSize = 5;\n\t\t\t}\n\t\t\tif (count > minimalSize) {\n\t\t\t\tfor (int i = 0; i < count - minimalSize; i++) {\n\t\t\t\t\tbuffer[(start + i) % buffer.length] = null;\n\t\t\t\t}\n\t\t\t\tstart = (start + count) - minimalSize;\n\t\t\t\tcount = minimalSize;\n\t\t\t}\n\t\t\tlog.log(Level.CONFIG, \"Shrinking statistics to {0} items for {1} time\",\n\t\t\t\t\tnew Object[]{minimalSize, highMemoryUsageCount});\n\t\t} else {\n\t\t\thighMemoryUsageCount = 0;\n\t\t}\n\t}\n\n\tpublic synchronized StatisticsList[] getCurrentHistory() {\n\t\tStatisticsList[] result = new StatisticsList[count];\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tint ix = (start + i) % buffer.length;\n\t\t\tresult[i] = buffer[ix];\n\t\t}\n\t\treturn result;\n\t}\n\n\tprotected boolean isHighMemoryUsage() {\n\t\treturn TigaseRuntime.getTigaseRuntime().getHeapMemUsage() > highMemoryLevel;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/util/historyCache/FloatHistoryCache.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.historyCache;\n\n/**\n * Created: Sep 8, 2009 7:32:09 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class FloatHistoryCache {\n\n\tprivate float[] buffer = null;\n\tprivate int count = 0;\n\tprivate int start = 0;\n\n\tpublic FloatHistoryCache(int limit) {\n\t\tbuffer = new float[limit];\n\t}\n\n\tpublic synchronized void addItem(float item) {\n\t\tint ix = (start + count) % buffer.length;\n\t\tbuffer[ix] = item;\n\t\tif (count < buffer.length) {\n\t\t\tcount++;\n\t\t} else {\n\t\t\tstart++;\n\t\t\tstart %= buffer.length;\n\t\t}\n\t}\n\n\tpublic synchronized float[] getCurrentHistory() {\n\t\tfloat[] result = new float[count];\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tint ix = (start + i) % buffer.length;\n\t\t\tresult[i] = buffer[ix];\n\t\t}\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/util/historyCache/IntHistoryCache.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.historyCache;\n\n/**\n * Created: Sep 8, 2009 7:39:27 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class IntHistoryCache {\n\n\tprivate int[] buffer = null;\n\tprivate int count = 0;\n\tprivate int start = 0;\n\n\tpublic IntHistoryCache(int limit) {\n\t\tbuffer = new int[limit];\n\t}\n\n\tpublic synchronized void addItem(int item) {\n\t\tint ix = (start + count) % buffer.length;\n\t\tbuffer[ix] = item;\n\t\tif (count < buffer.length) {\n\t\t\tcount++;\n\t\t} else {\n\t\t\tstart++;\n\t\t\tstart %= buffer.length;\n\t\t}\n\t}\n\n\tpublic synchronized int[] getCurrentHistory() {\n\t\tint[] result = new int[count];\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tint ix = (start + i) % buffer.length;\n\t\t\tresult[i] = buffer[ix];\n\t\t}\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/util/historyCache/LongHistoryCache.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.historyCache;\n\n/**\n * Created: Sep 8, 2009 7:39:27 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class LongHistoryCache {\n\n\tprivate long[] buffer = null;\n\tprivate int count = 0;\n\tprivate int start = 0;\n\n\tpublic LongHistoryCache(int limit) {\n\t\tbuffer = new long[limit];\n\t}\n\n\tpublic synchronized void addItem(long item) {\n\t\tint ix = (start + count) % buffer.length;\n\n\t\tbuffer[ix] = item;\n\t\tif (count < buffer.length) {\n\t\t\tcount++;\n\t\t} else {\n\t\t\tstart++;\n\t\t\tstart %= buffer.length;\n\t\t}\n\t}\n\n\tpublic synchronized long[] getCurrentHistory() {\n\t\tlong[] result = new long[count];\n\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tint ix = (start + i) % buffer.length;\n\n\t\t\tresult[i] = buffer[ix];\n\t\t}\n\n\t\treturn result;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/util/log/LogFilter.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.log;\n\nimport java.util.logging.Filter;\nimport java.util.logging.LogRecord;\n\n/**\n * @author kobit Created Dec 20, 2011\n */\npublic class LogFilter\n\t\timplements Filter {\n\n\tprivate String id = null;\n\tprivate String[] trackers = null;\n\n\tpublic LogFilter(String id, String... trackers) {\n\t\tthis.id = id;\n\t\tthis.trackers = trackers;\n\t}\n\n\tpublic String getId() {\n\t\treturn id;\n\t}\n\n\t@Override\n\tpublic boolean isLoggable(LogRecord record) {\n\t\tboolean matchTracker = false;\n\t\tString msg = record.getMessage();\n\t\tif (msg != null) {\n\t\t\tint i = 0;\n\t\t\twhile (!matchTracker && i < trackers.length) {\n\t\t\t\tmatchTracker = msg.contains(trackers[i++]);\n\t\t\t}\n\t\t}\n\t\treturn matchTracker;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/util/log/LogFormatter.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.log;\n\nimport tigase.util.ui.console.AnsiColor;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.text.DateFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentSkipListMap;\nimport java.util.logging.Formatter;\nimport java.util.logging.LogRecord;\n\nimport static tigase.util.StringUtilities.JUSTIFY.LEFT;\nimport static tigase.util.StringUtilities.JUSTIFY.RIGHT;\nimport static tigase.util.StringUtilities.padStringToColumn;\n\npublic class LogFormatter\n\t\textends Formatter {\n\n\tpublic static final Map<Integer, LogWithStackTraceEntry> errors = new ConcurrentSkipListMap<>();\n\tfinal static DateFormat simple = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss:SSS\");\n\tprivate static int DATE_TIME_LEN = 26;\n\tprivate static int LEVEL_OFFSET = 7;\n\tprivate static int METHOD_OFFSET = 37;\n\tprivate static int THREAD_OFFSET = 25;\n\tprotected Date timestamp = new Date();\n\n\tprivate boolean colorful = !Boolean.getBoolean(\"disable_logger_color\");\n\n\tpublic LogFormatter() {\n\t}\n\n\tpublic LogFormatter(boolean colorful) {\n\t\tthis.colorful = colorful;\n\t}\n\n\t@Override\n\tpublic synchronized String format(LogRecord record) {\n\t\tStringBuilder sb = new StringBuilder(200);\n\t\tint colorOffset = 0;\n\n\t\ttimestamp.setTime(record.getMillis());\n\t\tcolorOffset += setColor(sb, AnsiColor.GREEN_BOLD_BRIGHT);\n\t\tsb.append('[').append(simple.format(timestamp)).append(']');\n\t\tcolorOffset += setColor(sb, AnsiColor.CYAN);\n\t\tpadStringToColumn(sb, record.getLevel().toString(), LEFT, DATE_TIME_LEN + LEVEL_OFFSET + colorOffset, ' ', \" [\",\n\t\t\t\t\t\t  \"]\");\n\t\tcolorOffset += setColor(sb, AnsiColor.RESET);\n\t\tpadStringToColumn(sb, Thread.currentThread().getName(), RIGHT,\n\t\t\t\t\t\t  DATE_TIME_LEN + LEVEL_OFFSET + THREAD_OFFSET + colorOffset, ' ', \" [\", \" ]\");\n\t\tcolorOffset += setColor(sb, AnsiColor.BLUE_BOLD);\n\t\tpadStringToColumn(sb, getClassMethodName(record), LEFT,\n\t\t\t\t\t\t  DATE_TIME_LEN + LEVEL_OFFSET + THREAD_OFFSET + METHOD_OFFSET + colorOffset, ' ', \" \", \": \");\n\t\tsetColor(sb, AnsiColor.RESET);\n\t\tsb.append(formatMessage(record));\n\t\tif (record.getThrown() != null) {\n\t\t\tfinal String stackTrace = fillThrowable(record);\n\t\t\tsb.append(stackTrace);\n\t\t\taddError(record.getThrown(), stackTrace, sb.toString());\n\t\t}\n\t\treturn sb.append(\"\\n\").toString();\n\t}\n\n\tprotected void addError(Throwable thrown, String stack, String log_msg) {\n\t\terrors.computeIfAbsent(stack.hashCode(), integer -> {\n\t\t\tString msg = thrown.getMessage();\n\n\t\t\tif (msg == null) {\n\t\t\t\tmsg = thrown.toString();\n\t\t\t}\n\t\t\treturn new LogWithStackTraceEntry(msg, log_msg);\n\t\t}).increment();\n\t}\n\n\tprivate int setColor(StringBuilder sb, AnsiColor color) {\n\t\tif (colorful && AnsiColor.isCompatible()) {\n\t\t\tsb.append(color);\n\t\t\treturn color.toString().length();\n\t\t}\n\t\treturn 0;\n\t}\n\n\tprivate static String fillThrowable(LogRecord record) {\n\t\treturn fillThrowable(record.getThrown());\n\t}\n\n\tpublic static String fillThrowable(Throwable throwable) {\n\t\tStringWriter sw = new StringWriter();\n\t\tif (throwable != null) {\n\t\t\tPrintWriter pw = new PrintWriter(sw);\n\t\t\tpw.println();\n\t\t\tthrowable.printStackTrace(pw);\n\t\t\tpw.close();\n\t\t}\n\t\treturn sw.toString();\n\t}\n\n\tprivate String getClassMethodName(LogRecord record) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tif (record.getSourceClassName() != null) {\n\t\t\tString className = \"\";\n\t\t\tclassName = record.getSourceClassName();\n\t\t\tint idx = className.lastIndexOf('.');\n\t\t\tif (idx >= 0) {\n\t\t\t\tclassName = className.substring(idx + 1);\n\t\t\t}\n\t\t\tsb.append(className);\n\t\t}\n\t\tif (record.getSourceMethodName() != null) {\n\t\t\tsb.append(\".\").append(record.getSourceMethodName()).append(\"()\");\n\t\t}\n\t\treturn sb.toString();\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/util/log/LogUserFilter.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.log;\n\nimport tigase.xmpp.XMPPSession;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Map;\nimport java.util.logging.Filter;\nimport java.util.logging.LogRecord;\n\n/**\n * @author kobit Created Dec 20, 2011\n */\npublic class LogUserFilter\n\t\timplements Filter {\n\n\tMap<BareJID, XMPPSession> sessionsByNodeId = null;\n\tprivate LogFormatter format = new LogFormatter();\n\tprivate BareJID jid = null;\n\tprivate XMPPSession tracker = null;\n\n\tpublic LogUserFilter(BareJID jid, Map<BareJID, XMPPSession> sessionsByNodeId) {\n\t\tthis.jid = jid;\n\t\tthis.sessionsByNodeId = sessionsByNodeId;\n\t}\n\n\tpublic String getId() {\n\t\treturn jid.toString();\n\t}\n\n\t@Override\n\tpublic boolean isLoggable(LogRecord record) {\n\t\tboolean matchTracker = false;\n\t\tif (tracker == null || tracker.getActiveResourcesSize() == 0) {\n\t\t\ttracker = sessionsByNodeId.get(jid);\n\t\t}\n\t\tif (tracker != null) {\n\t\t\tString msg = format.format(record);\n\t\t\tif (msg != null) {\n\t\t\t\tif (!matchTracker) {\n\t\t\t\t\tJID[] trackers = tracker.getConnectionIds();\n\t\t\t\t\tif (trackers != null && trackers.length > 0) {\n\t\t\t\t\t\tint i = 0;\n\t\t\t\t\t\twhile (!matchTracker && i < trackers.length) {\n\t\t\t\t\t\t\tmatchTracker = msg.contains(trackers[i++].toString());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!matchTracker) {\n\t\t\t\t\tJID[] trackers = tracker.getJIDs();\n\t\t\t\t\tif (trackers != null && trackers.length > 0) {\n\t\t\t\t\t\tint i = 0;\n\t\t\t\t\t\twhile (!matchTracker && i < trackers.length) {\n\t\t\t\t\t\t\tmatchTracker = msg.contains(trackers[i++].toString());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn matchTracker;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/util/log/LogWithStackTraceEntry.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.log;\n\npublic class LogWithStackTraceEntry {\n\n\tprivate long counter = 0;\n\tprivate String msg = null;\n\tprivate String record = null;\n\n\tpublic LogWithStackTraceEntry(String msg, String record) {\n\t\tthis.msg = msg;\n\t\tthis.record = record;\n\t}\n\n\tpublic long getCounter() {\n\t\treturn counter;\n\t}\n\n\tpublic String getMessage() {\n\t\treturn msg;\n\t}\n\n\tpublic String getRecord() {\n\t\treturn record;\n\t}\n\n\tpublic long increment() {\n\t\treturn ++counter;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/util/processing/ProcessingThreads.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.processing;\n\nimport tigase.server.Packet;\nimport tigase.xmpp.XMPPProcessorIfc;\nimport tigase.xmpp.XMPPResourceConnection;\n\nimport java.util.ArrayList;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Apr 21, 2009 8:50:50 PM\n *\n* @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class ProcessingThreads<E extends WorkerThread> {\n\n\tprivate static final Logger log = Logger.getLogger(ProcessingThreads.class.getName());\n\n\tprivate long droppedPackets = 0;\n\n\t// private int maxQueueSize = 10000;\n\tprivate String name = null;\n\n\t// Packets are put in queues in such a way that all packets for the same\n\t// user end-up in the same queue. This is important in some cases as\n\t// packet processing order does matter in some cases, especially for\n\t// roster processing.\n\t// Therefore it is also recommended that there is a single thread for\n\t// each queue but we can ditribute load increasing number of queues.\n\t// private int numQueues = 2;\n\tprivate int numWorkerThreads = 1;\n\tprivate ArrayList<E> workerThreads = null;\n\n\t@SuppressWarnings({\"unchecked\"})\n\tpublic ProcessingThreads(E worker, int numWorkerThreads, int maxQueueSize, String name)\n\t\t\tthrows ClassNotFoundException, InstantiationException, IllegalAccessException {\n\n\t\t// this.numQueues = numQueues;\n\t\t// this.maxQueueSize = maxQueueSize;\n\t\tthis.numWorkerThreads = numWorkerThreads;\n\t\tworkerThreads = new ArrayList<E>(numWorkerThreads);\n\t\tthis.name = name;\n\n//  for (int i = 0; i < numQueues; i++) {\n\t\t// LinkedBlockingQueue<QueueItem> queue = new LinkedBlockingQueue<QueueItem>(maxQueueSize);\n//  queues.add(queue);\n\t\tfor (int j = 0; j < numWorkerThreads; j++) {\n\t\t\tWorkerThread t = worker.getNewInstance();\n\n\t\t\tt.setQueueMaxSize(maxQueueSize);\n\t\t\tt.setDaemon(true);\n\t\t\tt.setName(name + \"-proc-\" + j);\n\t\t\tt.start();\n\t\t\tworkerThreads.add((E) t);\n\t\t\tlog.log(Level.FINEST, \"Created worker thread: {0}, queueSize: {1}\",\n\t\t\t\t\tnew Object[]{t.getName(), maxQueueSize});\n\t\t}\n\n//  }\n\t}\n\n\tpublic boolean addItem(XMPPProcessorIfc processor, Packet packet, XMPPResourceConnection conn) {\n\t\tboolean ret = false;\n\t\tQueueItem item = new QueueItem(processor, packet, conn);\n\n\t\ttry {\n\t\t\tif ((item.getConn() != null) && item.getConn().isAuthorized()) {\n\n\t\t\t\t// Queueing packets per user...\n\t\t\t\tret = workerThreads.get(Math.abs(conn.getJID().getBareJID().hashCode()) % numWorkerThreads).offer(item);\n\n//      ret = queues.get(Math.abs(conn.getJID().getBareJID().hashCode()\n//          % numQueues)).offer(item, packet.getPriority().ordinal());\n\t\t\t} else {\n\t\t\t\tif (packet.getPacketFrom() != null) {\n\n\t\t\t\t\t// Queueing packets per user's connection...\n\t\t\t\t\tret = workerThreads.get(Math.abs(packet.getPacketFrom().hashCode()) % numWorkerThreads).offer(item);\n\t\t\t\t} else {\n\n\t\t\t\t\t// Otherwise per destination address\n\t\t\t\t\t// If the packet elemTo is set then used it, otherwise just packetTo:\n\t\t\t\t\tif (packet.getStanzaTo() != null) {\n\t\t\t\t\t\tret = workerThreads.get(\n\t\t\t\t\t\t\t\tMath.abs(packet.getStanzaTo().getBareJID().hashCode()) % numWorkerThreads).offer(item);\n\n//          ret = queues.get(Math.abs(packet.getStanzaTo().hashCode() % numQueues)).offer(item,\n//              packet.getPriority().ordinal());\n\t\t\t\t\t} else {\n\t\t\t\t\t\tret = workerThreads.get(Math.abs(packet.getTo().hashCode())).offer(item);\n\n//          ret = queues.get(Math.abs(packet.getTo().hashCode() % numQueues)).offer(item,\n//              packet.getPriority().ordinal());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\n\t\t\t// This should not happen, but just in case until we are sure all\n\t\t\t// cases are catched.\n\t\t\t// Otherwise per destination address\n\t\t\t// If the packet elemTo is set then used it, otherwise just packetTo:\n\t\t\tif (packet.getStanzaTo() != null) {\n\t\t\t\tret = workerThreads.get(Math.abs(packet.getStanzaTo().getBareJID().hashCode()) % numWorkerThreads)\n\t\t\t\t\t\t.offer(item);\n\t\t\t} else {\n\t\t\t\tret = workerThreads.get(Math.abs(packet.getTo().hashCode()) % numWorkerThreads).offer(item);\n\t\t\t}\n\n\t\t\t// ret = nullQueue.offer(item, packet.getPriority().ordinal());\n\t\t}\n\n\t\tif (!ret) {\n\t\t\t++droppedPackets;\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Packet dropped due to queue overflow: {0}\", packet);\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t}\n\n\tpublic long getAverageProcessingTime() {\n\t\tlong average = 0;\n\t\tint counters = 0;\n\n\t\tfor (WorkerThread workerThread : workerThreads) {\n\t\t\tif (workerThread.getAverageProcessingTime() > 0) {\n\t\t\t\taverage += workerThread.getAverageProcessingTime();\n\t\t\t\t++counters;\n\t\t\t}\n\t\t}\n\n\t\tif (counters > 0) {\n\t\t\treturn average / counters;\n\t\t} else {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tpublic long getDroppedPackets() {\n\t\treturn droppedPackets;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic int getTotalQueueSize() {\n\t\tint ret = 0;\n\n\t\tfor (E pq : workerThreads) {\n\t\t\tret += pq.size();\n\t\t}\n\n\t\treturn ret;\n\t}\n\n\tpublic int getTotalRuns() {\n\t\tint ret = 0;\n\n\t\tfor (WorkerThread workerThread : workerThreads) {\n\t\t\tret += workerThread.getRunsCounter();\n\t\t}\n\n\t\treturn ret;\n\t}\n\n\tpublic void shutdown() {\n\t\tfor (WorkerThread workerThread : workerThreads) {\n\t\t\tworkerThread.shutdown();\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/util/processing/QueueItem.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.processing;\n\nimport tigase.server.Packet;\nimport tigase.xmpp.XMPPProcessorIfc;\nimport tigase.xmpp.XMPPResourceConnection;\n\n/**\n * Created: Apr 21, 2009 9:05:23 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class QueueItem {\n\n\tprivate XMPPResourceConnection conn;\n\tprivate Packet packet;\n\tprivate XMPPProcessorIfc processor;\n\n\tpublic QueueItem(XMPPProcessorIfc processor, Packet packet, XMPPResourceConnection conn) {\n\t\tif (processor == null) {\n\t\t\tthrow new NullPointerException(\"Processor parameter cannot be null!\");\n\t\t}\n\n\t\tif (packet == null) {\n\t\t\tthrow new NullPointerException(\"Packet parameter cannot be null!\");\n\t\t}\n\n\t\tthis.processor = processor;\n\t\tthis.packet = packet;\n\t\tthis.conn = conn;\n\t}\n\n\tpublic XMPPResourceConnection getConn() {\n\t\treturn conn;\n\t}\n\n\tpublic Packet getPacket() {\n\t\treturn packet;\n\t}\n\n\tpublic XMPPProcessorIfc getProcessor() {\n\t\treturn processor;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Packet=\" + packet + \", connection=\" + conn;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/util/processing/WorkerThread.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.processing;\n\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Apr 21, 2009 9:02:57 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic abstract class WorkerThread\n\t\textends Thread {\n\n\tprotected static final Logger log = Logger.getLogger(WorkerThread.class.getName());\n\n\tprivate long averageProcessingTime = 0;\n\n\t//private PriorityQueueAbstract<QueueItem> queue = null;\n\tprivate LinkedBlockingQueue<QueueItem> queue = null;\n\tprivate long runsCnt = 0;\n\tprivate boolean stopped = false;\n\n\tpublic abstract WorkerThread getNewInstance();\n\n\tpublic abstract void process(QueueItem item);\n\n\tpublic long getAverageProcessingTime() {\n\t\treturn averageProcessingTime;\n\t}\n\n\tpublic long getRunsCounter() {\n\t\treturn runsCnt;\n\t}\n\n\tpublic boolean offer(QueueItem item) {\n\t\treturn queue.offer(item);\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\tQueueItem item = null;\n\n\t\twhile (!stopped) {\n\t\t\ttry {\n\t\t\t\titem = queue.take();\n\n\t\t\t\tlong start = System.currentTimeMillis();\n\n\t\t\t\tprocess(item);\n\n\t\t\t\tlong end = System.currentTimeMillis() - start;\n\n\t\t\t\tif (end > 0) {\n\t\t\t\t\taverageProcessingTime = (averageProcessingTime + end) / 2;\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tif (!stopped) {\n\t\t\t\t\tlog.log(Level.SEVERE,\n\t\t\t\t\t\t\tthis.getClass().getName() + \",(\" + getName() + \") Exception during packet processing: \" +\n\t\t\t\t\t\t\t\t\titem, e);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t++runsCnt;\n\t\t}\n\t}\n\n\tpublic void setQueueMaxSize(int maxSize) {\n\t\tLinkedBlockingQueue<QueueItem> oldQueue = queue;\n\n\t\tqueue = new LinkedBlockingQueue<QueueItem>(maxSize);\n\n\t\tif (oldQueue != null) {\n\t\t\tqueue.addAll(oldQueue);\n\t\t}\n\t}\n\n\tpublic int size() {\n\t\treturn queue.size();\n\t}\n\n\tpublic void shutdown() {\n\t\tstopped = true;\n\t\ttry {\n\t\t\tthis.interrupt();\n\t\t} catch (Exception ex) {\n\t\t}\n\t}\n\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/util/reflection/ClassUtilBean.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.reflection;\n\nimport tigase.util.ClassComparator;\nimport tigase.util.ClassUtil;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.*;\nimport java.util.function.Predicate;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * Created by andrzej on 08.09.2016.\n */\npublic class ClassUtilBean {\n\n\tprivate static final String[] DEFAULT_PACKAGES_TO_SKIP = {\"com.fasterxml.jackson\", \"com.mongodb\", \"org.bson\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"com.mysql\", \"com.notnoop\", \"javax.jmdns\", \"javax.mail\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"javax.servlet\", \"org.apache.commons\", \"org.apache.derby\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"org.apache.felix\", \"org.apache.http.client\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"org.apache.xml\", \"org.bouncycastle\", \"org.eclipse.jetty\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"org.hamcrest.core\", \"org.postgresql\", \"org.slf4j\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"ch.qos.logback\", \"com.sun\", \"groovy\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"org.codehaus.groovy\", \"org.netbeans\", \"org.python\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"tigase.jaxmpp\", \"tigase.pubsub.Utils\"};\n\n\tprivate static ClassUtilBean instance;\n\tprivate static Logger log = Logger.getLogger(ClassUtilBean.class.getCanonicalName());\n\tprotected HashSet<Class<?>> classes = new HashSet<>();\n\n\tpublic static List<String> getPackagesToSkip(String[] packagesToSkip) {\n\t\tif (packagesToSkip == null) {\n\t\t\treturn Arrays.asList(DEFAULT_PACKAGES_TO_SKIP);\n\t\t} else {\n\t\t\treturn Stream.concat(Arrays.stream(DEFAULT_PACKAGES_TO_SKIP), Arrays.stream(packagesToSkip))\n\t\t\t\t\t.distinct()\n\t\t\t\t\t.collect(Collectors.toList());\n\t\t}\n\t}\n\n\tpublic static ClassUtilBean getInstance() {\n\t\tsynchronized (ClassUtilBean.class) {\n\t\t\tif (instance == null) {\n\t\t\t\tClassUtilBean instance = new ClassUtilBean();\n\t\t\t\tinstance.initialize(getPackagesToSkip(null));\n\t\t\t}\n\t\t\treturn instance;\n\t\t}\n\t}\n\n\tpublic ClassUtilBean() {\n\t}\n\n\tpublic void initialize(Collection<String> skipPackages) {\n\t\ttry {\n\t\t\tPredicate<String> filter = null;\n\t\t\tif (skipPackages == null) {\n\t\t\t\tfilter = className -> true;\n\t\t\t} else {\n\t\t\t\tList<String> packages = skipPackages.stream().map(packageName -> packageName + \".\").collect(Collectors.toList());\n\t\t\t\tfilter = className -> {\n\t\t\t\t\tif (ClassUtil.filterIncorrectMultiVersionClasses(className)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tfor (String packageName : packages) {\n\t\t\t\t\t\tif (className.startsWith(packageName)) {\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t};\n\t\t\t}\n\t\t\tclasses.addAll(ClassUtil.getClassesFromClassPath(filter));\n\t\t\t// support for handling debugging test cases started by Maven Surefire Plugin\n\t\t\t// as without it Tigase Kernel is not able to see annotated beans\n\t\t\tclasses.addAll(getClassesFromSurefireClassLoader());\n\t\t} catch (IOException | ClassNotFoundException e) {\n\t\t\tlog.log(Level.SEVERE, \"Could not initialize list of classes\", e);\n\t\t}\n\t\tsynchronized (ClassUtilBean.class) {\n\t\t\tinstance = this;\n\t\t}\n\t}\n\n\tpublic Set<Class<?>> getAllClasses() {\n\t\treturn Collections.unmodifiableSet(classes);\n\t}\n\n\tprivate Set<Class<?>> getClassesFromSurefireClassLoader() {\n\t\tSet<Class<?>> classes_set = new TreeSet<Class<?>>(new ClassComparator());\n\t\tString classpath = System.getProperty(\"surefire.test.class.path\");\n\n\t\tif (classpath == null) {\n\t\t\treturn classes_set;\n\t\t}\n\t\t// System.out.println(\"classpath: \"+classpath);\n\t\tStringTokenizer stok = new StringTokenizer(classpath, File.pathSeparator, false);\n\n\t\twhile (stok.hasMoreTokens()) {\n\t\t\tString path = stok.nextToken();\n\t\t\tFile file = new File(path);\n\n\t\t\tif (file.exists()) {\n\t\t\t\ttry {\n\t\t\t\t\tif (file.isDirectory()) {\n\n\t\t\t\t\t\t// System.out.println(\"directory: \"+path);\n\t\t\t\t\t\tSet<String> class_names = ClassUtil.getClassNamesFromDir(file);\n\n\t\t\t\t\t\ttigase.osgi.util.ClassUtil.getClassesFromNames(Thread.currentThread().getContextClassLoader(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   class_names).stream().forEach(classes_set::add);\n\t\t\t\t\t} // end of if (file.isDirectory())\n\n\t\t\t\t\tif (file.isFile()) {\n\n\t\t\t\t\t\t// System.out.println(\"jar file: \"+path);\n\t\t\t\t\t\tSet<String> class_names = ClassUtil.getClassNamesFromJar(file);\n\n\t\t\t\t\t\t//classes_set.addAll(tigase.osgi.util.ClassUtil.getClassesFromNames(class_names));\n\t\t\t\t\t\ttigase.osgi.util.ClassUtil.getClassesFromNames(Thread.currentThread().getContextClassLoader(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   class_names).stream().forEach(classes_set::add);\n\n\t\t\t\t\t\t// System.out.println(\"Loaded jar file: \"+path);\n\t\t\t\t\t} // end of if (file.isFile())\n\t\t\t\t} catch (ClassNotFoundException | IOException ex) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Could not load classes for \" + file.getAbsolutePath());\n\t\t\t\t}\n\t\t\t} // end of if (file.exists())\n\t\t} // end of while (stok.hasMoreTokens())\n\n\t\treturn classes_set;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/util/reflection/ReflectionHelper.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.reflection;\n\nimport tigase.kernel.BeanUtils;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.*;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Utility class with useful methods to work with reflections\n *\n * @author andrzej\n */\npublic class ReflectionHelper {\n\n\tpublic static boolean boundMatch(Class c1, TypeVariable t2) {\n\t\tType[] b2 = t2.getBounds();\n\t\tif (b2.length != 1) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn (b2[0] instanceof Class) && ((Class) b2[0]).isAssignableFrom(c1);\n\t}\n\n\tpublic static boolean boundMatch(TypeVariable t1, TypeVariable t2) {\n\t\tType[] b1 = t1.getBounds();\n\t\tType[] b2 = t2.getBounds();\n\t\tif (b1.length != b2.length) {\n\t\t\treturn false;\n\t\t}\n\n\t\tboolean match = true;\n\t\tfor (int i = 0; i < b1.length; i++) {\n\t\t\tmatch &= b1[i].equals(b2[i]) || ((b1[i] instanceof Class) && (b2[i] instanceof Class) &&\n\t\t\t\t\t((Class) b1[i]).isAssignableFrom((Class) b2[i]));\n\t\t}\n\t\treturn match;\n\t}\n\n\tpublic static boolean classMatchesClassWithParameters(Class clazz, Class requiredType, Type[] requiredTypeParams) {\n\t\tParameterizedType pt = new ParameterizedType() {\n\t\t\t@Override\n\t\t\tpublic Type[] getActualTypeArguments() {\n\t\t\t\treturn requiredTypeParams;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Type getRawType() {\n\t\t\t\treturn requiredType;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Type getOwnerType() {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t};\n\t\treturn compareTypes(pt, clazz, null, null);\n\t}\n\n\tpublic static boolean classMatchesType(Class clazz, Type required) {\n\t\tif (required instanceof ParameterizedType) {\n\t\t\tParameterizedType pt = (ParameterizedType) required;\n\t\t\tClass rt = (Class) pt.getRawType();\n\t\t\tType[] ap = pt.getActualTypeArguments();\n\t\t\treturn classMatchesClassWithParameters(clazz, rt, ap);\n\t\t} else if (required instanceof Class) {\n\t\t\treturn ((Class) required).isAssignableFrom(clazz);\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * This method collects every method of consumer class annotated with passed annotation and for each of them\n\t * executes implementation of Handler.\n\t *\n\t */\n\tpublic static <A extends Annotation, T> Collection<T> collectAnnotatedMethods(final Object consumer,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  Class<A> annotationCls,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  Handler<A, T> handler) {\n\t\tArrayList<T> result = new ArrayList<>();\n\n\t\tMethod[] methods = BeanUtils.getAllMethods(consumer.getClass());\n\n\t\tfor (Method method : methods) {\n\t\t\tA annotation = method.getAnnotation(annotationCls);\n\t\t\tif (annotation == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tresult.add(handler.process(consumer, method, annotation));\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate static boolean compareParameterizedTypes(ParameterizedType expected, ParameterizedType actual,\n\t\t\t\t\t\t\t\t\t\t\t\t\t Map<TypeVariable<?>, Type> ownerExpectedTypesMap,\n\t\t\t\t\t\t\t\t\t\t\t\t\t Map<TypeVariable<?>, Type> ownerActualTypesMap) {\n\t\tClass expectedRawType = (Class) expected.getRawType();\n\t\tClass actualRawType = (Class) actual.getRawType();\n\t\tif (!expectedRawType.isAssignableFrom(actualRawType)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tboolean result = true;\n\t\tType[] actualTypes = actual.getActualTypeArguments();\n\t\tType[] expectedTypes = expected.getActualTypeArguments();\n\t\tif (actualTypes.length == expectedTypes.length) {\n\t\t\tfor (int i = 0; i < expectedTypes.length; i++) {\n\t\t\t\tType expectedType = resolveType(expectedTypes[i], ownerExpectedTypesMap);\n\t\t\t\tType actualType = resolveType(actualTypes[i], ownerActualTypesMap);\n\n\t\t\t\tif (!compareTypes(expectedType, actualType, ownerExpectedTypesMap, ownerActualTypesMap)) {\n\t\t\t\t\tresult = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (result) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} else {\n\t\t\tif (actualRawType.isInterface()) {\n\t\t\t\tfor (Type ifc : actualRawType.getGenericInterfaces()) {\n\t\t\t\t\tif (compareTypes(expected, ifc, ownerExpectedTypesMap, ownerActualTypesMap)) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tpublic static boolean compareTypes(Type expectedType, Type actualType,\n\t\t\t\t\t\t\t\t\t   Map<TypeVariable<?>, Type> ownerExpectedTypesMap,\n\t\t\t\t\t\t\t\t\t   Map<TypeVariable<?>, Type> ownerActualTypesMap) {\n\t\tif (expectedType.equals(actualType)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (expectedType instanceof WildcardType) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (expectedType instanceof TypeVariable) {\n\t\t\texpectedType = ReflectionHelper.resolveType(expectedType, ownerExpectedTypesMap);\n\t\t\tif (expectedType instanceof TypeVariable) {\n\t\t\t\tif (actualType instanceof TypeVariable) {\n\t\t\t\t\treturn ReflectionHelper.boundMatch((TypeVariable) expectedType, (TypeVariable) actualType);\n\t\t\t\t} else if (actualType instanceof Class) {\n\t\t\t\t\tType expectedBound = ((TypeVariable) expectedType).getBounds()[0];\n\t\t\t\t\treturn compareTypes(expectedBound, actualType, ownerExpectedTypesMap, ownerActualTypesMap);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn compareTypes(expectedType, actualType, ownerExpectedTypesMap, ownerActualTypesMap);\n\t\t\t}\n\t\t}\n\n\t\tif (expectedType instanceof Class) {\n\t\t\tif (actualType instanceof Class) {\n\t\t\t\treturn (((Class) expectedType).isAssignableFrom((Class) actualType));\n\t\t\t} else if (actualType instanceof ParameterizedType) {\n\t\t\t\treturn expectedType.equals(((ParameterizedType) actualType).getRawType());\n\t\t\t} else if (actualType instanceof TypeVariable) {\n\t\t\t\treturn ReflectionHelper.boundMatch((Class) expectedType, (TypeVariable) actualType);\n\t\t\t} else {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else if (expectedType instanceof ParameterizedType) {\n\t\t\tif (actualType instanceof ParameterizedType) {\n\t\t\t\treturn compareParameterizedTypes((ParameterizedType) expectedType, (ParameterizedType) actualType,\n\t\t\t\t\t\t\t\t\t\t\t\t ownerExpectedTypesMap, ownerActualTypesMap);\n\t\t\t} else if (actualType instanceof Class) {\n\t\t\t\tType tmp = actualType;\n\t\t\t\tif (ownerActualTypesMap == null) {\n\t\t\t\t\townerActualTypesMap = ReflectionHelper.createGenericsTypeMap((Class) actualType);\n\t\t\t\t}\n\t\t\t\tClass expectedClass = (Class) ((ParameterizedType) expectedType).getRawType();\n\t\t\t\tClass classToCheck;\n\t\t\t\twhile (tmp != null) {\n\t\t\t\t\tif (tmp instanceof ParameterizedType) {\n\t\t\t\t\t\tParameterizedType pt = (ParameterizedType) tmp;\n\t\t\t\t\t\tif (compareParameterizedTypes((ParameterizedType) expectedType, pt, ownerExpectedTypesMap,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  ownerActualTypesMap)) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tclassToCheck = (Class) pt.getRawType();\n\t\t\t\t\t} else if (tmp instanceof Class) {\n\t\t\t\t\t\tclassToCheck = (Class) tmp;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclassToCheck = null;\n\t\t\t\t\t}\n\t\t\t\t\tif (classToCheck != null) {\n\t\t\t\t\t\tType[] ifcs = classToCheck.getGenericInterfaces();\n\t\t\t\t\t\tfor (Type ifc : ifcs) {\n\t\t\t\t\t\t\tif (ifc instanceof ParameterizedType) {\n\t\t\t\t\t\t\t\tif (compareParameterizedTypes((ParameterizedType) expectedType, (ParameterizedType) ifc,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  ownerExpectedTypesMap, ownerActualTypesMap)) {\n\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (expectedClass.equals(classToCheck)) {\n\t\t\t\t\t\t\tif (compareParameterizedTypes((ParameterizedType) expectedType, (ParameterizedType) tmp,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  ownerExpectedTypesMap, ownerActualTypesMap)) {\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ttmp = classToCheck.getGenericSuperclass();\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttmp = null;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tpublic static Map<TypeVariable<?>, Type> createGenericsTypeMap(Class<?> cls) {\n\t\tMap<TypeVariable<?>, Type> map = new HashMap<>();\n\t\tcreateGenericsTypeMap(map, cls.getGenericInterfaces());\n\t\tType genericType = cls.getGenericSuperclass();\n\t\tClass<?> type = cls.getSuperclass();\n\t\twhile (type != null && !Object.class.equals(type)) {\n\t\t\tif (genericType instanceof ParameterizedType) {\n\t\t\t\tcreateGenericsTypeMap(map, (ParameterizedType) genericType);\n\t\t\t}\n\t\t\tcreateGenericsTypeMap(map, type.getGenericInterfaces());\n\t\t\tgenericType = type.getGenericSuperclass();\n\t\t\ttype = type.getSuperclass();\n\t\t}\n\t\treturn map;\n\t}\n\n\tprivate static void createGenericsTypeMap(Map<TypeVariable<?>, Type> map, Type[] ifcs) {\n\t\tfor (Type ifc : ifcs) {\n\t\t\tif (ifc instanceof ParameterizedType) {\n\t\t\t\tcreateGenericsTypeMap(map, (ParameterizedType) ifc);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static Map<TypeVariable<?>, Type> createGenericsTypeMap(Map<TypeVariable<?>, Type> map,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tParameterizedType type) {\n\t\tTypeVariable<?>[] tvs = ((Class) type.getRawType()).getTypeParameters();\n\t\tType[] ap = type.getActualTypeArguments();\n\t\tfor (int i = 0; i < tvs.length; i++) {\n\t\t\tmap.put(tvs[i], ap[i]);\n\t\t}\n\t\treturn map;\n\t}\n\n\tpublic static Type getCollectionParamter(Type genericType, Class clazz) {\n\t\tif (genericType instanceof ParameterizedType) {\n\t\t\treturn ((ParameterizedType) genericType).getActualTypeArguments()[0];\n//\t\t\tType type = ((ParameterizedType) genericType).getActualTypeArguments()[0];\n//\t\t\tif (!(type instanceof Class)) {\n//\t\t\t\tMap<TypeVariable<?>, Type> map = createGenericsTypeMap(clazz);\n//\t\t\t\twhile (type instanceof TypeVariable) {\n//\t\t\t\t\ttype = map.get((TypeVariable<?>) type);\n//\t\t\t\t}\n//\t\t\t}\n//\n//\t\t\tif (!(type instanceof Class)) {\n//\n//\t\t\t}\n//\t\t\treturn (Class) type;\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static Class<?> getItemClassOfGenericCollection(Field f) {\n\t\tType genericType = f.getGenericType();\n\t\tif (genericType == null || !(genericType instanceof ParameterizedType)) {\n\t\t\treturn null;\n\t\t}\n\n\t\tType[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments();\n\t\tif (actualTypeArguments == null || actualTypeArguments.length != 1) {\n\t\t\treturn null;\n\t\t}\n\n\t\tType type = actualTypeArguments[0];\n\t\treturn (type instanceof Class) ? (Class) type : null;\n\t}\n\n\tprivate static Type resolveType(Type t, Map<TypeVariable<?>, Type> ownerMap) {\n\t\tif (ownerMap == null) {\n\t\t\treturn t;\n\t\t}\n\t\twhile (t instanceof TypeVariable && ownerMap.containsKey(t)) {\n\t\t\tt = ownerMap.get(t);\n\t\t}\n\t\tif (t instanceof TypeVariable && ((TypeVariable) t).getBounds() != null &&\n\t\t\t\t((TypeVariable) t).getBounds().length > 0) {\n\t\t\tt = ((TypeVariable) t).getBounds()[0];\n\t\t}\n\t\treturn t;\n\t}\n\n\tpublic interface Handler<A extends Annotation, T> {\n\n\t\tT process(Object consumer, Method method, A annotation);\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/util/repository/DataTypes.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.repository;\n\nimport tigase.xmpp.jid.JID;\n\nimport java.lang.reflect.Array;\nimport java.util.Arrays;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: May 28, 2009 7:39:07 AM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class DataTypes {\n\n\tpublic static final Map<String, Character> typesMap = new LinkedHashMap<String, Character>();\n\n\tprivate static final Logger log = Logger.getLogger(DataTypes.class.getName());\n\tstatic {\n\t\ttypesMap.put(String.class.getName(), 'S');\n\t\ttypesMap.put(Long.class.getName(), 'L');\n\t\ttypesMap.put(Integer.class.getName(), 'I');\n\t\ttypesMap.put(Boolean.class.getName(), 'B');\n\t\ttypesMap.put(Float.class.getName(), 'F');\n\t\ttypesMap.put(Double.class.getName(), 'D');\n\t\ttypesMap.put(JID.class.getName(), 'J');\n\t\ttypesMap.put(String[].class.getName(), 's');\n\t\ttypesMap.put(Long[].class.getName(), 'l');\n\t\ttypesMap.put(Integer[].class.getName(), 'i');\n\t\ttypesMap.put(Boolean[].class.getName(), 'b');\n\t\ttypesMap.put(Float[].class.getName(), 'f');\n\t\ttypesMap.put(Double[].class.getName(), 'd');\n\t\ttypesMap.put(long[].class.getName(), 'l');\n\t\ttypesMap.put(int[].class.getName(), 'i');\n\t\ttypesMap.put(boolean[].class.getName(), 'b');\n\t\ttypesMap.put(float[].class.getName(), 'f');\n\t\ttypesMap.put(double[].class.getName(), 'd');\n\t\ttypesMap.put(JID[].class.getName(), 'j');\n\t}\n\n\t// public static char[] sizeChars = {'k', 'K', 'm', 'M', 'g', 'G', 't', 'T'};\n\n\tpublic static char decodeTypeIdFromName(String name) {\n\t\tchar result = 'S';\n\n\t\tif (name.endsWith(\"]\")) {\n\t\t\tresult = name.charAt(name.length() - 2);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic static Object decodeValueType(char typeId, String value) throws IllegalArgumentException {\n\t\tObject result = value.trim();\n\t\tif (\"null\".equalsIgnoreCase(value.trim())) {\n\t\t\treturn null;\n\t\t}\n\n\t\ttry {\n\t\t\tswitch (typeId) {\n\t\t\t\tcase 'L':\n\n\t\t\t\t\t// Long value\n\t\t\t\t\tresult = Long.decode(value.trim());\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'I':\n\n\t\t\t\t\t// Integer value\n\t\t\t\t\tresult = Integer.decode(value.trim());\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'B':\n\n\t\t\t\t\t// Boolean value\n\t\t\t\t\tresult = parseBool(value.trim());\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'F':\n\n\t\t\t\t\t// Float value\n\t\t\t\t\tresult = Float.parseFloat(value.trim());\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'D':\n\n\t\t\t\t\t// Double value\n\t\t\t\t\tresult = Double.parseDouble(value.trim());\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'J':\n\t\t\t\t\tresult = JID.jidInstance(value);\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 's':\n\n\t\t\t\t\t// Comma separated, Strings array\n\t\t\t\t\tString[] s_str = value.split(\",\");\n\t\t\t\t\tString[] trimed_str = new String[s_str.length];\n\t\t\t\t\tint si = 0;\n\n\t\t\t\t\tfor (String s : s_str) {\n\t\t\t\t\t\ttrimed_str[si++] = s.trim();\n\t\t\t\t\t}\n\t\t\t\t\tresult = trimed_str;\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'l':\n\n\t\t\t\t\t// Comma separated, long array\n\t\t\t\t\tString[] longs_str = value.split(\",\");\n\t\t\t\t\tlong[] longs = new long[longs_str.length];\n\t\t\t\t\tint l = 0;\n\n\t\t\t\t\tfor (String s : longs_str) {\n\t\t\t\t\t\tlongs[l++] = Long.parseLong(s.trim());\n\t\t\t\t\t}\n\t\t\t\t\tresult = longs;\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'i':\n\n\t\t\t\t\t// Comma separated, int array\n\t\t\t\t\tString[] ints_str = value.split(\",\");\n\t\t\t\t\tint[] ints = new int[ints_str.length];\n\t\t\t\t\tint i = 0;\n\n\t\t\t\t\tfor (String s : ints_str) {\n\t\t\t\t\t\tints[i++] = Integer.parseInt(s.trim());\n\t\t\t\t\t}\n\t\t\t\t\tresult = ints;\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'b':\n\n\t\t\t\t\t// Comma separated, boolean array\n\t\t\t\t\tString[] bools_str = value.split(\",\");\n\t\t\t\t\tboolean[] bools = new boolean[bools_str.length];\n\t\t\t\t\tint b = 0;\n\n\t\t\t\t\tfor (String s : bools_str) {\n\t\t\t\t\t\tbools[b++] = parseBool(s.trim());\n\t\t\t\t\t}\n\t\t\t\t\tresult = bools;\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'f':\n\n\t\t\t\t\t// Comma separated, float array\n\t\t\t\t\tString[] float_str = value.split(\",\");\n\t\t\t\t\tfloat[] floats = new float[float_str.length];\n\t\t\t\t\tint f = 0;\n\n\t\t\t\t\tfor (String s : float_str) {\n\t\t\t\t\t\tfloats[f++] = Float.parseFloat(s.trim());\n\t\t\t\t\t}\n\t\t\t\t\tresult = floats;\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'd':\n\n\t\t\t\t\t// Comma separated, double array\n\t\t\t\t\tString[] doubles_str = value.split(\",\");\n\t\t\t\t\tdouble[] doubles = new double[doubles_str.length];\n\t\t\t\t\tint d = 0;\n\n\t\t\t\t\tfor (String s : doubles_str) {\n\t\t\t\t\t\tdoubles[d++] = Double.parseDouble(s.trim());\n\t\t\t\t\t}\n\t\t\t\t\tresult = doubles;\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'j':\n\t\t\t\t\tString[] jids_str = value.split(\",\");\n\t\t\t\t\tJID[] jids = new JID[jids_str.length];\n\t\t\t\t\tint j = 0;\n\n\t\t\t\t\tfor (String s : jids_str) {\n\t\t\t\t\t\tjids[j++] = JID.jidInstance(s);\n\t\t\t\t\t}\n\t\t\t\t\tresult = jids;\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\n\t\t\t\t\t// Do nothing, default to String\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new IllegalArgumentException(e);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic static String encodeTypeIdInName(String name, Object value) {\n\t\tchar t = DataTypes.getTypeId(value);\n\n\t\treturn name + \"[\" + t + \"]\";\n\t}\n\n\tpublic static boolean getProperty(String key, Boolean def) {\n\t\tString val = System.getProperty(key, (def != null) ? def.toString() : null);\n\n\t\treturn parseBool(val);\n\t}\n\n\tpublic static char getTypeId(Object instance) {\n\t\tCharacter result;\n\n\t\tif (instance == null) {\n\t\t\tresult = 'S';\n\t\t} else {\n\t\t\tresult = typesMap.get(instance.getClass().getName());\n\t\t}\n\t\tif (result == null) {\n\t\t\tresult = 'S';\n\t\t}\n\n\t\treturn result.charValue();\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tSystem.out.println(parseSizeInt(\"256k\", 1));\n\t\tSystem.out.println(parseNum(\"256k\", Integer.class, 1));\n\t\tSystem.out.println(parseNum(\"655k\", Double.class, 1D));\n\t\tSystem.out.println(parseNum(\"256k\", Float.class, 1F));\n\t\tSystem.out.println(parseNum(\"256k\", Long.class, 1L));\n\t\tSystem.out.println(parseNum(\"25\", Short.class, Short.valueOf(\"1\")));\n\t\tSystem.out.println(parseNum(\"25\", Byte.class, (byte) 1));\n\t}\n\n\tpublic static boolean parseBool(final String val) {\n\t\treturn (val != null) &&\n\t\t\t\t(val.equalsIgnoreCase(\"yes\") || val.equalsIgnoreCase(\"true\") || val.equalsIgnoreCase(\"on\") ||\n\t\t\t\t\t\tval.equals(\"1\"));\n\t}\n\n\tpublic static <T extends Number> T parseNum(String num, Class<T> cls, T def) {\n\t\tif (num == null) {\n\t\t\treturn def;\n\t\t}\n\n\t\tT result = def;\n\t\tString toParse = num;\n\t\tLong multiplier = 1L;\n\n\t\ttry {\n\t\t\tswitch (num.charAt(num.length() - 1)) {\n\t\t\t\tcase 'k':\n\t\t\t\tcase 'K':\n\t\t\t\t\tmultiplier = 1024L;\n\t\t\t\t\ttoParse = num.substring(0, num.length() - 1);\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'm':\n\t\t\t\tcase 'M':\n\t\t\t\t\tmultiplier = 1024L * 1024L;\n\t\t\t\t\ttoParse = num.substring(0, num.length() - 1);\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'g':\n\t\t\t\tcase 'G':\n\t\t\t\t\tmultiplier = 1024L * 1024L * 1024L;\n\t\t\t\t\ttoParse = num.substring(0, num.length() - 1);\n\n\t\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (cls.equals(Integer.class)) {\n\t\t\t\tresult = cls.cast(Integer.valueOf(toParse) * multiplier.intValue());\n\t\t\t} else if (cls.equals(Long.class)) {\n\t\t\t\tresult = cls.cast(Long.valueOf(toParse) * multiplier);\n\t\t\t} else if (cls.equals(Double.class)) {\n\t\t\t\tresult = cls.cast(Double.valueOf(toParse) * multiplier.doubleValue());\n\t\t\t} else if (cls.equals(Float.class)) {\n\t\t\t\tresult = cls.cast(Float.valueOf(toParse) * multiplier.floatValue());\n\t\t\t} else if (cls.equals(Byte.class)) {\n\t\t\t\tInteger res = Byte.valueOf(toParse) * multiplier.byteValue();\n\t\t\t\tresult = cls.cast(res.byteValue());\n\t\t\t} else if (cls.equals(Short.class)) {\n\t\t\t\tInteger res = Short.valueOf(toParse) * multiplier.shortValue();\n\t\t\t\tresult = cls.cast(res.shortValue());\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Error parsing value: {0} as {1}, using default: {2}\", new Object[]{num, cls, def});\n\t\t\treturn def;\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic static int parseSizeInt(String size, int def) {\n\t\treturn parseNum(size, Integer.class, def);\n\t}\n\n\tpublic static String stripNameFromTypeId(String name) {\n\t\tif (name.endsWith(\"]\")) {\n\t\t\treturn name.substring(0, name.length() - 3);\n\t\t} else {\n\t\t\treturn name;\n\t\t}\n\t}\n\n\tpublic static String valueToString(final Object value) {\n\n\t\tif (value == null) {\n\t\t\treturn \"<null>\";\n\t\t}\n\n\t\tif (value.getClass().isArray()) {\n\n\t\t\tif (Array.getLength(value) == 0) {\n\t\t\t\treturn \"\";\n\t\t\t}\n\n\t\t\tString varr = null;\n\t\t\tchar t = DataTypes.getTypeId(value);\n\t\t\tswitch (t) {\n\t\t\t\tcase 'l':\n\t\t\t\t\tvarr = value instanceof long[] ? Arrays.toString((long[]) value) : Arrays.toString((Long[]) value);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'i':\n\t\t\t\t\tvarr = value instanceof int[] ? Arrays.toString((int[]) value) : Arrays.toString((Integer[]) value);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'b':\n\t\t\t\t\tvarr = value instanceof boolean[]\n\t\t\t\t\t\t   ? Arrays.toString((boolean[]) value)\n\t\t\t\t\t\t   : Arrays.toString((Boolean[]) value);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'f':\n\t\t\t\t\tvarr = value instanceof float[]\n\t\t\t\t\t\t   ? Arrays.toString((float[]) value)\n\t\t\t\t\t\t   : Arrays.toString((Float[]) value);\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase 'd':\n\t\t\t\t\tvarr = value instanceof double[]\n\t\t\t\t\t\t   ? Arrays.toString((double[]) value)\n\t\t\t\t\t\t   : Arrays.toString((Double[]) value);\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tvarr = Arrays.toString((Object[]) value);\n\t\t\t}\n\t\t\treturn varr.substring(1, varr.length() - 1);\n\t\t}\n\n\t\treturn value.toString();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/util/repository/RepositoryUtils.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.repository;\n\nimport tigase.db.AuthRepository;\nimport tigase.db.RepositoryFactory;\nimport tigase.db.UserExistsException;\nimport tigase.db.UserRepository;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xmpp.impl.roster.RosterAbstract;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.io.BufferedReader;\nimport java.io.FileReader;\nimport java.io.FileWriter;\nimport java.io.Writer;\nimport java.util.List;\n\n/**\n * Describe class RepositoryUtils here.\n * <br>\n * Created: Sat Oct 28 13:09:26 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class RepositoryUtils {\n\n\tprivate static boolean add = false;\n\tprivate static boolean add_user_test = false;\n\tprivate static boolean allowed_empty_groups = true;\n\tprivate static boolean check_roster = false;\n\tprivate static String content = null;\n\tprivate static boolean copy_repos = false;\n\tprivate static long counter = 0;\n\tprivate static boolean del = false;\n\tprivate static String dst_class = null;\n\tprivate static String dst_uri = null;\n\tprivate static boolean export_data = false;\n\tprivate static String export_file = null;\n\tprivate static boolean import_data = false;\n\tprivate static String import_file = null;\n\tprivate static String key = null;\n\tprivate static boolean key_val = false;\n\tprivate static boolean node = false;\n\tprivate static boolean print_repo = false;\n\tprivate static boolean simple_test = false;\n\tprivate static String src_class = \"tigase.db.jdbc.JDBCRepository\";\n\tprivate static String src_uri = null;\n\tprivate static String subnode = null;\n\tprivate static BareJID user = null;\n\tprivate static BareJID user1 = BareJID.bareJIDInstanceNS(\"user111@hostname\");\n\tprivate static BareJID user2 = BareJID.bareJIDInstanceNS(\"user222@hostname\");\n\tprivate static BareJID user3 = BareJID.bareJIDInstanceNS(\"user333@hostname\");\n\tprivate static String value = null;\n\n\n\tpublic static boolean checkContact(BareJID user, UserRepository repo, String cont) throws Exception {\n\n\t\t// String[] keys = repo.getKeys(user, \"roster/\"+contact);\n\t\tJID contact = JID.jidInstanceNS(cont);\n\t\tString[] vals = repo.getDataList(user, \"roster/\" + contact, RosterAbstract.GROUPS);\n\n\t\tif ((vals == null) || (vals.length == 0)) {\n\t\t\tSystem.out.println(\"      Empty groups list\");\n\n\t\t\tif (!allowed_empty_groups) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else {\n\t\t\tfor (String val : vals) {\n\t\t\t\tif (val.equals(\"Upline Support\") || val.equals(\"Support\") || val.startsWith(\"Level \")) {\n\t\t\t\t\tSystem.out.println(\"      Invalid group: \" + val);\n\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tpublic static void copyNode(BareJID user, String node, UserRepository src, UserRepository dst) throws Exception {\n\t\tString[] keys = src.getKeys(user, node);\n\n\t\tif (keys != null) {\n\t\t\tfor (String key : keys) {\n\t\t\t\tString[] vals = src.getDataList(user, node, key);\n\n\t\t\t\tif (vals != null) {\n\t\t\t\t\tdst.setDataList(user, node, key, vals);\n\t\t\t\t}    // end of if (vals != null) else\n\t\t\t}      // end of for (String key: keys)\n\t\t}        // end of if (keys != null)\n\n\t\tString[] nodes = src.getSubnodes(user, node);\n\n\t\tif (nodes != null) {\n\t\t\tfor (String subnode : nodes) {\n\t\t\t\tcopyNode(user, ((node != null) ? node + \"/\" + subnode : subnode), src, dst);\n\t\t\t}    // end of for (String node: nodes)\n\t\t}      // end of if (ndoes != null)\n\t}\n\n\tpublic static void copyRepositories(UserRepository src, UserRepository dst) throws Exception {\n\t\tif (user != null) {\n\t\t\tcopyUser(user, src, dst);\n\t\t} else {\n\t\t\tList<BareJID> users = src.getUsers();\n\n\t\t\tif (users != null) {\n\t\t\t\tSystem.out.println(\"Found \" + users.size() + \" in the source repository.\");\n\n\t\t\t\tfor (BareJID usr : users) {\n\t\t\t\t\tSystem.out.println(\"Found \" + usr + \" in the source repository.\");\n\t\t\t\t\tcopyUser(usr, src, dst);\n\t\t\t\t}    // end of for (String user: users)\n\t\t\t} else {\n\t\t\t\tSystem.out.println(\"There are no user accounts in source repository.\");\n\t\t\t}      // end of else\n\t\t}        // end of if (user != null) else\n\t}\n\n\tpublic static void copyRepositories(UserRepository src, AuthRepository dst) throws Exception {\n\t\tif (user != null) {\n\t\t\tcopyUser(user, src, dst);\n\t\t} else {\n\t\t\tList<BareJID> users = src.getUsers();\n\n\t\t\tif (users != null) {\n\t\t\t\tfor (BareJID usr : users) {\n\t\t\t\t\tcopyUser(usr, src, dst);\n\t\t\t\t}    // end of for (String user: users)\n\t\t\t} else {\n\t\t\t\tSystem.out.println(\"There are no user accounts in source repository.\");\n\t\t\t}      // end of else\n\t\t}        // end of if (user != null) else\n\t}\n\n\tpublic static void copyUser(BareJID user, UserRepository src, UserRepository dst) throws Exception {\n\t\tif (user == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tSystem.out.print(\"Copying user: \" + user + \"...\");\n\n\t\ttry {\n\t\t\tdst.addUser(user);\n\t\t\tcopyNode(user, null, src, dst);\n\t\t\tSystem.out.println(\"OK\");\n\t\t} catch (UserExistsException e) {\n\t\t\tSystem.out.println(\"ERROR, user already exists.\");\n\t\t}\n\t}\n\n\tpublic static void copyUser(BareJID user, UserRepository src, AuthRepository dst) throws Exception {\n\t\tif ((user == null)) {\n\t\t\treturn;\n\t\t}\n\n\t\tSystem.out.print(\"Copying user: \" + user + \"...\");\n\n\t\tString password = src.getData(user, \"password\");\n\n\t\ttry {\n\t\t\tdst.addUser(user, password);\n\t\t\tSystem.out.println(\"OK\");\n\t\t} catch (UserExistsException e) {\n\t\t\tSystem.out.println(\"ERROR, user already exists.\");\n\t\t}\n\t}\n\n\tpublic static void exportRoster(UserRepository repo, Writer w) throws Exception {\n\t\tif (user != null) {\n\t\t\texportUserRoster(user, repo, w);\n\t\t} else {\n\t\t\tList<BareJID> users = repo.getUsers();\n\n\t\t\tif (users != null) {\n\t\t\t\tfor (BareJID usr : users) {\n\n\t\t\t\t\t// System.out.println(usr);\n\t\t\t\t\texportUserRoster(usr, repo, w);\n\t\t\t\t}    // end of for (String user: users)\n\t\t\t} else {\n\t\t\t\tSystem.out.println(\"There are no user accounts in repository.\");\n\t\t\t}      // end of else\n\t\t}\n\t}\n\n\tpublic static void exportUserRoster(BareJID user, UserRepository repo, Writer w) throws Exception {\n\t\tSystem.out.println(\"  \" + (++counter) + \". \" + user + \" roster: \");\n\n\t\tString[] contacts = repo.getSubnodes(user, \"roster\");\n\n\t\tif (contacts != null) {\n\t\t\tfor (String contact : contacts) {\n\t\t\t\tSystem.out.println(\"    contact: \" + contact);\n\n\t\t\t\tboolean valid = checkContact(user, repo, contact);\n\n\t\t\t\tif (valid) {\n\t\t\t\t\tSystem.out.println(\"      looks OK\");\n\n\t\t\t\t\tString password = repo.getData(user, \"password\");\n\t\t\t\t\tString[] groups = repo.getDataList(user, \"roster/\" + contact, RosterAbstract.GROUPS);\n\t\t\t\t\tString contact_nick = repo.getData(user, \"roster/\" + contact, RosterAbstract.NAME);\n\t\t\t\t\tString subscription = repo.getData(user, \"roster/\" + contact, RosterAbstract.SUBSCRIPTION);\n\t\t\t\t\tStringBuilder sb = new StringBuilder(user.toString());\n\n\t\t\t\t\tsb.append(\",\");\n\n\t\t\t\t\tif (password != null) {\n\t\t\t\t\t\tsb.append(password);\n\t\t\t\t\t}\n\n\t\t\t\t\tsb.append(\",\" + contact);\n\t\t\t\t\tsb.append(\",\");\n\n\t\t\t\t\tif (contact_nick != null) {\n\t\t\t\t\t\tsb.append(contact_nick);\n\t\t\t\t\t}\n\n\t\t\t\t\tsb.append(\",\");\n\n\t\t\t\t\tif (subscription != null) {\n\t\t\t\t\t\tsb.append(subscription);\n\t\t\t\t\t}\n\n\t\t\t\t\tif ((groups != null) && (groups.length > 0)) {\n\t\t\t\t\t\tfor (String group : groups) {\n\t\t\t\t\t\t\tsb.append(\",\" + group);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tsb.append(\"\\n\");\n\t\t\t\t\tw.write(sb.toString());\n\t\t\t\t} else {\n\t\t\t\t\tSystem.out.println(\"      should be REMOVED\");\n\n\t\t\t\t\tString contact_node = \"roster/\" + contact;\n\n\t\t\t\t\tSystem.out.println(\"      removing node: \" + contact_node);\n\n\t\t\t\t\t// repo.removeSubnode(user, contact_node);\n\t\t\t\t\tSystem.out.println(\"      DONE.\");\n\t\t\t\t}\n\t\t\t}    // end of for (String node: nodes)\n\t\t} else {\n\t\t\tSystem.out.println(\"    empty roster...\");\n\t\t}\n\t}\n\n\tprivate static String help() {\n\t\treturn \"\\n\" + \"Parameters:\\n\" + \" -h          this help message\\n\" +\n\t\t\t\t\" -sc class   source repository class name\\n\" + \" -su uri     source repository init string\\n\" +\n\t\t\t\t\" -dc class   destination repository class name\\n\" +\n\t\t\t\t\" -du uri     destination repository init string\\n\" +\n\t\t\t\t\" -dt string  data content to set/remove in repository\\n\" +\n\t\t\t\t\" -u user     user ID, if given all operations are only for that ID\\n\" +\n\t\t\t\t\"             if you want to add user to AuthRepository parameter must\\n\" +\n\t\t\t\t\"             in form: \\\"user:password\\\"\\n\" + \" -st         perform simple test on repository\\n\" +\n\t\t\t\t\" -at         simple test for adding and removing user\\n\" +\n\t\t\t\t\" -cp         copy content from source to destination repository\\n\" +\n\t\t\t\t\" -pr         print content of the repository\\n\" +\n\t\t\t\t\" -n          data content string is a node string\\n\" +\n\t\t\t\t\" -kv         data content string is node/key=value string\\n\" +\n\t\t\t\t\" -add        add data content to repository\\n\" + \" -del        delete data content from repository\\n\" +\n\t\t\t\t\" ------------\\n\" + \" -roster     check the user roster\\n\" +\n\t\t\t\t\" -aeg [true|false]  Allow empty group list for the contact\\n\" +\n\t\t\t\t\" -import file  import user data from the file of following format:\\n\" +\n\t\t\t\t\"         user_jid, password, roser_jid, roster_nick, subscription, group\\n\" +\n\t\t\t\t\" -export file  export user roster data to the specified file in the following\\n\" +\n\t\t\t\t\"              format: user_jid, password, roser_jid, roster_nick, subscription,\\n\" +\n\t\t\t\t\"               group\\n\" + \"\\n\" + \"\\n\" +\n\t\t\t\t\"Note! If you put UserAuthRepository implementation as a class name\\n\" +\n\t\t\t\t\"      some operation are not allowed and will be silently skipped.\\n\" +\n\t\t\t\t\"      Have a look at UserAuthRepository to see what operations are\\n\" +\n\t\t\t\t\"      possible or what operation does make sense.\\n\" +\n\t\t\t\t\"      Alternatively look for admin tools guide on web site.\\n\";\n\t}\n\n\tpublic static void loadTestData(UserRepository repo) throws Exception {\n\t\ttry {\n\t\t\trepo.addUser(user1);\n\t\t} catch (UserExistsException e) {\n\t\t}\n\n\t\ttry {\n\t\t\trepo.addUser(user2);\n\t\t} catch (UserExistsException e) {\n\t\t}\n\n\t\ttry {\n\t\t\trepo.addUser(user3);\n\t\t} catch (UserExistsException e) {\n\t\t}\n\n\t\trepo.setData(user1, null, \"password\", \"secret111\");\n\t\trepo.setData(user2, null, \"password\", \"secret222\");\n\t\trepo.setData(user3, null, \"password\", \"secret333\");\n\t\trepo.setData(user1, \"roster/buddy111\", \"name\", \"budy1\");\n\t\trepo.setData(user1, \"roster/buddy222\", \"name\", \"budy2\");\n\t\trepo.setData(user1, \"roster/buddy333\", \"name\", \"budy3\");\n\t\trepo.setDataList(user1, \"roster/buddy111\", \"groups\", new String[]{\"buddies\", \"friends\"});\n\t\trepo.setDataList(user2, \"roster/buddy111\", \"groups\", new String[]{\"buddies\", \"friends\"});\n\t\trepo.addDataList(user2, \"roster/buddy111\", \"groups\", new String[]{\"family\", \"home\"});\n\t}\n\n\tpublic static void main(final String[] args) throws Exception {\n\t\tparseParams(args);\n\n\t\tException repo_exc = null;\n\t\tUserRepository src_repo = null;\n\t\tAuthRepository src_auth = null;\n\n\t\ttry {\n\t\t\tsrc_repo = RepositoryFactory.getUserRepository(src_class, src_uri, null);\n\t\t\tSystem.out.println(\n\t\t\t\t\t\"Loaded src_repo \" + src_repo.getClass().getName() + \" for parameters:\" + \"\\n   src_class=\" +\n\t\t\t\t\t\t\tsrc_class + \"\\n   src_uri=\" + src_uri);\n\t\t} catch (Exception e) {\n\t\t\trepo_exc = e;\n\t\t\tsrc_repo = null;\n\t\t}    // end of try-catch\n\n\t\t// Unsuccessful UserRepository initialization\n\t\t// Let's try with AuthRepository....\n\t\tif (src_repo == null) {\n\t\t\ttry {\n\t\t\t\tsrc_auth = RepositoryFactory.getAuthRepository(src_class, src_uri, null);\n\t\t\t\tSystem.out.println(\n\t\t\t\t\t\t\"Loaded src_auth \" + src_auth.getClass().getName() + \" for parameters:\" + \"\\n   src_class=\" +\n\t\t\t\t\t\t\t\tsrc_class + \"\\n   src_uri=\" + src_uri);\n\t\t\t} catch (Exception e) {\n\t\t\t\tSystem.out.println(\"Incorrect source class name given (or connection URI).\");\n\t\t\t\tSystem.out.println(\"class: \" + src_class);\n\t\t\t\tSystem.out.println(\"uri: \" + src_uri);\n\t\t\t\tSystem.out.println(\"Can't initialize repository:\");\n\t\t\t\trepo_exc.printStackTrace();\n\t\t\t\te.printStackTrace();\n\t\t\t\tSystem.exit(-1);\n\t\t\t}    // end of try-catch\n\t\t}      // end of if (src_repo == null)\n\n\t\tif (simple_test) {\n\t\t\tSystem.out.println(\"Simple test on repository:\");\n\t\t\tsimpleTest(src_repo);\n\t\t}    // end of if (simple_test)\n\n\t\tif (add_user_test) {\n\t\t\tSystem.out.println(\"Simple test on repository:\");\n\t\t\tuserAddTest(src_repo);\n\t\t}    // end of if (simple_test)\n\n\t\tif (add) {\n\t\t\tif (key_val && (src_repo != null)) {\n\t\t\t\tSystem.out.println(\"Adding key=value: \" + content);\n\t\t\t\tparseNodeKeyValue(content);\n\t\t\t\tSystem.out.println(\n\t\t\t\t\t\t\"Parsed parameters: user=\" + user + \", node=\" + subnode + \", key=\" + key + \", value=\" + value);\n\t\t\t\tsrc_repo.setData(user, subnode, key, value);\n\t\t\t} else {\n\t\t\t\tSystem.out.println(\"Adding user: \" + user);\n\n\t\t\t\tif (src_repo != null) {\n\t\t\t\t\tsrc_repo.addUser(user);\n\t\t\t\t}\n\n\t\t\t\tif (src_auth != null) {\n\t\t\t\t\tBareJID name = user;\n\t\t\t\t\tString password = \"\";\n\n\t\t\t\t\tsrc_auth.addUser(name, password);\n\t\t\t\t}\n\t\t\t}    // end of else\n\t\t}      // end of if (add)\n\n\t\tif (del) {\n\t\t\tif (key_val || node) {\n\t\t\t\tif (key_val) {\n\t\t\t\t\tSystem.out.println(\"Deleting data: \" + content);\n\t\t\t\t\tparseNodeKeyValue(content);\n\t\t\t\t\tSystem.out.println(\n\t\t\t\t\t\t\t\"Parsed parameters: user=\" + user + \", node=\" + subnode + \", key=\" + key + \", value=\" +\n\t\t\t\t\t\t\t\t\tvalue);\n\t\t\t\t\tsrc_repo.removeData(user, subnode, key);\n\t\t\t\t}    // end of if (key_val)\n\n\t\t\t\tif (node) {\n\t\t\t\t\tSystem.out.println(\"Deleting data node: \" + content);\n\t\t\t\t\tsrc_repo.removeSubnode(user, content);\n\t\t\t\t}    // end of if (node)\n\t\t\t} else {\n\t\t\t\tSystem.out.println(\"Deleting user: \" + user);\n\n\t\t\t\tif (src_repo != null) {\n\t\t\t\t\tsrc_repo.removeUser(user);\n\t\t\t\t}\n\n\t\t\t\tif (src_auth != null) {\n\t\t\t\t\tsrc_auth.removeUser(user);\n\t\t\t\t}\n\t\t\t}      // end of else\n\t\t}        // end of if (del)\n\n\t\tif (copy_repos) {\n\t\t\tUserRepository dst_repo = null;\n\t\t\tException dst_exc = null;\n\t\t\tAuthRepository dst_auth = null;\n\n\t\t\ttry {\n\t\t\t\tdst_repo = RepositoryFactory.getUserRepository(dst_class, dst_uri, null);\n\t\t\t\tSystem.out.println(\n\t\t\t\t\t\t\"Loaded dst_repo \" + dst_repo.getClass().getName() + \" for parameters:\" + \"\\n   src_class=\" +\n\t\t\t\t\t\t\t\tdst_class + \"\\n   src_uri=\" + dst_uri);\n\t\t\t\tcopyRepositories(src_repo, dst_repo);\n\t\t\t} catch (Exception e) {\n\t\t\t\tdst_exc = e;\n\t\t\t\tdst_repo = null;\n\t\t\t}    // end of try-catch\n\n\t\t\tif (dst_repo == null) {\n\t\t\t\ttry {\n\t\t\t\t\tdst_auth = RepositoryFactory.getAuthRepository(dst_class, dst_uri, null);\n\t\t\t\t\tSystem.out.println(\"Loaded dst_auth \" + dst_auth.getClass().getName() + \" for parameters:\" +\n\t\t\t\t\t\t\t\t\t\t\t   \"\\n   src_class=\" + dst_class + \"\\n   src_uri=\" + dst_uri);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tSystem.out.println(\"Incorrect destination class name given (or connection URI).\");\n\t\t\t\t\tSystem.out.println(\"Can't initialize repository:\");\n\t\t\t\t\tdst_exc.printStackTrace();\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t\tSystem.exit(-1);\n\t\t\t\t}    // end of try-catch\n\n\t\t\t\tcopyRepositories(src_repo, dst_auth);\n\t\t\t}      // end of if (dst_repo == null)\n\t\t}        // end of if (copy_repos)\n\n\t\tif (check_roster && (src_repo != null)) {\n\t\t\tSystem.out.println(\"Checking roster:\");\n\n\t\t\tif (user != null) {\n\t\t\t\trepairUserRoster(user, src_repo);\n\t\t\t} else {\n\t\t\t\trepairRoster(src_repo);\n\t\t\t}    // end of else\n\t\t}\n\n\t\tif (import_data && (src_repo != null)) {\n\t\t\tBufferedReader br = new BufferedReader(new FileReader(import_file));\n\t\t\tString line = null;\n\n\t\t\twhile ((line = br.readLine()) != null) {\n\t\t\t\tString[] vals = line.split(\",\");\n\t\t\t\tBareJID userId = BareJID.bareJIDInstance(vals[0].trim());\n\n\t\t\t\ttry {\n\t\t\t\t\tsrc_repo.addUser(userId);\n\t\t\t\t} catch (UserExistsException e) {\n\t\t\t\t}\n\n\t\t\t\tif ((vals.length >= 2) && (vals[1].trim().length() > 0)) {\n\t\t\t\t\tsrc_repo.setData(userId, null, \"password\", vals[1].trim());\n\t\t\t\t}\n\n\t\t\t\tif ((vals.length >= 3) && (vals[2].trim().length() > 0)) {\n\t\t\t\t\tsrc_repo.setData(userId, \"roster/\" + vals[2].trim(), \"name\", vals[2].trim());\n\t\t\t\t}\n\n\t\t\t\tif ((vals.length >= 4) && (vals[3].trim().length() > 0)) {\n\t\t\t\t\tsrc_repo.setData(userId, \"roster/\" + vals[2].trim(), \"name\", vals[3].trim());\n\t\t\t\t}\n\n\t\t\t\tif ((vals.length >= 5) && (vals[4].trim().length() > 0)) {\n\t\t\t\t\tsrc_repo.setData(userId, \"roster/\" + vals[2].trim(), \"subscription\", vals[4].trim());\n\t\t\t\t}\n\n\t\t\t\tif ((vals.length >= 6) && (vals[5].trim().length() > 0)) {\n\t\t\t\t\tsrc_repo.setData(userId, \"roster/\" + vals[2].trim(), \"groups\", vals[5].trim());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tbr.close();\n\t\t}\n\n\t\tif (export_data && (src_repo != null)) {\n\t\t\tFileWriter fr = new FileWriter(export_file);\n\n\t\t\tif (user != null) {\n\t\t\t\texportUserRoster(user, src_repo, fr);\n\t\t\t} else {\n\t\t\t\texportRoster(src_repo, fr);\n\t\t\t}    // end of else\n\n\t\t\tfr.close();\n\t\t}\n\n\t\tif (print_repo && (src_repo != null)) {\n\t\t\tSystem.out.println(\"Printing repository:\");\n\n\t\t\tif (content != null) {\n\t\t\t\tif (node) {\n\t\t\t\t\tsubnode = content;\n\t\t\t\t} else {\n\t\t\t\t\tparseNodeKeyValue(content);\n\t\t\t\t}\n\t\t\t}        // end of if (content != null)\n\n\t\t\tif (user != null) {\n\t\t\t\tif (key_val) {\n\t\t\t\t\tSystem.out.println(src_repo.getData(user, subnode, key, null));\n\t\t\t\t} else {\n\t\t\t\t\tif (node) {\n\t\t\t\t\t\tprintNode(user, src_repo, \"  \", subnode);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tprintNode(user, src_repo, \"\", null);\n\t\t\t\t\t}    // end of else\n\t\t\t\t}      // end of else\n\t\t\t} else {\n\t\t\t\tprintRepoContent(src_repo);\n\t\t\t}        // end of else\n\t\t}          // end of if (print_repo)\n\t}\n\n\tprivate static void parseNodeKeyValue(String data) {\n\t\tint val_idx = data.indexOf('=');\n\n\t\tvalue = data.substring(val_idx + 1);\n\n\t\tString tmp_str = data.substring(0, val_idx);\n\t\tint key_idx = tmp_str.lastIndexOf('/');\n\n\t\tif (key_idx >= 0) {\n\t\t\tkey = tmp_str.substring(key_idx + 1);\n\t\t\tsubnode = tmp_str.substring(0, key_idx);\n\t\t}    // end of if (key_idx >= 0)\n\t\telse {\n\t\t\tkey = tmp_str;\n\t\t}    // end of if (key_idx >= 0) else\n\t}\n\n\tpublic static void parseParams(final String[] args) throws TigaseStringprepException {\n\t\tif ((args != null) && (args.length > 0)) {\n\t\t\tfor (int i = 0; i < args.length; i++) {\n\t\t\t\tif (args[i].equals(\"-h\")) {\n\t\t\t\t\tSystem.out.print(help());\n\t\t\t\t\tSystem.exit(0);\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-sc\")) {\n\t\t\t\t\tsrc_class = args[++i];\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-su\")) {\n\t\t\t\t\tsrc_uri = args[++i];\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-dc\")) {\n\t\t\t\t\tdst_class = args[++i];\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-du\")) {\n\t\t\t\t\tdst_uri = args[++i];\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-dt\")) {\n\t\t\t\t\tcontent = args[++i];\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-st\")) {\n\t\t\t\t\tsimple_test = true;\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-at\")) {\n\t\t\t\t\tadd_user_test = true;\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-cp\")) {\n\t\t\t\t\tcopy_repos = true;\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-pr\")) {\n\t\t\t\t\tprint_repo = true;\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-u\")) {\n\t\t\t\t\tuser = BareJID.bareJIDInstance(args[++i]);\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-n\")) {\n\t\t\t\t\tnode = true;\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-kv\")) {\n\t\t\t\t\tkey_val = true;\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-add\")) {\n\t\t\t\t\tadd = true;\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-del\")) {\n\t\t\t\t\tdel = true;\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-roster\")) {\n\t\t\t\t\tcheck_roster = true;\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-import\")) {\n\t\t\t\t\timport_data = true;\n\t\t\t\t\timport_file = args[++i];\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-export\")) {\n\t\t\t\t\texport_data = true;\n\t\t\t\t\texport_file = args[++i];\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\n\t\t\t\tif (args[i].equals(\"-aeg\")) {\n\t\t\t\t\tallowed_empty_groups = args[++i].equals(\"true\");\n\t\t\t\t}    // end of if (args[i].equals(\"-h\"))\n\t\t\t}      // end of for (int i = 0; i < args.length; i++)\n\t\t}\n\t}\n\n\tpublic static void printNode(BareJID user, UserRepository repo, String prefix, String node) throws Exception {\n\t\tif (node != null) {\n\t\t\tSystem.out.println(prefix + \"node: \" + node);\n\t\t}    // end of if (node != null)\n\n\t\tString[] keys = repo.getKeys(user, node);\n\n\t\tif (keys != null) {\n\t\t\tfor (String key : keys) {\n\t\t\t\tString[] vals = repo.getDataList(user, node, key);\n\n\t\t\t\tif (vals != null) {\n\t\t\t\t\tStringBuilder valstr = new StringBuilder();\n\n\t\t\t\t\tfor (String val : vals) {\n\t\t\t\t\t\tvalstr.append(\" \").append(val);\n\t\t\t\t\t}    // end of for (String val: vals)\n\n\t\t\t\t\tSystem.out.println(prefix + \"  \" + key + \" = \" + valstr);\n\t\t\t\t} else {\n\t\t\t\t\tSystem.out.println(\"    \" + key);\n\t\t\t\t}      // end of if (vals != null) else\n\t\t\t}        // end of for (String key: keys)\n\t\t}          // end of if (keys != null)\n\n\t\tString[] nodes = repo.getSubnodes(user, node);\n\n\t\tif (nodes != null) {\n\t\t\tfor (String subnode : nodes) {\n\t\t\t\tprintNode(user, repo, prefix + \"  \", ((node != null) ? node + \"/\" + subnode : subnode));\n\t\t\t}    // end of for (String node: nodes)\n\t\t}      // end of if (ndoes != null)\n\t}\n\n\tpublic static void printRepoContent(UserRepository repo) throws Exception {\n\t\tif (user != null) {\n\t\t\tprintNode(user, repo, \"  \", subnode);\n\t\t} else {\n\t\t\tList<BareJID> users = repo.getUsers();\n\n\t\t\tif (users != null) {\n\t\t\t\tfor (BareJID usr : users) {\n\t\t\t\t\tSystem.out.println(usr);\n\t\t\t\t\tprintNode(usr, repo, \"  \", subnode);\n\t\t\t\t}    // end of for (String user: users)\n\t\t\t} else {\n\t\t\t\tSystem.out.println(\"There are no user accounts in repository.\");\n\t\t\t}      // end of else\n\t\t}\n\t}\n\n\tpublic static void removeTestData(UserRepository repo) throws Exception {\n\t\trepo.removeUser(user1);\n\t\trepo.removeUser(user2);\n\t\trepo.removeUser(user3);\n\t}\n\n\tpublic static void repairRoster(UserRepository repo) throws Exception {\n\t\tif (user != null) {\n\t\t\trepairUserRoster(user, repo);\n\t\t} else {\n\t\t\tList<BareJID> users = repo.getUsers();\n\n\t\t\tif (users != null) {\n\t\t\t\tfor (BareJID usr : users) {\n\n\t\t\t\t\t// System.out.println(usr);\n\t\t\t\t\trepairUserRoster(usr, repo);\n\t\t\t\t}    // end of for (String user: users)\n\t\t\t} else {\n\t\t\t\tSystem.out.println(\"There are no user accounts in repository.\");\n\t\t\t}      // end of else\n\t\t}\n\t}\n\n\tpublic static void repairUserRoster(BareJID user, UserRepository repo) throws Exception {\n\t\tSystem.out.println(\"  \" + (++counter) + \". \" + user + \" roster: \");\n\n\t\tString[] contacts = repo.getSubnodes(user, \"roster\");\n\n\t\tif (contacts != null) {\n\t\t\tfor (String contact : contacts) {\n\t\t\t\tSystem.out.println(\"    contact: \" + contact);\n\n\t\t\t\tboolean valid = checkContact(user, repo, contact);\n\n\t\t\t\tif (valid) {\n\t\t\t\t\tSystem.out.println(\"      looks OK\");\n\t\t\t\t} else {\n\t\t\t\t\tSystem.out.println(\"      should be REMOVED\");\n\n\t\t\t\t\tString contact_node = \"roster/\" + contact;\n\n\t\t\t\t\tSystem.out.println(\"      removing node: \" + contact_node);\n\t\t\t\t\trepo.removeSubnode(user, contact_node);\n\t\t\t\t\tSystem.out.println(\"      DONE.\");\n\t\t\t\t}\n\t\t\t}    // end of for (String node: nodes)\n\t\t} else {\n\t\t\tSystem.out.println(\"    empty roster...\");\n\t\t}\n\t}\n\n\tpublic static void simpleTest(UserRepository repo) throws Exception {\n\t\tprintRepoContent(repo);\n\n\t\ttry {\n\t\t\trepo.addUser(user1);\n\t\t} catch (UserExistsException e) {\n\t\t}\n\n\t\tprintRepoContent(repo);\n\t\tremoveTestData(repo);\n\t\tprintRepoContent(repo);\n\t}\n\n\tpublic static void userAddTest(UserRepository re) throws Exception {\n\t\tAuthRepository repo = (AuthRepository) re;\n\t\tBareJID test_user = BareJID.bareJIDInstanceNS(\"test111@localhost\");\n\n\t\tprintRepoContent(re);\n\n\t\ttry {\n\t\t\trepo.addUser(test_user, \"some-pass\");\n\t\t} catch (UserExistsException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\n\t\tprintRepoContent(re);\n\t\tSystem.out.println(re.getData(test_user, \"privacy\", \"default-list\", null));\n\t\trepo.removeUser(test_user);\n\t\tprintRepoContent(re);\n\t}\n}    // RepositoryUtils\n\n"
  },
  {
    "path": "src/main/java/tigase/util/routing/PatternComparator.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.routing;\n\nimport java.util.Comparator;\nimport java.util.regex.Pattern;\n\n/**\n * Created: Oct 22, 2009 6:08:50 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class PatternComparator\n\t\timplements Comparator<Pattern> {\n\n\t@Override\n\tpublic int compare(Pattern o1, Pattern o2) {\n\t\treturn o1.pattern().compareTo(o2.pattern());\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/util/routing/RoutingsContainer.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.routing;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.xmppclient.ClientConnectionManager;\nimport tigase.util.dns.DNSResolverFactory;\n\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.regex.Pattern;\n\nimport static tigase.conf.Configurable.DEF_SM_NAME;\n\n/**\n * Describe class RoutingsContainer here.\n * <br>\n * Created: Sat Feb 11 16:30:42 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class RoutingsContainer {\n\n\tprivate static final Logger log = Logger.getLogger(\"tigase.util.RoutingsContainer\");\n\n\tprivate RoutingComputer comp = null;\n\n\tpublic RoutingsContainer(boolean multiMode) {\n\t\tif (multiMode) {\n\t\t\tcomp = new MultiMode();\n\t\t} // end of if (mode)\n\t\telse {\n\t\t\tcomp = new SingleMode();\n\t\t} // end of if (mode) else\n\t}\n\n\tpublic void addRouting(final String pattern, final String address) {\n\t\tcomp.addRouting(pattern, address);\n\t}\n\n\tpublic String computeRouting(final String pattern) {\n\t\treturn comp.computeRouting(pattern);\n\t}\n\n\tpublic interface RoutingComputer {\n\n\t\tvoid addRouting(final String pattern, final String address);\n\n\t\tString computeRouting(final String pattern);\n\n\t}\n\n\tpublic static abstract class AbstractRoutingComputer\n\t\t\timplements RoutingComputer {\n\n\t\tpublic AbstractRoutingComputer() {\n\t\t\taddRouting(\".+\", DEF_SM_NAME + \"@\" + DNSResolverFactory.getInstance().getDefaultHost());\n\t\t}\n\n\t}\n\n\tpublic static class MultiMode\n\t\t\textends AbstractRoutingComputer {\n\n\t\tprivate String def = null;\n\t\t@ConfigField(desc = \"Routing patterns\")\n\t\tprivate Map<Pattern, String> routings = new LinkedHashMap<Pattern, String>();\n\n\t\tpublic void addRouting(final String pattern, final String address) {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.fine(\"Adding routing: \" + pattern + \" --> \" + address);\n\t\t\t}\n\t\t\troutings.put(Pattern.compile(pattern), address);\n\t\t\tif (def == null) {\n\t\t\t\tdef = address;\n\t\t\t} // end of if (def == null)\n\t\t}\n\n\t\tpublic String computeRouting(final String address) {\n\t\t\tif (address == null) {\n\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\tlog.finer(\"For null address returning default routing: \" + def);\n\t\t\t\t}\n\t\t\t\treturn def;\n\t\t\t} // end of if (address == null)\n\t\t\tfor (Map.Entry<Pattern, String> entry : routings.entrySet()) {\n\t\t\t\tif (entry.getKey().matcher(address).find()) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.finest(\"For address: \" + address + \" pattern: \" + entry.getKey().pattern() + \" matched.\");\n\t\t\t\t\t}\n\t\t\t\t\treturn entry.getValue();\n\t\t\t\t} // end of if (pattern.matcher(address).find())\n\t\t\t} // end of for ()\n\t\t\treturn def;\n\t\t}\n\n\t}\n\n\t@Bean(name = \"routingComputer\", parent = ClientConnectionManager.class, active = true)\n\tpublic static class SingleMode\n\t\t\textends AbstractRoutingComputer {\n\n\t\tprivate String routing;\n\n\t\tpublic void addRouting(final String pattern, final String address) {\n\t\t\trouting = address;\n\t\t}\n\n\t\tpublic String computeRouting(final String address) {\n\t\t\treturn routing;\n\t\t}\n\n\t}\n\n} // RoutingsContainer\n"
  },
  {
    "path": "src/main/java/tigase/util/setup/BeanDefinition.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.setup;\n\nimport tigase.cluster.ClusterConnectionManager;\nimport tigase.cluster.ClusterController;\nimport tigase.disteventbus.component.EventBusComponent;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.MessageRouter;\nimport tigase.server.ServerComponent;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.stats.StatisticsCollector;\nimport tigase.vhosts.VHostManager;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * Created by andrzej on 30.03.2017.\n */\npublic class BeanDefinition {\n\n\tpublic static final List<Class<? extends ServerComponent>> CORE_COMPONENTS = Arrays.asList(SessionManager.class,\n\t                                                                                           VHostManager.class,\n\t                                                                                           MessageRouter.class,\n\t                                                                                           ClusterConnectionManager.class,\n\t                                                                                           ClusterController.class,\n\t                                                                                           EventBusComponent.class,\n\t                                                                                           EventBusComponent.class,\n\t                                                                                           StatisticsCollector.class);\n\tprivate final boolean active;\n\tprivate final Class<?> clazz;\n\tprivate final String name;\n\n\tpublic BeanDefinition(Class<?> cls) {\n\t\tBean bean = cls.getAnnotation(Bean.class);\n\t\tname = bean.name();\n\t\tclazz = cls;\n\t\tactive = bean.active();\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic Class<?> getClazz() {\n\t\treturn clazz;\n\t}\n\n\tpublic boolean isActive() {\n\t\treturn active;\n\t}\n\n\tpublic boolean isCoreComponent() {\n\t\treturn CORE_COMPONENTS.contains(clazz) ||\n\t\t\t\tCORE_COMPONENTS.stream().filter(cmp -> cmp.isAssignableFrom(clazz)).findAny().isPresent();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/util/setup/SetupHelper.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.setup;\n\nimport tigase.conf.ConfigBuilder;\nimport tigase.kernel.beans.config.AbstractBeanConfigurator;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.beans.selector.ServerBeanSelector;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.ServerComponent;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xmpp.XMPPImplIfc;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\n/**\n * Created by andrzej on 30.03.2017.\n */\npublic class SetupHelper {\n\n\tpublic static enum RestApiSecurity {\n\t\tforbidden,\n\t\tapi_keys,\n\t\topen_access\n\t}\n\n\tpublic static BeanDefinition convertToBeanDefinition(Class<?> cls) {\n\t\treturn new BeanDefinition(cls);\n\t}\n\n\tpublic static ConfigBuilder generateConfig(ConfigTypeEnum configType, String dbUri, boolean clusterMode,\n\t\t\t\t\t\t\t\t\t\t\t   boolean acs, Optional<Set<String>> optionalComponentsOption,\n\t\t\t\t\t\t\t\t\t\t\t   Optional<Set<String>> forceEnabledComponentsOptions,\n\t\t\t\t\t\t\t\t\t\t\t   Optional<Set<String>> pluginsOption, String defaultVirtualHost,\n\t\t\t\t\t\t\t\t\t\t\t   Optional<BareJID[]> admins, Optional<HttpSecurity> httpSecurity) {\n\t\tConfigBuilder builder = new ConfigBuilder().with(\"config-type\", configType.id().toLowerCase());\n\n\t\tif (clusterMode) {\n\t\t\tbuilder.with(\"cluster-mode\", \"true\");\n\t\t}\n\t\tbuilder.with(\"default-virtual-host\", defaultVirtualHost)\n\t\t\t\t.with(\"admins\", admins)\n\t\t\t\t.with(\"debug\", Arrays.asList(\"server\"));\n\n\t\tbuilder.withBean(ds -> ds.name(\"dataSource\").withBean(def -> def.name(\"default\").with(\"uri\", dbUri)));\n\n\t\tList<AbstractBeanConfigurator.BeanDefinition> sessManSubBeans = new ArrayList<>();\n\t\tif (pluginsOption.isPresent()) {\n\t\t\tSet<String> plugins = pluginsOption.get();\n\t\t\tsessManSubBeans.addAll(SetupHelper.getAvailableProcessors(SessionManager.class, XMPPImplIfc.class)\n\t\t\t\t\t\t\t\t\t\t   .stream()\n\t\t\t\t\t\t\t\t\t\t   .filter(def -> (def.isActive() && !plugins.contains(def.getName())) ||\n\t\t\t\t\t\t\t\t\t\t\t\t   ((!def.isActive()) && plugins.contains(def.getName())))\n\t\t\t\t\t\t\t\t\t\t   .map(def -> new AbstractBeanConfigurator.BeanDefinition.Builder().name(\n\t\t\t\t\t\t\t\t\t\t\t\t   def.getName()).active(plugins.contains(def.getName())).build())\n\t\t\t\t\t\t\t\t\t\t   .collect(Collectors.toList()));\n\t\t}\n\t\tif (acs) {\n\t\t\tsessManSubBeans.add(new AbstractBeanConfigurator.BeanDefinition.Builder().name(\"strategy\")\n\t\t\t\t\t\t\t\t\t\t.clazz(\"tigase.server.cluster.strategy.OnlineUsersCachingStrategy\")\n\t\t\t\t\t\t\t\t\t\t.build());\n\t\t}\n\t\tif (!sessManSubBeans.isEmpty()) {\n\t\t\tbuilder.withBean(sessMan -> sessMan.name(\"sess-man\")\n\t\t\t\t\t.with(sessManSubBeans.stream().toArray(x -> new AbstractBeanConfigurator.BeanDefinition[x])));\n\t\t}\n\n\t\tSet<String> optionalComponents = optionalComponentsOption.orElse(SetupHelper.getAvailableComponents()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t .stream()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t .filter(def -> !def.isCoreComponent())\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t .map(def -> def.getName())\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t .collect(Collectors.toSet()));\n\t\tSetupHelper.getAvailableComponents().stream().filter(def -> !def.isCoreComponent()).filter(def -> {\n\t\t\tConfigType ct = def.getClazz().getAnnotation(ConfigType.class);\n\n\t\t\tif (optionalComponents.contains(def.getName())) {\n\t\t\t\treturn def.isActive() == false || (ct != null && !Arrays.asList(ct.value()).contains(configType)) ||\n\t\t\t\t\t\t(forceEnabledComponentsOptions.isPresent() &&\n\t\t\t\t\t\t\t\tforceEnabledComponentsOptions.get().contains(def.getName()))\n\t\t\t\t\t\t|| (\"http\".equals(def.getName()) && httpSecurity.isPresent())\n\t\t\t\t\t\t|| (\"pubsub\".equals(def.getName()) && optionalComponents.contains(\"http\"));\n\t\t\t} else {\n\t\t\t\treturn def.isActive() == true && (ct != null && Arrays.asList(ct.value()).contains(configType));\n\t\t\t}\n\t\t}).forEach(def -> {\n\t\t\tConfigType ct = def.getClazz().getAnnotation(ConfigType.class);\n\n\t\t\tbuilder.withBean(b -> {\n\t\t\t\tb.name(def.getName())\n\t\t\t\t\t\t.active(optionalComponents.contains(def.getName()))\n\t\t\t\t\t\t.clazz((ct != null && !Arrays.asList(ct.value()).contains(configType)) ? def.getClazz() : null);\n\t\t\t\tif (\"http\".equals(def.getName())) {\n\t\t\t\t\thttpSecurity.ifPresent(sec -> {\n\t\t\t\t\t\tif (sec.setupUser != null && !sec.setupUser.isEmpty() && sec.setupPassword != null &&\n\t\t\t\t\t\t\t\t!sec.setupPassword.isEmpty()) {\n\t\t\t\t\t\t\tb.withBean(setup -> setup.name(\"setup\")\n\t\t\t\t\t\t\t\t\t.with(\"admin-user\", sec.setupUser)\n\t\t\t\t\t\t\t\t\t.with(\"admin-password\", sec.setupPassword));\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (\"pubsub\".equals(def.getName()) && optionalComponents.contains(\"http\")) {\n\t\t\t\t\tb.with(\"trusted\", Arrays.asList(\"http@{clusterNode}\"));\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\n\t\treturn builder;\n\t}\n\n\tpublic static List<BeanDefinition> getAvailableBeans(Class processorClazz) {\n\t\treturn getAvailableBeans(processorClazz, Kernel.class);\n\t}\n\n\tpublic static List<BeanDefinition> getAvailableBeans(Class processorClazz, Class componentClazz) {\n\t\tKernel kernel = new Kernel(\"SetupHelper\");\n\t\tkernel.registerBean(\"beanSelector\").asInstance(new ServerBeanSelector()).exportable().exec();\n\t\treturn AbstractBeanConfigurator.getBeanClassesFromAnnotations(kernel, componentClazz)\n\t\t\t\t.entrySet()\n\t\t\t\t.stream()\n\t\t\t\t.filter(e -> processorClazz.isAssignableFrom(e.getValue()))\n\t\t\t\t.map(e -> e.getValue())\n\t\t\t\t.map(SetupHelper::convertToBeanDefinition)\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\tpublic static List<BeanDefinition> getAvailableComponents() {\n\t\treturn getAvailableBeans(ServerComponent.class);\n\t}\n\n\tpublic static List<BeanDefinition> getAvailableProcessors(Class componentClazz, Class processorClazz) {\n\t\treturn getAvailableBeans(processorClazz, componentClazz);\n\t}\n\n\tpublic static class HttpSecurity {\n\n\t\tpublic String setupPassword;\n\t\tpublic String setupUser;\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/util/tracer/TigaseTracer.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.tracer;\n\nimport tigase.server.Packet;\n\nimport java.io.File;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.Writer;\nimport java.util.Calendar;\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.ConcurrentSkipListSet;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Jun 30, 2009 5:18:10 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class TigaseTracer {\n\n\tpublic static final String TRACER_IPS_PROP_KEY = \"--tracer-ips\";\n\tpublic static final String TRACER_JIDS_PROP_KEY = \"--tracer-jids\";\n\tpublic static final String TRACER_CIDS_PROP_KEY = \"--tracer-cids\";\n\tprivate static final Logger log = Logger.getLogger(TigaseTracer.class.getName());\n\tprivate static final String DEF_DIR = \"logs\";\n\tprivate static final long DEF_MAX_FILE_SIZE = 10000000;\n\tprivate static final int DEF_FILES_COUNT = 5;\n\tprivate static final String DEF_FILE_NAME = \"packet-tracing.log\";\n\tprivate static ConcurrentSkipListSet<String> cids = new ConcurrentSkipListSet<String>();\n\tprivate static TigaseTracer instance = null;\n\tprivate static ConcurrentSkipListSet<String> ips = new ConcurrentSkipListSet<String>();\n\tprivate static ConcurrentSkipListSet<String> jids = new ConcurrentSkipListSet<String>();\n\tprivate String dir = DEF_DIR;\n\tprivate String fileName = DEF_FILE_NAME;\n\tprivate File files[] = null;\n\tprivate int filesCount = DEF_FILES_COUNT;\n\tprivate long maxFileSize = DEF_MAX_FILE_SIZE;\n\tprivate ArrayBlockingQueue<String> waiting = new ArrayBlockingQueue<String>(10000, true);\n\tprivate Runnable worker = null;\n\n\tpublic static void addCid(String cid) {\n\t\tcids.add(cid);\n\t}\n\n\tpublic static void addIP(String ip) {\n\t\tips.add(ip);\n\t}\n\n\tpublic static void addJid(String jid) {\n\t\tjids.add(jid);\n\t}\n\n\tpublic static TigaseTracer getTracer(String name) {\n\t\tif (instance == null) {\n\t\t\ttry {\n\t\t\t\tinstance = new TigaseTracer();\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.WARNING, \"Error initializing Tigase tracer: \", e);\n\t\t\t}\n\t\t}\n\t\treturn instance;\n\t}\n\n\tpublic static void removeCid(String cid) {\n\t\tcids.remove(cid);\n\t}\n\n\tpublic static void removeIP(String ip) {\n\t\tips.remove(ip);\n\t}\n\n\tpublic static void removeJid(String jid) {\n\t\tjids.remove(jid);\n\t}\n\n\tprivate TigaseTracer() throws IOException {\n\t\tinit();\n\t}\n\n\tpublic boolean trace(String ip, String to_jid, String from_jid, String cid, String id, String point, String msg,\n\t\t\t\t\t\t Packet packet) {\n\t\tif (ip != null && ips.contains(ip)) {\n\t\t\treturn waiting.offer(format(ip, id, point, msg, packet));\n\t\t}\n\t\tif (to_jid != null && jids.contains(to_jid)) {\n\t\t\treturn waiting.offer(format(\"TO: \" + to_jid, id, point, msg, packet));\n\t\t}\n\t\tif (from_jid != null && jids.contains(from_jid)) {\n\t\t\treturn waiting.offer(format(\"FROM: \" + from_jid, id, point, msg, packet));\n\t\t}\n\t\tif (cid != null && cids.contains(cid)) {\n\t\t\treturn waiting.offer(format(cid, id, point, msg, packet));\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic boolean tracegByIP(String ip, String point, String msg, Packet packet) {\n\t\treturn trace(ip, null, null, null, null, point, msg, packet);\n\t}\n\n\tpublic boolean traceByToJid(String jid, String point, String msg, Packet packet) {\n\t\treturn trace(null, jid, null, null, null, point, msg, packet);\n\t}\n\n\tpublic boolean traceByFromJid(String jid, String point, String msg, Packet packet) {\n\t\treturn trace(null, null, jid, null, null, point, msg, packet);\n\t}\n\n\tpublic boolean traceByCid(String cid, String point, String msg, Packet packet) {\n\t\treturn trace(null, null, null, cid, null, point, msg, packet);\n\t}\n\n\tprivate void init() throws IOException {\n\t\tfiles = new File[filesCount];\n\t\tfor (int i = 0; i < files.length; i++) {\n\t\t\tfiles[i] = new File(dir, fileName + \".\" + i);\n\t\t}\n\t\tworker = new TracingWorker(new FileWriter(files[0], true), files[0].length());\n\t\tThread thr = new Thread(worker);\n\t\tthr.setName(\"tracing-worker\");\n\t\tthr.setDaemon(true);\n\t\tthr.start();\n\t}\n\n\tprivate String format(String filter, String id, String point, String msg, Packet packet) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tCalendar cal = Calendar.getInstance();\n\t\tsb.append(cal.get(Calendar.YEAR));\n\t\tsb.append(\"-\");\n\t\tsb.append(cal.get(Calendar.MONTH) + 1);\n\t\tsb.append(\"-\");\n\t\tsb.append(cal.get(Calendar.DAY_OF_MONTH));\n\t\tsb.append(\" \");\n\t\tsb.append(cal.get(Calendar.HOUR_OF_DAY));\n\t\tsb.append(\":\");\n\t\tsb.append(cal.get(Calendar.MINUTE));\n\t\tsb.append(\":\");\n\t\tsb.append(cal.get(Calendar.SECOND));\n\t\tsb.append(\".\");\n\t\tsb.append(cal.get(Calendar.MILLISECOND));\n\t\tsb.append(\"[\");\n\t\tsb.append(filter);\n\t\tsb.append(\"] {\");\n\t\tsb.append(id);\n\t\tsb.append('-');\n\t\tsb.append(point);\n\t\tsb.append(\"} \");\n\t\tif (msg != null) {\n\t\t\tsb.append(msg);\n\t\t}\n\t\tif (packet != null) {\n\t\t\tsb.append(packet);\n\t\t}\n\t\tsb.append('\\n');\n\t\treturn sb.toString();\n\t}\n\n\tprivate Writer rotateFiles(Writer writer) throws IOException {\n\t\twriter.close();\n\t\tfor (int i = files.length - 2; i >= 0; --i) {\n\t\t\tFile file1 = files[i];\n\t\t\tFile file2 = files[i + 1];\n\t\t\tif (file1.exists()) {\n\t\t\t\tif (file2.exists()) {\n\t\t\t\t\tfile2.delete();\n\t\t\t\t}\n\t\t\t\tfile1.renameTo(file2);\n\t\t\t}\n\t\t}\n\t\treturn new FileWriter(files[0], false);\n\t}\n\n\tprivate class TracingWorker\n\t\t\timplements Runnable {\n\n\t\tprivate long size = 0;\n\t\tprivate boolean stopped = false;\n\t\tprivate Writer writer = null;\n\n\t\tprotected TracingWorker(Writer writer, long initSize) {\n\t\t\tthis.writer = writer;\n\t\t\tthis.size = initSize;\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\twhile (!stopped) {\n\t\t\t\ttry {\n\t\t\t\t\tString entry = waiting.take();\n\t\t\t\t\tif (entry != null) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t// Save the log entry\n\t\t\t\t\t\t\twriter.write(entry);\n\t\t\t\t\t\t\twriter.flush();\n\t\t\t\t\t\t\tsize += entry.length();\n\t\t\t\t\t\t\tif (size >= maxFileSize) {\n\t\t\t\t\t\t\t\twriter = rotateFiles(writer);\n\t\t\t\t\t\t\t\tsize = 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (IOException ex) {\n\t\t\t\t\t\t\tlog.log(Level.WARNING, \"Can not write to trace file: \", ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (InterruptedException ex) {\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/util/updater/UpdatesChecker.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.updater;\n\nimport tigase.component.ScheduledTask;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.EventBusEvent;\nimport tigase.eventbus.HandleEvent;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.MessageRouter;\nimport tigase.server.Packet;\nimport tigase.server.XMPPServer;\nimport tigase.util.Version;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.net.HttpURLConnection;\nimport java.net.URL;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.concurrent.ConcurrentSkipListSet;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\n/**\n * Describe class UpdatesChecker here.\n * <br>\n * Created: Fri Apr 18 09:35:32 2008\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\n@Bean(name = \"update-checker\", parent = MessageRouter.class, active = true)\npublic class UpdatesChecker\n\t\textends ScheduledTask {\n\n\tpublic static final String VERSION_REQUEST_KEY = \"tigase-server-version\";\n\tpublic static final String PRODUCTS_REQUEST_KEY = \"products\";\n\n\tprivate static final Logger log = Logger.getLogger(UpdatesChecker.class.getName());\n\tstatic final String VERSION_URL = \"http://update.tigase.net/check/\";\n\n\tprivate final Version serverVersion;\n\n\t@Inject\n\tprivate EventBus eventBus;\n\tprivate Version latestCheckedVersion = null;\n\t@ConfigField(desc = \"Enables sending XMPP notifications about new version\")\n\tprivate Boolean notificationsEnabled = true;\n\t@Inject(nullAllowed = true)\n\tprivate ArrayList<ProductInfoIfc> productInfos = new ArrayList<>();\n\t@ConfigField(desc = \"List of receivers JIDs\", alias = \"admins\")\n\tprivate ConcurrentSkipListSet<BareJID> receivers = new ConcurrentSkipListSet<BareJID>();\n\n\tprivate static Version getVersion(String version) {\n\t\ttry {\n\t\t\treturn Version.of(version);\n\t\t} catch (IllegalArgumentException e) {\n\t\t\tlog.log(Level.FINE, \"Error parsing version from server\");\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic static Optional<Version> retrieveCurrentVersionFromServer(Version currentVersion,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t List<ProductInfoIfc> products, String url,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t int timeoutInSeconds) {\n\n\t\tObjects.nonNull(currentVersion);\n\t\tObjects.nonNull(url);\n\n\t\tlog.log(Level.FINEST, \"Retrieving latest version information for localVersion: {0}, products: {1}\",\n\t\t\t\tnew String[]{String.valueOf(currentVersion), String.valueOf(products)});\n\t\ttry {\n\t\t\tfinal URL u = new URL(url);\n\t\t\tHttpURLConnection connection = (HttpURLConnection) u.openConnection();\n\n\t\t\tconnection.setConnectTimeout((int) TimeUnit.SECONDS.toMillis(timeoutInSeconds));\n\t\t\tconnection.setReadTimeout((int) TimeUnit.SECONDS.toMillis(timeoutInSeconds));\n\n\t\t\tconnection.setRequestProperty(VERSION_REQUEST_KEY, currentVersion.toString());\n\n\t\t\tif (products != null && !products.isEmpty()) {\n\t\t\t\tString requestProducts = products.stream()\n\t\t\t\t\t\t.filter(productInfoIfc -> productInfoIfc.getProductVersion().isPresent())\n\t\t\t\t\t\t.map(pi -> String.join(\":\", pi.getProductId(), pi.getProductVersion().get()))\n\t\t\t\t\t\t.collect(Collectors.joining(\";\"));\n\t\t\t\tconnection.setRequestProperty(PRODUCTS_REQUEST_KEY, requestProducts);\n\t\t\t}\n\n\t\t\tBufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));\n\n\t\t\treturn br.lines()\n\t\t\t\t\t.map(UpdatesChecker::getVersion)\n\t\t\t\t\t.filter(Objects::nonNull)\n//\t\t\t\t\t.peek(System.out::println)\n\t\t\t\t\t.filter(e -> e.compareTo(currentVersion) > 0)\n\t\t\t\t\t.findFirst();\n\n\t\t} catch (IOException e) {\n\t\t\tlog.log(Level.WARNING, \"Can not check updates for URL: \" + url, e);\n\t\t}\n\n\t\treturn Optional.empty();\n\t}\n\n\tpublic UpdatesChecker() {\n\t\tsuper(Duration.ofDays(7), Duration.ofDays(7));\n\t\tserverVersion = XMPPServer.getVersion();\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tsuper.initialize();\n\t\tif (eventBus != null) {\n\t\t\teventBus.registerAll(this);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\tfinal Optional<Version> version;\n\t\tversion = retrieveCurrentVersionFromServer(serverVersion, productInfos, VERSION_URL, 60);\n\t\tversion.ifPresent(this::sendNewVersionNotification);\n\t}\n\n\tpublic void setProductInfos(ArrayList<ProductInfoIfc> productInfos) {\n\t\tif (productInfos == null) {\n\t\t\tthis.productInfos = new ArrayList<>();\n\t\t} else {\n\t\t\tthis.productInfos = productInfos;\n\t\t}\n\t}\n\n\t@HandleEvent\n\tprotected void onUpdatedVersionDiscovered(UpdatesChecker.UpdatedVersionDiscovered event) {\n\t\tif ((event.getVersion() != null) && isNewerVersion(event.getVersion())) {\n\t\t\tlog.log(Level.CONFIG, \"New version updated from cluster\");\n\t\t}\n\t}\n\n\tprivate void sendNewVersionNotification(Version v) {\n\t\tlog.log(Level.CONFIG, \"Update available: \" + v + \" (current version: \" + serverVersion + \")\");\n\n\t\tif (notificationsEnabled && isNewerVersion(v)) {\n\t\t\treceivers.stream()\n\t\t\t\t\t.filter(addr -> !addr.toString().contains(\"{clusternode}\"))\n\t\t\t\t\t.filter(addr -> !component.getNodesConnectedWithLocal()\n\t\t\t\t\t\t\t.contains(JID.jidInstanceNS(addr.getDomain())))\n\t\t\t\t\t.map(admin -> prepareMessage(v, admin))\n\t\t\t\t\t.forEach(p -> component.addPacket(p));\n\t\t}\n\n\t\tfire(new UpdatedVersionDiscovered(v));\n\t}\n\n\tprivate void fire(Object event) {\n\t\tif (eventBus != null) {\n\t\t\teventBus.fire(event);\n\t\t}\n\t}\n\n\tprivate boolean isNewerVersion(Version ver) {\n\t\tif (latestCheckedVersion == null || ver.compareTo(latestCheckedVersion) > 0) {\n\t\t\tlatestCheckedVersion = ver;\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate Packet prepareMessage(Version v, BareJID jid) {\n\t\tString link = \"http://tigase.net/downloads\";\n\t\tfinal JID sender = JID.jidInstanceNS(\"updates.checker@\" + jid.getDomain());\n\t\tElement message = new Element(\"message\");\n\t\tElement subject = new Element(\"subject\", \"Updates checker - new version of the Tigase server\");\n\t\tmessage.addChild(subject);\n\t\tmessage.setXMLNS(\"jabber:client\");\n\t\tmessage.setAttribute(\"type\", \"normal\");\n\n\t\tElement body = new Element(\"body\", \"You are currently using: '\" + serverVersion + \"' version of Tigase\" +\n\t\t\t\t\" server. A new version of the server has been released: '\" + v +\n\t\t\t\t\"' and it is available for download at address: \" + link + \"\\n\\n\" +\n\t\t\t\t\"This is automated message generated by updates checking module (you can adjust it's configuration).\" +\n\t\t\t\t\"\\n\\n\" + \"You can adjust frequency of the check by setting 'delay' and 'period' \"\n\n\t\t);\n\t\tmessage.addChild(body);\n\t\treturn Packet.packetInstance(message, sender, JID.jidInstance(jid));\n\t}\n\n\tpublic interface ProductInfoIfc {\n\n\t\t/**\n\t\t * Product identifier\n\t\t */\n\t\tString getProductId();\n\n\t\t/**\n\t\t * Human readable product name\n\t\t */\n\t\tString getProductName();\n\n\t\t/**\n\t\t * Version of the product\n\t\t */\n\t\tdefault Optional<String> getProductVersion() {\n\t\t\treturn Optional.ofNullable(this.getClass().getPackage().getImplementationVersion());\n\t\t}\n\t}\n\n\tpublic static class UpdatedVersionDiscovered implements EventBusEvent {\n\n\t\tprivate final Version version;\n\n\t\tUpdatedVersionDiscovered(Version version) {\n\t\t\tthis.version = version;\n\t\t}\n\n\t\tpublic Version getVersion() {\n\t\t\treturn version;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/main/java/tigase/util/workqueue/NonpriorityQueue.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.workqueue;\n\nimport java.util.concurrent.LinkedBlockingQueue;\n\n/**\n * Created: Feb 9, 2010 11:32:30 AM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\npublic class NonpriorityQueue<E>\n\t\textends PriorityQueueAbstract<E> {\n\n\tprivate LinkedBlockingQueue<E> queue = null;\n\n\n\tpublic NonpriorityQueue() {\n\t}\n\n\tprotected NonpriorityQueue(int maxSize) {\n\t\tinit(0, maxSize);\n\t}\n\n\t@Override\n\tpublic final void init(int maxPriority, int maxSize) {\n\t\tqueue = new LinkedBlockingQueue<E>(maxSize);\n\t}\n\n\t@Override\n\tpublic boolean offer(E element, int priority) {\n\t\treturn queue.offer(element);\n\t}\n\n\t@Override\n\tpublic void put(E element, int priority) throws InterruptedException {\n\t\tqueue.put(element);\n\t}\n\n\t@Override\n\tpublic void setMaxSize(int maxSize) {\n\n//  TODO:\n//  The code below causes a dead-lock as the take() method waits on the\n//  old queue without any chance to be waken up. Up to now I haven't\n//   found a way to awake the thread waiting on the take() method.\n//     LinkedBlockingQueue<E> oldQueue = queue;\n//     int newSize = Math.max(oldQueue.size(), maxSize);\n//\n//     queue = new LinkedBlockingQueue<E>(newSize);\n//     oldQueue.drainTo(queue);\n\t}\n\n\t@Override\n\tpublic int[] size() {\n\t\tint[] result = new int[1];\n\n\t\tresult[0] = queue.size();\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic E take() throws InterruptedException {\n\t\treturn queue.take();\n\t}\n\n\t@Override\n\tpublic int totalSize() {\n\t\treturn queue.size();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/util/workqueue/PriorityQueueAbstract.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.workqueue;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.sys.TigaseRuntime;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Works like a LinkedBlockingQueue using the put() and take() methods but with an additional priority integer\n * parameter. The elemnt returned from take() will honor the priority in such a way that all elements of a lower\n * priority will be returned before any elemens of a higher priority.\n * <br>\n * Modified proposition taken from Noa Resare: http://resare.com/noa/ref/MultiPrioQueue.java\n *\n* @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic abstract class PriorityQueueAbstract<E> {\n\n\tpublic static final String NONPRIORITY_QUEUE = \"nonpriority-queue\";\n\tpublic static final String QUEUE_IMPLEMENTATION = \"queue-implementation\";\n\tprivate static final Logger log = Logger.getLogger(PriorityQueueAbstract.class.getName());\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic static <E> PriorityQueueAbstract<E> getPriorityQueue(int maxPriority, int maxSize) {\n\t\tClass<? extends PriorityQueueAbstract> result = null;\n\t\tString queue_class = System.getProperty(QUEUE_IMPLEMENTATION, null);\n\n\t\tif ((queue_class == null) || queue_class.isEmpty()) {\n\t\t\tif (Boolean.getBoolean(NONPRIORITY_QUEUE)) {\n\t\t\t\tresult = NonpriorityQueue.class;\n\t\t\t} else {\n\t\t\t\tresult = PriorityQueueRelaxed.class;\n\t\t\t}\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tresult = (Class<? extends PriorityQueueAbstract>) Class.forName(queue_class);\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.CONFIG, \"Error crating priority queue\", e);\n\t\t\t\tTigaseRuntime.getTigaseRuntime()\n\t\t\t\t\t\t.shutdownTigase(new String[]{\n\t\t\t\t\t\t\t\t\"Error: Could not instantiate or initialize priority queue of class: \" + queue_class,\n\t\t\t\t\t\t\t\t\"Got exception: \" + e.getMessage()});\n\t\t\t}\n\t\t}\n\n\t\treturn getPriorityQueue(maxPriority, maxSize, result);\n\t}\n\n\tpublic static <E> PriorityQueueAbstract<E> getPriorityQueue(int maxPriority, int maxSize,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tClass<? extends PriorityQueueAbstract> queueClass) {\n\t\ttry {\n\t\t\tPriorityQueueAbstract<E> result = queueClass.newInstance();\n\t\t\tresult.init(maxPriority, maxSize);\n\t\t\tlog.log(Level.FINER, \"Initialized queue implementation: \" + result.getClass().getName());\n\t\t\treturn result;\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.CONFIG, \"Error crating priority queue\", e);\n\t\t\tTigaseRuntime.getTigaseRuntime()\n\t\t\t\t\t.shutdownTigase(new String[]{\n\t\t\t\t\t\t\t\"Error: Could not instantiate or initialize priority queue of class: \" + queueClass,\n\t\t\t\t\t\t\t\"Got exception: \" + e.getMessage()});\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic abstract void init(int maxPriority, int maxSize);\n\n\tpublic abstract boolean offer(E element, int priority);\n\n\tpublic abstract void put(E element, int priority) throws InterruptedException;\n\n\tpublic abstract void setMaxSize(int maxSize);\n\n\tpublic abstract int[] size();\n\n\tpublic abstract E take() throws InterruptedException;\n\n\tpublic abstract int totalSize();\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/util/workqueue/PriorityQueueRelaxed.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.workqueue;\n\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.logging.Logger;\n\n/**\n * Created: Feb 9, 2010 11:12:56 AM\n *\n* @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class PriorityQueueRelaxed<E>\n\t\textends PriorityQueueAbstract<E> {\n\n\tprivate static final Logger log = Logger.getLogger(PriorityQueueRelaxed.class.getName());\n\n\tprivate int lowestNonEmpty = Integer.MAX_VALUE;\n\tprivate LinkedBlockingQueue<E>[] qs = null;\n\n\n\tpublic PriorityQueueRelaxed() {\n\t}\n\n\t@SuppressWarnings({\"unchecked\"})\n\tprotected PriorityQueueRelaxed(int maxPriority, int maxSize) {\n\t\tinit(maxPriority, maxSize);\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic final void init(int maxPriority, int maxSize) {\n\n//  qs = new LinkedBlockingQueue[maxPriority + 1];\n\t\tqs = new LinkedBlockingQueue[maxPriority];\n\n\t\tfor (int i = 0; i < qs.length; i++) {\n\t\t\tqs[i] = new LinkedBlockingQueue<E>(maxSize);\n\t\t}\n\n\t\t// System.out.println(\"PriorityQueueRelaxed Initialized: \" + maxSize);\n\t}\n\n\t@Override\n\tpublic boolean offer(E element, int priority) {\n\t\ttry {\n\n\t\t\t// return add(element, priority, false, owner);\n\t\t\treturn add(element, priority, false);\n\t\t} catch (InterruptedException e) {\n\t\t\tlog.warning(\"This should not happen, this is non-blocking operation.\");\n\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// public void put(E element, int priority, String owner) throws InterruptedException {\n\n\t@Override\n\tpublic void put(E element, int priority) throws InterruptedException {\n\n\t\t// add(element, priority, true, owner);\n\t\tadd(element, priority, true);\n\t}\n\n\t@Override\n\tpublic void setMaxSize(int maxSize) {\n\t\tfor (int i = 0; i < qs.length; i++) {\n\n\t\t\t// We don't want to lose any data so the new size must\n\t\t\t// be enough to keep all exising elements\n\t\t\tLinkedBlockingQueue<E> oldQueue = qs[i];\n\t\t\tint newSize = Math.max(oldQueue.size(), maxSize);\n\n\t\t\tqs[i] = new LinkedBlockingQueue<E>(newSize);\n\t\t\toldQueue.drainTo(qs[i]);\n\t\t}\n\t}\n\n\t@Override\n\tpublic int[] size() {\n\t\tint[] result = new int[qs.length];\n\n\t\tfor (int i = 0; i < result.length; i++) {\n\t\t\tresult[i] = qs[i].size();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t// public E take(String owner) throws InterruptedException {\n\n\t@Override\n\tpublic E take() throws InterruptedException {\n\t\tE e = null;\n\n\t\twhile (e == null) {\n\t\t\tsynchronized (this) {\n\t\t\t\ttry {\n\n\t\t\t\t\t// Safeguard for \"java wait spurious wakeup\", google for it\n\t\t\t\t\t// if you don't know it. I have just learned about it too...\n\t\t\t\t\twhile (lowestNonEmpty == Integer.MAX_VALUE) {\n\n//          log.finest(\"[\" + owner + \"] waiting...\");\n\t\t\t\t\t\tthis.wait();\n\t\t\t\t\t}\n\t\t\t\t} catch (InterruptedException ex) {\n\t\t\t\t}\n\n\t\t\t\tLinkedBlockingQueue<E> q = qs[lowestNonEmpty];\n\n\t\t\t\t// log.log(Level.FINEST, \"{0} taking from queue: \", lowestNonEmpty);\n\t\t\t\te = q.poll();\n\n//      if (e != null) {\n//        log.log(Level.FINEST, \"{0} element read: {1}\", new Object[] { lowestNonEmpty, e });\n//      } else {\n//        log.log(Level.FINEST, \"{0} NULL element read!\", lowestNonEmpty);\n//      }\n\t\t\t\tif ((e == null) || q.isEmpty()) {\n\t\t\t\t\tlowestNonEmpty = findNextNonEmpty();\n\n//        log.finest(\"[\" + owner + \"] \" + \"new lowestNonEmpty: \" + lowestNonEmpty);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn e;\n\t}\n\n\t@Override\n\tpublic int totalSize() {\n\t\tint result = 0;\n\n\t\tfor (int i = 0; i < qs.length; i++) {\n\t\t\tresult += qs[i].size();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t// TODO: Reduce synchronization but be carefull many threads are writing\n\t// to queues at the same time.\n\t// private boolean add(E element, int priority, boolean blocking, String owner)\n\tprivate boolean add(E element, int priority, boolean blocking) throws InterruptedException {\n\t\tif ((priority < 0) || (qs.length <= priority)) {\n\t\t\tthrow new IllegalArgumentException(\"parameter priority must be \" + \"between 0 and \" + (qs.length - 1));\n\t\t}\n\n\t\tboolean result = true;\n\t\tLinkedBlockingQueue<E> q = qs[priority];\n\n\t\tif (blocking) {\n\n//    log.log(Level.FINEST, \"{0} B before element add: {1}\",\n//        new Object[] { priority, element });\n\t\t\tq.put(element);\n\n\t\t\t// log.finest(\"[\" + owner + \"] \" + priority + \" B element added: \" + element.toString());\n//    log.log(Level.FINEST, \"{0} B element added: {1}\", new Object[] { priority, element });\n\t\t} else {\n\t\t\tresult = q.offer(element);\n\n//    log.log(Level.FINEST, \"{0} NB element added: {1}\", new Object[] { priority, element });\n//    log.finest(\"[\" + owner + \"] \" + priority + \" NB element added: \" +\n//            element.toString() + \", result: \" + result +\n//            \", lowestNonEmpty: \" + lowestNonEmpty + \", size: \" + q.size());\n\t\t}\n\n\t\tif (result) {\n\t\t\tsynchronized (this) {\n\t\t\t\tif (priority < lowestNonEmpty) {\n\n//        log.finest(\"[\" + owner + \"] setting new priority from \" +\n//                lowestNonEmpty + \", to: \" + priority);\n\t\t\t\t\tlowestNonEmpty = priority;\n\t\t\t\t}\n\n\t\t\t\tthis.notify();\n\t\t\t}\n\t\t} else {\n\t\t\tif (priority < qs.length - 1) {\n\n\t\t\t\t// result = add(element, priority + 1, blocking, owner);\n\t\t\t\tresult = add(element, priority + 1, blocking);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate int findNextNonEmpty() {\n\t\tfor (int i = 0; i < qs.length; i++) {\n\t\t\tif (!qs[i].isEmpty()) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\n\t\treturn Integer.MAX_VALUE;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/util/workqueue/PriorityQueueStrict.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.workqueue;\n\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.logging.Logger;\n\n/**\n * Created: Jul 25, 2010 4:09:05 PM\n *\n* @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class PriorityQueueStrict<E>\n\t\textends PriorityQueueAbstract<E> {\n\n\tprivate static final Logger log = Logger.getLogger(PriorityQueueRelaxed.class.getName());\n\n\tprivate int lowestNonEmpty = Integer.MAX_VALUE;\n\tprivate LinkedBlockingQueue<E>[] qs = null;\n\n\n\tpublic PriorityQueueStrict() {\n\t}\n\n\t@SuppressWarnings({\"unchecked\"})\n\tprotected PriorityQueueStrict(int maxPriority, int maxSize) {\n\t\tinit(maxPriority, maxSize);\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic final void init(int maxPriority, int maxSize) {\n\n//  qs = new LinkedBlockingQueue[maxPriority + 1];\n\t\t// Setting a fixed priority to 3 which means all standard XMPP\n\t\t// packets go to the same queue, preventing packets reordering\n\t\t// 3 - SYSTEM, CLUSTER, HIGH\n//    qs = new LinkedBlockingQueue[maxPriority];\n\t\tqs = new LinkedBlockingQueue[3];\n\n\t\tfor (int i = 0; i < qs.length; i++) {\n\t\t\tqs[i] = new LinkedBlockingQueue<E>(maxSize);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean offer(E element, int priority) {\n\t\ttry {\n\n\t\t\t// return add(element, priority, false, owner);\n\t\t\treturn add(element, priority, false);\n\t\t} catch (InterruptedException e) {\n\t\t\tlog.warning(\"This should not happen, this is non-blocking operation.\");\n\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// public void put(E element, int priority, String owner) throws InterruptedException {\n\n\t@Override\n\tpublic void put(E element, int priority) throws InterruptedException {\n\n\t\t// add(element, priority, true, owner);\n\t\tadd(element, priority, true);\n\t}\n\n\t@Override\n\tpublic void setMaxSize(int maxSize) {\n\t\tfor (int i = 0; i < qs.length; i++) {\n\n\t\t\t// We don't want to lose any data so the new size must\n\t\t\t// be enough to keep all exising elements\n\t\t\tLinkedBlockingQueue<E> oldQueue = qs[i];\n\t\t\tint newSize = Math.max(oldQueue.size(), maxSize);\n\n\t\t\tqs[i] = new LinkedBlockingQueue<E>(newSize);\n\t\t\toldQueue.drainTo(qs[i]);\n\t\t}\n\t}\n\n\t@Override\n\tpublic int[] size() {\n\t\tint[] result = new int[qs.length];\n\n\t\tfor (int i = 0; i < result.length; i++) {\n\t\t\tresult[i] = qs[i].size();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic E take() throws InterruptedException {\n\t\tE e = null;\n\n\t\twhile (e == null) {\n\t\t\tsynchronized (this) {\n\t\t\t\ttry {\n\n\t\t\t\t\t// Safeguard for \"java wait spurious wakeup\", google for it\n\t\t\t\t\t// if you don't know it. I have just learned about it too...\n\t\t\t\t\twhile (lowestNonEmpty == Integer.MAX_VALUE) {\n\n//          log.finest(\"[\" + owner + \"] waiting...\");\n\t\t\t\t\t\tthis.wait();\n\t\t\t\t\t}\n\t\t\t\t} catch (InterruptedException ex) {\n\t\t\t\t}\n\n\t\t\t\tLinkedBlockingQueue<E> q = qs[lowestNonEmpty];\n\n\t\t\t\t// log.finest(\"\" + lowestNonEmpty + \" taking from queue: \");\n\t\t\t\te = q.poll();\n\n//      if (e != null) {\n//        log.finest(\"[\" + owner + \"] \" + lowestNonEmpty + \" element read: \" + e.toString());\n//      } else {\n//        log.finest(\"[\" + owner + \"] \" + lowestNonEmpty + \" NULL element read!\");\n//      }\n\t\t\t\tif ((e == null) || q.isEmpty()) {\n\t\t\t\t\tlowestNonEmpty = findNextNonEmpty();\n\n//        log.finest(\"[\" + owner + \"] \" + \"new lowestNonEmpty: \" + lowestNonEmpty);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn e;\n\t}\n\n\t@Override\n\tpublic int totalSize() {\n\t\tint result = 0;\n\n\t\tfor (int i = 0; i < qs.length; i++) {\n\t\t\tresult += qs[i].size();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t// TODO: Reduce synchronization but be carefull many threads are writing\n\t// to queues at the same time.\n\t// private boolean add(E element, int priority, boolean blocking, String owner)\n\tprivate boolean add(E element, int pr, boolean blocking) throws InterruptedException {\n\t\tint priority = pr;\n\n\t\tif (priority < 0) {\n\t\t\tthrow new IllegalArgumentException(\"parameter priority must be \" + \"between 0 and \" + (qs.length - 1));\n\t\t}\n\n\t\tif (qs.length <= priority) {\n\t\t\tpriority = qs.length - 1;\n\t\t}\n\n\t\tboolean result = true;\n\t\tLinkedBlockingQueue<E> q = qs[priority];\n\n\t\tif (blocking) {\n\t\t\tq.put(element);\n\n//    log.finest(\"[\" + owner + \"] \" + priority + \" B element added: \" + element.toString());\n\t\t\t// log.finest(\"\" + priority + \" B element added: \" + element.toString());\n\t\t} else {\n\t\t\tresult = q.offer(element);\n\n\t\t\t// log.finest(\"\" + priority + \" B element added: \" + element.toString());\n//    log.finest(\"[\" + owner + \"] \" + priority + \" NB element added: \" +\n//            element.toString() + \", result: \" + result +\n//            \", lowestNonEmpty: \" + lowestNonEmpty + \", size: \" + q.size());\n\t\t}\n\n\t\tif (result) {\n\t\t\tsynchronized (this) {\n\t\t\t\tif (priority < lowestNonEmpty) {\n\n//        log.finest(\"[\" + owner + \"] setting new priority from \" +\n//                lowestNonEmpty + \", to: \" + priority);\n\t\t\t\t\tlowestNonEmpty = priority;\n\t\t\t\t}\n\n\t\t\t\tthis.notify();\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate int findNextNonEmpty() {\n\t\tfor (int i = 0; i < qs.length; i++) {\n\t\t\tif (!qs[i].isEmpty()) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\n\t\treturn Integer.MAX_VALUE;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/vhosts/AbstractVHostItemExtension.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts;\n\nimport tigase.server.DataForm;\nimport tigase.xml.Element;\n\n/**\n * Abstract class and subclass of <code>VHostItemExtension</code> providing a helper method to add a boolean fields\n * to ad-hoc forms for manipulation of vhost extension.\n */\npublic abstract class AbstractVHostItemExtension<T extends AbstractVHostItemExtension<T>> extends VHostItemExtension<T> {\n\n\t/**\n\t * Method adds a boolean field to the form\n\t * @param commandEl - command element\n\t * @param var - field id\n\t * @param label - field label\n\t * @param value - value for a field\n\t * @param forDefault - if true then we are setting values for default instance and we can choose only YES or NO.\n\t * In other case we can also select DEFAULT to enforce usage of the default value for this vhost item extension.s\n\t */\n\tprotected void addBooleanFieldWithDefaultToCommand(Element commandEl, String var, String label, Boolean value, boolean forDefault) {\n\t\tif (forDefault) {\n\t\t\tDataForm.addFieldValue(commandEl, var, value == null ? \"\" : value.toString(), label,\n\t\t\t\t\t\t\t\t   new String[]{\"Yes\", \"No\"}, new String[]{\"true\", \"false\"});\n\t\t} else {\n\t\t\tDataForm.addFieldValue(commandEl, var, value == null ? \"\" : value.toString(), label,\n\t\t\t\t\t\t\t\t   new String[]{\"Default\", \"Yes\", \"No\"}, new String[]{\"\", \"true\", \"false\"});\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/vhosts/DefaultAwareVHostManagerIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.List;\n\n@TigaseDeprecated(since = \"8.5.0\", removeIn = \"9.0.0\", note = \"Temporary interface to avoid breaking main API\")\n@Deprecated\npublic interface DefaultAwareVHostManagerIfc extends VHostManagerIfc {\n\n    List<JID> getAllVHosts(boolean includeDefaultVhost);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/vhosts/VHostComponentRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts;\n\nimport tigase.db.comp.ComponentRepository;\n\npublic interface VHostComponentRepository\n\t\textends ComponentRepository<VHostItem> {\n\n\tVHostItem getDefaultVHostItem();\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/vhosts/VHostComponentRepositoryDataSourceAware.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts;\n\nimport tigase.db.DataSource;\nimport tigase.db.comp.ComponentRepositoryDataSourceAware;\n\npublic interface VHostComponentRepositoryDataSourceAware<DS extends DataSource>\n\t\textends ComponentRepositoryDataSourceAware<VHostItem, DS>, VHostComponentRepository {\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/vhosts/VHostItem.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.comp.RepositoryItem;\nimport tigase.vhosts.filter.DomainFilterPolicy;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Set;\n\npublic interface VHostItem\n\t\textends RepositoryItem, Comparable<VHostItem> {\n\n\tstatic final String DEF_VHOST_KEY = \"default\";\n\n\tdefault int compareTo(VHostItem o) {\n\t\treturn getKey().compareTo(o.getKey());\n\t}\n\t\n\tString[] getComps();\n\n\tint[] getC2SPortsAllowed();\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.1.0\", removeIn = \"9.0.0\")\n\t<T> T getData(String key);\n\n\tDomainFilterPolicy getDomainFilter();\n\n\tString[] getDomainFilterDomains();\n\n\t<T extends VHostItemExtension> T getExtension(Class<T> clazz);\n\n\t<T extends VHostItemExtension> Set<T> getExtensions();\n\n\tSet<Class<? extends VHostItemExtension>> getExtensionClasses();\n\n\tLong getMaxUsersNumber();\n\n\tJID getMessageForward();\n\t\n\tdefault JID getMessageForwardAddress() {\n\t\treturn getMessageForward();\n\t}\n\n\tString getOtherDomainParams();\n\n\tJID getPresenceForward();\n\n\tdefault JID getPresenceForwardAddress() {\n\t\treturn getPresenceForward();\n\t}\n\n\tString getS2sSecret();\n\n\tSet<String> getTrustedJIDs();\n\n\tJID getVhost();\n\n\tvoid setKey(String domain);\n\n\t@Override\n\tdefault boolean isAdmin(String id) {\n\t\tif (getAdmins() == null) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (String admin : getAdmins()) {\n\t\t\tif (admin.equals(id)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tdefault boolean isDefault() {\n\t\treturn VHostItem.DEF_VHOST_KEY.equals(getKey());\n\t}\n\n\tboolean isAnonymousEnabled();\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.1.0\", removeIn = \"9.0.0\")\n\tboolean isData(String key);\n\n\tboolean isEnabled();\n\n\tboolean isRegisterEnabled();\n\n\tboolean isTlsRequired();\n\n\tdefault boolean isTrustedJID(JID jid) {\n\t\tSet<String> trustedJids = getTrustedJIDs();\n\t\tif (trustedJids == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn trustedJids.contains(jid.toString()) || trustedJids.contains(jid.getBareJID().toString()) ||\n\t\t\t\ttrustedJids.contains(jid.getDomain());\n\t}\n\n\tString[] getSaslAllowedMechanisms();\n}\n"
  },
  {
    "path": "src/main/java/tigase/vhosts/VHostItemDefaults.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts;\n\nimport tigase.io.SSLContextContainer;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.vhosts.filter.DomainFilterPolicy;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.concurrent.ConcurrentSkipListSet;\n\nimport static tigase.io.SSLContextContainer.HardenedModeVHostItemExtension.parseHardenedModeFromString;\nimport static tigase.vhosts.VHostItemImpl.*;\n\n/**\n * Created by andrzej on 01.07.2017.\n */\n@Bean(name = \"defaults\", parent = VHostManager.class, active = true, exportable = true)\npublic class VHostItemDefaults {\n\n\t@ConfigField(desc = \"ANONYMOUS authentication enabled\", alias = VHOST_ANONYMOUS_ENABLED_PROP_KEY)\n\tprivate boolean anonymousEnabled = VHOST_ANONYMOUS_ENABLED_PROP_DEF;\n\t@ConfigField(desc = \"Check DNS for domain\", alias = \"vhost-disable-dns-check\")\n\tprivate boolean disableDnsCheck = false;\n\tprivate DomainFilterPolicy domainFilter = DOMAIN_FILTER_POLICY_PROP_DEF;\n\tprivate String[] domainFilterDomains = null;\n\t@ConfigField(desc = \"Domain filter policy\", alias = DOMAIN_FILTER_POLICY_PROP_KEY)\n\tprivate String domainFilterStr = null;\n\tprivate SSLContextContainer.HARDENED_MODE hardenedMode = SSLContextContainer.HARDENED_MODE.secure;\n\t@ConfigField(desc = \"Hardened mode\", alias = \"hardened-mode\")\n\tprivate String hardenedModeStr = SSLContextContainer.HARDENED_MODE.secure.toString();\n\t@ConfigField(desc = \"Maximal number of users\", alias = VHOST_MAX_USERS_PROP_KEY)\n\tprivate long maxUsersNumber = VHOST_MAX_USERS_PROP_DEF;\n\t@ConfigField(desc = \"Message forward JID\", alias = VHOST_MESSAGE_FORWARD_PROP_KEY)\n\tprivate JID messageForward = JID.jidInstanceNS(VHOST_MESSAGE_FORWARD_PROP_DEF);\n\t@ConfigField(desc = \"Presence forward JID\", alias = VHOST_PRESENCE_FORWARD_PROP_KEY)\n\tprivate JID presenceForward = JID.jidInstanceNS(VHOST_PRESENCE_FORWARD_PROP_DEF);\n\t@ConfigField(desc = \"Registration allowed\", alias = VHOST_REGISTER_ENABLED_PROP_KEY)\n\tprivate boolean registerEnabled = VHOST_REGISTER_ENABLED_PROP_DEF;\n\t@ConfigField(desc = \"S2S secret\", alias = S2S_SECRET_PROP_KEY)\n\tprivate String s2sSecret = S2S_SECRET_PROP_DEF;\n\t@ConfigField(desc = \"TLS required\", alias = VHOST_TLS_REQUIRED_PROP_KEY)\n\tprivate boolean tlsRequired = VHOST_TLS_REQUIRED_PROP_DEF;\n\t@ConfigField(desc = \"Global trusted jids\", alias = \"trusted\")\n\tprivate ConcurrentSkipListSet<String> trusted = null;\n\n\tpublic DomainFilterPolicy getDomainFilter() {\n\t\treturn domainFilter;\n\t}\n\n\tpublic String[] getDomainFilterDomains() {\n\t\treturn domainFilterDomains;\n\t}\n\n\tpublic long getMaxUsersNumber() {\n\t\treturn maxUsersNumber;\n\t}\n\n\tpublic JID getMessageForward() {\n\t\treturn messageForward;\n\t}\n\n\tpublic JID getPresenceForward() {\n\t\treturn presenceForward;\n\t}\n\n\tpublic String getS2sSecret() {\n\t\treturn s2sSecret;\n\t}\n\n\tpublic ConcurrentSkipListSet<String> getTrusted() {\n\t\treturn trusted;\n\t}\n\n\tpublic boolean isAnonymousEnabled() {\n\t\treturn anonymousEnabled;\n\t}\n\n\tpublic boolean isCheckDns() {\n\t\treturn !disableDnsCheck;\n\t}\n\n\tpublic boolean isRegisterEnabled() {\n\t\treturn registerEnabled;\n\t}\n\n\tpublic boolean isTlsRequired() {\n\t\treturn tlsRequired;\n\t}\n\n\tpublic void setDomainFilterStr(String value) {\n\t\tdomainFilter = getPolicyFromConfString(value);\n\t\tdomainFilterDomains = getDomainsFromConfString(value);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/vhosts/VHostItemExtension.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts;\n\n/**\n * Abstract class required to be a superclass for all classes implementing <code>VHostItemExtensionIfc</code>.\n */\npublic abstract class VHostItemExtension<T extends VHostItemExtension<T>> implements VHostItemExtensionIfc<T> {\n\n\t/**\n\t * Abstract method required to be implemented for merging values stored in this instance with default settings\n\t * stored in the default virtual host item (global or default settings of the installation).\n\t * \n\t * @param defaults - instance of the extension with default values\n\t * @return instance of the extension containing merged values\n\t */\n\tpublic abstract T mergeWithDefaults(T defaults);\n\n\t/**\n\t * Generic implementation of a method which combines data returned by <code>toDebugString()</code> with\n\t * class name for easier debugging.\n\t * \n\t * @return - class\n\t */\n\t@Override\n\tpublic String toString() {\n\t\treturn this.getClass().getSimpleName() + \"(\" + toDebugString() + \")\";\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/vhosts/VHostItemExtensionBackwardCompatible.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts;\n\nimport tigase.annotations.TigaseDeprecated;\n\nimport java.util.Map;\n\n@Deprecated\n@TigaseDeprecated(since = \"8.1.0\", removeIn = \"9.0.0\")\npublic interface VHostItemExtensionBackwardCompatible<T extends VHostItemExtension<T>> extends VHostItemExtensionIfc<T> {\n\n\tvoid initFromData(Map<String, Object> data);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/vhosts/VHostItemExtensionIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts;\n\nimport tigase.server.Packet;\nimport tigase.xml.Element;\n\n/**\n * Interface required to be implemented by all extensions adding additional configuration options for the virtual\n * host configuration items.\n *\n * WARNING: This is just an interface and you have to use <code>VHostItemExtension</code> class as a base class\n * for your extension.\n *\n * @param <T> - should be provided the same class as we are defining!\n */\npublic interface VHostItemExtensionIfc<T extends VHostItemExtensionIfc<T>> {\n\n\t/**\n\t * Unique identifier of the extension. It has to be a valid XML element name!\n\t */\n\tString getId();\n\n\t/**\n\t * Method initializes instances of a class with values from the element which contains configuration\n\t * loaded from the database.\n\t *\n\t * see VHostItemExtensionIfc::toElement()\n\t *\n\t * @param item - provided element with data\n\t */\n\tvoid initFromElement(Element item);\n\n\t/**\n\t * Method initializes instance of a class with values provided by the user using ad-hoc command.\n\t * \n\t * @param prefix - prefix for data for fields added by this extension\n\t * @param packet - stanza with submitted ad-hoc command form\n\t * @throws IllegalArgumentException\n\t */\n\tvoid initFromCommand(String prefix, Packet packet) throws IllegalArgumentException;\n\n\t/**\n\t * Method serializes data stored by this instance to element which will be then stored in the database.\n\t *\n\t * Element name should be equal to the extension id.\n\t */\n\tElement toElement();\n\n\t/**\n\t * Method adds custom extension fields to the ad-hoc form which will be sent to the user for filling\n\t * with data required to create or update the VHost details.\n\t *\n\t * @param prefix - prefix which should be used by each added field\n\t * @param packet - packet which will be sent to the user\n\t * @param forDefault - if true, we are preparing form for \"default\" configuration used by default by\n\t * all vhosts.\n\t */\n\tvoid addCommandFields(String prefix, Packet packet, boolean forDefault);\n\n\t/**\n\t * Returns a string containing all information about the instance of the extension useful for debugging.\n\t */\n\tString toDebugString();\n}\n"
  },
  {
    "path": "src/main/java/tigase/vhosts/VHostItemExtensionManager.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\n\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n@Bean(name = \"extension-manager\", parent = VHostManager.class, active = true, exportable = true)\npublic class VHostItemExtensionManager {\n\n\tprivate static final Logger log = Logger.getLogger(VHostItemExtensionManager.class.getCanonicalName());\n\n\t@Inject(nullAllowed = true)\n\tprivate VHostItemExtensionProvider[] providers = new VHostItemExtensionProvider[0];\n\tprivate Map<Class<VHostItemExtension>, VHostItemExtensionProvider<?>> providersByClass = new ConcurrentHashMap<>();\n\n\tpublic void setProviders(VHostItemExtensionProvider[] providers) {\n\t\tthis.providers = Optional.ofNullable(providers).orElseGet(() -> new VHostItemExtensionProvider[0]);\n\t\tSet<VHostItemExtensionProvider> newProviders = new HashSet<>(Arrays.asList(this.providers));\n\t\tthis.providersByClass.values().removeIf(provider -> !newProviders.contains(provider));\n\t\tArrays.stream(this.providers).forEach(provider -> providersByClass.put(provider.getExtensionClazz(), provider));\n\t}\n\t\n\tpublic <T extends VHostItemExtension> T newExtensionInstanceForClass(Class<T> extensionClass) {\n\t\treturn Optional.ofNullable((VHostItemExtensionProvider<T>) providersByClass.get(extensionClass))\n\t\t\t\t.map(this::newExtensionInstance)\n\t\t\t\t.orElse(null);\n\t}\n\n\tpublic <T extends VHostItemExtension> Stream<T> newExtensionInstances() {\n\t\treturn newExtensionInstances(Arrays.stream(providers));\n\t}\n\n\tpublic <T extends VHostItemExtension> Stream<T> newExtensionInstances(Stream<VHostItemExtensionProvider> providerStream) {\n\t\treturn (Stream<T>) providerStream.map(this::newExtensionInstance).filter(Objects::nonNull);\n\t}\n\n\tprotected  <T extends VHostItemExtension> T newExtensionInstance(VHostItemExtensionProvider<T> provider) {\n\t\tClass<T> extensionClass = provider.getExtensionClazz();\n\t\ttry {\n\t\t\treturn extensionClass.newInstance();\n\t\t} catch (Throwable ex) {\n\t\t\tlog.log(Level.WARNING, \"Could not create extension \" + extensionClass + \" returned by \" +\n\t\t\t\t\tprovider.getClass().getCanonicalName(), ex);\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\tpublic Stream<VHostItemExtension> addMissingExtensions(Collection<VHostItemExtension> extensions) {\n\t\tSet<String> existingExtensions = extensions.stream().map(ext -> ext.getId()).collect(Collectors.toSet());\n\t\treturn Stream.concat(extensions.stream(), newExtensionInstances(Arrays.stream(providers).filter(provider -> !existingExtensions.contains(provider.getId()))));\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/vhosts/VHostItemExtensionProvider.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts;\n\n/**\n * Interface required to be implemented by factories which are adding extensions to vhost items.\n * @param <T> - class of the extension which will be provided by this factory\n *\n * Class to work should be annotated with <code>@Bean</code> annotation and annotation <code>name</code> parameter\n * should be equal to the extension unique id. Moreover, <code>parent</code> parameter should be set to\n * <code>VHostItemExtensionManager.class</code> and <code>active</code> parameter should be set to <code>true</code>.\n *\n */\npublic interface VHostItemExtensionProvider<T extends VHostItemExtension> {\n\n\t/**\n\t * Returns unique id of the extension\n\t */\n\tString getId();\n\n\t/**\n\t * Returns class of the extension\n\t */\n\tClass<T> getExtensionClazz();\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/vhosts/VHostItemImpl.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.comp.RepositoryItemAbstract;\nimport tigase.server.Command;\nimport tigase.server.Packet;\nimport tigase.util.StringUtilities;\nimport tigase.util.repository.DataTypes;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.vhosts.filter.DomainFilterPolicy;\nimport tigase.xml.Element;\nimport tigase.xml.XMLUtils;\nimport tigase.xmpp.jid.JID;\n\nimport java.lang.reflect.Array;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Function;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * Objects of this class represent virtual host with all hosts configuration settings. In most cases simple domain name\n * string is not enough to deal with the virtual host. VHost can be enabled/disabled. Can be available to selected\n * server components only and so on. Therefore every time there is a detailed information needed for a vhost this\n * classed must be used.\n * <br>\n * This class has it's own XML representation which can be used for creating an instance of the class or can be exported\n * to the XML form for permanent storage:\n * <br>\n * {@code <vhost hostname=\"vhost.something.com\" enabled=\"true\" anon=\"true\" register=\"true\" max-users=\"99999999999L\">\n * <comps/> <other/> }\n * <br>\n * From the init.property file it is also possible to set additional parameters for the vhost. By default everything is\n * enabled and max accounts set to unlimited. In the example below we configure 2 domains:\n * <strong>devel.tigase.org</strong> and <strong>test.tigase.org</strong>. For the first domain there are no additional\n * settings, hence the domain has everything on by default, whereas the second has everything switched off and max user\n * accounts set to 100.\n * <br>\n * <pre>\n * --virt-hosts = devel.tigase.org,test.tigase.org:-anon:-register:max-users=100\n * </pre>\n * <br>\n * It also possible to set forwarding for the domain:\n * <br>\n * <pre>\n * --virt-hosts = test.tigase.org:pres-forw=lpart@domain/res:mess-forw=lpart@domain/res\n * </pre>\n * <br>\n * Please note, forwarding address set this way cannot contain any of characters: [,:=] The order features are set for\n * domain is unimportant.\n * <br>\n * Created: 22 Nov 2008\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class VHostItemImpl\n\t\textends RepositoryItemAbstract\n\t\timplements VHostItem {\n\n\tpublic static final String ANONYMOUS_ENABLED_ATT = \"anon\";\n\tpublic static final String ANONYMOUS_ENABLED_LABEL = \"Anonymous enabled\";\n\tpublic static final String SASL_MECHANISM_ATT = \"sasl-mechanisms\";\n\tpublic static final String SASL_MECHANISM_LABEL = \"Allowed SASL mechanisms\";\n\tpublic static final String COMPONENTS_ATT = \"comps\";\n\t/**\n\t * Element name for the VHostItem XML child keeping list of the server component which can handle packets for this\n\t * domain. In most cases this element should be empty.\n\t */\n\tpublic static final String COMPONENTS_ELEM = \"comps\";\n\tpublic static final String C2S_PORTS_ALLOWED_ATT = \"c2s-ports-allowed\";\n\tpublic static final String C2S_PORTS_ALLOWED_LABEL = \"Allowed C2S,BOSH,WebSocket ports\";\n\tpublic static final String DOMAIN_FILTER_POLICY_ATT = \"domain-filter\";\n\tpublic static final String DOMAIN_FILTER_POLICY_DOMAINS_ATT = \"domain-filter-domains\";\n\tpublic static final String DOMAIN_FILTER_POLICY_LABEL = \"Domain filter policy\";\n\tpublic static final String DOMAIN_FILTER_POLICY_DOMAINS_LABEL = \"Domain filter domains (only LIST and BLACKLIST)\";\n\tpublic static final String ENABLED_ATT = \"enabled\";\n\tpublic static final String ENABLED_LABEL = \"Enabled\";\n\tpublic static final String HOSTNAME_ATT = \"hostname\";\n\tpublic static final String HOSTNAME_LABEL = \"Domain name\";\n\tpublic static final String MAX_USERS_NUMBER_ATT = \"max-users\";\n\tpublic static final String MAX_USERS_NUMBER_LABEL = \"Max users\";\n\tpublic static final String MESSAGE_FORWARD_ADDRESS_ATT = \"mess-forw\";\n\tpublic static final String MESSAGE_FORWARD_ADDRESS_LABEL = \"Message forward address\";\n\tpublic static final String OTHER_PARAMS_ELEM = \"other\";\n\tpublic static final String OTHER_PARAMS_LABEL = \"Other parameters\";\n\tpublic static final String PRESENCE_FORWARD_ADDRESS_ATT = \"pres-forw\";\n\tpublic static final String PRESENCE_FORWARD_ADDRESS_LABEL = \"Presence forward address\";\n\tpublic static final String REGISTER_ENABLED_ATT = \"register\";\n\tpublic static final String REGISTER_ENABLED_LABEL = \"In-band registration\";\n\tpublic static final String S2S_SECRET_ATT = \"s2s-secret\";\n\tpublic static final String S2S_SECRET_LABEL = \"S2S secret\";\n\tpublic static final String TLS_REQUIRED_ATT = \"tls-required\";\n\tpublic static final String TLS_REQUIRED_LABEL = \"TLS required\";\n\tprivate static final String TRUSTED_JIDS_ATT = \"trusted-jids\";\n\tpublic static final String TRUSTED_JIDS_LABEL = \"Trusted JIDs\";\n\tpublic static final String VHOST_ELEM = \"vhost\";\n\tprotected static final String DOMAIN_FILTER_POLICY_PROP_KEY = \"domain-filter-policy\";\n\tprotected static final String S2S_SECRET_PROP_DEF = null;\n\tprotected static final String S2S_SECRET_PROP_KEY = \"s2s-secret\";\n\tprotected static final String VHOST_ANONYMOUS_ENABLED_PROP_KEY = \"vhost-anonymous-enabled\";\n\tprotected static final Boolean VHOST_ANONYMOUS_ENABLED_PROP_DEF = Boolean.FALSE;\n\tprotected static final String VHOST_MAX_USERS_PROP_KEY = \"vhost-max-users\";\n\tprotected static final Long VHOST_MAX_USERS_PROP_DEF = Long.valueOf(0l);\n\tprotected static final String VHOST_MESSAGE_FORWARD_PROP_DEF = null;\n\tprotected static final String VHOST_MESSAGE_FORWARD_PROP_KEY = \"vhost-message-forward-jid\";\n\tprotected static final String VHOST_PRESENCE_FORWARD_PROP_DEF = null;\n\tprotected static final String VHOST_PRESENCE_FORWARD_PROP_KEY = \"vhost-presence-forward-jid\";\n\tprotected static final String VHOST_REGISTER_ENABLED_PROP_KEY = \"vhost-register-enabled\";\n\tprotected static final Boolean VHOST_REGISTER_ENABLED_PROP_DEF = Boolean.TRUE;\n\tprotected static final String VHOST_TLS_REQUIRED_PROP_KEY = \"vhost-tls-required\";\n\tprotected static final Boolean VHOST_TLS_REQUIRED_PROP_DEF = Boolean.TRUE;\n\tprotected static final DomainFilterPolicy DOMAIN_FILTER_POLICY_PROP_DEF = DomainFilterPolicy.ALL;\n\tprotected static final String[] VHOST_OTHER_PARAMS_PATH = {VHOST_ELEM, OTHER_PARAMS_ELEM};\n\tprotected static final String[] VHOST_COMPONENTS_PATH = {VHOST_ELEM, COMPONENTS_ELEM};\n\tprotected static final Map<String, DataType> dataTypes = Collections.synchronizedMap(new LinkedHashMap<>());\n\tprivate static final Logger log = Logger.getLogger(VHostItemImpl.class.getName());\n\t\n\tprivate boolean anonymousEnabled = VHOST_ANONYMOUS_ENABLED_PROP_DEF;\n\tprivate int[] c2sPortsAllowed = null;\n\n\tprivate String[] comps = new String[0];\n\tprivate Map<String, Element> unknownExtensions = new ConcurrentHashMap<>();\n\tprivate Map<Class<? extends VHostItemExtension>, VHostItemExtension> extensions = new ConcurrentHashMap<>();\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.1.0\", removeIn = \"9.0.0\")\n\tprivate Map<String, Object> oldData = new ConcurrentHashMap<String, Object>();\n\tprivate DomainFilterPolicy domainFilter = DOMAIN_FILTER_POLICY_PROP_DEF;\n\tprivate String[] domainFilterDomains = null;\n\tprivate boolean enabled = true;\n\tprivate Long maxUsersNumber = VHOST_MAX_USERS_PROP_DEF;\n\tprivate JID messageForward = JID.jidInstanceNS(VHOST_MESSAGE_FORWARD_PROP_DEF);\n\tprivate String otherDomainParams = null;\n\tprivate JID presenceForward = JID.jidInstanceNS(VHOST_PRESENCE_FORWARD_PROP_DEF);\n\tprivate boolean registerEnabled = VHOST_REGISTER_ENABLED_PROP_DEF;\n\tprivate String s2sSecret = S2S_SECRET_PROP_DEF;\n\tprivate String[] saslAllowedMechanisms = null;\n\tprivate boolean tlsRequired = VHOST_TLS_REQUIRED_PROP_DEF;\n\tprivate Set<String> trustedJids = Collections.emptySet();\n\tprivate JID vhost = null;\n\n\tprivate VHostItemExtensionManager extensionManager;\n\t\n\tstatic DomainFilterPolicy getPolicyFromConfString(String configuration) {\n\t\tString[] df = configuration.split(\"=\");\n\n\t\ttry {\n\t\t\tif (df.length >= 2) {\n\t\t\t\treturn DomainFilterPolicy.valueof(df[1]);\n\t\t\t} else {\n\t\t\t\treturn DomainFilterPolicy.ALL;\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\treturn DomainFilterPolicy.ALL;\n\t\t}\n\t}\n\n\tstatic String[] getDomainsFromConfString(String configuration) {\n\t\tString[] df = configuration.split(\"=\");\n\n\t\ttry {\n\t\t\tif (df.length >= 3 && df[2] != null && !df[2].trim().isEmpty()) {\n\t\t\t\treturn StringUtilities.stringToArrayOfString(df[2], \";\");\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\t// just return default for non-existing configuration\n\t\t}\n\t\treturn new String[0];\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.1.0\", removeIn = \"9.0.0\")\n\tpublic static void registerData(List<DataType> types) {\n\t\tfor (DataType type : types) {\n\t\t\tdataTypes.put(type.getKey(), type);\n\t\t}\n\t}\n\n\tpublic VHostItemImpl() {\n\t\t// added to ensure that we have s2sSecret set, as without it S2S connections\n\t\t// will always fail (needed mostly for newly added vhosts).\n\t\tif (s2sSecret == null) {\n\t\t\ts2sSecret = UUID.randomUUID().toString();\n\t\t}\n\t}\n\n\t/**\n\t * The constructor creates the <code>VHostItem</code> instance from a given XML element. Please refer to the class\n\t * documentation for more details of the XML element.\n\t *\n\t * @param elem is an <code>Element</code> object with virtual domain settings.\n\t */\n\tpublic VHostItemImpl(Element elem) {\n\t\tthis();\n\t\tinitFromElement(elem);\n\t}\n\n\t/**\n\t * The constructor creates the <code>VHostItem</code> instance for a given domain name with default values for all\n\t * other parameters. By the default all domain parameters are set to true.\n\t *\n\t * @param vhost is a <code>String</code> value with a domain name.\n\t */\n\tpublic VHostItemImpl(JID vhost) {\n\t\tthis();\n\t\tsetVHost(vhost);\n\t}\n\n\t/**\n\t * The constructor creates the <code>VHostItem</code> instance for a given domain name with default values for all\n\t * other parameters. By the default all domain parameters are set to true.\n\t *\n\t * @param vhost is a <code>String</code> value with a domain name.\n\t *\n\t * @throws TigaseStringprepException if the provided string causes stringprep processing errors.\n\t */\n\tpublic VHostItemImpl(String vhost) throws TigaseStringprepException {\n\t\tthis();\n\t\tsetVHost(vhost);\n\t}\n\n\tprotected void setExtensionManager(VHostItemExtensionManager extensionManager) {\n\t\tthis.extensionManager = extensionManager;\n\t}\n\n\t@Override\n\tpublic void addCommandFields(Packet packet) {\n\t\tif (!isDefault()) {\n\t\t\tCommand.addInstructions(packet, \"❗NOTE: Options without value set will use configuration defined in 'DEFAULT' VHost❗\");\n\t\t} else {\n\t\t\tCommand.addInstructions(packet, \"❗This VHost is intended to configure certain default values for all VHosts in the system and it's not actual, available domain to which you can connect❗\");\n\t\t}\n\t\tCommand.addFieldValue(packet, HOSTNAME_LABEL, (vhost != null) ? vhost.getDomain() : \"\");\n\t\tif (!isDefault()) {\n\t\t\tCommand.addCheckBoxField(packet, ENABLED_LABEL, enabled);\n\t\t}\n\t\tCommand.addCheckBoxField(packet, ANONYMOUS_ENABLED_LABEL, anonymousEnabled);\n\t\tCommand.addCheckBoxField(packet, REGISTER_ENABLED_LABEL, registerEnabled);\n\t\tCommand.addCheckBoxField(packet, TLS_REQUIRED_LABEL, tlsRequired);\n\t\tif (!isDefault()) {\n\t\t\tCommand.addFieldValue(packet, S2S_SECRET_LABEL, (s2sSecret != null) ? s2sSecret : \"\");\n\t\t}\n\t\tif (isDefault()) {\n\t\t\tCommand.addFieldValue(packet, DOMAIN_FILTER_POLICY_LABEL, domainFilter.toString(),\n\t\t\t\t\t\t\t\t  DOMAIN_FILTER_POLICY_LABEL, DomainFilterPolicy.valuesStr(), DomainFilterPolicy.valuesStr());\n\t\t} else {\n\t\t\tString[] values = Stream.concat(Stream.of(\"DEFAULT\"), Arrays.stream(DomainFilterPolicy.valuesStr()))\n\t\t\t\t\t.toArray(String[]::new);\n\t\t\tCommand.addFieldValue(packet, DOMAIN_FILTER_POLICY_LABEL, domainFilter != null ? domainFilter.toString() : \"\",\n\t\t\t\t\t\t\t\t  DOMAIN_FILTER_POLICY_LABEL, values, values);\n\t\t}\n\t\tCommand.addFieldValue(packet, DOMAIN_FILTER_POLICY_DOMAINS_LABEL,\n\t\t\t\t\t\t\t  domainFilterDomains != null ? StringUtilities.stringArrayToString(domainFilterDomains, \";\") : \"\");\n\t\tCommand.addFieldValue(packet, MAX_USERS_NUMBER_LABEL,\n\t\t\t\t\t\t\t  Optional.ofNullable(maxUsersNumber).map(String::valueOf).orElse(\"\"));\n\t\tString c2sPortsAllowedStr = intArrayToString(c2sPortsAllowed, \",\");\n\t\tCommand.addFieldValue(packet, C2S_PORTS_ALLOWED_LABEL, c2sPortsAllowedStr != null ? c2sPortsAllowedStr : \"\");\n\t\tCommand.addFieldValue(packet, PRESENCE_FORWARD_ADDRESS_LABEL,\n\t\t\t\t\t\t\t  ((presenceForward != null) ? presenceForward.toString() : \"\"));\n\t\tCommand.addFieldValue(packet, MESSAGE_FORWARD_ADDRESS_LABEL,\n\t\t\t\t\t\t\t  ((messageForward != null) ? messageForward.toString() : \"\"));\n\t\tCommand.addFieldValue(packet, OTHER_PARAMS_LABEL, (otherDomainParams != null) ? otherDomainParams : \"\");\n\t\tCommand.addFieldValue(packet, SASL_MECHANISM_LABEL,\n\t\t\t\t\t\t\t  saslAllowedMechanisms != null ? StringUtilities.stringArrayToString(saslAllowedMechanisms, \",\") : \"\");\n\n\t\tCommand.addFieldMultiValue(packet, TRUSTED_JIDS_LABEL,\n\t\t\t\t\t\t\t\t   trustedJids != null ? new ArrayList<>(trustedJids) : Collections.EMPTY_LIST);\n\n\t\tsuper.addCommandFields(packet);\n\n\t\textensionManager.addMissingExtensions(extensions.values()\n\t\t\t\t\t\t\t\t\t\t\t\t\t  .stream()\n\t\t\t\t\t\t\t\t\t\t\t\t\t  .map(v -> (VHostItemExtension) v)\n\t\t\t\t\t\t\t\t\t\t\t\t\t  .collect(Collectors.toSet()))\n\t\t\t\t.sorted(Comparator.comparing(VHostItemExtension::getId))\n\t\t\t\t.forEach(ext -> ext.addCommandFields(ext.getId(), packet, isDefault()));\n\n\t\tfor (DataType type : dataTypes.values()) {\n\t\t\tif (type.cls != Boolean.class) {\n\t\t\t\tObject[] options = type.getOptions();\n\t\t\t\tObject val = getData(type.getKey());\n\t\t\t\tif (val instanceof Collection) {\n\t\t\t\t\tCollection collection = (Collection) val;\n\t\t\t\t\tval = Array.newInstance(type.getCls().getComponentType(), collection.size());\n\t\t\t\t\tint i = 0;\n\t\t\t\t\tfor (Object v : collection) {\n\t\t\t\t\t\tArray.set(val, i, v);\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tString valueStr = val != null ? DataTypes.valueToString(val) : \"\";\n\t\t\t\tif (options == null || options.length == 0) {\n\t\t\t\t\tCommand.addFieldValue(packet, type.getName(), valueStr);\n\t\t\t\t} else {\n\t\t\t\t\tString[] optionsStr = new String[options.length];\n\t\t\t\t\tfor (int i = 0; i < options.length; i++) {\n\t\t\t\t\t\toptionsStr[i] = (options[i] != null) ? DataTypes.valueToString(options[i]) : \"\";\n\t\t\t\t\t}\n\t\t\t\t\tString[] optionsNames = type.getOptionsNames();\n\t\t\t\t\tif (optionsNames == null) {\n\t\t\t\t\t\toptionsNames = optionsStr;\n\t\t\t\t\t}\n\t\t\t\t\tCommand.addFieldValue(packet, type.getName(), valueStr, type.getName(), optionsNames, optionsStr);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tboolean val = isData(type.getKey());\n\t\t\t\tCommand.addCheckBoxField(packet, type.getName(), val);\n\t\t\t}\n\t\t}\n\t}\n\t\n\t@Override\n\tpublic boolean equals(Object v) {\n\t\treturn (v instanceof VHostItem) ? getKey().equals(((VHostItem) v).getKey()) : false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn vhost.hashCode();\n\t}\n\n\t@Override\n\tpublic void initFromCommand(Packet packet) {\n\t\tsuper.initFromCommand(packet);\n\n\t\tString tmp = Command.getFieldValue(packet, HOSTNAME_LABEL);\n\n\t\ttry {\n\t\t\tsetVHost(tmp);\n\t\t} catch (TigaseStringprepException ex) {\n\t\t\tthrow new IllegalArgumentException(\"Incorrect domain, unable to parse it: \" + tmp, ex);\n\t\t}\n\t\tenabled = Command.getCheckBoxFieldValue(packet, ENABLED_LABEL);\n\t\tanonymousEnabled = Command.getCheckBoxFieldValue(packet, ANONYMOUS_ENABLED_LABEL);\n\t\tregisterEnabled = Command.getCheckBoxFieldValue(packet, REGISTER_ENABLED_LABEL);\n\t\tOptional.ofNullable(Command.getFieldValue(packet, TLS_REQUIRED_LABEL))\n\t\t\t\t.ifPresent(s -> tlsRequired = Boolean.parseBoolean(s));\n\t\ttmp = Command.getFieldValue(packet, S2S_SECRET_LABEL);\n\t\tif ((tmp != null) && !tmp.trim().isEmpty()) {\n\t\t\ts2sSecret = tmp;\n\t\t}\n\t\ttmp = Command.getFieldValue(packet, DOMAIN_FILTER_POLICY_LABEL);\n\t\ttry {\n\t\t\tdomainFilter = DomainFilterPolicy.valueof(tmp);\n\t\t\tif (domainFilter == null && isDefault()) {\n\t\t\t\tdomainFilter = DOMAIN_FILTER_POLICY_PROP_DEF;\n\t\t\t}\n\t\t\tif (domainFilter != null && domainFilter.isDomainListRequired()) {\n\t\t\t\tdomainFilterDomains = Optional.ofNullable(\n\t\t\t\t\t\tCommand.getFieldValue(packet, DOMAIN_FILTER_POLICY_DOMAINS_LABEL))\n\t\t\t\t\t\t.map(String::trim)\n\t\t\t\t\t\t.filter(s -> !s.isEmpty())\n\t\t\t\t\t\t.map(s -> StringUtilities.stringToArrayOfString(s, \";\"))\n\t\t\t\t\t\t.orElse(null);\n\t\t\t} else {\n\t\t\t\tdomainFilterDomains = null;\n\t\t\t}\n\t\t} catch (Exception ex) {\n\t\t\tdomainFilter = isDefault() ? DOMAIN_FILTER_POLICY_PROP_DEF : null;\n\t\t\tdomainFilterDomains = null;\n\t\t}\n\t\ttry {\n\t\t\tmaxUsersNumber = Optional.ofNullable(Command.getFieldValue(packet, MAX_USERS_NUMBER_LABEL))\n\t\t\t\t\t.map(String::trim)\n\t\t\t\t\t.filter(s -> !s.isEmpty())\n\t\t\t\t\t.map(Long::parseLong)\n\t\t\t\t\t.orElse(isDefault() ? VHOST_MAX_USERS_PROP_DEF : null);\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Can not parse max users number: {0}\",\n\t\t\t\t\tCommand.getFieldValue(packet, MAX_USERS_NUMBER_LABEL));\n\t\t}\n\t\ttmp = Command.getFieldValue(packet, PRESENCE_FORWARD_ADDRESS_LABEL);\n\t\tif ((tmp != null) && !tmp.trim().isEmpty()) {\n\t\t\ttry {\n\t\t\t\tpresenceForward = JID.jidInstance(tmp);\n\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\tpresenceForward = null;\n\n\t\t\t\tthrow new IllegalArgumentException(\"Incorrect presence forward address: \" + tmp, ex);\n\t\t\t}\n\t\t}\n\t\ttmp = Command.getFieldValue(packet, MESSAGE_FORWARD_ADDRESS_LABEL);\n\t\tif ((tmp != null) && !tmp.trim().isEmpty()) {\n\t\t\ttry {\n\t\t\t\tmessageForward = JID.jidInstance(tmp);\n\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\tmessageForward = null;\n\n\t\t\t\tthrow new IllegalArgumentException(\"Incorrect message forward address: \" + tmp, ex);\n\t\t\t}\n\t\t}\n\t\totherDomainParams = Optional.ofNullable(Command.getFieldValue(packet, OTHER_PARAMS_LABEL)).filter(s -> !s.isEmpty()).orElse(null);\n\t\ttmp = Command.getFieldValue(packet, C2S_PORTS_ALLOWED_LABEL);\n\t\tc2sPortsAllowed = parseIntArray(tmp, \",\");\n\n\t\ttmp = Command.getFieldValue(packet, SASL_MECHANISM_LABEL);\n\t\tif ((tmp != null) && !tmp.trim().isEmpty()) {\n\t\t\tfinal String[] split = tmp.split(\",\");\n\t\t\tfor (String mechanism : split) {\n\t\t\t\tmechanism.trim();\n\t\t\t}\n\t\t\tsetSaslAllowedMechanisms(split);\n\t\t}\n\n\t\tString[] tmps = Command.getFieldValues(packet, TRUSTED_JIDS_LABEL);\n\t\tif (tmps != null) {\n\t\t\ttrustedJids = Arrays.stream(tmps).map(String::trim).filter(s -> !s.isEmpty()).collect(Collectors.toSet());\n\t\t} else {\n\t\t\ttrustedJids = Collections.emptySet();\n\t\t}\n\n\t\textensionManager.newExtensionInstances().map(extension -> {\n\t\t\textension.initFromCommand(extension.getId(), packet);\n\t\t\treturn extension;\n\t\t}).forEach(extension -> extensions.put((Class<VHostItemExtension>) extension.getClass(), extension));\n\n\t\tfor (DataType type : dataTypes.values()) {\n\t\t\tString valueStr = Command.getFieldValue(packet, type.getName());\n\t\t\tCharacter typeId = DataTypes.typesMap.get(type.cls.getName());\n\t\t\tObject value = (valueStr == null || valueStr.isEmpty())\n\t\t\t\t\t\t   ? null\n\t\t\t\t\t\t   : DataTypes.decodeValueType(typeId, valueStr);\n\t\t\tif (value != null && type.getCollectionCls() != null) {\n\t\t\t\ttry {\n\t\t\t\t\tCollection collection = type.getCollectionCls().newInstance();\n\t\t\t\t\tfor (int i = 0; i < Array.getLength(value); i++) {\n\t\t\t\t\t\tcollection.add(Array.get(value, i));\n\t\t\t\t\t}\n\t\t\t\t\tvalue = collection;\n\t\t\t\t} catch (InstantiationException | IllegalAccessException ex) {\n\t\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\t\"Could not instantiate collection of class: \" + type.getCollectionCls().getCanonicalName(),\n\t\t\t\t\t\t\tex);\n\t\t\t\t}\n\t\t\t}\n\t\t\tsetData(type.getKey(), value);\n\t\t}\n\n\t\tlog.log(Level.FINE, \"Initialized from command: {0}\", this);\n\n\t}\n\n\tprivate static final Set<String> bannedExtensionIds = new HashSet<>(Arrays.asList(COMPONENTS_ELEM, OTHER_PARAMS_ELEM, TRUSTED_JIDS_ATT, \"data\"));\n\n\t@Override\n\tpublic void initFromElement(Element elem) {\n\t\tif (elem.getName() != VHOST_ELEM) {\n\t\t\tthrow new IllegalArgumentException(\"Incorrect element name, expected: \" + VHOST_ELEM);\n\t\t}\n\t\tsuper.initFromElement(elem);\n\t\tsetVHost(JID.jidInstanceNS(elem.getAttributeStaticStr(HOSTNAME_ATT)));\n\t\tenabled = Boolean.parseBoolean(elem.getAttributeStaticStr(ENABLED_ATT));\n\t\tanonymousEnabled = Boolean.parseBoolean(elem.getAttributeStaticStr(ANONYMOUS_ENABLED_ATT));\n\t\tregisterEnabled = Boolean.parseBoolean(elem.getAttributeStaticStr(REGISTER_ENABLED_ATT));\n\t\ttlsRequired = Boolean.parseBoolean(elem.getAttributeStaticStr(TLS_REQUIRED_ATT));\n\t\ts2sSecret = elem.getAttributeStaticStr(S2S_SECRET_ATT);\n\t\ttry {\n\t\t\tdomainFilter = DomainFilterPolicy.valueof(elem.getAttributeStaticStr(DOMAIN_FILTER_POLICY_ATT));\n\t\t\tif (domainFilter == null && isDefault()) {\n\t\t\t\tdomainFilter = DOMAIN_FILTER_POLICY_PROP_DEF;\n\t\t\t}\n\t\t\tif (domainFilter != null && domainFilter.isDomainListRequired()) {\n\t\t\t\tString tmp = elem.getAttributeStaticStr(DOMAIN_FILTER_POLICY_DOMAINS_ATT);\n\t\t\t\tif (tmp != null && !tmp.trim().isEmpty()) {\n\t\t\t\t\tdomainFilterDomains = StringUtilities.stringToArrayOfString(tmp, \";\");\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tdomainFilter = isDefault() ? DOMAIN_FILTER_POLICY_PROP_DEF : null;\n\t\t}\n\t\ttry {\n\t\t\tmaxUsersNumber = Optional.ofNullable(elem.getAttributeStaticStr(MAX_USERS_NUMBER_ATT))\n\t\t\t\t\t.map(String::trim)\n\t\t\t\t\t.filter(s -> !s.isEmpty())\n\t\t\t\t\t.map(Long::parseLong)\n\t\t\t\t\t.orElse(isDefault() ? Long.getLong(VHOST_MAX_USERS_PROP_KEY, VHOST_MAX_USERS_PROP_DEF) : null);\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Can not parse max users number: {0}\",\n\t\t\t\t\telem.getAttributeStaticStr(MAX_USERS_NUMBER_ATT));\n\t\t\tmaxUsersNumber = isDefault() ? Long.getLong(VHOST_MAX_USERS_PROP_KEY, VHOST_MAX_USERS_PROP_DEF) : null;\n\t\t}\n\n\t\tString tmp = elem.getAttributeStaticStr(PRESENCE_FORWARD_ADDRESS_ATT);\n\n\t\tif ((tmp != null) && !tmp.trim().isEmpty()) {\n\t\t\tpresenceForward = JID.jidInstanceNS(tmp);\n\t\t}\n\t\ttmp = elem.getAttributeStaticStr(MESSAGE_FORWARD_ADDRESS_ATT);\n\t\tif ((tmp != null) && !tmp.trim().isEmpty()) {\n\t\t\tmessageForward = JID.jidInstanceNS(tmp);\n\t\t}\n\n\t\tString comps_str = elem.getCDataStaticStr(VHOST_COMPONENTS_PATH);\n\n\t\tif ((comps_str != null) && !comps_str.isEmpty()) {\n\t\t\tcomps = comps_str.split(\",\");\n\t\t}\n\t\totherDomainParams = Optional.ofNullable(elem.getCDataStaticStr(VHOST_OTHER_PARAMS_PATH))\n\t\t\t\t.map(String::trim)\n\t\t\t\t.filter(s -> !s.isEmpty())\n\t\t\t\t.orElse(isDefault() ? \"\" : null);\n\n\t\tthis.c2sPortsAllowed = parseIntArray(elem.getAttributeStaticStr(C2S_PORTS_ALLOWED_ATT), \",\");\n\n\t\ttmp = elem.getAttributeStaticStr(SASL_MECHANISM_ATT);\n\t\tif (tmp != null) {\n\t\t\tsetSaslAllowedMechanisms(tmp.split(\";\"));\n\t\t}\n\n\n\t\tElement data = elem.getChild(\"data\");\n\t\tif (data != null) {\n\t\t\tList<Element> items = data.getChildren();\n\t\t\tif (items != null) {\n\t\t\t\tfor (Element item : items) {\n\t\t\t\t\tDataType type = dataTypes.get(item.getName());\n\t\t\t\t\tCharacter typeChar = type != null\n\t\t\t\t\t\t\t\t\t\t ? DataTypes.typesMap.get(type.getCls().getName())\n\t\t\t\t\t\t\t\t\t\t : item.getAttributeStaticStr(\"type\").charAt(0);\n\t\t\t\t\tObject value = DataTypes.decodeValueType(typeChar, item.getCData());\n\t\t\t\t\tif (type != null && type.getCollectionCls() != null && value != null) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tCollection collection = type.getCollectionCls().newInstance();\n\t\t\t\t\t\t\tfor (int i = 0; i < Array.getLength(value); i++) {\n\t\t\t\t\t\t\t\tcollection.add(Array.get(value, i));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tvalue = collection;\n\t\t\t\t\t\t} catch (InstantiationException | IllegalAccessException ex) {\n\t\t\t\t\t\t\tthrow new IllegalArgumentException(\"Could not instantiate collection of class: \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   type.getCollectionCls().getCanonicalName(), ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tsetData(item.getName(), value);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\ttmp = elem.getCDataStaticStr(new String[]{VHOST_ELEM, TRUSTED_JIDS_ATT});\n\t\tif (tmp == null) {\n\t\t\ttmp = (String) oldData.remove(TRUSTED_JIDS_ATT);\n\t\t}\n\t\tif (tmp != null && !tmp.isEmpty()) {\n\t\t\tthis.trustedJids = Collections.unmodifiableSet(\n\t\t\t\t\tArrays.stream(tmp.split(\",\")).map(String::trim).collect(Collectors.toSet()));\n\t\t} else  {\n\t\t\tthis.trustedJids = Collections.emptySet();\n\t\t}\n\n\t\tList<Element> children = elem.getChildren();\n\t\tif (children != null) {\n\t\t\tunknownExtensions.putAll(children\n\t\t\t\t\t.stream()\n\t\t\t\t\t.filter(child -> !bannedExtensionIds.contains(child.getName()))\n\t\t\t\t\t.collect(Collectors.toConcurrentMap(Element::getName, Function.identity())));\n\t\t}\n\t\textensionManager.newExtensionInstances()\n\t\t\t\t.map(this::initExtension)\n\t\t\t\t.forEach(extension -> extensions.put((Class<VHostItemExtension>) extension.getClass(), extension));\n\n\t\tlog.log(Level.FINE, \"Initialized from element: {0}\", this);\n\t}\n\n\tprotected VHostItemExtension initExtension(VHostItemExtension extension) {\n\t\tfinal String id = extension.getId();\n\t\tElement extElem = unknownExtensions.remove(id);\n\t\tif (extElem != null) {\n\t\t\ttry {\n\t\t\t\textension.initFromElement(extElem);\n\t\t\t} catch (Throwable ex) {\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\"Could not initialize \" + extension.getClass().getCanonicalName() + \" with data \" +\n\t\t\t\t\t\t\t\textElem);\n\t\t\t}\n\t\t} else if (extension instanceof VHostItemExtensionBackwardCompatible) {\n\t\t\ttry {\n\t\t\t\t((VHostItemExtensionBackwardCompatible) extension).initFromData(oldData);\n\t\t\t} catch (Throwable ex) {\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\"Could not initialize \" + extension.getClass().getCanonicalName() + \" with oldData \" +\n\t\t\t\t\t\t\t\toldData, ex);\n\t\t\t}\n\t\t}\n\t\treturn extension;\n\t}\n\n\t@Override\n\tpublic void setKey(String key) {\n\t\tsetVHost(JID.jidInstanceNS(key));\n\t}\n\t\n\t@Override\n\tpublic void initFromPropertyString(String propString) {\n\t\tString[] props = propString.split(\":\");\n\n\t\ttry {\n\t\t\tsetVHost(props[0]);\n\t\t} catch (TigaseStringprepException ex) {\n\t\t\tthrow new IllegalArgumentException(\"Domain misconfiguration, cannot parse it: \" + props[0], ex);\n\t\t}\n\t\tfor (String tmp : props) {\n\t\t\tboolean val = true;\n\n\t\t\tif (tmp.startsWith(\"-\")) {\n\t\t\t\tval = false;\n\t\t\t}\n\t\t\tif (tmp.endsWith(ANONYMOUS_ENABLED_ATT)) {\n\t\t\t\tanonymousEnabled = val;\n\t\t\t}\n\t\t\tif (tmp.endsWith(REGISTER_ENABLED_ATT)) {\n\t\t\t\tregisterEnabled = val;\n\t\t\t}\n\t\t\tif (tmp.endsWith(TLS_REQUIRED_ATT)) {\n\t\t\t\ttlsRequired = val;\n\t\t\t}\n\t\t\tif (tmp.startsWith(S2S_SECRET_ATT)) {\n\t\t\t\tString[] s2 = tmp.split(\"=\");\n\n\t\t\t\ts2sSecret = s2[1];\n\t\t\t}\n\t\t\tif (tmp.startsWith(COMPONENTS_ATT)) {\n\t\t\t\tString[] c = tmp.split(\"=\");\n\t\t\t\tString comps_str = c[1];\n\n\t\t\t\tif (!comps_str.isEmpty()) {\n\t\t\t\t\tcomps = comps_str.split(\";\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (tmp.startsWith(DOMAIN_FILTER_POLICY_ATT)) {\n\t\t\t\tdomainFilter = getPolicyFromConfString(tmp);\n\t\t\t\tif (domainFilter == null && isDefault()) {\n\t\t\t\t\tdomainFilter = DOMAIN_FILTER_POLICY_PROP_DEF;\n\t\t\t\t}\n\t\t\t\tif (domainFilter.isDomainListRequired()) {\n\t\t\t\t\tdomainFilterDomains = getDomainsFromConfString(tmp);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (tmp.startsWith(MAX_USERS_NUMBER_ATT)) {\n\t\t\t\tString[] mu = tmp.split(\"=\");\n\n\t\t\t\ttry {\n\t\t\t\t\tmaxUsersNumber = Long.parseLong(mu[1]);\n\t\t\t\t} catch (NumberFormatException ex) {\n\t\t\t\t\tmaxUsersNumber = 0L;\n\t\t\t\t\tlog.log(Level.WARNING, \"Incorrect max users number for vhost settings, number parsing error: {0}\",\n\t\t\t\t\t\t\ttmp);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (tmp.startsWith(PRESENCE_FORWARD_ADDRESS_ATT)) {\n\t\t\t\tString[] mu = tmp.split(\"=\");\n\n\t\t\t\ttry {\n\t\t\t\t\tpresenceForward = JID.jidInstance(mu[1]);\n\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\tpresenceForward = null;\n\t\t\t\t\tlog.log(Level.WARNING, \"Incorrect presence forwarding address, address parsing error: {0}\", tmp);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (tmp.startsWith(MESSAGE_FORWARD_ADDRESS_ATT)) {\n\t\t\t\tString[] mu = tmp.split(\"=\");\n\n\t\t\t\ttry {\n\t\t\t\t\tmessageForward = JID.jidInstance(mu[1]);\n\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\tmessageForward = null;\n\t\t\t\t\tlog.log(Level.WARNING, \"Incorrect presence forwarding address, address parsing error: {0}\", tmp);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (tmp.startsWith(C2S_PORTS_ALLOWED_ATT)) {\n\t\t\t\tString[] mu = tmp.split(\"=\");\n\n\t\t\t\tc2sPortsAllowed = parseIntArray(mu[1], \";\");\n\t\t\t}\n\t\t\tif (tmp.startsWith(SASL_MECHANISM_ATT)) {\n\t\t\t\tString[] mu = tmp.split(\"=\");\n\t\t\t\tsetSaslAllowedMechanisms(mu[1].split(\";\"));\n\t\t\t}\n\n\t\t\tString[] mu = tmp.split(\"=\");\n\t\t\tif (mu != null && mu.length == 2) {\n\t\t\t\tif (dataTypes.containsKey(mu[0])) {\n\t\t\t\t\tparseDataValue(mu[0], mu[1]);\n\n\t\t\t\t} else if (mu[0].equals(TRUSTED_JIDS_ATT)) {\n\t\t\t\t\tif (mu[1].contains(\",\")) {\n\t\t\t\t\t\ttrustedJids = Collections.unmodifiableSet(Arrays.stream(mu[1].split(\",\")).map(String::trim).collect(Collectors.toSet()));\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttrustedJids = Collections.unmodifiableSet(Arrays.stream(mu[1].split(\";\")).map(String::trim).collect(Collectors.toSet()));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t\tif (trustedJids == null) {\n\t\t\ttrustedJids = Collections.EMPTY_SET;\n\t\t}\n\t\tif (extensionManager != null) {\n\t\t\textensionManager.newExtensionInstances()\n\t\t\t\t\t.map(this::initExtension)\n\t\t\t\t\t.forEach(extension -> extensions.put((Class<VHostItemExtension>) extension.getClass(), extension));\n\t\t}\n\t\tlog.log(Level.FINE, \"Initialized from property string: {0}\", this);\n\t}\n\n\t@Override\n\tpublic Element toElement() {\n\t\tElement elem = super.toElement();\n\t\tString comps_str = \"\";\n\n\t\tif ((comps != null) && (comps.length > 0)) {\n\t\t\tfor (String comp : comps) {\n\t\t\t\tif (!comps_str.isEmpty()) {\n\t\t\t\t\tcomps_str += \",\";\n\t\t\t\t}\n\t\t\t\tcomps_str += comp;\n\t\t\t}\n\t\t}\n\n\t\tString other_params = (otherDomainParams != null) ? otherDomainParams : \"\";\n\n\t\tif (comps_str != null && !comps_str.isEmpty()) {\n\t\t\telem.addChild(new Element(COMPONENTS_ELEM, comps_str));\n\t\t}\n\t\tif (other_params != null && !other_params.isEmpty()) {\n\t\t\telem.addChild(new Element(OTHER_PARAMS_ELEM, other_params));\n\t\t}\n\t\telem.addAttribute(HOSTNAME_ATT, vhost.getDomain());\n\t\telem.addAttribute(ENABLED_ATT, \"\" + enabled);\n\t\telem.addAttribute(ANONYMOUS_ENABLED_ATT, \"\" + anonymousEnabled);\n\t\tif (saslAllowedMechanisms != null) {\n\t\t\telem.addAttribute(SASL_MECHANISM_ATT, StringUtilities.stringArrayToString(saslAllowedMechanisms, \";\"));\n\t\t}\n\t\telem.addAttribute(REGISTER_ENABLED_ATT, \"\" + registerEnabled);\n\t\telem.addAttribute(TLS_REQUIRED_ATT, \"\" + tlsRequired);\n\t\tif (s2sSecret != null) {\n\t\t\telem.addAttribute(S2S_SECRET_ATT, s2sSecret);\n\t\t}\n\t\tif (domainFilter != null) {\n\t\t\telem.addAttribute(DOMAIN_FILTER_POLICY_ATT, domainFilter.toString());\n\t\t}\n\t\tif (domainFilterDomains != null) {\n\t\t\telem.addAttribute(DOMAIN_FILTER_POLICY_DOMAINS_ATT, StringUtilities.stringArrayToString(domainFilterDomains, \";\"));\n\t\t}\n\t\tif (maxUsersNumber != null) {\n\t\t\telem.addAttribute(MAX_USERS_NUMBER_ATT, \"\" + maxUsersNumber);\n\t\t}\n\t\tif (presenceForward != null) {\n\t\t\telem.addAttribute(PRESENCE_FORWARD_ADDRESS_ATT, presenceForward.toString());\n\t\t}\n\t\tif (messageForward != null) {\n\t\t\telem.addAttribute(MESSAGE_FORWARD_ADDRESS_ATT, messageForward.toString());\n\t\t}\n\t\tif (c2sPortsAllowed != null) {\n\t\t\tString c2sPortsAllowedStr = intArrayToString(c2sPortsAllowed, \",\");\n\t\t\telem.addAttribute(C2S_PORTS_ALLOWED_ATT, c2sPortsAllowedStr);\n\t\t}\n\n\t\tif (trustedJids != null && !trustedJids.isEmpty()) {\n\t\t\telem.addChild(new Element(TRUSTED_JIDS_ATT,\n\t\t\t\t\t\t\t\t\t  StringUtilities.stringArrayToString(trustedJids.toArray(new String[0]), \",\")));\n\t\t}\n\n\t\textensions.entrySet()\n\t\t\t\t.stream()\n\t\t\t\t.sorted(Comparator.comparing(e -> e.getKey().getSimpleName()))\n\t\t\t\t.map(Map.Entry::getValue)\n\t\t\t\t.map(VHostItemExtension::toElement)\n\t\t\t\t.filter(Objects::nonNull)\n\t\t\t\t.forEach(elem::addChild);\n\n\t\tunknownExtensions.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey)).forEach((e) -> {\n\t\t\telem.addChild((Element) e.getValue());\n\t\t});\n\n\t\tif (!oldData.isEmpty()) {\n\t\t\tElement data = new Element(\"data\");\n\t\t\tfor (Map.Entry<String, Object> e : this.oldData.entrySet()) {\n\t\t\t\tElement item = new Element(e.getKey());\n\t\t\t\titem.addAttribute(\"type\", String.valueOf(DataTypes.getTypeId(e.getValue())));\n\t\t\t\tObject val = e.getValue();\n\t\t\t\tDataType type = dataTypes.get(e.getKey());\n\t\t\t\tif (type != null && val instanceof Collection && type.getCollectionCls() != null) {\n\t\t\t\t\tCollection collection = (Collection) val;\n\t\t\t\t\tval = Array.newInstance(type.getCls().getComponentType(), collection.size());\n\t\t\t\t\tint i = 0;\n\t\t\t\t\tfor (Object v : collection) {\n\t\t\t\t\t\tArray.set(val, i, v);\n\t\t\t\t\t\ti++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\titem.setCData(DataTypes.valueToString(val));\n\t\t\t\tdata.addChild(item);\n\t\t\t}\n\t\t\telem.addChild(data);\n\t\t}\n\n\t\treturn elem;\n\t}\n\t\n\t@Override\n\tpublic String toPropertyString() {\n\t\tStringBuilder sb = new StringBuilder();\n\n\t\tsb.append(vhost.toString());\n\t\tif (!anonymousEnabled) {\n\t\t\tsb.append(\":-\").append(ANONYMOUS_ENABLED_ATT);\n\t\t}\n\t\tif (!registerEnabled) {\n\t\t\tsb.append(\":-\").append(REGISTER_ENABLED_ATT);\n\t\t}\n\t\tif (!tlsRequired) {\n\t\t\tsb.append(\":-\").append(TLS_REQUIRED_ATT);\n\t\t}\n\t\tif (s2sSecret != null) {\n\t\t\tsb.append(':').append(S2S_SECRET_ATT).append('=').append(s2sSecret);\n\t\t}\n\t\tsb.append(':').append(DOMAIN_FILTER_POLICY_ATT).append('=').append(domainFilter.toString());\n\t\tif (domainFilterDomains != null) {\n\t\t\tsb.append(\"=\").append(StringUtilities.stringArrayToString(domainFilterDomains, \";\"));\n\t\t}\n\t\tif (maxUsersNumber != null && maxUsersNumber > 0) {\n\t\t\tsb.append(':').append(MAX_USERS_NUMBER_ATT).append('=').append(maxUsersNumber);\n\t\t}\n\t\tif (presenceForward != null) {\n\t\t\tsb.append(':').append(PRESENCE_FORWARD_ADDRESS_ATT).append('=').append(presenceForward.toString());\n\t\t}\n\t\tif (messageForward != null) {\n\t\t\tsb.append(':').append(MESSAGE_FORWARD_ADDRESS_ATT).append('=').append(messageForward.toString());\n\t\t}\n\t\tif (c2sPortsAllowed != null) {\n\t\t\tsb.append(':').append(C2S_PORTS_ALLOWED_ATT).append('=').append(intArrayToString(c2sPortsAllowed, \";\"));\n\t\t}\n\n\t\tif (saslAllowedMechanisms != null) {\n\t\t\tsb.append(':')\n\t\t\t\t\t.append(SASL_MECHANISM_ATT)\n\t\t\t\t\t.append('=')\n\t\t\t\t\t.append(StringUtilities.stringArrayToString(saslAllowedMechanisms, \";\"));\n\t\t}\n\n\t\treturn sb.toString();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tfinal StringBuilder sb = toString(this);\n\t\tfor (Map.Entry<String, Object> e : oldData.entrySet()) {\n\t\t\tObject val = e.getValue();\n\t\t\tDataType type = dataTypes.get(e.getKey());\n\t\t\tif (type != null && val instanceof Collection && type.getCollectionCls() != null) {\n\t\t\t\tCollection collection = (Collection) val;\n\t\t\t\tval = Array.newInstance(type.getCls().getComponentType(), collection.size());\n\t\t\t\tint i = 0;\n\t\t\t\tfor (Object v : collection) {\n\t\t\t\t\tArray.set(val, i, v);\n\t\t\t\t\ti++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tsb.append(\", \").append(e.getKey()).append(\": \").append(DataTypes.valueToString(val));\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tprotected static StringBuilder toString(VHostItem item) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(\"Domain: \").append(item.getVhost());\n\t\tsb.append(\", enabled: \").append(item.isEnabled());\n\t\tsb.append(\", anonym: \").append(item.isAnonymousEnabled());\n\t\tsb.append(\", register: \").append(item.isRegisterEnabled());\n\t\tsb.append(\", tls: \").append(item.isTlsRequired());\n\t\tif (item.getMaxUsersNumber() != null) {\n\t\t\tsb.append(\", maxusers: \").append(item.getMaxUsersNumber());\n\t\t}\n\t\tif (item.getS2sSecret() != null) {\n\t\t\tsb.append(\", s2sSecret: \").append(item.getS2sSecret());\n\t\t}\n\t\tif (item.getDomainFilter() != null) {\n\t\t\tsb.append(\", domainFilter: \").append(item.getDomainFilter());\n\t\t}\n\t\tif (item.getDomainFilterDomains() != null && item.getDomainFilterDomains().length > 0) {\n\t\t\tsb.append(\", domainFilterDomains: \").append(StringUtilities.stringArrayToString(item.getDomainFilterDomains(), \";\"));\n\t\t}\n\t\tif (item.getC2SPortsAllowed() != null && item.getC2SPortsAllowed().length > 0) {\n\t\t\tsb.append(\", c2sPortsAllowed: \").append(Arrays.toString(item.getC2SPortsAllowed()));\n\t\t}\n\t\tif (item.getSaslAllowedMechanisms() != null && item.getSaslAllowedMechanisms().length > 0) {\n\t\t\tsb.append(\", saslAllowedMechanisms: \").append(Arrays.toString(item.getSaslAllowedMechanisms()));\n\t\t}\n\t\tif (item.getTrustedJIDs() != null && !item.getTrustedJIDs().isEmpty()) {\n\t\t\tsb.append(\", trustedJids: \").append(item.getTrustedJIDs());\n\t\t}\n\t\tif (item.getMessageForward() != null) {\n\t\t\tsb.append(\", messageForward: \").append(item.getMessageForward());\n\t\t}\n\t\tif (item.getPresenceForward() != null) {\n\t\t\tsb.append(\", presenceForward: \").append(item.getPresenceForward());\n\t\t}\n\t\tif (!item.getExtensions().isEmpty()) {\n\t\t\tsb.append(item.getExtensions()\n\t\t\t\t\t\t\t  .stream()\n\t\t\t\t\t\t\t  .map(VHostItemExtension::toString)\n\t\t\t\t\t\t\t  .collect(Collectors.joining(\", \", \"; Extensions: [\", \"]\")));\n\t\t}\n\t\treturn sb;\n\t}\n\n\t/**\n\t * Returns an array with the server components names which should process packets sent to this domain or\n\t * <code>null</code> (default) if there is no specific component assigned to this domain.\n\t *\n\t * @return a <code>String[]</code> object with server component names.\n\t */\n\t@Override\n\tpublic String[] getComps() {\n\t\treturn comps;\n\t}\n\n\t/**\n\t * Sets an array with the server component names by which packets to this domain can be processed. Every local\n\t * domain will be handled by <code>VHostListener</code> which returns <code>true</code> for\n\t * <code>handlesLocalDomains()</code> method call and by all components set via this method.\n\t *\n\t * @param comps is an <code>String[]</code> array with server component names.\n\t */\n\tpublic void setComps(String[] comps) {\n\t\tthis.comps = comps;\n\t}\n\n\t/**\n\t * Returns an array with ports on which C2S connections for this VHosts are allowed.\n\t *\n\t * @return a <code>int[]</code> object with allowed C2S ports.\n\t */\n\t@Override\n\tpublic int[] getC2SPortsAllowed() {\n\t\treturn c2sPortsAllowed;\n\t}\n\n\tpublic void setC2SPortsAllowed(int[] ports) {\n\t\tthis.c2sPortsAllowed = ports;\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.1.0\", removeIn = \"9.0.0\")\n\tpublic <T> T getData(String key) {\n\t\tT val = (T) oldData.get(key);\n\t\tif (val == null) {\n\t\t\tDataType type = dataTypes.get(key);\n\t\t\tif (type != null) {\n\t\t\t\tval = type.getDefValue();\n\t\t\t}\n\t\t}\n\t\treturn val;\n\t}\n\n\t@Override\n\tpublic DomainFilterPolicy getDomainFilter() {\n\t\treturn domainFilter;\n\t}\n\n\t/**\n\t * This method allow configure DomainFilterPolicy to be applied during packet filtering.\n\t *\n\t * @param domainFilter name of the DomainFilterPolicy to be applied\n\t */\n\tpublic void setDomainFilter(DomainFilterPolicy domainFilter) {\n\t\tthis.domainFilter = domainFilter;\n\t}\n\n\t@Override\n\tpublic String[] getDomainFilterDomains() {\n\t\treturn domainFilterDomains;\n\t}\n\n\t/**\n\t * This method allow specify list of domains that will be used for packet filtering when DomainFilteringPolicy is\n\t * set to either LIST or BLACKLIST.\n\t *\n\t * @param domainFilterDomains array of domains to be applied during filtering\n\t */\n\tpublic void setDomainFilterDomains(String[] domainFilterDomains) {\n\t\tthis.domainFilterDomains = StringUtilities.internStringArray(domainFilterDomains);\n\n\t}\n\n\t@Override\n\tpublic String getElemName() {\n\t\treturn VHOST_ELEM;\n\t}\n\tpublic <T extends VHostItemExtension> T getExtension(Class<T> clazz) {\n\t\treturn (T) extensions.computeIfAbsent(clazz, key -> {\n\t\t\tif (extensionManager == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tVHostItemExtension ext = extensionManager.newExtensionInstanceForClass(key);\n\t\t\tif (ext != null) {\n\t\t\t\tthis.initExtension(ext);\n\t\t\t}\n\t\t\treturn ext;\n\t\t});\n\t}\n\n\t@Override\n\tpublic <T extends VHostItemExtension> Set<T> getExtensions() {\n\t\tfinal Collection<VHostItemExtension> values = extensions.values();\n\t\tfinal Set<VHostItemExtension> vHostItemExtensions = new HashSet<>(values);\n\t\treturn (Set<T>) Collections.unmodifiableSet(vHostItemExtensions);\n\t}\n\n\tpublic Set<Class<? extends VHostItemExtension>> getExtensionClasses() {\n\t\treturn new HashSet(extensions.keySet());\n\t}\n\n\t@Override\n\tpublic String getKey() {\n\t\treturn this.vhost == null ? null : this.vhost.getDomain();\n\t}\n\n\t@Override\n\tpublic Long getMaxUsersNumber() {\n\t\treturn maxUsersNumber;\n\t}\n\n\tpublic void setMaxUsersNumber(long maxUsersNumber) {\n\t\tthis.maxUsersNumber = maxUsersNumber;\n\t}\n\n\t@Override\n\tpublic JID getMessageForward() {\n\t\treturn messageForward;\n\t}\n\n\tpublic void setMessageForward(JID messageForward) {\n\t\tthis.messageForward = messageForward;\n\t}\n\n\t@Override\n\tpublic JID getMessageForwardAddress() {\n\t\treturn messageForward;\n\t}\n\n\t/**\n\t * This method allows to access the virtual domain other configuration parameters. This is future feature API and it\n\t * is not used right now. It allows to access configuration parameters which are not specified at the time of API\n\t * definition.\n\t *\n\t * @return a <code>String</code> value with domain extra parameters.\n\t */\n\t@Override\n\tpublic String getOtherDomainParams() {\n\t\treturn otherDomainParams;\n\t}\n\n\t/**\n\t * This method allows to set extra configuration parameters for the virtual domain. This is future feature API and\n\t * it is not used right now. It allows to access configuration parameters which are not specified at the time of API\n\t * definition.\n\t *\n\t * @param otherParams is a <code>String</code> value with domain extra parameters.\n\t */\n\tpublic void setOtherDomainParams(String otherParams) {\n\t\tthis.otherDomainParams = otherParams;\n\t}\n\n\t@Override\n\tpublic JID getPresenceForward() {\n\t\treturn presenceForward;\n\t}\n\n\tpublic void setPresenceForward(JID presenceForward) {\n\t\tthis.presenceForward = presenceForward;\n\t}\n\n\t@Override\n\tpublic JID getPresenceForwardAddress() {\n\t\treturn presenceForward;\n\t}\n\n\t@Override\n\tpublic String getS2sSecret() {\n\t\treturn s2sSecret;\n\t}\n\n\tpublic void setS2sSecret(String s2sSecret) {\n\t\tthis.s2sSecret = s2sSecret;\n\t}\n\n\t@Override\n\tpublic Set<String> getTrustedJIDs() {\n\t\treturn trustedJids;\n\t}\n\t\n\tpublic JID getVhost() {\n\t\treturn this.vhost;\n\t}\n\n\tpublic boolean isAnonymousEnabled() {\n\t\treturn anonymousEnabled;\n\t}\n\n\tpublic void setAnonymousEnabled(boolean value) {\n\t\tthis.anonymousEnabled = value;\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.1.0\", removeIn = \"9.0.0\")\n\tpublic boolean isData(String key) {\n\t\tif (oldData.containsKey(key)) {\n\t\t\treturn (Boolean) oldData.get(key);\n\t\t} else {\n\t\t\tDataType type = dataTypes.get(key);\n\t\t\tBoolean defValue = (type == null) ? null : (Boolean) type.getDefValue();\n\t\t\treturn defValue != null ? defValue : false;\n\t\t}\n\t}\n\n\tpublic boolean isEnabled() {\n\t\treturn enabled;\n\t}\n\n\tpublic void setEnabled(boolean value) {\n\t\tthis.enabled = value;\n\t}\n\n\tpublic boolean isRegisterEnabled() {\n\t\treturn registerEnabled;\n\t}\n\n\tpublic void setRegisterEnabled(boolean value) {\n\t\tthis.registerEnabled = value;\n\t}\n\n\tpublic boolean isTlsRequired() {\n\t\treturn tlsRequired;\n\t}\n\n\tpublic void setTlsRequired(boolean value) {\n\t\tthis.tlsRequired = value;\n\t}\n\n\tpublic void setData(String key, Object value) {\n\t\tif (value == null) {\n\t\t\tthis.oldData.remove(key);\n\t\t} else {\n\t\t\tthis.oldData.put(key, value);\n\t\t}\n\t}\n\n\tpublic void parseDataValue(String key, String valueStr) {\n\t\tDataType type = dataTypes.get(key);\n\t\tif (type == null) {\n\t\t\tthrow new RuntimeException(\"Key \" + key + \" is not registered\");\n\t\t}\n\n\t\tif (valueStr == null) {\n\t\t\tthis.oldData.remove(key);\n\t\t} else {\n\t\t\tchar typeId = DataTypes.typesMap.get(type.cls.getName());\n\t\t\tif (valueStr.contains(\";\")) {\n\t\t\t\tvalueStr = valueStr.replace(';', ',');\n\t\t\t}\n\t\t\tObject value = (valueStr == null || valueStr.isEmpty())\n\t\t\t\t\t\t   ? null\n\t\t\t\t\t\t   : DataTypes.decodeValueType(typeId, valueStr);\n\t\t\tif (type.getCollectionCls() != null && value != null) {\n\t\t\t\ttry {\n\t\t\t\t\tCollection collection = type.getCollectionCls().newInstance();\n\t\t\t\t\tfor (int i = 0; i < Array.getLength(value); i++) {\n\t\t\t\t\t\tcollection.add(Array.get(value, i));\n\t\t\t\t\t}\n\t\t\t\t\tvalue = collection;\n\t\t\t\t} catch (InstantiationException | IllegalAccessException ex) {\n\t\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\t\"Could not instantiate collection of class: \" + type.getCollectionCls().getCanonicalName(),\n\t\t\t\t\t\t\tex);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsetData(type.getKey(), value);\n\t\t}\n\t}\n\n\tpublic void setVHost(String vhost) throws TigaseStringprepException {\n\t\tif (vhost == null) {\n\t\t\tvhost = \"\";\n\t\t}\n\t\tthis.vhost = JID.jidInstance(vhost);\n\t}\n\n\tpublic void setVHost(JID vhost) {\n\t\tthis.vhost = vhost;\n\t}\n\n\tpublic String[] getSaslAllowedMechanisms() {\n\t\treturn saslAllowedMechanisms;\n\t}\n\n\tpublic void setSaslAllowedMechanisms(String[] saslAllowedMechanisms) {\n\t\tthis.saslAllowedMechanisms =\n\t\t\t\tsaslAllowedMechanisms == null || saslAllowedMechanisms.length == 0 ? null : saslAllowedMechanisms;\n\t}\n\t\n\tprotected void initializeFromDefaults(VHostItemDefaults vhostDefaults) {\n\t\tif (comps == null) {\n\t\t\tcomps = new String[0];\n\t\t}\n\t\tif (vhostDefaults.getTrusted() != null) {\n\t\t\ttrustedJids = vhostDefaults.getTrusted();\n\t\t} else if (trustedJids == null) {\n\t\t\ttrustedJids = Collections.EMPTY_SET;\n\t\t}\n\t\tmaxUsersNumber = vhostDefaults.getMaxUsersNumber();\n\t\tmessageForward = vhostDefaults.getMessageForward();\n\t\tpresenceForward = vhostDefaults.getPresenceForward();\n\t\ttlsRequired = vhostDefaults.isTlsRequired();\n\t\ts2sSecret = vhostDefaults.getS2sSecret();\n\t\tregisterEnabled = vhostDefaults.isRegisterEnabled();\n\t\tdomainFilter = vhostDefaults.getDomainFilter();\n\t\tdomainFilterDomains = vhostDefaults.getDomainFilterDomains();\n\t\tanonymousEnabled = vhostDefaults.isAnonymousEnabled();\n\n\t\tif (s2sSecret == null) {\n\t\t\ts2sSecret = UUID.randomUUID().toString();\n\t\t}\n\t}\n\n\tprivate int[] parseIntArray(String tmp, String separator) {\n\t\tint[] c2s_ports_allowed = null;\n\t\tif (tmp != null && !tmp.isEmpty()) {\n\t\t\tString[] tmpPorts = tmp.split(separator);\n\t\t\tc2s_ports_allowed = new int[tmpPorts.length];\n\t\t\tint filled = 0;\n\t\t\tfor (String portStr : tmpPorts) {\n\t\t\t\ttry {\n\t\t\t\t\tc2s_ports_allowed[filled] = Integer.parseInt(portStr);\n\t\t\t\t\tfilled++;\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Can not parse allowed c2s port: {0}\", portStr);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (filled == 0) {\n\t\t\t\tc2s_ports_allowed = null;\n\t\t\t} else if (filled < c2s_ports_allowed.length) {\n\t\t\t\tc2s_ports_allowed = Arrays.copyOf(c2s_ports_allowed, filled);\n\t\t\t}\n\t\t\tif (c2s_ports_allowed != null) {\n\t\t\t\tArrays.sort(c2s_ports_allowed);\n\t\t\t}\n\t\t}\n\t\treturn c2s_ports_allowed;\n\t}\n\n\tprivate String intArrayToString(int[] arr, String separator) {\n\t\tif (arr == null) {\n\t\t\treturn null;\n\t\t}\n\t\tStringBuilder buf = new StringBuilder();\n\t\tfor (int i = 0; i < arr.length; i++) {\n\t\t\tif (i > 0) {\n\t\t\t\tbuf.append(separator);\n\t\t\t}\n\t\t\tbuf.append(arr[i]);\n\t\t}\n\t\treturn buf.toString();\n\t}\n\n\n\tpublic static class DataType {\n\n\t\tprivate final Class cls;\n\t\tprivate final Class collectionCls;\n\t\tprivate final Object defValue;\n\t\tprivate final String key;\n\t\tprivate final String name;\n\t\tprivate final Object[] options;\n\t\tprivate final String[] optionsNames;\n\n\t\tpublic DataType(String key, String name, Class cls, Class<? extends Collection> collectionCls, Object defValue,\n\t\t\t\t\t\tObject[] options, String[] optionsNames) {\n\t\t\tthis.key = key;\n\t\t\tthis.name = name;\n\t\t\tthis.cls = cls;\n\t\t\tthis.collectionCls = collectionCls;\n\t\t\tthis.defValue = defValue;\n\t\t\tthis.options = options;\n\t\t\tthis.optionsNames = optionsNames;\n\n\t\t\tif (defValue != null && !cls.isAssignableFrom(defValue.getClass())) {\n\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\"default value parameter must be of class \" + cls.getCanonicalName());\n\t\t\t}\n\n\t\t\tif (options != null) {\n\t\t\t\tfor (Object option : options) {\n\t\t\t\t\tif (option != null && !cls.isAssignableFrom(option.getClass())) {\n\t\t\t\t\t\tthrow new IllegalArgumentException(\"option values must of class \" + cls.getCanonicalName());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (optionsNames != null && options.length != optionsNames.length) {\n\t\t\t\t\tthrow new IllegalArgumentException(\"if passed options name must be specified for each option\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic DataType(String key, String name, Class cls, Class<? extends Collection> collectionCls, Object defValue,\n\t\t\t\t\t\tObject[] options) {\n\t\t\tthis(key, name, cls, collectionCls, defValue, options, null);\n\t\t}\n\n\t\tpublic DataType(String key, String name, Class cls, Class<? extends Collection> collectionCls,\n\t\t\t\t\t\tObject defValue) {\n\t\t\tthis(key, name, cls, collectionCls, defValue, null, null);\n\t\t}\n\n\t\tpublic DataType(String key, String name, Class cls, Object defValue, Object[] options) {\n\t\t\tthis(key, name, cls, null, defValue, options, null);\n\t\t}\n\n\t\tpublic DataType(String key, String name, Class cls, Object defValue) {\n\t\t\tthis(key, name, cls, null, defValue, null, null);\n\t\t}\n\n\t\tpublic <E extends Enum<E>> DataType(String key, String name, Class<? extends Enum<E>> e, E defValue) {\n\n\t\t\tString[] options = new String[defValue != null\n\t\t\t\t\t\t\t\t\t\t  ? e.getEnumConstants().length\n\t\t\t\t\t\t\t\t\t\t  : e.getEnumConstants().length + 1];\n\t\t\tint idx = 0;\n\n\t\t\tif (defValue == null) {\n\t\t\t\toptions[idx++] = null;\n\t\t\t}\n\t\t\tfor (Enum<E> en : e.getEnumConstants()) {\n\t\t\t\toptions[idx++] = en.name();\n\t\t\t}\n\n\t\t\tthis.key = key;\n\t\t\tthis.name = name;\n\t\t\tthis.cls = String.class;\n\t\t\tthis.collectionCls = null;\n\t\t\tthis.defValue = (defValue != null ? defValue.name() : null);\n\t\t\tthis.options = options;\n\t\t\tthis.optionsNames = null;\n\t\t}\n\n\t\tpublic String getName() {\n\t\t\treturn name;\n\t\t}\n\n\t\tpublic String getKey() {\n\t\t\treturn key;\n\t\t}\n\n\t\tpublic Class getCls() {\n\t\t\treturn cls;\n\t\t}\n\n\t\tpublic Class<? extends Collection> getCollectionCls() {\n\t\t\treturn collectionCls;\n\t\t}\n\n\t\tpublic <T> T getDefValue() {\n\t\t\treturn (T) defValue;\n\t\t}\n\n\t\tpublic <T> T[] getOptions() {\n\t\t\treturn (T[]) options;\n\t\t}\n\n\t\tpublic String[] getOptionsNames() {\n\t\t\treturn optionsNames;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tfinal StringBuilder sb = new StringBuilder();\n\t\t\tsb.append(key).append(':');\n\t\t\tsb.append(name).append(\" @ \");\n\t\t\tsb.append(cls);\n\t\t\treturn sb.toString();\n\t\t}\n\t}\n\n\tprotected static class VHostItemWrapper implements VHostItem {\n\n\t\tprivate VHostItem defaultVHost;\n\t\tprivate VHostItem item;\n\t\tprivate VHostItemDefaults vhostDefaults;\n\n\t\tprivate boolean anonymousEnabled = false;\n\t\tprivate int[] c2sPortsAllowed = null;\n\t\tprivate String[] comps;\n\t\tprivate DomainFilterPolicy domainFilter = DOMAIN_FILTER_POLICY_PROP_DEF;\n\t\tprivate String[] domainFilterDomains = null;\n\t\tprivate Long maxUsersNumber = VHOST_MAX_USERS_PROP_DEF;\n\t\tprivate JID messageForward = JID.jidInstanceNS(VHOST_MESSAGE_FORWARD_PROP_DEF);\n\t\tprivate String otherDomainParams = null;\n\t\tprivate JID presenceForward = JID.jidInstanceNS(VHOST_PRESENCE_FORWARD_PROP_DEF);\n\t\tprivate boolean registerEnabled = VHOST_REGISTER_ENABLED_PROP_DEF;\n\t\tprivate String s2sSecret = S2S_SECRET_PROP_DEF;\n\t\tprivate String[] saslAllowedMechanisms = null;\n\t\tprivate boolean tlsRequired = VHOST_TLS_REQUIRED_PROP_DEF;\n\t\tprivate Set<String> trustedJids = null;\n\n\t\tprivate Map<Class<? extends VHostItemExtension>, VHostItemExtension> extensions = new ConcurrentHashMap<>();\n\n\t\tprivate boolean editable = true;\n\n\t\tpublic VHostItemWrapper() {\n\t\t}\n\n\t\tpublic void setItem(VHostItem item) {\n\t\t\tthis.item = item;\n\t\t}\n\n\t\tpublic void setDefaultVHost(VHostItem item) {\n\t\t\tthis.defaultVHost = unwrap(item);\n\t\t\trefresh();\n\t\t}\n\n\t\tpublic void setVHostDefaults(VHostItemDefaults vHostDefaults) {\n\t\t\tthis.vhostDefaults = vHostDefaults;\n\t\t}\n\n\t\tprivate static VHostItem unwrap(VHostItem item) {\n\t\t\treturn (item instanceof VHostItemWrapper) ? ((VHostItemWrapper)item).item : item;\n\t\t}\n\n\t\tpublic void refresh() {\n\t\t\tif (defaultVHost == null || item == null) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (item.getKey() != null && item.getKey().equals(VHostItem.DEF_VHOST_KEY)) {\n\t\t\t\toverrideSettingsWithNewDefaults(item);\n\t\t\t} else {\n\t\t\t\tmergeSettingsWithUpdatedDefaults(item, defaultVHost);\n\t\t\t}\n\t\t}\n\n\t\tprivate void overrideSettingsWithNewDefaults(VHostItem item) {\n\t\t\tdefaultVHost = item;\n\t\t\tcomps = defaultVHost.getComps();\n\t\t\tc2sPortsAllowed = defaultVHost.getC2SPortsAllowed();\n\t\t\tdomainFilter = Optional.ofNullable(defaultVHost.getDomainFilter()).orElse(DOMAIN_FILTER_POLICY_PROP_DEF);\n\t\t\tdomainFilterDomains = defaultVHost.getDomainFilterDomains();\n\t\t\tmaxUsersNumber = defaultVHost.getMaxUsersNumber();\n\t\t\tmessageForward = defaultVHost.getMessageForward();\n\t\t\totherDomainParams = defaultVHost.getOtherDomainParams();\n\t\t\tpresenceForward = defaultVHost.getPresenceForward();\n\t\t\tregisterEnabled = defaultVHost.isRegisterEnabled();\n\t\t\ts2sSecret = defaultVHost.getS2sSecret();\n\t\t\ttrustedJids = defaultVHost.getTrustedJIDs();\n\t\t\tsaslAllowedMechanisms = defaultVHost.getSaslAllowedMechanisms();\n\t\t\ttlsRequired = defaultVHost.isTlsRequired();\n\t\t\tanonymousEnabled = defaultVHost.isAnonymousEnabled();\n\n\t\t\textensions.clear();\n\t\t\textensions = defaultVHost.getExtensionClasses()\n\t\t\t\t\t.stream()\n\t\t\t\t\t.map(defaultVHost::getExtension)\n\t\t\t\t\t.collect(Collectors.toMap(VHostItemExtension::getClass, Function.identity()));\n\t\t}\n\n\t\tprivate void mergeSettingsWithUpdatedDefaults(VHostItem item, VHostItem defaultVHost) {\n\t\t\tcomps = Stream.concat(Arrays.stream(item.getComps()), Arrays.stream(defaultVHost.getComps())).toArray(String[]::new);\n\t\t\tc2sPortsAllowed = Optional.ofNullable(item.getC2SPortsAllowed()).orElseGet(defaultVHost::getC2SPortsAllowed);\n\t\t\tdomainFilter = Optional.ofNullable(item.getDomainFilter()).orElseGet(defaultVHost::getDomainFilter);\n\t\t\tif (domainFilter.isDomainListRequired()) {\n\t\t\t\tdomainFilterDomains = Optional.ofNullable(item.getDomainFilterDomains()).orElseGet(defaultVHost::getDomainFilterDomains);\n\t\t\t} else {\n\t\t\t\tdomainFilterDomains = null;\n\t\t\t}\n\t\t\tmaxUsersNumber = Optional.ofNullable(Optional.ofNullable(item.getMaxUsersNumber()).orElseGet(defaultVHost::getMaxUsersNumber)).orElse(VHOST_MAX_USERS_PROP_DEF);\n\t\t\tmessageForward = Optional.ofNullable(item.getMessageForward()).orElseGet(defaultVHost::getMessageForward);\n\t\t\totherDomainParams = Optional.ofNullable(item.getOtherDomainParams()).orElseGet(defaultVHost::getOtherDomainParams);\n\t\t\tpresenceForward = Optional.ofNullable(item.getPresenceForward()).orElseGet(defaultVHost::getPresenceForward);\n\t\t\tregisterEnabled = defaultVHost.isRegisterEnabled() && item.isRegisterEnabled();\n\t\t\ts2sSecret = item.getS2sSecret();\n\t\t\ttrustedJids = Collections.unmodifiableSet(Stream.concat(item.getTrustedJIDs().stream(), defaultVHost.getTrustedJIDs().stream()).collect(\n\t\t\t\t\tCollectors.toSet()));\n\t\t\tsaslAllowedMechanisms = Optional.ofNullable(item.getSaslAllowedMechanisms()).orElseGet(defaultVHost::getSaslAllowedMechanisms);\n\t\t\ttlsRequired = defaultVHost.isTlsRequired() || item.isTlsRequired();\n\t\t\tanonymousEnabled = defaultVHost.isAnonymousEnabled() && item.isAnonymousEnabled();\n\n\t\t\textensions.clear();\n\t\t\tfor (Class<? extends VHostItemExtension> extClass : item.getExtensionClasses()) {\n\t\t\t\tfinal VHostItemExtension defaultsExtension = defaultVHost.getExtension(extClass);\n\t\t\t\tfinal VHostItemExtension itemExtension = item.getExtension(extClass);\n\t\t\t\textensions.put(extClass, itemExtension.mergeWithDefaults(defaultsExtension));\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic String[] getComps() {\n\t\t\treturn comps;\n\t\t}\n\n\t\t@Override\n\t\tpublic int[] getC2SPortsAllowed() {\n\t\t\treturn c2sPortsAllowed;\n\t\t}\n\n\t\t@Override\n\t\tpublic <T> T getData(String key) {\n\t\t\treturn this.item.getData(key);\n\t\t}\n\n\t\t@Override\n\t\tpublic DomainFilterPolicy getDomainFilter() {\n\t\t\treturn domainFilter;\n\t\t}\n\n\t\t@Override\n\t\tpublic String[] getDomainFilterDomains() {\n\t\t\treturn domainFilterDomains;\n\t\t}\n\n\t\t@Override\n\t\tpublic <T extends VHostItemExtension> T getExtension(Class<T> clazz) {\n\t\t\treturn (T) extensions.computeIfAbsent(clazz, cls -> {\n\t\t\t\tT ext = (T) item.getExtension(cls);\n\t\t\t\tif (ext != null) {\n\t\t\t\t\text.mergeWithDefaults(defaultVHost.getExtension(cls));\n\t\t\t\t}\n\t\t\t\treturn ext;\n\t\t\t});\n\t\t}\n\n\t\t@Override\n\t\tpublic <T extends VHostItemExtension> Set<T> getExtensions() {\n\t\t\tfinal Collection<VHostItemExtension> values = extensions.values();\n\t\t\tfinal Set<VHostItemExtension> vHostItemExtensions = new HashSet<>(values);\n\t\t\treturn (Set<T>)Collections.unmodifiableSet(vHostItemExtensions);\n\t\t}\n\n\t\t@Override\n\t\tpublic Set<Class<? extends VHostItemExtension>> getExtensionClasses() {\n\t\t\treturn new HashSet<>(extensions.keySet());\n\t\t}\n\n\t\t@Override\n\t\tpublic Long getMaxUsersNumber() {\n\t\t\treturn maxUsersNumber;\n\t\t}\n\n\t\t@Override\n\t\tpublic JID getMessageForward() {\n\t\t\treturn messageForward;\n\t\t}\n\t\t\n\t\t@Override\n\t\tpublic String getOtherDomainParams() {\n\t\t\treturn otherDomainParams;\n\t\t}\n\n\t\t@Override\n\t\tpublic JID getPresenceForward() {\n\t\t\treturn presenceForward;\n\t\t}\n\t\t\n\t\t@Override\n\t\tpublic String getS2sSecret() {\n\t\t\treturn s2sSecret;\n\t\t}\n\n\t\t@Override\n\t\tpublic Set<String> getTrustedJIDs() {\n\t\t\treturn trustedJids;\n\t\t}\n\n\t\t@Override\n\t\tpublic JID getVhost() {\n\t\t\treturn item.getVhost();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isAnonymousEnabled() {\n\t\t\treturn anonymousEnabled;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isData(String key) {\n\t\t\treturn item.isData(key);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isEnabled() {\n\t\t\treturn item.isEnabled();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isRegisterEnabled() {\n\t\t\treturn registerEnabled;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isTlsRequired() {\n\t\t\treturn tlsRequired;\n\t\t}\n\t\t\n\t\t@Override\n\t\tpublic String[] getSaslAllowedMechanisms() {\n\t\t\treturn saslAllowedMechanisms;\n\t\t}\n\n\t\t@Override\n\t\tpublic void addCommandFields(Packet packet) {\n\t\t\titem.addCommandFields(packet);\n\t\t\tif (vhostDefaults.isTlsRequired()) {\n\t\t\t\tElement commandEl = packet.getElement().getChild(Command.COMMAND_EL, Command.XMLNS);\n\t\t\t\tif (commandEl != null) {\n\t\t\t\t\tElement x = commandEl.getChild(\"x\", \"jabber:x:data\");\n\t\t\t\t\tif (x != null) {\n\t\t\t\t\t\tList<Element> fields = x.getChildren();\n\t\t\t\t\t\tif (fields != null) {\n\t\t\t\t\t\t\tfor (int i = 0; i < fields.size(); i++) {\n\t\t\t\t\t\t\t\tElement field = fields.get(i);\n\t\t\t\t\t\t\t\tx.removeChild(field);\n\t\t\t\t\t\t\t\tif (TLS_REQUIRED_LABEL.equals(field.getAttributeStaticStr(\"var\"))) {\n\t\t\t\t\t\t\t\t\tfield = new Element(\"field\", new String[]{\"var\", \"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew String[]{\"TLS\", \"fixed\"});\n\t\t\t\t\t\t\t\t\tfield.addChild(new Element(\"value\", XMLUtils.escape(\n\t\t\t\t\t\t\t\t\t\t\t\"This installation forces VHost to require TLS. If you need to use unencrypted connections set 'vhost-tls-required' property to 'false' in the installation configuration file\")));\n\t\t\t\t\t\t\t\t\tx.addChild(field);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tx.addChild(field);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic String[] getAdmins() {\n\t\t\treturn item.getAdmins();\n\t\t}\n\n\t\t@Override\n\t\tpublic void setAdmins(String[] admins) {\n\t\t\tthrow new UnsupportedOperationException(\"This is unmodifiable instance of VHostItem\");\n\t\t}\n\n\t\t@Override\n\t\tpublic String getKey() {\n\t\t\treturn item.getKey();\n\t\t}\n\n\t\t@Override\n\t\tpublic void setKey(String domain) {\n\t\t\tif (!editable) {\n\t\t\t\tthrow new UnsupportedOperationException(\"This is unmodifiable instance of VHostItem\");\n\t\t\t}\n\t\t\titem.setKey(domain);\n\t\t}\n\n\t\t@Override\n\t\tpublic String getOwner() {\n\t\t\treturn item.getOwner();\n\t\t}\n\n\t\t@Override\n\t\tpublic void setOwner(String owner) {\n\t\t\tthrow new UnsupportedOperationException(\"This is unmodifiable instance of VHostItem\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void initFromCommand(Packet packet) {\n\t\t\tif (!editable) {\n\t\t\t\tthrow new UnsupportedOperationException(\"This is unmodifiable instance of VHostItem\");\n\t\t\t}\n\t\t\titem.initFromCommand(packet);\n\t\t\trefresh();\n\t\t}\n\n\t\t@Override\n\t\tpublic void initFromElement(Element elem) {\n\t\t\tif (!editable) {\n\t\t\t\tthrow new UnsupportedOperationException(\"This is unmodifiable instance of VHostItem\");\n\t\t\t}\n\t\t\titem.initFromElement(elem);\n\t\t\trefresh();\n\t\t}\n\n\t\t@Override\n\t\tpublic void initFromPropertyString(String propString) {\n\t\t\tif (!editable) {\n\t\t\t\tthrow new UnsupportedOperationException(\"This is unmodifiable instance of VHostItem\");\n\t\t\t}\n\t\t\titem.initFromPropertyString(propString);\n\t\t\trefresh();\n\t\t}\n\t\t\n\t\t@Override\n\t\tpublic boolean isOwner(String id) {\n\t\t\treturn item.isOwner(id);\n\t\t}\n\n\t\t@Override\n\t\tpublic Element toElement() {\n\t\t\treturn item.toElement();\n\t\t}\n\n\t\t@Override\n\t\tpublic String toPropertyString() {\n\t\t\treturn item.toPropertyString();\n\t\t}\n\n\t\tprotected void readOnly() {\n\t\t\teditable = false;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tStringBuilder sb = new StringBuilder(\"(Wrapped)\");\n\t\t\tsb.append(VHostItemImpl.toString(this));\n\t\t\treturn sb.toString();\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/vhosts/VHostJDBCRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts;\n\nimport tigase.db.DBInitException;\nimport tigase.db.DataSource;\nimport tigase.db.Repository;\nimport tigase.db.comp.UserRepoRepository;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigAlias;\nimport tigase.kernel.beans.config.ConfigAliases;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.util.dns.DNSEntry;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.util.dns.DNSResolverIfc;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.net.UnknownHostException;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\n/**\n * This implementation stores virtual domains in the UserRepository database. It loads initial settings and virtual\n * hosts from the configuration file and then loads more vhosts from the database. Virtual domains from the database can\n * overwrite (disable) vhosts loaded from the configuration file.\n * <br>\n * This implementation keeps all virtual hosts and their parameters in a single database field. This might not be very\n * efficient if you want to manager big number of virtual domains. It is sufficient for hundreds of vhosts. If you need\n * thousands of VHosts support I advice to implement this storage in more efficient way using separate database tables\n * instead of UserRepository. Please note there is a limit of about 300 vhosts if you use Derby database.\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n * @since Nov 29, 2008 2:32:48 PM\n */\n@Repository.Meta(supportedUris = {\".*\"}, isDefault = true)\n@ConfigAliases({@ConfigAlias(field = \"items\", alias = \"virtual-hosts\")})\npublic class VHostJDBCRepository\n\t\textends UserRepoRepository<VHostItem>\n\t\timplements VHostComponentRepositoryDataSourceAware<DataSource> {\n\n\tprivate static final Logger log = Logger.getLogger(VHostJDBCRepository.class.getName());\n\n\t@ConfigField(desc = \"Default IP to which VHost should resolve\", alias = \"dns-def-ip\")\n\tprivate String def_ip_address = null;\n\t@ConfigField(desc = \"Default hostname to which VHost should resolve\", alias = \"dns-srv-def-addr\")\n\tprivate String def_srv_address = null;\n\t@ConfigField(desc = \"Max allowed number of domains per user\", alias = \"domains-per-user-limit\")\n\tprivate int max_domains_per_user = 25;\n\tprivate Map<String, Map<String, Object>> pendingItemsToSet = null;\n\tprivate String[] pendingItemsToSetOld = null;\n\t@ConfigField(desc = \"Main VHost name\", alias = \"default-virtual-host\")\n\tprivate String mainVHostName;\n\t@ConfigField(desc = \"DNS address under which whole installation is accessible (ie. name pointing to all cluster nodes or to the load balancer)\", alias = \"installation-dns-address\")\n\tprivate String installationDnsAddress = null;\n\n\t@Inject\n\tprivate VHostItemExtensionManager extensionManager;\n\t@Inject\n\tprivate VHostItemDefaults vhostDefaultValues;\n\tprivate VHostItem defaultVHost = null;\n\t\n\tpublic VHostJDBCRepository() {\n\t\tDNSResolverIfc resolver = DNSResolverFactory.getInstance();\n\t\tdef_srv_address = resolver.getDefaultHost();\n\t\ttry {\n\t\t\tdef_ip_address = resolver.getHostIP(resolver.getDefaultHost());\n\t\t} catch (Exception e) {\n\t\t\tdef_ip_address = resolver.getDefaultHost();\n\t\t}\n\n\t\tautoReloadInterval = 60;\n\t}\n\n\t@Override\n\tpublic void destroy() {\n\t\t// Nothing to do\n\t}\n\n\t@Override\n\tpublic String getConfigKey() {\n\t\treturn VHostRepoDefaults.getConfigKey();\n\t}\n\n\t@Override\n\tpublic String[] getDefaultPropetyItems() {\n\t\treturn VHostRepoDefaults.getDefaultPropetyItems();\n\t}\n\n\t@Override\n\tpublic VHostItem getItemInstance() {\n\t\tVHostItemImpl item = VHostRepoDefaults.getItemInstance();\n\t\titem.setExtensionManager(extensionManager);\n\t\tVHostItemImpl.VHostItemWrapper wrapper = new VHostItemImpl.VHostItemWrapper();\n\t\twrapper.setItem(item);\n\t\twrapper.setDefaultVHost(defaultVHost);\n\t\twrapper.setVHostDefaults(this.vhostDefaultValues);\n\t\treturn wrapper;\n\t}\n\n\t@Override\n\tpublic String getItemsListPKey() {\n\t\treturn VHostRepoDefaults.getItemsListPKey();\n\t}\n\n\t@Override\n\tpublic String getPropertyKey() {\n\t\treturn VHostRepoDefaults.getPropertyKey();\n\t}\n\n\t@Override\n\tpublic BareJID getRepoUser() {\n\t\treturn VHostRepoDefaults.getRepoUser();\n\t}\n\n\t@Deprecated\n\t@Override\n\tpublic void initRepository(String resource_uri, Map<String, String> params) throws DBInitException {\n\t\t// Nothing to do\n\t}\n\n\t@Override\n\tpublic void reload() {\n\t\tif (vhostDefaultValues == null) {\n\t\t\treturn;\n\t\t}\n\t\tsuper.reload();\n\n\t\tVHostItem defaultVHost = getItem(VHostItem.DEF_VHOST_KEY);\n\t\tif (defaultVHost == null) {\n\t\t\tthis.defaultVHost = new VHostItemImpl(JID.jidInstanceNS(VHostItem.DEF_VHOST_KEY));\n\t\t\t((VHostItemImpl) this.defaultVHost).initializeFromDefaults(vhostDefaultValues);\n\t\t\t((VHostItemImpl) this.defaultVHost).setExtensionManager(extensionManager);\n\t\t\taddItem(this.defaultVHost);\n\t\t} else {\n\t\t\tthis.defaultVHost = defaultVHost;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void addItemNoStore(VHostItem item) {\n\t\tif (item instanceof VHostItemImpl.VHostItemWrapper) {\n\t\t\t((VHostItemImpl.VHostItemWrapper) item).readOnly();\n\t\t\tif (defaultVHost != null) {\n\t\t\t\t((VHostItemImpl.VHostItemWrapper) item).setDefaultVHost(defaultVHost);\n\t\t\t}\n\t\t}\n\t\tsuper.addItemNoStore(item);\n\t\tif (VHostItem.DEF_VHOST_KEY.equals(item.getKey())) {\n\t\t\tthis.defaultVHost = item;\n\t\t\tfor (VHostItem it : allItems()) {\n\t\t\t\tif (it instanceof VHostItemImpl.VHostItemWrapper && it != item) {\n\t\t\t\t\t((VHostItemImpl.VHostItemWrapper) it).setDefaultVHost(item);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void setDef_srv_address(String address) {\n\t\tdef_srv_address = address;\n\t\tif (def_srv_address != null && !def_srv_address.endsWith(\".\")) {\n\t\t\tdef_srv_address = def_srv_address + \".\";\n\t\t}\n\t}\n\n\t@Override\n\tpublic String validateItem(VHostItem item) {\n\t\tif ((item.getVhost() == null) || (item.getVhost().getDomain() == null) ||\n\t\t\t\titem.getVhost().getDomain().isEmpty()) {\n\t\t\treturn \"Domain name not specified\";\n\t\t}\n\n\t\tint vhost_count = 0;\n\n\t\tfor (VHostItem it : allItems()) {\n\t\t\tif (it.isOwner(item.getOwner())) {\n\t\t\t\t++vhost_count;\n\t\t\t}\n\t\t}\n\t\tif (vhost_count >= max_domains_per_user) {\n\t\t\treturn \"Maximum number of domains exceeded for the user! Current number is: \" + vhost_count;\n\t\t}\n\n\t\tif (item.getS2sSecret() == null) {\n\t\t\treturn \"S2S Secret is required\";\n\t\t}\n\n\t\tif (vhostDefaultValues.isTlsRequired() && !item.isTlsRequired()) {\n\t\t\treturn \"This installation forces VHost to require TLS. If you need to use unencrypted connections set 'vhost-tls-required' property to 'false' in the installation configuration file.\";\n\t\t}\n\n\t\tif (!vhostDefaultValues.isCheckDns()) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (installationDnsAddress != null) {\n\t\t\ttry {\n\t\t\t\tString[] installationIpAddresses = DNSResolverFactory.getInstance().getHostIPs(installationDnsAddress);\n\t\t\t\tif (installationIpAddresses == null) {\n\t\t\t\t\treturn \"No DNS settings for load balancer DNS name: \" + installationDnsAddress;\n\t\t\t\t}\n\n\t\t\t\tList<String> installationIpAddressesList = Arrays.asList(installationIpAddresses);\n\n\t\t\t\ttry {\n\t\t\t\t\tDNSEntry[] entries = DNSResolverFactory.getInstance().getHostSRV_Entries(item.getKey());\n\n\t\t\t\t\tif (entries != null) {\n\t\t\t\t\t\tList<String> invalidAddresses = Arrays.stream(entries)\n\t\t\t\t\t\t\t\t.flatMap(dnsEntry -> Arrays.stream(dnsEntry.getIps()))\n\t\t\t\t\t\t\t\t.filter(ip -> !installationDnsAddress.equals(ip) && !installationIpAddressesList.contains(ip)).collect(\n\t\t\t\t\t\t\t\t\t\tCollectors.toList());\n\t\t\t\t\t\tif (invalidAddresses.isEmpty()) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn \"Incorrect DNS SRV settings\" + Arrays.asList(entries) + \", invalid addresses: \" +\n\t\t\t\t\t\t\t\tinvalidAddresses;\n\t\t\t\t\t}\n\t\t\t\t} catch (UnknownHostException ex) {\n\n\t\t\t\t\t// Ignore, maybe simply IP address is set in DNS\n\t\t\t\t}\n\n\t\t\t\t// verify DNS records\n\t\t\t\ttry {\n\t\t\t\t\tString[] ipAddress = DNSResolverFactory.getInstance().getHostIPs(item.getKey());\n\n\t\t\t\t\tif (ipAddress != null) {\n\t\t\t\t\t\tList<String> invalidAddresses = Arrays.stream(ipAddress)\n\t\t\t\t\t\t\t\t.filter(ip -> !installationIpAddressesList.contains(ip))\n\t\t\t\t\t\t\t\t.collect(Collectors.toList());\n\t\t\t\t\t\tif (invalidAddresses.isEmpty()) {\n\t                        return null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn \"Incorrect IP address: '\" + ipAddress +\n\t\t\t\t\t\t\t\t\"' found in DNS for the given host: \" + item.getKey();\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn \"No DNS settings found for given host: \" + item.getKey();\n\t\t\t\t\t}\n\t\t\t\t} catch (UnknownHostException ex1) {\n\t\t\t\t\treturn \"There is no DNS settings for given host: \" + item.getKey();\n\t\t\t\t}\n\n\t\t\t} catch (UnknownHostException ex) {\n\t\t\t\treturn \"There is no DNS settings for load balancer DNS name: \" + installationDnsAddress;\n\t\t\t}\n\t\t}\n\t\t// verify all SRV DNS records\n\t\ttry {\n\t\t\tDNSEntry[] entries = DNSResolverFactory.getInstance().getHostSRV_Entries(item.getKey());\n\n\t\t\tif (entries != null) {\n\t\t\t\tfor (DNSEntry dNSEntry : entries) {\n\t\t\t\t\tlog.finest(\"Validating DNS SRV settings ('\" + dNSEntry + \"') for the given hostname: \" +\n\t\t\t\t\t\t\t\t\t   item.getKey() + \" (defaults: \" + def_ip_address + \", \" + def_srv_address);\n\t\t\t\t\tif (Arrays.asList(dNSEntry.getIps()).contains(def_ip_address) ||\n\t\t\t\t\t\t\tdef_srv_address.equals(dNSEntry.getDnsResultHost())) {\n\n\t\t\t\t\t\t// configuration is OK\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn \"Incorrect DNS SRV settings\" + Arrays.asList(entries);\n\t\t\t}\n\t\t} catch (UnknownHostException ex) {\n\n\t\t\t// Ignore, maybe simply IP address is set in DNS\n\t\t}\n\n\t\t// verify DNS records\n\t\ttry {\n\t\t\tString[] ipAddress = DNSResolverFactory.getInstance().getHostIPs(item.getKey());\n\n\t\t\tif (ipAddress != null) {\n\t\t\t\tif (Arrays.asList(ipAddress).contains(def_ip_address)) {\n\t\t\t\t\treturn null;\n\t\t\t\t} else {\n\t\t\t\t\treturn \"Incorrect IP address: '\" + Arrays.asList(ipAddress) +\n\t\t\t\t\t\t\t\"' found in DNS for the given host: \" + item.getKey();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn \"No DNS settings found for given host: \" + item.getKey();\n\t\t\t}\n\t\t} catch (UnknownHostException ex1) {\n\t\t\treturn \"There is no DNS settings for given host: \" + item.getKey();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setDataSource(DataSource dataSource) {\n\t\t// this is needed as it is required by interface\n\t}\n\n\tpublic String getMainVHostName() {\n\t\treturn mainVHostName;\n\t}\n\n\tpublic void setMainVHostName(String vhost) {\n\t\tthis.mainVHostName = vhost;\n\t\treloadIfReady();\n\t}\n\n\t@Override\n\tpublic VHostItem getDefaultVHostItem() {\n\t\treturn getItem(getMainVHostName());\n\t}\n\n\tpublic void setExtensionManager(VHostItemExtensionManager extensionManager) {\n\t\tthis.extensionManager = extensionManager;\n\t\treloadIfReady();\n\t}\n\n\t@Override\n\tpublic void setItemsOld(String[] items_arr) {\n\t\tif (vhostDefaultValues == null) {\n\t\t\tthis.pendingItemsToSetOld = items_arr;\n\t\t} else {\n\t\t\tsuper.setItemsOld(items_arr);\n\t\t\tsuper.reload();\n\t\t}\n\t}\n\n\tpublic void setVhostDefaultValues(VHostItemDefaults vhostDefaultValues) {\n\t\tthis.vhostDefaultValues = vhostDefaultValues;\n\t\tif (pendingItemsToSetOld != null) {\n\t\t\tsetItemsOld(pendingItemsToSetOld);\n\t\t\tpendingItemsToSetOld = null;\n\t\t}\n\t\tsetMainVHostName(mainVHostName);\n\t}\n\n\tprivate void reloadIfReady() {\n\t\t//called from setters for below members -- to force reload only after everything is initialised\n\t\tif (vhostDefaultValues != null && extensionManager != null) {\n\t\t\treload();\n\t\t\tif (!contains(mainVHostName)) {\n\t\t\t\tVHostItem item = getItemInstance();\n\t\t\t\titem.setKey(mainVHostName);\n\t\t\t\tif (item instanceof VHostItemImpl.VHostItemWrapper) {\n\t\t\t\t\t((VHostItemImpl.VHostItemWrapper) item).readOnly();\n\t\t\t\t}\n\t\t\t\taddItem(item);\n\t\t\t}\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/vhosts/VHostListener.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts;\n\nimport tigase.server.ServerComponent;\n\n/**\n * Interface VHostListener needs to be implemented by every class wanting to accept and process XMPP packets sent to\n * virtual domains. It allows for basic component settings and retrieve information and configuration about virtual\n * domains server by the installation.\n * <br>\n * Created: Fri Nov 21 14:29:49 2008\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface VHostListener\n\t\textends ServerComponent {\n\n\t/**\n\t * Sets the VHostManager which works on this server installation. The VHostManager allows for accessing vritual\n\t * domain data.\n\t *\n\t * @param manager is a reference to VHostManager available on the server installation.\n\t */\n\tvoid setVHostManager(VHostManagerIfc manager);\n\n\t/**\n\t * Indicates whether the component accepts packets to all local domains. The best example would be SM component\n\t * which usually handles all requests sent to any local domain.\n\t *\n\t * @return 'true' if the component accepts packets to local domains 'false' otherwise.\n\t */\n\tboolean handlesLocalDomains();\n\n\t/**\n\t * Indicates whether the component can handle all packets to non-local domains. S2s connection manager component is\n\t * the best example of the component which handles all requests sent to non-local domains.\n\t *\n\t * @return 'true' if the component accepts packets to non-local domains 'false' otherwise.\n\t */\n\tboolean handlesNonLocalDomains();\n\n\t/**\n\t * Indicates whether the component can handle packets to 'name' subdomain. Name subdomain is an artificial domain\n\t * created from the component name and the domain name. The best example would be a 'MUC' component or a 'PubSub'\n\t * component. They are usually named respectively 'muc' and 'pubsub' and they accept requests sent to domains\n\t * 'muc.tigase.org' or 'pubsub.tigase.org', even though a local domain is just 'tigase.org'.\n\t *\n\t * @return 'true' if the component accepts packets to 'name' subdomains 'false' otherwise.\n\t */\n\tboolean handlesNameSubdomains();\n}\n"
  },
  {
    "path": "src/main/java/tigase/vhosts/VHostManager.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts;\n\nimport tigase.db.DBInitException;\nimport tigase.db.DataSource;\nimport tigase.db.DataSourceHelper;\nimport tigase.db.TigaseDBException;\nimport tigase.db.comp.AbstractSDComponentRepositoryBean;\nimport tigase.db.comp.ComponentRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.RegistrarBean;\nimport tigase.kernel.beans.selector.ConfigType;\nimport tigase.kernel.beans.selector.ConfigTypeEnum;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.AbstractComponentRegistrator;\nimport tigase.server.ServerComponent;\nimport tigase.stats.StatisticsContainer;\nimport tigase.stats.StatisticsList;\nimport tigase.util.reflection.ReflectionHelper;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport javax.script.Bindings;\nimport java.lang.reflect.Type;\nimport java.util.ArrayList;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.UUID;\nimport java.util.concurrent.ConcurrentSkipListSet;\nimport java.util.function.Predicate;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\n/**\n * Describe class VHostManager here.\n * <br>\n * Created: Fri Nov 21 14:28:20 2008\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"vhost-man\", parent = Kernel.class, active = true, exportable = true)\n@ConfigType({ConfigTypeEnum.DefaultMode, ConfigTypeEnum.SessionManagerMode, ConfigTypeEnum.ConnectionManagersMode, ConfigTypeEnum.ComponentMode})\npublic class VHostManager\n\t\textends AbstractComponentRegistrator<VHostListener>\n\t\timplements DefaultAwareVHostManagerIfc, StatisticsContainer, RegistrarBean {\n\n\tprivate static final Logger log = Logger.getLogger(VHostManager.class.getName());\n\n\t@Inject\n\tprotected VHostComponentRepository repo = null;\n\tprivate long getComponentsForLocalDomainCalls = 0;\n\tprivate long getComponentsForNonLocalDomainCalls = 0;\n\t// private ServiceEntity serviceEntity = null;\n\tprivate String identity_type = \"generic\";\n\tprivate long isAnonymousEnabledCalls = 0;\n\tprivate long isLocalDomainCalls = 0;\n\tprivate Kernel kernel;\n\tprivate LinkedHashSet<VHostListener> localDomainsHandlers = new LinkedHashSet<VHostListener>(10);\n\tprivate LinkedHashSet<VHostListener> nameSubdomainsHandlers = new LinkedHashSet<VHostListener>(10);\n\tprivate LinkedHashSet<VHostListener> nonLocalDomainsHandlers = new LinkedHashSet<VHostListener>(10);\n\tprivate ConcurrentSkipListSet<String> registeredComponentDomains = new ConcurrentSkipListSet<String>();\n\n\t/**\n\t * Creates a new <code>VHostManager</code> instance.\n\t */\n\tpublic VHostManager() {\n\t}\n\n\t@Override\n\tpublic void addComponentDomain(String domain) {\n\t\tregisteredComponentDomains.add(domain);\n\t}\n\n\t@Override\n\tpublic void componentAdded(VHostListener component) {\n\t\tcomponent.setVHostManager(this);\n\t\tif (component.handlesLocalDomains()) {\n\t\t\tlocalDomainsHandlers.add(component);\n\t\t}\n\t\tif (component.handlesNonLocalDomains()) {\n\t\t\tnonLocalDomainsHandlers.add(component);\n\t\t}\n\t\tif (component.handlesNameSubdomains()) {\n\t\t\tnameSubdomainsHandlers.add(component);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void componentRemoved(VHostListener component) {\n\t\tlocalDomainsHandlers.remove(component);\n\t\tnonLocalDomainsHandlers.remove(component);\n\t\tnameSubdomainsHandlers.remove(component);\n\t}\n\n\t@Override\n\tpublic void initBindings(Bindings binds) {\n\t\tsuper.initBindings(binds);\n\t\tbinds.put(ComponentRepository.COMP_REPO_BIND, repo);\n\t\tbinds.put(\"kernel\", kernel);\n\t}\n\n\t@Override\n\tpublic void removeComponentDomain(String domain) {\n\t\tregisteredComponentDomains.remove(domain);\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\t\tthis.kernel = kernel;\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\n\t}\n\n\t@Override\n\tpublic List<JID> getAllVHosts() {\n\t\treturn getAllVHosts(true);\n\t}\n\n\tpublic List<JID> getAllVHosts(boolean includeDefaultVhost) {\n\t\tList<JID> list = new ArrayList<>();\n\n\t\ttry {\n\t\t\treturn repo.allItems().stream()\n\t\t\t\t\t.filter(vHostItem -> (includeDefaultVhost || !vHostItem.isDefault()))\n\t\t\t\t\t.map(VHostItem::getVhost)\n\t\t\t\t\t.collect(Collectors.toList());\n\t\t} catch (TigaseDBException ex) {\n\t\t\tLogger.getLogger(VHostManager.class.getName()).log(Level.SEVERE, null, ex);\n\t\t}\n\n\t\treturn list;\n\t}\n\n\t@Override\n\tpublic ServerComponent[] getComponentsForLocalDomain(String domain) {\n\t\t++getComponentsForLocalDomainCalls;\n\n\t\tVHostItem vhost = repo.getItem(domain);\n\n\t\tif (vhost == null) {\n\n\t\t\t// This is not a local domain.\n\t\t\t// Maybe this is a 'name' subdomain: 'pubsub'.domain.name\n\t\t\tint idx = domain.indexOf('.');\n\n\t\t\tif (idx > 0) {\n\t\t\t\tString name = domain.substring(0, idx);\n\t\t\t\tString basedomain = domain.substring(idx + 1);\n\t\t\t\tVHostListener listener = components.get(name);\n\n\t\t\t\tif ((listener != null) && listener.handlesNameSubdomains() && isLocalDomain(basedomain)) {\n\t\t\t\t\treturn new ServerComponent[]{listener};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn null;\n\t\t} else {\n\n//    // First check whether the domain has special configuration\n//    // specifying what components are for this domain:\n//    String[] comps = vhost.getComps();\n//    if (comps != null && comps.length > 0) {\n//      !!\n//    }\n\t\t\t// Return all components for local domains and components selected\n\t\t\t// for this specific domain\n\t\t\tLinkedHashSet<ServerComponent> results = new LinkedHashSet<ServerComponent>(10);\n\n\t\t\t// are there any components explicitly bound to this domain?\n\t\t\tString[] comps = vhost.getComps();\n\n\t\t\tif ((comps != null) && (comps.length > 0)) {\n\t\t\t\tfor (String name : comps) {\n\t\t\t\t\tVHostListener listener = components.get(name);\n\n\t\t\t\t\tif (listener != null) {\n\t\t\t\t\t\tresults.add(listener);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// if not, then add any generic handlers\n\t\t\tif (results.size() == 0) {\n\t\t\t\tresults.addAll(localDomainsHandlers);\n\t\t\t}\n\t\t\tif (results.size() > 0) {\n\t\t\t\treturn results.toArray(new ServerComponent[results.size()]);\n\t\t\t} else {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic ServerComponent[] getComponentsForNonLocalDomain(String domain) {\n\t\t++getComponentsForNonLocalDomainCalls;\n\n\t\t// Return components for non-local domains\n\t\tif (nonLocalDomainsHandlers.size() > 0) {\n\t\t\treturn nonLocalDomainsHandlers.toArray(new ServerComponent[nonLocalDomainsHandlers.size()]);\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic BareJID getDefVHostItem() {\n\t\treturn repo.getDefaultVHostItem().getVhost().getBareJID();\n\t}\n\n\t@Override\n\tpublic String getDiscoCategoryType() {\n\t\treturn identity_type;\n\t}\n\n\t@Override\n\tpublic String getDiscoDescription() {\n\t\treturn \"VHost Manager\";\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t\tlist.add(getName(), \"Number of VHosts\", repo.size(), Level.FINE);\n\t\tlist.add(getName(), \"Checks: is local domain\", isLocalDomainCalls, Level.FINER);\n\t\tlist.add(getName(), \"Checks: is anonymous domain\", isAnonymousEnabledCalls, Level.FINER);\n\t\tlist.add(getName(), \"Get components for local domain\", getComponentsForLocalDomainCalls, Level.FINER);\n\t\tlist.add(getName(), \"Get components for non-local domain\", getComponentsForNonLocalDomainCalls, Level.FINER);\n\t}\n\n\t@Override\n\tpublic VHostItem getVHostItem(String domain) {\n\t\treturn repo.getItem(domain);\n\t}\n\n\t@Override\n\tpublic VHostItem getVHostItemDomainOrComponent(String domain) {\n\t\tVHostItem item = getVHostItem(domain);\n\t\tif (item == null) {\n\t\t\tint idx = domain.indexOf('.');\n\n\t\t\tif (idx > 0) {\n\t\t\t\tString name = domain.substring(0, idx);\n\t\t\t\tString basedomain = domain.substring(idx + 1);\n\t\t\t\tVHostListener listener = components.get(name);\n\t\t\t\tif (listener != null && listener.handlesNameSubdomains()) {\n\t\t\t\t\titem = getVHostItem(basedomain);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn item;\n\t}\n\n\t@Override\n\tpublic boolean isAnonymousEnabled(String domain) {\n\t\t++isAnonymousEnabledCalls;\n\n\t\tVHostItem vhost = repo.getItem(domain);\n\n\t\tif (vhost == null) {\n\t\t\treturn false;\n\t\t} else {\n\t\t\treturn vhost.isAnonymousEnabled();\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isCorrectType(ServerComponent component) {\n\t\treturn component instanceof VHostListener;\n\t}\n\n\t@Override\n\tpublic boolean isLocalDomain(String domain) {\n\t\t++isLocalDomainCalls;\n\n\t\treturn repo.contains(domain);\n\t}\n\n\t@Override\n\tpublic boolean isLocalDomainOrComponent(String domain) {\n\t\tboolean result = isLocalDomain(domain);\n\n\t\tif (!result) {\n\t\t\tresult = registeredComponentDomains.contains(domain);\n\t\t}\n\t\tif (!result) {\n\t\t\tint idx = domain.indexOf('.');\n\n\t\t\tif (idx > 0) {\n\t\t\t\tString name = domain.substring(0, idx);\n\t\t\t\tString basedomain = domain.substring(idx + 1);\n\t\t\t\tVHostListener listener = components.get(name);\n\n\t\t\t\tresult = ((listener != null) && listener.handlesNameSubdomains() && isLocalDomain(basedomain));\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic void setName(String name) {\n\t\tsuper.setName(name);\n\t}\n\n\tpublic void initializeRepository() throws TigaseDBException {\n\t\t// loading all items\n\t\trepo.reload();\n\n//\t\tList<VHostItem> items = new ArrayList<VHostItem>(repo.allItems());\n//\t\tfor (VHostItem item : items) {\n//\t\t\t// if there is no S2S secret set for vhost, then we need to generate it\n//\t\t\tif (item.getS2sSecret() == null) {\n//\t\t\t\tString secret = generateSecret();\n//\t\t\t\titem.setS2sSecret(secret);\n//\t\t\t\trepo.addItem(item);\n//\t\t\t}\n//\t\t}\n\t}\n\n\tpublic String generateSecret() {\n\t\tString random = UUID.randomUUID().toString();\n\t\treturn random;\n\t}\n\n\tpublic ComponentRepository<VHostItem> getComponentRepository() {\n\t\treturn repo;\n\t}\n\n\t@Bean(name = \"vhostRepository\", parent = VHostManager.class, active = true)\n\tpublic static class DefVHostRepositoryBean\n\t\t\textends AbstractSDComponentRepositoryBean<VHostItem> implements VHostComponentRepository {\n\n\t\tprivate static DataSourceHelper.Matcher matcher = (Class clazz) -> {\n\t\t\treturn ReflectionHelper.classMatchesClassWithParameters(clazz, VHostComponentRepositoryDataSourceAware.class,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew Type[]{DataSource.class});\n\t\t};\n\n\t\t@Override\n\t\tpublic VHostItem getDefaultVHostItem() {\n\t\t\treturn ((VHostComponentRepository) getRepository()).getDefaultVHostItem();\n\t\t}\n\n\t\t@Override\n\t\tprotected Class<? extends VHostComponentRepositoryDataSourceAware<DataSource>> findClassForDataSource(\n\t\t\t\tDataSource dataSource) throws DBInitException {\n\t\t\tClass cls = DataSourceHelper.getDefaultClass(VHostComponentRepository.class, dataSource.getResourceUri(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t matcher);\n\t\t\treturn (Class<VHostComponentRepositoryDataSourceAware<DataSource>>) cls;\n\t\t}\n\n\t}\n\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/vhosts/VHostManagerIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts;\n\nimport tigase.server.ServerComponent;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.List;\n\n/**\n * This is VHostManagerIfc interface which allows to access data for virtual domains server by this installation. There\n * can be normally only one instance of this interface implementation loaded on the server at any given time. The\n * instance is responsible for managing all virtual hosts and assigning correct component to each of the virtual hosts\n * or non-local domains.\n * <br>\n * Created: 22 Nov 2008\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface VHostManagerIfc {\n\n\t/**\n\t * This method checks whether given domain is server by this server instance. That is if this domain is local to\n\t * this server installation. It doesn't check however whether the domain is disabled or enabled. It only checks if\n\t * tthe list of local domains contains this virtual host.\n\t *\n\t * @param domain is a String with domain name to check.\n\t *\n\t * @return a boolean value indicating whether given domain is local or not.\n\t */\n\tboolean isLocalDomain(String domain);\n\n\t/**\n\t * Method check if domains is locally hosted as a vhost or a component of a vhost.\n\t * @return true - if domain is locally hosted as a vhost of a component of a vhost.\n\t */\n\tboolean isLocalDomainOrComponent(String domain);\n\n\t/**\n\t * This method checks whether anonymous login is enabled for a given domain. That is it checks whether this domains\n\t * is local and anonymousEnabled parameter for this domain is set to true.\n\t *\n\t * @param domain is a String with domain name to check.\n\t *\n\t * @return a boolean value indicating whether given domain is enabled for anonymous logins or not.\n\t */\n\tboolean isAnonymousEnabled(String domain);\n\n\t/**\n\t * The method returns an array with server components which can process packets for a given local domain. If the\n\t * domain is not local <code>null</code> is returned. The given domain may also consist of: component name\n\t * <code>ServerComponent.getName()</code> plus any local domain but only if the component returns <code>true</code>\n\t * from method call: <code>VHostListener.handlesNameSubdomains()</code>\n\t *\n\t * @param domain is a <code>String</code> with a domain name to check. It may by just a local domain or string\n\t * created with component name and localdomain.\n\t *\n\t * @return an array with ServerComponents which can handle packets for a given domain or <code>null</code> if no\n\t * component found for a given domain.\n\t */\n\tServerComponent[] getComponentsForLocalDomain(String domain);\n\n\t/**\n\t * The method returns an array of server components which can process packets sent to non-local domain. Most\n\t * commonly there is only one such component: server-2-server connections manager. It is possible however there\n\t * might be more such components. All of them will get the packet for processing.\n\t *\n\t * @param domain is a <code>String</code> with a domain to check. At the moment this parameter is ignored. In the\n\t * future it will be possible to assign a specific component for any non-local domain.\n\t *\n\t * @return an array with ServerComponents which can handle packets to non-local domains.\n\t */\n\tServerComponent[] getComponentsForNonLocalDomain(String domain);\n\n\t/**\n\t * Returns an object with all domain properties for given domain.\n\t *\n\t * @param domain is a domain name\n\t *\n\t * @return a VHostItem object with all domain properties.\n\t */\n\tVHostItem getVHostItem(String domain);\n\n\t/**\n\t * Returns an object with all domain properties for a given domain or base domain if passed domain is name of\n\t * subdomain used by  component.\n\t *\n\t * @param domain is a domain name\n\t *\n\t * @return a VHostItem object with all domain properties.\n\t */\n\tVHostItem getVHostItemDomainOrComponent(String domain);\n\n\t/**\n\t * Adds a component domain to the collection of local component domains. This is mainly needed/used by an external\n\t * components connecting to the server and binding hostnames. Normally the s2s component have no way of knowing\n\t * about this new and temporary domains handled by the server and would refuse all connections for these domains.\n\t * Adding them to a collection of component domains allows the s2s to detect them and accept connection for them.\n\t *\n\t * @param domain is a component domain name added to the collection.\n\t */\n\tvoid addComponentDomain(String domain);\n\n\t/**\n\t * Removes a domain previously registered by a component. It should not be normally used.\n\t *\n\t * @param domain is a component domain name being removed from the collection.\n\t */\n\tvoid removeComponentDomain(String domain);\n\n\t/**\n\t * Method <code>getDefVHostItem</code> returns a default VHost for the installation. In most cases this is the first\n\t * VHost defined in the server configuration.\n\t *\n\t * @return a <code>BareJID</code> value of the default VHost for the installation.\n\t */\n\tBareJID getDefVHostItem();\n\n\t/**\n\t * Method returns a list of all hosted VHosts.\n\t */\n\tList<JID> getAllVHosts();\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/vhosts/VHostRepoDefaults.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts;\n\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.xmpp.jid.BareJID;\n\nimport static tigase.conf.Configurable.GEN_VIRT_HOSTS;\nimport static tigase.conf.Configurable.HOSTNAMES_PROP_KEY;\n\n/**\n * Created: Oct 3, 2009 4:26:09 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic abstract class VHostRepoDefaults {\n\n\tprivate static final BareJID vhost_user = BareJID.bareJIDInstanceNS(\"vhost-manager\");\n\tprivate static final String vhost_list_pkey = \"vhosts-lists\";\n\n\tpublic static String getConfigKey() {\n\t\treturn HOSTNAMES_PROP_KEY;\n\t}\n\n\tpublic static String[] getDefaultPropetyItems() {\n\t\treturn DNSResolverFactory.getInstance().getDefaultHosts();\n\t}\n\n\tpublic static VHostItemImpl getItemInstance() {\n\t\treturn new VHostItemImpl();\n\t}\n\n\tpublic static String getItemsListPKey() {\n\t\treturn vhost_list_pkey;\n\t}\n\n\tpublic static String getPropertyKey() {\n\t\treturn GEN_VIRT_HOSTS;\n\t}\n\n\tpublic static BareJID getRepoUser() {\n\t\treturn vhost_user;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/vhosts/filter/CustomDomainFilter.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts.filter;\n\nimport tigase.vhosts.filter.Rule.RuleType;\nimport tigase.xmpp.jid.JID;\n\nimport java.text.ParseException;\nimport java.util.Arrays;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * @author Wojtek\n */\npublic final class CustomDomainFilter {\n\n\tprivate static final Logger log = Logger.getLogger(CustomDomainFilter.class.getName());\n\n\tpublic static boolean isAllowed(JID source, JID destination, String rules) {\n\t\ttry {\n\t\t\tSet<Rule> parseRules = parseRules(rules);\n\t\t\tif (parseRules != null) {\n\t\t\t\treturn isAllowed(source, destination, parseRules);\n\t\t\t} else {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (ParseException e) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tpublic static boolean isAllowed(JID source, JID destination, String[] rules) {\n\t\tSet<Rule> parseRules = null;\n\t\ttry {\n\t\t\tparseRules = parseRules(rules);\n\t\t} catch (ParseException e) {\n\t\t\tif (log.isLoggable(Level.WARNING)) {\n\t\t\t\tlog.log(Level.WARNING, \"Error while parsing rules: \" + Arrays.toString(rules), e);\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\tif (parseRules != null) {\n\t\t\treturn isAllowed(source, destination, parseRules);\n\t\t} else {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tpublic static boolean isAllowed(JID source, JID destination, Set<Rule> rules) {\n\t\tif (rules != null) {\n\t\t\tfor (Rule rule : rules) {\n\t\t\t\tlog.log(Level.FINEST, \"Processing source: {0}, destination: {1}, against rule: {2}\",\n\t\t\t\t\t\tnew Object[]{source, destination, rule});\n\t\t\t\tif (rule.isMatched(source, destination)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Matched source: {0}, destination: {1}, allowed: {2}\",\n\t\t\t\t\t\t\tnew Object[]{source, destination, rule.isAllowed()});\n\t\t\t\t\treturn rule.isAllowed();\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\treturn true;\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic static Set<Rule> parseRules(String[] rules) throws ParseException {\n\n\t\tSet rulesSet = new TreeSet<Rule>();\n\t\tfor (String rule : rules) {\n\n\t\t\tString[] split = rule.split(\"\\\\|\");\n\t\t\tif (split != null && (split.length == 3 || split.length == 4)) {\n\n\t\t\t\ttry {\n\t\t\t\t\tInteger id = Integer.valueOf(split[0]);\n\t\t\t\t\tboolean allow = false;\n\t\t\t\t\tif (split[1].equalsIgnoreCase(\"allow\")) {\n\t\t\t\t\t\tallow = true;\n\t\t\t\t\t} else if (split[1].equalsIgnoreCase(\"deny\")) {\n\t\t\t\t\t\tallow = false;\n\t\t\t\t\t}\n\t\t\t\t\tRuleType type = RuleType.valueOf(split[2].toLowerCase());\n\n\t\t\t\t\tJID jid = null;\n\n\t\t\t\t\tif (split.length == 4) {\n\t\t\t\t\t\tjid = JID.jidInstance(split[3]);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (type == RuleType.jid && jid == null) {\n\t\t\t\t\t\tthrow new ParseException(\"Error while parsing rule (no value for JID provided): \" + rule, 0);\n\t\t\t\t\t}\n\n\t\t\t\t\trulesSet.add(new Rule(id, allow, type, jid));\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Error while parsing rule: \" + rule, ex);\n\t\t\t\t\tthrow new ParseException(\"Error while parsing rule: \" + rule, 0);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tlog.log(Level.FINEST, \"Error while parsing rule (wrong number of parameters): \" + rule);\n\t\t\t\tthrow new ParseException(\"Error while parsing rule: \" + rule, 0);\n\t\t\t}\n\t\t}\n\n\t\treturn rulesSet;\n\t}\n\n\tpublic static Set<Rule> parseRules(String rules) throws ParseException {\n\t\tString[] rulesArr = rules.split(\";\");\n\t\tif (rulesArr != null) {\n\t\t\treturn parseRules(rulesArr);\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate CustomDomainFilter() {\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/vhosts/filter/DomainFilterPolicy.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts.filter;\n\nimport java.util.HashSet;\n\n/**\n * Enumeration of all possible filtering modes\n */\npublic enum DomainFilterPolicy {\n\n\t/** user can communicate with anyone */\n\tALL(false),\n\n\t/**\n\t * user can communicate with other local users (i.e. of the domains hosted on the same Tigase instance)\n\t */\n\tLOCAL(false),\n\n\t/** user can communicate with other users of the same domain */\n\tOWN(false),\n\n\t/** user can communicate with users of the domains within listen domains */\n\tLIST(true),\n\n\t/** user can communicate with anyone except of the users within listed domains */\n\tBLACKLIST(true),\n\n\t/**\n\t * Custom rules defining communication policies in CSV (using semicolon) in the format of {@code\n\t * rule_number;(allow|deny);[type_of_value];[value]} where {@code type_of_value::(jid)}\n\t * <pre>\n\t * 1|allow|self;\n\t * 2|allow|jid|admin@test2.com;\n\t * 3|allow|jid|pubsub@test.com;\n\t * 4|deny|all;\n\t * </pre>\n\t */\n\tCUSTOM(true),\n\n\t/** user can not communicate with anyone, account virtually disabled */\n\tBLOCK(false);\n\n\tprivate static HashSet<String> valuesDomainsListStr = null;\n\tprivate static String[] valuesStr = null;\n\tboolean domainListRequired;\n\n\tpublic static HashSet<String> valuePoliciesWithDomainListStr() {\n\t\tif (valuesDomainsListStr == null) {\n\t\t\tDomainFilterPolicy[] vals = values();\n\n\t\t\tvaluesDomainsListStr = new HashSet<>();\n\t\t\tfor (DomainFilterPolicy val : vals) {\n\t\t\t\tif (val.isDomainListRequired()) {\n\t\t\t\t\tvaluesDomainsListStr.add(val.name());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn valuesDomainsListStr;\n\t}\n\n\t/**\n\t * Helper method returning proper defaults in case parsed value doesn't correspond to any of the available modes.\n\t *\n\t * @param domainFilteringPolicy policy name as string\n\t *\n\t * @return enum value of corresponding mode, ALL if value doesn't match any mode or null for null parameter.\n\t */\n\tpublic static DomainFilterPolicy valueof(String domainFilteringPolicy) {\n\t\tif (domainFilteringPolicy == null) {\n\t\t\treturn null;\n\t\t}\n\t\ttry {\n\t\t\treturn DomainFilterPolicy.valueOf(domainFilteringPolicy);\n\t\t} catch (Exception e) {\n\t\t\treturn ALL;\n\t\t}    // end of try-catch\n\t}\n\n\t/**\n\t * Helper method to generate string array with mode values\n\t *\n\t * @return String array with mode values\n\t */\n\tpublic static String[] valuesStr() {\n\t\tif (valuesStr == null) {\n\t\t\tDomainFilterPolicy[] vals = values();\n\n\t\t\tvaluesStr = new String[vals.length];\n\t\t\tfor (int i = 0; i < vals.length; i++) {\n\t\t\t\tvaluesStr[i] = vals[i].name();\n\t\t\t}\n\t\t}\n\n\t\treturn valuesStr;\n\t}\n\n\tDomainFilterPolicy(boolean domainListRequired) {\n\t\tthis.domainListRequired = domainListRequired;\n\t}\n\n\tpublic boolean isDomainListRequired() {\n\t\treturn domainListRequired;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/vhosts/filter/Rule.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts.filter;\n\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Objects;\n\n/**\n * @author Wojtek\n */\npublic class Rule\n\t\timplements Comparable<Rule> {\n\n\tpublic static enum RuleType {\n\n\t\tjid,\n\t\tself,\n\t\tdomain,\n\t\tall\n\t}\n\tboolean allow;\n\tint id;\n\tRuleType type;\n\tJID value;\n\n\tpublic Rule(int id, boolean allow, RuleType type, JID value) {\n\t\tthis.id = id;\n\t\tthis.allow = allow;\n\t\tthis.type = type;\n\t\tthis.value = value;\n\t}\n\n\t@Override\n\tpublic int compareTo(Rule o) {\n\t\treturn id > o.getId() ? +1 : id < o.getId() ? -1 : 0;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (obj == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tfinal Rule other = (Rule) obj;\n\t\tif (this.id != other.id) {\n\t\t\treturn false;\n\t\t}\n\t\tif (this.allow != other.allow) {\n\t\t\treturn false;\n\t\t}\n\t\tif (this.type != other.type) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!Objects.equals(this.value, other.value)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic boolean isAllowed() {\n\t\treturn allow;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint hash = 5;\n\t\thash = 11 * hash + this.id;\n\t\thash = 11 * hash + (this.allow ? 1 : 0);\n\t\thash = 11 * hash + Objects.hashCode(this.type);\n\t\tif (value != null) {\n\t\t\thash = 11 * hash + value.toString().hashCode();\n\t\t}\n\t\treturn hash;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Rule{\" + \"id=\" + id + \", allow=\" + allow + \", type=\" + type + \", value=\" + value + '}';\n\t}\n\n\tpublic String toConfigurationString() {\n\t\treturn id + \"|\" + (allow ? \"allow\" : \"deny\") + \"|\" + type +\n\t\t\t\t(type == RuleType.domain || type == RuleType.jid ? \"|\" + value : \"\") + \";\";\n\t}\n\n\tboolean isMatched(JID source, JID destination) {\n\t\tboolean result = false;\n\t\tswitch (type) {\n\t\t\tcase all:\n\t\t\t\tresult = true;\n\t\t\t\tbreak;\n\t\t\tcase self:\n\t\t\t\tif (source != null && destination != null) {\n\t\t\t\t\tresult = source.getBareJID().equals(destination.getBareJID());\n\t\t\t\t} else if (source == null) {\n\t\t\t\t\tresult = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase domain:\n\t\t\t\tif (value != null && destination != null) {\n\t\t\t\t\tresult = value.getDomain().equals(destination.getDomain());\n\t\t\t\t}\n\t\t\t\tif (value != null && destination != null) {\n\t\t\t\t\tresult |= value.getDomain().equals(source.getDomain());\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase jid:\n\t\t\t\tif (value != null && destination != null) {\n\t\t\t\t\tresult = value.getBareJID().equals(destination.getBareJID());\n\t\t\t\t}\n\t\t\t\tif (value != null && destination != null) {\n\t\t\t\t\tresult |= value.getBareJID().equals(source.getBareJID());\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprotected int getId() {\n\t\treturn id;\n\t}\n\n\t;\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/Authorization.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport tigase.server.Packet;\n\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * <code>Authorization</code> enumeration type defines authorization error codes. It has also capability to build error\n * response message relevant to specific error code (or success code). It is used not only for authorization process but\n * also by other features implementation accessing session data.<br> All defined errors comes directly from\n * <em>XMPP</em> core RFC. For each error has assigned error code - from old <em>Jabber</em> spec. and error condition -\n * <em>XMPP</em> error spec.\n * <br>\n * <p> Created: Thu Oct 14 22:19:11 2004 </p>\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic enum Authorization {\n\tAUTHORIZED {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 0;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic Packet getResponseMessage(Packet packet, String text, boolean includeOriginalXML)\n\t\t\t\tthrows PacketErrorTypeException {\n\t\t\tlog.log(System.Logger.Level.TRACE,\n\t\t\t\t\t() -> \"Generating response message with text: \" + text + \", including original XML: \" +\n\t\t\t\t\t\t\tincludeOriginalXML + \", using type: \" + this + \"/\" + getCondition() + \", for packet from \" +\n\t\t\t\t\t\t\tpacket.getFrom() + \", packet: \" + packet);\n\t\t\tif (packet.getFrom() == null) {\n\t\t\t\tthrow new PacketInvalidAddressException(\n\t\t\t\t\t\t\"The packet does not have 'from' address, can't generate response: \" + packet.toString());\n\t\t\t}\n\t\t\treturn packet.okResult(text, 0);\n\t\t}\n\t},\n\tBAD_REQUEST {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn \"bad-request\";\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 400;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn ERR_TYPE_MODIFY;\n\t\t}\n\t},\n\tCONFLICT {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn \"conflict\";\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 409;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn ERR_TYPE_CANCEL;\n\t\t}\n\t},\n\tFEATURE_NOT_IMPLEMENTED {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn \"feature-not-implemented\";\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 501;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn ERR_TYPE_CANCEL;\n\t\t}\n\t},\n\tFORBIDDEN {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn \"forbidden\";\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 403;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn ERR_TYPE_AUTH;\n\t\t}\n\t},\n\tGONE {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn \"gone\";\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 302;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn ERR_TYPE_MODIFY;\n\t\t}\n\t},\n\tINTERNAL_SERVER_ERROR {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn \"internal-server-error\";\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 500;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn ERR_TYPE_WAIT;\n\t\t}\n\t},\n\tITEM_NOT_FOUND {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn \"item-not-found\";\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 404;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn ERR_TYPE_CANCEL;\n\t\t}\n\t},\n\tJID_MALFORMED {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn \"jid-malformed\";\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 400;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn ERR_TYPE_MODIFY;\n\t\t}\n\t},\n\tNOT_ACCEPTABLE {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn \"not-acceptable\";\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 406;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn ERR_TYPE_MODIFY;\n\t\t}\n\t},\n\tNOT_ALLOWED {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn \"not-allowed\";\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 405;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn ERR_TYPE_CANCEL;\n\t\t}\n\t},\n\tNOT_AUTHORIZED {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn \"not-authorized\";\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 401;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn ERR_TYPE_AUTH;\n\t\t}\n\t},\n\tPAYMENT_REQUIRED {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn \"payment-required\";\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 402;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn ERR_TYPE_AUTH;\n\t\t}\n\t},\n\tPOLICY_VIOLATION {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn \"policy-violation\";\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 0;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn ERR_TYPE_CANCEL;\n\t\t}\n\t},\n\tRECIPIENT_UNAVAILABLE {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn \"recipient-unavailable\";\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 404;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn ERR_TYPE_WAIT;\n\t\t}\n\t},\n\tREDIRECT {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn \"redirect\";\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 302;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn ERR_TYPE_MODIFY;\n\t\t}\n\t},\n\tREGISTRATION_REQUIRED {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn \"registration-required\";\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 407;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn ERR_TYPE_AUTH;\n\t\t}\n\t},\n\tREMOTE_SERVER_NOT_FOUND {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn \"remote-server-not-found\";\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 404;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn ERR_TYPE_CANCEL;\n\t\t}\n\t},\n\tREMOTE_SERVER_TIMEOUT {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn \"remote-server-timeout\";\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 504;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn ERR_TYPE_WAIT;\n\t\t}\n\t},\n\tRESOURCE_CONSTRAINT {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn \"resource-constraint\";\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 500;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn ERR_TYPE_WAIT;\n\t\t}\n\t},\n\tSERVICE_UNAVAILABLE {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn \"service-unavailable\";\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 503;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn ERR_TYPE_CANCEL;\n\t\t}\n\t},\n\tSUBSCRIPTION_REQUIRED {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn \"subscription-required\";\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 407;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn ERR_TYPE_AUTH;\n\t\t}\n\t},\n\tUNDEFINED_CONDITION {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn \"undefined-condition\";\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 500;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn \"[undefined]\";\n\t\t}\n\t},\n\tUNEXPECTED_REQUEST {\n\t\t@Override\n\t\tpublic String getCondition() {\n\t\t\treturn \"unexpected-request\";\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCode() {\n\t\t\treturn 400;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getErrorType() {\n\t\t\treturn ERR_TYPE_WAIT;\n\t\t}\n\t};\n\n\tprotected static final String ERR_TYPE_AUTH = \"auth\";\n\tprotected static final String ERR_TYPE_CANCEL = \"cancel\";\n\tprotected static final String ERR_TYPE_MODIFY = \"modify\";\n\tprotected static final String ERR_TYPE_WAIT = \"wait\";\n\tprivate final static System.Logger log = System.getLogger(Authorization.class.getName());\n\n\tprivate static final Map<String, Authorization> BY_CONDITION = new ConcurrentHashMap<String, Authorization>();\n\n\tstatic {\n\t\tfor (Authorization v : values()) {\n\t\t\tif (v.getCondition() == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tBY_CONDITION.put(v.getCondition(), v);\n\t\t}\n\t}\n\n\tpublic static Authorization getByCondition(String condition) {\n\t\tif (condition == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn BY_CONDITION.get(condition);\n\t}\n\n\tpublic abstract String getCondition();\n\n\tpublic abstract int getErrorCode();\n\n\tpublic abstract String getErrorType();\n\n\t/**\n\t * Utility method for generating an error response for a stanza (enclosed by the given <code>Packet</code>). In some\n\t * cases it may happen that the error packet is bounced back to the sender which triggers another attempt to\n\t * generate an error response for the error packet. This may lead to an infinite loop inside the component (Tigase\n\t * server) eating up CPU and slowing everything down. To prevent this the method detects the <em>error</em> stanza\n\t * typy and generates an exception.\n\t *\n\t * @param packet is the packet for which the error response is generated.\n\t * @param text is an error human readable text message.\n\t * @param includeOriginalXML is a boolean value indicating whether the original content of the stanza (children of\n\t * the top level element) have to be included in the error stanza.\n\t *\n\t * @return a new <code>Packet</code> instance with an error response for a given packet.\n\t *\n\t * @throws PacketErrorTypeException if the packet given as a parameter encloses an error stanza already.\n\t */\n\tpublic Packet getResponseMessage(Packet packet, String text, boolean includeOriginalXML)\n\t\t\tthrows PacketErrorTypeException {\n\t\tlog.log(System.Logger.Level.TRACE,\n\t\t\t\t() -> \"Generating response message with text: \" + text + \", including original XML: \" +\n\t\t\t\t\t\tincludeOriginalXML + \", using error type: \" + this + \"/\" + getCondition() + \"/\" +\n\t\t\t\t\t\tgetErrorType() + \", for packet from \" + packet.getFrom() + \", packet: \" + packet);\n\t\tif (packet.getFrom() == null) {\n\t\t\tthrow new PacketInvalidAddressException(\"The packet does not have 'from' address, can't generate error response: \" + packet.toString());\n\t\t}\n\t\tif ((packet.getType() == null) || (packet.getType() != StanzaType.error)) {\n\t\t\treturn packet.errorResult(getErrorType(), getErrorCode(), getCondition(), text, includeOriginalXML);\n\t\t} else {\n\t\t\tthrow new PacketInvalidTypeException(\"The packet has already 'error' type: \" + packet.toString());\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/ElementMatcher.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport tigase.server.Packet;\nimport tigase.xml.Element;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author andrzej\n */\npublic class ElementMatcher {\n\n\tprivate final String[] path;\n\tprivate final boolean value;\n\tprivate final String xmlns;\n\tprivate final String[][] attributes;\n\tprivate final boolean checkChildren;\n\n\tpublic static ElementMatcher create(String str) {\n\t\tList<String> path = new ArrayList<String>();\n\t\tString xmlns = null;\n\t\tList<String[]> attributes = new ArrayList<>();\n\t\tboolean checkChildren = false;\n\t\tint offset = 0;\n\t\tboolean value = !str.startsWith(\"-\");\n\t\tif (str.charAt(0) == '-' || str.charAt(0) == '+') {\n\t\t\tstr = str.substring(1);\n\t\t}\n\t\twhile (true) {\n\t\t\tString elemName = null;\n\n\t\t\tint slashIdx = str.indexOf('/', offset);\n\t\t\tint sIdx = str.indexOf('[', offset);\n\t\t\tif (slashIdx < 0) {\n\t\t\t\tslashIdx = str.length();\n\t\t\t}\n\n\t\t\tElement c = null;\n\t\t\tif (slashIdx < sIdx || sIdx < 0) {\n\t\t\t\telemName = str.substring(offset, slashIdx);\n\t\t\t\txmlns = null;\n\t\t\t} else {\n\t\t\t\tint eIdx = str.indexOf(']', sIdx);\n\t\t\t\telemName = str.substring(offset, sIdx);\n\t\t\t\tString content = str.substring(sIdx + 1, eIdx);\n\t\t\t\tString[] parts = content.split(\",\");\n\t\t\t\tfor (String part : parts) {\n\t\t\t\t\tint equalIdx = part.indexOf('=');\n\t\t\t\t\tif (equalIdx > 0) {\n\t\t\t\t\t\tattributes.add(new String[]{part.substring(0, equalIdx).intern(), part.substring(equalIdx + 1)});\n\t\t\t\t\t} else {\n\t\t\t\t\t\txmlns = part;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tslashIdx = str.indexOf('/', eIdx);\n\t\t\t\tif (slashIdx < 0) {\n\t\t\t\t\tslashIdx = str.length();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (elemName != null && !elemName.isEmpty()) {\n\t\t\t\tif (\"*\".equals(elemName)) {\n\t\t\t\t\tcheckChildren = true;\n\t\t\t\t} else {\n\t\t\t\t\tpath.add(elemName.intern());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (slashIdx == str.length()) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\toffset = slashIdx + 1;\n\t\t}\n\t\tif (xmlns != null) {\n\t\t\txmlns = xmlns.intern();\n\t\t}\n\n\t\treturn new ElementMatcher(path.toArray(new String[0]), checkChildren, xmlns, attributes.isEmpty() ? null : attributes.stream().toArray(String[][]::new), value);\n\t}\n\n\tpublic ElementMatcher(String[] path, String xmlns, boolean value) {\n\t\tthis(path, false, xmlns, null, value);\n\t}\n\n\tpublic ElementMatcher(String[] path, boolean checkChildren, String xmlns, String[][] attributes, boolean value) {\n\t\tthis.path = path;\n\t\tthis.xmlns = xmlns;\n\t\tthis.attributes = attributes;\n\t\tthis.value = value;\n\t\tthis.checkChildren = checkChildren;\n\t}\n\n\tpublic boolean matches(Packet packet) {\n\t\tElement child = path.length == 0 ? packet.getElement() : packet.getElement().findChildStaticStr(path);\n\t\tif (checkChildren) {\n\t\t\tif (child == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tList<Element> children = child.getChildren();\n\t\t\tif (children == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tfor (Element el : children) {\n\t\t\t\tif (matches(el)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t} else {\n\t\t\treturn matches(child);\n\t\t}\n\t}\n\n\tprotected boolean matches(Element child) {\n\t\tif (child == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (xmlns != null && xmlns != child.getXMLNS()) {\n\t\t\treturn false;\n\t\t}\n\t\tif (attributes != null) {\n\t\t\tfor (String[] pair : attributes) {\n\t\t\t\tif (!pair[1].equals(child.getAttributeStaticStr(pair[0]))) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic boolean getValue() {\n\t\treturn value;\n\t}\n\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tif (!value) {\n\t\t\tsb.append('-');\n\t\t}\n\t\tfor (String p : path) {\n\t\t\tsb.append('/');\n\t\t\tsb.append(p);\n\t\t}\n\n\t\tif (checkChildren) {\n\t\t\tsb.append(\"/*\");\n\t\t}\n\n\t\tif (xmlns != null || attributes != null) {\n\t\t\tsb.append(\"[\");\n\t\t\tif (xmlns != null) {\n\t\t\t\tsb.append(xmlns);\n\t\t\t}\n\t\t\tif (attributes != null && attributes.length > 0) {\n\t\t\t\tboolean first = xmlns == null;\n\t\t\t\tfor (String[] pair : attributes) {\n\t\t\t\t\tif (!first) {\n\t\t\t\t\t\tsb.append(\",\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfirst = false;\n\t\t\t\t\t}\n\t\t\t\t\tsb.append(pair[0]).append(\"=\").append(pair[1]);\n\t\t\t\t}\n\t\t\t}\n\t\t\tsb.append(\"]\");\n\t\t}\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/InvalidPacketException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.xmpp;\n\npublic class InvalidPacketException extends PacketErrorTypeException {\n\n\tpublic InvalidPacketException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic InvalidPacketException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/NoConnectionIdException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\n/**\n * @author kobit\n */\npublic class NoConnectionIdException\n\t\textends XMPPException {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\n\tpublic NoConnectionIdException() {\n\t\tsuper();\n\t}\n\n\n\tpublic NoConnectionIdException(String message) {\n\t\tsuper(message);\n\t}\n\n\n\tpublic NoConnectionIdException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n\tpublic NoConnectionIdException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/NotAuthorizedException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\n/**\n * This exception is usually trown when there is kind of access violation error. If some code try to access data to\n * which it doesn't have permission to or if try to access data before successful authorization process.\n * <br>\n * <p> Created: Sat Oct 30 08:44:37 2004 </p>\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class NotAuthorizedException\n\t\textends XMPPException {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * Creates a new <code>NotAuthorizedException</code> instance.\n\t */\n\tpublic NotAuthorizedException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic NotAuthorizedException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n} // NotAuthorizedException\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/PacketErrorTypeException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport tigase.annotations.TigaseDeprecated;\n\n@Deprecated\n@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"To be replaced with tigase.xmpp.InvalidPacketException and used tigase.xmpp.Authorization.getResponseMessage\")\npublic class PacketErrorTypeException\n\t\textends XMPPException {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic PacketErrorTypeException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic PacketErrorTypeException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/PacketInvalidAddressException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.xmpp;\n\npublic class PacketInvalidAddressException extends InvalidPacketException {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic PacketInvalidAddressException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic PacketInvalidAddressException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/PacketInvalidTypeException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.xmpp;\n\npublic class PacketInvalidTypeException\n\t\textends InvalidPacketException {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic PacketInvalidTypeException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic PacketInvalidTypeException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/RepositoryAccess.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.auth.credentials.Credentials;\nimport tigase.db.*;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.vhosts.VHostItem;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.db.AuthRepository.*;\nimport static tigase.db.NonAuthUserRepository.OFFLINE_DATA_NODE;\nimport static tigase.db.NonAuthUserRepository.PUBLIC_DATA_NODE;\n\n/**\n * Describe class RepositoryAccess here.\n * <br>\n * Created: Tue Oct 24 10:38:41 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic abstract class RepositoryAccess {\n\n\tprotected static final String NO_ACCESS_TO_REP_MSG = \"Can not access user repository.\";\n\n\tprotected static final String NOT_AUTHORIZED_MSG = \"Session has not been yet authorised.\";\n\tprivate static final String ANONYMOUS_MECH = \"ANONYMOUS\";\n\tprivate static final Logger log = Logger.getLogger(\"tigase.xmpp.RepositoryAccess\");\n\n\n\tprotected AuthRepository authRepo = null;\n\t/**\n\t * Current authorization state - initialy session i <code>NOT_AUTHORIZED</code>. It becomes <code>AUTHORIZED</code>\n\t */\n\tprotected Authorization authState = Authorization.NOT_AUTHORIZED;\n\tprotected VHostItem domain = null;\n\n\t// private boolean anon_allowed = false;\n\tprotected boolean is_anonymous = false;\n\t/**\n\t * Handle to user repository - permanent data base for storing user data.\n\t */\n\tprivate UserRepository repo = null;\n\n\t/**\n\t * Creates a new <code>RepositoryAccess</code> instance.\n\t */\n\tpublic RepositoryAccess(UserRepository rep, AuthRepository auth) {\n\t\trepo = rep;\n\t\tauthRepo = auth;\n\n\t\t// this.anon_allowed = anon_allowed;\n\t}\n\n\tpublic void addDataList(final String subnode, final String key, final String[] list)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tif (is_anonymous) {\n\t\t\treturn;\n\t\t}\n\t\tif (!isAuthorized()) {\n\t\t\tthrow new NotAuthorizedException(NO_ACCESS_TO_REP_MSG);\n\t\t}\n\t\ttry {\n\t\t\trepo.addDataList(getBareJID(), subnode, key, list);\n\t\t} catch (UserNotFoundException e) {\n\t\t\tlog.log(Level.FINEST, \"Problem accessing reposiotry: \", e);\n\n\t\t\tthrow new NotAuthorizedException(NO_ACCESS_TO_REP_MSG, e);\n\n\t\t\t// } catch (TigaseDBException e) {\n\t\t\t// log.log(Level.SEVERE, \"Repository access exception.\", e);\n\t\t}    // end of try-catch\n\t}\n\n\tpublic void addOfflineDataList(String subnode, String key, String[] list)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\taddDataList(calcNode(OFFLINE_DATA_NODE, subnode), key, list);\n\t}\n\n\tpublic void addPublicDataList(String subnode, String key, String[] list)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\taddDataList(calcNode(PUBLIC_DATA_NODE, subnode), key, list);\n\t}\n\n\tpublic void logout() throws NotAuthorizedException {\n\t\tauthState = Authorization.NOT_AUTHORIZED;\n\t}\n\n\tpublic void queryAuth(Map<String, Object> authProps) throws TigaseDBException {\n\t\tif (authRepo == null) {\n\t\t\tlog.severe(\"Authentication repository is not available! Misconfiguration error or \" +\n\t\t\t\t\t\t\t   \"authentication database is not available. Please check your logs from the \" +\n\t\t\t\t\t\t\t   \"server startup time.\");\n\n\t\t\treturn;\n\t\t}\n\t\tauthProps.put(AuthRepository.SERVER_NAME_KEY, getDomain().getVhost().getDomain());\n\t\tauthRepo.queryAuth(authProps);\n\t\tif (domain.isAnonymousEnabled() && (authProps.get(PROTOCOL_KEY) == AuthRepository.PROTOCOL_VAL_SASL)) {\n\t\t\tString[] auth_mechs = (String[]) authProps.get(AuthRepository.RESULT_KEY);\n\n\t\t\tif (auth_mechs == null) {\n\t\t\t\tthrow new TigaseDBException(\n\t\t\t\t\t\t\"No euthentication mechanisms found, probably \" + \"DB misconfiguration problem.\");\n\t\t\t}\n\t\t\tauth_mechs = Arrays.copyOf(auth_mechs, auth_mechs.length + 1);\n\t\t\tauth_mechs[auth_mechs.length - 1] = ANONYMOUS_MECH;\n\t\t\tauthProps.put(AuthRepository.RESULT_KEY, auth_mechs);\n\t\t}\n\t}\n\n\tpublic Authorization changeRegistration(final String name_param, final String pass_param,\n\t\t\t\t\t\t\t\t\t\t\tfinal Map<String, String> registr_params)\n\t\t\tthrows NotAuthorizedException, TigaseDBException, TigaseStringprepException {\n\t\tif ((name_param == null) || name_param.equals(\"\") || (pass_param == null) || pass_param.equals(\"\")) {\n\t\t\treturn Authorization.BAD_REQUEST;\n\t\t}\n\t\tif (getUserName().equals(name_param)) {\n\t\t\tsetRegistration(name_param, pass_param, registr_params);\n\n\t\t\treturn Authorization.AUTHORIZED;\n\t\t} else {\n\t\t\treturn Authorization.NOT_AUTHORIZED;\n\t\t}\n\t}\n\n\tpublic void removeData(String subnode, String key) throws NotAuthorizedException, TigaseDBException {\n\t\ttry {\n\t\t\trepo.removeData(getBareJID(), subnode, key);\n\t\t} catch (UserNotFoundException e) {\n\t\t\tlog.log(Level.FINEST, \"Problem accessing reposiotry: \", e);\n\n\t\t\tthrow new NotAuthorizedException(NO_ACCESS_TO_REP_MSG, e);\n\n\t\t\t// } catch (TigaseDBException e) {\n\t\t\t// log.log(Level.SEVERE, \"Repository access exception.\", e);\n\t\t}    // end of try-catch\n\t}\n\n\t/**\n\t * Removes the last data node given in subnode path as parameter to this method. All subnodes are moved as well an\n\t * all data stored as <code>(key, val)</code> are removed as well. Changes are commited to repository immediatelly\n\t * and there is no way to undo this operation so use it with care.\n\t *\n\t * @param subnode a <code>String</code> value of path to node which has to be removed.\n\t *\n\t * @throws NotAuthorizedException is thrown when session has not been authorized yet and there is no access to\n\t * permanent storage.\n\t * @see #setData(String, String, String)\n\t */\n\tpublic void removeDataGroup(final String subnode) throws NotAuthorizedException, TigaseDBException {\n\t\tif (is_anonymous) {\n\t\t\treturn;\n\t\t}\n\t\tif (!isAuthorized()) {\n\t\t\tthrow new NotAuthorizedException(NO_ACCESS_TO_REP_MSG);\n\t\t}\n\t\ttry {\n\t\t\trepo.removeSubnode(getBareJID(), subnode);\n\t\t} catch (UserNotFoundException e) {\n\t\t\tlog.log(Level.FINEST, \"Problem accessing reposiotry: \", e);\n\n\t\t\tthrow new NotAuthorizedException(NO_ACCESS_TO_REP_MSG, e);\n\n\t\t\t// } catch (TigaseDBException e) {\n\t\t\t// log.log(Level.SEVERE, \"Repository access exception.\", e);\n\t\t}    // end of try-catch\n\t}\n\n\tpublic void removeOfflineData(String subnode, String key) throws NotAuthorizedException, TigaseDBException {\n\t\tremoveData(calcNode(OFFLINE_DATA_NODE, subnode), key);\n\t}\n\n\tpublic void removeOfflineDataGroup(String subnode) throws NotAuthorizedException, TigaseDBException {\n\t\tremoveDataGroup(calcNode(OFFLINE_DATA_NODE, subnode));\n\t}\n\n\tpublic void removePublicData(String subnode, String key) throws NotAuthorizedException, TigaseDBException {\n\t\tremoveData(calcNode(PUBLIC_DATA_NODE, subnode), key);\n\t}\n\n\tpublic void removePublicDataGroup(String subnode) throws NotAuthorizedException, TigaseDBException {\n\t\tremoveDataGroup(calcNode(PUBLIC_DATA_NODE, subnode));\n\t}\n\n\tpublic String getAuthenticationToken(String xmpp_sessionId) throws NotAuthorizedException, TigaseDBException {\n\t\tUUID token = UUID.randomUUID();\n\n\t\tsetData(\"tokens\", xmpp_sessionId, token.toString());\n\n\t\treturn token.toString();\n\t}\n\n\t/**\n\t * Gets the value of authState\n\t *\n\t * @return the value of authState\n\t */\n\tpublic final Authorization getAuthState() {\n\t\treturn this.authState;\n\t}\n\n\t/**\n\t * Returns user JID but without <em>resource</em> part. This is real user ID not session ID. To retrieve session ID\n\t * - full JID refer to <code>getJID()</code> method.<br> If session has not been authorized yet this method throws\n\t * <code>NotAuthorizedException</code>.\n\t *\n\t * @return a <code>String</code> value of user ID - this is user JID without resource part. To obtain full user JID\n\t * please refer to <code>getJID</code> method.\n\t *\n\t * @throws NotAuthorizedException when this session has not been authorized yet and some parts of user JID are not\n\t * known yet.\n\t */\n\tpublic abstract BareJID getBareJID() throws NotAuthorizedException;\n\n\t/**\n\t * <code>getData</code> method is a twin sister (brother?) of <code>setData(String, String, String)</code> method.\n\t * It allows you to retrieve data stored with above method. It is data stored in given node with given key\n\t * identifier. If there are no data associated with given key or given node does not exist given <code>def</code>\n\t * value is returned.\n\t *\n\t * @param subnode a <code>String</code> value is path to node where pair <code>(key, value)</code> are stored.\n\t * @param key a <code>String</code> value of key ID for data to retrieve.\n\t * @param def a <code>String</code> value of default returned if there is nothing stored with given key.\n\t * <code>def</code> can be set to any value you wish to have back as default value or <code>null</code> if you want\n\t * to have back <code>null</code> if no data was found. If you set <code>def</code> to <code>null</code> it has\n\t * exactly the same effect as if you use <code>getData(String)</code> method.\n\t *\n\t * @return a <code>String</code> value of data found for given key or <code>def</code> if there was no data\n\t * associated with given key.\n\t *\n\t * @throws NotAuthorizedException is thrown when session has not been authorized yet and there is no access to\n\t * permanent storage.\n\t * @see #setData(String, String, String)\n\t */\n\tpublic String getData(String subnode, String key, String def) throws NotAuthorizedException, TigaseDBException {\n\t\tif (is_anonymous) {\n\t\t\treturn null;\n\t\t}\n\t\tif (!isAuthorized()) {\n\t\t\tthrow new NotAuthorizedException(NO_ACCESS_TO_REP_MSG);\n\t\t}\n\t\ttry {\n\t\t\treturn repo.getData(getBareJID(), subnode, key, def);\n\t\t} catch (UserNotFoundException e) {\n\t\t\tlog.log(Level.FINEST, \"Problem accessing reposiotry: \", e);\n\n\t\t\tthrow new NotAuthorizedException(NO_ACCESS_TO_REP_MSG, e);\n\n\t\t\t// } catch (TigaseDBException e) {\n\t\t\t// log.log(Level.SEVERE, \"Repository access exception.\", e);\n\t\t}    // end of try-catch\n\n\t\t// return null;\n\t}\n\n\t/**\n\t * This method retrieves list of all direct subnodes for given node. It works in similar way as <code>ls</code> unix\n\t * command or <code>dir</code> under DOS/Windows systems.\n\t *\n\t * @param subnode a <code>String</code> value of path to node for which we want to retrieve list of direct\n\t * subnodes.\n\t *\n\t * @return a <code>String[]</code> array of direct subnodes names for given node.\n\t *\n\t * @throws NotAuthorizedException is thrown when session has not been authorized yet and there is no access to\n\t * permanent storage.\n\t * @see #setData(String, String, String)\n\t */\n\tpublic String[] getDataGroups(String subnode) throws NotAuthorizedException, TigaseDBException {\n\t\tif (is_anonymous) {\n\t\t\treturn null;\n\t\t}\n\t\tif (!isAuthorized()) {\n\t\t\tthrow new NotAuthorizedException(NO_ACCESS_TO_REP_MSG);\n\t\t}\n\t\ttry {\n\t\t\treturn repo.getSubnodes(getBareJID(), subnode);\n\t\t} catch (UserNotFoundException e) {\n\t\t\tlog.log(Level.FINEST, \"Problem accessing reposiotry: \", e);\n\n\t\t\tthrow new NotAuthorizedException(NO_ACCESS_TO_REP_MSG, e);\n\n\t\t\t// } catch (TigaseDBException e) {\n\t\t\t// log.log(Level.SEVERE, \"Repository access exception.\", e);\n\t\t}    // end of try-catch\n\n\t\t// return null;\n\t}\n\n\t/**\n\t * This method returns all data keys available in permanent storage in given node. There is not though any\n\t * information what kind of data is stored with this key. This is up to user (developer) to determine what data type\n\t * is associated with key and what is it's meaning.\n\t *\n\t * @param subnode a <code>String</code> value pointing to specific subnode in user reposiotry where data have to be\n\t * stored.\n\t *\n\t * @return a <code>String[]</code> array containing all data keys found in given subnode.\n\t *\n\t * @throws NotAuthorizedException is thrown when session has not been authorized yet and there is no access to\n\t * permanent storage.\n\t * @see #setData(String, String, String)\n\t */\n\tpublic String[] getDataKeys(final String subnode) throws NotAuthorizedException, TigaseDBException {\n\t\tif (is_anonymous) {\n\t\t\treturn null;\n\t\t}\n\t\tif (!isAuthorized()) {\n\t\t\tthrow new NotAuthorizedException(NO_ACCESS_TO_REP_MSG);\n\t\t}\n\t\ttry {\n\t\t\treturn repo.getKeys(getBareJID(), subnode);\n\t\t} catch (UserNotFoundException e) {\n\t\t\tlog.log(Level.FINEST, \"Problem accessing reposiotry: \", e);\n\n\t\t\tthrow new NotAuthorizedException(NO_ACCESS_TO_REP_MSG, e);\n\n\t\t\t// } catch (TigaseDBException e) {\n\t\t\t// log.log(Level.SEVERE, \"Repository access exception.\", e);\n\t\t}    // end of try-catch\n\n\t\t// return null;\n\t}\n\n\t/**\n\t * This method allows to retrieve list of values associated with one key. As it is possible to store many values\n\t * with one key there are a few methods which provides this functionality. If given key does not exists in given\n\t * subnode <code>null</code> is returned.\n\t *\n\t * @param subnode a <code>String</code> value pointing to specific subnode in user reposiotry where data have to be\n\t * stored.\n\t * @param key a <code>String</code> value of data key ID.\n\t *\n\t * @return a <code>String[]</code> array containing all values found for given key.\n\t *\n\t * @throws NotAuthorizedException is thrown when session has not been authorized yet and there is no access to\n\t * permanent storage.\n\t * @see #setData(String, String, String)\n\t */\n\tpublic String[] getDataList(String subnode, String key) throws NotAuthorizedException, TigaseDBException {\n\t\tif (is_anonymous) {\n\t\t\treturn null;\n\t\t}\n\t\tif (!isAuthorized()) {\n\t\t\tthrow new NotAuthorizedException(NO_ACCESS_TO_REP_MSG);\n\t\t}\n\t\ttry {\n\t\t\treturn repo.getDataList(getBareJID(), subnode, key);\n\t\t} catch (UserNotFoundException e) {\n\t\t\tlog.log(Level.FINEST, \"Problem accessing reposiotry: \", e);\n\n\t\t\tthrow new NotAuthorizedException(NO_ACCESS_TO_REP_MSG, e);\n\n\t\t\t// } catch (TigaseDBException e) {\n\t\t\t// log.log(Level.SEVERE, \"Repository access exception.\", e);\n\t\t}    // end of try-catch\n\n\t\t// return null;\n\t}\n\n\tpublic VHostItem getDomain() {\n\t\treturn domain;\n\t}\n\n\tpublic void setDomain(final VHostItem domain) throws TigaseStringprepException {\n\t\tthis.domain = domain;\n\t}\n\n\tpublic JID getDomainAsJID() {\n\t\treturn domain.getVhost();\n\t}\n\n\tpublic String getOfflineData(String subnode, String key, String def)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\treturn getData(calcNode(OFFLINE_DATA_NODE, subnode), key, def);\n\t}\n\n\tpublic String[] getOfflineDataList(String subnode, String key) throws NotAuthorizedException, TigaseDBException {\n\t\treturn getDataList(calcNode(OFFLINE_DATA_NODE, subnode), key);\n\t}\n\n\tpublic String getPublicData(String subnode, String key, String def)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\treturn getData(calcNode(PUBLIC_DATA_NODE, subnode), key, def);\n\t}\n\n\tpublic String[] getPublicDataList(String subnode, String key) throws NotAuthorizedException, TigaseDBException {\n\t\treturn getDataList(calcNode(PUBLIC_DATA_NODE, subnode), key);\n\t}\n\n\tpublic abstract String getUserName() throws NotAuthorizedException;\n\n\tpublic boolean isAnonymous() {\n\t\treturn is_anonymous;\n\t}\n\t\n\t/**\n\t * This method allows you test this session if it already has been authorized. If <code>true</code> is returned as\n\t * method result it means session has already been authorized, if <code>false</code> however session is still not\n\t * authorized.\n\t *\n\t * @return a <code>boolean</code> value which informs whether this session has been already authorized or not.\n\t */\n\tpublic boolean isAuthorized() {\n\t\treturn authState == Authorization.AUTHORIZED;\n\t}\n\n\t/**\n\t * This method stores given data in permanent storage in given point of hierarchy of data base. This method is\n\t * similar to <code>setData(String, String)</code> and differs in one additional parameter which point to user data\n\t * base subnode where data must be stored. It helps to organize user data in more logical hierarchy.<br> User data\n\t * is kind of tree where you can store data in each tree node. The most relevant sample might be structure like\n\t * typical file system or XML like or LDAP data base. The first implementation is actually done as XML file to make\n\t * it easier test application and deploy simple installation where there is no more users than 1000.<br> To find out\n\t * more about user repository refer to <code>UserRepository</code> interface for general info and to\n\t * <code>XMLRepository</code> for detailed explanation regarding XML implementation of user repository. <br> Thus\n\t * <code>subnode</code> is kind of path to data node. If you specify <code>null</code> or empty node data will be\n\t * stored in root user node. This has exactly the same effect as you call <code>setData(String, String)</code>. If\n\t * you want to store data in different node you must just specify node path like you do it to directory on most file\n\t * systems:\n\t * <br>\n\t * <pre>\n\t * /roster\n\t * </pre>\n\t * <br>\n\t * Or, if you need access deeper node:\n\t * <br>\n\t * <pre>\n\t * /just/like/path/to/file\n\t * </pre>\n\t * <br>\n\t * <br> If given node does not yet exist it will be automaticaly created with all nodes in given path so there is no\n\t * need for developer to perform additional action to create node. There is, however method\n\t * <code>removeDataGroup(String)</code> for deleting specified node as nodes are not automaticaly deleted.\n\t *\n\t * @param subnode a <code>String</code> value pointing to specific subnode in user reposiotry where data have to be\n\t * stored.\n\t * @param key a <code>String</code> value of data key ID.\n\t * @param value a <code>String</code> actual data stored in user repository.\n\t *\n\t * @throws NotAuthorizedException is thrown when session has not been authorized yet and there is no access to\n\t * permanent storage.\n\t * @see #removeDataGroup(String)\n\t * @see UserRepository\n\t */\n\tpublic void setData(String subnode, String key, String value) throws NotAuthorizedException, TigaseDBException {\n\t\ttry {\n\t\t\trepo.setData(getBareJID(), subnode, key, value);\n\t\t} catch (UserNotFoundException e) {\n\t\t\tlog.log(Level.FINEST, \"Problem accessing reposiotry: \", e);\n\n\t\t\tthrow new NotAuthorizedException(NO_ACCESS_TO_REP_MSG, e);\n\n\t\t\t// } catch (TigaseDBException e) {\n\t\t\t// log.log(Level.SEVERE, \"Repository access exception.\", e);\n\t\t}    // end of try-catch\n\t}\n\n\t/**\n\t * This method allows to store list of values under one key ID reference. It is often necessary to keep set of\n\t * values which can be refered by one key. As an example might be list of groups for specific buddy in roster. There\n\t * is no actual need to store each group with separate key because we usually need to acces whole list of groups.\n\t *\n\t * @param subnode a <code>String</code> value pointing to specific subnode in user reposiotry where data have to be\n\t * stored.\n\t * @param key a <code>String</code> value of data key ID.\n\t * @param list a <code>String[]</code> keeping list of actual data to be stored in user repository.\n\t *\n\t * @throws NotAuthorizedException is thrown when session has not been authorized yet and there is no access to\n\t * permanent storage.\n\t * @see #setData(String, String, String)\n\t */\n\tpublic void setDataList(final String subnode, final String key, final String[] list)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tif (is_anonymous) {\n\t\t\treturn;\n\t\t}\n\t\tif (!isAuthorized()) {\n\t\t\tthrow new NotAuthorizedException(NO_ACCESS_TO_REP_MSG);\n\t\t}\n\t\ttry {\n\t\t\trepo.setDataList(getBareJID(), subnode, key, list);\n\t\t} catch (UserNotFoundException e) {\n\t\t\tlog.log(Level.FINEST, \"Problem accessing reposiotry: \", e);\n\n\t\t\tthrow new NotAuthorizedException(NO_ACCESS_TO_REP_MSG, e);\n\n\t\t\t// } catch (TigaseDBException e) {\n\t\t\t// log.log(Level.SEVERE, \"Repository access exception.\", e);\n\t\t}    // end of try-catch\n\t}\n\n\tpublic void setOfflineData(String subnode, String key, String value)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tsetData(calcNode(OFFLINE_DATA_NODE, subnode), key, value);\n\t}\n\n\tpublic void setOfflineDataList(String subnode, String key, String[] list)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tsetDataList(calcNode(OFFLINE_DATA_NODE, subnode), key, list);\n\t}\n\n\tpublic void setPublicData(String subnode, String key, String value)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tsetData(calcNode(PUBLIC_DATA_NODE, subnode), key, value);\n\t}\n\n\tpublic void setPublicDataList(String subnode, String key, String[] list)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tsetDataList(calcNode(PUBLIC_DATA_NODE, subnode), key, list);\n\t}\n\n\t// ~--- set methods ----------------------------------------------------------\n\tpublic void setRegistration(final String name_param, final String pass_param,\n\t\t\t\t\t\t\t\tfinal Map<String, String> registr_params)\n\t\t\tthrows TigaseDBException, TigaseStringprepException {\n\t\ttry {\n\t\t\tauthRepo.updateCredential(BareJID.bareJIDInstance(name_param, getDomain().getVhost().getDomain()),\n\t\t\t\t\t\t\t\t\t  Credentials.DEFAULT_CREDENTIAL_ID, pass_param);\n\t\t\tif (registr_params != null) {\n\t\t\t\tfor (Map.Entry<String, String> entry : registr_params.entrySet()) {\n\t\t\t\t\trepo.setData(BareJID.bareJIDInstance(name_param, getDomain().getVhost().getDomain()),\n\t\t\t\t\t\t\t\t entry.getKey(), entry.getValue());\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (UserNotFoundException e) {\n\t\t\tlog.log(Level.WARNING, \"Problem accessing reposiotry: \", e);\n\n\t\t\t// } catch (TigaseDBException e) {\n\t\t\t// log.log(Level.SEVERE, \"Repository access exception.\", e);\n\t\t}    // end of try-catch\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic Authorization unregister(String name_param)\n\t\t\tthrows NotAuthorizedException, TigaseDBException, TigaseStringprepException {\n\t\tif (!isAuthorized()) {\n\t\t\treturn Authorization.FORBIDDEN;\n\t\t}\n\n\t\t// Some clients send plain user name and others send\n\t\t// jid as user name. Let's resolve this here.\n\t\tString user_name = BareJID.parseJID(name_param)[0];\n\n\t\tif ((user_name == null) || user_name.trim().isEmpty()) {\n\t\t\tuser_name = name_param;\n\t\t}    // end of if (user_mame == null || user_name.equals(\"\"))\n\t\tif (getUserName().equals(user_name)) {\n\t\t\ttry {\n\t\t\t\tauthRepo.removeUser(BareJID.bareJIDInstance(user_name, getDomain().getVhost().getDomain()));\n\t\t\t\ttry {\n\t\t\t\t\trepo.removeUser(BareJID.bareJIDInstance(user_name, getDomain().getVhost().getDomain()));\n\t\t\t\t} catch (UserNotFoundException ex) {\n\n\t\t\t\t\t// We ignore this error here. If auth_repo and user_repo are in fact\n\t\t\t\t\t// the same\n\t\t\t\t\t// database, then user has been already removed with the\n\t\t\t\t\t// auth_repo.removeUser(...)\n\t\t\t\t\t// then the second call to user_repo may throw the exception which is\n\t\t\t\t\t// fine.\n\t\t\t\t}\n\n\t\t\t\t// We mark the session as no longer authorized to prevent data access through\n\t\t\t\t// this session.\n\t\t\t\tlogout();\n\n\t\t\t\t// Session authorized is returned only to indicate successful operation.\n\t\t\t\treturn Authorization.AUTHORIZED;\n\t\t\t} catch (UserNotFoundException e) {\n\t\t\t\treturn Authorization.REGISTRATION_REQUIRED;\n\t\t\t} catch (TigaseDBException e) {\n\t\t\t\tlog.log(Level.SEVERE, \"Repository access exception.\", e);\n\n\t\t\t\treturn Authorization.INTERNAL_SERVER_ERROR;\n\t\t\t}    // end of catch\n\t\t} else {\n\t\t\treturn Authorization.FORBIDDEN;\n\t\t}\n\t}\n\n\tprotected abstract void login();\n\n\tprivate String calcNode(String base, String subnode) {\n\t\tif (subnode == null) {\n\t\t\treturn base;\n\t\t}    // end of if (subnode == null)\n\n\t\treturn base + \"/\" + subnode;\n\t}\n\n\tprivate boolean isLoginAllowed() throws AuthorizationException {\n\t\tif (isAuthorized()) {\n\t\t\tthrow new AuthorizationException(\"User session already authenticated. \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t \"Subsequent login is forbidden. You must loggin on a different connection.\");\n\t\t}\n\n\t\treturn true;\n\t}\n}    // RepositoryAccess\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/SpamReportsConsumer.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * Classes implementing this interface should be beans registered in `SessionManager`s kernel to receive calls when\n * spammer is being reported by the user.\n */\npublic interface SpamReportsConsumer {\n\n\tstatic final String XMLNS_PREFIX = \"urn:xmpp:reporting:\";\n\tstatic final String XMLNS1_PREFIX = \"urn:xmpp:reporting:1\";\n\tstatic final String XMLNS = XMLNS_PREFIX + \"0\";\n\n\tstatic final Element[] FEATURES = Stream.concat(Stream.of(\"0\", \"abuse:0\", \"spam:0\")\n\t\t\t.map(suffix -> XMLNS_PREFIX + suffix), Stream.of(XMLNS1_PREFIX))\n\t\t\t.map(var -> new Element(\"feature\", new String[]{\"var\"}, new String[]{var}))\n\t\t\t.toArray(Element[]::new);\n\n\t/**\n\t * Method called when a user reports JID as a spammer\n\t * @param jid - jid of the spammer\n\t * @param type - type of the abuse\n\t * @return\n\t */\n\tboolean spamReportedFrom(BareJID jid, ReportType type);\n\n\tenum ReportType {\n\t\tabuse, spam;\n\n\t\tprivate static Map<String, ReportType> VALUES = Arrays.stream(ReportType.values())\n\t\t\t\t.collect(Collectors.toMap(it -> it.name(), Function.identity()));\n\n\t\tpublic static ReportType fromReport(Element report) {\n\t\t\tif (report.getXMLNS() == XMLNS1_PREFIX) {\n\t\t\t\tString reason = report.getAttributeStaticStr(\"reason\");\n\t\t\t\tif (reason != null) {\n\t\t\t\t\tswitch (reason) {\n\t\t\t\t\t\tcase \"urn:xmpp:reporting:spam\":\n\t\t\t\t\t\t\treturn ReportType.spam;\n\t\t\t\t\t\tcase \"urn:xmpp:reporting:abuse\":\n\t\t\t\t\t\t\treturn ReportType.abuse;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tList<Element> children = report.getChildren();\n\t\t\tif (children == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tfor (Element child : children) {\n\t\t\t\tReportType type = ReportType.fromElement(child);\n\t\t\t\tif (type != null) {\n\t\t\t\t\treturn type;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tpublic static ReportType fromElement(Element element) {\n\t\t\treturn VALUES.get(element.getName());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/StanzaType.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * Describe class StanzaType here.\n * <br>\n * Created: Fri Feb 10 13:13:50 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic enum StanzaType {\n\n\terror,\n\t// Common type\n\tget,\n\tset,\n\tresult,\n\t// iq types\n\tavailable,\n\tunavailable,\n\tprobe,\n\tsubscribe,\n\t// presence types\n\tsubscribed,\n\tunsubscribe,\n\tunsubscribed,\n\t// presence types\n\tchat,\n\tgroupchat,\n\theadline,\n\tnormal,\n\t// message types\n\tvalid,\n\tinvalid,\n\t// Dialback verification packets\n\tterminate,\n\t// Bosh - session termination stanza\n\tinvisible;                                // Other unknown types...\n\n\tprivate static Set<StanzaType> subTypes;\n\n\tpublic static Set<StanzaType> getSubsTypes() {\n\t\tif (subTypes == null) {\n\t\t\tsubTypes = new HashSet<>();\n\t\t\tsubTypes.add(subscribe);\n\t\t\tsubTypes.add(subscribed);\n\t\t\tsubTypes.add(unsubscribe);\n\t\t\tsubTypes.add(unsubscribed);\n\t\t}\n\t\treturn subTypes;\n\t}\n\n\tpublic static StanzaType valueof(String cmd) {\n\t\ttry {\n\t\t\treturn StanzaType.valueOf(cmd);\n\t\t} catch (IllegalArgumentException e) {\n\t\t\treturn null;\n\t\t} // end of try-catch\n\t}\n\n} // StanzaType\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/StreamError.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport tigase.xml.Element;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author andrzej\n */\npublic enum StreamError {\n\n\tBadFormat(\"bad-format\"),\n\tBadNamespacePrefix(\"bad-namespace-prefix\"),\n\tConflict(\"conflict\"),\n\tConnectionTimeout(\"connection-timeout\"),\n\tHostGone(\"host-gone\"),\n\tHostUnknown(\"host-unknown\"),\n\tImproperAddressing(\"improper-addressing\"),\n\tInternalServerError(\"internal-server-error\"),\n\tInvalidFrom(\"invalid-from\"),\n\tInvalidNamespace(\"invalid-namespace\"),\n\tInvalidXml(\"invalid-xml\"),\n\tNotAuthorized(\"not-authorized\"),\n\tNotWellFormed(\"not-well-formed\"),\n\tPolicyViolation(\"policy-violation\"),\n\tRemoteConnectionFailed(\"remote-connection-failed\"),\n\tReset(\"reset\"),\n\tResourceConstraint(\"resource-constraint\"),\n\tRestrictedXml(\"restricted-xml\"),\n\tSeeOtherHost(\"see-other-host\"),\n\tSystemShutdown(\"system-shutdown\"),\n\tUndefinedCondition(\"undefined-condition\"),\n\tUnsupportedEncoding(\"unsupported-encoding\"),\n\tUnsupportedFeature(\"unsupported-feature\"),\n\tUnsupportedStanzaType(\"unsupported-stanza-type\"),\n\tUnsupportedVersion(\"unsupported-version\");\n\n\tprivate static final Map<String, StreamError> BY_CONDITION = new HashMap<>();\n\n\tstatic {\n\t\tfor (StreamError err : StreamError.values()) {\n\t\t\tBY_CONDITION.put(err.getCondition(), err);\n\t\t}\n\t}\n\n\tprivate final String condition;\n\n\tpublic static StreamError getByCondition(String condition) {\n\t\tStreamError err = BY_CONDITION.get(condition);\n\t\tif (err == null) {\n\t\t\treturn UndefinedCondition;\n\t\t}\n\t\treturn err;\n\t}\n\n\tprivate StreamError(String condition) {\n\t\tthis.condition = condition;\n\t}\n\n\tpublic String getCondition() {\n\t\treturn condition;\n\t}\n\n\tpublic String prepareStreamError(String errorMessage) {\n\t\treturn \"<stream:error>\" + \"<\" + condition + \" xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>\" +\n\t\t\t\t(errorMessage != null\n\t\t\t\t ? \"<text xmlns='urn:ietf:params:xml:ns:xmpp-streams'>\" + errorMessage + \"</text>\"\n\t\t\t\t : \"\") + \"</stream:error>\" + \"</stream:stream>\";\n\t}\n\n\tpublic Element prepareStreamErrorElement(String errorMessage) {\n\t\tElement err_el = new Element(condition);\n\t\terr_el.setXMLNS(\"urn:ietf:params:xml:ns:xmpp-streams\");\n\t\tif (errorMessage != null) {\n\t\t\tElement text_el = new Element(\"text\");\n\t\t\ttext_el.setXMLNS(\"urn:ietf:params:xml:ns:xmpp-streams\");\n\t\t\ttext_el.setCData(errorMessage);\n\t\t\terr_el.addChild(text_el);\n\t\t}\n\t\treturn err_el;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/XMPPDomBuilderHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport tigase.xml.DefaultElementFactory;\nimport tigase.xml.Element;\nimport tigase.xml.ElementFactory;\nimport tigase.xml.SimpleHandler;\n\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * <code>XMPPDomBuilderHandler</code> - implementation of <code>SimpleHandler</code> building <em>DOM</em> strctures\n * during parsing time. It also supports creation multiple, sperate document trees if parsed buffer contains a few\n * <em>XML</em> documents. As a result of work it returns always <code>Queue</code> containing all found <em>XML</em>\n * trees in the same order as they were found in network data.<br> Document trees created by this <em>DOM</em> builder\n * consist of instances of <code>Element</code> class or instances of class extending <code>Element</code> class. To\n * receive trees built with instances of proper class user must provide <code>ElementFactory</code> implementation\n * creating instances of required <code>ELement</code> extension.\n * <br>\n * <p> Created: Sat Oct  2 22:01:34 2004 </p>\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class XMPPDomBuilderHandler<RefObject>\n\t\timplements SimpleHandler {\n\n\tprivate static final Logger log = Logger.getLogger(\"tigase.debug.xmpp.XMPPDomBuilderHandler\");\n\n\tprivate static final String ELEM_STREAM_STREAM = \"stream:stream\";\n\tprivate static ElementFactory defaultFactory = new DefaultElementFactory();\n\n\tprivate ArrayDeque<Element> all_roots = new ArrayDeque<>(1);\n\tprivate ElementFactory customFactory = null;\n\tprivate ArrayDeque<Element> el_stack = new ArrayDeque<>(10);\n\tprivate int elements_number_limit;\n\t/**\n\t * Protection from the system overload and DOS attack. We want to limit number of elements created within a single\n\t * XMPP stanza.\n\t */\n\tprivate int elements_number_limit_count = 0;\n\tprivate boolean error = false;\n\tprivate Map<String, String> namespaces = new TreeMap<>();\n\tprivate Object parserState = null;\n\tprivate XMPPIOService<RefObject> service = null;\n\tprivate boolean streamClosed = false;\n\tprivate String top_xmlns = null;\n\n\tpublic XMPPDomBuilderHandler(XMPPIOService<RefObject> ioserv) {\n\t\tcustomFactory = defaultFactory;\n\t\tservice = ioserv;\n//\t\telements_number_limit = (int)service.getSessionData().get( ELEMENTS_NUMBER_LIMIT_PROP_KEY);\n\t}\n\n\tpublic XMPPDomBuilderHandler(XMPPIOService<RefObject> ioserv, ElementFactory factory) {\n\t\tcustomFactory = factory;\n\t\tservice = ioserv;\n//\t\telements_number_limit = (int)service.getSessionData().get( ELEMENTS_NUMBER_LIMIT_PROP_KEY);\n\t}\n\n\t@Override\n\tpublic void elementCData(StringBuilder cdata) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Element CDATA: \" + cdata);\n\t\t}\n\n\t\tElement elem = el_stack.peek();\n\t\tif (elem != null) {\n\t\t\telem.addCData(cdata.toString());\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean endElement(StringBuilder name) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"End element name: \" + name);\n\t\t}\n\n\t\tString tmp_name = name.toString();\n\n\t\tif (tmp_name.equals(ELEM_STREAM_STREAM)) {\n\t\t\t// we should not call xmppStreamClosed() as we still may have received \n\t\t\t// some packets which may not be processed correctly if we close stream now!\n\t\t\t//service.xmppStreamClosed();\n\t\t\tstreamClosed = true;\n\t\t\treturn true;\n\t\t}    // end of if (tmp_name.equals(ELEM_STREAM_STREAM))\n\n\t\tif (el_stack.isEmpty()) {\n\t\t\tel_stack.push(newElement(tmp_name, null, null, null));\n\t\t}    // end of if (tmp_name.equals())\n\n\t\tElement elem = el_stack.pop();\n\t\tint idx = tmp_name.indexOf(':');\n\t\tString tmp_xmlns = null;\n\n\t\tif (idx > 0) {\n\t\t\tString tmp_name_prefix = tmp_name.substring(0, idx);\n\t\t\tif (tmp_name_prefix != null) {\n\t\t\t\tfor (String pref : namespaces.keySet()) {\n\t\t\t\t\tif (tmp_name_prefix.equals(pref)) {\n\t\t\t\t\t\ttmp_xmlns = namespaces.get(pref);\n\t\t\t\t\t\ttmp_name = tmp_name.substring(pref.length() + 1, tmp_name.length());\n\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.finest(\"new_xmlns = \" + tmp_xmlns);\n\t\t\t\t\t\t}\n\t\t\t\t\t}    // end of if (tmp_name.startsWith(xmlns))\n\t\t\t\t}      // end of for (String xmlns: namespaces.keys())\n\t\t\t}\n\t\t}\n\t\tif (elem.getName() != tmp_name.intern() || (tmp_xmlns != null && !tmp_xmlns.equals(elem.getXMLNS()))) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (el_stack.isEmpty()) {\n\t\t\telements_number_limit_count = 0;\n\t\t\tall_roots.offer(elem);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Adding new request: \" + elem.toString());\n\t\t\t}\n\t\t} else {\n\t\t\tel_stack.peek().addChild(elem);\n\t\t}    // end of if (el_stack.isEmpty()) else\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void error(String errorMessage) {\n\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\tlog.fine(errorMessage);\n\t\t}\n\n\t\terror = true;\n\t}\n\n\tpublic Queue<Element> getParsedElements() {\n\t\treturn all_roots;\n\t}\n\n\tpublic boolean isStreamClosed() {\n\t\treturn streamClosed;\n\t}\n\n\t@Override\n\tpublic void otherXML(StringBuilder other) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Other XML content: \" + other);\n\t\t}\n\n\t\t// Just ignore\n\t}\n\n\tpublic boolean parseError() {\n\t\treturn error;\n\t}\n\n\t@Override\n\tpublic Object restoreParserState() {\n\t\treturn parserState;\n\t}\n\n\t@Override\n\tpublic void saveParserState(Object state) {\n\t\tparserState = state;\n\t}\n\n\tpublic void setElementsLimit(int limit) {\n\t\telements_number_limit = limit;\n\t}\n\n\t@Override\n\tpublic void startElement(StringBuilder name, StringBuilder[] attr_names, StringBuilder[] attr_values) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Start element name: \" + name + \", element attributes names: \" + Arrays.toString(attr_names) +\n\t\t\t\t\t\t\t   \", element attributes values: \" + Arrays.toString(attr_values));\n\t\t}\n\n\t\t// Look for 'xmlns:' declarations:\n\t\tif (attr_names != null) {\n\t\t\tfor (int i = 0; i < attr_names.length; ++i) {\n\n\t\t\t\t// Exit the loop as soon as we reach end of attributes set\n\t\t\t\tif (attr_names[i] == null) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (attr_names[i].toString().startsWith(\"xmlns:\")) {\n\n\t\t\t\t\t// TODO should use a StringCache instead of intern() to avoid potential\n\t\t\t\t\t// DOS by exhausting permgen\n\t\t\t\t\tnamespaces.put(attr_names[i].substring(\"xmlns:\".length(), attr_names[i].length()).intern(),\n\t\t\t\t\t\t\t\t   attr_values[i].toString());\n\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.finest(\"Namespace found: \" + attr_values[i].toString());\n\t\t\t\t\t}\n\t\t\t\t}    // end of if (att_name.startsWith(\"xmlns:\"))\n\t\t\t}      // end of for (String att_name : attnames)\n\t\t}        // end of if (attr_names != null)\n\n\t\tString tmp_name = name.toString();\n\n\t\tif (tmp_name.equals(ELEM_STREAM_STREAM)) {\n\t\t\tstreamClosed = false;\n\t\t\tMap<String, String> attribs = new HashMap<String, String>();\n\n\t\t\tif (attr_names != null) {\n\t\t\t\tfor (int i = 0; i < attr_names.length; i++) {\n\t\t\t\t\tif ((attr_names[i] != null) && (attr_values[i] != null)) {\n\t\t\t\t\t\tattribs.put(attr_names[i].toString(), attr_values[i].toString());\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}    // end of else\n\t\t\t\t}      // end of for (int i = 0; i < attr_names.length; i++)\n\t\t\t}        // end of if (attr_name != null)\n\n\t\t\tservice.xmppStreamOpened(attribs);\n\n\t\t\treturn;\n\t\t}          // end of if (tmp_name.equals(ELEM_STREAM_STREAM))\n\n\t\tString new_xmlns = null;\n\t\tString prefix = null;\n\t\tString tmp_name_prefix = null;\n\t\tint idx = tmp_name.indexOf(':');\n\n\t\tif (idx > 0) {\n\t\t\ttmp_name_prefix = tmp_name.substring(0, idx);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Found prefixed element name, prefix: \" + tmp_name_prefix);\n\t\t\t}\n\t\t}\n\n\t\tif (tmp_name_prefix != null) {\n\t\t\tfor (String pref : namespaces.keySet()) {\n\t\t\t\tif (tmp_name_prefix.equals(pref)) {\n\t\t\t\t\tnew_xmlns = namespaces.get(pref);\n\t\t\t\t\ttmp_name = tmp_name.substring(pref.length() + 1, tmp_name.length());\n\t\t\t\t\tprefix = pref;\n\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.finest(\"new_xmlns = \" + new_xmlns);\n\t\t\t\t\t}\n\t\t\t\t}    // end of if (tmp_name.startsWith(xmlns))\n\t\t\t}      // end of for (String xmlns: namespaces.keys())\n\t\t}\n\n\t\tElement elem = newElement(tmp_name, null, attr_names, attr_values);\n\t\tString ns = elem.getXMLNS();\n\n\t\tif (ns == null) {\n\t\t\tif (el_stack.isEmpty() || (el_stack.peek().getXMLNS() == null)) {\n\n\t\t\t\t// elem.setDefXMLNS(top_xmlns);\n\t\t\t} else {\n\t\t\t\telem.setDefXMLNS(el_stack.peek().getXMLNS());\n\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"DefXMLNS assigned: \" + elem.toString());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (new_xmlns != null) {\n\t\t\telem.setXMLNS(new_xmlns);\n\t\t\telem.removeAttribute(\"xmlns:\" + prefix);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"new_xmlns assigned: \" + elem.toString());\n\t\t\t}\n\t\t}\n\n\t\tel_stack.push(elem);\n\t}\n\n\tprivate Element newElement(String name, String cdata, StringBuilder[] attnames, StringBuilder[] attvals) {\n\t\t++elements_number_limit_count;\n\t\tElement el = customFactory.elementInstance(name, cdata, attnames, attvals);\n\n\t\tif (elements_number_limit_count > elements_number_limit) {\n\t\t\tthrow new XMPPParserException(\n\t\t\t\t\t\"Too many elements for staza, possible DoS attack.\" + \"Current service \" + service.getClass() +\n\t\t\t\t\t\t\t\" limit of elements: \" + elements_number_limit);\n\t\t}\n\t\treturn el;\n\t}\n}    // XMPPDomBuilderHandler\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/XMPPException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\n/**\n * Base exception type used for other eceptions defined for <em>XMPP</em> protocol. This type and all descendants are\n * thrown by this package runtime.\n * <br>\n * <p> Created: Sat Oct 30 08:38:18 2004 </p>\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class XMPPException\n\t\textends Exception {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic XMPPException() {\n\t\tsuper();\n\t}\n\n\tpublic XMPPException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic XMPPException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n\tpublic XMPPException(Throwable cause) {\n\t\tsuper(cause);\n\t}\n\n} // XMPPException\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/XMPPIOService.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.net.IOService;\nimport tigase.server.ConnectionManager;\nimport tigase.server.Packet;\nimport tigase.server.xmppclient.StreamManagementIOProcessor;\nimport tigase.server.xmppclient.XMPPIOProcessor;\nimport tigase.util.StringUtilities;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xml.SimpleParser;\nimport tigase.xml.SingletonFactory;\nimport tigase.xmpp.jid.JID;\n\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Queue;\nimport java.util.concurrent.ConcurrentLinkedQueue;\nimport java.util.concurrent.ConcurrentSkipListMap;\nimport java.util.concurrent.locks.ReentrantLock;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Describe class XMPPIOService here.\n * <br>\n * Created: Tue Feb 7 07:15:02 2006\n *\n * @param <RefObject> is a reference object stored by this service. This is e reference to higher level data object\n * keeping more information about the connection.\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class XMPPIOService<RefObject>\n\t\textends IOService<RefObject> {\n\n\tpublic static final String ACK_NAME = \"ack\";\n\n\tpublic static final String CROSS_DOMAIN_POLICY_FILE_PROP_KEY = \"cross-domain-policy-file\";\n\n\tpublic static final String CROSS_DOMAIN_POLICY_FILE_PROP_VAL = \"etc/cross-domain-policy.xml\";\n\n\t/**\n\t * Key name of the system property for configuration protection from system overload and DOS attack.\n\t */\n\tpublic static final String DOM_HANDLER = \"XMPPDomBuilderHandler\";\n\n\tpublic static final String ID_ATT = \"id\";\n\n\tpublic static final String REQ_NAME = \"req\";\n\n\tpublic static final String STREAM_CLOSING = \"stream-closing\";\n\n\tprivate static final Logger log = Logger.getLogger(XMPPIOService.class.getName());\n\n\tpublic ReentrantLock writeInProgress = new ReentrantLock();\n\tprotected SimpleParser parser = SingletonFactory.getParserInstance();\n\tprotected XMPPIOProcessor[] processors = null;\n\tprivate XMPPDomBuilderHandler<RefObject> domHandler = null;\n\tprivate boolean firstPacket = true;\n\tprivate JID authorisedUserJid = null;\n\t/**\n\t * This variable keeps the time of last received XMPP packet, it is used to help detect dead connections.\n\t */\n\tprivate long lastXmppPacketReceivedTime = System.currentTimeMillis();\n\tprivate long packetsReceived = 0;\n\tprivate long packetsSent = 0;\n\t/**\n\t * The <code>readyPackets</code> queue keeps data which have been already processed and they are actual processing\n\t * results.\n\t */\n\tprivate ConcurrentLinkedQueue<Packet> receivedPackets = new ConcurrentLinkedQueue<Packet>();\n\tprivate long req_idx = 0;\n\t@SuppressWarnings(\"rawtypes\")\n\tprotected XMPPIOServiceListener serviceListener = null;\n\tprivate boolean strict_ack = false;\n\tprivate long totalPacketsReceived = 0;\n\tprivate long totalPacketsSent = 0;\n\tprivate ConcurrentSkipListMap<String, Packet> waitingForAck = new ConcurrentSkipListMap<String, Packet>();\n\t/**\n\t * The <code>waitingPackets</code> queue keeps data which have to be processed.\n\t */\n\tprivate ConcurrentLinkedQueue<Packet> waitingPackets = new ConcurrentLinkedQueue<Packet>();\n\tprivate boolean white_char_ack = false;\n\tprivate String xmlns = null;\n\tprivate boolean xmpp_ack = false;\n\n\t/**\n\t * Creates a new <code>XMPPIOService</code> instance.\n\t */\n\tpublic XMPPIOService() {\n\t\tsuper();\n\t\tdomHandler = new XMPPDomBuilderHandler<>(this);\n\t\tgetSessionData().put(DOM_HANDLER, domHandler);\n\t}\n\n\t/**\n\t * Method <code>addPacketToSend</code> adds new data which will be processed during next run. Data are kept in\n\t * proper order like in <em>FIFO</em> queue.\n\t *\n\t * @param packet a <code>Packet</code> value of data to process.\n\t */\n\tpublic void addPacketToSend(Packet packet) {\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Added packet to send: {1} [{0}]\", new Object[]{toString(), packet});\n\t\t}\n\n\t\t// processing packet using io level processors\n\t\tif (processors != null) {\n\t\t\tfor (XMPPIOProcessor processor : processors) {\n\t\t\t\tif (processor.processOutgoing(this, packet)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (xmpp_ack) {\n\t\t\tString req = \"\" + (++req_idx);\n\n\t\t\tpacket.getElement().addChild(new Element(REQ_NAME, new String[]{ID_ATT}, new String[]{req}));\n\t\t\twaitingForAck.put(req, packet);\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Added req {1} for packet: {2} [{0}]\", new Object[]{toString(), req, packet});\n\t\t\t}\n\t\t}\n\t\tif (shouldCountPacket(packet)) {\n\t\t\t++packetsSent;\n\t\t\t++totalPacketsSent;\n\t\t}\n\t\twaitingPackets.offer(packet);\n\t}\n\n\t@Override\n\tpublic IOService<?> call() throws IOException {\n\t\tIOService<?> io = super.call();\n\t\t// needed to send packets added by addPacketToSent when it was not able\n\t\t// to acquire lock for write as when this packet would not be followed by\n\t\t// next packet then it would stay in waitingPackets queue, however this\n\t\t// may slow down processing packets in SocketThread thread.\n\t\tif (isConnected() && !waitingPackets.isEmpty() && writeInProgress.tryLock()) {\n\t\t\ttry {\n\t\t\t\tprocessWaitingPackets();\n\t\t\t} finally {\n\t\t\t\twriteInProgress.unlock();\n\t\t\t}\n\t\t}\n\t\treturn io;\n\t}\n\n\t@Override\n\tpublic boolean checkBufferLimit(int bufferSize) {\n\t\tif (!super.checkBufferLimit(bufferSize)) {\n\t\t\ttry {\n\t\t\t\twriteRawData(\"<stream:error>\" + \"<policy-violation \" + \"xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>\" +\n\t\t\t\t\t\t\t\t\t \"</stream:error></stream:stream>\");\n\t\t\t\tint counter = 0;\n\n\t\t\t\twhile (isConnected() && waitingToSend() && (++counter < 10)) {\n\t\t\t\t\twriteData(null);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tThread.sleep(10);\n\t\t\t\t\t} catch (InterruptedException ex) {\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (IOException ex) {\n\t\t\t\tlog.log(Level.FINEST, \"Exception sending policy-violation stream error [{0}]\", new Object[]{toString()});\n\t\t\t}\n\t\t\tthis.forceStop();\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic boolean checkData(char[] data) throws IOException {\n\n\t\t// by default do nothing and return false\n\t\treturn false;\n\t}\n\n\t/**\n\t * Clears queue of packets waiting to send. In case of connection close this packets may be sent to offline store\n\t * but some processors may want stop this from happening - for that they may use this method\n\t */\n\tpublic void clearWaitingPackets() {\n\t\tthis.waitingPackets.clear();\n\t}\n\n\t/**\n\t * Returns queue with packets waiting to send. For use by ConnectionManager which may need to get undelivered\n\t * packets\n\t *\n\t */\n\tpublic Queue<Packet> getWaitingPackets() {\n\t\treturn waitingPackets;\n\t}\n\n\t@Override\n\tpublic void forceStop() {\n\t\tlog.finest(() -> \"Stopping connection for user: \" + authorisedUserJid);\n\n\t\tboolean stop = false;\n\n\t\tif (processors != null) {\n\t\t\tfor (XMPPIOProcessor processor : processors) {\n\t\t\t\tstop |= processor.serviceStopped(this, false);\n\t\t\t}\n\t\t}\n\t\tif (!stop) {\n\t\t\tsuper.forceStop();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processWaitingPackets() throws IOException {\n\t\tPacket packet = null;\n\n\t\t// int cnt = 0;\n\t\t// while ((packet = waitingPackets.poll()) != null && (cnt < 1000)) {\n\n\t\t// we should only peek for packet now, and poll it after sending it\n\t\twhile ((packet = waitingPackets.peek()) != null) {\n\n\t\t\t// ++cnt;\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Sending packet: {1} [{0}]\", new Object[]{toString(), packet});\n\t\t\t}\n\t\t\twriteRawData(packet.getElement().toString());\n\n\t\t\t// and after sending it we should remove it to minimalize chances of lost packets\n\t\t\twaitingPackets.poll();\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"SENT: {1} [{0}]\", new Object[]{toString(), packet.getElement().toString()});\n\t\t\t}\n\t\t}    // end of while (packet = waitingPackets.poll() != null)\n\n\t\t// notify io processors that all waiting packets were sent\n\t\tif (processors != null) {\n\t\t\tfor (XMPPIOProcessor processor : processors) {\n\t\t\t\tprocessor.packetsSent(this);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void stop() {\n\n\t\t// if (!streamClosed) {\n\t\t// streamClosed = true;\n\t\t// serviceListener.xmppStreamClosed(this);\n\t\t// } // end of if (!streamClosed)\n\t\tsuper.stop();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"jid: \" + authorisedUserJid + \", \" + super.toString();\n\t}\n\n\tpublic void writeRawData(String data) throws IOException {\n\n\t\t// We change state of this object in this method\n\t\t// It can be called by many threads simultanously\n\t\t// so we need to make it thread-safe\n\t\t// writeLock.lock();\n\t\t// try {\n\t\twriteData(data);\n\n\t\t// } finally {\n\t\t// writeLock.unlock();\n\t\t// }\n\t}\n\n\tpublic void xmppStreamOpen(final String data) {\n\t\ttry {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Sending data: {1} [{0}]\", new Object[]{toString(), data});\n\t\t\t}\n\t\t\twriteRawData(data);\n\t\t\tassert debug(data, \"--SENT:\");\n\t\t} catch (IOException e) {\n\t\t\tlog.log(Level.WARNING, \"Error sending stream open data: {1} [{0}]\", new Object[]{toString(), e});\n\t\t\tforceStop();\n\t\t}\n\t}\n\n\tpublic long getPacketsReceived(boolean reset) {\n\t\tlong tmp = packetsReceived;\n\n\t\tif (reset) {\n\t\t\tpacketsReceived = 0;\n\t\t}\n\n\t\treturn tmp;\n\t}\n\n\tpublic long getPacketsSent(boolean reset) {\n\t\tlong tmp = packetsSent;\n\n\t\tif (reset) {\n\t\t\tpacketsSent = 0;\n\t\t}\n\n\t\treturn tmp;\n\t}\n\n\tpublic Queue<Packet> getReceivedPackets() {\n\t\treturn receivedPackets;\n\t}\n\n\tpublic long getTotalPacketsReceived() {\n\t\treturn totalPacketsReceived;\n\t}\n\n\tpublic long getTotalPacketsSent() {\n\t\treturn totalPacketsSent;\n\t}\n\n\tpublic Optional<JID> getAuthorisedUserJid() {\n\t\treturn Optional.ofNullable(authorisedUserJid);\n\t}\n\n\tpublic void setAuthorisedUserJid(JID authorisedUserJid) {\n\t\tthis.authorisedUserJid = authorisedUserJid;\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(removeIn = \"9.0.0\", since = \"8.2.0\", note = \"#getAuthorisedUserJid should be used instead\")\n\tpublic String getUserJid() {\n\t\treturn getAuthorisedUserJid().map(JID::toString).orElse(null);\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(removeIn = \"9.0.0\", since = \"8.2.0\", note = \"#setAuthorisedUserJid should be used instead\")\n\tpublic void setUserJid(String jid) {\n\t\tthis.authorisedUserJid = JID.jidInstanceNS(jid);\n\t}\n\n\tpublic Map<String, Packet> getWaitingForAct() {\n\t\tfor (Packet p : waitingForAck.values()) {\n\t\t\tElement req = p.getElement().getChild(REQ_NAME);\n\n\t\t\tif (req == null) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Missing req element in waiting for ACK packet: {1} [{0}]\",\n\t\t\t\t\t\t\tnew Object[]{toString(), p});\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tp.getElement().removeChild(req);\n\t\t\t}\n\t\t}\n\n\t\treturn waitingForAck;\n\t}\n\n\tpublic String getXMLNS() {\n\t\treturn this.xmlns;\n\t}\n\n\tpublic void setXMLNS(String xmlns) {\n\t\tthis.xmlns = xmlns;\n\t}\n\n\tpublic void setAckMode(boolean white_char_ack, boolean xmpp_ack, boolean strict) {\n\t\tthis.white_char_ack = white_char_ack;\n\t\tthis.xmpp_ack = xmpp_ack;\n\t\tthis.strict_ack = strict;\n\t}\n\n\tpublic void setElementLimits(int limit) {\n\n\t}\n\n\t@SuppressWarnings({\"unchecked\", \"rawtypes\"})\n\tpublic void setIOServiceListener(XMPPIOServiceListener servList) {\n\t\tthis.serviceListener = servList;\n\t\tsuper.setIOServiceListener(servList);\n\t}\n\n\tpublic void setProcessors(XMPPIOProcessor[] processors) {\n\t\tthis.processors = processors;\n\t}\n\n\t/**\n\t * This method returns the time when the last XMPP packet was received, it is used to help detect dead connections.\n\t *\n\t * @return {@code long} number denoting time when the last XMPP packet was received.\n\t */\n\tpublic long getLastXmppPacketReceiveTime() {\n\t\treturn lastXmppPacketReceivedTime;\n\t}\n\n\t/**\n\t * Method <code>addReceivedPacket</code> puts processing results to queue. The processing results are usually data\n\t * (messages) which has been just received from socket.\n\t *\n\t * @param packet a <code>Packet</code> value of processing results.\n\t */\n\tprotected void addReceivedPacket(final Packet packet) {\n\t\tif (firstPacket) {\n\t\t\tif (\"policy-file-request\" == packet.getElemName()) {\n\t\t\t\tlog.log(Level.FINER, \"Got flash cross-domain request\" + packet);\n\t\t\t\tString cross_domain_policy = (this.serviceListener instanceof ConnectionManager)\n\t\t\t\t\t\t\t\t\t\t\t ? ((ConnectionManager) serviceListener).getFlashCrossDomainPolicy()\n\t\t\t\t\t\t\t\t\t\t\t : null;\n\t\t\t\tif (cross_domain_policy != null) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\twriteRawData(cross_domain_policy);\n\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\tlog.log(Level.CONFIG, \"Can't send cross-domain policy: \", ex);\n\t\t\t\t\t}\n\t\t\t\t\tlog.log(Level.FINER, \"Cross-domain policy sent: {1}\", cross_domain_policy);\n\t\t\t\t} else {\n\t\t\t\t\tlog.log(Level.FINER, \"No cross-domain policy defined to sent.\");\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfirstPacket = false;\n\t\t}\n\t\tif (processors != null) {\n\t\t\tboolean stop = false;\n\n\t\t\tfor (XMPPIOProcessor processor : processors) {\n\t\t\t\tstop |= processor.processIncoming(this, packet);\n\t\t\t}\n\t\t\tif (stop) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (packet.getElemName() == ACK_NAME) {\n\t\t\tString ack_id = packet.getAttributeStaticStr(ID_ATT);\n\t\t} else {\n\t\t\tsendAck(packet);\n\t\t\tif (shouldCountPacket(packet)) {\n\t\t\t\t++packetsReceived;\n\t\t\t\t++totalPacketsReceived;\n\t\t\t}\n\t\t\tsetLastXmppPacketReceiveTime();\n\t\t\treceivedPackets.offer(packet);\n\t\t}\n\t}\n\n\tprotected boolean shouldCountPacket(Packet packet) {\n\t\treturn packet.getXMLNS() != StreamManagementIOProcessor.XMLNS;\n\t}\n\n\tprotected String prepareStreamClose() {\n\t\treturn \"</stream:stream>\";\n\t}\n\n\t@Override\n\tprotected void processSocketData() throws IOException {\n\n\t\t// We change state of this object in this method\n\t\t// It can be called by many threads simultanously\n\t\t// so we need to make it thread-safe\n\t\t// log.finer(\"About to read socket data.\");\n\t\t// Correction:\n\t\t// The design is that this method should not be called concurrently by\n\t\t// multiple threads. However it may happen in some specific cases.\n\t\t// There is a 'non-blocking' synchronization in IOService.call() method\n\t\t// implemented instead.\n\t\t// readLock.lock();\n\t\t// try {\n\t\tif (isConnected()) {\n\t\t\tchar[] data = readData();\n\n\t\t\twhile (isConnected() && (data != null) && (data.length > 0)) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"READ:{1} [{0}]\", new Object[]{toString(), new String(data)});\n\t\t\t\t}\n\n\t\t\t\tboolean disconnect = checkData(data);\n\n\t\t\t\tif (disconnect) {\n\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\tlog.log(Level.FINE, \"checkData says disconnect: {1} [{0}]\",\n\t\t\t\t\t\t\t\tnew Object[]{toString(), new String(data)});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlog.log(Level.CONFIG, \"checkData says disconnect [{0}]\", toString());\n\t\t\t\t\t}\n\t\t\t\t\tforceStop();\n\n\t\t\t\t\treturn;\n\n\t\t\t\t\t// domHandler = new XMPPDomBuilderHandler<RefObject>(this);\n\t\t\t\t}\n\n\t\t\t\t// This is log for debugging only,\n\t\t\t\t// in normal mode don't even call below code\n\t\t\t\tassert debug(new String(data), \"--RECEIVED:\");\n\n\t\t\t\ttry {\n\t\t\t\t\tparser.parse(domHandler, data, 0, data.length);\n\t\t\t\t\tif (domHandler.parseError()) {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\t\tlog.log(Level.FINE, \"Data parsing error: {1} [{0}]\",\n\t\t\t\t\t\t\t\t\tnew Object[]{toString(), StringUtilities.convertNonPrintableCharactersToLiterals(new String(data))});\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlog.log(Level.CONFIG, \"Data parsing error, stopping connection [{0}]\", toString());\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (serviceListener != null) {\n\t\t\t\t\t\t\tElement err = new Element(\"not-well-formed\", new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{\"urn:ietf:params:xml:ns:xmpp-streams\"});\n\t\t\t\t\t\t\tString streamErrorStr = serviceListener.xmppStreamError(this,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCollections.singletonList(err));\n\t\t\t\t\t\t\twriteRawData(streamErrorStr);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tforceStop();\n\n\t\t\t\t\t\treturn;\n\n\t\t\t\t\t\t// domHandler = new XMPPDomBuilderHandler<RefObject>(this);\n\t\t\t\t\t}\n\n\t\t\t\t\tmoveParsedPacketsToReceived(true);\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tlog.log(Level.INFO,\n\t\t\t\t\t\t\t\"Incorrect XML data: \" + new String(data) + \", stopping connection \" + \" [\" + toString() +\n\t\t\t\t\t\t\t\t\t\"] exception: \", ex);\n\t\t\t\t\tforceStop();\n\t\t\t\t} finally {\n\t\t\t\t\tif (domHandler.isStreamClosed()) {\n\t\t\t\t\t\txmppStreamClosed();\n\t\t\t\t\t}\n\t\t\t\t}  // end of try-catch\n\t\t\t\tdata = readData();\n\t\t\t}\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"Function called when the service is not connected! forceStop() [{0}]\", toString());\n\t\t\t}\n\t\t\tforceStop();\n\t\t}\n\t}\n\n\t@Override\n\tprotected int receivedPackets() {\n\t\treturn receivedPackets.size();\n\t}\n\n\t@SuppressWarnings({\"unchecked\"})\n\tprotected void xmppStreamClosed() {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Received STREAM-CLOSE from the client [{0}]\", toString());\n\t\t}\n\n\t\tif (processors != null) {\n\t\t\tfor (XMPPIOProcessor processor : processors) {\n\t\t\t\tprocessor.serviceStopped(this, true);\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tif (isConnected()) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Sending data: </stream:stream>, as socket is still connected [{0}]\",\n\t\t\t\t\t\t\ttoString());\n\t\t\t\t}\n\t\t\t\twriteRawData(prepareStreamClose());\n\t\t\t} else {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Not sending data: </stream:stream>, as socket is already closed [{0}]\",\n\t\t\t\t\t\t\ttoString());\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (IOException e) {\n\t\t\tlog.log(Level.CONFIG, \"Error sending stream closed data: {1} [{0}]\", new Object[]{toString(), e});\n\t\t}\n\n\t\t// streamClosed = true;\n\t\tif (serviceListener != null) {\n\t\t\tserviceListener.xmppStreamClosed(this);\n\t\t}\n\t}\n\n\t@SuppressWarnings({\"unchecked\"})\n\tprotected void xmppStreamOpened(Map<String, String> attribs) {\n\t\tif (serviceListener != null) {\n\t\t\tString[] responses = serviceListener.xmppStreamOpened(this, attribs);\n\n\t\t\ttry {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Sending data: {1} [{0}]\", new Object[]{toString(), (responses != null ? String.join(\"\", responses) : \"null\")});\n\t\t\t\t}\n\t\t\t\tif (responses == null) {\n\t\t\t\t\tif (writeInProgress.tryLock()) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\twriteRawData(null);\n\t\t\t\t\t\t\tprocessWaitingPackets();\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\twriteInProgress.unlock();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\twriteInProgress.lock();\n\t\t\t\t\ttry {\n\t\t\t\t\t\tfor (String response : responses) {\n\t\t\t\t\t\t\twriteRawData(response);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tprocessWaitingPackets();\n\t\t\t\t\t} finally {\n\t\t\t\t\t\twriteInProgress.unlock();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif ((responses != null) && responses[responses.length-1].endsWith(\"</stream:stream>\")) {\n\t\t\t\t\tstop();\n\t\t\t\t}    // end of if (response.endsWith())\n\t\t\t} catch (IOException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Error sending stream open data: {1} [{0}]\", new Object[]{toString(), e});\n\t\t\t\tforceStop();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void sendAck(Packet packet) {\n\n\t\t// If stanza receiving confirmation is configured,\n\t\t// try to send confirmation back\n\t\tif (white_char_ack || xmpp_ack) {\n\t\t\tString ack = null;\n\n\t\t\tif (white_char_ack) {\n\n\t\t\t\t// If confirming via white space is enabled then prepare space ack.\n\t\t\t\tack = \" \";\n\t\t\t}\n\t\t\tif (xmpp_ack) {\n\t\t\t\tElement req = packet.getElement().getChild(REQ_NAME);\n\n\t\t\t\tif (req != null) {\n\t\t\t\t\tpacket.getElement().removeChild(req);\n\n\t\t\t\t\tString req_val = req.getAttributeStaticStr(ID_ATT);\n\n\t\t\t\t\tif (req_val != null) {\n\n\t\t\t\t\t\t// XMPP ack might be enabled in configuration but the client may not\n\t\t\t\t\t\t// support it. In such a case we do not send XMPP ack.\n\t\t\t\t\t\tack = \"<\" + ACK_NAME + \" \" + ID_ATT + \"=\\\"\" + req_val + \"\\\"/>\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (ack != null) {\n\t\t\t\ttry {\n\t\t\t\t\twriteRawData(ack);\n\t\t\t\t\tlog.log(Level.FINEST, \"Sent ack confirmation: '\" + ack + \"'\");\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\tforceStop();\n\t\t\t\t\tlog.log(Level.FINE, \"Can't send ack confirmation: '\" + ack + \"'\", ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected boolean hasParsedElements() {\n\t\treturn !domHandler.getParsedElements().isEmpty();\n\t}\n\n\tprotected void moveParsedPacketsToReceived(boolean sendAck) {\n\t\tElement elem = null;\n\t\tQueue<Element> elems = domHandler.getParsedElements();\n\n\t\tif (elems.size() > 0 && sendAck) {\n\t\t\treadCompleted();\n\t\t}\n\t\twhile ((elem = elems.poll()) != null) {\n\t\t\ttry {\n\t\t\t\t// assert debug(elem.toString() + \"\\n\");\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Read packet: {1} [{0}]\", new Object[]{toString(), elem});\n\t\t\t\t}\n\n\t\t\t\t// System.out.print(elem.toString());\n\t\t\t\tPacket pack = Packet.packetInstance(elem);\n\n\t\t\t\taddReceivedPacket(pack);\n\t\t\t\tif (sendAck) {\n\t\t\t\t\tsendAck(pack);\n\t\t\t\t}\n\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\tlog.log(Level.CONFIG, \"Incorrect to/from JID format for stanza: \" + elem.toString() + \" [\" + toString() + \"]\", ex);\n\t\t\t}\n\t\t}    // end of while ((elem = elems.poll()) != null)\n\t}\n\n\t/**\n\t * This method sets the time of last received XMPP packet, it is used to help detect dead connections.\n\t */\n\tprivate void setLastXmppPacketReceiveTime() {\n\t\tlastXmppPacketReceivedTime = System.currentTimeMillis();\n\t}\n}    // XMPPIOService\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/XMPPIOServiceListener.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport tigase.net.IOServiceListener;\nimport tigase.xml.Element;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Describe interface XMPPIOServiceListener here.\n * <br>\n * Created: Wed Feb  8 10:19:41 2006\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\npublic interface XMPPIOServiceListener<IO extends XMPPIOService<?>>\n\t\textends IOServiceListener<IO> {\n\n\tvoid xmppStreamClosed(IO serv);\n\n\tString[] xmppStreamOpened(IO serv, Map<String, String> attribs);\n\n\tString xmppStreamError(IO serv, List<Element> err_el);\n}    // XMPPIOServiceListener\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/XMPPImplIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.TigaseDBException;\nimport tigase.server.ComponentInfo;\nimport tigase.server.Packet;\nimport tigase.stats.StatisticsList;\nimport tigase.xml.Element;\n\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * This is a base interface for all session manager plugins. There are packet processing plugins, pre-processing,\n * post-processing and packet filters. They all have a common basic methods which are defined here.\n * <br>\n * Created: Sat Oct 14 16:11:22 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface XMPPImplIfc\n\t\textends Comparable<XMPPImplIfc> {\n\n\tpublic static final String CLIENT_XMLNS = \"jabber:client\";\n\n\t/**\n\t * By default the method uses {@link #supElementNamePaths() } and {@link #supTypes() } method results to determine\n\t * whether the plugin would process given packet. However, a plugin can implement own logic to determine packet\n\t * processing capabilities or conditions. Please note, this method must be very fast and efficient. No I/O\n\t * processing is recommended as it may impact performance of the whole system.\n\t *\n\t * @param packet is a <code>Packet</code> for processing.\n\t * @param conn is a user session object or null.\n\t *\n\t * @return returns <code>Authorization</code> enum value or null. Null means the plugin is simply not processing the\n\t * packet. {@link Authorization#AUTHORIZED} means the plugin can process the packet, any other {@link Authorization}\n\t * enum value means an error which has to be returned to the sender.\n\t */\n\tAuthorization canHandle(Packet packet, XMPPResourceConnection conn);\n\n\t/**\n\t * Methods returns a preferable number of threads/packets queues for the plugin. This number can be overwritten\n\t * through configuration settings, however, a default value should be reasonably good for most standard\n\t * installations. It is recommended to assign at least as much as twice a number of CPUs cores for I/O bound\n\t * processing and number a number equal to CPUs cores for fast processing not slowed down by any I/O.\n\t *\n\t * @return an integer preferred number of processing threads for the plugin.\n\t */\n\tdefault int concurrentQueuesNo() {\n\t\treturn Runtime.getRuntime().availableProcessors();\n\t}\n\n/**\n\t * Method <code>id</code> returns a unique ID of the plugin. Each plugin has own, unique ID which is used in the\n\t * configuration file to determine whether it needs to be loaded or not. In most cases the ID can be equal to XMLNS\n\t * of the packages processed by the plugin.\n\t *\n\t * @return a <code>String</code> value\n\t */\n\tString id();\n\n\t/**\n\t * Method <code>init</code> is called just after the plugin has been loaded into memory. The idea behind this is to\n\t * allow it to initialize or check the database. This might be especially useful for plugins which want to have a\n\t * database access via non-standard stored procedures or need schema upgrade.\n\t *\n\t * @param settings is a Map with initial processor settings from the configuration file.\n\t *\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tvoid init(Map<String, Object> settings) throws TigaseDBException;\n\n\t/**\n\t * Method <code>supDiscoFeatures</code> returns an array of XML <code>Element</code>s with service discovery\n\t * features which have to be returned to the client uppon request. Service discovery features returned by this\n\t * method correspond to services supported by this plugin.\n\t *\n\t * @param session a <code>XMPPResourceConnection</code> value\n\t *\n\t * @return an <code>Element[]</code> value\n\t */\n\tElement[] supDiscoFeatures(XMPPResourceConnection session);\n\n\t/**\n\t * Method <code>supElementNamePaths</code> returns an array of element names in form of a full path to the XML\n\t * element for stanzas which can be processed by this plugin. Each element name path corresponds to XMLNS returned\n\t * in array by <code>supNamespaces()</code> method. The element path itself is represented by a String array with\n\t * each path element as a separate String.\n\t *\n\t * @return a <code>String[][]</code> value is an array for element paths for which the plugin offers processing\n\t * capabilities. Each path is in form of a String array in order to reduce parsing overhead.\n\t */\n\tString[][] supElementNamePaths();\n\n\t/**\n\t * Method <code>supNamespaces</code> returns an array of name-spaces for stanzas which can be processed by this\n\t * plugin. Each namespace corresponds to element name returned in array by <code>supElemenets()</code> method.\n\t *\n\t * @return a <code>String[]</code> value\n\t */\n\tString[] supNamespaces();\n\n\t/**\n\t * Method <code>supStreamFeatures</code> returns an array of XML <code>Element</code>s with stream features which\n\t * have to be returned to the client uppon request. Stream features returned by this method correspond to features\n\t * supported by this plugin.\n\t *\n\t * @param session a <code>XMPPResourceConnection</code> value\n\t *\n\t * @return an <code>Element[]</code> value\n\t */\n\tElement[] supStreamFeatures(XMPPResourceConnection session);\n\n\t/**\n\t * Method returns an array of all stanza types which the plugin is able to handle. If the method returns NULL, then\n\t * all stanzas of all types will be passed to the plugin for processing. Otherwise only stanzas with selected types,\n\t * assuming that element names and name-spaces match as well.\n\t *\n\t * @return a <code>StanzaType[]</code> array of supported stanza types.\n\t */\n\tSet<StanzaType> supTypes();\n\n\t/**\n\t * Allows to obtain various informations about components\n\t *\n\t * @return information about particular component\n\t */\n\tComponentInfo getComponentInfo();\n\n\t/**\n\t * The method allows to retrieve plugin own statistics if it generates any.\n\t *\n\t * @param list is a statistics collection to which plugins own metrics can be added.\n\t */\n\tvoid getStatistics(StatisticsList list);\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/XMPPPacketFilterIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.server.Packet;\n\nimport java.util.Queue;\n\n/**\n * Created: Dec 30, 2008 12:53:16 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface XMPPPacketFilterIfc\n\t\textends XMPPImplIfc {\n\n\tvoid filter(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo, Queue<Packet> results);\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/XMPPParserException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\n/**\n * Describe class XMPPParserException here.\n * <br>\n * Created: Tue Oct  9 13:41:43 2007\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class XMPPParserException\n\t\textends RuntimeException {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * Creates a new <code>XMPPParserException</code> instance.\n\t */\n\tpublic XMPPParserException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic XMPPParserException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/XMPPPostprocessorIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.server.Packet;\n\nimport java.util.Map;\nimport java.util.Queue;\n\n/**\n * Describe interface XMPPPostprocessorIfc here.\n * <br>\n * Created: Sat Oct 14 17:41:09 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface XMPPPostprocessorIfc\n\t\textends XMPPImplIfc {\n\n\t/**\n\t * Performs processing of <code>packet</code> for which there was no processor.\n\t *\n\t * @param packet packet is which being processed. This parameter may never be null. Even though this is not\n\t * immutable object it mustn't be altered. None of it's fields or attributes can be changed during processing.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data. It allows for storing information in a permanent storage or in memory only during the live of\n\t * the online session. This parameter can be null if there is no online user session at the time of the packet\n\t * processing.\n\t * @param repo this is a user data storage which is normally used when the user session (parameter above) is null.\n\t * This is repository allows for a very restricted access only. It allows for storing some user private data\n\t * (doesn't allow overwriting existing data) like messages for offline users and it also allows for reading user\n\t * public data like VCard.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * Regardless a response to a user request is sent or the packet is forwarded to it's destination it is always\n\t * required that a copy of the input packet is created and stored in the results queue.\n\t * @param settings this map keeps plugin specific settings loaded from the Tigase server configuration. In most\n\t * cases it is unused, however if the plugin needs to access an external database that this is a way to pass\n\t * database connection string to the plugin.\n\t */\n\tvoid postProcess(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t Map<String, Object> settings);\n}    // XMPPPostprocessorIfc\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/XMPPPreprocessorIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.server.Packet;\n\nimport java.util.Map;\nimport java.util.Queue;\n\n/**\n * Describe interface XMPPPreprocessorIfc here.\n * <br>\n * Created: Sat Oct 14 16:16:50 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface XMPPPreprocessorIfc\n\t\textends XMPPImplIfc {\n\n\t/**\n\t * Performs pre-processing of <code>packet</code>. Intention for the pre-processors is to allow them for packet\n\t * blocking.\n\t *\n\t * @param packet packet is which being processed. This parameter may never be null. Even though this is not\n\t * immutable object it mustn't be altered. None of it's fields or attributes can be changed during processing.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data. It allows for storing information in a permanent storage or in memory only during the live of\n\t * the online session. This parameter can be null if there is no online user session at the time of the packet\n\t * processing.\n\t * @param repo this is a user data storage which is normally used when the user session (parameter above) is null.\n\t * This is repository allows for a very restricted access only. It allows for storing some user private data\n\t * (doesn't allow overwriting existing data) like messages for offline users and it also allows for reading user\n\t * public data like VCard.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * Regardless a response to a user request is sent or the packet is forwarded to it's destination it is always\n\t * required that a copy of the input packet is created and stored in the results queue.\n\t * @param settings this map keeps plugin specific settings loaded from the Tigase server configuration. In most\n\t * cases it is unused, however if the plugin needs to access an external database that this is a way to pass\n\t * database connection string to the plugin.\n\t *\n\t * @return the pre-processing result; if the value is ‘true’ then the packet is blocked and no further processing is\n\t * performed.\n\t */\n\tboolean preProcess(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t   Map<String, Object> settings);\n}    // XMPPPreprocessorIfc\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/XMPPProcessor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport tigase.db.TigaseDBException;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.ComponentInfo;\nimport tigase.server.Packet;\nimport tigase.stats.StatisticsList;\nimport tigase.xml.Element;\n\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * <code>XMPPProcessor</code> abstract class contains basic definition for <em>XMPP</em> processor. To create new\n * processor implementing particular <em>XMPP</em> functionality it is enough to extend this class and implement one\n * abstract method.<br> Additionally to allow system properly recognise this processor you need also to implement own\n * constructor which sets proper values to parent constructor. You must implement exactly one constructor with zero\n * parameters which calls parent constructor with proper values. Refer to constructor documentation for information\n * about required parameters.<br> To fully interact with entity connected to the session or with other entities in\n * <em>XMPP</em> network you should be also familiar with <code>addReply(...)</code>, <code>addMessage(...)</code> and\n * <code>addBroadcast(...)</code> methods.<br> There is also partially implemented functionality to send messages to\n * entities in other networks like <em>SMTP</em> or other implemented by the server. Once this implementation is\n * finished there will be more information available. If you, however, are interested in this particular feature send a\n * question to author.\n * <br>\n * <p> Created: Tue Oct  5 20:31:23 2004 </p>\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic abstract class XMPPProcessor\n\t\timplements XMPPImplIfc, XMPPProcessorConcurrencyAwareIfc {\n\n\tprotected static final String ALL_NAMES = \"*\";\n\n\tprotected static final String[][] ALL_PATHS = {{\"*\"}};\n\tprivate static final Logger log = Logger.getLogger(XMPPProcessor.class.getName());\n\tprotected static ComponentInfo cmpInfo = null;\n\t@ConfigField(desc = \"Queue size which should be used by processor\")\n\tprivate Integer queueSize = null;\n\t@ConfigField(desc = \"Numbers of threads which should be used by processor\")\n\tprivate int threadsNo = concurrentQueuesNo();\n\n\t{\n\t\tcmpInfo = new ComponentInfo(id(), this.getClass());\n\t}\n\n\tprotected XMPPProcessor() {\n\t}\n\n\t@Override\n\tpublic Authorization canHandle(Packet packet, XMPPResourceConnection conn) {\n\t\tAuthorization result = null;\n\t\tString[][] elemPaths = supElementNamePaths();\n\n\t\tif (elemPaths != null) {\n\n\t\t\t// This is the new API style\n\t\t\tString[] elemXMLNS = supNamespaces();\n\t\t\tSet<StanzaType> types = supTypes();\n\n\t\t\tresult = checkPacket(packet, elemPaths, elemXMLNS, types);\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"{0} ({1}), authorization/canHandle: {4}, Request: \" + \"{2}, conn: {3}\",\n\t\t\t\t\tnew Object[]{this.getClass().getSimpleName(), id(), packet, conn, result});\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic final int compareTo(XMPPImplIfc proc) {\n\t\treturn getClass().getName().compareTo(proc.getClass().getName());\n\t}\n\n\t@Override\n\tpublic int concurrentQueuesNo() {\n\t\treturn 1;\n\t}\n\n\t@Override\n\t@Deprecated\n\tpublic void init(Map<String, Object> settings) throws TigaseDBException {\n\t}\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(final XMPPResourceConnection session) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Element[] supStreamFeatures(final XMPPResourceConnection session) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Set<StanzaType> supTypes() {\n\t\treturn null;\n\t}\n\n\tpublic XMPPProcessor getInstance() {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t}\n\n\t@Override\n\tpublic ComponentInfo getComponentInfo() {\n\t\tif (cmpInfo == null) {\n\t\t\tcmpInfo = new ComponentInfo(id(), this.getClass());\n\t\t}\n\t\treturn cmpInfo;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn String.valueOf(getComponentInfo());\n\t}\n\n\t@Override\n\tpublic int getThreadsNo() {\n\t\treturn threadsNo;\n\t}\n\n\t@Override\n\tpublic Integer getQueueSize() {\n\t\treturn queueSize;\n\t}\n\n\tprivate Authorization checkPacket(Packet packet, String[][] elemPaths, String[] elemXMLNS, Set<StanzaType> types) {\n\t\tAuthorization result = null;\n\t\tboolean names_ok = elemPaths == ALL_PATHS;\n\n\t\tif (!names_ok) {\n\t\t\tfor (int i = 0; i < elemPaths.length; i++) {\n\t\t\t\tif (packet.isXMLNSStaticStr(elemPaths[i], elemXMLNS[i])) {\n\t\t\t\t\tnames_ok = true;\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (names_ok && ((types == null) || types.contains(packet.getType()))) {\n\t\t\tresult = Authorization.AUTHORIZED;\n\t\t}\n\n\t\treturn result;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/XMPPProcessorAbstract.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.server.Iq;\nimport tigase.server.Message;\nimport tigase.server.Packet;\nimport tigase.xmpp.impl.annotation.AnnotatedXMPPProcessor;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Utility abstract class detecting what kind of packet is processed. Releases developers from checking whether the\n * packet is addressed to the user of the session or from the user of the sesion or packet to the server itself.\n * <br>\n * Created: Mar 1, 2010 10:21:29 AM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic abstract class XMPPProcessorAbstract\n\t\textends AnnotatedXMPPProcessor\n\t\timplements XMPPProcessorIfc {\n\n\tprivate static final Logger log = Logger.getLogger(XMPPProcessorAbstract.class.getName());\n\n\tpublic static boolean isFromUserSession(Packet packet, XMPPResourceConnection session) {\n\t\tboolean b = false;\n\t\ttry {\n\t\t\tb = session != null && session.getConnectionId().equals(packet.getPacketFrom());\n\t\t} catch (NoConnectionIdException e) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"There is no connectionId for given session: {0}, for packet={1}\",\n\t\t\t\t\t\tnew Object[]{session, packet});\n\t\t\t}\n\t\t}\n\t\treturn b;\n\t}\n\n\tpublic static boolean isNullSession(Packet packet, XMPPResourceConnection session) {\n\t\treturn session == null;\n\t}\n\n\tpublic static boolean isServerSession(Packet packet, XMPPResourceConnection session) {\n\t\treturn session != null && session.isServerSession();\n\t}\n\n\tpublic static boolean isToUserSession(Packet packet, XMPPResourceConnection session) {\n\t\tboolean b = false;\n\t\ttry {\n\t\t\tb = session != null && session.isUserId(packet.getStanzaTo().getBareJID());\n\t\t} catch (NotAuthorizedException e) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Use session is not yet authorised: {0}, for packet={1}\",\n\t\t\t\t\t\tnew Object[]{session, packet});\n\t\t\t}\n\n\t\t}\n\t\treturn b;\n\t}\n\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\t\ttry {\n\t\t\tif (session == null) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Calling method: {0}, for packet={1}, for session={2}\",\n\t\t\t\t\t\t\tnew Object[]{\"processNullSessionPacket\", packet, session});\n\t\t\t\t}\n\t\t\t\tprocessNullSessionPacket(packet, repo, results, settings);\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (session.isServerSession()) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Calling method: {0}, for packet={1}, for session={2}\",\n\t\t\t\t\t\t\tnew Object[]{\"processServerSessionPacket\", packet, session});\n\t\t\t\t}\n\t\t\t\tprocessServerSessionPacket(packet, session, repo, results, settings);\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tJID connectionId = session.getConnectionId();\n\n\t\t\t\tif (connectionId.equals(packet.getPacketFrom())) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Calling method: {0}, for packet={1}, for session={2}\",\n\t\t\t\t\t\t\t\tnew Object[]{\"processFromUserPacket\", packet, session});\n\t\t\t\t\t}\n\t\t\t\t\tprocessFromUserPacket(connectionId, packet, session, repo, results, settings);\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch (NoConnectionIdException ex) {\n\t\t\t\tlog.log(Level.WARNING, \"This should not happen, this is not a server session and \" +\n\t\t\t\t\t\t\"still connection id is not set: \" + session + \", packet: \" + packet, ex);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tif (session.isUserId(packet.getStanzaTo().getBareJID())) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Calling method: {0}, for packet={1}, for session={2}\",\n\t\t\t\t\t\t\t\tnew Object[]{\"processToUserPacket\", packet, session});\n\t\t\t\t\t}\n\t\t\t\t\tprocessToUserPacket(packet, session, repo, results, settings);\n\t\t\t\t}\n\t\t\t} catch (NotAuthorizedException ex) {\n\t\t\t\tlog.log(Level.FINE,\n\t\t\t\t\t\t\"Packet to a user session which is not yet authenticated: \" + session + \", packet: \" + packet);\n\t\t\t}\n\t\t} catch (PacketErrorTypeException ex) {\n\t\t\tlog.log(Level.CONFIG, \"Attempt to send an error response to the error packet: \" + packet + \", session: \" + session +\n\t\t\t\t\t\t\t \", plugin: \" + id());\n\t\t}\n\t}\n\n\t/**\n\t * The method is called when a packet is sent from the user, owner of the session somewhere else to other XMPP\n\t * entity (other user on the server, other user on a different server, different server, component, transport,\n\t * etc....). The default implementation just forwards the packet doing nothing else, which is good enough in most\n\t * cases. You can overwrite the method to change the default behaviour.\n\t *\n\t * @param connectionId is a <code>JID</code> instance with the session connection ID.\n\t * @param session is a <code>XMPPResourceConnection</code> instance with all the sending user session data.\n\t * @param packet is a <code>Packet</code> sent by the user.\n\t * @param repo is a <code>NonAuthUserRepository</code> instance giving access to a part of the user repository which\n\t * is accessible regardless the session is authenticated or not.\n\t * @param results is a packets <code>Queue</code> with all the processing results from the plugin.\n\t * @param settings is a <code>Map</code> with all the configuration settings passed to the plugin.\n\t *\n\t * @throws PacketErrorTypeException on attempt to send an error response to the error packet.\n\t */\n\tpublic void processFromUserOutPacket(JID connectionId, Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t Map<String, Object> settings) throws PacketErrorTypeException {\n\t\tresults.offer(packet.copyElementOnly());\n\t}\n\n\t/**\n\t * The method is called for all packets sent by the user, owner of this connection/session. Please note, it is\n\t * likely that a user sends a packet addressed to his own server, like get server version information. In such a\n\t * case only this method is called. Such a packet is not later passed to the <code>processServerSessionPacket(...)</code>.\n\t * Note, the default implementation checks whether the packet is addressed to the server or is being sent to another\n\t * XMPP entity. In the first case it calls <code>processFromUserToServerPacket(...)</code> method, otherwise it\n\t * calls <code>processFromUserOutPacket</code>. You can overwrite the method to change the default behavior or\n\t * implement the two called methods to handle each case separately.\n\t *\n\t * @param connectionId is a <code>JID</code> instance with the session connection ID.\n\t * @param session is a <code>XMPPResourceConnection</code> instance with all the sending user session data.\n\t * @param packet is a <code>Packet</code> sent by the user.\n\t * @param repo is a <code>NonAuthUserRepository</code> instance giving access to a part of the user repository which\n\t * is accessible regardless the session is authenticated or not.\n\t * @param results is a packets <code>Queue</code> with all the processing results from the plugin.\n\t * @param settings is a <code>Map</code> with all the configuration settings passed to the plugin.\n\t *\n\t * @throws PacketErrorTypeException on attempt to send an error response to the error packet.\n\t */\n\tpublic void processFromUserPacket(JID connectionId, Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t  NonAuthUserRepository repo, Queue<Packet> results, Map<String, Object> settings)\n\t\t\tthrows PacketErrorTypeException {\n\t\ttry {\n\n\t\t\t// Check whether the packet is addressed to the server or some other, XMPP entity\n\t\t\tif ((packet.getStanzaTo() == null) || session.isLocalDomain(packet.getStanzaTo().toString(), false) ||\n\t\t\t\t\tsession.isUserId(packet.getStanzaTo().getBareJID())) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Calling method: {0}, for packet={1}, for session={2}\",\n\t\t\t\t\t\t\tnew Object[]{\"processFromUserToServerPacket\", packet, session});\n\t\t\t\t}\n\t\t\t\tprocessFromUserToServerPacket(connectionId, packet, session, repo, results, settings);\n\t\t\t} else {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Calling method: {0}, for packet={1}, for session={2}\",\n\t\t\t\t\t\t\tnew Object[]{\"processFromUserOutPacket\", packet, session});\n\t\t\t\t}\n\t\t\t\tprocessFromUserOutPacket(connectionId, packet, session, repo, results, settings);\n\t\t\t}\n\t\t} catch (NotAuthorizedException ex) {\n\t\t\tlog.log(Level.CONFIG, \"Session not yet authorized to send ping requests: \" + session + \", packet: \" + packet);\n\t\t}\n\t}\n\n\t/**\n\t * The method is called when a packet is send from the user who is owner of the session to the local server (ping,\n\t * roster management, privacy lists, etc...). There is no default implementation for the method.\n\t *\n\t * @param connectionId is a <code>JID</code> instance with the session connection ID.\n\t * @param session is a <code>XMPPResourceConnection</code> instance with all the sending user session data.\n\t * @param packet is a <code>Packet</code> sent by the user.\n\t * @param repo is a <code>NonAuthUserRepository</code> instance giving access to a part of the user repository which\n\t * is accessible regardless the session is authenticated or not.\n\t * @param results is a packets <code>Queue</code> with all the processing results from the plugin.\n\t * @param settings is a <code>Map</code> with all the configuration settings passed to the plugin.\n\t *\n\t * @throws PacketErrorTypeException on attempt to send an error response to the error packet.\n\t */\n\tpublic abstract void processFromUserToServerPacket(JID connectionId, Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   Map<String, Object> settings) throws PacketErrorTypeException;\n\n\t/**\n\t * The method is called for packets received by the server for which no user session is found - neither sender\n\t * session or receiver session. The default implementation does nothing, just ignores such a packet. To change the\n\t * default behaviour the method has to be overwritten.\n\t *\n\t * @param packet is a <code>Packet</code> received by the server.\n\t * @param repo is a <code>NonAuthUserRepository</code> instance giving access to a part of the user repository which\n\t * is accessible regardless the session is authenticated or not.\n\t * @param results is a packets <code>Queue</code> with all the processing results from the plugin.\n\t * @param settings is a <code>Map</code> with all the configuration settings passed to the plugin.\n\t *\n\t * @throws PacketErrorTypeException on attempt to send an error response to the error packet.\n\t */\n\tpublic void processNullSessionPacket(Packet packet, NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t Map<String, Object> settings) throws PacketErrorTypeException {\n\n\t\t// Do nothing, which is a correct thing to do in most cases.\n\t}\n\n\t/**\n\t * The method is called when a packet addressed to the server domain is received. Please note, if a local user sends\n\t * a packet to the server, the packet is handled by the <code>processFromUserPacket(...)</code> method. This method\n\t * is not called for such packets.\n\t *\n\t * @param session is a <code>XMPPResourceConnection</code> instance with all the server session data.\n\t * @param packet is a <code>Packet</code> received by the server and addressed to the server - the server virtual\n\t * domain name.\n\t * @param repo is a <code>NonAuthUserRepository</code> instance giving access to a part of the user repository which\n\t * is accessible regardless the session is authenticated or not.\n\t * @param results is a packets <code>Queue</code> with all the processing results from the plugin.\n\t * @param settings is a <code>Map</code> with all the configuration settings passed to the plugin.\n\t *\n\t * @throws PacketErrorTypeException on attempt to send an error response to the error packet.\n\t */\n\tpublic abstract void processServerSessionPacket(Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t\t\tNonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t\t\t\tMap<String, Object> settings) throws PacketErrorTypeException;\n\n\t/**\n\t * Method is called for all the packets sent TO the user - owner of the session. The default implementation just\n\t * forwards the packet to the user connection. To change the default behavior the method has to be overwritten.\n\t *\n\t * @param session is a <code>XMPPResourceConnection</code> instance with all the receiving user session data.\n\t * @param packet is a <code>Packet</code> received by the server and addressed to the server - the server virtual\n\t * domain name.\n\t * @param repo is a <code>NonAuthUserRepository</code> instance giving access to a part of the user repository which\n\t * is accessible regardless the session is authenticated or not.\n\t * @param results is a packets <code>Queue</code> with all the processing results from the plugin.\n\t * @param settings is a <code>Map</code> with all the configuration settings passed to the plugin.\n\t *\n\t * @throws PacketErrorTypeException on attempt to send an error response to the error packet.\n\t */\n\tpublic void processToUserPacket(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings)\n\t\t\tthrows PacketErrorTypeException {\n\t\ttry {\n\t\t\tList<XMPPResourceConnection> conns = new ArrayList<XMPPResourceConnection>(5);\n\t\t\tString resource = packet.getStanzaTo().getResource();\n\n\t\t\tif ((resource == null) && (packet.getElemName() == Message.ELEM_NAME)) {\n\t\t\t\tconns.addAll(session.getActiveSessions());\n\t\t\t} else {\n\t\t\t\tXMPPSession parentSession = session.getParentSession();\n\t\t\t\tif (parentSession != null) {\n\t\t\t\t\tXMPPResourceConnection con = parentSession.getResourceForResource(resource);\n\n\t\t\t\t\tif (con != null) {\n\t\t\t\t\t\tconns.add(con);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (conns.size() > 0) {\n\t\t\t\tfor (XMPPResourceConnection con : conns) {\n\t\t\t\t\tPacket result = packet.copyElementOnly();\n\n\t\t\t\t\tresult.setPacketTo(con.getConnectionId());\n\t\t\t\t\tresult.setPacketFrom(packet.getTo());\n\t\t\t\t\tresult.setStableId(packet.getStableId());\n\t\t\t\t\tresults.offer(result);\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Delivering packet: {0}, to session: {1}\", new Object[]{packet, con});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (packet.getElemName() == Iq.ELEM_NAME && (packet.getType().equals(StanzaType.error) || packet.getType().equals(StanzaType.result))) {\n\t\t\t\t\t// https://xmpp.org/rfcs/rfc6120.html#stanzas-semantics-iq\n\t\t\t\t\t// An entity that receives a stanza of type \"result\" or \"error\" MUST NOT respond to the stanza\n\t\t\t\t\t// by sending a further IQ response of type \"result\" or \"error\"; however, the requesting entity\n\t\t\t\t\t// MAY send another request (e.g., an IQ of type \"set\" to provide obligatory information discovered\n\t\t\t\t\t// through a get/result pair).\n\t\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\tPacket result = Authorization.RECIPIENT_UNAVAILABLE.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"The recipient is no longer available.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   true);\n\n\t\t\t\tresult.setPacketFrom(null);\n\t\t\t\tresult.setPacketTo(null);\n\t\t\t\tresults.offer(result);\n\t\t\t}\n\t\t} catch (NotAuthorizedException e) {\n\t\t\tlog.warning(\"NotAuthorizedException for packet: \" + packet);\n\t\t\tresults.offer(\n\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(packet, \"You must authorize session first.\", true));\n\t\t} catch (NoConnectionIdException ex) {\n\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\"This should not happen, this is not a server session and \" + \"still connection id is not set: \" +\n\t\t\t\t\t\t\tsession + \", packet: \" + packet, ex);\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/XMPPProcessorConcurrencyAwareIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\n/**\n * Created by andrzej on 27.06.2016.\n */\npublic interface XMPPProcessorConcurrencyAwareIfc {\n\n\tint getThreadsNo();\n\n\tInteger getQueueSize();\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/XMPPProcessorException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport tigase.server.Packet;\n\n/**\n * Created by bmalkow on 18.04.2017.\n */\npublic class XMPPProcessorException\n\t\textends XMPPException {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate Authorization errorCondition;\n\n\tprivate String text;\n\n\tpublic XMPPProcessorException(final Authorization errorCondition) {\n\t\tthis(errorCondition, (String) null, (String) null);\n\t}\n\n\t/**\n\t * @param text human readable message will be send to client\n\t */\n\tpublic XMPPProcessorException(Authorization errorCondition, String text) {\n\t\tthis(errorCondition, text, (String) null);\n\t}\n\n\tpublic XMPPProcessorException(Authorization errorCondition, String text, Throwable cause) {\n\t\tthis(errorCondition, text, (String) null, cause);\n\t}\n\n\t/**\n\t * @param message exception message for logging\n\t * @param text human readable message will be send to client\n\t */\n\tpublic XMPPProcessorException(Authorization errorCondition, String text, String message) {\n\t\tthis(errorCondition, text, message, null);\n\t}\n\n\tpublic XMPPProcessorException(Authorization errorCondition, String text, String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t\tthis.errorCondition = errorCondition;\n\t\tthis.text = text;\n\t}\n\n\t/**\n\t * @return Returns the code.\n\t */\n\tpublic String getCode() {\n\t\treturn String.valueOf(this.errorCondition.getErrorCode());\n\t}\n\n\tpublic Authorization getErrorCondition() {\n\t\treturn errorCondition;\n\t}\n\n\t@Override\n\tpublic String getMessage() {\n\t\tfinal StringBuilder sb = new StringBuilder();\n\t\tsb.append(getErrorMessagePrefix());\n\t\tsb.append(errorCondition.getCondition()).append(\" \");\n\t\tif (text != null) {\n\t\t\tsb.append(\"with message: \\\"\").append(text).append(\"\\\" \");\n\t\t}\n\t\tif (super.getMessage() != null) {\n\t\t\tsb.append(\"(\").append(super.getMessage()).append(\") \");\n\t\t}\n\n\t\treturn sb.toString();\n\t}\n\n\tpublic String getMessageWithPosition() {\n\t\tfinal StringBuilder sb = new StringBuilder();\n\t\tsb.append(getMessage());\n\n\t\tStackTraceElement[] stack = getStackTrace();\n\t\tif (stack.length > 0) {\n\t\t\tsb.append(\"generated by \");\n\t\t\tsb.append(getStackTrace()[0].toString());\n\t\t\tsb.append(\" \");\n\t\t}\n\n\t\treturn sb.toString();\n\t}\n\n\t/**\n\t * @return Returns the name.\n\t */\n\tpublic String getName() {\n\t\treturn errorCondition.getCondition();\n\t}\n\n\tpublic String getText() {\n\t\treturn text;\n\t}\n\n\t/**\n\t * @return Returns the type.\n\t */\n\tpublic String getType() {\n\t\treturn errorCondition.getErrorType();\n\t}\n\n\tpublic Packet makeElement(Packet packet, boolean insertOriginal) throws PacketErrorTypeException {\n\t\tPacket result = errorCondition.getResponseMessage(packet, text, insertOriginal);\n\t\treturn result;\n\t}\n\n\tprotected String getErrorMessagePrefix() {\n\t\treturn \"XMPP error condition: \";\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/XMPPProcessorIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.server.Packet;\n\nimport java.util.Map;\nimport java.util.Queue;\n\n/**\n * Describe interface XMPPProcessorIfc here.\n * <br>\n * Created: Wed Feb 8 13:47:56 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface XMPPProcessorIfc\n\t\textends XMPPImplIfc {\n\n\t/**\n\t * @param packet packet is which being processed. This parameter may never be null. Even though this is not\n\t * immutable object it mustn't be altered. None of it's fields or attributes can be changed during processing.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data. It allows for storing information in a permanent storage or in memory only during the live of\n\t * the online session. This parameter can be null if there is no online user session at the time of the packet\n\t * processing.\n\t * @param repo this is a user data storage which is normally used when the user session (parameter above) is null.\n\t * This is repository allows for a very restricted access only. It allows for storing some user private data\n\t * (doesn't allow overwriting existing data) like messages for offline users and it also allows for reading user\n\t * public data like VCard.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * Regardless a response to a user request is sent or the packet is forwarded to it's destination it is always\n\t * required that a copy of the input packet is created and stored in the results queue.\n\t * @param settings this map keeps plugin specific settings loaded from the Tigase server configuration. In most\n\t * cases it is unused, however if the plugin needs to access an external database that this is a way to pass\n\t * database connection string to the plugin.\n\t *\n\t */\n\tvoid process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t Map<String, Object> settings) throws XMPPException;\n} // XMPPProcessorIfc\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/XMPPResourceConnection.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.AuthRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserRepository;\nimport tigase.server.Packet;\nimport tigase.server.Presence;\nimport tigase.server.xmppsession.SessionManagerHandler;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.vhosts.VHostItem;\nimport tigase.xml.Element;\nimport tigase.xmpp.impl.JabberIqRegister;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Function;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Describe class XMPPResourceConnection here.\n * <br>\n * Created: Wed Feb 8 22:30:37 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class XMPPResourceConnection\n\t\textends RepositoryAccess {\n\n\tpublic static final String ALL_RESOURCES_CAPS_KEY = \"caps\";\n\n\tpublic static final String ALL_RESOURCES_KEY = \"all-resources\";\n\n\tpublic static final String ALL_RESOURCES_PRIORITY_KEY = \"priority\";\n\n\tpublic static final String AUTHENTICATION_TIMEOUT_KEY = \"authentication-timeout\";\n\n\tpublic static final String CLOSING_KEY = \"closing-conn\";\n\n\tpublic static final String ERROR_KEY = \"error-key\";\n\n\tpublic static final String ERROR_ELEMENT_KEY = \"error-element-key\";\n\n\tpublic static final String CONNECTION_CHECK_TIMESTAMP_KEY = \"connection-check-timestamp\";\n\n\t/**\n\t * Constant <code>PRESENCE_KEY</code> is a key in temporary session data where the last presence sent by the user to\n\t * server is stored, either initial presence or off-line presence before disconnecting.\n\t */\n\tpublic static final String PRESENCE_KEY = \"user-presence\";\n\n\tprivate static final Logger log = Logger.getLogger(XMPPResourceConnection.class.getName());\n\n\tprivate long authenticationTime = 0;\n\n\t/**\n\t * This variable is to keep relates XMPPIOService ID only.\n\t */\n\tprivate JID connectionId = null;\n\tprivate String connectionState = null;\n\tprivate long creationTime = 0;\n\tprivate String defLang = \"en\";\n\tprivate long id_counter = 0;\n\n\t/**\n\t * Value of <code>System.currentTimeMillis()</code> from the time when this session last active from user side.\n\t */\n\tprivate long lastAccessed = 0;\n\tprivate SessionManagerHandler loginHandler = null;\n\tprivate long packets_counter = 0;\n\tprivate XMPPSession parentSession = null;\n\tprivate int priority = -1;\n\n\t/**\n\t * Session resource - part of user's JID for this session\n\t */\n\tprivate String resource = null;\n\n\t/**\n\t * Session temporary data. All data stored in this <code>Map</code> disappear when session finishes.\n\t */\n\tprivate Map<String, Object> sessionData = null;\n\n\t/**\n\t * <code>sessionId</code> keeps XMPP stream session ID given at connection initialization time.\n\t */\n\tprivate String sessionId = null;\n\tprivate boolean tmpSession = false;\n\tprivate JID userJid = null;\n\n\tpublic XMPPResourceConnection(JID connectionId, UserRepository rep, AuthRepository authRepo,\n\t\t\t\t\t\t\t\t  SessionManagerHandler loginHandler) {\n\t\tsuper(rep, authRepo);\n\n\t\tlong currTime = System.currentTimeMillis();\n\n\t\tthis.connectionId = connectionId;\n\t\tthis.loginHandler = loginHandler;\n\t\tthis.creationTime = currTime;\n\t\tthis.lastAccessed = currTime;\n\t\tsessionData = new ConcurrentHashMap<String, Object>(4, 0.9f);\n\t}\n\n\tpublic void authorizeJID(BareJID jid, boolean anonymous) throws TigaseStringprepException {\n\t\tauthState = Authorization.AUTHORIZED;\n\t\tis_anonymous = anonymous;\n\t\tif (jid != null && getDomain().getVhost() != null &&\n\t\t\t\t!jid.getDomain().equals(getDomain().getVhost().getDomain())) {\n\t\t\tloginHandler.handleDomainChange(jid.getDomain(), this);\n\t\t}\n\t\tloginHandler.handleLogin(jid, this);\n//\t\tif (jid != null && getDomain().getVhost() != null && !jid.getDomain().equals(getDomain().getVhost().getDomain())) {\n//\t\t\tif (log.isLoggable(Level.INFO)) {\n//\t\t\t\tlog.log(Level.INFO, \"Replacing session VHost: {0} instead of {1}\", new Object[] { jid.getDomain(),\n//\t\t\t\t\t\tgetDomain().getVhost().getDomain() });\n//\t\t\t}\n//\t\t\tthis.userJid = JID.jidInstanceNS(jid.getLocalpart(), jid.getDomain(), this.userJid.getResource());\n//\t\t\tSessionManager sessMan = (SessionManager) XMPPServer.getConfigurator().getComponent(\"sess-man\");\n//\t\t\tVHostItem vHostItem = sessMan.getVHostItem(this.userJid.getDomain());\n//\t\t\tif (vHostItem == null) {\n//\t\t\t\tif (log.isLoggable(Level.INFO)) {\n//\t\t\t\t\tlog.log(Level.INFO, \"Can''t get VHostItem for domain: {0}, using default one instead: {1}\", new Object[] {\n//\t\t\t\t\t\t\tdomain, sessMan.getDefHostName() });\n//\t\t\t\t}\n//\t\t\t\tvHostItem = new VHostItem(sessMan.getDefHostName().getDomain());\n//\t\t\t}\n//\t\t\tsetDomain(vHostItem.getUnmodifiableVHostItem());\n//\t\t}\n\t\tlogin();\n\t}\n\n\t/**\n\t * Method checks if in {@code parentSession} in session data there is value for passed {@code key} and returns it if\n\t * exists. If not then it uses passed {@code valueFactory} to generate value and sets it in {@code parentSession} in\n\t * session data under passed {@code key} and returns newly set value\n\t */\n\tpublic Object computeCommonSessionDataIfAbsent(String key, Function<String, Object> valueFactory) {\n\t\tif (parentSession != null) {\n\t\t\treturn parentSession.computeCommonSessionDataIfAbsent(key, valueFactory);\n\t\t}\n\t\treturn valueFactory.apply(key);\n\t}\n\n\t/**\n\t * Method checks if in session data is value for passed {@code key} and returns it if exists. If not then it uses\n\t * passed {@code valueFactory} to generate value and sets it in session data under passed {@code key} and returns\n\t * newly set value\n\t */\n\tpublic Object computeSessionDataIfAbsent(String key, Function<String, Object> valueFactory) {\n\t\tsetLastAccessed(System.currentTimeMillis());\n\t\treturn sessionData.computeIfAbsent(key, valueFactory);\n\t}\n\n\t/**\n\t * Returns full user JID for this session without throwing the <code>NotAuthorizedException</code> exception if\n\t * session is not authorized yet and therefore user name and resource is not known yet. Please note this method is\n\t * for logging using only to avoid excessive use of try/catch for debugging code. It may return null.\n\t *\n\t * @return a <code>String</code> value of calculated user full JID for this session including resource name.\n\t */\n\tpublic final JID getjid() {\n\t\treturn userJid;\n\t}\n\n\tpublic void incPacketsCounter() {\n\t\t++packets_counter;\n\t}\n\n\t@Override\n\tpublic final void logout() throws NotAuthorizedException {\n\t\tloginHandler.handleLogout(getBareJID(), this);\n\t\tstreamClosed();\n\t\tsuper.logout();\n\t}\n\n\tpublic String nextStanzaId() {\n\t\treturn \"tig\" + (++id_counter);\n\t}\n\n\t/**\n\t * Method sets passed value under passed key in common sessionData kept in parentSession\n\t *\n\t */\n\tpublic void putCommonSessionData(String key, Object value) {\n\t\tif (parentSession != null) {\n\t\t\tparentSession.putCommonSessionData(key, value);\n\t\t}\n\t}\n\n\t/**\n\t * Method sets passed value under passed {@code key} in common {@code sessionData} kept in {@code parentSession} but\n\t * only if there is no value for this {@code key} already\n\t * @return previous value\n\t */\n\tpublic Object putCommonSessionDataIfAbsent(String key, Object value) {\n\t\tif (parentSession != null) {\n\t\t\treturn parentSession.putCommonSessionDataIfAbsent(key, value);\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Saves given session data. Data are saved to temporary storage only and are accessible during this session life\n\t * only and only from this session instance.<br> Any <code>Object</code> can be stored and retrieved through\n\t * <code>getSessionData(...)</code>.<br> To access permanent storage to keep data between session instances you must\n\t * use one of <code>get/setData...(...)</code> methods familly. They gives you access to hierachical permanent data\n\t * base. Permanent data base however can be accessed after successuf authorization while session storage is availble\n\t * all the time.\n\t *\n\t * @param key a <code>String</code> value of stored data key ID.\n\t * @param value a <code>Object</code> value of data stored in session.\n\t *\n\t * @see #getSessionData(String)\n\t */\n\tpublic final void putSessionData(final String key, final Object value) {\n\t\tsetLastAccessed(System.currentTimeMillis());\n\t\tsessionData.put(key, value);\n\t}\n\n\t/**\n\t * Method sets passed value under passed {@code key} in {@code sessionData} but only if there is no value for this\n\t * {@code key} already\n\t * @return previous value\n\t */\n\tpublic Object putSessionDataIfAbsent(String key, Object value) {\n\t\tsetLastAccessed(System.currentTimeMillis());\n\t\treturn sessionData.putIfAbsent(key, value);\n\t}\n\n\t@Override\n\tpublic void queryAuth(Map<String, Object> authProps) throws TigaseDBException {\n\t\tsuper.queryAuth(authProps);\n\t}\n\n\tpublic Object removeCommonSessionData(String key) {\n\t\treturn (parentSession == null) ? null : parentSession.removeCommonSessionData(key);\n\t}\n\n\tpublic void removeParentSession(final XMPPSession parent) {\n\t\tsynchronized (this) {\n\t\t\tparentSession = null;\n\t\t}\n\t}\n\n\tpublic final void removeSessionData(final String key) {\n\t\tsetLastAccessed(System.currentTimeMillis());\n\t\tsessionData.remove(key);\n\t}\n\n\tpublic void streamClosed() {\n\t\tsynchronized (this) {\n\t\t\tif (parentSession != null) {\n\t\t\t\tparentSession.streamClosed(this);\n\t\t\t\tparentSession = null;\n\t\t\t}\n\t\t}    // end of if (parentSession != null)\n\t\tresource = null;\n\t\t//sessionId = null;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"XMPPResourceConnection=[user_jid=\" + userJid + \", packets=\" + packets_counter + \", connectioId=\" +\n\t\t\t\tconnectionId + \", domain=\" + domain.getVhost().getDomain() + \", authState=\" + getAuthState().name() +\n\t\t\t\t\", isAnon=\" + isAnonymous() + \", isTmp=\" + isTmpSession() + \", parentSession hash=\" +\n\t\t\t\tSystem.identityHashCode(parentSession) + \", parentSession liveTime=\" +\n\t\t\t\t(parentSession != null ? parentSession.getLiveTime() : \"\") + \"]\";\n\t}\n\n\t/**\n\t * Method returns list of active connection for the same <code>XMPPSession</code>.\n\t *\n\t * @return a value of {@code List<XMPPResourceConnection>}\n\t *\n\t */\n\tpublic List<XMPPResourceConnection> getActiveSessions() throws NotAuthorizedException {\n\t\tif (!isAuthorized()) {\n\t\t\tthrow new NotAuthorizedException(NOT_AUTHORIZED_MSG);\n\t\t}    // end of if (username == null)\n\n\t\treturn parentSession.getActiveResources();\n\t}\n\n\t/**\n\t * Method returns list of jids of all connections for the same <code>XMPPSession</code> (same user).\n\t *\n\t * @return a value of <code>JID[]</code>\n\t */\n\tpublic JID[] getAllResourcesJIDs() {\n\t\treturn (parentSession == null) ? null : parentSession.getJIDs();\n\t}\n\n\tpublic AuthRepository getAuthRepository() {\n\t\treturn authRepo;\n\t}\n\n\tpublic long getAuthTime() {\n\t\treturn authenticationTime - creationTime;\n\t}\n\n\t@Override\n\tpublic final BareJID getBareJID() throws NotAuthorizedException {\n\t\tif (!isAuthorized()) {\n\t\t\tthrow new NotAuthorizedException(NOT_AUTHORIZED_MSG);\n\t\t}    // end of if (username == null)\n\n\t\treturn userJid.getBareJID();\n\t}\n\n\tpublic Object getCommonSessionData(String key) {\n\t\treturn (parentSession == null) ? null : parentSession.getCommonSessionData(key);\n\t}\n\n\t/**\n\t * Gets the value of connectionId\n\t *\n\t * @return the value of connectionId\n\t *\n\t */\n\tpublic JID getConnectionId() throws NoConnectionIdException {\n\t\treturn getConnectionId(true);\n\t}\n\n\tpublic JID getConnectionId(boolean updateLastAccessed) throws NoConnectionIdException {\n\t\tif (updateLastAccessed) {\n\t\t\tsetLastAccessed(System.currentTimeMillis());\n\t\t}\n\t\tif (this.connectionId == null) {\n\t\t\tthrow new NoConnectionIdException(\n\t\t\t\t\t\"Connection ID not set for this session. \" + \"This is probably the SM session to handle traffic \" +\n\t\t\t\t\t\t\t\"addressed to the server itself. Or maybe it's a bug.\");\n\t\t}\n\n\t\treturn this.connectionId;\n\t}\n\n\t/**\n\t * Sets the value of connectionId\n\t *\n\t * @param connectionId is a <code>JID</code>\n\t */\n\tpublic void setConnectionId(JID connectionId) {\n\t\tthis.connectionId = connectionId;\n\t}\n\n\tpublic JID getConnectionId(JID jid) throws NoConnectionIdException {\n\t\tJID result = null;\n\n\t\tif ((jid != null)) {\n\t\t\tif ((jid.getResource() != null) && (parentSession != null)) {\n\t\t\t\tXMPPResourceConnection conn = parentSession.getResourceForResource(jid.getResource());\n\n\t\t\t\tif (conn == null) {\n\t\t\t\t\tthrow new NoConnectionIdException(\"No connection available for given resource.\");\n\t\t\t\t} else {\n\t\t\t\t\tresult = conn.getConnectionId();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif ((jid.getResource() == null) || jid.getResource().equals(this.resource)) {\n\t\t\t\t\tresult = this.connectionId;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tresult = this.connectionId;\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tpublic long getCreationTime() {\n\t\treturn creationTime;\n\t}\n\n\tpublic String getDefLang() {\n\t\treturn this.defLang;\n\t}\n\n\tpublic void setDefLang(String lang) {\n\t\tthis.defLang = lang;\n\t}\n\n\t/**\n\t * Returns full user JID for this session or throws <code>NotAuthorizedException</code> if session is not authorized\n\t * yet and therefore user name and resource is not known yet.\n\t *\n\t * @return a <code>String</code> value of calculated user full JID for this session including resource name.\n\t *\n\t */\n\tpublic final JID getJID() throws NotAuthorizedException {\n\t\tif (!isAuthorized()) {\n\t\t\tthrow new NotAuthorizedException(NOT_AUTHORIZED_MSG);\n\t\t}    // end of if (username == null)\n\n\t\treturn userJid;\n\t}\n\n\t/**\n\t * Gets the value of lastAccessed\n\t *\n\t * @return the value of lastAccessed\n\t */\n\tpublic long getLastAccessed() {\n\t\treturn this.lastAccessed;\n\t}\n\n\t/**\n\t * Sets the value of lastAccessed\n\t *\n\t * @param argLastAccessed Value to assign to this.lastAccessed\n\t */\n\tpublic void setLastAccessed(final long argLastAccessed) {\n\t\tthis.lastAccessed = argLastAccessed;\n\t}\n\n\tpublic long getPacketsCounter() {\n\t\treturn packets_counter;\n\t}\n\n\tpublic XMPPSession getParentSession() {\n\t\treturn parentSession;\n\t}\n\n\tpublic void setParentSession(final XMPPSession parent) throws TigaseStringprepException {\n\t\tsynchronized (this) {\n\t\t\tif (parent != null) {\n\t\t\t\tuserJid = JID.jidInstance(parent.getUserName(), domain.getVhost().getDomain(),\n\t\t\t\t\t\t\t\t\t\t  ((resource != null) ? resource : sessionId));\n\t\t\t}\n\t\t\tthis.parentSession = parent;\n\t\t}\n\t}\n\n\t/**\n\t * Returns last presence packet with the user presence status or <code>null</code> if the user has not yet sent an\n\t * initial presence.\n\t *\n\t * @return an <code>Element</code> with last presence status received from the user.\n\t */\n\tpublic Element getPresence() {\n\t\treturn (Element) getSessionData(PRESENCE_KEY);\n\t}\n\n\tpublic void setPresence(Element packet) {\n\t\tputSessionData(PRESENCE_KEY, packet);\n\n\t\t// Parse resource priority:\n\t\tString pr_str = packet.getCDataStaticStr(Presence.PRESENCE_PRIORITY_PATH);\n\n\t\tif (pr_str != null) {\n\t\t\tint pr = 1;\n\n\t\t\ttry {\n\t\t\t\tpr = Integer.decode(pr_str);\n\t\t\t} catch (NumberFormatException e) {\n\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\tlog.finer(\"Incorrect priority value: \" + pr_str + \", setting 1 as default.\");\n\t\t\t\t}\n\t\t\t\tpr = 1;\n\t\t\t}\n\t\t\tsetPriority(pr);\n\t\t} else {\n\t\t\tsetPriority(0);\n\t\t\t// workaround for case when presence update came before presence was broadcasted due to\n\t\t\t// loading of roster data in roster processing thread\n\t\t\tif (getPriority() != 0 && !\"unavailable\".equals(packet.getAttributeStaticStr(\"type\"))) {\n\t\t\t\tpacket.addChild(new Element(\"priority\", String.valueOf(getPriority())));\n\t\t\t}\n\t\t\tputSessionData(PRESENCE_KEY, packet);\n\t\t}\n\t\tloginHandler.handlePresenceSet(this);\n\t}\n\n\tpublic int getPriority() {\n\t\treturn priority;\n\t}\n\n\tpublic void setPriority(final int priority) {\n\t\tthis.priority = priority;\n\t}\n\n\t/**\n\t * Gets the value of resource\n\t *\n\t * @return the value of resource\n\t */\n\tpublic String getResource() {\n\t\treturn this.resource;\n\t}\n\n\t/**\n\t * Sets the connection resource\n\t *\n\t * @param argResource Value to assign to this.resource\n\t *\n\t */\n\tpublic void setResource(final String argResource) throws NotAuthorizedException, TigaseStringprepException {\n\t\tif (!isAuthorized()) {\n\t\t\tthrow new NotAuthorizedException(NOT_AUTHORIZED_MSG);\n\t\t}    // end of if (username == null)\n\n\t\t// argResource shouldn't be null, but let's check that just in case\n\t\tif (argResource != null) {\n\t\t\t// added escaping to make sure that resource (later used in XML element attribute) is properly escaped\n\t\t\tthis.resource = argResource.replace(\"'\", \"&apos;\").replace(\"\\\"\", \"&quot;\");\n\t\t} else {\n\t\t\tthis.resource = argResource;\n\t\t}\n\n\t\t// This is really unlikely a parent session would be null here but it may\n\t\t// happen when the user disconnects just after sending resource bind.\n\t\t// Due to asynchronous nature of packets processing in the Tigase the\n\t\t// the authorization might be canceled while resource bind packet still\n\t\t// waits in the queue.....\n\t\t// This has already happened....\n\t\tif (parentSession != null) {\n\t\t\tparentSession.addResourceConnection(this);\n\t\t}\n\t\tuserJid = userJid.copyWithResource((resource == null) ? sessionId : resource);\n\t\tloginHandler.handleResourceBind(this);\n\t}\n\n\t/**\n\t * Retrieves session data. This method gives access to temporary session data only. You can retrieve earlier saved\n\t * data giving key ID to receive needed value. Please see <code>putSessionData</code> description for more details.\n\t *\n\t * @param key a <code>String</code> value of stored data ID.\n\t *\n\t * @return a <code>Object</code> value of data for given key.\n\t *\n\t * @see #putSessionData(String, Object)\n\t */\n\tpublic final Object getSessionData(final String key) {\n\t\tsetLastAccessed(System.currentTimeMillis());\n\n\t\treturn sessionData.get(key);\n\t}\n\n\t/**\n\t * Gets the value of sessionId\n\t *\n\t * @return the value of sessionId\n\t */\n\tpublic String getSessionId() {\n\t\treturn this.sessionId;\n\t}\n\n\t/**\n\t * Sets the value of sessionId\n\t *\n\t * @param argSessionId Value to assign to this.sessionId\n\t */\n\tpublic void setSessionId(final String argSessionId) {\n\t\tthis.sessionId = argSessionId;\n\t}\n\n\tpublic JID getSMComponentId() {\n\t\treturn loginHandler.getComponentId();\n\t}\n\n\t@Override\n\tpublic final String getUserName() throws NotAuthorizedException {\n\t\tif (!isAuthorized()) {\n\t\t\tthrow new NotAuthorizedException(NOT_AUTHORIZED_MSG);\n\t\t}    // end of if (username == null)\n\n\t\treturn parentSession.getUserName();\n\t}\n\n\t@Override\n\tpublic boolean isAuthorized() {\n\t\treturn super.isAuthorized() && (parentSession != null);\n\t}\n\n\tpublic boolean isLocalDomain(String outDomain, boolean includeComponents) {\n\t\treturn loginHandler.isLocalDomain(outDomain, includeComponents);\n\t}\n\n\tpublic boolean isResourceSet() {\n\t\treturn this.resource != null;\n\t}\n\n\t/**\n\t * Returns information whether this is a server (SessionManager) session or normal user session. The server session\n\t * is used to handle packets addressed to the server itself (local domain name).\n\t *\n\t * @return a <code>boolean</code> value of <code>true</code> if this is the server session and <code>false</code>\n\t * otherwise.\n\t */\n\tpublic boolean isServerSession() {\n\t\treturn false;\n\t}\n\n\tpublic boolean isTmpSession() {\n\t\treturn tmpSession;\n\t}\n\n\tpublic void setTmpSession(boolean tmp) {\n\t\ttmpSession = tmp;\n\t}\n\n\tpublic boolean isUserId(BareJID bareJID) throws NotAuthorizedException {\n\t\tif (!isAuthorized()) {\n\t\t\tthrow new NotAuthorizedException(NOT_AUTHORIZED_MSG);\n\t\t}    // end of if (username == null)\n\n\t\treturn userJid.getBareJID().equals(bareJID);\n\t}\n\n\t/**\n\t * @deprecated Code moved to {@link JabberIqRegister#doRemoveAccount(Packet, Element, XMPPResourceConnection, Queue)}\n\t */\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\")\n\tpublic Authorization unregister(String name_param)\n\t\t\tthrows NotAuthorizedException, TigaseDBException, TigaseStringprepException {\n\t\tAuthorization auth_res = super.unregister(name_param);\n\n\t\treturn auth_res;\n\t}\n\n\tpublic boolean isEncrypted() {\n\t\tBoolean ssl = (Boolean) getSessionData(\"SSL\");\n\t\tif (ssl != null && ssl) {\n\t\t\treturn true;\n\t\t}\n\t\tString tls = (String) getSessionData(\"starttls\");\n\t\treturn tls != null && \"true\".equals(tls);\n\t}\n\n\tpublic boolean isTlsRequired() {\n\t\tVHostItem vhost = getDomain();\n\t\ttry {\n\t\t\tif (\"c2s\".equals(getConnectionId().getLocalpart())) {\n\t\t\t\treturn vhost.isTlsRequired();\n\t\t\t} else {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} catch (NoConnectionIdException e) {\n\t\t\tlog.log(Level.WARNING, \"Can't check sessionId\", e);\n\t\t\treturn vhost.isTlsRequired();\n\t\t}\n\t}\n\n\t@Override\n\tprotected void login() {\n\t\tauthenticationTime = System.currentTimeMillis();\n\t}\n\n}    // XMPPResourceConnection\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/XMPPSession.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.ArrayDeque;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.function.Function;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * XMPPSession class is a container class for all {@link XMPPResourceConnection} objects for particular user (i.e. all\n * user's connected resources)\n */\npublic class XMPPSession {\n\n\t/**\n\t * Private logger for class instances.\n\t */\n\tprivate static final Logger log = Logger.getLogger(XMPPSession.class.getName());\n\n\tprivate CopyOnWriteArrayList<XMPPResourceConnection> activeResources = null;\n\tprivate long creationTime = 0;\n\tprivate long packets_counter = 0;\n\tprivate Map<String, Object> sessionData = null;\n\n\t/**\n\t * User name - localpart of user's JID\n\t */\n\tprivate String username = null;\n\n\t/**\n\t * Creates a new <code>XMPPSession</code> instance.\n\t *\n\t * @param username - localpart of user's JID\n\t */\n\tpublic XMPPSession(final String username) {\n\t\tsessionData = new ConcurrentHashMap<String, Object>();\n\t\tactiveResources = new CopyOnWriteArrayList<XMPPResourceConnection>();\n\t\tthis.username = username;\n\t\tthis.creationTime = System.currentTimeMillis();\n\t}\n\n\t/**\n\t * This method is called each time the resource is set for connection.\n\t * <br>\n\t * Method performs checking whether there is no collision of the resources.\n\t *\n\t * @param conn {@link XMPPResourceConnection} that is being added.\n\t *\n\t */\n\tpublic void addResourceConnection(XMPPResourceConnection conn) throws TigaseStringprepException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Adding resource connection for username : \" + username + \", id: \" + conn);\n\t\t}\n\n\t\t// There is a bug somewhere which causes to allow for 2 or more connections\n\t\t// with the same resource. Let's try to catch the case here and fix it....\n\t\tString resource = conn.getResource();\n\n\t\tif (resource != null) {\n\t\t\tArrayDeque<XMPPResourceConnection> old_ress = new ArrayDeque<XMPPResourceConnection>();\n\n\t\t\tfor (XMPPResourceConnection act_conn : activeResources) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"Resource checking for: \" + username + \" :: \" + act_conn.getResource() +\n\t\t\t\t\t\t\t\t\t   \", connectionID: \" + act_conn);\n\t\t\t\t}\n\t\t\t\tif (resource.equalsIgnoreCase(act_conn.getResource())) {\n\t\t\t\t\told_ress.add(act_conn);\n\t\t\t\t}    // end of if (resource.equals(conn.getResource()))\n\t\t\t}      // end of for (XMPPResourceConnection conn: activeResources)\n\n\t\t\tXMPPResourceConnection old_res = null;\n\n\t\t\twhile ((old_res = old_ress.poll()) != null) {\n\n\t\t\t\t// If they are equal, just ignore this. It may happen only for USER_STATUS\n\t\t\t\t// command where the user session is artificially created....\n\t\t\t\tif (old_res != conn) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.finest(\"Found old resource connection for: \" + username + \", id: \" + old_res);\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (!\"PUSH_OFFLINE_PRESENCE\".equals(old_res.getSessionId())) {\n\t\t\t\t\t\t\told_res.putSessionData(XMPPResourceConnection.ERROR_KEY, \"conflict\");\n\t\t\t\t\t\t\told_res.logout();\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (NotAuthorizedException e) {\n\t\t\t\t\t\tlog.log(Level.CONFIG, \"Exception during closing old connection, ignoring.\", e);\n\t\t\t\t\t}\n\t\t\t\t\tremoveResourceConnection(old_res);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// The connection could have been already added with null resource\n\t\t// to avoid adding it twice let's check if it is already there\n\t\tXMPPResourceConnection old_res;\n\n\t\ttry {\n\t\t\told_res = getResourceForConnectionId(conn.getConnectionId());\n\t\t} catch (NoConnectionIdException ex) {\n\t\t\told_res = null;\n\t\t}\n\t\tif (old_res == null) {\n\t\t\tactiveResources.add(conn);\n\t\t\tconn.setParentSession(this);\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Number of active resources of [\" + username + \"] = \" + activeResources.size() + \" : \" +\n\t\t\t\t\t\t\t   activeResources);\n\t\t}\n\t}\n\n\t/**\n\t * Increments counter of processed packet for the given user session (i.e. all users connections in total)\n\t */\n\tpublic void incPacketsCounter() {\n\t\t++packets_counter;\n\t}\n\n\t/**\n\t * Removes particular {@link XMPPResourceConnection} user's resource connection from the list of all active user\n\t * connections within given {@link XMPPSession} and detaches {@link XMPPSession} from the removed {@link\n\t * XMPPResourceConnection}\n\t *\n\t */\n\tpublic void removeResourceConnection(XMPPResourceConnection conn) {\n\t\tif (activeResources.remove(conn)) {\n\t\t\tconn.removeParentSession(null);\n\t\t}\n\t}\n\n\t/**\n\t * Method is called upon closing stream connection and removes particular {@link XMPPResourceConnection}\n\t *\n\t * @param conn - {@link XMPPResourceConnection} for which stream was closed.\n\t */\n\tpublic void streamClosed(XMPPResourceConnection conn) {\n\t\tremoveResourceConnection(conn);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tStringBuilder sb = new StringBuilder();\n\n\t\tsb.append(\"XMPPSession=[\");\n\t\tsb.append(\"hash=\" + System.identityHashCode(this));\n\t\tsb.append(\", username: \").append(username);\n\t\tsb.append(\", resources: \").append(activeResources.toString());\n\t\tsb.append(\"];\");\n\n\t\treturn sb.toString();\n\t}\n\n\t/**\n\t * Method returns a List of all {@link XMPPResourceConnection} objects related to this {@link XMPPSession} object\n\t */\n\tpublic List<XMPPResourceConnection> getActiveResources() {\n\t\treturn (List<XMPPResourceConnection>) activeResources;\n\t}\n\n\t/**\n\t * Method returns a cloned List of all {@link XMPPResourceConnection} objects related to this {@link XMPPSession}\n\t * object\n\t */\n\t@SuppressWarnings({\"unchecked\"})\n\tpublic List<XMPPResourceConnection> getActiveResourcesClone() {\n\t\treturn (List<XMPPResourceConnection>) activeResources.clone();\n\t}\n\n\t/**\n\t * Method returns number of all {@link XMPPResourceConnection} objects related to this {@link XMPPSession} object\n\t */\n\tpublic int getActiveResourcesSize() {\n\t\treturn activeResources.size();\n\t}\n\n\t/**\n\t * Method returns a data for particular {@code key} which is common to all resource connections within this {@link\n\t * XMPPSession}\n\t * <br>\n\t * related to this {@link XMPPSession} object\n\t *\n\t * @param key for which data should be returned\n\t */\n\tpublic Object getCommonSessionData(String key) {\n\t\treturn sessionData.get(key);\n\t}\n\n\t/**\n\t * Method returns an array of all ConnectionIDs related to this {@link XMPPSession}\n\t */\n\tpublic JID[] getConnectionIds() {\n\t\tJID[] result = new JID[activeResources.size()];\n\t\tint idx = 0;\n\n\t\tfor (XMPPResourceConnection conn : activeResources) {\n\t\t\ttry {\n\t\t\t\tresult[idx] = conn.getConnectionId();\n\t\t\t\t++idx;\n\t\t\t} catch (NoConnectionIdException ex) {\n\n\t\t\t\t// Skip connection with no connectionId set\n\t\t\t}\n\t\t}    // end of for (XMPPResourceConnection conn: activeResources)\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Method returns an array of all FullJIDs related to this {@link XMPPSession}\n\t */\n\tpublic JID[] getJIDs() {\n\t\tJID[] result = new JID[activeResources.size()];\n\t\tint idx = 0;\n\n\t\tfor (XMPPResourceConnection conn : activeResources) {\n\t\t\tresult[idx++] = conn.getjid();\n\t\t}    // end of for (XMPPResourceConnection conn: activeResources)\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Method returns time of how long the session is active (in milliseconds)\n\t */\n\tpublic long getLiveTime() {\n\t\treturn (System.currentTimeMillis() - creationTime);\n\t}\n\n\t/**\n\t * Returns number of processed packet for the given user session (i.e. all users connections in total)\n\t */\n\tpublic long getPacketsCounter() {\n\t\treturn packets_counter;\n\t}\n\n\t/**\n\t * Method returns {@link XMPPResourceConnection} for particular FullJID. In case there are no sessions {@code null}\n\t * is returned, in case there is more than one active session a session with the highest priority is returned. In\n\t * case there are still more than one connections with the same priority then the latest active one is returned.\n\t *\n\t * @param jid FullJID for which a {@code XMPPResourceConnection} should be returned\n\t */\n\tpublic synchronized XMPPResourceConnection getResourceConnection(JID jid) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Called for: \" + jid);\n\t\t}\n\t\tif (activeResources.size() == 0) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"No active resources found!\");\n\t\t\t}\n\t\t\treturn null;\n\t\t}    // end of if (activeResources.size() == 0)\n\t\tif (jid.getResource() != null) {\n\t\t\treturn this.getResourceForResource(jid.getResource());\n\t\t}\n\t\tif (activeResources.size() == 1) {\n\t\t\tXMPPResourceConnection result = activeResources.get(0);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Only 1 active resource: \" + result.getResource());\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}    // end of if (activeResources.size() == 1)\n\n\t\tXMPPResourceConnection conn = getResourceForJID(jid);\n\n\t\tif (conn != null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Number of resources: \" + activeResources.size() + \", got resource for jid: \" + jid);\n\t\t\t}\n\n\t\t\treturn conn;\n\t\t}    // end of if (conn != null)\n\n\t\t// There is no single active resource for this jid, so let's return\n\t\t// connection with the highest priority:\n\t\tArrayList<XMPPResourceConnection> al = new ArrayList<XMPPResourceConnection>();\n\n\t\tint highest_priority = 0;\n\t\tint priority;\n\n\t\tfor (XMPPResourceConnection conn_tmp : activeResources) {\n\t\t\tif (!conn_tmp.isAuthorized()) {\n\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\tlog.finest(\"Connection either not yet authorized or already gone, ignoring while processing: \" +\n\t\t\t\t\t\t\t\t\t   conn_tmp);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// if connections priority matches current highest priority add it only, otherwise if\n\t\t\t// it's greater clear current list and set new highest priority\n\t\t\tpriority = conn_tmp.getPriority();\n\t\t\tif (priority == highest_priority) {\n\t\t\t\tal.add(conn_tmp);\n\t\t\t}\n\t\t\tif (priority > highest_priority) {\n\t\t\t\tal.clear();\n\t\t\t\tal.add(conn_tmp);\n\t\t\t\thighest_priority = priority;\n\t\t\t}\n\t\t}\n\t\tif (al.size() == 0) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"No active resources found!\");\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\tif (al.size() == 1) {\n\t\t\t// We found 1 connection with highest priority\n\t\t\treturn al.get(0);\n\t\t}    // end of if (al.size() == 1)\n\n\t\t// We have a few connections with the same highest priority\n\t\t// Let's return the one which was the most recently used.\n\t\tXMPPResourceConnection conn_last = al.get(0);\n\t\tlong time = conn_last.getLastAccessed();\n\n\t\tfor (int i = 1; i < al.size(); ++i) {\n\t\t\tif (al.get(i).getLastAccessed() > time) {\n\t\t\t\tconn_last = al.get(i);\n\t\t\t\ttime = conn_last.getLastAccessed();\n\t\t\t}    // end of if (al.get(i).getLastAccessed() > time)\n\t\t}\n\n\t\treturn conn_last;\n\t}\n\n\t/**\n\t * Method returns {@link XMPPResourceConnection} for particular ConnectionID. In case there is no session that match\n\t * given ConnectionID then {@code null} is returned.\n\t *\n\t * @param connectionId ConnectionID for which {@code XMPPResourceConnection} should be returned\n\t */\n\tpublic XMPPResourceConnection getResourceForConnectionId(JID connectionId) {\n\t\ttry {\n\t\t\tfor (XMPPResourceConnection conn : activeResources) {\n\t\t\t\tif (connectionId.equals(conn.getConnectionId())) {\n\t\t\t\t\treturn conn;\n\t\t\t\t}    // end of if (resource.equals(conn.getResource()))\n\t\t\t}      // end of for (XMPPResourceConnection conn: activeResources)\n\t\t} catch (NoConnectionIdException ex) {\n\n\t\t\t// Logger.getLogger(XMPPSession.class.getName()).log(Level.SEVERE, null, ex);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Method returns {@link XMPPResourceConnection} for particular FullJID (using {@code resource} name as determinant.\n\t * In case there is no session that match - {@code null} is returned.\n\t *\n\t * @param jid FullJID for which a {@code XMPPResourceConnection} should be returned\n\t */\n\tpublic XMPPResourceConnection getResourceForJID(JID jid) {\n\t\tfinal String resource = jid.getResource();\n\n\t\treturn getResourceForResource(resource);\n\t}\n\n\t/**\n\t * Method returns {@link XMPPResourceConnection} for particular {@code resource}. In case there is no session that\n\t * match - {@code null} is returned.\n\t *\n\t * @param resource resource string for which a {@code XMPPResourceConnection} should be returned\n\t */\n\tpublic XMPPResourceConnection getResourceForResource(String resource) {\n\t\tif ((resource != null) && (resource.length() > 0)) {\n\t\t\tfor (XMPPResourceConnection conn : activeResources) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"Resource checking: \" + conn.getResource() + \", connectionID: \" + conn);\n\t\t\t\t}\n\t\t\t\tif (resource.equalsIgnoreCase(conn.getResource())) {\n\t\t\t\t\treturn conn;\n\t\t\t\t}    // end of if (resource.equals(conn.getResource()))\n\t\t\t}      // end of for (XMPPResourceConnection conn: activeResources)\n\t\t}        // end of if (resource.length() > 0)\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Method returns username that is related to this {@code XMPPSession} (i.e. mostly localpart of JID)\n\t */\n\tpublic String getUserName() {\n\t\treturn username;\n\t}\n\n\tpublic Object computeCommonSessionDataIfAbsent(String key, Function<String, Object> valueFactory) {\n\t\treturn sessionData.computeIfAbsent(key, valueFactory);\n\t}\n\n\t/**\n\t * Method used to store data common for all connections of the user.\n\t *\n\t * @param key under which data should be store\n\t * @param value data to be stored\n\t */\n\tprotected void putCommonSessionData(String key, Object value) {\n\t\tsessionData.put(key, value);\n\t}\n\n\tprotected Object putCommonSessionDataIfAbsent(String key, Object value) {\n\t\treturn sessionData.putIfAbsent(key, value);\n\t}\n\n\t/**\n\t * Method used to remove data common for all connections of the user.\n\t *\n\t * @param key for which data should be removed\n\t */\n\tprotected Object removeCommonSessionData(String key) {\n\t\treturn sessionData.remove(key);\n\t}\n}    // XMPPSession\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/XMPPStopListenerIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport tigase.server.Packet;\n\nimport java.util.Map;\nimport java.util.Queue;\n\n/**\n * Describe interface XMPPStopListener here.\n * <br>\n * Created: Sat Oct 14 16:14:18 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic interface XMPPStopListenerIfc\n\t\textends XMPPImplIfc {\n\n\t/**\n\t * Performs additional processing upon closing user session (user either disconnects or logs-out).\n\t *\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data. It allows for storing information in a permanent storage or in memory only during the live of\n\t * the online session. This parameter can be null if there is no online user session at the time of the packet\n\t * processing.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * Regardless a response to a user request is sent or the packet is forwarded to it's destination it is always\n\t * required that a copy of the input packet is created and stored in the results queue.\n\t * @param settings this map keeps plugin specific settings loaded from the Tigase server configuration. In most\n\t * cases it is unused, however if the plugin needs to access an external database that this is a way to pass\n\t * database connection string to the plugin.\n\t */\n\tvoid stopped(XMPPResourceConnection session, Queue<Packet> results, Map<String, Object> settings);\n}    // XMPPStopListener\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/AbstractAuthPreprocessor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.auth.BruteForceLockerBean;\nimport tigase.auth.CallbackHandlerFactory;\nimport tigase.auth.SaslInvalidLoginExcepion;\nimport tigase.db.AuthRepository;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserNotFoundException;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Abstract class which should be extended by any authorization processor as it implements preprocessor feature which is\n * responsible for stopping not allowed packets from not yet authorized client connections.\n *\n * @author andrzej\n */\npublic abstract class AbstractAuthPreprocessor\n\t\textends XMPPProcessor\n\t\timplements XMPPPreprocessorIfc {\n\n\tprivate static final Logger log = Logger.getLogger(AbstractAuthPreprocessor.class.getCanonicalName());\n\n\tprivate static final String[] AUTH_ONLY_ELEMS = {\"message\", \"presence\"};\n\n\t@Inject(nullAllowed = true)\n\tprivate BruteForceLockerBean bruteForceLocker;\n\n\t@ConfigField(desc = \"Matchers selecting allowed packets for unauthorized session\", alias = \"allow-unauthorized\")\n\tprivate ElementMatcher[] allowMatchers = new ElementMatcher[]{\n\t\t\tnew ElementMatcher(new String[0], \"urn:ietf:params:xml:ns:xmpp-tls\", true),\n\t\t\tnew ElementMatcher(new String[0], \"http://jabber.org/protocol/compress\", true),\n\t\t\tnew ElementMatcher(new String[0], \"urn:ietf:params:xml:ns:xmpp-sasl\", true),\n\t\t\tnew ElementMatcher(new String[0], \"urn:xmpp:sasl:2\", true),\n\t\t\tnew ElementMatcher(Iq.IQ_QUERY_PATH, JabberIqRegister.ID, true),\n\t\t\tnew ElementMatcher(Iq.IQ_QUERY_PATH, JabberIqAuth.ID, true)\n\t};\n\n\tprotected boolean isBruteForceLockerEnabled(XMPPResourceConnection session) {\n\t\treturn bruteForceLocker != null && bruteForceLocker.isEnabled(session);\n\t}\n\n\tpublic void addInvalidLogin(XMPPResourceConnection session, String ip, BareJID jid) {\n\t\tbruteForceLocker.addInvalidLogin(session, ip, jid);\n\t}\n\n\tprotected boolean isLoginAllowedByBruteForceLocker(XMPPResourceConnection session, String clientIp, BareJID jid) {\n\t\treturn isBruteForceLockerEnabled(session) && !bruteForceLocker.isLoginAllowed(session, clientIp, jid);\n\t}\n\n\t@Override\n\tpublic boolean preProcess(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t  Queue<Packet> results, Map<String, Object> settings) {\n\t\tif ((session == null) || session.isServerSession()) {\n\t\t\treturn false;\n\t\t}    // end of if (session == null)\n\t\ttry {\n\n\t\t\t// For all messages coming from the owner of this account set\n\t\t\t// proper 'from' attribute. This is actually needed for the case\n\t\t\t// when the user sends a message to himself.\n\t\t\tif (session.getConnectionId().equals(packet.getPacketFrom())) {\n\t\t\t\tif (!session.isAuthorized()) {\n\n\t\t\t\t\t// We allow only certain packets here...\n\t\t\t\t\t// For now it is simpler to disallow all messages and presences\n\t\t\t\t\t// packets, the rest should be bounced back anyway\n\t\t\t\t\tfor (String elem : AUTH_ONLY_ELEMS) {\n\t\t\t\t\t\tif (packet.getElemName() == elem) {\n\t\t\t\t\t\t\tresults.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"You must authenticate session first, before you\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \" can send any message or presence packet.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  true));\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINE, \"Packet received before the session has been authenticated.\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\"Session details: connectionId=\" + \"{0}, sessionId={1}, packet={2}\",\n\t\t\t\t\t\t\t\t\t\tnew Object[]{session.getConnectionId(), session.getSessionId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t packet.toStringSecure()});\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (!isPacketAllowed(packet)) {\n\t\t\t\t\t\tresults.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"You must authenticate session first, before you\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \" can send any message or presence packet.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  true));\n\t\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\t\tlog.log(Level.FINE, \"Packet received before the session has been authenticated.\" +\n\t\t\t\t\t\t\t\t\t\t\t\"Session details: connectionId=\" + \"{0}, sessionId={1}, packet={2}\",\n\t\t\t\t\t\t\t\t\tnew Object[]{session.getConnectionId(), session.getSessionId(),\n\t\t\t\t\t\t\t\t\t\t\t\t packet.toStringSecure()});\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t}\n\t\t} catch (PacketErrorTypeException e) {\n\n\t\t\t// Ignore this packet\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Ignoring packet with an error to non-existen user session: {0}\",\n\t\t\t\t\t\tpacket.toStringSecure());\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.FINEST, \"Packet preprocessing exception: \", e);\n\n\t\t\treturn false;\n\t\t}    // end of try-catch\n\n\t\treturn false;\n\t}\n\n\tpublic String[] getAllowMatchers() {\n\t\tString[] result = new String[allowMatchers.length];\n\t\tfor (int i = 0; i < allowMatchers.length; i++) {\n\t\t\tresult[i] = allowMatchers[i].toString();\n\t\t}\n\t\treturn result;\n\t}\n\n\tprotected void saveIntoBruteForceLocker(final XMPPResourceConnection session, final Exception e) {\n\t\ttry {\n\t\t\tif (isBruteForceLockerEnabled(session)) {\n\t\t\t\tfinal String clientIp = BruteForceLockerBean.getClientIp(session);\n\t\t\t\tfinal BareJID userJid = extractUserJid(e, session);\n\n\t\t\t\tif (clientIp == null && log.isLoggable(Level.FINE)) {\n\t\t\t\t\tlog.log(Level.FINE, \"There is no client IP. Cannot add entry to BruteForceLocker.\");\n\t\t\t\t}\n\t\t\t\tif (userJid == null && log.isLoggable(Level.FINE)) {\n\t\t\t\t\tlog.log(Level.FINE, \"There is no user JID. Cannot add entry to BruteForceLocker.\");\n\t\t\t\t}\n\n\t\t\t\tif (userJid != null && clientIp != null) {\n\t\t\t\t\tbruteForceLocker.addInvalidLogin(session, clientIp, userJid);\n\t\t\t\t}\n\n\t\t\t\tif (bruteForceLocker.canUserBeDisabled(session, clientIp, userJid)) {\n\t\t\t\t\tdisableUser(session, userJid);\n\t\t\t\t}\n\n\t\t\t}\n\t\t} catch (Throwable caught) {\n\t\t\tlog.log(Level.WARNING, \"Cannot update BruteForceLocker\", caught);\n\t\t}\n\t}\n\n\tpublic void setAllowMatchers(String[] matcherStrs) {\n\t\tList<ElementMatcher> matchers = new ArrayList<>();\n\t\tfor (String matcherStr : matcherStrs) {\n\t\t\tElementMatcher matcher = ElementMatcher.create(matcherStr);\n\t\t\tif (matcher != null) {\n\t\t\t\tmatchers.add(matcher);\n\t\t\t}\n\t\t}\n\t\tallowMatchers = matchers.toArray(new ElementMatcher[0]);\n\t}\n\n\tprotected boolean isPacketAllowed(Packet packet) {\n\t\tfor (ElementMatcher matcher : allowMatchers) {\n\t\t\tif (matcher.matches(packet)) {\n\t\t\t\treturn matcher.getValue();\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic void setBruteForceLocker(BruteForceLockerBean bruteForceLocker) {\n\t\tlog.log(Level.CONFIG, bruteForceLocker != null ? \"BruteForceLocker enabled\" : \"BruteForceLocker disabled\" );\n\t\tthis.bruteForceLocker = bruteForceLocker;\n\t}\n\n\tprotected void disableUser(final XMPPResourceConnection session, final BareJID userJID) {\n\t\ttry {\n\t\t\tAuthRepository.AccountStatus status = session.getAuthRepository().getAccountStatus(userJID);\n\t\t\tif (status == AuthRepository.AccountStatus.active) {\n\t\t\t\tlog.log(Level.CONFIG, \"Disabling user \" + userJID);\n\t\t\t\tsession.getAuthRepository().setAccountStatus(userJID, AuthRepository.AccountStatus.disabled);\n\t\t\t}\n\t\t} catch (UserNotFoundException e) {\n\t\t\tlog.log(Level.FINE, \"Cannot check status or disable nonexistent user!\", e);\n\t\t} catch (TigaseDBException e) {\n\t\t\tlog.log(Level.WARNING, \"Cannot check status or disable user!\", e);\n\t\t}\n\t}\n\n\tprotected BareJID extractUserJid(final Exception e, XMPPResourceConnection session) {\n\t\treturn (BareJID) session.getSessionData(CallbackHandlerFactory.AUTH_JID);\n\t}\n\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/AddressingSanitizer.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.StanzaSourceChecker;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Bean(name = \"addressing-sanitizer\", parent = SessionManager.class, active = true)\npublic class AddressingSanitizer\n\t\textends XMPPProcessor\n\t\timplements XMPPPreprocessorIfc {\n\n\tprivate static final Logger log = Logger.getLogger(AddressingSanitizer.class.getName());\n\n\tprivate static final String[] COMPRESS_PATH = {\"compress\"};\n\n\t@Inject\n\tprivate StanzaSourceChecker stanzaSourceChecker;\n\n\t@Override\n\tpublic String id() {\n\t\treturn \"addressing-sanitizer\";\n\t}\n\n\t@Override\n\tpublic boolean preProcess(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t  Queue<Packet> results, Map<String, Object> settings) {\n\t\tif (packet.getServerAuthorisedStanzaFrom().isPresent()) {\n\t\t\t// Correct user JID is already available in the packet so let's use it and short-circuit further processing.\n\t\t\tsanitizePacket(packet, packet.getServerAuthorisedStanzaFrom().get());\n\t\t\treturn false;\n\t\t}\n\t\tif (session == null && stanzaSourceChecker.isPacketFromConnectionManager(packet)) {\n\t\t\t// rationale: packets coming from clients connections and arriving without existing session are most\n\t\t\t// likely send after the session has been already closed and if we don't have correct JID\n\t\t\t// to stamp (neither from packet itself nor from the session that doesn't exists)\n\t\t\treturn true;\n\t\t}\n\t\tif ((session == null) || session.isServerSession() || !session.isAuthorized() ||\n\t\t\t\tC2SDeliveryErrorProcessor.isDeliveryError(packet)) {\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\tif (session.getConnectionId().equals(packet.getPacketFrom())) {\n\t\t\t\t// After authentication we require resource binding packet and\n\t\t\t\t// nothing else:\n\t\t\t\t// actually according to XEP-0170:\n\t\t\t\t// http://xmpp.org/extensions/xep-0170.html\n\t\t\t\t// stream compression might occur between authentication and resource\n\t\t\t\t// binding\n\t\t\t\tif (session.isResourceSet() ||\n\t\t\t\t\t\tpacket.isXMLNSStaticStr(Iq.IQ_BIND_PATH, \"urn:ietf:params:xml:ns:xmpp-bind\") ||\n\t\t\t\t\t\tpacket.isXMLNSStaticStr(COMPRESS_PATH, \"http://jabber.org/protocol/compress\")) {\n\t\t\t\t\tJID from_jid = session.getJID();\n\n\t\t\t\t\tif (from_jid != null) {\n\t\t\t\t\t\tsanitizePacket(packet, from_jid);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Session is authenticated but session.getJid() is empty: {0}\",\n\t\t\t\t\t\t\t\tpacket.toStringSecure());\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// We do not accept anything without resource binding....\n\t\t\t\t\tresults.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"You must bind the resource first: \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"http://www.xmpp.org/rfcs/rfc3920.html#bind\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  true));\n\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\tlog.log(Level.FINER, \"Session details: JID={0}, connectionId={1}, sessionId={2}\",\n\t\t\t\t\t\t\t\tnew Object[]{session.getjid(), session.getConnectionId(), session.getSessionId()});\n\t\t\t\t\t}\n\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (PacketErrorTypeException e) {\n\n\t\t\t// Ignore this packet\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Ignoring packet with an error to non-existen user session: {0}\",\n\t\t\t\t\t\tpacket.toStringSecure());\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.FINEST, \"Packet preprocessing exception: \", e);\n\n\t\t\treturn false;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprotected void sanitizePacket(Packet packet, JID stanzaFrom) {\n\t\tObjects.requireNonNull(packet);\n\t\tObjects.requireNonNull(stanzaFrom);\n\t\tif (Message.ELEM_NAME == packet.getElemName() && packet.getStanzaTo() == null) {\n\t\t\tpacket.initVars(packet.getStanzaFrom(), stanzaFrom.copyWithoutResource());\n\t\t}\n\n\t\t// http://xmpp.org/rfcs/rfc6120.html#stanzas-attributes-from\n\t\tif (packet.getElemName() == tigase.server.Presence.ELEM_NAME &&\n\t\t\t\tStanzaType.getSubsTypes().contains(packet.getType()) && (packet.getStanzaFrom() == null ||\n\t\t\t\t!stanzaFrom.getBareJID().equals(packet.getStanzaFrom().getBareJID()) ||\n\t\t\t\tpacket.getStanzaFrom().getResource() != null)) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Setting correct from attribute: {0}\", stanzaFrom);\n\t\t\t}\n\t\t\tpacket.initVars(stanzaFrom.copyWithoutResource(), packet.getStanzaTo());\n\t\t} else if ((packet.getStanzaFrom() == null) || ((packet.getElemName() == tigase.server.Presence.ELEM_NAME &&\n\t\t\t\t!StanzaType.getSubsTypes().contains(packet.getType()) ||\n\t\t\t\tpacket.getElemName() != tigase.server.Presence.ELEM_NAME) &&\n\t\t\t\t!stanzaFrom.equals(packet.getStanzaFrom()))) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Setting correct from attribute: {0}\", stanzaFrom);\n\t\t\t}\n\t\t\tpacket.initVars(stanzaFrom, packet.getStanzaTo());\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Skipping setting correct from attribute: {0}, is already correct.\",\n\t\t\t\t\t\tpacket.getStanzaFrom());\n\t\t\t}\n\t\t\tpacket.initVars(packet.getStanzaFrom(), packet.getStanzaTo());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/Bind2.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.component.exceptions.ComponentException;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.util.Base64;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.JID;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.List;\nimport java.util.UUID;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\nimport static tigase.xmpp.impl.SessionBind.SESSION_KEY;\n\n@Bean(name = Bind2.XMLNS, parent = SessionManager.class, active = false)\npublic class Bind2 implements SaslAuth2.Inline {\n\n\tpublic static final String XMLNS = \"urn:xmpp:bind:0\";\n\n\tprivate static final Logger log = Logger.getLogger(Bind2.class.getName());\n\n\t@Inject\n\tprivate List<SaslAuth2.Inline> inlines;\n\n\tpublic List<SaslAuth2.Inline> getInlines() {\n\t\treturn inlines;\n\t}\n\n\tpublic void setInlines(List<SaslAuth2.Inline> inlines) {\n\t\tthis.inlines = inlines.stream().filter(it -> !(it instanceof Bind2)).collect(Collectors.toList());\n\t}\n\n\t@Override\n\tpublic boolean canHandle(XMPPResourceConnection connection, Element el) {\n\t\treturn el.getName() == \"bind\" && el.getXMLNS() == XMLNS && connection.isAuthorized() &&\n\t\t\t\tconnection.getResource() == null;\n\t}\n\n\t@Override\n\tpublic Element[] supStreamFeatures(Action action) {\n\t\tif (action != Action.sasl2) {\n\t\t\treturn null;\n\t\t}\n\t\tElement bind = new Element(\"bind\");\n\t\tbind.setXMLNS(XMLNS);\n\t\t\n\t\tElement inlineEl = new Element(\"inline\");\n\t\tbind.addChild(inlineEl);\n\t\tfor (SaslAuth2.Inline inline : inlines) {\n\t\t\tElement[] features = inline.supStreamFeatures(Action.bind2);\n\t\t\tif (features != null) {\n\t\t\t\tfor (Element feature : features) {\n\t\t\t\t\tinlineEl.addChild(feature);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn new Element[]{bind};\n\t}\n\n\tpublic CompletableFuture<Result> process(XMPPResourceConnection session, JID _jid, Element action) {\n\t\tif (session == null) {\n\t\t\treturn CompletableFuture.failedFuture(new ComponentException(Authorization.UNEXPECTED_REQUEST));\n\t\t}    // end of if (session == null)\n\t\tif (!session.isAuthorized()) {\n\t\t\treturn CompletableFuture.failedFuture(new ComponentException(Authorization.NOT_AUTHORIZED));\n\t\t}    // end\n\n\t\tStringBuilder resourceBuilder = new StringBuilder();\n\n\t\tString tag = parseTag(action);\n\t\tif (tag != null) {\n\t\t\tresourceBuilder.append(tag);\n\t\t\tresourceBuilder.append(\"/\");\n\t\t}\n\n\t\tSaslAuth2.UserAgent userAgent = (SaslAuth2.UserAgent) session.getSessionData(SaslAuth2.USER_AGENT_KEY);\n\n\t\ttry {\n\t\t\tMessageDigest md = MessageDigest.getInstance(\"SHA-256\");\n\t\t\tif (userAgent != null && userAgent.getId() != null) {\n\t\t\t\tmd.update(userAgent.getId().getBytes(StandardCharsets.UTF_8));\n\t\t\t} else {\n\t\t\t\tmd.update(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8));\n\t\t\t}\n\t\t\tresourceBuilder.append(Base64.encode(md.digest()), 0, 12);\n\t\t\tJID boundJID = session.getJID().copyWithResource(resourceBuilder.toString());\n\n\t\t\tElement response = new Element(\"bound\");\n\t\t\tresponse.setXMLNS(XMLNS);\n\n\t\t\tCompletableFuture<SaslAuth2.Inline.Result> result = CompletableFuture.completedFuture(new SaslAuth2.Inline.Result(null, true));\n\t\t\tList<Element> features = action.getChildren();\n\t\t\tif (features != null) {\n\t\t\t\tfor (Element feature : features) {\n\t\t\t\t\tfor (SaslAuth2.Inline inline : inlines) {\n\t\t\t\t\t\tif (inline.canHandle(session, feature)) {\n\t\t\t\t\t\t\tresult = result.thenCompose(r -> {\n\t\t\t\t\t\t\t\tif (r.element != null) {\n\t\t\t\t\t\t\t\t\tresponse.addChild(r.element);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (r.shouldContinue) {\n\t\t\t\t\t\t\t\t\treturn inline.process(session, boundJID, feature);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\treturn CompletableFuture.completedFuture(new SaslAuth2.Inline.Result(null, false));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tsession.setResource(resourceBuilder.toString());\n\t\t\tsession.putSessionData(SESSION_KEY, \"true\");\n\n\t\t\treturn result.thenApply(r -> {\n\t\t\t\tif (r.element != null) {\n\t\t\t\t\tresponse.addChild(r.element);\n\t\t\t\t}\n\t\t\t\treturn new Result(response, true);\n\t\t\t});\n\n\t\t} catch (NotAuthorizedException ex) {\n\t\t\treturn CompletableFuture.failedFuture(new ComponentException(Authorization.NOT_AUTHORIZED));\n\t\t} catch (TigaseStringprepException ex) {\n\t\t\tlog.log(Level.WARNING, \"stringprep problem with the server generated resource: {0}\", resourceBuilder.toString());\n\t\t\treturn CompletableFuture.failedFuture(new ComponentException(Authorization.INTERNAL_SERVER_ERROR));\n\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\treturn CompletableFuture.failedFuture(new ComponentException(Authorization.INTERNAL_SERVER_ERROR));\n\t\t}\n\t}\n\n\tprivate String parseTag(Element action) {\n\t\tElement tagEl = action.getChild(\"tag\");\n\t\treturn tagEl != null ? tagEl.getCData() : null;\n\t}\n\t\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/BindResource.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\n\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.xmpp.impl.BindResource.ID;\n\n/**\n * RFC-3920, 7. Resource Binding\n * <br>\n * Created: Mon Feb 20 21:07:29 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\n@Bean(name = ID, parent = SessionManager.class, active = true)\npublic class BindResource\n\t\textends XMPPProcessor\n\t\timplements XMPPProcessorIfc {\n\n\tpublic static final String DEF_RESOURCE_PREFIX_PROP_KEY = \"def-resource-prefix\";\n\tprivate static final String EL_NAME = \"bind\";\n\tprivate static final String[][] ELEMENTS = {Iq.IQ_BIND_PATH};\n\tprivate static final Logger log = Logger.getLogger(BindResource.class.getName());\n\t// protected static final String RESOURCE_KEY = \"Resource-Binded\";\n\tprivate static final String XMLNS = \"urn:ietf:params:xml:ns:xmpp-bind\";\n\tprotected static final String ID = XMLNS;\n\tprivate static final String[] XMLNSS = {XMLNS};\n\tprivate static final Element[] FEATURES = {new Element(EL_NAME, new String[]{\"xmlns\"}, new String[]{XMLNS})};\n\tprivate static final Element[] DISCO_FEATURES = {new Element(\"feature\", new String[]{\"var\"}, new String[]{XMLNS})};\n\tprivate static final String RESOURCE_PREFIX_DEF = \"tigase-\";\n\tprivate static int resGenerator = 0;\n\tprivate String resourceDefPrefix = RESOURCE_PREFIX_DEF;\n\t@ConfigField(desc = \"Automatic resource assignment prefix\", alias = DEF_RESOURCE_PREFIX_PROP_KEY)\n\tprivate String resourcePrefix = null;\n\n\tpublic BindResource() {\n\t\tsetResourcePrefix(RESOURCE_PREFIX_DEF);\n\t}\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic void process(final Packet packet, final XMPPResourceConnection session, final NonAuthUserRepository repo,\n\t\t\t\t\t\tfinal Queue<Packet> results, final Map<String, Object> settings) throws XMPPException {\n\t\tif (session == null) {\n\t\t\treturn;\n\t\t}    // end of if (session == null)\n\t\tif (!session.isAuthorized()) {\n\t\t\tresults.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet, \"Session is not yet authorized.\", false));\n\n\t\t\treturn;\n\t\t}    // end of if (!session.isAuthorized())\n\n\t\t// TODO: test what happens if resource is bound multiple times for the same\n\t\t// user session. in particular if XMPPSession object removes the old\n\t\t// resource from the list.\n\t\tElement request = packet.getElement();\n\t\tStanzaType type = packet.getType();\n\n\t\ttry {\n\t\t\tswitch (type) {\n\t\t\t\tcase set:\n\t\t\t\t\tString resource = request.getChildCDataStaticStr(Iq.IQ_BIND_RESOURCE_PATH);\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif ((resource == null) || resource.trim().isEmpty()) {\n\t\t\t\t\t\t\tresource = resourceDefPrefix + (++resGenerator);\n\t\t\t\t\t\t\tsession.setResource(resource);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tsession.setResource(resource);\n\t\t\t\t\t\t\t} catch (TigaseStringprepException ex) {\n\n\t\t\t\t\t\t\t\t// User provided resource is invalid, generating different\n\t\t\t\t\t\t\t\t// server one\n\t\t\t\t\t\t\t\tlog.log(Level.CONFIG, \"Incrrect resource provided by the user: {0}, generating a \" +\n\t\t\t\t\t\t\t\t\t\t\"different one by the server.\", resource);\n\t\t\t\t\t\t\t\tresource = resourceDefPrefix + (++resGenerator);\n\t\t\t\t\t\t\t\tsession.setResource(resource);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}    // end of if (resource == null) else\n\t\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"stringprep problem with the server generated resource: {0}\", resource);\n\t\t\t\t\t}\n\t\t\t\t\tpacket.initVars(session.getJID(), packet.getStanzaTo());\n\n\t\t\t\t\t// session.putSessionData(RESOURCE_KEY, \"true\");\n\t\t\t\t\tresults.offer(packet.okResult(new Element(\"jid\", session.getJID().toString()), 1));\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tresults.offer(\n\t\t\t\t\t\t\tAuthorization.BAD_REQUEST.getResponseMessage(packet, \"Bind type is incorrect\", false));\n\n\t\t\t\t\tbreak;\n\t\t\t}    // end of switch (type)\n\t\t} catch (NotAuthorizedException e) {\n\t\t\tresults.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet, \"Session is not yet authorized.\", false));\n\t\t}    // end of try-catch\n\t}\n\n\tpublic void setResourcePrefix(String resourcePrefix) {\n\t\tthis.resourcePrefix = resourcePrefix;\n\t\tthis.resourceDefPrefix = Math.abs(DNSResolverFactory.getInstance().getDefaultHost().hashCode()) + \"-\" +\n\t\t\t\t(this.resourcePrefix != null ? this.resourcePrefix : resourceDefPrefix);\n\t}\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(final XMPPResourceConnection session) {\n\t\treturn DISCO_FEATURES;\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\t@Override\n\tpublic Element[] supStreamFeatures(final XMPPResourceConnection session) {\n\t\tif ((session != null) && (!session.isResourceSet()) && session.isAuthorized()) {\n\t\t\treturn FEATURES;\n\t\t} else {\n\t\t\treturn null;\n\t\t}    // end of if (session.isAuthorized()) else\n\t}\n}    // BindResource\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/BlockingCommand.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.eventbus.EventBus;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.annotation.*;\nimport tigase.xmpp.impl.roster.RosterAbstract;\nimport tigase.xmpp.impl.roster.RosterAbstract.SubscriptionType;\nimport tigase.xmpp.impl.roster.RosterFactory;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * XEP-0191: Blocking Command. Based on privacy lists.\n *\n * @author Andrzej Wójcik\n * <br>\n * Originally submitted by:\n * @author Daniele Ricci\n * @author Behnam Hatami\n */\n@Id(BlockingCommand.ID)\n@DiscoFeatures({BlockingCommand.XMLNS})\n@Handles({@Handle(path = {Iq.ELEM_NAME, BlockingCommand.BLOCKLIST}, xmlns = BlockingCommand.XMLNS),\n\t\t  @Handle(path = {Iq.ELEM_NAME, BlockingCommand.BLOCK}, xmlns = BlockingCommand.XMLNS),\n\t\t  @Handle(path = {Iq.ELEM_NAME, BlockingCommand.UNBLOCK}, xmlns = BlockingCommand.XMLNS)})\n@HandleStanzaTypes({StanzaType.set, StanzaType.get})\n@Bean(name = BlockingCommand.ID, parent = SessionManager.class, active = true)\npublic class BlockingCommand\n\t\textends XMPPProcessorAbstract\n\t\timplements XMPPProcessorIfc {\n\n\tprotected static final String XMLNS = \"urn:xmpp:blocking\";\n\tprotected static final String XMLNS_ERRORS = XMLNS + \":errors\";\n\tprotected static final String ID = XMLNS;\n\tprotected static final String BLOCKLIST = \"blocklist\";\n\tprotected static final String BLOCK = \"block\";\n\tprotected static final String UNBLOCK = \"unblock\";\n\tprivate static final Logger log = Logger.getLogger(BlockingCommand.class.getName());\n\tprivate static final String ITEM = \"item\";\n\tprivate static final String _JID = \"jid\";\n\n\tprivate final RosterAbstract roster_util = RosterFactory.getRosterImplementation(true);\n\n\tprivate Element[] DISCO_FEATURES;\n\n\t@Inject\n\tprivate EventBus eventBus;\n\n\t@Inject(nullAllowed = true)\n\tprivate List<SpamReportsConsumer> spamReportsConsumers = Collections.emptyList();\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(XMPPResourceConnection session) {\n\t\tif (DISCO_FEATURES == null) {\n\t\t\treturn super.supDiscoFeatures(session);\n\t\t} else {\n\t\t\treturn DISCO_FEATURES;\n\t\t}\n\t}\n\n\tpublic void setSpamReportsConsumers(List<SpamReportsConsumer> spamReportsConsumers) {\n\t\tthis.spamReportsConsumers = Optional.ofNullable(spamReportsConsumers).orElse(Collections.emptyList());\n\t\tif (spamReportsConsumers.isEmpty()) {\n\t\t\tDISCO_FEATURES = null;\n\t\t} else {\n\t\t\tDISCO_FEATURES = Stream.concat(Arrays.stream(super.supDiscoFeatures(null)),\n\t\t\t\t\t\t\t\t\t\t   Arrays.stream(SpamReportsConsumer.FEATURES)).toArray(Element[]::new);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\t\tif (session == null || packet.getElemName() != Iq.ELEM_NAME) {\n\t\t\treturn;\n\t\t}\n\n\t\tStanzaType type = packet.getType();\n\t\ttry {\n\t\t\tswitch (type) {\n\t\t\t\tcase get:\n\t\t\t\t\tprocessGet(packet, session, results);\n\t\t\t\t\tbreak;\n\t\t\t\tcase set:\n\t\t\t\t\tElement e = packet.getElement().findChild(c -> c.getXMLNS() == XMLNS);\n\t\t\t\t\tif (e != null) {\n\t\t\t\t\t\tswitch (e.getName()) {\n\t\t\t\t\t\t\tcase BLOCK:\n\t\t\t\t\t\t\t\tprocessSetBlock(packet, e, session, results);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase UNBLOCK:\n\t\t\t\t\t\t\t\tprocessSetUnblock(packet, e, session, results);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\tresults.offer(\n\t\t\t\t\t\t\t\t\t\tAuthorization.FEATURE_NOT_IMPLEMENTED.getResponseMessage(packet, null, true));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t} catch (TigaseStringprepRuntimeException ex) {\n\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet, ex.getMessage(), true));\n\t\t} catch (TigaseDBException ex) {\n\t\t\tresults.offer(Authorization.INTERNAL_SERVER_ERROR.getResponseMessage(packet, \"Database error\", true));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processFromUserToServerPacket(JID connectionId, Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t  NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t\t  Map<String, Object> settings) throws PacketErrorTypeException {\n\t}\n\n\t@Override\n\tpublic void processServerSessionPacket(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t\t\t\t   Queue<Packet> results, Map<String, Object> settings)\n\t\t\tthrows PacketErrorTypeException {\n\t}\n\n\tprivate void processGet(Packet packet, XMPPResourceConnection session, Queue<Packet> results)\n\t\t\tthrows XMPPException, NotAuthorizedException, TigaseDBException {\n\t\tif (packet.getElement().getChild(BLOCKLIST, XMLNS) != null) {\n\t\t\tElement list = new Element(BLOCKLIST);\n\t\t\tlist.setXMLNS(XMLNS);\n\t\t\tList<String> jids = Privacy.getBlocked(session);\n\t\t\tif (jids != null) {\n\t\t\t\tfor (String jid : jids) {\n\t\t\t\t\tlist.addChild(new Element(ITEM, new String[]{_JID}, new String[]{jid}));\n\t\t\t\t}\n\t\t\t}\n\t\t\tsession.putSessionData(ID, ID);\n\t\t\tresults.offer(packet.okResult(list, 0));\n\t\t} else {\n\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet, \"Bad request\", true));\n\t\t}\n\t}\n\n\tprivate void notifyPrivacyListChanged(XMPPResourceConnection session)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tString name = Privacy.getDefaultListName(session);\n\t\tif (name == null) {\n\t\t\tname = \"default\";\n\t\t}\n\t\teventBus.fire(\n\t\t\t\tnew JabberIqPrivacy.PrivacyListUpdatedEvent(session.getJID(), session.getJID().copyWithoutResource(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsession.getParentSession(), name));\n\t}\n\n\tprivate void processSetBlock(Packet packet, Element e, XMPPResourceConnection session, Queue<Packet> results)\n\t\t\tthrows NotAuthorizedException, TigaseDBException, PacketErrorTypeException {\n\t\tList<Entry> entries = collectEntries(e);\n\t\tif (entries != null && !entries.isEmpty() && entries.stream().filter(entry1 -> entry1.getJid() == null).findAny().isEmpty()) {\n\t\t\tPrivacy.block(session, entries.stream().map(Entry::getJid).map(JID::toString).collect(Collectors.toList()));\n\t\t\tnotifyPrivacyListChanged(session);\n\t\t\tfor (Entry entry : entries) {\n\t\t\t\tsendBlockPresences(session, entry.getJid(), results);\n\t\t\t}\n\t\t\tresults.offer(packet.okResult((Element) null, 0));\n\t\t\tsendPush(session.getParentSession(), packet, results);\n\t\t\tnotifySpamReportsConsumers(entries);\n\t\t} else {\n\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet, \"Bad request\", true));\n\t\t}\n\t}\n\n\tprivate void processSetUnblock(Packet packet, Element e, XMPPResourceConnection session, Queue<Packet> results)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tList<JID> jids = collectJids(e);\n\t\tif (jids == null || jids.isEmpty()) {\n\t\t\tList<String> jidsStr = Privacy.getBlocked(session);\n\t\t\tif (jidsStr != null) {\n\t\t\t\tPrivacy.unblock(session, jidsStr);\n\t\t\t\tnotifyPrivacyListChanged(session);\n\t\t\t\tfor (String jid : jidsStr) {\n\t\t\t\t\tsendBlockPresences(session, JID.jidInstanceNS(jid), results);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tPrivacy.unblock(session, jids.stream().map(JID::toString).collect(Collectors.toList()));\n\t\t\tfor (JID jid : jids) {\n\t\t\t\tsendUnblockPresences(session, jid, results);\n\t\t\t}\n\t\t}\n\n\t\tresults.offer(packet.okResult((Element) null, 0));\n\t\tsendPush(session.getParentSession(), packet, results);\n\t}\n\n\tprivate List<Entry> collectEntries(Element el) {\n\t\treturn el.mapChildren(Entry::new);\n\t}\n\n\tprivate List<JID> collectJids(Element el) {\n\t\treturn el.mapChildren(item -> {\n\t\t\tString jid = item.getAttributeStaticStr(_JID);\n\t\t\ttry {\n\t\t\t\treturn JID.jidInstance(jid);\n\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\tthrow new TigaseStringprepRuntimeException(\"Invalid JID: \" + jid, ex);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void notifySpamReportsConsumers(List<Entry> entries) {\n\t\tfor (Entry entry : entries) {\n\t\t\tnotifySpamReportsConsumers(entry);\n\t\t}\n\t}\n\n\tprivate void notifySpamReportsConsumers(Entry entry) {\n\t\tentry.getSpamReportType().ifPresent(reportType -> {\n\t\t\tfor (SpamReportsConsumer consumer : spamReportsConsumers) {\n\t\t\t\tconsumer.spamReportedFrom(entry.getJid().getBareJID(), reportType);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void sendUnblockPresences(XMPPResourceConnection session, JID jid, Queue<Packet> results)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tif (jid.getLocalpart() == null && jid.getResource() == null) {\n\t\t\tJID[] buddies = roster_util.getBuddies(session);\n\t\t\tif (buddies != null) {\n\t\t\t\tfor (JID buddy : buddies) {\n\t\t\t\t\tif (!jid.getDomain().equals(buddy.getDomain())) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (buddy.getLocalpart() == null && buddy.getResource() == null) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tsendUnblockPresences(session, buddy, results);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tSubscriptionType stype = roster_util.getBuddySubscription(session, jid);\n\t\tif (stype == SubscriptionType.both || stype == SubscriptionType.from) {\n\t\t\tPresenceAbstract.sendPresence(StanzaType.probe, JID.jidInstance(session.getBareJID()), jid, results, null);\n\t\t}\n\t\tif (stype == SubscriptionType.both || stype == SubscriptionType.to) {\n\t\t\tList<XMPPResourceConnection> conns = session.getActiveSessions();\n\t\t\tif (conns != null) {\n\t\t\t\tfor (XMPPResourceConnection conn : conns) {\n\t\t\t\t\tElement pres = conn.getPresence();\n\t\t\t\t\tif (pres != null) {\n\t\t\t\t\t\tPresenceAbstract.sendPresence(null, conn.getjid(), jid, results, pres);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void sendBlockPresences(XMPPResourceConnection session, JID jid, Queue<Packet> results)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tif (jid.getLocalpart() == null && jid.getResource() == null) {\n\t\t\tJID[] buddies = roster_util.getBuddies(session, EnumSet.of(SubscriptionType.both, SubscriptionType.to));\n\t\t\tif (buddies != null) {\n\t\t\t\tfor (JID buddy : buddies) {\n\t\t\t\t\tif (!jid.getDomain().equals(buddy.getDomain())) {\n\t\t\t\t\t\t continue;\n\t\t\t\t\t}\n\t\t\t\t\tif (buddy.getLocalpart() == null && buddy.getResource() == null) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tsendBlockPresences(session, buddy, results);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tSubscriptionType stype = roster_util.getBuddySubscription(session, jid);\n\t\tJID[] froms = session.getAllResourcesJIDs();\n\t\tif (stype == SubscriptionType.both || stype == SubscriptionType.to) {\n\t\t\tif (froms != null) {\n\t\t\t\tfor (JID from : froms) {\n\t\t\t\t\tPresenceAbstract.sendPresence(StanzaType.unavailable, from, jid, results, null);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void sendPush(XMPPSession session, Packet packet, Queue<Packet> results) {\n\t\tfor (XMPPResourceConnection conn : session.getActiveResources()) {\n\t\t\tif (conn.getSessionData(ID) == ID) {\n\t\t\t\ttry {\n\t\t\t\t\tPacket result = packet.copyElementOnly();\n\t\t\t\t\tresult.initVars(null, conn.getJID());\n\t\t\t\t\tresult.setPacketTo(conn.getConnectionId());\n\t\t\t\t\tresults.offer(result);\n\t\t\t\t} catch (NotAuthorizedException ex) {\n\t\t\t\t\tlog.log(Level.FINEST, \"failed to send push notification as session is not yet authorized\");\n\t\t\t\t} catch (NoConnectionIdException ex) {\n\t\t\t\t\tlog.log(Level.FINEST, \"failed to send push notification as session is do not have connection id\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate class Entry {\n\t\tprivate final JID jid;\n\t\tprivate final Optional<SpamReportsConsumer.ReportType> spamReportType;\n\n\t\tpublic Entry(Element element) {\n\t\t\tjid = JID.jidInstanceNS(element.getAttributeStaticStr(\"jid\"));\n\t\t\tspamReportType = Optional.ofNullable(element.getChild(\"report\", SpamReportsConsumer.XMLNS))\n\t\t\t\t\t.or(() -> Optional.ofNullable(element.getChild(\"report\", SpamReportsConsumer.XMLNS1_PREFIX)))\n\t\t\t\t\t.map(SpamReportsConsumer.ReportType::fromReport);\n\t\t}\n\n\t\tpublic JID getJid() {\n\t\t\treturn jid;\n\t\t}\n\n\t\tpublic Optional<SpamReportsConsumer.ReportType> getSpamReportType() {\n\t\t\treturn spamReportType;\n\t\t}\n\t}\n\n\tprivate static class TigaseStringprepRuntimeException\n\t\t\textends RuntimeException {\n\n\t\tpublic TigaseStringprepRuntimeException(String message, Throwable cause) {\n\t\t\tsuper(message, cause);\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/C2SDeliveryErrorProcessor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\nimport tigase.xmpp.NoConnectionIdException;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Class implements static methods used to create packets to resend messages undelivered to client by C2S and methods\n * used later to distinguish if packet was resent for redelivery\n *\n * @author andrzej\n */\npublic class C2SDeliveryErrorProcessor {\n\n\tpublic static final String ELEM_NAME = \"delivery-error\";\n\tpublic static final String XMLNS = \"http://tigase.org/delivery-error\";\n\tprivate static final Logger log = Logger.getLogger(C2SDeliveryErrorProcessor.class.getCanonicalName());\n\tprivate static final String[] DELAY_PATH = {Message.ELEM_NAME, \"delay\"};\n\n\t/**\n\t * Filters packets created by processors to remove delivery-error payload\n\t */\n\tpublic static void filter(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t  Queue<Packet> results, JID toIgnore) {\n\t\tfor (Packet p : results) {\n\t\t\tif (p.getElemName() != tigase.server.Message.ELEM_NAME) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tElement elem = p.getElement();\n\t\t\tElement error = elem.getChildStaticStr(ELEM_NAME);\n\t\t\tif (error != null && error.getXMLNS() == XMLNS) {\n\t\t\t\t// We are removing delivery-error payload for outgoing messages\n\t\t\t\t// to other components than with jid toIgnore\n\t\t\t\tif (toIgnore == null || !toIgnore.equals(packet.getPacketTo())) {\n\t\t\t\t\telem.removeChild(error);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static void filterErrorElement(Element messageElem) {\n\t\tElement error = messageElem.getChildStaticStr(ELEM_NAME);\n\t\tif (error != null && error.getXMLNS() == XMLNS) {\n\t\t\t// removing error element\n\t\t\tmessageElem.removeChild(error);\n\t\t}\n\t}\n\n\t/**\n\t * Finds delivery-error element in packet and returns it\n\t *\n\t * @return true - if packet is delivery-error\n\t */\n\tpublic static Element getDeliveryError(Packet packet) {\n\t\tElement elem = packet.getElement();\n\t\tElement error = elem.getChildStaticStr(ELEM_NAME);\n\t\treturn (error != null && error.getXMLNS() == XMLNS) ? error : null;\n\t}\n\n\t/**\n\t * Checks if packet is delivery-error packet\n\t *\n\t * @return true - if packet is delivery-error\n\t */\n\tpublic static boolean isDeliveryError(Packet packet) {\n\t\tElement elem = packet.getElement();\n\t\tElement error = elem.getChildStaticStr(ELEM_NAME);\n\t\treturn error != null && error.getXMLNS() == XMLNS;\n\t}\n\n\t/**\n\t * Creates delivery-error packets to send to session manager to reprocess undelivered packets\n\t */\n\tpublic static Packet makeDeliveryError(Packet packet, Long stamp) {\n\t\tPacket result = packet.copyElementOnly();\n\t\tresult.setStableId(packet.getStableId());\n\t\tresult.setPacketFrom(packet.getPacketTo());\n\t\tElement error = new Element(ELEM_NAME, new String[]{\"xmlns\"}, new String[]{XMLNS});\n\t\tif (stamp != null) {\n\t\t\terror.setAttribute(\"stamp\", String.valueOf(stamp));\n\t\t}\n\t\tresult.getElement().addChild(error);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Filters packets before they are processed by processors to filter out delivery-error packets sent to bare jid if\n\t * other connection is available\n\t */\n\tpublic static boolean preProcess(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t\t\t Queue<Packet> results, Map<String, Object> settings, MessageDeliveryLogic messageProcessor) {\n\t\tif (packet.getElemName() != tigase.server.Message.ELEM_NAME) {\n\t\t\treturn false;\n\t\t}\n\n\t\tElement deliveryError = getDeliveryError(packet);\n\t\tif (deliveryError == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\t// We should ignore messages sent to bare jid if message contains delivery-error\n\t\t\t// payload and other connection is currently active - this is needed to reduce\n\t\t\t// issues related to duplication of messages\n\t\t\t// This would be better if we would check if there is any active session but also\n\t\t\t// if there was any active session then message was processed!\n\t\t\tif (packet.getStanzaTo() != null && packet.getStanzaTo().getResource() == null && session != null) {\n\t\t\t\tif (packet.getElemName() != Message.ELEM_NAME) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tList<XMPPResourceConnection> sessionsForMessageDelivery = messageProcessor.getConnectionsForMessageDelivery(\n\t\t\t\t\t\tsession);\n\n\t\t\t\tif (sessionsForMessageDelivery.isEmpty()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tString delay = deliveryError.getAttributeStaticStr(\"stamp\");\n\t\t\t\tif (delay == null) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\t// maybe we should forward data to only active sessions which were not available at this point??\n\t\t\t\t// how to get time of error? or maybe original time of message? timestamp might be slow while\n\t\t\t\t// in other case we might get issues with servers in other timezones!\n\t\t\t\tlong time = Long.parseLong(delay);\n\n\t\t\t\tfor (XMPPResourceConnection conn : sessionsForMessageDelivery) {\n\t\t\t\t\tif (conn.getCreationTime() <= time) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tPacket result = packet.copyElementOnly();\n\t\t\t\t\tresult.setPacketFrom(packet.getPacketTo());\n\t\t\t\t\tresult.setPacketTo(conn.getConnectionId());\n\t\t\t\t\tresults.offer(result);\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t} catch (NotAuthorizedException ex) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"NotAuthorizedException while processing undelivered message from \" + \"C2S, packet = \" +\n\t\t\t\t\t\t\t\t   packet);\n\t\t\t}\n\t\t} catch (NoConnectionIdException ex) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"NotAuthorizedException while processing undelivered message from \" + \"C2S, packet = \" +\n\t\t\t\t\t\t\t\t   packet);\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/CAPS.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.conf.Configurable;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.Presence;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.roster.RosterAbstract;\nimport tigase.xmpp.impl.roster.RosterFactory;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * @author andrzej\n */\npublic class CAPS\n\t\textends XMPPProcessor\n\t\timplements XMPPProcessorIfc {\n\n\tpublic static final String XMLNS = \"http://jabber.org/protocol/caps\";\n\tprivate static final String[][] ELEMENTS = {{Presence.ELEM_NAME}, Iq.IQ_QUERY_PATH};\n\tprivate static final String ID = \"caps\";\n\tprivate static final Logger log = Logger.getLogger(CAPS.class.getCanonicalName());\n\tprivate static final String XMLNS_DISCO = \"http://jabber.org/protocol/disco#info\";\n\tprivate static final String[] XMLNSS = {\"jabber:client\", XMLNS_DISCO};\n\tprivate static final RosterAbstract roster_impl = RosterFactory.getRosterImplementation(true);\n\tprivate static final Element[] DISCO_FEATURES = {new Element(\"feature\", new String[]{\"var\"}, new String[]{XMLNS})};\n\n\tpublic static Set<JID> getJidsWithFeature(XMPPResourceConnection session, String feature) {\n\t\tSet<JID> jids = new HashSet<JID>();\n\t\tMap<JID, String[]> resources = (Map<JID, String[]>) session.getCommonSessionData(ID);\n\n\t\tif (resources != null) {\n\t\t\tList<JID> available = new ArrayList<JID>(resources.keySet());\n\n\t\t\tfor (JID jid : available) {\n\t\t\t\tString[] capsNodes = resources.get(jid);\n\n\t\t\t\tif (capsNodes == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tfor (String capsNode : capsNodes) {\n\t\t\t\t\tString[] features = PresenceCapabilitiesManager.getNodeFeatures(capsNode);\n\n\t\t\t\t\tif (features == null) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (Arrays.binarySearch(features, feature) >= 0) {\n\t\t\t\t\t\tjids.add(jid);\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn jids;\n\t}\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(XMPPResourceConnection session) {\n\t\treturn DISCO_FEATURES;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\t\tif ((session != null) && session.isAuthorized()) {\n\t\t\ttry {\n\t\t\t\tif (packet.getElemName() == Presence.ELEM_NAME) {\n\t\t\t\t\tJID to = packet.getStanzaTo();\n\t\t\t\t\tMap<JID, String[]> resources = (Map<JID, String[]>) session.getCommonSessionData(ID);\n\n\t\t\t\t\tif (resources == null) {\n\t\t\t\t\t\tresources = new ConcurrentHashMap<JID, String[]>();\n\t\t\t\t\t\tsession.putCommonSessionData(ID, resources);\n\t\t\t\t\t}\n\t\t\t\t\tif ((packet.getType() == null) || (packet.getType() == StanzaType.available)) {\n\t\t\t\t\t\tElement c = packet.getElement().getChild(\"c\");\n\n\t\t\t\t\t\tif (c == null) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tString[] capsNodes = PresenceCapabilitiesManager.processPresence(c);\n\t\t\t\t\t\tString[] capsNodesOld = resources.put(packet.getStanzaFrom(), capsNodes);\n\n\t\t\t\t\t\tif ((capsNodesOld == null) || !Arrays.equals(capsNodes, capsNodesOld)) {\n\n\t\t\t\t\t\t\t// we shoud send pep notifications now\n\t\t\t\t\t\t\tif ((to != null) && !roster_impl.isSubscribedFrom(session, packet.getStanzaFrom())) {\n\n\t\t\t\t\t\t\t\t// subscription is not sufficient\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (to == null) {\n\t\t\t\t\t\t\t\tto = session.getJID();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// checking for need of disco#info\n\t\t\t\t\t\t\tPresenceCapabilitiesManager.prepareCapsQueries(JID.jidInstanceNS(to.getDomain()),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   packet.getStanzaFrom(), capsNodes, results);\n\t\t\t\t\t\t\tif (!session.isUserId(to.getBareJID())) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tPresenceCapabilitiesManager.handlePresence(to, packet.getStanzaFrom(), capsNodes, results);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if ((packet.getType() == StanzaType.unavailable) || (packet.getType() == StanzaType.error)) {\n\t\t\t\t\t\tresources.remove(packet.getStanzaFrom());\n\t\t\t\t\t}\n\t\t\t\t} else if ((packet.getElemName() == Iq.ELEM_NAME) &&\n\t\t\t\t\t\t((packet.getType() == StanzaType.error) || (packet.getType() == StanzaType.result))) {\n\t\t\t\t\tString nick = packet.getStanzaTo().getLocalpart();\n\n\t\t\t\t\tif ((nick == null) || Configurable.DEF_SM_NAME.equals(nick)) {\n\t\t\t\t\t\tPresenceCapabilitiesManager.processCapsQueryResponse(packet);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (NotAuthorizedException ex) {\n\t\t\t\tLogger.getLogger(CAPS.class.getName()).log(Level.SEVERE, null, ex);\n\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\tLogger.getLogger(CAPS.class.getName()).log(Level.SEVERE, null, ex);\n\t\t\t}\n\t\t} else if (((session == null) || session.isServerSession()) && (packet.getElemName() == Iq.ELEM_NAME) &&\n\t\t\t\t((packet.getType() == StanzaType.error) || (packet.getType() == StanzaType.result))) {\n\t\t\tString nick = packet.getStanzaTo().getLocalpart();\n\n\t\t\tif ((nick == null) || Configurable.DEF_SM_NAME.equals(nick)) {\n\t\t\t\tPresenceCapabilitiesManager.processCapsQueryResponse(packet);\n\t\t\t}\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/CaptchaProvider.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.util.Base64;\nimport tigase.vhosts.VHostItem;\nimport tigase.xmpp.XMPPResourceConnection;\n\nimport javax.crypto.Mac;\nimport javax.crypto.spec.SecretKeySpec;\nimport java.nio.ByteBuffer;\nimport java.nio.charset.StandardCharsets;\nimport java.security.InvalidKeyException;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.SecureRandom;\nimport java.time.Duration;\nimport java.util.Arrays;\nimport java.util.Optional;\nimport java.util.Random;\nimport java.util.function.Supplier;\n\n@Bean(name = \"CaptchaProvider\", parent = JabberIqRegister.class, active = true)\npublic class CaptchaProvider {\n\n\tprivate Random random = new SecureRandom();\n\n\tpublic CaptchaItem generateCaptcha(XMPPResourceConnection connection) {\n\t\treturn new SimpleTextCaptcha(random, connection);\n\t}\n\n\tpublic CaptchaItem getCaptchaByID(String id) {\n\t\tif (id == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\ttry {\n\t\t\tString[] parts = id.split(\"\\\\.\");\n\t\t\tString type = parts[0];\n\t\t\tif (!\"simple-text\".equals(type)) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn new SimpleTextCaptcha(parts);\n\t\t} catch (Throwable ex) {\n\t\t\t// could not parse captcha data\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic interface CaptchaItem {\n\n\t\tString getID();\n\n\t\tString getCaptchaRequest(XMPPResourceConnection session);\n\n\t\tint getErrorCounter();\n\n\t\tvoid incraseErrorCounter();\n\n\t\tboolean isResponseValid(XMPPResourceConnection session, String response);\n\n\t}\n\n\tpublic static class SimpleTextCaptcha\n\t\t\timplements CaptchaItem {\n\n\t\tprivate static final Duration TIMEOUT = Duration.ofMinutes(5);\n\t\tprivate static final int NONCE_LENGTH = 16;\n\n\t\tprivate final int a;\n\n\t\tprivate final int b;\n\t\tprivate final int result;\n\t\tprivate final byte[] nonce;\n\t\tprivate final long time;\n\t\tprivate final byte[] hmac;\n\t\tprivate int errorCounter;\n\t\tprivate final Duration timeout = TIMEOUT;\n\n\t\tpublic static byte[] calculateHMac(byte[] data, String key) throws NoSuchAlgorithmException, InvalidKeyException {\n\t\t\tSecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), \"HmacSHA256\");\n\t\t\treturn calculateHMac(data, secretKeySpec);\n\t\t}\n\n\t\tpublic static byte[] calculateHMac(byte[] data, SecretKeySpec secretKeySpec) throws NoSuchAlgorithmException, InvalidKeyException {\n\t\t\tMac mac = Mac.getInstance(\"HmacSHA256\");\n\t\t\tmac.init(secretKeySpec);\n\t\t\treturn mac.doFinal(data);\n\t\t}\n\n\t\tpublic static String getSecret(XMPPResourceConnection connection) {\n\t\t\treturn Optional.ofNullable(connection.getDomain().getS2sSecret())\n\t\t\t\t\t.orElse(connection.getDomainAsJID().toString());\n\t\t}\n\n\t\tpublic static String getSecret(VHostItem item, String domain) {\n\t\t\treturn Optional.ofNullable(item).map(VHostItem::getS2sSecret).orElse(domain);\n\t\t}\n\t\t\n\t\tSimpleTextCaptcha(Random random, XMPPResourceConnection connection) {\n\t\t\tthis(random, () -> {\n\t\t\t\tString secret = getSecret(connection);\n\t\t\t\treturn new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), \"HmacSHA256\");\n\t\t\t});\n\t\t}\n\n\t\tpublic SimpleTextCaptcha(Random random, Supplier<SecretKeySpec> secretSupplier) {\n\t\t\tthis.a = 1 + random.nextInt(31);\n\t\t\tthis.b = 1 + random.nextInt(31);\n\t\t\tthis.result = a + b;\n\t\t\tthis.nonce = new byte[NONCE_LENGTH];\n\t\t\trandom.nextBytes(this.nonce);\n\t\t\tthis.time = System.currentTimeMillis();\n\n\t\t\ttry {\n\t\t\t\tthis.hmac = calculateHMac((getPrefix() + \".\" + result).getBytes(StandardCharsets.UTF_8),\n\t\t\t\t\t\t\t\t\t\t  secretSupplier.get());\n\t\t\t} catch (Throwable ex) {\n\t\t\t\tthrow new RuntimeException(\"Could not generate HMAC\", ex);\n\t\t\t}\n\t\t}\n\n\t\tpublic SimpleTextCaptcha(String[] parts) {\n\t\t\tthis.nonce = new byte[NONCE_LENGTH];\n\t\t\tByteBuffer tmp = ByteBuffer.wrap(Base64.decode(parts[1]));\n\t\t\ttmp.get(nonce);\n\t\t\tthis.a = tmp.getInt();\n\t\t\tthis.b = tmp.getInt();\n\t\t\tthis.time = tmp.getLong();\n\t\t\tthis.result = a + b;\n\t\t\tthis.hmac = Base64.decode(parts[2]);\n\t\t}\n\n\t\t@Override\n\t\tpublic String getID() {\n\t\t\treturn getPrefix() + \".\" + Base64.encode(hmac);\n\t\t}\n\n\t\tprotected String getPrefix() {\n\t\t\tByteBuffer tmp = ByteBuffer.allocate(nonce.length + 4 + 4 + 8);\n\t\t\ttmp.put(nonce);\n\t\t\ttmp.putInt(a);\n\t\t\ttmp.putInt(b);\n\t\t\ttmp.putLong(time);\n\t\t\treturn \"simple-text\" + \".\" + Base64.encode(tmp.array());\n\t\t}\n\n\t\t@Override\n\t\tpublic String getCaptchaRequest(XMPPResourceConnection session) {\n\t\t\treturn \"Solve: \" + String.valueOf(a) + \" + \" + String.valueOf(b);\n\t\t}\n\n\t\tpublic String getCaptchaRequest() {\n\t\t\treturn \"Solve: \" + String.valueOf(a) + \" + \" + String.valueOf(b);\n\t\t}\n\n\t\t@Override\n\t\tpublic int getErrorCounter() {\n\t\t\treturn errorCounter;\n\t\t}\n\n\t\t@Override\n\t\tpublic void incraseErrorCounter() {\n\t\t\t++this.errorCounter;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isResponseValid(XMPPResourceConnection session, String response) {\n\t\t\treturn isResponseValid(() -> {\n\t\t\t\tString secret = getSecret(session);\n\t\t\t\treturn new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), \"HmacSHA256\");\n\t\t\t}, response);\n\t\t}\n\t\t\n\t\tpublic boolean isResponseValid(Supplier<SecretKeySpec> secretKeySpecSupplier, String response) {\n\t\t\tif (response == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tString responseResult = response.trim();\n\t\t\t\t// check if response matches\n\t\t\t\tif (!String.valueOf(this.result).equals(responseResult)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t// check if it is not expired\n\t\t\t\tif (time > System.currentTimeMillis() || (System.currentTimeMillis() - time) > timeout.toMillis()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t// then check if anyone tampered with token\n\t\t\t\tbyte[] calculated = calculateHMac((getPrefix() + \".\" + responseResult).getBytes(StandardCharsets.UTF_8),\n\t\t\t\t\t\t\t\t\t\t\t\t  secretKeySpecSupplier.get());\n\t\t\t\treturn Arrays.equals(hmac, calculated);\n\t\t\t} catch (Throwable ex) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/ClientStateIndication.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.RegistrarBean;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.annotation.*;\n\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created by andrzej on 25.06.2016.\n */\n@Id(ClientStateIndication.ID)\n@Handles({@Handle(path = {ClientStateIndication.ACTIVE_NAME}, xmlns = ClientStateIndication.XMLNS),\n\t\t  @Handle(path = {ClientStateIndication.INACTIVE_NAME}, xmlns = ClientStateIndication.XMLNS)})\n@StreamFeatures({@StreamFeature(elem = \"csi\", xmlns = ClientStateIndication.XMLNS)})\n@Bean(name = ClientStateIndication.ID, parent = SessionManager.class, active = true)\npublic class ClientStateIndication\n\t\textends AnnotatedXMPPProcessor\n\t\timplements XMPPProcessorIfc, XMPPPacketFilterIfc, RegistrarBean {\n\n\tprotected static final String XMLNS = \"urn:xmpp:csi:0\";\n\tprotected static final String ID = XMLNS;\n\tprotected static final String ACTIVE_NAME = \"active\";\n\tprotected static final String INACTIVE_NAME = \"inactive\";\n\tprivate static final Logger log = Logger.getLogger(ClientStateIndication.class.getCanonicalName());\n\t@Inject\n\tprivate Logic logic;\n\n\t@Override\n\tpublic void filter(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t   Queue<Packet> results) {\n\t\tlogic.filter(packet, session, repo, results);\n\t}\n\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\t\tif (session == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (!session.isAuthorized()) {\n\t\t\ttry {\n\t\t\t\tresults.offer(\n\t\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(packet, \"Session is not yet authorized.\", false));\n\t\t\t} catch (PacketErrorTypeException ex) {\n\t\t\t\tlog.log(Level.FINEST, \"ignoring packet from not authorized session which is already of type error\");\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tswitch (packet.getElemName()) {\n\t\t\tcase ACTIVE_NAME:\n\t\t\t\tlogic.deactivate(session, results);\n\t\t\t\tbreak;\n\t\t\tcase INACTIVE_NAME:\n\t\t\t\tlogic.activate(session, results);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tresults.offer(Authorization.FEATURE_NOT_IMPLEMENTED.getResponseMessage(packet, null, true));\n\t\t}\n\t}\n\n\t@Override\n\tpublic Element[] supStreamFeatures(XMPPResourceConnection session) {\n\t\tif (session == null || !session.isAuthorized()) {\n\t\t\treturn null;\n\t\t}\n\t\treturn super.supStreamFeatures(session);\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\t\tkernel.registerBean(\"logic\").asClass(MobileV2.class).exec();\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\n\t}\n\n\tpublic interface Logic\n\t\t\textends XMPPPacketFilterIfc {\n\n\t\tvoid activate(XMPPResourceConnection session, Queue<Packet> results);\n\n\t\tvoid deactivate(XMPPResourceConnection session, Queue<Packet> results);\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/DomainFilter.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.util.StringUtilities;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.vhosts.VHostItem;\nimport tigase.vhosts.filter.CustomDomainFilter;\nimport tigase.vhosts.filter.DomainFilterPolicy;\nimport tigase.xmpp.*;\n\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.xmpp.impl.DomainFilter.ID;\n\n/**\n * Created: Dec 30, 2008 12:43:28 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\n@Bean(name = ID, parent = SessionManager.class, active = true)\npublic class DomainFilter\n\t\textends XMPPProcessor\n\t\timplements XMPPPacketFilterIfc, XMPPPreprocessorIfc {\n\n\t/** constant domain key name */\n\tpublic static final String ALLOWED_DOMAINS_KEY = \"allowed-domains\";\n\n\t/** constant domain list key name */\n\tpublic static final String ALLOWED_DOMAINS_LIST_KEY = \"allowed-domains-list\";\n\t/** id of the plugin */\n\tprotected static final String ID = \"domain-filter\";\n\n\t/** Private logger for class instances. */\n\tprivate static final Logger log = Logger.getLogger(DomainFilter.class.getName());\n\t/** paths for which plugin should be enabled */\n\tprivate static final String[][] ELEMENTS = ALL_PATHS;\n\n\t/** xmlns for which plugin should be enabled */\n\tprivate static final String[] XMLNSS = {ALL_NAMES};\n\tprivate static final String[] EMPTY_STRING_ARRAY = new String[0];\n\t/** default local hostname */\n\tprivate static String local_hostname = DNSResolverFactory.getInstance().getDefaultHost();\n\n\t@Override\n\tpublic void filter(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t   Queue<Packet> results) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Filtering (packet): {0}\", packet);\n\t\t}\n\t\tif ((session == null) || (results == null) || (results.size() == 0)) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tDomainFilterPolicy domains = getDomains(session);\n\n\t\t\t// Fast return when user is authorized to send packets to any domain\n\t\t\tif (domains == DomainFilterPolicy.ALL) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tQueue<Packet> errors = new ArrayDeque<Packet>(1);\n\n\t\t\tfor (Iterator<Packet> it = results.iterator(); it.hasNext(); ) {\n\t\t\t\tPacket res = it.next();\n\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Filtering (result): {0}\", res);\n\t\t\t\t}\n\n\t\t\t\tif (domains == DomainFilterPolicy.BLOCK) {\n\t\t\t\t\tif ((res.getType() != StanzaType.error) &&\n\t\t\t\t\t\t\t((((res.getStanzaFrom() != null) && !session.isUserId(res.getStanzaFrom().getBareJID())) ||\n\t\t\t\t\t\t\t\t\t((res.getStanzaTo() != null) &&\n\t\t\t\t\t\t\t\t\t\t\t!session.isUserId(res.getStanzaTo().getBareJID()))))) {\n\t\t\t\t\t\tremovePacket(it, res, errors, \"Communication blocked.\");\n\t\t\t\t\t}\n\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tString outDomain = null;\n\n\t\t\t\tif (res.getStanzaTo() != null) {\n\t\t\t\t\toutDomain = res.getStanzaTo().getDomain();\n\t\t\t\t}\n\t\t\t\tswitch (domains) {\n\t\t\t\t\tcase LOCAL:\n\t\t\t\t\t\tif ((outDomain != null) && !session.isLocalDomain(outDomain, true) &&\n\t\t\t\t\t\t\t\t!outDomain.equals(local_hostname)) {\n\t\t\t\t\t\t\tremovePacket(it, res, errors, \"You can only communicate within the server local domains.\");\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"LOCAL Domains only, blocking packet (filter): {0}\", res);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"LOCAL Domains only, packet not blocked (filter): {0}\", res);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase OWN:\n\t\t\t\t\t\tif ((outDomain != null) && !outDomain.equals(local_hostname) &&\n\t\t\t\t\t\t\t\t!outDomain.endsWith(session.getDomain().getVhost().getDomain())) {\n\n\t\t\t\t\t\t\tremovePacket(it, res, errors, \"You can only communicate within your own domain.\");\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"OWN Domain only, blocking packet (filter): {0}\", res);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"OWN Domain only, packet not blocked (filter): {0}\", res);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase CUSTOM:\n\t\t\t\t\t\tString[] customRules = getDomainsList(session);\n\n\t\t\t\t\t\tif ((outDomain == null) || outDomain.equals(local_hostname) ||\n\t\t\t\t\t\t\t\t(res.getType() != null && res.getType().equals(StanzaType.error)) ||\n\t\t\t\t\t\t\t\t(res.getStanzaFrom() == null && res.getStanzaTo() != null &&\n\t\t\t\t\t\t\t\t\t\tsession.isUserId(res.getStanzaTo().getBareJID()))) {\n\t\t\t\t\t\t\t// don't filter system packets, breaks things\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tboolean isAlowed = CustomDomainFilter.isAllowed(res.getStanzaFrom(), res.getStanzaTo(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcustomRules);\n\n\t\t\t\t\t\tif (!isAlowed) {\n\t\t\t\t\t\t\tremovePacket(it, res, errors,\n\t\t\t\t\t\t\t\t\t\t \"Your packet was blocked by server filtering rules - FORBIDDEN\");\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\t\t\t\"CUSTOM filtering rules for domain {0}, blocking packet (filter): {1}, rules: {2}\",\n\t\t\t\t\t\t\t\t\t\tnew Object[]{outDomain, res, Arrays.asList(customRules)});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\t\t\t\"CUSTOM filtering rules for domain {0}, packet not blocked (filter): {1}, rules: {2}\",\n\t\t\t\t\t\t\t\t\t\tnew Object[]{outDomain, res, Arrays.asList(customRules)});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase BLACKLIST:\n\t\t\t\t\t\tString[] blacklistedDomains = getDomainsList(session);\n\t\t\t\t\t\tboolean blacklist_match = false;\n\n\t\t\t\t\t\tif ((outDomain == null) || outDomain.equals(local_hostname)) {\n\t\t\t\t\t\t\t// don't filter system packets, breaks things\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (String domain : blacklistedDomains) {\n\n\t\t\t\t\t\t\t// Intentionally comparing domains by reference instead of value\n\t\t\t\t\t\t\t// domain is processed through the String.intern() method\n\t\t\t\t\t\t\tif (domain == outDomain) {\n\t\t\t\t\t\t\t\tblacklist_match = true;\n\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (blacklist_match) {\n\t\t\t\t\t\t\tremovePacket(it, res, errors,\n\t\t\t\t\t\t\t\t\t\t \"You attempted to communicate with the blacklisted domain - FORBIDDEN\");\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"BLACKLIST domain {1}, blocking packet (filter): {0}\",\n\t\t\t\t\t\t\t\t\t\tnew Object[]{res, outDomain});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"BLACKLIST domain {1], packet not blocked (filter): {0}\",\n\t\t\t\t\t\t\t\t\t\tnew Object[]{res, outDomain});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase LIST:\n\n\t\t\t\t\t\tString[] allowedDomains = getDomainsList(session);\n\t\t\t\t\t\tboolean found = false;\n\n\t\t\t\t\t\tif ((outDomain == null) || outDomain.equals(local_hostname)) {\n\t\t\t\t\t\t\t// don't filter system packets, breaks things\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (String domain : allowedDomains) {\n\n\t\t\t\t\t\t\t// Intentionally comparing domains by reference instead of value\n\t\t\t\t\t\t\t// domain is processed through the String.intern() method\n\t\t\t\t\t\t\tif (domain == outDomain) {\n\t\t\t\t\t\t\t\tfound = true;\n\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!found) {\n\t\t\t\t\t\t\tremovePacket(it, res, errors, \"You can only communicate within selected list of domains.\");\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"LIST Domain only {1}, blocking packet (filter): {0}\",\n\t\t\t\t\t\t\t\t\t\tnew Object[]{res, outDomain});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"LIST Domain only {1}, packet not blocked (filter): {0}\",\n\t\t\t\t\t\t\t\t\t\tnew Object[]{res, outDomain});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tresults.addAll(errors);\n\t\t} catch (NotAuthorizedException ex) {\n\n\t\t\t// This can happen actually easily when filtering initial handshaking\n\t\t\t// packets. I am not sure what to do yet.....\n\t\t\t// Assuming this an initial, and authentication traffic, allowing for\n\t\t\t// everything, which means, just ignore.\n\t\t} catch (TigaseDBException ex) {\n\t\t\tlog.log(Level.WARNING, \"Can''t access user repository.\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic boolean preProcess(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t  Queue<Packet> results, Map<String, Object> settings) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Processing: {0}\", packet);\n\t\t}\n\n\t\tboolean stop = false;\n\n\t\tif ((session == null) || session.isServerSession()) {\n\t\t\treturn stop;\n\t\t}\n\t\ttry {\n\t\t\tDomainFilterPolicy domains = getDomains(session);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"DOMAINS setting is: {0}\", domains.name());\n\t\t\t}\n\n\t\t\t// Fast return when user is authorized to send packets to any domain\n\t\t\tif (domains == DomainFilterPolicy.ALL) {\n\t\t\t\treturn stop;\n\t\t\t}\n\n\t\t\tString outDomain = (packet.getStanzaFrom() != null) ? packet.getStanzaFrom().getDomain() : null;\n\n\t\t\ttry {\n\t\t\t\tif (session.getConnectionId().equals(packet.getPacketFrom())) {\n\t\t\t\t\toutDomain = (packet.getStanzaTo() != null) ? packet.getStanzaTo().getDomain() : null;\n\t\t\t\t}\n\t\t\t} catch (NoConnectionIdException ex) {\n\t\t\t\tlog.log(Level.WARNING, \"No connection id for session, even though this is not a server \" +\n\t\t\t\t\t\t\"session: {0}, request: {1}\", new Object[]{session, packet});\n\t\t\t}\n\t\t\tswitch (domains) {\n\t\t\t\tcase BLOCK:\n\t\t\t\t\tif ((packet.getType() == StanzaType.error) || ((packet.getStanzaFrom() == null) ||\n\t\t\t\t\t\t\t(session.isUserId(packet.getStanzaFrom().getBareJID()) && ((packet.getStanzaTo() == null) ||\n\t\t\t\t\t\t\t\t\tsession.isUserId(packet.getStanzaTo().getBareJID()))))) {\n\t\t\t\t\t\treturn stop;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tremovePacket(null, packet, results, \"Communication blocked.\");\n\t\t\t\t\t\tstop = true;\n\t\t\t\t\t}\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"BLOCK, blocking packet: {0}\", packet);\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase LOCAL:\n\t\t\t\t\tif ((outDomain != null) && !session.isLocalDomain(outDomain, true)) {\n\t\t\t\t\t\tremovePacket(null, packet, results,\n\t\t\t\t\t\t\t\t\t \"You can only communicate within the server local domains.\");\n\t\t\t\t\t\tstop = true;\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"LOCAL Domains only {1}, blocking packet: {0}\",\n\t\t\t\t\t\t\t\t\tnew Object[]{packet, outDomain});\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"LOCAL Domains only {1}, packet not blocked: {0}\",\n\t\t\t\t\t\t\t\t\tnew Object[]{packet, outDomain});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase OWN:\n\t\t\t\t\tif ((outDomain != null) && !outDomain.equals(local_hostname) &&\n\t\t\t\t\t\t\t!outDomain.endsWith(session.getDomain().getVhost().getDomain())) {\n\t\t\t\t\t\tremovePacket(null, packet, results, \"You can only communicate within your own domain.\");\n\t\t\t\t\t\tstop = true;\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"OWN Domain only {1}, blocking packet: {0}\",\n\t\t\t\t\t\t\t\t\tnew Object[]{packet, outDomain});\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"OWN Domain only {1}, packet not blocked: {0}\",\n\t\t\t\t\t\t\t\t\tnew Object[]{packet, outDomain});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase CUSTOM:\n\t\t\t\t\tString[] customRules = getDomainsList(session);\n\n\t\t\t\t\tif ((outDomain == null) || outDomain.equals(local_hostname) ||\n\t\t\t\t\t\t\t(packet.getType() == StanzaType.error) ||\n\t\t\t\t\t\t\t(packet.getStanzaFrom() == null && packet.getStanzaTo() != null &&\n\t\t\t\t\t\t\t\t\tsession.isUserId(packet.getStanzaTo().getBareJID()))) {\n\t\t\t\t\t\t// don't filter system packets, breaks things\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Skipping filtering system packet: {0}, outDomain: {1}, local_hostname: {2}\", new Object[]{packet, outDomain, local_hostname});\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tboolean isAlowed = CustomDomainFilter.isAllowed(packet.getStanzaFrom(), packet.getStanzaTo(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcustomRules);\n\n\t\t\t\t\tif (!isAlowed) {\n\t\t\t\t\t\tremovePacket(null, packet, results,\n\t\t\t\t\t\t\t\t\t \"Your packet was blocked by server filtering rules - FORBIDDEN\");\n\t\t\t\t\t\tstop = true;\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"CUSTOM filtering rules {0}, blocking packet (filter): {1}\",\n\t\t\t\t\t\t\t\t\tnew Object[]{outDomain, packet});\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"CUSTOM filtering rules {0}, packet not blocked (filter): {1}\",\n\t\t\t\t\t\t\t\t\tnew Object[]{outDomain, packet});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase BLACKLIST:\n\t\t\t\t\tString[] disallowedDomains = getDomainsList(session);\n\t\t\t\t\tboolean blacklist_match = false;\n\t\t\t\t\tif ((outDomain == null) || outDomain.equals(local_hostname)) {\n\t\t\t\t\t\t// don't filter system packets, breaks things\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (String domain : disallowedDomains) {\n\n\t\t\t\t\t\t// Intentionally comparing domains by reference instead of value\n\t\t\t\t\t\t// domain is processed through the String.intern() method\n\t\t\t\t\t\tif (domain == outDomain) {\n\t\t\t\t\t\t\tblacklist_match = true;\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (blacklist_match) {\n\t\t\t\t\t\tremovePacket(null, packet, results,\n\t\t\t\t\t\t\t\t\t \"You attempted to communicate with the blacklisted domain - FORBIDDEN\");\n\t\t\t\t\t\tstop = true;\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Packet to blacklisted domain {1}, blocking packet: {0}\",\n\t\t\t\t\t\t\t\t\tnew Object[]{packet, outDomain});\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Packet NOT TO blacklisted domain {1}, NOT blocking packet: {0}\",\n\t\t\t\t\t\t\t\t\tnew Object[]{packet, outDomain});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase LIST:\n\t\t\t\t\tString[] allowedDomains = getDomainsList(session);\n\t\t\t\t\tboolean found = false;\n\n\t\t\t\t\tif ((outDomain == null) || outDomain.equals(local_hostname)) {\n\t\t\t\t\t\t// don't filter system packets, breaks things\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tfor (String domain : allowedDomains) {\n\n\t\t\t\t\t\t// Intentionally comparing domains by reference instead of value\n\t\t\t\t\t\t// domain is processed through the String.intern() method\n\t\t\t\t\t\tif (domain == outDomain) {\n\t\t\t\t\t\t\tfound = true;\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (!found) {\n\t\t\t\t\t\tremovePacket(null, packet, results,\n\t\t\t\t\t\t\t\t\t \"You can only communicate within selected list of domains.\");\n\t\t\t\t\t\tstop = true;\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"LISTED Domains only {1}, blocking packet: {0}\",\n\t\t\t\t\t\t\t\t\tnew Object[]{packet, outDomain});\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"LISTED Domain only {1}, packet not blocked: {0}\",\n\t\t\t\t\t\t\t\t\tnew Object[]{packet, outDomain});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t} catch (NotAuthorizedException ex) {\n\n\t\t\t// This can happen actually easily when filtering initial handshaking\n\t\t\t// packets. I am not sure what to do yet.....\n\t\t\t// Assuming this an initial, and authentication traffic, allowing for\n\t\t\t// everything, which means, just ignore.\n\t\t} catch (TigaseDBException ex) {\n\t\t\tlog.log(Level.WARNING, \"Can''t access user repository.\", ex);\n\t\t}\n\n\t\treturn stop;\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\t/**\n\t * Method retrieves filtering policy based on user session, from most specific to most general, i.e.: first user\n\t * session is checked, if that fails then user repository and if there is no rules configured then domain filtering\n\t * policy from VHost is being returned (if present).\n\t *\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data. It allows for storing information in a permanent storage or in memory only during the live of\n\t * the online session. This parameter can be null if there is no online user session at the time of the packet\n\t * processing.\n\t *\n\t * @return relevant domain filtering policy\n\t *\n\t */\n\tpublic DomainFilterPolicy getDomains(XMPPResourceConnection session)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tVHostItem domain = session.getDomain();\n\t\tDomainFilterPolicy domainFilterPolicy = (DomainFilterPolicy) session.getCommonSessionData(ALLOWED_DOMAINS_KEY);\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Domains read from user session: {0} for VHost: {1}\",\n\t\t\t\t\tnew Object[]{domainFilterPolicy, domain.getKey()});\n\t\t}\n\t\tif (domainFilterPolicy == null) {\n\t\t\tString dbDomains = session.getData(null, ALLOWED_DOMAINS_KEY, null);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Domains read from database: {0} for VHost: {1}\",\n\t\t\t\t\t\tnew Object[]{dbDomains, domain.getKey()});\n\t\t\t}\n\t\t\tdomainFilterPolicy = DomainFilterPolicy.valueof(dbDomains);\n\t\t\tif (domainFilterPolicy == null) {\n\t\t\t\tif (session.isAnonymous()) {\n\t\t\t\t\tdomainFilterPolicy = DomainFilterPolicy.LOCAL;\n\t\t\t\t} else {\n\t\t\t\t\t// by default ALL\n\t\t\t\t\tdomainFilterPolicy = domain.getDomainFilter();\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Domains read from VHost item: {0} for VHost: {1}\",\n\t\t\t\t\t\tnew Object[]{domainFilterPolicy, domain.getKey()});\n\t\t\t}\n\t\t\tsession.putCommonSessionData(ALLOWED_DOMAINS_KEY, domainFilterPolicy);\n\t\t}\n\n\t\treturn domainFilterPolicy;\n\t}\n\n\t/**\n\t * Method retrieves list of domains to be applied to {@code LIST} and {@code BLACKLIST} filtering policies based on\n\t * user session, from most specific to most general, i.e.: first user session is checked, if that fails then user\n\t * repository and if there is no rules configured then list of domains from VHost is being returned (if present).\n\t *\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data. It allows for storing information in a permanent storage or in memory only during the live of\n\t * the online session. This parameter can be null if there is no online user session at the time of the packet\n\t * processing.\n\t *\n\t * @return list of domains to be whitelisted/blacklisted\n\t *\n\t */\n\tpublic String[] getDomainsList(XMPPResourceConnection session) throws NotAuthorizedException, TigaseDBException {\n\t\tVHostItem domain = session.getDomain();\n\n\t\tString[] domainsList = (String[]) session.getCommonSessionData(ALLOWED_DOMAINS_LIST_KEY);\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Getting list of domains from user session: {0} for VHost: {1}\",\n\t\t\t\t\tnew Object[]{domainsList != null ? Arrays.asList(domainsList) : \"\", domain.getKey()});\n\t\t}\n\n\t\tif (domainsList == null) {\n\t\t\tString dbDomains = session.getData(null, ALLOWED_DOMAINS_LIST_KEY, null);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Domains list read from database: {0} for VHost: {1}\",\n\t\t\t\t\t\tnew Object[]{dbDomains, domain.getKey()});\n\t\t\t}\n\n\t\t\tif (dbDomains != null) {\n\t\t\t\tdomainsList = StringUtilities.stringToArrayOfString(dbDomains, \";\");\n\t\t\t} else {\n\t\t\t\tdomainsList = domain.getDomainFilterDomains();\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Domains list read from VHost: {0} for VHost: {1}\",\n\t\t\t\t\t\t\tnew Object[]{domainsList != null ? Arrays.asList(domainsList) : \"\", domain.getKey()});\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (domainsList == null) {\n\t\t\t\tdomainsList = EMPTY_STRING_ARRAY;\n\t\t\t}\n\t\t\tsession.putCommonSessionData(ALLOWED_DOMAINS_LIST_KEY, domainsList);\n\t\t}\n\n\t\treturn domainsList;\n\t}\n\n\t/**\n\t * Helper method removing packets from processing queue and generating appropriate error packet to be send back to\n\t * client\n\t *\n\t * @param it iterator over collection of packets being filtered\n\t * @param res currently processed packet\n\t * @param errors collection of outgoing errors\n\t * @param msg human readable error message\n\t */\n\tprivate void removePacket(Iterator<Packet> it, Packet res, Queue<Packet> errors, String msg) {\n\t\tif (it != null) {\n\t\t\tit.remove();\n\t\t}\n\t\ttry {\n\t\t\terrors.add(Authorization.FORBIDDEN.getResponseMessage(res, msg, true));\n\t\t} catch (PacketErrorTypeException ex) {\n\t\t\tlog.log(Level.FINE, \"Already error packet, dropping it..: {0}\", res);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/EntityTime.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.annotation.DiscoFeatures;\nimport tigase.xmpp.impl.annotation.Handle;\nimport tigase.xmpp.impl.annotation.Handles;\nimport tigase.xmpp.impl.annotation.Id;\nimport tigase.xmpp.jid.JID;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.TimeZone;\n\nimport static tigase.xmpp.impl.EntityTime.ID;\n\n/**\n * This supports the implementation of <a href='http://xmpp.org/extensions/xep-0202.html'>XEP-0202</a>: Entity Time.\n */\n@Id(EntityTime.XMLNS)\n@Handles({@Handle(path = {Iq.ELEM_NAME, EntityTime.TIME}, xmlns = EntityTime.XMLNS)})\n@DiscoFeatures({EntityTime.XMLNS})\n@Bean(name = ID, parent = SessionManager.class, active = true)\npublic class EntityTime\n\t\textends XMPPProcessorAbstract {\n\n\tprotected static final String XMLNS = \"urn:xmpp:time\";\n\n\tprotected static final String TIME = \"time\";\n\n\tprotected final static String ID = XMLNS;\n\n\tprivate static String getUtcOffset() {\n\t\tSimpleDateFormat sdf = new SimpleDateFormat(\"Z\");\n\t\tsdf.setTimeZone(TimeZone.getDefault());\n\t\tString dateTimeString = sdf.format(new Date());\n\n\t\treturn dateTimeString.substring(0, 3) + \":\" + dateTimeString.substring(3);\n\t}\n\n\tprivate static String getUtcTime() {\n\t\tSimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss'Z'\");\n\t\tsdf.setTimeZone(TimeZone.getTimeZone(\"GMT\"));\n\t\treturn sdf.format(new Date());\n\t}\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic void processFromUserOutPacket(JID connectionId, Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t Map<String, Object> settings) throws PacketErrorTypeException {\n\t\tsuper.processFromUserOutPacket(connectionId, packet, session, repo, results, settings);\n\t}\n\n\t@Override\n\tpublic void processFromUserToServerPacket(JID connectionId, Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t  NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t\t  Map<String, Object> settings) throws PacketErrorTypeException {\n\t\tif (packet.getStanzaTo() != null && packet.getStanzaFrom() != null &&\n\t\t\t\tpacket.getStanzaTo().equals(packet.getStanzaFrom())) {\n\t\t\tprocessFromUserOutPacket(connectionId, packet, session, repo, results, settings);\n\t\t} else if (packet.getType() == StanzaType.get) {\n\t\t\tsendTimeResult(packet, results);\n\t\t} else {\n\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet, \"Message type is incorrect\", true));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processNullSessionPacket(Packet packet, NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t Map<String, Object> settings) throws PacketErrorTypeException {\n\t\tif (packet.getType() == StanzaType.get) {\n\t\t\tsendTimeResult(packet, results);\n\t\t} else if (packet.getType() == StanzaType.set) {\n\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet, \"Message type is incorrect\", true));\n\t\t} else {\n\t\t\tsuper.processNullSessionPacket(packet, repo, results, settings);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processServerSessionPacket(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t\t\t\t   Queue<Packet> results, Map<String, Object> settings)\n\t\t\tthrows PacketErrorTypeException {\n\t}\n\n\t@Override\n\tpublic void processToUserPacket(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings)\n\t\t\tthrows PacketErrorTypeException {\n\t\tsuper.processToUserPacket(packet, session, repo, results, settings);\n\t}\n\n\tprivate void sendTimeResult(Packet packet, Queue<Packet> results) {\n\t\tPacket resp = packet.okResult((Element) null, 0);\n\n\t\tElement time = new Element(\"time\", new String[]{\"xmlns\"}, new String[]{XMLNS});\n\n\t\tElement tzo = new Element(\"tzo\");\n\t\ttzo.setCData(getUtcOffset());\n\t\ttime.addChild(tzo);\n\n\t\tElement utc = new Element(\"utc\");\n\t\tutc.setCData(getUtcTime());\n\t\ttime.addChild(utc);\n\n\t\tresp.getElement().addChild(time);\n\t\tresults.offer(resp);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/ErrorCounter.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.stats.CounterValue;\nimport tigase.stats.StatisticsList;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPPacketFilterIfc;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.impl.annotation.AnnotatedXMPPProcessor;\nimport tigase.xmpp.impl.annotation.HandleStanzaTypes;\nimport tigase.xmpp.impl.annotation.Id;\n\nimport java.util.Queue;\nimport java.util.logging.Level;\n\n/**\n * ErrorCounter class is implementation of XMPPProcessor responsible for counting packets with type=error which value is\n * added to Tigase XMPP Server statistics.\n *\n * @author andrzej\n */\n@Id(ErrorCounter.ID)\n@HandleStanzaTypes(StanzaType.error)\n@Bean(name = ErrorCounter.ID, parent = SessionManager.class, active = false)\npublic class ErrorCounter\n\t\textends AnnotatedXMPPProcessor\n\t\timplements XMPPPacketFilterIfc {\n\n\tprotected static final String ID = \"error-counter\";\n\tprivate static final String SM_COMP = \"sess-man\";\n\tprivate final ErrorStatisticsHolder holder = new ErrorStatisticsHolder();\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t\tsuper.getStatistics(list);\n\t\tholder.getStatistics(list);\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ALL_PATHS;\n\t}\n\n\t@Override\n\tpublic void filter(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t   Queue<Packet> results) {\n\t\t//process(packet, session);\n\t\tfor (Packet r : results) {\n\t\t\tprocess(r, session);\n\t\t}\n\t}\n\n\tprotected void process(Packet packet, XMPPResourceConnection session) {\n\t\tif (super.canHandle(packet, session) == Authorization.AUTHORIZED) {\n\t\t\tholder.count(packet);\n\t\t}\n\t}\n\n\tpublic static class ErrorStatisticsHolder {\n\n\t\tprivate static final String[] ERROR_NAMES;\n\n\t\tstatic {\n\t\t\tint counters = Authorization.values().length + 1;\n\t\t\tERROR_NAMES = new String[counters];\n\t\t\tAuthorization[] vals = Authorization.values();\n\t\t\tfor (int i = 0; i < vals.length; i++) {\n\t\t\t\tString name = vals[i].getCondition();\n\t\t\t\tif (name == null) {\n\t\t\t\t\tname = vals[i].name().toLowerCase();\n\t\t\t\t}\n\t\t\t\tStringBuilder sb = new StringBuilder();\n\t\t\t\tsb.append(vals[i].getErrorCode());\n\t\t\t\tfor (String part : name.split(\"-\")) {\n\t\t\t\t\tsb.append(Character.toUpperCase(part.charAt(0)));\n\t\t\t\t\tsb.append(part.substring(1));\n\t\t\t\t}\n\t\t\t\tERROR_NAMES[i] = sb.toString();\n\t\t\t}\n\t\t\tERROR_NAMES[vals.length] = \"0Unknown\";\n\t\t}\n\n\t\tprivate final CounterValue[] counters;\n\n\t\tpublic static String[] getErrorNames() {\n\t\t\treturn ERROR_NAMES;\n\t\t}\n\n\t\tpublic ErrorStatisticsHolder() {\n\t\t\tcounters = new CounterValue[ERROR_NAMES.length];\n\t\t\tfor (int i = 0; i < ERROR_NAMES.length; i++) {\n\t\t\t\tcounters[i] = new CounterValue(ERROR_NAMES[i], Level.FINER);\n\t\t\t}\n\t\t}\n\n\t\tpublic void count(Packet packet) {\n\t\t\tString condition = packet.getErrorCondition();\n\t\t\tAuthorization val = Authorization.getByCondition(condition);\n\t\t\tif (val != null) {\n\t\t\t\tcounters[val.ordinal()].inc();\n\t\t\t} else {\n\t\t\t\tcounters[counters.length - 1].inc();\n\t\t\t}\n\t\t}\n\n\t\tpublic void getStatistics(StatisticsList list) {\n\t\t\tfor (CounterValue c : counters) {\n\t\t\t\tlist.add(SM_COMP, \"ErrorStats/\" + c.getName() + \"ErrorsNumber\", c.getValue(), c.getLevel());\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/FlexibleOfflineMessageRetrieval.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.MsgRepositoryIfc;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserNotFoundException;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Command;\nimport tigase.server.DataForm;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.amp.db.MsgRepository;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.disco.XMPPService.INFO_XMLNS;\nimport static tigase.disco.XMPPService.ITEMS_XMLNS;\nimport static tigase.server.Message.ELEM_NAME;\n\n@Bean(name = FlexibleOfflineMessageRetrieval.ID, parent = SessionManager.class, active = false)\npublic class FlexibleOfflineMessageRetrieval\n\t\t//\t\textends XMPPProcessor\n\t\textends XMPPProcessorAbstract\n\t\timplements XMPPProcessorIfc {\n\n\t/** Field holds the xmlns of XEP-0013: Flexible offline messages retrieval */\n\tpublic static final String FLEXIBLE_OFFLINE_XMLNS = \"http://jabber.org/protocol/offline\";\n\tpublic static final String ITEM_ELEMENT_NAME = \"item\";\n\tpublic static final String NODE_ATTRIBUTE_NAME = \"node\";\n\tpublic static final String[] MESSAGE_EVENT_PATH = {ELEM_NAME, \"event\"};\n\tpublic static final String[] MESSAGE_HEADER_PATH = {ELEM_NAME, \"header\"};\n\tprotected static final String ID = FLEXIBLE_OFFLINE_XMLNS;\n\tprivate static final Logger log = Logger.getLogger(FlexibleOfflineMessageRetrieval.class.getName());\n\tprivate static final String OFFLINE_ELEMENT_NAME = \"offline\";\n\tprivate static final String ITEM_ACTION_ATTRIBUTE = \"action\";\n\tprivate static final String PURGE_ELEMENT_NAME = \"purge\";\n\tprivate static final String FETCH_ELEMENT_NAME = \"fetch\";\n\tprivate static final String[] XMLNSS = {INFO_XMLNS, ITEMS_XMLNS, FLEXIBLE_OFFLINE_XMLNS};\n\tprivate static final String[] IQ_OFFLINE = {Iq.ELEM_NAME, OFFLINE_ELEMENT_NAME};\n\tprivate static final String[][] ELEMENTS = {Iq.IQ_QUERY_PATH, Iq.IQ_QUERY_PATH, IQ_OFFLINE};\n\tprivate static final Element[] DISCO_FEATURES = {\n\t\t\tnew Element(\"feature\", new String[]{\"var\"}, new String[]{FLEXIBLE_OFFLINE_XMLNS})};\n\tprivate static final Element identity = new Element(\"identity\", new String[]{\"category\", \"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew String[]{\"automation\", \"message-list\"});\n\tprivate static final Element feature = new Element(\"feature\", new String[]{\"var\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{FLEXIBLE_OFFLINE_XMLNS});\n\n\tprivate static final String form_type = \"FORM_TYPE\";\n\tprivate static final String NUMBER_OF_ = \"number_of_\";\n\tprivate final MsgRepository.OfflineMessagesProcessor offlineMessagesStamper = new MsgStamper();\n\t@Inject\n\tprivate MsgRepositoryIfc msg_repo = null;\n\n\t@Override\n\tpublic Authorization canHandle(Packet packet, XMPPResourceConnection conn) {\n\t\tif (packet.isServiceDisco()) {\n\t\t\tif (packet.getStanzaTo() == null) {\n\t\t\t\tString node = packet.getAttributeStaticStr(Iq.IQ_QUERY_PATH, \"node\");\n\t\t\t\tif (FLEXIBLE_OFFLINE_XMLNS.equals(node)) {\n\t\t\t\t\treturn Authorization.AUTHORIZED;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t\treturn super.canHandle(packet, conn);\n\t}\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t;\n\n\t@Override\n\tpublic void processFromUserToServerPacket(JID connectionId, Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t  NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t\t  Map<String, Object> settings) throws PacketErrorTypeException {\n\t\tElement query = packet.getElement().findChildStaticStr(Iq.IQ_QUERY_PATH);\n\t\tElement offlineElement = packet.getElement().findChildStaticStr(IQ_OFFLINE);\n\n\t\tif (null != query) {\n\t\t\tquery = query.clone();\n\t\t\t// processing Service Discovery - info about number of elements and list of elements\n\t\t\tString queryXmlns = query.getXMLNS();\n\t\t\tfinal String node = query.getAttributeStaticStr(NODE_ATTRIBUTE_NAME);\n\n\t\t\tif (node != null && node.equals(FLEXIBLE_OFFLINE_XMLNS)) {\n\t\t\t\t// prevent restoring offline messags in other connections\n\t\t\t\tsession.putCommonSessionData(FLEXIBLE_OFFLINE_XMLNS, FLEXIBLE_OFFLINE_XMLNS);\n\t\t\t}\n\n\t\t\tif (node != null && queryXmlns != null) {\n\t\t\t\tswitch (queryXmlns) {\n\t\t\t\t\tcase INFO_XMLNS:\n\t\t\t\t\t\taddDiscoInfo(session, query);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ITEMS_XMLNS:\n\t\t\t\t\t\taddDiscoItems(session, query);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tresults.offer(packet.okResult(query, 0));\n\t\t\t}\n\n\t\t} else if (null != offlineElement) {\n\t\t\t// processing retrieve/remove stored message/presence query\n\t\t\tfinal List<Element> offlineElementChildren = offlineElement.getChildren();\n\t\t\tList<Element> itemChildren = new LinkedList<Element>();\n\t\t\tboolean fetch = false;\n\t\t\tboolean purge = false;\n\n\t\t\tfor (Element child : offlineElementChildren) {\n\t\t\t\tString name = child.getName();\n\t\t\t\tswitch (name) {\n\t\t\t\t\tcase ITEM_ELEMENT_NAME:\n\t\t\t\t\t\titemChildren.add(child);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase PURGE_ELEMENT_NAME:\n\t\t\t\t\t\tpurge = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase FETCH_ELEMENT_NAME:\n\t\t\t\t\t\tfetch = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (itemChildren.isEmpty() && (purge || fetch)) {\n\t\t\t\t// we don't have any items elements, only purge or fetch\n\t\t\t\ttry {\n\t\t\t\t\tif (fetch && !purge) {\n\t\t\t\t\t\tQueue<Packet> restorePacketForOffLineUser = restorePacketForOffLineUser(null, session,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tmsg_repo);\n\t\t\t\t\t\tresults.addAll(restorePacketForOffLineUser);\n\t\t\t\t\t} else if (purge && !fetch) {\n\t\t\t\t\t\tmsg_repo.deleteMessagesToJID(null, session);\n\t\t\t\t\t}\n\t\t\t\t} catch (TigaseDBException | NotAuthorizedException ex) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Problem retrieving messages from repository: \", ex);\n\t\t\t\t}\n\t\t\t\tresults.offer(packet.okResult(query, 0));\n\t\t\t} else if (offlineElementChildren.size() == itemChildren.size()) {\n\t\t\t\t// ok, we have items elements and all of the children are items, no fetch or purge\n\n\t\t\t\t// check if all elements have same action (view/remove)\n\t\t\t\tList<String> itemsView = new LinkedList<>();\n\t\t\t\tList<String> itemsRemove = new LinkedList<>();\n\t\t\t\tfor (Element item : itemChildren) {\n\t\t\t\t\tString actionString = item.getAttributeStaticStr(ITEM_ACTION_ATTRIBUTE);\n\t\t\t\t\tACTION action = ACTION.valueOf(actionString.toLowerCase());\n\t\t\t\t\tswitch (action) {\n\t\t\t\t\t\tcase view:\n\t\t\t\t\t\t\titemsView.add(item.getAttributeStaticStr(NODE_ATTRIBUTE_NAME));\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase remove:\n\t\t\t\t\t\t\titemsRemove.add(item.getAttributeStaticStr(NODE_ATTRIBUTE_NAME));\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tif (!itemsView.isEmpty() && itemsRemove.isEmpty()) {\n\t\t\t\t\t\t// ok, all items are 'view' type\n\t\t\t\t\t\tQueue<Packet> restorePacketForOffLineUser = restorePacketForOffLineUser(itemsView, session,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tmsg_repo);\n\t\t\t\t\t\tif (restorePacketForOffLineUser != null & !restorePacketForOffLineUser.isEmpty()) {\n\t\t\t\t\t\t\tresults.addAll(restorePacketForOffLineUser);\n\t\t\t\t\t\t\tresults.offer(packet.okResult(query, 0));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tPacket err = Authorization.ITEM_NOT_FOUND.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Requested item was not found\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true);\n\t\t\t\t\t\t\tresults.offer(err);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (itemsView.isEmpty() && !itemsRemove.isEmpty()) {\n\t\t\t\t\t\t// ok, all items are 'remove' type\n\t\t\t\t\t\tint deleteMessagesToJID = msg_repo.deleteMessagesToJID(itemsRemove, session);\n\t\t\t\t\t\tif (deleteMessagesToJID == 0) {\n\t\t\t\t\t\t\tPacket err = Authorization.ITEM_NOT_FOUND.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Requested item was not found\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true);\n\t\t\t\t\t\t\tresults.offer(err);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresults.offer(packet.okResult(query, 0));\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tPacket err = Authorization.NOT_ACCEPTABLE.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"All query items should have same action\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true);\n\t\t\t\t\t\tresults.offer(err);\n\t\t\t\t\t}\n\t\t\t\t} catch (UserNotFoundException | NotAuthorizedException ex) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Problem retrieving messages from repository: \", ex);\n\t\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\t\tresults.offer(Authorization.INTERNAL_SERVER_ERROR.getResponseMessage(packet, null, true));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic Queue<Packet> restorePacketForOffLineUser(List<String> db_ids, XMPPResourceConnection conn,\n\t\t\t\t\t\t\t\t\t\t\t\t\t MsgRepositoryIfc repo)\n\t\t\tthrows UserNotFoundException, NotAuthorizedException, TigaseDBException {\n\t\tQueue<Element> elems = repo.loadMessagesToJID(db_ids, conn, false, offlineMessagesStamper);\n\n\t\tif (elems != null) {\n\t\t\tLinkedList<Packet> pacs = new LinkedList<Packet>();\n\t\t\tElement elem = null;\n\n\t\t\twhile ((elem = elems.poll()) != null) {\n\t\t\t\ttry {\n\t\t\t\t\tfinal Packet packetInstance = Packet.packetInstance(elem);\n\t\t\t\t\tif (packetInstance.getElemName() == Iq.ELEM_NAME) {\n\t\t\t\t\t\tpacketInstance.initVars(packetInstance.getStanzaFrom(), conn.getJID());\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpacketInstance.setPacketTo(conn.getConnectionId());\n\t\t\t\t\t}\n\t\t\t\t\tpacs.offer(packetInstance);\n\t\t\t\t} catch (TigaseStringprepException | NoConnectionIdException ex) {\n\t\t\t\t\tlog.warning(\"Packet addressing problem, stringprep failed: \" + elem);\n\t\t\t\t}\n\t\t\t}    // end of while (elem = elems.poll() != null)\n\t\t\ttry {\n\t\t\t\tCollections.sort(pacs, new OfflineMessages.StampComparator());\n\t\t\t} catch (NullPointerException e) {\n\t\t\t\ttry {\n\t\t\t\t\tlog.warning(\"Can not sort off line messages: \" + pacs + \",\\n\" + e);\n\t\t\t\t} catch (Exception exc) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Can not print log message.\", exc);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn pacs;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void processServerSessionPacket(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t\t\t\t   Queue<Packet> results, Map<String, Object> settings)\n\t\t\tthrows PacketErrorTypeException {\n\t}\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(final XMPPResourceConnection session) {\n\t\treturn DISCO_FEATURES;\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\tprivate void addDiscoInfo(XMPPResourceConnection session, Element query) {\n\n\t\ttry {\n\n\t\t\tMap<Enum, Long> messagesCount = msg_repo.getMessagesCount(session.getJID());\n\n\t\t\tif (messagesCount != null && !messagesCount.isEmpty()) {\n\t\t\t\tquery.addChild(identity);\n\t\t\t\tquery.addChild(feature);\n\n\t\t\t\tDataForm.addDataForm(query, Command.DataType.result);\n\t\t\t\tDataForm.addHiddenField(query, form_type, FLEXIBLE_OFFLINE_XMLNS);\n\n\t\t\t\tfor (Map.Entry<Enum, Long> entrySet : messagesCount.entrySet()) {\n\t\t\t\t\tDataForm.addFieldValue(query, NUMBER_OF_ + entrySet.getKey(), entrySet.getValue().toString());\n\t\t\t\t}\n\t\t\t}\n\n\t\t} catch (NotAuthorizedException ex) {\n\t\t\tlog.log(Level.WARNING, \"Problem retrieving messages from repository: \", ex);\n\t\t} catch (TigaseDBException ex) {\n\n\t\t}\n\t}\n\n\tprivate void addDiscoItems(XMPPResourceConnection session, Element query) {\n\n\t\ttry {\n\t\t\tList<Element> messagesList = msg_repo.getMessagesList(session.getJID());\n\t\t\tif (null != messagesList && !messagesList.isEmpty()) {\n\t\t\t\tquery.addChildren(messagesList);\n\t\t\t}\n\t\t} catch (NotAuthorizedException | TigaseDBException ex) {\n\t\t\tlog.log(Level.WARNING, \"Problem retrieving messages from repository: \", ex);\n\t\t}\n\t}\n\n\tprivate enum ACTION {\n\n\t\tview,\n\t\tremove\n\t}\n\n\tprivate static class MsgStamper\n\t\t\timplements MsgRepository.OfflineMessagesProcessor {\n\n\t\tpublic static Element offlineElementIns = new Element(OFFLINE_ELEMENT_NAME,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  new Element[]{new Element(ITEM_ELEMENT_NAME)},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{FLEXIBLE_OFFLINE_XMLNS});\n\n\t\t@Override\n\t\tpublic void stamp(Element msg, String msgID) {\n\t\t\tElement clone = offlineElementIns.clone();\n\t\t\tfinal Element item = clone.getChild(FlexibleOfflineMessageRetrieval.ITEM_ELEMENT_NAME);\n\t\t\titem.setAttribute(FlexibleOfflineMessageRetrieval.NODE_ATTRIBUTE_NAME, msgID);\n\t\t\tmsg.addChild(clone);\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/InvisibleCommand.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.roster.RosterAbstract;\nimport tigase.xmpp.impl.roster.RosterFactory;\n\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Class InvisibleCommand implements XEP-0186 Invisible Command support\n *\n * @author andrzej\n * @see <a href=\"http://xmpp.org/extensions/xep-0186.html\">XEP-0186</a>\n */\n@Bean(name = InvisibleCommand.ID, parent = SessionManager.class, active = false)\npublic class InvisibleCommand\n\t\textends XMPPProcessor\n\t\timplements XMPPProcessorIfc, XMPPPreprocessorIfc {\n\n\tprotected static final String ID = \"invisible-command\";\n\tprivate static final Logger log = Logger.getLogger(InvisibleCommand.class.getCanonicalName());\n\tprivate static final String XMLNS = \"urn:xmpp:invisible:0\";\n\tprivate static final String[] VISIBLE_PATH = {Iq.ELEM_NAME, \"visible\"};\n\tprivate static final String[] INVISIBLE_PATH = {Iq.ELEM_NAME, \"invisible\"};\n\tprivate static final String[][] ELEMENT_PATHS = {INVISIBLE_PATH, VISIBLE_PATH};\n\tprivate static final String[] XMLNSS = {XMLNS, XMLNS};\n\tprivate static final String ACTIVE_KEY = ID + \"-active\";\n\n\tprotected RosterAbstract roster_util = getRosterUtil();\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic boolean preProcess(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t  Queue<Packet> results, Map<String, Object> settings) {\n\n\t\t// stop presence broadcast if invisibility is activated - only offline should be allowed to broadcast it to buddies with direct stanza sent\n\t\tif ((packet.getElemName() == \"presence\") && (packet.getStanzaTo() == null) &&\n\t\t\t\t(packet.getType() != StanzaType.unavailable)) {\n\t\t\tBoolean active = (Boolean) session.getSessionData(ACTIVE_KEY);\n\n\t\t\tactive = (active != null) && active;\n\t\t\tif (active) {\n\t\t\t\tpacket.processedBy(ID);\n\t\t\t}\n\n\t\t\treturn active;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void process(final Packet packet, final XMPPResourceConnection session, final NonAuthUserRepository repo,\n\t\t\t\t\t\tfinal Queue<Packet> results, final Map<String, Object> settings) {\n\t\tif (session == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (!session.isAuthorized()) {\n\t\t\ttry {\n\t\t\t\tresults.offer(\n\t\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(packet, \"Session is not yet authorized.\", false));\n\t\t\t} catch (PacketErrorTypeException ex) {\n\t\t\t\tlog.log(Level.FINEST, \"ignoring packet from not authorized session which is already of type error\");\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\ttry {\n\t\t\t\tStanzaType type = packet.getType();\n\n\t\t\t\tswitch (type) {\n\t\t\t\t\tcase set:\n\n\t\t\t\t\t\t// @todo: need to implement handing\n\t\t\t\t\t\tif (packet.getElement().findChildStaticStr(INVISIBLE_PATH) != null) {\n\n\t\t\t\t\t\t\t// invisibility started - set flag\n\t\t\t\t\t\t\tsession.putSessionData(ACTIVE_KEY, Boolean.TRUE);\n\n\t\t\t\t\t\t\t// send offline presence\n\t\t\t\t\t\t\tElement presence = new Element(\"presence\", new String[]{\"from\", \"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{session.getJID().toString(), \"unavailable\"});\n\n\t\t\t\t\t\t\tsession.putSessionData(XMPPResourceConnection.PRESENCE_KEY, presence);\n\t\t\t\t\t\t\tPresenceState.broadcastOffline(session, results, settings, roster_util);\n\t\t\t\t\t\t\tsession.removeSessionData(PresenceState.OFFLINE_BUD_SENT);\n\n\t\t\t\t\t\t\t// session.removeSessionData(XMPPResourceConnection.PRESENCE_KEY);\n\t\t\t\t\t\t} else if (packet.getElement().findChildStaticStr(VISIBLE_PATH) != null) {\n\n\t\t\t\t\t\t\t// invisibility left - clear flag\n\t\t\t\t\t\t\tsession.removeSessionData(ACTIVE_KEY);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// sending result\n\t\t\t\t\t\tresults.offer(packet.okResult((Element) null, 0));\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"InvisibleCommand processing type is incorrect\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   false));\n\t\t\t\t}\n\t\t\t} catch (NotAuthorizedException ex) {\n\t\t\t\tresults.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet, \"Not authorized\", false));\n\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\tresults.offer(Authorization.INTERNAL_SERVER_ERROR.getResponseMessage(packet, \"Error accessing database\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t false));\n\t\t\t}\n\t\t} catch (PacketErrorTypeException ex) {\n\t\t\tlog.log(Level.SEVERE, \"packet error type exception\", ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENT_PATHS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\tprotected RosterAbstract getRosterUtil() {\n\t\treturn RosterFactory.getRosterImplementation(true);\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/JabberIqAuth.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.auth.BruteForceLockerBean;\nimport tigase.auth.TigaseSaslProvider;\nimport tigase.auth.callbacks.VerifyPasswordCallback;\nimport tigase.db.AuthRepository;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Command;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.Priority;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.BareJID;\n\nimport javax.security.auth.callback.*;\nimport javax.security.sasl.Sasl;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * JEP-0078: Non-SASL Authentication\n * <br>\n * Created: Thu Feb 16 17:46:16 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = JabberIqAuth.ID, parent = SessionManager.class, active = true)\npublic class JabberIqAuth\n\t\textends AbstractAuthPreprocessor\n\t\timplements XMPPProcessorIfc {\n\n\tprivate static final String[][] ELEMENT_PATHS = {Iq.IQ_QUERY_PATH};\n\tprivate static final Logger log = Logger.getLogger(JabberIqAuth.class.getName());\n\tprivate static final String XMLNS = \"jabber:iq:auth\";\n\tprotected static final String ID = XMLNS;\n\tprivate static final String[] XMLNSS = {XMLNS};\n\tprivate static final String[] IQ_QUERY_USERNAME_PATH = {Iq.ELEM_NAME, Iq.QUERY_NAME, \"username\"};\n\tprivate static final String[] IQ_QUERY_RESOURCE_PATH = {Iq.ELEM_NAME, Iq.QUERY_NAME, \"resource\"};\n\tprivate static final String[] IQ_QUERY_PASSWORD_PATH = {Iq.ELEM_NAME, Iq.QUERY_NAME, \"password\"};\n\tprivate static final String[] IQ_QUERY_DIGEST_PATH = {Iq.ELEM_NAME, Iq.QUERY_NAME, \"digest\"};\n\tprivate static final Element[] FEATURES = {\n\t\t\tnew Element(\"auth\", new String[]{\"xmlns\"}, new String[]{\"http://jabber.org/features/iq-auth\"})};\n\tprivate static final Element[] DISCO_FEATURES = {new Element(\"feature\", new String[]{\"var\"}, new String[]{XMLNS})};\n\t@Inject\n\tprivate TigaseSaslProvider saslProvider;\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic void process(final Packet packet, final XMPPResourceConnection session, final NonAuthUserRepository repo,\n\t\t\t\t\t\tfinal Queue<Packet> results, final Map<String, Object> settings) throws XMPPException {\n\t\tif (session == null) {\n\t\t\treturn;\n\t\t}    // end of if (session == null)\n\t\tsynchronized (session) {\n\n\t\t\t// If authentication timeout expired, ignore the request....\n\t\t\tif (session.getSessionData(XMPPResourceConnection.AUTHENTICATION_TIMEOUT_KEY) != null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (session.isAuthorized()) {\n\n\t\t\t\t// Multiple authentication attempts....\n\t\t\t\t// Another authentication request on already authenticated\n\t\t\t\t// connection\n\t\t\t\t// This is not allowed and must be forbidden.\n\t\t\t\tPacket res = Authorization.NOT_AUTHORIZED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Cannot authenticate twice on the same stream.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t false);\n\n\t\t\t\t// Make sure it gets delivered before stream close\n\t\t\t\tres.setPriority(Priority.SYSTEM);\n\t\t\t\tresults.offer(res);\n\n\t\t\t\t// Optionally close the connection to make sure there is no\n\t\t\t\t// confusion about the connection state.\n\t\t\t\tresults.offer(Command.CLOSE.getPacket(packet.getTo(), packet.getFrom(), StanzaType.set,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  session.nextStanzaId()));\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Discovered second authentication attempt: {0}, packet: {1}\",\n\t\t\t\t\t\t\tnew Object[]{session.toString(), packet.toString()});\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tsession.logout();\n\t\t\t\t} catch (NotAuthorizedException ex) {\n\t\t\t\t\tlog.log(Level.FINER, \"Unsuccessful session logout: {0}\", session.toString());\n\t\t\t\t}\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Session after logout: {0}\", session.toString());\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tElement request = packet.getElement();\n\t\t\tStanzaType type = packet.getType();\n\n\t\t\tswitch (type) {\n\t\t\t\tcase get:\n\t\t\t\t\ttry {\n\t\t\t\t\t\tStringBuilder response = new StringBuilder(\"<username/>\");\n\t\t\t\t\t\tfinal Collection<String> auth_mechs = saslProvider.filterMechanisms(\n\t\t\t\t\t\t\t\tSasl.getSaslServerFactories(), session);\n\n\t\t\t\t\t\tif (auth_mechs.contains(\"PLAIN\")) {\n\t\t\t\t\t\t\tresponse.append(\"<password/>\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// response.append(\"<digest/>\");\n\t\t\t\t\t\tresponse.append(\"<resource/>\");\n\t\t\t\t\t\tresults.offer(packet.okResult(response.toString(), 1));\n\t\t\t\t\t} catch (NullPointerException ex) {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\t\tlog.fine(\"Database problem, most likely misconfiguration error: \" + ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresults.offer(Authorization.INTERNAL_SERVER_ERROR.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Database access problem, please contact administrator.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true));\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase set:\n\n\t\t\t\t\t// Now we use loginOther() instead to make it easier to\n\t\t\t\t\t// customize\n\t\t\t\t\t// the authentication protocol without a need to replace the\n\t\t\t\t\t// authentication plug-in. The authentication takes place on the\n\t\t\t\t\t// AuthRepository\n\t\t\t\t\t// level so we do not really care here what the user has sent.\n\t\t\t\t\tString user_name = request.getChildCDataStaticStr(IQ_QUERY_USERNAME_PATH);\n\t\t\t\t\tString resource = request.getChildCDataStaticStr(IQ_QUERY_RESOURCE_PATH);\n\t\t\t\t\tString password = request.getChildCDataStaticStr(IQ_QUERY_PASSWORD_PATH);\n\t\t\t\t\tString digest = request.getChildCDataStaticStr(IQ_QUERY_DIGEST_PATH);\n\t\t\t\t\tif (user_name == null || resource == null || (password == null && digest == null)) {\n\t\t\t\t\t\tresults.offer(Authorization.NOT_ACCEPTABLE.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"Authentication failed: Required Information Not Provided\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  false));\n\t\t\t\t\t\tresults.offer(Command.CLOSE.getPacket(packet.getTo(), packet.getFrom(), StanzaType.set,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  session.nextStanzaId()));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tBareJID user_id = BareJID.bareJIDInstance(user_name,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  session.getDomain().getVhost().getDomain());\n\n\t\t\t\t\t\tAuthRepository.AccountStatus status = session.getAuthRepository().getAccountStatus(user_id);\n\t\t\t\t\t\tswitch (status) {\n\t\t\t\t\t\t\tcase pending:\n\t\t\t\t\t\t\t\tresults.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"Account is pending verification, please confirm the email address by clicking on the link sent to you\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  false));\n\t\t\t\t\t\t\t\tresults.offer(Command.CLOSE.getPacket(packet.getTo(), packet.getFrom(), StanzaType.set,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  session.nextStanzaId()));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\tcase disabled:\n\t\t\t\t\t\t\tcase banned:\n\t\t\t\t\t\t\tcase spam:\n\t\t\t\t\t\t\tcase undefined_inactive:\n\t\t\t\t\t\t\t\tresults.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"Account was disabled, please contact the support\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  false));\n\t\t\t\t\t\t\t\tresults.offer(Command.CLOSE.getPacket(packet.getTo(), packet.getFrom(), StanzaType.set,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  session.nextStanzaId()));\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tAuthorization result = doAuth(repo, settings, session, user_id, password, digest);\n\n\t\t\t\t\t\tif (result == Authorization.AUTHORIZED) {\n\n\t\t\t\t\t\t\t// Some clients don't send resource here, instead they\n\t\t\t\t\t\t\t// send it later\n\t\t\t\t\t\t\t// in resource bind packet.\n\t\t\t\t\t\t\tif ((resource != null) && !resource.isEmpty()) {\n\t\t\t\t\t\t\t\tsession.setResource(resource);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tresults.offer(session.getAuthState()\n\t\t\t\t\t\t\t\t\t\t\t\t  .getResponseMessage(packet, \"Authentication successful.\", false));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresults.offer(\n\t\t\t\t\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(packet, \"Authentication failed\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfalse));\n\t\t\t\t\t\t\tresults.offer(Command.CLOSE.getPacket(packet.getTo(), packet.getFrom(), StanzaType.set,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  session.nextStanzaId()));\n\t\t\t\t\t\t}    // end of else\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tlog.log(Level.CONFIG, \"Authentication failed: \" + user_name);\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Authorization exception: \", e);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tPacket response = Authorization.NOT_AUTHORIZED.getResponseMessage(packet, e.getMessage(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  false);\n\n\t\t\t\t\t\tresponse.setPriority(Priority.SYSTEM);\n\t\t\t\t\t\tresults.offer(response);\n\n\t\t\t\t\t\tInteger retries = (Integer) session.getSessionData(\"auth-retries\");\n\n\t\t\t\t\t\tif (retries == null) {\n\t\t\t\t\t\t\tretries = new Integer(0);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (retries.intValue() < 3) {\n\t\t\t\t\t\t\tsession.putSessionData(\"auth-retries\", new Integer(retries.intValue() + 1));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresults.offer(Command.CLOSE.getPacket(packet.getTo(), packet.getFrom(), StanzaType.set,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  session.nextStanzaId()));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tresults.offer(\n\t\t\t\t\t\t\tAuthorization.BAD_REQUEST.getResponseMessage(packet, \"Message type is incorrect\", false));\n\t\t\t\t\tresults.offer(Command.CLOSE.getPacket(packet.getTo(), packet.getFrom(), StanzaType.set,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  session.nextStanzaId()));\n\n\t\t\t\t\tbreak;\n\t\t\t}    // end of switch (type)\n\t\t}\n\t}\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(final XMPPResourceConnection session) {\n\t\treturn DISCO_FEATURES;\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENT_PATHS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\t@Override\n\tpublic Element[] supStreamFeatures(final XMPPResourceConnection session) {\n\t\tif ((session == null) || session.isAuthorized()) {\n\t\t\treturn null;\n\t\t} else if (session.isTlsRequired() && !session.isEncrypted()) {\n\t\t\treturn null;\n\t\t} else {\n\t\t\treturn FEATURES;\n\t\t} // end of if (session.isAuthorized()) else\n\t}\n\n\tprotected Authorization doAuth(NonAuthUserRepository repo, Map<String, Object> settings,\n\t\t\t\t\t\t\t\t   XMPPResourceConnection session, BareJID user_id, String password, String digest) {\n\t\ttry {\n\t\t\tCallbackHandler cbh = saslProvider.create(\"PLAIN\", session, repo, settings);\n\t\t\tfinal NameCallback nc = new NameCallback(\"Authentication identity\", user_id.getLocalpart());\n\t\t\tfinal VerifyPasswordCallback vpc = new VerifyPasswordCallback(password);\n\t\t\tfinal String clientIp = BruteForceLockerBean.getClientIp(session);\n\n\t\t\tcbh.handle(new Callback[]{nc});\n\t\t\ttry {\n\t\t\t\tcbh.handle(new Callback[]{vpc});\n\t\t\t\tif (vpc.isVerified()) {\n\t\t\t\t\tif (isLoginAllowedByBruteForceLocker(session, clientIp, user_id)) {\n\t\t\t\t\t\taddInvalidLogin(session, clientIp, user_id);\n\t\t\t\t\t\treturn Authorization.NOT_AUTHORIZED;\n\t\t\t\t\t}\n\t\t\t\t\tsession.authorizeJID(user_id, false);\n\t\t\t\t\treturn Authorization.AUTHORIZED;\n\t\t\t\t}\n\t\t\t} catch (UnsupportedCallbackException e) {\n\t\t\t\tfinal PasswordCallback pc = new PasswordCallback(\"Password\", false);\n\n\t\t\t\tcbh.handle(new Callback[]{pc});\n\n\t\t\t\tchar[] p = pc.getPassword();\n\n\t\t\t\tif ((p != null) && password.equals(new String(p))) {\n\t\t\t\t\tif (isLoginAllowedByBruteForceLocker(session, clientIp, user_id)) {\n\t\t\t\t\t\taddInvalidLogin(session, clientIp, user_id);\n\t\t\t\t\t\treturn Authorization.NOT_AUTHORIZED;\n\t\t\t\t\t}\n\t\t\t\t\tsession.authorizeJID(user_id, false);\n\t\t\t\t\treturn Authorization.AUTHORIZED;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (isBruteForceLockerEnabled(session)) {\n\t\t\t\taddInvalidLogin(session, clientIp, user_id);\n\t\t\t}\n\n\t\t\treturn Authorization.NOT_AUTHORIZED;\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.WARNING, \"Can''t authenticate with given CallbackHandler\", e);\n\n\t\t\treturn Authorization.INTERNAL_SERVER_ERROR;\n\t\t}\n\t}\n}    // JabberIqAuth\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/JabberIqCommand.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.component.modules.impl.AdHocCommandModule;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Command;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.PacketDefaultHandler;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\n\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Logger;\n\n/**\n * Describe class JabberIqCommand here.\n * <br>\n * Created: Mon Jan 22 22:41:17 2007\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = JabberIqCommand.ID, parent = SessionManager.class, active = true)\npublic class JabberIqCommand\n\t\textends XMPPProcessor\n\t\timplements XMPPProcessorIfc {\n\n\tprivate static final String[][] ELEMENTS = {Iq.IQ_COMMAND_PATH};\n\tprivate static final Logger log = Logger.getLogger(JabberIqCommand.class.getName());\n\tprivate static final String[] XMLNSS = {Command.XMLNS};\n\tprivate static final String XMLNS = Command.XMLNS;\n\tprotected static final String ID = XMLNS;\n\tprivate static final Element[] DISCO_FEATURES = {new Element(\"feature\", new String[]{\"var\"}, new String[]{XMLNS})};\n\n\tprivate PacketDefaultHandler defaultHandler = new PacketDefaultHandler();\n\n\t@Inject(nullAllowed = true)\n\tprivate AdHocCommandModule adHocCommandModule;\n\n\t@Override\n\tpublic Authorization canHandle(Packet packet, XMPPResourceConnection conn) {\n\t\tif (conn == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn super.canHandle(packet, conn);\n\t}\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic void process(final Packet packet, final XMPPResourceConnection session, final NonAuthUserRepository repo,\n\t\t\t\t\t\tfinal Queue<Packet> results, final Map<String, Object> settings) throws XMPPException {\n\t\tif (session == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (session.isUserId(packet.getStanzaFrom().getBareJID()) && (packet.getStanzaTo() == null || (session.isUserId(packet.getStanzaTo().getBareJID())) && packet.getStanzaTo().getResource() == null)) {\n\t\t\tadHocCommandModule.process(packet);\n\t\t\treturn;\n\t\t}\n\t\tdefaultHandler.process(packet, session, repo, results);\n\t}\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(final XMPPResourceConnection session) {\n\t\treturn DISCO_FEATURES;\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/JabberIqIq.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserNotFoundException;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.util.Base64;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Describe class JabberIqIq here.\n * <br>\n * Created: Sun Feb 25 23:37:48 2007\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = JabberIqIq.ID, parent = SessionManager.class, active = false)\npublic class JabberIqIq\n\t\textends XMPPProcessor\n\t\timplements XMPPProcessorIfc, XMPPPreprocessorIfc {\n\n\tprivate static final String[][] ELEMENTS = {Iq.IQ_QUERY_PATH};\n\tprivate static final String LEVEL = \"level\";\n\tprivate static final String XMLNS = \"jabber:iq:iq\";\n\tprotected static final String ID = XMLNS;\n\tprivate static final String[] XMLNSS = {XMLNS};\n\tprivate static final Element[] DISCO_FEATURES = {new Element(\"feature\", new String[]{\"var\"}, new String[]{XMLNS})};\n\tprivate static Logger log = Logger.getLogger(JabberIqIq.class.getName());\n\t/**\n\t * I don't want to have any offensive texts in my code so let's encode them with Base64....\n\t */\n\tprivate static String[] not_so_smart_words = {\"ZnVjaw==\", \"c2hpdA==\", \"d2hvcmU=\", \"ZGljaw==\", \"YXNz\", \"YW51cw==\",\n\t\t\t\t\t\t\t\t\t\t\t\t  \"YXJzZQ==\", \"dmFnaW5h\", \"cG9ybg==\", \"cGVuaXM=\", \"cGlzcw==\", \"c3V4\"};\n\n\t/**\n\t * IQ range table: Number Range   Descriptive Label 140+   genius 120-139  very superior 110-119  superior 90-109\n\t * normal 80-89  dull 70-79  borderline deficiency 50-69  moron 20-49  imbecile 0-19   idiot\n\t *\n\t */\n\tpublic static String calculateIQ(String iq_level) {\n\t\tdouble value = 100;\n\n\t\ttry {\n\t\t\tvalue = Double.parseDouble(iq_level);\n\t\t} catch (NumberFormatException e) {\n\t\t\tvalue = 100;\n\t\t}\n\t\tif (value >= 140) {\n\t\t\treturn \"genius\";\n\t\t}\n\t\tif ((120 <= value) && (value <= 139)) {\n\t\t\treturn \"very superior\";\n\t\t}\n\t\tif ((110 <= value) && (value <= 119)) {\n\t\t\treturn \"superior\";\n\t\t}\n\t\tif ((90 <= value) && (value <= 109)) {\n\t\t\treturn \"normal\";\n\t\t}\n\t\tif ((80 <= value) && (value <= 89)) {\n\t\t\treturn \"dull\";\n\t\t}\n\t\tif ((70 <= value) && (value <= 79)) {\n\t\t\treturn \"borderline deficiency\";\n\t\t}\n\t\tif ((50 <= value) && (value <= 69)) {\n\t\t\treturn \"moron\";\n\t\t}\n\t\tif ((20 <= value) && (value <= 49)) {\n\t\t\treturn \"imbecile\";\n\t\t}\n\t\tif ((0 <= value) && (value <= 19)) {\n\t\t\treturn \"idiot\";\n\t\t}\n\n\t\treturn \"out of range\";\n\t}\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic boolean preProcess(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t  Queue<Packet> results, Map<String, Object> settings) {\n\t\ttry {\n\t\t\tif ((session != null) && (packet.getFrom() != null) && packet.getFrom().equals(session.getConnectionId()) &&\n\t\t\t\t\t(packet.getElemName() == tigase.server.Message.ELEM_NAME)) {\n\t\t\t\tevaluateMessage(session, packet.getElemCDataStaticStr(tigase.server.Message.MESSAGE_BODY_PATH));\n\t\t\t}\n\t\t} catch (Exception e) {\n\n\t\t\t// Ignore....\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, final Map<String, Object> settings) throws XMPPException {\n\t\tif ((session == null) && (packet.getType() != null) && (packet.getType() == StanzaType.get)) {\n\t\t\ttry {\n\t\t\t\tString iq_level = repo.getPublicData(packet.getStanzaTo().getBareJID(), ID, LEVEL, null);\n\n\t\t\t\tresults.offer(getResponsePacket(packet, iq_level));\n\t\t\t} catch (UserNotFoundException e) {\n\n\t\t\t\t// Just ignore....\n\t\t\t}    // end of try-catch\n\n\t\t\treturn;\n\t\t}      // end of if (session == null)\n\t\tif (session == null) {\n\t\t\tlog.log(Level.CONFIG, \"Session null, dropping packet: \" + packet.toString());\n\n\t\t\treturn;\n\t\t}    // end of if (session == null)\n\t\ttry {\n\n\t\t\t// Not needed anymore. Packet filter does it for all stanzas.\n//    if (packet.getFrom().equals(session.getConnectionId())) {\n//      packet.getElement().setAttribute(\"from\", session.getJID());\n//    } // end of if (packet.getFrom().equals(session.getConnectionId()))\n\t\t\tBareJID id = null;\n\n\t\t\tif (packet.getStanzaTo() != null) {\n\t\t\t\tid = packet.getStanzaTo().getBareJID();\n\t\t\t}    // end of if (packet.getElemTo() != null)\n\t\t\tif ((id == null) || session.isUserId(id)) {\n\t\t\t\tStanzaType type = packet.getType();\n\n\t\t\t\tswitch (type) {\n\t\t\t\t\tcase get:\n\t\t\t\t\t\tString iq_level = session.getPublicData(ID, LEVEL, null);\n\n\t\t\t\t\t\tresults.offer(getResponsePacket(packet, iq_level));\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase set:\n\t\t\t\t\t\tif (packet.getFrom().equals(session.getConnectionId())) {\n\t\t\t\t\t\t\tString curr_iq = changeIq(session, -2);\n\n\t\t\t\t\t\t\tresults.offer(Authorization.NOT_ALLOWED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"You are not allowed to set own IQ, your current IQ score: \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   curr_iq, true));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresults.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"You are not authorized to set vcard data.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  true));\n\t\t\t\t\t\t}    // end of else\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase result:\n\t\t\t\t\t\tPacket result = packet.copyElementOnly();\n\n\t\t\t\t\t\tresult.setPacketTo(session.getConnectionId());\n\t\t\t\t\t\tresult.setPacketFrom(packet.getTo());\n\t\t\t\t\t\tresults.offer(result);\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet, \"Request type is incorrect\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   false));\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t}    // end of switch (type)\n\t\t\t} else {\n\t\t\t\tPacket result = packet.copyElementOnly();\n\n\t\t\t\tresults.offer(result);\n\t\t\t}      // end of else\n\t\t} catch (NotAuthorizedException e) {\n\t\t\tlog.log(Level.FINE, \"Received privacy request but user session is not authorized yet: \" + packet.toString());\n\t\t\tresults.offer(\n\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(packet, \"You must authorize session first.\", true));\n\t\t} catch (TigaseDBException e) {\n\t\t\tlog.log(Level.WARNING, \"Database proble, please contact admin: \" + e);\n\t\t\tresults.offer(Authorization.INTERNAL_SERVER_ERROR.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Database access problem, please contact administrator.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true));\n\t\t}\n\t}\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(final XMPPResourceConnection session) {\n\t\treturn DISCO_FEATURES;\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\tprivate String changeIq(XMPPResourceConnection session, double val)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tdouble value = getIq(session);\n\n\t\tvalue += val;\n\n\t\tString curr = new Double(value).toString();\n\n\t\tsession.setPublicData(ID, LEVEL, curr);\n\n\t\treturn curr;\n\t}\n\n\tprivate void evaluateMessage(XMPPResourceConnection session, String msg)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tif (msg == null) {\n\t\t\treturn;\n\t\t}\n\n\t\t// User wrote a message, good + 0.01\n\t\tdouble val = 0.01;\n\t\tint msg_len = msg.trim().length();\n\n\t\tif ((msg_len > 10) && (msg_len < 100)) {\n\t\t\tval += 0.01;\n\t\t}\n\t\tif ((msg_len >= 100) && (msg_len < 200)) {\n\t\t\tval += 0.1;\n\t\t}\n\t\tif ((msg_len >= 200) && (msg_len < 500)) {\n\t\t\tval += 0.01;\n\t\t}\n\t\tif (msg_len >= 500) {\n\t\t\tval -= 0.1;\n\t\t}\n\t\tfor (String not_smart : not_so_smart_words) {\n\t\t\tif (msg.contains(new String(Base64.decode(not_smart)))) {\n\t\t\t\tval -= 0.1;\n\t\t\t}\n\t\t}\n\n\t\tdouble iq = getIq(session);\n\n\t\tval = val / iq;\n\t\tiq += val;\n\n\t\tString curr = new Double(iq).toString();\n\n\t\tsession.setPublicData(ID, LEVEL, curr);\n\t}\n\n\tprivate double getIq(XMPPResourceConnection session) throws NotAuthorizedException, TigaseDBException {\n\t\tString iq_level = session.getPublicData(ID, LEVEL, \"100\");\n\t\tdouble iq = 100;\n\n\t\ttry {\n\t\t\tiq = Double.parseDouble(iq_level);\n\t\t} catch (NumberFormatException e) {\n\t\t\tiq = 100;\n\t\t}\n\n\t\treturn iq;\n\t}\n\n\tprivate Packet getResponsePacket(Packet packet, String iq_level) {\n\t\tif (iq_level == null) {\n\t\t\tiq_level = \"100\";\n\t\t}\n\n\t\tElement query = new Element(\"query\", new Element[]{new Element(\"num\", iq_level),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   new Element(\"desc\", calculateIQ(iq_level))},\n\t\t\t\t\t\t\t\t\tnew String[]{\"xmlns\"}, new String[]{XMLNS});\n\n\t\treturn packet.okResult(query, 0);\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/JabberIqPrivacy.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.AuthRepository;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserRepository;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.EventBusEvent;\nimport tigase.eventbus.HandleEvent;\nimport tigase.kernel.beans.*;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.Presence;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.server.xmppsession.SessionManagerHandler;\nimport tigase.server.xmppsession.UserConnectedEvent;\nimport tigase.server.xmppsession.UserSessionEvent;\nimport tigase.util.cache.LRUConcurrentCache;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.vhosts.VHostItem;\nimport tigase.vhosts.VHostItemImpl;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.roster.RosterAbstract;\nimport tigase.xmpp.impl.roster.RosterElement;\nimport tigase.xmpp.impl.roster.RosterFactory;\nimport tigase.xmpp.impl.roster.RosterFlat;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.xmpp.impl.Privacy.*;\n\n/**\n * Describe class JabberIqPrivacy here.\n * <br>\n * Created: Mon Oct  9 18:18:11 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = JabberIqPrivacy.ID, parent = SessionManager.class, active = true)\npublic class JabberIqPrivacy\n\t\textends XMPPProcessor\n\t\timplements XMPPProcessorIfc, XMPPPreprocessorIfc, XMPPPacketFilterIfc, RegistrarBean, Initializable, UnregisterAware {\n\n\tprotected static final String ACTIVE_EL_NAME = \"active\";\n\tprotected static final Element BLOCKED_ELEM = new Element(\"blocked\", new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{\"urn:xmpp:blocking:errors\"});\n\tprotected static final String DEFAULT_EL_NAME = \"default\";\n\tprotected static final String[][] ELEMENTS = {Iq.IQ_QUERY_PATH};\n\tprotected static final String LIST_EL_NAME = \"list\";\n\tprotected static final String ERROR_EL_NAME = \"error\";\n\tprotected static final String PRESENCE_EL_NAME = \"presence\";\n\tprotected static final String PRESENCE_IN_EL_NAME = \"presence-in\";\n\tprotected static final String PRESENCE_OUT_EL_NAME = \"presence-out\";\n\tprotected static final String XMLNS = \"jabber:iq:privacy\";\n\tprotected static final String ID = XMLNS;\n\tprotected static final String[] XMLNSS = {XMLNS};\n\tprotected static final Element[] DISCO_FEATURES = {\n\t\t\tnew Element(\"feature\", new String[]{\"var\"}, new String[]{XMLNS})};\n\tprotected static final Comparator<Element> compar = new Comparator<Element>() {\n\t\t@Override\n\t\tpublic int compare(Element el1, Element el2) {\n\t\t\tString or1 = el1.getAttributeStaticStr(ORDER);\n\t\t\tString or2 = el2.getAttributeStaticStr(ORDER);\n\t\t\tif (or1.length() != or2.length()) {\n\t\t\t\treturn Integer.compare(or1.length(), or2.length());\n\t\t\t}\n\n\t\t\treturn or1.compareTo(or2);\n\t\t}\n\t};\n\tprotected static Logger log = Logger.getLogger(JabberIqPrivacy.class.getName());\n\tprotected static RosterAbstract roster_util = RosterFactory.getRosterImplementation(true);\n\t@Inject(nullAllowed = true)\n\tprotected PrivacyListOfflineCache cache;\n\n\tpublic static Authorization validateList(final XMPPResourceConnection session, final List<Element> items) {\n\t\tAuthorization result = null;\n\n\t\ttry {\n\t\t\tHashSet<Integer> orderSet = new HashSet<Integer>();\n\n\t\t\t// creating set of all known groups in roster\n\t\t\tHashSet<String> groups = new HashSet<String>();\n\n\t\t\tif (session != null) {\n\t\t\t\tJID[] jids = roster_util.getBuddies(session);\n\n\t\t\t\tif (jids != null) {\n\t\t\t\t\tfor (JID jid : jids) {\n\t\t\t\t\t\tString[] buddyGroups = roster_util.getBuddyGroups(session, jid);\n\n\t\t\t\t\t\tif (buddyGroups != null) {\n\t\t\t\t\t\t\tfor (String group : buddyGroups) {\n\t\t\t\t\t\t\t\tgroups.add(group);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (Element item : items) {\n\t\t\t\tITEM_TYPE type = ITEM_TYPE.all;\n\n\t\t\t\tif (item.getAttributeStaticStr(TYPE) != null) {\n\t\t\t\t\ttype = ITEM_TYPE.valueOf(item.getAttributeStaticStr(TYPE));\n\t\t\t\t}    // end of if (item.getAttribute(TYPE) != null)\n\n\t\t\t\tString value = item.getAttributeStaticStr(VALUE);\n\n\t\t\t\tswitch (type) {\n\t\t\t\t\tcase jid:\n\n\t\t\t\t\t\t// if jid is not valid it will throw exception\n\t\t\t\t\t\tJID.jidInstance(value);\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase group:\n\t\t\t\t\t\tboolean matched = groups.contains(value);\n\n\t\t\t\t\t\tif (!matched) {\n\t\t\t\t\t\t\tresult = Authorization.ITEM_NOT_FOUND;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase subscription:\n\n\t\t\t\t\t\t// if subscription is not valid it will throw exception\n\t\t\t\t\t\tITEM_SUBSCRIPTIONS.valueOf(value);\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase all:\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (result != null) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t// if action is not valid it will throw exception\n\t\t\t\tITEM_ACTION.valueOf(item.getAttributeStaticStr(ACTION));\n\n\t\t\t\t// checking unique order attribute value\n\t\t\t\tInteger order = Integer.parseInt(item.getAttributeStaticStr(ORDER));\n\n\t\t\t\tif ((order == null) || (order < 0) || !orderSet.add(order)) {\n\t\t\t\t\tresult = Authorization.BAD_REQUEST;\n\t\t\t\t}\n\t\t\t\tif (result != null) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception ex) {\n\n\t\t\t// if we get exception list is not valid\n\t\t\tresult = Authorization.BAD_REQUEST;\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\n\t}\n\n\t@Override\n\tpublic void filter(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t   Queue<Packet> results) {\n\t\tif ((session == null) || !session.isAuthorized() || (results == null) || (results.size() == 0)) {\n\t\t\treturn;\n\t\t}\n\t\tQueue<Packet> errors = null;\n\t\tfor (Iterator<Packet> it = results.iterator(); it.hasNext(); ) {\n\t\t\tPacket res = it.next();\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Checking outbound packet: {0}\", res);\n\t\t\t}\n\n\t\t\t// Always allow presence unavailable to go, privacy lists packets and\n\t\t\t// all other which are allowed by privacy rules\n\t\t\tif ((res.getType() == StanzaType.unavailable) || res.isXMLNSStaticStr(Iq.IQ_QUERY_PATH, XMLNS) ||\n\t\t\t\t\tallowed(res, session)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Packet not allowed to go, removing: {0}\", res);\n\t\t\t}\n\t\t\tit.remove();\n\n\t\t\t// support for sending error responses if packet is blocked\n\t\t\tPacket error = prepareError(res, session);\n\t\t\tif (error != null) {\n\t\t\t\tif (errors == null) {\n\t\t\t\t\terrors = new ArrayDeque<Packet>();\n\t\t\t\t}\n\t\t\t\terrors.offer(error);\n\t\t\t}\n\t\t}\n\t\tif (errors != null) {\n\t\t\tresults.addAll(errors);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t * <br>\n\t * <code>preProcess</code> method checks only incoming stanzas so it doesn't check for presence-out at all.\n\t */\n\t@Override\n\tpublic boolean preProcess(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t  Queue<Packet> results, Map<String, Object> settings) {\n\t\tif (packet.isXMLNSStaticStr(Iq.IQ_QUERY_PATH, XMLNS)) {\n\t\t\treturn false;\n\t\t}    // end of if (session == null)\n\n\t\tboolean sendError = session != null && session.isAuthorized();\n\n\t\tboolean allowed = allowed(packet, session);\n\t\tif (!allowed && sendError) {\n\t\t\tPacket error = prepareError(packet, session);\n\t\t\tif (error != null) {\n\t\t\t\tresults.offer(error);\n\t\t\t}\n\t\t}\n\t\treturn !allowed;\n\t}\n\n\t@Override\n\tpublic void process(final Packet packet, final XMPPResourceConnection session, final NonAuthUserRepository repo,\n\t\t\t\t\t\tfinal Queue<Packet> results, final Map<String, Object> settings) throws XMPPException {\n\t\tif (session == null) {\n\t\t\treturn;\n\t\t}    // end of if (session == null)\n\t\tif (session.isAnonymous()) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tStanzaType type = packet.getType();\n\n\t\t\tif (type == StanzaType.get) {\n\t\t\t\tprocessGetRequest(packet, session, results);\n\n\t\t\t} else if (type == StanzaType.set) {\n\t\t\t\tprocessSetRequest(packet, session, results);\n\n\t\t\t} else if (type != StanzaType.result && type != StanzaType.error) {\n\t\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet, \"Request type is incorrect\", false));\n\t\t\t}\n\t\t} catch (NotAuthorizedException e) {\n\t\t\tlog.log(Level.FINEST, \"Received privacy request but user session is not authorized yet: {0}\", packet);\n\t\t\tresults.offer(\n\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(packet, \"You must authorize session first.\", true));\n\t\t} catch (TigaseDBException e) {\n\t\t\tlog.log(Level.WARNING, \"Database problem, please contact admin: {0}\", e);\n\t\t\tresults.offer(Authorization.INTERNAL_SERVER_ERROR.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Database access problem, please contact administrator.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true));\n\t\t}\n\t}\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(final XMPPResourceConnection session) {\n\t\treturn DISCO_FEATURES;\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\t@Inject\n\tprivate EventBus eventBus;\n\n\t@Override\n\tpublic void initialize() {\n\t\teventBus.registerAll(this);\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\teventBus.unregisterAll(this);\n\t}\n\n\t@HandleEvent\n\tpublic void privacyListUpdated(PrivacyListUpdatedEvent event) {\n\t\tString name = event.getPrivacyListName();\n\t\tboolean first = true;\n\t\tfor (XMPPResourceConnection session : event.getSession().getActiveResources()) {\n\t\t\ttry {\n\t\t\t\tif (event.getSender().equals(session.getJID())) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tPrivacyList activeList = getActiveList(session);\n\t\t\t\tif (activeList != null  && event.getPrivacyListName().equals(activeList.getName())) {\n\t\t\t\t\tsetActiveList(session, activeList.getName());\n\t\t\t\t}\n\t\t\t\tif (first) {\n\t\t\t\t\tif (name.equals(Privacy.getDefaultListName(session))) {\n\t\t\t\t\t\tElement defaultListEl = getDefaultListElement(session);\n\t\t\t\t\t\tPrivacy.setDefaultList(session, defaultListEl);\n\t\t\t\t\t}\n\t\t\t\t\tfirst = false;\n\t\t\t\t}\n\t\t\t} catch (NotAuthorizedException|TigaseDBException ex) {\n\t\t\t\t// just ignore this, as privacy list was not loaded anyway or there is a database access issue and we\n\t\t\t\t// cannot do anything about that\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected boolean allowed(Packet packet, JID connId, BareJID userJid, PrivacyList privacyList) {\n\t\tif (allowedByDefault(packet, connId, userJid)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (privacyList != null) {\n\t\t\tJID jid = packet.getStanzaFrom();\n\t\t\tboolean packetIn = true;\n\n\t\t\tif ((jid == null) || userJid.equals(jid.getBareJID())) {\n\t\t\t\tjid = packet.getStanzaTo();\n\t\t\t\tpacketIn = false;\n\t\t\t}\n\n\t\t\tPrivacyList.Item.Type type = null;\n\t\t\tif (packetIn) {\n\t\t\t\tswitch (packet.getElemName()) {\n\t\t\t\t\tcase Presence.ELEM_NAME:\n\t\t\t\t\t\ttype = PrivacyList.Item.Type.presenceIn;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase Message.ELEM_NAME:\n\t\t\t\t\t\ttype = PrivacyList.Item.Type.message;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase Iq.ELEM_NAME:\n\t\t\t\t\t\ttype = PrivacyList.Item.Type.iq;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (packet.getElemName() == Presence.ELEM_NAME) {\n\t\t\t\t\ttype = PrivacyList.Item.Type.presenceOut;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (type != null) {\n\t\t\t\treturn privacyList.isAllowed(jid, type);\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprotected boolean allowed(Packet packet, XMPPResourceConnection session) {\n\t\tif (session != null && session.isAuthorized()) {\n\t\t\ttry {\n\t\t\t\tPrivacyList list = Privacy.getActiveList(session);\n\t\t\t\tif (list == null) {\n\t\t\t\t\tlist = Privacy.getDefaultList(session);\n\t\t\t\t}\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Using privacy list: {0}\", list);\n\t\t\t\t}\n\t\t\t\tif (list != null) {\n\t\t\t\t\treturn allowed(packet, session.getConnectionId(), session.getBareJID(), list);\n\t\t\t\t}\n\t\t\t} catch (NoConnectionIdException e) {\n\n\t\t\t\t// Always allow, this is server dummy session\n\t\t\t} catch (NotAuthorizedException e) {\n//    results.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet,\n//        \"You must authorize session first.\", true));\n\t\t\t} catch (TigaseDBException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Database problem, please notify the admin. {0}\", e);\n\t\t\t}\n\t\t} else if (cache != null) {\n\t\t\tJID to = packet.getStanzaTo();\n\t\t\tif (to != null) {\n\t\t\t\tPrivacyList list = cache.getPrivacyList(to.getBareJID());\n\t\t\t\tif (list != null) {\n\t\t\t\t\treturn allowed(packet, null, to.getBareJID(), list);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprotected boolean allowedByDefault(Packet packet, JID connId, BareJID userJid) {\n\t\t// If this is a preprocessing phase, always allow all packets to\n\t\t// make it possible for the client to communicate with the server.\n\t\tif (connId != null && connId.equals(packet.getPacketFrom())) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// allow packets without from attribute and packets with from attribute same as domain name\n\t\tif ((packet.getStanzaFrom() == null) || ((packet.getStanzaFrom().getLocalpart() == null) &&\n\t\t\t\tuserJid.getDomain().equals(packet.getStanzaFrom().getDomain()))) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// allow packets without to attribute and packets with to attribute same as domain name\n\t\tif ((packet.getStanzaTo() == null) || ((packet.getStanzaTo().getLocalpart() == null) &&\n\t\t\t\tuserJid.getDomain().equals(packet.getStanzaTo().getDomain()))) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Always allow packets sent between sessions of same user and\n\t\t// packets sent from user to his bare jid and results of this\n\t\t// packets\n\t\tif (packet.getStanzaFrom() != null && packet.getStanzaTo() != null &&\n\t\t\t\tpacket.getStanzaFrom().getBareJID().equals(packet.getStanzaTo().getBareJID())) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (packet.getType() == StanzaType.error && packet.getStanzaTo() != null &&\n\t\t\t\tuserJid.equals(packet.getStanzaTo().getBareJID())) {\n\t\t\t// this may be error sent back to sender of blocked request\n\t\t\t// or even to user who sent packet to blocked user\n\t\t\tElement error = packet.getElement().findChild(e -> e.getName() == ERROR_EL_NAME);\n\t\t\tif (error != null &&\n\t\t\t\t\terror.findChild(e -> e.getName() == Authorization.NOT_ACCEPTABLE.getCondition()) != null &&\n\t\t\t\t\terror.findChild(\n\t\t\t\t\t\t\te -> e.getName() == BLOCKED_ELEM.getName() && e.getXMLNS() == BLOCKED_ELEM.getXMLNS()) !=\n\t\t\t\t\t\t\tnull) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprotected Packet prepareError(Packet packet, XMPPResourceConnection session) {\n\t\tif (packet.getType() != StanzaType.error) {\n\t\t\ttry {\n\t\t\t\tPacket error = null;\n\t\t\t\tswitch (packet.getElemName()) {\n\t\t\t\t\tcase \"presence\":\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"message\":\n\t\t\t\t\t\tif (packet.getStanzaFrom() == null || session.isUserId(packet.getStanzaFrom().getBareJID())) {\n\t\t\t\t\t\t\t// we need this here to make sure that this error will be sent to user only once\n\t\t\t\t\t\t\t// as there might be many outgoing messages as result of sending message to single\n\t\t\t\t\t\t\t// user - ie. messages sent to message archive, etc.\n\t\t\t\t\t\t\tif (packet.getPacketTo() != null) {\n\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\terror = Authorization.NOT_ACCEPTABLE.getResponseMessage(packet, null, true);\n\t\t\t\t\t\t\terror.getElement().getChild(\"error\").addChild(BLOCKED_ELEM.clone());\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\terror = Authorization.SERVICE_UNAVAILABLE.getResponseMessage(packet, null, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn error;\n\t\t\t\t\tcase \"iq\":\n\t\t\t\t\t\tif (packet.getType() == StanzaType.get || packet.getType() == StanzaType.set) {\n\t\t\t\t\t\t\tif (packet.getStanzaFrom() == null ||\n\t\t\t\t\t\t\t\t\tsession.isUserId(packet.getStanzaFrom().getBareJID())) {\n\t\t\t\t\t\t\t\terror = Authorization.NOT_ACCEPTABLE.getResponseMessage(packet, null, true);\n\t\t\t\t\t\t\t\terror.getElement().getChild(\"error\").addChild(BLOCKED_ELEM.clone());\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\terror = Authorization.SERVICE_UNAVAILABLE.getResponseMessage(packet, null, true);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn error;\n\t\t\t\t}\n\t\t\t} catch (NotAuthorizedException ex) {\n\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\"Packet droped due to privacy list rules. Error could not be generated properly \" +\n\t\t\t\t\t\t\t\t\"as session is not authorized yet, should not happen.\", ex);\n\t\t\t} catch (PacketErrorTypeException ex) {\n\t\t\t\tlog.log(Level.FINER, \"Packet dropped due to privacy list rules. Packet is error type already: {0}\",\n\t\t\t\t\t\tpacket.toStringSecure());\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprotected void processGetRequest(final Packet packet, final XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t final Queue<Packet> results)\n\t\t\tthrows NotAuthorizedException, XMPPException, TigaseDBException {\n\t\tList<Element> children = packet.getElemChildrenStaticStr(Iq.IQ_QUERY_PATH);\n\n\t\tif ((children == null) || (children.size() == 0)) {\n\t\t\tString[] lists = Privacy.getLists(session);\n\n\t\t\tif (lists != null) {\n\t\t\t\tStringBuilder sblists = new StringBuilder(100);\n\n\t\t\t\tfor (String list : lists) {\n\t\t\t\t\tsblists.append(\"<list name=\\\"\").append(list).append(\"\\\"/>\");\n\t\t\t\t}\n\n\t\t\t\tString list = Privacy.getDefaultListName(session);\n\n\t\t\t\tif (list != null) {\n\t\t\t\t\tsblists.append(\"<default name=\\\"\").append(list).append(\"\\\"/>\");\n\t\t\t\t}    // end of if (defList != null)\n\t\t\t\tlist = Privacy.getActiveListName(session);\n\t\t\t\tif (list != null) {\n\t\t\t\t\tsblists.append(\"<active name=\\\"\").append(list).append(\"\\\"/>\");\n\t\t\t\t}    // end of if (defList != null)\n\t\t\t\tresults.offer(packet.okResult(sblists.toString(), 1));\n\t\t\t} else {\n\t\t\t\tresults.offer(packet.okResult((String) null, 1));\n\t\t\t}      // end of if (buddies != null) else\n\t\t} else {\n\t\t\tif (children.size() > 1) {\n\t\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"You can retrieve only one list at a time.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   true));\n\t\t\t} else {\n\t\t\t\tElement eList = Privacy.getList(session, children.get(0).getAttributeStaticStr(\"name\"));\n\n\t\t\t\tif (eList != null) {\n\t\t\t\t\tresults.offer(packet.okResult(eList, 1));\n\t\t\t\t} else {\n\t\t\t\t\tresults.offer(\n\t\t\t\t\t\t\tAuthorization.ITEM_NOT_FOUND.getResponseMessage(packet, \"Requested list not found.\", true));\n\t\t\t\t}    // end of if (eList != null) else\n\t\t\t}      // end of else\n\t\t}        // end of else\n\t}\n\n\tprotected void processSetRequest(final Packet packet, final XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t final Queue<Packet> results)\n\t\t\tthrows NotAuthorizedException, XMPPException, TigaseDBException {\n\t\tList<Element> children = packet.getElemChildrenStaticStr(Iq.IQ_QUERY_PATH);\n\n\t\tif ((children != null) && (children.size() == 1)) {\n\t\t\tElement child = children.get(0);\n\n\t\t\tif (child.getName() == LIST_EL_NAME) {\n\n\t\t\t\t// Broken privacy implementation sends list without name set\n\t\t\t\t// instead of sending BAD_REQUEST error I can just assign\n\t\t\t\t// 'default' name here.\n\t\t\t\tString name = child.getAttributeStaticStr(NAME);\n\n\t\t\t\tif ((name == null) || (name.length() == 0)) {\n\t\t\t\t\tchild.setAttribute(NAME, \"default\");\n\t\t\t\t}    // end of if (name == null || name.length() == 0)\n\n\t\t\t\tList<Element> items = child.getChildren();\n\n\t\t\t\tif ((items == null) || items.isEmpty()) {\n\t\t\t\t\t// if the list is in use then forbid changes\n\t\t\t\t\tboolean inUse = session.getCommonSessionData(PRIVACY_LIST_LOADED) != null &&\n\t\t\t\t\t\t\tsession.getCommonSessionData(DEFAULT) != null;\n\n\t\t\t\t\tif (!inUse) {\n\t\t\t\t\t\tfor (XMPPResourceConnection activeSession : session.getActiveSessions()) {\n\t\t\t\t\t\t\tif (activeSession.equals(session)) {\n\t\t\t\t\t\t\t\t// don't apply to the current session\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tinUse |= name.equals(Privacy.getActiveListName(activeSession));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (inUse) {\n\t\t\t\t\t\tresults.offer(Authorization.CONFLICT.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"Can not modify list while being in use by other session\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttrue));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tPrivacy.removeList(session, child);\n\t\t\t\t\t\tresults.offer(packet.okResult((String) null, 0));\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tAuthorization error = validateList(session, items);\n\n\t\t\t\t\tif (error == null) {\n\t\t\t\t\t\tPrivacy.addList(session, child);\n\n\t\t\t\t\t\t// updating active list if it's name matches name of updated list\n\t\t\t\t\t\tfor (XMPPResourceConnection activeSession : session.getActiveSessions()) {\n\t\t\t\t\t\t\tif (name.equals(Privacy.getActiveListName(activeSession))) {\n\t\t\t\t\t\t\t\tPrivacy.setActiveList(activeSession, name);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// update default list\n\t\t\t\t\t\tif (name.equals(Privacy.getDefaultListName(session))) {\n\t\t\t\t\t\t\tPrivacy.setDefaultList(session, child);\n\t\t\t\t\t\t}\n\t\t\t\t\t\teventBus.fire(\n\t\t\t\t\t\t\t\tnew PrivacyListUpdatedEvent(session.getJID(), session.getJID().copyWithoutResource(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsession.getParentSession(), name));\n\t\t\t\t\t\tresults.offer(packet.okResult((String) null, 0));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresults.offer(error.getResponseMessage(packet, null, true));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}      // end of if (child.getName().equals(\"list))\n\t\t\tif (child.getName() == DEFAULT_EL_NAME) {\n\n\t\t\t\t// User selects a different default list\n\t\t\t\tString listName = child.getAttributeStaticStr(NAME);\n\t\t\t\tElement list = Privacy.getList(session, listName);\n\n\t\t\t\tif ((listName != null) && (list == null)) {\n\t\t\t\t\tresults.offer(Authorization.ITEM_NOT_FOUND.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"Selected list was not found on the server\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  true));\n\t\t\t\t} else {\n\t\t\t\t\t// This is either declining of default list use or setting a new default list\n\t\t\t\t\tPrivacy.setDefaultList(session, list);\n\t\t\t\t\tresults.offer(packet.okResult((String) null, 0));\n\t\t\t\t}\n\t\t\t}      // end of if (child.getName().equals(\"list))\n\t\t\tif (child.getName() == ACTIVE_EL_NAME) {\n\n\t\t\t\t// User selects a different active list\n\t\t\t\tString listName = child.getAttributeStaticStr(NAME);\n\t\t\t\tElement list = Privacy.getList(session, listName);\n\n\t\t\t\tif ((listName != null) && (list == null)) {\n\t\t\t\t\tresults.offer(Authorization.ITEM_NOT_FOUND.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"Selected list was not found on the server\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  true));\n\t\t\t\t} else {\n\n\t\t\t\t\t// This is either declining of active list use or setting a new active list\n\t\t\t\t\tPrivacy.setActiveList(session, child.getAttributeStaticStr(NAME));\n\t\t\t\t\tresults.offer(packet.okResult((String) null, 0));\n\t\t\t\t}\n\t\t\t}    // end of if (child.getName().equals(\"list))\n\t\t} else {\n\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"Only 1 element is allowed in privacy set request.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   true));\n\t\t}    // end of else\n\t}\n\n\tprotected enum ITEM_ACTION {\n\t\tallow,\n\t\tdeny\n\t}\n\n\tprotected enum ITEM_SUBSCRIPTIONS {\n\t\tboth,\n\t\tto,\n\t\tfrom,\n\t\tnone\n\t}\n\n\tprotected enum ITEM_TYPE {\n\t\tjid,\n\t\tgroup,\n\t\tsubscription,\n\t\tall\n\t}\n\n\tpublic static class PrivacyListUpdatedEvent extends UserSessionEvent implements EventBusEvent {\n\n\t\tprivate String privacyListName;\n\n\t\tpublic PrivacyListUpdatedEvent() {\n\t\t\tsuper();\n\t\t}\n\n\t\tpublic PrivacyListUpdatedEvent(JID sender, JID userJid, XMPPSession session, String privacyListName) {\n\t\t\tsuper(sender, userJid, session);\n\t\t\tthis.privacyListName = privacyListName;\n\t\t}\n\n\t\tpublic String getPrivacyListName() {\n\t\t\treturn privacyListName;\n\t\t}\n\n\t\tpublic void setPrivacyListName(String privacyListName) {\n\t\t\tthis.privacyListName = privacyListName;\n\t\t}\n\t}\n\n\tpublic static class OfflineResourceConnection\n\t\t\textends XMPPResourceConnection {\n\n\t\t/**\n\t\t * Creates a new <code>XMPPResourceConnection</code> instance.\n\t\t */\n\t\tpublic OfflineResourceConnection(JID connectionId, UserRepository rep, AuthRepository authRepo,\n\t\t\t\t\t\t\t\t\t\t SessionManagerHandler loginHandler) {\n\t\t\tsuper(connectionId, rep, authRepo, loginHandler);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isAuthorized() {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t@Bean(name = \"privacyListOfflineCache\", parent = JabberIqPrivacy.class, active = false)\n\tpublic static class PrivacyListOfflineCache\n\t\t\timplements SessionManagerHandler, Initializable, UnregisterAware {\n\n\t\t@Inject\n\t\tprivate AuthRepository authRepository;\n\t\t@ConfigField(desc = \"Cache size\", alias = \"size\")\n\t\tprivate int cacheSize = 10000;\n\t\tprivate LRUConcurrentCache<BareJID, PrivacyList> cache = new LRUConcurrentCache<>(cacheSize);\n\t\tprivate JID compId = JID.jidInstanceNS(\"privacy-sessman\", DNSResolverFactory.getInstance().getDefaultHost());\n\t\t@Inject\n\t\tprivate EventBus eventBus;\n\t\tprivate JID offlineConnectionId = JID.jidInstanceNS(\"offline-connection\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tDNSResolverFactory.getInstance().getDefaultHost());\n\t\t@Inject\n\t\tprivate UserRepository userRepository;\n\n\t\tpublic void clear() {\n\t\t\tcache.clear();\n\t\t}\n\n\t\t@Override\n\t\tpublic JID getComponentId() {\n\t\t\treturn compId;\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleLogin(BareJID userId, XMPPResourceConnection conn) {\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleDomainChange(String domain, XMPPResourceConnection conn) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleLogout(BareJID userId, XMPPResourceConnection conn) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void handlePresenceSet(XMPPResourceConnection conn) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleResourceBind(XMPPResourceConnection conn) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isLocalDomain(String domain, boolean includeComponents) {\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic void initialize() {\n\t\t\teventBus.registerAll(this);\n\t\t}\n\n\t\t@Override\n\t\tpublic void beforeUnregister() {\n\t\t\teventBus.unregisterAll(this);\n\t\t}\n\n\t\tpublic void setCacheSize(int cacheSize) {\n\t\t\tthis.cacheSize = cacheSize;\n\t\t\tif (cache.limit() != cacheSize) {\n\t\t\t\tcache = new LRUConcurrentCache<>(cacheSize);\n\t\t\t}\n\t\t}\n\n\t\t@HandleEvent\n\t\tprotected void userConnected(UserConnectedEvent event) {\n\t\t\tcache.remove(event.getUserJid().getBareJID());\n\t\t}\n\n\t\tprotected PrivacyList getPrivacyList(BareJID userJID) {\n\t\t\tif (!cache.containsKey(userJID)) {\n\t\t\t\ttry {\n\t\t\t\t\tPrivacyList list = this.loadList(userJID);\n\t\t\t\t\tcache.put(userJID, list);\n\t\t\t\t} catch (NotAuthorizedException | TigaseDBException ex) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn cache.get(userJID);\n\t\t}\n\n\t\tprotected PrivacyList loadList(BareJID userJID) throws NotAuthorizedException, TigaseDBException {\n\t\t\tXMPPResourceConnection session = createXMPPResourceConnection(userJID);\n\t\t\tif (roster_util instanceof RosterFlat) {\n\t\t\t\tElement listEl = Privacy.getDefaultListElement(session);\n\t\t\t\tif (listEl == null) {\n\t\t\t\t\treturn PrivacyList.ALLOW_ALL;\n\t\t\t\t}\n\t\t\t\tfinal Map<BareJID, RosterElement> roster = ((RosterFlat) roster_util).loadUserRoster(session);\n\t\t\t\treturn PrivacyList.create(roster, listEl);\n\t\t\t} else {\n\t\t\t\treturn Privacy.getDefaultList(session);\n\t\t\t}\n\t\t}\n\n\t\tprivate XMPPResourceConnection createXMPPResourceConnection(BareJID userJid) {\n\t\t\ttry {\n\t\t\t\tXMPPResourceConnection session = new OfflineResourceConnection(offlineConnectionId, userRepository,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   authRepository, this);\n\t\t\t\tVHostItem vhost = new VHostItemImpl(userJid.getDomain());\n\t\t\t\tsession.setDomain(vhost);\n\t\t\t\tsession.authorizeJID(userJid, false);\n\t\t\t\tXMPPSession parentSession = new XMPPSession(userJid.getLocalpart());\n\t\t\t\tsession.setParentSession(parentSession);\n\t\t\t\treturn session;\n\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"creation of temporary session for offline user \" + userJid + \" failed\", ex);\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t}\n}    // JabberIqPrivacy\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/JabberIqPrivate.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.DomBuilderHandler;\nimport tigase.xml.Element;\nimport tigase.xml.SimpleParser;\nimport tigase.xml.SingletonFactory;\nimport tigase.xmpp.*;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Describe class JabberIqPrivate here.\n * <br>\n * Created: Mon Apr 16 08:28:18 2007\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = JabberIqPrivate.ID, parent = SessionManager.class, active = true)\npublic class JabberIqPrivate\n\t\textends XMPPProcessor\n\t\timplements XMPPProcessorIfc {\n\n\tprivate static final String[][] ELEMENTS = {Iq.IQ_QUERY_PATH};\n\tprivate static final String XMLNS = \"jabber:iq:private\";\n\tprotected static final String ID = XMLNS;\n\tprivate static final String PRIVATE_KEY = XMLNS;\n\tprivate static final String[] XMLNSS = {XMLNS};\n\tprivate static final SimpleParser parser = SingletonFactory.getParserInstance();\n\tprivate static final Element[] DISCO_FEATURES = {new Element(\"feature\", new String[]{\"var\"}, new String[]{XMLNS})};\n\tprivate static Logger log = Logger.getLogger(JabberIqPrivate.class.getName());\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\n\t\t// Don't do anything if session is null\n\t\tif (session == null) {\n\t\t\tlog.log(Level.CONFIG, \"Session null, dropping packet: \" + packet.toString());\n\n\t\t\treturn;\n\t\t}    // end of if (session == null)\n\t\ttry {\n\t\t\tif ((packet.getStanzaTo() != null) && !session.isUserId(packet.getStanzaTo().getBareJID())) {\n\t\t\t\tresults.offer(Authorization.SERVICE_UNAVAILABLE.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"You are not authorized to access this private storage.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   true));\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (packet.getFrom().equals(session.getConnectionId())) {\n\t\t\t\tList<Element> elems = packet.getElemChildrenStaticStr(Iq.IQ_QUERY_PATH);\n\n\t\t\t\tif ((elems != null) && (elems.size() > 0)) {\n\t\t\t\t\tElement elem = elems.get(0);\n\t\t\t\t\tStanzaType type = packet.getType();\n\n\t\t\t\t\tswitch (type) {\n\t\t\t\t\t\tcase get:\n\t\t\t\t\t\t\tString priv = session.getData(PRIVATE_KEY, elem.getName() + elem.getXMLNS(), null);\n\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.finest(\"Loaded private data for key: \" + elem.getName() + \": \" + priv);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (priv != null) {\n\t\t\t\t\t\t\t\tresults.offer(parseXMLData(priv, packet));\n\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tresults.offer(packet.okResult((String) null, 2));\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase set:\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.finest(\"Saving private data: \" + elem.toString());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tsession.setData(PRIVATE_KEY, elem.getName() + elem.getXMLNS(), elem.toString());\n\t\t\t\t\t\t\tresults.offer(packet.okResult((String) null, 0));\n\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase result:\n\n\t\t\t\t\t\t\t// Should never happen, it is an error and should be ignored\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tresults.offer(\n\t\t\t\t\t\t\t\t\tAuthorization.BAD_REQUEST.getResponseMessage(packet, \"Request type is incorrect\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t false));\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}    // end of switch (type)\n\t\t\t\t} else {\n\t\t\t\t\tresults.offer(Authorization.NOT_ACCEPTABLE.getResponseMessage(packet, \"Missing query child element\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  true));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tresults.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"You are not authorized to access this private storage.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  true));\n\t\t\t}    // end of else\n\t\t} catch (NotAuthorizedException e) {\n\t\t\tlog.log(Level.FINE, \"Received privacy request but user session is not authorized yet: \" + packet.toString());\n\t\t\tresults.offer(\n\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(packet, \"You must authorize session first.\", true));\n\t\t} catch (TigaseDBException e) {\n\t\t\tlog.log(Level.WARNING, \"Database proble, please contact admin: \" + e);\n\t\t\tresults.offer(Authorization.INTERNAL_SERVER_ERROR.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Database access problem, please contact administrator.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true));\n\t\t}    // end of try-catch\n\t}\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(final XMPPResourceConnection session) {\n\t\treturn DISCO_FEATURES;\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\tprivate Packet parseXMLData(String data, Packet packet) {\n\t\tDomBuilderHandler domHandler = new DomBuilderHandler();\n\n\t\tparser.parse(domHandler, data.toCharArray(), 0, data.length());\n\n\t\tQueue<Element> elems = domHandler.getParsedElements();\n\t\tPacket result = packet.okResult((Element) null, 1);\n\t\tElement query = result.getElement().findChildStaticStr(Iq.IQ_QUERY_PATH);\n\n\t\tfor (Element el : elems) {\n\t\t\tquery.addChild(el);\n\t\t}    // end of for (Element el: elems)\n\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/JabberIqRegister.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.auth.credentials.Credentials;\nimport tigase.db.*;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.EventBusEvent;\nimport tigase.eventbus.HandleEvent;\nimport tigase.form.*;\nimport tigase.kernel.beans.*;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Message;\nimport tigase.server.*;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.stats.StatisticsList;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.vhosts.VHostItem;\nimport tigase.xml.Element;\nimport tigase.xml.XMLUtils;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.io.Serializable;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\n\nimport static tigase.xmpp.Authorization.NOT_ALLOWED;\nimport static tigase.xmpp.impl.CaptchaProvider.CaptchaItem;\n\n/**\n * XEP-0077: In-Band Registration\n * <br>\n * Created: Thu Feb 16 13:14:06 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\n@Bean(name = JabberIqRegister.ID, parent = SessionManager.class, active = true)\npublic class JabberIqRegister\n\t\textends XMPPProcessor\n\t\timplements XMPPProcessorIfc, Initializable, UnregisterAware, RegistrarBean {\n\n\tpublic static final String ID = \"jabber:iq:register\";\n\tpublic static final String REGISTRATION_PER_SECOND_PROP_KEY = \"registrations-per-second\";\n\tpublic static final String REGISTRATION_BLACKLIST_PROP_KEY = \"registration-blacklist\";\n\tpublic static final String REGISTRATION_WHITELIST_PROP_KEY = \"registration-whitelist\";\n\tpublic static final String WHITELIST_REGISTRATION_ONLY_PROP_KEY = \"whitelist-registration-only\";\n\t/**\n\t * OAuth details for form verifier.\n\t */\n\tpublic static final String OAUTH_CONSUMERKEY_PROP_KEY = \"oauth-consumer-key\";\n\tpublic static final String OAUTH_CONSUMERSECRET_PROP_KEY = \"oauth-consumer-secret\";\n\tpublic static final String SIGNED_FORM_REQUIRED_PROP_KEY = \"signed-form-required\";\n\tprivate final static String INSTRUCTION_DEF = \"Please provide the following information to sign up for an account\";\n\tprivate final static String INSTRUCTION_EMAIL_REQUIRED_DEF = INSTRUCTION_DEF +\n\t\t\t\"\\n\\nPlease also provide your e-mail address (must be valid!) to which we will send confirmation link.\";\n\tprivate static final int REMOTE_ADDRESS_IDX = 2;\n\tprivate static final String[][] ELEMENTS = {Iq.IQ_QUERY_PATH};\n\tprivate static final String[] XMLNSS = {\"jabber:iq:register\"};\n\tprivate static final String[] IQ_QUERY_USERNAME_PATH = {Iq.ELEM_NAME, Iq.QUERY_NAME, \"username\"};\n\tprivate static final String[] IQ_QUERY_REMOVE_PATH = {Iq.ELEM_NAME, Iq.QUERY_NAME, \"remove\"};\n\tprivate static final String[] IQ_QUERY_PASSWORD_PATH = {Iq.ELEM_NAME, Iq.QUERY_NAME, \"password\"};\n\tprivate static final String[] IQ_QUERY_EMAIL_PATH = {Iq.ELEM_NAME, Iq.QUERY_NAME, \"email\"};\n\tprivate static final Element[] FEATURES = {\n\t\t\tnew Element(\"register\", new String[]{\"xmlns\"}, new String[]{\"http://jabber.org/features/iq-register\"})};\n\tprivate static final Element[] DISCO_FEATURES = {\n\t\t\tnew Element(\"feature\", new String[]{\"var\"}, new String[]{\"jabber:iq:register\"})};\n\tprivate static final BareJID smJid = BareJID.bareJIDInstanceNS(\"sess-man\");\n\tprivate static Logger log = Logger.getLogger(JabberIqRegister.class.getName());\n\t@Inject\n\tprivate CaptchaProvider captchaProvider;\n\t@ConfigField(desc = \"CAPTCHA Required\")\n\tprivate boolean captchaRequired = false;\n\t@ConfigField(desc = \"Email Required\")\n\tprivate boolean emailRequired = true;\n\t@Inject\n\tprivate EventBus eventBus;\n\t@ConfigField(desc = \"Instruction displayed during account registration\")\n\tprivate String instruction = INSTRUCTION_DEF;\n\t@ConfigField(desc = \"Instruction displayed during account registration when e-mail is required\")\n\tprivate String instructionEmailRequired = INSTRUCTION_EMAIL_REQUIRED_DEF;\n\t@ConfigField(desc = \"Maximum CAPTCHA repetition in session\")\n\tprivate int maxCaptchaRepetition = 3;\n\t@ConfigField(desc = \"OAuth consumer key\", alias = OAUTH_CONSUMERKEY_PROP_KEY)\n\tprivate String oauthConsumerKey;\n\t@ConfigField(desc = \"OAuth consumer secret\", alias = OAUTH_CONSUMERSECRET_PROP_KEY)\n\tprivate String oauthConsumerSecret;\n\t@ConfigField(desc = \"Registration blacklist\", alias = REGISTRATION_BLACKLIST_PROP_KEY)\n\tprivate LinkedList<CIDRAddress> registrationBlacklist = new LinkedList<CIDRAddress>();\n\t@ConfigField(desc = \"Registration whitelist\", alias = REGISTRATION_WHITELIST_PROP_KEY)\n\tprivate LinkedList<CIDRAddress> registrationWhitelist = new LinkedList<CIDRAddress>();\n\t@ConfigField(desc = \"Registrations per second\", alias = REGISTRATION_PER_SECOND_PROP_KEY)\n\tprivate long registrationsPerSecond = 0;\n\t@ConfigField(desc = \"Require form signed with OAuth\", alias = SIGNED_FORM_REQUIRED_PROP_KEY)\n\tprivate boolean signedFormRequired = false;\n\tprivate long statsInvalidRegistrations;\n\tprivate long statsRegisteredUsers;\n\tprivate TokenBucketPool tokenBucket = new TokenBucketPool();\n\t@Inject\n\tprivate UserRepository userRepository;\n\t@Inject(nullAllowed = true)\n\tprivate AccountValidator[] validators = null;\n\tprivate String welcomeMessage = null;\n\t@ConfigField(desc = \"Allow registration only for whitelisted addresses\", alias = WHITELIST_REGISTRATION_ONLY_PROP_KEY)\n\tprivate boolean whitelistRegistrationOnly = false;\n\n\tprivate static boolean contains(List<CIDRAddress> addresses, String address) {\n\t\tif (address == null) {\n\t\t\treturn false;\n\t\t}\n\t\tfor (CIDRAddress cidrAddress : addresses) {\n\t\t\tif (cidrAddress.inRange(address)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static LinkedList<CIDRAddress> parseList(String listStr) {\n\t\tString[] splitArray = listStr.split(\",\");\n\t\tLinkedList<CIDRAddress> splitList = new LinkedList<CIDRAddress>();\n\t\tfor (String listEl : splitArray) {\n\t\t\tsplitList.add(CIDRAddress.parse(listEl.trim()));\n\t\t}\n\t\treturn splitList;\n\t}\n\n\tprivate static String parseRemoteAddressFromJid(JID from) {\n\t\ttry {\n\t\t\tString connectionId = from.getResource();\n\t\t\treturn connectionId.split(\"_\")[REMOTE_ADDRESS_IDX];\n\t\t} catch (ArrayIndexOutOfBoundsException e) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic boolean isEmailRequired() {\n\t\treturn emailRequired;\n\t}\n\n\tpublic void setEmailRequired(boolean emailRequired) {\n\t\tthis.emailRequired = emailRequired;\n\t}\n\n\tpublic boolean isCaptchaRequired() {\n\t\treturn captchaRequired;\n\t}\n\n\tpublic void setCaptchaRequired(boolean captchaRequired) {\n\t\tthis.captchaRequired = captchaRequired;\n\t}\n\n\tpublic long getRegistrationsPerSecond() {\n\t\treturn tokenBucket.getDefaultRate();\n\t}\n\n\tpublic void setRegistrationsPerSecond(long registrationsPerSecond) {\n\t\ttokenBucket.setDefaultRate(registrationsPerSecond);\n\t}\n\n\tpublic LinkedList<String> getRegistrationBlacklist() {\n\t\treturn registrationBlacklist.stream()\n\t\t\t\t.map(cidr -> cidr.toString())\n\t\t\t\t.collect(Collectors.toCollection(LinkedList::new));\n\t}\n\n\tpublic void setRegistrationBlacklist(LinkedList<String> vals) {\n\t\tif (vals == null) {\n\t\t\tregistrationBlacklist = new LinkedList<>();\n\t\t} else {\n\t\t\tregistrationBlacklist = vals.stream()\n\t\t\t\t\t.map(val -> CIDRAddress.parse(val))\n\t\t\t\t\t.collect(Collectors.toCollection(LinkedList::new));\n\t\t}\n\t}\n\n\tpublic LinkedList<String> getRegistrationWhitelist() {\n\t\treturn registrationWhitelist.stream()\n\t\t\t\t.map(cidr -> cidr.toString())\n\t\t\t\t.collect(Collectors.toCollection(LinkedList::new));\n\t}\n\n\tpublic void setRegistrationWhitelist(LinkedList<String> vals) {\n\t\tif (vals == null) {\n\t\t\tregistrationWhitelist = new LinkedList<>();\n\t\t} else {\n\t\t\tregistrationWhitelist = vals.stream()\n\t\t\t\t\t.map(val -> CIDRAddress.parse(val))\n\t\t\t\t\t.collect(Collectors.toCollection(LinkedList::new));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\teventBus.unregisterAll(this);\n\t\ttokenBucket.beforeUnregister();\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\t\tkernel.registerBean(\"tokenBucketPool\").asClass(TokenBucketPool.class).exec();\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t\tsuper.getStatistics(list);\n\t\tlist.add(getComponentInfo().getName(), \"Registered users\", statsRegisteredUsers, Level.INFO);\n\t\tlist.add(getComponentInfo().getName(), \"Invalid registrations\", statsInvalidRegistrations, Level.INFO);\n\t}\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\ttokenBucket.initialize();\n\t\teventBus.registerAll(this);\n\t\ttry {\n\t\t\tif (userRepository.userExists(smJid)) {\n\t\t\t\twelcomeMessage = userRepository.getData(smJid, ID, \"welcome-message\");\n\t\t\t} else {\n\t\t\t\tuserRepository.addUser(smJid);\n\t\t\t}\n\t\t} catch (TigaseDBException ex) {\n\t\t\tlog.log(Level.WARNING, \"failed to read current welcome message from user repository\", ex);\n\t\t}\n\t}\n\n\tpublic boolean isSignedFormRequired() {\n\t\treturn signedFormRequired;\n\t}\n\n\tpublic void setSignedFormRequired(boolean required) {\n\t\tthis.signedFormRequired = required;\n\t}\n\n\t@HandleEvent\n\tpublic void onWelcomeMessageChange(WelcomeMessageChangedEvent event) {\n\t\tthis.welcomeMessage = event.getMessage();\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t * <br>\n\t * TODO: Implement registration form configurable and loading all the fields from the registration form TODO:\n\t * rewrite the plugin using the XMPPProcessorAbstract API\n\t */\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Processing packet: \" + packet.toString());\n\t\t}\n\t\tif (session == null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Session is null, ignoring\");\n\t\t\t}\n\n\t\t\treturn;\n\t\t} // end of if (session == null)\n\n\t\tBareJID id = session.getDomainAsJID().getBareJID();\n\n\t\tif (packet.getStanzaTo() != null) {\n\t\t\tid = packet.getStanzaTo().getBareJID();\n\t\t}\n\t\ttry {\n\n\t\t\t// The user may send an request from his own account to register with a transport or any other service,\n\t\t\t// then the connection ID matches the session id but this is still not a request to the local server.\n\t\t\t// The TO address must be checked too.....\n\t\t\tif ((packet.getPacketFrom() != null) && packet.getPacketFrom().equals(session.getConnectionId()) &&\n\t\t\t\t\t(!session.isAuthorized() ||\n\t\t\t\t\t\t\t(session.isUserId(id) || session.isLocalDomain(id.toString(), false)))) {\n\n\t\t\t\t// We want to allow password change but not user registration if registration is disabled. The only\n\t\t\t\t// way to tell apart registration from password change is to check whether the user is authenticated.\n\t\t\t\t// For authenticated user the request means password change or account removal, otherwise registration\n\t\t\t\t// attempt. Account removal is also called under authenticated session, so it should be blocked if\n\t\t\t\t// registration for domain is disabled. If user cannot register account he cannot also remove account.\n\t\t\t\tElement request = packet.getElement();\n\t\t\t\tboolean isRemoveAccount = request.findChildStaticStr(IQ_QUERY_REMOVE_PATH) != null;\n\n\t\t\t\tif (!session.isAuthorized() || isRemoveAccount) {\n\t\t\t\t\tif (!isRegistrationAllowedForConnection(packet.getFrom())) {\n\t\t\t\t\t\tresults.offer(NOT_ALLOWED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Registration is not allowed for this connection.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true));\n\t\t\t\t\t\t++statsInvalidRegistrations;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!session.getDomain().isRegisterEnabled()) {\n\t\t\t\t\t\tresults.offer(NOT_ALLOWED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Registration is not allowed for this domain.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true));\n\t\t\t\t\t\t++statsInvalidRegistrations;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!isTokenInBucket(packet.getFrom())) {\n\t\t\t\t\t\tresults.offer(NOT_ALLOWED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Server is busy. Too many registrations. Try later.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true));\n\t\t\t\t\t\t++statsInvalidRegistrations;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tStanzaType type = packet.getType();\n\t\t\t\tboolean isChangePassword = session.isAuthorized() && !isRemoveAccount;\n\t\t\t\tswitch (type) {\n\t\t\t\t\tcase set:\n\t\t\t\t\t\tif (isRemoveAccount) {\n\t\t\t\t\t\t\tdoRemoveAccount(packet, request, session, results);\n\t\t\t\t\t\t} else if (isChangePassword) {\n\t\t\t\t\t\t\tdoChangePassword(packet, request, session, results);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tdoRegisterNewAccount(packet, request, session, results);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase get: {\n\t\t\t\t\t\tdoGetRegistrationForm(packet, request, session, results);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase result:\n\t\t\t\t\t\t// It might be a registration request from transport for example...\n\t\t\t\t\t\tPacket pack_res = packet.copyElementOnly();\n\t\t\t\t\t\tpack_res.setPacketTo(session.getConnectionId());\n\t\t\t\t\t\tresults.offer(pack_res);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet, \"Message type is incorrect\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   true));\n\t\t\t\t\t\tbreak;\n\t\t\t\t} // end of switch (type)\n\t\t\t} else {\n\t\t\t\tif (session.isUserId(id)) {\n\t\t\t\t\t// It might be a registration request from transport for example...\n\t\t\t\t\tPacket pack_res = packet.copyElementOnly();\n\n\t\t\t\t\tpack_res.setPacketTo(session.getConnectionId());\n\t\t\t\t\tresults.offer(pack_res);\n\t\t\t\t} else {\n\t\t\t\t\tresults.offer(packet.copyElementOnly());\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (XMPPProcessorException e) {\n\t\t\t++statsInvalidRegistrations;\n\t\t\tPacket result = e.makeElement(packet, true);\n\t\t\tresults.offer(result);\n\t\t} catch (TigaseStringprepException ex) {\n\t\t\t++statsInvalidRegistrations;\n\t\t\tresults.offer(Authorization.JID_MALFORMED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Incorrect user name, stringprep processing failed.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true));\n\t\t} catch (NotAuthorizedException e) {\n\t\t\t++statsInvalidRegistrations;\n\t\t\tresults.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"You are not authorized to change registration settings.\\n\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  e.getMessage(), true));\n\t\t} catch (TigaseDBException e) {\n\t\t\tlog.log(Level.WARNING, \"Database problem: \" + e, e);\n\t\t\tresults.offer(Authorization.INTERNAL_SERVER_ERROR.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Database access problem, please contact administrator.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true));\n\t\t}\n\t}\n\n\tpublic void setOAuthCredentials(String oauthConsumerKey, String oauthConsumerSecret) {\n\t\tthis.oauthConsumerKey = oauthConsumerKey;\n\t\tthis.oauthConsumerSecret = oauthConsumerSecret;\n\t}\n\n\tpublic void setWelcomeMessage(String message) throws TigaseDBException {\n\t\tif (message != null) {\n\t\t\tuserRepository.setData(smJid, ID, \"welcome-message\", message);\n\t\t} else {\n\t\t\tuserRepository.removeData(smJid, ID, \"welcome-message\");\n\t\t}\n\t\teventBus.fire(new WelcomeMessageChangedEvent(message));\n\t}\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(XMPPResourceConnection session) {\n\t\tif (log.isLoggable(Level.FINEST) && (session != null)) {\n\t\t\tlog.finest(\"VHostItem: \" + session.getDomain());\n\t\t}\n\t\tif ((session != null) && ((!session.isTlsRequired()) || session.isEncrypted()) && session.getDomain().isRegisterEnabled()) {\n\t\t\treturn DISCO_FEATURES;\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\t@Override\n\tpublic Element[] supStreamFeatures(XMPPResourceConnection session) {\n\t\tif (log.isLoggable(Level.FINEST) && (session != null)) {\n\t\t\tlog.finest(\"VHostItem: \" + session.getDomain());\n\t\t}\n\t\tif ((session != null) && session.getDomain().isRegisterEnabled()) {\n\t\t\tif (session.isTlsRequired() && !session.isEncrypted()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn FEATURES;\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprotected void createAccount(XMPPResourceConnection session, String user_name, VHostItem domain, String password,\n\t\t\t\t\t\t\t\t String email, Map<String, String> reg_params)\n\t\t\tthrows XMPPProcessorException, TigaseStringprepException, TigaseDBException {\n\n\t\tfinal BareJID jid = BareJID.bareJIDInstance(user_name, domain.getVhost().getDomain());\n\n\t\ttry {\n\t\t\tcreateAccount(session.getAuthRepository(), jid, password, email, reg_params);\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Registration data set for: {0}, pass: {1}, reg_params: {2}\",\n\t\t\t\t\t\tnew Object[]{BareJID.toString(user_name, domain.getVhost().getDomain()), password, reg_params});\n\t\t\t}\n\t\t} catch (UserExistsException e) {\n\t\t\tthrow new XMPPProcessorException(Authorization.CONFLICT);\n\t\t}\n\t}\n\n\tpublic void createAccount(AuthRepository authRepository, BareJID jid, String password, String email, Map<String, String> reg_params)\n\t\t\tthrows TigaseDBException, XMPPProcessorException {\n\t\tif (reg_params == null) {\n\t\t\treg_params = Collections.emptyMap();\n\t\t\tif ((email != null) && !email.isBlank()) {\n\t\t\t\treg_params = new LinkedHashMap<>();\n\t\t\t\treg_params.put(\"email\", email.trim());\n\t\t\t}\n\t\t}\n\t\t\n\t\tif (validators != null) {\n\t\t\tfor (AccountValidator validator : validators) {\n\t\t\t\tvalidator.checkRequiredParameters(jid, reg_params);\n\t\t\t}\n\t\t}\n\n\t\tauthRepository.addUser(jid, password);\n\n\t\tboolean confirmationRequired = false;\n\t\tif (validators != null) {\n\t\t\tfor (AccountValidator validator : validators) {\n\t\t\t\tconfirmationRequired |= validator.sendAccountValidation(jid, reg_params);\n\t\t\t}\n\t\t\tif (confirmationRequired) {\n\t\t\t\tauthRepository.setAccountStatus(jid, AuthRepository.AccountStatus.pending);\n\t\t\t}\n\t\t}\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"User added: {0}, pass: {1}\",\n\t\t\t\t\tnew Object[]{jid, password});\n\t\t}\n\t\t++statsRegisteredUsers;\n\t\tauthRepository.updateCredential(jid,\n\t\t\t\t\t\t\t\t  Credentials.DEFAULT_CREDENTIAL_ID, password);\n\t\tif (reg_params != null) {\n\t\t\tfor (Map.Entry<String, String> entry : reg_params.entrySet()) {\n\t\t\t\tuserRepository.setData(jid,\n\t\t\t\t\t\t\t entry.getKey(), entry.getValue());\n\t\t\t}\n\t\t}\n\t\teventBus.fire(new UserRegisteredEvent(jid, email, confirmationRequired, reg_params));\n\t}\n\n\tprotected void doGetRegistrationForm(Packet packet, Element request, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t Queue<Packet> results) throws XMPPProcessorException, NoConnectionIdException {\n\t\tif (captchaRequired) {\n\t\t\tresults.offer(packet.okResult(prepareCaptchaRegistrationForm(session), 0));\n\t\t} else if (signedFormRequired) {\n\t\t\tresults.offer(packet.okResult(prepareSignedRegistrationForm(session), 0));\n\t\t} else if (emailRequired) {\n\t\t\tresults.offer(packet.okResult(prepareEmailRegistrationForm(), 0));\n\t\t} else {\n\t\t\tresults.offer(packet.okResult(\n\t\t\t\t\t\"<instructions>\" + \"Choose a user name and password for use with this service.\" +\n\t\t\t\t\t\t\t\"</instructions>\" + \"<username/>\" + \"<password/>\", 1));\n\t\t}\n\n\t}\n\n\tprotected void doRemoveAccount(final Packet packet, final Element request, final XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t   final Queue<Packet> results)\n\t\t\tthrows XMPPProcessorException, NoConnectionIdException, PacketErrorTypeException, NotAuthorizedException,\n\t\t\t\t   TigaseStringprepException, TigaseDBException {\n\t\t// Yes this is registration cancel request. According to XEP-0077 there\n\t\t// must not be any more subelements apart from <remove/>\n\t\tElement elem = request.findChildStaticStr(Iq.IQ_QUERY_PATH);\n\t\tif (elem.getChildren().size() > 1) {\n\t\t\tthrow new XMPPProcessorException(Authorization.BAD_REQUEST);\n\t\t}\n\n\t\tif (!session.isAuthorized()) {\n\t\t\tthrow new XMPPProcessorException(Authorization.FORBIDDEN);\n\t\t}\n\n\t\tfinal String user_name = packet.getStanzaFrom().getLocalpart();\n\n\t\tif (!session.getUserName().equals(user_name)) {\n\t\t\tthrow new XMPPProcessorException(Authorization.FORBIDDEN);\n\t\t}\n\n\t\tsession.getAuthRepository()\n\t\t\t\t.removeUser(BareJID.bareJIDInstance(user_name, session.getDomain().getVhost().getDomain()));\n\t\ttry {\n\t\t\tuserRepository.removeUser(BareJID.bareJIDInstance(user_name, session.getDomain().getVhost().getDomain()));\n\t\t} catch (UserNotFoundException ex) {\n\t\t\t// We ignore this error here. If auth_repo and user_repo are in fact\n\t\t\t// the same database, then user has been already removed with the auth_repo.removeUser(...)\n\t\t\t// then the second call to user_repo may throw the exception which is fine.\n\t\t}\n\n\t\tsession.logout();\n\n\t\tPacket ok_result = packet.okResult((String) null, 0);\n\n\t\t// We have to set SYSTEM priority for the packet here, otherwise the network connection is closed\n\t\t// before the client received a response\n\t\tok_result.setPriority(Priority.SYSTEM);\n\t\tresults.offer(ok_result);\n\n\t\tPacket close_cmd = Command.CLOSE.getPacket(session.getSMComponentId(), session.getConnectionId(),\n\t\t\t\t\t\t\t\t\t\t\t\t   StanzaType.set, session.nextStanzaId());\n\n\t\tclose_cmd.setPacketTo(session.getConnectionId());\n\t\tclose_cmd.setPriority(Priority.LOWEST);\n\t\tresults.offer(close_cmd);\n\t}\n\n\tprotected boolean isRegistrationAllowedForConnection(JID from) {\n\t\tString remoteAdress = parseRemoteAddressFromJid(from);\n\t\tif (whitelistRegistrationOnly) {\n\t\t\treturn contains(registrationWhitelist, remoteAdress);\n\t\t}\n\t\treturn !contains(registrationBlacklist, remoteAdress);\n\t}\n\n\tprotected boolean isTokenInBucket(final JID from) {\n\t\tString remoteAdress = parseRemoteAddressFromJid(from);\n\t\tif (remoteAdress == null || remoteAdress.isEmpty()) {\n\t\t\tremoteAdress = \"<default>\";\n\t\t}\n\t\treturn tokenBucket.consume(remoteAdress);\n\t}\n\n\tprivate tigase.server.Message createWelcomeMessage(String username, XMPPResourceConnection session)\n\t\t\tthrows TigaseStringprepException {\n\t\tif (welcomeMessage == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tJID jid = JID.jidInstance(username, session.getDomainAsJID().getDomain());\n\n\t\tElement messageEl = new Element(\"message\");\n\t\tmessageEl.setXMLNS(Message.CLIENT_XMLNS);\n\t\tmessageEl.addChild(new Element(\"body\", welcomeMessage));\n\n\t\treturn new Message(messageEl, session.getDomainAsJID(), jid);\n\t}\n\n\tprivate void doChangePassword(Packet packet, Element request, XMPPResourceConnection session, Queue<Packet> results)\n\t\t\tthrows XMPPProcessorException, NoConnectionIdException, TigaseStringprepException, NotAuthorizedException,\n\t\t\t\t   TigaseDBException {\n\t\tString user_name = request.getChildCDataStaticStr(IQ_QUERY_USERNAME_PATH);\n\t\tString password = request.getChildCDataStaticStr(IQ_QUERY_PASSWORD_PATH);\n\t\tif (null != password) {\n\t\t\tpassword = XMLUtils.unescape(password);\n\t\t}\n\n\t\tif ((user_name == null) || user_name.isBlank() || (password == null) || password.isBlank()) {\n\t\t\tthrow new XMPPProcessorException(Authorization.NOT_ACCEPTABLE);\n\t\t}\n\n\t\tsession.setRegistration(user_name, password, Collections.emptyMap());\n\t\tresults.offer(packet.okResult((String) null, 0));\n\t}\n\n\tprivate void doRegisterNewAccount(Packet packet, Element request, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t  Queue<Packet> results)\n\t\t\tthrows XMPPProcessorException, NoConnectionIdException, TigaseStringprepException, NotAuthorizedException,\n\t\t\t\t   TigaseDBException {\n\n\t\tfinal VHostItem domain = session.getDomain();\n\t\tif (!domain.isRegisterEnabled()) {\n\t\t\tthrow new NotAuthorizedException(\"Registration is now allowed for this domain\");\n\t\t}\n\n\t\tif (domain.getMaxUsersNumber() > 0) {\n\t\t\tlong domainUsers = session.getAuthRepository().getUsersCount(domain.getVhost().getDomain());\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\n\t\t\t\t\t\t\"Current number of users for domain: \" + domain.getVhost().getDomain() + \" is: \" + domainUsers);\n\t\t\t}\n\t\t\tif (domainUsers >= domain.getMaxUsersNumber()) {\n\t\t\t\tthrow new NotAuthorizedException(\"Maximum users number for the domain exceeded.\");\n\t\t\t}\n\t\t}\n\n\t\tElement queryEl = request.getChild(\"query\", \"jabber:iq:register\");\n\t\tElement formEl = queryEl == null ? null : queryEl.getChild(\"x\", \"jabber:x:data\");\n\t\tForm form = formEl == null ? null : new Form(formEl);\n\n\t\tif ((captchaRequired || signedFormRequired) && (form == null)) {\n\t\t\tthrow new XMPPProcessorException(Authorization.BAD_REQUEST, \"Use proper DataForm registration\");\n\t\t}\n\n\t\tif (captchaRequired) {\n\t\t\tvalidatCapchaForm(session, form);\n\t\t} else if (signedFormRequired) {\n\t\t\tvalidateSignedForm(packet, session, form);\n\t\t}\n\n\t\tString user_name =\n\t\t\t\tform != null ? form.getAsString(\"username\") : request.getChildCDataStaticStr(IQ_QUERY_USERNAME_PATH);\n\t\tString password =\n\t\t\t\tform != null ? form.getAsString(\"password\") : request.getChildCDataStaticStr(IQ_QUERY_PASSWORD_PATH);\n\t\tString email = form != null ? form.getAsString(\"email\") : request.getChildCDataStaticStr(IQ_QUERY_EMAIL_PATH);\n\n\t\tif (null != password) {\n\t\t\tpassword = XMLUtils.unescape(password);\n\t\t}\n\n\t\tif ((user_name == null) || user_name.equals(\"\") || (password == null) || password.equals(\"\")) {\n\t\t\tthrow new XMPPProcessorException(Authorization.NOT_ACCEPTABLE);\n\t\t}\n\n\t\tif (emailRequired && (email == null || email.isBlank())) {\n\t\t\tthrow new XMPPProcessorException(Authorization.NOT_ACCEPTABLE);\n\t\t}\n\n\t\tMap<String, String> reg_params = Collections.emptyMap();\n\t\tif ((email != null) && !email.isBlank()) {\n\t\t\treg_params = new LinkedHashMap<>();\n\t\t\treg_params.put(\"email\", email.trim());\n\t\t}\n\n\t\tcreateAccount(session, user_name, domain, password, email, reg_params);\n\n\t\tsession.removeSessionData(\"jabber:iq:register:captcha\");\n\t\tString localPart = BareJID.parseJID(user_name)[0];\n\t\tif (localPart == null || localPart.isEmpty()) {\n\t\t\tlocalPart = user_name;\n\t\t}\n\t\ttigase.server.Message msg = createWelcomeMessage(localPart, session);\n\t\tif (msg != null) {\n\t\t\tresults.offer(msg);\n\t\t}\n\n//\t\tif (emailRequired) {\n//\t\t\tElement query = new Element(\"query\");\n//\t\t\tquery.setXMLNS(\"jabber:iq:register\");\n//\t\t\tquery.addChild(new Element(\"instructions\",\n//\t\t\t\t\t\t\t\t\t   \"Please click on a link sent co provided e-mail address to activate your account\"));\n//\t\t\tquery.addChild(new Element(\"email-confirmation-required\"));\n//\t\t\tresults.offer(packet.okResult(query, 0));\n//\t\t} else {\n\t\t\tresults.offer(packet.okResult((String) null, 0));\n//\t\t}\n\t}\n\n\tprivate void validateSignedForm(Packet packet, XMPPResourceConnection session, Form form)\n\t\t\tthrows NoConnectionIdException, XMPPProcessorException {\n\t\tfinal String expectedToken = UUID.nameUUIDFromBytes(\n\t\t\t\t(session.getConnectionId() + \"|\" + session.getSessionId()).getBytes()).toString();\n\n\t\tFormSignatureVerifier verifier = new FormSignatureVerifier(oauthConsumerKey, oauthConsumerSecret);\n\t\tif (!expectedToken.equals(form.getAsString(\"oauth_token\"))) {\n\t\t\tlog.finest(\"Received oauth_token is different that sent one.\");\n\t\t\tthrow new XMPPProcessorException(Authorization.BAD_REQUEST, \"Unknown oauth_token\");\n\t\t}\n\t\tif (!oauthConsumerKey.equals(form.getAsString(\"oauth_consumer_key\"))) {\n\t\t\tlog.finest(\"Unknown oauth_consumer_key\");\n\t\t\tthrow new XMPPProcessorException(Authorization.BAD_REQUEST, \"Unknown oauth_consumer_key\");\n\t\t}\n\t\ttry {\n\t\t\tlong timestamp = verifier.verify(packet.getStanzaTo(), form);\n\t\t} catch (FormSignerException e) {\n\t\t\tlog.fine(\"Form Signature Validation Problem: \" + e.getMessage());\n\t\t\tthrow new XMPPProcessorException(Authorization.BAD_REQUEST, \"Invalid form signature\");\n\t\t}\n\t}\n\n\tprivate void validatCapchaForm(XMPPResourceConnection session, Form form) throws XMPPProcessorException {\n\t\tCaptchaItem captcha = (CaptchaItem) session.getSessionData(\"jabber:iq:register:captcha\");\n\t\tif (captcha == null) {\n\t\t\tcaptcha = captchaProvider.getCaptchaByID(form.getAsString(\"captcha-id\"));\n\t\t}\n\t\tif (captcha == null) {\n\t\t\tlog.finest(\"CAPTCHA is required\");\n\t\t\tthrow new XMPPProcessorException(Authorization.BAD_REQUEST,\n\t\t\t\t\t\t\t\t\t\t\t \"CAPTCHA is required. Please reload your registration form.\");\n\t\t}\n\n\t\t// using var='qa' instead of 'captcha' to comply with XEP-0158: CAPTCHA Forms, see https://xmpp.org/extensions/xep-0158.html#challenge-captcha\n\t\tString capResp = form.getAsString(\"qa\");\n\n\t\tif (!captcha.isResponseValid(session, capResp)) {\n\t\t\tcaptcha.incraseErrorCounter();\n\t\t\tlog.finest(\"Invalid captcha\");\n\n\t\t\tif (captcha.getErrorCounter() >= maxCaptchaRepetition) {\n\t\t\t\tlog.finest(\"Blocking session with not-solved captcha\");\n\t\t\t\tsession.removeSessionData(\"jabber:iq:register:captcha\");\n\t\t\t}\n\t\t\tthrow new XMPPProcessorException(NOT_ALLOWED, \"Invalid captcha\");\n\t\t}\n\t}\n\n\tprivate Element prepareCaptchaRegistrationForm(final XMPPResourceConnection session)\n\t\t\tthrows NoConnectionIdException {\n\t\tElement query = new Element(\"query\", new String[]{\"xmlns\"}, XMLNSS);\n\t\tquery.addChild(new Element(\"instructions\", (emailRequired ? INSTRUCTION_EMAIL_REQUIRED_DEF : INSTRUCTION_DEF)));\n\t\tForm form = prepareGenericRegistrationForm();\n\n\t\tCaptchaItem captcha = captchaProvider.generateCaptcha(session);\n\t\tsession.putSessionData(\"jabber:iq:register:captcha\", captcha);\n\t\tField field = Field.fieldHidden(\"captcha-id\", captcha.getID());\n\t\tfield.setRequired(true);\n\t\tform.addField(field);\n\t\t// using var='qa' instead of 'captcha' to comply with XEP-0158: CAPTCHA Forms, see https://xmpp.org/extensions/xep-0158.html#challenge-captcha\n\t\tfield = Field.fieldTextSingle(\"qa\", \"\", captcha.getCaptchaRequest(session));\n\t\tfield.setRequired(true);\n\t\tform.addField(field);\n\n\t\tquery.addChild(form.getElement());\n\t\treturn query;\n\t}\n\n\tprivate Element prepareSignedRegistrationForm(final XMPPResourceConnection session) throws NoConnectionIdException {\n\t\tElement query = new Element(\"query\", new String[]{\"xmlns\"}, XMLNSS);\n\t\tquery.addChild(new Element(\"instructions\", (emailRequired ? INSTRUCTION_EMAIL_REQUIRED_DEF : INSTRUCTION_DEF)));\n\t\tForm form = prepareGenericRegistrationForm();\n\n\t\tSignatureCalculator sc = new SignatureCalculator(oauthConsumerKey, oauthConsumerSecret);\n\t\tsc.setOauthToken(UUID.nameUUIDFromBytes((session.getConnectionId() + \"|\" + session.getSessionId()).getBytes())\n\t\t\t\t\t\t\t\t .toString());\n\t\tsc.addEmptyFields(form);\n\n\t\tquery.addChild(form.getElement());\n\t\treturn query;\n\t}\n\n\tprivate Element prepareEmailRegistrationForm() throws NoConnectionIdException {\n\t\tElement query = new Element(\"query\", new String[]{\"xmlns\"}, XMLNSS);\n\t\tquery.addChild(new Element(\"instructions\", (emailRequired ? INSTRUCTION_EMAIL_REQUIRED_DEF : INSTRUCTION_DEF)));\n\t\tForm form = prepareGenericRegistrationForm();\n\t\tquery.addChild(form.getElement());\n\t\treturn query;\n\t}\n\n\n\tprivate Form prepareGenericRegistrationForm() {\n\t\tForm form = new Form(\"form\", \"Account Registration\", (emailRequired ? INSTRUCTION_EMAIL_REQUIRED_DEF\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: INSTRUCTION_DEF));\n\n\t\tform.addField(Field.fieldHidden(\"FORM_TYPE\", \"jabber:iq:register\"));\n\n\t\tField field = Field.fieldTextSingle(\"username\", \"\", \"Username\");\n\t\tfield.setRequired(true);\n\t\tform.addField(field);\n\t\tfield = Field.fieldTextPrivate(\"password\", \"\", \"Password\");\n\t\tfield.setRequired(true);\n\t\tform.addField(field);\n\t\tfield = Field.fieldTextSingle(\"email\", \"\", \"Email\");\n\t\tif (emailRequired) {\n\t\t\tfield.setRequired(true);\n\t\t\tfield.setLabel(\"Email (MUST BE VALID!)\");\n\t\t}\n\t\tform.addField(field);\n\t\treturn form;\n\t}\n\n\tpublic interface AccountValidator {\n\n\t\tvoid checkRequiredParameters(BareJID jid, Map<String, String> reg_params) throws XMPPProcessorException;\n\n\t\tboolean sendAccountValidation(BareJID jid, Map<String, String> reg_params);\n\n\t\tBareJID validateAccount(String token);\n\n\t}\n\n\t/**\n\t * As in http://commons.apache.org/proper/commons-net/jacoco/org.apache.commons .net.util/SubnetUtils.java.html\n\t */\n\tpublic static class CIDRAddress {\n\n\t\tstatic final int NBITS = 32;\n\n\t\tstatic final String IP_ADDRESS_MASK = \"(\\\\d{1,3})\\\\.(\\\\d{1,3})\\\\.(\\\\d{1,3})\\\\.(\\\\d{1,3})\";\n\n\t\tstatic final String CIDR_ADDRESS_MASK = IP_ADDRESS_MASK + \"/(\\\\d{1,3})\";\n\n\t\tstatic final Pattern IP_PATTERN = Pattern.compile(IP_ADDRESS_MASK);\n\n\t\tstatic final Pattern CIDR_PATTERN = Pattern.compile(CIDR_ADDRESS_MASK);\n\n\t\tfinal int high;\n\n\t\tfinal int low;\n\n\t\tstatic int matchAddress(Matcher matcher) {\n\t\t\tint addr = 0;\n\t\t\tfor (int i = 1; i <= 4; ++i) {\n\t\t\t\tint n = Integer.parseInt(matcher.group(i));\n\t\t\t\taddr |= ((n & 0xff) << 8 * (4 - i));\n\t\t\t}\n\t\t\treturn addr;\n\t\t}\n\n\t\tstatic CIDRAddress parse(String mask) {\n\t\t\tif (!mask.contains(\"/\")) {\n\t\t\t\tmask = mask + \"/\" + NBITS;\n\t\t\t}\n\t\t\tMatcher matcher = CIDR_PATTERN.matcher(mask);\n\t\t\tmatcher.matches();\n\t\t\tint address = matchAddress(matcher);\n\n\t\t\t/*\n\t\t\t * Create a binary netmask from the number of bits specification /x\n\t\t\t */\n\t\t\tint cidrPart = Integer.parseInt(matcher.group(5));\n\t\t\tint netmask = 0;\n\t\t\tint broadcast = 0;\n\t\t\tint network = 0;\n\t\t\tfor (int j = 0; j < cidrPart; ++j) {\n\t\t\t\tnetmask |= (1 << 31 - j);\n\t\t\t}\n\n\t\t\t/* Calculate base network address */\n\t\t\tnetwork = (address & netmask);\n\n\t\t\t/* Calculate broadcast address */\n\t\t\tbroadcast = network | ~(netmask);\n\t\t\treturn new CIDRAddress(broadcast, network);\n\t\t}\n\n\t\tstatic int toInteger(String address) {\n\t\t\tMatcher matcher = IP_PATTERN.matcher(address);\n\t\t\tmatcher.matches();\n\t\t\treturn matchAddress(matcher);\n\t\t}\n\n\t\tprivate CIDRAddress(int high, int low) {\n\t\t\tthis.high = high;\n\t\t\tthis.low = low;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tint mask = 0;\n\t\t\tboolean z = false;\n\t\t\tfor (int j = 3; j >= 0; j--) {\n\t\t\t\tint shift = j * 8;\n\t\t\t\tbyte b = (byte) ((~(low >> shift & 0xff) ^ (high >> shift & 0xff)) & 0xff);\n\t\t\t\tint m = 0x80;\n\t\t\t\tfor (int i = 0; i < 8; i++) {\n\t\t\t\t\tif ((b & m) == 0) {\n\t\t\t\t\t\tz = true;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmask++;\n\t\t\t\t\t}\n\t\t\t\t\tm >>>= 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn String.format(\"%d.%d.%d.%d/%d\", (low >> 24 & 0xff), (low >> 16 & 0xff), (low >> 8 & 0xff),\n\t\t\t\t\t\t\t\t (low & 0xff), mask);\n\t\t}\n\n\t\tboolean inRange(String address) {\n\t\t\tint diff = toInteger(address) - low;\n\t\t\treturn diff >= 0 && (diff <= (high - low));\n\t\t}\n\t}\n\n\tpublic static class UserRegisteredEvent implements EventBusEvent {\n\n\t\tprivate boolean confirmationRequired;\n\t\tprivate String email;\n\t\tprivate Map<String, String> params;\n\t\tprivate BareJID user;\n\n\t\tpublic UserRegisteredEvent(BareJID user, String email, boolean confirmationRequired,\n\t\t\t\t\t\t\t\t   Map<String, String> params) {\n\t\t\tthis.user = user;\n\t\t\tthis.email = email;\n\t\t\tthis.params = params;\n\t\t\tthis.confirmationRequired = confirmationRequired;\n\t\t}\n\n\t\tpublic String getEmail() {\n\t\t\treturn email;\n\t\t}\n\n\t\tpublic void setEmail(String email) {\n\t\t\tthis.email = email;\n\t\t}\n\n\t\tpublic Map<String, String> getParams() {\n\t\t\treturn params;\n\t\t}\n\n\t\tpublic void setParams(Map<String, String> params) {\n\t\t\tthis.params = params;\n\t\t}\n\n\t\tpublic BareJID getUser() {\n\t\t\treturn user;\n\t\t}\n\n\t\tpublic void setUser(BareJID user) {\n\t\t\tthis.user = user;\n\t\t}\n\n\t\tpublic boolean isConfirmationRequired() {\n\t\t\treturn confirmationRequired;\n\t\t}\n\n\t\tpublic void setConfirmationRequired(boolean confirmationRequired) {\n\t\t\tthis.confirmationRequired = confirmationRequired;\n\t\t}\n\t}\n\n\tpublic static class WelcomeMessageChangedEvent\n\t\t\timplements Serializable, EventBusEvent {\n\n\t\tprivate String message;\n\n\t\tpublic WelcomeMessageChangedEvent() {\n\t\t}\n\n\t\tpublic WelcomeMessageChangedEvent(String message) {\n\t\t\tthis.message = message;\n\t\t}\n\n\t\tpublic String getMessage() {\n\t\t\treturn message;\n\t\t}\n\n\t}\n}"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/JabberIqRoster.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.PolicyViolationException;\nimport tigase.server.Priority;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.roster.*;\nimport tigase.xmpp.impl.roster.RosterAbstract.SubscriptionType;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.xmpp.impl.PresenceSubscription.AUTO_AUTHORIZE_PROP_KEY;\n\n/**\n * Class <code>JabberIqRoster</code> implements part of <em>RFC-3921</em> - <em>XMPP Instant Messaging</em>\n * specification describing roster management. 7. Roster Management\n * <br>\n * Created: Tue Feb 21 17:42:53 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = JabberIqRoster.ID, parent = SessionManager.class, active = true)\npublic class JabberIqRoster\n\t\textends XMPPProcessor\n\t\timplements XMPPProcessorIfc {\n\n\tpublic static final String ANON = \"anon\";\n\tprotected static final String ID = RosterAbstract.XMLNS;\n\tprivate static final String[][] ELEMENTS = {{Iq.ELEM_NAME, Iq.QUERY_NAME}, {Iq.ELEM_NAME, Iq.QUERY_NAME},\n\t\t\t\t\t\t\t\t\t\t\t\t{Iq.ELEM_NAME, Iq.QUERY_NAME}};\n\tprivate static final Logger log = Logger.getLogger(JabberIqRoster.class.getName());\n\tprivate static final String[] XMLNSS = {RosterAbstract.XMLNS, RosterAbstract.XMLNS_DYNAMIC,\n\t\t\t\t\t\t\t\t\t\t\tRosterAbstract.XMLNS_LOAD};\n\tprivate static final String[] IQ_QUERY_ITEM_PATH = {Iq.ELEM_NAME, Iq.QUERY_NAME, \"item\"};\n\t/** instance of class implementing {@link RosterAbstract} */\n\tprotected RosterAbstract roster_util = getRosterUtil();\n\t/**\n\t * variable holding setting regarding auto authorisation of items added to user roset\n\t */\n\t@ConfigField(desc = \"Automatically authorize subscription requests\", alias = AUTO_AUTHORIZE_PROP_KEY)\n\tprivate boolean autoAuthorize = false;\n\t// This is required to make sure that dynamic roster will get initialized\n\t@Inject(nullAllowed = true)\n\tprivate DynamicRoster dynamicRoster;\n\t@ConfigField(desc = \"Allow empty names in roster\", alias = \"empty_name_enabled\")\n\tprivate boolean emptyNameAllowed = false;\n\t@ConfigField(desc = \"Max roster size\", alias = \"max_roster_size\")\n\tprivate int maxRosterSize = 0;\n\n\t/**\n\t * Method processes roster <code>get</code> request related to dynamic roster. Generates output packet with data\n\t * from the DynamicRoster implementation for every <em>item</em>element from processed packet or error otherwise.\n\t *\n\t * @param packet packet is which being processed.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param settings this map keeps plugin specific settings loaded from the Tigase server configuration.\n\t *\n\t */\n\tprotected static void dynamicGetRequest(Packet packet, XMPPResourceConnection session, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t\tMap<String, Object> settings) throws NotAuthorizedException {\n\t\tElement request = packet.getElement();\n\t\tElement item = request.findChildStaticStr(IQ_QUERY_ITEM_PATH);\n\n\t\tif (item != null) {\n\t\t\tElement new_item = DynamicRoster.getItemExtraData(session, settings, item);\n\n\t\t\tif (new_item == null) {\n\t\t\t\tnew_item = item;\n\t\t\t}\n\t\t\tresults.offer(packet.okResult(new_item, 1));\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"Missing 'item' element, request can not be processed.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   true));\n\t\t\t} catch (PacketErrorTypeException ex) {\n\t\t\t\tlog.log(Level.SEVERE, \"Received error packet? not possible.\", ex);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Method processes roster <code>set</code> request related to dynamic roster. Sets extra data for every\n\t * <em>item</em>element from processed packet with the DynamicRoster implementation and generates success packet or,\n\t * in case of failure generates error.\n\t *\n\t * @param packet packet is which being processed.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param settings this map keeps plugin specific settings loaded from the Tigase server configuration.\n\t */\n\tprotected static void dynamicSetRequest(Packet packet, XMPPResourceConnection session, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t\tMap<String, Object> settings) {\n\t\tElement request = packet.getElement();\n\t\tList<Element> items = request.getChildrenStaticStr(Iq.IQ_QUERY_PATH);\n\n\t\tif ((items != null) && (items.size() > 0)) {\n\t\t\tfor (Element item : items) {\n\t\t\t\tDynamicRoster.setItemExtraData(session, settings, item);\n\t\t\t}\n\t\t\tresults.offer(packet.okResult((String) null, 0));\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"Missing 'item' element, request can not be processed.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   true));\n\t\t\t} catch (PacketErrorTypeException ex) {\n\t\t\t\tlog.log(Level.SEVERE, \"Received error packet? not possible.\", ex);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Returns an array of group names retrieved from Element item\n\t *\n\t * @param item from which groups should be obtained\n\t *\n\t * @return an array of group names retrieved from Element item\n\t */\n\tpublic static String[] getItemGroups(Element item) {\n\t\tList<Element> elgr = item.getChildren();\n\n\t\tif ((elgr != null) && (elgr.size() > 0)) {\n\t\t\tArrayList<String> groups = new ArrayList<String>(1);\n\n\t\t\tfor (Element grp : elgr) {\n\t\t\t\tif (grp.getName() == RosterAbstract.GROUP) {\n\t\t\t\t\tgroups.add(grp.getCData());\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (groups.size() > 0) {\n\t\t\t\treturn groups.toArray(new String[groups.size()]);\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic int concurrentQueuesNo() {\n\t\treturn super.concurrentQueuesNo() * 4;\n\t}\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\tpublic boolean isEmptyNameAllowed() {\n\t\treturn roster_util.isEmptyNameAllowed();\n\t}\n\n\tpublic void setEmptyNameAllowed(boolean emptyNameAllowed) {\n\t\troster_util.setEmptyNameAllowed(emptyNameAllowed);\n\t}\n\n\tpublic int getMaxRosterSize() {\n\t\treturn roster_util.getMaxRosterSize();\n\t}\n\n\tpublic void setMaxRosterSize(int maxRosterSize) {\n\t\troster_util.setMaxRosterSize(maxRosterSize);\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t * <br>\n\t * Performs processing of <em>IQ</em> packets with <em>jabber:iq:roster</em> xmlns with the regard whether it's\n\t * roster <em>set</em> or <em>get</em> request or possibly dynamic-roster is involved. request.\n\t */\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\t\tif (session == null) {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"Session is null, ignoring packet: {0}\", packet);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}    // end of if (session == null)\n\t\tif (!session.isAuthorized()) {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"Session is not authorized, ignoring packet: {0}\", packet);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\t// The roster request can be between the user and the server or between the\n\t\t// user and some other entity like transport\n\t\tJID connectionId = session.getConnectionId();\n\n\t\tif (connectionId.equals(packet.getPacketFrom())) {\n\n\t\t\t// Packet from the user, let's check where it should go\n\t\t\tif ((packet.getStanzaTo() != null) && !session.isLocalDomain(packet.getStanzaTo().toString(), false) &&\n\t\t\t\t\t!session.isUserId(packet.getStanzaTo().getBareJID())) {\n\n\t\t\t\t// This is most likely a roster reaquest sent to a transport or some other\n\t\t\t\t// roster resource.\n\t\t\t\tresults.offer(packet.copyElementOnly());\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else {\n\n\t\t\t// Packet probably to the user, let's check where it came from\n\t\t\tif (session.isUserId(packet.getStanzaTo().getBareJID())) {\n\t\t\t\tif (packet.getStanzaTo().getResource() != null) {\n\t\t\t\t\tPacket result = packet.copyElementOnly();\n\n\t\t\t\t\tresult.setPacketTo(session.getConnectionId(packet.getStanzaTo()));\n\t\t\t\t\tresult.setPacketFrom(packet.getTo());\n\t\t\t\t\tresults.offer(result);\n\t\t\t\t} else {\n\t\t\t\t\tprocessRemoteRosterManagementRequest(packet, session, results, settings);\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\t// Hm, I do not know what to do here, should not happen\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\tif ((packet.getStanzaFrom() != null) && !session.isUserId(packet.getStanzaFrom().getBareJID())) {\n\n\t\t\t\t// RFC says: ignore such request\n\t\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\t\"Roster request ''from'' attribute doesn''t match \" + \"session: {0}, request: {1}\",\n\t\t\t\t\t\tnew Object[]{session, packet});\n\t\t\t\treturn;\n\t\t\t}    // end of if (packet.getElemFrom() != null\n\t\t\tStanzaType type = packet.getType();\n\t\t\tString xmlns = packet.getElement().getXMLNSStaticStr(Iq.IQ_QUERY_PATH);\n\n\t\t\tif (xmlns == RosterAbstract.XMLNS) {\n\t\t\t\tswitch (type) {\n\t\t\t\t\tcase get:\n\t\t\t\t\t\tprocessGetRequest(packet, session, results, settings);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase set:\n\t\t\t\t\t\tprocessSetRequest(packet, session, results, settings);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase result:\n\t\t\t\t\t\t// Ignore\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet, \"Request type is incorrect\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   false));\n\t\t\t\t\t\tbreak;\n\t\t\t\t}    // end of switch (type)\n\t\t\t} else {\n\t\t\t\tif (xmlns == RosterAbstract.XMLNS_DYNAMIC) {\n\t\t\t\t\tswitch (type) {\n\t\t\t\t\t\tcase get:\n\t\t\t\t\t\t\tdynamicGetRequest(packet, session, results, settings);\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase set:\n\t\t\t\t\t\t\tdynamicSetRequest(packet, session, results, settings);\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase result:\n\t\t\t\t\t\t\t// Ignore\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tresults.offer(\n\t\t\t\t\t\t\t\t\tAuthorization.BAD_REQUEST.getResponseMessage(packet, \"Request type is incorrect\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t false));\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}    // end of switch (type)\n\t\t\t\t} else if (xmlns == RosterAbstract.XMLNS_LOAD) {\n\t\t\t\t\tswitch (type) {\n\t\t\t\t\t\tcase set:\n\t\t\t\t\t\t\tif (!roster_util.isRosterLoaded(session)) {\n\t\t\t\t\t\t\t\t// here we do not care about result as we only trigger\n\t\t\t\t\t\t\t\t// loading of roster from database\n\t\t\t\t\t\t\t\troster_util.getRosterElement(session, session.getJID());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tElement resultEl = packet.getElement().clone();\n\t\t\t\t\t\t\tresultEl.setAttribute(\"type\", StanzaType.result.name());\n\t\t\t\t\t\t\tPacket result = Packet.packetInstance(resultEl, packet.getStanzaFrom(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  packet.getStanzaTo());\n\t\t\t\t\t\t\tresult.setPacketFrom(packet.getPacketFrom());\n\t\t\t\t\t\t\tresult.setPacketTo(packet.getPacketTo());\n\t\t\t\t\t\t\tresults.add(result);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Hm, don't know what to do, unexpected name space, let's record it\n\t\t\t\t\tlog.log(Level.WARNING, \"Unknown XMLNS for the roster plugin: {0}\", packet);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (RosterRetrievingException e) {\n\t\t\tlog.log(Level.WARNING, \"Unknown roster retrieving exception: {0} for packet: {1}\", new Object[]{e, packet});\n\t\t\tresults.offer(Authorization.UNDEFINED_CONDITION.getResponseMessage(packet, e.getMessage(), true));\n\t\t} catch (RepositoryAccessException e) {\n\t\t\tlog.log(Level.WARNING, \"Problem with roster repository access: {0} for packet: {1}\",\n\t\t\t\t\tnew Object[]{e, packet});\n\t\t\tresults.offer(packet.okResult((String) null, 0));\n\t\t} catch (NotAuthorizedException e) {\n\t\t\tlog.log(Level.FINE, \"Received roster request but user session is not authorized yet: {0}\", packet);\n\t\t\tresults.offer(\n\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(packet, \"You must authorize session first.\", true));\n\t\t} catch (PolicyViolationException e) {\n\t\t\tlog.log(Level.FINE, \"Roster set request violated items number policy: {0}\", packet);\n\t\t\tresults.offer(Authorization.POLICY_VIOLATION.getResponseMessage(packet, e.getLocalizedMessage(), true));\n\t\t} catch (TigaseDBException e) {\n\t\t\tlog.log(Level.WARNING, \"Database problem, please contact admin:\", e);\n\t\t\tresults.offer(Authorization.INTERNAL_SERVER_ERROR.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Database access problem, please contact administrator.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true));\n\t\t}    // end of try-catch\n\t}\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(final XMPPResourceConnection session) {\n\t\treturn RosterAbstract.DISCO_FEATURES;\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\t@Override\n\tpublic Element[] supStreamFeatures(final XMPPResourceConnection session) {\n\t\tif (session != null && session.isAuthorized()) {\n\t\t\treturn RosterAbstract.FEATURES;\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Method processes roster <code>get</code> request. Generates output result packet containing user roster items\n\t * also considering roster version attribute to minimise traffic.\n\t *\n\t * @param packet packet is which being processed.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param settings this map keeps plugin specific settings loaded from the Tigase server configuration.\n\t *\n\t */\n\tprotected void processGetRequest(Packet packet, XMPPResourceConnection session, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t Map<String, Object> settings)\n\t\t\tthrows NotAuthorizedException, TigaseDBException, RosterRetrievingException, RepositoryAccessException {\n\n\t\t// Retrieve all Dynamic roster elements from the roster repository\n\t\tList<Element> its = DynamicRoster.getRosterItems(session, settings);\n\n\t\tElement queryEl = packet.getElement().getChildStaticStr(Iq.QUERY_NAME, RosterAbstract.XMLNS);\n\t\tboolean annotateMix = queryEl.getChildStaticStr(\"annotate\", \"urn:xmpp:mix:roster:0\") != null;\n\t\tsession.putSessionData(\"urn:xmpp:mix:roster:0\", annotateMix);\n\n\t\t// If the dynamic roster exists, we have to always recalculate hash, as the\n\t\t// part of the roster could have changed outside of the Tigase server.\n\t\t// the same goes for annotate mix\n\t\tif (annotateMix || ((its != null) && (its.size() > 0))) {\n\t\t\troster_util.updateRosterHash(session);\n\t\t}\n\n\t\t// Check roster version hash.\n\t\tString incomingHash = queryEl.getAttributeStaticStr(RosterAbstract.VER_ATT);\n\t\tString storedHash = \"\";\n\n\t\t// If client provided hash and the server calculated hash are the same\n\t\t// return the success result and abort further roster processing.\n\t\t// No need to send the whole roster to the client.\n\t\tif (incomingHash != null) {\n\t\t\tstoredHash = roster_util.getBuddiesHash(session);\n\t\t\tif ((storedHash == null) || storedHash.isEmpty()) {\n\t\t\t\troster_util.updateRosterHash(session);\n\t\t\t\tstoredHash = roster_util.getBuddiesHash(session);\n\t\t\t}\n\t\t\tif (incomingHash.equals(storedHash)) {\n\t\t\t\tresults.offer(packet.okResult((String) null, 0));\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t\n\t\t// Retrieve standard roster items.\n\t\tList<Element> ritems = roster_util.getRosterItems(session);\n\n\t\t// Send the user's standard roster first\n\t\tif ((ritems != null) && (ritems.size() > 0)) {\n\t\t\tElement query = new Element(\"query\");\n\n\t\t\tquery.setXMLNS(RosterAbstract.XMLNS);\n\t\t\tif (incomingHash != null) {\n\t\t\t\tquery.setAttribute(RosterAbstract.VER_ATT, storedHash);\n\t\t\t}\n\t\t\tquery.addChildren(ritems);\n\t\t\tresults.offer(packet.okResult(query, 0));\n\t\t} else {\n\t\t\tresults.offer(packet.okResult((String) null, 1));\n\t\t}\n\n\t\t// Push the dynamic roster items now\n\t\ttry {\n\t\t\tif ((its != null) && (its.size() > 0)) {\n\t\t\t\tArrayDeque<Element> items = new ArrayDeque<Element>(its);\n\n\t\t\t\twhile (items.size() > 0) {\n\t\t\t\t\tElement iq = new Element(\"iq\", new String[]{\"type\", \"id\", \"to\"},\n\t\t\t\t\t\t\t\t\t\t\t new String[]{\"set\", session.nextStanzaId(), session.getJID().toString()});\n\n\t\t\t\t\tiq.setXMLNS(CLIENT_XMLNS);\n\n\t\t\t\t\tElement query = new Element(\"query\");\n\n\t\t\t\t\tquery.setXMLNS(RosterAbstract.XMLNS);\n\t\t\t\t\tiq.addChild(query);\n\t\t\t\t\tquery.addChild(items.poll());\n\t\t\t\t\twhile ((query.getChildren().size() < 20) && (items.size() > 0)) {\n\t\t\t\t\t\tquery.addChild(items.poll());\n\t\t\t\t\t}\n\n\t\t\t\t\tPacket rost_res = Packet.packetInstance(iq, null, session.getJID());\n\n\t\t\t\t\trost_res.setPacketTo(session.getConnectionId());\n\t\t\t\t\trost_res.setPacketFrom(packet.getTo());\n\t\t\t\t\tresults.offer(rost_res);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (NoConnectionIdException ex) {\n\t\t\tlog.log(Level.WARNING, \"Problem with roster request, no connection ID for session: {0}, request: {1}\",\n\t\t\t\t\tnew Object[]{session, packet});\n\t\t}\n\t}\n\n\t/**\n\t * Method processes roster <code>set</code> request. Performs modifications of user roster.\n\t *\n\t * @param packet packet is which being processed.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param settings this map keeps plugin specific settings loaded from the Tigase server configuration.\n\t *\n\t */\n\tprotected void processSetRequest(Packet packet, XMPPResourceConnection session, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t final Map<String, Object> settings)\n\t\t\tthrows XMPPException, NotAuthorizedException, TigaseDBException, PolicyViolationException {\n\n\t\t// Element request = packet.getElement();\n\t\tList<Element> items = packet.getElemChildrenStaticStr(Iq.IQ_QUERY_PATH);\n\n\t\tif (items != null) {\n\t\t\ttry {\n\n\t\t\t\t// RFC-3921 draft bis-03 forbids multiple items in one request\n\t\t\t\t// This however seems to make no much sense and actually was\n\t\t\t\t// requested by many users to allow for multiple items\n\t\t\t\tfor (Element item : items) {\n\t\t\t\t\tJID buddy = JID.jidInstance(item.getAttributeStaticStr(\"jid\"));\n\n\t\t\t\t\tif (DynamicRoster.getBuddyItem(session, settings, buddy) != null) {\n\n\t\t\t\t\t\t// Let's return an error. Dynamic roster cannot be modified via\n\t\t\t\t\t\t// XMPP.\n\t\t\t\t\t\tresults.offer(Authorization.FEATURE_NOT_IMPLEMENTED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"You cannot modify this contact. It is controlled by an \" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"external service.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   true));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (session.isUserId(buddy.getBareJID())) {\n\t\t\t\t\t\tresults.offer(Authorization.NOT_ALLOWED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"User can't add himself to the roster, RFC says NO.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   true));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tString subscription = item.getAttributeStaticStr(\"subscription\");\n\n\t\t\t\t\tif ((subscription != null) && subscription.equals(\"remove\")) {\n\t\t\t\t\t\tSubscriptionType sub = roster_util.getBuddySubscription(session, buddy);\n\n\t\t\t\t\t\tif (sub == null) {\n\t\t\t\t\t\t\tsub = SubscriptionType.none;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tString type = item.getAttributeStaticStr(Packet.TYPE_ATT);\n\n\t\t\t\t\t\tif ((sub != SubscriptionType.none) && ((type == null) || !type.equals(ANON))) {\n\n\t\t\t\t\t\t\t// Unavailable presence should be sent first, otherwise it will be\n\t\t\t\t\t\t\t// blocked by the server after the subscription is canceled\n\t\t\t\t\t\t\tElement pres = new Element(PresenceAbstract.PRESENCE_ELEMENT_NAME);\n\n\t\t\t\t\t\t\tpres.setXMLNS(CLIENT_XMLNS);\n\t\t\t\t\t\t\tpres.setAttribute(Packet.TO_ATT, buddy.toString());\n\t\t\t\t\t\t\tpres.setAttribute(Packet.FROM_ATT, session.getJID().toString());\n\t\t\t\t\t\t\tpres.setAttribute(Packet.TYPE_ATT, \"unavailable\");\n\n\t\t\t\t\t\t\tPacket pres_packet = Packet.packetInstance(pres, session.getJID(), buddy);\n\n\t\t\t\t\t\t\t// We have to set a higher priority for this particular\n\t\t\t\t\t\t\t// unavailable packet\n\t\t\t\t\t\t\t// to make sure it is delivered before subscription cancellation\n\t\t\t\t\t\t\tpres_packet.setPriority(Priority.HIGH);\n\t\t\t\t\t\t\tresults.offer(pres_packet);\n\t\t\t\t\t\t\tpres = new Element(PresenceAbstract.PRESENCE_ELEMENT_NAME);\n\t\t\t\t\t\t\tpres.setXMLNS(CLIENT_XMLNS);\n\t\t\t\t\t\t\tpres.setAttribute(Packet.TO_ATT, buddy.toString());\n\t\t\t\t\t\t\tpres.setAttribute(Packet.FROM_ATT, session.getBareJID().toString());\n\t\t\t\t\t\t\tpres.setAttribute(Packet.TYPE_ATT, \"unsubscribe\");\n\t\t\t\t\t\t\tresults.offer(Packet.packetInstance(pres, session.getJID().copyWithoutResource(), buddy));\n\t\t\t\t\t\t\tpres = new Element(PresenceAbstract.PRESENCE_ELEMENT_NAME);\n\t\t\t\t\t\t\tpres.setXMLNS(CLIENT_XMLNS);\n\t\t\t\t\t\t\tpres.setAttribute(Packet.TO_ATT, buddy.toString());\n\t\t\t\t\t\t\tpres.setAttribute(Packet.FROM_ATT, session.getBareJID().toString());\n\t\t\t\t\t\t\tpres.setAttribute(Packet.TYPE_ATT, \"unsubscribed\");\n\t\t\t\t\t\t\tresults.offer(Packet.packetInstance(pres, session.getJID().copyWithoutResource(), buddy));\n\t\t\t\t\t\t}    // is in the roster while he isn't. In such a case just ensure the\n\n\t\t\t\t\t\t// client that the buddy has been removed for sure\n\t\t\t\t\t\tElement it = new Element(\"item\");\n\n\t\t\t\t\t\tit.setAttribute(\"jid\", buddy.toString());\n\t\t\t\t\t\tit.setAttribute(\"subscription\", \"remove\");\n\t\t\t\t\t\troster_util.removeBuddy(session, buddy);\n\t\t\t\t\t\troster_util.updateBuddyChange(session, results, it);\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// We are adding a new roster element here\n\t\t\t\t\t\tString name = item.getAttributeStaticStr(\"name\");\n\t\t\t\t\t\tList<Element> groups = item.getChildren();\n\t\t\t\t\t\tString[] gr = null;\n\n\t\t\t\t\t\tif ((groups != null) && (groups.size() > 0)) {\n\t\t\t\t\t\t\tgr = new String[groups.size()];\n\n\t\t\t\t\t\t\tint cnt = 0;\n\n\t\t\t\t\t\t\tfor (Element group : groups) {\n\t\t\t\t\t\t\t\tgr[cnt++] = ((group.getCData() == null) ? \"\" : group.getCData());\n\t\t\t\t\t\t\t}    // end of for (ElementData group : groups)\n\n\t\t\t\t\t\t\t// end of for (ElementData group : groups)\n\t\t\t\t\t\t}\n\t\t\t\t\t\troster_util.addBuddy(session, buddy, name, gr, null, null);\n\n\t\t\t\t\t\tString type = item.getAttributeStaticStr(Packet.TYPE_ATT);\n\n\t\t\t\t\t\tif ((type != null) && type.equals(ANON) || autoAuthorize) {\n\t\t\t\t\t\t\troster_util.setBuddySubscription(session, SubscriptionType.both, buddy);\n\n\t\t\t\t\t\t\tElement pres = (Element) session.getSessionData(XMPPResourceConnection.PRESENCE_KEY);\n\n\t\t\t\t\t\t\tif (pres == null) {\n\t\t\t\t\t\t\t\tpres = new Element(PresenceAbstract.PRESENCE_ELEMENT_NAME);\n\t\t\t\t\t\t\t\tpres.setXMLNS(CLIENT_XMLNS);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tpres = pres.clone();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tpres.setAttribute(Packet.TO_ATT, buddy.toString());\n\t\t\t\t\t\t\tpres.setAttribute(Packet.FROM_ATT, session.getJID().toString());\n\t\t\t\t\t\t\tresults.offer(Packet.packetInstance(pres, session.getJID(), buddy));\n\n\t\t\t\t\t\t\tif (autoAuthorize) {\n\t\t\t\t\t\t\t\tPresenceAbstract.sendPresence(StanzaType.subscribe,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  session.getJID().copyWithoutResource(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  buddy.copyWithoutResource(), results, null);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tElement new_buddy = roster_util.getBuddyItem(session, buddy);\n\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"1. New Buddy: {0}\", new_buddy.toString());\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (roster_util.getBuddySubscription(session, buddy) == null) {\n\t\t\t\t\t\t\troster_util.setBuddySubscription(session, SubscriptionType.none, buddy);\n\t\t\t\t\t\t}    // end of if (getBuddySubscription(session, buddy) == null)\n\t\t\t\t\t\tnew_buddy = roster_util.getBuddyItem(session, buddy);\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"2. New Buddy: {0}\", new_buddy.toString());\n\t\t\t\t\t\t}\n\t\t\t\t\t\troster_util.updateBuddyChange(session, results, new_buddy);\n\t\t\t\t\t}      // end of else\n\n\t\t\t\t\t// end of else\n\t\t\t\t}\n\t\t\t\tresults.offer(packet.okResult((String) null, 0));\n\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"Buddy JID is incorrct, stringprep failed.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   true));\n\t\t\t}\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"No items found in roster set request: {0}\", packet);\n\t\t\tresults.offer(\n\t\t\t\t\tAuthorization.BAD_REQUEST.getResponseMessage(packet, \"No items found in the roster set request\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true));\n\t\t}\n\t}\n\n\t/**\n\t * Calculates hash value based on the user roster items and saves it to user's session data.\n\t *\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param settings this map keeps plugin specific settings loaded from the Tigase server configuration.\n\t *\n\t */\n\tprotected void updateHash(XMPPResourceConnection session, Map<String, Object> settings)\n\t\t\tthrows NotAuthorizedException, TigaseDBException, RosterRetrievingException, RepositoryAccessException {\n\t\troster_util.updateRosterHash(session);\n\t}\n\n\t/**\n\t * Returns shared instance of class implementing {@link RosterAbstract} - either default one ({@link RosterFlat}) or\n\t * the one configured with <em>\"roster-implementation\"</em> property.\n\t *\n\t * @return a shared instance of class implementing {@link RosterAbstract}\n\t */\n\tprotected RosterAbstract getRosterUtil() {\n\t\treturn RosterFactory.getRosterImplementation(true);\n\t}\n\n\t/**\n\t * Performs processing of remote roster management requests as described in <a href=\"http://xmpp.org/extensions/xep-0321.html\">XEP-0321:\n\t * Remote Roster Management</a> for the purpose of, for example, gateways.\n\t *\n\t * @param packet packet is which being processed.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param settings this map keeps plugin specific settings loaded from the Tigase server configuration.\n\t *\n\t */\n\tprivate void processRemoteRosterManagementRequest(Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  Queue<Packet> results, final Map<String, Object> settings)\n\t\t\tthrows PacketErrorTypeException {\n\t\tif (!RemoteRosterManagement.isRemoteAllowed(packet.getStanzaFrom(), session)) {\n\t\t\tresults.offer(\n\t\t\t\t\tAuthorization.NOT_ALLOWED.getResponseMessage(packet, \"Not authorized for remote roster management\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true));\n\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tswitch (packet.getType()) {\n\t\t\t\tcase get:\n\t\t\t\t\tList<Element> ritems = roster_util.getRosterItems(session);\n\n\t\t\t\t\tif ((ritems != null) && !ritems.isEmpty()) {\n\t\t\t\t\t\tElement query = new Element(\"query\");\n\n\t\t\t\t\t\tquery.setXMLNS(RosterAbstract.XMLNS);\n\n\t\t\t\t\t\tString jidStr = \"@\" + packet.getStanzaFrom().getBareJID().toString();\n\n\t\t\t\t\t\tfor (Element ritem : ritems) {\n\t\t\t\t\t\t\tif (ritem.getAttributeStaticStr(\"jid\").endsWith(jidStr)) {\n\t\t\t\t\t\t\t\tquery.addChild(ritem);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresults.offer(packet.okResult(query, 0));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresults.offer(packet.okResult((String) null, 1));\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase set:\n\t\t\t\t\tprocessSetRequest(packet, session, results, settings);\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet, \"Bad stanza type\", true));\n\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t} catch (PolicyViolationException e) {\n\t\t\tlog.log(Level.WARNING, \"Roster set request violated items number policy: {0}\", packet);\n\t\t\tresults.offer(Authorization.POLICY_VIOLATION.getResponseMessage(packet, e.getLocalizedMessage(), true));\n\t\t} catch (Throwable ex) {\n\t\t\tlog.log(Level.WARNING, \"Reflection execution exception\", ex);\n\t\t\tresults.offer(\n\t\t\t\t\tAuthorization.INTERNAL_SERVER_ERROR.getResponseMessage(packet, \"Internal server error\", true));\n\t\t}\n\t}\n}    // JabberIqRoster\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/JabberIqStats.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Command;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * XEP-0039: Statistics Gathering. http://www.xmpp.org/extensions/xep-0039.html\n * <br>\n * Created: Sat Mar 25 06:45:00 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n\n// FIXME: Plugin doesn't work correctly currently so it's disabled, see #server-1242 for details\n@Bean(name = JabberIqStats.ID, parent = SessionManager.class, active = false)\npublic class JabberIqStats\n\t\textends XMPPProcessor\n\t\timplements XMPPProcessorIfc {\n\n\tprivate static final String[][] ELEMENTS = {Iq.IQ_QUERY_PATH, Iq.IQ_COMMAND_PATH};\n\tprivate static final Logger log = Logger.getLogger(JabberIqStats.class.getName());\n\tprivate static final String XMLNS = \"http://jabber.org/protocol/stats\";\n\tprotected static final String ID = XMLNS;\n\tprivate static final String[] XMLNSS = {XMLNS, Command.XMLNS};\n\tprivate static final Element[] DISCO_FEATURES = {new Element(\"feature\", new String[]{\"var\"}, new String[]{XMLNS})};\n\n\t@Override\n\tpublic Authorization canHandle(Packet packet, XMPPResourceConnection conn) {\n\t\tif (conn == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn super.canHandle(packet, conn);\n\t}\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic void process(final Packet packet, final XMPPResourceConnection session, final NonAuthUserRepository repo,\n\t\t\t\t\t\tfinal Queue<Packet> results, final Map<String, Object> settings) throws XMPPException {\n\t\tif (session == null) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Received packet: {0}\", packet);\n\t\t\t}\n\t\t\tif (packet.isCommand()) {\n\t\t\t\tif ((packet.getCommand() == Command.GETSTATS) && (packet.getType() == StanzaType.result)) {\n\t\t\t\t\tJID conId = session.getConnectionId(packet.getStanzaTo());\n\n\t\t\t\t\tif (conId == null) {\n\n\t\t\t\t\t\t// Drop it, user is no longer online.\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Send it back to user.\n\t\t\t\t\tElement query = new Element(\"query\", XMLNS);\n\t\t\t\t\tElement stats = Command.getData(packet, \"statistics\", null);\n\n\t\t\t\t\tquery.addChildren(stats.getChildren());\n\n\t\t\t\t\tPacket result = packet.okResult(query, 0);\n\n\t\t\t\t\tresult.setPacketTo(conId);\n\t\t\t\t\tresults.offer(result);\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Sending result: {0}\", result);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn;\n\t\t\t\t} else {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}    // end of if (packet.isCommand()\n\n\t\t\t// Maybe it is message to admininstrator:\n\t\t\tBareJID id = (packet.getStanzaTo() != null) ? packet.getStanzaTo().getBareJID() : null;\n\n\t\t\t// If ID part of user account contains only host name\n\t\t\t// and this is local domain it is message to admin\n\t\t\tif ((id == null) || session.isLocalDomain(id.toString(), false)) {\n\t\t\t\tString oldto = packet.getAttributeStaticStr(\"oldto\");\n\t\t\t\tPacket result = Command.GETSTATS.getPacket(packet.getStanzaFrom(), session.getDomainAsJID(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   StanzaType.get, packet.getStanzaId());\n\n\t\t\t\tif (oldto != null) {\n\t\t\t\t\tresult.getElement().setAttribute(\"oldto\", oldto);\n\t\t\t\t}\n\t\t\t\tresults.offer(result);\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"Sending result: \" + result);\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (session.isUserId(id)) {\n\n\t\t\t\t// Yes this is message to 'this' client\n\t\t\t\tPacket result = packet.copyElementOnly();\n\n\t\t\t\tresult.setPacketTo(session.getConnectionId(packet.getStanzaTo()));\n\t\t\t\tresult.setPacketFrom(packet.getTo());\n\t\t\t\tresults.offer(result);\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"Sending result: \" + result);\n\t\t\t\t}\n\t\t\t} else {\n\n\t\t\t\t// This is message to some other client so I need to\n\t\t\t\t// set proper 'from' attribute whatever it is set to now.\n\t\t\t\t// Actually processor should not modify request but in this\n\t\t\t\t// case it is absolutely safe and recommended to set 'from'\n\t\t\t\t// attribute\n\t\t\t\t// Not needed anymore. Packet filter does it for all stanzas.\n//      // According to spec we must set proper FROM attribute\n//      el_res.setAttribute(\"from\", session.getJID());\n\t\t\t\tPacket result = packet.copyElementOnly();\n\n\t\t\t\tresults.offer(result);\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"Sending result: \" + result);\n\t\t\t\t}\n\t\t\t}    // end of else\n\t\t} catch (NotAuthorizedException e) {\n\t\t\tlog.warning(\"Received stats request but user session is not authorized yet: \" + packet);\n\t\t\tresults.offer(\n\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(packet, \"You must authorize session first.\", true));\n\t\t}    // end of try-catch\n\t}\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(final XMPPResourceConnection session) {\n\t\treturn DISCO_FEATURES;\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n}    // JabberIqStats\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/JabberIqVersion.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.XMPPServer;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.PacketErrorTypeException;\nimport tigase.xmpp.XMPPProcessorAbstract;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * XEP-0092: Software Version\n * <br>\n * Created: Tue Mar 21 06:45:51 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = JabberIqVersion.ID, parent = SessionManager.class, active = true)\npublic class JabberIqVersion\n\t\textends XMPPProcessorAbstract {\n\n\tprivate static final Logger log = Logger.getLogger(JabberIqVersion.class.getName());\n\tprivate static final String XMLNS = \"jabber:iq:version\";\n\tprotected static final String ID = XMLNS;\n\tprivate static final String[][] ELEMENTS = {Iq.IQ_QUERY_PATH};\n\tprivate static final String[] XMLNSS = {XMLNS};\n\tprivate static final Element SERVER_VERSION;\n\n\tstatic {\n\t\t// @formatter:off\n\t\tSERVER_VERSION = new Element(\"query\", new Element[]{\n\t\t\t\tnew Element(\"name\", XMPPServer.NAME),\n\t\t\t\tnew Element(\"version\", XMPPServer.getImplementationVersion()),\n\t\t\t\tnew Element(\"os\", System.getProperty(\"os.name\") + \"-\" +\n\t\t\t\t\t\tSystem.getProperty(\"os.arch\") + \"-\" +\n\t\t\t\t\t\tSystem.getProperty(\"os.version\") + \", \" +\n\t\t\t\t\t\tSystem.getProperty(\"java.vm.name\") + \"-\" +\n\t\t\t\t\t\tSystem.getProperty(\"java.vm.version\") + \"-\" +\n\t\t\t\t\t\tSystem.getProperty(\"java.vm.vendor\"))},\n\t new String[]{\"xmlns\"}, new String[]{XMLNS});\n\t\t// @formatter:onn\n\t}\n\n\tprivate static final Element[] DISCO_FEATURES = {new Element(\"feature\", new String[]{\"var\"}, new String[]{XMLNS})};\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic void processFromUserPacket(JID connectionId, Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t  NonAuthUserRepository repo, Queue<Packet> results, Map<String, Object> settings)\n\t\t\tthrows PacketErrorTypeException {\n\t\t// Check whether the packet is addressed to the server or some other, XMPP entity\n\t\tif ((packet.getStanzaTo() == null) || session.isLocalDomain(packet.getStanzaTo().toString(), false) ||\n\t\t\t\t(packet.getStanzaTo().getLocalpart() != null && packet.getStanzaTo().getResource() == null &&\n\t\t\t\t\t\tsession.getSMComponentId().getLocalpart().equals(packet.getStanzaTo().getLocalpart()) &&\n\t\t\t\t\t\tsession.isLocalDomain(packet.getStanzaTo().getDomain(), false))) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Calling method: {0}, for packet={1}, for session={2}\",\n\t\t\t\t\t\tnew Object[]{\"processFromUserToServerPacket\", packet, session});\n\t\t\t}\n\t\t\tprocessFromUserToServerPacket(connectionId, packet, session, repo, results, settings);\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Calling method: {0}, for packet={1}, for session={2}\",\n\t\t\t\t\t\tnew Object[]{\"processFromUserOutPacket\", packet, session});\n\t\t\t}\n\t\t\tprocessFromUserOutPacket(connectionId, packet, session, repo, results, settings);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processFromUserOutPacket(JID connectionId, Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t Map<String, Object> settings) {\n\t\tresults.offer(packet.copyElementOnly());\n\t}\n\n\t@Override\n\tpublic void processFromUserToServerPacket(JID connectionId, Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t  NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t\t  Map<String, Object> settings) {\n\t\tresults.offer(packet.okResult(SERVER_VERSION, 0));\n\t}\n\n\t@Override\n\tpublic void processServerSessionPacket(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t\t\t\t   Queue<Packet> results, Map<String, Object> settings) {\n\t\tresults.offer(packet.okResult(SERVER_VERSION, 0));\n\t}\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(final XMPPResourceConnection session) {\n\t\treturn DISCO_FEATURES;\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n}    // JabberIqVersion\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/Jingle.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Describe class Jingle here.\n * <br>\n * Created: Wed Feb 21 23:05:34 2007\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = Jingle.ID, parent = SessionManager.class, active = false)\npublic class Jingle\n\t\textends XMPPProcessor\n\t\timplements XMPPProcessorIfc {\n\n\tprotected static final String ID = \"http://jabber.org/protocol/jingle\";\n\tprivate static final Logger log = Logger.getLogger(Jingle.class.getName());\n\tprivate static final String[] JINGLE_PATH = {Iq.ELEM_NAME, \"jingle\"};\n\tprivate static final String[][] ELEMENTS = {JINGLE_PATH, JINGLE_PATH, JINGLE_PATH, {Iq.ELEM_NAME, \"session\"}};\n\tprivate static final String[] XMLNSS = {\"http://jabber.org/protocol/jingle\",\n\t\t\t\t\t\t\t\t\t\t\t\"http://www.xmpp.org/extensions/xep-0166.html#ns\",\n\t\t\t\t\t\t\t\t\t\t\t\"http://www.xmpp.org/extensions/xep-0167.html#ns\",\n\t\t\t\t\t\t\t\t\t\t\t\"http://www.google.com/session\"};\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic void process(final Packet packet, final XMPPResourceConnection conn,\n\t\t\t\t\t\tfinal NonAuthUserRepository nonAuthUserRepo, final Queue<Packet> results,\n\t\t\t\t\t\tfinal Map<String, Object> settings) throws XMPPException {\n\t\tif (conn == null) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Received packet: \" + packet);\n\t\t\t}\n\n\t\t\t// Not needed anymore. Packet filter does it for all stanzas.\n//    // For all messages coming from the owner of this account set\n//    // proper 'from' attribute. This is actually needed for the case\n//    // when the user sends a message to himself.\n//    if (packet.getFrom().equals(conn.getConnectionId())) {\n//      packet.getElement().setAttribute(\"from\", conn.getJID());\n//    } // end of if (packet.getFrom().equals(session.getConnectionId()))\n\t\t\tBareJID id = packet.getStanzaTo().getBareJID();\n\n\t\t\tif (conn.isUserId(id)) {\n\n\t\t\t\t// Yes this is message to 'this' client\n\t\t\t\t// Make sure we send it to right client connection - the connection\n\t\t\t\t// which supports jingle - this Yate specific code....\n\t\t\t\tList<XMPPResourceConnection> res = conn.getParentSession().getActiveResources();\n\t\t\t\tXMPPResourceConnection session = conn;\n\n\t\t\t\tif ((res != null) && (res.size() > 1)) {\n\n\t\t\t\t\t// If there are more than 1 connection for this user\n\t\t\t\t\t// let's look for a connection with jingle flag set...\n\t\t\t\t\tfor (XMPPResourceConnection sess : res) {\n\t\t\t\t\t\tif (sess.getSessionData(\"jingle\") != null) {\n\t\t\t\t\t\t\tsession = sess;\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tPacket result = packet.copyElementOnly();\n\n\t\t\t\tresult.setPacketTo(session.getConnectionId());\n\t\t\t\tresult.setPacketFrom(packet.getTo());\n\t\t\t\tresults.offer(result);\n\t\t\t} else {\n\n\t\t\t\t// This is message to some other client\n\t\t\t\tresults.offer(packet.copyElementOnly());\n\t\t\t}    // end of else\n\t\t} catch (NotAuthorizedException e) {\n\t\t\tlog.warning(\"NotAuthorizedException for packet: \" + packet);\n\t\t\tresults.offer(\n\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(packet, \"You must authorize session first.\", true));\n\t\t}    // end of try-catch\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/LastActivity.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserNotFoundException;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.Permissions;\nimport tigase.sys.TigaseRuntime;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.annotation.*;\nimport tigase.xmpp.impl.roster.RosterAbstract;\nimport tigase.xmpp.impl.roster.RosterAbstract.SubscriptionType;\nimport tigase.xmpp.impl.roster.RosterElement;\nimport tigase.xmpp.impl.roster.RosterFactory;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.EnumSet;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.xmpp.impl.LastActivity.ID;\nimport static tigase.xmpp.impl.LastActivityAbstract.XMLNS;\n\n/**\n * Implementation of <a href='http://xmpp.org/extensions/xep-0012.html'>XEP-0012</a>: Last Activity.\n *\n * @author bmalkow\n */\n\n@Id(ID)\n@DiscoFeatures({XMLNS})\n@HandleStanzaTypes(StanzaType.get)\n@Handles(@Handle(path = {Iq.ELEM_NAME, Iq.QUERY_NAME}, xmlns = XMLNS))\n@Bean(name = LastActivity.ID, parent = LastActivityMarker.class, active = true)\npublic class LastActivity\n\t\textends XMPPProcessorAbstract\n\t\timplements LastActivityRetriever {\n\n\tprotected final static String ID = XMLNS;\n\tprivate static final Logger log = Logger.getLogger(LastActivity.class.getName());\n\tprivate final static String PROTECTION_LEVEL_KEY = \"protection-level\";\n\tprivate final static EnumSet<SubscriptionType> inTypes = EnumSet.of(SubscriptionType.both, SubscriptionType.from);\n\tprivate final static EnumSet<SubscriptionType> outTypes = EnumSet.of(SubscriptionType.both, SubscriptionType.to);\n\t@ConfigField(desc = \"Protection level\", alias = PROTECTION_LEVEL_KEY)\n\tprivate ProtectionLevel protectionLevel = ProtectionLevel.ALL;\n\n\tprotected static RosterAbstract getRosterUtil() {\n\t\treturn RosterFactory.getRosterImplementation(true);\n\t}\n\n\tprivate static Packet preventFromINFLoop(Packet packet) {\n\t\tpacket.setPacketTo(null);\n\t\treturn packet;\n\t}\n\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Processing packet: {0}\", packet);\n\t\t}\n\n\t\tsuper.process(packet, session, repo, results, settings);\n\t}\n\n\t@Override\n\tpublic void processFromUserPacket(JID connectionId, Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t  NonAuthUserRepository repo, Queue<Packet> results, Map<String, Object> settings)\n\t\t\tthrows PacketErrorTypeException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Process From user packet: {0}\", packet);\n\t\t}\n\n\t\tswitch (packet.getType()) {\n\t\t\tcase get:\n\t\t\t\tswitch (protectionLevel) {\n\t\t\t\t\tcase BUDDIES:\n\t\t\t\t\t\tif (getRosterUtil() == null) {\n\t\t\t\t\t\t\tlog.warning(\"Roster factory returned null\");\n\t\t\t\t\t\t\tresults.offer(preventFromINFLoop(\n\t\t\t\t\t\t\t\t\tAuthorization.SERVICE_UNAVAILABLE.getResponseMessage(packet, null, false)));\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tRosterElement element = getRosterUtil().getRosterElement(session, packet.getStanzaTo());\n\t\t\t\t\t\t\tif (element == null || !outTypes.contains(element.getSubscription())) {\n\t\t\t\t\t\t\t\tresults.offer(preventFromINFLoop(\n\t\t\t\t\t\t\t\t\t\tAuthorization.FORBIDDEN.getResponseMessage(packet, null, false)));\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tsuper.processFromUserPacket(connectionId, packet, session, repo, results, settings);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (NotAuthorizedException | TigaseDBException e) {\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINE, e.getMessage(), e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tresults.offer(preventFromINFLoop(\n\t\t\t\t\t\t\t\t\tAuthorization.SERVICE_UNAVAILABLE.getResponseMessage(packet, null, false)));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ALL:\n\t\t\t\t\t\tsuper.processFromUserPacket(connectionId, packet, session, repo, results, settings);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase error:\n\t\t\tcase result:\n\t\t\t\tsuper.processFromUserPacket(connectionId, packet, session, repo, results, settings);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tresults.offer(preventFromINFLoop(\n\t\t\t\t\t\tAuthorization.BAD_REQUEST.getResponseMessage(packet, \"Message type is incorrect\", false)));\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processFromUserToServerPacket(JID connectionId, Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t  NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t\t  Map<String, Object> settings) throws PacketErrorTypeException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Processing from user to server packet: {0}\", packet);\n\t\t}\n\n\t\tpacket.processedBy(ID);\n\t\tif (packet.getPermissions() == Permissions.ADMIN) {\n\t\t\thandleLastActivityRequest(packet, TigaseRuntime.getTigaseRuntime().getUptime(), null, results);\n\t\t} else {\n\t\t\tresults.offer(preventFromINFLoop(Authorization.FORBIDDEN.getResponseMessage(packet, null, false)));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processNullSessionPacket(Packet packet, NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t Map<String, Object> settings) throws PacketErrorTypeException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Processing null session packet: {0}\", packet);\n\t\t}\n\n\t\tpacket.processedBy(ID);\n\t\tswitch (packet.getType()) {\n\t\t\tcase get:\n\t\t\t\tBareJID requestedJid = packet.getStanzaTo().getBareJID();\n\t\t\t\ttry {\n\t\t\t\t\tfinal long last = LastActivityAbstract.getLastActivity(repo, requestedJid);\n\t\t\t\t\tfinal String status = LastActivityAbstract.getStatus(repo, requestedJid);\n\t\t\t\t\thandleLastActivityRequest(packet, last, status, results);\n\t\t\t\t} catch (UserNotFoundException e) {\n\t\t\t\t\tresults.offer(preventFromINFLoop(Authorization.FORBIDDEN.getResponseMessage(packet, null, false)));\n\t\t\t\t}\n\t\t\tcase error:\n\t\t\tcase result:\n\t\t\t\tsuper.processNullSessionPacket(packet, repo, results, settings);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tresults.offer(preventFromINFLoop(\n\t\t\t\t\t\tAuthorization.BAD_REQUEST.getResponseMessage(packet, \"Message type is incorrect\", false)));\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processServerSessionPacket(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t\t\t\t   Queue<Packet> results, Map<String, Object> settings)\n\t\t\tthrows PacketErrorTypeException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Processing server session packet: {0}\", packet);\n\t\t}\n\t\tpacket.processedBy(ID);\n\t\thandleLastActivityRequest(packet, TigaseRuntime.getTigaseRuntime().getUptime(), null, results);\n\t}\n\n\t@Override\n\tpublic void processToUserPacket(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings)\n\t\t\tthrows PacketErrorTypeException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Processing to user packet: {0}\", packet);\n\t\t}\n\n\t\tpacket.processedBy(ID);\n\t\tswitch (packet.getType()) {\n\t\t\tcase get:\n\t\t\t\tlong last = LastActivityAbstract.getLastActivity(session, packet);\n\t\t\t\tString status = LastActivityAbstract.getStatus(session);\n\n\t\t\t\tswitch (protectionLevel) {\n\t\t\t\t\tcase BUDDIES:\n\t\t\t\t\t\tif (getRosterUtil() == null) {\n\t\t\t\t\t\t\tlog.warning(\"Roster factory returned null\");\n\t\t\t\t\t\t\tresults.offer(preventFromINFLoop(\n\t\t\t\t\t\t\t\t\tAuthorization.SERVICE_UNAVAILABLE.getResponseMessage(packet, null, false)));\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tRosterElement element = getRosterUtil().getRosterElement(session, packet.getStanzaFrom());\n\t\t\t\t\t\t\tif (element == null || !inTypes.contains(element.getSubscription())) {\n\t\t\t\t\t\t\t\tresults.offer(preventFromINFLoop(\n\t\t\t\t\t\t\t\t\t\tAuthorization.FORBIDDEN.getResponseMessage(packet, null, false)));\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\thandleLastActivityRequest(packet, last, status, results);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (NotAuthorizedException | TigaseDBException e) {\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINE, e.getMessage(), e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tresults.offer(preventFromINFLoop(\n\t\t\t\t\t\t\t\t\tAuthorization.SERVICE_UNAVAILABLE.getResponseMessage(packet, null, false)));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase ALL:\n\t\t\t\t\t\thandleLastActivityRequest(packet, last, status, results);\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase error:\n\t\t\tcase result:\n\t\t\t\tsuper.processToUserPacket(packet, session, repo, results, settings);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tresults.offer(preventFromINFLoop(\n\t\t\t\t\t\tAuthorization.BAD_REQUEST.getResponseMessage(packet, \"Message type is incorrect\", false)));\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate void handleLastActivityRequest(Packet packet, long last, String status, Queue<Packet> results)\n\t\t\tthrows PacketErrorTypeException {\n\t\tif (last >= 0) {\n\t\t\tlong result = (System.currentTimeMillis() - last) / 1000;\n\t\t\tPacket resp = packet.okResult((Element) null, 0);\n\t\t\tElement q;\n\t\t\tif (status == null) {\n\t\t\t\tq = new Element(Iq.QUERY_NAME, new String[]{Packet.XMLNS_ATT, \"seconds\"},\n\t\t\t\t\t\t\t\tnew String[]{XMLNS, \"\" + result});\n\t\t\t} else {\n\t\t\t\tq = new Element(Iq.QUERY_NAME, status, new String[]{Packet.XMLNS_ATT, \"seconds\"},\n\t\t\t\t\t\t\t\tnew String[]{XMLNS, \"\" + result});\n\t\t\t}\n\t\t\tresp.getElement().addChild(q);\n\t\t\tresults.offer(resp);\n\t\t} else {\n\t\t\tresults.offer(preventFromINFLoop(\n\t\t\t\t\tAuthorization.ITEM_NOT_FOUND.getResponseMessage(packet, \"Unknown last activity time\", false)));\n\t\t}\n\t}\n\n\tenum ProtectionLevel {\n\t\tALL,\n\t\tBUDDIES;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/LastActivityAbstract.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserNotFoundException;\nimport tigase.server.Packet;\nimport tigase.server.Presence;\nimport tigase.xml.DomBuilderHandler;\nimport tigase.xml.Element;\nimport tigase.xml.SimpleParser;\nimport tigase.xml.SingletonFactory;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.XMPPProcessorIfc;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.impl.annotation.AnnotatedXMPPProcessor;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.Optional;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Implementation of <a href='http://xmpp.org/extensions/xep-0012.html'>XEP-0012</a>: Last Activity.\n *\n * @author bmalkow\n */\n\npublic abstract class LastActivityAbstract\n\t\textends AnnotatedXMPPProcessor\n\t\timplements XMPPProcessorIfc {\n\n\tprotected static final String XMLNS = \"jabber:iq:last\";\n\tpublic final static String LAST_ACTIVITY_KEY = \"LAST_ACTIVITY_KEY\";\n\tpublic final static String LAST_STATUS_KEY = \"LAST_STATUS_KEY\";\n\tpublic final static String LAST_SHOW_KEY = \"LAST_SHOW_KEY\";\n\tpublic final static String LAST_PRESENCE_KEY = \"LAST_PRESENCE_KEY\";\n\tprivate static final Logger log = Logger.getLogger(LastActivityAbstract.class.getName());\n\tpublic final static String[] STATUS_PATH = new String[]{Presence.ELEM_NAME, \"status\"};\n\tpublic final static String[] SHOW_PATH = new String[]{Presence.ELEM_NAME, \"show\"};\n\n\tprivate SimpleParser parser = SingletonFactory.getParserInstance();\n\n\tprotected static long getLastActivity(NonAuthUserRepository repo, BareJID requestedJid) throws UserNotFoundException {\n\t\tString result = repo.getPublicData(requestedJid, XMLNS, LAST_ACTIVITY_KEY, \"-1\");\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\"last-activity for: {0}, result: {1}\",\n\t\t\t\t\tnew String[]{String.valueOf(requestedJid), String.valueOf(result)});\n\t\t}\n\n\t\tif (result != null) {\n\t\t\treturn Long.parseLong(result);\n\t\t} else {\n\t\t\tthrow new UserNotFoundException(requestedJid + \" doesn't exist\");\n\t\t}\n\t}\n\n\tpublic static long getLastActivity(XMPPResourceConnection session, boolean global) {\n\t\tLong result = null;\n\n\t\tif (global) {\n\t\t\tresult = (Long) session.getCommonSessionData(LAST_ACTIVITY_KEY);\n\t\t} else {\n\t\t\tresult = (Long) session.getSessionData(LAST_ACTIVITY_KEY);\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\"last-activity for: {0}, result: {1}\",\n\t\t\t\t\tnew String[]{String.valueOf(session.getjid()), String.valueOf(result)});\n\t\t}\n\n\t\treturn result == null ? -1 : result;\n\t}\n\n\tprotected static long getLastActivity(XMPPResourceConnection session, Packet packet) {\n\t\treturn getLastActivity(session, (packet.getStanzaTo().getResource() == null ||\n\t\t\t\tpacket.getStanzaTo().getResource().length() == 0));\n\t}\n\n\tprotected static String getStatus(XMPPResourceConnection session) {\n\t\treturn session.getPresence() == null ? null : session.getPresence().getChildCDataStaticStr(STATUS_PATH);\n\t}\n\n\tprotected static String getShow(XMPPResourceConnection session) {\n\t\treturn session.getPresence() == null ? null : session.getPresence().getChildCDataStaticStr(SHOW_PATH);\n\t}\n\n\tprotected static String getShow(NonAuthUserRepository repo, BareJID requestedJid) throws UserNotFoundException {\n\t\treturn repo.getPublicData(requestedJid, XMLNS, LAST_SHOW_KEY, null);\n\t}\n\n\tprotected static String getType(XMPPResourceConnection session) {\n\t\treturn session.getPresence() == null ? null : session.getPresence().getAttributeStaticStr(\"type\");\n\t}\n\n\tprotected static String getType(NonAuthUserRepository repo, BareJID requestedJid) throws UserNotFoundException {\n\t\treturn repo.getPublicData(requestedJid, XMLNS, LAST_SHOW_KEY, null);\n\t}\n\n\n\tprotected Optional<Element> getPresence(NonAuthUserRepository repo, BareJID requestedJid)\n\t\t\tthrows UserNotFoundException {\n\t\tfinal String publicData = repo.getPublicData(requestedJid, XMLNS, LAST_PRESENCE_KEY, null);\n\n\t\tif (publicData != null ) {\n\t\t\tDomBuilderHandler domHandler = new DomBuilderHandler();\n\t\t\tchar[] data = publicData.toCharArray();\n\t\t\tparser.parse(domHandler, data, 0, data.length);\n\n\t\t\tfinal Queue<Element> parsedElements = domHandler.getParsedElements();\n\t\t\treturn parsedElements.peek() != null ? Optional.of(parsedElements.poll()) : Optional.empty();\n\t\t} else {\n\t\t\treturn Optional.empty();\n\t\t}\n\t}\n\n\tprotected static String getStatus(NonAuthUserRepository repo, BareJID requestedJid) throws UserNotFoundException {\n\t\treturn repo.getPublicData(requestedJid, XMLNS, LAST_STATUS_KEY, null);\n\t}\n\n\tprotected static void persistLastActivity(XMPPResourceConnection session, Element presence) {\n\t\tlong last = getLastActivity(session, false);\n\t\tString status = getStatus(session);\n\n\t\ttry {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\n\t\t\t\t\t\t\"Persiting last:activity of user \" + session.getUserName() + \" in storage (value=\" + last +\n\t\t\t\t\t\t\t\t\", \" + \"presence=\" + presence + \").\");\n\t\t\t}\n\t\t\tsession.setPublicData(XMLNS, LAST_ACTIVITY_KEY, String.valueOf(last));\n\t\t\tif (presence != null) {\n\t\t\t\tsession.setPublicData(XMLNS, LAST_PRESENCE_KEY, String.valueOf(presence));\n\t\t\t}\n\t\t\tsession.setPublicData(XMLNS, LAST_STATUS_KEY, status);\n\t\t} catch (NotAuthorizedException e) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"session isn't authorized\" + session);\n\t\t\t}\n\t\t} catch (TigaseDBException e) {\n\t\t\tlog.warning(\"Tigase Db Exception\");\n\t\t}\n\t}\n\n\n\tenum ProtectionLevel {\n\t\tALL,\n\t\tBUDDIES;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/LastActivityMarker.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.RegistrarBean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Message;\nimport tigase.server.Packet;\nimport tigase.server.Presence;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.XMPPException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.XMPPStopListenerIfc;\nimport tigase.xmpp.impl.annotation.DiscoFeatures;\nimport tigase.xmpp.impl.annotation.Handle;\nimport tigase.xmpp.impl.annotation.Handles;\nimport tigase.xmpp.impl.annotation.Id;\n\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.xmpp.impl.LastActivityAbstract.XMLNS;\nimport static tigase.xmpp.impl.LastActivityMarker.ID;\n\n/**\n * Implementation of <a href='http://xmpp.org/extensions/xep-0012.html'>XEP-0012</a>: Last Activity.\n *\n * @author bmalkow\n */\n\n@Id(ID)\n@DiscoFeatures({XMLNS})\n@Handles({@Handle(path = {Presence.ELEM_NAME}, xmlns = Packet.CLIENT_XMLNS),\n\t\t  @Handle(path = {Message.ELEM_NAME}, xmlns = Packet.CLIENT_XMLNS)})\n@Bean(name = LastActivityMarker.ID, parent = SessionManager.class, active = false)\npublic class LastActivityMarker\n\t\textends LastActivityAbstract\n\t\timplements XMPPStopListenerIfc, RegistrarBean {\n\n\tprotected final static String ID = XMLNS + \"-marker\";\n\tprivate static final Logger log = Logger.getLogger(LastActivityMarker.class.getName());\n\tprivate Kernel kernel;\n\t@ConfigField(desc = \"To persist all updates to repository\")\n\tprivate boolean persistAllToRepository = true;\n\t@Inject\n\tprivate LastActivityRetriever[] retrievers;\n\t@ConfigField(desc = \"Whether to update last activity information on message packets\", alias = \"message\")\n\tprivate boolean updateOnMessage = false;\n\t@ConfigField(desc = \"Whether to update last activity information on presence packets\", alias = \"presence\")\n\tprivate boolean updateOnPresence = true;\n\n\tprivate static void setLastActivity(XMPPResourceConnection session, Long last, Element presence, boolean repository) {\n\t\tsession.putCommonSessionData(LastActivityAbstract.LAST_ACTIVITY_KEY, last);\n\t\tsession.putSessionData(LastActivityAbstract.LAST_ACTIVITY_KEY, last);\n\t\tif (repository) {\n\t\t\tpersistLastActivity(session, presence);\n\t\t}\n\t}\n\n\tpublic void setRetrievers(LastActivityRetriever[] retrievers) {\n\t\tthis.retrievers = retrievers;\n\t\tif (kernel != null) {\n\t\t\tfor (LastActivityRetriever retriever : retrievers) {\n\t\t\t\tfinal Bean bean = retriever.getClass().getAnnotation(Bean.class);\n\t\t\t\tif (bean != null) {\n\t\t\t\t\tkernel.ln(bean.name(), kernel.getParent(), bean.name());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Processing packet: {0}\", packet);\n\t\t}\n\n\t\tif (session != null && packet.getStanzaFrom() != null &&\n\t\t\t\tsession.isUserId(packet.getStanzaFrom().getBareJID())) {\n\t\t\tfinal long time = System.currentTimeMillis();\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"Updating last:activity of user \" + session.getUserName() + \" to \" + time);\n\t\t\t}\n\n\t\t\tif ((updateOnMessage && packet.getElemName() == Message.ELEM_NAME) ||\n\t\t\t\t\t(updateOnPresence && packet.getElemName() == Presence.ELEM_NAME)) {\n\t\t\t\tsetLastActivity(session, time, packet.getElement(), persistAllToRepository);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void stopped(XMPPResourceConnection session, Queue<Packet> results, Map<String, Object> settings) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Session stoppped for \" + session.getjid() + \", persisting last activity\");\n\t\t}\n\t\tif (session != null && session.isAuthorized()) {\n\t\t\tfinal Element presence = session.getPresence();\n\t\t\tpersistLastActivity(session, presence);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\t\tthis.kernel = kernel;\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/LastActivityRetriever.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\npublic interface LastActivityRetriever {\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/MIXProcessor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.jspecify.annotations.Nullable;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.PolicyViolationException;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.util.Algorithms;\nimport tigase.util.Base64;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.annotation.*;\nimport tigase.xmpp.impl.roster.RosterAbstract;\nimport tigase.xmpp.impl.roster.RosterElement;\nimport tigase.xmpp.impl.roster.RosterFactory;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.nio.charset.StandardCharsets;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.EnumSet;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Queue;\nimport java.util.function.Consumer;\n\nimport static tigase.xmpp.impl.MIXProcessor.ID;\n\n@Id(ID)\n@Bean(name = ID, parent = SessionManager.class, active = true)\n@Handles({\n\t\t@Handle(path = {Iq.ELEM_NAME, \"client-join\"}, xmlns = \"urn:xmpp:mix:pam:2\"),\n\t\t@Handle(path = {Iq.ELEM_NAME, \"client-leave\"}, xmlns = \"urn:xmpp:mix:pam:2\"),\n\t\t@Handle(path = {Iq.ELEM_NAME, \"join\"}, xmlns = \"urn:xmpp:mix:core:1\"),\n\t\t@Handle(path = {Iq.ELEM_NAME, \"leave\"}, xmlns = \"urn:xmpp:mix:core:1\"),\n\t\t@Handle(path = {Message.ELEM_NAME, \"event\", \"items\", \"retract\"}, xmlns = Handle.ANY_XMLNS)\n})\n@DiscoFeatures({ ID })\npublic class MIXProcessor\n\t\textends AnnotatedXMPPProcessor implements XMPPProcessorIfc {\n\n\tpublic static final String ID = \"urn:xmpp:mix:pam:2\";\n\n\t@Inject\n\tprivate UserRepository userRepository;\n\n\tprivate static final RosterAbstract rosterUtil = RosterFactory.getRosterImplementation(true);\n\n\tprivate static final EnumSet<StanzaType> RESPONSE_TYPES = EnumSet.of(StanzaType.result, StanzaType.error);\n\tprivate static final String[] retractItemMessagePath = {Message.ELEM_NAME, \"event\", \"items\", \"retract\"};\n\n\t@Override\n\tpublic Authorization canHandle(Packet packet, XMPPResourceConnection conn) {\n\t\tAuthorization result = super.canHandle(packet, conn);\n\t\tif (result == Authorization.AUTHORIZED) {\n\t\t\tif (packet.getElement().findChild(el -> el.getXMLNS() == ID) != null) {\n\t\t\t\t// we need packet to be sent from local connection to process PAM request\n\t\t\t\treturn conn != null ? result : null;\n\t\t\t} else {\n\t\t\t\t// join/leave response is only processed if returned to bare JID\n\t\t\t\t// and is sent with type \"result\" or \"error\"\n\t\t\t\tif (RESPONSE_TYPES.contains(packet.getType())) {\n\t\t\t\t\treturn packet.getStanzaTo() != null && packet.getStanzaTo().getResource() == null ? result : null;\n\t\t\t\t} else {\n\t\t\t\t\tif  (Message.ELEM_NAME.equals(packet.getElemName())) {\n\t\t\t\t\t\treturn packet.getElement().findChild(retractItemMessagePath) != null ? result : null;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// stanza is a request and for direct communication with MIX component so leave it..\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\t\ttry {\n\t\t\tif (XMPPProcessorAbstract.isFromUserSession(packet, session)) {\n\t\t\t\t// processing packet coming fom the user..\n\t\t\t\tElement actionEl = packet.getElement().findChild(el -> el.getXMLNS() == ID);\n\t\t\t\tif (actionEl == null || !EnumSet.of(StanzaType.set).contains(packet.getType())) {\n\t\t\t\t\t// TODO: Add a way to forward iq's sent from the client directly to MIX component (leave/join)\n\t\t\t\t\t// FIXED: we do not need that as leave/join are no longer processed here\n\t\t\t\t\tthrow new XMPPProcessorException(Authorization.BAD_REQUEST);\n\t\t\t\t}\n\n\t\t\t\tBareJID channel = Optional.ofNullable(actionEl.getAttributeStaticStr(\"channel\"))\n\t\t\t\t\t\t.map(BareJID::bareJIDInstanceNS)\n\t\t\t\t\t\t.orElseThrow(() -> new XMPPProcessorException(Authorization.BAD_REQUEST));\n\n\t\t\t\tString id = packet.getStanzaId();\n\t\t\t\tif (id == null) {\n\t\t\t\t\tthrow new XMPPProcessorException(Authorization.BAD_REQUEST);\n\t\t\t\t}\n\n\t\t\t\tswitch (actionEl.getName()) {\n\t\t\t\t\tcase \"client-join\":\n\t\t\t\t\t\tElement joinEl = Optional.ofNullable(actionEl.getChildStaticStr(\"join\", \"urn:xmpp:mix:core:1\"))\n\t\t\t\t\t\t\t\t.orElseThrow(() -> new XMPPProcessorException(Authorization.BAD_REQUEST));\n\t\t\t\t\t\tsendToChannel(session.getBareJID(), session.getResource(), channel, id, joinEl, results::offer);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"client-leave\":\n\t\t\t\t\t\tElement leaveEl = Optional.ofNullable(actionEl.getChildStaticStr(\"leave\", \"urn:xmpp:mix:core:1\"))\n\t\t\t\t\t\t\t\t.orElseThrow(() -> new XMPPProcessorException(Authorization.BAD_REQUEST));\n\t\t\t\t\t\tsendToChannel(session.getBareJID(), session.getResource(), channel, id, leaveEl, results::offer);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthrow new XMPPProcessorException(Authorization.BAD_REQUEST);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// processing packet from the outside, ie. from other component or MIX component.. but always sent to the bare JID\n\t\t\t\tBareJID channel = packet.getStanzaFrom().getBareJID();\n\t\t\t\tif (packet.getStanzaId() != null &&\n\t\t\t\t\t\tEnumSet.of(StanzaType.result, StanzaType.error).contains(packet.getType())) {\n\t\t\t\t\tBareJID userJID = packet.getStanzaTo().getBareJID();\n\t\t\t\t\tString requestId = generateId(channel, packet.getStanzaId());\n\t\t\t\t\tString resource = userRepository.getData(userJID, ID, requestId, null);\n\t\t\t\t\tif (resource == null) {\n\t\t\t\t\t\t// this is error or result, so lets just ignore that as most likely it was already handled...\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tElement actionEl = packet.getElement().findChild(el -> el.getXMLNS() == \"urn:xmpp:mix:core:1\");\n\t\t\t\t\tuserRepository.removeData(userJID, ID, requestId);\n\n\t\t\t\t\tif (packet.getType() == StanzaType.result) {\n\t\t\t\t\t\t// we need to modify roster..\n\t\t\t\t\t\tJID channelJID = JID.jidInstance(channel);\n\t\t\t\t\t\tRosterElement item = new RosterElement(channelJID, null, null);\n\t\t\t\t\t\titem.setSubscription(RosterAbstract.SubscriptionType.both);\n\t\t\t\t\t\titem.setMixParticipantId(actionEl.getAttributeStaticStr(\"id\"));\n\t\t\t\t\t\tswitch (actionEl.getName()) {\n\t\t\t\t\t\t\tcase \"join\":\n\t\t\t\t\t\t\t\tresults.addAll(rosterUtil.addJidToRoster(userRepository, session == null ? null : session.getParentSession(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t userJID, item));\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase \"leave\":\n\t\t\t\t\t\t\t\tresults.addAll(rosterUtil.removeJidFromRoster(userRepository, session == null ? null : session.getParentSession(), userJID, channelJID));\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tOptional.ofNullable(session)\n\t\t\t\t\t\t\t\t.map(XMPPResourceConnection::getParentSession)\n\t\t\t\t\t\t\t\t.map(parent -> parent.getResourceForResource(resource))\n\t\t\t\t\t\t\t\t.map(conn -> {\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\treturn conn.getConnectionId();\n\t\t\t\t\t\t\t\t\t} catch (NoConnectionIdException ex) {\n\t\t\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t.ifPresent(connJID -> {\n\t\t\t\t\t\t\t\t\tElement actionElCopy = actionEl.clone();\n\t\t\t\t\t\t\t\t\tString id = actionEl.getAttributeStaticStr(\"id\");\n\t\t\t\t\t\t\t\t\tif (id != null) {\n\t\t\t\t\t\t\t\t\t\tactionElCopy.setAttribute(\"jid\", actionEl.getAttributeStaticStr(\"id\") + \"#\" +\n\t\t\t\t\t\t\t\t\t\t\t\tchannel.toString());\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tsendToUser(userJID, resource, connJID, packet.getType(), packet.getStanzaId(),\n\t\t\t\t\t\t\t\t\t\t\t   actionElCopy, null, results::offer);\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t} else if(packet.getType() == StanzaType.error) {\n\t\t\t\t\t\tOptional.ofNullable(session).map(XMPPResourceConnection::getParentSession)\n\t\t\t\t\t\t\t\t.map(parent -> parent.getResourceForResource(resource))\n\t\t\t\t\t\t\t\t.map(conn -> {\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\treturn conn.getConnectionId();\n\t\t\t\t\t\t\t\t\t} catch (NoConnectionIdException ex) {\n\t\t\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t.ifPresent(connJID -> {\n\t\t\t\t\t\t\t\t\tElement actionElCopy = actionEl.clone();\n\t\t\t\t\t\t\t\t\tactionElCopy.setAttribute(\"jid\", actionEl.getAttributeStaticStr(\"id\") + \"#\" + channel.toString());\n\t\t\t\t\t\t\t\t\tsendToUser(userJID, resource, connJID, packet.getType(),\n\t\t\t\t\t\t\t\t\t\t\t   packet.getStanzaId(), actionElCopy, packet.getElement().findChild(el -> el.getName() == \"error\"), results::offer);\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} else if (session != null && packet.getElemName() == Message.ELEM_NAME) {\n\t\t\t\t\tBareJID userJID = packet.getStanzaTo().getBareJID();\n\t\t\t\t\tJID channelJID = JID.jidInstance(channel);\n\t\t\t\t\tvar participantId = generateParticipantId(channelJID.getBareJID(), session.getBareJID());\n\t\t\t\t\tvar childStaticStr = packet.getElement().findChildStaticStr(retractItemMessagePath);\n\t\t\t\t\tvar retractedId = childStaticStr != null ? childStaticStr.getAttributeStaticStr(\"id\") : null;\n\t\t\t\t\tif (participantId != null && participantId.equals(retractedId)) {\n\n\t\t\t\t\t\tresults.addAll(\n\t\t\t\t\t\t\trosterUtil.removeJidFromRoster(userRepository, session == null ? null : session.getParentSession(),\n\t\t\t\t\t\t\t                               userJID, channelJID));\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthrow new XMPPProcessorException(Authorization.BAD_REQUEST);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (PolicyViolationException ex) {\n\t\t\tthrow new XMPPProcessorException(Authorization.POLICY_VIOLATION, ex.getMessage(), ex);\n\t\t} catch (TigaseDBException ex) {\n\t\t\tthrow new XMPPProcessorException(Authorization.INTERNAL_SERVER_ERROR, \"Cound not access database\", ex);\n\t\t}\n\t}\n\n\tprotected void sendToChannel(BareJID userJID, BareJID channel, String id, Element actionEl, Consumer<Packet> writer) {\n\t\tElement iqEl = new Element(\"iq\");\n\t\tiqEl.setXMLNS(Iq.CLIENT_XMLNS);\n\t\tiqEl.setAttribute(\"id\", id);\n\t\tiqEl.setAttribute(\"type\", \"set\");\n\n\t\tiqEl.addChild(actionEl);\n\n\t\twriter.accept(Packet.packetInstance(iqEl, JID.jidInstance(userJID), JID.jidInstance(channel)));\n\t}\n\n\tprotected void sendToUser(BareJID userJID, String resource, JID connectionJID, StanzaType stanzaType, String id, Element actionEl, Element errorEl, Consumer<Packet> writer) {\n\t\tElement iqEl = new Element(\"iq\");\n\t\tiqEl.setXMLNS(Iq.CLIENT_XMLNS);\n\t\tiqEl.setAttribute(\"id\", id);\n\t\tif (stanzaType != null) {\n\t\t\tiqEl.setAttribute(\"type\", stanzaType.toString());\n\t\t}\n\n\t\tif (actionEl != null) {\n\t\t\tElement wrapEl = null;\n\t\t\tswitch (actionEl.getName()) {\n\t\t\t\tcase \"join\":\n\t\t\t\t\twrapEl = new Element(\"client-join\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"leave\":\n\t\t\t\t\twrapEl = new Element(\"client-leave\");\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (wrapEl != null) {\n\t\t\t\twrapEl.setXMLNS(ID);\n\t\t\t\twrapEl.addChild(actionEl);\n\t\t\t\tiqEl.addChild(wrapEl);\n\t\t\t}\n\t\t}\n\t\tif (errorEl != null) {\n\t\t\tiqEl.addChild(errorEl);\n\t\t}\n\n\t\tPacket response = Packet.packetInstance(iqEl, JID.jidInstance(userJID), JID.jidInstanceNS(userJID, resource));\n\t\tresponse.setPacketTo(connectionJID);\n\t\twriter.accept(response);\n\t}\n\n\tpublic void sendToChannel(BareJID userJid, String resource, BareJID channel, String requestStanzaId, Element joinEl, Consumer<Packet> writer)\n\t\t\tthrows XMPPProcessorException, TigaseDBException {\n\t\tuserRepository.setData(userJid, ID, generateId(channel, requestStanzaId), resource);\n\t\tsendToChannel(userJid, channel, requestStanzaId, joinEl, writer);\n\t}\n\n\tprotected String generateId(BareJID channel, String packetID) throws XMPPProcessorException {\n\t\ttry {\n\t\t\tMessageDigest md = MessageDigest.getInstance(\"SHA-256\");\n\t\t\tmd.update(channel.toString().getBytes(StandardCharsets.UTF_8));\n\t\t\tbyte[] hash = md.digest(packetID.getBytes(StandardCharsets.UTF_8));\n\t\t\treturn Base64.encode(hash);\n\t\t} catch (NoSuchAlgorithmException ex) {\n\t\t\tthrow new XMPPProcessorException(Authorization.INTERNAL_SERVER_ERROR);\n\t\t}\n\t}\n\n\tpublic static @Nullable String generateParticipantId(BareJID channelJID, BareJID participantRealJID) {\n\t\tif (channelJID.getLocalpart() == null) {\n\t\t\tthrow new IllegalArgumentException(\"Channel JID `\" + channelJID + \"` must contain localpart!\");\n\t\t}\n\t\ttry {\n\t\t\tMessageDigest md = MessageDigest.getInstance(\"SHA1\");\n\t\t\tmd.update(channelJID.getDomain().getBytes(StandardCharsets.UTF_8));\n\t\t\tmd.update(participantRealJID.toString().getBytes(StandardCharsets.UTF_8));\n\t\t\tmd.update(channelJID.getLocalpart().getBytes(StandardCharsets.UTF_8));\n\t\t\treturn Algorithms.bytesToHex(md.digest());\n\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\treturn null;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/Message.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Iq;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.annotation.AnnotatedXMPPProcessor;\nimport tigase.xmpp.impl.annotation.Handle;\nimport tigase.xmpp.impl.annotation.Handles;\nimport tigase.xmpp.impl.annotation.Id;\n\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Logger;\n\nimport static tigase.xmpp.impl.Message.ELEM_NAME;\nimport static tigase.xmpp.impl.Message.XMLNS;\n\n/**\n * Message forwarder class. Forwards <code>Message</code> packet to it's destination address.\n * <br>\n * Created: Tue Feb 21 15:49:08 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Id(ELEM_NAME)\n@Handles({@Handle(path = {ELEM_NAME}, xmlns = XMLNS), @Handle(path = {Iq.ELEM_NAME, \"fin\"}, xmlns = \"urn:xmpp:mam:2\"),\n\t\t  @Handle(path = {Iq.ELEM_NAME, \"fin\"}, xmlns = \"urn:xmpp:mam:1\")})\n@Bean(name = ELEM_NAME, parent = SessionManager.class, active = false, exportable = true)\npublic class Message\n\t\textends AnnotatedXMPPProcessor\n\t\timplements XMPPProcessorIfc, XMPPPreprocessorIfc, XMPPPacketFilterIfc {\n\n\t@Inject\n\tprivate MessageDeliveryLogic messageDeliveryLogic;\n\t\n\tprotected static final String ELEM_NAME = tigase.server.Message.ELEM_NAME;\n\tprotected static final String XMLNS = \"jabber:client\";\n\tprivate static final Logger log = Logger.getLogger(Message.class.getName());\n\n\t@Override\n\tpublic void filter(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t   Queue<Packet> results) {\n\t\tC2SDeliveryErrorProcessor.filter(packet, session, repo, results, null);\n\t}\n\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\t\tmessageDeliveryLogic.handleDelivery(packet, session, repo, results, settings);\n\t}\n\n\t@Override\n\tpublic boolean preProcess(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t  Queue<Packet> results, Map<String, Object> settings) {\n\t\tboolean result = C2SDeliveryErrorProcessor.preProcess(packet, session, repo, results, settings, messageDeliveryLogic);\n\t\tif (messageDeliveryLogic.preProcessFilter(packet, session)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (result) {\n\t\t\tpacket.processedBy(id());\n\t\t}\n\t\treturn result;\n\t}\n\n}    // Message\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/MessageAll.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Variant of the <code>Message</code> forwarder class. This implementation forwards messages to all connected\n * resources. <br> When a message <strong>'from'</strong> the user is being processed, the plugin forwards the message\n * to the destination address and also sends the message to all other connected resources.<br> When a message\n * <strong>'to'</strong> the user is being processed, the plugin forwards the message to all connected resources.<br>\n * The idea behind this implementation is to keep all connected resources synchronized with a complete chat content.\n * User should be able to switch between connections and continue the chat.\n * <br>\n * Created: Feb 14, 2010 4:35:45 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = MessageAll.ID, parent = SessionManager.class, active = false)\npublic class MessageAll\n\t\textends XMPPProcessor\n\t\timplements XMPPProcessorIfc {\n\n\tprotected static final String ID = \"message-all\";\n\tprivate static final String[][] ELEMENTS = {{\"message\"}};\n\tprivate static final Logger log = Logger.getLogger(MessageAll.class.getName());\n\tprivate static final String XMLNS = \"jabber:client\";\n\tprivate static final String[] XMLNSS = {XMLNS};\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\n\t\t// For performance reasons it is better to do the check\n\t\t// before calling logging method.\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.finest(\"Processing packet: \" + packet);\n\t\t}\n\n\t\t// You may want to skip processing completely if the user is offline.\n\t\tif (session == null) {\n\t\t\treturn;\n\t\t}    // end of if (session == null)\n\t\ttry {\n\n\t\t\t// Remember to cut the resource part off before comparing JIDs\n\t\t\tBareJID id = (packet.getStanzaTo() != null) ? packet.getStanzaTo().getBareJID() : null;\n\n\t\t\t// Checking if this is a packet TO the owner of the session\n\t\t\tif (session.isUserId(id)) {\n\n\t\t\t\t// Yes this is message to 'this' client\n\t\t\t\t// Send the message to all connected resources:\n\t\t\t\tfor (XMPPResourceConnection conn : session.getActiveSessions()) {\n\t\t\t\t\tPacket result = packet.copyElementOnly();\n\n\t\t\t\t\t// This is where and how we set the address of the component\n\t\t\t\t\t// which should receive the packet for the final delivery\n\t\t\t\t\t// to the end-user. In most cases this is a c2s or Bosh component\n\t\t\t\t\t// which keep the user connection.\n\t\t\t\t\tresult.setPacketTo(conn.getConnectionId());\n\n\t\t\t\t\t// In most cases this might be skept, however if there is a\n\t\t\t\t\t// problem during packet delivery an error might be sent back\n\t\t\t\t\tresult.setPacketFrom(packet.getTo());\n\n\t\t\t\t\t// Don't forget to add the packet to the results queue or it\n\t\t\t\t\t// will be lost.\n\t\t\t\t\tresults.offer(result);\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}    // end of else\n\n\t\t\t// Remember to cut the resource part off before comparing JIDs\n\t\t\tid = (packet.getStanzaFrom() != null) ? packet.getStanzaFrom().getBareJID() : null;\n\n\t\t\t// Checking if this is maybe packet FROM the client\n\t\t\tif (session.isUserId(id)) {\n\n\t\t\t\t// First we have to update all the other resources with this message just send\n\t\t\t\t// from the user.\n\t\t\t\tfor (XMPPResourceConnection conn : session.getActiveSessions()) {\n\n\t\t\t\t\t// Don't send the message back to the connection from which is has been\n\t\t\t\t\t// received, only to all other connections.\n\t\t\t\t\tif (conn != session) {\n\t\t\t\t\t\tPacket result = packet.copyElementOnly();\n\n\t\t\t\t\t\t// This is where and how we set the address of the component\n\t\t\t\t\t\t// which should receive the packet for the final delivery\n\t\t\t\t\t\t// to the end-user. In most cases this is a c2s or Bosh component\n\t\t\t\t\t\t// which keep the user connection.\n\t\t\t\t\t\tresult.setPacketTo(conn.getConnectionId());\n\n\t\t\t\t\t\t// Don't forget to add the packet to the results queue or it\n\t\t\t\t\t\t// will be lost.\n\t\t\t\t\t\tresults.offer(result);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// This is a packet FROM this client, the simplest action is\n\t\t\t\t// to forward it to is't destination:\n\t\t\t\t// Simple clone the XML element and....\n\t\t\t\t// ... putting it to results queue is enough\n\t\t\t\tresults.offer(packet.copyElementOnly());\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Can we really reach this place here?\n\t\t\t// If the point is reached then the message packet does not have either from or\n\t\t\t// to address. In such a case we ignore it.\n\t\t} catch (NotAuthorizedException e) {\n\t\t\tlog.warning(\"NotAuthorizedException for packet: \" + packet);\n\t\t\tresults.offer(\n\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(packet, \"You must authorize session first.\", true));\n\t\t}    // end of try-catch\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/MessageAmp.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.MsgRepositoryIfc;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserNotFoundException;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.RegistrarBean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.server.amp.AmpFeatureIfc.*;\n\n/**\n * Created: Apr 29, 2010 5:00:25 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = MessageAmp.ID, parent = SessionManager.class, active = true, exportable = true)\npublic class MessageAmp\n\t\textends XMPPProcessor\n\t\timplements XMPPPacketFilterIfc, XMPPPostprocessorIfc, XMPPPreprocessorIfc, XMPPProcessorIfc, RegistrarBean {\n\n\tprotected static final String ID = \"amp\";\n\tprivate static final String AMP_JID_PROP_KEY = \"amp-jid\";\n\tprivate static final String STATUS_ATTRIBUTE_NAME = \"status\";\n\tprivate static final String[][] ELEMENTS = {{\"message\"}, {\"presence\"}, {\"iq\", \"msgoffline\"}, {\"iq\", \"fin\"}, {\"iq\", \"fin\"}};\n\tprivate static final Logger log = Logger.getLogger(MessageAmp.class.getName());\n\tprivate static final String XMLNS = \"http://jabber.org/protocol/amp\";\n\tprivate static final String[] XMLNSS = {\"jabber:client\", \"jabber:client\", \"msgoffline\", \"urn:xmpp:mam:2\", \"urn:xmpp:mam:1\"};\n\tprivate static final Element[] DISCO_FEATURES_WITH_OFFLINE = {\n\t\t\tnew Element(\"feature\", new String[]{\"var\"}, new String[]{XMLNS}),\n\t\t\tnew Element(\"feature\", new String[]{\"var\"}, new String[]{\"msgoffline\"})};\n\tprivate static final Element[] DISCO_FEATURES_WITHOUT_OFFLINE = new Element[]{\n\t\t\tnew Element(\"feature\", new String[]{\"var\"}, new String[]{XMLNS})};\n\tprivate static String defHost = DNSResolverFactory.getInstance().getDefaultHost();\n\n//\tprivate static final String STATUS_ATTRIBUTE_NAME = \"status\";\n\n\t@ConfigField(desc = \"AMP component JID\", alias = AMP_JID_PROP_KEY)\n\tprivate JID ampJID = JID.jidInstanceNS(\"amp@\" + defHost);\n\t@Inject\n\tprivate MessageDeliveryLogic messageProcessor;\n\t@Inject\n\tprivate MsgRepositoryIfc msg_repo = null;\n\t@Inject(nullAllowed = true)\n\tprivate OfflineMessages offlineProcessor;\n\t@ConfigField(desc = \"\", alias = \"quota-exceeded\")\n\tprivate QuotaRule quotaExceededRule = QuotaRule.error;\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic void filter(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t   Queue<Packet> results) {\n\t\tC2SDeliveryErrorProcessor.filter(packet, session, repo, results, ampJID);\n\t}\n\n\t@Override\n\tpublic void postProcess(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) {\n\t\tif ((offlineProcessor != null) && (session == null || (packet.getElemName() == Message.ELEM_NAME &&\n\t\t\t\t!messageProcessor.hasConnectionForMessageDelivery(session)))) {\n\t\t\tif (packet.getElemName() == tigase.server.Message.ELEM_NAME && packet.getStanzaTo() != null &&\n\t\t\t\t\tpacket.getStanzaTo().getResource() != null) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tElement amp = packet.getElement().getChild(\"amp\");\n\n\t\t\tif ((amp == null) || (amp.getXMLNS() != XMLNS)\n//\t\t\t\t\t \"Individual action definitions MAY provide their own requirements.\" regarding\n//\t\t\t\t\t\t\"status\" attribute requirement!!! applies to \"alert\" and \"notify\"\n//\t\t\t\t\t|| (amp.getAttributeStaticStr(STATUS_ATTRIBUTE_NAME) != null)\n\t\t\t\t\t) {\n\t\t\t\ttry {\n\t\t\t\t\tif (session != null && packet.getStanzaTo() != null &&\n\t\t\t\t\t\t\t(packet.getStanzaTo().getLocalpart() == null ||\n\t\t\t\t\t\t\t\t\t!session.isUserId(packet.getStanzaTo().getBareJID()))) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tAuthorization saveResult = offlineProcessor.savePacketForOffLineUser(packet, msg_repo, repo);\n\t\t\t\t\tPacket result = null;\n\n\t\t\t\t\tofflineProcessor.notifyNewOfflineMessage(packet, session, results, settings);\n\n\t\t\t\t\tswitch (saveResult) {\n\t\t\t\t\t\tcase SERVICE_UNAVAILABLE:\n\t\t\t\t\t\t\tswitch (quotaExceededRule) {\n\t\t\t\t\t\t\t\tcase error:\n\t\t\t\t\t\t\t\t\tresult = saveResult.getResponseMessage(packet, \"Offline messages queue is full\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   true);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase drop:\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (result != null) {\n\t\t\t\t\t\tresults.offer(result);\n\t\t\t\t\t}\n\t\t\t\t} catch (UserNotFoundException ex) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.finest(\"UserNotFoundException at trying to save packet for off-line user.\" + packet);\n\t\t\t\t\t}\n\t\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Could not save packet for offline user, returning error \" + packet, ex);\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tresults.offer(Authorization.INTERNAL_SERVER_ERROR.getResponseMessage(packet, null, true));\n\t\t\t\t\t} catch (PacketErrorTypeException e) {\n\t\t\t\t\t\tlog.finest(\"Could not sent error for unsaved packet for offline user \" + packet);\n\t\t\t\t\t}\n\t\t\t\t} catch (NotAuthorizedException ex) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"NotAuthorizedException when checking if message is to this \" +\n\t\t\t\t\t\t\t\t\t\t\"user at trying to save packet for off-line user, {0}, {1}\",\n\t\t\t\t\t\t\t\tnew Object[]{packet, session});\n\t\t\t\t\t}\n\t\t\t\t} catch (PacketErrorTypeException ex) {\n\t\t\t\t\tlog.log(Level.FINE,\n\t\t\t\t\t\t\t\"Could not sent error to packet sent to offline user which storage to offline \" +\n\t\t\t\t\t\t\t\t\t\"store failed. Packet is error type already: {0}\", packet.toStringSecure());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean preProcess(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t  Queue<Packet> results, Map<String, Object> settings) {\n\t\tboolean processed = C2SDeliveryErrorProcessor.preProcess(packet, session, repo, results, settings,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t messageProcessor);\n\t\tif (messageProcessor.preProcessFilter(packet, session)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (processed && packet.getPacketFrom() != null &&\n\t\t\t\tpacket.getPacketFrom().getLocalpart().equals(ampJID.getLocalpart())) {\n\t\t\tprocessed = false;\n\t\t}\n\t\tif (processed) {\n\t\t\tpacket.processedBy(ID);\n\t\t} else if (packet.getElemName() == Message.ELEM_NAME) {\n\t\t\tElement amp = packet.getElement().getChild(\"amp\", XMLNS);\n\t\t\tif (amp == null\n//\t\t\t\t\t \"Individual action definitions MAY provide their own requirements.\" regarding\n//\t\t\t\t\t\t\"status\" attribute requirement!!! applies to \"alert\" and \"notify\"\n\t\t\t\t\t|| (amp.getAttributeStaticStr(STATUS_ATTRIBUTE_NAME) != null) ||\n\t\t\t\t\tampJID.equals(packet.getPacketFrom())) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tif (session == null) {\n\t\t\t\t\tPacket result = packet.copyElementOnly();\n\t\t\t\t\tresult.setPacketTo(ampJID);\n\t\t\t\t\tresult.setStableId(packet.getStableId());\n\t\t\t\t\tresults.offer(result);\n\t\t\t\t\tresult.getElement().addAttribute(OFFLINE, \"1\");\n\t\t\t\t\tpacket.processedBy(ID);\n\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tif (session.isUserId(packet.getStanzaTo().getBareJID()) && session.getjid() != null &&\n\t\t\t\t\t\tsession.getjid().equals(packet.getStanzaTo())) {\n\t\t\t\t\tPacket result = packet.copyElementOnly();\n\t\t\t\t\tresult.setStableId(packet.getStableId());\n\t\t\t\t\tresult.setPacketTo(ampJID);\n\t\t\t\t\tif (packet.getStanzaTo().getResource() != null) {\n\t\t\t\t\t\tresult.getElement().addAttribute(TO_RES, session.getResource());\n\t\t\t\t\t}\n\t\t\t\t\tresults.offer(result);\n\t\t\t\t\tboolean offline = !messageProcessor.hasConnectionForMessageDelivery(session);\n\t\t\t\t\tif (offline) {\n\t\t\t\t\t\tresult.getElement().addAttribute(OFFLINE, \"1\");\n\t\t\t\t\t}\n\t\t\t\t\tpacket.processedBy(ID);\n\t\t\t\t\treturn true;\n//\t\t\t\t} else {\n\t\t\t\t\t// this needs to be handled in process() method so we need to allow packet \n\t\t\t\t\t// to be processed in this method\n//\t\t\t\t\tJID connectionId = session.getConnectionId();\n//\n//\t\t\t\t\tif (connectionId.equals(packet.getPacketFrom())) {\n//\t\t\t\t\t\tresult.getElement().addAttribute(FROM_CONN_ID, connectionId.toString());\n//\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t} catch (XMPPException ex) {\n\t\t\t\tlog.log(Level.SEVERE, \"this should not happen\", ex);\n\t\t\t}\n\t\t}\n\t\treturn processed;\n\t}\n\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\t\tswitch (packet.getElemName()) {\n\t\t\tcase \"presence\":\n\t\t\t\tif ((offlineProcessor != null) && offlineProcessor.loadOfflineMessages(packet, session)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tQueue<Packet> packets = offlineProcessor.restorePacketForOffLineUser(session, msg_repo);\n\n\t\t\t\t\t\tif (packets != null) {\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\t\t\tlog.finer(\"Sending off-line messages: \" + packets.size());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tresults.addAll(packets);\n\n\t\t\t\t\t\t\tif (!packets.isEmpty()) {\n\t\t\t\t\t\t\t\tofflineProcessor.notifyOfflineMessagesRetrieved(session, results);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}    // end of if (packets != null)\n\t\t\t\t\t} catch (TigaseDBException e) {\n\t\t\t\t\t\tlog.log(Level.CONFIG, \"Something wrong, DB problem, cannot load offline messages. \" + e);\n\t\t\t\t\t}      // end of try-catch\n\n\t\t\t\t\t// notify AMP component that user is online now\n\t\t\t\t\tif (packet.getStanzaTo() == null) {\n\t\t\t\t\t\tPacket notification = packet.copyElementOnly();\n\t\t\t\t\t\tnotification.initVars(session.getJID(), ampJID);\n\t\t\t\t\t\tresults.offer(notification);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"message\":\n\t\t\t\tElement amp = packet.getElement().getChild(\"amp\", XMLNS);\n\n\t\t\t\tif ((amp == null)\n//\t\t\t\t\t \"Individual action definitions MAY provide their own requirements.\" regarding\n//\t\t\t\t\t\t\"status\" attribute requirement!!! applies to \"alert\" and \"notify\"\n\t\t\t\t\t\t|| (amp.getAttributeStaticStr(STATUS_ATTRIBUTE_NAME) != null) ||\n\t\t\t\t\t\t(packet.getPacketFrom() != null &&\n\t\t\t\t\t\t\t\tampJID.getLocalpart().equals(packet.getPacketFrom().getLocalpart()))) {\n\t\t\t\t\tmessageProcessor.handleDelivery(packet, session, repo, results, settings);\n\t\t\t\t} else {\n\t\t\t\t\t// when packet from user with AMP is sent we need to forward it to AMP\n\t\t\t\t\t// for processing but we need to do this here and not in preProcess method\n\t\t\t\t\t// as in other case other processors would not receive this packet at all!\n\t\t\t\t\tJID connectionId = session.getConnectionId();\n\t\t\t\t\tPacket result = packet.copyElementOnly();\n\t\t\t\t\tif (connectionId.equals(packet.getPacketFrom())) {\n\t\t\t\t\t\t//if (!session.isUserId(packet.getStanzaTo().getBareJID()))\n\t\t\t\t\t\tresult.getElement().addAttribute(FROM_CONN_ID, connectionId.toString());\n\t\t\t\t\t\tif (null != session.getBareJID()) {\n\t\t\t\t\t\t\tresult.getElement().addAttribute(SESSION_JID, session.getJID().toString());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tresult.setPacketTo(ampJID);\n\t\t\t\t\tresults.offer(result);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"iq\":\n\t\t\t\t// if there is no session do not try to deliver this stanza, instead use offline processor\n\t\t\t\tif (session != null && packet.getElemChild(\"fin\") != null) {\n\t\t\t\t\tmessageProcessor.handleDelivery(packet, session, repo, results, settings);\n\t\t\t\t} else {\n\t\t\t\t\tif (offlineProcessor != null) {\n\t\t\t\t\t\tofflineProcessor.processIq(packet, session, repo, results);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresults.offer(Authorization.FEATURE_NOT_IMPLEMENTED.getResponseMessage(packet, ID, true));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(final XMPPResourceConnection session) {\n\t\treturn offlineProcessor == null ? DISCO_FEATURES_WITHOUT_OFFLINE : DISCO_FEATURES_WITH_OFFLINE;\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\t\tkernel.registerBean(OfflineMessages.class).setActive(true).exec();\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\n\t}\n\t\n\tprivate enum QuotaRule {\n\t\terror,\n\t\tdrop;\n\n\t\tpublic static QuotaRule valueof(String name) {\n\t\t\ttry {\n\t\t\t\tif (name != null) {\n\t\t\t\t\treturn QuotaRule.valueOf(name);\n\t\t\t\t}\n\t\t\t} catch (IllegalArgumentException ex) {\n\t\t\t}\n\t\t\treturn QuotaRule.error;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/MessageCarbons.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.component.exceptions.ComponentException;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.eventbus.HandleEvent;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Iq;\nimport tigase.server.Message;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.server.xmppsession.UserPresenceChangedEvent;\nimport tigase.server.xmppsession.UserSessionEvent;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Function;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\n/**\n * MessageCarbons class implements XEP-0280 Message Carbons protocol extension.\n *\n * @author andrzej\n */\n@Bean(name = MessageCarbons.ID, parent = SessionManager.class, active = true)\npublic class MessageCarbons\n\t\textends XMPPProcessor\n\t\timplements XMPPProcessorIfc, XMPPPacketFilterIfc, SaslAuth2.Inline {\n\n\tpublic static final String XMLNS = \"urn:xmpp:carbons:2\";\n\tprotected static final String ID = \"message-carbons\";\n\tprivate static final Logger log = Logger.getLogger(MessageCarbons.class.getCanonicalName());\n\tprivate static final String[][] ELEMENTS = {{\"message\"}, {\"iq\", \"enable\"}, {\"iq\", \"disable\"}};\n\tprivate static final String[] XMLNSS = {XMPPProcessor.CLIENT_XMLNS, XMLNS, XMLNS};\n\n\tprivate static final Element[] DISCO_FEATURES = {new Element(\"feature\", new String[]{\"var\"}, new String[]{XMLNS})};\n\tprivate static final Element[] DISCO_FEATURES_WITH_RULES = {\n\t\t\tnew Element(\"feature\", new String[]{\"var\"}, new String[]{XMLNS}),\n\t\t\tnew Element(\"feature\", new String[]{\"var\"}, new String[]{\"urn:xmpp:carbons:rules:0\"})};\n\n\tprivate static final String ENABLED_KEY = XMLNS + \"-enabled\";\n\n\tprivate static final String ENABLED_RESOURCES_KEY = XMLNS + \"-resources\";\n\n\tprivate static final String ENABLE_ELEM_NAME = \"enable\";\n\tprivate static final String DISABLE_ELEM_NAME = \"disable\";\n\n\tprivate static final String[] MESSAGE_HINTS_NO_COPY = {Message.ELEM_NAME, \"no-copy\"};\n\tprivate static final String MESSAGE_HINTS_XMLNS = \"urn:xmpp:hints\";\n\n\tprivate static final Function<String, Object> RESOURCES_MAP_FACTORY = (k) -> {\n\t\treturn new ConcurrentHashMap<JID, Boolean>();\n\t};\n\n\tprivate static final ElementMatcher[] DEF_MSG_CARBON_PATHS = {\n\t\t\tnew ElementMatcher(new String[]{Message.ELEM_NAME, \"body\"}, null, true),\n\t\t\t// handling recipts, markers and states..\n\t\t\tnew ElementMatcher(new String[]{Message.ELEM_NAME, \"received\"}, \"urn:xmpp:receipts\", true),\n\t\t\tnew ElementMatcher(new String[]{Message.ELEM_NAME}, true, \"urn:xmpp:chat-markers:0\", null, true),\n\t\t\tnew ElementMatcher(new String[]{Message.ELEM_NAME}, true, \"http://jabber.org/protocol/chatstates\", null, true),\n\t\t\t// handling MUC invitations..\n\t\t\tnew ElementMatcher(new String[]{Message.ELEM_NAME, \"x\"},\"jabber:x:conference\", true),\n\t\t\tnew ElementMatcher(new String[]{Message.ELEM_NAME, \"x\", \"invite\"}, null, true)\n\t};\n\n\tprivate final EventBus eventBus = EventBusFactory.getInstance();\n\t@Inject\n\tprivate MessageDeliveryLogic messageProcessor;\n\n\t@ConfigField(desc = \"Send carbons of messages of type normal with matching paths\", alias = \"msg-carbons-paths\")\n\tprivate ElementMatcher[] msgCarbonPaths = DEF_MSG_CARBON_PATHS;\n\n\tprivate boolean usesDefCarbonRules = true;\n\n\t/**\n\t * Returns true if session is enabled for receiving carbon copy messages\n\t */\n\tprivate static boolean isEnabled(XMPPResourceConnection session) throws NotAuthorizedException {\n\t\tBoolean value = (Boolean) session.getSessionData(ENABLED_KEY);\n\t\treturn (value != null && value);\n\t}\n\n\t@Inject(nullAllowed = true)\n\tprivate SessionManager.MessageArchive messageArchive;\n\n\t/**\n\t * Prepare packet which is carbon copy of message passed as argument\n\t */\n\tprivate static Packet prepareCarbonCopy(Packet packet, JID srcJid, JID jid,\n\t\t\t\t\t\t\t\t\t\t\tString type) { //throws NoConnectionIdException {\n\t\tPacket msgClone = Message.getMessage(srcJid, jid, packet.getType(), null, null, null, packet.getStanzaId());\n\n\t\t//msgClone.setPacketTo(session.getConnectionId(jid));\n\n\t\tElement received = new Element(type);\n\t\treceived.setXMLNS(XMLNS);\n\t\tmsgClone.getElement().addChild(received);\n\n\t\tElement forwarded = new Element(\"forwarded\");\n\t\tforwarded.setXMLNS(\"urn:xmpp:forward:0\");\n\t\treceived.addChild(forwarded);\n\n\t\tforwarded.addChild(packet.getElement().clone());\n\n\t\treturn msgClone;\n\n\t}\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic boolean canHandle(XMPPResourceConnection connection, Element el) {\n\t\treturn el.getName() == \"enable\" && el.getXMLNS() == XMLNS;\n\t}\n\n\tpublic String[] getMsgCarbonPaths() {\n\t\tString[] result = new String[msgCarbonPaths.length];\n\t\tfor (int i = 0; i < msgCarbonPaths.length; i++) {\n\t\t\tresult[i] = msgCarbonPaths[i].toString();\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic void setMsgCarbonPaths(String[] matcherStrs) {\n\t\tList<ElementMatcher> matchers = new ArrayList<>();\n\t\tfor (String matcherStr : matcherStrs) {\n\t\t\tElementMatcher matcher = ElementMatcher.create(matcherStr);\n\t\t\tif (matcher != null) {\n\t\t\t\tmatchers.add(matcher);\n\t\t\t}\n\t\t}\n\t\tmsgCarbonPaths = matchers.toArray(new ElementMatcher[0]);\n\t\tusesDefCarbonRules = Arrays.equals(\n\t\t\t\tArrays.stream(DEF_MSG_CARBON_PATHS).map(matcher -> matcher.toString()).toArray(String[]::new),\n\t\t\t\tmatcherStrs);\n\t}\n\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\n\t\tif (session == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!session.isAuthorized()) {\n\t\t\ttry {\n\t\t\t\tresults.offer(\n\t\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(packet, \"Session is not yet authorized.\", false));\n\t\t\t} catch (PacketErrorTypeException ex) {\n\t\t\t\tlog.log(Level.FINEST, \"ignoring packet from not authorized session\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (packet.getElemName() == Iq.ELEM_NAME) {\n\n\t\t\tboolean enable = packet.getElement().getChild(ENABLE_ELEM_NAME, XMLNS) != null;\n\t\t\tboolean disable = packet.getElement().getChild(DISABLE_ELEM_NAME, XMLNS) != null;\n\n\t\t\t// we can only enable or disable but we cannot do both\n\t\t\tif ((enable && disable) || (!enable && !disable)) {\n\t\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet, null, false));\n\t\t\t} else {\n\t\t\t\tsetEnabled(session, enable);\n\t\t\t\t// send result of operation\n\t\t\t\tresults.offer(packet.okResult((Element) null, 0));\n\t\t\t}\n\t\t} else if (packet.getElemName() == Message.ELEM_NAME\n//\t\t\t\t&& packet.getType() == StanzaType.chat\n\t\t\t\t&& packet.getStanzaTo() != null) {\n\n\t\t\t// ignoring if message packet was resent from c2s for redelivery\n\t\t\tif (C2SDeliveryErrorProcessor.isDeliveryError(packet)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tMap<JID, Boolean> resources = (Map<JID, Boolean>) session.getCommonSessionData(ENABLED_RESOURCES_KEY);\n\t\t\tif (resources == null || resources.isEmpty()) {\n\t\t\t\t// no session has enabled message carbons\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// if this is error delivering forked message we should not fork it\n\t\t\t// but we need to fork only messsages with type chat so no need to check it\n\t\t\t//if (isErrorDeliveringForkedMessage(packet, session))\n\t\t\t//\treturn;\n\n\t\t\t// it is better to carbon copy all messages except from errors..\n\t\t\t// this way all devices will be kept in sync\n\t\t\tif (shouldSendCarbons(packet, session)) {\n\n\t\t\t\t// if this is error delivering forked message we should not fork it\n\t\t\t\t// but we need to fork only messsages with type chat so no need to check it\n\t\t\t\t//if (isErrorDeliveringForkedMessage(packet, session))\n\t\t\t\t//\treturn;\n\t\t\t\tif (packet.getElement().getChild(\"received\", XMLNS) != null ||\n\t\t\t\t\t\tpacket.getElement().getChild(\"sent\", XMLNS) != null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// support for XEP-0334 Message Processing Hints\n\t\t\t\tif (packet.getAttributeStaticStr(MESSAGE_HINTS_NO_COPY, \"xmlns\") == MESSAGE_HINTS_XMLNS) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// if this is private message then do not send carbon copy\n\t\t\t\tElement privateEl = packet.getElement().getChild(\"private\", XMLNS);\n\n\t\t\t\tif (privateEl != null) {\n\t\t\t\t\t// TODO: is it enought to just remove this element?\n\t\t\t\t\tpacket.getElement().removeChild(privateEl);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tString type = session.isUserId(packet.getStanzaTo().getBareJID()) ? \"received\" : \"sent\";\n\t\t\t\tJID srcJid = JID.jidInstance(session.getBareJID());\n\t\t\t\t// collections of jid to which message will be delivered by default so we need to skip them\n\t\t\t\tSet<JID> skipForkingTo = prepareSkipForkingToList(packet, session,resources);\n\t\t\t\t\n\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\tlog.log(Level.FINER,\n\t\t\t\t\t\t\t\"Sending message carbon copy, packet: {0}, resources {1}, skipForkingTo: {2}, session: {3}\",\n\t\t\t\t\t\t\tnew Object[]{packet, resources, skipForkingTo, session});\n\t\t\t\t}\n\n\t\t\t\tList<JID> copyTo = resources.entrySet()\n\t\t\t\t\t\t.stream()\n\t\t\t\t\t\t.filter(Map.Entry::getValue)\n\t\t\t\t\t\t.map(Map.Entry::getKey)\n\t\t\t\t\t\t.filter(jid -> !skipForkingTo.contains(jid))\n\t\t\t\t\t\t.collect(Collectors.toList());\n\n\t\t\t\tif (!copyTo.isEmpty()) {\n\t\t\t\t\tPacket clone = packet.copyElementOnly();\n\t\t\t\t\tclone.setStableId(packet.getStableId());\n\t\t\t\t\tif (messageArchive != null) {\n\t\t\t\t\t\tmessageArchive.addStableId(clone, session);\n\t\t\t\t\t}\n\t\t\t\t\tfor (JID jid : copyTo) {\n\t\t\t\t\t\tPacket msgClone = prepareCarbonCopy(clone, srcJid, jid, type);\n\t\t\t\t\t\tresults.offer(msgClone);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic CompletableFuture<Result> process(XMPPResourceConnection session, JID boundJID, Element action) {\n\t\tif (action.getName() == \"enable\" && action.getXMLNS() == XMLNS) {\n\t\t\ttry {\n\t\t\t\tsetEnabled(session, boundJID, true);\n\t\t\t\treturn CompletableFuture.completedFuture(new Result(new Element(\"enabled\", new String[]{\"xmlns\"}, new String[]{XMLNS}), true));\n\t\t\t} catch (NotAuthorizedException ex) {\n\t\t\t\treturn CompletableFuture.failedFuture(ex);\n\t\t\t}\n\t\t} else {\n\t\t\treturn CompletableFuture.failedFuture(new ComponentException(Authorization.BAD_REQUEST, \"Only 'enable' is supported\"));\n\t\t}\n\t}\n\t\n\t@Override\n\tpublic Element[] supStreamFeatures(Action action) {\n\t\treturn switch (action) {\n\t\t\tcase sasl2 -> new Element[0];\n\t\t\tcase bind2 -> DISCO_FEATURES;\n\t\t};\n\t}\n\n\tprotected Set<JID> prepareSkipForkingToList(Packet packet, XMPPResourceConnection session, Map<JID, Boolean> resources)\n\t\t\tthrows NotAuthorizedException {\n\t\tif (session.isUserId(packet.getStanzaTo().getBareJID()) && packet.getStanzaTo().getResource() == null) {\n\t\t\t// message is cloned to all resources by Message.java, it violates RFC6121\n\t\t\t// while it should be copied only to resources with non negative priority!!\n\t\t\t// until it is not solved there is no need to fork messages\n\n\t\t\t// as we started to respect connection priority we need to implement proper\n\t\t\t// forking of messages sent to bare jid\n\t\t\t// we need to fork this message\n\t\t\tSet<JID> skipForkingTo = messageProcessor.getJIDsForMessageDelivery(session);\n\n\t\t\t// we should skip forking to JID with enabled message carbons if jid is not from local node\n\t\t\tfor (JID jid : resources.keySet()) {\n\t\t\t\tif (session.getParentSession().getResourceForJID(jid) == null) {\n\t\t\t\t\tskipForkingTo.add(jid);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn skipForkingTo;\n\t\t} else {\n\t\t\treturn Collections.singleton(session.getJID());\n\t\t}\n\t}\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(XMPPResourceConnection session) {\n\t\tif (usesDefCarbonRules) {\n\t\t\treturn DISCO_FEATURES_WITH_RULES;\n\t\t} else {\n\t\t\treturn DISCO_FEATURES;\n\t\t}\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\t@Override\n\tpublic void filter(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t   Queue<Packet> results) {\n\t\tif ((session == null) || !session.isAuthorized() || (results == null) || (results.size() == 0) ||\n\t\t\t\tpacket == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (packet.getElemName() != Message.ELEM_NAME) {\n\t\t\treturn;\n\t\t}\n\n\t\tfor (Iterator<Packet> it = results.iterator(); it.hasNext(); ) {\n\t\t\tPacket res = it.next();\n\n\t\t\tif (res.getElemName() != Message.ELEM_NAME) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// if it is error during delivering forked message then drop it\n\t\t\tif (isErrorDeliveringForkedMessage(packet, session)) {\n\t\t\t\tit.remove();\n\t\t\t}\n\n\t\t\tElement messageEl = res.getElement();\n\n\t\t\tElement privateEl = messageEl.getChild(\"private\", XMLNS);\n\n\t\t\tif (privateEl != null) {\n\t\t\t\t// TODO: is it enought to just remove this element?\n\t\t\t\tmessageEl.removeChild(privateEl);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Method responsible for handing <code>MessageCarbonsStateChangedEvent</code> and marking JIDs of user as\n\t * MessageCarbons enabled/disabled.\n\t *\n\t */\n\t@HandleEvent\n\tprotected void stateChanged(MessageCarbonsStateChangedEvent event) {\n\t\tXMPPSession session = event.getSession();\n\t\tConcurrentHashMap<JID, Boolean> resources = (ConcurrentHashMap<JID, Boolean>) session.computeCommonSessionDataIfAbsent(\n\t\t\t\tENABLED_RESOURCES_KEY, RESOURCES_MAP_FACTORY);\n\n\t\tfor (JID jid : event.getEnabledJids()) {\n\t\t\tresources.put(jid, true);\n\t\t}\n\t\tfor (JID jid : event.getDisabledJids()) {\n\t\t\tresources.put(jid, false);\n\t\t}\n\t}\n\n\t/**\n\t * Method handles <code>UserPresenceChangedEvent</code> and synchronizes state of JIDs for MessageCarbons for\n\t * particular user.\n\t */\n\t@HandleEvent\n\tprotected void presenceUpdate(UserPresenceChangedEvent presenceEvent) throws NotAuthorizedException {\n\t\tXMPPSession session = presenceEvent.getSession();\n\t\tPacket packet = presenceEvent.getPresence();\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"session = {0} processing presence = {1}\", new Object[]{session, packet.toString()});\n\t\t}\n\n\t\tConcurrentHashMap<JID, Boolean> resources = (ConcurrentHashMap<JID, Boolean>) session.computeCommonSessionDataIfAbsent(\n\t\t\t\tENABLED_RESOURCES_KEY, RESOURCES_MAP_FACTORY);\n\n\t\tStanzaType type = packet.getType();\n\t\tif (type == null || type == StanzaType.available) {\n\t\t\tif (resources.putIfAbsent(packet.getStanzaFrom(), false) != null) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// if this is first time we see presence from this resource, \n\t\t\t// notify this user about every other connected resources\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.log(Level.FINER, \"session = {0} adding resource = {1} to list of available resources\",\n\t\t\t\t\t\tnew Object[]{session, packet.getStanzaFrom()});\n\t\t\t}\n\n\t\t\tMessageCarbonsStateChangedEvent event = new MessageCarbonsStateChangedEvent(\n\t\t\t\t\tpacket.getStanzaFrom().copyWithoutResource(), packet.getStanzaFrom(), session);\n\n\t\t\tfor (XMPPResourceConnection res : session.getActiveResources()) {\n\t\t\t\tif (res.isAuthorized()) {\n\t\t\t\t\tevent.add(res.getJID(), isEnabled(res));\n\t\t\t\t}\n\t\t\t}\n\t\t\teventBus.fire(event);\n\n\t\t} else if (type == StanzaType.unavailable) {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.log(Level.FINER, \"session = {0} removing resource = {1} from list of available resources\",\n\t\t\t\t\t\tnew Object[]{session, packet.getStanzaFrom()});\n\t\t\t}\n\t\t\tresources.remove(packet.getStanzaFrom());\n\t\t}\n\n\t}\n\n\tprotected boolean shouldSendCarbons(Packet packet, XMPPResourceConnection session) {\n\t\tif (packet.getElemChild(\"private\", \"urn:xmpp:carbons:2\") != null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (packet.getElemChild(\"no-copy\", \"urn:xmpp:hints\") != null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (packet.getType() == StanzaType.chat) {\n\t\t\tJID to = packet.getStanzaTo();\n\t\t\tif (to != null && packet.getElemChild(\"x\", \"http://jabber.org/protocol/muc#user\") != null && session.isAuthorized()) {\n\t\t\t\ttry {\n\t\t\t\t\tif (session.isUserId(to.getBareJID())) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t} catch (NotAuthorizedException ex) {\n\t\t\t\t\t// we can ignore this..\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\tif (packet.getType() == null || packet.getType() == StanzaType.normal) {\n\t\t\tfor (ElementMatcher matcher : msgCarbonPaths) {\n\t\t\t\tif (matcher.matches(packet)) {\n\t\t\t\t\treturn matcher.getValue();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Add/Remove session JID to set of JIDs with enabled carbon copy protocol\n\t */\n\tprivate void setEnabled(XMPPResourceConnection session, boolean value) throws NotAuthorizedException {\n\t\tsetEnabled(session, session.getJID(), value);\n\t}\n\n\tprivate void setEnabled(XMPPResourceConnection session, JID boundJID, boolean value) throws NotAuthorizedException {\n\t\tsession.putSessionData(ENABLED_KEY, value);\n\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.log(Level.FINER, \"session = {0}\" + \" enabling \" + XMLNS, session);\n\t\t}\n\n\t\tMessageCarbonsStateChangedEvent event = new MessageCarbonsStateChangedEvent(boundJID, session.getJID()\n\t\t\t\t.copyWithoutResource(), session.getParentSession());\n\t\tevent.add(boundJID, value);\n\t\teventBus.fire(event);\n\t}\n\n\t/**\n\t * Method returns true if\n\t *\n\t */\n\tprivate boolean isErrorDeliveringForkedMessage(Packet packet, XMPPResourceConnection session) {\n\n\t\tif (!session.isAuthorized() || packet.getStanzaTo() == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\t// check if it is error from delivering forked message\n\t\t\tif (packet.getType() == StanzaType.error && packet.getStanzaTo().getResource() == null &&\n\t\t\t\t\tsession.isUserId(packet.getStanzaTo().getBareJID())) {\n\n\t\t\t\tElement messageEl = packet.getElement();\n\n\t\t\t\t// it will be error if it contains sent element\n\t\t\t\tElement sentEl = messageEl.getChild(\"sent\", XMLNS);\n\t\t\t\tif (sentEl != null) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\t// it will also be error if it contains received element\n\t\t\t\tElement receivedEl = messageEl.getChild(\"received\", XMLNS);\n\t\t\t\tif (receivedEl != null) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (NotAuthorizedException ex) {\n\t\t\t// should not happens\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Event class responsible for notification other sessions of same user that message carbons state was changed.\n\t */\n\tpublic static class MessageCarbonsStateChangedEvent\n\t\t\textends UserSessionEvent {\n\n\t\tprivate HashSet<String> disabled = null;\n\t\tprivate HashSet<String> enabled = null;\n\n\t\t/**\n\t\t * Empty constructor to be able to serialize/deserialize event\n\t\t */\n\t\tpublic MessageCarbonsStateChangedEvent() {\n\t\t\tsuper();\n\t\t}\n\n\t\tpublic MessageCarbonsStateChangedEvent(JID sender, JID userJid, XMPPSession session) {\n\t\t\tsuper(sender, userJid, session);\n\t\t}\n\n\t\tpublic void add(JID jid, Boolean value) {\n\t\t\tif (value == null) {\n\t\t\t\tvalue = false;\n\t\t\t}\n\n\t\t\tHashSet<String> tmp = null;\n\t\t\tif (value) {\n\t\t\t\tif (enabled == null) {\n\t\t\t\t\tenabled = new HashSet<>();\n\t\t\t\t}\n\t\t\t\ttmp = enabled;\n\t\t\t} else {\n\t\t\t\tif (disabled == null) {\n\t\t\t\t\tdisabled = new HashSet<>();\n\t\t\t\t}\n\t\t\t\ttmp = disabled;\n\t\t\t}\n\t\t\ttmp.add(jid.getResource());\n\t\t}\n\n\t\tpublic Set<JID> getEnabledJids() {\n\t\t\tHashSet<JID> result = new HashSet<>();\n\t\t\tif (this.enabled != null) {\n\t\t\t\tfor (String res : this.enabled) {\n\t\t\t\t\tresult.add(JID.jidInstanceNS(getUserJid().getBareJID(), res));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\t\tpublic Set<JID> getDisabledJids() {\n\t\t\tHashSet<JID> result = new HashSet<>();\n\t\t\tif (this.disabled != null) {\n\t\t\t\tfor (String res : this.disabled) {\n\t\t\t\t\tresult.add(JID.jidInstanceNS(getUserJid().getBareJID(), res));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/MessageDeliveryLogic.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.PacketDefaultHandler;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.vhosts.VHostManagerIfc;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.function.Predicate;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Bean(name = \"message-delivery-logic\", parent = SessionManager.class, active = true, exportable = true)\npublic class MessageDeliveryLogic implements MessageDeliveryProviderIfc {\n\n\tprivate static final Logger log = Logger.getLogger(MessageDeliveryLogic.class.getCanonicalName());\n\n\tprotected static final String ELEM_NAME = tigase.server.Message.ELEM_NAME;\n\tprotected static final String XMLNS = \"jabber:client\";\n\tprivate static final String DELIVERY_RULES_KEY = \"delivery-rules\";\n\tprivate static final String SILENTLY_IGNORE_ERROR_KEY = \"silently-ignore-message\";\n\tpublic static Predicate<XMPPResourceConnection> VIABLE_FOR_MESSAGE_DELIVERY = (conn) -> conn.getPriority() >= 0;\n\t@ConfigField(desc = \"Message delivery rules\", alias = DELIVERY_RULES_KEY)\n\tprivate MessageDeliveryRules deliveryRules = MessageDeliveryRules.inteligent;\n\t@ConfigField(desc = \"Silently ignore errors\", alias = SILENTLY_IGNORE_ERROR_KEY)\n\tprivate boolean silentlyIgnoreError = false;\n\t@Inject(nullAllowed = true)\n\tprivate SessionManager.MessageArchive messageArchive;\n\t@Inject\n\tprivate VHostManagerIfc vHostManager;\n\n\tprivate PacketDefaultHandler packetDefaultHandler = new PacketDefaultHandler();\n\n\tpublic void handleDelivery(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\n\t\t// For performance reasons it is better to do the check\n\t\t// before calling logging method.\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Processing packet: {0}, for session: {1}\", new Object[]{packet, session});\n\t\t}\n\n\t\tif (packet.getElemName() == Iq.ELEM_NAME) {\n\t\t\tpacketDefaultHandler.process(packet, session, repo, results);\n\t\t\treturn;\n\t\t}\n\n\t\t// You may want to skip processing completely if the user is offline.\n\t\tif (session == null) {\n\t\t\tprocessOfflineUser(packet, results);\n\t\t\treturn;\n\t\t}    // end of if (session == null)\n\t\ttry {\n\n\t\t\t// Remember to cut the resource part off before comparing JIDs\n\t\t\tBareJID id = (packet.getStanzaTo() != null) ? packet.getStanzaTo().getBareJID() : null;\n\n\t\t\t// Checking if this is a packet TO the owner of the session\n\t\t\tif (session.isUserId(id)) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Message ''to'' this user, packet: {0}, for session: {1}\",\n\t\t\t\t\t\t\tnew Object[]{packet, session});\n\t\t\t\t}\n\n\t\t\t\tif (packet.getStanzaFrom() != null && session.isUserId(packet.getStanzaFrom().getBareJID())) {\n\t\t\t\t\tJID connectionId = session.getConnectionId();\n\t\t\t\t\tif (connectionId.equals(packet.getPacketFrom())) {\n\t\t\t\t\t\tPacket forward = packet.copyElementOnly();\n\t\t\t\t\t\tforward.setStableId(packet.getStableId());\n\t\t\t\t\t\tresults.offer(forward);\n\t\t\t\t\t\t// this would cause message packet to be stored in offline storage and will not\n\t\t\t\t\t\t// send recipient-unavailable error but it will behave the same as a message to\n\t\t\t\t\t\t// unavailable resources from other sessions or servers\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Yes this is message to 'this' client\n\t\t\t\tList<XMPPResourceConnection> conns = new ArrayList<XMPPResourceConnection>(5);\n\n\t\t\t\t// This is where and how we set the address of the component\n\t\t\t\t// which should rceive the result packet for the final delivery\n\t\t\t\t// to the end-user. In most cases this is a c2s or Bosh component\n\t\t\t\t// which keep the user connection.\n\t\t\t\tString resource = packet.getStanzaTo().getResource();\n\n\t\t\t\tif (resource == null) {\n\n\t\t\t\t\t// If the message is sent to BareJID then the message is delivered to\n\t\t\t\t\t// all resources\n\t\t\t\t\tconns.addAll(getConnectionsForMessageDelivery(session));\n\t\t\t\t} else {\n\n\t\t\t\t\t// Otherwise only to the given resource or sent back as error.\n\t\t\t\t\tXMPPResourceConnection con = session.getParentSession() == null\n\t\t\t\t\t\t\t\t\t\t\t\t ? null\n\t\t\t\t\t\t\t\t\t\t\t\t : session.getParentSession().getResourceForResource(resource);\n\n\t\t\t\t\tif (con != null) {\n\t\t\t\t\t\tconns.add(con);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// MessageCarbons: message cloned to all resources? why? it should be copied only\n\t\t\t\t// to resources with non negative priority!!\n\n\t\t\t\tif (conns.size() > 0) {\n\t\t\t\t\tfor (XMPPResourceConnection con : conns) {\n\t\t\t\t\t\tPacket result = packet.copyElementOnly();\n\n\t\t\t\t\t\tresult.setStableId(packet.getStableId());\n\t\t\t\t\t\tresult.setPacketTo(con.getConnectionId());\n\n\n\t\t\t\t\t\t// In most cases this might be skept, however if there is a\n\t\t\t\t\t\t// problem during packet delivery an error might be sent back\n\t\t\t\t\t\tresult.setPacketFrom(packet.getTo());\n\n\t\t\t\t\t\tif (messageArchive != null) {\n\t\t\t\t\t\t\tmessageArchive.addStableId(result, session);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Don't forget to add the packet to the results queue or it\n\t\t\t\t\t\t// will be lost.\n\t\t\t\t\t\tresults.offer(result);\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Delivering message, packet: {0}, to session: {1}\",\n\t\t\t\t\t\t\t\t\tnew Object[]{packet, con});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// if there are no user connections we should process packet\n\t\t\t\t\t// the same as with missing session (i.e. should be stored if\n\t\t\t\t\t// has type 'chat'\n\t\t\t\t\tprocessOfflineUser(packet, results);\n\t\t\t\t}\n\n\t\t\t\treturn;\n\t\t\t}    // end of else\n\n\t\t\t// Remember to cut the resource part off before comparing JIDs\n\t\t\tid = (packet.getStanzaFrom() != null) ? packet.getStanzaFrom().getBareJID() : null;\n\n\t\t\t// Checking if this is maybe packet FROM the client\n\t\t\tif (session.isUserId(id)) {\n\n\t\t\t\t// This is a packet FROM this client, the simplest action is\n\t\t\t\t// to forward it to is't destination:\n\t\t\t\t// Simple clone the XML element and....\n\t\t\t\t// ... putting it to results queue is enough\n\t\t\t\tresults.offer(packet.copyElementOnly());\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Can we really reach this place here?\n\t\t\t// Yes, some packets don't even have from or to address.\n\t\t\t// The best example is IQ packet which is usually a request to\n\t\t\t// the server for some data. Such packets may not have any addresses\n\t\t\t// And they usually require more complex processing\n\t\t\t// This is how you check whether this is a packet FROM the user\n\t\t\t// who is owner of the session:\n\t\t\tJID jid = packet.getFrom();\n\n\t\t\t// This test is in most cases equal to checking getElemFrom()\n\t\t\tif (session.getConnectionId().equals(jid)) {\n\n\t\t\t\t// Do some packet specific processing here, but we are dealing\n\t\t\t\t// with messages here which normally need just forwarding\n\t\t\t\tElement el_result = packet.getElement().clone();\n\n\t\t\t\t// If we are here it means FROM address was missing from the\n\t\t\t\t// packet, it is a place to set it here:\n\t\t\t\tel_result.setAttribute(\"from\", session.getJID().toString());\n\n\t\t\t\tPacket result = Packet.packetInstance(el_result, session.getJID(), packet.getStanzaTo());\n\n\t\t\t\t// ... putting it to results queue is enough\n\t\t\t\tresults.offer(result);\n\t\t\t}\n\t\t} catch (NotAuthorizedException e) {\n\t\t\tlog.log(Level.FINE, \"NotAuthorizedException for packet: \" + packet + \" for session: \" + session, e);\n\t\t\tresults.offer(\n\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(packet, \"You must authorize session first.\", true));\n\t\t}    // end of try-catch\n\t}\n\t\n\t/**\n\t * Method returns list of XMPPResourceConnections to which message should be delivered for session passes as\n\t * parameter if message was sent to bare JID\n\t */\n\tpublic List<XMPPResourceConnection> getConnectionsForMessageDelivery(XMPPResourceConnection session)\n\t\t\tthrows NotAuthorizedException {\n\t\tList<XMPPResourceConnection> conns = new ArrayList<XMPPResourceConnection>();\n\t\tfor (XMPPResourceConnection conn : session.getActiveSessions()) {\n\t\t\tif (VIABLE_FOR_MESSAGE_DELIVERY.test(conn)) {\n\t\t\t\tconns.add(conn);\n\t\t\t}\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Out of: {0} total connections, only: {1} have non-negative priority\",\n\t\t\t\t\tnew Object[]{session.getActiveSessions().size(), conns.size()});\n\t\t}\n\n\t\treturn conns;\n\t}\n\n\t/**\n\t * Method returns list of JIDs to which message should be delivered for session passes as parameter if message was\n\t * sent to bare JID\n\t */\n\tpublic Set<JID> getJIDsForMessageDelivery(XMPPResourceConnection session) throws NotAuthorizedException {\n\t\tSet<JID> jids = new HashSet<JID>();\n\t\tfor (XMPPResourceConnection conn : session.getActiveSessions()) {\n\t\t\tif (VIABLE_FOR_MESSAGE_DELIVERY.test(conn)) {\n\t\t\t\tjids.add(conn.getJID());\n\t\t\t}\n\t\t}\n\t\treturn jids;\n\t}\n\n\t/**\n\t * Method returns true if there is at least one XMPPResourceConnection which is allowed to receive message for\n\t * XMPPResourceConnection\n\t */\n\tpublic boolean hasConnectionForMessageDelivery(XMPPResourceConnection session) {\n\t\ttry {\n\t\t\tfor (XMPPResourceConnection conn : session.getActiveSessions()) {\n\t\t\t\tif (VIABLE_FOR_MESSAGE_DELIVERY.test(conn)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (NotAuthorizedException ex) {\n\t\t\t// should not happen, end even if it happend then we should return false\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic boolean preProcessFilter(Packet packet, XMPPResourceConnection session) {\n\t\tif (packet.getElemName() != Message.ELEM_NAME) {\n\t\t\treturn false;\n\t\t}\n\t\tfinal StanzaType type = packet.getType();\n\t\t// only handle messages within local domains; errors to external domains and components (i.e. mix, pubsub) will be delivered\n\t\tif (type == StanzaType.error && packet.getStanzaTo() != null && vHostManager.isLocalDomain(packet.getStanzaTo().getDomain())) {\n\t\t\tif (packet.getStanzaTo().getResource() == null) {\n\t\t\t\t// 1) https://xmpp.org/rfcs/rfc6121.html#rules-localpart-barejid\n\t\t\t\t//* 8.5.2.1.  Available or Connected Resources > 8.5.2.1.1.  Message\n\t\t\t\t//> For a message stanza of type \"error\", the server MUST silently ignore the message.\n\t\t\t\t//\n\t\t\t\t//* 8.5.2.2.  No Available or Connected Resources > 8.5.2.2.1.  Message\n\t\t\t\t//> For a message stanza of type \"headline\" or \"error\", the server MUST silently ignore the message.\n\t\t\t\t//\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\t//\n\t\t\t\t//* 8.5.3.2.  No Resource Matches > 8.5.3.2.1.  Message\n\t\t\t\t//> For a message stanza of type \"error\", the server MUST silently ignore the stanza.\n\t\t\t\tif (session != null && session.getjid() != null && session.getjid().equals(packet.getStanzaTo())) {\n\t\t\t\t\t// 2) https://xmpp.org/rfcs/rfc6121.html#rules-localpart-fulljid\n\t\t\t\t\t//* 8.5.3.1.  Resource Matches\n\t\t\t\t\t//> For a message stanza, the server MUST deliver the stanza to the resource.\n\t\t\t\t\treturn false;\n\t\t\t\t} else {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate void processOfflineUser(Packet packet, Queue<Packet> results) throws PacketErrorTypeException {\n\t\tif (packet.getStanzaTo() != null && packet.getStanzaTo().getResource() != null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Processing message to offline user, packet: {0}, deliveryRules: {1}\",\n\t\t\t\t\t\tnew Object[]{packet, deliveryRules});\n\t\t\t}\n\n\t\t\tif (deliveryRules != MessageDeliveryRules.strict) {\n\t\t\t\tStanzaType type = packet.getType();\n\t\t\t\tif (type == null) {\n\t\t\t\t\ttype = StanzaType.normal;\n\t\t\t\t}\n\t\t\t\tswitch (type) {\n\t\t\t\t\tcase chat:\n\t\t\t\t\t\t// try to deliver this message to all available resources so we should\n\t\t\t\t\t\t// treat it as a stanza with bare \"to\" attribute\n\t\t\t\t\t\tPacket result = packet.copyElementOnly();\n\t\t\t\t\t\tresult.setStableId(packet.getStableId());\n\t\t\t\t\t\tresult.initVars(packet.getStanzaFrom(), packet.getStanzaTo().copyWithoutResource());\n\t\t\t\t\t\tresults.offer(result);\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase error:\n\t\t\t\t\t\t// for error packet we should ignore stanza according to RFC 6121\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase headline:\n\t\t\t\t\tcase groupchat:\n\t\t\t\t\tcase normal:\n\t\t\t\t\tdefault:\n\t\t\t\t\t\t// for each of this types RFC 6121 recomends silent ignoring of stanza\n\t\t\t\t\t\t// or to return error recipient-unavailable - we will send error as\n\t\t\t\t\t\t// droping packet without response may not be a good idea\n\t\t\t\t\t\tif (!silentlyIgnoreError) {\n\t\t\t\t\t\t\tresults.offer(Authorization.RECIPIENT_UNAVAILABLE.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"The recipient is no longer available.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true));\n\t\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (!silentlyIgnoreError) {\n\t\t\t\t\tresults.offer(Authorization.RECIPIENT_UNAVAILABLE.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"The recipient is no longer available.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static enum MessageDeliveryRules {\n\t\tstrict,\n\t\tinteligent\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/MessageDeliveryProviderIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Set;\n\npublic interface MessageDeliveryProviderIfc {\n\n\tSet<JID> getJIDsForMessageDelivery(XMPPResourceConnection session) throws NotAuthorizedException;\n\n\tboolean hasConnectionForMessageDelivery(XMPPResourceConnection session);\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/MessageForwarding.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Message forwarder class. Forwards <code>Message</code> packet to it's destination address.\n * <br>\n * Created: Tue Feb 21 15:49:08 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n * @version $Rev: 2348 $\n */\n@Bean(name = MessageForwarding.ID, parent = SessionManager.class, active = false)\npublic class MessageForwarding\n\t\textends XMPPProcessor\n\t\timplements XMPPProcessorIfc {\n\n\tprotected static final String ID = \"message-vhost-forward\";\n\tprivate static final String FORWARD_EL = \"forward\";\n\tprivate static final String FORWARD_XMLNS = \"http://tigase.org/protocol/forward#v1\";\n\tprivate static final Logger log = Logger.getLogger(MessageForwarding.class.getName());\n\tprivate static final String XMLNS = \"jabber:client\";\n\tprivate static final String[][] ELEMENTS = {{tigase.server.Message.ELEM_NAME}};\n\tprivate static final String[] XMLNSS = {XMLNS};\n\tprivate static final String[] MESSAGE_FORWARD_PATH = {tigase.server.Message.ELEM_NAME, FORWARD_EL};\n\tprivate static final Element forw_el = new Element(FORWARD_EL, new String[]{\"xmlns\"}, new String[]{FORWARD_XMLNS});\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\n\t\t// For performance reasons it is better to do the check\n\t\t// before calling logging method.\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Processing packet: {0}\", packet);\n\t\t}\n\n\t\t// If the session is null (user offline), then we cannot forward as we\n\t\t// do not have access to user's configuration. The message should be\n\t\t// processed\n\t\t// once again when the user comes back online and his messages are read\n\t\t// from offline storage.\n\t\tif (session == null) {\n\t\t\treturn;\n\t\t}    // end of if (session == null)\n\n\t\t// We only want to forward messages which contain body element\n\t\tif (packet.getElemCDataStaticStr(tigase.server.Message.MESSAGE_BODY_PATH) == null) {\n\t\t\treturn;\n\t\t}\n\n\t\t// If the message contains forward payload already we drop it to prevent\n\t\t// infinite loop due to some misconfiguration\n\t\tif (packet.isXMLNSStaticStr(MESSAGE_FORWARD_PATH, FORWARD_XMLNS)) {\n\t\t\treturn;\n\t\t}\n\n\t\tJID forwardAddr = session.getDomain().getMessageForward();\n\n\t\t// No forward address means no forwarding which means no further processing\n\t\t// is required.\n\t\tif (forwardAddr == null) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tJID user = null;\n\n\t\t\t// Remember to cut the resource part off before comparing JIDs\n\t\t\tBareJID id = (packet.getStanzaTo() != null) ? packet.getStanzaTo().getBareJID() : null;\n\n\t\t\t// Checking if this is a packet TO the owner of the session\n\t\t\tif (session.isUserId(id)) {\n\n\t\t\t\t// Yes this is message to 'this' client\n\t\t\t\tuser = packet.getStanzaTo();\n\t\t\t} else {\n\n\t\t\t\t// Remember to cut the resource part off before comparing JIDs\n\t\t\t\tid = (packet.getStanzaFrom() != null) ? packet.getStanzaFrom().getBareJID() : null;\n\n\t\t\t\t// Checking if this is maybe packet FROM the client\n\t\t\t\tif (session.isUserId(id)) {\n\t\t\t\t\tuser = packet.getStanzaFrom();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Can we really reach this place here?\n\t\t\t// Yes, some packets don't even have from or to address.\n\t\t\t// The best example is IQ packet which is usually a request to\n\t\t\t// the server for some data. Such packets may not have any addresses\n\t\t\t// And they usually require more complex processing\n\t\t\t// This is how you check whether this is a packet FROM the user\n\t\t\t// who is owner of the session:\n\t\t\tif (user == null) {\n\t\t\t\tuser = session.getJID();\n\t\t\t}\n\n\t\t\tElement forward_payload = forw_el.clone();\n\n\t\t\tforward_payload.addAttribute(\"from\", packet.getStanzaFrom().toString());\n\t\t\tforward_payload.addAttribute(\"to\", packet.getStanzaTo().toString());\n\n\t\t\tPacket result = Packet.packetInstance(packet.getElement(), user, forwardAddr);\n\n\t\t\tresult.getElement().addChild(forward_payload);\n\t\t\tresults.offer(result);\n\t\t} catch (NotAuthorizedException e) {\n\t\t\tlog.warning(\"NotAuthorizedException for packet: \" + packet);\n\t\t\tresults.offer(\n\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(packet, \"You must authorize session first.\", true));\n\t\t}    // end of try-catch\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n}    // Message\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/MobileV1.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.Presence;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\n\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Class responsible for queuing packets (usable in connections from mobile clients - power usage optimization) version\n * 1\n *\n * @author andrzej\n */\n@Bean(name = MobileV1.ID, parent = SessionManager.class, active = false)\npublic class MobileV1\n\t\textends XMPPProcessor\n\t\timplements XMPPProcessorIfc, ClientStateIndication.Logic {\n\n\tprotected static final String ID = \"mobile_v1\";\n\t// default values\n\tprivate static final int DEF_MAX_QUEUE_SIZE_VAL = 50;\n\tprivate static final long DEF_MAX_TIMEOUT_VAL = 6 * 60 * 1000;\n\tprivate static final Logger log = Logger.getLogger(MobileV1.class.getCanonicalName());\n\tprivate static final String MAX_QUEUE_SIZE_KEY = \"max-queue-size\";\n\tprivate static final String MAX_TIMEOUT_KEY = \"max-timeout\";\n\tprivate static final String MOBILE_EL_NAME = \"mobile\";\n\tprivate static final String XMLNS = \"http://tigase.org/protocol/mobile#v1\";\n\tprivate static final String[][] ELEMENT_PATHS = {{Iq.ELEM_NAME, MOBILE_EL_NAME}};\n\tprivate static final String[] XMLNSS = {XMLNS};\n\tprivate static final String TIMEOUT_KEY = ID + \"-timeout\";\n\tprivate static final Element[] SUP_FEATURES = {\n\t\t\tnew Element(MOBILE_EL_NAME, new String[]{\"xmlns\"}, new String[]{XMLNS})};\n\tprivate static final String QUEUE_KEY = ID + \"-queue\";\n\n\t// keys\n\tprivate static final String LAST_TRANSFER_KEY = ID + \"-last-transfer\";\n\n\t@ConfigField(desc = \"Max queue size\", alias = MAX_QUEUE_SIZE_KEY)\n\tprivate int maxQueueSize = DEF_MAX_QUEUE_SIZE_VAL;\n\t@ConfigField(desc = \"Max timeout\", alias = MAX_TIMEOUT_KEY)\n\tprivate long maxTimeout = DEF_MAX_TIMEOUT_VAL;\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic void process(final Packet packet, final XMPPResourceConnection session, final NonAuthUserRepository repo,\n\t\t\t\t\t\tfinal Queue<Packet> results, final Map<String, Object> settings) {\n\t\tif (session == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (!session.isAuthorized()) {\n\t\t\ttry {\n\t\t\t\tresults.offer(\n\t\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(packet, \"Session is not yet authorized.\", false));\n\t\t\t} catch (PacketErrorTypeException ex) {\n\t\t\t\tlog.log(Level.FINEST, \"ignoring packet from not authorized session which is already of type error\");\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tStanzaType type = packet.getType();\n\n\t\t\tswitch (type) {\n\t\t\t\tcase set:\n\t\t\t\t\tElement el = packet.getElement().getChild(MOBILE_EL_NAME);\n\t\t\t\t\tString valueStr = el.getAttributeStaticStr(\"enable\");\n\n\t\t\t\t\t// if value is true queuing will be enabled\n\t\t\t\t\tboolean value = (valueStr != null) && (\"true\".equals(valueStr) || \"1\".equals(valueStr));\n\n\t\t\t\t\tif (el.getAttributeStaticStr(\"timeout\") != null) {\n\n\t\t\t\t\t\t// we got timeout so we should set it for this session\n\t\t\t\t\t\tlong timeout = Long.parseLong(el.getAttributeStaticStr(\"timeout\"));\n\n\t\t\t\t\t\tsetTimeout(session, timeout);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (value) {\n\t\t\t\t\t\tactivate(session, results);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdeactivate(session, results);\n\t\t\t\t\t}\n\n\t\t\t\t\tresults.offer(packet.okResult((Element) null, 0));\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tresults.offer(\n\t\t\t\t\t\t\tAuthorization.BAD_REQUEST.getResponseMessage(packet, \"Mobile processing type is incorrect\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t false));\n\t\t\t}\n\t\t} catch (PacketErrorTypeException ex) {\n\t\t\tLogger.getLogger(MobileV1.class.getName()).log(Level.SEVERE, null, ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENT_PATHS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\t@Override\n\tpublic Element[] supStreamFeatures(XMPPResourceConnection session) {\n\t\tif (session == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (!session.isAuthorized()) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn SUP_FEATURES;\n\t}\n\n\t@Override\n\tpublic void activate(XMPPResourceConnection session, Queue<Packet> results) {\n\t\tif (session.getSessionData(QUEUE_KEY) == null) {\n\t\t\tsession.putSessionDataIfAbsent(QUEUE_KEY, new LinkedBlockingQueue<Packet>());\n\t\t}\n\t\tsession.putSessionData(XMLNS, true);\n\t}\n\n\t@Override\n\tpublic void deactivate(XMPPResourceConnection session, Queue<Packet> results) {\n\t\tsession.putSessionData(XMLNS, false);\n\n\t\tflushQueue(session, results);\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void filter(Packet _packet, XMPPResourceConnection sessionFromSM, NonAuthUserRepository repo,\n\t\t\t\t\t   Queue<Packet> results) {\n\t\tif ((sessionFromSM == null) || !sessionFromSM.isAuthorized() || (results == null) || (results.size() == 0)) {\n\t\t\treturn;\n\t\t}\n\t\tfor (Iterator<Packet> it = results.iterator(); it.hasNext(); ) {\n\t\t\tPacket currentlyFilteredResultPacket = it.next();\n\n\t\t\t// check if packet contains destination\n\t\t\tif ((currentlyFilteredResultPacket == null) || (currentlyFilteredResultPacket.getPacketTo() == null)) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"packet without destination\");\n\t\t\t\t}\n\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// get parent session to look up for connection for destination\n\t\t\tXMPPSession parentSession = sessionFromSM.getParentSession();\n\t\t\tif (parentSession == null) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"no session for destination {0} for packet {1} - missing parent session\",\n\t\t\t\t\t\t\tnew Object[]{currentlyFilteredResultPacket.getPacketTo().toString(), currentlyFilteredResultPacket.toString()});\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// get resource connection for destination\n\t\t\tXMPPResourceConnection session = parentSession.getResourceForConnectionId(currentlyFilteredResultPacket.getPacketTo());\n\n\t\t\tif (session == null) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"no session for destination {0} for packet {1}\",\n\t\t\t\t\t\t\tnew Object[]{currentlyFilteredResultPacket.getPacketTo().toString(), currentlyFilteredResultPacket.toString()});\n\t\t\t\t}\n\n\t\t\t\t// if there is no session we should not queue\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// if queue is not enabled we do nothing\n\t\t\tif (!isQueueEnabled(session)) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"queue is no enabled\");\n\t\t\t\t}\n\n\t\t\t\tflushQueue(session, results);\n\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tQueue<Packet> queue = (Queue<Packet>) session.getSessionData(QUEUE_KEY);\n\n\t\t\t// lets check if packet should be queued\n\t\t\tif (shouldBeQueued(session, currentlyFilteredResultPacket, queue)) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"queuing packet = {0}\", currentlyFilteredResultPacket.toString());\n\t\t\t\t}\n\t\t\t\tqueue.offer(currentlyFilteredResultPacket);\n\t\t\t\tit.remove();\n\t\t\t\tif (queue.size() > maxQueueSize) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.finest(\"sending packets from queue (OVERFLOW)\");\n\t\t\t\t\t}\n\n\t\t\t\t\tPacket p;\n\n\t\t\t\t\twhile ((p = queue.poll()) != null) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t// setting destination for packet in case if\n\t\t\t\t\t\t\t// stream was resumed and connId changed\n\t\t\t\t\t\t\tp.setPacketTo(session.getConnectionId());\n\t\t\t\t\t\t\tresults.offer(p);\n\t\t\t\t\t\t} catch (NoConnectionIdException ex) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"should not happen, as connection is ready\", ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected boolean shouldBeQueued(XMPPResourceConnection session, Packet currentlyFilteredResultPacket, Queue<Packet> queue) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"checking if packet should be queued {0}\", currentlyFilteredResultPacket.toString());\n\t\t}\n\t\tif (currentlyFilteredResultPacket.getElemName() != Presence.ELEM_NAME) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"ignoring packet, packet is not presence:  {0}\", currentlyFilteredResultPacket.toString());\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\tStanzaType type = currentlyFilteredResultPacket.getType();\n\n\t\tif ((type != null) && (type != StanzaType.unavailable) && (type != StanzaType.available)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"queuing packet {0}\", currentlyFilteredResultPacket.toString());\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprotected void flushQueue(XMPPResourceConnection session, Queue<Packet> results) {\n\t\tQueue<Packet> queue = (Queue<Packet>) session.getSessionData(QUEUE_KEY);\n\n\t\tif ((queue != null) && !queue.isEmpty()) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"sending packets from queue (DISABLED)\");\n\t\t\t}\n\t\t\tPacket p;\n\t\t\twhile ((p = queue.poll()) != null) {\n\t\t\t\ttry {\n\t\t\t\t\t// setting destination for packet in case if\n\t\t\t\t\t// stream was resumed and connId changed\n\t\t\t\t\tp.setPacketTo(session.getConnectionId());\n\t\t\t\t\tresults.offer(p);\n\t\t\t\t} catch (NoConnectionIdException ex) {\n\t\t\t\t\tlog.log(Level.FINEST, \"should not happen, as connection is ready\", ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Check if queuing is enabled\n\t *\n\t */\n\tprotected boolean isQueueEnabled(XMPPResourceConnection session) {\n\t\tBoolean enabled = (Boolean) session.getSessionData(XMLNS);\n\n\t\treturn (enabled != null) && enabled;\n\t}\n\n\t/**\n\t * Check timeout for queue\n\t *\n\t */\n\tprotected boolean isTimedOut(XMPPResourceConnection session) {\n\t\tLong lastAccessTime = (Long) session.getSessionData(LAST_TRANSFER_KEY);\n\n\t\tif (lastAccessTime == null) {\n\t\t\treturn true;\n\t\t}\n\t\tif (lastAccessTime + getTimeout(session) < System.currentTimeMillis()) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Update last send time\n\t *\n\t */\n\tprotected void updateLastAccessTime(XMPPResourceConnection session) {\n\t\tsession.putSessionData(LAST_TRANSFER_KEY, System.currentTimeMillis());\n\t}\n\n\t/**\n\t * Get timeout used for session queue\n\t *\n\t */\n\tprivate long getTimeout(XMPPResourceConnection session) {\n\t\tLong timeout = (Long) session.getSessionData(TIMEOUT_KEY);\n\n\t\tif (timeout == null) {\n\t\t\treturn maxTimeout;\n\t\t}\n\n\t\treturn timeout;\n\t}\n\n\t/**\n\t * Set timeout for session queue\n\t *\n\t */\n\tprivate void setTimeout(XMPPResourceConnection session, long timeout) {\n\t\tif (timeout == 0) {\n\t\t\tsession.removeSessionData(TIMEOUT_KEY);\n\t\t} else {\n\t\t\tsession.putSessionData(TIMEOUT_KEY, timeout);\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/MobileV2.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Class responsible for queuing packets (usable in connections from mobile clients - power usage optimization) version\n * 2\n *\n * @author andrzej\n */\n@Bean(name = MobileV2.ID, parent = SessionManager.class, active = false)\npublic class MobileV2\n\t\textends XMPPProcessor\n\t\timplements XMPPProcessorIfc, XMPPPacketFilterIfc, ClientStateIndication.Logic {\n\n\tprotected static final String ID = \"mobile_v2\";\n\t// default values\n\tprivate static final int DEF_MAX_QUEUE_SIZE_VAL = 50;\n\tprivate static final Logger log = Logger.getLogger(MobileV2.class.getCanonicalName());\n\n\t// keys\n\tprivate static final String MAX_QUEUE_SIZE_KEY = \"max-queue-size\";\n\tprivate static final String MOBILE_EL_NAME = \"mobile\";\n\tprivate static final String XMLNS = \"http://tigase.org/protocol/mobile#v2\";\n\tprivate static final String[][] ELEMENT_PATHS = {{Iq.ELEM_NAME, MOBILE_EL_NAME}};\n\tprivate static final String[] XMLNSS = {XMLNS};\n\tprivate static final Element[] SUP_FEATURES = {\n\t\t\tnew Element(MOBILE_EL_NAME, new String[]{\"xmlns\"}, new String[]{XMLNS})};\n\tprivate static final String QUEUE_KEY = ID + \"-queue\";\n\n\t@ConfigField(desc = \"Max queue size\", alias = MAX_QUEUE_SIZE_KEY)\n\tprivate int maxQueueSize = DEF_MAX_QUEUE_SIZE_VAL;\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic void process(final Packet packet, final XMPPResourceConnection session, final NonAuthUserRepository repo,\n\t\t\t\t\t\tfinal Queue<Packet> results, final Map<String, Object> settings) {\n\t\tif (session == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (!session.isAuthorized()) {\n\t\t\ttry {\n\t\t\t\tresults.offer(\n\t\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(packet, \"Session is not yet authorized.\", false));\n\t\t\t} catch (PacketErrorTypeException ex) {\n\t\t\t\tlog.log(Level.FINEST, \"ignoring packet from not authorized session which is already of type error\");\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tStanzaType type = packet.getType();\n\n\t\t\tswitch (type) {\n\t\t\t\tcase set:\n\t\t\t\t\tElement el = packet.getElement().getChild(MOBILE_EL_NAME);\n\t\t\t\t\tString valueStr = el.getAttributeStaticStr(\"enable\");\n\n\t\t\t\t\t// if value is true queuing will be enabled\n\t\t\t\t\tboolean value = (valueStr != null) && (\"true\".equals(valueStr) || \"1\".equals(valueStr));\n\n\t\t\t\t\tif (value) {\n\t\t\t\t\t\tactivate(session, results);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdeactivate(session, results);\n\t\t\t\t\t}\n\n\t\t\t\t\tresults.offer(packet.okResult((Element) null, 0));\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tresults.offer(\n\t\t\t\t\t\t\tAuthorization.BAD_REQUEST.getResponseMessage(packet, \"Mobile processing type is incorrect\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t false));\n\t\t\t}\n\t\t} catch (PacketErrorTypeException ex) {\n\t\t\tLogger.getLogger(MobileV2.class.getName()).log(Level.SEVERE, null, ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENT_PATHS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\t@Override\n\tpublic Element[] supStreamFeatures(XMPPResourceConnection session) {\n\t\tif (session == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (!session.isAuthorized()) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn SUP_FEATURES;\n\t}\n\n\t@Override\n\tpublic void activate(XMPPResourceConnection session, Queue<Packet> results) {\n\t\tif (session.getSessionData(QUEUE_KEY) == null) {\n\n\t\t\t// session.putSessionData(QUEUE_KEY, new\n\t\t\t// LinkedBlockingQueue<Packet>());\n\t\t\tsession.putSessionData(QUEUE_KEY, new ConcurrentHashMap<JID, Packet>());\n\t\t}\n\t\tsession.putSessionData(XMLNS, true);\n\t}\n\n\t@Override\n\tpublic void deactivate(XMPPResourceConnection session, Queue<Packet> results) {\n\t\tif (session.getSessionData(QUEUE_KEY) == null) {\n\n\t\t\t// session.putSessionData(QUEUE_KEY, new\n\t\t\t// LinkedBlockingQueue<Packet>());\n\t\t\tsession.putSessionData(QUEUE_KEY, new ConcurrentHashMap<JID, Packet>());\n\t\t}\n\t\tsession.putSessionData(XMLNS, false);\n\n\t\tflushQueue(session, results);\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void filter(Packet _packet, XMPPResourceConnection sessionFromSM, NonAuthUserRepository repo,\n\t\t\t\t\t   Queue<Packet> results) {\n\t\tif ((sessionFromSM == null) || !sessionFromSM.isAuthorized() || (results == null) || (results.size() == 0)) {\n\t\t\treturn;\n\t\t}\n\t\tfor (Iterator<Packet> it = results.iterator(); it.hasNext(); ) {\n\t\t\tPacket currentlyFilteredResultPacket = it.next();\n\n\t\t\t// check if packet contains destination\n\t\t\tif ((currentlyFilteredResultPacket == null) || (currentlyFilteredResultPacket.getPacketTo() == null)) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"packet without destination\");\n\t\t\t\t}\n\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// get parent session to look up for connection for destination\n\t\t\tXMPPSession parentSession = sessionFromSM.getParentSession();\n\t\t\tif (parentSession == null) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"no session for destination {0} for packet {1} - missing parent session\",\n\t\t\t\t\t\t\tnew Object[]{currentlyFilteredResultPacket.getPacketTo().toString(), currentlyFilteredResultPacket.toString()});\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// get resource connection for destination\n\t\t\tXMPPResourceConnection session = parentSession.getResourceForConnectionId(currentlyFilteredResultPacket.getPacketTo());\n\t\t\tif (session == null) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"no session for destination {0} for packet {1}\",\n\t\t\t\t\t\t\tnew Object[]{currentlyFilteredResultPacket.getPacketTo().toString(), currentlyFilteredResultPacket.toString()});\n\t\t\t\t}\n\n\t\t\t\t// if there is no session we should not queue\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// if queue is not enabled we do nothing\n\t\t\tif (!isQueueEnabled(session)) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"queue is no enabled\");\n\t\t\t\t}\n\t\t\t\tflushQueue(session, results);\n\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tMap<JID, Packet> queue = (Map<JID, Packet>) session.getSessionData(QUEUE_KEY);\n\n\t\t\t// lets check if packet should be queued\n\t\t\tif (shouldBeQueued(session, currentlyFilteredResultPacket, queue)) {\n\t\t\t\tqueue.put(currentlyFilteredResultPacket.getStanzaFrom(), currentlyFilteredResultPacket);\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"queuing packet = {0}\", currentlyFilteredResultPacket.toString());\n\t\t\t\t}\n\t\t\t\tit.remove();\n\t\t\t\tif (queue.size() > maxQueueSize) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.finest(\"sending packets from queue (OVERFLOW)\");\n\t\t\t\t\t}\n\t\t\t\t\tfor (Packet queuePacket : queue.values()) {\n\t\t\t\t\t\tresults.offer(queuePacket);\n\t\t\t\t\t}\n\t\t\t\t\tqueue.clear();\n\n\t\t\t\t\t// we are sending all items from map so there is no need\n\t\t\t\t\t// to filter it (fix for ConcurrentModificationException)\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected boolean shouldBeQueued(XMPPResourceConnection session, Packet currentlyFilteredResultPacket, Map<JID, Packet> queue) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"checking if packet should be queued {0}\", currentlyFilteredResultPacket.toString());\n\t\t}\n\t\tif (currentlyFilteredResultPacket.getElemName() != \"presence\") {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"ignoring packet, packet is not presence:  {0}\", currentlyFilteredResultPacket.toString());\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\tStanzaType type = currentlyFilteredResultPacket.getType();\n\n\t\tif ((type != null) && (type != StanzaType.unavailable) && (type != StanzaType.available)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"queuing packet {0}\", currentlyFilteredResultPacket.toString());\n\t\t}\n\t\treturn true;\n\t}\n\n\tprotected void flushQueue(XMPPResourceConnection session, Queue<Packet> results) {\n\t\tMap<JID, Packet> queue = (Map<JID, Packet>) session.getSessionData(QUEUE_KEY);\n\n\t\tif ((queue != null) && !queue.isEmpty()) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"sending packets from queue (DISABLED)\");\n\t\t\t}\n\t\t\tfor (Packet p : queue.values()) {\n\t\t\t\ttry {\n\t\t\t\t\t// setting destination for packet in case if\n\t\t\t\t\t// stream was resumed and connId changed\n\t\t\t\t\tp.setPacketTo(session.getConnectionId());\n\t\t\t\t\tresults.offer(p);\n\t\t\t\t} catch (NoConnectionIdException ex) {\n\t\t\t\t\tlog.log(Level.FINEST, \"should not happen, as connection is ready\", ex);\n\t\t\t\t}\n\t\t\t}\n\t\t\tqueue.clear();\n\t\t}\n\t}\n\n\t/**\n\t * Check if queuing is enabled\n\t *\n\t */\n\tprotected boolean isQueueEnabled(XMPPResourceConnection session) {\n\t\tBoolean enabled = (Boolean) session.getSessionData(XMLNS);\n\n\t\treturn (enabled != null) && enabled;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/MobileV3.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.annotation.*;\nimport tigase.xmpp.jid.JID;\n\nimport java.text.SimpleDateFormat;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.xmpp.impl.MobileV3.*;\n\n/**\n * Class responsible for queuing packets (usable in connections from mobile clients - power usage optimization) version\n * 3\n *\n * @author andrzej\n */\n@Id(ID)\n@Handles({@Handle(path = {Iq.ELEM_NAME, MOBILE_EL_NAME}, xmlns = XMLNS)})\n@StreamFeatures({@StreamFeature(elem = MOBILE_EL_NAME, xmlns = XMLNS)})\n@Bean(name = MobileV3.ID, parent = SessionManager.class, active = false)\npublic class MobileV3\n\t\textends AnnotatedXMPPProcessor\n\t\timplements XMPPProcessorIfc, XMPPPacketFilterIfc {\n\n\tprotected static final String ID = \"mobile_v3\";\n\tprotected static final String MOBILE_EL_NAME = \"mobile\";\n\tprotected static final String XMLNS = \"http://tigase.org/protocol/mobile#v3\";\n\t// default values\n\tprivate static final int DEF_MAX_QUEUE_SIZE_VAL = 50;\n\tprivate static final Logger log = Logger.getLogger(MobileV3.class.getCanonicalName());\n\t// keys\n\tprivate static final String MAX_QUEUE_SIZE_KEY = \"max-queue-size\";\n\tprivate static final String PRESENCE_QUEUE_KEY = ID + \"-presence-queue\";\n\tprivate static final String PACKET_QUEUE_KEY = ID + \"-packet-queue\";\n\tprivate static final String DELAY_ELEM_NAME = \"delay\";\n\tprivate static final String DELAY_XMLNS = \"urn:xmpp:delay\";\n\tprivate static final String MESSAGE_ELEM_NAME = \"message\";\n\t//private static final ThreadLocal<Queue> prependResultsThreadQueue = new ThreadLocal<Queue>();\n\tprivate static final ThreadLocal<StateHolder> threadState = new ThreadLocal<StateHolder>();\n\tprivate SimpleDateFormat formatter;\n\n\t@ConfigField(desc = \"Max queue size\", alias = MAX_QUEUE_SIZE_KEY)\n\tprivate int maxQueueSize = DEF_MAX_QUEUE_SIZE_VAL;\n\n\t{\n\t\tthis.formatter = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'\");\n\t\tthis.formatter.setTimeZone(TimeZone.getTimeZone(\"UTC\"));\n\t}\n\n\t/**\n\t * Check if queuing is enabled\n\t *\n\t */\n\tprotected static boolean isQueueEnabled(XMPPResourceConnection session) {\n\t\tBoolean enabled = (Boolean) session.getSessionData(XMLNS);\n\n\t\treturn (enabled != null) && enabled;\n\t}\n\n\t@Override\n\tpublic void process(final Packet packet, final XMPPResourceConnection session, final NonAuthUserRepository repo,\n\t\t\t\t\t\tfinal Queue<Packet> results, final Map<String, Object> settings) {\n\t\tif (session == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (!session.isAuthorized()) {\n\t\t\ttry {\n\t\t\t\tresults.offer(\n\t\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(packet, \"Session is not yet authorized.\", false));\n\t\t\t} catch (PacketErrorTypeException ex) {\n\t\t\t\tlog.log(Level.FINEST, \"ignoring packet from not authorized session which is already of type error\");\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tStanzaType type = packet.getType();\n\n\t\t\tswitch (type) {\n\t\t\t\tcase set:\n\t\t\t\t\tElement el = packet.getElement().getChild(MOBILE_EL_NAME);\n\t\t\t\t\tString valueStr = el.getAttributeStaticStr(\"enable\");\n\n\t\t\t\t\t// if value is true queuing will be enabled\n\t\t\t\t\tboolean value = (valueStr != null) && (\"true\".equals(valueStr) || \"1\".equals(valueStr));\n\n\t\t\t\t\tif (session.getSessionData(PRESENCE_QUEUE_KEY) == null) {\n\n\t\t\t\t\t\t// session.putSessionData(QUEUE_KEY, new\n\t\t\t\t\t\t// LinkedBlockingQueue<Packet>());\n\t\t\t\t\t\tsession.putSessionData(PRESENCE_QUEUE_KEY, new ConcurrentHashMap<JID, Packet>());\n\t\t\t\t\t}\n\t\t\t\t\tif (session.getSessionData(PACKET_QUEUE_KEY) == null) {\n\t\t\t\t\t\tsession.putSessionData(PACKET_QUEUE_KEY, new ArrayDeque<Packet>());\n\t\t\t\t\t}\n\t\t\t\t\tsession.putSessionData(XMLNS, value);\n\t\t\t\t\tresults.offer(packet.okResult((Element) null, 0));\n\n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tresults.offer(\n\t\t\t\t\t\t\tAuthorization.BAD_REQUEST.getResponseMessage(packet, \"Mobile processing type is incorrect\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t false));\n\t\t\t}\n\t\t} catch (PacketErrorTypeException ex) {\n\t\t\tLogger.getLogger(MobileV3.class.getName()).log(Level.SEVERE, null, ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Element[] supStreamFeatures(XMPPResourceConnection session) {\n\t\tif (session == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (!session.isAuthorized()) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn super.supStreamFeatures(session);\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void filter(Packet _packet, XMPPResourceConnection sessionFromSM, NonAuthUserRepository repo,\n\t\t\t\t\t   Queue<Packet> results) {\n\t\tif ((sessionFromSM == null) || !sessionFromSM.isAuthorized() || (results == null) || (results.size() == 0)) {\n\t\t\treturn;\n\t\t}\n\n\t\tStateHolder holder = threadState.get();\n\t\tif (holder == null) {\n\t\t\tholder = new StateHolder();\n\t\t\tthreadState.set(holder);\n\t\t}\n\n\t\tfor (Iterator<Packet> it = results.iterator(); it.hasNext(); ) {\n\t\t\tPacket res = it.next();\n\n\t\t\t// check if packet contains destination\n\t\t\tif ((res == null) || (res.getPacketTo() == null)) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"packet without destination\");\n\t\t\t\t}\n\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// get parent session to look up for connection for destination\n\t\t\tXMPPSession parentSession = sessionFromSM.getParentSession();\n\t\t\tif (parentSession == null) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"no session for destination {0} for packet {1} - missing parent session\",\n\t\t\t\t\t\t\tnew Object[]{res.getPacketTo().toString(), res.toString()});\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// get resource connection for destination\n\t\t\tXMPPResourceConnection session = parentSession.getResourceForConnectionId(res.getPacketTo());\n\n\t\t\tif (session == null) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"no session for destination {0} for packet {1}\",\n\t\t\t\t\t\t\tnew Object[]{res.getPacketTo().toString(), res.toString()});\n\t\t\t\t}\n\n\t\t\t\t// if there is no session we should not queue\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tMap<JID, Packet> presenceQueue = (Map<JID, Packet>) session.getSessionData(PRESENCE_QUEUE_KEY);\n\t\t\tQueue<Packet> packetQueue = (Queue<Packet>) session.getSessionData(PACKET_QUEUE_KEY);\n\n\t\t\t//QueueState state = QueueState.need_flush;\n\t\t\tif (!isQueueEnabled(session)) {\n\t\t\t\tif ((presenceQueue == null && packetQueue == null) ||\n\t\t\t\t\t\t(presenceQueue.isEmpty() && packetQueue.isEmpty())) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"mobile queues needs flushing - presences: {0}, packets: {1}\",\n\t\t\t\t\t\t\tnew Object[]{presenceQueue.size(), packetQueue.size()});\n\t\t\t\t}\n\t\t\t\tholder.setState(res.getPacketTo(), QueueState.need_flush);\n\t\t\t} else {\n\t\t\t\tQueueState state = filter(session, res, holder.getState(res.getPacketTo()), presenceQueue, packetQueue);\n\t\t\t\tif (state == QueueState.queued) {\n\t\t\t\t\tit.remove();\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"queue packet = {0}\", res.toString());\n\t\t\t\t\t}\n\t\t\t\t\tif (presenceQueue.size() > maxQueueSize) {\n\t\t\t\t\t\tstate = QueueState.need_flush;\n\t\t\t\t\t} else if (packetQueue.size() > maxQueueSize) {\n\t\t\t\t\t\tstate = QueueState.need_packet_flush;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tstate = holder.setState(res.getPacketTo(), state);\n\t\t\t}\n\n//\t\t\tswitch (state) {\n//\t\t\t\tcase need_flush:\n//\t\t\t\t\tprependResults = prependResultsThreadQueue.get();\n//\t\t\t\t\tif (prependResults == null) {\n//\t\t\t\t\t\tprependResults = new ArrayDeque<Packet>();\n//\t\t\t\t\t\tprependResultsThreadQueue.set(prependResults);\n//\t\t\t\t\t}\n//\t\t\t\t\t\n//\t\t\t\t\ttry {\n//\t\t\t\t\t\tsynchronized (presenceQueue) {\n//\t\t\t\t\t\t\tJID connId = session.getConnectionId();\n//\t\t\t\t\t\t\tfor (Packet p : presenceQueue.values()) {\n//\t\t\t\t\t\t\t\t// we need to set packet to again in case Stream\n//\t\t\t\t\t\t\t\t// Management resumption happend in meanwhile\n//\t\t\t\t\t\t\t\tp.setPacketTo(connId);\n//\t\t\t\t\t\t\t\tprependResults.offer(p);\n//\t\t\t\t\t\t\t}\n//\t\t\t\t\t\t\tpresenceQueue.clear();\n//\t\t\t\t\t\t}\n//\t\t\t\t\t}\n//\t\t\t\t\tcatch (NoConnectionIdException ex) {\n//\t\t\t\t\t\tlog.log(Level.SEVERE, \"this should not happen\", ex);\n//\t\t\t\t\t}\n//\t\t\t\t\t\n//\t\t\t\tcase need_packet_flush:\n//\t\t\t\t\tif (prependResults == null) {\n//\t\t\t\t\t\tprependResults = prependResultsThreadQueue.get();\n//\t\t\t\t\t\tif (prependResults == null) {\n//\t\t\t\t\t\t\tprependResults = new ArrayDeque<Packet>();\n//\t\t\t\t\t\t\tprependResultsThreadQueue.set(prependResults);\n//\t\t\t\t\t\t}\n//\t\t\t\t\t}\n//\t\t\t\t\ttry {\n//\t\t\t\t\t\tsynchronized (packetQueue) {\n//\t\t\t\t\t\t\tJID connId = session.getConnectionId();\n//\t\t\t\t\t\t\tPacket p = null;\n//\t\t\t\t\t\t\twhile ((p = packetQueue.poll()) != null) {\n//\t\t\t\t\t\t\t\t// we need to set packet to again in case Stream\n//\t\t\t\t\t\t\t\t// Management resumption happend in meanwhile\n//\t\t\t\t\t\t\t\tp.setPacketTo(connId);\n//\t\t\t\t\t\t\t\tprependResults.offer(p);\n//\t\t\t\t\t\t\t}\n//\t\t\t\t\t\t\tpacketQueue.clear();\n//\t\t\t\t\t\t}\n//\t\t\t\t\t}\n//\t\t\t\t\tcatch (NoConnectionIdException ex) {\n//\t\t\t\t\t\tlog.log(Level.SEVERE, \"this should not happen\", ex);\n//\t\t\t\t\t}\t\t\t\t\n//\t\t\t\tcase queued:\t\t\t\t\t\n//\t\t\t\t\tbreak;\n//\t\t\t\t\t\n//\t\t\t\tdefault:\n//\t\t\t\t\tbreak;\n//\t\t\t}\n\t\t}\n\n\t\tfor (Map.Entry<JID, QueueState> e : holder.states.entrySet()) {\n\t\t\tXMPPResourceConnection session = null;\n\t\t\tswitch (e.getValue()) {\n\t\t\t\tcase need_flush:\n\t\t\t\t\ttry {\n\t\t\t\t\t\tXMPPSession parentSession = sessionFromSM.getParentSession();\n\t\t\t\t\t\tsession = (parentSession == null) ? null : parentSession.getResourceForConnectionId(e.getKey());\n\t\t\t\t\t\tif (session != null) {\n\t\t\t\t\t\t\tMap<JID, Packet> presenceQueue = (Map<JID, Packet>) session.getSessionData(\n\t\t\t\t\t\t\t\t\tPRESENCE_QUEUE_KEY);\n\t\t\t\t\t\t\tsynchronized (presenceQueue) {\n\t\t\t\t\t\t\t\tJID connId = session.getConnectionId();\n\t\t\t\t\t\t\t\tfor (Packet p : presenceQueue.values()) {\n\t\t\t\t\t\t\t\t\t// we need to set packet to again in case Stream\n\t\t\t\t\t\t\t\t\t// Management resumption happend in meanwhile\n\t\t\t\t\t\t\t\t\tp.setPacketTo(connId);\n\t\t\t\t\t\t\t\t\tholder.queue.offer(p);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tpresenceQueue.clear();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (NoConnectionIdException ex) {\n\t\t\t\t\t\tlog.log(Level.SEVERE, \"this should not happen\", ex);\n\t\t\t\t\t}\n\t\t\t\tcase need_packet_flush:\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (session == null) {\n\t\t\t\t\t\t\tXMPPSession parentSession = sessionFromSM.getParentSession();\n\t\t\t\t\t\t\tsession = (parentSession == null)\n\t\t\t\t\t\t\t\t\t  ? null\n\t\t\t\t\t\t\t\t\t  : parentSession.getResourceForConnectionId(e.getKey());\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (session != null) {\n\t\t\t\t\t\t\tQueue<Packet> packetQueue = (Queue<Packet>) session.getSessionData(PACKET_QUEUE_KEY);\n\t\t\t\t\t\t\tsynchronized (packetQueue) {\n\t\t\t\t\t\t\t\tJID connId = session.getConnectionId();\n\t\t\t\t\t\t\t\tPacket p = null;\n\t\t\t\t\t\t\t\twhile ((p = packetQueue.poll()) != null) {\n\t\t\t\t\t\t\t\t\t// we need to set packet to again in case Stream\n\t\t\t\t\t\t\t\t\t// Management resumption happend in meanwhile\n\t\t\t\t\t\t\t\t\tp.setPacketTo(connId);\n\t\t\t\t\t\t\t\t\tholder.queue.offer(p);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tpacketQueue.clear();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (NoConnectionIdException ex) {\n\t\t\t\t\t\tlog.log(Level.SEVERE, \"this should not happen\", ex);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase queued:\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (!holder.queue.isEmpty()) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"sending queued packets = {0}\", holder.queue.size());\n\t\t\t}\n\t\t\tholder.queue.addAll(results);\n\t\t\tresults.clear();\n\t\t\tresults.addAll(holder.queue);\n\t\t\t//holder.queue.clear();\n\t\t}\n\t\tholder.reset();\n\t}\n\n\tprivate QueueState filter(XMPPResourceConnection session, Packet res, QueueState state,\n\t\t\t\t\t\t\t  Map<JID, Packet> presenceQueue, Queue<Packet> packetQueue) {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"checking if packet should be queued {0}\", res.toString());\n\t\t}\n\n\t\tif (state == QueueState.need_flush) {\n\t\t\treturn state;\n\t\t}\n\n\t\tif (res.getElemName() == MESSAGE_ELEM_NAME) {\n\t\t\tif (state.value() > QueueState.queued.value()) {\n\t\t\t\treturn state;\n\t\t\t}\n\n\t\t\tList<Element> children = res.getElement().getChildren();\n\t\t\tif (children != null) {\n\t\t\t\tfor (Element child : children) {\n\t\t\t\t\tif (MessageCarbons.XMLNS.equals(child.getXMLNS())) {\n\t\t\t\t\t\tElement delay = res.getElement().getChild(DELAY_ELEM_NAME, DELAY_XMLNS);\n\t\t\t\t\t\tif (delay == null) {\n\t\t\t\t\t\t\tdelay = createDelayElem(session);\n\t\t\t\t\t\t\tif (delay != null) {\n\t\t\t\t\t\t\t\tElement forward = child.getChild(\"forward\", \"urn:xmpp:forward:0\");\n\t\t\t\t\t\t\t\tif (forward != null) {\n\t\t\t\t\t\t\t\t\tElement msg = forward.getChild(MESSAGE_ELEM_NAME);\n\t\t\t\t\t\t\t\t\tif (msg != null) {\n\t\t\t\t\t\t\t\t\t\tmsg.addChild(delay);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsynchronized (packetQueue) {\n\t\t\t\t\t\t\tpacketQueue.offer(res);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn QueueState.queued;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn QueueState.need_packet_flush;\n\t\t}\n\n\t\tif (res.getElemName() != \"presence\") {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"ignoring packet, packet is not presence:  {0}\", res.toString());\n\t\t\t}\n\n\t\t\treturn QueueState.need_packet_flush;\n\t\t}\n\n\t\tStanzaType type = res.getType();\n\n\t\tif ((type != null) && (type != StanzaType.unavailable) && (type != StanzaType.available)) {\n\t\t\treturn QueueState.need_flush;\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"queuing packet {0}\", res.toString());\n\t\t}\n\t\tElement delay = res.getElement().getChild(DELAY_ELEM_NAME, DELAY_XMLNS);\n\t\tif (delay == null) {\n\t\t\tdelay = createDelayElem(session);\n\t\t\tif (delay != null) {\n\t\t\t\tres.getElement().addChild(delay);\n\t\t\t}\n\t\t}\n\t\tsynchronized (presenceQueue) {\n\t\t\tpresenceQueue.put(res.getStanzaFrom(), res);\n\t\t}\n\n\t\treturn QueueState.queued;\n\t}\n\n\tprivate Element createDelayElem(XMPPResourceConnection session) {\n\t\tString timestamp = null;\n\t\tsynchronized (formatter) {\n\t\t\ttimestamp = formatter.format(new Date());\n\t\t}\n\n\t\ttry {\n\t\t\treturn new Element(DELAY_ELEM_NAME, new String[]{\"xmlns\", \"from\", \"stamp\"},\n\t\t\t\t\t\t\t   new String[]{DELAY_XMLNS, session.getBareJID().getDomain(), timestamp});\n\t\t} catch (NotAuthorizedException ex) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate static enum QueueState {\n\t\tnone(0),\n\t\tqueued(1),\n\t\tneed_flush(3),\n\t\tneed_packet_flush(2);\n\n\t\tprivate final int value;\n\n\t\tQueueState(int value) {\n\t\t\tthis.value = value;\n\t\t}\n\n\t\tpublic int value() {\n\t\t\treturn value;\n\t\t}\n\t}\n\n\tprivate static class StateHolder {\n\n\t\tprivate final Queue<Packet> queue = new ArrayDeque<Packet>();\n\t\tprivate final Map<JID, QueueState> states = new HashMap<JID, QueueState>();\n\n\t\tprotected QueueState setState(JID jid, QueueState state) {\n\t\t\tQueueState oldState = getState(jid);\n\t\t\tif (oldState.value() < state.value()) {\n\t\t\t\tstates.put(jid, state);\n\t\t\t\treturn state;\n\t\t\t}\n\t\t\treturn oldState;\n\t\t}\n\n\t\tprotected QueueState getState(JID jid) {\n\t\t\tQueueState state = states.get(jid);\n\t\t\tif (state == null) {\n\t\t\t\tstate = QueueState.none;\n\t\t\t}\n\t\t\treturn state;\n\t\t}\n\n\t\tprotected void reset() {\n\t\t\tstates.clear();\n\t\t\tqueue.clear();\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/MotdProcessor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserNotFoundException;\nimport tigase.db.UserRepository;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.EventBusEvent;\nimport tigase.eventbus.HandleEvent;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.server.Message;\nimport tigase.server.Packet;\nimport tigase.server.Presence;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.XMPPException;\nimport tigase.xmpp.XMPPProcessorIfc;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.impl.annotation.AnnotatedXMPPProcessor;\nimport tigase.xmpp.impl.annotation.Handle;\nimport tigase.xmpp.impl.annotation.Handles;\nimport tigase.xmpp.impl.annotation.Id;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.io.Serializable;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.xmpp.impl.MotdProcessor.ID;\n\n/**\n * Created by andrzej on 10.12.2016.\n */\n@Id(ID)\n@Handles(@Handle(path = {Presence.ELEM_NAME}, xmlns = Presence.CLIENT_XMLNS))\n@Bean(name = ID, parent = SessionManager.class, active = false)\npublic class MotdProcessor\n\t\textends AnnotatedXMPPProcessor\n\t\timplements XMPPProcessorIfc, Initializable, UnregisterAware {\n\n\tprotected static final String ID = \"motd\";\n\tprivate static final Logger log = Logger.getLogger(MotdProcessor.class.getCanonicalName());\n\tprivate static final long HOURS_24 = 24 * 60 * 60 * 1000;\n\n\tprivate static final String[] PRESENCE_PRIORITY_PATH = {\"presence\", \"priority\"};\n\tprivate static final BareJID smJid = BareJID.bareJIDInstanceNS(\"sess-man\");\n\t@Inject\n\tprivate EventBus eventBus;\n\tprivate String motd = null;\n\tprivate Long motdTimestamp = null;\n\t@Inject\n\tprivate UserRepository userRepository;\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\teventBus.unregisterAll(this);\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\ttry {\n\t\t\tmotd = userRepository.getData(smJid, ID, \"message\");\n\t\t\tString stamp = userRepository.getData(smJid, ID, \"timestamp\");\n\t\t\tmotdTimestamp = stamp == null ? null : Long.parseLong(stamp);\n\t\t} catch (UserNotFoundException ex) {\n\t\t\tlog.log(Level.FINEST, \"MotD has never been set - nothing to load\");\n\t\t\ttry {\n\t\t\t\tuserRepository.addUser(smJid);\n\t\t\t} catch (TigaseDBException ex1) {\n\t\t\t\tlog.log(Level.WARNING, \"failed to create user '\" + smJid + \"' for SessionManager\", ex1);\n\t\t\t}\n\t\t} catch (TigaseDBException ex) {\n\t\t\tlog.log(Level.WARNING, \"failed to read current MOTD from user repository\", ex);\n\t\t}\n\t\teventBus.registerAll(this);\n\t}\n\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\n\t\tif (session == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (packet.getStanzaTo() != null) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (session.getSessionData(ID) != null) {\n\t\t\treturn;\n\t\t}\n\n\t\tString priority = packet.getElemCDataStaticStr(PRESENCE_PRIORITY_PATH);\n\t\tif (priority != null && Integer.parseInt(priority) < 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tsession.putSessionData(ID, ID);\n\n\t\tif (motd == null || motdTimestamp == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tlong deliveryTimestamp = getLastDeliveryTime(session);\n\t\tif (deliveryTimestamp > motdTimestamp && (deliveryTimestamp + HOURS_24) > System.currentTimeMillis()) {\n\t\t\treturn;\n\t\t}\n\n\t\tsetLastDeliveryTime(session);\n\n\t\tElement messageEl = new Element(\"message\");\n\t\tmessageEl.setXMLNS(Message.CLIENT_XMLNS);\n\t\tmessageEl.addChild(new Element(\"body\", motd));\n\n\t\tMessage message = new Message(messageEl, session.getDomainAsJID(), session.getJID());\n\t\tmessage.setPacketTo(session.getConnectionId());\n\n\t\tresults.add(message);\n\t}\n\n\t@HandleEvent\n\tpublic void onMotdChanged(MotdUpdatedEvent event) {\n\t\tthis.motd = event.getMessage();\n\t\tthis.motdTimestamp = event.getTimestmap();\n\t}\n\n\tpublic String getMotd() {\n\t\treturn motd;\n\t}\n\n\tpublic void setMotd(String motd) throws TigaseDBException {\n\t\tLong timestamp = motd == null ? null : System.currentTimeMillis();\n\t\tif (motd != null) {\n\t\t\tuserRepository.setData(smJid, ID, \"message\", motd);\n\t\t\tuserRepository.setData(smJid, ID, \"timestamp\", String.valueOf(timestamp));\n\t\t} else {\n\t\t\tuserRepository.removeData(smJid, ID, \"message\");\n\t\t\tuserRepository.removeData(smJid, ID, \"timestamp\");\n\t\t}\n\t\teventBus.fire(new MotdUpdatedEvent(motd, timestamp));\n\t}\n\n\tprivate long getLastDeliveryTime(XMPPResourceConnection session) {\n\t\ttry {\n\t\t\tString str = session.getData(ID, \"last-delivery\", null);\n\t\t\tif (str == null) {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\treturn Long.parseLong(str);\n\t\t} catch (NotAuthorizedException | TigaseDBException ex) {\n\t\t\tlog.log(Level.FINEST, session.toString() + \", could not retrieve last delivery timestamp\", ex);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprivate void setLastDeliveryTime(XMPPResourceConnection session) {\n\t\ttry {\n\t\t\tString stamp = String.valueOf(System.currentTimeMillis());\n\t\t\tsession.setData(ID, \"last-delivery\", stamp);\n\t\t} catch (NotAuthorizedException | TigaseDBException ex) {\n\t\t\tlog.log(Level.FINEST, session.toString() + \", could not update last delivery timestamp\", ex);\n\t\t}\n\t}\n\n\tpublic static class MotdUpdatedEvent\n\t\t\timplements Serializable, EventBusEvent {\n\n\t\tprivate String message;\n\t\tprivate Long timestamp;\n\n\t\tpublic MotdUpdatedEvent() {\n\t\t}\n\n\t\tpublic MotdUpdatedEvent(String msg, Long timestamp) {\n\t\t\tthis.message = msg;\n\t\t\tthis.timestamp = timestamp;\n\t\t}\n\n\t\tpublic String getMessage() {\n\t\t\treturn message;\n\t\t}\n\n\t\tpublic Long getTimestmap() {\n\t\t\treturn timestamp;\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/OfflineMessages.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.MsgRepositoryIfc;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserNotFoundException;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.osgi.ModulesManagerImpl;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.amp.db.MsgRepository;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.DomBuilderHandler;\nimport tigase.xml.Element;\nimport tigase.xml.SimpleParser;\nimport tigase.xml.SingletonFactory;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.JID;\n\nimport java.text.SimpleDateFormat;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.server.Message.ELEM_NAME;\n\n/**\n * OfflineMessages plugin implementation which follows <a href=\"http://xmpp.org/extensions/xep-0160.html\">XEP-0160: Best\n * Practices for Handling Offline Messages</a> specification. Responsible for storing messages send to offline users -\n * either as a standalone plugin or as a processor for other plugins (e.g. AMP). Is registered to handle packets of type\n * {@code <presence>}.\n * <br>\n * Created: Mon Oct 16 13:28:53 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\n@Bean(name = OfflineMessages.ID, parent = SessionManager.class, active = false)\npublic class OfflineMessages\n\t\textends XMPPProcessor\n\t\timplements XMPPPostprocessorIfc, XMPPProcessorIfc {\n\n\t/**\n\t * Field holds an array for element paths for which the plugin offers message saving capabilities. In case of {@code\n\t * msgoffline} plugin it is <em>presence</em> stanza\n\t */\n\tpublic static final String[] MESSAGE_EVENT_PATH = {ELEM_NAME, \"event\"};\n\t/**\n\t * Field holds an array for element paths for which the plugin offers processing capabilities. In case of {@code\n\t * msgoffline} plugin it is <em>presence</em> stanza\n\t */\n\tpublic static final String[] MESSAGE_HEADER_PATH = {ELEM_NAME, \"header\"};\n\tpublic static final String[] MESSAGE_HINTS_NO_STORE = {ELEM_NAME, \"no-store\"};\n\tpublic static final String[] MESSAGE_HINTS_STORE = {ELEM_NAME, \"store\"};\n\tpublic static final String MESSAGE_HINTS_XMLNS = \"urn:xmpp:hints\";\n\tpublic static final String[] MESSAGE_RECEIVED_PATH = {ELEM_NAME, \"received\"};\n\tpublic static final String MESSAGE_RECEIVED_XMLNS = \"urn:xmpp:receipts\";\n\tpublic static final String[] PUBSUB_NODE_PATH = {Message.ELEM_NAME, \"event\", \"items\"};\n\tpublic static final String PUBSUB_NODE_KEY = \"node\";\n\t/**\n\t * Field holds default client namespace for stanzas. In case of {@code msgoffline} plugin it is\n\t * <em>jabber:client</em>\n\t */\n\tprotected static final String XMLNS = \"jabber:client\";\n\t/**\n\t * Field holds identification string for the plugin. In case of {@code msgoffline} plugin it is <em>msgoffline</em>\n\t */\n\tprotected static final String ID = \"msgoffline\";\n\tprivate static final Logger log = Logger.getLogger(OfflineMessages.class.getName());\n\t/**\n\t * Field holds an array for element paths for which the plugin offers processing capabilities. In case of {@code\n\t * msgoffline} plugin it is <em>presence</em> stanza\n\t */\n\tprivate static final String[][] ELEMENTS = {{PresenceAbstract.PRESENCE_ELEMENT_NAME}, {\"iq\", \"msgoffline\"}};\n\t/**\n\t * Field holds an array of name-spaces for stanzas which can be processed by this plugin. In case of {@code\n\t * msgoffline} plugin it is <em>jabber:client</em>\n\t */\n\tprivate static final String[] XMLNSS = {XMLNS, ID};\n\t/**\n\t * Field holds an array of XML Elements with service discovery features which have to be returned to the client\n\t * uppon request. In case of {@code msgoffline} plugin it is the same as plugin name - <em>msgoffline</em>\n\t */\n\tprivate static final Element[] DISCO_FEATURES = {\n\t\t\tnew Element(\"feature\", new String[]{\"var\"}, new String[]{\"msgoffline\"})};\n\tprivate static final String MSG_OFFLINE_STORAGE_PATHS = \"msg-store-offline-paths\";\n\tprivate static final String MSG_REPO_CLASS_KEY = \"msg-repo-class\";\n\tprivate static final String MSG_PUBSUB_JID = \"msg-pubsub-jid\";\n\tprivate static final String MSG_PUBSUB_NODE = \"msg-pubsub-node\";\n\tprivate static final String MSG_PUBSUB_PUBLISHER = \"msg-pubsub-publisher\";\n\t/** Field holds the default hostname of the machine. */\n\tprivate static String defHost = DNSResolverFactory.getInstance().getDefaultHost();\n\n\t/**\n\t * Field holds class for formatting and parsing dates in a locale-sensitive manner\n\t */\n\tprivate final SimpleDateFormat formatter;\n\t@Inject\n\tprivate MessageDeliveryLogic message;\n\t@Inject(nullAllowed = true)\n\tprivate SessionManager.MessageArchive messageArchive;\n\t@ConfigField(desc = \"Offline message implementation repository class\", alias = MSG_REPO_CLASS_KEY)\n\tprivate String msgRepoCls = null;\n\t@Inject(nullAllowed = true)\n\tprivate List<Notifier> notifiers;\n\t@ConfigField(desc = \"Store offline messages with mathing paths\", alias = MSG_OFFLINE_STORAGE_PATHS)\n\tprivate ElementMatcher[] offlineStorageMatchers = new ElementMatcher[0];\n\n\t{\n\t\tthis.formatter = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'\");\n\t\tthis.formatter.setTimeZone(TimeZone.getTimeZone(\"UTC\"));\n\t}\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t * <br>\n\t * OfflineMessages postprocessor simply calls {@code savePacketForOffLineUser} method to store packet to offline\n\t * repository.\n\t */\n\t@Override\n\tpublic void postProcess(final Packet packet, final XMPPResourceConnection conn, final NonAuthUserRepository repo,\n\t\t\t\t\t\t\tfinal Queue<Packet> queue, Map<String, Object> settings) {\n\t\tif (conn == null ||\n\t\t\t\t(packet.getElemName() == Message.ELEM_NAME && !message.hasConnectionForMessageDelivery(conn))) {\n\t\t\ttry {\n\t\t\t\tif (packet.getElemName() == tigase.server.Message.ELEM_NAME && packet.getStanzaTo() != null &&\n\t\t\t\t\t\tpacket.getStanzaTo().getResource() != null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (conn != null && packet.getStanzaTo() != null && !conn.isUserId(packet.getStanzaTo().getBareJID())) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tOfflineMsgRepositoryIfc msg_repo = getMsgRepoImpl(repo, conn);\n\n\t\t\t\tAuthorization saveResult = savePacketForOffLineUser(packet, msg_repo, repo);\n\t\t\t\tPacket result = null;\n\t\t\t\tnotifyNewOfflineMessage(packet, conn, queue, settings);\n\n\t\t\t\tswitch (saveResult) {\n\t\t\t\t\tcase SERVICE_UNAVAILABLE:\n\t\t\t\t\t\tresult = saveResult.getResponseMessage(packet, \"Offline messages queue is full\", true);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (result != null) {\n\t\t\t\t\tqueue.offer(result);\n\t\t\t\t}\n\t\t\t} catch (UserNotFoundException e) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"UserNotFoundException at trying to save packet for off-line user.\" + packet);\n\t\t\t\t}\n\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Could not save packet for offline user, returning error \" + packet, ex);\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tqueue.offer(Authorization.INTERNAL_SERVER_ERROR.getResponseMessage(packet, null, true));\n\t\t\t\t} catch (PacketErrorTypeException e) {\n\t\t\t\t\tlog.finest(\"Could not sent error for unsaved packet for offline user \" + packet);\n\t\t\t\t}\n\t\t\t} catch (NotAuthorizedException ex) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"NotAuthorizedException when checking if message is to this \" +\n\t\t\t\t\t\t\t\"user at trying to save packet for off-line user, {0}, {1}\", new Object[]{packet, conn});\n\t\t\t\t}\n\t\t\t} catch (PacketErrorTypeException ex) {\n\t\t\t\tlog.log(Level.FINE, \"Could not sent error to packet sent to offline user which storage to offline \" +\n\t\t\t\t\t\t\"store failed. Packet is error type already: {0}\", packet.toStringSecure());\n\t\t\t}    // end of try-catch\n\t\t}      // end of if (conn == null)\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t * <br>\n\t * {@code OfflineMessages} processor is triggered by {@code <presence>} stanza. Upon receiving it plugin tries to\n\t * load messages from repository and, if the result is not empty, sends them to the user\n\t */\n\t@Override\n\tpublic void process(final Packet packet, final XMPPResourceConnection conn, final NonAuthUserRepository repo,\n\t\t\t\t\t\tfinal Queue<Packet> results, final Map<String, Object> settings) throws NotAuthorizedException {\n\t\tswitch (packet.getElemName()) {\n\t\t\tcase tigase.server.Presence.ELEM_NAME:\n\t\t\t\tif (loadOfflineMessages(packet, conn)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tOfflineMsgRepositoryIfc msg_repo = getMsgRepoImpl(repo, conn);\n\t\t\t\t\t\tQueue<Packet> packets = restorePacketForOffLineUser(conn, msg_repo);\n\n\t\t\t\t\t\tif (packets != null) {\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\t\t\tlog.finer(\"Sending off-line messages: \" + packets.size());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tresults.addAll(packets);\n\n\t\t\t\t\t\t\tif (!packets.isEmpty()) {\n\t\t\t\t\t\t\t\tnotifyOfflineMessagesRetrieved(conn, results);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}    // end of if (packets != null)\n\t\t\t\t\t} catch (TigaseDBException e) {\n\t\t\t\t\t\tlog.log(Level.CONFIG, \"Something wrong, DB problem, cannot load offline messages. \" + e);\n\t\t\t\t\t}      // end of try-catch\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase Iq.ELEM_NAME:\n\t\t\t\tprocessIq(packet, conn, repo, results);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tpublic void processIq(Packet packet, XMPPResourceConnection conn, NonAuthUserRepository repo, Queue<Packet> results)\n\t\t\tthrows NotAuthorizedException {\n\t\ttry {\n\t\t\tif (conn != null && packet.getFrom().equals(conn.getConnectionId())) {\n\t\t\t\tElement msgoffline = packet.getElement().getChild(\"msgoffline\");\n\t\t\t\tString limitStr = null;\n\t\t\t\tswitch (packet.getType()) {\n\t\t\t\t\tcase set:\n\t\t\t\t\t\tlimitStr = msgoffline.getAttributeStaticStr(\"limit\");\n\t\t\t\t\t\tLong limit = null;\n\t\t\t\t\t\tif (limitStr != null) {\n\t\t\t\t\t\t\tif (\"none\".equals(limitStr) || \"false\".equals(limitStr)) {\n\t\t\t\t\t\t\t\tlimit = (long) -1;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tlimit = Long.parseLong(limitStr);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (limit == null) {\n\t\t\t\t\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"Value of limit attribute is incorrect\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   false));\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (limit >= 0) {\n\t\t\t\t\t\t\t\tconn.setPublicData(MsgRepository.OFFLINE_MSGS_KEY, MsgRepository.MSGS_STORE_LIMIT_KEY,\n\t\t\t\t\t\t\t\t\t\t\t\t   limitStr);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tconn.removePublicData(MsgRepository.OFFLINE_MSGS_KEY,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  MsgRepository.MSGS_STORE_LIMIT_KEY);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// following get case will return result for set as well\n\t\t\t\t\tcase get:\n\t\t\t\t\t\tif (limitStr == null) {\n\t\t\t\t\t\t\tlimitStr = conn.getPublicData(MsgRepository.OFFLINE_MSGS_KEY,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  MsgRepository.MSGS_STORE_LIMIT_KEY, null);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (limitStr == null) {\n\t\t\t\t\t\t\tlimitStr = \"false\";\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmsgoffline = new Element(\"msgoffline\", new String[]{\"xmlns\", \"limit\"},\n\t\t\t\t\t\t\t\t\t\t\t\t new String[]{\"msgoffline\", limitStr});\n\t\t\t\t\t\tresults.offer(packet.okResult(msgoffline, 0));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet, \"Request type is incorrect\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   false));\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tresults.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"You are not authorized to access this private storage.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  false));\n\t\t\t}\n\t\t} catch (TigaseDBException ex) {\n\t\t} catch (NoConnectionIdException ex) {\n\t\t} catch (PacketErrorTypeException ex) {\n\n\t\t}\n\t}\n\n\t/**\n\t * Method restores all messages from repository for the JID of the current session. All retrieved elements are then\n\t * instantiated as {@code Packet} objects added to {@code LinkedList} collection and, if possible, sorted by\n\t * timestamp.\n\t *\n\t * @param conn user session which keeps all the user session data and also gives an access to the user's repository\n\t * data.\n\t * @param repo an implementation of {@link MsgRepositoryIfc} interface\n\t *\n\t * @return a {@link Queue} of {@link Packet} objects based on all stored payloads for the JID of the current\n\t * session.\n\t *\n\t */\n\tpublic Queue<Packet> restorePacketForOffLineUser(XMPPResourceConnection conn,\n\t\t\t\t\t\t\t\t\t\t\t\t\t tigase.db.OfflineMsgRepositoryIfc repo)\n\t\t\tthrows UserNotFoundException, NotAuthorizedException, TigaseDBException {\n\t\tQueue<Element> elems = repo.loadMessagesToJID(conn, true);\n\n\t\tif (elems != null) {\n\t\t\tLinkedList<Packet> pacs = new LinkedList<Packet>();\n\t\t\tElement elem = null;\n\n\t\t\twhile ((elem = elems.poll()) != null) {\n\t\t\t\ttry {\n\t\t\t\t\tPacket p = Packet.packetInstance(elem);\n\t\t\t\t\tif (p.getElemName() == Iq.ELEM_NAME) {\n\t\t\t\t\t\tp.initVars(p.getStanzaFrom(), conn.getJID());\n\t\t\t\t\t}\n\t\t\t\t\tpacs.offer(p);\n\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\tlog.warning(\"Packet addressing problem, stringprep failed: \" + elem);\n\t\t\t\t}\n\t\t\t}    // end of while (elem = elems.poll() != null)\n\t\t\ttry {\n\t\t\t\tCollections.sort(pacs, new StampComparator());\n\t\t\t} catch (NullPointerException e) {\n\t\t\t\ttry {\n\t\t\t\t\tlog.warning(\"Can not sort off line messages: \" + pacs + \",\\n\" + e);\n\t\t\t\t} catch (Exception exc) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Can not print log message.\", exc);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn pacs;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Method stores messages to offline repository with the following rules applied, i.e. saves only: <ul> <li> message\n\t * stanza with either nonempty {@code <body>}, {@code <event>} or {@code <header>} child element and only messages\n\t * of type normal, chat.</li> <li> presence stanza of type subscribe, subscribed, unsubscribe and unsubscribed.</li>\n\t * </ul> <br> Processed messages are stamped with the {@code delay} element and appropriate timestamp. <br>\n\t *\n\t * @param packet a {@link Packet} object containing packet that should be verified and saved\n\t * @param repo a {@link MsgRepositoryIfc} repository handler responsible for storing messages\n\t *\n\t * @return {@code true} if the packet was correctly saved to repository, {@code false} otherwise.\n\t *\n\t */\n\tpublic Authorization savePacketForOffLineUser(Packet packet, tigase.db.OfflineMsgRepositoryIfc repo,\n\t\t\t\t\t\t\t\t\t\t\t\t  NonAuthUserRepository userRepo) throws UserNotFoundException, TigaseDBException {\n\t\tStanzaType type = packet.getType();\n\n\t\t// save only:\n\t\t// message stanza with either {@code <body>} or {@code <event>} child element and only of type normal, chat\n\t\t// presence stanza of type subscribe, subscribed, unsubscribe and unsubscribed\n\t\tif (isAllowedForOfflineStorage(packet)) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Storing packet for offline user: {0}\", packet);\n\t\t\t}\n\n\t\t\tPacket pac = packet.copyElementOnly();\n\t\t\tpac.setStableId(packet.getStableId());\n\t\t\tif (messageArchive != null) {\n\t\t\t\tmessageArchive.addStableId(pac, null);\n\t\t\t}\n\n\t\t\tC2SDeliveryErrorProcessor.filterErrorElement(pac.getElement());\n\n\t\t\tString stamp = null;\n\t\t\tsynchronized (formatter) {\n\t\t\t\tstamp = formatter.format(new Date());\n\t\t\t}\n\n\n\t\t\tString from = pac.getStanzaTo().getDomain();\n\t\t\tElement x = new Element(\"delay\", \"Offline Storage - \" + defHost, new String[]{\"from\", \"stamp\", \"xmlns\"},\n\t\t\t\t\t\t\t\t\tnew String[]{from, stamp, \"urn:xmpp:delay\"});\n\n\t\t\tpac.getElement().addChild(x);\n\t\t\tpac.processedBy(ID);\n\n\n\t\t\tif (repo.storeMessage(pac.getStanzaFrom(), pac.getStanzaTo(), null, pac.getElement(), userRepo)) {\n\t\t\t\treturn Authorization.AUTHORIZED;\n\t\t\t} else {\n\t\t\t\treturn Authorization.SERVICE_UNAVAILABLE;\n\t\t\t}\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Packet for offline user not suitable for storing: {0}\", packet);\n\t\t\t}\n\t\t}\n\n\t\treturn Authorization.FEATURE_NOT_IMPLEMENTED;\n\t}\n\n\tpublic String[] getOfflineStorageMatchers() {\n\t\tString[] result = new String[offlineStorageMatchers.length];\n\t\tfor (int i = 0; i < offlineStorageMatchers.length; i++) {\n\t\t\tresult[i] = offlineStorageMatchers[i].toString();\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic void setOfflineStorageMatchers(String[] matcherStrs) {\n\t\tList<ElementMatcher> matchers = new ArrayList<>();\n\t\tfor (String matcherStr : matcherStrs) {\n\t\t\tElementMatcher matcher = ElementMatcher.create(matcherStr);\n\t\t\tif (matcher != null) {\n\t\t\t\tmatchers.add(matcher);\n\t\t\t}\n\t\t}\n\t\tofflineStorageMatchers = matchers.toArray(new ElementMatcher[0]);\n\t}\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(final XMPPResourceConnection session) {\n\t\treturn DISCO_FEATURES;\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\t/**\n\t * Method allows obtaining instance of {@link MsgRepositoryIfc} interface implementation.\n\t *\n\t * @param conn user session which keeps all the user session data and also gives an access to the user's repository\n\t * data.\n\t * @param repo an implementation of {@link MsgRepositoryIfc} interface\n\t *\n\t * @return instance of {@link MsgRepositoryIfc} interface implementation.\n\t */\n\tprotected OfflineMsgRepositoryIfc getMsgRepoImpl(NonAuthUserRepository repo, XMPPResourceConnection conn) {\n\t\tif (msgRepoCls == null) {\n\t\t\treturn new MsgRepositoryImpl(repo, conn);\n\t\t} else {\n\t\t\ttry {\n\t\t\t\tOfflineMsgRepositoryIfc msgRepo = (OfflineMsgRepositoryIfc) ModulesManagerImpl.getInstance()\n\t\t\t\t\t\t.forName(msgRepoCls)\n\t\t\t\t\t\t.newInstance();\n\t\t\t\tmsgRepo.init(repo, conn);\n\t\t\t\treturn msgRepo;\n\t\t\t} catch (Exception ex) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Method determines whether packet sent to offline user should be stored in offline storage or not\n\t */\n\tprotected boolean isAllowedForOfflineStorage(Packet pac) {\n\t\t// custom element matchers override default values so let's check\n\t\t// this matchers at first\n\t\tfor (ElementMatcher matcher : offlineStorageMatchers) {\n\t\t\tif (matcher.matches(pac)) {\n\t\t\t\treturn matcher.getValue();\n\t\t\t}\n\t\t}\n\n\t\treturn isAllowedForOfflineStorageDefaults(pac);\n\t}\n\n\tprotected boolean isAllowedForOfflineStorageDefaults(Packet pac) {\n\t\tStanzaType type = pac.getType();\n\t\tswitch (pac.getElemName()) {\n\t\t\tcase \"message\":\n\t\t\t\tif (type == null || type == StanzaType.normal || type == StanzaType.chat) {\n\t\t\t\t\t// support for XEP-0334 Message Processing Hints\t\t\t\t\t\n\t\t\t\t\tif (pac.getAttributeStaticStr(MESSAGE_HINTS_NO_STORE, \"xmlns\") == MESSAGE_HINTS_XMLNS) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tif (pac.getAttributeStaticStr(MESSAGE_HINTS_STORE, \"xmlns\") == MESSAGE_HINTS_XMLNS) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\tif (pac.getElemCDataStaticStr(tigase.server.Message.MESSAGE_BODY_PATH) != null) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\tif (pac.getElemChildrenStaticStr(MESSAGE_EVENT_PATH) != null) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\tif (pac.getElemChildrenStaticStr(MESSAGE_HEADER_PATH) != null) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\tif (pac.getElement().getXMLNSStaticStr(MESSAGE_RECEIVED_PATH) == MESSAGE_RECEIVED_XMLNS) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"presence\":\n\t\t\t\tif ((type == StanzaType.subscribe) || (type == StanzaType.subscribed) ||\n\t\t\t\t\t\t(type == StanzaType.unsubscribe) || (type == StanzaType.unsubscribed)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Method determines whether offline messages should be loaded - the process should be run only once per user\n\t * session and only for available/null presence with priority greater than 0.\n\t *\n\t * @param packet a {@link Packet} object containing packet that should be verified and saved\n\t * @param conn user session which keeps all the user session data and also gives an access to the user's repository\n\t * data.\n\t *\n\t * @return {@code true} if the messages should be loaded, {@code false} otherwise.\n\t */\n\tprotected boolean loadOfflineMessages(Packet packet, XMPPResourceConnection conn) {\n\n\t\t// If the user session is null or the user is anonymous just\n\t\t// ignore it.\n\t\tif ((conn == null) || conn.isAnonymous()) {\n\t\t\treturn false;\n\t\t}    // end of if (session == null)\n\n\t\t// Try to restore the offline messages only once for the user session\n\t\tif (conn.getSessionData(ID) != null) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// make sure this is broadcast presence as only in this case we should sent offline messages\n\t\tif (packet.getStanzaTo() != null) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// if we are using XEP-0013: Flexible offline messages retrieval then we skip loading\n\t\tif (conn.getCommonSessionData(FlexibleOfflineMessageRetrieval.FLEXIBLE_OFFLINE_XMLNS) != null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tStanzaType type = packet.getType();\n\n\t\tif ((type == null) || (type == StanzaType.available)) {\n\n\t\t\t// Should we send off-line messages now?\n\t\t\t// Let's try to do it here and maybe later I find better place.\n\t\t\tString priority_str = packet.getElemCDataStaticStr(tigase.server.Presence.PRESENCE_PRIORITY_PATH);\n\t\t\tint priority = 0;\n\n\t\t\tif (priority_str != null) {\n\t\t\t\ttry {\n\t\t\t\t\tpriority = Integer.decode(priority_str);\n\t\t\t\t} catch (NumberFormatException e) {\n\t\t\t\t\tpriority = 0;\n\t\t\t\t}    // end of try-catch\n\t\t\t}      // end of if (priority != null)\n\t\t\tif (priority >= 0) {\n\t\t\t\tif (conn.getPresence() == null) {\n\t\t\t\t\tconn.setPriority(priority);\n\t\t\t\t}\n\t\t\t\tconn.putSessionData(ID, ID);\n\n\t\t\t\treturn true;\n\t\t\t}      // end of if (priority >= 0)\n\t\t}        // end of if (type == null || type == StanzaType.available)\n\n\t\treturn false;\n\t}\n\n\tprotected void notifyNewOfflineMessage(Packet packet, XMPPResourceConnection conn, Queue<Packet> queue,\n\t\t\t\t\t\t\t\t\t\t   Map<String, Object> settings) {\n\t\tif (notifiers != null) {\n\t\t\tnotifiers.forEach(notifier -> notifier.notifyNewOfflineMessage(packet, conn, queue, settings));\n\t\t}\n\t}\n\n\tprotected void notifyOfflineMessagesRetrieved(XMPPResourceConnection conn, Queue<Packet> queue) {\n\t\tif (notifiers != null) {\n\t\t\tnotifiers.forEach(notifier -> notifier.notifyOfflineMessagesRetrieved(conn, queue));\n\t\t}\n\t}\n\n\tpublic interface Notifier {\n\n\t\tvoid notifyNewOfflineMessage(Packet packet, XMPPResourceConnection conn, Queue<Packet> queue,\n\t\t\t\t\t\t\t\t\t Map<String, Object> settings);\n\n\t\tdefault void notifyOfflineMessagesRetrieved(XMPPResourceConnection session, Queue<Packet> results) {\n\t\t\t// default implementation does nothing...\n\t\t}\n\n\t}\n\n\tpublic static interface OfflineMsgRepositoryIfc\n\t\t\textends tigase.db.OfflineMsgRepositoryIfc {\n\n\t\tvoid init(NonAuthUserRepository repo, XMPPResourceConnection conn);\n\n\t}\n\n\t@Bean(name = \"msg-offline-pubsub-publisher-notifier\", parent = SessionManager.class, active = false, exportable = true)\n\tpublic static class PubSubPublisherNotifier\n\t\t\timplements Notifier {\n\n\t\t@ConfigField(desc = \"PubSub offline message publisher\", alias = MSG_PUBSUB_PUBLISHER)\n\t\tprivate String defaultPublisher;\n\t\t@ConfigField(desc = \"PubSub component JID\", alias = MSG_PUBSUB_JID)\n\t\tprivate String pubSubJID;\n\t\t@ConfigField(desc = \"PubSub node for offline messages\", alias = MSG_PUBSUB_NODE)\n\t\tprivate String pubSubNode;\n\n\t\tpublic void notifyNewOfflineMessage(final Packet packet, final XMPPResourceConnection conn,\n\t\t\t\t\t\t\t\t\t\t\tfinal Queue<Packet> queue, Map<String, Object> settings) {\n\t\t\tif (pubSubJID == null || pubSubNode == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfinal StanzaType type = packet.getType();\n\t\t\tif ((packet.getElemName().equals(\"message\") &&\n\t\t\t\t\t((packet.getElemCDataStaticStr(tigase.server.Message.MESSAGE_BODY_PATH) != null) ||\n\t\t\t\t\t\t\t(packet.getElemChildrenStaticStr(MESSAGE_EVENT_PATH) != null) ||\n\t\t\t\t\t\t\t(packet.getElemChildrenStaticStr(MESSAGE_HEADER_PATH) != null)) &&\n\t\t\t\t\t((type == null) || (type == StanzaType.normal) || (type == StanzaType.chat))) ||\n\t\t\t\t\t(packet.getElemName().equals(\"presence\") &&\n\t\t\t\t\t\t\t((type == StanzaType.subscribe) || (type == StanzaType.subscribed) ||\n\t\t\t\t\t\t\t\t\t(type == StanzaType.unsubscribe) || (type == StanzaType.unsubscribed)))) {\n\t\t\t\tString tmpNode = packet.getElement().getAttributeStaticStr(PUBSUB_NODE_PATH, PUBSUB_NODE_KEY);\n\t\t\t\tif (tmpNode != null && tmpNode.equals(this.pubSubNode)) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Publishing skipped to prevent loops: {0}\", packet);\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Publishing packet in pubsub: {0}\", packet);\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tif (defaultPublisher != null) {\n\t\t\t\t\t\tElement iq = new Element(\"iq\", new String[]{\"type\", \"id\", \"to\", \"from\"},\n\t\t\t\t\t\t\t\t\t\t\t\t new String[]{\"set\", \"\" + System.nanoTime(), pubSubJID,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  defaultPublisher});\n\t\t\t\t\t\tElement pubsub = new Element(\"pubsub\", new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t new String[]{\"http://jabber.org/protocol/pubsub\"});\n\t\t\t\t\t\tiq.addChild(pubsub);\n\t\t\t\t\t\tElement publish = new Element(\"publish\", new String[]{\"node\"}, new String[]{this.pubSubNode});\n\t\t\t\t\t\tpubsub.addChild(publish);\n\t\t\t\t\t\tElement item = new Element(\"item\");\n\t\t\t\t\t\tpublish.addChild(item);\n\n\t\t\t\t\t\titem.addChild(packet.getElement());\n\n\t\t\t\t\t\tPacket out = Packet.packetInstance(iq);\n\t\t\t\t\t\tout.setXMLNS(Packet.CLIENT_XMLNS);\n\t\t\t\t\t\tqueue.add(out);\n\t\t\t\t\t} else if (log.isLoggable(Level.WARNING)) {\n\t\t\t\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\t\t\t\"Cannot publish message in PubSub, because cannot determine publisher. Please define default publisher JID.\",\n\t\t\t\t\t\t\t\tpacket);\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Problem during publish packet in pubsub\", e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * {@link Comparator} interface implementation for the purpose of sorting Elements retrieved from the repository by\n\t * the timestamp stored in {@code delay} element.\n\t */\n\tpublic static class StampComparator\n\t\t\timplements Comparator<Packet> {\n\n\t\t@Override\n\t\tpublic int compare(Packet p1, Packet p2) {\n\t\t\t// Try XEP-0203 - the new XEP...\n\t\t\tElement stamp_el1 = p1.getElement().getChild(\"delay\", \"urn:xmpp:delay\");\n\t\t\tElement stamp_el2 = p2.getElement().getChild(\"delay\", \"urn:xmpp:delay\");\n\t\t\tboolean isStamp1New = (stamp_el1 != null);\n\t\t\tboolean isStamp2New = (stamp_el2 != null);\n\n\t\t\t// if both entries are stamped with XEP-0203 then just compare stamps\n\t\t\tif (isStamp1New && isStamp2New) {\n\t\t\t\tString stamp1 = stamp_el1.getAttributeStaticStr(\"stamp\");\n\t\t\t\tString stamp2 = stamp_el2.getAttributeStaticStr(\"stamp\");\n\t\t\t\treturn stamp1.compareTo(stamp2);\n\t\t\t}\n\n\t\t\t// retrieve XEP-0091 if there is no XEP-0203 stamp\n\t\t\tif (!isStamp1New) {\n\t\t\t\t// XEP-0091 support - the old one...\n\t\t\t\tstamp_el1 = p1.getElement().getChild(\"x\", \"jabber:x:delay\");\n\t\t\t}\n\t\t\tif (!isStamp2New) {\n\t\t\t\t// XEP-0091 support - the old one...\n\t\t\t\tstamp_el2 = p2.getElement().getChild(\"x\", \"jabber:x:delay\");\n\t\t\t}\n\n\t\t\t// retrive stamps\n\t\t\tString stamp1 = stamp_el1 == null ? \"\" : stamp_el1.getAttributeStaticStr(\"stamp\");\n\t\t\tString stamp2 = stamp_el2 == null ? \"\" : stamp_el2.getAttributeStaticStr(\"stamp\");\n\n\t\t\t// convert XEP-0203 stamp to XEP-0091 stamp, simple removal of '-' should work\n\t\t\tif (isStamp1New) {\n\t\t\t\tstamp1 = stamp1.replace(\"-\", \"\");\n\t\t\t} else if (isStamp2New) {\n\t\t\t\tstamp2 = stamp2.replace(\"-\", \"\");\n\t\t\t}\n\n\t\t\treturn stamp1.compareTo(stamp2);\n\t\t}\n\t}\n\n\t/**\n\t * Implementation of {@code MsgRepositoryIfc} interface providing basic support for storing and loading of Elements\n\t * from repository.\n\t */\n\tprivate class MsgRepositoryImpl\n\t\t\timplements OfflineMsgRepositoryIfc {\n\n\t\t/** Field holds user session which keeps all the user session data and also\n\t\t * gives an access to the user's repository data. */\n\t\t//private XMPPResourceConnection conn = null;\n\t\t/**\n\t\t * Field holds a reference to user session which keeps all the user session data and also gives an access to the\n\t\t * user's repository data.\n\t\t */\n\t\tprivate SimpleParser parser = SingletonFactory.getParserInstance();\n\t\t/**\n\t\t * Field holds reference to an implementation of {@link MsgRepositoryIfc} interface\n\t\t */\n\t\tprivate NonAuthUserRepository repo = null;\n\n\t\t/**\n\t\t * Constructs {@code MsgRepositoryImpl} object referencing user session and having handle to user repository.\n\t\t *\n\t\t * @param repo an implementation of {@link MsgRepositoryIfc} interface\n\t\t * @param conn user session which keeps all the user session data and also gives an access to the user's\n\t\t * repository data.\n\t\t */\n\t\tprivate MsgRepositoryImpl(NonAuthUserRepository repo, XMPPResourceConnection conn) {\n\t\t\tinit(repo, conn);\n\t\t}\n\n\t\t@Override\n\t\tpublic void init(NonAuthUserRepository repo, XMPPResourceConnection conn) {\n\t\t\tthis.repo = repo;\n\t\t\t//this.conn = conn;\n\t\t}\n\n\t\t@Override\n\t\t@Deprecated\n\t\tpublic void initRepository(String conn_str, Map<String, String> map) {\n\t\t\t// nothing to do here as we base on UserRepository which is already initialized\n\t\t}\n\n\t\t@Override\n\t\tpublic Element getMessageExpired(long time, boolean delete) {\n\t\t\tthrow new UnsupportedOperationException(\"Not supported yet.\");\n\t\t}\n\n\t\t@Override\n\t\tpublic Queue<Element> loadMessagesToJID(XMPPResourceConnection session, boolean delete)\n\t\t\t\tthrows UserNotFoundException {\n\t\t\ttry {\n\t\t\t\tDomBuilderHandler domHandler = new DomBuilderHandler();\n\t\t\t\tString[] msgs = session.getOfflineDataList(ID, \"messages\");\n\n\t\t\t\tif ((msgs != null) && (msgs.length > 0)) {\n\t\t\t\t\tsession.removeOfflineData(ID, \"messages\");\n\n\t\t\t\t\tStringBuilder sb = new StringBuilder();\n\n\t\t\t\t\tfor (String msg : msgs) {\n\t\t\t\t\t\tsb.append(msg);\n\t\t\t\t\t}\n\n\t\t\t\t\tchar[] data = sb.toString().toCharArray();\n\n\t\t\t\t\tparser.parse(domHandler, data, 0, data.length);\n\n\t\t\t\t\treturn domHandler.getParsedElements();\n\t\t\t\t}    // end of while (elem = elems.poll() != null)\n\t\t\t} catch (NotAuthorizedException ex) {\n\t\t\t\tlog.log(Level.CONFIG, \"User not authrized to retrieve offline messages, \" +\n\t\t\t\t\t\t\t\t \"this happens quite often on some installations where there\" +\n\t\t\t\t\t\t\t\t \" are a very short living client connections. They can \" + \"disconnect at any time. \" +\n\t\t\t\t\t\t\t\t ex);\n\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\tlog.warning(\"Error accessing database for offline message: \" + ex);\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean storeMessage(JID from, JID to, Date expired, Element msg, NonAuthUserRepository userRepo)\n\t\t\t\tthrows UserNotFoundException {\n\t\t\trepo.addOfflineDataList(to.getBareJID(), ID, \"messages\", new String[]{msg.toString()});\n\t\t\treturn true;\n\t\t}\n\t}\n\n}    // OfflineMessages\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/PepPlugin.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.roster.RosterAbstract;\nimport tigase.xmpp.impl.roster.RosterAbstract.SubscriptionType;\nimport tigase.xmpp.impl.roster.RosterFactory;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * @author Artur Hefczyc\n * @version 5.0.0, 2010.03.01 at 03:12:30 GMT\n */\n@Bean(name = PepPlugin.ID, parent = SessionManager.class, active = false)\n@Deprecated\n@TigaseDeprecated(since = \"8.5.0\", removeIn = \"9.0.0\", note = \"use full-featured PEP plugin from pubsub component: `tigase.pubsub.PepPlugin`\")\npublic class PepPlugin\n\t\textends XMPPProcessorAbstract {\n\n\tprotected static final String ID = \"pep-simple\";\n\tprivate static final String _XMLNS = \"http://jabber.org/protocol/pubsub\";\n\tprivate static final Element[] DISCO_FEATURES = {new Element(\"feature\", new String[]{\"var\"}, new String[]{_XMLNS}),\n\t\t\t\t\t\t\t\t\t\t\t\t\t new Element(\"feature\", new String[]{\"var\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t new String[]{_XMLNS + \"#owner\"}),\n\t\t\t\t\t\t\t\t\t\t\t\t\t new Element(\"feature\", new String[]{\"var\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t new String[]{_XMLNS + \"#publish\"}),\n\t\t\t\t\t\t\t\t\t\t\t\t\t new Element(\"identity\", new String[]{\"category\", \"type\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t new String[]{\"pubsub\", \"pep\"}),};\n\tprivate static final String[][] ELEMENTS = {Iq.IQ_PUBSUB_PATH};\n\tprivate static final String[] IQ_PUBSUB_PATH = {\"iq\", \"pubsub\"};\n\tprivate static final Logger log = Logger.getLogger(\"tigase.xmpp.impl.PepPlugin\");\n\tprivate static final String PUBSUB_COMPONENT_URL = \"pubsub-component\";\n\tprivate static final EnumSet<SubscriptionType> SUBSCRITION_TYPES = EnumSet.of(SubscriptionType.both,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  SubscriptionType.from);\n\tprivate static final String[] XMLNSS = {_XMLNS};\n\tprivate static RosterAbstract roster = RosterFactory.getRosterImplementation(true);\n\n\tprivate final HashSet<String> supportedNodes = new HashSet<String>();\n\n\n\tpublic PepPlugin() {\n\t\tthis.supportedNodes.add(\"http://jabber.org/protocol/tune\");\n\t\tthis.supportedNodes.add(\"http://jabber.org/protocol/mood\");\n\t\tthis.supportedNodes.add(\"http://jabber.org/protocol/activity\");\n\t\tthis.supportedNodes.add(\"http://jabber.org/protocol/geoloc\");\n\t\tthis.supportedNodes.add(\"urn:xmpp:avatar:data\");\n\t\tthis.supportedNodes.add(\"urn:xmpp:avatar:metadata\");\n\t}\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic void processFromUserToServerPacket(JID connectionId, Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t  NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t\t  Map<String, Object> settings) throws PacketErrorTypeException {\n\t\ttry {\n\t\t\tList<Element> x = packet.getElemChildrenStaticStr(IQ_PUBSUB_PATH);\n\t\t\tboolean processed = false;\n\n\t\t\tfor (Element element : x) {\n\t\t\t\tString action = element.getName();\n\t\t\t\tString node = element.getAttributeStaticStr(\"node\");\n\n\t\t\t\tif (this.supportedNodes.contains(node)) {\n\t\t\t\t\tif (action == \"retract\") {\n\t\t\t\t\t\tElement item = element.getChild(\"item\", null);\n\t\t\t\t\t\tElement retract = new Element(\"retract\");\n\n\t\t\t\t\t\tif (item.getAttributeStaticStr(Packet.ID_ATT) != null) {\n\t\t\t\t\t\t\tretract.addAttribute(Packet.ID_ATT, item.getAttributeStaticStr(Packet.ID_ATT));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tprocessed = true;\n\t\t\t\t\t\tprocessPEPPublish(packet, node, retract, session, repo, results, settings);\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (action == \"publish\") {\n\t\t\t\t\t\t\tElement item = element.getChild(\"item\", null);\n\n\t\t\t\t\t\t\tprocessed = true;\n\t\t\t\t\t\t\tprocessPEPPublish(packet, node, item, session, repo, results, settings);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (processed) {\n\t\t\t\tresults.offer(packet.okResult((Element) null, 0));\n\t\t\t} else {\n\t\t\t\tresults.offer(Authorization.FEATURE_NOT_IMPLEMENTED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"The PEP node is not yet supported.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   true));\n\t\t\t}\n\t\t} catch (NotAuthorizedException ex) {\n\t\t\tlog.warning(\"NotAuthorizedException for packet: \" + packet);\n\t\t\tresults.offer(\n\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(packet, \"You must authorize session first.\", true));\n\t\t} catch (TigaseDBException ex) {\n\t\t\tLogger.getLogger(PepPlugin.class.getName()).log(Level.SEVERE, null, ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processNullSessionPacket(Packet packet, NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t Map<String, Object> settings) throws PacketErrorTypeException {\n\t\tresults.offer(Authorization.SERVICE_UNAVAILABLE.getResponseMessage(packet, \"Service not available.\", true));\n\t}\n\n\t@Override\n\tpublic void processServerSessionPacket(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t\t\t\t   Queue<Packet> results, Map<String, Object> settings)\n\t\t\tthrows PacketErrorTypeException {\n\n\t\t// I guess we ignore such packets here, no pep support for the server\n\t\t// itself yet\n\t}\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(final XMPPResourceConnection session) {\n\t\treturn DISCO_FEATURES;\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\tprivate void forward(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t Queue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\t\tString pubSubComponentUrl = (settings == null) ? null : (String) settings.get(PUBSUB_COMPONENT_URL);\n\n\t\tif ((session == null) || (pubSubComponentUrl == null)) {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.fine(\"Packet reject. No Session or PubSub Component URL.\");\n\t\t\t}\n\n\t\t\treturn;\n\t\t}    // end of if (session == null)\n\t\ttry {\n\t\t\tpacket.getElement().setAttribute(\"to\", pubSubComponentUrl);\n\n\t\t\tBareJID id = packet.getStanzaTo().getBareJID();\n\n\t\t\tif (session.isUserId(id)) {\n\n\t\t\t\t// Yes this is message to 'this' client\n\t\t\t\tPacket result = packet.copyElementOnly();\n\n\t\t\t\tresult.setStableId(packet.getStableId());\n\t\t\t\tresult.setPacketTo(session.getConnectionId());\n\t\t\t\tresult.setPacketFrom(packet.getTo());\n\t\t\t\tresults.offer(result);\n\t\t\t} else {\n\n\t\t\t\t// This is message to some other client\n\t\t\t\tPacket result = packet.copyElementOnly();\n\t\t\t\tresult.setStableId(packet.getStableId());\n\t\t\t\tresults.offer(result);\n\t\t\t}    // end of else\n\t\t} catch (NotAuthorizedException e) {\n\t\t\tlog.warning(\"NotAuthorizedException for packet: \" + packet);\n\t\t\tresults.offer(\n\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(packet, \"You must authorize session first.\", true));\n\t\t}    // end of try-catch\n\t}\n\n\tprivate void processPEPPublish(Packet packet, String node, Element pepItem, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t   NonAuthUserRepository repo, Queue<Packet> results, Map<String, Object> settings)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tJID[] buddies = roster.getBuddies(session, SUBSCRITION_TYPES);\n\t\tElement event = new Element(\"event\", new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\tnew String[]{\"http://jabber.org/protocol/pubsub#event\"});\n\t\tElement items = new Element(\"items\", new String[]{\"node\"}, new String[]{node});\n\n\t\tevent.addChild(items);\n\t\titems.addChild(pepItem);\n\n\t\tJID from = packet.getStanzaFrom();\n\n\t\tif (buddies != null) {\n\t\t\tfor (JID buddy : buddies) {\n\t\t\t\tElement message = new Element(\"message\", new String[]{\"from\", \"to\", \"type\", \"id\"},\n\t\t\t\t\t\t\t\t\t\t\t  new String[]{from.toString(), buddy.toString(), \"headline\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   packet.getStanzaId()});\n\n\t\t\t\tmessage.addChild(event);\n\t\t\t\tresults.offer(Packet.packetInstance(message, from, buddy));\n\t\t\t}\n\t\t}\n\n\t\tElement message = new Element(\"message\", new String[]{\"from\", \"to\", \"type\", \"id\"},\n\t\t\t\t\t\t\t\t\t  new String[]{from.toString(), from.toString(), \"headline\", packet.getStanzaId()});\n\n\t\tmessage.addChild(event);\n\t\tresults.offer(Packet.packetInstance(message, from, from));\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/PresenceAbstract.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.TigaseDBException;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Packet;\nimport tigase.sys.TigaseRuntime;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.annotation.AnnotatedXMPPProcessor;\nimport tigase.xmpp.impl.roster.*;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.xmpp.impl.roster.RosterAbstract.*;\n\n/**\n * Class responsible for handling Presence packets\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\npublic abstract class PresenceAbstract\n\t\textends AnnotatedXMPPProcessor\n\t\timplements XMPPProcessorIfc {\n\n\tpublic static final String PRESENCE_ELEMENT_NAME = \"presence\";\n\n\tpublic static final String SKIP_OFFLINE_PROP_KEY = \"skip-offline\";\n\n\tpublic static final String PRESENCE_PROBE_FULL_JID_KEY = \"probe-full-jid\";\n\n\tpublic static final String SKIP_OFFLINE_SYS_PROP_KEY = \"skip-offline-sys\";\n\n\tpublic static final String USERS_STATUS_CHANGES = \"Users status changes\";\n\n\tprotected static final String XMLNS = CLIENT_XMLNS;\n\n\tprivate static final Logger log = Logger.getLogger(PresenceAbstract.class.getName());\n\t//private static final String[]   PRESENCE_PRIORITY_PATH  = { \"presence\", \"priority\" };\n\t//private static final String[]   XMLNSS                  = { XMLNS, RosterAbstract.XMLNS_LOAD };\n\t@ConfigField(desc = \"Probe full JID\", alias = PRESENCE_PROBE_FULL_JID_KEY)\n\tprotected static boolean probeFullJID = false;\n\t@ConfigField(desc = \"Skip offline\", alias = SKIP_OFFLINE_PROP_KEY)\n\t// Setting this option to true will cause issues with PEP support!\n\tprotected static boolean skipOffline = false;\n\t@ConfigField(desc = \"Skip offline sys\", alias = SKIP_OFFLINE_SYS_PROP_KEY)\n\t// Setting this option to true will cause issues with PEP support!\n\tprivate static boolean skipOfflineSys = false;\n\n\tprotected RosterAbstract roster_util = getRosterUtil();\n\n\t// This is required to make sure that dynamic roster will get initialized\n\t@Inject(nullAllowed = true)\n\tprivate DynamicRoster dynamicRoster;\n\n\t/**\n\t * Simply forwards packet to the destination\n\t *\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param packet to forward\n\t * @param from is a <code>JID</code> instance with stanza source address.\n\t */\n\tprotected static void forwardPresence(Queue<Packet> results, Packet packet, JID from) {\n\t\tElement result = packet.getElement().clone();\n\n\t\t// Not needed anymore. Packet filter does it for all stanzas.\n\t\t// According to spec we must set proper FROM attribute\n\t\t// Yes, but packet filter puts full JID and we need a subscription\n\t\t// presence without resource here.\n\t\tresult.setAttribute(\"from\", from.toString());\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"\\n\\nFORWARD presence: {0}\", result.toString());\n\t\t}\n\t\tresults.offer(Packet.packetInstance(result, from, packet.getStanzaTo()));\n\t}\n\n\t/**\n\t * Returns shared instance of class implementing {@link RosterAbstract} - either default one ({@link RosterFlat}) or\n\t * the one configured with <em>\"roster-implementation\"</em> property.\n\t *\n\t * @return shared instance of class implementing {@link RosterAbstract}\n\t */\n\tprotected static RosterAbstract getRosterUtil() {\n\t\treturn RosterFactory.getRosterImplementation(true);\n\t}\n\n\tprotected static Element prepareProbe(XMPPResourceConnection session) throws NotAuthorizedException {\n\t\tElement presProbe = new Element(PRESENCE_ELEMENT_NAME);\n\t\tpresProbe.setXMLNS(XMLNS);\n\t\tpresProbe.setAttribute(\"type\", StanzaType.probe.toString());\n\t\tif (probeFullJID) {\n\t\t\tpresProbe.setAttribute(\"from\", session.getJID().toString());\n\t\t} else {\n\t\t\tpresProbe.setAttribute(\"from\", session.getBareJID().toString());\n\t\t}\n\t\treturn presProbe;\n\t}\n\n\t/**\n\t * Method checks whether a given contact requires sending presence. In case of enabling option {@code skipOffline}\n\t * and user being offline in the roster the presence is not sent. Alternatively enabling option {@code\n\t * skipOfflineSys} would cause local environment check for user status and omit sending presence if the local use is\n\t * offline.\n\t *\n\t * @param roster instance of class implementing {@link RosterAbstract}.\n\t * @param buddy JID of a contact for which a check is to be performed.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param systemCheck indicates whether the check should be only based on local environment state ({@code true}) or\n\t * rooster state of given user should also be taken into consideration ({@code false}).\n\t *\n\t * @return {code true} if the contact requires sending presence (e.g. is not online and options skipOffline or\n\t * skipOfflineSys are enabled)\n\t *\n\t */\n\tprotected static boolean requiresPresenceSending(RosterAbstract roster, JID buddy, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t\t\t boolean systemCheck)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tboolean result = true;\n\n\t\t// if non-system check is enabled during broadcast of non-first initial\n\t\t// presence or offline presence\n\t\tif (!systemCheck) {\n\t\t\tboolean isOnline = roster.isOnline(session, buddy);\n\t\t\tif (skipOffline && !isOnline) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"{0} | buddy: {1} is online: {2}\",\n\t\t\t\t\t\t\tnew Object[]{session.getJID(), buddy, isOnline});\n\t\t\t\t}\n\t\t\t\tresult = result && false;\n\t\t\t}\n\t\t}\n\t\tif (skipOfflineSys) {\n\t\t\tTigaseRuntime runtime = TigaseRuntime.getTigaseRuntime();\n\t\t\tboolean isJidOnline = runtime.isJidOnline(buddy);\n\n\t\t\tif (runtime.hasCompleteJidsInfo() && session.isLocalDomain(buddy.getDomain(), false) && !isJidOnline) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"{0} | buddy: {1} is online (sys): {2}\",\n\t\t\t\t\t\t\tnew Object[]{session.getJID(), buddy, isJidOnline});\n\t\t\t\t}\n\t\t\t\tresult = result && false;\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Sends Presence stanza from provided parameters as well as returns created result {@link Packet} object. In case\n\t * of missing {@code  pres} parameter a Presence stanza will be created with provided {@link StanzaType} type {@code\n\t * t}, {@link JID} type {@code from} and {@link JID} type {@code to}. Otherwise Presence stanza {@code pres} will be\n\t * cloned and {@code to} attribute will be set from parameter {@code to}.\n\t *\n\t * @param t specifies type of the presence to be send.\n\t * @param from is a <code>JID</code> instance with stanza source address.\n\t * @param to is a <code>JID</code> instance with stanza destination address.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param pres an Object of type {@link Element} holding Presence stanza to be sent.\n\t *\n\t * @return an instance of {@link Packet} holding Presence stanza created from provided parameters.\n\t */\n\tpublic static Packet sendPresence(StanzaType t, JID from, JID to, Queue<Packet> results, Element pres) {\n\t\tObjects.requireNonNull(from);\n\t\tObjects.requireNonNull(to);\n\t\tElement presence = null;\n\t\tPacket result = null;\n\n\t\tif (pres == null) {\n\t\t\tpresence = new Element(PRESENCE_ELEMENT_NAME);\n\t\t\tif (t != null) {\n\t\t\t\tpresence.setAttribute(\"type\", t.toString());\n\t\t\t}    // end of if (t != null)\n\t\t\telse {\n\t\t\t\tpresence.setAttribute(\"type\", StanzaType.unavailable.toString());\n\t\t\t}    // end of if (t != null) else\n\t\t\tif (null != from) {\n\t\t\t\tpresence.setAttribute(\"from\", from.toString());\n\t\t\t}\n\t\t\tpresence.setXMLNS(XMLNS);\n\t\t} else {\n\t\t\tpresence = pres.clone();\n\t\t}      // end of if (pres == null) else\n\t\tpresence.setAttribute(\"to\", to.toString());\n\t\ttry {\n\n\t\t\t// Connection IDs are not available so let's send it a normal way\n\t\t\tresult = Packet.packetInstance(presence);\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Sending presence info: {0}\", result);\n\t\t\t}\n\t\t\tresults.offer(result);\n\t\t} catch (TigaseStringprepException ex) {\n\t\t\tlog.log(Level.FINE, \"Packet stringprep addressing problem, skipping presence send: {0}\", presence);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Sends Presence stanza from provided parameters without returning created result {@link Packet} object. In case of\n\t * missing {@code  pres} parameter a Presence stanza will be created with provided {@link StanzaType} type {@code\n\t * t}, {@link JID} type {@code from} and {@link JID} type {@code to}. Otherwise Presence stanza {@code pres} will be\n\t * cloned and {@code to} attribute will be set from parameter {@code to}.\n\t *\n\t * @param t specifies type of the presence to be send.\n\t * @param from is a <code>JID</code> instance with stanza source address.\n\t * @param to is a <code>JID</code> instance with stanza destination address.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param pres an Object of type {@link Element} holding Presence stanza to be sent.\n\t */\n\tpublic static void sendPresence(StanzaType t, BareJID from, BareJID to, Queue<Packet> results, Element pres) {\n\t\tsendPresence(t, JID.jidInstance(from), JID.jidInstance(to), results, pres);\n\t}\n\n\t/**\n\t * <code>updatePresenceChange</code> method is used to broadcast to all active resources presence stanza received\n\t * from other users, like incoming availability presence, subscription presence and so on... Initial presences are\n\t * however sent only to those resources which already have sent initial presence.\n\t *\n\t * @param presence an <code>Element</code> presence received from other users, we have to change 'to' attribute to\n\t * full resource JID.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t *\n\t * @throws NotAuthorizedException if an error occurs\n\t */\n\tpublic static void updatePresenceChange(Packet presence, XMPPResourceConnection session, Queue<Packet> results)\n\t\t\tthrows NotAuthorizedException {\n\t\tboolean initial_p = ((presence.getAttributeStaticStr(Packet.TYPE_ATT) == null) ||\n\t\t\t\t\"available\".equals(presence.getAttributeStaticStr(Packet.TYPE_ATT)) ||\n\t\t\t\t\"unavailable\".equals(presence.getAttributeStaticStr(Packet.TYPE_ATT)));\n\n\t\tfor (XMPPResourceConnection conn : session.getActiveSessions()) {\n\n\t\t\t// Update presence change only for online resources that is\n\t\t\t// resources which already sent initial presence.\n\t\t\tif (((conn.getPresence() == null) && initial_p)) {\n\n\t\t\t\t// Ignore....\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.finest(\"Skipping update presence change for a resource which hasn't sent \" +\n\t\t\t\t\t\t\t\t\t   \"initial presence yet, or is remote connection: \" + conn);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\tlog.finer(\"Update presence change to: \" + conn);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Send to old resource presence about new resource\n\t\t\t\t\tPacket pres_update = presence.copyElementOnly();\n\n\t\t\t\t\tpres_update.initVars(presence.getStanzaFrom(), conn.getJID().copyWithoutResource());\n\t\t\t\t\tpres_update.setPacketTo(conn.getConnectionId());\n\t\t\t\t\tresults.offer(pres_update);\n\t\t\t\t} catch (NotAuthorizedException | NoConnectionIdException e) {\n\n\t\t\t\t\t// It might be quite possible that one of the user connections\n\t\t\t\t\t// is in state not allowed for sending presence, in such a case\n\t\t\t\t\t// none of user connections would receive presence.\n\t\t\t\t\t// This catch is to make sure all other resources receive\n\t\t\t\t\t// notification.\n\t\t\t\t}\n\t\t\t}\n\t\t}    // end of for (XMPPResourceConnection conn: sessions)\n\t}\n\n\t/**\n\t * <code>updateUserResources</code> method is used to broadcast to all <strong>other</strong> resources presence\n\t * stanza from one user resource. So if new resource connects this method updates presence information about new\n\t * resource to old resources and about old resources to new resource.\n\t *\n\t * @param presence an <code>Element</code> presence received from other users, we have to change 'to' attribute to\n\t * full resource JID.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param initial specifies whether this is an initial presence or not (i.e. if there is a presence data from the\n\t * presence stored within user session object or not)\n\t *\n\t * @throws NotAuthorizedException if an error occurs\n\t */\n\tpublic static void updateUserResources(Element presence, XMPPResourceConnection session, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t   boolean initial) throws NotAuthorizedException {\n\t\tfor (XMPPResourceConnection conn : session.getActiveSessions()) {\n\t\t\ttry {\n\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\tlog.log(Level.FINER, \"Update presence change to: {0}\", conn.getJID());\n\t\t\t\t}\n\n\t\t\t\t// We also do not send presence updates to any remote connections on\n\t\t\t\t// different cluster nodes. Each node takes care of delivering presence\n\t\t\t\t// locally\n\t\t\t\tif (conn.isResourceSet()) {\n\n\t\t\t\t\t// Send to old resource presence about new resource\n\t\t\t\t\tElement pres_update = presence.clone();\n\t\t\t\t\tPacket pack_update = Packet.packetInstance(pres_update, session.getJID(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   conn.getJID().copyWithoutResource());\n\n\t\t\t\t\tpack_update.setPacketTo(conn.getConnectionId());\n\t\t\t\t\tresults.offer(pack_update);\n\n\t\t\t\t\tElement presence_el = conn.getPresence();\n\n\t\t\t\t\t// Send to new resource last presence sent by the old resource\n\t\t\t\t\tif ((presence_el != null) && initial && (conn != session)) {\n\t\t\t\t\t\tpres_update = presence_el.clone();\n\t\t\t\t\t\tpack_update = Packet.packetInstance(pres_update, conn.getJID(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsession.getJID().copyWithoutResource());\n\t\t\t\t\t\tpack_update.setPacketTo(session.getConnectionId());\n\t\t\t\t\t\tresults.offer(pack_update);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\tlog.finer(\"Skipping presence update to: \" + conn.getJID());\n\t\t\t\t\t}\n\t\t\t\t}    // end of else\n\t\t\t} catch (NotAuthorizedException | NoConnectionIdException e) {\n\n\t\t\t\t// It might be quite possible that one of the user connections\n\t\t\t\t// is in state not allowed for sending presence, in such a case\n\t\t\t\t// none of user connections would receive presence.\n\t\t\t\t// This catch is to make sure all other resources receive notification.\n\t\t\t}\n\t\t}        // end of for (XMPPResourceConnection conn: sessions)\n\t}\n\n\tpublic boolean isSkipOfflineSys() {\n\t\treturn skipOfflineSys;\n\t}\n\n\tpublic void setSkipOfflineSys(boolean skipOfflineSys) {\n\t\tPresenceAbstract.skipOfflineSys = skipOfflineSys;\n\t}\n\n\tpublic boolean isSkipOffline() {\n\t\treturn skipOffline;\n\t}\n\n\tpublic void setSkipOffline(boolean skipOffline) {\n\t\tPresenceAbstract.skipOffline = skipOffline;\n\t}\n\n\tpublic boolean getProbeFullJID() {\n\t\treturn probeFullJID;\n\t}\n\n\tpublic void setProbeFullJID(boolean probeFullJID) {\n\t\tPresenceAbstract.probeFullJID = probeFullJID;\n\t}\n\n//\t/**\n//\t * Method updates resources information upon receiving initial availability\n//\t * presence (type available or missing type)\n//\t *\n//\t *\n//\t * @param session user session which keeps all the user session data and also\n//\t *                gives an access to the user's repository data.\n//\t * @param type    specifies type of the stanza.\n//\t * @param packet  packet is which being processed.\n//\t */\n//\tprotected static void updateResourcesAvailable(XMPPResourceConnection session,\n//\t\t\tStanzaType type, Packet packet) {\n//\t\tXMPPSession parentSession = session.getParentSession();\n//\n//\t\tif (parentSession != null) {\n//\t\t\tMap<JID, Map> resources;\n//\t\t\tboolean       online = (type == null) || (type == StanzaType.available);\n//\n//\t\t\tsynchronized (parentSession) {\n//\t\t\t\tresources = (Map<JID, Map>) parentSession.getCommonSessionData(\n//\t\t\t\t\t\tXMPPResourceConnection.ALL_RESOURCES_KEY);\n//\t\t\t\tif (resources == null) {\n//\t\t\t\t\tif (!online) {\n//\t\t\t\t\t\treturn;\n//\t\t\t\t\t}\n//\t\t\t\t\tresources = new ConcurrentHashMap<>();\n//\t\t\t\t\tsession.putCommonSessionData(XMPPResourceConnection.ALL_RESOURCES_KEY,\n//\t\t\t\t\t\t\tresources);\n//\t\t\t\t}\n//\t\t\t}\n//\t\t\tif (online) {\n//\t\t\t\tMap map = resources.get(packet.getStanzaFrom());\n//\n//\t\t\t\tif (map == null) {\n//\t\t\t\t\tmap = new ConcurrentHashMap();\n//\t\t\t\t\tresources.put(packet.getStanzaFrom(), map);\n//\t\t\t\t}\n//\n//\t\t\t\tString priorityStr = packet.getElemCDataStaticStr(PRESENCE_PRIORITY_PATH);\n//\n//\t\t\t\tif (priorityStr != null) {\n//\t\t\t\t\tmap.put(XMPPResourceConnection.ALL_RESOURCES_PRIORITY_KEY, Integer.parseInt(\n//\t\t\t\t\t\t\tpriorityStr));\n//\t\t\t\t} else if (!map.containsKey(XMPPResourceConnection.ALL_RESOURCES_PRIORITY_KEY)) {\n//\t\t\t\t\tmap.put(XMPPResourceConnection.ALL_RESOURCES_PRIORITY_KEY, 0);\n//\t\t\t\t}\n//\n//\t\t\t\tElement c = packet.getElement().getChild(\"c\", \"http://jabber.org/protocol/caps\");\n//\n//\t\t\t\tif (c != null) {\n//\t\t\t\t\tmap.put(XMPPResourceConnection.ALL_RESOURCES_CAPS_KEY,\n//\t\t\t\t\t\t\tPresenceCapabilitiesManager.processPresence(c));\n//\t\t\t\t}\n//\t\t\t} else {\n//\t\t\t\tresources.remove(packet.getStanzaFrom());\n//\t\t\t}\n//\t\t}\n//\t}\n\n\tprotected boolean forceSendingProbe() {\n\t\treturn true;\n\t}\n\n\t/**\n\t * <code>sendPresenceBroadcast</code> method broadcasts given presence to all buddies from roster and to all users\n\t * to which direct presence was sent. Before sending presence method calls {@code  requiresPresenceSending()},\n\t * configured to only check local environment status (if enabled) to verify whether presence needs to be sent.\n\t *\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param settings this map keeps plugin specific settings loaded from the Tigase server configuration.\n\t *\n\t * @throws NotAuthorizedException if an error occurs\n\t */\n\tpublic void broadcastProbe(XMPPResourceConnection session, Queue<Packet> results, Map<String, Object> settings)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Broadcasting probes for: {0}\", session);\n\t\t}\n\n\t\t// Probe is always broadcasted with initial presence\n\t\tElement presInit = session.getPresence();\n\t\tElement presProbe = prepareProbe(session);\n\n\t\tJID[] buddies = roster_util.getBuddies(session, SUB_BOTH);\n\n\t\ttry {\n\t\t\tbuddies = DynamicRoster.addBuddies(session, settings, buddies, SUB_BOTH);\n\t\t} catch (RosterRetrievingException | RepositoryAccessException ex) {\n\n\t\t\t// Ignore, handled in the JabberIqRoster code\n\t\t}\n\t\tif (buddies != null) {\n\t\t\tfor (JID buddy : buddies) {\n\t\t\t\tif (forceSendingProbe() || requiresPresenceSending(roster_util, buddy, session, true)) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, session.getBareJID() + \" | Sending presence probe to: \" + buddy);\n\t\t\t\t\t}\n\t\t\t\t\tsendPresence(null, session.getBareJID(), buddy.getBareJID(), results, presProbe);\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, session.getBareJID() + \" | Sending initial presence to: \" + buddy);\n\t\t\t\t\t}\n\t\t\t\t\tsendPresence(null, session.getBareJID(), buddy.getBareJID(), results, presInit);\n\t\t\t\t\troster_util.setPresenceSent(session, buddy, true);\n\t\t\t\t} else {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\tsession.getBareJID() + \" | Skipping sending initial presence and probe to: \" + buddy);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}    // end of for (String buddy: buddies)\n\t\t}      // end of if (buddies == null)\n\n\t\tJID[] buddies_to = roster_util.getBuddies(session, SUB_TO);\n\n\t\ttry {\n\t\t\tbuddies_to = DynamicRoster.addBuddies(session, settings, buddies_to, SUB_TO);\n\t\t} catch (RosterRetrievingException | RepositoryAccessException ex) {\n\t\t\t// Ignore, handled in the JabberIqRoster code\n\t\t}\n\n\t\tif (buddies_to != null) {\n\t\t\tfor (JID buddy : buddies_to) {\n\t\t\t\tif (forceSendingProbe() || requiresPresenceSending(roster_util, buddy, session, true)) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, session.getBareJID() + \" | Sending probe to: \" + buddy);\n\t\t\t\t\t}\n\t\t\t\t\tsendPresence(null, session.getBareJID(), buddy.getBareJID(), results, presProbe);\n\t\t\t\t} else {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, session.getBareJID() + \" | Skipping sending presence probe to: \" + buddy);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}    // end of for (String buddy: buddies)\n\t\t}      // end of if (buddies == null)\n\n\t\t// TODO: It might be a marginal number of cases here but just make it clear\n\t\t// we send a presence here regardless\n\t\tJID[] buddies_from = roster_util.getBuddies(session, SUB_FROM);\n\n\t\ttry {\n\t\t\tbuddies_from = DynamicRoster.addBuddies(session, settings, buddies_from, SUB_FROM);\n\t\t} catch (RosterRetrievingException | RepositoryAccessException ex) {\n\t\t\t// Ignore, handled in the JabberIqRoster code\n\t\t}\n\n\t\tif (buddies_from != null) {\n\t\t\tfor (JID buddy : buddies_from) {\n\t\t\t\tif (requiresPresenceSending(roster_util, buddy, session, true)) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, session.getBareJID() + \" | Sending initial presence to: \" + buddy);\n\t\t\t\t\t}\n\t\t\t\t\tsendPresence(null, session.getBareJID(), buddy.getBareJID(), results, presInit);\n\t\t\t\t\troster_util.setPresenceSent(session, buddy, true);\n\t\t\t\t} else {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\tsession.getBareJID() + \" | Skipping sending initial presence and probe to: \" + buddy);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}    // end of for (String buddy: buddies)\n\t\t}      // end of if (buddies == null)\n\t}\n\n\t@Override\n\tpublic int concurrentQueuesNo() {\n\t\treturn super.concurrentQueuesNo() * 4;\n\t}\n\n} \n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/PresenceCapabilitiesManager.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.disco.ServiceIdentity;\nimport tigase.server.DataForm;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.util.Base64;\nimport tigase.xml.Element;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.JID;\n\nimport java.io.UnsupportedEncodingException;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.*;\nimport java.util.concurrent.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class PresenceCapabilitiesManager {\n\n\tpublic static final String CAPS_NODE = \"https://tigase.net/tigase-xmpp-server\";\n\tpublic final static String HASH_ALGORITHM = \"SHA-1\";\n\tpublic final static String charsetName = \"UTF-8\";\n\t// Map<capsNode,Set<feature>>\n\tprivate static final Map<String, String[]> nodeFeatures = new ConcurrentHashMap<String, String[]>(250);\n\tprivate static final ConcurrentMap<String, Set<String>> featureNodes = new ConcurrentHashMap<String, Set<String>>(\n\t\t\t250);\n\tprivate static final List<PresenceCapabilitiesListener> handlers = new CopyOnWriteArrayList<PresenceCapabilitiesListener>();\n\tprivate static long idCounter = 0;\n\tprivate static Logger log = Logger.getLogger(PresenceCapabilitiesManager.class.getName());\n\n\tprivate static MessageDigest addValues(String[] features, MessageDigest md) throws UnsupportedEncodingException {\n\t\tif (features != null) {\n\t\t\tArrays.sort(features);\n\n\t\t\tfor (String f : features) {\n\t\t\t\tmd.update(f.getBytes(charsetName));\n\t\t\t\tmd.update((byte) '<');\n\t\t\t}\n\t\t}\n\t\treturn md;\n\t}\n\n\tpublic static String generateVerificationString(String[] identities, String[] features) {\n\t\treturn generateVerificationString(identities, features, null);\n\t}\n\n\tpublic static String generateVerificationString(String[] identities, String[] features, Element extensions) {\n\t\ttry {\n\t\t\tlog.log(Level.FINEST, \"Generating caps for identities: {0}, features: {1}, extensions: {2}\",\n\t\t\t\t\tnew String[]{Arrays.toString(identities), Arrays.toString(features), String.valueOf(extensions)});\n\t\t\tMessageDigest md = MessageDigest.getInstance(HASH_ALGORITHM);\n\n\t\t\tmd = addValues(identities, md);\n\t\t\tmd = addValues(features, md);\n\n\t\t\tif (extensions != null) {\n\t\t\t\tfinal Set<String> fields = DataForm.getFields(extensions);\n\t\t\t\tif (fields != null) {\n\t\t\t\t\tmd.update(DataForm.getFormType(extensions).getBytes(charsetName));\n\t\t\t\t\tmd.update((byte) '<');\n\t\t\t\t\tSortedSet<String> vars = new TreeSet<>(fields);\n\t\t\t\t\tfor (String var : vars) {\n\t\t\t\t\t\tmd.update(var.getBytes(charsetName));\n\t\t\t\t\t\tmd.update((byte) '<');\n\t\t\t\t\t\tfinal String[] values = DataForm.getFieldValues(extensions, var);\n\t\t\t\t\t\taddValues(values, md);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tbyte[] digest = md.digest();\n\t\t\treturn Base64.encode(digest);\n\t\t} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {\n\t\t\tlog.warning(\"Cannot calculate verification string.\");\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static String generateVerificationStringFromDiscoInfo(Element discoInfo) {\n\t\tString[] features = getFeaturesFromDiscoInfo(discoInfo);\n\t\tString[] identities = ServiceIdentity.getServiceIdentitiesCapsFromDiscoInfo(discoInfo);\n\t\tElement extension = discoInfo.getChild(\"x\", \"jabber:x:data\");\n\t\treturn generateVerificationString(identities, features, extension);\n\t}\n\n\tpublic static Element getCapsElement(String caps) {\n\t\treturn new Element(\"c\", new String[]{\"xmlns\", \"hash\", \"node\", \"ver\"},\n\t\t\t\t\t\t   new String[]{CAPS.XMLNS, HASH_ALGORITHM.toLowerCase(), CAPS_NODE, caps});\n\t}\n\n\tpublic static String[] getFeaturesFromDiscoInfo(Element discoInfo) {\n\t\tString[] features = null;\n\t\tfinal List<Element> featureElements = discoInfo.findChildren(child -> child.getName().equals(\"feature\"));\n\t\tif (featureElements != null && !featureElements.isEmpty()) {\n\t\t\tList<String> list = new ArrayList<>();\n\t\t\tfor (Element element : featureElements) {\n\t\t\t\tString var = element.getAttributeStaticStr(\"var\");\n\t\t\t\tlist.add(var);\n\t\t\t}\n\t\t\tfeatures = list.toArray(new String[0]);\n\t\t}\n\t\treturn features;\n\t}\n\n\tpublic static String[] getNodeFeatures(String capsNode) {\n\t\treturn nodeFeatures.get(capsNode);\n\t}\n\n\tpublic static Set<String> getNodesWithFeature(String feature) {\n\t\tSet<String> nodes = featureNodes.get(feature);\n\t\tif (nodes == null) {\n\t\t\treturn Collections.emptySet();\n\t\t} else {\n\t\t\treturn Collections.unmodifiableSet(nodes);\n\t\t}\n\t}\n\n\tpublic static void handlePresence(JID owner, JID from, String[] capsNodes, Queue<Packet> results) {\n\t\tif (capsNodes == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tList<PresenceCapabilitiesListener> handlers = PresenceCapabilitiesManager.handlers;\n\n\t\tfor (PresenceCapabilitiesListener handler : handlers) {\n\t\t\thandler.handlePresence(owner, from, capsNodes, results);\n\t\t}\n\t}\n\n\tpublic static void prepareCapsQueries(JID compJid, JID to, String[] caps_nodes, Queue<Packet> results) {\n\t\tif (caps_nodes != null) {\n\t\t\tfor (String caps_node : caps_nodes) {\n\t\t\t\tif (!nodeFeatures.containsKey(caps_node)) {\n\t\t\t\t\tresults.offer(prepareCapsQuery(to, compJid, caps_node));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static void prepareCapsQueriesEl(JID compJid, JID to, String[] caps_nodes, Queue<Element> results) {\n\t\tif (caps_nodes != null) {\n\t\t\tfor (String caps_node : caps_nodes) {\n\t\t\t\tif (!nodeFeatures.containsKey(caps_node)) {\n\t\t\t\t\tresults.offer(prepareCapsQueryEl(to, compJid, caps_node));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static Packet prepareCapsQuery(JID to, JID from, String node) {\n\t\tElement iq = prepareCapsQueryEl(to, from, node);\n\t\tfinal Iq iqPacket = new Iq(iq, from, to);\n\t\tiqPacket.setXMLNS(Packet.CLIENT_XMLNS);\n\n\t\treturn iqPacket;\n\t}\n\n\tpublic static Element prepareCapsQueryEl(JID to, JID from, String node) {\n\t\tString id = String.valueOf(idCounter++);\n\t\tElement iq = new Element(\"iq\", new String[]{\"from\", \"to\", \"id\", \"type\", Packet.XMLNS_ATT},\n\t\t\t\t\t\t\t\t new String[]{from.toString(), to.toString(), id, \"get\", Packet.CLIENT_XMLNS});\n\t\tElement query = new Element(\"query\", new String[]{\"xmlns\", \"node\"},\n\t\t\t\t\t\t\t\t\tnew String[]{\"http://jabber.org/protocol/disco#info\", node});\n\n\t\tiq.addChild(query);\n\n\t\treturn iq;\n\t}\n\n\tpublic static void processCapsQueryResponse(Packet packet) {\n\n\t\t// No need for checking to domain - processors and components should do this\n//  if (VHostManager.isLocalDomainOrComponent(packet.getStanzaTo().getDomain())) {\n\t\tElement query = packet.getElement().getChild(\"query\", \"http://jabber.org/protocol/disco#info\");\n\n\t\tif (query != null) {\n\t\t\tif (packet.getType() == StanzaType.result) {\n\t\t\t\tif (query.getAttributeStaticStr(\"node\") == null) {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.finest(\"disco#info query without node attribute!\");\n\t\t\t\t\t}\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tList<Element> ch = query.getChildren();\n\n\t\t\t\tif (ch != null) {\n\t\t\t\t\tSet<String> features = new ConcurrentSkipListSet<String>();\n\n\t\t\t\t\tfor (Element item : ch) {\n\t\t\t\t\t\tif (!\"feature\".equals(item.getName())) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfeatures.add(item.getAttributeStaticStr(\"var\"));\n\t\t\t\t\t}\n\t\t\t\t\tsetNodeFeatures(query.getAttributeStaticStr(\"node\"), features.toArray(new String[features.size()]));\n\t\t\t\t}\n\t\t\t}\n\n//      else if (packet.getType() == StanzaType.error && manager.getNodeFeatures(query.getAttribute(\"node\")) == null) {\n//          getInstance().setNodeFeatures(query.getAttribute(\"node\"), NULL_NODES);\n//      }\n//      return;\n\t\t}\n\n//  }\n\t}\n\n\tpublic static String[] processPresence(Element c) {\n\t\tSet<String> caps_nodes = null;\n\n\t\tif (c != null) {\n\t\t\tcaps_nodes = new HashSet<String>();\n\n\t\t\tString caps_node = c.getAttributeStaticStr(\"node\") + \"#\" + c.getAttributeStaticStr(\"ver\");\n\n\t\t\tcaps_nodes.add(caps_node);\n\t\t\tif ((c.getAttributeStaticStr(\"hash\") == null) && (c.getAttributeStaticStr(\"ext\") != null)) {\n\t\t\t\tfor (String e : c.getAttributeStaticStr(\"ext\").split(\" \")) {\n\t\t\t\t\tcaps_nodes.add(c.getAttributeStaticStr(\"node\") + \"#\" + e);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn caps_nodes.toArray(new String[caps_nodes.size()]);\n\t}\n\n\tpublic static String[] processPresence(JID compJid, Packet p, Queue<Packet> results) {\n\t\tElement c = p.getElement().getChild(\"c\");\n\t\tSet<String> features = new HashSet<String>();\n\n\t\tif (c != null) {\n\t\t\tString caps_node = c.getAttributeStaticStr(\"node\") + \"#\" + c.getAttributeStaticStr(\"ver\");\n\n\t\t\t// String[] nFeatures = nodeFeatures.get(caps_node);\n\t\t\tif (!nodeFeatures.containsKey(caps_node)) {\n\t\t\t\tSet<String> caps_nodes = new HashSet<String>();\n\n\t\t\t\tcaps_nodes.add(caps_node);\n\t\t\t\tif ((c.getAttributeStaticStr(\"hash\") == null) && (c.getAttributeStaticStr(\"ext\") != null)) {\n\t\t\t\t\tfor (String e : c.getAttributeStaticStr(\"ext\").split(\" \")) {\n\t\t\t\t\t\tcaps_nodes.add(c.getAttributeStaticStr(\"node\") + \"#\" + e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor (String node : caps_nodes) {\n\t\t\t\t\tif (!nodeFeatures.containsKey(node)) {\n\t\t\t\t\t\tresults.offer(prepareCapsQuery(p.getFrom(), compJid, node));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn features.toArray(new String[features.size()]);\n\t}\n\n\tpublic static void registerPresenceHandler(PresenceCapabilitiesListener handler) {\n\t\thandlers.add(handler);\n\t}\n\n\tpublic static void setNodeFeatures(String capsNode, String[] features) {\n\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\tlog.log(Level.FINER, \"setting features for node = {0}, features = {1}\",\n\t\t\t\t\tnew Object[]{capsNode, Arrays.asList(features)});\n\t\t}\n\t\tArrays.sort(features);\n\t\tnodeFeatures.put(capsNode, features);\n\t\tfor (String feature : features) {\n\t\t\tSet<String> caps = featureNodes.get(feature);\n\t\t\tif (caps == null) {\n\t\t\t\tSet<String> tmp = new CopyOnWriteArraySet<String>();\n\t\t\t\tcaps = featureNodes.putIfAbsent(feature, tmp);\n\t\t\t\tif (caps == null) {\n\t\t\t\t\tcaps = tmp;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcaps.add(capsNode);\n\t\t}\n\t}\n\n\tpublic static void unregisterPresenceHandler(PresenceCapabilitiesListener handler) {\n\t\thandlers.remove(handler);\n\t}\n\n\tpublic static interface PresenceCapabilitiesListener {\n\n\t\tvoid handlePresence(JID owner, JID sender, String[] capsNodes, Queue<Packet> results);\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/PresenceOffline.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserRepository;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.EventBusEvent;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.eventbus.HandleEvent;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.sys.TigaseRuntime;\nimport tigase.util.cache.LRUConcurrentCache;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.DomBuilderHandler;\nimport tigase.xml.Element;\nimport tigase.xml.SimpleParser;\nimport tigase.xml.SingletonFactory;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.XMPPStopListenerIfc;\nimport tigase.xmpp.impl.annotation.Handle;\nimport tigase.xmpp.impl.annotation.Handles;\nimport tigase.xmpp.impl.annotation.Id;\nimport tigase.xmpp.impl.roster.RosterAbstract;\nimport tigase.xmpp.impl.roster.RosterElement;\nimport tigase.xmpp.impl.roster.RosterFlat;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.io.Serializable;\nimport java.text.SimpleDateFormat;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.xmpp.impl.roster.RosterAbstract.ROSTER;\n\n@Id(PresenceOffline.ID)\n@Handles({@Handle(path = {PresenceAbstract.PRESENCE_ELEMENT_NAME}, xmlns = PresenceAbstract.CLIENT_XMLNS),\n\t\t  @Handle(path = {Iq.ELEM_NAME, Iq.QUERY_NAME}, xmlns = RosterAbstract.XMLNS)})\n@Bean(name = PresenceOffline.ID, parent = SessionManager.class, active = false)\npublic class PresenceOffline\n\t\textends PresenceAbstract\n\t\timplements XMPPStopListenerIfc, Initializable, UnregisterAware {\n\n\tpublic static final String CACHE_SIZE_PROP_KEY = \"cache-size\";\n\tprotected static final String ID = \"presence-offline\";\n\tprivate static final Logger log = Logger.getLogger(PresenceOffline.class.getCanonicalName());\n\tprivate static final EnumSet<StanzaType> PRESENCE_SUB_CHANGE_TYPES = EnumSet.of(StanzaType.subscribed,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tStanzaType.unsubscribe,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tStanzaType.unsubscribed);\n\tprivate final static String LAST_OFFLINE_PRESENCE_KEY = \"last-offline-presence\";\n\tprivate final static String DELAY_STAMP_KEY = \"delay-stamp\";\n\tprivate final EventBus eventBus = EventBusFactory.getInstance();\n\tprivate final SimpleDateFormat formatter;\n\tprivate final SimpleParser parser = SingletonFactory.getParserInstance();\n\tprivate final String presenceSessionEventName = \"start-stop\";\n\t@ConfigField(desc = \"Add delay stamp to offline presences\", alias = DELAY_STAMP_KEY)\n\tboolean delayStamp = true;\n\t@ConfigField(desc = \"Default cache size\", alias = CACHE_SIZE_PROP_KEY)\n\tprivate int cacheSize = 1000;\n\tprivate LRUConcurrentCache<BareJID, Element> presenceCache = new LRUConcurrentCache<>(10000);\n\tprivate LRUConcurrentCache<BareJID, Map<BareJID, RosterElement>> rosterCache = new LRUConcurrentCache<>(10000);\n\t@Inject\n\tprivate UserRepository userRepository = null;\n\n\t{\n\t\tthis.formatter = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'\");\n\t\tthis.formatter.setTimeZone(TimeZone.getTimeZone(\"UTC\"));\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\teventBus.registerAll(this);\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\teventBus.unregisterAll(this);\n\t}\n\n\tpublic void setCacheSize(int size) {\n\t\tthis.cacheSize = size;\n\n\t\tpresenceCache = new LRUConcurrentCache<>(cacheSize);\n\t\trosterCache = new LRUConcurrentCache<>(cacheSize);\n\t}\n\n\t@HandleEvent(filter = HandleEvent.Type.remote)\n\tpublic void onEvent(StartStopEvent event) {\n\n\t\tBareJID[] jidElements = event.getJids();\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Procesing userPresence event: {0} with following jids: {1}\",\n\t\t\t\t\tnew Object[]{event, jidElements});\n\t\t}\n\n\t\tif (jidElements != null) {\n\t\t\tfor (BareJID jid : jidElements) {\n\t\t\t\tString actionStr = event.getAction();\n\t\t\t\tif (actionStr != null) {\n\n\t\t\t\t\tswitch (actionStr) {\n\t\t\t\t\t\tcase \"presence\":\n\t\t\t\t\t\t\tpresenceCache.remove(jid);\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Clearing presence cache: {0}, remaining items: {1}\",\n\t\t\t\t\t\t\t\t\t\tnew Object[]{jid, presenceCache.size()});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t\tcase \"roster\":\n\t\t\t\t\t\t\trosterCache.remove(jid);\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Clearing roster cache: {0}, remaining items: {1}\",\n\t\t\t\t\t\t\t\t\t\tnew Object[]{jid, rosterCache.size()});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@SuppressWarnings({\"unchecked\", \"fallthrough\"})\n\t@Override\n\tpublic void process(final Packet packet, final XMPPResourceConnection session, final NonAuthUserRepository repo,\n\t\t\t\t\t\tfinal Queue<Packet> results, final Map<String, Object> settings) throws XMPPException {\n\n\t\t// if presence probe & user is offline -> send last offline presence\n\t\t// if presence unavailable - from that user - store presence &\n\t\t// invalidate cache\n\t\t// if presence sub change or roster change -- invalidate roster cache\n\t\tif (\"presence\".equals(packet.getElemName())) {\n\n\t\t\tif (session == null && packet.getType() == StanzaType.probe && packet.getStanzaFrom() != null &&\n\t\t\t\t\tpacket.getStanzaTo() != null && !packet.getStanzaFrom().equals(packet.getStanzaTo())) {\n\n\t\t\t\tBareJID stanzaTo = packet.getStanzaTo() != null ? packet.getStanzaTo().getBareJID() : null;\n\t\t\t\tBareJID stanzaFrom = packet.getStanzaFrom() != null ? packet.getStanzaFrom().getBareJID() : null;\n\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Processing presence probe {0} to offline user: {1}\",\n\t\t\t\t\t\t\tnew Object[]{packet, packet.getStanzaTo()});\n\t\t\t\t}\n\n\t\t\t\tif (stanzaTo != null && stanzaFrom != null) {\n\n\t\t\t\t\tif (isSubscriptionValid(stanzaTo, stanzaFrom)) {\n\n\t\t\t\t\t\tElement presence = presenceCache.get(stanzaTo);\n\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Retrieved presence from cache: {0}\", presence);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (presence == null) {\n\t\t\t\t\t\t\tpresence = loadPresenceFromRepo(stanzaTo);\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Retrieved presence from respository: {0}\", presence);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (presence != null) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tPacket p = Packet.packetInstance(presence.clone());\n\t\t\t\t\t\t\t\tp.initVars(p.getStanzaFrom(), packet.getStanzaFrom());\n\n\t\t\t\t\t\t\t\tresults.offer(p);\n\t\t\t\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\t\t\t\tlog.log(Level.WARNING, \"Error creating packet instance from presence: \" + presence, ex);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t} else if (session != null && packet.getStanzaFrom() != null) {\n\n\t\t\t\tif (PRESENCE_SUB_CHANGE_TYPES.contains(packet.getType())) {\n\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Presence sub change - sending event to cleare cache {0}\",\n\t\t\t\t\t\t\t\tpacket.getElement());\n\t\t\t\t\t}\n\n\t\t\t\t\tsendEvent(\"roster\", packet.getStanzaFrom() != null ? packet.getStanzaFrom().getBareJID() : null,\n\t\t\t\t\t\t\t  packet.getStanzaTo() != null ? packet.getStanzaTo().getBareJID() : null);\n\n\t\t\t\t} else if (session.isUserId(packet.getStanzaFrom().getBareJID()) &&\n\t\t\t\t\t\t((packet.getType() == null) || (packet.getType() == StanzaType.available)) // &&\n\t\t\t\t\t// !isNotOnlySession(\n\t\t\t\t\t// session\n\t\t\t\t\t// )\n\t\t\t\t\t\t) {\n\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\t\"Presence session: {0} started - sending event and removing data from repository, packet: \",\n\t\t\t\t\t\t\t\tnew Object[]{session, packet});\n\t\t\t\t\t}\n\n\t\t\t\t\tsendEvent(\"presence\", session.getJID().getBareJID());\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tuserRepository.removeData(packet.getStanzaFrom().getBareJID(), LAST_OFFLINE_PRESENCE_KEY);\n\t\t\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\t\t\tlog.log(Level.WARNING,\n\t\t\t\t\t\t\t\t\"Error removing data from repository while starting new presence session\", ex);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\t\t} else if (packet.getType() == StanzaType.set &&\n\t\t\t\tpacket.getElement().getXMLNSStaticStr(Iq.IQ_QUERY_PATH) == RosterAbstract.XMLNS) {\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Roster change - updated roster cache, packet: {0}\", packet);\n\t\t\t}\n\n\t\t\tif (packet.getStanzaFrom() != null) {\n\t\t\t\tfinal BareJID user = packet.getStanzaFrom().getBareJID();\n\t\t\t\tsendEvent(\"roster\", user);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void stopped(XMPPResourceConnection session, Queue<Packet> results, Map<String, Object> settings) {\n\n\t\t// if ( isNotOnlySession( session ) ){\n\t\t// return;\n\t\t// }\n\t\t// Synchronization to avoid conflict with login/logout events\n\t\t// processed in the SessionManager asynchronously\n\t\tif (session == null || !session.isAuthorized()) {\n\t\t\treturn;\n\t\t}\n\n\t\tsynchronized (session) {\n\n\t\t\tsendEvent(\"presence\", session.getjid().getBareJID());\n\n\t\t\tfinal Element lastPresence = session.getPresence() != null ? session.getPresence().clone() : null;\n\t\t\tif (lastPresence != null) {\n\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Session: {0} stopped, storing to repository last presence: {1}\",\n\t\t\t\t\t\t\tnew Object[]{session, lastPresence});\n\t\t\t\t}\n\n\t\t\t\tif (delayStamp) {\n\t\t\t\t\tString stamp = null;\n\n\t\t\t\t\tsynchronized (formatter) {\n\t\t\t\t\t\tstamp = formatter.format(new Date());\n\t\t\t\t\t}\n\n\t\t\t\t\tif (stamp != null) {\n\t\t\t\t\t\tElement x = new Element(\"delay\", new String[]{\"stamp\", \"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t\tnew String[]{stamp, \"urn:xmpp:delay\"});\n\t\t\t\t\t\tlastPresence.addChild(x);\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tif (!StanzaType.unavailable.toString().equals(lastPresence.getAttributeStaticStr(Packet.TYPE_ATT))) {\n\t\t\t\t\tlastPresence.setAttribute(Packet.TYPE_ATT, StanzaType.unavailable.toString());\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tuserRepository.setData(session.getjid().getBareJID(), LAST_OFFLINE_PRESENCE_KEY,\n\t\t\t\t\t\t\t\t\t\t   lastPresence.toString());\n\t\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Error storing last offline presence to repository: \" + lastPresence, ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n\tprotected boolean isNotOnlySession(XMPPResourceConnection session) {\n\t\tif (TigaseRuntime.getTigaseRuntime().hasCompleteJidsInfo() && session != null) {\n\t\t\tfinal JID userJID = session.getjid();\n\t\t\tif (TigaseRuntime.getTigaseRuntime().isJidOnline(userJID)) {\n\t\t\t\t// ok, this use is still connected - don't update the repository\n\t\t\t\t// just yet\n\t\t\t\tJID[] connectionIdsForJid = TigaseRuntime.getTigaseRuntime().getConnectionIdsForJid(userJID);\n\t\t\t\tif (connectionIdsForJid != null && connectionIdsForJid.length > 0) {\n\t\t\t\t\tif (connectionIdsForJid.length != 1 || !connectionIdsForJid[0].equals(userJID)) {\n\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"There are other user {0} sessions still active: {1}\",\n\t\t\t\t\t\t\t\t\tnew Object[]{session.getjid(), Arrays.asList(connectionIdsForJid)});\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprotected boolean isSubscriptionValid(BareJID owner, BareJID contact) {\n\t\tRosterAbstract.SubscriptionType buddy_subscr = null;\n\t\tMap<BareJID, RosterElement> roster = null;\n\n\t\tboolean result = false;\n\n\t\troster = rosterCache.get(owner);\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Checking user {0} subscription of {1}, present in cache: {2}\",\n\t\t\t\t\tnew Object[]{owner, contact, roster != null});\n\t\t}\n\n\t\tif (roster == null) {\n\t\t\tString rosterString = null;\n\t\t\ttry {\n\t\t\t\trosterString = userRepository.getData(owner, ROSTER);\n\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\tlog.log(Level.WARNING, \"Problem reading roster from DB: \", ex);\n\t\t\t}\n\n\t\t\tif (rosterString != null) {\n\t\t\t\troster = new ConcurrentHashMap<BareJID, RosterElement>(100, 0.25f, 1);\n\t\t\t\tRosterFlat.parseRosterUtil(rosterString, roster, null);\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Loaded roster from DB: {0}\", roster);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (roster != null) {\n\t\t\tRosterElement rosterElement = roster.get(contact);\n\t\t\tbuddy_subscr = rosterElement.getSubscription();\n\t\t\tif (buddy_subscr == null) {\n\t\t\t\tbuddy_subscr = RosterAbstract.SubscriptionType.none;\n\t\t\t}\n\t\t\tresult = roster_util.isSubscribedFrom(buddy_subscr);\n\t\t}\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"isSubscriptionValid, owner: {0}, contact: {1}, result: {2}\",\n\t\t\t\t\tnew Object[]{owner, contact, result});\n\t\t}\n\t\treturn result;\n\t}\n\n\tprotected Element loadPresenceFromRepo(BareJID stanzaTo) {\n\t\tElement presence = null;\n\t\ttry {\n\t\t\tString presString = userRepository.getData(stanzaTo, LAST_OFFLINE_PRESENCE_KEY);\n\t\t\tDomBuilderHandler domHandler = new DomBuilderHandler();\n\n\t\t\tQueue<Element> parsedElements = null;\n\t\t\tif (presString != null) {\n\n\t\t\t\tchar[] data = presString.toCharArray();\n\t\t\t\tparser.parse(domHandler, data, 0, data.length);\n\t\t\t\tparsedElements = domHandler.getParsedElements();\n\t\t\t}\n\n\t\t\tif (parsedElements != null && parsedElements.size() > 0) {\n\t\t\t\tpresence = parsedElements.poll();\n\t\t\t\tpresenceCache.put(stanzaTo, presence);\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Loaded presence: {0} and stored it in cache\", presence);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (TigaseDBException ex) {\n\t\t\tlog.log(Level.WARNING, \"Loading presence from repository failed!\", ex);\n\t\t}\n\t\treturn presence;\n\t}\n\n\tprivate void sendEvent(String action, BareJID... user) {\n\t\tif (user != null && user.length > 0) {\n\n\t\t\tStartStopEvent event = new StartStopEvent();\n\t\t\tevent.setAction(action);\n\t\t\tevent.setJids(user);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Sending event: \" + event);\n\t\t\t}\n\n\t\t\teventBus.fire(event);\n\t\t}\n\n\t}\n\n\tpublic class StartStopEvent\n\t\t\timplements Serializable, EventBusEvent {\n\n\t\tpublic StartStopEvent() {}\n\n\t\tprivate String action;\n\t\tprivate BareJID[] jids;\n\n\t\tpublic String getAction() {\n\t\t\treturn action;\n\t\t}\n\n\t\tpublic void setAction(String action) {\n\t\t\tthis.action = action;\n\t\t}\n\n\t\tpublic BareJID[] getJids() {\n\t\t\treturn jids;\n\t\t}\n\n\t\tpublic void setJids(BareJID[] jids) {\n\t\t\tthis.jids = jids;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/PresenceState.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.Priority;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.stats.StatisticsList;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.annotation.Handle;\nimport tigase.xmpp.impl.annotation.Handles;\nimport tigase.xmpp.impl.annotation.Id;\nimport tigase.xmpp.impl.push.PushPresence;\nimport tigase.xmpp.impl.roster.*;\nimport tigase.xmpp.impl.roster.RosterAbstract.PresenceType;\nimport tigase.xmpp.impl.roster.RosterAbstract.SubscriptionType;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.function.Consumer;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.xmpp.impl.roster.RosterAbstract.FROM_SUBSCRIBED;\nimport static tigase.xmpp.impl.roster.RosterAbstract.TO_SUBSCRIBED;\n\n/**\n * @author andrzej\n */\n@Id(PresenceState.ID)\n@Handles({@Handle(path = {PresenceAbstract.PRESENCE_ELEMENT_NAME}, xmlns = PresenceAbstract.CLIENT_XMLNS),\n\t\t  @Handle(path = {Iq.ELEM_NAME, Iq.QUERY_NAME}, xmlns = RosterAbstract.XMLNS_LOAD)})\n@Bean(name = PresenceState.ID, parent = SessionManager.class, active = true)\npublic class PresenceState\n\t\textends PresenceAbstract\n\t\timplements XMPPStopListenerIfc {\n\n\t/**\n\t * <code>DIRECT_PRESENCE</code> is a key in temporary session data for the collection of JIDs where direct presence\n\t * was sent. To all these addresses unavailable presence must be sent when user disconnects.\n\t */\n\tpublic static final String DIRECT_PRESENCE = \"direct-presences\";\n\n\tpublic static final String ENABLE_ROSTER_LAZY_LOADING_KEY = \"enable-roster-lazy-loading\";\n\n\tpublic static final String EXTENDED_PRESENCE_PROCESSORS_KEY = \"extended-presence-processors\";\n\n\tpublic static final String OFFLINE_BUD_SENT = \"offline-bud-sent\";\n\n\tpublic static final String OFFLINE_RES_SENT = \"offline-res-sent\";\n\n\tpublic static final String OFFLINE_ROSTER_LAST_SEEN_PROP_KEY = \"offline-roster-last-seen\";\n\n\t/**\n\t * key allowing setting global forwarding JID address.\n\t */\n\tpublic static final String PRESENCE_GLOBAL_FORWARD = \"presence-global-forward\";\n\tprotected static final String ID = \"presence-state\";\n\tprivate static final Logger log = Logger.getLogger(PresenceState.class.getCanonicalName());\n\tprivate static final long MAX_DIRECT_PRESENCES_NO = 1000;\n\tprivate static final String[] PRESENCE_C_PATH = {PRESENCE_ELEMENT_NAME, \"c\"};\n\tprivate static final Set<StanzaType> TYPES = new HashSet<>(\n\t\t\tArrays.asList(StanzaType.available, StanzaType.unavailable, StanzaType.probe, StanzaType.error,\n\t\t\t\t\t\t  StanzaType.result, null));\n\tprotected static int HIGH_PRIORITY_PRESENCES_NO = 10;\n\t@Inject(nullAllowed = true)\n\tprivate List<ExtendedPresenceProcessorIfc> extendedPresenceProcessors = new ArrayList<>();\n\t@ConfigField(desc = \"Send last seen infomations for matching clients\", alias = OFFLINE_ROSTER_LAST_SEEN_PROP_KEY)\n\tprivate String[] offlineRosterLastSeen = null;\n\t@ConfigField(desc = \"Forward all presences to following JID\", alias = PRESENCE_GLOBAL_FORWARD)\n\tprivate JID presenceGLobalForward = null;\n\t@ConfigField(desc = \"Enable roster lazy loading\", alias = ENABLE_ROSTER_LAZY_LOADING_KEY)\n\tprivate boolean rosterLazyLoading = true;\n\tprivate long usersStatusChanges = 0;\n\t@Inject(nullAllowed = true)\n\tprivate PushPresence pushDevicesPresence;\n\n\tprivate void withPushDevicesPresence(Consumer<PushPresence> consumer) {\n\t\tif (pushDevicesPresence != null) {\n\t\t\tconsumer.accept(pushDevicesPresence);\n\t\t}\n\t}\n\n\t@Override\n\tprotected boolean forceSendingProbe() {\n\t\treturn pushDevicesPresence != null;\n\t}\n\n\t/**\n\t * Add JID to collection of JIDs to which direct presence was sent. To all these addresses unavailable presence must\n\t * be sent when user disconnects.\n\t *\n\t * @param jid to which direct presence was sent.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t */\n\t@SuppressWarnings({\"unchecked\"})\n\tpublic static void addDirectPresenceJID(JID jid, XMPPResourceConnection session) {\n\t\tSet<JID> direct_presences = (Set<JID>) session.getSessionData(DIRECT_PRESENCE);\n\n\t\tif (direct_presences == null) {\n\t\t\tdirect_presences = new LinkedHashSet<JID>(10);\n\t\t\tsession.putSessionData(DIRECT_PRESENCE, direct_presences);\n\t\t}    // end of if (direct_presences == null)\n\t\tif (direct_presences.size() < MAX_DIRECT_PRESENCES_NO) {\n\t\t\tdirect_presences.add(jid);\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Added direct presence jid: {0}\", jid);\n\t\t}\n\t}\n\n\t/**\n\t * {@code broadcastDirectPresences} broadcast a direct Presence from provided {@code pres} {@link Element} object to\n\t * the collection of JIDs stored in temporary session data under key {@code DIRECT_PRESENCE}.\n\t *\n\t * @param t specifies type of the presence to be send.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param pres an Object of type {@link Element} holding Presence stanza to be sent.\n\t *\n\t */\n\t@SuppressWarnings({\"unchecked\"})\n\tprotected static void broadcastDirectPresences(StanzaType t, XMPPResourceConnection session, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t\t\t   Element pres) throws NotAuthorizedException, TigaseDBException {\n\t\tSet<JID> direct_presences = (Set<JID>) session.getSessionData(DIRECT_PRESENCE);\n\n\t\tif ((direct_presences != null) && (t != null) && (t == StanzaType.unavailable)) {\n\t\t\tfor (JID buddy : direct_presences) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Updating direct presence for: {0}\", buddy);\n\t\t\t\t}\n\n\t\t\t\tPacket pack = sendPresence(t, session.getJID(), buddy, results, pres);\n\n\t\t\t\tpack.setPriority(Priority.LOW);\n\t\t\t}    // end of for (String buddy: buddies)\n\t\t}      // end of if (direct_presence != null)\n\t}\n\n\t/**\n\t * <code>sendPresenceBroadcast</code> method broadcasts given presence to all buddies from roster and to all users\n\t * to which direct presence was sent.\n\t *\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param settings this map keeps plugin specific settings loaded from the Tigase server configuration.\n\t * @param roster_util instance of class implementing {@link RosterAbstract}.\n\t *\n\t * @throws NotAuthorizedException if an error occurs\n\t */\n\tpublic static void broadcastOffline(XMPPResourceConnection session, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\tMap<String, Object> settings, RosterAbstract roster_util)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\n\t\t// Preventing sending offline notifications more than once\n\t\tif (session.getSessionData(OFFLINE_BUD_SENT) != null) {\n\t\t\treturn;\n\t\t}\n\t\tsession.putSessionData(OFFLINE_BUD_SENT, OFFLINE_BUD_SENT);\n\n\t\tElement pres = session.getPresence();\n\n\t\tif (pres != null) {\n\t\t\tsendPresenceBroadcast(StanzaType.unavailable, session, FROM_SUBSCRIBED, results, pres, settings,\n\t\t\t\t\t\t\t\t  roster_util);\n\t\t} else {\n\t\t\tbroadcastDirectPresences(StanzaType.unavailable, session, results, pres);\n\t\t}\n\t}\n\n\t/**\n\t * Remove JID from collection of JIDs to which direct presence was sent.\n\t *\n\t * @param jid to which direct presence was sent.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t */\n\t@SuppressWarnings({\"unchecked\"})\n\tpublic static void removeDirectPresenceJID(JID jid, XMPPResourceConnection session) {\n\t\tSet<JID> direct_presences = (Set<JID>) session.getSessionData(DIRECT_PRESENCE);\n\n\t\tif (direct_presences != null) {\n\t\t\tdirect_presences.remove(jid);\n\t\t}    // end of if (direct_presences == null)\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Added direct presence jid: {0}\", jid);\n\t\t}\n\t}\n\n\t/**\n\t * <code>sendPresenceBroadcast</code> method broadcasts given presence to all buddies from roster and to all users\n\t * to which direct presence was sent. Before sending presence method calls {@code  requiresPresenceSending()}\n\t * performing, if configured, both system and roster check to verify whether presence needs to be sent.\n\t *\n\t * @param t specifies type of the presence to be send.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param subscrs an {@code EnumSet<SubscriptionType>} holding all {@link SubscriptionType} to which a Presence\n\t * should be broadcast.\n\t * @param pres an Object of type {@link Element} holding Presence stanza to be sent.\n\t * @param settings this map keeps plugin specific settings loaded from the Tigase server configuration.\n\t * @param roster_util instance of class implementing {@link RosterAbstract}.\n\t *\n\t * @throws NotAuthorizedException if an error occurs\n\t */\n\tpublic static void sendPresenceBroadcast(StanzaType t, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t EnumSet<RosterAbstract.SubscriptionType> subscrs, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t\t Element pres, Map<String, Object> settings, RosterAbstract roster_util)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\n\t\t// Direct presence if any should be sent first\n\t\tbroadcastDirectPresences(t, session, results, pres);\n\n\t\tRosterAbstract roster = roster_util;\n\n\t\tif (roster == null) {\n\t\t\troster = RosterFactory.getRosterImplementation(true);\n\t\t}\n\n\t\tJID[] buddies = roster.getBuddies(session, subscrs);\n\n\t\ttry {\n\t\t\tbuddies = DynamicRoster.addBuddies(session, settings, buddies, subscrs);\n\t\t} catch (RosterRetrievingException | RepositoryAccessException ex) {\n\n\t\t\t// Ignore, handled in the JabberIqRoster code\n\t\t}\n\t\tif (buddies != null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, session.getBareJID() + \" | Buddies found: \" + Arrays.toString(buddies));\n\t\t\t}\n\n\t\t\tPriority pack_priority = Priority.PRESENCE;\n\t\t\tint pres_cnt = 0;\n\n\t\t\tfor (JID buddy : buddies) {\n\t\t\t\tif (requiresPresenceSending(roster, buddy, session, false)) {\n\t\t\t\t\tPacket pack = sendPresence(t, session.getJID(), buddy, results, pres);\n\n\t\t\t\t\tif (pres_cnt == HIGH_PRIORITY_PRESENCES_NO) {\n\t\t\t\t\t\t++pres_cnt;\n\t\t\t\t\t\tpack_priority = Priority.LOWEST;\n\t\t\t\t\t}\n\t\t\t\t\tif (pack != null) {\n\t\t\t\t\t\tpack.setPriority(pack_priority);\n\t\t\t\t\t\troster.setPresenceSent(session, buddy, true);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, session.getBareJID() + \" | Not sending presence to buddy: \" + buddy);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}    // end of for (String buddy: buddies)\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"No buddies found!!!!\");\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * <code>updateOfflineChange</code> method broadcast off-line presence to all other user active resources.\n\t *\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t *\n\t * @throws NotAuthorizedException if an error occurs\n\t */\n\tprotected static void updateOfflineChange(XMPPResourceConnection session, Queue<Packet> results)\n\t\t\tthrows NotAuthorizedException {\n\n\t\t// Preventing sending offline notifications more than once\n\t\tif (session.getSessionData(OFFLINE_RES_SENT) != null) {\n\t\t\treturn;\n\t\t}\n\t\tsession.putSessionData(OFFLINE_RES_SENT, OFFLINE_RES_SENT);\n\t\tfor (XMPPResourceConnection conn : session.getActiveSessions()) {\n\t\t\ttry {\n\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\tlog.log(Level.FINER, \"Update presence change to: {0}\", conn.getJID());\n\t\t\t\t}\n\t\t\t\tif ((conn != session) && (conn.isResourceSet()) && !conn.getResource().equals(session.getResource())) {\n\n\t\t\t\t\t// Send to old resource presence about new resource\n\t\t\t\t\tElement pres_update = new Element(PRESENCE_ELEMENT_NAME);\n\n\t\t\t\t\tpres_update.setAttribute(\"type\", StanzaType.unavailable.toString());\n\t\t\t\t\tpres_update.setXMLNS(XMLNS);\n\n\t\t\t\t\t// accroding to RFC1621, 4.5.2.  Server Processing of Outbound Unavailable Presence\n\t\t\t\t\t// this presece packet should be addressed to fullJID\n\t\t\t\t\tPacket pack_update = Packet.packetInstance(pres_update, session.getJID(), conn.getJID());\n\n\t\t\t\t\tpack_update.setPacketTo(conn.getConnectionId());\n\t\t\t\t\tresults.offer(pack_update);\n\t\t\t\t} else {\n\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\tlog.log(Level.FINER, \"Skipping presence update to: {0}\", conn.getJID());\n\t\t\t\t\t}\n\t\t\t\t}    // end of else\n\t\t\t} catch (NoConnectionIdException | NotAuthorizedException e) {\n\n\t\t\t\t// It might be quite possible that one of the user connections\n\t\t\t\t// is in state not allowed for sending presence, in such a case\n\t\t\t\t// none of user connections would receive presence.\n\t\t\t\t// This catch is to make sure all other resources receive notification.\n\t\t\t}\n\t\t}        // end of for (XMPPResourceConnection conn: sessions)\n\t}\n\n\t@Override\n\tpublic Set<StanzaType> supTypes() {\n\t\treturn TYPES;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t * <br>\n\t * Performs processing of <em>presence</em> packets and calls different methods for particular {@link PresenceType}\n\t */\n\t@SuppressWarnings({\"unchecked\", \"fallthrough\"})\n\t@Override\n\tpublic void process(final Packet packet, final XMPPResourceConnection session, final NonAuthUserRepository repo,\n\t\t\t\t\t\tfinal Queue<Packet> results, final Map<String, Object> settings) throws XMPPException {\n\t\tif (session == null) {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"Session is null, ignoring packet: {0}\", packet);\n\t\t\t}\n\t\t\twithPushDevicesPresence(\n\t\t\t\t\tpushPresence -> pushPresence.processPresenceToOffline(packet.getStanzaTo(), packet.getStanzaFrom(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  packet.getType(), results::offer));\n\t\t\treturn;\n\t\t}    // end of if (session == null)\n\t\tif (!session.isAuthorized()) {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"Session is not authorized, ignoring packet: {0}\", packet);\n\t\t\t}\n\t\t\twithPushDevicesPresence(\n\t\t\t\t\tpushPresence -> pushPresence.processPresenceToOffline(packet.getStanzaTo(), packet.getStanzaFrom(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  packet.getType(), results::offer));\n\t\t\treturn;\n\t\t}\n\n\t\tif (packet.getElemName() == Iq.ELEM_NAME) {\n\t\t\t// here we process results of roster loading process requests\n\t\t\tboolean finishProcessing = true;\n\t\t\tswitch (packet.getType()) {\n\t\t\t\tcase result:\n\t\t\t\t\tsynchronized (session) {\n\t\t\t\t\t\tElement presEl = session.getPresence();\n\t\t\t\t\t\tif (presEl != null) {\n\t\t\t\t\t\t\tsession.removeSessionData(XMPPResourceConnection.PRESENCE_KEY);\n\t\t\t\t\t\t\tpresEl.removeAttribute(\"from\");\n\t\t\t\t\t\t\tpresEl.removeAttribute(\"to\");\n\t\t\t\t\t\t\tPacket pres = Packet.packetInstance(presEl, packet.getStanzaFrom(), packet.getStanzaTo());\n\t\t\t\t\t\t\tpres.setPacketFrom(packet.getPacketFrom());\n\t\t\t\t\t\t\tpres.setPacketTo(packet.getPacketTo());\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tprocessOutInitial(pres, session, results, settings,\n\t\t\t\t\t\t\t\t\t\t\t\t  RosterAbstract.PresenceType.out_initial);\n\t\t\t\t\t\t\t} catch (NotAuthorizedException e) {\n\t\t\t\t\t\t\t\tlog.log(Level.CONFIG,\n\t\t\t\t\t\t\t\t\t\t\"Can not access user Roster, user session is not authorized yet: {0}\", packet);\n\t\t\t\t\t\t\t\tlog.log(Level.FINEST, \"presence problem...\", e);\n\t\t\t\t\t\t\t} catch (TigaseDBException e) {\n\t\t\t\t\t\t\t\tlog.log(Level.WARNING, \"Error accessing database for presence data: {0}\", e);\n\t\t\t\t\t\t\t}    // end of try-catch\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\t// ignore this\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (finishProcessing) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tsynchronized (session) {\n\t\t\ttry {\n\t\t\t\tRosterAbstract.PresenceType pres_type = roster_util.getPresenceType(session, packet);\n\n\t\t\t\tif (pres_type == null) {\n\t\t\t\t\tlog.log(Level.CONFIG, \"Invalid presence found: {0}\", packet);\n\n\t\t\t\t\treturn;\n\t\t\t\t}    // end of if (type == null)\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"{0} | {1} presence found: {2}\",\n\t\t\t\t\t\t\tnew Object[]{session.getBareJID().toString(), pres_type, packet});\n\t\t\t\t}\n\n\t\t\t\tswitch (pres_type) {\n\t\t\t\t\tcase out_initial:\n\t\t\t\t\t\tprocessOutInitial(packet, session, results, settings, pres_type);\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase in_initial:\n\t\t\t\t\t\tprocessInInitial(packet, session, results, settings, pres_type);\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase in_probe:\n\t\t\t\t\t\twithPushDevicesPresence(pushPresence -> pushPresence.processPresenceProbe(packet.getStanzaTo(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  packet.getStanzaFrom(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  results::offer));\n\t\t\t\t\t\tif (session.getPresence() == null) {\n\n\t\t\t\t\t\t\t// If the user has not yet sent initial presence then ignore the\n\t\t\t\t\t\t\t// probe.\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tprocessInProbe(packet, session, results, settings, pres_type);\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase out_probe:\n\t\t\t\t\t\tforwardPresence(results, packet, session.getJID());\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase error:\n\t\t\t\t\t\tprocessError(packet, session, results, settings, pres_type);\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet, \"Request type is incorrect\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   false));\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t}    // end of switch (type)\n\t\t\t} catch (NotAuthorizedException e) {\n\t\t\t\tlog.log(Level.CONFIG, \"Can not access user Roster, user session is not authorized yet: {0}\", packet);\n\t\t\t\tlog.log(Level.FINEST, \"presence problem...\", e);\n\t\t\t} catch (TigaseDBException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Error accessing database for presence data: {0}\", e);\n\t\t\t}    // end of try-catch\n\t\t}\n\t}\n\n\t@Override\n\tpublic void stopped(XMPPResourceConnection session, Queue<Packet> results, Map<String, Object> settings) {\n\n\t\t// Synchronization to avoid conflict with login/logout events\n\t\t// processed in the SessionManager asynchronously\n\t\tsynchronized (session) {\n\n\t\t\t// According to the spec and logic actually offline status should\n\t\t\t// not be broadcasted if initial presence was not sent by the client.\n\t\t\ttry {\n\t\t\t\tif (session.getPresence() != null) {\n\n\t\t\t\t\t// If this was called without sending unavailable presence\n\t\t\t\t\t// we have to generate it on our own.\n\t\t\t\t\tElement pres = session.getPresence();\n\n\t\t\t\t\tif (!StanzaType.unavailable.toString().equals(pres.getAttributeStaticStr(Packet.TYPE_ATT))) {\n\t\t\t\t\t\tpres.setAttribute(Packet.TYPE_ATT, StanzaType.unavailable.toString());\n\t\t\t\t\t\tsession.setPresence(pres);\n\t\t\t\t\t}\n\t\t\t\t\tbroadcastOffline(session, results, settings, roster_util);\n\t\t\t\t\tupdateOfflineChange(session, results);\n\t\t\t\t} else {\n\t\t\t\t\tbroadcastDirectPresences(StanzaType.unavailable, session, results, null);\n\t\t\t\t}\n\t\t\t\troster_util.logout(session);\n\t\t\t} catch (NotAuthorizedException e) {\n\n\t\t\t\t// Do nothing, it may happen quite often when the user disconnects\n\t\t\t\t// before it authenticates.\n\t\t\t} catch (TigaseDBException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Error accessing database for offline message: \", e);\n\t\t\t}    // end of try-catch\n\t\t}\n\t}\n\n\t@Override\n\tpublic void getStatistics(StatisticsList list) {\n\t\tsuper.getStatistics(list);\n\t\tlist.add(id(), USERS_STATUS_CHANGES, usersStatusChanges, Level.INFO);\n\t}\n\n\tpublic void rebroadcastPresence(XMPPResourceConnection session, Queue<Packet> results)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tif (session.getPresence() == null) {\n\t\t\t// user has not sent initial presence yet, ignore\n\t\t\treturn;\n\t\t}\n\n\t\tElement presence = session.getPresence().clone();\n\n\t\tfor (ExtendedPresenceProcessorIfc processor : extendedPresenceProcessors) {\n\t\t\tElement extendContent = processor.extend(presence, session, results);\n\t\t\tif (extendContent != null) {\n\t\t\t\t// avoid duplicate\n\t\t\t\tElement child = presence.getChild(extendContent.getName(), extendContent.getXMLNS());\n\t\t\t\tif (child != null) {\n\t\t\t\t\tpresence.removeChild(child);\n\t\t\t\t}\n\t\t\t\tpresence.addChild(extendContent);\n\t\t\t}\n\t\t}\n\n\t\tsendPresenceBroadcast(StanzaType.available, session, FROM_SUBSCRIBED, results, presence, null, getRosterUtil());\n\n\t\tupdateUserResources(presence, session, results, false);\n\n//\t\tsendPresenceBroadcast( StanzaType.get, session, SUB_TO, results, presence, null, null );\n\t}\n\n\t/**\n\t * Sends out all pending subscription request during user log-in.\n\t *\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t *\n\t */\n\tpublic void resendPendingInRequests(XMPPResourceConnection session, Queue<Packet> results)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tJID[] buddies = roster_util.getBuddies(session, RosterAbstract.PENDING_IN);\n\n\t\tif (buddies != null) {\n\t\t\tfor (JID buddy : buddies) {\n\t\t\t\tElement presence = new Element(PRESENCE_ELEMENT_NAME);\n\n\t\t\t\tpresence.setAttribute(\"type\", StanzaType.subscribe.toString());\n\t\t\t\tpresence.setXMLNS(XMLNS);\n\n\t\t\t\tPacket pres = Packet.packetInstance(presence, buddy, null);\n\n\t\t\t\tupdatePresenceChange(pres, session, results);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Process presence stanza of type Error. Allows errors sent from server to user and ignore presence errors sent\n\t * from the user.\n\t *\n\t * @param packet packet is which being processed.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param settings this map keeps plugin specific settings loaded from the Tigase server configuration.\n\t * @param presenceType specifies type of the presence\n\t *\n\t */\n\tprotected void processError(Packet packet, XMPPResourceConnection session, Queue<Packet> results,\n\t\t\t\t\t\t\t\tMap<String, Object> settings, RosterAbstract.PresenceType presenceType)\n\t\t\tthrows NotAuthorizedException, TigaseDBException, NoConnectionIdException {\n\n\t\t// Strategy change.\n\t\t// Now we allow all error presences sent to the user, but we just ignore\n\t\t// presence errors sent from the user\n\t\tif (session.isUserId(packet.getStanzaTo().getBareJID())) {\n\t\t\tPacket result = packet.copyElementOnly();\n\n\t\t\tresult.setPacketTo(session.getConnectionId());\n\t\t\tresult.setPacketFrom(packet.getTo());\n\t\t\tresults.offer(result);\n\t\t} else {\n\n\t\t\t// Ignore....\n\t\t}\n\t}\n\n\t/**\n\t * Method is responsible for processing incoming initial presence (i.e. in the receivers session manager).\n\t * <br>\n\t * It validates the packet (whether from is present or if it's a direct presence to existing resource) and\n\t * subsequently set received presence for the contact that sent it.\n\t *\n\t * @param packet packet is which being processed.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param settings this map keeps plugin specific settings loaded from the Tigase server configuration.\n\t * @param presenceType specifies type of the presence.\n\t *\n\t */\n\tprotected void processInInitial(Packet packet, XMPPResourceConnection session, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\tMap<String, Object> settings, RosterAbstract.PresenceType presenceType)\n\t\t\tthrows NoConnectionIdException, NotAuthorizedException, TigaseDBException {\n\t\tif (packet.getStanzaFrom() == null) {\n\n\t\t\t// That really happened already. It looks like a bug in tigase\n\t\t\t// let's try to catch it here....\n\t\t\tlog.log(Level.WARNING, \"Initial presence without from attribute set: {0}\", packet);\n\n\t\t\treturn;\n\t\t}\n\n\t\t// If this is a direct presence to a resource which is already gone\n\t\t// Ignore it.\n\t\tString resource = packet.getStanzaTo().getResource();\n\n\t\tif ((resource != null) && !resource.isEmpty()) {\n\t\t\tXMPPResourceConnection direct = session.getParentSession().getResourceForResource(resource);\n\n\t\t\tif (direct != null) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Received direct presence from: {0} to: {1}\",\n\t\t\t\t\t\t\tnew Object[]{packet.getStanzaFrom(), packet.getStanzaTo()});\n\t\t\t\t}\n\n\t\t\t\t// Send a direct presence to correct resource, otherwise ignore\n\t\t\t\tPacket result = packet.copyElementOnly();\n\n\t\t\t\tresult.setPacketTo(direct.getConnectionId());\n\t\t\t\tresult.setPacketFrom(packet.getTo());\n\t\t\t\tresults.offer(result);\n\t\t\t} else {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Ignoring direct presence from: {0} to: {1}, resource gone.\",\n\t\t\t\t\t\t\tnew Object[]{packet.getStanzaFrom(), packet.getStanzaTo()});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tboolean online = StanzaType.unavailable != packet.getType();\n\n\t\tbuddyOnline(session, packet.getStanzaFrom(), results, online);\n\t\tif (session.getPresence() == null) {\n\n\t\t\t// Just ignore, this user does not want to receive presence updates\n\t\t\treturn;\n\t\t}\n\n\t\tJID presBuddy = packet.getStanzaFrom().copyWithoutResource();\n\n\t\t// If other users are in 'to' or 'both' contacts, broadcast\n\t\t// their presences to all active resources\n\t\tElement dynItem;\n\n\t\ttry {\n\t\t\tdynItem = DynamicRoster.getBuddyItem(session, settings, presBuddy);\n\t\t} catch (RosterRetrievingException | RepositoryAccessException ex) {\n\t\t\tdynItem = null;\n\t\t}\n\t\tif (roster_util.isSubscribedTo(session, presBuddy) || (dynItem != null)) {\n\t\t\tRosterElement rel = roster_util.getRosterElement(session, presBuddy);\n\n\t\t\tif (rel != null) {\n\t\t\t\trel.setLastSeen(System.currentTimeMillis());\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Received initial presence, setting buddy: {0} online status to: {1}\",\n\t\t\t\t\t\tnew Object[]{packet.getStanzaFrom(), online});\n\t\t\t}\n\t\t}\n\t\tupdatePresenceChange(packet, session, results);\n\t}\n\n\t/**\n\t * Method is responsible for processing incoming presence probe (i.e. in the receivers session manager).\n\t * <br>\n\t * It validates whether the packet comes from a contact that has correct subscription and responds with presence of\n\t * all user's resources presences.\n\t *\n\t * @param packet packet is which being processed.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param settings this map keeps plugin specific settings loaded from the Tigase server configuration.\n\t * @param presenceType specifies type of the presence.\n\t *\n\t */\n\tprotected void processInProbe(Packet packet, XMPPResourceConnection session, Queue<Packet> results,\n\t\t\t\t\t\t\t\t  Map<String, Object> settings, RosterAbstract.PresenceType presenceType)\n\t\t\tthrows NotAuthorizedException, TigaseDBException, PacketErrorTypeException {\n\t\tRosterAbstract.SubscriptionType buddy_subscr = null;\n\t\tElement dynItem;\n\n\t\ttry {\n\t\t\tdynItem = DynamicRoster.getBuddyItem(session, settings, packet.getStanzaFrom());\n\t\t} catch (RosterRetrievingException | RepositoryAccessException ex) {\n\t\t\tdynItem = null;\n\t\t}\n\t\tif (dynItem != null) {\n\t\t\tbuddy_subscr = RosterAbstract.SubscriptionType.both;\n\t\t} else {\n\t\t\tbuddy_subscr = roster_util.getBuddySubscription(session, packet.getStanzaFrom());\n\t\t}\n\t\tif (buddy_subscr == null) {\n\t\t\tbuddy_subscr = RosterAbstract.SubscriptionType.none;\n\t\t}    // end of if (buddy_subscr == null)\n\t\tif (roster_util.isSubscribedFrom(buddy_subscr)) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Received probe, setting buddy: {0} as online.\", packet.getStanzaFrom());\n\t\t\t}\n\n\t\t\t// Probe is usually without resource anyway, so probably more correct approach would\n\t\t\t// be to remove below.\n\t\t\tif (packet.getStanzaFrom().getResource() != null) {\n\t\t\t\troster_util.setOnline(session, packet.getStanzaFrom(), true);\n\t\t\t}\n\t\t\tfor (XMPPResourceConnection conn : session.getActiveSessions()) {\n\t\t\t\ttry {\n\t\t\t\t\tElement pres = conn.getPresence();\n\n\t\t\t\t\tif (pres != null) {\n\t\t\t\t\t\tJID to = probeFullJID ? packet.getStanzaFrom(): packet.getStanzaFrom().copyWithoutResource();\n\t\t\t\t\t\tsendPresence(null, conn.getJID(), to, results, pres);\n\t\t\t\t\t\troster_util.setPresenceSent(session, packet.getStanzaFrom(), true);\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Received probe, sending presence response to: {0}\", to);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (NotAuthorizedException | TigaseDBException e) {\n\n\t\t\t\t\t// It might be quite possible that one of the user connections\n\t\t\t\t\t// is in state not allowed for sending presence, in such a case\n\t\t\t\t\t// none of user connections would receive presence.\n\t\t\t\t\t// This catch is to make sure all other resources receive\n\t\t\t\t\t// notification.\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// acording to spec 4.3.2. Server Processing of Inbound Presence Probe\n\t\t\t// http://xmpp.org/rfcs/rfc6121.html#presence-probe-inbound\n\t\t\t// If the user's bare JID is in the contact's roster with a subscription\n\t\t\t// state other\n\t\t\t// than \"From\", \"From + Pending Out\", or \"Both\", then the contact's server\n\t\t\t// SHOULD return a presence stanza of type \"unsubscribed\"\n\t\t\tif (!isAllowedForPresenceProbe(session, packet.getStanzaFrom())) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\"Received probe, users bare JID: {0} is not in the roster. Responding with unsubscribed\",\n\t\t\t\t\t\t\tpacket.getStanzaFrom().getBareJID());\n\t\t\t\t}\n\t\t\t\tsendPresence(StanzaType.unsubscribed, session.getBareJID(), packet.getStanzaFrom().getBareJID(),\n\t\t\t\t\t\t\t results, null);\n\t\t\t} else {\n\t\t\t\t// However, if a server receives a presence probe from a configured\n\t\t\t\t// domain of the server itself or another such trusted service, it MAY\n\t\t\t\t// provide presence information about the user to that entity.\n\t\t\t\tfor (XMPPResourceConnection conn : session.getActiveSessions()) {\n\t\t\t\t\tElement pres = conn.getPresence();\n\n\t\t\t\t\tif (pres != null) {\n\t\t\t\t\t\tsendPresence(null, conn.getJID(), packet.getStanzaFrom(), results, pres);\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Received probe, sending presence response to: {0}\",\n\t\t\t\t\t\t\t\t\tpacket.getStanzaFrom());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}    // end of if (roster_util.isSubscribedFrom(session, packet.getElemFrom()))\n\t}\n\n\t/**\n\t * Method is responsible for processing outgoing initial presence (i.e. in the sender session manager).\n\t * <br>\n\t * Process packet accordingly whether it's a direct presence (forward it, add to proper collection of JIDs to which\n\t * a direct presence has been sent) or regular presence. THe latter causes properly address the packet, store\n\t * presence within session data for subsequent use, and for the first availability presence (in case there is no\n\t * prior presence stored in user session data) server sends probes to all contacts and pushes out all pending\n\t * subscription request or (if there i already presence stored in session data) broadcast presence update to\n\t * contacts.\n\t * <br>\n\t * If there is a JID forwarding set up, presence is also forwarded to configured JID.\n\t *\n\t * @param packet packet is which being processed.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param settings this map keeps plugin specific settings loaded from the Tigase server configuration.\n\t * @param type specifies type of the presence.\n\t *\n\t */\n\tprotected void processOutInitial(Packet packet, XMPPResourceConnection session, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t Map<String, Object> settings, RosterAbstract.PresenceType type)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\n\t\t// Is it a direct presence to some entity on the network?\n\t\tif (packet.getStanzaTo() != null) {\n\n\t\t\tPacket presence = packet.copyElementOnly();\n\t\t\tElement presenceEl = presence.getElement();\n\t\t\tfor (ExtendedPresenceProcessorIfc processor : extendedPresenceProcessors) {\n\t\t\t\tElement extendContent = processor.extend(presenceEl, session, results);\n\t\t\t\tif (extendContent != null) {\n\t\t\t\t\tpresenceEl.addChild(extendContent);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Yes this is it, send direct presence\n\t\t\tresults.offer(presence);\n\n\t\t\t// If this is unavailable presence, remove jid from Set\n\t\t\t// otherwise add it to the Set\n\t\t\tif (packet.getType() == StanzaType.unavailable) {\n\t\t\t\tremoveDirectPresenceJID(packet.getStanzaTo(), session);\n\t\t\t} else {\n\t\t\t\taddDirectPresenceJID(packet.getStanzaTo(), session);\n\t\t\t}\n\t\t} else {\n\t\t\t++usersStatusChanges;\n\n\t\t\tboolean first = false;\n\n\t\t\tif (session.getPresence() == null) {\n\t\t\t\tfirst = true;\n\t\t\t}\n\t\t\tPacket resultPacket = packet.copyElementOnly();\n\t\t\tresultPacket.initVars(session.getJID(), packet.getStanzaTo());\n\t\t\tfinal Element presenceEl = resultPacket.getElement();\n\n\t\t\tfor (ExtendedPresenceProcessorIfc processor : extendedPresenceProcessors) {\n\t\t\t\tElement extendContent = processor.extend(presenceEl, session, results);\n\t\t\t\tif (extendContent != null) {\n\t\t\t\t\tpresenceEl.addChild(extendContent);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Store user presence for later time...\n\t\t\t// To send response to presence probes for example.\n\t\t\tsession.setPresence(presenceEl);\n\n\t\t\t// here we need to check if roster is loaded\n\t\t\tif (!rosterLazyLoading || roster_util.isRosterLoaded(session)) {\n\t\t\t\t// if it is already loaded then continue processing\n\t\t\t\t// Special actions on the first availability presence\n\t\t\t\tif ((packet.getType() == null) || (packet.getType() == StanzaType.available)) {\n\t\t\t\t\tsession.removeSessionData(OFFLINE_BUD_SENT);\n\t\t\t\t\tsession.removeSessionData(OFFLINE_RES_SENT);\n\t\t\t\t\tif (first) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tsendRosterOfflinePresence(session, results);\n\t\t\t\t\t\t} catch (NotAuthorizedException | TigaseDBException | NoConnectionIdException ex) {\n\t\t\t\t\t\t\tlog.log(Level.CONFIG, \"Experimental code throws exception: \", ex);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Send presence probes to 'to' or 'both' contacts\n\t\t\t\t\t\tbroadcastProbe(session, results, settings);\n\n\t\t\t\t\t\t// Resend pending in subscription requests\n\t\t\t\t\t\tresendPendingInRequests(session, results);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Broadcast initial presence to 'from' or 'both' contacts\n\t\t\t\t\t\tsendPresenceBroadcast(StanzaType.available, session, FROM_SUBSCRIBED, results, presenceEl,\n\t\t\t\t\t\t\t\t\t\t\t  settings, roster_util);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Broadcast initial presence to other available user resources\n\t\t\t\t\tupdateUserResources(presenceEl, session, results, first);\n\t\t\t\t} else {\n\t\t\t\t\tstopped(session, results, settings);\n\t\t\t\t}\n\n\t\t\t\t// Presence forwarding\n\t\t\t\tJID forwardTo = session.getDomain().getPresenceForward();\n\n\t\t\t\tif (forwardTo == null) {\n\t\t\t\t\tforwardTo = presenceGLobalForward;\n\t\t\t\t}\n\t\t\t\tif (forwardTo != null) {\n\t\t\t\t\tsendPresence(null, session.getJID(), forwardTo, results, presenceEl);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// if roster is not yet loaded we need to trigger roster load by Roster plugin\n\t\t\t\tElement iq = new Element(Iq.ELEM_NAME, new String[]{\"type\"}, new String[]{\"set\"});\n\t\t\t\tElement query = new Element(Iq.QUERY_NAME, new String[]{Iq.XMLNS_ATT},\n\t\t\t\t\t\t\t\t\t\t\tnew String[]{RosterAbstract.XMLNS_LOAD});\n\t\t\t\tiq.addChild(query);\n\t\t\t\tPacket loadCmd = Packet.packetInstance(iq, packet.getStanzaFrom(), packet.getStanzaTo());\n\t\t\t\tloadCmd.setPacketFrom(packet.getPacketFrom());\n\t\t\t\tloadCmd.setPacketTo(packet.getPacketTo());\n\t\t\t\tresults.add(loadCmd);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Method sends back presence to contact while it becomes online (i.e. during processing of incoming initial\n\t * presence of the contact/buddy)\n\t *\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param buddy {@link JID} of a roster element for which an online state will be set\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param online set whether given contact is online or offline\n\t *\n\t */\n\tprotected void buddyOnline(XMPPResourceConnection session, JID buddy, Queue<Packet> results, boolean online)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\troster_util.setOnline(session, buddy, online);\n\t\tif (online && skipOffline && !roster_util.presenceSent(session, buddy) &&\n\t\t\t\troster_util.isSubscribedFrom(session, buddy)) {\n\t\t\tElement pres = session.getPresence();\n\n\t\t\tif (pres != null) {\n\t\t\t\tsendPresence(null, session.getJID(), buddy, results, pres);\n\t\t\t\troster_util.setPresenceSent(session, buddy, true);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Method sends server generated presence unavailable for all buddies from the roster with a custom status message.\n\t *\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t *\n\t */\n\t@SuppressWarnings(\"empty-statement\")\n\tprotected void sendRosterOfflinePresence(XMPPResourceConnection session, Queue<Packet> results)\n\t\t\tthrows NotAuthorizedException, TigaseDBException, NoConnectionIdException {\n\t\tif (offlineRosterLastSeen == null) {\n\t\t\tlog.finest(\"No clients specified in config, skipping...\");\n\n\t\t\treturn;\n\t\t}\n\n\t\tElement pres = session.getPresence();\n\n\t\tif (pres == null) {\n\t\t\tlog.finest(\"Presence not set yet, skipping...\");\n\n\t\t\treturn;\n\t\t}\n\n\t\tString node = pres.getAttributeStaticStr(PRESENCE_C_PATH, \"node\");\n\n\t\tif (node == null) {\n\t\t\tlog.finest(\"Presence node not set, skipping...\");\n\n\t\t\treturn;\n\t\t}\n\n\t\tboolean validClient = false;\n\t\tint i = 0;\n\n\t\tif (offlineRosterLastSeen.length > 0 && !offlineRosterLastSeen[0].equals(\"*\")) {\n\t\t\twhile ((i < offlineRosterLastSeen.length) && !(validClient |= node.contains(offlineRosterLastSeen[i++]))) {\n\t\t\t\t;\n\t\t\t}\n\t\t\tif (!validClient) {\n\t\t\t\tlog.finest(\"Client does not match, skipping...\");\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else {\n\t\t\t// enabled for all clients\n\t\t}\n\n\t\tJID[] buddies = roster_util.getBuddies(session, TO_SUBSCRIBED);\n\n\t\tif (buddies != null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Buddies found: {0}\", Arrays.toString(buddies));\n\t\t\t}\n\n\t\t\tPriority pack_priority = Priority.PRESENCE;\n\t\t\tint pres_cnt = 0;\n\n\t\t\tfor (JID buddy : buddies) {\n\t\t\t\tList<Element> children = roster_util.getCustomChildren(session, buddy);\n\n\t\t\t\tif (children != null && !children.isEmpty()) {\n\t\t\t\t\tPacket pack = sendPresence(StanzaType.unavailable, buddy, session.getJID(), results, null);\n\n\t\t\t\t\tif (pres_cnt == HIGH_PRIORITY_PRESENCES_NO) {\n\t\t\t\t\t\t++pres_cnt;\n\t\t\t\t\t\tpack_priority = Priority.LOWEST;\n\t\t\t\t\t}\n\t\t\t\t\tpack.setPriority(pack_priority);\n\t\t\t\t\tpack.setPacketTo(session.getConnectionId());\n\t\t\t\t\tfor (Element child : children) {\n\t\t\t\t\t\tpack.getElement().addChild(child);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}    // end of for (String buddy: buddies)\n\t\t}\n\t}\n\n\tprivate boolean isAllowedForPresenceProbe(XMPPResourceConnection session, JID jid) {\n\t\tif (jid == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn session.getDomain().isTrustedJID(jid);\n\t}\n\n\tpublic interface ExtendedPresenceProcessorIfc {\n\n\t\tdefault Element extend(Element presence, XMPPResourceConnection session, Queue<Packet> results) {\n\t\t\treturn extend(session, results);\n\t\t}\n\n\t\tElement extend(XMPPResourceConnection session, Queue<Packet> results);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/PresenceSubscription.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Command;\nimport tigase.server.Packet;\nimport tigase.server.PolicyViolationException;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.vhosts.*;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.annotation.Handle;\nimport tigase.xmpp.impl.annotation.Id;\nimport tigase.xmpp.impl.roster.RosterAbstract;\nimport tigase.xmpp.impl.roster.RosterAbstract.PresenceType;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.xmpp.impl.roster.RosterAbstract.SUB_NONE;\n\n/**\n * @author andrzej\n */\n@Id(PresenceSubscription.ID)\n@Handle(path = {PresenceAbstract.PRESENCE_ELEMENT_NAME}, xmlns = PresenceAbstract.CLIENT_XMLNS)\n@Bean(name = PresenceSubscription.ID, parent = SessionManager.class, active = true)\npublic class PresenceSubscription\n\t\textends PresenceAbstract {\n\n\t/**\n\t * key allowing enabling automatic authorisation.\n\t */\n\tpublic static final String AUTO_AUTHORIZE_PROP_KEY = \"auto-authorize\";\n\tprotected static final String ID = \"presence-subscription\";\n\tprivate static final Logger log = Logger.getLogger(PresenceSubscription.class.getCanonicalName());\n\tprivate static final Set<StanzaType> TYPES = new HashSet<>(\n\t\t\tArrays.asList(StanzaType.subscribe, StanzaType.subscribed, StanzaType.unsubscribe,\n\t\t\t\t\t\t  StanzaType.unsubscribed));\n\t/**\n\t * variable holding setting regarding auto authorisation of items added to user roset\n\t */\n\t@ConfigField(desc = \"Automatically authorize subscription requests\", alias = AUTO_AUTHORIZE_PROP_KEY)\n\tprivate static boolean autoAuthorize = false;\n\t\n\t@Inject(nullAllowed = true)\n\tprotected VHostManagerIfc vHostManager = null;\n\n\t@Override\n\tpublic Set<StanzaType> supTypes() {\n\t\treturn TYPES;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t * <br>\n\t * Performs processing of <em>presence</em> packets and calls different methods for particular {@link PresenceType}\n\t */\n\t@SuppressWarnings({\"unchecked\", \"fallthrough\"})\n\t@Override\n\tpublic void process(final Packet packet, final XMPPResourceConnection session, final NonAuthUserRepository repo,\n\t\t\t\t\t\tfinal Queue<Packet> results, final Map<String, Object> settings) throws XMPPException {\n\t\tif (session == null) {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"Session is null, ignoring packet: {0}\", packet);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}    // end of if (session == null)\n\t\tif (!session.isAuthorized()) {\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE, \"Session is not authorized, ignoring packet: {0}\", packet);\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\t// Synchronization to avoid conflict with login/logout events\n\t\t// processed in the SessionManager asynchronously\n\t\tsynchronized (session) {\n\t\t\ttry {\n\t\t\t\tRosterAbstract.PresenceType pres_type = roster_util.getPresenceType(session, packet);\n\n\t\t\t\tif (pres_type == null) {\n\t\t\t\t\tlog.log(Level.CONFIG, \"Invalid presence found: {0}\", packet);\n\n\t\t\t\t\treturn;\n\t\t\t\t}    // end of if (type == null)\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"{0} | {1} presence found: {2}\",\n\t\t\t\t\t\t\tnew Object[]{session.getBareJID().toString(), pres_type, packet});\n\t\t\t\t}\n\n\t\t\t\t// All 'in' subscription presences must have a valid from address\n\t\t\t\tswitch (pres_type) {\n\t\t\t\t\tcase in_unsubscribe:\n\t\t\t\t\tcase in_subscribe:\n\t\t\t\t\tcase in_unsubscribed:\n\t\t\t\t\tcase in_subscribed:\n\t\t\t\t\t\tif (packet.getStanzaFrom() == null) {\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\t\t\tlog.fine(\"'in' subscription presence without valid 'from' address, \" +\n\t\t\t\t\t\t\t\t\t\t\t\t \"dropping packet: \" + packet);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (session.isUserId(packet.getStanzaFrom().getBareJID())) {\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\t\t\tlog.log(Level.FINE, \"''in'' subscription to myself, not allowed, returning \" +\n\t\t\t\t\t\t\t\t\t\t\"error for packet: \" + \"{0}\", packet);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tresults.offer(Authorization.NOT_ALLOWED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"You can not subscribe to yourself.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   false));\n\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// as per http://xmpp.org/rfcs/rfc6121.html#sub\n\t\t\t\t\t\t// Implementation Note: When a server processes or generates an outbound\n\t\t\t\t\t\t// presence stanza of type \"subscribe\", \"subscribed\", \"unsubscribe\",\n\t\t\t\t\t\t// or \"unsubscribed\", the server MUST stamp the outgoing presence\n\t\t\t\t\t\t// stanza with the bare JID <localpart@domainpart> of the sending entity,\n\t\t\t\t\t\t// not the full JID <localpart@domainpart/resourcepart>.\n\t\t\t\t\t\t//\n\t\t\t\t\t\t// we enforce this rule also for incomming presence subscirption packets\n\t\t\t\t\t\tpacket.initVars(packet.getStanzaFrom().copyWithoutResource(),\n\t\t\t\t\t\t\t\t\t\tsession.getJID().copyWithoutResource());\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase out_subscribe:\n\t\t\t\t\tcase out_unsubscribe:\n\t\t\t\t\tcase out_subscribed:\n\t\t\t\t\tcase out_unsubscribed:\n\n\t\t\t\t\t\t// Check wheher the destination address is correct to prevent\n\t\t\t\t\t\t// broken/corrupted roster entries:\n\t\t\t\t\t\tif ((packet.getStanzaTo() == null) || packet.getStanzaTo().toString().isEmpty()) {\n\t\t\t\t\t\t\tresults.offer(Authorization.JID_MALFORMED.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"The destination address is incorrect.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t false));\n\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// According to RFC 3921 draft bis-3, both source and destination\n\t\t\t\t\t\t// addresses must be BareJIDs, handled by initVars(...)\n\t\t\t\t\t\tpacket.initVars(session.getJID().copyWithoutResource(),\n\t\t\t\t\t\t\t\t\t\tpacket.getStanzaTo().copyWithoutResource());\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tswitch (pres_type) {\n\t\t\t\t\tcase out_subscribe:\n\t\t\t\t\tcase out_unsubscribe:\n\t\t\t\t\t\tprocessOutSubscribe(packet, session, results, settings, pres_type);\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase out_subscribed:\n\t\t\t\t\tcase out_unsubscribed:\n\t\t\t\t\t\tprocessOutSubscribed(packet, session, results, settings, pres_type);\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase in_subscribe:\n\t\t\t\t\t\tprocessInSubscribe(packet, session, results, settings, pres_type);\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase in_unsubscribe:\n\t\t\t\t\t\tprocessInUnsubscribe(packet, session, results, settings, pres_type);\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase in_subscribed:\n\t\t\t\t\t\tprocessInSubscribed(packet, session, results, settings, pres_type);\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase in_unsubscribed:\n\t\t\t\t\t\tprocessInUnsubscribed(packet, session, results, settings, pres_type);\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet, \"Request type is incorrect\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   false));\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t}    // end of switch (type)\n\t\t\t} catch (NotAuthorizedException e) {\n\t\t\t\tlog.log(Level.CONFIG, \"Can not access user Roster, user session is not authorized yet: {0}\", packet);\n\t\t\t\tlog.log(Level.FINEST, \"presence problem...\", e);\n\t\t\t} catch (PolicyViolationException e) {\n\t\t\t\tlog.log(Level.FINE, \"Violation of roster items number policy: {0}\", packet);\n\t\t\t} catch (TigaseDBException e) {\n\t\t\t\tlog.log(Level.WARNING, \"Error accessing database for presence data: {0}\", e);\n\t\t\t}    // end of try-catch\n\t\t}\n\n\t}\n\n\t/**\n\t * Method is responsible for processing incoming subscription request (i.e. in the receivers session manager).\n\t * <br>\n\t * If the contact is already subscribed the an auto-reply with type='subscribded' is sent, otherwise contact is\n\t * added to the roster (if it's missing/there is no current subscription), sets the subscription type to {@code\n\t * PresenceType.in_subscribe} and subsequently broadcast presence update to all connected resources.\n\t *\n\t * @param packet packet is which being processed.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param settings this map keeps plugin specific settings loaded from the Tigase server configuration.\n\t * @param pres_type specifies type of the presence.\n\t *\n\t */\n\tprotected void processInSubscribe(Packet packet, XMPPResourceConnection session, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t  Map<String, Object> settings, RosterAbstract.PresenceType pres_type)\n\t\t\tthrows NotAuthorizedException, TigaseDBException, NoConnectionIdException, PolicyViolationException {\n\n\t\t// If the buddy is already subscribed then auto-reply with subscribed\n\t\t// presence stanza.\n\t\tif (roster_util.isSubscribedFrom(session, packet.getStanzaFrom())) {\n\t\t\tsendPresence(StanzaType.subscribed, session.getJID().copyWithoutResource(), packet.getStanzaFrom(), results,\n\t\t\t\t\t\t null);\n\t\t} else {\n\t\t\tRosterAbstract.SubscriptionType curr_sub = roster_util.getBuddySubscription(session,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpacket.getStanzaFrom());\n\n\t\t\tif (curr_sub == null) {\n\t\t\t\troster_util.addBuddy(session, packet.getStanzaFrom(), null, null,null, null);\n\t\t\t}    // end of if (curr_sub == null)\n\t\t\tfinal boolean preApproved = roster_util.isPreApproved(session, packet.getStanzaFrom());\n\t\t\troster_util.updateBuddySubscription(session, pres_type, packet.getStanzaFrom());\n\t\t\tif (!isAutoAuthorizeEnabled(session.getJID().getDomain())) {\n\t\t\t\t// broadcast the subscription request only if it wasn't pre-approved\n\t\t\t\tif (!preApproved) {\n\t\t\t\t\tupdatePresenceChange(packet, session, results);\n\t\t\t\t} else {\n\t\t\t\t\t// was pre-approved, update status and send probe\n\t\t\t\t\tfinal Element buddyItem = roster_util.getBuddyItem(session,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   packet.getStanzaFrom().copyWithoutResource());\n\t\t\t\t\troster_util.updateBuddyChange(session, results, buddyItem);\n\t\t\t\t\tbroadcastProbe(session, results, settings);\n\t\t\t\t\tsendPresence(StanzaType.subscribed, session.getJID(), packet.getStanzaFrom(), results, null);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\troster_util.setBuddySubscription(session, RosterAbstract.SubscriptionType.both,\n\t\t\t\t\t\t\t\t\t\t\t\t packet.getStanzaFrom().copyWithoutResource());\n\t\t\t}\n\t\t}    // end of else\n\t\tif (isAutoAuthorizeEnabled(session.getJID().getDomain())) {\n\t\t\tfinal Element buddyItem = roster_util.getBuddyItem(session, packet.getStanzaFrom().copyWithoutResource());\n\t\t\troster_util.updateBuddyChange(session, results, buddyItem);\n\t\t\tbroadcastProbe(session, results, settings);\n\t\t\tsendPresence(StanzaType.subscribed, session.getJID(), packet.getStanzaFrom(), results, null);\n\t\t}\n\t}\n\n\t/**\n\t * Method is responsible for processing incoming subscribed presence (i.e. in the receivers session manager).\n\t * <br>\n\t * Contact is added to the roster (if it's missing/there is no current subscription), sets the subscription type to\n\t * {@code PresenceType.in_subscribed} and subsequently, if subscription has changed,forwards the presence to user\n\t * resource connection as well as broadcast presence update to all connected resources.\n\t *\n\t * @param packet packet is which being processed.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param settings this map keeps plugin specific settings loaded from the Tigase server configuration.\n\t * @param pres_type specifies type of the presence.\n\t *\n\t */\n\tprotected void processInSubscribed(Packet packet, XMPPResourceConnection session, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t   Map<String, Object> settings, RosterAbstract.PresenceType pres_type)\n\t\t\tthrows NotAuthorizedException, TigaseDBException, NoConnectionIdException, PolicyViolationException {\n\t\tRosterAbstract.SubscriptionType curr_sub = roster_util.getBuddySubscription(session, packet.getStanzaFrom());\n\n\t\tif (!isAutoAuthorizeEnabled(session.getJID().getDomain()) && (curr_sub == null)) {\n\t\t\troster_util.addBuddy(session, packet.getStanzaFrom(), null, null, null, null);\n\t\t}    // end of if (curr_sub == null)\n\n\t\tboolean subscr_changed = roster_util.updateBuddySubscription(session, pres_type, packet.getStanzaFrom());\n\n\t\tif (subscr_changed) {\n\t\t\tPacket forward_p = packet.copyElementOnly();\n\n\t\t\tforward_p.setPacketTo(session.getConnectionId());\n\t\t\tresults.offer(forward_p);\n\t\t\tif (isAutoAuthorizeEnabled(session.getJID().getDomain())) {\n\t\t\t\troster_util.setBuddySubscription(session, RosterAbstract.SubscriptionType.both,\n\t\t\t\t\t\t\t\t\t\t\t\t packet.getStanzaFrom().copyWithoutResource());\n\t\t\t}\n\t\t\troster_util.updateBuddyChange(session, results, roster_util.getBuddyItem(session, packet.getStanzaFrom()));\n\n\t\t\tElement delay = packet.getElement().getChild(\"delay\", \"urn:xmpp:delay\");\n\t\t\tif (delay != null) {\n\t\t\t\t// offline packet, lets send probe\n\t\t\t\tElement presProbe = prepareProbe(session);\n\t\t\t\tsendPresence(null, session.getJID(), packet.getStanzaFrom(), results, presProbe);\n\t\t\t}\n\n\t\t}\n\t}\n\n\t/**\n\t * Method is responsible for processing incoming unsubscribe presence (i.e. in the receivers session manager).\n\t * <br>\n\t * First method performs update of subscription of the given contact and subsequently the request is forwarded to\n\t * the client to make sure it says in synch with the server (in case there was actual change in subscription).\n\t * Lastly a roster push is generated to all connected resources to update them with current state of the roster and\n\t * items subscriptions.\n\t *\n\t * @param packet packet is which being processed.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param settings this map keeps plugin specific settings loaded from the Tigase server configuration.\n\t * @param pres_type specifies type of the presence.\n\t *\n\t */\n\tprotected void processInUnsubscribe(Packet packet, XMPPResourceConnection session, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\tMap<String, Object> settings, RosterAbstract.PresenceType pres_type)\n\t\t\tthrows NotAuthorizedException, TigaseDBException, NoConnectionIdException, PolicyViolationException {\n\t\tboolean subscr_changed = roster_util.updateBuddySubscription(session, pres_type, packet.getStanzaFrom());\n\n\t\tif (subscr_changed) {\n\n\t\t\t// First forward the request to the client to make sure it stays in sync\n\t\t\t// with the server. This should be done only in the case of actual change of the state\n\t\t\t// and with auto-authorization disabled\n\t\t\tif (!isAutoAuthorizeEnabled(session.getJID().getDomain())) {\n\t\t\t\tPacket forward_p = packet.copyElementOnly();\n\n\t\t\t\tforward_p.setPacketTo(session.getConnectionId());\n\t\t\t\tresults.offer(forward_p);\n\t\t\t}\n\n\t\t\tElement item = roster_util.getBuddyItem(session, packet.getStanzaFrom());\n\n\t\t\tif (item != null) {\n\t\t\t\troster_util.updateBuddyChange(session, results, item);\n\t\t\t} else {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Received unsubscribe request from a user who is not in the roster: {0}\",\n\t\t\t\t\t\t\tpacket.getStanzaFrom());\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (isAutoAuthorizeEnabled(session.getJID().getDomain())) {\n\t\t\t\tbroadcastProbe(session, results, settings);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Method is responsible for processing incoming unsubscribed presence (i.e. in the receivers session manager).\n\t * <br>\n\t * First method checks for the current subscription of the contact and if this verifies performs subsequent actions\n\t * such as forwarding presence to the user connection to make sure it says in synch with the server, updates contact\n\t * subscription with {@code PresenceType.in_unsubscribed} and in case that there was a change in user subscription\n\t * send out a roster push to all connected resources to update them with current state of the roster and items\n\t * subscriptions.\n\t *\n\t * @param packet packet is which being processed.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param settings this map keeps plugin specific settings loaded from the Tigase server configuration.\n\t * @param pres_type specifies type of the presence.\n\t *\n\t */\n\tprotected void processInUnsubscribed(Packet packet, XMPPResourceConnection session, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t Map<String, Object> settings, RosterAbstract.PresenceType pres_type)\n\t\t\tthrows NotAuthorizedException, TigaseDBException, NoConnectionIdException, PolicyViolationException {\n\t\tRosterAbstract.SubscriptionType curr_sub = roster_util.getBuddySubscription(session, packet.getStanzaFrom());\n\n\t\tif (curr_sub != null) {\n\n\t\t\t// First forward the request to the client to make sure it stays in sync\n\t\t\t// with the server. This should be done only with auto-authorization disabled\n\t\t\tif (!isAutoAuthorizeEnabled(session.getJID().getDomain())) {\n\t\t\t\tPacket forward_p = packet.copyElementOnly();\n\n\t\t\t\tforward_p.setPacketTo(session.getConnectionId());\n\t\t\t\tresults.offer(forward_p);\n\t\t\t}\n\n\t\t\tboolean subscr_changed = roster_util.updateBuddySubscription(session, pres_type, packet.getStanzaFrom());\n\n\t\t\tif (subscr_changed) {\n\t\t\t\tElement item = roster_util.getBuddyItem(session, packet.getStanzaFrom());\n\n\t\t\t\t// The roster item could have been removed in the meantime....\n\t\t\t\tif (item != null) {\n\t\t\t\t\troster_util.updateBuddyChange(session, results,\n\t\t\t\t\t\t\t\t\t\t\t\t  roster_util.getBuddyItem(session, packet.getStanzaFrom()));\n\t\t\t\t} else {\n\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Received unsubscribe request from a user who is not in the roster: {0}\",\n\t\t\t\t\t\t\t\tpacket.getStanzaFrom());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (isAutoAuthorizeEnabled(session.getJID().getDomain())) {\n\t\t\t\t\tbroadcastProbe(session, results, settings);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Method is responsible for processing outgoing subscribe and unsubscribe presence (i.e. in the sender session\n\t * manager).\n\t * <br>\n\t * Presence packet is forwarded to the destination with the JID stripped from the resource. <p>In case of {@code\n\t * PresenceType.out_subscribe} packet type contact is added to the roster (in case it was missing), a subscription\n\t * state is being updated and, in case there was a change, a roster push is being sent to all user resources.\n\t * <br>\n\t * In case of {@code PresenceType.out_unsubscribe} method updates contact subscription (and generates roster push if\n\t * there was a change) and if the resulting contact subscription is NONE then contact is removed from the roster.\n\t *\n\t * @param packet packet is which being processed.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param settings this map keeps plugin specific settings loaded from the Tigase server configuration.\n\t * @param pres_type specifies type of the presence.\n\t *\n\t */\n\tprotected void processOutSubscribe(Packet packet, XMPPResourceConnection session, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t   Map<String, Object> settings, RosterAbstract.PresenceType pres_type)\n\t\t\tthrows NotAuthorizedException, TigaseDBException, NoConnectionIdException, PolicyViolationException {\n\n\t\t// According to RFC-3921 I must forward all these kind presence\n\t\t// requests, it allows to resynchronize\n\t\t// subscriptions in case of synchronization loss\n\t\tboolean subscr_changed = false;\n\n\t\tforwardPresence(results, packet, session.getJID().copyWithoutResource());\n\n\t\tRosterAbstract.SubscriptionType current_subscription = roster_util.getBuddySubscription(session,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpacket.getStanzaTo());\n\n\t\tif (pres_type == RosterAbstract.PresenceType.out_subscribe) {\n\t\t\tif (current_subscription == null) {\n\t\t\t\troster_util.addBuddy(session, packet.getStanzaTo(), null, null, null, null);\n\t\t\t}    // end of if (current_subscription == null)\n\t\t\tsubscr_changed = roster_util.updateBuddySubscription(session, pres_type, packet.getStanzaTo());\n\t\t\tif (isAutoAuthorizeEnabled(session.getJID().getDomain())) {\n\t\t\t\troster_util.setBuddySubscription(session, RosterAbstract.SubscriptionType.both,\n\t\t\t\t\t\t\t\t\t\t\t\t packet.getStanzaTo().copyWithoutResource());\n\t\t\t}\n\t\t\tif (subscr_changed) {\n\t\t\t\troster_util.updateBuddyChange(session, results,\n\t\t\t\t\t\t\t\t\t\t\t  roster_util.getBuddyItem(session, packet.getStanzaTo()));\n\t\t\t}    // end of if (subscr_changed)\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"out_subscribe: current_subscription = \" + current_subscription);\n\t\t\t}\n\t\t\tif (current_subscription != null) {\n\t\t\t\tsubscr_changed = roster_util.updateBuddySubscription(session, pres_type, packet.getStanzaTo());\n\t\t\t\tcurrent_subscription = roster_util.getBuddySubscription(session, packet.getStanzaTo());\n\t\t\t\tif (subscr_changed) {\n\t\t\t\t\troster_util.updateBuddyChange(session, results,\n\t\t\t\t\t\t\t\t\t\t\t\t  roster_util.getBuddyItem(session, packet.getStanzaTo()));\n\t\t\t\t}    // end of if (subscr_changed)\n\t\t\t\tif (SUB_NONE.contains(current_subscription)) {\n\t\t\t\t\troster_util.removeBuddy(session, packet.getStanzaTo());\n\t\t\t\t}    // end of if (current_subscription == null)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Method is responsible for processing outgoing subscribed and unsubscribed presence (i.e. in the sender session\n\t * manager).\n\t * <br>\n\t * Presence packet is forwarded to the destination with the JID stripped from the resource, a subscription state is\n\t * being updated and, in case there was a change, a roster push is being sent to all user resources. Also, in case\n\t * of presence type out_subscribed server send current presence to the user from each of the contact's available\n\t * resources. For the presence type out_unsubscribed an unavailable presence is sent.\n\t *\n\t * @param packet packet is which being processed.\n\t * @param session user session which keeps all the user session data and also gives an access to the user's\n\t * repository data.\n\t * @param results this a collection with packets which have been generated as input packet processing results.\n\t * @param settings this map keeps plugin specific settings loaded from the Tigase server configuration.\n\t * @param pres_type specifies type of the presence.\n\t *\n\t */\n\tprotected void processOutSubscribed(Packet packet, XMPPResourceConnection session, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\tMap<String, Object> settings, RosterAbstract.PresenceType pres_type)\n\t\t\tthrows NotAuthorizedException, TigaseDBException, NoConnectionIdException, PolicyViolationException {\n\n\t\t// According to RFC-3921 I must forward all these kind presence\n\t\t// requests, it allows to re-synchronize\n\t\t// subscriptions in case of synchronization loss\n\n\t\tElement initial_presence = session.getPresence();\n\t\tJID buddy = packet.getStanzaTo().copyWithoutResource();\n\t\tboolean subscr_changed = roster_util.updateBuddySubscription(session, pres_type, buddy);\n\n\t\tfinal boolean isPreApproved = roster_util.isPreApproved(session, packet.getStanzaTo());\n\t\tif (isAutoAuthorizeEnabled(session.getJID().getDomain()) &&\n\t\t\t\t(pres_type == RosterAbstract.PresenceType.out_subscribed)) {\n\t\t\troster_util.setBuddySubscription(session, RosterAbstract.SubscriptionType.both,\n\t\t\t\t\t\t\t\t\t\t\t buddy.copyWithoutResource());\n\t\t}\n\n\t\t// do not forward the subscribed if it's a pre-approval\n\t\tif (!isPreApproved) {\n\t\t\tforwardPresence(results, packet, session.getJID().copyWithoutResource());\n\t\t}\n\n\t\tif (subscr_changed) {\n\t\t\troster_util.updateBuddyChange(session, results, roster_util.getBuddyItem(session, buddy));\n\t\t\tif (initial_presence != null && !isPreApproved) {\n\t\t\t\tif (pres_type == RosterAbstract.PresenceType.out_subscribed) {\n\n\t\t\t\t\t// The contact's server MUST then also send current presence to the user\n\t\t\t\t\t// from each of the contact's available resources.\n\t\t\t\t\tList<XMPPResourceConnection> activeSessions = session.getActiveSessions();\n\n\t\t\t\t\tfor (XMPPResourceConnection userSessions : activeSessions) {\n\t\t\t\t\t\tElement presence = userSessions.getPresence();\n\n\t\t\t\t\t\tsendPresence(StanzaType.available, userSessions.getjid(), buddy, results, presence);\n\t\t\t\t\t}\n\t\t\t\t\troster_util.setPresenceSent(session, buddy, true);\n\t\t\t\t} else {\n\t\t\t\t\tsendPresence(StanzaType.unavailable, session.getJID(), buddy, results, null);\n\t\t\t\t}\n\t\t\t}    // end of if (subscr_changed)\n\t\t}\n\t}\n\n\tprivate boolean isAutoAuthorizeEnabled(String domain) {\n\n\t\tAUTO_AUTHORIZE_MODE mode = AUTO_AUTHORIZE_MODE.global;\n\t\tif (vHostManager != null) {\n\t\t\tPresenceSubscriptionVHostItemExtension extension = vHostManager.getVHostItem(domain).getExtension(PresenceSubscriptionVHostItemExtension.class);\n\t\t\tif (extension != null) {\n\t\t\t\tmode = extension.getAutoAuthorizeMode();\n\t\t\t}\n\t\t}\n\n\t\tif (AUTO_AUTHORIZE_MODE.global.equals(mode)) {\n\t\t\treturn autoAuthorize;\n\t\t} else {\n\t\t\treturn mode.isEnabled();\n\t\t}\n\t}\n\n\tenum AUTO_AUTHORIZE_MODE {\n\t\tglobal,\n\t\ton(true),\n\t\toff(false);\n\n\t\tprivate static String[] names = null;\n\t\tprivate boolean enabled;\n\n\t\tAUTO_AUTHORIZE_MODE() {\n\t\t\tenabled = false;\n\t\t}\n\n\t\tAUTO_AUTHORIZE_MODE(boolean b) {\n\t\t\tenabled = b;\n\t\t}\n\n\t\tprotected boolean isEnabled() {\n\t\t\treturn enabled;\n\t\t}\n\t}\n\n\t@Bean(name = PresenceSubscriptionVHostItemExtension.ID, parent = VHostItemExtensionManager.class, active = true)\n\tpublic static class PresenceSubscriptionVHostItemExtensionProvider implements VHostItemExtensionProvider<PresenceSubscriptionVHostItemExtension> {\n\n\t\t@Override\n\t\tpublic String getId() {\n\t\t\treturn PresenceSubscriptionVHostItemExtension.ID;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<PresenceSubscriptionVHostItemExtension> getExtensionClazz() {\n\t\t\treturn PresenceSubscriptionVHostItemExtension.class;\n\t\t}\n\t}\n\n\tpublic static class PresenceSubscriptionVHostItemExtension extends AbstractVHostItemExtension<PresenceSubscriptionVHostItemExtension> implements VHostItemExtensionBackwardCompatible<PresenceSubscriptionVHostItemExtension> {\n\n\t\tpublic static final String ID = \"presence-subscription\";\n\n\t\tprivate AUTO_AUTHORIZE_MODE autoAuthorizeMode = AUTO_AUTHORIZE_MODE.global;\n\n\t\tpublic AUTO_AUTHORIZE_MODE getAutoAuthorizeMode() {\n\t\t\treturn autoAuthorizeMode;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getId() {\n\t\t\treturn ID;\n\t\t}\n\n\t\t@Override\n\t\tpublic void initFromElement(Element item) {\n\t\t\tString tmp = item.getAttributeStaticStr(AUTO_AUTHORIZE_PROP_KEY);\n\t\t\tif (tmp != null) {\n\t\t\t\tautoAuthorizeMode = AUTO_AUTHORIZE_MODE.valueOf(tmp);\n\t\t\t} else {\n\t\t\t\tautoAuthorizeMode = AUTO_AUTHORIZE_MODE.global;\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void initFromCommand(String prefix, Packet packet) throws IllegalArgumentException {\n\t\t\tString tmp = Command.getFieldValue(packet, prefix + \"-\" + AUTO_AUTHORIZE_PROP_KEY);\n\t\t\tautoAuthorizeMode = AUTO_AUTHORIZE_MODE.global;\n\t\t\tif (tmp != null) {\n\t\t\t\tif (Boolean.parseBoolean(tmp)) {\n\t\t\t\t\tautoAuthorizeMode = AUTO_AUTHORIZE_MODE.on;\n\t\t\t\t} else {\n\t\t\t\t\tautoAuthorizeMode = AUTO_AUTHORIZE_MODE.off;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic String toDebugString() {\n\t\t\treturn \"mode: \" + autoAuthorizeMode;\n\t\t}\n\n\t\t@Override\n\t\tpublic Element toElement() {\n\t\t\tif (autoAuthorizeMode == AUTO_AUTHORIZE_MODE.global) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t\n\t\t\tElement el = new Element(getId());\n\t\t\tel.setAttribute(AUTO_AUTHORIZE_PROP_KEY, autoAuthorizeMode.name());\n\t\t\treturn el;\n\t\t}\n\n\t\t@Override\n\t\tpublic void addCommandFields(String prefix, Packet packet, boolean forDefault) {\n\t\t\tElement commandEl = packet.getElemChild(Command.COMMAND_EL, Command.XMLNS);\n\t\t\tBoolean value = null;\n\t\t\tswitch (autoAuthorizeMode) {\n\t\t\t\tcase global:\n\t\t\t\t\tbreak;\n\t\t\t\tcase off:\n\t\t\t\t\tvalue = false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase on:\n\t\t\t\t\tvalue = true;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\taddBooleanFieldWithDefaultToCommand(commandEl, prefix + \"-\" + AUTO_AUTHORIZE_PROP_KEY,\n\t\t\t\t\t\t\t\t\t\t\t\t\"Automatically authorize subscription requests\", value, forDefault);\n\t\t}\n\n\t\t@Override\n\t\tpublic PresenceSubscriptionVHostItemExtension mergeWithDefaults(\n\t\t\t\tPresenceSubscriptionVHostItemExtension defaults) {\n\t\t\treturn autoAuthorizeMode == AUTO_AUTHORIZE_MODE.global ? defaults : this;\n\t\t}\n\n\t\t@Override\n\t\tpublic void initFromData(Map<String, Object> data) {\n\t\t\tString tmp = (String) data.remove(AUTO_AUTHORIZE_PROP_KEY);\n\t\t\tif (tmp == null) {\n\t\t\t\tautoAuthorizeMode = AUTO_AUTHORIZE_MODE.global;\n\t\t\t} else {\n\t\t\t\tautoAuthorizeMode = AUTO_AUTHORIZE_MODE.valueOf(tmp);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/Privacy.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.TigaseDBException;\nimport tigase.xml.DomBuilderHandler;\nimport tigase.xml.Element;\nimport tigase.xml.SimpleParser;\nimport tigase.xml.SingletonFactory;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.impl.roster.RosterFactory;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\n/**\n * Class defining data structure for privacy lists. Sample data storage: {@code <node name=\"privacy\"> <map> <entry\n * value=\"private\" type=\"String\" key=\"default\"/> </map> List name: <node name=\"private\"> <map/> Item order: <node\n * name=\"1\"> <map> <entry value=\"jid\" type=\"String\" key=\"type\"/> <entry value=\"user%40domain.com/res\" type=\"String\"\n * key=\"value\"/> <entry value=\"deny\" type=\"String\" key=\"action\"/> <entry type=\"String[]\" key=\"stanzas\"> <item\n * value=\"message\"/> <item value=\"iq\"/> </entry> </map> </node> </node> }\n * <br>\n * Created: Mon Oct  9 20:50:09 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\npublic class Privacy {\n\n\tpublic static final String ACTION = \"action\";\n\n\tpublic static final String ACTIVE = \"active-list\";\n\n\tpublic static final String DEFAULT = \"default-list\";\n\n\tpublic static final String ITEM = \"item\";\n\n\tpublic static final String LIST = \"list\";\n\n\tpublic static final String NAME = \"name\";\n\n\tpublic static final String ORDER = \"order\";\n\n\tpublic static final String PRIVACY = \"privacy\";\n\n\tpublic static final String PRIVACY_LIST = \"privacy-list\";\n\n\tpublic static final String STANZAS = \"stanzas\";\n\n\tpublic static final String TYPE = \"type\";\n\n\tpublic static final String VALUE = \"value\";\n\n\tpublic static final String PRIVACY_LIST_LOADED = \"privacy-lists-loaded\";\n\n\tprivate static Logger log = Logger.getLogger(Privacy.class.getName());\n\n\tpublic static void addList(XMPPResourceConnection session, Element list)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Saving privacy list: {0}\", list);\n\t\t}\n\n\t\tString lNode = listNode(list.getAttributeStaticStr(NAME));\n\n\t\tsession.setData(lNode, PRIVACY_LIST, list.toString());\n\t}\n\n\tpublic static boolean block(XMPPResourceConnection session, List<String> jids)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tString name = getDefaultListName(session);\n\t\tif (name == null) {\n\t\t\tname = \"default\";\n\t\t}\n\t\tElement list_new = new Element(LIST, new String[]{NAME}, new String[]{name});\n\t\tfor (String jid : jids) {\n\t\t\tlist_new.addChild(new Element(ITEM, new String[]{TYPE, ACTION, VALUE},\n\t\t\t\t\t\t\t\t\t\t  new String[]{\"jid\", \"deny\", jid}));\n\t\t}\n\n\t\tElement list = getList(session, name);\n\t\tif (list != null) {\n\t\t\tList<Element> items = list.getChildren();\n\t\t\tif (items != null) {\n\t\t\t\titems.stream().filter(it -> !jids.contains(it.getAttributeStaticStr(VALUE))).sorted(JabberIqPrivacy.compar).forEach(it -> {\n\t\t\t\t\tlist_new.addChild(it);\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tint i=0;\n\t\tfor (Element item : list_new.getChildren()) {\n\t\t\titem.setAttribute(ORDER, String.valueOf(++i));\n\t\t}\n\t\tupdateList(session, name, list_new);\n\t\treturn true;\n\t}\n\n\tpublic static PrivacyList getActiveList(XMPPResourceConnection session) throws NotAuthorizedException {\n\t\treturn (PrivacyList) session.getSessionData(ACTIVE);\n\t}\n\n\tpublic static String getActiveListName(XMPPResourceConnection session) throws NotAuthorizedException {\n\t\tPrivacyList list = getActiveList(session);\n\n\t\tif (list != null) {\n\t\t\treturn list.getName();\n\t\t} else {\n\t\t\treturn null;\n\t\t}    // end of if (list != null) else\n\t}\n\n\tpublic static List<String> getBlocked(XMPPResourceConnection session)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tPrivacyList list = getDefaultList(session);\n\t\tList<String> ulist = null;\n\t\tif (list != null) {\n\t\t\tulist = list.getBlockedJids().map(jid -> jid.toString()).collect(Collectors.toList());\n\t\t}\n\t\treturn ulist;\n\t}\n\n\tpublic static PrivacyList getDefaultList(XMPPResourceConnection session)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tPrivacyList sessionDefaultList = (PrivacyList) session.getCommonSessionData(DEFAULT);\n\t\tif (session.getCommonSessionData(PRIVACY_LIST_LOADED) == null) {\n\t\t\tsessionDefaultList = PrivacyList.create(session, RosterFactory.getRosterImplementation(true),\n\t\t\t\t\t\t\t\t\t\t\t\t\tgetDefaultListElement(session));\n\t\t\tif (null != sessionDefaultList) {\n\t\t\t\tsession.putCommonSessionData(DEFAULT, sessionDefaultList);\n\t\t\t}\n\t\t\tsession.putCommonSessionData(PRIVACY_LIST_LOADED, PRIVACY_LIST_LOADED);\n\t\t}\n\t\treturn sessionDefaultList;\n\t}\n\n\tpublic static Element getDefaultListElement(XMPPResourceConnection session)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tElement sessionDefaultList = null;\n\t\tString defaultListName = getDefaultListName(session);\n\t\tif (defaultListName != null) {\n\t\t\tsessionDefaultList = Privacy.getList(session, defaultListName);\n\t\t}\n\t\treturn sessionDefaultList;\n\t}\n\n\tpublic static String getDefaultListName(XMPPResourceConnection session)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\treturn session.getData(PRIVACY, DEFAULT, null);\n\t}\n\n\tpublic static Element getList(XMPPResourceConnection session, String list)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Loading privacy list: {0}\", list);\n\t\t}\n\n\t\tString lNode = listNode(list);\n\t\tString list_str = session.getData(lNode, PRIVACY_LIST, null);\n\n\t\tif ((list_str != null) && !list_str.isEmpty()) {\n\t\t\tSimpleParser parser = SingletonFactory.getParserInstance();\n\t\t\tDomBuilderHandler domHandler = new DomBuilderHandler();\n\n\t\t\tparser.parse(domHandler, list_str.toCharArray(), 0, list_str.length());\n\n\t\t\tQueue<Element> elems = domHandler.getParsedElements();\n\t\t\tElement result = elems.poll();\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Loaded privacy list: {0}\", result);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} else {\n\t\t\treturn getListOld(session, list);\n\t\t}\n\t}\n\n\tpublic static Element getListOld(XMPPResourceConnection session, String list)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tString lNode = listNode(list);\n\t\tString[] items = session.getDataGroups(lNode);\n\n\t\tif (items != null && list != null) {\n\t\t\tElement eList = new Element(LIST, new String[]{NAME}, new String[]{list});\n\n\t\t\tfor (String item : items) {\n\t\t\t\tString iNode = lNode + \"/\" + item;\n\t\t\t\tString type = session.getData(iNode, TYPE, null);\n\t\t\t\tString value = session.getData(iNode, VALUE, null);\n\t\t\t\tString action = session.getData(iNode, ACTION, null);\n\t\t\t\tString[] stanzas = session.getDataList(iNode, STANZAS);\n\n\t\t\t\tif ((item == null) || (action == null)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tElement eItem = new Element(ITEM, new String[]{ORDER, ACTION}, new String[]{item, action});\n\n\t\t\t\tif (type != null) {\n\t\t\t\t\teItem.addAttribute(TYPE, type);\n\t\t\t\t}      // end of if (type != null)\n\t\t\t\tif (value != null) {\n\t\t\t\t\teItem.addAttribute(VALUE, value);\n\t\t\t\t}      // end of if (value != null)\n\t\t\t\tif (stanzas != null) {\n\t\t\t\t\tfor (String stanza : stanzas) {\n\t\t\t\t\t\teItem.addChild(new Element(stanza));\n\t\t\t\t\t}    // end of for (String stanza: stanzas)\n\t\t\t\t}      // end of if (stanzas != null)\n\t\t\t\teList.addChild(eItem);\n\t\t\t}        // end of for (String item: items)\n\n\t\t\treturn eList;\n\t\t}          // end of if (items != null)\n\n\t\treturn null;\n\t}\n\n\tpublic static String[] getLists(XMPPResourceConnection session) throws NotAuthorizedException, TigaseDBException {\n\t\treturn session.getDataGroups(PRIVACY);\n\t}\n\n\tprivate static boolean isBlockItem(Element item) {\n\t\treturn \"jid\".equals(item.getAttributeStaticStr(TYPE)) && \"deny\".equals(item.getAttributeStaticStr(ACTION)) &&\n\t\t\t\titem.getChildren() == null;\n\t}\n\n\tpublic static String listNode(final String list) {\n\t\treturn PRIVACY + \"/\" + list;\n\t}\n\n\tpublic static void removeList(XMPPResourceConnection session, Element list)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Removing privacy list: {0}\", list);\n\t\t}\n\n\t\tString lNode = listNode(list.getAttributeStaticStr(NAME));\n\n\t\tsession.removeData(lNode, PRIVACY_LIST);\n\t\tsession.removeDataGroup(lNode);\n\t}\n\n\tpublic static void setActiveList(XMPPResourceConnection session, String lName)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tif (lName == null) {\n\n\t\t\t// User declines to use current actiev list\n\t\t\tsession.removeSessionData(ACTIVE);\n\t\t} else {\n\n\t\t\t// User selects a different active list\n\t\t\tPrivacyList list = PrivacyList.create(session, RosterFactory.getRosterImplementation(true),\n\t\t\t\t\t\t\t\t\t\t\t\t  getList(session, lName));\n\n\t\t\tif (list != null) {\n\t\t\t\tsession.putSessionData(ACTIVE, list);\n\t\t\t} else {\n\t\t\t\tlog.log(Level.CONFIG, \"Setting active list to null, do something better than that, perhaps notify user.\");\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static void setDefaultList(XMPPResourceConnection session, Element list)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tif ((list != null) && (list.getAttributeStaticStr(NAME) != null)) {\n\t\t\tsession.setData(PRIVACY, DEFAULT, list.getAttributeStaticStr(NAME));\n\t\t\tsession.putCommonSessionData(DEFAULT,\n\t\t\t\t\t\t\t\t\t\t PrivacyList.create(session, RosterFactory.getRosterImplementation(true),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlist));\n\t\t} else {\n\t\t\tsession.removeData(PRIVACY, DEFAULT);\n\t\t\tsession.removeCommonSessionData(DEFAULT);\n\t\t}\n\t}\n\n\tpublic static boolean unblock(XMPPResourceConnection session, List<String> jids)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tString name = getDefaultListName(session);\n\t\tElement list = getList(session, name);\n\t\tif (list == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tElement list_new = new Element(LIST, new String[]{NAME}, new String[]{name});\n\t\tList<Element> items = list.findChildren(\n\t\t\t\titem -> !jids.contains(item.getAttributeStaticStr(VALUE)));\n\t\tif (items != null) {\n\t\t\tCollections.sort(items, JabberIqPrivacy.compar);\n\t\t\tfor (int i = 0; i < items.size(); i++) {\n\t\t\t\titems.get(i).setAttribute(ORDER, String.valueOf(i+1));\n\t\t\t}\n\t\t\tlist_new.addChildren(items);\n\t\t}\n\n\t\tupdateList(session, name, list_new);\n\n\t\treturn false;\n\t}\n\n\tpublic static List<String> unblockAll(XMPPResourceConnection session)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tString name = getDefaultListName(session);\n\t\tElement list = getList(session, name);\n\t\tif (list == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tList<String> ulist = list.mapChildren(item -> isBlockItem(item), item -> item.getAttributeStaticStr(VALUE));\n\n\t\tElement list_new = new Element(LIST, new String[]{NAME}, new String[]{name});\n\t\tList<Element> items = list.findChildren(item -> !isBlockItem(item));\n\t\tif (items != null) {\n\t\t\tCollections.sort(items, JabberIqPrivacy.compar);\n\t\t\tfor (int i = 0; i < items.size(); i++) {\n\t\t\t\titems.get(i).setAttribute(ORDER, \"\" + (i + 1));\n\t\t\t}\n\t\t\tlist_new.addChildren(items);\n\t\t}\n\n\t\tupdateList(session, name, list_new);\n\n\t\treturn ulist;\n\t}\n\n\tprivate static void updateList(XMPPResourceConnection session, String name, Element list_new)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\taddList(session, list_new);\n\t\tPrivacy.setDefaultList(session, list_new);\n\t\tif (getDefaultList(session) == null) {\n\t\t\tPrivacy.setActiveList(session, name);\n\t\t} else if (name.equals(getActiveListName(session))) {\n\t\t\tsession.putCommonSessionData(ACTIVE, list_new);\n\t\t\tsession.putSessionData(ACTIVE, list_new);\n\t\t}\n\t}\n\n}    // Privacy\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/PrivacyList.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.TigaseDBException;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.impl.roster.RosterAbstract;\nimport tigase.xmpp.impl.roster.RosterElement;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.function.Function;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\npublic class PrivacyList {\n\n\tpublic static final PrivacyList ALLOW_ALL = new PrivacyList(null, new Element(\"list\"));\n\tprivate static final Logger log = Logger.getLogger(PrivacyList.class.getCanonicalName());\n\tprivate static final Set<Item.Type> ALL_TYPES = EnumSet.allOf(Item.Type.class);\n\tprivate static final PrivacyList DENY_ALL = new PrivacyList(null, new Element(\"list\")) {\n\t\t@Override\n\t\tpublic boolean isAllowed(JID jid, Item.Type type) {\n\t\t\treturn false;\n\t\t}\n\t};\n\tprivate final Item[] items;\n\tprivate final String name;\n\tprivate final Function<JID, RosterElement> rosterElementGetter;\n\n\tpublic static PrivacyList create(final Map<BareJID, RosterElement> roster, Element el) {\n\t\tif (el == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn new PrivacyList((jid) -> {\n\t\t\tif (jid == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn roster.get(jid.getBareJID());\n\t\t}, el).getSingletonIfPossible();\n\t}\n\n\tpublic static PrivacyList create(XMPPResourceConnection session, RosterAbstract rosterUtil, Element el)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tif (el == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn new PrivacyList(rosterUtil.rosterElementProvider(session), el).getSingletonIfPossible();\n\t}\n\n\tpublic PrivacyList(Function<JID, RosterElement> rosterElementGetter, Element el) {\n\t\tthis.rosterElementGetter = rosterElementGetter;\n\t\tthis.name = el.getAttributeStaticStr(Privacy.NAME);\n\t\tthis.items = Optional.ofNullable(el.getChildren())\n\t\t\t\t.orElse(Collections.emptyList())\n\t\t\t\t.stream()\n\t\t\t\t.sorted(JabberIqPrivacy.compar)\n\t\t\t\t.map(this::elemToItem)\n\t\t\t\t.filter(it -> it != null)\n\t\t\t\t.toArray(x -> new Item[x]);\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic Stream<JID> getBlockedJids() {\n\t\treturn Arrays.stream(items).filter(x -> x instanceof ItemJid).filter(x -> !x.isAllowed()).map(x -> ((ItemJid) x).jid);\n\t}\n\n\tpublic boolean isAllowed(JID jid, Item.Type type) {\n\t\tfor (Item item : items) {\n\t\t\tif (item.matches(jid, type)) {\n\t\t\t\treturn item.isAllowed();\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tpublic boolean isEmpty() {\n\t\treturn items.length == 0;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tfinal StringJoiner joiner = new StringJoiner(\", \", \"PrivacyList[name=\" + name + \", size: \" + items.length +\n\t\t\t\t\", items: \", \"]\");\n\n\t\tfor (int i = 0; i < items.length; i++) {\n\t\t\tItem item = items[i];\n\t\t\tjoiner.add(item.toString());\n\t\t\tif (i + 1 >= 10) {\n\t\t\t\tjoiner.add(\"... \" + (items.length - i - 1) + \" more...\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn joiner.toString();\n\t}\n\n\tprivate PrivacyList getSingletonIfPossible() {\n\t\tif (isEmpty()) {\n\t\t\treturn ALLOW_ALL;\n\t\t} else if (items.length == 1) {\n\t\t\tif (items[0] instanceof ItemAll) {\n\t\t\t\treturn items[0].isAllowed() ? ALLOW_ALL : DENY_ALL;\n\t\t\t}\n\t\t}\n\t\treturn this;\n\t}\n\n\tprivate Item elemToItem(Element el) {\n\t\tString type = el.getAttributeStaticStr(\"type\");\n\t\tString value = el.getAttributeStaticStr(\"value\");\n\t\tString action = el.getAttributeStaticStr(\"action\");\n\t\tif (action == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tboolean allow;\n\t\tswitch (action) {\n\t\t\tcase \"allow\":\n\t\t\t\tallow = true;\n\t\t\t\tbreak;\n\t\t\tcase \"deny\":\n\t\t\t\tallow = false;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\treturn null;\n\t\t}\n\n\t\tif (type == null) {\n\t\t\treturn new ItemAll(allow);\n\t\t}\n\n\t\tSet<Item.Type> types = ALL_TYPES;\n\t\tList<Element> children = el.getChildren();\n\t\tif (children != null && !children.isEmpty()) {\n\t\t\ttypes = children.stream().map(child -> {\n\t\t\t\tswitch (child.getName()) {\n\t\t\t\t\tcase \"message\":\n\t\t\t\t\t\treturn Item.Type.message;\n\t\t\t\t\tcase \"iq\":\n\t\t\t\t\t\treturn Item.Type.iq;\n\t\t\t\t\tcase \"presence-in\":\n\t\t\t\t\t\treturn Item.Type.presenceIn;\n\t\t\t\t\tcase \"presence-out\":\n\t\t\t\t\t\treturn Item.Type.presenceOut;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}).filter(val -> val != null).collect(Collectors.toSet());\n\t\t\tif (types.isEmpty()) {\n\t\t\t\ttypes = ALL_TYPES;\n\t\t\t}\n\t\t}\n\n\t\tswitch (type) {\n\t\t\tcase \"jid\":\n\t\t\t\ttry {\n\t\t\t\t\tJID jid = JID.jidInstance(value);\n\t\t\t\t\treturn new ItemJid(jid, allow, types);\n\t\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Exception while creating jid instance for value: \" + value, ex);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\tcase \"group\":\n\t\t\t\treturn new ItemGroup(value, allow, types);\n\t\t\tcase \"subscription\":\n\t\t\t\ttry {\n\t\t\t\t\treturn new ItemSubscription(RosterAbstract.SubscriptionType.valueOf(value), allow, types);\n\t\t\t\t} catch (IllegalArgumentException ex) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Exception while parsing subscription value: \" + value, ex);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic interface Item {\n\n\t\tenum Type {\n\t\t\tmessage,\n\t\t\tiq,\n\t\t\tpresenceIn,\n\t\t\tpresenceOut\n\t\t}\n\n\t\tboolean isAllowed();\n\n\t\tboolean matches(JID jid, Type type);\n\t}\n\n\tprivate abstract class AbstractItem\n\t\t\timplements Item {\n\n\t\tprotected final boolean allowed;\n\t\tprotected final Set<Type> types;\n\n\t\tprotected AbstractItem(boolean allowed, Set<Type> types) {\n\t\t\tthis.allowed = allowed;\n\t\t\tthis.types = types;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isAllowed() {\n\t\t\treturn allowed;\n\t\t}\n\n\t\tprotected String[] getRosterGroupsForJid(JID jid) {\n\t\t\tRosterElement item = rosterElementGetter.apply(jid);\n\t\t\treturn item == null ? null : item.getGroups();\n\t\t}\n\n\t\tprotected RosterAbstract.SubscriptionType getSubscriptionForJID(JID jid) {\n\t\t\tRosterElement item = rosterElementGetter.apply(jid);\n\t\t\treturn item == null ? null : item.getSubscription();\n\t\t}\n\t}\n\n\tprivate class ItemAll\n\t\t\timplements Item {\n\n\t\tprivate final boolean allowed;\n\n\t\tpublic ItemAll(boolean allowed) {\n\t\t\tthis.allowed = allowed;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isAllowed() {\n\t\t\treturn allowed;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean matches(JID jid, Type type) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"All items: \" + (allowed ? \"allowed\" : \"blocked\");\n\t\t}\n\t}\n\n\tprivate class ItemGroup\n\t\t\textends AbstractItem {\n\n\t\tprivate final String group;\n\n\t\tpublic ItemGroup(String group, boolean allowed, Set<Type> types) {\n\t\t\tsuper(allowed, types);\n\t\t\tthis.group = group;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean matches(JID jid, Type type) {\n\t\t\tif (!types.contains(type)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tString[] groups = getRosterGroupsForJid(jid);\n\t\t\tif (groups != null) {\n\t\t\t\tfor (String group : groups) {\n\t\t\t\t\tif (group.equals(this.group)) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"Group: \" + group + \": \" + (allowed ? \"allowed\" : \"blocked\");\n\t\t}\n\t}\n\n\tprivate class ItemJid\n\t\t\textends AbstractItem {\n\n\t\tprivate final JID jid;\n\n\t\tpublic ItemJid(JID jid, boolean allowed, Set<Type> types) {\n\t\t\tsuper(allowed, types);\n\t\t\tthis.jid = jid;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean matches(JID jid, Type type) {\n\t\t\treturn matches(jid) && types.contains(type);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"JID: \" + jid + \": \" + (allowed ? \"allowed\" : \"blocked\");\n\t\t}\n\n\t\tprivate boolean matches(JID jid) {\n\t\t\tif (this.jid.getLocalpart() != null) {\n\t\t\t\tif (this.jid.getResource() != null) {\n\t\t\t\t\treturn jid.equals(this.jid);\n\t\t\t\t} else if (this.jid.getResource() == null) {\n\t\t\t\t\treturn jid.getBareJID().equals(this.jid.getBareJID());\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (this.jid.getResource() != null) {\n\t\t\t\t\treturn jid.equals(this.jid);\n\t\t\t\t} else if (this.jid.getResource() == null) {\n\t\t\t\t\treturn jid.getDomain().equals(this.jid.getDomain());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate class ItemSubscription\n\t\t\textends AbstractItem {\n\n\t\tprivate final RosterAbstract.SubscriptionType subscription;\n\n\t\tpublic ItemSubscription(RosterAbstract.SubscriptionType subscription, boolean allowed, Set<Type> types) {\n\t\t\tsuper(allowed, types);\n\t\t\tthis.subscription = subscription;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean matches(JID jid, Type type) {\n\t\t\tif (!types.contains(type)) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tRosterAbstract.SubscriptionType subscription = getSubscriptionForJID(jid);\n\t\t\tswitch (this.subscription) {\n\t\t\t\tcase none:\n\t\t\t\t\treturn subscription == null || (!RosterAbstract.TO_SUBSCRIBED.contains(subscription) &&\n\t\t\t\t\t\t\t!RosterAbstract.FROM_SUBSCRIBED.contains(subscription));\n\t\t\t\tcase to:\n\t\t\t\t\treturn RosterAbstract.TO_SUBSCRIBED.contains(subscription);\n\t\t\t\tcase from:\n\t\t\t\t\treturn RosterAbstract.FROM_SUBSCRIBED.contains(subscription);\n\t\t\t\tcase both:\n\t\t\t\t\treturn RosterAbstract.TO_SUBSCRIBED.contains(subscription) &&\n\t\t\t\t\t\t\tRosterAbstract.FROM_SUBSCRIBED.contains(subscription);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"Subscription: \" + subscription + \": \" + (allowed ? \"allowed\" : \"blocked\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/RemoteRosterManagement.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.form.Field;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * @author andrzej\n */\n@Bean(name = RemoteRosterManagement.ID, parent = SessionManager.class, active = false)\npublic class RemoteRosterManagement\n\t\textends XMPPProcessorAbstract {\n\n\tprotected static final String ID = \"remote-roster-management\";\n\tprivate static final String[][] ELEMENT_PATHS = {{\"iq\", \"query\"}, {\"message\", \"x\"}};\n\tprivate static final Logger log = Logger.getLogger(\"eu.hilow.xtigase.server.xmpp.RemoteRosterManagement\");\n\tprivate static final String XMLNS = \"http://spectrum.im/protocol/remote-roster\";\n\tprivate static final String[] XMLNSS = {XMLNS, \"jabber:x:data\"};\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static Set<JID> getAllowed(XMPPResourceConnection session)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\n\t\t// Get set of allowed jids\n\t\tsynchronized (session) {\n\t\t\tSet<JID> allowed = (Set<JID>) session.getCommonSessionData(\"remote-roster-allowed\");\n\n\t\t\tif (allowed == null) {\n\t\t\t\tString data = session.getData(\"remote-roster-management\", \"allowed\", null);\n\n\t\t\t\tallowed = new HashSet<JID>();\n\t\t\t\tif (data != null) {\n\t\t\t\t\tif (data.contains(\"/\")) {\n\t\t\t\t\t\tString[] jidsStr = data.split(\"/\");\n\n\t\t\t\t\t\tfor (String jidStr : jidsStr) {\n\t\t\t\t\t\t\tif (jidStr.length() > 0) {\n\t\t\t\t\t\t\t\tallowed.add(JID.jidInstanceNS(jidStr));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tallowed.add(JID.jidInstanceNS(data));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tsession.putCommonSessionData(\"remote-roster-allowed\", allowed);\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"read list of jids allowed to modify roster = {0}\", allowed);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn allowed;\n\t\t}\n\t}\n\n\tpublic static boolean isRemoteAllowed(JID jid, XMPPResourceConnection session) {\n\t\ttry {\n\t\t\tif (session == null) {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"no session to check if {0} is allowed\", jid.toString());\n\t\t\t\t}\n\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tSet<JID> allowed = getAllowed(session);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"checking if sender jid = {0} is in allowed set = {1}\",\n\t\t\t\t\t\tnew Object[]{jid, allowed});\n\t\t\t}\n\n\t\t\treturn allowed.contains(jid);\n\t\t} catch (Exception ex) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate static void setAllowed(XMPPResourceConnection session, Set<JID> allowed)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\n\t\t// Save set of allowed jids\n\t\tStringBuilder buf = new StringBuilder(1024);\n\n\t\tsession.putCommonSessionData(\"remote-roster-allowed\", allowed);\n\n\t\tboolean first = true;\n\n\t\tfor (JID jid : allowed) {\n\t\t\tif (!first) {\n\t\t\t\tbuf.append(\"/\");\n\t\t\t} else {\n\t\t\t\tfirst = false;\n\t\t\t}\n\t\t\tbuf.append(jid.toString());\n\t\t}\n\t\tsession.setData(\"remote-roster-management\", \"allowed\", buf.toString());\n\t}\n\n\tpublic static void updateBuddyChange(XMPPResourceConnection session, Element item, Element update,\n\t\t\t\t\t\t\t\t\t\t Queue<Packet> results) throws NotAuthorizedException, TigaseDBException {\n\t\tJID jid = JID.jidInstanceNS(item.getAttributeStaticStr(\"jid\"));\n\n\t\tif (jid.getLocalpart() == null) {\n\t\t\treturn;\n\t\t}\n\t\tjid = JID.jidInstanceNS(jid.getDomain());\n\t\tif (isRemoteAllowed(jid, session)) {\n\t\t\tElement iq = update.clone();\n\n\t\t\tiq.setAttribute(\"from\", session.getBareJID().toString());\n\t\t\tiq.setAttribute(\"to\", jid.getDomain());\n\t\t\tiq.setAttribute(\"id\", \"rst\" + session.nextStanzaId());\n\t\t\tresults.offer(Packet.packetInstance(iq, JID.jidInstance(session.getBareJID()), jid));\n\t\t}\n\t}\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic void processFromUserToServerPacket(JID connectionId, Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t  NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t\t  Map<String, Object> settings) throws PacketErrorTypeException {\n\t\tif (packet.getElemName() == \"message\") {\n\t\t\ttry {\n\t\t\t\tprocessMessageFormResponse(packet, session, repo, results);\n\t\t\t} catch (NotAuthorizedException ex) {\n\t\t\t\tresults.offer(Authorization.FORBIDDEN.getResponseMessage(packet, null, false));\n\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\tresults.offer(Authorization.INTERNAL_SERVER_ERROR.getResponseMessage(packet, null, true));\n\t\t\t}\n\t\t} else {\n\t\t\tresults.offer(packet);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processServerSessionPacket(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t\t\t\t   Queue<Packet> results, Map<String, Object> settings)\n\t\t\tthrows PacketErrorTypeException {\n\t}\n\n\t@Override\n\tpublic void processToUserPacket(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings)\n\t\t\tthrows PacketErrorTypeException {\n\t\tif (packet.getElemName() == \"iq\") {\n\t\t\tprocessIq(packet, session, repo, results, settings);\n\t\t} else {\n\t\t\tsuper.processToUserPacket(packet, session, repo, results, settings);\n\t\t}\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENT_PATHS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\tprivate void processIq(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t   Queue<Packet> results, Map<String, Object> settings) throws PacketErrorTypeException {\n\t\tElement query = packet.getElement().getChild(\"query\", \"http://spectrum.im/protocol/remote-roster\");\n\n\t\tif (query == null) {\n\t\t\tsuper.processToUserPacket(packet, session, repo, results, settings);\n\n\t\t\t// log.warn(\"processing IQ with bad query = {}\", packet.toString());\n\t\t\treturn;\n\t\t}\n\t\tswitch (packet.getType()) {\n\t\t\tcase set:\n\t\t\t\tif (\"request\".equals(query.getAttributeStaticStr(Packet.TYPE_ATT))) {\n\t\t\t\t\tJID from = JID.jidInstanceNS(packet.getStanzaTo().getDomain());\n\t\t\t\t\tElement msg = new Element(tigase.server.Message.ELEM_NAME);\n\n\t\t\t\t\tmsg.setAttribute(Packet.FROM_ATT, from.toString());\n\t\t\t\t\tmsg.setAttribute(Packet.TO_ATT, packet.getStanzaTo().toString());\n\n\t\t\t\t\tForm form = new Form(Packet.FROM_ATT, \"Roster change permission\",\n\t\t\t\t\t\t\t\t\t\t packet.getStanzaFrom().getBareJID().toString() +\n\t\t\t\t\t\t\t\t\t\t\t\t \" wants to edit your roster with following reason: \" +\n\t\t\t\t\t\t\t\t\t\t\t\t query.getAttributeStaticStr(\"reason\") + \". Do you want to allow it?\");\n\n\t\t\t\t\tform.addField(Field.fieldHidden(\"FORM_TYPE\", XMLNS));\n\t\t\t\t\tform.addField(Field.fieldHidden(\"jid\", packet.getStanzaFrom().getBareJID().toString()));\n\t\t\t\t\tform.addField(Field.fieldBoolean(\"answer\", true, \"Allow roster edit\"));\n\t\t\t\t\tmsg.addChildren(Collections.singletonList(form.getElement()));\n\n\t\t\t\t\tPacket resp = Packet.packetInstance(msg, from, packet.getStanzaTo());\n\n\t\t\t\t\tresults.offer(resp);\n\t\t\t\t} else {\n\t\t\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet, \"Unknown request type\", false));\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet, \"Bad request\", true));\n\t\t}\n\t}\n\n\tprivate void processMessageFormResponse(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t\t\t\t\tQueue<Packet> results) throws NotAuthorizedException, TigaseDBException {\n\t\tElement x = packet.getElement().getChild(\"x\");\n\n\t\tif (x != null) {\n\t\t\tForm form = new Form(x);\n\t\t\tif (!XMLNS.equals(form.getAsString(\"FORM_TYPE\")) &&\n\t\t\t\t\t!session.isLocalDomain(packet.getStanzaTo().toString(), false)) {\n\t\t\t\tresults.offer(packet);\n\t\t\t}\n\t\t\tJID jid = JID.jidInstanceNS(form.get(\"jid\").getValue());\n\t\t\tBoolean answer = false;\n\t\t\tString answerStr = form.get(\"answer\").getValue();\n\n\t\t\tif (\"true\".equals(answerStr) || \"1\".equals(answerStr)) {\n\t\t\t\tanswer = true;\n\t\t\t}\n\t\t\tsynchronized (session) {\n\t\t\t\tSet<JID> allowed = getAllowed(session);\n\n\t\t\t\t// Apply result of form\n\t\t\t\tif (answer) {\n\t\t\t\t\tallowed.add(jid);\n\t\t\t\t} else {\n\t\t\t\t\tallowed.remove(jid);\n\t\t\t\t}\n\t\t\t\tsetAllowed(session, allowed);\n\t\t\t}\n\n\t\t\t// Notify remote jid about answer\n\t\t\tElement iq = new Element(\"iq\");\n\n\t\t\tiq.setAttribute(\"from\", packet.getStanzaFrom().getBareJID().toString());\n\t\t\tiq.setAttribute(\"to\", jid.toString());\n\n\t\t\tElement query = new Element(\"query\");\n\n\t\t\tquery.setAttribute(\"xmlns\", XMLNS);\n\t\t\tquery.setAttribute(\"type\", answer ? \"allowed\" : \"rejected\");\n\t\t\tiq.addChildren(Collections.singletonList(query));\n\t\t\tresults.offer(Packet.packetInstance(iq, packet.getStanzaFrom().copyWithoutResource(), jid));\n\t\t} else {\n\t\t\tresults.offer(packet);\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/SaslAuth.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.auth.BruteForceLockerBean;\nimport tigase.auth.XmppSaslException;\nimport tigase.auth.XmppSaslException.SaslError;\nimport tigase.auth.mechanisms.AbstractSasl;\nimport tigase.auth.mechanisms.SaslANONYMOUS;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.util.Base64;\nimport tigase.xml.Element;\nimport tigase.xmpp.XMPPProcessorIfc;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\n\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.sasl.Sasl;\nimport javax.security.sasl.SaslException;\nimport javax.security.sasl.SaslServer;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.function.Function;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\n/**\n * Describe class SaslAuth here.\n * <br>\n * Created: Mon Feb 20 16:28:13 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = SaslAuth.ID, parent = SessionManager.class, active = true)\npublic class SaslAuth\n\t\textends SaslAuthAbstract\n\t\timplements XMPPProcessorIfc {\n\n\tpublic static final String ID = \"urn:ietf:params:xml:ns:xmpp-sasl\";\n\tprivate static final String _XMLNS = \"urn:ietf:params:xml:ns:xmpp-sasl\";\n\tprivate static final Element[] DISCO_FEATURES = {new Element(\"feature\", new String[]{\"var\"}, new String[]{_XMLNS})};\n\tprivate static final String[][] ELEMENTS = {{\"auth\"}, {\"response\"}, {\"challenge\"}, {\"failure\"}, {\"success\"},\n\t\t\t\t\t\t\t\t\t\t\t\t{\"abort\"}};\n\tprivate static final Logger log = Logger.getLogger(SaslAuth.class.getName());\n\tprivate static final String[] XMLNSS = {_XMLNS, _XMLNS, _XMLNS, _XMLNS, _XMLNS, _XMLNS};\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\tpublic enum ElementType {\n\t\tABORT,\n\t\tAUTH,\n\t\tCHALLENGE,\n\t\tFAILURE,\n\t\tRESPONSE,\n\t\tSUCCESS;\n\n\t\tprivate static final Map<String, ElementType> ALL_TYPES = Arrays.stream(\n\t\t\t\t\t\tElementType.values())\n\t\t\t\t.collect(Collectors.toMap(ElementType::getElementName, Function.identity()));\n\t\tprivate final String elementName;\n\n\t\tpublic static ElementType parse(String name) {\n\t\t\treturn ALL_TYPES.get(name);\n\t\t}\n\n\t\tElementType() {\n\t\t\tthis.elementName = name().toLowerCase();\n\t\t}\n\n\t\tpublic String getElementName() {\n\t\t\treturn elementName;\n\t\t}\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic void process(final Packet packet, final XMPPResourceConnection session, final NonAuthUserRepository repo,\n\t\t\t\t\t\tfinal Queue<Packet> results, final Map<String, Object> settings) {\n\t\tif (session == null) {\n\t\t\treturn;\n\t\t}\n\t\tsynchronized (session) {\n\t\t\t// If authentication timeout expired, ignore the request....\n\t\t\tif (session.getSessionData(XMPPResourceConnection.AUTHENTICATION_TIMEOUT_KEY) != null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfinal String clientIp = BruteForceLockerBean.getClientIp(session);\n\t\t\tif (session.isAuthorized()) {\n\t\t\t\tprocessSessionAlreadyAuthorized(packet, session, results);\n\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\tElement request = packet.getElement();\n\t\t\t\ttry {\n\t\t\t\t\tSaslServer ss;\n\n\t\t\t\t\tif (\"auth\" == request.getName()) {\n\t\t\t\t\t\tfinal String mechanismName = packet.getElement().getAttributeStaticStr(\"mechanism\");\n\n\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\tlog.finest(\"Start SASL auth. mechanism=\" + mechanismName);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tCollection<String> allowedMechanisms = (Collection<String>) session.getSessionData(\n\t\t\t\t\t\t\t\tALLOWED_SASL_MECHANISMS_KEY);\n\t\t\t\t\t\tsession.removeSessionData(ALLOWED_SASL_MECHANISMS_KEY);\n\n\t\t\t\t\t\tif (allowedMechanisms == null) {\n\t\t\t\t\t\t\tallowedMechanisms = saslProvider.filterMechanisms(Sasl.getSaslServerFactories(), session);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ((mechanismName == null) || allowedMechanisms == null ||\n\t\t\t\t\t\t\t\t!allowedMechanisms.contains(mechanismName)) {\n\t\t\t\t\t\t\tthrow new XmppSaslException(SaslError.invalid_mechanism,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"Mechanism '\" + mechanismName + \"' is not allowed\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tCallbackHandler cbh = saslProvider.create(mechanismName, session, repo, settings);\n\n\t\t\t\t\t\tss = Sasl.createSaslServer(mechanismName, \"xmpp\", session.getDomain().getVhost().getDomain(),\n\t\t\t\t\t\t\t\t\t\t\t\t   props, cbh);\n\t\t\t\t\t\tif (ss == null) {\n\t\t\t\t\t\t\tthrow new XmppSaslException(SaslError.invalid_mechanism,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"Mechanism '\" + mechanismName + \"' is not allowed\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsession.putSessionData(SASL_SERVER_KEY, ss);\n\t\t\t\t\t} else if (\"response\" == request.getName()) {\n\t\t\t\t\t\tss = (SaslServer) session.getSessionData(SASL_SERVER_KEY);\n\t\t\t\t\t\tif (ss == null) {\n\t\t\t\t\t\t\tthrow new XmppSaslException(SaslError.malformed_request);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new XmppSaslException(SaslError.malformed_request,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"Unrecognized element \" + request.getName());\n\t\t\t\t\t}\n\n\t\t\t\t\tbyte[] data;\n\t\t\t\t\tString cdata = request.getCData();\n\n\t\t\t\t\tif ((cdata != null) && (cdata.length() == 1) && cdata.equals(\"=\")) {\n\t\t\t\t\t\tdata = new byte[]{};\n\t\t\t\t\t} else if ((cdata != null) && (cdata.length() > 0)) {\n\t\t\t\t\t\tdata = Base64.decode(cdata);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdata = new byte[]{};\n\t\t\t\t\t}\n\n\t\t\t\t\tbyte[] challenge = ss.evaluateResponse(data);\n\t\t\t\t\tString challengeData;\n\n\t\t\t\t\tif (challenge != null) {\n\t\t\t\t\t\tchallengeData = Base64.encode(challenge);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tchallengeData = null;\n\t\t\t\t\t}\n\t\t\t\t\tif (ss.isComplete() && (ss.getAuthorizationID() != null)) {\n\t\t\t\t\t\tBareJID jid;\n\n\t\t\t\t\t\tif (ss.getAuthorizationID().contains(\"@\")) {\n\t\t\t\t\t\t\tjid = BareJID.bareJIDInstance(ss.getAuthorizationID());\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tjid = BareJID.bareJIDInstance(ss.getAuthorizationID(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  session.getDomain().getVhost().getDomain());\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (isLoginAllowedByBruteForceLocker(session, clientIp, jid)) {\n\t\t\t\t\t\t\tthrow new BruteForceLockerBean.LoginLockedException();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\t\tlog.finest(\"Authorized as \" + jid);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tboolean anonymous;\n\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tBoolean x = (Boolean) ss.getNegotiatedProperty(SaslANONYMOUS.IS_ANONYMOUS_PROPERTY);\n\n\t\t\t\t\t\t\tanonymous = x != null && x;\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\tanonymous = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsession.removeSessionData(SASL_SERVER_KEY);\n\t\t\t\t\t\tsession.authorizeJID(jid, anonymous);\n\t\t\t\t\t\tif (session.getAuthRepository() != null) {\n\t\t\t\t\t\t\tsession.getAuthRepository().loggedIn(jid);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tprocessSuccess(packet, session, challengeData, results);\n\t\t\t\t\t} else if (!ss.isComplete()) {\n\t\t\t\t\t\tresults.offer(packet.swapFromTo(createReply(ElementType.CHALLENGE, challengeData), null, null));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new XmppSaslException(SaslError.malformed_request);\n\t\t\t\t\t}\n\t\t\t\t} catch (BruteForceLockerBean.LoginLockedException e) {\n\t\t\t\t\tonAuthFail(session);\n\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\tlog.log(Level.FINER, \"Account locked by BruteForceLocker.\");\n\t\t\t\t\t}\n\t\t\t\t\tresults.offer(createSaslErrorResponse(SaslError.not_authorized, AbstractSasl.PASSWORD_NOT_VERIFIED_MSG, packet));\n\t\t\t\t} catch (XmppSaslException e) {\n\t\t\t\t\tsaveIntoBruteForceLocker(session, e);\n\t\t\t\t\tonAuthFail(session);\n\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\tlog.log(Level.FINER, \"SASL unsuccessful\", e);\n\t\t\t\t\t}\n\t\t\t\t\tresults.offer(createSaslErrorResponse(e.getSaslError(), e.getMessage(), packet));\n\t\t\t\t} catch (SaslException e) {\n\t\t\t\t\tsaveIntoBruteForceLocker(session, e);\n\t\t\t\t\tonAuthFail(session);\n\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\tlog.log(Level.FINER, \"SASL unsuccessful\", e);\n\t\t\t\t\t}\n\t\t\t\t\tresults.offer(createSaslErrorResponse(SaslError.not_authorized, null, packet));\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tonAuthFail(session);\n\t\t\t\t\tif (log.isLoggable(Level.WARNING)) {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Problem with SASL\", e);\n\t\t\t\t\t}\n\t\t\t\t\tresults.offer(createSaslErrorResponse(SaslError.temporary_auth_failure, null, packet));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(final XMPPResourceConnection session) {\n\t\treturn DISCO_FEATURES;\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\t@Override\n\tpublic Element[] supStreamFeatures(final XMPPResourceConnection session) {\n\t\tif ((session == null) || session.isAuthorized()) {\n\t\t\treturn null;\n\t\t} else {\n\t\t\tCollection<String> auth_mechs = saslProvider.filterMechanisms(Sasl.getSaslServerFactories(), session);\n\t\t\tif (auth_mechs.isEmpty()) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tElement[] mechs = new Element[auth_mechs.size()];\n\t\t\tint idx = 0;\n\n\t\t\tsession.putSessionData(ALLOWED_SASL_MECHANISMS_KEY, auth_mechs);\n\t\t\tfor (String mech : auth_mechs) {\n\t\t\t\tmechs[idx++] = new Element(\"mechanism\", mech);\n\t\t\t}\n\n\t\t\treturn new Element[]{new Element(\"mechanisms\", mechs, new String[]{\"xmlns\"}, new String[]{_XMLNS})};\n\t\t}\n\t}\n\n\tprotected String getXmlns() {\n\t\treturn _XMLNS;\n\t}\n\n\t@Override\n\tprotected void processSuccess(Packet packet, XMPPResourceConnection session, String challengeData,\n\t                              Queue<Packet> results) {\n\t\tresults.offer(packet.swapFromTo(createReply(ElementType.SUCCESS, challengeData), null, null));\n\t}\n\n\tprotected Element createReply(final ElementType type, final String cdata) {\n\t\tElement reply = new Element(type.getElementName());\n\n\t\treply.setXMLNS(getXmlns());\n\t\tif (cdata != null) {\n\t\t\treply.setCData(cdata);\n\t\t}\n\n\t\treturn reply;\n\t}\n}"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/SaslAuth2.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.auth.BruteForceLockerBean;\nimport tigase.auth.XmppSaslException;\nimport tigase.auth.mechanisms.AbstractSasl;\nimport tigase.auth.mechanisms.SaslANONYMOUS;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Command;\nimport tigase.server.Packet;\nimport tigase.server.Priority;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.util.Base64;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.sasl.Sasl;\nimport javax.security.sasl.SaslException;\nimport javax.security.sasl.SaslServer;\nimport java.util.*;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.function.Function;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\nimport static tigase.auth.XmppSaslException.SaslError;\n\n@Bean(name = SaslAuth2.ID, parent = SessionManager.class, active = false)\npublic class SaslAuth2 extends SaslAuthAbstract\n\t\timplements XMPPProcessorIfc {\n\n\tpublic static final String ID = \"urn:xmpp:sasl:2\";\n\tprivate static final Logger log = Logger.getLogger(SaslAuth2.class.getName());\n\tprivate static final String XMLNS = \"urn:xmpp:sasl:2\";\n\n\tprotected final static String USER_AGENT_KEY = \"user-agent-key\";\n\tprotected final static String SASL_FEATURES_KEY = \"sasl-features-key\";\n\n\tprivate static final Element[] DISCO_FEATURES = {new Element(\"feature\", new String[]{\"var\"}, new String[]{XMLNS})};\n\tprivate static final String[][] ELEMENTS = {{\"authenticate\"}, {\"response\"}, {\"challenge\"}, {\"failure\"}, {\"success\"},\n\t\t\t\t\t\t\t\t\t\t\t\t{\"continue\"}, {\"next\"}, {\"data\"}, {\"upgrade\"}, {\"parameters\"}, {\"hash\"},\n\t\t\t\t\t\t\t\t\t\t\t\t{\"abort\"}};\n\tprivate static final String[] XMLNSS = {XMLNS, XMLNS, XMLNS, XMLNS, XMLNS, XMLNS, XMLNS, XMLNS, XMLNS, XMLNS, XMLNS,\n\t\t\t\t\t\t\t\t\t\t\tXMLNS};\n\n\t@Inject(nullAllowed = true)\n\tprivate SessionManager sessionManager;\n\n\t@Inject\n\tprivate List<Inline> inlines;\n\n\tpublic enum ElementType {\n\t\tABORT,\n\t\tAUTHENTICATE,\n\t\tCHALLENGE,\n\t\tFAILURE,\n\t\tRESPONSE,\n\t\tSUCCESS,\n\t\tCONTINUE,\n\t\tNEXT,\n\t\tDATA,\n\t\tUPGRADE,\n\t\tPARAMETERS,\n\t\tHASH;\n\n\t\tprivate static final Map<String, ElementType> ALL_TYPES = Arrays.stream(ElementType.values())\n\t\t\t\t.collect(Collectors.toMap(ElementType::getElementName, Function.identity()));\n\t\tprivate final String elementName;\n\n\t\tpublic static ElementType parse(String name) {\n\t\t\treturn ALL_TYPES.get(name);\n\t\t}\n\n\t\tElementType() {\n\t\t\tthis.elementName = name().toLowerCase();\n\t\t}\n\n\t\tpublic String getElementName() {\n\t\t\treturn elementName;\n\t\t}\n\t}\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(final XMPPResourceConnection session) {\n\t\treturn DISCO_FEATURES;\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\tpublic List<Inline> getInlines() {\n\t\treturn inlines;\n\t}\n\n\tpublic void setInlines(List<Inline> inlines) {\n\t\tif (inlines == null) {\n\t\t\tinlines = Collections.emptyList();\n\t\t}\n\t\tList<Inline> smInlines = inlines.stream().filter(it -> it instanceof StreamManagementInline).toList();\n\t\tif (!smInlines.isEmpty()) {\n\t\t\tinlines.removeAll(smInlines);\n\t\t\tinlines.addAll(0, smInlines);\n\t\t}\n\t\tthis.inlines = inlines;\n\t}\n\n\t@Override\n\tpublic Element[] supStreamFeatures(final XMPPResourceConnection session) {\n\t\tif ((session == null) || session.isAuthorized()) {\n\t\t\treturn null;\n\t\t} else {\n\t\t\tCollection<String> auth_mechs = saslProvider.filterMechanisms(Sasl.getSaslServerFactories(), session);\n\t\t\tif (auth_mechs.isEmpty()) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tsession.putSessionData(ALLOWED_SASL_MECHANISMS_KEY, auth_mechs);\n\n\t\t\tList<Element> children = new ArrayList<>();\n\t\t\tfor (String mech : auth_mechs) {\n\t\t\t\tchildren.add(new Element(\"mechanism\", mech));\n\t\t\t}\n\n\t\t\tElement inlineEl = new Element(\"inline\");\n\t\t\tchildren.add(inlineEl);\n\t\t\tfor (Inline inline : inlines) {\n\t\t\t\tElement[] features = inline.supStreamFeatures(Inline.Action.sasl2);\n\t\t\t\tif (features != null) {\n\t\t\t\t\tfor (Element feature : features) {\n\t\t\t\t\t\tinlineEl.addChild(feature);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn new Element[]{new Element(\"authentication\", children.toArray(Element[]::new), new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t new String[]{XMLNS}) };\n\t\t}\n\t}\n\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\t\tif (session == null) {\n\t\t\treturn;\n\t\t}\n\t\tsynchronized (session) {\n\t\t\t// If authentication timeout expired, ignore the request....\n\t\t\tif (session.getSessionData(XMPPResourceConnection.AUTHENTICATION_TIMEOUT_KEY) != null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfinal String clientIp = BruteForceLockerBean.getClientIp(session);\n\t\t\tif (session.isAuthorized()) {\n\t\t\t\tprocessSessionAlreadyAuthorized(packet, session, results);\n\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\ttry {\n\t\t\t\t\tElementType action = ElementType.parse(packet.getElemName());\n\t\t\t\t\tif (action == null) {\n\t\t\t\t\t\tthrow new XmppSaslException(SaslError.malformed_request,\n\t\t\t\t\t\t                            \"Unrecognized element \" + packet.getElemName());\n\t\t\t\t\t}\n\n\t\t\t\t\tSaslServer ss;\n\t\t\t\t\tbyte[] data = new byte[0];\n\n\t\t\t\t\tswitch (action) {\n\t\t\t\t\t\tcase AUTHENTICATE:\n\t\t\t\t\t\t\tfinal String mechanismName = packet.getElement().getAttributeStaticStr(\"mechanism\");\n\n\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\tlog.finest(\"Start SASL auth. mechanism=\" + mechanismName);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tCollection<String> allowedMechanisms = (Collection<String>) session.getSessionData(\n\t\t\t\t\t\t\t\t\tALLOWED_SASL_MECHANISMS_KEY);\n\t\t\t\t\t\t\tsession.removeSessionData(ALLOWED_SASL_MECHANISMS_KEY);\n\n\t\t\t\t\t\t\tif (allowedMechanisms == null) {\n\t\t\t\t\t\t\t\tallowedMechanisms = saslProvider.filterMechanisms(Sasl.getSaslServerFactories(), session);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif ((mechanismName == null) || allowedMechanisms == null ||\n\t\t\t\t\t\t\t\t\t!allowedMechanisms.contains(mechanismName)) {\n\t\t\t\t\t\t\t\tthrow new XmppSaslException(SaslError.invalid_mechanism,\n\t\t\t\t\t\t\t\t                            \"Mechanism '\" + mechanismName + \"' is not allowed\");\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tCallbackHandler cbh = saslProvider.create(mechanismName, session, repo, settings);\n\n\t\t\t\t\t\t\tss = Sasl.createSaslServer(mechanismName, \"xmpp\", session.getDomain().getVhost().getDomain(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t   props, cbh);\n\t\t\t\t\t\t\tif (ss == null) {\n\t\t\t\t\t\t\t\tthrow new XmppSaslException(SaslError.invalid_mechanism,\n\t\t\t\t\t\t\t\t                            \"Mechanism '\" + mechanismName + \"' is not allowed\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tsession.putSessionData(SASL_SERVER_KEY, ss);\n\n\t\t\t\t\t\t\tUserAgent userAgent = parseUserAgent(packet);\n\t\t\t\t\t\t\tsession.putSessionData(USER_AGENT_KEY, userAgent);\n\t\t\t\t\t\t\tList<Element> features = packet.getElement().findChildren(el -> el.getName() != \"initial-response\" && el.getName() != \"user-agent\");\n\t\t\t\t\t\t\tif (features != null && !features.isEmpty()) {\n\t\t\t\t\t\t\t\tsession.putSessionData(SASL_FEATURES_KEY, features);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tElement initialResponse = packet.getElement().getChild(\"initial-response\");\n\t\t\t\t\t\t\tif (initialResponse != null) {\n\t\t\t\t\t\t\t\tString cdata = initialResponse.getCData();\n\t\t\t\t\t\t\t\tif (cdata != null && !cdata.equals(\"=\")) {\n\t\t\t\t\t\t\t\t\tdata = Base64.decode(cdata);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase RESPONSE:\n\t\t\t\t\t\t\tss = (SaslServer) session.getSessionData(SASL_SERVER_KEY);\n\t\t\t\t\t\t\tif (ss == null) {\n\t\t\t\t\t\t\t\tthrow new XmppSaslException(SaslError.malformed_request);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tString cdata = packet.getElement().getCData();\n\t\t\t\t\t\t\tif (cdata != null && !cdata.equals(\"=\")) {\n\t\t\t\t\t\t\t\tdata = Base64.decode(cdata);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tthrow new XmppSaslException(SaslError.malformed_request,\n\t\t\t\t\t\t\t                            \"Unrecognized element \" + action.getElementName());\n\t\t\t\t\t}\n\n\t\t\t\t\tbyte[] challenge = ss.evaluateResponse(data);\n\t\t\t\t\tString challengeData;\n\n\t\t\t\t\tif (challenge != null) {\n\t\t\t\t\t\tchallengeData = Base64.encode(challenge);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tchallengeData = null;\n\t\t\t\t\t}\n\t\t\t\t\tif (ss.isComplete() && (ss.getAuthorizationID() != null)) {\n\t\t\t\t\t\tBareJID jid;\n\n\t\t\t\t\t\tif (ss.getAuthorizationID().contains(\"@\")) {\n\t\t\t\t\t\t\tjid = BareJID.bareJIDInstance(ss.getAuthorizationID());\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tjid = BareJID.bareJIDInstance(ss.getAuthorizationID(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t  session.getDomain().getVhost().getDomain());\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (isLoginAllowedByBruteForceLocker(session, clientIp, jid)) {\n\t\t\t\t\t\t\tthrow new BruteForceLockerBean.LoginLockedException();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\t\t\t\tlog.finest(\"Authorized as \" + jid);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tboolean anonymous;\n\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tBoolean x = (Boolean) ss.getNegotiatedProperty(SaslANONYMOUS.IS_ANONYMOUS_PROPERTY);\n\n\t\t\t\t\t\t\tanonymous = x != null && x;\n\t\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\t\tanonymous = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsession.removeSessionData(SASL_SERVER_KEY);\n\t\t\t\t\t\tsession.authorizeJID(jid, anonymous);\n\t\t\t\t\t\tif (session.getAuthRepository() != null) {\n\t\t\t\t\t\t\tsession.getAuthRepository().loggedIn(jid);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tprocessSuccess(packet, session, challengeData, results);\n\t\t\t\t\t} else if (!ss.isComplete()) {\n\t\t\t\t\t\tresults.offer(packet.swapFromTo(createReply(ElementType.CHALLENGE, challengeData), null, null));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new XmppSaslException(SaslError.malformed_request);\n\t\t\t\t\t}\n\t\t\t\t} catch (BruteForceLockerBean.LoginLockedException e) {\n\t\t\t\t\tonAuthFail(session);\n\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\tlog.log(Level.FINER, \"Account locked by BruteForceLocker.\");\n\t\t\t\t\t}\n\t\t\t\t\tresults.offer(createSaslErrorResponse(SaslError.not_authorized, AbstractSasl.PASSWORD_NOT_VERIFIED_MSG, packet));\n\t\t\t\t} catch (XmppSaslException e) {\n\t\t\t\t\tsaveIntoBruteForceLocker(session, e);\n\t\t\t\t\tonAuthFail(session);\n\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\tlog.log(Level.FINER, \"SASL unsuccessful\", e);\n\t\t\t\t\t}\n\t\t\t\t\tresults.offer(createSaslErrorResponse(e.getSaslError(), e.getMessage(), packet));\n\t\t\t\t} catch (SaslException e) {\n\t\t\t\t\tsaveIntoBruteForceLocker(session, e);\n\t\t\t\t\tonAuthFail(session);\n\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\tlog.log(Level.FINER, \"SASL unsuccessful\", e);\n\t\t\t\t\t}\n\t\t\t\t\tresults.offer(createSaslErrorResponse(SaslError.not_authorized, null, packet));\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tonAuthFail(session);\n\t\t\t\t\tif (log.isLoggable(Level.WARNING)) {\n\t\t\t\t\t\tlog.log(Level.WARNING, \"Problem with SASL\", e);\n\t\t\t\t\t}\n\t\t\t\t\tresults.offer(createSaslErrorResponse(SaslError.temporary_auth_failure, null, packet));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tprotected void processSuccess(Packet packet, XMPPResourceConnection session, String challengeData,\n\t                              Queue<Packet> results) {\n\t\tElement success = new Element(\"success\");\n\t\tsuccess.setXMLNS(XMLNS);\n\t\tif (challengeData != null) {\n\t\t\tsuccess.addChild(new Element(\"additional-data\", challengeData));\n\t\t}\n\n\t\tCompletableFuture<Inline.Result> result = CompletableFuture.completedFuture(new Inline.Result(null, true));\n\n\t\tList<Element> children = (List<Element>) session.getSessionData(SASL_FEATURES_KEY);\n\t\tif (children != null && !children.isEmpty()) {\n\t\t\tfor (Element child : children) {\n\t\t\t\tfor (Inline inline : inlines) {\n\t\t\t\t\tif (inline.canHandle(session, child)) {\n\t\t\t\t\t\tresult = result.thenCompose(r -> {\n\t\t\t\t\t\t\tif (r.element != null) {\n\t\t\t\t\t\t\t\tsuccess.addChild(r.element);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (r.shouldContinue) {\n\t\t\t\t\t\t\t\treturn inline.process(session, null, child);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\treturn CompletableFuture.completedFuture(new Inline.Result(null, false));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tresult.thenCompose(x -> {\n\t\t\tif (x.element != null) {\n\t\t\t\tsuccess.addChild(x.element);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tsuccess.addChild(new Element(\"authorization-identifier\", session.getJID().toString()));\n\t\t\t\treturn CompletableFuture.completedFuture(packet.swapFromTo(success, null, null));\n\t\t\t} catch (NotAuthorizedException ex) {\n\t\t\t\treturn CompletableFuture.failedFuture(ex);\n\t\t\t}\n\t\t}).thenAccept(resultPacket -> {\n\t\t\tresultPacket.setPriority(Priority.HIGH);\n\t\t\tsessionManager.addOutPacket(resultPacket);\n\t\t\t// we need to send new stream features, so lets request them from SM and send result directly to C2S\n\t\t\tPacket getFeaturesCmd = Command.GETFEATURES.getPacket(packet.getFrom(), packet.getTo(), StanzaType.get,\n\t\t\t                                                      UUID.randomUUID().toString(), null);\n\t\t\tgetFeaturesCmd.setPacketFrom(packet.getPacketFrom());\n\t\t\tgetFeaturesCmd.setPacketTo(packet.getPacketTo());\n\t\t\tsessionManager.addOutPacket(getFeaturesCmd);\n\t\t}).exceptionally(ex -> {\n\t\t\tif (log.isLoggable(Level.WARNING)) {\n\t\t\t\tlog.log(Level.WARNING, \"SASL2 inline processing failed with exception:\", ex);\n\t\t\t}\n\t\t\tsessionManager.addOutPacket(createSaslErrorResponse(\n\t\t\t\t\tSaslError.not_authorized, ex.getMessage(), packet));\n\t\t\treturn (Void) null;\n\t\t});\n\t}\n\n\tprotected String getXmlns() {\n\t\treturn XMLNS;\n\t}\n\n\tprivate UserAgent parseUserAgent(Packet packet) {\n\t\tElement el = packet.getElemChild(\"user-agent\");\n\t\tif (el == null) {\n\t\t\treturn null;\n\t\t}\n\n\t\tElement software = el.getChild(\"software\");\n\t\tElement device = el.getChild(\"device\");\n\n\t\treturn new UserAgent(el.getAttributeStaticStr(\"id\"), software != null ? software.getCData() : null,\n\t\t\t\t\t\t\t device != null ? device.getCData() : null);\n\t}\n\n\tpublic interface Inline {\n\n\t\tenum Action {\n\t\t\tsasl2,\n\t\t\tbind2\n\t\t}\n\n\t\tboolean canHandle(XMPPResourceConnection connection, Element el);\n\n\t\tElement[] supStreamFeatures(Inline.Action action);\n\n\t\tCompletableFuture<Result> process(XMPPResourceConnection session, JID boundJID, Element action);\n\n\t\tpublic static class Result {\n\t\t\tpublic final Element element;\n\t\t\tpublic final boolean shouldContinue;\n\n\t\t\tpublic Result(Element element, boolean shouldContinue) {\n\t\t\t\tthis.element = element;\n\t\t\t\tthis.shouldContinue = shouldContinue;\n\t\t\t}\n\t\t}\n\n\t}\n\n\tpublic static class UserAgent {\n\t\tprivate final String id;\n\t\tprivate final String software;\n\t\tprivate final String device;\n\n\t\tpublic UserAgent(String id, String software, String device) {\n\t\t\tthis.id = id;\n\t\t\tthis.software = software;\n\t\t\tthis.device = device;\n\t\t}\n\n\t\tpublic String getId() {\n\t\t\treturn id;\n\t\t}\n\n\t\tpublic String getSoftware() {\n\t\t\treturn software;\n\t\t}\n\n\t\tpublic String getDevice() {\n\t\t\treturn device;\n\t\t}\n\t}\n\n\tprotected Element createReply(final ElementType type, final String cdata) {\n\t\tElement reply = new Element(type.getElementName());\n\n\t\treply.setXMLNS(getXmlns());\n\t\tif (cdata != null) {\n\t\t\treply.setCData(cdata);\n\t\t}\n\n\t\treturn reply;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/SaslAuthAbstract.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.xmpp.impl;\n\nimport tigase.auth.*;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Command;\nimport tigase.server.Packet;\nimport tigase.server.Priority;\nimport tigase.xml.Element;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPProcessorIfc;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.function.Function;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\nabstract public class SaslAuthAbstract\n\t\textends AbstractAuthPreprocessor\n\t\timplements XMPPProcessorIfc {\n\n\tprotected final static String SASL_SERVER_KEY = \"SASL_SERVER_KEY\";\n\tprivate static final String SASL_SUCCESS_ELEMENT_NAME = \"success\";\n\tprivate static final String SASL_FAILURE_ELEMENT_NAME = \"failure\";\n\tprotected final static String ALLOWED_SASL_MECHANISMS_KEY = \"allowed-sasl-mechanisms\";\n\n\tprivate static final Logger log = Logger.getLogger(SaslAuthAbstract.class.getName());\n\n\tprotected final Map<String, Object> props = new HashMap<>();\n\t@Inject\n\tprotected TigaseSaslProvider saslProvider;\n\n\t@Override\n\tpublic int concurrentQueuesNo() {\n\t\treturn super.concurrentQueuesNo() * 4;\n\t}\n\n\tprotected Packet createSaslErrorResponse(XmppSaslException.SaslError error, String message, Packet packet) {\n\t\tElement failure = new Element(SASL_FAILURE_ELEMENT_NAME);\n\t\tfailure.setXMLNS(getXmlns());\n\t\tfailure.addChild((error == null ? XmppSaslException.SaslError.not_authorized : error).getElement());\n\t\tif (message != null) {\n\t\t\tfailure.addChild(new Element(\"text\", message, new String[]{\"xml:lang\"}, new String[]{\"en\"}));\n\t\t}\n\n\t\tPacket response = packet.swapFromTo(failure, null, null);\n\t\tresponse.setPriority(Priority.SYSTEM);\n\t\treturn response;\n\t}\n\n\t/**\n\t * Tries to extract BareJID of user who try to log in.\n\t */\n\t@Override\n\tprotected BareJID extractUserJid(final Exception e, XMPPResourceConnection session) {\n\t\tBareJID jid = null;\n\n\t\tif (e instanceof SaslInvalidLoginExcepion) {\n\t\t\tString t = ((SaslInvalidLoginExcepion) e).getJid();\n\t\t\tjid = t == null ? null : BareJID.bareJIDInstanceNS(t);\n\t\t}\n\n\t\tif (jid != null) {\n\t\t\tjid = super.extractUserJid(e, session);\n\t\t}\n\n\t\treturn jid;\n\t}\n\n\tabstract protected String getXmlns();\n\n\tprotected void onAuthFail(final XMPPResourceConnection session) {\n\t\tsession.removeSessionData(SASL_SERVER_KEY);\n\t}\n\n\tprotected void processSessionAlreadyAuthorized(Packet packet, XMPPResourceConnection session,\n\t                                               Queue<Packet> results) {\n\t\t// Multiple authentication attempts\n\t\t// Another authentication request on already authenticated connection\n\t\t// This is not allowed and must be forbidden\n\t\tPacket res = createSaslErrorResponse(null, null, packet);\n\n\t\t// Make sure it gets delivered before stream close\n\t\tres.setPriority(Priority.SYSTEM);\n\t\tresults.offer(res);\n\n\t\t// Optionally close the connection to make sure there is no\n\t\t// confusion about the connection state.\n\t\tresults.offer(\n\t\t\t\tCommand.CLOSE.getPacket(packet.getTo(), packet.getFrom(), StanzaType.set, session.nextStanzaId()));\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Discovered second authentication attempt: {0}, packet: {1}\",\n\t\t\t        new Object[]{session.toString(), packet.toString()});\n\t\t}\n\t\ttry {\n\t\t\tsession.logout();\n\t\t} catch (NotAuthorizedException ex) {\n\t\t\tlog.log(Level.FINER, \"Unsuccessful session logout: {0}\", session.toString());\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Session after logout: {0}\", session.toString());\n\t\t}\n\t}\n\n\tprotected abstract void processSuccess(Packet packet, XMPPResourceConnection session, String challengeData,\n\t                                       Queue<Packet> results);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/SaslChannelBinding.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.auth.TigaseSaslProvider;\nimport tigase.auth.mechanisms.AbstractSaslSCRAM;\nimport tigase.auth.mechanisms.SaslSCRAMPlus;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.JID;\n\nimport javax.security.sasl.Sasl;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Queue;\n\n@Bean(name = SaslChannelBinding.ID, parent = SessionManager.class, active = true)\npublic class SaslChannelBinding extends XMPPProcessorAbstract {\n\n\tpublic static final String ID = \"urn:xmpp:sasl-cb:0\";\n\n\t@Inject\n\tprotected TigaseSaslProvider saslProvider;\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic Authorization canHandle(Packet packet, XMPPResourceConnection conn) {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\n\t}\n\n\t@Override\n\tpublic void processFromUserToServerPacket(JID connectionId, Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t  NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t\t  Map<String, Object> settings) throws PacketErrorTypeException {\n\t\t\n\t}\n\n\t@Override\n\tpublic void processServerSessionPacket(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t\t\t\t   Queue<Packet> results, Map<String, Object> settings)\n\t\t\tthrows PacketErrorTypeException {\n\n\t}\n\n\t@Override\n\tpublic Element[] supStreamFeatures(XMPPResourceConnection session) {\n\t\tif ((session == null) || session.isAuthorized()) {\n\t\t\treturn null;\n\t\t} else {\n\t\t\tCollection<String> auth_mechs = saslProvider.filterMechanisms(Sasl.getSaslServerFactories(), session);\n\t\t\tif (auth_mechs.isEmpty()) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tif (session.isEncrypted() && session.getSessionData(AbstractSaslSCRAM.LOCAL_CERTIFICATE_KEY) != null &&\n\t\t\t\t\tSaslSCRAMPlus.containsScramPlus(auth_mechs)) {\n\t\t\t\tElement bindings = AbstractSaslSCRAM.getSupportedChannelBindings(session);\n\t\t\t\treturn new Element[] { bindings };\n\t\t\t} else {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/ServerInfo.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.XMPPException;\nimport tigase.xmpp.XMPPProcessorIfc;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.impl.annotation.AnnotatedXMPPProcessor;\nimport tigase.xmpp.impl.annotation.DiscoFeatures;\nimport tigase.xmpp.impl.annotation.Id;\n\nimport java.util.Map;\nimport java.util.Queue;\n\n/**\n * ProtoXEP: PubSub Server Information (partial)\n * \n * Enabling this processor enables 'opt-in' for inclusion of local domain names in the data exposed by other domains\n * (per the domain).\n *\n * @author Andrzej Wójcik\n */\n@Id(ServerInfo.ID)\n@DiscoFeatures(ServerInfo.XMLNS)\n@Bean(name = ServerInfo.ID, active = false, parent = SessionManager.class)\npublic class ServerInfo extends AnnotatedXMPPProcessor implements XMPPProcessorIfc {\n\n\tprotected static final String XMLNS = \"urn:xmpp:serverinfo:0\";\n\tprotected static final String ID = XMLNS;\n\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\t\t// this processor does nothing...\n\t\tresults.offer(\n\t\t\t\tAuthorization.FEATURE_NOT_IMPLEMENTED.getResponseMessage(packet, null, true));\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/ServiceDiscovery.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.component.modules.impl.AdHocCommandModule;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserRepository;\nimport tigase.db.services.AccountExpirationService;\nimport tigase.disco.XMPPServiceCollector;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Command;\nimport tigase.server.DataForm;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.util.datetime.TimestampHelper;\nimport tigase.vhosts.VHostManagerIfc;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.JID;\n\nimport java.time.LocalDate;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.component.modules.impl.DiscoveryModule.DISCO_ITEMS_XMLNS;\n\n/**\n * Implementation of JEP-030.\n * <br>\n * Created: Mon Mar 27 20:45:36 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\n@Bean(name = ServiceDiscovery.ID, parent = SessionManager.class, active = true)\npublic class ServiceDiscovery\n\t\textends XMPPProcessorAbstract {\n\n\tprotected static final String ID = \"disco\";\n\tprivate static final Logger log = Logger.getLogger(ServiceDiscovery.class.getName());\n\tprivate static final String[][] ELEMENTS = {Iq.IQ_QUERY_PATH, Iq.IQ_QUERY_PATH, Iq.IQ_QUERY_PATH};\n\tprivate static final String[] XMLNSS = {XMPPServiceCollector.INFO_XMLNS, XMPPServiceCollector.ITEMS_XMLNS,\n\t\t\t\t\t\t\t\t\t\t\tCommand.XMLNS};\n\tprivate static final Element[] DISCO_FEATURES = {\n\t\t\tnew Element(\"feature\", new String[]{\"var\"}, new String[]{XMPPServiceCollector.INFO_XMLNS}),\n\t\t\tnew Element(\"feature\", new String[]{\"var\"}, new String[]{XMPPServiceCollector.ITEMS_XMLNS})};\n\tprivate static final TimestampHelper timestampHelper = new TimestampHelper();\n\n\t@Inject(nullAllowed = true)\n\tprivate AccountServiceProvider serviceProvider;\n\n\t@Inject(nullAllowed = true)\n\tprivate SessionManager sessionManager;\n\t@Inject\n\tprivate VHostManagerIfc vhostManager;\n\t@Inject\n\tprivate AdHocCommandModule adHocCommandModule;\n\t@Inject\n\tprivate UserRepository userRepository;\n\n\t@ConfigField(alias = \"additional-account-details\", desc = \"Additional account details\")\n\tprivate Map<String, Map<String,String>> additionalAccountDetails = new HashMap<>();\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\n\t\tJID to = packet.getStanzaTo();\n\t\tif (to == null) {\n\t\t\tif (session == null || !session.isAuthorized()) {\n\t\t\t\tlog.log(Level.FINEST, \"got <iq/> packet with no ''from'' attribute = {0}\", packet);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tto = JID.jidInstance(session.getBareJID());\n\t\t}\n\n\t\tif (to != null) {\n\t\t\tJID from = packet.getStanzaFrom();\n\t\t\tif (from == null) {\n\t\t\t\tif (session == null || !session.isAuthorized()) {\n\t\t\t\t\tlog.log(Level.FINEST, \"got <iq/> packet with no ''from'' attribute = {0}\", packet);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tfrom = session.getjid();\n\t\t\t}\n\n\t\t\tif (packet.getType() == StanzaType.get && to.getLocalpart() != null && to.getResource() == null &&\n\t\t\t\t\tsessionManager.isLocalDomain(to.getDomain())) {\n\t\t\t\tElement query = packet.getElement()\n\t\t\t\t\t\t.findChild(el -> el.getName() == \"query\" &&\n\t\t\t\t\t\t\t\tel.getXMLNS() == \"http://jabber.org/protocol/disco#info\");\n\t\t\t\tif (!isLocalComponent(to)) {\n\t\t\t\t\tif (serviceProvider != null) {\n\t\t\t\t\t\tif (query != null && query.getAttributeStaticStr(\"node\") == null) {\n\t\t\t\t\t\t\t// custom handling..\n\t\t\t\t\t\t\tPacket result = packet.copyElementOnly();\n\t\t\t\t\t\t\tif (result.getStanzaTo() == null) {\n\t\t\t\t\t\t\t\tresult.initVars(from, to);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tresult.setPacketTo(serviceProvider.getServiceProviderComponentJid());\n\t\t\t\t\t\t\tresult.setPacketFrom(sessionManager.getComponentId());\n\t\t\t\t\t\t\tresults.offer(result);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// this packet is to local user (offline or not but to bare JID) - just forward to service provider component\n\t\t\t\t\t\t\tElement itemsQuery = packet.getElement().findChild(el -> el.getName() == \"query\" && el.getXMLNS() == \"http://jabber.org/protocol/disco#items\");\n\t\t\t\t\t\t\tif (itemsQuery != null && \"http://jabber.org/protocol/commands\".equals(itemsQuery.getAttributeStaticStr(\"node\".intern()))) {\n\t\t\t\t\t\t\t\tElement resultQuery = new Element(\"query\", new String[]{Packet.XMLNS_ATT}, new String[]{DISCO_ITEMS_XMLNS});\n\t\t\t\t\t\t\t\tadHocCommandModule.addCommandListItemsElements(\"http://jabber.org/protocol/commands\", to, from, resultQuery::addChild);\n\t\t\t\t\t\t\t\tPacket result = packet.okResult(resultQuery, 0);\n\t\t\t\t\t\t\t\tresults.offer(result);\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tPacket result = packet.copyElementOnly();\n\t\t\t\t\t\t\tif (packet.getStanzaTo() == null && session != null) {\n\t\t\t\t\t\t\t\tresult.initVars(packet.getStanzaFrom() != null ? packet.getStanzaFrom() : session.getJID(),\n\t\t\t\t\t\t\t\t\t\t\t\tto);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (packet.getStanzaFrom() == null) {\n\t\t\t\t\t\t\t\t// at this point we should already have \"from\" attribute set\n\t\t\t\t\t\t\t\t// if we do not have it, then we should drop this packet\n\t\t\t\t\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\t\t\t\t\"received <iq/> packet to forward to service provider component without ''from'' attribute, dropping packet = {0}\",\n\t\t\t\t\t\t\t\t\t\t\tpacket);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tresult.setPacketFrom(packet.getFrom());\n\t\t\t\t\t\t\tresult.setPacketTo(serviceProvider.getServiceProviderComponentJid());\n\n\t\t\t\t\t\t\tresults.offer(result);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (query != null && query.getAttributeStaticStr(\"node\") == null) {\n\t\t\t\t\t\tquery = new Element(\"query\");\n\t\t\t\t\t\tquery.setXMLNS(\"http://jabber.org/protocol/disco#info\");\n\t\t\t\t\t\tPacket result = packet.okResult(query, 0);\n\t\t\t\t\t\taddAccountFeatures(result, from.getBareJID().equals(to.getBareJID()));\n\t\t\t\t\t\tresults.offer(result);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsuper.process(packet, session, repo, results, settings);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tsuper.process(packet, session, repo, results, settings);\n\t\t\t\t}\n\t\t\t} else if ((packet.getType() == StanzaType.result || packet.getType() == StanzaType.error) &&\n\t\t\t\t\tfrom.getLocalpart() != null && from.getResource() == null &&\n\t\t\t\t\tsessionManager.isLocalDomain(from.getDomain())) {\n\t\t\t\tif (serviceProvider != null && packet.getPacketFrom() != null &&\n\t\t\t\t\t\tpacket.getPacketFrom().equals(serviceProvider.getServiceProviderComponentJid())) {\n\t\t\t\t\tif (session != null && session.isUserId(to.getBareJID())) {\n\t\t\t\t\t\tPacket result = packet.copyElementOnly();\n\t\t\t\t\t\tresult.setPacketFrom(packet.getFrom());\n\t\t\t\t\t\tresult.setPacketTo(session.getConnectionId());\n\t\t\t\t\t\taddAccountFeatures(result, from.getBareJID().equals(to.getBareJID()));\n\t\t\t\t\t\tresults.offer(result);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (sessionManager.isLocalDomain(to.getDomain())) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tPacket result = packet.copyElementOnly();\n\t\t\t\t\t\t\tresult.setPacketFrom(packet.getFrom());\n\t\t\t\t\t\t\tresult.setPacketTo(null);\n\t\t\t\t\t\t\taddAccountFeatures(result, false);\n\t\t\t\t\t\t\tresults.offer(result);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tsuper.process(packet, session, repo, results, settings);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tsuper.process(packet, session, repo, results, settings);\n\t\t\t}\n\t\t} else {\n\t\t\tsuper.process(packet, session, repo, results, settings);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processFromUserToServerPacket(JID connectionId, Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t  NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t\t  Map<String, Object> settings) throws PacketErrorTypeException {\n\n\t\t// Handled elsewhere (in MessageRouter)\n\t\tif (packet.getStanzaTo() != null) {\n\t\t\tlog.log(Level.FINEST, \"forwarding packet to MR\" + packet.toString());\n\t\t\tresults.offer(packet.copyElementOnly());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processNullSessionPacket(Packet packet, NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t Map<String, Object> settings) throws PacketErrorTypeException {\n\t\tif (packet.getElemName() == Iq.ELEM_NAME && (packet.getType().equals(StanzaType.error) || packet.getType().equals(StanzaType.result))) {\n\t\t\t// https://xmpp.org/rfcs/rfc6120.html#stanzas-semantics-iq\n\t\t\t// An entity that receives a stanza of type \"result\" or \"error\" MUST NOT respond to the stanza\n\t\t\t// by sending a further IQ response of type \"result\" or \"error\"; however, the requesting entity\n\t\t\t// MAY send another request (e.g., an IQ of type \"set\" to provide obligatory information discovered\n\t\t\t// through a get/result pair).\n\t\t\treturn;\n\t\t} else {\n\t\t\tresults.offer(Authorization.RECIPIENT_UNAVAILABLE.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"The target is unavailable at this time.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processServerSessionPacket(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t\t\t\t   Queue<Packet> results, Map<String, Object> settings)\n\t\t\tthrows PacketErrorTypeException {\n\n\t\t// Handled elsewhere (in MessageRouter)\n\t}\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(final XMPPResourceConnection session) {\n\t\treturn DISCO_FEATURES;\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\tprivate boolean isLocalComponent(JID jid) {\n\t\treturn vhostManager.isLocalDomainOrComponent(jid.getLocalpart() + \".\" + jid.getDomain());\n\t}\n\n\tprivate void addAccountFeatures(Packet result, boolean isOwner) {\n\t\tif (result.getType() != StanzaType.result) {\n\t\t\treturn;\n\t\t}\n\t\tElement query = result.getElement().getChild(\"query\");\n\t\tif (query != null) {\n\t\t\tif (isOwner) {\n\t\t\t\tList<Element> features = query.getChildren();\n\t\t\t\tquery.setChildren(Collections.emptyList());\n\t\t\t\tquery.addChild(new Element(\"identity\", new String[]{\"category\", \"type\"},\n\t\t\t\t\t\t\t\t\t\t   new String[]{\"account\", \"registered\"}));\n\t\t\t\tif (features != null) {\n\t\t\t\t\tfeatures.forEach(query::addChild);\n\t\t\t\t}\n\t\t\t\tsessionManager.getDiscoFeatures(result.getStanzaFrom()).forEach(query::addChild);\n\t\t\t\ttry {\n\t\t\t\t\tOptional.ofNullable(userRepository.getData(result.getStanzaFrom().getBareJID(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   AccountExpirationService.ACCOUNT_EXPIRATION_DATE))\n\t\t\t\t\t\t\t.map(LocalDate::parse)\n\t\t\t\t\t\t\t.ifPresent(expirationDate -> {\n\t\t\t\t\t\t\t\tElement x = new DataForm.Builder(Command.DataType.result).withFields(builder -> {\n\t\t\t\t\t\t\t\t\tbuilder.withField(DataForm.FieldType.Hidden, \"FORM_TYPE\", field -> {\n\t\t\t\t\t\t\t\t\t\tfield.setValue(\"tigase:disco:account:0\");\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t\t.withField(DataForm.FieldType.TextSingle, \"expiration-date\", field -> field.setValue(expirationDate.toString()));\n\t\t\t\t\t\t\t\t}).build();\n\t\t\t\t\t\t\t\tquery.addChild(x);\n\t\t\t\t\t\t\t});\n\t\t\t\t} catch (TigaseDBException e) {\n\t\t\t\t\tlog.log(Level.SEVERE, \"Failed to retrieve account \" + result.getStanzaFrom() + \" expiration date\", e);\n\t\t\t\t}\n\t\t\t\tif (additionalAccountDetails != null) {\n\t\t\t\t\tfor (var entry : additionalAccountDetails.entrySet()) {\n\t\t\t\t\t\tif (entry.getValue() != null) {\n\t\t\t\t\t\t\tElement x = new DataForm.Builder(Command.DataType.result).withFields(builder -> {\n\t\t\t\t\t\t\t\tbuilder.withField(DataForm.FieldType.Hidden, \"FORM_TYPE\", field -> {\n\t\t\t\t\t\t\t\t\t\t\tfield.setValue(entry.getKey());\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tfor (var fieldEntry : entry.getValue().entrySet()) {\n\t\t\t\t\t\t\t\t\tbuilder.withField(DataForm.FieldType.TextSingle, fieldEntry.getKey(), field -> field.setValue(fieldEntry.getValue()));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}).build();\n\t\t\t\t\t\t\tquery.addChild(x);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic interface AccountServiceProvider {\n\n\t\tJID getServiceProviderComponentJid();\n\n\t}\n}    // ServiceDiscovery\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/SessionBind.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.annotation.*;\n\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Logger;\n\nimport static tigase.xmpp.impl.SessionBind.XMLNS;\n\n/**\n * Describe class SessionBind here.\n * <br>\n * Created: Mon Feb 20 22:43:59 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Id(XMLNS)\n@Handle(path = {Iq.ELEM_NAME, \"session\"}, xmlns = XMLNS)\n@StreamFeatures(@StreamFeature(elem = \"session\", xmlns = XMLNS, children = {\"optional\"}))\n@DiscoFeatures({XMLNS})\n@Bean(name = SessionBind.XMLNS, parent = SessionManager.class, active = true)\npublic class SessionBind\n\t\textends AnnotatedXMPPProcessor\n\t\timplements XMPPProcessorIfc {\n\n\tprotected static final String XMLNS = \"urn:ietf:params:xml:ns:xmpp-session\";\n\tpublic static final String SESSION_KEY = \"Session-Set\";\n\tprivate static final Logger log = Logger.getLogger(SessionBind.class.getName());\n\n\t@Override\n\tpublic void process(final Packet packet, final XMPPResourceConnection session, final NonAuthUserRepository repo,\n\t\t\t\t\t\tfinal Queue<Packet> results, final Map<String, Object> settings) throws XMPPException {\n\t\tif (session == null) {\n\t\t\treturn;\n\t\t}    // end of if (session == null)\n\t\tif (!session.isAuthorized()) {\n\t\t\tresults.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet, \"Session is not yet authorized.\", false));\n\n\t\t\treturn;\n\t\t}    // end of if (!session.isAuthorized())\n\n\t\t// Element request = packet.getElement();\n\t\tStanzaType type = packet.getType();\n\n\t\tswitch (type) {\n\t\t\tcase set:\n\t\t\t\tsession.putSessionData(SESSION_KEY, \"true\");\n\t\t\t\tresults.offer(packet.okResult((String) null, 0));\n\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet, \"Session type is incorrect\", false));\n\n\t\t\t\tbreak;\n\t\t}    // end of switch (type)\n\t}\n\n\t@Override\n\tpublic Element[] supStreamFeatures(final XMPPResourceConnection session) {\n\t\tif ((session != null) && (session.getSessionData(SESSION_KEY) == null) && session.isAuthorized()) {\n\t\t\treturn super.supStreamFeatures(session);\n\t\t} else {\n\t\t\treturn null;\n\t\t}    // end of if (session.isAuthorized()) else\n\t}\n}    // SessionBind\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/SimpleForwarder.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.server.Packet;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Logger;\n\n/**\n * Describe class SimpleForwarder here.\n * <br>\n * Created: Sat Jan 13 16:58:41 2007\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic abstract class SimpleForwarder\n\t\textends XMPPProcessor\n\t\timplements XMPPProcessorIfc {\n\n\tprivate static Logger log = Logger.getLogger(SimpleForwarder.class.getName());\n\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\t\tif (session == null) {\n\t\t\treturn;\n\t\t}    // end of if (session == null)\n\t\ttry {\n\t\t\tBareJID id = packet.getStanzaTo().getBareJID();\n\n\t\t\tif (session.isUserId(id)) {\n\n\t\t\t\t// Yes this is message to 'this' client\n\t\t\t\tPacket result = packet.copyElementOnly();\n\n\t\t\t\tresult.setStableId(packet.getStableId());\n\t\t\t\tresult.setPacketTo(session.getConnectionId(packet.getStanzaTo()));\n\t\t\t\tresult.setPacketFrom(packet.getTo());\n\t\t\t\tresults.offer(result);\n\t\t\t} else {\n\n\t\t\t\t// This is message to some other client\n\t\t\t\tPacket result = packet.copyElementOnly();\n\t\t\t\tresults.offer(result);\n\t\t\t}    // end of else\n\t\t} catch (NotAuthorizedException e) {\n\t\t\tlog.warning(\"NotAuthorizedException for packet: \" + packet);\n\t\t\tresults.offer(\n\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(packet, \"You must authorize session first.\", true));\n\t\t}    // end of try-catch\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/StartTLS.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Command;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.vhosts.VHostItem;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\n\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Describe class StartTLS here.\n * <br>\n * Created: Fri Mar 24 07:22:57 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = StartTLS.ID, parent = SessionManager.class, active = true)\npublic class StartTLS\n\t\textends XMPPProcessor\n\t\timplements XMPPProcessorIfc, XMPPPreprocessorIfc {\n\t// private static final String TLS_STARTED_KEY = \"TLS-Started\";\n\n\tpublic static final String EL_NAME = \"starttls\";\n\n\tprotected static final String ID = EL_NAME;\n\tprivate static final String[][] ELEMENTS = {{EL_NAME}, {\"proceed\"}, {\"failure\"}};\n\tprivate static final Logger log = Logger.getLogger(StartTLS.class.getName());\n\tprivate static final String XMLNS = \"urn:ietf:params:xml:ns:xmpp-tls\";\n\tprivate static final String[] XMLNSS = {XMLNS, XMLNS, XMLNS};\n\tprivate static final Element[] F_REQUIRED = {\n\t\t\tnew Element(\"starttls\", new Element[]{new Element(\"required\")}, new String[]{Packet.XMLNS_ATT},\n\t\t\t\t\t\tnew String[]{XMLNS})};\n\tprivate static final Element[] F_NOT_REQUIRED = {\n\t\t\tnew Element(EL_NAME, new String[]{Packet.XMLNS_ATT}, new String[]{XMLNS})};\n\n\tprivate Element failure = new Element(\"failure\", new String[]{Packet.XMLNS_ATT}, new String[]{XMLNS});\n\tprivate Element proceed = new Element(\"proceed\", new String[]{Packet.XMLNS_ATT}, new String[]{XMLNS});\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic void process(final Packet packet, final XMPPResourceConnection session, final NonAuthUserRepository repo,\n\t\t\t\t\t\tfinal Queue<Packet> results, final Map<String, Object> settings) {\n\t\tif (session == null) {\n\t\t\treturn;\n\t\t}    // end of if (session == null)\n\t\tif (packet.isElement(\"starttls\", XMLNS)) {\n\t\t\tif (session.getSessionData(ID) != null) {\n\n\t\t\t\t// Somebody tries to activate multiple TLS layers.\n\t\t\t\t// This is possible and can even work but this can also be\n\t\t\t\t// a DOS attack. Blocking it now, unless someone requests he wants\n\t\t\t\t// to have multiple layers of TLS for his connection\n\t\t\t\t//\n\t\t\t\t// 2021-09-20, wojtek: switching level from WARNING to FINE as while it could be possible to have\n\t\t\t\t// multiple starttls requests, we don't advertise it in features after first time so the client is\n\t\t\t\t// definitely doing something wrong…\n\t\t\t\tlog.log(Level.FINEST, \"Multiple TLS requests, possible DOS attack, closing connection: {0}\", packet);\n\t\t\t\tresults.offer(packet.swapFromTo(failure, null, null));\n\t\t\t\tresults.offer(Command.CLOSE.getPacket(packet.getTo(), packet.getFrom(), StanzaType.set,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  session.nextStanzaId()));\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsession.putSessionData(ID, \"true\");\n\n\t\t\tPacket result = Command.STARTTLS.getPacket(packet.getTo(), packet.getFrom(), StanzaType.set,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   session.nextStanzaId(), Command.DataType.submit);\n\n\t\t\tCommand.setData(result, proceed);\n\t\t\tresults.offer(result);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"Unknown TLS element: {0}\", packet);\n\t\t\tresults.offer(packet.swapFromTo(failure, null, null));\n\t\t\tresults.offer(\n\t\t\t\t\tCommand.CLOSE.getPacket(packet.getTo(), packet.getFrom(), StanzaType.set, session.nextStanzaId()));\n\t\t}    // end of if (packet.getElement().getName().equals(\"starttls\")) else\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\t@Override\n\tpublic Element[] supStreamFeatures(final XMPPResourceConnection session) {\n\n\t\t// If session does not exist, just return null, we don't provide features\n\t\t// for non-existen stream, the second condition means that the TLS\n\t\t// has not been yet completed for the user connection.\n\t\tif ((session != null) && (session.getSessionData(ID) == null)) {\n\t\t\tif (session.isEncrypted()) {\n\t\t\t\t// connection is already SSL/TLS protected\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tVHostItem vhost = session.getDomain();\n\n\t\t\tif ((vhost != null) && session.isTlsRequired()) {\n\t\t\t\treturn F_REQUIRED;\n\t\t\t} else {\n\t\t\t\treturn F_NOT_REQUIRED;\n\t\t\t}\n\t\t} else {\n\t\t\treturn null;\n\t\t}    // end of if (session.isAuthorized()) else\n\t}\n\n\t@Override\n\tpublic boolean preProcess(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t  Queue<Packet> results, Map<String, Object> settings) {\n\t\tboolean stop = false;\n\n\t\tif ((session == null) || session.isServerSession()) {\n\t\t\treturn stop;\n\t\t}\n\n\t\tVHostItem vhost = session.getDomain();\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"VHost: {0}\", new Object[]{vhost});\n\t\t}\n\n\t\t// Check whether the TLS has been completed\n\t\t// and the packet is allowed to be processed.\n\t\tif ((vhost != null) && (session.isTlsRequired() && !session.isEncrypted()) &&\n\t\t\t\t!packet.isElement(EL_NAME, XMLNS)) {\n\t\t\tstop = true;\n\t\t}\n\n\t\treturn stop;\n\t}\n}    // StartTLS\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/StartZLib.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Command;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\n\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Jul 29, 2009 4:03:44 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = StartZLib.ID, parent = SessionManager.class, active = true)\npublic class StartZLib\n\t\textends XMPPProcessor\n\t\timplements XMPPProcessorIfc {\n\n\tprotected static final String ID = \"zlib\";\n\tprivate static final String[][] ELEMENTS = {{\"compress\"}, {\"compressed\"}, {\"failure\"}};\n\tprivate static final String XMLNS = \"http://jabber.org/protocol/compress\";\n\tprivate static final String[] XMLNSS = {XMLNS, XMLNS, XMLNS};\n\tprivate static final Element[] FEATURES = {\n\t\t\tnew Element(\"compression\", new Element[]{new Element(\"method\", \"zlib\")}, new String[]{\"xmlns\"},\n\t\t\t\t\t\tnew String[]{\"http://jabber.org/features/compress\"})};\n\tprivate static Logger log = Logger.getLogger(StartZLib.class.getName());\n\n\tprivate Element compressed = new Element(\"compressed\", new String[]{\"xmlns\"}, new String[]{XMLNS});\n\tprivate Element failure = new Element(\"failure\", new String[]{\"xmlns\"}, new String[]{XMLNS});\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings) throws XMPPException {\n\t\tif (session == null) {\n\t\t\treturn;\n\t\t}    // end of if (session == null)\n\t\tif (packet.isElement(\"compress\", XMLNS)) {\n\t\t\tif (session.getSessionData(ID) != null) {\n\n\t\t\t\t// Somebody tries to activate multiple TLS layers.\n\t\t\t\t// This is possible and can even work but this can also be\n\t\t\t\t// a DOS attack. Blocking it now, unless someone requests he wants\n\t\t\t\t// to have multiple layers of TLS for his connection\n\t\t\t\tlog.log(Level.WARNING, \"Multiple ZLib requests, possible DOS attack, closing connection: {0}\", packet);\n\t\t\t\tresults.offer(packet.swapFromTo(failure, null, null));\n\t\t\t\tresults.offer(Command.CLOSE.getPacket(packet.getTo(), packet.getFrom(), StanzaType.set,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  session.nextStanzaId()));\n\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsession.putSessionData(ID, \"true\");\n\n\t\t\tPacket result = Command.STARTZLIB.getPacket(packet.getTo(), packet.getFrom(), StanzaType.set,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tsession.nextStanzaId(), Command.DataType.submit);\n\n\t\t\tCommand.setData(result, compressed);\n\t\t\tresults.offer(result);\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"Unknown ZLIB element: {0}\", packet);\n\t\t\tresults.offer(packet.swapFromTo(failure, null, null));\n\t\t\tresults.offer(\n\t\t\t\t\tCommand.CLOSE.getPacket(packet.getTo(), packet.getFrom(), StanzaType.set, session.nextStanzaId()));\n\t\t}\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\t@Override\n\tpublic Element[] supStreamFeatures(final XMPPResourceConnection session) {\n\n\t\t// If session does not exist, just return null, we don't provide features\n\t\t// for non-existen stream\n\t\t// We also do not want to provide compression if it is already started\n\t\t// and the compression has to be available after TLS has been completed.\n\t\tif ((session != null) && (session.getSessionData(ID) == null) && !session.isAuthorized()) {\n\n//    && session.getSessionData(StartTLS.ID) != null) {\n\t\t\treturn FEATURES;\n\t\t} else {\n\t\t\treturn null;\n\t\t}    // end of if (session.isAuthorized()) else\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/StreamManagementInline.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.component.exceptions.ComponentException;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Command;\nimport tigase.server.Packet;\nimport tigase.server.ReceiverTimeoutHandler;\nimport tigase.server.xmppclient.StreamManagementCommand;\nimport tigase.server.xmppclient.StreamManagementIOProcessor;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.JID;\n\nimport java.lang.reflect.Field;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.TimeUnit;\n\n@Bean(name = \"urn:xmpp:sm:3\", parent = SessionManager.class, active = true)\npublic class StreamManagementInline\n\t\timplements SaslAuth2.Inline {\n\n\tpublic static final String SESSION_RESUMPTION_ID_KEY = \"session-resumption-id\";\n\n\t@Inject(bean = \"service\", nullAllowed = true)\n\tprivate SessionManager sessionManager;\n\n\tpublic boolean canHandle(XMPPResourceConnection connection, Element el) {\n\t\treturn switch (el.getName()) {\n\t\t\tcase \"enable\", \"resume\" -> el.getXMLNS() == StreamManagementIOProcessor.XMLNS;\n\t\t\tdefault -> false;\n\t\t};\n\t}\n\n\tpublic Element[] supStreamFeatures(Action action) {\n\t\treturn switch (action) {\n\t\t\tcase sasl2 -> new Element[]{\n\t\t\t\t\tnew Element(\"sm\", new String[]{\"xmlns\"}, new String[]{StreamManagementIOProcessor.XMLNS})};\n\t\t\tcase bind2 -> new Element[]{\n\t\t\t\t\tnew Element(\"feature\", new String[]{\"var\"}, new String[]{StreamManagementIOProcessor.XMLNS})};\n\t\t};\n\t}\n\n\tpublic CompletableFuture<Result> process(XMPPResourceConnection session, JID boundJID, Element action) {\n\t\treturn switch (action.getName()) {\n\t\t\tcase \"resume\" -> resumeSession(session, action);\n\t\t\tcase \"enable\" -> enable(session, action);\n\t\t\tdefault -> CompletableFuture.failedFuture(new ComponentException(Authorization.BAD_REQUEST));\n\t\t};\n\t}\n\n\tprivate CompletableFuture<Result> enable(XMPPResourceConnection session, Element action) {\n\t\ttry {\n\t\t\tboolean resume = Boolean.parseBoolean(action.getAttributeStaticStr(\"resume\"));\n\t\t\tInteger max = action.getAttributeStaticStr(\"max\") != null\n\t\t\t\t\t\t  ? Integer.parseInt(action.getAttributeStaticStr(\"max\"))\n\t\t\t\t\t\t  : null;\n\t\t\tPacket request = StreamManagementCommand.ENABLE.create(session.getSMComponentId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   session.getConnectionId());\n\t\t\tCommand.addCheckBoxField(request, \"resume\", resume);\n\t\t\tif (resume && max != null) {\n\t\t\t\tCommand.addFieldValue(request, \"max\", String.valueOf(max));\n\t\t\t}\n\t\t\tCompletableFuture<Result> future = new CompletableFuture();\n\t\t\tsessionManager.addOutPacketWithTimeout(request, new ReceiverTimeoutHandler() {\n\t\t\t\t@Override\n\t\t\t\tpublic void timeOutExpired(Packet data) {\n\t\t\t\t\tfuture.complete(prepareFailed(Authorization.REMOTE_SERVER_TIMEOUT));\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void responseReceived(Packet data, Packet result) {\n\t\t\t\t\tElement enabled = new Element(\"enabled\");\n\t\t\t\t\tenabled.setXMLNS(StreamManagementIOProcessor.XMLNS);\n\t\t\t\t\tString resumptionId = Command.getFieldValue(result, \"id\");\n\t\t\t\t\tif (resumptionId != null) {\n\t\t\t\t\t\tenabled.setAttribute(\"id\", resumptionId);\n\t\t\t\t\t}\n\t\t\t\t\tString location = Command.getFieldValue(result, \"location\");\n\t\t\t\t\tif (location != null) {\n\t\t\t\t\t\tenabled.addAttribute(\"location\", location);\n\t\t\t\t\t}\n\t\t\t\t\tString maxTimeout = Command.getFieldValue(result, \"max\");\n\t\t\t\t\tif (maxTimeout != null) {\n\t\t\t\t\t\tenabled.addAttribute(\"max\", maxTimeout);\n\t\t\t\t\t}\n\n\t\t\t\t\tfuture.complete(new Result(enabled, true));\n\t\t\t\t}\n\t\t\t}, 10, TimeUnit.SECONDS);\n\n\t\t\treturn future;\n\t\t} catch (Exception ex) {\n\t\t\treturn CompletableFuture.completedFuture(prepareFailed(Authorization.INTERNAL_SERVER_ERROR));\n\t\t}\n\t}\n\n\tprivate CompletableFuture<Result> resumeSession(XMPPResourceConnection session, Element action) {\n\t\tString resumptionId = action.getAttributeStaticStr(\"previd\");\n\t\tint h = Integer.parseInt(action.getAttributeStaticStr(\"h\"));\n\t\tOptional<XMPPResourceConnection> oldSessionOptional = session.getParentSession()\n\t\t\t\t.getActiveResources()\n\t\t\t\t.stream()\n\t\t\t\t.filter(sess -> resumptionId.equals(sess.getSessionData(SESSION_RESUMPTION_ID_KEY)))\n\t\t\t\t.findFirst();\n\t\tif (oldSessionOptional.isEmpty()) {\n\t\t\treturn CompletableFuture.completedFuture(prepareFailed(Authorization.ITEM_NOT_FOUND));\n\t\t}\n\n\t\ttry {\n\t\t\tPacket request = StreamManagementCommand.MOVE_STREAM.create(session.getSMComponentId(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsession.getConnectionId());\n\t\t\tCommand.addFieldValue(request, \"resumption-id\", resumptionId);\n\t\t\tCommand.addFieldValue(request, \"h\", String.valueOf(h));\n\n\t\t\tCompletableFuture<Result> future = new CompletableFuture<>();\n\t\t\tsessionManager.addOutPacketWithTimeout(request, new ReceiverTimeoutHandler() {\n\t\t\t\t@Override\n\t\t\t\tpublic void timeOutExpired(Packet data) {\n\t\t\t\t\tfuture.complete(prepareFailed(Authorization.RESOURCE_CONSTRAINT));\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void responseReceived(Packet data, Packet result) {\n\t\t\t\t\tif (result.getType() == StanzaType.error) {\n\t\t\t\t\t\tAuthorization auth = Authorization.INTERNAL_SERVER_ERROR;\n\t\t\t\t\t\tElement el = result.getElemChild(\"error\");\n\t\t\t\t\t\tif (el != null) {\n\t\t\t\t\t\t\tList<Element> children = el.getChildren();\n\t\t\t\t\t\t\tif (children != null) {\n\t\t\t\t\t\t\t\tfor (Element child : children) {\n\t\t\t\t\t\t\t\t\tAuthorization tmp = Authorization.getByCondition(child.getName());\n\t\t\t\t\t\t\t\t\tif (tmp != null) {\n\t\t\t\t\t\t\t\t\t\tauth = tmp;\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfuture.complete(prepareFailed(auth));\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tField resourceField = XMPPResourceConnection.class.getDeclaredField(\"resource\");\n\t\t\t\t\t\t\tresourceField.setAccessible(true);\n\t\t\t\t\t\t\tresourceField.set(session, oldSessionOptional.get().getResource());\n\t\t\t\t\t\t\tElement resumed = new Element(\"resumed\");\n\t\t\t\t\t\t\tresumed.setXMLNS(StreamManagementIOProcessor.XMLNS);\n\t\t\t\t\t\t\tString hResponse = Command.getFieldValue(result, \"h\");\n\t\t\t\t\t\t\tresumed.setAttribute(\"h\", hResponse);\n\t\t\t\t\t\t\tfuture.complete(new Result(resumed, false));\n\t\t\t\t\t\t} catch (Throwable ex) {\n\t\t\t\t\t\t\tfuture.completeExceptionally(ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}, 10, TimeUnit.SECONDS);\n\n\t\t\treturn future;\n\t\t} catch (NoConnectionIdException ex) {\n\t\t\treturn CompletableFuture.completedFuture(prepareFailed(Authorization.INTERNAL_SERVER_ERROR));\n\t\t}\n\t}\n\n\tprivate Result prepareFailed(Authorization authorization) {\n\t\tElement failed = new Element(\"failed\");\n\t\tfailed.setXMLNS(StreamManagementIOProcessor.XMLNS);\n\t\tfailed.withElement(authorization.getCondition(), Packet.ERROR_NS, (String) null);\n\t\treturn new Result(failed, true);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/TokenBucketPool.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.UnregisterAware;\n\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Timer;\nimport java.util.TimerTask;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class TokenBucketPool\n\t\timplements Initializable, UnregisterAware {\n\n\tprivate final static Logger log = Logger.getLogger(TokenBucketPool.class.getName());\n\tprivate final ConcurrentHashMap<String, TokenBucket> items = new ConcurrentHashMap<>();\n\tprivate boolean autoPurgeEnabled = true;\n\tprivate final TimerTask purgerTask = new TimerTask() {\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tif (autoPurgeEnabled) {\n\t\t\t\tpurge();\n\t\t\t}\n\t\t}\n\t};\n\t// unit: events\n\tprivate long defaultRate = 100000;\n\tprivate TimeUnit timeUnit = TimeUnit.SECONDS;\n\t// unit: ns\n\tprivate long defaultPer = timeUnit.toNanos(1);\n\tprivate Timer timer;\n\n\tpublic TokenBucketPool(long rate, long per, TimeUnit timeUnit) {\n\t\tthis.defaultRate = rate;\n\t\tthis.defaultPer = timeUnit.toNanos(per);\n\t\tthis.timeUnit = timeUnit;\n\t}\n\n\tpublic TokenBucketPool(long rate, long per) {\n\t\tthis.defaultRate = rate;\n\t\tthis.defaultPer = timeUnit.toNanos(per);\n\t}\n\n\tpublic TokenBucketPool() {\n\t}\n\n\tpublic void setAutoPurgeEnabled(boolean enabled) {\n\t\tthis.autoPurgeEnabled = enabled;\n\t}\n\n\tpublic TimeUnit getTimeUnit() {\n\t\treturn timeUnit;\n\t}\n\n\tpublic void setTimeUnit(TimeUnit timeUnit) {\n\t\tthis.timeUnit = timeUnit;\n\t}\n\n\tpublic long getDefaultRate() {\n\t\treturn defaultRate;\n\t}\n\n\tpublic void setDefaultRate(long defaultRate) {\n\t\tthis.defaultRate = defaultRate;\n\t}\n\n\tpublic long getDefaultPer() {\n\t\treturn timeUnit.convert(defaultPer, TimeUnit.NANOSECONDS);\n\t}\n\n\tpublic void setDefaultPer(long defaultPer) {\n\t\tthis.defaultPer = timeUnit.toNanos(defaultPer);\n\t}\n\n\tpublic boolean consume(final String key) {\n\t\tTokenBucket item = items.get(key);\n\t\tif (item == null) {\n\t\t\titem = new TokenBucket(System.nanoTime(), defaultRate, defaultPer);\n\t\t\titems.put(key, item);\n\t\t}\n\n\t\treturn consume(item);\n\t}\n\n\tpublic void purge() {\n\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\tlog.fine(\"Purging full TokenBuckets...\");\n\t\t}\n\t\tIterator<Map.Entry<String, TokenBucket>> iterator = this.items.entrySet().iterator();\n\t\tfinal long current = System.nanoTime();\n\n\t\twhile (iterator.hasNext()) {\n\t\t\tMap.Entry<String, TokenBucket> entry = iterator.next();\n\t\t\tif (entry.getValue().estimateAllowance(current) >= entry.getValue().rate) {\n\t\t\t\titerator.remove();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\tif (timer == null) {\n\t\t\ttimer.cancel();\n\t\t}\n\t\ttimer = null;\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tif (timer != null) {\n\t\t\tbeforeUnregister();\n\t\t}\n\n\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\tlog.fine(\"TokenBucketPool Created. Auto purge task created.\");\n\t\t}\n\n\t\ttimer = new Timer(\"TokenBuckerPoolTimerThread\", true);\n\n\t\ttimer.schedule(purgerTask, TimeUnit.HOURS.toMillis(4));\n\t}\n\n\tint size() {\n\t\treturn this.items.size();\n\t}\n\n\tprivate boolean consume(TokenBucket item) {\n\t\treturn item.consume();\n\t}\n\n\t/**\n\t * Single TokenBucket.\n\t */\n\tpublic static class TokenBucket {\n\n\t\t// unit: ns\n\t\tprivate final float per;\n\t\t// unit: events\n\t\tprivate final float rate;\n\t\t// unit: events\n\t\tprivate float allowance = 1;\n\t\t// unit: ns\n\t\tprivate long lastCheck;\n\n\t\tTokenBucket(long lastCheck, float rate, float per) {\n\t\t\tthis.per = per;\n\t\t\tthis.rate = rate;\n\t\t\tthis.lastCheck = lastCheck;\n\t\t}\n\n\t\t/**\n\t\t * Create Token Bucket.\n\t\t *\n\t\t * @param rate amount of available tokens\n\t\t * @param per per nanosecond!\n\t\t */\n\t\tpublic TokenBucket(long rate, long per) {\n\t\t\tthis.lastCheck = System.nanoTime();\n\t\t\tthis.per = per;\n\t\t\tthis.rate = rate;\n\t\t}\n\n\t\t/**\n\t\t * Consume token if available.\n\t\t *\n\t\t * @return {@code true} if token was available.\n\t\t */\n\t\tpublic boolean consume() {\n\t\t\tfinal long current = System.nanoTime();\n\t\t\tupdateAllowance(current);\n\t\t\treturn consumeNoUpdate();\n\t\t}\n\n\t\tfinal boolean consumeNoUpdate() {\n\t\t\tif (this.allowance < 1.0) {\n\t\t\t\treturn false;\n\t\t\t} else {\n\t\t\t\tthis.allowance -= 1.0;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\tfloat getAllowance() {\n\t\t\treturn allowance;\n\t\t}\n\n\t\tfinal float estimateAllowance(long current) {\n\t\t\tfinal float timePassed = current - this.lastCheck;\n\t\t\tthis.lastCheck = current;\n\t\t\treturn this.allowance + timePassed * (this.rate / this.per);\n\t\t}\n\n\t\tfinal void updateAllowance(long current) {\n\t\t\tfinal long timePassed = current - this.lastCheck;\n\t\t\tthis.lastCheck = current;\n\t\t\tthis.allowance += timePassed * (this.rate / this.per);\n\t\t\tif (this.allowance > this.rate) {\n\t\t\t\tthis.allowance = this.rate; // throttle\n\t\t\t}\n\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/UrnXmppPing.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Logger;\n\n/**\n * XEP-0199: XMPP Ping\n *\n * @author <a href=\"mailto:bmalkow@tigase.org\">Bartosz Małkowski</a>\n*/\n@Bean(name = UrnXmppPing.ID, parent = SessionManager.class, active = true)\npublic class UrnXmppPing\n\t\textends XMPPProcessorAbstract {\n\n\tprivate static final Logger log = Logger.getLogger(UrnXmppPing.class.getName());\n\tprivate static final String[][] ELEMENTS = {{Iq.ELEM_NAME, \"ping\"}};\n\tprivate static final String XMLNS = \"urn:xmpp:ping\";\n\tprotected static final String ID = XMLNS;\n\tprivate static final Element[] DISCO_FEATURES = {new Element(\"feature\", new String[]{\"var\"}, new String[]{XMLNS})};\n\tprivate static final String[] XMLNSS = {XMLNS};\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic void processFromUserToServerPacket(JID connectionId, Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t  NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t\t  Map<String, Object> settings) {\n\t\tresults.offer(packet.okResult((Element) null, 0));\n\t}\n\n\t@Override\n\tpublic void processNullSessionPacket(Packet packet, NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t Map<String, Object> settings) throws PacketErrorTypeException {\n\n\t\tif (packet.getElemName() == Iq.ELEM_NAME && (packet.getType().equals(StanzaType.error) || packet.getType().equals(StanzaType.result))) {\n\t\t\t// https://xmpp.org/rfcs/rfc6120.html#stanzas-semantics-iq\n\t\t\t// An entity that receives a stanza of type \"result\" or \"error\" MUST NOT respond to the stanza\n\t\t\t// by sending a further IQ response of type \"result\" or \"error\"; however, the requesting entity\n\t\t\t// MAY send another request (e.g., an IQ of type \"set\" to provide obligatory information discovered\n\t\t\t// through a get/result pair).\n\t\t\treturn;\n\t\t} else {\n\t\t\tresults.offer(Authorization.SERVICE_UNAVAILABLE.getResponseMessage(packet, \"Service not available.\", true));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processServerSessionPacket(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t\t\t\t   Queue<Packet> results, Map<String, Object> settings) {\n\t\tresults.offer(packet.okResult((Element) null, 0));\n\t}\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(final XMPPResourceConnection session) {\n\t\treturn DISCO_FEATURES;\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/VCard4.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserNotFoundException;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.DomBuilderHandler;\nimport tigase.xml.Element;\nimport tigase.xml.SimpleParser;\nimport tigase.xml.SingletonFactory;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.annotation.DiscoFeatures;\nimport tigase.xmpp.impl.annotation.Handle;\nimport tigase.xmpp.impl.annotation.Handles;\nimport tigase.xmpp.impl.annotation.Id;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.xmpp.impl.VCard4.*;\n\n/**\n * @author andrzej\n */\n@Id(ID)\n@Handles({@Handle(path = {Iq.ELEM_NAME, VCARD_EL}, xmlns = XMLNS)})\n@DiscoFeatures({XMLNS})\n@Bean(name = VCard4.ID, parent = SessionManager.class, active = true)\npublic class VCard4\n\t\textends VCardXMPPProcessorAbstract {\n\n\tprotected static final String VCARD_EL = \"vcard\";\n\tprotected static final String XMLNS = \"urn:ietf:params:xml:ns:vcard-4.0\";\n\t// private varibles reused in public variables\n\tstatic final String ID = \"vcard-xep-0292\";\n\t// public variables used in other places which depends on private variables above\n\tpublic static final String REPO_NODE = ID;\n\t// private variables used only by this processor\n\tstatic final String VCARD_KEY = ID;\n\tprivate static final Logger log = Logger.getLogger(VCard4.class.getCanonicalName());\n\tprivate static final SimpleParser parser = SingletonFactory.getParserInstance();\n\n\t@Override\n\tpublic void processFromUserOutPacket(JID connectionId, Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t Map<String, Object> settings) throws PacketErrorTypeException {\n\t\tif (session.isLocalDomain(packet.getStanzaTo().getDomain(), false)) {\n\n\t\t\t// This is a local user so we can quickly get his vCard from the database\n\t\t\ttry {\n\t\t\t\tString strvCard = repo.getPublicData(packet.getStanzaTo().getBareJID(), ID, VCARD_KEY, null);\n\t\t\t\tPacket result = null;\n\n\t\t\t\tif (strvCard != null) {\n\t\t\t\t\tresult = parseXMLData(strvCard, packet);\n\t\t\t\t} else {\n\t\t\t\t\tresult = packet.okResult((String) null, 1);\n\t\t\t\t}    // end of if (vcard != null)\n\t\t\t\tresult.setPacketTo(connectionId);\n\t\t\t\tresults.offer(result);\n\t\t\t} catch (UserNotFoundException e) {\n\t\t\t\tresults.offer(Authorization.ITEM_NOT_FOUND.getResponseMessage(packet, \"User not found\", true));\n\t\t\t}    // end of try-catch\n\t\t} else {\n\n\t\t\t// Else forward the packet to a remote server\n\t\t\tresults.offer(packet.copyElementOnly());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processFromUserToServerPacket(JID connectionId, Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t  NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t\t  Map<String, Object> settings) throws PacketErrorTypeException {\n\t\tif (packet.getType() != null) {\n\t\t\ttry {\n\t\t\t\tPacket result = null;\n\n\t\t\t\tswitch (packet.getType()) {\n\t\t\t\t\tcase get:\n\t\t\t\t\t\tString strvCard = session.getPublicData(ID, VCARD_KEY, null);\n\n\t\t\t\t\t\tif (strvCard != null) {\n\t\t\t\t\t\t\tresult = parseXMLData(strvCard, packet);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresult = packet.okResult((String) null, 1);\n\t\t\t\t\t\t}    // end of if (vcard != null) else\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase set:\n\t\t\t\t\t\tElement elvCard = packet.getElement().getChild(VCARD_EL, XMLNS);\n\n\t\t\t\t\t\tsetVCard(session, elvCard);\n\t\t\t\t\t\tresult = packet.okResult((String) null, 0);\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\n\t\t\t\t\t\t// Ignore all others...\n\t\t\t\t}\n\t\t\t\tif (result != null) {\n\t\t\t\t\tresult.setPacketTo(session.getConnectionId());\n\t\t\t\t\tresults.offer(result);\n\t\t\t\t}\n\t\t\t} catch (NoConnectionIdException ex) {\n\n\t\t\t\t// This should not happen unless somebody sends a result vcard packet\n\t\t\t\t// to the server itself\n\t\t\t\tlog.warning(\"This should not happen, unless this is a vcard result packet \" +\n\t\t\t\t\t\t\t\t\t\"sent to the server, which should not happen: \" + packet);\n\t\t\t} catch (NotAuthorizedException ex) {\n\t\t\t\tlog.log(Level.CONFIG, \"Received vCard request but user session is not authorized yet: \" + packet);\n\t\t\t\tresults.offer(\n\t\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(packet, \"You must authorize session first.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttrue));\n\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\tlog.log(Level.WARNING, \"Database problem, please contact admin: \" + ex, ex);\n\t\t\t\tresults.offer(Authorization.INTERNAL_SERVER_ERROR.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Database access problem, please contact administrator.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true));\n\t\t\t}\n\t\t} else {\n\n\t\t\t// TODO: if this really happen that this is clearly protocol error, as\n\t\t\t// that would be\n\t\t\t// vCard packet with no type set, do we really need to handle such an\n\t\t\t// erro? Let's\n\t\t\t// ignore it for now.\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processNullSessionPacket(Packet packet, NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t Map<String, Object> settings) throws PacketErrorTypeException {\n\t\tif (packet.getType() == StanzaType.get) {\n\t\t\ttry {\n\t\t\t\tString strvCard = repo.getPublicData(packet.getStanzaTo().getBareJID(), ID, VCARD_KEY, null);\n\n\t\t\t\tif (strvCard != null) {\n\t\t\t\t\tresults.offer(parseXMLData(strvCard, packet));\n\t\t\t\t} else {\n\t\t\t\t\tresults.offer(packet.okResult((String) null, 1));\n\t\t\t\t}    // end of if (vcard != null)\n\t\t\t} catch (UserNotFoundException e) {\n\t\t\t\tresults.offer(Authorization.ITEM_NOT_FOUND.getResponseMessage(packet, \"User not found\", true));\n\t\t\t}    // end of try-catch\n\t\t} else {\n\n\t\t\t// This is most likely a response to the user from the remote\n\t\t\t// entity with vCard request results.\n\t\t\t// Processed in processToUserPacket() method.\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processServerSessionPacket(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t\t\t\t   Queue<Packet> results, Map<String, Object> settings) {\n\n\t\t// TODO: Hm, the server vCard should be sent here, not yet implemented....\n\t}\n\n\t@Override\n\tpublic void processToUserPacket(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings)\n\t\t\tthrows PacketErrorTypeException {\n\t\tprocessNullSessionPacket(packet, repo, results, settings);\n\t\tif ((session != null) && session.isAuthorized() && (packet.getType() != StanzaType.get)) {\n\t\t\ttry {\n\t\t\t\tJID conId = session.getConnectionId(packet.getStanzaTo());\n\n\t\t\t\tif (conId == null) {\n\n\t\t\t\t\t// Drop it, user is no longer online.\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tPacket result = packet.copyElementOnly();\n\n\t\t\t\tresult.setPacketTo(session.getConnectionId(packet.getStanzaTo()));\n\t\t\t\tresults.offer(result);\n\t\t\t} catch (NoConnectionIdException ex) {\n\n\t\t\t\t// This should not happen unless somebody sends a result vcard packet\n\t\t\t\t// to the server itself\n\t\t\t\tlog.warning(\"This should not happen, unless this is a vcard result packet \" +\n\t\t\t\t\t\t\t\t\t\"sent to the server, which should not happen: \" + packet);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tprotected String getVCardXMLNS() {\n\t\treturn XMLNS;\n\t}\n\n\t@Override\n\tprotected void storeVCard(XMPPResourceConnection session, Element elvCard)\n\t\t\tthrows TigaseDBException, NotAuthorizedException {\n\t\tif (elvCard != null && elvCard.getChildren() != null) {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.finer(\"Adding vCard: \" + elvCard);\n\t\t\t}\n\t\t\tsession.setPublicData(ID, VCARD_KEY, elvCard.toString());\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.finer(\"Removing vCard\");\n\t\t\t}\n\t\t\tsession.removePublicData(ID, VCARD_KEY);\n\t\t}    // end of else\t\t\n\t}\n\n\tprivate Packet parseXMLData(String data, Packet packet) {\n\t\tDomBuilderHandler domHandler = new DomBuilderHandler();\n\n\t\tparser.parse(domHandler, data.toCharArray(), 0, data.length());\n\n\t\tQueue<Element> elems = domHandler.getParsedElements();\n\t\tPacket result = packet.okResult((Element) null, 0);\n\n\t\tresult.setPacketFrom(null);\n\t\tresult.setPacketTo(null);\n\t\tfor (Element el : elems) {\n\t\t\tresult.getElement().addChild(el);\n\t\t}    // end of for (Element el: elems)\n\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/VCardTemp.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserNotFoundException;\nimport tigase.db.UserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.Priority;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.DomBuilderHandler;\nimport tigase.xml.Element;\nimport tigase.xml.SimpleParser;\nimport tigase.xml.SingletonFactory;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.annotation.DiscoFeatures;\nimport tigase.xmpp.impl.annotation.Handle;\nimport tigase.xmpp.impl.annotation.Handles;\nimport tigase.xmpp.impl.annotation.Id;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Queue;\nimport java.util.function.Consumer;\nimport java.util.function.Supplier;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.db.NonAuthUserRepository.PUBLIC_DATA_NODE;\nimport static tigase.xmpp.impl.VCardTemp.*;\n\n/**\n * Describe class VCardTemp here.\n * <br>\n * Created: Thu Oct 19 23:37:23 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Id(XMLNS)\n@Handles({@Handle(path = {Iq.ELEM_NAME, vCard}, xmlns = XMLNS), @Handle(path = {Iq.ELEM_NAME, VCARD}, xmlns = XMLNS)})\n@DiscoFeatures({XMLNS})\n@Bean(name = VCardTemp.ID, parent = SessionManager.class, active = true)\npublic class VCardTemp\n\t\textends VCardXMPPProcessorAbstract implements PresenceState.ExtendedPresenceProcessorIfc {\n\n\tpublic static final String VCARD_KEY = \"vCard\";\n\t// VCARD element is added to support old vCard protocol where element\n\t// name was all upper cases. Now the plugin should catch both cases.\n\tprotected static final String vCard = \"vCard\";\n\tprotected static final String VCARD = \"VCARD\";\n\tprotected static final String XMLNS = \"vcard-temp\";\n\tprotected static final String ID = XMLNS;\n\tprivate static final SimpleParser parser = SingletonFactory.getParserInstance();\n\t/**\n\t * Private logger for class instances.\n\t */\n\tprivate static Logger log = Logger.getLogger(VCardTemp.class.getName());\n\tprivate final String PEP_VCARD_TEMP_HASH_KEY = \"pep-vcard-temp-conv-hash\";\n\n\t@Inject\n\tprivate UserRepository userRepository;\n\n\tpublic void processFromUserOutPacket(JID connectionId, Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t Map<String, Object> settings) throws PacketErrorTypeException {\n\t\tif (session.isLocalDomain(packet.getStanzaTo().getDomain(), false)) {\n\n\t\t\t// This is a local user so we can quickly get his vCard from the database\n\t\t\ttry {\n\t\t\t\tString strvCard = repo.getPublicData(packet.getStanzaTo().getBareJID(), ID, VCARD_KEY, null);\n\t\t\t\tPacket result = null;\n\n\t\t\t\tif (strvCard != null) {\n\t\t\t\t\tresult = parseXMLData(strvCard, packet);\n\t\t\t\t} else {\n\t\t\t\t\tresult = packet.okResult((String) null, 1);\n\t\t\t\t}    // end of if (vcard != null)\n\t\t\t\tresult.setPacketTo(connectionId);\n\t\t\t\tresults.offer(result);\n\t\t\t} catch (UserNotFoundException e) {\n\t\t\t\tresults.offer(Authorization.ITEM_NOT_FOUND.getResponseMessage(packet, \"User not found\", true));\n\t\t\t}    // end of try-catch\n\t\t} else {\n\n\t\t\t// Else forward the packet to a remote server\n\t\t\tresults.offer(packet.copyElementOnly());\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processFromUserToServerPacket(JID connectionId, Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t  NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t\t  Map<String, Object> settings) throws PacketErrorTypeException {\n\t\tif (packet.getType() != null) {\n\t\t\ttry {\n\t\t\t\tPacket result = null;\n\n\t\t\t\tswitch (packet.getType()) {\n\t\t\t\t\tcase get:\n\t\t\t\t\t\tString strvCard = session.getPublicData(ID, VCARD_KEY, null);\n\n\t\t\t\t\t\tif (strvCard != null) {\n\t\t\t\t\t\t\tresult = parseXMLData(strvCard, packet);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresult = packet.okResult((String) null, 1);\n\t\t\t\t\t\t}    // end of if (vcard != null) else\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase set:\n\t\t\t\t\t\tElement elvCard = packet.getElement().getChild(vCard);\n\n\t\t\t\t\t\t// This is added to support old vCard protocol where element\n\t\t\t\t\t\t// name was all upper cases. So here I am checking both\n\t\t\t\t\t\t// possibilities\n\t\t\t\t\t\tif (elvCard == null) {\n\t\t\t\t\t\t\telvCard = packet.getElement().getChild(VCARD);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsetVCard(session, elvCard);\n\t\t\t\t\t\tresult = packet.okResult((String) null, 0);\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\n\t\t\t\t\t\t// Ignore all others...\n\t\t\t\t}\n\t\t\t\tif (result != null) {\n\t\t\t\t\tresult.setPacketTo(session.getConnectionId());\n\t\t\t\t\tresults.offer(result);\n\t\t\t\t}\n\t\t\t} catch (NoConnectionIdException ex) {\n\n\t\t\t\t// This should not happen unless somebody sends a result vcard packet\n\t\t\t\t// to the server itself\n\t\t\t\tlog.log(Level.FINE, \"This should not happen, unless this is a vcard result packet \" +\n\t\t\t\t\t\t\t\t\t\"sent to the server, which should not happen: \" + packet);\n\t\t\t} catch (NotAuthorizedException ex) {\n\t\t\t\tlog.log(Level.FINE, \"Received vCard request but user session is not authorized yet: \" + packet);\n\t\t\t\tresults.offer(\n\t\t\t\t\t\tAuthorization.NOT_AUTHORIZED.getResponseMessage(packet, \"You must authorize session first.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttrue));\n\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\tlog.log(Level.WARNING, \"Database problem, please contact admin: \" + ex);\n\t\t\t\tresults.offer(Authorization.INTERNAL_SERVER_ERROR.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"Database access problem, please contact administrator.\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t true));\n\t\t\t}\n\t\t} else {\n\n\t\t\t// TODO: if this really happen that this is clearly protocol error, as\n\t\t\t// that would be\n\t\t\t// vCard packet with no type set, do we really need to handle such an\n\t\t\t// erro? Let's\n\t\t\t// ignore it for now.\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processNullSessionPacket(Packet packet, NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t Map<String, Object> settings) throws PacketErrorTypeException {\n\t\tif (packet.getType() == StanzaType.get) {\n\t\t\ttry {\n\t\t\t\tString strvCard = repo.getPublicData(packet.getStanzaTo().getBareJID(), ID, VCARD_KEY, null);\n\n\t\t\t\tif (strvCard != null) {\n\t\t\t\t\tresults.offer(parseXMLData(strvCard, packet));\n\t\t\t\t} else {\n\t\t\t\t\tresults.offer(packet.okResult((String) null, 1));\n\t\t\t\t}    // end of if (vcard != null)\n\t\t\t} catch (UserNotFoundException e) {\n\t\t\t\tresults.offer(Authorization.ITEM_NOT_FOUND.getResponseMessage(packet, \"User not found\", true));\n\t\t\t}    // end of try-catch\n\t\t} else {\n\n\t\t\t// This is most likely a response to the user from the remote\n\t\t\t// entity with vCard request results.\n\t\t\t// Processed in processToUserPacket() method.\n\t\t}\n\t}\n\n\t@Override\n\tpublic void processServerSessionPacket(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t\t\t\t   Queue<Packet> results, Map<String, Object> settings) {\n\n\t\t// TODO: Hm, the server vCard should be sent here, not yet implemented....\n\t}\n\n\t@Override\n\tpublic void processToUserPacket(Packet packet, XMPPResourceConnection session, NonAuthUserRepository repo,\n\t\t\t\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> settings)\n\t\t\tthrows PacketErrorTypeException {\n\t\tprocessNullSessionPacket(packet, repo, results, settings);\n\t\tif ((session != null) && session.isAuthorized() && (packet.getType() != StanzaType.get)) {\n\t\t\ttry {\n\t\t\t\tJID conId = session.getConnectionId(packet.getStanzaTo());\n\n\t\t\t\tif (conId == null) {\n\n\t\t\t\t\t// Drop it, user is no longer online.\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tPacket result = packet.copyElementOnly();\n\n\t\t\t\tresult.setPacketTo(session.getConnectionId(packet.getStanzaTo()));\n\t\t\t\tresults.offer(result);\n\t\t\t} catch (NoConnectionIdException ex) {\n\n\t\t\t\t// This should not happen unless somebody sends a result vcard packet\n\t\t\t\t// to the server itself\n\t\t\t\tlog.log(Level.WARNING,\"This should not happen, unless this is a vcard result packet \" +\n\t\t\t\t\t\t\t\t\t\"sent to the server, which should not happen: \" + packet);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void pepToVCardTemp_onPublication(BareJID userJid, XMPPResourceConnection session, String itemId, String mimeType, Supplier<JID> pubsubComponentJidSupplier, Consumer<Packet> writer) {\n\t\tElement iqEl = new Element(\"iq\", new String[]{\"type\", \"id\"}, new String[]{\"get\", \"sm-query-vcard-pep-\" + mimeType});\n\t\tiqEl.withElement(\"pubsub\", \"http://jabber.org/protocol/pubsub\", pubsubEl -> {\n\t\t\tpubsubEl.withElement(\"items\", itemsEl -> {\n\t\t\t\titemsEl.setAttribute(\"node\", \"urn:xmpp:avatar:data\");\n\t\t\t\titemsEl.withElement(\"item\", itemEl -> {\n\t\t\t\t\titemEl.setAttribute(\"id\", itemId);\n\t\t\t\t});\n\t\t\t});\n\t\t});\n\n\t\tIq iq = (Iq) Packet.packetInstance(iqEl, JID.jidInstanceNS(userJid), JID.jidInstanceNS(userJid));\n\t\tiq.setPacketTo(pubsubComponentJidSupplier.get());\n\t\t// lower priority to make sure that publication will take place before we would query for an item\n\t\tiq.setPriority(Priority.LOW);\n\n\t\twriter.accept(iq);\n\t}\n\n\tpublic void pepToVCardTemp_onDataRetrieved(Packet packet, XMPPResourceConnection session) {\n\t\tElement itemEl = packet.getElement().findChild(new String[] { \"iq\", \"pubsub\", \"items\", \"item\" });\n\t\tString mimeType = packet.getAttributeStaticStr(\"id\").replace(\"sm-query-vcard-pep-\", \"\");\n\t\tif (itemEl != null && mimeType != null) {\n\t\t\tString id = itemEl.getAttributeStaticStr(\"id\");\n\t\t\tElement data = itemEl.getChild(\"data\", \"urn:xmpp:avatar:data\");\n\t\t\tif (id != null && data != null) {\n\t\t\t\ttry {\n\t\t\t\t\tElement vCard = this.parseXMLDataToElement(userRepository.getData(packet.getStanzaFrom().getBareJID(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t PUBLIC_DATA_NODE + \"/vcard-temp\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t VCardTemp.VCARD_KEY))\n\t\t\t\t\t\t\t.orElseGet(() -> new Element(\"vCard\", new String[]{\"xmlns\"}, new String[]{\"vcard-temp\"}));\n\n\t\t\t\t\tElement photoEl = vCard.findChild(new String[]{\"vCard\", \"PHOTO\"});\n\t\t\t\t\tif (photoEl != null) {\n\t\t\t\t\t\tvCard.removeChild(photoEl);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (data.getCData() != null) {\n\t\t\t\t\t\tphotoEl = new Element(\"PHOTO\");\n\t\t\t\t\t\tphotoEl.withElement(\"TYPE\", null, mimeType);\n\t\t\t\t\t\tphotoEl.withElement(\"BINVAL\", null, data.getCData());\n\t\t\t\t\t\tvCard.addChild(photoEl);\n\t\t\t\t\t}\n\t\t\t\t\tuserRepository.setData(packet.getStanzaFrom().getBareJID(), ID, PEP_VCARD_TEMP_HASH_KEY, id);\n\t\t\t\t\tif (session != null) {\n\t\t\t\t\t\tsession.putCommonSessionData(PEP_VCARD_TEMP_HASH_KEY, id);\n\t\t\t\t\t}\n\t\t\t\t\tsetVCard(session, vCard);\n\t\t\t\t\tlog.log(Level.FINEST, \"Updated vCard, avatar hash: {0}, payload: {1}!\", new Object[]{id, vCard});\n\t\t\t\t} catch (RepositoryException | NotAuthorizedException ex) {\n\t\t\t\t\tlog.log(Level.FINEST, \"failed to update VCardTemp avatar on PEP User Avatar change!\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic Element extend(Element presence, XMPPResourceConnection session, Queue<Packet> results) {\n\t\tElement x = presence.getChild(\"x\", \"vcard-temp:x:update\");\n\t\tElement photoEl = x == null ? null : x.getChild(\"photo\");\n\t\tif (photoEl != null) {\n\t\t\treturn null;\n\t\t}\n\t\tString hash = (String) session.computeCommonSessionDataIfAbsent(PEP_VCARD_TEMP_HASH_KEY, (key) -> {\n\t\t\ttry {\n\t\t\t\treturn Optional.ofNullable(userRepository.getData(session.getBareJID(), ID, PEP_VCARD_TEMP_HASH_KEY))\n\t\t\t\t\t\t.orElse(\"\");\n\t\t\t} catch (NotAuthorizedException ex) {\n\t\t\t\tlog.log(Level.FINEST, \"failed to retrieve VCardTemp avatar hash - session not authorized yet!\", ex);\n\t\t\t} catch (RepositoryException ex) {\n\t\t\t\tlog.log(Level.FINEST, \"failed to retrieve VCardTemp avatar hash!\", ex);\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\t\tif (hash == null || hash.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tif (x == null) {\n\t\t\tx = new Element(\"x\");\n\t\t\tx.setXMLNS(\"vcard-temp:x:update\");\n\t\t} else if (photoEl != null) {\n\t\t\tpresence.removeChild(x);\n\t\t\tx.removeChild(photoEl);\n\t\t}\n\t\tphotoEl = new Element(\"photo\", hash);\n\t\tx.addChild(photoEl);\n\t\treturn x;\n\t}\n\n\t@Override\n\tpublic Element extend(XMPPResourceConnection session, Queue<Packet> results) {\n\t\t// this will never be called!\n\t\tthrow new UnsupportedOperationException(\"It should never be called!\");\n\t}\n\n\t@Override\n\tprotected String getVCardXMLNS() {\n\t\treturn XMLNS;\n\t}\n\n\t@Override\n\tprotected void storeVCard(XMPPResourceConnection session, Element elvCard)\n\t\t\tthrows TigaseDBException, NotAuthorizedException {\n\t\tif (elvCard != null) {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.finer(\"Adding vCard: \" + elvCard);\n\t\t\t}\n\t\t\tsession.setPublicData(ID, VCARD_KEY, elvCard.toString());\n\t\t} else {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tlog.finer(\"Removing vCard\");\n\t\t\t}\n\t\t\tsession.removePublicData(ID, VCARD_KEY);\n\t\t}    // end of else\n\t}\n\n\tprivate Packet parseXMLData(String data, Packet packet) {\n\t\tDomBuilderHandler domHandler = new DomBuilderHandler();\n\n\t\tparser.parse(domHandler, data.toCharArray(), 0, data.length());\n\n\t\tQueue<Element> elems = domHandler.getParsedElements();\n\t\tPacket result = packet.okResult((Element) null, 0);\n\n\t\tresult.setPacketFrom(null);\n\t\tresult.setPacketTo(null);\n\t\tfor (Element el : elems) {\n\t\t\tresult.getElement().addChild(el);\n\t\t}    // end of for (Element el: elems)\n\n\t\treturn result;\n\t}\n\n\tprivate Optional<Element> parseXMLDataToElement(String data) {\n\t\tif (data == null) {\n\t\t\treturn Optional.empty();\n\t\t}\n\t\tDomBuilderHandler domHandler = new DomBuilderHandler();\n\n\t\tparser.parse(domHandler, data.toCharArray(), 0, data.length());\n\n\t\tQueue<Element> elems = domHandler.getParsedElements();\n\n\t\treturn Optional.ofNullable(elems.poll());\n\t}\n\n}    // VCardTemp\n\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/VCardXMPPProcessorAbstract.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n * To change this license header, choose License Headers in Project Properties.\n * To change this template file, choose Tools | Templates\n * and open the template in the editor.\n */\npackage tigase.xmpp.impl;\n\nimport tigase.db.TigaseDBException;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.xml.Element;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.XMPPProcessorAbstract;\nimport tigase.xmpp.XMPPResourceConnection;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * @author andrzej\n */\npublic abstract class VCardXMPPProcessorAbstract\n\t\textends XMPPProcessorAbstract\n\t\timplements Initializable, UnregisterAware {\n\n\tprivate static final Pattern DATA_URI_PATTERN = Pattern.compile(\"data:(.+);base64,(.+)\");\n\tprivate static final Pattern TEL_URI_PATTERN = Pattern.compile(\"tel:(.+)\");\n\tprivate static final Pattern XMPP_URI_PATTERN = Pattern.compile(\"xmpp:(.+)\");\n\tprivate static final Pattern GEO_URI_PATTERN = Pattern.compile(\"geo:([\\\\-0-9\\\\.]+),([\\\\-0-9\\\\.]+)\");\n\tprivate static Map<String, Converter> CONVERTERS = new ConcurrentHashMap<>();\n\tprivate static Map<String, VCardXMPPProcessorAbstract> PROCESSORS = new ConcurrentHashMap<>();\n\n\tstatic {\n\t\tCONVERTERS.put(VCardTemp.XMLNS + \"|\" + VCard4.XMLNS, (Element vcardTemp) -> {\n\t\t\tif (vcardTemp == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tElement vcard4 = new Element(VCard4.VCARD_EL, new String[]{\"xmlns\"}, new String[]{VCard4.XMLNS});\n\n\t\t\tvcardTemp.forEachChild((Element c) -> {\n\t\t\t\tElement r = null;\n\t\t\t\tList<Element> list = null;\n\t\t\t\tElement parameters = null;\n\t\t\t\tElement type = null;\n\n\t\t\t\tswitch (c.getName()) {\n\t\t\t\t\tcase \"FN\":\n\t\t\t\t\tcase \"NICKNAME\":\n\t\t\t\t\tcase \"TZ\":\n\t\t\t\t\tcase \"TITLE\":\n\t\t\t\t\tcase \"ROLE\":\n\t\t\t\t\tcase \"CATEGORIES\":\n\t\t\t\t\tcase \"NOTE\":\n\t\t\t\t\tcase \"PRODID\":\n\t\t\t\t\t\tr = new Element(c.getName().toLowerCase());\n\t\t\t\t\t\tr.addChild(new Element(\"text\", c.getCData()));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"N\":\n\t\t\t\t\t\tlist = c.mapChildren((Element c1) -> {\n\t\t\t\t\t\t\tswitch (c1.getName()) {\n\t\t\t\t\t\t\t\tcase \"GIVEN\":\n\t\t\t\t\t\t\t\t\treturn new Element(\"given\", c1.getCData());\n\t\t\t\t\t\t\t\tcase \"FAMILY\":\n\t\t\t\t\t\t\t\t\treturn new Element(\"surname\", c1.getCData());\n\t\t\t\t\t\t\t\tcase \"MIDDLE\":\n\t\t\t\t\t\t\t\t\treturn new Element(\"additional\", c1.getCData());\n\t\t\t\t\t\t\t\tcase \"PREFIX\":\n\t\t\t\t\t\t\t\t\treturn new Element(\"prefix\", c1.getCData());\n\t\t\t\t\t\t\t\tcase \"SUFFIX\":\n\t\t\t\t\t\t\t\t\treturn new Element(\"suffix\", c1.getCData());\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t\tif (list != null) {\n\t\t\t\t\t\t\tlist.removeIf((Element c1) -> c1 == null);\n\t\t\t\t\t\t\tif (!list.isEmpty()) {\n\t\t\t\t\t\t\t\tr = new Element(\"n\");\n\t\t\t\t\t\t\t\tr.addChildren(list);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"PHOTO\":\n\t\t\t\t\tcase \"LOGO\":\n\t\t\t\t\t\tr = new Element(c.getName().toLowerCase());\n\t\t\t\t\t\tElement extVal = c.findChild((Element c1) -> c1.getName() == \"EXTVAL\");\n\t\t\t\t\t\tif (extVal != null) {\n\t\t\t\t\t\t\tr.addChild(new Element(\"uri\", extVal.getCData()));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tString binval = c.getChildCData((Element c1) -> c1.getName() == \"BINVAL\");\n\t\t\t\t\t\t\tString tp = c.getChildCData((Element c1) -> c1.getName() == \"TYPE\");\n\t\t\t\t\t\t\tif (binval != null && tp != null) {\n\t\t\t\t\t\t\t\tr.addChild(new Element(\"uri\", \"data:\" + tp + \";base64,\" +\n\t\t\t\t\t\t\t\t\t\tbinval.replace(\"\\n\", \"\").replace(\"\\r\", \"\")));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"BDAY\":\n\t\t\t\t\t\tr = new Element(c.getName().toLowerCase());\n\t\t\t\t\t\tr.addChild(new Element(\"date\", c.getCData()));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"ADR\":\n\t\t\t\t\t\tif (c.getChildren() != null) {\n\t\t\t\t\t\t\tr = new Element(\"adr\");\n\t\t\t\t\t\t\tfor (Element c1 : c.getChildren()) {\n\t\t\t\t\t\t\t\tswitch (c1.getName()) {\n\t\t\t\t\t\t\t\t\tcase \"POBOX\":\n\t\t\t\t\t\t\t\t\tcase \"STREET\":\n\t\t\t\t\t\t\t\t\tcase \"LOCALITY\":\n\t\t\t\t\t\t\t\t\tcase \"REGION\":\n\t\t\t\t\t\t\t\t\t\tr.addChild(new Element(c1.getName().toLowerCase(), c1.getCData()));\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase \"EXTADD\":\n\t\t\t\t\t\t\t\t\t\tr.addChild(new Element(\"ext\", c1.getCData()));\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase \"PCODE\":\n\t\t\t\t\t\t\t\t\t\tr.addChild(new Element(\"code\", c1.getCData()));\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase \"CTRY\":\n\t\t\t\t\t\t\t\t\t\tr.addChild(new Element(\"country\", c1.getCData()));\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase \"HOME\":\n\t\t\t\t\t\t\t\t\tcase \"WORK\":\n\t\t\t\t\t\t\t\t\t\tif (parameters == null) {\n\t\t\t\t\t\t\t\t\t\t\tparameters = new Element(\"parameters\");\n\t\t\t\t\t\t\t\t\t\t\tr.addChild(parameters);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tif (type == null) {\n\t\t\t\t\t\t\t\t\t\t\ttype = new Element(\"type\");\n\t\t\t\t\t\t\t\t\t\t\tparameters.addChild(type);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\ttype.addChild(new Element(\"text\", c1.getName().toLowerCase()));\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase \"PREF\":\n\t\t\t\t\t\t\t\t\t\tif (parameters == null) {\n\t\t\t\t\t\t\t\t\t\t\tparameters = new Element(\"parameters\");\n\t\t\t\t\t\t\t\t\t\t\tr.addChild(parameters);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tElement pref = new Element(\"pref\");\n\t\t\t\t\t\t\t\t\t\tpref.addChild(new Element(\"integer\", \"1\"));\n\t\t\t\t\t\t\t\t\t\tparameters.addChild(pref);\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"TEL\":\n\t\t\t\t\t\tr = new Element(\"tel\");\n\t\t\t\t\t\tif (c.getChildren() != null) {\n\t\t\t\t\t\t\tfor (Element c1 : c.getChildren()) {\n\t\t\t\t\t\t\t\tswitch (c1.getName()) {\n\t\t\t\t\t\t\t\t\tcase \"HOME\":\n\t\t\t\t\t\t\t\t\tcase \"WORK\":\n\t\t\t\t\t\t\t\t\tcase \"TEXT\":\n\t\t\t\t\t\t\t\t\tcase \"FAX\":\n\t\t\t\t\t\t\t\t\tcase \"CELL\":\n\t\t\t\t\t\t\t\t\tcase \"VOICE\":\n\t\t\t\t\t\t\t\t\tcase \"VIDEO\":\n\t\t\t\t\t\t\t\t\tcase \"PAGER\":\n\t\t\t\t\t\t\t\t\tcase \"TEXTPHONE\":\n\t\t\t\t\t\t\t\t\t\tif (parameters == null) {\n\t\t\t\t\t\t\t\t\t\t\tparameters = new Element(\"parameters\");\n\t\t\t\t\t\t\t\t\t\t\tr.addChild(parameters);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tif (type == null) {\n\t\t\t\t\t\t\t\t\t\t\ttype = new Element(\"type\");\n\t\t\t\t\t\t\t\t\t\t\tparameters.addChild(type);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\ttype.addChild(new Element(\"text\", c1.getName().toLowerCase()));\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase \"PREF\":\n\t\t\t\t\t\t\t\t\t\tif (parameters == null) {\n\t\t\t\t\t\t\t\t\t\t\tparameters = new Element(\"parameters\");\n\t\t\t\t\t\t\t\t\t\t\tr.addChild(parameters);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tElement pref = new Element(\"pref\");\n\t\t\t\t\t\t\t\t\t\tpref.addChild(new Element(\"integer\", \"1\"));\n\t\t\t\t\t\t\t\t\t\tparameters.addChild(pref);\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase \"NUMBER\":\n\t\t\t\t\t\t\t\t\t\tElement uri = new Element(\"uri\", \"tel:\" + c1.getCData());\n\t\t\t\t\t\t\t\t\t\tr.addChild(uri);\n\t\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"EMAIL\":\n\t\t\t\t\t\tr = new Element(\"email\");\n\t\t\t\t\t\tif (c.getChildren() != null) {\n\t\t\t\t\t\t\tfor (Element c1 : c.getChildren()) {\n\t\t\t\t\t\t\t\tswitch (c1.getName()) {\n\t\t\t\t\t\t\t\t\tcase \"HOME\":\n\t\t\t\t\t\t\t\t\tcase \"WORK\":\n\t\t\t\t\t\t\t\t\t\tif (parameters == null) {\n\t\t\t\t\t\t\t\t\t\t\tparameters = new Element(\"parameters\");\n\t\t\t\t\t\t\t\t\t\t\tr.addChild(parameters);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tif (type == null) {\n\t\t\t\t\t\t\t\t\t\t\ttype = new Element(\"type\");\n\t\t\t\t\t\t\t\t\t\t\tparameters.addChild(type);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\ttype.addChild(new Element(\"text\", c1.getName().toLowerCase()));\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase \"PREF\":\n\t\t\t\t\t\t\t\t\t\tif (parameters == null) {\n\t\t\t\t\t\t\t\t\t\t\tparameters = new Element(\"parameters\");\n\t\t\t\t\t\t\t\t\t\t\tr.addChild(parameters);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tElement pref = new Element(\"pref\");\n\t\t\t\t\t\t\t\t\t\tpref.addChild(new Element(\"integer\", \"1\"));\n\t\t\t\t\t\t\t\t\t\tparameters.addChild(pref);\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase \"USERID\":\n\t\t\t\t\t\t\t\t\t\tr.addChild(new Element(\"text\", c1.getCData()));\n\t\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"JABBERID\":\n\t\t\t\t\t\tr = new Element(\"impp\");\n\t\t\t\t\t\tr.addChild(new Element(\"uri\", \"xmpp:\" + c.getCData()));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"GEO\":\n\t\t\t\t\t\tr = new Element(c.getName().toLowerCase());\n\t\t\t\t\t\tr.addChild(new Element(\"uri\",\n\t\t\t\t\t\t\t\t\t\t\t   \"geo:\" + c.getChildCData((Element c1) -> c1.getName() == \"LAT\") + \",\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t   c.getChildCData((Element c1) -> c1.getName() == \"LON\")));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"AGENT\":\n\t\t\t\t\t\tr = new Element(c.getName().toLowerCase());\n\t\t\t\t\t\tr.addChild(new Element(\"uri\", c.getChildCData((Element c1) -> c1.getName() == \"EXTVAL\")));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"ORG\":\n\t\t\t\t\t\tc = c.findChild((Element c1) -> c1.getName() == \"ORGNAME\");\n\t\t\t\t\t\tif (c != null) {\n\t\t\t\t\t\t\tr = new Element(\"org\");\n\t\t\t\t\t\t\tr.addChild(new Element(\"text\", c.getCData()));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"REV\":\n\t\t\t\t\t\tr = new Element(\"rev\");\n\t\t\t\t\t\tr.addChild(new Element(\"timestamp\", c.getCData()));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"SORT-STRING\":\n\t\t\t\t\t\tr = new Element(\"sort-as\", c.getCData());\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"SOUND\":\n\t\t\t\t\t\tr = new Element(\"sound\");\n\t\t\t\t\t\textVal = c.findChildStaticStr(new String[]{\"EXTVAL\"});\n\t\t\t\t\t\tif (extVal != null) {\n\t\t\t\t\t\t\tr.addChild(new Element(\"uri\", extVal.getCData()));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tr.addChild(new Element(\"uri\", \"data:audio/basic;base64,\" +\n\t\t\t\t\t\t\t\t\tc.getChildCData((Element c1) -> c1.getName() == \"BINVAL\")));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"UID\":\n\t\t\t\t\tcase \"URL\":\n\t\t\t\t\t\tr = new Element(c.getName().toLowerCase());\n\t\t\t\t\t\tr.addChild(new Element(\"uri\", c.getCData()));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"KEY\":\n\t\t\t\t\t\tr = new Element(c.getName().toLowerCase());\n\t\t\t\t\t\tr.addChild(new Element(\"text\", c.getChildCData((Element c1) -> c1.getName() == \"CRED\")));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"DESC\":\n\t\t\t\t\t\tr = new Element(\"note\");\n\t\t\t\t\t\tr.addChild(new Element(\"text\", c.getCData()));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (r != null) {\n\t\t\t\t\tvcard4.addChild(r);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn vcard4;\n\t\t});\n\n\t\tCONVERTERS.put(VCard4.XMLNS + \"|\" + VCardTemp.XMLNS, (Element vcard4) -> {\n\t\t\tElement vcardTemp = new Element(VCardTemp.vCard, new String[]{\"xmlns\"}, new String[]{VCardTemp.XMLNS});\n\n\t\t\tvcard4.forEachChild((Element c) -> {\n\t\t\t\tElement r = null;\n\t\t\t\tElement text = null;\n\t\t\t\tElement uri = null;\n\t\t\t\tList<Element> list = null;\n\n\t\t\t\tElement parameters = null;\n\t\t\t\tElement type = null;\n\n\t\t\t\tswitch (c.getName()) {\n\t\t\t\t\tcase \"fn\":\n\t\t\t\t\tcase \"nickname\":\n\t\t\t\t\tcase \"tz\":\n\t\t\t\t\tcase \"title\":\n\t\t\t\t\tcase \"role\":\n\t\t\t\t\tcase \"categories\":\n\t\t\t\t\tcase \"note\":\n\t\t\t\t\tcase \"prodid\":\n\t\t\t\t\t\ttext = c.findChild((Element c1) -> c1.getName() == \"text\");\n\t\t\t\t\t\tif (text != null) {\n\t\t\t\t\t\t\tr = new Element(c.getName().toUpperCase(), text.getCData());\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"n\":\n\t\t\t\t\t\tlist = c.mapChildren((Element c1) -> {\n\t\t\t\t\t\t\tswitch (c1.getName()) {\n\t\t\t\t\t\t\t\tcase \"given\":\n\t\t\t\t\t\t\t\t\treturn new Element(\"GIVEN\", c1.getCData());\n\t\t\t\t\t\t\t\tcase \"surname\":\n\t\t\t\t\t\t\t\t\treturn new Element(\"FAMILY\", c1.getCData());\n\t\t\t\t\t\t\t\tcase \"additional\":\n\t\t\t\t\t\t\t\t\treturn new Element(\"MIDDLE\", c1.getCData());\n\t\t\t\t\t\t\t\tcase \"prefix\":\n\t\t\t\t\t\t\t\t\treturn new Element(\"PREFIX\", c1.getCData());\n\t\t\t\t\t\t\t\tcase \"suffix\":\n\t\t\t\t\t\t\t\t\treturn new Element(\"SUFFIX\", c1.getCData());\n\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t\tif (list != null) {\n\t\t\t\t\t\t\tlist.removeIf((Element c1) -> c1 == null);\n\t\t\t\t\t\t\tif (!list.isEmpty()) {\n\t\t\t\t\t\t\t\tr = new Element(\"N\");\n\t\t\t\t\t\t\t\tr.addChildren(list);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"photo\":\n\t\t\t\t\tcase \"logo\":\n\t\t\t\t\t\turi = c.findChild((Element c1) -> c1.getName() == \"uri\");\n\t\t\t\t\t\tif (uri != null) {\n\t\t\t\t\t\t\tr = new Element(c.getName().toUpperCase());\n\t\t\t\t\t\t\tString uriStr = uri.getCData();\n\t\t\t\t\t\t\tif (uriStr != null) {\n\t\t\t\t\t\t\t\tMatcher matcher = DATA_URI_PATTERN.matcher(uriStr);\n\t\t\t\t\t\t\t\tif (matcher.matches()) {\n\t\t\t\t\t\t\t\t\tr.addChild(new Element(\"TYPE\", matcher.group(1)));\n\t\t\t\t\t\t\t\t\tr.addChild(new Element(\"BINVAL\", matcher.group(2)));\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tr.addChild(new Element(\"EXTVAL\", uriStr));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"bday\":\n\t\t\t\t\t\tElement date = c.findChild((Element c1) -> c1.getName() == \"date\");\n\t\t\t\t\t\tif (date != null) {\n\t\t\t\t\t\t\tr = new Element(c.getName().toUpperCase(), date.getCData());\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"adr\":\n\t\t\t\t\t\tif (c.getChildren() != null) {\n\t\t\t\t\t\t\tr = new Element(\"ADR\");\n\t\t\t\t\t\t\tfor (Element c1 : c.getChildren()) {\n\t\t\t\t\t\t\t\tswitch (c1.getName()) {\n\t\t\t\t\t\t\t\t\tcase \"pobox\":\n\t\t\t\t\t\t\t\t\tcase \"street\":\n\t\t\t\t\t\t\t\t\tcase \"locality\":\n\t\t\t\t\t\t\t\t\tcase \"region\":\n\t\t\t\t\t\t\t\t\t\tr.addChild(new Element(c1.getName().toUpperCase(), c1.getCData()));\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase \"ext\":\n\t\t\t\t\t\t\t\t\t\tr.addChild(new Element(\"EXTADD\", c1.getCData()));\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase \"code\":\n\t\t\t\t\t\t\t\t\t\tr.addChild(new Element(\"PCODE\", c1.getCData()));\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase \"country\":\n\t\t\t\t\t\t\t\t\t\tr.addChild(new Element(\"CTRY\", c1.getCData()));\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase \"parameters\":\n\t\t\t\t\t\t\t\t\t\tfor (Element c2 : c1.getChildren()) {\n\t\t\t\t\t\t\t\t\t\t\tswitch (c2.getName()) {\n\t\t\t\t\t\t\t\t\t\t\t\tcase \"type\":\n\t\t\t\t\t\t\t\t\t\t\t\t\tlist = c2.findChildren((Element c3) -> c3.getName() == \"text\");\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (list != null) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tfor (Element c3 : list) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tr.addChild(new Element(c3.getCData().toUpperCase()));\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\tcase \"pref\":\n\t\t\t\t\t\t\t\t\t\t\t\t\ttext = c2.findChild((Element c3) -> c3.getName() == \"integer\");\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (text != null) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tr.addChild(new Element(\"PREF\"));\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"tel\":\n\t\t\t\t\t\tr = new Element(\"TEL\");\n\t\t\t\t\t\tif (c.getChildren() != null) {\n\t\t\t\t\t\t\tfor (Element c1 : c.getChildren()) {\n\t\t\t\t\t\t\t\tswitch (c1.getName()) {\n\t\t\t\t\t\t\t\t\tcase \"parameters\":\n\t\t\t\t\t\t\t\t\t\tfor (Element c2 : c1.getChildren()) {\n\t\t\t\t\t\t\t\t\t\t\tswitch (c2.getName()) {\n\t\t\t\t\t\t\t\t\t\t\t\tcase \"type\":\n\t\t\t\t\t\t\t\t\t\t\t\t\tlist = c2.findChildren((Element c3) -> c3.getName() == \"text\");\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (list != null) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tfor (Element c3 : list) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tr.addChild(new Element(c3.getCData().toUpperCase()));\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\tcase \"pref\":\n\t\t\t\t\t\t\t\t\t\t\t\t\ttext = c2.findChild((Element c3) -> c3.getName() == \"integer\");\n\t\t\t\t\t\t\t\t\t\t\t\t\tif (text != null) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tr.addChild(new Element(\"PREF\"));\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase \"uri\":\n\t\t\t\t\t\t\t\t\t\tString uriStr = c1.getCData();\n\t\t\t\t\t\t\t\t\t\tif (uriStr != null) {\n\t\t\t\t\t\t\t\t\t\t\tMatcher matcher = TEL_URI_PATTERN.matcher(uriStr);\n\t\t\t\t\t\t\t\t\t\t\tif (matcher.matches()) {\n\t\t\t\t\t\t\t\t\t\t\t\tr.addChild(new Element(\"NUMBER\", matcher.group(1)));\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"email\":\n\t\t\t\t\t\tr = new Element(\"EMAIL\");\n\t\t\t\t\t\tif (c.getChildren() != null) {\n\t\t\t\t\t\t\tfor (Element c1 : c.getChildren()) {\n\t\t\t\t\t\t\t\tswitch (c1.getName()) {\n\t\t\t\t\t\t\t\t\tcase \"parameters\":\n\t\t\t\t\t\t\t\t\t\tList<Element> parametersChildren = c1.getChildren();\n\t\t\t\t\t\t\t\t\t\tif (parametersChildren != null) {\n\t\t\t\t\t\t\t\t\t\t\tfor (Element c2 : c1.getChildren()) {\n\t\t\t\t\t\t\t\t\t\t\t\tswitch (c2.getName()) {\n\t\t\t\t\t\t\t\t\t\t\t\t\tcase \"type\":\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tlist = c2.findChildren((Element c3) -> c3.getName() == \"text\");\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (list != null) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tfor (Element c3 : list) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tr.addChild(new Element(c3.getCData().toUpperCase()));\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\tcase \"pref\":\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttext = c2.findChild((Element c3) -> c3.getName() == \"integer\");\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (text != null) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tr.addChild(new Element(\"PREF\"));\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase \"text\":\n\t\t\t\t\t\t\t\t\t\tr.addChild(new Element(\"USERID\", c1.getCData()));\n\t\t\t\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"impp\":\n\t\t\t\t\t\turi = c.findChild((Element c1) -> c1.getName() == \"uri\");\n\t\t\t\t\t\tif (uri != null) {\n\t\t\t\t\t\t\tMatcher matcher = XMPP_URI_PATTERN.matcher(uri.getCData());\n\t\t\t\t\t\t\tif (matcher.matches()) {\n\t\t\t\t\t\t\t\tr = new Element(\"JABBERID\", matcher.group(1));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\tcase \"geo\":\n\t\t\t\t\t\turi = c.findChild((Element c1) -> c1.getName() == \"uri\");\n\t\t\t\t\t\tif (uri != null) {\n\t\t\t\t\t\t\tMatcher matcher = GEO_URI_PATTERN.matcher(uri.getCData());\n\t\t\t\t\t\t\tif (matcher.matches()) {\n\t\t\t\t\t\t\t\tr = new Element(\"GEO\");\n\t\t\t\t\t\t\t\tr.addChild(new Element(\"LAT\", matcher.group(1)));\n\t\t\t\t\t\t\t\tr.addChild(new Element(\"LON\", matcher.group(2)));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"agent\":\n\t\t\t\t\t\tr = new Element(c.getName().toUpperCase());\n\t\t\t\t\t\turi = c.findChild((Element c1) -> c1.getName() == \"uri\");\n\t\t\t\t\t\tr.addChild(new Element(\"EXTVAL\", uri.getCData()));\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"org\":\n\t\t\t\t\t\ttext = c.findChild((Element c1) -> c1.getName() == \"text\");\n\t\t\t\t\t\tif (text != null) {\n\t\t\t\t\t\t\tr = new Element(\"ORG\");\n\t\t\t\t\t\t\tr.addChild(new Element(\"ORGNAME\", text.getCData()));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"rev\":\n\t\t\t\t\t\ttext = c.findChild((Element c2) -> c2.getName() == \"timestamp\");\n\t\t\t\t\t\tif (text != null) {\n\t\t\t\t\t\t\tr = new Element(\"REV\", text.getCData());\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"sort-as\":\n\t\t\t\t\t\tr = new Element(\"SORT-STRING\", c.getCData());\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"sound\":\n\t\t\t\t\t\tr = new Element(\"SOUND\");\n\t\t\t\t\t\turi = c.findChild((Element c1) -> c1.getName() == \"uri\");\n\t\t\t\t\t\tif (uri != null) {\n\t\t\t\t\t\t\tMatcher matcher = DATA_URI_PATTERN.matcher(uri.getCData());\n\t\t\t\t\t\t\tif (matcher.matches()) {\n\t\t\t\t\t\t\t\tr.addChild(new Element(\"BINVAL\", matcher.group(2)));\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tr.addChild(new Element(\"EXTVAL\", uri.getCData()));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"uid\":\n\t\t\t\t\tcase \"url\":\n\t\t\t\t\t\turi = c.findChild((Element c1) -> c1.getName() == \"uri\");\n\t\t\t\t\t\tif (uri != null) {\n\t\t\t\t\t\t\tr = new Element(c.getName().toUpperCase(), uri.getCData());\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"key\":\n\t\t\t\t\t\ttext = c.findChild((Element c1) -> c1.getName() == \"text\");\n\t\t\t\t\t\tif (text != null) {\n\t\t\t\t\t\t\tr = new Element(c.getName().toUpperCase());\n\t\t\t\t\t\t\tr.addChild(new Element(\"CRED\", text.getCData()));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (r != null) {\n\t\t\t\t\tvcardTemp.addChild(r);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn vcardTemp;\n\t\t});\n\t}\n\n\tpublic static Element convertVCard4ToVCardTemp(Element vcard4) {\n\t\treturn CONVERTERS.get(VCard4.XMLNS + \"|\" + VCardTemp.XMLNS).convert(vcard4);\n\t}\n\n\tpublic static Element convertVCardTempToVCard4(Element vcardTemp) {\n\t\treturn CONVERTERS.get(VCardTemp.XMLNS + \"|\" + VCard4.XMLNS).convert(vcardTemp);\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\tPROCESSORS.remove(this.id(), this);\n\t}\n\n\tprotected abstract String getVCardXMLNS();\n\n\t@Override\n\tpublic void initialize() {\n\t\tPROCESSORS.put(this.id(), this);\n\t}\n\n\tprotected void setVCard(XMPPResourceConnection session, Element vcard)\n\t\t\tthrows TigaseDBException, NotAuthorizedException {\n\t\tfor (VCardXMPPProcessorAbstract processor : PROCESSORS.values()) {\n\t\t\tConverter conv = CONVERTERS.get(getVCardXMLNS() + \"|\" + processor.getVCardXMLNS());\n\t\t\tElement vcardEl = conv == null ? vcard : conv.convert(vcard);\n\t\t\tprocessor.storeVCard(session, vcardEl);\n\t\t}\n\t}\n\n\tprotected abstract void storeVCard(XMPPResourceConnection session, Element vcard)\n\t\t\tthrows TigaseDBException, NotAuthorizedException;\n\n\tprivate interface Converter {\n\n\t\tElement convert(Element vcard);\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/annotation/AnnotatedXMPPProcessor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.annotation;\n\nimport tigase.xml.Element;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPProcessor;\nimport tigase.xmpp.XMPPResourceConnection;\n\nimport java.util.*;\n\n/**\n * This class is extension of {@link tigase.xmpp.XMPPProcessor XMPPProcessor} which provides support for defining\n * processor Id, supported paths and XMLNSs and more using annotations\n *\n * @author andrzej\n */\npublic abstract class AnnotatedXMPPProcessor\n\t\textends XMPPProcessor {\n\n\tprivate Element[] DISCO_FEATURES;\n\tprivate String[][] ELEMENTS;\n\tprivate String ID;\n\tprivate Set<StanzaType> STANZA_TYPES;\n\tprivate Element[] STREAM_FEATURES;\n\tprivate String[] XMLNSS;\n\n\tprivate static void processHandle(Handle handle, List<String[]> elems, List<String> xmlnss) {\n\t\tif (handle != null) {\n\t\t\tString[] path = null;\n\t\t\tif (handle.path().length > 0) {\n\t\t\t\tpath = handle.path();\n\t\t\t} else {\n\t\t\t\tpath = handle.pathStr().split(\"/\");\n\t\t\t}\n\t\t\tfor (int i = 0; i < path.length; i++) {\n\t\t\t\tpath[i] = path[i].intern();\n\t\t\t}\n\t\t\telems.add(path);\n\t\t\txmlnss.add(handle.xmlns().intern());\n\t\t}\n\t}\n\n\tprotected AnnotatedXMPPProcessor() {\n\t\tClass cls = this.getClass();\n\n\t\tcmpInfo = null;\n\t\t// support for id\n\t\tId id = (Id) cls.getAnnotation(Id.class);\n\t\tif (id != null) {\n\t\t\tID = id.value();\n\t\t}\n\n\t\t// support for supported elements paths and xmlnss\n\t\tprocessHandleAnnotation(cls);\n\n\t\t// support for supported type of stanzas\n\t\tprocessHandleStazaTypesAnnotation(cls);\n\n\t\t// support for stream features\n\t\tprocessStreamFeaturesAnnotation(cls);\n\n\t\t// support for disco features\n\t\tprocessDiscoFeaturesAnnotation(cls);\n\n\t\tcmpInfo = null;\n\t}\n\n\t@Override\n\tpublic String id() {\n\t\treturn ID;\n\t}\n\n\t@Override\n\tpublic String[][] supElementNamePaths() {\n\t\treturn ELEMENTS;\n\t}\n\n\t@Override\n\tpublic String[] supNamespaces() {\n\t\treturn XMLNSS;\n\t}\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(XMPPResourceConnection session) {\n\t\treturn DISCO_FEATURES;\n\t}\n\n\t@Override\n\tpublic Element[] supStreamFeatures(XMPPResourceConnection session) {\n\t\treturn STREAM_FEATURES;\n\t}\n\n\t@Override\n\tpublic Set<StanzaType> supTypes() {\n\t\treturn STANZA_TYPES;\n\t}\n\n\tprivate void processHandleAnnotation(Class cls) {\n\t\tList<String[]> elems = new ArrayList<String[]>();\n\t\tList<String> xmlnss = new ArrayList<String>();\n\n\t\tHandles handles = (Handles) cls.getAnnotation(Handles.class);\n\t\tif (handles != null) {\n\t\t\tfor (Handle handle : handles.value()) {\n\t\t\t\tprocessHandle(handle, elems, xmlnss);\n\t\t\t}\n\t\t}\n\t\tHandle handle = (Handle) cls.getAnnotation(Handle.class);\n\t\tprocessHandle(handle, elems, xmlnss);\n\n\t\tif (!elems.isEmpty()) {\n\t\t\tELEMENTS = elems.toArray(new String[elems.size()][]);\n\t\t\tXMLNSS = xmlnss.toArray(new String[xmlnss.size()]);\n\t\t}\n\t}\n\n\tprivate void processStreamFeaturesAnnotation(Class cls) {\n\t\tStreamFeatures streamFeatures = (StreamFeatures) cls.getAnnotation(StreamFeatures.class);\n\t\tif (streamFeatures != null) {\n\t\t\tList<Element> values = new ArrayList<Element>();\n\t\t\tfor (StreamFeature feature : streamFeatures.value()) {\n\t\t\t\tfinal Element featureElement = new Element(feature.elem(), new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{feature.xmlns()});\n\t\t\t\tif (feature.children().length > 0) {\n\t\t\t\t\tfor (String child : feature.children()) {\n\t\t\t\t\t\tfeatureElement.addChild(new Element(child));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tvalues.add(featureElement);\n\t\t\t}\n\t\t\tSTREAM_FEATURES = values.toArray(new Element[values.size()]);\n\t\t}\n\t}\n\n\tprivate void processDiscoFeaturesAnnotation(Class cls) {\n\t\tDiscoFeatures discoFeatures = (DiscoFeatures) cls.getAnnotation(DiscoFeatures.class);\n\t\tif (discoFeatures != null) {\n\t\t\tList<Element> values = new ArrayList<Element>();\n\t\t\tfor (String feature : discoFeatures.value()) {\n\t\t\t\tvalues.add(new Element(\"feature\", new String[]{\"var\"}, new String[]{feature}));\n\t\t\t}\n\t\t\tDISCO_FEATURES = values.toArray(new Element[values.size()]);\n\t\t}\n\t}\n\n\tprivate void processHandleStazaTypesAnnotation(Class cls) {\n\t\tHandleStanzaTypes handleStanzaTypes = (HandleStanzaTypes) cls.getAnnotation(HandleStanzaTypes.class);\n\t\tif (handleStanzaTypes != null) {\n\t\t\tStanzaType[] types = handleStanzaTypes.value();\n\t\t\tEnumSet<StanzaType> tmp = EnumSet.noneOf(StanzaType.class);\n\t\t\ttmp.addAll(Arrays.asList(types));\n\t\t\tSTANZA_TYPES = tmp;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/annotation/DiscoFeatures.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.annotation;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n/**\n * Annotation which placed on {@link tigase.xmpp.impl.annotation.AnnotatedXMPPProcessor AnnotatedXMPPProcessor} can\n * define discovery features provided by processor\n *\n * @author andrzej\n */\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface DiscoFeatures {\n\n\tString[] value();\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/annotation/Handle.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.annotation;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n/**\n * Annotation which placed on {@link tigase.xmpp.impl.annotation.AnnotatedXMPPProcessor AnnotatedXMPPProcessor} can\n * define path and XMLNSS supported by implemented processor\n *\n * @author andrzej\n */\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface Handle {\n\n\tString ANY_XMLNS = \"*\";\n\n\tString[] path() default {};\n\n\tString pathStr() default \"\";\n\n\tString xmlns();\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/annotation/HandleStanzaTypes.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.annotation;\n\nimport tigase.xmpp.StanzaType;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n/**\n * Annotation which placed on {@link tigase.xmpp.impl.annotation.AnnotatedXMPPProcessor AnnotatedXMPPProcessor} can\n * define which stanza types are supported by implemented processor\n *\n * @author andrzej\n */\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface HandleStanzaTypes {\n\n\tStanzaType[] value();\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/annotation/Handles.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.annotation;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n/**\n * Annotation which placed on {@link tigase.xmpp.impl.annotation.AnnotatedXMPPProcessor AnnotatedXMPPProcessor} can\n * define paths and corresponding XMLNSs supported by processor\n *\n * @author andrzej\n */\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface Handles {\n\n\tHandle[] value();\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/annotation/Id.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.annotation;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n/**\n * Annotation which placed on {@link tigase.xmpp.impl.annotation.AnnotatedXMPPProcessor AnnotatedXMPPProcessor} can\n * define of processor implementation\n *\n * @author andrzej\n */\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface Id {\n\n\tString value();\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/annotation/StreamFeature.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.annotation;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n/**\n * Annotation which placed on {@link tigase.xmpp.impl.annotation.AnnotatedXMPPProcessor AnnotatedXMPPProcessor} can\n * define supported stream features provided by processor\n *\n * @author andrzej\n */\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface StreamFeature {\n\n\tString elem();\n\n\tString xmlns();\n\n\tString[] children() default {};\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/annotation/StreamFeatures.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.annotation;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n/**\n * Annotation which placed on {@link tigase.xmpp.impl.annotation.AnnotatedXMPPProcessor AnnotatedXMPPProcessor} can\n * define supported stream features provided by processor\n *\n * @author andrzej\n */\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface StreamFeatures {\n\n\tStreamFeature[] value();\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/package-info.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/**\n * Package containing implementation of all Session Manager plugins\n */\npackage tigase.xmpp.impl;\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/push/AbstractPushNotifications.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.push;\n\nimport tigase.db.*;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.*;\nimport tigase.server.amp.db.MsgRepository;\nimport tigase.util.datetime.TimestampHelper;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.DomBuilderHandler;\nimport tigase.xml.Element;\nimport tigase.xml.SimpleParser;\nimport tigase.xml.SingletonFactory;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.annotation.AnnotatedXMPPProcessor;\nimport tigase.xmpp.impl.annotation.DiscoFeatures;\nimport tigase.xmpp.impl.annotation.Handle;\nimport tigase.xmpp.impl.annotation.Handles;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Consumer;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.xmpp.impl.push.AbstractPushNotifications.XMLNS;\n\n@DiscoFeatures({PushNotifications.ID})\n@Handles({@Handle(path = {Iq.ELEM_NAME, \"enable\"}, xmlns = XMLNS),\n\t\t  @Handle(path = {Iq.ELEM_NAME, \"disable\"}, xmlns = XMLNS),\n\t\t  @Handle(path = {Message.ELEM_NAME, \"pubsub\"}, xmlns = \"http://jabber.org/protocol/pubsub\")\n})\npublic class AbstractPushNotifications\n\t\textends AnnotatedXMPPProcessor\n\t\timplements XMPPProcessorIfc {\n\n\tpublic static final String XMLNS = \"urn:xmpp:push:0\";\n\tpublic static final String ID = XMLNS;\n\tprivate static final Logger log = Logger.getLogger(AbstractPushNotifications.class.getCanonicalName());\n\tprivate static final String JABBER_X_DATA_XMLNS = \"jabber:x:data\";\n\tprivate static final String SUMMARY_XMLNS = XMLNS + \":summary\";\n\tprivate static final SimpleParser parser = SingletonFactory.getParserInstance();\n\n\t@ConfigField(desc = \"Send notifications with body\", alias = \"with-body\")\n\tprotected boolean withBody = true;\n\t@ConfigField(desc = \"Send notifications with sender\", alias = \"with-sender\")\n\tprotected boolean withSender = true;\n\t@ConfigField(desc = \"Max notification timeout\", alias = \"max-timeout\")\n\tprotected Duration maxTimeout = Duration.ofMinutes(6);\n\t@ConfigField(desc = \"Device registration TTL\")\n\tprivate Duration deviceRegistrationTTL = null;\n\n\t@Inject\n\tprivate MsgRepositoryIfc msgRepository;\n\n\t@Inject\n\tprivate UserRepository userRepository;\n\n\t@Inject(bean = \"sess-man\")\n\tprivate PacketWriterWithTimeout writer;\n\n\t@Inject(nullAllowed = true)\n\tprivate PushPresence pushDevicesPresence;\n\n\t@ConfigField(desc = \"Notification to display for encrypted messages\", alias = \"encrypted-message-body\")\n\tprivate String encryptedMessageBody = \"New secure message. Open to see the message.\";\n\n\tpublic PushPresence getPushDevicesPresence() {\n\t\treturn pushDevicesPresence;\n\t}\n\n\tpublic void setPushDevicesPresence(PushPresence pushDevicesPresence) {\n\t\tthis.pushDevicesPresence = pushDevicesPresence;\n\t}\n\n\tprotected boolean shouldDisablePush(Authorization error) {\n\t\tif (error == null) {\n\t\t\treturn false;\n\t\t}\n\t\tswitch (error) {\n\t\t\tcase REMOTE_SERVER_TIMEOUT:\n\t\t\tcase SERVICE_UNAVAILABLE:\n\t\t\tcase INTERNAL_SERVER_ERROR:\n\t\t\tcase BAD_REQUEST:\n\t\t\t\treturn false;\n\t\t\tdefault:\n\t\t\t\t// we need to handle possible error\n\t\t\t\treturn true;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository nonAuthUserRepository,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> map) throws XMPPException {\n\t\ttry {\n\t\t\tif (packet.getElemName() == Message.ELEM_NAME) {\n\t\t\t\tprocessMessage(packet, session, results::offer);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (session == null || !session.getConnectionId().equals(packet.getPacketFrom())) {\n\t\t\t\tresults.offer(Authorization.FORBIDDEN.getResponseMessage(packet, null, true));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tElement actionEl = packet.getElement().findChild(element -> element.getXMLNS() == XMLNS);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Processing PUSH registration, jid: {0}, actionEl: {1}\",\n\t\t\t\t\t\tnew Object[]{session.getjid(), actionEl});\n\t\t\t}\n\n\t\t\tif (actionEl != null) {\n\t\t\t\tString jidStr = actionEl.getAttributeStaticStr(\"jid\");\n\t\t\t\tif (jidStr == null) {\n\t\t\t\t\tthrow new TigaseStringprepException(\"JID is NULL!\");\n\t\t\t\t}\n\t\t\t\tJID jid = JID.jidInstance(actionEl.getAttributeStaticStr(\"jid\"));\n\t\t\t\tString node = actionEl.getAttributeStaticStr(\"node\");\n\t\t\t\tswitch (actionEl.getName()) {\n\t\t\t\t\tcase \"enable\":\n\t\t\t\t\t\tenableNotifications(session, jid, node, actionEl, actionEl.findChild(\n\t\t\t\t\t\t\t\telement -> element.getXMLNS() == JABBER_X_DATA_XMLNS && element.getName() == \"x\"), results::offer);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"disable\":\n\t\t\t\t\t\tdisableNotifications(session, session.getBareJID(), jid, node, results::offer);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet, null, true));\n\t\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tresults.offer(packet.okResult((Element) null, 0));\n\t\t\t}\n\t\t} catch (NotAuthorizedException ex) {\n\t\t\tresults.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet, \"Session is not authorized\", true));\n\t\t} catch (TigaseDBException ex) {\n\t\t\tresults.offer(Authorization.INTERNAL_SERVER_ERROR.getResponseMessage(packet, null, true));\n\t\t} catch (TigaseStringprepException ex) {\n\t\t\tresults.offer(Authorization.BAD_REQUEST.getResponseMessage(packet,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"Attribute 'jid' is not valid JID of Push Notifications service\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   true));\n\t\t}\n\t}\n\n\tprotected void processMessage(Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t  Consumer<Packet> results) throws NotAuthorizedException, TigaseDBException {\n\t\tElement pubsubEl = packet.getElemChild(\"pubsub\", \"http://jabber.org/protocol/pubsub\");\n\t\tif (pubsubEl != null) {\n\t\t\tString node = pubsubEl.getAttributeStaticStr(\"node\");\n\t\t\tif (node != null) {\n\t\t\t\tElement affiliationEl = pubsubEl.getChild(\"affiliation\");\n\t\t\t\tif (affiliationEl != null) {\n\t\t\t\t\tString userJid = affiliationEl.getAttributeStaticStr(\"jid\");\n\t\t\t\t\tif (\"none\".equals(affiliationEl.getAttributeStaticStr(\"affiliation\"))) {\n\t\t\t\t\t\tif (userJid != null) {\n\t\t\t\t\t\t\tBareJID bareJid = BareJID.bareJIDInstanceNS(userJid);\n\t\t\t\t\t\t\tuserRepository.removeData(bareJid, ID,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  packet.getStanzaFrom().toString() + \"/\" + node);\n\t\t\t\t\t\t\tif (getPushServices(bareJid).isEmpty() && pushDevicesPresence != null) {\n\t\t\t\t\t\t\t\tpushDevicesPresence.pushAvailabilityChanged(bareJid, false, results);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected void enableNotifications(XMPPResourceConnection session, JID jid, String node, Element enableElem,\n\t\t\t\t\t\t\t\t\t   Element optionsForm, Consumer<Packet> packetConsumer) throws NotAuthorizedException, TigaseDBException {\n\t\tElement settings = createSettingsElement(jid, node, enableElem, optionsForm);\n\n\t\tenableNotifications(session, jid, node, settings, packetConsumer);\n\t}\n\n\tprivate static final TimestampHelper timestampHelper = new TimestampHelper();\n\n\tprotected Element createSettingsElement(JID jid, String node, Element enableElem, Element optionsForm) {\n\t\tElement settings = new Element(\"settings\", new String[]{\"jid\", \"node\", \"createdAt\"},\n\t\t\t\t\t\t\t\t\t   new String[]{jid.toString(), node.toString(), timestampHelper.format(new Date())});\n\t\tif (optionsForm != null) {\n\t\t\tsettings.addChild(optionsForm);\n\t\t}\n\t\treturn settings;\n\t}\n\t\n\tprotected void enableNotifications(XMPPResourceConnection session, JID jid, String node, Element settings, Consumer<Packet> packetConsumer)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tString key = jid.toString() + \"/\" + node;\n\t\tMap<String, Element> pushServices = getPushServices(session);\n\t\tsession.setData(ID, key, settings.toString());\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Enabled push notifications for JID: {0}, node: {1}, settings: {2}\",\n\t\t\t\t\tnew Object[]{jid, node, settings.toString()});\n\t\t}\n\n\t\tboolean hadPushServices = !pushServices.isEmpty();\n\t\tpushServices.put(key, settings);\n\t\tif (!hadPushServices && pushDevicesPresence != null) {\n\t\t\tpushDevicesPresence.pushAvailabilityChanged(session.getBareJID(), true, packetConsumer);\n\t\t}\n\t}\n\n\tprotected void disableNotifications(XMPPResourceConnection session, BareJID userJid, JID jid, String node, Consumer<Packet> packetConsumer)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tMap<String, Element> pushServices = session != null ? getPushServices(session) : getPushServices(userJid);\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Disabled push notifications for JID: {0}, node: {1}, pushServices: {2}\",\n\t\t\t\t\tnew Object[]{jid, node, pushServices});\n\t\t}\n\n\t\tif (pushServices != null) {\n\t\t\tboolean hadPushServices = !pushServices.isEmpty();\n\t\t\tif (node != null) {\n\t\t\t\tString key = jid.toString() + \"/\" + node;\n\t\t\t\tpushServices.remove(key);\n\t\t\t\tif (session != null) {\n\t\t\t\t\tsession.removeData(ID, key);\n\t\t\t\t} else {\n\t\t\t\t\tuserRepository.removeData(userJid, ID, key);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tString prefix = jid.toString() + \"/\";\n\t\t\t\tList<String> removed = new ArrayList<>();\n\t\t\t\tpushServices.keySet().removeIf(key -> {\n\t\t\t\t\tif (key.startsWith(prefix)) {\n\t\t\t\t\t\tremoved.add(key);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t});\n\t\t\t\tfor (String key : removed) {\n\t\t\t\t\tif (session != null) {\n\t\t\t\t\t\tsession.removeData(ID, key);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tuserRepository.removeData(userJid, ID, key);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (hadPushServices && pushServices.isEmpty() && pushDevicesPresence != null) {\n\t\t\t\tpushDevicesPresence.pushAvailabilityChanged(session != null ? session.getBareJID() : userJid, false, packetConsumer);\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected Map<String, Element> getPushServices(XMPPResourceConnection session) {\n\t\treturn (Map<String, Element>) session.computeCommonSessionDataIfAbsent(ID, (key) -> {\n\t\t\tMap<String, Element> pushServices = new ConcurrentHashMap<String, Element>();\n\t\t\ttry {\n\t\t\t\tpushServices.putAll(getPushServices(session.getBareJID()));\n\t\t\t} catch (NotAuthorizedException | UserNotFoundException ex) {\n\t\t\t\tlog.log(Level.FINEST, \"Could not load push services for session \" + session, ex);\n\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\tlog.log(Level.WARNING, \"Could not load push services for session \" + session, ex);\n\t\t\t}\n\n\t\t\treturn pushServices;\n\t\t});\n\t}\n\n\tprotected Element prepareNotificationPayload(Element pushServiceSettings, PushNotificationCause cause, Packet packet, long msgCount) {\n\t\tElement notification = new Element(\"notification\", new String[]{\"xmlns\"}, new String[]{XMLNS});\n\n\t\tElement x = new Element(\"x\", new String[]{\"xmlns\"}, new String[]{\"jabber:x:data\"});\n\t\tnotification.addChild(x);\n\n\t\tDataForm.addFieldValue(notification, \"FORM_TYPE\", SUMMARY_XMLNS);\n\t\tDataForm.addFieldValue(notification, \"message-count\", String.valueOf(msgCount));\n\t\tif (packet != null) {\n\t\t\tif (withSender) {\n\t\t\t\tDataForm.addFieldValue(notification, \"last-message-sender\", packet.getStanzaFrom().toString());\n\t\t\t}\n\t\t\tif (withBody) {\n\t\t\t\tboolean isEncrypted = packet.getElemChild(\"encrypted\", \"eu.siacs.conversations.axolotl\") != null ||\n\t\t\t\t\t\tpacket.getElemChild(\"encrypted\", \"urn:xmpp:omemo:1\") != null;\n\t\t\t\tDataForm.addFieldValue(notification, \"last-message-body\", isEncrypted ? encryptedMessageBody :\n\t\t\t\t\t\t\t\t\t\t   packet.getElemCDataStaticStr(tigase.server.Message.MESSAGE_BODY_PATH));\n\t\t\t}\n\t\t\tif (withSender && packet.getElemName() == Message.ELEM_NAME && packet.getType() == StanzaType.groupchat) {\n\t\t\t\tElement mix = packet.getElement().getChild(\"mix\", \"urn:xmpp:mix:core:1\");\n\t\t\t\tif (mix != null) {\n\t\t\t\t\tElement nickEl = mix.getChild(\"nick\");\n\t\t\t\t\tif (nickEl != null) {\n\t\t\t\t\t\tString nick = nickEl.getCData();\n\t\t\t\t\t\tif (nick != null) {\n\t\t\t\t\t\t\tElement groupchat = new Element(\"groupchat\");\n\t\t\t\t\t\t\tgroupchat.setXMLNS(\"http://tigase.org/protocol/muc#offline\");\n\t\t\t\t\t\t\tgroupchat.addChild(new Element(\"nickname\", nick));\n\t\t\t\t\t\t\tnotification.addChild(groupchat);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tElement groupchat = new Element(\"groupchat\");\n\t\t\t\t\tgroupchat.setXMLNS(\"http://tigase.org/protocol/muc#offline\");\n\t\t\t\t\tgroupchat.addChild(new Element(\"nickname\", packet.getStanzaFrom().getResource()));\n\t\t\t\t\tnotification.addChild(groupchat);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif (cause == PushNotificationCause.ACCOUNT_REMOVED) {\n\t\t\t\tElement eventEl = new Element(\"event\");\n\t\t\t\teventEl.setXMLNS(\"http://tigase.org/protocol/account#event\");\n\t\t\t\teventEl.setAttribute(\"name\", \"account-removed\");\n\t\t\t\tnotification.addChild(eventEl);\n\t\t\t}\n\t\t}\n\t\treturn notification;\n\t}\n\n\tprotected void sendPushNotification(BareJID userJid, Collection<Element> pushServices,\n\t\t\t\t\t\t\t\t\t\tXMPPResourceConnection session, PushNotificationCause cause, Packet packet, Map<Enum, Long> notificationData, Consumer<Packet> packetConsumer) {\n\t\tfor (Element settings : pushServices) {\n\t\t\ttry {\n\t\t\t\tif (packet != null && !isSendingNotificationAllowed(userJid, session, settings, packet)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tJID pushService = JID.jidInstance(settings.getAttributeStaticStr(\"jid\"));\n\t\t\t\tString pushNode = settings.getAttributeStaticStr(\"node\");\n\t\t\t\tif (deviceRegistrationTTL != null) {\n\t\t\t\t\t// check device registration TTL\n\t\t\t\t\tDate createdAt = timestampHelper.parseTimestamp(settings.getAttributeStaticStr(\"createdAt\"));\n\t\t\t\t\tif (createdAt != null && Duration.between(createdAt.toInstant(), Instant.now()).compareTo(deviceRegistrationTTL) > 0) {\n\t\t\t\t\t\t// registration is expired\n\t\t\t\t\t\tlog.log(Level.WARNING, \"disabling push service \" + pushService + \"/\" + pushNode + \" for user \" + userJid + \", expired due to TTL\");\n\t\t\t\t\t\tdisableNotifications(session, userJid, pushService, pushNode, packetConsumer);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfinal Element notification = prepareNotificationPayload(settings, cause, packet, notificationData.getOrDefault(\n\t\t\t\t\t\tMsgRepository.MSG_TYPES.message, 0l));\n\t\t\t\tElement publishOptionsForm = settings.findChild(\n\t\t\t\t\t\telement -> element.getXMLNS() == JABBER_X_DATA_XMLNS && element.getName() == \"x\");\n\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Push notifications for JID: {0}, notification: {1}, pushServices: {2}\",\n\t\t\t\t\t\t\tnew Object[]{userJid, notification, pushService});\n\t\t\t\t}\n\n\t\t\t\tsendPushNotification(userJid, notification, pushService, pushNode, publishOptionsForm);\n\t\t\t} catch (Exception ex) {\n\t\t\t\tlog.log(Level.FINE, \"Could not publish notification for \" + userJid + \" to \" +\n\t\t\t\t\t\tsettings.getAttributeStaticStr(\"jid\") + \" at \" + settings.getAttributeStaticStr(\"node\"));\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected Map<String, Element> getPushServices(BareJID userJid) throws TigaseDBException {\n\t\treturn userRepository.getDataMap(userJid, ID, this::parseElement);\n\t}\n\n\tprotected void sendPushNotification(XMPPResourceConnection session, PushNotificationCause cause, Packet packet, Consumer<Packet> packetConsumer)\n\t\t\tthrows TigaseDBException {\n\t\tfinal BareJID userJid = packet.getStanzaTo().getBareJID();\n\t\tMap<String, Element> pushServices = (session != null && session.isAuthorized())\n\t\t\t\t\t\t\t\t\t\t\t? getPushServices(session)\n\t\t\t\t\t\t\t\t\t\t\t: getPushServices(userJid);\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Sending push notifications for JID: {0}, packet: {1}, pushServices: {2}\",\n\t\t\t\t\tnew Object[]{userJid, packet, pushServices});\n\t\t}\n\t\tif (pushServices.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\n\t\tMap<Enum, Long> typesCount = msgRepository.getMessagesCount(packet.getStanzaTo());\n\n\t\tsendPushNotification(userJid, pushServices.values(), session, cause, packet, typesCount, packetConsumer);\n\t}\n\n\tprotected boolean isSendingNotificationAllowed(BareJID userJid, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t\t   Element pushServiceSettings, Packet packet) {\n\t\treturn true;\n\t}\n\n\tprivate void sendPushNotification(BareJID userJid, Element notification, JID pushService, String pushNode,\n\t\t\t\t\t\t\t\t\t  Element publishOptionsForm) {\n\t\tElement iq = new Element(\"iq\", new String[]{\"xmlns\", \"type\"},\n\t\t\t\t\t\t\t\t new String[]{Packet.CLIENT_XMLNS, StanzaType.set.name()});\n\n\t\tElement pubsub = new Element(\"pubsub\", new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t new String[]{\"http://jabber.org/protocol/pubsub\"});\n\t\tiq.addChild(pubsub);\n\t\tElement publish = new Element(\"publish\", new String[]{\"node\"}, new String[]{pushNode});\n\t\tpubsub.addChild(publish);\n\t\tElement item = new Element(\"item\");\n\t\tpublish.addChild(item);\n\n\t\titem.addChild(notification);\n\n\t\tif (publishOptionsForm != null) {\n\t\t\tElement publishOptions = new Element(\"publish-options\");\n\t\t\tpublishOptions.addChild(publishOptionsForm);\n\t\t\tpubsub.addChild(publishOptions);\n\t\t}\n\n\t\twriter.addOutPacketWithTimeout(new Iq(iq, JID.jidInstanceNS(null, userJid.getDomain(), null), pushService),\n\t\t\t\t\t\t\t\t\t   maxTimeout, result -> {\n\t\t\t\t\tif (result == null) {\n\t\t\t\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\t\t\t\tlog.log(Level.FINER, \"push notification delivery to \" + pushService + \" from \" + userJid +\n\t\t\t\t\t\t\t\t\t\" timed out!\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (!shouldDisablePush(Authorization.getByCondition(result.getErrorCondition()))) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\ttry {\n\t\t\t\t\t\tuserRepository.removeData(userJid, ID, pushService + \"/\" + pushNode);\n\t\t\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\t\t\tlog.log(Level.FINEST,\n\t\t\t\t\t\t\t\t\"could not disable push for \" + userJid + \" on \" + pushService + \"/\" + pushNode, ex);\n\t\t\t\t\t}\n\t\t\t\t});\n\t}\n\n\tprivate Element parseElement(String data) {\n\t\tDomBuilderHandler domHandler = new DomBuilderHandler();\n\t\tparser.parse(domHandler, data.toCharArray(), 0, data.length());\n\n\t\tQueue<Element> elems = domHandler.getParsedElements();\n\n\t\treturn (elems == null) ? null : elems.poll();\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/push/AwayPushNotificationsExtension.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.push;\n\nimport tigase.cluster.strategy.ClusteringStrategyIfc;\nimport tigase.cluster.strategy.ConnectionRecordIfc;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.HandleEvent;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.server.Message;\nimport tigase.server.Packet;\nimport tigase.server.Presence;\nimport tigase.server.xmppsession.SessionManagerHandler;\nimport tigase.server.xmppsession.UserPresenceChangedEvent;\nimport tigase.xml.Element;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.lang.reflect.Method;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport static tigase.xmpp.impl.push.AbstractPushNotifications.ID;\n\n@Bean(name = \"away\", parent = PushNotifications.class, active = false)\npublic class AwayPushNotificationsExtension implements PushNotificationsExtension, Initializable, UnregisterAware {\n\n\tprivate static final Logger log = Logger.getLogger(AwayPushNotificationsExtension.class.getCanonicalName());\n\n\tprivate static final String PRESENCE_PREV_KEY = ID + \"#presence-prev\";\n\n\tprivate static final Element[] DISCO_FEATURES = new Element[]{\n\t\t\tnew Element(\"feature\", new String[]{\"var\"}, new String[]{\"tigase:push:away:0\"})};\n\n\t@Inject(nullAllowed = true)\n\tprivate ClusteringStrategyIfc clusteringStrategy;\n\t@Inject\n\tprivate EventBus eventBus;\n\t@Inject\n\tprivate SessionManagerHandler sessionManagerHandler;\n\t@Inject\n\tprivate PushNotifications pushNotifications;\n\n\t@Override\n\tpublic Element[] getDiscoFeatures() {\n\t\treturn DISCO_FEATURES;\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\teventBus.registerAll(this);\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\teventBus.unregisterAll(this);\n\t}\n\n\t@HandleEvent(filter = HandleEvent.Type.local)\n\tpublic void presenceChanged(UserPresenceChangedEvent event) {\n\t\tif (event.getPresence().getType() == StanzaType.unavailable) {\n\t\t\treturn;\n\t\t}\n\n\t\tXMPPResourceConnection conn = event.getSession().getResourceForJID(event.getPresence().getStanzaFrom());\n\t\tif (conn == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tPacket oldPresence = (Packet) conn.getSessionData(PRESENCE_PREV_KEY);\n\t\tconn.putSessionData(PRESENCE_PREV_KEY, event.getPresence());\n\t\tif (oldPresence != null) {\n\t\t\tString show = event.getPresence().getElemCDataStaticStr(Presence.PRESENCE_SHOW_PATH);\n\t\t\tif (show != null && !\"chat\".equals(show)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tString oldShow = event.getPresence().getElemCDataStaticStr(Presence.PRESENCE_SHOW_PATH);\n\t\t\tif (oldShow == null || \"chat\".equals(oldShow)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// client changed presence to \"online\"/\"chat\" from \"away\", \"xa\", \"dnd\", \"offline\"\n\t\t// so we need to send notification\n\t\tCollection<Element> services = getPushServicesForAwayNotifications(conn);\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Sending push notifications for JID: {0}, oldPresence: {1}, services: {2}\",\n\t\t\t\t\tnew Object[]{conn.getjid(), oldPresence, services});\n\t\t}\n\n\t\tif (services.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tpushNotifications.notifyOfflineMessagesRetrieved(conn.getBareJID(), services, packet -> {});\n\t\t} catch (NotAuthorizedException ex) {\n\t\t\tlog.log(Level.FINEST, \"Connection {0} not yet authorized, ignoring..\", conn);\n\t\t}\n\t}\n\n\n\t@Override\n\tpublic void processEnableElement(Element enableEl, Element settingsEl) {\n\t\tString away = enableEl.getAttributeStaticStr(\"away\");\n\t\tif (\"true\".equals(away)) {\n\t\t\tsettingsEl.addAttribute(\"away\", \"true\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setPushNotifications(PushNotifications pushNotifications) {\n\t\tthis.pushNotifications = pushNotifications;\n\t}\n\n\t@Override\n\tpublic boolean shouldSendNotification(Packet packet, BareJID userJid, XMPPResourceConnection session)\n\t\t\tthrows XMPPException {\n\t\tif (packet.getElemName() != Message.ELEM_NAME || packet.getType() == StanzaType.groupchat) {\n\t\t\treturn false;\n\t\t}\n\t\tif (session == null || !session.isAuthorized()) {\n\t\t\treturn false;\n\t\t}\n\t\tif (packet.getStanzaTo() == null || !session.isUserId(packet.getStanzaTo().getBareJID())) {\n\t\t\treturn false;\n\t\t}\n\n\t\tElement body = packet.getElement().findChild(Message.MESSAGE_BODY_PATH);\n\t\tif (body == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tCollection<Element> services = getPushServicesForAwayNotifications(session);\n\t\tif (services.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tboolean hasOnlineResource = session.getActiveSessions()\n\t\t\t\t.stream()\n\t\t\t\t.filter(conn -> conn.getPriority() >= 0)\n\t\t\t\t.map(conn -> conn.getPresence())\n\t\t\t\t.filter(presence -> presence != null)\n\t\t\t\t.filter(presence -> presence.getCDataStaticStr(Presence.PRESENCE_SHOW_PATH) == null)\n\t\t\t\t.findAny()\n\t\t\t\t.isPresent();\n\n\t\tif (hasOnlineResource) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (clusteringStrategy != null) {\n\t\t\tSet<ConnectionRecordIfc> connections = clusteringStrategy.getConnectionRecords(packet.getStanzaTo().getBareJID());\n\t\t\tif (connections != null) {\n\t\t\t\tif (connections.stream().filter(rec -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tMethod m = rec.getClass().getMethod(\"getLastPresence\");\n\t\t\t\t\t\tElement presence = (Element) m.invoke(rec);\n\t\t\t\t\t\tif (presence == null) {\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn presence.getCDataStaticStr(Presence.PRESENCE_SHOW_PATH) == null;\n\t\t\t\t\t} catch (Throwable ex) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}).findAny().isPresent()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tOptional<JID> notificationSender = Stream.concat(connections.stream().map(rec -> rec.getNode()), Stream.of(sessionManagerHandler.getComponentId()))\n\t\t\t\t\t\t.distinct()\n\t\t\t\t\t\t.sorted()\n\t\t\t\t\t\t.findFirst();\n\t\t\t\tif (notificationSender.isPresent() &&\n\t\t\t\t\t\t!notificationSender.filter(jid -> !sessionManagerHandler.getComponentId().equals(jid)).isPresent()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif (session.getActiveSessions()\n\t\t\t\t\t.stream()\n\t\t\t\t\t.map(conn -> conn.getPresence())\n\t\t\t\t\t.filter(Objects::nonNull)\n\t\t\t\t\t.filter(presence -> presence.getCDataStaticStr(Presence.PRESENCE_SHOW_PATH) == null)\n\t\t\t\t\t.findAny()\n\t\t\t\t\t.isPresent()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprotected Collection<Element> getPushServicesForAwayNotifications(XMPPResourceConnection session) {\n\t\tMap<String, Element> serviceSettings = pushNotifications.getPushServices(session);\n\t\tif (serviceSettings.isEmpty()) {\n\t\t\treturn Collections.EMPTY_LIST;\n\t\t}\n\n\t\treturn serviceSettings.values()\n\t\t\t\t.stream()\n\t\t\t\t.filter(el -> el.getAttribute(\"away\") != null)\n\t\t\t\t.collect(Collectors.toList());\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/push/EncryptedPushNotificationExtension.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.push;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Message;\nimport tigase.server.Packet;\nimport tigase.util.Base64;\nimport tigase.xml.Element;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.spec.GCMParameterSpec;\nimport javax.crypto.spec.SecretKeySpec;\nimport java.nio.ByteBuffer;\nimport java.nio.CharBuffer;\nimport java.nio.charset.Charset;\nimport java.nio.charset.CharsetEncoder;\nimport java.nio.charset.CoderResult;\nimport java.nio.charset.StandardCharsets;\nimport java.security.Key;\nimport java.security.SecureRandom;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Bean(name = \"encrypted\", parent = PushNotifications.class, active = true)\npublic class EncryptedPushNotificationExtension implements PushNotificationsExtension {\n\n\tprivate static final Logger log = Logger.getLogger(EncryptedPushNotificationExtension.class.getCanonicalName());\n\n\tpublic static final String XMLNS = \"tigase:push:encrypt:0\";\n\tprivate static final String AES128GCM_FEATURE = \"tigase:push:encrypt:aes-128-gcm\";\n\tprivate static final Charset UTF8 = Charset.forName(\"UTF-8\");\n\n\tprivate static final Element[] DISCO_FEATURES = new Element[]{\n\t\t\tnew Element(\"feature\", new String[]{\"var\"}, new String[]{XMLNS}),\n\t\t\tnew Element(\"feature\", new String[]{\"var\"}, new String[]{AES128GCM_FEATURE})};\n\n\tprivate final SecureRandom random = new SecureRandom();\n\t\n\t@ConfigField(desc = \"Notification to display for encrypted messages\", alias = \"encrypted-message-body\")\n\tprivate String encryptedMessageBody = \"New secure message. Open to see the message.\";\n\n\t@Override\n\tpublic Element[] getDiscoFeatures() {\n\t\treturn DISCO_FEATURES;\n\t}\n\n\t@Override\n\tpublic boolean shouldSendNotification(Packet packet, BareJID userJid, XMPPResourceConnection session)\n\t\t\tthrows XMPPException {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void processEnableElement(Element enableEl, Element settingsEl) {\n\t\tElement encryptEl = enableEl.getChild(\"encrypt\", XMLNS);\n\t\tif (encryptEl == null) {\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tsettingsEl.addChild(encryptEl);\n\t}\n\n\t@Override\n\tpublic void prepareNotificationPayload(Element pushServiceSettings, PushNotificationCause cause, Packet packet, long msgCount, Element notification) {\n\t\tElement encryptEl = pushServiceSettings.getChild(\"encrypt\", XMLNS);\n\t\tif (encryptEl == null) {\n\t\t\treturn;\n\t\t}\n\t\tString alg = encryptEl.getAttributeStaticStr(\"alg\");\n\t\t// default limit should be 4000 bytes as 4096 bytes is current limit for APNs and FCM\n\t\tlong maxSizeBytes = Optional.ofNullable(encryptEl.getAttributeStaticStr(\"max-size\"))\n\t\t\t\t.map(Integer::parseInt)\n\t\t\t\t.orElse(3000);\n\t\tString keyStr = encryptEl.getCData();\n\n\t\tif (alg == null || keyStr == null) {\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tif (!alg.equalsIgnoreCase(\"aes-128-gcm\")) {\n\t\t\treturn;\n\t\t}\n\t\t\n\t\tElement actionEl = null;\n\t\tMap<String, Object> payload = new HashMap<>();\n\t\tpayload.put(\"unread\", msgCount);\n\t\tif (packet != null) {\n\t\t\tpayload.put(\"sender\", packet.getStanzaFrom().getBareJID());\n\t\t\t\n\t\t\tactionEl = packet.getElement().findChild(el -> el.getXMLNS() == \"urn:xmpp:jingle-message:0\");\n\t\t\tif (packet.getElemName() == Message.ELEM_NAME) {\n\t\t\t\tif (packet.getType() == StanzaType.groupchat) {\n\t\t\t\t\tpayload.put(\"type\", \"groupchat\");\n\t\t\t\t\tElement mix = packet.getElement().getChild(\"mix\", \"urn:xmpp:mix:core:1\");\n\t\t\t\t\tif (mix != null) {\n\t\t\t\t\t\tElement nickEl = mix.getChild(\"nick\");\n\t\t\t\t\t\tif (nickEl != null) {\n\t\t\t\t\t\t\tString nickname = nickEl.getCData();\n\t\t\t\t\t\t\tif (nickname != null) {\n\t\t\t\t\t\t\t\tpayload.put(\"nickname\", nickname);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tString nickname = packet.getStanzaFrom().getResource();\n\t\t\t\t\t\tif (nickname != null) {\n\t\t\t\t\t\t\tpayload.put(\"nickname\", nickname);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (actionEl != null) {\n\t\t\t\t\tpayload.put(\"type\", \"call\");\n\t\t\t\t\tpayload.put(\"sender\", packet.getStanzaFrom());\n\t\t\t\t\tpayload.put(\"sid\", actionEl.getAttributeStaticStr(\"id\"));\n\t\t\t\t\tpayload.put(\"media\", actionEl.mapChildren(\n\t\t\t\t\t\t\tel -> el.getName() == \"description\" && el.getXMLNS() == \"urn:xmpp:jingle:apps:rtp:1\",\n\t\t\t\t\t\t\tel -> el.getAttributeStaticStr(\"media\")));\n\t\t\t\t\tpayload.put(\"features\", actionEl.mapChildren(\n\t\t\t\t\t\t\tel -> Objects.equals(\"feature\", el.getName()), Element::getXMLNS));\n\t\t\t\t} else {\n\t\t\t\t\tpayload.put(\"type\", \"chat\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tboolean isEncrypted = packet.getElemChild(\"encrypted\", \"eu.siacs.conversations.axolotl\") != null ||\n\t\t\t\t\tpacket.getElemChild(\"encrypted\", \"urn:xmpp:omemo:1\") != null;\n\t\t\tString body = isEncrypted ? encryptedMessageBody : packet.getElemCDataStaticStr(tigase.server.Message.MESSAGE_BODY_PATH);\n\t\t\tif (body != null) {\n\t\t\t\t// body is encrypted and base64 encoded so we need to adjust the size and reduce it by 64 (header size)\n\t\t\t\tint maxSize = (int) (((maxSizeBytes - 64) * 6) / 8);\n\t\t\t\tbody = trimBodyToSize(maxSize, body);\n\t\t\t\tpayload.put(\"message\", body);\n\t\t\t}\n\t\t} else {\n\t\t\tswitch (cause) {\n\t\t\t\tcase ACCOUNT_REMOVED -> {\n\t\t\t\t\tpayload.put(\"type\", \"account-removed\");\n\t\t\t\t}\n\t\t\t\tdefault -> {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tElement x = notification.getChild(\"x\", \"jabber:x:data\");\n\t\tif (x != null) {\n\t\t\tnotification.removeChild(x);\n\t\t}\n\n\t\t//String content = valueToString(payload);\n\t\t\n\t\tStringBuilder sb = new StringBuilder();\n\t\tvalueToString(payload, sb);\n\t\tString content = sb.toString();\n\t\ttry {\n\t\t\tKey key = new SecretKeySpec(Base64.decode(keyStr), \"AES\");\n\t\t\tbyte[] iv = new byte[12];\n\t\t\trandom.nextBytes(iv);\n\t\t\tGCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, iv);\n\n\t\t\tCipher cipher = Cipher.getInstance(\"AES/GCM/NoPadding\");\n\t\t\tcipher.init(Cipher.ENCRYPT_MODE, key, gcmParameterSpec);\n\n\t\t\tbyte[] data = cipher.doFinal(content.getBytes(UTF8));\n\n\t\t\tElement encryped = new Element(\"encrypted\", Base64.encode(data));\n\t\t\tif (actionEl != null) {\n\t\t\t\tencryped.setAttribute(\"type\", \"voip\");\n\t\t\t}\n\t\t\tencryped.addAttribute(\"iv\", Base64.encode(iv));\n\t\t\tencryped.setXMLNS(XMLNS);\n\t\t\tnotification.addChild(encryped);\n\t\t} catch (Throwable ex) {\n\t\t\tlog.log(Level.WARNING, \"Could not encode payload\", ex);\n\t\t}\n\t}\n\n\tpublic static String trimBodyToSize(int limit, String body) {\n\t\tCharsetEncoder enc = StandardCharsets.UTF_8.newEncoder();\n\t\tByteBuffer bb = ByteBuffer.allocate((int) limit);\n\t\tCharBuffer cb = CharBuffer.wrap(body);\n\t\tCoderResult r = enc.encode(cb, bb, true);\n\t\treturn r.isOverflow() ? cb.flip().toString() : body;\n\t}\n\n\tprotected static void valueToString(Object value, StringBuilder sb) {\n\t\tif (value instanceof Integer) {\n\t\t\tsb.append((int) value);\n\t\t} else if (value instanceof Double) {\n\t\t\tsb.append((double) value);\n\t\t} else if (value instanceof String) {\n\t\t\tescapeValue((String) value, sb);\n\t\t} else if (value instanceof List) {\n\t\t\tsb.append(\"[\");\n\t\t\tboolean first = true;\n\t\t\tfor (Object item : (List<Object>) value) {\n\t\t\t\tif (first) {\n\t\t\t\t\tfirst = false;\n\t\t\t\t} else {\n\t\t\t\t\tsb.append(\", \");\n\t\t\t\t}\n\t\t\t\tvalueToString(item, sb);\n\t\t\t}\n\t\t\tsb.append(\"]\");\n\t\t} else if (value instanceof Map) {\n\t\t\tsb.append(\"{\");\n\t\t\tboolean first = true;\n\t\t\tfor (Map.Entry e : ((Map<String,Object>) value).entrySet()) {\n\t\t\t\tif (first) {\n\t\t\t\t\tfirst = false;\n\t\t\t\t} else {\n\t\t\t\t\tsb.append(\", \");\n\t\t\t\t}\n\t\t\t\tsb.append(\"\\\"\").append(e.getKey()).append(\"\\\" : \");\n\t\t\t\tvalueToString(e.getValue(), sb);\n\t\t\t}\n\t\t\tsb.append(\"}\");\n\t\t} else {\n\t\t\tsb.append(\"null\");\n\t\t}\n\t}\n\n\tprivate static void escapeValue(String in, StringBuilder sb) {\n\t\tsb.append('\\\"');\n\t\tfor (char c : in.toCharArray()) {\n\t\t\tswitch (c) {\n\t\t\t\tcase '\\b':\n\t\t\t\t\tsb.append(\"\\\\b\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase '\\f':\n\t\t\t\t\tsb.append(\"\\\\f\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase '\\n':\n\t\t\t\t\tsb.append(\"\\\\n\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase '\\r':\n\t\t\t\t\tsb.append(\"\\\\r\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase '\\t':\n\t\t\t\t\tsb.append(\"\\\\t\");\n\t\t\t\t\tbreak;\n\t\t\t\tcase '\"':\n\t\t\t\tcase '\\\\':\n\t\t\t\tcase '/':\n\t\t\t\t\tsb.append(\"\\\\\");\n\t\t\t\tdefault:\n\t\t\t\t\tsb.append(c);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tsb.append('\\\"');\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/push/GroupchatFilter.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.push;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Message;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.List;\n\nimport static tigase.xmpp.impl.push.GroupchatFilter.ID;\n\n@Bean(name = ID, parent = PushNotifications.class, active = true)\npublic class GroupchatFilter\n\t\timplements PushNotificationsFilter {\n\n\tpublic static final String XMLNS = \"tigase:push:filter:groupchat:0\";\n\tpublic static final String ID = \"groupchat-filter\";\n\tprivate static final Element[] DISCO_FEATURES = { new Element(\"feature\", new String[]{\"var\"}, new String[]{XMLNS}) };\n\n\t@Override\n\tpublic Element[] getDiscoFeatures() {\n\t\treturn DISCO_FEATURES;\n\t}\n\n\t@Override\n\tpublic void processEnableElement(Element enableEl, Element settingsEl) {\n\t\tElement allowNotMentionedEl = enableEl.getChild(\"groupchat\", XMLNS);\n\t\tif (allowNotMentionedEl != null) {\n\t\t\tsettingsEl.addChild(allowNotMentionedEl);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isSendingNotificationAllowed(BareJID userJid, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t\tElement pushServiceSettings, Packet packet) {\n\t\tif (packet.getType() != StanzaType.groupchat) {\n\t\t\treturn true;\n\t\t}\n\n\t\tElement mucEl = pushServiceSettings.getChild(\"groupchat\", XMLNS);\n\t\tif (mucEl == null) {\n\t\t\treturn true;\n\t\t}\n\n\t\tList<Element> rooms = mucEl.getChildren();\n\t\tif (rooms == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (Element room : rooms) {\n\t\t\tString jidStr = room.getAttributeStaticStr(\"jid\");\n\t\t\tif (jidStr == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!packet.getStanzaFrom().getBareJID().toString().equals(jidStr)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tString allow = room.getAttributeStaticStr(\"allow\");\n\t\t\tif (allow == null) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tswitch (allow) {\n\t\t\t\tcase \"always\":\n\t\t\t\t\treturn true;\n\t\t\t\tcase \"mentioned\":\n\t\t\t\t\tString nick = room.getAttributeStaticStr(\"nick\");\n\t\t\t\t\tif (nick == null) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\treturn isMentioned(packet, nick);\n\t\t\t\tdefault:\n\t\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprotected boolean isMentioned(Packet packet, String nick) {\n\t\tString body = packet.getElemCDataStaticStr(Message.MESSAGE_BODY_PATH);\n\t\tif (body == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn body.contains(nick);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/push/GroupchatPushNotificationsExtension.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.push;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Message;\nimport tigase.server.Packet;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.impl.MessageDeliveryProviderIfc;\nimport tigase.xmpp.jid.BareJID;\n\n@Bean(name = \"groupchat\", parent = PushNotifications.class, active = true)\npublic class GroupchatPushNotificationsExtension implements PushNotificationsExtension {\n\n\t@Inject\n\tprotected MessageDeliveryProviderIfc message;\n\n\t@Override\n\tpublic boolean shouldSendNotification(Packet packet, BareJID userJid, XMPPResourceConnection session)\n\t\t\tthrows XMPPException {\n\t\tif (packet.getElemName() != Message.ELEM_NAME || packet.getType() != StanzaType.groupchat) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (session == null || !this.message.hasConnectionForMessageDelivery(session)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (packet.getStanzaTo() == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn session.isUserId(packet.getStanzaTo().getBareJID()) && packet.getStanzaTo().getResource() == null &&\n\t\t\t\tpacket.getElement().findChild(el -> el.getName() == \"mix\" && el.getXMLNS() == \"urn:xmpp:mix:core:1\") ==\n\t\t\t\t\t\tnull && packet.getElemChild(\"body\") != null;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/push/IgnoreFromUnknownFilter.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.push;\n\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserRepository;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.impl.roster.RosterAbstract;\nimport tigase.xmpp.impl.roster.RosterElement;\nimport tigase.xmpp.impl.roster.RosterFactory;\nimport tigase.xmpp.impl.roster.RosterFlat;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.xmpp.impl.push.IgnoreFromUnknownFilter.ID;\n\n@Bean(name = ID, parent = PushNotifications.class, active = true)\npublic class IgnoreFromUnknownFilter\n\t\timplements PushNotificationsFilter {\n\n\tpublic static final String XMLNS = \"tigase:push:filter:ignore-unknown:0\";\n\t\n\tprivate static final Logger log = Logger.getLogger(IgnoreFromUnknownFilter.class.getCanonicalName());\n\n\tpublic static final String ID = \"ignore-from-unknown\";\n\n\tprivate static final Element[] DISCO_FEATURES = { new Element(\"feature\", new String[]{\"var\"}, new String[]{XMLNS}) };\n\n\tprotected final RosterAbstract roster_util = RosterFactory.getRosterImplementation(true);\n\n\t@Inject\n\tprivate UserRepository userRepository;\n\n\t@Override\n\tpublic Element[] getDiscoFeatures() {\n\t\treturn DISCO_FEATURES;\n\t}\n\n\t@Override\n\tpublic void processEnableElement(Element enableEl, Element settingsEl) {\n\t\tif (enableEl.getChild(\"ignore-unknown\", XMLNS) != null) {\n\t\t\tsettingsEl.addAttribute(ID, \"true\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isSendingNotificationAllowed(BareJID userJid, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t\tElement pushServiceSettings, Packet packet) {\n\t\tif (Boolean.valueOf(pushServiceSettings.getAttributeStaticStr(ID)) && packet.getType() != StanzaType.groupchat) {\n\t\t\treturn isInRoster(packet, session);\n\t\t}\n\t\treturn true;\n\t}\n\n\tprotected boolean isInRoster(Packet packet, XMPPResourceConnection session) {\n\t\ttry {\n\t\t\tif (session != null && session.isAuthorized()) {\n\t\t\t\tRosterElement rosterElement = roster_util.getRosterElement(session, packet.getStanzaFrom());\n\t\t\t\tif (rosterElement == null) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn rosterElement.getSubscription() != RosterAbstract.SubscriptionType.none_pending_in;\n\t\t\t}\n\n\t\t\tMap<BareJID, RosterElement> roster = getRoster(packet.getStanzaTo().getBareJID());\n\n\t\t\tif (roster != null) {\n\t\t\t\tRosterElement rosterElement = roster.get(packet.getStanzaFrom().getBareJID());\n\t\t\t\tif (rosterElement == null) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn rosterElement.getSubscription() != RosterAbstract.SubscriptionType.none_pending_in;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t} catch (RepositoryException | NotAuthorizedException ex) {\n\t\t\tlog.log(Level.WARNING, \"Could not retrieve roster for user \" + packet.getStanzaTo(), ex);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprotected Map<BareJID, RosterElement> getRoster(BareJID jid) throws TigaseDBException {\n\t\tString tmp = userRepository.getData(jid, \"roster\");\n\t\tMap<BareJID, RosterElement> roster = new HashMap<BareJID, RosterElement>();\n\t\tif (tmp != null) {\n\t\t\tRosterFlat.parseRosterUtil(tmp, roster, null);\n\t\t}\n\t\treturn roster;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/push/JinglePushNotificationsExtension.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.push;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Message;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\nimport tigase.xmpp.XMPPException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.Objects;\nimport java.util.Optional;\n\n@Bean(name = \"jingle\", parent = PushNotifications.class, active = true)\npublic class JinglePushNotificationsExtension implements PushNotificationsExtension, PushNotificationsFilter, PushNotificationsAware {\n\n\tprivate static final Element[] FEATURES = {\n\t\t\tnew Element(\"feature\", new String[]{\"var\"}, new String[]{\"tigase:push:jingle:0\"})};\n\n\t@Inject\n\tprivate EncryptedPushNotificationExtension encryptedPushNotificationExtension;\n\n\t@ConfigField(desc = \"Always enable\", alias = \"push-jingle-always-enabled\")\n\tprivate boolean alwaysEnabled = false;\n\n\t@Override\n\tpublic Element[] getDiscoFeatures() {\n\t\treturn FEATURES;\n\t}\n\n\t@Override\n\tpublic void processEnableElement(Element enableEl, Element settingsEl) {\n\t\tElement jingle = enableEl.getChild(\"jingle\", \"tigase:push:jingle:0\");\n\t\tif (jingle != null) {\n\t\t\tsettingsEl.addChild(jingle);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void prepareNotificationPayload(Element pushServiceSettings, Packet packet, long msgCount,\n\t\t\t\t\t\t\t\t\t\t   Element notification) {\n\t\tif (packet == null || packet.getElemName() != Message.ELEM_NAME) {\n\t\t\treturn;\n\t\t}\n\t\tElement actionEl = packet.getElement().findChild(el -> el.getXMLNS() == \"urn:xmpp:jingle-message:0\");\n\t\tif (actionEl == null) {\n\t\t\treturn;\n\t\t}\n\t\tString id = actionEl.getAttributeStaticStr(\"id\");\n\t\tif (id == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tnotification.withElement(\"jingle\", \"tigase:push:jingle:0\", jingle -> {\n\t\t\tjingle.addAttribute(\"action\", actionEl.getName());\n\t\t\tjingle.addAttribute(\"sid\", id);\n\t\t\tOptional.ofNullable(actionEl.mapChildren(el -> Objects.equals(el.getName(), \"description\") && Objects.equals(el.getXMLNS(), \"urn:xmpp:jingle:apps:rtp:1\"),\n\t\t\t\t\t\t\t\t el -> el.getAttributeStaticStr(\"media\")))\n\t\t\t\t\t.ifPresent(mediaTypes -> mediaTypes.forEach(media -> jingle.withElement(\"media\", null, media)));\n\t\t\tOptional.ofNullable(actionEl.mapChildren(el -> Objects.equals(el.getName(), \"feature\"), Element::getXMLNS))\n\t\t\t\t\t.ifPresent(features -> features.forEach(\n\t\t\t\t\t\t\tfeature -> jingle.withElement(\"feature\", el -> el.setXMLNS(feature))));\n\t\t});\n\t}\n\n\t@Override\n\tpublic boolean shouldSendNotification(Packet packet, BareJID userJid, XMPPResourceConnection session)\n\t\t\tthrows XMPPException {\n\t\tif (packet.getElemName() != Message.ELEM_NAME) {\n\t\t\treturn false;\n\t\t}\n\t\tElement actionEl = packet.getElement().findChild(el -> el.getXMLNS() == \"urn:xmpp:jingle-message:0\");\n\t\tif (actionEl == null || actionEl.getAttributeStaticStr(\"id\") == null) {\n\t\t\treturn false;\n\t\t}\n\t\tswitch (actionEl.getName()) {\n\t\t\tcase \"retract\":\n\t\t\t\treturn false;\n\t\t\tcase \"propose\":\n\t\t\t\treturn packet.getStanzaFrom() != null && !packet.getStanzaFrom().getBareJID().equals(userJid);\n\t\t\tdefault:\n\t\t\t\treturn packet.getStanzaFrom() != null && session != null && session.isUserId(packet.getStanzaFrom().getBareJID());\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isSendingNotificationAllowed(BareJID userJid, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t\tElement pushServiceSettings, Packet packet) {\n\t\tElement actionEl = packet.getElement().findChild(el -> el.getXMLNS() == \"urn:xmpp:jingle-message:0\");\n\t\tif (actionEl == null) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (alwaysEnabled) {\n\t\t\treturn true;\n\t\t}\n\t\t\n\t\tElement jingle = pushServiceSettings.getChild(\"jingle\", \"tigase:push:jingle:0\");\n\t\tif (jingle == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\t\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/push/MeetPushNotificationExtension.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.push;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.server.Message;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\nimport tigase.xmpp.XMPPException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.Optional;\n\n@Bean(name = \"meet\", parent = PushNotifications.class, active = true)\npublic class MeetPushNotificationExtension implements PushNotificationsExtension, PushNotificationsFilter {\n\n\t@ConfigField(desc = \"Always enable\", alias = \"push-meet-always-enabled\")\n\tprivate boolean alwaysEnabled = false;\n\n\t@Override\n\tpublic void processEnableElement(Element enableEl, Element settingsEl) {\n\t\tElement meet = enableEl.getChild(\"meet\", \"tigase:push:meet:0\");\n\t\tif (meet != null) {\n\t\t\tsettingsEl.addChild(meet);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void prepareNotificationPayload(Element pushServiceSettings, Packet packet, long msgCount,\n\t\t\t\t\t\t\t\t\t\t   Element notification) {\n\t\tif (packet == null || packet.getElemName() != Message.ELEM_NAME) {\n\t\t\treturn;\n\t\t}\n\t\tElement actionEl = packet.getElement().findChild(el -> el.getXMLNS() == \"tigase:meet:0\");\n\t\tif (actionEl == null) {\n\t\t\treturn;\n\t\t}\n\t\tString id = actionEl.getAttributeStaticStr(\"id\");\n\t\tif (id == null) {\n\t\t\treturn;\n\t\t}\n\t\tString jid = actionEl.getAttributeStaticStr(\"jid\");\n\t\tif (jid == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tnotification.withElement(\"meet\", \"tigase:push:meet:0\", meet -> {\n\t\t\tmeet.addAttribute(\"id\", id);\n\t\t\tmeet.addAttribute(\"jid\", jid);\n\t\t\tOptional.ofNullable(actionEl.mapChildren(el -> el.getName() == \"media\",\n\t\t\t\t\t\t\t\t el -> el.getAttributeStaticStr(\"type\")))\n\t\t\t\t\t.ifPresent(mediaTypes -> mediaTypes.forEach(media -> meet.withElement(\"media\", null, media)));\n\t\t});\n\t}\n\n\t@Override\n\tpublic boolean shouldSendNotification(Packet packet, BareJID userJid, XMPPResourceConnection session)\n\t\t\tthrows XMPPException {\n\t\tif (packet.getElemName() != Message.ELEM_NAME) {\n\t\t\treturn false;\n\t\t}\n\t\tElement actionEl = packet.getElement().findChild(el -> el.getXMLNS() == \"tigase:meet:0\");\n\t\tif (actionEl == null || actionEl.getAttributeStaticStr(\"id\") == null) {\n\t\t\treturn false;\n\t\t}\n\t\tswitch (actionEl.getName()) {\n\t\t\tcase \"propose\":\n\t\t\t\treturn packet.getStanzaFrom() != null && !packet.getStanzaFrom().getBareJID().equals(userJid);\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isSendingNotificationAllowed(BareJID userJid, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t\tElement pushServiceSettings, Packet packet) {\n\t\tElement actionEl = packet.getElement().findChild(el -> el.getXMLNS() == \"tigase:meet:0\");\n\t\tif (actionEl == null) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (alwaysEnabled) {\n\t\t\treturn true;\n\t\t}\n\n\t\tElement meet = pushServiceSettings.getChild(\"meet\", \"tigase:push:meet:0\");\n\t\tif (meet == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/push/MutedFilter.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.push;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.List;\n\nimport static tigase.xmpp.impl.push.MutedFilter.ID;\n\n@Bean(name = ID, parent = PushNotifications.class, active = true)\npublic class MutedFilter implements PushNotificationsFilter {\n\n\tpublic static final String XMLNS = \"tigase:push:filter:muted:0\";\n\tpublic static final String ID = \"muted-filter\";\n\tprivate static final Element[] DISCO_FEATURES = { new Element(\"feature\", new String[]{\"var\"}, new String[]{XMLNS}) };\n\n\t@Override\n\tpublic Element[] getDiscoFeatures() {\n\t\treturn DISCO_FEATURES;\n\t}\n\n\t@Override\n\tpublic void processEnableElement(Element enableEl, Element settingsEl) {\n\t\tElement el = enableEl.getChild(\"muted\", XMLNS);\n\t\tif (el != null) {\n\t\t\tsettingsEl.addChild(el);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isSendingNotificationAllowed(BareJID userJid, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t\tElement pushServiceSettings, Packet packet) {\n\t\tElement mutedEl = pushServiceSettings.getChild(\"muted\", XMLNS);\n\t\tif (mutedEl == null) {\n\t\t\treturn true;\n\t\t}\n\t\tList<BareJID> list = mutedEl.mapChildren(child -> child.getAttributeStaticStr(\"jid\") != null,\n\t\t\t\t\t\t\t\t\t\t\t\t child -> BareJID.bareJIDInstanceNS(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t child.getAttributeStaticStr(\"jid\")));\n\t\tif (list == null) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn !list.contains(packet.getStanzaFrom().getBareJID());\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/push/PriorityExtension.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.push;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\nimport tigase.xmpp.XMPPException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\n\n@Bean(name = \"priority\", parent = PushNotifications.class, active = true)\npublic class PriorityExtension implements PushNotificationsExtension {\n\n\tprivate static final String XMLNS = \"tigase:push:priority:0\";\n\n\tprivate static final Element[] DISCO_FEATURES = { new Element(\"feature\", new String[]{\"var\"}, new String[]{XMLNS}) };\n\n\t@Override\n\tpublic Element[] getDiscoFeatures() {\n\t\treturn DISCO_FEATURES;\n\t}\n\n\t@Override\n\tpublic boolean shouldSendNotification(Packet packet, BareJID userJid, XMPPResourceConnection session)\n\t\t\tthrows XMPPException {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void processEnableElement(Element enableEl, Element settingsEl) {\n\t\tif (enableEl.getChild(\"priority\", XMLNS) != null) {\n\t\t\tsettingsEl.addAttribute(\"priority\", \"true\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void prepareNotificationPayload(Element pushServiceSettings, PushNotificationCause cause, Packet packet, long msgCount,\n\t\t\t\t\t\t\t\t\t\t   Element notification) {\n\t\tboolean supportsPriority = Boolean.parseBoolean(pushServiceSettings.getAttributeStaticStr(\"priority\"));\n\t\tboolean hasBody = packet != null && packet.getElemChild(\"body\") != null;\n\t\tboolean hasJinglePropose = packet != null && packet.getElemChild(\"propose\", \"urn:xmpp:jingle-message:0\") != null;\n\t\tboolean hasMeetPropose = packet != null && packet.getElemChild(\"propose\", \"tigase:meet:0\") != null;\n\t\tif (supportsPriority) {\n\t\t\tElement element = new Element(\"priority\", (hasBody || hasJinglePropose || hasMeetPropose) ? \"high\" : \"low\");\n\t\t\telement.setXMLNS(XMLNS);\n\t\t\tnotification.addChild(element);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/push/PushNotificationCause.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.push;\n\npublic enum PushNotificationCause {\n\tSTANZA,\n\tMESSAGES_FETCHED,\n\tACCOUNT_REMOVED\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/push/PushNotifications.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.push;\n\nimport tigase.component.adhoc.AdHocCommand;\nimport tigase.component.adhoc.AdHocCommandException;\nimport tigase.component.adhoc.AdHocResponse;\nimport tigase.component.adhoc.AdhHocRequest;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserNotFoundException;\nimport tigase.db.UserRepository;\nimport tigase.eventbus.HandleEvent;\nimport tigase.form.Field;\nimport tigase.form.Form;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.RegistrarBean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Iq;\nimport tigase.server.Message;\nimport tigase.server.Packet;\nimport tigase.server.amp.db.MsgRepository;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.OfflineMessages;\nimport tigase.xmpp.impl.annotation.DiscoFeatures;\nimport tigase.xmpp.impl.annotation.Handle;\nimport tigase.xmpp.impl.annotation.Handles;\nimport tigase.xmpp.impl.annotation.Id;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.function.Consumer;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Stream;\n\nimport static tigase.xmpp.impl.push.PushNotifications.XMLNS;\n\n/**\n * Created by andrzej on 30.12.2016.\n */\n@Bean(name = PushNotifications.ID, parent = SessionManager.class, active = true, exportable = true)\n@Id(PushNotifications.ID)\n@DiscoFeatures({PushNotifications.ID})\n@Handles({@Handle(path = {Iq.ELEM_NAME, \"enable\"}, xmlns = XMLNS),\n\t\t  @Handle(path = {Iq.ELEM_NAME, \"disable\"}, xmlns = XMLNS),\n\t\t  @Handle(path = {Message.ELEM_NAME}, xmlns = Message.CLIENT_XMLNS)})\npublic class PushNotifications\n\t\textends AbstractPushNotifications\n\t\timplements XMPPProcessorIfc, OfflineMessages.Notifier, RegistrarBean {\n\n\tprivate static final Logger log = Logger.getLogger(PushNotifications.class.getCanonicalName());\n\n\tprivate Element[] discoFeatures = new Element[0];\n\n\t@Inject\n\tprivate ArrayList<PushNotificationsAware> awares = new ArrayList<>();\n\t@Inject\n\tprivate ArrayList<PushNotificationsExtension> triggers = new ArrayList<>();\n\t@Inject(nullAllowed = true)\n\tprivate ArrayList<PushNotificationsFilter> filters = new ArrayList<>();\n\t@ConfigField(desc = \"Send offline messages retrieved notification\", alias = \"send-offline-messages-retrieved-notification\")\n\tprivate boolean sendOfflineMessagesRetrievedNotification = true;\n\t@ConfigField(desc = \"Send account removal notification\", alias = \"send-account-removal-notification\")\n\tprivate boolean sendAccountRemovalNotification = false;\n\n\t@Override\n\tpublic Element[] supDiscoFeatures(XMPPResourceConnection session) {\n\t\treturn discoFeatures;\n\t}\n\n\tpublic void setAwares(ArrayList<PushNotificationsAware> awares) {\n\t\tthis.awares = awares;\n\t}\n\n\tpublic void setFilter(ArrayList<PushNotificationsFilter> filters) {\n\t\tthis.filters = Optional.ofNullable(filters).orElseGet(ArrayList::new);\n\t\trefreshDiscoFeatures();\n\t}\n\n\tpublic void setTriggers(ArrayList<PushNotificationsExtension> triggers) {\n\t\tthis.triggers = triggers;\n\t\trefreshDiscoFeatures();\n\t}\n\n\tprotected void refreshDiscoFeatures() {\n\t\tthis.discoFeatures = Stream.concat(Arrays.stream(super.supDiscoFeatures(null)),\n\t\t\t\t\t\t\t\t\t\t   awares.stream().map(PushNotificationsAware::getDiscoFeatures).flatMap(Arrays::stream)).toArray(Element[]::new);\n\t}\n\n\t@Override\n\tpublic void process(Packet packet, XMPPResourceConnection session, NonAuthUserRepository nonAuthUserRepository,\n\t\t\t\t\t\tQueue<Packet> results, Map<String, Object> map) throws XMPPException {\n\t\ttry {\n\t\t\tif (packet.getElemName() == Message.ELEM_NAME) {\n\t\t\t\tprocessMessage(packet, session, results::offer);\n\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\tsuper.process(packet, session, nonAuthUserRepository, results, map);\n\t\t\t}\n\t\t} catch (NotAuthorizedException ex) {\n\t\t\tresults.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet, \"Session is not authorized\", true));\n\t\t} catch (TigaseDBException ex) {\n\t\t\tresults.offer(Authorization.INTERNAL_SERVER_ERROR.getResponseMessage(packet, null, true));\n\t\t}\n\t}\n\n\t@Override\n\tprotected void processMessage(Packet packet, XMPPResourceConnection session, Consumer<Packet> consumer)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tsuper.processMessage(packet, session, consumer);\n\n\t\tif (session == null || !session.isAuthorized() || !shouldSendNotification(packet, session.getBareJID(), session)) {\n\t\t\treturn;\n\t\t}\n\t\tsendPushNotification(session, PushNotificationCause.STANZA, packet, consumer);\n\t}\n\n\t@Override\n\tpublic void notifyNewOfflineMessage(Packet packet, XMPPResourceConnection session, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\tMap<String, Object> map) {\n\t\tif (packet.getElemName() != tigase.server.Message.ELEM_NAME) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!shouldSendNotification(packet, packet.getStanzaTo().getBareJID(), session)) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tsendPushNotification(session, PushNotificationCause.MESSAGES_FETCHED, packet, results::offer);\n\t\t} catch (UserNotFoundException ex) {\n\t\t\tlog.log(Level.FINEST, \"Could not send push notification for message \" + packet, ex);\n\t\t} catch (TigaseDBException ex) {\n\t\t\tlog.log(Level.WARNING, \"Could not send push notification for message \" + packet, ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void notifyOfflineMessagesRetrieved(XMPPResourceConnection session, Queue<Packet> results) {\n\t\ttry {\n\t\t\tBareJID userJid = session.getBareJID();\n\t\t\tMap<String, Element> pushServices = getPushServices(userJid);\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Offline messages retrieved push notifications for JID: {0}, pushServices: {1}\",\n\t\t\t\t        new Object[]{userJid, pushServices});\n\t\t\t}\n\t\t\tif (pushServices.isEmpty()) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tnotifyOfflineMessagesRetrieved(userJid, pushServices.values(), results::offer);\n\t\t} catch (UserNotFoundException | NotAuthorizedException ex) {\n\t\t\tlog.log(Level.FINEST, \"Could not send push notification about offline message retrieval by \" + session, ex);\n\t\t} catch (TigaseDBException ex) {\n\t\t\tlog.log(Level.WARNING, \"Could not send push notification about offline message retrieval by \" + session,\n\t\t\t\t\tex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\t\t\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\n\t}\n\n\t@HandleEvent(filter = HandleEvent.Type.local, sync = true)\n\tpublic void onUserRemoved(UserRepository.UserBeforeRemovedEvent event) {\n\t\ttry {\n\t\t\tif (!sendAccountRemovalNotification) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tMap<String,Element> pushServices = getPushServices(event.getJid());\n\t\t\tif (pushServices.isEmpty()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\n\t\t\tsendPushNotification(event.getJid(), pushServices.values(), null, PushNotificationCause.ACCOUNT_REMOVED,\n\t\t\t\t\t\t\t\t null, Map.of(), consumer -> {\n\t\t\t\t\t});\n\t\t} catch (Throwable ex) {\n\t\t\tlog.log(Level.WARNING, \"Could not get push services for \" + event.getJid(), ex);\n\t\t}\n\t}\n\t\n\t@Override\n\tprotected Element createSettingsElement(JID jid, String node, Element enableElem, Element optionsForm) {\n\t\tElement settingsEl = super.createSettingsElement(jid, node, enableElem, optionsForm);\n\t\tString name = enableElem.getAttributeStaticStr(\"name\");\n\t\tif (name != null && !name.isBlank()) {\n\t\t\tsettingsEl.setAttribute(\"name\", name);\n\t\t}\n\t\tfor (PushNotificationsAware trigger : awares) {\n\t\t\ttrigger.processEnableElement(enableElem, settingsEl);\n\t\t}\n\t\treturn settingsEl;\n\t}\n\n\tprotected void notifyOfflineMessagesRetrieved(BareJID userJid, Collection<Element> pushServices, Consumer<Packet> packetConsumer) {\n\t\tif (!sendOfflineMessagesRetrievedNotification) {\n\t\t\treturn;\n\t\t}\n\t\tMap<Enum, Long> map = new HashMap<>();\n\t\tmap.put(MsgRepository.MSG_TYPES.message, 0l);\n\t\tsendPushNotification(userJid, pushServices, null, PushNotificationCause.MESSAGES_FETCHED, null, map,\n\t\t\t\t\t\t\t packetConsumer);\n\t}\n\n\t@Override\n\tprotected Element prepareNotificationPayload(Element pushServiceSettings, PushNotificationCause cause, Packet packet, long msgCount) {\n\t\tElement notification = super.prepareNotificationPayload(pushServiceSettings, cause, packet, msgCount);\n\t\tfor (PushNotificationsExtension trigger : triggers) {\n\t\t\ttrigger.prepareNotificationPayload(pushServiceSettings, cause, packet, msgCount, notification);\n\t\t}\n\t\treturn notification;\n\t}\n\n\t@Override\n\tprotected boolean isSendingNotificationAllowed(BareJID userJid, XMPPResourceConnection session, Element pushServiceSettings, Packet packet) {\n\t\tif (!super.isSendingNotificationAllowed(userJid, session, pushServiceSettings, packet)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (PushNotificationsFilter filter : filters) {\n\t\t\tif (!filter.isSendingNotificationAllowed(userJid, session, pushServiceSettings, packet)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t// move to filter\n\tprotected boolean shouldSendNotification(Packet packet, BareJID userJid, XMPPResourceConnection session) {\n\t\t// if packet was already stored in offline message repository, it shouldn't trigger notification\n\t\tElement delay = packet.getElemChild(\"delay\", \"urn:xmpp:delay\");\n\t\tif (delay != null && Objects.equals(userJid.getDomain(), delay.getAttributeStaticStr(\"from\"))) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (session == null && packet.getElemName() == Message.ELEM_NAME && packet.getElemChild(\"body\") != null) {\n\t\t\treturn true;\n\t\t}\n\n\t\tfor (PushNotificationsExtension trigger : triggers) {\n\t\t\ttry {\n\t\t\t\tif (trigger.shouldSendNotification(packet, userJid, session)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t} catch (XMPPException ex) {\n\t\t\t\tlog.log(Level.FINER, \"exception while checking if trigger \" + trigger.getClass().getCanonicalName() +\n\t\t\t\t\t\t\" should be fired\", ex);\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprotected static abstract class AbstractAdhocCommand implements AdHocCommand {\n\n\t\tprivate final String node;\n\t\tprivate final String name;\n\n\t\t@Inject\n\t\tprivate SessionManager component;\n\t\t@Inject\n\t\tprivate AbstractPushNotifications pushNotifications;\n\n\t\tprotected AbstractAdhocCommand(String node, String name) {\n\t\t\tthis.node = node;\n\t\t\tthis.name = name;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn name;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getNode() {\n\t\t\treturn node;\n\t\t}\n\n\t\t@Override\n\t\tpublic void execute(AdhHocRequest request, AdHocResponse response) throws AdHocCommandException {\n\t\t\ttry {\n\t\t\t\tfinal Element data = request.getCommand().getChild(\"x\", \"jabber:x:data\");\n\n\t\t\t\tif (request.isAction(\"cancel\")) {\n\t\t\t\t\tresponse.cancelSession();\n\t\t\t\t} else {\n\t\t\t\t\tif (data == null) {\n\t\t\t\t\t\tresponse.getElements().add(prepareForm(request, response).getElement());\n\t\t\t\t\t\tresponse.startSession();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tForm form = new Form(data);\n\t\t\t\t\t\tif (form.isType(\"submit\")) {\n\t\t\t\t\t\t\tForm responseForm = submitForm(request, response, form);\n\t\t\t\t\t\t\tif (responseForm != null) {\n\t\t\t\t\t\t\t\tresponse.getElements().add(responseForm.getElement());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (AdHocCommandException ex) {\n\t\t\t\tthrow ex;\n\t\t\t} catch (Exception e) {\n\t\t\t\tlog.log(Level.FINE, \"Exception during execution of adhoc command \" + getNode(), e);\n\t\t\t\tthrow new AdHocCommandException(Authorization.INTERNAL_SERVER_ERROR, e.getMessage());\n\t\t\t}\n\t\t}\n\n\t\tprotected abstract Form prepareForm(AdhHocRequest request, AdHocResponse response) throws AdHocCommandException;\n\t\tprotected abstract Form submitForm(AdhHocRequest request, AdHocResponse response, Form form)\n\t\t\t\tthrows AdHocCommandException;\n\n\t\tprotected boolean isEmpty(String input) {\n\t\t\treturn input == null || input.isBlank();\n\t\t}\n\n\t\tprotected String assertNotEmpty(String input, String message) throws AdHocCommandException {\n\t\t\tif (isEmpty(input)) {\n\t\t\t\tthrow new AdHocCommandException(Authorization.BAD_REQUEST, message);\n\t\t\t}\n\t\t\treturn input.trim();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isAllowedFor(JID jid) {\n\t\t\treturn component.isAdmin(jid);\n\t\t}\n\n\t\tpublic SessionManager getComponent() {\n\t\t\treturn component;\n\t\t}\n\n\t\tpublic AbstractPushNotifications getPushNotifications() {\n\t\t\treturn pushNotifications;\n\t\t}\n\t}\n\n\t@Bean(name = \"push-list-devices\", parent = SessionManager.class, active = true)\n\tpublic static class ListDevicesAdhocCommand extends AbstractAdhocCommand {\n\n\t\tpublic ListDevicesAdhocCommand() {\n\t\t\tsuper(\"push-list-devices\", \"List push devices\");\n\t\t}\n\n\t\t@Override\n\t\tprotected Form prepareForm(AdhHocRequest request, AdHocResponse response) throws AdHocCommandException {\n\t\t\tForm form = new Form(\"form\", \"Unregister device\", \"Fill out and submit this form to list enabled devices with push notifications\");\n\t\t\tform.addField(Field.fieldJidSingle(\"userJid\", \"\", \"Account JID\"));\n\t\t\treturn form;\n\t\t}\n\n\t\t@Override\n\t\tprotected Form submitForm(AdhHocRequest request, AdHocResponse response, Form form)\n\t\t\t\tthrows AdHocCommandException {\n\t\t\tForm result = new Form(\"result\", \"List of push devices\", null);\n\t\t\ttry {\n\t\t\t\tBareJID accountJid = BareJID.bareJIDInstance(\n\t\t\t\t\t\tassertNotEmpty(form.getAsString(\"userJid\"), \"Account JID is required!\"));\n\t\t\t\tMap<String, Element> pushServices = getPushNotifications().getPushServices(accountJid);\n\t\t\t\tString[] deviceIds = pushServices.keySet().stream().sorted().toArray(String[]::new);\n\t\t\t\tresult.addField(Field.fieldTextMulti(\"deviceIds\", deviceIds, \"List of devices\"));\n\t\t\t\treturn result;\n\t\t\t} catch (TigaseStringprepException|TigaseDBException e) {\n\t\t\t\tthrow new RuntimeException(e);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Bean(name = \"push-unregister-device\", parent = SessionManager.class, active = true)\n\tpublic static class DisableDeviceAdHocCommand\n\t\t\textends AbstractAdhocCommand {\n\n\n\t\tpublic DisableDeviceAdHocCommand() {\n\t\t\tsuper(\"push-disable-device\", \"Disable push notifications\");\n\t\t}\n\n\t\tprotected Form prepareForm(AdhHocRequest request, AdHocResponse response) throws AdHocCommandException {\n\t\t\ttry {\n\t\t\t\treturn prepareForm(null);\n\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\tthrow new RuntimeException(ex);\n\t\t\t}\n\t\t}\n\n\t\tprotected Form submitForm(AdhHocRequest request, AdHocResponse response, Form form)\n\t\t\t\tthrows AdHocCommandException {\n\t\t\ttry {\n\t\t\t\tBareJID accountJid = BareJID.bareJIDInstance(assertNotEmpty(form.getAsString(\"userJid\"), \"Account JID is required!\"));\n\t\t\t\tString key = form.getAsString(\"deviceId\");\n\t\t\t\tif (isEmpty(key)) {\n\t\t\t\t\treturn prepareForm(accountJid);\n\t\t\t\t}\n\t\t\t\tint idx = key.indexOf('/');\n\t\t\t\tif (idx < 0) {\n\t\t\t\t\tthrow new RuntimeException(\"Invalid device ID: \" + key);\n\t\t\t\t}\n\t\t\t\tJID jid = JID.jidInstance(key.substring(0, idx));\n\t\t\t\tString node = key.substring(idx + 1);\n\t\t\t\tgetPushNotifications().disableNotifications(null, accountJid, jid, node, getComponent()::addOutPacket);\n\t\t\t\treturn null;\n\t\t\t} catch (TigaseStringprepException|TigaseDBException|NotAuthorizedException e) {\n\t\t\t\tthrow new RuntimeException(e);\n\t\t\t}\n\t\t}\n\n\t\tprotected Form prepareForm(BareJID accountJid) throws TigaseDBException {\n\t\t\tForm form = new Form(\"form\", \"Unregister device\", \"Fill out and submit this form to disable sending push notifications to selected device\");\n\t\t\tform.addField(Field.fieldJidSingle(\"userJid\", accountJid == null ? \"\" : accountJid.toString(), \"Account JID\"));\n\t\t\tif (accountJid != null) {\n\t\t\t\tMap<String, Element> pushServices = getPushNotifications().getPushServices(accountJid);\n\t\t\t\tList<Map.Entry<String,Element>> entries = pushServices.entrySet().stream().sorted(\n\t\t\t\t\t\tMap.Entry.comparingByKey()).toList();\n\t\t\t\tform.addField(Field.fieldListSingle(\"deviceId\", \"\", \"Device\", entries.stream()\n\t\t\t\t\t\t.map(Map.Entry::getValue)\n\t\t\t\t\t\t.map(settings -> Optional.ofNullable(settings.getAttributeStaticStr(\"name\"))\n\t\t\t\t\t\t\t\t.orElseGet(() -> settings.getAttributeStaticStr(\"jid\") + \" / \" +\n\t\t\t\t\t\t\t\t\t\tsettings.getAttributeStaticStr(\"node\")))\n\t\t\t\t\t\t.toArray(String[]::new), entries.stream().map(Map.Entry::getKey).toArray(String[]::new)));\n\t\t\t}\n\t\t\treturn form;\n\t\t}\n\t\t\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/push/PushNotificationsAware.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.push;\n\nimport tigase.xml.Element;\n\npublic interface PushNotificationsAware {\n\n\tdefault Element[] getDiscoFeatures() {\n\t\treturn new Element[0];\n\t}\n\t\n\tdefault void processEnableElement(Element enableEl, Element settingsEl) { }\n\t\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/push/PushNotificationsExtension.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.push;\n\nimport tigase.server.Packet;\nimport tigase.xml.Element;\nimport tigase.xmpp.XMPPException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\n\npublic interface PushNotificationsExtension extends PushNotificationsAware {\n\n\tboolean shouldSendNotification(Packet packet, BareJID userJid, XMPPResourceConnection session)\n\t\t\tthrows XMPPException;\n\t\n\tdefault void prepareNotificationPayload(Element pushServiceSettings, PushNotificationCause cause, Packet packet, long msgCount, Element notification) {\n\t\tswitch (cause) {\n\t\t\tcase ACCOUNT_REMOVED -> {}\n\t\t\tdefault -> prepareNotificationPayload(pushServiceSettings, packet, msgCount, notification);\n\t\t}\n\t}\n\t\n\tdefault void prepareNotificationPayload(Element pushServiceSettings, Packet packet, long msgCount, Element notification) {}\n\n\tdefault void setPushNotifications(PushNotifications pushNotifications) {}\n\t\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/push/PushNotificationsFilter.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.push;\n\nimport tigase.server.Packet;\nimport tigase.xml.Element;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\n\npublic interface PushNotificationsFilter extends PushNotificationsAware {\n\n\tboolean isSendingNotificationAllowed(BareJID userJid, XMPPResourceConnection session, Element pushServiceSettings, Packet packet);\n\t\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/push/PushPresence.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.push;\n\nimport tigase.cluster.strategy.ClusteringStrategyIfc;\nimport tigase.cluster.strategy.ConnectionRecordIfc;\nimport tigase.component.PacketWriter;\nimport tigase.db.AuthRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserRepository;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.HandleEvent;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.map.ClusterMapFactory;\nimport tigase.server.Packet;\nimport tigase.server.Presence;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.server.xmppsession.SessionManagerHandler;\nimport tigase.util.cache.LRUConcurrentCache;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.vhosts.VHostItem;\nimport tigase.vhosts.VHostItemImpl;\nimport tigase.xml.Element;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.XMPPSession;\nimport tigase.xmpp.impl.JabberIqPrivacy;\nimport tigase.xmpp.impl.annotation.AnnotatedXMPPProcessor;\nimport tigase.xmpp.impl.roster.RosterAbstract;\nimport tigase.xmpp.impl.roster.RosterFactory;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.concurrent.CopyOnWriteArraySet;\nimport java.util.function.Consumer;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Bean(name = \"push-presence\", parent = SessionManager.class, active = false)\npublic class PushPresence\n\t\textends AnnotatedXMPPProcessor\n\t\timplements Initializable, UnregisterAware {\n\n\tprivate static final Logger log = Logger.getLogger(PushPresence.class.getCanonicalName());\n\t\n\tenum PresenceStatus {\n\t\tchat,\n\t\tavailable,\n\t\taway,\n\t\txa,\n\t\tdnd,\n\t\tunavailable\n\t}\n\n\t@ConfigField(desc = \"Presence show value for accounts with push devices\")\n\tprivate PresenceStatus presenceStatus = PresenceStatus.xa;\n\t@ConfigField(desc = \"Push presence resource name\")\n\tprivate String resourceName = \"push\";\n\t@Inject\n\tprivate AuthRepository authRepository;\n\t@Inject\n\tprivate UserRepository userRepository;\n\t@Inject\n\tprivate EventBus eventBus;\n\t@Inject\n\tprivate AbstractPushNotifications pushNotifications;\n\t@Inject(bean = \"sess-man\")\n\tprivate SessionManagerHandler sessionManager;\n\t@Inject\n\tprivate PacketWriter packetWriter;\n\t@Inject(nullAllowed = true)\n\tprivate ClusteringStrategyIfc<ConnectionRecordIfc> strategy;\n\tprivate Map<BareJID,Boolean> pushAvailability;\n\tprivate final JID offlineConnectionId = JID.jidInstanceNS(\"push-offline-conn\", DNSResolverFactory.getInstance().getDefaultHost());\n\tprivate final LRUConcurrentCache<BareJID, Set<BareJID>> rosterSubscribedFromCache = new LRUConcurrentCache<>(10000);\n\tprivate final SessionManagerHandler offlineSessionManagerHandler = new SessionManagerHandler() {\n\t\t@Override\n\t\tpublic JID getComponentId() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleLogin(BareJID userId, XMPPResourceConnection conn) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleDomainChange(String domain, XMPPResourceConnection conn) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleLogout(BareJID userId, XMPPResourceConnection conn) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void handlePresenceSet(XMPPResourceConnection conn) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleResourceBind(XMPPResourceConnection conn) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isLocalDomain(String domain, boolean includeComponents) {\n\t\t\treturn false;\n\t\t}\n\t};\n\n\tprivate RosterAbstract rosterUtil = RosterFactory.getRosterImplementation(true);\n\n\tpublic PushPresence() {\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tthis.pushAvailability = ClusterMapFactory.get().createMap(\"push-availability\", BareJID.class, Boolean.class);\n\t\tthis.pushNotifications.setPushDevicesPresence(this);\n\t\tif (eventBus != null) {\n\t\t\teventBus.registerAll(this);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\tif (eventBus != null) {\n\t\t\teventBus.unregisterAll(this);\n\t\t}\n\t}\n\n\tprotected void setRosterUtil(RosterAbstract rosterUtil) {\n\t\tthis.rosterUtil = rosterUtil;\n\t}\n\n\tpublic EventBus getEventBus() {\n\t\treturn eventBus;\n\t}\n\n\tpublic void setEventBus(EventBus eventBus) {\n\t\tthis.eventBus = eventBus;\n\t}\n\t\n\tprotected boolean isPushAvailable(BareJID userJid) throws TigaseDBException {\n\t\tBoolean value = pushAvailability.get(userJid);\n\t\tif (value == null) {\n\t\t\tvalue = !pushNotifications.getPushServices(userJid).isEmpty();\n\t\t\tpushAvailability.put(userJid, value);\n\t\t}\n\t\treturn value;\n\t}\n\t\n\tprivate boolean shouldNodeGeneratePresence(BareJID userJid) {\n\t\tif (strategy == null) {\n\t\t\treturn true;\n\t\t}\n\n\t\tSet<ConnectionRecordIfc> records = strategy.getConnectionRecords(userJid);\n\t\tif (records != null) {\n\t\t\tList<String> nodes = new ArrayList<>();\n\t\t\t// if we are executing, either our node has user session or there is no user session on any nodes\n\t\t\tnodes.add(sessionManager.getComponentId().getDomain());\n\n\t\t\t// we know nodes that may have user session\n\t\t\tfor (ConnectionRecordIfc record : records) {\n\t\t\t\tString node = record.getNode().getDomain();\n\t\t\t\tif (!nodes.contains(node)) {\n\t\t\t\t\tnodes.add(node);\n\t\t\t\t}\n\t\t\t}\n\t\t\tnodes.sort(String::compareTo);\n\t\t\t\n\t\t\tString selectedNode = nodes.get(userJid.hashCode() % nodes.size());\n\t\t\treturn sessionManager.getComponentId().getDomain().equals(selectedNode);\n\t\t}\n\t\t// If records were null, we have no idea which nodes will be able to handle request as each node may\n\t\t// or may not have a user session. We are executing assuming that we should handle request supposing\n\t\t// that see-other-host will reconnect all connections to the same node\n\t\treturn true;\n\t}\n\n\tpublic void processPresenceToOffline(JID recipient, JID sender, StanzaType stanzaType, Consumer<Packet> packetConsumer) {\n\t\tif (stanzaType != StanzaType.probe) {\n\t\t\treturn;\n\t\t}\n\t\tprocessPresenceProbe(recipient, sender, packetConsumer);\n\t}\n\n\tpublic void processPresenceProbe(JID recipient, JID sender, Consumer<Packet> packetConsumer) {\n\t\tif (recipient == null || recipient.getResource() != null || sender == null) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tif (!isPushAvailable(recipient.getBareJID())) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!shouldNodeGeneratePresence(recipient.getBareJID())) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!getSubscribedWithFrom(recipient.getBareJID()).contains(sender.getBareJID())) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsendPresenceFormPushDevices(recipient.getBareJID(), true, List.of(sender.getBareJID()), packetConsumer);\n\t\t} catch (TigaseDBException ex) {\n\t\t\tlog.log(Level.FINEST, \"failed to fetch push devices for jid \" + recipient.getBareJID(), ex);\n\t\t}\n\t}\n\n\tprivate Packet createPresenceForPushDevices(boolean hasPushDevices) throws TigaseStringprepException {\n\t\tElement presenceEl = new Element(\"presence\", new Element[]{new Element(\"priority\", \"-1\"),}, null, null);\n\t\tpresenceEl.setXMLNS(Presence.CLIENT_XMLNS);\n\t\tif (!hasPushDevices) {\n\t\t\tpresenceEl.setAttribute(\"type\", StanzaType.unavailable.toString());\n\t\t} else {\n\t\t\tswitch (presenceStatus) {\n\t\t\t\tcase unavailable -> presenceEl.setAttribute(\"type\", StanzaType.unavailable.name());\n\t\t\t\tcase available -> {}\n\t\t\t\tdefault -> presenceEl.withElement(\"show\", null, presenceStatus.name());\n\t\t\t}\n\t\t}\n\t\treturn Packet.packetInstance(presenceEl);\n\t}\n\n\tprivate void sendPresenceFormPushDevices(BareJID userJid, boolean hasPushDevices, Collection<BareJID> recipients, Consumer<Packet> packetConsumer){\n\t\ttry {\n\t\t\tJID from = JID.jidInstance(userJid, resourceName);\n\t\t\tPacket presence = createPresenceForPushDevices(hasPushDevices);\n\t\t\tfor (BareJID jid : recipients) {\n\t\t\t\tPacket clone = presence.copyElementOnly();\n\t\t\t\tclone.initVars(from, JID.jidInstance(jid));\n\t\t\t\tpacketConsumer.accept(clone);\n\t\t\t}\n\t\t} catch (TigaseStringprepException ex) {\n\t\t\tlog.log(Level.FINEST, \"failed to prepare JID for push presence\", ex);\n\t\t}\n\t}\n\n\tpublic void broadcastPresenceFromPushDevices(BareJID userJid, boolean hasPushDevices, Consumer<Packet> packetConsumer) {\n\t\tsendPresenceFormPushDevices(userJid, hasPushDevices, getSubscribedWithFrom(userJid), packetConsumer);\n\t}\n\t\n\tpublic void pushAvailabilityChanged(BareJID userJid, boolean newValue, Consumer<Packet> packetConsumer) {\n\t\tpushAvailability.put(userJid, newValue);\n\t\tbroadcastPresenceFromPushDevices(userJid, newValue, packetConsumer);\n\t}\n\n\tprivate Set<BareJID> getSubscribedWithFrom(BareJID userJid) {\n\t\tSet<BareJID> jids = rosterSubscribedFromCache.get(userJid);\n\t\tif (jids == null) {\n\t\t\tjids = new CopyOnWriteArraySet<>();\n\t\t\trosterSubscribedFromCache.put(userJid, jids);\n\t\t\tsynchronized (jids) {\n\t\t\t\tXMPPResourceConnection session = createOfflineXMPPResourceConnection(userJid);\n\t\t\t\ttry {\n\t\t\t\t\tJID[] buddies = rosterUtil.getBuddies(session, RosterAbstract.FROM_SUBSCRIBED);\n\t\t\t\t\tif (buddies != null) {\n\t\t\t\t\t\tfor (JID buddy : buddies) {\n\t\t\t\t\t\t\tjids.add(buddy.getBareJID());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (NotAuthorizedException | TigaseDBException ex) {\n\t\t\t\t\tlog.log(Level.FINEST, \"failed to fetch buddies subscribed 'from' for jid \" + userJid, ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn jids;\n\t}\n\n\t@HandleEvent\n\tpublic void handleRosterModified(RosterAbstract.RosterModifiedEvent event) {\n\t\tBareJID userJid = event.getUserJid().getBareJID();\n\t\tif (!shouldNodeGeneratePresence(userJid)) {\n\t\t\treturn;\n\t\t}\n\t\t\n\t\ttry {\n\t\t\tif (isPushAvailable(userJid)) {\n\t\t\t\tSet<BareJID> jids = rosterSubscribedFromCache.get(userJid);\n\t\t\t\tswitch (event.getSubscription()) {\n\t\t\t\t\tcase from:\n\t\t\t\t\tcase from_pending_out:\n\t\t\t\t\tcase both:\n\t\t\t\t\t\tif (jids != null) {\n\t\t\t\t\t\t\tjids.add(event.getJid().getBareJID());\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsendPresenceFormPushDevices(userJid, true, List.of(event.getJid().getBareJID()),\n\t\t\t\t\t\t\t\t\t\t\t\t\tpacketWriter::write);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase to:\n\t\t\t\t\tcase none:\n\t\t\t\t\t\tif (jids != null) {\n\t\t\t\t\t\t\tjids.remove(event.getJid().getBareJID());\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsendPresenceFormPushDevices(userJid, false, List.of(event.getJid().getBareJID()),\n\t\t\t\t\t\t\t\t\t\t\t\t\tpacketWriter::write);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (TigaseDBException ex) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"failed to check for registered push devices for user \" + userJid, ex);\n\t\t\t}\n\t\t}\n\t}\n\t\n\tprivate XMPPResourceConnection createOfflineXMPPResourceConnection(BareJID userJid) {\n\t\ttry {\n\t\t\tXMPPResourceConnection session = new JabberIqPrivacy.OfflineResourceConnection(offlineConnectionId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   userRepository,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   authRepository, offlineSessionManagerHandler);\n\t\t\tVHostItem vhost = new VHostItemImpl(userJid.getDomain());\n\t\t\tsession.setDomain(vhost);\n\t\t\tsession.authorizeJID(userJid, false);\n\t\t\tXMPPSession parentSession = new XMPPSession(userJid.getLocalpart());\n\t\t\tsession.setParentSession(parentSession);\n\t\t\treturn session;\n\t\t} catch (TigaseStringprepException ex) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"creation of temporary session for offline user \" + userJid + \" failed\", ex);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\t}\n\t\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/roster/AnonymousRoster.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.roster;\n\nimport tigase.xml.Element;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.impl.PresenceState;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n//import tigase.xmpp.impl.Roster;\n//import tigase.xmpp.impl.Presence;\n\n/**\n * Describe class AnonymousRoster here.\n * <br>\n * Created: Tue Apr 22 21:41:46 2008\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class AnonymousRoster\n\t\timplements DynamicRosterIfc {\n\n\t@Override\n\tpublic void init(Map<String, Object> props) {\n\t}\n\n\t@Override\n\tpublic void init(String par) {\n\t}\n\n\t@SuppressWarnings({\"unchecked\"})\n\t@Override\n\tpublic JID[] getBuddies(XMPPResourceConnection session) throws NotAuthorizedException {\n\t\tif (session.isAnonymous()) {\n\t\t\tSet<JID> direct_presences = (Set<JID>) session.getSessionData(PresenceState.DIRECT_PRESENCE);\n\t\t\tif (direct_presences != null) {\n\t\t\t\tJID[] result = new JID[direct_presences.size()];\n\t\t\t\tint i = 0;\n\t\t\t\tfor (JID peer : direct_presences) {\n\t\t\t\t\tresult[i++] = peer;\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic Element getBuddyItem(XMPPResourceConnection session, JID buddy) throws NotAuthorizedException {\n\t\tif (session.isAnonymous()) {\n\t\t\tJID[] anon_peers = getBuddies(session);\n\t\t\tif (anon_peers != null) {\n\t\t\t\tfor (JID peer : anon_peers) {\n\t\t\t\t\tif (peer.getBareJID().equals(buddy.getBareJID())) {\n\t\t\t\t\t\tElement item = new Element(\"item\", new Element[]{new Element(\"group\", \"Anonymous peers\")},\n\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{\"jid\", \"subscription\", \"name\"},\n\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{peer.toString(), \"both\", peer.getLocalpart()});\n\t\t\t\t\t\treturn item;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic List<Element> getRosterItems(XMPPResourceConnection session) throws NotAuthorizedException {\n\t\tif (session.isAnonymous()) {\n\t\t\tJID[] anon_peers = getBuddies(session);\n\t\t\tif (anon_peers != null) {\n\t\t\t\tArrayList<Element> al = new ArrayList<Element>();\n\t\t\t\tfor (JID peer : anon_peers) {\n\t\t\t\t\tElement item = new Element(\"item\", new Element[]{new Element(\"group\", \"Anonymous peers\")},\n\t\t\t\t\t\t\t\t\t\t\t   new String[]{\"jid\", \"subscription\", \"name\"},\n\t\t\t\t\t\t\t\t\t\t\t   new String[]{peer.toString(), \"both\", peer.getLocalpart()});\n\t\t\t\t\tal.add(item);\n\t\t\t\t}\n\t\t\t\treturn al;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void setItemExtraData(Element item) {\n\t}\n\n\t@Override\n\tpublic Element getItemExtraData(Element item) {\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/roster/DynamicRoster.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.roster;\n\nimport tigase.kernel.beans.*;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.impl.roster.RosterAbstract.SubscriptionType;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Describe class DynamicRoster here.\n * <br>\n * Created: Tue Nov 6 11:28:10 2007\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\n@Bean(name = \"dynamic-rosters\", parent = SessionManager.class, active = false)\npublic class DynamicRoster\n\t\timplements RegistrarBean, Initializable, UnregisterAware {\n\n\tprivate static final EnumSet<SubscriptionType> subs = EnumSet.noneOf(SubscriptionType.class);\n\tprivate static DynamicRoster instance;\n\tprivate static Logger log = Logger.getLogger(\"tigase.xmpp.impl.DynamicRoster\");\n\t@Inject(nullAllowed = true)\n\tprivate DynamicRosterIfc[] dynamicRosters;\n\tpublic static JID[] addBuddies(final XMPPResourceConnection session, final Map<String, Object> settings,\n\t\t\t\t\t\t\t\t   JID[] buddies)\n\t\t\tthrows NotAuthorizedException, RosterRetrievingException, RepositoryAccessException {\n\t\treturn addBuddies(session, settings, buddies, subs);\n\t}\n\n\tpublic static JID[] addBuddies(final XMPPResourceConnection session, final Map<String, Object> settings,\n\t\t\t\t\t\t\t\t   JID[] buddies, final EnumSet<SubscriptionType> subscrs)\n\t\t\tthrows NotAuthorizedException, RosterRetrievingException, RepositoryAccessException {\n\t\tList<JID> result = getBuddiesList(session, settings);\n\n\t\tif (result != null && subscrs != null && !subscrs.isEmpty()) {\n\t\t\tfinal Iterator<JID> iterator = result.iterator();\n\t\t\twhile (iterator.hasNext()) {\n\t\t\t\tfinal JID jid = iterator.next();\n\t\t\t\tElement buddy = getBuddyItem(session, settings, jid);\n\t\t\t\tString sub = null;\n\t\t\t\tif (buddy != null && ((sub = buddy.getAttributeStaticStr(\"subscription\")) != null)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tfinal SubscriptionType subType = SubscriptionType.valueOf(sub.toLowerCase());\n\t\t\t\t\t\tif (!subscrs.contains(subType)) {\n\t\t\t\t\t\t\titerator.remove();\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (IllegalArgumentException e) {\n\t\t\t\t\t\tlog.log(Level.FINEST, \"Illegal dynamic roster element, skipping\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (buddies != null) {\n\t\t\tif (result == null) {\n\t\t\t\tresult = new ArrayList<JID>();\n\t\t\t}\n\t\t\taddBuddiesToList(result, buddies);\n\t\t}\n\t\tif ((result != null) && (result.size() > 0)) {\n\t\t\treturn result.toArray(new JID[result.size()]);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic static void addBuddiesToList(List<JID> list, JID[] buddies) {\n\t\tfor (JID buddy : buddies) {\n\t\t\tif (!list.contains(buddy)) {\n\t\t\t\tlist.add(buddy);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic static JID[] getBuddies(final XMPPResourceConnection session, final Map<String, Object> settings)\n\t\t\tthrows NotAuthorizedException, RosterRetrievingException, RepositoryAccessException {\n\t\tList<JID> result = getBuddiesList(session, settings);\n\n\t\tif ((result != null) && (result.size() > 0)) {\n\t\t\treturn result.toArray(new JID[result.size()]);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic static List<JID> getBuddiesList(final XMPPResourceConnection session, final Map<String, Object> settings)\n\t\t\tthrows NotAuthorizedException, RosterRetrievingException, RepositoryAccessException {\n\t\tDynamicRosterIfc[] dynr = getDynamicRosters(settings);\n\n\t\tif (dynr != null) {\n\t\t\tArrayList<JID> result = new ArrayList<JID>();\n\n\t\t\tfor (DynamicRosterIfc dri : dynr) {\n\t\t\t\tJID[] buddies = dri.getBuddies(session);\n\n\t\t\t\tif (buddies != null) {\n\t\t\t\t\taddBuddiesToList(result, buddies);\n\n\t\t\t\t\t// result.addAll(Arrays.asList(buddies));\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (result.size() > 0) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic static Element getBuddyItem(final XMPPResourceConnection session, final Map<String, Object> settings,\n\t\t\t\t\t\t\t\t\t   JID buddy)\n\t\t\tthrows NotAuthorizedException, RosterRetrievingException, RepositoryAccessException {\n\t\tDynamicRosterIfc[] dynr = getDynamicRosters(settings);\n\n\t\tif (dynr != null) {\n\t\t\tfor (DynamicRosterIfc dri : dynr) {\n\t\t\t\tElement item = dri.getBuddyItem(session, buddy);\n\n\t\t\t\tif (item != null) {\n\t\t\t\t\treturn item;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic static DynamicRosterIfc[] getDynamicRosters(Map<String, Object> settings) {\n\t\treturn instance == null ? null : instance.dynamicRosters;\n\t}\n\n\tpublic static Element getItemExtraData(XMPPResourceConnection session, Map<String, Object> settings, Element item) {\n\t\tDynamicRosterIfc[] dynr = getDynamicRosters(settings);\n\n\t\tif (dynr != null) {\n\t\t\tElement result = null;\n\n\t\t\tfor (DynamicRosterIfc dri : dynr) {\n\t\t\t\tif ((result = dri.getItemExtraData(item)) != null) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic static List<Element> getRosterItems(final XMPPResourceConnection session, final Map<String, Object> settings)\n\t\t\tthrows NotAuthorizedException, RosterRetrievingException, RepositoryAccessException {\n\t\tDynamicRosterIfc[] dynr = getDynamicRosters(settings);\n\n\t\tif (dynr != null) {\n\t\t\tArrayList<Element> result = new ArrayList<Element>();\n\n\t\t\tfor (DynamicRosterIfc dri : dynr) {\n\t\t\t\tList<Element> items = dri.getRosterItems(session);\n\n\t\t\t\tif (items != null) {\n\t\t\t\t\tresult.addAll(items);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (result.size() > 0) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic static void setItemExtraData(XMPPResourceConnection session, Map<String, Object> settings, Element item) {\n\t\tDynamicRosterIfc[] dynr = getDynamicRosters(settings);\n\n\t\tif (dynr != null) {\n\t\t\tfor (DynamicRosterIfc dri : dynr) {\n\t\t\t\tdri.setItemExtraData(item);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\n\t}\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\tif (instance == this) {\n\t\t\tinstance = null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void initialize() {\n\t\tinstance = this;\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/roster/DynamicRosterIfc.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.roster;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.xml.Element;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Interface <code>DynamicRosterIfc</code> is to dynamically generate user roster entries when the user requests the\n * roster content. The dynamic roster feature doesn't replace the normal roster with entries added by the user. It just\n * allows you to inject extra contacts lists to the user roster. <br> There is a very simple example implementing this\n * interface which creates roster entries for anonymous users - <code>tigase.xmpp.impl.AnonymousRoster</code>. You can\n * use it as a starting point for your code. <br> You can have as many implementations of this interface loaded at the\n * same time as you need and all of them are called for each user roster request. <br> To load your implementations you\n * have to specify them in the configuration file. The simplest way is to use <code>config.tdsl</code> file. Have a look\n * at the example file available in the repository. Following line added to the end of the file tell the server to load\n * the dynamic roster implementation:\n * <pre>sess-man/plugins-conf/dynamic-roster-classes=tigase.xmpp.impl.AnonymousRoster</pre>\n * If you want to load more implementations you just put a comma separated list of classes instead. If your\n * implementation needs to connect to a database or any other resource or just needs extra configuration parameters you\n * can also specify them in the properties file:\n * <pre>sess-man/plugins-conf/dynamic-roster-classes=tigase.xmpp.impl.AnonymousRoster\n * sess-man/plugins-conf/dbinit=jdbc:jtds:mysql://localhost/roster-db;user=user-name;password=db-passwd\n * sess-man/plugins-conf/max-buddies=1000\n * </pre>\n * Basically all parameters starting with string: <code>sess-man/plugins-conf/</code> will be provided at initialization\n * time in the <code>init(....)</code> method. <br> There is also a simplified form for providing configuration\n * parameters. It is used if you want to provide just one parameter to the object (like database connection string):\n * <pre>sess-man/plugins-conf/tigase.xmpp.impl.AnonymousRoster.init=configuration-string</pre>\n * <br> Created: Mon Oct 29 08:52:22 2007\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\npublic interface DynamicRosterIfc {\n\n\t/**\n\t * <code>init</code> method is used to provide configuration parameters and initialize the object. Please have a\n\t * look at the interface description for more details about configuration parameters. The object is never used\n\t * before it's <code>init(...)</code> method is called but it might be used straight away after the method has\n\t * finished.\n\t *\n\t * @param props a {@code Map<String, Object> props} is a configuration parameters map in the form: key:value exactly\n\t * as they were specified in the configuration file.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(note = \"No longer used, use Tigase Kernel and @ConfigField annotation\", since = \"8.1.0\")\n\tvoid init(Map<String, Object> props);\n\n\t/**\n\t * <code>init</code> method is called at the initialization time when simple form of startup parameters are used:\n\t * <pre>sess-man/plugins-conf/class-name.init=configuration-string</pre>\n\t * The <code>configuration-string</code> is passed to this <code>init(...)</code> method in exact form as it was\n\t * found in the configuration file.\n\t *\n\t * @param par a <code>String</code> value of the configuration string.\n\t */\n\t@Deprecated\n\t@TigaseDeprecated(note = \"No longer used, use Tigase Kernel and @ConfigField annotation\", since = \"8.1.0\")\n\tvoid init(String par);\n\n\t/**\n\t * <code>getBuddies</code> method returns <code>String</code> array with all roster buddies JIDs. Normally they are\n\t * bare JIDs (without resource part). This method is normally used by presence plugin to send probe and initial\n\t * presence to all contacts in the dynamic roster.\n\t *\n\t * @param session a <code>XMPPResourceConnection</code> value of the connection session object.\n\t *\n\t * @return a <code>String[]</code> array of bare JIDs for the dynamic part of the user roster.\n\t *\n\t * @throws NotAuthorizedException may be thrown if the connection session is not yet authenticated but authorization\n\t * is required to access roster data.\n\t * @throws RosterRetrievingException may be thrown when an unknown error in the custom roster retrieving logic\n\t * occurs. A message from the exception must be sent back to a user as an error message.\n\t * @throws RepositoryAccessException may be thrown when there is an error accessing the roster data repository, even\n\t * though the user is correctly authenticated. No error is sent back to a user, only an empty roster but the\n\t * repository exception is logged to the log file.\n\t */\n\tJID[] getBuddies(XMPPResourceConnection session)\n\t\t\tthrows NotAuthorizedException, RosterRetrievingException, RepositoryAccessException;\n\n\t/**\n\t * <code>getBuddyItem</code> method returns buddy item element for a given JID or <code>null</code> if the buddy\n\t * doesn't exist on the user roster list.\n\t *\n\t * @param session a <code>XMPPResourceConnection</code> value of the connection session object.\n\t * @param buddy a <code>String</code> value of the buddy JID. It may be bare JID or full JID.\n\t *\n\t * @return an <code>Element</code> value of the XML element with all the roster item data - JID, subscription, nick\n\t * name and groups. Sample code for creating the buddy element could look like this:\n\t * <pre>Element item = new Element(\"item\", new Element[] {\n\t *     new Element(\"group\", \"Tigase devs\")},\n\t *  new String[] {\"jid\", \"subscription\", \"name\"},\n\t *  new String[] {peer, \"both\", JIDUtils.getNodeNick(peer)});</pre>\n\t *\n\t * @throws NotAuthorizedException may be thrown if the connection session is not yet authenticated but authorization\n\t * is required to access roster data.\n\t * @throws RosterRetrievingException may be thrown when an unknown error in the custom roster retrieving logic\n\t * occurs. A message from the exception must be sent back to a user as an error message.\n\t * @throws RepositoryAccessException may be thrown when there is an error accessing the roster data repository, even\n\t * though the user is correctly authenticated. No error is sent back to a user, only an empty roster but the\n\t * repository exception is logged to the log file.\n\t */\n\tElement getBuddyItem(XMPPResourceConnection session, JID buddy)\n\t\t\tthrows NotAuthorizedException, RosterRetrievingException, RepositoryAccessException;\n\n\t/**\n\t * Returns a new roster Item element with additional, non-standard information for a given item. This is a way to\n\t * associate custom roster information with a contact.\n\t *\n\t * @param item is a <code>Element</code>\n\t *\n\t * @return a value of <code>Element</code>\n\t */\n\tElement getItemExtraData(Element item);\n\n\t/**\n\t * <code>getRosterItems</code> method returns a full list with all buddies generated by this dynamic roster\n\t * implementation. The list contains all contacts for the roster with all contacts details - buddy JID, nick name,\n\t * subscription (typically always both) and groups. Please have a look at <code>getBuddyItem(...)</code> description\n\t * for details how to create an Element entry for the roster item. <br> In theory you could here call the\n\t * <code>getBuddies(...)</code> method and then for each entry from the array call the\n\t * <code>getBuddyItem(...)</code>. I strongly advice to not do it. This is a server with thousands of connected\n\t * users and possibly thousands of packets going through the server. Think of a performance and execute database\n\t * query once if possible rather then many times.\n\t *\n\t * @param session a <code>XMPPResourceConnection</code> value of the connection session object.\n\t *\n\t * @return a {@code List<Element>} value\n\t *\n\t * @throws NotAuthorizedException may be thrown if the connection session is not yet authenticated but authorization\n\t * is required to access roster data.\n\t * @throws RosterRetrievingException may be thrown when an unknown error in the custom roster retrieving logic\n\t * occurs. A message from the exception must be sent back to a user as an error message.\n\t * @throws RepositoryAccessException may be thrown when there is an error accessing the roster data repository, even\n\t * though the user is correctly authenticated. No error is sent back to a user, only an empty roster but the\n\t * repository exception is logged to the log file.\n\t */\n\tList<Element> getRosterItems(XMPPResourceConnection session)\n\t\t\tthrows NotAuthorizedException, RosterRetrievingException, RepositoryAccessException;\n\n\tvoid setItemExtraData(Element item);\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/roster/DynamicRosterTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.roster;\n\nimport tigase.xml.Element;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created: Nov 28, 2008 10:27:55 PM\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic class DynamicRosterTest\n\t\timplements DynamicRosterIfc {\n\n\tprivate static Logger log = Logger.getLogger(DynamicRosterTest.class.getName());\n\n\tprivate Map<String, Element> memStorage = new LinkedHashMap<String, Element>();\n\n\t@Override\n\tpublic JID[] getBuddies(XMPPResourceConnection session) throws NotAuthorizedException {\n\t\treturn new JID[]{JID.jidInstanceNS(\"dynrost@test-d\")};\n\t}\n\n\t@Override\n\tpublic Element getBuddyItem(XMPPResourceConnection session, JID buddy) throws NotAuthorizedException {\n\t\tif (\"dynrost@test-d\".equals(buddy.getBareJID().toString())) {\n\t\t\treturn getBuddy();\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t@Override\n\tpublic Element getItemExtraData(Element item) {\n\t\tString jid = item.getAttributeStaticStr(\"jid\");\n\t\tElement result = memStorage.get(jid);\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Retrieving item: {0}, for jid={1}\", new Object[]{result, jid});\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic List<Element> getRosterItems(XMPPResourceConnection session) throws NotAuthorizedException {\n\t\treturn new ArrayList<Element>(Arrays.asList(getBuddy()));\n\t}\n\n\t@Override\n\tpublic void init(Map<String, Object> props) {\n\t}\n\n\t@Override\n\tpublic void init(String par) {\n\t}\n\n\t@Override\n\tpublic void setItemExtraData(Element item) {\n\t\tString jid = item.getAttributeStaticStr(\"jid\");\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Storing item: {0}, for jid={1}\", new Object[]{item, jid});\n\t\t}\n\t\tmemStorage.put(jid, item);\n\t}\n\n\tprivate Element getBuddy() {\n\t\treturn new Element(\"item\", new Element[]{new Element(\"group\", \"test group\")},\n\t\t\t\t\t\t   new String[]{\"jid\", \"name\", \"subscription\"},\n\t\t\t\t\t\t   new String[]{\"dynrost@test-d\", \"dynrost\", \"both\"});\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/roster/DynamicRosterTest123.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.roster;\n\nimport tigase.xml.Element;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author Artur Hefczyc Created Apr 1, 2011\n */\npublic class DynamicRosterTest123\n\t\timplements DynamicRosterIfc {\n\n\tprivate static final String[] buddy_names = {\"test1\", \"test2\", \"test3\"};\n\tprivate static final String[] managers = {\"manager1\"};\n\n\t@Override\n\tpublic void setItemExtraData(Element item) {\n\t\t// TODO Auto-generated method stub\n\n\t}\n\n\t@Override\n\tpublic Element getItemExtraData(Element item) {\n\t\t// TODO Auto-generated method stub\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void init(Map<String, Object> props) {\n\t\t// TODO Auto-generated method stub\n\n\t}\n\n\t@Override\n\tpublic void init(String par) {\n\t\t// TODO Auto-generated method stub\n\n\t}\n\n\t@Override\n\tpublic JID[] getBuddies(XMPPResourceConnection session) throws NotAuthorizedException {\n\t\tString domain = session.getDomain().getVhost().getDomain();\n\t\tArrayList<JID> result = new ArrayList<JID>(buddy_names.length);\n\t\tfor (String name : buddy_names) {\n\t\t\tif (!name.equals(session.getUserName())) {\n\t\t\t\tresult.add(JID.jidInstanceNS(name, domain, null));\n\t\t\t}\n\t\t}\n\t\tfor (String name : managers) {\n\t\t\tif (!name.equals(session.getUserName())) {\n\t\t\t\tresult.add(JID.jidInstanceNS(name, domain, null));\n\t\t\t}\n\t\t}\n\n\t\treturn result.toArray(new JID[result.size()]);\n\t}\n\n\t@Override\n\tpublic Element getBuddyItem(XMPPResourceConnection session, JID buddy) throws NotAuthorizedException {\n\t\treturn new Element(\"item\", new Element[]{new Element(\"group\", \"test group\")},\n\t\t\t\t\t\t   new String[]{\"jid\", \"name\", \"subscription\"},\n\t\t\t\t\t\t   new String[]{buddy.getBareJID().toString(), buddy.getLocalpart(),\n\t\t\t\t\t\t\t\t\t\t(buddy.toString().contains(\"manager\") ? \"from\" : \"to\")});\n\n\t}\n\n\t@Override\n\tpublic List<Element> getRosterItems(XMPPResourceConnection session) throws NotAuthorizedException {\n\t\tArrayList<Element> result = new ArrayList<Element>(buddy_names.length);\n\t\tfor (JID buddy : getBuddies(session)) {\n\t\t\tresult.add(getBuddyItem(session, buddy));\n\t\t}\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/roster/RepositoryAccessException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.roster;\n\nimport tigase.xmpp.XMPPException;\n\n/**\n * @author kobit\n */\npublic class RepositoryAccessException\n\t\textends XMPPException {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * Creates a new <code>PacketErrorTypeException</code> instance.\n\t *\n\t */\n\tpublic RepositoryAccessException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic RepositoryAccessException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/roster/RosterAbstract.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.roster;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserRepository;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.HandleEvent;\nimport tigase.server.Packet;\nimport tigase.server.PolicyViolationException;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.server.xmppsession.UserSessionEventWithProcessorResultWriter;\nimport tigase.util.Algorithms;\nimport tigase.xml.Element;\nimport tigase.xml.XMLUtils;\nimport tigase.xmpp.*;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Stream;\n\n/**\n * Describe class RosterAbstract here.\n * <br>\n * Created: Thu Sep 4 18:09:52 2008\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\npublic abstract class RosterAbstract {\n\n\tpublic static final String CLIENT_XMLNS = \"jabber:client\";\n\n\tpublic static final String GROUP = \"group\";\n\n\tpublic static final String GROUPS = \"groups\";\n\n\tpublic static final String NAME = \"name\";\n\n\tpublic static final String ROSTER = \"roster\";\n\n\tpublic static final String ROSTER_XMLNS = \"jabber:iq:roster\";\n\n\tpublic static final String ROSTERHASH = \"rosterhash\";\n\n\tpublic static final String SUBSCRIPTION = \"subscription\";\n\n\tpublic static final String VER_ATT = \"ver\";\n\n\tpublic static final String XMLNS = \"jabber:iq:roster\";\n\n\tpublic static final String XMLNS_DYNAMIC = \"jabber:iq:roster-dynamic\";\n\n\tpublic static final String XMLNS_LOAD = XMLNS + \"-load\";\n\n\t// ~--- static fields --------------------------------------------------------\n\tpublic static final EnumSet<SubscriptionType> TO_SUBSCRIBED = EnumSet.of(SubscriptionType.to,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t SubscriptionType.to_pending_in,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t SubscriptionType.both,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t SubscriptionType.to_pre_approved);\n\tpublic static final EnumSet<SubscriptionType> SUB_TO = EnumSet.of(SubscriptionType.to,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  SubscriptionType.to_pending_in,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  SubscriptionType.to_pre_approved);\n\tpublic static final EnumSet<SubscriptionType> SUB_NONE = EnumSet.of(SubscriptionType.none,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSubscriptionType.none_pending_out,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSubscriptionType.none_pending_in,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSubscriptionType.none_pending_out_in,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSubscriptionType.none_pending_out_pre_approved);\n\tpublic static final EnumSet<SubscriptionType> SUB_FROM = EnumSet.of(SubscriptionType.from,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSubscriptionType.from_pending_out);\n\tpublic static final EnumSet<SubscriptionType> SUB_BOTH = EnumSet.of(SubscriptionType.both);\n\tpublic static final EnumSet<SubscriptionType> PENDING_OUT = EnumSet.of(SubscriptionType.none_pending_out,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   SubscriptionType.none_pending_out_in,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   SubscriptionType.from_pending_out,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   SubscriptionType.none_pending_out_pre_approved);\n\tpublic static final EnumSet<SubscriptionType> PENDING_IN = EnumSet.of(SubscriptionType.none_pending_in,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  SubscriptionType.none_pending_out_in,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  SubscriptionType.to_pending_in);\n\tpublic static final EnumSet<StanzaType> INITIAL_PRESENCES = EnumSet.of(StanzaType.available,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   StanzaType.unavailable);\n\t/** Holds all {link @SubscriptionType} elements that can be perceived as <em>FROM</em> subscription */\n\tpublic static final EnumSet<SubscriptionType> FROM_SUBSCRIBED = EnumSet.of(SubscriptionType.from,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   SubscriptionType.from_pending_out,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   SubscriptionType.both);\n\t/** Holds all {link @SubscriptionType} that are pre-approved subscriptions on the contact's side */\n\tpublic static final EnumSet<SubscriptionType> PRE_APPROVED = EnumSet.of(SubscriptionType.none_pre_approved,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSubscriptionType.none_pending_out_pre_approved,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSubscriptionType.to_pre_approved);\n\tpublic static final Element[] FEATURES = {\n\t\t\tnew Element(\"ver\", new String[]{\"xmlns\"}, new String[]{\"urn:xmpp:features:rosterver\"}),\n\t\t\tnew Element(\"sub\", new String[]{\"xmlns\"}, new String[]{\"urn:xmpp:features:pre-approval\"})};\n\tpublic static final Element[] DISCO_FEATURES = {new Element(\"feature\", new String[]{\"var\"}, new String[]{XMLNS}),\n\t\t\t\t\t\t\t\t\t\t\t\t\tnew Element(\"feature\", new String[]{\"var\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew String[]{XMLNS_DYNAMIC})};\n\t/**\n\t * Private logger for class instances.\n\t */\n\tprivate static final Logger log = Logger.getLogger(RosterAbstract.class.getName());\n\tpublic enum PresenceType {\n\t\tout_initial,\n\t\tout_subscribe,\n\t\tout_unsubscribe,\n\t\tout_subscribed,\n\t\tout_unsubscribed,\n\t\tout_probe,\n\t\tin_initial,\n\t\tin_subscribe,\n\t\tin_unsubscribe,\n\t\tin_subscribed,\n\t\tin_unsubscribed,\n\t\tin_probe,\n\t\terror;\n\t}\n\n\tpublic enum StateTransition {\n\t\tnone(SubscriptionType.none_pre_approved,                              // Table 1.\n\t\t\t SubscriptionType.none,                                       // Table 2.\n\t\t\t SubscriptionType.none_pending_in,                            // Table 3.\n\t\t\t SubscriptionType.none,                                       // Table 4.\n\t\t\t SubscriptionType.none,                                       // Table 5.\n\t\t\t SubscriptionType.none,                                       // Table 6.\n\t\t\t SubscriptionType.none_pending_out,                           // Table 7.\n\t\t\t SubscriptionType.none                                        // Table 8.\n\t\t),\n\t\tnone_pending_out(SubscriptionType.none_pending_out_pre_approved,  // Table 1.\n\t\t\t\t\t\t SubscriptionType.none_pending_out,                           // Table 2.\n\t\t\t\t\t\t SubscriptionType.none_pending_out_in,                        // Table 3.\n\t\t\t\t\t\t SubscriptionType.none_pending_out,                           // Table 4.\n\t\t\t\t\t\t SubscriptionType.to,                                         // Table 5.\n\t\t\t\t\t\t SubscriptionType.none,                                       // Table 6.\n\t\t\t\t\t\t SubscriptionType.none_pending_out,                           // Table 7.\n\t\t\t\t\t\t SubscriptionType.none                                        // Table 8.\n\t\t),\n\t\tnone_pending_in(SubscriptionType.from,                    // Table 1.\n\t\t\t\t\t\tSubscriptionType.none,                                       // Table 2.\n\t\t\t\t\t\tSubscriptionType.none_pending_in,                            // Table 3.\n\t\t\t\t\t\tSubscriptionType.none,                                       // Table 4.\n\t\t\t\t\t\tSubscriptionType.none_pending_in,                            // Table 5.\n\t\t\t\t\t\tSubscriptionType.none_pending_in,                            // Table 6.\n\t\t\t\t\t\tSubscriptionType.none_pending_out_in,                        // Table 7.\n\t\t\t\t\t\tSubscriptionType.none_pending_in                             // Table 8.\n\t\t),\n\t\tnone_pending_out_in(SubscriptionType.from_pending_out,    // Table 1.\n\t\t\t\t\t\t\tSubscriptionType.none_pending_out,                           // Table 2.\n\t\t\t\t\t\t\tSubscriptionType.none_pending_out_in,                        // Table 3.\n\t\t\t\t\t\t\tSubscriptionType.none_pending_out,                           // Table 4.\n\t\t\t\t\t\t\tSubscriptionType.to_pending_in,                              // Table 5.\n\t\t\t\t\t\t\tSubscriptionType.none_pending_in,                            // Table 6.\n\t\t\t\t\t\t\tSubscriptionType.none_pending_out_in,                        // Table 7.\n\t\t\t\t\t\t\tSubscriptionType.none_pending_in                             // Table 8.\n\t\t),\n\t\tto(SubscriptionType.to_pre_approved,                     // Table 1.\n\t\t   SubscriptionType.to,                                         // Table 2.\n\t\t   SubscriptionType.to_pending_in,                              // Table 3.\n\t\t   SubscriptionType.to,                                         // Table 4.\n\t\t   SubscriptionType.to,                                         // Table 5.\n\t\t   SubscriptionType.none,                                       // Table 6.\n\t\t   SubscriptionType.to,                                         // Table 7.\n\t\t   SubscriptionType.none                                        // Table 8.\n\t\t),\n\t\tto_pending_in(SubscriptionType.both,                      // Table 1.\n\t\t\t\t\t  SubscriptionType.to,                                         // Table 2.\n\t\t\t\t\t  SubscriptionType.to_pending_in,                              // Table 3.\n\t\t\t\t\t  SubscriptionType.to,                                         // Table 4.\n\t\t\t\t\t  SubscriptionType.to_pending_in,                              // Table 5.\n\t\t\t\t\t  SubscriptionType.none_pending_in,                            // Table 6.\n\t\t\t\t\t  SubscriptionType.to_pending_in,                              // Table 7.\n\t\t\t\t\t  SubscriptionType.none_pending_in                             // Table 8.\n\t\t),\n\t\tfrom(SubscriptionType.from,                               // Table 1.\n\t\t\t SubscriptionType.none,                                       // Table 2.\n\t\t\t SubscriptionType.from,                                       // Table 3.\n\t\t\t SubscriptionType.none,                                       // Table 4.\n\t\t\t SubscriptionType.from,                                       // Table 5.\n\t\t\t SubscriptionType.from,                                       // Table 6.\n\t\t\t SubscriptionType.from_pending_out,                           // Table 7.\n\t\t\t SubscriptionType.from                                        // Table 8.\n\t\t),\n\t\tfrom_pending_out(SubscriptionType.from_pending_out,       // Table 1.\n\t\t\t\t\t\t SubscriptionType.none_pending_out,                           // Table 2.\n\t\t\t\t\t\t SubscriptionType.from_pending_out,                           // Table 3.\n\t\t\t\t\t\t SubscriptionType.none_pending_out,                           // Table 4.\n\t\t\t\t\t\t SubscriptionType.both,                                       // Table 5.\n\t\t\t\t\t\t SubscriptionType.from,                                       // Table 6.\n\t\t\t\t\t\t SubscriptionType.from_pending_out,                           // Table 7.\n\t\t\t\t\t\t SubscriptionType.from                                        // Table 8.\n\t\t),\n\t\tboth(SubscriptionType.both,                               // Table 1.\n\t\t\t SubscriptionType.to,                                         // Table 2.\n\t\t\t SubscriptionType.both,                                       // Table 3.\n\t\t\t SubscriptionType.to,                                         // Table 4.\n\t\t\t SubscriptionType.both,                                       // Table 5.\n\t\t\t SubscriptionType.from,                                       // Table 6.\n\t\t\t SubscriptionType.both,                                       // Table 7.\n\t\t\t SubscriptionType.from                                        // Table 8.\n\t\t),\n\t\tnone_pre_approved(SubscriptionType.none_pre_approved, SubscriptionType.none, SubscriptionType.from,\n\t\t\t\t\t\t  SubscriptionType.none_pre_approved, SubscriptionType.none_pre_approved,\n\t\t\t\t\t\t  SubscriptionType.none_pre_approved, SubscriptionType.none_pending_out_pre_approved,\n\t\t\t\t\t\t  SubscriptionType.none_pre_approved),\n\t\tnone_pending_out_pre_approved(SubscriptionType.none_pending_out_pre_approved, SubscriptionType.none_pending_out,\n\t\t\t\t\t\t\t\t\t  SubscriptionType.from_pending_out, SubscriptionType.none_pending_out_pre_approved,\n\t\t\t\t\t\t\t\t\t  SubscriptionType.to_pre_approved,\n\t\t\t\t\t\t\t\t\t  SubscriptionType.none_pre_approved,\n\t\t\t\t\t\t\t\t\t  SubscriptionType.none_pending_out_pre_approved,\n\t\t\t\t\t\t\t\t\t  SubscriptionType.none_pre_approved),\n\t\tto_pre_approved(SubscriptionType.to_pre_approved, SubscriptionType.to, SubscriptionType.both,\n\t\t\t\t\t\tSubscriptionType.to_pre_approved, SubscriptionType.to_pre_approved,\n\t\t\t\t\t\tSubscriptionType.none_pre_approved, SubscriptionType.to_pre_approved,\n\t\t\t\t\t\tSubscriptionType.none_pre_approved);\n\n\t\tprivate EnumMap<PresenceType, SubscriptionType> stateTransition = new EnumMap<PresenceType, SubscriptionType>(\n\t\t\t\tPresenceType.class);\n\n\t\t// ~--- constructors -------------------------------------------------------\n\t\tprivate StateTransition(SubscriptionType out_subscribed, SubscriptionType out_unsubscribed,\n\t\t\t\t\t\t\t\tSubscriptionType in_subscribe, SubscriptionType in_unsubscribe,\n\t\t\t\t\t\t\t\tSubscriptionType in_subscribed, SubscriptionType in_unsubscribed,\n\t\t\t\t\t\t\t\tSubscriptionType out_subscribe, SubscriptionType out_unsubscribe) {\n\t\t\tstateTransition.put(PresenceType.out_subscribed, out_subscribed);\n\t\t\tstateTransition.put(PresenceType.out_unsubscribed, out_unsubscribed);\n\t\t\tstateTransition.put(PresenceType.in_subscribe, in_subscribe);\n\t\t\tstateTransition.put(PresenceType.in_unsubscribe, in_unsubscribe);\n\t\t\tstateTransition.put(PresenceType.in_subscribed, in_subscribed);\n\t\t\tstateTransition.put(PresenceType.in_unsubscribed, in_unsubscribed);\n\t\t\tstateTransition.put(PresenceType.out_subscribe, out_subscribe);\n\t\t\tstateTransition.put(PresenceType.out_unsubscribe, out_unsubscribe);\n\t\t}\n\n\t\tpublic SubscriptionType getStateTransition(PresenceType pres_type) {\n\t\t\tSubscriptionType res = stateTransition.get(pres_type);\n\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.finest(\"this=\" + this.toString() + \", pres_type=\" + pres_type + \", res=\" + res);\n\t\t\t}\n\n\t\t\treturn res;\n\t\t}\n\t}\n\tpublic enum SubscriptionType {\n\t\tnone(\"none\"),\n\t\tnone_pending_out(\"none\", \"subscribe\"),\n\t\tnone_pending_in(\"none\"),\n\t\tnone_pending_out_in(\"none\", \"subscribe\"),\n\t\tto(\"to\"),\n\t\tto_pending_in(\"to\"),\n\t\tfrom(\"from\"),\n\t\tfrom_pending_out(\"from\", \"subscribe\"),\n\t\tboth(\"both\"),\n\t\tremove(\"remove\"),\n\t\tnone_pre_approved(\"none\", null, true),\n\t\tnone_pending_out_pre_approved(\"none\", \"subscribe\", true),\n\t\tto_pre_approved(\"to\", null, true);\n\n\t\tprivate Map<String, String> attrs = new LinkedHashMap<String, String>(2, 1.0f);\n\n\t\tprivate SubscriptionType(String subscr) {\n\t\t\tthis(subscr, null, false);\n\t\t}\n\n\t\tprivate SubscriptionType(String subscr, String ask) {\n\t\t\tthis(subscr, ask, false);\n\t\t}\n\n\t\tprivate SubscriptionType(String subscr, String ask, boolean approved) {\n\t\t\tattrs.put(\"subscription\", subscr);\n\t\t\tif (ask != null) {\n\t\t\t\tattrs.put(\"ask\", ask);\n\t\t\t}    // end of if (ask != null)\n\t\t\tif (approved) {\n\t\t\t\tattrs.put(\"approved\", \"true\");\n\t\t\t}\n\t\t}\n\n\t\tpublic Map<String, String> getSubscriptionAttr() {\n\t\t\treturn attrs;\n\t\t}\n\t}\n\n\tprotected static boolean emptyNameAllowed = false;\n\n\t// ~--- constant enums -------------------------------------------------------\n\tprotected static int maxRosterSize = new Long(Runtime.getRuntime().maxMemory() / 250000L).intValue();\n\n\t// Below StateTransition enum is implementation of all below tables\n\t// coming from RFC-3921\n\t// Table 1: Recommended handling of outbound \"subscribed\" stanzas\n\t// +----------------------------------------------------------------+\n\t// | EXISTING STATE          | ROUTE? | NEW STATE                   |\n\t// +----------------------------------------------------------------+\n\t// | \"None\"                  | no     | pre-approval                |\n\t// | \"None + Pending Out\"    | no     | pre-approval                |\n\t// | \"None + Pending In\"     | yes    | \"From\"                      |\n\t// | \"None + Pending Out/In\" | yes    | \"From + Pending Out\"        |\n\t// | \"To\"                    | no     | pre-approval                |\n\t// | \"To + Pending In\"       | yes    | \"Both\"                      |\n\t// | \"From\"                  | no     | no state change             |\n\t// | \"From + Pending Out\"    | no     | no state change             |\n\t// | \"Both\"                  | no     | no state change             |\n\t// +----------------------------------------------------------------+\n\t// Table 2: Recommended handling of outbound \"unsubscribed\" stanzas\n\t// +----------------------------------------------------------------+\n\t// | EXISTING STATE          | ROUTE? | NEW STATE                   |\n\t// +----------------------------------------------------------------+\n\t// | \"None\"                  | no     | no state change [1]         |\n\t// | \"None + Pending Out\"    | no     | no state change [1]         |\n\t// | \"None + Pending In\"     | yes    | \"None\"                      |\n\t// | \"None + Pending Out/In\" | yes    | \"None + Pending Out\"        |\n\t// | \"To\"                    | no     | no state change [1]         |\n\t// | \"To + Pending In\"       | yes    | \"To\"                        |\n\t// | \"From\"                  | yes    | \"None\"                      |\n\t// | \"From + Pending Out\"    | yes    | \"None + Pending Out\"        |\n\t// | \"Both\"                  | yes    | \"To\"                        |\n\t// +----------------------------------------------------------------+\n\t// [1] This event can result in cancellation of a subscription pre-approval.\n\t//\n\t// Table 3: Recommended handling of inbound \"subscribe\" stanzas\n\t// +------------------------------------------------------------------+\n\t// | EXISTING STATE | DELIVER? | NEW STATE |\n\t// +------------------------------------------------------------------+\n\t// | \"None\" | yes | \"None + Pending In\" |\n\t// | \"None + Pending Out\" | yes | \"None + Pending Out/In\" |\n\t// | \"None + Pending In\" | no | no state change |\n\t// | \"None + Pending Out/In\" | no | no state change |\n\t// | \"To\" | yes | \"To + Pending In\" |\n\t// | \"To + Pending In\" | no | no state change |\n\t// | \"From\" | no * | no state change |\n\t// | \"From + Pending Out\" | no * | no state change |\n\t// | \"Both\" | no * | no state change |\n\t// +------------------------------------------------------------------+\n\t// Table 4: Recommended handling of inbound \"unsubscribe\" stanzas\n\t// +------------------------------------------------------------------+\n\t// | EXISTING STATE | DELIVER? | NEW STATE |\n\t// +------------------------------------------------------------------+\n\t// | \"None\" | no | no state change |\n\t// | \"None + Pending Out\" | no | no state change |\n\t// | \"None + Pending In\" | yes * | \"None\" |\n\t// | \"None + Pending Out/In\" | yes * | \"None + Pending Out\" |\n\t// | \"To\" | no | no state change |\n\t// | \"To + Pending In\" | yes * | \"To\" |\n\t// | \"From\" | yes * | \"None\" |\n\t// | \"From + Pending Out\" | yes * | \"None + Pending Out\" |\n\t// | \"Both\" | yes * | \"To\" |\n\t// +------------------------------------------------------------------+\n\t// Table 5: Recommended handling of inbound \"subscribed\" stanzas\n\t// +------------------------------------------------------------------+\n\t// | EXISTING STATE | DELIVER? | NEW STATE |\n\t// +------------------------------------------------------------------+\n\t// | \"None\" | no | no state change |\n\t// | \"None + Pending Out\" | yes | \"To\" |\n\t// | \"None + Pending In\" | no | no state change |\n\t// | \"None + Pending Out/In\" | yes | \"To + Pending In\" |\n\t// | \"To\" | no | no state change |\n\t// | \"To + Pending In\" | no | no state change |\n\t// | \"From\" | no | no state change |\n\t// | \"From + Pending Out\" | yes | \"Both\" |\n\t// | \"Both\" | no | no state change |\n\t// +------------------------------------------------------------------+\n\t// Table 6: Recommended handling of inbound \"unsubscribed\" stanzas\n\t// +------------------------------------------------------------------+\n\t// | EXISTING STATE | DELIVER? | NEW STATE |\n\t// +------------------------------------------------------------------+\n\t// | \"None\" | no | no state change |\n\t// | \"None + Pending Out\" | yes | \"None\" |\n\t// | \"None + Pending In\" | no | no state change |\n\t// | \"None + Pending Out/In\" | yes | \"None + Pending In\" |\n\t// | \"To\" | yes | \"None\" |\n\t// | \"To + Pending In\" | yes | \"None + Pending In\" |\n\t// | \"From\" | no | no state change |\n\t// | \"From + Pending Out\" | yes | \"From\" |\n\t// | \"Both\" | yes | \"From\" |\n\t// +------------------------------------------------------------------+\n\t// There are 2 tables missing I think in RFC-3921:\n\t// Table 7: Recommended handling of outbound \"subscribe\" stanzas\n\t// +------------------------------------------------------------------+\n\t// | EXISTING STATE | ROUTE? | NEW STATE |\n\t// +------------------------------------------------------------------+\n\t// | \"None\" | yes | \"None + Pending Out\" |\n\t// | \"None + Pending Out\" | no | no state change |\n\t// | \"None + Pending In\" | yes | \"None + Pending Out/In\" |\n\t// | \"None + Pending Out/In\" | no | no state change |\n\t// | \"To\" | no | no state change |\n\t// | \"To + Pending In\" | no | no state change |\n\t// | \"From\" | yes | \"From + Pending Out\" |\n\t// | \"From + Pending Out\" | no | no state change |\n\t// | \"Both\" | no | no state change |\n\t// +------------------------------------------------------------------+\n\t// Table 8: Recommended handling of outbound \"unsubscribe\" stanzas\n\t// +------------------------------------------------------------------+\n\t// | EXISTING STATE | ROUTE? | NEW STATE |\n\t// +------------------------------------------------------------------+\n\t// | \"None\" | no | no state change |\n\t// | \"None + Pending Out\" | yes | \"None\" |\n\t// | \"None + Pending In\" | no | no state change |\n\t// | \"None + Pending Out/In\" | yes | \"None + Pending In\" |\n\t// | \"To\" | yes | \"None\" |\n\t// | \"To + Pending In\" | yes | \"None + Pending In\" |\n\t// | \"From\" | no | no state change |\n\t// | \"From + Pending Out\" | yes | \"From\" |\n\t// | \"Both\" | yes | \"From\" |\n\t// +------------------------------------------------------------------+\n\tprivate static EnumMap<SubscriptionType, StateTransition> subsToStateMap = new EnumMap<SubscriptionType, StateTransition>(\n\t\t\tSubscriptionType.class);\n\n\t// ~--- static initializers --------------------------------------------------\n\tstatic {\n\t\tsubsToStateMap.put(SubscriptionType.none, StateTransition.none);\n\t\tsubsToStateMap.put(SubscriptionType.none_pre_approved, StateTransition.none_pre_approved);\n\t\tsubsToStateMap.put(SubscriptionType.none_pending_out, StateTransition.none_pending_out);\n\t\tsubsToStateMap.put(SubscriptionType.none_pending_out_pre_approved,\n\t\t\t\t\t\t   StateTransition.none_pending_out_pre_approved);\n\t\tsubsToStateMap.put(SubscriptionType.none_pending_in, StateTransition.none_pending_in);\n\t\tsubsToStateMap.put(SubscriptionType.none_pending_out_in, StateTransition.none_pending_out_in);\n\t\tsubsToStateMap.put(SubscriptionType.to, StateTransition.to);\n\t\tsubsToStateMap.put(SubscriptionType.to_pre_approved, StateTransition.to_pre_approved);\n\t\tsubsToStateMap.put(SubscriptionType.to_pending_in, StateTransition.to_pending_in);\n\t\tsubsToStateMap.put(SubscriptionType.from, StateTransition.from);\n\t\tsubsToStateMap.put(SubscriptionType.from_pending_out, StateTransition.from_pending_out);\n\t\tsubsToStateMap.put(SubscriptionType.both, StateTransition.both);\n\t}\n\n\tprivate EventBus eventBus;\n\n\tpublic static int getMaxRosterSize() {\n\t\treturn maxRosterSize;\n\t}\n\n\tpublic static void setMaxRosterSize(int maxRosterSize) {\n\t\tRosterAbstract.maxRosterSize = maxRosterSize;\n\t}\n\n\tpublic static SubscriptionType getStateTransition(final SubscriptionType subscription,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  final PresenceType presence) {\n\t\tStateTransition transition = subsToStateMap.get(subscription);\n\n\t\tif (transition == null) {\n\t\t\treturn getStateTransition(SubscriptionType.none, presence);\n\t\t}\n\n\t\treturn transition.getStateTransition(presence);\n\t}\n\n\tpublic static boolean isEmptyNameAllowed() {\n\t\treturn emptyNameAllowed;\n\t}\n\n\tpublic static void setEmptyNameAllowed(boolean emptyNameAllowed) {\n\t\tRosterAbstract.emptyNameAllowed = emptyNameAllowed;\n\t}\n\n\tpublic EventBus getEventBus() {\n\t\treturn eventBus;\n\t}\n\n\tpublic void setEventBus(EventBus eventBus) {\n\t\tif (this.eventBus != null && this.eventBus != eventBus) {\n\t\t\tthis.eventBus.unregisterAll(this);\n\t\t}\n\t\tthis.eventBus = eventBus;\n\t\tif (this.eventBus != null) {\n\t\t\tthis.eventBus.registerAll(this);\n\t\t}\n\t}\n\n\tpublic Queue<Packet> addJidToRoster(UserRepository repository, XMPPSession session, BareJID owner, RosterElement rosterElement)\n\t\t\tthrows NotAuthorizedException, PolicyViolationException, TigaseDBException, NoConnectionIdException {\n\t\tArrayDeque<Packet> results = new ArrayDeque<>();\n\t\tOptional<XMPPResourceConnection> conn = getActiveConnections(session).stream().findFirst();\n\t\tif (conn.isEmpty()) {\n\t\t\t// there is no session..\n\t\t\tmodifyStoredRoster(repository, owner, roster -> {\n\t\t\t\tRosterElement item = roster.get(rosterElement.getJid().getBareJID());\n\t\t\t\tif (item != null) {\n\t\t\t\t\titem.setGroups(rosterElement.getGroups());\n\t\t\t\t\titem.setSubscription(rosterElement.getSubscription());\n\t\t\t\t\titem.setMixParticipantId(rosterElement.getMixParticipantId());\n\t\t\t\t} else {\n\t\t\t\t\troster.put(rosterElement.getJid().getBareJID(), rosterElement);\n\t\t\t\t}\n\t\t\t});\n\t\t} else {\n\t\t\taddBuddy(conn.get(), rosterElement.getJid(), rosterElement.getName(), rosterElement.getGroups(), rosterElement.getSubscription(), rosterElement.getMixParticipantId(), rosterElement.getOtherData());\n\t\t\tElement new_buddy = getBuddyItem(conn.get(), rosterElement.getJid());\n\t\t\tupdateBuddyChange(conn.get(), results, new_buddy);\n\t\t}\n\t\treturn results;\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic Queue<Packet> removeJidFromRoster(UserRepository repository, XMPPSession session, BareJID owner, JID jid)\n\t\t\tthrows TigaseDBException, NotAuthorizedException, NoConnectionIdException {\n\t\tArrayDeque<Packet> results = new ArrayDeque<>();\n\t\tOptional<XMPPResourceConnection> conn = getActiveConnections(session).stream().findFirst();\n\t\tif (conn.isEmpty()) {\n\t\t\t// there is no session..\n\t\t\tmodifyStoredRoster(repository, owner, roster -> {\n\t\t\t\troster.remove(jid.getBareJID());\n\t\t\t});\n\t\t} else {\n\t\t\tElement it = new Element(\"item\");\n\n\t\t\tit.setAttribute(\"jid\", jid.toString());\n\t\t\tit.setAttribute(\"subscription\", \"remove\");\n\t\t\tremoveBuddy(conn.get(), jid);\n\t\t\tupdateBuddyChange(conn.get(), results, it);\n\t\t}\n\t\treturn results;\n\t}\n\n\tprotected List<XMPPResourceConnection> getActiveConnections(XMPPSession session) {\n\t\treturn session == null ? Collections.emptyList() : session.getActiveResources();\n\t}\n\n\tpublic void modifyStoredRoster(UserRepository repository, BareJID owner, Consumer<Map<BareJID,RosterElement>> modifyRoster)\n\t\t\tthrows TigaseDBException {\n\t\tthrow new UnsupportedOperationException(\"Feature not implemented in \" + this.getClass().getCanonicalName());\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic abstract void addBuddy(XMPPResourceConnection session, JID jid, String name, String[] groups,\n\t\t\t\t\t\t\t\t  SubscriptionType subscription, String mixParticipantId, String otherData)\n\t\t\tthrows NotAuthorizedException, TigaseDBException, PolicyViolationException;\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic abstract void addBuddy(XMPPResourceConnection session, JID jid, String name, String[] groups,\n\t\t\t\t\t\t\t\t  SubscriptionType subscription, String otherData)\n\t\t\tthrows NotAuthorizedException, TigaseDBException, PolicyViolationException;\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.0.0\", removeIn = \"9.0.0\")\n\tpublic void addBuddy(XMPPResourceConnection session, JID jid, String name, String[] groups,\n\t\t\t\t\t\t\t\t  String otherData)\n\t\t\tthrows NotAuthorizedException, TigaseDBException, PolicyViolationException {\n\t\taddBuddy(session, jid, name, groups, null, otherData);\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic abstract boolean addBuddyGroup(final XMPPResourceConnection session, JID buddy, final String[] groups)\n\t\t\tthrows NotAuthorizedException, TigaseDBException;\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic abstract boolean containsBuddy(final XMPPResourceConnection session, JID buddy)\n\t\t\tthrows NotAuthorizedException, TigaseDBException;\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic String groupNode(JID buddy) {\n\t\treturn ROSTER + \"/\" + buddy.getBareJID();\n\t}\n\n\tpublic void init(UserRepository repo) throws TigaseDBException, TigaseDBException {\n\t}\n\n\tpublic abstract void logout(XMPPResourceConnection session);\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic abstract boolean presenceSent(XMPPResourceConnection session, JID jid)\n\t\t\tthrows NotAuthorizedException, TigaseDBException;\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic abstract boolean removeBuddy(final XMPPResourceConnection session, JID jid)\n\t\t\tthrows NotAuthorizedException, TigaseDBException;\n\n\tpublic void updateBuddyChange(final XMPPResourceConnection session, final Queue<Packet> results, final Element item)\n\t\t\tthrows NotAuthorizedException, TigaseDBException, NoConnectionIdException {\n\n\t\tJID jid = JID.jidInstanceNS(item.getAttributeStaticStr(\"jid\"));\n\t\tbroadcastRosterChange(session.getParentSession(), jid, results::offer);\n\t\tif (eventBus != null) {\n\t\t\tRosterElement rosterElement = getRosterElement(session, jid);\n\t\t\tif (rosterElement != null) {\n\t\t\t\teventBus.fire(new RosterModifiedEvent(session.getJID().copyWithoutResource(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t  session.getJID().copyWithoutResource(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t  session.getParentSession(), rosterElement));\n\t\t\t} else {\n\t\t\t\teventBus.fire(new RosterModifiedEvent(session.getJID().copyWithoutResource(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t  session.getJID().copyWithoutResource(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t  session.getParentSession(), jid, SubscriptionType.remove));\n\t\t\t}\n\t\t}\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic boolean updateBuddySubscription(final XMPPResourceConnection session, final PresenceType presence, JID jid)\n\t\t\tthrows NotAuthorizedException, TigaseDBException, PolicyViolationException {\n\t\tSubscriptionType current_subscription = getBuddySubscription(session, jid);\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"current_subscription={0} for jid={1}\", new Object[]{current_subscription, jid});\n\t\t}\n\t\tif (current_subscription == null) {\n\n\t\t\t// don't create new roster item for incomming unsubscribe presence #219 /\n\t\t\t// #210\n\t\t\tif ((presence != PresenceType.in_unsubscribe) && (presence != PresenceType.out_unsubscribe)) {\n\t\t\t\taddBuddy(session, jid, null, null, null, null);\n\t\t\t}\n\t\t\tcurrent_subscription = SubscriptionType.none;\n\t\t}\n\n\t\tfinal SubscriptionType new_subscription = getStateTransition(current_subscription, presence);\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"new_subscription={0} for presence={1}\", new Object[]{new_subscription, presence});\n\t\t}\n\t\tif ((current_subscription == SubscriptionType.none_pending_in) &&\n\t\t\t\t(presence == PresenceType.out_unsubscribed || presence == PresenceType.in_unsubscribe)) {\n\t\t\tremoveBuddy(session, jid);\n\n\t\t\treturn false;\n\t\t}\n\t\tif (current_subscription != new_subscription) {\n\t\t\tsetBuddySubscription(session, new_subscription, jid);\n\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\n\t}\n\n\tpublic void updateRosterHash(String roster_str, XMPPResourceConnection session) {\n\t\tString roster_hash = null;\n\n\t\ttry {\n\t\t\troster_hash = Algorithms.hexDigest(\"\", roster_str, \"MD5\");\n\t\t} catch (Exception e) {\n\t\t\troster_hash = null;\n\t\t}\n\t\tsession.putSessionData(ROSTERHASH, roster_hash);\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic abstract JID[] getBuddies(final XMPPResourceConnection session)\n\t\t\tthrows NotAuthorizedException, TigaseDBException;\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic JID[] getBuddies(final XMPPResourceConnection session, final EnumSet<SubscriptionType> subscrs)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\n\t\t// final String[] allBuddies = getBuddies(session, onlineOnly);\n\t\tJID[] allBuddies = getBuddies(session);\n\n\t\tif (allBuddies == null) {\n\t\t\treturn null;\n\t\t}    // end of if (allBuddies == null)\n\n\t\tArrayList<JID> list = new ArrayList<JID>();\n\n\t\tfor (JID buddy : allBuddies) {\n\t\t\tfinal SubscriptionType subs = getBuddySubscription(session, buddy);\n\n\t\t\tif (subscrs.contains(subs)) {\n\t\t\t\tlist.add(buddy);\n\t\t\t}    // end of if (subscrs.contains(subs))\n\t\t}      // end of for ()\n\n\t\treturn list.toArray(new JID[list.size()]);\n\t}\n\n\tpublic String getBuddiesHash(final XMPPResourceConnection session) {\n\t\tString hash = (String) session.getSessionData(ROSTERHASH);\n\n\t\treturn ((hash != null) ? hash : \"\");\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic abstract String[] getBuddyGroups(final XMPPResourceConnection session, JID buddy)\n\t\t\tthrows NotAuthorizedException, TigaseDBException;\n\n\tpublic Element getBuddyItem(final XMPPResourceConnection session, JID buddy)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tSubscriptionType subscr = getBuddySubscription(session, buddy);\n\n\t\tif (subscr == null) {\n\t\t\tsubscr = SubscriptionType.none;\n\t\t\tsetBuddySubscription(session, subscr, buddy);\n\t\t}    // end of if\n\n\t\tElement item = new Element(\"item\");\n\n\t\titem.setAttribute(\"jid\", buddy.toString());\n\t\titem.addAttributes(subscr.getSubscriptionAttr());\n\n\t\tString name = getBuddyName(session, buddy);\n\n\t\tif (name != null) {\n\t\t\titem.setAttribute(\"name\", XMLUtils.escape(name));\n\t\t}\n\n\t\tString[] groups = getBuddyGroups(session, buddy);\n\n\t\tif (groups != null) {\n\t\t\tfor (String gr : groups) {\n\t\t\t\tElement group = new Element(\"group\");\n\n\t\t\t\tgroup.setCData(XMLUtils.escape(gr));\n\t\t\t\titem.addChild(group);\n\t\t\t}    // end of for ()\n\t\t}      // end of if-else\n\n\t\tif (Boolean.TRUE.equals(session.getSessionData(\"urn:xmpp:mix:roster:0\"))) {\n\t\t\tString participantId = getMixParticipantId(session, buddy);\n\t\t\tif (participantId != null) {\n\t\t\t\titem.addChild(new Element(\"channel\", new String[] { \"xmlns\", \"participant-id\" }, new String[] { \"urn:xmpp:mix:roster:0\", participantId }));\n\t\t\t}\n\t\t}\n\n\t\treturn item;\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic abstract String getBuddyName(final XMPPResourceConnection session, JID buddy)\n\t\t\tthrows NotAuthorizedException, TigaseDBException;\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic abstract SubscriptionType getBuddySubscription(final XMPPResourceConnection session, JID buddy)\n\t\t\tthrows NotAuthorizedException, TigaseDBException;\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic abstract Element getCustomChild(XMPPResourceConnection session, JID buddy)\n\t\t\tthrows NotAuthorizedException, TigaseDBException;\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic List<Element> getCustomChildren(XMPPResourceConnection session, JID buddy)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\n\t\tList<Element> result = new LinkedList<Element>();\n\n\t\tElement customChild = getCustomChild(session, buddy);\n\t\tif (customChild != null) {\n\t\t\tresult.add(customChild);\n\t\t}\n\t\treturn result;\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic abstract String getMixParticipantId(final XMPPResourceConnection session, JID buddy)\n\t\t\tthrows NotAuthorizedException, TigaseDBException;\n\t\n\tpublic PresenceType getPresenceType(final XMPPResourceConnection session, final Packet packet)\n\t\t\tthrows NotAuthorizedException {\n\t\tBareJID to = (packet.getStanzaTo() != null) ? packet.getStanzaTo().getBareJID() : null;\n\t\tStanzaType type = packet.getType();\n\n\t\tif (type == null) {\n\t\t\ttype = StanzaType.available;\n\t\t} else {\n\t\t\tif (type == StanzaType.error) {\n\t\t\t\treturn PresenceType.error;\n\t\t\t}\n\t\t}\n\t\tif ((to == null) || !session.isUserId(to)) {\n\t\t\tif (INITIAL_PRESENCES.contains(type)) {\n\t\t\t\treturn PresenceType.out_initial;\n\t\t\t}\n\t\t\tif (type == StanzaType.subscribe) {\n\t\t\t\treturn PresenceType.out_subscribe;\n\t\t\t}    // end of if (type == StanzaType.subscribe)\n\t\t\tif (type == StanzaType.unsubscribe) {\n\t\t\t\treturn PresenceType.out_unsubscribe;\n\t\t\t}    // end of if (type == StanzaType.unsubscribe)\n\t\t\tif (type == StanzaType.subscribed) {\n\t\t\t\treturn PresenceType.out_subscribed;\n\t\t\t}    // end of if (type == StanzaType.subscribed)\n\t\t\tif (type == StanzaType.unsubscribed) {\n\t\t\t\treturn PresenceType.out_unsubscribed;\n\t\t\t}    // end of if (type == StanzaType.unsubscribed)\n\t\t\tif (type == StanzaType.probe) {\n\t\t\t\treturn PresenceType.out_probe;\n\t\t\t}\n\t\t\t// StanzaType.probe is invalid here....\n\t\t\t// if (type == StanzaType.probe) {\n\t\t\t// return PresenceType.out_probe;\n\t\t\t// } // if (type == StanzaType.probe)\n\t\t}      // end of if (to == null || to.equals(session.getUserId()))\n\t\tif ((to != null) && session.isUserId(to)) {\n\t\t\tif (INITIAL_PRESENCES.contains(type)) {\n\t\t\t\treturn PresenceType.in_initial;\n\t\t\t}\n\t\t\tif (type == StanzaType.subscribe) {\n\t\t\t\treturn PresenceType.in_subscribe;\n\t\t\t}    // end of if (type == StanzaType.subscribe)\n\t\t\tif (type == StanzaType.unsubscribe) {\n\t\t\t\treturn PresenceType.in_unsubscribe;\n\t\t\t}    // end of if (type == StanzaType.unsubscribe)\n\t\t\tif (type == StanzaType.subscribed) {\n\t\t\t\treturn PresenceType.in_subscribed;\n\t\t\t}    // end of if (type == StanzaType.subscribed)\n\t\t\tif (type == StanzaType.unsubscribed) {\n\t\t\t\treturn PresenceType.in_unsubscribed;\n\t\t\t}    // end of if (type == StanzaType.unsubscribed)\n\t\t\tif (type == StanzaType.probe) {\n\t\t\t\treturn PresenceType.in_probe;\n\t\t\t}    // end of if (type == StanzaType.probe)\n\t\t}      // end of if (to != null && !to.equals(session.getUserId()))\n\n\t\treturn null;\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic abstract RosterElement getRosterElement(XMPPResourceConnection session, JID jid)\n\t\t\tthrows NotAuthorizedException, TigaseDBException;\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic abstract Function<JID, RosterElement> rosterElementProvider(XMPPResourceConnection session) throws NotAuthorizedException, TigaseDBException;\n\n\tpublic List<Element> getRosterItems(XMPPResourceConnection session)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tLinkedList<Element> items = new LinkedList<Element>();\n\n\t\t// String[] buddies = getBuddies(session, online);\n\t\tJID[] buddies = getBuddies(session);\n\n\t\tif (buddies != null) {\n\t\t\tfor (JID buddy : buddies) {\n\t\t\t\tElement buddy_item = getBuddyItem(session, buddy);\n\n\t\t\t\t// String item_group = buddy_item.getCData(\"/item/group\");\n\t\t\t\titems.add(buddy_item);\n\t\t\t}\n\t\t}\n\t\treturn items;\n\t}\n\n\t/**\n\t * Check if data containing user roster for this session is loaded from database\n\t */\n\tpublic abstract boolean isRosterLoaded(XMPPResourceConnection session);\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic abstract boolean isOnline(XMPPResourceConnection session, JID jid)\n\t\t\tthrows NotAuthorizedException, TigaseDBException;\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic boolean isPendingIn(final XMPPResourceConnection session, JID jid)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tSubscriptionType subscr = getBuddySubscription(session, jid);\n\n\t\treturn PENDING_IN.contains(subscr);\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic boolean isSubscribedFrom(final XMPPResourceConnection session, JID jid)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tSubscriptionType subscr = getBuddySubscription(session, jid);\n\n\t\treturn FROM_SUBSCRIBED.contains(subscr);\n\t}\n\n\tpublic boolean isSubscribedFrom(SubscriptionType subscr) {\n\t\treturn FROM_SUBSCRIBED.contains(subscr);\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic boolean isSubscribedTo(final XMPPResourceConnection session, JID jid)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tSubscriptionType subscr = getBuddySubscription(session, jid);\n\n\t\treturn TO_SUBSCRIBED.contains(subscr);\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic boolean isPreApproved(final XMPPResourceConnection session, JID jid)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tSubscriptionType subscr = getBuddySubscription(session, jid);\n\n\t\treturn PRE_APPROVED.contains(subscr);\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic abstract void setBuddyName(final XMPPResourceConnection session, JID buddy, final String name)\n\t\t\tthrows NotAuthorizedException, TigaseDBException;\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic abstract void setBuddySubscription(final XMPPResourceConnection session, final SubscriptionType subscription,\n\t\t\t\t\t\t\t\t\t\t\t  JID buddy) throws NotAuthorizedException, TigaseDBException;\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic abstract void setOnline(XMPPResourceConnection session, JID jid, boolean online)\n\t\t\tthrows NotAuthorizedException, TigaseDBException;\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic abstract void setPresenceSent(XMPPResourceConnection session, JID jid, boolean sent)\n\t\t\tthrows NotAuthorizedException, TigaseDBException;\n\n\tpublic void setProperties(Map<String, Object> settings) {\n\t\tif (settings.get(\"empty_name_enabled\") != null) {\n\t\t\temptyNameAllowed = Boolean.valueOf((String) settings.get(\"empty_name_enabled\"));\n\t\t}\n\t\tlog.log(Level.CONFIG, \"Configuring empty name allowed as: \" + emptyNameAllowed);\n\t\tif (settings.get(\"max_roster_size\") != null) {\n\t\t\ttry {\n\t\t\t\tmaxRosterSize = Integer.parseInt((String) settings.get(\"max_roster_size\"));\n\t\t\t} catch (NumberFormatException e) {\n\t\t\t\tmaxRosterSize = new Long(Runtime.getRuntime().maxMemory() / 250000L).intValue();\n\t\t\t}\n\t\t}\n\t\tlog.log(Level.CONFIG, \"Setting maximum number of roster items as: \" + maxRosterSize);\n\t}\n\n\t@HandleEvent\n\tpublic void handleRosterModified(RosterModifiedEvent event) {\n\t\tSessionManager.ProcessorResultWriter writer = event.getPacketWriter();\n\t\tif (writer != null) {\n\t\t\tevent.getSession()\n\t\t\t\t\t.getActiveResources()\n\t\t\t\t\t.stream()\n\t\t\t\t\t.filter(conn -> conn.isAuthorized())\n\t\t\t\t\t.findFirst()\n\t\t\t\t\t.ifPresent(conn -> {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tsynchronized (event.getSession()) {\n\t\t\t\t\t\t\t\tupdateRosterItem(conn, event);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\t\t\t\t\t\tbroadcastRosterChange(event.getSession(), event.getJid(), results::offer);\n\t\t\t\t\t\t\tpreparePresencePackets(event.getSession(), event.getJid(), event.getSubscription(),\n\t\t\t\t\t\t\t\t\t\t\t\t   results::offer);\n\n\t\t\t\t\t\t\twriter.write(results.peek(), conn, results);\n\t\t\t\t\t\t} catch (NotAuthorizedException | NoConnectionIdException | TigaseDBException ex) {\n\t\t\t\t\t\t\tlog.log(Level.FINEST, \"Failed to update roster with changes from the other cluster node\");\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t}\n\t}\n\n\tprotected void updateRosterItem(XMPPResourceConnection conn, RosterModifiedEvent event)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tupdateRosterHash(conn);\n\t}\n\n\tpublic void updateRosterHash(XMPPResourceConnection conn) throws NotAuthorizedException, TigaseDBException {\n\t\tfor (XMPPResourceConnection session : conn.getActiveSessions()) {\n\t\t\tupdateRosterHashForConnection(session);\n\t\t}\n\t}\n\n\tprotected void updateRosterHashForConnection(XMPPResourceConnection conn) throws NotAuthorizedException, TigaseDBException {\n\t\ttry {\n\t\t\tList<Element> items = getRosterItems(conn);\n\t\t\tif (items != null) {\n\t\t\t\tStringBuilder sb = new StringBuilder(4096);\n\t\t\t\tfor (Element item : items) {\n\t\t\t\t\titem.toString(sb);\n\t\t\t\t}\n\t\t\t\tList<Element> its = DynamicRoster.getRosterItems(conn, Collections.emptyMap());\n\t\t\t\tif (its != null && !its.isEmpty()) {\n\t\t\t\t\tfor (Element item : its) {\n\t\t\t\t\t\titem.toString(sb);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tupdateRosterHash(sb.toString(), conn);\n\t\t\t}\n\t\t} catch (RosterRetrievingException|RepositoryAccessException ex) {\n\t\t\tthrow new TigaseDBException(\"Could not load dynamic roster\", ex);\n\t\t}\n\t}\n\n\tprivate void preparePresencePackets(XMPPSession session, JID jid, SubscriptionType subscription,\n\t\t\t\t\t\t\t\t\t\tConsumer<Packet> consumer) {\n\t\tStream<XMPPResourceConnection> sessions = session.getActiveResources()\n\t\t\t\t.stream()\n\t\t\t\t.filter(conn -> conn.isAuthorized())\n\t\t\t\t.filter(conn -> conn.getPresence() != null);\n\n\t\tswitch (subscription) {\n\t\t\tcase from:\n\t\t\tcase from_pending_out:\n\t\t\tcase both:\n\t\t\t\tsessions.map(conn -> conn.getPresence())\n\t\t\t\t\t\t.filter(presence -> presence != null)\n\t\t\t\t\t\t.map(elem -> Packet.packetInstance(elem, JID.jidInstanceNS(elem.getAttributeStaticStr(\"from\")),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   jid))\n\t\t\t\t\t\t.forEach(consumer);\n\t\t\t\tbreak;\n\n\t\t\tcase to:\n\t\t\tcase none:\n\t\t\t\tsessions.map(conn -> Packet.packetInstance(new Element(\"presence\", new String[]{\"type\", \"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{\"unavailable\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tPacket.CLIENT_XMLNS}),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   conn.getjid(), jid)).forEach(consumer);\n\t\t}\n\t}\n\n\tprivate void broadcastRosterChange(XMPPSession session, JID jid, Consumer<Packet> consumer)\n\t\t\tthrows NotAuthorizedException, NoConnectionIdException {\n\t\tfor (XMPPResourceConnection conn : session.getActiveResources()) {\n\t\t\ttry {\n\t\t\t\tElement item = getBuddyItem(conn, jid);\n\t\t\t\tif (item == null) {\n\t\t\t\t\titem = new Element(\"item\");\n\t\t\t\t\titem.setAttribute(\"jid\", jid.toString());\n\t\t\t\t\titem.setAttribute(\"subscription\", \"remove\");\n\t\t\t\t}\n\t\t\t\tbroadcastRosterChange(conn, item, consumer);\n\t\t\t} catch (TigaseDBException ex) {\n\t\t\t\tlog.log(Level.FINE, \"could not load roster for \" + session.getUserName(), ex);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void broadcastRosterChange(XMPPResourceConnection conn, Element item, Consumer<Packet> consumer)\n\t\t\tthrows NotAuthorizedException, NoConnectionIdException {\n\t\tElement update = new Element(\"iq\");\n\n\t\tupdate.setXMLNS(CLIENT_XMLNS);\n\t\tupdate.setAttribute(\"type\", StanzaType.set.toString());\n\n\t\tElement query = new Element(\"query\");\n\n\t\tquery.setXMLNS(ROSTER_XMLNS);\n\t\tquery.addAttribute(VER_ATT, getBuddiesHash(conn));\n\t\tquery.addChild(item);\n\t\tupdate.addChild(query);\n\n\t\tupdate.setAttribute(\"to\", conn.getBareJID().toString());\n\t\tupdate.setAttribute(\"id\", \"rst\" + conn.nextStanzaId());\n\n\t\tPacket pack_update = Packet.packetInstance(update, null, conn.getJID());\n\n\t\tpack_update.setPacketTo(conn.getConnectionId());\n\n\t\t// pack_update.setPacketFrom(session.getJID());\n\t\tconsumer.accept(pack_update);\n\t\t// end of for (XMPPResourceConnection conn: sessions)\n\t}\n\n\tpublic static class RosterModifiedEvent\n\t\t\textends UserSessionEventWithProcessorResultWriter {\n\n\t\tprivate String[] groups;\n\t\tprivate JID jid;\n\t\tprivate String name;\n\t\tprivate SubscriptionType subscription;\n\t\tprivate String mixParticipantId;\n\n\t\tpublic RosterModifiedEvent() {\n\t\t}\n\n\t\t@Deprecated\n\t\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\t\tpublic RosterModifiedEvent(JID sender, JID userJid, XMPPSession session, RosterElement rosterElement) {\n\t\t\tsuper(sender, userJid, session, null);\n\t\t\tthis.jid = rosterElement.getJid();\n\t\t\tthis.name = rosterElement.getName();\n\t\t\tthis.subscription = rosterElement.getSubscription();\n\t\t\tthis.groups = rosterElement.getGroups();\n\t\t\tthis.mixParticipantId = rosterElement.getMixParticipantId();\n\t\t}\n\n\t\t@Deprecated\n\t\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\t\tpublic RosterModifiedEvent(JID sender, JID userJid, XMPPSession session, JID jid,\n\t\t\t\t\t\t\t\t   SubscriptionType subscription) {\n\t\t\tsuper(sender, userJid, session, null);\n\t\t\tthis.jid = jid;\n\t\t\tthis.name = null;\n\t\t\tthis.subscription = subscription;\n\t\t\tthis.groups = null;\n\t\t}\n\n\t\t@Deprecated\n\t\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\t\tpublic JID getJid() {\n\t\t\treturn jid;\n\t\t}\n\n\t\tpublic String getName() {\n\t\t\treturn name;\n\t\t}\n\n\t\tpublic SubscriptionType getSubscription() {\n\t\t\treturn subscription;\n\t\t}\n\n\t\tpublic String[] getGroups() {\n\t\t\treturn groups;\n\t\t}\n\n\t\tpublic String getMixParticipantId() {\n\t\t\treturn mixParticipantId;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/roster/RosterElement.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.roster;\n\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.util.stringprep.XMPPStringPrepFactory;\nimport tigase.xml.Element;\nimport tigase.xml.XMLUtils;\nimport tigase.xmpp.impl.roster.RosterAbstract.SubscriptionType;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.logging.Logger;\n\n/**\n * Describe class RosterElement here.\n * <br>\n * Created: Wed Oct 29 14:21:16 2008\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\npublic class RosterElement {\n\n\tprotected static final long INITIAL_LAST_SEEN_VAL = 1000l;\n\tprivate static final String ACTIVITY_ATT = \"activity\";\n\tprivate static final String ELEM_NAME = \"contact\";\n\tprivate static final String GRP_ATT = \"groups\";\n\tprivate static final double INITIAL_ACTIVITY_VAL = 1d;\n\tprivate static final double INITIAL_WEIGHT_VAL = 1d;\n\tprivate static final String JID_ATT = \"jid\";\n\tprivate static final String LAST_SEEN_ATT = \"last-seen\";\n\tprivate static final Logger log = Logger.getLogger(RosterElement.class.getName());\n\tprivate static final String NAME_ATT = \"name\";\n\tprivate static final String OTHER_ATT = \"other\";\n\tprivate static final String STRINGPREP_ATT = \"preped\";\n\tprivate static final String SUBS_ATT = \"subs\";\n\tprivate static final String WEIGHT_ATT = \"weight\";\n\tprivate static final String MIX_PARTICIPANT_ID_ATT = \"mix-id\";\n\n\tprivate double activity = INITIAL_ACTIVITY_VAL;\n\tprivate String[] groups = null;\n\tprivate JID jid = null;\n\tprivate long lastSeen = INITIAL_LAST_SEEN_VAL;\n\tprivate String mixParticipantId = null;\n\t// private Element item = null;\n\t// private boolean online = false;\n\tprivate boolean modified = false;\n\tprivate String name = null;\n\tprivate Map<String, Boolean> onlineMap = new ConcurrentHashMap<String, Boolean>();\n\tprivate String otherData = null;\n\tprivate boolean persistent = true;\n\tprivate boolean presence_sent = false;\n\tprivate String stringpreped = null;\n\tprivate SubscriptionType subscription = null;\n\tprivate double weight = INITIAL_WEIGHT_VAL;\n\n\tpublic RosterElement(Element roster_el) throws TigaseStringprepException {\n\t\tif (roster_el.getName() == ELEM_NAME) {\n\t\t\tthis.stringpreped = roster_el.getAttributeStaticStr(STRINGPREP_ATT);\n\t\t\tsetJid(roster_el.getAttributeStaticStr(JID_ATT));\n\t\t\tsetName(roster_el.getAttributeStaticStr(NAME_ATT));\n\t\t\tif (roster_el.getAttributeStaticStr(SUBS_ATT) == null) {\n\t\t\t\tsubscription = SubscriptionType.none;\n\t\t\t} else {\n\t\t\t\tsubscription = SubscriptionType.valueOf(roster_el.getAttributeStaticStr(SUBS_ATT));\n\t\t\t}\n\n\t\t\tString grps = roster_el.getAttributeStaticStr(GRP_ATT);\n\n\t\t\tif ((grps != null) && !grps.trim().isEmpty()) {\n\t\t\t\tsetGroups(grps.split(\",\"));\n\t\t\t}\n\n\t\t\tmixParticipantId = roster_el.getAttributeStaticStr(MIX_PARTICIPANT_ID_ATT);\n\n\t\t\tString other_data = roster_el.getAttributeStaticStr(OTHER_ATT);\n\n\t\t\tif ((other_data != null) && !other_data.trim().isEmpty()) {\n\t\t\t\totherData = other_data;\n\t\t\t}\n\n\t\t\tString num_str = roster_el.getAttributeStaticStr(ACTIVITY_ATT);\n\n\t\t\tif (num_str != null) {\n\t\t\t\ttry {\n\t\t\t\t\tactivity = Double.parseDouble(num_str);\n\t\t\t\t} catch (NumberFormatException nfe) {\n\t\t\t\t\tlog.warning(\"Incorrect activity field: \" + num_str);\n\t\t\t\t\tactivity = INITIAL_ACTIVITY_VAL;\n\t\t\t\t}\n\t\t\t}\n\t\t\tnum_str = roster_el.getAttributeStaticStr(WEIGHT_ATT);\n\t\t\tif (num_str != null) {\n\t\t\t\ttry {\n\t\t\t\t\tweight = Double.parseDouble(num_str);\n\t\t\t\t} catch (NumberFormatException nfe) {\n\t\t\t\t\tlog.warning(\"Incorrect weight field: \" + num_str);\n\t\t\t\t\tweight = INITIAL_WEIGHT_VAL;\n\t\t\t\t}\n\t\t\t}\n\t\t\tnum_str = roster_el.getAttributeStaticStr(LAST_SEEN_ATT);\n\t\t\tif (num_str != null) {\n\t\t\t\ttry {\n\t\t\t\t\tlastSeen = Long.parseLong(num_str);\n\t\t\t\t} catch (NumberFormatException nfe) {\n\t\t\t\t\tlog.warning(\"Incorrect last seen field: \" + num_str);\n\t\t\t\t\tlastSeen = INITIAL_LAST_SEEN_VAL;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// how roster can be modified as it is read from DB?\n\t\t\tmodified = false;\n\t\t} else {\n\t\t\tlog.warning(\"Incorrect roster data: \" + roster_el.toString());\n\t\t}\n\t}\n\n\tpublic RosterElement(JID jid, String name, String[] groups) {\n\t\tthis.stringpreped = XMPPStringPrepFactory.STRINGPREP_PROCESSOR;\n\t\tsetJid(jid);\n\t\tsetName(name);\n\t\tsetGroups(groups);\n\t\tthis.subscription = SubscriptionType.none;\n\t}\n\n\tpublic void addGroups(String[] groups) {\n\t\tif (groups != null) {\n\t\t\tif (this.groups == null) {\n\t\t\t\tsetGroups(groups);\n\t\t\t} else {\n\n\t\t\t\t// Groups names must be unique\n\t\t\t\tSet<String> groupsSet = new HashSet<String>();\n\n\t\t\t\tfor (String group : this.groups) {\n\t\t\t\t\tgroupsSet.add(group);\n\t\t\t\t}\n\t\t\t\tfor (String group : groups) {\n\t\t\t\t\tgroupsSet.add(group);\n\t\t\t\t}\n\t\t\t\tsetGroups(groupsSet.toArray(new String[groupsSet.size()]));\n\t\t\t}\n\t\t}\n\n\t}\n\n\tpublic String[] getGroups() {\n\t\treturn groups;\n\t}\n\n\tpublic final void setGroups(String[] groups) {\n\t\tif ((groups != null) && (groups.length > 0)) {\n\t\t\tthis.groups = new String[groups.length];\n\t\t\tfor (int i = 0; i < groups.length; i++) {\n\t\t\t\tthis.groups[i] = XMLUtils.unescape(groups[i]);\n\t\t\t}\n\t\t} else {\n\t\t\tthis.groups = null;\n\t\t}\n\t\tmodified = true;\n\t}\n\n\tpublic JID getJid() {\n\t\treturn jid;\n\t}\n\n\tprivate void setJid(String jid) throws TigaseStringprepException {\n\t\tif (XMPPStringPrepFactory.STRINGPREP_PROCESSOR.equals(stringpreped)) {\n\t\t\tthis.jid = JID.jidInstanceNS(jid);\n\t\t} else {\n\t\t\tthis.jid = JID.jidInstance(jid);\n\t\t\tmodified = true;\n\t\t}\n\t\tstringpreped = XMPPStringPrepFactory.STRINGPREP_PROCESSOR;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic final void setName(final String name) {\n\t\tif (name == this.name || (name != null && this.name != null && name.equals(this.name))) {\n\t\t\treturn;\n\t\t} else {\n\t\t\tthis.name = name == null ? null : XMLUtils.unescape(name);\n\t\t\tthis.modified = true;\n\t\t}\n\n//\t\tString old_name = this.name;\n//\t\tif (name == null) {\n//\t\t\tthis.name = this.jid.getLocalpart();\n//\t\t\tif ((this.name == null) || this.name.trim().isEmpty()) {\n//\t\t\t\tthis.name = this.jid.getBareJID().toString();\n//\t\t\t}\n//\t\t} else {\n//\t\t\tthis.name = XMLUtils.unescape(name);\n//\t\t}\n//\t\tif (!this.name.equals(old_name)) {\n//\t\t\tmodified = true;\n//\t\t}\n\t}\n\n\tpublic String getOtherData() {\n\t\treturn otherData;\n\t}\n\n\tpublic void setOtherData(String other_data) {\n\t\totherData = other_data;\n\t}\n\n\tpublic String getMixParticipantId() {\n\t\treturn mixParticipantId;\n\t}\n\n\tpublic void setMixParticipantId(String value) {\n\t\tthis.mixParticipantId = value;\n\t}\n\n\tpublic Element getRosterElement() {\n\t\tElement elem = new Element(ELEM_NAME, new String[]{JID_ATT, SUBS_ATT, STRINGPREP_ATT},\n\t\t\t\t\t\t\t\t   new String[]{jid.toString(), subscription.toString(), \"\" + stringpreped});\n\n\t\tif (name != null) {\n\t\t\telem.setAttribute(NAME_ATT, XMLUtils.escape(name));\n\t\t}\n\n\t\tif ((groups != null) && (groups.length > 0)) {\n\t\t\tString grps = \"\";\n\n\t\t\tfor (String group : groups) {\n\t\t\t\tgrps += XMLUtils.escape(group) + \",\";\n\t\t\t}\n\t\t\tgrps = grps.substring(0, grps.length() - 1);\n\t\t\telem.setAttribute(GRP_ATT, grps);\n\t\t}\n\t\tif (mixParticipantId != null) {\n\t\t\telem.setAttribute(MIX_PARTICIPANT_ID_ATT, mixParticipantId);\n\t\t}\n\t\tif (otherData != null) {\n\t\t\telem.setAttribute(OTHER_ATT, otherData);\n\t\t}\n\t\telem.setAttribute(ACTIVITY_ATT, Double.toString(activity));\n\t\telem.setAttribute(WEIGHT_ATT, Double.toString(weight));\n\t\telem.setAttribute(LAST_SEEN_ATT, Long.toString(lastSeen));\n\t\tmodified = false;\n\n\t\treturn elem;\n\t}\n\n\tpublic Element getRosterItem() {\n\n\t\t// This is actually not a good idea to cache the item element.\n\t\t// This causes a huge memory consumption and usually the item\n\t\t// is needed only once at the roster retrieving time.\n\t\t// if (item == null) {\n\t\tElement item = new Element(\"item\");\n\n\t\titem.setAttribute(\"jid\", jid.toString());\n\t\titem.addAttributes(subscription.getSubscriptionAttr());\n\t\tif (name != null) {\n\t\t\titem.setAttribute(\"name\", XMLUtils.escape(name));\n\t\t}\n\t\tif (groups != null) {\n\t\t\tfor (String gr : groups) {\n\t\t\t\tElement group = new Element(\"group\");\n\n\t\t\t\tgroup.setCData(XMLUtils.escape(gr));\n\t\t\t\titem.addChild(group);\n\t\t\t}    // end of for ()\n\t\t}      // end of if-else\n\t\treturn item;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getRosterItem().toString();\n\t}\n\n\tpublic SubscriptionType getSubscription() {\n\t\treturn subscription;\n\t}\n\n\tpublic void setSubscription(SubscriptionType subscription) {\n\t\tif (subscription == null) {\n\t\t\tthis.subscription = SubscriptionType.none;\n\t\t} else {\n\t\t\tthis.subscription = subscription;\n\t\t}\n\t\tmodified = true;\n\t}\n\n\tpublic boolean isModified() {\n\t\treturn modified;\n\t}\n\n\tpublic boolean isOnline() {\n\t\treturn onlineMap.size() > 0;\n\t}\n\n\tpublic boolean isPresence_sent() {\n\t\treturn presence_sent;\n\t}\n\n\tpublic void setPresence_sent(boolean presence_sent) {\n\t\tthis.presence_sent = presence_sent;\n\t}\n\n\tpublic void setOnline(String resource, boolean online) {\n\t\tif ((onlineMap != null) && (resource != null)) {\n\t\t\tif (online) {\n\t\t\t\tonlineMap.put(resource, Boolean.TRUE);\n\t\t\t} else {\n\t\t\t\tonlineMap.remove(resource);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic boolean isPersistent() {\n\t\treturn persistent;\n\t}\n\n\tpublic void setPersistent(boolean persistent) {\n\t\tthis.persistent = persistent;\n\t}\n\n\tpublic double getActivity() {\n\t\treturn activity;\n\t}\n\n\tpublic void setActivity(double activity) {\n\t\tthis.activity = activity;\n\t\tif (activity != 0) {\n\t\t\tweight = 1 / activity;\n\t\t}\n\t\tmodified = true;\n\t}\n\n\tpublic double getWeight() {\n\t\treturn weight;\n\t}\n\n\tpublic void setWeight(double weight) {\n\t\tthis.weight = weight;\n\t\tmodified = true;\n\t}\n\n\tpublic long getLastSeen() {\n\t\treturn lastSeen;\n\t}\n\n\tpublic void setLastSeen(long lastSeen) {\n\t\tthis.lastSeen = lastSeen;\n\t\tmodified = true;\n\t}\n\n\tprivate void setJid(JID jid) {\n\t\tthis.jid = jid;\n\t\tmodified = true;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/roster/RosterFactory.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.roster;\n\nimport tigase.eventbus.EventBus;\nimport tigase.kernel.beans.Initializable;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.kernel.beans.config.ConfigField;\n\n/**\n * {@link RosterFactory} is an factory that is responsible for creation appropriate instance of {@link RosterAbstract}\n * class\n * <br>\n * Created: Thu Sep 4 18:33:11 2008\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n*/\npublic abstract class RosterFactory {\n\n\t/** Key used to configure class name holding roster implementation */\n\tpublic static final String ROSTER_IMPL_PROP_KEY = \"roster-implementation\";\n\t/** Default roster implementation class - {@link RosterFactory} */\n\tpublic static final String ROSTER_IMPL_PROP_VAL = RosterFlat.class.getCanonicalName();\n\tpublic static String defaultRosterImplementation = ROSTER_IMPL_PROP_VAL;\n\t/** Holds shared implementation of {@link RosterAbstract} */\n\tprivate static RosterAbstract shared = null;\n\t/**\n\t * Creates new instance of class implementing {@link RosterAbstract} - either default one ({@link RosterFlat}) or\n\t * the one configured with <em>\"roster-implementation\"</em> property.\n\t *\n\t * @param shared_impl determines whether to returns shared or non shared implementation\n\t *\n\t * @return new instance of class implementing {@link RosterAbstract}\n\t */\n\tpublic static RosterAbstract getRosterImplementation(boolean shared_impl) {\n\t\ttry {\n\t\t\tif (shared_impl) {\n\t\t\t\treturn shared;\n\t\t\t}\n\t\t\treturn newRosterInstance(defaultRosterImplementation);\n\t\t} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic static RosterAbstract newRosterInstance(String class_name)\n\t\t\tthrows ClassNotFoundException, IllegalAccessException, InstantiationException {\n\t\treturn (RosterAbstract) Class.forName(class_name).newInstance();\n\t}\n\n\t@tigase.kernel.beans.Bean(name = \"rosterFactory\", exportable = true, active = true)\n\tpublic static class Bean\n\t\t\timplements Initializable, UnregisterAware {\n\n\t\t@ConfigField(desc = \"Roster implementation class\", alias = ROSTER_IMPL_PROP_KEY)\n\t\tprivate String defaultRosterImplementation = ROSTER_IMPL_PROP_VAL;\n\n\t\t@Inject\n\t\tprivate EventBus eventBus;\n\n\t\tpublic Bean() {\n\n\t\t}\n\n\t\tpublic void setDefaultRosterImplementation(String defaultRosterImplementation) {\n\t\t\tthis.defaultRosterImplementation = defaultRosterImplementation;\n\t\t\tRosterFactory.defaultRosterImplementation = defaultRosterImplementation;\n\t\t\ttry {\n\t\t\t\tRosterFactory.shared = newRosterInstance(defaultRosterImplementation);\n\t\t\t} catch (ClassNotFoundException | IllegalAccessException | InstantiationException ex) {\n\t\t\t\tthrow new RuntimeException(ex);\n\t\t\t}\n\t\t\tif (eventBus != null && shared != null) {\n\t\t\t\tsynchronized (RosterFactory.class) {\n\t\t\t\t\tshared.setEventBus(eventBus);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic void setEventBus(EventBus eventBus) {\n\t\t\tthis.eventBus = eventBus;\n\t\t\tsynchronized (RosterFactory.class) {\n\t\t\t\tif (shared != null) {\n\t\t\t\t\tshared.setEventBus(eventBus);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void beforeUnregister() {\n\t\t\tif (shared != null) {\n\t\t\t\tshared.setEventBus(null);\n\t\t\t}\n\t\t}\n\n\t\tpublic void initialize() {\n\t\t\tif (shared == null) {\n\t\t\t\ttry {\n\t\t\t\t\tRosterFactory.shared = newRosterInstance(defaultRosterImplementation);\n\t\t\t\t} catch (ClassNotFoundException | IllegalAccessException | InstantiationException ex) {\n\t\t\t\t\tthrow new RuntimeException(ex);\n\t\t\t\t}\n\t\t\t\tif (eventBus != null && shared != null) {\n\t\t\t\t\tsynchronized (RosterFactory.class) {\n\t\t\t\t\t\tshared.setEventBus(eventBus);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/roster/RosterFlat.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.roster;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.db.TigaseDBException;\nimport tigase.db.UserNotFoundException;\nimport tigase.db.UserRepository;\nimport tigase.server.PolicyViolationException;\nimport tigase.xml.DomBuilderHandler;\nimport tigase.xml.Element;\nimport tigase.xml.SimpleParser;\nimport tigase.xml.SingletonFactory;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.XMPPSession;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.text.SimpleDateFormat;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Describe class RosterFlat here.\n * <br>\n * Created: Tue Feb 21 18:05:53 2006\n *\n * @author <a href=\"mailto:artur.hefczyc@tigase.org\">Artur Hefczyc</a>\n */\npublic class RosterFlat\n\t\textends RosterAbstract {\n\n\tprivate static final Logger log = Logger.getLogger(RosterFlat.class.getName());\n\tprivate static final SimpleParser parser = SingletonFactory.getParserInstance();\n\n\tprivate final SimpleDateFormat formatter;\n\n\t{\n\t\tthis.formatter = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'\");\n\t\tthis.formatter.setTimeZone(TimeZone.getTimeZone(\"UTC\"));\n\t}\n\n\tpublic static boolean addBuddy(RosterElement relem, Map<BareJID, RosterElement> roster) {\n\t\tif (roster.size() < maxRosterSize) {\n\t\t\troster.put(relem.getJid().getBareJID(), relem);\n\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tpublic static boolean parseRosterUtil(String roster_str, Map<BareJID, RosterElement> roster,\n\t\t\t\t\t\t\t\t\t\t  XMPPResourceConnection session) {\n\t\tboolean result = false;\n\t\tDomBuilderHandler domHandler = new DomBuilderHandler();\n\n\t\tparser.parse(domHandler, roster_str.toCharArray(), 0, roster_str.length());\n\n\t\tQueue<Element> elems = domHandler.getParsedElements();\n\n\t\tif ((elems != null) && (elems.size() > 0)) {\n\t\t\tfor (Element elem : elems) {\n\t\t\t\ttry {\n\t\t\t\t\tRosterElement relem = new RosterElement(elem);\n\n\t\t\t\t\tresult |= relem.isModified();\n\t\t\t\t\tif (!addBuddy(relem, roster)) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tlog.log(Level.WARNING, \"Can't load roster element: \" + elem, e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic RosterElement addTempBuddy(JID buddy, XMPPResourceConnection session)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tRosterElement relem = getRosterElementInstance(buddy.copyWithoutResource(), null, null, session);\n\n\t\trelem.setPersistent(false);\n\t\trelem.setSubscription(null);\n\t\taddBuddy(relem, getUserRoster(session));\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Added temporary buddy to roster: {0}, name: {1}, item: {2}\",\n\t\t\t\t\tnew Object[]{relem.getJid(), relem.getName(), relem.getRosterItem(), relem.toString()});\n\t\t}\n\t\treturn relem;\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic void addBuddy(XMPPResourceConnection session, JID buddy, String name, String[] groups, SubscriptionType subscription, String otherData)\n\t\t\tthrows NotAuthorizedException, TigaseDBException, PolicyViolationException {\n\t\tthis.addBuddy(session, buddy, name, groups, subscription, null, otherData);\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic void addBuddy(XMPPResourceConnection session, JID buddy, String name, String[] groups, SubscriptionType subscription, String mixParticipantId, String otherData)\n\t\t\tthrows NotAuthorizedException, TigaseDBException, PolicyViolationException {\n\n\t\t// String buddy = JIDUtils.getNodeID(jid);\n\t\tRosterElement relem = getRosterElement(session, buddy);\n\n\t\tif (relem == null) {\n\t\t\tMap<BareJID, RosterElement> roster = getUserRoster(session);\n\n\t\t\trelem = getRosterElementInstance(buddy, name, groups, session);\n\t\t\tif (emptyNameAllowed && (name == null || name.isEmpty())) {\n\t\t\t\trelem.setName(null);\n\t\t\t} else if (name == null || name.isEmpty()) {\n\t\t\t\tString n = buddy.getLocalpart();\n\t\t\t\tif ((n == null) || n.trim().isEmpty()) {\n\t\t\t\t\tn = buddy.getBareJID().toString();\n\t\t\t\t}\n\t\t\t\trelem.setName(n);\n\t\t\t} else {\n\t\t\t\trelem.setName(name);\n\t\t\t}\n\t\t\trelem.setOtherData(otherData);\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"1. Added buddy to roster: {0}, name: {1}, item: {2}\",\n\t\t\t\t\t\tnew Object[]{relem.getJid(), relem.getName(), relem.getRosterItem()});\n\t\t\t}\n\t\t\tif (subscription != null) {\n\t\t\t\trelem.setSubscription(subscription);\n\t\t\t}\n\t\t\t// set it only if not null to make sure that roster item name change will not remove MIX annotation\n\t\t\tif (mixParticipantId != null) {\n\t\t\t\trelem.setMixParticipantId(mixParticipantId);\n\t\t\t}\n\t\t\tif (addBuddy(relem, roster)) {\n\t\t\t\tsaveUserRoster(session);\n\t\t\t\t// notify that roster element was changed!\n\t\t\t} else {\n\t\t\t\tthrow new PolicyViolationException(\"Too many elements in the user roster. Limit: \" + maxRosterSize);\n\t\t\t}\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"2. Added buddy to roster: {0}, name: {1}, item: {2}\",\n\t\t\t\t\t\tnew Object[]{relem.getJid(), relem.getName(), relem.getRosterItem()});\n\t\t\t}\n\t\t} else {\n\t\t\tif (emptyNameAllowed && (name == null || name.isEmpty())) {\n\t\t\t\trelem.setName(null);\n\t\t\t} else if (name == null || name.isEmpty()) {\n\t\t\t\tString n = buddy.getLocalpart();\n\t\t\t\tif ((n == null) || n.trim().isEmpty()) {\n\t\t\t\t\tn = buddy.getBareJID().toString();\n\t\t\t\t}\n\t\t\t\trelem.setName(n);\n\t\t\t} else {\n\t\t\t\trelem.setName(name);\n\t\t\t}\n\n\t\t\t// Hm, as one user reported this make it impossible to remove the user\n\t\t\t// from\n\t\t\t// all groups. Let's comments it out for now to see how it works.\n\t\t\t// Probably added this some time ago , before RosterFlat to prevent NPE.\n\t\t\t// if ((groups != null) && (groups.length > 0)) {\n\t\t\trelem.setGroups(groups);\n\n\t\t\t// }\n\t\t\trelem.setMixParticipantId(mixParticipantId);\n\t\t\trelem.setPersistent(true);\n\t\t\tif (subscription != null) {\n\t\t\t\trelem.setSubscription(subscription);\n\t\t\t}\n\t\t\tsaveUserRoster(session);\n\t\t\t// notify that roster element was changed!\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Updated buddy in roster: {0}\", buddy);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic boolean addBuddyGroup(XMPPResourceConnection session, JID buddy, String[] groups)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tRosterElement relem = getRosterElement(session, buddy);\n\n\t\tif (relem != null) {\n\t\t\trelem.addGroups(groups);\n\n\t\t\t// Intentionally not saving the roster here.\n\t\t\t// At the moment it is only used to combine dynamic roster with the\n\t\t\t// static roster in case a contact exist in both but in a different\n\t\t\t// group.\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic boolean containsBuddy(XMPPResourceConnection session, JID buddy)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tRosterElement relem = getRosterElement(session, buddy);\n\n\t\treturn (relem != null) && relem.isPersistent();\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic JID[] getBuddies(XMPPResourceConnection session) throws NotAuthorizedException, TigaseDBException {\n\t\tMap<BareJID, RosterElement> roster = getUserRoster(session);\n\n\t\tif (roster.size() == 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tJID[] result = new JID[roster.size()];\n\t\tint idx = 0;\n\n\t\tfor (RosterElement rosterElement : roster.values()) {\n\t\t\tresult[idx++] = rosterElement.getJid();\n\t\t}\n\n\t\t// TODO: this sorting should be optional as it may impact performance\n\t\tArrays.sort(result, new RosterElemComparator(roster));\n\n\t\treturn result;\n\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic String[] getBuddyGroups(XMPPResourceConnection session, JID buddy)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tRosterElement relem = getRosterElement(session, buddy);\n\n\t\tif (relem == null) {\n\t\t\treturn null;\n\t\t} else {\n\t\t\treturn relem.getGroups();\n\t\t}\n\t}\n\n\tprivate Element getBuddyItem(RosterElement relem) {\n\t\treturn relem.getRosterItem();\n\t}\n\n\tprivate Element getBuddyItem(final XMPPResourceConnection session, RosterElement relem) {\n\t\tElement item = getBuddyItem(relem);\n\t\tif (Boolean.TRUE.equals(session.getSessionData(\"urn:xmpp:mix:roster:0\"))) {\n\t\t\tString participantId = relem.getMixParticipantId();\n\t\t\tif (participantId != null) {\n\t\t\t\titem.addChild(new Element(\"channel\", new String[] { \"xmlns\", \"participant-id\" }, new String[] { \"urn:xmpp:mix:roster:0\", participantId }));\n\t\t\t}\n\t\t}\n\t\treturn item;\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic Element getBuddyItem(final XMPPResourceConnection session, JID buddy)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tRosterElement relem = getRosterElement(session, buddy);\n\n\t\tif (relem == null) {\n\t\t\treturn null;\n\t\t} else {\n\t\t\treturn getBuddyItem(session, relem);\n\t\t}\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic String getBuddyName(XMPPResourceConnection session, JID buddy)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tRosterElement relem = getRosterElement(session, buddy);\n\n\t\tif (relem == null) {\n\t\t\treturn null;\n\t\t} else {\n\t\t\treturn relem.getName();\n\t\t}\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic SubscriptionType getBuddySubscription(XMPPResourceConnection session, JID buddy)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tRosterElement relem = getRosterElement(session, buddy);\n\n\t\t// either we don't have such contact or it's not persistend in which case it shouldn't\n\t\t// have subscription -- this is simpler solution instead of reworking whole RosterElement\n\t\t// to allow sub==null\n\t\tif (relem == null || (!relem.isPersistent())) {\n\t\t\treturn null;\n\t\t} else {\n\t\t\treturn relem.getSubscription();\n\t\t}\n\n\t\t// return SubscriptionType.both;\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic String getMixParticipantId(XMPPResourceConnection session, JID buddy)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tRosterElement relem = getRosterElement(session, buddy);\n\n\t\tif (relem == null) {\n\t\t\treturn null;\n\t\t} else {\n\t\t\treturn relem.getMixParticipantId();\n\t\t}\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic RosterElement getRosterElementInstance(JID buddy, String name, String[] groups,\n\t\t\t\t\t\t\t\t\t\t\t\t  XMPPResourceConnection session) {\n\t\treturn new RosterElement(buddy.copyWithoutResource(), name, groups);\n\t}\n\n\t@Override\n\tpublic List<Element> getRosterItems(XMPPResourceConnection session)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tLinkedList<Element> items = new LinkedList<Element>();\n\t\tMap<BareJID, RosterElement> roster = getUserRoster(session);\n\n\t\tfor (RosterElement relem : roster.values()) {\n\n\t\t\t// Skip temporary roster elements added only for online presence tracking\n\t\t\t// from dynamic roster\n\t\t\tif (relem.isPersistent() && !SubscriptionType.none_pending_in.equals(relem.getSubscription())) {\n\t\t\t\titems.add(getBuddyItem(session, relem));\n\t\t\t}\n\t\t}\n\n\t\treturn items;\n\t}\n\n\t@Override\n\tpublic boolean isRosterLoaded(XMPPResourceConnection session) {\n\t\treturn session.getCommonSessionData(ROSTER) != null;\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic boolean isOnline(XMPPResourceConnection session, JID buddy)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tRosterElement relem = getRosterElement(session, buddy);\n\n\t\treturn (relem != null) && relem.isOnline();\n\t}\n\n\tpublic boolean parseRoster(String roster_str, Map<BareJID, RosterElement> roster, XMPPResourceConnection session) {\n\t\treturn parseRosterUtil(roster_str, roster, session);\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic boolean presenceSent(XMPPResourceConnection session, JID buddy)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tRosterElement relem = getRosterElement(session, buddy);\n\n\t\treturn (relem != null) && relem.isPresence_sent();\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic boolean removeBuddy(XMPPResourceConnection session, JID jid)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tMap<BareJID, RosterElement> roster = getUserRoster(session);\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Removing roster buddy: {0}, before removal: {1}\", new Object[]{jid, roster});\n\t\t}\n\t\troster.remove(jid.getBareJID());\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Removing roster buddy: {0}, after removal: {1}\", new Object[]{jid, roster});\n\t\t}\n\t\tsaveUserRoster(session);\n\t\t// notify that roster element was changed!\n\n\t\treturn true;\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic void setBuddyName(XMPPResourceConnection session, JID buddy, String name)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tRosterElement relem = getRosterElement(session, buddy);\n\n\t\tif (relem != null) {\n\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\tlog.log(Level.FINEST, \"Setting name: ''{0}'' for buddy: {1}\", new Object[]{name, buddy});\n\t\t\t}\n\t\t\tif (emptyNameAllowed && (name == null || name.isEmpty())) {\n\t\t\t\trelem.setName(null);\n\t\t\t} else if (name == null || name.isEmpty()) {\n\t\t\t\tString n = buddy.getLocalpart();\n\t\t\t\tif ((n == null) || n.trim().isEmpty()) {\n\t\t\t\t\tn = buddy.getBareJID().toString();\n\t\t\t\t}\n\t\t\t\trelem.setName(n);\n\t\t\t} else {\n\t\t\t\trelem.setName(name);\n\t\t\t}\n\t\t\tsaveUserRoster(session);\n\t\t\t// notify that roster element was changed!\n\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"Setting buddy name for non-existen contact: {0}\", buddy);\n\t\t}\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic void setBuddySubscription(XMPPResourceConnection session, SubscriptionType subscription, JID buddy)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tRosterElement relem = getRosterElement(session, buddy);\n\n\t\tif (relem != null) {\n\t\t\trelem.setSubscription(subscription);\n\t\t\tsaveUserRoster(session);\n\t\t\t// notify that roster element was changed!\n\n\t\t} else {\n\t\t\tlog.log(Level.WARNING, \"Missing roster contact for subscription set: {0}\", buddy);\n\t\t}\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic void setOnline(XMPPResourceConnection session, JID buddy, boolean online)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tRosterElement relem = getRosterElement(session, buddy);\n\n\t\tif (relem == null) {\n\t\t\trelem = addTempBuddy(buddy, session);\n\t\t}\n\t\trelem.setOnline(buddy.getResource(), online);\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic void setPresenceSent(XMPPResourceConnection session, JID buddy, boolean sent)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tRosterElement relem = getRosterElement(session, buddy);\n\n\t\tif (relem == null) {\n\t\t\trelem = addTempBuddy(buddy, session);\n\t\t}\n\t\trelem.setPresence_sent(sent);\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic RosterElement getRosterElement(XMPPResourceConnection session, JID buddy)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tMap<BareJID, RosterElement> roster = getUserRoster(session);\n\n\t\treturn roster.get(buddy.getBareJID());\n\t}\n\n\tpublic Map<BareJID, RosterElement> loadUserRoster(XMPPResourceConnection session)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\n\t\t// In most times we just read from this data structure\n\t\t// From time to time there might be some modification, posibly concurrent\n\t\t// very unlikely by more than one thread\n\t\tMap<BareJID, RosterElement> roster = new ConcurrentHashMap<BareJID, RosterElement>(100, 0.25f, 1);\n\n\t\tsession.putCommonSessionData(ROSTER, roster);\n\n\t\tString roster_str = session.getData(null, ROSTER, null);\n\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"Loaded user {1} roster: {0}\", new Object[]{roster_str, session.getjid()});\n\t\t}\n\t\tif ((roster_str != null) && !roster_str.isEmpty()) {\n\t\t\tboolean modified = parseRoster(roster_str, roster, session);\n\n\t\t\tif (modified) {\n\t\t\t\tsaveUserRoster(session);\n\t\t\t\t// notify that roster element was changed!\n\t\t\t}\n\t\t}\n\n\t\treturn roster;\n\t}\n\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic Function<JID, RosterElement> rosterElementProvider(XMPPResourceConnection session)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tXMPPSession commonSession = session.getParentSession();\n\t\tif (commonSession == null || !session.isAuthorized()) {\n\t\t\tthrow new NotAuthorizedException(\"No parent session set!\");\n\t\t}\n\t\tgetUserRoster(session);\n\n\t\treturn (jid) -> {\n\t\t\tMap<BareJID, RosterElement> roster = (Map<BareJID, RosterElement>) commonSession.getCommonSessionData(\n\t\t\t\t\tROSTER);\n\t\t\treturn roster == null ? null : roster.get(jid.getBareJID());\n\t\t};\n\t}\n\n\t@Override\n\t@Deprecated\n\t@TigaseDeprecated(since = \"8.2.0\", removeIn = \"9.0.0\", note = \"We should only allow BareJIDs\")\n\tpublic Element getCustomChild(XMPPResourceConnection session, JID buddy)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tRosterElement rel = getRosterElement(session, buddy);\n\n\t\tif (rel != null && rel.getLastSeen() > RosterElement.INITIAL_LAST_SEEN_VAL) {\n\t\t\tString stamp;\n\t\t\tsynchronized (formatter) {\n\t\t\t\tstamp = formatter.format(new Date(rel.getLastSeen()));\n\t\t\t}\n\n\t\t\treturn new Element(\"delay\", new String[]{\"stamp\", \"xmlns\"}, new String[]{stamp, \"urn:xmpp:delay\"});\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void logout(XMPPResourceConnection session) {\n\t\ttry {\n\t\t\tif (session.isAuthorized() && isModified(session)) {\n\t\t\t\tsaveUserRoster(session);\n\t\t\t\t// notify that roster element was changed!\n\t\t\t}\n\t\t} catch (NotAuthorizedException | TigaseDBException ex) {\n\t\t\tif (ex.getCause() instanceof UserNotFoundException) {\n\t\t\t\tlog.log(Level.FINEST, \"Error logging out user, user already removed? \" + session, ex);\n\t\t\t} else {\n\t\t\t\tlog.log(Level.WARNING, \"Error logging out user\", ex);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic boolean isModified(XMPPResourceConnection session) throws NotAuthorizedException, TigaseDBException {\n\t\tMap<BareJID, RosterElement> roster = getUserRoster(session);\n\t\tboolean result = false;\n\n\t\tif (roster != null) {\n\t\t\tfor (RosterElement rel : roster.values()) {\n\t\t\t\tresult |= rel.isModified();\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t@SuppressWarnings({\"unchecked\"})\n\tprotected Map<BareJID, RosterElement> getUserRoster(XMPPResourceConnection session)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tMap<BareJID, RosterElement> roster = null;\n\n\t\t// The method can be called from different plugins concurrently.\n\t\t// If the roster is not yet loaded from DB this causes concurent\n\t\t// access problems\n\t\tsynchronized (session) {\n\t\t\troster = (Map<BareJID, RosterElement>) session.getCommonSessionData(ROSTER);\n\t\t\tif (roster == null) {\n\t\t\t\troster = loadUserRoster(session);\n\t\t\t}\n\t\t\tif (getBuddiesHash(session) == null) {\n\t\t\t\tupdateRosterHash(session);\n\t\t\t}\n\t\t}\n\n\t\treturn roster;\n\t}\n\n\t@Override\n\tpublic void modifyStoredRoster(UserRepository repository, BareJID owner,\n\t\t\t\t\t\t\t\t   Consumer<Map<BareJID, RosterElement>> modifyRoster) throws TigaseDBException {\n\t\tString rosterStr = repository.getData(owner, null, RosterAbstract.ROSTER, null);\n\t\tMap<BareJID, RosterElement> roster = new LinkedHashMap<>();\n\t\tif (rosterStr != null) {\n\t\t\tRosterFlat.parseRosterUtil(rosterStr, roster, null);\n\t\t}\n\t\tmodifyRoster.accept(roster);\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (RosterElement relem : roster.values()) {\n\t\t\tsb.append(relem.getRosterElement().toString());\n\t\t}\n\t\trepository.setData(owner, null, RosterAbstract.ROSTER, sb.toString());\n\t}\n\n\tprotected void saveUserRoster(XMPPResourceConnection session) throws NotAuthorizedException, TigaseDBException {\n\t\tMap<BareJID, RosterElement> roster = getUserRoster(session);\n\t\tStringBuilder sb = new StringBuilder(5000);\n\n\t\tfor (RosterElement relem : roster.values()) {\n\t\t\tif (relem.isPersistent()) {\n\t\t\t\tsb.append(relem.getRosterElement().toString());\n\t\t\t}\n\t\t\t// here we could detect changed records, but is removed record changed??\n\t\t\t// no - item removed is gone!!!\n\t\t}\n\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\tlog.log(Level.FINEST, \"{0} | Saving user roster: {1}\",\n\t\t\t\t\tnew String[]{session.getBareJID().toString(), sb.toString()});\n\t\t}\n\t\tsession.setData(null, ROSTER, sb.toString());\n\n\t\t// here we should record changes? but how to detect that change was made and what was changed?\n\t\t//\n\t}\n\n\t@Override\n\tprotected void updateRosterItem(XMPPResourceConnection session, RosterModifiedEvent event)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\t// apply changes got from notification item\n\n\t\tMap<BareJID, RosterElement> roster = getUserRoster(session);\n\t\tif (event.getSubscription() == SubscriptionType.remove) {\n\t\t\troster.remove(event.getJid().getBareJID());\n\t\t\treturn;\n\t\t}\n\n\t\tRosterElement element = roster.get(event.getJid().getBareJID());\n\t\tif (element == null) {\n\t\t\telement = new RosterElement(event.getJid(), event.getName(), event.getGroups());\n\t\t\telement.setSubscription(event.getSubscription());\n\t\t\telement.setMixParticipantId(event.getMixParticipantId());\n\t\t\taddBuddy(element, getUserRoster(session));\n\t\t} else {\n\t\t\telement.setName(event.getName());\n\t\t\telement.setGroups(event.getGroups());\n\t\t\telement.setSubscription(event.getSubscription());\n\t\t\telement.setMixParticipantId(event.getMixParticipantId());\n\t\t}\n\n\t\tsuper.updateRosterItem(session, event);\n\t}\n\n\tprivate class RosterElemComparator\n\t\t\timplements Comparator<JID> {\n\n\t\tprivate Map<BareJID, RosterElement> roster = null;\n\n\t\tprivate RosterElemComparator(Map<BareJID, RosterElement> roster) {\n\t\t\tthis.roster = roster;\n\t\t}\n\n\t\t@Override\n\t\tpublic int compare(JID arg0, JID arg1) {\n\t\t\tdouble w0 = roster.get(arg0.getBareJID()).getWeight();\n\t\t\tdouble w1 = roster.get(arg1.getBareJID()).getWeight();\n\n\t\t\treturn Double.compare(w0, w1);\n\t\t}\n\t}\n}    // RosterFlat\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/impl/roster/RosterRetrievingException.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.roster;\n\nimport tigase.xmpp.XMPPException;\n\n/**\n * @author kobit\n */\npublic class RosterRetrievingException\n\t\textends XMPPException {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * Creates a new <code>PacketErrorTypeException</code> instance.\n\t *\n\t */\n\tpublic RosterRetrievingException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic RosterRetrievingException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n}\n\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/mam/ExtendedQuery.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.mam;\n\nimport java.util.Collection;\n\npublic interface ExtendedQuery extends Query {\n\n\tString getBeforeId();\n\n\tvoid setBeforeId(String id);\n\n\tString getAfterId();\n\n\tvoid setAfterId(String id);\n\n\tCollection<String> getIds();\n\n\tvoid setIds(Collection<String> ids);\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/mam/ExtendedQueryImpl.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.mam;\n\nimport java.util.Collection;\nimport java.util.Collections;\n\npublic class ExtendedQueryImpl\n\t\textends QueryImpl\n\t\timplements ExtendedQuery {\n\n\tprivate String beforeId;\n\tprivate String afterId;\n\tprivate Collection<String> ids = Collections.emptyList();\n\n\tpublic ExtendedQueryImpl() {\n\t}\n\n\t@Override\n\tpublic String getBeforeId() {\n\t\treturn beforeId;\n\t}\n\n\t@Override\n\tpublic void setBeforeId(String id) {\n\t\tbeforeId = id;\n\t}\n\n\t@Override\n\tpublic String getAfterId() {\n\t\treturn afterId;\n\t}\n\n\t@Override\n\tpublic void setAfterId(String id) {\n\t\tafterId = id;\n\t}\n\n\t@Override\n\tpublic Collection<String> getIds() {\n\t\treturn ids;\n\t}\n\n\t@Override\n\tpublic void setIds(Collection<String> ids) {\n\t\tif (ids != null) {\n\t\t\tthis.ids = ids;\n\t\t} else {\n\t\t\tthis.ids = Collections.emptyList();\n\t\t}\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tfinal StringBuffer sb = new StringBuffer(\"ExtendedQueryImpl{\");\n\t\tsb.append(\"QueryImpl='\").append(super.toString()).append('\\'');\n\t\tsb.append(\", beforeId='\").append(beforeId).append('\\'');\n\t\tsb.append(\", afterId='\").append(afterId).append('\\'');\n\t\tsb.append(\", ids=\").append(ids);\n\t\tsb.append('}');\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/mam/MAM2ExtendedQueryParser.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.mam;\n\nimport tigase.component.exceptions.ComponentException;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.DataForm;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Optional;\nimport java.util.stream.Stream;\n\npublic class MAM2ExtendedQueryParser<Query extends tigase.xmpp.mam.Query> extends MAM2QueryParser<Query> {\n\n\tprotected static final String MAM2_EXTENDED_XMLNS = MAM2_XMLNS + \"#extended\";\n\n\t@Inject\n\tprivate MAMRepository mamRepository;\n\n\tpublic MAM2ExtendedQueryParser() {\n\t\tthis(Stream.empty());\n\t}\n\n\tprotected MAM2ExtendedQueryParser(Stream<String> additionalNamespaces) {\n\t\tsuper(Stream.concat(additionalNamespaces, Stream.of(MAM2_EXTENDED_XMLNS)));\n\t}\n\n\t@Override\n\tpublic Query parseQuery(Query query, Packet packet) throws ComponentException {\n\t\tQuery result = super.parseQuery(query, packet);\n\t\tElement queryEl = packet.getElement().getChildStaticStr(\"query\");\n\t\tif (query instanceof ExtendedQuery) {\n\t\t\tExtendedQuery extQuery = (ExtendedQuery) result;\n\t\t\textQuery.setBeforeId(DataForm.getFieldValue(queryEl, \"before-id\"));\n\t\t\textQuery.setAfterId(DataForm.getFieldValue(queryEl, \"after-id\"));\n\t\t\textQuery.setIds(Optional.ofNullable(DataForm.getFieldValues(queryEl, \"ids\"))\n\t\t\t\t\t\t\t\t  .map(Arrays::asList)\n\t\t\t\t\t\t\t\t  .orElseGet(Collections::emptyList));\n\t\t}\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic Element prepareForm(Element elem, String xmlns, Packet packet) {\n\t\tElement form = super.prepareForm(elem, xmlns, packet);\n\t\tElement x = form.getChild(\"x\", \"jabber:x:data\");\n\t\tif (x != null && xmlns == MAM2_XMLNS) {\n\t\t\tJID from = packet.getStanzaFrom();\n\t\t\tif (from != null) {\n\t\t\t\tif (mamRepository.newQuery(from.getBareJID()) instanceof ExtendedQuery) {\n\t\t\t\t\taddField(x, \"after-id\", \"text-single\", \"After item with ID\");\n\t\t\t\t\taddField(x, \"before-id\", \"text-single\", \"Before item with ID\");\n\t\t\t\t\taddField(x, \"ids\", \"text-multi\", \"Item IDs\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn form;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/mam/MAM2QueryParser.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.mam;\n\nimport java.util.stream.Stream;\n\npublic class MAM2QueryParser<Query extends tigase.xmpp.mam.Query> extends MAMQueryParser<Query> {\n\n\tprotected static final String MAM2_XMLNS = \"urn:xmpp:mam:2\";\n\t\n\tpublic MAM2QueryParser() {\n\t\tthis(Stream.empty());\n\t}\n\n\tprotected MAM2QueryParser(Stream<String> additionalNamespaces) {\n\t\tsuper(Stream.concat(additionalNamespaces, Stream.of(MAM2_XMLNS)));\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/mam/MAMItemHandler.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.mam;\n\nimport tigase.component.PacketWriter;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Message;\nimport tigase.server.Packet;\nimport tigase.server.Priority;\nimport tigase.util.datetime.TimestampHelper;\nimport tigase.xml.Element;\nimport tigase.xmpp.mam.modules.QueryModule;\n\n/**\n * Basic implementation of handler processing items found in repository and converting into forward messages for\n * delivery to client as specified in XEP-0313: Message Archive Management\n * <br>\n * Created by andrzej on 19.07.2016.\n */\n@Bean(name = \"mamItemHandler\", parent = QueryModule.class, active = true)\npublic class MAMItemHandler\n\t\timplements MAMRepository.ItemHandler {\n\n\tprivate static final TimestampHelper TIMESTAMP_FORMATTER = new TimestampHelper();\n\tprivate final static System.Logger log = System.getLogger(MAMItemHandler.class.getName());\n\n\t@Inject\n\tprivate PacketWriter packetWriter;\n\n\t@Override\n\tpublic void itemFound(Query query, MAMRepository.Item item) {\n\t\tElement m = new Element(\"message\");\n\t\tElement result = prepareResult(query, item);\n\t\tm.addChild(result);\n\n\t\tMessage packet = new Message(m, query.getComponentJID(), query.getQuestionerJID());\n\t\tpacket.setXMLNS(Packet.CLIENT_XMLNS);\n\t\tpacket.setPriority(Priority.HIGH);\n\n\t\tif (query.getRsm().getFirst() == null) {\n\t\t\tquery.getRsm().setFirst(item.getId());\n\t\t}\n\t\tquery.getRsm().setLast(item.getId());\n\t\tlog.log(System.Logger.Level.TRACE,\n\t\t\t\t() -> \"Processing item: \" + item + \" from query: \" + query + \", resulting packet: \" + packet);\n\n\t\tpacketWriter.write(packet);\n\t}\n\n\tprotected Element prepareResult(Query query, MAMRepository.Item item) {\n\t\tElement result = new Element(\"result\", new String[]{\"xmlns\", \"id\"},\n\t\t\t\t\t\t\t\t\t new String[]{query.getXMLNS(), item.getId()});\n\t\tif (query.getId() != null) {\n\t\t\tresult.setAttribute(\"queryid\", query.getId());\n\t\t}\n\t\tElement forwarded = new Element(\"forwarded\", new String[]{\"xmlns\"}, new String[]{\"urn:xmpp:forward:0\"});\n\t\tresult.addChild(forwarded);\n\n\t\tString timestampStr = TIMESTAMP_FORMATTER.formatWithMs(item.getTimestamp());\n\n\t\tElement delay = new Element(\"delay\", new String[]{\"xmlns\", \"stamp\"},\n\t\t\t\t\t\t\t\t\tnew String[]{\"urn:xmpp:delay\", timestampStr});\n\t\tforwarded.addChild(delay);\n\n\t\tif (item.getMessage() != null) {\n\t\t\tforwarded.addChild(item.getMessage());\n\t\t}\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/mam/MAMQueryParser.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.mam;\n\nimport tigase.component.exceptions.ComponentException;\nimport tigase.kernel.beans.Bean;\nimport tigase.server.Command;\nimport tigase.server.DataForm;\nimport tigase.server.Packet;\nimport tigase.util.datetime.TimestampHelper;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.jid.JID;\nimport tigase.xmpp.rsm.RSM;\n\nimport java.text.ParseException;\nimport java.util.Set;\nimport java.util.UUID;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * Implementation of parser for XEP-0313: Message Archive Management\n * <br>\n * Created by andrzej on 19.07.2016.\n */\n@Bean(name = \"mamQueryParser\", active = true)\npublic class MAMQueryParser<Query extends tigase.xmpp.mam.Query>\n\t\timplements QueryParser<Query> {\n\n\tprotected static final String MAM_XMLNS = \"urn:xmpp:mam:1\";\n\n\tprivate final TimestampHelper timestampHelper = new TimestampHelper();\n\n\tprivate final Set<String> XMLNNS;\n\n\tpublic MAMQueryParser() {\n\t\tthis(Stream.empty());\n\t}\n\n\tprotected MAMQueryParser(Stream<String> additionalNamespaces) {\n\t\tXMLNNS = Stream.concat(additionalNamespaces, Stream.of(MAM_XMLNS)).collect(Collectors.toUnmodifiableSet());\n\t}\n\n\t@Override\n\tpublic Set<String> getXMLNSs() {\n\t\treturn XMLNNS;\n\t}\n\n\t@Override\n\tpublic Query parseQuery(Query query, Packet packet) throws ComponentException {\n\t\tElement queryEl = packet.getElement().getChildStaticStr(\"query\");\n\t\tquery.setXMLNS(queryEl.getXMLNS());\n\n\t\tquery.setQuestionerJID(packet.getStanzaFrom());\n\t\tquery.setComponentJID(packet.getStanzaTo());\n\n\t\tquery.setId(queryEl.getAttributeStaticStr(\"queryid\"));\n\n\t\tif (queryEl.getChild(\"x\", \"jabber:x:data\") == null) {\n\t\t\tquery.getRsm().fromElement(queryEl);\n\t\t\tvalidateRsm(query.getRsm());\n\t\t\t\n\t\t\treturn query;\n\t\t}\n\n\t\tif (!getXMLNSs().contains(DataForm.getFieldValue(queryEl, \"FORM_TYPE\"))) {\n\t\t\tthrow new ComponentException(Authorization.BAD_REQUEST, \"Invalid form type\");\n\t\t}\n\n\t\tString start = DataForm.getFieldValue(queryEl, \"start\");\n\t\ttry {\n\t\t\tquery.setStart(timestampHelper.parseTimestamp(start));\n\t\t} catch (ParseException ex) {\n\t\t\tthrow new ComponentException(Authorization.BAD_REQUEST, \"Invalid value in 'start' field\", ex);\n\t\t}\n\n\t\tString end = DataForm.getFieldValue(queryEl, \"end\");\n\t\ttry {\n\t\t\tquery.setEnd(timestampHelper.parseTimestamp(end));\n\t\t} catch (ParseException ex) {\n\t\t\tthrow new ComponentException(Authorization.BAD_REQUEST, \"Invalid value in 'end' field\", ex);\n\t\t}\n\n\t\tString with = DataForm.getFieldValue(queryEl, \"with\");\n\t\tif (with != null && !with.isEmpty()) {\n\t\t\ttry {\n\t\t\t\tquery.setWith(JID.jidInstance(with));\n\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\tthrow new ComponentException(Authorization.BAD_REQUEST, \"Invalid value in 'with' field\", ex);\n\t\t\t}\n\t\t}\n\n\t\tquery.getRsm().fromElement(queryEl);\n\t\tvalidateRsm(query.getRsm());\n\n\t\treturn query;\n\t}\n\n\t@Override\n\tpublic Element prepareForm(Element elem) {\n\t\treturn prepareForm(elem, MAM_XMLNS);\n\t}\n\n\t@Override\n\tpublic Element prepareForm(Element elem, String xmlns, Packet packet) {\n\t\tElement x = DataForm.addDataForm(elem, Command.DataType.form);\n\t\tDataForm.addHiddenField(elem, \"FORM_TYPE\", xmlns);\n\n\t\taddField(x, \"with\", \"jid-single\", \"With\");\n\t\taddField(x, \"start\", \"jid-single\", \"Start\");\n\t\taddField(x, \"end\", \"jid-single\", \"End\");\n\n\t\treturn elem;\n\t}\n\n\tprotected void addField(Element x, String var, String type, String label) {\n\t\tElement field = new Element(\"field\", new String[]{\"type\", \"var\"}, new String[]{type, var});\n\t\tif (label != null) {\n\t\t\tfield.setAttribute(\"label\", label);\n\t\t}\n\t\tx.addChild(field);\n\t}\n\n\tprotected void validateRsm(RSM rsm) throws ComponentException {\n\t\tassertIsUUID(rsm.getAfter());\n\t\tassertIsUUID(rsm.getBefore());\n\t}\n\n\tprotected void assertIsUUID(String uuid) throws ComponentException {\n\t\tif (uuid == null) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tUUID.fromString(uuid);\n\t\t} catch (IllegalArgumentException ex) {\n\t\t\tthrow new ComponentException(Authorization.NOT_ACCEPTABLE, \"Invalid item id: \" + uuid, ex);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/mam/MAMRepository.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.mam;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.component.exceptions.ComponentException;\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.Date;\n\n/**\n * Base interface which is required to be implemented by class which should be used as repository implementation for\n * quering using XEP-0313: Message Archive Management\n * <br>\n * Created by andrzej on 19.07.2016.\n */\npublic interface MAMRepository<Q extends Query, I extends MAMRepository.Item> {\n\n//\tint countItems(Q query);\n\n\tvoid queryItems(Q query, ItemHandler<Q, I> itemHandler) throws RepositoryException, ComponentException;\n\n\t@TigaseDeprecated(removeIn = \"9.0.0\", note = \"Use method with `jid` containing jid of queried entity archive\", since = \"8.3.0\")\n\t@Deprecated\n\tQ newQuery();\n\n\tdefault Q newQuery(BareJID jid) {\n\t\treturn newQuery();\n\t}\n\n\tinterface Item {\n\n\t\tString getId();\n\n\t\tElement getMessage();\n\n\t\tDate getTimestamp();\n\t}\n\n\tinterface ItemHandler<Q extends Query, I extends Item> {\n\n\t\tvoid itemFound(Q query, I item);\n\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/mam/Query.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.mam;\n\nimport tigase.xmpp.jid.JID;\nimport tigase.xmpp.rsm.RSM;\n\nimport java.util.Date;\n\n/**\n * Interface defining methods required by base version MAMRepository to execute queries. Custom implementations of\n * MAMRepository may use additional methods.\n * <br>\n * Created by andrzej on 19.07.2016.\n */\npublic interface Query {\n\n\tdefault String getXMLNS() {\n\t\treturn MAMQueryParser.MAM_XMLNS;\n\t}\n\n\tdefault void setXMLNS(String xmlns) {\n\t\t// nothing to do..\n\t}\n\n\tJID getQuestionerJID();\n\n\tvoid setQuestionerJID(JID questionerJID);\n\n\tJID getComponentJID();\n\n\tvoid setComponentJID(JID componentJID);\n\n\tString getId();\n\n\tvoid setId(String id);\n\n\tDate getStart();\n\n\tvoid setStart(Date start);\n\n\tDate getEnd();\n\n\tvoid setEnd(Date end);\n\n\tJID getWith();\n\n\tvoid setWith(JID with);\n\n\tRSM getRsm();\n\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/mam/QueryImpl.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.mam;\n\nimport tigase.xmpp.jid.JID;\nimport tigase.xmpp.rsm.RSM;\n\nimport java.util.Date;\n\n/**\n * Class implements Query interface and is base implementation of query holder used by implementation of XEP-0313:\n * Message Archive Management\n * <br>\n * Created by andrzej on 21.07.2016.\n */\npublic class QueryImpl\n\t\timplements Query {\n\n\tprivate String xmlns = \"urn:xmpp:mam:1\";\n\tprivate final RSM rsm = new RSM();\n\tprivate JID componentJID;\n\tprivate Date end;\n\tprivate String id;\n\tprivate JID questionerJID;\n\tprivate Date start;\n\tprivate JID with;\n\n\tpublic QueryImpl() {\n\n\t}\n\n\tpublic String getXMLNS() {\n\t\treturn xmlns;\n\t}\n\n\t@Override\n\tpublic void setXMLNS(String xmlns) {\n\t\tthis.xmlns = xmlns;\n\t}\n\n\tpublic void setXmlns(String xmlns) {\n\t\tthis.xmlns = xmlns;\n\t}\n\n\tpublic JID getQuestionerJID() {\n\t\treturn questionerJID;\n\t}\n\n\tpublic void setQuestionerJID(JID questionerJID) {\n\t\tthis.questionerJID = questionerJID;\n\t}\n\n\tpublic JID getComponentJID() {\n\t\treturn componentJID;\n\t}\n\n\tpublic void setComponentJID(JID componentJID) {\n\t\tthis.componentJID = componentJID;\n\t}\n\n\tpublic String getId() {\n\t\treturn id;\n\t}\n\n\tpublic void setId(String id) {\n\t\tthis.id = id;\n\t}\n\n\tpublic Date getStart() {\n\t\treturn start;\n\t}\n\n\tpublic void setStart(Date start) {\n\t\tthis.start = start;\n\t}\n\n\tpublic Date getEnd() {\n\t\treturn end;\n\t}\n\n\tpublic void setEnd(Date end) {\n\t\tthis.end = end;\n\t}\n\n\tpublic JID getWith() {\n\t\treturn with;\n\t}\n\n\tpublic void setWith(JID with) {\n\t\tthis.with = with;\n\t}\n\n\tpublic RSM getRsm() {\n\t\treturn rsm;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tfinal StringBuffer sb = new StringBuffer(\"QueryImpl{\");\n\t\tsb.append(\"xmlns='\").append(xmlns).append('\\'');\n\t\tsb.append(\", componentJID=\").append(componentJID);\n\t\tsb.append(\", questionerJID=\").append(questionerJID);\n\t\tsb.append(\", id='\").append(id).append('\\'');\n\t\tsb.append(\", start=\").append(start);\n\t\tsb.append(\", end=\").append(end);\n\t\tsb.append(\", with=\").append(with);\n\t\tsb.append(\", rsm=\").append(rsm);\n\t\tsb.append('}');\n\t\treturn sb.toString();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/mam/QueryParser.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.mam;\n\nimport tigase.annotations.TigaseDeprecated;\nimport tigase.component.exceptions.ComponentException;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\n\nimport java.util.Collections;\nimport java.util.Set;\n\n/**\n * Interface of which class instance is used by QueryModule to process incoming stanzas into query.\n * <br>\n * Created by andrzej on 19.07.2016.\n */\npublic interface QueryParser<Q extends Query> {\n\n\tdefault Set<String> getXMLNSs() {\n\t\treturn Collections.singleton(MAMQueryParser.MAM_XMLNS);\n\t}\n\n\tQ parseQuery(Q query, Packet packet) throws ComponentException;\n\n\tdefault Element prepareForm(Element elem, String xmlns, Packet packet) {\n\t\treturn prepareForm(elem, xmlns);\n\t}\n\n\t@TigaseDeprecated(removeIn = \"9.0.0\", note = \"Use method with `xmlns` and `packet` paramters\", since = \"8.3.0\")\n\t@Deprecated\n\tdefault Element prepareForm(Element elem, String xmlns) {\n\t\treturn prepareForm(elem);\n\t}\n\n\t@TigaseDeprecated(removeIn = \"9.0.0\", note = \"Use method with `xmlns` and `packet` paramters\", since = \"8.3.0\")\n\t@Deprecated\n\tElement prepareForm(Element elem);\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/mam/modules/GetFormModule.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.mam.modules;\n\nimport tigase.component.PacketWriter;\nimport tigase.component.exceptions.ComponentException;\nimport tigase.component.modules.Module;\nimport tigase.criteria.Criteria;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Packet;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.mam.Query;\nimport tigase.xmpp.mam.QueryParser;\n\n/**\n * Implementation of module responsible for handling request to retrive form used in XEP-0313: Message Archive\n * Management\n * <br>\n * Created by andrzej on 19.07.2016.\n */\n@Bean(name = \"mamGetFormModule\", active = true)\npublic class GetFormModule\n\t\timplements Module {\n\n\t@Inject\n\tprivate PacketWriter packetWriter;\n\t@Inject(bean = \"mamQueryParser\")\n\tprivate QueryParser<Query> queryParser;\n\n\t@Override\n\tpublic String[] getFeatures() {\n\t\treturn new String[0];\n\t}\n\n\t@Override\n\tpublic Criteria getModuleCriteria() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean canHandle(Packet packet) {\n\t\treturn packet.getElement().findChild(child -> child.getName() == \"query\" && queryParser.getXMLNSs().contains(child.getXMLNS())) != null &&\n\t\t\t\tpacket.getType() == StanzaType.get;\n\t}\n\n\t@Override\n\tpublic void process(Packet packet) throws ComponentException, TigaseStringprepException {\n\t\tElement reqQuery = packet.getElement().findChild(child -> child.getName() == \"query\" && queryParser.getXMLNSs().contains(child.getXMLNS()));\n\t\tElement query = new Element(\"query\");\n\t\tquery.setXMLNS(reqQuery.getXMLNS());\n\n\t\tqueryParser.prepareForm(query, reqQuery.getXMLNS(), packet);\n\n\t\tpacketWriter.write(packet.okResult(query, 0));\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/mam/modules/QueryModule.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.mam.modules;\n\nimport tigase.component.PacketWriter;\nimport tigase.component.exceptions.ComponentException;\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.component.modules.Module;\nimport tigase.criteria.Criteria;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.server.Packet;\nimport tigase.server.Priority;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.mam.ExtendedQuery;\nimport tigase.xmpp.mam.MAMRepository;\nimport tigase.xmpp.mam.Query;\nimport tigase.xmpp.mam.QueryParser;\n\n/**\n * Implementation of module processing requests to retrieve items using XEP-0313: Message Archive Management\n * <br>\n * Created by andrzej on 19.07.2016.\n */\n@Bean(name = \"mamQueryModule\", active = true)\npublic class QueryModule\n\t\timplements Module {\n\n\tprivate static final String[] FEATURES = {\"urn:xmpp:mam:1\"};\n\t@Inject(bean = \"mamItemHandler\")\n\tprivate MAMRepository.ItemHandler itemHandler;\n\t@Inject\n\tprivate MAMRepository mamRepository;\n\t@Inject\n\tprivate PacketWriter packetWriter;\n\t@Inject(bean = \"mamQueryParser\")\n\tprivate QueryParser queryParser;\n\n\tprivate final static System.Logger log = System.getLogger(QueryModule.class.getName());\n\n\t@Override\n\tpublic String[] getFeatures() {\n\t\treturn (String[]) queryParser.getXMLNSs().toArray(String[]::new);\n\t}\n\n\t@Override\n\tpublic Criteria getModuleCriteria() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic boolean canHandle(Packet packet) {\n\t\treturn packet.getElement().findChild(child -> child.getName() == \"query\" && isXMLNSSupported(child.getXMLNS())) != null &&\n\t\t\t\tpacket.getType() == StanzaType.set;\n\t}\n\n\tprotected boolean isXMLNSSupported(String xmlns) {\n\t\treturn queryParser.getXMLNSs().contains(xmlns);\n\t}\n\n\t@Override\n\tpublic void process(Packet packet) throws ComponentException, TigaseStringprepException {\n\t\tQuery query = queryParser.parseQuery(mamRepository.newQuery(getArchiveOwner(packet)), packet);\n\t\tlog.log(System.Logger.Level.TRACE, () -> \"Retrieving items for packet: \" + packet + \" using query: \" + query);\n\t\ttry {\n\t\t\tmamRepository.queryItems(query, itemHandler);\n\t\t} catch (RepositoryException ex) {\n\t\t\tthrow new RuntimeException(\"Error retrieving messages from database\", ex);\n\t\t}\n\n\t\tElement fin = new Element(\"fin\");\n\t\tfin.setXMLNS(query.getXMLNS());\n\t\tif (query instanceof ExtendedQuery && !((ExtendedQuery) query).getIds().isEmpty()) {\n\t\t\t// do not add RSM, as we were asked about items with specified list of ids\n\t\t} else {\n\t\t\tfin.addChild(query.getRsm().toElement());\n\t\t\tif (query.getRsm().getIndex() + query.getRsm().getMax() >= query.getRsm().getCount()) {\n\t\t\t\tfin.setAttribute(\"complete\", \"true\");\n\t\t\t}\n\t\t}\n\n\t\tPacket result = packet.okResult(fin, 0);\n\t\tresult.setPacketFrom(null);\n\t\tresult.setPriority(Priority.LOW);\n\n\t\tpacketWriter.write(result);\n\t}\n\n\tprotected BareJID getArchiveOwner(Packet packet) {\n\t\tif (packet.getStanzaTo() != null) {\n\t\t\treturn packet.getStanzaTo().getBareJID();\n\t\t}\n\t\treturn packet.getStanzaFrom().getBareJID();\n\t}\n\t\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/mam/util/MAMRepositoryManagerExtensionHelper.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.mam.util;\n\nimport tigase.db.util.importexport.Exporter;\nimport tigase.server.Message;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\nimport tigase.xmpp.mam.ExtendedQuery;\nimport tigase.xmpp.mam.MAMItemHandler;\nimport tigase.xmpp.mam.MAMRepository;\nimport tigase.xmpp.mam.Query;\n\nimport java.io.Writer;\nimport java.util.Date;\nimport java.util.Optional;\nimport java.util.concurrent.atomic.AtomicReference;\nimport java.util.function.BiConsumer;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class MAMRepositoryManagerExtensionHelper {\n\t\n\tprivate static Logger log = Logger.getLogger(MAMRepositoryManagerExtensionHelper.class.getCanonicalName());\n\n\tpublic static void exportDataFromRepository(MAMRepository mamRepository, BareJID repoJID, BareJID askingJID, Writer archiveWriter) throws Exception {\n\t\texportDataFromRepository(mamRepository, repoJID, askingJID, null, archiveWriter);\n\t}\n\tpublic static void exportDataFromRepository(MAMRepository mamRepository, BareJID repoJID, BareJID askingJID, BiConsumer<MAMRepository.Item,Element> outputModifier, Writer archiveWriter) throws Exception {\n\t\tarchiveWriter.append(\"<archive xmlns='urn:xmpp:pie:0#mam' xmlns:xi='http://www.w3.org/2001/XInclude'>\");\n\t\tQuery query = mamRepository.newQuery(repoJID);\n\t\tquery.setComponentJID(JID.jidInstance(repoJID));\n\t\tquery.setQuestionerJID(JID.jidInstance(askingJID));\n\t\tquery.setXMLNS(\"urn:xmpp:mam:2\");\n\t\tExporter.getExportMAMSinceValue().ifPresent(query::setStart);\n\t\tquery.getRsm().setMax(Exporter.getExportMAMBatchSize());\n\n\t\tAtomicReference<MAMRepository.Item> lastItem = new AtomicReference<>();\n\t\tint batchNo = 0;\n\t\tInteger itemsToExport = null;\n\t\twhile (true) {\n\t\t\tmamRepository.queryItems(query, new MAMItemHandler() {\n\t\t\t\t@Override\n\t\t\t\tpublic void itemFound(Query query, MAMRepository.Item item) {\n\t\t\t\t\tlastItem.set(item);\n\t\t\t\t\tElement result = this.prepareResult(query, item);\n\t\t\t\t\tif (result != null) {\n\t\t\t\t\t\tif (outputModifier != null) {\n\t\t\t\t\t\t\toutputModifier.accept(item, result);\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tarchiveWriter.append(result.toString());\n\t\t\t\t\t\t} catch (Throwable ex) {\n\t\t\t\t\t\t\tlog.log(Level.SEVERE, ex.getMessage(), ex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t\tif (lastItem.get() == null) {\n\t\t\t\tbreak;\n\t\t\t} else {\n\t\t\t\tif (itemsToExport == null) {\n\t\t\t\t\titemsToExport = query.getRsm().getCount();\n\t\t\t\t}\n\t\t\t\tif (itemsToExport != null) {\n\t\t\t\t\t++batchNo;\n\t\t\t\t\tint completed = Math.min(batchNo * Exporter.getExportMAMBatchSize(), itemsToExport);\n\t\t\t\t\tint percent = (completed * 100) / itemsToExport;\n\t\t\t\t\tif (batchNo > 1 || percent < 100) {\n\t\t\t\t\t\tlog.info(\"exported MAM archive for \" + repoJID + \" batch no. \" + batchNo + \", \" + (percent) +\n\t\t\t\t\t\t\t\t\t\t \"%...\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (query instanceof ExtendedQuery extendedQuery) {\n\t\t\t\t\textendedQuery.setAfterId(lastItem.get().getId());\n\t\t\t\t} else {\n\t\t\t\t\tquery.getRsm().setAfter(lastItem.get().getId());\n\t\t\t\t}\n\t\t\t\tlastItem.set(null);\n\t\t\t}\n\t\t}\n\t\tarchiveWriter.append(\"</archive>\");\n\t}\n\n\tpublic abstract static class AbstractImporterExtension extends tigase.db.util.importexport.AbstractImporterExtension {\n\n\t\tpublic boolean handleElement(Element element) throws Exception {\n\t\t\tif (!\"result\".equals(element.getName())) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tString stanzaId = Optional.ofNullable(element.getAttributeStaticStr(\"id\")).orElseThrow();\n\t\t\tElement forwardedEl = Optional.ofNullable(element.findChild(\n\t\t\t\t\t\t\tel -> \"forwarded\".equals(el.getName()) && \"urn:xmpp:forward:0\".equals(el.getXMLNS())))\n\t\t\t\t\t.orElseThrow();\n\t\t\tElement messageEl = Optional.ofNullable(forwardedEl.findChild(el -> \"message\".equals(el.getName())))\n\t\t\t\t\t.orElseThrow();\n\t\t\tDate timestamp = Optional.ofNullable(\n\t\t\t\t\t\t\tforwardedEl.findChild(el -> \"delay\".equals(el.getName()) && \"urn:xmpp:delay\".equals(el.getXMLNS())))\n\t\t\t\t\t.map(delay -> delay.getAttributeStaticStr(\"stamp\"))\n\t\t\t\t\t.map(this::parseTimestamp)\n\t\t\t\t\t.orElseThrow();\n\n\t\t\tMessage message = new Message(messageEl);\n\n\t\t\treturn handleMessage(message, stanzaId, timestamp, element);\n\t\t}\n\n\t\tprotected abstract boolean handleMessage(Message message, String stableId, Date timestamp, Element source) throws Exception;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/mam/util/MAMUtil.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.mam.util;\n\nimport tigase.xmpp.rsm.RSM;\n\npublic class MAMUtil {\n\n\tpublic static Range rangeFromPositions(Integer afterPos, Integer beforePos) {\n\t\treturn new Range(afterPos != null ? afterPos + 1 : 0,\n\t\t\t\t\t\t beforePos != null ? beforePos : Integer.MAX_VALUE);\n\t}\n\n\tpublic static void calculateOffsetAndPosition(RSM rsm, int count, Integer before, Integer after, Range idRange) {\n\t\tint index = rsm.getIndex() == null ? 0 : rsm.getIndex();\n\t\tint newCount = idRange.isUpperUnbound() ? count - idRange.getLowerBound() : idRange.size();\n\n\t\tif (after != null) {\n\t\t\t// it is ok, if we go out of range we will return empty result\n\t\t\tindex = Math.max((after - idRange.getLowerBound()) + 1, 0);\n\t\t} else if (before != null) {\n\t\t\tindex = Math.max((Math.min(before, idRange.getUpperBound()) - rsm.getMax()) - idRange.getLowerBound(), 0);\n\t\t} else if (rsm.hasBefore()) {\n\t\t\tindex = Math.max(newCount - rsm.getMax(), 0);\n\t\t}\n\n\t\tint limit = Math.min(rsm.getMax(), Math.min(newCount, before != null ? before : Integer.MAX_VALUE) - index);\n\n\t\trsm.setIndex(index);\n\t\trsm.setMax(limit);\n\t\trsm.setCount(newCount);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/tigase/xmpp/mam/util/Range.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.mam.util;\n\npublic class Range {\n\n\tpublic static final Range FULL = new Range(0, Integer.MAX_VALUE);\n\t\n\tprivate final int lowerBound;\n\tprivate final int upperBound;\n\n\tpublic Range(int lowerBound, int upperBound) {\n\t\tthis.lowerBound = lowerBound;\n\t\tthis.upperBound = upperBound;\n\t}\n\n\tpublic int getLowerBound() {\n\t\treturn lowerBound;\n\t}\n\n\tpublic int getUpperBound() {\n\t\treturn upperBound;\n\t}\n\n\tpublic int size() {\n\t\treturn upperBound - lowerBound;\n\t}\n\n\tpublic boolean isUpperUnbound() {\n\t\treturn upperBound == Integer.MAX_VALUE;\n\t}\n\t\n}\n"
  },
  {
    "path": "src/main/php/drupal/modules/tigase.module",
    "content": "<?php\n\t/*\n\t * Tigase Drupal Module for Jabber/XMPP service\n\t * Copyright (C) 2004-2012 \"Artur Hefczyc\" <artur.hefczyc@tigase.org>\n\t *\n\t * This program is free software: you can redistribute it and/or modify\n\t * it under the terms of the GNU Affero General Public License as published by\n\t * the Free Software Foundation, either version 3 of the License.\n\t *\n\t * This program is distributed in the hope that it will be useful,\n\t * but WITHOUT ANY WARRANTY; without even the implied warranty of\n\t * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\t * GNU Affero General Public License for more details.\n\t *\n\t * You should have received a copy of the GNU Affero General Public License\n\t * along with this program. Look for COPYING file in the top folder.\n\t * If not, see http://www.gnu.org/licenses/.\n\t *\n\t * $Rev:  $\n\t * Last modified by $Author: $\n\t * $Date:  $\n\t */\n\n\t/**\n\t * Display help and module information\n\t * @param section which section of the site we're displaying help\n\t * @return help text for section\n\t */\nfunction tigase_help($section='') {\n\n\t$output = '';\n\n\tswitch ($section) {\n\tcase \"admin/modules#description\":\n\t\t$output = t(\"Integration with Tigase Jabber/XMPP server.\");\n\t\tbreak;\n\tcase \"admin/help#tigase\":\n\t\t$output = t(\"Integration with Tigase Jabber/XMPP server.\") . \"\\n\"\n\t\t\t. t(\"This is not a Jabber/XMPP client within Drupal. This is more like\")\n\t\t\t. t(\" closer integration between Drupal and Tigase. The basic idea is to\")\n\t\t\t. t(\" allow for full Jabber/XMPP account data management from Drupal site.\")\n\t\t\t. \"\\n\" . t(\"The first step is to allow to display on-line user status on\")\n\t\t\t. t(\" Drupal site in similar form to 'Online users' list.\");\n\t\tbreak;\n\t}\n\n\treturn $output;\n}\n\nfunction tigase_perm() {\n  return array('access tigase');\n}\n\n/**\n* Generate list of users loggedin to Jabber server\n* @param op the operation from the URL\n* @param delta offset\n* @returns block HTML\n*/\nfunction tigase_block($op='list', $delta=0) {\n\t// listing of blocks, such as on the admin/block page\n\tif ($op == \"list\") {\n\t\t$block[0][\"info\"] = t('Jabber online users');\n\t\treturn $block;\n\t} else if ($op == 'view') {\n\t\t\n\t}\n}\n"
  },
  {
    "path": "src/main/php/drupal/modules/tigase_monitor-5.x/tigase_monitor.info",
    "content": "; $Id: $\nname = Tigase server monitor\ndescription = Displays Tigase server statistics\npackage = Tigase\n"
  },
  {
    "path": "src/main/php/drupal/modules/tigase_monitor-5.x/tigase_monitor.module",
    "content": "<?php\n\t/*\n\t * Tigase Drupal Monitor Module\n\t * Copyright (C) 2004-2012 \"Artur Hefczyc\" <artur.hefczyc@tigase.org>\n\t *\n\t * This program is free software: you can redistribute it and/or modify\n\t * it under the terms of the GNU Affero General Public License as published by\n\t * the Free Software Foundation, either version 3 of the License.\n\t *\n\t * This program is distributed in the hope that it will be useful,\n\t * but WITHOUT ANY WARRANTY; without even the implied warranty of\n\t * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\t * GNU Affero General Public License for more details.\n\t *\n\t * You should have received a copy of the GNU Affero General Public License\n\t * along with this program. Look for COPYING file in the top folder.\n\t * If not, see http://www.gnu.org/licenses/.\n\t *\n\t * $Rev:  $\n\t * Last modified by $Author: $\n\t * $Date:  $\n\t */\n\n\t/**\n\t * Display help and module information\n\t * @param section which section of the site we're displaying help\n\t * @return help text for section\n\t */\nfunction tigase_monitor_help($section='') {\n\n\t$output = '';\n\n\tswitch ($section) {\n\tcase \"admin/help#tigase_monitor\":\n\t\t$output = t(\"Monitor displaying various server statistics.\") . \"\\n\"\n\t\t\t. t(\" This module displays information about number of downloads, last software version, number of registered user accounts, number of users online, number of s2s connections and so on...\");\n\t\tbreak;\n\t}\n\n\treturn $output;\n}\n\nfunction tigase_monitor_perm() {\n  return array('access tigase_monitor', 'access tigase_monitor content',\n\t\t'access tigase_monitor config');\n}\n\n/**\n* Output all monitor information\n* @param op the operation from the URL\n* @param delta offset\n* @returns block HTML\n*/\nfunction tigase_monitor_block($op='list', $delta=0) {\n\t// listing of blocks, such as on the admin/block page\n\tif ($op == \"list\") {\n\t\t$block[0][\"info\"] = t('Monitor - Tigase Server');\n\t\treturn $block;\n\t} else if ($op == 'view') {\n\t\t$folder = 'files/downloads/tigase-server/';\n\t\t$descript = $folder . 'descript.ion';\n\t\t$info = tigase_monitor_decript($descript);\n\t\t$jabber_stats_file = 'files/jabber_stats.txt';\n\t\t$jabber_stats = tigase_monitor_stats($jabber_stats_file);\n \t\t$server_downloads = '';\n\t\t$server_version = ' ';\n\t\t$file = '';\n\t\tforeach ($info as $name => $description) {\n\t\t\tif (($name != 'TOTAL:') && !stripos($name, 'xmltools') && !stripos($name, 'utils')) {\n\t\t\t\t$name_ver = str_ireplace(\"tigase-server-\", \"\", $name);\n\t\t\t\t$name_ver = str_ireplace(\".zip\", \"\", $name_ver);\n\t\t\t\t$name_ver = str_ireplace(\".gz\", \"\", $name_ver);\n\t\t\t\t$name_ver = str_ireplace(\".tar\", \"\", $name_ver);\n\t\t\t\t$name_ver = str_ireplace(\".exe\", \"\", $name_ver);\n\t\t\t\t$name_ver = str_ireplace(\".jar\", \"\", $name_ver);\n\t\t\t\t$name_ver = str_ireplace(\".src\", \"\", $name_ver);\n\t\t\t\tif ($name_ver >= $server_version) {\n\t\t\t\t\t$server_version = $name_ver;\n\n\t\t\t\t\t$zip_file = \"$folder\".\"tigase-server-$server_version\".\".zip\";\n\t\t\t\t\t$exe_file = \"$folder\".\"tigase-server-$server_version\".\".exe\";\n\t\t\t\t\t$targz_file = \"$folder\".\"tigase-server-$server_version\".\".tar.gz\";\n\t\t\t\t\t$jar_file = \"$folder\".\"tigase-server-$server_version\".\".jar\";\n\n// \t\t\t\t\t\t$file = $name;\n// \t\t\t\t\tif (strpos($name, \".zip\")) {\n// \t\t\t\t\t\t$zip_file = \"$folder$file\";\n// \t\t\t\t\t}\n// \t\t\t\t\tif (strpos($name, \".exe\")) {\n// \t\t\t\t\t\t$exe_file = \"$folder$file\";\n// \t\t\t\t\t}\n// \t\t\t\t\tif (strpos($name, \".tar.gz\")) {\n// \t\t\t\t\t\t$targz_file = \"$folder$file\";\n// \t\t\t\t\t}\n// \t\t\t\t\tif (strpos($name, \".jar\")) {\n// \t\t\t\t\t\t$jar_file = \"$folder$file\";\n// \t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$server_downloads = $description[0];\n\t\t\t}\n\t\t}\n\t\t$link_jar = \"<a href=\\\"{$base_path}/{$jar_file}\\\">jar</a>, \";\n\t\t$link_exe = \"<a href=\\\"{$base_path}/{$exe_file}\\\">exe</a>, \";\n\t\t$link_targz = \"<a href=\\\"{$base_path}/{$targz_file}\\\">tar.gz</a>, \";\n\t\t$link_zip = \"<a href=\\\"{$base_path}/{$zip_file}\\\">zip</a>\";\n\t\t$link_all = \"<a href=\\\"{$base_path}/filebrowser/tigase-server\\\">$server_downloads</a>\";\n\t\t//$users_cnt = db_fetch_object(db_query(\"SELECT COUNT(*) as count from {users};\"));\n\t\t$online_cnt = db_fetch_object(db_query(\"SELECT COUNT(*) as count from {users} where online_status > 0;\"));\n\t\t// Count users with activity in the past defined period.\n\t\t$time_period = variable_get('user_block_seconds_online', 900);\n\t\t// Perform database queries to gather online user lists.\n\t\t$guests = db_fetch_object(db_query('SELECT COUNT(sid) AS count FROM {sessions} WHERE timestamp >= %d AND uid = 0', time() - $time_period));\n\t\t$users = db_fetch_object(db_query('SELECT COUNT(*) AS count FROM {users} WHERE access >= %d AND uid != 0 ORDER BY access DESC', time() - $time_period));\n\t\t$block_content .= '<table border=\"0\">';\n\t\t//\t\t$block_content .= '<tr><td>Registered:</td><td>' . $users_cnt->count . '</td></tr>';\n\t\t//\t\t$block_content .= '<tr><td>Jabber Online:</td><td>' . $online_cnt->count . '</td></tr>';\n// \t\tprint_r( $jabber_stats );\n// \t\tprint_r( '* <br>' );\n\t\tforeach ($jabber_stats as $name => $description) {\n\t\t\t$block_content .= \"<tr><td>{$name}</td><td>{$description}</td></tr>\";\n\t\t}\n\t\t$block_content .= '<tr><td>Website online:</td><td>' . ($users->count + $guests->count) . '</td></tr>';\n\t\t$block_content .= '<tr><td>Ver ' . $server_version . ':</td><td>'\n\t\t\t. $link_jar . $link_exe . $link_targz . $link_zip . '</td></tr>';\n\t\t$block_content .= '<tr><td>All downloads:</td><td>' . $link_all . '</td></tr>';\n\t\t$block_content .= '<tr><td><a href=\"http://www.ohloh.net/\">Ohloh</a> stats:</td>' .\n\t\t\t'<td><a href=\"http://www.ohloh.net/projects/4171\"><img src=\"/files/ohloh_badge.gif\"/></a></td></tr>';\n\t\t$block_content .= '</table>';\n\t\t$block_content .=\n\t\t\t\"<div class=\\\"more-link\\\">\"\n\t\t\t.'More '\n\t\t\t.l(t(\"details...\"), \"jabber-stats\", array(\"title\" => t(\"Statistics details.\")))\n\t\t\t.' or '\n\t\t\t.l(t(\"configuration...\"), \"jabber-config\", array(\"title\" => t(\"Server configuration.\")))\n\t\t\t.\"</div>\";\n    // set up the block\n    $block['subject'] = 'Monitor';\n    $block['content'] = $block_content;\n    return $block;\n\t}\n}\n\n/**\n * Output full jabber statistics from files/jabber_stats.html file\n */\nfunction tigase_monitor_full_stats() {\n\t$page_content = '';\n\t$src_file = 'files/jabber_stats.html';\n\n  if (is_readable($src_file) && ($file = file($src_file))) {\n\t\t$started = 0;\n    foreach ($file as $line) {\n\t\t\tif (!$started) {\n\t\t\t\t$started = stripos($line, 'Server version info:')\n\t\t\t\t\t|| stripos($line, 'Tests results:');\n\t\t\t} else {\n\t\t\t\t$started = !stripos($line, 'Server basic configuration parameters:');\n\t\t\t}\n\t\t\tif ($started) {\n\t\t\t\t$page_content .= $line;\n\t\t\t}\n    }\n\t}\n\n\t// check to see if there was any content before\n  // setting up the block\n  if ($page_content == '') {\n    // No statistics file...\n    $page_content = \"Server statistics are not available yet...\";\n  }\n\n  return $page_content;\n}\n\n/**\n * Output full jabber statistics from files/jabber_stats.html file\n */\nfunction tigase_monitor_tigase_config() {\n\t$page_content = '';\n\t$src_file = 'files/jabber_stats.html';\n\n  if (is_readable($src_file) && ($file = file($src_file))) {\n\t\t$started = 0;\n    foreach ($file as $line) {\n\t\t\tif (!$started) {\n\t\t\t\t$started = stripos($line, 'Server basic configuration parameters:');\n\t\t\t} else {\n\t\t\t\t$started = !stripos($line, 'Tests results:');\n\t\t\t}\n\t\t\tif ($started) {\n\t\t\t\t$page_content .= $line;\n\t\t\t}\n    }\n\t}\n\n\t// check to see if there was any content before\n  // setting up the block\n  if ($page_content == '') {\n    // No statistics file...\n    $page_content = \"Server configuration are not available yet...\";\n  }\n\n  return $page_content;\n}\n\nfunction tigase_monitor_menu() {\n\n  $items = array();\n\n  $items[] = array(\n\t\t'path' => 'jabber-stats',\n\t\t'title' => t('Tigase server statistics'),\n\t\t'callback' => 'tigase_monitor_full_stats',\n\t\t'access' => user_access('access tigase_monitor content'),\n\t\t'type' => MENU_CALLBACK);\n  $items[] = array(\n\t\t'path' => 'jabber-config',\n\t\t'title' => t('Tigase server configuration'),\n\t\t'callback' => 'tigase_monitor_tigase_config',\n\t\t'access' => user_access('access tigase_monitor config'),\n\t\t'type' => MENU_CALLBACK);\n\n  return $items;\n}\n\n/**\n * Loads file metainformation from the specified file.\n */\nfunction tigase_monitor_decript($decript = NULL) {\n  static $decriptcols = array();\n\n  // Return (previously generated) decript column list\n  if (!isset($decript)) { return $decriptcols; }\n\n  // Build decript information list\n  $decriptinfo = array();\n  if (is_readable($decript) && ($file = file($decript))) {\n    foreach ($file as $line) {\n      // Skip empty and commented lines\n      if (trim($line) == '' || strpos(trim($line), '#') === 0) {\n        continue;\n      }\n      list($name, $description) = explode(\" \", $line, 2);\n      if (isset($decriptinfo[$name])) {\n        $decriptinfo[$name] .= trim($description) . \" \";\n      } else {\n        $decriptinfo[$name] = trim($description) . \" \";\n      }\n    }\n\n    foreach ($decriptinfo as $name => $description) {\n      $decriptinfo[$name] = array(trim($description));\n    }\n  }\n  return $decriptinfo;\n}\n\n/**\n * Loads file metainformation from the specified file.\n */\nfunction tigase_monitor_stats($stats = NULL) {\n  static $statscols = array();\n\n  // Return (previously generated) stats column list\n  if (!isset($stats)) { return $statscols; }\n\n\t// Build stats information list\n  $statsinfo = array();\n  if (is_readable($stats) && ($file = file($stats))) {\n    foreach ($file as $line) {\n      // Skip empty and commented lines\n      if ((trim($line) == '') || strpos(trim($line), '#')) {\n        continue;\n      }\n\t\t\tif (stripos($line, 'Registered')) {\n\t\t\t\t$split_line = explode(' ', trim($line));\n\t\t\t\t$statsinfo['Registered:'] = $split_line[3];\n\t\t\t}\n\t\t\tif (stripos($line, 'Packets received')) {\n\t\t\t\t$split_line = explode(' ', trim($line));\n\t\t\t\t$statsinfo['Packets:'] = $split_line[2];\n\t\t\t}\n\t\t\tif (stripos($line, 'Open authorized sessions')) {\n\t\t\t\t$split_line = explode(' ', trim($line));\n\t\t\t\t$statsinfo['Sessions online:'] = $split_line[4];\n\t\t\t}\n\t\t\tif (stripos($line, 'Uptime')) {\n\t\t\t\t$split_line = explode(' ', trim($line));\n\t\t\t\t$statsinfo['Uptime:'] = $split_line[1] . substr($split_line[2], 0, 1)\n\t\t\t\t\t. ', ' . $split_line[3] . substr($split_line[4], 0, 1);\n\t\t\t}\n\t\t\tif (stripos($line, 'Connected servers')) {\n\t\t\t\t$split_line = explode(' ', trim($line));\n\t\t\t\t$statsinfo['Connected servers:'] = $split_line[3];\n\t\t\t}\n\t\t\tif (stripos($line, 'Open s2s connections')) {\n\t\t\t\t$split_line = explode(' ', trim($line));\n\t\t\t\t$statsinfo['Server connections:'] = $split_line[4];\n\t\t\t}\n\t\t\tif (stripos($line, 'c2s')) {\n\t\t\t\t$split_line = explode(' ', trim($line));\n\t\t\t\t$statsinfo['Clients online:'] = $split_line[3];\n\t\t\t}\n\t\t\tif (stripos($line, 'bosh')) {\n\t\t\t\t$split_line = explode(' ', trim($line));\n\t\t\t\t$statsinfo['Bosh online:'] = $split_line[3];\n\t\t\t}\n\t\t\tif (stripos($line, 'Version')) {\n        $split_line = explode(' ', trim($line));\n        $statsinfo['Running version:'] = $split_line[1];\n      }\n\t\t\tif (stripos($line, 'CPU')) {\n        $split_line = explode(' ', trim($line));\n        $statsinfo['CPU usage:'] = $split_line[2];\n      }\n    }\n\n  }\n//  \tprint_r( $statsinfo );\n//  \tprint_r( '* <br>' );\n  return $statsinfo;\n}\n"
  },
  {
    "path": "src/main/php/drupal/modules/tigase_monitor.module",
    "content": "<?php\n\t/*\n\t * Tigase Drupal Monitor Module\n\t * Copyright (C) 2004-2012 \"Artur Hefczyc\" <artur.hefczyc@tigase.org>\n\t *\n\t * This program is free software: you can redistribute it and/or modify\n\t * it under the terms of the GNU Affero General Public License as published by\n\t * the Free Software Foundation, either version 3 of the License.\n\t *\n\t * This program is distributed in the hope that it will be useful,\n\t * but WITHOUT ANY WARRANTY; without even the implied warranty of\n\t * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\t * GNU Affero General Public License for more details.\n\t *\n\t * You should have received a copy of the GNU Affero General Public License\n\t * along with this program. Look for COPYING file in the top folder.\n\t * If not, see http://www.gnu.org/licenses/.\n\t *\n\t * $Rev:  $\n\t * Last modified by $Author: $\n\t * $Date:  $\n\t */\n\n\t/**\n\t * Display help and module information\n\t * @param section which section of the site we're displaying help\n\t * @return help text for section\n\t */\nfunction tigase_monitor_help($section='') {\n\n\t$output = '';\n\n\tswitch ($section) {\n\tcase \"admin/modules#description\":\n\t\t$output = t(\"Monitor displaying various server statistics.\");\n\t\tbreak;\n\tcase \"admin/help#tigase_monitor\":\n\t\t$output = t(\"Monitor displaying various server statistics.\") . \"\\n\"\n\t\t\t. t(\" This module displays information about number of downloads, last software version, number of registered user accounts, number of users online, number of s2s connections and so on...\");\n\t\tbreak;\n\t}\n\n\treturn $output;\n}\n\nfunction tigase_monitor_perm() {\n  return array('access tigase_monitor', 'access tigase_monitor content',\n\t\t'access tigase_monitor config');\n}\n\n/**\n* Output all monitor information\n* @param op the operation from the URL\n* @param delta offset\n* @returns block HTML\n*/\nfunction tigase_monitor_block($op='list', $delta=0) {\n\t// listing of blocks, such as on the admin/block page\n\tif ($op == \"list\") {\n\t\t$block[0][\"info\"] = t('Monitor');\n\t\treturn $block;\n\t} else if ($op == 'view') {\n\t\t$folder = 'files/downloads/tigase-server/';\n\t\t$descript = $folder . 'descript.ion';\n\t\t$info = tigase_monitor_decript($descript);\n\t\t$jabber_stats_file = 'files/jabber_stats.txt';\n\t\t$jabber_stats = tigase_monitor_stats($jabber_stats_file);\n \t\t$server_downloads = '';\n\t\t$server_version = ' ';\n\t\t$file = '';\n\t\tforeach ($info as $name => $description) {\n\t\t\tif ($name != 'TOTAL:') {\n\t\t\t\t$name_ver = str_ireplace(\"tigase-server-\", \"\", $name);\n\t\t\t\t$name_ver = str_ireplace(\".zip\", \"\", $name_ver);\n\t\t\t\t$name_ver = str_ireplace(\".gz\", \"\", $name_ver);\n\t\t\t\t$name_ver = str_ireplace(\".tar\", \"\", $name_ver);\n\t\t\t\t$name_ver = str_ireplace(\".exe\", \"\", $name_ver);\n\t\t\t\tif ($name_ver >= $server_version) {\n\t\t\t\t\t$server_version = $name_ver;\n\t\t\t\t\t$file = $name;\n\t\t\t\t\tif (strpos($name, \".zip\")) {\n\t\t\t\t\t\t$zip_file = \"$folder/$file\";\n\t\t\t\t\t}\n\t\t\t\t\tif (strpos($name, \".exe\")) {\n\t\t\t\t\t\t$exe_file = \"$folder/$file\";\n\t\t\t\t\t}\n\t\t\t\t\tif (strpos($name, \".tar.gz\")) {\n\t\t\t\t\t\t$targz_file = \"$folder/$file\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t$server_downloads = $description[0];\n\t\t\t}\n\t\t}\n\t\t$link_zip = \"<a href=\\\"{$base_path}{$zip_file}\\\">zip</a>, \";\n\t\t$link_exe = \"<a href=\\\"{$base_path}{$exe_file}\\\">exe</a>, \";\n\t\t$link_targz = \"<a href=\\\"{$base_path}{$targz_file}\\\">tar.gz</a>\";\n\t\t$link_all = \"<a href=\\\"{$base_path}/filebrowser/tigase-server\\\">$server_downloads</a>\";\n\t\t//$users_cnt = db_fetch_object(db_query(\"SELECT COUNT(*) as count from {users};\"));\n\t\t$online_cnt = db_fetch_object(db_query(\"SELECT COUNT(*) as count from {users} where online_status > 0;\"));\n\t\t// Count users with activity in the past defined period.\n\t\t$time_period = variable_get('user_block_seconds_online', 900);\n\t\t// Perform database queries to gather online user lists.\n\t\t$guests = db_fetch_object(db_query('SELECT COUNT(sid) AS count FROM {sessions} WHERE timestamp >= %d AND uid = 0', time() - $time_period));\n\t\t$users = db_fetch_object(db_query('SELECT COUNT(*) AS count FROM {users} WHERE access >= %d AND uid != 0 ORDER BY access DESC', time() - $time_period));\n\t\t$block_content .= '<table border=\"0\">';\n\t\t//\t\t$block_content .= '<tr><td>Registered:</td><td>' . $users_cnt->count . '</td></tr>';\n\t\t//\t\t$block_content .= '<tr><td>Jabber Online:</td><td>' . $online_cnt->count . '</td></tr>';\n// \t\tprint_r( $jabber_stats );\n// \t\tprint_r( '* <br>' );\n\t\tforeach ($jabber_stats as $name => $description) {\n\t\t\t$block_content .= \"<tr><td>{$name}</td><td>{$description}</td></tr>\";\n\t\t}\n\t\t$block_content .= '<tr><td>Website online:</td><td>' . ($users->count + $guests->count) . '</td></tr>';\n\t\t$block_content .= '<tr><td>Ver ' . $server_version . ':</td><td>'\n\t\t\t. $link_zip . $link_exe . $link_targz . '</td></tr>';\n\t\t$block_content .= '<tr><td>All downloads:</td><td>' . $link_all . '</td></tr>';\n\t\t$block_content .= '<tr><td><a href=\"http://www.ohloh.net/\">Ohloh</a> stats:</td>' .\n\t\t\t'<td><a href=\"http://www.ohloh.net/projects/4171\"><img src=\"/files/ohloh_badge.gif\"/></a></td></tr>';\n\t\t$block_content .= '</table>';\n\t\t$block_content .=\n\t\t\t\"<div class=\\\"more-link\\\">\"\n\t\t\t.'More '\n\t\t\t.l(t(\"details...\"), \"jabber-stats\", array(\"title\" => t(\"Statistics details.\")))\n\t\t\t.' or '\n\t\t\t.l(t(\"configuration...\"), \"jabber-config\", array(\"title\" => t(\"Server configuration.\")))\n\t\t\t.\"</div>\";\n    // set up the block\n    $block['subject'] = 'Monitor';\n    $block['content'] = $block_content;\n    return $block;\n\t}\n}\n\n/**\n * Output full jabber statistics from files/jabber_stats.html file\n */\nfunction tigase_monitor_full_stats() {\n\t$page_content = '';\n\t$src_file = 'files/jabber_stats.html';\n\n  if (is_readable($src_file) && ($file = file($src_file))) {\n\t\t$started = 0;\n    foreach ($file as $line) {\n\t\t\tif (!$started) {\n\t\t\t\t$started = stripos($line, 'Server version info:')\n\t\t\t\t\t|| stripos($line, 'Tests results:');\n\t\t\t} else {\n\t\t\t\t$started = !stripos($line, 'Server basic configuration parameters:');\n\t\t\t}\n\t\t\tif ($started) {\n\t\t\t\t$page_content .= $line;\n\t\t\t}\n    }\n\t}\n\n\t// check to see if there was any content before\n  // setting up the block\n  if ($page_content == '') {\n    // No statistics file...\n    $page_content = \"Server statistics are not available yet...\";\n  }\n\n  return $page_content;\n}\n\n/**\n * Output full jabber statistics from files/jabber_stats.html file\n */\nfunction tigase_monitor_tigase_config() {\n\t$page_content = '';\n\t$src_file = 'files/jabber_stats.html';\n\n  if (is_readable($src_file) && ($file = file($src_file))) {\n\t\t$started = 0;\n    foreach ($file as $line) {\n\t\t\tif (!$started) {\n\t\t\t\t$started = stripos($line, 'Server basic configuration parameters:');\n\t\t\t} else {\n\t\t\t\t$started = !stripos($line, 'Tests results:');\n\t\t\t}\n\t\t\tif ($started) {\n\t\t\t\t$page_content .= $line;\n\t\t\t}\n    }\n\t}\n\n\t// check to see if there was any content before\n  // setting up the block\n  if ($page_content == '') {\n    // No statistics file...\n    $page_content = \"Server configuration are not available yet...\";\n  }\n\n  return $page_content;\n}\n\nfunction tigase_monitor_menu() {\n\n  $items = array();\n\n  $items[] = array('path' => 'jabber-stats',\n\t\t'title' => t('Tigase server statistics'),\n\t\t'callback' => 'tigase_monitor_full_stats',\n\t\t'access' => user_access('access tigase_monitor content'),\n\t\t'type' => MENU_CALLBACK);\n  $items[] = array('path' => 'jabber-config',\n\t\t'title' => t('Tigase server configuration'),\n\t\t'callback' => 'tigase_monitor_tigase_config',\n\t\t'access' => user_access('access tigase_monitor config'),\n\t\t'type' => MENU_CALLBACK);\n\n  return $items;\n}\n\n/**\n * Loads file metainformation from the specified file.\n */\nfunction tigase_monitor_decript($decript = NULL) {\n  static $decriptcols = array();\n\n  // Return (previously generated) decript column list\n  if (!isset($decript)) { return $decriptcols; }\n\n  // Build decript information list\n  $decriptinfo = array();\n  if (is_readable($decript) && ($file = file($decript))) {\n    foreach ($file as $line) {\n      // Skip empty and commented lines\n      if (trim($line) == '' || strpos(trim($line), '#') === 0) {\n        continue;\n      }\n      list($name, $description) = explode(\" \", $line, 2);\n      if (isset($decriptinfo[$name])) {\n        $decriptinfo[$name] .= trim($description) . \" \";\n      } else {\n        $decriptinfo[$name] = trim($description) . \" \";\n      }\n    }\n\n    foreach ($decriptinfo as $name => $description) {\n      $decriptinfo[$name] = array(trim($description));\n    }\n  }\n  return $decriptinfo;\n}\n\n/**\n * Loads file metainformation from the specified file.\n */\nfunction tigase_monitor_stats($stats = NULL) {\n  static $statscols = array();\n\n  // Return (previously generated) stats column list\n  if (!isset($stats)) { return $statscols; }\n\n\t// Build stats information list\n  $statsinfo = array();\n  if (is_readable($stats) && ($file = file($stats))) {\n    foreach ($file as $line) {\n      // Skip empty and commented lines\n      if ((trim($line) == '') || strpos(trim($line), '#')) {\n        continue;\n      }\n\t\t\tif (stripos($line, 'Registered')) {\n\t\t\t\t$split_line = explode(' ', trim($line));\n\t\t\t\t$statsinfo['Registered:'] = $split_line[3];\n\t\t\t}\n\t\t\tif (stripos($line, 'Packets received')) {\n\t\t\t\t$split_line = explode(' ', trim($line));\n\t\t\t\t$statsinfo['Packets:'] = $split_line[2];\n\t\t\t}\n\t\t\tif (stripos($line, 'Open authorized sessions')) {\n\t\t\t\t$split_line = explode(' ', trim($line));\n\t\t\t\t$statsinfo['Sessions online:'] = $split_line[4];\n\t\t\t}\n\t\t\tif (stripos($line, 'Uptime')) {\n\t\t\t\t$split_line = explode(' ', trim($line));\n\t\t\t\t$statsinfo['Uptime:'] = $split_line[1] . substr($split_line[2], 0, 1)\n\t\t\t\t\t. ', ' . $split_line[3] . substr($split_line[4], 0, 1);\n\t\t\t}\n\t\t\tif (stripos($line, 's2s')) {\n\t\t\t\t$split_line = explode(' ', trim($line));\n\t\t\t\t$statsinfo['Servers online:'] = $split_line[4];\n\t\t\t}\n\t\t\tif (stripos($line, 'c2s')) {\n\t\t\t\t$split_line = explode(' ', trim($line));\n\t\t\t\t$statsinfo['Clients online:'] = $split_line[3];\n\t\t\t}\n\t\t\tif (stripos($line, 'bosh')) {\n\t\t\t\t$split_line = explode(' ', trim($line));\n\t\t\t\t$statsinfo['Bosh online:'] = $split_line[3];\n\t\t\t}\n    }\n\n  }\n//  \tprint_r( $statsinfo );\n//  \tprint_r( '* <br>' );\n  return $statsinfo;\n}\n"
  },
  {
    "path": "src/main/php/drupal/modules/tigase_usrreg-5.x/tigase_usrreg.info",
    "content": "; $Rev: $\n; Last modified by $Author: $\n; $Date: $\n\nname = User register hook\ndescription = Catches user creation event and for each new user creates a file in the filesystem with the new user name. This file is later used by system scripts which create system directory for the user so he can receive emails.\npackage = Tigase\nversion = 1.0\n"
  },
  {
    "path": "src/main/php/drupal/modules/tigase_usrreg-5.x/tigase_usrreg.module",
    "content": "<?php\n\t/*\n\t * Tigase Drupal User Register Module\n\t * Copyright (C) 2004-2012 \"Artur Hefczyc\" <artur.hefczyc@tigase.org>\n\t *\n\t * This program is free software: you can redistribute it and/or modify\n\t * it under the terms of the GNU Affero General Public License as published by\n\t * the Free Software Foundation, either version 3 of the License.\n\t *\n\t * This program is distributed in the hope that it will be useful,\n\t * but WITHOUT ANY WARRANTY; without even the implied warranty of\n\t * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\t * GNU Affero General Public License for more details.\n\t *\n\t * You should have received a copy of the GNU Affero General Public License\n\t * along with this program. Look for COPYING file in the top folder.\n\t * If not, see http://www.gnu.org/licenses/.\n\t *\n\t * $Rev:  $\n\t * Last modified by $Author: $\n\t * $Date:  $\n\t */\n\nfunction tigase_usrreg_user($op, &$edit, &$user) {\n\n  if ($op == 'insert') {\n\t  $folder = 'users-list/';\n\t\t$user_name = $user->name;\n\t\t$handle = fopen($folder . $user_name, \"w+\");\n\t\tfwrite($handle, $user_name);\n\t\tfclose($handle);\n  }\n\n}\n"
  },
  {
    "path": "src/main/python/tigase/admin/PythonExample.py",
    "content": "#\n# Tigase XMPP Server - The instant messaging server\n# Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, version 3 of the License.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. Look for COPYING file in the top folder.\n# If not, see http://www.gnu.org/licenses/.\n#\n\nfrom java.lang import *\nfrom tigase.server import *\n\n\nnum1 = Command.getFieldValue(packet, \"num1\")\nnum2 = Command.getFieldValue(packet, \"num2\")\n\nif num1 is None or num2 is None:\n   res = Iq.commandResultForm(packet)\n   Command.addTextField(res, \"Note\", \"This is Python script!\")\n   Command.addFieldValue(res, \"num1\", \"\")\n   Command.addFieldValue(res, \"num2\", \"\")\n   packet = res\nelse:\n   result = num1 + num2\n"
  },
  {
    "path": "src/main/resources/META-INF/java.security.Provider",
    "content": "# our security providers\ntigase.auth.TigaseSaslProvider"
  },
  {
    "path": "src/main/resources/assembly/resources.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<!--\r\n  ~ Tigase Jabber/XMPP Server\r\n  ~ Copyright (C) 2004-2019 \"Tigase, Inc.\" <office@tigase.com>\r\n  ~\r\n  ~ This program is free software: you can redistribute it and/or modify\r\n  ~ it under the terms of the GNU Affero General Public License as published by\r\n  ~ the Free Software Foundation, either version 3 of the License,\r\n  ~ or (at your option) any later version.\r\n  ~\r\n  ~ This program is distributed in the hope that it will be useful,\r\n  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n  ~ GNU Affero General Public License for more details.\r\n  ~\r\n  ~ You should have received a copy of the GNU Affero General Public License\r\n  ~ along with this program. Look for COPYING file in the top folder.\r\n  ~ If not, see http://www.gnu.org/licenses/.\r\n  -->\r\n\r\n<assembly>\r\n\t<id>resources</id>\r\n\t<formats>\r\n\t\t<format>zip</format>\r\n\t\t<format>tar.gz</format>\r\n\t</formats>\r\n\t<includeBaseDirectory>false</includeBaseDirectory>\r\n\r\n\t<fileSets>\r\n\t\t<fileSet>\r\n\t\t\t<directory>./</directory>\r\n\t\t\t<outputDirectory>./</outputDirectory>\r\n\t\t\t<includes>\r\n\t\t\t\t<include>COPYING</include>\r\n\t\t\t\t<include>README</include>\r\n\t\t\t\t<include>ChangeLog</include>\r\n\t\t\t\t<include>License.html</include>\r\n\t\t\t\t<include>package.html</include>\r\n\t\t\t</includes>\r\n\t\t</fileSet>\r\n\t\t<fileSet>\r\n\t\t\t<directory>certs</directory>\r\n\t\t\t<outputDirectory>certs</outputDirectory>\r\n\t\t\t<includes>\r\n\t\t\t\t<include>rsa-keystore</include>\r\n\t\t\t\t<include>truststore</include>\r\n\t\t\t\t<include>keystore-letsencrypt</include>\r\n\t\t\t</includes>\r\n\t\t</fileSet>\r\n\t\t<fileSet>\r\n\t\t\t<directory>src/main/database</directory>\r\n\t\t\t<outputDirectory>database</outputDirectory>\r\n\t\t\t<includes>\r\n\t\t\t\t<include>*</include>\r\n\t\t\t</includes>\r\n\t\t</fileSet>\r\n\t\t<fileSet>\r\n\t\t\t<directory>src/main/asciidoc</directory>\r\n\t\t\t<outputDirectory>documentation</outputDirectory>\r\n\t\t\t<includes>\r\n\t\t\t\t<include>**/*</include>\r\n\t\t\t</includes>\r\n\t\t\t<excludes>\r\n\t\t\t\t<exclude>**/*pom.xml*</exclude>\r\n\t\t\t</excludes>\r\n\t\t</fileSet>\r\n\t\t<fileSet>\r\n\t\t\t<directory>src/main/resources/win-stuff</directory>\r\n\t\t\t<outputDirectory>win-stuff</outputDirectory>\r\n\t\t\t<includes>\r\n\t\t\t\t<include>*/**</include>\r\n\t\t\t</includes>\r\n\t\t</fileSet>\r\n\t\t<fileSet>\r\n\t\t\t<directory>etc</directory>\r\n\t\t\t<outputDirectory>etc</outputDirectory>\r\n\t\t\t<includes>\r\n\t\t\t\t<include>bosh-extra-headers.txt</include>\r\n\t\t\t\t<include>cross-domain-policy.xml</include>\r\n\t\t\t\t<include>client-access-policy.xml</include>\r\n\t\t\t\t<include>config.tdsl</include>\r\n\t\t\t\t<include>tigase.conf</include>\r\n\t\t\t\t<include>jmx.access</include>\r\n\t\t\t\t<include>jmx.password</include>\r\n\t\t\t\t<include>logback.xml</include>\r\n\t\t\t\t<include>snmp.acl</include>\r\n\t\t\t\t<include>tigase-http-context.xml</include>\r\n\t\t\t</includes>\r\n\t\t</fileSet>\r\n\t\t<!-- OSGi configuration file -->\r\n\t\t<fileSet>\r\n\t\t\t<directory>conf</directory>\r\n\t\t\t<outputDirectory>conf</outputDirectory>\r\n\t\t\t<includes>\r\n\t\t\t\t<include>config.properties</include>\r\n\t\t\t</includes>\r\n\t\t</fileSet>\r\n\t\t<fileSet>\r\n\t\t\t<directory>src/main/groovy/tigase/admin</directory>\r\n\t\t\t<outputDirectory>scripts/admin</outputDirectory>\r\n\t\t\t<includes>\r\n\t\t\t\t<include>**/*</include>\r\n\t\t\t</includes>\r\n\t\t</fileSet>\r\n        <fileSet>\r\n            <directory>scripts</directory>\r\n            <outputDirectory>scripts</outputDirectory>\r\n            <includes>\r\n                <include>**/*</include>\r\n            </includes>\r\n            <excludes>\r\n                <exclude></exclude>\r\n            </excludes>\r\n            <fileMode>0744</fileMode>\r\n            <directoryMode>0744</directoryMode>\r\n        </fileSet>\r\n\t</fileSets>\r\n</assembly>\r\n"
  },
  {
    "path": "src/main/resources/mib/JVM-MANAGEMENT-MIB.mib",
    "content": "-- \n-- @(#)JVM-MANAGEMENT-MIB.mib\t1.32 04/07/16\n-- \n-- Copyright 2004 Sun Microsystems, Inc. All rights reserved.\n-- This software is the proprietary information of Sun Microsystems, Inc.\n-- Use is subject to license terms.\n--\n-- The JVM-MANAGEMENT-MIB Module\n--\n-- See jvmManagementMIB MODULE-IDENTITY for a description overview.\n-- See conformance statements for mandatory objects \n--\n\nJVM-MANAGEMENT-MIB DEFINITIONS ::= BEGIN\n\nIMPORTS\n    MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE, Counter32, Gauge32, \n\tInteger32, Counter64, enterprises\n        FROM SNMPv2-SMI\n    DisplayString, TEXTUAL-CONVENTION, RowPointer\n        FROM SNMPv2-TC\n    MODULE-COMPLIANCE, OBJECT-GROUP, NOTIFICATION-GROUP\n        FROM SNMPv2-CONF;\n\n-- Module Identity\n------------------\n\njvmMgtMIB MODULE-IDENTITY \n    LAST-UPDATED \"200403041800Z\"\n    -- Format is \"YYYYMMDDhhmmZ\"\n    ORGANIZATION \"Sun Microsystems, Inc.\"\n    CONTACT-INFO \"Sun Microsystems, Inc.\n                  4150 Network Circle\n\t\t  Santa Clara, CA 95054\n\t\t  1-800-555-9SUN or\n\t\t  1-650-960-1300\n\t\t  http://www.sun.com\n\t\t  or contact your local support representative\"            \n    DESCRIPTION\n            \"Copyright 2004 Sun Microsystems, Inc. All rights reserved.\n\n             This module defines the MIB that provides access to the\n\t     Java[tm] Virtual Machine monitoring data.\n\t     This module is derived from the Java[tm] programming language APIs\n             described in the java.lang.management package of \n             Java[tm] 2, Standard Edition, 5.0.\n\n\t     See the Java programming language APIs of JSR 163 for\n             'Monitoring and Management of the Java[TM] Virtual Machine'\n             for more details.\n\n\t     Where the Java programming language API uses long, or int, \n             the MIB often uses the corresponding unsigned quantity - \n             which is closer to the object semantics.\n\n             In those cases, it often happens that the -1 value that might\n             be used by the API to indicate an unknown/unimplemented\n             value cannot be used. Instead the MIB uses the value 0, which\n             stricly speaking cannot be distinguished from a valid value.\n             In many cases however, a running system will have non-zero\n             values, so using 0 instead of -1 to indicate an unknown \n             quantity does not lose any functionality.\n\t    \"\n    REVISION     \"200403041800Z\"\n    -- Format is \"YYYYMMDDhhmmZ\"\n    DESCRIPTION\n            \"\n            JVM-MANAGEMENT-MIB - JSR 163 Final Release 1.0\n            \"\n\n    ::= { standard jsr163(163) 1 }\n\n\n-- Enterprise OIDs\n------------------\n\n--        internet          OBJECT IDENTIFIER ::= { iso(1) org(3) dod(6) 1 }\n--        private           OBJECT IDENTIFIER ::= { internet 4 }\n--        enterprises       OBJECT IDENTIFIER ::= { private 1 }\n\tsun\t  \t  OBJECT IDENTIFIER ::= { enterprises 42 }\n\tjmgt\t\t  OBJECT IDENTIFIER ::= { sun products(2) 145 }\n\t-- experimental      OBJECT IDENTIFIER ::= { jmgt 1 }\n        standard          OBJECT IDENTIFIER ::= { jmgt 3 }\n\n----------------------------------------------------------------------------\n-- Textual Conventions\n----------------------\n--\n-- Note: Some of the TEXTUAL-CONVENTIONs defined in this module are \n--       OCTET STRING with a 1023 size limitation (SIZE(0..1023)).\n--\n-- As per RFC2578, section 7.1.2.  OCTET STRING:\n--\n--       \"The OCTET STRING type represents arbitrary binary or textual data.\n--        Although the SMI-specified size limitation for this type is 65535\n--        octets, MIB designers should realize that there may be \n--        implementation and interoperability limitations for sizes in \n--        excess of 255 octets.\"\n--\n-- As a consequence an agent implementing this MIB may decide to \n-- restrict this maximum size to a lesser value than 1023, provided that \n-- it makes it clear in an AGENT-CAPABILITY statement.\n-- \n----------------------------------------------------------------------------\n\nJvmUnsigned64TC ::= TEXTUAL-CONVENTION\n    STATUS       current\n    DESCRIPTION\n           \"A non-negative 64-bit bit integer, without counter\n            semantics.\"\n    -- We have cloned the Unsigned64TC defined in RFC 2564 rather\n    -- than importing it because the JVM-MANAGEMENT-MIB and the\n    -- APPLICATION-MIB are not related.\n    --\n    REFERENCE \"RFC 2564 - APPLICATION-MIB, Unsigned64TC.\"\n    SYNTAX Counter64\n\n\nJvmJavaObjectNameTC ::= TEXTUAL-CONVENTION\n    DISPLAY-HINT \"255a\"\n    STATUS       current\n    DESCRIPTION\n          \"An Object Name, as implemented by the java.lang.management API,\n\t  which identify a runtime Object (e.g. a Class Loader, a\n          Memory Manager, etc...). \n\t  The name is assumed to be unique in the scope of the object's\n\t  class.\n\n\t  This object syntax is equivalent to a DisplayString, but with a\n          a 1023 bytes size limits (instead of 255 for a DisplayString).\n\t  \n\t  Note that the SNMP agent may have to truncate the string returned\n          by the underlying API if it does not fit in this type. \n\t  (1023 bytes max).\n\t  \"\n    SYNTAX       OCTET STRING (SIZE (0..1023))\n\nJvmPathElementTC ::= TEXTUAL-CONVENTION\n    DISPLAY-HINT \"255a\"\n    STATUS       current\n    DESCRIPTION\n          \"A file or directory element in a PATH/CLASSPATH/LIBRARY_PATH\n           structure.\n\n\t  This object syntax is equivalent to a DisplayString, but with a\n          a 1023 bytes size limits (instead of 255 for a DisplayString).\n\t  \n\t  Note that the SNMP agent may have to truncate the string returned\n          by the underlying API if it does not fit in this type. \n\t  (1023 bytes max).\n\t  \"\n    SYNTAX       OCTET STRING (SIZE (0..1023))\n\nJvmArgValueTC ::= TEXTUAL-CONVENTION\n    DISPLAY-HINT \"255a\"\n    STATUS       current\n    DESCRIPTION\n          \"A string representing an input argument.\n\n\t  This object syntax is equivalent to a DisplayString, but with a\n          a 1023 bytes size limits (instead of 255 for a DisplayString).\n\t  \n\t  Note that the SNMP agent may have to truncate the string returned\n          by the underlying API if it does not fit in this type. \n\t  (1023 bytes max).\n\t  \"\n    SYNTAX       OCTET STRING (SIZE (0..1023))\n\nJvmVerboseLevelTC ::= TEXTUAL-CONVENTION\n    STATUS       current\n    DESCRIPTION\n\t\"Defines whether the verbose flag for a feature is active.\n\t verbose: the flag is on.\n\t silent:  the flag is off.\n\t\"\n    SYNTAX INTEGER { silent(1), verbose(2) }\n\n\nJvmImplSupportStateTC ::= TEXTUAL-CONVENTION\n    STATUS       current\n    DESCRIPTION\n\t\"Defines whether a feature is supported or not.\n\t\"\n    SYNTAX INTEGER { unsupported(1), supported(2) }\n\nJvmImplOptFeatureStateTC ::= TEXTUAL-CONVENTION\n    STATUS       current\n    DESCRIPTION\n\t\"Defines whether an optional feature is supported, enabled, \n\t or disabled.\n\n         An optional feature can be:\n\n\t unsupported: The JVM does not support this feature.\n\t enabled    : The JVM supports this feature, and it\n\t              is enabled.\n\t disabled   : The JVM supports this feature, and it\n\t              is disabled.\n\n         Only enabled(3) and disabled(4) may be supplied as values to a\n         SET request. unsupported(1) can only be set internally by the\n         agent.\t \n\t \"\n    SYNTAX INTEGER { unsupported(1), enabled(3), disabled(4) }\n\nJvmTimeMillis64TC ::= TEXTUAL-CONVENTION\n    STATUS       current\n    DESCRIPTION\n          \"An elapsed time, expressed in milli-seconds.\n           This type is based on Counter64, but without its specific\n           semantics.\n\t  \"  \n    SYNTAX Counter64\n\nJvmTimeNanos64TC ::= TEXTUAL-CONVENTION\n    STATUS       current\n    DESCRIPTION\n          \"An elapsed time, expressed in nano-seconds.\n           This type is based on Counter64, but without its specific\n           semantics.\n\t  \"  \n    SYNTAX Counter64\n\nJvmPositive32TC ::= TEXTUAL-CONVENTION\n    STATUS       current\n    DESCRIPTION\n          \"A positive Integer32. In Java that would be a number \n           in [0..Integer.MAX_VALUE].\n\t  \"  \n    -- We use Integer32 (0..2147483647) rather than Unsigned32 because\n    -- Unsigned32 (0..2147483647) because Unsigned32 is based on \n    -- Gauge32 - which has a specific ASN.1 tag and a specific semantics. \n    -- In principle you cannot use a Gauge32 as base type for an index \n    -- in a table.\n    -- Note also that Unsigned32 is (0..2^32-1) \n    --          while Positive32 is (0..2^31-1)\n    -- \n    SYNTAX Integer32 (0..2147483647)\n\nJvmManagedMemoryTypeTC ::= TEXTUAL-CONVENTION\n    STATUS  current\n    DESCRIPTION\n\t\"\n         Defines the type of memory contained in a memory pool.\n\t The pool may contain, heap memory or non-heap memory.\n\t\"\n    SYNTAX  INTEGER { nonheap(1), heap(2) }\n\t\n    \nJvmValidityStateTC ::= TEXTUAL-CONVENTION\n    STATUS  current\n    DESCRIPTION\n\t\"\n         Defines whether an object is still valid.\n\t\"\n    SYNTAX  INTEGER { invalid(1), valid(2) }\n\t\n    \nJvmThreadStateTC ::= TEXTUAL-CONVENTION\n    STATUS  current\n    DESCRIPTION\n\t\"Defines the possible states of a thread running in the \n\t Java virtual machine. They are virtual machine thread states \n\t and do not reflect any operating system thread states. \n\t \n\t The first two bits: inNative(1) and suspended(2) can be \n         combined together and with any other bits. The remaining\n         bits 3-9, are mutually exclusive. Bits 10-16 are reserved\n         for future evolution of this MIB.\n\n         An agent MUST always return a thread state with one of the\n         bits in the range 3-9 set to 1. The other(9) bit should only\n         be set to 1 if new thread states which are mutally exclusive\n         with bits 3-8 are defined.  An implementation can define \n         additional implementation dependant states and uses bits\n         from bit 17. \n\n\t See java.lang.Thread.State,\n\t     java.lang.management.ThreadInfo.\n\t \"\n     --\n     -- Take care that in SNMP bits are numbered starting at 1, from\n     -- left to right (1 is the highest bit). A bitmap defined by the\n     -- BITS construct is thus a byte array where bit 1 is the highest bit\n     -- of the first byte.\n     --\n     SYNTAX  BITS { -- Bits 1-2 may be specified in any combination\n                    inNative(1),\n                    suspended(2),\n\n                    -- Bits 3-9 are mutually exclusive. Attempting to\n                    -- set more than a single bit to 1 will result in\n                    -- a returned error-status of inconsistentValue.\n                    newThread(3),\n                    runnable(4),\n                    blocked(5),\n                    terminated(6),\n                    waiting(7),\n                    timedWaiting(8),\n                    other(9)\n                    -- Bits 10-16 are reserved for future use by\n                    -- this MIB\n                  }\n\n\nJvmIndex64TC ::= TEXTUAL-CONVENTION\n    STATUS current\n    DESCRIPTION\n\t\"A 64 bits string mapping an unsigned 64 bits integer value\n         in big-endian ordering (i.e: 1 is encoded as 0x0000000000000001).\n\n\t This type can be used when an unsigned 64 bits integer needs\n\t to be used inside a table index.\n\t\"\n    SYNTAX OCTET STRING (SIZE(8))\n          \n  \n-- OBJECT-TYPE OID tree\n-----------------------\n                  \njvmMgtMIBObjects \n\tOBJECT IDENTIFIER ::= { jvmMgtMIB 1 }\njvmMgtMIBNotifications \n\tOBJECT IDENTIFIER ::= { jvmMgtMIB 2 }\njvmMgtMIBConformance\n\tOBJECT IDENTIFIER ::= { jvmMgtMIB 3 }\n\n-----------------------------------------------------------------------\n--\n-- The JVM Class Loading group\n--\n-- A collection of objects used to monitor Class Loading in the\n-- Java Virtual Machine. These objects define the SNMP  management \n-- interface for the class loading system of the Java virtual machine.\n--\n-- This group only contains a few scalar object and no tables. The objects\n-- from this group are mapped from the java.lang.management.ClassLoadingMXBean\n-- interface.\n-- \n-- See J2SE 5.0 API Specification, \n--     java.lang.management.ClassLoadingMXBean\n-----------------------------------------------------------------------\n\n-- Root OBJECT IDENTIFIER for ClassLoading group.\n--\njvmClassLoading   OBJECT IDENTIFIER ::= { jvmMgtMIBObjects 1 }\n\n-- The following objects are mapped from the ClassLoadingMXBean interface.\n-----------------------------------------------------------------------\n\njvmClassesLoadedCount OBJECT-TYPE\n    SYNTAX      Gauge32 \n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n            \"The number of classes currently loaded in the JVM.\n\n\t     See java.lang.management.ClassLoadingMXBean.getLoadedClassCount()\n            \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ClassLoadingMXBean\"\n    ::= { jvmClassLoading 1 }\n\njvmClassesTotalLoadedCount OBJECT-TYPE\n    SYNTAX      Counter64\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n            \"The total number of classes that have been loaded since\n\t     the JVM has started execution.\n\n\t     See java.lang.management.ClassLoadingMXBean.\n\t              getTotalLoadedClassCount()\n            \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ClassLoadingMXBean\"\n    ::= { jvmClassLoading 2 }\n\njvmClassesUnloadedCount OBJECT-TYPE\n    SYNTAX      Counter64\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n            \"The total number of classes that have been unloaded since\n\t     the JVM has started execution.\n\n\t     See java.lang.management.ClassLoadingMXBean.getUnloadedClassCount()\n            \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ClassLoadingMXBean\"\n    ::= { jvmClassLoading 3 }\n\njvmClassesVerboseLevel OBJECT-TYPE\n    SYNTAX      JvmVerboseLevelTC\n    MAX-ACCESS  read-write\n    STATUS      current\n    DESCRIPTION\n            \"Enables or disables the verbose output for the class loading \n             system. The verbose output information and the output stream \n             to which the verbose information is emitted are implementation \n             dependent. Typically, a Java virtual machine implementation \n             prints a message each time a class file is loaded.\n\n\t     verbose: if the verbose output is enabled.\n\t     silent:  otherwise. \n\n\t     See java.lang.management.ClassLoadingMXBean.isVerbose(), \n                 java.lang.management.ClassLoadingMXBean.setVerbose()\n            \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ClassLoadingMXBean\"\n    DEFVAL { silent }\n    ::= { jvmClassLoading 4 }\n\n\n-----------------------------------------------------------------------\n--\n-- The JVM Memory group\n--\n-- A collection of objects used to monitor memory management in the\n-- Java Virtual Machine. These objects define management interface for \n-- the memory system of the Java virtual machine. \n--\n-- Memory:\n--\n-- The memory system of the Java virtual machine manages the following \n-- kinds of memory: heap, and non-heap. More information on these types\n-- of memory can be obtained from the J2SE 5.0 API Specification,\n-- java.lang.management.MemoryMXBean.\n--\n-- Memory Pools and Memory Managers:\n--\n-- Memory pools and memory managers are the abstract entities that monitor \n-- and manage the memory system of the Java virtual machine.\n--\n-- Memory managers are represented by the jvmMemManagerTable, which contains\n-- one row per Memory manager. \n-- The garbage collector is one type of memory  manager responsible for \n-- reclaiming memory occupied by unreachable objects.\n-- The jvmMemGCTable is an extension of the jvmMemManagerTable, which contains\n-- the attribute specific to garbage collectors. A garbage collector entity\n-- is thus represented by one row in the jvmMemManagerTable, and one \n-- extension row in the jvmMemGCTable.\n--\n-- Memory Pools are represented by the jvmMemPoolTable, which contains one \n-- row per memory pool. A Java virtual machine may create or remove \n-- memory pools during execution. A memory pool can belong to either the \n-- heap or the non-heap memory. \n--\n-- A memory manager is responsible for managing one or more memory pools. \n-- A memory pool can be managed by more than one memory manager.\n-- The jvmMemMgrRelPoolTable represents this managing/managed relationship. \n--\n-- A Java virtual machine may add or remove memory managers during execution. \n--\n-- See J2SE 5.0 API Specification, java.lang.management.MemoryMXBean for\n--     more information on memory types, memory managers, memory pools,\n--     and the memory subsystem.\n--\n-----------------------------------------------------------------------\n\n-- Root OBJECT IDENTIFIER for the JVM Memory group.\n--\njvmMemory   OBJECT IDENTIFIER ::= { jvmMgtMIBObjects 2 }\n\n-- The following objects are mapped from the MemoryMXBean interface.\n-----------------------------------------------------------------------\n\njvmMemoryPendingFinalCount OBJECT-TYPE\n    SYNTAX      Gauge32\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The approximate number objects that are pending for finalization.\n        \n\t See java.lang.management.MemoryMXBean.\n                  getObjectPendingFinalizationCount()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryMXBean\"\n    ::= { jvmMemory 1 }\n\njvmMemoryGCVerboseLevel OBJECT-TYPE\n    SYNTAX      JvmVerboseLevelTC\n    MAX-ACCESS  read-write\n    STATUS      current\n    DESCRIPTION\n\t\"Enables or disables verbose output for the memory system. \n         The verbose output information and the output stream to which \n         the verbose information is emitted are implementation dependent. \n         Typically, a Java virtual machine implementation prints a \n         message whenever it frees memory at garbage collection.\n\n\t verbose: if the verbose output is enabled,\n         silent:  otherwise.\n\n\t See java.lang.management.MemoryMXBean.isVerbose(), \n             java.lang.management.MemoryMXBean.setVerbose()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryMXBean\"\n    ::= { jvmMemory 2 }\n\njvmMemoryGCCall OBJECT-TYPE\n    SYNTAX      INTEGER { unsupported(1), supported(2), start(3), \n                          started(4), failed(5) }\n    MAX-ACCESS  read-write\n    STATUS      current\n    DESCRIPTION\n\t\"This object makes it possible to remotelly trigger the\n\t Garbage Collector in the JVM.\n\n\t This object's syntax is an enumeration which defines:\n\n\t * Two state values, that can be returned from a GET request:\n\n           unsupported(1): means that remote invocation of gc() is not \n                           supported by the SNMP agent.\n           supported(2)  : means that remote invocation of gc() is supported\n\t                   by the SNMP agent.\n\n         * One action value, that can be provided in a SET request to\n           trigger the garbage collector:\n\n           start(3)      : means that a manager wishes to trigger\n                           garbage collection.\n\n         * Two result value, that will be returned in the response to a\n           SET request when remote invocation of gc is supported\n\t   by the SNMP agent:\n\n\t   started(4)    : means that garbage collection was \n                           successfully triggered. It does not mean \n                           however that the action was successfullly\n                           completed: gc might still be running when\n                           this value is returned.\n\t   failed(5)     : means that garbage collection couldn't be\n                           triggered.\n\n         * If remote invocation is not supported by the SNMP agent, then\n           unsupported(1) will always be returned as a result of either\n           a GET request, or a SET request with start(3) as input value.\n\n         * If a SET request with anything but start(3) is received, then\n\t   the agent will return a wrongValue error.\n\n\t See java.lang.management.MemoryMXBean.gc()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryMXBean\"\n    ::= { jvmMemory 3 }\n\n-- The object identifiers in the range jvmMemory.[4-9] are reserved for future\n-- evolution of this MIB.\n--\n-- We use the range jvmMemory.[10..19] for objects related to global JVM \n-- heap memory  usage, as returned by \n--      java.lang.management.MemoryMXBean.getHeapMemoryUsage().\n-- Object identifiers in the range jvmMemory.[14..19] are not used but \n-- reserved for future evolution of this MIB.\n--\njvmMemoryHeapInitSize OBJECT-TYPE\n    SYNTAX      JvmUnsigned64TC\n    UNITS       \"bytes\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"\n        Total amount of memory (in bytes) that the Java virtual machine \n        initially requests from the operating system for memory management\n\tfor heap memory pools.\n\n\tSee java.lang.management.MemoryMXBean.getHeapMemoryUsage().getInit()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryMXBean,\n              java.lang.management.MemoryUsage\"\n    ::= { jvmMemory 10 }\n\n\njvmMemoryHeapUsed OBJECT-TYPE\n    SYNTAX      JvmUnsigned64TC\n    UNITS       \"bytes\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"\n        Total amount of used memory (in bytes) from heap memory pools.\n\n\tSee java.lang.management.MemoryMXBean.getHeapMemoryUsage().getUsed()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryMXBean,\n              java.lang.management.MemoryUsage\"\n    ::= { jvmMemory 11 }\n\njvmMemoryHeapCommitted OBJECT-TYPE\n    SYNTAX      JvmUnsigned64TC\n    UNITS       \"bytes\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"\n        Total amount of memory (in bytes) committed by heap memory pools.\n\n\tSee java.lang.management.MemoryMXBean.getHeapMemoryUsage().\n\t         getCommitted()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryMXBean,\n              java.lang.management.MemoryUsage\"\n    ::= { jvmMemory 12 }\n\njvmMemoryHeapMaxSize OBJECT-TYPE\n    SYNTAX      JvmUnsigned64TC\n    UNITS       \"bytes\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"\n        Total maximum size of memory (in bytes) for all heap memory pools.\n\n\tSee java.lang.management.MemoryMXBean.getHeapMemoryUsage().getMax()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryMXBean,\n              java.lang.management.MemoryUsage\"\n    ::= { jvmMemory 13 }\n\n-- We use the range jvmMemory.[20..29] for objects related to global JVM \n-- heap memory usage, as returned by\n--      lang.management.MemoryMXBean.getNonHeapMemoryUsage().\n-- Object identifiers in the range jvmMemory.[24..29] are not used but are\n-- reserved for future evolution of this MIB.\n--\njvmMemoryNonHeapInitSize OBJECT-TYPE\n    SYNTAX      JvmUnsigned64TC\n    UNITS       \"bytes\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"\n        Total amount of memory (in bytes) that the Java virtual machine \n        initially requests from the operating system for memory management\n\tfor non heap memory pools.\n\n\tSee java.lang.management.MemoryMXBean.getNonHeapMemoryUsage().getInit()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryMXBean,\n              java.lang.management.MemoryUsage\"\n    ::= { jvmMemory 20 }\n\n\njvmMemoryNonHeapUsed OBJECT-TYPE\n    SYNTAX      JvmUnsigned64TC\n    UNITS       \"bytes\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"\n        Total amount of used memory (in bytes) from non heap memory pools.\n\n\tSee java.lang.management.MemoryMXBean.getNonHeapMemoryUsage().getUsed()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryMXBean,\n              java.lang.management.MemoryUsage\"\n    ::= { jvmMemory 21 }\n\njvmMemoryNonHeapCommitted OBJECT-TYPE\n    SYNTAX      JvmUnsigned64TC\n    UNITS       \"bytes\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"\n        Total amount of memory (in bytes) committed by non heap memory pools.\n\n\tSee java.lang.management.MemoryMXBean.\n\t         getNonHeapMemoryUsage().getCommitted()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryMXBean,\n              java.lang.management.MemoryUsage\"\n    ::= { jvmMemory 22 }\n\njvmMemoryNonHeapMaxSize OBJECT-TYPE\n    SYNTAX      JvmUnsigned64TC\n    UNITS       \"bytes\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"\n        Total maximum size of memory (in bytes) for all non heap memory pools.\n\n\tSee java.lang.management.MemoryMXBean.getNonHeapMemoryUsage().getMax()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryMXBean,\n              java.lang.management.MemoryUsage\"\n    ::= { jvmMemory 23 }\n\n-- The object identifiers in the range jvmMemory.[30-99] are not used but are \n-- reserved for future evolution of this MIB. \n--\n-- The JVM Memory Manager Table\n--\n-- The jvmMemManagerTable represent memory manager abstract entities.\n-- The jvmMemManagerTable contains one row per memory manager. In \n-- addition, those memory managers which are also garbage collectors have\n-- an extension row in the jvmMemGCTable.\n--\n-- See J2SE 5.0 API Specification, java.lang.management.MemoryMXBean for\n--     a detailed description of the memory subsystem.\n--\n-- See J2SE 5.0 API Specification, java.lang.management.MemoryManagerMXBean \n--     for more information on memory managers.\n-- \n-----------------------------------------------------------------------\n--\n-- We use the range jvmMemory.[100..109] for objects related to memory \n-- managers. \n-- Object identifiers in the range jvmMemory.[102-109] are not used \n-- but are reserved for future evolution of this MIB. \n--\njvmMemManagerTable OBJECT-TYPE\n    SYNTAX      SEQUENCE OF JvmMemManagerEntry\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"The Memory Manager Table contains the whole list of Memory \n\t Managers  as returned by ManagementFactory.getMemoryManagerMXBeans(). \n\n\t When a MemoryManagerMXBean object is an instance of \n\t GarbageCollectorMXBean, then additional information specific to \n\t the GarbageCollectorMXBean class will be found in the \n\t jvmGCTable, at the same index. \n\n\t Relationships between MemoryManagers and MemoryPools are shown \n\t by the Memory Manager-Pool Relation table (jvmMemMgrPoolRelTable). \n\t\"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryManagerMXBean\"\n    ::= { jvmMemory 100 }\n\njvmMemManagerEntry OBJECT-TYPE\n    SYNTAX      JvmMemManagerEntry\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"A jvmMemManagerEntry conceptual row represent an instance of the\n\t java.lang.management.MemoryManagerMXBean interface. If that instance \n\t is also an instance of java.lang.management.GarbageCollectorMXBean, \n\t then additional information will be found in the jvmGCTable, at the \n         same index.\n\n\t Columnar objects in this table are mapped from attributes of\n\t the MemoryManagerMXBean interface.\n\n\t See java.lang.management.MemoryManagerMXBean\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryManagerMXBean\"\n    INDEX { jvmMemManagerIndex }\n    ::= { jvmMemManagerTable 1 }\n\nJvmMemManagerEntry ::= SEQUENCE {\n        jvmMemManagerIndex JvmPositive32TC,\n        jvmMemManagerName  JvmJavaObjectNameTC,\n\tjvmMemManagerState JvmValidityStateTC\n}\n\njvmMemManagerIndex OBJECT-TYPE\n    SYNTAX      JvmPositive32TC\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"An index opaquely computed by the agent and which uniquely \n\t identifies a Memory Manager.\n\n\t The jvmMemManagerIndex index is opaquely computed by the agent,\n\t from e.g the hash code of the MemoryManager (or MemoryManager name). \n\t The agent is responsible for allocating a free index when it needs \n\t one (e.g. if two objects have the same hash, then it may increment\n\t one of the values until the conflict is resolved). As a result a \n\t manager must not depend on the value of that index across, \n\t e.g. reboot of the agent, as this value is not guaranteed to \n\t stay identical after the agent restarts.\n\t \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryManagerMXBean\"\n    ::= { jvmMemManagerEntry 1 }\n\njvmMemManagerName OBJECT-TYPE\n    SYNTAX      JvmJavaObjectNameTC\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The name of this memory manager, as returned by \n\t MemoryManagerMXBean.getName().\n\n\t See java.mangement.MemoryManagerMXBean.getName().\n\t\"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryManagerMXBean\"\n    ::= { jvmMemManagerEntry 2 }\n\njvmMemManagerState OBJECT-TYPE\n    SYNTAX      JvmValidityStateTC\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"\n         Indicates whether this memory manager is valid in the Java \n         virtual machine. A memory manager becomes invalid once the \n         Java virtual machine removes it from the memory system.\n\n\t See java.lang.management.MemoryManagerMXBean.isValid()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryManagerMXBean\"\n    ::= { jvmMemManagerEntry 3 }\n\n\n-- The JVM Garbage Collector Table\n--\n-- The jvmMemGCTable is an extension of the jvmMemManagerTable.\n-- It represents garbage collector abstract entities. A garbage collector \n-- is a memory manager responsible for reclaiming  memory occupied by \n-- unreachable objects.\n-- \n-- A garbage collector is thus represented by one row in the \n-- jvmMemManagerTable, plus an extension row in the jvmMemGCTable.\n-- The extension row in the jvmMemGCTable contains those attributes which\n-- are specific to garbage collectors.\n--\n-- See J2SE 5.0 API Specification, java.lang.management.MemoryMXBean for\n--     a detailed description of the memory subsystem.\n--\n-- See J2SE 5.0 API Specification, java.lang.management.MemoryManagerMXBean \n--     for more information on memory managers, and \n--     java.lang.management.GarbageCollectorMXBean for more information on \n--     garbage collectors.\n-- \n-----------------------------------------------------------------------\n\njvmMemGCTable OBJECT-TYPE\n    SYNTAX      SEQUENCE OF JvmMemGCEntry\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"The Garbage Collector table provides additional information \n\t on those MemoryManagers which are also GarbageCollectors. \n\t This table extends the  jvmMemManagerTable table. The index \n\t used in the jvmMemGCTable table is imported from the \n\t jvmMemManagerTable table. If a row from the jvmMemManagerTable \n\t table is deleted, and if it has an extension in the jvmMemGCTable \n\t table, then the extension row will also be deleted.\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.GarbageCollectorMXBean\"\n    ::= { jvmMemory 101 }\n\njvmMemGCEntry OBJECT-TYPE\n    SYNTAX      JvmMemGCEntry\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"Provide additional information on Garbage Collectors.\n\t \n\t Columnar objects in this table are mapped from the\n\t GarbageCollectorMXBean interface.\n\n\t See java.lang.management.GarbageCollectorMXBean\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.GarbageCollectorMXBean\"\n    INDEX   { jvmMemManagerIndex }\n    ::= {jvmMemGCTable 1 }\n\nJvmMemGCEntry ::= SEQUENCE {\n        jvmMemGCCount  Counter64,\n        jvmMemGCTimeMs JvmTimeMillis64TC\n}\n\njvmMemGCCount OBJECT-TYPE\n    SYNTAX      Counter64\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The total number of collections that have occurred, \n\t as returned by GarbageCollectorMXBean.getCollectionCount().\n\t \n\t If garbage collection statistics are not available, this\n\t object is set to 0. \n\n\t See java.lang.management.GarbageCollectorMXBean.getCollectionCount()\n\t\"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.GarbageCollectorMXBean\"\n    ::= { jvmMemGCEntry 2 }\n\njvmMemGCTimeMs OBJECT-TYPE\n    SYNTAX      JvmTimeMillis64TC\n    UNITS       \"milliseconds\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The approximate accumulated collection elapsed time in \n\t milliseconds, since the Java virtual machine has started. \n\t This object is set to 0 if the collection elapsed time is \n\t undefined for this collector.\n\n\t See java.lang.management.GarbageCollectorMXBean.getCollectionTime()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.GarbageCollectorMXBean\"\n    DEFVAL { 0 }\n    ::= { jvmMemGCEntry 3 }\n\n-- The JVM Memory Pool Table\n--\n-- The jvmMemPoolTable represent memory pool abstract entities.\n-- The jvmMemPoolTable contains one row per memory pool. \n--\n-- See J2SE 5.0 API Specification, java.lang.management.MemoryMXBean for\n--     a detailed description of the memory subsystem.\n--\n-- See J2SE 5.0 API Specification, java.lang.management.MemoryPoolMXBean \n--     for more information on memory pool.\n-- \n-----------------------------------------------------------------------\n--\n-- We use the range jvmMemory.[110..119] for objects related to memory pools. \n-- Object identifiers in the range jvmMemory.[111-119] are not used but\n-- are reserved for future evolution of this MIB. \n--\njvmMemPoolTable OBJECT-TYPE\n    SYNTAX      SEQUENCE OF JvmMemPoolEntry\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"The Memory Pool Table contains the whole list of MemoryPools \n\t as returned by ManagementFactory.getMemoryPoolMXBeans(). \n\t\"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean\"\n    ::= { jvmMemory 110 }\n\njvmMemPoolEntry OBJECT-TYPE\n    SYNTAX      JvmMemPoolEntry\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"\n         Represents a memory pool. The pool may contain heap memory or\n         non-heap memory. A row in this table represents\n\t an instance of MemoryPoolMXBean.\n\n\t See java.lang.management.MemoryPoolMXBean\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean\"\n    INDEX { jvmMemPoolIndex }\n    ::= { jvmMemPoolTable 1 }\n\nJvmMemPoolEntry ::= SEQUENCE {\n        jvmMemPoolIndex                 JvmPositive32TC,\n\tjvmMemPoolName                  JvmJavaObjectNameTC,\n\tjvmMemPoolType                  JvmManagedMemoryTypeTC,\n\tjvmMemPoolState                 JvmValidityStateTC,\n\tjvmMemPoolPeakReset             JvmTimeMillis64TC,\n\n\tjvmMemPoolInitSize              JvmUnsigned64TC,\n\tjvmMemPoolUsed                  JvmUnsigned64TC,\n\tjvmMemPoolCommitted             JvmUnsigned64TC,\n\tjvmMemPoolMaxSize               JvmUnsigned64TC,\n\n\tjvmMemPoolPeakUsed              JvmUnsigned64TC,\n\tjvmMemPoolPeakCommitted         JvmUnsigned64TC,\n\tjvmMemPoolPeakMaxSize           JvmUnsigned64TC,\t\n\t\n\tjvmMemPoolCollectUsed           JvmUnsigned64TC,\n\tjvmMemPoolCollectCommitted      JvmUnsigned64TC,\n\tjvmMemPoolCollectMaxSize        JvmUnsigned64TC,\n\n\tjvmMemPoolThreshold             JvmUnsigned64TC,\n\tjvmMemPoolThreshdCount          Counter64,\n\tjvmMemPoolThreshdSupport        JvmImplSupportStateTC,\t\n\tjvmMemPoolCollectThreshold      JvmUnsigned64TC,\n\tjvmMemPoolCollectThreshdCount   Counter64,\n\tjvmMemPoolCollectThreshdSupport JvmImplSupportStateTC\n\n}\n\njvmMemPoolIndex OBJECT-TYPE\n    SYNTAX      JvmPositive32TC\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"An index value opaquely computed by the agent which uniquely\n\t identifies a row in the jvmMemPoolTable.\n\n\t The jvmMemPoolIndex index is opaquely computed by the agent, \n\t from e.g the hash code of the MemoryPool (or MemoryPool name). \n\t The agent is responsible for allocating a free index when it \n\t needs one (e.g. if two objects have the same hash, then it may \n\t increment one of the values until the conflict is resolved).  \n\t As a result a manager must not depend on the value of that \n\t index across, e.g. reboot of the agent, as this value is not \n\t guaranteed to stay identical after the agent restarts.\n        \"\n    ::= { jvmMemPoolEntry 1 }\n\njvmMemPoolName OBJECT-TYPE\n    SYNTAX      JvmJavaObjectNameTC\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The name of this memory pool, as returned by \n         MemoryPoolMXBean.getName().\n\n\t See java.lang.management.MemoryPoolMXBean.getName()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean\"\n    ::= { jvmMemPoolEntry 2 }\n\njvmMemPoolType OBJECT-TYPE\n    SYNTAX      JvmManagedMemoryTypeTC\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The type of memory managed in this pool. This pool may be used for\n\t heap memory or non-heap memory.\n\n\t See java.lang.management.MemoryPoolMXBean.getMemoryType()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean\"\n    ::= { jvmMemPoolEntry 3 }\n\njvmMemPoolState OBJECT-TYPE\n    SYNTAX      JvmValidityStateTC\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"\n         Indicates whether this memory pool is valid in the Java \n         virtual machine. A memory pool becomes invalid once the \n         Java virtual machine removes it from the memory system.\n\n\t See java.lang.management.MemoryPoolMXBean.isValid()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean\"\n    ::= { jvmMemPoolEntry 4 }\n\njvmMemPoolPeakReset OBJECT-TYPE\n    SYNTAX      JvmTimeMillis64TC\n    UNITS       \"milliseconds\"\n    MAX-ACCESS  read-write\n    STATUS      current\n    DESCRIPTION\n\t\"\n        This object indicates the last time - in milliseconds - at which \n        the peak memory usage statistic of this memory pool was reset\n\tto the current memory usage. This corresponds to a time stamp\n\tas returned by java.lang.System.currentTimeMillis();\n\n\tSetting this object to a time earlier than its current time value\n\thas no effect. Setting this object to a time later than its current\n\ttime value causes the peak memory usage statistic of this memory \n\tpool to be reset to the current memory usage. The new value of this\n\tobject will be the time at which the reset operation is triggered.\n\n\tThere could be a delay between the time at which the reset operation \n\tis triggered and the time at which the actual resetting happens, so\n\tthis value is only indicative.\n\n\t See java.lang.management.MemoryPoolMXBean.resetPeakUsage()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean\"\n    ::= { jvmMemPoolEntry 5 }\n\n\n-- The object identifier arcs in the range jvmMemPoolEntry.[6-9] are \n-- reserved for future evolution of this MIB.\n--\n-- We use the range jvmMemPoolEntry.[10..19] for objects related to this \n-- pool memory usage, as returned by \n--      java.lang.management.MemoryPoolMXBean.getUsage().\n-- Object identifiers in the range jvmMemPoolEntry.[14..19] are not \n-- used but are reserved for future evolution of this MIB.\n--\njvmMemPoolInitSize OBJECT-TYPE\n    SYNTAX      JvmUnsigned64TC\n    UNITS       \"bytes\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"\n        Initial size of this memory pool.\n\n\tSee java.lang.management.MemoryPoolMXBean.getUsage().getInit()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean,\n              java.lang.management.MemoryUsage\"\n    ::= { jvmMemPoolEntry 10 }\n\n\njvmMemPoolUsed OBJECT-TYPE\n    SYNTAX      JvmUnsigned64TC\n    UNITS       \"bytes\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"\n        Amount of used memory in this memory pool.\n\n\tSee java.lang.management.MemoryPoolMXBean.getUsage().getUsed()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean,\n              java.lang.management.MemoryUsage\"\n    ::= { jvmMemPoolEntry 11 }\n\njvmMemPoolCommitted OBJECT-TYPE\n    SYNTAX      JvmUnsigned64TC\n    UNITS       \"bytes\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"\n        Amount of committed memory in this memory pool.\n\n\tSee java.lang.management.MemoryPoolMXBean.getUsage().getCommitted()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean,\n              java.lang.management.MemoryUsage\"\n    ::= { jvmMemPoolEntry 12 }\n\njvmMemPoolMaxSize OBJECT-TYPE\n    SYNTAX      JvmUnsigned64TC\n    UNITS       \"bytes\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"\n        Maximal size of this memory pool.\n\n\tSee java.lang.management.MemoryPoolMXBean.getUsage().getMax()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean,\n              java.lang.management.MemoryUsage\"\n    ::= { jvmMemPoolEntry 13 }\n\n-- We use the range jvmMemPoolEntry.[20..29] for objects related to \n-- this pool peak memory usage, as returned by \n--      java.lang.management.MemoryPoolMXBean.getPeakUsage().\n-- The object identifier arc jvmMemPoolEntry.20 which would have been \n-- used for the initial size is not used because the notion of initial \n-- size in the context of peak usage is meaningless. \n-- Therefore, we start numbering objects at 21.\n-- Object identifiers in the range jvmMemPoolEntry.[24..29] are not \n-- used but are reserved for future evolution of this MIB.\n--\njvmMemPoolPeakUsed OBJECT-TYPE\n    SYNTAX      JvmUnsigned64TC\n    UNITS       \"bytes\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"\n        Amount of used memory in this memory pool when the peak usage \n\twas reached.\n\n\tSee java.lang.management.MemoryPoolMXBean.getPeakUsage().getUsed()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean,\n              java.lang.management.MemoryUsage\"\n    ::= { jvmMemPoolEntry 21 }\n\njvmMemPoolPeakCommitted OBJECT-TYPE\n    SYNTAX      JvmUnsigned64TC\n    UNITS       \"bytes\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"\n        Amount of committed memory in this memory pool when the peak usage \n\twas reached.\n\n\tSee java.lang.management.MemoryPoolMXBean.getPeakUsage().getCommitted()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean,\n              java.lang.management.MemoryUsage\"\n    ::= { jvmMemPoolEntry 22 }\n\njvmMemPoolPeakMaxSize OBJECT-TYPE\n    SYNTAX      JvmUnsigned64TC\n    UNITS       \"bytes\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"\n        Maximal size of this memory pool when the peak usage \n\twas reached.\n\n\tSee java.lang.management.MemoryPoolMXBean.getPeakUsage().getMax()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean,\n              java.lang.management.MemoryUsage\"\n    ::= { jvmMemPoolEntry 23 }\n\n-- We use the range jvmMemPoolEntry.[30..39] for objects related to this \n-- pool collection memory usage, as returned by \n--      java.lang.management.MemoryPoolMXBean.getCollectionUsage().\n-- The object identifier arc jvmMemPoolEntry.30 which would have been used \n-- for the initial size is not used because the notion of initial size in the\n-- context of collection usage is meaningless. \n-- Therefore, we start numbering objects at 31.\n-- Object identifiers in the range jvmMemPoolEntry.[34..39] are not used \n-- but are reserved for future evolution of this MIB.\n--\njvmMemPoolCollectUsed OBJECT-TYPE\n    SYNTAX      JvmUnsigned64TC\n    UNITS       \"bytes\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"\n         The amount of used memory at the most recent time that the \n\t Java virtual machine has expended effort in recycling unused objects\n\t in this memory pool.  \n\t \n\tSee java.lang.management.MemoryPoolMXBean.getCollectionUsage().getUsed()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean,\n              java.lang.management.MemoryUsage\"\n    ::= { jvmMemPoolEntry 31 }\n\njvmMemPoolCollectCommitted OBJECT-TYPE\n    SYNTAX      JvmUnsigned64TC\n    UNITS       \"bytes\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"\n         The amount of committed memory at the most recent time that the \n\t Java virtual machine has expended effort in recycling unused objects\n\t in this memory pool.  \n\t \n\t See java.lang.management.MemoryPoolMXBean.getCollectionUsage().\n            getCommitted()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean,\n              java.lang.management.MemoryUsage\"\n    ::= { jvmMemPoolEntry 32 }\n\njvmMemPoolCollectMaxSize OBJECT-TYPE\n    SYNTAX      JvmUnsigned64TC\n    UNITS       \"bytes\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"\n         The value of the maximum amount of memory at the most recent time \n\t that the Java virtual machine has expended effort in recycling \n\t unused objects in this memory pool.  \n\t \n\tSee java.lang.management.MemoryPoolMXBean.getCollectionUsage().getMax()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean,\n              java.lang.management.MemoryUsage\"\n    ::= { jvmMemPoolEntry 33 }\n\n-- Object identifiers in the range jvmMemPoolEntry.[40-109] are reserved \n-- for future evolution of this MIB.\n--\n-- We use the range jvmMemPoolEntry.[110..119] for objects related to this \n-- pool memory usage thresholds (range jvmMemPoolEntry.[10..19] was used for \n-- this pool memory usage). \n-- Object identifier arcs in the range jvmMemPoolEntry.[113..119] are not \n-- used but are reserved for future evolution of this MIB.\n--\njvmMemPoolThreshold OBJECT-TYPE\n    SYNTAX      JvmUnsigned64TC\n    UNITS       \"bytes\"\n    MAX-ACCESS  read-write\n    STATUS      current\n    DESCRIPTION\n\t\"The threshold value for the memory usage of this memory pool, \n\t in bytes. A zero value (0) indicates that no threshold value is\n         configured.\n\t When the amount of used memory crosses over this threshold \n\t value the JVM will trigger a usage memory threshold exceeded \n\t notification, and the jvmMemPoolThreshdCount increases.\n\n\t If memory usage threshold is not supported, then this object, if\n\t implemented, will always be equals to 0. In that case, attempting \n         to set this object will trigger an inconsistentValue error.\n\n\t See also jvmMemPoolThreshdSupport.\n\n\t See java.lang.management.MemoryPoolMXBean.getUsageThreshold(),\n             java.lang.management.MemoryPoolMXBean.setUsageThreshold(long),\n             java.lang.management.MemoryPoolMXBean.getUsageThresholdCount(),\n             java.lang.management.MemoryPoolMXBean.isUsageThresholdSupported()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean\"\n    DEFVAL { 0 }\n    ::= { jvmMemPoolEntry 110 }\n\njvmMemPoolThreshdCount OBJECT-TYPE\n    SYNTAX      Counter64\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The number of times that the memory usage has crossed\n\t the usage threshold, as detected by the Java virtual machine.\n\n\t If memory usage threshold is not supported, then this object, if\n\t implemented, will always be equals to 0.\n\n\t See also jvmMemPoolThresholdSupport.\n\n\t See java.lang.management.MemoryPoolMXBean.getUsageThresholdCount(),\n             java.lang.management.MemoryPoolMXBean.isUsageThresholdSupported()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean\"\n    ::= { jvmMemPoolEntry 111 }\n\njvmMemPoolThreshdSupport OBJECT-TYPE\n    SYNTAX      JvmImplSupportStateTC\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"Tells whether this memory pool supports usage threshold.\n\n\t See java.lang.management.MemoryPoolMXBean.isUsageThresholdSupported()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean\"\n    ::= { jvmMemPoolEntry 112 }\n\n-- Object identifiers in the range jvmMemPoolEntry.[120-129] are reserved \n-- for future evolution of this MIB.\n--\n-- We use the range jvmMemPoolEntry.[130..139] for objects related to \n-- this pool memory collection usage thresholds (range \n-- jvmMemPoolEntry.[30..39] was used for this pool collection memory usage). \n-- Object identifiers in the range jvmMemPoolEntry.[133..139] are not used \n-- but are reserved for future evolution of this MIB.\n--\njvmMemPoolCollectThreshold OBJECT-TYPE\n    SYNTAX      JvmUnsigned64TC\n    UNITS       \"bytes\"\n    MAX-ACCESS  read-write\n    STATUS      current\n    DESCRIPTION\n\t\"The threshold value for the collection usage of this memory pool, \n\t in bytes. A zero value (0) indicates that no threshold value is\n         configured.\n\t When the amount of used memory crosses over this threshold \n\t value the JVM will trigger a collection memory threshold exceeded\n\t notification, and the jvmMemPoolCollectThreshdCount increases.\n\n\t If collection usage threshold is not supported, then this object, if\n\t implemented, will always be equals to 0. In that case, attempting \n         to set this object will trigger an inconsistentValue error.\n\n\t See also jvmMemPoolCollectThreshdSupport.\n\n\t See java.lang.management.MemoryPoolMXBean.\n                                  getCollectionUsageThreshold(),\n             java.lang.management.MemoryPoolMXBean.\n                                  setCollectionUsageThreshold(long),\n\t     java.lang.management.MemoryPoolMXBean.\n\t                          isCollectionUsageThresholdSupported(),\n\t     java.lang.management.MemoryPoolMXBean.\n\t                          getCollectionUsageThresholdCount()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean\"\n    DEFVAL { 0 }\n    ::= { jvmMemPoolEntry 131 }\n\njvmMemPoolCollectThreshdCount OBJECT-TYPE\n    SYNTAX      Counter64\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The number of times that the memory usage has crossed\n\t the collection usage threshold, as detected by the Java virtual \n\t machine.\n\n\t If memory usage threshold is not supported, then this object, if\n\t implemented, will always be equals to 0.\n\n\t See also jvmMemPoolCollectThreshdSupport.\n\n\t See java.lang.management.MemoryPoolMXBean.\n                                  getCollectionUsageThresholdCount(),\n             java.lang.management.MemoryPoolMXBean.\n                                  isCollectionUsageThresholdSupported()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean\"\n    ::= { jvmMemPoolEntry 132 }\n\njvmMemPoolCollectThreshdSupport OBJECT-TYPE\n    SYNTAX      JvmImplSupportStateTC\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"Tells whether this memory pool supports collection usage threshold.\n\n\t See java.lang.management.MemoryPoolMXBean.\n                       isCollectionUsageThresholdSupported()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean\"\n    ::= { jvmMemPoolEntry 133 }\n\n-- The JVM Memory Manager-Pool Relation Table \n-----------------------------------------------------------------------\n-- The JVM Memory Pool Table\n--\n-- The jvmMemPoolTable represent memory pool abstract entities.\n-- The jvmMemPoolTable contains one row per memory pool. \n--\n-- See J2SE 5.0 API Specification, java.lang.management.MemoryMXBean for\n--     a detailed description of the memory subsystem.\n--\n-- See J2SE 5.0 API Specification, java.lang.management.MemoryPoolMXBean \n--     for more information on memory pool.\n-- \n-----------------------------------------------------------------------\n--\n-- We use the range jvmMemory.[110..119] for objects related to memory pools. \n-- Object identifier arcs in the range jvmMemory.[111-119] are not used \n-- but are reserved for future evolution of this MIB. \n--\n\njvmMemMgrPoolRelTable OBJECT-TYPE\n    SYNTAX      SEQUENCE OF JvmMemMgrPoolRelEntry\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"The Memory Manager-Pool Relation Table shows the \n         Memory Manager / Memory Pool relations, as returned by \n         MemoryPoolMXBean.getMemoryManagerNames() and \n         MemoryManagerMXBean.getMemoryPoolNames(). \n         This table imports the indexes from the jvmMemManagerTable table \n         and jvmMemPoolTable table. The jvmMemMgrRelManagerName and \n         jvmMemMgrRelPoolName objects are not actually necessary since \n         the indexes are self-sufficient to express the relationship - \n         but the names  will make the table more understandable when displayed \n         in a management console. \n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean,\n\t      java.lang.management.MemoryManagerMXBean\"\n    ::= { jvmMemory 120 }\n\njvmMemMgrPoolRelEntry OBJECT-TYPE\n    SYNTAX      JvmMemMgrPoolRelEntry\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"A row in this table indicates that the Memory Manager identified\n         by jvmMemManagerIndex manages the Memory Pool identified by\n         jvmMemPoolIndex. Note that a pool may be managed by several \n         memory managers, and a memory manager can manage several \n         memory pool.\n\n\t See java.lang.management.MemoryManagerMXBean.getMemoryPoolNames(),\n\t     java.lang.management.MemoryPoolMXBean.getMemoryManagerNames()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean,\n\t      java.lang.management.MemoryManagerMXBean\"\n    INDEX { jvmMemManagerIndex, jvmMemPoolIndex }\n    ::= { jvmMemMgrPoolRelTable 1 }\n\nJvmMemMgrPoolRelEntry ::= SEQUENCE {\n       jvmMemMgrRelManagerName JvmJavaObjectNameTC,\n       jvmMemMgrRelPoolName    JvmJavaObjectNameTC\n}\n\njvmMemMgrRelManagerName OBJECT-TYPE\n    SYNTAX      JvmJavaObjectNameTC\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The name of the memory manager.\n\n\t See java.manangement.MemoryManagerMXBean.getName();\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n\t      java.lang.management.MemoryManagerMXBean\"\n    ::= { jvmMemMgrPoolRelEntry 2 }\n\njvmMemMgrRelPoolName OBJECT-TYPE\n    SYNTAX      JvmJavaObjectNameTC\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The name of the memory pool.\n\n\t See java.manangement.MemoryPoolMXBean.getName();\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryPoolMXBean\"\n    ::= { jvmMemMgrPoolRelEntry 3 }\n\n\n-----------------------------------------------------------------------\n--\n-- The JVM Thread group\n--\n-- A collection of objects used to monitor threads in the\n-- Java Virtual Machine. These objects define the SNMP management \n-- interface for the thread system of the Java virtual machine. \n--\n-- The jvmThreadInstanceTable represents the threads which are currently\n-- alive in the system. The representation of a thread is derived from the\n-- set of methods in the ThreadMXBean that return information about a\n-- given thread.\n--\n-- See J2SE 5.0 API Specification, java.lang.management.ThreadMXBean for\n--     a detailed description of the threading subsystem.\n-- \n-----------------------------------------------------------------------\n\n--\n-----------------------------------------------------------------------\n\njvmThreading   OBJECT IDENTIFIER ::= { jvmMgtMIBObjects 3 }\n\n-- The following objects are mapped from the ThreadMXBean interface.\n-----------------------------------------------------------------------\n\njvmThreadCount OBJECT-TYPE\n    SYNTAX      Gauge32\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The current number of live threads.\n\n\t See java.lang.management.ThreadMXBean.getThreadCount()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ThreadMXBean\"\n    ::= { jvmThreading 1 }\n\njvmThreadDaemonCount OBJECT-TYPE\n    SYNTAX      Gauge32\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The current number of daemon threads.\n\t \n\t See java.lang.management.ThreadMXBean.getDaemonThreadCount()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ThreadMXBean\"\n    ::= { jvmThreading 2 }\n\njvmThreadPeakCount OBJECT-TYPE\n    SYNTAX      Counter32\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The peak thread count since the execution of the application.\n\n\t See java.lang.management.ThreadMXBean.getPeakThreadCount()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ThreadMXBean\"\n    ::= { jvmThreading 3 }\n\njvmThreadTotalStartedCount OBJECT-TYPE\n    SYNTAX      Counter64\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The total number of threads created and started since the Java\n\t Virtual Machine started.\n\n\t See java.lang.management.ThreadMXBean.getTotalStartedThreadCount()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ThreadMXBean\"\n    ::= { jvmThreading 4 }\n\njvmThreadContentionMonitoring OBJECT-TYPE\n    SYNTAX      JvmImplOptFeatureStateTC\n    MAX-ACCESS  read-write\n    STATUS      current\n    DESCRIPTION\n\t\"The state of the Thread Contention Monitoring feature.\n         This feature can be:\n\n\t unsupported: The JVM does not support Thread Contention Monitoring.\n\t enabled    : The JVM supports Thread Contention Monitoring, and it\n\t              is enabled.\n\t disabled   : The JVM supports Thread Contention Monitoring, and it\n\t              is disabled.\n\n         Only enabled(3) and disabled(4) may be supplied as values to a\n         SET request. unsupported(1) can only be set internally by the\n         agent.\n\n\t When the feature is unsupported(1), any attempt to change\n\t that value will fail: trying to set this object to \n         enabled(3) or disabled(4) will result in an `inconsistentValue'\n         error. Trying to set it to any other value will result in an\n\t `wrongValue' error.\n\n\t See java.lang.management.ThreadMXBean.\n\t                     isThreadContentionMonitoringSupported(),\n\t     java.lang.management.ThreadMXBean.\n                             isThreadContentionMonitoringEnabled(),\n             java.lang.management.ThreadMXBean.\n                             setThreadContentionMonitoringEnabled()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ThreadMXBean\"\n    ::= { jvmThreading 5 }\n\njvmThreadCpuTimeMonitoring OBJECT-TYPE\n    SYNTAX      JvmImplOptFeatureStateTC\n    MAX-ACCESS  read-write\n    STATUS      current\n    DESCRIPTION\n\t\"The state of the Thread CPU Time Monitoring feature.\n         This feature can be:\n\n\t unsupported: The JVM does not support Thread CPU Time Monitoring.\n\t enabled    : The JVM supports Thread CPU Time Monitoring, and it\n\t              is enabled.\n\t disabled   : The JVM supports Thread CPU Time Monitoring, and it\n\t              is disabled.\n\n         Only enabled(3) and disabled(4) may be supplied as values to a\n         SET request. unsupported(1) can only be set internally by the\n         agent.\n\n\t When the feature is unsupported(1), any attempt to change\n\t that value will fail: trying to set this object to \n         enabled(3) or disabled(4) will result in an `inconsistentValue'\n         error. Trying to set it to any other value will result in an\n\t `wrongValue' error.\n\n\t See java.lang.management.ThreadMXBean.\n\t                     isThreadCpuTimeSupported(),\n\t     java.lang.management.ThreadMXBean.\n                             isThreadCpuTimeEnabled(),\n             java.lang.management.ThreadMXBean.\n                             setThreadCpuTimeEnabled()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ThreadMXBean\"\n    ::= { jvmThreading 6 }\n\njvmThreadPeakCountReset OBJECT-TYPE\n    SYNTAX      JvmTimeMillis64TC\n    UNITS       \"milliseconds\"\n    MAX-ACCESS  read-write\n    STATUS      current\n    DESCRIPTION\n\t\"\n        This object indicates the last time - in milliseconds - at which \n        the peak thread count was reset to the current thread count.\n\tThis corresponds to a time stamp as returned by\n\tjava.lang.System.currentTimeMillis().\n\n\tSetting this object to a time earlier than its current time value\n\thas no effect. Setting this object to a time later than its current\n\ttime value causes the peak thread count statistic to be reset to \n        the current thread count. The new value of this object will be \n        the time at which the reset operation is triggered.\n\n\tThere could be a delay between the time at which the reset operation \n\tis triggered and the time at which the actual resetting happens, so\n\tthis value is only indicative.\n\n\t See java.lang.management.ThreadMXBean.resetPeakThreadCount()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ThreadMXBean\"\n    ::= { jvmThreading 7 }\n\n\n-- Object identifiers in the range jvmThreading.[8-10] are reserved \n-- for future evolution of this MIB.\n--\n-----------------------------------------------------------------------\n-- The JVM Thread Instance Table\n--\n-- The jvmThreadInstanceTable represents the threads which are currently\n-- alive in the system. The representation of a thread is derived from the\n-- set of methods in the ThreadMXBean that return information about a\n-- given thread.\n--\n-- See J2SE 5.0 API Specification, java.lang.management.ThreadMXBean for\n--     a detailed description of the threading subsystem.\n-- See also J2SE 5.0 API Specification, java.lang.management.ThreadInfo,\n--     and java.lang.Thread\n-- \n-----------------------------------------------------------------------\n\njvmThreadInstanceTable OBJECT-TYPE\n    SYNTAX      SEQUENCE OF JvmThreadInstanceEntry\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"The Thread Instance Table is built from all the methods of \n\t ThreadMXBean that take a ThreadID as parameter. \n\n\t See java.lang.management.ThreadMXBean.getAllThreadIds()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ThreadMXBean\"\n    ::= { jvmThreading 10 }\n\njvmThreadInstanceEntry OBJECT-TYPE\n    SYNTAX      JvmThreadInstanceEntry\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"A row in this table represents a live thread.\n\t Attributes in this row are built from all the methods of \n\t ThreadMXBean that take a ThreadID as parameter.\n\n\t See java.lang.management.ThreadMXBean\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ThreadMXBean\"\n    INDEX { jvmThreadInstIndex }\n    ::= { jvmThreadInstanceTable 1 }\n\nJvmThreadInstanceEntry ::= SEQUENCE {\n        jvmThreadInstIndex            JvmIndex64TC,\n\tjvmThreadInstId               JvmUnsigned64TC,\n\tjvmThreadInstState            JvmThreadStateTC,\n        jvmThreadInstBlockCount       Counter64,\n        jvmThreadInstBlockTimeMs      JvmTimeMillis64TC,\n        jvmThreadInstWaitCount        Counter64,\n        jvmThreadInstWaitTimeMs       JvmTimeMillis64TC,\n        jvmThreadInstCpuTimeNs        JvmTimeNanos64TC,\n\tjvmThreadInstLockName         JvmJavaObjectNameTC,\n\tjvmThreadInstLockOwnerPtr     RowPointer,\n\tjvmThreadInstName             JvmJavaObjectNameTC\n}\n\njvmThreadInstIndex OBJECT-TYPE\n    SYNTAX      JvmIndex64TC\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"An index uniquely identifying a live thread, and directly\n         derived from the value of jvmThreadInstId. The jvmThreadInstId \n\t cannot be used directly as index in the table, because integer \n\t indexes cannot exceed an unsigned 32 int. \n\t \n\t The jvmThreadInstIndex index is an 8 byte octet string as\n         defined by the JvmIndex64TC TEXTUAL-CONVENTION. Its value is \n         directly derived from the value of the corresponding ThreadID\n         returned by jvmThreadInstId.\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ThreadMXBean, java.lang.Thread\"\n    ::= { jvmThreadInstanceEntry 1 }\n\njvmThreadInstId OBJECT-TYPE\n    SYNTAX      JvmUnsigned64TC\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The thread ID, as returned by Thread.getId().\n\n\t See java.lang.management.ThreadMXBean.getThreadInfo(long,boolean).\n\t                          getThreadId()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ThreadMXBean, java.lang.Thread\"\n    ::= { jvmThreadInstanceEntry 2 }\n\njvmThreadInstState OBJECT-TYPE\n    SYNTAX      JvmThreadStateTC\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The state of this thread instance.\n\n\t See java.lang.management.ThreadMXBean.getThreadInfo(long,boolean).\n\t                          getThreadState()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ThreadMXBean\"\n    ::= { jvmThreadInstanceEntry 3 }\n\njvmThreadInstBlockCount OBJECT-TYPE\n    SYNTAX      Counter64\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The total number of times that this thread has blocked to enter\n         or re-enter a monitor..\n\n\t See java.lang.management.ThreadMXBean.getThreadInfo(long,boolean).\n\t                          getBlockedCount()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ThreadMXBean\"\n    ::= { jvmThreadInstanceEntry 4 }\n\njvmThreadInstBlockTimeMs OBJECT-TYPE\n    SYNTAX      JvmTimeMillis64TC\n    UNITS       \"milliseconds\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The approximate accumulated elapsed time (in millisecond) \n\t that a thread has blocked to enter or re-enter a monitor since\n         it has started - or since thread contention monitoring was\n\t enabled.\n\n\t This object is always set to 0 if thread contention monitoring \n\t is disabled or not supported.\n\n\t See java.lang.management.ThreadMXBean.getThreadInfo(long,boolean).\n\t                          getBlockedTime()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ThreadMXBean\"\n    ::= { jvmThreadInstanceEntry 5 }\n\njvmThreadInstWaitCount OBJECT-TYPE\n    SYNTAX      Counter64\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The total number of times that this thread has waited for\n         notification.\n\n\t See java.lang.management.ThreadMXBean.getThreadInfo(long,boolean).\n\t                          getWaitedCount()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ThreadMXBean\"\n    ::= { jvmThreadInstanceEntry 6 }\n\njvmThreadInstWaitTimeMs OBJECT-TYPE\n    SYNTAX      JvmTimeMillis64TC\n    UNITS       \"milliseconds\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The approximate accumulated elapsed time (in millisecond) \n\t that a thread has waited on a monitor through a \n         java.lang.Object.wait method since it has started - or since \n\t thread contention monitoring wasenabled.\n\n\t This object is always set to 0 if thread contention monitoring \n\t is disabled or not supported.\n\n\t See java.lang.management.ThreadMXBean.getThreadInfo(long,boolean).\n\t                          getWaitedTime()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ThreadMXBean\"\n    ::= { jvmThreadInstanceEntry 7 }\n\njvmThreadInstCpuTimeNs OBJECT-TYPE\n    SYNTAX      JvmTimeNanos64TC\n    UNITS       \"nanoseconds\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The approximate accumulated CPU time (in nanosecond) for a thread\n         since it has started - or since thread CPU time monitoring was\n\t enabled.\n\n\t If the thread of the specified ID is not alive or does not exist,\n\t or the CPU time measurement is disabled or not supported, \n\t this object is set to 0.\n\n\t See java.lang.management.ThreadMXBean.getThreadCpuTime(long),\n\t     java.lang.management.ThreadMXBean.isThreadCpuTimeSupported(),\n\t     java.lang.management.ThreadMXBean.isThreadCpuTimeEnabled()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ThreadMXBean\"\n    ::= { jvmThreadInstanceEntry 8 }\n\njvmThreadInstName OBJECT-TYPE\n    SYNTAX      JvmJavaObjectNameTC\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"This thread name - as returned by Thread.getThreadName().\n\n\t See java.lang.management.ThreadInfo.getThreadName()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ThreadMXBean,\n              java.lang.management.ThreadInfo\"\n    ::= { jvmThreadInstanceEntry 9 }\n\njvmThreadInstLockName OBJECT-TYPE\n    SYNTAX      JvmJavaObjectNameTC\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The string representation of the monitor lock that this thread \n\t is blocked to enter or waiting to be notified through the \n\t Object.wait method. \n\n\t See J2SE 5.0 API Specification, \n             java.lang.management.ThreadInfo.getLockName()\n         for more information on the format of this string.\n\n\t If this thread is not blocked then a zero-length string is returned.\n\n\t Note that the SNMP agent may have to truncate the string returned\n         by the underlying API if it does not fit in the JvmJavaObjectNameTC \n\t (1023 bytes max).\n\n\t See java.lang.management.ThreadInfo.getLockName()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ThreadMXBean,\n              java.lang.management.ThreadInfo\"\n    ::= { jvmThreadInstanceEntry 10 }\n\njvmThreadInstLockOwnerPtr OBJECT-TYPE\n    SYNTAX      RowPointer\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"A pointer to the thread which owns the monitor of the\n\t object on which this thread instance is blocked.\n         This object will point to jvmThreadInstId of the\n\t lock owner thread.\n\n\t If this thread is not blocked then 0.0 is returned.\n\n\t See java.lang.management.ThreadInfo.getLockOwnerId()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.ThreadMXBean,\n              java.lang.management.ThreadInfo\"\n    ::= { jvmThreadInstanceEntry 11 }\n\n-----------------------------------------------------------------------\n--\n-- The JVM Runtime group\n--\n-- A collection of objects used to monitor the Java Virtual Machine\n-- Runtime. These objects define the SNMP management interface for the \n-- runtime system of the Java virtual machine.\n--\n-- The JVM Runtime group defines object mapped from the\n-- java.lang.management.RuntimeMXBean interface.\n--\n-- See J2SE 5.0 API Specification, java.lang.management.RuntimeMXBean for\n--     a detailed description of the runtime system.\n--\n-----------------------------------------------------------------------\n\njvmRuntime   OBJECT IDENTIFIER ::= { jvmMgtMIBObjects 4 }\n\n-- The following objects are mapped from the RuntimeMXBean interface.\n-----------------------------------------------------------------------\n\njvmRTName OBJECT-TYPE\n    SYNTAX      DisplayString\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The name representing the running Java virtual machine.\n\n\t Note that the SNMP agent may have to truncate the name returned\n         by the underlying API if it does not fit in the DisplayString \n\t (255 bytes max).\n\n\t See java.lang.management.RuntimeMXBean.getName()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRuntime 1 }\n\njvmRTVMName OBJECT-TYPE\n    SYNTAX      JvmJavaObjectNameTC\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The Java virtual machine implementation name.\n\n\t See java.lang.management.RuntimeMXBean.getVmName()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRuntime 2 }\n\njvmRTVMVendor OBJECT-TYPE\n    SYNTAX      DisplayString\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The Java virtual machine implementation vendor.\n\n\t Note that the SNMP agent may have to truncate the string returned\n         by the underlying API if it does not fit in the DisplayString \n\t (255 bytes max).\n\n\t See java.lang.management.RuntimeMXBean.getVmVendor()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRuntime 3 }\n\njvmRTVMVersion OBJECT-TYPE\n    SYNTAX      DisplayString\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The Java virtual machine implementation version.\n\n\t Note that the SNMP agent may have to truncate the string returned\n         by the underlying API if it does not fit in the DisplayString \n\t (255 bytes max).\n\n\t See java.lang.management.RuntimeMXBean.getVmVersion()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRuntime 4 }\n\njvmRTSpecName OBJECT-TYPE\n    SYNTAX      DisplayString\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The Java virtual machine specification name.\n\n\t Note that the SNMP agent may have to truncate the string returned\n         by the underlying API if it does not fit in the DisplayString \n\t (255 bytes max).\n\n\t See java.lang.management.RuntimeMXBean.getSpecName()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRuntime 5 }\n\njvmRTSpecVendor OBJECT-TYPE\n    SYNTAX      DisplayString\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The Java virtual machine specification vendor.\n\n\t Note that the SNMP agent may have to truncate the string returned\n         by the underlying API if it does not fit in the DisplayString \n\t (255 bytes max).\n\n         See java.lang.management.RuntimeMXBean.getSpecVendor()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRuntime 6 }\n\njvmRTSpecVersion OBJECT-TYPE\n    SYNTAX      DisplayString\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The Java virtual machine specification version.\n\n\t Note that the SNMP agent may have to truncate the string returned\n         by the underlying API if it does not fit in the DisplayString \n\t (255 bytes max).\n\n         See java.lang.management.RuntimeMXBean.getSpecVersion()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRuntime 7 }\n\njvmRTManagementSpecVersion OBJECT-TYPE\n    SYNTAX      DisplayString\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The version of the management specification for the Java virtual \n\t machine implementation.\n\n\t Note that the SNMP agent may have to truncate the string returned\n         by the underlying API if it does not fit in the DisplayString \n\t (255 bytes max).\n\n         See java.lang.management.RuntimeMXBean.getManagementSpecVersion()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRuntime 8 }\n\njvmRTBootClassPathSupport OBJECT-TYPE\n    SYNTAX      JvmImplSupportStateTC\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"Indicates whether the Java virtual machine supports the \n\t boot class path mechanism used by the bootstrap class loader \n         to search for class files.\n\n\t See java.lang.management.RuntimeMXBean.isBootClassPathSupported()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRuntime 9 }\n\njvmRTInputArgsCount OBJECT-TYPE\n    SYNTAX      JvmPositive32TC\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The number of input arguments passed to the Java Virtual Machine.\n\t \n\t See java.lang.management.RuntimeMXBean.getInputArguments()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRuntime 10 }\n\njvmRTUptimeMs OBJECT-TYPE\n    SYNTAX      JvmTimeMillis64TC\n    UNITS       \"milliseconds\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"Uptime of the Java virtual machine, in milliseconds. This is\n         equivalent to ( System.currentTimeMillis() - jvmStartTimeMs ).\n\n         See also jvmRTStartTimeMs.\n\n         See java.lang.management.RuntimeMXBean.getUptime()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRuntime 11 }\n\njvmRTStartTimeMs OBJECT-TYPE\n    SYNTAX      JvmTimeMillis64TC\n    UNITS       \"milliseconds\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The approximate time when the Java virtual machine started, in\n         milliseconds. This is a time stamp as returned by \n         System.currentTimeMillis(). This time will not change unless\n         the Java Virtual Machine is restarted. \n\n         See also jvmRTUptimeMs.\n\n         See java.lang.management.RuntimeMXBean.getStartTime()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRuntime 12 }\n\n\n-- Object identifiers in the range jvmRuntime.[13-19] are reserved \n-- for future evolution of this MIB.\n--\n-----------------------------------------------------------------------\n--\n-- The JVM Input Argument Table\n--\n-- The jvmRTInputArgsTable contains one row per input argument given on\n-- the Java command line.\n--\n-- See J2SE 5.0 API Specification,\n--     java.lang.management.RuntimeMXBean.getInputArguments()\n--     for more information.\n-----------------------------------------------------------------------\n\njvmRTInputArgsTable OBJECT-TYPE\n    SYNTAX      SEQUENCE OF JvmRTInputArgsEntry\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"The Input Argument Table lists the input arguments passed\n\t to the Java Virtual Machine. \n\n\t The jvmRTInputArgsIndex is the index of the argument in \n\t the array returned by RuntimeMXBean.getInputArguments().\n\t \n\t See java.lang.management.RuntimeMXBean.getInputArguments()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRuntime 20 }\n\njvmRTInputArgsEntry OBJECT-TYPE\n    SYNTAX      JvmRTInputArgsEntry\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"Represent an input argument passed to the Java Virtual Machine.\n\n\t See java.lang.management.RuntimeMXBean.getInputArguments()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    INDEX { jvmRTInputArgsIndex }\n    ::= { jvmRTInputArgsTable 1 }\n\nJvmRTInputArgsEntry ::= SEQUENCE {\n        jvmRTInputArgsIndex JvmPositive32TC,\n\tjvmRTInputArgsItem  JvmArgValueTC\n}\n\njvmRTInputArgsIndex OBJECT-TYPE\n    SYNTAX      JvmPositive32TC\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"The index of the input argument, as in the array returned \n\t by RuntimeMXBean.getInputArguments().\n\n\t See java.lang.management.RuntimeMXBean.getInputArguments()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRTInputArgsEntry 1 }\n\njvmRTInputArgsItem OBJECT-TYPE\n    SYNTAX      JvmArgValueTC\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"An input argument at index jvmRTInputArgsIndex, as in the array \n\t returned by RuntimeMXBean.getInputArguments().\n\n\t Note that the SNMP agent may have to truncate the string returned\n         by the underlying API if it does not fit in the JvmArgValueTC \n         (1023 bytes max).\n\n\t See java.lang.management.RuntimeMXBean.getInputArguments()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRTInputArgsEntry 2 }\n\n\n-----------------------------------------------------------------------\n--\n-- The JVM Boot Class Path Table\n--\n-- The jvmRTBootClassPathTable contains one row per path element in the\n-- bootclasspath. This table may not be implemented (or may be empty) if\n-- the bootclasspath feature is not supported by the underlying \n-- implementation.\n--\n-- See J2SE 5.0 API Specification,\n--     java.lang.management.RuntimeMXBean.getBootClassPath()\n--     java.lang.management.RuntimeMXBean.isBootClassPathSupported()\n--     for more information.\n-----------------------------------------------------------------------\n\njvmRTBootClassPathTable OBJECT-TYPE\n    SYNTAX      SEQUENCE OF JvmRTBootClassPathEntry\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"The boot class path that is used by the bootstrap class loader \n\t to search for a class file for loading.\n\t \n\t Note that the SNMP agent may have to truncate the bootclasspath\n\t elements contained in the string returned by the underlying API \n\t if it does not fit in the JvmPathElementTC (1023 bytes max).\n\n\t This table is not implemented (or empty) if jvmRTBootClassPathSupport\n         is unsupported(1).\n\t \n\t See java.lang.management.RuntimeMXBean.getBootClassPath()\n\t     java.lang.management.RuntimeMXBean.isBootClassPathSupported()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRuntime 21 }\n\njvmRTBootClassPathEntry OBJECT-TYPE\n    SYNTAX      JvmRTBootClassPathEntry\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"Represent a path element in the Java Virtual Machine bootclasspath.  \n\n\t See java.lang.management.RuntimeMXBean.getBootClassPath()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    INDEX { jvmRTBootClassPathIndex }\n    ::= { jvmRTBootClassPathTable 1 }\n\nJvmRTBootClassPathEntry ::= SEQUENCE {\n        jvmRTBootClassPathIndex JvmPositive32TC,\n\tjvmRTBootClassPathItem  JvmPathElementTC\n}\n\njvmRTBootClassPathIndex OBJECT-TYPE\n    SYNTAX      JvmPositive32TC\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"The index of the path element, as in the array obtained \n\t by splitting RuntimeMXBean.getBootClassPath() in its elementary path\n\t constituents.\n\n\t See java.lang.management.RuntimeMXBean.getBootClassPath()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRTBootClassPathEntry 1 }\n\njvmRTBootClassPathItem OBJECT-TYPE\n    SYNTAX      JvmPathElementTC\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"An path element at index jvmRTBootClassPathIndex, as in the \n\t array obtained by splitting RuntimeMXBean.getBootClassPath() in \n\t its elementary path constituents.\n\n\t Note that the SNMP agent may have to truncate the string returned\n         by the underlying API if it does not fit in the JvmPathElementTC\n\t (1023 bytes max).\n\n\t See java.lang.management.RuntimeMXBean.getBootClassPath()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRTBootClassPathEntry 2 }\n\n-----------------------------------------------------------------------\n--\n-- The JVM Class Path Table\n--\n-- The jvmRTClassPathTable contains one row per path element in the\n-- classpath.\n--\n-- See J2SE 5.0 API Specification,\n--     java.lang.management.RuntimeMXBean.getClassPath()\n--     for more information.\n-----------------------------------------------------------------------\n\njvmRTClassPathTable OBJECT-TYPE\n    SYNTAX      SEQUENCE OF JvmRTClassPathEntry\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"The  class path that is used by the system class loader \n\t to search for a class file.\n\t \n\t Note that the SNMP agent may have to truncate the classpath\n\t elements contained in the string returned by the underlying API \n\t if it does not fit in the JvmPathElementTC (1023 bytes max).\n\n\t See java.lang.management.RuntimeMXBean.getClassPath()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRuntime 22 }\n\njvmRTClassPathEntry OBJECT-TYPE\n    SYNTAX      JvmRTClassPathEntry\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"Represent a path element in the Java Virtual Machine classpath.  \n\n\t See java.lang.management.RuntimeMXBean.getClassPath()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    INDEX { jvmRTClassPathIndex }\n    ::= { jvmRTClassPathTable 1 }\n\nJvmRTClassPathEntry ::= SEQUENCE {\n        jvmRTClassPathIndex JvmPositive32TC,\n\tjvmRTClassPathItem  JvmPathElementTC\n}\n\njvmRTClassPathIndex OBJECT-TYPE\n    SYNTAX      JvmPositive32TC\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"The index of the path element, as in the array obtained \n\t by splitting RuntimeMXBean.getClassPath() in its elementary\n\t path constituents.\n\n\t See java.lang.management.RuntimeMXBean.getClassPath()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRTClassPathEntry 1 }\n\njvmRTClassPathItem OBJECT-TYPE\n    SYNTAX      JvmPathElementTC\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"An path element at index jvmRTClassPathIndex, as in the array \n\t obtained by splitting RuntimeMXBean.getClassPath() in its elementary\n\t path constituents.\n\n\t Note that the SNMP agent may have to truncate the string returned\n         by the underlying API if it does not fit in the JvmPathElementTC\n\t (1023 bytes max).\n\n\t See java.lang.management.RuntimeMXBean.getClassPath()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRTClassPathEntry 2 }\n\n-----------------------------------------------------------------------\n--\n-- The JVM Library Path Table\n--\n-- The jvmRTLibraryPathTable contains one row per path element in the\n-- librarypath.\n--\n-- See J2SE 5.0 API Specification,\n--     java.lang.management.RuntimeMXBean.getLibraryPath()\n--     for more information.\n-----------------------------------------------------------------------\n\njvmRTLibraryPathTable OBJECT-TYPE\n    SYNTAX      SEQUENCE OF JvmRTLibraryPathEntry\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"The  library path.\n\t \n\t Note that the SNMP agent may have to truncate the librarypath\n\t elements contained in the string returned by the underlying API \n\t if it does not fit in the JvmPathElementTC (1023 bytes max).\n\n\t See java.lang.management.RuntimeMXBean.getLibraryPath()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRuntime 23 }\n\njvmRTLibraryPathEntry OBJECT-TYPE\n    SYNTAX      JvmRTLibraryPathEntry\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"Represent a path element in the Java Virtual Machine librarypath.  \n\n\t See java.lang.management.RuntimeMXBean.getLibraryPath()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    INDEX { jvmRTLibraryPathIndex }\n    ::= { jvmRTLibraryPathTable 1 }\n\nJvmRTLibraryPathEntry ::= SEQUENCE {\n        jvmRTLibraryPathIndex JvmPositive32TC,\n\tjvmRTLibraryPathItem  JvmPathElementTC\n}\n\njvmRTLibraryPathIndex OBJECT-TYPE\n    SYNTAX      JvmPositive32TC\n    MAX-ACCESS  not-accessible\n    STATUS      current\n    DESCRIPTION\n\t\"The index of the path element, as in the array obtained \n\t by splitting RuntimeMXBean.getLibraryPath() in its elementary\n\t constituents.\n\n\t See java.lang.management.RuntimeMXBean.getLibraryPath()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRTLibraryPathEntry 1 }\n\njvmRTLibraryPathItem OBJECT-TYPE\n    SYNTAX      JvmPathElementTC\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"An path element at index jvmRTLibraryPathIndex, as in the array \n\t obtained by splitting RuntimeMXBean.getLibraryPath() in its elementary\n\t path constituents.\n\n\t Note that the SNMP agent may have to truncate the string returned\n         by the underlying API if it does not fit in the JvmPathElementTC\n\t (1023 bytes max).\n\n\t See java.lang.management.RuntimeMXBean.getLibraryPath()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.RuntimeMXBean\"\n    ::= { jvmRTLibraryPathEntry 2 }\n\n-----------------------------------------------------------------------\n--\n-- The JVM Compilation group\n--\n-- A collection of objects used to monitor the Java Virtual Machine\n-- Runtime Compiler (JIT). These objects define the SNMP management \n-- interface for the compilation system of the Java virtual machine. \n--\n-- The JVM Compilation group defines object mapped from the\n-- java.lang.management.CompilationMXBean interface.\n--\n-- See J2SE 5.0 API Specification, java.lang.management.CompilationMXBean for\n--     a detailed description of the runtime system.\n--\n-----------------------------------------------------------------------\n\njvmCompilation   OBJECT IDENTIFIER ::= { jvmMgtMIBObjects 5 }\n\n-- The following objects are mapped from the CompilationMXBean interface.\n-----------------------------------------------------------------------\n\njvmJITCompilerName OBJECT-TYPE\n    SYNTAX      JvmJavaObjectNameTC\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The name of the Just-in-time (JIT) compiler.\n\n\t See java.lang.management.CompilationMXBean.getName()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.CompilationMXBean\"\n    ::= { jvmCompilation 1 }\n\njvmJITCompilerTimeMs OBJECT-TYPE\n    SYNTAX      JvmTimeMillis64TC\n    UNITS       \"milliseconds\"\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"Gets the approximate accumulated elapsed time (in milliseconds) \n\t spent in compilation since the Java virtual machine has started. \n\t If multiple threads are used for compilation, this value is \n\t the summation of the approximate time that each thread \n\t spent in compilation.\n\n\t If compiler time monitoring is not supported, then this object\n\t remains set to 0.\n\t \n\t See java.lang.management.CompilationMXBean.getTotalCompilationTime()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.CompilationMXBean\"\n    ::= { jvmCompilation 2 }\n\n\njvmJITCompilerTimeMonitoring OBJECT-TYPE\n    SYNTAX      JvmImplSupportStateTC\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"Indicates whether the Java virtual machine supports\n\t compilation time monitoring.\n\n\t See java.lang.management.CompilationMXBean.\n                             isCompilationTimeMonitoringSupported()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.CompilationMXBean\"\n    ::= { jvmCompilation 3 }\n\n-----------------------------------------------------------------------\n--\n-- The JVM Operating System group\n--\n-- A collection of objects used to monitor some resource of the \n-- Operating System the Java Virtual Machine is running on. These objects \n-- define the SNMP management interface offered by the Java virtual machine\n-- for the operating system on which it is running. \n--\n-- The JVM Operating System group defines object mapped from the\n-- java.lang.management.OperatingSystemMXBean interface.\n--\n-- See J2SE 5.0 API Specification, java.lang.management.OperatingSystemMXBean \n--     for a detailed description of the operating system.\n--\n-----------------------------------------------------------------------\n\njvmOS   OBJECT IDENTIFIER ::= { jvmMgtMIBObjects 6 }\n\n-- The following objects are mapped from the OperatingSystemMXBean interface.\n-----------------------------------------------------------------------\n\njvmOSName OBJECT-TYPE\n    SYNTAX      JvmJavaObjectNameTC\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The operating system name.\n\n\t See java.lang.management.OperatingSystemMXBean.getName()\n\t\"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.OperatingSystemMXBean\"\n    ::= { jvmOS 1 }\n\njvmOSArch OBJECT-TYPE\n    SYNTAX      DisplayString\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The operating system architecture.\n\n\t Note that the SNMP agent may have to truncate the string returned\n         by the underlying API if it does not fit in the DisplayString \n\t (255 bytes max).\n\n\t See java.lang.management.OperatingSystemMXBean.getArch()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.OperatingSystemMXBean\"\n    ::= { jvmOS 2 }\n\njvmOSVersion OBJECT-TYPE\n    SYNTAX      DisplayString\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The operating system version.\n\n\t Note that the SNMP agent may have to truncate the string returned\n         by the underlying API if it does not fit in the DisplayString \n\t (255 bytes max).\n\n\t See java.lang.management.OperatingSystemMXBean.getVersion()\n\t\"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.OperatingSystemMXBean\"\n    ::= { jvmOS 3 }\n\njvmOSProcessorCount OBJECT-TYPE\n\n    SYNTAX      Integer32\n    MAX-ACCESS  read-only\n    STATUS      current\n    DESCRIPTION\n\t\"The number of processors available to the Java virtual machine.\n\n         See java.lang.management.OperatingSystemMXBean.getAvailableProcessors()\n        \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.OperatingSystemMXBean\"\n    ::= { jvmOS 4 }\n\n--\n-- NOTIFICATIONS\n--\n-----------------------------------------------------------------------\n\n--\n-- Low Memory Notifications\n--\n\njvmMgtMIBMemoryNotifs    OBJECT IDENTIFIER ::= { jvmMgtMIBNotifications 2 }\njvmMgtMIBLowMemoryNotifs OBJECT IDENTIFIER ::= { jvmMgtMIBMemoryNotifs  1 }\n\njvmLowMemoryPrefix OBJECT IDENTIFIER \n    ::= { jvmMgtMIBLowMemoryNotifs 0 } \n\n-- Not used at this time, but reserved for future evolution of this MIB:\n--\n-- jvmLowMemoryData OBJECT IDENTIFIER \n--    ::= { jvmMgtMIBLowMemoryNotifs 1 } \n--\n\njvmLowMemoryPoolUsageNotif NOTIFICATION-TYPE\n    OBJECTS {  jvmMemPoolName, jvmMemPoolUsed, jvmMemPoolThreshdCount }\n    STATUS current\n    DESCRIPTION\n           \"This notification is sent when the memory usage threshold of\n\t    a memory pool is exceeded.\n           \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryNotification,\n\t      java.lang.management.MemoryPoolMXBean\"\n    ::= { jvmLowMemoryPrefix  1 }\n\njvmLowMemoryPoolCollectNotif NOTIFICATION-TYPE\n    OBJECTS {  jvmMemPoolName, jvmMemPoolCollectUsed, \n               jvmMemPoolCollectThreshdCount }\n    STATUS current\n    DESCRIPTION\n           \"This notification is sent when the collection memory usage \n\t    threshold of a memory pool is exceeded.\n           \"\n    REFERENCE \"J2SE 5.0 API Specification,\n              java.lang.management.MemoryNotification,\n\t      java.lang.management.MemoryPoolMXBean\"\n    ::= { jvmLowMemoryPrefix  2 }\n\n--\n-- Conformance Section\n--\n-----------------------------------------------------------------------\n\n-- conformance information\n\njvmMgtMIBCompliances\n               OBJECT IDENTIFIER ::= { jvmMgtMIBConformance 1 }\njvmMgtMIBGroups  \n               OBJECT IDENTIFIER ::= { jvmMgtMIBConformance 2 }\n\n\n-- compliance statements\n\njvmManagementCompliance MODULE-COMPLIANCE\n    STATUS  current\n    DESCRIPTION\n            \"The compliance statement for SNMP entities which\n            implement this MIB.\"\n    MODULE  -- this module\n    MANDATORY-GROUPS { \n                     jvmClassLoadingBasicGroup,\n\t\t     jvmClassLoadingSetGroup,\n\t\t     jvmMemoryBasicGroup,\n\t\t     jvmMemoryHeapUsageGroup,\n\t\t     jvmMemoryNonHeapUsageGroup,\n\t\t     jvmMemorySetGroup,\n\t\t     jvmMemManagerGroup,\n\t\t     jvmMemGCGroup,\n\t\t     jvmMemPoolBasicGroup,\n\t\t     jvmMemPoolUsageGroup,\n\t\t     jvmMemPoolPeakUsageGroup,\n\t\t     jvmMemPoolCollectUsageGroup,\n\t\t     jvmMemMgrPoolRelationGroup,\n\t\t     jvmThreadBasicGroup,\n\t\t     jvmThreadInstanceBasicGroup,\n\t\t     jvmRuntimeBasicGroup,\n\t\t     jvmOSGroup\n    }\n\n    -- optional/conditional groups\n    GROUP  jvmMemPoolMonitoringGroup\n        DESCRIPTION\n            \"This group may not be implemented if the Java virtual \n             machine does not support low memory detection in memory usage.\n\t    \"\n    GROUP  jvmMemPoolCollectMonitoringGroup\n        DESCRIPTION\n            \"This group may not be implemented if the Java virtual \n             machine does not support low memory detection in collection \n\t     memory usage.\n\t    \"\n    GROUP  jvmLowMemoryUsageNotifGroup\n        DESCRIPTION\n            \"This group may not be implemented if the Java virtual \n             machine does not support low memory usage detection.\n\t    \"\n    GROUP  jvmLowMemoryCollectNotifGroup\n        DESCRIPTION\n            \"This group may not be implemented if the Java virtual \n             machine does not support low collection memory usage detection.\n\t    \"\n    GROUP  jvmThreadInstanceCpuGroup\n        DESCRIPTION\n            \"This group may not be implemented if the Java virtual \n             machine does not support CPU time measurement for other threads.\n\t    \"\n    GROUP  jvmThreadInstanceBlockGroup\n        DESCRIPTION\n            \"This group may not be implemented if the Java virtual \n             machine does not support thread contention monitoring.\n\t    \"\n    GROUP  jvmRuntimeBootCPGroup\n        DESCRIPTION\n            \"This group may not be implemented if the underlying \n\t     implementation does not support the bootclasspath feature.\n            \"\n    GROUP  jvmJITCompilerBasicGroup\n        DESCRIPTION\n            \"This group may not be implemented if the Java virtual \n             machine has no compilation system.\n\t    \"        \n    GROUP  jvmJITCompilerTimeStatGroup\n        DESCRIPTION\n            \"This group may not be implemented if the Java virtual \n             machine has no compilation system, or does not support \n             JIT Compiler time statistics.\n\t    \"\n    ::= { jvmMgtMIBCompliances 1 }\n\n\n-- units of conformance\n\njvmClassLoadingGroups OBJECT IDENTIFIER ::= { jvmMgtMIBGroups 1 }\n\njvmClassLoadingBasicGroup  OBJECT-GROUP\n    OBJECTS {\n             jvmClassesLoadedCount,\n\t     jvmClassesTotalLoadedCount,\n\t     jvmClassesUnloadedCount\n    }\n    STATUS current\n    DESCRIPTION\n\t\"A collection of  objects that are mapped from JSR 163 \n\t java.lang.management.ClassLoadingMXBean interface.\n        \"\n    ::= { jvmClassLoadingGroups 1 }\n\njvmClassLoadingSetGroup  OBJECT-GROUP\n    OBJECTS {\n             jvmClassesVerboseLevel\n    }\n    STATUS current\n    DESCRIPTION\n\t\"A collection of writable scalar objects that are mapped from JSR 163 \n\t java.lang.management.ClassLoadingMXBean interface, and make it possible\n\t to act on class loading. Accessing these objects may\n\t require special permissions - the agent implementation is\n\t responsible for puting in place the appropriate access control\n\t if needed.\n        \"\n    ::= { jvmClassLoadingGroups 2 }\n\njvmMemoryGroups OBJECT IDENTIFIER ::= { jvmMgtMIBGroups 2 }\n\njvmMemoryBasicGroup  OBJECT-GROUP\n    OBJECTS {\n             jvmMemoryPendingFinalCount\n    }\n    STATUS current\n    DESCRIPTION\n\t\"A collection of columnar objects that are mapped from JSR 163 \n\t java.lang.management.MemoryManagerMXBean interface.\n        \"\n    ::= { jvmMemoryGroups 1 }\n\njvmMemoryHeapUsageGroup OBJECT-GROUP\n    OBJECTS {\n             jvmMemoryHeapInitSize,\n\t     jvmMemoryHeapUsed,\n\t     jvmMemoryHeapCommitted,\n\t     jvmMemoryHeapMaxSize\n    }\n    STATUS current\n    DESCRIPTION\n\t\"A collection of objects that are mapped from JSR 163 \n\t java.lang.management.MemoryMXBean.getHeapMemoryUsage().\n\t When several of these objects are requested within a single\n\t SNMP request, the agent must ensure that \n         java.lang.management.MemoryPoolMXBean.getHeapMemoryUsage() is \n\t called only once, in order to guarantee that the set of\n\t values returned for these objects remain coherent and give\n\t a consistent snapshot of the heap memory usage made by \n\t Heap Memory Pools.\n        \"\n    ::= { jvmMemoryGroups 2 }\n\njvmMemoryNonHeapUsageGroup OBJECT-GROUP\n    OBJECTS {\n             jvmMemoryNonHeapInitSize,\n\t     jvmMemoryNonHeapUsed,\n\t     jvmMemoryNonHeapCommitted,\n\t     jvmMemoryNonHeapMaxSize\n    }\n    STATUS current\n    DESCRIPTION\n\t\"A collection of objects that are mapped from JSR 163 \n\t java.lang.management.MemoryMXBean.getNonHeapMemoryUsage().\n\t When several of these objects are requested within a single\n\t SNMP request, the agent must ensure that \n         java.lang.management.MemoryPoolMXBean.getNonHeapMemoryUsage() is \n\t called only once, in order to guarantee that the set of\n\t values returned for these objects remain coherent and give\n\t a consistent snapshot of the non heap memory usage made by \n\t Non Heap Memory Pools.\n        \"\n    ::= { jvmMemoryGroups 3 }\n\njvmMemorySetGroup OBJECT-GROUP\n    OBJECTS {\n             jvmMemoryGCVerboseLevel,\n\t     jvmMemoryGCCall\n    }\n    STATUS current\n    DESCRIPTION\n\t\"A collection of writable scalar objects that are mapped from JSR 163 \n\t java.lang.management.MemoryMXBean interface, and make it possible\n\t to act on the Garbage Collector. Accessing these objects may\n\t require special permissions - the agent implementation is\n\t responsible for puting in place the appropriate access control\n\t if needed.\n        \"\n    ::= { jvmMemoryGroups 4 }\n\njvmMemManagerGroup  OBJECT-GROUP\n    OBJECTS {\n\t     jvmMemManagerName,\n\t     jvmMemManagerState\n    }\n    STATUS current\n    DESCRIPTION\n\t\"A collection of columnar objects that are mapped from JSR 163 \n\t java.lang.management.MemoryManagerMXBean interface.\n        \"\n    ::= { jvmMemoryGroups 5 }\n\njvmMemGCGroup  OBJECT-GROUP\n    OBJECTS {\n             jvmMemGCCount,\n             jvmMemGCTimeMs\n    }\n    STATUS current\n    DESCRIPTION\n\t\"A collection of columnar objects that are mapped from JSR 163 \n\t java.lang.management.GarbageCollectorMXBean interface, and are\n\t specific to GarbageCollector MXBeans. \n\t These objects are used to model the inheritence link between \n\t GarbageCollectorMXBean and its super interface - MemoryManagerMXBean.\n        \"\n    ::= { jvmMemoryGroups 6 }\n\njvmMemPoolGroups OBJECT IDENTIFIER ::= { jvmMemoryGroups 7 }\n\njvmMemPoolBasicGroup  OBJECT-GROUP\n    OBJECTS {\n\t     jvmMemPoolName,\n\t     jvmMemPoolType,\n\t     jvmMemPoolState,\n\t     jvmMemPoolPeakReset,\n\t     jvmMemPoolThreshdSupport,\n\t     jvmMemPoolCollectThreshdSupport\n    }\n    STATUS current\n    DESCRIPTION\n\t\"A collection of columnar objects that are mapped from JSR 163 \n\t java.lang.management.MemoryPoolMXBean interface.\n        \"\n    ::= { jvmMemPoolGroups 1 }\n\njvmMemPoolMonitoringGroup OBJECT-GROUP\n     OBJECTS {   \n\t     jvmMemPoolThreshold,\n\t     jvmMemPoolThreshdCount\n     }\n    STATUS current\n    DESCRIPTION\n\t\"Memory usage threshold objects mapped from \n\t JSR 163 java.lang.management.MemoryPoolMXBean interface, which makes \n\t it possible to configure low memory detection.\n\t Accessing this object may require special permissions - the agent \n\t implementation is responsible for puting in place the appropriate \n\t access control if needed.\n        \"\n    ::= { jvmMemPoolGroups 2 }\n\njvmMemPoolUsageGroup OBJECT-GROUP\n    OBJECTS {\n             jvmMemPoolInitSize,\n\t     jvmMemPoolUsed,\n\t     jvmMemPoolCommitted,\n\t     jvmMemPoolMaxSize\n    }\n    STATUS current\n    DESCRIPTION\n\t\"A collection of objects that are mapped from JSR 163 \n\t java.lang.management.MemoryPoolMXBean.getUsage().\n\t When several of these objects are requested within a single\n\t SNMP request, the agent must ensure that \n         java.lang.management.MemoryPoolMXBean.getUsage() is \n\t called only once, in order to guarantee that the set of\n\t values returned for these objects remain coherent and give\n\t a consistent snapshot of the memory used by this Memory\n\t Pool.\n        \"\n    ::= { jvmMemPoolGroups 3 }\n\njvmMemPoolPeakUsageGroup OBJECT-GROUP\n    OBJECTS {\n             jvmMemPoolPeakUsed,\n\t     jvmMemPoolPeakCommitted,\n\t     jvmMemPoolPeakMaxSize\n    }\n    STATUS current\n    DESCRIPTION\n\t\"A collection of objects that are mapped from JSR 163 \n\t java.lang.management.MemoryPoolMXBean.getPeakUsage().\n\t When several of these objects are requested within a single\n\t SNMP request, the agent must ensure that \n         java.lang.management.MemoryPoolMXBean.getPeakUsage() is \n\t called only once, in order to guarantee that the set of\n\t values returned for these objects remain coherent and give\n\t a consistent snapshot of the peak memory usage made by \n\t this Memory Pool.\n        \"\n    ::= { jvmMemPoolGroups 4 }\n\njvmMemPoolCollectUsageGroup OBJECT-GROUP\n    OBJECTS {\n             jvmMemPoolCollectUsed,\n\t     jvmMemPoolCollectCommitted,\n\t     jvmMemPoolCollectMaxSize\n    }\n    STATUS current\n    DESCRIPTION\n\t\"A collection of objects that are mapped from JSR 163 \n\t java.lang.management.MemoryPoolMXBean.getCollectionUsage().\n\t When several of these objects are requested within a single\n\t SNMP request, the agent must ensure that \n         java.lang.management.MemoryPoolMXBean.getCollectionUsage() is \n\t called only once, in order to guarantee that the set of\n\t values returned for these objects remain coherent and give\n\t a consistent snapshot of the collection memory usage made by \n\t this Memory Pool.\n        \"\n    ::= { jvmMemPoolGroups 5 }\n\njvmMemPoolCollectMonitoringGroup OBJECT-GROUP\n     OBJECTS { \n\t     jvmMemPoolCollectThreshold,\n\t     jvmMemPoolCollectThreshdCount\n     }\n    STATUS current\n    DESCRIPTION\n\t\"Memory collection usage threshold objects mapped from JSR 163 \n\t java.lang.management.MemoryPoolMXBean interface, which makes \n\t it possible to configure low memory detection.\n\t Accessing this object may require special permissions - the agent \n\t implementation is responsible for putting in place the appropriate \n\t access control if needed.\n        \"\n    ::= { jvmMemPoolGroups 6 }\n\n\njvmMemMgrPoolRelationGroup  OBJECT-GROUP\n    OBJECTS {\n             jvmMemMgrRelManagerName,\n\t     jvmMemMgrRelPoolName\n    }\n    STATUS current\n    DESCRIPTION\n\t\"A collection of columnar objects that are mapped from JSR 163 \n\t java.lang.management.MemoryPoolMXBean and \n         java.lang.management.MemoryManagerMXBean interface, and show the\n\t relationship between Memory Managers and Memory Pools.\n        \"\n    ::= { jvmMemoryGroups 8 }\n\njvmThreadGroups OBJECT IDENTIFIER ::= { jvmMgtMIBGroups 3 }\n\njvmThreadBasicGroup OBJECT-GROUP\n    OBJECTS {\n             jvmThreadCount,\n\t     jvmThreadDaemonCount,\n\t     jvmThreadPeakCount,\n\t     jvmThreadTotalStartedCount,\n\t     jvmThreadContentionMonitoring,\n\t     jvmThreadCpuTimeMonitoring,\n\t     jvmThreadPeakCountReset\n    }\n    STATUS current\n    DESCRIPTION\n\t\"A collection of scalar objects that are mapped from JSR 163 \n\t java.lang.management.ThreadMXBean interface.\n        \"\n    ::= { jvmThreadGroups 1 }\n\njvmThreadInstanceGroups OBJECT IDENTIFIER ::= { jvmThreadGroups 2 }\n\njvmThreadInstanceBasicGroup OBJECT-GROUP\n    OBJECTS {\n\t     jvmThreadInstId,\n\t     jvmThreadInstState,\n\t     jvmThreadInstName,\n\t     jvmThreadInstLockName,\n\t     jvmThreadInstLockOwnerPtr\n    }\n    STATUS current\n    DESCRIPTION\n\t\"A collection of columnar objects that are mapped from JSR 163 \n\t java.lang.management.ThreadMXBean interface, and are\n\t relative to an instance of java.lang.Thread.\n        \"\n    ::= { jvmThreadInstanceGroups 1}\n\njvmThreadInstanceCpuGroup OBJECT-GROUP\n    OBJECTS {\n\t     jvmThreadInstCpuTimeNs\n    }\n    STATUS current\n    DESCRIPTION\n\t\"A columnar object mapped from JSR 163 \n\t java.lang.management.ThreadMXBean interface which provides CPU\n\t time statistics about an instance of java.lang.Thread.\n        \"\n    ::= { jvmThreadInstanceGroups 2 }\n\n    \njvmThreadInstanceBlockGroup OBJECT-GROUP\n    OBJECTS {\n\t     jvmThreadInstBlockCount,\n\t     jvmThreadInstBlockTimeMs,\n\t     jvmThreadInstWaitCount,\n\t     jvmThreadInstWaitTimeMs\n    }\n    STATUS current\n    DESCRIPTION\n\t\"A collection of columnar objects that are mapped from JSR 163 \n\t java.lang.management.ThreadMXBean interface, and which provide\n         synchronization statistics about an instance of java.lang.Thread.\n        \"\n    ::= { jvmThreadInstanceGroups 3 }\n\n\njvmRuntimeGroups OBJECT IDENTIFIER ::= { jvmMgtMIBGroups 4 }\n\njvmRuntimeBasicGroup OBJECT-GROUP\n    OBJECTS {\n             jvmRTName,\n\t     jvmRTVMName,\n\t     jvmRTVMVendor,\n\t     jvmRTVMVersion,\n\t     jvmRTSpecName,\n\t     jvmRTSpecVendor,\n\t     jvmRTSpecVersion,\n\t     jvmRTManagementSpecVersion,\n\t     jvmRTUptimeMs,\n\t     jvmRTStartTimeMs,\n\t     jvmRTBootClassPathSupport,\n\t     jvmRTInputArgsCount,\n\t     jvmRTInputArgsItem,\n\t     jvmRTClassPathItem,\n\t     jvmRTLibraryPathItem\n    }\t\n    STATUS current\n    DESCRIPTION\n\t\"A collection of objects that are mapped from JSR 163 \n\t java.lang.management.RuntimeMXBean interface.        \n        \"\n    ::= { jvmRuntimeGroups 1 }\n\n\njvmRuntimeBootCPGroup OBJECT-GROUP\n    OBJECTS {\n\t     jvmRTBootClassPathItem\n    }\t\n    STATUS current\n    DESCRIPTION\n\t\"A columnar object that is mapped from JSR 163 \n\t java.lang.management.RuntimeMXBean.getBootClassPath() interface, \n\t and provide information about bootclasspath elements.         \n        \"\n    ::= { jvmRuntimeGroups 2 }\n\njvmJITCompilerGroups OBJECT IDENTIFIER ::= { jvmMgtMIBGroups 5 }\n\njvmJITCompilerBasicGroup OBJECT-GROUP\n    OBJECTS {\n             jvmJITCompilerName,\n             jvmJITCompilerTimeMonitoring\n    }    \n    STATUS current\n    DESCRIPTION\n\t\"A collection of objects that are mapped from JSR 163 \n\t java.lang.management.CompilationMXBean interface.        \n        \"\n    ::= { jvmJITCompilerGroups 1 }\n\njvmJITCompilerTimeStatGroup OBJECT-GROUP\n    OBJECTS {\n             jvmJITCompilerTimeMs\n    }\n    STATUS current\n    DESCRIPTION\n\t\"A collection of objects that are mapped from JSR 163 \n\t java.lang.management.CompilationMXBean interface and provide\n\t time statistic about the JIT Compiler.\n        \"\n    ::= { jvmJITCompilerGroups 2 }\n    \njvmOSGroup OBJECT-GROUP\n    OBJECTS {\n             jvmOSName,\n\t     jvmOSArch,\n\t     jvmOSVersion,\n\t     jvmOSProcessorCount\n    }\n    STATUS current\n    DESCRIPTION\n\t\"A collection of objects that are mapped from JSR 163 \n\t java.lang.management.OperatingSystemMXBean interface.        \n        \"\n    ::= { jvmMgtMIBGroups 6 }\n\njvmLowMemoryUsageNotifGroup NOTIFICATION-GROUP\n    NOTIFICATIONS  {  \n             jvmLowMemoryPoolUsageNotif\n    }\n    STATUS current\n    DESCRIPTION\n           \"A collection of notifications emitted when low\n            memory usage conditions are detected.\n           \"\n    ::= { jvmMgtMIBGroups 7 }\n\njvmLowMemoryCollectNotifGroup NOTIFICATION-GROUP\n    NOTIFICATIONS  {  \n             jvmLowMemoryPoolCollectNotif\n    }\n    STATUS current\n    DESCRIPTION\n           \"A collection of notifications emitted when low\n            collection memory usage conditions are detected.\n           \"\n    ::= { jvmMgtMIBGroups 8 }\n\nEND\n"
  },
  {
    "path": "src/main/resources/templates/StatusReportTemplate.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n    <title>Tigase Status Page</title>\n    <link rel=\"stylesheet\" href=\"https://use.fontawesome.com/releases/v5.7.1/css/all.css\"\n          integrity=\"sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr\" crossorigin=\"anonymous\">\n    <link rel=\"stylesheet\" href=\"https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css\"\n          integrity=\"sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS\" crossorigin=\"anonymous\">\n    <style type=\"text/css\">\n        .infocards div.card > .card-body span.h2 {\n            font-size: 20px;\n            font-family: Nunito, -apple-system, system-ui, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n            color: #5a5c69;\n        }\n\n        .infocards div.card {\n            position: relative;\n            display: flex;\n            flex-direction: column;\n            min-width: 0;\n            word-wrap: break-word;\n            background-color: #fff;\n            background-clip: border-box;\n            border: 1px solid #edf2f9;\n            border-radius: .5rem;\n        }\n\n        .infocards .card-title {\n            color: #95aac9;\n            font-family: \"Cerebri Sans\", sans-serif;\n            font-size: 10px;\n            font-weight: 500;\n            letter-spacing: 0.8px;\n\n\n        }\n    </style>\n    <script>\n        var json = ${dataJson};\n\n    </script>\n</head>\n<script src=\"https://code.jquery.com/jquery-3.3.1.min.js\"\n        integrity=\"sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=\"\n        crossorigin=\"anonymous\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js\"\n        integrity=\"sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut\"\n        crossorigin=\"anonymous\"></script>\n<script src=\"https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js\"\n        integrity=\"sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k\"\n        crossorigin=\"anonymous\"></script>\n<body class=\"bg-light\">\n<main role=\"main\">\n    <nav class=\"navbar navbar-light bg-white shadow-sm\">\n        <span class=\"navbar-brand mb-0 h1\">Tigase Status</span>\n        <span id=\"report-creation-timstamp\" class=\"navbar-text\"></span>\n    </nav>\n    <div class=\"container mt-3 infocards\">\n        <div class=\"row\">\n\n            <div class=\"col-sm-4 mb-4\">\n                <div class=\"card shadow\">\n                    <div class=\"card-body\">\n                        <div class=\"row no-gutters align-items-center\">\n                            <div class=\"col\">\n                                <h6 class=\"card-title text-uppercase text-muted mb-2\">\n                                    CPU usage\n                                </h6>\n                                <div class=\"row no-gutters align-items-center\">\n                                    <span class=\"h2 mb-0\"><span class=\"data-cpu-usage-proc\"></span>%</span>\n                                    <div class=\"col\">\n                                        <div class=\"progress mr-2 ml-2\" style=\"height: 5px;\">\n                                            <div id=\"data-cpu-usage-proc\" class=\"progress-bar bg-info\"\n                                                 role=\"progressbar\"\n                                                 style=\"width: 0%\"></div>\n                                        </div>\n                                    </div>\n                                </div>\n\n                            </div>\n                            <div class=\"col-auto \">\n                                <i class=\"fas fa-microchip fa-2x\" style=\"color: #dddfeb;\"></i></div>\n                        </div>\n                    </div>\n                </div>\n            </div>\n            <div class=\"col-sm-4 mb-4\">\n                <div class=\"card shadow\">\n                    <div class=\"card-body\">\n                        <div class=\"row no-gutters align-items-center\">\n                            <div class=\"col\">\n                                <h6 class=\"card-title text-uppercase text-muted mb-2\">\n                                    HEAP usage\n                                </h6>\n                                <div class=\"row no-gutters align-items-center\">\n                                    <span class=\"h2 mb-0\"><span class=\"data-heap-usage-proc\"></span>%</span>\n                                    <div class=\"col\">\n                                        <div class=\"progress mr-2 ml-2\" style=\"height: 5px;\">\n                                            <div id=\"data-heap-usage-proc\" class=\"progress-bar bg-info\"\n                                                 role=\"progressbar\"\n                                                 style=\"width: 0%\"></div>\n                                        </div>\n                                    </div>\n                                </div>\n\n                            </div>\n                            <div class=\"col-auto \">\n                                <i class=\"fas fa-memory fa-2x\" style=\"color: #dddfeb;\"></i></div>\n                        </div>\n                    </div>\n                </div>\n            </div>\n\n            <div class=\"col-sm-4 mb-4\">\n                <div class=\"card shadow\">\n                    <div class=\"card-body\">\n                        <div class=\"row align-items-center\">\n                            <div class=\"col\">\n                                <h6 class=\"card-title text-uppercase text-muted mb-2\">\n                                    Load average\n                                </h6>\n                                <span id=\"data-load-average\" class=\"h2 mb-0\">&nbsp;</span>\n                            </div>\n                            <div class=\"col-auto \">\n                                <i class=\"fas fa-tachometer-alt fa-2x\" style=\"color: #dddfeb;\"></i></div>\n                        </div>\n                    </div>\n                </div>\n            </div>\n\n        </div>\n    </div>\n\n    <div class=\"container \">\n        <div class=\"card\">\n            <div class=\"card-header\">\n                Details\n            </div>\n            <div class=\"card-body\">\n                <table class=\"table\">\n                    <tbody>\n                    <tr>\n                        <th scope=\"row\" style=\"width: 20%\">Server version</th>\n                        <td class=\"version\"></td>\n                    </tr>\n                    <tr>\n                        <th scope=\"row\" style=\"width: 20%\">Uptime</th>\n                        <td class=\"data-uptime\"></td>\n                    </tr>\n                    <tr>\n                        <th scope=\"row\">Load average</th>\n                        <td><span class=\"data-load-average\"></span></td>\n                    </tr>\n                    <tr>\n                        <th scope=\"row\">CPUs no</th>\n                        <td class=\"data-cpus-no\"></td>\n                    </tr>\n                    <tr>\n                        <th scope=\"row\">Threads</th>\n                        <td class=\"data-threads-count\"></td>\n                    </tr>\n                    <tr>\n                        <th scope=\"row\">CPU usage</th>\n                        <td><span class=\"data-cpu-usage-proc\"></span>%</td>\n                    </tr>\n                    <tr>\n                        <th scope=\"row\">HEAP usage</th>\n                        <td><span class=\"data-heap-usage-proc\"></span>%</td>\n                    </tr>\n                    <tr>\n                        <th scope=\"row\">NONHEAP usage</th>\n                        <td><span class=\"data-nonheap-usage-proc\"></span>%</td>\n                    </tr>\n\n                    </tbody>\n                </table>\n            </div>\n        </div>\n\n\n        <table>\n\n        </table>\n\n    </div>\n</main>\n<script language=\"JavaScript\">\n    for (key in json) {\n        if (key.endsWith(\"-proc\")) {\n            \\$('#' + key).css('width', json[key] + \"%\");\n            \\$('.' + key).text(json[key]);\n        } else{\n            \\$('#' + key).text(json[key]);\n            \\$('.' + key).text(json[key]);\n        }\n    }\n</script>\n</body>\n</html>"
  },
  {
    "path": "src/main/resources/win-stuff/Licence.txt",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU Affero General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU Affero General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU Affero General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU Affero General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU Affero General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU Affero General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU Affero General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "src/main/resources/win-stuff/Setup.bat",
    "content": "copy \"tigase.ico\" \"..\\\"\ncopy \"wrapper\\wrapper.jar\" \"..\\jars\"\ncopy \"wrapper\\wrapper.dll\" \"..\\jars\"\ncopy \"wrapper\\wrapper.exe\" \"..\\\"\ncopy \"wrapper\\wrapper.conf\" \"..\\\"\ncopy \"wrapper\\wrapper-community-license-1.2.txt\" \"..\\\"\ncopy \"scripts\\*.*\" \"..\\\""
  },
  {
    "path": "src/main/resources/win-stuff/Tigase-minimal.iss",
    "content": "#define ver \"3.1.1-b745\";\r\n\r\n[Setup]\r\nAppName=Tigase Server\r\nAppVersion={#ver}\r\nAppVerName=Tigase XMPP Server {#ver}\r\nAppPublisher=Tigase.org\r\nAppPublisherURL=http://www.tigase.org/\r\nAppSupportURL=http://www.tigase.org/\r\nAppUpdatesURL=http://www.tigase.org/\r\nDefaultDirName={pf}\\Tigase\r\nDefaultGroupName=Tigase Server\r\nAllowNoIcons=true\r\nLicenseFile=Licence.txt\r\nOutputDir=..\\packages\r\nOutputBaseFilename=tigase-server-minimal-{#ver}\r\nCompression=lzma\r\nSolidCompression=true\r\nUninstallDisplayIcon={app}\\Tigase.ico\r\nVersionInfoCopyright=Artur Hefczyc\r\nAppCopyright=Artur Hefczyc\r\n\r\n[Languages]\r\nName: english; MessagesFile: compiler:Default.isl\r\nName: basque; MessagesFile: compiler:Languages\\Basque.isl\r\nName: brazilianportuguese; MessagesFile: compiler:Languages\\BrazilianPortuguese.isl\r\nName: catalan; MessagesFile: compiler:Languages\\Catalan.isl\r\nName: czech; MessagesFile: compiler:Languages\\Czech.isl\r\nName: danish; MessagesFile: compiler:Languages\\Danish.isl\r\nName: dutch; MessagesFile: compiler:Languages\\Dutch.isl\r\nName: finnish; MessagesFile: compiler:Languages\\Finnish.isl\r\nName: french; MessagesFile: compiler:Languages\\French.isl\r\nName: german; MessagesFile: compiler:Languages\\German.isl\r\nName: hungarian; MessagesFile: compiler:Languages\\Hungarian.isl\r\nName: italian; MessagesFile: compiler:Languages\\Italian.isl\r\nName: norwegian; MessagesFile: compiler:Languages\\Norwegian.isl\r\nName: polish; MessagesFile: compiler:Languages\\Polish.isl\r\nName: portuguese; MessagesFile: compiler:Languages\\Portuguese.isl\r\nName: russian; MessagesFile: compiler:Languages\\Russian.isl\r\nName: slovak; MessagesFile: compiler:Languages\\Slovak.isl\r\nName: slovenian; MessagesFile: compiler:Languages\\Slovenian.isl\r\nName: spanish; MessagesFile: compiler:Languages\\Spanish.isl\r\n\r\n[Files]\r\n\r\n\r\nSource: wrapper\\wrapper.exe; DestDir: {app}; Flags: ignoreversion\r\nSource: ..\\certs\\*; DestDir: {app}\\certs; Flags: ignoreversion recursesubdirs createallsubdirs\r\nSource: ..\\docs-tigase-server\\*; DestDir: {app}\\docs; Flags: ignoreversion recursesubdirs createallsubdirs\r\nSource: ..\\jars\\*; DestDir: {app}\\jars; Flags: ignoreversion recursesubdirs createallsubdirs\r\nSource: z:\\home\\kobit\\projects\\tigase\\server\\libs\\tigase-utils.jar; DestDir: {app}\\libs; Flags: ignoreversion recursesubdirs createallsubdirs\r\nSource: z:\\home\\kobit\\projects\\tigase\\server\\libs\\tigase-xmltools.jar; DestDir: {app}\\libs; Flags: ignoreversion recursesubdirs createallsubdirs\r\nSource: z:\\home\\kobit\\projects\\tigase\\server\\database\\mysql-schema.sql; DestDir: {app}\\database\\mysql-schema.sql; Flags: ignoreversion\r\nSource: z:\\home\\kobit\\projects\\tigase\\server\\database\\postgresql-schema.sql; DestDir: {app}\\database\\postgresql-schema.sql; Flags: ignoreversion\r\nSource: wrapper\\wrapper.conf; DestDir: {app}; Flags: ignoreversion\r\nSource: scripts\\InstallTigaseService.bat; DestDir: {app}; Flags: ignoreversion\r\nSource: ..\\README.html; DestDir: {app}; Flags: ignoreversion\r\nSource: scripts\\Run.bat; DestDir: {app}; Flags: ignoreversion\r\nSource: scripts\\Tigase.bat; DestDir: {app}; Flags: ignoreversion\r\nSource: scripts\\UninstallTigaseService.bat; DestDir: {app}; Flags: ignoreversion\r\nSource: Tigase.ico; DestDir: {app}; Flags: ignoreversion\r\nSource: scripts\\Uninst.bat; DestDir: {app}; Flags: ignoreversion\r\n; NOTE: Don't use \"Flags: ignoreversion\" on any shared system files\r\nSource: Licence.txt; DestDir: {app}; Flags: ignoreversion\r\nSource: wrapper\\wrapper.dll; DestDir: {app}\\libs; Flags: ignoreversion\r\nSource: wrapper\\wrapper.jar; DestDir: {app}\\libs; Flags: ignoreversion\r\nSource: ..\\etc\\tigase.conf; DestDir: {app}\\etc; Flags: ignoreversion\r\nSource: ..\\etc\\initial.properties; DestDir: {app}\\etc; Flags: ignoreversion\r\n\r\n\r\n[Dirs]\r\nName: {app}\\logs\r\n\r\n[Icons]\r\nName: {group}\\Start Tigase Server; Filename: {app}\\Run.bat; WorkingDir: {app}; IconFilename: {app}\\Tigase.ico\r\nName: {group}\\Install Tigase Service; Filename: {app}\\InstallTigaseService.bat\r\nName: {group}\\Uninstall Tigase Service; Filename: {app}\\UninstallTigaseService.bat\r\nName: {group}\\{cm:ProgramOnTheWeb,Tigase}; Filename: http://www.tigase.org/\r\n\r\n[UninstallRun]\r\nFilename: {app}\\Uninst.bat\r\n"
  },
  {
    "path": "src/main/resources/win-stuff/Tigase.iss",
    "content": "#define ver \"3.3.2-b889\";\r\n\r\n[Setup]\r\nAppName=Tigase Server\r\nAppVersion={#ver}\r\nAppVerName=Tigase XMPP Server {#ver}\r\nAppPublisher=Tigase.org\r\nAppPublisherURL=http://www.tigase.org/\r\nAppSupportURL=http://www.tigase.org/\r\nAppUpdatesURL=http://www.tigase.org/\r\nDefaultDirName={pf}\\Tigase\r\nDefaultGroupName=Tigase Server\r\nAllowNoIcons=true\r\nLicenseFile=Licence.txt\r\nOutputDir=..\\packages\r\nOutputBaseFilename=tigase-server-{#ver}\r\nCompression=lzma\r\nSolidCompression=true\r\nUninstallDisplayIcon={app}\\Tigase.ico\r\nVersionInfoCopyright=Artur Hefczyc\r\nAppCopyright=Artur Hefczyc\r\n\r\n[Languages]\r\nName: english; MessagesFile: compiler:Default.isl\r\nName: basque; MessagesFile: compiler:Languages\\Basque.isl\r\nName: brazilianportuguese; MessagesFile: compiler:Languages\\BrazilianPortuguese.isl\r\nName: catalan; MessagesFile: compiler:Languages\\Catalan.isl\r\nName: czech; MessagesFile: compiler:Languages\\Czech.isl\r\nName: danish; MessagesFile: compiler:Languages\\Danish.isl\r\nName: dutch; MessagesFile: compiler:Languages\\Dutch.isl\r\nName: finnish; MessagesFile: compiler:Languages\\Finnish.isl\r\nName: french; MessagesFile: compiler:Languages\\French.isl\r\nName: german; MessagesFile: compiler:Languages\\German.isl\r\nName: hungarian; MessagesFile: compiler:Languages\\Hungarian.isl\r\nName: italian; MessagesFile: compiler:Languages\\Italian.isl\r\nName: norwegian; MessagesFile: compiler:Languages\\Norwegian.isl\r\nName: polish; MessagesFile: compiler:Languages\\Polish.isl\r\nName: portuguese; MessagesFile: compiler:Languages\\Portuguese.isl\r\nName: russian; MessagesFile: compiler:Languages\\Russian.isl\r\nName: slovak; MessagesFile: compiler:Languages\\Slovak.isl\r\nName: slovenian; MessagesFile: compiler:Languages\\Slovenian.isl\r\nName: spanish; MessagesFile: compiler:Languages\\Spanish.isl\r\n\r\n[Files]\r\nSource: wrapper\\wrapper.exe; DestDir: {app}; Flags: ignoreversion; Languages: \r\nSource: ..\\certs\\dummy.cer; DestDir: {app}\\certs; Flags: ignoreversion recursesubdirs createallsubdirs\r\nSource: ..\\certs\\rsa-keystore; DestDir: {app}\\certs; Flags: ignoreversion recursesubdirs createallsubdirs\r\nSource: ..\\certs\\truststore; DestDir: {app}\\certs; Flags: ignoreversion recursesubdirs createallsubdirs\r\nSource: ..\\certs\\localhost.pem; DestDir: {app}\\certs; Flags: ignoreversion recursesubdirs createallsubdirs\r\nSource: ..\\docs-tigase-server\\*; DestDir: {app}\\docs; Flags: ignoreversion recursesubdirs createallsubdirs\r\nSource: ..\\jars\\*; DestDir: {app}\\jars; Flags: ignoreversion recursesubdirs createallsubdirs\r\nSource: ..\\libs\\tigase-utils.jar; DestDir: {app}\\libs; Flags: ignoreversion recursesubdirs createallsubdirs\r\nSource: ..\\libs\\tigase-xmltools.jar; DestDir: {app}\\libs; Flags: ignoreversion recursesubdirs createallsubdirs\r\nSource: ..\\libs\\bcprov-jdk16-136.jar; DestDir: {app}\\libs; Flags: ignoreversion recursesubdirs createallsubdirs\r\nSource: ..\\libs\\cindy.jar; DestDir: {app}\\libs; Flags: ignoreversion recursesubdirs createallsubdirs\r\nSource: ..\\libs\\commons-logging.jar; DestDir: {app}\\libs; Flags: ignoreversion recursesubdirs createallsubdirs\r\nSource: ..\\libs\\jdbc-mysql.jar; DestDir: {app}\\libs; Flags: ignoreversion recursesubdirs createallsubdirs\r\nSource: ..\\libs\\jdbc-postgresql.jar; DestDir: {app}\\libs; Flags: ignoreversion recursesubdirs createallsubdirs\r\nSource: ..\\libs\\jml-1.0b2.jar; DestDir: {app}\\libs; Flags: ignoreversion recursesubdirs createallsubdirs\r\nSource: ..\\libs\\jtds-1.2.2.jar; DestDir: {app}\\libs; Flags: ignoreversion recursesubdirs createallsubdirs\r\nSource: ..\\libs\\tigase-extras-0.2.0-SNAPSHOT.jar; DestDir: {app}\\libs; Flags: ignoreversion recursesubdirs createallsubdirs\r\nSource: ..\\libs\\tigase-muc-0.1.6-SNAPSHOT.jar; DestDir: {app}\\libs; Flags: ignoreversion recursesubdirs createallsubdirs\r\nSource: ..\\database\\mysql-schema.sql; DestDir: {app}\\database; Flags: ignoreversion\r\nSource: ..\\database\\postgresql-schema.sql; DestDir: {app}\\database; Flags: ignoreversion\r\nSource: ..\\database\\sqlserver-schema.sql; DestDir: {app}\\database; Flags: ignoreversion\r\nSource: wrapper\\wrapper.conf; DestDir: {app}; Flags: ignoreversion\r\nSource: scripts\\InstallTigaseService.bat; DestDir: {app}; Flags: ignoreversion\r\nSource: ..\\README.html; DestDir: {app}; Flags: ignoreversion\r\nSource: scripts\\Run.bat; DestDir: {app}; Flags: ignoreversion\r\nSource: scripts\\Tigase.bat; DestDir: {app}; Flags: ignoreversion\r\nSource: scripts\\UninstallTigaseService.bat; DestDir: {app}; Flags: ignoreversion\r\nSource: Tigase.ico; DestDir: {app}; Flags: ignoreversion\r\nSource: scripts\\Uninst.bat; DestDir: {app}; Flags: ignoreversion\r\n; NOTE: Don't use \"Flags: ignoreversion\" on any shared system files\r\nSource: Licence.txt; DestDir: {app}; Flags: ignoreversion\r\nSource: wrapper\\wrapper.dll; DestDir: {app}\\libs; Flags: ignoreversion\r\nSource: wrapper\\wrapper.jar; DestDir: {app}\\libs; Flags: ignoreversion\r\nSource: ..\\etc\\tigase.conf; DestDir: {app}\\etc; Flags: ignoreversion\r\nSource: ..\\etc\\init.properties; DestDir: {app}\\etc; Flags: ignoreversion\r\n\r\n[Dirs]\r\nName: {app}\\logs\r\n\r\n\r\n[Icons]\r\nName: {group}\\Start Tigase Server; Filename: {app}\\Run.bat; WorkingDir: {app}; IconFilename: {app}\\Tigase.ico\r\nName: {group}\\Install Tigase Service; Filename: {app}\\InstallTigaseService.bat\r\nName: {group}\\Uninstall Tigase Service; Filename: {app}\\UninstallTigaseService.bat\r\nName: {group}\\{cm:ProgramOnTheWeb,Tigase}; Filename: http://www.tigase.org/\r\n\r\n[UninstallRun]\r\nFilename: {app}\\Uninst.bat\r\n"
  },
  {
    "path": "src/main/resources/win-stuff/scripts/InstallTigaseService.bat",
    "content": "@echo off\r\nsetlocal\r\n\r\nrem ******************************************************\r\nrem Java Service Wrapper general NT service install script\r\nrem ******************************************************\r\n\r\nif \"%OS%\"==\"Windows_NT\" goto nt\r\necho This script only works with NT-based versions of Windows.\r\ngoto :eof\r\n\r\n:nt\r\nrem\r\nrem Find the application home.\r\nrem\r\nrem %~dp0 is location of current script under NT\r\nset _REALPATH=%~dp0\r\n\r\nrem Decide on the wrapper binary.\r\nset _WRAPPER_BASE=wrapper\r\nset _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe\r\nif exist \"%_WRAPPER_EXE%\" goto conf\r\nset _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe\r\nif exist \"%_WRAPPER_EXE%\" goto conf\r\nset _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%.exe\r\nif exist \"%_WRAPPER_EXE%\" goto conf\r\necho Unable to locate a Wrapper executable using any of the following names:\r\necho %_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe\r\necho %_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe\r\necho %_REALPATH%%_WRAPPER_BASE%.exe\r\npause\r\ngoto :eof\r\n\r\nrem\r\nrem Find the wrapper.conf\r\nrem\r\n:conf\r\nset _WRAPPER_CONF=\"%~f1\"\r\nif not %_WRAPPER_CONF%==\"\" goto startup\r\nset _WRAPPER_CONF=\"%_REALPATH%wrapper.conf\"\r\n\r\nrem\r\nrem Install the Wrapper as an NT service.\r\nrem\r\n:startup\r\n\"%_WRAPPER_EXE%\" -i %_WRAPPER_CONF%\r\npause\r\n\r\n"
  },
  {
    "path": "src/main/resources/win-stuff/scripts/Run.bat",
    "content": "@echo off\nset task=%1\nset filename=%0\n\nshift\nshift\nset args=%1\nshift\n:start\nif [%1] == [] goto done\nset args=%args% %1\nshift\ngoto start\n\n:schemaManager\njava -cp \"jars/*\" -Djdbc.drivers=com.mysql.jdbc.Driver -DscriptName='%filename%' tigase.db.util.SchemaManager %task% %args%\ngoto finish\n\n:done\n\nset cmd = \nset class=\"tigase.server.XMPPServer\"\nif \"%task%\" == \"upgrade-schema\" goto schemaManager\nif \"%task%\" == \"install-schema\" goto schemaManager\nif \"%task%\" == \"destroy-schema\" goto schemaManager\n\njava -cp \"jars/*\" -Djdbc.drivers=com.mysql.jdbc.Driver tigase.server.XMPPServer %args%\n\n:finish"
  },
  {
    "path": "src/main/resources/win-stuff/scripts/Tigase.bat",
    "content": "@echo off\r\nsetlocal\r\n\r\n\r\nrem *******************************************\r\nrem Java Service Wrapper general startup script\r\nrem *******************************************\r\n\r\nrem\r\nrem Resolve the real path of the wrapper.exe\r\nrem  For non NT systems, the _REALPATH and _WRAPPER_CONF values\r\nrem  can be hard-coded below and the following test removed.\r\nrem\r\nif \"%OS%\"==\"Windows_NT\" goto nt\r\necho This script only works with NT-based versions of Windows.\r\ngoto :eof\r\n\r\n:nt\r\nrem\r\nrem Find the application home.\r\nrem\r\nrem %~dp0 is location of current script under NT\r\nset _REALPATH=%~dp0\r\n\r\nrem Decide on the wrapper binary.\r\nset _WRAPPER_BASE=wrapper\r\nset _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe\r\nif exist \"%_WRAPPER_EXE%\" goto conf\r\nset _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe\r\nif exist \"%_WRAPPER_EXE%\" goto conf\r\nset _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%.exe\r\nif exist \"%_WRAPPER_EXE%\" goto conf\r\necho Unable to locate a Wrapper executable using any of the following names:\r\necho %_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe\r\necho %_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe\r\necho %_REALPATH%%_WRAPPER_BASE%.exe\r\npause\r\ngoto :eof\r\n\r\nrem\r\nrem Find the wrapper.conf\r\nrem\r\n:conf\r\nset _WRAPPER_CONF=\"%~f1\"\r\nif not %_WRAPPER_CONF%==\"\" goto startup\r\nset _WRAPPER_CONF=\"%_REALPATH%wrapper.conf\"\r\n\r\nrem\r\nrem Start the Wrapper\r\nrem\r\n:startup\r\n\"%_WRAPPER_EXE%\" -c %_WRAPPER_CONF%\r\nif not errorlevel 1 goto :eof\r\npause\r\n\r\n"
  },
  {
    "path": "src/main/resources/win-stuff/scripts/Uninst.bat",
    "content": "@echo off\r\nsetlocal\r\n\r\n\r\nrem *****************************\r\nrem This script runs on uninstall\r\nrem *****************************\r\n\r\nif \"%OS%\"==\"Windows_NT\" goto nt\r\necho This script only works with NT-based versions of Windows.\r\ngoto :eof\r\n\r\n:nt\r\nrem\r\nrem Find the application home.\r\nrem\r\nrem %~dp0 is location of current script under NT\r\nset _REALPATH=%~dp0\r\n\r\nrem Decide on the wrapper binary.\r\nset _WRAPPER_BASE=wrapper\r\nset _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe\r\nif exist \"%_WRAPPER_EXE%\" goto conf\r\nset _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe\r\nif exist \"%_WRAPPER_EXE%\" goto conf\r\nset _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%.exe\r\nif exist \"%_WRAPPER_EXE%\" goto conf\r\necho Unable to locate a Wrapper executable using any of the following names:\r\necho %_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe\r\necho %_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe\r\necho %_REALPATH%%_WRAPPER_BASE%.exe\r\ngoto :eof\r\n\r\nrem\r\nrem Find the wrapper.conf\r\nrem\r\n:conf\r\nset _WRAPPER_CONF=\"%~f1\"\r\nif not %_WRAPPER_CONF%==\"\" goto startup\r\nset _WRAPPER_CONF=\"%_REALPATH%wrapper.conf\"\r\n\r\nrem\r\nrem Uninstall the Wrapper as an NT service.\r\nrem\r\n:startup\r\n\"%_WRAPPER_EXE%\" -r %_WRAPPER_CONF%\r\n\r\n"
  },
  {
    "path": "src/main/resources/win-stuff/scripts/UninstallTigaseService.bat",
    "content": "@echo off\r\nsetlocal\r\n\r\n\r\nrem ********************************************************\r\nrem Java Service Wrapper general NT service uninstall script\r\nrem ********************************************************\r\n\r\nif \"%OS%\"==\"Windows_NT\" goto nt\r\necho This script only works with NT-based versions of Windows.\r\ngoto :eof\r\n\r\n:nt\r\nrem\r\nrem Find the application home.\r\nrem\r\nrem %~dp0 is location of current script under NT\r\nset _REALPATH=%~dp0\r\n\r\nrem Decide on the wrapper binary.\r\nset _WRAPPER_BASE=wrapper\r\nset _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe\r\nif exist \"%_WRAPPER_EXE%\" goto conf\r\nset _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe\r\nif exist \"%_WRAPPER_EXE%\" goto conf\r\nset _WRAPPER_EXE=%_REALPATH%%_WRAPPER_BASE%.exe\r\nif exist \"%_WRAPPER_EXE%\" goto conf\r\necho Unable to locate a Wrapper executable using any of the following names:\r\necho %_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe\r\necho %_REALPATH%%_WRAPPER_BASE%-windows-x86-64.exe\r\necho %_REALPATH%%_WRAPPER_BASE%.exe\r\npause\r\ngoto :eof\r\n\r\nrem\r\nrem Find the wrapper.conf\r\nrem\r\n:conf\r\nset _WRAPPER_CONF=\"%~f1\"\r\nif not %_WRAPPER_CONF%==\"\" goto startup\r\nset _WRAPPER_CONF=\"%_REALPATH%wrapper.conf\"\r\n\r\nrem\r\nrem Uninstall the Wrapper as an NT service.\r\nrem\r\n:startup\r\n\"%_WRAPPER_EXE%\" -r %_WRAPPER_CONF%\r\npause\r\n\r\n"
  },
  {
    "path": "src/main/resources/win-stuff/wrapper/wrapper-community-license-1.2.txt",
    "content": "----------------------------------------------------------------------\n-----------------                                    -----------------\n                         Tanuki Software, Ltd. \n                 Community Software License Agreement\n                             Version 1.2\n\nIMPORTANT-READ CAREFULLY: This license agreement is a legal agreement\nbetween you (\"Licensee\") and Tanuki Software, Ltd. (\"TSI\"), which\nincludes computer software, associated media, printed materials, and\nmay include online or electronic documentation ( Software ).  PLEASE\nREAD THIS AGREEMENT CAREFULLY BEFORE YOU INSTALL, COPY, DOWNLOAD OR\nUSE THE SOFTWARE ACCOMPANYING THIS PACKAGE.\n\nSection 1 - Grant of License\n\nCommunity editions of the Software are made available on the GNU\nGeneral Public License, Version 2 (\"GPLv2\"), included in Section 4 of\nthis license document.  All sections of the Community Software License\nAgreement must be complied with in addition to those of the GPLv2.\n\n\nSection 2 - Definitions \n\n2.1. \"Community Edition\" shall mean versions of the Software Program\ndistributed in source form under this license agreement, and all new\nreleases, corrections, enhancements and updates to the Software\nProgram, which TSI makes generally available under this agreement.\n\n2.2. \"Documentation\" shall mean the contents of the website\ndescribing the functionality and use of the Software Program, located\nat http://wrapper.tanukisoftware.org\n\n2.3. \"Product\" shall mean the computer programs, that are provided by\nLicensee to Licensee customers or potential customers, and that\ncontain both the Software Program as a component of the Product, and a\ncomponent or components (other than the Software Program) that provide\nthe material functionality of the Product.  If the Product is released\nin source form, the Software Program or any of its components may only\nbe included in executable form.\n\n2.4. \"Software Program\" shall mean the computer software and license\nfile provided by TSI under this Agreement, including all new releases,\ncorrections, enhancements and updates to such computer software, which\nTSI makes generally available and which Licensee receive pursuant to\nLicensee subscription to TSIMS. Some specific features or platforms\nmay not be enabled if they do not fall under the feature set(s)\ncovered by the specific license fees paid.\n\n2.5 \"End User\" shall mean the customers of the Licensee or any\nrecipient of the Product whether or not any payment is made to use\nthe Product.\n\n\nSection 3 - Licensee Obligations\n\nA copy of this license must be distributed in full with the Product\nin a location that is obvious to any End User.\n\nIn accordance with Section 4, the full source code of all components\nof the Product must be made available to any and all End Users.\n\nLicensee may extend and/or modify the Software Program and distribute\nunder the terms of this agreement provided that the copyright notice\nand license information displayed in the console and log files are\nnot obfuscated or obstructed in any way.\n\n\nSection 4 - GPLv2 License Agreement\n\n                        GNU GENERAL PUBLIC LICENSE\n                           Version 2, June 1991\n\n         Copyright (C) 1989, 1991 Free Software Foundation, Inc.\n       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n\n    Everyone is permitted to copy and distribute verbatim copies of\n    this license document, but changing it is not allowed.\n\n    Preamble\n\n    The licenses for most software are designed to take away your\n    freedom to share and change it. By contrast, the GNU General\n    Public License is intended to guarantee your freedom to share and\n    change free software--to make sure the software is free for all\n    its users. This General Public License applies to most of the Free\n    Software Foundation's software and to any other program whose\n    authors commit to using it.  (Some other Free Software Foundation\n    software is covered by the GNU Library General Public License\n    instead.) You can apply it to your programs, too.\n\n    When we speak of free software, we are referring to freedom, not\n    price. Our General Public Licenses are designed to make sure that\n    you have the freedom to distribute copies of free software (and\n    charge for this service if you wish), that you receive source code\n    or can get it if you want it, that you can change the software or\n    use pieces of it in new free programs; and that you know you can\n    do these things.\n\n    To protect your rights, we need to make restrictions that forbid\n    anyone to deny you these rights or to ask you to surrender the\n    rights. These restrictions translate to certain responsibilities\n    for you if you distribute copies of the software, or if you modify\n    it.\n\n    For example, if you distribute copies of such a program, whether\n    gratis or for a fee, you must give the recipients all the rights\n    that you have. You must make sure that they, too, receive or can\n    get the source code. And you must show them these terms so they\n    know their rights.\n\n    We protect your rights with two steps:\n\n    (1) copyright the software, and\n    (2) offer you this license which gives you legal permission to\n    copy, distribute and/or modify the software.\n\n    Also, for each author's protection and ours, we want to make\n    certain that everyone understands that there is no warranty for\n    this free software. If the software is modified by someone else\n    and passed on, we want its recipients to know that what they have\n    is not the original, so that any problems introduced by others\n    will not reflect on the original authors' reputations.\n\n    Finally, any free program is threatened constantly by software\n    patents. We wish to avoid the danger that redistributors of a free\n    program will individually obtain patent licenses, in effect making\n    the program proprietary. To prevent this, we have made it clear\n    that any patent must be licensed for everyone's free use or not\n    licensed at all.\n\n    The precise terms and conditions for copying, distribution and\n    modification follow.\n    GNU GENERAL PUBLIC LICENSE\n    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n    0. This License applies to any program or other work which\n    contains a notice placed by the copyright holder saying it may be\n    distributed under the terms of this General Public License. The\n    \"Program\", below, refers to any such program or work, and a \"work\n    based on the Program\" means either the Program or any derivative\n    work under copyright law: that is to say, a work containing the\n    Program or a portion of it, either verbatim or with modifications\n    and/or translated into another language. (Hereinafter, translation\n    is included without limitation in the term \"modification\".) Each\n    licensee is addressed as \"you\".\n\n    Activities other than copying, distribution and modification are\n    not covered by this License; they are outside its scope. The act\n    of running the Program is not restricted, and the output from the\n    Program is covered only if its contents constitute a work based on\n    the Program (independent of having been made by running the\n    Program). Whether that is true depends on what the Program does.\n\n    1. You may copy and distribute verbatim copies of the Program's\n    source code as you receive it, in any medium, provided that you\n    conspicuously and appropriately publish on each copy an\n    appropriate copyright notice and disclaimer of warranty; keep\n    intact all the notices that refer to this License and to the\n    absence of any warranty; and give any other recipients of the\n    Program a copy of this License along with the Program.\n\n    You may charge a fee for the physical act of transferring a copy,\n    and you may at your option offer warranty protection in exchange\n    for a fee.\n\n    2. You may modify your copy or copies of the Program or any\n    portion of it, thus forming a work based on the Program, and copy\n    and distribute such modifications or work under the terms of\n    Section 1 above, provided that you also meet all of these\n    conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License. (Exception: if the Program itself is interactive but does\n    not normally print such an announcement, your work based on the\n    Program is not required to print an announcement.)\n\n    These requirements apply to the modified work as a whole. If\n    identifiable sections of that work are not derived from the\n    Program, and can be reasonably considered independent and separate\n    works in themselves, then this License, and its terms, do not\n    apply to those sections when you distribute them as separate works.\n    But when you distribute the same sections as part of a whole which\n    is a work based on the Program, the distribution of the whole must\n    be on the terms of this License, whose permissions for other\n    licensees extend to the entire whole, and thus to each and every\n    part regardless of who wrote it.\n\n    Thus, it is not the intent of this section to claim rights or\n    contest your rights to work written entirely by you; rather, the\n    intent is to exercise the right to control the distribution of\n    derivative or collective works based on the Program.\n\n    In addition, mere aggregation of another work not based on the\n    Program with the Program (or with a work based on the Program) on\n    a volume of a storage or distribution medium does not bring the\n    other work under the scope of this License.\n\n    3. You may copy and distribute the Program (or a work based on it,\n    under Section 2) in object code or executable form under the terms\n    of Sections 1 and 2 above provided that you also do one of the\n    following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software\n    interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code. (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\n    The source code for a work means the preferred form of the work\n    for making modifications to it. For an executable work, complete\n    source code means all the source code for all modules it contains,\n    plus any associated interface definition files, plus the scripts\n    used to control compilation and installation of the executable.\n    However, as a special exception, the source code distributed need\n    not include anything that is normally distributed (in either\n    source or binary form) with the major components (compiler,\n    kernel, and so on) of the operating system on which the executable\n    runs, unless that component itself accompanies the executable.\n\n    If distribution of executable or object code is made by offering\n    access to copy from a designated place, then offering equivalent\n    access to copy the source code from the same place counts as\n    distribution of the source code, even though third parties are not\n    compelled to copy the source along with the object code.\n\n    4. You may not copy, modify, sublicense, or distribute the Program\n    except as expressly provided under this License. Any attempt\n    otherwise to copy, modify, sublicense or distribute the Program is\n    void, and will automatically terminate your rights under this\n    License. However, parties who have received copies, or rights,\n    from you under this License will not have their licenses\n    terminated so long as such parties remain in full compliance.\n\n    5. You are not required to accept this License, since you have not\n    signed it. However, nothing else grants you permission to modify\n    or distribute the Program or its derivative works. These actions\n    are prohibited by law if you do not accept this License.\n    Therefore, by modifying or distributing the Program (or any work\n    based on the Program), you indicate your acceptance of this\n    License to do so, and all its terms and conditions for copying,\n    distributing or modifying the Program or works based on it.\n\n    6. Each time you redistribute the Program (or any work based on\n    the Program), the recipient automatically receives a license from\n    the original licensor to copy, distribute or modify the Program\n    subject to these terms and conditions. You may not impose any\n    further restrictions on the recipients' exercise of the rights\n    granted herein. You are not responsible for enforcing compliance\n    by third parties to this License.\n\n    7. If, as a consequence of a court judgment or allegation of\n    patent infringement or for any other reason (not limited to\n    patent issues), conditions are imposed on you (whether by court\n    order, agreement or otherwise) that contradict the conditions of\n    this License, they do not excuse you from the conditions of this\n    License. If you cannot distribute so as to satisfy simultaneously\n    your obligations under this License and any other pertinent\n    obligations, then as a consequence you may not distribute the\n    Program at all. For example, if a patent license would not permit\n    royalty-free redistribution of the Program by all those who\n    receive copies directly or indirectly through you, then the only\n    way you could satisfy both it and this License would be to refrain\n    entirely from distribution of the Program.\n\n    If any portion of this section is held invalid or unenforceable\n    under any particular circumstance, the balance of the section is\n    intended to apply and the section as a whole is intended to apply\n    in other circumstances.\n\n    It is not the purpose of this section to induce you to infringe\n    any patents or other property right claims or to contest validity\n    of any such claims; this section has the sole purpose of\n    protecting the integrity of the free software distribution system,\n    which is implemented by public license practices. Many people have\n    made generous contributions to the wide range of software\n    distributed through that system in reliance on consistent\n    application of that system; it is up to the author/donor to decide\n    if he or she is willing to distribute software through any other\n    system and a licensee cannot impose that choice.\n\n    This section is intended to make thoroughly clear what is believed\n    to be a consequence of the rest of this License.\n\n    8. If the distribution and/or use of the Program is restricted in\n    certain countries either by patents or by copyrighted interfaces,\n    the original copyright holder who places the Program under this\n    License may add an explicit geographical distribution limitation\n    excluding those countries, so that distribution is permitted only\n    in or among countries not thus excluded. In such case, this\n    License incorporates the limitation as if written in the body of\n    this License.\n\n    9. The Free Software Foundation may publish revised and/or new\n    versions of the General Public License from time to time. Such new\n    versions will be similar in spirit to the present version, but may\n    differ in detail to address new problems or concerns.\n\n    Each version is given a distinguishing version number. If the\n    Program specifies a version number of this License which applies\n    to it and \"any later version\", you have the option of following\n    the terms and conditions either of that version or of any later\n    version published by the Free Software Foundation. If the Program\n    does not specify a version number of this License, you may choose\n    any version ever published by the Free Software Foundation.\n\n    10. If you wish to incorporate parts of the Program into other\n    free programs whose distribution conditions are different, write\n    to the author to ask for permission. For software which is\n    copyrighted by the Free Software Foundation, write to the Free\n    Software Foundation; we sometimes make exceptions for this. Our\n    decision will be guided by the two goals of preserving the free\n    status of all derivatives of our free software and of promoting\n    the sharing and reuse of software generally.\n    \n    NO WARRANTY\n\n    11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO\n    WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE\n    LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS\n    AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\n    OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT\n    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n    FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND\n    PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE\n    DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR\n    OR CORRECTION.\n\n    12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN\n    WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY\n    MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE\n    LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,\n    INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR\n    INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\n    DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU\n    OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY\n    OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN\n    ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\n\n    END OF TERMS AND CONDITIONS\n\n\nSection 4 - 3rd Party Components\n\n(1) The Software Program includes software and documentation components\ndeveloped in part by Silver Egg Technology, Inc.(\"SET\") prior to 2001\nand released under the following license.\n\n    Copyright (c) 2001 Silver Egg Technology\n\n    Permission is hereby granted, free of charge, to any person\n    obtaining a copy of this software and associated documentation\n    files (the \"Software\"), to deal in the Software without\n    restriction, including without limitation the rights to use,\n    copy, modify, merge, publish, distribute, sub-license, and/or\n    sell copies of the Software, and to permit persons to whom the\n    Software is furnished to do so, subject to the following\n    conditions:\n    \n    The above copyright notice and this permission notice shall be\n    included in all copies or substantial portions of the Software.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n    NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n    OTHER DEALINGS IN THE SOFTWARE.\n\n"
  },
  {
    "path": "src/main/resources/win-stuff/wrapper/wrapper.conf",
    "content": "#********************************************************************\r\n# Wrapper Properties\r\n#********************************************************************\r\n# Java Application\r\nwrapper.java.command=java\r\n\r\n# Java Main class.  This class must implement the WrapperListener interface\r\n#  or guarantee that the WrapperManager class is initialized.  Helper\r\n#  classes are provided to do this for you.  See the Integration section\r\n#  of the documentation for details.\r\nwrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp\r\n\r\n# Java Classpath (include wrapper.jar)  Add class path elements as\r\n#  needed starting from 1\r\nwrapper.java.classpath.1=jars/*.jar\r\n\r\n\r\n# Java Library Path (location of Wrapper.DLL or libwrapper.so)\r\nwrapper.java.library.path.1=jars\r\n\r\n# Java Additional Parameters\r\nwrapper.java.additional.1=-Dfile.encoding=UTF-8\r\nwrapper.java.additional.2=-Dsun.jnu.encoding=UTF-8\r\nwrapper.java.additional.3=-Djdbc.drivers=com.mysql.jdbc.Driver;org.postgresql.Driver;org.apache.derby.jdbc.EmbeddedDriver\r\n\r\n# Initial Java Heap Size (in MB) # needs to be changed for production deployment!\r\n#wrapper.java.initmemory=5000\r\n\r\n# Maximum Java Heap Size (in MB) # needs to be changed for production deployment!\r\n#wrapper.java.maxmemory=5000\r\n\r\n# Application parameters.  Add parameters as needed starting from 1\r\nwrapper.app.parameter.1=tigase.server.XMPPServer\r\nwrapper.app.parameter.2=--property-file\r\nwrapper.app.parameter.3=etc/init.properties\r\n\r\n#********************************************************************\r\n# Wrapper Logging Properties\r\n#********************************************************************\r\n# Format of output for the console.  (See docs for formats)\r\nwrapper.console.format=PM\r\n\r\n# Log Level for console output.  (See docs for log levels)\r\nwrapper.console.loglevel=INFO\r\n\r\n# Log file to use for wrapper output logging.\r\nwrapper.logfile=logs/wrapper.log\r\n\r\n# Format of output for the log file.  (See docs for formats)\r\nwrapper.logfile.format=LPTM\r\n\r\n# Log Level for log file output.  (See docs for log levels)\r\nwrapper.logfile.loglevel=INFO\r\n\r\n# Maximum size that the log file will be allowed to grow to before\r\n#  the log is rolled. Size is specified in bytes.  The default value\r\n#  of 0, disables log rolling.  May abbreviate with the 'k' (kb) or\r\n#  'm' (mb) suffix.  For example: 10m = 10 megabytes.\r\nwrapper.logfile.maxsize=0\r\n\r\n# Maximum number of rolled log files which will be allowed before old\r\n#  files are deleted.  The default value of 0 implies no limit.\r\nwrapper.logfile.maxfiles=0\r\n\r\n# Log Level for sys/event log output.  (See docs for log levels)\r\nwrapper.syslog.loglevel=NONE\r\n\r\n#********************************************************************\r\n# Wrapper Windows Properties\r\n#********************************************************************\r\n# Title to use when running as a console\r\nwrapper.console.title=Tigase XMPP server\r\n\r\n#********************************************************************\r\n# Wrapper Windows NT/2000/XP Service Properties\r\n#********************************************************************\r\n# WARNING - Do not modify any of these properties when an application\r\n#  using this configuration file has been installed as a service.\r\n#  Please uninstall the service before modifying this section.  The\r\n#  service can then be reinstalled.\r\n\r\n# Name of the service\r\nwrapper.ntservice.name=Tigase\r\n\r\n# Display name of the service\r\nwrapper.ntservice.displayname=Tigase XMPP server\r\n\r\n# Description of the service\r\nwrapper.ntservice.description=Tigase XMPP server\r\n\r\n# Service dependencies.  Add dependencies as needed starting from 1\r\nwrapper.ntservice.dependency.1=\r\n\r\n# Mode in which the service is installed.  AUTO_START or DEMAND_START\r\nwrapper.ntservice.starttype=AUTO_START\r\n\r\n# Allow the service to interact with the desktop.\r\nwrapper.ntservice.interactive=false\r\n"
  },
  {
    "path": "src/main/restructured/Makefile",
    "content": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line, and also\n# from the environment for the first two.\nSPHINXOPTS    =\nSPHINXBUILD   = sphinx-build\nSOURCEDIR     = .\nBUILDDIR      = _build\n\n# Put it first so that \"make\" without argument is like \"make help\".\nhelp:\n\t@$(SPHINXBUILD) -M help \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n\n.PHONY: help Makefile\n\n# Catch-all target: route all unknown targets to Sphinx using the new\n# \"make mode\" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).\n%: Makefile\n\t@$(SPHINXBUILD) -M $@ \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Components/Advanced_Message_Processing.inc",
    "content": "Advanced Message Processing - AMP XEP-0079\r\n----------------------------------------------\r\n\r\nTigase server offers support for `XEP-0079: Advanced Message Processing <http://xmpp.org/extensions/xep-0079.html>`__ (often abbreviated to AMP).\r\n\r\nIt is enabled by default but there are several configuration options that you may tweak.\r\n\r\nConfiguration of AMP is not very complex, but as it is implemented as a component in the Tigase server it does needs a few settings to get it right.\r\n\r\nHere is a first, brief overview of the AMP configuration and later detailed explanation of each parameter.\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       amp () {\r\n           'amp-jid' = 'amp@your-domain.tld'\r\n       }\r\n       message (active: false) {}\r\n       msgoffline (active: false) {}\r\n   }\r\n   'amp-security-level' = 'STRICT'\r\n\r\nFirst of all: plugins\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nEven though the whole functionality is implemented inside the component you need a way to forward messages with ``AMP`` payload to that component. This is what the ``amp`` plugin does. The ``amp`` plugin intercepts all ``<message/>`` packets even without AMP payload, redirecting some of the to the ``AMP`` component and others processing in a standard way. Therefore you no longer need ``message`` plugin or ``msgoffline`` plugin. Those are all functions are offered by the ``amp`` plugin now. Hence you have to switch ``message`` and ``msgoffline`` plugins off (the ``amp`` plugin is loaded by default):\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       amp () {}\r\n       message (active: false) {}\r\n       msgoffline (active: false) {}\r\n   }\r\n\r\nThe ``amp`` plugin needs to know where to forward all the ``AMP`` packets. By default plugin uses hostname of the given machine as this is true to the most installations. However, this is configured by the last line of the example configuration, which forwards all packets to the address ``amp@your-domain.tld``:\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       amp () {\r\n           'amp-jid' = 'amp@your-domain.tld'\r\n       }\r\n   }\r\n\r\nSecondly: component\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nBy default Tigase loads the component with the standard name ``amp``\r\n\r\nOptional parameters\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThere is also one parameter shared between the component and the plugin. Connection to the database where offline messages are stored. The AMP component has a dedicated schema for storing offline messages designed for a high traffic and high load installations. It does not use ``UserRepository`` for storing messages.\r\n\r\nBy default the same physical database as for ``UserRepository`` is used but you can change it and store messages in a completely separate location to reduce performance degradation of rest of the system. You can set a database connection string using following property:\r\n\r\n.. code::\r\n\r\n   dataSource {\r\n       'default-amp' () {\r\n           uri = 'jdbc:mysql://localhost/tigasedb?user=db_usr&password=db_pwd'\r\n       }\r\n   }\r\n\r\nThe `XEP-0079 <http://xmpp.org/extensions/xep-0079.html>`__ specification has a `Section 9. - Security Considerations <http://xmpp.org/extensions/xep-0079.html#security>`__. As it describes, in some cases the AMP protocol can be used to reveal user’s presence information by other users who are not authorized for presence updates. There are a few possible ways to prevent this.\r\n\r\nTigase’s implementation offers 3 modes to handle ``AMP`` requests to prevent revealing user’s status to non-authorized users:\r\n\r\n.. code::\r\n\r\n   'amp-security-level' = 'STRICT'\r\n\r\nIn this mode the server performs strict checking. The ``AMP`` specification is fully handled. This however involves roster loading for each offline user, hence it may impact the service performance. It may not be feasible or possible to run in this mode for services under a high load with lots of AMP messages.\r\n\r\nIn the XEP this mode is described in the following way:\r\n\r\n*Accept the relevant condition only if the sender is authorized to receive the receiver’s presence, as a result of which the server MUST reply with a <not-acceptable/> error condition if the sender is not so authorized; this is the RECOMMENDED behavior. This is also the default in Tigase.*\r\n\r\n.. code::\r\n\r\n   'amp-security-level' = 'PERFORMANCE'\r\n\r\nDummy checking is performed efficiently by just returning an error response every time there is a chance that the default action may reveal user status without looking into the user’s roster. This does not affect performance but it does impact the ``AMP`` compliance.\r\n\r\nIn the XEP this mode is described in the following way:\r\n\r\n*Accept the relevant condition only if the action is \"drop\", as a result of which the server MUST reply with a <not-acceptable/> error condition if the action is \"alert\", \"error\", or \"notify\"; this is slightly less restrictive but still unnecessarily restricts the functionality of the system, so is NOT RECOMMENDED.*\r\n\r\nIt does not do any checking. It acts like all users are authorized to receive notifications, even if it may reveal user status to unauthorized users. It does not impact the server performance and it offers full AMP compliance.\r\n\r\n.. code::\r\n\r\n   'amp-security-level' = 'NONE'\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Components/C2S.inc",
    "content": "Client to Server Communication\r\n------------------------------------------\r\n\r\nClient to server communication is an integral part of XMPP communication. C2S handles all client communication to the server, and is responsible for filtering and handling remote communications. C2S CAN be disabled, however doing so will only allow communication of internal components, and S2S communications.\r\n\r\nConfiguration\r\n^^^^^^^^^^^^^^^^\r\n\r\nTo disable C2S, use the following line in ``config.tdsl`` folder.\r\n\r\n.. code::\r\n\r\n   c2s (active: false) {}\r\n\r\nOtherwise, C2S component is activated by default.\r\n\r\nConnections\r\n^^^^^^^^^^^^^^^^\r\n\r\nThe connections container houses all configuration related to connections with the component. Each port may be configured individually.\r\n\r\n.. code::\r\n\r\n   c2s {\r\n       connections {\r\n           5222 {\r\n               <configuration>\r\n           }\r\n           5080 {\r\n               <configuration>\r\n           }\r\n       }\r\n   }\r\n\r\nnew-connections-throttling\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nThe property allows you to limit how many new users' connection per second the server accepts on a particular port. Connections established within the limit are processed normally, all others are simply disconnected. This allows you to avoid server overload in case there is a huge number of users trying to connect at the same time. Mostly this happens after a server restart.\r\n\r\n.. code::\r\n\r\n   c2s {\r\n       connections {\r\n           5222 {\r\n               'new-connections-throttling' = 150L\r\n           }\r\n       }\r\n   }\r\n\r\nHere, this limits the number to 150 connections per second before connection attempts are dropped.\r\n\r\nThis replaces the old ``--new-connections-throttling`` property.\r\n\r\nResumption timeout\r\n^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIt is now possible to set a default stream resumption timeout that the server uses. This allows control of how long a server will wait for a reconnection from a client. This can be particularly helpful to manage mobile clients connecting to your server as they may not have complete coverage, and you do not want to close the stream right away. By default, Tigase sets this value to 60 seconds.\r\n\r\n.. code::\r\n\r\n   c2s {\r\n       'urn:xmpp:sm:3' {\r\n           'resumption-timeout' = 90\r\n       }\r\n   }\r\n\r\nThis sets the default timeout to 90 seconds. You may, if you choose, specify a maximum timeout time, which will allow the server to wait between the default and maximum before a connection is closed.\r\n\r\n.. code::\r\n\r\n   c2s {\r\n       'urn:xmpp:sm:3' {\r\n           'max-resumption-timeout' = 900\r\n       }\r\n   }\r\n\r\n.. Note::\r\n\r\n   If the max-resumption-timeout is not set, it will always equal the resumption-timeout number, or default is none is set.\r\n\r\nAvailable since v7.1.0\r\n\r\nIt is also possible to configure max queue size of unacknowledged packages, burst period, and burst period ratio limit. By default max queue size is set to ``2000``, burst period to ``30`` (seconds), and burst ratio to ``20``. That means that for we limit no. of unacked packages to ``2000``, but for a short period of time (``30`` seconds) we allow this queue to be ``20`` times larger, ie. to allow MAM packets burst during MAM sync to complete successfully without hitting queue size limit.\r\n\r\n.. code::\r\n\r\n   c2s {\r\n       'urn:xmpp:sm:3' {\r\n           'max-resumption-queue-size' = 2000\r\n           'max-resumption-queue-size-burst-period' = 30\r\n           'max-resumption-queue-size-burst-ratio' = 20\r\n       }\r\n   }\r\n\r\n.. Note::\r\n\r\n   If you wish to disable burst mechanism, please set burst ratio to ``0``.\r\n\r\nAvailable since v8.5.0\r\n\r\nPacket Redelivery\r\n^^^^^^^^^^^^^^^^^^^^^\r\n\r\nNormally packets are handled by C2S and are typically processed in the first run, however if that fails to send, a retry of sending that packet will occur after 60 seconds. If that second try fails, the delay will increase by a factor of 1.5. This means that the next retry will occur at 90, 135, and so on until the retry count is reached. By default this count is 15, however it can be changed by using the following setting:\r\n\r\n.. code::\r\n\r\n   c2s {\r\n       'packet-deliver-retry-count' = '20'\r\n   }\r\n\r\nThis setting prevents packet redelivery attempts from continuing into infinity (or when the host machine runs out of memory).\r\n\r\nPROXY protocol support\r\n^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nClient to server connections usually are direct TCP connections. However, in some cases it is required to use load balancers to distribute traffic from the Internet facing servers to the XMPP server that may be running in the internal network. If in this process TCP connection is accepted and then a new connection is established from load balancer to the XMPP server, then XMPP server is not aware of the XMPP client real IP address. This missing information makes it difficult to protect the server from DDOS attacks or to limit registration from the same IP addresses.\r\nTo overcome this issue, we've added PROXY protocol support (v1 & v2) that when enabled will allow load balancer to share client IP address (and some additional connection details) with Tigase XMPP Server.\r\n\r\nTo enable PROXY protocol support by setting ``enableProxy`` to ``true`` on selected port.\r\n\r\n.. code::\r\n   c2s {\r\n       5322 () {\r\n           enableProxy = true\r\n       }\r\n   }\r\n\r\n.. Warning::\r\n\r\n   Do not enable PROXY protocol support on ports that are open for anyone to connect. Only load balancers should be able to connect to those ports as Tigase XMPP Server will trust IP addresses provide using PROXY protocol."
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Components/Components.inc",
    "content": ".. _Components:\r\n\r\nComponents\r\n================\r\n\r\nThe only step is to tell the server what components to load, how to name them and optionally give some extra parameters. To do so open the ``config.tdsl`` file you use in your installation.\r\n\r\nLet’s say you want to just add PubSub for now. All you need to do is add the following to the properties file:\r\n\r\n.. code::\r\n\r\n   pubsub (class: tigase.pubsub.PubSubComponent) {}\r\n\r\nNormally, this is not necessary since pubsub is loaded by default, however this is just an example of loading a class with the DSL format.\r\n\r\n.. code::\r\n\r\n   'pubsub-priv' (class: tigase.pubsub.PubSubComponent) {}\r\n\r\nAs you can see, we can customize the name of a component in the deceleration, here we are using pubsub-priv.\r\n\r\nAlthough this may be rare, it allows for wide compatibility and platform stability.\r\n\r\nNormally, however we want to load few different components like PubSub, MUC, MSN Transport and so on…​. Therefore instead of the above second PubSub we can load the MUC component:\r\n\r\n.. code::\r\n\r\n   muc (class: tigase.muc.MUCComponent) {}\r\n   pubsub (class: tigase.pubsub.PubSubComponent) {}\r\n\r\nChanges to the ``config.tdsl`` file will take effect upon server restart.\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Components/External_Component_Configuration/Configuration.inc",
    "content": ".. _External-Component-Configuration:\r\n\r\nExternal Component Configuration\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nAs for all Tigase components you can load and configure external components via the ``config.tdsl`` file described in details in the :ref:`DSL configuration<dslConfig>` section. This document describes how to enable the component and set the initial configuration to accept or initiate connections for an external component.\r\n\r\nFirst thing to do is to specify the component class and the component name which must be unique within the Tigase installation. The most commonly name used is ``ext`` and the class is ``tigase.server.ext.ComponentProtocol`` (class doesn’t have to be specified when using default name).\r\n\r\nThe following line in the ``config.tdsl`` will load the component during the server startup time:\r\n\r\n.. code::\r\n\r\n   ext (class: tigase.server.ext.ComponentProtocol) {}\r\n\r\nWhile this would load the component, without any additional configurations provided, the component would be practically useless. It is necessary to configure the virtual host domains of the external component during run-time via ad-hoc commands to make use of this component.\r\n\r\nYou may additionally configure the :ref:`bind-ext-hostnames<bindExtHostnames>` property.\r\n\r\nTo configure external component connections using Admin UI you need to open Admin UI web page (if you are logged in the same computer on which Tigase XMPP Server is running by default it should be available at http://localhost:8080/admin/). Then you should click on ``Configuration`` on the left side of the Admin UI web page and then select ``Add new item`` on ``ext`` component or by execution corresponding ad-hoc command on ``ext`` component using ad-hoc capable XMPP client, ie. `Psi <http://psi-im.org>`__.\r\n\r\n|adminui ext add item button|\r\n\r\nYou will be presented with a form which you should fill to configure external component connection details:\r\n\r\n|adminui ext add item form|\r\n\r\n-  *Domain name* - external component domain name (``muc.devel.tigase.org``)\r\n\r\n-  *Domain password* - password for authentication of the external component connection (``muc-pass``)\r\n\r\n-  *Connection type* - ``accept`` to make component wait for connection or ``connect`` force component to connect to the server (``connect``)\r\n\r\n-  *Port number* - port on which component should wait for connection or on which it try to connect (``5270``)\r\n\r\n-  *Remote host* - host to connect to (``devel.tigase.org``) *(may be left blank if component will only accept connections)*\r\n\r\n-  *Protocol* - id of protocol used for establishing connection\r\n\r\n   -  if connection type is ``connect``:\r\n\r\n      -  ``XEP-0114: Jabber Component Protocol (accept)`` - for `XEP-0114: Jabber Component Protocol <https://xmpp.org/extensions/xep-0114.html>`__\r\n\r\n      -  ``XEP-0225: Component Connections`` - for `XEP-0225: Component Connections <https://xmpp.org/extensions/xep-0225.html>`__\r\n\r\n   -  if connection type is ``accept``:\r\n\r\n      -  ``Autodetect`` - for automatic detection of protocol used by incoming connection *(recommended)*\r\n\r\n      -  ``XEP-0114: Jabber Component Protocol (accept)`` - for `XEP-0114: Jabber Component Protocol <https://xmpp.org/extensions/xep-0114.html>`__\r\n\r\n      -  ``XEP-0225: Component Connections`` - for `XEP-0225: Component Connections <https://xmpp.org/extensions/xep-0225.html>`__\r\n\r\nAdditional options may be left with defaults.\r\n\r\nLater on if you would like to modify this values, you can do that using Admin UI by clicking on ``Configuration`` and ``Remove an item`` or ``Update item configuration`` at ``ext`` component or by execution corresponding ad-hoc commands on ``ext`` component using ad-hoc capable XMPP client, ie. `Psi <http://psi-im.org>`__.\r\n\r\n.. |adminui ext add item button| image:: /images/admin/adminui_ext_add_item_button.png\r\n.. |adminui ext add item form| image:: /images/admin/adminui_ext_add_item_form.png\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Components/External_Component_Configuration/Intro.inc",
    "content": "External Component Configuration\r\n-------------------------------------\r\n\r\nTigase can connect to external components, this guide will show you how this can be accomplished.\r\n\r\nConfiguration follows the same standards as all other components. It is also much more powerful as a single Tigase instance can control many TCP/IP ports and many external components on each port and even allows for multiple connections for the same component. It supports both XEP-0114 and XEP-0225 with protocol auto-detection mechanisms. Protocols are pluggable so more protocols can be supported or custom extensions to existing protocols can be added.\r\n\r\nThe implementation also supports a scripting API and new domains with passwords can be added at run-time using ad-hoc commands. New scripts can be loaded to even further control all connected external components.\r\n\r\nPages in this guide describe in details all the administration aspects of setting up and managing external components.\r\n\r\n-  :ref:`External Component Configuration<External-Component-Configuration>`\r\n\r\n-  :ref:`Tigase as an External Component<Tigase-as-an-External-Component>`\r\n\r\n-  :ref:`Load Balancing External Components in Cluster Mode<Load-Balancing-External-Components-in-Cluster-Mode>`"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc",
    "content": ".. _Load-Balancing-External-Components-in-Cluster-Mode:\r\n\r\nLoad Balancing External Components in Cluster Mode\r\n------------------------------------------------------\r\n\r\nThis document describes how to load balance any external components using Tigase XMPP Server and how to make Tigase’s components work as external components in a cluster mode.\r\n\r\n*Please note, all configuration options described here apply to Tigase XMPP Server version 8.0.0 or later.*\r\n\r\nThese are actually 2 separate topics:\r\n\r\n1. One is to distribute load over many instances of a single component to handle larger traffic, or perhaps for high availability.\r\n\r\n2. The second is to make Tigase’s components work as an external component and make it work in a cluster mode, even if the component itself does not support cluster mode.\r\n\r\nHere are step by step instructions and configuration examples teaching how to achieve both goals.\r\n\r\nLoad Balancing External Component\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThe first, and most simple scenario is to connect multiple instances of an external component to a single Tigase XMPP Server to distribute load.\r\n\r\nThere are at least 2 reasons why this would be an optimal solution: one would be to spread load over more instances/machines and the second is to improve reliability in case one component fails the other one can take over the work.\r\n\r\nSo here is a simple picture showing the use case.\r\n\r\n|ExternalCompClustering002|\r\n\r\nWe have a single machine running Tigase XMPP Server and 2 instances of the MUC component connecting to Tigase.\r\n\r\nOn the server side we will enable ``ComponentProtocol`` component as we need to do to enable external component without clustering support.\r\n\r\nThen using Admin UI we will add a new external component connection settings using ``Add item`` position for ``ext`` component in ``Configuration`` section of the web page just as it is described in `External Component Configuration <#tigaseExternalComponent>`__ section.\r\n\r\n|adminui ext add item form_1|\r\n\r\nThe only change here is that we will specify value for field ``Load balancer class`` and we will use ``ReceiverBareJidLB`` as a value.\r\n\r\nThe configuration for both instances of the MUC component (identical for both of them) can be done in the same way as it is done for a single instance of the MUC component. There is nothing to change here.\r\n\r\nThe difference is one small element in the server configuration. At the value of ``Load balancer class`` field in ``Add item`` form is set to **ReceiverBareJidLB**.\r\n\r\nThis is the load balancing plugin class. Load balancing plugin decides how the traffic is distributed among different component connections that is different component instances. For the MUC component it makes sense to distribute the traffic based on the receiver bare JID because this is the MUC room address. This way we just distribute MUC rooms and traffic over different MUC component instances.\r\n\r\nThis distribution strategy does not always work for all possible components however. For transports for example this would not work at all. A better way to spread load for transports would be based on the source bare JID. And it is possible if you use plugin with class name: **SenderBareJidLB**.\r\n\r\nThis are two basic load distribution strategies available now. For some use cases none of them is good enough. If you have PubSub, then you probably want to distribute load based on the PubSub node. There is no plugin for that yet but it is easy enough to write one and put the class name in configuration.\r\n\r\nExternal Component and Cluster\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIf you want to use Tigase’s component in a cluster mode which does not have clustering implemented yet there is a way to make it kind of cluster-able. In the previous section we connected many MUC components to a single Tigase server. Now we want to connect a single MUC component to many Tigase servers (or many Tigase cluster nodes).\r\n\r\nLet’s say we have Tigase XMPP Server working for domain: **xmpp-test.org** and the server is installed on three cluster nodes: **red.xmpp-test.org,** **green.xmpp-test.org** and **blue.xmpp-test.org.**\r\n\r\n|ExternalCompClustering003 0|\r\n\r\nWe want to make it possible to connect the MUC component to all nodes. To do so, we are configuring Tigase XMPP Server to run in the cluster mode and on each of cluster nodes we need to enable ``ComponentProtocol`` component.\r\n\r\nThis can be simply done by adding following line to the server configuration file:\r\n\r\n.. code::\r\n\r\n   ext () {}\r\n\r\nAfter this is done we need to add a new external component connection settings using ``Add item`` position for ``ext`` component in ``Configuration`` section of the web page just as it is described in :ref:`External Component Configuration<External-Component-Configuration>` section.\r\n\r\nAs you can see there is nothing special here. The most interesting part comes on the MUC side, but it is only a very small change from the configuration of the component to use with single node Tigase XMPP Server installation.\r\n\r\nWhen you are adding/configuring external component settings using Admin UI (``Add item`` or ``Update item configuration`` for ``ext-man`` component) or using separate configuration file (when you are not using shared database) then you need to pass as a value for ``Remote host`` field a semicolon separated list of all of the cluster nodes to which external component should connect.\r\n\r\nIn our case it would be:\r\n\r\n::\r\n\r\n   red.xmpp-test.org;green.xmpp-test.org;blue.xmpp-test.org\r\n\r\nAs you can see remote host name is not a simple domain but a character string with a few comma separated parts. The first part is our remote domain and the rest are addresses of the host to connect to. This can be a list of domain names or IP addresses.\r\n\r\nOf course it is possible to connect multiple external component to all cluster nodes, this way the whole installation would be really working in the cluster and also load balanced.\r\n\r\n.. |ExternalCompClustering002| image:: /images/admin/ExternalCompClustering002.png\r\n.. |adminui ext add item form_1| image:: /images/admin/adminui_ext_add_item_form.png\r\n.. |ExternalCompClustering003 0| image:: /images/admin/ExternalCompClustering003_0.png"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc",
    "content": ".. _Tigase-as-an-External-Component:\r\n\r\nTigase as an External Component\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThere are cases when you want to deploy one or more Tigase components separately from the main server, or perhaps you want to run some Tigase components connecting to a different XMPP server, or perhaps you work on a component and you do not want to restart the main server every time you make a change.\r\n\r\nThere is a way to run the Tigase server in *external component mode*. In fact you can run any of Tigase’s components as an external component and connect them to the main XMPP server either via `XEP-0114 <http://xmpp.org/extensions/xep-0114.html>`__ or `XEP-0225 <http://xmpp.org/extensions/xep-0225.html>`__ connection.\r\n\r\nLet’s look at the examples…​\r\n\r\nUsage with shared database (since version 8.0.0)\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nWhen you are using Tigase server 8.0.0 or newer in the \"external component mode\" while using shared default \"user repository\" and you have main server also running Tigase XMPP Server 8.0.0 or newer, then you can benefit from the remote management of the component connections from the main server. To use that, you need to enable external component and external component manager on the main server by adding following line to the config file:\r\n\r\n.. code::\r\n\r\n   'ext' () {}\r\n   'ext-man' () {}\r\n\r\nWith that in place you can use Admin UI or ad-hoc commands available at ``ext-man`` component of the main server to configure connection details of the servers running in the ``component`` mode.\r\n\r\nIn Admin UI you click on ``Configuration`` section and select ``Add new item`` at the ``ext-man`` component, which will present you with a following form to fill in external component connectivity details:\r\n\r\n|adminui extman add item form|\r\n\r\n\r\nA Simple Case - MUC as an External Component\r\n\r\nA few assumptions:\r\n\r\n1. We want to run a MUC component for a domain: ``muc.devel.tigase.org`` and password ``muc-pass``\r\n\r\n2. The main server works at an address: devel.tigase.org and for the same virtual domain\r\n\r\n3. We want to connect to the server using `XEP-0114 <http://xmpp.org/extensions/xep-0114.html>`__ protocol and port ``5270``.\r\n\r\nThere is a special configuration type for this case which simplifies setting needed to run Tigase as an external component:\r\n\r\n.. code::\r\n\r\n   'config-type' = 'component'\r\n\r\nKnowing that we can now create simple configuration file for Tigase XMPP Server:\r\n\r\n.. code::\r\n\r\n   admins = [ 'admin@devel.tigase.org' ]\r\n   'config-type' = 'component'\r\n   debug = [ 'server' ]\r\n   'default-virtual-host' = [ 'devel.tigase.org' ]\r\n   dataSource {\r\n       default () {\r\n           uri = 'master_server_default_database_url'\r\n       }\r\n   }\r\n   userRepository {\r\n       default () {}\r\n   }\r\n   authRepository {\r\n       default () {}\r\n   }\r\n   muc (class: tigase.muc.MUCComponent) {}\r\n   ext () {\r\n   }\r\n\r\nwhere ``master_server_default_database_url`` is the same URL as the one used on the main server for default data source.\r\n\r\nWith that in place we can use ad-hoc commands or Admin UI on the main server to configure Tigase XMPP Server to accept external component connections and to connect from the external component to the master server.\r\n\r\n**Adding external component connection settings to the manager (ext-man) using Admin UI.**\r\n\r\n|adminui extman add item form external muc|\r\n\r\nYou need to pass:\r\n\r\n-  Domain name - external component domain name (``muc.devel.tigase.org``)\r\n\r\n-  Domain password - password for authentication of the external component connection (``muc-pass``)\r\n\r\n-  Connection type - ``accept`` to make component wait for connection or ``connect`` force component to connect to the server (``connect``)\r\n\r\n-  Port number - port on which component should wait for connection or on which it try to connect (``5270``)\r\n\r\n-  Remote host - host to connect to (``devel.tigase.org``)\r\n\r\n-  Protocol - id of protocol used for establishing connection\r\n\r\n   -  ``XEP-0114: Jabber Component Protocol (accept)`` - establish connection using `XEP-0114: Jabber Component Protocol <https://xmpp.org/extensions/xep-0114.html>`__\r\n\r\n   -  ``XEP-0225: Component Connections`` - establish connection using `XEP-0225: Component Connections <https://xmpp.org/extensions/xep-0225.html>`__\r\n\r\nAdditional options may be left with defaults.\r\n\r\nMore Components\r\n\r\nSuppose you want to run more than one component as an external components within one Tigase instance. Let’s add another - PubSub component to the configuration above and see how to set it up.\r\n\r\nThe most straightforward way is just to add another component to the server running in the component mode for the component domain\r\n\r\n.. code::\r\n\r\n   admins = [ 'admin@devel.tigase.org' ]\r\n   'config-type' = 'component'\r\n   debug = [ 'server' ]\r\n   'default-virtual-host' = [ 'devel.tigase.org' ]\r\n   dataSource {\r\n       default () {\r\n           uri = 'jdbc:derby:/tigasedb'\r\n       }\r\n   }\r\n   userRepository {\r\n       default () {}\r\n   }\r\n   authRepository {\r\n       default () {}\r\n   }\r\n   muc (class: tigase.muc.MUCComponent) {}\r\n   pubsub (class: tigase.pubsub.PubSubComponent) {}\r\n   ext () {}\r\n\r\nand then to add new connection domain to the main server external component settings and to the external component manager settings. You basically do the same thing as you did while adding only MUC component as the external component.\r\n\r\nPlease note however that we are opening two connections to the same server. This can waste resources and over-complicate the system. For example, what if we want to run even more components? Opening a separate connection for each component is a tad overkill.\r\n\r\nIn fact there is a way to reuse the same connection for all component domains running as an external component. The property ``bind-ext-hostnames`` contains a comma separated list of all hostnames (external domains) which should reuse the existing connection.\r\n\r\nThere is one catch however. Since you are reusing connections (hostname binding is defined in `XEP-0225 <http://xmpp.org/extensions/xep-0225.html>`__ only), you must use this protocol for the functionality.\r\n\r\nHere is an example configuration with a single connection over the `XEP-0225 <http://xmpp.org/extensions/xep-0225.html>`__ protocol used by both external domains:\r\n\r\n.. code::\r\n\r\n   admins = [ 'admin@devel.tigase.org' ]\r\n   'bind-ext-hostnames' = [ 'pubsub.devel.tigase.org' ]\r\n   'config-type' = 'component'\r\n   debug = [ 'server' ]\r\n   'default-virtual-host' = [ 'devel.tigase.org' ]\r\n   dataSource {\r\n       default () {\r\n           uri = 'jdbc:derby:/tigasedb'\r\n       }\r\n   }\r\n   ext () {\r\n   }\r\n   userRepository {\r\n       default () {}\r\n   }\r\n   authRepository {\r\n       default () {}\r\n   }\r\n   muc (class: tigase.muc.MUCComponent) {}\r\n   pubsub (class: tigase.pubsub.PubSubComponent) {}\r\n\r\nWith this configuration you do not need to configure entries in ``ext-man`` for PubSub component, only for MUC component but you need to user ``client`` as the value for protocol field.\r\n\r\nUsage with a separate database\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nA Simple Case - MUC as an External Component\r\n\r\nA few assumptions:\r\n\r\n1. We want to run a MUC component for a domain: ``muc.devel.tigase.org`` and password ``muc-pass``\r\n\r\n2. The main server works at an address: devel.tigase.org and for the same virtual domain\r\n\r\n3. We want to connect to the server using `XEP-0114 <http://xmpp.org/extensions/xep-0114.html>`__ protocol and port ``5270``.\r\n\r\nThere is a special configuration type for this case which simplifies setting needed to run Tigase as an external component:\r\n\r\n.. code::\r\n\r\n   'config-type' = 'component'\r\n\r\nThis generates a configuration for Tigase with only one component loaded by default - the component used for external component connection. If you use this configuration type, your ``config.tdsl`` file may look like this:\r\n\r\n.. code::\r\n\r\n   admins = [ 'admin@devel.tigase.org' ]\r\n   'config-type' = 'component'\r\n   debug = [ 'server' ]\r\n   'default-virtual-host' = [ 'devel.tigase.org' ]\r\n   dataSource {\r\n       default () {\r\n           uri = 'jdbc:derby:/tigasedb'\r\n       }\r\n   }\r\n   userRepository {\r\n       default () {}\r\n   }\r\n   authRepository {\r\n       default () {}\r\n   }\r\n   muc (class: tigase.muc.MUCComponent) {}\r\n   ext () {\r\n   }\r\n\r\nTo make this new instance connect to the Tigase XMPP Server, you need to create one more file with external connection configuration at ``etc/externalComponentItems`` which will be loaded to the local database and then removed.\r\n\r\n.. code:: text\r\n\r\n   muc.devel.tigase.org:muc-pass:connect:5270:devel.tigase.org:accept\r\n\r\n.. Warning::\r\n\r\n    While loading configuration from ``etc/externalComponentItems`` file is supported, we recommend usage of shared database if possible. In future this method may be deprecated.\r\n\r\n\r\nMore Components\r\n\r\nSuppose you want to run more than one component as an external components within one Tigase instance. Let’s add another - PubSub component to the configuration above and see how to set it up.\r\n\r\nThe most straightforward way is just to add another external component connection to the main server for the component domain using Admin UI or ad-hoc command on the main server.\r\n\r\nThen we can use following configuration on the server running in the ``component`` mode:\r\n\r\n.. code::\r\n\r\n   admins = [ 'admin@devel.tigase.org' ]\r\n   'config-type' = 'component'\r\n   debug = [ 'server' ]\r\n   'default-virtual-host' = [ 'devel.tigase.org' ]\r\n   dataSource {\r\n       default () {\r\n           uri = 'jdbc:derby:/tigasedb'\r\n       }\r\n   }\r\n   userRepository {\r\n       default () {}\r\n   }\r\n   authRepository {\r\n       default () {}\r\n   }\r\n   muc (class: tigase.muc.MUCComponent) {}\r\n   pubsub (class: tigase.pubsub.PubSubComponent) {}\r\n   ext () {\r\n   }\r\n\r\nand we need to create a file with configuration for external component connection which will be loaded to the internal database:\r\n\r\n.. code:: text\r\n\r\n   muc.devel.tigase.org:muc-pass:connect:5270:devel.tigase.org:accept\r\n   pubsub.devel.tigase.org:pubsub-pass:connect:5270:devel.tigase.org:accept\r\n\r\nPlease note however that we are opening two connections to the same server. This can waste resources and over-complicate the system. For example, what if we want to run even more components? Opening a separate connection for each component is a tad overkill.\r\n\r\nIn fact there is a way to reuse the same connection for all component domains running as an external component. The property ``bind-ext-hostnames`` contains a comma separated list of all hostnames (external domains) which should reuse the existing connection.\r\n\r\nThere is one catch however. Since you are reusing connections (hostname binding is defined in `XEP-0225 <http://xmpp.org/extensions/xep-0225.html>`__ only), you must use this protocol for the functionality.\r\n\r\nHere is an example configuration with a single connection over the `XEP-0225 <http://xmpp.org/extensions/xep-0225.html>`__ protocol used by both external domains:\r\n\r\n.. code::\r\n\r\n   admins = [ 'admin@devel.tigase.org' ]\r\n   'bind-ext-hostnames' = [ 'pubsub.devel.tigase.org' ]\r\n   'config-type' = 'component'\r\n   debug = [ 'server' ]\r\n   'default-virtual-host' = [ 'devel.tigase.org' ]\r\n   dataSource {\r\n       default () {\r\n           uri = 'jdbc:derby:/tigasedb'\r\n       }\r\n   }\r\n   ext () {\r\n   }\r\n   userRepository {\r\n       default () {}\r\n   }\r\n   authRepository {\r\n       default () {}\r\n   }\r\n   muc (class: tigase.muc.MUCComponent) {}\r\n   pubsub (class: tigase.pubsub.PubSubComponent) {}\r\n\r\nand example of the external connections configuration file:\r\n\r\n.. code:: text\r\n\r\n   muc.devel.tigase.org:muc-pass:connect:5270:devel.tigase.org:client\r\n\r\n.. |adminui extman add item form| image:: /images/admin/adminui_extman_add_item_form.png\r\n.. |adminui extman add item form external muc| image:: /images/admin/adminui_extman_add_item_form_external_muc.png"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Components/External_Service_Discovery_Component.inc",
    "content": "Tigase External Service Discovery\r\n-------------------------------------\r\n\r\nWelcome to the Tigase External Service Discovery component user guide. Component provides support for `XEP-0215: External Service Discovery <http://xmpp.org/extensions/xep-0215.html>`__ which allows discovery of external services which are not accessible using XMPP protocol.\r\n\r\nSetup & Configuration\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nComponent (which is implemented in class ``tigase.server.extdisco.ExternalServiceDiscoveryComponent``) is by default registered under name ``ext-disco`` and disabled. To enable it you need to enable it in configuration. Example:\r\n\r\n-  in DSL format:\r\n\r\n   .. code::\r\n\r\n      ext-disco () { }\r\n\r\nAdditionally you need to activate ``urn:xmpp:extdisco:2`` XMPP processor in ``SessionManager`` by:\r\n\r\n-  in DSL - enable subbean of ``sess-man``:\r\n\r\n   .. code::\r\n\r\n      sess-man {\r\n          'urn:xmpp:extdisco:2'() {}\r\n      }\r\n\r\nList of external services returned by server is configurable using ad-hoc commands provided for this component. AdHoc commands are accessible only for server administrator using XMPP client with support for AdHoc commands or using Tigase Admin UI. Usage of AdHoc commands provides easiest and flexible way to add, modify or remove entries for services which will be returned by discovery.\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Components/Load_Balancing.inc",
    "content": ".. _loadBalancing:\r\n\r\nTigase Load Balancing\r\n----------------------------\r\n\r\nTigase includes load balancing functionality allowing users to be redirected to the most suitable cluster node. Functionality relies on a see-other-host XMPP stream error message. The basic principle behind the mechanism is that user will get redirect if the host returned by the implementation differ from the host to which user currently tries to connect. It is required that the user JID to be known for the redirection to work correctly.\r\n\r\nAvailable Implementations\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTigase implementation is, as usual, extensible and allows for different, pluggable redirection strategies that implement the ``SeeOtherHostIfc`` interface.\r\n\r\nCurrently there are three strategies available:\r\n\r\n-  ``SeeOtherHost`` - most basic implementation returning either single host configured in ``config.tdsl`` file or name of the current host;\r\n\r\n-  ``SeeOtherHostHashed`` (default) - default implementation for cluster environment of SeeOtherHostIfc returning redirect host based on the hash value of the user’s JID; list of the available nodes from which a selection would be made is by default composed and reflects all connected nodes, alternatively hosts list can be configured in the config.tdsl;\r\n\r\n-  ``SeeOtherHostDB`` - extended implementation of SeeOtherHost using redirect information from database in the form of pairs ``user_id`` and ``node_id`` to which given user should be redirected.\r\n\r\n-  ``SeeOtherHostDualIP`` - matches internal Tigase cluster nodes against the lookup table to provide relevant redirection hostname/IP (by default internal Tigase tig_cluster_nodes table will be used)\r\n\r\nConfiguration Options\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThe most basic configuration is related to the choice of actual redirection implementation by declaring class for each connector:\r\n\r\n.. code::\r\n\r\n   bosh {\r\n       seeOtherHost (class: <value>) {}\r\n   }\r\n   c2s {\r\n       seeOtherHost (class: <value>) {}\r\n   }\r\n   ws2s {\r\n       seeOtherHost (class: <value>) {}\r\n   }\r\n\r\nPossible values are:\r\n\r\n-  ``tigase.server.xmppclient.SeeOtherHost``\r\n\r\n-  ``tigase.server.xmppclient.SeeOtherHostHashed``\r\n\r\n-  ``tigase.server.xmppclient.SeeOtherHostDB``\r\n\r\n-  ``tigase.server.xmppclient.SeeOtherHostDualIP``\r\n\r\n-  ``none`` - disables redirection\r\n\r\nAll options are configured on a per-connection-manager basis, thus all options need to be prefixed with the corresponding connection manager ID, i.e. c2s, bosh or ws; we will use c2s in the examples:\r\n\r\n.. code::\r\n\r\n   c2s {\r\n       'cm-see-other-host' {\r\n           'default-host' = 'host1;host2;host3'\r\n           'phases' = [ 'OPEN', 'LOGIN' ]\r\n       }\r\n   }\r\n\r\n-  ``'default-host' = 'host1;host2;host3'`` - a semicolon separated list of hosts to be used for redirection.\r\n\r\n-  ``'phases' = []`` - an array of phases in which redirection should be active, currently possible values are:\r\n\r\n   -  ``OPEN`` which enables redirection during opening of the XMPP stream;\r\n\r\n   -  ``LOGIN`` which enables redirection upon authenticating user session;\r\n\r\nBy default redirection is currently enabled only in the ``OPEN`` phase.\r\n\r\nSeeOtherHostDB\r\n~~~~~~~~~~~~~~~~~~~\r\n\r\nFor ``SeeOtherHostDB`` implementation there are additional options:\r\n\r\n.. code::\r\n\r\n   c2s {\r\n       'cm-see-other-host' {\r\n           'db-url' = 'jdbc:mysqk://localhost/username?,password?'\r\n           'get-all-query-timeout' = '10'\r\n       }\r\n   }\r\n\r\n-  ``db-url`` - a JDBC connection URI which should be used to query redirect information; if not configured the default ``dataSource`` will be used;\r\n\r\n-  ``get-host-query`` - a SQL query which should return redirection hostname;\r\n\r\n-  ``get-all-data-query`` - a SQL helper query which should return all redirection data from database;\r\n\r\n-  ``get-all-query-timeout`` - allows to set timeout for executed queries.\r\n\r\nSeeOtherHostDualIP\r\n~~~~~~~~~~~~~~~~~~~\r\n\r\nThis mechanisms matches internal Tigase cluster nodes against the lookup table to provide matching and relevant redirection hostname/IP. By default internal Tigase ``tig_cluster_nodes`` table is used (and appropriate repository implementation will be used).\r\n\r\nTo enable this redirection mechanism following configuration / class should be used. Note that for global use, all connection managers must have the same class defined. You can define each connection manager individually.\r\n\r\n.. code::\r\n\r\n   bosh {\r\n       seeOtherHost (class: tigase.server.xmppclient.SeeOtherHostDualIP) {}\r\n   }\r\n   c2s {\r\n       seeOtherHost (class: tigase.server.xmppclient.SeeOtherHostDualIP) {}\r\n   }\r\n   ws2s {\r\n       seeOtherHost (class: tigase.server.xmppclient.SeeOtherHostDualIP) {}\r\n   }\r\n\r\nIt offers following configuration options:\r\n\r\n-  ``data-source`` - configuration of the source of redirection information - by default internal Tigase ``tig_cluster_nodes`` table will be used (and appropriate repository implementation will be used); alternatively it’s possible to use ``eventbus`` source;\r\n\r\n-  ``db-url`` - a JDBC connection URI which should be used to query redirect information; if not configured ``user-db-uri`` will be used;\r\n\r\n-  ``get-all-data-query`` - a SQL helper query which should return all redirection data from database;\r\n\r\n-  ``get-all-query-timeout`` - allows to set timeout for executed queries;\r\n\r\n-  ``fallback-redirection-host`` - if there is no redirection information present (i.e. secondary hostname is not configured for the particular node) redirection won’t be generated; with this it’s possible to configure fallback redirection address.\r\n\r\nAll options are configured or on per-component basis:\r\n\r\n.. code::\r\n\r\n   <connector> {\r\n       'cm-see-other-host' {\r\n           'data-source' = '<class implementing tigase.server.xmppclient.SeeOtherHostDualIP.DualIPRepository>'\r\n           'db-url' = 'jdbc:<database>://<uri>'\r\n           'fallback-redirection-host' = '<hostname>'\r\n           'get-all-data-query' = 'select * from tig_cluster_nodes'\r\n           'get-all-query-timeout' = 10\r\n       }\r\n   }\r\n\r\nEventBus as a source of information\r\n\r\nIt’s possible to utilize EventBus and internal Tigase events as a source of redirection data. In order to do that ``eventbus-repository-notifications`` needs to be enabled in ClusterConnectionManager:\r\n\r\n.. code::\r\n\r\n   'cl-comp' {\r\n       'eventbus-repository-notifications' = true\r\n   }\r\n\r\n\r\nAuxiliary setup options\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nEnforcing redirection\r\n~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nIt’s possible to enforce redirection of connections on the particular port of connection manager with ``force-redirect-to`` set to Integer with the following general setting option:\r\n\r\n.. code::\r\n\r\n   <connection_manager> {\r\n       connections {\r\n           <listening_port> {\r\n               'force-redirect-to' = <destination_port>\r\n           }\r\n       }\r\n   }\r\n\r\nfor example, enable additional port ``5322`` for ``c2s`` connection manager and enforce all connections to be redirected to port ``5222`` (it will utilize hostname retrieved from ``SeeOtherHost`` implementation and will be only used when such value is returned):\r\n\r\n.. code::\r\n\r\n   c2s {\r\n       connections {\r\n           ports = [ 5222, 5322 ]\r\n           5322 {\r\n               'force-redirect-to' = 5222\r\n               socket = 'plain'\r\n               type = 'accept'\r\n           }\r\n       }\r\n   }\r\n\r\n\r\nConfiguring hostnames\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nTo fully utilize ``SeeOtherHostDualIP`` setup in automated fashion it’s now possible to provide both primary (*internal*) and secondary (*external*) hostname/IP (they need to be correct, ``InetAddress.getByName( property );`` will be used to verify correctness). It can be done via JVM properties ``tigase-primary-address`` and ``tigase-secondary-address``. You can also utilize different implementation of DNS resolver by providing class implementing ``tigase.util.DNSResolverIfc`` interface as value to ``resolver-class`` property. Those properties can be set via ``etc/tigase.conf`` (uncommenting following lines, or manually exposing in environment):\r\n\r\n.. code:: bash\r\n\r\n   DNS_RESOLVER=\" -Dresolver-class=tigase.util.DNSResolverDefault \"\r\n\r\n   INTERNAL_IP=\" -Dtigase-primary-address=hostname.local \"\r\n   EXTERNAL_IP=\" -Dtigase-secondary-address=hostname \"\r\n\r\nor in the ``etc/config.tdsl`` (they will be converted to JVM properties):\r\n\r\n.. code::\r\n\r\n   'dns-resolver' {\r\n       'tigase-resolver-class' = 'tigase.util.DNSResolverDefault'\r\n       'tigase-primary-address' = 'hostname.local'\r\n       'tigase-secondary-address' = 'hostname'\r\n   }"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Components/Monitoring/MonitorComponent.inc",
    "content": ".. _Monitor-Component:\r\n\r\nMonitor Component\r\n^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTigase includes an **Monitor Component** to help with monitoring has been implemented. This allows you to set thresholds for certain predefined tasks and you or other JIDs can be sent a message when those thresholds are passed. You can even configure a mailer extension to have an E-mail sent to system administrators to let them know an event has occurred! Lets begin with setup and requirements.\r\n\r\nMonitor Component is based on eventbus which in turn is based on a limited `PubSub <http://www.xmpp.org/extensions/xep-0060.html>`__ specification. Events are delivered to subscribers as a normal PubSub notification.\r\n\r\nEach component or client may subscribe for specific types of events. Only components on cluster nodes are allowed to publish events.\r\n\r\nSetup\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nMonitor Component is enabled by default on v7.1.0 b4001 and later, so no setup needed!\r\n\r\nHow it Works\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nEvents in Eventbus are identified by two elements: name of event and its namespace:\r\n\r\n.. code:: xml\r\n\r\n   <EventName xmlns=\"tigase:demo\">\r\n     <sample_value>1</sample_value>\r\n   </EventName>\r\n\r\nWhere event name is ``EventName`` and namespace is ``tigase:demo``.\r\n\r\nListeners may subscribe for a specific event or for all events with specific a namespace. Because in pubsub, only one node name exists, so we have to add a way to convert the event name and namespace to a node name:\r\n\r\n::\r\n\r\n   nodename = eventname + \"|\" + namespace\r\n\r\nSo for example, to subscribe to ``<EventName xmlns=\"tigase:demo\">``, node must be: ``EventName|tigase:demo``. If you wish to subscribe to all events with a specific namespace, use an asterisk (``*``) instead of the event name: ``*|tigase:demo``.\r\n\r\n.. **Note**::  \r\n\r\n   If client is subscribed to ``*|tigase:demo node``, then events will not be sent from node ``*|tigase:demo``, but from the **real** node (in this case: ``EventName|tigase:demo``).\r\n\r\n.. _availableTasks:\r\n\r\nAvailable Tasks\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nMonitor Component has several pre-defined tasks that can be monitored and set to trigger. What follows is the list of tasks with the options attributed to each task.\r\n\r\n-  | **disk-task** - Used to check disk usage.\r\n   | Available Options\r\n\r\n   1. ``enabled`` - Enable or disable task, Boolean value.\r\n\r\n   2. ``period`` - Period of running check, Integer value.\r\n\r\n   3. ``threshold`` - Percentage of used space on disk, Float value.\r\n\r\n-  | **cpu-temp-task** - Used to check CPU temperature.\r\n   | Available Options\r\n\r\n   1. ``enabled`` - Enable or disable task, Boolean value.\r\n\r\n   2. ``period`` - Period of running check, Integer value.\r\n\r\n   3. ``cpuTempThreshold`` - Temperature threshold of CPU in °C.\r\n\r\n-  | **load-checker-task** - Used to check system load.\r\n   | Available Options\r\n\r\n   1. ``enabled`` - Enable or disable task, Boolean value.\r\n\r\n   2. ``period`` - Period of running check, Integer value.\r\n\r\n   3. ``averageLoadThreshold`` - Average percent load threshold, Long value.\r\n\r\n-  | **memory-checker-task** - Used to check memory usage.\r\n   | Available Options\r\n\r\n   1. ``enabled`` - Enable or disable task, Boolean value.\r\n\r\n   2. ``period`` - Period of running check, Integer value.\r\n\r\n   3. ``maxHeapMemUsagePercentThreshold`` - Alarm when percent of used Heap memory is larger than, Integer value.\r\n\r\n   4. ``maxNonHeapMemUsagePercentThreshold`` - Alarm when percent of used Non Heap memory is larger than, Integer value.\r\n\r\n-  | **logger-task** - Used to transmit log entries depending on level entered.\r\n\r\n   1. ``enabled`` - Enable or disable task, Boolean value.\r\n\r\n   2. ``levelThreshold`` - Minimal log level that will be the threshold. Possible values are SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, and ALL.\r\n\r\n-  | **connections-task** - Used to check users disconnections.\r\n   | **NOTE: The event will be generated only if both thresholds (amount and percentage) will be fulfilled.**\r\n\r\n   1. ``enabled`` - Enable or disable task, Boolean value.\r\n\r\n   2. ``period`` - Period of running check in ms, Integer value.\r\n\r\n   3. ``thresholdMinimal`` - Minimal amount of disconnected users required to generate alarm.\r\n\r\n   4. ``threshold`` - Minimal percent of disconnected users required to generate alarm.\r\n\r\nConfiguration\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nConfiguration of the monitor can be done one of two ways; either by lines in ``config.tdsl`` file, or by sending XMPP stanzas to the server. You may also send XMPP stanzas VIA HTTP REST. XMPP stanza configurations will override ones in config.tdsl, but they will only last until the server restarts.\r\n\r\nconfig.tdsl\r\n\r\nTasks can be configured in the ``config.tdsl`` file. See :ref:`available tasks<availableTasks>` for the tasks that can be setup.\r\n\r\nTo enable a specific monitor task, use the following line:\r\n\r\n.. code::\r\n\r\n   monitor {\r\n       '$TASKNAME' {\r\n           setting = value\r\n       }\r\n   }\r\n\r\nWhere monitor is the component name for ``MonitorComponent``, and ``$TASKNAME`` is one of the :ref:`available task names<availableTasks>`.\r\n\r\nThis format will be the same for other settings for tasks, and it’s best to group settings under one heading. For example:\r\n\r\n.. code::\r\n\r\n   monitor {\r\n       'connections-task' {\r\n           enabled = true\r\n           period = 1000\r\n       }\r\n   }\r\n\r\nsets the check period to 1000 milliseconds and enables ``connections-task``.\r\n\r\n.. Note::\r\n\r\n   Once triggers have been activated, they will become dormant. Think of these as one-shot settings.\r\n\r\n\r\nSubscription Limitations\r\n\r\nTo define list of JIDs allowed to subscribe for events:\r\n\r\n.. code::\r\n\r\n   eventbus {\r\n       affiliations {\r\n           allowedSubscribers = 'francisco@denmark.lit,bernardo@denmark.lit'\r\n       }\r\n   }\r\n\r\nIf this is not specified, all users can subscribe.\r\n\r\nConfiguration via XMPP\r\n\r\nWe can also configure the eventbus monitor component using XMPP stanzas. This allows us to set and change configurations during server runtime. This is done using a series of ``iq`` stanzas send to the monitor component.\r\n\r\nWe can query each component for its current settings using the following stanza.\r\n\r\n.. code:: xml\r\n\r\n   <iq type=\"set\" to=\"monitor@$DOMAIN/disk-task\" id=\"aad0a\">\r\n       <command xmlns=\"http://jabber.org/protocol/commands\" node=\"x-config\"/>\r\n   </iq>\r\n\r\nThe server will return the component current settings which will make things easier if you wish to edit them. In this case, the server has returned the following to us\r\n\r\n.. code:: xml\r\n\r\n   <iq from=\"monitor@$DOMAIN/disk-task\" type=\"result\" id=\"aad0a\" to=\"alice@coffeebean.local/Psi+\">\r\n       <command xmlns=\"http://jabber.org/protocol/commands\" status=\"executing\" node=\"x-config\"\r\n                sessionid=\"0dad3436-a029-4082-b0e0-04d838c6c0da\">\r\n           <x xmlns=\"jabber:x:data\" type=\"\">\r\n               <title>Task Configuration</title>\r\n               <instructions/>\r\n               <field type=\"boolean\" label=\"Enabled\" var=\"x-task#enabled\">\r\n                   <value>0</value>\r\n               </field>\r\n               <field type=\"text-single\" label=\"Period [ms]\" var=\"x-task#period\">\r\n                   <value>60000</value>\r\n               </field>\r\n               <field type=\"text-single\" label=\"Disk usage ratio threshold\" var=\"threshold\">\r\n                   <value>0.8</value>\r\n               </field>\r\n           </x>\r\n       </command>\r\n   </iq>\r\n\r\nThis tells us that the disk-task setting is not active, has a period of 60000ms, and will trigger when disk usage is over 80%.\r\n\r\nTo send new settings to the monitor component, we can send a similar stanza back to the monitor component.\r\n\r\n.. code:: xml\r\n\r\n   <iq type=\"set\" to=\"monitor@$DOMAIN/disk-task\" id=\"aad1a\">\r\n       <command xmlns=\"http://jabber.org/protocol/commands\" node=\"x-config\"\r\n                sessionid=\"0dad3436-a029-4082-b0e0-04d838c6c0da\">\r\n           <x xmlns=\"jabber:x:data\" type=\"submit\">\r\n               <field type=\"boolean\" var=\"x-task#enabled\">\r\n                   <value>0</value>\r\n               </field>\r\n               <field type=\"text-single\" var=\"x-task#period\">\r\n                   <value>60000</value>\r\n               </field>\r\n               <field type=\"text-single\" var=\"threshold\">\r\n                   <value>0.8</value>\r\n               </field>\r\n           </x>\r\n       </command>\r\n   </iq>\r\n\r\nTo which a successful update will give you an XMPP success stanza to let you know everything is set correctly.\r\n\r\nAlternatively, you can update specific settings by editing a single field without adding anything else. For example, if we just wanted to turn the disk-task on we could send the following stanza:\r\n\r\n.. code:: xml\r\n\r\n   <iq type=\"set\" to=\"monitor@$HOSTNAME/disk-task\" id=\"ab53a\">\r\n       <command xmlns=\"http://jabber.org/protocol/commands\" node=\"x-config\">\r\n           <x xmlns=\"jabber:x:data\" type=\"submit\">\r\n               <field type=\"boolean\" var=\"x-task#enabled\">\r\n                   <value>1</value>\r\n               </field>\r\n           </x>\r\n       </command>\r\n   </iq>\r\n\r\nTo set any other values, do not forget that certain parts may need to be changed, specifically the ``<field type=\"boolean\" var=x-task#enabled\">`` fields:\r\n\r\n-  | Your field type will be defined by the type of variable specified in the :ref:`Available Tasks<availableTasks>` section.\r\n\r\n-  ``var=x task#`` will be followed by the property value taken directly from the ref:`Available Tasks<availableTasks>` section.\r\n\r\nGetting the Message\r\n''''''''''''''''''''\r\n\r\nWithout a place to send messages to, monitor will just trigger and shut down. There are two different methods that monitor can deliver alarm messages and relevant data; XMPP messages and using the mailer extension.\r\n\r\nXMPP notification\r\n\r\n| In order to retrieve notifications, a subscription to the ``eventbus@<VHost>`` user must be made. Keep in mind that subscriptions are not persistent across server restarts, or triggers.\r\n| The monitor schema is very similar to most XMPP subscription requests but with a few tweaks to differentiate it if you wanted to subscribe to a certain task or all of them. Each task is considered a node, and each node has the following pattern: ``eventName|eventXMLNS``. Since each monitoring task has the ``tigase:monitor:event`` event XMLNS, we just need to pick the event name from the list of tasks. So like the above example, our event node for the disk task will be ``disk-task|tigase:monitor:event``. Applied to an XMPP stanza, it will look something like this:\r\n\r\n.. code:: xml\r\n\r\n   <iq type='set'\r\n       to='eventbus@<VHost>'\r\n       id='sub1'>\r\n     <pubsub xmlns='http://jabber.org/protocol/pubsub'>\r\n       <subscribe node='disk-taskEvent|tigase:monitor:event' jid='$USER_JID'/>\r\n     </pubsub>\r\n   </iq>\r\n\r\nDon’t forget to replace ``$USER_JID`` with the bare JID of the user you want to receive those messages. You can even have them sent to a MUC or any component with a JID.\r\n\r\nAvailable events are as follows:\r\n\r\n-  DiskUsageMonitorEvent for ``disk-task``\r\n\r\n-  LoggerMonitorEvent for ``logger-task``\r\n\r\n-  HeapMemoryMonitorEvent for ``memory-checker-task``\r\n\r\n-  LoadAverageMonitorEvent for ``load-checker-task``\r\n\r\n-  CPUTempMonitorEvent for ``cpu-temp-task``\r\n\r\n-  UsersDisconnected for ``connections-task``\r\n\r\nAlternatively, you can also subscribe to all events within the eventbus by using a wildcard \\* in place of the event XMLNS like this example:\r\n\r\n.. code:: xml\r\n\r\n   <iq type='set'\r\n       to='eventbus@<VHost>'\r\n       id='sub1'>\r\n     <pubsub xmlns='http://jabber.org/protocol/pubsub'>\r\n       <subscribe node='*|tigase:monitor:event' jid='$USER_JID'/>\r\n     </pubsub>\r\n   </iq>\r\n\r\nSample notification from Monitor\r\n\r\n.. code:: xml\r\n\r\n   <message from='eventbus.shakespeare.lit' to='francisco@denmark.lit' id='foo'>\r\n     <event xmlns='http://jabber.org/protocol/pubsub#event'>\r\n       <items node='EventName|tigase:demo'>\r\n         <item>\r\n           <EventName xmlns=\"tigase:demo\" eventSource=\"samplecomponent.shakespeare.lit'\" eventTimestamp=\"1444216850\">\r\n             <sample_value>1</sample_value>\r\n           </EventName>\r\n         </item>\r\n       </items>\r\n     </event>\r\n   </message>\r\n\r\nMailer Extension\r\n'''''''''''''''''\r\n\r\n*Tigase Server Monitor Mailer Extension* (TSMME) can send messages from the monitor component to a specified E-mail address so system administrators who are not logged into the XMPP server.\r\n\r\nFor v7.1.0 versions and later, TSMME is already included in your distribution package and no extra installation is needed.\r\n\r\nConfiguration\r\n\r\nTigase Mailer Extension may be configured via the ``config.tdsl`` file in the following manner:\r\n\r\n.. code::\r\n\r\n   monitor {\r\n       'mailer-from-address' = 'sender@<VHost>'\r\n       'mailer-smtp-host' = 'mail.tigase.org'\r\n       'mailer-smtp-password' = '********'\r\n       'mailer-smtp-port' = '587'\r\n       'mailer-smtp-username' = 'sender'\r\n       'mailer-to-addresses' = 'receiver@<VHost>,admin@<VHost>'\r\n   }\r\n\r\nHere is an explanation of those variables.\r\n\r\n-  ``mailer-smtp-host`` - SMTP Server hostname.\r\n\r\n-  ``mailer-smtp-port`` - SMTP Server port.\r\n\r\n-  ``mailer-smtp-usernam`` - name of sender account.\r\n\r\n-  ``mailer-smtp-password`` - password of sender account.\r\n\r\n-  ``mailer-from-address`` - sender email address. It will be set in field from in email.\r\n\r\n-  ``mailer-to-addresses`` - comma separated notification receivers email addresses.\r\n\r\nIt is recommended to create a specific e-mail address in your mail server for this purpose only, as the account settings are stored in plaintext without encryption."
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Components/Monitoring/Monitoring.inc",
    "content": ".. _serverMonitoring:\r\n\r\nServer Monitoring\r\n-----------------------\r\n\r\nAll the documentation and resources related to the Tigase server monitoring.\r\n\r\n-  :ref:`Setting up Remote Monitoring in the Server<Setting-Up-Remote-Monitoring-in-the-Server>`\r\n\r\n-  :ref:`Statistics Logger Configuration<Configuration-of-statistics-loggers>`\r\n\r\n-  :ref:`Retrieving Statistics from the Server<Retrieving-statistics-from-the-server>`\r\n\r\n-  :ref:`Monitor Component<Monitor-Component>`"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc",
    "content": ".. _Retrieving-statistics-from-the-server:\r\n\r\nRetrieving statistics from the server\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nBy default we can retrieve server statistics using XMPP, no additional setup is necessary.\r\n\r\nRetrieving statistics using XMPP\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nAccessing statistics over XMPP protocol requires any XMPP client capable of executing `XEP-0050: Ad-Hoc Commands <http://xmpp.org/extensions/xep-0050.html>`__. It’s essential to remember, that only administrator (a user whose JID is configured as administrative) can access the statistics.\r\n\r\nPsi XMPP Client\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nFor the purpose of this guide `Psi <http://psi-im.org/>`__ client will be used. After successfully configuring and connecting to account with administrative privileges we need to access *Service Discovery*, either from application menu or from context menu of the particular account account:\r\n\r\n|roster-discovery|\r\n\r\nIn the *Service Discovery* window we need to find *Server Statistics* component:\r\n\r\n|discovery-stats|\r\n\r\nWe can either access statistics for all components or select particular component after expanding the tree. To execute ad-hoc command simply double click on the particular node which will open window with statistics:\r\n\r\n|server-stats|\r\n\r\nIn this window, in addition to see the statistics, we can adjust *Stats level* by selecting desired level from the list and confirm by clicking *Finish*.\r\n\r\nRetrieving statistics using JMX\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nIn order to access statistics over JMX we need to enable support for it in Tigase - `Monitoring Activation <#monitoring_activation>`__. Afterwards we can use a number of tools to get to the statistics, for example the following:\r\n\r\nJConsole\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nAfter opening JConsole we either select local process or provide details of the remote process, including IP, port and credentials from **etc/jmx.**\\ \\* files:\r\n\r\n|jconsole|\r\n\r\nAfterwards we navigate to the MBeans tab from where we can access the ``tigase.stats`` MBean. It offers similar options to XMPP - either accessing statistics for all components or only for particular component as well as adjusting level for which we want to obtain statistics:\r\n\r\n|jconsole-1|\r\n\r\n\r\nStatsDumper.groovy\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nIn order to collect statistics over period of time following groovy script can be used: `StatsDumper.groovy <https://raw.githubusercontent.com/tigase/tigase-server/master/src/main/restructured/files/StatsDumper.groovy>`__. It’s a Simple JMX client that connects to Tigase and periodically saves all statistics to files.\r\n\r\nIt takes following parameters:\r\n\r\n.. code:: bash\r\n\r\n   $ groovy StatsDumper.groovy [hostname] [username] [password] [dir] [port] [delay(ms)] [interval(ms)] [loadhistory(bool)]\r\n\r\n-  ``hostname`` - address of the instance\r\n\r\n-  ``username`` - JMX username\r\n\r\n-  ``password`` - JMX username\r\n\r\n-  ``dir`` - directory to which save the files with statistics\r\n\r\n-  ``port`` - port on which to make the connection\r\n\r\n-  ``delay``\\ (ms) - initial delay in milliseconds after which statistics should be saved\r\n\r\n-  ``interval``\\ (ms) - interval between each retrieval/saving of statistics\r\n\r\n-  ``loadhistory``\\ (bool) - indicates whether or not load statistics history from server (if such is enabled in Tigase)\r\n\r\n.. |roster-discovery| image:: /images/admin/monitoring_xmpp_1.png\r\n.. |discovery-stats| image:: /images/admin/monitoring_xmpp_2.png\r\n.. |server-stats| image:: /images/admin/monitoring_xmpp_3.png\r\n.. |jconsole| image:: /images/admin/monitoring_jmx_jconsole_1.png\r\n.. |jconsole-1| image:: /images/admin/monitoring_jmx_jconsole_2.png"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Components/Monitoring/Setting_up.inc",
    "content": ".. _Setting-Up-Remote-Monitoring-in-the-Server:\r\n\r\nSetting Up Remote Monitoring in the Server\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTigase server can be remotely monitored over following protocols: ``JMX/RMI``, ``SNMP`` and ``HTTP``. Even though ``JMX`` offers the biggest control and visibility to the server states, all of the monitoring services give the same basic set of the server statistics:\r\n\r\n-  Number of network connections for s2s, c2s and Bosh\r\n\r\n-  Last second, last minute and last hour load for all main components: SM, MR, c2s, s2s, Bosh, MUC and PubSub\r\n\r\n-  System statistics - memory usage (heap and non heap) and the server uptime in milliseconds and human readable text.\r\n\r\n-  Users statistics - number of registered users and number of online user session.\r\n\r\nJMX/RMI and SNMP servers offer basic security and can restrict access while the HTTP server doesn’t offer any access restriction mechanisms. Therefore HTTP monitoring is recommended to operate behind a firewall.\r\n\r\nThe monitoring itself causes very low overhead in terms of the resources and CPU consumption on top of the normal Tigase processing requirements so it can be left on without worrying about performance degradation.\r\n\r\nNOTE This works with the Tigase server from version **4.2.0** or build **1418**.\r\n\r\nWhat You Need\r\n~~~~~~~~~~~~~~\r\n\r\nStatistics binaries are built-in ``-dist-max`` and no extra files are needed. If you have downloaded ``-dist`` file, you will need tigase-extras[https://github.com/tigase/tigase-extras] built and included in the ``jars/`` directory.\r\n\r\nActivation\r\n~~~~~~~~~~~~~~\r\n\r\nYou can either run the Tigase installer and use the configuration wizard to activate the monitoring or edit etc/config.tdsl file and add following lines:\r\n\r\n.. code::\r\n\r\n   monitoring() {\r\n     jmx() {\r\n       port = 9050\r\n     }\r\n     http() {\r\n       port = 9080\r\n     }\r\n     snmp() {\r\n       port = 9060\r\n     }\r\n   }\r\n\r\nAs you see there is a separate block for each monitoring server you want to activate. Each server is responsible for activation of a different protocol and takes a single parameter - port number. There are following protocols supported right now:\r\n\r\n-  ``jmx`` - activating monitoring via JMX/RMI\r\n\r\n-  ``http`` - activating monitoring over HTTP protocol\r\n\r\n-  ``snmp`` - activating monitoring over SNMP protocol\r\n\r\nYou can have all protocols active at the same time or any combination of them or none.\r\n\r\nSecurity\r\n~~~~~~~~~~~~~~\r\n\r\nBoth JMX and SNMP offer security protection to limit access to monitoring data. The security configuration is a bit different for both.\r\n\r\nJMX\r\n~~~~~~~~~~~~~~\r\n\r\nAfter the server installation or in the SVN repository you can find 2 files in the ``etc/`` directory: ``jmx.access`` and ``jmx.password``.\r\n\r\n-  ``jmx.access`` is a user permission file. You can use it to specify whether the user can access the monitoring data for reading only ``readonly`` or with read-write ``readwrite`` access. There are example entries in the file already and the content may simply look like:\r\n\r\n   .. code:: bash\r\n\r\n      monitor readonly\r\n      admin readwrite\r\n\r\n-  ``jmx.password`` is a user password file. You can set user passwords here and the format again is very simple and the same as for jmx.access. There are example entries already provided for you convenience. Content of the file may look like the example below:\r\n\r\n   .. code:: bash\r\n\r\n      admin admin_pass\r\n      monitor monitor_pass\r\n\r\nUsing above to files you can control who and how can access the JMX monitoring services.\r\n\r\nSNMP\r\n\r\nAccess to SNMP monitoring is controlled using ACL (access control lists) which can be configured in the file ``snmp.acl`` located in ``etc/`` directory. It contains lots of detailed instructions how to setup ACL and restrict access per user, host and what kind access is allowed. The simplest possible configuration may look like this:\r\n\r\n.. code:: bash\r\n\r\n   acl = {\r\n     {\r\n       communities = public, private\r\n       access = read-only\r\n       managers = public.host.com, private.host.com\r\n     }\r\n     {\r\n       communities = admin\r\n       access = read-write\r\n       managers = localhost, admin.host.com\r\n     }\r\n   }\r\n\r\nYou might also need Tigase MIB definition: `TIGASE-MANAGEMENT-MIB.mib <https://github.com/tigase/tigase-server/blob/master/src/main/resources/mib/JVM-MANAGEMENT-MIB.mib>`__ for the server specific statistics. The MIB contains definition for all the server statistics exposed via SNMP.\r\n\r\nHTTP\r\n\r\nAccess the server at example.com:9080 and you will be presented with an Agent View."
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Components/Monitoring/Statistics_Logger_Config.inc",
    "content": ".. _Configuration-of-statistics-loggers:\r\n\r\nConfiguration of statistics loggers\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIt is possible to enable and configure automatic storage of statistics information. To do that you need to configure any of following statistics loggers as a ``StatisticsCollector`` component sub-beans:\r\n\r\n``tigase.stats.CounterDataArchivizer``\r\n   every execution put current basic server metrics (CPU usage, memory usage, number of user connections, uptime) into database (overwrites previous entry).\r\n\r\n``tigase.stats.CounterDataLogger``\r\n   every execution insert new row with new set of number of server statistics (CPU usage, memory usage, number of user connections per connector, number of processed packets of different types, uptime, etc) into the database.\r\n\r\n``tigase.stats.CounterDataFileLogger``\r\n   every execution store all server statistics into separate file.\r\n\r\nAs an example to configure ``tigase.stats.CounterDataFileLogger`` to archive statistics data with level ``FINE`` every 60 seconds to file prefixed with ``stat`` and located in ``logs/server_statistics`` following entry is needed:\r\n\r\n.. code::\r\n\r\n   stats() {\r\n       'stats-file-logger' (class: tigase.stats.CounterDataFileLogger) {\r\n           'stats-directory' = 'logs/server_statistics'\r\n           'stats-filename' = 'stat'\r\n           'stats-unixtime' = false\r\n           'stats-datetime' = true\r\n           'stats-datetime-format' = 'HH:mm:ss'\r\n           'stats-level' = 'FINEST'\r\n       }\r\n   }"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Components/Server_to_Server_Protocol.inc",
    "content": "Server to Server Protocol Settings\r\n------------------------------------\r\n\r\nTigase server-to-server communication component facilitates communication with other XMPP servers (federation) and allows you to tweak it’s configuration to get a better performance in your installation.\r\n\r\nS2S (or server to server) protocol is enabled by default with optimal settings chosen. There are however, a set of configuration parameters you can adjust the server behavior to achieve optimal performance on your installation.\r\n\r\nThis documents describes following elements of the Tigase server configuration:\r\n\r\n1. Number of concurrent connections to external servers\r\n\r\n2. The connection throughput parameters\r\n\r\n3. Maximum waiting time for packets addressed to external servers and the connection inactivity time\r\n\r\n4. Custom plugins selecting connection to the remote server\r\n\r\nNumber of Concurrent Connections\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nNormally only one connection to the remote server is required to send XMPP stanza to that server. In some cases however, under a high load, you can get much better throughput and performance if you open multiple connections to the remote server.\r\n\r\nThis is especially true when the remote server works in a cluster mode. Ideally you want to open a connection to each of the cluster nodes on the remote server. This way you can spread the traffic evenly among cluster nodes and improve the performance for s2s connections.\r\n\r\nTigase server offers 2 different parameters to tweak the number of concurrent, s2s connections:\r\n\r\n-  ``max-out-total-conns`` - this property specifies the maximum outgoing connections the Tigase server opens to any remote XMPP server. This is a **per domain** limit, which means that this limit applies to each of the remote domains Tigase connects to. If it is set to ``4`` then Tigase opens a maximum of 4 connections to ``jabber.org`` plus maximum 4 connections to ``muc.jabber.org`` even if this is the same physical server behind the same IP address.\r\n\r\n   To adjust the limit you have to add following to the ``config.tdsl`` file:\r\n\r\n   .. code::\r\n\r\n      s2s {\r\n          'max-out-total-conns' = 2\r\n      }\r\n\r\n-  ``max-out-per-ip-conns`` - this property specifies the maximum outgoing connections Tigase server opens to any remote XMPP server to its single IP address. This too, is **per domain** limit, which means that this limit applies to each of the remote domains Tigase connects to. If it is set to ``1``, and the above limit is set to ``4``, and the remote server is visible behind 1 IP address, then Tigase opens a maximum of 1 connection to ``jabber.org`` plus a maximum of 1 connection to ``muc.jabber.org`` and other subdomains.\r\n\r\n   To adjust the limit you have to add following line to the ``config.tdsl`` file:\r\n\r\n   .. code::\r\n\r\n      s2s {\r\n          'max-out-per-ip-conns' = 2\r\n      }\r\n\r\n\r\nConnection Throughput\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nOf course everybody wants his server to run with maximum throughput. This comes with a cost on resources, usually increased memory usage. This is especially important if you have large number of s2s connections on your installations. High throughput means lots of memory for network buffers for every single s2s connection. You may soon run out of all available memory.\r\n\r\nThere is one configuration property which allows you to adjust the network buffers for s2s connections to lower your memory usage or increase data throughput for s2s communication.\r\n\r\nMore details about are available in the :ref:`net-buff-high-throughput<netBuffHighThroughput>` or :ref:`net-buff-Standard<netBuffStandard>` property descriptions.\r\n\r\nMaximum Packet Waiting Time and Connection Inactivity Time\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThere are 2 timeouts you can set for the component controlling s2s communication.\r\n\r\n-  ``max-packet-waiting-time`` - this sets the maximum time for the packets waiting for sending to some remote server. Sometimes, due to networking problems or DNS problems it might be impossible to send message to remote server right away. Establishing a new connection may take time or there might be communication problems between servers or perhaps the remote server is restarted. Tigase will try a few times to connect to the remote server before giving up. This parameter specifies how long the packet is waiting for sending before it is returned to the sender with an error. The timeout is specified in seconds:\r\n\r\n   .. code::\r\n\r\n      s2s {\r\n          'max-packet-waiting-time' = 420L\r\n      }\r\n\r\n-  ``max-inactivity-time`` - this parameters specifies the maximum s2s connection inactivity time before it is closed. If a connection is not in use for a long time, it doesn’t make sense to keep it open and tie resources up. Tigase closes s2s connection after specified period of time and reconnects when it is necessary. The timeout is specified in seconds:\r\n\r\n   .. code::\r\n\r\n      s2s {\r\n          'max-inactivity-time' = 900L\r\n      }\r\n\r\nCustom Plugin: Selecting s2s Connection\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nSometimes for very large installations you may want to set larger number of s2s connections to remote servers, especially if they work in cluster of several nodes. In such a case you can also have a control over XMPP packets distribution among s2s connections to a single remote server.\r\n\r\nThis piece of code is pluggable and you can write your own connection selector. It is enough to implement ``S2SConnectionSelector`` interface and set your class name in the configuration using following parameter in ``config.tdsl`` file:\r\n\r\n.. code::\r\n\r\n   s2s {\r\n       's2s-conn-selector' = 'YourSelectorImplementation'\r\n   }\r\n\r\nThe default selector picks connections randomly.\r\n\r\nskip-tls-hostnames\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThe ``s2s-skip-tls-hostnames`` property disables TLS handshaking for s2s connections to selected remote domains. Unfortunately some servers (certain versions of Openfire - [`1 <http://community.igniterealtime.org/thread/36206>`__] or [`2 <http://community.igniterealtime.org/thread/30578>`__]) have problems with TLS handshaking over s2s which prevents establishing a usable connection. This completely blocks any communication to these servers. As a workaround you can disable TLS for these domains to get communication back. Enabling this can be done on any vhost, but must be configured under the s2s component.\r\n\r\n.. code::\r\n\r\n   s2s {\r\n       'skip-tls-hostnames' = [ 'domain1', 'domain2' ]\r\n   }\r\n\r\nejabberd-bug-workaround\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThis property activates a workaround for a bug in EJabberd in it’s s2s implementation. EJabberd does not send dialback in stream features after TLS handshaking even if the dialback is expected/needed. This results in unusable connection as EJabberd does not accept any packets on this connection either. The workaround is enabled by default right now until the EJabberd version without the bug is popular enough. A disadvantage of the workaround is that dialback is always performed even if the SSL certificate is fully trusted and in theory this dialback could be avoided. By default, this is not enabled.\r\n\r\n.. code::\r\n\r\n   s2s {\r\n       dialback () {\r\n           'ejabbered-bug-workaround' = true\r\n           }\r\n   }\r\n\r\nThis replaces the old ``--s2s-ejabberd-bug-workaround-active`` property.\r\n\r\nAvailable since v8.5.0\r\n\r\nPROXY protocol support\r\n^^^^^^^^^^^^^^^^^^^^^^^\r\nPROXY protocol support (v1 & v2) may be enabled in the similar way as for client to server connections, by setting ``enableProxy`` to ``true`` on selected port.\r\n\r\n.. code::\r\n   s2s {\r\n       5369 () {\r\n           enableProxy = true\r\n       }\r\n   }\r\n\r\n.. Warning::\r\n\r\n   Do not enable PROXY protocol support on ports that are open for anyone to connect. Only load balancers should be able to connect to those ports as Tigase XMPP Server will trust IP addresses provide using PROXY protocol."
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Components/_Components.rst",
    "content": ".. include:: Components.inc\r\n.. include:: Advanced_Message_Processing.inc\r\n.. include:: Monitoring/Monitoring.inc\r\n.. include:: Monitoring/Setting_up.inc\r\n.. include:: Monitoring/Retrieving_statistics.inc\r\n.. include:: Monitoring/MonitorComponent.inc\r\n.. include:: Monitoring/Statistics_Logger_Config.inc\r\n.. include:: Server_to_Server_Protocol.inc\r\n.. include:: Load_Balancing.inc\r\n.. include:: External_Component_Configuration/Intro.inc\r\n.. include:: External_Component_Configuration/Configuration.inc\r\n.. include:: External_Component_Configuration/Tigase_as_External.inc\r\n.. include:: External_Component_Configuration/Load_Balancing.inc\r\n.. include:: C2S.inc\r\n.. include:: External_Service_Discovery_Component.inc"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Configuration/Advanced_Options.inc",
    "content": "Tigase Advanced Options\r\n-----------------------------\r\n\r\nThis section is designed to include a number of advanced configuration options available within Tigase, but may not have a relevant section yet to house them.\r\n\r\nUsing CAPTCHA for in-band registration\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTo reduce false or spam registrations to Tigase XMPP Server, there is now the ability to add CAPTCHA forms as a part of the in-band registration. The CAPTCHA will generate a random math equation and ask the user registering a new account to answer it. This may be enabled as a sub-option of enabling registration in config.tdsl:\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       'jabber:iq:register' {\r\n           captchaRequired = 'true'\r\n       }\r\n   }\r\n\r\n.. **Note**::\r\n\r\n   Because some clients do not refresh a registration form after an unsuccessful attempt, this option allows 3 retries with the same CAPTCHA.\r\n\r\n3 unsuccessful attempts will result in the captcha being invalidated and a client will receive an error message.\r\n\r\n\r\nEnabling Empty Nicknames\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTigase can now support users with empty nicknames. This can be enabled by adding the following code in config.tdsl.\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       'jabber:iq:roster' {\r\n           empty_name_enabled = 'true'\r\n       }\r\n   }\r\n\r\n\r\nEnable Silent Ignore on Packets Delivered to Unavailable Resources\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nYou can now have Tigase ignore packets delivered to unavailable resources to avoid having a packet bounce around and create unnecessary traffic. You may set this globally, within standard message handling only, or within the AMP component using the following settings:\r\n\r\nGlobally:\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       'silently-ignore-message' = 'true'\r\n   }\r\n\r\nMessage Processing Only:\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       message {\r\n           'silently-ignore-message' = 'true'\r\n       }\r\n   }\r\n\r\nAMP Component:\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       amp () {\r\n           'silently-ignore-message' = 'true'\r\n   }\r\n\r\nMechanism to count errors within Tigase\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nA new processor within statistics has been added to count the number of errors that Tigase returns. This processor, named ``error-counter``, will count all errors returned by Tigase, however by default the number is always zero if it is not enabled. It can be found as an MBean object in JMX under ``ErrorStatistics`` and contains values for packets with ``ERROR`` and grouped by type. To enable counting of these errors, you must ensure the processor is included in your ``sess-man`` configuration:\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       'error-counter' () {}\r\n   }\r\n\r\nIncluding stream errors\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nStream ``ERROR`` packets are not included in the above counter by default as they are processed separately. To enable this to be added to the counter, the following line must be in your ``config.tdsl`` file.\r\n\r\n.. code::\r\n\r\n   c2s {\r\n       'stream-error-counter' () {\r\n           active = true\r\n       }\r\n   }\r\n\r\nStream resumption default & max-timeout\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\n``SteamManagementIOProcessor`` now has a setting that can be used to change the maximum timeout time it will wait for reconnection if a client does not send a time to wait. Two settings are now available:\r\n\r\n.. code::\r\n\r\n   c2s {\r\n       'urn:xmpp:sm:3' {\r\n           'resumption-timeout' = 90\r\n       }\r\n   }\r\n\r\nThe above setting in ``config.tdsl`` file will change the default timeout period to 90 seconds.\r\n\r\n.. code::\r\n\r\n   c2s {\r\n       'urn:xmpp:sm:3' {\r\n           'max-resumption-timeout' = 900\r\n       }\r\n   }\r\n\r\nThis setting will set the maximum time allowed for stream resumption to 900 seconds. This can be handy if you expect a number of mobile phones to connect to your server and want to avoid duplicate messages being sent back and forth.\r\n\r\nAutomatic subscription approval\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nYou may setup a server to automatically approve presence subscriptions or roster authorizations for all users. Say you were hosting bots and wanted to automate the process. This can be done with the following settings:\r\n\r\n.. code::\r\n\r\n   'sess-man' () {\r\n       'jabber:iq:roster' {\r\n           'auto-authorize' = 'true'\r\n       }\r\n       'presence-subscription' () {\r\n           'auto-authorize' = 'true'\r\n       }\r\n   }\r\n\r\nBoth of these settings are false by default, and you may use them together or separately.\r\n\r\n.. **Note**::\r\n\r\n   ``presence-subscription`` is current default plugin. If you are using old ``presence`` then you should configure the option with correct plugin name.\r\n\r\nThe following behavior is followed when they are both activated:\r\n\r\n-  Upon sending a subscription request - Both contacts will each others' subscription and be added to each others' roster. Presence information will immediately be exchanged between both parties.\r\n\r\n-  Upon sending presence with type either unsubscribe or unsubscribed follows the rules defined in RFC regarding processing of these stanzas (i.e. adjusting subscription type of user/contact), but without forwarding those stanzas to the receiving entity to avoid any notifications to the client. However, a roster push is generated to reflect changes to presence in user roster in a seamless manner.\r\n\r\n-  Simply adding an item to the roster (i.e. with <iq/> stanza with correct semantics) will also cause an automatic subscription between the user and the contact in a matter explained above.\r\n\r\nAbuse Contacts\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nTigase has support for `XEP-0128: Service Discovery Extensions <https://xmpp.org/extensions/xep-0128.html>`__ for providing additional information to the server and component discovery information. One of the important usages for this feature is `XEP-0157: Contact Addresses for XMPP Services <https://xmpp.org/extensions/xep-0157.html>`__ which describes usage of this feature for providing contact information to server administrators or abuse response team.\r\n\r\nAll supported fields: ``abuse-addresses``, ``admin-addresses``, ``feedback-addresses``, ``sales-addresses``, ``security-addresses``, ``support-addresses``.\r\n\r\nTo set abuse contact details you should set ``disco-extensions`` in property in ``etc/config.tdsl`` file with subproperty ``abuse-addresses`` set to your abuse address URI (for email you need to prefix it with ``mailto:`` and for XMPP address you need to prefix it with ``xmpp``):\r\n\r\n.. code::\r\n\r\n   'disco-extensions' = {\r\n       'abuse-addresses' = [ 'mailto:abuse@localhost', 'xmpp:abuse@localhost' ]\r\n   }\r\n\r\nPush Notifications\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nTigase XMPP Server comes with support for `XEP-0357: Push Notifications <https://xmpp.org/extensions/xep-0357.html>`__ allowing user to receive notifications for messages received while his XMPP client is not connected enabled by default.\r\n\r\n\r\nDisabling notifications\r\n\r\nYou can disable this feature with following settings:\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       'urn:xmpp:push:0' (active: false) {}\r\n   }\r\n\r\nDisabling notifications after offline messages are retrieved\r\n\r\nYou can disabled send push notification after offline messages are retrieved from the server by user's XMPP client:\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       'urn:xmpp:push:0' () {\r\n           'send-offline-messages-retrieved-notification' = false\r\n       }\r\n   }\r\n\r\nRemoving body and sender from notifications\r\n\r\nIf you wish Tigase XMPP Server not to forward body of the message or sender details in the push notification you can disable that with following settings:\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       'urn:xmpp:push:0' () {\r\n           'with-body' = false\r\n           'with-sender' = false\r\n       }\r\n   }\r\n\r\nOverriding body of notifications\r\n\r\nIf you wish Tigase XMPP Server to override forward body of the encrypted message in the push notification (for example to avoid indicating that there is an \"error\") you can do that with following settings:\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       'urn:xmpp:push:0' () {\r\n           'encryptedMessageBody' = \"You have a new secure message. Open to see the message\"\r\n       }\r\n   }\r\n\r\nEnabling TTL (time-to-live) for enabled push devices\r\n\r\nBy default, each device enabled for push notifications is kept in the servers storage until it is not disabled (or disabled due to errors reported by push component. To make sure that older push devices that are no longer in use and do not return errors are removed from the list of push enabled devices, it is now possible to configure TTL for enabled push devices. After TTL, subsequent notification will remove devices that reached TTL. Value is in `Java Period format <https://docs.oracle.com/javase/8/docs/api/java/time/Period.html#parse-java.lang.CharSequence-:>`__\r\n\r\nExample of setting TTL to 2 days.\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       'urn:xmpp:push:0' () {\r\n           'deviceRegistrationTTL' = 'P2D'\r\n       }\r\n   }\r\n\r\nEnabling push notifications for account removal\r\n\r\nIn some cases, you want your applications to be notified, if your XMPP account was removed from the sever (ie. due to account removal using another application). To receive this notification, you should enable this feature.\r\n\r\nExample of enabling notifications on account removal.\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       'urn:xmpp:push:0' () {\r\n           'send-account-removal-notification' = true\r\n       }\r\n   }\r\n\r\nEnabling push notifications for messages received when all resources are AWAY/XA/DND\r\n\r\nPush notifications may also be sent by Tigase XMPP Server when new message is received and all resources of recipient are in AWAY/XA/DND state. To enable this type of notifications you need to enable additional push delivery extension named ``away`` in default push processor:\r\n\r\n.. code::\r\n\r\n   'sess-man' () {\r\n       'urn:xmpp:push:0' () {\r\n           'away' () {}\r\n       }\r\n   }\r\n\r\nAs this behaviour may not be expected by users and users need a compatible XMPP client to properly handle this notifications (XMPP client needs to retrieve message history from server to get actual message), in addition to enabling this plugin on the server, XMPP clients need to explicitly activate this feature. They can do that by including ``away`` attribute with value of ``true`` in push ``enable`` element send to the server, as in following example:\r\n\r\n**Enabling Push notifications for away/xa/dnd account.**\r\n\r\n.. code:: xml\r\n\r\n   <iq type='set' id='x43'>\r\n     <enable xmlns='urn:xmpp:push:0' away='true' jid='push-5.client.example' node='yxs32uqsflafdk3iuqo'>\r\n       <x xmlns='jabber:x:data' type='submit'>\r\n           ....\r\n       </x>\r\n     </enable>\r\n   </iq>\r\n\r\nIf later on, user decides to disable notification for account in away/xa/dnd state, it may disable push notifications or once again send stanza to enable push notification but without ``away`` attribute being set:\r\n\r\n.. code:: xml\r\n\r\n   <iq type='set' id='x43'>\r\n     <enable xmlns='urn:xmpp:push:0' away='true' jid='push-5.client.example' node='yxs32uqsflafdk3iuqo'>\r\n       <x xmlns='jabber:x:data' type='submit'>\r\n           ....\r\n       </x>\r\n     </enable>\r\n   </iq>\r\n\r\n\r\nPush Devices - Presence\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\nTigase XMPP Server comes with support for non-standard feature that allows setting it to send presence for specific resource for each user account for which push notifications were enabled. This allows XMPP users see their contacts using push capable clients as connected but eg.: away (configurable).\r\n\r\nEnabling push devices presence\r\n\r\n.. code::\r\n\r\n   'sess-man' () {\r\n       'push-presence' () {\r\n       }\r\n   }\r\n\r\n Configuring sent presence ``<show/>`` value (default is ``xa``) to ``away``.\r\n\r\n.. code::\r\n\r\n   'sess-man' () {\r\n       'push-presence' () {\r\n           presenceStatus = 'away'\r\n       }\r\n   }"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Configuration/DSL_configuration.inc",
    "content": ".. _dslConfig:\r\n\r\nDSL file format\r\n-------------------\r\n\r\nIn previous Tigase XMPP Server releases configuration was stored in properties based configuration file. From Tigase XMPP Server 8.0.0 release it will be required to use new DSL based configuration file format. This file format was inspired by Groovy language syntax and new core feature of Tigase XMPP Server - Tigase Kernel Framework.\r\n\r\nwhy new format?\r\n^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIn properties configuration format each line contained key and value with optional definition of type of stored value:\r\n\r\n.. code::\r\n\r\n   c2s/ports[i]=5222,5223\r\n\r\nwhere ``c2s/ports`` was name of property, ``[i]`` defined that type of value is array of integers, and ``5222,5223`` was comma separated list of values.\r\n\r\nThis format worked but in fact ``c2s/ports`` was not name of property you configured but key which was later split on ``/`` char to parts which defined by names path to property which name was in last part.From that you can see that it was domain based setting of properties.\r\n\r\nExcept from this multi-part keys we also used properties starting with ``--`` which were global properties accessible for every part of application, i.e.: to add new component and set some properties you needed to write:\r\n\r\n.. code::\r\n\r\n   --comp-name-1=pubsub\r\n   --comp-class-1=tigase.pubsub.PubSubComponent\r\n   pubsub/test[B]=true\r\n   pubsub/pubsub-repo-url=\"jdbc:XXXX:XXXX/db_name\"\r\n\r\nThis lead to mistakes like duplicated definition of name and class for same number of component or redefined property value in other place of a configuration file - especially in cases where configuration was big.\r\n\r\nIn this configuration structure it was hard to tell where is configuration for particular component or what databases this installation uses. This could be defined all over the file.\r\n\r\nIn this release we are introducing Tigase Kernel Framework, which allows to configure beans in configuration file and even define usage of new beans loaded from external jars which can modify behavior of Tigase components. This would make configuration file even more complex, difficult and less readable.\r\n\r\nWhat is DSL?\r\n^^^^^^^^^^^^^^^\r\n\r\nDSL stands for domain-specific language - in this case language created for storage of configuration.\r\n\r\nNow we use domain based configuration which means that our configuration file is not a flat key=value storage but it defines objects, it's properties and assigned values.\r\n\r\nTo illustrate it better let's start with a simple example. In properties file in order to configure PubSub component named ``pubsub`` you would use following properties:\r\n\r\n.. code::\r\n\r\n   --comp-name-1=pubsub\r\n   --comp-class-1=tigase.pubsub.PubSubComponent\r\n   pubsub/test[B]=true\r\n\r\nIn DSL based configuration this would be replaced by following block\r\n\r\n.. code::\r\n\r\n   pubsub (class: tigase.pubsub.PubSubComponent) {\r\n       # comment\r\n       test = true\r\n   }\r\n\r\nin which we define bean with name `pubsub` and set it's class inside ``()`` block to ``tigase.pubsub.PubSubComponent``.\r\nWe also use block between ``{}`` chars to define properties which are related to bean.\r\nWhich means this properties will be passed only to this instance of Tigase PubSub Component, same as it was before where we needed to add prefix.\r\nEntries after ``\\#`` are comments, to pass ``#`` you need to wrap whole part containing it in ``''``, ie. ``'test#242'``\r\n\r\n.. WARNING:: \r\n    If a string value assigned to a property contains any char from a following list ``=:,[]#+-*/`` it needs to be wrapped in a ``''``.\r\n\r\nWhy DSL?\r\n^^^^^^^^^\r\n\r\nDSL configuration format provides a number of advantages over the old system of configuration. All configurations for components are related in a single block, so they are not spread out over several different lines. No need for long property names, no longer have to invoke a long string of settings for multiple values. Support is provided for environment variables. No longer need to escape certain characters, making settings far more readable at a glance. Values may be set using basic calculations, such as ``100 * 200 * 2`` rather than ``40000``. Parameter type values are no longer necessary, no more [i], [S], [B] etc.. Comma separated values can now be simplified lists with separate entries being able to be in multiple lines.\r\n\r\nAlthough the format may seem more complex, looking like a section of java code, the formatting is consistent and can be far more readable. After some experience with DSL format, you'll find it's far more intuitive and user friendly than it may appear. Of course if there's any real confusion, Tigase can automatically convert old style properties files to the DSL format using the following command:\r\n\r\n.. code-block:: bash\r\n\r\n   ./scripts/tigase.sh upgrade-config etc/tigase.conf\r\n\r\nSetting property\r\n~~~~~~~~~~~~~~~~~~\r\n\r\nTo set property you just write property name followed by `=` and value to set. This is always done in context of bean which configuration property you want to set.\r\n\r\n.. code-block::\r\n\r\n   test=true\r\n\r\nIt is also possible to set property in main context by placing property outside of any context.\r\nThis sets property which value is available to access by any bean.\r\n\r\nSetting global property\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nLike in properties file it is still possible to use property names starting with ``--`` without any context or any other properties at global scope. Format is the same as in case of setting property but they are defined without scope (in global scope). This properties are global and accessible by any bean but also set as system property in JVM.\r\n\r\nDefining bean\r\n~~~~~~~~~~~~~~~~~~\r\n\r\nYou can configure bean by using following format:\r\n\r\n.. code:: bash\r\n\r\n   beanName (class: className, active: activeValue, exportable: exportableValue) {\r\n       # scope of bean properties\r\n   }\r\n\r\nwhere ``beanName`` is name under which you want to configure bean.\r\n`beanName` must be wrapped in ``''``, if ``beanName`` contains characters like ``=:,[]#+-*/`` and is recommended, if ``beanName`` is numeric only.\r\n\r\nInside block between ``(` and `)`` you can define:\r\n\r\n* ``class`` which will be used as a bean, in example above we set class as ``className``. **(default: if you try to configure bean under name which has default class assigned with it in Tigase framework then this assigned class will be used. In other case you need to pass name of class to use as a bean)**\r\n* ``active`` (boolean) whether you want the bean to be active or not (beans with ``active`` set to ``false`` are not loaded). **(default: true)**\r\n* ``exportable`` (boolean) defines if this bean should be exported and available for use for beans in inner scopes. This is advanced option in most cases it is recommended to omit this field in configuration. **(default: false)**\r\n\r\nSpaces between ``beanName`` and block between ``()`` is optional as well as space between block ``()`` and block ``{}``.\r\nIt is recommended that properties of bean would be placed in separate lines with indentation and first property will be placed in new line.\r\n\r\n.. important::\r\n\r\n   Usage of ``()`` block is very important. When this block is used in configuration it automatically sets ``active`` property of bean definition for bean for which it is used to to `true`. This is done due to fact that default value of ``active`` is ``true``.\r\n\r\n   If you omit it in configuration, you will set bean configuration but it may remain ``inactive``. In this state bean will not be loaded and as a result will not be used by Tigase XMPP Server.\r\n\r\nConfiguring bean\r\n~~~~~~~~~~~~~~~~~~\r\n\r\nIf you know that bean is defined and you do not want to change it's activity or class then you can just pass properties to configure bean in following way:\r\n\r\n.. code::\r\n\r\n   beanName {\r\n       # scope of bean properties\r\n       test = true\r\n   }\r\n\r\n\r\nwhere ``beanName`` is name of bean to configure and `test` is name of property to set to ``true`` in this bean.\r\n\r\nFormat of values\r\n~~~~~~~~~~~~~~~~~~\r\n\r\nIn properties based configuration file every property was defined as a string and only by defining expected format it was properly converted to expected value. In DSL it is possible to set values in two ways:\r\n\r\nas an object\r\n   Using this format you set list as a list and integer is set as an integer.\r\n\r\n.. table:: Format of values\r\n\r\n    +-------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n    | Type        | Description                                                                                                                                                                                                            |\r\n    +=============+========================================================================================================================================================================================================================+\r\n    | **string**  | Wrap it in ``''``, ie. to set ``test`` as string you use ``'test'``                                                                                                                                                    |\r\n    +-------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n    | **integer** | Just put value, ie. to set ``543`` use ``543``                                                                                                                                                                         |\r\n    +-------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n    | **long**    | Put value and follow it with ``L``, ie. to set ``23645434`` as long use ``23645434L``                                                                                                                                  |\r\n    +-------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n    | **float**   | Put value and follow it with ``f``, ie. to set ``231.342`` use ``231.342f``                                                                                                                                            |\r\n    +-------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n    | **boolean** | To set value just use ``true`` or ``false``                                                                                                                                                                            |\r\n    +-------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n    | **list**    | Lists can be of many types and to make it simple we decided to use as a comma separated list of values in proper format wrapped in ``[]``.                                                                             |\r\n    |             |                                                                                                                                                                                                                        |\r\n    |             | -  of strings - ``[ 'alfa', 'beta', 'gamma' ]``                                                                                                                                                                        |\r\n    |             |                                                                                                                                                                                                                        |\r\n    |             | -  of integers - ``[ 1, 2, 3, 4]``                                                                                                                                                                                     |\r\n    |             |                                                                                                                                                                                                                        |\r\n    |             | You can write it in multiple lines if you want:                                                                                                                                                                        |\r\n    |             |                                                                                                                                                                                                                        |\r\n    |             | ::                                                                                                                                                                                                                     |\r\n    |             |                                                                                                                                                                                                                        |\r\n    |             |    [                                                                                                                                                                                                                   |\r\n    |             |        'alfa'                                                                                                                                                                                                          |\r\n    |             |        'beta'                                                                                                                                                                                                          |\r\n    |             |        'gamma'                                                                                                                                                                                                         |\r\n    |             |    ]                                                                                                                                                                                                                   |\r\n    +-------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n    | **map**     | Maps can be written as a block of properties wrapped in ``{}``. This format of map is the same as used for passing configuration to bean properties. Keys and values can be written in separate lines *(recommended)*: |\r\n    |             |                                                                                                                                                                                                                        |\r\n    |             | ::                                                                                                                                                                                                                     |\r\n    |             |                                                                                                                                                                                                                        |\r\n    |             |    {                                                                                                                                                                                                                   |\r\n    |             |        test = true                                                                                                                                                                                                     |\r\n    |             |        ssl = false                                                                                                                                                                                                     |\r\n    |             |        ssl-certificate = '/test/cert.pem'                                                                                                                                                                              |\r\n    |             |        another-map = {                                                                                                                                                                                                 |\r\n    |             |            key = 'value'                                                                                                                                                                                               |\r\n    |             |        }                                                                                                                                                                                                               |\r\n    |             |    }                                                                                                                                                                                                                   |\r\n    |             |                                                                                                                                                                                                                        |\r\n    |             | or in single line *(separation with spaces is not required)*:                                                                                                                                                          |\r\n    |             |                                                                                                                                                                                                                        |\r\n    |             | ::                                                                                                                                                                                                                     |\r\n    |             |                                                                                                                                                                                                                        |\r\n    |             |    { test = true, ssl = false, ssl-certificate = '/test/cert.pem' }                                                                                                                                                    |\r\n    +-------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n\r\nas a plain string\r\n   Very similar to properties based configuration, in fact values are passed in same format and later are converted to correct type by checking type expected by bean. *(Not recommended)*\r\n\r\n.. table:: Types\r\n\r\n    +-------------+-----------------------------------------------------------------------------------------------+\r\n    | Type        | Description                                                                                   |\r\n    +=============+===============================================================================================+\r\n    | **string**  | Just put value, ie. to set ``test`` use ``test``                                              |\r\n    +-------------+-----------------------------------------------------------------------------------------------+\r\n    | **integer** | Just put value, ie. to set ``543`` use ``543``                                                |\r\n    +-------------+-----------------------------------------------------------------------------------------------+\r\n    | **long**    | Put value, ie. to set ``23645434`` as long use ``23645434``                                   |\r\n    +-------------+-----------------------------------------------------------------------------------------------+\r\n    | **float**   | Put value, ie. to set ``231.342`` use ``231.342``                                             |\r\n    +-------------+-----------------------------------------------------------------------------------------------+\r\n    | **boolean** | To set value just use ``true`` or ``false``                                                   |\r\n    +-------------+-----------------------------------------------------------------------------------------------+\r\n    | **list**    | List needs to be written as comma separated list of values, ie. ``test,abc,efg`` or ``1,2,3`` |\r\n    +-------------+-----------------------------------------------------------------------------------------------+\r\n    | **map**     | Not possible                                                                                  |\r\n    +-------------+-----------------------------------------------------------------------------------------------+\r\n\r\n\r\nUsing values from System Properties and Environment Variables\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nNow it is possible to use values of `system properties <https://docs.oracle.com/javase/tutorial/essential/environment/sysprop.html>`__ and `environment variables <https://docs.oracle.com/javase/tutorial/essential/environment/env.html>`__ and assign them to bean properties. For this purpose we added functions which can be used in DSL and which will return values of:\r\n\r\nsystem property\r\n   ``prop('property-name')`` or ``prop('property-name','default value')``\r\n\r\nenvironment variable\r\n   ``env('variable-name')``\r\n\r\n**Example of setting value of system property and environment variable to bean ``user``.**\r\n\r\n.. code::\r\n\r\n   user {\r\n     name = env('USER')\r\n     home = prop('user.home')\r\n     paths = [ prop('user.home'), prop('user.dir') ]\r\n   }\r\n\r\n\r\n.. Warning::\r\n\r\n    For properties which accepts lists it is not allowed to set value using variable/property with comma separated values like ``value1,value2`` wrapped in ``[]``, ie. ``property = [ env('some-variable') ]``. It needs to be set in following way ``property = env('some-variable')``\r\n \r\nComputed values\r\n~~~~~~~~~~~~~~~~~~\r\n\r\nWith DSL configuration format we introduce support for computable values for properties. It is now possible to set value which is result of a computation, ie. concatenation of a strings or very simple mathematical expression. We currently support only following mathematical operations:\r\n\r\n-  add\r\n\r\n-  subtract\r\n\r\n-  multiply\r\n\r\n-  divide\r\n\r\n**Example of setting environment variable related path and computed timeout.**\r\n\r\n.. code::\r\n\r\n   bean {\r\n     # setting path to `some-subdirectory` of user home directory\r\n     path = prop('user.home') + '/some-subdirectory/'\r\n\r\n     # setting timeout to 5 minutes (setting value in milliseconds)\r\n     timeout = 5L * 60 * 1000\r\n     # previously it would need to be configured in following way:\r\n     # timeout = 300000L\r\n   }\r\n\r\n.. warning::\r\n\r\n    For properties which accepts lists it is not allowed to set value using computed values with comma separated values like ``value1,value2`` wrapped in ``[]``, ie. ``property = [ env('some-variable') + ',other-value' ]``. It needs to be set in following way ``property = env('some-variable') + ',other-value'``.\r\n\r\n.. _PeriodDurationvalues:\r\n\r\nPeriod / Duration values\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nSome configuration options allow control of execution of tasks with particular period or within certain duration. DSL file format accepts strings denoting particular amount of time, which follows Java’s native structures (see: `Period <https://docs.oracle.com/javase/8/docs/api/java/time/Period.html#parse-java.lang.CharSequence->`__ and `Duration <https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html#parse-java.lang.CharSequence->`__ for detailed explanation).\r\n\r\n-  ``Duration`` formats accepted are based on the ISO-8601 duration format ``PnDTnHnMn.nS`` with days considered to be exactly 24 hours, for example:\r\n\r\n   -  ``PT20.345S`` - 20.345 seconds\r\n\r\n   -  ``PT15M`` - 15 minutes (where a minute is 60 seconds)\r\n\r\n   -  ``PT10H`` - 10 hours (where an hour is 3600 seconds)\r\n\r\n   -  ``P2D`` - 2 days (where a day is 24 hours or 86400 seconds)\r\n\r\n   -  ``P2DT3H4M`` - 2 days, 3 hours and 4 minutes\r\n\r\n-  ``Period`` format is based on the ISO-8601 period formats PnYnMnD and PnW, for example, the following are valid inputs:\r\n\r\n   -  ``P2Y`` - 2 years\r\n\r\n   -  ``P3M`` - 3 months\r\n\r\n   -  ``P4W`` - 4 weeks\r\n\r\n   -  ``P5D`` - 5 days\r\n\r\n   -  ``P1Y2M3D`` - 1 year, 2 months, 3 days\r\n\r\n   -  ``P1Y2M3W4D`` - 1 year, 2 months, 3 weeks, 4 days\r\n\r\n\r\nExample configuration file in DSL\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n.. code::\r\n\r\n   # Enable cluster mode\r\n   --cluster-mode = true\r\n   # Enable debugging for server and xmpp.impl\r\n   --debug = 'server,xmpp.impl'\r\n   # Set list of virtual hosts (old way)\r\n   --virt-hosts = 'example.com,test-1.example.com,test-2.example.com'\r\n\r\n   # Configure list of administrator jids\r\n   admins = [ 'admin@zeus', 'http@macbook-pro-andrzej.local' ]\r\n   # Set config type\r\n   config-type = '--gen-config-def'\r\n\r\n   # Configure dataSource bean with database configuration\r\n   dataSource {\r\n       # Configure default data source (using default implementation so class is omitted)\r\n       default () {\r\n           uri = 'jdbc:postgresql://127.0.0.1/tigase?user=test&password=test&autoCreateUser=true'\r\n       }\r\n\r\n       # Configure data source with name exaple.com (will be used by domain example.com)\r\n       'example.com' () {\r\n           uri = 'jdbc:mysq://127.0.0.1/example?user=test&password=test&autoCreateUser=true'\r\n       }\r\n   }\r\n\r\n   # Configure C2S component\r\n   c2s {\r\n       # Enable Stream Management bean\r\n       'urn:xmpp:sm:3' () {}\r\n\r\n       # Register tigase.server.xmppclient.SeeOtherHostDualIP as seeOtherHost bean\r\n       seeOtherHost (class: tigase.server.xmppclient.SeeOtherHostDualIP) {}\r\n\r\n       # Add additional port 5224 which is SSL port and disable port 5223\r\n       connections () {\r\n           '5224' () {\r\n                socket = ssl\r\n             }\r\n           '5223' (active: false) {}\r\n       }\r\n   }\r\n\r\n   # Configure HTTP API component\r\n   http {\r\n       # Set list of API keys\r\n       api-keys = [ 'test1234', 'test2356' ]\r\n       rest {\r\n           # Set value of environment property as a path to look for REST scripts\r\n           rest-scripts-dir = env('TIGASE_REST_SCRIPTS_DIR')\r\n       }\r\n   }\r\n\r\n   # Register pubsub-2 (class is passed as pubsub-2 name do not have default class assigned)\r\n   pubsub-2 (class: tigase.pubsub.cluster.PubSubComponentClustered) {\r\n       # Set configuration bean properties\r\n       pubsubConfig {\r\n           persistentPep = true\r\n       }\r\n       # Use tigase.pubsub.cluster.ClusteredNodeStrategy as advanced clustering strategy\r\n       strategy (class: tigase.pubsub.cluster.ClusteredNodeStrategy) {}\r\n   }\r\n\r\n   # Configure Session Manager\r\n   sess-man {\r\n       # Here we enable pep, urn:xmpp:mam:1 processors and disable message-archive-xep-0136 processor\r\n       pep () {}\r\n       'urn:xmpp:mam:1' () {}\r\n       message-archive-xep-0136 (active: false) {}\r\n\r\n       # Define class used as clustering strategy (it is different than default so class is required)\r\n       strategy (class: tigase.server.cluster.strategy.OnlineUsersCachingStrategy) {}\r\n   }\r\n\r\nDefault configuration\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTigase XMPP Server is packaged with a basic ``config.tdsl`` file that tells the server to start up in setup mode.\r\n\r\n.. code::\r\n\r\n   'config-type' = 'setup'\r\n\r\n   http () {\r\n       setup () {\r\n           'admin-user' = 'admin'\r\n       'admin-password' = 'tigase'\r\n       }\r\n   }\r\n\r\nThis tells Tigase to operate in a setup mode, and tells the http component to allow login with the username and password admin/tigase. With this you can enter the setup process that is covered in `this section <#webinstall>`__.\r\n\r\nThere are other options for config-type: ``default``, ``session-manager``, ``connection-managers``, and ``component``. For more information, visit `Config Type <#configType>`__ property description."
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Configuration/JVM_settings.inc",
    "content": "JVM settings and recommendations\r\n------------------------------------\r\n\r\nTigase configuration file ``tigase.conf`` (described in more detail in `Startup File for tigase.sh-tigase.conf <#manualconfig>`__) mentioned a couple of environmental variables which are related to the operation of the JVM. In this guide we would like to expound on those configuration options and provide hints for the optimal settings.\r\n\r\nSettings included in the ``etc/tigase.conf`` are as follows:\r\n\r\n.. code:: bash\r\n\r\n   #GC=\"-XX:+UseBiasedLocking -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:NewRatio=2 -XX:+CMSIncrementalMode -XX:-ReduceInitialCardMarks -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSInitiatingOccupancyOnly\"\r\n   #EX=\"-XX:+OptimizeStringConcat -XX:+DoEscapeAnalysis -XX:+UseNUMA\"\r\n\r\n   #GC_DEBUG=\" -XX:+PrintTenuringDistribution -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -Xloggc:logs/jvm.log -verbose:gc \"\r\n\r\n   #PRODUCTION_HEAP_SETTINGS=\" -Xms5G -Xmx5G \" # heap memory settings must be adjusted on per deployment-base!\r\n   JAVA_OPTIONS=\"${GC} ${GC_DEBUG} ${EX} ${ENC} ${DRV} ${JMX_REMOTE_IP} -server ${PRODUCTION_HEAP_SETTINGS} ${DNS_RESOLVER} ${INTERNAL_IP} ${EXTERNAL_IP}  -XX:MaxDirectMemorySize=128m \"\r\n\r\nAnd while this file utilizes bash variables, JVM configuration options can be used in the same manner on all operating systems.\r\n\r\nThe guide will consists of two main parts - memory settings and Garbage Collector tweaks descriptions and hints.\r\n\r\nWe recommend using ``-server`` JVM parameter in all cases.\r\n\r\nHeap Sizing\r\n^^^^^^^^^^^^^^^^\r\n\r\nFor the non-production deployments (development or stating environments) we recommend using default memory settings of the JVM (which depends on the underlaying operating system), which result i automatic memory allocation and, by the rule of thumb - are the safest in such environments.\r\n\r\nFor the production environments we recommend a fixed size HEAP - both initial and maximum size, which can be set with (respectively)``-Xms`` and ``-Xmx`` JVM flags - ideally to the same value (which should be roughly 95% of the available memory, if Tigase will be the only service on the machine) to avoid allocation and deallocation.\r\n\r\nFor convenience it’s possible to uncomment line with ``PRODUCTION_HEAP_SETTINGS`` and adjust parameters accordingly.\r\n\r\nMemory consideration - total usage\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nThe HEAP size is not the only thing that affects JVM memory usage. When trying to size accordingly for your usage and machine specification you have to consider other factors that count towards total: loaded classes, threads' stack, JIT code cache, garbage collector and others. In principle consider following equation:\r\n\r\n::\r\n\r\n   Maximum memory usage = [-Xmx] + [-XX:MaxMetaspaceSize] + number_of_threads * [-Xss] + [-XX:MaxDirectMemorySize]\r\n                          (heap)   (classes)                (threads' stack)             (direct memory)\r\n\r\n\r\n.. **Note**::\r\n\r\n   before Java8 memory dedicated to loaded classes was configured with ``-XX:PermSize`` and ``-XX:MaxPermSize`` instead of, respectively, ``-XX:MetaspaceSize`` and ``-XX:MaxMetaspaceSize``\r\n\r\nIn case of Tigase XMPP Server, apart from heap we limit remaining factors:\r\n\r\n-  direct memory to **128** MB\r\n\r\n-  loaded classes to **128** MB\r\n\r\n-  single thread’s stack size to **228** KB (number of threads depends on number of CPU cores and may vary from 500 to couple of thousands)\r\n\r\nIn principle, in addition to HEAP’s maximum size defined by ``-Xmx`` you should add roughly **512** MB\r\n\r\nIf you are interested in detailed tracking of memory take a look at [Memory footprint of the JVM](\\ https://spring.io/blog/2019/03/11/memory-footprint-of-the-jvm/), [Native Memory Tracking in JVM](\\ https://www.baeldung.com/native-memory-tracking-in-jvm) or [Why does my Java process consume more memory than Xmx?](\\ https://plumbr.io/blog/memory-leaks/why-does-my-java-process-consume-more-memory-than-xmx)\r\n\r\nTo facilitate getting information about complete memory usage we include this information in Tigase statistics, but it requires explicitly enabling it:\r\n\r\n- uncomment ``JVM_MEMORY`` line in ``etc/tigase.conf``:\r\n\r\n .. code:: bash\r\n\r\n    JVM_MEMORY=\" -XX:+UnlockDiagnosticVMOptions -XX:NativeMemoryTracking=summary -XX:+PrintNMTStatistics \"\r\n\r\n- enable ``detailed-memory-statistics`` in ``message-router`` bean in ``etc/config.tdsl`` file:\r\n\r\n .. code:: bash\r\n\r\n    'message-router' () {\r\n        'detailed-memory-statistics' = true\r\n    }\r\n\r\n\r\nGC settings\r\n^^^^^^^^^^^^^^\r\n\r\nLet’s start with stating that there is no \"one to rule them all\" - each deployment and use-case is different, however we will try to give a couple of pointers and recommendations proceed with short introduction to GC itself.\r\n\r\nXMPP is quite specific in terms of memory allocation - short-lived objects (various types of stanzas) usually exceed number of long-lived objects (user connections and related data). This is important bit of information in the context of how usually JVM HEAP is organized and how Garbage Collector works. On the most basic level Heap is separated into couple of regions:\r\n\r\nGenerations\r\n~~~~~~~~~~~~~~\r\n\r\n-  **Young Generation**, which is further divided in to:\r\n\r\n   -  **Eden** - the region when the objects are usually allocated when they are created;\r\n\r\n   -  **Survivor Spaces** - (*to* and *from* - one of which is always empty) - responsible for storing all live object remaining after collecting **Young Generation** (process is repeated several times until objects are finally considered *old enough*);\r\n\r\n-  **Old Generation** - (*Tenured Space*) - responsible for live objects remaining after running GC on **Survivor Spaces** - those would be *long-lived* objects (usually user connections and associated data);\r\n\r\nMinor, Major and Full GC - optimizing\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nGeneral thinking suggests that:\r\n\r\n-  **Minor GC** cleans Young generation;\r\n\r\n-  **Major GC** cleans Tenured space;\r\n\r\n-  **Full GC** cleans all heap.\r\n\r\nHowever, while we can certainly state that Minor GC cleans Young generation it’s a bit more difficult to differentiate Major and Full GC, especially considering that Major GC can be quite often triggered by Minor GC and some garbage collectors can perform cleaning concurrently. Instead of focusing of distinguishing phases one should pay closer attention to actual operations of Garbage Collector itself - uncommenting the line ``GC_DEBUG=\" -XX:+PrintTenuringDistribution -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -Xloggc:logs/jvm.log -verbose:gc \"`` in ``etc/tigase.conf`` (or adding same properties to the java commandline) and subsequently analyzing the results should prove more helpful. In addition monitoring GC operation using for example VisualVM (with VisualGC plugin) will also be helpful.\r\n\r\nSettings for XMPP\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nIdeally we should limit both number of GC pauses as well as their duration. After running rather tests following conclusions were made:\r\n\r\n-  Garbage Collection is the faster the more dead objects occupies given space, therefore on high-traffic installation it’s better to have rather large YoungGen resulting in lower promotion of the objects to the OldGen;\r\n\r\n-  with JVM8 default sizing of Young / Old generation changed, even tho NewRatio is still defaulting to “2” - setting it explicitly to \"2\" brought back previous sizing;\r\n\r\n-  Concurrent Mark and Sweep (CMS) enabled (applies to Tenured space only) with explicit configuration of NewRatio set to default value of 2 (i.e. ``-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:NewRatio=2``) in general behaves best;\r\n\r\n-  For small installations (few core CPU, less memory) with low traffic default Parallel collector may be a better solution;\r\n\r\n-  Using Heap size adjusted to the actual usage is better as the larger the heap the larger are spaces over which collection needs to be performed thus resulting in longer pauses; in case of huge heaps G1 collector may be better solution to avoid longer pauses;\r\n\r\nConsidering all of the above using following options should be a good starting point toward further optimizing of Garbage Collection:\r\n\r\n``GC=\"-XX:+UseBiasedLocking -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSIncrementalMode -XX:-ReduceInitialCardMarks -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSInitiatingOccupancyOnly\"``\r\n\r\nGC settings worth considering\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nIn addition to the general recommendation to use CMS collector, following options (or changes to the options) may be worth considering:\r\n\r\n-  ``-XX:NewRatio=2`` - defines the ratio between the young and tenured generation is 1:2. In other words, the combined size of the eden and survivor spaces will be one-third of the total heap size. The parameters NewSize and MaxNewSize bound the young generation size from below and above. Setting these to the same value fixes the young generation, just as setting -Xms and -Xmx to the same value fixes the total heap size.\r\n\r\n-  ``-XX:CMSInitiatingOccupancyFraction=percent`` - sets the percentage of the old generation occupancy (0 to 100) at which to start a CMS collection cycle.\r\n\r\n-  ``-XX:+UseCMSInitiatingOccupancyOnly`` - instructs the JVM not to base its decision when to start a CMS cycle on run time statistics but instead it uses the value of CMSInitiatingOccupancyFraction for every CMS cycle.\r\n\r\n-  ``-XX:ParallelGCThreads=x`` - sets the number of threads used for parallel garbage collection in the young and old generations. The default value depends on the number of CPUs available to the JVM. If the Tigase JMV is the only one running on the installation default value is recommended.\r\n\r\n-  ``-XX:ConcGCThreads=x`` - sets the number of threads used for concurrent GC. The default value depends on the number of CPUs available to the JVM. If the Tigase JMV is the only one running on the installation default value is recommended.\r\n\r\n-  ``-XX:+UseBiasedLocking`` and ``-XX:+DoEscapeAnalysis`` - designed to eliminate locking overhead, however their effect on performance is unpredictable therefore testing is required; reduced locking should improve concurrency and, on current multi-core hardware, improve throughput.\r\n\r\n-  ``-XX:+OptimizeStringConcat`` - enables the optimization of String concatenation operations. This option is enabled by default.\r\n\r\n-  ``-XX:+UseNUMA`` - enables performance optimization of an application on a machine with nonuniform memory architecture (NUMA - most modern computers are based on NUMA architecture) by increasing the application’s use of lower latency memory. By default, this option is disabled and no optimization for NUMA is made. The option is only available when the parallel garbage collector is used (-XX:+UseParallelGC).\r\n\r\n-  ``-XX:-UseCompressedOops`` — disables the use of compressed pointers. By default, this option is enabled, and compressed pointers are used when Java heap sizes are less than 32 GB. When this option is enabled, object references are represented as 32-bit offsets instead of 64-bit pointers, which typically increases performance when running the application with Java heap sizes less than 32 GB. This option works only for 64-bit JVMs.\r\n\r\n\r\nWhat to use with Machine x, y, z?\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nServer class machine (non-VM), > 16GB, >= 8 core CPU\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nFor such setup enabling CMS garbage collector is recommended. Depending on the traffic usage and particular use-case adjusting NewRatio may be needed. Adjusting Xms and Xms sizes for actual available memory is needed (or better yet, for the actual traffic!). Following should be used:\r\n\r\n.. code:: bash\r\n\r\n   GC=\"-XX:+UseBiasedLocking -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:NewRatio=2 -XX:+CMSIncrementalMode -XX:-ReduceInitialCardMarks -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSInitiatingOccupancyOnly\"\r\n   EX=\"-XX:+OptimizeStringConcat -XX:+DoEscapeAnalysis -XX:+UseNUMA\"\r\n\r\n   #GC_DEBUG=\" -XX:+PrintTenuringDistribution -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -Xloggc:logs/jvm.log -verbose:gc \"\r\n\r\n   PRODUCTION_HEAP_SETTINGS=\" -Xms15G -Xmx15G \" # heap memory settings must be adjusted on per deployment-base!\r\n   JAVA_OPTIONS=\"${GC} ${GC_DEBUG} ${EX} ${ENC} ${DRV} ${JMX_REMOTE_IP} -server ${PRODUCTION_HEAP_SETTINGS} ${DNS_RESOLVER} ${INTERNAL_IP} ${EXTERNAL_IP}  -XX:MaxDirectMemorySize=128m \"\r\n\r\nFor installation with lot of available memory and intention to utilize it all, using G1GC collector may be a better idea :\r\n\r\n.. code:: bash\r\n\r\n   GC=\"-XX:+UseG1GC -XX:ConcGCThreads=4 -XX:G1HeapRegionSize=2 -XX:InitiatingHeapOccupancyPercent=35 -XX:MaxGCPauseMillis=100\"\r\n   EX=\"-XX:+OptimizeStringConcat -XX:+DoEscapeAnalysis -XX:+UseNUMA\"\r\n\r\n   #GC_DEBUG=\" -XX:+PrintTenuringDistribution -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -Xloggc:logs/jvm.log -verbose:gc \"\r\n\r\n   PRODUCTION_HEAP_SETTINGS=\" -Xms60G -Xmx60G \" # heap memory settings must be adjusted on per deployment-base!\r\n   JAVA_OPTIONS=\"${GC} ${GC_DEBUG} ${EX} ${ENC} ${DRV} ${JMX_REMOTE_IP} -server ${PRODUCTION_HEAP_SETTINGS} ${DNS_RESOLVER} ${INTERNAL_IP} ${EXTERNAL_IP}  -XX:MaxDirectMemorySize=128m \"\r\n\r\n\r\nVM machine, 8GB of RAM, 4 core CPU equivalent\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nFor such setup enabling CMS garbage collector is also recommended. Depending on the traffic usage and particular use-case adjusting NewRatio may be needed (and configuring NewRatio is a must!). Adjusting Xms and Xms sizes for actual available memory is needed (or better yet, for the actual traffic!). Following should be used:\r\n\r\n.. code:: bash\r\n\r\n   GC=\"-XX:+UseBiasedLocking -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:NewRatio=2 -XX:+CMSIncrementalMode -XX:-ReduceInitialCardMarks -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSInitiatingOccupancyOnly\"\r\n   EX=\"-XX:+OptimizeStringConcat -XX:+DoEscapeAnalysis -XX:+UseNUMA\"\r\n\r\n   #GC_DEBUG=\" -XX:+PrintTenuringDistribution -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -Xloggc:logs/jvm.log -verbose:gc \"\r\n\r\n   PRODUCTION_HEAP_SETTINGS=\" -Xms7G -Xmx7G \" # heap memory settings must be adjusted on per deployment-base!\r\n   JAVA_OPTIONS=\"${GC} ${GC_DEBUG} ${EX} ${ENC} ${DRV} ${JMX_REMOTE_IP} -server ${PRODUCTION_HEAP_SETTINGS} ${DNS_RESOLVER} ${INTERNAL_IP} ${EXTERNAL_IP}  -XX:MaxDirectMemorySize=128m \"\r\n\r\n\r\nVM machine with 4GB or less of RAM, and less than 4 core CPU equivalent\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nSmall installations with limited resources could operate better with default (for JVM versions up to 8, which is the most current at the moment of the writing). Again - depending on the traffic usage and particular use-case adjusting NewRatio may be needed. Adjusting Xms and Xms sizes for actual available memory is recommended (or better yet, for the actual traffic!). Following should be used (i.e. ``GC`` line should be commented so the defaults will be used):\r\n\r\n.. code:: bash\r\n\r\n   #GC=\"-XX:+UseBiasedLocking -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:NewRatio=2 -XX:+CMSIncrementalMode -XX:-ReduceInitialCardMarks -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSInitiatingOccupancyOnly\"\r\n   EX=\"-XX:+OptimizeStringConcat -XX:+DoEscapeAnalysis -XX:+UseNUMA\"\r\n\r\n   #GC_DEBUG=\" -XX:+PrintTenuringDistribution -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -Xloggc:logs/jvm.log -verbose:gc \"\r\n\r\n   PRODUCTION_HEAP_SETTINGS=\" -Xms3G -Xmx3G \" # heap memory settings must be adjusted on per deployment-base!\r\n   JAVA_OPTIONS=\"${GC} ${GC_DEBUG} ${EX} ${ENC} ${DRV} ${JMX_REMOTE_IP} -server ${PRODUCTION_HEAP_SETTINGS} ${DNS_RESOLVER} ${INTERNAL_IP} ${EXTERNAL_IP}  -XX:MaxDirectMemorySize=128m \"\r\n\r\n\r\nAdditional resources\r\n^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n-  `Sizing the Generations <https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/sizing.html>`__\r\n\r\n-  `About Java, parallel garbage collection and processor sets <http://www.c0t0d0s0.org/archives/6617-About-Java,-parallel-garbage-collection-and-processor-sets.html>`__\r\n\r\n-  `GC Threads <http://hiroshiyamauchi.blogspot.cl/2009/12/gc-threads.html>`__\r\n\r\n-  `GCViewer readme <https://github.com/chewiebug/GCViewer#readme>`__\r\n\r\n-  `Java HotSpot™ Virtual Machine Performance Enhancements <http://docs.oracle.com/javase/7/docs/technotes/guides/vm/performance-enhancements-7.html>`__\r\n\r\n-  `Java Garbage Collection handbook <https://plumbr.eu/java-garbage-collection-handbook>`__\r\n\r\n-  Useful JVM Flags\r\n\r\n   -  `Part 1 - JVM Types and Compiler Modes <https://blog.codecentric.de/en/2012/07/useful-jvm-flags-part-1-jvm-types-and-compiler-modes/>`__\r\n\r\n   -  `Part 2 - Flag Categories and JIT Compiler Diagnostics) <https://blog.codecentric.de/en/2012/07/useful-jvm-flags-part-2-flag-categories-and-jit-compiler-diagnostics/>`__\r\n\r\n   -  `Part 3 - Printing all XX Flags and their Values <https://blog.codecentric.de/en/2012/07/useful-jvm-flags-part-3-printing-all-xx-flags-and-their-values/>`__\r\n\r\n   -  `Part 4 - Heap Tuning <https://blog.codecentric.de/en/2012/07/useful-jvm-flags-part-4-heap-tuning/>`__\r\n\r\n   -  `Part 5 - Young Generation Garbage Collection <https://blog.codecentric.de/en/2012/08/useful-jvm-flags-part-5-young-generation-garbage-collection/>`__\r\n\r\n   -  `Part 6 - Throughput Collector <https://blog.codecentric.de/en/2013/01/useful-jvm-flags-part-6-throughput-collector/>`__\r\n\r\n   -  `Part 7 - CMS Collector <https://blog.codecentric.de/en/2013/10/useful-jvm-flags-part-7-cms-collector/>`__\r\n\r\n   -  `Part 8 - GC Logging <https://blog.codecentric.de/en/2014/01/useful-jvm-flags-part-8-gc-logging/>`__\r\n\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Configuration/Logging.inc",
    "content": "Settings for Custom Logging in Tigase\r\n----------------------------------------\r\n\r\nLogging can be an important tool to monitor your server’s health and performance. Logging may be controlled and customized on a per-component basis.\r\n\r\nA ``logging`` bean has been implemented to allow more flexible configuration of logging in the Tigase XMPP Server.\r\n\r\nConfiguring logging\r\n^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIn the config file\r\n~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nDefault logging configuration for your installation is kept in the config file and it may be adjusted there.\r\n\r\n.. code::\r\n\r\n   logging () {\r\n       rootLevel = CONFIG\r\n       'packet-debug-full' = true\r\n       loggers = {\r\n           'tigase.server' = {\r\n               level = ALL\r\n           }\r\n           'tigase.conf' = {\r\n               level = FINEST\r\n           }\r\n       }\r\n       handlers = {\r\n           ' java.util.logging.FileHandler' = {\r\n               level = ALL\r\n               append = true\r\n               count = 5\r\n               formatter = 'tigase.util.LogFormatter'\r\n               limit = 10000000\r\n               pattern = 'logs/tigase.log'\r\n           }\r\n           'java.util.logging.ConsoleHandler' = {\r\n               level = WARNING\r\n               formatter = 'tigase.util.LogFormatter'\r\n           }\r\n       }\r\n   }\r\n\r\nYou only need to specify the settings you wish to customize, otherwise they will be left as default.\r\n\r\n-  ``packet-debug-full`` - controls whether log entries should be obfuscated (all CData of all elements will be replaced by ``CData size: <length in bytes of the replaced string>``) or not; default: ``false``.\r\n\r\n-  ``rootLevel`` - Defines the root level of logging for all components not otherwise defined. Default is CONFIG\r\n\r\n-  ``loggers`` - Defines the level of logging for packages running in tigase server. This is similar to the --debug setting, however you must use ``tigase.{package}`` format. Default is NONE.\r\n\r\n-  ``handlers`` - Defines the level of logging for File output and Console output.\r\n\r\n   1. ``FileHandler`` - is the file output for log files, with the following options:\r\n\r\n      a. ``level`` - specifies the level of logs to be written, default is ALL.\r\n\r\n      b. ``append`` - whether to append to the log or replace it during restart. Default is true.\r\n\r\n      c. ``count`` - number of individual log files to keep at set limit. Default is 5. (default settings will continue appending logs until 5 files at 10MB are reached, then the oldest file will be overwritten.)\r\n\r\n      d. ``formatter`` - specifies the package to format logging output. Default is tigase.util.LogFormatter.\r\n\r\n      e. ``limit`` - Byte limit for each log file. Default is 10000000 or 10MB.\r\n\r\n      f. ``pattern`` - Directory and filename of the log file with respect to the Tigase installation directory. Default is logs/tigase.log.\r\n\r\n   2. ``ConsoleHandler`` - Determines the formatting for Tigase output to console.\r\n\r\n      a. ``level`` - specifies the level of logs to be written, default is WARNING.\r\n\r\n      b. ``formatter`` - specifies the package to format logging output. Default is tigase.util.LogFormatter.\r\n\r\n\r\nDisabling colored output\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIf for some reason you don’t want colored output in the logs you can disable it by setting ``disable_logger_color`` to ``true``. For convenience, you can uncomment in ``etc/tigase.conf`` following line:\r\n\r\n.. code:: bash\r\n\r\n   #DISABLE_LOGGER_COLOR=\" -Ddisable_logger_color=true \"\r\n\r\nAd-hoc changes to the logging configuration\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nIt is also possible to use ad-hoc command named **Set package logging** with id ``logging-set`` available at ``message-router@domain`` (where domain is your server name) to reconfigure logging level of packets at runtime without requirement of restarting the Tigase XMPP Server.\r\n\r\n   **Note**\r\n\r\n   Those changes will be applied to this single cluster node.\r\n\r\n.. **Note**::\r\n\r\n   Those changes will not be saved in the Tigase XMPP Server configuration file and will be lost after restart of Tigase XMPP Server.\r\n\r\nUsing Admin UI\r\n\r\nIf your Tigase XMPP Server is running with HTTP server and with Admin UI enabled, then the easiest way to change logging configuration is by using Admin UI. After logging into web interface, open ``Configuration`` section and select ``Set package logging`` command. This will bring to you a form which you need to fill in with following fields:\r\n\r\n-  ``Package name`` - should contain Java package or class name for which you wish to change logging level\r\n\r\n-  ``Level`` - select a logging level you wish to apply to entered package name *(``OFF`` means that logging will be disabled)*\r\n\r\nAfter pressing ``Submit`` your form will be passed to the server for validation and selected changes will be applied.\r\n\r\n\r\nUsing ad-hoc command\r\n\r\nIf you have access to the XMPP admin account of Tigase XMPP Server and XMPP client which supports ad-hoc command execution, you may connect with your XMPP client to the Tigase XMPP Server and look for adh-hoc commands available at ``message-router@domain`` (where domain is your server name). Within found ad-hoc commands you should find command named ``Set package logging`` or ``logging-set`` (that depends what your XMPP client is showing, id or name of the command) and you should execute it. Tigase XMPP Server will return a form which you need to fill in with following fields:\r\n\r\n-  ``Package name`` - should contain Java package or class name for which you wish to change logging level\r\n\r\n-  ``Level`` - select a logging level you wish to apply to entered package name *(``OFF`` means that logging will be disabled)*\r\n\r\nAfter submitting the form, Tigase XMPP Server will validate your request and update logging configuration.\r\n\r\n\r\nUsing REST API\r\n\r\nIf you have Tigase XMPP Server with REST API enabled, you can use it for configuring logging of Tigase XMPP Server as well.\r\n\r\n   **Note**\r\n\r\n   As with all HTTP REST API requests you will require a valid API key and in this case a valid admin credentials to authenticate a HTTP request using Basic HTTP Authentication.\r\n\r\nAll you need to to is to send a HTTP POST request to ``/rest/adhoc/message-router@domain.com`` (where domain is your server name) with ``Contect-Type`` set to ``application/xml`` and a following XML as a payload to set logging level of ``tigase.server`` package to ``ALL``.\r\n\r\n.. code:: xml\r\n\r\n   <command>\r\n     <node>logging-set</node>\r\n     <fields>\r\n       <item>\r\n         <var>package-name</var>\r\n         <value>tigase.server</value>\r\n       </item>\r\n       <item>\r\n         <var>level</var>\r\n         <value>ALL</value>\r\n       </item>\r\n     </fields>\r\n   </command>\r\n\r\n.. **Tip**::\r\n\r\n   You can set logging levels also using JSON payload. For more details please refer to `Tigase HTTP API Guide <https://docs.tigase.net/index.html#collapse-tigase-http-api-master-snapshot-head:>`__, to section ``Executing example ad-hoc commands`` in ``REST API - usage examples``.\r\n\r\nAlternate loggers in Tigase - Logback\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIt’s possible to use Logback for logging purposes, which offers certain interesting features (async logging, better control over log rotation, on the fly changing logging configuration)\r\n\r\nRequirements: \\* slf4j-api.jar (provided in ``-dist-max`` package) \\* jul-to-slf4j.jar (provided in ``-dist-max`` package) \\* desired logger libraries (for logback it’s ``logback-classic.jar`` and ``logback-core.jar`` (provided in -dist-max).\r\n\r\nConfiguration boils down to adding slf4j bridge handler to the list of build-in Java Logger handlers configuration, which in Tigase translates to adding following line to ``etc/config.tdsl``:\r\n\r\n.. code:: bash\r\n\r\n   logging () {\r\n       rootHandlers = [ 'java.util.logging.ConsoleHandler', 'java.util.logging.FileHandler', 'org.slf4j.bridge.SLF4JBridgeHandler' ]\r\n   }\r\n\r\nAfter that ``etc/logback.xml`` configuration file will be used.\r\n\r\nAs stated in [jul-to-slf4j bridge documentation](\\ http://www.slf4j.org/legacy.html#jul-to-slf4j) it’s essential to include ``LevelChangePropagator`` to eliminate translation overhead for disabled log statements:\r\n\r\n.. code:: xml\r\n\r\n   <configuration debug=\"true\">\r\n     <contextListener class=\"ch.qos.logback.classic.jul.LevelChangePropagator\"/>\r\n     ...\r\n   </configuration>\r\n\r\nNOTE, that it may be prudent to remove configuration of all old JUL logger by appending following to ``etc/logback.xml`` configuration:\r\n\r\n.. code:: xml\r\n\r\n   <configuration debug=\"true\">\r\n     <contextListener class=\"ch.qos.logback.classic.jul.LevelChangePropagator\"/>\r\n       <resetJUL>true</resetJUL>\r\n   </configuration>"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Configuration/Session_Manager.inc",
    "content": "Session Manager\r\n-----------------\r\n\r\nTigase Session Manager is where most of Tigase basic options can be configured, and where many operations are controlled from. Changes to session manager can effect operations throughout an entire XMPP installation, so care must be made when changing settings here.\r\n\r\n.. _sessManMobileOpts:\r\n\r\nMobile Optimizations\r\n^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nBy default, Tigase employs XEP-0352 Client State Indication which allows for a more streamlined mobile experiencing by allowing the XMPP server to suppress or reduce the number of updates sent to a client thereby reducing the number of stanzas sent to a mobile client that is inactive. This employment is contained within the processor ``ClientStateIndication`` and is independent from the MobileV1, MobileV2, MobileV3 settings.\r\n\r\nHowever, this can be fine tuned by using mobile plugins from Tigase which can be used at the same time by adding the following line to the ``config.tdsl`` file:\r\n\r\n.. code::\r\n\r\n   }\r\n   'sess-man' {\r\n       'urn:xmpp:csi:0' {\r\n           logic = 'tigase.xmpp.impl.MobileV1'\r\n       }\r\n   }\r\n\r\nLogic Options are:\r\n\r\nMobileV1\r\n~~~~~~~~~~~~\r\n\r\nKeeps all presence stanzas in queue until client is active.\r\n\r\n::\r\n\r\n   logic = 'tigase.xmpp.impl.MobileV1'\r\n\r\n\r\nMobileV2\r\n~~~~~~~~~~~~\r\n\r\nThis setting delays delivery of presences while client is in inactive state, but only keeps the last presence for each full jid. **This is the default setting for CSI logic**.\r\n\r\n::\r\n\r\n   logic = 'tigase.xmpp.impl.MobileV2'\r\n\r\n\r\nMobileV3\r\n~~~~~~~~~~~~\r\n\r\nKeeps the same presence logic as MobileV2, but also queues Message Carbons. **Currently not supported by CSI processor, will cause issues**.\r\n\r\n::\r\n\r\n   logic = 'tigase.xmpp.impl.MobileV3'\r\n\r\n\r\nDisabling CSI\r\n~~~~~~~~~~~~~~~\r\n\r\nIf you wish to not use the ``ClientStateIndication`` processor, set the following in your ``config.tdsl`` file:\r\n\r\n.. code::\r\n\r\n   'sess-man' () {\r\n       'urn:xmpp:csi:0' (active: false) {}\r\n   }\r\n\r\n\r\nA note about Mobile Plugins\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nPreviously, you could enable Mobile optimization logic using by enabling ``Mobile_V1 (){}`` bean to Session Manager: ``sess-man () {}`` bean.\r\n\r\nIf you have used these in the past, it is recommended you change your system to use the CSI processor with the appropriate mobile processing logic.\r\n\r\nIf you require v3 logic, or do not wish to use CSI, be sure to disable it using the above option.\r\n\r\n\r\nthreads-pool\r\n^^^^^^^^^^^^^^^^\r\n\r\nThe ``threadsNo`` property allows you to fine-tune the SM plugin’s (processors) thread pool. With the default settings every plugin gets his own thread pool. This guarantees the best performance and optimal resource usage. The downside of this setting is that packets can arrive out of order if they are processed within different thread pools.\r\n\r\nWe can even fine tune this packet processing. Let’s say you want most of the plugins to be executed within a single thread pool to preserve packet ordering for them, but for some selected plugins that should execute within separate thread pools to improve performance. Let’s say, authentication packets and user registration can be actually executed in a separate thread pools as we do not worry about an order for them. Users cannot send or receive anything else before they authenticates anyway. The solution is to specify a number of threads for the selected plugin. For example, setting a common thread pool for all plugins but registration and authentication can be done with following configuration:\r\n\r\n.. code::\r\n\r\n   'sess-man' () {\r\n       'amp' () {\r\n           threadsNo = 30\r\n       }\r\n       'presence-state' () {\r\n           threadsNo = 27\r\n       }\r\n   }\r\n\r\nThis replaces the old ``--sm-threads-pool`` property, as well as specifying thread pools in ``--sm-plugins``.\r\n\r\n\r\nThread Pool factor\r\n^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nSession manager can control the number of available thread pools for each processor. By adding the following line to the ``config.tdsl`` file, the global thread pool can be increased by a specified factor:\r\n\r\n.. code::\r\n\r\n   'sess-man' () {\r\n       'sm-threads-factor' = 3\r\n   }\r\n\r\nIn this case, the global thread pools is increased by a factor or 3.\r\n\r\n\r\nStrategy\r\n^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThe ``Strategy`` property allows users to specify Clustering Strategy class which should be used for handling clustering environment; by default ``SMNonCachingAllNodes`` is used.\r\n\r\nAny class implementing ``tigase.cluster.strategy.ClusteringStrategyIfc`` interface may be used for this setting.\r\n\r\nExample:\r\n\r\n.. code::\r\n\r\n   'sess-man' () {\r\n       strategy (class: tigase.cluster.strategy.SMCachingAllNodes)\r\n   }\r\n\r\nThis replaces the old ``--sm-cluster-strategy-class`` setting from v7.1."
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc",
    "content": ".. _linuxhighload:\r\n\r\nLinux Settings for High Load Systems\r\n---------------------------------------\r\n\r\nThere are a few basic settings you have to adjust for high load systems to make sure the server has enough resources to handle a big number of network connections.\r\n\r\nThe main parameter is a maximum number of opened files allowed for the process to keep at the same time. Each network connection uses a file handler, therefore if the limit is too low you can quickly run out of handlers and the server can not accept any more connections.\r\n\r\nThis limit is set on 2 levels - on the kernel level (``fs.file-max``) and on the system level (``nofile``).\r\n\r\nAnother kernel property which can be important in certain configurations (like transports installations or when you use proxy for Bosh connections) is: ``net.ipv4.ip_local_port_range``. This parameter can be set the same way as the ``fs.file-max`` property.\r\n\r\nfs.file-max\r\n^^^^^^^^^^^^^^\r\n\r\nThe ``fs.file-max`` kernel property is set via sysctl command. You can see current settings by executing the command:\r\n\r\n.. code:: sh\r\n\r\n   # sysctl fs.file-max\r\n   fs.file-max = 358920\r\n\r\nIf you plan to run high load service with large number of server connections, then this parameter should be at least as twice big as the number of network connections you expect to support. You can change this setting by executing the command:\r\n\r\n::\r\n\r\n   # sysctl -w fs.file-max=360000\r\n   fs.file-max = 360000\r\n\r\nnet.ipv4.ip_local_port_range\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nYou can see current settings by executing the command:\r\n\r\n.. code:: sh\r\n\r\n   # sysctl net.ipv4.ip_local_port_range\r\n   net.ipv4.ip_local_port_range = 32768    61000\r\n\r\nYou can change this setting by executing the command:\r\n\r\n.. code:: sh\r\n\r\n   # sysctl -w net.ipv4.ip_local_port_range=\"1024 65000\"\r\n   net.ipv4.ip_local_port_range = 1024 65000\r\n\r\nTCP_keepalive\r\n^^^^^^^^^^^^^^\r\n\r\nAccording to `Using TCP keepalive to Detect Network Errors <http://www.gnugk.org/keepalive.html>`__ and `TCP Keepalive HOWTO <https://tldp.org/HOWTO/TCP-Keepalive-HOWTO/usingkeepalive.html>`__ some keepalive settings should be changed to improve reliability - it will enable keep alive functionality (checking if the connection is established and valid) and, by decreasing times and interval - will make detection of broken connections faster.\r\n\r\n.. code:: sh\r\n\r\n   # sysctl -w net.ipv4.tcp_keepalive_time=\"60\"\r\n   net.ipv4.tcp_keepalive_time = 60\r\n   # sysctl -w net.ipv4.tcp_keepalive_probes=\"3\"\r\n   net.ipv4.tcp_keepalive_probes = 3\r\n   # sysctl -w net.ipv4.tcp_keepalive_intvl=\"90\"\r\n   net.ipv4.tcp_keepalive_intvl = 90\r\n   # sysctl -w net.ipv4.tcp_retries2=4\r\n   net.ipv4.tcp_retries2 = 4\r\n\r\n\r\n/etc/sysctl.conf\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThe above commands let the system remember new settings until the next system restart. If you want to make the change permanent you have to edit the file: ``/etc/sysctl.conf`` and add the property at the end of the file:\r\n\r\n.. code:: sh\r\n\r\n   fs.file-max=360000\r\n   net.ipv4.ip_local_port_range=1024 65000\r\n   net.ipv4.tcp_keepalive_time=60\r\n   net.ipv4.tcp_keepalive_probes=3\r\n   net.ipv4.tcp_keepalive_intvl=90\r\n   net.ipv4.tcp_retries2=4\r\n\r\nIt will be automatically loaded next time you start the server.\r\n\r\nCommand:\r\n\r\n.. code:: sh\r\n\r\n   # sysctl -p\r\n\r\nCauses the ``/etc/systcl.conf`` to be reloaded which is useful when you have added more parameters to the file and don’t want to restart the server.\r\n\r\nnofile\r\n^^^^^^^^^^^^^^\r\n\r\nThis is the property used by the system limits. For example running the command ``ulimit -a`` shows you all limits set for the current user:\r\n\r\n.. code:: sh\r\n\r\n   # ulimit -a\r\n   core file size          (blocks, -c) 0\r\n   data seg size           (kbytes, -d) unlimited\r\n   file size               (blocks, -f) unlimited\r\n   pending signals                 (-i) 38912\r\n   max locked memory       (kbytes, -l) 32\r\n   max memory size         (kbytes, -m) unlimited\r\n   open files                      (-n) 40960\r\n   pipe size            (512 bytes, -p) 8\r\n   POSIX message queues     (bytes, -q) 819200\r\n   stack size              (kbytes, -s) 8192\r\n   cpu time               (seconds, -t) unlimited\r\n   max user processes              (-u) 38912\r\n   virtual memory          (kbytes, -v) unlimited\r\n   file locks                      (-x) unlimited\r\n\r\nTo make it even more interesting and more complex, there are 2 types of system limits: **soft limit** which can be temporarily exceeded by the user and **hard limit** which can not be exceeded. To see your **hard limit** execute command:\r\n\r\n.. code:: sh\r\n\r\n   # ulimit -a -H\r\n   core file size          (blocks, -c) unlimited\r\n   data seg size           (kbytes, -d) unlimited\r\n   file size               (blocks, -f) unlimited\r\n   pending signals                 (-i) 38912\r\n   max locked memory       (kbytes, -l) 32\r\n   max memory size         (kbytes, -m) unlimited\r\n   open files                      (-n) 40960\r\n   pipe size            (512 bytes, -p) 8\r\n   POSIX message queues     (bytes, -q) 819200\r\n   stack size              (kbytes, -s) unlimited\r\n   cpu time               (seconds, -t) unlimited\r\n   max user processes              (-u) 38912\r\n   virtual memory          (kbytes, -v) unlimited\r\n   file locks                      (-x) unlimited\r\n\r\nThe hard limits are usually bigger then the soft limits or sometimes the same.\r\n\r\nFor us the most important parameter is: **open files**. You can change the property in file: ``/etc/security/limits.conf``. You have to append 2 following lines to the end of the file:\r\n\r\n.. code:: sh\r\n\r\n   jabber               soft    nofile         350000\r\n   jabber               hard    nofile         350000\r\n\r\nWhere the ``jabber`` is the user name of the account running you IM service. You can also set the limits for all users on the machine in a following way:\r\n\r\n.. code:: sh\r\n\r\n   *               soft    nofile         350000\r\n   *               hard    nofile         350000\r\n\r\nFor those changes to make an effect you have to logout from the modified account and login again. New limits should be applied.\r\n\r\n\r\nsu and init script\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIf one intends to use init scripts for startup purposes (or simply wants to be able to start the server utilizing su command) it’s necessary to adjust PAM configuration by modifying /etc/pam.d/su file and uncomment following line:\r\n\r\n.. code:: sh\r\n\r\n   session    required   pam_limits.so\r\n\r\nAfterwards the init scripts will respect configured limits."
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Configuration/Startup_Files.inc",
    "content": ".. _manualconfig:\r\n\r\nStartup File for tigase.sh - tigase.conf\r\n--------------------------------------------\r\n\r\nProperty file names for ``tigase.sh`` startup script is a second parameter for the startup script. It can be skipped if environmental variables are set in different location or in different way.\r\n\r\nConfig file for startup script simply sets number of environment variables with the location of required components. Possible variables to set in this file are:\r\n\r\n-  ``JAVA_HOME`` - location of Java installation home directory. **Must be set**.\r\n\r\n-  ``TIGASE_HOME`` - location of Tigase installation home directory. *By default script try to find this location by searching directories from the location where the script has been run.*\r\n\r\n-  ``TIGASE_CONSOLE_LOG`` - file to which all console messages will be redirected if server is run in background. By default it will be: ``TIGASE_HOME/logs/tigase-console.log``. **If this file/directory is not writable by Tigase process all console messages will be redirected to /dev/null**\r\n\r\n-  ``TIGASE_PID`` location of the file with server PID number. By default it will be ``TIGASE_HOME/logs/tigase.pid``.\r\n\r\n-  ``JAVA_OPTIONS`` - options for JVM like size of RAM allocated for the JVM, properties and so on.\r\n\r\n-  ``TIGASE_OPTIONS`` - (optional) additional options for Tigase server program. You can tweak initial parameters for your environment here. If you want to specify custom location of your configuration file you should use ``--config-file <path/to/config.tdsl>`` configuration\r\n\r\nSample file to run **Tigase** with **PostgreSQL** database may look like:\r\n\r\n.. code:: bash\r\n\r\n   ENC=\"-Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8\"\r\n   DRV=\"-Djdbc.drivers=org.postgresql.Driver\"\r\n   JAVA_OPTIONS=\"${ENC} ${DRV} -server -Xms100M -Xmx100M \"\r\n   CLASSPATH=\"\"\r\n   TIGASE_CONFIG=\"tigase-pgsql.xml\"\r\n   TIGASE_OPTIONS=\" \"\r\n\r\nPlease note encoding settings. JVM by default uses encoding set in operating system environment. XMPP protocol, however uses ``UTF-8`` for all data processing. So the ENC settings enforces ``UTF-8`` encoding for all operations.\r\n\r\nAnother significant setting is \\\\'**CLASSPATH**'. It is intentionally set to an empty string. The **tigase.sh** startup script builds the **CLASSPATH** on it’s own from files found in **jars/** and **libs/** directories. It is advised to set the **CLASSPATH** to the empty string because the Tigase server scans all available classes to find all components and plugins implementation. If the **CLASSPATH** contains lots of libraries which are not used anyway it can cause a long startup time and high system loads.\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Configuration/Vhosts.inc",
    "content": ".. _tigase41virtualHosts:\r\n\r\nVirtual Hosts in Tigase Server\r\n----------------------------------\r\n\r\nTigase server supports multiple virtual hosts in a single server installation. Virtual hosts can be added or removed, enabled or disabled during runtime without restarting the service or disrupting normal operation.\r\n\r\nThis document describes how virtual hosts work in Tigase server and how to get the most out of this feature in your installation.\r\n\r\nThe :ref:`'default-virtual-host'<virtHosts>` property allows to define name of the single vhost domain which will be considered a default vhost domain for this installation. It allows you just to configure the domain name. Any additional configuration needs to be configured using ad-hoc commands.\r\n\r\nVirtual hosts should be managed using ad-hoc commands or admin ui, visit :ref:`Add and Manage Domains<addManageDomain>` for description of vhosts management process or visit :ref:`Specification for ad-hoc Commands Used to Manage Virtual Domains<adhocCommands>` for more information about ad-hoc commands.\r\n\r\nIf you have components that may not be able to handle multiple vhosts or cluster mode, we have developed a virtual component solution as well, details in the :ref:`Virtual Components for the Tigase Cluster<virtualComponents>` section.\r\n\r\nYou may also want to reference the Vhosts API for additional information: - :ref:`API Description for Virtual Domains Management in Tigase Server<addManageDomain>`.\r\n\r\n.. **Tip**::\r\n\r\n   If you have a Tigase XMPP Server running in the cluster mode hidden behind some load balancer, or if internal IP or hostname of cluster nodes differ from the DNS name under which it is available from the internet, we would suggest setting a property ``installation-dns-address`` of ``vhost-man`` component to the DNS name which allows you to connect to all cluster nodes (ie. to the DNS name of the load balancer). This will allow Tigase XMPP Server to do proper DNS lookups to verify that DNS domain name of the virtual host which you will try to add or update points to your XMPP installation.\r\n\r\n\r\nDefault VHost configuration\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIt’s possible to specify initial default configuration for all Virtual Host in TDSL configuration file (i.e. ``etc/config.tdsl``) for selected parameters. To do so you should specify each configuration option within ``defaults`` bean belonging to ``vhost-man`` bean:\r\n\r\n::\r\n\r\n   'vhost-man' () {\r\n       'defaults' () {\r\n           'domain-filter-policy' = null\r\n           's2s-secret' = null\r\n           trusted = null\r\n           'vhost-disable-dns-check' = false\r\n           'vhost-max-users' = 0L\r\n           'vhost-message-forward-jid' = null\r\n           'vhost-presence-forward-jid' = null\r\n           'vhost-register-enabled' = true\r\n           'vhost-tls-required' = false\r\n       }\r\n   }\r\n\r\nAfter initial definition of default configuration or after first startup of Tigase XMPP Server it is possible to configure Virtual Host defaults using ad-hoc commands by modifying values for ``default`` using ad-hoc as described in :ref:`Specification for ad-hoc Commands Used to Manage Virtual Domains<adhocCommands>`.\r\n\r\nAlternatively, you may edit default Virtual Host configuration (configuration for domain ``default``) using Admin UI which by default is available at http://localhost:8080/admin/.\r\n\r\n.. _adhocCommands:\r\n\r\nSpecification for ad-hoc Commands Used to Manage Virtual Domains\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThere are 3 ad-hoc commands for virtual domains management in the Tigase server:\r\n\r\n1. ``VHOSTS_RELOAD`` used to reload virtual domains list from the repository (database).\r\n\r\n2. ``VHOSTS_UPDATE`` used to add a new virtual domain or update information for existing one.\r\n\r\n3. ``VHOSTS_REMOVE`` used to remove an existing virtual host from the running server.\r\n\r\nSyntax of the commands follows the specification described in `XEP-0050 <http://xmpp.org/extensions/xep-0050.html>`__. Extra information required to complete the command is carried as data forms described in `XEP-0004 <http://xmpp.org/extensions/xep-0004.html>`__.\r\n\r\nAll commands are accepted by the server only when send by the installation administrator. If the command is sent from any other account ``<not-authorized />`` error is returned. To grant administrator rights to an account you have to set ``admins`` property in the ``config.tdsl`` configuration file.\r\n\r\nCommands are sent to the 'vhost-man' server component and the 'to' attribute of the stanza must contain a full JID of the VHostManager on the server. The full **JID** consists of the component name: 'vhost-man' and the local domain, that is domain which is already on the list of virtual domains and is active. Assuming 'existing.domain.com' one of domains already activated for the server installation the **JID** is: 'vhost-man@existing.domain.com'.\r\n\r\nReloading the Domains List from the Database\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nIn order to reload virtual domains from the permanent repository other than configuration file, you have to send ``VHOSTS_RELOAD`` ad-hoc command to the VHostManager on the server.\r\n\r\nThe reload command request is of the form:\r\n\r\n.. code:: xml\r\n\r\n   <iq type=\"set\"\r\n       to=\"vhost-man@existing.domain.com\"\r\n       id=\"aac8a\">\r\n     <command xmlns=\"http://jabber.org/protocol/commands\"\r\n              node=\"VHOSTS_RELOAD\" />\r\n   </iq>\r\n\r\nThe server sends a response upon successful completion of the command with current number of virtual domains server by the installation:\r\n\r\n.. code:: xml\r\n\r\n   <iq from=\"vhost-man@existing.domain.com\"\r\n       type=\"result\"\r\n       to=\"cmd-sender-admin@existing.domain.com\"\r\n       id=\"aac8a\">\r\n     <command xmlns=\"http://jabber.org/protocol/commands\"\r\n              status=\"completed\"\r\n              node=\"VHOSTS_RELOAD\">\r\n       <x xmlns=\"jabber:x:data\" type=\"result\">\r\n         <field type=\"fixed\" var=\"Note\">\r\n           <value>Current number of VHosts: 123</value>\r\n         </field>\r\n       </x>\r\n     </command>\r\n   </iq>\r\n\r\nIf the command is sent from an account other than admin, the server returns an error:\r\n\r\n.. code:: xml\r\n\r\n   <iq from=\"vhost-man@existing.domain.com\"\r\n       type=\"error\"\r\n       to=\"cmd-sender-admin@existing.domain.com\"\r\n       id=\"aac8a\">\r\n     <command xmlns=\"http://jabber.org/protocol/commands\"\r\n              node=\"VHOSTS_RELOAD\" />\r\n     <error type=\"auth\" code=\"401\">\r\n       <not-authorized xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\" />\r\n       <text xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"\r\n             xml:lang=\"en\">\r\n         You are not authorized for this action.\r\n       </text>\r\n     </error>\r\n   </iq>\r\n\r\nThe response doesn’t have any special meaning other then end-user information. The client may ignore the response as it is sent after the command has been executed.\r\n\r\nAdding a New Domain or Updating Existing One\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nIn order to add a new domain or update existing one you have to send an ad-hoc command ``VHOSTS_UPDATE`` with at least one domain name in the command data form. You can also specify whether the domain is enabled or disabled but this is optional. Future releases may allow for setting additional parameters for the domain: maximum number of user accounts for this domain, anonymous login enabled/disabled for the domain, registration via XMPP enabled/disabled for this domain and some more parameters not specified yet.\r\n\r\nThe domain add/update command request is of the form:\r\n\r\n.. code:: xml\r\n\r\n   <iq type=\"set\"\r\n       to=\"vhost-man@existing.domain.com\"\r\n       id=\"aacba\">\r\n     <command xmlns=\"http://jabber.org/protocol/commands\"\r\n              node=\"VHOSTS_UPDATE\">\r\n       <x xmlns=\"jabber:x:data\" type=\"submit\">\r\n         <field type=\"text-single\"\r\n                var=\"VHost\">\r\n           <value>new-virt.domain.com</value>\r\n         </field>\r\n         <field type=\"list-single\"\r\n                var=\"Enabled\">\r\n           <value>true</value>\r\n         </field>\r\n       </x>\r\n     </command>\r\n   </iq>\r\n\r\nPlease note: Character case in the command field variable names does matter.\r\n\r\nUpon successful completion of the command the server sends a response back to the client with information of the existing number of virtual hosts on the server:\r\n\r\n.. code:: xml\r\n\r\n   <iq from=\"vhost-man@existing.domain.com\"\r\n       type=\"result\"\r\n       to=\"cmd-sender-admin@existing.domain.com\"\r\n       id=\"aacba\">\r\n     <command xmlns=\"http://jabber.org/protocol/commands\"\r\n              status=\"completed\"\r\n              node=\"VHOSTS_UPDATE\">\r\n       <x xmlns=\"jabber:x:data\" type=\"result\">\r\n         <field type=\"fixed\" var=\"Note\">\r\n           <value>Current number of VHosts: 124</value>\r\n         </field>\r\n       </x>\r\n     </command>\r\n   </iq>\r\n\r\nRemoving a Virtual Domain From the Server\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nIn order to remove a virtual domain you have to send ``VHOSTS_REMOVE`` command to the server with the domain name.\r\n\r\nThe domain remove command is sent by the client:\r\n\r\n.. code:: xml\r\n\r\n   <iq type=\"set\"\r\n       to=\"vhost-man@existing.domain.com\"\r\n       id=\"aacba\">\r\n     <command xmlns=\"http://jabber.org/protocol/commands\"\r\n              node=\"VHOSTS_REMOVE\">\r\n       <x xmlns=\"jabber:x:data\" type=\"submit\">\r\n         <field type=\"text-single\"\r\n                var=\"VHost\">\r\n           <value>virt-nn.domain.com</value>\r\n         </field>\r\n       </x>\r\n     </command>\r\n   </iq>\r\n\r\nUpon successful completion of the command the server sends a response back to the client with information of the existing number of virtual hosts on the server:\r\n\r\n.. code:: bash\r\n\r\n   <iq from=\"vhost-man@existing.domain.com\"\r\n       type=\"result\"\r\n       to=\"cmd-sender-admin@existing.domain.com\"\r\n       id=\"aacba\">\r\n     <command xmlns=\"http://jabber.org/protocol/commands\"\r\n              status=\"completed\"\r\n              node=\"VHOSTS_REMOVE\">\r\n       <x xmlns=\"jabber:x:data\" type=\"result\">\r\n         <field type=\"fixed\" var=\"Note\">\r\n           <value>Current number of VHosts: 124</value>\r\n         </field>\r\n       </x>\r\n     </command>\r\n   </iq>\r\n\r\n.. _virtualComponents:\r\n\r\nVirtual Components for the Cluster Mode\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nLet’s assume you have a cluster installation and you want to include a component in your installation which doesn’t support the cluster mode yet. If you put it on all nodes as a separate instances they will work out of sync and overall functionality might be useless. If you put on one node only it will work correctly but it will be visible to users connected to this one node only.\r\n\r\nIdeally you would like to have a mechanism to install it on one node and put some redirections on other nodes to forward all packets for this component to a node where this component is working. Redirection on it’s own is not enough because the component must be visible in service discovery list and must be visible somehow to users connected to all nodes.\r\n\r\nThis is where the virtual components are handy. They are visible to users as a local normal component, they seem to be a real local component but in fact they just forward all requests/packets to a cluster node where the real component is working.\r\n\r\nVirtual component is a very lightweight ServerComponent implementation in Tigase server. It can pretend to be any kind of component and can redirect all packets to a given address. They can mimic native Tigase components as well as third-party components connected over external component protocol (XEP-0114).\r\n\r\nConfiguration is very simple and straightforward, in fact it is very similar to configuration of any Tigase component. You set a real component name as a name of the component and a virtual component class name to load. Let’s say we want to deploy MUC component this way. The MUC component is visible as ``muc.domain.oug`` in the installation. Thus the name of the component is: ``muc``\r\n\r\n.. code::\r\n\r\n   muc (class: tigase.cluster.VirtualComponent) {}\r\n\r\nThis is pretty much all you need to load a virtual component. A few other options are needed to point to correct destination addresses for packets forwarding and to set correct service discovery parameters:\r\n\r\n.. code::\r\n\r\n   }\r\n   muc (class: tigase.cluster.VirtualComponent) {\r\n       'disco-category' = 'conference'\r\n       'disco-features' = 'http://jabber.org/protocol/muc'\r\n       'disco-name' = 'Multi User Chat'\r\n       'disco-node' = ''\r\n       'disco-type' = 'text'\r\n       'redirect-to' = 'muc@cluster-node-with-real-muc.domain.our'\r\n   }\r\n\r\nThat’s it.\r\n\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Configuration/_Configuration.rst",
    "content": "Configuration\r\n=================\r\n\r\nWhen the user tries to setup the client for the first time he comes across 2 configuration files: ``tigase.conf`` and ``config.tdsl`` in the ``/etc`` folder. Here is a brief explanation what all those files are about and in other sections you can learn all the details needed to configure the server.\r\n\r\n1. :ref:`config.tdsl<dslConfig>` file is a simple text file with server parameters in form: **key** = **value**. When the XML configuration file is missing the Tigase server reads ``config.tdsl`` file and uses parameters found there as defaults for generation of the XML file. Therefore if you change the ``config.tdsl`` file you normally have to stop the server, remove the XML file and start the server again. All the settings from the ``config.tdsl`` are read and applied to the XML configuration. The properties file is easy to read and very safe to modify. At the moment this is the recommended way change the server configuration.\r\n\r\n2. :ref:`tigase.conf<manualconfig>` is the Tigase server startup configuration. It is actually not used by the server itself. It rather contains operating system settings and environment parameters to correctly run the `Java Virtual Machine <https://www.oracle.com/java/technologies/>`__. It is only useful on the unix-like systems with Bash shell. If you run the server on MS Windows systems ``tigase.bat`` and ``wrapper.conf`` files are used instead. The ``tigase.conf`` file is read and loaded by the ``scripts/tigase.sh`` shell script which also scans the operating system environment for Java VM and other tools needed.\r\n\r\n \r\n.. include:: DSL_configuration.inc\r\n.. include:: Startup_Files.inc\r\n.. include:: Settings_for_High_Load_Systems.inc\r\n.. include:: JVM_settings.inc\r\n.. include:: Session_Manager.inc  \r\n.. include:: Vhosts.inc\r\n.. include:: Logging.inc\r\n.. include:: Advanced_Options.inc\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc",
    "content": ".. _SchemaUtility:\r\n\r\nSchema Utility\r\n^^^^^^^^^^^^^^^\r\n\r\nWith the release of v8.0.0 calling the Tigase dbSchemaLoader utility now can be done using tasks instead of calling the specific method. Support for Derby, MySQL, PostgreSQL, MSSQL, and MongoDB is available.\r\n\r\nIn order to use this utility with any of the databases, you will need to first have the database environment up and running, and have established user credentials. You may use root or an account with administrator write privileges.\r\n\r\nOperation & Variables\r\n~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nOperation\r\n\r\nOperating the schema utility is quite easy! To use it run this command from the installation directory:\r\n\r\n.. code:: bash\r\n\r\n   ./scripts/tigase.sh [task] [params_file.conf] [options]\r\n\r\nOperations are now converted to tasks, of which there are now three: ``install-schema``, ``upgrade-schema``, and ``destroy-schema``.\r\n\r\n-  ``upgrade-schema``: Upgrade the schema of the database specified in your ``config.tdsl`` configuration file. (options are ignored for this option)\r\n\r\n-  ``install-schema``: Install a schema to database.\r\n\r\n-  ``destroy-schema``: Destroy database and schemas. **DANGEROUS**\r\n\r\nOptions\r\n\r\nUse the following options to customize. Options in bold are required, *{potential options are in brackets}*:\r\n\r\n-  ``--help`` Prints the help for the task.\r\n\r\n-  ``-I`` or ``--interactive`` - enables interactive mode which will prompt for parameters not defined.\r\n\r\n-  ``-T`` or ``--dbType`` - database type {derby, mongodb, mysql, postgresql, sqlserver}.\r\n\r\n-  ``-C`` or ``--components`` - Allows the specification of components for use when installing a schema.\r\n\r\nUsage\r\n~~~~~~~\r\n\r\nupgrade-schema\r\n\r\nThis task will locate any schema versions above your current one, and will install them to the database configured in the ``config.tdsl`` file.\r\n\r\n.. Note::\r\n\r\n   To use this utility, you must have Tigase XMPP server fully setup with a configured configuration file.\r\n\r\n.. code:: bash\r\n\r\n   ./scripts/tigase.sh upgrade-schema etc/tigase.conf\r\n\r\nWindows users will need to run the command using the following command:\r\n\r\n.. code::\r\n\r\n   java -cp \"jars/*\" tigase.db.util.SchemaManager \"upgrade-schema\" --config-file=etc/config.tdsl\r\n\r\n.. _install-schema:\r\n\r\ninstall-schema\r\n'''''''''''''''\r\n\r\nThis task will install a schema using the parameters provided.\r\n\r\n**If you are setting up a server manually, we HIGHLY recommend using this method**\r\n\r\n.. code:: bash\r\n\r\n   ./scripts/tigase.sh install-schema [Options]\r\n\r\nThis command will install tigase using a Derby database on one named ``tigasedb`` hosted on ``localhost``. The username and password editing the database is ``tigase_pass`` and ``root``. Note that ``-J`` explicitly adds the administrator, this is highly recommended with the ``-N`` passing the password.\r\n\r\nIf you are using a windows system, you need to call the program directly:\r\n\r\n.. code::\r\n\r\n   java -cp \"jars/*\" tigase.db.util.SchemaManager \"install-schema\" [options]\r\n\r\n\r\nOptions\r\n'''''''''\r\n\r\nOptions for schema installation are as follows, required options are in bold\r\n\r\n-  ``--help``, Outputs the help.\r\n\r\n-  ``-I``, ``--interactive`` - enables interactive mode, which will result in prompting for any missing parameters.\r\n\r\n-  ``-C``, ``--components=`` - list of enabled components identifiers (+/-), possible values: [``amp``, ``bosh``, ``c2s``, ``eventbus``, ``ext-disco``, ``http``, ``mdns``, ``message-archive``, ``monitor``, ``muc``, ``pubsub``, ``push``, ``s2s``, ``socks5``, ``test``, ``unified-archive``, ``upload``, ``ws2s``] (default: amp,bosh,c2s,eventbus,http,message-archive,monitor,muc,pubsub,s2s,ws2s). **This is required for certain components like socks5.**\r\n\r\n-  ``-T``, ``--dbType=`` - database server type, possible values are: [``derby``, ``mongodb``, ``mysql``, ``postgresql``, ``sqlserver``] (*required*)\r\n\r\n-  ``-D``, ``--dbName=`` - name of the database that will be created (by default it is ``tigasedb``). (*required*)\r\n\r\n-  ``-H``, ``--dbHostname=`` - address of the database instance (by default it is ``localhost``). (*required*)\r\n\r\n-  ``-U``, ``--dbUser=`` - name of the user that will be created specifically to access Tigase XMPP Server database (default is ``tigase_user``). (*required*)\r\n\r\n-  ``-P``, ``--dbPass=`` - password of the user that will be created specifically to access Tigase XMPP Server database (default is ``tigase_pass``). (*required*)\r\n\r\n-  ``-R``, ``--rootUser=`` - database root account username used to create user and database (default is ``root``). (*required*)\r\n\r\n-  ``-A``, ``--rootPass=`` - database root account password used to create user and database (default is ``root``). (*required*)\r\n\r\n-  ``-S``, ``--useSSL`` - enable SSL support for database connection (if the database supports it) (default is false).\r\n\r\n-  ``-F``, ``--file=`` - comma separated list of SQL files that will be processed.\r\n\r\n-  ``-Q``, ``--query=`` - custom queries to be executed, see :ref:`Query function<queryschema>` for details.\r\n\r\n-  ``-L``, ``--logLevel=`` - logger level used during loading process (default is ``CONFIG``).\r\n\r\n-  ``-J``, ``--adminJID=`` - comma separated list of administrator JID(s).\r\n\r\n-  ``-N``, ``--adminJIDpass=`` - password that will be used for the entered JID(s) - one password for all configured JIDs.\r\n\r\n-  ``--getURI=`` - generate database URI (default is ``false``).\r\n\r\n-  ``--ignoreMissingFiles=`` - force ignoring missing files errors (default is ``false``).\r\n\r\n.. _queryschema:\r\n\r\nQuery function\r\n\r\nShould you decide to customize your own functions, or have specific information you want to put into the database, you can use the -query function to perform a single query step.\r\n\r\n.. code::\r\n\r\n   ./scripts/tigase.sh install-schema -T mysql -D tigasedb -R root -A root -Q \"CREATE TABLE tigasedb.EXTRA_TABLE (id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, name VARCHAR(10) NOT NULL)\"\r\n\r\nOf course this would break the schema for tigasedb by adding an unexpected table, you will receive the following message:\r\n\r\n::\r\n\r\n   tigase.db.util.DBSchemaLoader       printInfo          WARNING       Database schema is invalid\r\n\r\nBut this is a demonstration how you may run a query through the database without the need to use another tool. Note that you will need to select the specific database for each query.\r\n\r\ndestroy-schema\r\n\r\n\r\nThis will destroy the database specified in the configuration file.\r\n\r\n.. Warning::\r\n\r\n    **THIS ACTION IS NOT REVERSIBLE**\r\n\r\n.. code::\r\n\r\n   ./scripts/tigase.sh destroy-schema etc/config.tdsl\r\n\r\nOnly use this if you wish to destroy a database and not have the information recoverable.\r\n\r\nWindows users will need to call the method directly:\r\n\r\n.. code::\r\n\r\n   java -cp \"jars/*\" tigase.db.util.SchemaManager \"destroy-schema\" etc/config.tdsl\r\n\r\n\r\nA note about MySQL\r\n\r\nIf you are using these commands, you may result in the following error:\r\n\r\n.. code:: bash\r\n\r\n   tigase.util.DBSchemaLoader       validateDBConnection    WARNING    Table 'performance_schema.session_variables' does not exist\r\n\r\nIf this occurs, you will need to upgrade your version of MySQL using the following command:\r\n\r\n.. code:: bash\r\n\r\n   mysql_upgrade -u root -p --force\r\n\r\nAfter entering the password and upgrading MySQL the schema error should no longer show when working with Tigase databases."
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Database_Management/Database_Preparation/Derby.inc",
    "content": ".. _Prepare-the-Derby-Database-for-the-Tigase-Server:\r\n\r\nPrepare the Derby Database for the Tigase Server\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThis guide describes how to prepare Derby database for connecting the Tigase server.\r\n\r\nBasic Setup\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\nPreparation of Derby database is quite simple, but the following assumptions are made\r\n\r\n-  ``DerbyDB`` - Derby database name\r\n\r\n-  ``database/`` directory contains all necessary schema files\r\n\r\n-  ``jars/`` and ``libs/`` directories contains Tigase and Derby binaries\r\n\r\nGeneral Approach\r\n\r\nFrom the main Tigase directory execute following commands (Linux and Windows accordingly)\r\n\r\n.. Note::\r\n\r\n   You must use these sql files on order FIRST!\r\n\r\n**Linux**\r\n\r\n.. code:: sh\r\n\r\n   java -Dij.protocol=jdbc:derby: -Dij.database=\"DerbyDB;create=true\" -cp libs/derby.jar:libs/derbytools.jar:jars/tigase-server.jar org.apache.derby.tools.ij database/derby-common-0.0.1.sql\r\n\r\n**Windows**\r\n\r\n.. code:: sh\r\n\r\n   java -Dij.protocol=jdbc:derby: -Dij.database=\"DerbyDB;create=true\" -cp libs\\derby.jar;libs\\derbytools.jar;jars\\tigase-server.jar org.apache.derby.tools.ij \"database\\derby-common-0.0.1.sql\"\r\n\r\nThis will create Derby database named DerbyDB in the main Tigase directory and load common version for common v0.1.\r\n\r\nYou will need to repeat this process again in for following order:\r\n\r\n.. code::\r\n\r\n   derby-common-0.0.1.sql\r\n   derby-common-0.0.2.sql\r\n   derby-server-7.0.0.sql\r\n   derby-server-7.1.0.sql\r\n   derby-server-8.0.0.sql\r\n   derby-muc-3.0.0.sql\r\n   derby-pubsub-3.1.0.sql\r\n   derby-pubsub-3.2.0.sql\r\n   derby-pubsub-4.0.0.sql\r\n   derby-http-api-2.0.0.sql\r\n\r\nOther components may require installation such as:\r\n\r\n.. code::\r\n\r\n   derby-socks5-2.0.0.sql\r\n   derby-push-1.0.0.sql\r\n   derby-unified-archive-2.0.0.sql\r\n\r\nConnecting Tigase to database\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nOnce the database is setup, configure the ``config.tdsl`` file in Tigase and add the following configuration:\r\n\r\n.. code::\r\n\r\n   dataSource {\r\n       default () {\r\n           uri = 'jdbc:derby:{location of derby database};'\r\n       }\r\n   }"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc",
    "content": ".. _Prepare-the-MS-SQL-Server-Database-for-the-Tigase-Server:\r\n\r\nPrepare the MS SQL Server Database for the Tigase Server\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThis guide describes how to prepare the MS SQL Server database for connecting the Tigase server to it.\r\n\r\nIt’s expected that a working installation of Microsoft SQL Server is present. The following guide will describe the necessary configurations required for using MS SQL Server with Tigase XMPP Server.\r\n\r\nPreparing MS SQL Server Instance\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nAfter installation of MS SQL Server an instance needs to be configure to handle incoming JDBC connections. For that purpose it’s required to open *SQL Server Configuration Manager*. In the left-hand side panel navigate to *SQL Server Configuration Manager*, then *SQL Server Network Configuration → Protocols for ${INSTANCE_NAME}*. After selecting instance in the right-hand side panel select TCP/IP and open *Properties*, in the Protocol tab in General section select Yes for Enabled property. In the IP Addresses tab select Yes for Active and Enabled properties of all IP Addresses that you want MS SQL Server to handle. Subsequently set the TCP Port property (if missing) to the default value - 1433. A restart of the instance may be required afterwards.\r\n\r\nConfiguration using MS SQL Server Management Studio\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nIn order to prepare the database you can use either a wizard or execute queries directly in the Query Editor. Firstly you need to establish a connection to the MS SQL Server instance. From Object Explorer select Connect and in the Connect to Server dialog enter administrator credentials.\r\n\r\nUsing Wizards\r\n\r\n-  Create Login\r\n\r\n   In the left-hand side panel select Security → Logins and from context menu choose New Login, in the Wizard window enter desired Login name, select SQL Server authentication and enter desired password subsequently confirming action with OK\r\n\r\n-  Create Database\r\n\r\n   From the Object Explorer select Databases node and from context menu select New Database; in the Wizard window enter desired Database name and enter previously created Login name into Owner field; subsequently confirming action with OK.\r\n\r\n\r\nUsing Queries\r\n\r\nFrom the Object Explorer root node’s context menu select New Query. In the Query windows execute following statements adjusting details to your liking:\r\n\r\n.. code:: sql\r\n\r\n   USE [master]\r\n   GO\r\n\r\n   CREATE DATABASE [tigasedb];\r\n   GO\r\n\r\n   CREATE LOGIN [tigase] WITH PASSWORD=N'tigase12', DEFAULT_DATABASE=[tigasedb]\r\n   GO\r\n\r\n   ALTER AUTHORIZATION ON DATABASE::tigasedb TO tigase;\r\n   GO\r\n\r\n\r\n\r\nImport Schema\r\n''''''''''''''\r\n\r\nFrom the File menu Select Open → File (or use Ctrl+O) and then open following files:\r\n\r\n.. code::\r\n\r\n   sqlserver-common-0.0.1.sql\r\n   sqlserver-common-0.0.2.sql\r\n   sqlserver-server-7.0.0.sql\r\n   sqlserver-server-7.1.0.sql\r\n   sqlserver-server-8.0.0.sql\r\n   sqlserver-muc-3.0.0.sql\r\n   sqlserver-pubsub-3.1.0.sql\r\n   sqlserver-pubsub-3.2.0.sql\r\n   sqlserver-pubsub-4.0.0.sql\r\n   sqlserver-http-api-2.0.0.sql\r\n\r\n.. Note::\r\n\r\n   These files must be done sequentially! They are not linked, and so may need to be done one at a time.\r\n\r\nOther components may require installation such as:\r\n\r\n.. code::\r\n\r\n   sqlserver-socks5-2.0.0.sql\r\n   sqlserver-push-1.0.0.sql\r\n   sqlserver-message-archiving-2.0.0.sql\r\n   sqlserver-unified-archive-2.0.0.sql\r\n\r\nSubsequently select created database from the list of Available Databases (Ctrl+U) available on the toolbar and execute each of the opened files in the order listed above.\r\n\r\nConfiguring from command line tool\r\n'''''''''''''''''''''''''''''''''''\r\n\r\nCreation of the database and import of schema can be done from command line as well. In order to do that, execute following commands from the directory where Tigase XMPP Server is installed otherwise paths to the schema need to be adjusted accordingly:\r\n\r\n.. code:: bash\r\n\r\n   sqlcmd -S %servername% -U %root_user% -P %root_pass% -Q \"CREATE DATABASE [%database%]\"\r\n   sqlcmd -S %servername% -U %root_user% -P %root_pass% -Q \"CREATE LOGIN [%user%] WITH PASSWORD=N'%password%', DEFAULT_DATABASE=[%database%]\"\r\n   sqlcmd -S %servername% -U %root_user% -P %root_pass% -d %database% -Q \"ALTER AUTHORIZATION ON DATABASE::%database% TO %user%;\"\r\n   sqlcmd -S %servername% -U %root_user% -P %root_pass% -d %database% -i database\\sqlserver-schema-7-1-schema.sql\r\n   sqlcmd -S %servername% -U %root_user% -P %root_pass% -d %database% -i database\\sqlserver-schema-7-1-sp.sql\r\n   sqlcmd -S %servername% -U %root_user% -P %root_pass% -d %database% -i database\\sqlserver-schema-7-1-props.sql\r\n   sqlcmd -S %servername% -U %root_user% -P %root_pass% -d %database% -i database\\sqlserver-pubsub-schema-3.2.0.sql\r\n\r\nAbove can be automatized with provided script %tigase-server%\\scripts\\db-create-sqlserver.cmd (note: it needs to be executed from main Tigase XMPP Server directory due to maintain correct paths):\r\n\r\n.. code:: sh\r\n\r\n   $ scripts\\db-create-sqlserver.cmd %database_servername% %database_name% %tigase_username% %tigase_password% %root_username% %root_password%\r\n\r\nIf no parameters are provided then the following defaults are used:\r\n\r\n.. code:: bash\r\n\r\n   %database_servername%=localhost\r\n   %database_name%=tigasedb\r\n   %tigase_username%=tigase\r\n   %tigase_password%=tigase12\r\n   %root_username%=root\r\n   %root_password%=root\r\n\r\nTigase configuration - config.tdsl\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nConfiguration of the MS SQL Server follows general database convention.\r\n\r\n.. code:: bash\r\n\r\n   dataSource {\r\n       default () {\r\n           uri = 'jdbc:sqlserver://db_hostname:port[;property=val]'\r\n       }\r\n   }\r\n\r\nwhere any number of additional parameters can (and should) consist of:\r\n\r\n-  ``databaseName`` - name of the database\r\n\r\n-  ``user`` - username configured to access database\r\n\r\n-  ``password`` - password for the above username\r\n\r\n-  ``schema`` - name of the database schema\r\n\r\n-  ``lastUpdateCount`` - 'false' value causes all update counts to be returned, including those returned by server triggers\r\n\r\nExample:\r\n\r\n.. code::\r\n\r\n   dataSource {\r\n       default () {\r\n           uri = 'jdbc:sqlserver://hostname:1433;databaseName=tigasedb;user=tigase;password=tigase12;schema=dbo;lastUpdateCount=false'\r\n       }\r\n   }\r\n\r\nJDBC: jTDS vs MS JDBC driver (obsolete)\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nPreviously Tigase XMPP Server was shipped with the jTDS open source driver however since 8.3.0 we switched to the FOSS driver provided by the Microsoft itself. Previous jdbc url will fallback to the Microsoft driver automatically.\r\n\r\n-  Microsoft driver:\r\n\r\n   .. code::\r\n\r\n      dataSource {\r\n          default () {\r\n              uri = 'jdbc:sqlserver://...'\r\n          }\r\n      }\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc",
    "content": ".. _PreparingTigaseforMongoDB:\r\n\r\nPreparing Tigase for MongoDB\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTigase now supports MongoDB for auth, settings, and storage repositories. If you wish to use MongoDB for Tigase, please use this guide to help you.\r\n\r\nDependencies\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nTo run Tigase MongoDB support library requires drivers for MongoDB for Java which can be downloaded from `here <https://github.com/mongodb/mongo-java-driver/releases>`__. This driver needs to be placed in ``jars/`` directory located in Tigase XMPP Server installation directory. If you are using a dist-max distribution, it is already included.\r\n\r\nConfiguration\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nNote that fresh installations of MongoDB do not come with users or databases installed. Once you have setup MongoDB you will need to create a user to be used with Tigase. To do this, bring up the mongo console by running mongo.exe in a cmd window for windows, or run mongo in linux. Once connected, enter then following:\r\n\r\n.. code:: bash\r\n\r\n   use admin\r\n   db.createUser( { user: \"tigase\",\r\n                    pwd: \"password\",\r\n                    customData: { employeeId: 12345 },\r\n                    roles: [ \"root\" ]\r\n                   }\r\n                 )\r\n\r\nBe sure to give this user a ``root`` role in order to properly write to the database. Once you receive a ``user successfully created`` message, you are ready to install tigase on MongoDB.\r\n\r\nConfiguration of user repository for Tigase XMPP Server\r\n\r\nTo configure Tigase XMPP Server to use MongoDB you need to set ``dataSource`` in etc/config.tdsl file to proper MongoDB URI pointing to which MongoDB database should be used (it will be created by MongoDB if it does not exist). ``userRepository`` property should not be set to let Tigase XMPP Server auto-detect proper implementation of ``UserRepository``. Tigase XMPP Server will create proper collections in MongoDB if they do not exist so no schema files are necessary.\r\n\r\nExample configuration of XMPP Server pointing to MongoDB database ``tigase_test`` in a local instance:\r\n\r\n.. code::\r\n\r\n   dataSource {\r\n       default () {\r\n           uri = 'mongodb://user:pass@localhost/tigase_test'\r\n       }\r\n   }\r\n   userRepository {\r\n       default () {}\r\n   }\r\n   authRepository {\r\n       default () {}\r\n   }\r\n\r\nIf Tigase Server is not able to detect a proper storage layer implementation, it can be forced to use one provided by Tigase using the following lines in ``etc/config.tdsl`` file:\r\n\r\n.. code::\r\n\r\n   userRepository {\r\n       default () {\r\n           cls = 'tigase.mongodb.MongoRepository'\r\n       }\r\n   }\r\n   authRepository {\r\n       default () {\r\n           cls = 'tigase.mongodb.MongoRepository'\r\n       }\r\n   }\r\n\r\nEvery component should be able to use proper implementation to support MongoDB using this URI. Also MongoDB URI can be passed as any URI in configuration of any component.\r\n\r\nConfiguration for MUC\r\n\r\nBy default, MUC component will use MongoDB to store data if Tigase is configured to use it as a default store. However, if you would like to use a different MongoDB database to store MUC message archive, you can do this by adding the following lines to ``etc/config.tdsl`` file:\r\n\r\n.. code::\r\n\r\n   muc {\r\n       'history-db-uri' = 'mongodb://user:pass@localhost/tigase_test'\r\n   }\r\n\r\nIf MUC components fails to detect and use a proper storage layer for MongoDB, you can force it to use one provided by Tigase by using the following line in the ``config.tdsl`` file:\r\n\r\n.. code::\r\n\r\n   muc {\r\n       'history-db' = 'tigase.mongodb.muc.MongoHistoryProvider'\r\n   }\r\n\r\n\r\nConfiguration for PubSub\r\n\r\nBy default, PubSub component will use MongoDB to store data if Tigase is configured to use it as a default store. However, if you would like to use a different MongoDB database to store PubSub component data, you can do this by adding the following lines to ``etc/config.tdsl`` file:\r\n\r\n.. code::\r\n\r\n   pubsub {\r\n       'pubsub-repo-url' = 'mongodb://user:pass@localhost/tigase_test'\r\n   }\r\n\r\nIf the PubSub components fails to detect and use a proper storage layer for MongoDB, you can force it to use one provided by Tigase by using the following line in the ``config.tdsl`` file:\r\n\r\n.. code::\r\n\r\n   pubsub {\r\n       'pubsub-repo-class' = 'tigase.mongodb.pubsub.PubSubDAOMongo'\r\n   }\r\n\r\n\r\nConfiguration for Message Archiving\r\n\r\nBy default, the Message Archiving component will use MongoDB to store data if Tigase is configured to use it as a default store. However, if you would like to use a different MongoDB database to store message archives, you can do this by adding the following lines to ``etc/config.tdsl`` file:\r\n\r\n.. code::\r\n\r\n   'message-archive' {\r\n       'archive-repo-uri' = 'mongodb://user:pass@localhost/tigase_test'\r\n   }\r\n\r\nIf Message Archiving component fails to detect and use a proper storage layer for MongoDB, you can force it to use one provided by Tigase by using the following line in the ``config.tdsl`` file:\r\n\r\n.. code::\r\n\r\n   'message-archive' {\r\n       'archive-repo-class' = 'tigase.mongodb.archive.MongoMessageArchiveRepository'\r\n   }\r\n\r\n\r\nSchema Description\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nThis description contains only basic description of schema and only basic part of it. More collections may be created if additional components of Tigase XMPP Server are loaded and configured to use MongoDB.\r\n\r\nTigase XMPP Server Schema\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nBasic schema for UserRespository and AuthRepository consists of two collections: . tig_users - contains list of users . tig_nodes - contains data related to users in tree-like way\r\n\r\n``tig_users`` collection contains the following fields:\r\n\r\n.. table:: Table 9. tig_users\r\n\r\n   +----------+--------------------------------------------------------------------+\r\n   | Name     | Description                                                        |\r\n   +==========+====================================================================+\r\n   | \\_id     | id of user which is SHA256 hash of users jid (raw byte array).     |\r\n   +----------+--------------------------------------------------------------------+\r\n   | user_id  | contains full user jid.                                            |\r\n   +----------+--------------------------------------------------------------------+\r\n   | domain   | domain to which user belongs for easier lookup of users by domain. |\r\n   +----------+--------------------------------------------------------------------+\r\n   | password | password of user (will be removed after upgrade to 8.0.0).         |\r\n   +----------+--------------------------------------------------------------------+\r\n\r\n``tig_nodes`` collection contains the following fields\r\n\r\n.. table:: Table 10. tig_nodes\r\n\r\n   +-------+--------------------------------------------------------------------------+\r\n   | Name  | Description                                                              |\r\n   +=======+==========================================================================+\r\n   | \\_id  | id of row auto-generated by MongoDB.                                     |\r\n   +-------+--------------------------------------------------------------------------+\r\n   | uid   | id of user which is SHA256 hash of users jid (raw byte array).           |\r\n   +-------+--------------------------------------------------------------------------+\r\n   | node  | full path of node in tree-like structure separated by / (may not exist). |\r\n   +-------+--------------------------------------------------------------------------+\r\n   | key   | key for which value for node is set.                                     |\r\n   +-------+--------------------------------------------------------------------------+\r\n   | value | value which is set for node key.                                         |\r\n   +-------+--------------------------------------------------------------------------+\r\n\r\nTigase XMPP Server also uses additional collections for storage of Offline Messages\r\n\r\n.. table:: Table 11. msg_history collection\r\n\r\n   +-----------+-----------------------------------------------------------------------------+\r\n   | Name      | Description                                                                 |\r\n   +===========+=============================================================================+\r\n   | from      | full user jid of message sender.                                            |\r\n   +-----------+-----------------------------------------------------------------------------+\r\n   | from_hash | SHA256 hash of message sender jid as raw byte array.                        |\r\n   +-----------+-----------------------------------------------------------------------------+\r\n   | to        | full users jid of message recipient.                                        |\r\n   +-----------+-----------------------------------------------------------------------------+\r\n   | to_hash   | SHA256 hash of message recipient full jid as raw byte array.                |\r\n   +-----------+-----------------------------------------------------------------------------+\r\n   | ts        | timestamp of message as date.                                               |\r\n   +-----------+-----------------------------------------------------------------------------+\r\n   | message   | serialized XML stanza containing message.                                   |\r\n   +-----------+-----------------------------------------------------------------------------+\r\n   | expire-at | timestamp of expiration of message (if message contains AMP expire-at set). |\r\n   +-----------+-----------------------------------------------------------------------------+\r\n\r\nDue to changes in authentication and credentials storage in AuthRepository, we moved ``password`` field from ``tig_users`` collection to a newly created collection called ``tig_user_credentials``.\r\n\r\nThis new collection has following fields:\r\n\r\n+----------------+----------------------------------------------------------------------------------+\r\n| Name           | Description                                                                      |\r\n+================+==================================================================================+\r\n| \\_id           | id of document automatically generated by MongoDB                                |\r\n+----------------+----------------------------------------------------------------------------------+\r\n| uid            | SHA256 hash of a user for which credentails are stored                           |\r\n+----------------+----------------------------------------------------------------------------------+\r\n| username       | username provided during authentication (or ``default``)                         |\r\n+----------------+----------------------------------------------------------------------------------+\r\n| account_status | name of an account state (copy of value stored in user document from`tig_users`) |\r\n+----------------+----------------------------------------------------------------------------------+\r\n\r\nAdditionally for each mechanism we store separate field in this object, so for:\r\n\r\n-  ``PLAIN`` we have ``PLAIN`` field with value for this mechanism\r\n\r\n-  ``SCRAM-SHA-1`` we have ``SCRAM-SHA-1`` field with value for this mechanism\r\n\r\n-  etc…​\r\n\r\nUpgrade is not done in one step, and rather will be done once a particular user will log in. During authentication if there is no data in ``tig_user_credentials``, Tigase XMPP Server will check if ``password`` field in ``tig_user`` exists. If it does, and it is filled credentials will be migrated to the new collection.\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc",
    "content": ".. _Prepare-the-MySQL-Database-for-the-Tigase-Server:\r\n\r\nPrepare the MySQL Database for the Tigase Server\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThis guide describes how to prepare MySQL database for connecting Tigase server.\r\n\r\nThe MySQL database can be prepared in many ways. Most Linux distributions contain tools which allow you to go through all steps from the shell command line. To make sure it works on all platforms in the same way, we will first show how to do it under MySQL command line client.\r\n\r\nConfiguring from MySQL command line tool\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nRun the MySQL command line client in either Linux or MS Windows environment and enter following instructions from the Tigase installation directory:\r\n\r\n.. code:: sql\r\n\r\n   mysql -u root -p\r\n\r\nOnce logged in, create the database for the Tigase server:\r\n\r\n.. code:: sql\r\n\r\n   mysql> create database tigasedb;\r\n\r\nAdd the ``tigase_user`` user and grant him access to the ``tigasedb`` database. Depending on how you plan to connect to the database (locally or over the network) use one of following commands or all if you are not sure:\r\n\r\n-  Grant access to tigase_user connecting from any network address.\r\n\r\n   .. code:: sql\r\n\r\n      mysql> GRANT ALL ON tigasedb.* TO tigase_user@'%'\r\n                  IDENTIFIED BY 'tigase_passwd';\r\n\r\n-  Grant access to tigase_user connecting from localhost.\r\n\r\n   .. code:: sql\r\n\r\n      mysql> GRANT ALL ON tigasedb.* TO tigase_user@'localhost'\r\n                  IDENTIFIED BY 'tigase_passwd';\r\n\r\n-  Grant access to tigase_user connecting from local machine only.\r\n\r\n   .. code:: sql\r\n\r\n      mysql> GRANT ALL ON tigasedb.* TO tigase_user\r\n                  IDENTIFIED BY 'tigase_passwd';\r\n\r\nAnd now you can update user permission changes in the database:\r\n\r\n.. code:: sql\r\n\r\n   mysql> FLUSH PRIVILEGES;\r\n\r\n.. Important::\r\n\r\n   It’s essential to enable `log_bin_trust_function_creators <https://dev.mysql.com/doc/refman/8.0/en/replication-options-binary-log.html#sysvar_log_bin_trust_function_creators>`__ option in MySQL server, for example by running:\r\n\r\n   .. code:: sql\r\n\r\n      mysql> SET GLOBAL log_bin_trust_function_creators = 1;\r\n\r\nInstalling Schemas\r\n\r\nStarting with v8.0.0 the Schemas are no longer linked, and will need to manually be installed in the following order.\r\n\r\nSwitch to the database you have created:\r\n\r\n.. code:: sql\r\n\r\n   mysql> use tigasedb;\r\n\r\n..  Note::\r\n\r\n   We are assuming you run the mysql client in Linux from the Tigase installation directory, so all file links will be relative.\r\n\r\nNext install the schema files:\r\n\r\n.. code:: sql\r\n\r\n   mysql> source database/mysql-common-0.0.1.sql;\r\n\r\nYou will need to repeat this process for the following files:\r\n\r\n.. code::\r\n\r\n   mysql-common-0.0.1.sql\r\n   mysql-common-0.0.2.sql\r\n   mysql-server-7.0.0.sql\r\n   mysql-server-7.1.0.sql\r\n   mysql-server-8.0.0.sql\r\n   mysql-muc-3.0.0.sql\r\n   mysql-pubsub-3.1.0.sql\r\n   mysql-pubsub-3.2.0.sql\r\n   mysql-pubsub-4.0.0.sql\r\n   mysql-http-api-2.0.0.sql\r\n\r\nOther components may require installation such as:\r\n\r\n.. code::\r\n\r\n   mysql-socks5-2.0.0.sql\r\n   mysql-push-1.0.0.sql\r\n   mysql-message-archiving-2.0.0.sql\r\n   mysql-unified-archive-2.0.0.sql\r\n\r\n\r\nWindows instructions:\r\n\r\nOn Windows you have probably to enter the full path, assuming Tigase is installed in C:\\Program Files\\Tigase:\r\n\r\n.. code:: sql\r\n\r\n   mysql> source c:/Program Files/Tigase/database/mysql-common-0.0.1.sql;\r\n   mysql> source c:/Program Files/Tigase/database/mysql-common-0.0.2.sql;\r\n   mysql> source c:/Program Files/Tigase/database/mysql-server-7.0.0.sql;\r\n   and so on...\r\n\r\n\r\nConfiguring From the Linux Shell Command Line\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nFollow steps below to prepare the MySQL database:\r\n\r\nCreate the database space for the Tigase server:\r\n\r\n.. code:: sql\r\n\r\n   mysqladmin -p create tigasedb\r\n\r\nAdd the ``tigase_user`` user and grant access to the tigasedb database. Depending on how you plan to connect to the database (locally or over the network) use one of following commands or all if you are not sure:\r\n\r\nSelective access configuration\r\n\r\nGrant access to tigase_user connecting from any network address.\r\n\r\n.. code:: sql\r\n\r\n   echo \"GRANT ALL ON tigasedb.* TO tigase_user@'%' \\\r\n               IDENTIFIED BY 'tigase_passwd'; \\\r\n               FLUSH PRIVILEGES;\" | mysql -u root -pdbpass mysql\r\n\r\n\r\nGrant access to tigase_user connecting from localhost.\r\n\r\n.. code:: sql\r\n\r\n   echo \"GRANT ALL ON tigasedb.* TO tigase_user@'localhost' \\\r\n               IDENTIFIED BY 'tigase_passwd'; \\\r\n               FLUSH PRIVILEGES;\" | mysql -u root -pdbpass mysql\r\n\r\n\r\nGrant access to tigase_user connecting from local machine only.\r\n\r\n.. code:: sql\r\n\r\n   echo \"GRANT ALL ON tigasedb.* TO tigase_user \\\r\n               IDENTIFIED BY 'tigase_passwd'; \\\r\n               FLUSH PRIVILEGES;\" | mysql -u root -pdbpass mysql\r\n\r\n\r\nSchema Installation\r\n\r\nLoad the proper mysql schemas into the database.\r\n\r\n.. code:: sql\r\n\r\n   mysql -u dbuser -p tigasedb < mysql-common-0.0.1.sql\r\n   mysql -u dbuser -p tigasedb < mysql-common-0.0.2.sql\r\n   etc..\r\n\r\nYou will need to repeat this process for the following files:\r\n\r\n.. code::\r\n\r\n   mysql-common-0.0.1.sql\r\n   mysql-common-0.0.2.sql\r\n   mysql-server-7.0.0.sql\r\n   mysql-server-7.1.0.sql\r\n   mysql-server-8.0.0.sql\r\n   mysql-muc-3.0.0.sql\r\n   mysql-pubsub-3.1.0.sql\r\n   mysql-pubsub-3.2.0.sql\r\n   mysql-pubsub-4.0.0.sql\r\n   mysql-http-api-2.0.0.sql\r\n\r\nOther components may require installation such as:\r\n\r\n.. code::\r\n\r\n   mysql-socks5-2.0.0.sql\r\n   mysql-push-1.0.0.sql\r\n   mysql-message-archiving-2.0.0.sql\r\n   mysql-unified-archive-2.0.0.sql\r\n\r\n\r\nConfiguring MySQL for UTF-8 Support\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nIn my.conf put following lines:\r\n\r\n.. code:: bash\r\n\r\n   [mysql]\r\n   default-character-SET=utf8\r\n\r\n   [client]\r\n   default-character-SET=utf8\r\n\r\n   [mysqld]\r\n   init_connect='SET collation_connection = utf8_general_ci; SET NAMES utf8;'\r\n   character-set-server=utf8\r\n   default-character-SET=utf8\r\n   collation-server=utf8_general_ci\r\n   skip-character-set-client-handshake\r\n\r\nThen connect to the database from the command line shell check settings:\r\n\r\n.. code:: sql\r\n\r\n   SHOW VARIABLES LIKE 'character_set_database';\r\n   SHOW VARIABLES LIKE 'character_set_client';\r\n\r\nIf any of these shows something else then 'utf8' then you need to fix it using the command:\r\n\r\n.. code:: sql\r\n\r\n   ALTER DATABASE tigasedb DEFAULT CHARACTER SET utf8;\r\n\r\nYou can now also test your database installation if it accepts UTF-8 data. The easiest way to ensure this is to just to create an account with UTF-8 characters:\r\n\r\n.. code:: sql\r\n\r\n   call TigAddUserPlainPw('żółw@some.domain.com', 'żółw');\r\n\r\nAnd then check that the account has been created:\r\n\r\n.. code:: sql\r\n\r\n   SELECT * FROM tig_users WHERE user_id = 'żółw@some.domain.com';\r\n\r\nIf the last command gives you no results it means there is still something wrong with your settings. You might also want to check your shell settings to make sure your command line shell supports UTF-8 characters and passes them correctly to MySQL:\r\n\r\n.. code:: sh\r\n\r\n   export LANG=en_US.UTF-8\r\n   export LOCALE=UTF-8\r\n   export LESSCHARSET='utf-8'\r\n\r\nIt seems that MySQL 5.0.x also needs extra parameters in the connection string: '&useUnicode=true&characterEncoding=UTF-8' while MySQL 5.1.x seems to not need it but it doesn’t hurt to have it for both versions. You have to edit ``etc/config.tdsl`` file and append this to the database connection string.\r\n\r\nFor MySQL 5.1.x, however, you need to also update code for all database stored procedures and functions used by the Tigase. They are updated for Tigase version 4.4.x and up, however if you use an older version of the Tigase server, you can reload stored procedures using the file from SVN.\r\n\r\nOther MySQL Settings Worth Considering\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nThere are a number of other useful options, especially for performance improvements. Please note, you will have to review them as some of them may impact data reliability and are useful for performance or load tests installations only.\r\n\r\n.. code:: bash\r\n\r\n   # InnoDB seems to be a better choice\r\n   # so lets make it a default DB engine\r\n   default-storage-engine = innodb\r\n\r\nSome the general MySQL settings which mainly affect performance:\r\n\r\n.. code:: bash\r\n\r\n   key_buffer = 64M\r\n   max_allowed_packet = 32M\r\n   sort_buffer_size = 64M\r\n   net_buffer_length = 64K\r\n   read_buffer_size = 16M\r\n   read_rnd_buffer_size = 16M\r\n   thread_stack = 192K\r\n   thread_cache_size = 8\r\n   query_cache_limit = 10M\r\n   query_cache_size = 64M\r\n\r\nInnoDB specific settings:\r\n\r\n.. code:: bash\r\n\r\n   # Keep data in a separate file for each table\r\n   innodb_file_per_table = 1\r\n   # Allocate memory for data buffers\r\n   innodb_buffer_pool_size = 1000M\r\n   innodb_additional_mem_pool_size = 100M\r\n   # A location of the MySQL database\r\n   innodb_data_home_dir = /home/databases/mysql/\r\n   innodb_log_group_home_dir = /home/databases/mysql/\r\n   # The main thing here is the 'autoextend' property\r\n   # without it your data file may reach maximum size and\r\n   # no more records can be added to the table.\r\n   innodb_data_file_path = ibdata1:10M:autoextend\r\n   innodb_log_file_size = 10M\r\n   innodb_log_buffer_size = 32M\r\n   # Some other performance affecting settings\r\n   innodb_flush_log_at_trx_commit = 2\r\n   innodb_lock_wait_timeout = 50\r\n   innodb_thread_concurrency = 16\r\n\r\nThese settings may not be fully optimized for your system, and have been only tested on our systems. If you have found better settings for your systems, feel free to `let us know <http://tigase.net/contact>`__.\r\n\r\n\r\nSupport for emoji and other icons\r\n\r\nTigase Database Schema can support emojis and other icons, however by using UTF-8 in ``mysqld`` settings will not allow this. To employ settings to support emojis and other icons, we recommend you use the following in your MySQL configuration file:\r\n\r\n.. code::\r\n\r\n   [mysqld]\r\n   character-set-server = utf8mb4\r\n   collation-server = utf8mb4_bin\r\n   character-set-client-handshake = FALSE\r\n\r\nDoing this, Tigase XMPP Server Database will still use ``utf8`` character set, with ``utf8_general_ci`` as collation, and only fields which require support for emojis will be converted to ``utf8mb4``.\r\n\r\n.. Note::\r\n\r\n   If for some reason, with above settings applied to your MySQL instance, you still receive :literal:`java.sql.SQLException: Incorrect string value: ` you should add to your database URI passed in Tigase XMPP Server following configuration `&useUnicode=true&characterEncoding=UTF-8`. If even this fails too, then you may try adding ``&connectionCollation=utf8mb4_bin`` as a last resort. This changes situation from previous versions that shipped older MySQL JDBC connector.\r\n\r\n.. Note::\r\n\r\n   Tigase XMPP Server databases should be created with ``utf8_general_ci`` collation as it will work properly and is fastest from ``utf8mb4_general_ci`` collations supported by MySQL"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc",
    "content": ".. _Prepare-the-PostgreSQL-Database-for-the-Tigase-Server:\r\n\r\nPrepare the PostgreSQL Database for the Tigase Server\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nThis guide describes how to prepare PostgreSQL database for connecting to Tigase server.\r\n\r\nThe PostgreSQL database can be prepared in many ways. Below are presented two possible ways. The following assumptions apply to both methods:\r\n\r\n-  ``admin_db_user`` - database user with admin rights\r\n\r\n-  ``tigase_user`` - database user for Tigase\r\n\r\n-  ``tigasedb`` - database for Tigase\r\n\r\nConfiguring from PostgreSQL Command Line Tool\r\n''''''''''''''''''''''''''''''''''''''''''''''\r\n\r\nRun the PostgreSQL command line client and enter following instructions:\r\n\r\nAdd the ``tigase_user``:\r\n\r\n.. code:: sql\r\n\r\n   psql=# create role tigase_user with login password 'tigase123';\r\n\r\nNext, Create the database for the Tigase server with ``tigase_user`` as owner of database:\r\n\r\n.. code:: sql\r\n\r\n   psql=# create database tigasedb owner tigase_user;\r\n\r\nSchema Installation\r\n\r\nLoad database schema to initialize the Tigase server from the file that corresponds to the version of Tigase you want to use. First you need to switch to ``tigasedb``.\r\n\r\n.. code::\r\n\r\n   psql=# \\c tigasedb\r\n\r\nBegin by applying the basic Schema\r\n\r\n.. code::\r\n\r\n   psql=# \\i database/postgresql-common-0.0.1.sql\r\n\r\nContinue by adding the schema files listed below:\r\n\r\n.. code::\r\n\r\n   postgresql-common-0.0.1.sql\r\n   postgresql-common-0.0.2.sql\r\n   postgresql-server-7.0.0.sql\r\n   postgresql-server-7.1.0.sql\r\n   postgresql-server-8.0.0.sql\r\n   postgresql-muc-3.0.0.sql\r\n   postgresql-pubsub-3.1.0.sql\r\n   postgresql-pubsub-3.2.0.sql\r\n   postgresql-pubsub-4.0.0.sql\r\n   postgresql-http-api-2.0.0.sql\r\n\r\nOther components may require installation such as:\r\n\r\n.. code::\r\n\r\n   postgresql-socks5-2.0.0.sql\r\n   postgresql-push-1.0.0.sql\r\n   postgresql-message-archiving-2.0.0.sql\r\n   postgresql-unified-archive-2.0.0.sql\r\n\r\nConfiguring From the Linux Shell Command Line\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nFollow steps below to prepare the PostgreSQL database:\r\n\r\nFirst, add the ``tigase_user``:\r\n\r\n.. code:: sql\r\n\r\n   createuser -U admin_db_user -W -D -R -S -P tigase_user\r\n\r\nYou will be asked for credentials for admin_db_user and password for new database user.\r\n\r\nCreate the database for the Tigase server with tigase_user as owner of database:\r\n\r\n.. code:: sql\r\n\r\n   createdb -U admin_db_user -W -O tigase_user tigasedb\r\n\r\nDatabase Schema Installation\r\n\r\nLoad database schema to initialize the Tigase server\r\n\r\n.. code:: sql\r\n\r\n   psql -q -U tigase_user -W tigasedb -f database/postgresql-common-0.0.1.sql\r\n   psql -q -U tigase_user -W tigasedb -f database/postgresql-common-0.0.2.sql\r\n   etc..\r\n\r\nContinue by adding the schema files listed below:\r\n\r\n.. code::\r\n\r\n   postgresql-common-0.0.1.sql\r\n   postgresql-common-0.0.2.sql\r\n   postgresql-server-7.0.0.sql\r\n   postgresql-server-7.1.0.sql\r\n   postgresql-server-8.0.0.sql\r\n   postgresql-muc-3.0.0.sql\r\n   postgresql-pubsub-3.1.0.sql\r\n   postgresql-pubsub-3.2.0.sql\r\n   postgresql-pubsub-4.0.0.sql\r\n   postgresql-http-api-2.0.0.sql\r\n\r\nOther components may require installation such as:\r\n\r\n.. code::\r\n\r\n   postgresql-socks5-2.0.0.sql\r\n   postgresql-push-1.0.0.sql\r\n   postgresql-message-archiving-2.0.0.sql\r\n   postgresql-unified-archive-2.0.0.sql\r\n\r\n.. Note::\r\n\r\n   The above commands should be executed from the main Tigase directory. The initialization schema file should be also available locally in database/ directory of your Tigase installation."
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Database_Management/Database_Preparation.inc",
    "content": "Database Preparation\r\n-----------------------\r\n\r\nTigase uses generally the same database schema and the same set of stored procedures and functions on every database. However, the schema creation scripts and code for stored procedures is different for each database. Therefore the manual process to prepare database is different for each database system.\r\n\r\nStarting with v8.0.0, most of the database tasks have been automated and can be called using simple text, or using interactive question and answer style. We **DO NOT RECOMMEND** going through manual operation, however we have kept manual activation of different databases to the Appendix. If you are interested in how we manage and update our database schemas, you may visit the :ref:`Schema files maintenance<Schemafilesmaintenance>` section of our Redmine installation for more detailed information.\r\n\r\n-  :ref:`The DBSchemaLoader Utility<SchemaUtility>`\r\n\r\n-  :ref:`Hashed User Passwords in Database<HashedUserPasswordsinDatabase>`\r\n\r\n-  :ref:`Support for MongoDB<PreparingTigaseforMongoDB>`\r\n\r\nAppendix entries\r\n\r\n-  :ref:`Manual installtion for MySQL<Prepare-the-MySQL-Database-for-the-Tigase-Server>`\r\n\r\n-  :ref:`Manual installtion for Derby<Prepare-the-Derby-Database-for-the-Tigase-Server>`\r\n\r\n-  :ref:`Manual installtion for SQLServer<Prepare-the-MS-SQL-Server-Database-for-the-Tigase-Server>`\r\n\r\n-  :ref:`Manual installtion for PostGRESQL<Prepare-the-PostgreSQL-Database-for-the-Tigase-Server>`\r\n\r\n.. include:: Database_Preparation/DbSchemaLoader.inc\r\n\r\n.. include:: Database_Preparation/MySQL.inc\r\n\r\n.. include:: Database_Preparation/Derby.inc\r\n\r\n.. include:: Database_Preparation/MSSQL.inc\r\n\r\n.. include:: Database_Preparation/PostGRE.inc\r\n\r\n.. include:: Database_Preparation/MongoDB.inc"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Database_Management/Existing_Databases.inc",
    "content": "Importing Existing Data\r\n--------------------------\r\n\r\nInformation about importing user data from other databases.\r\n\r\nConnecting the Tigase Server to MySQL Database\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nPlease before continuing reading of this manual have a look at the :ref:`initial MySQL database setup<Prepare-the-MySQL-Database-for-the-Tigase-Server>`. It will help you with database preparation for connecting with Tigase server.\r\n\r\nThis guide describes MySQL database connection parameters.\r\n\r\nThis guide is actually very short as there are example configuration files which can be used and customized for your environment.\r\n\r\n.. code::\r\n\r\n   dataSource {\r\n       default () {\r\n           uri = 'jdbc:mysql://localhost/tigasedb?user=tigase_user&password=mypass'\r\n       }\r\n   }\r\n   userRepository {\r\n       default () {}\r\n   }\r\n   authRepository {\r\n       default () {}\r\n   }\r\n\r\nThis is the basic setup for setting up an SQL repository for Tigase. dataSource contains the uri for ``default`` which is the mysql database. MySQL connector requires connection string in the following format: ``jdbc:mysql://[hostname]/[database name]?user=[user name]&password=[user password]``\r\n\r\nEdit the ``config.tdsl`` file for your environment.\r\n\r\nStart the server using following command:\r\n\r\n.. code:: sh\r\n\r\n   ./scripts/tigase.sh start etc/tigase.conf\r\n\r\n\r\nPostgreSQL Database Use\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThis guide describes how to configure Tigase server to use `PostgreSQL <http://www.postgresql.org/>`__ database as a user repository.\r\n\r\nIf you used an XML based user repository before you can copy all user data to PostgreSQL database using repository management tool. All steps are described below.\r\n\r\nPostgreSQL Database Preparation\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nCreate new database user account which will be used to connect to your database:\r\n\r\n.. code:: sh\r\n\r\n   # createuser\r\n   Enter name of user to add: tigase\r\n   Shall the new user be allowed to create databases? (y/n) y\r\n   Shall the new user be allowed to create more new users? (y/n) y\r\n\r\nNow using new database user account create database for your service:\r\n\r\n.. code:: sh\r\n\r\n   # createdb -U tigase tigasedb\r\n   CREATE DATABASE\r\n\r\nNow you can load the database schema:\r\n\r\n.. code:: sh\r\n\r\n   # psql -U tigase -d tigasedb -f postgresql-schema.sql\r\n\r\nNow the database is ready for Tigase server to use.\r\n\r\nServer Configuration\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nServer configuration is almost identical to MySQL database setup. The only difference is the connection string which usually looks like:\r\n\r\n.. code::\r\n\r\n   dataSource {\r\n       default () {\r\n           uri = 'postgresql://localhost/tigasdb?user=tigase'\r\n       }\r\n   }\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc",
    "content": ".. _HashedUserPasswordsinDatabase:\r\n\r\nHashed User Passwords in Database\r\n--------------------------------------\r\n\r\n.. Warning::\r\n\r\n   This feature is still available, but passwords are stored encrypted by default since v8.0.0. We do not recommend using these settings.\r\n\r\nBy default, user passwords are stored in plain-text in the Tigase’s database. However, there is an easy way to have them encoded in either one of already supported ways or to even add a new encoding algorithm on your own.\r\n\r\nStoring passwords in hashed format in the database makes it possible to avoid using a plain-text password authentication mechanism. You cannot have hashed passwords in the database and non-plain-text password authentication. On the other hand, the connection between the server and the client is almost always secured by SSL/TLS so the plain-text password authentication method is perhaps less of a problem than storing plain-text passwords in the database.\r\n\r\nNevertheless, it is simple enough to adjust this in Tigase’s database.\r\n\r\nShortcut\r\n^^^^^^^^^^^^\r\n\r\nConnect to your database from a command line and execute following statement for MySQL database:\r\n\r\n.. code:: sql\r\n\r\n   call TigPutDBProperty('password-encoding', 'encoding-mode');\r\n\r\nWhere encoding mode is one of the following:\r\n\r\n-  ``MD5-PASSWORD`` the database stores MD5 hash code from the user’s password.\r\n\r\n-  ``MD5-USERID-PASSWORD`` the database stores MD5 hash code from concatenated user’s bare JID and password.\r\n\r\n-  ``MD5-USERNAME-PASSWORD`` the database stores MD5 hash code from concatenated user’s name (localpart) and password.\r\n\r\nFor example:\r\n\r\n.. code:: sql\r\n\r\n   call TigPutDBProperty('password-encoding', 'MD5-PASSWORD');\r\n\r\nFull Route\r\n^^^^^^^^^^^^\r\n\r\nThe way passwords are stored in the DB is controlled by Tigase database schema property. Properties in the database schema can be set by a stored procedure called: ``TigPutDBProperty(key, value)``. Properties from the DB schema can be retrieved using another stored function called: ``TigGetDBProperty(key)``.\r\n\r\nThe simplest way to call them is via command-line interface to the database.\r\n\r\nFor the purpose of this guide let’s say we have a MySQL database and a test account: ``test@example.com`` with password ``test77``.\r\n\r\nBy default, most of DB actions for Tigase, are performed using stored procedures including user authentication. So, the first thing to do is to make sure the stored procedures are working correctly.\r\n\r\nCreate a Test User Account\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nTo add a new user account we use a stored procedure: ``TigAddUserPlainPw(bareJid, password)``. As you can see there is this strange appendix to the procedure name: ``PlainPw``. This procedure accepts plain passwords regardless how it is stored in the database. So it is safe and easy to use either for plain-text passwords or hashed in the DB. There are also versions of procedures without this appendix but they are sensitive on the data format and always have to pass password in the exact format it is stored in the database.\r\n\r\nSo, let’s add a new user account:\r\n\r\n.. code:: sql\r\n\r\n   call TigAddUserPlainPw('test@example.com', 'test77');\r\n\r\nIf the result was 'Query OK', then it means the user account has been successfully created.\r\n\r\nTest User Authentication\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nWe can now test user authentication:\r\n\r\n.. code:: sql\r\n\r\n   call TigUserLoginPlainPw('test@example.com', 'test77');\r\n\r\nIf authentication was successful the result looks like this:\r\n\r\n.. code:: sql\r\n\r\n   +--------------------+\r\n   | user_id            |\r\n   +--------------------+\r\n   | 'test@example.com' |\r\n   +--------------------+\r\n   1 row in set (0.01 sec)\r\n\r\n   Query OK, 0 rows affected (0.01 sec)\r\n\r\nIf authentication was unsuccessful, the result looks like this:\r\n\r\n.. code:: sql\r\n\r\n   +---------+\r\n   | user_id |\r\n   +---------+\r\n   |    NULL |\r\n   +---------+\r\n   1 row in set (0.01 sec)\r\n\r\n   Query OK, 0 rows affected (0.01 sec)\r\n\r\nPassword Encoding Check\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\n``TigGetDBProperty`` is a function, not a procedure in MySQL database so we have to use select to call it:\r\n\r\n.. code:: sql\r\n\r\n   select TigGetDBProperty('password-encoding');\r\n\r\nMost likely output is this:\r\n\r\n.. code:: sql\r\n\r\n   +---------------------------------------+\r\n   | TigGetDBProperty('password-encoding') |\r\n   +---------------------------------------+\r\n   | NULL                                  |\r\n   +---------------------------------------+\r\n   1 row in set, 1 warning (0.00 sec)\r\n\r\nWhich means a default password encoding is used, in plain-text and thus no encoding. And we can actually check this in the database directly:\r\n\r\n.. code:: sql\r\n\r\n   select uid, user_id, user_pw from tig_users where user_id = 'test@example.com';\r\n\r\nAnd expected result with plain-text password format would be:\r\n\r\n.. code:: sql\r\n\r\n   +-----+--------------------+---------+\r\n   | uid | user_id            | user_pw |\r\n   +-----+--------------------+---------+\r\n   |  41 | 'test@example.com' | test77  |\r\n   +-----+--------------------+---------+\r\n   1 row in set (0.00 sec)\r\n\r\nPassword Encoding Change\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nNow let’s set password encoding to MD5 hash:\r\n\r\n.. code:: sql\r\n\r\n   call TigPutDBProperty('password-encoding', 'MD5-PASSWORD');\r\n\r\n'Query OK', means the password encoding has been successfully changed. Of course we changed the property only. All the existing passwords in the database are still in plain-text format. Therefore we expect that attempt to authenticate the user would fail:\r\n\r\n.. code:: sql\r\n\r\n   call TigUserLoginPlainPw('test@example.com', 'test777');\r\n   +---------+\r\n   | user_id |\r\n   +---------+\r\n   |    NULL |\r\n   +---------+\r\n   1 row in set (0.00 sec)\r\n\r\n   Query OK, 0 rows affected (0.00 sec)\r\n\r\nWe can fix this by updating the user’s password in the database:\r\n\r\n.. code:: sql\r\n\r\n   call TigUpdatePasswordPlainPw('test@example.com', 'test777');\r\n   Query OK, 1 row affected (0.01 sec)\r\n\r\n   mysql> call TigUserLoginPlainPw('test@example.com', 'test777');\r\n   +--------------------+\r\n   | user_id            |\r\n   +--------------------+\r\n   | 'test@example.com' |\r\n   +--------------------+\r\n   1 row in set (0.00 sec)\r\n\r\n   Query OK, 0 rows affected (0.00 sec)\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Database_Management/Importing_Exporting_User_Data.inc",
    "content": "Importing & Exporting User Data\r\n--------------------------\r\n\r\nImporting User Data from compatible repository\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nYou can easily copy data between Tigase compatible repositories that is repositories for which there is a database connector. However, it is not that easy to import data from an external source. Therefore a simple data import functionality has been added to repository utilities package.\r\n\r\nYou can access repository utilities through command ``./bin/repo.sh`` or ``./scripts/repo.sh`` depending on whether you use a binary package or source distribution.\r\n\r\n``-h`` parameter gives you a list of all possible parameters:\r\n\r\n.. code:: sh\r\n\r\n   ./scripts/repo.sh -h\r\n\r\n   Parameters:\r\n    -h          this help message\r\n    -sc class   source repository class name\r\n    -su uri     source repository init string\r\n    -dc class   destination repository class name\r\n    -du uri     destination repository init string\r\n    -dt string  data content to set/remove in repository\r\n    -u user     user ID, if given all operations are only for that ID\r\n                if you want to add user to AuthRepository parameter must\r\n                in form: \"user:password\"\r\n    -st         perform simple test on repository\r\n    -at         simple test for adding and removing user\r\n    -cp         copy content from source to destination repository\r\n    -pr         print content of the repository\r\n    -n          data content string is a node string\r\n    -kv         data content string is node/key=value string\r\n    -add        add data content to repository\r\n    -del        delete data content from repository\r\n    ------------\r\n    -roster     check the user roster\r\n    -aeg [true|false]  Allow empty group list for the contact\r\n    -import file  import user data from the file of following format:\r\n            user_jid, password, roser_jid, roster_nick, subscription, group\r\n\r\n\r\n\r\n   Note! If you put UserAuthRepository implementation as a class name\r\n         some operation are not allowed and will be silently skipped.\r\n         Have a look at UserAuthRepository to see what operations are\r\n         possible or what operation does make sense.\r\n         Alternatively look for admin tools guide on web site.\r\n\r\nThe most critical parameters are the source repository class name and the initialization string. Therefore there are a few example preset parameters which you can use and adjust for your system. If you look inside the ``repo.sh`` script you can find at the end of the script following lines:\r\n\r\n.. code:: sh\r\n\r\n   XML_REP=\"-sc tigase.db.xml.XMLRepository -su ../testsuite/user-repository.xml_200k_backup\"\r\n   MYSQL_REP=\"-sc tigase.db.jdbc.JDBCRepository -su jdbc:mysql://localhost/tigase?user=root&password=mypass\"\r\n   PGSQL_REP=\"-sc tigase.db.jdbc.JDBCRepository -su jdbc:postgresql://localhost/tigase?user=tigase\"\r\n\r\n   java $D -cp $CP tigase.util.RepositoryUtils $MYSQL_REP $*\r\n\r\nYou can see that the source repository has been set to MySQL database with ``tigase`` as the database name, ``root`` the database user and ``mypass`` the user password.\r\n\r\nYou can adjust these settings for your system.\r\n\r\nNow to import data to your repository simply execute the command:\r\n\r\n.. code:: sh\r\n\r\n   ./bin/repo.sh -import import-file.txt\r\n\r\n*Note, the import function is available from* **b895**\r\n\r\nThe format of the import file is very simple. This is a flat file with comma separated values:\r\n\r\n.. code:: bash\r\n\r\n   jid,password,roster_jid,roster_nick,subscriptio,group\r\n\r\nTo create such a file from MySQL database you will have to execute a command like this one:\r\n\r\n.. code:: sql\r\n\r\n   SELECT a, b, c, d INTO OUTFILE 'import-file.txt'\r\n   FIELDS TERMINATED BY ','\r\n   LINES TERMINATED BY '\\n'\r\n   FROM test_table;\r\n\r\nImporting User Data with XEP-0227: Portable Import/Export Format\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nYou can easily import data exported from another XMPP server (or exported for Tigase XMPP Server, see below) using XEP-0227: Portable Import/Export Format.\r\n\r\nTo import data, you need to have Tigase XMPP Server configured but stopped and files containing exported data stored in some location (in our case in directory ``xep-0227-data`` stored inside Tigase XMPP Server installation directory) on the same server as Tigase XMPP Server.\r\n\r\nNow to import data to your repository you simply execute the command:\r\n\r\n.. code:: sh\r\n\r\n   scripts/tigase.sh import-data --from=xep-0227-data --include-mix --include-muc --include-pubsub\r\n\r\nThat will import all data including data from MIX, MUC, and PubSub component as well as data specified in XEP-0227.\r\n\r\n*Note, if your data is located at a different place you will need to adjust ``--from`` parameter value.*\r\n\r\nTo list all of the options available for importing data (ie. allowing to import data from MIX, MUC, PubSub, or excluding MAM data), you should execute following command:\r\n\r\n.. code:: sh\r\n\r\n   scripts/tigase.sh import-data --help\r\n\r\n   Usage:\r\n   \t$ scripts/tigase.sh [task] [params-file.conf] [options]\r\n   \t\tif the option defines default then <value> is optional\r\n\r\n   Tasks:\r\n   \texport-data\t-\tExport data to XML\r\n   \timport-data\t-\tImport data from XML\r\n\r\n   Options:\r\n\r\n   \t--help,  (optional)\r\n   \t\tPrint the help\r\n\r\n   \t-I, --interactive,  (optional)\r\n   \t\tEnable interactive mode, which will result in prompting for missing parameters\r\n\r\n   \t--config-file=value,  (optional)\r\n   \t\tPath to Tigase XMPP Server config file (default: etc/config.tdsl)\r\n\r\n   \t--from=value,\r\n   \t\tPath to import data from\r\n\r\n   \t--debug,  (optional)\r\n   \t\tEnable verbose logging (default: false)\r\n\r\n   \t--exclude-user-mam,  (optional)\r\n   \t\tExclude users MAM archives (default: false)\r\n\r\n   \t--plain-credentials,  (optional)\r\n   \t\tImport PLAIN credentials (default: false)\r\n\r\n   \t--include-mix,  (optional)\r\n   \t\tInclude MIX component data (default: false)\r\n\r\n   \t--include-muc,  (optional)\r\n   \t\tInclude MUC component data (default: false)\r\n\r\n   \t--include-pubsub,  (optional)\r\n   \t\tInclude PubSub component data (default: false)\r\n\r\n\r\nExporting User Data to XEP-0227: Portable Import/Export Format\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nYou can easily export data from Tigase XMPP Server to the XML files using XEP-0227: Portable Import/Export Format.\r\n\r\nTo export data you need to execute following command in the Tigase XMPP Server installation directory. We are assuming here that files will be exported to ``xep-0227-data`` directory in Tigase XMPP Server installation directory.\r\n\r\nNow to export data from your repository you simply execute the command:\r\n\r\n.. code:: sh\r\n\r\n   scripts/tigase.sh export-data --to=xep-0227-data --include-mix --include-muc --include-pubsub\r\n\r\nThat will export all data including data from MIX, MUC, and PubSub component as well as data specified in XEP-0227.\r\n\r\n*Note, you prefer exported data to be located in a different place you will need to adjust ``--to`` parameter value.*\r\n\r\nTo list all of the options available for exporting data (ie. allowing to import data from MIX, MUC, PubSub, or excluding MAM data), you should execute following command:\r\n\r\n.. code:: sh\r\n\r\n   scripts/tigase.sh export-data --help\r\n\r\n   Usage:\r\n   \t$ scripts/tigase.sh [task] [params-file.conf] [options]\r\n   \t\tif the option defines default then <value> is optional\r\n\r\n   Tasks:\r\n   \texport-data\t-\tExport data to XML\r\n   \timport-data\t-\tImport data from XML\r\n\r\n   Options:\r\n\r\n   \t--help,  (optional)\r\n   \t\tPrint the help\r\n\r\n   \t-I, --interactive,  (optional)\r\n   \t\tEnable interactive mode, which will result in prompting for missing parameters\r\n\r\n   \t--config-file=value,  (optional)\r\n   \t\tPath to Tigase XMPP Server config file (default: etc/config.tdsl)\r\n\r\n   \t--to=value,\r\n   \t\tPath to export data to\r\n\r\n   \t--debug,  (optional)\r\n   \t\tEnable verbose logging (default: false)\r\n\r\n   \t--exclude-user-mam,  (optional)\r\n   \t\tExclude users MAM archives (default: false)\r\n\r\n   \t--export-mam-since=value,  (optional)\r\n   \t\tExport MAM archive since\r\n\r\n   \t--export-mam-batch-size=value,  (optional)\r\n   \t\tExport MAM archive batch size (default: 50000)\r\n\r\n   \t--plain-credentials,  (optional)\r\n   \t\tExport PLAIN credentials (if any exist) (default: false)\r\n\r\n   \t--include-mix,  (optional)\r\n   \t\tInclude MIX component data (default: false)\r\n\r\n   \t--include-muc,  (optional)\r\n   \t\tInclude MUC component data (default: false)\r\n\r\n   \t--include-pubsub,  (optional)\r\n   \t\tInclude PubSub component data (default: false)\r\n\r\n   \t--exclude-pep,  (optional)\r\n   \t\tExclude user PEP data (default: false)"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Database_Management/Management.rst",
    "content": "Database Management\r\n====================\r\n\r\nTigase is coded to perform with multiple database types and numbers. Owing to it’s versatile nature, there are some tools and procedures that may be of use to certain administrators.\r\n\r\nRecommended database versions\r\n--------------------------------\r\n\r\nAs of v8.0.0 here are the minimum and recommended versions of databases for use with Tigase:\r\n\r\n+------------+---------------------+-----------------+------------------------------------------------------------------------------------------------------------------------------------+\r\n| Database   | Recommended Version | Minimum Version | Additional Information                                                                                                             |\r\n+============+=====================+=================+====================================================================================================================================+\r\n| DerbyDB    | 10.12.1.1           | 10.12.1.1       | Included with Tigase XMPP Server                                                                                                   |\r\n+------------+---------------------+-----------------+------------------------------------------------------------------------------------------------------------------------------------+\r\n| MySQL      | 5.7                 | 5.7             | Required to properly support timestamp storage with millisecond precision                                                          |\r\n+------------+---------------------+-----------------+------------------------------------------------------------------------------------------------------------------------------------+\r\n| SQLServer  | 2014                | 2012            | 2012 needed so we can count use fetch-offset pagination feature.                                                                   |\r\n+------------+---------------------+-----------------+------------------------------------------------------------------------------------------------------------------------------------+\r\n| PostgreSQL | 13.0                | 9.4             | New UA schema requires at least 9.4; if using version older than 13 manual installation of ``uuid-ossp`` extension is required (1) |\r\n+------------+---------------------+-----------------+------------------------------------------------------------------------------------------------------------------------------------+\r\n| MongoDB    | 3.2                 | 3.0             |                                                                                                                                    |\r\n+------------+---------------------+-----------------+------------------------------------------------------------------------------------------------------------------------------------+\r\n| MariaDB    | ?                   | 10.0.12         | Basic features works with 10.0.12-MariaDB Homebrew, but is not fully tested.                                                       |\r\n+------------+---------------------+-----------------+------------------------------------------------------------------------------------------------------------------------------------+\r\n\r\n.. Note::\r\n\r\n   (1) For PostgreSQL version older than 13.0 manual installation of ``uuid-ossp`` by the superuser to the *created database* is required:\r\n\r\n   .. code:: postgresql\r\n\r\n      CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";\r\n\r\nAlthough Tigase may support other versions of databases, these are the ones we are most familiar with in offering support and advice. Use of databases outside these guidelines may result in unforeseen errors.\r\n\r\nDatabase Watchdog\r\n--------------------\r\n\r\nIt is possible to have Tigase test availability and existence of database periodically only when db connections are idle. By default this ping is sent once every 60 minutes to each connected repository. However this can be overridden as a part of the dataSource property:\r\n\r\n.. code::\r\n\r\n   dataSource {\r\n       default () {\r\n           uri = '....'\r\n       }\r\n       'test' () {\r\n           uri =  '...'\r\n           'watchdog-frequency' = 'PT30M'\r\n       }\r\n   }\r\n\r\nThis setting changes frequency to 30 minutes.\r\n\r\n.. code::\r\n\r\n   dataSource {\r\n       default () {\r\n           uri = '...'\r\n       }\r\n       'watchdog-frequency' = 'PT15M'\r\n   }\r\n\r\nThis one changes to 15 minutes.\r\n\r\n.. Note::\r\n\r\n   see :ref:`Period / Duration values<PeriodDurationvalues>` for format details\r\n\r\nUsing modified database schema\r\n--------------------------------\r\n\r\nIf you are using Tigase XMPP Server with modified schema (changed procedures or tables) and you do not want Tigase XMPP Server to maintain it and automatically upgrade, you can disable ``schema-management`` for any data source. If ``schema-management`` is disable for particular data source then Tigase XMPP Server will not update or modify database schema in any way. Moreover it will not check if schema version is correct or not.\r\n\r\n**Disabling ``schema-management`` for ``default`` data source**\r\n\r\n.. code::\r\n\r\n   dataSource {\r\n       default () {\r\n           uri = '...'\r\n           'schema-management' = false\r\n       }\r\n   }\r\n\r\n.. Warning::\r\n\r\n    If ``schema-management`` is disabled, then it is administrator responsibility to maintain database schema and update it if needed (ie. if Tigase XMPP Server schema was changed).\r\n\r\n.. _Schemafilesmaintenance:\r\n\r\nSchema files maintenance\r\n--------------------------------\r\n\r\nThis document describes schema files layout and assumptions about it. In addition it describes how and when it should be updated.\r\n\r\nAssumptions\r\n^^^^^^^^^^^^^^\r\n\r\nFollowing assumptions are in place:\r\n\r\n-  All schema files are *loadable* multiple times - this is by far most important assumptions and it’s allow to get away without explicit and detailed checking of loaded version (it’s already handled on the schema level as of version 8.0.0)\r\n\r\n-  Required schema version is calculated from the component version (which is set in the project configuration file - usually ``pom.xml``, but it’s possible to override it in code via annotations - please see Developer Guild in Server documentation for details)\r\n\r\n-  we will maintain *\"3 versions schema files\"*, i.e. in the distribution package we will provide schema versions for the ``current_version`` and two major versions behind (and all maintenance version schema files) - this will allow *quick upgrade* even from rather older versions\r\n\r\n-  ``SNAPSHOT`` versions will print a log entry indicating that there may have been changes in schema and it’s recommended to run the upgrade (we are aiming at frequent releases thus mandatory schema version check will be done only with final version)\r\n\r\nChecks\r\n^^^^^^^^^^^^^^\r\n\r\nWe will check:\r\n\r\n-  if it’s possible to upgrade the schema (based on the current schema version in the database and available SQL files and their respective versions - if );\r\n\r\n-  if it’s required to upgrade the schema during server startup (until 7.1.x [inclusive] it was done only for tigase-server, will be done by all components)\r\n\r\n-  if it’s required to upgrade the schema during run of ``upgrade-schema``) (if schema is already in the latest required version, executing all SQL files is not required hence speeding up upgrade)\r\n\r\n   -  During startup of ``SNAPSHOT`` version, even if the schema version match, a prompt to re-run ``upgrade-schema`` will be printed in the ``logs/tigase-console.log``\r\n\r\nSchema files layout\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nFilename layout\r\n~~~~~~~~~~~~~~~~~~~~\r\n\r\nBasic schema filename layout consists of 3 basic parts:\r\n\r\n-  name of relational database management system (RDBMS) for which it’s intended (e.g. ``derby``, ``mysql``, ``postgresql``, ``sqlserver``);\r\n\r\n-  name of the Tigase component for which it’s intended;\r\n\r\n-  version of the schema file.\r\n\r\nFor each component and version it’s possible (but not mandatory) to split all database related functionality into multiple files but it’s essential that they would be linked/included in the base file for particular database/component/version file. This allows separating Stored Procedures (``-sp``), base schema (``-schema``) and setting properties (``-props``). In principle the filename pattern looks as follows\r\n\r\n::\r\n\r\n   <RDBMS_name>-<tigase_component>-schema-<version>[-<sub_schema>].sql\r\n\r\nFor example schema file for version 7.0.0 of Tigase Server for Derby looks as follow:\r\n\r\n::\r\n\r\n   derby-server-schema-7.0.0-schema.sql\r\n\r\n\r\nFiles structure\r\n~~~~~~~~~~~~~~~~~~~~\r\n\r\nAs mentioned before, we should support all versions matching ``old-stable``, ``stable`` and ``master``, which translates to two main versions behind *current-version*, that is version: *current-version - 2*). This results in having 3 versions of the schema in the repository at any given time (two of them being \\``upgrades'' to the oldest, base schema):\r\n\r\n-  ``current-version`` *minus* 2: base schema\r\n\r\n-  ``current-version`` *minus* 1: all changes from ``current-version`` *minus* 2 to ``current-version`` *minus* 1\r\n\r\n-  ``current-version``: all changes from ``current-version`` *minus* 1 to ``current-version``\r\n\r\n.. Note::\r\n\r\n   ``current-version`` *MUST* always match version of the component (defined in pom.xml).\r\n\r\n .. Note::\r\n\r\n   It’s possible to have multiple files within version (related to smaller, maintenance upgrade) as the SchemaLoader would collect all files which version falls within range and .\r\n\r\nFor example with the release of version 8.0.0 this will translate to following versions:\r\n\r\n-  ``7.0.0``: base schema\r\n\r\n-  ``7.1.0``: all changes from ``7.0.0`` to ``7.1.0``\r\n\r\n-  ``8.0.0``: all changes from ``7.1.0`` to ``8.0.0``\r\n\r\n.. Note::\r\n\r\n   All schema files must be stored under ``src/main/database/``\r\n\r\nHandling of changes in the schema\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThere are two main workflows defined\r\n\r\nDuring release of the version\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nAs we keep at the most only 3 versions of the schema, after release of the version we need to adjust (flatten) the files to maintain structure defined in *Files structure* (it may happen, that there wouldn’t be any changes in the schema for particular version which will result in relatively empty ``current-version`` schema file – only setting current version for component with ``setVersion('component','<current-version></current-version>');`` ).\r\n\r\nFor example we are about to release version ``8.0.0``. This results in the following versions of the schema (in the example for the server) in the repository:\r\n\r\n-  ``<database>-server-schema-7.0.0.sql``: base schema\r\n\r\n-  ``<database>-server-schema-7.1.0.sql``: including changes for ``7.1.0``\r\n\r\n-  ``<database>-server-schema-8.0.0.sql``: including changes for ``8.0.0``\r\n\r\n.. Note::\r\n\r\n   It’s possible that there will be maintenance versions in the list as well, e.g.: ``<database>-server-schema-7.1.1.sql`` and ``<database>-server-schema-7.1.2.sql``\r\n\r\nAfter the release we specify the version of the next release in pom.xml (for example ``8.1.0`` and the same version will be the ``current-version`` making the oldest available version ``7.1.0``. Because of that we *MUST* incorporate all the changes in ``7.1.0`` onto ``7.0.0`` creating new base file with version ``7.1.0``, i.e.:\r\n\r\n-  ``<database>-server-schema-7.1.0.sql``: base schema\r\n\r\n-  ``<database>-server-schema-8.0.0.sql``: including changes for ``8.0.0``\r\n\r\n-  ``<database>-server-schema-8.1.0.sql``: including changes for ``8.1.0``\r\n\r\nMaintenance releases\r\n~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nFollowing cases will be discussed with solid-version examples. Comments will be provided in-line Following assumptions are made:\r\n\r\n-  Version succession: ``5.1.0``, ``5.2.0``, ``7.0.0``, ``7.1.0``, ``8.0.0``\r\n\r\n-  Versions mapping: ``master`` (``8.0.0``), ``stable`` (``7.1.0``), ``old-stable`` (``7.0.0``):\r\n\r\n   -  schema files in ``old-stable`` branch\r\n\r\n      -  5.1.0 (base)\r\n\r\n      -  5.2.0 (upgrade)\r\n\r\n      -  7.0.0 (upgrade)\r\n\r\n   -  schema files in ``stable`` branch\r\n\r\n      -  5.2.0 (base)\r\n\r\n      -  7.0.0 (upgrade)\r\n\r\n      -  7.1.0 (upgrade)\r\n\r\n   -  schema files in ``master`` branch\r\n\r\n      -  7.0.0 (base)\r\n\r\n      -  7.1.0 (upgrade)\r\n\r\n      -  8.0.0 (upgrade)\r\n\r\n\r\nMaking a change in ``old-stable`` (and ``stable``)\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIf we made a schema change in ``old-stable`` version (and it’s branch) we must:\r\n\r\n-  create a new file with upgraded version number;\r\n\r\n-  propagate the change to the ``stable`` and ``master`` branch.\r\n\r\nRepository changes:\r\n\r\n-  schema files in ``old-stable`` branch\r\n\r\n   -  5.1.0 (base)\r\n\r\n   -  5.2.0 (upgrade)\r\n\r\n   -  7.0.0 (upgrade)\r\n\r\n   -  7.0.1 (upgrade) **←** making a *change* here results in the schema version being bumped to 7.0.1\r\n\r\n-  schema files in ``stable`` branch\r\n\r\n   -  5.2.0 (base)\r\n\r\n   -  7.0.0 (upgrade)\r\n\r\n   -  7.0.1 (upgrade) **←** we must port the *change* here\r\n\r\n   -  7.1.0 (upgrade)\r\n\r\n-  schema files in ``master`` branch\r\n\r\n   -  7.0.0 (base)\r\n\r\n   -  7.0.1 (upgrade) **←** we must port the *change* here\r\n\r\n   -  7.1.0 (upgrade)\r\n\r\n   -  8.0.0 (upgrade)\r\n\r\n\r\nMaking a change in ``master``\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIf we made a schema change in ``master`` version we don’t propagate the change to the ``stable`` and ``old-stable`` branch.\r\n\r\n-  schema files in ``old-stable`` branch\r\n\r\n   -  5.1.0 (base)\r\n\r\n   -  5.2.0 (upgrade)\r\n\r\n   -  7.0.0 (upgrade)\r\n\r\n-  schema files in ``stable`` branch\r\n\r\n   -  5.2.0 (base)\r\n\r\n   -  7.0.0 (upgrade)\r\n\r\n   -  7.1.0 (upgrade)\r\n\r\n-  schema files in ``master`` branch\r\n\r\n   -  7.0.0 (base)\r\n\r\n   -  7.1.0 (upgrade)\r\n\r\n   -  8.0.0 (upgrade) **←** we make the *change* here, as this is the development version schema version remains the same.\r\n\r\n\r\nImplementation details\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIn-file control\r\n~~~~~~~~~~~~~~~~~\r\n\r\nThere are two main control instructions (intended for ``SchemaLoader``):\r\n\r\n-  denoting Queries with ``-- QUERY START:`` and ``-- QUERY END:`` - each must be placed in own, separate file with the query being enclosed by the two of them, for example:\r\n\r\n   .. code:: sql\r\n\r\n      -- QUERY START:\r\n      call TigPutDBProperty('schema-version', '5.1');\r\n      -- QUERY END:\r\n\r\n-  sourcing other file with ``-- LOAD FILE: <path to .sql file>`` - path must be on the same line, following control instruction, for example:\r\n\r\n   .. code:: sql\r\n\r\n      -- LOAD FILE: database/mysql-server-schema-7.0.0-schema.sql\r\n\r\nStoring version in the database\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nEach repository will have a table ``tig_schema_versions`` with the information about all installed components and it’s versions in that particular repository. There will be an associated stored procedure to obtain and set version:\r\n\r\n-  table:\r\n\r\n   .. code:: sql\r\n\r\n      tig_schema_versions (\r\n        component varchar(100) NOT NULL,\r\n        version varchar(100) NOT NULL,\r\n        last_update timestamp NOT NULL,\r\n        primary key (component)\r\n      );\r\n\r\n-  stored procedures ``get/setVersion(‘component’,'version');``\r\n\r\nIt will be stored and maintained in the file named ``<RDBMS_name>-common-schema-<version>.sql``\r\n\r\n.. include:: Database_Preparation.inc\r\n.. include:: Hashed_User_Passwords_in_Database.inc\r\n.. include:: Multiple_Databases.inc\r\n.. include:: Importing_Exporting_User_Data.inc\r\n.. include:: Existing_Databases.inc\r\n.. include:: Schema_Updates.inc"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Database_Management/Multiple_Databases.inc",
    "content": "Tigase Server and Multiple Databases\r\n-----------------------------------------\r\n\r\nSplitting user authentication data from all other XMPP information such as roster, vcards, etc…​ was almost always possible in Tigase XMPP Server. Possible and quite simple thing to configure. Also it has been always possible and easy to assign a different database for each Tigase component (MUC, PubSub, AMP), for recording the server statistics. Almost every data type or component can store information in a different location, simple and easy to setup through the configuration file.\r\n\r\nHowever it is much less known that it is also possible to have a different database for each virtual domain. This applies to both the user repository and authentication repository. This allows for very interesting configuration such as user database sharing where each shard keeps users for a specific domain, or physically split data based on virtual domain if each domain refers to a different customer or group of people.\r\n\r\nHow can we do that then?\r\n\r\nThis is very easy to do through the Tigase’s configuration file.\r\n\r\n.. code::\r\n\r\n   dataSource {\r\n       default () {\r\n           uri = 'jdbc:mysql://db2.tigase/dbname?user&password'\r\n       }\r\n       'default-auth' () {\r\n           uri = 'jdbc:mysql://db1.tigase/dbname?user&password'\r\n       }\r\n   }\r\n   userRepository {\r\n       default () {}\r\n   }\r\n   authRepository {\r\n       default () {\r\n           'repo-class' = 'tigase.db.jdbc.TigaseCustomAuth'\r\n           'data-source' = 'default-auth'\r\n       }\r\n   }\r\n\r\nThis configuration defines just a default databases for both user repository and authentication repository. Default means it is used when there is no repository specified for a particular virtual domain. However, you can have a separate, both user and authentication repository for each virtual domain.\r\n\r\nHere is, how it works:\r\n\r\nFirst, let’s define our default database for all VHosts\r\n\r\n.. code::\r\n\r\n   dataSource {\r\n       default () {\r\n           uri = 'jdbc:mysql://db2.tigase/dbname?user&password'\r\n       }\r\n       'default-auth' () {\r\n           uri = 'jdbc:mysql://db1.tigase/dbname?user&password'\r\n       }\r\n   }\r\n   userRepository {\r\n       default () {}\r\n   }\r\n   authRepository {\r\n       default () {\r\n           'repo-class' = 'tigase.db.jdbc.TigaseCustomAuth'\r\n           'data-source' = 'default-auth'\r\n       }\r\n   }\r\n\r\nNow, we have VHost: `domain1.com`. User authentication data for this VHost is stored in `db7.tigase` database\r\n\r\n.. code::\r\n\r\n   dataSource {\r\n     'domain1.com-auth' () {\r\n       uri = jdbc:mysql://db7.tigase/dbname?user&password'\r\n     }\r\n   }\r\n   authRepository {\r\n     domain1.com () {\r\n       'repo-class' = 'tigase/db/jdbc.TigaseCustomAuth'\r\n       'data-source' = 'domain1.com-auth'\r\n     }\r\n   }\r\n\r\nAll other user data is stored in Tigase’s standard database in `db4.tigase` MySQL database\r\n\r\n.. code::\r\n\r\n   dataSource {\r\n     'domain1.com' () {\r\n       uri = jdbc:mysql://db4.tigase/dbname?user&password'\r\n     }\r\n   }\r\n   userRepository {\r\n     domain1.com () {}\r\n   }\r\n\r\nNext VHost: `domain2.com`\r\n\r\nUser authentication is in **LDAP** server but all other user data is stored in Tigase’s standard database\r\n\r\n.. code::\r\n\r\n   authRepository {\r\n       domain2.com () {\r\n           'repo-class' = 'tigase.db.ldap.LdapAuthProvider'\r\n           'repo-uri' = 'ldap://ldap.domain2.com:389'\r\n           'data-source' = 'default'\r\n       }\r\n   }\r\n\r\nNow is something new, we have a custom authentication repository and separate user settings for a single domain. Please note how we define the VHost for which we set custom parameters\r\n\r\n.. code::\r\n\r\n   authRepository {\r\n       domain2.com {\r\n           'user-dn-pattern' = 'cn=,ou=,dc=,dc='\r\n       }\r\n   }\r\n\r\nAll other user data is stored in the same as default repository\r\n\r\n.. code::\r\n\r\n   userRepository {\r\n       domain2.com () {}\r\n   }\r\n   dataSource {\r\n       domain2.com () {\r\n           uri = 'jdbc:mysql://db2.tigase/dbname?user&password'\r\n       }\r\n   }\r\n\r\nWhen combined, the DSL output should look like this:\r\n\r\n.. code::\r\n\r\n   dataSource {\r\n       domain2.com () {\r\n           uri = 'jdbc:mysql://db2.tigase/dbname?user&password'\r\n       }\r\n   }\r\n   userRepository {\r\n       domain2.com () {}\r\n   }\r\n   authRepository {\r\n       domain2.com () {\r\n           'repo-class' = 'tigase.db.ldap.LdapAuthProvider'\r\n           'repo-uri' = 'ldap://ldap.domain2.com:389'\r\n           'user-dn-pattern' = 'cn=,ou=,dc=,dc='\r\n       }\r\n   }\r\n\r\nNext VHost: `domain3.com`\r\n\r\nAgain user authentication is in **LDAP** server but pointing to a different LDAP server with different access credentials and parameters. User information is stored in a postgreSQL database.\r\n\r\n.. code::\r\n\r\n   dataSource {\r\n       domain3.com () {\r\n           uri = 'jdbc:pgsql://db.domain3.com/dbname?user&password'\r\n       }\r\n   }\r\n   userRepository {\r\n       domain3.com () {}\r\n   }\r\n   authRepository {\r\n       domain3.com () {\r\n           'repo-class' = 'tigase.db.ldap.LdapAuthProvider'\r\n           'repo-uri' = 'ldap://ldap.domain3.com:389'\r\n           'user-dn-pattern' = 'cn=,ou=,dc=,dc='\r\n       }\r\n   }\r\n\r\nFor VHost: `domain4.`com all the data, both authentication and user XMPP data are stored on a separate MySQL server with custom stored procedures for both user login and user logout processing.\r\n\r\n.. code::\r\n\r\n   dataSource {\r\n       domain4.com () {\r\n           uri = 'jdbc:mysql://db14.domain4.com/dbname?user&password'\r\n       }\r\n   }\r\n   userRepository {\r\n       domain4.com () {}\r\n   }\r\n   authRepository {\r\n       domain4.com () {\r\n           'repo-class' = 'tigase.db.jdbc.TigaseCustomAuth'\r\n           'user-login-query' = '{ call UserLogin(?, ?) }'\r\n           'user-logout-query' = '{ call UserLogout(?) }'\r\n           'sasl-mechs' = [ 'PLAIN', 'DIGEST-MD5' ]\r\n       }\r\n   }\r\n\r\nAs you can see, it requires some writing but flexibility is very extensive and you can setup as many separate databases as you need or want. If one database (recognized by the database connection string) is shared among different VHosts, Tigase still uses a single connection pool, so it won’t create an excessive number of connections to the database.\r\n\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Database_Management/Schema_Updates.inc",
    "content": "Schema Updates\r\n--------------------\r\n\r\nThis is a repository for Schema updates in case you have to upgrade from older installations.\r\n\r\n-  `Tigase Server Schema v7.1 Updates <#tigaseServer71>`__ Applies to v7.1.0 and v8.0.0\r\n\r\nChanges to Schema in v8.0.0\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nFor version 8.0.0 of Tigase XMPP Server, we decided to improve authentication and security that was provided. In order to do this, implementation of repository and database schemas needed to be changed to achieve this goal. This document, as well one in the HTTP API, will describe the changes to the schemas in this new version.\r\n\r\nReasons\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nBefore version 8.0.0, user passwords were stored in plaintext in ``user_pw`` database field within ``tig_users`` table, but in plaintext. It was possible to enable storage of the MD5 hash of the password instead, however this limited authentication mechanism SASL PLAIN only. However an MD5 hash of a password is not really a secure method as it is possible to revert this mechanism using rainbow tables.\r\n\r\nTherefore, we decided to change this and store only encrypted versions of a password in ``PBKDF2`` form which can be easily used for ``SCRAM-SHA-1`` authentication mechanism or ``SCRAM-SHA-256``. SASL PLAIN mechanism can also used these encrypted passwords. The storage of encrypted passwords is now enabled **by default** in v8.0.0 of Tigase.\r\n\r\nSummary of changes\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nAdded support for storage of encrypted password\r\n\r\nPasswords are no longer stored in plaintext on any database.\r\n\r\nUsing same salt for any subsequent authentications\r\n\r\nThis allows clients to reuse calculated credentials and keep them instead of storing plaintext passwords.\r\n\r\nDisabled usage of stored procedure for authentication\r\n\r\nIn previous versions, Tigase used stored procedures ``TigUserLoginPlainPw`` and ``TigUserLogin`` for SASL PLAIN authentication. From version 8.0.0, those procedures are no longer used, but they are updated to use passwords stored in ``tig_user_credentials`` table.\r\n\r\nIt is still possible to use this procedures for authentication, but to do that you need add:\r\n\r\n.. code::\r\n\r\n   'user-login-query' = '{ call TigUserLoginPlainPw(?, ?) }'\r\n\r\nto configuration block of **every** authentication repository.\r\n\r\nTo enable this for default repository, the ``authRepository`` configuration block will look like this:\r\n\r\n.. code::\r\n\r\n   authRepository () {\r\n       default () {\r\n           'user-login-query' = '{ call TigUserLoginPlainPw(?, ?) }'\r\n       }\r\n   }\r\n\r\n\r\nDeprecated API\r\n\r\nSome methods of ``AuthRepository`` API were deprecated and should not be used. Most of them were used for authentication using stored procedures, retrieval of password in plaintext or for password change.\r\n\r\nFor most of these methods, new versions based on ``tig_user_credentials`` table and user credentials storage are provided where possible.\r\n\r\nDeprecated storage procedures\r\n\r\nStored procedures for authentication and password manipulation were updated to a new form, so that will be possible to use them by older versions of Tigase XMPP Server during rolling updates of a cluster. However, these procedures will not be used any more and will be depreciated and removed in future versions of Tigase XMPP Server.\r\n\r\nUsage of MD5 hashes of passwords\r\n\r\nIf you have changed ``password-encoding`` database property in previous versions of Tigase XMPP Server, then you will need to modify your configuration to keep it working. If you wish only to allow access using old passwords and to store changed passwords in the new form, then you need to enable credentials decoder for the correct authentication repository. In this example we will provided changes required for ``MD5-PASSWORD`` value of ``password-encoding`` database property. If you have used a different one, then just replace ``MD5-PASSWORD`` with ``MD5-USERNAME-PASSWORD`` or ``MD5-USERID-PASSWORD``.\r\n\r\n**Usage of MD5 decoder.**\r\n\r\n.. code::\r\n\r\n   authRepository () {\r\n       default () {\r\n           credentialDecoders () {\r\n               'MD5-PASSWORD' () {}\r\n           }\r\n       }\r\n   }\r\n\r\nIf you wish to store passwords in MD5 form then use following entries in your configuration file:\r\n\r\n**Usage of MD5 encoder.**\r\n\r\n.. code::\r\n\r\n   authRepository () {\r\n       default () {\r\n           credentialEncoders () {\r\n               'MD5-PASSWORD' () {}\r\n           }\r\n       }\r\n   }\r\n\r\n\r\nEnabling and disabling credentials encoders/decoders\r\n\r\nYou may enable which encoders and decoders used on your installation. By enabling encoders/decoders you are deciding in what form the password is stored in the database. Those changes may impact which SASL mechanisms may be allowed to use on your installation.\r\n\r\n**Enabling PLAIN decoder.**\r\n\r\n.. code::\r\n\r\n   authRepository () {\r\n       default () {\r\n           credentialDecoders () {\r\n               'PLAIN' () {}\r\n           }\r\n       }\r\n   }\r\n\r\n**Disabling SCRAM-SHA-1 encoder.**\r\n\r\n.. code::\r\n\r\n   authRepository () {\r\n       default () {\r\n           credentialEncoders () {\r\n               'SCRAM-SHA-1' (active: false) {}\r\n               'SCRAM-SHA-256' (active: false) {}\r\n           }\r\n       }\r\n   }\r\n\r\n.. Warning::\r\n\r\n    It is strongly recommended not to disable encoders if you have enabled decoder of the same type as it may lead to the authentication issues, if client tries to use a mechanism which that is not available.\r\n\r\nSchema changes\r\n\r\nThis change resulted in a creation of the new table ``tig_user_credentials`` with following fields:\r\n\r\n**uid**\r\n   id of a user row in ``tig_users``.\r\n\r\n**username**\r\n   username used for authentication (if ``authzid`` is not provided or ``authzid`` localpart is equal to ``authcid`` then row with ``default`` value will be used).\r\n\r\n**mechanism**\r\n   name of mechanism for which this credentials will be used, ie. ``SCRAM-SHA-1`` or ``PLAIN``.\r\n\r\n**value**\r\n   serialized value required for mechanism to confirm that credentials match.\r\n\r\n.. Warning::\r\n\r\n    During execution of ``upgrade-schema`` task, passwords will be removed from ``tig_users`` table from ``user_pw`` field and moved to ``tig_user_credentials`` table.\r\n\r\nAdded password reset mechanism\r\n\r\nAs a part of Tigase HTTP API component and Tigase Extras, we developed a mechanism which allows user to reset their password. To use this mechanism HTTP API component and its REST module **must** to be enabled on Tigase XMPP Server installation.\r\n\r\n.. Note::\r\n\r\n   Additionally this mechanism need to be enabled in the configuration file. For more information about configuration of this mechanism please check Tigase HTTP API component documentation.\r\n\r\nAssuming that HTTP API component is configured to run on port 8080 *(default)*, then after accessing address http://localhost:8080/rest/user/resetPassword in the web browser it will present a web form. By filling and submitting this form, the user will initiate a password reset process. During this process, Tigase XMPP Server will send an email to the user’s email address (provided during registration) with a link to the password change form.\r\n\r\nUpgrading from v7.1.x\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nWhen upgrading from previous versions of Tigase, it is recommended that you first backup the database. Refer to the documentation of your database software to find out how to export a copy. Once the backup is made, it will be time to run the schema upgrade. Be sure that your schema is up to date, and should be v7.1.0 Schema.\r\n\r\nTo upgrade, use the new ``upgrade-schema`` task of SchemaManager:\r\n\r\n-  In linux\r\n\r\n   .. code:: bash\r\n\r\n      ./scripts/tigase.sh install-schema etc/tigase.conf\r\n\r\n-  In Windows\r\n\r\n   .. code:: bash\r\n\r\n      java -cp \"jars/*\" tigase.db.util.SchemaManager \"install-schema\"\r\n\r\nYou will need to configure the following switches:\r\n\r\n-  | ``-T`` Specifies Database Type\r\n   | Possible values are: ``mysql``, ``derby``, ``sqlserver``, ``postgresql``, ``mongodb``\r\n\r\n-  | ``-D`` Specifies Databse Name\r\n   | The explicit name of the database you wish to upgrade.\r\n\r\n-  | ``-H`` Specifies Host address\r\n   | By default, this is localhost, but may be set to IP address or FQDNS address.\r\n\r\n-  | ``-U`` Specifies Username\r\n   | This is the username that is authorized to make changes to the database defined in -D.\r\n\r\n-  | ``-P`` Specifies Password\r\n   | The password for username specified in -U.\r\n\r\n-  ``-R`` Password for Administrator or Root DB account.\r\n\r\n-  ``-A`` Password for Administrator or Root DB account.\r\n\r\n-  ``-J`` Jid of user authorized as admin user from Tigase.\r\n\r\n-  ``-N`` Password for user specified in -J.\r\n\r\n-  | ``-F`` Points to the file that will perform the upgrade.\r\n   | Will follow this form database/{dbname}-server-schema-8.0.0.sql\r\n\r\nTigase Server Schema v7.2 Updates\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**FOR ALL USERS UPGRADING TO v8.0.0 FROM A v7.0.2 INSTALLATION**\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\n| The schema has changed for the main database, and the pubsub repository. In order to upgrade to the new schemas, you will need to do the following:\r\n\r\n1. Upgrade the Main database schema to v7.1 using the ``database/${DB_TYPE}-schema-upgrade-to-7-1.sql`` file\r\n\r\n2. Upgrade the Pubsub Schema to v3.1.0 using the ``database/${DB_TYPE}-pubsub-schema-3.1.0.sql`` file\r\n\r\n3. Upgrade the Pubsub Schema to v3.2.0 using the ``database/${DB_TYPE}-pubsub-schema-3.2.0.sql`` file\r\n\r\n4. Upgrade the Pubsub Schema to v3.3.0 using the ``database/${DB_TYPE}-pubsub-schema-3.3.0.sql`` file\r\n\r\nAll three commands may be done at the same time in that order, it is suggested you make a backup of your current database to prevent data loss.\r\n\r\nTigase Schema Change for v7.1\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nTigase has made changes to its database to include primary keys in the tig_pairs table to improve performance of the Tigase server. This is an auto-incremented column for Primary Key items appended to the previous schema.\r\n\r\n.. Warning::\r\n\r\n    **You MUST update your database to be compliant with the new schema. If you do not, Tigase will not function properly.**\r\n\r\n.. Note::\r\n\r\n   *This change will affect all users of Tigase using v7.1.0 and newer.*\r\n\r\nIf you are installing a new version of v8.0.0 on a new database, the schema should automatically be installed.\r\n\r\nFirst, shut down any running instances of Tigase to prevent conflicts with database editing. Then from command line use the DBSchemaLoader class to run the -schema-upgrade-to-7.1.sql file to the database. The command is as follows:\r\n\r\nIn a linux environment\r\n\r\n.. code:: bash\r\n\r\n   java -cp \"jars/*\" tigase.db.util.DBSchemaLoader -dbHostname ${HOSTNAME} -dbType ${DB_TYPE} -rootUser ${ROOT_USER} -dbPass ${DB_USER_PASS} -dbName ${DB_NAME} -schemaVersion ${DB_VERSION} -rootPass ${ROOT_USER_PASS} -dbUser ${DB_USER}  -adminJID \"${ADMIN_JID}\" -adminJIDpass ${ADMIN_JID_PASS}  -logLevel ALL -file database/${DB_TYPE}-schema-upgrade-to-7-1.sql\r\n\r\nIn a windows environment\r\n\r\n.. code:: bash\r\n\r\n   java -cp jars/* tigase.db.util.DBSchemaLoader -dbHostname ${HOSTNAME} -dbType ${DB_TYPE} -rootUser ${ROOT_USER} -dbPass ${DB_USER_PASS} -dbName ${DB_NAME} -schemaVersion ${DB_VERSION} -rootPass ${ROOT_USER_PASS} -dbUser ${DB_USER}  -adminJID \"${ADMIN_JID}\" -adminJIDpass ${ADMIN_JID_PASS}  -logLevel ALL -file database/${DB_TYPE}-schema-upgrade-to-7-1.sql\r\n\r\nAll variables will be required, they are as follows:\r\n\r\n-  ``${HOSTNAME}`` - Hostname of the database you wish to upgrade.\r\n\r\n-  ``${DB_TYPE}`` - Type of database [derby, mysql, postgresql, sqlserver].\r\n\r\n-  ``${ROOT_USER}`` - Username of root user.\r\n\r\n-  ``${ROOT_USER_PASS}`` - Password of specified root user.\r\n\r\n-  ``${DB_USER}`` - Login of user that can edit database.\r\n\r\n-  ``${DB_USER_PASS}`` - Password of the specified user.\r\n\r\n-  ``${DB_NAME}`` - Name of the database to be edited.\r\n\r\n-  ``${DB_VERSION}`` - In this case, we want this to be 7.1.\r\n\r\n-  ``${ADMIN_JID}`` - Bare JID of a database user with admin privileges. Must be contained within quotation marks.\r\n\r\n-  ``${ADMIN_JID_PASS}`` - Password of associated admin JID.\r\n\r\nPlease note that the SQL file for the update will have an associated database with the filename. i.e. postgresql-update-to-7.1.sql for postgresql database.\r\n\r\nA finalized command will look something like this:\r\n\r\n.. code:: bash\r\n\r\n   java -cp \"jars/*\" tigase.db.util.DBSchemaLoader -dbHostname localhost -dbType mysql -rootUser root -rootPass root -dbUser admin -dbPass admin -schemaVersion 7.1 -dbName Tigasedb -adminJID \"admin@local.com\" -adminJIDPass adminpass -logLevel ALL -file database/mysql-schema-upgrade-to-7.1.sql\r\n\r\nOnce this has successfully executed, you may restart you server. Watch logs for any db errors that may indicate an incomplete schema upgrade.\r\n\r\nChanges to Pubsub Schema\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nTigase has had a change to the PubSub Schema, to upgrade to PubSub Schema v7.1 without having to reform your databases, use this guide to update your databases to be compatible with the new version of Tigase.\r\n\r\n.. Note::\r\n\r\n   Current PubSub Schema is v3.3.0, you will need to repeat these instructions for v3.1.0, v3.2.0 and then v3.3.0 before you run Tigase V7.1.0.\r\n\r\nThe PubSub Schema has been streamlined for better resource use, this change affects all users of Tigase. To prepare your database for the new schema, first be sure to create a backup! Then apply the appropriate PubSub schema to your MySQL and it will add the new storage procedure.\r\n\r\nAll these files should be in your /database folder within Tigase, however if you are missing the appropriate files, use the links below and place them into that folder.\r\n\r\nThe MySQL schema can be found `Here <https://github.com/tigase/tigase-pubsub/blob/master/src/main/database/mysql-pubsub-4.1.0.sql>`__.\r\n\r\nThe Derby schema can be found `Here <https://github.com/tigase/tigase-pubsub/blob/master/src/main/database/derby-pubsub-4.1.0.sql>`__.\r\n\r\nThe PostGRESQL schema can be found `Here <https://github.com/tigase/tigase-pubsub/blob/master/src/main/database/postgresql-pubsub-4.1.0.sql>`__.\r\n\r\nThe same files are also included in all distributions of v8.0.0 in [tigaseroot]/database/ . All changes to database schema are meant to be backward compatible.\r\n\r\nYou can use a utility in Tigase to update the schema using the following command from the Tigase root:\r\n\r\n-  Linux\r\n\r\n   .. code:: bash\r\n\r\n      java -cp \"jars/*\" tigase.db.util.DBSchemaLoader\r\n\r\n-  Windows:\r\n\r\n   ::\r\n\r\n      java -cp jars/* tigase.db.util.DBSchemaLoader\r\n\r\n.. Note::\r\n\r\n   **Some variation may be necessary depending on how your java build uses** ``-cp`` **option**\r\n\r\nUse the following options to customize. Options in bold are required.:\r\n\r\n-  ``-dbType`` database_type {derby, mysql, postgresql, sqlserver} (*required*)\r\n\r\n-  ``-schemaVersion`` schema version {4, 5, 5-1}\r\n\r\n-  ``-dbName`` database name (*required*)\r\n\r\n-  ``-dbHostname`` database hostname (default is localhost)\r\n\r\n-  ``-dbUser`` tigase username\r\n\r\n-  ``-dbPass`` tigase user password\r\n\r\n-  ``-rootUser`` database root username (*required*)\r\n\r\n-  ``-rootPass`` database root password (*required*)\r\n\r\n-  ``-file path`` to sql schema file (*required*)\r\n\r\n-  ``-query`` sql query to execute\r\n\r\n-  ``-logLevel`` java logger Level\r\n\r\n-  ``-adminJID`` comma separated list of admin JIDs\r\n\r\n-  ``-adminJIDpass`` password (one for all entered JIDs\r\n\r\n.. Note::\r\n\r\n   Arguments take following precedent: query, file, whole schema\r\n\r\nAs a result your final command should look something like this:\r\n\r\n::\r\n\r\n   java -cp \"jars/*\" tigase.db.util.DBSchemaLoader -dbType mysql -dbName tigasedb -dbUser root -dbPass password -file database/mysql-pubsub-schema-3.1.0.sql\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Licensing_Open_Source.rst",
    "content": "Licensing and Open Source\r\n==============================\r\n\r\nAs mentioned previously, Tigase is open source under AGPLv3. If you are not familiar with open source software, or the environment, here are some frequently asked questions that might provide some answers.\r\n\r\n| **What does open source mean?**\r\n| This means that Tigase’s source code is available to the public to see how Tigase works. There are no 'black boxes' for packets where things just happen, everything is out in the open, whereas other companies may consider this propitiatory information. In addition, we have the benefit of many talented people working with Tigase to constantly improve Tigase server and related projects. These people not only include the Tigase development team, but other members of the community who submit code improvements, patches, enhancements, or other changes to Tigase.\r\n\r\n| **Does this mean that the binaries are open to malicious code?**\r\n| Although we accept patches from contributors, our repository does not accept them directly. Code may be submitted through our `tigase.tech <http://tigase.tech>`__ page and our developers will review the code before it is added. All builds are tested for functionality and security when they are built.\r\n\r\n| **Does this mean it is less secure?**\r\n| Not at all. Although anybody can see the source code, and know how Tigase works; your installation, connections, and settings are uniquely yours. Tigase is regularly tested and written to be as secure as possible using the latest encryption and secure connection protocols.\r\n\r\n| **Is Tigase free?**\r\n| Tigase is free for download and use in it’s unmodified state. Our commercial grade products such as Advanced Clustering Strategy is available for free use for testing & development.\r\n\r\n| **Does this mean I cannot use it in my product or commercial environment?**\r\n| Not necessarily, consult the Affero General Public License Agreement v3 to see if your use qualifies. Tigase is offered under commercial license if your use is not covered by AGPLv3.\r\n\r\n| **Are there options for closed code or extensions?**\r\n| Yes! Commercial licenses can be custom made for each client, and software written for your company may be made private or part of our open source distributions at your discretion.\r\n\r\n| **Can I contribute code?**\r\n| Sure! We accept code through GitHub pull-requests - submit them to one of our projects listed in our `GitHub organisation <https://github.com/tigase/>`__\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Properties/_cluster.inc",
    "content": "Cluster\r\n-----------\r\n\r\ncl-comp\r\n^^^^^^^^^\r\n\r\n**Description:** Container specifying cluster component configuration.\r\n\r\n**Default value:** By default, the cl-comp container is not listed in the ``config.tdsl`` file.\r\n\r\n**Example:**\r\n\r\n.. code::\r\n\r\n   'cl-comp' {\r\n       <configuration>\r\n   }\r\n\r\nconnect-all\r\n~~~~~~~~~~~~~~~\r\n\r\nThe ``cluster-connect-all`` property is used to open active connections to all nodes listed in the :ref:`cluster-nodes<clusterNodes>` configuration property. This property should be used only on the node which is added to the live cluster at later time. Normally this new cluster node is not listed in the configuration of the existing cluster nodes. This is why they can not open connections the new node. The new node opens connection to all existing nodes instead. False is the default value and you can skip this option if you want to have it switched off which it is by default.\r\n\r\n**Example**\r\n\r\n.. code::\r\n\r\n   'cl-comp' {\r\n       'connect-all' = true\r\n   }\r\n\r\nThis replaces the ``--cluster-connect-all`` property.\r\n\r\n**Available since:** 8.0.0\r\n\r\ncluster-mode\r\n^^^^^^^^^^^^^^^^^^\r\n\r\n**Description:** The property is used to switch cluster mode on. The default value is ``false`` so you can normally skip the parameter if you don’t want the server to run in cluster mode. You can run the server in the cluster mode even if there is only one node running. The performance impact is insignificant and you will have the opportunity to connect mode cluster nodes at any time without restarting the server.\r\n\r\n**Default value:** ``false`` Tigase by default does not run in clustered mode.\r\n\r\n**Example:** ``'cluster-mode' = 'true'``\r\n\r\n**Possible values:** ``true|false``\r\n\r\n**Available since:** 8.0.0\r\n\r\n.. _clusterNodes:\r\n\r\ncluster-nodes\r\n^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** none\r\n\r\n**Example:** ``'cluster-nodes' = [ 'node1.domain:pass:port' , 'node2.domain:pass:port' , 'node3.domain:pass:port' ]``\r\n\r\n**Possible values:** a comma separated list of hostnames.\r\n\r\n**Description:** The property is used to specify a list of cluster nodes running on your installation. The node is the full DNS name of the machine running the node. Please note the proper DNS configuration is critical for the cluster to work correctly. Make sure the 'hostname' command returns a full DNS name on each cluster node. Nodes don’t have to be in the same network although good network connectivity is also a critical element for an effective cluster performance.\r\n\r\nAll cluster nodes must be connected with each other to maintain user session synchronization and exchange packets between users connected to different nodes. Therefore each cluster node opens a 'cluster port' on which it is listening for connections from different cluster nodes. As there is only one connection between each two nodes Tigase server has to decide which nodes to connect to and which has to accept the connection. If you put the same list of cluster nodes in the configuration for all nodes this is not a problem. Tigase server has a way to find and void any conflicts that are found. If you however want to add a new node later on, without restarting and changing configuration on old nodes, there is no way the old nodes will try to establish a connection to the new node they don’t know them. To solve this particular case the next parameter is used.\r\n\r\n.. Note::\r\n\r\n   Cluster nodes are not required to be configured, as they can automatically find/add/remove cluster nodes. This is for installations where nodes will be limited and static!\r\n\r\n**Available since:** 8.0.0\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Properties/_external.inc",
    "content": "External\r\n---------------\r\n\r\n.. _bindExtHostnames:\r\n\r\nbind-ext-hostnames\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** none\r\n\r\n**Example:** ``'bind-ext-hostnames' = [ 'pubsub.host.domain' ]``\r\n\r\n**Possible values:** comma separated list of domains.\r\n\r\n**Description:** This property enables setting a list of domains to be bound to the external component connection. Let’s say we have a Tigase instance with only MUC and PubSub components loaded and we want to connect this instance to the main server via external component protocol. Using `--external property <#external>`__ we can define a domain (perhaps muc.devel.tigase.org), password, TCP/IP port, remote host address, connection type, etc…​ This would make one of our components (MUC) visible on the remote server.\r\n\r\nTo make the second component (PubSub) visible we would need to open another connection with the domain name (pubsub.devel.tigase.org) for the other component. Of course the second connection is redundant as all communication could go through a single connection. This is what this property is used. In our example with 2 components you can just put the 'pubsub.devel.tigase.org' domain as a value to this property and it will bind the second domain to a single connection on top of the domain which has been authenticated during protocol handshaking.\r\n\r\n**Available since:** 5.0.0\r\n\r\n.. _virtHosts:\r\n\r\ndefault-virtual-host\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Description:** The ``default-virtual-host`` property allows setting of the name of default virtual host that is served by the installation. It is loaded during startup of the application and stored in the database. **It may only contain single domain name!**\r\n\r\nAny additional configuration options or additional virtual hosts domains should be added and configured using ad-hoc commands such as ``Add new item``, ``Update item configuration`` and ``Remove an item`` available at the JID of the ``VHostManager`` component of your installation (``vhost-man@your-server-domain``).\r\n\r\n**Available since:** 8.0.0\r\n\r\next\r\n^^^^^^^^^\r\n\r\n**Description:** This property defines parameters for external component connections.\r\n\r\nThe component is loaded the same way as all other Tigase components. In your ``config.tdsl`` file you need to add the external class:\r\n\r\n.. code::\r\n\r\n   ext (class: tigase.server.ext.ComponentProtocol) {}\r\n\r\nThis will load the component with an empty configuration and is practically useless. You have to tell the component on what port to listen to (or on what port to connect to) and external domains list with passwords.\r\n\r\nThose values need to be configured while the Tigase XMPP Server is running using XMPP ad-hoc commands such as ``Add new item``, ``Update item configuration`` and ``Remove an item`` available at the JID of the external component which you have just enabled (``ext@your-server-domain``).\r\n\r\n**Possible values:** external domains parameters list.\r\n\r\n**Available since:** 4.3.0\r\n\r\n**Removed in:** 8.0.0"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Properties/_general.inc",
    "content": "General\r\n--------\r\n\r\n.. _admins:\r\n\r\nadmins\r\n^^^^^^^^^\r\n\r\n**Description:** Specifies a list of administrator accounts.\r\n\r\n**Default value:** the administration account created when the server is setup. Typically it would be something like ``admins = ['admin@server.com']``.\r\n\r\n**Example:** ``admins = [ 'admin@domain.com', 'user2@domain.com' ]``\r\n\r\n**Possible values:** Comma seperated values of Bare JIDs.\r\n\r\n**Available since:** 2.0.0\r\n\r\nCertificate Container\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThe certificate container houses all configuration related to SSL certificate configuration. This container replaces a number of former — properties.\r\n\r\nssl-certs-location\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThis option allows you to specify the location where SSL certificates are stored. The meaning of this property depends on the SSL container :ref:`class implementation<sslContainerClass>`. By default it just points to the directory where the server SSL certificates are stored in files in PEM format.\r\n\r\nDefault location is ``/certs`` however it can be changed using the following setting:\r\n\r\n.. code::\r\n\r\n   }\r\n   'certificate-container' {\r\n       'ssl-certs-location' = '/etc/vhost-certs'\r\n   }\r\n\r\nThis replaces the former ``--ssl-certs-location`` property.\r\n\r\nssl-def-cert-domain\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThis property allows you to specify a default alias/domain name for certificate. It is mostly used to load certificates for unknown domain names during the SSL negotiation. Unlike in TLS protocol where the domain name is known at the handshaking time, for SSL domain name is not known, therefore, the server does not know which certificate to use. Specifying a domain name in this property allows you to use a certificate for a specific domain in such case. This property value is also sometimes used if there is no certificate for one of virtual domains and the container does not automatically generate a self-signed certificate, then it can use a default one.\r\n\r\nThis may be configured as follows:\r\n\r\n.. code::\r\n\r\n   }\r\n   'certificate-container' {\r\n       'ssl-def-cert-domain' = 'some.domain.com'\r\n   }\r\n\r\nThis replaces the former ``--ssl-def-cert-domain`` property.\r\n\r\nComponent\r\n^^^^^^^^^^^^^^^^^^\r\n\r\n**Description:** Container specifying component configuration. All components if they require configuration must be called in the conf.tdsl file in the following manner:\r\n\r\n.. code::\r\n\r\n   componentName (class: value) {\r\n       <configuration>\r\n   }\r\n\r\nDSL allows for custom naming of the component, and specifying of the class in the same line. This method replaces the old ``comp-class`` and ``comp-name`` style of configuration.\r\n\r\nFor example, what used to be\r\n\r\n.. code::\r\n\r\n   --comp-name-1 = socks5\r\n   --comp-class-1 = tigase.socks5.Socks5Component\r\n   --comp-name-2 = stun\r\n   --comp-class-2 = tigase.stun.StunComponent\r\n\r\nis now\r\n\r\n.. code::\r\n\r\n   socks5 (class: tigase.socks5.Socks5Component) {}\r\n   stun (class: tigase.stun.StunComponent) {}\r\n\r\nIn fact, if you are using the default class & name for a component, you don’t need to specify it either, so MUC in this is now called by\r\n\r\n.. code::\r\n\r\n   socks5 () {}\r\n\r\n**Default value:** By default, component configuration runs of default, and does not need to be specified.\r\n\r\nThere are many many configuration options under each component, which are specified in :ref:`component documentation<Components>`.\r\n\r\nPorts\r\n^^^^^^^^^\r\n\r\nThe ports property is a subclass of connections, which is used to set a ports list for a connection manager. 'list of ports' is a comma separated list of ports numbers. For example for the server to server connection manager named s2s the property would like like the example below:\r\n\r\n.. code::\r\n\r\n   s2s {\r\n       connections {\r\n           ports = [ 5290, 5291 ]\r\n       }\r\n   }\r\n\r\nEach port many be individually configured underneath ports\r\n\r\n.. code::\r\n\r\n   s2s {\r\n       connections {\r\n           ports = [ 5290, 5291 ]\r\n           5291 {\r\n               type = 'accept'\r\n           }\r\n       }\r\n   }\r\n\r\nthis replaces the ``--cmpname-ports`` property.\r\n\r\n**Available since:** 8.0.0\r\n\r\nconfig-type\r\n^^^^^^^^^^^^^^^^^^\r\n\r\n**Description:** This property sets the server type and determines what components are started up without needing to declare and configure all components. Possible values are listed below:\r\n\r\n-  ``setup`` - This setting will setup a basic server that is prepared for initial setup after unpacking. This is set by default, and starts up http component as well as basic server components. This should be changed after the server is configured.\r\n\r\n-  ``default`` - creates default configuration file. That is configuration which is most likely needed for a typical installation. Components included in configuration are: session manager, client-to-server connection manager and server-to-server connection manager.\r\n\r\n-  ``session-manager`` - creates configuration for instance with session manager and external component only. This is useful for distributed installation where you want to have session manager installed on separate machine and components managing network connections on different machines (one or more). Components included in configuration are: sm and ext2s.\r\n\r\n-  ``connection-managers`` - creates configuration for instance with components managing network connections. This is useful for distributed installation where you want to have session manager installed on separate machine and components managing network connections on different machines (one or more). Components included in configuration are: c2s, s2s, ext2s.\r\n\r\n-  ``component`` - generating a configuration with only one component - component managing external components connection, either XEP-0114 or XEP-0225. This is used to deploy a Tigase instance as external component connecting to the main server. You have to add more components handled by this instance, usually these are MUC, PubSub or any other custom components. You have to configure the external component connection, domain name, password, port, etc…​\r\n\r\n**Default value:** ``'config-type' = 'setup'``\r\n\r\n**Possible values:** ``setup``\\ \\|\\ ``default``\\ \\|\\ ``connection-managers``\\ \\|\\ ``session-manager``\\ \\|\\ ``connection-managers``\\ \\|\\ ``component``\r\n\r\n**Available since:** 2.0.0\r\n\r\n.. _debugPackages:\r\n\r\ndebug-packages\r\n^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** No default as Tigase does not expect custom classes out of the box.\r\n\r\n**Example:** ``'debug-packages' = [ 'com.company.CustomPlugin' , 'com.company.custom' ]``\r\n\r\n**Possible values:** comma separated list of Java packages or classes.\r\n\r\n**Description:** This property is used to turn debugging on for any package not located within the default Tigase packages. Be sure class case is correct.\r\n\r\n**Available since:** 5.0.0\r\n\r\ndebug\r\n^^^^^^^^^\r\n\r\n**Description:** The ``debug`` property is used to turn on the debug log for the specified Tigase package. For example if you want to turn debug logs on for the ``tigase.server`` package, then you have to use the ``server`` parameter. If you have any problems with your server the best way to get help from the Tigase team is to generate configuration with this enabled at a minimum and run the server. Then from the ``logs/tigase-console.log`` log file I can provide the best information for us to provide assistance. More details about server logging and adjusting logging level is described in the Debugging Tigase article in the admin guide. If you wish to debug packages not compiled with Tigase, use the :ref:`debug-packages<debugPackages>` setting.\r\n\r\n**Default value:** 'none'\r\n\r\n**Example:** ``debug = [ 'server', 'xmpp.impl' ]``\r\n\r\n**Possible values:** Comma separated list of Tigase’s package names.\r\n\r\n**Available since:** 2.0.0\r\n\r\nmonitoring\r\n^^^^^^^^^^^^^^^^^^\r\n\r\n**Description:** This property activates monitoring interfaces through selected protocols on selected TCP/IP port numbers. For more details please refer to the :ref:`monitoring guide<serverMonitoring>` in the user guide for details. Each monitoring protocol should be called in it’s own child bean under ``monitoring ()``. If a protocol is not specified, monitoring under that will not be available.\r\n\r\n**Default value:** By default monitoring is disabled.\r\n\r\n**Example:**\r\n\r\n.. code::\r\n\r\n   monitoring () {\r\n       http () {\r\n           port = '9080'\r\n       }\r\n       jmx () {\r\n           port = '9050'\r\n       }\r\n       snmp () {\r\n           port = '9060'\r\n       }\r\n   }\r\n\r\n.. Warning::\r\n\r\n    DO NOT CONFUSE monitoring with monitor component.\r\n\r\n**Possible values:** 'list of monitoring protocols with port numbers.'\r\n\r\n**Available since:** 8.0.0\r\n\r\nplugins\r\n^^^^^^^^^^^^^^^^^^\r\n\r\n**Description:** The former ``--sm-plugins`` property has been replaced by a new style of formatting with DSL. The former long unbroken string of plusses and minuses have been replaced by a compartmentalized style. Plugins controlled under session manager will now be children of the ``'sess-man'`` bean. For example, to turn on the personal eventing protocol, the following may be used:\r\n\r\n.. code::\r\n\r\n   'sess-man' () {\r\n       pep ()\r\n   }\r\n\r\nShould any plugin require configuration, those configurations will be under it’s own brackets. For example, this section not only turns on jabber:iq:auth but also sets the treads to 16.\r\n\r\n.. code::\r\n\r\n   'sess-man' () {\r\n       'jabber:iq:auth' () {\r\n           threadsNo = 16\r\n       }\r\n   }\r\n\r\nAs you may have noticed, beans or configuration options that require escape characters such as ``:`` or ``-`` will fall into single quotes to contain any special characters. If no special characters are in the bean name, then no single quotes are not required. If you need to disable certain plugins, you can do so after declaring the bean.\r\n\r\n.. code::\r\n\r\n   'sess-man' () {\r\n       pep (active: false) {}\r\n   }\r\n\r\nTypically if a bean is called, it is automatically active. Session manager plugins will typically look like a list of plugins without configurations. The example section will show what one will look like.\r\n\r\n**Default value:** 'none'\r\n\r\n**Example:**\r\n\r\n.. code::\r\n\r\n   'sess-man' () {\r\n       'version' () {}\r\n       amp () {}\r\n       'basic-filter' () {}\r\n       'domain-filter' () {}\r\n       'http:' {\r\n            {\r\n               'jabber.org' {\r\n                   protocol {\r\n                       commands () {}\r\n                       stats () {}\r\n                   }\r\n               }\r\n           }\r\n       }\r\n       'jabber:iq:auth' () {\r\n           threadsNo = 16\r\n       }\r\n       'jabber:iq:privacy' () {}\r\n       'jabber:iq:private' () {}\r\n       'jabber:iq:register' () {}\r\n       'jabber:iq:roster' () {}\r\n       'message-archive-xep-0136' () {}\r\n       msgoffline (active: false) {}\r\n       pep () {}\r\n       'presence-state' () {}\r\n       'presence-subscription' () {}\r\n       starttls () {}\r\n       'urn:ietf:params:xml:ns:xmpp-bind' () {}\r\n       'urn:ietf:params:xml:ns:xmpp-sasl' () {}\r\n       'urn:ietf:params:xml:ns:xmpp-session' () {}\r\n       'urn:xmpp:ping' () {}\r\n       'vcard-temp' () {}\r\n       zlib () {}\r\n   }\r\n\r\n**Possible values:** DSL format plugins list and configurations.\r\n\r\n**Available since:** 8.0.0\r\n\r\npriority-queue-implementation\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``tigase.util.PriorityQueueRelaxed``\r\n\r\n**Example:** ``'priority-queue-implementation' = 'tigase.util.PriorityQueueStrict``\r\n\r\n**Possible values:** class name extending ``tigase.util.PriorityQueueAbstract``.\r\n\r\n**Description:** The ``priority-queue-implementation`` property sets Tigase’s internal queue implementation. You can choose between already available and ready to use or you can create own queue implementation and let Tigase load it instead of the default one. Currently following queue implementations are available:\r\n\r\n1. **tigase.util.workqueue.PriorityQueueRelaxed** - specialized priority queue designed to efficiently handle very high load and prevent packets loss for higher priority queues. This means that sometimes, under the system overload packets may arrive out of order in cases when they could have been dropped. Packets loss (drops) can typically happen for the lowest priority packets (presences) under a very high load.\r\n\r\n2. **tigase.util.workqueue.PriorityQueueStrict** - specialized priority queue designed to efficiently handle very high load but prefers packet loss over packet reordering. It is suitable for systems with a very high load where the packets order is the critical to proper system functioning. This means that the packets of the same priority with the same source and destination address are never reordered. Packets loss (drops) can typically happen for all packets with the same probability, depending which priority queue is overloaded.\r\n\r\n3. **tigase.util.workqueue.NonpriorityQueue** - specialized non-priority queue. All packets are stored in a single physical collection, hence they are never reordered. Packets are not prioritized, hence system critical packets may have to wait for low priority packets to be processed. This may impact the server functioning and performance in many cases. Therefore this queue type should be chosen very carefully. Packets of the same type are never reordered. Packets loss (drops) can typically happen for all packets which do not fit into the single queue.\r\n\r\n.. Note::\r\n\r\n   *Since the packets are processed by plugins in the SessionManager component and each plugin has own thread-pool with own queues packet reordering may happen regardless what queue type you set. The reordering may only happen, however between different packet types. That is 'message' may take over 'iq' packet or 'iq' packet may take over 'presence' packet and so on…​ This is unpredictable.*\r\n\r\n**Available since:** 5.1.0\r\n\r\nroster-implementation\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``RosterFlat.class.getCanonicalName()``\r\n\r\n**Example:** ``'roster-implementation' = 'my.pack.CustomRosterImpl'``\r\n\r\n**Possible values:** Class extending tigase.xmpp.impl.roster.RosterAbstract.\r\n\r\n**Description:** The ``roster-implementation`` property allows you to specify a different RosterAbstract implementation. This might be useful for a customized roster storage, extended roster content, or in some cases for some custom logic for certain roster elements.\r\n\r\n**Available since:** 5.2.0\r\n\r\ns2s-secret\r\n^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``none``\r\n\r\n**Example:**\r\n\r\n.. code::\r\n\r\n   'vhost-man' {\r\n       defaults {\r\n           's2s-secret' = 'some-s2s-secret'\r\n       }\r\n   }\r\n\r\n**Possible values:** 'ascii string.'\r\n\r\n**Description:** This property is a global setting for s2s secrets to generate dialback keys on the Tigase installation. By default it is null, which means the secret is automatically generated for each s2s connection and handshake.\r\n\r\nThis is a global property which is overridden by settings for each VHost (see :ref:`Add and Manage Domains (VHosts)<addManageDomain>`)\r\n\r\nAs in the example provided, 'defaults' settings for all virtual hosts for which the configuration is not defined. This settings is useful mostly for installations with many virtual hosts listed in the init.property file for which there is no individual settings specified. It allows to configure a default values for all of them, instead of having to provide individual configuration for each vhost.\r\n\r\n**Available since:** 5.2.0\r\n\r\nscripts-dir\r\n^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``scripts/admin``\r\n\r\n**Example:** ``'scripts-dir' = ''/opt/admin-scripts'``\r\n\r\n**Possible values:** path to a directory on the file system.\r\n\r\n**Description:** This property sets the directory where all administrator scripts for ad-hoc commands are stored.\r\n\r\n**Available since:** 4.3.0\r\n\r\n.. _sslContainerClass:\r\n\r\nssl-container-class\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n*Default value:** ``tigase.io.SSLContextContainer``\r\n\r\n**Example:** ``rootSslContextContainer (class: class.implementing.SSLContextContainerIFC) {}``\r\n\r\n**Possible values:** a class implementing tigase.io.SSLContectContainerIfc.\r\n\r\n**Description:** The ``rootSslContextContainer`` property allows you to specify a class implementing storage for SSL/TLS certificates. The class presented in the example to this description allows for loading certificates from PEM files which is a common storage used on many systems.\r\n\r\n**Available since:** 5.0.0\r\n\r\nstats\r\n^^^^^^^^^\r\n\r\nThe stats block contains settings for statistics collection. To begin the stats block, use the following:\r\n\r\n.. code::\r\n\r\n   stats {}\r\n\r\n**Default value:** 'By default, stats is not listed in the ``config.tdsl`` file'\r\n\r\n**Description**\r\n\r\nTigase XMPP Server can store server statistics internally for a given period of time. This allows you to connect to a running system and collect all the server metrics along with historic data which are stored on the server. This is very useful when something happens on your production system you can connect and see when exactly this happened and what other metrics looked around this time. **Please be aware that Tigase XMPP Server produces about 1,000 different metrics of the system. Therefore caching large number of statistics sets requires lots of memory.**\r\n\r\nstats-history-size\r\n~~~~~~~~~~~~~~~~~~~~\r\n\r\nStats-history defines the size of the history buffer. That is how many complete sets of historic metrics to store in memory.\r\n\r\n.. code::\r\n\r\n   stats {\r\n       'stats-history-size' = '2160'\r\n   }\r\n\r\nstats-history-interval\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nSets the interval for which statistics will be gathered from the server.\r\n\r\n.. code::\r\n\r\n   stats {\r\n       'stats-history-interval' = '10'\r\n   }\r\n\r\n\r\nstats-logger\r\n~~~~~~~~~~~~~~~~~~~~\r\n\r\nAllow enabling and configuring components responsible for storing statistic information. Note that this controls the logging system for retrieving using JMX, clients, or ad-hoc commands.\r\n\r\n.. code::\r\n\r\n   stats {\r\n       'stats-logger' (class: value) {\r\n           <other settings>\r\n       }\r\n   }\r\n\r\nCurrently following classes are available:\r\n\r\n-  ``tigase.stats.CounterDataArchivizer`` - every execution put current basic server metrics (CPU usage, memory usage, number of user connections, uptime) into database (overwrites previous entry)\r\n\r\n-  ``tigase.stats.CounterDataLogger`` - every execution insert new row with new set of number of server statistics (CPU usage, memory usage, number of user connections per connector, number of processed packets of different types, uptime, etc) into the database\r\n\r\n-  ``tigase.stats.CounterDataFileLogger`` - every execution store all server statistics into separate file.\r\n\r\nfrequency\r\n\r\nstats-logger may also be controlled using frequency, which is the time interval between executions of the archiver ``.execute()`` method in seconds.\r\n\r\n.. code::\r\n\r\n   stats {\r\n       'stats-logger' (class: tigase.stats.CounterDataLogger) {\r\n           repository() {\r\n               'default'() {\r\n                   'data-source' = 'default';\r\n               }\r\n           }\r\n           frequency = '60'\r\n       }\r\n   }\r\n\r\n\r\nstats-file-logger\r\n~~~~~~~~~~~~~~~~~~~~\r\n\r\nThis allows configuring of statistics gathering to an external file. This only has one class, and may be controlled independently from the internal statistics.\r\n\r\n.. code::\r\n\r\n   stats {\r\n       'stats-file-logger' (class: tigase.stats.CounterDataFileLogger) {\r\n           <other settings>\r\n       }\r\n   }\r\n\r\n\r\nfrequency\r\n~~~~~~~~~~~~~~~~~~~~\r\n\r\nstats-file-logger may also be controlled using frequency, which is the time interval between executions of the archiver ``.execute()`` method in seconds.\r\n\r\n.. code::\r\n\r\n   stats {\r\n       'stats-file-logger' (class: tigase.stats.CounterDataFileLogger) {\r\n           frequency = '60'\r\n       }\r\n   }\r\n\r\n\r\nfile configuration\r\n\r\nYou can customize the file output for stats-file-logger using the following setting options, these are all optional.\r\n\r\n.. code::\r\n\r\n   stats {\r\n       'stats-history-size' = '2160'\r\n       'stats-update-interval' = '10'\r\n       'stats-file-logger' (class: tigase.stats.CounterDataFileLogger) {\r\n           frequency = '60'\r\n           'stats-datetime' = 'true'\r\n           'stats-datetime-format' = 'HH:mm:ss'\r\n           'stats-directory' = 'logs/server_statistics'\r\n           'stats-filename' = 'stat'\r\n           'stats-level' = 'FINEST'\r\n           'stats-unixtime' = 'false'\r\n\r\n-  **'stats-datetime'** - Whether to include date & time timestamp.\r\n\r\n-  **'stats-datetime-format'** - Specifies the formatting of datetime timestamp.\r\n\r\n-  **'stats-directory'** - The directory to which the statistics file should be saved.\r\n\r\n-  **'stats-filename'** - The filename prefix to name the output statistics file.\r\n\r\n-  **'stats-level'** - Sets the level of statistics to be gathered.\r\n\r\n-  **'stats-unixtime'** - Control the format of the timestamp to use java DateFormat pattern.\r\n\r\nwhich configures accordingly: directory to which files should be saved, filename prefix, whether to include or not unix timestamp in filename, whether to include or not datetime timestamp, control format of timestamp (using java DateFormat pattern) and also set level of the statistics we want to save (using java Logger.Level)\r\n\r\nDatabase logger\r\n^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThis allows configuring of statistics gathering to a database. Without additional configuration ``default`` data source will be used but it’s possible to store statistics in any database - simply define new data source and configure logger with it’s name.\r\n\r\n   **Note**\r\n\r\n   After enabling the component it’s database schema should be loaded by executing ``./scripts/tigase.sh upgrade-schema etc/tigase.conf`` from the main Tigase directory\r\n\r\n.. code::\r\n\r\n   stats {\r\n       'stats-logger' (class: tigase.stats.CounterDataLogger) {\r\n           repository() {\r\n               'default'() {\r\n                   'data-source' = 'customDataSourceName';\r\n               }\r\n           }\r\n           frequency = '60'\r\n       }\r\n   }\r\n\r\n\r\nExample configuration block\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n.. code::\r\n\r\n   stats {\r\n       'stats-history-size' = '2160'\r\n       'stats-update-interval' = '10'\r\n       'stats-file-logger' (class: tigase.stats.CounterDataFileLogger) {\r\n           frequency = '120'\r\n           'stats-datetime' = 'false'\r\n           'stats-datetime-format' = 'HH:mm:ss'\r\n           'stats-directory' = 'logs/statistics'\r\n           'stats-filename' = 'output'\r\n           'stats-level' = 'WARNING'\r\n           'stats-unixtime' = 'true'\r\n       }\r\n       'stats-logger' (class: tigase.stats.CounterDataLogger) {\r\n           repository() {\r\n               'default'() {\r\n                   'data-source' = 'default';\r\n               }\r\n           }\r\n           frequency = '60'\r\n       }\r\n   }\r\n\r\n**Available since:** 8.0.0\r\n\r\n.. _stream-error-counter:\r\n\r\nstream-error-counter\r\n^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Description:** Add stream-error-counter to comma separated processors of components for which you wish to count the number of stream errors made. Without enabling this, statistics will return 0. This setting turns on stream-error-counter for both c2s and ws2s:\r\n\r\n.. code::\r\n\r\n   c2s {\r\n       'stream-error-counter' () {\r\n           active = true\r\n       }\r\n   }\r\n   ws2s {\r\n       'stream-error-counter' () {\r\n           active = true\r\n         }\r\n   }\r\n\r\nYou may if you wish turn off stream error counters by setting ``active = false``.\r\n\r\n**Default value:** Stream error counters are not turned on by default, thus no default value is set.\r\n\r\n**Example:**\r\n\r\n.. code::\r\n\r\n   <component> {\r\n       'stream-error-counter' () {\r\n           active = true\r\n       }\r\n\r\n**Available since:** 7.1.0\r\n\r\nstringprep-processor\r\n^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Description:** The ``'stringprep-processor'`` property sets the stringprep processor for all JIDs handled by Tigase. The default 'simple' implementation uses regular expressions to parse and check the user JID. Although it does not fulfill the RFC-3920 requirements, it also puts much less stress on the server CPU, hence impact on the performance is very low.\r\n\r\nOther possible values are:\r\n\r\n``'libidn'`` - provides full stringprep processing exactly as described in the RFC-3920. It requires lots of CPU power and significantly impacts performance.\r\n\r\n``'empty'`` - doesn’t do anything to JIDs. JIDs are accepted in the form they are received. No impact on the performance and doesn’t use any CPU. This is suitable for use in automated systems where JIDs are generated by some algorithm, hence there is no way incorrect JIDs may enter the system.\r\n\r\n**Default value:** ``simple``\r\n\r\n**Example:** ``'stringprep-processor' = 'libidn'``\r\n\r\n**Possible values:** ``simple|libidn|empty``\r\n\r\n**Available since:** 8.0.0\r\n\r\ntest\r\n^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** By default test mode is disabled.\r\n\r\n**Description:** This property sets the server for test mode, which means that all logging is turned off, offline message storage is off, and possibly some other changes to the system configuration are being made.\r\n\r\nThe idea behind this mode is to test Tigase XMPP Server with minimal performance impact from environment such as hard drive, database and others…​\r\n\r\nTest function has been replaced by the following setting:\r\n\r\n.. code::\r\n\r\n   logging {\r\n       rootLevel = 'WARNING'\r\n   }\r\n\r\n**Available since:** 8.0.0\r\n\r\ntls-jdk-nss-bug-workaround-active\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``false``\r\n\r\n**Example:** ``'tls-jdk-nss-bug-workaround-active' = true``\r\n\r\n**Possible values:** ``true|false``\r\n\r\n**Description:** This is a workaround for TLS/SSL bug in new JDK7 using the native library for keys generation and connection encryption used with new version of nss library.\r\n\r\nThis caused a number of problems with Tigase installed on systems with JDK7 and the new library installed, such as hanging connections, or broken SSL/TLS. Our earlier suggestion was to avoid using either JDK7 or the problematic native library. Now we have a proper fix/workaround which allows you to run Tigase with JDK7.\r\n\r\n-  http://stackoverflow.com/q/10687200/427545\r\n\r\n-  http://bugs.sun.com/bugdatabase/view_bug.do;jsessionid=b509d9cb5d8164d90e6731f5fc44?bug_id=6928796\r\n\r\nNote, while this setting is still supported, the issues mentioned above are fixed in v8 JDK.\r\n\r\n**Available since:** 8.0.0\r\n\r\ntrusted\r\n^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``none``\r\n\r\n**Example:** ``trusted = [ 'user@domain.com' , 'user-2@domain2.com' ]``\r\n\r\n**Possible values:** comma separated list of user bare JIDs.\r\n\r\n**Description:** The ``trusted`` property allows users to specify a list of accounts which are considered as trusted, thus whom can perform some specific actions on the server. They can execute some commands, send a broadcast message, set MOTD and so on. The configuration is similar to :ref:`admins<admins>` setting.\r\n\r\n**Available since:** 8.0.0"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Properties/_performance.inc",
    "content": "Performance\r\n-------------------\r\n\r\ncm-ht-traffic-throttling\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``xmpp:25k:0:disc,bin:200m:0:disc``\r\n\r\n**Example:** ``'cm-ht-traffic-throttling' = 'xmpp:25k:0:disc,bin:200m:0:disc'``\r\n\r\n**Possible values:** comma separated list of traffic limits settings.\r\n\r\n**Description:** This property is used to specify traffic limit of non-user connections, that is s2s, external components and other high traffic server connections. The meaning of the property and values encoded are in the same way as for the :ref:`cm-traffic-throttling property<cmTrafficThrottling>`.\r\n\r\n**Available since:** 8.0.0\r\n\r\n.. _cmTrafficThrottling:\r\n\r\ncm-traffic-throttling\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``xmpp:2500:0:disc,bin:20m:0:disc``\r\n\r\n**Example:** ``'cm-traffic-throttling' = 'xmpp:2500:0:disc,bin:20m:0:disc'``\r\n\r\n**Possible values:** comma separated list of traffic limits settings.\r\n\r\n**Description:** The ``cm-traffic-throttling`` property allows you to limit traffic on user connections. These limits are applied to each user connection and if a limit is exceeded then a specified action is applied.\r\n\r\nThe property value is a comma separated list of traffic limits settings. For example the first part: ``xmpp:2500:0:disc`` specifies traffic limits for XMPP data to 2,500 packets allowed within last minute either sent to or received from a user and unlimited (0) total traffic on the user connection, in case any limit is exceeded the action is to **disconnect** the user.\r\n\r\n-  **[xmpp|bin]** traffic type, xmpp - XMPP traffic, that is limits refer to a number of XMPP packets transmitted, bin - binary traffic, that is limits refer to a number of bytes transmitted.\r\n\r\n-  **2500** maximum traffic allowed within 1 minute. 0 means unlimited, or no limits.\r\n\r\n-  **0** maximum traffic allowed for the life span of the connection. 0 means unlimited or no limits.\r\n\r\n-  **[disc|drop]** action performed on the connection if limits are exceeded. disc - means disconnect, drop - means drop data.\r\n\r\n**Available since:** 5.1.3\r\n\r\nelements-number-limit\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``1000``\r\n\r\n**Possible values:** any integer.\r\n\r\n**Description:** ``elements-number-limit`` configuration property allows configuring a Denial of Service protection mechanism which limits number of elements sent in stanza. It must be configured on a per ConnectionManager basis:\r\n\r\n.. code:: bash\r\n\r\n   '<ConnectionManager>' {\r\n       'elements-number-limit' = ###\r\n   }\r\n\r\nfor example (for ClusterConnectionManager):\r\n\r\n.. code:: bash\r\n\r\n   'cl-comp' {\r\n       'elements-number-limit' = 100000\r\n\r\n**Available since:** 5.2.0\r\n\r\n.. _hardenedMode:\r\n\r\nhardened-mode\r\n^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``secure``\r\n\r\n**Example:** ``'hardened-mode' = secure``\r\n\r\n**Possible values:** ``relaxed|secure|strict``\r\n\r\n**Description:** Adjusting hardened mode affects handling of security aspects within Tigase. The higher the level the more strict are the rules: \\* ``relaxed`` - uses default security capabilities from installed JVM; \\* ``secure`` - disables old SSLv2 and SSLv3, disables weak cyphers; \\* ``strict`` - in addition to ``secure`` level changes it also disables ``TLSv1`` and ``TLSv1.1`` as well as ciphers that don’t support Forward secrecy.\r\n\r\nOn older JVM versions it required `UnlimitedJCEPolicyJDK <http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html>`__ installed. It’s not required with OpenJDK8 and newer an OracleJVM 11 and newer.\r\n\r\n**Available since:** 5.2.0\r\n\r\nmax-queue-size\r\n^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** default queue size is variable depending on RAM size.\r\n\r\n**Example:** ``'max-queue-size' = 10000``\r\n\r\n**Possible values:** integer number.\r\n\r\n**Description:** The ``max-queue-size`` property sets internal queues maximum size to a specified value. By default Tigase sets the queue size depending on the maximum available memory to the Tigase server process. It set’s 1000 for each 100MB memory assigned for JVM. This is enough for most cases. If you have however, an extremely busy service with Pubsub or MUC component generating huge number of packets (presence or messages) this size should be equal or bigger to the maximum expected number of packets generated by the component in a single request. Otherwise Tigase may drop packets that it is unable to process.\r\n\r\n**Available since:** 5.1.0\r\n\r\n.. _netBuffHighThroughput:\r\n\r\nnet-buff-high-throughput\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``64k``\r\n\r\n**Example:** ``'net-buff-high-throughput' = '256k'``\r\n\r\n**Possible values:** network buffer size as integer.\r\n\r\n**Description:** The ``net-buff-high-throughput`` property sets the network buffer for high traffic connections like s2s or connections between cluster nodes. The default is ``64k`` and is optimal for medium traffic websites. If your cluster installation can not cope with traffic between nodes try to increase this number.\r\n\r\n**Available since:** 4.3.0\r\n\r\n.. _netBuffStandard:\r\n\r\nnet-buff-standard\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``2k``\r\n\r\n**Example:** ``'net-buff-standard' = '16k'``\r\n\r\n**Possible values:** network buffer size as integer.\r\n\r\n**Description:** This property sets the network buffer for standard (usually c2s) connections, default value is 2k and is optimal for most installations.\r\n\r\n**Available since:** 4.3.0\r\n\r\nnet-buffer\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``2k``\r\n\r\n**Example:** ``'net-buffer' = 16 * 1024``\r\n\r\n**Possible values:** internal network buffer size as integer.\r\n\r\n**Description:** Sets default size of a internal network buffer used by ``ConnectionManager`` within the context in which it is set.\r\n\r\n**Available since:** 8.0.0\r\n\r\nsocket-buffer-size\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``4k`` (for client-to-server connections) and ``64k`` (for server-to-server connections)\r\n\r\n**Example:** ``'socket-buffer-size' = 16 * 1024``\r\n\r\n**Possible values:** socket network buffer size as integer.\r\n\r\n**Description:** Sets default size of a socket network buffer used by ``ConnectionManager`` for each connection within the context in which it is set.\r\n\r\n**Available since:** 8.3.0 (previously value of ``net-buffer`` was used)\r\n\r\nnonpriority-queue\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``false``\r\n\r\n**Example:** ``'nonpriority-queue' =  true``\r\n\r\n**Possible values:** ``true|false``\r\n\r\n**Description:** The ``nonpriority`` property can be used to switch to non-priority queues usage in Tigase server (value set to 'true'). Using non-priority queues prevents packets reordering. By default Tigase uses priority queues which means that packets with highest priority may take over packets with lower priority (presence updates) which may result in packets arriving out of order.\r\n\r\nThis may happen however only for packets of different types. That is, messages may take over presence packets. However, one message never takes over another message for the same user. Therefore, out of order packet delivery is not an issue for the most part.\r\n\r\n**Available since:** 5.0.0"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Properties/_properties.rst",
    "content": "Appendix II - Properties Guide\r\n===============================\r\n\r\n.. include:: _general.inc\r\n.. include:: _repository.inc\r\n.. include:: _cluster.inc\r\n.. include:: _user_connectivity.inc\r\n.. include:: _external.inc\r\n.. include:: _performance.inc\r\n.. include:: _vhost.inc\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Properties/_repository.inc",
    "content": "Repository\r\n------------\r\n\r\n**Description:** Container specifying authentication repository. This container replaces the old ``auth-db`` property types, and may contain some other configuration values.\r\n\r\n**Default value:**\r\n\r\n.. code::\r\n\r\n   authRepository {\r\n     <configuration>\r\n   }\r\n\r\nThis is the basic setup for authRepository, where <configuration> settings are global for all authentication databases. However, you may configure multiple databases individually.\r\n\r\n**Example:**\r\n\r\n.. code::\r\n\r\n   authRepository {\r\n       'auth-repo-pool-size' = 50\r\n       domain1.com () {\r\n           cls = 'tigase.db.jdbc.JDBCRepository'\r\n           'data-source' = 'domain1'\r\n       }\r\n       domain2.com () {\r\n           cls = 'tigase.db.jdbc.JDBCRepository'\r\n           'data-source' = 'domain2'\r\n           'auth-repo-pool-size' = 30\r\n       }\r\n   }\r\n\r\n\r\n**Configuration Values:**\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nContainer has the following options\r\n\r\npool-size\r\n~~~~~~~~~~~~\r\n\r\nThis property sets the database connections pool size for the associated ``UserRepository``.\r\n\r\n   **Note**\r\n\r\n   in some cases instead of default for this property setting for :ref:`data-repo-pool-size<dataRepoPoolSize>` is used if pool-size is not defined in ``userRepository``. This depends on the repository implementation and the way it is initialized.\r\n\r\n.. code::\r\n\r\n   authRepository {\r\n       default ()\r\n         'pool-size' = 10\r\n   }\r\n\r\nThis is a global property that may be overridden by individual repository settings:\r\n\r\n.. code::\r\n\r\n   userRepository {\r\n       default () {\r\n         'pool-size' = 10\r\n       }\r\n       special-repo () {\r\n         'pool-size' = 30\r\n       }\r\n   }\r\n\r\n\r\n**cls**\r\n~~~~~~~~~~~~\r\n\r\n| Defines the class used for repository connection. You can use this to specify specific drivers for different DB types.\r\n\r\nUnless specified, the pool class will use the one included with Tigase. You may configure individual repositories in the same way. This replaces the former ``--auth-repo-pool`` property.\r\n\r\n.. Note::\r\n\r\n   File conversion will not remove and convert this property, it **MUST BE DONE MANUALLY**.\r\n\r\n**Available since:** 8.0.0\r\n\r\n.. _dataSource:\r\n\r\nauthRepository\r\n^^^^^^^^^^^^^^^^^\r\n\r\n**Description:** Container specifying repository URIs. This container replaces the old ``auth-db-uri`` and ``user-db-uri`` property types.\r\n\r\n**Default value:**\r\n\r\n.. code::\r\n\r\n   dataSource {\r\n       default () {\r\n           uri = 'jdbc:mysql://localhost/tigasedb?user=tigase&password=tigase12'\r\n   }\r\n\r\nOnce your configuration is setup, you will see the uri of your user database here. If other databases need to be defined, they will be listed in the same dataSource bean.\r\n\r\n**Example:**\r\n\r\n.. code::\r\n\r\n   dataSource {\r\n       default () {\r\n           uri = 'jdbc:mysql://localhost/tigasedb?user=tigase&password=tigase12'\r\n       }\r\n       'default-auth' () {\r\n           uri = 'jdbc:mysql://localhost/tigasedbath?user=tigase&password=tigase12'\r\n       }\r\n   }\r\n\r\n**Possible values:** Broken down list of customized names for DB URIs. Each name must have a defined uri property. DB name can be customized by the bean name.\r\n\r\n.. Note::\r\n\r\n   URI name may be used as shorthand to define DB location URI in other containers, so be sure to name them uniquely.\r\n\r\n.. Note::\r\n\r\n   default () URI setting replaces the ``user-db-uri`` as well as the ``auth-repo-uri`` property.\r\n\r\nMSSQL\r\n^^^^^^^^^\r\n\r\nMSSql support works out of the box, however Tigase provides an open source driver for the database. We recommend using Microsoft’s own driver for better functionality.\r\n\r\n.. code::\r\n\r\n   dataSource () {\r\n       default () {\r\n           uri = 'jdbc:sqlserver://localhost;databaseName=tigasedb;user=tigase_user;password=tigase_pass;schema=dbo;lastUpdateCount=false;cacheMetaData=false'\r\n       }\r\n   }\r\n\r\nWhere the uri is divided as follows: jdbc:<driver>:sqlserver://<server address>;databaseName=<database name>;user=<username for db>;password=<password for db>;schema=dbo;lastUpdateCount=false;cacheMetaData=false We do not recommend modification of schema and onward unless you are explicit in your changes.\r\n\r\nMongoDb\r\n^^^^^^^^^\r\n\r\nFor using mongoDB as the repository, the setting will look slightly different:\r\n\r\n.. code::\r\n\r\n   dataSource () {\r\n       default () {\r\n           uri = 'mongodb://username:password@localhost/dbname'\r\n       }\r\n   }\r\n\r\nMySQL\r\n^^^^^^^^^\r\n\r\nMySQL support works out of the box, however Tigase uses prepared calls for calling procedures accessing data stored in database. While this works very fast, it takes time during Tigase XMPP Server startup to prepare those prepared calls. Since version 8.2.0, it is possible to enable workaround which will force Tigase XMPP Server to use prepared statements instead of prepared calls, that will improve startup time but may have slight impact on performance during execution of queries and disables startup verification checking if stored procedures and function in database exist and have correct parameter types. To enable this mode you need to set ``useCallableMysqlWorkaround`` to ``true``.\r\n\r\n.. code::\r\n\r\n   dataSource {\r\n       default () {\r\n           uri = 'jdbc:mysql://localhost/tigasedb?user=tigase&password=tigase12'\r\n           useCallableMysqlWorkaround = 'true'\r\n       }\r\n   }\r\n\r\n.. _dataRepoPoolSize:\r\n\r\npool-size\r\n^^^^^^^^^^^^^^^^^^\r\n\r\n``DataSource`` is an abstraction layer between any higher level data access repositories such as ``userRepository`` or ``authRepository`` and SQL database or JDBC driver to be more specific. Many implementations use ``DataSource`` for DB connections and in fact on many installations they also share the same DataRepository instance if they connect to the same DB. In this case it is desired to use a specific connection pool on this level to an avoid excessive number of connections to the database.\r\n\r\nTo do so, specify the number of number of database connection as an integer:\r\n\r\n.. code::\r\n\r\n   dataSource {\r\n       default () {\r\n           uri = 'jdbc:mysql://localhost/tigasedb?user=tigase&password=tigase12'\r\n           'pool-size' = '50'\r\n       }\r\n   }\r\n\r\nBy default, the number of connections is 10.\r\n\r\n**Available since:** 8.0.0"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Properties/_user_connectivity.inc",
    "content": "User connectivity\r\n--------------------\r\n\r\nbosh-close-connection\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``false``\r\n\r\n**Example:** ``'bosh-close-connection' = true``\r\n\r\n**Possible values:** ``true|false``\r\n\r\n**Description:** This property globally disables Bosh keep-alive support for Tigase server. It causes the Bosh connection manager to force close the HTTP connection each time data is sent to the Bosh client. To continue communication the client must open a new HTTP connection.\r\n\r\nThis setting is rarely required but on installations where the client cannot control/disable keep-alive Bosh connections and keep-alive does not work correctly for some reason.\r\n\r\n**Available since:** 8.0.0\r\n\r\nbosh-extra-headers-file\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``'etc/bosh-extra-headers.txt'``\r\n\r\n**Example:** ``'bosh-extra-headers-file' = ''/path/to/file.txt'``\r\n\r\n**Possible values:** 'path to a file on the filesystem.'\r\n\r\n**Description:** This property allows you to specify a path to a text file with additional HTTP headers which will be sent to a Bosh client with each request. This gives some extra flexibility for Bosh clients running on some systems with special requirements for the HTTP headers and some additional settings.\r\n\r\nBy default a file distributed with the installation contains following content:\r\n\r\n.. code:: bash\r\n\r\n   Access-Control-Allow-Origin: *\r\n   Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n   Access-Control-Allow-Headers: Content-Type\r\n   Access-Control-Max-Age: 86400\r\n\r\nThis can be modified, removed or replaced with a different content on your installation.\r\n\r\n**Available since:** 8.0.0\r\n\r\nclient-access-policy-file\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``etc/client-access-policy.xml``\r\n\r\n**Example:** ``'client-access-policy-file' = ''/path/to/access-policy-file.xml'``\r\n\r\n**Possible values:** path to a file on the filesystem.\r\n\r\n**Description:** The ``client-access-policy-file`` property allows control of the cross domain access policy for Silverlight based web applications. The cross domain policy is controlled via XML file which contains the policy and rules.\r\n\r\nBy default Tigase is distributed with an example policy file which allows for full access from all sources to the whole installation. This is generally okay for most Bosh server installations. The configuration through the property and XML files allows for a very easy and flexible modification of the policy on any installation.\r\n\r\n**Available since:** 5.2.0\r\n\r\nclient-port-delay-listening\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Description:** The property allows to enabled or disable delaying of listening for client connections **in cluster mode** until the cluster is correctly connected.\r\n\r\n**Default value:** ``true``\r\n\r\n**Example:**\r\n\r\n.. code::\r\n\r\n   <component> {\r\n       'port-delay-listening' = false\r\n     }\r\n\r\n**Possible values:** ``true``, ``false``\r\n\r\nIn cluster mode, in order to ensure correct user status broadcast, we are delaying opening client ports (components: ``c2s``, ``ws2s``, ``bosh``) and enable those only after cluster is fully and correctly connected (i.e. either there is only single node or in case of multiple nodes all nodes connected correctly).\r\n\r\nIt’s possible to enable/disable this on per-component basis with the following configuration:\r\n\r\n.. code::\r\n\r\n   bosh {\r\n       'port-delay-listening' = true\r\n   }\r\n   c2s {\r\n       'port-delay-listening' = true\r\n   }\r\n   ws2s {\r\n       'port-delay-listening' = true\r\n   }\r\n\r\nMaximum delay time depends on the component and it’s multiplication of ``ConnectionManager`` default connection delay times ``30s`` - in case of client connection manager this delay equals 60s.\r\n\r\n.. Note::\r\n\r\n   Only applicable if **Cluster Mode** is active!\r\n\r\n**Available since:** 7.1.0\r\n\r\ncross-domain-policy-file\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``etc/cross-domain-policy.xml``\r\n\r\n**Example:** ``'cross-domain-policy-file' = ''/path/to/cross-domain-policy.xml'``\r\n\r\n**Possible values:** path to a file on the file system.\r\n\r\n**Description:** This property allows you to set a path to a file with cross domain access policy for flash based clients. This is a standard XML file which is sent to the flash client upon request.\r\n\r\nA default file distributed with Tigase installations allows for full access for all. This is good enough for most use cases but it can be changed by simply editing the file.\r\n\r\nThis is a global property that can also be overridden by configuring connection managers [ c2s, s2s, ws2s, bosh, ext, etc] and they may all have their own policies.\r\n\r\n.. code::\r\n\r\n   c2s {\r\n       'cross-domain-policy-file' = '/path/to/cross-domain-policy.xml'\r\n   }\r\n\r\n**Available since:** 5.1.0\r\n\r\ndomain-filter-policy\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``ALL``\r\n\r\n**Example:** ``domain-filter-policy' = 'LOCAL``\r\n\r\n**Possible values:** ``ALL|LOCAL|OWN|BLOCK|LIST=domain1;domain2|BLACKLIST=domain1;domain2``\r\n\r\n**Description:** The ``domain-filter-policy`` property is a global setting for setting communication filtering for vhosts. This function is kind of an extension of the same property which could be set on a single user level. However, in many cases it is desired to control users communication not on per user-level but on the domain level. Domain filtering (communication filtering) allows you to specify with whom users can communicate for a particular domain. It enables restriction of communications for a selected domain or for the entire installation. A default value ``ALL`` renders users for the domain (by default for all domains) able to communicate with any user on any other domains. Other possible values are:\r\n\r\n1. ``ALL`` a default value allowing users to communicate with anybody on any other domain, including external servers.\r\n\r\n2. ``LOCAL`` allows users to communicate with all users on the same installation on any domain. It only blocks communication with external servers.\r\n\r\n3. ``OWN`` allows users to communicate with all other users on the same domain. Plus it allows users to communicate with subdomains such as **muc.domain**, **pubsub.domain**, etc…\r\n\r\n4. ``BLOCK`` value completely blocks communication for the domain or for the user with anybody else. This could be used as a means to temporarily disable account or domain.\r\n\r\n5. ``LIST`` property allows to set a list of domains (users' JIDs) with which users on the domain can communicate (i.e. *whitelist*).\r\n\r\n6. ``BLACKLIST`` - user can communicate with everybody (like ``ALL``), except contacts on listed domains.\r\n\r\nThis is a global property which is overridden by settings for particular VHosts (see :ref:`Add and Manage Domains(VHosts)<addManageDomain>`).\r\n\r\nA default settings for all virtual hosts for which the configuration is not defined. This settings is useful mostly for installations with many virtual hosts listed in the init.property file for which there is no individual settings specified. It allows default value for all of servers, instead of having to provide individual configuration for each vhost.\r\n\r\n``ALL`` is also applied as a default value for all new vhosts added at run-time.\r\n\r\n**Available since:** 5.2.0\r\n\r\nsee-other-host\r\n^^^^^^^^^^^^^^^^^^\r\n\r\n--cmSeeOtherHost has been replaced with using ``seeOtherHost`` setting, and can be configured for each connection manager (c2s, s2s, etc..)\r\n\r\n**Default value:** ``tigase.server.xmppclient.SeeOtherHostHashed``\r\n\r\n**Example:**\r\n\r\n.. code::\r\n\r\n   <connectionManager> {\r\n     seeOtherHost (class: value) { }\r\n   }\r\n\r\n**Possible values:** 'none' 'or class implementing SeeOtherHostIfc.'\r\n\r\n**Description:** Allows you to specify a load balancing mechanism by specifying SeeOtherHostIfc implementation. More details about functionality and implementation details can be found in Tigase Load Balancing documentation.\r\n\r\n**Available since:** 8.0.0\r\n\r\n.. _watchdogTimeout:\r\n\r\nwatchdog_timeout\r\n^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``1740000``\r\n\r\n**Example:** ``watchdog_timeout=60000``\r\n\r\n**Possible values:** ``any integer.``\r\n\r\n**Description:** The ``watchdog_timeout`` property allows for fine-tuning ConnectionManager Watchdog (service responsible for detecting broken connections and closing them). Timeout property relates to the amount of time (in miliseconds) after which lack of response/activity on a given connection will considered such connection as broken an close it. In addition to global configuration presented above a per component configuration is possible:\r\n\r\n.. code::\r\n\r\n   <ConnectionManager> {\r\n       watchdog_timeout = 60000L\r\n   }\r\n\r\nfor example (for C2SConnectionManager):\r\n\r\n.. code::\r\n\r\n   c2s {\r\n       watchdog_timeout = 150000L\r\n   }\r\n\r\nAll related configuration options:\r\n\r\n-  :ref:`watchdog_Ping_Type<watchdogPingType>`\r\n\r\n-  :ref:`watchdog_delay<watchdogDelay>`\r\n\r\n-  watchdog_timeout\r\n\r\n**Available since:** 8.0.0\r\n\r\n.. _watchdogDelay:\r\n\r\nwatchdog_delay\r\n^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``600000``\r\n\r\n**Example:** ``watchdog_delay = '30000'``\r\n\r\n**Possible values:** 'any integer.'\r\n\r\n**Description:** ``watchdog_delay`` configuration property allows configuring delay (in milliseconds) between subsequent checks that ConnectionManager Watchdog (service responsible for detecting broken connections and closing them) will use to verify the connection. In addition to global configuration presented above a per component configuration is possible:\r\n\r\n.. code::\r\n\r\n   <ConnectionManager> {\r\n     watchdog_delay = 60000L\r\n   }\r\n\r\nfor example (for ClusterConnectionManager):\r\n\r\n.. code::\r\n\r\n   'cl-comp' {\r\n       watchdog_delay = 150000L\r\n   }\r\n\r\nAll related configuration options:\r\n\r\n-  :ref:`watchdog_Ping_Type<watchdogPingType>`\r\n\r\n-  watchdog_delay\r\n\r\n-  :ref:`watchdog_timeout<watchdogTimeout>`\r\n\r\n**Available since:** 8.0.0\r\n\r\n.. _watchdogPingType:\r\n\r\nwatchdog_ping_type\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``whitespace``\r\n\r\n**Example:** ``watchdog_ping_type = 'XMPP'``\r\n\r\n**Possible values:** ``WHITESPACE``,\\ ``XMPP``\r\n\r\n**Description:** ``watchdog_ping_type`` configuration property allows configuring of the type of ping that ConnectionManager Watchdog (service responsible for detecting broken connections and closing them) will use to check the connection. In addition to global configuration presented above a per component configuration is possible:\r\n\r\n.. code::\r\n\r\n   <ConnectionManager> {\r\n     watchdog_ping_type = 'XMPP'\r\n   }\r\n\r\nfor example (for ClusterConnectionManager):\r\n\r\n.. code::\r\n\r\n   cl-comp {\r\n       watchdog_ping_type = 'WHITESPACE'\r\n   }\r\n\r\nAll related configuration options:\r\n\r\n-  watchdog_ping_type\r\n\r\n-  :ref:`watchdog_Delay<watchdogDelay>`\r\n\r\n-  watchdog_timeout\r\n\r\n**Available since:** 8.0.0\r\n\r\nws-allow-unmasked-frames\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``false``\r\n\r\n**Example:** ``'ws-allow-unmasked-frames' = true``\r\n\r\n**Possible values:** ``true|false``\r\n\r\n**Description:** RFC 6455 specifies that all clients must mask frames that it sends to the server over Websocket connections. If unmasked frames are sent, regardless of any encryption, the server must close the connection. Some clients however, may not support masking frames, or you may wish to bypass this security measure for development purposes. This setting, when enabled true, will allow connections over websocket to be unmasked to the server, and may operate without Tigase closing that connection.\r\n\r\n**Available since:** 8.0.0"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Properties/_vhost.inc",
    "content": "VHost / domain\r\n--------------------\r\n\r\nvhost-anonymous-enabled\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``true``\r\n\r\n**Example:** ``'vhost-anonymous-enabled' = 'false'``\r\n\r\n**Possible values:** ``true|false``\r\n\r\n**Description:** The ``vhost-anonymous-enabled`` property specifies whether anonymous user logins are allowed for the installation for all vhosts.\r\n\r\nThis is a global property which is overridden by settings for particular VHost (see :ref:`Add and Manage Domains (VHosts)<addManageDomain>`).\r\n\r\nDefault settings for all virtual hosts are used when this property is not defined. This settings is useful mostly for installations with many virtual hosts listed in the ``config.tdsl`` file for which there is no individual settings specified. It allows the configuration of default values for all of them, instead of having to provide individual configuration for each VHost.\r\n\r\n**Available since:** 8.0.0\r\n\r\nvhost-disable-dns-check\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``false``\r\n\r\n**Example:** ``'vhost-disable-dns-check' = 'true'``\r\n\r\n**Possible values:** ``true|false``\r\n\r\n**Description:** This property disables DNS validation when adding or editing vhosts in Tigase server. This also exempts administrative accounts from validation. With this property enabled, you will not benefit from seeing if proper SRV records are set so other people can connect to specific vhosts from outside your network.\r\n\r\nThis is a global property which is overridden by settings for particular VHost (see :ref:`Add and Manage Domains (VHosts)<addManageDomain>`).\r\n\r\n**Available since:** 8.0.0\r\n\r\nvhost-max-users\r\n^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``0``\r\n\r\n**Example:** ``'vhost-max-users' = '1000'``\r\n\r\n**Possible values:** integer number.\r\n\r\n**Description:** The ``vhost-max-users`` property specifies how many user accounts can be registered on the installations for all vhosts.\r\n\r\n**0 - zero** means unlimited and this is a default. Otherwise greater than zero value specifies accounts number limit.\r\n\r\nThis is a global property which is overridden by settings for particular vhost.\r\n\r\nThe default setting is used for all virtual hosts for which the configuration is not defined. This settings is most useful for installations with many virtual hosts listed in the ``init.property`` file for which there is no individual settings specified. It provides an ability to use default values for all of them, instead of having to provide individual configuration for each vhost.\r\n\r\nThis is a global property which is overridden by settings for particular VHost (see :ref:`Add and Manage Domains (VHosts)<addManageDomain>`).\r\n\r\n**Available since:** 8.0.0\r\n\r\nvhost-message-forward-jid\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** <null>\r\n\r\n**Example:** ``'vhost-message-forward-jid' = 'archive@domain.com'``\r\n\r\n**Possible values:** 'valid JID'\r\n\r\n**Description:** This is a global property for message forwarding for the installation. This property is normally specified on the vhost configuration level, however if you want to forward all messages on your installation and you have many virtual domains this property allows to set message forwarding for all of them. A valid JID must be specified as the forwarding destination. Also a message forwarding plugin must be loaded and activated on the installation for the message forwarding to work.\r\n\r\nThe null value is used as a default when no configuration is set. This setting is mostly useful for installations with many virtual hosts listed in the ``init.property`` file for which there is no individual settings specified. It provides the ability to configure a default values for all of them, instead of having to provide individual configuration for each vhost.\r\n\r\nIt is also applied as a default value for all new vhosts added at run-time.\r\n\r\nThis is a global property which is overridden by settings for particular VHost (see :ref:`Add and Manage Domains (VHosts)<addManageDomain>`).\r\n\r\n**Available since:** 8.0.0\r\n\r\nvhost-presence-forward-jid\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``<null>``\r\n\r\n**Example:** ``'vhost-presence-forward-jid' = 'presence-collector@domain.com'``\r\n\r\n**Possible values:** valid JID.\r\n\r\n**Description:** This is a global property for presence forwarding function for the installation. All user status presences will be forwarded to given XMPP address which can be a component or any other XMPP entity. If the destination entity is a bot connected via c2s connection it probably should be addressed via full JID (with resource part) or the standard XMPP presence processing would refuse to deliver presences from users who are not in the contact list.\r\n\r\nThis is a global property which is overridden by settings for particular vhost.\r\n\r\nThe null value is used as a default when no configuration is set. This settings is useful for installations with many virtual hosts listed in the ``init.property`` file for which there is no individual settings specified. It enables the ability to configure default values for all of them, instead of having to provide individual configuration for each vhost.\r\n\r\nIt is also applied as a default value for all new vhosts added at run-time.\r\n\r\nThis may be used on a per-VHost (see `??? <#addManageDomain>`__).\r\n\r\n**Available since:** 8.0.0\r\n\r\nvhost-register-enabled\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``true``\r\n\r\n**Example:** ``'vhost-register-enabled' = false``\r\n\r\n**Possible values:** ``true|false``\r\n\r\n**Description:** ``vhost-register-enabled`` is a global property which allows you to switch on/off user registration on the installation. Setting this property to ``false`` does not disable the registration plugin on the server. You can enable registration for selected domains in the domain configuration settings.\r\n\r\nThis is a global property which is overridden by settings for particular vhost.\r\n\r\nThe ``true`` value is used as a default when no configuration is set. This settings is useful for installations with many virtual hosts listed in the ``init.property`` file for which there is no individual settings specified. It allows admins to configure default values for all of them, instead of having to provide individual configuration for each vhost.\r\n\r\nIt is also applied as a default value for all new vhosts added at run-time.\r\n\r\nThis may be used on a per-VHost (see `??? <#addManageDomain>`__).\r\n\r\n**Available since:** 8.0.0\r\n\r\nvhost-tls-required\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Default value:** ``false``\r\n\r\n**Example:** ``'vhost-tls-required' = true``\r\n\r\n**Possible values:** ``true|false``\r\n\r\n**Description:** This property is a global settings to switch on/off TLS required mode on the Tigase installation. Setting this property to ``false`` does not turn TLS off. TLS is still available on the server but as an option and this is the client’s decision whether to use encryption or not. If the property is set to true the server will not allow for user authentication or sending any other user data before TLS handshaking is completed.\r\n\r\nThis is a global property which is overridden by settings for particular vhost.\r\n\r\nThe ``false`` value is used as a default when no configuration is set. This settings is useful for installations with many virtual hosts listed in the ``init.property`` file for which there is no individual settings specified. It allows admins to configure default values for all of them, instead of having to provide individual configuration for each vhost.\r\n\r\nIt is also applied as a default value for all new vhosts added at run-time.\r\n\r\nThis may be used on a per-VHost (see `??? <#addManageDomain>`__).\r\n\r\n**Available since:** 8.0.0\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc",
    "content": ".. _webinstall:\r\n\r\nInstallation Using Web Installer\r\n-----------------------------------\r\n\r\nWhen Tigase XMPP Server starts up, it looks for the default configuration file: ``etc/config.tdsl``. If this file has not been modified you can run the web installer. Which will step you through the process of configuring Tigase. If you are installing Tigase in a Windows environment, please see the :ref:`Windows Installation<winWebInstall>` section.\r\n\r\n\r\nDownload and Extract\r\n^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nFirst download Tigase XMPP Server and extract it. You can download the `official binaries <https://tigase.net/downloads>`__, or the latest and greatest `nightly builds <https://build.tigase.net/nightlies/dists/>`__. Once you have the distribution binary extract it and navigate to the directory:\r\n\r\n.. code:: bash\r\n\r\n   $ tar -xf tigase-server-<version>-dist-max.tar.gz\r\n   $ cd tigase-server-<version>\r\n\r\n\r\n.. Tip::\r\n\r\n   Do not run as root user!\r\n\r\n\r\nStart the Server\r\n^^^^^^^^^^^^^^^^\r\n\r\n.. Note::\r\n\r\n   Please make sure ``JAVA_HOME`` is set and points to your JVM installation\r\n\r\n.. code:: bash\r\n\r\n   scripts/tigase.sh start etc/tigase.conf\r\n\r\n\r\nVerify Tigase is ready to start installation\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTigase should start listening on port 8080 - you can check it using ``lsof`` command:\r\n\r\n.. code:: bash\r\n\r\n   $ lsof -i -P\r\n   COMMAND   PID   USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME\r\n   java    18387 tigase  141u  IPv6 22185825      0t0  TCP *:8080 (LISTEN)\r\n\r\nYou can also check console log under ``logs/tigase-console.log``, which should point you directly to the installer.\r\n\r\n.. _connecttoWebInstall:\r\n\r\nConnect to the Web Installer\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nSome points before you can connect:\r\n\r\nThis setup page is restricted access, however for first setup there is a default account set to setup Tigase: Username: ``admin`` Password: ``tigase``\r\n\r\nThis combination will only be valid once as it will be removed from ``config.tdsl`` file on completion of setup process. After this point the setup page will only be accessible using the following:\r\n\r\n1. JID accounts listed as administrators in admins line in ``config.tdsl`` file.\r\n\r\n2. Username and password combinations added to ``config.tdsl`` file manually, or at the last page in this process.\r\n\r\nPoint your browser to http://localhost:8080/setup/ unless you are working remotely. You can also use the domain name, or IP address.\r\n\r\nEnter the username and password above to gain access.\r\n\r\n\r\nStep Through the Installation Process\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nYou will be greeted by the following \"About software\" page.\r\n\r\n|web install 01|\r\n\r\nRead it and then click \"Next\"\r\n\r\nThe setup consists of several steps that help you configure your installation: selecting features and providing database configuration.\r\n\r\n.. Note::\r\n\r\n   Order and design of the steps may slightly differ thus we only provide a broad overview of how to proceed:\r\n\r\n1. **Advanced Clustering Strategy information**\r\n\r\n   You will see some information about our commercial products and licensing. Please read though the agreement, and as a confirmation of agreement type in your name or company and click \"Next\" to go to the next page.\r\n\r\n2. **Basic Tigase server configuration**\r\n\r\n   This page will look over your basic configuration settings, those include the server type, domain you wish to use, and gives you a chance to specify an administrator for the domain. Also, you will be selecting what type of database Tigase server will be using (configuration comes later).\r\n\r\n   If you do not specify an administrator and password, one is made for you, which will be admin@yourdomain and password is tigase.\r\n\r\n3. **Connectivity**\r\n\r\n   At this page you will be presented with a list of possible connectivity options supported by Tigase XMPP Server and a way to enable/disable each of them (desktop, mobile, http, websocket, federation, etc.). After making this decisions, click \"Next\".\r\n\r\n4. **Features**\r\n\r\n   Now you will be able to select which features of Tigase XMPP Server (such as MUC, PubSub, MIX, MAM, Push Notifications) should be enabled or disabled.\r\n\r\n   At this step will also be able to enable clustering on your installation\r\n\r\n   When you will be ready, click \"Next\".\r\n\r\n5. **Database configuration**\r\n\r\n   This is where the database is configured. The type of database selected in step 3 will influence available options. **BE SURE TO SPECIFY DATABASE ROOT USER ACCOUNT AND PASSWORD**\r\n\r\n6. **Database connectivity check**\r\n\r\n   After database setup, you should see a page with executed actions and their results. All presented items should be \"green\", meaning that everything went well. If anything is presented in \"red\" or \"yellow\", please read description presented below this header to learn more about this issue. If setup is completed, click \"Next\".\r\n\r\n7. **Setup security**\r\n\r\n   The Setup Access Page will be locked from the admin/tigase user as specified above. This is your chance to have the setup pages add a specific user in addition to admin accounts to re-access this setup process later. If left blank, only JIDs listed in admin will be allowed to access.\r\n\r\n8. **Saving configuration**\r\n\r\n   The installation is almost complete and you will be presented with a page showing what the resulting configuration (stored in ``config.tdsl`` file) will look like.\r\n\r\n   If you have a custom setup, or would like to put your own settings in, you may copy and past the contents here to edit the current ``config.tdsl`` file.\r\n\r\n   Click \"Save\" to write the file to disk.\r\n\r\n9. **Finished**\r\n\r\n   You have now finished the installation, proceed to the next step to restart the server.\r\n\r\nRestart the Server\r\n^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIt is recommended at this point to stop the server manually and restart it using the proper script for your OS. From the Tigase base directory enter\r\n\r\n.. code:: bash\r\n\r\n   ./scripts/tigase.sh stop\r\n\r\n   ./scripts/tigase.sh start etc/tigase.conf\r\n\r\n\r\n.. Note::\r\n\r\n   In order to make Tigase XMPP Server start automatically during system startup you should setup startup scripts as described in :ref:`Tigase Script Selection<tigaseScriptStart>`\r\n\r\nTo further fine tune the server you should edit ``etc/tigase.conf``. Ensure ``JAVA_HOME`` path is correct, and increase memory if needed using ``JAVA_OPTIONS`` -Xmx (max), and -Xms (initial). You will need to direct Tigase to read settings from this file on startup as follows.\r\n\r\nEverything should be running smooth at this point. Check the logfiles in ``logs/`` if you experience any problems.\r\n\r\nVerify Tigase is Running\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nYou should see a list of listening ports.\r\n\r\n.. code:: bash\r\n\r\n   $ lsof -i -P\r\n   COMMAND   PID   USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME\r\n   java    18387 tigase  141u  IPv6 22185825      0t0  TCP *:8080 (LISTEN)\r\n   java    18387 tigase  148u  IPv6 22185834      0t0  TCP *:5222 (LISTEN)\r\n   java    18387 tigase  149u  IPv6 22185835      0t0  TCP *:5223 (LISTEN)\r\n   java    18387 tigase  150u  IPv6 22185836      0t0  TCP *:5290 (LISTEN)\r\n   java    18387 tigase  151u  IPv6 22185837      0t0  TCP *:5280 (LISTEN)\r\n   java    18387 tigase  152u  IPv6 22185838      0t0  TCP *:5269 (LISTEN)\r\n\r\n.. _winWebInstall:\r\n\r\nWindows Instructions for using Web Installer\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThere are a few steps involved with setting up Tigase with the web installer in a Windows environment. Please follow this guide.\r\n\r\nFirst step is to extract the distribution archive in it’s entirety to the intended running directory. Once there, run the ``Setup.bat`` file inside the ``win-stuff`` folder. This will move the necessary files to the correct folders before Tigase begins operation.\r\n\r\nFrom here, you have a few options how to run Tigase; ``run.bat`` will operate Tigase using a java command, or ``tigase.bat`` which will start Tigase using the wrapper. You may also install Tigase and run it as a service.\r\n\r\nOnce this setup is finished, web installer will continue the same from :ref:`here<connecttoWebInstall>`.\r\n\r\n.. |web install 01| image:: /images/admin/web-install-01.png\r\n\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Quick_Start_Guide/Intro.rst",
    "content": "Quick Start Guide\r\n===================\r\n\r\nMinimum Requirements\r\n-----------------------\r\n\r\nBefore you begin installing Tigase server onto your system, please make sure the minimum requirements are met first:\r\n\r\n-  **Java Development Kit (JDK) 17 (LTS)** - We recommend OpenJDK\r\n\r\n-  **Administrator access** - We recommend that you install Tigase Server from a user login with administrator access.\r\n\r\n..\r\n\r\n.. Important::\r\n\r\n   You should always run the latest point/bugfix release of the recommended JDK.\r\n\r\n.. Note::\r\n\r\n   While it should be possible to use newer versions of the JDK, we don’t guarantee it and we recommend using the one mentioned above.\r\n\r\nContents\r\n----------\r\n\r\nThis is a set of documents allowing you to quickly start with our software. Every document provides an introduction to a single topic allowing you to start using/developing or just working on the subject. Please have a look at the documents list below to find a topic you are looking for. If you don’t find a document for the topic you need please `let us know <http://www.tigase.net/contact>`__.\r\n\r\n-  :ref:`Installation Using Web Installer<webinstall>`\r\n\r\n-  :ref:`Manual installation in console mode<manualinstall>`\r\n\r\n-  :ref:`Installing Tigase on Windows <windowsInstallation>`\r\n\r\n-  :ref:`Network settings for Tigase<setupTigaseServer>`\r\n\r\n-  :ref:`Running Tigase XMPP Server as a service<tigaseScriptStart>`\r\n\r\n.. include:: Installation_Using_Web_Installer.inc \r\n.. include:: Manual_Installation_in_Console_Mode.inc \r\n.. include:: Windows_Installation.inc\r\n.. include:: Tigase_Server_Network_Instructions.inc\r\n.. include:: Script_Selection.inc\r\n.. include:: Shutting_Down.inc\r\n.. include:: Upgrading_from_Older_versions.inc\r\n\r\n\r\n\r\n\r\n\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc",
    "content": ".. _manualinstall:\r\n\r\nManual Installation in Console Mode\r\n----------------------------------------\r\n\r\nOur preferred way to install the Tigase server is using `Web installer <#webinstall>`__ and configuration program which comes with one of the binary packages. Please pick up the latest version of the distribution archive in our `download section <https://tigase.net/downloads>`__.\r\n\r\nIn many cases however it is not always possible to use the web installer. In many cases you have just an ssh access or even a direct access in console mode only. We are going to provide a text-only installer in one of the next releases but for the time being you can use our binary packages to install the server manually. Please continue reading to learn how to install and setup the server in a few easy steps…​\r\n\r\nIf you have an old version of the Tigase server running and working and you intend to upgrade it please always backup the old version first.\r\n\r\n.. Note::\r\n\r\n   Please note that these instructions are for \\*nix operating systems, and some modifications may be required for other Operating Systems!\r\n\r\n\r\nGet the Binary Package\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nHave a look at our `download area <https://tigase.net/downloads>`__. Always pick the latest version of the package available. For manual installation either ``zip`` or ``tar.gz`` file is available. Pick one of files with filename looking like: ``tigase-server-<version>-b<build>-<type>.<archive>``, where ``<version>`` is in the form of ``major.minor.bugfix``, ``<type>`` can be either ``dist`` (basic package) or ``dist-max`` (extended set of components) and archive type can be eitehr ``tar.gz`` or ``zip``.\r\n\r\n\r\nUnpack the Package\r\n^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nUnpack the file using command for the tar.gz file:\r\n\r\n.. code:: sh\r\n\r\n    $ tar -xzvf tigase-server-x.y.z-bv.tar.gz\r\n\r\nor for the zip file:\r\n\r\n.. code:: sh\r\n\r\n    $ unzip tigase-server-x.y.z-bv.zip\r\n\r\nA new directory will be created: **tigase-server-x.y.z-bv/**.\r\n\r\nSometimes after unpacking package on unix system startup script doesn’t have execution permissions. To fix the problem you have to run following command:\r\n\r\n.. code:: sh\r\n\r\n    $ chmod u+x ./scripts/tigase.sh\r\n\r\n\r\nPrepare Configuration\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIf you look inside the new directory, it should like this output:\r\n\r\n.. code:: sh\r\n\r\n    $ ls -l\r\n   total 88\r\n   drwxr-xr-x 2 tigase tigase  4096 Aug 15 18:17 certs\r\n   -rw-r--r-- 1 tigase tigase     0 Aug 15 18:26 ChangeLog\r\n   drwxr-xr-x 2 tigase tigase 12288 Aug 15 18:17 database\r\n   drwxrwxr-x 4 tigase tigase  4096 Oct 12 09:48 docs\r\n   drwxrwxr-x 2 tigase tigase  4096 Oct 12 09:48 etc\r\n   drwxrwxr-x 2 tigase tigase  4096 Oct 12 09:48 jars\r\n   -rw-r--r-- 1 tigase tigase 34203 Aug 15 18:26 License.html\r\n   drwxr-xr-- 2 tigase tigase  4096 Aug 15 18:26 logs\r\n   -rw-r--r-- 1 tigase tigase  3614 Aug 15 18:26 package.html\r\n   -rw-r--r-- 1 tigase tigase  2675 Aug 15 18:26 README\r\n   drwxr-xr-x 9 tigase tigase  4096 Aug 15 18:17 scripts\r\n   drwxr-xr-x 5 tigase tigase  4096 Aug 15 18:17 tigase\r\n   drwxrwxr-x 4 tigase tigase  4096 Oct 12 09:48 win-stuff\r\n\r\nAt the moment the most important is the etc/ directory with these files:\r\n\r\n.. code:: sh\r\n\r\n    $ ls -l etc/\r\n   total 36\r\n   -rw-r--r-- 1 tigase tigase  153 Aug 15 18:11 bosh-extra-headers.txt\r\n   -rw-r--r-- 1 tigase tigase  325 Aug 15 18:11 client-access-policy.xml\r\n   -rw-r--r-- 1 tigase tigase  124 Aug 15 18:11 config.tdsl\r\n   -rw-r--r-- 1 tigase tigase  263 Aug 15 18:11 cross-domain-policy.xml\r\n   -rw-r--r-- 1 tigase tigase 2337 Aug 15 18:11 jmx.access\r\n   -rw-r--r-- 1 tigase tigase 2893 Aug 15 18:11 jmx.password\r\n   -rw-r--r-- 1 tigase tigase  735 Aug 15 18:11 logback.xml\r\n   -rw-r--r-- 1 tigase tigase 3386 Aug 15 18:11 snmp.acl\r\n   -rw-r--r-- 1 tigase tigase 1346 Aug 15 18:11 tigase.conf\r\n\r\n\r\nConfigure tigase.conf\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nTigase.conf is a file that contains general program operating parameters, and java settings for Tigase to run. For now, the only setting we need to set is the **JAVA_HOME** directory.\r\n\r\n.. code:: sh\r\n\r\n   JAVA_HOME=\"${JDKPath}\"\r\n\r\nReplace **${JDKPath}** with a path to Java JDK installation on your system.\r\n\r\n\r\nConfigure config.tdsl\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nYou need also to edit the ``config.tdsl`` file. It contains initial parameters normally set by the configuration program. As this is a manual installation, you will have to edit this document yourself. It contains already a few lines:\r\n\r\n.. code::\r\n\r\n   'config-type' = 'setup'\r\n\r\n   http () {\r\n       setup () {\r\n           'admin-user' = 'admin'\r\n           'admin-password' = 'tigase'\r\n       }\r\n   }\r\n\r\nYou will need to set a few things in order to get Tigase up and running.\r\n\r\n\r\n\r\nStep 1: Change config-type\r\n\r\n\r\nRefer to `config-type <#configType>`__ property description for details, but for most operations, change ``setup`` to ``default``.\r\n\r\n\r\nStep 2: Set virtual host\r\n\r\nWithout a virtual host, your XMPP server has no domain with which to operate. To set a virtual host use the following configuration:\r\n\r\n.. code::\r\n\r\n   'default-virtual-host' = 'hostname'\r\n\r\nYou have to replace ``hostname`` with a domain name used for your XMPP installation. Let’s say this is **jabber.your-great.net**. Your setting should look like this:\r\n\r\n.. code::\r\n\r\n   'default-virtual-host' = 'jabber.your-great.net'\r\n\r\nThere are many other settings that can be configured :ref:`visit this section for details<tigase41virtualHosts>`.\r\n\r\n\r\nStep 3: Set Administrators\r\n\r\n\r\nAt least one administrator is required, and once the database is setup will have the default password of ``tigase``. Be sure to change this once you have finished setting up your server. To add admins, use the following line in the ``config.tdsl`` file:\r\n\r\n.. code::\r\n\r\n   admins = [ 'admin@jabber.your-great.net', 'user2jabber.your-great.net' ]\r\n\r\nStep 4: Set databases\r\n\r\nYou will also need to configure connection to the database. First you have to decide what database you want to use: ``Derby``, ``MySQL``, ``PostgreSQL``, ``MSSQL``, or ``MondoDB``. Each database will have slightly different configurations. If we are using derby, in a directory called ``tigasedb``, your configuration would look like this:\r\n\r\n.. code::\r\n\r\n   dataSource () {\r\n       default () {\r\n           uri = 'jdbc:derby:tigasedb;create=true'\r\n       }\r\n   }\r\n\r\nConsult :ref:`dataSource<dataSource>` property for more configuration info.\r\n\r\nThis is enough basic configuration to have your Tigase server installation running.\r\n\r\n\r\nInstall Database\r\n^^^^^^^^^^^^^^^^^^^\r\n\r\nCreating the database is the next step. Previously, we had scripts to handle this process, but we now have the advantage of functions in the ``tigase.sh`` script that can be used. Setting up the database can now be done using a single command.\r\n\r\n.. code::\r\n\r\n   ./scripts/tigase.sh install-schema etc/tigase.conf -T derby -D tigasedb -H localhost -U tigase_user -P tigase_pass -R root -A rootpass -J admin@jabber.your-great.net -N pass\r\n\r\nThis command will install tigase using a Derby database on one named ``tigasedb`` hosted on ``localhost``. The username and password editing the database is ``tigase_pass`` and ``root``. Note that ``-J`` explicitly adds the administrator, this is highly recommended with the ``-N`` passing the password. You may customize this command as needed, refer to the :ref:`install-schema<install-schema>` section of the documentation for more information.\r\n\r\nOn a windows system, you need to call the program directly:\r\n\r\n.. code::\r\n\r\n   C:\\tigase>java -cp \"jars/*\" tigase.db.util.SchemaManager \"install-schema\" -T derby -D tigasedb -H localhost -U tigase_user -P tigase_pass -R root -A rootpass -J admin@jabber.your-great.net -N pass\r\n\r\nIf this successfully passes, you should see some information printed out\r\n\r\n.. code:: bash\r\n\r\n   LogLevel: CONFIG\r\n   2017-10-12 20:05:47.987 [main]             DBSchemaLoader.init()                   CONFIG:   Parameters: [ingoreMissingFiles: false, logLevel: CONFIG, adminPassword: pass, admins: [admin@jabber.your-great.net], dbRootPass: rootpass, dbRootUser: root, dbType: derby, dbName: tigasedbx, dbHostname: localhost, dbUser: tigase_user, dbPass: tigase_pass, useSSL: false, useLegacyDatetimeCode: false, serverTimezone: null, file: null, query: null]\r\n   Oct 12, 2017 8:05:48 PM tigase.util.DNSResolverDefault <init>\r\n   WARNING: Resolving default host name: ubuntu took: 7\r\n   Oct 12, 2017 8:05:49 PM tigase.db.util.SchemaManager loadSchemas\r\n   INFO: found 1 data sources to upgrade...\r\n   Oct 12, 2017 8:05:49 PM tigase.db.util.SchemaManager loadSchemas\r\n   INFO: begining upgrade...\r\n   LogLevel: CONFIG\r\n   2017-10-12 20:05:49.877 [main]             DBSchemaLoader.init()                   CONFIG:   Parameters: [ingoreMissingFiles: false, logLevel: CONFIG, adminPassword: pass, admins: [admin@jabber.your-great.net], dbRootPass: rootpass, dbRootUser: root, dbType: derby, dbName: tigasedbx, dbHostname: null, dbUser: null, dbPass: null, useSSL: null, useLegacyDatetimeCode: false, serverTimezone: null, file: null, query: null]\r\n   2017-10-12 20:05:49.877 [main]             DBSchemaLoader.validateDBConnection()   INFO:     Validating DBConnection, URI: jdbc:derby:tigasedbx;create=true\r\n   2017-10-12 20:05:50.932 [main]             DBSchemaLoader.validateDBConnection()   CONFIG:   DriverManager (available drivers): [org.apache.derby.jdbc.AutoloadedDriver@65262308, jTDS 1.3.1, com.mysql.jdbc.Driver@54997f67, com.mysql.fabric.jdbc.FabricMySQLDriver@189633f2, org.postgresql.Driver@76fc5687]\r\n   2017-10-12 20:05:50.932 [main]             DBSchemaLoader.validateDBConnection()   INFO:     Connection OK\r\n   2017-10-12 20:05:50.933 [main]             DBSchemaLoader.validateDBExists()       INFO:     Validating whether DB Exists, URI: jdbc:derby:tigasedbx;create=true\r\n   2017-10-12 20:05:50.936 [main]             DBSchemaLoader.withConnection()         CONFIG:   DriverManager (available drivers): [org.apache.derby.jdbc.AutoloadedDriver@65262308, jTDS 1.3.1, com.mysql.jdbc.Driver@54997f67, com.mysql.fabric.jdbc.FabricMySQLDriver@189633f2, org.postgresql.Driver@76fc5687]\r\n   2017-10-12 20:05:50.937 [main]             DBSchemaLoader.lambda$validateDBExists$283()  INFO: Exists OK\r\n   2017-10-12 20:05:50.939 [main]             DBSchemaLoader.loadSchemaFile()         INFO:     Loading schema from file(s): database/derby-schema-7-2.sql, URI: jdbc:derby:tigasedbx;create=true\r\n   2017-10-12 20:05:50.941 [main]             DBSchemaLoader.withConnection()         CONFIG:   DriverManager (available drivers): [org.apache.derby.jdbc.AutoloadedDriver@65262308, jTDS 1.3.1, com.mysql.jdbc.Driver@54997f67, com.mysql.fabric.jdbc.FabricMySQLDriver@189633f2, org.postgresql.Driver@76fc5687]\r\n   2017-10-12 20:05:51.923 [main]             DBSchemaLoader.lambda$loadSchemaFile$287()  INFO:  completed OK\r\n   2017-10-12 20:05:51.925 [main]             DBSchemaLoader.loadSchemaFile()         INFO:     Loading schema from file(s): database/derby-message-archiving-schema-1.3.0.sql, URI: jdbc:derby:tigasedbx;create=true\r\n   2017-10-12 20:05:51.926 [main]             DBSchemaLoader.withConnection()         CONFIG:   DriverManager (available drivers): [org.apache.derby.jdbc.AutoloadedDriver@65262308, jTDS 1.3.1, com.mysql.jdbc.Driver@54997f67, com.mysql.fabric.jdbc.FabricMySQLDriver@189633f2, org.postgresql.Driver@76fc5687]\r\n   2017-10-12 20:05:52.209 [main]             DBSchemaLoader.lambda$loadSchemaFile$287()  INFO:  completed OK\r\n   2017-10-12 20:05:52.210 [main]             DBSchemaLoader.loadSchemaFile()         INFO:     Loading schema from file(s): database/derby-muc-schema-2.5.0.sql, URI: jdbc:derby:tigasedbx;create=true\r\n   2017-10-12 20:05:52.211 [main]             DBSchemaLoader.withConnection()         CONFIG:   DriverManager (available drivers): [org.apache.derby.jdbc.AutoloadedDriver@65262308, jTDS 1.3.1, com.mysql.jdbc.Driver@54997f67, com.mysql.fabric.jdbc.FabricMySQLDriver@189633f2, org.postgresql.Driver@76fc5687]\r\n   2017-10-12 20:05:52.305 [main]             DBSchemaLoader.lambda$loadSchemaFile$287()  INFO:  completed OK\r\n   2017-10-12 20:05:52.306 [main]             DBSchemaLoader.loadSchemaFile()         INFO:     Loading schema from file(s): database/derby-pubsub-schema-3.3.0.sql, URI: jdbc:derby:tigasedbx;create=true\r\n   2017-10-12 20:05:52.307 [main]             DBSchemaLoader.withConnection()         CONFIG:   DriverManager (available drivers): [org.apache.derby.jdbc.AutoloadedDriver@65262308, jTDS 1.3.1, com.mysql.jdbc.Driver@54997f67, com.mysql.fabric.jdbc.FabricMySQLDriver@189633f2, org.postgresql.Driver@76fc5687]\r\n   2017-10-12 20:05:52.731 [main]             DBSchemaLoader.lambda$loadSchemaFile$287()  INFO:  completed OK\r\n   2017-10-12 20:05:52.732 [main]             DBSchemaLoader.addXmppAdminAccount()    INFO:     Adding XMPP Admin Account, URI: jdbc:derby:tigasedbx;create=true\r\n   2017-10-12 20:05:52.732 [main]             DBSchemaLoader.addXmppAdminAccount()    CONFIG:   RepositoryFactory.getAuthRepository(null, jdbc:derby:tigasedbx;create=true,{data-repo-pool-size=1})\r\n   Oct 12, 2017 8:05:52 PM tigase.db.jdbc.DataRepositoryImpl initialize\r\n   INFO: Table schema found: jdbc:derby:tigasedbx;create=true, database type: derby, database driver: org.apache.derby.jdbc.EmbeddedDriver\r\n   Oct 12, 2017 8:05:52 PM tigase.db.jdbc.DataRepositoryImpl initialize\r\n   INFO: Initialized database connection: jdbc:derby:tigasedbx;create=true\r\n   2017-10-12 20:05:52.884 [main]             DBSchemaLoader.addXmppAdminAccount()    INFO:     All users added\r\n   2017-10-12 20:05:52.884 [main]             DBSchemaLoader.postInstallation()       INFO:     Post Installation, URI: jdbc:derby:tigasedbx;create=true\r\n   2017-10-12 20:05:52.891 [main]             DBSchemaLoader.withConnection()         CONFIG:   DriverManager (available drivers): [org.apache.derby.jdbc.AutoloadedDriver@65262308, jTDS 1.3.1, com.mysql.jdbc.Driver@54997f67, com.mysql.fabric.jdbc.FabricMySQLDriver@189633f2, org.postgresql.Driver@76fc5687]\r\n   2017-10-12 20:05:52.892 [main]             DBSchemaLoader.lambda$postInstallation$286()  INFO: Finalizing...\r\n   2017-10-12 20:05:52.893 [main]             DBSchemaLoader.lambda$postInstallation$286()  INFO:  completed OK\r\n   2017-10-12 20:05:52.895 [main]             DBSchemaLoader.shutdownDerby()          INFO:     Validating DBConnection, URI: jdbc:derby:tigasedbx;create=true\r\n   2017-10-12 20:05:53.129 [main]             DBSchemaLoader.withConnection()         SEVERE:\r\n\r\n\r\n   =====\r\n   Failure: Database 'tigasedbx' shutdown.\r\n   =====\r\n\r\n\r\n   Oct 12, 2017 8:05:53 PM tigase.db.util.SchemaManager loadSchemas\r\n   INFO: schema upgrade finished!\r\n\r\n\r\n\r\n\r\n     =============================================================================\r\n       Schema installation finished\r\n\r\n     Data source: default with uri jdbc:derby:tigasedbx;create=true\r\n       Checking connection to database ok\r\n       Checking if database exists ok\r\n       Loading schema: Tigase XMPP Server (Core), version: 8.0.0   ok\r\n       Loading schema: Tigase Message Archiving Component, version: 1.3.0  ok\r\n       Loading schema: Tigase MUC Component, version: 2.5.0    ok\r\n       Loading schema: Tigase PubSub Component, version: 3.3.0 ok\r\n       Adding XMPP admin accounts  ok\r\n       Post installation action    ok\r\n\r\n     Example etc/config.tdsl configuration file:\r\n\r\n     'config-type' = 'default'\r\n     debug = [ 'server' ]\r\n     'default-virtual-host' = [ 'ubuntu' ]\r\n     dataSource () {\r\n         default () {\r\n             uri = 'jdbc:derby:tigasedbx;create=true'\r\n         }\r\n     }\r\n     amp () {}\r\n     bosh () {}\r\n     c2s () {}\r\n     eventbus () {}\r\n     http () {}\r\n     'message-archive' () {}\r\n     monitor () {}\r\n     muc () {}\r\n     pubsub () {}\r\n     s2s () {}\r\n     ws2s () {}\r\n     =============================================================================\r\n\r\nNote at the end, the script will output a recommended example file. You may use this in conjunction with your written config file, but some settings may not be set using this configuration. Again, it is only an **EXAMPLE**.\r\n\r\n\r\nStart the Server\r\n^^^^^^^^^^^^^^^^^^^^\r\n\r\nYou can start the server using the tigase file found in the scripts sub-directory of Tigase server base directory. There, select the type of linux you have, debian, gentoo, mendriva or redhat. In the root server directory type the following command:\r\n\r\n.. code:: bash\r\n\r\n   ./scripts/{OS}/init.d/tigase start etc/tigase.conf\r\n\r\nWhere {OS} is your \\*nix operating system.\r\n\r\nand you should get the output like this:\r\n\r\n.. code:: sh\r\n\r\n   Starting Tigase:\r\n   nohup: redirecting stderr to stdout\r\n   Tigase running pid=18103\r\n\r\nCheck if it is Working\r\n^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThe server is started already but how do you know if it is really working and there were no problems. Have a look in the ``logs/`` directory. There should be a few files in there:\r\n\r\n.. code:: sh\r\n\r\n    $ ls -l logs/\r\n   total 40K\r\n   -rw-r--r-- 1 20K 2009-02-03 21:48 tigase-console.log\r\n   -rw-r--r-- 1 16K 2009-02-03 21:48 tigase.log.0\r\n   -rw-r--r-- 1   0 2009-02-03 21:48 tigase.log.0.lck\r\n   -rw-r--r-- 1   6 2009-02-03 21:48 tigase.pid\r\n\r\nThe first 2 files are the most interesting for us: **tigase-console.log** and **tigase.log.0**. The first one contains very limited information and only the most important entries. Have a look inside and check if there are any **WARNING** or **SEVERE** entries. If not everything should be fine.\r\n\r\nNow you can connect with an XMPP client of your choice with the administrator account you setup earlier."
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Quick_Start_Guide/Script_Selection.inc",
    "content": ".. _tigaseScriptStart:\r\n\r\nTigase Script Selection\r\n--------------------------\r\n\r\nAs mentioned in each of the quick start sections, each distribution of Tigase XMPP server comes with a number of scripts that are customized for different versions of Linux.\r\n\r\n.. table:: init.d chart\r\n\r\n   +------------------+--------------------------------------------------+-----------------------------------------------------------------------------------------------------------------+\r\n   | Operating system | init.d file path                                 | Types of Operating Systems                                                                                      |\r\n   +==================+==================================================+=================================================================================================================+\r\n   | Systemd          | ``tigase-server/scripts/systemd/*``              | Systemd-based distributions                                                                                     |\r\n   +------------------+--------------------------------------------------+-----------------------------------------------------------------------------------------------------------------+\r\n   | Debian           | ``tigase-server/scripts/debian/tigase.init.d``   | Knoppix, Ubuntu (before v15.04), Raspbian or Duvian                                                             |\r\n   +------------------+--------------------------------------------------+-----------------------------------------------------------------------------------------------------------------+\r\n   | Gentoo           | ``tigase-server/scripts/gentoo/init.d/tigase``   | CoreOS (before v94.0.0), Tin Hat Linux or other \\*too based systems                                             |\r\n   +------------------+--------------------------------------------------+-----------------------------------------------------------------------------------------------------------------+\r\n   | Mandriva         | ``tigase-server/scripts/mandriva/init.d/tigase`` | Specific init.d file for Mandriva Linux                                                                         |\r\n   +------------------+--------------------------------------------------+-----------------------------------------------------------------------------------------------------------------+\r\n   | Redhat           | ``tigase-server/scripts/redhat/init.d/tigase``   | RedHat (before v7.0) and other RPM based linux derivatives like CentOS (before v.7.14), openSUSE (before v12.2) |\r\n   +------------------+--------------------------------------------------+-----------------------------------------------------------------------------------------------------------------+\r\n\r\n\r\n\r\n.. Note::\r\n\r\n   If your operating system is a systemd-based linux distribution, we recommend to use systemd service scripts. It may be possible to use (in this case legacy) ``init.d`` startup files as before, but usage of systemd startup scripts will allow better control of the startup process and will even allow for automatic restart of the Tigase XMPP Server in the case of JVM crash.\r\n\r\n\r\nConfiguration: For Linux Distributions using systemd\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTo set up Tigase XMPP Server as a system service it is required to copy ``tigase-server.service`` file to ``/etc/systemd/system/`` directory\r\n\r\n.. code:: bash\r\n\r\n   sudo cp $SCRIPT_FILE_PATH/tigase-server.service /etc/systemd/system/\r\n\r\nThis file contains following parameters which may need to be adjusted:\r\n\r\n-  ``User`` - Specifies the user that will run the program. This should be a user with SU permissions.\r\n\r\n-  ``WorkingDirectory`` - Specifies installation directory *(default: ``/home/tigase/tigase-server``)*\r\n\r\n-  ``ExecStart`` - Specifies startup command *(default: runs ``scripts/tigase.sh start etc/tigase.conf`` in the Tigase installation directory)*\r\n\r\n-  ``ExecStop`` - Specifies shutdown command *(default: runs ``scripts/tigase.sh stop etc/tigase.conf`` in the Tigase installation directory)*\r\n\r\n-  ``PIDFile`` - Specifies location of the PID file *(default: ``logs/tigase.pid`` file in the Tigase installation directory)*\r\n\r\nIt is also required to copy options file ``tigase-server`` to ``/etc/default/`` directory\r\n\r\n.. code:: bash\r\n\r\n   sudo cp $SCRIPT_FILE_PATH/tigase-server /etc/default/\r\n\r\nWith those files in place you need to reload ``systemctl`` daemon\r\n\r\n.. code:: bash\r\n\r\n   sudo systemctl daemon-reload\r\n\r\n..\r\n\r\n   **Note**\r\n\r\n   If you are upgrading from the previous version of the Tigase XMPP Server which was not running as the systemd system service it is required to uninstall old service and remove old service files.\r\n\r\n\r\nConfiguration: For All Linux Distributions\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nOnce you’ve located the appropriate distribution scripts (please take a look at the table above), copy it to your system’s init.d folder (usually it’s ``/etc/init.d/``):\r\n\r\n.. code:: bash\r\n\r\n   sudo cp $SCRIPT_FILE_PATH /etc/init.d/tigase\r\n\r\nYou may also need to make it executable:\r\n\r\n.. code:: bash\r\n\r\n   sudo chmod +x /etc/init.d/tigase\r\n\r\nIt is recommended that you open the script files or configuration files as some have some parameters that you will need to specify.\r\n\r\n\r\nGentoo\r\n~~~~~~~~~~\r\n\r\nThe conf.d script must contain the following parameters:\r\n\r\n.. code::\r\n\r\n   TIGASE_HOME=\"/home/tigase/tigase-server\"\r\n   TIGASE_USER=tigase\r\n   TIGASE_CONF=\"etc/tigase.conf\"\r\n\r\nThe following should be configured:\r\n\r\n-  ``TIGASE_HOME`` - Specifies the Tigase Server installation directory.\r\n\r\n-  ``TIGASE_USER`` - Specifies the user that will run the program. This should be a user with SU permissions.\r\n\r\n-  ``TIGASE_CONF`` - The location of tigase.conf file, relative to the ``TIGASE_HOME`` directory.\r\n\r\n\r\nMandriva\r\n'''''''''\r\n\r\nMandriva has a single init.d file, however it should be configured:\r\n\r\n.. code:: bash\r\n\r\n   …\r\n   export JAVA_HOME=/usr/java/jdk1.8.0\r\n   export TIGASE_DIR=/opt/tigase/server/\r\n   tigase=$TIGASE_DIR/scripts/tigase.sh\r\n   prog=tigase\r\n   config=$TIGASE_DIR/etc/tigase.conf\r\n   …\r\n\r\nThe following should be configured:\r\n\r\n-  ``JAVA_HOME`` - The location of your JDK Installation.\r\n\r\n-  ``TIGASE_DIR`` - Tigase Server installation directory.\r\n\r\n-  ``tigase`` - The location of your tigase.sh script. This should not need adjusting if you maintain the default file structure.\r\n\r\n-  ``config`` - The location of your tigase.conf file. This should not need adjusting if you maintain the default file structure.\r\n\r\n``pid`` file will be stored in ``/var/run/ser.pid``\r\n\r\n\r\nRedhat\r\n~~~~~~~~~~\r\n\r\nSimilar to Mandriva, you will need to configure the init.d file:\r\n\r\n.. code:: bash\r\n\r\n   …\r\n   JAVA_HOME=/usr/lib/jvm/java/\r\n\r\n   USERNAME=tigase\r\n   USERGROUP=tigase\r\n   NAME=tigase\r\n   DESC=\"Tigase XMPP server\"\r\n\r\n   TIGASE_HOME=/home/tigase/tigase-server\r\n   TIGASE_LIB=${TIGASE_HOME}/jars\r\n   TIGASE_CONFIG=/etc/tigase.conf\r\n   TIGASE_OPTIONS=\r\n   TIGASE_PARAMS=\r\n\r\n   PIDFILE=\r\n   TIGASE_CONSOLE_LOG=\r\n   …\r\n\r\n-  ``USERNAME`` - Username running Tigase, should have su permissions.\r\n\r\n-  ``USERGROUP`` - The usergroup of the username.\r\n\r\n-  ``NAME`` - OS name for Tigase program.\r\n\r\n-  ``DESC`` - Optional description.\r\n\r\n-  ``TIGASE_HOME`` - The location of your Tigase Server installation directory.\r\n\r\n-  ``TIGASE_LIB`` - The location of your Tigase Jars folder, you should not need to adjust this if you set ``TIGASE_HOME`` properly, and maintain the default file structure.\r\n\r\n-  ``TIGASE_CONFIG`` - The location of your tigase.conf file relative to ``TIGASE_HOME``\r\n\r\n-  ``TIGASE_OPTIONS`` - Legacy options for Tigase, most are now handled in ``config.tdsl`` or tigase.conf.\r\n\r\n-  ``TIGASE_PARAMS`` - Parameters passed to command line when launching Tigase.\r\n\r\n-  ``PIDFILE`` - Location of Tigase PID file if you wish to use custom directory. Default will be located in /logs or /var/temp directory.\r\n\r\n-  ``TIGASE_CONSOLE_LOG`` - Location of Tigase Server console log file if you wish to use a custom directory. Default will be located in /logs directory, failing that /dev/null.\r\n\r\nAfter you’ve copied the script, in order to install sysinit script you have to add it to the configuration:\r\n\r\n.. code:: bash\r\n\r\n   /sbin/chkconfig --add tigase\r\n\r\nService can be enabled or disabled service with:\r\n\r\n.. code:: bash\r\n\r\n   /sbin/chkconfig tigase <on|off|reset>\r\n\r\nDebian\r\n~~~~~~~~~~\r\n\r\nAs with other distributions you should copy init.d script to the correct location. Afterwards it should be edited and correct values for variables need to be set:\r\n\r\n.. code:: bash\r\n\r\n   …\r\n   USERNAME=tigase\r\n   USERGROUP=tigase\r\n   NAME=tigase\r\n   DESC=\"Tigase XMPP server\"\r\n\r\n   TIGASE_HOME=/usr/share/tigase\r\n   TIGASE_CONFIG=/etc/tigase/tigase.config\r\n   TIGASE_OPTIONS=\r\n   TIGASE_PARAMS=\r\n\r\n   PIDFILE=\r\n   TIGASE_CONSOLE_LOG=\r\n   …\r\n\r\n-  ``USERNAME`` - Username running Tigase, should have su permissions.\r\n\r\n-  ``USERGROUP`` - The usergroup of the username.\r\n\r\n-  ``NAME`` - OS name for Tigase program.\r\n\r\n-  ``DESC`` - Optional description.\r\n\r\n-  ``TIGASE_HOME`` - The location of your Tigase Server installation directory.\r\n\r\n-  ``TIGASE_CONFIG`` - The location of your tigase-server.xml file relative (old configuration format)\r\n\r\n-  ``TIGASE_OPTIONS`` - command line arguments passed to Tigase server (which may include path to ``init.properies`` (if correct ``tigase.conf`` configuration will be found then it will translate to ``TIGASE_OPTIONS=\" --property-file etc/config.tdsl \"``\r\n\r\n-  ``TIGASE_PARAMS`` - Parameters passed to command line when launching Tigase.\r\n\r\n-  ``PIDFILE`` - Location of Tigase PID file if you wish to use custom directory. Default will be located in ``/var/run/tigase/tigase.pid`` or under (in this case relative to tigase home directory)\\ ``logs/tigase.pid``.\r\n\r\n-  ``TIGASE_CONSOLE_LOG`` - Location of Tigase Server console log file if you wish to use a custom directory. Default will be located in /logs directory, failing that /dev/null.\r\n\r\nAfterwards we need to install service in the system with following command:\r\n\r\n.. code:: bash\r\n\r\n   update-rc.d tigase defaults\r\n\r\n\r\nRunning Tigase as a system service\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThere are a number of benefits to running Tigase as a service, one of which is to ensure that the program will run even in the event of a power outage or accidental server restart, Tigase will always be up and running.\r\n\r\n\r\nFor systemd-based linux distributions\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nOnce installation is complete you may start Tigase as a typical systemd service using following command:\r\n\r\n.. code:: bash\r\n\r\n   sudo systemctl start tigase-server\r\n\r\nTo stop it, you may run following command:\r\n\r\n.. code:: bash\r\n\r\n   sudo systemctl stop tigase-server\r\n\r\nIt is also possible to enable service, to make it start during startup of the operating system:\r\n\r\n.. code:: bash\r\n\r\n   sudo systemctl enable tigase-server\r\n\r\n\r\nFor other linux distributions\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nOnce installation is complete, you should be able to start Tigase using the following command:\r\n\r\n.. code:: bash\r\n\r\n   service tigase start\r\n\r\nTigase should begin running in the background. Since Tigase is now installed as a service, it can be controlled with any of the service commands, such as:\r\n\r\n-  ``service tigase stop``\r\n\r\n-  ``service tigase restart``"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc",
    "content": "Shutting Down Tigase\r\n----------------------\r\n\r\nAlthough Tigase XMPP Server can be terminated by ending the process, it is preferred and recommended to use it’s own shutdown scripts instead. Not only does this allow for a proper purge of Tigase form the system, but allows for all shutdown functions to operate, such as amending logs and completing statistics. To trigger a shutdown of Tigase server, the following command can be used from the tigase directory:\r\n\r\n.. code:: bash\r\n\r\n   ./scripts/tigase.sh stop\r\n\r\nYou may specify the config file if you want, but it will make no differences\r\n\r\nThis will:\r\n\r\n-  Begin shutdown thread\r\n\r\n-  Stop accepting new connections\r\n\r\n-  Close all current connections\r\n\r\n-  Collect runtime statistics\r\n\r\n-  Write statistics to log\r\n\r\n-  Dump full stacktrace to a file\r\n\r\n-  Run GC and clear from memory\r\n\r\n\r\nShutdown statistics\r\n^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nUpon shutdown, statistics for the server’s runtime will be appended to the log file. For a description of the statistics and what they mean, refer to the :ref:`Statistics Description<statsticsDescription>` portion of the documentation.\r\n\r\nShutdown StackTrace Dump\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTo aid with troubleshooting purposes, the full stacktrace will be dumped to a seperate file located at $serverdir/logs/threads-dump.log.# Stacktrace logs will follow the same log file numbering scheme described in :ref:`Log file description<Tigase-Log-Guide>`.\r\n\r\nThis feature is enabled by default, however you may disable this by adding the following to your ``config.tdsl`` file:\r\n\r\n.. code::\r\n\r\n   'shutdown-thread-dump' = false\r\n\r\nShutting Down Cluster Nodes\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nStarting with v8.0.0 you can now shut down individual cluster nodes without shutting down the whole server. This command will use the *SeeOtherHost* strategy to direct traffic to other nodes and update the cluster map to gracefully shut down the single node\r\n\r\nShutting down individual nodes can be done VIA Ad-hoc command and fill out the response forms. The command is available from message-router as http://jabber.org/protocol/admin#shutdown."
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc",
    "content": ".. _setupTigaseServer:\r\n\r\nTigase Server Network Instructions\r\n------------------------------------\r\n\r\nOne you have installed Tigase XMPP Server on a machine, you’re going to want to use it. If you are just using for local communications on a network behind a router, you’re all set. Enjoy and use!\r\n\r\nHowever, if you want to have people from other computers outside your network connect to your server, you’re going to have to go through a few more steps to show your server out to the public.\r\n\r\n   **Note**\r\n\r\n   This guide is merely a recommendation of how to get a local server to be open to incoming communications. Any time you open ports, or take other security measures you risk compromising your network security. These are only recommendations, and may not be appropriate for all installations. Please consult your IT Security expert for securing your own installation.\r\n\r\nXMPP, being a decentralized communication method, relies on proper DNS records to figure out where and how an XMPP server is setup. Operating an XMPP Server will require you to properly setup DNS routing so not only can clients connect to you, but if you decide to run a federated server and enable server to server communication, you will need to do the same. If you already have a DNS server already, you should have little issue adding these records. If you do not have a DNS setup pointing to your server, you may use a free dynamic name service such as dynu.com.\r\n\r\nA Records\r\n^^^^^^^^^^\r\n\r\nYou will not be able to use an IP Address or a CNAME record to setup an XMPP Server. While it’s not required, an A record can provide some other benefits such serving as a backup in case the SRV record is not configured right.\r\n\r\nSRV Records\r\n^^^^^^^^^^^^\r\n\r\nYou will need to set SRV records both for client-to-server (c2s) communication and, if you plan to use it, server to server (s2s) communication. We recommend both records are entered for every server as some resources or clients will check for both records. For this example we will use tigase.org is our domain, and xmpp as the xmpp server subdomain.\r\n\r\nSRV records have the following form:\r\n\r\n::\r\n\r\n   _service._protocol.name. TTL class SRV Priority weight port target.\r\n\r\nThe key is as follows:\r\n\r\n-  ``service``: is the symbolic name of the desired service, in this case it would be *xmpp-client* or *xmpp-server*.\r\n\r\n-  ``protocol``: is the transport protocol, either TCP or UDP, XMPP traffic will take place over *TCP*.\r\n\r\n-  ``name``: the domain name where the server resides, in this case *tigase.org*.\r\n\r\n-  ``TTL``: a numeric value for DNS time to live in milliseconds, by default use *86400*.\r\n\r\n-  ``class``: DNS class field, this is always *IN*.\r\n\r\n-  ``priority``: the priority of the target host with lower numbers being higher priority. Since we are not setting up multiple SRV records, we can use *0*.\r\n\r\n-  ``weight``: the relative weight for records with the same priority. We can use *5*.\r\n\r\n-  ``port``: the specific TCP or UDP port where the service can be found. In this case it will be *5222* or *5269*.\r\n\r\n-  ``target``: the hostname of the machine providing the service, here we will use *xmpp.tigase.org*.\r\n\r\nFor our example server, the SRV records will then look like this:\r\n\r\n::\r\n\r\n   _xmpp-client._TCP.tigase.org 86400 IN SRV 0 5 5222 xmpp.tigase.org\r\n   _xmpp-server._TCP.tigase.org 86400 IN SRV 0 5 5269 xmpp.tigase.org\r\n\r\nTigase and Vhosts\r\n^^^^^^^^^^^^^^^^^^^^\r\n\r\nIf you are running multiple vhosts or subdomains that you wish to separate, you will need another record. In this case an A record will be all you need if you are using default ports. If you are using custom ports, you will need to have a new SRV record for each subdomain.\r\n\r\nHosting via Tigase.me\r\n^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIf you don’t want to do all the hosting yourself, you can still have an XMPP service running in your own domain. The only condition right now is proper DNS service record (SRV) configuration that point to the following DNS address: ``tigase.me``.\r\n\r\nWe highly encourage using SRV records. If you want to register: **your-domain.tld** on our XMPP service make sure that it resolves correctly:\r\n\r\n+----------------------------------------------+----------+--------------------------+--------------------------------------+\r\n| Service                                      | DNS Type | DNS record               | Comment                              |\r\n+----------------------------------------------+----------+--------------------------+--------------------------------------+\r\n| ``_xmpp-client._tcp.your-domain.tld``        | SRV      | ``10 0 5222 tigase.me.`` | Basic XMPP                           |\r\n+----------------------------------------------+----------+--------------------------+--------------------------------------+\r\n| ``_xmpps-client._tcp.your-domain.tld``       | SRV      | ``10 0 5223 tigase.me.`` | DirectTLS                            |\r\n+----------------------------------------------+----------+--------------------------+--------------------------------------+\r\n| ``_xmpp-server._tcp.your-domain.tld``        | SRV      | ``10 0 5269 tigase.me.`` | Federation / s2s connection          |\r\n+----------------------------------------------+----------+--------------------------+--------------------------------------+\r\n| ``_xmpp-server._tcp.muc.your-domain.tld``    | SRV      | ``10 0 5269 tigase.me.`` | Federation / s2s connection (MUC)    |\r\n+----------------------------------------------+----------+--------------------------+--------------------------------------+\r\n| ``_xmpp-server._tcp.mix.your-domain.tld``    | SRV      | ``10 0 5269 tigase.me.`` | Federation / s2s connection (MIX)    |\r\n+----------------------------------------------+----------+--------------------------+--------------------------------------+\r\n| ``_xmpp-server._tcp.pubsub.your-domain.tld`` | SRV      | ``10 0 5269 tigase.me.`` | Federation / s2s connection (PubSub) |\r\n+----------------------------------------------+----------+--------------------------+--------------------------------------+\r\n\r\n.. **Note**::\r\n\r\n   If you want to have MUC, MIX and PubSub available under your domain as subdomains, you have to setup DNS for your ``muc.your-domain.tld``, ``mix.your-domain.tld`` and ``pubsub.your-domain.tld`` domains too but they are optional.\r\n\r\nYou can check if the configuration is correct by issuing following commands:\r\n\r\n.. code:: sh\r\n\r\n   $ host -t SRV _xmpp-client._tcp.your-domain.tld\r\n   $ host -t SRV _xmpps-client._tcp.your-domain.tld\r\n   $ host -t SRV _xmpp-server._tcp.your-domain.tld\r\n   $ host -t SRV _xmpp-server._tcp.muc.your-domain.tld\r\n   $ host -t SRV _xmpp-server._tcp.pubsub.your-domain.tld\r\n\r\nNow, how do you register your domain with our service?\r\n\r\nThere are a few ways. We recommend checking with the `Add and Manage Domains <#addManageDomain>`__ section of the documentation on setting that up. If you cannot or don’t want to do it on your own, the way described in the guide please send us a message, either via XMPP to admin@tigase.im or the contact form requesting new domain. User registration is available via in-band registration protocol. You can also specify whether you want to allow anonymous authentication to be available for your domain and you can specify maximum number of users for your domain.\r\n\r\n\r\nProviding certificate\r\n~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nIt’s also encouraged to provide dedicated SSL certificate - there are various ways to do it and they are described in :ref:`Installing/Loading Certificate To the Tigase Server<InstallingSSLCertificate>`. You may want to take advantage of free Let’s Encrypt certificates and automate whole upload and renewal process as described in :ref:`Installing LetsEncrypt Certificates in Your Linux System<LetsEncryptCertificate>`\r\n\r\n\r\nChecking setup\r\n^^^^^^^^^^^^^^^^^^^\r\n\r\nIf you have a cell phone on a separate network with an XMPP client, you can now try to login to test the server. If that is not handy, you can use an online tool to check proper DNS records such as kingant’s: https://kingant.net/check_xmpp_dns/ and it will tell you if anything is missing.\r\n\r\n\r\nPorts description\r\n^^^^^^^^^^^^^^^^^^^\r\n\r\n\r\nOnce your server is setup, you may need to open at least two ports. By default XMPP communication happens on ports 5222/5269, to which point SRV records. Other ports used by the server are:\r\n\r\n-  ``3478`` - TURN or STUN, plain socket, TCP and UDP\r\n\r\n-  ``5349`` - TURN or STUN, over TLS, TCP and UDP\r\n\r\n-  ``5222`` - incoming client to server XMPP connections\r\n\r\n-  ``5223`` - incoming client to server XMPP connections over TLS/SSL, including DirectTLS\r\n\r\n-  ``5269`` - default s2s port, i.e.: federation support\r\n\r\n-  ``5270`` - default external-component connection port\r\n\r\n-  ``5277`` - inter-cluster communication\r\n\r\n-  ``5280`` - default BOSH connections\r\n\r\n-  ``5290`` - default WebSocket connections\r\n\r\n-  ``5291`` - default WebSocket connections over TLS/SSL\r\n\r\n-  ``8080`` - for HTTP server (web-based setup, REST API, file upload extension, etc.)\r\n\r\n-  ``9050`` - JMX Monitoring\r\n\r\nIf for any reason you can’t use default ports and have to change them it’s possible to point SRV records those ports. Please keep in mind, that you have to open those ports for incoming connections in your firewall. In case you are using ``iptables`` you can use following command to include those ports in your rules:\r\n\r\n.. code:: bash\r\n\r\n   iptables -A INPUT -p tcp -m tcp --dport 5222 -j ACCEPT\r\n   iptables -A INPUT -p tcp -m tcp --dport 5223 -j ACCEPT\r\n   iptables -A INPUT -p tcp -m tcp --dport 5269 -j ACCEPT\r\n   iptables -A INPUT -p tcp -m tcp --dport 5277 -j ACCEPT\r\n   iptables -A INPUT -p tcp -m tcp --dport 5280 -j ACCEPT\r\n   iptables -A INPUT -p tcp -m tcp --dport 5290 -j ACCEPT\r\n   iptables -A INPUT -p tcp -m tcp --dport 8080 -j ACCEPT\r\n   iptables -A INPUT -p tcp -m tcp --dport 9050 -j ACCEPT\r\n\r\nBoth ports should be setup to use TCP only. If for any reason you want to make service available for different ports you can:\r\n\r\n1. change ports in Tigase configuration and update DNS SRV records;\r\n\r\n2. forward those ports to default Tigase ports (this is especially useful under \\*nix operating system if you want to utilize ports lower than ``1024`` while running, as recommended, Tigase service from user account - there is a limitation and user accounts can bind to ports lower than ``1024``), for example using ``iptables`` rules (in following example we are making available Tigase SSL websocket port available under port ``443``, which is usually opened in corporate firewalls):\r\n\r\n   .. code:: bash\r\n\r\n      iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-ports 5291\r\n\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc",
    "content": "Upgrading Tigase to newer version\r\n------------------------------------\r\n\r\n.. Note::\r\n\r\n   Depending of the deployment we recommend installing Tigase XMPP Server next to the existing one and following with replace of the service once the upgrade finishes correctly.\r\n\r\nBackup your data\r\n^^^^^^^^^^^^^^^^^^^^^\r\n\r\nAs with any migration it is **highly recommended** that you backup your repository before conducting any upgrade operations. It can be done via simple SQL dump od the database or more elaborate snapshot of the database offered by cloud providers.\r\n\r\n\r\nConfiguration files to migrate\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nDuring the upgrade the most important files to migrate to newer versions are:\r\n\r\n* `etc/tigase.conf`\r\n* `etc/config.tdsl`\r\n* `etc/jmx.access` and `etc/jmx.password`\r\n* `certs/*` (if configured to use local filesystem, though we recommend to use database for storing certificates)\r\n\r\nUpgrade Database schema\r\n^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nUpgrading database schemas is now possible using the ``upgrade-schema`` option. Do this now.\r\n\r\n.. code:: bash\r\n\r\n   ./scripts/tigase.sh upgrade-schema etc/tigase.conf\r\n\r\n\r\n.. Warning::\r\n\r\n    Your database schema MUST be v8 or conversion will not occur properly!\r\n\r\nYou will be asked for rood credentials. Those can be provided as parameters to `/scripts/tigase.sh upgrade-schema` - please check `./scripts/tigase.sh upgrade-schema --help` for more details\r\n\r\nUpon success, you should see the following:\r\n\r\n.. code:: bash\r\n\r\n   =============================================================================\r\n           Schema upgrade finished\r\n\r\n     Data source: default with uri\r\n   jdbc:<database>://HOST/DATABASE?user=USERNAME&password=PASSWORD\r\n           Checking connection to database ok\r\n           Checking if database exists     ok\r\n           Loading schema: Tigase XMPP Server (Core), version: x.y.z       ok\r\n           Loading schema: Tigase Message Archiving Component, version: x.y.z      ok\r\n           Loading schema: Tigase MUC Component, version: x.y.z    ok\r\n           Loading schema: Tigase PubSub Component, version: x.y.z ok\r\n           Adding XMPP admin accounts      warning\r\n                   Message: Error: No admin users entered\r\n           Post installation action        ok\r\n\r\n   =============================================================================\r\n\r\nStart Tigase!\r\n\r\n\r\nHelp?\r\n^^^^^^^^^^^^^^^^^^^^^\r\n\r\nBoth ``upgrade`` commands also have a build in help function, they can be called if needed from the command line. You can also run these commands for help.\r\n\r\n::\r\n\r\n   scripts/tigase.sh upgrade-config etc/tigase.conf --help\r\n   scripts/tigase.sh upgrade-schema etc/tigase.conf --help\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc",
    "content": ".. _windowsInstallation:\r\n\r\nWindows Installation\r\n----------------------------------\r\n\r\nTigase XMPP Server can also work on Microsoft Windows systems and servers, although some slight modifications may be necessary to get things ready to run.\r\n\r\nAlthough you may wish to use command line, take note that commands entered in shell may require quotations in some cases.\r\n\r\nMake sure that you have Java JDK v8 installed on your system prior to installing Tigase. It will also help to fully setup whatever database software you will be using as well.\r\n\r\nStep 1: Initial Setup\r\n^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nDownload the Tigase XMPP Server archive from `our repository <https://tigase.net/downloads>`__ and extract it to a directory of your choice.\r\n\r\nOnce that is completed, enter the directory ``win-stuff`` and run the setup.bat program. This program when run, will extract the necessary files to appropriate places on your computer. The bat file should look like the following:\r\n\r\n.. code:: bat\r\n\r\n   copy \"tigase.ico\" \"..\\\"\r\n   copy \"wrapper\\wrapper.jar\" \"..\\jars\"\r\n   copy \"wrapper\\wrapper.dll\" \"..\\jars\"\r\n   copy \"wrapper\\wrapper.exe\" \"..\\\"\r\n   copy \"wrapper\\wrapper.conf\" \"..\\\"\r\n   copy \"wrapper\\wrapper-community-license-1.2.txt\" \"..\\\"\r\n   copy \"scripts\\*.*\" \"..\\\"\r\n\r\nStep 2: Starting Server\r\n^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTo start the server you may use a command prompt from the installation directory\r\n\r\n.. code:: bash\r\n\r\n   java -cp \"jars/*\" tigase.server.XMPPServer\r\n\r\n..\r\n\r\n   **Note**\r\n\r\n   this may freeze the command window, and will only display output from Tigase.\r\n\r\nOr you may run wrapper.exe or tigase.bat from the GUI.\r\n\r\n2A: Installing as a service\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nThe cleanest way to operate Tigase in a Windows environment is to install Tigase as a Service by running the InstallTigaseService.bat program. This will install Tigase as a system service, and now the server can be controlled from the services.msc panel. This allows for stopping, starting, and pausing of Tigase XMPP Server and allowing for graceful shutdowns.\r\n\r\nFor a basic installation, MySQL is recommended over Derby DB. For that purpose, we have included a basic installation guide for MySQL on Windows systems here:\r\n\r\nMySQL Database Installation\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThe section describes installation and configuration of the MySQL database to work with Tigase server.\r\n\r\nDownload the binary package from MySQL download area at `mysql.com <http://dev.mysql.com/downloads/mysql/5.0.html#win32>`__. Make sure you select executable proper for your operating system.\r\n\r\nRun the installation program and follow default installation steps. When the installation is complete find the MySQL elements in the Windows Start menu and run the MySQL Configuration Wizard. Follow the wizard and make sure to check settings against the screenshots in the guide below.\r\n\r\nIn Welcome window just press 'Next'.(pic.1)\r\n\r\n|sql1|\r\n\r\nIn the next window select option: 'Detailed Configuration' and press 'Next' (pic. 2)\r\n\r\n|sql2|\r\n\r\nOn the next screen select option: 'Server Machine' and press 'Next' (pic. 3)\r\n\r\n|sql3|\r\n\r\nOn the forth windows leave the default\" 'Multi-functional Database' and press 'Next' (pic. 4)\r\n\r\n|sql4|\r\n\r\nOn the step number five just press 'Next' using defaults. (pic. 5)\r\n\r\n|sql5|\r\n\r\nAgain, on window 6 select the default - 'Decision Support (DSS)/OLAP' and press 'Next' (pic.6)\r\n\r\n|sql6|\r\n\r\nMake sure you switch OFF the 'Strict mode' and and press 'Next' (pic. 7)\r\n\r\n|sql7|\r\n\r\nOn the character encoding page select: 'Manual Selected Default Character set/ Collation' and 'utf8', press 'Next' (pic.8)\r\n\r\n|sql8|\r\n\r\nOn next window select 'Include Bin Directory in Windows PATH' and press 'Next' (pic.9)\r\n\r\n|sql9|\r\n\r\nOn this window just enter the database super user password and make sure you remember it. When ready press 'Next' (pic. 10)\r\n\r\n|sql10|\r\n\r\nThis is the last screen. Press 'Execute' to save the configuration parameters. (pic. 11)\r\n\r\n|sql11|\r\n\r\nWhen the configuration is saved you can repeat all the steps and change settings at any time by running: **START ⇒ Programs ⇒ MYSQL⇒ MYSQL serwer machine⇒ MySQL Server Instance Config Wizard**\r\n\r\nNow we have to setup Tigase database. From the Start menu run the MySQL console and enter all commands below finishing them with **<ENTER>**:\r\n\r\n1. Create the database:\r\n\r\n   .. code:: sql\r\n\r\n      mysql>create database tigasedb;\r\n\r\n2. Add database user:\r\n\r\n   .. code:: bash\r\n\r\n      mysql> GRANT ALL ON tigasedb.* TO tigase_user@'%' IDENTIFIED BY 'tigase_passwd';\r\n      mysql> GRANT ALL ON tigasedb.* TO tigase_user@'localhost' IDENTIFIED BY 'tigase_passwd';\r\n      mysql> GRANT ALL ON tigasedb.* TO tigase_user IDENTIFIED BY 'tigase_passwd';\r\n      mysql> FLUSH PRIVILEGES;\r\n\r\n3. Load Tigase database schema:\r\n\r\n   .. code:: bash\r\n\r\n      mysql> use tigasedb;\r\n      mysql> source c:/Program Files/Tigase/database/mysql-schema.sql;\r\n\r\nWhen the system is up and running you can connect with any XMPP client (Psi for example) to your server to see if it is working.\r\n\r\n.. |sql1| image:: /images/admin/sql1.JPG\r\n.. |sql2| image:: /images/admin/sql2.JPG\r\n.. |sql3| image:: /images/admin/sql3.JPG\r\n.. |sql4| image:: /images/admin/sql4.JPG\r\n.. |sql5| image:: /images/admin/sql5.JPG\r\n.. |sql6| image:: /images/admin/sql6.JPG\r\n.. |sql7| image:: /images/admin/sql7.JPG\r\n.. |sql8| image:: /images/admin/sql8.JPG\r\n.. |sql9| image:: /images/admin/sql9.JPG\r\n.. |sql10| image:: /images/admin/sql10.JPG\r\n.. |sql11| image:: /images/admin/sql11.JPG"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Release_Notes/Tigase_Release_Notes.rst",
    "content": "Tigase 8.4.0 Release Notes\n=============================================\n\n.. include:: tigase-server-current.inc\n.. include:: Tigase_v8.3.0.inc\n.. include:: Tigase_v8.2.0.inc\n.. include:: Tigase_v8.1.0.inc\n.. include:: Tigase_v8.0.0.inc\n\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc",
    "content": "Tigase XMPP Server 8.0.0 Change notes and announcement\n------------------------------------------------------\n\nMajor Changes\n^^^^^^^^^^^^^\n\nKernel and beans configuration\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nTigase now operates using a Kernel and Beans style of programming. What does this mean for Tigase and You? Good news, really. Tigase XMPP Server is now working as a Kernel program, which will operate on it’s own and handle all the core functionality of the server. Component, and non-essential functionality will now be loaded as Beans. As a user, your experience will not change all that much. However, beans can be loaded and unloaded without having to restart Tigase, meaning that the program will behave more dynamically. This means a smaller footprint on memory on resources when components are not needed, and longer uptimes without having to rest art the program! This also allows for greater flexibility for Tigase XMPP Server to be better customized for unique solutions.\n\n\nNew Configuration File Format\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWith the change of Tigase to a Kernel and Beans style of programming, we have also changed how the configuration file is managed. Although you will still edit the ``config.tdsl`` file like a plaintext file, a new style of formatting will be used known as DSL. Domain Specific Language may add more lines, but is a cleaner format, and provides a more secure configuration design since validation of the configuration is done at the domain level. For more information on this format and how to configure Tigase, visit `DSL Configuration Guide <#dslConfig>`__.\n\n\nCluster Node Shutdown Changes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nStarting with Tigase XMPP Server 8.0.0, users connected on clustered nodes will be able use a ``see-other-host`` strategy when a node is being shutdown. **Note: This may not be compatible with all clients.** The Ad-hoc command is designed for a graceful shutdown of cluster nodes as a groovy script ``Shutdown.groovy``. This script also allows for the -timeout setting which will delay shutdown of the node, and alert all users (via a headline message) that the server will be shutdown after a time. User clients that are compatible with the command will then detect other connected clusters and maintain their connections.\n\nIf the command is being sent to shut down the whole cluster, no ``see-other-host`` implementation will be sent, however timeout settings may still be used.\n\nThe script may be activated by an ad-hoc command, or sent using REST from remote or Tigase Admin UI.\n\n.. _`_significant_cleanup_of_code_and_repositories`:\n\nSignificant cleanup of code and repositories\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nMultiple changes have been made to the structure and coding for v8, many related to trimming size of repositories and old calls. Some of these improvements are listed here:\n\n-  Empty JavaDocs that do not convey values have been removed.\n\n-  All code is reformatted to be compliant with out `codestyle guidelines <#tigaseCodeStyle>`__.\n\n-  Calls to ``System.out.print*()`` and ``printStackTrace()`` have been removed from code.\n\n-  Depreciated and unused classes have been removed.\n\n.. _`_bouncycastle_being_used_for_starttls`:\n\nBouncyCastle being used for StartTLS\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n`BouncyCastle <https://www.bouncycastle.org/java.html>`__ Crypto API has now been employed to handle StartTLS negotiation. By doing this, Tigase now supports ``tls-unique`` within the SCRAM PLUS authentication implementation. This API is may be employed by calling the class in your configuration file:\n\n.. code::\n\n   c2s () {\n       sslContextContainer(class: tigase.extras.bcstarttls.BCSSLContextContainer) {}\n   }\n\nThe BouncyCastle classes are included in the dist-max archives.\n\n.. _`_default_virtual_host_property_changes`:\n\ndefault-virtual-host property changes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nDefault virtual hosts property is now able to be configured only as a domain name instead of the list of virtual host domains with options. Additional virtual host domains and their options need to be configured using ad-hoc commands or web AdminUI. Reference `Virtual-Hosts Configuration <#virtHosts>`__ for more details.\n\n.. _`_all_artifacts_are_signed`:\n\nAll artifacts are signed\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nSince work began on v8.0.0 Tigase has required that all changes to Tigase XMPP Server and dependencies be signed with known certificates. This version marks the first to be totally signed.\n\n.. _`_scaled_down_installation_methods`:\n\nScaled Down Installation Methods\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWe have cleaned up installation methods for Tigase and now recommend the use of web-installer method. IzPack installer (files ``tigase-server-<version>-b<build>.jar`` installation methods have been removed and will no longer be produced for v8.0.0 and later. Manual installation is still available for those unable to use HTTP or browser access. Visit our `Quick Start <#quickstart>`__ guide for instructions on these other methods.\n\n.. _`_emojis_now_supported_on_tigase_xmpp_servers`:\n\nEmojis now supported on Tigase XMPP Servers\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nEmojis are now supported on MySQL databases, however some settings may be need to be changed, although they won’t affect existing databases. `Visit this section <#emojisupportSQL>`__ for details.\n\n.. _`_xep_0215_external_service_discovery_now_supported`:\n\nXEP-0215 External Service Discovery now supported\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nTigase now supports `XEP-0215 - External Service Discovery <https://xmpp.org/extensions/xep-0215.html>`__ allowing Tigase to discover services that are not available VIA the XMPP Protocol. For setup and configuration information visit `External Service Discovery Component <#_tigase_external_service_discovery>`__ documentation.\n\n.. _`_xep_0313_message_archive_management_now_supported`:\n\nXEP-0313 Message Archive Management now supported\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n`XEP-0313 - Message Archive Management <https://xmpp.org/extensions/xep-0313.html>`__ is now supported by Tigase featuring custom enhancements like full-text search and searching by tags. MAM requires Tigase’s message archive to be enabled in the ``config.tdsl`` file, and the schema (XEP-0136 or XEP-0313) must be configured in session manager settings. To turn on MAM, see configuration guide `located here <#_support_for_mam>`__.\n\n.. _`_xep_0363_http_file_upload_now_supported`:\n\nXEP-0363 HTTP File Upload now supported\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n`XEP-0363 - HTTP File Upload <https://xmpp.org/extensions/xep-0363.html>`__ is now supported using Tigase HTTP API component now allowing for a more robust one-to-many file uploading option. Configuration details are available at the `HTTP File Upload Component <#XEP0363>`__ section of documentation.\n\n.. _`_startup_now_uses_bootstrapping`:\n\nStartup now uses bootstrapping\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nTigase now uses bootstrapping to startup, which will load configuration from ``config.tdsl`` file like before. Then Tigase will begin it’s normal operations with the configuration options. All startup functions for Tigase will now run under the ``bootstrap`` bean.\n\n.. _`_captcha_system_now_available_for_in_band_registration`:\n\nCAPTCHA system now available for in-band registration\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n`XEP-0077 In band registration <https://xmpp.org/extensions/xep-0077.html>`__ can use Data Forms as an option to process new registrations. Now you can secure these registrations by employing a CAPTCHA solution. By enabling this option you can reduce the number of potential spammers and bots on your server.\n\n.. _`_schema_changes`:\n\nSchema changes\n~~~~~~~~~~~~~~\n\nNow each component has it’s own schema for databases, they are no longer tied into Tigase XMPP server versions making changes and updates to individual components easier, and may not disrupt all users not using certain components. See the `schema update section <#schemaChangev800>`__ for more details.\n\n.. _`_shrinkable_statistics_history`:\n\nShrinkable Statistics History\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nStatistics history can now be automatically made smaller if a systems memory resources are above a certain amount. By default this is enabled and will trigger when over 95% of memory is in use. Half of all existing entries will be removed at this time. The same pattern will continue to halve the available records every time the threshold is met. A hard-set minimum of 5 entries is set, so you will always have the last 5 entries. This setting may be adjusted by adding the following setting to your ``config.tdsl`` file and adjusting the integer value:\n\n.. code::\n\n   stats() {\n     'stats-high-memory-level' = 95\n   }\n\n.. _`_statistics_now_available_for_all_modules`:\n\nStatistics now available for all modules\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nFor any bean, you may enable statistics by using the following\n\n.. code::\n\n   bean (class) {\n     statistics = true\n   }\n\n.. _`_spam_protection`:\n\nSpam Protection\n...............\n\nTigase XMPP Server v8.0.0 now includes some efforts to prevent spam bot accounts from running on servers.\n\nAccount Registration Limits Expanded\n....................................\n\nAccount registration limits have been expanded and now you can set separate counters, or configure components individually for their own limits. Visit `this section <#accountRegLimit>`__ for configuration details.\n\nAccounts created using in-band registration now will use confirmation E-mail\n............................................................................\n\nIn an effort to create a more secure method for implementing ``JabberIqRegister`` Tigase XMPP Server will now require the use of a confirmation E-mail by default in the process. The E-mail must be valid, and accounts will be made into pending status until a user clicks the generated URI in the E-mail and activates the account. This is a plugin and must be enabled in the ``config.tdsl`` file by using the following code:\n\n.. code::\n\n   'account-registration-email-validator'() {}\n\nFurther Spam prevention\n.......................\n\nTigase-spam component is now in ``dist-max`` distribution package, and has a number of features described here `in this section <#tigase_spam_filter>`__.\n\n.. _`_changes_in_password_storage`:\n\nChanges in password storage\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nBefore version 8.0.0, user passwords were stored in plaintext in the ``user_pw`` database field within ``tig_users`` table, but in plaintext. It was possible to enable storage of the MD5 hash of the password instead, however this limited authentication mechanism SASL PLAIN only. However an MD5 hash of a password is not really a secure method as it is possible to revert this mechanism using rainbow tables.\n\nTherefore, we decided to change this and store only encrypted versions of a password in ``PBKDF2`` form which can be easily used for ``SCRAM-SHA-1`` authentication mechanism or ``SCRAM-SHA-256``. ``SASL PLAIN`` mechanism can also use these encrypted passwords.\n\nThe storage of encrypted passwords is now enabled **by default** in v8.0.0 of Tigase.\n\n.. _`_dynamic_tls_buffer`:\n\nDynamic TLS Buffer\n~~~~~~~~~~~~~~~~~~\n\nMemory Buffer for TLS no longer remains at highest buffer size needed for the server session. Buffer will now free memory during idle connections. Thus drastically improving program footprint.\n\n.. _`_xep_305_quickstart_now_supported`:\n\nXEP-305 Quickstart now supported\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIt’s now possible to establish connection faster due to implementation of `XEP-0305: XMPP Quickstart <https://xmpp.org/extensions/xep-0305.html>`__ (`#1936 <https://projects.tigase.net/issues?q=Redmine%20ID:%201936>`__). Feature is only available for ``c2s`` Connection Manager (i.e. connections on port 5222) and needs to be enabled in ``config.tdsl``\n\n.. code::\n\n   c2s () {\n       'pipelining' = true\n   }\n\n.. _`_database_timestamps`:\n\nDatabase Timestamps\n~~~~~~~~~~~~~~~~~~~\n\nTimestamps in database will be stored using UTC time.\n\n.. _`_config_type_properties_have_changed`:\n\nConfig-type properties have changed\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nConfig-type is now configured using DSL format. Visit `this section <#configType>`__ for more information. The names of different config-type properties have changed: ``default`` replaces ``--gen-config-def``, ``--gen=config-all``, and ``--gen-config-default`` configuration types. ``session-manager`` replaces ``--gen-config-sm``. ``connection-managers`` replaces ``--gen-config-cs``. ``component`` replaces ``--gen-config-comp``. ``setup`` - is a new type of config created for initial configuration of Tigase XMPP Server.\n\n.. note::\n\n   Old versions are no longer supported, you HAVE to replace old versions with the new ones manually when upgrading to v8.0.0.\n\n.. _`_database_watchdog_implemented`:\n\nDatabase Watchdog implemented\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIt is now possible to set connection testing to databases when connections are idle and customize the frequency with which this is done. Visit `this section <#databaseWatchdog>`__ for more details.\n\n.. _`_packet_statistics_expanded`:\n\nPacket statistics expanded\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nPacket statistics both retrieved VIA XMPP and during graceful shutdown have now been separated to a per-XMLNS basis. This may be disabled by adding the following line to ``config.tdsl`` file:\n\n.. code::\n\n   'detailed-other-statistics' = false\n\n.. _`_xep_0016_behavior_changes`:\n\nXEP-0016 Behavior changes\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\nXEP states that Privacy lists should be used when no user session exists in addition to when there is. Previously, Tigase would only filter results when retrieving messages, allowing blocked users to store offline messages. This has now been changed to reflect the XEP properly, and messages will be filtered while there is no user session. If however, you wish to use the previous version, where offline messages are cached first and then filtered, you may use the following configuration:\n\n.. code::\n\n   'sess-man' {\n       'jabber:iq:privacy' () {\n           privacyListOfflineCache (active: true) {\n             size = 20000\n           }\n       }\n   }\n\nBy default, the cache has a limit of 10000 entries, that may be set by using size bean as seen above.\n\n.. _`_access_control_list_has_new_acl_modifiers`:\n\nAccess Control List has new ACL modifiers\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nNew permissions have been added to ACL including ``DOMAIN_OWNER`` and ``DOMAIN_ADMIN`` to reduce permissions checking, and add another level of fine-grained permissions. For more details, please see `Tigase ACL <#accessControlList>`__ configuration for more details.\n\n.. _`_option_to_ignore_schema_version_check_added`:\n\nOption to ignore schema-version check added\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nYou can now skip the schema check phase for individual databases. To do this, add the following do the datasource configuration block:\n\n.. code::\n\n   DataSource () {\n     default () {\n       'schema-management' = false\n     }\n   }\n\nThis will do the following:\n\n-  Print a warning during repository startup.\n\n-  Skip schema upgrades for the source.\n\n-  Skip schema destruction for the source.\n\n.. _`_protection_against_brute_force_attacks`:\n\nProtection against brute-force attacks\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nVersion 8.0.0 improves security by preventing brute-force attacks. Feature needs to be explicitly enabled and configured (on per VHost basis). Detailed configuration is described in `??? <#bruteForcePrevention>`__ (`#8160 <https://projects.tigase.net/issues?q=Redmine%20ID:%208160>`__)\n\nNew Minor Features & Behavior Changes\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n-  `#611 <https://projects.tigase.net/issues?q=Redmine%20ID:%20611>`__ Support for Message of the Day is now enabled in Tigase XMPP Server and can be administered using `XEP-0133 Service Administration <http://xmpp.org/extensions/xep-0133.html#set-motd>`__.\n\n-  `#1569 <https://projects.tigase.net/issues?q=Redmine%20ID:%201569>`__ Re-implemented XEP-0133 Service Administration Scripts ``4.3 Disable User`` and ``4.4 Re-enable User``.\n\n-  `#1449 <https://projects.tigase.net/issues?q=Redmine%20ID:%201449>`__ Monitoring modules now works in OSGi mode.\n\n-  `#1706 <https://projects.tigase.net/issues?q=Redmine%20ID:%201706>`__ ``auto-authorize`` of presence subscriptions can now be set for individual vhosts.\n\n-  `#1968 <https://projects.tigase.net/issues?q=Redmine%20ID:%201968>`__ Added a Proxy Wrapper to handle reconnections to database connection pool to help prevent deadlocking threads.\n\n-  `#3511 <https://projects.tigase.net/issues?q=Redmine%20ID:%203511>`__ Mechanism responsible for closing XMPP in SessionManager has been changed to process all packets from TCP connection before closing connection.\n\n-  `#3802 <https://projects.tigase.net/issues?q=Redmine%20ID:%203802>`__ Implementation and API of LocalEventBus and ClusteredEventBus has been unified and is now available as EventBus.\n\n-  `#3918 <https://projects.tigase.net/issues?q=Redmine%20ID:%203918>`__ Session Establishment Advertisement is now optional, bringing session establishment in line with `RFC 6121 <https://tools.ietf.org/html/rfc6121>`__.\n\n-  `#4111 <https://projects.tigase.net/issues?q=Redmine%20ID:%204111>`__ Changed input buffer sizing to use a ratio of 2 to 1 based on input capacity. No longer using a constant value.\n\n-  `#4212 <https://projects.tigase.net/issues?q=Redmine%20ID:%204212>`__ Database schema files have been flattened and made for better organization.\n\n-  `#4501 <https://projects.tigase.net/issues?q=Redmine%20ID:%204501>`__ ``CounterDataFileLogger`` now has an upper limit and will be default be shrunk to 75% if available disk space is 5% or less than 100MB.\n\n-  `#4654 <https://projects.tigase.net/issues?q=Redmine%20ID:%204654>`__ PubSub component has been updated and new schema uses UTF-8 encoding when hashing database lookup.\n\n-  `#4776 <https://projects.tigase.net/issues?q=Redmine%20ID:%204776>`__ Tigase ``DbSchemaLoader`` now prompts for password if one is missing from command line.\n\n-  `#4788 <https://projects.tigase.net/issues?q=Redmine%20ID:%204788>`__ Push component added to dist-max archive.\n\n-  `#4814 <https://projects.tigase.net/issues?q=Redmine%20ID:%204814>`__ SASL-SCRAM will now be automatically disabled if auth database uses encoded passwords.\n\n-  `#4844 <https://projects.tigase.net/issues?q=Redmine%20ID:%204844>`__ External components can now have SSL socket connections assigned to them.\n\n-  `#4859 <https://projects.tigase.net/issues?q=Redmine%20ID:%204859>`__ Tigase ``DbSchemaLoader`` now can support using SSL when connecting to databases.\n\n-  `#4874 <https://projects.tigase.net/issues?q=Redmine%20ID:%204874>`__ Tigase Test Suite has been updated to correspond to all changes for v8.0.0.\n\n-  `#4877 <https://projects.tigase.net/issues?q=Redmine%20ID:%204877>`__ In-memory repository implemented for **testing ONLY**.\n\n-  `#4880 <https://projects.tigase.net/issues?q=Redmine%20ID:%204880>`__ Tigase config-type settings have been reduced and changed. See `this section <#configType>`__ for more details.\n\n-  `#4908 <https://projects.tigase.net/issues?q=Redmine%20ID:%204908>`__ Limited Ad-hoc execution to admin only within monitor component.\n\n-  `#5005 <https://projects.tigase.net/issues?q=Redmine%20ID:%205005>`__ Detailed logging configuration is now available in DSL format. See xref:[customLogging] for more details.\n\n-  `#5069 <https://projects.tigase.net/issues?q=Redmine%20ID:%205069>`__ Packet processed statistics now separates results based on XML Namespaces.\n\n-  `#5079 <https://projects.tigase.net/issues?q=Redmine%20ID:%205079>`__ Tigase ``DbSchemaLoader`` can now process multiple .sql files in one command by using a comma separated list when calling.\n\n-  `#5086 <https://projects.tigase.net/issues?q=Redmine%20ID:%205086>`__ Tigase server monitor is loaded after delay to prevent NPE during startup.\n\n-  `#5149 <https://projects.tigase.net/issues?q=Redmine%20ID:%205149>`__ ``StanzaReceiver`` and ``StanzaSender`` Components have been deprecated and are no longer part of Tigase XMPP Server. Related SQL tables ``xmpp_stanza`` and ``short_news`` have also been removed from schemas.\n\n-  `#5150 <https://projects.tigase.net/issues?q=Redmine%20ID:%205150>`__ All TigaseDB tables now use the ``tig_`` prefix.\n\n-  `#5214 <https://projects.tigase.net/issues?q=Redmine%20ID:%205214>`__ Check has been added if recipient exists before storing offline messages for local jid.\n\n-  `#5293 <https://projects.tigase.net/issues?q=Redmine%20ID:%205293>`__ ``DbSchemaLoader`` now will fail execution instead of skipping when encountering missing files.\n\n-  `#5379 <https://projects.tigase.net/issues?q=Redmine%20ID:%205379>`__ Server ready detection has been improved in testrunner.sh.\n\n-  `#5397 <https://projects.tigase.net/issues?q=Redmine%20ID:%205397>`__ Webhelp Documentation will no longer be built.\n\n-  `#5422 <https://projects.tigase.net/issues?q=Redmine%20ID:%205422>`__ Errors with Beans will now result in compact and more readable StackTrace print in console log.\n\n-  `#5423 <https://projects.tigase.net/issues?q=Redmine%20ID:%205423>`__ System configuration will now be printed to log file as ``ConfigHolder.loadConfiguration`` output.\n\n-  `#5425 <https://projects.tigase.net/issues?q=Redmine%20ID:%205425>`__ ``GetAnyFile`` and ``GetConfigFile`` scripts moved to message-router instead of basic-conf.\n\n-  `#5429 <https://projects.tigase.net/issues?q=Redmine%20ID:%205429>`__ Adjusted settings for Dynamic Rostering now can use separate beans for multiple implementations.\n\n-  `#5430 <https://projects.tigase.net/issues?q=Redmine%20ID:%205430>`__ ``BindResource`` is now set to FINER log level to reduce console output verbosity.\n\n-  `#5475 <https://projects.tigase.net/issues?q=Redmine%20ID:%205475>`__ Setting default environment variables is now possible in ``config.tdsl`` file using ``env('env-1', 'def-value')`` lines. Details available `in DSL Configuration <#dslEnv>`__ section.\n\n-  `#5496 <https://projects.tigase.net/issues?q=Redmine%20ID:%205496>`__ ``Destroy Schema`` task now added to schema manager.\n\n-  `#5583 <https://projects.tigase.net/issues?q=Redmine%20ID:%205583>`__ Error messages now properly sent when offline message storage is full.\n\n-  `#5674 <https://projects.tigase.net/issues?q=Redmine%20ID:%205674>`__ All components now use UTC timestamp when interacting with databases.\n\n-  `#5800 <https://projects.tigase.net/issues?q=Redmine%20ID:%205800>`__ Better annotation of deprecated code, cleanup and removal code previously marked as deprecated.\n\n-  `#5964 <https://projects.tigase.net/issues?q=Redmine%20ID:%205964>`__ Server version is now added to JMX statistics.\n\n-  `#5982 <https://projects.tigase.net/issues?q=Redmine%20ID:%205982>`__ Remote JVM debugging configuration added to tigase.conf file, commented by default.\n\n-  `#6038 <https://projects.tigase.net/issues?q=Redmine%20ID:%206038>`__ Data Source pool connections are now initialized concurrently instead of one at a time, dropping initializing time.\n\n-  `#6103 <https://projects.tigase.net/issues?q=Redmine%20ID:%206103>`__ :literal:`RosterElement`no longer keeps `XMPPResourceConnection` instance as it is cached elsewhere. Removal results in net improvement in memory footprint.\n\n-  `#6133 <https://projects.tigase.net/issues?q=Redmine%20ID:%206133>`__ Tigase now checks components against server version to ensure compatibility.\n\n-  `#6163 <https://projects.tigase.net/issues?q=Redmine%20ID:%206163>`__ Groovy plugin updated to v2.4.12.\n\n-  `#6206 <https://projects.tigase.net/issues?q=Redmine%20ID:%206206>`__ Separated TigaseXMLTools and TigaseUtil packages for better compatibility with JDK v9.\n\n-  `#6216 <https://projects.tigase.net/issues?q=Redmine%20ID:%206216>`__ MongoDB Driver now updated to v3.5.0.\n\n-  `#6560 <https://projects.tigase.net/issues?q=Redmine%20ID:%206560>`__ tigase anti-spam component now included in tigase dist-max archive.\n\n-  `#6821 <https://projects.tigase.net/issues?q=Redmine%20ID:%206821>`__ Improved error reporting when errors from ``ConfigReader``.\n\n-  `#6842 <https://projects.tigase.net/issues?q=Redmine%20ID:%206842>`__ ``DefaultTypesConverter`` no longer requires case sensitive enums.\n\n-  `#7082 <https://projects.tigase.net/issues?q=Redmine%20ID:%207082>`__ ``ClassUtilBean`` now handles packet filtering for packets part of Tigase Server but not containing beans, other improvements to mDNS.\n\n-  `#7433 <https://projects.tigase.net/issues?q=Redmine%20ID:%207433>`__ ``SeeOtherHost`` no longer uses ``PropertiesBeanConfigurator`` to parse configuration.\n\n-  `#7446 <https://projects.tigase.net/issues?q=Redmine%20ID:%207446>`__ User credentials can now be managed with Ad-hoc commands.\n\n-  `#7743 <https://projects.tigase.net/issues?q=Redmine%20ID:%207743>`__ Improved error message when repository is not found.\n\n-  `#7773 <https://projects.tigase.net/issues?q=Redmine%20ID:%207773>`__ Ad-hoc commands can now by executed asynchronously.\n\n-  `#2341 <https://projects.tigase.net/issues?q=Redmine%20ID:%202341>`__ allow specifying SubscriptionType when adding buddy to avoid calling separately .setBuddySubscription() thus eliminating saving roster twice to database if not needed\n\n.. _`_fixes`:\n\nFixes\n^^^^^\n\n-  `#2750 <https://projects.tigase.net/issues?q=Redmine%20ID:%202750>`__ Multiple artifact and depreciated file cleanup. Massive code cleanup and javadoc cleaning.\n\n-  `#3582 <https://projects.tigase.net/issues?q=Redmine%20ID:%203582>`__ Schema files streamlined, and no longer embedded in code.\n\n-  `#3611 <https://projects.tigase.net/issues?q=Redmine%20ID:%203611>`__ Fixed TheadExceptionHandler caused by ACS unable to read PubSub schema changes.\n\n-  `#3686 <https://projects.tigase.net/issues?q=Redmine%20ID:%203686>`__ Issues with processing XHTML-IM have been fixed, and now render correctly messages with multiple CData items.\n\n-  `#3689 <https://projects.tigase.net/issues?q=Redmine%20ID:%203689>`__ Packets returned from CM no longer bear the original senders' jid.\n\n-  `#3803 <https://projects.tigase.net/issues?q=Redmine%20ID:%203803>`__ New call ``RouteEvent`` has been added to check to list and check events and determine which should be forwarded to other nodes.\n\n-  `#3822 <https://projects.tigase.net/issues?q=Redmine%20ID:%203822>`__ Error is now thrown if listener is registered for an event that is not found in EventBus.\n\n-  `#3910 <https://projects.tigase.net/issues?q=Redmine%20ID:%203910>`__ Fixed NPE in SessionManager when session is closed during execution of everyMinute method.\n\n-  `#3911 <https://projects.tigase.net/issues?q=Redmine%20ID:%203911>`__ Fixed issue of dropping connections during thread load distribution.\n\n-  `#4185 <https://projects.tigase.net/issues?q=Redmine%20ID:%204185>`__ Fixed an error where messages would be duplicated on stream resumption due to a counter being reset upon reconnection.\n\n-  `#4447 <https://projects.tigase.net/issues?q=Redmine%20ID:%204447>`__ Fixed condition where expired messages in offline store would cause locks.\n\n-  `#4547 <https://projects.tigase.net/issues?q=Redmine%20ID:%204547>`__ config.dump file now is fully compatible with init.tdsl file and DSL file formatting.\n\n-  `#4672 <https://projects.tigase.net/issues?q=Redmine%20ID:%204672>`__ Fixed ``UnsupportedOperationException`` occurring during configuration of ``WebSocketConnectionClustered``.\n\n-  `#4776 <https://projects.tigase.net/issues?q=Redmine%20ID:%204776>`__ ``DBSchemaLoader`` now asks for user credentials if parameter is missing. Exceptions are no longer thrown if file specified is not found.\n\n-  `#4885 <https://projects.tigase.net/issues?q=Redmine%20ID:%204885>`__ ``client-port-delay-listening`` no longer causes exception when called.\n\n-  `#4973 <https://projects.tigase.net/issues?q=Redmine%20ID:%204973>`__ Changed Message History query to now include a limit when selecting items, preventing an SQLTimeoutException.\n\n-  `#5005 <https://projects.tigase.net/issues?q=Redmine%20ID:%205005>`__ Fixed an issue where disabling components would result in server shutdown.\n\n-  `#5042 <https://projects.tigase.net/issues?q=Redmine%20ID:%205042>`__ Fixed issue when implementing custom SASL providers, mechanisms and callback handler factories.\n\n-  `#5066 <https://projects.tigase.net/issues?q=Redmine%20ID:%205066>`__ Fixed issue initializing databases using MongoDB.\n\n-  `#5076 <https://projects.tigase.net/issues?q=Redmine%20ID:%205076>`__ last_login and last_logout values are now properly updated while using SASL SCRAM authentication.\n\n-  `#5084 <https://projects.tigase.net/issues?q=Redmine%20ID:%205084>`__ SCRAM now checks to see if account is disabled before retrieving password.\n\n-  `#5085 <https://projects.tigase.net/issues?q=Redmine%20ID:%205085>`__ Fixed ``too many beans implemented`` error in Monitor Component.\n\n-  `#5088 <https://projects.tigase.net/issues?q=Redmine%20ID:%205088>`__ Removed unnecessary SASL request processing after session is closed.\n\n-  `#5118 <https://projects.tigase.net/issues?q=Redmine%20ID:%205118>`__ Fixed NPE during query of privacy lists then ``type`` is missing.\n\n-  `#5303 <https://projects.tigase.net/issues?q=Redmine%20ID:%205303>`__ Fixed beans not being overridden by configuration if they were registered in ``RegistrarBean`` or ``AbstractKernelBasedComponent``.\n\n-  `#5311 <https://projects.tigase.net/issues?q=Redmine%20ID:%205311>`__ Offline messages are no longer dumped from MongoDB when restarting server.\n\n-  `#5394 <https://projects.tigase.net/issues?q=Redmine%20ID:%205394>`__ Loading main Derby schema no longer throws exceptions.\n\n-  `#5428 <https://projects.tigase.net/issues?q=Redmine%20ID:%205428>`__ Fixed parsing of v-host per domain limit property.\n\n-  `#5450 <https://projects.tigase.net/issues?q=Redmine%20ID:%205450>`__ Server no longer automatically shuts down when default or other db can not be found or accessed.\n\n-  `#5458 <https://projects.tigase.net/issues?q=Redmine%20ID:%205458>`__ Fixed potential timeout arising from ``XMPPIOService::xmppStreamOpened()`` method.\n\n-  `#5480 <https://projects.tigase.net/issues?q=Redmine%20ID:%205480>`__ Fixed issue in Derby DB where obtaining offline messages results in SQLException.\n\n-  `#5525 <https://projects.tigase.net/issues?q=Redmine%20ID:%205525>`__ Fixed S2S ``invalid-namespace`` error being returned during connection establishment.\n\n-  `#5587 <https://projects.tigase.net/issues?q=Redmine%20ID:%205587>`__ Fixed unclosed ``ResultSet`` when storing a message to AMP-offline database in Derby causing deadlock.\n\n-  `#5645 <https://projects.tigase.net/issues?q=Redmine%20ID:%205645>`__ Added fix for possible NPE when failing to retrieve beans.\n\n-  `#5670 <https://projects.tigase.net/issues?q=Redmine%20ID:%205670>`__ config-dump now prints configuration for inactive components and beans to log.\n\n-  `#5692 <https://projects.tigase.net/issues?q=Redmine%20ID:%205692>`__ Messages sent with negative priority were being occasionally dropped and not processed to ``OfflineMessageHandler``.\n\n-  `#5727 <https://projects.tigase.net/issues?q=Redmine%20ID:%205727>`__ Fixed potential issue with MySQL procedures not being killed properly.\n\n-  `#5750 <https://projects.tigase.net/issues?q=Redmine%20ID:%205750>`__ Statistics now filter out zero-value results unless FINEST level is requested.\n\n-  `#5831 <https://projects.tigase.net/issues?q=Redmine%20ID:%205831>`__ Fixed occurrence of ``OutOfMemory`` error.\n\n-  `#5864 <https://projects.tigase.net/issues?q=Redmine%20ID:%205864>`__ Fixed NPE when executing BOSH pre-bind script.\n\n-  `#5867 <https://projects.tigase.net/issues?q=Redmine%20ID:%205867>`__ Fixed NPE occurring during configuration dump.\n\n-  `#6000 <https://projects.tigase.net/issues?q=Redmine%20ID:%206000>`__ Fixed a few issues with dynamic rosters properly handling presence subscription requests.\n\n-  `#6006 <https://projects.tigase.net/issues?q=Redmine%20ID:%206006>`__ Improved configuration file and DB Schema handling.\n\n-  `#6041 <https://projects.tigase.net/issues?q=Redmine%20ID:%206041>`__ Fixed potential issue where vhosts DB could be overwritten by vhosts configuration in ``init.config``.\n\n-  `#6078 <https://projects.tigase.net/issues?q=Redmine%20ID:%206078>`__ Fixed ``ClusterConnectionManager`` to use custom_elements_limit instead of a fixed value.\n\n-  `#6080 <https://projects.tigase.net/issues?q=Redmine%20ID:%206080>`__ Fixed Packet Filtering to not filter cluster node information requests.\n\n-  `#6083 <https://projects.tigase.net/issues?q=Redmine%20ID:%206083>`__ Fixed clustered mode shutting down server when certain components are disabled.\n\n-  `#6135 <https://projects.tigase.net/issues?q=Redmine%20ID:%206135>`__ Tigase now properly enabled selective TLS if not enabled globally.\n\n-  `#6140 <https://projects.tigase.net/issues?q=Redmine%20ID:%206140>`__ Fixed issue while sending server welcome message.\n\n-  `#6141 <https://projects.tigase.net/issues?q=Redmine%20ID:%206141>`__ Fixed NPE at startup.\n\n-  `#6234 <https://projects.tigase.net/issues?q=Redmine%20ID:%206234>`__ Fixed an error where an error message would repeat unnecessarily.\n\n-  `#6284 <https://projects.tigase.net/issues?q=Redmine%20ID:%206284>`__ Ad-hoc commands now refresh SSL Certificate, and restart is no longer required.\n\n-  `#6293 <https://projects.tigase.net/issues?q=Redmine%20ID:%206293>`__ Server no longer sends no response upon setting empty photo in vCard.\n\n-  `#6263 <https://projects.tigase.net/issues?q=Redmine%20ID:%206263>`__ Fixed missing namespaces in responses from adhoc commands.\n\n-  `#6400 <https://projects.tigase.net/issues?q=Redmine%20ID:%206400>`__ Added a proper error when max-queue-size is too small and server cannot start.\n\n-  `#6408 <https://projects.tigase.net/issues?q=Redmine%20ID:%206408>`__ Fixed an issue where single WebSocket frames contained multiple XML stanzas instead of one per frame.\n\n-  `#6411 <https://projects.tigase.net/issues?q=Redmine%20ID:%206411>`__ Main kernel is now called to smooth shutdown. Further, timeout periods are opened up for large instances.\n\n-  `#6574 <https://projects.tigase.net/issues?q=Redmine%20ID:%206574>`__ SSL certificate upload handling is now fixed within cluster mode.\n\n-  `#6598 <https://projects.tigase.net/issues?q=Redmine%20ID:%206598>`__ Fixed EventBus Registration connection issues between cluster nodes.\n\n-  `#6658 <https://projects.tigase.net/issues?q=Redmine%20ID:%206658>`__ Cluster connections no longer potentially keep open connection after cluster is no longer connected or available.\n\n-  `#6749 <https://projects.tigase.net/issues?q=Redmine%20ID:%206749>`__ Fixed schema parsing for DerbyDB.\n\n-  `#6776 <https://projects.tigase.net/issues?q=Redmine%20ID:%206776>`__ Fixed failing Websocket connections if header contains more than one value.\n\n-  `#6875 <https://projects.tigase.net/issues?q=Redmine%20ID:%206875>`__ Fixed an issue where C2S connections could be accepted before SessionManager was initialized.\n\n-  `#7037 <https://projects.tigase.net/issues?q=Redmine%20ID:%207037>`__ Fixed error while parsing negative values from ``config.tdsl`` file.\n\n-  `#7055 <https://projects.tigase.net/issues?q=Redmine%20ID:%207055>`__ Improvements to metaspace use and other memory use tweaks.\n\n-  `#7304 <https://projects.tigase.net/issues?q=Redmine%20ID:%207304>`__ Virtual host logs now properly follow log size limits.\n\n-  `#7431 <https://projects.tigase.net/issues?q=Redmine%20ID:%207431>`__ AdHoc requests between the same user with different resources are no longer dropped with \\`NoConnectionIdExecption`error.\n\n-  `#7434 <https://projects.tigase.net/issues?q=Redmine%20ID:%207434>`__ Adjusted ``SeeOtherHotDualIP`` to use new table name in cluster nodes database.\n\n-  `#7491 <https://projects.tigase.net/issues?q=Redmine%20ID:%207491>`__ Stacktraces from ``CertificateContainer`` are no longer printed to tigase-console.log, but will be printed to tigase.log.\n\n-  `#7687 <https://projects.tigase.net/issues?q=Redmine%20ID:%207687>`__ Fixed an error where connections failed after authentication timeout were marked as active after cleanup.\n\n-  `#7747 <https://projects.tigase.net/issues?q=Redmine%20ID:%207747>`__ Fixed ``ClusterRepoItemEvent`` serialization issues causing unsupported conversion error in cluster mode.\n\n-  `#7495 <https://projects.tigase.net/issues?q=Redmine%20ID:%207495>`__ fix issue with not all logs being obfuscated, added testcase, documentation\n\n-  `#8305 <https://projects.tigase.net/issues?q=Redmine%20ID:%208305>`__ fix issue with SeeOtherHostDualIP when using MongoDB\n\n.. _`_component_changes`:\n\nComponent Changes\n^^^^^^^^^^^^^^^^^\n\nAMP\n~~~\n\n-  `#7301 <https://projects.tigase.net/issues?q=Redmine%20ID:%207301>`__ Tigase AMP component now uses multiple processing threads.\n\n.. _`_pubsub`:\n\nPubSub\n~~~~~~\n\n-  `#5033 <https://projects.tigase.net/issues?q=Redmine%20ID:%205033>`__ PubSub now compatible with using emojis in pubsub items.\n\n-  `#5693 <https://projects.tigase.net/issues?q=Redmine%20ID:%205693>`__ Fixed parsing configuration of SessionManager processors.\n\n-  `#5766 <https://projects.tigase.net/issues?q=Redmine%20ID:%205766>`__ PubSub now writes to all databases with UTC timestamp.\n\n-  `#5953 <https://projects.tigase.net/issues?q=Redmine%20ID:%205953>`__ Fixed presences not being removed from ``presenceByService`` collection if client disconnects without ``<unavailable/>`` presence being sent.\n\n-  `#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ version changed to PubSub v4.0.0.\n\n-  `#7707 <https://projects.tigase.net/issues?q=Redmine%20ID:%207707>`__ Fixed potential NPE in PubSub.\n\n.. _`_http_api`:\n\nhttp-api\n~~~~~~~~\n\n-  `#4873 <https://projects.tigase.net/issues?q=Redmine%20ID:%204873>`__ Support added to display timestamp fields as data, time, and timezone fields.\n\n-  `#4876 <https://projects.tigase.net/issues?q=Redmine%20ID:%204876>`__ Implemented using XML repository for new setups, and updated default config to use this.\n\n-  `#4888 <https://projects.tigase.net/issues?q=Redmine%20ID:%204888>`__ ``http-api`` now is enabled by default.\n\n-  `#5209 <https://projects.tigase.net/issues?q=Redmine%20ID:%205209>`__ Updated visual styling of pages hosted by component.\n\n-  `#5290 <https://projects.tigase.net/issues?q=Redmine%20ID:%205290>`__ Fixed invalid property name.\n\n-  `#5316 <https://projects.tigase.net/issues?q=Redmine%20ID:%205316>`__ Account Registration now can now require and send confirmation E-mails.\n\n-  `#5415 <https://projects.tigase.net/issues?q=Redmine%20ID:%205415>`__ Web Setup now checks configuration for message archive conflicts.\n\n-  `#5460 <https://projects.tigase.net/issues?q=Redmine%20ID:%205460>`__ MongoDB now supported through web-setup.\n\n-  `#5717 <https://projects.tigase.net/issues?q=Redmine%20ID:%205717>`__ Fixed default values of check-boxes in admin UI not being shown.\n\n-  `#5950 <https://projects.tigase.net/issues?q=Redmine%20ID:%205950>`__ Supported added for `XEP-0363: HTTP File Upload <https://xmpp.org/extensions/xep-0363.html>`__.\n\n-  `#6159 <https://projects.tigase.net/issues?q=Redmine%20ID:%206159>`__ Fixed NPE thrown if scripts directory is not present.\n\n-  `#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ version changed to tigase-http-api v2.0.0.\n\n-  `#6212 <https://projects.tigase.net/issues?q=Redmine%20ID:%206212>`__ Added mechanism for password changing through HTTP API.\n\n-  `#7307 <https://projects.tigase.net/issues?q=Redmine%20ID:%207307>`__ Fixed scripts returning 404 while handling rest/user/ requests even though user exists.\n\n-  `#7178 <https://projects.tigase.net/issues?q=Redmine%20ID:%207178>`__ Ad-hoc commands are now categorized in groups for better organization.\n\n-  `#7568 <https://projects.tigase.net/issues?q=Redmine%20ID:%207568>`__ Added timeout reading for HTTP request headers, added configurable ``accept-timeout``.\n\n.. _`_message_archive`:\n\nmessage-archive\n~~~~~~~~~~~~~~~\n\n-  `#4867 <https://projects.tigase.net/issues?q=Redmine%20ID:%204867>`__ fixed issue when changing MA jid.\n\n-  `#4888 <https://projects.tigase.net/issues?q=Redmine%20ID:%204888>`__ ``message-archive`` is enabled by default.\n\n-  `#5033 <https://projects.tigase.net/issues?q=Redmine%20ID:%205033>`__ Update message archive to be compatible with emojis.\n\n-  `#5391 <https://projects.tigase.net/issues?q=Redmine%20ID:%205391>`__ Added missing query statement block starts and ends to be compatible with SQL Server.\n\n-  `#5604 <https://projects.tigase.net/issues?q=Redmine%20ID:%205604>`__ Modified access to static fields and functions.\n\n-  `#5681 <https://projects.tigase.net/issues?q=Redmine%20ID:%205681>`__ Fixed duplication of groupchat messages with different ids by modifying hash algorithm.\n\n-  `#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ version changed to message-archive v2.0.0.\n\n-  `#7615 <https://projects.tigase.net/issues?q=Redmine%20ID:%207615>`__ ``feature-not-implemented`` response no longer occurs when removing stored messages.\n\n.. _`_muc`:\n\nMUC\n~~~\n\n-  `#4888 <https://projects.tigase.net/issues?q=Redmine%20ID:%204888>`__ ``muc`` now is enabled by default.\n\n-  `#5033 <https://projects.tigase.net/issues?q=Redmine%20ID:%205033>`__ MUC component is now compatible with emojis.\n\n-  `#5066 <https://projects.tigase.net/issues?q=Redmine%20ID:%205066>`__ Fixed issues working with MongoDB repository.\n\n-  `#5085 <https://projects.tigase.net/issues?q=Redmine%20ID:%205085>`__ Removed invalid annotation parameter values.\n\n-  `#5559 <https://projects.tigase.net/issues?q=Redmine%20ID:%205559>`__ Fixed NPE while changing default room configuration.\n\n-  `#5666 <https://projects.tigase.net/issues?q=Redmine%20ID:%205666>`__ User may add more than one ``<item/>`` elements to query when querying room members.\n\n-  `#5715 <https://projects.tigase.net/issues?q=Redmine%20ID:%205715>`__ Welcome messages may now be disabled globally, or in individual room configurations.\n\n-  `#5736 <https://projects.tigase.net/issues?q=Redmine%20ID:%205736>`__ Rooms with no subject now return empty ``<subject/>`` element, as per `XEP-0048 7.2.16 <https://xmpp.org/extensions/xep-0045.html#enter-subject>`__.\n\n-  `#5813 <https://projects.tigase.net/issues?q=Redmine%20ID:%205813>`__ Fixed NPE during room creation.\n\n-  `#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ version changed to tigase-muc v3.0.0.\n\n-  `#6395 <https://projects.tigase.net/issues?q=Redmine%20ID:%206395>`__ Fixed ``tigase.db.UserNotFoundException`` during retrieval of MUC user.\n\n-  `#6734 <https://projects.tigase.net/issues?q=Redmine%20ID:%206734>`__ Introduced ``muc#roomconfig_maxresources`` to allow configuration of max number of resources for a single occupant.\n\n-  `#7443 <https://projects.tigase.net/issues?q=Redmine%20ID:%207443>`__ Disabled XEP-0091 by default, added history attribute validation.\n\n.. _`_socks5_proxy`:\n\nsocks5 Proxy\n~~~~~~~~~~~~\n\n-  `#2750 <https://projects.tigase.net/issues?q=Redmine%20ID:%202750>`__ Cleanup of code and removal of empty javadocs.\n\n-  `#5867 <https://projects.tigase.net/issues?q=Redmine%20ID:%205867>`__ Fixed NPE during configuration dump when component is disabled.\n\n-  `#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ version changed to tigase-socks5 v2.0.0.\n\n.. _`_stats`:\n\nstats\n~~~~~\n\n-  `#5206 <https://projects.tigase.net/issues?q=Redmine%20ID:%205206>`__ Fixed exception causing duplicate error entry.\n\n-  `#5728 <https://projects.tigase.net/issues?q=Redmine%20ID:%205728>`__ Fixed ``MySQLIntegrityConstraintViolationException`` in upload handler.\n\n-  `#6161 <https://projects.tigase.net/issues?q=Redmine%20ID:%206161>`__ Removed usage of classes from javax.xml.ws package for JDKv9 compatibility.\n\n.. _`_stun_server`:\n\nSTUN Server\n~~~~~~~~~~~\n\n-  `#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ version changed to tigase-stun v2.0.0.\n\n.. _`_websocket`:\n\nWebSocket\n~~~~~~~~~\n\n-  `#6481 <https://projects.tigase.net/issues?q=Redmine%20ID:%206481>`__ Websocket component has been improved to be more compliant with `rfc6455 <https://tools.ietf.org/html/rfc6455>`__\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc",
    "content": "Tigase XMPP Server 8.1.0 Change notes and announcement\n------------------------------------------------------\n\nMajor Changes\n^^^^^^^^^^^^^\n\nMore XMPP extensions\n~~~~~~~~~~~~~~~~~~~~\n\nFollowing XMPP guidelines specified in *Compliance Suites* a number of extensions was included in this release:\n\n-  **XEP-0157**: Contact Addresses for XMPP Services (`server-995 <https://projects.tigase.net/issue/server-995>`__) that can be configured on per VHost basis (`server-1015 <https://projects.tigase.net/issue/server-1015>`__)\n\n-  **XEP-0398**: User Avatar to vCard-Based Avatars Conversion (`server-1017 <https://projects.tigase.net/issue/server-1017>`__)\n\n-  **XEP-0156**: Discovering Alternative XMPP Connection Methods - Tigase already supported handling DNS queries and standardised our ``webservice`` to XEP-0156 (`http-76 <https://projects.tigase.net/issue/http-76>`__)\n\n-  **XEP-0410**: MUC Self-Ping (Schrödinger’s Chat) (`muc-122 <https://projects.tigase.net/issue/http-76>`__)\n\n-  **XEP-0153**: vCard-Based Avatars - added support for setting **vCard avatar for MUC rooms** (`muc-112 <https://projects.tigase.net/issue/http-76>`__)\n\n-  **XEP-0411**: Bookmarks Conversion (`pubsub-79 <https://projects.tigase.net/issue/http-76>`__)\n\n-  **XEP-0157**: Contact Addresses for XMPP Services (`server-995 <https://projects.tigase.net/issue/server-995>`__)\n\n.. _`_improved_connectivity_with_other_servers`:\n\nImproved connectivity with other servers\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n``SASL-EXTERNAL`` mechanism was added for server-to-server (federated, s2s) connections greatly improving compliance with XMPP network. It’s possible to use both SASL-EXTERNAL and Diallback depending on support in other servers.\n\n.. _`_better_security_privacy`:\n\nBetter security & privacy\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWhen it comes to connectivity, Tigase XMPP Server sported **Hardened Mode** that adjusted networking security settings (supported protocols, cipher suites and keys' length where applicable). We decided include 3-level configuration option for **Hardened Mode** (roughly following *Mozilla’s SSL Configuration Generator*): ``relaxed``, ``secure`` (default) and ``strict`` and to further eliminate cipher suites that are currently considered insecure.\n\nWe also enabled by default our anti-spam plugin and because we like all-things-extensible we created a guide how to create your own pluggable filters for anti-spam-plugin.\n\n.. _`_multiple_domains_vhosts_support_is_even_better`:\n\nMultiple domains (VHosts) support is even better\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIt was always quite easy to configure and serve multiple domains in Tigase XMPP Server. In this release we made it even better! First of all - we included ``Default`` VHost item, which allows configuring global defaults for the installation on the fly without having to change configuration files and restart the instance.\n\nInternally, we introduced *VHost Extensions* - a mechanism that allows easy addition of configurable options that can be set on per-domain basis.\n\nOn top of that we reworked how SSL certificates are handled (especially wildcard ones) and now they are loaded and assigned to correct domain automatically - no need to configure *star*-certificates manually anymore.\n\n.. _`_mobile_first`:\n\nMobile First\n~~~~~~~~~~~~~~~~~~~~~~\n\nNotifications send to mobile applications via Apple’s and Google’s push servers using **Tigase’s PUSH component** are now encrypted (`#push-25 <https://projects.tigase.net/issue/push-25>`__), requires compatible clients)\n\nMUC component now allows users to register permanent nickname, which makes it possible to receive PUSH notifications even if our client disconnects and is offline (`#muc-115 <https://projects.tigase.net/issue/muc-115>`__)\n\n.. _`_installation_management`:\n\nInstallation & management\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe (web) installer was simplified making setting up and configuring Tigase even easier (`#http-78 <https://projects.tigase.net/issue/http-78>`__) - now it’s only needed to select desired database, provide it’s details and eventually adjust which components and plugins should be enabled or disabled, but we believe that provided defaults should work well in most of the cases.\n\nAfter the installation and startup, it’s possible to see basic instance state via web browser either opening ``/server/`` endpoint (`#server-1164 <https://projects.tigase.net/issue/server-1164>`__), or local file from ``logs/server-info.html``) and manage the installation using Admin WebUI, that received slight visual face-lift (`#http-90 <https://projects.tigase.net/issue/http-90>`__)\n\n.. _`_noteworthy`:\n\nNoteworthy\n~~~~~~~~~~~~~~\n\n-  Startup time was significantly reduced due to improvements of creating repository pools (`#server-1149 <https://projects.tigase.net/issue/server-1149>`__)\n\n-  Multi-thread, highly concurrent script execution was improved (`#server-1154 <https://projects.tigase.net/issue/server-1154>`__)\n\n-  StreamManagement was available, but in this version we decided to enabled it by default.\n\n-  More places offers support for `XEP-0059: Result Set Management <https://xmpp.org/extensions/xep-0059.html>`__ - namely PubSub nodes discovery and ``jabber:iq:serach``\n\n-  `Publishing Options <https://xmpp.org/extensions/xep-0060.html#publisher-publish-options>`__ were added to PubSub (`#pubsub-75 <https://projects.tigase.net/issue/pubsub-75>`__)\n\nNew Minor Features & Behavior Changes\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n-  `server-918 <https://projects.tigase.net/issue/server-918>`__: AWS obtain public IP and/or DNS address of the EC2 instance\n\n-  `server-985 <https://projects.tigase.net/issue/server-985>`__: Add support for SCRAM-SHA-512(-PLUS)\n\n-  `spam-8 <https://projects.tigase.net/issue/spam-8>`__: Enable spam processor by default\n\n-  `server-1012 <https://projects.tigase.net/issue/server-1012>`__: UserDomainFilter.groovy fails to load\n\n-  `server-1014 <https://projects.tigase.net/issue/server-1014>`__: Can’t upgrade from 8.0.0GA to 8.1.0-SNAPSHOT\n\n-  `server-798 <https://projects.tigase.net/issue/server-798>`__: Limit number of messages that are stored in DB per user within a period of time\n\n-  `server-827 <https://projects.tigase.net/issue/server-827>`__: Seperate Component-based statistics\n\n-  `server-1026 <https://projects.tigase.net/issue/server-1026>`__: NPE: in JabberIqRegister/EmailConfirmationSender\n\n-  `pubsub-82 <https://projects.tigase.net/issue/pubsub-82>`__: NPE in RetrieveItemsModule\n\n-  `tigaseim-78 <https://projects.tigase.net/issue/tigaseim-78>`__: IPv6 connectivity issue\n\n-  `server-239 <https://projects.tigase.net/issue/server-239>`__: OSGi mode - exceptions in logs\n\n-  `server-1020 <https://projects.tigase.net/issue/server-1020>`__: Enable stream management by default\n\n-  `pubsub-83 <https://projects.tigase.net/issue/pubsub-83>`__: NPE in PublishItemModule\n\n-  `pubsub-81 <https://projects.tigase.net/issue/pubsub-81>`__: Exception during execution of event: tigase.pubsub.modules.PresenceCollectorModule.PresenceChangeEvent\n\n-  `server-1021 <https://projects.tigase.net/issue/server-1021>`__: NPE: Cannot update BruteForceLocker\n\n-  `server-826 <https://projects.tigase.net/issue/server-826>`__: UserRepository caches force synchronization even if caching is disabled\n\n-  `server-958 <https://projects.tigase.net/issue/server-958>`__: Add timeout for opened TCP connections\n\n-  `server-1029 <https://projects.tigase.net/issue/server-1029>`__: Read receipients are not copied via carbons\n\n-  `server-1015 <https://projects.tigase.net/issue/server-1015>`__: Allow configuring XEP-0157: Contact Addresses on per VHost basis\n\n-  `pubsub-65 <https://projects.tigase.net/issue/pubsub-65>`__: RSM and jabber:search for pubsub discovery\n\n-  `server-1030 <https://projects.tigase.net/issue/server-1030>`__: NPE in VCardTemp when processing initial presence\n\n-  `http-72 <https://projects.tigase.net/issue/http-72>`__: Change Content-Disposition from attachment to inline\n\n-  `server-1045 <https://projects.tigase.net/issue/server-1045>`__: NPE in DiscoExtensionsForm\n\n-  `server-1048 <https://projects.tigase.net/issue/server-1048>`__: Update parent pom and information about suggested JDK\n\n-  `push-23 <https://projects.tigase.net/issue/push-23>`__: [JDK12] Can’t establish encrypted connection with Push/FCM\n\n-  `server-978 <https://projects.tigase.net/issue/server-978>`__: Improve VHost configuration / extending\n\n-  `server-1068 <https://projects.tigase.net/issue/server-1068>`__: Improve LogFormat readability (and maybe performance)\n\n-  `server-1070 <https://projects.tigase.net/issue/server-1070>`__: Improve privacy list loggging\n\n-  `server-1071 <https://projects.tigase.net/issue/server-1071>`__: NPE in IOService.accept\n\n-  `server-710 <https://projects.tigase.net/issue/server-710>`__: Registration improvements\n\n-  `pubsub-79 <https://projects.tigase.net/issue/pubsub-79>`__: XEP-0411: Bookmarks Conversion\n\n-  `pubsub-75 <https://projects.tigase.net/issue/pubsub-75>`__: Add support for Publishing Options\n\n-  `server-1017 <https://projects.tigase.net/issue/server-1017>`__: XEP-0398: User Avatar to vCard-Based Avatars Conversion\n\n-  `server-994 <https://projects.tigase.net/issue/server-994>`__: Add server support for Entity Capabilities: Stream Feature\n\n-  `server-995 <https://projects.tigase.net/issue/server-995>`__: XEP-0157: Contact Addresses for XMPP Services\n\n-  `http-76 <https://projects.tigase.net/issue/http-76>`__: Standardise DNS webservice to XEP-0156\n\n-  `server-1109 <https://projects.tigase.net/issue/server-1109>`__: Add recommended JDK version to documentation\n\n-  `push-28 <https://projects.tigase.net/issue/push-28>`__: Non-tigase notifications should use high priority (APNS)\n\n-  `server-1114 <https://projects.tigase.net/issue/server-1114>`__: Can’t register on sure.im with StorkIM\n\n-  `server-1005 <https://projects.tigase.net/issue/server-1005>`__: Flatten schema to match versioning document\n\n-  `server-1116 <https://projects.tigase.net/issue/server-1116>`__: account_status is not checked\n\n-  `server-1074 <https://projects.tigase.net/issue/server-1074>`__: Hardened Mode improvements\n\n-  `server-1125 <https://projects.tigase.net/issue/server-1125>`__: StatsDumper.groovy doesn’t work in documentation in 8.x\n\n-  `http-85 <https://projects.tigase.net/issue/http-85>`__: Pasword resset doesn’t work\n\n-  `server-1128 <https://projects.tigase.net/issue/server-1128>`__: Possible vulnerability in XML parser\n\n-  `server-1130 <https://projects.tigase.net/issue/server-1130>`__: NPE i JabberIqAuth\n\n-  `http-84 <https://projects.tigase.net/issue/http-84>`__: Configurable ``resetPassword`` endpoint hostname\n\n-  `server-1129 <https://projects.tigase.net/issue/server-1129>`__: BOSH timeouts on GET requests\n\n-  `prv-436 <https://projects.tigase.net/issue/prv-436>`__: Conversations compliance - contact developers\n\n-  `server-1100 <https://projects.tigase.net/issue/server-1100>`__: CAAS and WS testers fail to connect to wss://tigase.im:5291\n\n-  `server-1047 <https://projects.tigase.net/issue/server-1047>`__: Add SASL-EXTERNAL on s2s conections\n\n-  `server-1103 <https://projects.tigase.net/issue/server-1103>`__: High priority PUSH notifications are sent for all messages\n\n-  `pubsub-93 <https://projects.tigase.net/issue/pubsub-93>`__: NPE in CapsChangeEvent\n\n-  `server-1137 <https://projects.tigase.net/issue/server-1137>`__: Don’t require setting JAVA_HOME to start server\n\n-  `server-1136 <https://projects.tigase.net/issue/server-1136>`__: upgrade-schema --help not available\n\n-  `utils-19 <https://projects.tigase.net/issue/utils-19>`__: tigase-utils doesn’t compile with JDK12\n\n-  `server-1138 <https://projects.tigase.net/issue/server-1138>`__: Schema files are not sorted correctly during loading\n\n-  `pubsub-98 <https://projects.tigase.net/issue/pubsub-98>`__: Resources with emoji chars are causing issues with MySQL backend\n\n-  `server-1110 <https://projects.tigase.net/issue/server-1110>`__: Disabling TLS in VHost configuration doesn’t work\n\n-  `server-1078 <https://projects.tigase.net/issue/server-1078>`__: Don’t send root CA certificate in chain\n\n-  `server-1113 <https://projects.tigase.net/issue/server-1113>`__: Don’t advertise SASL-EXTERNAL if own certificate is not valid\n\n-  `http-78 <https://projects.tigase.net/issue/http-78>`__: Simplify installer\n\n-  `server-1133 <https://projects.tigase.net/issue/server-1133>`__: Not able to connect via S2S to server with incorrect SSL certificate\n\n-  `serverdistribution-2 <https://projects.tigase.net/issue/serverdistribution-2>`__: MUC upgrade not linked correctly in global tigase guide\n\n-  `server-1149 <https://projects.tigase.net/issue/server-1149>`__: Reduce startup time with a lot of database connections\n\n-  `server-1148 <https://projects.tigase.net/issue/server-1148>`__: \"ERROR! Component <x> schema version is not loaded in the database or it is old!\" during shutdown\n\n-  `server-1153 <https://projects.tigase.net/issue/server-1153>`__: Refactor Credentials related ``username`` to ``credentialId`` to avoid confussion\n\n-  `servers-312 <https://projects.tigase.net/issue/servers-312>`__: No cluster connection to send a packet\n\n-  `server-1154 <https://projects.tigase.net/issue/server-1154>`__: Multi-thread script execution yields wrong results\n\n-  `servers-294 <https://projects.tigase.net/issue/servers-294>`__: Can’t connect from tigase.im to rsocks.net\n\n-  `server-1111 <https://projects.tigase.net/issue/server-1111>`__: Can’t establish s2s to upload.pouet.ovh\n\n-  `server-1143 <https://projects.tigase.net/issue/server-1143>`__: S2S connectivity issue with OpenFire when SASL external is used\n\n-  `servers-309 <https://projects.tigase.net/issue/servers-309>`__: Issue when connecting to xabber.org: not-authorized: self signed certificate\n\n-  `tigaseim-80 <https://projects.tigase.net/issue/tigaseim-80>`__: Siskin IM push server is not accessible\n\n-  `server-1080 <https://projects.tigase.net/issue/server-1080>`__: After updating certificate via ad-hoc/rest only main certificate is updated\n\n-  `http-88 <https://projects.tigase.net/issue/http-88>`__: Improve REST documentation\n\n-  `http-87 <https://projects.tigase.net/issue/http-87>`__: \"request accept time exceeded\" for every request when using ``JavaStandaloneHttpServer``\n\n-  `server-1151 <https://projects.tigase.net/issue/server-1151>`__: BruteForceLockerExtension (and possibly others) settings are not correctly retrieved\n\n-  `http-89 <https://projects.tigase.net/issue/http-89>`__: Drop result/error packages received by HTTP-API if no connection present to write response to\n\n-  `pubsub-99 <https://projects.tigase.net/issue/pubsub-99>`__: Notifications are not sent for +notify from nodes with whitelist access mode\n\n-  `pubsub-79 <https://projects.tigase.net/issue/pubsub-79>`__: XEP-0411: Bookmarks Conversion\n\n-  `server-1157 <https://projects.tigase.net/issue/server-1157>`__: SCRAM-SHA512 not working\n\n-  `server-1159 <https://projects.tigase.net/issue/server-1159>`__: Improve handling establishing and terminating of the session\n\n-  `server-1152 <https://projects.tigase.net/issue/server-1152>`__: Cleanup warnings from JDBCMsgRepository\n\n-  `server-1112 <https://projects.tigase.net/issue/server-1112>`__: Fallback to diallback if SASL-EXTERNAL fails\n\n-  `servers-292 <https://projects.tigase.net/issue/servers-292>`__: S2S connectivity issues\n\n-  `acspubsub-19 <https://projects.tigase.net/issue/acspubsub-19>`__: REST execution fails on other nodes\n\n-  `server-1145 <https://projects.tigase.net/issue/server-1145>`__: Race condition during storing/loading of offline messages\n\n-  `http-90 <https://projects.tigase.net/issue/http-90>`__: Add direct links to most useful task in AdminUI main page\n\n-  `spam-10 <https://projects.tigase.net/issue/spam-10>`__: Add documentation for creation of a custom filter\n\n-  `server-1163 <https://projects.tigase.net/issue/server-1163>`__: Review and update ``SASL Custom Mechanisms and Configuration`` documentation\n\n-  `server-1164 <https://projects.tigase.net/issue/server-1164>`__: After-installation report - installation status\n\n-  `systems-76 <https://projects.tigase.net/issue/systems-76>`__: Fix issue with StackOverflow due to recursive call in TLSIO; improve debug log\n\n-  `server-1082 <https://projects.tigase.net/issue/server-1082>`__: Sec-WebSocket-Accept not calculated correctly\n\n-  `server-1083 <https://projects.tigase.net/issue/server-1083>`__: Messages sent to full jid are returned with error\n\n-  `push-25 <https://projects.tigase.net/issue/push-25>`__: Add support for sending encrypted PUSHes\n\n-  `server-1085 <https://projects.tigase.net/issue/server-1085>`__: Improve retrieval of values for all keys in a node in UserRepository\n\n-  `muc-115 <https://projects.tigase.net/issue/muc-115>`__: Add support for MUC and offline message delivery\n\n-  `muc-122 <https://projects.tigase.net/issue/muc-122>`__: XEP-0410: MUC Self-Ping (Schrödinger’s Chat)\n\n-  `muc-112 <https://projects.tigase.net/issue/muc-112>`__: Support for setting vCard avatar for room\n\n-  `http-83 <https://projects.tigase.net/issue/http-83>`__: Issue with multithreading access to HttpExchange instance\n\n-  `httpapijetty-3 <https://projects.tigase.net/issue/httpapijetty-3>`__: Support for HTTP/2\n\n-  `httpapijetty-6 <https://projects.tigase.net/issue/httpapijetty-6>`__: Update Jetty version\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc",
    "content": "Tigase XMPP Server 8.2.0 Change notes\n-------------------------------------\n\nMajor Changes\n^^^^^^^^^^^^^\n\n-  **Improvements to s2s connection**: Version 8.2.0 brings a lot of improvements related to s2s connectivity: support for TLS1.3, improved logic during authentication and stream negotiation solving connectivity issues with various deployments\n\n-  **Better handling of certificates**: It’s now possible to store certificates in the database making it easier to manage them in clustered environment.\n\n-  Deprecation of ``Element`` based events in favour of Object based events\n\n-  Improved performance: reduced memory usage and decrease startup time\n\nAll Minor Features & Behavior Changes\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n-  `#server-1050 <https://projects.tigase.net/issue/server-1050>`__: Database installation without root credentials\n\n-  `#server-1062 <https://projects.tigase.net/issue/server-1062>`__: Deprecate Element based Event-bus\n\n-  `#server-1097 <https://projects.tigase.net/issue/server-1097>`__: It’s not possible to configure additional PacketFilters\n\n-  `#server-1101 <https://projects.tigase.net/issue/server-1101>`__: Enabling TLS1.3 causes s2s connections to fail\n\n-  `#server-1102 <https://projects.tigase.net/issue/server-1102>`__: Add possibility to extend MAM to MAM:2\n\n-  `#server-1105 <https://projects.tigase.net/issue/server-1105>`__: Enhance Add SSL Certificate ad-hoc with option to set default\n\n-  `#server-1119 <https://projects.tigase.net/issue/server-1119>`__: Use database for certificate storage instead of filesystem\n\n-  `#server-1120 <https://projects.tigase.net/issue/server-1120>`__: JabberIqRegister should allow enforcing both CAPTCHA and e-mail\n\n-  `#server-1132 <https://projects.tigase.net/issue/server-1132>`__: Don’t use s2s socket if only one-direction works\n\n-  `#server-1142 <https://projects.tigase.net/issue/server-1142>`__: After registration inform the client that the account activation (email) is required\n\n-  `#server-1158 <https://projects.tigase.net/issue/server-1158>`__: Establishing JMX connection to the server causes excessive memory allocation\n\n-  `#server-1162 <https://projects.tigase.net/issue/server-1162>`__: Allow interfaces in @ConfigField\n\n-  `#server-1170 <https://projects.tigase.net/issue/server-1170>`__: TLS infinity loop impacts Tigase XMPP Server performance\n\n-  `#server-1175 <https://projects.tigase.net/issue/server-1175>`__: Connection with diebesban.de stopped with invalid-namespace error\n\n-  `#server-1177 <https://projects.tigase.net/issue/server-1177>`__: Ability to change log level during runtime\n\n-  `#server-1178 <https://projects.tigase.net/issue/server-1178>`__: Remove ``online_status`` from the repository\n\n-  `#server-1179 <https://projects.tigase.net/issue/server-1179>`__: Add support for {clusterNode} in XEP-0215 host field\n\n-  `#server-1181 <https://projects.tigase.net/issue/server-1181>`__: NoSuchElementException in MaxDailyCounterQueue\n\n-  `#server-1182 <https://projects.tigase.net/issue/server-1182>`__: NPE while processing <iq type=\"result\"/> without existing session\n\n-  `#server-1187 <https://projects.tigase.net/issue/server-1187>`__: SchemaLoader should not print passwords in the logs (URL logs)\n\n-  `#server-1192 <https://projects.tigase.net/issue/server-1192>`__: Obfuscate repository passwords\n\n-  `#server-1190 <https://projects.tigase.net/issue/server-1190>`__: Executing EditUser on non-existen’t user causes creation of the user\n\n-  `#server-1193 <https://projects.tigase.net/issue/server-1193>`__: Push notifications are sent for groupchat messages without <body/>\n\n-  `#server-1197 <https://projects.tigase.net/issue/server-1197>`__: Infinite loop while cutting body of encrypted push notification to fit the push notifications limit\n\n-  `#server-1199 <https://projects.tigase.net/issue/server-1199>`__: Don’t send any packets until s2s stream negotiation is finished\n\n-  `#server-1200 <https://projects.tigase.net/issue/server-1200>`__: Use proper size of network buffers for high-throughput connections\n\n-  `#server-1203 <https://projects.tigase.net/issue/server-1203>`__: Handing error packets in CIDConnections.sendPacketsBack\n\n-  `#server-1217 <https://projects.tigase.net/issue/server-1217>`__: Prevent performing schema upgrade concurrently\n\n-  `#server-1219 <https://projects.tigase.net/issue/server-1219>`__: Use all JDBC URI parameters from config.tdsl when performing database upgrade.\n\n-  `#server-1222 <https://projects.tigase.net/issue/server-1222>`__: Add support for XEP-0377: Spam Reporting\n\n-  `#server-1229 <https://projects.tigase.net/issue/server-1229>`__: Enabling CAPTCHA or e-mail for JabberIqRegister breaks password changing functionality.\n\n-  `#server-1229 <https://projects.tigase.net/issue/server-1229>`__: Enabling CAPTCHA or e-mail for JabberIqRegister breaks password changing functionality.\n\n-  `#server-1233 <https://projects.tigase.net/issue/server-1233>`__: Add option to CertificateRepository to load certificates from the filesystem\n\n-  `#server-1234 <https://projects.tigase.net/issue/server-1234>`__: Roster API improvements\n\n-  `#server-1237 <https://projects.tigase.net/issue/server-1237>`__: Rework CertificateRepository so items are stored individually\n\n-  `#server-1238 <https://projects.tigase.net/issue/server-1238>`__: Can’t set MOTD via ad-hoc.\n\n-  `#server-1243 <https://projects.tigase.net/issue/server-1243>`__: Include wait-for-it.sh script in base distribution\n\n-  `#server-1245 <https://projects.tigase.net/issue/server-1245>`__: MethodStatistics doesn’t work well for interfaces with overloaded methods\n\n-  `#server-1251 <https://projects.tigase.net/issue/server-1251>`__: Can’t initialise MAM processor with default installation\n\n-  `#server-1252 <https://projects.tigase.net/issue/server-1252>`__: Remove select row_count() from Tig_OfflineMessages_DeleteMessage\n\n-  `#server-1253 <https://projects.tigase.net/issue/server-1253>`__: It seems that 'expired-processor' doesn’t remove periodically expired messages\n\n-  `#server-1254 <https://projects.tigase.net/issue/server-1254>`__: Fix slow startup and shutdown\n\n-  `#server-1258 <https://projects.tigase.net/issue/server-1258>`__: Allow beans to be instantiated without the requirement to reference/inject them\n\n-  `#server-1260 <https://projects.tigase.net/issue/server-1260>`__: UserConnectedEvent should be a cluster event\n\n-  `#server-1261 <https://projects.tigase.net/issue/server-1261>`__: Revise and improve EventBus developer guide\n\n-  `#server-1269 <https://projects.tigase.net/issue/server-1269>`__: SSL issues are hidden by default making it difficult to identify\n\n-  `#server-1273 <https://projects.tigase.net/issue/server-1273>`__: Add option to limit number of concurrently connected resources\n\n-  `#server-1277 <https://projects.tigase.net/issue/server-1277>`__: Fix HUGE out queue in StreamManagementIOProcessor\n\n-  `#server-1278 <https://projects.tigase.net/issue/server-1278>`__: NPE in StreamManagementIOProcessor.serviceStopped\n\n-  `#server-1282 <https://projects.tigase.net/issue/server-1282>`__: XMPPProcessorAbstract.processToUserPacket() responds to IQ result with error\n\n-  `#server-1284 <https://projects.tigase.net/issue/server-1284>`__: Add validation to JabberIqAuth\n\n-  `#server-1285 <https://projects.tigase.net/issue/server-1285>`__: Wrong field type for XEP-0157 entries\n\n-  `#server-1290 <https://projects.tigase.net/issue/server-1290>`__: Improve StringPrep to actually forbid space in localpart/domain as per rfc7622\n\n-  `#server-1292 <https://projects.tigase.net/issue/server-1292>`__: TLS connectivity issue with search.jabber.network\n\n-  `#server-1297 <https://projects.tigase.net/issue/server-1297>`__: Add option to push plugin that would allow to overwrite unencrypted part in (OMEMO) encrypted messages\n\n-  `#server-1303 <https://projects.tigase.net/issue/server-1303>`__: Better handling of \"The target is unavailable at this time.\" / PacketInvalidTypeException\n\n-  `#server-1305 <https://projects.tigase.net/issue/server-1305>`__: Allow creation of admin user (if not exist) during ``upgrade-schema`` task\n\n-  `#server-1306 <https://projects.tigase.net/issue/server-1306>`__: Fix farge amount of direct memory being used.\n\n-  `#server-1307 <https://projects.tigase.net/issue/server-1307>`__: Fix disconnection on MAM sync\n\n-  `#extras-3 <https://projects.tigase.net/issue/extras-3>`__: Add AWS logback and documentation how to use it\n\n-  `#extras-4 <https://projects.tigase.net/issue/extras-4>`__: Unescape and normalise logs in mail notifications before sending them\n\n-  `#extras-7 <https://projects.tigase.net/issue/extras-7>`__: Add email validation during in-band-registration; better handling of mail sending exceptions regarding to non-existent addresses\n\n-  `#extras-9 <https://projects.tigase.net/issue/extras-9>`__: Deprecate mDNS implementation\n\n-  `#serverdist-8 <https://projects.tigase.net/issue/serverdist-8>`__: Remove DNS resolution part from XEP-0156 implementation\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc",
    "content": "Tigase XMPP Server 8.3.0 Change notes\n-------------------------------------\n\nMajor Changes\n^^^^^^^^^^^^^\n\n* This version requires JDK17 to run\n* Added support for mam2#extended [#mam-73]\n* Rework certificate generation to utilise `keygen` tool instead of using `sun.*` API unavailable under JDK17\n* Added support for XEP-0440 SASL Channel Binding Type Capability and fixed and reenabled `SCRAM-*-PLUS SASL` mechanisms [#server-1335]\n* Added initial,preview support for SASL2 and Bind2 (disabled by default) - to enable, activate beans `'urn:xmpp:bind:0'` and `'urn:xmpp:sasl:2'` in `'sess-man'` [#server-1332]\n\n\nAll Minor Features & Behavior Changes\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n- Increased network socket buffer from 2K to 4K to improve performance when reading data from socket. It can increase somewhat memory usage proportionally to number of concurrent user connections. It's possible to configure size of this buffer using `socket-buffer-size` property - please see documentation.\n- Add configuration to log size generated by LoggerTask in Monitor and decrease default from 1M to 50K; Disable serialisation of monitor events; #servers-372\n- Add DOAP file; update documentation with supported features; #server-1076\n- Fix issue with NPE in JabberIqAuth plugin when no password was presented due to missing return statement; fixed similar issue where, after closing the connection, the execution of the code wasn't terminated in JabberIqAuth and SaslAuth plugins #server-1317\n- Add support for XEP-0398 to feature list and updated list of supported features; #server-1316\n- Change try-catch statement in database schema loader to better catch edge cases; #serverdist-10\n- Remove wildcard certificate generation (as main DN) in certificate container to avoid issues that it entails (inability to override such self-signed certificate via ad-hoc commands!). Wildcards are now properly handled by CertificateGenerateor and are included correctly as SAN in addition to DN for main domain; Fix handling \"default\" certificates from repository; #server-1279\n- Change default watchdog ping from (forbidden by RFC) whitespace to xmpp; add warning if someone configures it as whitespace either way; server-1318\n- Improve XMPPDomBuilderHandler logging; #server-1323\n- Improved Stream Management code responsible for generating `<r/>` requests #server-1324 (#150)\n- Added `socket-buffer-size` option to `ConnectionManager` to configure `SO_RCVBUF` separately from internal network buffers #server-1325\n- Increased socket-buffer-size for client-to-server and intercluster connections and added documentation #server-1325\n- Fix MAX_PAUSE property name; #server-1326\n- Updated implementation of XEP-0377: Spam Reporting #server-1327\n- Fixed issue with errors being sent for unexpected <iq type=result/> stanzas #server-1328\n- Improved exceptions handling in StanzaProcessor #server-1328\n- Switch from jtds to MS own jdbc driver; #serverdist-12\n- Prevent re-delivery of certain S2S packets (sasl, features, dialback, etc) as it doesn't make sense; #server-1320\n- Adjust log levels to avoid WARNINGS during startup for regular messages; #server-1115\n- Add 'active in last x' statistic; #server-1281\n- Include option to restart JVM on OOM (off by default)\n- Correctly process packets from mobile queue instead of re-adding currently filtered packet; #server-1331\n- Improvements to NativeMemoryTracking implementation with units; documentation; #server-1330\n- Improve MAM logging; #servers-384\n- Only count stanzas in StreamManagement #server-1333\n- Fixed advertisement stream features for unauthorized stream #server-1334\n- Fixed NPE during preparing stream features when connection is already closed #server-1334\n- Added initial support for SASL2 and Bind2 (preview feature, disabled by default) #server-1332\n- Added support for XEP-0440 SASL Channel Binding Type Capability and fixed and reenabled `SCRAM-*-PLUS SASL` mechanisms [#server-1335]\n- Fixed NPE during enabling of stream resumption #server-1332\n- Fixed sending block/unblock presences from blocking command for domain #server-1336\n- Better default for lastXmppPacketReceivedTime member to avoid WatchDog closing connection before lastXmppPacketReceivedTime is set; #server-1337\n- Add proper addressing validation in S2S connection and allow connections without 'from' set; #server-1338"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Release_Notes/tigase-server-current.inc",
    "content": "Tigase XMPP Server 8.4.0 Change notes\n-------------------------------------\n\n.. Note::\n\n   This version requires JDK17!\n\n.. Note::\n\n   Complete list of changes available in [`tigase-server-8.4.0` milestone](https://tigase.dev/tigase/_server/~milestones/11)\n\nMajor Changes\n^^^^^^^^^^^^^\n\n* Add support for XEP-0227: Portable Import/Export Format for XMPP-IM Servers (tigase/_server/server-core#1456)\n* Add support for showing accounts with push notifications enabled as `away` (tigase/_server/server-core#1527)\n* Add support for banning users with support of xmppbl.org (tigase/_server/tigase-muc#156)\n* Add support for XEP-0404: Mediated Information eXchange (MIX): JID Hidden Channels\n* Add ad-hoc commands to manage MIX channel\n* Initial rewrite of HTTP-API from Groovy to Java and switch to jte for templating for improve performance\n* Add dashboard to easily manage users (tigase/_server/tigase-http-api#132)\n* Added support for opt-in for ProtoXEP: PubSub Server Information\n* Add support for authentication of APNS provider using P8 (tigase-push)\n* Allow overriding default \"networkaddress.cache.ttl\" configuration (tigase/_server/tigase-utils#26)\n* Deprecate MD5*CredentialsEntry (tigase/_server/server-core#1359)\n* Add support for message retraction by admins/owners (tigase/_server/tigase-mix#34)\n* Add support for XEP-0425: Message Moderation (tigase/_server/tigase-muc#152)\n\n\nAll Minor Features & Behavior Changes\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n- Fix issue where exception thrown within StoredProcedure that has transaction can cause a lock (tigase/_server/server-core#1347)\n- Improve caching (tigase/_server/tigase-pubsub#92)\n- Improve handling of database failovers (connecting to read-only instance due to DNS propagation being to slow on AWS part) (tigase/_server/server-core#1354)\n- Update MongoDB driver (tigase/_server/tigase-mongodb#34)\n- Implement support for XEP-0404: Mediated Information eXchange (MIX): JID Hidden Channels (tigase/_server/tigase-mix#37)\n- Remove JaXMPP dependency in tigase-push-fcm (tigase/_server/tigase-push#50)\n- Improved and refactor HTTP API implementation (tigase/_server/tigase-http-api#122)\n- Expose account status value in endpoint to retrieve user details (tigase/_server/tigase-http-api#131)\n- Setup not working (NPE because of missing JTE compiler) (tigase/_server/tigase-http-api#135)\n- Add method to generate tokens/QR codes for authentication (tigase/_server/tigase-http-api#133)\n- http-api should delay listening on port until server finished startup (tigase/_server/tigase-http-api#130)\n- Add ad-hoc commands to manage MIX channel (tigase/_server/tigase-mix#36)\n- Add support for exporting/importing MIX channel data (tigase/_server/tigase-mix#38)\n- Add support for XEP-0227: Portable Import/Export Format (tigase/_server/tigase-message-archiving#81)\n- XEP-0227: Portable Import/Export Format for XMPP-IM Servers (tigase/_server/tigase-pubsub#138)\n- Fix disco#items of PubSub node ignores permission checking (tigase/_server/tigase-pubsub#137)\n- NumberFormatException: For input string: \"max\" (tigase/_server/tigase-pubsub#134)\n- RSM sends <max/> element in response (tigase/_server/tigase-utils#27)\n- Unexpected `<build/>` element in XEP-0092: Software Version (tigase/_server/server-core#1522)\n- Add support for exporting MUC rooms and history to XML (tigase/_server/tigase-muc#161)\n- Registration captcha makes registration impossible (tigase/_server/server-core#1510)\n- Return better SASL error for accounts pending confirmation (tigase/_server/server-core#1511)\n- Missing escaping of some chars in encrypted Push Notifications (tigase/_server/server-core#1512)\n- SCRAM *-PLUS mechanisms unavailable after StartTLS (tigase/_server/server-core#1508)\n- Rename 'captcha' field to 'qa' (tigase/_server/server-core#1516)\n- Added support for opt-in for ProtoXEP: PubSub Server Information (tigase/_server/server-core#1515)\n- Adjust TLS ciphers and options values used by default (tigase/_server/server-core#1517)\n- Add support for token/HMAC based authentication (tigase/_server/server-core#1520)\n- Not XML-well-formed stanza being accepted by server and routed to recipient (tigase/_server/tigase-xmltools#16)\n- Dockerize with Jib (tigase/_server/tigase-xmpp-server-docker#12)\n- Fix issue where it's not possible to configure seeOtherHost.defaultHost (tigase/_server/server-core#1364)\n- Add support for testing S2S connectivity over IPv6 (tigase/_server/server-core#1363)\n- Add support for setting user as admin using REST API (tigase/_server/tigase-http-api#127)\n- Create pre-processor that would filter out error messages (tigase/_server/server-core#1344)\n- Make it easier to disable BruteForceLocker (tigase/_server/server-core#1345)\n- Cache usage statistics are not collected/computed correctly (tigase/_server/tigase-pubsub#135)\n- Improve handling for MAM:1 queries with old IDs (tigase/_server/tigase-muc#153)\n- Undelivered stanzas are processed before stream closure commands (tigase/_server/server-core#1343)\n- [Setup] Add support for env-variables exposed in docker start.sh script (tigase/_server/tigase-http-api#142)\n- Add API-KEY repo implementation that would be based on environment variables (tigase/_server/tigase-http-api#97)\n- Add support for certificates private key using `ecdsa` (tigase/_server/tigase-utils#28)\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Security/Access_Control_List.inc",
    "content": "Access Control Lists in Tigase\r\n-------------------------------------\r\n\r\nTigase offers support for **Access Control List (ACL)** to allow for fine grained access to administration commands on the server.\r\n\r\nBy default, all administration commands are only accessible (visible through service discovery and can be executed) by the service administrators. Service administrators are existing accounts with JIDs (**BareJIDs**) listed in the ``config.tdsl`` file under ``admins = []`` (please see :ref:`admins<admins>` for details).\r\n\r\nAdditionally, other XMPP users and entities can be assigned permissions to execute a command or commands using Tigase’s ACL capabilities.\r\n\r\nThe following is a list of possible ACL modifiers for administrator command accessibility:\r\n\r\n-  ``ALL`` - Everybody can execute the command, even users from different federated servers.\r\n\r\n-  ``ADMIN`` - Local server administrators can execute the command, this is a default setting if no ACL is set for a command.\r\n\r\n-  ``LOCAL`` - All users with accounts on the local server can execute the command. Users from other, federated servers will not be able to execute the command.\r\n\r\n-  ``NONE`` - No one will be allowed to execute this command\r\n\r\n-  ``DOMAIN_OWNER`` - Only user which is owner of the domain which items are being manipulated is allowed to execute the comment. If script is not checking permissions for the manipulated item, this value will behave in the same way as ``LOCAL``.\r\n\r\n-  ``DOMAIN_ADMIN`` - Only user which is one of the domain administrators will be able to execute the command manipulating items related to the domain. If script is not checking permissions for the manipulated item, this value will behave in the same way as ``LOCAL``.\r\n\r\n-  ``example.com`` - Only users with accounts on the selected domain will be able to execute the command. It may be useful to setup a domain specifically for admin accounts, and automatically all users within that domain would be able to run the command.\r\n\r\n-  ``user@example.com`` - Comma separated list of JIDs of users who can execute the command.\r\n\r\nIn any case, regardless of ACL settings, any command can be executed and accessed by the designated service wide administrators, that is accounts listed as admins in the ``config.tdsl`` file.\r\n\r\nMultiple ACL modifiers can be combined and applied for any command. This may not always makes sense. For example ALL supersedes all other settings, so it does not make sense to combine it with any other modifier. However, most others can be combined with JID to broaden access to specific accounts.\r\n\r\nOn Tigase server the Access Control List is checked for the first matching modifier. Therefore if you combine ALL with any other modifier, anybody from a local or remote service will always be able to execute the command, no matter what other modifiers are added.\r\n\r\nPlease note, the ACL lists work on the command framework level. Access is verified before the command is actually executed. There might be additional access restrictions within a command itself. In many cases, even if all local users are permitted to execute a command (LOCAL modifier), some commands allow only to be executed by a domain owner or a domain administrator (and of course by the service-wide administrators as well). All the commands related to a user management such as adding a new user, removing a user, password changes, etc… belong to this category. When conducting domain (vhost) management, creation/registration of a new domain can be done by any local user (if LOCAL ACL modifier is set) but then all subsequent domain management tasks such as removing the vhost, updating its configuration, setting SSL certificate can be done by the domain owner or administrator only.\r\n\r\nThe ACL list is set for a specific Tigase component and a specific command. Therefore the configuration property must specify all the details. So the general format for configuring ACL for a command is this:\r\n\r\n.. code::\r\n\r\n   comp-id () {\r\n       commands {\r\n           'command-id' = [ 'ACL_modifier', 'ACL_modifier', 'ACL_modifier' ]\r\n       }\r\n   }\r\n\r\nThe breakdown is as such:\r\n\r\n-  ``comp-id`` is the Tigase server component ID such as: sess-man, vhost-man, c2s, etc..\r\n\r\n-  ``commands`` is a static text which indicates that the property is for component’s command settings.\r\n\r\n-  ``command-id`` is a command ID for which we set the ACL such as query-dns, http://jabber.org/protocol/admin#add-user, user-roster-management, etc…\r\n\r\nHere are a few examples:\r\n\r\n*Allowing local users to create and manage their own domains*\r\n\r\n.. code::\r\n\r\n   'vhost-man' () {\r\n       commands {\r\n           'comp-repo-item-add' = 'LOCAL'\r\n           'comp-repo-item-remove' = 'LOCAL'\r\n           'comp-repo-item-update' = 'LOCAL'\r\n           'ssl-certificate-add' = 'LOCAL'\r\n       }\r\n   }\r\n\r\nIn fact all the commands except item-add can be executed by the domain owner or administrator.\r\n\r\n*Allowing local users to execute user management commands:*\r\n\r\n.. code::\r\n\r\n   'sess-man' () {\r\n       'commands' {\r\n           'http://jabber.org/protocol/admin#add-user' = 'LOCAL'\r\n           'http://jabber.org/protocol/admin#change-user-password' = 'LOCAL'\r\n           'http://jabber.org/protocol/admin#delete-user' = 'LOCAL'\r\n           'http://jabber.org/protocol/admin#get-online-users-list' = 'LOCAL'\r\n           'http://jabber.org/protocol/admin#get-registered-user-list' = 'LOCAL'\r\n           'http://jabber.org/protocol/admin#user-stats' = 'LOCAL'\r\n           'http://jabber.org/protocol/admin#get-online-users-list' = 'LOCAL'\r\n       }\r\n   }\r\n\r\nAs in the previous example, the commands will by executed only by local users who are the specific domain administrators.\r\n\r\n*Allowing users from a specific domain to execute query-dns command and some other users for given JIDs from other domains:*\r\n\r\n.. code::\r\n\r\n   'vhost-man' () {\r\n       'commands' {\r\n           'query-dns' = [ 'tigase.com', 'admin@tigase.org', 'frank@example.com' ]\r\n       }\r\n   }\r\n\r\nTo be able to set a correct ACL property you need to know component names and command IDs. Component IDs can be found in the service discovery information on running server or in the server logs during startup. A command ID can be found in the command script source code. Each script contains a list of metadata at the very beginning of it’s code. One of them is ``AS:CommandId`` which is what you have to use for the ACL setting.\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Security/Account_Registration_Limits.inc",
    "content": "Account Registration Limits\r\n--------------------------------\r\n\r\nIn order to protect Tigase servers from DOS attacks, a limit on number of account registrations per second has been implemented. This may be configured by adding the following line in the ``config.tdsl`` file:\r\n\r\n.. code::\r\n\r\n   'registration-throttling' () {\r\n       limit = 10\r\n   }\r\n\r\nThis setting allows for 10 registrations from a single IP per second. If the limit is exceeded, a ``NOT_ALLOWED`` error will be returned."
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Security/Application_Passwords.inc",
    "content": "\r\n.. _application-passwords-admin-guide:\r\n\r\nApplication passwords\r\n-------------------------\r\n\r\nIn recent versions of Tigase XMPP Server it is possible to create and use multiple username and password pairs to authorize connection to the single XMPP account.\r\n\r\nWith that in place it is now possible to have multiple password for a multiple clients accessing the same account what can be used to increase security of the account as even if one password compromised you can still log in and block lost or compromised device.\r\n\r\nAdding application password\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTo add new username-password pair you need to execute ``Add user credentials`` ad-hoc command (command node ``auth-credentials-add`` at ``sess-man``) while logged in the XMPP account for which you want to add a new application password.\r\n\r\nDuring execution for a command you will be provided with a form to fill in with following fields:\r\n\r\n-  The Jabber ID for the account (``jid``) - bare JID of your account\r\n\r\n-  Credential ID (``credentialId``) - username for the new application password\r\n\r\n-  Password (``password``) - a new password\r\n\r\n.. code::\r\n\r\n    <iq type='set' to='sess-man@example.com' id='sasl-app-add-1'>\r\n        <command xmlns='http://jabber.org/protocol/commands' node='auth-credentials-add' action='execute'/>\r\n    </iq>\r\n\r\n    <iq type='result' from='sess-man@example.com' id='sasl-app-add-1' to='user@example.com/resource-1'>\r\n        <command xmlns='http://jabber.org/protocol/commands' node='auth-credentials-add' session-id='uuid-xxxxxx' status='executing'>\r\n            <x xmlns='jabber:x:data' type='form'>\r\n                <title>Add user credentials\"</title>\r\n                <field var='jid' label='The Jabber ID for the account' type='jid-single'/>\r\n                <field var='credentialId' label='Credential ID' type='jid-single'/>\r\n                <field var='password' label='Password' type='text-single'/>\r\n            </x>\r\n        </command>\r\n    </iq>\r\n\r\n\r\nAfter submitting this form a new credential will be added.\r\n\r\n.. code::\r\n\r\n    <iq type='set' to='sess-man@example.com' id='sasl-app-add-2'>\r\n        <command xmlns='http://jabber.org/protocol/commands' node='auth-credentials-add' action='execute'>\r\n            <x xmlns='jabber:x:data' type='submit'>\r\n                <title>Add user credentials\"</title>\r\n                <field var='jid' label='The Jabber ID for the account' type='jid-single'>\r\n                    <value>user@example.com</value>\r\n                </field>\r\n                <field var='credentialId' label='Credential ID' type='jid-single'>\r\n                    <value>my-new-app-1</value>\r\n                </field>\r\n                <field var='password' label='Password' type='text-single'>\r\n                    <value>39jfnwu053743</value>\r\n                </field>\r\n            </x>\r\n        </command>\r\n    </iq>\r\n\r\n    <iq type='result' from='sess-man@example.com' id='sasl-app-add-2' to='user@example.com/resource-1'>\r\n        <command xmlns='http://jabber.org/protocol/commands' node='auth-credentials-add' session-id='uuid-xxxxxx' status='completed'>\r\n            <x xmlns='jabber:x:data' type='result'>\r\n                <field type='fixed'>\r\n                    <value>OK</value>\r\n                </field>\r\n            </x>\r\n        </command>\r\n    </iq>\r\n\r\n\r\nLogin in with application password\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTo log in with new password the XMPP client can use any SASL mechanism but it needs to provide (in SASL message):\r\n\r\n-  ``authzid`` - account JID\r\n\r\n-  ``authcid`` - username for application password\r\n\r\n-  ``passwd`` - application password\r\n\r\nWith proper values, you application will be able to log in using application password.\r\n\r\nIn case of SASL PLAIN which has the following format (spaces should be ommited and ``[]`` means it is optional):\r\n``[authzid] UTF8NUL authcid UTF8NUL passwd``\r\nnot encoded payload would look like this:\r\n``user@example.com UTF8NUL my-new-app-1 UTF8NUL 39jfnwu053743``\r\n\r\nThat after Base64 encoding would be presented as `dXNlckBleGFtcGxlLmNvbQBteS1uZXctYXBwLTEDOWpmbnd1MDUzNzQz` and this value can be used as a correct CData of `<auth/>` element:\r\n\r\n.. code::\r\n\r\n    <auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>dXNlckBleGFtcGxlLmNvbQBteS1uZXctYXBwLTEDOWpmbnd1MDUzNzQz</auth>\r\n\r\n\r\n\r\nRemoving application password\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIf your device is compromised or lost and you want to remove application password, you need to use a different device and log in on your XMPP account. Then you need to execute ``Delete user credentials`` ad-hoc command (command node ``auth-credentials-delete`` at ``sess-man``).\r\n\r\nDuring execution for a command you will be provided with a form to fill in with following fields:\r\n\r\n-  The Jabber ID for the account (``jid``) - bare JID of your account\r\n\r\n-  Credential ID (``credentialId``) - username for the application password which you want to remove\r\n\r\n.. code:\r\n\r\n    <iq type='set' to='sess-man@example.com' id='sasl-app-delete-1'>\r\n        <command xmlns='http://jabber.org/protocol/commands' node='auth-credentials-delete' action='execute'/>\r\n    </iq>\r\n\r\n    <iq type='result' from='sess-man@example.com' id='sasl-app-delete-1' to='user@example.com/resource-1'>\r\n        <command xmlns='http://jabber.org/protocol/commands' node='auth-credentials-delete' session-id='uuid-xxxxxx' status='executing'>\r\n            <x xmlns='jabber:x:data' type='form'>\r\n                <title>Add user credentials\"</title>\r\n                <field var='jid' label='The Jabber ID for the account' type='jid-single'/>\r\n                <field var='credentialId' label='Credential ID' type='jid-single'/>\r\n            </x>\r\n        </command>\r\n    </iq>\r\n\r\nAfter submitting this form a credential will be removed.\r\n\r\n.. code::\r\n\r\n    <iq type='set' to='sess-man@example.com' id='sasl-app-delete-2'>\r\n        <command xmlns='http://jabber.org/protocol/commands' node='auth-credentials-delete' action='execute'>\r\n            <x xmlns='jabber:x:data' type='submit'>\r\n                <title>Add user credentials\"</title>\r\n                <field var='jid' label='The Jabber ID for the account' type='jid-single'>\r\n                    <value>user@example.com</value>\r\n                </field>\r\n                <field var='credentialId' label='Credential ID' type='jid-single'>\r\n                    <value>my-new-app-1</value>\r\n                </field>\r\n            </x>\r\n        </command>\r\n    </iq>\r\n\r\n    <iq type='result' from='sess-man@example.com' id='sasl-app-delete-2' to='user@example.com/resource-1'>\r\n        <command xmlns='http://jabber.org/protocol/commands' node='auth-credentials-delete' session-id='uuid-xxxxxx' status='completed'>\r\n            <x xmlns='jabber:x:data' type='result'>\r\n                <field type='fixed'>\r\n                    <value>OK</value>\r\n                </field>\r\n            </x>\r\n        </command>\r\n    </iq>"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc",
    "content": ".. _custonAuthConnector:\r\n\r\nTigase Custom Auth Connector\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThe Tigase Custom Auth connector with shortcut name: **tigase-custom** is implemented in the class: `tigase.db.jdbc.TigaseCustomAuth <https://github.com/tigase/tigase-server/tree/master/src/main/java/tigase/db/jdbc/TigaseCustomAuth.java>`__. It allows you to connect to any external database to perform user authentication and use a custom queries for all actions.\r\n\r\nYou can find more details how to setup a custom connector in the Custom Authentication Connectors guide.\r\n\r\nThe basic configuration is very simple:\r\n\r\n.. code:: bash\r\n\r\n   authRepository {\r\n       default () {\r\n           cls = 'tigase.db.jdbc.TigaseCustomAuth'\r\n           'data-source' = 'default-auth'\r\n       }\r\n   }\r\n\r\nThat’s it.\r\n\r\nThe connector loads correctly and starts working using predefined, default list of queries. In most cases you also might want to define your own queries in the configuration file. The shortest possible description is the following example of the content from the ``config.tdsl`` file:\r\n\r\nThis query is used to check connection to the database, whether it is still alive or not\r\n\r\n.. code::\r\n\r\n   authRepository {\r\n       default () {\r\n           'conn-valid-query' = 'select 1'\r\n       }\r\n   }\r\n\r\nThis is database initialization query, normally we do not use it, especially in clustered environment\r\n\r\n.. code::\r\n\r\n   authRepository {\r\n       default () {\r\n           'init-db-query' = 'update tig_users set online_status = 0'\r\n       }\r\n   }\r\n\r\n.. Note::\r\n\r\n   ``online_status`` column does not exist and would need to be added for that query to work.\r\n\r\nBelow query performs user authentication on the database level. The Tigase server does not need to know authentication algorithm or password encoding type, it simply passes user id (BareJID) and password in form which was received from the client, to the stored procedure. If the authentication was successful the procedure returns user bare JID or null otherwise. Tigase checks whether the JID returned from the query matches JID passed as a parameter. If they match, the authentication is successful.\r\n\r\n.. code::\r\n\r\n   authRepository {\r\n       default () {\r\n           'user-login-query' = '{ call TigUserLoginPlainPw(?, ?) }'\r\n       }\r\n   }\r\n\r\n\r\n.. Note::\r\n\r\n   ``TigUserLoginPlainPw`` is no longer part of a Tigase XMPP Server database schema and would need to be created.\r\n\r\nBelow query returns number of user accounts in the database, this is mainly used for the server metrics and monitoring components.\r\n\r\n.. code::\r\n\r\n   authRepository {\r\n       default () {\r\n         'users-count-query' = '{ call TigAllUsersCount() }'\r\n       }\r\n   }\r\n\r\nThe Below query is used to add a new user account to the database.\r\n\r\n.. code::\r\n\r\n   authRepository {\r\n       default () {\r\n           'add-user-query' = '{call TigAddUserPlainPw(?, ?) }'\r\n       }\r\n   }\r\n\r\nBelow query is used to remove existing account with all user’s data from the database.\r\n\r\n.. code::\r\n\r\n   authRepository {\r\n       default () {\r\n           'del-user-query' = '{ call TigRemoveUser(?) }'\r\n       }\r\n   }\r\n\r\nThis query is used for the user authentication if ``user-login-query`` is not defined, that is if there is no database level user authentication algorithm available. In such a case the Tigase server loads user’s password from the database and compares it with data received from the client.\r\n\r\n.. code::\r\n\r\n   authRepository {\r\n       default () {\r\n           'get-password-query' = 'select user_pw from tig_users where user_id = ?'\r\n       }\r\n   }\r\n\r\nBelow query is used for user password update in case user decides to change his password.\r\n\r\n.. code::\r\n\r\n   authRepository {\r\n       default () {\r\n           'update-password-query' = 'update tig_users set user_pw = ? where user_id = ?'\r\n       }\r\n   }\r\n\r\nThis query is called on user logout event. Usually we use a stored procedure which records user logout time and marks user as offline in the database.\r\n\r\n.. code::\r\n\r\n   authRepository {\r\n       default () {\r\n           'update-logout-query' = 'update tig_users, set online_status = online_status - 1 where user_id = ?'\r\n       }\r\n   }\r\n\r\n\r\n.. Note::\r\n\r\n   ``online_status`` column does not exist and would need to be added for that query to work.\r\n\r\nThis configuration specifies what non-sasl authentication mechanisms to expose to the client\r\n\r\n.. code::\r\n\r\n   authRepository {\r\n       default () {\r\n           'non-sasl-mechs' = [ 'password', 'digest' ]\r\n       }\r\n   }\r\n\r\nThis setting to specify what sasl authentication mechanisms expose to the client\r\n\r\n.. code::\r\n\r\n   authRepository {\r\n       default () {\r\n           'sasl-mechs' = 'PLAIN,DIGEST-MD5'\r\n       }\r\n   }\r\n\r\nQueries are defined in the configuration file and they can be either plain SQL queries or stored procedures. If the query starts with characters: ``{ call`` then the server assumes this is a stored procedure call, otherwise it is executed as a plain SQL query. Each configuration value is stripped from white characters on both ends before processing.\r\n\r\nPlease don’t use semicolon ``;`` at the end of the query as many JDBC drivers get confused and the query may not work.\r\n\r\nSome queries can take arguments. Arguments are marked by question marks ``?`` in the query. Refer to the configuration parameters description for more details about what parameters are expected in each query.\r\n\r\nThis example shows how to use a stored procedure to add a user as a query with 2 required parameters (username, and password).\r\n\r\n.. code::\r\n\r\n   authRepository {\r\n       default () {\r\n           'add-user-query' = '{call TigAddUserPlainPw(?, ?) }'\r\n       }\r\n   }\r\n\r\nThe same query with plain SQL parameters instead:\r\n\r\n.. code::\r\n\r\n   'add-user-query' = 'insert into users (user_id, password) values (?, ?)'\r\n\r\nThe order of the query arguments is important and must be exactly as described in specification for each parameter.\r\n\r\n+---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------+-------------------------------------------------------------------------------+\r\n| Query Name                | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | Arguments                                        | Example Query                                                                 |\r\n+===========================+===========================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================+==================================================+===============================================================================+\r\n| ``conn-valid-query``      | Query executed periodically to ensure active connection with the database.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | Takes no arguments.                              | ``select 1``                                                                  |\r\n+---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------+-------------------------------------------------------------------------------+\r\n| ``init-db-query``         | Database initialization query which is run after the server is started.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | Takes no arguments.                              | ``update tig_users set online_status = 0``                                    |\r\n+---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------+-------------------------------------------------------------------------------+\r\n| ``add-user-query``        | Query adding a new user to the database.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | Takes 2 arguments: ``(user_id (JID), password)`` | ``insert into tig_users (user_id, user_pw) values (?, ?)``                    |\r\n+---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------+-------------------------------------------------------------------------------+\r\n| ``del-user-query``        | Removes a user from the database.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | Takes 1 argument: ``(user_id (JID))``            | ``delete from tig_users where user_id = ?``                                   |\r\n+---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------+-------------------------------------------------------------------------------+\r\n| ``get-password-query``    | Retrieves user password from the database for given user_id (JID).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | Takes 1 argument: ``(user_id (JID))``            | ``select user_pw from tig_users where user_id = ?``                           |\r\n+---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------+-------------------------------------------------------------------------------+\r\n| ``update-password-query`` | Updates (changes) password for a given user_id (JID).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Takes 2 arguments: ``(password, user_id (JID))`` | ``update tig_users set user_pw = ? where user_id = ?``                        |\r\n+---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------+-------------------------------------------------------------------------------+\r\n| ``user-login-query``      | Performs user login. Normally used when there is a special SP used for this purpose. This is an alternative way to a method requiring retrieving user password. Therefore at least one of those queries must be defined: ``user-login-query`` or ``get-password-query``. If both queries are defined then ``user-login-query`` is used. Normally this method should be only used with plain text password authentication or sasl-plain. Tigase expects a result set with user_id to be returned from the query if login is successful and empty results set if the login is unsuccessful. | Takes 2 arguments: ``(user_id (JID), password)`` | ``select user_id from tig_users where (user_id = ?) AND (user_pw = ?)``       |\r\n+---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------+-------------------------------------------------------------------------------+\r\n| ``user-logout-query``     | This query is called when user logs out or disconnects. It can record that event in the database.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | Takes 1 argument: ``(user_id (JID))``            | ``update tig_users, set online_status = online_status - 1 where user_id = ?`` |\r\n+---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------+-------------------------------------------------------------------------------+\r\n| ``non-sasl-mechs``        | Comma separated list of NON-SASL authentication mechanisms. Possible mechanisms are: ``password`` and ``digest``. The digest mechanism can work only with ``get-password-query`` active and only when password are stored in plain text format in the database.                                                                                                                                                                                                                                                                                                                           |                                                  |                                                                               |\r\n+---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------+-------------------------------------------------------------------------------+\r\n| ``sasl-mechs``            | Comma separated list of SASL authentication mechanisms. Possible mechanisms are all mechanisms supported by Java implementation. The most common are: ``PLAIN``, ``DIGEST-MD5``, ``CRAM-MD5``. \"Non-PLAIN\" mechanisms will work only with the ``get-password-query`` active and only when passwords are stored in plain text format in the database.                                                                                                                                                                                                                                      |                                                  |                                                                               |\r\n+---------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------+-------------------------------------------------------------------------------+"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Security/Auth_Connectors/LDAP_Auth.inc",
    "content": "LDAP Authentication Connector\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTigase XMPP Server offers support for authenticating users against an LDAP server in **Bind** **Authentication** mode.\r\n\r\nConfiguration for the LDAP support is really simple you just have to add a few lines to your ``config.tdsl`` file.\r\n\r\n.. code::\r\n\r\n   authRepository {\r\n       default () {\r\n           cls = 'tigase.db.ldap.LdapAuthProvider'\r\n           uri = 'ldap://ldap.tigase.com:389'\r\n           'user-dn-pattern' = 'cn=USER_ID,ou=people,dc=tigase,dc=org'\r\n       }\r\n   }\r\n\r\nPlease note the ``USER_ID`` element, this is a special element of the configuration which is used to authenticate particular user. Tigase LDAP connector replaces it with appropriate data during authentication. You can control what Tigase should put into this part. In your configuration you must replace this string with one of the following:\r\n\r\n1. ``%1$s`` - use user name only for authentication (JabberID’s localpart)\r\n\r\n2. ``%2$s`` - use domain name only for authentication (JabberID’s domain part)\r\n\r\n3. ``%3$s`` - use the whole Jabber ID (JID) for authentication\r\n\r\n.. Important::\r\n\r\n   Please make sure that you included ``autoCreateUser=true`` in your main data source (UserRepository and **not** above AuthRepository) as outlined in `??? <#autoCreateUser>`__ - otherwise you may run into problems with data access."
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Security/Auth_Connectors/SASL_EXTERNAL.inc",
    "content": "Configuration of SASL EXTERNAL\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIn order to enable SASL External set \"Client Certificate CA\" (``client-trust-extension-ca-cert-path``) to the path containing Certification Authority (CA) certificate in the VHost (domain) configuration, for example ``/path/to/cacert.pem``\r\n\r\nFile ``cacert.pem`` contains Certificate Authority certificate which is used to sign clients certificate.\r\n\r\nClient certificate must include user’s Jabber ID as ``XmppAddr`` in ``subjectAltName``:\r\n\r\n   As specified in RFC 3920 and updated in RFC 6120, during the stream negotiation process an XMPP client can present a certificate (a “client certificate”). If a JabberID is included in a client certificate, it is encapsulated as an id-on-xmppAddr Object Identifier (“xmppAddr”), i.e., a subjectAltName entry of type otherName with an ASN.1 Object Identifier of “id-on-xmppAddr” as specified in Section 13.7.1.4 of RFC 6120, `XEP-0178 <http://xmpp.org/extensions/xep-0178.html#c2s>`__.\r\n\r\nIt is possible to make client certificate **required** using same VHost configuration and enabling option ``Client Certificate Required`` (``client-trust-extension-cert-required``).\r\n\r\nIf this option will be enabled, then client **must provide** certificate. This certificate will be verified against ``clientCertCA``. If client does not provide certificate or certificate will be invalid, **TLS handshake will be interrupted and client will be disconnected**.\r\n\r\nUsing this options does not force client to use SASL EXTERNAL. Client still may authenticate with other SASL mechanisms."
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc",
    "content": ".. _tigaseAuthConnector:\r\n\r\nTigase Auth Connector (DEPRECATED)\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n.. **Warning**::\r\n\r\n    Tigase Auth connector is **DEPRECATED** as of version 8.0.0 and will be removed in future releases\r\n\r\nThe Tigase Auth connector with shortcut name: **tigase-auth** is implemented in the class: `tigase.db.jdbc.TigaseAuth <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/db/jdbc/TigaseAuth.java>`__. It allows you to connect to any external database to perform user authentication. You can find more details how to setup a custom connector in the :ref:`Custom Authentication Connectors<customAuthentication>` guide.\r\n\r\nTo make this connector working you have to prepare your database to offer set of stored procedures for Tigase server to perform all the authentication actions. The best description is the example schema with all the stored procedures defined - please refer to the Tigase repositories for the schema definition files (each component has it’s dedicated schema). For example:\r\n\r\n-  `tigase-server <https://github.com/tigase/tigase-server/tree/master/src/main/database>`__\r\n\r\n-  `tigase-pubsub <https://github.com/tigase/tigase-pubsub/tree/master/src/main/database>`__\r\n\r\n-  `tigase-muc <https://github.com/tigase/tigase-muc/tree/master/src/main/database>`__\r\n\r\n-  `tigase-message-archiving <https://github.com/tigase/tigase-message-archiving/tree/master/src/main/database>`__\r\n\r\n-  `tigase-socks5 <https://github.com/tigase/tigase-socks5/tree/master/src/main/database>`__\r\n\r\nThe absolute minimum of stored procedures you have to implement is:\r\n\r\n-  ``TigUserLoginPlainPw`` - to perform user authentication. The procedure is always called when the user tries to login to the XMPP server. This is the only procedure which must be implemented and actually must work.\r\n\r\n-  ``TigUserLogout`` - to perform user logout. The procedure is always called when the user logouts or disconnects from the server. This procedure must be implemented but it can be empty and can do nothing. It just needs to exist because Tigase expect it to exist and attempts to call it.\r\n\r\nWith these 2 above stored procedures you can only perform user login/logouts on the external database. You can’t register a user account, change user password or remove the user. In many cases this is fine as all the user management is handled by the external system.\r\n\r\nIf you however want to allow for account management via XMPP you have to implement also following procedures:\r\n\r\n-  ``TigAddUserPlainPw`` - to add a new user account\r\n\r\n-  ``TigRemoveUser`` - to remove existing user account\r\n\r\n-  ``TigUpdatePasswordPlainPw`` - to change a user password for existing account\r\n\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Security/Auth_Connectors.inc",
    "content": ".. _customAuthentication:\r\n\r\nCustom Authentication Connectors\r\n-------------------------------------\r\n\r\nThis article presents configuration options available to the administrator and describe how to set Tigase server up to use user accounts data from a different database.\r\n\r\nThe first thing to know is that Tigase server always opens 2 separate connections to the database. One connection is used for user login data and the other is for all other user data like the user roster, vCard, private data storage, privacy lists and so on…​\r\n\r\nIn this article we still assume that Tigase server keeps user data in it’s own database and only login data is retrieved from the external database.\r\n\r\nAt the moment Tigase offers following authentication connectors:\r\n\r\n-  ``mysql``, ``pgsql``, ``derby`` - standard authentication connector used to load user login data from the main user database used by the Tigase server. In fact the same physical implementation is used for all JDBC databases.\r\n\r\n-  ``tigase-custom`` - is the authentication connector which can be used with any database. Unlike the 'tigase-auth' connector it allows you to define SQL queries in the configuration file. The advantage of this implementation is that you don’t have to touch your database. You can use either simple plain SQL queries or stored procedures. The configuration is more difficult as you have to enter carefully all SQL queries in the config file and changing the query usually involves restarting the server. For more details about this implementation and all configuration parameters please refer to :ref:`Tigase Custom Auth documentation<custonAuthConnector>`.\r\n\r\n-  |ss| ``tigase-auth``\\ |se|\\  (**DEPRECATED**) - is the authentication connector which can be used with any database. It executes stored procedures to perform all actions. Therefore it is a very convenient way to integrate the server with an external database if you don’t want to expose the database structure. You just have to provide a set of stored procedures in the database. While implementing all stored procedures expected by the server might be a bit of work it allows you to hide the database structure and change the SP implementation at any time. You can add more actions on user login/logout without restarting or touching the server. And the configuration on the server side is very simple. For detailed description of this implementation please refer to :ref:`Tigase Auth documentation<tigaseAuthConnector>`.\r\n\r\nAs always the simplest way to configure the server is through the ``config.tdsl`` file. In the article describing this file you can find long list with all available options and all details how to handle it. For the authentication connector setup however we only need 2 options:\r\n\r\n.. code::\r\n\r\n   dataSource {\r\n       'default-auth' () {\r\n           uri = 'database connection url'\r\n       }\r\n   }\r\n   authRepository {\r\n       default () {\r\n           cls = 'connector'\r\n           'data-source' = 'default-auth'\r\n       }\r\n   }\r\n\r\nYou have to use a class name if you want to attach your own authentication connector.\r\n\r\nDefault is:\r\n\r\n.. code::\r\n\r\n   authRepository {\r\n       default {\r\n           cls = 'tigase.db.jdbc.TigaseAuth'\r\n       }\r\n   }\r\n\r\nIn the same exact way you can setup connector for any different database type.\r\n\r\nFor example, tigase-custom authentication connector.\r\n\r\n.. code::\r\n\r\n   authRepository {\r\n       default {\r\n           cls = 'tigase.db.jdbc.TigaseCustomAuth'\r\n       }\r\n   }\r\n\r\nThe different ``cls`` or classes are:\r\n\r\n-  MySQL, Derby, PostgreSQL, MS SQL Server - ``tigase.db.jdbc.JDBCRepository``\r\n\r\nYou can normally skip configuring connectors for the default Tigase database format: ``mysql``, ``pgsql`` and ``derby``, ``sqlserver`` as they are applied automatically if the parameter is missing.\r\n\r\nOne more important thing to know is that you will have to modify ``authRepository`` if you use a custom authentication connector. This is because if you retrieve user login data from the external database this external database is usually managed by an external system. User accounts are added without notifying Tigase server. Then, when the user logs in and tries to retrieve the user roster, the server can not find such a user in the roster database.\r\n\r\n.. Important::\r\n\r\n   To keep user accounts in sync between the authentication database and the main user database you have to add following option to the end of the database connection URL: ``autoCreateUser=true``.\r\n\r\nFor example:\r\n\r\n.. code::\r\n\r\n   dataSource {\r\n       default () {\r\n           uri = 'jdbc:mysql://localhost/tigasedb?user=nobody&password=pass&autoCreateUser=true'\r\n       }\r\n   }\r\n\r\nIf you are interested in even further customizing your authentication connector by writing your own queries or stored procedures, please have a look at the following guides:\r\n\r\n- :ref:`Tigase Auth guide<tigaseAuthConnector>`\r\n\r\n- :ref:`Tigase Custom Auth guide<custonAuthConnector>`\r\n\r\n.. include:: Auth_Connectors/Tigase_Auth_Connector.inc\r\n.. include:: Auth_Connectors/Custom_Auth_Connector.inc\r\n.. include:: Auth_Connectors/LDAP_Auth.inc\r\n.. include:: Auth_Connectors/SASL_EXTERNAL.inc\r\n\r\n.. |ss| raw:: html\r\n\r\n    <strike>\r\n.. |se| raw:: html\r\n\r\n    </strike>"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Security/Brute-force_attack_prevention.inc",
    "content": "Brute-force attack prevention\r\n---------------------------------\r\n\r\nBrute-force Prevention is designed to protect Tigase Server against user password guessing. It counts invalid login tries and when it is above limit, it locks login ability for specific time (soft ban). When invalid login counter reaches second level, account will be disabled permanently.\r\n\r\nConfiguration\r\n^^^^^^^^^^^^^^^^^\r\n\r\nBrute-force Prevention is configured by VHost. There is following lis of configuration parameters:\r\n\r\n+-------------------------------------+-------------+---------------------------------------------------------------------------+\r\n| ``brute-force-lock-enabled``        | ``boolean`` | Brute Force Prevention Enabled                                            |\r\n+-------------------------------------+-------------+---------------------------------------------------------------------------+\r\n| ``brute-force-lock-after-fails``    | ``long``    | Number of allowed invalid login                                           |\r\n+-------------------------------------+-------------+---------------------------------------------------------------------------+\r\n| ``brute-force-period-time``         | ``long``    | Time [sec] in what failed login tries are counted                         |\r\n+-------------------------------------+-------------+---------------------------------------------------------------------------+\r\n| ``brute-force-disable-after-fails`` | ``long``    | Threshold beyond which account will be permanently disabled               |\r\n+-------------------------------------+-------------+---------------------------------------------------------------------------+\r\n| ``brute-force-lock-time``           | ``long``    | Time [sec] of soft ban (first threshold)                                  |\r\n+-------------------------------------+-------------+---------------------------------------------------------------------------+\r\n| ``brute-force-mode``                | ``string``  | Working mode (see :ref:`Working modes<WorkingModes>`)                     |\r\n+-------------------------------------+-------------+---------------------------------------------------------------------------+\r\n\r\nDetailed statistics\r\n~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nBy default, in order not to pollute statistics, Brute-Force locker will only provide details about number of locker IPs and JIDs (and total number of locked attempts). In order to have detailed information about IPs and JIDs that has been locked in statistics you should use following configuration:\r\n\r\n::\r\n\r\n   'sess-man' () {\r\n       'brute-force-locker' () {\r\n           detailedStatistics = false\r\n       }\r\n   }\r\n\r\n.. _WorkingModes:\r\n\r\nWorking modes\r\n~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nThere are three working modes:\r\n\r\n-  ``Ip`` - it counts invalid login tries from IP, and locks login ability (soft ban) for IP what reach the threshold\r\n\r\n-  ``IpJid`` - it counts tries from IP to specific user account. Soft ban locks ability of login to specific JID from specific IP.\r\n\r\n-  ``Jid``- similar to ``IpJid`` but checks only JID. Soft ban locks ability of login to specific JID from all IPs.\r\n\r\n.. **Note**::\r\n\r\n   Only in modes ``Jid`` and ``IpJid`` account may be permanently disabled.\r\n\r\nPermanent ban\r\n~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nIn modes ``Jid`` and ``IpJid``, when invalid login counter reach threshold ``brute-force-disable-after-fails``, account status will be set o ``disabled``. To enable it again you should use `Re-Enable User <https://xmpp.org/extensions/xep-0133.html#reenable-users>`__ Ad-hoc Command.\r\n\r\nDisabling Brute Force protection\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nIn order to disable Brute-Force detection you should deactivate ``BruteForceLockerBean``:\r\n\r\n::\r\n\r\n   'sess-man' () {\r\n       'brute-force-locker' (active: false) {}\r\n   }\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Security/Packet_Filtering.inc",
    "content": "Packet Filtering\r\n----------------------\r\n\r\nTigase offers different ways to filter XMPP packets flying through the server. The most common use for packet filtering is to restrict users from sending or receiving packets based on the sender or received address.\r\n\r\nThere are also different possible scenarios: time based filtering, content filtering, volume filtering and so on.\r\n\r\nAll pages in this section describe different filtering strategies.\r\n\r\nDomain Based Packet Filtering\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nDomain based packet filtering is a simple filter allowing to restrict user communication based on the source/destination domain name. This is especially useful if we want to limit user communication within a single - own domain only or a list of domains.\r\n\r\nA company might not wish to allow employers to chat during work hours with anybody in the world. A company may also have a few different domains used by different branches or departments. An administrator may restrict communication to a list of domains.\r\n\r\nIntroduction\r\n~~~~~~~~~~~~~~~\r\n\r\nThe restriction is on a per-user basis. So the administrator can set a different filtering rules for each user. There is also a per-domain configuration and global-installation setting (applied from most general to most specific, i.e. from installation to user).\r\n\r\nRegular users can not change the settings. So this is not like a privacy list where the user control the filter. Domain filter can not be changed or controlled by the user. The system administrator can change the settings based on the company policy.\r\n\r\nThere are predefined rules for packet filtering:\r\n\r\n1. ``ALL`` - user can send and receive packets from anybody.\r\n\r\n2. ``LOCAL`` - user can send and receive packets within the server installation only and all it’s virtual domains.\r\n\r\n3. ``OWN`` - user can send and receive packets within his own domains only\r\n\r\n4. ``BLOCK`` - user can’t communicate with anyone. This could be used as a means to temporarily disable account or domain.\r\n\r\n5. ``LIST`` - user can send and receive packets within listed domains only (i.e. *whitelist*).\r\n\r\n6. ``BLACKLIST`` - user can communicate with everybody (like ``ALL``), except contacts on listed domains.\r\n\r\n7. ``CUSTOM`` - user can communicate only within custom created rules set.\r\n\r\nWhitelist (``LIST``) and blacklist (``BLACKLIST``) settings are mutually exclusive, i.e. at any given point of time only one of them can be used.\r\n\r\nThose rules applicable to particular users are stored in the user repository and are loaded for each user session. If there are no rules stored for a particular user server tries to apply rules for a VHost of particular user, and if there is no VHost filtering policy server uses global server configuration. If there is no filtering policy altogether server applies defaults based on following criteria:\r\n\r\n1. If this is **Anonymous** user then ``LOCAL`` rule is applied\r\n\r\n2. For all **other** users ``ALL`` rule is applied.\r\n\r\nConfiguration\r\n~~~~~~~~~~~~~~~\r\n\r\nFiltering is performed by the domain filter plugin which must be loaded at startup time. It is loaded by default if the plugins list is not set in the configuration file. However if you have a list of loaded plugins in the configuration file make sure ``domain-filter`` is on the list.\r\n\r\nThere is no other configuration required for the plugin to work.\r\n\r\nAdministration, Rules Management\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nAlthough controlling domain filtering rules is possible for each user separately, it is not practical for large installations. In most cases users are stored in the database and a third-party system keeps all the user information.\r\n\r\nTo change the rule for a single user you can use loadable administration scripts feature and load `UserDomainFilter.groovy <https://github.com/tigase/tigase-server/tree/master/src/main/groovy/tigase/admin/UserDomainFilter.groovy>`__ script. It enables modifying rules for a given user JID.\r\n\r\nImplementation\r\n~~~~~~~~~~~~~~~\r\n\r\nIf you have a third party system which keeps and manages all user information than you probably have your own UserRepository implementation which allows the Tigase server to access user data. Filtering rules are loaded from user repository using following command:\r\n\r\n.. code:: java\r\n\r\n   repo.getData(user_id, null, DomainFilter.ALLOWED_DOMAINS_KEY, null);\r\n   repo.getData(user_id, null, DomainFilter.ALLOWED_DOMAINS_LIST_KEY, null);\r\n\r\nWhere ``user_id`` is user Jabber ID without resource part, ``DomainFilter.ALLOWED_DOMAINS_KEY`` is a property key: ``allowed-domains``. The user repository MUST return one of following only:\r\n\r\n1. ``ALL`` - if the user is allowed to communicate with anybody\r\n\r\n2. ``LOCAL`` - if the user is allowed to communicate with users on the same server installation.\r\n\r\n3. ``OWN`` - if the user is allowed to communicate with users within his own domain only.\r\n\r\n4. ``LIST`` - list of domains within which the user is allowed to communicate with other users. No wild-cards are supported. User’s own domain should be included too.\r\n\r\n5. ``BLACKLIST`` - list of domains within which the user is NOT allowed to communicate with other users. No wild-cards are supported. User’s own domain should NOT be included.\r\n\r\n6. ``CUSTOM`` - list of rules defining custom communication permissions (server processes stanza according to first matched rule, similar to XEP-0016) in the following format:\r\n\r\n::\r\n\r\n   ruleSet = rule1;rule2;ruleX;\r\n\r\n   rule = order_number|policy|UID_type[|UID]\r\n\r\n   order_number = any integer;\r\n   policy = (allow|deny);\r\n   UID_type = [jid|domain|all];\r\n   UID = user JID or domain, for example pubsub@test.com; if UID_type is ALL then this is ignored.\r\n\r\nFor example:\r\n\r\n::\r\n\r\n   1|allow|self;\r\n   2|allow|jid|admin@test2.com;\r\n   3|allow|jid|pubsub@test.com;\r\n   4|deny|all;\r\n\r\n1. ``null`` - a java null if there are no settings for the user.\r\n\r\nIn case of ``LIST`` and ``BLACKLIST`` filtering options, it’s essential to provide list of domains for the whitelisting/blacklisting. ``DomainFilter.ALLOWED_DOMAINS_LIST_KEY`` is a property key: \"allowed-domains-list\". The user repository MUST return semicolon separated list of domains: ``domain1.com;domain2.com,domain3.org``\r\n\r\nThe filtering is performed by the `tigase.xmpp.impl.DomainFilter <https://github.com/tigase/tigase-server/tree/master/src/main/java/tigase/xmpp/impl/DomainFilter.java>`__ plugin. Please refer to source code for more implementation details."
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Security/RealTimeBlockList.inc",
    "content": "Real-Time Block List (RTBL)\n---------------------------------\n\nReal-Time Block List (RTBL) is an answer to spam and abuse. Shared blocklists are commonly used in email to identify sources of spam and are often shared between providers to improve spam detection.\n\nTigase XMPP Server supports (`XMPP real-time block <https://xmppbl.org/>`__) lists using XMPP Publish-Subscribe protocol (`XEP-0060 <https://xmpp.org/extensions/xep-0060.html>`__). Due to used technology, Tigase XMPP Server with configured RTBL will receive updates from the remotly hosted blocklist immediately, improving reaction time on the possible abuse and decreasing administrator or moderator workload. For more details about XMPP RTBL please visit https://xmppbl.org/.\n\n.. note:: \n   Currently, block lists are used by MUC component, but in the future more functionality will be using block lists to further reduce spam and abuse.\n\n   JIDs blocked are not allowed to:\n\n   * enter any MUC room,\n   * send message to MUC room,\n   * send private message to MUC room members,\n\n   for rooms hosted on the installation, with the exception to the rooms in which JID is a member of the room.\n\n.. warning::\n   This functionality requires working server-to-server (S2S) communication between your default virtual domain on your server and the server hosting XMPP real-time blocklist.\n\n   Without working S2S communication, blocklist may be added but will not receive any entries and will not block anything.\n\nConfiguration\n^^^^^^^^^^^^^^^^^\n\nReal-time block lists are configured with ad-hoc commands on the `rtbl-component.example.com` XMPP component (`example.com` is the virtual host domain hosted on your installation of Tigase XMPP Server).\n\nIf multiple RTBL are configured, then if JID is blocked by any of the block lists, it will be blocked.\n\n\nAdding RTBL\n~~~~~~~~~~~~~~~~~~~~~\n\nTo add publicly available RTBL hosted at node `muc_bans_sha256` on `xmppbl.org` we will use Tigase Admin UI. We will select `Add real-time blocklist` (`rtbl-add`) command and then in the provided form we will enter:\n\n* `xmppbl.org` in the `Service address (JID)` field\n* `muc_bans_sha256` in the `Node` field\n* `SHA-256` in the `Hashing algorithm`\n\nas this list uses `SHA-256` hashing algorithm to report identifiers of blocked JIDs.\n\n.. image:: /images/admin/rtbl-add-form.png\n    :width: 600\n    :alt: Screenshot of filled form for adding RTBL\n\nAfter submition of the form, the new blocklist will be added and automatically fetched from the remote server.\n\nRemoving RTBL\n~~~~~~~~~~~~~~~~~~~~~\n\nTo delete RTBL previously added to the installation, we will use `Delete real-time blocklist` (`rtbl-delete`) command and in the provided form we will select blocklist to remove from the list of available blocklists;\n\n.. image:: /images/admin/rtbl-delete-form.png\n    :width: 600\n    :alt: Screenshot of filled form for removing RTBL\n\nAfter submission of the form, selected blocklist will be removed and all JIDs blocked by it will be automatically unblocked.\n\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Security/SASL_Mechanisms.inc",
    "content": "SASL Mechanisms\r\n----------------------\r\n\r\nXMPP protocol supports many authentication methods, but most of them are used as `SASL <https://tools.ietf.org/html/rfc4422>`__ mechanisms. Tigase XMPP Server provides many SASL-based authentication mechanisms such as:\r\n\r\n-  PLAIN *(enabled)*\r\n\r\n-  ANONYMOUS\r\n\r\n-  EXTERNAL\r\n\r\n-  SCRAM-SHA-1 *(enabled)*\r\n\r\n-  SCRAM-SHA-256 *(enabled)*\r\n\r\n-  SCRAM-SHA-512\r\n\r\nMost of them are enabled by default on default Tigase XMPP Server installation.\r\n\r\nEnabling and disabling SASL mechanisms (credentials encoder/decoder)\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIf you want to enable or disable one of password-based authentication mechanism such as ``SCRAM-SHA-1``, ``SCRAM-SHA-256`` or ``SCRAM-SHA-512`` you can do that by enabling or disabling encoders and decoders used on your installation. By enabling encoders/decoders you are deciding in what form the password is stored in the database. Those changes may (and in most cases will) impact which SASL mechanisms may be allowed to use on your installation.\r\n\r\n.. Note::\r\n\r\n   In most cases you should enable or disable both (credentials encoder and decoder) of the same type at the same time. The only exception of this rule is when you are changing those on already working installation. In this case you should only enable encoder of the type which you want to enable and request users to change their passwords. Then, after users will change their passwords, you should reconfigure server to enable decoder of the particular type. *(in other case user may loose a way to log in to your installation as system will reject their credentials as it may not have matching credentials for particular SASL mechanism)*.\r\n\r\n**Enabling SCRAM-SHA-512 encoder**\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\n.. code::\r\n\r\n   authRepository () {\r\n       default () {\r\n           credentialEncoders () {\r\n               'SCRAM-SHA-512' () {}\r\n           }\r\n       }\r\n   }\r\n\r\n**Disabling SCRAM-SHA-1 decoder**\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n.. code::\r\n\r\n   authRepository () {\r\n       default () {\r\n           credentialDecoders () {\r\n               'SCRAM-SHA-1' (active: false) {}\r\n           }\r\n       }\r\n   }\r\n\r\n.. Warning::\r\n\r\n    It is strongly recommended not to disable encoders if you have enabled decoder of the same type as it may lead to the authentication issues, if client tries to use a mechanism which that is not available."
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Security/Server_Certificates.inc",
    "content": "Server Certificates\r\n---------------------\r\n\r\n-  :ref:`Creating and Loading the Server Certificate in pem Files<certspem>`\r\n\r\n.. _certspem:\r\n\r\nCreating and Loading the Server Certificate in pem Files\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nServer Certificates\r\n~~~~~~~~~~~~~~~~~~~~~\r\n\r\nServer certificates are needed when you use secure socket connections - SSL/TLS.\r\n\r\nFor secure socket connection a proper certificate is needed. You can either generate your own self-signed certificate or obtain certificate from trusted third party organization.\r\n\r\nHere are steps how to obtain certificate from a trusted organization.\r\n\r\nGenerating your Own Certificates\r\n\r\nSelf-signed certificates can be generated easily on a Linux system. Although it may not be considered a 'trusted' certificate authority, it can be useful to test server installations. **We do not recommend regular use of self-signed certificates**.\r\n\r\n.. Note:: \r\n\r\n   that Tigase v5.0 and later can automatically create self signed PEM files if needed. However we will cover doing this process by hand.\r\n\r\nThis tutorial assumes you are running a Linux-based operating system with access to command shell, and the 'Openssl' package is installed on the system.\r\n\r\n| The process takes the following steps:\r\n| 1. Create a local private key. This file ends with .key extension. It is recommended to create a new private key for the process.\r\n| 2. Generate a certificate request. This file ends with the .csr extension and is the file sent to the Certificate Authority to be signed.\r\n| 3. CA signs private key. This can be done by your own computer, but can also be done by private CAs for a fee.\r\n| 4. Results are obtained from the CA. This is a ``.crt`` file which contains a separate public certificate.\r\n| 5. Combine the ``.csr`` and ``.crt`` file into a unified ``.pem`` file. Tigase requires keys to be non-password protected PEM files.\r\n\r\n**Generate local private key.**\r\n\r\n.. code:: sh\r\n\r\n   openssl genrsa -out [domain.com.key] 1024\r\n\r\nThis command generates a private key using a 1024 bit RSA algorithm. ``-out`` designates the name of the file, in this case it will be ``domain.com.key``. The exact name is not important, and the file will be created in whatever directory you are currently in.\r\n\r\n**Generate a certificate request:.**\r\n\r\n.. code:: sh\r\n\r\n   openssl req -nodes -key domain.com.key -out domain.com.csr\r\n\r\nThis command generates a certificate request using the file specified after ``-key``, and the result file will be ``domain.com.csr``. You will be asked a series of questions to generate the request.\r\n\r\n.. code:: sh\r\n\r\n   Country Name (2 letter code) [AU]:AU\r\n   State or Province Name (full name) [Some-State]:Somestate\r\n   Locality Name (eg, city) []:Your city name\r\n   Organization Name (eg, company) [Internet Widgits Pty Ltd]:Company name\r\n   Organizational Unit Name (eg, section) []:Department or any unit\r\n   Common Name (eg, YOUR name) []:*.yourdomain.com\r\n   Email Address []:your_email_address@somedomain.com\r\n\r\n   Please enter the following 'extra' attributes\r\n   to be sent with your certificate request\r\n   A challenge password []:\r\n   An optional company name []:\r\n\r\n**Sign the Certificate Request:.**\r\n\r\nNow the .csr file will be signed by a Certificate Authority. In this tutorial, we will be self-signging our certificate. This practice however is generally not recommended, and you will receive notifications that your certificate is not trusted. There are commercial offers from companies to sign your certificate from trusted sources. Please see the :ref:`Certificate From Other Providers<OtherSources>` section for more information.\r\n\r\n.. code:: bash\r\n\r\n   openssl x509 -req -days 365 -in domain.com.csr -signkey domain.com.key -out domain.com.crt\r\n\r\nThis command signs the certificate for 365 days and generates the ``domain.com.crt`` file. You can, of course use any number of days you like.\r\n\r\n**Generate PEM file.**\r\n\r\nYou should now have the following files in the working directory: ..\\\\ domain.com.key domain.com.csr domain.com.crt\r\n\r\n.. code:: sh\r\n\r\n   cat yourdomain.com.cert.pem intermediate.cert.pem root.cert.pem > yourdomain.com.pem\r\n\r\nIf the certificate is issued by third-party authority you will have to attach the certificate chain, that being certificate of the authority who has generated your certificate. You normally need to obtain certificates for your chain from the authority who has generated your certificate.\r\n\r\nThe result file should looks similar to:\r\n\r\n.. code:: sh\r\n\r\n   -----BEGIN CERTIFICATE-----\r\n   MIIG/TCCBeWgAwIBAgIDAOwZMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ\r\n   .\r\n   .\r\n   .\r\n   pSLqw/PmSLSmUNIr8yQnhy4=\r\n   -----END CERTIFICATE-----\r\n   -----BEGIN RSA PRIVATE KEY-----\r\n   WW91J3JlIGtpZGRpbmchISEKSSBkb24ndCBzaG93IHlvdSBvdXIgcHJpdmF0ZSBr\r\n   .\r\n   .\r\n   .\r\n   ZXkhISEhCkNyZWF0ZSB5b3VyIG93biA7KSA7KSA7KQo=\r\n   -----END RSA PRIVATE KEY-----\r\n   -----BEGIN CERTIFICATE-----\r\n   MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW\r\n   .\r\n   .\r\n   .\r\n   xV/stleh\r\n   -----END CERTIFICATE-----\r\n\r\nFor Tigase server as well as many other servers (Apache 2.x), the order is following; your domain certificate, your private key, authority issuing your certificate, root certificate.\r\n\r\n.. NOTE::\r\n\r\n   **Tigase requires full certificate chain in PEM file (described above)! Different applications may require pem file with certificates and private key in different order. So the same file may not be necessarily used by other services like Web server or e-mail server. Currently, Tigase can automatically sort certificates in PEM file while loading it.**\r\n\r\n.. _InstallingSSLCertificate:\r\n\r\nInstalling/Loading Certificate To the Tigase Server\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nInstalling and loading certificates is very easy. The server can load all certificates directly from **pem** files. You just need to create a separate pem file for each of your virtual domains and put the file in a directory accessible by the server. Tigase server can automatically load all **pem** files found in given directory. By default, and to make things easy, we recommend the ``Tigase/certs`` directory.\r\n\r\nIt’s also possible to use: \\* Admin ad-hoc command via XMPP client - you should navigate to Service Discovery of your server and in the list of commands for ``VHost Manager`` component select ``Add SSL Certificate`` and then follow instructions \\* Admin WebUI - open ``http://<host>/admin``, navigate to ``Other`` category and in it select ``Add SSL Certificate`` and then follow instructions \\* REST API - make a ``POST`` request to http://localhost:8080/rest/adhoc/vhost-man@domain.com with payload containing your certificate; to get the required form fields make ``GET`` request to the same endpoint\r\n\r\n.. _OtherSources:\r\n\r\nCertificate From Other Providers\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nThere is number of certificate providers offering certificates either for free or for money. You can use any of them, however you have to be aware that sometimes certificates might not be recognized by all XMPP servers, especially if it’s one from a new provider. Here is an example list of providers:\r\n\r\n-  LetsEncrypt - please see :ref:`Installing LetsEncrypt Certificates in Your Linux System<LetsEncryptCertificate>` for details\r\n\r\n-  `CAcert <https://www.cacert.org/>`__ - free certificates with Web GUI. (WARNING: it’s not widely accepted)\r\n\r\n-  `Verisign <https://www.verisign.com/>`__ - very expensive certificates comparing to above provides but the provider is recognized by everybody. If you have a certificate from Verisign you can be sure it is identified as a valid certificate.\r\n\r\n-  `Comodo Certificate Authority <http://www.comodo.com/business-security/digital-certificates/ssl-certificates.php>`__ offers different kind of commercial certificates\r\n\r\nTo obtain certificate from a third party authority you have to go to its website and request the certificate using certificate request generated above. I cannot provide any instructions for this as each of the providers listed have different requirements and interfaces.\r\n\r\nWe **highly** recommend using LetsEncrypt keys to self-sign and secure your domain. Instructions are in the :ref:`next section<LetsEncryptCertificate>`.\r\n\r\nUsing one certificate for multiple domains\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\n.. Note::\r\n\r\n   Tigase tries to be *smart* and automatically detects wildcard domain and alternative domains so it’s not needed to duplicate same certificate in multiple files to match domains - same file will be loaded and make available for all domains (CNames) available in the certificate.\r\n\r\n.. _LetsEncryptCertificate:\r\n\r\nInstalling LetsEncrypt Certificates in Your Linux System\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nLetsEncrypt is a trusted CA that provides free security certificates. Unlike previously self-signed certificates, we can use LetsEncrypt Certificates to certify your domains from a trusted source.\r\n\r\nPlease refer to official `certbot User Guide <https://certbot.eff.org/docs/using.html>`__ for details how to install and operate the tool, choosing desired method of domain authentication (DNS or webserver). After successful execution the certificate with all related files will be stored under ``/etc/letsencrypt/live/$domain``\r\n\r\n.. code:: bash\r\n\r\n   $ sudo ls  /etc/letsencrypt/live/$domain\r\n   cert.pem  chain.pem  fullchain.pem  privkey.pem  README\r\n\r\nIn that directory, you will find four files:\r\n\r\n-  ``privkey.pem`` - private key for the certificate\r\n\r\n-  ``cert.pem`` - contains the server certificate by itself\r\n\r\n-  ``chain.pem`` - contains the additional intermediate certificate or certificates\r\n\r\n-  ``fullchain.pem`` - all certificates, including server certificate (aka leaf certificate or end-entity certificate). The server certificate is the first one in this file, followed by any intermediates.\r\n\r\nFor Tigase XMPP Server, we are only concerned with ``privkey.pem`` and ``fullchain.pem`` (or ``chain.pem`` - please consider actual issuers and certification chain in this case!).\r\n\r\nAt this point we will need to obtain the root and intermediate certificates, this can be done by downloading these certificates from the `LetsEncrypt Chain of Trust website <https://letsencrypt.org/certificates/>`__.\r\n\r\n.. Note::\r\n\r\n   Please pay utmost attention to the actual certificate issuers and make sure that the certification chain is maintained!\r\n\r\nOn the time of the writing, LetsEncrypt root CA is ``ISRG Root X1``. In order to provide complete chain to the root CA ``ISRG Root X1`` root certificate: https://letsencrypt.org/certs/isrgrootx1.pem and combine it with private key and full certificate chain from certbot.\r\n\r\n.. code:: bash\r\n\r\n   wget https://letsencrypt.org/certs/isrgrootx1.pem\r\n\r\nThese are the root certificate, and the intermediate certificate signed by root certificate.\r\n\r\n.. Note::\r\n\r\n   IdenTrust cross-signed certificate will not function properly in the future!\r\n\r\nTake the contents of your ``privkey.pem``, certificate, and combine them with the contents of ``isrgrootx1.pem``  into a single pem certificate.\r\n\r\nDepending on your configuration you either need to name the file after your domain such as ``mydomain.com.pem`` and place it under ``certs/`` subdirectory of Tigase XMPP Server installation or update it using admin ad-hoc (see :ref:`Storing and managing certificates<certificateStorage>`)\r\n\r\nIf you moved all certs to a single directory, you may combine them using the following command under \\*nix operating systems:.\r\n\r\n.. code:: bash\r\n\r\n   cat ./cert.pem ./privkey.pem ./isrgrootx1.pem > mydomain.com.pem\r\n\r\nYour certificate should look something like this:\r\n\r\n.. code::rtificate\r\n\r\n   -----BEGIN PRIVATE KEY-----\r\n   MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDAUAqqKu7Z4odo\r\n   ...\r\n   og89F9AbWr1mNmyRoScyqMXo\r\n   -----END PRIVATE KEY-----\r\n   -----BEGIN CERTIFICATE-----\r\n   cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\r\n   ...\r\n   TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\r\n   -----END CERTIFICATE-----\r\n   -----BEGIN CERTIFICATE-----\r\n   FhpodHRwOi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCBqwYIKwYBBQUHAgIwgZ4MgZtU\r\n   ...\r\n   bmcgUGFydGllcyBhbmQgb25seSBpbiBhY2NvcmRhbmNlIHdpdGggdGhlIENlcnRp\r\n   -----END CERTIFICATE-----\r\n\r\n.. Warning::\r\n\r\n    LetsEncrypt certificates expire 90 days from issue and need to be renewed in order for them to remain valid!\r\n\r\nYou can check your certificate with utility class:\r\n\r\n::\r\n\r\n   java -cp <path_to_tigase-server_installation>/jars/tigase-utils.jar tigase.cert.CertificateUtil -lc mydomain.com.pem -simple\r\n\r\nLet’s encrypt and DNS verification\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nThe only way to obtain wildcard (``*.domain.com``) certificate is via DNS verification. Certbot support a number of DNS operators - you can check if your DNS provider is listed by executing ``$ certbot plugins``\r\n\r\nAWS Route53\r\n\r\nIf you want to use it with Amazon Cloud you should install plugin for AWS:\r\n\r\n::\r\n\r\n   pip install certbot-dns-route53\r\n\r\n.. Note::\r\n\r\n   If you are using certbot under macOS and you installed it via brew then you should use: ``$( brew --prefix certbot )/libexec/bin/pip install certbot-dns-route53``\r\n\r\nYou should store your credentials in ``~/.aws/credentials`` (you may want to create dedicated policy for updating DNS as described in `plugin’s documentation <https://certbot-dns-route53.readthedocs.io/en/stable/>`__:\r\n\r\n.. code:: bash\r\n\r\n   [default]\r\n   aws_access_key_id = <key_id>\r\n   aws_secret_access_key = <key>\r\n\r\nAnd afterward you should execute ``certbot`` with ``--dns-route53`` parameter\r\n\r\nCertbot update hook and Tigase API\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nFor greater automation it’s possible to automate updating certificate obtained with ``certbot`` in Tigase XMPP Server. You should use following deploy hook - either add it to ``/etc/letsencrypt/renewal-hooks/deploy/`` or use it directly in ``certboot`` commandline with ``--deploy-hook`` parameter (in the latter case, it will be added to particular domain configuration so it’s not necessary to specify UPDATE_DOMAINS).\r\n\r\n.. Note::\r\n\r\n   Please adjust account credentials used for deployment (``USER`` (only localpart, not JID!), ``PASS``, ``DOMAIN``) as well as paths to Let’s Encrypt certificates (``LE_CERTS_PATH``, i.e. path pointing to the directory with ``isrgrootx1.pem`` file)\r\n\r\n.. Note::\r\n\r\n   It's recommended to create dedicated user responsible for updating the certificate configured as domain administrator  to limit the scope of the access the user has on the server.\r\n\r\n.. code:: bash\r\n\r\n   #!/bin/bash\r\n\r\n   set -e\r\n\r\n   ## Configuration START\r\n\r\n   USER=\"admin_username\"\r\n   PASS=\"admin_password\"\r\n   DOMAIN=\"my_domain.tld\"\r\n   HOST=${DOMAIN}\r\n   #UPDATE_DOMAINS=(${DOMAIN})\r\n   PROTOCOL=https\r\n   # PORT=\":8080\"\r\n   # APIKEY=\"?api-key=mySecretKey\"\r\n   LE_CERTS_PATH=\"/path/to/letsencrypt/CA/certificates/\"\r\n\r\n   ## Configuration END\r\n\r\n   fail_count=0\r\n\r\n   for domain in ${RENEWED_DOMAINS[@]}; do\r\n       if [[ $domain == \"*.\"* ]]; then\r\n           CERT_DOMAIN=${domain#*\\*.}\r\n       else\r\n           CERT_DOMAIN=${domain}\r\n       fi\r\n\r\n       if [[ ! -z \"${UPDATE_DOMAINS}\" ]] ; then\r\n           match=0\r\n           for dn in \"${UPDATE_DOMAINS[@]}\"; do\r\n               if [[ $dn = \"$CERT_DOMAIN\" ]]; then\r\n                   match=1\r\n                   break\r\n               fi\r\n           done\r\n           if [[ $match = 0 ]]; then\r\n               echo \"Skipping updating ${domain} because it's not in the list of supported domains: ${UPDATE_DOMAINS[@]}\"\r\n               continue\r\n           fi\r\n       fi\r\n\r\n       CERT=`cat \"$RENEWED_LINEAGE/fullchain.pem\" \"$RENEWED_LINEAGE/privkey.pem\" \"${LE_CERTS_PATH}/isrgrootx1.pem\"`\r\n\r\n       REQUEST=\"\r\n       <command>\r\n         <node>ssl-certificate-add</node>\r\n         <fields>\r\n           <item>\r\n             <var>Certificate in PEM format</var>\r\n             <value>${CERT}</value>\r\n           </item>\r\n           <item>\r\n             <var>command-marker</var>\r\n             <value>command-marker</value>\r\n           </item>\r\n           <item>\r\n             <var>VHost</var>\r\n             <value>${CERT_DOMAIN}</value>\r\n           </item>\r\n           <item>\r\n             <var>Save permanently (to disk or repository)</var>\r\n             <value>true</value>\r\n           </item>\r\n         </fields>\r\n       </command>\"\r\n\r\n       response=`curl -s -L -H \"Content-Type: text/xml\" -X POST  ${PROTOCOL}://${USER}%40${DOMAIN}:${PASS}${HOST}${PORT}/rest/adhoc/vhost-man@${DOMAIN}${APIKEY} -d \"${REQUEST}\"`\r\n\r\n       if [[ ! ${response} = *\"loaded successfully\"* ]] ; then\r\n           echo -e \"Server returned error while updating   ${domain}   certificate:\\n ${response}\"\r\n           fail_count=$((${fail_count}+1))\r\n       else\r\n           echo \"Correctly updated ${domain} certificate\"\r\n       fi\r\n   done\r\n\r\n   exit ${fail_count}\r\n\r\n.. Note::\r\n\r\n   If you are not using wildcard certificate when you have to provide certificate for main domain as well as certificates for subdomains that mach all components that you want to expose (muc, pubsub, push, etc…)\r\n\r\n.. _certificateStorage:\r\n\r\nStoring and managing certificates\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nFilesystem\r\n~~~~~~~~~~~~~~\r\n\r\nBy default Tigase loads and stores certificates in ``certs/`` subdirectory. Each *domain* certificate should be stored in a file which filename consists of domain name and ``.pem`` extension, i.e. ``<domain>.pem``. For example for domain tigase.net it would be ``certs/tigase.net.pem``.\r\n\r\n.. Note::\r\n\r\n   Tigase tries to be *smart* and automatically detects wildcard domain and alternative domains so it’s not needed to duplicate same certificate in multiple files to match domains.\r\n\r\nDatabase repository\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nAlternatively it’s possible to use database as a storage for the certificates. Upon enabling it certificates won’t be read nor stored to the filesystem. You can enable it by adding ``repository () {}`` bean to ``'certificate-container' () {}`` in your TDSL configuration file:\r\n\r\n::\r\n\r\n   'certificate-container' () {\r\n       repository () {}\r\n   }\r\n\r\nIf you are using database repository then you manage/update certificates using either ad-hoc command ``Add SSL certificate`` from *VHost Manager* or via HTTP REST API."
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Security/TLS_Features_Configuration.inc",
    "content": "TLS/SSL encryption features configuration\r\n---------------------------------------------\r\n\r\nTigase allows adjusting the most important parameters used when establishing TLS connections - set of protocols and ciphers that will be used during negotiation of the connection. The single most important is ``hardened-mode`` - it’s the most general configuration and offers three-step adjustment of the settings - please see :ref:`hardened-mode<hardenedMode>` for details. ``hardened-mode`` can be configured both via TDSL configuration file (either on ``root`` level or for ``sslContextContainer`` for particular connection managers) or on VHost level.\r\n\r\nIf you want to disable certain protocols or ciphers you can use two options: ``tls-disabled-protocols`` and ``tls-disabled-ciphers`` respectively. They allow, as name suggests, disabling certain items from default sets. They both takes an array of strings, which ten be removed from the lists.\r\n\r\nLet’s say you’d like to remove support for ``SSL``, ``SSLv2`` and ``SSLv3`` protocols. You should simply use following configuraiton: ``'tls-disabled-protocols' = ['SSL', 'SSLv2', 'SSLv3']``. Complete list of protocols depends on particular Java version that you use - please refer to the documentation for details. For example for the default Java11 list you can check `SSLContext Algorithms <https://docs.oracle.com/en/java/javase/11/docs/specs/security/standard-names.html#sslcontext-algorithms>`__\r\n\r\n``tls-disabled-ciphers`` follows same format and uses names defined in `JSSE Cipher Suite Names <https://docs.oracle.com/en/java/javase/11/docs/specs/security/standard-names.html#jsse-cipher-suite-names>`__. It’s also possible to use regular expressions to quickly eliminate groups of ciphers.\r\n\r\nIf you want to enable only specific protocols or ciphers irrespective of ``hardened-mode`` or above disabling options you can use ``tls-enabled-protocols`` and ``tls-enabled-ciphers`` - those two options take arrays as well and they will configure Tigase to use only those protocols or ciphers that are provided (without support for regular expressions). Therefore if you configure Tigase with ``'tls-enabled-protocols' = [ 'TLSv1.2' ]`` then **only** ``TLSv1.2`` will be supported by Tigase.\r\n\r\nThe last option that you may be interested in adjusting is ``ephemeral-key-size`` - it follows Java’s configuration capabilities outlined in `Customizing Size of Ephemeral Diffie-Hellman Keys <https://docs.oracle.com/en/java/javase/11/security/java-secure-socket-extension-jsse-reference-guide.html#GUID-D9B216E8-3EFC-4882-B76E-17A87D8F2F9D>`__. Tigase defaults Diffie-Hellman keys of 4096 bits.\r\n\r\n.. Important::\r\n\r\n   We try to provide the best default set of options therefore **it’s recommendable to use defaults provided by Tigase**. If you want to make your extremely secure (considering possible connectivity issues with installations that may be less secure) then you should only adjust ``hardened-mode`` setting (and switch it to ``strict``).\r\n\r\nTesting hosts TLS capabilities\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIf you run into issues with TLS connectivity it’s helpful to compare if both installations support same set of protocols and ciphers. One of the most versatile and helpful tools is `Mozilla’s CipherScan <https://github.com/mozilla/cipherscan>`__. For example for our installation ``tigase.im`` result would look like this:\r\n\r\n::\r\n\r\n   $ ./cipherscan --curves -starttls xmpp -servername tigase.im tigase.me:5222\r\n   .......................................................................\r\n   Target: tigase.me:5222\r\n\r\n   prio  ciphersuite                  protocols              pfs                 curves\r\n   1     ECDHE-RSA-AES256-GCM-SHA384  TLSv1.2                ECDH,B-571,570bits  sect283k1,sect283r1,sect409k1,sect409r1,sect571k1,sect571r1,secp256k1,prime256v1,secp384r1,secp521r1\r\n   2     ECDHE-RSA-AES256-SHA384      TLSv1.2                ECDH,B-571,570bits  sect283k1,sect283r1,sect409k1,sect409r1,sect571k1,sect571r1,secp256k1,prime256v1,secp384r1,secp521r1\r\n   3     ECDHE-RSA-AES256-SHA         TLSv1,TLSv1.1,TLSv1.2  ECDH,B-571,570bits  sect283k1,sect283r1,sect409k1,sect409r1,sect571k1,sect571r1,secp256k1,prime256v1,secp384r1,secp521r1\r\n   4     DHE-RSA-AES256-GCM-SHA384    TLSv1.2                DH,4096bits         None\r\n   5     DHE-RSA-AES256-SHA256        TLSv1.2                DH,4096bits         None\r\n   6     DHE-RSA-AES256-SHA           TLSv1,TLSv1.1,TLSv1.2  DH,4096bits         None\r\n   7     ECDHE-RSA-AES128-GCM-SHA256  TLSv1.2                ECDH,B-571,570bits  sect283k1,sect283r1,sect409k1,sect409r1,sect571k1,sect571r1,secp256k1,prime256v1\r\n   8     ECDHE-RSA-AES128-SHA256      TLSv1.2                ECDH,B-571,570bits  sect283k1,sect283r1,sect409k1,sect409r1,sect571k1,sect571r1,secp256k1,prime256v1,secp384r1,secp521r1\r\n   9     ECDHE-RSA-AES128-SHA         TLSv1,TLSv1.1,TLSv1.2  ECDH,B-571,570bits  sect283k1,sect283r1,sect409k1,sect409r1,sect571k1,sect571r1,secp256k1,prime256v1,secp384r1,secp521r1\r\n   10    DHE-RSA-AES128-GCM-SHA256    TLSv1.2                DH,4096bits         None\r\n   11    DHE-RSA-AES128-SHA256        TLSv1.2                DH,4096bits         None\r\n   12    DHE-RSA-AES128-SHA           TLSv1,TLSv1.1,TLSv1.2  DH,4096bits         None\r\n\r\n   Certificate: trusted, 2048 bits, sha256WithRSAEncryption signature\r\n   TLS ticket lifetime hint: None\r\n   NPN protocols: None\r\n   OCSP stapling: not supported\r\n   Cipher ordering: client\r\n   Curves ordering: client - fallback: no\r\n   Server supports secure renegotiation\r\n   Server supported compression methods: NONE\r\n   TLS Tolerance: yes\r\n\r\nTLS 1.3 compatibility\r\n^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nDue to compatibility issues, TLS 1.3 is currently (version 8.1.x) disabled by default. It can be enabled by setting property ``tls-disable-tls13`` of ``sslContextContainer`` bean to ``false``:\r\n\r\n::\r\n\r\n   sslContextContainer () {\r\n       'tls-disable-tls13' = false\r\n   }\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Security/XEP-0191_Blocking_Command.inc",
    "content": "XEP-0191: Blocking Command\r\n-------------------------------\r\n\r\nThe simplest security feature, however, inside an XMPP server is the ability to block users and JIDS. `XEP-0191 <http://xmpp.org/extensions/xep-0191>`__ specifies the parameters of simple blocking without using privacy lists. Below is a breakdown and some sample commands you may find helpful. To enable this feature, be sure the following is in your ``config.tdsl`` file:\r\n\r\n::\r\n\r\n   }\r\n   'sess-man' {\r\n       'urn:xmpp:blocking' () {}\r\n   }\r\n\r\nIf you have other plugins running, then just add ``'urn:xmpp:blocking' () {}`` to the list to activate this feature.\r\n\r\nTo confirm if your installation of Tigase supports this feature, a quick disco#info of your server should reveal the following feature:\r\n\r\n::\r\n\r\n   <feature var='urn:xmpp:blocking'/>\r\n\r\nBlocked users are stored on the server on a per-JID basis, so one user may only see his or her blocked JIDs. Lists of blocked JIDs will return as an IQ stanza with a list of <item> fields. To retrieve the blocklist, the following command is issued:\r\n\r\n.. code:: xml\r\n\r\n   <iq type='get' id='blockedjids'>\r\n     <blocklist xmlns='urn:xmpp:blocking'/>\r\n   </iq>\r\n\r\nThe server responds:\r\n\r\n.. code:: xml\r\n\r\n   <iq type='result' id='blockedjids'>\r\n     <blocklist xmlns='urn:xmpp:blocking'>\r\n       <item jid='user1@domain.net'/>\r\n       <item jid='admin@example.com'/>\r\n     </blocklist>\r\n   </iq>\r\n\r\nTo block a JID, a similar stanza to the one above is sent to the server with the items of the blocked JIDs you wish to add:\r\n\r\n.. code:: xml\r\n\r\n   <iq from='admin@domain.net' type='set' id='block'>\r\n     <block xmlns='urn:xmpp:blocking'>\r\n       <item jid='user2@domain.net'/>\r\n     </block>\r\n   </iq>\r\n\r\nThe server will then push an unavailable presence to blocked contacts. Communication between a contact that is blocked, and an entity that blocked it will result in a <not-acceptable> error:\r\n\r\n.. code:: xml\r\n\r\n   <message type='error' from='user2@domain.net' to='admin@domain.net'>\r\n     <body>Hello, are you online?</body>\r\n     <error type='cancel'>\r\n       <not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>\r\n       <blocked xmlns='urn:xmpp:blocking:errors'/>\r\n     </error>\r\n   </message>\r\n\r\nUnblocking a contact is just as easy as blocking, send an unblock stanza to the server:\r\n\r\n.. code:: xml\r\n\r\n   <iq from='admin@domain.net' type='set' id='unblock'>\r\n     <unblock xmlns='urn:xmpp:blocking'>\r\n       <item jid='user2@domain.net'/>\r\n     </unblock>\r\n   </iq>\r\n\r\nThe server will begin pushing presence information to unblocked contacts and resources so long as permissions have not changed between.\r\n\r\nYou may also opt to unblock all contacts and essentially clear out your blocked list using the following command:\r\n\r\n.. code:: xml\r\n\r\n   <iq type='set' id='unblockall'>\r\n     <unblock xmlns='urn:xmpp:blocking'/>\r\n   </iq>\r\n\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Security/_Security.rst",
    "content": "Security\r\n========\r\n\r\nThe articles here cover advanced security features built into to Tigase Server, and some options for adding your own levels of security.\r\n\r\n.. include:: XEP-0191_Blocking_Command.inc\r\n.. include:: Account_Registration_Limits.inc\r\n.. include:: Brute-force_attack_prevention.inc\r\n.. include:: RealTimeBlockList.inc\r\n.. include:: Server_Certificates.inc\r\n.. include:: Auth_Connectors.inc\r\n.. include:: SASL_Mechanisms.inc\r\n.. include:: Application_Passwords.inc\r\n.. include:: Packet_Filtering.inc\r\n.. include:: Access_Control_List.inc\r\n.. include:: TLS_Features_Configuration.inc"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Statistics_Description.rst",
    "content": ".. _statsticsDescription:\r\n\r\nAppendix I - Statistics description\r\n==========================================\r\n\r\nStatistics are divided between data sources, components and processors. You may see the same statistics collected for multiple components which are defined in common components section. Note that statistics are defined by {$component}/statistic so if you wanted Max queue size on pubsub, you would look for pubsub/Max queue size. Statistics will not be provided by components that are not enabled.\r\n\r\nData source statistics\r\n-----------------------------\r\n\r\nData sources used to access data storages such as JDBC (databases) or MongoDB provide statistics related to stability of connections to data storage and number of open connections.\r\n\r\n+---------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+------------------------------------------------------+\r\n| Statistics Name                                         | Description                                                                                                                                                                                 | Statistics Level | Format  | List of Possible Statistics                          |\r\n+=========================================================+=============================================================================================================================================================================================+==================+=========+======================================================+\r\n| Number of active data sources                           | Number of defined and active data sources (i.e. connection pools). This is not a number of connections to data sources as it varies and is listed separately for every defined data source. | FINE             | Integer | ``dataSource/Number of data sources``                |\r\n+---------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+------------------------------------------------------+\r\n| Number of connections for ``{dataSourceName}``          | Number of connections for defined data source.                                                                                                                                              | FINE             | String  | ``dataSource/{dataSourceName}/uri``                  |\r\n+---------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+------------------------------------------------------+\r\n| Number of failed reconnections for ``{dataSourceName}`` | Number of reconnections that has failed since start to the defined data source.                                                                                                             | FINE             | Integer | ``dataSource/{dataSourceName}/failed reconnections`` |\r\n+---------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+------------------------------------------------------+\r\n| Number of reconnections for ``{dataSourceName}``        | Number of reconnections for defined data source.                                                                                                                                            | FINE             | Integer | ``dataSource/{dataSourceName}/reconnections``        |\r\n+---------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+------------------------------------------------------+\r\n| URI of ``{dataSourceName}``                             | Returns URI of defined data source.                                                                                                                                                         | FINE             | String  | ``dataSource/{dataSourceName}/uri``                  |\r\n+---------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+------------------------------------------------------+\r\n\r\nUser repository statistics of {repo}\r\n------------------------------------------\r\n\r\nFor every {method} declared in ``UserRepository`` we gather execution statistics. This statistics are collected separately for every data source for which user repository is defined.\r\n\r\n+-------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------+\r\n| Statistics Name                                       | Description                                                                                                                                                                               | Statistics Level | Format  | List of Possible Statistics                                    |\r\n+=======================================================+===========================================================================================================================================================================================+==================+=========+================================================================+\r\n| Average processing time of {method}                   | Average time taken by call of {method} for this data source since creation of data source (most likely from server startup). It includes time taken by calls which thrown exception, etc. | FINE             | Integer | ``userRepository/{repo}/{method}/Average processing time``     |\r\n+-------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------+\r\n| Number of exceptions of a {method}                    | Number of exceptions the specified method has caused                                                                                                                                      | FINE             | Integer | ``userRepository/{repo}/{method}/Exceptions during execution`` |\r\n+-------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------+\r\n| Number of exceptions of a {method} in last {interval} | Number of exceptions the specified method has caused within the specified interval                                                                                                        | FINEST           | Integer | ``userRepository/{repo}/{method}/Executions last {interval}``  |\r\n+-------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------+\r\n| Number of executions of a {method}                    | Number of times specified method has been executed                                                                                                                                        | FINE             | Integer | ``userRepository/{repo}/{method}/Executions``                  |\r\n+-------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------+\r\n\r\nAuth repository statistics of {repo}\r\n-------------------------------------------\r\n\r\nFor every {method} declared in ``AuthRepository`` we gather execution statistics. This statistics are collected separately for every data source for which authentication repository is defined.\r\n\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------+\r\n| Statistics Name                                     | Description                                                                     | Statistics Level | Format  | List of Possible Statistics                                    |\r\n+=====================================================+=================================================================================+==================+=========+================================================================+\r\n| Average processing time of {method}                 | Average time it takes to process {method}.                                      | FINE             | Integer | ``authRepository/{repo}/{method}/Average processing time``     |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------+\r\n| Number of exceptions of {method}                    | Number of times {method} has caused an exception.                               | FINE             | Integer | ``authRepository/{repo}/{method}/Exceptions during execution`` |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------+\r\n| Number of exceptions of {method} in last {interval} | Number of times {method} has caused an exception within the specified interval. | FINEST           | Integer | ``authRepository/{repo}/{method}/Executions last {interval}``  |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------+\r\n| Number of executions of {method}                    | Number of times {method} has been executed.                                     | FINE             | Integer | ``authRepository/{repo}/{method}/Executions``                  |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------+\r\n\r\nStatistics common to custom {compname} component repositories\r\n-----------------------------------------------------------------\r\n\r\nThese statistics may be found in many components which are using repository implementations created just for them. An example of such components may be:\r\n\r\n**amp**\r\n   with msgBroadcastRepository as {repo} name,\r\n\r\n**message-archive**\r\n   with repositoryPool as a {repo} name,\r\n\r\n**muc**\r\n   with muc-dao as a {repo} name,\r\n\r\n**pubsub**\r\n   with dao as a {repo} name,\r\n\r\n**sess-man**\r\n   with msgRepository as a {repo} name\r\n\r\nFor custom component repositories we gather statistics in a same way as we do for user and authorization repositories. Statistics are collected on per {method} basis separately for every data source ({dataSourceName}) for which repository is defined.\r\n\r\n+-------------------------------------+---------------------------------------------+------------------+---------+-----------------------------------------------------------------------------+\r\n| Statistics Name                     | Description                                 | Statistics Level | Format  | List of Possible Statistics                                                 |\r\n+=====================================+=============================================+==================+=========+=============================================================================+\r\n| Average processing time of {method} | Average time it takes to process {method}.  | FINE             | Integer | ``{compname}/{repo}/{dataSourceName}/{method}/Average processing time``     |\r\n+-------------------------------------+---------------------------------------------+------------------+---------+-----------------------------------------------------------------------------+\r\n| Number of exceptions of a {method}  | Number of exceptions {method} has caused.   | FINE             | Integer | ``{compname}/{repo}/{dataSourceName}/{method}/Exceptions during execution`` |\r\n+-------------------------------------+---------------------------------------------+------------------+---------+-----------------------------------------------------------------------------+\r\n| Number of executions of a {method}  | Number of times {method} has been executed. | FINE             | Integer | ``{compname}/{repo}/{dataSourceName}/{method}/Executions``                  |\r\n+-------------------------------------+---------------------------------------------+------------------+---------+-----------------------------------------------------------------------------+\r\n\r\nStatistics common to components\r\n---------------------------------------------\r\n\r\nThese statistics may be found in multiple components and may be seen multiple times. For example both s2s and c2s will have Bytes received statistic, so each can be found the following way:\r\n\r\n.. code::\r\n\r\n   s2s/Bytes received\r\n   c2s/Bytes received\r\n\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Statistics Name                               | Description                                                                                                                                                                                                                                                                                                                                                                                                            | Statistics Level | Format  | Available {field}                                                                                                                                                                                                          | List of Possible Statistics                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |\r\n+===============================================+========================================================================================================================================================================================================================================================================================================================================================================================================================+==================+=========+============================================================================================================================================================================================================================+===========================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================+\r\n| add-script last {interval}                    | The number of times that ``add-script`` adhoc command has been run within the last interval.                                                                                                                                                                                                                                                                                                                           | FINEST           | Integer | hour minute second                                                                                                                                                                                                         | ``{compname}/adhoc-command/add-script last hour`` ``{compname}/adhoc-command/add-script last minute`` ``{compname}/adhoc-command/add-script last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| add-script/Average processing time            | The average processing time ``add-script`` takes to complete.                                                                                                                                                                                                                                                                                                                                                          | FINEST           | Integer |                                                                                                                                                                                                                            | ``add-script/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Average processing time on last 100 runs [ms] | The average processing time in milliseconds for all commands and scripts for this component over the last 100 times component is called. This number will populate with less than 100 runs, and will continue averaging until 100 runs happens, at that point, it’s the most recent 100 instances. This statistic will reset every time the server shuts down or restarts.                                             | FINEST           | Integer |                                                                                                                                                                                                                            | ``{compname}/Average processing time on last 100 runs [ms]``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Bytes received                                | The total number of bytes that the component has received during the current server instance. This statistic resets at server shutdown or restart.                                                                                                                                                                                                                                                                     | FINE or FINEST   | Integer |                                                                                                                                                                                                                            | ``{compname}/Bytes received``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Bytes sent                                    | The total number of bytes that the component has sent during the current server instance. This statistic resets at server shutdown or restart.                                                                                                                                                                                                                                                                         | FINE or FINEST   | Integer |                                                                                                                                                                                                                            | ``{compname}/Bytes sent``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| del-script last {interval}                    | The number of times that ``del-script`` adhoc command has been run within the last interval.                                                                                                                                                                                                                                                                                                                           | FINEST           | Integer | hour minute second                                                                                                                                                                                                         | ``{compname}/adhoc-command/del-script last hour`` ``{compname}/adhoc-command/del-script last minute`` ``{compname}/adhoc-command/del-script last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| del-script Average processing time            | The average time in ms, returned as an integer, it takes for ``del-script`` to execute.                                                                                                                                                                                                                                                                                                                                | FINEST           | Integer |                                                                                                                                                                                                                            | ``{compname}/adhoc-command/del-script/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Last {interval} packets                       | The number of packets that have been handled by this component in the last interval.                                                                                                                                                                                                                                                                                                                                   | FINEST           | Integer | hour minute second                                                                                                                                                                                                         | ``{compname}/last hour packets`` ``{compname}/last minute packets`` ``{compname}/last second packets``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| List-commands last {interval}                 | The number of ``list-commands`` requests sent to the component in the last interval.                                                                                                                                                                                                                                                                                                                                   | FINEST           | Integer | hour minute second                                                                                                                                                                                                         | ``{compname}/list-commands last hour`` ``{compname}/list-commands last minute`` ``{compname}/list-commands last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| List-commands Average processing time         | The average time in ms, returned as an integer, it takes for ``list-commands`` to execute on this component.                                                                                                                                                                                                                                                                                                           | FINEST           | Integer |                                                                                                                                                                                                                            | ``{compname}/list-commands/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| {IN/OUT/Total} queue overflow                 | The number of times the in or out queue has overflown for this component. That is there are more packets queues than the max queue size. A total statistic is also available that combines both results.                                                                                                                                                                                                               | FINEST           | Integer |                                                                                                                                                                                                                            | ``{compname}/IN queue overflow`` ``{compname}/OUT queue overflow`` ``{compname}/Total queue overflow``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| {in/out} queue wait: {priority}               | The number of packets with {priority} priority currently in the incoming or outgoing queue.                                                                                                                                                                                                                                                                                                                            | FINEST           | Integer | SYSTEM CLUSTER HIGH NORMAL LOW PRESENCE LOWEST                                                                                                                                                                             | ``{compname}/In queue wait: SYSTEM`` ``{compname}/In queue wait: CLUSTER`` ``{compname}/In queue wait: HIGH`` ``{compname}/In queue wait: NORMAL`` ``{compname}/In queue wait: LOW`` ``{compname}/In queue wait: PRESENCE`` ``{compname}/In queue wait: LOWEST`` ``{compname}/Out queue wait: SYSTEM`` ``{compname}/Out queue wait: CLUSTER`` ``{compname}/Out queue wait: HIGH`` ``{compname}/Out queue wait: NORMAL`` ``{compname}/Out queue wait: LOW`` ``{compname}/Out queue wait: PRESENCE`` ``{compname}/Out queue wait: LOWEST``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| {IN/OUT}_QUEUE processed {type}               | The number of stanzas of different types that have been processed VIA the In or Out Queue of this component. This number will reset at the end of the server instance. Each component will have a list of the different types of stanzas it can process.                                                                                                                                                               | FINER            | Integer | # messages presences cluster other IQ no XMLNS IQ http://jabber.org/protocol/disco#items IQ bind IQ jabber:iq:roster IQ session IQ vCard IQ command IQ jabber:iq:private IQ http://jabber.org/protocol/disco#info total IQ | ``{compname}/IN_QUEUE processed`` ``{compname}/IN_QUEUE processed messages`` ``{compname}/IN_QUEUE processed presences`` ``{compname}/IN_QUEUE processed cluster`` ``{compname}/IN_QUEUE processed other`` ``{compname}/IN_QUEUE processed IQ no XMLNS`` ``{compname}/IN_QUEUE processed IQ http://jabber.org/protocol/disco#items`` ``{compname}/IN_QUEUE processed IQ http://jabber.org/protocol/disco#info`` ``{compname}/IN_QUEUE processed IQ bind`` ``{compname}/IN_QUEUE processed IQ jabber:iq:roster`` ``{compname}/IN_QUEUE processed IQ jabber:iq:private`` ``{compname}/IN_QUEUE processed IQ session`` ``{compname}/IN_QUEUE processed IQ vCard`` ``{compname}/IN_QUEUE processed IQ command`` ``{compname}/IN_QUEUE processed total IQ`` ``{compname}/OUT_QUEUE processed messages`` ``{compname}/OUT_QUEUE processed presences`` ``{compname}/OUT_QUEUE processed cluster`` ``{compname}/OUT_QUEUE processed other`` ``{compname}/OUT_QUEUE processed IQ no XMLNS`` ``{compname}/OUT_QUEUE processed IQ http://jabber.org/protocol/disco#items`` ``{compname}/OUT_QUEUE processed IQ http://jabber.org/protocol/disco#info`` ``{compname}/OUT_QUEUE processed IQ bind`` ``{compname}/OUT_QUEUE processed IQ jabber:iq:roster`` ``{compname}/OUT_QUEUE processed IQ jabber:iq:private`` ``{compname}/OUT_QUEUE processed IQ session`` ``{compname}/OUT_QUEUE processed IQ vCard`` ``{compname}/OUT_QUEUE processed IQ command`` ``{compname}/OUT_QUEUE processed total IQ`` |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n|                                               | NOTE: Several statistics are only available from statistics component, shutdown thread will ONLY print the following: messages, presences, cluster, other, IQ no XLMNS, total IQ.                                                                                                                                                                                                                                      |                  |         |                                                                                                                                                                                                                            |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| max queue size                                | The maximum number of items allowed in the packet queue for this component.                                                                                                                                                                                                                                                                                                                                            | FINEST           | Integer |                                                                                                                                                                                                                            | ``{compname}/max queue size``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Open Connections                              | The number of open connections to the component.                                                                                                                                                                                                                                                                                                                                                                       | INFO/FINEST      | Integer |                                                                                                                                                                                                                            | ``{compname}/Open connections``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Packets received                              | The total number of packets received by the component from external sources in the current instance. This number resets at server shutdown or restart.                                                                                                                                                                                                                                                                 | FINE             | Integer |                                                                                                                                                                                                                            | ``{compname}/Packets received``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Packets sent                                  | The total number of packets sent by the component in the current instance. This number resets at server shutdown or restart.                                                                                                                                                                                                                                                                                           | FINE             | Integer |                                                                                                                                                                                                                            | ``{compname}/Packets sent``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Processed packets thread: {in/out}            | How many packets have been processed in and out by each processing thread.                                                                                                                                                                                                                                                                                                                                             | FINEST           | Integer |                                                                                                                                                                                                                            | ``{compname}/Processed packets thread: IN`` ``{compname}/Processed packets thread: OUT`` ``{compname}/Processed packets thread (outliers) IN`` ``{compname}/Processed packets thread (outliers) OUT``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n|                                               | | Statistics will provide an array for each processor, listed from 0, 1, 2, 3 etc.. Let’s say that we have 4 threads set for ws2s, a list will be seen like this:                                                                                                                                                                                                                                                      |                  |         |                                                                                                                                                                                                                            |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\r\n|                                               | | ``ws2s/Processed packets thread: IN=[2, 6, 4, 2]``                                                                                                                                                                                                                                                                                                                                                                   |                  |         |                                                                                                                                                                                                                            |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\r\n|                                               | | ``ws2s/Processed packets thread: OUT=[8, 0, 1, 3]``                                                                                                                                                                                                                                                                                                                                                                  |                  |         |                                                                                                                                                                                                                            |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\r\n|                                               | | ``ws2s/Processed packets thread (outliers) IN=mean: 79.0, deviation: 441, outliers: [in_10-ws2s: 2359]``                                                                                                                                                                                                                                                                                                             |                  |         |                                                                                                                                                                                                                            |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\r\n|                                               | | ``ws2s/Processed packets thread (outliers) OUT=mean: 16.5, deviation: 23.2058941, outliers: [out_ws2s: 80]``                                                                                                                                                                                                                                                                                                         |                  |         |                                                                                                                                                                                                                            |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\r\n|                                               | | Note that the processor arrray will only have as many threads as the component has as defined in {compname}/Processing threads.                                                                                                                                                                                                                                                                                      |                  |         |                                                                                                                                                                                                                            |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| processing threads                            | The number of threads provided for the particular component.                                                                                                                                                                                                                                                                                                                                                           | FINER            | Integer |                                                                                                                                                                                                                            | ``{compname}/processing threads``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| stream-error-counter                          |The number of errors counted during the operation of the server for this component. Will only be available if :ref:`stream-error-counter<stream-error-counter>` is enabled in config.tdsl, otherwise will be 0.                                                                                                                                                                                                         | FINE             | Integer |                                                                                                                                                                                                                            | ``{compname}/processors/stream-error-counter``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Socket overflow                               | The number of times that this component has experienced socket queue overflow and had to drop packets. The XMPP server queues packets which are being sent over connection if receiver is not able to read them fast enough or if the network connection too slow to the amount of data which needs to be sent. If the queue will over flow that will be counted. This does not include the number of dropped packets. | FINEST           | Integer |                                                                                                                                                                                                                            | ``{compname}/Socket overflow``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Total {in/out} queues wait                    | The number of packets in the inbound or outbound queue that are currently waiting to be sent. This includes packets of all types. This is an instant statistics, in that the number in queue is only as many in the queue the moment statistics are gathered.                                                                                                                                                          | FINEST           | Integer |                                                                                                                                                                                                                            | ``{compname}/Total in queues wait`` ``{compname}/Total out queues wait``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Total queue wait                              | A combined total of ``Total in queue wait`` and ``Total out queue wait`` statistics for this component.                                                                                                                                                                                                                                                                                                                | FINEST           | Integer |                                                                                                                                                                                                                            | ``{compname}/Total queue wait``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Total queues wait                             | A combined total of all component queue wait statistics.                                                                                                                                                                                                                                                                                                                                                               | FINEST           | Integer |                                                                                                                                                                                                                            | ``Total queues wait``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Total queues overflow                         | The number of times the component packet wait queue has overflown and had to drop packets. This statistic does not keep track of the number of dropped packets.                                                                                                                                                                                                                                                        | FINEST           | Integer |                                                                                                                                                                                                                            | ``{compname}/Total queues overflow``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Total/Total queues overflow                   | The combined total of all queue overflow statistics for all components.                                                                                                                                                                                                                                                                                                                                                | FINEST           | Integer |                                                                                                                                                                                                                            | ``total/Total queues overflow``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Waiting to send                               | The number of packets in the component’s queue that are waiting to be sent. This number will usually be 0 however it will grow if a large number of packets are jamming up your system, or your queue sizes are set too low.                                                                                                                                                                                           | FINEST           | Integer |                                                                                                                                                                                                                            | ``{compname}/Waiting to send``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Watchdog runs                                 | The number of times watchdog has been run on this component to check for stale connections.                                                                                                                                                                                                                                                                                                                            | FINER            | Integer |                                                                                                                                                                                                                            | ``{compname}/Watchdog runs``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Watchdog stopped                              |The number of times watchdog identified and closed a connection it has found to be stale according to the settings in ``config.tdsl`` or by the defaults defined :ref:`in this section<watchdog>`.                                                                                                                                                                                                                      | FINER            | Integer |                                                                                                                                                                                                                            | ``{compname}/Watchdog stopped``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Watchdog tests                                | The number of times watchdog has found a potential stale connection and has conducted a test to determine whether or not to close the connection. This is per component in the current server instance.                                                                                                                                                                                                                | FINER            | Integer |                                                                                                                                                                                                                            | ``{compname}/Watchdog tests``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |\r\n+-----------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n\r\nComponent statistics\r\n------------------------\r\n\r\nAMP\r\n^^^^^\r\n\r\nNo exclusive amp specific statistics\r\n\r\nbosh\r\n^^^^^\r\n\r\n+-------------------------------------------+--------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Statistics Name                           | Description                                                                                            | Statistics Level | Format  | Available {field}  | List of Possible Statistics                                                                                                                               |\r\n+-------------------------------------------+--------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Bosh sessions                             | The number of currently open and running BOSH sessions to the server.                                  | FINEST           | Integer |                    | ``bosh/Bosh sessions``                                                                                                                                    |\r\n+-------------------------------------------+--------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| pre-bind session last {interval}          | The number of times the pre-bind-session command has been executed within the last specified interval. | FINEST           | Integer | hour minute second | ``bosh/adhoc-command/pre-bind-session last hour`` ``bosh/adhoc-command/pre-bind-session last minute`` ``bosh/adhoc-command/pre-bind-session last second`` |\r\n+-------------------------------------------+--------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| pre-bind-sessions/Average processing time | The average time in ms, returned as an integer, it takes for ``pre-bind-session`` to execute.          | FINEST           | Integer |                    | ``bosh/adhoc-command/pre-bind-session/Average processing time``                                                                                           |\r\n+-------------------------------------------+--------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n\r\nc2s\r\n^^^^\r\n\r\nNo exclusive c2s specific statistics.\r\n\r\ncl-comp\r\n^^^^^^^^^^\r\n\r\n+----------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+-----------------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Statistics Name                                          | Description                                                                                                                                                           | Statistics Level | Format                | Available {field}  | List of Possible Statistics                                                                                                                                              |\r\n+----------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+-----------------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/cluster-nodes-list last {interval}         | The number of times per interval that the cluster-nodes-list command has been executed.                                                                               | FINEST           | Integer               | hour minute second | ``cl-comp/adhoc-command/cluster-nodes-list last hour`` ``cl-comp/adhoc-command/cluster-nodes-list last minute`` ``cl-comp/adhoc-command/cluster-nodes-list last second`` |\r\n+----------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+-----------------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/cluster-nodes-list/Average processing time | The average time in ms, returned as an integer, it takes for ``cluster-nodes-list`` to execute.                                                                       | FINEST           | Integer               |                    | ``cl-comp/adhoc-command/cluster-nodes-list/Average processing time``                                                                                                     |\r\n+----------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+-----------------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/force-stop-service last {interval}         | The number of times per interval that the force-stop-service command has been executed.                                                                               | FINEST           | Integer               | hour minute second | ``cl-comp/adhoc-command/force-stop-service last hour`` ``cl-comp/adhoc-command/force-stop-service last minute`` ``cl-comp/adhoc-command/force-stop-service last second`` |\r\n+----------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+-----------------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Adhoc-command/force-stop-service/Average processing time | The average time in ms, returned as an integer, it takes for ``force-stop-service`` to execute.                                                                       | FINEST           | Integer               |                    | ``cl-comp/adhoc-command/force-stop-service/Average processing time``                                                                                                     |\r\n+----------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+-----------------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/service-keys last {interval}               | The number of times per interval that the ``service-keys`` command has been executed.                                                                                 | FINEST           | Integer               | hour minute second | ``cl-comp/adhoc-command/service-keys last hour`` ``cl-comp/adhoc-command/service-keys last minute`` ``cl-comp/adhoc-command/service-keys last second``                   |\r\n+----------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+-----------------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Adhoc-command/service-keys/Average processing time       | The average time in ms, returned as an integer, it takes for ``service-keys`` to execute.                                                                             | FINEST           | Integer               |                    | ``cl-comp/adhoc-command/service-keys/Average processing time``                                                                                                           |\r\n+----------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+-----------------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/sim-serv-stopped {interval}                | The number of times per interval that the ``sim-serv-stopped`` command has been executed.                                                                             | FINEST           | Integer               | hour minute second | ``cl-comp/adhoc-command/sim-serv-stopped last hour`` ``cl-comp/adhoc-command/sim-serv-stopped last minute`` ``cl-comp/adhoc-command/sim-serv-stopped last second``       |\r\n+----------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+-----------------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Adhoc-command/sim-serv-stopped/Average processing time   | The average time in ms, returned as an integer, it takes for ``sim-serv-stopped`` to execute.                                                                         | FINEST           | Integer               |                    | ``cl-comp/adhoc-command/sim-serv-stopped/Average processing time``                                                                                                       |\r\n+----------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+-----------------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Average compression ratio                                | The average compression ratio of data sent to other clusters during the session.                                                                                      | FINE             | Float                 |                    | ``cl-comp/Average compression ratio``                                                                                                                                    |\r\n+----------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+-----------------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Average decompression ratio                              | The average compression ratio of data received from other clusters during the session.                                                                                | FINE             | Float                 |                    | ``cl-comp/Average decompression ratio``                                                                                                                                  |\r\n+----------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+-----------------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Known cluster nodes                                      | The number of cluster nodes currently connected to the server.                                                                                                        | INFO             | Integer               |                    | ``cl-comp/Known cluster nodes``                                                                                                                                          |\r\n+----------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+-----------------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Last {interval} disconnects                              | The number of cluster disconnections within the specified interval.                                                                                                   | FINE             | Comma Separated Array | day hour           | ``cl-comp/Last day disconnects`` ``cl-comp/Last hour disconnects``                                                                                                       |\r\n+----------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+-----------------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n|                                                          | For day, each array is the number of disconnections each hour, most recent first. For hour each array is the number of disconnections each minute, most recent first. |                  |                       |                    |                                                                                                                                                                          |\r\n+----------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+-----------------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Service connected time-outs                              | The number of time-outs during connection initialization of cluster nodes.                                                                                            | FINEST           | Integer               |                    | ``cl-comp/Service connected time-outs``                                                                                                                                  |\r\n+----------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+-----------------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Total disconnects                                        | The number of clusters that have disconnected during the current session.                                                                                             | FINEST           | Integer               |                    | ``cl-comp/Total disconnects``                                                                                                                                            |\r\n+----------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+-----------------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n\r\neventbus\r\n^^^^^^^^^^^^\r\n\r\nNo exclusive eventbus specific statistics.\r\n\r\nmessage-archive\r\n^^^^^^^^^^^^^^^^^^^\r\n\r\n+----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+-------------------+------------------------------------------------------------+\r\n| Statistics Name                        | Description                                                                                                                                                 | Statistics Level | Format  | Available {field} | List of Possible Statistics                                |\r\n+----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+-------------------+------------------------------------------------------------+\r\n| Removal time of expired messages (avg) | The average amount of time in milliseconds it takes to remove expired messages from the repository. This includes manual and automatic removal of messages. | FINE             | Integer |                   | ``message-archive/Removal time of expired messages (avg)`` |\r\n+----------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+-------------------+------------------------------------------------------------+\r\n\r\nmessage-router\r\n^^^^^^^^^^^^^^^^^\r\n\r\n+-------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+--------------+-------------------+---------------------------------------------------------------+\r\n| Statistics Name   | Description                                                                                                                                                                                      | Statistics Level | Format       | Available {field} | List of Possible Statistics                                   |\r\n+-------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+--------------+-------------------+---------------------------------------------------------------+\r\n| CPUs no           | The number of CPUs available on the host machine.                                                                                                                                                | FINEST           | Integer      |                   | ``message-router/CPUs no``                                    |\r\n+-------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+--------------+-------------------+---------------------------------------------------------------+\r\n| CPU Usage         | % of available CPU power used by Tigase Server at the moment statistics are taken.                                                                                                               | FINE             | Float/String |                   | ``message-router/CPU usage [%]`` ``message-router/CPU usage`` |\r\n+-------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+--------------+-------------------+---------------------------------------------------------------+\r\n|                   | Two formats are available for CPU usage: A float integer which expresses a long decimal available from CPU Usage [%], and a string which provides a rounded number with a % sign from CPU usage. |                  |              |                   |                                                               |\r\n+-------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+--------------+-------------------+---------------------------------------------------------------+\r\n| Free Heap         | The amount of heap memory that is available for use, expressed in KB.                                                                                                                            | FINE             | String       |                   | ``message-router/Free Heap``                                  |\r\n+-------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+--------------+-------------------+---------------------------------------------------------------+\r\n| Free NonHeap      | The amount of non-heap memory that is available for use, expressed in KB.                                                                                                                        | FINE             | String       |                   | ``message-router/Free NonHeap``                               |\r\n+-------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+--------------+-------------------+---------------------------------------------------------------+\r\n| HEAP usage [%]    | Total percent of HEAP memory in use by Tigase.                                                                                                                                                   | FINE             | Float        |                   | ``message-router/HEAP usage [%]``                             |\r\n+-------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+--------------+-------------------+---------------------------------------------------------------+\r\n| Local hostname    | The local hostname of the physical server.                                                                                                                                                       | INFO             | String       |                   | ``message-router/Local hostname``                             |\r\n+-------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+--------------+-------------------+---------------------------------------------------------------+\r\n| Load average      | The average system load for the previous minute. The way in which the load average is calculated is operating system specific but is typically a damped time-dependent average.                  | FINE             | Float        |                   | ``message-router/Load average``                               |\r\n+-------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+--------------+-------------------+---------------------------------------------------------------+\r\n| Max Heap mem      | Maximum amount of heap memory available as defined by JAVA_OPTIONS in tigase.conf, in Kb.                                                                                                        | INFO             | String       |                   | ``message-router/Max Heap mem``                               |\r\n+-------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+--------------+-------------------+---------------------------------------------------------------+\r\n| Max NonHeap mem   | Maximum amount of non-heap memory available as defined by JAVA_OPTIONS in tigase.conf, in Kb.                                                                                                    | FINE             | String       |                   | ``message-router/Max NonHeap mem``                            |\r\n+-------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+--------------+-------------------+---------------------------------------------------------------+\r\n| NONHEAP Usage [%] | Total amount of NONHEAP memory in use expressed as a percentage.                                                                                                                                 | FINE             | Float        |                   | ``message-router/NONHEAP usage [%]``                          |\r\n+-------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+--------------+-------------------+---------------------------------------------------------------+\r\n| Threads count     | The total number of processing threads available across all components.                                                                                                                          | FINEST           | Integer      |                   | ``message-router/Threads count``                              |\r\n+-------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+--------------+-------------------+---------------------------------------------------------------+\r\n| Uptime            | The total amount of time the server has been online for this session.                                                                                                                            | INFO             | String       |                   | ``message-router/Uptime``                                     |\r\n+-------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+--------------+-------------------+---------------------------------------------------------------+\r\n| Used Heap         | The amount of heap memory in use in KB.                                                                                                                                                          | INFO             | String       |                   | ``message-router/Used Heap``                                  |\r\n+-------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+--------------+-------------------+---------------------------------------------------------------+\r\n| Used NonHeap      | The amount of non-heap memory in use shown in KB.                                                                                                                                                | FINE             | String       |                   | ``message-router/Used NonHeap``                               |\r\n+-------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+--------------+-------------------+---------------------------------------------------------------+\r\n\r\n\r\nmonitor\r\n^^^^^^^^^^\r\n\r\n+---------------------------------------------------+------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Statistics Name                                   | Description                                                                              | Statistics Level | Format  | Available {field}  | List of Possible Statistics                                                                                                                         |\r\n+---------------------------------------------------+------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/load-errors last {interval}         | The number of times per interval that the load-errors command has been executed.         | FINEST           | Integer | hour minute second | ``monitor/adhoc-command/load-errors last hour`` ``monitor/adhoc-command/load-errors last minute`` ``monitor/adhoc-command/load-errors last second`` |\r\n+---------------------------------------------------+------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Adhoc-command/load-errors/Average processing time | The average time in ms, returned as an integer, it takes for ``load-errors`` to execute. | FINEST           | Integer |                    | ``monitor/adhoc-command/load-errors/Average processing time``                                                                                       |\r\n+---------------------------------------------------+------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------+\r\n\r\nmuc\r\n^^^^\r\n\r\n+-----------------------------------------------------------+--------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Statistics Name                                           | Description                                                                                      | Statistics Level | Format  | Available {field}  | List of Possible Statistics                                                                                                                                     |\r\n+-----------------------------------------------------------+--------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/remove-room last {interval}                 | The number of times per interval that the remove-room command has been executed.                 | FINEST           | Integer | hour minute second | ``monitor/adhoc-command/remove-room last hour`` ``monitor/adhoc-command/remove-room last minute`` ``monitor/adhoc-command/remove-room last second``             |\r\n+-----------------------------------------------------------+--------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Adhoc-command/remove-room/Average processing time         | The average time in ms, returned as an integer, it takes for ``remove-room`` to execute.         | FINEST           | Integer |                    | ``monitor/adhoc-command/remove-room/Average processing time``                                                                                                   |\r\n+-----------------------------------------------------------+--------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/default-room-config last {interval}         | The number of times per interval that the default-room-command command has been executed.        | FINEST           | Integer | hour minute second | ``muc/adhoc-command/default-room-config last hour`` ``muc/adhoc-command/default-room-config last minute`` ``muc/adhoc-command/default-room-config last second`` |\r\n+-----------------------------------------------------------+--------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Adhoc-command/default-room-config/Average processing time | The average time in ms, returned as an integer, it takes for ``default-room-config`` to execute. | FINEST           | Integer |                    | ``muc/adhoc-command/default-room-config/Average processing time``                                                                                               |\r\n+-----------------------------------------------------------+--------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n\r\nproxy\r\n^^^^^^^^^^\r\n\r\n+-----------------------------+--------------------------------------------------------------------------------------+------------------+---------+---------------------------------------+\r\n| Statistics Name             | Description                                                                          | Statistics Level | Format  | List of Possible Statistics           |\r\n+-----------------------------+--------------------------------------------------------------------------------------+------------------+---------+---------------------------------------+\r\n| Average transfer size in KB | Average size of packets sent through the proxy component during the current session. | FINEST           | Integer | ``proxy/Average transfer size in KB`` |\r\n+-----------------------------+--------------------------------------------------------------------------------------+------------------+---------+---------------------------------------+\r\n| KBytes transferred          | Total number of Kb transferred through the proxy component.                          | FINEST           | Integer | ``proxy/KBytes transferred``          |\r\n+-----------------------------+--------------------------------------------------------------------------------------+------------------+---------+---------------------------------------+\r\n| Open streams                | Number of currently open proxy streams.                                              | FINEST           | Integer | ``proxy/Open streams``                |\r\n+-----------------------------+--------------------------------------------------------------------------------------+------------------+---------+---------------------------------------+\r\n| Transfers completed         | Number of specific transfers completed through proxy component.                      | FINEST           | Integer | ``proxy/Transfers completed``         |\r\n+-----------------------------+--------------------------------------------------------------------------------------+------------------+---------+---------------------------------------+\r\n\r\npubsub\r\n^^^^^^^^^^^\r\n\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Statistics Name                                     | Description                                                                                                                                                                                                                                                                                                                                                   | Statistics Level | Format         | Available {field}  | List of Possible Statistics                                                                                                                            |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Added new nodes                                     | The total number of new nodes that has been added in the current server instance. This statistic is reset when the server resets.                                                                                                                                                                                                                             | FINEST           | Integer        |                    | ``pubsub/Added new nodes``                                                                                                                             |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/delete-item last {interval}           | The number of times per interval that the ``delete-item`` command has been executed.                                                                                                                                                                                                                                                                          | FINEST           | Integer        | hour minute second | ``pubsub/adhoc-command/delete-item last hour`` ``pubsub/adhoc-command/delete-item last minute`` ``pubsub/adhoc-command/delete-item last second``       |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/delete-item/Average processing time   | The average time in ms, returned as an integer, it takes for ``delete-item`` to execute.                                                                                                                                                                                                                                                                      | FINEST           | Integer        |                    | ``pubsub/adhoc-command/delete-item/Average processing time``                                                                                           |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/delete-node last {interval}           | The number of times per interval that the ``delete-node`` command has been executed.                                                                                                                                                                                                                                                                          | FINEST           | Integer        | hour minute second | ``pubsub/adhoc-command/delete-node last hour`` ``pubsub/adhoc-command/delete-node last minute`` ``pubsub/adhoc-command/delete-node last second``       |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/delete-node/Average processing time   | The average time in ms, returned as an integer, it takes for ``delete-node`` to execute.                                                                                                                                                                                                                                                                      | FINEST           | Integer        |                    | ``pubsub/adhoc-command/delete-node/Average processing time``                                                                                           |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/list-items last {interval}            | The number of times per interval that the ``list-items`` command has been executed.                                                                                                                                                                                                                                                                           | FINEST           | Integer        |                    | ``pubsub/adhoc-command/list-items last hour`` ``pubsub/adhoc-command/list-items last minute`` ``pubsub/adhoc-command/list-items last second``          |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/list-items/Average processing time    | The average time in ms, returned as an integer, it takes for ``list-items`` to execute.                                                                                                                                                                                                                                                                       | FINEST           | Integer        |                    | ``pubsub/adhoc-command/list-items/Average processing time``                                                                                            |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/list-nodes last {interval}            | The number of times per interval that the ``list-nodes`` command has been executed.                                                                                                                                                                                                                                                                           | FINEST           | Integer        |                    | ``pubsub/adhoc-command/list-nodes last hour`` ``pubsub/adhoc-command/list-nodes last minute`` ``pubsub/adhoc-command/list-nodes last second``          |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/list-nodes/Average processing time    | The average time in ms, returned as an integer, it takes for ``list-nodes`` to execute.                                                                                                                                                                                                                                                                       | FINEST           | Integer        |                    | ``pubsub/adhoc-command/list-nodes/Average processing time``                                                                                            |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/publish-item last {interval}          | The number of times per interval that the ``publish-item`` command has been executed.                                                                                                                                                                                                                                                                         | FINEST           | Integer        |                    | ``pubsub/adhoc-command/publish-item last hour`` ``pubsub/adhoc-command/publish-item last minute`` ``pubsub/adhoc-command/publish-item last second``    |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/publish-item/Average processing time  | The average time in ms, returned as an integer, it takes for ``publish-item`` to execute.                                                                                                                                                                                                                                                                     | FINEST           | Integer        |                    | ``pubsub/adhoc-command/publish-item/Average processing time``                                                                                          |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/retrieve-item last {interval}         | The number of times per interval that the ``retrieve-item`` command has been executed.                                                                                                                                                                                                                                                                        | FINEST           | Integer        | hour minute second | ``pubsub/adhoc-command/retrieve-item last hour`` ``pubsub/adhoc-command/retrieve-item last minute`` ``pubsub/adhoc-command/retrieve-item last second`` |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/retrieve-item/Average processing time | The average time in ms, returned as an integer, it takes for ``retrieve-item`` to execute.                                                                                                                                                                                                                                                                    | FINEST           | Integer        |                    | ``pubsub/adhoc-command/retrieve-item/Average processing time``                                                                                         |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| AdHocConfigCommandModule last {interval}            | The number of times per interval that the ``AdHocConfigCommandModule`` command has been executed.                                                                                                                                                                                                                                                             | FINEST           | Integer        | hour minute second | ``pubsub/AdHocConfigCommandModule last hour`` ``pubsub/AdHocConfigCommandModule last minute`` ``pubsub/AdHocConfigCommandModule last second``          |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| AdHocConfigCommandModule/Average processing time    | The average time in ms, returned as an integer, it takes for ``AdHocConfigCommandModule`` to execute.                                                                                                                                                                                                                                                         | FINEST           | Integer        |                    | ``pubsub/AdHocConfigCommandModule/Average processing time``                                                                                            |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Affiliations count (in cache)                       | The total number of pubsub affiliations that are resident in cache memory. Affiliations include JIDs that are one of the following; Owner, Publisher, Publish-Only, Member, None, Outcast. This may not reflect total pubsub affiliations in repository.                                                                                                      | FINEST           | Integer        |                    | ``pubsub/Affiliations count (in cache)``                                                                                                               |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Average DB write time [ms]                          | The average time of all DB writes from PubSub component. Average is calculated using two other statistics: (Total writing time / Database writes)                                                                                                                                                                                                             | FINEST           | Integer        |                    | ``pubsub/Average DB write time [ms]``                                                                                                                  |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| cache/hits last {interval}                          | The number of times the cache has achieved a hit within the last interval. A hit is when a request for information is matched to data that is inside the cache memory.                                                                                                                                                                                        | FINEST           | Integer        | hour minute second | ``pubsub/cache/hits last hour`` ``pubsub/cache/hits last minute`` ``pubsub/cache/hits last second``                                                    |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| cache/hit-miss ratio per {interval}                 | The ratio of cache hits to cache misses over the specified period. A cache hit is when a request for information from the cache is matched with information in the cache. A miss is when that information request cannot find a match in cache. A miss only indicates that that information was not found in the cache, not that it is not in the repository. | FINE             | Float          | hour minute        | ``pubsub/cache/hit-miss ratio per hour`` ``pubsub/cache/hit-miss ratio per minute``                                                                    |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| cache/requests last {interval}                      | The number of memory cache requests made within the last interval.                                                                                                                                                                                                                                                                                            | FINEST           | Integer        | hour minute second | ``pubsub/cache/Requests last hour`` ``pubsub/cache/Requests last minute`` ``pubsub/cache/Requests last second``                                        |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Cached nodes                                        | The number of nodes that is currently in memory cache.                                                                                                                                                                                                                                                                                                        | FINEST           | Integer        |                    | ``pubsub/Cached nodes``                                                                                                                                |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| CapsModule                                          | The number of times per interval that the CapsModule command has been executed.                                                                                                                                                                                                                                                                               | FINEST           | Integer        | hour minute second | ``pubsub/CapsModule last hour`` ``pubsub/CapsModule last minute`` ``pubsub/CapsModule last second``                                                    |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| CapsModule/Average processing time                  | The average time in ms, returned as an integer, it takes for ``CapsModule`` to execute.                                                                                                                                                                                                                                                                       | FINEST           | Integer        |                    | ``pubsub/CapsModule/Average processing time``                                                                                                          |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| db/GetNodeItems requests last {interval}            | The number of times ``GetNodeItems`` command has been run within the specified interval.                                                                                                                                                                                                                                                                      | FINEST           | Integer        | hour minute second | ``pubsub/db/GetNodeItems last hour`` ``pubsub/db/GetNodeItems last minute`` ``pubsub/db/GetNodeItems last second``                                     |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| db/GetNodeItems/Average processing time             | The average time in ms, returned as an integer, it takes for ``GetNodeItems`` to execute.                                                                                                                                                                                                                                                                     | FINEST           | Integer        |                    | ``pubsub/db/GetNodeItems/Average processing time``                                                                                                     |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| DefaultConfigModule last {interval}                 | The number of times per interval that the ``DefaultConfigModule`` command has been executed.                                                                                                                                                                                                                                                                  | FINEST           | Integer        | hour minute second | ``pubsub/DefaultConfigModule last hour`` ``pubsub/DefaultConfigModule last minute`` ``pubsub/DefaultConfigModule last second``                         |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| DefaultConfigModule/Average processing time         | The average time in ms, returned as an integer, it takes for ``DefaultConfigModule`` to execute.                                                                                                                                                                                                                                                              | FINEST           | Integer        |                    | ``pubsub/DefaultConfigModule/Average processing time``                                                                                                 |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| DiscoverInfoModule last {interval}                  | The number of times per interval that the DiscoverInfoModule command has been executed.                                                                                                                                                                                                                                                                       | FINEST           | Integer        |                    | ``pubsub/DiscoverInfoModule last hour`` ``pubsub/DiscoverInfoModule last minute`` ``pubsub/DiscoverInfoModule last second``                            |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| DiscoverInfoModule/Average processing time          | The average time in ms, returned as an integer, it takes for ``DiscoverInfoModule`` to execute.                                                                                                                                                                                                                                                               | FINEST           | Integer        |                    | ``pubsub/DiscoverInfoModule/Average processing time``                                                                                                  |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| DiscoverItemsModule last {interval}                 | The number of times per interval that the DiscoverItemsModule command has been executed.                                                                                                                                                                                                                                                                      | FINEST           | Integer        |                    | ``pubsub/DiscoverItemsModule last hour`` ``pubsub/DiscoverItemsModule last minute`` ``pubsub/DiscoverItemsModule last second``                         |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| DiscoverItemsModule/Average processing time         | The average time in ms, returned as an integer, it takes for ``DiscoverItemsModule`` to execute.                                                                                                                                                                                                                                                              | FINEST           | Integer        |                    | ``pubsub/DiscoverItemsModule/Average processing time``                                                                                                 |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| JabberVersionModule last {interval}                 | The number of times per interval that the ``JabberVersionModule`` command has been executed.                                                                                                                                                                                                                                                                  | FINEST           | Integer        | hour minute second | ``pubsub/JabberVersionModule last hour`` ``pubsub/JabberVersionModule last minute`` ``pubsub/JabberVersionModule last second``                         |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| JabberVersionModule/Average processing time         | The average time in ms, returned as an integer, it takes for ``JabberVersionModule`` to execute.                                                                                                                                                                                                                                                              | FINEST           | Integer        |                    | ``pubsub/JabberVersionModule/Average processing time``                                                                                                 |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| ManageAffiiationsModule last {interval}             | The number of times per interval that the ``ManageAffiliationsModule`` command has been executed.                                                                                                                                                                                                                                                             | FINEST           | Integer        | hour minute second | ``pubsub/ManageAffiliationsModule last hour`` ``pubsub/ManageAffiliationsModule last minute`` ``pubsub/ManageAffiliationsModule last second``          |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| ManageAffiliationsModule/Average processing time    | The average time in ms, returned as an integer, it takes for ``ManageAffiliationsModule`` to execute.                                                                                                                                                                                                                                                         | FINEST           | Integer        |                    | ``pubsub/ManageAffiliationsModule/Average processing time``                                                                                            |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| ManageSubscriptionModule last {interval}            | The number of times per interval that the ``ManageSubscriptionModule`` command has been executed.                                                                                                                                                                                                                                                             | FINEST           | Integer        | hour minute second | ``pubsub/ManageSubscriptionModule last hour`` ``pubsub/ManageSubscriptionModule last minute`` ``pubsub/ManageSubscriptionModule last second``          |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| ManageSubscriptionModule/Average processing time    | The average time in ms, returned as an integer, it takes for ``ManageSubscriptionModule`` to execute.                                                                                                                                                                                                                                                         | FINEST           | Integer        |                    | ``pubsub/ManageSubscriptionModule/Average processing time``                                                                                            |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| NodeConfigModule last {interval}                    | The number of times per interval that the ``NodeConfigModule`` command has been executed.                                                                                                                                                                                                                                                                     | FINEST           | Integer        | hour minute second | ``pubsub/NodeConfigModule last hour`` ``pubsub/NodeConfigModule last minute`` ``pubsub/NodeConfigModule last second``                                  |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| NodeConfigModule/Average processing time            | The average time in ms, returned as an integer, it takes for ``NodeConfigModule`` to execute.                                                                                                                                                                                                                                                                 | FINEST           | Integer        |                    | ``pubsub/NodeConfigModule/Average processing time``                                                                                                    |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| NodeCreateModule last {interval}                    | The number of times per interval that the ``NodeCreateModule`` command has been executed.                                                                                                                                                                                                                                                                     | FINEST           | Integer        | hour minute second | ``pubsub/NodeCreateModule last hour`` ``pubsub/NodeCreateModule last minute`` ``pubsub/NodeCreateModule last second``                                  |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| NodeCreateModule/Average processing time            | The average time in ms, returned as an integer, it takes for ``NodeCreateModule`` to execute.                                                                                                                                                                                                                                                                 | FINEST           | Integer        |                    | ``pubsub/NodeCreateModule/Average processing time``                                                                                                    |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| NodeDeleteModule last {interval}                    | The number of times per interval that the ``NodeDeleteModule`` command has been executed.                                                                                                                                                                                                                                                                     | FINEST           | Integer        | hour minute second | ``pubsub/NodeDeleteModule last hour`` ``pubsub/NodeDeleteModule last minute`` ``pubsub/NodeDeleteModule last second``                                  |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| NodeDeleteModule/Average processing time            | The average time in ms, returned as an integer, it takes for ``NodeDeleteModule`` to execute.                                                                                                                                                                                                                                                                 | FINEST           | Integer        |                    | ``pubsub/NodeDeleteModule/Average processing time``                                                                                                    |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| PresenceCollectorModule last {interval}             | The number of times per interval that the ``PresenceCollectorModule`` command has been executed.                                                                                                                                                                                                                                                              | FINEST           | Integer        | hour minute second | ``pubsub/PresenceCollectorModule last hour`` ``pubsub/PresenceCollectorModule last minute`` ``pubsub/PresenceCollectorModule last second``             |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| PresenceCollectorModule/Average processing time     | The average time in ms, returned as an integer, it takes for ``PresenceCollectorModule`` to execute.                                                                                                                                                                                                                                                          | FINEST           | Integer        |                    | ``pubsub/PresenceCollectorModule/Average processing time``                                                                                             |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| PendingSubscriptionModule last {interval}           | The number of times per interval that the ``PendingSubscriptionModule`` command has been executed.                                                                                                                                                                                                                                                            | FINEST           | Integer        | hour minute second | ``pubsub/PendingSubscriptionModule last hour`` ``pubsub/PendingSubscriptionModule last minute`` ``pubsub/PendingSubscriptionModule last second``       |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| PendingSubscriptionModule/Average processing time   | The average time in ms, returned as an integer, it takes for ``PendingSubscriptionModule`` to execute.                                                                                                                                                                                                                                                        | FINEST           | Integer        |                    | ``pubsub/PendingSubscriptionModule/Average processing time``                                                                                           |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| PresenceNotifierModule last {interval}              | The number of times per interval that the ``PresenceNotifierModule`` command has been executed.                                                                                                                                                                                                                                                               | FINEST           | Integer        | hour minute second | ``pubsub/PresenceNotifierModule last hour`` ``pubsub/PresenceNotifierModule last minute`` ``pubsub/PresenceNotifierModule last second``                |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| PresenceNotifierModule/Average processing time      | The average time in ms, returned as an integer, it takes for ``PresenceNotifierModule`` to execute.                                                                                                                                                                                                                                                           | FINEST           | Integer        |                    | ``pubsub/PresenceNotifierModule/Average processing time``                                                                                              |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| PublishItemModule last {interval}                   | The number of times per interval that the ``PublishItemModule`` command has been executed.                                                                                                                                                                                                                                                                    | FINEST           | Integer        | hour minute second | ``pubsub/PublishItemModule last hour`` ``pubsub/PublishItemModule last minute`` ``pubsub/PublishItemModule last second``                               |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| PublishItemModule/Average processing time           | The average time in ms, returned as an integer, it takes for ``PublishItemModule`` to execute.                                                                                                                                                                                                                                                                | FINEST           | Integer        |                    | ``pubsub/PublishItemModule/Average processing time``                                                                                                   |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| PurgeItemsModule last {interval}                    | The number of times per interval that the ``PurgeItemsModule`` command has been executed.                                                                                                                                                                                                                                                                     | FINEST           | Integer        | hour minute second | ``pubsub/PurgeItemsModule last hour`` ``pubsub/PurgeItemsModule last minute`` ``pubsub/PurgeItemsModule last second``                                  |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| PurgeItemsModule/Average processing time            | The average time in ms, returned as an integer, it takes for ``PurgeItemsModule`` to execute.                                                                                                                                                                                                                                                                 | FINEST           | Integer        |                    | ``pubsub/PurgeItemsModule/Average processing time``                                                                                                    |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Repository writes                                   | Number of individual writes to Repository from the pubsub component since startup.                                                                                                                                                                                                                                                                            | FINEST           | Integer        |                    | ``pubsub/Repository writes``                                                                                                                           |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| RetractItemModule last {interval}                   | The number of times per interval that the ``RetractItemModule`` command has been executed.                                                                                                                                                                                                                                                                    | FINEST           | Integer        | hour minute second | ``pubsub/RetractItemModule last hour`` ``pubsub/RetractItemModule last minute`` ``pubsub/RetractItemModule last second``                               |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| RetractItemModule/Average processing time           | The average time in ms, returned as an integer, it takes for ``RetractItemModule`` to execute.                                                                                                                                                                                                                                                                | FINEST           | Integer        |                    | ``pubsub/RetractItemModule/Average processing time``                                                                                                   |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| RetrieveAffiliationsModule last {interval}          | The number of times per interval that the ``RetrieveAffiliationsModule`` command has been executed.                                                                                                                                                                                                                                                           | FINEST           | Integer        | hour minute second | ``pubsub/RetrieveAffiliationsModule last hour`` ``pubsub/RetrieveAffiliationsModule last minute`` ``pubsub/RetrieveAffiliationsModule last second``    |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| RetrieveAffiliationsModule/Average processing time  | The average time in ms, returned as an integer, it takes for ``RetrieveAffiliationsModule`` to execute.                                                                                                                                                                                                                                                       | FINEST           | Integer        |                    | ``pubsub/RetrieveAffiliationsModule/Average processing time``                                                                                          |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| RetrieveItemsModule last {interval}                 | The number of times per interval that the ``RetrieveItemsModule`` command has been executed.                                                                                                                                                                                                                                                                  | FINEST           | Integer        | hour minute second | ``pubsub/RetrieveItemsModule last hour`` ``pubsub/RetrieveItemsModule last minute`` ``pubsub/RetrieveItemsModule last second``                         |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| RetrieveItemsModule/Average processing time         | The average time in ms, returned as an integer, it takes for ``RetrieveItemsModule`` to execute.                                                                                                                                                                                                                                                              | FINEST           | Integer        |                    | ``pubsub/RetrieveItemsModule/Average processing time``                                                                                                 |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| RetrieveSubscriptionsModule last {interval}         | The number of times per interval that the ``RetrieveSubscriptionsModule`` command has been executed.                                                                                                                                                                                                                                                          | FINEST           | Integer        | hour minute second | ``pubsub/RetrieveSubscriptionsModule last hour`` ``pubsub/RetrieveSubscriptionsModule last minute`` ``pubsub/RetrieveSubscriptionsModule last second`` |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| RetrieveSubscriptionsModule/Average processing time | The average time in ms, returned as an integer, it takes for ``RetrieveSubscriptionsModule`` to execute.                                                                                                                                                                                                                                                      | FINEST           | Integer        |                    | ``pubsub/RetrieveSubscriptionsModule/Average processing time``                                                                                         |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| SubscribeNodeModule last {interval}                 | The number of times per interval that the ``SubscribeNodeModule`` command has been executed.                                                                                                                                                                                                                                                                  | FINEST           | Integer        | hour minute second | ``pubsub/SubscribeNodeModule last hour`` ``pubsub/SubscribeNodeModule last minute`` ``pubsub/SubscribeNodeModule last second``                         |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| SubscribeNodeModule/Average processing time         | The average time in ms, returned as an integer, it takes for ``SubscribeNodeModule`` to execute.                                                                                                                                                                                                                                                              | FINEST           | Integer        |                    | ``pubsub/SubscribeNodeModule/Average processing time``                                                                                                 |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Subscription count (in cache)                       | The total number of pubsub subscriptions that are resident in cache memory. This may not reflect total pubsub subscriptions in repository.                                                                                                                                                                                                                    | FINEST           | Integer        |                    | ``pubsub/Subscription count (in cache)``                                                                                                               |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Total writing time                                  | The cumulative total of time pubsub component has written to the database expressed in milliseconds.                                                                                                                                                                                                                                                          | FINEST           | String (###ms) |                    | ``pubsub/Total writing time``                                                                                                                          |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| UnsubscribeNodeModule last {interval}               | The number of times per interval that the ``UnsubscribeNodeModule`` command has been executed.                                                                                                                                                                                                                                                                | FINEST           | Integer        | hour minute second | ``pubsub/UnsubscribeNodeModule last hour`` ``pubsub/UnsubscribeNodeModule last minute`` ``pubsub/UnsubscribeNodeModule last second``                   |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| UnsubscribeNodeModule/Average processing time       | The average time in ms, returned as an integer, it takes for ``UnsubscribeNodeModule`` to execute.                                                                                                                                                                                                                                                            | FINEST           | Integer        |                    | ``pubsub/UnsubscribeNodeModule/Average processing time``                                                                                               |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Update subscription calls                           | Number of times Subscriptions have been updated (this includes new, deleted, and edited).                                                                                                                                                                                                                                                                     | FINEST           | Integer        |                    | ``pubsub/Update subscriptions calls``                                                                                                                  |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| XmppPingModule last {interval}                      | The number of times per interval that the XmppPingModule command has been executed.                                                                                                                                                                                                                                                                           | FINEST           | Integer        | hour minute second | ``pubsub/XmppPingModule last hour`` ``pubsub/XmppPingModule last minute`` ``pubsub/XmppPingModule last second``                                        |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| XmppPingModule/Average processing time              | The average time in ms, returned as an integer, it takes for ``XmppPingModule`` to execute.                                                                                                                                                                                                                                                                   | FINEST           | Integer        |                    | ``pubsub/XmppPingModule/Average processing time``                                                                                                      |\r\n+-----------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+----------------+--------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n\r\nrepo-factory\r\n^^^^^^^^^^^^^\r\n\r\n+------------------------------------------------+---------------------------------------------------------------------------+------------------+---------+-----------------------------------------------------------------+\r\n| Statistics Name                                | Description                                                               | Statistics Level | Format  | List of Possible Statistics                                     |\r\n+------------------------------------------------+---------------------------------------------------------------------------+------------------+---------+-----------------------------------------------------------------+\r\n| Number of data repositories                    | The number of data repositories setup for this XMPP server.               | FINE             | Integer | ``repo-factory/Number of data repositories``                    |\r\n+------------------------------------------------+---------------------------------------------------------------------------+------------------+---------+-----------------------------------------------------------------+\r\n| Repository {jdbclocation} connections count    | The number of connections made to this database.                          | FINE             | Integer | ``repo-factory/repository {jdbclocation} connections count``    |\r\n+------------------------------------------------+---------------------------------------------------------------------------+------------------+---------+-----------------------------------------------------------------+\r\n| repository {jdbclocation} reconnections        | The number of reconnections made to this database.                        | FINEST           | Integer | ``repo-factory/repository {jdbclocation} reconnections``        |\r\n+------------------------------------------------+---------------------------------------------------------------------------+------------------+---------+-----------------------------------------------------------------+\r\n| repository {jdbclocation} failed reconnections | The number of reconnections that have failed to connect to this database. | FINEST           | Integer | ``repo-factory/repository {jdbclocation} failed reconnections`` |\r\n+------------------------------------------------+---------------------------------------------------------------------------+------------------+---------+-----------------------------------------------------------------+\r\n\r\nrest\r\n^^^^^\r\n\r\nNo exclusive rest specific statistics\r\n\r\n\r\ns2s\r\n^^^^\r\n\r\n+-----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Statistics Name                               | Description                                                                                                                                                                                               | Statistics Level | Format  | Available {field}  | List of Possible Statistics                                                                                                                                           |\r\n+===============================================+===========================================================================================================================================================================================================+==================+=========+====================+=======================================================================================================================================================================+\r\n| CIDs number                                   | ConnectionID for the server. This may include multiple CIDs if server is running multiple vhosts.                                                                                                         | FINEST           | String  |                    | ``s2s/CIDs number``                                                                                                                                                   |\r\n+-----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| get-cid-connection last {interval}            | The number of times get-cid-connection command has been executed within the specified interval.                                                                                                           | FINEST           | Integer | hour minute second | ``s2s/adhoc-command/get-cid-connection last hour`` ``s2s/adhoc-command/get-cid-connection last minute`` ``s2s/adhoc-command/get-cid-connection last second``          |\r\n+-----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| get-cid-connection/Average processing time    | The average time in ms, returned as an integer, it takes for ``get-cid-connection`` to execute.                                                                                                           | FINEST           | Integer |                    | ``s2s/adhoc-command/get-cid-connection/Average processing time``                                                                                                      |\r\n+-----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| s2s-bad-state-conns last {interval}           | The number of times s2s-bad-state-conns command has been executed within the specified interval.                                                                                                          | FINEST           | Integer | hour minute second | ``s2s/adhoc-command/s2s-bad-state-conns last hour`` ``s2s/adhoc-command/s2s-bad-state-conns last minute`` ``s2s/adhoc-command/s2s-bad-state-conns last second``       |\r\n+-----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| s2s-bad-state-conns/Average processing time   | The average time in ms, returned as an integer, it takes for ``s2s-bad-state-conns`` to execute.                                                                                                          | FINEST           | Integer |                    | ``s2s/adhoc-command/s2s-bad-state-conns/Average processing time``                                                                                                     |\r\n+-----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| reset-bad-state-conns last {interval}         | The number of times reset-bad-state-conns command has been executed within the specified interval.                                                                                                        | FINEST           | Integer | hour minute second | ``s2s/adhoc-command/reset-bad-state-conns last hour`` ``s2s/adhoc-command/reset-bad-state-conns last minute`` ``s2s/adhoc-command/reset-bad-state-conns last second`` |\r\n+-----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| reset-bad-state-conns/Average processing time | The average time in ms, returned as an integer, it takes for ``reset-bad-state-conns`` to execute.                                                                                                        | FINEST           | Integer |                    | ``s2s/adhoc-command/reset-bad-state-conns/Average processing time``                                                                                                   |\r\n+-----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Total DB keys                                 | Total number of database keys.                                                                                                                                                                            | FINEST           | Integer |                    | ``s2s/Total DB keys``                                                                                                                                                 |\r\n+-----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Total {incoming/outgoing}                     | The total number of server-to-server connections, outgoing is local server connecting to other servers, and incoming is connections from other servers. The results may or may not be the same.           | FINEST           | Integer |                    | ``s2s/Total incoming`` ``s2s/Total outgoing``                                                                                                                         |\r\n+-----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Total {incoming/outgoing} TLS                 | The total number of server-to-server connections using TLS, outgoing is local server connecting to other servers, and incoming is connections from other servers. The results may or may not be the same. | FINEST           | Integer |                    | ``s2s/Total incoming TLS`` ``s2s/Total outgoing TLS``                                                                                                                 |\r\n+-----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Total outgoing handshaking                    | Total number of outgoing connections that are currently handshaking to other servers.                                                                                                                     | FINEST           | Integer |                    | ``s2s/Total outgoing handshaking``                                                                                                                                    |\r\n+-----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Total control waiting                         | Total number of connections that were manually told to wait.                                                                                                                                              | FINEST           | Integer |                    | ``s2s/Total control waiting``                                                                                                                                         |\r\n+-----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Total waiting                                 | Total number of connections that are currently waiting for response from other server.                                                                                                                    | FINEST           | Integer |                    | ``s2s/Total waiting``                                                                                                                                                 |\r\n+-----------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n\r\nsess-man\r\n^^^^^^^^^\r\n\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Statistics Name                                                                                  | Description                                                                                                                                                                                             | Statistics Level | Format  | Available {field}  | List of Possible Statistics                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |\r\n+==================================================================================================+=========================================================================================================================================================================================================+==================+=========+====================+==================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================+\r\n| Active user connections                                                                          | Number of user connections that are considered active. An active user is a user that has sent stanzas to the server or through the server within the last 5 minutes.                                    | FINER            | Integer |                    | ``sess-man/Active user connections``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/connection-time last {interval}                                                    | The number of times ``connection-time`` command has been executed within the specified interval.                                                                                                        | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/connection-time last hour`` ``sess-man/adhoc-command/connection-time last minute`` ``sess-man/adhoc-command/connection-time last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/connection-time/Average processing time                                            | The average time in ms, returned as an integer, it takes for ``connection-time`` to execute.                                                                                                            | FINEST           | Integer |                    | ``sess-man/adhoc-command/connection-time/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#add-user last {interval}                          | The number of times ``admin#add-user`` command has been executed within the specified interval.                                                                                                         | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-user last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-user last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-user last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#add-user/Average processing time                  | The average time in ms, returned as an integer, it takes for ``admin#add-user`` to execute.                                                                                                             | FINEST           | Integer |                    | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-user/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#add-user-tracker last {interval}                  | The number of times ``admin#add-user-tracker`` command has been executed within the specified interval.                                                                                                 | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-user-tracker last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-user-tracker last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-user-tracker last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#add-user-tracker/Average processing time          | The average time in ms, returned as an integer, it takes for ``admin#add-user-tracker`` to execute.                                                                                                     | FINEST           | Integer |                    | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-user-tracker/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#announce last {interval}                          | The number of times ``admin#announce`` command has been executed within the specified interval.                                                                                                         | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#announce last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#announce last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#announce last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#announce/Average processing time                  | The average time in ms, returned as an integer, it takes for ``admin#announce`` to execute.                                                                                                             | FINEST           | Integer |                    | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#announce/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#change-user-password last {interval}              | The number of times ``admin#change-user-password`` command has been executed within the specified interval.                                                                                             | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#change-user-password last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#change-user-password last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#change-user-password last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#change-user-password/Average processing time      | The average time in ms, returned as an integer, it takes for ``admin#change-user-password`` to execute.                                                                                                 | FINEST           | Integer |                    | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#change-user-password/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#delete-user last {interval}                       | The number of times ``admin#delete-user`` command has been executed within the specified interval.                                                                                                      | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#delete-user last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#delete-user last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#delete-user last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#delete-user/Average processing time               | The average time in ms, returned as an integer, it takes for ``admin#delete-user`` to execute.                                                                                                          | FINEST           | Integer |                    | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#delete-user/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#end-user-session last {interval}                  | The number of times ``admin#end-user-session`` command has been executed within the specified interval.                                                                                                 | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#end-user-session last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#end-user-session last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#end-user-session last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#end-user-session/Average processing time          | The average time in ms, returned as an integer, it takes for ``admin#end-user-session`` to execute.                                                                                                     | FINEST           | Integer |                    | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#end-user-session/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#get-active-users last {interval}                  | The number of times ``admin#get-active-users`` command has been executed within the specified interval.                                                                                                 | FINEST           | Integer |                    | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-active-users last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-active-users last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-active-users last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#get-active-users/Average processing time          | The average time in ms, returned as an integer, it takes for ``admin#get-active-users`` to execute.                                                                                                     | FINEST           | Integer |                    | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-active-users/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#get-active-user-num last {interval}               | The number of times ``admin#get-active-user-num`` command has been executed within the specified interval.                                                                                              | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-active-user-num last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-active-user-num last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-active-user-num last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#get-active-user-num/Average processing time       | The average time in ms, returned as an integer, it takes for ``admin#get-active-user-num`` to execute.                                                                                                  | FINEST           | Integer |                    | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-active-user-num/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#get-idle-users last {interval}                    | The number of times ``admin#get-idle-users`` command has been executed within the specified interval.                                                                                                   | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-idle-users last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-idle-users last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-idle-users last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#get-idle-users/Average processing time            | The average time in ms, returned as an integer, it takes for ``admin#get-idle-users`` to execute.                                                                                                       | FINEST           | Integer |                    | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-idle-users/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#get-idle-users-num last {interval}                | The number of times ``admin#get-idle-users-num`` command has been executed within the specified interval.                                                                                               | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-idle-users-num last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-idle-users-num last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-idle-users-num last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#get-idle-users-num/Average processing time        | The average time in ms, returned as an integer, it takes for ``admin#get-idle-users-num`` to execute.                                                                                                   | FINEST           | Integer |                    | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-idle-users-num/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#get-online-users-list last {interval}             | The number of times ``admin#get-online-users-list`` command has been executed within the specified interval.                                                                                            | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-online-users-list last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-online-users-list last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-online-users-list last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#get-online-users-list/Average processing time     | The average time in ms, returned as an integer, it takes for ``admin#get-online-users-list`` to execute.                                                                                                | FINEST           | Integer |                    | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-online-users-list/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#get-top-active-users last {interval}              | The number of times ``admin#get-top-active-users`` command has been executed within the specified interval.                                                                                             | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-top-active-users last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-top-active-users last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-top-active-users last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#get-top-active-users/Average processing time      | The average time in ms, returned as an integer, it takes for ``admin#get-top-active-users`` to execute.                                                                                                 | FINEST           | Integer |                    | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-top-active-users/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#get-registered-users-list last {interval}         | The number of times ``admin#get-registered-users-list`` command has been executed within the specified interval.                                                                                        | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-registered-users-list last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-registered-users-list last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-registered-users-list last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#get-registered-users-list/Average processing time | The average time in ms, returned as an integer, it takes for ``admin#get-registered-users-list`` to execute.                                                                                            | FINEST           | Integer |                    | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-registered-users-list/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#get-user-roster last {interval}                   | The number of times ``admin#get-user-roster`` command has been executed within the specified interval.                                                                                                  | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-user-roster last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-user-roster last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-user-roster last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#get-user-roster/Average processing time           | The average time in ms, returned as an integer, it takes for ``admin#get-user-roster`` to execute.                                                                                                      | FINEST           | Integer |                    | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-user-roster/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#remove-user last {interval}                       | The number of times ``admin#remove-user`` command has been executed within the specified interval.                                                                                                      | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#remove-user last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#remove-user last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#remove-user last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#remove-user/Average processing time               | The average time in ms, returned as an integer, it takes for ``admin#remove-user`` to execute.                                                                                                          | FINEST           | Integer |                    | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#remove-user/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#user-stats last {interval}                        | The number of times ``admin#user-stats`` command has been executed within the specified interval.                                                                                                       | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#user-stats last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#user-stats last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#user-stats last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/http://jabber.org/protocol/admin#user-stats/Average processing time                | The average time in ms, returned as an integer, it takes for ``admin#user-stats`` to execute.                                                                                                           | FINEST           | Integer |                    | ``sess-man/adhoc-command/http://jabber.org/protocol/admin#user-stats/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/get-user-info last {interval}                                                      | The number of times ``get-user-info command`` has been executed within the specified interval.                                                                                                          | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/get-user-info last hour`` ``sess-man/adhoc-command/get-user-info last minute`` ``sess-man/adhoc-command/get-user-info last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/get-user-info/Average processing time                                              | The average time in ms, returned as an integer, it takes for ``get-user-info`` to execute.                                                                                                              | FINEST           | Integer |                    | ``sess-man/adhoc-command/get-user-info/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/modify-user last {interval}                                                        | The number of times ``modify-user`` command has been executed within the specified interval.                                                                                                            | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/modify-user last hour`` ``sess-man/adhoc-command/modify-user last minute`` ``sess-man/adhoc-command/modify-user last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/modify-user/Average processing time                                                | The average time in ms, returned as an integer, it takes for ``modify-user`` to execute.                                                                                                                | FINEST           | Integer |                    | ``sess-man/adhoc-command/modify-user/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/oauth-credentials last {interval}                                                  | The number of times ``oauth-credentials`` command has been executed within the specified interval.                                                                                                      | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/oauth-credentials last hour`` ``sess-man/adhoc-command/oauth-credentials last minute`` ``sess-man/adhoc-command/oauth-credentials last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/oauth-credentials/Average processing time                                          | The average time in ms, returned as an integer, it takes for ``oauth-credentials`` to execute.                                                                                                          | FINEST           | Integer |                    | ``sess-man/adhoc-command/oauth-credentials/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/roster-fixer last {interval}                                                       | The number of times ``roster-fixer`` command has been executed within the specified interval.                                                                                                           | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/roster-fixer last hour`` ``sess-man/adhoc-command/roster-fixer last minute`` ``sess-man/adhoc-command/roster-fixer last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/roster-fixer/Average processing time                                               | The average time in ms, returned as an integer, it takes for ``roster-fixer`` to execute.                                                                                                               | FINEST           | Integer |                    | ``sess-man/adhoc-command/roster-fixer/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/roster-fixer-cluster last {interval}                                               | The number of times ``roster-fixer-cluster`` command has been executed within the specified interval.                                                                                                   | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/roster-fixer-cluster last hour`` ``sess-man/adhoc-command/roster-fixer-cluster last minute`` ``sess-man/adhoc-command/roster-fixer-cluster last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/roster-fixer-cluster/Average processing time                                       | The average time in ms, returned as an integer, it takes for ``roster-fixer-cluster`` to execute.                                                                                                       | FINEST           | Integer |                    | ``sess-man/adhoc-command/roster-fixer-cluster/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/user-domain-perm last {interval}                                                   | The number of times user-domain-perm command has been executed within the specified interval.                                                                                                           | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/user-domain-perm last hour`` ``sess-man/adhoc-command/user-domain-perm last minute`` ``sess-man/adhoc-command/user-domain-perm last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/user-domain-perm/Average processing time                                           | The average time in ms, returned as an integer, it takes for ``user-domain-perm`` to execute.                                                                                                           | FINEST           | Integer |                    | ``sess-man/adhoc-command/user-domain-perm/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/user-roster-management last {interval}                                             | The number of times ``user-roster-management`` command has been executed within the specified interval.                                                                                                 | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/user-roster-management last hour`` ``sess-man/adhoc-command/user-roster-management last minute`` ``sess-man/adhoc-command/user-roster-management last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/user-roster-management/Average processing time                                     | The average time in ms, returned as an integer, it takes for ``user-roster-management`` to execute.                                                                                                     | FINEST           | Integer |                    | ``sess-man/adhoc-command/user-roster-management/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/user-roster-management-ext last {interval}                                         | The number of times ``user-roster-management-ext`` command has been executed within the specified interval.                                                                                             | FINEST           | Integer | hour minute second | ``sess-man/adhoc-command/user-roster-management-ext last hour`` ``sess-man/adhoc-command/user-roster-management-ext last minute`` ``sess-man/adhoc-command/user-roster-management-ext last second``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| adhoc-command/user-roster-management-ext/Average processing time                                 | The average time in ms, returned as an integer, it takes for ``user-roster-management-ext`` to execute.                                                                                                 | FINEST           | Integer |                    | ``sess-man/adhoc-command/user-roster-management-ext/Average processing time``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Authentication timeouts                                                                          | The number of connections that have timed out during the authentication process. Default timeout is 2 minutes.                                                                                          | FINEST           | Integer |                    | ``sess-man/Authentication timeouts``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Closed user connections                                                                          | User connections that have been terminated by the user (as opposed to the server).                                                                                                                      | FINEST           | Integer |                    | ``sess-man/Closed user connections``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| default-handler/Invalid registrations                                                            | Number of invalid registrations attempted with the server.                                                                                                                                              | FINEST           | Integer |                    | ``sess-man/default-handler/Invalid registrations``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| default-handler/Registered users                                                                 | Number of registered users for this server.                                                                                                                                                             | FINEST           | Integer |                    | ``sess-man/default-handler/Registered users``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Maximum user connections                                                                         | Maximum number of connections that have been made during server instance, this number includes users connecting multiple times.                                                                         | INFO             | Integer |                    | ``sess-man/Maximum user connections``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Maximum user sessions {today/yesterday}                                                          | The number of most simultaneous sessions within the specified interval. Today = previous 24 hours, Yesterday = 24 hours after previous 24 hours (does not go by calendar date).                         | INFO/FINEST      | Integer |                    | ``sess-man/Maximum user sessions today`` ``sess-man/Maximum user sessions yesterday``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Registered accounts                                                                              | Sum total of registered accounts for the server.                                                                                                                                                        | FINEST           | Integer |                    | ``sess-man/Registered accounts``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Open user connections                                                                            | The current number of open user connections. This may be interpreted as number of connections from users, however a user can have more than one connection (connection from mobile and PC for example). | INFO             | Integer |                    | ``sess-man/Open user connections``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Open user sessions                                                                               | The current number of open user sessions.                                                                                                                                                               | INFO             | Integer |                    | ``sess-man/Open user sessions``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Total user connections                                                                           | The cumulative number of connections that have been made to the server during the current instance.                                                                                                     | FINER            | Integer |                    | ``sess-man/Total user connections``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| Total user sessions                                                                              | The cumulative number of sessions that this server has negotiated during the current instance.                                                                                                          | FINER            | Integer |                    | ``sess-man/Total user sessions``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| presence/Users status changes                                                                    | The number of presence changes for all users that have been conducted during the server instance.                                                                                                       | INFO             | Integer |                    | ``sess-man/presence/Users status changes`` ``sess-man/presence-state/Users status changes``                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| sess-man/Processor                                                                               | Processor statistics will result in a field of labels and values exclusive to that processor.                                                                                                           | FINEST           | FIELD   |                    | ``sess-man/Processor: message carbons`` ``sess-man/Processor: http://jabber.org/protocol/stats`` ``sess-man/Processor: jabber:iq:auth`` ``sess-man/Processor: vcard-temp`` ``sess-man/Processor: amp`` ``sess-man/Processor: presence-subscription`` ``sess-man/Processor: disco`` ``sess-man/Processor: msgoffline`` ``sess-man/Processor: urn:xmpp:blocking`` ``sess-man/Processor: urn:xmpp:ping`` ``sess-man/Processor: jabber:iq:register`` ``sess-man/Processor: urn:ietf:params:xml:ns:xmpp-sasl`` ``sess-man/Processor: prp`` ``sess-man/Processor: presence`` ``sess-man/Processor: message-archive-xep-0136`` ``sess-man/Processor: default-handler`` ``sess-man/Processor: jabber:iq:roster`` ``sess-man/Processor: starttls`` ``sess-man/Processor: presence-state`` ``sess-man/Processor: jabber:iq:version`` ``sess-man/Processor: urn:xmpp:time`` ``sess-man/Processor: session-open`` ``sess-man/Processor: jabber:iq:privacy`` ``sess-man/Processor: urn:ietf:params:xml:ns:xmpp-bind`` ``sess-man/Processor: http://jabber.org/protocol/commands`` ``sess-man/Processor: vcard-xep0292`` ``sess-man/Processor: session-close`` ``sess-man/Processor: urn:ietf:params:xml:ns:xmpp-session`` ``sess-man/Processor: jabber:iq:private`` ``sess-man/Processor: Average amp on last 100 runs [ms]`` ``sess-man/Processor: Average msgoffline on last 100 runs[ms]`` |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n| | The field shows as follows:                                                                    |                                                                                                                                                                                                         |                  |         |                    |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\r\n| | ``, Queue: 0, AvTime: 0, Runs: 0, Lost: 0``                                                    |                                                                                                                                                                                                         |                  |         |                    |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\r\n| | Where:                                                                                         |                                                                                                                                                                                                         |                  |         |                    |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\r\n| | Queue: Number of packets in process queue                                                      |                                                                                                                                                                                                         |                  |         |                    |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\r\n| | AvTime: Average time in ms processor takes to conduct it’s operation.                          |                                                                                                                                                                                                         |                  |         |                    |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\r\n| | Runs: Number of times Processor has been run.                                                  |                                                                                                                                                                                                         |                  |         |                    |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\r\n| | Lost: Number of packets lost during processing.                                                |                                                                                                                                                                                                         |                  |         |                    |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\r\n+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n\r\nvhost-man\r\n^^^^^^^^^^^^\r\n\r\n+-------------------------------------+------------------------------------------------------------------------+------------------+---------+---------------------------------------------------+\r\n| Statistics Name                     | Description                                                            | Statistics Level | Format  | List of Possible Statistics                       |\r\n+=====================================+========================================================================+==================+=========+===================================================+\r\n| Checks is anonymous domain          | Number of anonymous domain checks that have been run within vhost-man. | FINEST           | Integer | ``vhost-man/Checks is anonymous domain``          |\r\n+-------------------------------------+------------------------------------------------------------------------+------------------+---------+---------------------------------------------------+\r\n| Checks: is local domain             | Number of local domain checks that have been run within vhost-man.     | FINER            | Integer | ``vhost-man/Checks: is local domain``             |\r\n+-------------------------------------+------------------------------------------------------------------------+------------------+---------+---------------------------------------------------+\r\n| Get components for local domain     | Number of components loaded within local domain.                       | FINER            | Integer | ``vhost-man/Get components for local domain``     |\r\n+-------------------------------------+------------------------------------------------------------------------+------------------+---------+---------------------------------------------------+\r\n| Get components for non-local domain | Number of components loaded outside local domain.                      | FINEST           | Integer | ``vhost-man/Get components for non-local domain`` |\r\n+-------------------------------------+------------------------------------------------------------------------+------------------+---------+---------------------------------------------------+\r\n| Number of Vhosts                    | Number of configured and running Virtual Hosts.                        | FINE             | Integer | ``vhost-man/Number of VHosts``                    |\r\n+-------------------------------------+------------------------------------------------------------------------+------------------+---------+---------------------------------------------------+\r\n\r\nws2s\r\n^^^^^^^^\r\n\r\nNo exclusive ws2s specific statistics.\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Tigase_Server_Binary_Updates.rst",
    "content": "Tigase Server Binary Updates\r\n==================================\r\n\r\nMost open source projects try to make sure that the nightly builds compile correctly so that these builds can be used. However, we at Tigase believe that these builds should be separated until they are thoroughly tested and released. Although lots of installations out there we know of just run from our nightly builds, this puts an extra responsibility to make sure all code is functional and will constantly work. Therefore, our general approach is to run all basic functionality tests before each code commit to make sure it works correctly. This does not guarantee that there will never be a problem, but it is a precaution from preventing bad builds from arriving in the hands of our customers.\r\n\r\nSome users on the other hand, like to be on the bleeding edge and regularly use our nightly builds exploring new code changes and playing with new features before they are put to a full release. Others prefer to stick to stable and fully tested public releases. Others however, want something from the middle, the most recent features, but bug fixes, something like a beta or a release-candidate state.\r\n\r\nShould you choose to use the nightly builds, a few things you should consider:\r\n\r\n-  Changes may be made to the code that can negatively affect performance.\r\n\r\n-  Changes may be made to the code that can negatively affect security.\r\n\r\nWe **highly** recommend testing these builds in your environments before upgrading.\r\n\r\nWith these considerations in mind, we provide nightly builds at `this link <https://build.tigase.net/nightlies/dists/>`__ which provides directories by date.\r\n\r\nStandard naming format is ``tigase-server-<version>-SNAPSHOT-b<build>-<type>`` where ``<version>`` is in the form of ``major.minor.bugfix``\r\n\r\n.. Note::\r\n\r\n   individual days may have the same builds as noted by the byyyy section of the file.\\*\r\n\r\nJust like the standard distributions, the builds are available with the following extensions (``<type>``):\r\n\r\n1. ``javadoc.jar`` - Java installer for javadoc only\r\n\r\n2. ``dist.zip`` - Compressed binaries with no dependencies.\r\n\r\n3. ``dist.tar.gz`` - tarball compressed binaries with no dependencies.\r\n\r\n4. ``dist-max.zip`` - Compressed binaries with all dependencies.\r\n\r\n5. ``dist-max.tar.gz`` - tarball compressed binaries with all dependencies.\r\n\r\nWe also provide automated testing of each of our nightly builds for each supported databases. Tests are done with both functional and low memory parameters in mind, and are available `at this link <https://build.tigase.net/nightlies/tests/>`__. These tests can provide a quick examination of function before upgrading your current build.\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc",
    "content": "Configuration instructions for Psi\r\n-----------------------------------\r\n\r\nPsi - Initial configuration\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThe first time you run Psi you see a screen like this:\r\n\r\n|Psi First Run|\r\n\r\nTo connect to tigase.org server we need to configure the program. Below are step-by-step instructions for novice users on how to setup Psi.\r\n\r\n1. Psi can connect to many Jabber servers at the same time so we have to identify each connection somehow. The first thing to do is assign a name to the connection we just created. As we are going to define connection to tigase.org server let’s just name it: **Tigase**.\r\n\r\n   |Psi Add Account|\r\n\r\n\r\n2. When you press the Add button you will see next window where you can enter your Jabber account details:\r\n\r\n   |Psi Empty Account|\r\n\r\n3. Invent your user name for the account on Tigase server. Let’s assume your user name is: **frank**. Jabber ID’s however consist of 2 parts - your user name and server address. Exactly the same as an e-mail address. As you are registering an account on tigase.org server, you will have to enter in this field: **frank@tigase.org**. Next enter the password of your choice and click the Register button.\r\n\r\n   |Psi Register Account|\r\n\r\n4. On successful registration you will receive a confirmation message and you should see a window like this:\r\n\r\n   |Register Account Success|\r\n\r\n   It may happen that somebody earlier registered an account with the same name you’ve selected for yourself. If so, you will receive error message. You will then have to select another user name and try to register again.\r\n\r\n5. After clicking the **OK** button you will see a window with your connection and account setup. You can stick with default values for now.\r\n\r\n   |PSI After Registration|\r\n\r\n   Just click the **Save** button and this window closes.\r\n\r\n6. Now you have your account configured and ready to use but you are still off-line. You can find out whether you are on-line or off-line by looking at the bottom of main Psi window. There you can see **Offline** text.\r\n\r\n   Click on this **Offline** text and you will see a list of possible options. Just select **Online**.\r\n\r\n   |PSI Connected|\r\n\r\n   Now you are connected!\r\n\r\nWell, you are now connected but how to talk to other people? How to add friends to the contact list? You can send a message to your friends straight away using the **Psi menu** option **New blank message**. It is much more convenient however, if you could see which of your friends is online and available for chatting and if you could start talking to your friend just by clicking on his name.\r\n\r\n\r\nShort Instructions How to Add Your First Contact\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n1. Click on Psi menu - the button next to the **Online** text. You will see something like this:\r\n\r\n   |PSI Menu|\r\n\r\n   From all menu options select the top one - Add a contact:\r\n\r\n   |PSI Menu add Contact|\r\n\r\n2. The next window will display where you can enter your contact details:\r\n\r\n   |PSI Add User Empty|\r\n\r\n   You have to know the Jabber ID of the person you want to add to your contact list. Let’s assume, for example, you want to add Tigase server administrator’s Jabber ID to your contact list. So, after you enter these details the window will look like this:\r\n\r\n   |PSI Add User Filled|\r\n\r\n   Click the **Add** button.\r\n\r\n3. Now you will see a confirmation window that a new person has been added to your contact list:\r\n\r\n   |PSI Kobit Added|\r\n\r\n   But there is more behind the scenes. Adding a contact to your **Roster** (contact list) usually means you can see whether the person is online and available to talk or not. The person however, may not wish you to see his presence. So, to make sure the other person accepts you as a friend Psi sent a request to the address you just entered with the question of whether he agrees to show his presence to you.\r\n\r\n   You won’t be able to see the users availability until he sends confirmation.\r\n\r\n4. Once the other user sends confirmation back, you will usually receive 2 system events:\r\n\r\n   |PSI Kobit Auth Received|\r\n\r\n5. Click on the contact to see a window with these messages:\r\n\r\n   |PSI Authorized Window|\r\n\r\n6. One message just says you have been authorized by the other user:\r\n\r\n   |PSI Authorized Window 2|\r\n\r\n   So you simply click **Next** to see the second message.\r\n\r\n7. The second message is a bit more interesting. It contains the question of whether you also authorize the other user to see your presence. If you want to accept this request just click **Add/Auth**.\r\n\r\n   |PSI Authorized Window 3|\r\n\r\n8. Finally main Psi window with your new contact:\r\n\r\n   |PSI Kobit Added Authorized|\r\n\r\nWell done!\r\n\r\nYou are ready to start Jabbering. Good luck.\r\n\r\nWhere to go next? For detailed Psi documentation refer to the program Wiki page: http://psi-im.org/wiki/Main_Page\r\n\r\nWelcome to the Tigase Administration Guide.\r\n\r\n.. |Psi First Run| image:: /images/user/psi-first-run.png\r\n.. |Psi Add Account| image:: /images/user/psi-add-account.png\r\n.. |Psi Empty Account| image:: /images/user/psi-register-account-empty.png\r\n.. |Psi Register Account| image:: /images/user/psi-register-account-nossl.png\r\n.. |Register Account Success| image:: /images/user/psi-register-account-success.png\r\n.. |PSI After Registration| image:: /images/user/psi-after-registration.png\r\n.. |PSI Connected| image:: /images/user/psi-connected.png\r\n.. |PSI Menu| image:: /images/user/psi-menu.png\r\n.. |PSI Menu add Contact| image:: /images/user/psi-menu-add-contact.png\r\n.. |PSI Add User Empty| image:: /images/user/psi-add-user-empty.png\r\n.. |PSI Add User Filled| image:: /images/user/psi-add-user-filled.png\r\n.. |PSI Kobit Added| image:: /images/user/psi-kobit-added.png\r\n.. |PSI Kobit Auth Received| image:: /images/user/psi-kobit-auth-received.png\r\n.. |PSI Authorized Window| image:: /images/user/psi-authorized-window.png\r\n.. |PSI Authorized Window 2| image:: /images/user/psi-authorized-window-2.png\r\n.. |PSI Authorized Window 3| image:: /images/user/psi-authorized-window-3.png\r\n.. |PSI Kobit Added Authorized| image:: /images/user/psi-kobit-added-authorized.png"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Tigase_User_Guide/How_to_Use.inc",
    "content": "How to Use Tigase Service\r\n-----------------------------------\r\n\r\n\r\nThis Article Describes How to use **tigase.im** Service for Instant Communications\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nYou have to install and run a Jabber client application to use the service.\r\n\r\nThere are multiple domains available: tigase.im, sure.im, xmpp.cloud (and you can host your own domain as well)\r\n\r\n\r\nShort instructions:\r\n~~~~~~~~~~~~~~~~~~~\r\n\r\nUsually you just need to enter the user name of the form: user@tigase.im. Your XMPP client should take care of all other things as our service doesn’t need any special settings. If you don’t have an account on tigase.im server yet just tick the option to register new account. That’s it!\r\n\r\n\r\n**Long Instructions:**\r\n~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nGood news is that there are many programs to choose from which allow you to communicate through our server. So you can pick up your favorite application or use an existing one that is compatible and start using our service.\r\n\r\nAll clients presented below support multiple accounts on Jabber servers. What this means is that you can have a few Jabber accounts on different Jabber servers and you can still use just one program to connect to all of them at the same time.\r\n\r\nThe `full list of all known XMPP clients <https://xmpp.org/software/clients.html>`__ is very long. You can obviously try them all but below is a selection which is recommended by the Tigase team. The selected programs might not be the best choice for you, but these programs have been tested and we can offer help with using them. Here is a list of recommended instant messaging clients:\r\n\r\n-  `Beagle.im <https://beagle.im/>`__ - macOS desktop client developed by Tigase team supporting all the latest and greatest features\r\n\r\n-  `Tigase Messenger for iOS <https://itunes.apple.com/us/app/tigase-messenger/id1153516838>`__ - lightweight, powerful XMPP client developed by Tigase, Inc. It provides an easy way to start using the XMPP Protocol (formerly known as Jabber) if you’ve never used it before. Veterans of the protocol will find many features here they are familiar with along with enhancements to reduce data use and extend battery life.\r\n\r\n-  `Tigase Messenger for Android <https://play.google.com/store/apps/details?id=org.tigase.messenger.phone.pro>`__ - mobile chat client to use with XMPP services and servers. The totally revamped v3.0 now has new features, a better design, and Google integration. Application supports any XMPP server, from free services like sure.im or Tigase.im, to a server you may host on your own.\r\n\r\n-  tigase.im[Tigase.im] - web-based client allowing to easily chat with friends independently of platform.\r\n\r\n-  `Psi <http://psi-im.org/>`__ Pure Jabber client. Although it supports only Jabber network it is a very user friendly and comfortable program. It works on most popular operating systems like Linux, MS Windows, and Apple MacOS X.\r\n\r\n-  `Gajim <http://www.gajim.org/>`__ This is another Jabber only client. Very user friendly and works on most of Linux distributions, FreeBSD, and MS Windows.\r\n\r\n-  `Pidgin <http://www.pidgin.im/>`__ (previously `Gaim <http://gaim.sourceforge.net/>`__) This is not just a Jabber client. This type of application is called multicommunicator as apart from Jabber it supports many other instant messaging networks/protocols such as: AIM/ICQ, MSN, Yahoo, Gadu-Gadu, IRC, and a few others. So it is especially convenient if you have friends using other messaging networks. Pidgin works on most Linux distributions, and on MS Windows.\r\n\r\n-  `Kopete <http://kopete.kde.org/>`__ This is a `KDE <http://www.kde.org/>`__ component and although it only works on Linux based system it also supports many of the most popular instant messaging protocols apart from Jabber like: AIM, Gadu-Gadu, ICQ, IRC, MSN, Yahoo.\r\n\r\nInstall the Jabber client of your choice and set up for a Tigase account:"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Tigase_User_Guide/Introduction.inc",
    "content": "Jabber/XMPP introduction\r\n--------------------------\r\n\r\nJabber/XMPP is Instant Messaging Technology\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nAll federated **XMPP** servers are connected in one global communications network allowing you to send messages to friends who have accounts on other Jabber servers.\r\n\r\nThis is very much like sending e-mail but the difference between Jabber and e-mail is the same as the difference between sending a traditional mail and talking on the phone.\r\n\r\nAll messages sent through Jabber are sent instantly and you also receive responses instantly. More over you can see whether your mate is online and available for talking or not.\r\n\r\nThere exists similar technologies to Jabber like WhatsApp Messenger, Facebook Messenger, Signal, Telegram, WeChat, QQ and other. There are, however, quite a few differences.\r\n\r\nXMPP is an open standard which means everybody can know how it works, everybody can implement their own software connecting to the network both client and server side.\r\n\r\nThe server side is actually the biggest difference and advantage. Many companies have offices in different locations, and such instant messaging technology could be very useful to employees for communication. Companies are not inclined to allow confidential discussions to go outside the company’s network. Especially if it is not very secure to leave such information on third party public servers.\r\n\r\nXMPP servers on the other hand, allows you to deploy server software on your own company network. Employees can then talk securely and all information remains on the company’s secure network. Of course if offices are located in different locations or countries then all messages are transmitted over the public network - the Internet. This is not a problem since XMPP supports SSL/TLS - secure encrypted connections which helps you protect your discussion.\r\n\r\nThen if your employees need to contact customers outside your company, the whole discussion can go through your server and a server located on the customer side.\r\n\r\nThere are many other scenarios and use cases but I hope this brief introduction gives you an idea of the differences and advantages of XMPP technology."
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Tigase_User_Guide/User_Guide.rst",
    "content": "\r\nTigase User Guide\r\n===================\r\n\r\n.. include:: Introduction.inc\r\n.. include:: How_to_Use.inc\r\n.. include:: Configuration_Instructions.inc\r\n\r\n\r\n\r\n\r\n\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Using_Tigase/Anonymous_Users.inc",
    "content": "Anonymous Users & Authentication\r\n-------------------------------------\r\n\r\nTo support anonymous users, you must first enable anonymous authentication on your server.\r\n\r\nAnonymous Authentication\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTigase Server can support anonymous logins via SASL-ANONYMOUS in certain scenarios. This can be enabled on per-VHost basis by adjusting *Anonymous enabled* option as described in :ref:`Add and Manage Domains (VHosts)<addManageDomain>` This setting is false by default as SASL-ANONYMOUS may not be totally secure as users can connect without prior permission (username and password).\r\n\r\nAnonymous User Features\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTo connect to your server anonymously, you must use a client that supports anonymous authentication and users. Connect to the server with the name of the server as the username, and no password. For example, to connect anonymously to ``xmpp.example.com`` use the following credentials,\r\n\r\nUsername: ``xmpp.example.com`` Password:\r\n\r\nIn this mode all login information is stored in memory, and cannot be retrieved at a later date.\r\n\r\nOther features of Anonymous Authentication\r\n\r\n-  Temporary Jid is assigned and destroyed the moment of login/logout.\r\n\r\n-  Anonymous users cannot access the database\r\n\r\n-  Anonymous users cannot communicate outside the server (use s2s connections)\r\n\r\n-  Anonymous users have a default limit on traffic generated per user.\r\n\r\nReconnection on Anonymous\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nOn products such as our JaXMPP Server, users connected using SASL-ANONYMOUS can reconnect to existing sessions using cookie management. However, reconnection can be improved and extended using `Bosh Session Cache <http://docs.tigase.org/tigase-server/snapshot/Development_Guide/html/#boshsessioncache>`__ which allows for session storage in memory rather than using client-side data for reconnection."
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Using_Tigase/Basic_System_Checks.inc",
    "content": ".. _Basic-System-Checks:\r\n\r\nBasic System Checks\r\n----------------------\r\n\r\nPreviously, a configuration article is available about :ref:`Linux settings for high load systems<linuxhighload>`. This has a description of basic settings which are essential to successfully run XMPP service for hundreds or thousands of online users.\r\n\r\nOf course, high load and high traffic systems require much more tuning and adjustments. If you use selinux you have to be careful as it can interfere with the service while it is under a high load. Also some firewall settings may cause problems as the system may decide it is under a DDOS attack and can start blocking incoming connections or throttle the traffic.\r\n\r\nIn any case, there are some basic checks to do every time you deploy XMPP service to make sure it will function properly. I am trying to keep the article mentioned above up to date and add all the settings and parameters I discover while working with different installations. *If you have some suggestions for different values or different parameters to add, please let me know.*\r\n\r\nIf you want to run a service on a few cluster nodes (5 or even 10), then manually checking every machine and adjusting these settings is time consuming and it is very easy to forget about.\r\n\r\nTo overcome this problem I started to work on a shell script which would run all the basic checks and report problems found. Ideally it should be also able to adjust some parameters for you.\r\n\r\nInside the Tigase server `scripts/ <https://github.com/tigase/tigase-server/blob/master/scripts/>`__ repository find a script called ``machine-check.sh``. It performs all the basic checks from the article and also tries to adjust them when necessary. Have a `look at the code <https://github.com/tigase/tigase-server/blob/master/scripts/machine-check.sh>`__ and run for yourself.\r\n\r\nAny comments or suggestions, as usual, are very much appreciated."
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Using_Tigase/Clustering.inc",
    "content": "Tigase Clustering\r\n-----------------------\r\n\r\nTigase Clustering allows the use of a number of servers to be unified in delivering, from what a client or user sees, a single unified platform. There are two typical reasons why clustering should be employed:\r\n\r\n-  High Availability\r\n\r\n   ::\r\n\r\n      By using clustering, services can be provided with a high reliability and redundancy. By using a network of multiple servers, content or services can be served on any of the clustered servers maintaining a consistent uptime without relying on one machine.\r\n\r\n-  Load Balancing\r\n\r\n   ::\r\n\r\n      This type of cluster helps to distribute a workload over a number of servers to reduce bottlenecking from heavy resource loads on a particular server.\r\n\r\nWith Tigase, you don’t have to choose between either/or!\r\n\r\n**Tigase Clustering** offers **Full Redundancy** and **Automatic Load Balancing** allowing addition of new nodes at runtime with a simple configuration. All without a severe tax on resource consumption.\r\n\r\nAll basic components support clustering configuration, and some may be turned on or off.\r\n\r\nConfiguration\r\n^^^^^^^^^^^^^^^^\r\n\r\nTo enable Clustering on Tigase servers, use the following line in your ``config.tdsl`` file:\r\n\r\n.. code::\r\n\r\n   'cluster-mode' = true\r\n\r\nThat’s it!\r\n\r\nCustom Ports\r\n~~~~~~~~~~~~~~~~\r\n\r\nYou can customize ports for the cluster component, just be sure that each clustered server also has the same settings so they can communicate.\r\n\r\n.. code::\r\n\r\n   cl-comp {\r\n       connections {\r\n           4250 {}\r\n           3540 {}\r\n       }\r\n   }\r\n\r\nYou can fine tune each port configuration, however this is not typically needed.\r\n\r\nCustom Port Configuration\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nEach port has it’s own details that can be manipulated via the following ports. Again **THIS IS OPTIONAL**\r\n\r\n.. code::\r\n\r\n   'cl-comp' {\r\n       connections {\r\n           4250 {\r\n               ifc = [ '*' ]\r\n               'remote-host' = 'localhost'\r\n               socket = 'plain'\r\n               type = 'accept'\r\n               connections {\r\n                   tls {\r\n                       required = false\r\n                   }\r\n               }\r\n           }\r\n       }\r\n   }\r\n\r\n\r\nMulti-node configuration\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nEach node should have ``'cluster-mode' = true`` enabled that you wish to connect to the cluster. They will automatically discover other nodes to connect to VIA Server to Server traffic. Nodes that are added or removed will be periodically updated.\r\n\r\nTraffic Control\r\n~~~~~~~~~~~~~~~~~~\r\n\r\nYou can customize the traffic going between clustered servers with a few options.\r\n\r\ncm-ht-traffic-throttling\r\n\r\nThis setting will control the number of bytes sent over non-user connections. Namely, Server to Server or S2S connections.\r\n\r\n.. code::\r\n\r\n   'cm-ht-traffic-throttling' = 'xmpp:25k:0:disc,bin:200m:0:disc'\r\n\r\nThe format is as follows: ``{traffic-type}:{maximum-traffic}:{max-lifespan-traffic}:{action}``\r\n\r\n**traffic-type**\r\n   Specifies the type of traffic controlled. This can either be **XMPP** or **bin**. XMPP limits the number of packets transferred, whereas bin limits the number of bytes transferred.\r\n\r\n**maximum-traffic**\r\n   Specifies how many bytes or packets may be sent within one minute.\r\n\r\n**max-lifespan-traffic**\r\n   Specifies how many bytes or packets may be sent within the lifetime of the connection. 0 means unlimited.\r\n\r\n**action**\r\n   Specifies the action to be taken which can be **disc** which disconnects the connection, or **drop** which will drop any data exceeding the thresholds.\r\n\r\ncm-see-other-host\r\n\r\nThis allows the specific use of a load balancing mechanism by selecting ``SeeOtherHostIfc`` implementation. For more details, see :ref:`Tigase Load Balancing<loadBalancing>` documentation.\r\n\r\nOld configuration method\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nWhile these options are still available these settings CAN be less reliable. **Use ONLY if you need specific setups that cannot be accommodated by the automatic cluster mode**.\r\n\r\n\r\nSpecifying Specific nodes\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nYou can still use the old method of specifying every node on each server. Server 3 needs the following set\r\n\r\n.. code::\r\n\r\n   'cluster-nodes' = [ 'serv1.xmpp-test.org' , 'serv2.xmpp-test.org' ]\r\n\r\nServer 2 needs\r\n\r\n.. code::\r\n\r\n   'cluster-nodes' = [ 'serv1.xmpp-test.org' , 'serv3.xmpp-test.org' ]\r\n\r\nand so on…​\r\n\r\nHowever, we do not recommend this.\r\n\r\nPassword and Port configuration\r\n\r\nYou may specify a password and port to specific cluster servers if that is required. To do so, you will need to add {password}:{port} to the domain, like this example:\r\n\r\n.. code::\r\n\r\n   'cluster-nodes' = [ 'serv1.xmpp-test.org:domainpass:5600' ]\r\n\r\n.. _Checking-Cluster-Connections:\r\n\r\nChecking Cluster Connections\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nAfter setting up clustering you may want to verify that the clusters are operational. Right now it can be done in two manners - first by checking that there are actual network connections established between cluster nodes. The other is to check internal status of the server.\r\n\r\nEstablished connections\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nThere are number of ways to check for opened connections, simplest one use command line. (Tigase uses port *5277* for cluster connections)\r\n\r\n-  Linux\r\n\r\n   .. code:: sh\r\n\r\n      $ lsof -iTCP:5277 -sTCP:ESTABLISHED -P -n\r\n\r\n-  Windows\r\n\r\n   .. code:: sh\r\n\r\n      C:\\WINNT>netstat -anp tcp | find \":5277 \"\r\n\r\n\r\nCluster nodes connected (using XMPP)\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nVerifying clustering connectivity over XMPP protocol requires any XMPP client capable of `XEP-0030: Service Discovery <http://xmpp.org/extensions/xep-0030.html>`__. It’s essential to remember that only an administrator (a user whose JID is configured as administrative) has access.\r\n\r\nPsi XMPP Client\r\n\r\nFor the purpose of this guide a `Psi <http://psi-im.org/>`__ client will be used. After successfully configuring and connecting to account with administrative privileges we need to access *Service Discovery*, either from application menu or from context menu of the particular account account:\r\n\r\n|roster-discovery|\r\n\r\nIn the *Service Discovery* window we need to find *Cluster Connection Manager* component. After expanding the tree node for the component a list of all cluster nodes will be presented with the current status (either *connected* or *disconnected*). Node column will contain actual hostname of the cluster node:\r\n\r\n|discovery-nodes|\r\n\r\n.. |roster-discovery| image:: /images/admin/monitoring_xmpp_1.png\r\n.. |discovery-nodes| image:: /images/admin/monitoring_clustering.png\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Using_Tigase/Debugging_Tigase.inc",
    "content": ".. _Debuging-Tigase:\r\n\r\nDebuging Tigase\r\n--------------------\r\n\r\nIf something goes wrong and you can’t find out why it is not working as expected, you might want more detailed debugging options switched on.\r\n\r\nTigase is a Java application and it uses Java logging library, this gives you the flexibility to switch logging on for selected Java packages or even for a single Java class.\r\n\r\nLogs files are stored in ``logs/`` directory. ``tigase-console.log`` stores basic log data, but is the main log file. ``tigase.log.N`` files keep all the detailed logging entries. So this is the place where you should look in case of problems.\r\n\r\nConfiguration\r\n^^^^^^^^^^^^^^^^\r\n\r\nBy default, Tigase has the old ``debug = ['server']`` setting is turned on and does not need to be added.\r\n\r\nHowever, people want to see what is going on the network level. That is what has been sent and what has been received by the server - the actual character data. The class which would print all received and sent character data is: ``tigase.xmpp.XMPPIOService``. To enable all debugging info for this class you have to modify the debug line:\r\n\r\n.. code::\r\n\r\n   debug = [ 'xmpp.XMPPIOService' ]\r\n\r\nYou can also have debugging switched on for many packages/classes at the same time:\r\n\r\n.. code::\r\n\r\n   debug = [ 'cluster' , 'xmpp.XMPPIOService' ]\r\n\r\nOther packages you might be interested in are:\r\n\r\n-  ``io`` can print out what is going on a very low level network level including TLS/SSL stuff.\r\n\r\n-  ``xml`` would print the XML parser debugging data.\r\n\r\n-  ``cluster`` would print all the clustering related stuff.\r\n\r\n-  ``xmpp.impl`` would print logs from all plugins loaded to Tigase server.\r\n\r\nNon-Tigase packages\r\n~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nTo enable logging for your own packages from those different than Tigase, you have to use another option which has been made available for this:\r\n\r\n.. code:: bash\r\n\r\n   debug-packages = [ your.com.package ]"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Using_Tigase/Intro.inc",
    "content": "Using Tigase\r\n==============\r\n\r\nThis section keeps set of documents which apply to all the Tigase server version and contain more generic or introductory information on general use and features.\r\n\r\n-  :ref:`Tigase Log Guide<Tigase-Log-Guide>`\r\n\r\n-  :ref:`Debugging Tigase<Debuging-Tigase>`\r\n\r\n-  :ref:`Basic System Checks<Basic-System-Checks>`\r\n\r\n-  :ref:`Add and Manage Domains<addManageDomain>`\r\n\r\n-  :ref:`Presence Forwarding<Presence-Forwarding>`\r\n\r\n-  :ref:`Watchdog<Watchdog>`\r\n\r\n   1. :ref:`Runtime Environment Tip<Tigase-Tip-Checking-the-Runtime-Environment>`\r\n\r\n   2. :ref:`Checking Cluster Connections<Checking-Cluster-Connections>`\r\n\r\n   3. :ref:`Best Practices for Connecting to Tigase XMPP server From Web Browser<Best-Practices-for-Connecting-to-Tigase-XMPP-server-From-Web-Browser>`\r\n\r\n-  :ref:`Scripting Support in Tigase<Scripting-support-in-Tigase>`\r\n\r\n   1. :ref:`Scripting Introduction - Hello World!<Scripting-Introduction - Hello-World!>`\r\n\r\n   2. :ref:`Tigase Scripting Version 4.4.x Update for Administrators<Tigase-Scripting-Version-4.4.x-Update-for-Administrators>`\r\n\r\n   3. :ref:`Tigase and Python Scripting<Tigase-and-Python>`\r\n\r\n-  :ref:`Configuration Wizards<tigase3xconfiguration>`\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Using_Tigase/LastActivity.inc",
    "content": "Last Activity\r\n----------------\r\n\r\nTigase XMPP Server supports `XEP-0012: Last Activity <https://xmpp.org/extensions/xep-0012.html>`__ extension, which allows retrieval information when particular contact was active last time. It’s not enabled by default.\r\n\r\nThe functionality itself is split in two plugins:\r\n\r\n-  ``jabber:iq:last-marker`` - responsible for updating information about last activity of user\r\n\r\n-  ``jabber:iq:last`` - responsible for handling requests to retrieve last activity information (it depends on ``jabber:iq:last-marker`` plugin).\r\n\r\nIn order to enable functionality you should add both plugins to your configuration file\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       'jabber:iq:last-marker' (active: true) {\r\n           'jabber:iq:last' (active: true) {}\r\n       }\r\n   }\r\n\r\nWhat updates last activity\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nBy default marker plugin will only update last activity information on presence stanza. It’s possible to control whether ``<presence/>`` and/or ``<message/>`` should update with respective options:\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       'jabber:iq:last-marker' (active: true) {\r\n           message = true\r\n           presence = true\r\n       }\r\n   }\r\n\r\nThose settings will cause updating last activity information for both ``<message/>`` and ``<presence/>`` stanzas\r\n\r\nPersist everything to repository\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTo lower impact on performance, by default last activity information is persisted to repository less frequently. This can yield slightly less accurate results on installations with multiple cluster nodes with users having multiple resources connected. To get more accurate results you should set ``persistAllToRepository`` to ``true``, which will cause all update times to be persisted (please bear in mind that this could cause higher impact on the repository).\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       'jabber:iq:last-marker' (active: true) {\r\n           persistAllToRepository = true\r\n       }\r\n   }"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Using_Tigase/Licensing.inc",
    "content": "Licensing\r\n--------------\r\n\r\nWith the release of v7.1.0, users and commercial clients alike may now be able to register and request a license file from our servers on their own. This process makes it easier for everyone to obtain valid license file when needed. Users who do not wish to register will not be required to register. However, If you are using Tigase ACS or other commercial pieces of software, you will be required to register.\r\n\r\n.. Warning::\r\n\r\n    Tigase XMPP Server will shut down during license check if no installation-id or license is received within a given period of time.\r\n\r\n**Again, Tigase XMPP Server will still be available free under AGPLv3, and free users will not need to register.**\r\n\r\n.. Note::\r\n\r\n   COMMERCIAL COMPONENTS REQUIRE THE USE OF A LICENSE.\r\n\r\nRegistering for a License\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThere are currently two ways for registering for a license with Tigase commercial products. **The easiest and recommended method is using the built in automatic registration function**. However, you may also register via a web portal if your installation has limitations on network connectivity.\r\n\r\n.. _AutomaticLicenceRegistration:\r\n\r\nAutomatic Registration (recommended)\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nOnce a commercial component is activated on Tigase XMPP Server, the program will then retrieve an *Installation ID* from our servers, and make a file called ``installation-id`` in your ``etc/`` directory including the *Installation ID* for your instance. An installation ID is generated using the complete cluster map and all machines within the same cluster should have the same *Installation ID*. This *Installation ID* will then be sent along with server details to a license server, and appropriate license files will be made in your *tigasedir/etc* directory. When the license is due to be expired, this mechanism will update your license file automatically.\r\n\r\nManual\r\n~~~~~~~~~~~\r\n\r\n.. Caution::\r\n\r\n   This method should be used only in extreme cases when :ref:`Automatic Registration (recommended)<AutomaticLicenceRegistration>` can’t be used.\r\n\r\nIf you do not wish to use the automatic method, you may decide to generate a license file using our web portal. Offline installation may obtain *Installation IDs* from our web portal in a three-step process: registration, generating hash, and obtaining license file.\r\n\r\nGenerating Installation ID\r\n\r\nFor offline installations, you may obtain an *Installation ID* from this address: https://license.tigase.software/register.\r\n\r\nData Fields:\r\n\r\n-  ``Customer name``: Company or user name used to identify machines. Multiple clusters or servers can have the same customer name.\r\n\r\n-  ``VHosts``: Comma separated list of VHosts you will be using on this node. NOTE: these fields are case sensitive!\r\n\r\n-  ``Legacy license hashes``: Copy the digest hash generated for all legacy licenses - it’s available in the ``etc/tigase-console.log`` after startup (if such licenses are present).\r\n\r\n-  ``Captcha question``: Enter the basic math answer for this form to prove you are not a robot.\r\n\r\nThe next page will provide you with an installation ID like the following:\r\n\r\n::\r\n\r\n   1TCICGG7K8AS2JSSEVMDA9QOLR4NVLJSR\r\n\r\nEdit your ``config.tdsl`` file and add your installation-id\r\n\r\n.. code::\r\n\r\n   'installation-id' = '1TCICGG7K8AS2JSSEVMDA9QOLR4NVLJSR'\r\n\r\nNote that the ``installation-id`` file will be made automatically once the license file is installed and verified by the server.\r\n\r\nObtaining a Server Code\r\n\r\nOnce you have the *Installation ID*, you will need to generate a server code. This can be done by accessing the admin UI page and navigating to the License section. Once there, click on Retrieve code for license. Select the component you wish to generate a code for and click Submit. You will see a fields with installation-id, module, VHosts filled out based on your server’s configuration. Copy the contents of the Code field and proceed to the next section.\r\n\r\nObtaining license file\r\n\r\nOpen a new browser and navigate to this address: https://license.tigase.software/retrieve once there, paste the generated code from the last step in the field and click submit. Afterwards you will be prompted to download a license file, place this file in your *etc/* folder and restart your server, your license is now activated and installed on your server.\r\n\r\n**If you are provided a manually produced license, you will need to place it in the same** ``etc/`` **directory with the name** ``<component_name>.license`` (**e.g.:** ``etc/acs.license``)\r\n\r\nWhat happens if I do not use a license file or it is expired?\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTigase permits commercial products to be used without a license, but a validation process must complete otherwise the server will shutdown. Within the first hour of runtime, Tigase will check for the presence and validity of the license file. If none is found, or it is invalid or expired the server will then contact Tigase master server in order to obtain a valid one.\r\n\r\nCommunications will be made to license.tigase.software over https (port 443) to verify the license or download a new one.\r\n\r\nDemo mode\r\n^^^^^^^^^^^\r\n\r\nIf no valid license can be found, Tigase will revert to a demonstration mode. Most functions will be available and usable, but with a caveat. Statistics from that server will be sent to https://stats.tigase.software about your server and it’s usage. Details are in the next section. If this information cannot be sent, the server will assume unauthorized use and will shut down.\r\n\r\nStatistics Sent\r\n~~~~~~~~~~~~~~~~~~~\r\n\r\nStatistics of your server may be sent to Tigase server’s if the all of following happens:\r\n\r\n-  You are using commercial Tigase components.\r\n\r\n-  You have registered an ``installation-id``.\r\n\r\n-  You do not have a current license to run Tigase commercial components.\r\n\r\nIf these conditions exist, statistics will be sent to our servers and a warning will be posted in your logs. The following is an example of what information will be sent.\r\n\r\n.. Note::\r\n\r\n   The text below has been better formatted for readability, but does not reflect the actual text being sent to Tigase.\r\n\r\n.. code:: xml\r\n\r\n   <statistics version=\"1\">\r\n       <domain>xmppserver</domain>\r\n       <timestamp>2016-06-23T17:16:24.777-0700</timestamp>\r\n       <vhosts>\r\n           <item>vhost1.xmppserver.com</item>\r\n       </vhosts>\r\n       <uptime>308833</uptime>\r\n       <heap>\r\n           <used>30924376</used>\r\n           <max>1426063360</max>\r\n       </heap>\r\n       <cluster>\r\n           <nodes_count>1</nodes_count>\r\n       </cluster>\r\n       <users>\r\n           <online>0</online>\r\n           <active>0</active>\r\n           <max_today>1</max_today>\r\n           <max_yesterday>0</max_yesterday>\r\n       </users>\r\n       <additional_data>\r\n           <components>\r\n               <cmpInfo>\r\n                   <name>amp</name>\r\n                   <title>Tigase XMPP Server</title>\r\n                   <version>7.1.0-SNAPSHOT-b4226/5e7210f6 (2016-06-01/23:15:52)</version>\r\n                   <class>tigase.cluster.AmpComponentClustered</class>\r\n               </cmpInfo>\r\n\r\n               <cmpInfo>\r\n                   <name>bosh</name>\r\n                   <title>Tigase XMPP Server</title>\r\n                   <version>7.1.0-SNAPSHOT-b4226/5e7210f6 (2016-06-01/23:15:52)</version>\r\n                   <class>tigase.cluster.BoshConnectionClustered</class>\r\n               </cmpInfo>\r\n\r\n               <cmpInfo>\r\n                   <name>c2s</name>\r\n                   <title>Tigase XMPP Server</title>\r\n                   <version>7.1.0-SNAPSHOT-b4226/5e7210f6 (2016-06-01/23:15:52)</version>\r\n                   <class>tigase.cluster.ClientConnectionClustered</class>\r\n               </cmpInfo>\r\n\r\n               <cmpInfo>\r\n                   <name>cl-comp</name>\r\n                   <title>Tigase XMPP Server</title>\r\n                   <version>7.1.0-SNAPSHOT-b4226/5e7210f6 (2016-06-01/23:15:52)</version>\r\n                   <class>tigase.cluster.ClusterConnectionManager</class>\r\n               </cmpInfo>\r\n\r\n               <cmpInfo>\r\n                   <name>eventbus</name>\r\n                   <title>Tigase XMPP Server</title>\r\n                   <version>7.1.0-SNAPSHOT-b4226/5e7210f6 (2016-06-01/23:15:52)</version>\r\n                   <class>tigase.disteventbus.component.EventBusComponent</class>\r\n               </cmpInfo>\r\n\r\n               <cmpInfo>\r\n                   <name>http</name>\r\n                   <title>Tigase HTTP API component: Tigase HTTP API component</title>\r\n                   <version>1.2.0-SNAPSHOT-b135/27310f9b-7.1.0-SNAPSHOT-b4226/5e7210f6 (2016-06-01/23:15:52)</version>\r\n                   <class>tigase.http.HttpMessageReceiver</class>\r\n               </cmpInfo>\r\n\r\n               <cmpInfo>\r\n                   <name>monitor</name>\r\n                   <title>Tigase XMPP Server</title>\r\n                   <version>7.1.0-SNAPSHOT-b4226/5e7210f6 (2016-06-01/23:15:52)</version>\r\n                   <class>tigase.monitor.MonitorComponent</class>\r\n               </cmpInfo>\r\n\r\n               <cmpInfo>\r\n                   <name>muc</name>\r\n                   <title>Tigase ACS - MUC Component</title>\r\n                   <version>1.2.0-SNAPSHOT-b62/74afbb91-2.4.0-SNAPSHOT-b425/d2e26014</version>\r\n                   <class>tigase.muc.cluster.MUCComponentClustered</class>\r\n                   <cmpData>\r\n                       <MUCClusteringStrategy>class tigase.muc.cluster.ShardingStrategy</MUCClusteringStrategy>\r\n                   </cmpData>\r\n               </cmpInfo>\r\n\r\n               <cmpInfo>\r\n                   <name>pubsub</name>\r\n                   <title>Tigase ACS - PubSub Component</title>\r\n                   <version>1.2.0-SNAPSHOT-b65/1c802a4c-3.2.0-SNAPSHOT-b524/892f867f</version>\r\n                   <class>tigase.pubsub.cluster.PubSubComponentClustered</class>\r\n                   <cmpData>\r\n                       <PubSubClusteringStrategy>class tigase.pubsub.cluster.PartitionedStrategy</PubSubClusteringStrategy>\r\n                   </cmpData>\r\n               </cmpInfo>\r\n\r\n               <cmpInfo>\r\n                   <name>s2s</name>\r\n                   <title>Tigase XMPP Server</title>\r\n                   <version>7.1.0-SNAPSHOT-b4226/5e7210f6 (2016-06-01/23:15:52)</version>\r\n                   <class>tigase.server.xmppserver.S2SConnectionManager</class>\r\n               </cmpInfo>\r\n\r\n               <cmpInfo>\r\n                   <name>sess-man</name>\r\n                   <title>Tigase XMPP Server</title>\r\n                   <version>7.1.0-SNAPSHOT-b4226/5e7210f6 (2016-06-01/23:15:52)</version>\r\n                   <class>tigase.cluster.SessionManagerClustered</class>\r\n                   <cmpData>\r\n                       <ClusteringStrategy>class tigase.server.cluster.strategy.OnlineUsersCachingStrategy\r\n                       </ClusteringStrategy>\r\n                   </cmpData>\r\n               </cmpInfo>\r\n\r\n               <cmpInfo>\r\n                   <name>ws2s</name>\r\n                   <title>Tigase XMPP Server</title>\r\n                   <version>7.1.0-SNAPSHOT-b4226/5e7210f6 (2016-06-01/23:15:52)</version>\r\n                   <class>tigase.cluster.WebSocketClientConnectionClustered</class>\r\n               </cmpInfo>\r\n\r\n               <cmpInfo>\r\n                   <name>vhost-man</name>\r\n                   <title>Tigase XMPP Server</title>\r\n                   <version>7.1.0-SNAPSHOT-b4226/5e7210f6 (2016-06-01/23:15:52)</version>\r\n                   <class>tigase.vhosts.VHostManager</class>\r\n               </cmpInfo>\r\n\r\n               <cmpInfo>\r\n                   <name>stats</name>\r\n                   <title>Tigase XMPP Server</title>\r\n                   <version>7.1.0-SNAPSHOT-b4226/5e7210f6 (2016-06-01/23:15:52)</version>\r\n                   <class>tigase.stats.StatisticsCollector</class>\r\n               </cmpInfo>\r\n\r\n               <cmpInfo>\r\n                   <name>cluster-contr</name>\r\n                   <title>Tigase XMPP Server</title>\r\n                   <version>7.1.0-SNAPSHOT-b4226/5e7210f6 (2016-06-01/23:15:52)</version>\r\n                   <class>tigase.cluster.ClusterController</class>\r\n               </cmpInfo>\r\n           </components>\r\n\r\n           <unlicencedComponenents>\r\n               <ComponentAdditionalInfo name=&quot;acs&quot;/>\r\n           </unlicencedComponenents>\r\n       </additional_data>\r\n   </statistics>\r\n\r\nUnauthorized use\r\n^^^^^^^^^^^^^^^^^^^^\r\n\r\nTigase will consider itself unauthorized if the following conditions are met:\r\n\r\n-  if Tigase XMPP Server does not have a valid license file and\r\n\r\n-  cannot contact the licensing server to obtain installation id and attached licenses.\r\n\r\nThen the program will then attempt to send statistics.\r\n\r\n-  if unable to sent statistics the server after a random number of retries.\r\n\r\n-  if these retries are not successful within 10 attempts, the server will then shutdown.\r\n\r\nIf you are experiencing this condition, please contact Tigase.\r\n\r\nManual mode\r\n^^^^^^^^^^^^^^^^\r\n\r\nIf you cannot open communication to ``stats.tigase.software`` or ``license.tigase.software`` over the required ports (https over port 443), you may request to use manual mode. Manual mode requires Tigase to create a license file to be used on your machine locally. This must be placed in the same folder as the above information, and the license check system will not seek communication unless the license is invalid or expired.\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Using_Tigase/Log_Guide.inc",
    "content": ".. _Tigase-Log-Guide:\r\n\r\nTigase Log Guide\r\n--------------------\r\n\r\nTigase has multiple levels of logging available to help provide targeted and detailed information on processes, components, or traffic. In these documents we will look at where tigase generates logs, what they contain, and how we can customize them to our needs.\r\n\r\ninstall.log\r\n^^^^^^^^^^^^^^^\r\n\r\nThis log file is a basic list of files that are made on install of Tigase server. Although you may not need to use it, it can provide a handy list to see if any files were not written to your hard drive upon installation.\r\n\r\nderby.log\r\n^^^^^^^^^^^^^^^\r\n\r\nIf you are using the derby database installed with Tigase, this is the startup log for the database itself. Issues that might be related to the database, can be found in this file. Typically, if everything works okay, it’s a very small file with only 10 lines. It is overwritten on startup of the database.\r\n\r\netc/config-dump.properties\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThe config-dump.properties is dump file of all your properties listed for every option within Tigase and components. The structure of the log lines is the same as the structure of Tigase XMPP Server config file - TDSL. Lets take the value for admins, listing who is administrator for the server.\r\n\r\n::\r\n\r\n   admins = [ 'admin@jabber.freehost.org', 'administrator@jabber.freehost.org', 'fJones@jabber.freehost.org' ]\r\n\r\nThe admin parameter which is an array of strings and has 3 users listed.\r\n\r\nThis file is re-written every time tigase starts.\r\n\r\nlogs/tigase.log.#\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThe tigase.log files are where the majority of logging will take place. The rules for writing to these longs can be manipulated by editing files in the int.properties file. To see how, see the :ref:`Debugging Tigase<Debuging-Tigase>` section of this manual for more details about how to turn on debug logging, and how to manipulate log settings. Entries to these logs are made in the following format:\r\n\r\n::\r\n\r\n   2015-08-10 13:09:41.504 [main]      Sctipr.init()         INFO: Initilized script command, id: comp-manager, lang:Groovy, ext: groovy\r\n\r\nThe format of these logs is below: ``<timestamp> <thread_name> <class>.<method>    <log_level>: <message> <thread_name>``. This can vary - for components it would be ``<direction>_<int>_<component name>``, for plugins it will just be the plugin name.\r\n\r\nLet’s look at another example from the log file.\r\n\r\n::\r\n\r\n   2015-08-10 12:31:40.893 [in_14_muc] InMemoryMucRepository.createNewRoom()   FINE: Creating new room 'chatroom@muc.localhost.com'\r\n\r\nThe process ID may sometimes come in a different format such as ``[in_14-muc]`` which specifies the component (muc) along with the process thread identifier (14). As you can see, the format otherwise is nearly identical.\r\n\r\n``tigase.log.#`` files are *rotated* - this means that server begins writing to tigase.log.0 when it is first run, and continues to dump information until the log size limit is hit. At this point, Tigase renames tigase.log.0 as tigase.log.1. A new tigase.log.0 will be created, and Tigase will begin logging to this file. When this file is full, tigase.log.1 will be renamed tigase.log.2 and tigase.log.0 will be renamed tigase.log.1. Using this scheme, tigase.log.0 will **always** be your most recent log.\r\n\r\nBy default, Tigase has a limit of 10000000 bytes or 10MB with a file rotation of 10 files. You can edit these values by editing the ``config.tdsl`` file and adding the following lines.\r\n\r\n.. code::\r\n\r\n   logging {\r\n       java.util.logging.FileHandler {\r\n           count = '15'\r\n           limit = '20000000'\r\n       }\r\n   }\r\n\r\nThis code, if entered into the ``config.tdsl`` file increases the size of the files to 15, and enlarges the maximum size to 20MB. Note the larger the collective log space is, the larger number of sectors on hard disk are active. Large log blocks may impact system performance.\r\n\r\n*You may see a tigase.log.0.lck file in the directory while the server is running. This is a temporary file only and is deleted once Tigase is cleanly shut down.*\r\n\r\nlogs/statistics.log.#\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nStatistics log will duplicate any information that is related to sending of statistics to Tigase if you are using an unlicensed copy of Tigase XMPP server. Mainly it will consist output of LicenceChecker. The numbering logic will be the same as ``tigase.log.#`` files.\r\n\r\nlogs/tigase.pid\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n``tigase.pid`` is a file that just contains the Process ID or PID for the current run of Tigase. It is only valid for the current or most recent run cycle and is overwritten every time Tigase starts.\r\n\r\nlogs/tigase-console.log\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n.. Important::\r\n\r\n   This is the most important log file containing the most essential information related to operation of the Tigase XMPP Server. Any errors or exceptions in this file indicate with high probability serious issues with server operation.\r\n\r\nThis file contains information related to Tigase’s running environment, and is a dump from the server itself on what is being loaded, when, and if any issues are encountered. It will start by loading Java classes (consequently making sure the Java environment is present and functioning). Then it will begin loading the configuration file, and adding default values to settings that have not been customized. You can then see all the components being loaded, and settings added where default values are needed. Lastly you will see a log of any plugins that are loaded, and any parameters therein. You may see tags such as INFO or WARNING in the logs. Although they may contain important information, the program will continue to operate as normal are not of too great concern.\r\n\r\nERROR flags are issues you will want to pay attention as they may list problems that prevent Tigase or components from properly functioning.\r\n\r\n.. Note::\r\n\r\n   Windows does not create this file, rather the output is shown in the command line and is not dumped to a file.\r\n\r\nIf Tigase is gracefully shut down, tigase-console.log will add statistics from the server’s operation life in the following format.\r\n\r\n::\r\n\r\n   component/statistic = value\r\n\r\n*Any component that may have a statistic, whether used or not, will place a value here*\r\n\r\nThis file can be handy if you are tracking issues in the server.\r\n\r\ntigase-console.log is appended during each run session of the server.\r\n\r\nLog File Location\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nYou can also change the location of log files if you have a specific directory you wish to use. The configuration may be made by the following lines in your ``config.tdsl`` file:\r\n\r\n.. code::\r\n\r\n   logging {\r\n       java.util.logging.FileHandler {\r\n           pattern = '/var/log/tigase/tigase.log'\r\n       }\r\n   }\r\n\r\nThis setting changes the log file location to /var/log/tigase/ where all log files will be made. Files in the original location will be left."
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Using_Tigase/Offline_Messages.inc",
    "content": "Offline Messages\r\n---------------------\r\n\r\nTigase like any XMPP server supports storing of messages for users who are offline so that they may receive messages sent to them while they were not logged in.\r\n\r\nBy default, Tigase ``MessageAmp`` processor is responsible for storing offline messages, and will automatically store offline messages. This guide has multiple sections for setting limits globally, per user, and others.\r\n\r\nMany of the features listed here require the use of the Advanced Message Processor Plugin which is turned on by default. To ensure AMP is turned on your system, view your ``config.tdsl`` file and be sure the following is there in your plugins line:\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       amp () {}\r\n   }\r\n\r\nMessages will be delivered to intended recipients when they first login after roster exchange.\r\n\r\nOffline Message Limits\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nSupport for limiting number of stored offline messages on a per-user basis has now been added to Tigase as of v7.1.0. By default, Tigase comes with a limit of stored offline messages which is set for every user. This limit by default is 100 offline messages for barejid-barejid pair. This value can be changed by the ``store-limit`` property. To change to 200 messages on barejid-barejid paid, add the following entries to the ``config.tdsl`` file:\r\n\r\n.. code::\r\n\r\n   amp {\r\n       'store-limit' = 200L\r\n   }\r\n   'sess-man' {\r\n       amp () {\r\n           'store-limit' = 200L\r\n       }\r\n   }\r\n\r\nThis setting applies to every user.\r\n\r\nUser Limit\r\n~~~~~~~~~~~~~~~~~\r\n\r\nEach user is able to configure the number of offline messages which should be stored for him. To enable this feature, the following lines need to be entered into the ``config.tdsl`` file:\r\n\r\n.. code::\r\n\r\n   amp {\r\n       'user-store-limit-enable' = true\r\n   }\r\n   'sess-man' {\r\n       amp () {\r\n           'user-store-limit-enable' = true\r\n       }\r\n   }\r\n\r\nValues of user-specific limits will be stored in UserRepository under subnode of ``offline-msgs`` and key ``store-limit``. Data storage will be stored in ``tig_pairs`` key with the value and a proper record from ``tig_nodes`` points to this record.\r\n\r\nHandling of Offline Messages Exceeding Limits\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nThere are two possible ways to handle offline messages that exceed the limitations: . ``error`` sending message with error type back to sender. . ``drop`` drop of message without notifications to sender.\r\n\r\nBy default, Tigase sends a message back to the original sender with an error type of ``service-unavailable`` with a proper description of error according to `XEP-0160 <http://www.xmpp.org/extensions/xep-0160.html>`__. However, it is possible to change this behavior to better suit your needs. This is done by adding the following line to your ``config.tdsl`` file.\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       amp () {\r\n           'quota-exceeded' = 'drop'\r\n       }\r\n   }\r\n\r\nThis will force Tigase to drop packets that exceed the offline message limit.\r\n\r\nSetting the Limits by User\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nUsers wishing to set a custom limit of stored offline messages for barejid-barejid pairs needs to send the following XMPP stanza to the server:\r\n\r\n.. code:: xml\r\n\r\n   <iq type=\"set\" id=\"${random-id}\">\r\n     <msgoffline xmlns=\"msgoffline\" limit=\"${limit}\"/>\r\n   </iq>\r\n\r\nWhere: . ``${random-id}`` is a random ID of the stanza (can be any string). . ``${limit}`` is the integer value of the offline message limit. This can be set to ``false`` to disable offline message limits.\r\n\r\nIn response, the server will send back an ``iq`` stanza with a result type:\r\n\r\n.. code:: xml\r\n\r\n   <iq type=\"result\" id=\"${random-id}\">\r\n     <msgoffline xmlns=\"msgoffline\" limit=\"${limit}\"/>\r\n   </iq>\r\n\r\nExample of Setting Limit of Stored Offline Messages to 10\r\n\r\nXMPP client sends the following to the server:\r\n\r\n.. code:: xml\r\n\r\n   <iq type=\"set\" id=\"aabba\">\r\n     <msgoffline xmlns=\"msgoffline\" limit=\"10\"/>\r\n   </iq>\r\n\r\nServer response:\r\n\r\n.. code:: xml\r\n\r\n   <iq type=\"result\" id=\"aabba\">\r\n     <msgoffline xmlns=\"msgoffline\" limit=\"10\"/>\r\n   </iq>\r\n\r\nExample of Disabling Offline Message Limit\r\n\r\nXMPP client sends the following to the server:\r\n\r\n.. code:: xml\r\n\r\n   <iq type=\"set\" id=\"aabbb\">\r\n     <msgoffline xmlns=\"msgoffline\" limit=\"false\"/>\r\n   </iq>\r\n\r\nServer response:\r\n\r\n.. code:: xml\r\n\r\n   <iq type=\"result\" id=\"aabbb\">\r\n     <msgoffline xmlns=\"msgoffline\" limit=\"false\"/>\r\n   </iq>\r\n\r\nStoring offline messages without body content\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTigase can now store offline messages without ``<body/>`` content.\r\n\r\nSee `XEP-0334 <http://xmpp.org/extensions/xep-0334.html>`__ for protocol details.\r\n\r\nThis can include message receipts, and messages with specific ``do-not-store`` tags.\r\n\r\nSupport has been added to set a list of paths and xmlns to trigger and place storage of offline messages using the following settings in ``config.tdsl``:\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       amp () {\r\n           'msg-store-offline-paths' = [ '/message/received[urn:xmpp:receipts]', '/message/store-offline' ]\r\n       }\r\n   }\r\n\r\nThis example results in two settings:\r\n\r\n``/message/received[urn:xmpp:receipts]``\r\n   Results in storage of messages with a ``recieved`` subelement and with the xlmns set to ``urn:xmpp:receipts``\r\n\r\n``/message/store-offline``\r\n   Results in storing messages with a ``store-offline`` subelement without checking xmlns.\r\n\r\nFiltering of offline storage\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nIt is possible to set storage of other types to save:\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       amp () {\r\n           'msg-store-offline-paths' = [ '/message/store-offline', '-/message/do-not-store' ]\r\n       }\r\n   }\r\n\r\nThe above setting in the ``config.tdsl`` file will cause that:\r\n\r\n-  messages with ``<store-offline>`` subelement will be stored without checking for associated xmlns.\r\n\r\n-  messages with ``<do-not-store>`` element **will not** be saved.\r\n\r\nAny of these can be adjusted for your installation, remember that a '-' will stop storage of messages with the indicated property. Messages will be checked by these matchers and if any of them result in a positive they will override default settings.\r\n\r\nFor example, if you wanted to store messages with <received> element, but not ones with <plain> element, your filter will look like this:\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       amp () {\r\n           'msg-store-offline-paths' = [ '/message/received', '-/message/plain' ]\r\n       }\r\n   }\r\n\r\nHowever…​.\r\n\r\n.. Note::\r\n\r\n   **THE ABOVE STATEMENT WILL NOT WORK** As it will just store all messages with <received> subelement.\r\n\r\nThe below statement will properly filter your results.\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       amp () {\r\n           'msg-store-offline-paths' = [ '-/message/plain', '-/message/received' ]\r\n       }\r\n   }\r\n\r\nFiltering logic is done in order from left to right. Matches on the first statement will ignore or override matches listed afterwards.\r\n\r\nDisabling Offline Messages\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIf you wish to disable the storing of offline messages, use the following line in your ``config.tdsl`` file. This will not disable other features of the AMP plugin.\r\n\r\n.. code::\r\n\r\n   'sess-man' {\r\n       amp () {\r\n           msgoffline (active: false) {}\r\n       }\r\n   }"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Using_Tigase/Presence_Forwarding.inc",
    "content": ".. _Presence-Forwarding:\r\n\r\nPresence Forwarding\r\n------------------------\r\n\r\nHave you ever thought of displaying your users presence status on the website? Or, maybe, you wanted to integrate XMPP service with your own system and share not only users' accounts but also presence status?\r\n\r\nNot only is it possible but also very simple. You have a new option in the domain control form.\r\n\r\nActually there are 2 new options:\r\n\r\n1. Presence forward address\r\n\r\n2. Message forward address - not fully implemented yet\r\n\r\nPresence forward address can be any XMPP address. Usually you want it to be a bot address which can collect your users' presence information. Once this option is set to a valid XMPP address Tigase forwards user’s presence, every time the user changes his status. The presence is processed normally, of course, and distributed to all people from the contact list (roster), plus to this special address. It can be a component or a bot. If this is a bot connecting to a regular XMPP account, **Make sure the presence forward address contains resource part and the bot is connecting with this resource.** Otherwise the presence won’t be delivered to the bot.\r\n\r\n|vhost presence forward|\r\n\r\nAs the screenshot shows, there are new input lines with option for presence forwarding address and message forwarding address. As you can see this option can be specified separately for each domain, so you can have a different forward address for each domain.\r\n\r\nIf you have your own Tigase installation, the forwarding address can be also set globally and can be the same for all domains. However, for this website, we offer this feature to all our users who have own domains and this can be set on per-domain basis.\r\n\r\nNow, the big question. How this can be used? I am attaching below an example code. With just a few lines of code you can connect a command line bot to the server as a client which would collect all presences from users. Code below is a simple Groovy script which receives presence packet and displays them on the console. However, it should be easy enough to store users' presence information in a database and then load it from a web application.\r\n\r\nThe bot/client uses our `JaXMPP2 <https://github.com/tigase/jaxmpp>`__ library which is included in current builds of Tigase XMPP Server.\r\n\r\nYou should be able to find a few more code examples on the wiki page.\r\n\r\n.. code:: groovy\r\n\r\n   package jaxmppexample\r\n\r\n   import tigase.jaxmpp.core.client.BareJID\r\n   import tigase.jaxmpp.core.client.SessionObject\r\n   import tigase.jaxmpp.core.client.exceptions.JaxmppException\r\n   import tigase.jaxmpp.core.client.observer.Listener\r\n   import tigase.jaxmpp.core.client.xmpp.modules.presence.PresenceModule\r\n   import tigase.jaxmpp.core.client.xmpp.modules.presence.PresenceModule.PresenceEvent\r\n   import tigase.jaxmpp.j2se.Jaxmpp\r\n\r\n   final Jaxmpp jaxmpp = new Jaxmpp()\r\n   jaxmpp.getProperties().setUserProperty( SessionObject.USER_BARE_JID,\r\n     BareJID.bareJIDInstance( \"test4@test.tigase.org\" ) )\r\n   jaxmpp.getProperties().setUserProperty(SessionObject.RESOURCE, \"presence-collector\")\r\n   jaxmpp.getProperties().setUserProperty( SessionObject.PASSWORD, \"pass\" )\r\n   jaxmpp.getModulesManager().getModule( PresenceModule.class ).addListener(\r\n     PresenceModule.ContactChangedPresence,  new Listener() {\r\n       public void handleEvent( PresenceEvent be ) {\r\n         def msg = (be.getStatus() != null) ? be.getStatus() : \"none\"\r\n         println( \"Presence received:\\t\" + be.getJid() + \" is now \" + be.getShow() +\r\n           \" (\" + msg + \")\" )\r\n       }\r\n     }\r\n   )\r\n   println( \"Loging in...\" )\r\n   jaxmpp.login()\r\n   println( \"Waiting for the presence for 10 minutes\" )\r\n   Thread.sleep( 10 * 60 * 1000 )\r\n   disconnect()\r\n\r\n.. |vhost presence forward| image:: /images/admin/vhost-presence-forward.png"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Using_Tigase/PubSub_Server_Information.inc",
    "content": "PubSub Server Information\n----------------------------\n\nTigase offers support for `PubSub Server Information extension <https://github.com/xsf/xeps/pull/1312>`_ of the XMPP. This feature allows your server to expose its server information over Pub/Sub allowing it to be included ie. in the XMPP network graph.\n\nThis plugin, if enabled, announces support for PubSub Server Information extension and acts as opt-in.\n\nIn the future this feature will be extended to expose additional server information, ie. currently open server-to-server connections (only made to servers that also opted in), etc.\n\nEnabling feature\n^^^^^^^^^^^^^^^^^^\n\nTo enable this feature, you need to modify ``config.tdsl`` file by adding folliwing line ``'urn:xmpp:serverinfo:0' () {}`` to the ``'sess-man'`` section of the config file.\n\nYour ``'sess-man'`` section, should include this new line, like in the following example:\n\n.. code::\n\n   'sess-man' {\n       'urn:xmpp:serverinfo:0' () {}\n   }\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Using_Tigase/Scripting/Introduction.inc",
    "content": ".. _Scripting-Introduction - Hello-World!:\r\n\r\nScripting Introduction - Hello World!\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThis document is the first in a series describing scripting support in the Tigase server showing how to load, install, update and call a script. It contains also an introduction to the scripting API with the first *Hello world!* example.\r\n\r\nSince Tigase version 4.3.1 the server supports scripting for administrator commands as well as standard commands.\r\n\r\nIn theory many different languages can be used to write scripts and the only requirement is that support `JSR-223 <http://www.jcp.org/en/jsr/detail?id=223>`__ for the language is installed. More details can be found on the `Java scripting project site <https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/prog_guide/api.html>`__.\r\n\r\nIn practice some languages are better supported than others, at the moment we recommend `Groovy <http://groovy-lang.org/>`__. However the following languages are also confirmed to be working: `Scala <http://www.scala-lang.org/>`__, `Python <http://www.python.org/>`__ and `Ruby <http://www.ruby-lang.org/>`__. The `tigase-server GitHub <https://github.com/tigase/tigase-server/blob/master/src/main>`__ contains a few examples for these languages.\r\n\r\n.. Note::\r\n\r\n   the default Tigase installation contains only libraries for Groovy. Adding support for a different language is as simple as copying a few JAR files to the Tigase ``libs/`` directory.\r\n\r\nAll the examples presented in this guide are also available as ready to use scripts in the Tigase SVN repository in directory: `src/main/groovy/tigase/admin <https://github.com/tigase/tigase-server/blob/master/src/main/groovy/tigase/admin>`__.\r\n\r\nThe scripting utilizes only standard XMPP extensions and is by no means specific to any particular solution. We use and prefer Psi client. The whole guide and all the screen-shots are created using Psi client. You can, however, use any other client which supports these extensions as well. As the whole thing is based on the service discovery and ad-hoc commands you need a XMPP client with a good support for both features.\r\n\r\nTo follow the guide and run all the examples you need will need to have installed Tigase server version 4.3.1 or newer and you have to connect to the server as administrator.\r\n\r\nLoading Script at Run Time\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nAll the scripting stuff is usually based on the service discovery and ad-hoc commands in the Tigase server.\r\n\r\n|service disco|\r\n\r\nThe first thing to do, therefore, is to browse service discovery on the running server. The result you receive will depend on your installation and installed components.\r\n\r\nThe most interesting things right now are all items with \"http://jabber.org/protocol/admin\" in their node part. You may have a few scripts loaded already but there are two commands used for scripting management. Their names are descriptive enough: ``New command script`` and ``Remove command script``.\r\n\r\nThe first is for adding a new script or updating existing and the second is for removing script from the server.\r\n\r\nTo add a new script you have just to execute ``New command script``. In Psi this is done by double clicking on the element in service discovery list.\r\n\r\n|hello1 new script|\r\n\r\nThe screenshot above shows a couple of options to set for the loaded script:\r\n\r\nDescription\r\n   is what shows as the script name in the service discovery window. There are no special restrictions on what to put there.\r\n\r\nCommand id\r\n   is a unique ID of the script (admin command). This is what shows after the \"http://jabber.org/protocol/admin\" in node part. This needs to be unique or existing script is overwritten.\r\n\r\nLanguage\r\n   a drop down list of all supported scripting languages for your installation. Tigase automatically detects all libraries for scripting languages and lists them here. So all you need is to select the correct language for your script.\r\n\r\nScript text\r\n   is just your script content.\r\n\r\nWhen your script is ready and all fields are correctly set, simply press \"**Finish**\" button and you should receive a message confirming that the script has been loaded successfully.\r\n\r\n|loaded ok small|\r\n\r\nIn this guide we are creating a simple \"Hello world\" script written in Groovy. What it does is displays a window (ad-hoc command result) with a message: \"*Hello admin, how are you?*\".\r\n\r\nIt uses a basic scripting API which is described line by line below:\r\n\r\n1. It imports basic Tigase classes.\r\n\r\n2. Sets a local variable ``p`` which points to a ``packet`` variable with data received from the client.\r\n\r\n3. Creates a ``res`` variable which is response sent back to the client (administrator). The response to the client is of type ``result``. Other possible types will be introduced later.\r\n\r\n4. We operate on ad-hoc commands here so the script uses Tigase utility class to set/retrieve command parameters. It sets the window title and a simple message displayed to the user (administrator).\r\n\r\n5. The last line returns new packet as a script execution result.\r\n\r\nThe first, very simple version looks like this:\r\n\r\n.. code:: groovy\r\n\r\n   import tigase.server.*\r\n   def p = (Packet)packet\r\n   def res = p.commandResult(Command.DataType.result)\r\n   Command.addTitle(res, \"Hello World Script\")\r\n   Command.addInstructions(res, \"Hello admin, how are you?\")\r\n   return res\r\n\r\nExecuting Script\r\n~~~~~~~~~~~~~~~~~~~~~\r\n\r\nOnce the script is successfully loaded you will have to reload/refresh the service discovery window which now should display one more element on the list.\r\n\r\n|service disco with new hello|\r\n\r\nAs you can see script name is set to what you have entered as \"Description\" in script loading window - \"*Hello world script*\". The command node is set to: \"http://jabber.org/protocol/admin#hello\" if \"**hello**\" is what is set as the script ID.\r\n\r\nTo execute the script you just have to double click on the script name (or click execute command if you use any other client).\r\n\r\nAs a result you should see a simple window similar to the screenshot below displaying our message.\r\n\r\n|hello1 result small|\r\n\r\nInteraction in Scripts\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nDisplaying just a message is very nice but is not very useful in most cases. Normally you need to ask the user for some more data or parameters before you can perform any real processing.\r\n\r\nTherefore in most cases the administrator script has to display a new window with input fields asking the user for some more data. In this document we present very simple examples, just an introduction so let’s ask about the administrator name before displaying a greeting.\r\n\r\n|hello2 asking for name small|\r\n\r\nTo ask the user for some more information we have to extend example above with some more code:\r\n\r\n.. code:: groovy\r\n\r\n   import tigase.server.*\r\n\r\n   def p = (Packet)packet\r\n\r\n   def name = Command.getFieldValue(packet, \"name\")\r\n\r\n   if (name == null) {\r\n     def res = p.commandResult(Command.DataType.form);\r\n     Command.addTitle(res, \"Hello World Script\")\r\n     Command.addInstructions(res, \"Please provide some details\")\r\n     Command.addFieldValue(res, \"name\", name ?: \"\", \"text-single\",\r\n       \"Your name\")\r\n     return res\r\n   }\r\n\r\n   def res = p.commandResult(Command.DataType.result)\r\n   Command.addTitle(res, \"Hello World Script\")\r\n   Command.addInstructions(res, \"Hello ${name}, how are you?\")\r\n\r\n   return res\r\n\r\nIf you compare both scripts you see that they are quite similar. Before displaying greeting, however, the script tries to retrieve data from the ``name`` input field. If the name had been provided the greeting is displayed, otherwise the script asks for the user name.\r\n\r\n|hello2 result small|\r\n\r\nPlease note, in this case the packet sent back to the user is of type form instead of ``result``. The practical difference is that the type ``result`` displays only **OK** button which when pressed doesn’t send any data to the server. The form packet displays more buttons - **Finish** and **Cancel**. Whichever you press some data is sent back to the server.\r\n\r\nThis script demonstrates use of two new methods from the utility class \"Command\": getFieldValue and addFieldValue.\r\n\r\n-  The first argument to all Command methods is the packet with ad-hoc command.\r\n\r\n-  The second argument is usually the input field name\r\n\r\nThese two method parameters are actually enough to read the ad-hoc command data. Methods creating input fields in the ad-hoc command need a few arguments more:\r\n\r\n-  Next arguments sets a default value displayed to the user. The way to it is set in the example above is specific to Groovy language and is quite useful what will be apparent in later examples.\r\n\r\n-  After that we have to specify the field type. All field types are defined in the `XEP-0004 <http://xmpp.org/extensions/xep-0004.html#protocol-fieldtypes>`__ article.\r\n\r\n-  The last argument specifies the field label which is displayed to the user.\r\n\r\n|hello2 new script|\r\n\r\nThere are a few other different utility methods in the Command class to set different types of input fields and they will be described in details later on.\r\n\r\nTo reload the script simply call \"New command script\" again, enter the script text and make sure you entered exactly the same command ID to replace the old script with the new one.\r\n\r\nOr of course, you can enter a new command id to create a new command and make it available on your server.\r\n\r\nWhen the script is loaded on the server, try to execute it. You should get a new dialog window asking for your name as in the screenshot at the beginning of this section. When you have entered your name and clicked the \"Finish\" button you will see another window with a greeting message along with your name.\r\n\r\nAutomatic Scripts Loading at Startup Time\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nThe last thing described in this guide is how to automatically load your scripts when the Tigase server starts. The ability to load scripts at run time, update and remove remove them is very useful, especially in emergency cases if something wrong is going on and you want to act without affecting the service.\r\n\r\nIf you, however have a few dozens scripts you don’t want to manually load them every time the server restarts.\r\n\r\nTigase server automatically loads all scripts at the startup time which are located in the admin scripts directory. Unless you set it differently in the configuration it is: **YourTigaseInstallationDir/scripts/admin/**. All you have to do is to copy all your scripts to this directory and they will be loaded next time the server starts.\r\n\r\nBut hold on. What about the script parameters: language, description, command id? How are you supposed to set them?\r\n\r\nLanguage is simple. It is detected automatically by the script file extension. So just make sure file extensions are correct and the language is sorted.\r\n\r\nThe script description and command id needs a little bit more work. You have to include in your script following lines:\r\n\r\n::\r\n\r\n   AS:Description: The command description\r\n   AS:CommandId: command-id\r\n   AS:Component: comp_name\r\n\r\nPlease note, there must be at least a single space after the ``AS:Description:`` or ``AS:CommandId:`` string. Everything rest after that, until the end of the line, is treated as either the script description or command id. Put these in your script file and the loader will detect them and set correctly for your script.\r\n\r\n.. |service disco| image:: /images/admin/service-disco.png\r\n.. |hello1 new script| image:: /images/admin/hello1-new-script.png\r\n.. |loaded ok small| image:: /images/admin/loaded-ok-small.png\r\n.. |service disco with new hello| image:: /images/admin/service-disco-with-new-hello.png\r\n.. |hello1 result small| image:: /images/admin/hello1-result-small.png\r\n.. |hello2 asking for name small| image:: /images/admin/hello2-asking-for-name-small.png\r\n.. |hello2 result small| image:: /images/admin/hello2-result-small.png\r\n.. |hello2 new script| image:: /images/admin/hello2-new-script.png\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Using_Tigase/Scripting/Python.inc",
    "content": ".. _Tigase-and-Python:\r\n\r\nTigase and Python\r\n^^^^^^^^^^^^^^^^^^^^\r\n\r\nThis article describes how to get Python working as a scripting language for ad-hoc commands in Tigase server. The first part is installation, and the second shows a few code examples with explanation of the differences between Python usage and some other languages.\r\n\r\n*Please note, we are not a Python developer, and by no means this is Python development guide. All the code examples are used only to present the API available and there are certainly better ways to do it in the proper Python style. If you have any suggestions or have a better code examples I am happy to include them in the guide.*\r\n\r\nInstallation\r\n~~~~~~~~~~~~~~~\r\n\r\nIn short, installation is extremely simple: just copy the file attached to this article to your Tigase installation, to the ``libs/`` directory. Restart the server and you are ready to start scripting and executing Python.\r\n\r\nIn theory the Tigase offers scripting support defined in `JSR-223 <http://www.jcp.org/en/jsr/detail?id=223>`__. You can use any language for which there is such support for JVM. This includes also stand-alone python implementations and the JSR-223 plugins acts just as a bridge. This, however, does not make much sense as you are not able to interact with JVM code (Tigase API). Therefore you need a language which is executed within JVM and can easily exchange data between the main application (Tigase server) and the script.\r\n\r\n|lang list no python small|\r\n\r\nThe best way to go is to use Jython implementation. It works very well within JVM and more importantly, perfectly integrates with Tigase server. Tigase server is tested with **Jython-2.2.1** and is confirmed to work fine. Version **Jython-2.5.1** is recommended however, and all the examples are executed with this version installed. Please note, *Jython-2.5.0* does not work at all. Both supported versions can be downloaded from the `Jython website <http://wiki.python.org/jython/DownloadInstructions>`__.\r\n\r\n**Version 2.5.1** is a bit simpler to install. When you download and run the Jython installer, find ``jython.jar`` file in the directory where you installed Jython. Copy the file to the Tigase’s **libs/** directory and all is ready to go. Please note, this is the same file as the one attached to this article for your convenience.\r\n\r\n**Version 2.2.1** needs a little bit more work. The first part is the same. It is not, however enough to copy the ``jython.jar`` file. One more file is necessary for the Jython to work with the Tigase server. You have to install JSR-223 engine separately. The binary file has to be unpacked and ``jython-engine.jar`` file needs to be copied to the Tigase ``libs/`` directory.\r\n\r\nThe best way to check if the Jython is installed correctly and support for Python is enabled, is by trying to submit a new script to the Tigase server. Browser the server service discovery, select \"*Session manager*\" component and run \"*Execute command*\" function. A new window should show with a list of all available ad-hoc commands. Select \"*New command script*\" item and click \"*Execute*\". Ad-hoc command dialog windows should show up. One of the field is \"*Language*\" with pull down list of available scripting languages. If \"*python*\" is on the list it means everything is ok and support for Python is enabled.\r\n\r\n|lang list with python small|\r\n\r\nWriting Python Scripts\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nPython scripts work in a similar way to Groovy or other languages scripts, except one significant difference. You cannot call \"*return*\" from the script itself. Hence you cannot simply pass script results by calling \"*return*\" statement directly from the script.\r\n\r\nTo overcome the problem, Tigase offers another way to pass script execution results. It checks the value of a special variables on the script completion: “result” and “packet”. By assigning value to one of these variables the Python (or any other language) can pass execution results back to the Tigase server.\r\n\r\n-  ``result`` allows to return simple text (or characters String) from the script.\r\n\r\n-  ``packet`` allows to return Packet instance which is send back to the user.\r\n\r\nThe simplest possible Python script may look like this one:\r\n\r\n``result = \"Hello world!\"``\r\n\r\nFor instructions how to load and execute the script, please refer to the :ref:`introductory article<Scripting-Introduction - Hello-World!>` for scripting in Tigase server. There were some minor changes in Tigase 4.4.0 and later versions, so please have a look at the :ref:`article <Tigase-Scripting-Version-4.4.x-Update-for-Administrators>` describing new elements as well.\r\n\r\nAn example of a more advanced script asks the user for providing required parameters for the actual script execution:\r\n\r\n.. code:: python\r\n\r\n   from java.lang import *\r\n   from tigase.server import *\r\n\r\n   num1 = Command.getFieldValue(packet, \"num1\")\r\n   num2 = Command.getFieldValue(packet, \"num2\")\r\n\r\n   if num1 is None or num2 is None:\r\n     res = Iq.commandResultForm(packet)\r\n     Command.addTextField(res, \"Note\", \"This is a Python script!\")\r\n     Command.addFieldValue(res, \"num1\", \"\")\r\n     Command.addFieldValue(res, \"num2\", \"\")\r\n     packet = res\r\n   else:\r\n     result = num1 + num2\r\n\r\nExcept this minor difference, the rest part of scripting in Python for the Tigase administrator commands is the same as all other languages. As all languages can return execution results via these special variables, it could be argued there is no difference at all.\r\n\r\nIn article *\"Component Implementation - Lesson 6 - Scripting Support\"* in Developer guide, I am going to present the Tigase server API available for scripting framework. My main language is Groovy as it offers the best integration with JVM and Tigase API, however I will try to include Python example code as well.\r\n\r\n.. |lang list no python small| image:: /images/admin/lang-list-no-python-small.png\r\n.. |lang list with python small| image:: /images/admin/lang-list-with-python-small.png\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Using_Tigase/Scripting/Scripting_Support.inc",
    "content": ".. _Scripting-support-in-Tigase:\r\n\r\nScripting support in Tigase\r\n--------------------------------\r\n\r\nTigase server supports scripting languages in versions 4.3.1 and higher. These pages describe this feature in details how to create new scripts, upload them to the server, and execute them. The guide also contains API description with code examples.\r\n\r\n.. Note::\r\n\r\n   Tigase server is known for it very low memory consumption and successfully runs with less then 10MB of RAM memory. However adding scripting support for any non-standard (default) language to Tigase server significantly increases memory requirements for the installation. You cannot expect Tigase server to run on 10MB RAM system if you enabled Python, Scala or any other non-standard language.\r\n\r\n.. include:: /Tigase_Administration/Using_Tigase/Scripting/Introduction.inc\r\n.. include:: /Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc\r\n.. include:: /Tigase_Administration/Using_Tigase/Scripting/Python.inc\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc",
    "content": ".. _Tigase-Scripting-Version-4.4.x-Update-for-Administrators:\r\n\r\nTigase Scripting Version 4.4.x Update for Administrators\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nScripting functionality is quite useful in Tigase server for all sorts of administrator tasks. The possibility to load new scripts or replace old ones at the server runtime opens quite new area for the service maintenance.\r\n\r\nIn earlier versions of the Tigase server scripting capabilities was available only in the session manager component while it might be very useful in many other places - connection managers, MUC, PubSub, VHostManager and what even more important in completely new, custom components created for specific needs. It would be quite wasteful to reinvent the wheel every time and implementing scripting capabilities for each component separately.\r\n\r\nTherefore the scripting capabilities has been implemented in the core of the Tigase server. It is now part of the API and is automatically available to all components without any additional coding. A detailed developer guide will be published separately.\r\n\r\nThis document describes changes from the user/administrator perspective because there are some usability changes related to the new implementation.\r\n\r\nPlease note. The description and screenshots are taken from the Psi client and most likely interface for ad-hoc commands and service discovery on other client looks different. I recommend to do some initial testing and experiments using Psi client and then switch to your preferred application for your day-to-day use.\r\n\r\nAs it always was in the Tigase you can access all the functions via XMPP service discovery on the server. However, as soon as you connect to the server you can see some changes there.\r\n\r\n|new service disco admin|\r\n\r\nThere are no command on the list. They are hidden from the main service discovery list. You can see on the list only the server main components.\r\n\r\nThis had to be done for many reasons. One of them is, obviously, the cleaner access to the main server stuff. Another, probably more important, is to avoid a long list of commands for different components mixed together. Commands for different components can have the same name/description and they can even do similar things but they are executed on a different server component. To avoid any confusion and minimize opportunities for mistake the commands are now closely tight to their components. To access a list of commands for a particular component you have to double click on the component name on the list or click 'Execute command\" icon on top of the window when your component is selected.\r\n\r\nA new window should show up with drop-down list of available commands. All the commands are related to the selected component and are executed kind of \"inside the component environment\". You can of course add new command or delete existing one and of course execute any of the commands showing on the list.\r\n\r\n|new command list|\r\n\r\nAs a reminder, in the window title you can see the component ID and you should check it before running any command to make sure you accidentally don’t break your system.\r\n\r\n|new add command|\r\n\r\nThere has been also a small change made to the script adding window. As you can see on the screenshot there is one additional option added - \"Save to disk\". This means that once you submitted the script to the server it is written to the hard drive and will be automatically loaded at next startup time.\r\n\r\nThis option is enabled by default as this seems to be a logical choice that the administrator wants to save his new script for later reuse. This, however requires proper configuration of the server and give writing permission to the directory where all scripts are stored. Otherwise the server won’t be able to write script files on the hard drive.\r\n\r\nAs in previous version only users with administrator permissions can execute commands and access all the critical elements on the server. There has been, however, another change made, long time requested by users. In the new version all the administrator specific elements are hidden for the rest of users.\r\n\r\nServer components don’t show up on the service discovery, the user can’t see administrator commands nor he can execute them. This hasn’t been implemented to improve the server security but to reduce confusion for general users who would otherwise see a lot of stuff which can’t be used by them anyway.\r\n\r\n.. |new service disco admin| image:: /images/admin/new-service-disco-admin.png\r\n.. |new command list| image:: /images/admin/new-command-list.png\r\n.. |new add command| image:: /images/admin/new-add-command.png"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc",
    "content": ".. _Tigase-Tip-Checking-the-Runtime-Environment:\r\n\r\nTips and Tricks\r\n----------------------\r\n\r\nThe section contains some short tricks and tips helping in different kinds of issues related to the server administration and maintenance.\r\n\r\n-  :ref:`Runtime Environment Tip<tigaseTip_RuntimeEnvironment>`\r\n\r\n-  :ref:`Best Practices for Connecting to Tigase XMPP server From Web Browser<Best-Practices-for-Connecting-to-Tigase-XMPP-server-From-Web-Browser>`\r\n\r\n.. _tigaseTip_RuntimeEnvironment:\r\n\r\nTigase Tip: Checking the Runtime Environment\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIt has happened recently that we have tried very hard to fix a few annoying problems on one of the Tigase installations. Whatever we did, the problem still existed after uploading a new version and restarting the server. It worked fine in our development environment and it just didn’t on the target system.\r\n\r\nIt turned out that due to specific environment settings on the target system, an old version of Tigase server was always started regardless of what updates uploaded. We finally located the problem by noticing that the logs were not being generated in the proper locations. This led us to finding the issue: improper environment settings.\r\n\r\nThe best way to check all the environment settings used to start the Tigase server is to use… ``check`` command line parameter:\r\n\r\n.. code:: bash\r\n\r\n   $ ./scripts/tigase.sh check etc/tigase.conf\r\n\r\n   Checking arguments to Tigase\r\n   TIGASE_HOME = .\r\n   TIGASE_JAR = jars/tigase-server.jar\r\n   TIGASE_PARAMS = etc/tigase.conf\r\n   TIGASE_CONFIG = etc/tigase.xml\r\n   TIGASE_RUN = tigase.server.XMPPServer -c etc/tigase.xml --property-file etc/init.properties\r\n   TIGASE_PID = ./logs/tigase.pid\r\n   TIGASE_OPTIONS = --property-file etc/init.properties\r\n   JAVA_OPTIONS = -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 \\\r\n       -Djdbc.drivers=com.mysql.jdbc.Driver:org.postgresql.Driver \\\r\n       -server -Xms100M -Xmx200M -XX:PermSize=32m -XX:MaxPermSize=256m\r\n   JAVA = /System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/bin/java\r\n   JAVA_CMD =\r\n   CLASSPATH = ./jars/tigase-server.jar:./libs/jdbc-mysql.jar:./libs/jdbc-postgresql.jar:\\\r\n       ./libs/tigase-extras.jar:./libs/tigase-muc.jar:./libs/tigase-pubsub.jar:\\\r\n       ./libs/tigase-utils.jar:./libs/tigase-xmltools.jar\r\n   TIGASE_CMD = /System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/bin/java \\\r\n       -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 \\\r\n       -Djdbc.drivers=com.mysql.jdbc.Driver:org.postgresql.Driver \\\r\n       -server -Xms100M -Xmx200M -XX:PermSize=32m -XX:MaxPermSize=256m \\\r\n       -cp ./jars/tigase-server.jar:./libs/jdbc-mysql.jar:./libs/jdbc-postgresql.jar:\\\r\n       ./libs/tigase-extras.jar:./libs/tigase-muc.jar:./libs/tigase-pubsub.jar:\\\r\n       ./libs/tigase-utils.jar:./libs/tigase-xmltools.jar tigase.server.XMPPServer \\\r\n       -c etc/tigase.xml --property-file etc/init.properties\r\n   TIGASE_CONSOLE_LOG = ./logs/tigase-console.log\r\n\r\nIn our case ``TIGASE_HOME`` was set to a fixed location pointing to an old version of the server files. The quick ``check`` command may be a real time saver.\r\n\r\n.. _Best-Practices-for-Connecting-to-Tigase-XMPP-server-From-Web-Browser:\r\n\r\nBest Practices for Connecting to Tigase XMPP server From Web Browser\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nCurrently we have 2 ways to connect to Tigase XMPP Server from web browsers:\r\n\r\n1. BOSH (Bidirectional-streams Over Synchronous HTTP)\r\n\r\n2. WebSocket (XMPP over WebSocket)\r\n\r\nYou will find more information about these ways for connecting to Tigase XMPP Server with some useful tips below.\r\n\r\nBOSH\r\n^^^^^\r\n\r\nBOSH protocol specified in `XEP-0124 <http://xmpp.org/extensions/xep-0124.html>`__ is one of first protocols defined to allow to establish XMPP connection to XMPP servers from web browsers due to this protocol being widely supported and used. It is also easy to use in single server mode. It’s enabled by default in Tigase XMPP Server and available at port 5280.\r\n\r\nIn clustered mode we can deploy it with load balancer deployed with guarantees that each BOSH connection from web browser will be forwarded to same Tigase XMPP Server instance. So in clustered mode if we have two XMPP server ``t1`` and ``t2`` which are hosting domain ``example.com`` we would need to have load balancer which will respond for HTTP request to domain ``example.com`` and forward all requests from same IP address to same node of a cluster (i.e. all request from ``192.168.122.32`` should be forwarded always to node ``t1``.\r\n\r\n\r\nTip #1 - BOSH in Cluster Mode Without Load Balancer\r\n\r\nThere is also a way to use BOSH without load balancer enabled. In this case the XMPP client needs to have more logic and knowledge about all available cluster nodes (with names of nodes which will identify particular cluster nodes from internet). Using this knowledge XMPP client should select one random node from list of available nodes and always establish BOSH connections to this particular node. In case if BOSH connection fails due to network connection issues, the XMPP client should randomly pick other node from list of rest of available nodes.\r\n\r\n*Solution:*\r\n\r\nTigase XMPP Server by default provides server side solution for this issue by sending additional ``host`` attribute in ``body`` element of BOSH response. As value of this attribute Tigase XMPP Server sends domain name of server cluster node to which client connected and to which next connections of this session should be opened. It is possible to disable this custom feature by addition of of following line to ``etc/config.tdsl`` config file:\r\n\r\n.. code::\r\n\r\n   bosh {\r\n       'send-node-hostname' = false\r\n   }\r\n\r\n*Example:*\r\n\r\nWe have servers ``t1.example.com`` and ``t2.example.com`` which are nodes of a cluster hosting domain ``example.com``. Web client retrieves list of cluster nodes from web server and then when it needs to connect to the XMPP server it picks random host from list of retrieved cluster nodes (i.e. ``t2.example.com``) and tries to connect using BOSH protocol to host ``t2.example.com`` but it should send ``example.com`` as name of the server it tries to connect to (``example.com`` should be value of ``to`` attribute of XMPP stream).\r\n\r\nWebSocket\r\n^^^^^^^^^^^^^^\r\n\r\nWebSocket protocol is newly standardized protocol which is supported by many of current versions of browsers. Currently there is a draft of protocol `draft-ietf-xmpp-websocket-00 <https://datatracker.ietf.org/doc/draft-ietf-xmpp-websocket/>`__ which describes usage of WebSocket to connect to XMPP servers. Tigase XMPP Server implementation of WebSocket protocol to connect to XMPP server is very close to this draft of this specification. By default Tigase XMPP Server has XMPP-over-WebSocket protocol enabled without encryption on port 5290. To use this protocol you need to use library which supports XMPP-over-WebSocket protocol.\r\n\r\n\r\nTip #1 - Encrypted WebSocket Connection\r\n\r\nIt is possible to enable encrypted WebSocket connection in Tigase XMPP Server. To do this you need to add following lines to ``etc/config.tdsl`` config file:\r\n\r\n.. code::\r\n\r\n   ws2s {\r\n       connections {\r\n           ports = [ 5290, 5291 ]\r\n           5290 {\r\n               socket = 'ssl'\r\n               type = 'accept'\r\n           }\r\n           5291 {\r\n               socket = 'plain'\r\n               type = 'accept'\r\n           }\r\n       }\r\n   }\r\n\r\nIn this example we enabled WebSocket endpoint on port 5290 allowing unencrypted connections, and encrypted WebSocket endpoint on port 5291. As this is TLS/SSL connection (no STARTTLS) it uses default certificate installed in Tigase XMPP Server instance. This certificate is located in ``certs/default.pem``.\r\n\r\n.. Note::\r\n\r\n   There is no default configuration for non-default ports. All ports outside 443 MUST be configured.\r\n\r\nTip #2 - Encrypted WebSocket Connection - Dealing With Multiple VHosts\r\n\r\nAs mentioned in Tip #1 WebSocket endpoint is plain TLS/SSL port, so it always serves default certificate for Tigase XMPP Server instance. That is ok if we are hosting single domain and if default certificate matches matches our domain. But If we host multiple domain we cannot use ``wss://example1.com:5291/`` connection URL, if our default certificate is for domain ``example2.com``. In this situation it is recommended to use the default certificate for the domain under which the server is accessible from the internet. This domain should identify this server, so this domain would not point to two nodes of a cluster. After we deploy separate certificate for each of cluster nodes, we should follow same tip as Tip #1 for BOSH. Our web-based XMPP client should have knowledge about each node of a cluster and when it needs to connect it should randomly select one node from list of available cluster nodes and try to connect using connection URL that would contain name of server under which it can be identified from internet.\r\n\r\n*Example:*\r\n\r\nWe have servers ``t1.example1.com`` and ``t2.example1.com`` which are nodes of a cluster in hosting domain ``example2.com``. Each of our nodes contains default SSL certificate with domain names matching the cluster node. Web client retrieves list of cluster nodes from web server and then when it needs to connect to XMPP server it picks random host from list of retrieved cluster nodes (i.e. ``t2.example1.com``) and tries to connect using WebSocket encrypted protocol to host ``t2.example1.com`` using the following URL: ``wss://t2.example1.com:5291/``. Upon connection the client should still send example2.com as name of server to which it tries to connect (``example2.com`` should be value of to attribute of XMPP stream). This will allow browser to validate certificate as it will be for the same domain to which browser connects, and it will allow XMPP client to connect to domain ``example2.com``, which is one of hosted vhosts."
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Using_Tigase/Virtual_Domains.inc",
    "content": ".. _addManageDomain:\r\n\r\nAdd and Manage Domains (VHosts)\r\n------------------------------------\r\n\r\nTigase XMPP Server offers an easy to use and very flexible way to add and manage domains hosted on installation (vhosts).\r\n\r\nThere are two ways of managing domains you host on your server:\r\n\r\n-  using web-based admin management console - :ref:`Admin UI<usingAdminUI>`\r\n\r\n-  using XMPP ad-hoc commands by XMPP client, ie. `Psi <http://psi-im.org/>`__\r\n\r\n.. Note::\r\n\r\n   To use any of those ways, you need to be an administrator of the server, which means that you have a XMPP account created on this XMPP server and your account JID is added to :ref:`the list of the administrators<admins>` in the Tigase XMPP Server configuration file.\r\n\r\n.. _usingAdminUI:\r\n\r\nUsing Admin UI\r\n^^^^^^^^^^^^^^^^^^\r\n\r\nFirst, you need to open Admin UI web page. By default Admin UI is enabled and available at the port ``8080`` at path ``/admin/`` on the XMPP server. Assuming that your are logged on the same machine which hosts Tigase XMPP Server, it will be available at http://localhost:8080/admin/.\r\n\r\nWhen you will be prompted for username and password to login to the Admin UI please fill username with full JID of your XMPP admin account and fill password field with password for this account. When you submit correct credentials you will get access to the Admin UI and Tigase XMPP Server configuration and management web-based interface.\r\n\r\nAdding a new domain\r\n~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nTo add a new domain you need to open ``Configuration`` section of the Admin UI (by clicking on ``Configuration`` label and then selecting ``Add new item`` position which mentions ``vhost-man``.\r\n\r\n|adminui vhost add item button|\r\n\r\nAfter doing that, you will be presented with a form which you need to fill in. This form allows you to pass ``Domain name`` to add and other options (some of the are advanced options).\r\n\r\n|adminui vhost add item form|\r\n\r\n.. Tip::\r\n\r\n   All options with exception of ``Domain name`` may be changed later on by modifying vhost settings.\r\n\r\nWhen you will be ready, please submit the form using button below the form. As a result you will be presented with a result of this operation. If it was successful it show ``Operation successful`` message and if something was not OK, it will display an error to help you fix this issue which you encountered.\r\n\r\nModifying domain settings\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nModifying a domain settings is very similar to adding a new domain. You need to open ``Configuration`` section of the Admin UI and then select ``Update item configuration`` position which mentions ``vhost-man``.\r\n\r\n|adminui vhost update item button|\r\n\r\nYou will be presented with a list of domains hosted on this Tigase XMPP Server installation. From them you should choose the one for which you wish to modify settings.\r\n\r\n|adminui vhost update item domains list|\r\n\r\nAfter submitting this selection, you will be presented with a the same form as the one used during adding a new domain. It presents configuration options for this domain and currently used values.\r\n\r\n|adminui vhost update item form|\r\n\r\nNow you should adjust them as you wish and submit this form using the button below the form.\r\n\r\nAs a result you will be presented with a result of this operation. If it was successful it show ``Operation successful`` message and if something was not OK, it will display an error to help you fix this issue which you encountered.\r\n\r\nRemoving a domain\r\n~~~~~~~~~~~~~~~~~~~~~\r\n\r\nRemoving a hosted domain from the Tigase XMPP Server installation is quite simple as well. You need to open ``Configuration`` section of the Admin UI and then select ``Remove an item`` position which mentions ``vhost-man``.\r\n\r\n|adminui vhost remove item button|\r\n\r\nYou will be presented with a list of domains hosted on this Tigase XMPP Server installation. From them you should select the one which should be removed.\r\n\r\n|adminui vhost remove item domains list|\r\n\r\nAfter submitting your selection, Tigase XMPP Server will try to remove this domain from the list of hosted domains and will present you with the result. If it was successful it show ``Operation successful`` message and if something was not OK, it will display an error to help you fix this issue which you encountered.\r\n\r\nUsing ad-hoc commands\r\n^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nFor everybody interested in using our service to host their own XMPP domain we have good news! You do not have to ask an administrator to add your domain or add users for your domain anymore. You can do it on your own.\r\n\r\nPlease note, this is very new stuff. Something may go wrong or may not be polished. Please report any problems, notices or suggestions.\r\n\r\nThis is the guide to walk you through the new functions and describes how to add a new domain and new users within your domain.\r\n\r\nYou can do everything from your XMPP client or you can use our web application that allows you to connect to the service and execute admin commands. I recommend `Psi <http://psi-im.org/>`__ because of its excellent support for parts of the XMPP protocol which are used for domains and user management. You may use other clients as well, but we can only offer support and help if you use Psi client.\r\n\r\nSecondly, you need an account on the server. This is because all the commands and features described here are available to local users only. Therefore, if you do not have a registered domain with us yet, please go ahead and register an account on the website either the `Tigase.IM <http://www.tigase.im/>`__ or `Jabber.Today <http://jabber.today/>`__.\r\n\r\nAdding a New Domain\r\n~~~~~~~~~~~~~~~~~~~~~\r\n\r\nOnce you register an account on one of the websites, connect to the XMPP server using the account on the Psi client. We will be using the following account: green@tigase.im which is this guide.\r\n\r\nWhen you are ready right click on the account name in Psi roster window to bring up context menu. Select **Service Discovery** element.\r\n\r\n|service disco menu|\r\n\r\nA new windows pops up as in the example on the right. The service discovery window is where all the stuff installed on XMPP service should show up. Most of elements on the list are well known transports, MUC and PubSub components. The new stuff on the list, which we are interested in, are 2 elements: **VHost Manager** and **Session Manager**.\r\n\r\n|service disco window vhost|\r\n\r\n**VHost Manager** component in Tigase is responsible for managing and controlling virtual hosts on the installation. It provides virtual hosts information to all other parts of the system and also allows you to add new hosts and remove/update existing virtual hosts.\r\n\r\n**Session Manager** component in Tigase is responsible for managing users. In most cases online users but it can also perform some actions on user repository where all user data is stored.\r\n\r\nSelect **VHost Manager** and double click on it. A new windows shows up (might be hidden behind the service discovery window). The window contains another menu with a few items: **Add…​, Remove…​** and **Update…​** . These are for adding, removing and updating VHost information. For now, just select the first element **Add…​.**\r\n\r\n|command menu add vhost|\r\n\r\nClick **Execute** and you get a new window where you can enter all of your VHost configuration details. All fields should be self explanatory. Leave a blank field for **Other parameters** for now. **Owner** is you, that is Jabber ID which controls the domain and can change the domain configuration settings or can remove the domain from the service. **Administrators** field can be left blank or can contain comma separated list of Jabber IDs for people who can manage users within the domain. You do not need to add your user name to the list as Owners can always manage users for the domain.\r\n\r\n|add vhost window|\r\n\r\nWhen you are ready click the **Finish** button. All done, hopefully. You can get either a window confirming everything went well or a window printing an error message if something went wrong. What can be wrong? There are some restrictions I decided to put on the service to prevent abuse. One of the restrictions is the maximum number of domains a user can register for himself which is **25** right now. Another restriction is that the domain which you add must have a valid DNS entry pointing to our service. The XMPP guide describes all the details about DNS settings. Please refer to these instructions if you need more details.\r\n\r\nAdding a New User\r\n~~~~~~~~~~~~~~~~~~~~~\r\n\r\nAdding a new user process is quite similar, almost identical to adding a new domain. This time, however we have to select **Session Manager** in the service discovery window.\r\n\r\n|service disco window sm|\r\n\r\nDouble click on the **Session Manager** and a window with SM’s commands list shows up. Right now, there is only one command available to domain administrators - **Add user**. I am going to make available more commands in the future and I am waiting for your suggestions.\r\n\r\n|command menu add user|\r\n\r\nIf you click **Execute** a window presented on the left shows up. Fill all fields accordingly and press **Finish**.\r\n\r\n|add user window|\r\n\r\nIf everything went well you have just added a new user and you should get a window confirming successful operation. If something went wrong, a window with an error message should show up. Possible errors may be you tried to add a user which is already present, or you may have tried to add a user for a domain to which you do not have permission or to non-existen domain.\r\n\r\nSSL Certificate Management\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nSSL Certificate Management has been implemented, and certificates can be manipulated when in a .pem form. For more details, see :ref:`Creating and Loading the Server Certificate in pem Files<certspem>` section of documentation for more information.\r\n\r\n.. |adminui vhost add item button| image:: /images/admin/adminui_vhost_add_item_button.png\r\n.. |adminui vhost add item form| image:: /images/admin/adminui_vhost_add_item_form.png\r\n.. |adminui vhost update item button| image:: /images/admin/adminui_vhost_update_item_button.png\r\n.. |adminui vhost update item domains list| image:: /images/admin/adminui_vhost_update_item_domains_list.png\r\n.. |adminui vhost update item form| image:: /images/admin/adminui_vhost_update_item_form.png\r\n.. |adminui vhost remove item button| image:: /images/admin/adminui_vhost_remove_item_button.png\r\n.. |adminui vhost remove item domains list| image:: /images/admin/adminui_vhost_remove_item_domains_list.png\r\n.. |service disco menu| image:: /images/admin/service_disco_menu.png\r\n.. |service disco window vhost| image:: /images/admin/service_disco_window_vhost.png\r\n.. |command menu add vhost| image:: /images/admin/command_menu_add_vhost.png\r\n.. |add vhost window| image:: /images/admin/add_vhost_window.png\r\n.. |service disco window sm| image:: /images/admin/service_disco_window_sm.png\r\n.. |command menu add user| image:: /images/admin/command_menu_add_user.png\r\n.. |add user window| image:: /images/admin/add_user_window.png\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Using_Tigase/Watchdog.inc",
    "content": ".. _Watchdog:\r\n\r\nWatchdog\r\n-------------\r\n\r\nTigase’s Watchdog was implemented to help Tigase close connections that have become stale or inactive. Sometimes the connection is delayed, maybe dropped packets, or a service interruption. After a time, if that connection is re-established, both server and client (or server and server) will continue on as if nothing happened. However, these gaps in connection can last longer, and some installations will rely on the operating system to detect and close stale connections. Some operating systems or environments can take up to 2 hours or more to determine whether a connection is bad and wait for a response from a foreign entity and may not be configured. This can not only slow down performance, but can lead to security issues as well. To solve this problem, we have introduced Watchdog to monitor connections independent of operating system and environments to keep those broken connections from becoming a problem.\r\n\r\nSetup\r\n^^^^^^^\r\n\r\nNo extra setup is necessary, Watchdog is already included with your build of Tigase (as long as it’s 7.1.0 or newer). Follow the steps in the configuration section.\r\n\r\nWatchdog Configuration\r\n^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTo configure watchdog, the following lines need to be present or edited in ``config.tdsl`` file:\r\n\r\n.. code::\r\n\r\n   'watchdog-timeout' = 70000\r\n   'watchdog-delay' = 60000\r\n   'watchdog-ping-type' = 'xmpp'\r\n\r\nThe three settings are as follows:\r\n\r\n-  ``'watchdog-timeout'= 70000`` This setting sets the amount of time that watchdog will consider before it determines a connection may be stale. This setting sets the timeout at 70000ms or 70 seconds.\r\n\r\n-  ``'watchdog-delay' = 60000`` This setting sets how often the watchdog should conduct the check, the default delay at 60000ms or 60 seconds.\r\n\r\n-  ``'watchdog-ping-type'`` This setting determines the type of ping sent to components when watchdog is testing for activity.\r\n\r\nYou may, if you choose, to specify individual watchdog settings for specific components by adding them to the component settings, for example if we wanted to change the Client2Server settings to include watchdog, use the following lines in config.tdsl:\r\n\r\n.. code::\r\n\r\n   c2s {\r\n       watchdog-delay = '1500'\r\n       watchdog-timeout = '3000'\r\n   }\r\n\r\nIf any settings are not set, the global or settings will be used. ``watchdog-delay`` default is set to 10 min ``watchdog-ping-type`` default is set to XMPP\r\n\r\nLogic\r\n^^^^^^^^\r\n\r\nWatchdog compares it’s own pings, and records the time it takes for a round trip to different components, clustered connections, and if one variable is larger than the other, watchdog will commence closing that stale connection. Here is a breakdown:\r\n\r\n1. A check is performed of a connection(s) on every ``watchdog-delay`` interval.\r\n\r\n2. During this check two things occur\r\n\r\n   -  If the last transfer time exceeds ``max-inactivity-time`` a stop service command is given to terminate and broadcast unavailable presence.\r\n\r\n   -  If the last transfer time is lower than ``max-inactivity-time`` but exceeds ``watchdog-timeout`` watchdog will try to send a ping (of ``watchdog-ping-type``). This ping may be one of two varieties (set in config.tdsl)\r\n\r\n      -  ``WHITESPACE`` ping which will yield the time of the last data transfer in any direction.\r\n\r\n      -  ``XMPP`` ping which will yield the time of the last received xmpp stanza.\r\n\r\n3. If the 2nd option is true, the connection will remain open, and another check will begin after the ``watchdog-delay`` time has expired.\r\n\r\nFor example, lets draw this out and get a visual representation\r\n\r\n::\r\n\r\n   -----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-- \r\n        |     |     |     |     |     |     |     |     |     |     |\r\n   ---+---------------------------------------------------------------- \r\n      1 2     3     4     5     6\r\n   ---*-*-----*-----*-----*----------- \r\n\r\n-  This line represents how often the check is performed. Each ``-`` (dash) is 10 seconds, so the check is done every 60 seconds (``'watchdog-delay' = 60000``)\r\n\r\n-  This line is client activity, here the client sent a message at 40 seconds (marked by ``+``) and has gone idle.\r\n\r\n-  The following line represents the watchdog logic, with timeout at 120 seconds and max inactivity timeout at 180 seconds:\r\n\r\n   .. code::\r\n\r\n      'watchdog-timeout' = 120000\r\n      c2s {\r\n          'max-inactivity-time' = '180000'\r\n      }\r\n      (timeout at 120 seconds and max inactivity timeout at 180 seconds)\r\n\r\nHow the check is performed:\r\n\r\n1. 30 seconds - at this point *last transfer* or *last received* time is updated.\r\n\r\n2. 60 seconds - watchdog runs - it check the connection and says: \\_ok, last client transfer was 20s ago - but it’s lower than both inactivity (so don’t disconnect) and timeout (so don’t send ping).\r\n\r\n3. 120 seconds - 2nd check - last transfer was 100s ago - still lower than both values - do nothing.\r\n\r\n4. 180 seconds - 3rd check - last transfer was 160s ago - lower than inactivity but greater than delay - ping it sent.\r\n\r\n5. 240 seconds - 4th check - last transfer was 220s ago - client still hasn’t responded, watchdog compares idle time to ``max-inactivity-timeout`` and finds out that it is greater, connection is terminated.\r\n\r\n6. 300 seconds - watchdog is run again but given the connection was terminatet there is no XMPP session to check for that particular client.\r\n\r\n.. Tip::\r\n\r\n   It is possible that the connection is broken, and could be detected during the sending of a ping and the connection would be severed at step 4 instead of waiting for step 5. **NOTE** This MAY cause JVM to throw an exception.\r\n\r\n.. Note::\r\n\r\n   Global settings may not be ideal for every setup. Since each component has its own settings for ``max-inactivity-time`` you may find it necessary to design custom watchdog settings, or edit the inactivity times to better suit your needs. Below is a short list of components with thier default settings:\r\n\r\n   .. code::\r\n\r\n      bosh {\r\n          'max-inactivity-time' = 600L\r\n      }\r\n      c2s {\r\n          'max-inactivity-time' = 86400L\r\n      }\r\n      'cl-comp' {\r\n          'max-inactivity-time' = 180L\r\n      }\r\n      s2s {\r\n          'max-inactivity-time' = 900L\r\n      }\r\n      ws2s {\r\n          'max-inactivity-time' = 86400L\r\n      }\r\n\r\n.. Important::\r\n\r\n   Again remember, for Watchdog to properly work, the ``max-inactivity-time`` MUST be longer than the ``watchdog-timeout`` setting\r\n\r\nTesting\r\n~~~~~~~~\r\n\r\nThe tigase.log.0 file can reveal some information about watchdog and how it is working (or how it might be fighting your settings). To do so, enter the following line into your ``config.tdsl`` file:\r\n\r\n::\r\n\r\n   debug = [ 'server', 'xmpp.init' ]\r\n\r\nThis will set debug mode for your log, and enable some more information about what Tigase is doing. These logs are truncated for simplicity. Lets look at the above scenario in terms of the logs:\r\n\r\n**Stage Two.**\r\n\r\n::\r\n\r\n   2015-10-16 08:00:00.000 [Watchdog - c2s]   ConnectionManager$Watchdog$1.check()  FINEST: Testing service: c2s@xmpp.domain.com/192.168.0.150_5222_192.168.0.201_50368, type: accept, Socket: TLS: c2s@xmpp.domain.com/192.168.0.150_5222_192.168.0.201_50368 Socket[addr=/192.168.0.201,port=50368,localport=5222], jid: user@xmpp.domain.org/mobile, sinceLastTransfer: 20,000, maxInactivityTime: 180,000, watchdogTimeout: 120,000, watchdogDelay: 60,000, watchdogPingType: XMPP\r\n\r\n**Stage Three.**\r\n\r\n::\r\n\r\n   2015-10-16 08:01:00.000 [Watchdog - c2s]   ConnectionManager$Watchdog$1.check()  FINEST: Testing service: c2s@xmpp.domain.com/192.168.0.150_5222_192.168.0.201_50368, type: accept, Socket: TLS: c2s@xmpp.domain.com/192.168.0.150_5222_192.168.0.201_50368 Socket[addr=/192.168.0.201,port=50368,localport=5222], jid: user@xmpp.domain.org/mobile, sinceLastTransfer: 100,000, maxInactivityTime: 180,000, watchdogTimeout: 120,000, watchdogDelay: 60,000, watchdogPingType: XMPP\r\n\r\n**Stage Four.**\r\n\r\n::\r\n\r\n   2015-10-16 08:02:00.000 [Watchdog - c2s]   ConnectionManager$Watchdog$1.check()  FINEST: Testing service: c2s@xmpp.domain.com/192.168.0.150_5222_192.168.0.201_50368, type: accept, Socket: TLS: c2s@xmpp.domain.com/192.168.0.150_5222_192.168.0.201_50368 Socket[addr=/192.168.0.201,port=50368,localport=5222], jid: user@xmpp.domain.org/mobile, sinceLastTransfer: 160,000, maxInactivityTime: 180,000, watchdogTimeout: 120,000, watchdogDelay: 60,000, watchdogPingType: XMPP\r\n   2015-10-16 08:02:00.697 [Watchdog - c2s]   ConnectionManager$Watchdog$1.check()  FINEST: c2s@xmpp.domain.com/192.168.0.150_5222_192.168.0.201_50368, type: accept, Socket: TLS: c2s@xmpp.domain.com/192.168.0.150_5222_192.168.0.201_50368 Socket[addr=/192.168.0.201,port=50368,localport=5222], jid: user@xmpp.domain.org/mobile, sending XMPP ping from=null, to=null, DATA=<iq from=\"xmpp.domain.com\" id=\"tigase-ping\" to=\"user@xmpp.domain.com/mobile\" type=\"get\"><ping xmlns=\"urn:xmpp:ping\"/></iq>, SIZE=134, XMLNS=null, PRIORITY=NORMAL, PERMISSION=NONE, TYPE=get\r\n\r\n**Stage Five.**\r\n\r\n::\r\n\r\n   2015-10-16 08:03:00.000 [Watchdog - c2s]   ConnectionManager$Watchdog$1.check()  FINEST: Testing service: c2s@xmpp.domain.com/192.168.0.150_5222_192.168.0.201_50368, type: accept, Socket: TLS: c2s@xmpp.domain.com/192.168.0.150_5222_192.168.0.201_50368 Socket[addr=/192.168.0.201,port=50368,localport=5222], jid: user@xmpp.domain.org/mobile, sinceLastTransfer: 100,000, maxInactivityTime: 180,000, watchdogTimeout: 120,000, watchdogDelay: 60,000, watchdogPingType: XMPP\r\n   2015-10-16 08:03:00.248 [pool-20-thread-6]  ConnectionManager.serviceStopped()  FINER:  [[c2s]] Connection stopped: c2s@xmpp./domain.com/192.168.0.150_5222_192.168.0.201_50368, type: accept, Socket: TLS: c2s@lenovo-z585/192.168.0.150_5222_192.168.0.201_50368 Socket[unconnected], jid: user@xmpp.domain.com\r\n   2015-10-16 08:03:00.248 [pool-20-thread-6]  ClientConnectionManager.xmppStreamClosed()  FINER: Stream closed: c2s@xmpp.domain.com/192.168.0.150_5222_192.168.0.201_50368"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/Using_Tigase/_using_tigase.rst",
    "content": ".. include:: Intro.inc\r\n.. include:: Offline_Messages.inc\r\n.. include:: LastActivity.inc \r\n.. include:: Log_Guide.inc \r\n.. include:: Debugging_Tigase.inc \r\n.. include:: Basic_System_Checks.inc \r\n.. include:: Virtual_Domains.inc \r\n.. include:: Presence_Forwarding.inc \r\n.. include:: Watchdog.inc\r\n.. include:: PubSub_Server_Information.inc\r\n.. include:: Tips_and_Tricks.inc \r\n.. include:: Licensing.inc \r\n.. include:: Clustering.inc \r\n.. include:: Anonymous_Users.inc \r\n.. include:: Scripting/Scripting_Support.inc"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst",
    "content": "About Tigase XMPP Server\r\n===========================\r\n\r\n**Tigase XMPP Server** is an **Open Source and Free (AGPLv3)** Java based server. The goals behind its design and implementation of the server are:\r\n\r\n1. Make the server robust and reliable.\r\n\r\n2. Make the server a secure communication platform.\r\n\r\n3. Make a flexible server which can be applied to different use cases.\r\n\r\n4. Make an extensible server which takes full advantage of XMPP protocol extensibility.\r\n\r\n5. Make the server easy to setup and maintain.\r\n\r\nRobust and reliable\r\n---------------------\r\n\r\nThis means that the server can handle many concurrent requests/connections and can run for a long time reliably. The server is designed and implemented to handle millions of simultaneous connections.\r\n\r\nIt is not enough however to design and implement a high load server and hope it will run well. The main focus of the project is put in into testing. Tests are taken so seriously that a dedicated testing framework has been implemented. All server functions are considered as implemented only when they pass a rigorous testing cycle. The testing cycle consists of 3 fundamental tests:\r\n\r\n1. **Functional tests** - Checking whether the function works at all.\r\n\r\n2. **Performance tests** - Checking whether the function performs well enough.\r\n\r\n3. **Stability tests** - Checking whether the function behaves well in long term run. It must handle hundreds of requests a second in a several hour server run.\r\n\r\n\r\nSecurity\r\n---------\r\n\r\nThere are a few elements of the security related to XMPP servers: secure data transmissions which is met by the implementation of **SSL** or **TLS** protocol, secure user authorization which is met by the implementation of **DIGEST** or **SASL** user authorization and secure deployment which is met by component architecture.\r\n\r\n**Secure deployment** Tigase software installation does not impact network security. Companies usually have their networks divided into 2 parts: **DMZ** which is partially open to the outside world and the **Private network** which is closed to the outside world.\r\n\r\nIf the XMPP server is to provide an effective way of communication between company employees regardless if they are in a secure company office or outside (perhaps at a customer site), it needs to accept both internal and external connections. So the natural location for the server deployment is the **DMZ**. However, this solution has some considerations: each company has normally established network users base and integrated authorization mechanisms. However, that information should be stored outside the DMZ to protect internal security, so how to maintain ease of installation and system security?\r\n\r\n**Tigase server** offers a solution for such a case. With it’s component structure, Tigase can be easily deployed on any number machines and from the user’s point of view it is seen as a one logical XMPP server. In this case we can install a Session Manager module in the **private** network, and a Client Connection Manager with Server Connection Manager in the **DMZ**.\r\n\r\nSession Manager connects to **DMZ** and receives all packets from external users. Thus is can securely realize users authorization based on company authorization mechanisms.\r\n\r\n\r\nFlexibility\r\n--------------\r\nThere are many different XMPP server implementations. The most prevalent are:\r\n\r\n-  Used as a business communication platform in small and medium companies where the server is not under a heavy load. For such deployments security is a key feature.\r\n\r\n-  For huge community websites or internet portal servers is, on the other hand, usually under very heavy load and has to support thousands or millions of simultaneous connections. For such a deployment we need a different level of security as most of the service is open to the public.\r\n\r\n-  For very small community deployments or for small home networks the key factor is ease to deploy and maintain.\r\n\r\nArchitecture based on components provides the ability to run selected modules on separate machines so the server can be easily applied in any scenario.\r\n\r\nFor simple installation the server generates a config file which can be used straight away with very few modifications or none at all. For complex deployments though, you can tweak configurations to your needs and setup XMPP server on as many physical machines as you need.\r\n\r\nExtensibility\r\n-----------------\r\n\r\nThe world changes all the time as does user’s needs. The XMPP protocol has been designed to be extensible to make it easy to add new features and apply it to those different user’s needs. As a result, XMPP is a very effective platform not only for sending messages to other users, it can also be extended for sending instant notifications about events, a useful platform for on-line customer service, voice communication, and other cases where sending information instantly to other people is needed.\r\n\r\n**Tigase server** has been designed to be extensible using a modular architecture. You can easily replace components which do not fulfill your requirements with others better fitting your needs. But that is not all, another factor of extensibility is how easy is to replace or add new extensions. A great deal of focus has been put into the server design API to make it easy for other software developers to create extensions and implement new features.\r\n\r\nEase of Use\r\n----------------\r\n\r\nComplex computer networks consisting of many servers with different services are hard to maintain. This requires employing professional staff to operate and maintain the network.\r\n\r\nNot all networks are so complex however, most small companies have just a few servers for their needs with services like e-mail and a HTTP server. They might want to add an XMPP server to the collection of their services and don’t want to dedicate resources on setup and maintenance. For such users our default configuration is exactly what they need. If the operating system on the server is well configured, then Tigase should automatically pickup the correct hostname and be ready to operate immediately.\r\n\r\nTigase server is designed and implemented to allow dynamic reconfiguration during runtime so there is no need to restart the server each time you want to change configuration settings.\r\n\r\nThere are also interfaces and handlers available to make it easy to implement a web user interface for server monitoring and configuring.\r\n\r\n.. include:: Supported_Extension.inc\r\n.. include:: Custom_Extensions.inc"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/XMPP_Server/Custom_Extensions.inc",
    "content": "Tigase Custom Extensions\r\n----------------------------\r\n\r\nGeneral features\r\n^^^^^^^^^^^^^^^^^^^\r\n\r\n.. table:: tabel 6.Monitoring\r\n\r\n   +---------+--------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | Support | Name                           | Comment                                                                                                                                                                           |\r\n   +---------+--------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓ [#]_  | AuditLog                       | Ability functionality to log important events in a system (loggins, message exchanges, calls)                                                                                     |\r\n   +---------+--------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | Anti Abuse                     | Fight stanza SPAM, DoS, brute-force attacks and other threats                                                                                                                     |\r\n   +---------+--------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | Virtual domains                | Ability to create and manage multiple virtual domains from a single instance and restart-less management                                                                          |\r\n   +---------+--------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | MUC subscribe for offline push | Option to register permanently to the room to receive push notifications about new messages.                                                                                      |\r\n   +---------+--------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | Scripting API                  | Supports the Java Scripting API JSR-223                                                                                                                                           |\r\n   +---------+--------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | JMX monitoring                 | Advanced monitoring the server via JMX protocol with an API for connecting custom monitors and TCP/IP end-point for connecting general purpose JMX tools                          |\r\n   +---------+--------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | HTTP monitoring                | Basic monitoring via HTTP protocol                                                                                                                                                |\r\n   +---------+--------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | XMPP Monitoring                | Pluggable, active monitoring via XMPP, retrieving detailed server statistics, receiving automatic notifications about possible problems discovered by the self-monitor mechanisms |\r\n   +---------+--------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | SNMP Monitoring                | Advanced server monitoring via SNMP.                                                                                                                                              |\r\n   +---------+--------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | Bosh Cache                     | Bosh Session Cache - a feature to quickly reload user data - roster, presences and messages history by the web client (for example after web page reload)                         |\r\n   +---------+--------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | Clustering                     | Full clustering support for HA and LB with pluggabble clustering strategies for perfect optimising the cluster to the client’s system                                             |\r\n   +---------+--------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | Advanced Clustering Strategy   | Dedicated, specialised clustering strategy for best possible performance                                                                                                          |\r\n   +---------+--------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | MUC Clustered                  | Support for clustering group chatrooms with various, pluggable strategies                                                                                                         |\r\n   +---------+--------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | PubSub Clustered               | Support for clustering PubSub component with various, pluggable strategies                                                                                                        |\r\n   +---------+--------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | Mobile optimisations           | Optimizations designed for Mobile Devices                                                                                                                                         |\r\n   +---------+--------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | OSGi                           | Support for running in OSGi environment, i.e. as embedded XMPP server in advanced application server                                                                              |\r\n   +---------+--------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | Dynamic rosters                | Ability to create users' rosters entries on the fly based on data retrieved from any sources                                                                                      |\r\n   +---------+--------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | Command line admin tools       | Commandline utility to manage server                                                                                                                                              |\r\n   +---------+--------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | Unified Archive                | An extension to XEP-0313 Message Archive Management, with greatly improved flexibility in terms of what can be archived.                                                          |\r\n   +---------+--------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n\r\n\r\nRepositories/Databases\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n.. table:: Table 7.Repositories/Databases\r\n\r\n   +---------+---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | Support | Name          | Comment                                                                                                                                                                 |\r\n   +---------+---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | DB per domain | Ability to have multiple databases for specific domains.                                                                                                                |\r\n   +---------+---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | PostgreSQL    | Full support for PostgreSQL database with database schemas excluding dedicated DB schema for PubSub component                                                           |\r\n   +---------+---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | MySQL         | Full support for MySQL database with database schemas, dedicated DB schema for PubSub component                                                                         |\r\n   +---------+---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | SQL Server    | Full support for MS SQL Server database with database schemas excluding dedicated DB schema for PubSub component, only in Tigase server version 3.x                     |\r\n   +---------+---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | Derby DB      | Full support for built-in Derby database with database schemas excluding dedicated DB schema for PubSub component                                                       |\r\n   +---------+---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | JDBC          | Support for all JDBC enabled databases, although the database schemas are available for some databases                                                                  |\r\n   +---------+---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n   | ✓       | LDAP-Auth     | LDAP Authentication Connector Supported                                                                                                                                 |\r\n   +---------+---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n\r\n.. [#]\r\n   Requires commercial license\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/XMPP_Server/Supported_Extension.inc",
    "content": "XMPP Supported Extensions\r\n---------------------------------\r\n\r\nBased on `XEP-0387: XMPP Compliance Suites 2018 <https://xmpp.org/extensions/xep-0387.html>`__\r\n\r\n\r\nCore Compliance Suite\r\n^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n.. table:: Table 1.Core Compliance Suite\r\n   :widths: grid\r\n\r\n   +---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------------------------------------------------+\r\n   | Support | Specification                                            | Name                                                                                           | Comment                                                   |\r\n   +---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------------------------------------------------+\r\n   | ✓       | `RFC6120 <https://tools.ietf.org/html/rfc6120>`__        | Extensible Messaging and Presence Protocol (XMPP): Core                                        |                                                           |\r\n   +---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------------------------------------------------+\r\n   | ⍻       | `RFC7622 <https://tools.ietf.org/html/rfc7622>`__        | Extensible Messaging and Presence Protocol (XMPP): Address Format                              | We support previous version of the specification: RFC6122 |\r\n   +---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------------------------------------------------+\r\n   | ✓       | `RFC7590 <https://tools.ietf.org/html/rfc7590>`__        | Use of Transport Layer Security (TLS) in the Extensible Messaging and Presence Protocol (XMPP) |                                                           |\r\n   +---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------------------------------------------------+\r\n   | ✓       | `XEP-0368 <https://xmpp.org/extensions/xep-0368.html>`__ | SRV records for XMPP over TLS                                                                  | Requires adding DNS entries pointing to port 5223         |\r\n   +---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------------------------------------------------+\r\n   | ✓       | `XEP-0030 <https://xmpp.org/extensions/xep-0030.html>`__ | Service Discovery                                                                              |                                                           |\r\n   +---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------------------------------------------------+\r\n   | ✓       | `XEP-0115 <https://xmpp.org/extensions/xep-0115.html>`__ | Entity Capabilities                                                                            |                                                           |\r\n   +---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------------------------------------------------+\r\n   | ✓       | `XEP-0114 <https://xmpp.org/extensions/xep-0114.html>`__ | Jabber Component Protocol                                                                      |                                                           |\r\n   +---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------------------------------------------------+\r\n   | ✓       | `XEP-0163 <https://xmpp.org/extensions/xep-0163.html>`__ | Personal Eventing Protocol                                                                     |                                                           |\r\n   +---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+-----------------------------------------------------------+\r\n\r\nWeb Compliance Suite\r\n^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n.. table:: Table 2.Web Compliance Suite\r\n\r\n   +---------+----------------------------------------------------------+--------------------------------------------------------------------------------+---------+\r\n   | Support | Specification                                            | Name                                                                           | Comment |\r\n   +---------+----------------------------------------------------------+--------------------------------------------------------------------------------+---------+\r\n   | ✓       | `RFC7395 <https://tools.ietf.org/html/rfc7395>`__        | An Extensible Messaging and Presence Protocol (XMPP) Subprotocol for WebSocket |         |\r\n   +---------+----------------------------------------------------------+--------------------------------------------------------------------------------+---------+\r\n   | ✓       | `XEP-0206 <https://xmpp.org/extensions/xep-0206.html>`__ | XMPP Over BOSH                                                                 |         |\r\n   +---------+----------------------------------------------------------+--------------------------------------------------------------------------------+---------+\r\n   | ✓       | `XEP-0124 <https://xmpp.org/extensions/xep-0124.html>`__ | Bidirectional-streams Over Synchronous HTTP (BOSH)                             |         |\r\n   +---------+----------------------------------------------------------+--------------------------------------------------------------------------------+---------+\r\n\r\nIM Compliance Suite\r\n^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n.. table:: Table 3.Web Compliance Suite\r\n\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------------------------------+-------------------------------------------------------------+\r\n   | Support | Specification                                            | Name                                                                              | Comment                                                     |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------------------------------+-------------------------------------------------------------+\r\n   | ✓       | `RFC6120 <https://tools.ietf.org/html/rfc6120>`__        | Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence |                                                             |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------------------------------+-------------------------------------------------------------+\r\n   | ✓       | `XEP-0084 <https://xmpp.org/extensions/xep-0084.html>`__ | User Avatar                                                                       |                                                             |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------------------------------+-------------------------------------------------------------+\r\n   | ✓       | `XEP-0153 <https://xmpp.org/extensions/xep-0153.html>`__ | vCard-Based Avatars                                                               |                                                             |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------------------------------+-------------------------------------------------------------+\r\n   | ✓       | `XEP-0054 <https://xmpp.org/extensions/xep-0054.html>`__ | vcard-temp                                                                        |                                                             |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------------------------------+-------------------------------------------------------------+\r\n   | ✓       | `XEP-0280 <https://xmpp.org/extensions/xep-0280.html>`__ | Message Carbons                                                                   |                                                             |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------------------------------+-------------------------------------------------------------+\r\n   | ✓       | `XEP-0191 <https://xmpp.org/extensions/xep-0191.html>`__ | Blocking Command                                                                  |                                                             |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------------------------------+-------------------------------------------------------------+\r\n   | ✓       | `XEP-0045 <https://xmpp.org/extensions/xep-0045.html>`__ | Multi-User Chat                                                                   |                                                             |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------------------------------+-------------------------------------------------------------+\r\n   | ✓       | `XEP-0249 <https://xmpp.org/extensions/xep-0249.html>`__ | Direct MUC Invitations                                                            |                                                             |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------------------------------+-------------------------------------------------------------+\r\n   | ✓       | `XEP-0048 <https://xmpp.org/extensions/xep-0048.html>`__ | Bookmarks                                                                         |                                                             |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------------------------------+-------------------------------------------------------------+\r\n   | ✓       | `XEP-0223 <https://xmpp.org/extensions/xep-0223.html>`__ | Persistent Storage of Private Data via PubSub                                     |                                                             |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------------------------------+-------------------------------------------------------------+\r\n   | ✓       | `XEP-0049 <https://xmpp.org/extensions/xep-0049.html>`__ | Private XML Storage                                                               |                                                             |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------------------------------+-------------------------------------------------------------+\r\n   | ✓       | `XEP-0198 <https://xmpp.org/extensions/xep-0198.html>`__ | Stream Management                                                                 | Both ``Session Resumption`` and ``Stanza Acknowledgements`` |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------------------------------+-------------------------------------------------------------+\r\n   | ✓       | `XEP-0313 <https://xmpp.org/extensions/xep-0313.html>`__ | Message Archive Management                                                        |                                                             |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------------------------------+-------------------------------------------------------------+\r\n\r\nMobile Compliance Suite\r\n^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n.. table:: Table 4.Web Compliance Suite\r\n\r\n   +---------+----------------------------------------------------------+--------------------------------------------------------------------------------+-------------------------------------------------------------+\r\n   | Support | Specification                                            | Name                                                                           | Comment                                                     |\r\n   +---------+----------------------------------------------------------+--------------------------------------------------------------------------------+-------------------------------------------------------------+\r\n   | ✓       | `RFC7395 <https://tools.ietf.org/html/rfc7395>`__        | An Extensible Messaging and Presence Protocol (XMPP) Subprotocol for WebSocket |                                                             |\r\n   +---------+----------------------------------------------------------+--------------------------------------------------------------------------------+-------------------------------------------------------------+\r\n   | ✓       | `XEP-0198 <https://xmpp.org/extensions/xep-0198.html>`__ | Stream Management                                                              | Both ``Session Resumption`` and ``Stanza Acknowledgements`` |\r\n   +---------+----------------------------------------------------------+--------------------------------------------------------------------------------+-------------------------------------------------------------+\r\n   | ✓       | `XEP-0352 <https://xmpp.org/extensions/xep-0352.html>`__ | Client State Indication                                                        |                                                             |\r\n   +---------+----------------------------------------------------------+--------------------------------------------------------------------------------+-------------------------------------------------------------+\r\n   | ✓       | `XEP-0357 <https://xmpp.org/extensions/xep-0357.html>`__ | Push Notifications                                                             |                                                             |\r\n   +---------+----------------------------------------------------------+--------------------------------------------------------------------------------+-------------------------------------------------------------+\r\n\r\nNon-Compliance Suite Extensions\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n.. table:: Table 5.Core Compliance Suite\r\n\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | Support | Specification                                            | Name                                                      | Comment                                                      |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0004 <https://xmpp.org/extensions/xep-0004.html>`__ | Data Forms                                                |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0008 <https://xmpp.org/extensions/xep-0004.html>`__ | IQ-Based Avatars                                          |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0012 <https://xmpp.org/extensions/xep-0012.html>`__ | Last Activity                                             |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0013 <https://xmpp.org/extensions/xep-0013.html>`__ | Flexible Offline Message Retrieval                        |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0016 <https://xmpp.org/extensions/xep-0016.html>`__ | Privacy Lists                                             |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0020 <https://xmpp.org/extensions/xep-0020.html>`__ | Feature Negotiation                                       |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0022 <https://xmpp.org/extensions/xep-0022.html>`__ | Message Events                                            |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0047 <https://xmpp.org/extensions/xep-0047.html>`__ | In-Band Bytestreams                                       |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0050 <https://xmpp.org/extensions/xep-0050.html>`__ | Ad-Hoc Commands                                           |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0059 <https://xmpp.org/extensions/xep-0059.html>`__ | Result Set Management                                     |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0060 <https://xmpp.org/extensions/xep-0060.html>`__ | Publish-Subscribe                                         |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0065 <https://xmpp.org/extensions/xep-0065.html>`__ | SOCKS5 Bytestreams                                        |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0066 <https://xmpp.org/extensions/xep-0066.html>`__ | Out of Band Data                                          |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0068 <https://xmpp.org/extensions/xep-0068.html>`__ | Field Standardization for Data Forms                      |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0071 <https://xmpp.org/extensions/xep-0071.html>`__ | XHTML-IM                                                  |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0072 <https://xmpp.org/extensions/xep-0072.html>`__ | SOAP Over XMPP                                            |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0077 <https://xmpp.org/extensions/xep-0077.html>`__ | In-Band Registration                                      |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0078 <https://xmpp.org/extensions/xep-0078.html>`__ | Non-SASL Authentication                                   |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0079 <https://xmpp.org/extensions/xep-0079.html>`__ | Advanced Message Processing                               |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0080 <https://xmpp.org/extensions/xep-0080.html>`__ | User Location                                             |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0082 <https://xmpp.org/extensions/xep-0082.html>`__ | XMPP Date and Time Profiles                               |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0083 <https://xmpp.org/extensions/xep-0083.html>`__ | Nested Roster Groups                                      |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0085 <https://xmpp.org/extensions/xep-0085.html>`__ | Chat State Notifications                                  |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0086 <https://xmpp.org/extensions/xep-0086.html>`__ | Error Condition Mappings                                  |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0091 <https://xmpp.org/extensions/xep-0091.html>`__ | Legacy Delayed Delivery                                   |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0092 <https://xmpp.org/extensions/xep-0092.html>`__ | Software Version                                          |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0096 <https://xmpp.org/extensions/xep-0096.html>`__ | File Transfer                                             |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0100 <https://xmpp.org/extensions/xep-0100.html>`__ | Gateway Interaction                                       |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0106 <https://xmpp.org/extensions/xep-0106.html>`__ | JID Escaping                                              |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0107 <https://xmpp.org/extensions/xep-0107.html>`__ | User Mood                                                 | Server support via ``Personal Eventing Protocol (XEP-0163)`` |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0108 <https://xmpp.org/extensions/xep-0108.html>`__ | User Activity                                             | Server support via ``Personal Eventing Protocol (XEP-0163)`` |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0118 <https://xmpp.org/extensions/xep-0118.html>`__ | User Tune                                                 | Server support via ``Personal Eventing Protocol (XEP-0163)`` |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0127 <https://xmpp.org/extensions/xep-0127.html>`__ | Common Alerting Protocol (CAP) Over XMPP                  |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0128 <https://xmpp.org/extensions/xep-0128.html>`__ | Service Discovery Extensions                              |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0131 <https://xmpp.org/extensions/xep-0131.html>`__ | Stanza Headers and Internet Metadata (SHIM)               |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0133 <https://xmpp.org/extensions/xep-0133.html>`__ | Service Administration                                    |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0136 <https://xmpp.org/extensions/xep-0136.html>`__ | Message Archiving                                         |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0141 <https://xmpp.org/extensions/xep-0141.html>`__ | Data Forms Layout                                         |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓ [#]_  | `XEP-0142 <https://xmpp.org/extensions/xep-0142.html>`__ | Workgroup Queues                                          |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0144 <https://xmpp.org/extensions/xep-0144.html>`__ | Roster Item Exchange                                      |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0145 <https://xmpp.org/extensions/xep-0145.html>`__ | Annotations                                               |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0146 <https://xmpp.org/extensions/xep-0146.html>`__ | Remote Controlling Clients                                |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0152 <https://xmpp.org/extensions/xep-0152.html>`__ | Reachability Addresses                                    |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0155 <https://xmpp.org/extensions/xep-0155.html>`__ | Stanza Session Negotiation                                |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0156 <https://xmpp.org/extensions/xep-0156.html>`__ | Discovering Alternative XMPP Connection Methods           | Uses DNS records, so will work with Tigase XMPP Server       |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0157 <https://xmpp.org/extensions/xep-0157.html>`__ | Contact Addresses for XMPP Services                       |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0160 <https://xmpp.org/extensions/xep-0160.html>`__ | Best Practices for Handling Offline Messages              |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0166 <https://xmpp.org/extensions/xep-0166.html>`__ | Jingle                                                    |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0167 <https://xmpp.org/extensions/xep-0167.html>`__ | Jingle RTP Sessions                                       |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0170 <https://xmpp.org/extensions/xep-0170.html>`__ | Recommended Order of Stream Feature Negotiation           |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0171 <https://xmpp.org/extensions/xep-0171.html>`__ | Language Translation                                      |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0172 <https://xmpp.org/extensions/xep-0172.html>`__ | User Nickname                                             |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0174 <https://xmpp.org/extensions/xep-0174.html>`__ | Serverless Messaging                                      |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0175 <https://xmpp.org/extensions/xep-0175.html>`__ | Best Practices for Use of SASL ANONYMOUS                  |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0176 <https://xmpp.org/extensions/xep-0176.html>`__ | Jingle ICE-UDP Transport Method                           |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0177 <https://xmpp.org/extensions/xep-0177.html>`__ | Jingle Raw UDP Transport Method                           |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0178 <https://xmpp.org/extensions/xep-0178.html>`__ | Best Practices for Use of SASL EXTERNAL with Certificates |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0179 <https://xmpp.org/extensions/xep-0179.html>`__ | Jingle IAX Transport Method                               |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0180 <https://xmpp.org/extensions/xep-0180.html>`__ | Jingle Video via RTP                                      |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0181 <https://xmpp.org/extensions/xep-0181.html>`__ | Jingle DTMF                                               |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0184 <https://xmpp.org/extensions/xep-0184.html>`__ | Message Receipts                                          |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0185 <https://xmpp.org/extensions/xep-0185.html>`__ | Dialback Key Generation and Validation                    |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0190 <https://xmpp.org/extensions/xep-0190.html>`__ | Best Practice for Closing Idle Streams                    |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0199 <https://xmpp.org/extensions/xep-0199.html>`__ | XMPP Ping                                                 |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0201 <https://xmpp.org/extensions/xep-0201.html>`__ | Best Practices for Message Threads                        |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0202 <https://xmpp.org/extensions/xep-0202.html>`__ | Entity Time                                               |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0203 <https://xmpp.org/extensions/xep-0203.html>`__ | Delayed Delivery                                          |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0205 <https://xmpp.org/extensions/xep-0205.html>`__ | Best Practices to Discourage Denial of Service Attacks    |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0209 <https://xmpp.org/extensions/xep-0209.html>`__ | Metacontacts                                              |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0220 <https://xmpp.org/extensions/xep-0220.html>`__ | Server Dialback                                           |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0224 <https://xmpp.org/extensions/xep-0224.html>`__ | Attention                                                 |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0225 <https://xmpp.org/extensions/xep-0225.html>`__ | Component Connections                                     |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0226 <https://xmpp.org/extensions/xep-0226.html>`__ | Message Stanza Profiles                                   |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0231 <https://xmpp.org/extensions/xep-0231.html>`__ | Bits of Binary                                            |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0234 <https://xmpp.org/extensions/xep-0234.html>`__ | Jingle File Transfer                                      |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0245 <https://xmpp.org/extensions/xep-0245.html>`__ | The /me Command                                           |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0246 <https://xmpp.org/extensions/xep-0246.html>`__ | End-to-End XML Streams                                    |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0247 <https://xmpp.org/extensions/xep-0247.html>`__ | Jingle XML Streams                                        |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0250 <https://xmpp.org/extensions/xep-0250.html>`__ | C2C Authentication Using TLS                              |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0251 <https://xmpp.org/extensions/xep-0251.html>`__ | Jingle Session Transfer                                   |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0260 <https://xmpp.org/extensions/xep-0260.html>`__ | Jingle SOCKS5 Bytestreams Transport Method                |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0261 <https://xmpp.org/extensions/xep-0261.html>`__ | Jingle In-Band Bytestreams Transport                      |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0262 <https://xmpp.org/extensions/xep-0262.html>`__ | Use of ZRTP in Jingle RTP Sessions                        |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0277 <https://xmpp.org/extensions/xep-0277.html>`__ | Microblogging over XMPP                                   |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0292 <https://xmpp.org/extensions/xep-0292.html>`__ | vCard4 Over XMPP                                          |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0301 <https://xmpp.org/extensions/xep-0301.html>`__ | In-Band Real Time Text                                    |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0305 <https://xmpp.org/extensions/xep-0305.html>`__ | XMPP Quickstart                                           |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0323 <https://xmpp.org/extensions/xep-0323.html>`__ | Internet of Things - Sensor Data                          |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0324 <https://xmpp.org/extensions/xep-0324.html>`__ | Internet of Things - Provisioning                         |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0325 <https://xmpp.org/extensions/xep-0325.html>`__ | Internet of Things - Control                              |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0326 <https://xmpp.org/extensions/xep-0326.html>`__ | Internet of Things - Concentrators                        |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0333 <https://xmpp.org/extensions/xep-0333.html>`__ | Chat Markers                                              |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0363 <https://xmpp.org/extensions/xep-0363.html>`__ | HTTP File Upload                                          |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n   | ✓       | `XEP-0387 <https://xmpp.org/extensions/xep-0387.html>`__ | XMPP Compliance Suites 2018                               |                                                              |\r\n   +---------+----------------------------------------------------------+-----------------------------------------------------------+--------------------------------------------------------------+\r\n\r\nFull, ordered list of supported RFCs and XEPs:\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| Support | Specification                                            | Name                                                                                           | Comment                                                      |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `RFC6120 <https://tools.ietf.org/html/rfc6120>`__        | Extensible Messaging and Presence Protocol (XMPP): Core                                        |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `RFC6120 <https://tools.ietf.org/html/rfc6120>`__        | Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence              |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ⍻       | `RFC7622 <https://tools.ietf.org/html/rfc7622>`__        | Extensible Messaging and Presence Protocol (XMPP): Address Format                              | We support previous version of the specification: RFC6122    |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `RFC7395 <https://tools.ietf.org/html/rfc7395>`__        | An Extensible Messaging and Presence Protocol (XMPP) Subprotocol for WebSocket                 |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `RFC7395 <https://tools.ietf.org/html/rfc7395>`__        | An Extensible Messaging and Presence Protocol (XMPP) Subprotocol for WebSocket                 |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `RFC7590 <https://tools.ietf.org/html/rfc7590>`__        | Use of Transport Layer Security (TLS) in the Extensible Messaging and Presence Protocol (XMPP) |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0004 <https://xmpp.org/extensions/xep-0004.html>`__ | Data Forms                                                                                     |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0008 <https://xmpp.org/extensions/xep-0004.html>`__ | IQ-Based Avatars                                                                               |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0012 <https://xmpp.org/extensions/xep-0012.html>`__ | Last Activity                                                                                  |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0013 <https://xmpp.org/extensions/xep-0013.html>`__ | Flexible Offline Message Retrieval                                                             |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0016 <https://xmpp.org/extensions/xep-0016.html>`__ | Privacy Lists                                                                                  |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0020 <https://xmpp.org/extensions/xep-0020.html>`__ | Feature Negotiation                                                                            |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0022 <https://xmpp.org/extensions/xep-0022.html>`__ | Message Events                                                                                 |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0030 <https://xmpp.org/extensions/xep-0030.html>`__ | Service Discovery                                                                              |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0045 <https://xmpp.org/extensions/xep-0045.html>`__ | Multi-User Chat                                                                                |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0047 <https://xmpp.org/extensions/xep-0047.html>`__ | In-Band Bytestreams                                                                            |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0048 <https://xmpp.org/extensions/xep-0048.html>`__ | Bookmarks                                                                                      |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0049 <https://xmpp.org/extensions/xep-0049.html>`__ | Private XML Storage                                                                            |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0050 <https://xmpp.org/extensions/xep-0050.html>`__ | Ad-Hoc Commands                                                                                |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0054 <https://xmpp.org/extensions/xep-0054.html>`__ | vcard-temp                                                                                     |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0059 <https://xmpp.org/extensions/xep-0059.html>`__ | Result Set Management                                                                          |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0060 <https://xmpp.org/extensions/xep-0060.html>`__ | Publish-Subscribe                                                                              |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0065 <https://xmpp.org/extensions/xep-0065.html>`__ | SOCKS5 Bytestreams                                                                             |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0066 <https://xmpp.org/extensions/xep-0066.html>`__ | Out of Band Data                                                                               |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0068 <https://xmpp.org/extensions/xep-0068.html>`__ | Field Standardization for Data Forms                                                           |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0071 <https://xmpp.org/extensions/xep-0071.html>`__ | XHTML-IM                                                                                       |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0072 <https://xmpp.org/extensions/xep-0072.html>`__ | SOAP Over XMPP                                                                                 |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0077 <https://xmpp.org/extensions/xep-0077.html>`__ | In-Band Registration                                                                           |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0078 <https://xmpp.org/extensions/xep-0078.html>`__ | Non-SASL Authentication                                                                        |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0079 <https://xmpp.org/extensions/xep-0079.html>`__ | Advanced Message Processing                                                                    |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0080 <https://xmpp.org/extensions/xep-0080.html>`__ | User Location                                                                                  |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0082 <https://xmpp.org/extensions/xep-0082.html>`__ | XMPP Date and Time Profiles                                                                    |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0083 <https://xmpp.org/extensions/xep-0083.html>`__ | Nested Roster Groups                                                                           |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0084 <https://xmpp.org/extensions/xep-0084.html>`__ | User Avatar                                                                                    |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0085 <https://xmpp.org/extensions/xep-0085.html>`__ | Chat State Notifications                                                                       |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0086 <https://xmpp.org/extensions/xep-0086.html>`__ | Error Condition Mappings                                                                       |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0091 <https://xmpp.org/extensions/xep-0091.html>`__ | Legacy Delayed Delivery                                                                        |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0092 <https://xmpp.org/extensions/xep-0092.html>`__ | Software Version                                                                               |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0096 <https://xmpp.org/extensions/xep-0096.html>`__ | File Transfer                                                                                  |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0100 <https://xmpp.org/extensions/xep-0100.html>`__ | Gateway Interaction                                                                            |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0106 <https://xmpp.org/extensions/xep-0106.html>`__ | JID Escaping                                                                                   |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0107 <https://xmpp.org/extensions/xep-0107.html>`__ | User Mood                                                                                      | Server support via ``Personal Eventing Protocol (XEP-0163)`` |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0108 <https://xmpp.org/extensions/xep-0108.html>`__ | User Activity                                                                                  | Server support via ``Personal Eventing Protocol (XEP-0163)`` |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0114 <https://xmpp.org/extensions/xep-0114.html>`__ | Jabber Component Protocol                                                                      |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0115 <https://xmpp.org/extensions/xep-0115.html>`__ | Entity Capabilities                                                                            |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0118 <https://xmpp.org/extensions/xep-0118.html>`__ | User Tune                                                                                      | Server support via ``Personal Eventing Protocol (XEP-0163)`` |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0124 <https://xmpp.org/extensions/xep-0124.html>`__ | Bidirectional-streams Over Synchronous HTTP (BOSH)                                             |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0128 <https://xmpp.org/extensions/xep-0128.html>`__ | Service Discovery Extensions                                                                   |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0127 <https://xmpp.org/extensions/xep-0127.html>`__ | Common Alerting Protocol (CAP) Over XMPP                                                       |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0131 <https://xmpp.org/extensions/xep-0131.html>`__ | Stanza Headers and Internet Metadata (SHIM)                                                    |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0133 <https://xmpp.org/extensions/xep-0133.html>`__ | Service Administration                                                                         |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0136 <https://xmpp.org/extensions/xep-0136.html>`__ | Message Archiving                                                                              |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0141 <https://xmpp.org/extensions/xep-0141.html>`__ | Data Forms Layout                                                                              |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0142 <https://xmpp.org/extensions/xep-0142.html>`__ | Workgroup Queues                                                                               |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0144 <https://xmpp.org/extensions/xep-0144.html>`__ | Roster Item Exchange                                                                           |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0145 <https://xmpp.org/extensions/xep-0145.html>`__ | Annotations                                                                                    |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0146 <https://xmpp.org/extensions/xep-0146.html>`__ | Remote Controlling Clients                                                                     |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0152 <https://xmpp.org/extensions/xep-0152.html>`__ | Reachability Addresses                                                                         |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0153 <https://xmpp.org/extensions/xep-0153.html>`__ | vCard-Based Avatars                                                                            |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0155 <https://xmpp.org/extensions/xep-0155.html>`__ | Stanza Session Negotiation                                                                     |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0156 <https://xmpp.org/extensions/xep-0156.html>`__ | Discovering Alternative XMPP Connection Methods                                                | Uses DNS records, so will work with Tigase XMPP Server       |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0157 <https://xmpp.org/extensions/xep-0157.html>`__ | Contact Addresses for XMPP Services                                                            |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0160 <https://xmpp.org/extensions/xep-0160.html>`__ | Best Practices for Handling Offline Messages                                                   |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0163 <https://xmpp.org/extensions/xep-0163.html>`__ | Personal Eventing Protocol                                                                     |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0166 <https://xmpp.org/extensions/xep-0166.html>`__ | Jingle                                                                                         |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0167 <https://xmpp.org/extensions/xep-0167.html>`__ | Jingle RTP Sessions                                                                            |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0170 <https://xmpp.org/extensions/xep-0170.html>`__ | Recommended Order of Stream Feature Negotiation                                                |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0171 <https://xmpp.org/extensions/xep-0171.html>`__ | Language Translation                                                                           |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0172 <https://xmpp.org/extensions/xep-0172.html>`__ | User Nickname                                                                                  |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0174 <https://xmpp.org/extensions/xep-0174.html>`__ | Serverless Messaging                                                                           |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0175 <https://xmpp.org/extensions/xep-0175.html>`__ | Best Practices for Use of SASL ANONYMOUS                                                       |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0176 <https://xmpp.org/extensions/xep-0176.html>`__ | Jingle ICE-UDP Transport Method                                                                |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0177 <https://xmpp.org/extensions/xep-0177.html>`__ | Jingle Raw UDP Transport Method                                                                |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0178 <https://xmpp.org/extensions/xep-0178.html>`__ | Best Practices for Use of SASL EXTERNAL with Certificates                                      |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0179 <https://xmpp.org/extensions/xep-0179.html>`__ | Jingle IAX Transport Method                                                                    |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0180 <https://xmpp.org/extensions/xep-0180.html>`__ | Jingle Video via RTP                                                                           |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0181 <https://xmpp.org/extensions/xep-0181.html>`__ | Jingle DTMF                                                                                    |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0184 <https://xmpp.org/extensions/xep-0184.html>`__ | Message Receipts                                                                               |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0185 <https://xmpp.org/extensions/xep-0185.html>`__ | Dialback Key Generation and Validation                                                         |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0190 <https://xmpp.org/extensions/xep-0190.html>`__ | Best Practice for Closing Idle Streams                                                         |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0191 <https://xmpp.org/extensions/xep-0191.html>`__ | Blocking Command                                                                               |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0198 <https://xmpp.org/extensions/xep-0198.html>`__ | Stream Management                                                                              | Both ``Session Resumption`` and ``Stanza Acknowledgements``  |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0199 <https://xmpp.org/extensions/xep-0199.html>`__ | XMPP Ping                                                                                      |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0201 <https://xmpp.org/extensions/xep-0201.html>`__ | Best Practices for Message Threads                                                             |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0202 <https://xmpp.org/extensions/xep-0202.html>`__ | Entity Time                                                                                    |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0203 <https://xmpp.org/extensions/xep-0203.html>`__ | Delayed Delivery                                                                               |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0205 <https://xmpp.org/extensions/xep-0205.html>`__ | Best Practices to Discourage Denial of Service Attacks                                         |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0206 <https://xmpp.org/extensions/xep-0206.html>`__ | XMPP Over BOSH                                                                                 |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0209 <https://xmpp.org/extensions/xep-0209.html>`__ | Metacontacts                                                                                   |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0220 <https://xmpp.org/extensions/xep-0220.html>`__ | Server Dialback                                                                                |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0223 <https://xmpp.org/extensions/xep-0223.html>`__ | Persistent Storage of Private Data via PubSub                                                  |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0224 <https://xmpp.org/extensions/xep-0224.html>`__ | Attention                                                                                      |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0225 <https://xmpp.org/extensions/xep-0225.html>`__ | Component Connections                                                                          |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0226 <https://xmpp.org/extensions/xep-0226.html>`__ | Message Stanza Profiles                                                                        |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0231 <https://xmpp.org/extensions/xep-0231.html>`__ | Bits of Binary                                                                                 |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0234 <https://xmpp.org/extensions/xep-0234.html>`__ | Jingle File Transfer                                                                           |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0245 <https://xmpp.org/extensions/xep-0245.html>`__ | The /me Command                                                                                |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0246 <https://xmpp.org/extensions/xep-0246.html>`__ | End-to-End XML Streams                                                                         |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0247 <https://xmpp.org/extensions/xep-0247.html>`__ | Jingle XML Streams                                                                             |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0249 <https://xmpp.org/extensions/xep-0249.html>`__ | Direct MUC Invitations                                                                         |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0250 <https://xmpp.org/extensions/xep-0250.html>`__ | C2C Authentication Using TLS                                                                   |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0251 <https://xmpp.org/extensions/xep-0251.html>`__ | Jingle Session Transfer                                                                        |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0260 <https://xmpp.org/extensions/xep-0260.html>`__ | Jingle SOCKS5 Bytestreams Transport Method                                                     |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0261 <https://xmpp.org/extensions/xep-0261.html>`__ | Jingle In-Band Bytestreams Transport                                                           |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0262 <https://xmpp.org/extensions/xep-0262.html>`__ | Use of ZRTP in Jingle RTP Sessions                                                             |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0277 <https://xmpp.org/extensions/xep-0277.html>`__ | Microblogging over XMPP                                                                        |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0280 <https://xmpp.org/extensions/xep-0280.html>`__ | Message Carbons                                                                                |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0292 <https://xmpp.org/extensions/xep-0292.html>`__ | vCard4 Over XMPP                                                                               |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0301 <https://xmpp.org/extensions/xep-0301.html>`__ | In-Band Real Time Text                                                                         |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0305 <https://xmpp.org/extensions/xep-0305.html>`__ | XMPP Quickstart                                                                                |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0313 <https://xmpp.org/extensions/xep-0313.html>`__ | Message Archive Management                                                                     |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0323 <https://xmpp.org/extensions/xep-0323.html>`__ | Internet of Things - Sensor Data                                                               |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0324 <https://xmpp.org/extensions/xep-0324.html>`__ | Internet of Things - Provisioning                                                              |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0325 <https://xmpp.org/extensions/xep-0325.html>`__ | Internet of Things - Control                                                                   |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0326 <https://xmpp.org/extensions/xep-0326.html>`__ | Internet of Things - Concentrators                                                             |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0333 <https://xmpp.org/extensions/xep-0333.html>`__ | Chat Markers                                                                                   |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0352 <https://xmpp.org/extensions/xep-0352.html>`__ | Client State Indication                                                                        |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0357 <https://xmpp.org/extensions/xep-0357.html>`__ | Push Notifications                                                                             |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0363 <https://xmpp.org/extensions/xep-0363.html>`__ | HTTP File Upload                                                                               |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0368 <https://xmpp.org/extensions/xep-0368.html>`__ | SRV records for XMPP over TLS                                                                  | Requires adding DNS entries pointing to port 5223            |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n| ✓       | `XEP-0387 <https://xmpp.org/extensions/xep-0387.html>`__ | XMPP Compliance Suites 2018                                                                    |                                                              |\r\n+---------+----------------------------------------------------------+------------------------------------------------------------------------------------------------+--------------------------------------------------------------+\r\n\r\n.. [#]\r\n   Requires commercial license\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Administration/index.rst",
    "content": "=============================================\r\nAdministration Guide\r\n=============================================\r\n\r\n.. toctree::\r\n   :titlesonly:\r\n   :numbered: 3\r\n\r\n   Release_Notes/Tigase_Release_Notes\r\n   Tigase_User_Guide/User_Guide\r\n   XMPP_Server/About_Tigase_XMPP_Server\r\n   Licensing_Open_Source\r\n   Tigase_Server_Binary_Updates\r\n   Quick_Start_Guide/Intro\r\n   Configuration/_Configuration\r\n   Security/_Security\r\n   Database_Management/Management\r\n   Components/_Components\r\n   Using_Tigase/_using_tigase\r\n   Statistics_Description\r\n   Properties/_properties\r\n   "
  },
  {
    "path": "src/main/restructured/Tigase_Development/Basic_Information.rst",
    "content": "Basic Information\r\n========================\r\n\r\nTigase Architecture\r\n---------------------\r\n\r\nThe most important thing to understand is that Tigase is very modular and you can have multiple components running inside single instance. However one of the most important components is MessageRouter, which sits in the centre and serves as a, as name suggest, packet router directing packets to the appropriate components.\r\n\r\nThere is also a group of specialised component responsible for handling users connections: ``ConnectionManagers`` (``c2s``, ``s2s``, ``ws2s``, ``bosh``). They receive packets from the incoming connection, then subsequently they forward processed packet to ``MessageRouter``. Most of the time, especially for packets coming from user connections, packet is routed to ``SessionManager`` component (with the session object referring to appropriate user in case of client to server connection). After processing in ``SessionManager`` packet goes back to ``MessageRouter`` and then, based on the stanza addressing\\` can go to different component (muc, pubsub) or if it’s addressed to another user it can go through:\r\n\r\n-  ``SessionManager`` (again), ``MessageRouter`` and then (user) ``ConnectionManagers`` or,\r\n\r\n-  ``s2s`` (*server to server connection manager*) if the user or component is on the different, federated, xmpp server;\r\n\r\nIn a very broad view this can be depicted with a following graph:\r\n\r\n|Tigase architecture|\r\n\r\nTigase Server Elements\r\n--------------------------\r\n\r\nTo make it easier to get into the code below are defined basic terms in the Tigase server world and there is a brief explanation how the server is designed and implemented. This document also points you to basic interfaces and implementations which can be used as example code reference.\r\n\r\nLogically all server code can be divided into 3 kinds of modules: **components**, **plug-ins** and **connectors**.\r\n\r\n1. **Components** are the main element of Tigase server. Components are a bigger piece of code which can have separate address, receive and send stanzas, and be configured to respond to numerous events. Sample components implemented for Tigase server are: *c2s connection manager*, *s2s connection manager*, *session manager*, *XEP-0114 - external component connection manager*, *MUC - multi user char rooms*.\r\n\r\n2. **Plug-ins** are usually small pieces of code responsible for processing specific XMPP stanzas. They don’t have their own address. As a result of stanza processing they can produce new XMPP stanzas. Plug-ins are loaded by *session manager* component or the *c2s connection manager* component. Sample plug-ins are: *vCard* stanza processing, *jabber:iq:register* to register new user accounts, *presence* stanza processing, and *jabber:iq:auth* for non-sasl authentication.\r\n\r\n3. **Connectors** are modules responsible for access to data repositories like databases or LDAP to store and retrieve user data. There are 2 kinds of connectors: authentication connectors and user data connectors. Both of them are independent and can connect to different data sources. Sample connectors are: *JDBC database* connector, *XMLDB - embedded database* connector.\r\n\r\nThere is an API defined for each kind of above modules and all you have to do is enable the implementation of that specific interface. Then the module can be loaded to the server based on it’s configuration settings. There is also abstract classes available, implementing these interfaces to make development easier.\r\n\r\nHere is a brief list of all interfaces to look at and for more details you have to refer to the guide for specific kind of module.\r\n\r\nComponents\r\n^^^^^^^^^^^^^^^^\r\n\r\nThis is list of interfaces to look at when you work on a new component:\r\n\r\n1. **tigase.server.ServerComponent** - This is the very basic interface for component. All components must implement it.\r\n\r\n2. **tigase.server.MessageReceiver** - This interface extends ``ServerComponent`` and is required to implement by components which want to receive data packets like *session manager* and *c2s connection manager*.\r\n\r\n3. **tigase.conf.Configurable** - Implementing this interface is required to make it configurable. For each object of this type, configuration is pushed to it at any time at runtime. This is necessary to make it possible to change configuration at runtime. Be careful to implement this properly as it can cause issues for modules that cannot be configured.\r\n\r\n4. **tigase.disco.XMPPService** - Objects using this interface can respond to \"ServiceDiscovery\" requests.\r\n\r\n5. **tigase.stats.StatisticsContainer** - Objects using this interface can return runtime statistics. Any object can collect job statistics and implementing this interface guarantees that statistics will be presented in consisted way to user who wants to see them.\r\n\r\nInstead of implementing above interfaces directly, it is recommended to extend one of existing abstract classes which take care of the most of \"dirty and boring\" stuff. Here is a list the most useful abstract classes:\r\n\r\n-  **tigase.server.AbstractMessageReceiver** - Implements 4 basic interfaces:\r\n\r\n   ``ServerComponent``, ``MessageReceiver``, ``Configurable`` and ``StatisticsContainer``. AbstractMessageReceiver also manages internal data queues using it’s own threads which prevents dead-locks from resource starvation. It offers even-driven data processing which means whenever packet arrives the ``abstract void processPacket(Packet packet);`` method is called to process it. You have to implement this abstract method in your component, if your component wants to send a packet (in response to data it received for example).\r\n\r\n   .. code:: java\r\n\r\n      boolean addOutPacket(Packet packet)\r\n\r\n-  **tigase.server.ConnectionManager** - This is an extension of ``AbstractMessageReceiver`` abstract class. As the name says this class takes care of all network connection management stuff. If your component needs to send and receive data directly from the network (like c2s connection, s2s connection or external component) you should use this implementation as a basic class. It takes care of all things related to networking, I/O, reconnecting, listening on socket, connecting and so on. If you extend this class you have to expect data coming from to sources:\r\n\r\n   From the ``MessageRouter`` and this is when the ``abstract void processPacket(Packet packet);`` method is called and second, from network connection and then the ``abstract Queue processSocketData(XMPPIOService serv);`` method is called.\r\n\r\nPlug-ins\r\n^^^^^^^^^\r\n\r\nAll Tigase plugins currently implemented are located in package: tigase.xmpp.impl. You can use this code as a sample code base. There are 3 types of plug-ins and they are defined in interfaces located in ``tigase.xmpp`` package:\r\n\r\n1. **XMPPProcessorIfc** - The most important and basic plug-in. This is the most common plug-in type which just processes stanzas in normal mode. It receives packets, processes them on behalf of the user and returns resulting stanzas.\r\n\r\n2. **XMPPPreprocessorIfc** - This plugin performs pre-processing of the packet, intended for the pre-processors to setup for packet blocking.\r\n\r\n3. **XMPPPostprocessorIfc** - This plugin performs processing of packets for which there was no specific processor.\r\n\r\nConnector\r\n------------\r\n\r\nData, Stanzas, Packets - Data Flow and Processing\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nData received from the network are read from the network sockets as bytes by code in the ``tigase.io`` package. Bytes then are changed into characters in classes of ``tigase.net`` package and as characters they are sent to the XML parser (``tigase.xml``) which turns them to XML DOM structures.\r\n\r\nAll data inside the server is exchanged in XML DOM form as this is the format used by XMPP protocol. For basic XML data processing (parsing characters stream, building DOM, manipulate XML elements and attributes) we use `Tigase XML parser and DOM builder <https://github.com/tigase/tigase-xmltools>`__.\r\n\r\nEach stanza is stored in the ``tigase.xml.Element`` object. Every Element can contain any number of **Child Elements** and any number of attributes. You can access all these data through the class API.\r\n\r\nTo simplify some, most common operations Element is wrapped in ``tigase.server.Packet`` class which offers another level of API for the most common operations like preparation of response stanza based on the element it contains (swap to/from values, put type=result attribute and others).\r\n\r\n.. |Tigase architecture| image:: /images/devguide/tigase-architecture.svg\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Development/Cluster_Map_Interface.rst",
    "content": "Cluster Map Interface\r\n==========================\r\n\r\nStarting with v7.1.0, a cluster map interface has been implemented. The cluster map is aided by use of the distributed event bus system to communicate between all clusters.\r\n\r\nRequirements\r\n--------------------------\r\n\r\nAny full distribution of Tigase will support the Cluster Map API so long as the eventbus component is not disabled. JDK v8 is required for this feature, however since Tigase requires this, you should already have it installed.\r\n\r\nThe cluster map is stored in memory and follows the ``map.util.interface`` java standards can be used to improve cluster connections, and help clustered servers keep track of each other.\r\n\r\nMap Creation\r\n----------------\r\n\r\nMap must be created with the following command:\r\n\r\n.. code:: java\r\n\r\n   java.util.Map<String, String> map = ClusterMapFactory.get().createMap(\"type\",String.class,String.class,\"1\",\"2\",\"3\" )\r\n\r\nWhere \"type\" is the map ID. This creates the map locally and then fires an event to all clustered servers. Each cluster server has an event handler waiting for, in this case, ``NewMapCreate`` event. Map Key class and Map Value class are used to type conversion. Arrays of strings are parameters, for example ID of user session. Once received, the distributed eventbus will create a local map.\r\n\r\n.. code:: java\r\n\r\n   eventBus.addHandler(MapCreatedEvent.class, new EventHandler<MapCreatedEvent>() {\r\n       @Override\r\n       public void onEvent(MapCreatedEvent e) {\r\n       }\r\n   });\r\n\r\nA brief example of a map creation is shown here:\r\n\r\n.. code:: java\r\n\r\n   java.util.Map<String, String> map = ClusterMapFactory.get().createMap(\"Very_Important_Map_In_User_Session\",JID.class,Boolean.class,\"user-session-identifier-123\");\r\n\r\nThis will fire event ``MapCreatedEvent`` on all other cluster nodes. Strings \"Very_Important_Map_In_User_Session\" and \"user-session-identifier-123\" are given as parameters in :literal:`onMapCreated()\\`` method. The event consumer code must know what to do with map with type \"Very_Important_Map_In_User_Session\". It may retrieve user session \"user-session-identifier-123\" and put this map in this session. It should be used to tell other nodes how to treat the event with a newly created map, and it should be stored in user session.\r\n\r\nMap Changes\r\n----------------\r\n\r\nChanges to the map on one cluster will trigger ``AddValue`` or ``RemoveValue`` events in eventbus. Stanzas sent between clusters will look something like this:\r\n\r\n.. code:: xml\r\n\r\n   <ElementAdd xmlns=\"tigase:clustered:map\">\r\n     <uid>1-2-3</uid>\r\n     <item>\r\n       <key>xKEY</key>\r\n       <value>xVALUE</value>\r\n     </item>\r\n     <item>\r\n       <key>yKEY</key>\r\n       <value>yVALUE</value>\r\n     </item>\r\n   </ElementAdd>\r\n\r\nCode to handle adding an item:\r\n\r\n.. code:: java\r\n\r\n   eventBus.addHandler(ElementAdd, tigase:clustered:map, new EventHandler() {\r\n     @Override\r\n     public void onEvent(String name, String xmlns, Element event) {\r\n     });\r\n\r\nWhere the element 'event' is the UID, and the name string is the name of the map key/value pair.\r\n\r\nThis example removes an element from the cluster map. Removal of items look similar:\r\n\r\n.. code:: xml\r\n\r\n   <ElementRemove xmlns=\"tigase:clustered:map\">\r\n     <uid>1-2-3</uid>\r\n     <item>\r\n       <key>xKEY</key>\r\n       <value>xVALUE</value>\r\n     </item>\r\n   </ElementRemove>\r\n\r\nwith the code also being similar:\r\n\r\n.. code:: java\r\n\r\n   eventBus.addHandler(ElementRemove, tigase:clustered:map, new EventHandler() {\r\n     @Override\r\n     public void onEvent(String name, String xmlns, Element name) {\r\n     });\r\n\r\n\r\nMap Destruction\r\n------------------\r\n\r\nJava Garbage Collector will normally remove a local map if it is no longer used. Clustered maps however are not removed in this manner. These maps must be destroyed manually if they are no longer used:\r\n\r\n.. code:: java\r\n\r\n   ClusterMapFactory.get().destroyMap(clmap);\r\n\r\nCalling this, the map named clmap will be destroyed on each cluster node.\r\n\r\nThe event handler will catch event when map is destroyed on another cluster node:\r\n\r\n.. code:: java\r\n\r\n   eventBus.addHandler(MapDestroyedEvent.class, new EventHandler<MapDestroyedEvent>() {\r\n       @Override\r\n       public void onEvent(MapDestroyedEvent event) {\r\n       }\r\n   });\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Development/CodeStyleGuide.rst",
    "content": "Tigase Code Style\r\n=====================\r\n\r\nIntroduction\r\n-------------\r\n\r\n\r\nThis documents defines and describes coding style and standard used in Tigase projects source code.\r\n\r\nExamples should be considered as **non-normative**, that is formatting choices should not be treated as rules.\r\n\r\nSource file basics\r\n----------------------\r\n\r\nTechnicals details\r\n^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n-  File name consists of the case-sensitive, camel-cased name of the top-level class it contains plus the ``.java`` extension.\r\n\r\n-  Source files are encoded in **UTF-8**.\r\n\r\n\r\nSource file structure\r\n--------------------------\r\n\r\nA source file consists of, **in order**:\r\n\r\n1. License or copyright information, if present\r\n\r\n2. Package statement\r\n\r\n3. Import statements\r\n\r\n4. Exactly one top-level class\r\n\r\nAdditionally:\r\n\r\n-  **Exactly one blank line** separates sections 2-4;\r\n\r\n-  The package statement is **not line-wrapped** (column limit does not apply);\r\n\r\nImport statements\r\n^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n-  Wildcard imports can be used for:\r\n\r\n   -  more than 5 class imports;\r\n\r\n   -  more than 3 name imports;\r\n\r\n-  import statements are **not line-wrapped** (column limit does not apply);\r\n\r\n-  following import ordering applies:\r\n\r\n   -  all imports not pertaining to any of the groups listed below\r\n\r\n   -  ``blank line``\r\n\r\n   -  ``javax.*`` classes\r\n\r\n   -  ``java.*`` classes\r\n\r\n   -  ``blank line``\r\n\r\n   -  all static imports in single block\r\n\r\n-  items in each block are ordered by names in ASCII sort order (since ``;`` sorts before ``.``)\r\n\r\nClass declaration\r\n^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n-  Each top-level class resides in a source file of its own.\r\n\r\nClass contents order\r\n~~~~~~~~~~~~~~~~~~~~\r\n\r\nFollowing order of the elements of the class is mandatory:\r\n\r\n-  ``final``, ``static`` fields in following order:\r\n\r\n   -  ``public``\r\n\r\n   -  ``protected``\r\n\r\n   -  ``package-private``\r\n\r\n   -  ``private``\r\n\r\n-  ``public`` ``enum``\r\n\r\n-  ``static`` fields in following order:\r\n\r\n   -  ``public``\r\n\r\n   -  ``protected``\r\n\r\n   -  ``package-private``\r\n\r\n   -  ``private``\r\n\r\n-  ``static`` initializer block\r\n\r\n-  ``final`` fields in following order:\r\n\r\n   -  ``public``\r\n\r\n   -  ``protected``\r\n\r\n   -  ``package-private``\r\n\r\n   -  ``private``\r\n\r\n-  fields without modifiers in following order:\r\n\r\n   -  ``public``\r\n\r\n   -  ``protected``\r\n\r\n   -  ``package-private``\r\n\r\n   -  ``private``\r\n\r\n-  initializer block\r\n\r\n-  ``static`` method(s)\r\n\r\n-  constructor(s)\r\n\r\n-  methods(s) without modifiers\r\n\r\n-  enums(s) without modifiers\r\n\r\n-  interfaces(s) without modifiers\r\n\r\n-  inner ``static`` classes\r\n\r\n-  inner classes\r\n\r\nIn addition:\r\n\r\n-  Getters and Setters are kept together\r\n\r\n-  Overloads are never split - multiple constructors or methods with the same name appear sequentially.\r\n\r\nFormatting\r\n-------------\r\n\r\nBraces\r\n^^^^^^^^\r\n\r\n-  Braces are mandatory in optional cases - for all syntax where braces use can be optional, Tigase mandate using braces even if the body is empty or contains only single statement.\r\n\r\n-  Braces follow the Kernighan and Ritchie style (`Egyptian brackets <http://www.codinghorror.com/blog/2012/07/new-programming-jargon.html>`__):\r\n\r\n   -  No line break before the opening brace.\r\n\r\n   -  Line break after the opening brace.\r\n\r\n   -  Line break before the closing brace.\r\n\r\n   -  Line break after the closing brace, *only if* that brace terminates a statement or terminates the body of a method, constructor, or *named* class. For example, there is *no* line break after the brace if it is followed by ``else`` or a comma.\r\n\r\n\r\nBlock indentation: tab\r\n^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nAll indentation (opening a new block of block-like construct) must be made with tabs. After the block, then indent returns to the previous.\r\n\r\nIdeal tab-size: 4\r\n\r\nColumn limit: 120\r\n^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nDefined column limit is 120 characters and must be line-wrapped as described below Java code has a column limit of 100 characters. Except as noted below, any line that would exceed this limit must be line-wrapped, as explained in section :ref:`Line-wrapping<linewrapping>`.\r\n\r\n.. _linewrapping:\r\n\r\nLine-wrapping\r\n^^^^^^^^^^^^^^^^\r\n\r\n*line-wrapping* is a process of dividing long lines that would otherwise go over the defined Column Limit (above). It’s recommended to wrap lines whenever it’s possible even if they are not longer than defined limit.\r\n\r\nWhitespace\r\n^^^^^^^^^^^^^^^^\r\n\r\nVertical Whitespace\r\n~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nA single blank line appears:\r\n\r\n-  after package statement;\r\n\r\n-  before imports;\r\n\r\n-  after imports;\r\n\r\n-  around class;\r\n\r\n-  after class header;\r\n\r\n-  around field in interface;\r\n\r\n-  around method in interface;\r\n\r\n-  around method;\r\n\r\n-  around initializer;\r\n\r\n-  as required by other sections of this document.\r\n\r\nMultiple blank lines are not permitted.\r\n\r\nHorizontal whitespace\r\n~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nBeyond where required by the language or other style rules, and apart from literals, comments and Javadoc, a single ASCII space also appears in the following places **only**.\r\n\r\n1. Separating any reserved word, such as ``if``, ``for``, ``while``, ``switch``, ``try``, ``catch`` or ``synchronized``, from an open parenthesis (``(``) that follows it on that line\r\n\r\n2. Separating any reserved word, such as ``else`` or ``catch``, from a closing curly brace (``}``) that precedes it on that line\r\n\r\n3. Before any open curly brace (``{``), with two exceptions:\r\n\r\n   -  ``@SomeAnnotation({a, b})`` (no space is used)\r\n\r\n   -  ``String[][] x = {{\"foo\"}};`` (no space is required between ``{{``, by item 8 below)\r\n\r\n4. On both sides of any binary or ternary operator. This also applies to the following \"operator-like\" symbols:\r\n\r\n   -  the ampersand in a conjunctive type bound: ``<T extends Foo & Bar>``\r\n\r\n   -  the pipe for a catch block that handles multiple exceptions: ``catch (FooException | BarException e)``\r\n\r\n   -  the colon (``:``) in an enhanced ``for`` (\"foreach\") statement\r\n\r\n   -  the arrow in a lambda expression: ``(String str) → str.length()``\r\n\r\n      **but not:**\r\n\r\n   -  the two colons (``::``) of a method reference, which is written like ``Object::toString``\r\n\r\n   -  the dot separator (``.``), which is written like ``object.toString()``\r\n\r\n5. After ``,:;`` or the closing parenthesis (``)``) of a cast\r\n\r\n6. Between the type and variable of a declaration: ``List<String> list``\r\n\r\nHorizontal alignment: never required\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\n*Horizontal alignment* is the practice of adding a variable number of additional spaces in your code with the goal of making certain tokens appear directly below certain other tokens on previous lines.\r\n\r\nThis practice is permitted, but is **never required**. It is not even required to *maintain* horizontal alignment in places where it was already used.\r\n\r\nSpecific constructs\r\n^^^^^^^^^^^^^^^^^^^^\r\n\r\nEnum classes\r\n~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nAfter each comma that follows an enum constant, a line break is mandatory.\r\n\r\nVariable declarations\r\n~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\n-  One variable per declaration - Every variable declaration (field or local) declares only one variable: declarations such as ``int a, b;`` are not used.\r\n\r\n-  Declared when needed -Local variables are **not** habitually declared at the start of their containing block or block-like construct. Instead, local variables are declared close to the point they are first used (within reason), to minimize their scope. Local variable declarations typically have initializers, or are initialized immediately after declaration.\r\n\r\nArrays\r\n~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nAny array initializer may *optionally* be formatted as if it were a \"block-like construct.\" (especially when line-wrapping need to be applied).\r\n\r\nNaming\r\n-----------------\r\n\r\nRules common to all identifiers\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIdentifiers use only ASCII letters and digits, and, in a small number of cases noted below, underscores. Thus each valid identifier name is matched by the regular expression ``\\w+`` .\r\n\r\nSpecific Rules by identifier type\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\n-  Package names are all lowercase, with consecutive words simply concatenated together (no underscores, not camel-case).\r\n\r\n-  Class names are written in **UpperCamelCase**.\r\n\r\n-  Method names are written in **lowerCamelCase**.\r\n\r\n-  Constant names use ``CONSTANT_CASE``: all uppercase letters, with words separated by underscores.\r\n\r\n-  Non-constant field names (static or otherwise) are written in **lowerCamelCase**.\r\n\r\n-  Parameter names are written in **lowerCamelCase** (one-character parameter names in public methods should be avoided).\r\n\r\n-  Local variable names are written in **lowerCamelCase**.\r\n\r\nProgramming Practices\r\n------------------------\r\n\r\n-  A method is marked with the ``@Override`` annotation whenever it is legal. This includes a class method overriding a superclass method, a class method implementing an interface method, and an interface method re-specifying a super-interface method.\r\n\r\n-  Caught exceptions should not be ignored (and if this is a must then a log entry is required).\r\n\r\nJavadoc\r\n----------\r\n\r\n-  blank lines should be inserted after:\r\n\r\n   -  description,\r\n\r\n   -  parameter description,\r\n\r\n   -  return tag;\r\n\r\n-  empty tag should be included for following tags:\r\n\r\n   -  ``@params``\r\n\r\n   -  ``@return``\r\n\r\n   -  ``@throws``\r\n\r\nUsage\r\n^^^^^^^\r\n\r\nAt the *minimum*, Javadoc is present for every ``public`` class, and every ``public`` or ``protected`` member of such a class, with a few exceptions:\r\n\r\n-  is optional for \"simple, obvious\" methods like ``getFoo``, in cases where there *really and truly* is nothing else worthwhile to say but \"Returns the foo\".\r\n\r\n-  in methods that overrides a supertype method.\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Development/Component_Implementation.rst",
    "content": "Component Development\r\n==========================\r\n\r\nA component in the Tigase is an entity with its own JID address. It can receive packets, process them, and can also generate packets.\r\n\r\nAn example of the best known components is MUC or PubSub. In Tigase however, almost everything is actually a component: Session Manager, s2s connections manager, Message Router, etc…​ Components are loaded based on the server configuration, new components can be loaded and activated at run-time. You can easily replace a component implementation and the only change to make is a class name in the configuration entry.\r\n\r\nCreating components for Tigase server is an essential part of the server development hence there is a lot of useful API and ready to use code available. This guide should help you to get familiar with the API and how to quickly and efficiently create your own component implementations.\r\n\r\n1. :ref:`Component implementation - Lesson 1 - Basics<cil1>`\r\n\r\n2. :ref:`Component implementation - Lesson 2 - Configuration<cil2>`\r\n\r\n3. :ref:`Component implementation - Lesson 3 - Multi-Threading<cil3>`\r\n\r\n4. :ref:`Component implementation - Lesson 4 - Service Discovery<cil4>`\r\n\r\n5. :ref:`Component implementation - Lesson 5 - Statistics<cil5>`\r\n\r\n6. :ref:`Component implementation - Lesson 6 - Scripting Support<cil6>`\r\n\r\n7. :ref:`Component implementation - Lesson 7 - Data Repository<cil7>`\r\n\r\n8. :ref:`Component implementation - Lesson 8 - Startup Time<cil8>`\r\n\r\n9. :ref:`Packet Filtering in Component<packetfiltering>`\r\n\r\n.. _cil1:\r\n\r\nComponent Implementation - Lesson 1 - Basics\r\n------------------------------------------------\r\n\r\nCreating a Tigase component is actually very simple and with broad API available you can create a powerful component with just a few lines of code. You can find detailed API description elsewhere. This series presents hands on lessons with code examples, teaching how to get desired results in the simplest possible code using existing Tigase API.\r\n\r\nEven though all Tigase components are just implementations of the **ServerComponent** interface I will keep such a low level information to necessary minimum. Creating a new component based on just interfaces, while very possible, is not very effective. This guide intends to teach you how to make use of what is already there, ready to use with a minimal coding effort.\r\n\r\nThis is just the first lesson of the series where I cover basics of the component implementation.\r\n\r\nLet’s get started and create the Tigase component:\r\n\r\n.. code:: java\r\n\r\n   import java.util.logging.Logger;\r\n   import tigase.component.AbstractKernelBasedComponent;\r\n   import tigase.server.Packet;\r\n\r\n   public class TestComponent extends AbstractKernelBasedComponent {\r\n\r\n     private static final Logger log = Logger.getLogger(TestComponent.class.getName());\r\n\r\n     @Override\r\n     public String getComponentVersion() {\r\n       String version = this.getClass().getPackage().getImplementationVersion();\r\n       return version == null ? \"0.0.0\" : version;\r\n     }\r\n\r\n     @Override\r\n     public boolean isDiscoNonAdmin() {\r\n       return false;\r\n     }\r\n\r\n     @Override\r\n     protected void registerModules(Kernel kernel) {\r\n       // here we need to register modules responsible for processing packets\r\n     }\r\n\r\n   }\r\n\r\nAs you can see we have 3 mandatory methods when we extends **AbstractKernelBasedComponent**:\r\n\r\n-  **String getComponentVersion()** which returns version of a component for logging purposes\r\n\r\n-  **boolean isDiscoNonAdmin()** which decides if component will be visible for users other that server administrators\r\n\r\n-  **void registerModules(Kernel kernel)** which allows you to register component modules responsible for actual processing of packets\r\n\r\n.. Tip::\r\n\r\n   If you decide you do not want to use modules for processing packets (even though we strongly suggest to use them, as thanks to modules components are easily extendible) you can implement one more method **void processPacket(Packet packet)** which will be called for every packet sent to a component. This method is actually logical as the main task for your component is processing packets.\r\n\r\nClass name for our new component is **TestComponent** and we have also initialized a separated logger for this class. Doing This is very useful as it allows us to easily find log entries created by our class.\r\n\r\nWith these a few lines of code you have a fully functional Tigase component which can be loaded to the Tigase server; it can receive and process packets, shows as an element on service discovery list (for administrators only), responds to administrator ad-hoc commands, supports scripting, generates statistics, can be deployed as an external component, and a few other things.\r\n\r\nNext important step is to create modules responsible for processing packets. For now let’s create module responsible for handling messages by appending them to log file:\r\n\r\n.. code:: java\r\n\r\n   @Bean(name = \"test-module\", parent = TestComponent.class, active = true)\r\n   public static class TestModule extends AbstractModule {\r\n\r\n     private static final Logger log = Logger.getLogger(TestModule.class.getCanonicalName());\r\n\r\n     private static final Criteria CRITERIA = ElementCriteria.name(\"message\");\r\n\r\n     @Override\r\n     public Criteria getModuleCriteria() {\r\n       return CRITERIA;\r\n     }\r\n\r\n     @Override\r\n     public void process(Packet packet) throws ComponentException, TigaseStringprepException {\r\n       log.finest(\"My packet: \" + packet.toString());\r\n     }\r\n   }\r\n\r\nInstance of ``Criteria`` class returned by ``Criteria getModuleCriteria()`` is used by component class to decide if packet should be processed by this module or not. In this case we returned instance which matches any packet which is a **message**.\r\n\r\nAnd finally we have a very important method ``void process(Packet packet)`` which is main processing method of a component. If component will receive packet that matches criteria returned by module - this method will be called.\r\n\r\nBut how we can send packet from a module? **AbstractModule** contains method **void write(Packet packet)** which you can use to send packets from a component.\r\n\r\nBefore we go any further with the implementation let’s configure the component in Tigase server so it is loaded next time the server starts. Assuming our **init.tdsl** file looks like this one:\r\n\r\n::\r\n\r\n   'config-type' = 'default'\r\n   'debug' = ['server']\r\n   'default-virtual-host' = [ 'devel.tigase.org' ]\r\n   admins = [ 'admin@devel.tigase.org' ]\r\n   dataSource {\r\n     default () {\r\n       uri = 'jdbc:derby:/Tigase/tigasedb'\r\n     }\r\n   }\r\n   muc() {}\r\n   pubsub() {}\r\n\r\nWe can see that it already is configured to load two other components: **MUC** and **PubSub**. Let’s add a third - our new component to the configuration file by appending the following line in the properties file:\r\n\r\n.. code::\r\n\r\n   test(class: TestComponent) {}\r\n\r\nNow we have to restart the server.\r\n\r\nThere are a few ways to check whether our component has been loaded to the server. Probably the easiest is to connect to the server from an administrator account and look at the service discovery list.\r\n\r\n|service disco test comp admin 300|\r\n\r\nIf everything goes well you should see an entry on the list similar to the highlighted one on the screenshot. The component description is \"*Undefined description*\" which is a default description and we can change it later on, the component default JID is: **test@devel.tigase.org**, where **devel.tigase.org** is the server domain and test is the component name.\r\n\r\nAnother way to find out if the component has been loaded is by looking at the log files. Getting yourself familiar with Tigase log files will be very useful thing if you plan on developing Tigase components. So let’s look at the log file **logs/tigase.log.0**, if the component has been loaded you should find following lines in the log:\r\n\r\n.. code:: bash\r\n\r\n   MessageRouter.setProperties() FINER: Loading and registering message receiver: test\r\n   MessageRouter.addRouter() INFO: Adding receiver: TestComponent\r\n   MessageRouter.addComponent() INFO: Adding component: TestComponent\r\n\r\nIf your component did not load you should first check configuration files. Maybe the Tigase could not find your class at startup time. Make sure your class is in **CLASSPATH** or copy a JAR file with your class to Tigase **jars/** directory.\r\n\r\nAssuming everything went well and your component is loaded by the sever and it shows on the service discovery list as on the screenshot above you can double click on it to get a window with a list of ad-hoc commands - administrator scripts. A window on the screenshot shows only two basic commands for adding and removing script which is a good start.\r\n\r\n|commands list test 200|\r\n\r\nMoreover, you can browse the server statistics in the service discovery window to find your new test component on the list. If you click on the component it shows you a window with component statistics, very basic packets counters.\r\n\r\n|service disco stats 200|\r\n\r\nAs we can see with just a few lines of code our new component is quite mighty and can do a lot of things without much effort from the developer side.\r\n\r\nNow, the time has come to the most important question. Can our new component do something useful, that is can it receive and process XMPP packets?\r\n\r\nLet’s try it out. Using you favorite client send a message to JID: **test@devel.tigase.org** (assuming your server is configured for **devel.tigase.org** domain). You can either use kind of XML console in your client or just send a plain message to the component JID. According to our code in **process(…​)** method it should log our message. For this test I have sent a message with subject: \"*test message*\" and body: \"*this is a test*\". The log file should contain following entry:\r\n\r\n.. code:: bash\r\n\r\n   TestModule.process() FINEST: My packet: to=null, from=null,\r\n   data=<message from=\"admin@devel.tigase.org/devel\"\r\n     to=\"test@devel.tigase.org\" id=\"abcaa\" xmlns=\"jabber:client\">\r\n     <subject>test message</subject>\r\n     <body>this is a test</body>\r\n   </message>, XMLNS=jabber:client, priority=NORMAL\r\n\r\nIf this is a case we can be sure that everything works as expected and all we now have to do is to fill the **process(…​)** method with some useful code.\r\n\r\n.. |service disco test comp admin 300| image:: /images/devguide/service-disco-test-comp-admin-300.png\r\n.. |commands list test 200| image:: /images/devguide/commands-list-test-200.png\r\n.. |service disco stats 200| image:: /images/devguide/service-disco-stats-200.png\r\n\r\n.. _cil2:\r\n\r\nComponent Implementation - Lesson 2 - Configuration\r\n----------------------------------------------------------------\r\n\r\nIt might be hard to tell what the first important thing you should do with your new component implementation. Different developers may have a different view on this. It seems to me however that it is always a good idea to give to your component a way to configure it and provide some runtime settings.\r\n\r\nThis guide describes how to add configuration handling to your component.\r\n\r\nTo demonstrate how to implement component configuration let’s say we want to configure which types of packets will be logged by the component. There are three possible packet types: **message**, **presence** and **iq** and we want to be able to configure logging of any combination of the three. Furthermore we also want to be able to configure the text which is prepended to the logged message and to optionally switch secure login. (Secure logging replaces all packet CData with text: *CData size: NN* to protect user privacy.)\r\n\r\nLet’s create the following private variables in our component **TestModule**:\r\n\r\n.. code:: java\r\n\r\n   private String[] packetTypes = {\"message\", \"presence\", \"iq\"};\r\n   private String prependText = \"My packet: \";\r\n   private boolean secureLogging = false;\r\n\r\nTo make them configurable we have to annote them with ``@ConfigField`` annotation. It requires ``desc`` field (describing configuration option) and has following optional properties: \\* ``alias`` - alternative name of the variable \\* ``allowAliasFromParent`` - specifies whether value from alias from parent bean should be allowed \\* ``type`` - specifies general type of the field, which impacts possible obfuscation of the value in the logs/\"config-dump\" file; possible values: ``Plain`` (no changes), ``Password`` (complete value will be obfuscated) and ``JdbcUrl`` (only password part will be obfuscated from the URL)\r\n\r\n.. code:: java\r\n\r\n   @ConfigField(desc = \"Logged packet types\", alias = \"packet-types\")\r\n   private String[] packetTypes = {\"message\", \"presence\", \"iq\"};\r\n   @ConfigField(desc = \"Prefix\", alias = \"log-prepend\")\r\n   private String prependText = \"My packet: \";\r\n   @ConfigField(desc = \"Secure logging\", alias = \"secure-logging\")\r\n   private boolean secureLogging = false;\r\n\r\nAnd this is it. Tigase Kernel will take care of this fields and will update them when configuration will change.\r\n\r\nThe syntax in ``config.tdsl`` file is very simple and is described in details in the *Admin Guide*. To set the configuration for your component in ``config.tdsl`` file you have to append following lines to the file inside test component configuration block:\r\n\r\n.. code::\r\n\r\n   test-module {\r\n     log-prepend = 'My packet: '\r\n     packet-types = [ 'message', 'presence', 'iq' ]\r\n     secure-logging = true\r\n   }\r\n\r\nThe square brackets are used to mark that we set a list consisting of a few elements, have a look at the *Admin Guide* documentation for more details.\r\n\r\nAnd this is the complete code of the new component module with a modified ``process(…​)`` method taking advantage of configuration settings:\r\n\r\n.. code:: java\r\n\r\n   @Bean(name = \"test-module\", parent = TestComponent.class, active = true)\r\n   public static class TestModule extends AbstractModule {\r\n\r\n     private static final Logger log = Logger.getLogger(TestModule.class.getCanonicalName());\r\n\r\n     private Criteria CRITERIA = ElementCriteria.name(\"message\");\r\n\r\n     @ConfigField(desc = \"Logged packet types\", alias = \"packet-types\")\r\n     private String[] packetTypes = {\"message\", \"presence\", \"iq\"};\r\n     @ConfigField(desc = \"Prefix\", alias = \"log-prepend\")\r\n     private String prependText = \"My packet: \";\r\n     @ConfigField(desc = \"Secure logging\", alias = \"secure-logging\")\r\n     private boolean secureLogging = false;\r\n\r\n     @Override\r\n     public Criteria getModuleCriteria() {\r\n       return CRITERIA;\r\n     }\r\n\r\n     public void setPacketTypes(String[] packetTypes) {\r\n       this.packetTypes = packetTypes;\r\n       Criteria crit = new Or();\r\n       for (String packetType : packetTypes) {\r\n         crit.add(ElementCriteria.name(packetType));\r\n       }\r\n       CRITERIA = crit;\r\n     }\r\n\r\n     @Override\r\n     public void process(Packet packet) throws ComponentException, TigaseStringprepException {\r\n       log.finest(prependText + packet.toString(secureLogging));\r\n     }\r\n   }\r\n\r\nOf course we can do much more useful packet processing in the ``process(…​)`` method. This is just an example code.\r\n\r\n.. Tip::\r\n\r\n   Here we used a setter **setPacketType(String[] packetTypes)** which is a setter for field **packetTypes**. Tigase Kernel will use it instead of assigning value directly to a field which gives up opportunity to convert value to different type and update other field - in our case we updated **CRITERIA** field which will result in change of packet types which for which method **void process(…​)** will be called.\r\n\r\n.. _cil3:\r\n\r\nComponent Implementation - Lesson 3 - Multi-Threading\r\n-------------------------------------------------------------\r\n\r\nMulti core and multi CPU machines are very common nowadays. Your new custom component however, processes all packets in a single thread.\r\n\r\nThis is especially important if the packet processing is CPU expensive like, for example, SPAM checking. In such a case you could experience single Core/CPU usage at 100% while other Cores/CPUs are idling. Ideally, you want your component to use all available CPUs.\r\n\r\nTigase API offers a very simple way to execute component’s ``processPacket(Packet packet)`` method in multiple threads. Methods ``int processingOutThreads()`` and ``int processingInThreads()`` returns number of threads assigned to the component. By default it returns just '1' as not all component implementations are prepared to process packets concurrently. By overwriting the method you can return any value you think is appropriate for the implementation. Please note, there are two methods, one is for a number of threads for incoming packets to the component and another for outgoing packets from the component. It used to be a single method but different components have different needs and the best performance can be achieved when the outgoing queues have a separate threads pool from incoming queues. Also some components only receive packets while other only send, therefore assigning an equal number of threads for both that could be a waste of resources.\r\n\r\n.. Note::\r\n\r\n   Due to how Kernel works you MUST avoid using variables in those methods. If you would like to have this configurable at startup time you could simply set ``processing-in-threads`` and ``processing-out-threads`` in your component’s bean configuration.\r\n\r\nIf the packet processing is CPU bound only, you normally want to have as many threads as there are CPUs available:\r\n\r\n.. code:: java\r\n\r\n   @Override\r\n   public int processingInThreads() {\r\n     return Runtime.getRuntime().availableProcessors();\r\n   }\r\n   @Override\r\n   public int processingOutThreads() {\r\n     return Runtime.getRuntime().availableProcessors();\r\n   }\r\n\r\nIf the processing is I/O bound (network or database) you probably want to have more threads to process requests. It is hard to guess the ideal number of threads right on the first try. Instead you should run a few tests to see how many threads is best for implementation of the component.\r\n\r\nNow you have many threads for processing your packets, but there is one slight problem with this. In many cases packet order is essential. If our ``processPacket(…​)`` method is executed concurrently by a few threads it is quite possible that a message sent to user can takeover the message sent earlier. Especially if the first message was large and the second was small. We can prevent this by adjusting the method responsible for packet distribution among threads.\r\n\r\nThe algorithm for packets distribution among threads is very simple:\r\n\r\n.. code:: java\r\n\r\n   int thread_idx = hashCodeForPacket(packet) % threads_total;\r\n\r\nSo the key here is using the ``hashCodeForPacket(…​)`` method. By overwriting it we can make sure that all packets addressed to the same user will always be processed by the same thread:\r\n\r\n.. code:: java\r\n\r\n   @Override\r\n   public int hashCodeForPacket(Packet packet) {\r\n     if (packet.getElemTo() != null) {\r\n       return packet.getElemTo().hashCode();\r\n     }\r\n     // This should not happen, every packet must have a destination\r\n     // address, but maybe our SPAM checker is used for checking\r\n     // strange kind of packets too....\r\n     if (packet.getStanzaFrom() != null) {\r\n       return packet.getStanzaFrom().hashCode();\r\n     }\r\n     // If this really happens on your system you should look\r\n     // carefully at packets arriving to your component and\r\n     // find a better way to calculate hashCode\r\n     return 1;\r\n   }\r\n\r\nThe above two methods give control over the number of threads assigned to the packets processing in your component and to the packet distribution among threads. This is not all Tigase API has to offer in terms of multi-threading.\r\n\r\nSometimes you want to perform some periodic actions. You can of course create Timer instance and load it with TimerTasks. As there might be a need for this, every level of the Class hierarchy could end-up with multiple Timer (threads in fact) objects doing similar job and using resources. There are a few methods which allow you to reuse common Timer object to perform all sorts of actions.\r\n\r\nFirst, you have three methods allowing your to perform some periodic actions:\r\n\r\n.. code:: java\r\n\r\n   public synchronized void everySecond();\r\n   public synchronized void everyMinute();\r\n   public synchronized void everyHour();\r\n\r\nAn example implementation for periodic notifications sent to some address could look like this one:\r\n\r\n.. code:: java\r\n\r\n   @Override\r\n   public synchronized void everyMinute() {\r\n     super.everyMinute();\r\n     if ((++delayCounter) >= notificationFrequency) {\r\n       addOutPacket(Packet.getMessage(abuseAddress, getComponentId(),\r\n         StanzaType.chat, \"Detected spam messages: \" + spamCounter,\r\n         \"Spam counter\", null, newPacketId(\"spam-\")));\r\n       delayCounter = 0;\r\n       spamCounter = 0;\r\n     }\r\n   }\r\n\r\nThis method sends every **notificationFrequency** minute a message to **abuseAddress** reporting how many spam messages have been detected during last period. Please note, you have to call ``super.everyMinute()`` to make sure other actions are executed as well and you have to also remember to keep processing in this method to minimum, especially if you overwrite ``everySecond()`` method.\r\n\r\nThere is also a method which allow you to schedule tasks executed at certain time, it is very similar to the ``java.util.Timer`` API. The only difference is that we are using **ScheduledExecutorService** as a backend which is being reused among all levels of Class hierarchy. There is a separate ``ScheduledExecutorService`` for each Class instance though, to avoid interferences between separate components:\r\n\r\n.. code:: java\r\n\r\n   addTimerTask(tigase.util.TimerTask task, long delay);\r\n\r\nHere is a code of an example component and module which uses all the API discussed in this article:\r\n\r\n**Example component code.**\r\n\r\n.. code:: java\r\n\r\n   public class TestComponent extends AbstractKernelBasedComponent {\r\n\r\n     private static final Logger log = Logger.getLogger(TestComponent.class.getName());\r\n\r\n     @Inject\r\n     private TestModule testModule;\r\n\r\n     @Override\r\n     public synchronized void everyMinute() {\r\n       super.everyMinute();\r\n       testModule.everyMinute();\r\n     }\r\n\r\n     @Override\r\n     public String getComponentVersion() {\r\n     String version = this.getClass().getPackage().getImplementationVersion();\r\n       return version == null ? \"0.0.0\" : version;\r\n     }\r\n\r\n     @Override\r\n     public int hashCodeForPacket(Packet packet) {\r\n       if (packet.getElemTo() != null) {\r\n         return packet.getElemTo().hashCode();\r\n       }\r\n       // This should not happen, every packet must have a destination\r\n       // address, but maybe our SPAM checker is used for checking\r\n       // strange kind of packets too....\r\n       if (packet.getStanzaFrom() != null) {\r\n         return packet.getStanzaFrom().hashCode();\r\n       }\r\n       // If this really happens on your system you should look carefully\r\n       // at packets arriving to your component and decide a better way\r\n       // to calculate hashCode\r\n       return 1;\r\n     }\r\n\r\n     @Override\r\n     public boolean isDiscoNonAdmin() {\r\n       return false;\r\n     }\r\n\r\n     @Override\r\n     public int processingInThreads() {\r\n       return Runtime.getRuntime().availableProcessors();\r\n     }\r\n\r\n     @Override\r\n     public int processingOutThreads() {\r\n       return Runtime.getRuntime().availableProcessors();\r\n     }\r\n\r\n     @Override\r\n     protected void registerModules(Kernel kernel) {\r\n       // here we need to register modules responsible for processing packets\r\n     }\r\n\r\n   }\r\n\r\n**Example module code.**\r\n\r\n.. code:: java\r\n\r\n   @Bean(name = \"test-module\", parent = TestComponent.class, active = true)\r\n   public static class TestModule extends AbstractModule {\r\n\r\n     private static final Logger log = Logger.getLogger(TestModule.class.getCanonicalName());\r\n\r\n     private Criteria CRITERIA = ElementCriteria.name(\"message\");\r\n\r\n     @ConfigField(desc = \"Bad words\", alias = \"bad-words\")\r\n     private String[] badWords = {\"word1\", \"word2\", \"word3\"};\r\n     @ConfigField(desc = \"White listed addresses\", alias = \"white-list\")\r\n     private String[] whiteList = {\"admin@localhost\"};\r\n     @ConfigField(desc = \"Logged packet types\", alias = \"packet-types\")\r\n     private String[] packetTypes = {\"message\", \"presence\", \"iq\"};\r\n     @ConfigField(desc = \"Prefix\", alias = \"log-prepend\")\r\n     private String prependText = \"Spam detected: \";\r\n     @ConfigField(desc = \"Secure logging\", alias = \"secure-logging\")\r\n     private boolean secureLogging = false;\r\n     @ConfigField(desc = \"Abuse notification address\", alias = \"abuse-address\")\r\n     private JID abuseAddress = JID.jidInstanceNS(\"abuse@locahost\");\r\n     @ConfigField(desc = \"Frequency of notification\", alias = \"notification-frequency\")\r\n     private int notificationFrequency = 10;\r\n     private int delayCounter = 0;\r\n     private long spamCounter = 0;\r\n\r\n     @Inject\r\n     private TestComponent component;\r\n\r\n     public void everyMinute() {\r\n       if ((++delayCounter) >= notificationFrequency) {\r\n         write(Message.getMessage(abuseAddress, component.getComponentId(), StanzaType.chat,\r\n                                  \"Detected spam messages: \" + spamCounter, \"Spam counter\", null,\r\n                                  component.newPacketId(\"spam-\")));\r\n         delayCounter = 0;\r\n         spamCounter = 0;\r\n       }\r\n     }\r\n\r\n     @Override\r\n     public Criteria getModuleCriteria() {\r\n       return CRITERIA;\r\n     }\r\n\r\n     public void setPacketTypes(String[] packetTypes) {\r\n       this.packetTypes = packetTypes;\r\n       Criteria crit = new Or();\r\n       for (String packetType : packetTypes) {\r\n         crit.add(ElementCriteria.name(packetType));\r\n       }\r\n       CRITERIA = crit;\r\n     }\r\n\r\n     @Override\r\n     public void process(Packet packet) throws ComponentException, TigaseStringprepException {\r\n       // Is this packet a message?\r\n       if (\"message\" == packet.getElemName()) {\r\n         String from = packet.getStanzaFrom().toString();\r\n         // Is sender on the whitelist?\r\n         if (Arrays.binarySearch(whiteList, from) < 0) {\r\n           // The sender is not on whitelist so let's check the content\r\n           String body = packet.getElemCDataStaticStr(Message.MESSAGE_BODY_PATH);\r\n           if (body != null && !body.isEmpty()) {\r\n             body = body.toLowerCase();\r\n             for (String word : badWords) {\r\n               if (body.contains(word)) {\r\n                 log.finest(prependText + packet.toString(secureLogging));\r\n                 ++spamCounter;\r\n                 return;\r\n               }\r\n             }\r\n           }\r\n         }\r\n       }\r\n       // Not a SPAM, return it for further processing\r\n       Packet result = packet.swapFromTo();\r\n       write(result);\r\n     }\r\n   }\r\n\r\n.. _cil4:\r\n\r\nComponent Implementation - Lesson 4 - Service Discovery\r\n----------------------------------------------------------\r\n\r\nYou component still shows in the service discovery list as an element with \"*Undefined description*\". It also doesn’t provide any interesting features or sub-nodes.\r\n\r\nIn this article I will show how to, in a simple way, change the basic component information presented on the service discovery list and how to add some service disco features. As a bit more advanced feature the guide will teach you about adding/removing service discovery nodes at run-time and about updating existing elements.\r\n\r\nIn order for the component to properly respond to ``disco#info`` and ``disco#items`` request you should register ``DiscoveryModule`` in your component:\r\n\r\n.. code:: java\r\n\r\n   @Override\r\n   protected void registerModules(Kernel kernel) {\r\n       kernel.registerBean(\"disco\").asClass(DiscoveryModule.class).exec();\r\n   }\r\n\r\nNOTE It’s essential to **explicitly** register ``DiscoveryModule`` in your component.\r\n\r\nComponent description and category type can be changed by overriding two following methods:\r\n\r\n.. code:: java\r\n\r\n   @Override\r\n   public String getDiscoDescription() {\r\n     return \"Spam filtering\";\r\n   }\r\n\r\n   @Override\r\n   public String getDiscoCategoryType() {\r\n     return \"spam\";\r\n   }\r\n\r\nPlease note, there is no such **'spam'** category type defined in the `Service Discovery Identities registry <http://xmpp.org/registrar/disco-categories.html>`__. It has been used here as a demonstration only. Please refer to the Service Discovery Identities registry document for a list of categories and types and pick the one most suitable for you.\r\n\r\nAfter you have added the two above methods and restarted the server with updated code, have a look at the service discovery window. You should see something like on the screenshot.\r\n\r\n|spam filtering disco small|\r\n\r\nNow let’s add method which will allow our module ``TestModule`` to return supported features. This way our component will automatically report features supported by all it’s modules. To do so we need to implement a method **String[] getFeatures()** which returns array of ``String`` items. This items are used to generate a list of features supported by component.\r\n\r\nAlthough this was easy, this particular change doesn’t affect anything apart from just a visual appearance. Let’s get then to more advanced and more useful changes.\r\n\r\nOne of the limitations of methods above is that you can not update or change component information at run-time with these methods. They are called only once during initialization of a component when component service discovery information is created and prepared for later use. Sometimes, however it is useful to be able to change the service discovery during run-time.\r\n\r\nIn our simple spam filtering component let’s show how many messages have been checked out as part of the service discovery description string. Every time we receive a message we can to call:\r\n\r\n.. code:: java\r\n\r\n   updateServiceDiscoveryItem(getName(), null, getDiscoDescription() + \": [\" + (++messagesCounter) + \"]\", true);\r\n\r\n*A small performance note, in some cases calling ``updateServiceDiscoveryItem(…​)`` might be an expensive operation so probably a better idea would be to call the method not every time we receive a message but maybe every 100 times or so.*\r\n\r\nThe first parameter is the component JID presented on the service discovery list. However, Tigase server may work for many virtual hosts so the hostname part is added by the lower level functions and we only provide the component name here. The second parameter is the service discovery node which is usually '**null**' for top level disco elements. Third is the item description (which is actually called 'name' in the disco specification). The last parameter specifies if the element is visible to administrators only.\r\n\r\n|spam filter counter small|\r\n\r\nThe complete method code is presented below and the screenshot above shows how the element of the service discovery for our component can change if we apply our code and send a few messages to the component.\r\n\r\nUsing the method we can also add submodes to our component element. The XMPP service discovery really is not for showing application counters, but this case it is good enough to demonstrate the API available in Tigase so we continue with presenting our counters via service discovery. This time, instead of using 'null' as a node we put some meaningful texts as in example below:\r\n\r\n.. code:: java\r\n\r\n   // This is called whenever a message arrives\r\n   // to the component\r\n   updateServiceDiscoveryItem(getName(), \"messages\",\r\n     \"Messages processed: [\" + (++messagesCounter) + \"]\", true);\r\n   // This is called every time the component detects\r\n   // spam message\r\n   updateServiceDiscoveryItem(getName(), \"spam\", \"Spam caught: [\" +\r\n     (++totalSpamCounter) + \"]\", true);\r\n\r\nAgain, have a look at the full method body below for a complete code example. Now if we send a few messages to the component and some of them are spam (contain words recognized as spam) we can browse the service discovery of the server. Your service discovery should show a list similar to the one presented on the screenshot on the left.\r\n\r\nOf course depending on the implementation, initially there might be no sub-nodes under our component element if we call the ``updateServiceDiscoveryItem(…​)`` method only when a message is processed. To make sure that sub-nodes of our component show from the very beginning you can call them in ``setProperties(…​)`` for the first time to populate the service discovery with initial sub-nodes.\r\n\r\nPlease note, the ``updateServiceDiscoveryItem(…​)`` method is used for adding a new item and updating existing one. There is a separate method though to remove the item:\r\n\r\n.. code:: java\r\n\r\n   void removeServiceDiscoveryItem(String jid,\r\n     String node, String description)\r\n\r\nActually only two first parameters are important: the **jid** and the **node** which must correspond to the existing, previously created service discovery item.\r\n\r\nThere are two additional variants of the *update* method which give you more control over the service discovery item created. Items can be of different categories and types and can also present a set of features.\r\n\r\nThe simpler is a variant which sets a set of features for the updated service discovery item. There is a `document <http://xmpp.org/registrar/disco-features.html>`__ describing existing, registered features. We are creating an example which is going to be a spam filter and there is no predefined feature for spam filtering but for purpose of this guide we can invent two feature identification strings and set it for our component. Let’s call ``update`` method with following parameters:\r\n\r\n.. code:: java\r\n\r\n   updateServiceDiscoveryItem(getName(), null, getDiscoDescription(),\r\n     true, \"tigase:x:spam-filter\", \"tigase:x:spam-reporting\");\r\n\r\nThe best place to call this method is the ``setProperties(…​)`` method so our component gets a proper service discovery settings at startup time. We have set two features for the component disco: *tigase:x:spam-filter* and *tigase:x:spam-reporting*. This method accepts a variable set of arguments so we can pass to it as many features as we need or following Java spec we can just pass an array of **Strings**.\r\n\r\nUpdate your code with call presented above, and restart the server. Have a look at the service discovery for the component now.\r\n\r\nThe last functionality might be not very useful for our case of the spam filtering component, but it is for many other cases like MUC or PubSub for which it is setting proper category and type for the service discovery item. There is a document listing all currently registered service discovery identities (categories and types). Again there is entry for spam filtering. Let’s use the *automation* category and *spam-filter* type and set it for our component:\r\n\r\n.. code:: java\r\n\r\n   updateServiceDiscoveryItem(getName(), null, getDiscoDescription(),\r\n     \"automation\", \"spam-filtering\", true,\r\n     \"tigase:x:spam-filter\", \"tigase:x:spam-reporting\");\r\n\r\nOf course all these setting can be applied to any service discovery create or update, including sub-nodes. And here is a complete code of the component:\r\n\r\n**Example component code.**\r\n\r\n.. code:: java\r\n\r\n   public class TestComponent extends AbstractKernelBasedComponent {\r\n\r\n     private static final Logger log = Logger.getLogger(TestComponent.class.getName());\r\n\r\n     @Inject\r\n     private TestModule testModule;\r\n\r\n     @Override\r\n     public synchronized void everyMinute() {\r\n       super.everyMinute();\r\n       testModule.everyMinute();\r\n     }\r\n\r\n     @Override\r\n     public String getComponentVersion() {\r\n     String version = this.getClass().getPackage().getImplementationVersion();\r\n       return version == null ? \"0.0.0\" : version;\r\n     }\r\n\r\n     @Override\r\n     public String getDiscoDescription() {\r\n       return \"Spam filtering\";\r\n     }\r\n\r\n     @Override\r\n     public String getDiscoCategoryType() {\r\n         return \"spam\";\r\n     }\r\n\r\n     @Override\r\n     public int hashCodeForPacket(Packet packet) {\r\n       if (packet.getElemTo() != null) {\r\n         return packet.getElemTo().hashCode();\r\n       }\r\n       // This should not happen, every packet must have a destination\r\n       // address, but maybe our SPAM checker is used for checking\r\n       // strange kind of packets too....\r\n       if (packet.getStanzaFrom() != null) {\r\n         return packet.getStanzaFrom().hashCode();\r\n       }\r\n       // If this really happens on your system you should look carefully\r\n       // at packets arriving to your component and decide a better way\r\n       // to calculate hashCode\r\n       return 1;\r\n     }\r\n\r\n     @Override\r\n     public boolean isDiscoNonAdmin() {\r\n       return false;\r\n     }\r\n\r\n     @Override\r\n     public int processingInThreads() {\r\n       return Runtime.getRuntime().availableProcessors();\r\n     }\r\n\r\n     @Override\r\n     public int processingOutThreads() {\r\n       return Runtime.getRuntime().availableProcessors();\r\n     }\r\n\r\n     @Override\r\n     protected void registerModules(Kernel kernel) {\r\n       // here we need to register modules responsible for processing packets\r\n       kernel.registerBean(\"disco\").asClass(DiscoveryModule.class).exec();\r\n     }\r\n\r\n   }\r\n\r\n**Example module code.**\r\n\r\n.. code:: java\r\n\r\n   @Bean(name = \"test-module\", parent = TestComponent.class, active = true)\r\n   public static class TestModule extends AbstractModule {\r\n\r\n     private static final Logger log = Logger.getLogger(TestModule.class.getCanonicalName());\r\n\r\n     private Criteria CRITERIA = ElementCriteria.name(\"message\");\r\n     private String[] FEATURES = { \"tigase:x:spam-filter\", \"tigase:x:spam-reporting\" };\r\n\r\n     @ConfigField(desc = \"Bad words\", alias = \"bad-words\")\r\n     private String[] badWords = {\"word1\", \"word2\", \"word3\"};\r\n     @ConfigField(desc = \"White listed addresses\", alias = \"white-list\")\r\n     private String[] whiteList = {\"admin@localhost\"};\r\n     @ConfigField(desc = \"Logged packet types\", alias = \"packet-types\")\r\n     private String[] packetTypes = {\"message\", \"presence\", \"iq\"};\r\n     @ConfigField(desc = \"Prefix\", alias = \"log-prepend\")\r\n     private String prependText = \"Spam detected: \";\r\n     @ConfigField(desc = \"Secure logging\", alias = \"secure-logging\")\r\n     private boolean secureLogging = false;\r\n     @ConfigField(desc = \"Abuse notification address\", alias = \"abuse-address\")\r\n     private JID abuseAddress = JID.jidInstanceNS(\"abuse@locahost\");\r\n     @ConfigField(desc = \"Frequency of notification\", alias = \"notification-frequency\")\r\n     private int notificationFrequency = 10;\r\n     private int delayCounter = 0;\r\n     private long spamCounter = 0;\r\n     private long totalSpamCounter = 0;\r\n     private long messagesCounter = 0;\r\n\r\n\r\n     @Inject\r\n     private TestComponent component;\r\n\r\n     public void everyMinute() {\r\n       if ((++delayCounter) >= notificationFrequency) {\r\n         write(Message.getMessage(abuseAddress, component.getComponentId(), StanzaType.chat,\r\n                                  \"Detected spam messages: \" + spamCounter, \"Spam counter\", null,\r\n                                  component.newPacketId(\"spam-\")));\r\n         delayCounter = 0;\r\n         spamCounter = 0;\r\n       }\r\n     }\r\n\r\n     @Override\r\n     public String[] getFeatures() {\r\n       return FEATURES;\r\n     }\r\n\r\n     @Override\r\n     public Criteria getModuleCriteria() {\r\n       return CRITERIA;\r\n     }\r\n\r\n     public void setPacketTypes(String[] packetTypes) {\r\n       this.packetTypes = packetTypes;\r\n       Criteria crit = new Or();\r\n       for (String packetType : packetTypes) {\r\n         crit.add(ElementCriteria.name(packetType));\r\n       }\r\n       CRITERIA = crit;\r\n     }\r\n\r\n     @Override\r\n     public void process(Packet packet) throws ComponentException, TigaseStringprepException {\r\n       // Is this packet a message?\r\n       if (\"message\" == packet.getElemName()) {\r\n         component.updateServiceDiscoveryItem(component.getName(), \"messages\",\r\n                                              \"Messages processed: [\" + (++messagesCounter) + \"]\", true);\r\n         String from = packet.getStanzaFrom().toString();\r\n         // Is sender on the whitelist?\r\n         if (Arrays.binarySearch(whiteList, from) < 0) {\r\n           // The sender is not on whitelist so let's check the content\r\n           String body = packet.getElemCDataStaticStr(Message.MESSAGE_BODY_PATH);\r\n           if (body != null && !body.isEmpty()) {\r\n             body = body.toLowerCase();\r\n             for (String word : badWords) {\r\n               if (body.contains(word)) {\r\n                 log.finest(prependText + packet.toString(secureLogging));\r\n                 ++spamCounter;\r\n                 component.updateServiceDiscoveryItem(component.getName(), \"spam\", \"Spam caught: [\" +\r\n                                                      (++totalSpamCounter) + \"]\", true);\r\n                 return;\r\n               }\r\n             }\r\n           }\r\n         }\r\n       }\r\n       // Not a SPAM, return it for further processing\r\n       Packet result = packet.swapFromTo();\r\n       write(result);\r\n     }\r\n   }\r\n\r\n.. |spam filtering disco small| image:: /images/devguide/spam-filtering-disco-small.png\r\n.. |spam filter counter small| image:: /images/devguide/spam-filter-counter-small.png\r\n\r\n.. _cil5:\r\n\r\nComponent Implementation - Lesson 5 - Statistics\r\n------------------------------------------------------\r\n\r\nIn most cases you’ll want to gather some run-time statistics from your component to see how it works, detect possible performance issues or congestion problems. All server statistics are exposed and are accessible via XMPP with ad-hoc commands, HTTP, JMX and some selected statistics are also available via SNMP. As a component developer you don’t have to do anything to expose your statistic via any of those protocols, you just have to provide your statistics and the admin will be able to access them any way he wants.\r\n\r\nThis lesson will teach you how to add your own statistics and how to make sure that the statistics generation doesn’t affect application performance.\r\n\r\n|spam statitics small|\r\n\r\nYour component from the very beginning generates some statistics by classes it inherits. Let’s add a few statistics to our spam filtering component:\r\n\r\n.. code:: java\r\n\r\n   @Override\r\n   public void getStatistics(StatisticsList list) {\r\n     super.getStatistics(list);\r\n     list.add(getName(), \"Spam messages found\", totalSpamCounter, Level.INFO);\r\n     list.add(getName(), \"All messages processed\", messagesCounter, Level.FINER);\r\n     if (list.checkLevel(Level.FINEST)) {\r\n       // Some very expensive statistics generation code...\r\n     }\r\n   }\r\n\r\nThe code should be pretty much self-explanatory.\r\n\r\nYou have to call ``super.getStatistics(…​)`` to update stats of the parent class. ``StatisticsList`` is a collection which keeps all the statistics in a way which is easy to update, search, and retrieve them. You actually don’t need to know all the implementation details but if you are interested please refer to the source code and JavaDoc documentation.\r\n\r\nThe first parameter of the ``add(…​)`` method is the component name. All the statistics are grouped by the component names to make it easier to look at particular component data. Next is a description of the element. The third parameter is the element value which can be any number or string.\r\n\r\nThe last parameter is probably the most interesting. The idea has been borrowed from the logging framework. Each statistic item has importance level. Levels are exactly the same as for logging methods with **SEVERE** the most critical and **FINEST** the least important. This parameter has been added to improve performance and statistics retrieval. When the **StatisticsList** object is created it gets assigned a level requested by the user. If the ``add(…​)`` method is called with lower priority level then the element is not even added to the list. This saves network bandwidth, improves statistics retrieving speed and is also more clear to present to the end-user.\r\n\r\nOne thing which may be a bit confusing at first is that, if there is a numerical element added to statistics with **0** value then the Level is always forced to **FINEST**. The assumption is that the administrator is normally not interested **zero-value** statistics, therefore unless he intentionally request the lowest level statistics he won’t see elements with **zeros**.\r\n\r\nThe **if** statement requires some explanation too. Normally adding a new statistics element is not a very expensive operation so passing it with ``add(…​)`` method at an appropriate level is enough. Sometimes, however preparing statistics data may be quite expensive, like reading/counting some records from database. Statistics can be collected quite frequently therefore it doesn’t make sense to collect the statistics at all if there not going to be used as the current level is higher then the item we pass anyway. In such a case it is recommended to test whether the element level will be accepted by the collection and if not skip the whole processing altogether.\r\n\r\nAs you can see, the API for generating and presenting component statistics is very simple and straightforward. Just one method to overwrite and a simple way to pass your own counters. Below is the whole code of the example component:\r\n\r\n**Example component code.**\r\n\r\n.. code:: java\r\n\r\n   public class TestComponent extends AbstractKernelBasedComponent {\r\n\r\n     private static final Logger log = Logger.getLogger(TestComponent.class.getName());\r\n\r\n     @Inject\r\n     private TestModule testModule;\r\n\r\n     @Override\r\n     public synchronized void everyMinute() {\r\n       super.everyMinute();\r\n       testModule.everyMinute();\r\n     }\r\n\r\n     @Override\r\n     public String getComponentVersion() {\r\n     String version = this.getClass().getPackage().getImplementationVersion();\r\n       return version == null ? \"0.0.0\" : version;\r\n     }\r\n\r\n     @Override\r\n     public String getDiscoDescription() {\r\n       return \"Spam filtering\";\r\n     }\r\n\r\n     @Override\r\n     public String getDiscoCategoryType() {\r\n         return \"spam\";\r\n     }\r\n\r\n     @Override\r\n     public int hashCodeForPacket(Packet packet) {\r\n       if (packet.getElemTo() != null) {\r\n         return packet.getElemTo().hashCode();\r\n       }\r\n       // This should not happen, every packet must have a destination\r\n       // address, but maybe our SPAM checker is used for checking\r\n       // strange kind of packets too....\r\n       if (packet.getStanzaFrom() != null) {\r\n         return packet.getStanzaFrom().hashCode();\r\n       }\r\n       // If this really happens on your system you should look carefully\r\n       // at packets arriving to your component and decide a better way\r\n       // to calculate hashCode\r\n       return 1;\r\n     }\r\n\r\n     @Override\r\n     public boolean isDiscoNonAdmin() {\r\n       return false;\r\n     }\r\n\r\n     @Override\r\n     public int processingInThreads() {\r\n       return Runtime.getRuntime().availableProcessors();\r\n     }\r\n\r\n     @Override\r\n     public int processingOutThreads() {\r\n       return Runtime.getRuntime().availableProcessors();\r\n     }\r\n\r\n     @Override\r\n     protected void registerModules(Kernel kernel) {\r\n       // here we need to register modules responsible for processing packets\r\n     }\r\n\r\n     @Override\r\n     public void getStatistics(StatisticsList list) {\r\n       super.getStatistics(list);\r\n       list.add(getName(), \"Spam messages found\", testModule.getTotalSpamCounter(), Level.INFO);\r\n       list.add(getName(), \"All messages processed\", testModule.getMessagesCounter(), Level.FINE);\r\n       if (list.checkLevel(Level.FINEST)) {\r\n         // Some very expensive statistics generation code...\r\n       }\r\n     }\r\n\r\n   }\r\n\r\n**Example module code.**\r\n\r\n.. code:: java\r\n\r\n   @Bean(name = \"test-module\", parent = TestComponent.class, active = true)\r\n   public static class TestModule extends AbstractModule {\r\n\r\n     private static final Logger log = Logger.getLogger(TestModule.class.getCanonicalName());\r\n\r\n     private Criteria CRITERIA = ElementCriteria.name(\"message\");\r\n     private String[] FEATURES = { \"tigase:x:spam-filter\", \"tigase:x:spam-reporting\" };\r\n\r\n     @ConfigField(desc = \"Bad words\", alias = \"bad-words\")\r\n     private String[] badWords = {\"word1\", \"word2\", \"word3\"};\r\n     @ConfigField(desc = \"White listed addresses\", alias = \"white-list\")\r\n     private String[] whiteList = {\"admin@localhost\"};\r\n     @ConfigField(desc = \"Logged packet types\", alias = \"packet-types\")\r\n     private String[] packetTypes = {\"message\", \"presence\", \"iq\"};\r\n     @ConfigField(desc = \"Prefix\", alias = \"log-prepend\")\r\n     private String prependText = \"Spam detected: \";\r\n     @ConfigField(desc = \"Secure logging\", alias = \"secure-logging\")\r\n     private boolean secureLogging = false;\r\n     @ConfigField(desc = \"Abuse notification address\", alias = \"abuse-address\")\r\n     private JID abuseAddress = JID.jidInstanceNS(\"abuse@locahost\");\r\n     @ConfigField(desc = \"Frequency of notification\", alias = \"notification-frequency\")\r\n     private int notificationFrequency = 10;\r\n     private int delayCounter = 0;\r\n     private long spamCounter = 0;\r\n     private long totalSpamCounter = 0;\r\n     private long messagesCounter = 0;\r\n\r\n\r\n     @Inject\r\n     private TestComponent component;\r\n\r\n     public void everyMinute() {\r\n       if ((++delayCounter) >= notificationFrequency) {\r\n         write(Message.getMessage(abuseAddress, component.getComponentId(), StanzaType.chat,\r\n                                  \"Detected spam messages: \" + spamCounter, \"Spam counter\", null,\r\n                                  component.newPacketId(\"spam-\")));\r\n         delayCounter = 0;\r\n         spamCounter = 0;\r\n       }\r\n     }\r\n\r\n     @Override\r\n     public String[] getFeatures() {\r\n       return FEATURES;\r\n     }\r\n\r\n     @Override\r\n     public Criteria getModuleCriteria() {\r\n       return CRITERIA;\r\n     }\r\n\r\n     public long getMessagesCounter() {\r\n       return messagesCounter;\r\n     }\r\n\r\n     public long getTotalSpamCounter() {\r\n       return totalSpamCounter;\r\n     }\r\n\r\n     public void setPacketTypes(String[] packetTypes) {\r\n       this.packetTypes = packetTypes;\r\n       Criteria crit = new Or();\r\n       for (String packetType : packetTypes) {\r\n         crit.add(ElementCriteria.name(packetType));\r\n       }\r\n       CRITERIA = crit;\r\n     }\r\n\r\n     @Override\r\n     public void process(Packet packet) throws ComponentException, TigaseStringprepException {\r\n       // Is this packet a message?\r\n       if (\"message\" == packet.getElemName()) {\r\n         component.updateServiceDiscoveryItem(component.getName(), \"messages\",\r\n                                              \"Messages processed: [\" + (++messagesCounter) + \"]\", true);\r\n         String from = packet.getStanzaFrom().toString();\r\n         // Is sender on the whitelist?\r\n         if (Arrays.binarySearch(whiteList, from) < 0) {\r\n           // The sender is not on whitelist so let's check the content\r\n           String body = packet.getElemCDataStaticStr(Message.MESSAGE_BODY_PATH);\r\n           if (body != null && !body.isEmpty()) {\r\n             body = body.toLowerCase();\r\n             for (String word : badWords) {\r\n               if (body.contains(word)) {\r\n                 log.finest(prependText + packet.toString(secureLogging));\r\n                 ++spamCounter;\r\n                 component.updateServiceDiscoveryItem(component.getName(), \"spam\", \"Spam caught: [\" +\r\n                                                      (++totalSpamCounter) + \"]\", true);\r\n                 return;\r\n               }\r\n             }\r\n           }\r\n         }\r\n       }\r\n       // Not a SPAM, return it for further processing\r\n       Packet result = packet.swapFromTo();\r\n       write(result);\r\n     }\r\n   }\r\n\r\n.. |spam statitics small| image:: /images/devguide/spam-statitics-small.png\r\n\r\n.. _cil6:\r\n\r\nComponent Implementation - Lesson 6 - Scripting Support\r\n---------------------------------------------------------\r\n\r\nScripting support is a basic API built-in to Tigase server and automatically available to any component at no extra resource cost. This framework, however, can only access existing component variables which are inherited by your code from parent classes. It can not access any data or any structures you added in your component. A little effort is needed to expose some of your data to the scripting API.\r\n\r\nThis guide shows how to extend existing scripting API with your component specific data structures.\r\n\r\nIntegrating your component implementation with the scripting API is as simple as the code below:\r\n\r\n.. code:: java\r\n\r\n   private static final String BAD_WORDS_VAR = \"badWords\";\r\n   private static final String WHITE_LIST_VAR = \"whiteList\";\r\n\r\n   @Override\r\n   public void initBindings(Bindings binds) {\r\n     super.initBindings(binds);\r\n     binds.put(BAD_WORDS_VAR, testModule.badWords);\r\n     binds.put(WHITE_LIST_VAR, testModule.whiteList);\r\n   }\r\n\r\nThis way you expose two the component variables: ``badWords`` and ``whiteList`` to scripts under names the same names - two defined constants. You could use different names of course but it is always a good idea to keep things straightforward, hence we use the same variable names in the component and in the script.\r\n\r\nAlmost done, almost…​ In our old implementation these two variables are Java arrays of ``String``. Therefore we can only change their elements but we can not add or remove elements from these structures inside the script. This is not very practical and it puts some serious limits on the script’s code. To overcome this problem I have changed the test component code to keep bad words and whitelist in ``java.util.Set`` collection. This gives us enough flexibility to manipulate data.\r\n\r\nAs our component is now ready to cooperate with the scripting API, I will demonstrate now how to add remove or change elements of these collections using a script and ad-hoc commands.\r\n\r\n|test comp newscript|\r\n\r\nFirst, browse the server service discovery and double click on the test component. If you use `Psi <http://psi-im.org/>`__ client this should bring to you a new window with ad-hoc commands list. Other clients may present available ad-hoc commands differently.\r\n\r\nThe screenshot below shows how this may look. You have to provide some description for the script and an ID string. We use Groovy in this guide but you can as well use any different scripting language.\r\n\r\n|badwords list script|\r\n\r\nPlease refer to the Tigase scripting documentation for all the details how to add support for more languages. From the Tigase API point of view it all looks the same. You have to select a proper language from the pull-down list on windows shown on the right. If your preferred language is not on the list, it means it is not installed properly and Tigase is unable to detect it.\r\n\r\nThe script to pull a list of current bad words can be as simple as the following Groovy code:\r\n\r\n.. code:: groovy\r\n\r\n   def badw = (java.util.Set)badWords\r\n   def result = \"\"\r\n   for (s in badw) { result += s + \"\\n\" }\r\n   return result\r\n\r\nAs you see from the code, you have to reference your component variables to a variables in your script to make sure a correct type is used. The rest is very simple and is a pure scripting language stuff.\r\n\r\nLoad the script on to the server and execute it. You should receive a new window with a list of all bad words currently used by the spam filter.\r\n\r\nBelow is another simple script which allows updating (adding/removing) bad words from the list.\r\n\r\n.. code:: groovy\r\n\r\n   import tigase.server.Command\r\n   import tigase.server.Packet\r\n\r\n   def WORDS_LIST_KEY = \"words-list\"\r\n   def OPERATION_KEY = \"operation\"\r\n   def REMOVE = \"Remove\"\r\n   def ADD = \"Add\"\r\n   def OPERATIONS = [ADD, REMOVE]\r\n\r\n   def badw = (java.util.Set)badWords\r\n   def Packet p = (Packet)packet\r\n   def words = Command.getFieldValue(p, WORDS_LIST_KEY)\r\n   def operation = Command.getFieldValue(p, OPERATION_KEY)\r\n\r\n   if (words == null) {\r\n     // No data to process, let's ask user to provide\r\n     // a list of words\r\n     def res = (Packet)p.commandResult(Command.DataType.form)\r\n     Command.addFieldValue(res, WORDS_LIST_KEY, \"\", \"Bad words list\")\r\n     Command.addFieldValue(res, OPERATION_KEY, ADD, \"Operation\",\r\n       (String[])OPERATIONS, (String[])OPERATIONS)\r\n     return res\r\n   }\r\n\r\n   def words_list = words.tokenize(\",\")\r\n\r\n   if (operation == ADD) {\r\n     words_list.each { badw.add(it.trim()) }\r\n     return \"Words have been added.\"\r\n   }\r\n\r\n   if (operation == REMOVE) {\r\n     words_list.each { badw.remove(it.trim()) }\r\n     return \"Words have been removed.\"\r\n   }\r\n\r\n   return \"Unknown operation: \" + operation\r\n\r\nThese two scripts are just the beginning. The possibilities are endless and with the simple a few lines of code in your test component you can then extend your application at runtime with scripts doing various things; you can reload scripts, add and remove them, extending and modifying functionality as you need. No need to restart the server, no need to recompile the code and you can use whatever scripting language you like.\r\n\r\nOf course, scripts for whitelist modifications would look exactly the same and it doesn’t make sense to attach them here.\r\n\r\nHere is a complete code of the test component with the new method described at the beginning and data structures changed from array of **String*s to Java \\*Set**:\r\n\r\n**Example component code.**\r\n\r\n.. code:: java\r\n\r\n   public class TestComponent extends AbstractKernelBasedComponent {\r\n\r\n     private static final Logger log = Logger.getLogger(TestComponent.class.getName());\r\n\r\n     private static final String BAD_WORDS_KEY = \"bad-words\";\r\n     private static final String WHITELIST_KEY = \"white-list\";\r\n\r\n     @Inject\r\n     private TestModule testModule;\r\n\r\n     @Override\r\n     public synchronized void everyMinute() {\r\n       super.everyMinute();\r\n       testModule.everyMinute();\r\n     }\r\n\r\n     @Override\r\n     public String getComponentVersion() {\r\n     String version = this.getClass().getPackage().getImplementationVersion();\r\n       return version == null ? \"0.0.0\" : version;\r\n     }\r\n\r\n     @Override\r\n     public String getDiscoDescription() {\r\n       return \"Spam filtering\";\r\n     }\r\n\r\n     @Override\r\n     public String getDiscoCategoryType() {\r\n         return \"spam\";\r\n     }\r\n\r\n     @Override\r\n     public int hashCodeForPacket(Packet packet) {\r\n       if (packet.getElemTo() != null) {\r\n         return packet.getElemTo().hashCode();\r\n       }\r\n       // This should not happen, every packet must have a destination\r\n       // address, but maybe our SPAM checker is used for checking\r\n       // strange kind of packets too....\r\n       if (packet.getStanzaFrom() != null) {\r\n         return packet.getStanzaFrom().hashCode();\r\n       }\r\n       // If this really happens on your system you should look carefully\r\n       // at packets arriving to your component and decide a better way\r\n       // to calculate hashCode\r\n       return 1;\r\n     }\r\n\r\n     @Override\r\n     public boolean isDiscoNonAdmin() {\r\n       return false;\r\n     }\r\n\r\n     @Override\r\n     public int processingInThreads() {\r\n       return Runtime.getRuntime().availableProcessors();\r\n     }\r\n\r\n     @Override\r\n     public int processingOutThreads() {\r\n       return Runtime.getRuntime().availableProcessors();\r\n     }\r\n\r\n     @Override\r\n     protected void registerModules(Kernel kernel) {\r\n       // here we need to register modules responsible for processing packets\r\n     }\r\n\r\n     @Override\r\n     public void getStatistics(StatisticsList list) {\r\n       super.getStatistics(list);\r\n       list.add(getName(), \"Spam messages found\", testModule.getTotalSpamCounter(), Level.INFO);\r\n       list.add(getName(), \"All messages processed\", testModule.getMessagesCounter(), Level.FINE);\r\n       if (list.checkLevel(Level.FINEST)) {\r\n         // Some very expensive statistics generation code...\r\n       }\r\n     }\r\n\r\n       @Override\r\n       public void initBindings(Bindings binds) {\r\n           super.initBindings(binds);\r\n           binds.put(BAD_WORDS_KEY, testModule.badWords);\r\n           binds.put(WHITELIST_KEY, testModule.whiteList);\r\n       }\r\n\r\n   }\r\n\r\n**Example module code.**\r\n\r\n.. code:: java\r\n\r\n   @Bean(name = \"test-module\", parent = TestComponent.class, active = true)\r\n   public static class TestModule extends AbstractModule {\r\n\r\n     private static final Logger log = Logger.getLogger(TestModule.class.getCanonicalName());\r\n\r\n     private Criteria CRITERIA = ElementCriteria.name(\"message\");\r\n     private String[] FEATURES = { \"tigase:x:spam-filter\", \"tigase:x:spam-reporting\" };\r\n\r\n     @ConfigField(desc = \"Bad words\", alias = \"bad-words\")\r\n     protected CopyOnWriteArraySet<String> badWords = new CopyOnWriteArraySet<>(Arrays.asList(new String[] {\"word1\", \"word2\", \"word3\"}));\r\n     @ConfigField(desc = \"White listed addresses\", alias = \"white-list\")\r\n     protected CopyOnWriteArraySet<String> whiteList = new CopyOnWriteArraySet<>(Arrays.asList(new String[] {\"admin@localhost\"}));\r\n     @ConfigField(desc = \"Logged packet types\", alias = \"packet-types\")\r\n     private String[] packetTypes = {\"message\", \"presence\", \"iq\"};\r\n     @ConfigField(desc = \"Prefix\", alias = \"log-prepend\")\r\n     private String prependText = \"Spam detected: \";\r\n     @ConfigField(desc = \"Secure logging\", alias = \"secure-logging\")\r\n     private boolean secureLogging = false;\r\n     @ConfigField(desc = \"Abuse notification address\", alias = \"abuse-address\")\r\n     private JID abuseAddress = JID.jidInstanceNS(\"abuse@locahost\");\r\n     @ConfigField(desc = \"Frequency of notification\", alias = \"notification-frequency\")\r\n     private int notificationFrequency = 10;\r\n     private int delayCounter = 0;\r\n     private long spamCounter = 0;\r\n     private long totalSpamCounter = 0;\r\n     private long messagesCounter = 0;\r\n\r\n\r\n     @Inject\r\n     private TestComponent component;\r\n\r\n     public void everyMinute() {\r\n       if ((++delayCounter) >= notificationFrequency) {\r\n         write(Message.getMessage(abuseAddress, component.getComponentId(), StanzaType.chat,\r\n                                  \"Detected spam messages: \" + spamCounter, \"Spam counter\", null,\r\n                                  component.newPacketId(\"spam-\")));\r\n         delayCounter = 0;\r\n         spamCounter = 0;\r\n       }\r\n     }\r\n\r\n     @Override\r\n     public String[] getFeatures() {\r\n       return FEATURES;\r\n     }\r\n\r\n     @Override\r\n     public Criteria getModuleCriteria() {\r\n       return CRITERIA;\r\n     }\r\n\r\n     public int getMessagesCounter() {\r\n       return messagesCounter;\r\n     }\r\n\r\n     public int getTotalSpamCounter() {\r\n       return totalSpamCounter;\r\n     }\r\n\r\n     public void setPacketTypes(String[] packetTypes) {\r\n       this.packetTypes = packetTypes;\r\n       Criteria crit = new Or();\r\n       for (String packetType : packetTypes) {\r\n         crit.add(ElementCriteria.name(packetType));\r\n       }\r\n       CRITERIA = crit;\r\n     }\r\n\r\n     @Override\r\n     public void process(Packet packet) throws ComponentException, TigaseStringprepException {\r\n       // Is this packet a message?\r\n       if (\"message\" == packet.getElemName()) {\r\n         component.updateServiceDiscoveryItem(component.getName(), \"messages\",\r\n                                              \"Messages processed: [\" + (++messagesCounter) + \"]\", true);\r\n         String from = packet.getStanzaFrom().toString();\r\n         // Is sender on the whitelist?\r\n         if (!whiteList.contains(from)) {\r\n           // The sender is not on whitelist so let's check the content\r\n           String body = packet.getElemCDataStaticStr(Message.MESSAGE_BODY_PATH);\r\n           if (body != null && !body.isEmpty()) {\r\n             body = body.toLowerCase();\r\n             for (String word : badWords) {\r\n               if (body.contains(word)) {\r\n                 log.finest(prependText + packet.toString(secureLogging));\r\n                 ++spamCounter;\r\n                 component.updateServiceDiscoveryItem(component.getName(), \"spam\", \"Spam caught: [\" +\r\n                                                      (++totalSpamCounter) + \"]\", true);\r\n                 return;\r\n               }\r\n             }\r\n           }\r\n         }\r\n       }\r\n       // Not a SPAM, return it for further processing\r\n       Packet result = packet.swapFromTo();\r\n       write(result);\r\n     }\r\n   }\r\n\r\n.. |test comp newscript| image:: /images/devguide/test-comp-newscript.png\r\n.. |badwords list script| image:: /images/devguide/badwords-list-script.png\r\n\r\n.. _cil7:\r\n\r\nComponent Implementation - Lesson 7 - Data Repository\r\n---------------------------------------------------------------\r\n\r\nConfigRepository\r\n^^^^^^^^^^^^^^^^^\r\n\r\nThere are cases when you want to store some data permanently by your component. You can of course use the component configuration to provide some database connection settings, implement your own database connector and store records you need. There is, however, a very simple and useful framework which allows you to read and store some data transparently in either a database or a disk file. The framework also supports ad-hoc command interface straight away so you can manipulate your component data using an XMPP client.\r\n\r\nIn order to use it one needs to extend ``tigase.db.comp.ConfigRepository`` abstract class.\r\n\r\nAccessing UserRepository or AuthRepository\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTo use **AuthRepository** or **UserRepository** you need only to declare fields properly and annotated them with **@Inject**. This fields must be part of a class managed by Tigase Kernel - class of a component or any class annotated with **@Bean** annotation. For that classes proper instances of repositories will be injected by dependency injection.\r\n\r\n**Example usage of AuthRepository and UserRepository.**\r\n\r\n.. code:: java\r\n\r\n   @Inject\r\n   private AuthRepository authRepository;\r\n   @Inject\r\n   private UserRepository userRepository;\r\n\r\n.. _accessing-other-repositories-with-data-repository:\r\n\r\nAccessing other repositories with ``DataRepository``\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIn order to have more freedom while accessing repositories it’s possible to create and use custom repository implementation which implements **DataSourceAware** interface.\r\n\r\nFor our example let’s assume it will be class implementing **TestRepositoryIfc** and our implementation will be using JDBC. To make it work, we need to define ``TestRepositoryIfc`` as a generic interface extending ``DataSourceAware`` interface. ``DataSourceAware`` interface will provide definition for methods required by Tigase XMPP Server internals to initialize custom repository classes based on ``TestRepositoryIfc``.\r\n\r\n**TestRepositoryIfc.**\r\n\r\n.. code:: java\r\n\r\n   public interface TestRepositoryIfc<DS extends DataSource> extends DataSourceAware<DS> {\r\n     // Example method\r\n     void addItem(BareJID userJid, String item) throws RepositoryException;\r\n   }\r\n\r\nNext we need to prepare our actual implementation of repository - class responsible for execution of SQL statements. In this class we need to implement all of methods from our interface and method **void setDataSource(DataSource dataSource)** which comes from **DataSourceAware** interface. In this method we need to initialize data source, ie. create prepared statements. We should annotate our new class with ``@Repository.Meta`` annotation which will allow Tigase XMPP Server to find this class whenever class implementing ``TestRepositoryIfc`` and with support for data source with jdbc URI.\r\n\r\n.. code:: java\r\n\r\n   @Repository.Meta(supportedUris = \"jdbc:.*\")\r\n   public static class JDBCTestRepository implements TestRepositoryIfc<DataRepository> {\r\n\r\n     private static final String SOME_STATEMENT = \"select * from tig_users\";\r\n\r\n     private DataRepository repository;\r\n\r\n     @Override\r\n     public void setDataSource(DataRepository repository) {\r\n       // here we need to initialize required prepared statements\r\n       try {\r\n         repository.initPreparedStatement(SOME_STATEMENT, SOME_STATEMENT);\r\n       } catch (SQLException ex) {\r\n         throw new RuntimeException(\"Could not initialize repository\", ex);\r\n       }\r\n       this.repository = repository;\r\n     }\r\n\r\n     @Override\r\n     public void addItem(BareJID userJid, String item) throws RepositoryException {\r\n       try {\r\n         PreparedStatement stmt = repository.getPreparedStatement(userJid, SOME_STATEMENT);\r\n         synchronized (stmt) {\r\n           // do what needs to be done\r\n         }\r\n       } catch (SQLException ex) {\r\n         throw new RepositoryException(ex);\r\n       }\r\n     }\r\n   }\r\n\r\nAs you can see we defined type of a data source generic parameter for interface ``TestRepositoryIfc``. With that we make sure that only instance implementing ``DataRepository`` interface will be provided and thanks to that we do not need to cast provided instance of ``DataSource`` to this interface before any access to data source.\r\n\r\nWith that in place we need to create class which will take care of adding support for multi-database setup. In our case it will be **TestRepositoryMDBean**, which will take care of discovery of repository class, initialization and re-injection of data source. It is required to do so, as it was just mentioned our ``TestRepositoryMDBean`` will be responsible for initialization of ``JDBCTestRepository`` (actually this will be done by ``MDRepositoryBean`` which is extended by ``TestRepositoryMDBean``.\r\n\r\n.. code:: java\r\n\r\n   @Bean(name = \"repository\", parent = TestComponent.class, active = true)\r\n   public static class TestRepositoryMDBean extends MDRepositoryBeanWithStatistics<TestRepositoryIfc>\r\n       implements TestRepositoryIfc {\r\n\r\n     public TestRepositoryMDBean() {\r\n       super(TestRepositoryIfc.class);\r\n     }\r\n\r\n     @Override\r\n     public Class<?> getDefaultBeanClass() {\r\n       return TestRepositoryConfigBean.class;\r\n     }\r\n\r\n     @Override\r\n     public void setDataSource(DataSource dataSource) {\r\n       // nothing to do here\r\n     }\r\n\r\n     @Override\r\n     public void addItem(BareJID userJid, String item) throws RepositoryException {\r\n       getRepository(userJid.getDomain()).addItem(userJid, item);\r\n     }\r\n\r\n     @Override\r\n     protected Class<? extends TestRepositoryIfc> findClassForDataSource(DataSource dataSource)\r\n                   throws DBInitException {\r\n       return DataSourceHelper.getDefaultClass(TestRepositoryIfc.class, dataSource.getResourceUri());\r\n     }\r\n\r\n     public static class TestRepositoryConfigBean extends MDRepositoryConfigBean<TestRepositoryIfc> {\r\n     }\r\n   }\r\n\r\nMost of this code will be the same in all implementations based on ``MDRepositoryBeanWithStatistics``. In our case only custom method is **void addItem(…​)** which uses **getRepository(String domain)** method to retrieve correct repository for a domain. This retrieval of actual repository instance for a domain will need to be done for every custom method of ``TestRepositoryIfc``.\r\n\r\n.. Tip::\r\n\r\n   It is also possible to extend ``MDRepositoryBean`` or ``SDRepositoryBean`` instead of ``MDRepositoryBeanWithStatistics``. However, if you decide to extend abstract repository bean classes without ``withStatistics`` suffix, then no statistics data related to usage of this repository will be gathered. The only change, will be that you will not need to pass interface class to constructor of a superclass as it is not needed.\r\n\r\n.. Note::\r\n\r\n   As mentioned above, it is also possible to extend ``SDRepostioryBean`` and ``SDRepositoryBeanWithStatistics``. Methods which you would need to implement are the same is in case of extending ``MDRepositoryBeanWithStatistics``, however internally ``SDRepositoryBean`` will not have support for using different repository for different domain. In fact ``SDRepositoryBeanWithStatistics`` has only one repository instance and uses only one data source for all domains. The same behavior is presented by ``MDRepositoryBeanWithStatistics`` if only single ``default`` instance of repository is configured. However, ``MDRepositoryBeanWithStatistics`` gives better flexibility and due to that usage of ``SDRepositoryBean`` and ``SDRepositoryBeanWithStatistics`` is discouraged.\r\n\r\nWhile this is more difficult to implement than in previous version, it gives you support for multi database setup and provides you with statistics of database query times which may be used for diagnosis.\r\n\r\nAs you can also see, we’ve annotated **TestRepositoryMDBean** with **@Bean** annotation which will force Tigase Kernel to load it every time **TestComponent** will be loaded. This way it is possible to inject instance of this class as a dependency to any bean used by this component (ie. component, module, etc.) by just creating a field and annotating it:\r\n\r\n.. code:: java\r\n\r\n   @Inject\r\n   private TestRepositoryIfc testRepository;\r\n\r\n.. Tip::\r\n\r\n   In **testRepository** field instance of **TestRepositoryMDBean** will be injected.\r\n\r\n.. Note::\r\n\r\n   If the class in which we intend to use our repository is deeply nested within Kernel dependencies and we want to leverage automatic schema versioning we have to implement ``tigase.kernel.beans.RegistrarBean`` in our class!\r\n\r\nConfiguration\r\n^^^^^^^^^^^^^^^^^\r\n\r\nOur class ``TestRepositoryMDBean`` is annotated with ``@Bean`` which sets its name as ``repository`` and sets parent as ``TestComponent``. Instance of this component was configured by use under name of ``test`` in Tigase XMPP Server configuration file. As a result, all configuration related to our repositories should be placed in ``repository`` section placed inside ``test`` section.\r\n\r\n**Example.**\r\n\r\n.. code::\r\n\r\n   test(class: TestComponent) {\r\n       repository () {\r\n           // repository related configuration\r\n       }\r\n   }\r\n\r\nDefaults\r\n~~~~~~~~~~~~~~~\r\n\r\nAs mentioned above, if we use ``MDRepositoryBeanWithStatistics`` as our base class for ``TestRepositoryMDBean``, then we may have different data sources used for different domains. By default, if we will not configure it otherwise, ``MDRepositoryBeanWithStatistics`` will create only single repository instance named ``default``. It will be used for all domains and it will, by default, use data source named the same as repository instance - it will use data source named ``default``. This defaults are equal to following configuration entered in the config file:\r\n\r\n.. code::\r\n\r\n   test(class: TestComponent) {\r\n       repository () {\r\n           default () {\r\n               dataSourceName = 'default'\r\n           }\r\n       }\r\n   }\r\n\r\nChanging data source used by repository\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nIt is possible to make any repository use different data source than data source configured under the same name as repository instance. To do so, you need to set ``dataSourceName`` property of repository instance to the name of data source which it should use.\r\n\r\n**Example setting repository ``default`` to use data source named ``test``.**\r\n\r\n.. code::\r\n\r\n   test(class: TestComponent) {\r\n       repository () {\r\n           default () {\r\n               dataSourceName = 'test'\r\n           }\r\n       }\r\n   }\r\n\r\nConfiguring separate repository for domain\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nTo configure repository instance to be used for particular domain, you need to define repository with the same name as domain for which it should be used. It will, by default, use data source with name equal domain name.\r\n\r\n**Separate repository for ``example.com`` using data source named ``example.com``.**\r\n\r\n.. code::\r\n\r\n   dataSource () {\r\n       // configuration of data sources here is not complete\r\n       default () {\r\n           uri = \"jdbc:derby:/database\"\r\n       }\r\n       'example.com' () {\r\n           uri = \"jdbc:derby/example\"\r\n       }\r\n   }\r\n\r\n   test(class: TestComponent) {\r\n       repository () {\r\n           default () {\r\n           }\r\n           'example.com' () {\r\n           }\r\n       }\r\n   }\r\n\r\n**Separate repository for ``example.com`` using data source named ``test``.**\r\n\r\n.. code::\r\n\r\n   dataSource () {\r\n       // configuration of data sources here is not complete\r\n       default () {\r\n           uri = \"jdbc:derby:/database\"\r\n       }\r\n       'test' () {\r\n           uri = \"jdbc:derby/example\"\r\n       }\r\n   }\r\n\r\n   test(class: TestComponent) {\r\n       repository () {\r\n           default () {\r\n           }\r\n           'example.com' () {\r\n               dataSourceName = 'test'\r\n           }\r\n       }\r\n   }\r\n\r\n.. Note::\r\n\r\n   In both examples presented above, for domains other than ``example.com``, repository instance named ``default`` will be used and it will use data source named ``default``.\r\n\r\nRepository Versioning\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIt’s also possible to enable repository versioning capabilities when creating custom implementation. There are a couple of parts/steps to fully take advantage of this mechanism.\r\n\r\nEach ``DataSource`` has a table ``tig_schema_versions`` which contains information about component schema version installed in the database associated with particular DataSource.\r\n\r\nEnabling version checking in implementation\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nFirst of all, repository implementation should implement ``tigase.db.util.RepositoryVersionAware`` interface (all it’s methods are defined by default) and annotate it with ``tigase.db.Repository.SchemaId``. For example .Repository annoted with ``SchemaId`` and implementing ``RepositoryVersionAware``\r\n\r\n.. code:: java\r\n\r\n   @Repository.SchemaId(id = \"test-component\", name = \"Test Component\")\r\n   public static class TestRepositoryMDBean extends MDRepositoryBeanWithStatistics<TestRepositoryIfc>\r\n       implements TestRepositoryIfc {\r\n   }\r\n\r\nThis action alone will result in performing the check during Tigase XMPP Server startup and initialisation of repository whether tables, indexes, stored procedures and other elements are present in the configured data source in the required version. By default, required version matches the implementation version (obtained via call to ``java.lang.Package.getImplementationVersion()``), however it’s possible to specify required version manually, either:\r\n\r\n-  by utilizing ``tigase.db.util.RepositoryVersionAware.SchemaVersion`` annotation:\r\n\r\n::\r\n\r\n   @Repository.SchemaId(id = \"test_component\", name = \"Test Component\")\r\n   @RepositoryVersionAware.SchemaVersion(version = \"0.0.1\")\r\n   public static class TestRepositoryMDBean extends MDRepositoryBeanWithStatistics<TestRepositoryIfc>\r\n       implements TestRepositoryIfc {\r\n   …\r\n   }\r\n\r\n-  or by overriding ``tigase.db.util.RepositoryVersionAware.getVersion`` method:\r\n\r\n::\r\n\r\n       @Override\r\n       public Version getVersion() {\r\n           return \"0.0.1\";\r\n       }\r\n\r\n\r\nHandling wrong version and the upgrade\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nTo detect that version information in database is inadequate following logic will take place:\r\n\r\n-  if there is no version information in the database the service will be stopped completely prompting to install the schema (either via ``update-schema`` or ``install-schema`` depending on user preference);\r\n\r\n-  if there is an information about loaded component schema version in the repository and the base part of the required schema version (i.e. taking into account only *major.minor.bugfix* part) is different from the one present in the repository then:\r\n\r\n   -  if the required version of the component schema is *final* (i.e. non ``SNAPSHOT``) the server will shutdown and print in the log file (namely ``logs/tigase-console.log``) terminal error forcing the user to upgrade the schema;\r\n\r\n   -  if the required version of the component schema is *non-final* (i.e. having ``SNAPSHOT`` part) then there will be a warning printed in the log file (namely ``logs/tigase-console.log``) prompting user to run the upgrade procedure due to possible changes in the schema but the *server*\\ **will not**\\ *stop*;\r\n\r\nUpgrade of the loaded schema in the database will be performed by executing:\r\n\r\n.. code:: bash\r\n\r\n   ./scripts/tigase.sh upgrade-schema etc/tigase.conf\r\n\r\nThe above command will load current configuration, information about all configured data sources and enabled components, and then perform upgrade of the schema of each configured component in the appropriate data source.\r\n\r\nDepending on the type of the database (or specified annotation), how the upgrade procedure is handled internally is slightly different.\r\n\r\n.. _relationalDatabases:\r\n\r\nRelational databases (external handling)\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nFor all relational databases (MySQL, PostgreSQL, MS SQL Server, etc…) we highly recommend storing complete database schema in external files with following naming convention: ``<database_type>-<component_name>-<version>.sql``, for example complete schema for our Test component version 0.0.5 intended for MySQL would be stored in file named ``mysql-test-0.0.5.sql``. What’s more - schema files must be stored under ``database/`` subdirectory in Tigase XMPP Server installation directory.\r\n\r\n.. Note::\r\n\r\n   this can be controlled with ``external`` property of ``Repository.SchemaId`` annotation, which defaults to \"true\", if set to ``false`` then handling will be done as described in :ref:`Relational databases (external handling)<relationalDatabases>`\r\n\r\nFor example:\r\n\r\n-  ``database/mysql-test-0.0.1.sql``\r\n\r\n-  ``database/mysql-test-0.0.2.sql``\r\n\r\n-  ``database/mysql-test-0.0.3.sql``\r\n\r\n-  ``database/mysql-test-0.0.4.sql``\r\n\r\n-  ``database/mysql-test-0.0.5.sql``\r\n\r\nDuring the upgrade process all required schema files will be loaded in the ascending version order. Version range will depend on the conditions and will follow simple rules:\r\n\r\n-  Start of the range will start at the next version to the one currently loaded in the database (e.g. if the current version loaded to the database is ``0.0.3`` and we are deploying component version ``0.0.5`` then SchemaLoader will try to load schema from files: ``database/mysql-test-0.0.4.sql`` and ``database/mysql-test-0.0.5.sql``)\r\n\r\n-  If we are trying to deploy a *SNAPSTHOT* version of the component then schema file matching that version will always be included in the list of files to be loaded (e.g. if we are trying to deploy a nightly build with component version ``0.0.5-SNAPSHOT`` and currently loaded schema version in the database is ``0.0.5`` then SchemaLoader will include ``database/mysql-test-0.0.5.sql`` in the list of files to be loaded)\r\n\r\nIt’s also possible to skip above filtering logic and force loading all schema files for particular component/database from ``database/`` directory by appending ``--forceReloadAllSchemaFiles=true`` parameter to the ``upgrade-schema``/``install-schema`` command.\r\n\r\nNon-relational databases (internal handling)\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nIf there is a need to handle database schema internally (for example for cases like NoSQL databases or simply there is such preference) then it’s possible to do so by setting ``external`` attribute of ``Repository.SchemaId`` annotation to ``false``:\r\n\r\n.. code:: java\r\n\r\n   @Repository.SchemaId(id = \"test_component\", name = \"Test Component\", external = false)\r\n\r\nIn such case, ``updateSchema`` method from ``tigase.db.util.RepositoryVersionAware`` interface should be implemented to handle installation/updating of the schema. It takes two arguments:\r\n\r\n-  ``Optional<Version> oldVersion`` - indicating current version of the schema loaded to the database (if it’s present)\r\n\r\n-  ``Version newVersion`` - indicating required version (either version of component or specific version of the repository)\r\n\r\nSetting required repository version in database\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nEach versioned schema file should consist at the end code responsible for setting appropriate version of the loaded schema in the form of Stored Procedure call with the name of the component and the version as parameters:\r\n\r\n-  Postgresql\r\n\r\n::\r\n\r\n   -- QUERY START:\r\n   select TigSetComponentVersion('test_component', '0.0.5');\r\n   -- QUERY END:\r\n\r\n-  MsSQL Server\r\n\r\n::\r\n\r\n   -- QUERY START:\r\n   exec TigSetComponentVersion 'test_component', '0.0.5';\r\n   -- QUERY END:\r\n   GO\r\n\r\n-  MySQL\r\n\r\n::\r\n\r\n   -- QUERY START:\r\n   call TigSetComponentVersion('test_component', '0.0.5');\r\n   -- QUERY END:\r\n\r\n-  Derby\r\n\r\n::\r\n\r\n   -- QUERY START:\r\n   call TigSetComponentVersion('test_component', '0.0.5');\r\n   -- QUERY END:\r\n\r\nIn case of schema handled internally, after successful load (i.e. execution of the implemented ``tigase.db.util.RepositoryVersionAware.updateSchema`` method returning ``tigase.db.util.SchemaLoader.Result.ok``) the version in the database will be set to the current version of the component.\r\n\r\nThis allows (in case of schema handled externally) to load it by hand by directly importing ``.sql`` files into database.\r\n\r\n.. _cil8:\r\n\r\nComponent Implementation - Lesson 8 - Lifecycle of a component\r\n---------------------------------------------------------------\r\n\r\nInitialization of a component\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nA startup hook in the Tigase is different from the shutdown hook.\r\n\r\nThis is because you cannot really tell when exactly the startup time is. Is it when the application started, is it when configuration is loaded, is it when all objects are initialized. And this might be even different for each component. Therefore, in fact, there is no startup hook in Tigase in the same sense as the shutdown hook.\r\n\r\nThere are a few methods which are called at startup time of a component in the following order:\r\n\r\n1. **Constructor** - there is of course constructor which has no parameters. However it does not guarantee that this instance of the component will be used at all. The object could be created just to get default values of a config fields and may be destroyed afterwards.\r\n\r\n2. **Getters/Setters** - at second step of initialization of a component, Kernel configures component by reading and setting values of fields annotated with ``@ConfigField()`` annotation. If there is a public getter or setter for the same name as an annotated field - it will be used.\r\n\r\n3. **void beanConfigurationChanged(Collection<String> changedFields)** *(optional)* - if component implements ``ConfigurationChangedAware`` interface, this method will be called to notify component about fields which values were changed. It is useful if case in which component internals depends on configuration stored in more than one field, as it allows you to reconfigure component internals only once.\r\n\r\n4. **void register(Kernel kernel)** *(optional)* - if component implements ``RegistrarBean`` interface this method is called to allow registration of component private beans.\r\n\r\n5. **Dependency Injection** - during this time Kernel injects beans to component fields annotated with ``@Inject``. If public getters or setters for this fields exist - kernel will use them.\r\n\r\n6. **void initialized()** *(optional)* - called if component implements ``Initializable`` interface to notify it that configuration is set and dependencies are injected.\r\n\r\n7. **void start()** - during this call component starts it’s internal jobs or worker threads or whatever it needs for future activity. Component’s queues and threads are initialized at this point. **(after this method returns the component is ready)**\r\n\r\nTherefore, the ``start()`` hook is the best point if you want to be sure that component is fully loaded, initialized and functional.\r\n\r\n.. Tip::\r\n\r\n   Component instance may be started and stopped only once, however new instances of the same component with the same name may be created during Tigase XMPP Server uptime, ie. as a result of a server reconfiguration.\r\n\r\nReconfiguration\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nDuring lifecycle of a component instance it may happen that Tigase XMPP Server will be reconfigured. If change in configuration of this component will not be related to it’s activity, then Kernel will set values of changes fields annotated with ``@ConfigField()``. In this case public field setters may be used.\r\n\r\n.. Tip::\r\n\r\n   If component implements ``ConfigurationChangedAware`` interface, then method **void beanConfigurationChanged(Collection<String> changedFields)** will be called to notify component about fields which values were changed. It is useful if same component internal depends on configuration stored in more than one field, as it allows you to reconfigure this internal once.\r\n\r\nUpdate of injected dependencies\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nDuring lifecycle of a component instance it may happen that due to reconfiguration of a server other bean needs to be injected as a dependency to a component. In this case Tigase Kernel will inject dependencies to fields annotated with ``@Inject`` which value needs to be updated.\r\n\r\nStopping a component\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nComponent instance may be stopped at any point of Tigase XMPP Server runtime, ie. due to reconfiguration, or due to server graceful shutdown.\r\n\r\nIn both cases following methods of a component will be called:\r\n\r\n1. **void stop()** - first method stops component internal processing queues.\r\n\r\n2. **void beforeUnregister()** *(optional)* - if component implements @UnregisterAware@ interface this method is called to notify instance of a component that it is being unloaded.\r\n\r\n3. **void unregister(Kernel kernel)** *(optional)* - if component implements ``RegistrarBean`` called to give component a way to unregister beans (if needed).\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Development/CustomAuthRepository.rst",
    "content": "Custom ``AuthRepository`` implementation\n========================================\n\n``Credentials`` - solid base for authentication\n------------------------------------------------\n\nTigase supports having per-application passwords, allowing having distinct password for each application greatly improving security (see :ref:`application-passwords-admin-guide` for more details). To facilitate it uses ``Credentials``, specified by the namesake interface: ``tigase.auth.credentials.Credentials``, which implementations may store multiple credentials for single account (under ``credentialId``, ie. different credentials for different authentication mechanisms).\n\nIt's specifies a couple of essential API methods:\n\t* ``boolean canLogin();`` - checks if account can perform logging-in\n\t* ``AccountStatus getAccountStatus();`` - returns account status, i.e. if it's enabled or not\n\t* ``Entry getEntryForMechanism(String mechanism);`` - find a credential for specified encryption mechanism\n\t* ``Entry getFirst();`` - returns first available instance of credentials entry\n\t* ``BareJID getUser();`` - returns bare jid of an account\n\t* ``boolean isAccountDisabled();`` - checks if account is disabled\n\nthat allows handling of Credential entry for particular encryption/authentication mechanism. Essential part of the Credentials API are entries (defined in ``tigase.auth.credentials.Credentials.Entry`` that are actually responsible for performing provided password against defined mechanism.\n\n\nSimplest ``AuthRepository`` implementation\n------------------------------------------\n\nThe most basic way to create completely custom authentication is by implementing ``AuthRepository`` interface. While it has a handful or required methods the most important one is ``tigase.db.AuthRepository.getCredentials`` (it has default implementation and you could use ``tigase.db.AuthRepository.getPassword`` but it's deprecated and will be removed).\n\nCredentials allow having multiple, per-application passwords, but in the basic case - using ``PLAIN`` SALS method for authentication, you can use entry implementation just for plain passwords (``tigase.auth.credentials.entries.PlainCredentialsEntry`` class) and wrap it in ``tigase.db.AuthRepository.SingleCredential`` as you would only use single password. It also requires passing parameter indicating whether the account is ``active`` (allowed to login) or ``disabled`` (not allowed to log in).\n\nHere's the basic snippet that will authenticate all authentication attempts if `password` will be used as password:\n\n.. Note::\n\n    Particular password retrieval method is out of scope of this document; see :ref:`using-data-repository` and :ref:`accessing-other-repositories-with-data-repository` for more information on how to use Tigase API for accessing databases.\n\n.. code:: java\n\n    public class SimpleCustomAuthRepository implements AuthRepository {\n\n        @Override\n        public Credentials getCredentials(BareJID user, String credentialId) throws TigaseDBException {\n            final String passwordFromRepository = \"password\";\n            final PlainCredentialsEntry passwordEntry = new PlainCredentialsEntry(passwordFromRepository);\n            return new SingleCredential(user, getAccountStatus(user), passwordEntry);\n        }\n\n        @Override\n        public AccountStatus getAccountStatus(BareJID user) throws TigaseDBException {\n            return AccountStatus.active;\n        }\n    }\n\nThere are also 2 useful methods that are called when user is authenticated or logs out, that may optionally allow you to perform certain additional operations on repository/external services:\n\n.. code:: java\n\n    @Override\n    public void loggedIn(BareJID jid) throws TigaseDBException {\n\n    }\n\n    @Override\n    public void logout(BareJID user) throws TigaseDBException {\n\n    }\n\nComplete source example is included in the sources as ``tigase.examples.SampleCustomAuthRepository``\n"
  },
  {
    "path": "src/main/restructured/Tigase_Development/Data_Sources_And_Repositories.rst",
    "content": ".. _data-sources-and-repositories:\r\n\r\nData Source and Repositories\r\n================================\r\n\r\nIn Tigase XMPP Server 8.0.0 a new concept of data sources was introduced. It was introduced to create distinction between classes responsible for maintaining connection to actual data source and classes operating on this data source.\r\n\r\nData sources\r\n--------------\r\n\r\n|Relations between DataSourceBean and DataSources|\r\n\r\nDataSource\r\n^^^^^^^^^^^^^^\r\n\r\n``DataSource`` is an interface which should be implemented by all classes implementing access to data source, i.e. implementing access to database using JDBC connection or to MongoDB. Implementation of ``DataSource`` is automatically selected using uri provided in configuration and ``@Repository.Meta`` annotation on classes implementing ``DataSource`` interface.\r\n\r\nDataSourcePool\r\n^^^^^^^^^^^^^^^\r\n\r\n``DataSourcePool`` is interface which should be implemented by classes acting as a pool of data sources for single domain. There is no requirement to create class implementing this interface, however if implementation of ``DataSource`` is blocking and does not support concurrent requests, then creation of ``DataSourcePool`` is recommended. An example of such case is implementation of ``DataRepositoryImpl`` which executes all requests using single connection and for this class there is ``DataRepositoryPool`` implementing ``DataSourcePool`` interface and improving performance. Implementation of ``DataSourcePool`` is automatically selected using uri provided in configuration and ``@Repository.Meta`` annotation on classes implementing ``DataSourcePool`` interface.\r\n\r\nDataSourceBean\r\n^^^^^^^^^^^^^^^\r\n\r\nThis class is a helper class and provides support for handling multiple data sources. You can think of a ``DataSourceBean`` as a map of named ``DataSource`` or ``DataSourcePool`` instances. This class is also responsible for initialization of data source. Moreover, if data source will change during runtime ``DataSourceBean`` is responsible for firing a ``DataSourceChangedEvent`` to notify other classes about this change.\r\n\r\nUser and authentication repositories\r\n------------------------------------------\r\n\r\nThis repositories may be using existing (configured and initialized) data sources. However, it is also possible to that they may have their own connections. Usage of data sources is recommended if possible.\r\n\r\n|Relations between AuthRepositories and DataSources|\r\n\r\n|Relations between UserRepositories and DataSources|\r\n\r\nAuthRepository and UserRepository\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThis are a base interfaces which needs to be implemented by authentication repository (``AuthRepository``) and by repository of users (``UserRepository``). Classes implementing this interfaces should be only responsible for retrieving data from data sources.\r\n\r\nAuthRepositoryPool and UserRepositoryPool\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIf class implementing ``AuthRepositoryPool`` or ``UserRepositoryPool`` is not using data sources or contains blocking or is not good with concurrent access, then it should be wrapped within proper repository pool. Most of implementations provided as part of Tigase XMPP Server do not require to be wrapped within repository pool. If your implementation is blocking or not perform well with concurrent access (ie. due to synchronization), then it should be wrapped within this pool. To wrap implementation within a pool, you need to set ``pool-cls`` property of configured user or authentication repository in your configuration file.\r\n\r\nAuthRepositoryMDPoolBean and UserRepositoryMDPoolBean\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThis classes are for classes implementing ``AuthRepository`` and ``UserRepository`` what ``DataSourceBean`` is for classes implementing ``DataSource`` interface. This classes holds map of named authentication or user repositories. They are also responsible for initialization of classes implementing this repositories.\r\n\r\n\r\nOther repositories\r\n-----------------------\r\n\r\nIt is possible to implement repositories not implementing ``AuthRepository`` or ``UserRepository``. Each type of custom repository should have its own API and its own interface.\r\n\r\n|Relations between custom repositories and DataSources|\r\n\r\nDataSourceAware\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nCustom repositories should implement they own interface specifying its API. This interfaces should extend ``DataSourceAware`` interface which is base interface required to be implemented by custom repositories. ``DataSourceAware`` has a method ``setDataSource()`` which will be called with instance of data source to initialize instance of custom repository. Implementations should be annotated with ``@Repository.Meta`` implementation to make the automatically selected for proper type of ``DataSource`` implementation.\r\n\r\nSee :ref:`using-data-repository` and :ref:`accessing-other-repositories-with-data-repository` for more details.\r\n\r\nMDRepositoryBean\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIt is required to create a class extending ``MDRepositoryBean`` implementing same custom interface as the custom repository. This class will be a multi domain pool, allowing you to have separate implementation of custom repository for each domain. Moreover, it will be responsible for creation and initialization of your custom repository instances.\r\n\r\n.. |Relations between DataSourceBean and DataSources| image:: /images/devguide/datasourcebean-datasources.png\r\n.. |Relations between AuthRepositories and DataSources| image:: /images/devguide/datasource-authrepository.png\r\n.. |Relations between UserRepositories and DataSources| image:: /images/devguide/datasource-userrepository.png\r\n.. |Relations between custom repositories and DataSources| image:: /images/devguide/datasource-customrepository.png\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Development/EventBus_API.rst",
    "content": "\r\nEventBus API in Tigase\r\n============================\r\n\r\nEventBus is a custom publish-subscribe mechanism which allows for the use of Event Listener within Tigase Server. For a more detailed overview of EventBus and it’s features, please visit `The Administration Guide <http://docs.tigase.org/tigase-server/snapshot/Administration_Guide/html/#eventBus>`__.\r\n\r\nEventBus API\r\n-----------------\r\n\r\nTo create instance of EventBus use the following code:\r\n\r\n.. code:: java\r\n\r\n   EventBus eventBus = EventBusFactory.getInstance();\r\n\r\n.. Note::\r\n\r\n   Remember, that EventBus is asynchronous. All handlers are called in a different thread than the thread that initially fired the event.\r\n\r\nEvents\r\n^^^^^^^^^^^\r\n\r\nEvents may be defined in two ways: as a class |ss| or as an XML element(XML/Element based events are deprecated since version 8.2 and will be removed in version 9.0)\\. |se|\\\r\n\r\n**Serialized event class.**\r\n\r\n.. code:: java\r\n\r\n   public class SampleSerializedEvent implements Serializable {\r\n       private JID data;\r\n       public JID getData() {\r\n           return this.data;\r\n       }\r\n       public void setData(JID data) {\r\n           this.data = data;\r\n       }\r\n   }\r\n\r\n**Event class.**\r\n\r\n.. code:: java\r\n\r\n   public class SampleEvent {\r\n       private JID data;\r\n       public JID getData() {\r\n           return this.data;\r\n       }\r\n       public void setData(JID data) {\r\n           this.data = data;\r\n       }\r\n   }\r\n\r\n|ss| **XML Element event(deprecated)**\\ |se|\\\r\n\r\n.. code:: xml\r\n\r\n   <EventName xmlns=\"tigase:demo\">\r\n     <sample_value>1</sample_value>\r\n   </EventName>\r\n\r\n.. Note::\r\n\r\n   Events defined as XML element and class implementing ``Serializable`` interface will be distributed to all servers in cluster. Event ``SampleEvent`` will be broadcast only in the same instance what fired the event.\r\n\r\nRequirements for class-based events\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n-  Default, explicit, public, paremeter-less constructor is mandatory.\r\n\r\n-  If the event should be delivered to all cluster nodes then it **MUST** implement ``Serializable`` interface.\r\n\r\n-  Variables serialisation follows ``Serializable`` semantics, which means that ``final``, ``static`` nor ``transient`` fields will be skipped. What’s more, fields with ``null`` value will not be serialised neither.\r\n\r\nSerialisation of class-based events\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nClass based events are serialized (if it is required and possible) to XML element. Name of XML element is taken from full name of class:\r\n\r\n**Class based event serialized to XML.**\r\n\r\n.. code:: xml\r\n\r\n   <net.tigase.sample.SampleSerializedEvent>\r\n       <data>sample@data.tigase.net</data>\r\n   </net.tigase.sample.SampleSerializedEvent>\r\n\r\nFiring events\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTo fire event, just get instance of EventBus and call method ``fire()``.\r\n\r\n**Firing serialized event.**\r\n\r\n.. code:: java\r\n\r\n   EventBus eventBus = EventBusFactory.getInstance();\r\n   SampleSerializedEvent event = new SampleSerializedEvent();\r\n   eventBus.fire(event)\r\n\r\n**Firing simple event.**\r\n\r\n.. code:: java\r\n\r\n   EventBus eventBus = EventBusFactory.getInstance();\r\n   SampleEvent event = new SampleEvent();\r\n   eventBus.fire(event)\r\n\r\n|ss| **Firing event based on XML Element(deprecated)** |se|\\\r\n\r\n.. code:: java\r\n\r\n   EventBus eventBus = EventBusFactory.getInstance();\r\n   Element event = new Element(\"tigase.eventbus.impl.Event1\");\r\n   eventBus.fire(event)\r\n\r\nHandling events\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTo handle fired event, we have to register listener in EventBus. When listener is registered, EventBus automatically subscribes for this type of event in all instances in cluster.\r\n\r\nDepends on expected event type, we have to decide what type of listener we should register.\r\n\r\nHandling class based events\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nThis option is reserved for class based events only. It doesn’t matter if it is serialized class or not.\r\n\r\n.. code:: java\r\n\r\n   eventBus.addListener(SampleEvent.class, new EventListener<SampleEvent>() {\r\n\r\n       @Override\r\n       public void onEvent(SampleEvent event) {\r\n       }\r\n   });\r\n\r\nTo make registering listeners more easy, you can use method ``registerAll()`` from EventBus. This method registers all methods given class, annotated by ``@HandleEvent`` as listeners for event declared as the method argument.\r\n\r\n.. code:: java\r\n\r\n   public class SomeConsumer {\r\n\r\n       @HandleEvent\r\n       public void event1(Event12 e) {\r\n       }\r\n\r\n       public void initialize() {\r\n           eventBus.registerAll(this);\r\n       }\r\n   }\r\n\r\n\r\n|ss| Handling XML events |se|\\\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nTo handle XML events we have to register listener for specific event package and name. In our example, package is empty because event name has no package declared (see also :ref:`Filtering events<filteringEvents>`).\r\n\r\n.. code:: java\r\n\r\n   eventBus.addListener(\"\", \"EventName\", new EventListener<Element>() {\r\n       @Override\r\n       public void onEvent(Element event) {\r\n\r\n       }\r\n   });\r\n   eventBus.addListener(\"tigase.eventbus.impl\", \"Event1\", new EventListener<Element>() {\r\n       @Override\r\n       public void onEvent(Element event) {\r\n\r\n       }\r\n   });\r\n\r\nBecause serialized class events, ale transformed to XML elements, we are able to listen for XML representation of class based event. To do that, we have to register listener for specific package and class name:\r\n\r\n.. code:: java\r\n\r\n   eventBus.addListener(\"net.tigase.sample\", \"SampleSerializedEvent\", new EventListener<Element>() {\r\n       @Override\r\n       public void onEvent(Element event) {\r\n\r\n       }\r\n   });\r\n\r\n..\r\n\r\n   **Important**\r\n\r\n   XML events created on others cluster node, will have attribute ``remote`` set to ``true`` and attribute ``source`` set to event creator node name:\r\n\r\n   .. code:: xml\r\n\r\n      <EventName xmlns=\"tigase:demo\" remote=\"true\" source=\"node1.example\">\r\n        <sample_value>1</sample_value>\r\n      </EventName>\r\n\r\n.. _filteringEvents:\r\n\r\nFiltering events\r\n~~~~~~~~~~~~~~~~\r\n\r\nSometimes you may want to receive many kinds of events with the same handler. EventBus has very simple mechanism to generalization:\r\n\r\n.. code:: java\r\n\r\n   eventBus.addListener(\"net.tigase.sample\", null,  event -> {}); \r\n   eventBus.addListener(null, null,  event -> {}); \r\n\r\n-  This listener will be called for each event with given package name (XML based, or serialized class based).\r\n\r\n-  This listener will be called for ALL events (XML based, or serialized class based).\r\n\r\nIn case of class based events, EventBus is checking class inheritance.\r\n\r\n.. code:: java\r\n\r\n   class MainEvent { }\r\n   class SpecificEvent extends MainEvent {}\r\n\r\n   eventBus.addListener(SpecificEvent.class, event -> {}); \r\n   eventBus.addListener(MainEvent.class, event -> {}); \r\n\r\n   eventBus.fire(new SpecificEvent());\r\n\r\n-  Will be called, because this is listener stricte for ``SpecificEvent``.\r\n\r\n-  Will be called, because ``SpecificEvent`` extends ``MainEvent``.\r\n\r\n\r\n.. |ss| raw:: html\r\n\r\n    <strike>\r\n.. |se| raw:: html\r\n\r\n    </strike>"
  },
  {
    "path": "src/main/restructured/Tigase_Development/Experimental.rst",
    "content": "Experimental\r\n=============\r\n\r\nThe guide contains description of non-standard or experimental functionality of the server. Some of them are based on never published extensions, some of them are just test implementation for new ideas or performance improvements.\r\n\r\n-  :ref:`Dynamic Rosters<dynamicRosters>`\r\n\r\n-  :ref:`Mobile Optimizations<mobileoptimizations>`\r\n\r\n-  :ref:`Bosh Session Cache<boshsessioncache>`\r\n\r\n\r\n.. _dynamicRosters:\r\n\r\nDynamic Rosters\r\n----------------\r\n\r\nProblem Description\r\n^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nNormal roster contacts stored and created as **dynamic roster parts** are delivered to the end user transparently. The XMPP client doesn’t really know what contacts come from its own **static** roster created manually by the user and what contacts come from a **dynamic** roster part; contacts and groups generated dynamically by the server logic.\r\n\r\nSome specialized clients need to store extra bits of information about roster contacts. For the normal user **static** roster information can be stored as private data and is available only to this single user. In some cases however, clients need to store information about contacts from the dynamic roster part and this information must be available to all users accessing **dynamic** roster part.\r\n\r\nThe protocol defined here allows the exchange of information, saving and retrieving extra data about the contacts.\r\n\r\nSyntax and Semantics\r\n^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nExtra contact data is accessed using IQ stanzas, specifically by means of a child element qualified by the **jabber:iq:roster-dynamic** namespace. The child element MAY contain one or more children, each describing a unique contact item. Content of the element is not specified and is implementation dependent. From Tigase’s point of view it can contain any valid XML data. Whole element is passed to the DynamicRoster implementation class as is and without any verification. Upon retrieving the contact extra data the DynamicRoster implementation is supposed to provide a valid XML element with all the required data for requested **jid**.\r\n\r\nThe **jid** attribute specifies the Jabber Identifier (JID) that uniquely identifies the roster item. Inclusion of the **jid** attribute is **REQUIRED**.\r\n\r\nFollowing actions on the extra contact data are allowed:\r\n\r\n-  **set** - stores extra information about the contact\r\n\r\n-  **get** - retrieves extra information about the contact\r\n\r\nRetrieving Contact Data\r\n^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nUpon connecting to the server and becoming an active resource, a client can request the extra contact data. This request can be made either before or after requesting the user roster. The client’s request for the extra contact data is **OPTIONAL**.\r\n\r\nExample: Client requests contact extra data from the server using **get** request:\r\n\r\n.. code:: xml\r\n\r\n   <iq type='get' id='rce_1'>\r\n   <query xmlns='jabber:iq:roster-dynamic'>\r\n   <item jid='archimedes@eureka.com'/>\r\n   </query>\r\n   </iq>\r\n\r\nExample: Client receives contact extra data from the server, but there was either no extra information for the user, or the user was not found in the dynamic roster:\r\n\r\n.. code:: xml\r\n\r\n   <iq type='result' id='rce_1'>\r\n   <query xmlns='jabber:iq:roster-dynamic'>\r\n   <item jid='archimedes@eureka.com'/>\r\n   </query>\r\n   </iq>\r\n\r\nExample: Client receives contact extra data from the server, and there was some extra information found about the contact:\r\n\r\n.. code:: xml\r\n\r\n   <iq type='result' id='rce_1'>\r\n   <query xmlns='jabber:iq:roster-dynamic'>\r\n   <item jid='archimedes@eureka.com'>\r\n   <phone>+12 3234 322342</phone>\r\n   <note>This is short note about the contact</note>\r\n   <fax>+98 2343 3453453</fax>\r\n   </item>\r\n   </query>\r\n   </iq>\r\n\r\nUpdating/Saving Extra Information About the Contact\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nAt any time, a client **MAY** update extra contact information on the server.\r\n\r\nExample: Client sends contact extra information using **set** request.\r\n\r\n.. code:: xml\r\n\r\n   <iq type='set' id='a78b4q6ha463'>\r\n   <query xmlns='jabber:iq:roster-dynamic'>\r\n   <item jid='archimedes@eureka.com'>\r\n   <phone>+22 3344 556677</phone>\r\n   <note>he is a smart guy, he knows whether the crown is made from pure gold or not.</note>\r\n   </item>\r\n   </query>\r\n   </iq>\r\n\r\nClient responds to the server:\r\n\r\n.. code:: xml\r\n\r\n   <iq type='result' id='a78b4q6ha463'/>\r\n\r\nA client **MAY** update contact extra information for more than a single item in one request:\r\n\r\nExample: Client sends contact extra information using **set** request with many <item/> elements.\r\n\r\n.. code:: xml\r\n\r\n   <iq type='set' id='a78b4q6ha464'>\r\n   <query xmlns='jabber:iq:roster-dynamic'>\r\n   <item jid='archimedes@eureka.com'>\r\n   <phone>+22 3344 556677</phone>\r\n   <note>he is a smart guy, he knows whether the crown is made from pure gold or not.</note>\r\n   </item>\r\n   <item jid='newton@eureka.com'>\r\n   <phone>+22 3344 556688</phone>\r\n   <note>He knows how heavy I am.</note>\r\n   </item>\r\n   <item jid='pascal@eureka.com'>\r\n   <phone>+22 3344 556699</phone>\r\n   <note>This guy helped me cure my sickness!</note>\r\n   </item>\r\n   </query>\r\n   </iq>\r\n\r\nClient responds to the server:\r\n\r\n.. code:: xml\r\n\r\n   <iq type='result' id='a78b4q6ha464'/>\r\n\r\nConfiguration\r\n^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nDynamicRoster implementation class should be configured in the **config.tdsl** file:\r\n\r\n.. code::\r\n\r\n   'sess-man' () {\r\n       'dynamic-rosters' () {\r\n           class (class: package.custom.DynamicRosterImplementation) {}\r\n       }\r\n   }\r\n\r\nIf you want to pass configuration to your implementation simply use ``@ConfigField`` annotation on your variable (see :ref:`Component implementation - Lesson 2 - Configuration<cil2>` for more details).\r\n\r\n.. _mobileoptimizations:\r\n\r\nMobile Optimizations\r\n-----------------------------\r\n\r\nProblem Description\r\n^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIn default configuration stanzas are sent to the client when processing is finished, but in mobile environment sending or receiving data drains battery due to use of the radio.\r\n\r\nTo save energy data should be sent to client only if it is important or client is waiting for it.\r\n\r\nSolution\r\n^^^^^^^^^^\r\n\r\nWhen mobile client is entering inactive state it notifies server about it by sending following stanza:\r\n\r\n.. code:: xml\r\n\r\n   <iq type=\"set\" id=\"xx\">\r\n   <mobile\r\n     xmlns=\"http://tigase.org/protocol/mobile#v3\"\r\n     enable=\"true\"/>\r\n   </iq>\r\n\r\nAfter receiving stanza server starts queuing stanza which should be send to mobile client. What kind of queued stanzas depends on the plugins used and in case of **Mobile v3** presence stanzas are queued as well as message stanzas which are Message Carbons. Any other stanza (such as iq or plain message) is sent immediately to the client and every stanza from queue is also sent at this time.\r\n\r\nWhen mobile client is entering active state it notifies server by sending following stanza:\r\n\r\n.. code:: xml\r\n\r\n   <iq type=\"set\" id=\"xx\">\r\n   <mobile\r\n     xmlns=\"http://tigase.org/protocol/mobile#v3\"\r\n     enable=\"false\"/>\r\n   </iq>\r\n\r\nAfter receiving stanza server sends all queued stanzas to the client.\r\n\r\nAlso all stanzas from queue will be sent if number of stanzas in queue will reach queue size limit. By default this limit is set to 50.\r\n\r\nQueuing Algorithms\r\n^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThere are three mobile optimization plugins for Tigase:\r\n\r\n-  **Mobile v1** - all presence stanzas are kept in queue\r\n\r\n-  **Mobile v2** - only last presence from each source is kept in queue\r\n\r\n-  **Mobile v3** - only last presence from each source is kept in queue, also Message Carbons are queued\r\n\r\nIf you wish to activate you Mobile v1 plugin you need to send presented above with xmlns attribute value replaced with http://tigase.org/protocol/mobile#v1\r\n\r\nIf you wish to activate you Mobile v2 plugin you need to send presented above with xmlns attribute value replaced with http://tigase.org/protocol/mobile#v2\r\n\r\nConfiguration\r\n^^^^^^^^^^^^^^^^^\r\n\r\nMobile plugins are not activated by default thus additional entry in the ``config.tdsl`` is required:\r\n\r\n.. code::\r\n\r\n   'sess-man' () {\r\n       mobile_v1 () {}\r\n   }\r\n\r\nYou may substitute ``mobile_v1`` with ``mobile_v2`` or ``mobile_v3`` depending on which algorithm you wish to use.\r\n\r\n.. Note::\r\n\r\n   USE ONLY ONE PLUGIN AT A TIME!\r\n\r\n.. _boshsessioncache:\r\n\r\nBosh Session Cache\r\n--------------------------------\r\n\r\nProblem Description\r\n^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nWeb clients have no way to store any data locally, on the client side. Therefore after a web page reload the web clients loses all the context it was running in before the page reload.\r\n\r\nSome elements of the context can be retrieved from the server like the roster and all contacts presence information. Some other data however, can not be restored easily like opened chat windows and the chat windows contents. Even if the roster restoring is possible, this operation is very expensive in terms of time and resources on the server side.\r\n\r\nOn of possible solutions is to allow web client to store some data in the Bosh component cache on the server side for the time while the Bosh session is active. After the page reloads, if the client can somehow retrieve SID (stored in cookie or provided by the web application running the web client) it is possible to reload all the data stored in the Bosh cache to the client.\r\n\r\nBosh session context data are: roster, contacts presence information, opened chat windows, chat windows content and some other minor data. Ideally the web client should be able to store any data in the Bosh component cache it wants.\r\n\r\n\r\nBosh Session Cache Description\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThe Bosh Session Cache is divided into 2 parts - automatic cache and dynamic cache.\r\n\r\nThe reason for splitting the cache into 2 parts is that some data can be collected automatically by the Bosh component and it would be very inefficient to require the client to store the data in the Bosh cache. The best example for such data is the Roster and contacts presence information.\r\n\r\n-  **automatic cache** - is the cache part which is created automatically by the Bosh component without any interaction with the client. The client, however, can access the cache at any time. I would say this is a read-only cache but I don’t want to stop client from manipulating the cache if it needs. The client usually, only retrieves data from this part of the cache as all changes should be automatically updated by the Bosh component. The general idea for the automatic cache is that the data stored there are accessible in the standard XMPP form. So no extra code is needed for processing them.\r\n\r\n-  **dynamic cache** - is the cache part which is or can be modified at any time by the client. Client can store, retrieve, delete and modify data in this part of the cache.\r\n\r\nCache Protocol\r\n^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nAll the Bosh Session Cache actions are executed using additional ``<body/>`` element attributes: ``cache`` and ``cache-id``. Attribute cache stores the action performed on the Bosh ``cache`` and the ``cache-id`` attribute refers to the ``cache`` element if the action attribute needs it. ``cache-id`` is optional. There is a default cache ID (empty one) associated with the elements for which the ``cache-id`` is not provided.\r\n\r\nIf the ``<body/>`` element contains the cache attribute it means that all data included in the ``<body/>`` refer to the cache action. It is not allowed, for example to send a message in the body and have the cache action set to **get**. The ``<body/>`` element with cache action **get**, **get_all**, **on**, **off**, **remove** must be empty. The ``<body/>`` element with actions **set** or **add** must contain data to store in the cache.\r\n\r\nCache Actions\r\n~~~~~~~~~~~~~~~\r\n\r\n-  **on** or **off** - the client can switch the cache on or off at any time during the session. It is recommended, however that the client switches the cache **on** in the first body packet, otherwise some information from the automatic cache may be missing. The automatic cache is created from the stream of data passing the Bosh component. Therefore if the cache is switched on after the roster retrieval is completed then the roster information will be missing in the cache. If the cache is set to **off** (the default value) all requests to the cache are ignored. This is to ensure backward compatibility with the original Bosh specification and to make sure that in a default environment the Bosh component doesn’t consume any extra resources for cache processing and storing as the cache wouldn’t be used by the client anyway.\r\n\r\n-  **get** - retrieves the cache element pointing by the cache-id from the Bosh cache. Note there is no **result** cache action. The ``<body/>`` sent as a response from the server to the client may contain cache results for a given cache-id and it may also contain other data received by the Bosh component for the client. It may also happen that large cached data are split into a few parts and each part can be sent in a separate ``<body/>`` element. It may usually happen for the Roster data.\r\n\r\n-  **get_all** - retrieves all the elements kept in the Bosh cache. That action can can be performed after the page reload. The client doesn’t have to request every single cached item one by one. It can retrieve all cache items in one go. It doesn’t mean however the whole cache is sent to the client in a single ``<body/>`` element. The cache content will be divided into a smaller parts of a reasonable size and will be sent to the client in a separate ``<body/>`` elements. It may also happen that the **``<body/>``** element contain the cache elements as well as the new requests sent to the user like new messages or presence information.\r\n\r\n-  **set** - sends data to the Bosh Session cache for later retrieval. The client can store any data it wants in the cache. The Bosh components stores in the cache under the selected ID all the data inside the ``<body/>`` element. The only restriction is that the cached data must be a valid XML content. The data are returned to the client in exactly the same form as they were received from the server. The **set** action replaces any previously stored data under this ID.\r\n\r\n-  **add** - adds new element to the cache under the given ID. This action might be useful for storing data for the opened chat window. The client can add new elements for the chat window, like new messages, icons and so on…​\r\n\r\n-  **remove** - removes the cached element for the given cache ID.\r\n\r\nCache ID\r\n~~~~~~~~~~~~\r\n\r\nCache ID can be an any character string. There might be some IDs reserved for a special cases, like for the Roster content. To avoid any future ID conflicts reserved ID values starts with: **bosh** - string.\r\n\r\nThere is a default cache ID - en empty string. Thus cache-id attribute can be omitted and then the requests refers to data stored under the default (empty) ID.\r\n\r\nReserved Cache ID Names\r\n~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nHere is a list of reserved Cache IDs:\r\n\r\n-  **bosh-roster** - The user roster is cached in the Bosh component in exactly the same form as it was received from the core server. The Bosh Cache might or might not do optimizations on the roster like removing elements from the cached roster if the roster **remove** has been received or may just store all the roster requests and then send them all to the client. There is a one mandatory optimization the Bosh Cache must perform. It must remember the last (and only the last) presence status for each roster item. Upon roster retrieving from the cache the Bosh component must send the roster item first and then the presence for the item. If the presence is missing it means an offline presence. If the roster is small it can be sent to the client in a single packet but for a large roster it is recommended to split contact lists to batches of max 100 elements. The Bosh component may send all roster contacts first and then all presences or it can send a part of the roster, presences for sent items, next part of the roster, presences for next items and so on.\r\n\r\n-  **bosh-resource-bind** - The user resource bind is also cached to allow the client quickly retrieve information about the full JID for the established Bosh session.\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst",
    "content": "Hack Tigase XMPP Server in Eclipse\r\n====================================\r\n\r\nIf you want to write code for **Tigase** server we recommend using `Eclipse IDE <//https://eclipse.org/downloads/>`__. Either the IDE for Java or Java EE developers will work.\r\n\r\nRequirements\r\n---------------\r\n\r\nEclipse IDE currently requires the use of `Java Development Kit 8 <http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html>`__.\r\n\r\nYou will also need the M2E plugin for Maven integration, however this can be done inside Eclipse now, so refer to the :ref:`Plugin Installation<m2EPlugin>` section for that.\r\n\r\nInstallation\r\n--------------\r\n\r\nEclipse does not come as an installer, but rather an archive. Extract the directory to a working location wherever you would like. Now install the JDK software, location is not important as Eclipse will find it automatically.\r\n\r\nBefore we begin, we will need to clone the repository from git.\r\n\r\nLinux\r\n^^^^^^^\r\n\r\nFor linux operating systems, navigate to a directory where you want the repository to be cloned to and type the following into terminal.\r\n\r\n::\r\n\r\n   git clone https://repository.tigase.org/git/tigase-server.git\r\n\r\nWindows\r\n^^^^^^^^^^^^\r\n\r\nPlease see the Windows coding guide for instructions on how to obtain source code from git. If you don’t want to install git software specifically, you can use Eclipse’s git plugin to obtain the repository without any new software. First click on File, then Import…​ Next select from Git folder and the Projects from Git\r\n\r\n|win git1|\r\n\r\nClick next, and now select clone URI\r\n\r\n|win git2|\r\n\r\nNow click next, and in this window enter the following into the URI field\r\n\r\n::\r\n\r\n   git://repository.tigase.org/git/tigase-server.git\r\n\r\nThe rest of the fields will populate automatically\r\n\r\n|win git3|\r\n\r\nSelect the master branch, and any branches you wish to edit. **The master branch should be the only one you need, branches are used for specific code changes**\r\n\r\n|win git4|\r\n\r\nNow select the directory where you wanted to clone the repository to. This was function as the project root directory you will use later on in the setup.\r\n\r\n|win git5|\r\n\r\nOnce you click next Eclipse will download the repository and any branches you selected to that directory. Note you will be unable to import this git directory since there are no git a project specific files downloaded. However, once downloading is complete you may click cancel, and the git repository will remain in the directory you have chosen.\r\n\r\n.. _m2EPlugin:\r\n\r\nSetup\r\n---------\r\n\r\nOnce you have the main window open and have established a workspace (where most of your working files will be stored), click on Help and then Install New Software…​\r\n\r\n|Eclipse help|\r\n\r\nUnder the Work With field enter the following and press enter: http://download.eclipse.org/technology/m2e/releases/\r\n\r\n**Note: You may wish to click the Add…​ button and add the above location as a permanent software location to keep the location in memory**\r\n\r\n|Eclipse m2Einstall|\r\n\r\nYou should see the M2 Eclipse software packages show in the main window. Click the check-box and click Next. Once the installer is finished it will need to restart Eclipse.\r\n\r\nOnce that is done, lets connect Eclipse to the cloned repository.\r\n\r\nClick File and Import…​ to bring up the import dialog window. Select Maven and then Existing Maven Project.\r\n\r\n|Eclipse importMaven|\r\n\r\nNow click Next and point the root directory to where you cloned the git repository, Eclipse should automatically see the pom.xml file and show up in the next window.\r\n\r\n|Eclipse importMaven2|\r\n\r\nOnce the import is finished, you are able to now begin working with Tigase’s code inside Eclipse! Happy coding!\r\n\r\n.. |win git1| image:: /images/devguide/win-git1.jpg\r\n.. |win git2| image:: /images/devguide/win-git2.jpg\r\n.. |win git3| image:: /images/devguide/win-git3.jpg\r\n.. |win git4| image:: /images/devguide/win-git4.jpg\r\n.. |win git5| image:: /images/devguide/win-git5.jpg\r\n.. |Eclipse help| image:: /images/devguide/Eclipse-help.jpg\r\n.. |Eclipse m2Einstall| image:: /images/devguide/Eclipse-m2Einstall.jpg\r\n.. |Eclipse importMaven| image:: /images/devguide/Eclipse-importMaven.jpg\r\n.. |Eclipse importMaven2| image:: /images/devguide/Eclipse-importMaven2.jpg"
  },
  {
    "path": "src/main/restructured/Tigase_Development/Old_Stuff.rst",
    "content": "Old Stuff\r\n==============\r\n\r\nThis contains sections on old features, or information pertaining to old builds of Tigase. It is kept here for archival purposes.\r\n\r\nTigase DB Schema Explained\r\n--------------------------------\r\n\r\nThe schema basics, how it looks like and brief explanation to all rows can be found in the `list of schema files <https://github.com/tigase/tigase-server/tree/master/src/main/database>`__. However, this is hardly enough to understand how it works and how all the data is accessed. There are only 3 basic tables which actually keep all the Tigase server users' data: **tig_users**, **tig_nodes** and **tig_pairs**. Therefore it is not clear at first how Tigase’s data is organized.\r\n\r\nBefore you can understand the Tigase XMPP Server database schema, how it works and how to use it, is it essential to know what were the goals of it’s development and why it works that way. Let’s start with the API as this gives you the best introduction.\r\n\r\nSimplified access can be made through methods:\r\n\r\n.. code:: java\r\n\r\n   void setData(BareJID user, String key, String value);\r\n   String getData(BareJID user, String key);\r\n\r\nAnd more a complex version:\r\n\r\n.. code:: java\r\n\r\n   void setData(BareJID user, String subnode, String key, String value);\r\n   String getData(BareJID user, String subnode, String key, String def);\r\n\r\nEven though the API contains more methods, the rest is more or less a variation of presented above. A complete API description for all access methods is available in JavaDoc documentation in the `UserRepository <https://github.com/tigase/tigase-server/tree/master/src/main/java/tigase/db/UserRepository.java>`__ interface. So we are not going into too much detail here except for the main idea.\r\n\r\nTigase operates on <*key*, **value**> pairs for the individual user data. The idea behind this was to make the API very simple and also at the same time very flexible, so adding a new plugin or component would not require a database schema change, adding new tables, or conversion of the DB schema to a new version.\r\n\r\nAs a result the **UserRepository** interface is exposed to all of Tigase’s code, mainly the components and plugins (let’s call all of them modules). These modules simply call set/get methods to store or access module specific data.\r\n\r\nAs plugins or components are developed independently it may easily happen that developer choses the same key name to store some information. To avoid key name conflicts in the database a 'node' concept has been introduced. Therefore, most modules when set/get key value they also provide a subnode part, which in most cases is just XMLNS or some other unique string.\r\n\r\nThe 'node' thing is a little bit like directory in a file system, it may contain subnodes which makes the Tigase database behave like a hierarchical structure. And the notation is also similar to file systems, you use just **/** to separate node levels. In practice you can have the database organized like this:\r\n\r\n.. code:: sql\r\n\r\n   user-name@domain  --> (key, value) pairs\r\n                      |\r\n                  roster -->\r\n                          |\r\n                        item1 --> (key1, value1) pairs.\r\n                          |\r\n                        item2 --> (key1, value1) pairs.\r\n\r\nSo to access item’s 1 data from the roster you could call method like this:\r\n\r\n.. code:: java\r\n\r\n   getData(\"user-name@domain\", \"roster/item1\", key1, def1);\r\n\r\nThis is huge convenience for the developer, as he can focus on the module logic instead of worrying about data storage implementation and organization. Especially at the prototype phase it speeds development up and allows for a quick experiments with different solutions. In practice, accessing user’s roster in such a way would be highly inefficient so the roster is stored a bit differently but you get the idea. Also there is a more complex API used in some places allowing for more direct access to the database and store data in any format optimized for the scenario.\r\n\r\nRight now such a hierarchical structure is implemented on top of SQL databases but initially Tigase’s database was implemented as an XML structure, so it was natural and simple.\r\n\r\nIn the SQL database we simulate hierarchical structure with three tables:\r\n\r\n1. **tig_users** - with main users data, user id (JID), optional password, active flag, creation time and some other basic properties of the account. All of them could be actually stored in tig_pairs but for performance reasons they are in one place to quickly access them with a single, simple query.\r\n\r\n2. **tig_nodes** - is a table where the hierarchy is implemented. When Tigase was storing data in XML database the hierarchy was quite complex. However, in a SQL database it resulted in a very slow access to the data and a now more flat structure is used by most components. Please note, every user’s entry has something called root node, which is represented by 'root' string;\r\n\r\n3. **tig_pairs** - this is the table where all the user’s information is stored in form of the <key, value> pairs.\r\n\r\nSo we now know how the data is organized. Now we are going to learn how to access the data directly in the database using SQL queries.\r\n\r\nLet’s assume we have a user 'admin@test-d' for whom we want to retrieve the roster. We could simply execute query:\r\n\r\n.. code:: sql\r\n\r\n   select pval\r\n     from tig_users, tig_pairs\r\n     where user_id = 'admin@test-d' and\r\n           tig_users.uid = tig_pairs.uid and\r\n           pkey = 'roster';\r\n\r\nHowever, if multiple modules store data under the key 'roster' for a single user, we would receive multiple results. To access the correct 'roster' we also have to know the node hierarchy for this particular key. The main users roster is stored under the 'root' node, so the query would look like:\r\n\r\n.. code:: sql\r\n\r\n   select pval\r\n     from tig_users, tig_nodes, tig_pairs\r\n     where user_id = 'admin@test-d' and\r\n               tig_users.uid = tig_nodes.uid and\r\n               node = 'root' and\r\n               tig_users.uid = tig_pairs.uid and\r\n              pkey = 'roster';\r\n\r\nHow exactly the information is stored in the **tig_pairs** table depends on the particular module. For the roster it looks a bit like XML content:\r\n\r\n.. code:: xml\r\n\r\n   <contact jid=\"all-xmpp-test@test-d\" subs=\"none\" preped=\"simple\" name=\"all-xmpp-test\"/>\r\n\r\nWhy the most recent JDK?\r\n--------------------------\r\n\r\nThere are many reasons but the main is that we are a small team working on source code. So the whole approach is to make life easier for us, make the project easier to maintain, and development more efficient.\r\n\r\nHere is the list:\r\n\r\n-  **Easy to maintain** - No third-party libraries are used for the project which makes this project much easier to maintain. This simplifies issues of compatibility between particular versions of libraries. This also unifies coding with a single library package without having to rely on specific versions that may not be supported.\r\n\r\n-  **Easy to deploy** - Another reason to not use third-party tools is to make it easier for end-users to install and use the server.\r\n\r\n-  **Efficient development** - As no third-party libraries are used, Tigase needs either to implement many things on its own or use as much as possible of JDK functionality. We try to use as much as possible of existing library provided with JDK and the rest is custom coded.\r\n\r\nWhat features of JDKv5 are critical for Tigase development? Why I can’t simply re-implement some code to make it compatible with earlier JDK versions?\r\n\r\n-  **Non-blocking I/O for SSL/TLS** - This is functionality which can’t be simply re-implemented in JDK-1.4. As the whole server uses NIO it doesn’t make sense to use blocking I/O for SSL and TLS.\r\n\r\n-  **SASL** - This could be re-implemented for JDK-1.4 without much effort.\r\n\r\n-  **Concurrent package** - This could be re-implemented for JDK-1.4 but takes a lot of work. This is a critical part of the server as it uses multi-threading and concurrent processing.\r\n\r\n-  **Security package** - There number of extensions to the security package which otherwise would not work as easily with earlier versions of JDK.\r\n\r\n-  **LinkedHashMap** - in JDKv6 is a basement for the Tigase cache implementation.\r\n\r\n-  **Light HTTP server** - JDKv6 offers built-in light HTTP server which is needed to implement HTTP binding (JEP-0124) and HTTP user interface to monitor server activity and work with the server configuration.\r\n\r\nAs the JDK improves, so does our programming as we gain the ability to use new methods, efficiencies, and sometimes shortcuts.\r\n\r\nCurrently Tigase requires **JDKv8** and we recommend updating it as often as needed!\r\n\r\nAPI Description for Virtual Domains Management in the Tigase Server\r\n------------------------------------------------------------------------\r\n\r\nThe purpose of this guide is to introduce vhost management in Tigase server. Please refer to the JavaDoc documentation for all specific details not covered in this guide. All interfaces are well documented and you can use existing implementation as an example code base and reference point. The VHost management files are located in the repository and you can browse them using the `source viewer <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/vhosts>`__.\r\n\r\nVirtual hosts management in Tigase can be adjusted in many ways through the flexible API. The core elements of the virtual domains management is interface `VHostManager <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/vhosts/VHostManager.java>`__ class. They are responsible for providing the virtual hosts information to the rest of the Tigase server components. In particular to the `MessageRouter <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/server/MessageRouter.java>`__ class which controls how XMPP packets flow inside the server.\r\n\r\nThe class you most likely want to re-implement is `VHostJDBCRepository <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/vhosts/VHostJDBCRepository.java>`__ used as a default virtual hosts storage and implementing the `VHostRepository <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/vhosts/VHostRepository.java>`__ interface. You might need to have your own implementation in order to store and access virtual hosts in other than Tigase’s own data storage. This is especially important if you are going to modify the virtual domains list through systems other than Tigase.\r\n\r\nThe very basic virtual hosts storage is provided by `VHostItem <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/vhosts/VHostItem.java>`__ class. This is read only storage and provides the server a bootstrap vhosts data at the first startup time when the database with virtual hosts is empty or is not accessible. Therefore it is advised that all `VHostItem <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/vhosts/VHostItem.java>`__ implementations extend this class. The example code is provided in the `VHostJDBCRepository <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/vhosts/VHostJDBCRepository.java>`__ file.\r\n\r\nAll components which may need virtual hosts information or want to interact with virtual hosts management subsystem should implement the `VHostListener <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/vhosts/VHostListener.java>`__ interface. In some cases implementing this interface is necessary to receive packets for processing.\r\n\r\nVirtual host information is carried out in 2 forms inside the Tigase server:\r\n\r\n1. As a **String** value with the domain name\r\n\r\n2. As a `VHostItem <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/vhosts/VHostItem.java>`__ which contains all the domain information including the domain name, maximum number of users for this domain, whether the domain is enabled or disabled and so on. The JavaDoc documentation contains all the details about all available fields and usage.\r\n\r\nHere is a complete list of all interfaces and classes with a brief description for each of them:\r\n\r\n1. `VHostManagerIfc <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/vhosts/VHostManagerIfc.java>`__ - is an interface used to access virtual hosts information in all other server components. There is one default implementation of the interface: `VHostManager <#vhostMgr>`__.\r\n\r\n2. `VHostListener <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/vhosts/VHostListener.java>`__ - is an interface which allows components to interact with the `VHostManager <#vhostMgr>`__. The interaction is in both ways. The VHostManager provides virtual hosts information to components and components provide some control data required to correctly route packets to components.\r\n\r\n3. `VHostRepository <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/vhosts/VHostRepository.java>`__ - is an interface used to store and load virtual domains list from the database or any other storage media. There are 2 implementations for this interface: `VHostConfigRepository <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/vhosts/VhostConfigRepository.java>`__ which loads vhosts information for the configuration file and provides read-only storage and - `VHostJDBCRepository <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/vhosts/VHostJDBCRepository.java>`__ class which extends `VHostConfigRepository <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/vhosts/VhostConfigRepository.java>`__ and allows for both - reading and saving virtual domains list. VHostJDBCRepository is loaded as a default repository by Tigase server.\r\n\r\n4. `VHostItem <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/vhosts/VHostItem.java>`__ - is an interface which allows for accessing all the virtual domain properties. Sometimes the domain name is not sufficient for data processing. The domain may be temporarily disabled, may have a limited number of users and so on. Instances of this class keep all the information about the domain which might be needed by the server components.\r\n\r\n5. `VHostManager <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/vhosts/VHostManager.java>`__ - the default implementation of the VHostManagerIfc interface. It provides components with the virtual hosts information and manages the virtual hosts list. Processes ad-hoc commands for reloading, updating and removing domains.\r\n\r\n6. `VHostConfirRepository <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/vhosts/VhostConfigRepository.java>`__ - a very basic implementation of the `VHostRepository <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/vhosts/VHostRepository.java>`__ for loading domains list from the configuration file.\r\n\r\n7. `VHostJDBCRepository <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/vhosts/VHostJDBCRepository.java>`__ - the default implementation of the `VHostRepository <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/vhosts/VHostRepository.java>`__ loaded by Tigase server. It allows to read and store virtual domains list in the database accessible through UserRepository.\r\n\r\nExtending Virtual Domain settings\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIn some cases it is desired to extend Virtual Domain to add some additional settings. Since version 8.1.0 it is possible with use of ``VHostItemExtension`` and VHostItemExtensionProvider`.\r\n\r\nTo do so, you need to create a class implementing ``VHostItemExtension``. This class will hold values of settings for each virtual host. It is required to make it serializable to ``Element`` and deserializable from ``Element``. Moreover, it is required to make values of this class modifiable by ad-hoc commands.\r\n\r\nIt is recommended to provide additional methods allowing you to access values of this class.\r\n\r\nAdditionally, you need to implement ``VHostItemExtensionProvider`` interface as a bean and return a class of your implementation of ``VHostItemExtension``.\r\n\r\n*Example VHostItemExtensionProvider implementation for* ``SeeOtherHostVHostItemExtension``.\r\n\r\n.. code:: java\r\n\r\n   @Bean(name = SeeOtherHostVHostItemExtension.ID, parent = VHostItemExtensionManager.class, active = true)\r\n   public static class SeeOtherHostVHostItemExtensionProvider implements VHostItemExtensionProvider<SeeOtherHostVHostItemExtension> {\r\n\r\n       @Override\r\n       public String getId() {\r\n           return SeeOtherHostVHostItemExtension.ID;\r\n       }\r\n\r\n       @Override\r\n       public Class<SeeOtherHostVHostItemExtension> getExtensionClazz() {\r\n           return SeeOtherHostVHostItemExtension.class;\r\n       }\r\n   }\r\n\r\nStanza Limitations\r\n---------------------\r\n\r\nAlthough XMPP is robust and can process stanzas of any size in bytes, there are some limitations to keep in mind for Tigase server.\r\n\r\nPlease keep these in mind when using default Tigase settings and creating custom stanzas.\r\n\r\n-  Limit to number of attributes of single element = 50 attributes\r\n\r\n-  Limit to number of elements = 1024 elements\r\n\r\n-  Limit to length of element name = 1024 characters\r\n\r\n-  Limit to length of attribute name = 1024 characters\r\n\r\n-  Limit to length of attribute value = 10240 characters\r\n\r\n-  Limit to length of content of single element CDATA = 1048576b or 1Mb\r\n\r\nThese values may be changed.\r\n\r\n**Note that these limitations are to elements and attributes that may be within a stanza, but do not limit the overall stanza length.**\r\n\r\nEscape Characters\r\n^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThere are special characters that need to be escaped if they are included in the stanza to avoid conflicts. The rules are similar to normal XML escaping. The following is a list of characters that need to be escaped and what to use to escape them:\r\n\r\n::\r\n\r\n   &    &amp;\r\n   <    &lt;\r\n   >    &gt;\r\n   \"    &quot;\r\n   '    &apos;\r\n\r\nAPI changes in the Tigase Server 5.x\r\n-----------------------------------------\r\n\r\n**THIS INFORMATION IS FOR OLDER VERSIONS OF TIGASE**\r\n\r\nThe API changes can effect you only if you develop own code to run inside Tigase server. The changes are not extensive but in some circumstances may require many simple changes in a few files.\r\n\r\nAll the changes are related to introducing tigase.xmpp.JID and tigase.xmpp.BareJID classes. It is recommended to use them for all operations performed on the user JID instead of the String class which was used before changes.\r\n\r\nThere are a few advantages to using the new classes. First of all they do all the user JID checking and parsing, they also perform stringprep processing. Therefore if you use data kept by instance of the JID or BareJID you can be sure they are valid and correct.\r\n\r\nThese are not all advantages however. JID parsing code appears to use a lot of CPU power to conduct it’s operations. JIDs and parts of the JIDs are used in many places of the stanza processing and the parsing is performed over and over again in all these places, wasting CPU cycles, memory and time. Therefore, great performance benefits can be gained from these new class are in if, once parsed, JIDs are reused in all further stanza processing.\r\n\r\nThis is where the tigase.server.Packet class comes in handy. Instances of the Packet class encloses XML stanza and pre-parses some, the most commonly used elements of the stanza, stanza source and destination addresses among them. As an effect there are all new methods available in the class:\r\n\r\n.. code:: java\r\n\r\n   JID getStanzaFrom();\r\n   JID getStanzaTo();\r\n   JID getFrom();\r\n   JID getTo();\r\n   JID getPacketFrom();\r\n   JID getPacketTo();\r\n\r\nWhereas following methods are still available but have been deprecated:\r\n\r\n.. code:: java\r\n\r\n   String getStanzaFrom();\r\n   String getStanzaTo();\r\n\r\nPlease refer to the JavaDoc documentation for the `Packet <http://docs.tigase.org/tigase-server/snapshot/javadoc/tigase/server/Packet.html>`__ class and methods to learn all the details of these methods and difference between them.\r\n\r\nAnother difference is that you can no longer create the ``Packet`` instance using a constructor. Instead there are a few factory methods available:\r\n\r\n.. code:: java\r\n\r\n   static Packet packetInstance(Element elem);\r\n   static Packet packetInstance(Element elem,\r\n       JID stanzaFrom, JID stanzaTo);\r\n\r\nAgain, please refer to the JavaDoc documentation for all the details. The main point of using these methods is that they actually return an instance of one of the following classes instead of the ``Packet`` class: ``Iq``, ``Presence`` or ``Message``.\r\n\r\nThere is also a number of utility methods helping with creating a copy of the Packet instance preserving as much pre-parsed data as possible:\r\n\r\n.. code:: java\r\n\r\n   Packet copyElementOnly();\r\n   Packet errorResult(...);\r\n   Packet okResult(...);\r\n   Packet swapFromTo();\r\n   Packet swapStanzaFromTo();\r\n\r\nWe try to keep the `JavaDoc <http://docs.tigase.org/tigase-server/snapshot/javadoc/>`__ documentation as complete as possible. Please contact us if you find missing or incorrect information.\r\n\r\nThe main point is to reuse ``JID`` or ``BareJID`` instances in your code as much as possible. You never know, your code may run in highly loaded systems with throughput of 100k XMPP packets per second.\r\n\r\nAnother change. This one a bit risky as it is very difficult to find all places where this could be used. There are several utility classes and methods which accept source and destination address of a stanza and produce something. There was a great confusion with them, as in some of them the first was the source address and in others the destination address. All the code has been re-factored to keep the parameter order the same in all places. Right now the policy is: **source address first**. Therefore in all places where there was a method:\r\n\r\n.. code:: java\r\n\r\n   Packet method(String to, String from);\r\n\r\nit has been changed to:\r\n\r\n.. code:: java\r\n\r\n   Packet method(JID from, JID to);\r\n\r\nAs far as I know most of these method were used only by myself so I do not expect much trouble for other developers.\r\n\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Development/Packet_Filtering_in_Component.rst",
    "content": ".. _packetfiltering:\r\n\r\nPacket Filtering in Components\r\n================================\r\n\r\nThe Packet Filter API\r\n---------------------------\r\n\r\nTigase server offers an API to filter packet traffic inside every component. You can separately filter incoming and outgoing packets.\r\n\r\nBy filtering we mean intercepting a packet and possibly making some changes to the packet or just blocking the packet completely. By blocking we mean stopping from any further processing and just dropping the packet.\r\n\r\nThe packet filtering is based on the `PacketFilterIfc <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/server/PacketFilterIfc.java>`__ interface. Please have a look in the JavaDoc documentation to this interface for all the details. The main filtering method is ``Packet filter(Packet packet);`` which takes packets as an input, processes it, possibly alerting the packet content (may add or remove some payloads) and returns a **Packet** for further processing. If it returns **null** it means the packet is blocked and no further processing is permitted otherwise it returns a **Packet** object which is either the same object it received as a parameter or a modified copy of the original object.\r\n\r\nPlease note, although **Packet** object is not an unmodifiable instance, it is recommended that changes to the existing object are not made. The same **Packet** might be processed at the same time by other components or threads, therefore modification of the **Packet** may lead to unpredictable results.\r\n\r\nPlease refer to an example code in `PacketCounter <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/server/filters/PacketCounter.java>`__ which is a very simple filter counting different types of packets. This filter is by default loaded to all components which might be very helpful for assessing traffic shapes on newly deployed installation. You can get counters for all types of packets, where they are generated, where they flow, what component they put the most load on.\r\n\r\nThis is because packet filter can also generate and present its own statistics which are accessible via normal statistics monitoring mechanisms. To take advantage of the statistics functionality, the packet filter has to implement the ``void getStatistics(StatisticsList list);`` method. Normally, the method is empty. However, you can generate and add statistics from the filter to the list. Please refer to `PacketCounter <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/server/filters/PacketCounter.java>`__ for an example implementation code.\r\n\r\nConfiguration\r\n-----------------\r\n\r\nPacket filters are configurable, that is a packet filters instances can be configured in Tigase server’s configuration for each component separately and for each traffic direction. This gives you a great flexibility and control over the data flow inside the Tigase server.\r\n\r\nYou can for example, load specific packet filters to all connections managers to block specific traffic or specific packet source from sending messages to users on your server. You could also reduce the server overall load by removing certain payload from all packets. The possibilities are endless.\r\n\r\nThe default configuration is generated in such a way that each component loads a single packet filter - ``PacketCounter`` for each traffic direction:\r\n\r\n.. code:: bash\r\n\r\n   bosh {\r\n       incomingFilters (class: tigase.server.filters.PacketFiltersBean$IncomingPacketFiltersBean) {\r\n           packetCounter (class: tigase.server.filters.PacketCounter) {}\r\n       }\r\n       outgoingFilters (class: tigase.server.filters.PacketFiltersBean$OutgoingPacketFiltersBean) {\r\n           packetCounter (class: tigase.server.filters.PacketCounter) {}\r\n       }\r\n       seeOtherHost {}\r\n   }\r\n   c2s {\r\n       incomingFilters (class: tigase.server.filters.PacketFiltersBean$IncomingPacketFiltersBean) {\r\n           packetCounter (class: tigase.server.filters.PacketCounter) {}\r\n       }\r\n       outgoingFilters (class: tigase.server.filters.PacketFiltersBean$OutgoingPacketFiltersBean) {\r\n           packetCounter (class: tigase.server.filters.PacketCounter) {}\r\n       }\r\n       seeOtherHost {}\r\n   }\r\n   'message-router' {\r\n       incomingFilters (class: tigase.server.filters.PacketFiltersBean$IncomingPacketFiltersBean) {\r\n           packetCounter (class: tigase.server.filters.PacketCounter) {}\r\n       }\r\n       outgoingFilters (class: tigase.server.filters.PacketFiltersBean$OutgoingPacketFiltersBean) {\r\n           packetCounter (class: tigase.server.filters.PacketCounter) {}\r\n       }\r\n   }\r\n   muc {\r\n       incomingFilters (class: tigase.server.filters.PacketFiltersBean$IncomingPacketFiltersBean) {\r\n           packetCounter (class: tigase.server.filters.PacketCounter) {}\r\n       }\r\n       outgoingFilters (class: tigase.server.filters.PacketFiltersBean$OutgoingPacketFiltersBean) {\r\n           packetCounter (class: tigase.server.filters.PacketCounter) {}\r\n       }\r\n   }\r\n   s2s {\r\n       incomingFilters (class: tigase.server.filters.PacketFiltersBean$IncomingPacketFiltersBean) {\r\n           packetCounter (class: tigase.server.filters.PacketCounter) {}\r\n       }\r\n       outgoingFilters (class: tigase.server.filters.PacketFiltersBean$OutgoingPacketFiltersBean) {\r\n           packetCounter (class: tigase.server.filters.PacketCounter) {}\r\n       }\r\n   }\r\n   'sess-man' () {\r\n       incomingFilters (class: tigase.server.filters.PacketFiltersBean$IncomingPacketFiltersBean) {\r\n           packetCounter (class: tigase.server.filters.PacketCounter) {}\r\n       }\r\n       outgoingFilters (class: tigase.server.filters.PacketFiltersBean$OutgoingPacketFiltersBean) {\r\n           packetCounter (class: tigase.server.filters.PacketCounter) {}\r\n       }\r\n   }\r\n\r\nNow, let’s say you have a packet filter implemented in class: **com.company.SpamBlocker**. You want to disable PacketCounter on most of the components leaving it only in the message router component and you want to install SpamBlocker in all connection managers.\r\n\r\n*Please note, in case of the connection managers 'incoming' and 'outgoing' traffic is probably somehow opposite from what you would normally expect.*\r\n\r\n-  **incoming** is traffic which is submitted to a component by message router and has to be further processed. For connection managers this further processing means sending it out to the network.\r\n\r\n-  **outgoing** is traffic which is 'generated' by the component and goes out of the component. Such a packet is submitted to message router which then decides where to send it for further processing. For connection managers **outgoing** traffic is all the packets just received from the network.\r\n\r\nAccording to that we have to apply the SpamBlocker filter to all 'outgoing' traffic in all connection managers. You may also decide that it might be actually useful to compare traffic shape between Bosh connections and standard XMPP c2s connections. So let’s leave packet counters for this components too.\r\n\r\nHere is our new configuration applying SpamBlocker to connection managers and PacketCounter to a few other components:\r\n\r\n.. code:: bash\r\n\r\n   bosh {\r\n       incomingFilters () {\r\n           packetCounter () {}\r\n       }\r\n       outgoingFilters () {\r\n           packetCounter (active: false) {}\r\n           spamBlocker (class: com.company.SpamBlocker, active: true) {}\r\n       }\r\n       seeOtherHost {}\r\n   }\r\n   c2s {\r\n       incomingFilters () {\r\n           packetCounter () {}\r\n       }\r\n       outgoingFilters () {\r\n           packetCounter (active: false) {}\r\n           spamBlocker (class: com.company.SpamBlocker, active: true) {}\r\n       }\r\n       seeOtherHost {}\r\n   }\r\n   'message-router' {\r\n       incomingFilters () {\r\n           packetCounter () {}\r\n       }\r\n       outgoingFilters () {\r\n           packetCounter () {}\r\n       }\r\n   }\r\n   muc {\r\n       incomingFilters () {\r\n           packetCounter (active: false) {}\r\n       }\r\n       outgoingFilters () {\r\n           packetCounter (active: false) {}\r\n       }\r\n   }\r\n   s2s {\r\n       incomingFilters () {\r\n           packetCounter (active: false) {}\r\n       }\r\n       outgoingFilters () {\r\n           packetCounter (active: false) {}\r\n           spamBlocker (class: com.company.SpamBlocker, active: true) {}\r\n       }\r\n   }\r\n   'sess-man' () {\r\n       incomingFilters () {\r\n           packetCounter (active: false) {}\r\n       }\r\n       outgoingFilters () {\r\n           packetCounter (active: false) {}\r\n       }\r\n   }\r\n\r\nIn case of ``incomingFilters`` ``outgoingFilters`` and ``packetCounter`` we were able to skip providing ``class`` parameter as those classes are properly annotated with ``@Bean`` annotation.\r\n\r\nThe simplest way to apply the new configuration is via the ``config.tdsl`` file which is in details described in the *Admin Guide*.\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Development/Plugin_Development.rst",
    "content": "Plugin Development\r\n===================\r\n\r\nThis is a set of documents explaining details what is a plugin, how they are designed and how they work inside the Tigase server. The last part of the documentation explains step by step creating the code for a new plugin.\r\n\r\n-  :ref:`Writing Plugin Code<writePluginCode>`\r\n\r\n-  :ref:`Plugin Configuration<pluginConf>`\r\n\r\n-  :ref:`How Packets are Processed by the SM and Plugins<packetprocess>`\r\n\r\n-  :ref:`SASL Custom Mechanisms and Configuration<saslcmac>`\r\n\r\n.. _writePluginCode:\r\n\r\nWriting Plugin Code\r\n---------------------\r\n\r\nStanza processing takes place in 4 steps. A different kind of plugin is responsible for each step of processing:\r\n\r\n1. `XMPPPreprocessorIfc <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/xmpp/XMPPPreprocessorIfc.java>`__ - is the interface for packets pre-processing plugins.\r\n\r\n2. `XMPPProcessorIfc <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/xmpp/XMPPProcessor.java>`__ - is the interface for packets processing plugins.\r\n\r\n3. `XMPPPostprocessorIfc <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/xmpp/XMPPPostprocessorIfc.java>`__ - is the interface for packets post-processing plugins.\r\n\r\n4. `XMPPPacketFilterIfc <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/xmpp/XMPPPacketFilterIfc.java>`__ - is the interface for processing results filtering.\r\n\r\nIf you look inside any of these interfaces you will only find a single method. This is where all the packet processing takes place. All of them take a similar set of parameters and below is a description for all of them:\r\n\r\n-  **Packet packet** - packet is which being processed. This parameter may never be null. Even though this is not an immutable object it mustn’t be altered. None of it’s fields or attributes can be changed during processing.\r\n\r\n-  **XMPPResourceConnection session** - user session which keeps all the user session data and also gives access to the user’s data repository. It allows for the storing of information in permanent storage or in memory only during the life of the session. This parameter can be null if there is no online user session at the time of the packet processing.\r\n\r\n-  **NonAuthUserRepository repo** - this is a user data storage which is normally used when the user session (parameter above) is null. This repository allows for a very restricted access only. It allows for storing some user private data (but doesn’t allow overwriting existing data) like messages for offline users and it also allows for reading user public data like VCards.\r\n\r\n-  **Queue<Packet> results** - this a collection with packets which have been generated as input packet processing results. Regardless a response to a user request is sent or the packet is forwarded to it’s destination it is always required that a copy of the input packet is created and stored in the **results** queue.\r\n\r\n-  **Map<String, Object> settings** - this map keeps plugin specific settings loaded from the Tigase server configuration. In most cases it is unused, however if the plugin needs to access an external database that this is a way to pass the database connection string to the plugin.\r\n\r\nAfter a closer look in some of the interfaces you can see that they extend another interface: `XMPPImplIfc <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/xmpp/XMPPImplIfc.java>`__ which provides a basic meta information about the plugin implementation. Please refer to `JavaDoc <http://docs.tigase.org/tigase-server/snapshot/javadoc/tigase/xmpp/impl/package-summary.html>`__ documentation for all details.\r\n\r\nFor purpose of this guide we are implementing a simple plugin for handling all **<message/>** packets that is forwarding packets to the destination address. Incoming packets are forwarded to the user connection and outgoing packets are forwarded to the external destination address. This `message plugin <https://github.com/tigase/tigase-server/blob/master/src/main/java/tigase/xmpp/impl/Message.java>`__ is actually implemented already and it is available in our Git repository. The code has some comments inside already but this guide goes deeper into the implementation details.\r\n\r\nFirst of all you have to choose what kind of plugin you want to implement. If this is going to be a packet processor you have to implement the **XMPPProcessorIfc** interface, if this is going to be a pre-processor then you have to implement the **XMPPPreprocessorIfc** interface. Of course your implementation can implement more than one interface, even all of them. There are also two abstract helper classes, one of which you should use as a base for all you plugins **XMPPProcessor** or use **AnnotatedXMPPProcessor** for annotation support.\r\n\r\nUsing annotation support\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThe class declaration should look like this (assuming you are implementing just the packet processor):\r\n\r\n.. code:: java\r\n\r\n   public class Message extends AnnotatedXMPPProcessor\r\n      implements XMPPProcessorIfc\r\n\r\nThe first thing to create is the plugin **ID**. This is a unique string which you put in the configuration file to tell the server to load and use the plugin. In most cases you can use XMLNS if the plugin wants packets with elements with a very specific name space. Of course there is no guarantee there is no other packet for this specific XML element too. As we want to process all messages and don’t want to spend whole day on thinking about a cool ID, let’s say our ID is: *message*.\r\n\r\nA plugin informs about it’s presence using a static **ID** field and **@Id** annotation placed on class:\r\n\r\n.. code:: java\r\n\r\n   @Id(ID)\r\n   public class Message extends AnnotatedXMPPProcessor\r\n      implements XMPPProcessorIfc {\r\n     protected static final String ID = \"message\";\r\n   }\r\n\r\nAs mentioned before, this plugin receives only this kind of packets for processing which it is interested in. In this example, the plugin is interested only in packets with **<message/>** elements and only if they are in the \"**jabber:client**\" namespace. To indicate all supported elements and namespaces we have to add 2 more annotations:\r\n\r\n.. code:: java\r\n\r\n   @Id(ID)\r\n   @Handles({\r\n     @Handle(path={ \"message\" },xmlns=\"jabber:client\")\r\n   })\r\n   public class Message extends AnnotatedXMPPProcessor\r\n      implements XMPPProcessorIfc {\r\n     private static final String ID = \"message\";\r\n   }\r\n\r\nUsing older non-annotation based implementation\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThe class declaration should look like this (assuming you are implementing just the packet processor):\r\n\r\n.. code:: java\r\n\r\n   public class Message extends XMPPProcessor\r\n      implements XMPPProcessorIfc\r\n\r\nThe first thing to create is the plugin **ID** like above.\r\n\r\nA plugin informs about it’s ID using following code:\r\n\r\n.. code:: java\r\n\r\n   private static final String ID = \"message\";\r\n   public String id() { return ID; }\r\n\r\nAs mentioned before this plugin receives only this kind of packets for processing which it is interested in. In this example, the plugin is interested only in packets with **<message/>** elements and only if they are in \"**jabber:client**\" namespace. To indicate all supported elements and namespaces we have to add 2 more methods:\r\n\r\n.. code:: java\r\n\r\n   public String[] supElements() {\r\n     return new String[] {\"message\"};\r\n   }\r\n\r\n   public String[] supNamespaces() {\r\n     return new String[] {\"jabber:client\"};\r\n   }\r\n\r\nImplementation of processing method\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nNow we have our plugin prepared for loading in Tigase. The next step is the actual packet processing method. For the complete code, please refer to the plugin in the Git. I will only comment here on elements which might be confusing or add a few more lines of code which might be helpful in your case.\r\n\r\n.. code:: java\r\n\r\n   @Override\r\n   public void process(Packet packet, XMPPResourceConnection session,\r\n       NonAuthUserRepository repo, Queue<Packet> results, Map<String, Object> settings)\r\n       throws XMPPException {\r\n\r\n       // For performance reasons it is better to do the check\r\n       // before calling logging method.\r\n       if (log.isLoggable(Level.FINEST)) {\r\n           log.log(Level.FINEST, \"Processing packet: {0}\", packet);\r\n       }\r\n\r\n       // You may want to skip processing completely if the user is offline.\r\n       if (session == null) {\r\n           return;\r\n       }    // end of if (session == null)\r\n\r\n       try {\r\n\r\n           // Remember to cut the resource part off before comparing JIDs\r\n           BareJID id = (packet.getStanzaTo() != null) ? packet.getStanzaTo().getBareJID() : null;\r\n\r\n           // Checking if this is a packet TO the owner of the session\r\n           if (session.isUserId(id)) {\r\n\r\n               // Yes this is message to 'this' client\r\n               Packet result = packet.copyElementOnly();\r\n\r\n               // This is where and how we set the address of the component\r\n               // which should receive the result packet for the final delivery\r\n               // to the end-user. In most cases this is a c2s or Bosh component\r\n               // which keep the user connection.\r\n               result.setPacketTo(session.getConnectionId(packet.getStanzaTo()));\r\n\r\n               // In most cases this might be skipped, however if there is a\r\n               // problem during packet delivery an error might be sent back\r\n               result.setPacketFrom(packet.getTo());\r\n\r\n               // Don't forget to add the packet to the results queue or it\r\n               // will be lost.\r\n               results.offer(result);\r\n\r\n               return;\r\n           }    // end of else\r\n\r\n           // Remember to cut the resource part off before comparing JIDs\r\n           id = (packet.getStanzaFrom() != null) ? packet.getStanzaFrom().getBareJID() : null;\r\n\r\n           // Checking if this is maybe packet FROM the client\r\n           if (session.isUserId(id)) {\r\n\r\n               // This is a packet FROM this client, the simplest action is\r\n               // to forward it to its destination:\r\n               // Simple clone the XML element and....\r\n               // ... putting it to results queue is enough\r\n               results.offer(packet.copyElementOnly());\r\n\r\n               return;\r\n           }\r\n\r\n           // Can we really reach this place here?\r\n           // Yes, some packets don't even have from or to address.\r\n           // The best example is IQ packet which is usually a request to\r\n           // the server for some data. Such packets may not have any addresses\r\n           // And they usually require more complex processing\r\n           // This is how you check whether this is a packet FROM the user\r\n           // who is owner of the session:\r\n           JID jid = packet.getFrom();\r\n\r\n           // This test is in most cases equal to checking getStanzaFrom()\r\n           if (session.getConnectionId().equals(jid)) {\r\n\r\n               // Do some packet specific processing here, but we are dealing\r\n               // with messages here which normally need just forwarding\r\n               Element el_result = packet.getElement().clone();\r\n\r\n               // If we are here it means FROM address was missing from the\r\n               // packet, it is a place to set it here:\r\n               el_result.setAttribute(\"from\", session.getJID().toString());\r\n\r\n               Packet result = Packet.packetInstance(el_result, session.getJID(),\r\n                   packet.getStanzaTo());\r\n\r\n               // ... putting it to results queue is enough\r\n               results.offer(result);\r\n           }\r\n       } catch (NotAuthorizedException e) {\r\n           log.warning(\"NotAuthorizedException for packet: \" + packet);\r\n           results.offer(Authorization.NOT_AUTHORIZED.getResponseMessage(packet,\r\n                   \"You must authorize session first.\", true));\r\n       }    // end of try-catch\r\n   }\r\n\r\n.. _pluginConf:\r\n\r\nPlugin Configuration\r\n-----------------------\r\n\r\nPlugin configuration is straightforward.\r\n\r\nTell the Tigase server to load or not to load the plugins via the ``config.tdsl`` file. Plugins fall within the ``'sess-man'`` container. To activate a plugin, simply list it among the sess-man plugins.\r\n\r\nIf you do not wish to use this method to find out what plugins are running, there are two ways you can identify if a plugin is running. One is the log file: logs/tigase-console.log. If you look inside you can find following output:\r\n\r\n.. code:: bash\r\n\r\n   Loading plugin: jabber:iq:register ...\r\n   Loading plugin: jabber:iq:auth ...\r\n   Loading plugin: urn:ietf:params:xml:ns:xmpp-sasl ...\r\n   Loading plugin: urn:ietf:params:xml:ns:xmpp-bind ...\r\n   Loading plugin: urn:ietf:params:xml:ns:xmpp-session ...\r\n   Loading plugin: roster-presence ...\r\n   Loading plugin: jabber:iq:privacy ...\r\n   Loading plugin: jabber:iq:version ...\r\n   Loading plugin: http://jabber.org/protocol/stats ...\r\n   Loading plugin: starttls ...\r\n   Loading plugin: vcard-temp ...\r\n   Loading plugin: http://jabber.org/protocol/commands ...\r\n   Loading plugin: jabber:iq:private ...\r\n   Loading plugin: urn:xmpp:ping ...\r\n\r\nand this is a list of plugins which are loaded in your installation.\r\n\r\nAnother way is to look inside the session manager source code which has the default list hardcoded:\r\n\r\n.. code:: java\r\n\r\n   private static final String[] PLUGINS_FULL_PROP_VAL =\r\n     {\"jabber:iq:register\", \"jabber:iq:auth\", \"urn:ietf:params:xml:ns:xmpp-sasl\",\r\n      \"urn:ietf:params:xml:ns:xmpp-bind\", \"urn:ietf:params:xml:ns:xmpp-session\",\r\n      \"roster-presence\", \"jabber:iq:privacy\", \"jabber:iq:version\",\r\n      \"http://jabber.org/protocol/stats\", \"starttls\", \"msgoffline\",\r\n      \"vcard-temp\", \"http://jabber.org/protocol/commands\", \"jabber:iq:private\",\r\n      \"urn:xmpp:ping\", \"basic-filter\", \"domain-filter\"};\r\n\r\nIn you wish to load a plugin outside these defaults, you have to edit the list and add your plugin IDs as a value to the plugin list under 'sess-man'. Let’s say our plugin ID is **message** as in our all examples:\r\n\r\n.. code:: bash\r\n\r\n   'sess-man' () {\r\n       'jabber:iq:register' () {}\r\n       'jabber:iq:auth' () {}\r\n       message () {}\r\n   }\r\n\r\nAssuming your plugin class is in the classpath it will be loaded and used at the runtime. You may specify class by adding ``class: class.implementing.plugin`` within the parenthesis of the plugin.\r\n\r\n.. Note::\r\n\r\n   If your plugin name has any special characters (-,:\\|/.) it needs to be encapsulated in single quotation marks.\r\n\r\nThere is another part of the plugin configuration though. If you looked at the :ref:`Writing Plugin Code \r\n<writePluginCode>` guide you can remember the **Map settings** processing parameter. This is a map of properties you can set in the configuration file and these setting will be passed to the plugin at the processing time.\r\n\r\nAgain **config.tdsl** is the place to put the stuff. These kind of properties start under your **plugin ID** and each key and value will be a child underneath:\r\n\r\n.. code::\r\n\r\n   'sess-man' () {\r\n       pluginID {\r\n         key1 = 'val1'\r\n         key2 = 'val2'\r\n         key3 = 'val3'\r\n       }\r\n   }\r\n\r\n.. Note::\r\n\r\n   From v8.0.0 you will no longer be able to specify one value for multiple keys, you must set each one individually.\r\n\r\nLast but not least - in case you have **omitted plugin ID**:\r\n\r\n.. code:: bash\r\n\r\n   'sess-man' () {\r\n       key1 = 'val1'\r\n   }\r\n\r\nthen the configured key-value pair will be a global/common plugin setting available to all loaded plugins.\r\n\r\n.. _packetprocess:\r\n\r\nHow Packets are Processed by the SM and Plugins\r\n---------------------------------------------------\r\n\r\nFor Tigase server plugin development it is important to understand how it all works. There are different kind of plugins responsible for processing packets at different stages of the data flow. Please read the introduction below before proceeding to the actual coding part.\r\n\r\nIntroduction\r\n^^^^^^^^^^^^^^^^^\r\n\r\nIn Tigase server **plugins** are pieces of code responsible for processing particular XMPP stanzas. A separate plugin might be responsible for processing messages, a different one for processing presences, a separate plugins responsible for iq roster, and a different one for iq version and so on.\r\n\r\nA plugin provides information about what exact XML element(s) name(s) with xmlns it is interested in. So you can create a plugin which is interested in all packets containing caps child.\r\n\r\nThere might be no plugin for a particular stanza element, in this case the default action is used which is simple forwarding stanza to a destination address. There might be also more than one plugin for a specific XML element and then they all process the same stanza simultaneously in separate threads so there is no guarantee on the order in which the stanza is processed by a different plugins.\r\n\r\nEach stanza goes through the Session Manager component which processes packets in a few steps. Have a look at the picture below:\r\n\r\n|Consumer|\r\n\r\nThe picture shows that each stanza is processed by the session manager in 4 steps:\r\n\r\n1. Pre-processing - All loaded pre-processors receive the packet for processing. They work within session manager thread and they have no internal queue for processing. As they work within Session Manager thread it is important that they limit processing time to absolute minimum as they may affect the Session Manager performance. The intention for the pre-processors is to use them for packet blocking. If the pre-processing result is 'true' then the packet is blocked and no further processing is performed.\r\n\r\n2. Processing - This is the next step the packet gets through if it wasn’t blocked by any of the pre-processors. It gets inserted to all processors queues with requested interest in this particular XML element. Each processor works in a separate thread and has own internal fixed size processing queue.\r\n\r\n3. Post-processing - If there is no processor for the stanza then the packet goes through all post-processors. The last post-processor that is built into session manager post-processor tries to apply a default action to a packet which hasn’t been processed in step 2. Normally the default action is just forwarding the packet to a destination. Most commonly it is applied to <message/> packets.\r\n\r\n4. Finally, if any of above 3 steps produced output/result packets all of them go through all filters which may or may not block them.\r\n\r\nAn important thing to note is that we have two kinds or two places where packets may be blocked or filtered out. One place is before packet is processed by the plugin and another place is after processing where filtering is applied to all results generated by the processor plugins.\r\n\r\nIt is also important to note that session manager and processor plugins act as packet consumers. The packet is taken for processing and once processing is finished the packet is destroyed. Therefore to forward a packet to a destination one of the processor must create a copy of the packet, set all properties and attributes and return it as a processing result. Of course processor can generate any number of packets as a result. Result packets can be generated in any of above 4 steps of the processing. Have a look at the picture below:\r\n\r\n|User Send to Comp|\r\n\r\nIf the packet P1 is sent from outside of the server, for example to a user on another server or to some component (MUC, PubSub, transport), then one of the processor must create a copy (P2) of the packet and set all attributes and destination addresses correctly. Packet P1 has been consumed by the session manager during processing and a new packet has been generated by one of the plugins.\r\n\r\nThe same of course happens on the way back from the component to the user:\r\n\r\n|Comp Sends to User|\r\n\r\nThe packet from the component is processed and one of the plugins must generate a copy of the packet to deliver it to the user. Of course packet forwarding is a default action which is applied when there is no plugin for the particular packet.\r\n\r\nIt is implemented this way because the input packet P1 can be processed by many plugins at the same time therefore the packet should be in fact immutable and must not change once it got to the session manager for processing.\r\n\r\nThe most obvious processing work flow is when a user sends request to the server and expects a response from the server:\r\n\r\n|User Request Response|\r\n\r\nThis design has one surprising consequence though. If you look at the picture below showing communication between 2 users you can see that the packet is copied twice before it is delivered to a final destination:\r\n\r\n|User Sends to User|\r\n\r\nThe packet has to be processed twice by the session manager. The first time it is processed on behalf of the User A as an outgoing packet and the second time it is processed on behalf of the User B as an incoming packet.\r\n\r\nThis is to make sure the User A has permission to send a packet out and all processing is applied to the packet and also to make sure that User B has permission to receive the packet and all processing is applied. If, for example, the User B is offline there is an offline message processor which should send the packet to a database instead of User B.\r\n\r\n.. |Consumer| image:: /images/devguide/sm-consumer.png\r\n.. |User Send to Comp| image:: /images/devguide/user-sends-to-comp.png\r\n.. |Comp Sends to User| image:: /images/devguide/comp-sends-to-user.png\r\n.. |User Request Response| image:: /images/devguide/user-request-response.png\r\n.. |User Sends to User| image:: /images/devguide/user-sends-to-user.png\r\n\r\n.. _saslcmac:\r\n\r\nSASL Custom Mechanisms and Configuration\r\n----------------------------------------------\r\n\r\n**This API is available from Tigase XMPP Server version 5.2.0 or later on our current master branch.**\r\n\r\n**In version 8.0.0 there was a major change to the API and configuration of custom SASL mechanisms.**\r\n\r\n*Note that API is under active development. This description may be updated at any time.*\r\n\r\nBasic SASL Configuration\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nSASL implementation in Tigase XMPP Server is compatible with Java API, the same exact interfaces are used.\r\n\r\nThe SASL implementation consists of following parts:\r\n\r\n1. mechanism\r\n\r\n2. CallbackHandler\r\n\r\nMechanisms Configuration\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nTo add a new mechanism, a new factory for the mechanism has to be implemented and registered.\r\n\r\nThe simplest way to add register a new factory is to annotate its class with ``@Bean`` annotation:\r\n\r\n**Example of the registration of a SASL mechanism factory with an annotation setting id of the factory to** ``customSaslFactory``.\r\n\r\n.. code:: java\r\n\r\n   @Bean(name=\"customSaslFactory\", parent = TigaseSaslProvider.class, active = true)\r\n   public class OwnFactory implements SaslServerFactory {}\r\n\r\nIt can also be done by specifying the class directly for bean ``customSaslFactory`` in the ``config.tdsl`` file like in the example below:\r\n\r\n**Example of the registration of a SASL mechanism factory with TDSL setting id of the factory to ``customSaslFactory``.**\r\n\r\n.. code::\r\n\r\n   'sess-man' () {\r\n       'sasl-provider' () {\r\n           customSaslFactory(class: com.example.OwnFactory) {}\r\n       }\r\n   }\r\n\r\nThe class must implement the ``SaslServerFactory`` interface and has public constructor without any arguments. All mechanisms returned by ``getMechanismNames()`` method will be registered automatically.\r\n\r\nThe default factory that is available and registered by default is ``tigase.auth.TigaseSaslServerFactory`` which provides ``PLAIN``, ``ANONYMOUS``, ``EXTERNAL``, ``SCRAM-SHA-1``, ``SCRAM-SHA-256`` and ``SCRAM-SHA-512`` mechanisms.\r\n\r\nCallbackHandler Configuration\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nThe ``CallbackHandler`` is a helper class used for loading/retrieving authentication data from data repository and providing them to a mechanism.\r\n\r\nTo register a new callback handler you need to create a new class extending ``tigase.auth.CallbackHandlerFactory`` (if you wish to keep existing SASL callback handlers) or implementing ``tigase.auth.CallbackHandlerFactoryIfc``. You will need to override ``create()`` method to return an instance of your custom ``CallbackHandler`` when appropriate.\r\n\r\nNext you need to register new implementation of ``CallbackHandlerFactoryIfc``. The ``config.tdsl`` file should include:\r\n\r\n.. code::\r\n\r\n   'sess-man' () {\r\n       'sasl-provider' () {\r\n           callback-handler-factory(class: com.example.OwnCallbackHandlerFactory) {}\r\n       }\r\n   }\r\n\r\nDuring the authentication process, Tigase server always checks for asks callback handler factory for specific handler to selected mechanisms, and if there is no specific handler the default one is used.\r\n\r\nSelecting Mechanisms Available in the Stream\r\n'''''''''''''''''''''''''''''''''''''''''''''\r\n\r\nThe ``tigase.auth.MechanismSelector`` interface is used for selecting mechanisms available in a stream. Method ``filterMechanisms()`` should return a collection with mechanisms available based on:\r\n\r\n1. all registered SASL factories\r\n\r\n2. XMPP session data (from ``XMPPResourceConnection`` class)\r\n\r\nThe default selector returns mechanisms from all mechanism factories registered in ``sasl-provider`` ``(TigaseSaslProvider)``.\r\n\r\nIt is possible to use a custom selector by specifying it’s class int the ``config.tdsl`` file:\r\n\r\n.. code::\r\n\r\n   'sess-man' () {\r\n       'sasl-provider' () {\r\n           'mechanism-selector'(class: com.example.OwnSelector) {}\r\n       }\r\n   }\r\n\r\nLogging/Authentication\r\n^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nAfter the XMPP stream is opened by a client, the server checks which SASL mechanisms are available for the XMPP session. Depending on whether the stream is encrypted or not, depending on the domain, the server can present different available authentication mechanisms. ``MechanismSelector`` is responsible for choosing mechanisms. List of allowed mechanisms is stored in the XMPP session object.\r\n\r\nWhen the client/user begins the authentication procedure it uses one particular mechanism. It must use one of the mechanisms provided by the server as available for this session. The server checks whether mechanisms used by the client is on the list of allowed mechanisms. It the check is successful, the server creates ``SaslServer`` class instance and proceeds with exchanging authentication information. Authentication data is different depending on the mechanism used.\r\n\r\nWhen the SASL authentication is completed without any error, Tigase server should have authorized user name or authorized BareJID. In the first case, the server automatically builds user’s JID based on the domain used in the stream opening element in ``to`` attribute.\r\n\r\nIf, after a successful authentication, method call: ``getNegotiatedProperty(\"IS_ANONYMOUS\")`` returns ``Boolean.TRUE`` then the user session is marked as anonymous. For valid and registered users this can be used for cases when we do not want to load any user data such as roster, vcard, privacy lists and so on. This is a performance and resource usage implication and can be useful for use cases such as support chat. The authorization is performed based on the client database but we do not need to load any XMPP specific data for the user’s session.\r\n\r\nMore details about implementation can be found in the :ref:`custom mechanisms development<cmd>` section.\r\n\r\n.. _cmd:\r\n\r\nCustom Mechanisms Development\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**Mechanism**\r\n~~~~~~~~~~~~~~~~~~\r\n\r\n``getAuthorizationID()`` method from ``SaslServer`` class **should** return bare JID authorized user. In case that the method returns only user name such as **romeo** for example, the server automatically appends domain name to generate a valid BareJID: *romeo@example.com*. In case the method returns a full, valid BareJID, the server does not change anything.\r\n\r\n``handleLogin()`` method from ``SessionManagerHandler`` will be called with user’s Bare JID provided by ``getAuthorizationID()`` (or created later using stream domain name).\r\n\r\n**CallbackHandler**\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nFor each session authorization, the server creates a new and separate empty handler. Factory which creates handler instance allows to inject different objects to the handler, depending on interfaces implemented by the handler class:\r\n\r\n-  ``AuthRepositoryAware`` - injects ``AuthRepository;``\r\n\r\n-  ``DomainAware`` - injects domain name within which the user attempts to authenticate\r\n\r\n-  ``NonAuthUserRepositoryAware`` - injects ``NonAuthUserRepository``\r\n\r\nGeneral Remarks\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\n``JabberIqAuth`` used for non-SASL authentication mechanisms uses the same callback as the SASL mechanisms.\r\n\r\nMethods ``auth`` in ``Repository`` interfaces will be deprecated. These interfaces will be treated as user details providers only. There will be new methods available which will allow for additional login operations on the database such as last successful login recording.\r\n"
  },
  {
    "path": "src/main/restructured/Tigase_Development/Server_Compilation.rst",
    "content": "Server Compilation\r\n====================\r\n\r\nTigase XMPP Server Project uses Maven for compilation. For details on Maven and it’s use, please see the :ref:`Maven Guide.<usingMaven>`\r\n\r\nDistribution Packages\r\n-----------------------\r\n\r\nOnce Compiled, Tigase creates two separate distribution archives:\r\n\r\n-  **-dist** is a minimal version containing only tigase-server, tigase-xmltools and tigase-utils, MUC, Pubsub, and HTTP.\r\n\r\n-  **-dist-max** is a version containing all additional tigase components as well as dependencies required by those components.\r\n\r\nThey will be available as both zip and tarball.\r\n\r\nBuilding Server and Generating Packages\r\n-------------------------------------------\r\n\r\nServer binary and it’s documentation\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nAfter cloning tigase-server repository:\r\n\r\n.. code:: bash\r\n\r\n   git clone https://repository.tigase.org/git/tigase-server.git\r\n   cd tigase-server\r\n\r\nYou compile server with maven :\r\n\r\n.. code:: bash\r\n\r\n   mvn clean install\r\n\r\nThis will: - Build Tigase XMPP tigase-server jar in tigase-server/target.\r\n\r\nIf you wish to include compilation of the documentation use *distribution* profile:\r\n\r\n.. code:: bash\r\n\r\n   mvn -Pdist clean install\r\n\r\nThis will - compile server binaries. - generate javadoc and manual documentation ``tigase-server/target/_docs`` directory.\r\n\r\nServer distribution packages\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nDistribution building is handled by separate project (`Tigase Server Distribution <https://github.com/tigase/tigase-server-distribution>`__)\r\n\r\nIn order to build distribution packages \\* clone tigase-server-distribution repository:\r\n\r\n.. code:: bash\r\n\r\n   git clone https://git.tigase.tech/tigase-server-distribution\r\n   tigase-server-distribution\r\n\r\nand compile it using maven with *distribution* profile:\r\n\r\n.. code:: bash\r\n\r\n   mvn -Pdist clean install\r\n\r\nThis will:\r\n\r\n-  compile all documentation sources (including dependencies) and place them in ``tigase-server-distribution/target/_docs`` directory\r\n\r\n-  download all dependencies in defined versions and put them in ``tigase-server-distribution/target/dist/jars/`` directory.\r\n\r\n-  create both types of distribution packages (-dist and -dist-max) and place them in ``tigase-server-distribution/target/_dist/`` directory.\r\n\r\nRunning Server\r\n-------------------\r\n\r\nAfterwards you can run the server with the regular shell script from within ``server`` module:\r\n\r\n.. code:: bash\r\n\r\n   cd server\r\n   ./scripts/tigase.sh start etc/tigase.conf\r\n\r\nPlease bear in mind, that you need to provide correct setup in etc/config.tdsl configuration files for the server to work correctly."
  },
  {
    "path": "src/main/restructured/Tigase_Development/Tests.rst",
    "content": "Tests\r\n==========\r\n\r\nTests\r\n-------------------\r\n\r\nTests are very important part of Tigase server development process.\r\n\r\nEach release goes through fully automated testing process. All server functions are considered implemented only when they pass the testing cycle. Tigase test suite is used for all our automatic tests which allows to define different test scenarios.\r\n\r\nThere is no tweaking on databases for tests. All databases are installed in a standard way and run with default settings. Databases are cleared each time before the test cycle starts.\r\n\r\nThere are no modifications needed to be made to Tigase’s configuration file as well. All tests are performed on a default configuration generated by the configuration wizards.\r\n\r\nThe server is tested in all supported environments:\r\n\r\n1. **XMLDB** - tests with built-in simple XML database. This is a simple and efficient solution for small installations. It is recommended for services with up to 100 user accounts although it has been successfully tested with 10,000 user accounts.\r\n\r\n2. **MySQL** - tests with a `MySQL <http://www.mysql.com/>`__ database. Much slower than XMLDB but may handle many more user accounts.\r\n\r\n3. **PostgreSQL** - tests with a `PostgreSQL <http://www.postgresql.org/>`__ database. Again it is much slower than XMLDB but may handle much more user accounts. This is basically exactly the same code as for MySQL database (SQL Connector) but tests are executed to make sure the code is compatible with all supported SQL databases and to compare performance.\r\n\r\n4. **Distributed** - is a test for distributed installation where c2s and s2s components run on separated machine which connects using external an component protocol (`XEP-0114 <http://www.xmpp.org/extensions/xep-0114.html>`__) to another machine with SessionManager running.\r\n\r\n\r\nFunctional Tests\r\n^^^^^^^^^^^^^^^^^^^^\r\n\r\nBasic checking to see if all the functions work at correctly. These tests are performed every time the code is sent to source repository.\r\n\r\n+------------+------------------------------------------------------------------+------------------------------------------------------------------+------------------------------------------------------------------+---------------------------------------------------------------------+\r\n| Version    | XMLDB                                                            | MySQL                                                            | PGSQL                                                            | Distributed                                                         |\r\n+------------+------------------------------------------------------------------+------------------------------------------------------------------+------------------------------------------------------------------+---------------------------------------------------------------------+\r\n| 3.3.2-b889 | `00:00:12 <tests/3.3.2-b889/func/xmldb/functional-tests.html>`__ | `00:00:17 <tests/3.3.2-b889/func/mysql/functional-tests.html>`__ | `00:00:17 <tests/3.3.2-b889/func/pgsql/functional-tests.html>`__ | none                                                                |\r\n+------------+------------------------------------------------------------------+------------------------------------------------------------------+------------------------------------------------------------------+---------------------------------------------------------------------+\r\n| 3.3.2-b880 | `00:00:13 <tests/3.3.2-b880/func/xmldb/functional-tests.html>`__ | `00:00:15 <tests/3.3.2-b880/func/mysql/functional-tests.html>`__ | `00:00:15 <tests/3.3.2-b880/func/pgsql/functional-tests.html>`__ | None                                                                |\r\n+------------+------------------------------------------------------------------+------------------------------------------------------------------+------------------------------------------------------------------+---------------------------------------------------------------------+\r\n| 3.0.2-b700 | `00:00:22 <tests/3.0.2-b700/func/xmldb/functional-tests.html>`__ | `00:00:24 <tests/3.0.2-b700/func/mysql/functional-tests.html>`__ | `00:00:25 <tests/3.0.2-b700/func/pgsql/functional-tests.html>`__ | `00:00:25 <tests/3.0.2-b700/func/sm-mysql/functional-tests.html>`__ |\r\n+------------+------------------------------------------------------------------+------------------------------------------------------------------+------------------------------------------------------------------+---------------------------------------------------------------------+\r\n| 2.9.5-b606 | `00:00:22 <tests/2.9.5-b606/func/xmldb/functional-tests.html>`__ | `00:00:24 <tests/2.9.5-b606/func/mysql/functional-tests.html>`__ | `00:00:24 <tests/2.9.5-b606/func/pgsql/functional-tests.html>`__ | `00:00:24 <tests/2.9.5-b606/func/sm-mysql/functional-tests.html>`__ |\r\n+------------+------------------------------------------------------------------+------------------------------------------------------------------+------------------------------------------------------------------+---------------------------------------------------------------------+\r\n| 2.9.3-b548 | `00:00:22 <tests/2.9.3-b548/func/xmldb/functional-tests.html>`__ | `00:00:23 <tests/2.9.3-b548/func/mysql/functional-tests.html>`__ | `00:00:25 <tests/2.9.3-b548/func/pgsql/functional-tests.html>`__ | `00:00:25 <tests/2.9.3-b548/func/sm-mysql/functional-tests.html>`__ |\r\n+------------+------------------------------------------------------------------+------------------------------------------------------------------+------------------------------------------------------------------+---------------------------------------------------------------------+\r\n| 2.9.1-b528 | `00:00:21 <tests/2.9.1-b528/func/xmldb/functional-tests.html>`__ | `00:00:23 <tests/2.9.1-b528/func/mysql/functional-tests.html>`__ | `00:00:24 <tests/2.9.1-b528/func/pgsql/functional-tests.html>`__ | `00:00:25 <tests/2.9.1-b528/func/sm-mysql/functional-tests.html>`__ |\r\n+------------+------------------------------------------------------------------+------------------------------------------------------------------+------------------------------------------------------------------+---------------------------------------------------------------------+\r\n| 2.8.6-b434 | `00:00:21 <tests/2.8.6-b434/func/xmldb/functional-tests.html>`__ | `00:00:24 <tests/2.8.6-b434/func/mysql/functional-tests.html>`__ | `00:00:24 <tests/2.8.6-b434/func/pgsql/functional-tests.html>`__ | `00:00:25 <tests/2.8.6-b434/func/sm-mysql/functional-tests.html>`__ |\r\n+------------+------------------------------------------------------------------+------------------------------------------------------------------+------------------------------------------------------------------+---------------------------------------------------------------------+\r\n| 2.8.5-b422 | `00:00:21 <tests/2.8.5-b422/func/xmldb/functional-tests.html>`__ | `00:00:24 <tests/2.8.5-b422/func/mysql/functional-tests.html>`__ | `00:00:24 <tests/2.8.5-b422/func/pgsql/functional-tests.html>`__ | `00:00:26 <tests/2.8.5-b422/func/sm-mysql/functional-tests.html>`__ |\r\n+------------+------------------------------------------------------------------+------------------------------------------------------------------+------------------------------------------------------------------+---------------------------------------------------------------------+\r\n| 2.8.3-b409 | `00:00:27 <tests/2.8.3-b409/func/xmldb/functional-tests.html>`__ | `00:00:29 <tests/2.8.3-b409/func/mysql/functional-tests.html>`__ | `00:00:29 <tests/2.8.3-b409/func/pgsql/functional-tests.html>`__ | `00:00:32 <tests/2.8.3-b409/func/sm-mysql/functional-tests.html>`__ |\r\n+------------+------------------------------------------------------------------+------------------------------------------------------------------+------------------------------------------------------------------+---------------------------------------------------------------------+\r\n| 2.7.2-b378 | `00:00:30 <tests/2.7.2-b378/func/xmldb/functional-tests.html>`__ | `00:00:34 <tests/2.7.2-b378/func/mysql/functional-tests.html>`__ | `00:00:33 <tests/2.7.2-b378/func/pgsql/functional-tests.html>`__ | `00:00:35 <tests/2.7.2-b378/func/sm-mysql/functional-tests.html>`__ |\r\n+------------+------------------------------------------------------------------+------------------------------------------------------------------+------------------------------------------------------------------+---------------------------------------------------------------------+\r\n| 2.6.4-b300 | `00:00:30 <tests/2.6.4-b300/func/xmldb/functional-tests.html>`__ | `00:00:32 <tests/2.6.4-b300/func/mysql/functional-tests.html>`__ | `00:00:35 <tests/2.6.4-b300/func/pgsql/functional-tests.html>`__ | `00:00:39 <tests/2.6.4-b300/func/sm-mysql/functional-tests.html>`__ |\r\n+------------+------------------------------------------------------------------+------------------------------------------------------------------+------------------------------------------------------------------+---------------------------------------------------------------------+\r\n| 2.6.4-b295 | `00:00:29 <tests/2.6.4-b295/func/xmldb/functional-tests.html>`__ | `00:00:32 <tests/2.6.4-b295/func/mysql/functional-tests.html>`__ | `00:00:45 <tests/2.6.4-b295/func/pgsql/functional-tests.html>`__ | `00:00:36 <tests/2.6.4-b295/func/sm-mysql/functional-tests.html>`__ |\r\n+------------+------------------------------------------------------------------+------------------------------------------------------------------+------------------------------------------------------------------+---------------------------------------------------------------------+\r\n| 2.6.0-b287 | `00:00:31 <tests/2.6.0-b287/func/xmldb/functional-tests.html>`__ | `00:00:34 <tests/2.6.0-b287/func/mysql/functional-tests.html>`__ | `00:00:47 <tests/2.6.0-b287/func/pgsql/functional-tests.html>`__ | `00:00:43 <tests/2.6.0-b287/func/sm-mysql/functional-tests.html>`__ |\r\n+------------+------------------------------------------------------------------+------------------------------------------------------------------+------------------------------------------------------------------+---------------------------------------------------------------------+\r\n| 2.5.0-b279 | `00:00:30 <tests/2.5.0-b279/func/xmldb/functional-tests.html>`__ | `00:00:34 <tests/2.5.0-b279/func/mysql/functional-tests.html>`__ | `00:00:45 <tests/2.5.0-b279/func/pgsql/functional-tests.html>`__ | `00:00:43 <tests/2.5.0-b279/func/sm-mysql/functional-tests.html>`__ |\r\n+------------+------------------------------------------------------------------+------------------------------------------------------------------+------------------------------------------------------------------+---------------------------------------------------------------------+\r\n| 2.4.0-b263 | `00:00:29 <tests/2.4.0-b263/func/xmldb/functional-tests.html>`__ | `00:00:33 <tests/2.4.0-b263/func/mysql/functional-tests.html>`__ | `00:00:45 <tests/2.4.0-b263/func/pgsql/functional-tests.html>`__ | `00:00:44 <tests/2.4.0-b263/func/sm-mysql/functional-tests.html>`__ |\r\n+------------+------------------------------------------------------------------+------------------------------------------------------------------+------------------------------------------------------------------+---------------------------------------------------------------------+\r\n| 2.3.4-b226 | None                                                             | `00:00:48 <tests/functional-tests.html>`__                       | None                                                             | None                                                                |\r\n+------------+------------------------------------------------------------------+------------------------------------------------------------------+------------------------------------------------------------------+---------------------------------------------------------------------+\r\n\r\nPerformance Tests\r\n^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nChecking to see whether the function performs well enough.\r\n\r\n+------------+-------------------------------------------------------------------+-------------------------------------------------------------------+-------------------------------------------------------------------+----------------------------------------------------------------------+\r\n| Version    | XMLDB                                                             | MySQL                                                             | PGSQL                                                             | Distributed                                                          |\r\n+------------+-------------------------------------------------------------------+-------------------------------------------------------------------+-------------------------------------------------------------------+----------------------------------------------------------------------+\r\n| 3.3.2-b889 | `00:12:17 <tests/3.3.2-b889/perf/xmldb/performance-tests.html>`__ | `00:13:42 <tests/3.3.2-b889/perf/mysql/performance-tests.html>`__ | `00:17:10 <tests/3.3.2-b889/perf/pgsql/performance-tests.html>`__ | none                                                                 |\r\n+------------+-------------------------------------------------------------------+-------------------------------------------------------------------+-------------------------------------------------------------------+----------------------------------------------------------------------+\r\n| 3.3.2-b880 | `00:13:39 <tests/3.3.2-b880/perf/xmldb/performance-tests.html>`__ | `00:14:09 <tests/3.3.2-b880/perf/mysql/performance-tests.html>`__ | `00:17:39 <tests/3.3.2-b880/perf/pgsql/performance-tests.html>`__ | None                                                                 |\r\n+------------+-------------------------------------------------------------------+-------------------------------------------------------------------+-------------------------------------------------------------------+----------------------------------------------------------------------+\r\n| 3.0.2-b700 | `00:10:26 <tests/3.0.2-b700/perf/xmldb/performance-tests.html>`__ | `00:11:00 <tests/3.0.2-b700/perf/mysql/performance-tests.html>`__ | `00:12:08 <tests/3.0.2-b700/perf/pgsql/performance-tests.html>`__ | `00:24:05 <tests/3.0.2-b700/perf/sm-mysql/performance-tests.html>`__ |\r\n+------------+-------------------------------------------------------------------+-------------------------------------------------------------------+-------------------------------------------------------------------+----------------------------------------------------------------------+\r\n| 2.9.5-b606 | `00:09:54 <tests/2.9.5-b606/perf/xmldb/performance-tests.html>`__ | `00:11:18 <tests/2.9.5-b606/perf/mysql/performance-tests.html>`__ | `00:37:08 <tests/2.9.5-b606/perf/pgsql/performance-tests.html>`__ | `00:16:20 <tests/2.9.5-b606/perf/sm-mysql/performance-tests.html>`__ |\r\n+------------+-------------------------------------------------------------------+-------------------------------------------------------------------+-------------------------------------------------------------------+----------------------------------------------------------------------+\r\n| 2.9.3-b548 | `00:10:00 <tests/2.9.3-b548/perf/xmldb/performance-tests.html>`__ | `00:11:29 <tests/2.9.3-b548/perf/mysql/performance-tests.html>`__ | `00:36:43 <tests/2.9.3-b548/perf/pgsql/performance-tests.html>`__ | `00:16:47 <tests/2.9.3-b548/perf/sm-mysql/performance-tests.html>`__ |\r\n+------------+-------------------------------------------------------------------+-------------------------------------------------------------------+-------------------------------------------------------------------+----------------------------------------------------------------------+\r\n| 2.9.1-b528 | `00:09:46 <tests/2.9.1-b528/perf/xmldb/performance-tests.html>`__ | `00:11:15 <tests/2.9.1-b528/perf/mysql/performance-tests.html>`__ | `00:36:12 <tests/2.9.1-b528/perf/pgsql/performance-tests.html>`__ | `00:16:36 <tests/2.9.1-b528/perf/sm-mysql/performance-tests.html>`__ |\r\n+------------+-------------------------------------------------------------------+-------------------------------------------------------------------+-------------------------------------------------------------------+----------------------------------------------------------------------+\r\n| 2.8.6-b434 | `00:10:02 <tests/2.8.6-b434/perf/xmldb/performance-tests.html>`__ | `00:11:45 <tests/2.8.6-b434/perf/mysql/performance-tests.html>`__ | `00:36:36 <tests/2.8.6-b434/perf/pgsql/performance-tests.html>`__ | `00:17:36 <tests/2.8.6-b434/perf/sm-mysql/performance-tests.html>`__ |\r\n+------------+-------------------------------------------------------------------+-------------------------------------------------------------------+-------------------------------------------------------------------+----------------------------------------------------------------------+\r\n| 2.8.5-b422 | `00:12:37 <tests/2.8.5-b422/perf/xmldb/performance-tests.html>`__ | `00:14:40 <tests/2.8.5-b422/perf/mysql/performance-tests.html>`__ | `00:38:59 <tests/2.8.5-b422/perf/pgsql/performance-tests.html>`__ | `00:21:40 <tests/2.8.5-b422/perf/sm-mysql/performance-tests.html>`__ |\r\n+------------+-------------------------------------------------------------------+-------------------------------------------------------------------+-------------------------------------------------------------------+----------------------------------------------------------------------+\r\n| 2.8.3-b409 | `00:12:32 <tests/2.8.3-b409/perf/xmldb/performance-tests.html>`__ | `00:14:26 <tests/2.8.3-b409/perf/mysql/performance-tests.html>`__ | `00:37:57 <tests/2.8.3-b409/perf/pgsql/performance-tests.html>`__ | `00:21:26 <tests/2.8.3-b409/perf/sm-mysql/performance-tests.html>`__ |\r\n+------------+-------------------------------------------------------------------+-------------------------------------------------------------------+-------------------------------------------------------------------+----------------------------------------------------------------------+\r\n| 2.7.2-b378 | `00:12:28 <tests/2.7.2-b378/perf/xmldb/performance-tests.html>`__ | `00:14:57 <tests/2.7.2-b378/perf/mysql/performance-tests.html>`__ | `00:37:09 <tests/2.7.2-b378/perf/pgsql/performance-tests.html>`__ | `00:22:20 <tests/2.7.2-b378/perf/sm-mysql/performance-tests.html>`__ |\r\n+------------+-------------------------------------------------------------------+-------------------------------------------------------------------+-------------------------------------------------------------------+----------------------------------------------------------------------+\r\n| 2.6.4-b300 | `00:12:46 <tests/2.6.4-b300/perf/xmldb/performance-tests.html>`__ | `00:14:59 <tests/2.6.4-b300/perf/mysql/performance-tests.html>`__ | `00:36:56 <tests/2.6.4-b300/perf/pgsql/performance-tests.html>`__ | `00:35:00 <tests/2.6.4-b300/perf/sm-mysql/performance-tests.html>`__ |\r\n+------------+-------------------------------------------------------------------+-------------------------------------------------------------------+-------------------------------------------------------------------+----------------------------------------------------------------------+\r\n| 2.6.4-b295 | `00:12:23 <tests/2.6.4-b295/perf/xmldb/performance-tests.html>`__ | `00:14:59 <tests/2.6.4-b295/perf/mysql/performance-tests.html>`__ | `00:42:24 <tests/2.6.4-b295/perf/pgsql/performance-tests.html>`__ | `00:30:18 <tests/2.6.4-b295/perf/sm-mysql/performance-tests.html>`__ |\r\n+------------+-------------------------------------------------------------------+-------------------------------------------------------------------+-------------------------------------------------------------------+----------------------------------------------------------------------+\r\n| 2.6.0-b287 | `00:13:50 <tests/2.6.0-b287/perf/xmldb/performance-tests.html>`__ | `00:16:53 <tests/2.6.0-b287/perf/mysql/performance-tests.html>`__ | `00:48:17 <tests/2.6.0-b287/perf/pgsql/performance-tests.html>`__ | `00:49:06 <tests/2.6.0-b287/perf/sm-mysql/performance-tests.html>`__ |\r\n+------------+-------------------------------------------------------------------+-------------------------------------------------------------------+-------------------------------------------------------------------+----------------------------------------------------------------------+\r\n| 2.5.0-b279 | `00:13:29 <tests/2.5.0-b279/perf/xmldb/performance-tests.html>`__ | `00:16:58 <tests/2.5.0-b279/perf/mysql/performance-tests.html>`__ | `00:47:15 <tests/2.5.0-b279/perf/pgsql/performance-tests.html>`__ | `00:41:52 <tests/2.5.0-b279/perf/sm-mysql/performance-tests.html>`__ |\r\n+------------+-------------------------------------------------------------------+-------------------------------------------------------------------+-------------------------------------------------------------------+----------------------------------------------------------------------+\r\n| 2.4.0-b263 | `00:13:20 <tests/2.4.0-b263/perf/xmldb/performance-tests.html>`__ | `00:16:21 <tests/2.4.0-b263/perf/mysql/performance-tests.html>`__ | `00:43:56 <tests/2.4.0-b263/perf/pgsql/performance-tests.html>`__ | `00:42:08 <tests/2.4.0-b263/perf/sm-mysql/performance-tests.html>`__ |\r\n+------------+-------------------------------------------------------------------+-------------------------------------------------------------------+-------------------------------------------------------------------+----------------------------------------------------------------------+\r\n| 2.3.4-b226 | None                                                              | `01:23:30 <tests/performance-tests.html>`__                       | None                                                              | None                                                                 |\r\n+------------+-------------------------------------------------------------------+-------------------------------------------------------------------+-------------------------------------------------------------------+----------------------------------------------------------------------+\r\n\r\nStability Tests\r\n^^^^^^^^^^^^^^^^\r\n\r\nChecking to see whether the function behaves well in long term run. It must handle hundreds of requests a second in a several hour server run.\r\n\r\n+------------+-------+-------------------------------------------+-------+-------------+\r\n| Version    | XMLDB | MySQL                                     | PGSQL | Distributed |\r\n+------------+-------+-------------------------------------------+-------+-------------+\r\n| 2.3.4-b226 | None  | `16:06:31 <tests/stability-tests.html>`__ | None  | None        |\r\n+------------+-------+-------------------------------------------+-------+-------------+\r\n\r\nTigase Test Suite\r\n---------------------\r\n\r\nTigase Test Suite is an engine which allows you to run tests. Essentially it just executes **TestCase** implementations. The tests may depend on other tests which means they are executed in specific order. For example authentication test is executed after the stream open test which in turn is executed after network socket connection test.\r\n\r\nEach **TestCase** implementation may have it’s own set of specific parameters. There is a set of common parameters which may be applied to any **TestCase**. As an example of the common parameter you can take **-loop = 10** which specified that the **TestCase** must be executed 10 times. The test specific parameter might be **-user-name = tester** which may set the user name for authentication test.\r\n\r\nThe engine is very generic and allows you to write any kind of tests but for the Tigase projects the current TestCase implementations mimic an XMPP client and are designed to test XMPP servers.\r\n\r\nThe suite contains a kind of scripting language which allows you to combine test cases into a test scenarios. The test scenario may contain full set of functional tests for example, another test scenario may contain performance tests and so on.\r\n\r\nRunning Tigase Test Suite (TTS)\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTo obtain TTS, you will first need to clone the repository\r\n\r\n::\r\n\r\n   git clone https://repository.tigase.org/git/tigase-testsuite.git\r\n\r\nOnce cloning is finished, navigate to the TTS root directory and compile with maven:\r\n\r\n::\r\n\r\n   mvn clean install\r\n\r\nMaven will compile TTS and place jars in the necessary locations. From the same directory, you can begin running TTS using the following command:\r\n\r\n::\r\n\r\n   ./scripts/all-tests-runner.sh\r\n\r\nYou should see the following, which outlines the possible options to customize your test run\r\n\r\n.. code:: bash\r\n\r\n   Run selected or all tests for Tigase server\r\n   ----\r\n   Author: Artur Hefczyc <artur_hefczyc@vnu.co.uk>\r\n   Version: 2.0.0\r\n   ----\r\n     --help|-h This help message\r\n     --func [mysql|pgsql|derby|mssql|mongodb]\r\n                 Run all functional tests for a single database configuration\r\n     --lmem [mysql|pgsql|derby|mssql|mongodb]\r\n                 Run low memory tests for a single database configuration\r\n     --perf [mysql|pgsql|derby|mssql|mongodb]\r\n                 Run all performance tests for a single database configuration\r\n     --stab [mysql|pgsql|derby|mssql|mongodb]\r\n                 Run all stability tests for a single database\r\n                 configuration\r\n     --func-all  Run all functional tests for all database\r\n                 configurations\r\n     --lmem-all  Run low memory tests for all database\r\n                 configurations\r\n     --perf-all  Run all performance tests for all database\r\n                 configurations\r\n     --stab-all  Run all stability tests for all database\r\n                 configurations\r\n     --all-tests Run all functionality and performance tests for\r\n                 database configurations\r\n     --single test_file.cot\r\n     --other script_file.xmpt\r\n   ----\r\n     Special parameters only at the beginning of the parameters list\r\n     --debug|-d                 Turns on debug mode\r\n     --skip-db-relad|-no-db     Turns off reloading database\r\n     --skip-server|-no-serv     Turns off Tigase server start\r\n     --small-mem|-sm            Run in small memory mode\r\n   -----------\r\n     Other possible parameters are in following order:\r\n     [server-dir] [server-ip]\r\n\r\nCustomizing Tigase Test Suite\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nYou may run the tests from a command line like above, however you may create and edit the /scripts/tests-runner-settings.sh file to fit your Tigase installation and avoid having to have long complex commands as this template shows:\r\n\r\n.. code::\r\n\r\n   #!/bin/bash\r\n\r\n   func_rep=\"func-rep.html\"\r\n   perf_rep=\"perf-rep.html\"\r\n   db_name=\"tigasetest\"\r\n   db_user=\"tigase\"\r\n   db_pass=\"tigase\"\r\n   root_user=\"root\"\r\n   root_pass=\"root\"\r\n\r\n   TESTS=(\"derby\" \"mysql\" \"pgsql\" \"mssql\")\r\n   IPS=(\"127.0.0.1\" \"127.0.0.1\" \"127.0.0.1\" \"127.0.0.1\")\r\n\r\n   server_timeout=10\r\n\r\n   server_dir=\"/home/tigase/tigase-server\"\r\n   database=\"derby\"\r\n   #database=\"mysql\"\r\n   server_ip=\"127.0.0.1\"\r\n\r\n   MS_MEM=100\r\n   MX_MEM=1000\r\n\r\n   SMALL_MS_MEM=10\r\n   SMALL_MX_MEM=50\r\n\r\nThis will allow you to maintain identical settings through multiple runs of TTS. See the next section for learning how the scripting language works and how you can create and run your own custom tests.\r\n\r\nTest Suite Scripting Language\r\n----------------------------------\r\n\r\nThe test suite contains scripting language which allows you to combine test cases into a test scenarios. On the lowest level, however the language is designed to allow you to describe the test by setting test parameters, test comments, identification and so on.\r\n\r\nLet’s look at the example test description.\r\n\r\n.. code::\r\n\r\n   Short name@test-id-1;test-id-2: Short description for the test case\r\n   {\r\n    -loop = 10\r\n    -user-name = Frank\r\n    # This is a comment which is ignored\r\n   }\r\n   >> Long, detailed description of the test case <<\r\n\r\nMeaning of all elements:\r\n\r\n1. **Short name** is any descriptive name you want. It doesn’t need to be unique, just something which tells you what this test is about. @ is a separator between the short name and the test ids.\r\n\r\n2. **test-id-1;test-id-2** is a semicolon separated of the test cases IDs. The tests cases are executed in the listed order. And listing them there means that the test-id-2 depends on test-id-1. Normally you don’t have to list all the dependencies because all mandatory dependencies are included automatically. Which means if you have an authentication test case the suite adds the network socket connection and stream opening tests automatically. Sometimes however, there are dependencies which are optional or multiple mandatory dependencies and you need to select which one has to be executed. As a good example is the authentications test case. There are many authentication tests: PLAIN-AUTH, SASL-DIGESTMD5, SASL-PLAIN, DIGEST-AUTH and they are all mandatory for most of other tests like roster, presence and so on. One of the authentication tests is a default dependency but if you put on the list different authentication it will be used instead of default one.\r\n\r\n3. **:** is a separator between test cases ids list and the short test description.\r\n\r\n4. **Short test description** is placed between : - colon and opening **{** - curly bracket. This is usually quite brief, single line test description.\r\n\r\n5. **{ }** curly brackets contain all the test parameters, like how many times the test has to be executed or run the test in a separate thread, user name, host IP address for the network connection and many others.\r\n\r\n6. **>> <<** inside the double greater than and double less than you put a very long, multiple line test description.\r\n\r\nAs for the testing script between open curly brackets { and close one } you can put all the test case parameters you wish. The format for it is:\r\n\r\n**-parameter-name = value**\r\n\r\nParameter names always start with **-**. Note, some parameters don’t require any value. They can exist on their own without any value assigned:\r\n\r\n**-debug-on-error**\r\n\r\nThis imitates if you were to put **yes** or **true** as the value.\r\n\r\nThe scripting language includes also support for variables which can be assigned any value and used multiple times later on. You assign a value to the variable the same way as you assign it to the parameter:\r\n\r\n**$(variable-name) = value**\r\n\r\nThe variable name must be always enclosed with brackets **()** and start with **$**.\r\n\r\nThe value may be enclosed within double quotes **\"\"** or double quotes may be omitted. If this is a simple string like a number or character string consisting only of digits, letters, underscore **\\_** and hyphen **-** then you can omit double quotes otherwise you must enclose the value.\r\n\r\nThe test case descriptions can be nested inside other test case descriptions. Nested test case descriptions inherit parameters and variables from outer test case description.\r\n\r\nWriting Tests for Plugins\r\n-----------------------------\r\n\r\nYou can write tests in a simple text file which is loaded during test suite runtime.\r\n\r\nYou simply specify what should be send to the server and what response should be expected from the server. No need to write Java code and recompile the whole test suite for new tests. It means new test cases can be now written easily and quickly which hopefully means more detailed tests for the server.\r\n\r\nHow it works:\r\n\r\nLet’s take `XEP-0049 <http://www.xmpp.org/extensions/xep-0049.html>`__ Private XML Storage. Looking into the spec we can see the first example:\r\n\r\nExample: Client Stores Private Data\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n**CLIENT:**\r\n\r\n.. code:: xml\r\n\r\n   <iq type=\"set\" id=\"1001\">\r\n     <query xmlns=\"jabber:iq:private\">\r\n       <exodus xmlns=\"exodus:prefs\">\r\n         <defaultnick>Hamlet</defaultnick>\r\n       </exodus>\r\n     </query>\r\n   </iq>\r\n\r\n**SERVER:**\r\n\r\n.. code:: xml\r\n\r\n   <iq type=\"result\" id=\"1001\"/>\r\n\r\nThis is enough for the first simple test. I have to create text file ``JabberIqPrivate.test`` looking like this:\r\n\r\n::\r\n\r\n   send: {\r\n\r\n   <iq type=\"set\" id=\"1001\">\r\n     <query xmlns=\"jabber:iq:private\">\r\n       <exodus xmlns=\"exodus:prefs\">\r\n         <defaultnick>Hamlet</defaultnick>\r\n       </exodus>\r\n     </query>\r\n   </iq>\r\n   }\r\n\r\n   expect: {\r\n   <iq type=\"result\" id=\"1001\"/>\r\n   }\r\n\r\nAnd now I can execute the test:\r\n\r\n.. code:: bash\r\n\r\n   testsuite $ ./scripts/all-tests-runner.sh --single JabberIqPrivate.test\r\n\r\n   Tigase server home directory: ../server\r\n   Version: 2.8.5-b422\r\n   Database:         xmldb\r\n   Server IP:        127.0.0.1\r\n   Extra parameters: JabberIqPrivate.test\r\n   Starting Tigase:\r\n   Tigase running pid=6751\r\n\r\n   Running: 2.8.5-b422-xmldb test, IP 127.0.0.1...\r\n   Script name: scripts/single-xmpp-test.xmpt\r\n   Common test:  Common test  ...        failure!\r\n   FAILURE,  (Received result doesnt match expected result.,\r\n   Expected one of: [<iq id=\"1001\" type=\"result\"/>],\r\n   received:\r\n   [<iq id=\"1001\" type=\"error\">\r\n     <query xmlns=\"jabber:iq:private\">\r\n       <exodus xmlns=\"exodus:prefs\">\r\n         <defaultnick>Hamlet</defaultnick>\r\n       </exodus>\r\n     </query>\r\n     <error type=\"cancel\">\r\n       <feature-not-implemented xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>\r\n       <text xml:lang=\"en\" xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\">\r\n         Feature not supported yet.</text>\r\n     </error>\r\n   </iq>]),\r\n\r\n   Total: 100ms\r\n   Test time: 00:00:02\r\n   Shutting down Tigase: 6751\r\n\r\nIf I just started working on this XEP and there is no code on the server side, the result is perfectly expected although maybe this is not what we want. After a while of working on the server code I can execute the test once again:\r\n\r\n.. code:: bash\r\n\r\n   testsuite $ ./scripts/all-tests-runner.sh --single JabberIqPrivate.test\r\n\r\n   Tigase server home directory: ../server\r\n\r\n   Version: 2.8.5-b422\r\n\r\n   Database:         xmldb\r\n\r\n   Server IP:        127.0.0.1\r\n\r\n   Extra parameters: JabberIqPrivate.test\r\n\r\n   Starting Tigase:\r\n\r\n   Tigase running pid=6984\r\n\r\n   Running: 2.8.5-b422-xmldb test, IP 127.0.0.1...\r\n\r\n   Script name: scripts/single-xmpp-test.xmpt\r\n\r\n   Common test:  Common test  ... success,  Total: 40ms\r\n\r\n   Test time: 00:00:01\r\n\r\n   Shutting down Tigase: 6984\r\n\r\nThis is it. The result we want in a simple and efficient way. We can repeat it as many times we want which is especially important in longer term trials. Every time we change the server code we can re-run tests to make sure we get correct responses from the server.\r\n\r\nYou can have a look in the current build, with more complete test cases, file for `JabberIqPrivate <https://github.com/tigase/tigase-testsuite/tree/master/tests/data/JabberIqPrivate.cot>`__.\r\n\r\nNow my server tests are no longer outdated. Of course not all cases are so simple. Some XEPs require calculations to be done before stanza is sent or to compare received results. A good example for this case is user authentication like SASL and even NON-SASL. But still, there are many cases which can be covered by simple tests: roster management, privacy lists management, vCard, private data storage and so on.\r\n\r\nTest Case Parameters Description\r\n--------------------------------------\r\n\r\nThere is long list of parameters which can be applied to any test case. Here is the description of all possible parameters which can be used to build test scenarios.\r\n\r\nThere is long list of parameters which can be applied to any test case. Here is the description of all possible parameters which can be used to build test scenarios.\r\n\r\nTest Report Configuration\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThere are test report parameters which must be set in the main script file in order to generate HTML report from the test. These parameters have no effect is they are set inside the test case description.\r\n\r\n1. **-version = 2.0.0** sets the test script version. This is used to easily detect incompatibility issues when the test suite loads a script created for more recent version of the suite and may not work properly for this version.\r\n\r\n2. **-output-format = (html \\| html-content)** sets the output format for the test report. There is actually only one format possible right now - HTML. The only difference between these 2 options is that the **html** format creates full HTML page with HTML header and body. The **html-content** format on the other hand creates only what is inside ``<body/>`` element. And is used to embed test result inside other HTML content.\r\n\r\n3. **-output-file = \"report-file.html\"** sets the file name for the test report.\r\n\r\n4. **-output-history = (yes \\| no)** sets logging of the all protocol data sent between test suite and the XMPP server. Normally for functional tests it is recommended to set it to **yes** but for all other tests like performance or load tests it should be set to **no**.\r\n\r\n5. **-history-format = separate-file** sets protocol data logging to a separate file. Currently this is the only possible option.\r\n\r\n6. **-output-cols = (5 \\| 7)** Only valid values are:\r\n\r\n   .. code:: bash\r\n\r\n      5: \"Test name\", \"Result\", \"Test time\", \"Description\" [, \"History\" ]\r\n      7: \"Test name\", \"Result\", \"Total time\", \"OK\", \"Average\", \"Description\" [, \"History\" ]\r\n\r\n7. **-title** = \"The title of the report page\" This parameter sets the test report title which is placed in the HTML page in the ``<title/>`` element as well as in the first page header.\r\n\r\nBasic Test Parameters\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThese parameters can be set on per-test case basis but usually they are set in the main script file to apply them to all test cases.\r\n\r\n1.  **-base-ns = \"jabber:client\"** sets the XML name space used for the XML stream in the XMPP connection. Some test cases can be used to test client to server protocol as well as server to server protocol and possibly different protocols added in the future.\r\n\r\n2.  **-debug** switches debugging mode on. All the communication between the test suite and the server is printed out to the text console and all other debugging information including java exceptions are displayed as well. It is especially useful when some test fails and you want to find out why.\r\n\r\n3.  **-debug-on-error** switches on debugging mode on error detection. Normally debug output generates lots of message which makes the output very hard to read. Especially in the performance tests not only you can read fast scrolling lines of the protocol data but also it slows the test down. This option however turns debugging off if everything is working well and then generates debug output if any test error us detected.\r\n\r\n4.  **-def-auth = (auth-plain \\| auth-digest \\| auth-sasl)** sets the default authentication method for the user connection.\r\n\r\n5.  **-def-stream = (stream-client \\| stream-server \\| stream-component \\| stream-bosh)** sets the connection stream to be tested and the name space for the connection.\r\n\r\n6.  **-host = \"host.name\"** the vhost name the tested server runs for. It may be the real DNS name or just configured for testing purposes hostname. It must match however the server configuration.\r\n\r\n7.  **-keys-file = \"certs/keystore\"** sets the location of the keys store file. No need to touch it.\r\n\r\n8.  **-keys-file-password = keystore** sets the password for the keystore file. Normally you don’t have to touch it.\r\n\r\n9.  **-serverip = \"127.0.0.1\"** defines the XMPP server IP address. You may omit this parameter and then the IP address will be determined automatically based on the server DNS address. However if the DNS address can not be correctly resolved or if you run tests on the localhost you can use this parameter to enforce the IP address.\r\n\r\n10. **-socket-wait = 10000** sets the network socket timeout in milliseconds that is maximum time the test suite will wait for the response from the server. You may want to increase the timeout for some specific tests which require lots of computation or database activity on the server. Normally 10 seconds is enough for most cases.\r\n\r\n11. **-stop-on-fail = true** causes the script to terminate all actions on the first failed test case. It helps diagnosing the server state at the failure point.\r\n\r\n12. **-trust-file = \"certs/client_truststore\"** sets the file name for the client trust store file. No need to change it.\r\n\r\n13. **-trust-file-password = truststore** sets the password for the trust store file. Normally you don’t have to touch it.\r\n\r\n14. **-user-name = tester** sets the user name used for the XMPP connections between the test suite and the XMPP server. It is usually set globally the same for all tests and for some tests like receiving the server configuration you may want to use a different account (with admin permissions). Then you can set a different user for this specific test case.\r\n\r\n15. **-user-pass = tester-password** sets the password for the user used for the XMPP connection between the test suite and the XMPP server.\r\n\r\n16. **-user-resr = resource** sets the user JID resource part for the XMPP connection between the test suite and the XMPP server.\r\n\r\nTest Case Parameters\r\n^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTest parameters which are normally set on per-test case basis and apply only to the test they are set for and all inherited tests. Some of the parameters though are applied only to inherited test cases. Please look in the description below to find more details.\r\n\r\n1.  **-active-connection** is a similar parameter to **-on-one-socket** option. If set the suite doesn’t close the network socket and if the test is run in loop each loop run re-uses the network connection. Unlike in the -on-one-socket mode the whole test is executed on each run including XMPP stream initialization and user authentication. This option is currently not recommended in a normal use. It is useful only to debug the server behavior in very special use cases.\r\n\r\n2.  **-background** executes the test in a separate thread in background and immediately returns control to the test suite program without waiting for the test to complete. Default behavior is to execute all tests sequentially and run next test when previous one has been completed. This parameter however allows to run tests concurrently. This a bit similar option to the **-daemon** parameter. The daemon test/task however is ignored completely and results from the daemon are not collected where the background test is a normal test which is run concurrently with another one or possibly many other tests.\r\n\r\n3.  **-daemon** creates a task running in background in a separate thread. Such a test runs infinitely as a daemon, it is not recorded in the test report and it’s result is not calculated. The purpose of such test/task is to work as a helper for other test cases. A good example of such daemon test is message responder - the test which runs under a different user name and waits for messages and responding to the sender.\r\n\r\n4.  **-delay = 1000** sets the waiting time in milliseconds after the test case is completed. You may use it if you want to introduce short delay between each test cases run in the loop or if you start the helper daemon thread and you have to add the delay to make sure it is ready to work before next real test starts sending requests to the daemon.\r\n\r\n5.  **-expect-type = error** sets the type for a packet expected as a response. Some test cases like message sender expects sometimes response with the same type it has sent the packet ( **chat** ) but in some other cases when it sends a message to a user who has privacy lists set to block messages the response should be with an error. This way we can use the same test cases for testing different responses scenarios.\r\n\r\n6.  **-loop = 10** sets the number of times the test (and all inherited tests) are repeated. You can use a **$(loop)** pseudo-variable to obtain and use the current loop run number. This is useful if you want to run every loop run for a different user name like registering 10 different user accounts. To do this you stick the $(loop) variable to the user name string: **-user-name = \"nick_name_$(loop)\"**.\r\n\r\n7.  **-loop-delay = 10** sets a delay in milliseconds between each individual loop run for the tests which is run multiple times. This is similar parameter to the **-delay** one but the **-delay** option introduces a delay after the whole test (or all loop runs) has been completed. The loop delay options adds waiting time between each run of the looped test.\r\n\r\n8.  **-loop-start = 5** sets the loop starting value. It doesn’t affect number of loop runs in a any way. It only affects the value of the **$(loop)** variable. Let’s say you want to run a load test for the server with 100k concurrent users and you want to run the test from 3 different machines. To make sure each machine uses distinct user accounts you have to set a different **-loop-start** parameter on each to prevent from overlapping.\r\n\r\n9.  **-messages = 10** sets the number of messages to send to the server. This is another way of looping the test. Instead of repeating the whole test with opening network connection, XMPP stream, authentication and so on it causes only to send the message this many times. This parameters is accepted by some test cases only which send messages. For the messages listeners - test cases which is supposed to respond to the messages the number set here specifies how many times the the response must be sent before the test successfully terminates it’s work.\r\n\r\n10. **-multi-thread** option causes to run the test case and all inherited in all levels test cases in separate threads. Normally the test case where you put the parameter doesn’t have a test ID (what you put between '@' and ':' characters so it doesn’t run a test on it’s own. Instead it contains a series of test cases inside which are then run in a separate thread each. This is a key parameter to run tests for many concurrent users. (Not a load tests though.) For example you can see whether the server behaves correctly when 5 simultaneous modifies their roster. The execution time all inherited tests run in a separate threads is added together and also results from each individual test is calculated and added to the total main test results.\r\n\r\n11. **-no-record** is used for kind of configuration tests (tasks) which are used to prepare the XMPP server or database for later tests. As an example can be creation of the test user account which is later on used for the roster tests. Usually you don’t want to include such tests in the test report and using this parameter you essentially exclude the test from the report. The test and the result however shows in the command line output so you can still track what is really going on.\r\n\r\n12. **-on-one-socket** is a modifier for a looped test case. Normally when we switch looping on using **-loop** parameter the suite resets the state, closes the network socket and runs the test from the very beginning including opening network socket, XMPP stream, authentication and so on. This parameter however changes this behavior. The network socket is not closed when the test run is completed (successfully) and next run executes only the last part of the test omitting the XMPP stream initialization, authentication and all others but last. This is useful when you want to send many messages to the server (although this effect may be accomplished using **-messages** parameter as well) or registering many user accounts on the server, unregistering user accounts and any other which might make sense repeating many times.\r\n\r\n13. **-port = 5223** this parameter is similar to the IP address setting and can be also set globally for all tests. Normally however you set it for a selected tests only to check SSL connection. For all other tests default port number is used. Therefore this parameters has been included in this section instead of \"Basic test parameters\".\r\n\r\n14. **-presence** this parameter enables sending initial presence with positive priority after connection and binding the session.\r\n\r\n15. **-repeat-script = 100** and **-repeat-wait = 10** are 2 parameters are specific to the common test cases. (The test cases which reads the test input/output data from the pseudo-xml text file. The first parameter is another variation of test looping. It sets how many times the test has to be repeated. It works very much like the **-on-one-socket** parameter. The only difference is that the common test can preserve some internal states between runs and therefore it has more control over the data. The second parameter sets the timeout in milliseconds to wait/delay between each individual test run and it is a very similar parameter to the **-delay** one but it sets a timeout inside the common test instead.\r\n\r\n16. **-source-file = \"dir/path/to/file.cot\"** is a parameter to set the \"common test\" script file. The common test is a test cases which depends on the authentication test case and can read data to send and responses to expect from the text file. The \"cot\" file is a pseudo-xml file with stanzas to send and stanzas to expect. The the test cases compares the received packets with those in the text file and reports the test result. This is usually a more convenient way to write a new test cases than coding them in Java.\r\n\r\n17. **-time-out-ok** is set for a test case when we expect socket timeout as a correct result from the test case. Normally the timeout means that the test failed and there was no response from the server at all or the response was incorrect. For some tests however (like sending a message to the user who is blocking messages through privacy lists) the timeout is the desired correct test result.\r\n\r\n18. **-to-jid = \"**\\ user_name@host.name\\ **\"** sets the destination address for packets sending packets somewhere. As an example is the test case sending ``<message/>`` packet. You can set the destination address for the packet. Mind, normally every test expects some response for the data sent so make sure the destination end-point will send back the data expected by the test case."
  },
  {
    "path": "src/main/restructured/Tigase_Development/Tigase_Kernel.rst",
    "content": "Tigase Kernel\r\n=================\r\n\r\nTigase Kernel is an implementation of `IoC <https://en.wikipedia.org/wiki/Inversion_of_control>`__ created for Tigase XMPP Server. It is responsible for maintaining object lifecycle and provides mechanisms for dependency resolutions between beans.\r\n\r\nAdditionally, as and optional feature, Tigase Kernel is capable of configuring beans using a provided bean configurator.\r\n\r\nBasics\r\n------------\r\n\r\nWhat is kernel?\r\n^^^^^^^^^^^^^^^^^^^\r\n\r\nKernel is an instance of the ``Kernel`` class which is responsible for managing scope and visibility of beans. Kernel handles bean:\r\n\r\n-  registration of a bean\r\n\r\n-  unregistration of a bean\r\n\r\n-  initialization of a bean\r\n\r\n-  deinitialization of a bean\r\n\r\n-  dependency injection to the bean\r\n\r\n-  handling of bean lifecycle\r\n\r\n-  registration of additional beans based on annotations (*optionally using registered class implementing* ``BeanConfigurator`` *as* ``defaultBeanConfigurator``)\r\n\r\n-  configuration of a bean (*optionally thru registered class implementing* ``BeanConfigurator`` *as* ``defaultBeanConfigurator``)\r\n\r\nKernel core is responsible for dependency resolution and maintaining lifecycle of beans. Other features, like proper configuration of beans are done by additional beans working inside the Kernel.\r\n\r\nKernel identifies beans by their name, so each kernel may have only one bean named ``abc``. If more than one bean has the same name, then the last one registered will be used as its registration will override previously registered beans. You may use whatever name you want to name a bean inside kernel but it cannot:\r\n\r\n-  be ``service`` as this name is used by Tigase Kernel internally when RegistrarBean\\`s are in use (see :ref:`RegistrarBean<registrarBean>`)\r\n\r\n-  end with ``#KERNEL`` as this names are also used by Tigase Kernel internally\r\n\r\n.. Tip::\r\n\r\n   Kernel initializes beans using lazy initialization. This means that if a bean is not required by any other beans, or not retrieved from the kernel manually, an instance will not be created.\r\n\r\nDuring registration of a bean, the kernel checks if there is any beans which requires this newly registered bean and if so, then instance of a newly registered bean will be created and injected to fields which require it.\r\n\r\nWhat is a kernel scope?\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nEach kernel has its own scope in which it can look for beans. By default kernel while injecting dependencies may look for them only in the same kernel instance in which new instance of a bean is created or in the direct parent kernel. This way it is possible to have separate beans named the same in the different kernel scopes.\r\n\r\n.. Note::\r\n\r\n   If bean is marked as ``exportable``, it is also visible in all descendants kernel scopes.\r\n\r\nWhat is a bean?\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\nA bean is a named instance of the class which has parameterless constructor and which is registered in the kernel.\r\n\r\n.. Warning::\r\n\r\n    Parameterless constructor is a required as it will be used by kernel to create an instance of the bean, see :ref:`bean lifecycle<beanLifecycle>`.\r\n\r\n.. _beanLifecycle:\r\n\r\nLifecycle of a bean\r\n--------------------------\r\n\r\nCreating instance of a bean\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nInstantiation of a bean\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nDuring this step, kernel creates instance of the class which was registered for this bean (for more details see **Registration of a bean**). Instance of a bean is created using paremeterless constructor of a class.\r\n\r\n.. Note::\r\n\r\n   Bean instance is only created for required beans (i.e. beans that were injected somewhere).\r\n\r\n.. Note::\r\n\r\n   It’s possible to create bean instance without the need to inject it anywhere - such bean should be annoted with ``@Autostart`` annotation.\r\n\r\n.. _beanConfiguration:\r\n\r\nConfiguring a bean *(optional)*\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nIn this step kernel passes class instance of a bean to the configurator bean (an instance of ``BeanConfigurator`` if available), for configuring it. During this step, ``BeanConfigurator`` instance, which is aware of the configuration loaded from the file, injects this configuration to the bean fields annotated with ``@ConfigField`` annotation. By default configurator uses reflections to access those fields. However, if a bean has a corresponding public ``setter``/``getter`` methods for a field annotated with ``@ConfigField`` (method parameter/return type matches field type), then configurator will use them instead of accessing a field via reflection.\r\n\r\n.. Note::\r\n\r\n   If there is no value for a field specified in the configuration or value is equal to the current value of the field, then configurator will skip setting value for this field (It will also not call ``setter`` method even if it exists).\r\n\r\nAt the end of the configuration step, if bean implements ``ConfigurationChangedAware`` interface, then method ``beanConfigurationChanged(Collection<String> changedFields)`` is being called, to notify bean about field names which values has changed. This is useful, if you need to update bean configuration, when you have all configuration available inside bean.\r\n\r\n.. Note::\r\n\r\n   Configuration of the bean may be changed at runtime and it will be applied in the same way as initial configuration is passed to the bean. So please keep in mind that ``getter``/``setter`` may be called multiple times - even for already configured and initialized bean.\r\n\r\n.. _beanInjectingDependencies:\r\n\r\nInjecting dependencies\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nAt this point kernel looks for the bean class fields annotated with ``@Inject`` and looks for a value for each of this fields. During this step, kernel checks list of available beans in this kernel, which matches field type and additional constraints specified in the annotation.\r\n\r\nWhen a required value (instance of a bean) is found, then kernel tries to inject it using reflection. However, if there is a matching ``getter``/``setter`` defined for that field it will be called instead of reflection.\r\n\r\n.. Note::\r\n\r\n   If dependency changes, ie. due to reconfiguration, then value of the dependent field will change and ``setter`` will be called if it exists. So please keep in mind that ``getter``/``setter`` may be called multiple times - even for already configured and initialized bean.\r\n\r\nInitialization of a bean\r\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n\r\nWhen bean is configured and dependencies are set, then initialization of a bean is almost finished. At this point, if bean implements ``Initializable`` interface, kernel calls ``initialize()`` method to allow bean initialize properly if needed.\r\n\r\nDestroying instance of a bean\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nWhen bean is being unloaded, then reference to its instance is just dropped. However, if bean class implements ``UnregisterAware`` interface, then kernel calls ``beforeUnregister()`` method. This is very useful in case which bean acquires some resources during initialization and should release them now.\r\n\r\n.. Note::\r\n\r\n   This method will not be called if bean was not initialized fully (bean initialization step was note passed)!\r\n\r\nReconfiguration of a bean *(optional)*\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nAt any point in time bean may be reconfigured by default bean configurator (instance of ``BeanConfigurator``) registered in the kernel. This will happen in the same way as it described in :ref:`Configuring a bean<beanConfiguration>` in **Creating instace of a bean** section.\r\n\r\nUpdating dependencies\r\n^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIt may happen, that due to reconfiguration or registration/unregistration or activation/deactivation of some other beans dependencies of a bean will change. As a result, Tigase Kernel will inject new dependencies as described in :ref:`Injecting dependencies<beanInjectingDependencies>`\r\n\r\nRegistration of a bean\r\n---------------------------\r\n\r\nThere are few ways to register a bean.\r\n\r\nUsing annotation *(recommended but optional)*\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nTo register a bean using annotation you need to annotate it with ``@Bean`` annotation and pass values for following properties:\r\n\r\n-  ``name`` - name under which item should be registered\r\n\r\n-  ``active`` - ``true`` if bean should be enabled without enabling it in the configuration *(however it is still possible to disable it using configuration)*\r\n\r\n-  ``parent`` - class of the parent bean which activation should trigger registration of your bean. **In most cases parent class should be implementing ``RegistrarBean``**\r\n\r\n-  ``parents`` - array of classes which should be threaten as ``parent`` classes if more than one parent class is required *(optional)*\r\n\r\n-  ``exportable`` - ``true`` if bean should be visible in all descendant kernels (in other case default visibility rules will be applied) *(optional)*\r\n\r\n-  ``selectors`` - array of selector classes which will decide whether class should be registered or not *(optional)*\r\n\r\n.. Tip::\r\n\r\n   If ``parent`` is set to ``Kernel.class`` it tells kernel to register this bean in the root/main kernel (top-level kernel).\r\n\r\nIf you want your bean ``SomeDependencyBean`` to be registered when another bean ``ParentBean`` is being registered (like a required dependency), you may annotate your bean ``SomeDependencyBean`` with ``@Bean`` annotation like this example:\r\n\r\n.. code:: java\r\n\r\n   @Bean(name = \"nameOfSomeDependencyBean\", parent = ParentBean.class, active = true)\r\n   public class SomeDependencyBean {\r\n       ...\r\n   }\r\n\r\n.. Warning::\r\n\r\n    Works only if bean registered as ``defaultBeanConfigurator`` supports this feature. By default Tigase XMPP Server uses ``DSLBeanConfigurator`` which is subclass of ``AbstractBeanConfigurator`` which provides support for this feature.\r\n\r\nSetting ``parent`` to class not implementing ``RegistrarBean`` interface\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIf ``parent`` is set to the class which is not implementing ``RegistrarBean`` interface, then your bean will be registered in the same kernel scope in which parent bean is registered. If you do so, ie. by setting parent to the class of the bean which is registered in the ``kernel1`` and your bean will be also registered in ``kernel1``. As the result it will be exposed to other beans in the same kernel scope. This also means that if you will configure it in the same way as you would set ``parent`` to the ``parent`` of annotation of the class to which your ``parent`` point to.\r\n\r\n**Example.**\r\n\r\n.. code:: java\r\n\r\n   @Bean(name=\"bean1\", parent=Kernel.class)\r\n   public class Bean1 {\r\n       @ConfigField(desc=\"Description\")\r\n       private int field1 = 0;\r\n       ....\r\n   }\r\n\r\n   @Bean(name=\"bean2\", parent=Bean1.class)\r\n   public class Bean2 {\r\n       @ConfigField(desc=\"Description\")\r\n       private int field2 = 0;\r\n       ....\r\n   }\r\n\r\nIn this case it means that ``bean1`` is registered in the root/main kernel instance. At the same time, ``bean2`` is also registered to the root/main kernel as its value of ``parent`` property of annotation points to class not implementing ``RegistrarBean``.\r\n\r\nTo configure value of ``field1`` in instance of ``bean1`` and ``field2`` in instance of ``bean2`` in DSL (for more information about DSL format please check section ``DSL file format`` of the ``Admin Guide``) you would need to use following entry in the config file:\r\n\r\n.. code::\r\n\r\n   bean1 {\r\n       field1 = 1\r\n   }\r\n   bean2 {\r\n       field2 = 2\r\n   }\r\n\r\nAs you can see, this resulted in the ``bean2`` configuration being on the same level as ``bean1`` configuration.\r\n\r\nCalling kernel methods\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nAs a class\r\n~~~~~~~~~~~~~~\r\n\r\nTo register a bean as a class, you need to have an instance of a Tigase Kernel execute it’s ``registerBean()`` method passing your ``Bean1`` class.\r\n\r\n.. code:: java\r\n\r\n   kernel.registerBean(Bean1.class).exec();\r\n\r\n.. Note::\r\n\r\n   To be able to use this method you will need to annotate ``Bean1`` class with ``@Bean`` annotation and provide a bean name which will be used for registration of the bean.\r\n\r\nAs a factory\r\n~~~~~~~~~~~~~~\r\n\r\nTo do this you need to have an instance of a Tigase Kernel execute it’s ``registerBean()`` method passing your bean ``Bean5`` class.\r\n\r\n.. code:: java\r\n\r\n   kernel.registerBean(\"bean5\").asClass(Bean5.class).withFactory(Bean5Factory.class).exec();\r\n\r\nAs an instance\r\n~~~~~~~~~~~~~~\r\n\r\nFor this you need to have an instance of a Tigase Kernel execute it’s ``registerBean()`` method passing your bean ``Bean41`` class instance.\r\n\r\n.. code:: java\r\n\r\n   Bean41 bean41 = new Bean41();\r\n   kernel.registerBean(\"bean4_1\").asInstance(bean41).exec();\r\n\r\n.. Warning::\r\n\r\n    Beans registered as an instance will not inject dependencies. As well this bean instances will not be configured by provided bean configurators.\r\n\r\nUsing config file *(optional)*\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nIf there is registered a bean ``defaultBeanConfigurator`` which supports registration in the config file, it is possible to do so. By default Tigase XMPP Server uses ``DSLBeanConfigurator`` which provides support for that and registration is possible in the config file in DSL. As registration of beans using a config file is part of the admin of the Tigase XMPP Server tasks, it is described in explained in the Admin Guide in subsection ``Defining bean`` of ``DSL file format`` section.\r\n\r\n.. Tip::\r\n\r\n   This way allows admin to select different class for a bean. This option should be used to provide alternative implementations to the default beans which should be registered using annotations.\r\n\r\n.. Warning::\r\n\r\n    Works only if bean registered as ``defaultBeanConfigurator`` supports this feature. By default Tigase XMPP Server uses ``DSLBeanConfigurator`` which provides support for that.\r\n\r\nDefining dependencies\r\n-------------------------\r\n\r\nAll dependencies are defined with annotations:\r\n\r\n.. code:: java\r\n\r\n   public class Bean1 {\r\n     @Inject\r\n     private Bean2 bean2;\r\n\r\n     @Inject(bean = \"bean3\")\r\n     private Bean3 bean3;\r\n\r\n     @Inject(type = Bean4.class)\r\n     private Bean4 bean4;\r\n\r\n     @Inject\r\n     private Special[] tableOfSpecial;\r\n\r\n     @Inject(type = Special.class)\r\n     private Set<Special> collectionOfSpecial;\r\n\r\n     @Inject(nullAllowed = true)\r\n     private Bean5 bean5;\r\n   }\r\n\r\nKernel automatically determines type of a required beans based on field type. As a result, there is no need to specify the type of a bean in case of ``bean4`` field.\r\n\r\nWhen there are more than one bean instances matching required dependency fields, the type needs to be an array or collection. If kernel is unable to resolve dependencies, it will throw an exception unless ``@Inject`` annotation has ``nullAllowed`` set to ``true``. This is useful to make some dependencies optional. To help kernel select a single bean instance when more that one bean will match field dependency, you may set name of a required bean as shown in annotation to field ``bean3``.\r\n\r\nDependencies are inserted using getters/setters if those methods exist, otherwise they are inserted directly to the fields. Thanks to usage of setters, it is possible to detect a change of dependency instance and react as required, i.e. clear internal cache.\r\n\r\n.. Warning::\r\n\r\n    Kernel is resolving dependencies during injection only using beans visible in its scope. This makes it unable to inject an instance of a class which is not registered in the same kernel as a bean or not visible in this kernel scope (see :ref:`Scope and visibility<kernelScope>`).\r\n\r\n.. Warning::\r\n\r\n    If two beans have bidirectional dependencies, then it is required to allow at least one of them be ``null`` (make it an optional dependency). In other case it will create circular dependency which cannot be satisfied and kernel will throw exceptions at runtime.\r\n\r\nNested kernels and exported beans\r\n--------------------------------------\r\n\r\nTigase Kernel allows the usage of nested kernels. This allows you to create complex applications and maintain proper separation and visibility of beans in scopes as each module (subkernel) may work within its own scope.\r\n\r\nSubkernels may be created using one of two ways:\r\n\r\nManual registration of new a new kernel\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nYou can create an instance of a new kernel and register it as a bean within the parent kernel.\r\n\r\n.. code:: java\r\n\r\n   Kernel parent = new Kernel(\"parent\");\r\n   Kernel child = new Kernel(\"child\");\r\n   parent.registerBean(child.getName()).asInstance(child).exec();\r\n\r\n.. _registrarBean:\r\n\r\nUsage of RegistrarBean\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nYou may create a bean which implements the ``RegistrarBean`` interfaces. For all beans that implement this interface, subkernels are created. You can access this new kernel within an instance of ``RegistrarBean`` class as ``register(Kernel)`` and ``unregister(Kernel)`` methods are called once the ``RegistrarBean`` instance is created or destroyed.\r\n\r\nThere is also an interface named ``RegistrarBeanWithDefaultBeanClass``. This interface is very useful if you want or need to create a bean which would allow you to configure many subbeans which will have the same class but different names and you do not know names of those beans before configuration will be set. All you need to do is to implement this interface and in method ``getDefaultBeanClass()`` return class which should be used for all subbeans defined in configuration for which there will be no class configured.\r\n\r\nAs an example of such use case is ``dataSource`` bean, which allows administrator to easily configure many data sources without passing their class names, ie.\r\n\r\n.. code::\r\n\r\n   dataSource {\r\n       default () { .... }\r\n       domain1 () { .... }\r\n       domain2 () { .... }\r\n   }\r\n\r\nWith this config we just defined 3 beans named ``default``, ``domain1`` and ``domain2``. All of those beans will be instances of a class returned by a ``getDefaultBeanClass()`` method of ``dataSource`` bean.\r\n\r\n.. _kernelScope:\r\n\r\nScope and visibility\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nBeans that are registered within a parent kernel are visible to beans registered within the first level of child kernels. However, **beans registered within child kernels are not available to beans registered in a parent kernel** with the exception that they are visible to bean that created the subkernel (an instance of ``RegistrarBean``).\r\n\r\nIt is possible to export beans so they can be visible outside the first level of child kernels.\r\n\r\nTo do so, you need to mark the bean as exportable using annotations or by calling the ``exportable()`` method.\r\n\r\n**Using annotation.**\r\n\r\n.. code:: java\r\n\r\n   @Bean(name = \"bean1\", exportable = true)\r\n   public class Bean1 {\r\n   }\r\n\r\n*Calling* ``exportable()``.\r\n\r\n.. code:: java\r\n\r\n   kernel.registerBean(Bean1.class).exportable().exec();\r\n\r\nDependency graph\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nKernel allows the creation of a dependency graph. The following lines will generate it in a format supported by `Graphviz <http://www.graphviz.org>`__.\r\n\r\n.. code:: java\r\n\r\n   DependencyGrapher dg = new DependencyGrapher(krnl);\r\n   String dot = dg.getDependencyGraph();\r\n\r\nConfiguration\r\n----------------\r\n\r\nThe kernel core does not provide any way to configure created beans. Do do that you need to use the ``DSLBeanConfigurator`` class by providing its instance within configuration and registration of this instances within kernel.\r\n\r\n**Example.**\r\n\r\n.. code:: java\r\n\r\n   Kernel kernel = new Kernel(\"root\");\r\n   kernel.registerBean(DefaultTypesConverter.class).exportable().exec();\r\n   kernel.registerBean(DSLBeanConfigurator.class).exportable().exec();\r\n   DSLBeanConfigurator configurator = kernel.getInstance(DSLBeanConfigurator.class);\r\n   Map<String, Object> cfg = new ConfigReader().read(file);\r\n   configurator.setProperties(cfg);\r\n   // and now register other beans...\r\n\r\nDSL and kernel scopes\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nDSL is a structure based format explained in `Tigase XMPP Server Administration Guide: DSL file format section <http://docs.tigase.org/tigase-server/snapshot/Administration_Guide/html/#dslConfig>`__. **It is important to know that kernel and beans structure have an impact on what the configuration in DSL will look like.**\r\n\r\n**Example kernel and beans classes.**\r\n\r\n.. code:: java\r\n\r\n   @Bean(name = \"bean1\", parent = Kernel.class, active = true )\r\n   public class Bean1 implements RegistrarBean {\r\n     @ConfigField(desc = \"V1\")\r\n     private String v1;\r\n\r\n     public void register(Kernel kernel) {\r\n       kernel.registerBean(\"bean1_1\").asClass(Bean11.class).exec();\r\n     }\r\n\r\n     public void unregister(Kernel kernel) {}\r\n   }\r\n\r\n   public class Bean11 {\r\n     @ConfigField(desc = \"V11\")\r\n     private String v11;\r\n   }\r\n\r\n   @Bean(name = \"bean1_2\", parent = Bean1.class, active = true)\r\n   public class Bean12 {\r\n     @ConfigField(desc = \"V12\")\r\n     private String v12;\r\n   }\r\n\r\n   @Bean(name = \"bean2\", active = true)\r\n   public class Bean2 {\r\n     @ConfigField(desc = \"V2\")\r\n     private String v2;\r\n   }\r\n\r\n   public class Bean3 {\r\n     @ConfigField(desc = \"V3\")\r\n     private String v3;\r\n   }\r\n\r\n   public class Main {\r\n     public static void main(String[] args) {\r\n       Kernel kernel = new Kernel(\"root\");\r\n       kernel.registerBean(DefaultTypesConverter.class).exportable().exec();\r\n       kernel.registerBean(DSLBeanConfigurator.class).exportable().exec();\r\n       DSLBeanConfigurator configurator = kernel.getInstance(DSLBeanConfigurator.class);\r\n       Map<String, Object> cfg = new ConfigReader().read(file);\r\n       configurator.setProperties(cfg);\r\n\r\n       configurator.registerBeans(null, null, config.getProperties());\r\n\r\n       kernel.registerBean(\"bean4\").asClass(Bean2.class).exec();\r\n       kernel.registerBean(\"bean3\").asClass(Bean3.class).exec();\r\n     }\r\n   }\r\n\r\nFollowing classes will produce following structure of beans:\r\n\r\n-  \"bean1\" of class ``Bean1``\r\n\r\n   -  \"bean1_1\" of class ``Bean11``\r\n\r\n   -  \"bean1_2\" of class ``Bean12``\r\n\r\n-  \"bean4\" of class ``Bean2``\r\n\r\n-  \"bean3\" of class ``Bean3``\r\n\r\n.. Note::\r\n\r\n   This is a simplified structure, the actual structure is slightly more complex. However. this version makes it easier to explain structure of beans and impact on configuration file structure.\r\n\r\n.. Warning::\r\n\r\n    Even though ``Bean2`` was annotated with name ``bean2``, it was registered with name ``bean4`` as this name was passed during registration of a bean in ``main()`` method.\r\n\r\n.. Tip::\r\n\r\n   ``Bean12`` was registered under name ``bean1_2`` as subbean of ``Bean1`` as a result of annotation of ``Bean12``\r\n\r\nAs mentioned DSL file structure depends on structure of beans, a file to set a config field in each bean to bean name should look like that:\r\n\r\n.. code::\r\n\r\n   'bean1' () {\r\n       'v1' = 'bean1'\r\n\r\n       'bean1_1' () {\r\n           'v11' = 'bean1_1'\r\n       }\r\n       'bean1_2' () {\r\n           'v12' = 'bean1_2'\r\n       }\r\n   }\r\n   'bean4' () {\r\n       'v2' = 'bean4'\r\n   }\r\n   'bean3' () {\r\n       'v3' = 'bean3'\r\n   }"
  },
  {
    "path": "src/main/restructured/Tigase_Development/UsingDataRepository.rst",
    "content": ".. _using-data-repository:\n\nUsing ``DataRepository``\n========================\n\nIn order to use ``DataRepository`` the class has to implement ``DataSourceAware<DataRepository>`` interface and it's only API method: ``tigase.db.DataSourceAware.setDataSource``. In it you will get reference to an instance of the data repository (pool) and you should initialise all the prepared statements that you would like to use:\n\n.. code:: java\n\n    private static final String GET_DATA = \"SELECT field FROM my_custom_table WHERE user_id = ?\";\n\n    private DataRepository data_repo = null;\n\n    @Override\n    public void setDataSource(DataRepository dataSource) throws RepositoryException {\n        this.data_repo = dataSource;\n        try {\n            data_repo.initPreparedStatement(\"GET_DATA\", GET_DATA);\n        } catch (SQLException e) {\n            log.log(Logger.Level.WARNING, \"Failed to init prepared statement: \" + e.getMessage());\n        }\n    }\n\n\nTo use such initialised data source you should obtain the prepared statement from it, set all the required parameters, execute it and retrieve the data from the ResultSet. It's essential to always close the result set:\n\n.. code:: java\n\n    public void getDataFromRepo(String userID) throws TigaseDBException {\n        try {\n            var getDataQuery = data_repo.getPreparedStatement(null, GET_DATA);\n            getDataQuery.setString(1, userID);\n            ResultSet rs = null;\n            synchronized (getDataQuery) {\n                try {\n                    rs = getDataQuery.executeQuery();\n                    if (rs.next()) {\n                        log.log(Logger.Level.INFO, \"User data: \" + rs.getString(1));\n                    }\n                } finally {\n                    data_repo.release(null, rs);\n                }\n            }\n        } catch (SQLException e) {\n            throw new TigaseDBException(\"Failed to get prepared statement: \" + e.getMessage());\n        }\n    }\n\nComplete source example is included in the sources as ``tigase.examples.ExampleUsingDataRepository``"
  },
  {
    "path": "src/main/restructured/Tigase_Development/Using_Maven.rst",
    "content": ".. _usingMaven:\r\n\r\nUsing Maven\r\n================\r\n\r\nDocuments Describing Maven Use with the Tigase Projects\r\n\r\nSetting up Maven in Windows\r\n--------------------------------\r\n\r\nHere at Tigase, we employ Apache Maven to download latest builds, compile codes for export, and check for errors in the code during build. This guide will go over installing and running Maven from a Windows operating environment. We will consider windows versions 7, 8, and 8.1 for this guide. Because Maven does not come with an installer, there is a manual install process which might be a bit daunting for the new user, but setup and use is fairly simple.\r\n\r\nRequirements\r\n^^^^^^^^^^^^^^^^^\r\n\r\n1. Maven requires Java Development Kit (JDK) 6 or later. As Tigase requires the latest JDK to run, that will do for our purposes. If you haven’t installed it yet, download the installer from `this website <http://www.oracle.com/technetwork/java/javase/downloads/index.html>`__. Once you install JDK and restart your machine, be sure that you have the **JAVA_HOME** variable entered into Environment Variables so calls to Java will work from the command line.\r\n\r\n2. Download the Maven package from `here <https://maven.apache.org/download.cgi>`__ and unpack it into a directory of your choice. For this guide we will use ``C:\\Maven\\`` .\r\n\r\nSetting up Environment Variables\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nThe Environment Variables panel is brought up from the Control Panel by clicking **System and Security** > **System** > **Advanced System Settings**. Now click the |Environment Variables| button at the bottom of the panel and the Environment Variables panel will show.\r\n\r\n**IMPORTANT NOTICE: CHANGING THESE SETTINGS CAN BREAK OTHER FUNCTIONS IN THE OPERATING SYSTEM. DO NOT FOLLOW THIS GUIDE IF YOU DO NOT KNOW WHAT YOU ARE DOING!**\r\n\r\n|Env Panel|\r\n\r\nWe need to first add two variable paths to the System variables to account for Maven’s install location. As there are some programs that look for M2_HOME, and others that look for MAVEN_HOME, it’s easier to just add both and have all the bases covered.\r\n\r\nClick on New…​\r\n\r\n|Env New|\r\n\r\nFor the Name, use M2_HOME, and for the variable enter the path to maven, which in this case is\r\n\r\n::\r\n\r\n   C:\\Maven\r\n\r\nCreate another new variable with the MAVEN_HOME name and add the same directory. **These variable values just point to where you have unpacked maven, so they do not have to be in the C directory.**\r\n\r\nGo down to the system variables dialog and select Path, then click on Edit. The Path variables are separated by semicolons, find the end of the Variable value string, and add the following after the last entry:\r\n\r\n::\r\n\r\n   ;%M2_HOME%\\bin;%MAVEN_HOME%\\bin;\r\n\r\nWe have added two variables using the %% wildcards surrounding our Variable names from earlier.\r\n\r\nTesting Maven\r\n^^^^^^^^^^^^^^^^^^^\r\n\r\nNow we must test the command line to be sure everything installed correctly. Bring up the command line either by typing ``cmd`` in search, or navigating the start menu.\r\n\r\nFrom the prompt, you do not need to change directory as setting Path allows you to reference it. Type the following command: ``mvn -v``\r\n\r\nsomething like this should show up\r\n\r\n::\r\n\r\n   Apache Maven 3.3.3 (7994120775791599e205a5524ec3e0dfe41d4a06; 2015-04-22T04:57:3\r\n   7-07:00)\r\n   Maven home: C:\\Maven\r\n   Java version: 1.8.0_45, vendor: Oracle Corporation\r\n   Java home: C:\\Program Files\\Java\\jdk1.8.0_45\\jre\r\n   Default locale: en_US, platform encoding: Cp1252\r\n   OS name: \"windows 7\", version: \"6.1\", arch: \"amd64\", family: \"dos\"\r\n\r\nIf you see this message, success! You have finished installation and are ready to use Maven! If not, go back on your settings and insure that JDK is installed, and the JAVA_HOME, M2_HOME, and MAVEN_HOME variables are set properly.\r\n\r\n.. |Environment Variables| image:: /images/devguide/Env-button.jpg\r\n.. |Env Panel| image:: /images/devguide/Env-Panel.jpg\r\n.. |Env New| image:: /images/devguide/Env-New.jpg\r\n\r\nA Very Short Maven Guide\r\n------------------------------\r\n\r\nIf you don’t use `Maven <http://maven.apache.org/>`__ at all or use it once a year you may find the document a useful maven commands reminder:\r\n\r\nSnapshot Compilation and Snapshot Package Generation\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n-  ``mvn compile`` - compilation of the snapshot package\r\n\r\n-  ``mvn package`` - create snapshot jar file\r\n\r\n-  ``mvn install`` - install in local repository snapshot jar file\r\n\r\n-  ``mvn deploy`` - deploy to the remote repository snapshot jar file\r\n\r\nRelease Compilation, Generation\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n-  ``mvn release:prepare`` prepare the project for a new version release\r\n\r\n-  ``mvn release:perform`` execute new version release generation\r\n\r\nGenerating tar.gz, tar.bz2 File With Sources Only\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\n-  ``mvn -DdescriptorId=src assembly:assembly``\r\n\r\nAny of these commands will work when your commandline is in a directory with a pom.xml file. This file will instruct what Maven will do.\r\n\r\nProfiles\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nMaven uses profiles with the -P switch to tell what to compile and build. Tigase uses two different profiles:\r\n\r\n-  -Pdist - creates distribution archives\r\n\r\n-  -Pdoc - creates documentation"
  },
  {
    "path": "src/main/restructured/Tigase_Development/index.rst",
    "content": "=============================================\r\nDevelopment Guide\r\n=============================================\r\n\r\n.. toctree::\r\n   :titlesonly:\r\n   :numbered: 3\r\n\r\n   Basic_Information\r\n   CodeStyleGuide\r\n   Hack_Tigase_Jabber-XMPP_Server_in_Eclipse\r\n   Server_Compilation\r\n   Tigase_Kernel\r\n   Data_Sources_And_Repositories\r\n   UsingDataRepository\r\n   CustomAuthRepository\r\n   Component_Implementation\r\n   Packet_Filtering_in_Component\r\n   EventBus_API\r\n   Cluster_Map_Interface\r\n   Plugin_Development\r\n   Using_Maven\r\n   Tests\r\n   Experimental\r\n   Old_Stuff"
  },
  {
    "path": "src/main/restructured/conf.py",
    "content": "#\n# Tigase XMPP Server - The instant messaging server\n# Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, version 3 of the License.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. Look for COPYING file in the top folder.\n# If not, see http://www.gnu.org/licenses/.\n#\n\n# Configuration file for the Sphinx documentation builder.\n#\n# This file only contains a selection of the most common options. For a full\n# list see the documentation:\n# https://www.sphinx-doc.org/en/master/usage/configuration.html\n\n# -- Path setup --------------------------------------------------------------\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#\nimport os\n#import sys\n# sys.path.insert(0, os.path.abspath('.'))\n\n\n# -- Project configuration ---------------------------------------------------\n\n# Define the canonical URL if you are using a custom domain on Read the Docs\nhtml_baseurl = os.environ.get(\"READTHEDOCS_CANONICAL_URL\", \"\")\n\n# Tell Jinja2 templates the build is running on Read the Docs\nif os.environ.get(\"READTHEDOCS\", \"\") == \"True\":\n    if \"html_context\" not in globals():\n        html_context = {}\n    html_context[\"READTHEDOCS\"] = True\n\n\n# -- Project information -----------------------------------------------------\n\nproject = 'TigaseDoc'\ncopyright = '2004-2022, Tigase, Inc'\nauthor = 'Tigase, Inc.'\n\n# The full version, including alpha/beta/rc tags\nrelease = '0.1'\n\n\n# -- General configuration ---------------------------------------------------\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = [\n    'sphinx_rtd_theme'\n]\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\n# This pattern also affects html_static_path and html_extra_path.\nexclude_patterns = []\n\n\n# -- Options for HTML output -------------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\n#\nimport sphinx_rtd_theme\nhtml_theme = 'sphinx_rtd_theme'\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\n# html_static_path = ['_static']\n\n# The suffix(es) of source filenames.\n# You can specify multiple suffix as a list of string:\n#\n# source_suffix = ['.rst', '.md']\nsource_suffix = '.rst'\n\n# The master toctree document.\nmaster_doc = 'index'\n\nhtml_theme_options = {'collapse_navigation': True,\n    'sticky_navigation': True,\n    'navigation_depth': 0,\n    'includehidden': True,\n    'titles_only': False,\n    'display_version': True,\n}\n\ngettext_compact = False \nlanguage = \"zh_CN\"\nlocale_dirs = [\"locale/\"]\n\n\ngettext_compact = False \nlanguage = \"pl\"\nlocale_dirs = [\"locale/\"]\n\ngettext_allow_fuzzy_translations = True"
  },
  {
    "path": "src/main/restructured/files/.gitignore",
    "content": "# Placeholder"
  },
  {
    "path": "src/main/restructured/files/StatsDumper.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n//CLASSPATH=/home/tigase/tigase-server/jars/tigase-server.jar\nimport tigase.stats.JavaJMXProxyOpt\nimport tigase.stats.JMXProxyListener\nimport tigase.stats.StatisticsProviderMBean\n\nif (args.size() > 0) {\n  if (args[0] == \"-h\") {\n    println (\"Parameters are: [hostname] [username] [password] [dir] [port] [delay(ms)] [interval(ms)] [loadhistory(bool)]\")\n    System.exit(0)\n  }\n}\n\n\ndef hostname = args.size() > 0 ? args[0] : \"0.0.0.0\"\ndef username = args.size() > 1 ? args[1] : \"admin\"\ndef password = args.size() > 2 ? args[2] : \"pass\"\ndef dir = args.size() > 3 ? args[3] : \"stats\"\ndef port = args.size() > 4 ? args[4].toInteger() : 9050\ndef delay = args.size() > 5 ? args[5].toLong() : 10000\ndef interval = args.size() > 6 ? args[6].toLong() : 10000\ndef loadHistory = args.size() > 7 ? args[7].toBoolean() : false\n\nif (!new File(dir).exists()) {\n  new File(dir).mkdir()\n}\n\ndef proxy = new JavaJMXProxyOpt(hostname, hostname, port, username, password, delay, interval, loadHistory)\nproxy.start()\n\nsleep delay\n\nwhile (true) {\n  try {\n    def allStats = proxy.getAllStats(0)\n    if (allStats != null) {\n      def timeToken = (int) System.currentTimeMillis() / 1000.toInteger() / 60.toInteger()\n      def file1 = new File(\"${dir}/stats_${new Date().format('yyyy-MM-dd_hh:mm:ss')}.txt\")\n      println \"Saving stats to the file ${file1.path}\"\n      allStats.each { key, value -> file1 << key + \"     \" + value + \"\\n\" }\n    }\n  } catch(all) {\n    println(\"An exception: \" + all)\n  }\n  sleep interval\n}\n"
  },
  {
    "path": "src/main/restructured/files/tigase-upgrade.sh",
    "content": "#!/bin/bash\n#\n# Tigase XMPP Server - The instant messaging server\n# Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, version 3 of the License.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. Look for COPYING file in the top folder.\n# If not, see http://www.gnu.org/licenses/.\n#\n\nPWD=`pwd`\nRED=\"\\033[0;31m\"\nGREEN=\"\\033[0;32m\"\nNO_COLOR=\"\\033[0m\"\nLEFT_OFFSET=\"80\"\n\nfunction usage() {\n  echo \"Usage: $0 {upgrade|rollback} install_package install_directory [tar|dir]\"\n  exit 1\n}\n\nfunction backup_installation() {\n  local installationDir=$1;\n  local backupPath=$2;\n  local backupFormat=$3 || \"dir\";\n\n  case \"${backupFormat}\" in\n    dir)\n    backup_installation_dir $installationDir $backupPath\n    ;;\n    tar)\n    backup_installation_tar $installationDir \"${backupPath}.tar.gz\"\n    ;;\n    *)\n    backup_installation_dir $installationDir $backupPath\n    ;;\n  esac\n}\n\nfunction backup_installation_dir() {\n  local installationDir=$1;\n  local backupDir=$2;\n  local tmp=\n\n  echo \"Backuping installation from $installationDir to $backupDir ...\"\n  tmp=$(cp -pR $installationDir $backupDir 2>&1)\n  if [ $? -ne 0 ]; then\n    printf \"${RED}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"FAIL\"\n    echo -e \"${RED}Failed to move backup to $backupDir${NO_COLOR}\\n${tmp}\"\n    exit 1\n  fi\n  printf \"${GREEN}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"DONE\";\n}\n\nfunction backup_installation_tar() {\n  local installationDir=$1;\n  local backupFile=$2;\n  local tarResult=\n\n  echo \"Backuping installation from $installationDir to $backupFile ...\"\n  tarResult=$(COPYFILE_DISABLE=1 tar -cz -C `dirname $installationDir` -f $backupFile `basename $installationDir` 2>&1)\n  if [ $? -ne 0 ]; then\n    printf \"${RED}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"FAIL\"\n    echo -e \"${RED}Failed to create archive with backup $backupFile${NO_COLOR}\\n${tarResult}\"\n    exit 1\n  fi\n  printf \"${GREEN}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"DONE\";\n}\n\nfunction download_package() {\n  local url=$1;\n  local result=$2;\n  local tmp=`basename \"$url\"`;\n  local curlResult=\n  echo \"Downloading archive $archiveFile ...\"\n  curlResult=$(curl --fail -O -J -L $url 2>&1)\n  if [ $? -ne 0 ]; then\n    printf \"${RED}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"FAIL\"\n    echo -e \"${RED}Failed to download archive from $url${NO_COLOR}\\n${curlResult}\"\n    exit 1\n  else\n    eval \"$result=`pwd`/$tmp\"\n  fi\n  printf \"${GREEN}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"DONE\";\n}\n\nfunction unpack_archive() {\n  local archive=$1;\n  local homeDir=$2;\n  local result=$3;\n  local tarResult=\n  echo \"Unpacking archive $archive ...\"\n  tarResult=$(tar -xC $homeDir -f $archive 2>&1)\n  if [ $? -ne 0 ]; then\n    printf \"${RED}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"FAIL\"\n    echo -e \"${RED}Failed to unpack archive $archive!${NO_COLOR}\\n${tarResult}\"\n    exit 1\n  fi\n  local firstFile=$(tar -tf $archive | head -1 | cut -f1 -d\"/\");\n  eval \"$result=$homeDir/`dirname $firstFile/.`\"\n  printf \"${GREEN}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"DONE\";\n}\n\nfunction copy_data_to_new_installation() {\n  local oldDir=$1;\n  local newDir=$2;\n  local tmp=\n  echo \"Removing default config file ...\"\n  tmp=$(rm \"$newDir/etc/config.tdsl\" 2>&1)\n  if [ $? -ne 0 ]; then\n    printf \"${RED}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"FAIL\"\n    echo -e \"${RED}Failed to remove default config file!${NO_COLOR}\\n${tmp}\"\n    exit 1\n  fi\n  printf \"${GREEN}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"DONE\";\n  echo \"Copying configuration files ...\"\n  tmp=$(cp -pR \"$oldDir/etc/\" \"$newDir/etc/\" 2>&1)\n  if [ $? -ne 0 ]; then\n    printf \"${RED}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"FAIL\"\n    echo -e \"${RED}Failed to copy configuration files!${NO_COLOR}\\n${tmp}\";\n    exit 1\n  fi\n  printf \"${GREEN}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"DONE\";\n  echo \"Copying SSL certificates ...\"\n  tmp=$(cp -pR \"$oldDir/certs/\" \"$newDir/certs/\" 2>&1)\n  if [ $? -ne 0 ]; then\n    printf \"${RED}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"FAIL\"\n    echo -e \"${RED}Failed to copy SSL certificates!${NO_COLOR}\\n${tmp}\";\n    exit 1\n  fi\n  printf \"${GREEN}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"DONE\";\n}\n\nfunction upgrade_installation() {\n  local archiveFile=$1;\n  local installationDir=$2;\n  local backupFormat=$3;\n  local installationLink=$4;\n  local unpackedArchive=\n  local wasDownloaded=0\n  local tmp=\n\n  if [[ $archiveFile == http\\:* ]] || [[ $archiveFile == https\\:* ]]; then\n    download_package $archiveFile archiveFile\n    wasDownloaded=1\n  fi\n\n  local homeDir=$(dirname $installationDir)\n  local backupDir=$homeDir\n  local backupPath=\"$backupDir/`basename $installationDir`_backup-`date +%y-%m-%d_%H%M`\"\n\n  backup_installation $installationDir $backupPath $backupFormat;\n  unpack_archive $archiveFile $homeDir unpackedArchive\n\n  echo \"Unpacked archive to $unpackedArchive\"\n  if [ $wasDownloaded -ne 0 ]; then\n    rm $archiveFile\n  fi\n\n  copy_data_to_new_installation $installationDir $unpackedArchive\n\n  echo \"Removing old installation directory ...\"\n  tmp=$(rm -rf $installationDir 2>&1)\n  if [ $? -ne 0 ]; then\n    printf \"${RED}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"FAIL\"\n    echo -e \"${RED}Failed to remove old installation directory!${NO_COLOR}\\n${tmp}\";\n    exit 1\n  fi\n\n  printf \"${GREEN}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"DONE\";\n\n  if [ -z $installationLink ] ; then\n    echo \"Replacing installation directories ...\"\n    tmp=$(mv $unpackedArchive $installationDir 2>&1)\n    if [ $? -ne 0 ]; then\n      printf \"${RED}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"FAIL\"\n      echo -e \"${RED}Failed to move unpacked archive to installation directory!${NO_COLOR}\\n${tmp}\";\n      exit 1\n    fi\n    printf \"${GREEN}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"DONE\";\n  else\n    echo \"Updating symlink $installationLink ...\"\n    tmp=$(unlink $installationLink 2>&1)\n    if [ $? -ne 0 ]; then\n      printf \"${RED}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"FAIL\"\n      echo -e \"${RED}Failed to remove symlink!${NO_COLOR}\\n${tmp}\";\n      exit 1\n    fi\n    printf \"${GREEN}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"DONE\";\n    tmp=$(ln -s $unpackedArchive $installationLink 2>&1)\n    if [ $? -ne 0 ]; then\n      printf \"${RED}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"FAIL\"\n      echo -e \"${RED}Failed to create new symlink!${NO_COLOR}\\n${tmp}\";\n      exit 1\n    fi\n    printf \"${GREEN}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"DONE\";\n  fi\n\n  echo \"Starting Tigase XMPP Server schema upgrade ...\"\n  if [ -z $INSTALLATION_LINK ] ; then\n    cd $INSTALLATION_DIR\n  else\n    cd $INSTALLATION_LINK\n  fi\n  scripts/tigase.sh upgrade-schema etc/tigase.conf\n  cd $PWD\n  if [ $? -ne 0 ]; then\n    echo -e \"${RED}Failed to upgrade database schema!${NO_COLOR}\";\n    exit 1\n  fi\n\n  echo -e \"\\n${GREEN}Upgrade of Tigase XMPP Server finished!${NO_COLOR}\";\n}\n\nfunction rollback_installation() {\n  local archiveFile=$1;\n  local installationDir=$2;\n  local backupFormat=$3;\n  local installationLink=$4;\n  local wasDownloaded=0\n  local unpackedArchive\n  local tmp=\n\n  if [[ $archiveFile == http\\:* ]] || [[ $archiveFile == https\\:* ]]; then\n    download_package $archiveFile archiveFile\n    wasDownloaded=1\n  fi\n\n  local homeDir=$(dirname $installationDir)\n  local backupDir=$homeDir\n  local backupPath=\"$backupDir/`basename $installationDir`_rollback-`date +%y-%m-%d_%H%M`\"\n\n  backup_installation $installationDir $backupPath $backupFormat;\n\n  echo \"Removing installation directory ...\"\n  tmp=$(rm -rf $installationDir 2>&1)\n  if [ $? -ne 0 ]; then\n    printf \"${RED}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"FAIL\"\n    echo -e \"${RED}Failed to remove installation directory!${NO_COLOR}\\n${tmp}\";\n    exit 1\n  fi\n\n  if [ $wasDownloaded -eq 0 ] && [[ $archiveFile != *.tar.gz ]] ; then\n    echo \"Restoring data from backup location $archiveFile ...\"\n    unpackedArchive=$(echo $archiveFile | sed \"s/\\(.*\\)_backup.*/\\1/\")\n    tmp=$(mv $archiveFile $unpackedArchive 2>&1)\n    if [ $? -ne 0 ]; then\n      printf \"${RED}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"FAIL\"\n      echo -e \"${RED}Failed to copy backup data to installation directory!${NO_COLOR}\\n${tmp}\";\n      exit 1\n    fi\n  else\n    unpack_archive $archiveFile $homeDir $unpackedArchive\n  fi\n\n  if [ $wasDownloaded -ne 0 ]; then\n    rm $archiveFile\n  fi\n\n  if ! [ -z $installationLink ] ; then\n    echo \"Updating symlink $installationLink ...\"\n    tmp=$(unlink $installationLink 2>&1)\n    if [ $? -ne 0 ]; then\n      printf \"${RED}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"FAIL\"\n      echo -e \"${RED}Failed to remove symlink!${NO_COLOR}\\n${tmp}\";\n      exit 1\n    fi\n    printf \"${GREEN}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"DONE\";\n    tmp=$(ln -s $unpackedArchive $installationLink 2>&1)\n    if [ $? -ne 0 ]; then\n      printf \"${RED}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"FAIL\"\n      echo -e \"${RED}Failed to create new symlink!${NO_COLOR}\\n${tmp}\";\n      exit 1\n    fi\n    printf \"${GREEN}%${LEFT_OFFSET}s${NO_COLOR}\\n\" \"DONE\";\n  fi\n\n  echo \"Starting Tigase XMPP Server schema upgrade ...\"\n  if [ -z $INSTALLATION_LINK ] ; then\n    cd $INSTALLATION_DIR\n  else\n    cd $INSTALLATION_LINK\n  fi\n  scripts/tigase.sh upgrade-schema etc/tigase.conf\n  cd $PWD\n  if [ $? -ne 0 ]; then\n    echo -e \"${RED}Failed to upgrade database schema!${NO_COLOR}\";\n    exit 1\n  fi\n\n  echo -e \"\\n${GREEN}Rollback of Tigase XMPP Server finished!${NO_COLOR}\";\n}\n\nif [ $# -lt 3 ] ; then\n  usage\nfi\n\nINSTALLATION_DIR=$3\nINSTALLATION_LINK=\n\nif [ $# -gt 2 ] ; then\n  INSTALLATION_DIR=`readlink $3`\n  if [ -z $INSTALLATION_DIR ] ; then\n    INSTALLATION_DIR=$3\n  else\n    INSTALLATION_LINK=$3;\n    echo -e \"Passed installation directory is symlink to $INSTALLATION_DIR\"\n  fi\n  if [ \"${INSTALLATION_DIR:0:1}\" != '/' ] ; then\n    INSTALLATION_DIR=\"$PWD/$INSTALLATION_DIR\"\n  fi\nfi\nINSTALLATION_DIR=\"`dirname ${INSTALLATION_DIR}//.`\"\n\nBACKUP_FORMAT=\"dir\"\nif [ $# -gt 3 ] ; then\n  BACKUP_FORMAT=$4\nfi\n\nARCHIVE_FILE=$2\n\ncase \"${1}\" in\n  upgrade)\n  upgrade_installation $ARCHIVE_FILE $INSTALLATION_DIR $BACKUP_FORMAT $INSTALLATION_LINK\n  ;;\n  rollback)\n  rollback_installation $ARCHIVE_FILE $INSTALLATION_DIR $BACKUP_FORMAT $INSTALLATION_LINK\n  ;;\n  *)\n  usage\n  ;;\nesac\n"
  },
  {
    "path": "src/main/restructured/images/devguide/tigase-architecture-draw.io.xml",
    "content": "<!--\n\n    Tigase XMPP Server - The instant messaging server\n    Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published by\n    the Free Software Foundation, version 3 of the License.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program. Look for COPYING file in the top folder.\n    If not, see http://www.gnu.org/licenses/.\n\n-->\n<mxfile userAgent=\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:62.0) Gecko/20100101 Firefox/62.0\" version=\"9.2.7\" editor=\"www.draw.io\" type=\"device\"><diagram id=\"c09a98ef-5761-1ef1-ceec-782d9a08a578\" name=\"Page-1\">7Z1dc6M2FIZ/jS/j4Rt8mbhJtzObmUy9O+1eYlAMDUYURGz311cCCcyXjTHIIcG+iDkIgaTD80riiMzk5Xb/e2gGzjO0gTeTBHs/k3+bSZKoyAL+QyyH1CIJ6iK1bELXpqlyw8r9D1AjPXATuzaICgkRhB5yg6LRgr4PLFSwmWEId8Vkr9ArnjUwN6BiWFmmV7X+5drISa2GpOf2b8DdOOzMokbLtzatt00IY5+ebybJT8kn3b01WV60oJFj2nB3ZJIfZ/IyhBClv7b7JfBI5bJqS497atibXXcIfNTmAFaiCB1Y2YGNq4JuwhA5cAN903vMrQ9J+QDJQcRb/8TbgKX3oU8SOGjr0b1g76K/j37/wr+FuYq3ImSG6J40FjZZnhlFrsXMT67HMki2qYdo5HQAoQPdNmMEsSm/yu8QBtlxIXwDS+jBMCmXLCSfbA9rV1zlD6/QR3Up07ohFdJYvdQUwTi0aCola0d8gwC4BSg84CQh8EzkvhezMqmjbrJ02aEv0MUnkQR2VwkK9Rl2U6lqMQ9cURuA6GF5m+MfR9eRmxJPqPcKJc343fRiwPxY8xCtKXJDmbSw2r8xcdWH77Hl2ibes4R+BLEvZHvwrw39m+SwLhtolrkHsiPJjrsoael7nECSgn01W0uKWEa4VGlexfyxuXLOStLSPbBzXARWQVrKHYZc0alfsXse+ctr8jnpc6c97B2ECOxP+1jVoegBhljyC8aWXU4uUaM254hautDsgwXnOeEponQ1P2pgIVRggesiPGQcIRv5vrZEaGqDU6RohagaApapVWVON7bQWz691Zn4XMmbtm0tGZNW9K0VKh+tEIfTCrVRK2z3vYH1H0c+on7kIzPzLHNq22+DgGQPbBBin4H+JIdF1+cph1m3fkJkb4jU+CBSlgdDpDZqRO44MPJj1YNYXw9gHUHrDaAs/7B9mbtWzycCMc6c47hE5QTiy4YqnxbS+i3HJdokuoO05/Ciq4jKUKKrj1p01zByJlVpoyq6MVc56gov1lR1ZQSsOSV93ThkDKAr9WzS9LJjldylPzRJvDonX0iwDD6CpYqDjRKNUQuW5cURAuGkWS00SzF4joR4PeGdFCsxLbgpli6X2KQNp1jKpFh9K9aCj2JpwmCKtRi1YoE91ivsDF9rbrNBveE2mKY1O4m5qnMUc6X6fOkiyjK1E+ZSNlX565igLYIdOII1csyAnOfVA3t6ZQ8UXyB0ce2BkHiN629wortCZ4QWkElJZ0qLLPzwSM/7QTcukHk4ShAQIkdXIpld7kiZ/AyiiIRmTkh++BPG03iqHsGKpLA5v0ufLYlKH0Mq+erecKvx0zgD2/ofUinchlSGVBR3USt1ntPLqnSeq2OzcnSBMdjYTLx+bDZ54wXeqN3MG6Wu3qjpKi9vHPmgcIU7ID3F0I29A/Js+rgrNvVA2vVANJ4dEDbxN9Kb7CVer+L15Fh1swul/ofcdnJBvd6rlNtN8uYTE1WV/oDzEt06DnLNVAK3ICmZ/8RAVzz0JF8/l80zmlEcdD1bfXDoc+whF+/9GRHFFJaOic5Dq+YqJhg2w7D9wrY+YPjhYzRGADzplsCTRt5HmgIA20BBFnlC4cNHGo8ACvItoSCPGgrZ4xHhPrQc9508+JoYcZ4Ri/Yxwn1Q4nbv1Pga46ghnge0JkjzuzHGQJBvP3683N2//DFh4zw2FEPiio3bvV7ha2BjiAc3rbEx7jXAK2i9RSqp4xDuDxM8zsND1XSu8FhM8BgUHrdc4CqPey3jfD6fiHGeGJrKtbsh8yLGtEI+re8hljK2buxxLy6bCNIuFl1rG4XQS5ej1yjIzxdd1sy/bgCpi0SXhXr/uDLwTCkFnlXWQrcNPJPKGQ23GEmte95T8s+i911wp1OHaO18iyX5ErqakZOdMHvZb1VvZGwJqmsaeuKEppaepFYxodRiohwn2IUTanXOfZm+tjkJtBNonFlUaS1cXlRskmI9s0aptpPpuRuf3O24fgC2P5Dacy3Tu6c7tq5tJzyq84HcS4RyoxWas+QNhkC+1E6pIbF+BCMLO+7J3LoeaY2KpvbS4PrCKN53NR1LQ6k2eDnAtFN7V8OIOnUb53p5Bc/R8iX9cy5f6k8ZBlpeqpbj2oWOylDOSCpn1KAMg6yaUuteYtrow8J57agh/ZGDPGrkW3GlE6JzChd1uKnDUg9YKan5XV1AThayeMwVQ71cSPBm/h78tFHz/zYgP/4P</diagram></mxfile>"
  },
  {
    "path": "src/main/restructured/index.rst",
    "content": "\n===================================\nTigase Documentation\n===================================\n\nTigase Projects documentations\n-------------------------------------\n\nServer Components\n^^^^^^^^^^^^^^^^^\n\n-  `Tigase MIX <../../projects/tigase-tigase-mix/en/latest/>`__ Next generation groupchat protocol\n-  `Tigase MUC <../../projects/tigase-tigase-muc/en/latest/>`__ Current generation groupchat protocol\n-  `Tigase Spam <../../projects/tigase-tigase-spam/en/latest/>`__ Project aiming at fighting spam\n-  `Tigase HTTP API <../../projects/tigase-tigase-http-api/en/latest/>`__ Operate and manage Tigase via HTTP... and more!\n-  `Tigase PubSub <../../projects/tigase-tigase-pubsub/en/latest/>`__ Implementation of XEP-0045: PubSub protocol\n-  `Tigase Message Archiving <../../projects/tigase-tigase-message-archiving/en/latest/>`__ Implementation of XEP-0136: PMessage archiving\n-  `Tigase Unified Archive <../../projects/tigase-tigase-unified-archive/en/latest/>`__ The extended version of Tigase Message Archiving Component\n-  `Tigase Socks5 <../../projects/tigase-socks5/en/latest/>`__ Implementation of XEP-0065 SOCKS5 Bytestreams\n-  `Tigase Push <../../projects/tigase-tigase-push-2/en/latest/>`__ A gateway between Push Notification services and XMPP servers\n-  `Tigase ACS <../../projects/tigase-tigase-acs-2/en/latest/>`__ General purpose, commercial clustering strategy \n-  `Tigase Auditlog <../../projects/tigase-tigase-auditlog-2/en/latest/>`__ An implementation of Audit Log pattern functionality to log important events \n-  `Tigase Workgroup Queues <../../projects/tigase-tigase-workgroup-queues/en/latest/>`__ Implementation of XEP-0142: Workgroup Queues\n-  `Tigase Databse Migrator <../../projects/tigase-database-migrator/en/latest/>`__ Component allowing migration of different types of data from various XMPP servers\n-  `Tigase Extras AWS <../../projects/tigase-tigase-extras/en/latest/>`__ provides you with support for additional features and integrations with Amazon AWS.\n-  `Tigase Extras LDAP Server <../../projects/tigase-tigase-extras-ldap/en/latest/>`__ provides you with basic LDAP server to allow for integration with software allowing for LDAP-based authentication.\n\nXMPP Clients\n^^^^^^^^^^^^^^^^^\n\n-  `SiskinIM <../../projects/tigase-siskin-im/en/latest/>`__ iOS XMPP Client\n\n-  `BeagleIM <../../projects/tigase-beagle-im/en/latest/>`__ macOS XMPP Client\n\n-  `StorkIM <../../projects/tigase-stork/en/latest/>`__ android XMPP Client\n\nXMPP Libraries\n^^^^^^^^^^^^^^^^^\n\n-  `Martin <../../projects/tigase-tigase-swift/en/latest/>`__ Swift Lang XMPP Library\n\n-  `Halcyon <../../projects/tigase-halcyon/en/latest/>`__ a multiplatform, extensible XMPP client library\n\n-  `Tigase XML Tools <../../projects/tigase-xmltools/en/latest/>`__ A library providing support for working with XML documents\n\n-  `Tigase JaXMPP <../../projects/tigase-jaxmpp/en/latest/>`__ an extensible XMPP client library\n\n\nJavadoc\n^^^^^^^^\n\n-  `Tigase Mongodb <../../projects/tigase-tigase-mongodb/en/latest/>`__ Java documentation for tigase mangodb\n\n-  `Tigase Utils <../../projects/tigase-tigase-utils/en/latest/>`__ Java documentation for tigase utils\n\nMiscellaneous\n^^^^^^^^^^^^^\n\n-  `Tigase TTS NG <../../projects/tigase-tigase-tts-ng/en/latest/>`__ Project intended to run automated funtionality tests\n\n-  `Tigase XEPs <https://xeps.tigase.net/>`__ XMPP extensions that we Tigase is working on with the goal to transition them to full, standard XMPP extensions.\n\n\nLegacy documentation website\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n-  `Legacy documentation website <https://docs-legacy.tigase.net/>`__ Our legacy documentation website which contains documentation for older versions of componetns and applications\n\n.. toctree::\n   :caption: Administration Guide\n   :maxdepth: 3\n\n   Tigase_Administration/index.rst\n\n\n.. toctree::\n   :caption: Development Guide\n   :maxdepth: 3\n\n   Tigase_Development/index.rst\n"
  },
  {
    "path": "src/main/restructured/locale/es/LC_MESSAGES/Tigase_Administration/Release_Notes/Tigase_Release_Notes.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2023-02-16 15:01-0800\\n\"\n\"PO-Revision-Date: 2024-06-13 07:13+0000\\n\"\n\"Last-Translator: Wojciech Kapcia <wojtek@tigase.net>\\n\"\n\"Language-Team: Spanish <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/release_notes/es/>\\n\"\n\"Language: es\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=2; plural=n != 1;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.11.0\\n\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_Release_Notes.rst:2\nmsgid \"Tigase 8.3.0 Release Notes\"\nmsgstr \"Notas de la versión de Tigase 8.3.0\"\n\n#: ../../Tigase_Administration/Release_Notes/tigase-server-current.inc:2\nmsgid \"Tigase XMPP Server 8.4.0 Change notes\"\nmsgstr \"Tigase XMPP Server 8.4.0 Notas de cambio\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:5\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:5\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:5\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:5\n#: ../../Tigase_Administration/Release_Notes/tigase-server-current.inc:5\nmsgid \"Major Changes\"\nmsgstr \"Cambios importantes\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:16\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:15\n#: ../../Tigase_Administration/Release_Notes/tigase-server-current.inc:11\nmsgid \"All Minor Features & Behavior Changes\"\nmsgstr \"Todas las funciones y cambios de comportamiento menores\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:2\nmsgid \"Tigase XMPP Server 8.3.0 Change notes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:7\nmsgid \"This version requires JDK17 to run\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:8\nmsgid \"Added support for mam2#extended [#mam-73]\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:9\nmsgid \"\"\n\"Rework certificate generation to utilise `keygen` tool instead of using \"\n\"`sun.*` API unavailable under JDK17\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:10\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:45\nmsgid \"\"\n\"Added support for XEP-0440 SASL Channel Binding Type Capability and fixed\"\n\" and reenabled `SCRAM-*-PLUS SASL` mechanisms [#server-1335]\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:11\nmsgid \"\"\n\"Added initial,preview support for SASL2 and Bind2 (disabled by default) -\"\n\" to enable, activate beans `'urn:xmpp:bind:0'` and `'urn:xmpp:sasl:2'` in\"\n\" `'sess-man'` [#server-1332]\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:17\nmsgid \"\"\n\"Increased network socket buffer from 2K to 4K to improve performance when\"\n\" reading data from socket. It can increase somewhat memory usage \"\n\"proportionally to number of concurrent user connections. It's possible to\"\n\" configure size of this buffer using `socket-buffer-size` property - \"\n\"please see documentation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:18\nmsgid \"\"\n\"Add configuration to log size generated by LoggerTask in Monitor and \"\n\"decrease default from 1M to 50K; Disable serialisation of monitor events;\"\n\" #servers-372\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:19\nmsgid \"Add DOAP file; update documentation with supported features; #server-1076\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:20\nmsgid \"\"\n\"Fix issue with NPE in JabberIqAuth plugin when no password was presented \"\n\"due to missing return statement; fixed similar issue where, after closing\"\n\" the connection, the execution of the code wasn't terminated in \"\n\"JabberIqAuth and SaslAuth plugins #server-1317\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:21\nmsgid \"\"\n\"Add support for XEP-0398 to feature list and updated list of supported \"\n\"features; #server-1316\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:22\nmsgid \"\"\n\"Change try-catch statement in database schema loader to better catch edge\"\n\" cases; #serverdist-10\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:23\nmsgid \"\"\n\"Remove wildcard certificate generation (as main DN) in certificate \"\n\"container to avoid issues that it entails (inability to override such \"\n\"self-signed certificate via ad-hoc commands!). Wildcards are now properly\"\n\" handled by CertificateGenerateor and are included correctly as SAN in \"\n\"addition to DN for main domain; Fix handling \\\"default\\\" certificates \"\n\"from repository; #server-1279\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:24\nmsgid \"\"\n\"Change default watchdog ping from (forbidden by RFC) whitespace to xmpp; \"\n\"add warning if someone configures it as whitespace either way; \"\n\"server-1318\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:25\nmsgid \"Improve XMPPDomBuilderHandler logging; #server-1323\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:26\nmsgid \"\"\n\"Improved Stream Management code responsible for generating `<r/>` \"\n\"requests #server-1324 (#150)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:27\nmsgid \"\"\n\"Added `socket-buffer-size` option to `ConnectionManager` to configure \"\n\"`SO_RCVBUF` separately from internal network buffers #server-1325\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:28\nmsgid \"\"\n\"Increased socket-buffer-size for client-to-server and intercluster \"\n\"connections and added documentation #server-1325\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:29\nmsgid \"Fix MAX_PAUSE property name; #server-1326\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:30\nmsgid \"Updated implementation of XEP-0377: Spam Reporting #server-1327\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:31\nmsgid \"\"\n\"Fixed issue with errors being sent for unexpected <iq type=result/> \"\n\"stanzas #server-1328\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:32\nmsgid \"Improved exceptions handling in StanzaProcessor #server-1328\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:33\nmsgid \"Switch from jtds to MS own jdbc driver; #serverdist-12\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:34\nmsgid \"\"\n\"Prevent re-delivery of certain S2S packets (sasl, features, dialback, \"\n\"etc) as it doesn't make sense; #server-1320\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:35\nmsgid \"\"\n\"Adjust log levels to avoid WARNINGS during startup for regular messages; \"\n\"#server-1115\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:36\nmsgid \"Add 'active in last x' statistic; #server-1281\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:37\nmsgid \"Include option to restart JVM on OOM (off by default)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:38\nmsgid \"\"\n\"Correctly process packets from mobile queue instead of re-adding \"\n\"currently filtered packet; #server-1331\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:39\nmsgid \"\"\n\"Improvements to NativeMemoryTracking implementation with units; \"\n\"documentation; #server-1330\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:40\nmsgid \"Improve MAM logging; #servers-384\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:41\nmsgid \"Only count stanzas in StreamManagement #server-1333\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:42\nmsgid \"Fixed advertisement stream features for unauthorized stream #server-1334\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:43\nmsgid \"\"\n\"Fixed NPE during preparing stream features when connection is already \"\n\"closed #server-1334\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:44\nmsgid \"\"\n\"Added initial support for SASL2 and Bind2 (preview feature, disabled by \"\n\"default) #server-1332\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:46\nmsgid \"Fixed NPE during enabling of stream resumption #server-1332\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:47\nmsgid \"\"\n\"Fixed sending block/unblock presences from blocking command for domain \"\n\"#server-1336\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:48\nmsgid \"\"\n\"Better default for lastXmppPacketReceivedTime member to avoid WatchDog \"\n\"closing connection before lastXmppPacketReceivedTime is set; #server-1337\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:49\nmsgid \"\"\n\"Add proper addressing validation in S2S connection and allow connections \"\n\"without 'from' set; #server-1338\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:2\nmsgid \"Tigase XMPP Server 8.2.0 Change notes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:7\nmsgid \"\"\n\"**Improvements to s2s connection**: Version 8.2.0 brings a lot of \"\n\"improvements related to s2s connectivity: support for TLS1.3, improved \"\n\"logic during authentication and stream negotiation solving connectivity \"\n\"issues with various deployments\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:9\nmsgid \"\"\n\"**Better handling of certificates**: It’s now possible to store \"\n\"certificates in the database making it easier to manage them in clustered\"\n\" environment.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:11\nmsgid \"Deprecation of ``Element`` based events in favour of Object based events\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:13\nmsgid \"Improved performance: reduced memory usage and decrease startup time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:18\nmsgid \"\"\n\"`#server-1050 <https://projects.tigase.net/issue/server-1050>`__: \"\n\"Database installation without root credentials\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:20\nmsgid \"\"\n\"`#server-1062 <https://projects.tigase.net/issue/server-1062>`__: \"\n\"Deprecate Element based Event-bus\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:22\nmsgid \"\"\n\"`#server-1097 <https://projects.tigase.net/issue/server-1097>`__: It’s \"\n\"not possible to configure additional PacketFilters\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:24\nmsgid \"\"\n\"`#server-1101 <https://projects.tigase.net/issue/server-1101>`__: \"\n\"Enabling TLS1.3 causes s2s connections to fail\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:26\nmsgid \"\"\n\"`#server-1102 <https://projects.tigase.net/issue/server-1102>`__: Add \"\n\"possibility to extend MAM to MAM:2\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:28\nmsgid \"\"\n\"`#server-1105 <https://projects.tigase.net/issue/server-1105>`__: Enhance\"\n\" Add SSL Certificate ad-hoc with option to set default\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:30\nmsgid \"\"\n\"`#server-1119 <https://projects.tigase.net/issue/server-1119>`__: Use \"\n\"database for certificate storage instead of filesystem\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:32\nmsgid \"\"\n\"`#server-1120 <https://projects.tigase.net/issue/server-1120>`__: \"\n\"JabberIqRegister should allow enforcing both CAPTCHA and e-mail\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:34\nmsgid \"\"\n\"`#server-1132 <https://projects.tigase.net/issue/server-1132>`__: Don’t \"\n\"use s2s socket if only one-direction works\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:36\nmsgid \"\"\n\"`#server-1142 <https://projects.tigase.net/issue/server-1142>`__: After \"\n\"registration inform the client that the account activation (email) is \"\n\"required\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:38\nmsgid \"\"\n\"`#server-1158 <https://projects.tigase.net/issue/server-1158>`__: \"\n\"Establishing JMX connection to the server causes excessive memory \"\n\"allocation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:40\nmsgid \"\"\n\"`#server-1162 <https://projects.tigase.net/issue/server-1162>`__: Allow \"\n\"interfaces in @ConfigField\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:42\nmsgid \"\"\n\"`#server-1170 <https://projects.tigase.net/issue/server-1170>`__: TLS \"\n\"infinity loop impacts Tigase XMPP Server performance\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:44\nmsgid \"\"\n\"`#server-1175 <https://projects.tigase.net/issue/server-1175>`__: \"\n\"Connection with diebesban.de stopped with invalid-namespace error\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:46\nmsgid \"\"\n\"`#server-1177 <https://projects.tigase.net/issue/server-1177>`__: Ability\"\n\" to change log level during runtime\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:48\nmsgid \"\"\n\"`#server-1178 <https://projects.tigase.net/issue/server-1178>`__: Remove \"\n\"``online_status`` from the repository\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:50\nmsgid \"\"\n\"`#server-1179 <https://projects.tigase.net/issue/server-1179>`__: Add \"\n\"support for {clusterNode} in XEP-0215 host field\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:52\nmsgid \"\"\n\"`#server-1181 <https://projects.tigase.net/issue/server-1181>`__: \"\n\"NoSuchElementException in MaxDailyCounterQueue\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:54\nmsgid \"\"\n\"`#server-1182 <https://projects.tigase.net/issue/server-1182>`__: NPE \"\n\"while processing <iq type=\\\"result\\\"/> without existing session\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:56\nmsgid \"\"\n\"`#server-1187 <https://projects.tigase.net/issue/server-1187>`__: \"\n\"SchemaLoader should not print passwords in the logs (URL logs)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:58\nmsgid \"\"\n\"`#server-1192 <https://projects.tigase.net/issue/server-1192>`__: \"\n\"Obfuscate repository passwords\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:60\nmsgid \"\"\n\"`#server-1190 <https://projects.tigase.net/issue/server-1190>`__: \"\n\"Executing EditUser on non-existen’t user causes creation of the user\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:62\nmsgid \"\"\n\"`#server-1193 <https://projects.tigase.net/issue/server-1193>`__: Push \"\n\"notifications are sent for groupchat messages without <body/>\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:64\nmsgid \"\"\n\"`#server-1197 <https://projects.tigase.net/issue/server-1197>`__: \"\n\"Infinite loop while cutting body of encrypted push notification to fit \"\n\"the push notifications limit\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:66\nmsgid \"\"\n\"`#server-1199 <https://projects.tigase.net/issue/server-1199>`__: Don’t \"\n\"send any packets until s2s stream negotiation is finished\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:68\nmsgid \"\"\n\"`#server-1200 <https://projects.tigase.net/issue/server-1200>`__: Use \"\n\"proper size of network buffers for high-throughput connections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:70\nmsgid \"\"\n\"`#server-1203 <https://projects.tigase.net/issue/server-1203>`__: Handing\"\n\" error packets in CIDConnections.sendPacketsBack\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:72\nmsgid \"\"\n\"`#server-1217 <https://projects.tigase.net/issue/server-1217>`__: Prevent\"\n\" performing schema upgrade concurrently\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:74\nmsgid \"\"\n\"`#server-1219 <https://projects.tigase.net/issue/server-1219>`__: Use all\"\n\" JDBC URI parameters from config.tdsl when performing database upgrade.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:76\nmsgid \"\"\n\"`#server-1222 <https://projects.tigase.net/issue/server-1222>`__: Add \"\n\"support for XEP-0377: Spam Reporting\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:78\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:80\nmsgid \"\"\n\"`#server-1229 <https://projects.tigase.net/issue/server-1229>`__: \"\n\"Enabling CAPTCHA or e-mail for JabberIqRegister breaks password changing \"\n\"functionality.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:82\nmsgid \"\"\n\"`#server-1233 <https://projects.tigase.net/issue/server-1233>`__: Add \"\n\"option to CertificateRepository to load certificates from the filesystem\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:84\nmsgid \"\"\n\"`#server-1234 <https://projects.tigase.net/issue/server-1234>`__: Roster \"\n\"API improvements\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:86\nmsgid \"\"\n\"`#server-1237 <https://projects.tigase.net/issue/server-1237>`__: Rework \"\n\"CertificateRepository so items are stored individually\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:88\nmsgid \"\"\n\"`#server-1238 <https://projects.tigase.net/issue/server-1238>`__: Can’t \"\n\"set MOTD via ad-hoc.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:90\nmsgid \"\"\n\"`#server-1243 <https://projects.tigase.net/issue/server-1243>`__: Include\"\n\" wait-for-it.sh script in base distribution\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:92\nmsgid \"\"\n\"`#server-1245 <https://projects.tigase.net/issue/server-1245>`__: \"\n\"MethodStatistics doesn’t work well for interfaces with overloaded methods\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:94\nmsgid \"\"\n\"`#server-1251 <https://projects.tigase.net/issue/server-1251>`__: Can’t \"\n\"initialise MAM processor with default installation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:96\nmsgid \"\"\n\"`#server-1252 <https://projects.tigase.net/issue/server-1252>`__: Remove \"\n\"select row_count() from Tig_OfflineMessages_DeleteMessage\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:98\nmsgid \"\"\n\"`#server-1253 <https://projects.tigase.net/issue/server-1253>`__: It \"\n\"seems that 'expired-processor' doesn’t remove periodically expired \"\n\"messages\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:100\nmsgid \"\"\n\"`#server-1254 <https://projects.tigase.net/issue/server-1254>`__: Fix \"\n\"slow startup and shutdown\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:102\nmsgid \"\"\n\"`#server-1258 <https://projects.tigase.net/issue/server-1258>`__: Allow \"\n\"beans to be instantiated without the requirement to reference/inject them\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:104\nmsgid \"\"\n\"`#server-1260 <https://projects.tigase.net/issue/server-1260>`__: \"\n\"UserConnectedEvent should be a cluster event\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:106\nmsgid \"\"\n\"`#server-1261 <https://projects.tigase.net/issue/server-1261>`__: Revise \"\n\"and improve EventBus developer guide\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:108\nmsgid \"\"\n\"`#server-1269 <https://projects.tigase.net/issue/server-1269>`__: SSL \"\n\"issues are hidden by default making it difficult to identify\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:110\nmsgid \"\"\n\"`#server-1273 <https://projects.tigase.net/issue/server-1273>`__: Add \"\n\"option to limit number of concurrently connected resources\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:112\nmsgid \"\"\n\"`#server-1277 <https://projects.tigase.net/issue/server-1277>`__: Fix \"\n\"HUGE out queue in StreamManagementIOProcessor\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:114\nmsgid \"\"\n\"`#server-1278 <https://projects.tigase.net/issue/server-1278>`__: NPE in \"\n\"StreamManagementIOProcessor.serviceStopped\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:116\nmsgid \"\"\n\"`#server-1282 <https://projects.tigase.net/issue/server-1282>`__: \"\n\"XMPPProcessorAbstract.processToUserPacket() responds to IQ result with \"\n\"error\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:118\nmsgid \"\"\n\"`#server-1284 <https://projects.tigase.net/issue/server-1284>`__: Add \"\n\"validation to JabberIqAuth\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:120\nmsgid \"\"\n\"`#server-1285 <https://projects.tigase.net/issue/server-1285>`__: Wrong \"\n\"field type for XEP-0157 entries\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:122\nmsgid \"\"\n\"`#server-1290 <https://projects.tigase.net/issue/server-1290>`__: Improve\"\n\" StringPrep to actually forbid space in localpart/domain as per rfc7622\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:124\nmsgid \"\"\n\"`#server-1292 <https://projects.tigase.net/issue/server-1292>`__: TLS \"\n\"connectivity issue with search.jabber.network\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:126\nmsgid \"\"\n\"`#server-1297 <https://projects.tigase.net/issue/server-1297>`__: Add \"\n\"option to push plugin that would allow to overwrite unencrypted part in \"\n\"(OMEMO) encrypted messages\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:128\nmsgid \"\"\n\"`#server-1303 <https://projects.tigase.net/issue/server-1303>`__: Better \"\n\"handling of \\\"The target is unavailable at this time.\\\" / \"\n\"PacketInvalidTypeException\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:130\nmsgid \"\"\n\"`#server-1305 <https://projects.tigase.net/issue/server-1305>`__: Allow \"\n\"creation of admin user (if not exist) during ``upgrade-schema`` task\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:132\nmsgid \"\"\n\"`#server-1306 <https://projects.tigase.net/issue/server-1306>`__: Fix \"\n\"farge amount of direct memory being used.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:134\nmsgid \"\"\n\"`#server-1307 <https://projects.tigase.net/issue/server-1307>`__: Fix \"\n\"disconnection on MAM sync\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:136\nmsgid \"\"\n\"`#extras-3 <https://projects.tigase.net/issue/extras-3>`__: Add AWS \"\n\"logback and documentation how to use it\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:138\nmsgid \"\"\n\"`#extras-4 <https://projects.tigase.net/issue/extras-4>`__: Unescape and \"\n\"normalise logs in mail notifications before sending them\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:140\nmsgid \"\"\n\"`#extras-7 <https://projects.tigase.net/issue/extras-7>`__: Add email \"\n\"validation during in-band-registration; better handling of mail sending \"\n\"exceptions regarding to non-existent addresses\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:142\nmsgid \"\"\n\"`#extras-9 <https://projects.tigase.net/issue/extras-9>`__: Deprecate \"\n\"mDNS implementation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:144\nmsgid \"\"\n\"`#serverdist-8 <https://projects.tigase.net/issue/serverdist-8>`__: \"\n\"Remove DNS resolution part from XEP-0156 implementation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:2\nmsgid \"Tigase XMPP Server 8.1.0 Change notes and announcement\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:8\nmsgid \"More XMPP extensions\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:10\nmsgid \"\"\n\"Following XMPP guidelines specified in *Compliance Suites* a number of \"\n\"extensions was included in this release:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:12\nmsgid \"\"\n\"**XEP-0157**: Contact Addresses for XMPP Services (`server-995 \"\n\"<https://projects.tigase.net/issue/server-995>`__) that can be configured\"\n\" on per VHost basis (`server-1015 \"\n\"<https://projects.tigase.net/issue/server-1015>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:14\nmsgid \"\"\n\"**XEP-0398**: User Avatar to vCard-Based Avatars Conversion (`server-1017\"\n\" <https://projects.tigase.net/issue/server-1017>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:16\nmsgid \"\"\n\"**XEP-0156**: Discovering Alternative XMPP Connection Methods - Tigase \"\n\"already supported handling DNS queries and standardised our \"\n\"``webservice`` to XEP-0156 (`http-76 \"\n\"<https://projects.tigase.net/issue/http-76>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:18\nmsgid \"\"\n\"**XEP-0410**: MUC Self-Ping (Schrödinger’s Chat) (`muc-122 \"\n\"<https://projects.tigase.net/issue/http-76>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:20\nmsgid \"\"\n\"**XEP-0153**: vCard-Based Avatars - added support for setting **vCard \"\n\"avatar for MUC rooms** (`muc-112 \"\n\"<https://projects.tigase.net/issue/http-76>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:22\nmsgid \"\"\n\"**XEP-0411**: Bookmarks Conversion (`pubsub-79 \"\n\"<https://projects.tigase.net/issue/http-76>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:24\nmsgid \"\"\n\"**XEP-0157**: Contact Addresses for XMPP Services (`server-995 \"\n\"<https://projects.tigase.net/issue/server-995>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:29\nmsgid \"Improved connectivity with other servers\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:31\nmsgid \"\"\n\"``SASL-EXTERNAL`` mechanism was added for server-to-server (federated, \"\n\"s2s) connections greatly improving compliance with XMPP network. It’s \"\n\"possible to use both SASL-EXTERNAL and Diallback depending on support in \"\n\"other servers.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:36\nmsgid \"Better security & privacy\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:38\nmsgid \"\"\n\"When it comes to connectivity, Tigase XMPP Server sported **Hardened \"\n\"Mode** that adjusted networking security settings (supported protocols, \"\n\"cipher suites and keys' length where applicable). We decided include \"\n\"3-level configuration option for **Hardened Mode** (roughly following \"\n\"*Mozilla’s SSL Configuration Generator*): ``relaxed``, ``secure`` \"\n\"(default) and ``strict`` and to further eliminate cipher suites that are \"\n\"currently considered insecure.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:40\nmsgid \"\"\n\"We also enabled by default our anti-spam plugin and because we like all-\"\n\"things-extensible we created a guide how to create your own pluggable \"\n\"filters for anti-spam-plugin.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:45\nmsgid \"Multiple domains (VHosts) support is even better\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:47\nmsgid \"\"\n\"It was always quite easy to configure and serve multiple domains in \"\n\"Tigase XMPP Server. In this release we made it even better! First of all \"\n\"- we included ``Default`` VHost item, which allows configuring global \"\n\"defaults for the installation on the fly without having to change \"\n\"configuration files and restart the instance.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:49\nmsgid \"\"\n\"Internally, we introduced *VHost Extensions* - a mechanism that allows \"\n\"easy addition of configurable options that can be set on per-domain \"\n\"basis.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:51\nmsgid \"\"\n\"On top of that we reworked how SSL certificates are handled (especially \"\n\"wildcard ones) and now they are loaded and assigned to correct domain \"\n\"automatically - no need to configure *star*-certificates manually \"\n\"anymore.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:56\nmsgid \"Mobile First\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:58\nmsgid \"\"\n\"Notifications send to mobile applications via Apple’s and Google’s push \"\n\"servers using **Tigase’s PUSH component** are now encrypted (`#push-25 \"\n\"<https://projects.tigase.net/issue/push-25>`__), requires compatible \"\n\"clients)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:60\nmsgid \"\"\n\"MUC component now allows users to register permanent nickname, which \"\n\"makes it possible to receive PUSH notifications even if our client \"\n\"disconnects and is offline (`#muc-115 \"\n\"<https://projects.tigase.net/issue/muc-115>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:65\nmsgid \"Installation & management\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:67\nmsgid \"\"\n\"The (web) installer was simplified making setting up and configuring \"\n\"Tigase even easier (`#http-78 \"\n\"<https://projects.tigase.net/issue/http-78>`__) - now it’s only needed to\"\n\" select desired database, provide it’s details and eventually adjust \"\n\"which components and plugins should be enabled or disabled, but we \"\n\"believe that provided defaults should work well in most of the cases.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:69\nmsgid \"\"\n\"After the installation and startup, it’s possible to see basic instance \"\n\"state via web browser either opening ``/server/`` endpoint (`#server-1164\"\n\" <https://projects.tigase.net/issue/server-1164>`__), or local file from \"\n\"``logs/server-info.html``) and manage the installation using Admin WebUI,\"\n\" that received slight visual face-lift (`#http-90 \"\n\"<https://projects.tigase.net/issue/http-90>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:74\nmsgid \"Noteworthy\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:76\nmsgid \"\"\n\"Startup time was significantly reduced due to improvements of creating \"\n\"repository pools (`#server-1149 \"\n\"<https://projects.tigase.net/issue/server-1149>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:78\nmsgid \"\"\n\"Multi-thread, highly concurrent script execution was improved \"\n\"(`#server-1154 <https://projects.tigase.net/issue/server-1154>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:80\nmsgid \"\"\n\"StreamManagement was available, but in this version we decided to enabled\"\n\" it by default.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:82\nmsgid \"\"\n\"More places offers support for `XEP-0059: Result Set Management \"\n\"<https://xmpp.org/extensions/xep-0059.html>`__ - namely PubSub nodes \"\n\"discovery and ``jabber:iq:serach``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:84\nmsgid \"\"\n\"`Publishing Options <https://xmpp.org/extensions/xep-0060.html#publisher-\"\n\"publish-options>`__ were added to PubSub (`#pubsub-75 \"\n\"<https://projects.tigase.net/issue/pubsub-75>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:304\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:87\nmsgid \"New Minor Features & Behavior Changes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:89\nmsgid \"\"\n\"`server-918 <https://projects.tigase.net/issue/server-918>`__: AWS obtain\"\n\" public IP and/or DNS address of the EC2 instance\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:91\nmsgid \"\"\n\"`server-985 <https://projects.tigase.net/issue/server-985>`__: Add \"\n\"support for SCRAM-SHA-512(-PLUS)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:93\nmsgid \"\"\n\"`spam-8 <https://projects.tigase.net/issue/spam-8>`__: Enable spam \"\n\"processor by default\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:95\nmsgid \"\"\n\"`server-1012 <https://projects.tigase.net/issue/server-1012>`__: \"\n\"UserDomainFilter.groovy fails to load\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:97\nmsgid \"\"\n\"`server-1014 <https://projects.tigase.net/issue/server-1014>`__: Can’t \"\n\"upgrade from 8.0.0GA to 8.1.0-SNAPSHOT\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:99\nmsgid \"\"\n\"`server-798 <https://projects.tigase.net/issue/server-798>`__: Limit \"\n\"number of messages that are stored in DB per user within a period of time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:101\nmsgid \"\"\n\"`server-827 <https://projects.tigase.net/issue/server-827>`__: Seperate \"\n\"Component-based statistics\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:103\nmsgid \"\"\n\"`server-1026 <https://projects.tigase.net/issue/server-1026>`__: NPE: in \"\n\"JabberIqRegister/EmailConfirmationSender\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:105\nmsgid \"\"\n\"`pubsub-82 <https://projects.tigase.net/issue/pubsub-82>`__: NPE in \"\n\"RetrieveItemsModule\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:107\nmsgid \"\"\n\"`tigaseim-78 <https://projects.tigase.net/issue/tigaseim-78>`__: IPv6 \"\n\"connectivity issue\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:109\nmsgid \"\"\n\"`server-239 <https://projects.tigase.net/issue/server-239>`__: OSGi mode \"\n\"- exceptions in logs\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:111\nmsgid \"\"\n\"`server-1020 <https://projects.tigase.net/issue/server-1020>`__: Enable \"\n\"stream management by default\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:113\nmsgid \"\"\n\"`pubsub-83 <https://projects.tigase.net/issue/pubsub-83>`__: NPE in \"\n\"PublishItemModule\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:115\nmsgid \"\"\n\"`pubsub-81 <https://projects.tigase.net/issue/pubsub-81>`__: Exception \"\n\"during execution of event: \"\n\"tigase.pubsub.modules.PresenceCollectorModule.PresenceChangeEvent\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:117\nmsgid \"\"\n\"`server-1021 <https://projects.tigase.net/issue/server-1021>`__: NPE: \"\n\"Cannot update BruteForceLocker\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:119\nmsgid \"\"\n\"`server-826 <https://projects.tigase.net/issue/server-826>`__: \"\n\"UserRepository caches force synchronization even if caching is disabled\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:121\nmsgid \"\"\n\"`server-958 <https://projects.tigase.net/issue/server-958>`__: Add \"\n\"timeout for opened TCP connections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:123\nmsgid \"\"\n\"`server-1029 <https://projects.tigase.net/issue/server-1029>`__: Read \"\n\"receipients are not copied via carbons\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:125\nmsgid \"\"\n\"`server-1015 <https://projects.tigase.net/issue/server-1015>`__: Allow \"\n\"configuring XEP-0157: Contact Addresses on per VHost basis\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:127\nmsgid \"\"\n\"`pubsub-65 <https://projects.tigase.net/issue/pubsub-65>`__: RSM and \"\n\"jabber:search for pubsub discovery\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:129\nmsgid \"\"\n\"`server-1030 <https://projects.tigase.net/issue/server-1030>`__: NPE in \"\n\"VCardTemp when processing initial presence\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:131\nmsgid \"\"\n\"`http-72 <https://projects.tigase.net/issue/http-72>`__: Change Content-\"\n\"Disposition from attachment to inline\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:133\nmsgid \"\"\n\"`server-1045 <https://projects.tigase.net/issue/server-1045>`__: NPE in \"\n\"DiscoExtensionsForm\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:135\nmsgid \"\"\n\"`server-1048 <https://projects.tigase.net/issue/server-1048>`__: Update \"\n\"parent pom and information about suggested JDK\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:137\nmsgid \"\"\n\"`push-23 <https://projects.tigase.net/issue/push-23>`__: [JDK12] Can’t \"\n\"establish encrypted connection with Push/FCM\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:139\nmsgid \"\"\n\"`server-978 <https://projects.tigase.net/issue/server-978>`__: Improve \"\n\"VHost configuration / extending\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:141\nmsgid \"\"\n\"`server-1068 <https://projects.tigase.net/issue/server-1068>`__: Improve \"\n\"LogFormat readability (and maybe performance)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:143\nmsgid \"\"\n\"`server-1070 <https://projects.tigase.net/issue/server-1070>`__: Improve \"\n\"privacy list loggging\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:145\nmsgid \"\"\n\"`server-1071 <https://projects.tigase.net/issue/server-1071>`__: NPE in \"\n\"IOService.accept\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:147\nmsgid \"\"\n\"`server-710 <https://projects.tigase.net/issue/server-710>`__: \"\n\"Registration improvements\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:149\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:249\nmsgid \"\"\n\"`pubsub-79 <https://projects.tigase.net/issue/pubsub-79>`__: XEP-0411: \"\n\"Bookmarks Conversion\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:151\nmsgid \"\"\n\"`pubsub-75 <https://projects.tigase.net/issue/pubsub-75>`__: Add support \"\n\"for Publishing Options\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:153\nmsgid \"\"\n\"`server-1017 <https://projects.tigase.net/issue/server-1017>`__: \"\n\"XEP-0398: User Avatar to vCard-Based Avatars Conversion\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:155\nmsgid \"\"\n\"`server-994 <https://projects.tigase.net/issue/server-994>`__: Add server\"\n\" support for Entity Capabilities: Stream Feature\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:157\nmsgid \"\"\n\"`server-995 <https://projects.tigase.net/issue/server-995>`__: XEP-0157: \"\n\"Contact Addresses for XMPP Services\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:159\nmsgid \"\"\n\"`http-76 <https://projects.tigase.net/issue/http-76>`__: Standardise DNS \"\n\"webservice to XEP-0156\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:161\nmsgid \"\"\n\"`server-1109 <https://projects.tigase.net/issue/server-1109>`__: Add \"\n\"recommended JDK version to documentation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:163\nmsgid \"\"\n\"`push-28 <https://projects.tigase.net/issue/push-28>`__: Non-tigase \"\n\"notifications should use high priority (APNS)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:165\nmsgid \"\"\n\"`server-1114 <https://projects.tigase.net/issue/server-1114>`__: Can’t \"\n\"register on sure.im with StorkIM\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:167\nmsgid \"\"\n\"`server-1005 <https://projects.tigase.net/issue/server-1005>`__: Flatten \"\n\"schema to match versioning document\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:169\nmsgid \"\"\n\"`server-1116 <https://projects.tigase.net/issue/server-1116>`__: \"\n\"account_status is not checked\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:171\nmsgid \"\"\n\"`server-1074 <https://projects.tigase.net/issue/server-1074>`__: Hardened\"\n\" Mode improvements\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:173\nmsgid \"\"\n\"`server-1125 <https://projects.tigase.net/issue/server-1125>`__: \"\n\"StatsDumper.groovy doesn’t work in documentation in 8.x\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:175\nmsgid \"\"\n\"`http-85 <https://projects.tigase.net/issue/http-85>`__: Pasword resset \"\n\"doesn’t work\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:177\nmsgid \"\"\n\"`server-1128 <https://projects.tigase.net/issue/server-1128>`__: Possible\"\n\" vulnerability in XML parser\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:179\nmsgid \"\"\n\"`server-1130 <https://projects.tigase.net/issue/server-1130>`__: NPE i \"\n\"JabberIqAuth\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:181\nmsgid \"\"\n\"`http-84 <https://projects.tigase.net/issue/http-84>`__: Configurable \"\n\"``resetPassword`` endpoint hostname\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:183\nmsgid \"\"\n\"`server-1129 <https://projects.tigase.net/issue/server-1129>`__: BOSH \"\n\"timeouts on GET requests\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:185\nmsgid \"\"\n\"`prv-436 <https://projects.tigase.net/issue/prv-436>`__: Conversations \"\n\"compliance - contact developers\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:187\nmsgid \"\"\n\"`server-1100 <https://projects.tigase.net/issue/server-1100>`__: CAAS and\"\n\" WS testers fail to connect to wss://tigase.im:5291\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:189\nmsgid \"\"\n\"`server-1047 <https://projects.tigase.net/issue/server-1047>`__: Add \"\n\"SASL-EXTERNAL on s2s conections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:191\nmsgid \"\"\n\"`server-1103 <https://projects.tigase.net/issue/server-1103>`__: High \"\n\"priority PUSH notifications are sent for all messages\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:193\nmsgid \"\"\n\"`pubsub-93 <https://projects.tigase.net/issue/pubsub-93>`__: NPE in \"\n\"CapsChangeEvent\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:195\nmsgid \"\"\n\"`server-1137 <https://projects.tigase.net/issue/server-1137>`__: Don’t \"\n\"require setting JAVA_HOME to start server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:197\nmsgid \"\"\n\"`server-1136 <https://projects.tigase.net/issue/server-1136>`__: upgrade-\"\n\"schema --help not available\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:199\nmsgid \"\"\n\"`utils-19 <https://projects.tigase.net/issue/utils-19>`__: tigase-utils \"\n\"doesn’t compile with JDK12\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:201\nmsgid \"\"\n\"`server-1138 <https://projects.tigase.net/issue/server-1138>`__: Schema \"\n\"files are not sorted correctly during loading\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:203\nmsgid \"\"\n\"`pubsub-98 <https://projects.tigase.net/issue/pubsub-98>`__: Resources \"\n\"with emoji chars are causing issues with MySQL backend\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:205\nmsgid \"\"\n\"`server-1110 <https://projects.tigase.net/issue/server-1110>`__: \"\n\"Disabling TLS in VHost configuration doesn’t work\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:207\nmsgid \"\"\n\"`server-1078 <https://projects.tigase.net/issue/server-1078>`__: Don’t \"\n\"send root CA certificate in chain\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:209\nmsgid \"\"\n\"`server-1113 <https://projects.tigase.net/issue/server-1113>`__: Don’t \"\n\"advertise SASL-EXTERNAL if own certificate is not valid\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:211\nmsgid \"\"\n\"`http-78 <https://projects.tigase.net/issue/http-78>`__: Simplify \"\n\"installer\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:213\nmsgid \"\"\n\"`server-1133 <https://projects.tigase.net/issue/server-1133>`__: Not able\"\n\" to connect via S2S to server with incorrect SSL certificate\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:215\nmsgid \"\"\n\"`serverdistribution-2 \"\n\"<https://projects.tigase.net/issue/serverdistribution-2>`__: MUC upgrade \"\n\"not linked correctly in global tigase guide\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:217\nmsgid \"\"\n\"`server-1149 <https://projects.tigase.net/issue/server-1149>`__: Reduce \"\n\"startup time with a lot of database connections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:219\nmsgid \"\"\n\"`server-1148 <https://projects.tigase.net/issue/server-1148>`__: \\\"ERROR!\"\n\" Component <x> schema version is not loaded in the database or it is \"\n\"old!\\\" during shutdown\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:221\nmsgid \"\"\n\"`server-1153 <https://projects.tigase.net/issue/server-1153>`__: Refactor\"\n\" Credentials related ``username`` to ``credentialId`` to avoid confussion\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:223\nmsgid \"\"\n\"`servers-312 <https://projects.tigase.net/issue/servers-312>`__: No \"\n\"cluster connection to send a packet\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:225\nmsgid \"\"\n\"`server-1154 <https://projects.tigase.net/issue/server-1154>`__: Multi-\"\n\"thread script execution yields wrong results\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:227\nmsgid \"\"\n\"`servers-294 <https://projects.tigase.net/issue/servers-294>`__: Can’t \"\n\"connect from tigase.im to rsocks.net\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:229\nmsgid \"\"\n\"`server-1111 <https://projects.tigase.net/issue/server-1111>`__: Can’t \"\n\"establish s2s to upload.pouet.ovh\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:231\nmsgid \"\"\n\"`server-1143 <https://projects.tigase.net/issue/server-1143>`__: S2S \"\n\"connectivity issue with OpenFire when SASL external is used\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:233\nmsgid \"\"\n\"`servers-309 <https://projects.tigase.net/issue/servers-309>`__: Issue \"\n\"when connecting to xabber.org: not-authorized: self signed certificate\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:235\nmsgid \"\"\n\"`tigaseim-80 <https://projects.tigase.net/issue/tigaseim-80>`__: Siskin \"\n\"IM push server is not accessible\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:237\nmsgid \"\"\n\"`server-1080 <https://projects.tigase.net/issue/server-1080>`__: After \"\n\"updating certificate via ad-hoc/rest only main certificate is updated\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:239\nmsgid \"\"\n\"`http-88 <https://projects.tigase.net/issue/http-88>`__: Improve REST \"\n\"documentation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:241\nmsgid \"\"\n\"`http-87 <https://projects.tigase.net/issue/http-87>`__: \\\"request accept\"\n\" time exceeded\\\" for every request when using \"\n\"``JavaStandaloneHttpServer``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:243\nmsgid \"\"\n\"`server-1151 <https://projects.tigase.net/issue/server-1151>`__: \"\n\"BruteForceLockerExtension (and possibly others) settings are not \"\n\"correctly retrieved\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:245\nmsgid \"\"\n\"`http-89 <https://projects.tigase.net/issue/http-89>`__: Drop \"\n\"result/error packages received by HTTP-API if no connection present to \"\n\"write response to\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:247\nmsgid \"\"\n\"`pubsub-99 <https://projects.tigase.net/issue/pubsub-99>`__: \"\n\"Notifications are not sent for +notify from nodes with whitelist access \"\n\"mode\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:251\nmsgid \"\"\n\"`server-1157 <https://projects.tigase.net/issue/server-1157>`__: SCRAM-\"\n\"SHA512 not working\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:253\nmsgid \"\"\n\"`server-1159 <https://projects.tigase.net/issue/server-1159>`__: Improve \"\n\"handling establishing and terminating of the session\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:255\nmsgid \"\"\n\"`server-1152 <https://projects.tigase.net/issue/server-1152>`__: Cleanup \"\n\"warnings from JDBCMsgRepository\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:257\nmsgid \"\"\n\"`server-1112 <https://projects.tigase.net/issue/server-1112>`__: Fallback\"\n\" to diallback if SASL-EXTERNAL fails\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:259\nmsgid \"\"\n\"`servers-292 <https://projects.tigase.net/issue/servers-292>`__: S2S \"\n\"connectivity issues\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:261\nmsgid \"\"\n\"`acspubsub-19 <https://projects.tigase.net/issue/acspubsub-19>`__: REST \"\n\"execution fails on other nodes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:263\nmsgid \"\"\n\"`server-1145 <https://projects.tigase.net/issue/server-1145>`__: Race \"\n\"condition during storing/loading of offline messages\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:265\nmsgid \"\"\n\"`http-90 <https://projects.tigase.net/issue/http-90>`__: Add direct links\"\n\" to most useful task in AdminUI main page\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:267\nmsgid \"\"\n\"`spam-10 <https://projects.tigase.net/issue/spam-10>`__: Add \"\n\"documentation for creation of a custom filter\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:269\nmsgid \"\"\n\"`server-1163 <https://projects.tigase.net/issue/server-1163>`__: Review \"\n\"and update ``SASL Custom Mechanisms and Configuration`` documentation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:271\nmsgid \"\"\n\"`server-1164 <https://projects.tigase.net/issue/server-1164>`__: After-\"\n\"installation report - installation status\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:273\nmsgid \"\"\n\"`systems-76 <https://projects.tigase.net/issue/systems-76>`__: Fix issue \"\n\"with StackOverflow due to recursive call in TLSIO; improve debug log\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:275\nmsgid \"\"\n\"`server-1082 <https://projects.tigase.net/issue/server-1082>`__: Sec-\"\n\"WebSocket-Accept not calculated correctly\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:277\nmsgid \"\"\n\"`server-1083 <https://projects.tigase.net/issue/server-1083>`__: Messages\"\n\" sent to full jid are returned with error\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:279\nmsgid \"\"\n\"`push-25 <https://projects.tigase.net/issue/push-25>`__: Add support for \"\n\"sending encrypted PUSHes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:281\nmsgid \"\"\n\"`server-1085 <https://projects.tigase.net/issue/server-1085>`__: Improve \"\n\"retrieval of values for all keys in a node in UserRepository\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:283\nmsgid \"\"\n\"`muc-115 <https://projects.tigase.net/issue/muc-115>`__: Add support for \"\n\"MUC and offline message delivery\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:285\nmsgid \"\"\n\"`muc-122 <https://projects.tigase.net/issue/muc-122>`__: XEP-0410: MUC \"\n\"Self-Ping (Schrödinger’s Chat)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:287\nmsgid \"\"\n\"`muc-112 <https://projects.tigase.net/issue/muc-112>`__: Support for \"\n\"setting vCard avatar for room\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:289\nmsgid \"\"\n\"`http-83 <https://projects.tigase.net/issue/http-83>`__: Issue with \"\n\"multithreading access to HttpExchange instance\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:291\nmsgid \"\"\n\"`httpapijetty-3 <https://projects.tigase.net/issue/httpapijetty-3>`__: \"\n\"Support for HTTP/2\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:293\nmsgid \"\"\n\"`httpapijetty-6 <https://projects.tigase.net/issue/httpapijetty-6>`__: \"\n\"Update Jetty version\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:2\nmsgid \"Tigase XMPP Server 8.0.0 Change notes and announcement\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:8\nmsgid \"Kernel and beans configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:10\nmsgid \"\"\n\"Tigase now operates using a Kernel and Beans style of programming. What \"\n\"does this mean for Tigase and You? Good news, really. Tigase XMPP Server \"\n\"is now working as a Kernel program, which will operate on it’s own and \"\n\"handle all the core functionality of the server. Component, and non-\"\n\"essential functionality will now be loaded as Beans. As a user, your \"\n\"experience will not change all that much. However, beans can be loaded \"\n\"and unloaded without having to restart Tigase, meaning that the program \"\n\"will behave more dynamically. This means a smaller footprint on memory on\"\n\" resources when components are not needed, and longer uptimes without \"\n\"having to rest art the program! This also allows for greater flexibility \"\n\"for Tigase XMPP Server to be better customized for unique solutions.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:14\nmsgid \"New Configuration File Format\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:16\nmsgid \"\"\n\"With the change of Tigase to a Kernel and Beans style of programming, we \"\n\"have also changed how the configuration file is managed. Although you \"\n\"will still edit the ``config.tdsl`` file like a plaintext file, a new \"\n\"style of formatting will be used known as DSL. Domain Specific Language \"\n\"may add more lines, but is a cleaner format, and provides a more secure \"\n\"configuration design since validation of the configuration is done at the\"\n\" domain level. For more information on this format and how to configure \"\n\"Tigase, visit `DSL Configuration Guide <#dslConfig>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:20\nmsgid \"Cluster Node Shutdown Changes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:22\nmsgid \"\"\n\"Starting with Tigase XMPP Server 8.0.0, users connected on clustered \"\n\"nodes will be able use a ``see-other-host`` strategy when a node is being\"\n\" shutdown. **Note: This may not be compatible with all clients.** The Ad-\"\n\"hoc command is designed for a graceful shutdown of cluster nodes as a \"\n\"groovy script ``Shutdown.groovy``. This script also allows for the \"\n\"-timeout setting which will delay shutdown of the node, and alert all \"\n\"users (via a headline message) that the server will be shutdown after a \"\n\"time. User clients that are compatible with the command will then detect \"\n\"other connected clusters and maintain their connections.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:24\nmsgid \"\"\n\"If the command is being sent to shut down the whole cluster, no ``see-\"\n\"other-host`` implementation will be sent, however timeout settings may \"\n\"still be used.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:26\nmsgid \"\"\n\"The script may be activated by an ad-hoc command, or sent using REST from\"\n\" remote or Tigase Admin UI.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:31\nmsgid \"Significant cleanup of code and repositories\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:33\nmsgid \"\"\n\"Multiple changes have been made to the structure and coding for v8, many \"\n\"related to trimming size of repositories and old calls. Some of these \"\n\"improvements are listed here:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:35\nmsgid \"Empty JavaDocs that do not convey values have been removed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:37\nmsgid \"\"\n\"All code is reformatted to be compliant with out `codestyle guidelines \"\n\"<#tigaseCodeStyle>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:39\nmsgid \"\"\n\"Calls to ``System.out.print*()`` and ``printStackTrace()`` have been \"\n\"removed from code.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:41\nmsgid \"Depreciated and unused classes have been removed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:46\nmsgid \"BouncyCastle being used for StartTLS\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:48\nmsgid \"\"\n\"`BouncyCastle <https://www.bouncycastle.org/java.html>`__ Crypto API has \"\n\"now been employed to handle StartTLS negotiation. By doing this, Tigase \"\n\"now supports ``tls-unique`` within the SCRAM PLUS authentication \"\n\"implementation. This API is may be employed by calling the class in your \"\n\"configuration file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:56\nmsgid \"The BouncyCastle classes are included in the dist-max archives.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:61\nmsgid \"default-virtual-host property changes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:63\nmsgid \"\"\n\"Default virtual hosts property is now able to be configured only as a \"\n\"domain name instead of the list of virtual host domains with options. \"\n\"Additional virtual host domains and their options need to be configured \"\n\"using ad-hoc commands or web AdminUI. Reference `Virtual-Hosts \"\n\"Configuration <#virtHosts>`__ for more details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:68\nmsgid \"All artifacts are signed\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:70\nmsgid \"\"\n\"Since work began on v8.0.0 Tigase has required that all changes to Tigase\"\n\" XMPP Server and dependencies be signed with known certificates. This \"\n\"version marks the first to be totally signed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:75\nmsgid \"Scaled Down Installation Methods\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:77\nmsgid \"\"\n\"We have cleaned up installation methods for Tigase and now recommend the \"\n\"use of web-installer method. IzPack installer (files ``tigase-\"\n\"server-<version>-b<build>.jar`` installation methods have been removed \"\n\"and will no longer be produced for v8.0.0 and later. Manual installation \"\n\"is still available for those unable to use HTTP or browser access. Visit \"\n\"our `Quick Start <#quickstart>`__ guide for instructions on these other \"\n\"methods.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:82\nmsgid \"Emojis now supported on Tigase XMPP Servers\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:84\nmsgid \"\"\n\"Emojis are now supported on MySQL databases, however some settings may be\"\n\" need to be changed, although they won’t affect existing databases. \"\n\"`Visit this section <#emojisupportSQL>`__ for details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:89\nmsgid \"XEP-0215 External Service Discovery now supported\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:91\nmsgid \"\"\n\"Tigase now supports `XEP-0215 - External Service Discovery \"\n\"<https://xmpp.org/extensions/xep-0215.html>`__ allowing Tigase to \"\n\"discover services that are not available VIA the XMPP Protocol. For setup\"\n\" and configuration information visit `External Service Discovery \"\n\"Component <#_tigase_external_service_discovery>`__ documentation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:96\nmsgid \"XEP-0313 Message Archive Management now supported\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:98\nmsgid \"\"\n\"`XEP-0313 - Message Archive Management \"\n\"<https://xmpp.org/extensions/xep-0313.html>`__ is now supported by Tigase\"\n\" featuring custom enhancements like full-text search and searching by \"\n\"tags. MAM requires Tigase’s message archive to be enabled in the \"\n\"``config.tdsl`` file, and the schema (XEP-0136 or XEP-0313) must be \"\n\"configured in session manager settings. To turn on MAM, see configuration\"\n\" guide `located here <#_support_for_mam>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:103\nmsgid \"XEP-0363 HTTP File Upload now supported\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:105\nmsgid \"\"\n\"`XEP-0363 - HTTP File Upload \"\n\"<https://xmpp.org/extensions/xep-0363.html>`__ is now supported using \"\n\"Tigase HTTP API component now allowing for a more robust one-to-many file\"\n\" uploading option. Configuration details are available at the `HTTP File \"\n\"Upload Component <#XEP0363>`__ section of documentation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:110\nmsgid \"Startup now uses bootstrapping\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:112\nmsgid \"\"\n\"Tigase now uses bootstrapping to startup, which will load configuration \"\n\"from ``config.tdsl`` file like before. Then Tigase will begin it’s normal\"\n\" operations with the configuration options. All startup functions for \"\n\"Tigase will now run under the ``bootstrap`` bean.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:117\nmsgid \"CAPTCHA system now available for in-band registration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:119\nmsgid \"\"\n\"`XEP-0077 In band registration \"\n\"<https://xmpp.org/extensions/xep-0077.html>`__ can use Data Forms as an \"\n\"option to process new registrations. Now you can secure these \"\n\"registrations by employing a CAPTCHA solution. By enabling this option \"\n\"you can reduce the number of potential spammers and bots on your server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:124\nmsgid \"Schema changes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:126\nmsgid \"\"\n\"Now each component has it’s own schema for databases, they are no longer \"\n\"tied into Tigase XMPP server versions making changes and updates to \"\n\"individual components easier, and may not disrupt all users not using \"\n\"certain components. See the `schema update section <#schemaChangev800>`__\"\n\" for more details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:131\nmsgid \"Shrinkable Statistics History\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:133\n#, python-format\nmsgid \"\"\n\"Statistics history can now be automatically made smaller if a systems \"\n\"memory resources are above a certain amount. By default this is enabled \"\n\"and will trigger when over 95% of memory is in use. Half of all existing \"\n\"entries will be removed at this time. The same pattern will continue to \"\n\"halve the available records every time the threshold is met. A hard-set \"\n\"minimum of 5 entries is set, so you will always have the last 5 entries. \"\n\"This setting may be adjusted by adding the following setting to your \"\n\"``config.tdsl`` file and adjusting the integer value:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:144\nmsgid \"Statistics now available for all modules\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:146\nmsgid \"For any bean, you may enable statistics by using the following\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:157\nmsgid \"Spam Protection\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:159\nmsgid \"\"\n\"Tigase XMPP Server v8.0.0 now includes some efforts to prevent spam bot \"\n\"accounts from running on servers.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:162\nmsgid \"Account Registration Limits Expanded\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:164\nmsgid \"\"\n\"Account registration limits have been expanded and now you can set \"\n\"separate counters, or configure components individually for their own \"\n\"limits. Visit `this section <#accountRegLimit>`__ for configuration \"\n\"details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:167\nmsgid \"\"\n\"Accounts created using in-band registration now will use confirmation \"\n\"E-mail\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:169\nmsgid \"\"\n\"In an effort to create a more secure method for implementing \"\n\"``JabberIqRegister`` Tigase XMPP Server will now require the use of a \"\n\"confirmation E-mail by default in the process. The E-mail must be valid, \"\n\"and accounts will be made into pending status until a user clicks the \"\n\"generated URI in the E-mail and activates the account. This is a plugin \"\n\"and must be enabled in the ``config.tdsl`` file by using the following \"\n\"code:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:176\nmsgid \"Further Spam prevention\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:178\nmsgid \"\"\n\"Tigase-spam component is now in ``dist-max`` distribution package, and \"\n\"has a number of features described here `in this section \"\n\"<#tigase_spam_filter>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:183\nmsgid \"Changes in password storage\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:185\nmsgid \"\"\n\"Before version 8.0.0, user passwords were stored in plaintext in the \"\n\"``user_pw`` database field within ``tig_users`` table, but in plaintext. \"\n\"It was possible to enable storage of the MD5 hash of the password \"\n\"instead, however this limited authentication mechanism SASL PLAIN only. \"\n\"However an MD5 hash of a password is not really a secure method as it is \"\n\"possible to revert this mechanism using rainbow tables.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:187\nmsgid \"\"\n\"Therefore, we decided to change this and store only encrypted versions of\"\n\" a password in ``PBKDF2`` form which can be easily used for ``SCRAM-\"\n\"SHA-1`` authentication mechanism or ``SCRAM-SHA-256``. ``SASL PLAIN`` \"\n\"mechanism can also use these encrypted passwords.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:189\nmsgid \"\"\n\"The storage of encrypted passwords is now enabled **by default** in \"\n\"v8.0.0 of Tigase.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:194\nmsgid \"Dynamic TLS Buffer\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:196\nmsgid \"\"\n\"Memory Buffer for TLS no longer remains at highest buffer size needed for\"\n\" the server session. Buffer will now free memory during idle connections.\"\n\" Thus drastically improving program footprint.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:201\nmsgid \"XEP-305 Quickstart now supported\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:203\nmsgid \"\"\n\"It’s now possible to establish connection faster due to implementation of\"\n\" `XEP-0305: XMPP Quickstart \"\n\"<https://xmpp.org/extensions/xep-0305.html>`__ (`#1936 \"\n\"<https://projects.tigase.net/issues?q=Redmine%20ID:%201936>`__). Feature \"\n\"is only available for ``c2s`` Connection Manager (i.e. connections on \"\n\"port 5222) and needs to be enabled in ``config.tdsl``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:214\nmsgid \"Database Timestamps\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:216\nmsgid \"Timestamps in database will be stored using UTC time.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:221\nmsgid \"Config-type properties have changed\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:223\nmsgid \"\"\n\"Config-type is now configured using DSL format. Visit `this section \"\n\"<#configType>`__ for more information. The names of different config-type\"\n\" properties have changed: ``default`` replaces ``--gen-config-def``, \"\n\"``--gen=config-all``, and ``--gen-config-default`` configuration types. \"\n\"``session-manager`` replaces ``--gen-config-sm``. ``connection-managers``\"\n\" replaces ``--gen-config-cs``. ``component`` replaces ``--gen-config-\"\n\"comp``. ``setup`` - is a new type of config created for initial \"\n\"configuration of Tigase XMPP Server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:227\nmsgid \"\"\n\"Old versions are no longer supported, you HAVE to replace old versions \"\n\"with the new ones manually when upgrading to v8.0.0.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:232\nmsgid \"Database Watchdog implemented\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:234\nmsgid \"\"\n\"It is now possible to set connection testing to databases when \"\n\"connections are idle and customize the frequency with which this is done.\"\n\" Visit `this section <#databaseWatchdog>`__ for more details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:239\nmsgid \"Packet statistics expanded\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:241\nmsgid \"\"\n\"Packet statistics both retrieved VIA XMPP and during graceful shutdown \"\n\"have now been separated to a per-XMLNS basis. This may be disabled by \"\n\"adding the following line to ``config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:250\nmsgid \"XEP-0016 Behavior changes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:252\nmsgid \"\"\n\"XEP states that Privacy lists should be used when no user session exists \"\n\"in addition to when there is. Previously, Tigase would only filter \"\n\"results when retrieving messages, allowing blocked users to store offline\"\n\" messages. This has now been changed to reflect the XEP properly, and \"\n\"messages will be filtered while there is no user session. If however, you\"\n\" wish to use the previous version, where offline messages are cached \"\n\"first and then filtered, you may use the following configuration:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:264\nmsgid \"\"\n\"By default, the cache has a limit of 10000 entries, that may be set by \"\n\"using size bean as seen above.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:269\nmsgid \"Access Control List has new ACL modifiers\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:271\nmsgid \"\"\n\"New permissions have been added to ACL including ``DOMAIN_OWNER`` and \"\n\"``DOMAIN_ADMIN`` to reduce permissions checking, and add another level of\"\n\" fine-grained permissions. For more details, please see `Tigase ACL \"\n\"<#accessControlList>`__ configuration for more details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:276\nmsgid \"Option to ignore schema-version check added\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:278\nmsgid \"\"\n\"You can now skip the schema check phase for individual databases. To do \"\n\"this, add the following do the datasource configuration block:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:288\nmsgid \"This will do the following:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:290\nmsgid \"Print a warning during repository startup.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:292\nmsgid \"Skip schema upgrades for the source.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:294\nmsgid \"Skip schema destruction for the source.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:299\nmsgid \"Protection against brute-force attacks\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:301\nmsgid \"\"\n\"Version 8.0.0 improves security by preventing brute-force attacks. \"\n\"Feature needs to be explicitly enabled and configured (on per VHost \"\n\"basis). Detailed configuration is described in `??? \"\n\"<#bruteForcePrevention>`__ (`#8160 \"\n\"<https://projects.tigase.net/issues?q=Redmine%20ID:%208160>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:306\nmsgid \"\"\n\"`#611 <https://projects.tigase.net/issues?q=Redmine%20ID:%20611>`__ \"\n\"Support for Message of the Day is now enabled in Tigase XMPP Server and \"\n\"can be administered using `XEP-0133 Service Administration \"\n\"<http://xmpp.org/extensions/xep-0133.html#set-motd>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:308\nmsgid \"\"\n\"`#1569 <https://projects.tigase.net/issues?q=Redmine%20ID:%201569>`__ Re-\"\n\"implemented XEP-0133 Service Administration Scripts ``4.3 Disable User`` \"\n\"and ``4.4 Re-enable User``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:310\nmsgid \"\"\n\"`#1449 <https://projects.tigase.net/issues?q=Redmine%20ID:%201449>`__ \"\n\"Monitoring modules now works in OSGi mode.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:312\nmsgid \"\"\n\"`#1706 <https://projects.tigase.net/issues?q=Redmine%20ID:%201706>`__ \"\n\"``auto-authorize`` of presence subscriptions can now be set for \"\n\"individual vhosts.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:314\nmsgid \"\"\n\"`#1968 <https://projects.tigase.net/issues?q=Redmine%20ID:%201968>`__ \"\n\"Added a Proxy Wrapper to handle reconnections to database connection pool\"\n\" to help prevent deadlocking threads.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:316\nmsgid \"\"\n\"`#3511 <https://projects.tigase.net/issues?q=Redmine%20ID:%203511>`__ \"\n\"Mechanism responsible for closing XMPP in SessionManager has been changed\"\n\" to process all packets from TCP connection before closing connection.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:318\nmsgid \"\"\n\"`#3802 <https://projects.tigase.net/issues?q=Redmine%20ID:%203802>`__ \"\n\"Implementation and API of LocalEventBus and ClusteredEventBus has been \"\n\"unified and is now available as EventBus.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:320\nmsgid \"\"\n\"`#3918 <https://projects.tigase.net/issues?q=Redmine%20ID:%203918>`__ \"\n\"Session Establishment Advertisement is now optional, bringing session \"\n\"establishment in line with `RFC 6121 \"\n\"<https://tools.ietf.org/html/rfc6121>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:322\nmsgid \"\"\n\"`#4111 <https://projects.tigase.net/issues?q=Redmine%20ID:%204111>`__ \"\n\"Changed input buffer sizing to use a ratio of 2 to 1 based on input \"\n\"capacity. No longer using a constant value.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:324\nmsgid \"\"\n\"`#4212 <https://projects.tigase.net/issues?q=Redmine%20ID:%204212>`__ \"\n\"Database schema files have been flattened and made for better \"\n\"organization.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:326\n#, python-format\nmsgid \"\"\n\"`#4501 <https://projects.tigase.net/issues?q=Redmine%20ID:%204501>`__ \"\n\"``CounterDataFileLogger`` now has an upper limit and will be default be \"\n\"shrunk to 75% if available disk space is 5% or less than 100MB.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:328\nmsgid \"\"\n\"`#4654 <https://projects.tigase.net/issues?q=Redmine%20ID:%204654>`__ \"\n\"PubSub component has been updated and new schema uses UTF-8 encoding when\"\n\" hashing database lookup.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:330\nmsgid \"\"\n\"`#4776 <https://projects.tigase.net/issues?q=Redmine%20ID:%204776>`__ \"\n\"Tigase ``DbSchemaLoader`` now prompts for password if one is missing from\"\n\" command line.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:332\nmsgid \"\"\n\"`#4788 <https://projects.tigase.net/issues?q=Redmine%20ID:%204788>`__ \"\n\"Push component added to dist-max archive.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:334\nmsgid \"\"\n\"`#4814 <https://projects.tigase.net/issues?q=Redmine%20ID:%204814>`__ \"\n\"SASL-SCRAM will now be automatically disabled if auth database uses \"\n\"encoded passwords.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:336\nmsgid \"\"\n\"`#4844 <https://projects.tigase.net/issues?q=Redmine%20ID:%204844>`__ \"\n\"External components can now have SSL socket connections assigned to them.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:338\nmsgid \"\"\n\"`#4859 <https://projects.tigase.net/issues?q=Redmine%20ID:%204859>`__ \"\n\"Tigase ``DbSchemaLoader`` now can support using SSL when connecting to \"\n\"databases.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:340\nmsgid \"\"\n\"`#4874 <https://projects.tigase.net/issues?q=Redmine%20ID:%204874>`__ \"\n\"Tigase Test Suite has been updated to correspond to all changes for \"\n\"v8.0.0.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:342\nmsgid \"\"\n\"`#4877 <https://projects.tigase.net/issues?q=Redmine%20ID:%204877>`__ In-\"\n\"memory repository implemented for **testing ONLY**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:344\nmsgid \"\"\n\"`#4880 <https://projects.tigase.net/issues?q=Redmine%20ID:%204880>`__ \"\n\"Tigase config-type settings have been reduced and changed. See `this \"\n\"section <#configType>`__ for more details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:346\nmsgid \"\"\n\"`#4908 <https://projects.tigase.net/issues?q=Redmine%20ID:%204908>`__ \"\n\"Limited Ad-hoc execution to admin only within monitor component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:348\nmsgid \"\"\n\"`#5005 <https://projects.tigase.net/issues?q=Redmine%20ID:%205005>`__ \"\n\"Detailed logging configuration is now available in DSL format. See \"\n\"xref:[customLogging] for more details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:350\nmsgid \"\"\n\"`#5069 <https://projects.tigase.net/issues?q=Redmine%20ID:%205069>`__ \"\n\"Packet processed statistics now separates results based on XML \"\n\"Namespaces.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:352\nmsgid \"\"\n\"`#5079 <https://projects.tigase.net/issues?q=Redmine%20ID:%205079>`__ \"\n\"Tigase ``DbSchemaLoader`` can now process multiple .sql files in one \"\n\"command by using a comma separated list when calling.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:354\nmsgid \"\"\n\"`#5086 <https://projects.tigase.net/issues?q=Redmine%20ID:%205086>`__ \"\n\"Tigase server monitor is loaded after delay to prevent NPE during \"\n\"startup.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:356\nmsgid \"\"\n\"`#5149 <https://projects.tigase.net/issues?q=Redmine%20ID:%205149>`__ \"\n\"``StanzaReceiver`` and ``StanzaSender`` Components have been deprecated \"\n\"and are no longer part of Tigase XMPP Server. Related SQL tables \"\n\"``xmpp_stanza`` and ``short_news`` have also been removed from schemas.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:358\nmsgid \"\"\n\"`#5150 <https://projects.tigase.net/issues?q=Redmine%20ID:%205150>`__ All\"\n\" TigaseDB tables now use the ``tig_`` prefix.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:360\nmsgid \"\"\n\"`#5214 <https://projects.tigase.net/issues?q=Redmine%20ID:%205214>`__ \"\n\"Check has been added if recipient exists before storing offline messages \"\n\"for local jid.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:362\nmsgid \"\"\n\"`#5293 <https://projects.tigase.net/issues?q=Redmine%20ID:%205293>`__ \"\n\"``DbSchemaLoader`` now will fail execution instead of skipping when \"\n\"encountering missing files.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:364\nmsgid \"\"\n\"`#5379 <https://projects.tigase.net/issues?q=Redmine%20ID:%205379>`__ \"\n\"Server ready detection has been improved in testrunner.sh.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:366\nmsgid \"\"\n\"`#5397 <https://projects.tigase.net/issues?q=Redmine%20ID:%205397>`__ \"\n\"Webhelp Documentation will no longer be built.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:368\nmsgid \"\"\n\"`#5422 <https://projects.tigase.net/issues?q=Redmine%20ID:%205422>`__ \"\n\"Errors with Beans will now result in compact and more readable StackTrace\"\n\" print in console log.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:370\nmsgid \"\"\n\"`#5423 <https://projects.tigase.net/issues?q=Redmine%20ID:%205423>`__ \"\n\"System configuration will now be printed to log file as \"\n\"``ConfigHolder.loadConfiguration`` output.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:372\nmsgid \"\"\n\"`#5425 <https://projects.tigase.net/issues?q=Redmine%20ID:%205425>`__ \"\n\"``GetAnyFile`` and ``GetConfigFile`` scripts moved to message-router \"\n\"instead of basic-conf.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:374\nmsgid \"\"\n\"`#5429 <https://projects.tigase.net/issues?q=Redmine%20ID:%205429>`__ \"\n\"Adjusted settings for Dynamic Rostering now can use separate beans for \"\n\"multiple implementations.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:376\nmsgid \"\"\n\"`#5430 <https://projects.tigase.net/issues?q=Redmine%20ID:%205430>`__ \"\n\"``BindResource`` is now set to FINER log level to reduce console output \"\n\"verbosity.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:378\nmsgid \"\"\n\"`#5475 <https://projects.tigase.net/issues?q=Redmine%20ID:%205475>`__ \"\n\"Setting default environment variables is now possible in ``config.tdsl`` \"\n\"file using ``env('env-1', 'def-value')`` lines. Details available `in DSL\"\n\" Configuration <#dslEnv>`__ section.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:380\nmsgid \"\"\n\"`#5496 <https://projects.tigase.net/issues?q=Redmine%20ID:%205496>`__ \"\n\"``Destroy Schema`` task now added to schema manager.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:382\nmsgid \"\"\n\"`#5583 <https://projects.tigase.net/issues?q=Redmine%20ID:%205583>`__ \"\n\"Error messages now properly sent when offline message storage is full.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:384\nmsgid \"\"\n\"`#5674 <https://projects.tigase.net/issues?q=Redmine%20ID:%205674>`__ All\"\n\" components now use UTC timestamp when interacting with databases.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:386\nmsgid \"\"\n\"`#5800 <https://projects.tigase.net/issues?q=Redmine%20ID:%205800>`__ \"\n\"Better annotation of deprecated code, cleanup and removal code previously\"\n\" marked as deprecated.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:388\nmsgid \"\"\n\"`#5964 <https://projects.tigase.net/issues?q=Redmine%20ID:%205964>`__ \"\n\"Server version is now added to JMX statistics.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:390\nmsgid \"\"\n\"`#5982 <https://projects.tigase.net/issues?q=Redmine%20ID:%205982>`__ \"\n\"Remote JVM debugging configuration added to tigase.conf file, commented \"\n\"by default.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:392\nmsgid \"\"\n\"`#6038 <https://projects.tigase.net/issues?q=Redmine%20ID:%206038>`__ \"\n\"Data Source pool connections are now initialized concurrently instead of \"\n\"one at a time, dropping initializing time.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:394\nmsgid \"\"\n\"`#6103 <https://projects.tigase.net/issues?q=Redmine%20ID:%206103>`__ \"\n\":literal:`RosterElement`no longer keeps `XMPPResourceConnection` instance\"\n\" as it is cached elsewhere. Removal results in net improvement in memory \"\n\"footprint.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:396\nmsgid \"\"\n\"`#6133 <https://projects.tigase.net/issues?q=Redmine%20ID:%206133>`__ \"\n\"Tigase now checks components against server version to ensure \"\n\"compatibility.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:398\nmsgid \"\"\n\"`#6163 <https://projects.tigase.net/issues?q=Redmine%20ID:%206163>`__ \"\n\"Groovy plugin updated to v2.4.12.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:400\nmsgid \"\"\n\"`#6206 <https://projects.tigase.net/issues?q=Redmine%20ID:%206206>`__ \"\n\"Separated TigaseXMLTools and TigaseUtil packages for better compatibility\"\n\" with JDK v9.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:402\nmsgid \"\"\n\"`#6216 <https://projects.tigase.net/issues?q=Redmine%20ID:%206216>`__ \"\n\"MongoDB Driver now updated to v3.5.0.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:404\nmsgid \"\"\n\"`#6560 <https://projects.tigase.net/issues?q=Redmine%20ID:%206560>`__ \"\n\"tigase anti-spam component now included in tigase dist-max archive.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:406\nmsgid \"\"\n\"`#6821 <https://projects.tigase.net/issues?q=Redmine%20ID:%206821>`__ \"\n\"Improved error reporting when errors from ``ConfigReader``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:408\nmsgid \"\"\n\"`#6842 <https://projects.tigase.net/issues?q=Redmine%20ID:%206842>`__ \"\n\"``DefaultTypesConverter`` no longer requires case sensitive enums.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:410\nmsgid \"\"\n\"`#7082 <https://projects.tigase.net/issues?q=Redmine%20ID:%207082>`__ \"\n\"``ClassUtilBean`` now handles packet filtering for packets part of Tigase\"\n\" Server but not containing beans, other improvements to mDNS.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:412\nmsgid \"\"\n\"`#7433 <https://projects.tigase.net/issues?q=Redmine%20ID:%207433>`__ \"\n\"``SeeOtherHost`` no longer uses ``PropertiesBeanConfigurator`` to parse \"\n\"configuration.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:414\nmsgid \"\"\n\"`#7446 <https://projects.tigase.net/issues?q=Redmine%20ID:%207446>`__ \"\n\"User credentials can now be managed with Ad-hoc commands.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:416\nmsgid \"\"\n\"`#7743 <https://projects.tigase.net/issues?q=Redmine%20ID:%207743>`__ \"\n\"Improved error message when repository is not found.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:418\nmsgid \"\"\n\"`#7773 <https://projects.tigase.net/issues?q=Redmine%20ID:%207773>`__ Ad-\"\n\"hoc commands can now by executed asynchronously.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:420\nmsgid \"\"\n\"`#2341 <https://projects.tigase.net/issues?q=Redmine%20ID:%202341>`__ \"\n\"allow specifying SubscriptionType when adding buddy to avoid calling \"\n\"separately .setBuddySubscription() thus eliminating saving roster twice \"\n\"to database if not needed\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:425\nmsgid \"Fixes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:427\nmsgid \"\"\n\"`#2750 <https://projects.tigase.net/issues?q=Redmine%20ID:%202750>`__ \"\n\"Multiple artifact and depreciated file cleanup. Massive code cleanup and \"\n\"javadoc cleaning.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:429\nmsgid \"\"\n\"`#3582 <https://projects.tigase.net/issues?q=Redmine%20ID:%203582>`__ \"\n\"Schema files streamlined, and no longer embedded in code.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:431\nmsgid \"\"\n\"`#3611 <https://projects.tigase.net/issues?q=Redmine%20ID:%203611>`__ \"\n\"Fixed TheadExceptionHandler caused by ACS unable to read PubSub schema \"\n\"changes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:433\nmsgid \"\"\n\"`#3686 <https://projects.tigase.net/issues?q=Redmine%20ID:%203686>`__ \"\n\"Issues with processing XHTML-IM have been fixed, and now render correctly\"\n\" messages with multiple CData items.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:435\nmsgid \"\"\n\"`#3689 <https://projects.tigase.net/issues?q=Redmine%20ID:%203689>`__ \"\n\"Packets returned from CM no longer bear the original senders' jid.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:437\nmsgid \"\"\n\"`#3803 <https://projects.tigase.net/issues?q=Redmine%20ID:%203803>`__ New\"\n\" call ``RouteEvent`` has been added to check to list and check events and\"\n\" determine which should be forwarded to other nodes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:439\nmsgid \"\"\n\"`#3822 <https://projects.tigase.net/issues?q=Redmine%20ID:%203822>`__ \"\n\"Error is now thrown if listener is registered for an event that is not \"\n\"found in EventBus.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:441\nmsgid \"\"\n\"`#3910 <https://projects.tigase.net/issues?q=Redmine%20ID:%203910>`__ \"\n\"Fixed NPE in SessionManager when session is closed during execution of \"\n\"everyMinute method.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:443\nmsgid \"\"\n\"`#3911 <https://projects.tigase.net/issues?q=Redmine%20ID:%203911>`__ \"\n\"Fixed issue of dropping connections during thread load distribution.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:445\nmsgid \"\"\n\"`#4185 <https://projects.tigase.net/issues?q=Redmine%20ID:%204185>`__ \"\n\"Fixed an error where messages would be duplicated on stream resumption \"\n\"due to a counter being reset upon reconnection.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:447\nmsgid \"\"\n\"`#4447 <https://projects.tigase.net/issues?q=Redmine%20ID:%204447>`__ \"\n\"Fixed condition where expired messages in offline store would cause \"\n\"locks.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:449\nmsgid \"\"\n\"`#4547 <https://projects.tigase.net/issues?q=Redmine%20ID:%204547>`__ \"\n\"config.dump file now is fully compatible with init.tdsl file and DSL file\"\n\" formatting.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:451\nmsgid \"\"\n\"`#4672 <https://projects.tigase.net/issues?q=Redmine%20ID:%204672>`__ \"\n\"Fixed ``UnsupportedOperationException`` occurring during configuration of\"\n\" ``WebSocketConnectionClustered``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:453\nmsgid \"\"\n\"`#4776 <https://projects.tigase.net/issues?q=Redmine%20ID:%204776>`__ \"\n\"``DBSchemaLoader`` now asks for user credentials if parameter is missing.\"\n\" Exceptions are no longer thrown if file specified is not found.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:455\nmsgid \"\"\n\"`#4885 <https://projects.tigase.net/issues?q=Redmine%20ID:%204885>`__ \"\n\"``client-port-delay-listening`` no longer causes exception when called.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:457\nmsgid \"\"\n\"`#4973 <https://projects.tigase.net/issues?q=Redmine%20ID:%204973>`__ \"\n\"Changed Message History query to now include a limit when selecting \"\n\"items, preventing an SQLTimeoutException.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:459\nmsgid \"\"\n\"`#5005 <https://projects.tigase.net/issues?q=Redmine%20ID:%205005>`__ \"\n\"Fixed an issue where disabling components would result in server \"\n\"shutdown.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:461\nmsgid \"\"\n\"`#5042 <https://projects.tigase.net/issues?q=Redmine%20ID:%205042>`__ \"\n\"Fixed issue when implementing custom SASL providers, mechanisms and \"\n\"callback handler factories.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:463\nmsgid \"\"\n\"`#5066 <https://projects.tigase.net/issues?q=Redmine%20ID:%205066>`__ \"\n\"Fixed issue initializing databases using MongoDB.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:465\nmsgid \"\"\n\"`#5076 <https://projects.tigase.net/issues?q=Redmine%20ID:%205076>`__ \"\n\"last_login and last_logout values are now properly updated while using \"\n\"SASL SCRAM authentication.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:467\nmsgid \"\"\n\"`#5084 <https://projects.tigase.net/issues?q=Redmine%20ID:%205084>`__ \"\n\"SCRAM now checks to see if account is disabled before retrieving \"\n\"password.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:469\nmsgid \"\"\n\"`#5085 <https://projects.tigase.net/issues?q=Redmine%20ID:%205085>`__ \"\n\"Fixed ``too many beans implemented`` error in Monitor Component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:471\nmsgid \"\"\n\"`#5088 <https://projects.tigase.net/issues?q=Redmine%20ID:%205088>`__ \"\n\"Removed unnecessary SASL request processing after session is closed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:473\nmsgid \"\"\n\"`#5118 <https://projects.tigase.net/issues?q=Redmine%20ID:%205118>`__ \"\n\"Fixed NPE during query of privacy lists then ``type`` is missing.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:475\nmsgid \"\"\n\"`#5303 <https://projects.tigase.net/issues?q=Redmine%20ID:%205303>`__ \"\n\"Fixed beans not being overridden by configuration if they were registered\"\n\" in ``RegistrarBean`` or ``AbstractKernelBasedComponent``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:477\nmsgid \"\"\n\"`#5311 <https://projects.tigase.net/issues?q=Redmine%20ID:%205311>`__ \"\n\"Offline messages are no longer dumped from MongoDB when restarting \"\n\"server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:479\nmsgid \"\"\n\"`#5394 <https://projects.tigase.net/issues?q=Redmine%20ID:%205394>`__ \"\n\"Loading main Derby schema no longer throws exceptions.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:481\nmsgid \"\"\n\"`#5428 <https://projects.tigase.net/issues?q=Redmine%20ID:%205428>`__ \"\n\"Fixed parsing of v-host per domain limit property.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:483\nmsgid \"\"\n\"`#5450 <https://projects.tigase.net/issues?q=Redmine%20ID:%205450>`__ \"\n\"Server no longer automatically shuts down when default or other db can \"\n\"not be found or accessed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:485\nmsgid \"\"\n\"`#5458 <https://projects.tigase.net/issues?q=Redmine%20ID:%205458>`__ \"\n\"Fixed potential timeout arising from \"\n\"``XMPPIOService::xmppStreamOpened()`` method.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:487\nmsgid \"\"\n\"`#5480 <https://projects.tigase.net/issues?q=Redmine%20ID:%205480>`__ \"\n\"Fixed issue in Derby DB where obtaining offline messages results in \"\n\"SQLException.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:489\nmsgid \"\"\n\"`#5525 <https://projects.tigase.net/issues?q=Redmine%20ID:%205525>`__ \"\n\"Fixed S2S ``invalid-namespace`` error being returned during connection \"\n\"establishment.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:491\nmsgid \"\"\n\"`#5587 <https://projects.tigase.net/issues?q=Redmine%20ID:%205587>`__ \"\n\"Fixed unclosed ``ResultSet`` when storing a message to AMP-offline \"\n\"database in Derby causing deadlock.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:493\nmsgid \"\"\n\"`#5645 <https://projects.tigase.net/issues?q=Redmine%20ID:%205645>`__ \"\n\"Added fix for possible NPE when failing to retrieve beans.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:495\nmsgid \"\"\n\"`#5670 <https://projects.tigase.net/issues?q=Redmine%20ID:%205670>`__ \"\n\"config-dump now prints configuration for inactive components and beans to\"\n\" log.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:497\nmsgid \"\"\n\"`#5692 <https://projects.tigase.net/issues?q=Redmine%20ID:%205692>`__ \"\n\"Messages sent with negative priority were being occasionally dropped and \"\n\"not processed to ``OfflineMessageHandler``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:499\nmsgid \"\"\n\"`#5727 <https://projects.tigase.net/issues?q=Redmine%20ID:%205727>`__ \"\n\"Fixed potential issue with MySQL procedures not being killed properly.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:501\nmsgid \"\"\n\"`#5750 <https://projects.tigase.net/issues?q=Redmine%20ID:%205750>`__ \"\n\"Statistics now filter out zero-value results unless FINEST level is \"\n\"requested.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:503\nmsgid \"\"\n\"`#5831 <https://projects.tigase.net/issues?q=Redmine%20ID:%205831>`__ \"\n\"Fixed occurrence of ``OutOfMemory`` error.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:505\nmsgid \"\"\n\"`#5864 <https://projects.tigase.net/issues?q=Redmine%20ID:%205864>`__ \"\n\"Fixed NPE when executing BOSH pre-bind script.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:507\nmsgid \"\"\n\"`#5867 <https://projects.tigase.net/issues?q=Redmine%20ID:%205867>`__ \"\n\"Fixed NPE occurring during configuration dump.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:509\nmsgid \"\"\n\"`#6000 <https://projects.tigase.net/issues?q=Redmine%20ID:%206000>`__ \"\n\"Fixed a few issues with dynamic rosters properly handling presence \"\n\"subscription requests.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:511\nmsgid \"\"\n\"`#6006 <https://projects.tigase.net/issues?q=Redmine%20ID:%206006>`__ \"\n\"Improved configuration file and DB Schema handling.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:513\nmsgid \"\"\n\"`#6041 <https://projects.tigase.net/issues?q=Redmine%20ID:%206041>`__ \"\n\"Fixed potential issue where vhosts DB could be overwritten by vhosts \"\n\"configuration in ``init.config``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:515\nmsgid \"\"\n\"`#6078 <https://projects.tigase.net/issues?q=Redmine%20ID:%206078>`__ \"\n\"Fixed ``ClusterConnectionManager`` to use custom_elements_limit instead \"\n\"of a fixed value.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:517\nmsgid \"\"\n\"`#6080 <https://projects.tigase.net/issues?q=Redmine%20ID:%206080>`__ \"\n\"Fixed Packet Filtering to not filter cluster node information requests.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:519\nmsgid \"\"\n\"`#6083 <https://projects.tigase.net/issues?q=Redmine%20ID:%206083>`__ \"\n\"Fixed clustered mode shutting down server when certain components are \"\n\"disabled.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:521\nmsgid \"\"\n\"`#6135 <https://projects.tigase.net/issues?q=Redmine%20ID:%206135>`__ \"\n\"Tigase now properly enabled selective TLS if not enabled globally.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:523\nmsgid \"\"\n\"`#6140 <https://projects.tigase.net/issues?q=Redmine%20ID:%206140>`__ \"\n\"Fixed issue while sending server welcome message.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:525\nmsgid \"\"\n\"`#6141 <https://projects.tigase.net/issues?q=Redmine%20ID:%206141>`__ \"\n\"Fixed NPE at startup.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:527\nmsgid \"\"\n\"`#6234 <https://projects.tigase.net/issues?q=Redmine%20ID:%206234>`__ \"\n\"Fixed an error where an error message would repeat unnecessarily.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:529\nmsgid \"\"\n\"`#6284 <https://projects.tigase.net/issues?q=Redmine%20ID:%206284>`__ Ad-\"\n\"hoc commands now refresh SSL Certificate, and restart is no longer \"\n\"required.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:531\nmsgid \"\"\n\"`#6293 <https://projects.tigase.net/issues?q=Redmine%20ID:%206293>`__ \"\n\"Server no longer sends no response upon setting empty photo in vCard.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:533\nmsgid \"\"\n\"`#6263 <https://projects.tigase.net/issues?q=Redmine%20ID:%206263>`__ \"\n\"Fixed missing namespaces in responses from adhoc commands.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:535\nmsgid \"\"\n\"`#6400 <https://projects.tigase.net/issues?q=Redmine%20ID:%206400>`__ \"\n\"Added a proper error when max-queue-size is too small and server cannot \"\n\"start.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:537\nmsgid \"\"\n\"`#6408 <https://projects.tigase.net/issues?q=Redmine%20ID:%206408>`__ \"\n\"Fixed an issue where single WebSocket frames contained multiple XML \"\n\"stanzas instead of one per frame.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:539\nmsgid \"\"\n\"`#6411 <https://projects.tigase.net/issues?q=Redmine%20ID:%206411>`__ \"\n\"Main kernel is now called to smooth shutdown. Further, timeout periods \"\n\"are opened up for large instances.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:541\nmsgid \"\"\n\"`#6574 <https://projects.tigase.net/issues?q=Redmine%20ID:%206574>`__ SSL\"\n\" certificate upload handling is now fixed within cluster mode.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:543\nmsgid \"\"\n\"`#6598 <https://projects.tigase.net/issues?q=Redmine%20ID:%206598>`__ \"\n\"Fixed EventBus Registration connection issues between cluster nodes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:545\nmsgid \"\"\n\"`#6658 <https://projects.tigase.net/issues?q=Redmine%20ID:%206658>`__ \"\n\"Cluster connections no longer potentially keep open connection after \"\n\"cluster is no longer connected or available.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:547\nmsgid \"\"\n\"`#6749 <https://projects.tigase.net/issues?q=Redmine%20ID:%206749>`__ \"\n\"Fixed schema parsing for DerbyDB.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:549\nmsgid \"\"\n\"`#6776 <https://projects.tigase.net/issues?q=Redmine%20ID:%206776>`__ \"\n\"Fixed failing Websocket connections if header contains more than one \"\n\"value.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:551\nmsgid \"\"\n\"`#6875 <https://projects.tigase.net/issues?q=Redmine%20ID:%206875>`__ \"\n\"Fixed an issue where C2S connections could be accepted before \"\n\"SessionManager was initialized.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:553\nmsgid \"\"\n\"`#7037 <https://projects.tigase.net/issues?q=Redmine%20ID:%207037>`__ \"\n\"Fixed error while parsing negative values from ``config.tdsl`` file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:555\nmsgid \"\"\n\"`#7055 <https://projects.tigase.net/issues?q=Redmine%20ID:%207055>`__ \"\n\"Improvements to metaspace use and other memory use tweaks.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:557\nmsgid \"\"\n\"`#7304 <https://projects.tigase.net/issues?q=Redmine%20ID:%207304>`__ \"\n\"Virtual host logs now properly follow log size limits.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:559\nmsgid \"\"\n\"`#7431 <https://projects.tigase.net/issues?q=Redmine%20ID:%207431>`__ \"\n\"AdHoc requests between the same user with different resources are no \"\n\"longer dropped with \\\\`NoConnectionIdExecption`error.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:561\nmsgid \"\"\n\"`#7434 <https://projects.tigase.net/issues?q=Redmine%20ID:%207434>`__ \"\n\"Adjusted ``SeeOtherHotDualIP`` to use new table name in cluster nodes \"\n\"database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:563\nmsgid \"\"\n\"`#7491 <https://projects.tigase.net/issues?q=Redmine%20ID:%207491>`__ \"\n\"Stacktraces from ``CertificateContainer`` are no longer printed to \"\n\"tigase-console.log, but will be printed to tigase.log.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:565\nmsgid \"\"\n\"`#7687 <https://projects.tigase.net/issues?q=Redmine%20ID:%207687>`__ \"\n\"Fixed an error where connections failed after authentication timeout were\"\n\" marked as active after cleanup.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:567\nmsgid \"\"\n\"`#7747 <https://projects.tigase.net/issues?q=Redmine%20ID:%207747>`__ \"\n\"Fixed ``ClusterRepoItemEvent`` serialization issues causing unsupported \"\n\"conversion error in cluster mode.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:569\nmsgid \"\"\n\"`#7495 <https://projects.tigase.net/issues?q=Redmine%20ID:%207495>`__ fix\"\n\" issue with not all logs being obfuscated, added testcase, documentation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:571\nmsgid \"\"\n\"`#8305 <https://projects.tigase.net/issues?q=Redmine%20ID:%208305>`__ fix\"\n\" issue with SeeOtherHostDualIP when using MongoDB\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:576\nmsgid \"Component Changes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:579\nmsgid \"AMP\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:581\nmsgid \"\"\n\"`#7301 <https://projects.tigase.net/issues?q=Redmine%20ID:%207301>`__ \"\n\"Tigase AMP component now uses multiple processing threads.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:586\nmsgid \"PubSub\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:588\nmsgid \"\"\n\"`#5033 <https://projects.tigase.net/issues?q=Redmine%20ID:%205033>`__ \"\n\"PubSub now compatible with using emojis in pubsub items.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:590\nmsgid \"\"\n\"`#5693 <https://projects.tigase.net/issues?q=Redmine%20ID:%205693>`__ \"\n\"Fixed parsing configuration of SessionManager processors.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:592\nmsgid \"\"\n\"`#5766 <https://projects.tigase.net/issues?q=Redmine%20ID:%205766>`__ \"\n\"PubSub now writes to all databases with UTC timestamp.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:594\nmsgid \"\"\n\"`#5953 <https://projects.tigase.net/issues?q=Redmine%20ID:%205953>`__ \"\n\"Fixed presences not being removed from ``presenceByService`` collection \"\n\"if client disconnects without ``<unavailable/>`` presence being sent.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:596\nmsgid \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"version changed to PubSub v4.0.0.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:598\nmsgid \"\"\n\"`#7707 <https://projects.tigase.net/issues?q=Redmine%20ID:%207707>`__ \"\n\"Fixed potential NPE in PubSub.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:603\nmsgid \"http-api\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:605\nmsgid \"\"\n\"`#4873 <https://projects.tigase.net/issues?q=Redmine%20ID:%204873>`__ \"\n\"Support added to display timestamp fields as data, time, and timezone \"\n\"fields.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:607\nmsgid \"\"\n\"`#4876 <https://projects.tigase.net/issues?q=Redmine%20ID:%204876>`__ \"\n\"Implemented using XML repository for new setups, and updated default \"\n\"config to use this.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:609\nmsgid \"\"\n\"`#4888 <https://projects.tigase.net/issues?q=Redmine%20ID:%204888>`__ \"\n\"``http-api`` now is enabled by default.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:611\nmsgid \"\"\n\"`#5209 <https://projects.tigase.net/issues?q=Redmine%20ID:%205209>`__ \"\n\"Updated visual styling of pages hosted by component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:613\nmsgid \"\"\n\"`#5290 <https://projects.tigase.net/issues?q=Redmine%20ID:%205290>`__ \"\n\"Fixed invalid property name.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:615\nmsgid \"\"\n\"`#5316 <https://projects.tigase.net/issues?q=Redmine%20ID:%205316>`__ \"\n\"Account Registration now can now require and send confirmation E-mails.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:617\nmsgid \"\"\n\"`#5415 <https://projects.tigase.net/issues?q=Redmine%20ID:%205415>`__ Web\"\n\" Setup now checks configuration for message archive conflicts.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:619\nmsgid \"\"\n\"`#5460 <https://projects.tigase.net/issues?q=Redmine%20ID:%205460>`__ \"\n\"MongoDB now supported through web-setup.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:621\nmsgid \"\"\n\"`#5717 <https://projects.tigase.net/issues?q=Redmine%20ID:%205717>`__ \"\n\"Fixed default values of check-boxes in admin UI not being shown.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:623\nmsgid \"\"\n\"`#5950 <https://projects.tigase.net/issues?q=Redmine%20ID:%205950>`__ \"\n\"Supported added for `XEP-0363: HTTP File Upload \"\n\"<https://xmpp.org/extensions/xep-0363.html>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:625\nmsgid \"\"\n\"`#6159 <https://projects.tigase.net/issues?q=Redmine%20ID:%206159>`__ \"\n\"Fixed NPE thrown if scripts directory is not present.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:627\nmsgid \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"version changed to tigase-http-api v2.0.0.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:629\nmsgid \"\"\n\"`#6212 <https://projects.tigase.net/issues?q=Redmine%20ID:%206212>`__ \"\n\"Added mechanism for password changing through HTTP API.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:631\nmsgid \"\"\n\"`#7307 <https://projects.tigase.net/issues?q=Redmine%20ID:%207307>`__ \"\n\"Fixed scripts returning 404 while handling rest/user/ requests even \"\n\"though user exists.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:633\nmsgid \"\"\n\"`#7178 <https://projects.tigase.net/issues?q=Redmine%20ID:%207178>`__ Ad-\"\n\"hoc commands are now categorized in groups for better organization.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:635\nmsgid \"\"\n\"`#7568 <https://projects.tigase.net/issues?q=Redmine%20ID:%207568>`__ \"\n\"Added timeout reading for HTTP request headers, added configurable \"\n\"``accept-timeout``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:640\nmsgid \"message-archive\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:642\nmsgid \"\"\n\"`#4867 <https://projects.tigase.net/issues?q=Redmine%20ID:%204867>`__ \"\n\"fixed issue when changing MA jid.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:644\nmsgid \"\"\n\"`#4888 <https://projects.tigase.net/issues?q=Redmine%20ID:%204888>`__ \"\n\"``message-archive`` is enabled by default.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:646\nmsgid \"\"\n\"`#5033 <https://projects.tigase.net/issues?q=Redmine%20ID:%205033>`__ \"\n\"Update message archive to be compatible with emojis.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:648\nmsgid \"\"\n\"`#5391 <https://projects.tigase.net/issues?q=Redmine%20ID:%205391>`__ \"\n\"Added missing query statement block starts and ends to be compatible with\"\n\" SQL Server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:650\nmsgid \"\"\n\"`#5604 <https://projects.tigase.net/issues?q=Redmine%20ID:%205604>`__ \"\n\"Modified access to static fields and functions.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:652\nmsgid \"\"\n\"`#5681 <https://projects.tigase.net/issues?q=Redmine%20ID:%205681>`__ \"\n\"Fixed duplication of groupchat messages with different ids by modifying \"\n\"hash algorithm.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:654\nmsgid \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"version changed to message-archive v2.0.0.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:656\nmsgid \"\"\n\"`#7615 <https://projects.tigase.net/issues?q=Redmine%20ID:%207615>`__ \"\n\"``feature-not-implemented`` response no longer occurs when removing \"\n\"stored messages.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:661\nmsgid \"MUC\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:663\nmsgid \"\"\n\"`#4888 <https://projects.tigase.net/issues?q=Redmine%20ID:%204888>`__ \"\n\"``muc`` now is enabled by default.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:665\nmsgid \"\"\n\"`#5033 <https://projects.tigase.net/issues?q=Redmine%20ID:%205033>`__ MUC\"\n\" component is now compatible with emojis.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:667\nmsgid \"\"\n\"`#5066 <https://projects.tigase.net/issues?q=Redmine%20ID:%205066>`__ \"\n\"Fixed issues working with MongoDB repository.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:669\nmsgid \"\"\n\"`#5085 <https://projects.tigase.net/issues?q=Redmine%20ID:%205085>`__ \"\n\"Removed invalid annotation parameter values.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:671\nmsgid \"\"\n\"`#5559 <https://projects.tigase.net/issues?q=Redmine%20ID:%205559>`__ \"\n\"Fixed NPE while changing default room configuration.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:673\nmsgid \"\"\n\"`#5666 <https://projects.tigase.net/issues?q=Redmine%20ID:%205666>`__ \"\n\"User may add more than one ``<item/>`` elements to query when querying \"\n\"room members.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:675\nmsgid \"\"\n\"`#5715 <https://projects.tigase.net/issues?q=Redmine%20ID:%205715>`__ \"\n\"Welcome messages may now be disabled globally, or in individual room \"\n\"configurations.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:677\nmsgid \"\"\n\"`#5736 <https://projects.tigase.net/issues?q=Redmine%20ID:%205736>`__ \"\n\"Rooms with no subject now return empty ``<subject/>`` element, as per \"\n\"`XEP-0048 7.2.16 <https://xmpp.org/extensions/xep-0045.html#enter-\"\n\"subject>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:679\nmsgid \"\"\n\"`#5813 <https://projects.tigase.net/issues?q=Redmine%20ID:%205813>`__ \"\n\"Fixed NPE during room creation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:681\nmsgid \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"version changed to tigase-muc v3.0.0.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:683\nmsgid \"\"\n\"`#6395 <https://projects.tigase.net/issues?q=Redmine%20ID:%206395>`__ \"\n\"Fixed ``tigase.db.UserNotFoundException`` during retrieval of MUC user.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:685\nmsgid \"\"\n\"`#6734 <https://projects.tigase.net/issues?q=Redmine%20ID:%206734>`__ \"\n\"Introduced ``muc#roomconfig_maxresources`` to allow configuration of max \"\n\"number of resources for a single occupant.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:687\nmsgid \"\"\n\"`#7443 <https://projects.tigase.net/issues?q=Redmine%20ID:%207443>`__ \"\n\"Disabled XEP-0091 by default, added history attribute validation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:692\nmsgid \"socks5 Proxy\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:694\nmsgid \"\"\n\"`#2750 <https://projects.tigase.net/issues?q=Redmine%20ID:%202750>`__ \"\n\"Cleanup of code and removal of empty javadocs.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:696\nmsgid \"\"\n\"`#5867 <https://projects.tigase.net/issues?q=Redmine%20ID:%205867>`__ \"\n\"Fixed NPE during configuration dump when component is disabled.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:698\nmsgid \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"version changed to tigase-socks5 v2.0.0.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:703\nmsgid \"stats\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:705\nmsgid \"\"\n\"`#5206 <https://projects.tigase.net/issues?q=Redmine%20ID:%205206>`__ \"\n\"Fixed exception causing duplicate error entry.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:707\nmsgid \"\"\n\"`#5728 <https://projects.tigase.net/issues?q=Redmine%20ID:%205728>`__ \"\n\"Fixed ``MySQLIntegrityConstraintViolationException`` in upload handler.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:709\nmsgid \"\"\n\"`#6161 <https://projects.tigase.net/issues?q=Redmine%20ID:%206161>`__ \"\n\"Removed usage of classes from javax.xml.ws package for JDKv9 \"\n\"compatibility.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:714\nmsgid \"STUN Server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:716\nmsgid \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"version changed to tigase-stun v2.0.0.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:721\nmsgid \"WebSocket\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:723\nmsgid \"\"\n\"`#6481 <https://projects.tigase.net/issues?q=Redmine%20ID:%206481>`__ \"\n\"Websocket component has been improved to be more compliant with `rfc6455 \"\n\"<https://tools.ietf.org/html/rfc6455>`__\"\nmsgstr \"\"\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Administration/Components/_Components.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Administration/Components/Components.inc:4\nmsgid \"Components\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Components.inc:6\nmsgid \"\"\n\"The only step is to tell the server what components to load, how to name \"\n\"them and optionally give some extra parameters. To do so open the \"\n\"``config.tdsl`` file you use in your installation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Components.inc:8\nmsgid \"\"\n\"Let’s say you want to just add PubSub for now. All you need to do is add \"\n\"the following to the properties file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Components.inc:14\nmsgid \"\"\n\"Normally, this is not necessary since pubsub is loaded by default, \"\n\"however this is just an example of loading a class with the DSL format.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Components.inc:20\nmsgid \"\"\n\"As you can see, we can customize the name of a component in the \"\n\"deceleration, here we are using pubsub-priv.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Components.inc:22\nmsgid \"\"\n\"Although this may be rare, it allows for wide compatibility and platform \"\n\"stability.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Components.inc:24\nmsgid \"\"\n\"Normally, however we want to load few different components like PubSub, \"\n\"MUC, MSN Transport and so on…​. Therefore instead of the above second \"\n\"PubSub we can load the MUC component:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Components.inc:31\nmsgid \"Changes to the ``config.tdsl`` file will take effect upon server restart.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:2\nmsgid \"Advanced Message Processing - AMP XEP-0079\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:4\nmsgid \"\"\n\"Tigase server offers support for `XEP-0079: Advanced Message Processing \"\n\"<http://xmpp.org/extensions/xep-0079.html>`__ (often abbreviated to AMP).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:6\nmsgid \"\"\n\"It is enabled by default but there are several configuration options that\"\n\" you may tweak.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:8\nmsgid \"\"\n\"Configuration of AMP is not very complex, but as it is implemented as a \"\n\"component in the Tigase server it does needs a few settings to get it \"\n\"right.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:10\nmsgid \"\"\n\"Here is a first, brief overview of the AMP configuration and later \"\n\"detailed explanation of each parameter.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:24\nmsgid \"First of all: plugins\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:26\nmsgid \"\"\n\"Even though the whole functionality is implemented inside the component \"\n\"you need a way to forward messages with ``AMP`` payload to that \"\n\"component. This is what the ``amp`` plugin does. The ``amp`` plugin \"\n\"intercepts all ``<message/>`` packets even without AMP payload, \"\n\"redirecting some of the to the ``AMP`` component and others processing in\"\n\" a standard way. Therefore you no longer need ``message`` plugin or \"\n\"``msgoffline`` plugin. Those are all functions are offered by the ``amp``\"\n\" plugin now. Hence you have to switch ``message`` and ``msgoffline`` \"\n\"plugins off (the ``amp`` plugin is loaded by default):\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:36\nmsgid \"\"\n\"The ``amp`` plugin needs to know where to forward all the ``AMP`` \"\n\"packets. By default plugin uses hostname of the given machine as this is \"\n\"true to the most installations. However, this is configured by the last \"\n\"line of the example configuration, which forwards all packets to the \"\n\"address ``amp@your-domain.tld``:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:47\nmsgid \"Secondly: component\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:49\nmsgid \"By default Tigase loads the component with the standard name ``amp``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:52\nmsgid \"Optional parameters\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:54\nmsgid \"\"\n\"There is also one parameter shared between the component and the plugin. \"\n\"Connection to the database where offline messages are stored. The AMP \"\n\"component has a dedicated schema for storing offline messages designed \"\n\"for a high traffic and high load installations. It does not use \"\n\"``UserRepository`` for storing messages.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:56\nmsgid \"\"\n\"By default the same physical database as for ``UserRepository`` is used \"\n\"but you can change it and store messages in a completely separate \"\n\"location to reduce performance degradation of rest of the system. You can\"\n\" set a database connection string using following property:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:66\nmsgid \"\"\n\"The `XEP-0079 <http://xmpp.org/extensions/xep-0079.html>`__ specification\"\n\" has a `Section 9. - Security Considerations \"\n\"<http://xmpp.org/extensions/xep-0079.html#security>`__. As it describes, \"\n\"in some cases the AMP protocol can be used to reveal user’s presence \"\n\"information by other users who are not authorized for presence updates. \"\n\"There are a few possible ways to prevent this.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:68\nmsgid \"\"\n\"Tigase’s implementation offers 3 modes to handle ``AMP`` requests to \"\n\"prevent revealing user’s status to non-authorized users:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:74\nmsgid \"\"\n\"In this mode the server performs strict checking. The ``AMP`` \"\n\"specification is fully handled. This however involves roster loading for \"\n\"each offline user, hence it may impact the service performance. It may \"\n\"not be feasible or possible to run in this mode for services under a high\"\n\" load with lots of AMP messages.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:76\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:86\nmsgid \"In the XEP this mode is described in the following way:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:78\nmsgid \"\"\n\"*Accept the relevant condition only if the sender is authorized to \"\n\"receive the receiver’s presence, as a result of which the server MUST \"\n\"reply with a <not-acceptable/> error condition if the sender is not so \"\n\"authorized; this is the RECOMMENDED behavior. This is also the default in\"\n\" Tigase.*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:84\nmsgid \"\"\n\"Dummy checking is performed efficiently by just returning an error \"\n\"response every time there is a chance that the default action may reveal \"\n\"user status without looking into the user’s roster. This does not affect \"\n\"performance but it does impact the ``AMP`` compliance.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:88\nmsgid \"\"\n\"*Accept the relevant condition only if the action is \\\"drop\\\", as a \"\n\"result of which the server MUST reply with a <not-acceptable/> error \"\n\"condition if the action is \\\"alert\\\", \\\"error\\\", or \\\"notify\\\"; this is \"\n\"slightly less restrictive but still unnecessarily restricts the \"\n\"functionality of the system, so is NOT RECOMMENDED.*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:90\nmsgid \"\"\n\"It does not do any checking. It acts like all users are authorized to \"\n\"receive notifications, even if it may reveal user status to unauthorized \"\n\"users. It does not impact the server performance and it offers full AMP \"\n\"compliance.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Monitoring.inc:4\nmsgid \"Server Monitoring\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Monitoring.inc:6\nmsgid \"\"\n\"All the documentation and resources related to the Tigase server \"\n\"monitoring.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Monitoring.inc:8\nmsgid \"\"\n\":ref:`Setting up Remote Monitoring in the Server<Setting-Up-Remote-\"\n\"Monitoring-in-the-Server>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Monitoring.inc:10\nmsgid \"\"\n\":ref:`Statistics Logger Configuration<Configuration-of-statistics-\"\n\"loggers>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Monitoring.inc:12\nmsgid \"\"\n\":ref:`Retrieving Statistics from the Server<Retrieving-statistics-from-\"\n\"the-server>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Monitoring.inc:14\nmsgid \":ref:`Monitor Component<Monitor-Component>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:4\nmsgid \"Setting Up Remote Monitoring in the Server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:6\nmsgid \"\"\n\"Tigase server can be remotely monitored over following protocols: \"\n\"``JMX/RMI``, ``SNMP`` and ``HTTP``. Even though ``JMX`` offers the \"\n\"biggest control and visibility to the server states, all of the \"\n\"monitoring services give the same basic set of the server statistics:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:8\nmsgid \"Number of network connections for s2s, c2s and Bosh\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:10\nmsgid \"\"\n\"Last second, last minute and last hour load for all main components: SM, \"\n\"MR, c2s, s2s, Bosh, MUC and PubSub\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:12\nmsgid \"\"\n\"System statistics - memory usage (heap and non heap) and the server \"\n\"uptime in milliseconds and human readable text.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:14\nmsgid \"\"\n\"Users statistics - number of registered users and number of online user \"\n\"session.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:16\nmsgid \"\"\n\"JMX/RMI and SNMP servers offer basic security and can restrict access \"\n\"while the HTTP server doesn’t offer any access restriction mechanisms. \"\n\"Therefore HTTP monitoring is recommended to operate behind a firewall.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:18\nmsgid \"\"\n\"The monitoring itself causes very low overhead in terms of the resources \"\n\"and CPU consumption on top of the normal Tigase processing requirements \"\n\"so it can be left on without worrying about performance degradation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:20\nmsgid \"\"\n\"NOTE This works with the Tigase server from version **4.2.0** or build \"\n\"**1418**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:23\nmsgid \"What You Need\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:25\nmsgid \"\"\n\"Statistics binaries are built-in ``-dist-max`` and no extra files are \"\n\"needed. If you have downloaded ``-dist`` file, you will need tigase-\"\n\"extras[https://github.com/tigase/tigase-extras] built and included in the\"\n\" ``jars/`` directory.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:28\nmsgid \"Activation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:30\nmsgid \"\"\n\"You can either run the Tigase installer and use the configuration wizard \"\n\"to activate the monitoring or edit etc/config.tdsl file and add following\"\n\" lines:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:46\nmsgid \"\"\n\"As you see there is a separate block for each monitoring server you want \"\n\"to activate. Each server is responsible for activation of a different \"\n\"protocol and takes a single parameter - port number. There are following \"\n\"protocols supported right now:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:48\nmsgid \"``jmx`` - activating monitoring via JMX/RMI\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:50\nmsgid \"``http`` - activating monitoring over HTTP protocol\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:52\nmsgid \"``snmp`` - activating monitoring over SNMP protocol\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:54\nmsgid \"\"\n\"You can have all protocols active at the same time or any combination of \"\n\"them or none.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:57\nmsgid \"Security\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:59\nmsgid \"\"\n\"Both JMX and SNMP offer security protection to limit access to monitoring\"\n\" data. The security configuration is a bit different for both.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:62\nmsgid \"JMX\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:64\nmsgid \"\"\n\"After the server installation or in the SVN repository you can find 2 \"\n\"files in the ``etc/`` directory: ``jmx.access`` and ``jmx.password``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:66\nmsgid \"\"\n\"``jmx.access`` is a user permission file. You can use it to specify \"\n\"whether the user can access the monitoring data for reading only \"\n\"``readonly`` or with read-write ``readwrite`` access. There are example \"\n\"entries in the file already and the content may simply look like:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:73\nmsgid \"\"\n\"``jmx.password`` is a user password file. You can set user passwords here\"\n\" and the format again is very simple and the same as for jmx.access. \"\n\"There are example entries already provided for you convenience. Content \"\n\"of the file may look like the example below:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:80\nmsgid \"\"\n\"Using above to files you can control who and how can access the JMX \"\n\"monitoring services.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:82\nmsgid \"SNMP\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:84\nmsgid \"\"\n\"Access to SNMP monitoring is controlled using ACL (access control lists) \"\n\"which can be configured in the file ``snmp.acl`` located in ``etc/`` \"\n\"directory. It contains lots of detailed instructions how to setup ACL and\"\n\" restrict access per user, host and what kind access is allowed. The \"\n\"simplest possible configuration may look like this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:101\nmsgid \"\"\n\"You might also need Tigase MIB definition: `TIGASE-MANAGEMENT-MIB.mib \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/resources/mib/JVM-MANAGEMENT-MIB.mib>`__ for \"\n\"the server specific statistics. The MIB contains definition for all the \"\n\"server statistics exposed via SNMP.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:103\nmsgid \"HTTP\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:105\nmsgid \"\"\n\"Access the server at example.com:9080 and you will be presented with an \"\n\"Agent View.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:4\nmsgid \"Retrieving statistics from the server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:6\nmsgid \"\"\n\"By default we can retrieve server statistics using XMPP, no additional \"\n\"setup is necessary.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:9\nmsgid \"Retrieving statistics using XMPP\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:11\nmsgid \"\"\n\"Accessing statistics over XMPP protocol requires any XMPP client capable \"\n\"of executing `XEP-0050: Ad-Hoc Commands \"\n\"<http://xmpp.org/extensions/xep-0050.html>`__. It’s essential to \"\n\"remember, that only administrator (a user whose JID is configured as \"\n\"administrative) can access the statistics.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:14\nmsgid \"Psi XMPP Client\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:16\nmsgid \"\"\n\"For the purpose of this guide `Psi <http://psi-im.org/>`__ client will be\"\n\" used. After successfully configuring and connecting to account with \"\n\"administrative privileges we need to access *Service Discovery*, either \"\n\"from application menu or from context menu of the particular account \"\n\"account:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:18\nmsgid \"|roster-discovery|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:74\nmsgid \"roster-discovery\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:20\nmsgid \"\"\n\"In the *Service Discovery* window we need to find *Server Statistics* \"\n\"component:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:22\nmsgid \"|discovery-stats|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:75\nmsgid \"discovery-stats\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:24\nmsgid \"\"\n\"We can either access statistics for all components or select particular \"\n\"component after expanding the tree. To execute ad-hoc command simply \"\n\"double click on the particular node which will open window with \"\n\"statistics:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:26\nmsgid \"|server-stats|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:76\nmsgid \"server-stats\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:28\nmsgid \"\"\n\"In this window, in addition to see the statistics, we can adjust *Stats \"\n\"level* by selecting desired level from the list and confirm by clicking \"\n\"*Finish*.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:31\nmsgid \"Retrieving statistics using JMX\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:33\nmsgid \"\"\n\"In order to access statistics over JMX we need to enable support for it \"\n\"in Tigase - `Monitoring Activation <#monitoring_activation>`__. \"\n\"Afterwards we can use a number of tools to get to the statistics, for \"\n\"example the following:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:36\nmsgid \"JConsole\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:38\nmsgid \"\"\n\"After opening JConsole we either select local process or provide details \"\n\"of the remote process, including IP, port and credentials from \"\n\"**etc/jmx.**\\\\ \\\\* files:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:40\nmsgid \"|jconsole|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:77\nmsgid \"jconsole\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:42\nmsgid \"\"\n\"Afterwards we navigate to the MBeans tab from where we can access the \"\n\"``tigase.stats`` MBean. It offers similar options to XMPP - either \"\n\"accessing statistics for all components or only for particular component \"\n\"as well as adjusting level for which we want to obtain statistics:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:44\nmsgid \"|jconsole-1|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:78\nmsgid \"jconsole-1\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:48\nmsgid \"StatsDumper.groovy\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:50\nmsgid \"\"\n\"In order to collect statistics over period of time following groovy \"\n\"script can be used: `StatsDumper.groovy \"\n\"<https://raw.githubusercontent.com/tigase/tigase-\"\n\"server/master/src/main/restructured/files/StatsDumper.groovy>`__. It’s a \"\n\"Simple JMX client that connects to Tigase and periodically saves all \"\n\"statistics to files.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:52\nmsgid \"It takes following parameters:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:58\nmsgid \"``hostname`` - address of the instance\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:60\nmsgid \"``username`` - JMX username\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:62\nmsgid \"``password`` - JMX username\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:64\nmsgid \"``dir`` - directory to which save the files with statistics\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:66\nmsgid \"``port`` - port on which to make the connection\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:68\nmsgid \"\"\n\"``delay``\\\\ (ms) - initial delay in milliseconds after which statistics \"\n\"should be saved\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:70\nmsgid \"``interval``\\\\ (ms) - interval between each retrieval/saving of statistics\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:72\nmsgid \"\"\n\"``loadhistory``\\\\ (bool) - indicates whether or not load statistics \"\n\"history from server (if such is enabled in Tigase)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:4\nmsgid \"Monitor Component\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:6\nmsgid \"\"\n\"Tigase includes an **Monitor Component** to help with monitoring has been\"\n\" implemented. This allows you to set thresholds for certain predefined \"\n\"tasks and you or other JIDs can be sent a message when those thresholds \"\n\"are passed. You can even configure a mailer extension to have an E-mail \"\n\"sent to system administrators to let them know an event has occurred! \"\n\"Lets begin with setup and requirements.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:8\nmsgid \"\"\n\"Monitor Component is based on eventbus which in turn is based on a \"\n\"limited `PubSub <http://www.xmpp.org/extensions/xep-0060.html>`__ \"\n\"specification. Events are delivered to subscribers as a normal PubSub \"\n\"notification.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:10\nmsgid \"\"\n\"Each component or client may subscribe for specific types of events. Only\"\n\" components on cluster nodes are allowed to publish events.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:13\nmsgid \"Setup\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:15\nmsgid \"\"\n\"Monitor Component is enabled by default on v7.1.0 b4001 and later, so no \"\n\"setup needed!\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:18\nmsgid \"How it Works\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:20\nmsgid \"\"\n\"Events in Eventbus are identified by two elements: name of event and its \"\n\"namespace:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:28\nmsgid \"Where event name is ``EventName`` and namespace is ``tigase:demo``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:30\nmsgid \"\"\n\"Listeners may subscribe for a specific event or for all events with \"\n\"specific a namespace. Because in pubsub, only one node name exists, so we\"\n\" have to add a way to convert the event name and namespace to a node \"\n\"name:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:36\nmsgid \"\"\n\"So for example, to subscribe to ``<EventName xmlns=\\\"tigase:demo\\\">``, \"\n\"node must be: ``EventName|tigase:demo``. If you wish to subscribe to all \"\n\"events with a specific namespace, use an asterisk (``*``) instead of the \"\n\"event name: ``*|tigase:demo``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:45\nmsgid \"Available Tasks\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:47\nmsgid \"\"\n\"Monitor Component has several pre-defined tasks that can be monitored and\"\n\" set to trigger. What follows is the list of tasks with the options \"\n\"attributed to each task.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc\nmsgid \"**disk-task** - Used to check disk usage.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc\nmsgid \"Available Options\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:52\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:61\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:70\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:79\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:89\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:96\nmsgid \"``enabled`` - Enable or disable task, Boolean value.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:54\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:63\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:72\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:81\nmsgid \"``period`` - Period of running check, Integer value.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:56\nmsgid \"``threshold`` - Percentage of used space on disk, Float value.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc\nmsgid \"**cpu-temp-task** - Used to check CPU temperature.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:65\nmsgid \"``cpuTempThreshold`` - Temperature threshold of CPU in °C.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc\nmsgid \"**load-checker-task** - Used to check system load.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:74\nmsgid \"``averageLoadThreshold`` - Average percent load threshold, Long value.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc\nmsgid \"**memory-checker-task** - Used to check memory usage.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:83\nmsgid \"\"\n\"``maxHeapMemUsagePercentThreshold`` - Alarm when percent of used Heap \"\n\"memory is larger than, Integer value.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:85\nmsgid \"\"\n\"``maxNonHeapMemUsagePercentThreshold`` - Alarm when percent of used Non \"\n\"Heap memory is larger than, Integer value.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc\nmsgid \"**logger-task** - Used to transmit log entries depending on level entered.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:91\nmsgid \"\"\n\"``levelThreshold`` - Minimal log level that will be the threshold. \"\n\"Possible values are SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, \"\n\"and ALL.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc\nmsgid \"**connections-task** - Used to check users disconnections.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc\nmsgid \"\"\n\"**NOTE: The event will be generated only if both thresholds (amount and \"\n\"percentage) will be fulfilled.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:98\nmsgid \"``period`` - Period of running check in ms, Integer value.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:100\nmsgid \"\"\n\"``thresholdMinimal`` - Minimal amount of disconnected users required to \"\n\"generate alarm.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:102\nmsgid \"\"\n\"``threshold`` - Minimal percent of disconnected users required to \"\n\"generate alarm.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:7\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:105\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:308\nmsgid \"Configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:107\nmsgid \"\"\n\"Configuration of the monitor can be done one of two ways; either by lines\"\n\" in ``config.tdsl`` file, or by sending XMPP stanzas to the server. You \"\n\"may also send XMPP stanzas VIA HTTP REST. XMPP stanza configurations will\"\n\" override ones in config.tdsl, but they will only last until the server \"\n\"restarts.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:109\nmsgid \"config.tdsl\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:111\nmsgid \"\"\n\"Tasks can be configured in the ``config.tdsl`` file. See :ref:`available \"\n\"tasks<availableTasks>` for the tasks that can be setup.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:113\nmsgid \"To enable a specific monitor task, use the following line:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:123\nmsgid \"\"\n\"Where monitor is the component name for ``MonitorComponent``, and \"\n\"``$TASKNAME`` is one of the :ref:`available task names<availableTasks>`.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:125\nmsgid \"\"\n\"This format will be the same for other settings for tasks, and it’s best \"\n\"to group settings under one heading. For example:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:136\nmsgid \"\"\n\"sets the check period to 1000 milliseconds and enables ``connections-\"\n\"task``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:140\nmsgid \"\"\n\"Once triggers have been activated, they will become dormant. Think of \"\n\"these as one-shot settings.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:143\nmsgid \"Subscription Limitations\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:145\nmsgid \"To define list of JIDs allowed to subscribe for events:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:155\nmsgid \"If this is not specified, all users can subscribe.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:157\nmsgid \"Configuration via XMPP\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:159\nmsgid \"\"\n\"We can also configure the eventbus monitor component using XMPP stanzas. \"\n\"This allows us to set and change configurations during server runtime. \"\n\"This is done using a series of ``iq`` stanzas send to the monitor \"\n\"component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:161\nmsgid \"\"\n\"We can query each component for its current settings using the following \"\n\"stanza.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:169\nmsgid \"\"\n\"The server will return the component current settings which will make \"\n\"things easier if you wish to edit them. In this case, the server has \"\n\"returned the following to us\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:192\nmsgid \"\"\n\"This tells us that the disk-task setting is not active, has a period of \"\n\"60000ms, and will trigger when disk usage is over 80%.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:194\nmsgid \"\"\n\"To send new settings to the monitor component, we can send a similar \"\n\"stanza back to the monitor component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:215\nmsgid \"\"\n\"To which a successful update will give you an XMPP success stanza to let \"\n\"you know everything is set correctly.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:217\nmsgid \"\"\n\"Alternatively, you can update specific settings by editing a single field\"\n\" without adding anything else. For example, if we just wanted to turn the\"\n\" disk-task on we could send the following stanza:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:231\nmsgid \"\"\n\"To set any other values, do not forget that certain parts may need to be \"\n\"changed, specifically the ``<field type=\\\"boolean\\\" \"\n\"var=x-task#enabled\\\">`` fields:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc\nmsgid \"\"\n\"Your field type will be defined by the type of variable specified in the \"\n\":ref:`Available Tasks<availableTasks>` section.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:235\nmsgid \"\"\n\"``var=x task#`` will be followed by the property value taken directly \"\n\"from the ref:`Available Tasks<availableTasks>` section.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:238\nmsgid \"Getting the Message\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:240\nmsgid \"\"\n\"Without a place to send messages to, monitor will just trigger and shut \"\n\"down. There are two different methods that monitor can deliver alarm \"\n\"messages and relevant data; XMPP messages and using the mailer extension.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:242\nmsgid \"XMPP notification\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:244\nmsgid \"\"\n\"In order to retrieve notifications, a subscription to the \"\n\"``eventbus@<VHost>`` user must be made. Keep in mind that subscriptions \"\n\"are not persistent across server restarts, or triggers.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:245\nmsgid \"\"\n\"The monitor schema is very similar to most XMPP subscription requests but\"\n\" with a few tweaks to differentiate it if you wanted to subscribe to a \"\n\"certain task or all of them. Each task is considered a node, and each \"\n\"node has the following pattern: ``eventName|eventXMLNS``. Since each \"\n\"monitoring task has the ``tigase:monitor:event`` event XMLNS, we just \"\n\"need to pick the event name from the list of tasks. So like the above \"\n\"example, our event node for the disk task will be ``disk-\"\n\"task|tigase:monitor:event``. Applied to an XMPP stanza, it will look \"\n\"something like this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:257\nmsgid \"\"\n\"Don’t forget to replace ``$USER_JID`` with the bare JID of the user you \"\n\"want to receive those messages. You can even have them sent to a MUC or \"\n\"any component with a JID.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:259\nmsgid \"Available events are as follows:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:261\nmsgid \"DiskUsageMonitorEvent for ``disk-task``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:263\nmsgid \"LoggerMonitorEvent for ``logger-task``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:265\nmsgid \"HeapMemoryMonitorEvent for ``memory-checker-task``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:267\nmsgid \"LoadAverageMonitorEvent for ``load-checker-task``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:269\nmsgid \"CPUTempMonitorEvent for ``cpu-temp-task``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:271\nmsgid \"UsersDisconnected for ``connections-task``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:273\nmsgid \"\"\n\"Alternatively, you can also subscribe to all events within the eventbus \"\n\"by using a wildcard \\\\* in place of the event XMLNS like this example:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:285\nmsgid \"Sample notification from Monitor\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:302\nmsgid \"Mailer Extension\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:304\nmsgid \"\"\n\"*Tigase Server Monitor Mailer Extension* (TSMME) can send messages from \"\n\"the monitor component to a specified E-mail address so system \"\n\"administrators who are not logged into the XMPP server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:306\nmsgid \"\"\n\"For v7.1.0 versions and later, TSMME is already included in your \"\n\"distribution package and no extra installation is needed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:310\nmsgid \"\"\n\"Tigase Mailer Extension may be configured via the ``config.tdsl`` file in\"\n\" the following manner:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:323\nmsgid \"Here is an explanation of those variables.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:325\nmsgid \"``mailer-smtp-host`` - SMTP Server hostname.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:327\nmsgid \"``mailer-smtp-port`` - SMTP Server port.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:329\nmsgid \"``mailer-smtp-usernam`` - name of sender account.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:331\nmsgid \"``mailer-smtp-password`` - password of sender account.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:333\nmsgid \"\"\n\"``mailer-from-address`` - sender email address. It will be set in field \"\n\"from in email.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:335\nmsgid \"\"\n\"``mailer-to-addresses`` - comma separated notification receivers email \"\n\"addresses.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:337\nmsgid \"\"\n\"It is recommended to create a specific e-mail address in your mail server\"\n\" for this purpose only, as the account settings are stored in plaintext \"\n\"without encryption.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Statistics_Logger_Config.inc:4\nmsgid \"Configuration of statistics loggers\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Statistics_Logger_Config.inc:6\nmsgid \"\"\n\"It is possible to enable and configure automatic storage of statistics \"\n\"information. To do that you need to configure any of following statistics\"\n\" loggers as a ``StatisticsCollector`` component sub-beans:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Statistics_Logger_Config.inc:9\nmsgid \"``tigase.stats.CounterDataArchivizer``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Statistics_Logger_Config.inc:9\nmsgid \"\"\n\"every execution put current basic server metrics (CPU usage, memory \"\n\"usage, number of user connections, uptime) into database (overwrites \"\n\"previous entry).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Statistics_Logger_Config.inc:12\nmsgid \"``tigase.stats.CounterDataLogger``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Statistics_Logger_Config.inc:12\nmsgid \"\"\n\"every execution insert new row with new set of number of server \"\n\"statistics (CPU usage, memory usage, number of user connections per \"\n\"connector, number of processed packets of different types, uptime, etc) \"\n\"into the database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Statistics_Logger_Config.inc:15\nmsgid \"``tigase.stats.CounterDataFileLogger``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Statistics_Logger_Config.inc:15\nmsgid \"every execution store all server statistics into separate file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Statistics_Logger_Config.inc:17\nmsgid \"\"\n\"As an example to configure ``tigase.stats.CounterDataFileLogger`` to \"\n\"archive statistics data with level ``FINE`` every 60 seconds to file \"\n\"prefixed with ``stat`` and located in ``logs/server_statistics`` \"\n\"following entry is needed:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:2\nmsgid \"Server to Server Protocol Settings\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:4\nmsgid \"\"\n\"Tigase server-to-server communication component facilitates communication\"\n\" with other XMPP servers (federation) and allows you to tweak it’s \"\n\"configuration to get a better performance in your installation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:6\nmsgid \"\"\n\"S2S (or server to server) protocol is enabled by default with optimal \"\n\"settings chosen. There are however, a set of configuration parameters you\"\n\" can adjust the server behavior to achieve optimal performance on your \"\n\"installation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:8\nmsgid \"\"\n\"This documents describes following elements of the Tigase server \"\n\"configuration:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:10\nmsgid \"Number of concurrent connections to external servers\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:12\nmsgid \"The connection throughput parameters\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:14\nmsgid \"\"\n\"Maximum waiting time for packets addressed to external servers and the \"\n\"connection inactivity time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:16\nmsgid \"Custom plugins selecting connection to the remote server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:19\nmsgid \"Number of Concurrent Connections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:21\nmsgid \"\"\n\"Normally only one connection to the remote server is required to send \"\n\"XMPP stanza to that server. In some cases however, under a high load, you\"\n\" can get much better throughput and performance if you open multiple \"\n\"connections to the remote server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:23\nmsgid \"\"\n\"This is especially true when the remote server works in a cluster mode. \"\n\"Ideally you want to open a connection to each of the cluster nodes on the\"\n\" remote server. This way you can spread the traffic evenly among cluster \"\n\"nodes and improve the performance for s2s connections.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:25\nmsgid \"\"\n\"Tigase server offers 2 different parameters to tweak the number of \"\n\"concurrent, s2s connections:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:27\nmsgid \"\"\n\"``max-out-total-conns`` - this property specifies the maximum outgoing \"\n\"connections the Tigase server opens to any remote XMPP server. This is a \"\n\"**per domain** limit, which means that this limit applies to each of the \"\n\"remote domains Tigase connects to. If it is set to ``4`` then Tigase \"\n\"opens a maximum of 4 connections to ``jabber.org`` plus maximum 4 \"\n\"connections to ``muc.jabber.org`` even if this is the same physical \"\n\"server behind the same IP address.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:29\nmsgid \"To adjust the limit you have to add following to the ``config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:37\nmsgid \"\"\n\"``max-out-per-ip-conns`` - this property specifies the maximum outgoing \"\n\"connections Tigase server opens to any remote XMPP server to its single \"\n\"IP address. This too, is **per domain** limit, which means that this \"\n\"limit applies to each of the remote domains Tigase connects to. If it is \"\n\"set to ``1``, and the above limit is set to ``4``, and the remote server \"\n\"is visible behind 1 IP address, then Tigase opens a maximum of 1 \"\n\"connection to ``jabber.org`` plus a maximum of 1 connection to \"\n\"``muc.jabber.org`` and other subdomains.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:39\nmsgid \"\"\n\"To adjust the limit you have to add following line to the ``config.tdsl``\"\n\" file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:49\nmsgid \"Connection Throughput\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:51\nmsgid \"\"\n\"Of course everybody wants his server to run with maximum throughput. This\"\n\" comes with a cost on resources, usually increased memory usage. This is \"\n\"especially important if you have large number of s2s connections on your \"\n\"installations. High throughput means lots of memory for network buffers \"\n\"for every single s2s connection. You may soon run out of all available \"\n\"memory.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:53\nmsgid \"\"\n\"There is one configuration property which allows you to adjust the \"\n\"network buffers for s2s connections to lower your memory usage or \"\n\"increase data throughput for s2s communication.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:55\nmsgid \"\"\n\"More details about are available in the :ref:`net-buff-high-\"\n\"throughput<netBuffHighThroughput>` or :ref:`net-buff-\"\n\"Standard<netBuffStandard>` property descriptions.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:58\nmsgid \"Maximum Packet Waiting Time and Connection Inactivity Time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:60\nmsgid \"\"\n\"There are 2 timeouts you can set for the component controlling s2s \"\n\"communication.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:62\nmsgid \"\"\n\"``max-packet-waiting-time`` - this sets the maximum time for the packets \"\n\"waiting for sending to some remote server. Sometimes, due to networking \"\n\"problems or DNS problems it might be impossible to send message to remote\"\n\" server right away. Establishing a new connection may take time or there \"\n\"might be communication problems between servers or perhaps the remote \"\n\"server is restarted. Tigase will try a few times to connect to the remote\"\n\" server before giving up. This parameter specifies how long the packet is\"\n\" waiting for sending before it is returned to the sender with an error. \"\n\"The timeout is specified in seconds:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:70\nmsgid \"\"\n\"``max-inactivity-time`` - this parameters specifies the maximum s2s \"\n\"connection inactivity time before it is closed. If a connection is not in\"\n\" use for a long time, it doesn’t make sense to keep it open and tie \"\n\"resources up. Tigase closes s2s connection after specified period of time\"\n\" and reconnects when it is necessary. The timeout is specified in \"\n\"seconds:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:79\nmsgid \"Custom Plugin: Selecting s2s Connection\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:81\nmsgid \"\"\n\"Sometimes for very large installations you may want to set larger number \"\n\"of s2s connections to remote servers, especially if they work in cluster \"\n\"of several nodes. In such a case you can also have a control over XMPP \"\n\"packets distribution among s2s connections to a single remote server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:83\nmsgid \"\"\n\"This piece of code is pluggable and you can write your own connection \"\n\"selector. It is enough to implement ``S2SConnectionSelector`` interface \"\n\"and set your class name in the configuration using following parameter in\"\n\" ``config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:91\nmsgid \"The default selector picks connections randomly.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:94\nmsgid \"skip-tls-hostnames\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:96\nmsgid \"\"\n\"The ``s2s-skip-tls-hostnames`` property disables TLS handshaking for s2s \"\n\"connections to selected remote domains. Unfortunately some servers \"\n\"(certain versions of Openfire - [`1 \"\n\"<http://community.igniterealtime.org/thread/36206>`__] or [`2 \"\n\"<http://community.igniterealtime.org/thread/30578>`__]) have problems \"\n\"with TLS handshaking over s2s which prevents establishing a usable \"\n\"connection. This completely blocks any communication to these servers. As\"\n\" a workaround you can disable TLS for these domains to get communication \"\n\"back. Enabling this can be done on any vhost, but must be configured \"\n\"under the s2s component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:105\nmsgid \"ejabberd-bug-workaround\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:107\nmsgid \"\"\n\"This property activates a workaround for a bug in EJabberd in it’s s2s \"\n\"implementation. EJabberd does not send dialback in stream features after \"\n\"TLS handshaking even if the dialback is expected/needed. This results in \"\n\"unusable connection as EJabberd does not accept any packets on this \"\n\"connection either. The workaround is enabled by default right now until \"\n\"the EJabberd version without the bug is popular enough. A disadvantage of\"\n\" the workaround is that dialback is always performed even if the SSL \"\n\"certificate is fully trusted and in theory this dialback could be \"\n\"avoided. By default, this is not enabled.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:117\nmsgid \"This replaces the old ``--s2s-ejabberd-bug-workaround-active`` property.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:4\nmsgid \"Tigase Load Balancing\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:6\nmsgid \"\"\n\"Tigase includes load balancing functionality allowing users to be \"\n\"redirected to the most suitable cluster node. Functionality relies on a \"\n\"see-other-host XMPP stream error message. The basic principle behind the \"\n\"mechanism is that user will get redirect if the host returned by the \"\n\"implementation differ from the host to which user currently tries to \"\n\"connect. It is required that the user JID to be known for the redirection\"\n\" to work correctly.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:9\nmsgid \"Available Implementations\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:11\nmsgid \"\"\n\"Tigase implementation is, as usual, extensible and allows for different, \"\n\"pluggable redirection strategies that implement the ``SeeOtherHostIfc`` \"\n\"interface.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:13\nmsgid \"Currently there are three strategies available:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:15\nmsgid \"\"\n\"``SeeOtherHost`` - most basic implementation returning either single host\"\n\" configured in ``config.tdsl`` file or name of the current host;\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:17\nmsgid \"\"\n\"``SeeOtherHostHashed`` (default) - default implementation for cluster \"\n\"environment of SeeOtherHostIfc returning redirect host based on the hash \"\n\"value of the user’s JID; list of the available nodes from which a \"\n\"selection would be made is by default composed and reflects all connected\"\n\" nodes, alternatively hosts list can be configured in the config.tdsl;\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:19\nmsgid \"\"\n\"``SeeOtherHostDB`` - extended implementation of SeeOtherHost using \"\n\"redirect information from database in the form of pairs ``user_id`` and \"\n\"``node_id`` to which given user should be redirected.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:21\nmsgid \"\"\n\"``SeeOtherHostDualIP`` - matches internal Tigase cluster nodes against \"\n\"the lookup table to provide relevant redirection hostname/IP (by default \"\n\"internal Tigase tig_cluster_nodes table will be used)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:24\nmsgid \"Configuration Options\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:26\nmsgid \"\"\n\"The most basic configuration is related to the choice of actual \"\n\"redirection implementation by declaring class for each connector:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:40\nmsgid \"Possible values are:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:42\nmsgid \"``tigase.server.xmppclient.SeeOtherHost``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:44\nmsgid \"``tigase.server.xmppclient.SeeOtherHostHashed``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:46\nmsgid \"``tigase.server.xmppclient.SeeOtherHostDB``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:48\nmsgid \"``tigase.server.xmppclient.SeeOtherHostDualIP``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:50\nmsgid \"``none`` - disables redirection\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:52\nmsgid \"\"\n\"All options are configured on a per-connection-manager basis, thus all \"\n\"options need to be prefixed with the corresponding connection manager ID,\"\n\" i.e. c2s, bosh or ws; we will use c2s in the examples:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:63\nmsgid \"\"\n\"``'default-host' = 'host1;host2;host3'`` - a semicolon separated list of \"\n\"hosts to be used for redirection.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:65\nmsgid \"\"\n\"``'phases' = []`` - an array of phases in which redirection should be \"\n\"active, currently possible values are:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:67\nmsgid \"``OPEN`` which enables redirection during opening of the XMPP stream;\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:69\nmsgid \"``LOGIN`` which enables redirection upon authenticating user session;\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:71\nmsgid \"By default redirection is currently enabled only in the ``OPEN`` phase.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:74\nmsgid \"SeeOtherHostDB\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:76\nmsgid \"For ``SeeOtherHostDB`` implementation there are additional options:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:87\nmsgid \"\"\n\"``db-url`` - a JDBC connection URI which should be used to query redirect\"\n\" information; if not configured the default ``dataSource`` will be used;\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:89\nmsgid \"``get-host-query`` - a SQL query which should return redirection hostname;\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:91\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:120\nmsgid \"\"\n\"``get-all-data-query`` - a SQL helper query which should return all \"\n\"redirection data from database;\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:93\nmsgid \"``get-all-query-timeout`` - allows to set timeout for executed queries.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:96\nmsgid \"SeeOtherHostDualIP\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:98\nmsgid \"\"\n\"This mechanisms matches internal Tigase cluster nodes against the lookup \"\n\"table to provide matching and relevant redirection hostname/IP. By \"\n\"default internal Tigase ``tig_cluster_nodes`` table is used (and \"\n\"appropriate repository implementation will be used).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:100\nmsgid \"\"\n\"To enable this redirection mechanism following configuration / class \"\n\"should be used. Note that for global use, all connection managers must \"\n\"have the same class defined. You can define each connection manager \"\n\"individually.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:114\nmsgid \"It offers following configuration options:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:116\nmsgid \"\"\n\"``data-source`` - configuration of the source of redirection information \"\n\"- by default internal Tigase ``tig_cluster_nodes`` table will be used \"\n\"(and appropriate repository implementation will be used); alternatively \"\n\"it’s possible to use ``eventbus`` source;\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:118\nmsgid \"\"\n\"``db-url`` - a JDBC connection URI which should be used to query redirect\"\n\" information; if not configured ``user-db-uri`` will be used;\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:122\nmsgid \"``get-all-query-timeout`` - allows to set timeout for executed queries;\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:124\nmsgid \"\"\n\"``fallback-redirection-host`` - if there is no redirection information \"\n\"present (i.e. secondary hostname is not configured for the particular \"\n\"node) redirection won’t be generated; with this it’s possible to \"\n\"configure fallback redirection address.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:126\nmsgid \"All options are configured or on per-component basis:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:140\nmsgid \"EventBus as a source of information\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:142\nmsgid \"\"\n\"It’s possible to utilize EventBus and internal Tigase events as a source \"\n\"of redirection data. In order to do that ``eventbus-repository-\"\n\"notifications`` needs to be enabled in ClusterConnectionManager:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:152\nmsgid \"Auxiliary setup options\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:155\nmsgid \"Enforcing redirection\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:157\nmsgid \"\"\n\"It’s possible to enforce redirection of connections on the particular \"\n\"port of connection manager with ``force-redirect-to`` set to Integer with\"\n\" the following general setting option:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:169\nmsgid \"\"\n\"for example, enable additional port ``5322`` for ``c2s`` connection \"\n\"manager and enforce all connections to be redirected to port ``5222`` (it\"\n\" will utilize hostname retrieved from ``SeeOtherHost`` implementation and\"\n\" will be only used when such value is returned):\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:186\nmsgid \"Configuring hostnames\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:188\nmsgid \"\"\n\"To fully utilize ``SeeOtherHostDualIP`` setup in automated fashion it’s \"\n\"now possible to provide both primary (*internal*) and secondary \"\n\"(*external*) hostname/IP (they need to be correct, \"\n\"``InetAddress.getByName( property );`` will be used to verify \"\n\"correctness). It can be done via JVM properties ``tigase-primary-\"\n\"address`` and ``tigase-secondary-address``. You can also utilize \"\n\"different implementation of DNS resolver by providing class implementing \"\n\"``tigase.util.DNSResolverIfc`` interface as value to ``resolver-class`` \"\n\"property. Those properties can be set via ``etc/tigase.conf`` \"\n\"(uncommenting following lines, or manually exposing in environment):\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:197\nmsgid \"or in the ``etc/config.tdsl`` (they will be converted to JVM properties):\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:4\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Intro.inc:2\nmsgid \"External Component Configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Intro.inc:4\nmsgid \"\"\n\"Tigase can connect to external components, this guide will show you how \"\n\"this can be accomplished.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Intro.inc:6\nmsgid \"\"\n\"Configuration follows the same standards as all other components. It is \"\n\"also much more powerful as a single Tigase instance can control many \"\n\"TCP/IP ports and many external components on each port and even allows \"\n\"for multiple connections for the same component. It supports both \"\n\"XEP-0114 and XEP-0225 with protocol auto-detection mechanisms. Protocols \"\n\"are pluggable so more protocols can be supported or custom extensions to \"\n\"existing protocols can be added.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Intro.inc:8\nmsgid \"\"\n\"The implementation also supports a scripting API and new domains with \"\n\"passwords can be added at run-time using ad-hoc commands. New scripts can\"\n\" be loaded to even further control all connected external components.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Intro.inc:10\nmsgid \"\"\n\"Pages in this guide describe in details all the administration aspects of\"\n\" setting up and managing external components.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Intro.inc:12\nmsgid \":ref:`External Component Configuration<External-Component-Configuration>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Intro.inc:14\nmsgid \":ref:`Tigase as an External Component<Tigase-as-an-External-Component>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Intro.inc:16\nmsgid \"\"\n\":ref:`Load Balancing External Components in Cluster Mode<Load-Balancing-\"\n\"External-Components-in-Cluster-Mode>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:6\nmsgid \"\"\n\"As for all Tigase components you can load and configure external \"\n\"components via the ``config.tdsl`` file described in details in the \"\n\":ref:`DSL configuration<dslConfig>` section. This document describes how \"\n\"to enable the component and set the initial configuration to accept or \"\n\"initiate connections for an external component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:8\nmsgid \"\"\n\"First thing to do is to specify the component class and the component \"\n\"name which must be unique within the Tigase installation. The most \"\n\"commonly name used is ``ext`` and the class is \"\n\"``tigase.server.ext.ComponentProtocol`` (class doesn’t have to be \"\n\"specified when using default name).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:10\nmsgid \"\"\n\"The following line in the ``config.tdsl`` will load the component during \"\n\"the server startup time:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:16\nmsgid \"\"\n\"While this would load the component, without any additional \"\n\"configurations provided, the component would be practically useless. It \"\n\"is necessary to configure the virtual host domains of the external \"\n\"component during run-time via ad-hoc commands to make use of this \"\n\"component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:18\nmsgid \"\"\n\"You may additionally configure the :ref:`bind-ext-\"\n\"hostnames<bindExtHostnames>` property.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:20\nmsgid \"\"\n\"To configure external component connections using Admin UI you need to \"\n\"open Admin UI web page (if you are logged in the same computer on which \"\n\"Tigase XMPP Server is running by default it should be available at \"\n\"http://localhost:8080/admin/). Then you should click on ``Configuration``\"\n\" on the left side of the Admin UI web page and then select ``Add new \"\n\"item`` on ``ext`` component or by execution corresponding ad-hoc command \"\n\"on ``ext`` component using ad-hoc capable XMPP client, ie. `Psi <http\"\n\"://psi-im.org>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:22\nmsgid \"|adminui ext add item button|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:58\nmsgid \"adminui ext add item button\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:24\nmsgid \"\"\n\"You will be presented with a form which you should fill to configure \"\n\"external component connection details:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:26\nmsgid \"|adminui ext add item form|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:59\nmsgid \"adminui ext add item form\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:28\nmsgid \"*Domain name* - external component domain name (``muc.devel.tigase.org``)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:30\nmsgid \"\"\n\"*Domain password* - password for authentication of the external component\"\n\" connection (``muc-pass``)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:32\nmsgid \"\"\n\"*Connection type* - ``accept`` to make component wait for connection or \"\n\"``connect`` force component to connect to the server (``connect``)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:34\nmsgid \"\"\n\"*Port number* - port on which component should wait for connection or on \"\n\"which it try to connect (``5270``)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:36\nmsgid \"\"\n\"*Remote host* - host to connect to (``devel.tigase.org``) *(may be left \"\n\"blank if component will only accept connections)*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:38\nmsgid \"*Protocol* - id of protocol used for establishing connection\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:40\nmsgid \"if connection type is ``connect``:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:42\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:50\nmsgid \"\"\n\"``XEP-0114: Jabber Component Protocol (accept)`` - for `XEP-0114: Jabber \"\n\"Component Protocol <https://xmpp.org/extensions/xep-0114.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:44\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:52\nmsgid \"\"\n\"``XEP-0225: Component Connections`` - for `XEP-0225: Component \"\n\"Connections <https://xmpp.org/extensions/xep-0225.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:46\nmsgid \"if connection type is ``accept``:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:48\nmsgid \"\"\n\"``Autodetect`` - for automatic detection of protocol used by incoming \"\n\"connection *(recommended)*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:54\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:94\nmsgid \"Additional options may be left with defaults.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:56\nmsgid \"\"\n\"Later on if you would like to modify this values, you can do that using \"\n\"Admin UI by clicking on ``Configuration`` and ``Remove an item`` or \"\n\"``Update item configuration`` at ``ext`` component or by execution \"\n\"corresponding ad-hoc commands on ``ext`` component using ad-hoc capable \"\n\"XMPP client, ie. `Psi <http://psi-im.org>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:4\nmsgid \"Tigase as an External Component\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:6\nmsgid \"\"\n\"There are cases when you want to deploy one or more Tigase components \"\n\"separately from the main server, or perhaps you want to run some Tigase \"\n\"components connecting to a different XMPP server, or perhaps you work on \"\n\"a component and you do not want to restart the main server every time you\"\n\" make a change.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:8\nmsgid \"\"\n\"There is a way to run the Tigase server in *external component mode*. In \"\n\"fact you can run any of Tigase’s components as an external component and \"\n\"connect them to the main XMPP server either via `XEP-0114 \"\n\"<http://xmpp.org/extensions/xep-0114.html>`__ or `XEP-0225 \"\n\"<http://xmpp.org/extensions/xep-0225.html>`__ connection.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:10\nmsgid \"Let’s look at the examples…​\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:13\nmsgid \"Usage with shared database (since version 8.0.0)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:15\nmsgid \"\"\n\"When you are using Tigase server 8.0.0 or newer in the \\\"external \"\n\"component mode\\\" while using shared default \\\"user repository\\\" and you \"\n\"have main server also running Tigase XMPP Server 8.0.0 or newer, then you\"\n\" can benefit from the remote management of the component connections from\"\n\" the main server. To use that, you need to enable external component and \"\n\"external component manager on the main server by adding following line to\"\n\" the config file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:22\nmsgid \"\"\n\"With that in place you can use Admin UI or ad-hoc commands available at \"\n\"``ext-man`` component of the main server to configure connection details \"\n\"of the servers running in the ``component`` mode.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:24\nmsgid \"\"\n\"In Admin UI you click on ``Configuration`` section and select ``Add new \"\n\"item`` at the ``ext-man`` component, which will present you with a \"\n\"following form to fill in external component connectivity details:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:26\nmsgid \"|adminui extman add item form|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:285\nmsgid \"adminui extman add item form\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:29\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:161\nmsgid \"A Simple Case - MUC as an External Component\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:31\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:163\nmsgid \"A few assumptions:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:33\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:165\nmsgid \"\"\n\"We want to run a MUC component for a domain: ``muc.devel.tigase.org`` and\"\n\" password ``muc-pass``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:35\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:167\nmsgid \"\"\n\"The main server works at an address: devel.tigase.org and for the same \"\n\"virtual domain\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:37\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:169\nmsgid \"\"\n\"We want to connect to the server using `XEP-0114 \"\n\"<http://xmpp.org/extensions/xep-0114.html>`__ protocol and port ``5270``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:39\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:171\nmsgid \"\"\n\"There is a special configuration type for this case which simplifies \"\n\"setting needed to run Tigase as an external component:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:45\nmsgid \"\"\n\"Knowing that we can now create simple configuration file for Tigase XMPP \"\n\"Server:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:68\nmsgid \"\"\n\"where ``master_server_default_database_url`` is the same URL as the one \"\n\"used on the main server for default data source.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:70\nmsgid \"\"\n\"With that in place we can use ad-hoc commands or Admin UI on the main \"\n\"server to configure Tigase XMPP Server to accept external component \"\n\"connections and to connect from the external component to the master \"\n\"server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:72\nmsgid \"\"\n\"**Adding external component connection settings to the manager (ext-man) \"\n\"using Admin UI.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:74\nmsgid \"|adminui extman add item form external muc|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:286\nmsgid \"adminui extman add item form external muc\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:76\nmsgid \"You need to pass:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:78\nmsgid \"Domain name - external component domain name (``muc.devel.tigase.org``)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:80\nmsgid \"\"\n\"Domain password - password for authentication of the external component \"\n\"connection (``muc-pass``)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:82\nmsgid \"\"\n\"Connection type - ``accept`` to make component wait for connection or \"\n\"``connect`` force component to connect to the server (``connect``)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:84\nmsgid \"\"\n\"Port number - port on which component should wait for connection or on \"\n\"which it try to connect (``5270``)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:86\nmsgid \"Remote host - host to connect to (``devel.tigase.org``)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:88\nmsgid \"Protocol - id of protocol used for establishing connection\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:90\nmsgid \"\"\n\"``XEP-0114: Jabber Component Protocol (accept)`` - establish connection \"\n\"using `XEP-0114: Jabber Component Protocol \"\n\"<https://xmpp.org/extensions/xep-0114.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:92\nmsgid \"\"\n\"``XEP-0225: Component Connections`` - establish connection using \"\n\"`XEP-0225: Component Connections \"\n\"<https://xmpp.org/extensions/xep-0225.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:96\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:211\nmsgid \"More Components\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:98\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:213\nmsgid \"\"\n\"Suppose you want to run more than one component as an external components\"\n\" within one Tigase instance. Let’s add another - PubSub component to the \"\n\"configuration above and see how to set it up.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:100\nmsgid \"\"\n\"The most straightforward way is just to add another component to the \"\n\"server running in the component mode for the component domain\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:123\nmsgid \"\"\n\"and then to add new connection domain to the main server external \"\n\"component settings and to the external component manager settings. You \"\n\"basically do the same thing as you did while adding only MUC component as\"\n\" the external component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:125\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:248\nmsgid \"\"\n\"Please note however that we are opening two connections to the same \"\n\"server. This can waste resources and over-complicate the system. For \"\n\"example, what if we want to run even more components? Opening a separate \"\n\"connection for each component is a tad overkill.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:127\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:250\nmsgid \"\"\n\"In fact there is a way to reuse the same connection for all component \"\n\"domains running as an external component. The property ``bind-ext-\"\n\"hostnames`` contains a comma separated list of all hostnames (external \"\n\"domains) which should reuse the existing connection.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:129\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:252\nmsgid \"\"\n\"There is one catch however. Since you are reusing connections (hostname \"\n\"binding is defined in `XEP-0225 \"\n\"<http://xmpp.org/extensions/xep-0225.html>`__ only), you must use this \"\n\"protocol for the functionality.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:131\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:254\nmsgid \"\"\n\"Here is an example configuration with a single connection over the \"\n\"`XEP-0225 <http://xmpp.org/extensions/xep-0225.html>`__ protocol used by \"\n\"both external domains:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:156\nmsgid \"\"\n\"With this configuration you do not need to configure entries in ``ext-\"\n\"man`` for PubSub component, only for MUC component but you need to user \"\n\"``client`` as the value for protocol field.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:159\nmsgid \"Usage with a separate database\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:177\nmsgid \"\"\n\"This generates a configuration for Tigase with only one component loaded \"\n\"by default - the component used for external component connection. If you\"\n\" use this configuration type, your ``config.tdsl`` file may look like \"\n\"this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:200\nmsgid \"\"\n\"To make this new instance connect to the Tigase XMPP Server, you need to \"\n\"create one more file with external connection configuration at \"\n\"``etc/externalComponentItems`` which will be loaded to the local database\"\n\" and then removed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:208\nmsgid \"\"\n\"While loading configuration from ``etc/externalComponentItems`` file is \"\n\"supported, we recommend usage of shared database if possible. In future \"\n\"this method may be deprecated.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:215\nmsgid \"\"\n\"The most straightforward way is just to add another external component \"\n\"connection to the main server for the component domain using Admin UI or \"\n\"ad-hoc command on the main server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:217\nmsgid \"\"\n\"Then we can use following configuration on the server running in the \"\n\"``component`` mode:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:241\nmsgid \"\"\n\"and we need to create a file with configuration for external component \"\n\"connection which will be loaded to the internal database:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:279\nmsgid \"and example of the external connections configuration file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:4\nmsgid \"Load Balancing External Components in Cluster Mode\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:6\nmsgid \"\"\n\"This document describes how to load balance any external components using\"\n\" Tigase XMPP Server and how to make Tigase’s components work as external \"\n\"components in a cluster mode.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:8\nmsgid \"\"\n\"*Please note, all configuration options described here apply to Tigase \"\n\"XMPP Server version 8.0.0 or later.*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:10\nmsgid \"These are actually 2 separate topics:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:12\nmsgid \"\"\n\"One is to distribute load over many instances of a single component to \"\n\"handle larger traffic, or perhaps for high availability.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:14\nmsgid \"\"\n\"The second is to make Tigase’s components work as an external component \"\n\"and make it work in a cluster mode, even if the component itself does not\"\n\" support cluster mode.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:16\nmsgid \"\"\n\"Here are step by step instructions and configuration examples teaching \"\n\"how to achieve both goals.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:19\nmsgid \"Load Balancing External Component\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:21\nmsgid \"\"\n\"The first, and most simple scenario is to connect multiple instances of \"\n\"an external component to a single Tigase XMPP Server to distribute load.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:23\nmsgid \"\"\n\"There are at least 2 reasons why this would be an optimal solution: one \"\n\"would be to spread load over more instances/machines and the second is to\"\n\" improve reliability in case one component fails the other one can take \"\n\"over the work.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:25\nmsgid \"So here is a simple picture showing the use case.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:27\nmsgid \"|ExternalCompClustering002|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:82\nmsgid \"ExternalCompClustering002\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:29\nmsgid \"\"\n\"We have a single machine running Tigase XMPP Server and 2 instances of \"\n\"the MUC component connecting to Tigase.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:31\nmsgid \"\"\n\"On the server side we will enable ``ComponentProtocol`` component as we \"\n\"need to do to enable external component without clustering support.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:33\nmsgid \"\"\n\"Then using Admin UI we will add a new external component connection \"\n\"settings using ``Add item`` position for ``ext`` component in \"\n\"``Configuration`` section of the web page just as it is described in \"\n\"`External Component Configuration <#tigaseExternalComponent>`__ section.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:35\nmsgid \"|adminui ext add item form_1|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:83\nmsgid \"adminui ext add item form_1\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:37\nmsgid \"\"\n\"The only change here is that we will specify value for field ``Load \"\n\"balancer class`` and we will use ``ReceiverBareJidLB`` as a value.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:39\nmsgid \"\"\n\"The configuration for both instances of the MUC component (identical for \"\n\"both of them) can be done in the same way as it is done for a single \"\n\"instance of the MUC component. There is nothing to change here.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:41\nmsgid \"\"\n\"The difference is one small element in the server configuration. At the \"\n\"value of ``Load balancer class`` field in ``Add item`` form is set to \"\n\"**ReceiverBareJidLB**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:43\nmsgid \"\"\n\"This is the load balancing plugin class. Load balancing plugin decides \"\n\"how the traffic is distributed among different component connections that\"\n\" is different component instances. For the MUC component it makes sense \"\n\"to distribute the traffic based on the receiver bare JID because this is \"\n\"the MUC room address. This way we just distribute MUC rooms and traffic \"\n\"over different MUC component instances.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:45\nmsgid \"\"\n\"This distribution strategy does not always work for all possible \"\n\"components however. For transports for example this would not work at \"\n\"all. A better way to spread load for transports would be based on the \"\n\"source bare JID. And it is possible if you use plugin with class name: \"\n\"**SenderBareJidLB**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:47\nmsgid \"\"\n\"This are two basic load distribution strategies available now. For some \"\n\"use cases none of them is good enough. If you have PubSub, then you \"\n\"probably want to distribute load based on the PubSub node. There is no \"\n\"plugin for that yet but it is easy enough to write one and put the class \"\n\"name in configuration.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:50\nmsgid \"External Component and Cluster\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:52\nmsgid \"\"\n\"If you want to use Tigase’s component in a cluster mode which does not \"\n\"have clustering implemented yet there is a way to make it kind of \"\n\"cluster-able. In the previous section we connected many MUC components to\"\n\" a single Tigase server. Now we want to connect a single MUC component to\"\n\" many Tigase servers (or many Tigase cluster nodes).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:54\nmsgid \"\"\n\"Let’s say we have Tigase XMPP Server working for domain: **xmpp-\"\n\"test.org** and the server is installed on three cluster nodes: **red\"\n\".xmpp-test.org,** **green.xmpp-test.org** and **blue.xmpp-test.org.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:56\nmsgid \"|ExternalCompClustering003 0|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:84\nmsgid \"ExternalCompClustering003 0\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:58\nmsgid \"\"\n\"We want to make it possible to connect the MUC component to all nodes. To\"\n\" do so, we are configuring Tigase XMPP Server to run in the cluster mode \"\n\"and on each of cluster nodes we need to enable ``ComponentProtocol`` \"\n\"component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:60\nmsgid \"\"\n\"This can be simply done by adding following line to the server \"\n\"configuration file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:66\nmsgid \"\"\n\"After this is done we need to add a new external component connection \"\n\"settings using ``Add item`` position for ``ext`` component in \"\n\"``Configuration`` section of the web page just as it is described in \"\n\":ref:`External Component Configuration<External-Component-Configuration>`\"\n\" section.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:68\nmsgid \"\"\n\"As you can see there is nothing special here. The most interesting part \"\n\"comes on the MUC side, but it is only a very small change from the \"\n\"configuration of the component to use with single node Tigase XMPP Server\"\n\" installation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:70\nmsgid \"\"\n\"When you are adding/configuring external component settings using Admin \"\n\"UI (``Add item`` or ``Update item configuration`` for ``ext-man`` \"\n\"component) or using separate configuration file (when you are not using \"\n\"shared database) then you need to pass as a value for ``Remote host`` \"\n\"field a semicolon separated list of all of the cluster nodes to which \"\n\"external component should connect.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:72\nmsgid \"In our case it would be:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:78\nmsgid \"\"\n\"As you can see remote host name is not a simple domain but a character \"\n\"string with a few comma separated parts. The first part is our remote \"\n\"domain and the rest are addresses of the host to connect to. This can be \"\n\"a list of domain names or IP addresses.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:80\nmsgid \"\"\n\"Of course it is possible to connect multiple external component to all \"\n\"cluster nodes, this way the whole installation would be really working in\"\n\" the cluster and also load balanced.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:2\nmsgid \"Client to Server Communication\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:4\nmsgid \"\"\n\"Client to server communication is an integral part of XMPP communication.\"\n\" C2S handles all client communication to the server, and is responsible \"\n\"for filtering and handling remote communications. C2S CAN be disabled, \"\n\"however doing so will only allow communication of internal components, \"\n\"and S2S communications.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:9\nmsgid \"To disable C2S, use the following line in ``config.tdsl`` folder.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:15\nmsgid \"Otherwise, C2S component is activated by default.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:18\nmsgid \"Connections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:20\nmsgid \"\"\n\"The connections container houses all configuration related to connections\"\n\" with the component. Each port may be configured individually.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:36\nmsgid \"new-connections-throttling\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:38\nmsgid \"\"\n\"The property allows you to limit how many new users' connection per \"\n\"second the server accepts on a particular port. Connections established \"\n\"within the limit are processed normally, all others are simply \"\n\"disconnected. This allows you to avoid server overload in case there is a\"\n\" huge number of users trying to connect at the same time. Mostly this \"\n\"happens after a server restart.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:50\nmsgid \"\"\n\"Here, this limits the number to 150 connections per second before \"\n\"connection attempts are dropped.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:52\nmsgid \"This replaces the old ``--new-connections-throttling`` property.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:55\nmsgid \"Resumption timeout\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:57\nmsgid \"\"\n\"It is now possible to set a default stream resumption timeout that the \"\n\"server uses. This allows control of how long a server will wait for a \"\n\"reconnection from a client. This can be particularly helpful to manage \"\n\"mobile clients connecting to your server as they may not have complete \"\n\"coverage, and you do not want to close the stream right away. By default,\"\n\" Tigase sets this value to 60 seconds.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:67\nmsgid \"\"\n\"This sets the default timeout to 90 seconds. You may, if you choose, \"\n\"specify a maximum timeout time, which will allow the server to wait \"\n\"between the default and maximum before a connection is closed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:79\nmsgid \"\"\n\"If the max-resumption-timeout is not set, it will always equal the \"\n\"resumption-timeout number, or default is none is set.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:81\nmsgid \"Available since v7.1.0\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:84\nmsgid \"Packet Redelivery\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:86\nmsgid \"\"\n\"Normally packets are handled by C2S and are typically processed in the \"\n\"first run, however if that fails to send, a retry of sending that packet \"\n\"will occur after 60 seconds. If that second try fails, the delay will \"\n\"increase by a factor of 1.5. This means that the next retry will occur at\"\n\" 90, 135, and so on until the retry count is reached. By default this \"\n\"count is 15, however it can be changed by using the following setting:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:94\nmsgid \"\"\n\"This setting prevents packet redelivery attempts from continuing into \"\n\"infinity (or when the host machine runs out of memory).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Service_Discovery_Component.inc:2\nmsgid \"Tigase External Service Discovery\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Service_Discovery_Component.inc:4\nmsgid \"\"\n\"Welcome to the Tigase External Service Discovery component user guide. \"\n\"Component provides support for `XEP-0215: External Service Discovery \"\n\"<http://xmpp.org/extensions/xep-0215.html>`__ which allows discovery of \"\n\"external services which are not accessible using XMPP protocol.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Service_Discovery_Component.inc:7\nmsgid \"Setup & Configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Service_Discovery_Component.inc:9\nmsgid \"\"\n\"Component (which is implemented in class \"\n\"``tigase.server.extdisco.ExternalServiceDiscoveryComponent``) is by \"\n\"default registered under name ``ext-disco`` and disabled. To enable it \"\n\"you need to enable it in configuration. Example:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Service_Discovery_Component.inc:11\nmsgid \"in DSL format:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Service_Discovery_Component.inc:17\nmsgid \"\"\n\"Additionally you need to activate ``urn:xmpp:extdisco:2`` XMPP processor \"\n\"in ``SessionManager`` by:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Service_Discovery_Component.inc:19\nmsgid \"in DSL - enable subbean of ``sess-man``:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Components/External_Service_Discovery_Component.inc:27\nmsgid \"\"\n\"List of external services returned by server is configurable using ad-hoc\"\n\" commands provided for this component. AdHoc commands are accessible only\"\n\" for server administrator using XMPP client with support for AdHoc \"\n\"commands or using Tigase Admin UI. Usage of AdHoc commands provides \"\n\"easiest and flexible way to add, modify or remove entries for services \"\n\"which will be returned by discovery.\"\nmsgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Setting up Remote Monitoring in the \"\n#~ \"Server <#Setting-Up-Remote-Monitoring-\"\n#~ \"in-the-Server>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Statistics Logger Configuration <#Configuration-\"\n#~ \"of-statistics-loggers>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Retrieving Statistics from the Server \"\n#~ \"<#Retrieving-statistics-from-the-server>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Monitor Component <#Monitor-Component>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"In order to collect statistics over \"\n#~ \"period of time following groovy script\"\n#~ \" can be used: `StatsDumper.groovy \"\n#~ \"<files/StatsDumper.groovy>`__. It’s a Simple \"\n#~ \"JMX client that connects to Tigase \"\n#~ \"and periodically saves all statistics to\"\n#~ \" files.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"ref:`Statistics Logger Configuration<Configuration-\"\n#~ \"of-statistics-loggers>`\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"ref:`Retrieving Statistics from the Server\"\n#~ \"<Retrieving-statistics-from-the-server>`\"\n#~ msgstr \"\"\n\n#~ msgid \"ref:`Monitor Component<Monitor-Component>`\"\n#~ msgstr \"\"\n\n#~ msgid \"**Note**\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"If client is subscribed to \"\n#~ \"``*|tigase:demo node``, then events will \"\n#~ \"not be sent from node ``*|tigase:demo``,\"\n#~ \" but from the **real** node (in \"\n#~ \"this case: ``EventName|tigase:demo``).\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"Tasks can be configured in the \"\n#~ \"``config.tdsl`` file. See `available tasks \"\n#~ \"<#availableTasks>`__ for the tasks that \"\n#~ \"can be setup.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"Where monitor is the component name \"\n#~ \"for ``MonitorComponent``, and ``$TASKNAME`` is\"\n#~ \" one of the `available task names \"\n#~ \"<#availableTasks>`__.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"Your field type will be defined by\"\n#~ \" the type of variable specified in\"\n#~ \" the `Available Tasks <#availableTasks>`__ \"\n#~ \"section.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"``var=x task#`` will be followed by \"\n#~ \"the property value taken directly from\"\n#~ \" the `Available Tasks <#availableTasks>`__ \"\n#~ \"section.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"More details about are available in \"\n#~ \"the `net-buff-high-throughput \"\n#~ \"<#netBuffHighThroughput>`__ or `net-buff-\"\n#~ \"Standard <#netBuffStandard>`__ property \"\n#~ \"descriptions.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`External Component Configuration <#External-\"\n#~ \"Component-Configuration>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Tigase as an External Component <#Tigase-as-an-External-Component>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Load Balancing External Components in \"\n#~ \"Cluster Mode <#Load-Balancing-External-\"\n#~ \"Components-in-Cluster-Mode>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"As for all Tigase components you \"\n#~ \"can load and configure external \"\n#~ \"components via the ``config.tdsl`` file \"\n#~ \"described in details in the `DSL \"\n#~ \"configuration <#dslConfig>`__ section. This \"\n#~ \"document describes how to enable the \"\n#~ \"component and set the initial \"\n#~ \"configuration to accept or initiate \"\n#~ \"connections for an external component.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"You may additionally configure the \"\n#~ \"```bind-ext-hostnames`` <#bindExtHostnames>`__ \"\n#~ \"property.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"After this is done we need to \"\n#~ \"add a new external component connection\"\n#~ \" settings using ``Add item`` position \"\n#~ \"for ``ext`` component in ``Configuration`` \"\n#~ \"section of the web page just as\"\n#~ \" it is described in `External \"\n#~ \"Component Configuration <#tigaseExternalComponent>`__ \"\n#~ \"section.\"\n#~ msgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Administration/Configuration/_Configuration.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-11-15 00:08-0800\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.10.3\\n\"\n\n#: ../../Tigase_Administration/Configuration/_Configuration.rst:2\nmsgid \"Configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/_Configuration.rst:4\nmsgid \"\"\n\"When the user tries to setup the client for the first time he comes \"\n\"across 2 configuration files: ``tigase.conf`` and ``config.tdsl`` in the \"\n\"``/etc`` folder. Here is a brief explanation what all those files are \"\n\"about and in other sections you can learn all the details needed to \"\n\"configure the server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/_Configuration.rst:6\nmsgid \"\"\n\":ref:`config.tdsl<dslConfig>` file is a simple text file with server \"\n\"parameters in form: **key** = **value**. When the XML configuration file \"\n\"is missing the Tigase server reads ``config.tdsl`` file and uses \"\n\"parameters found there as defaults for generation of the XML file. \"\n\"Therefore if you change the ``config.tdsl`` file you normally have to \"\n\"stop the server, remove the XML file and start the server again. All the \"\n\"settings from the ``config.tdsl`` are read and applied to the XML \"\n\"configuration. The properties file is easy to read and very safe to \"\n\"modify. At the moment this is the recommended way change the server \"\n\"configuration.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/_Configuration.rst:8\nmsgid \"\"\n\":ref:`tigase.conf<manualconfig>` is the Tigase server startup \"\n\"configuration. It is actually not used by the server itself. It rather \"\n\"contains operating system settings and environment parameters to \"\n\"correctly run the `Java Virtual Machine \"\n\"<https://www.oracle.com/java/technologies/>`__. It is only useful on the \"\n\"unix-like systems with Bash shell. If you run the server on MS Windows \"\n\"systems ``tigase.bat`` and ``wrapper.conf`` files are used instead. The \"\n\"``tigase.conf`` file is read and loaded by the ``scripts/tigase.sh`` \"\n\"shell script which also scans the operating system environment for Java \"\n\"VM and other tools needed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:4\nmsgid \"DSL file format\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:6\nmsgid \"\"\n\"In previous Tigase XMPP Server releases configuration was stored in \"\n\"properties based configuration file. From Tigase XMPP Server 8.0.0 \"\n\"release it will be required to use new DSL based configuration file \"\n\"format. This file format was inspired by Groovy language syntax and new \"\n\"core feature of Tigase XMPP Server - Tigase Kernel Framework.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:9\nmsgid \"why new format?\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:11\nmsgid \"\"\n\"In properties configuration format each line contained key and value with\"\n\" optional definition of type of stored value:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:17\nmsgid \"\"\n\"where ``c2s/ports`` was name of property, ``[i]`` defined that type of \"\n\"value is array of integers, and ``5222,5223`` was comma separated list of\"\n\" values.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:19\nmsgid \"\"\n\"This format worked but in fact ``c2s/ports`` was not name of property you\"\n\" configured but key which was later split on ``/`` char to parts which \"\n\"defined by names path to property which name was in last part.From that \"\n\"you can see that it was domain based setting of properties.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:21\nmsgid \"\"\n\"Except from this multi-part keys we also used properties starting with \"\n\"``--`` which were global properties accessible for every part of \"\n\"application, i.e.: to add new component and set some properties you \"\n\"needed to write:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:30\nmsgid \"\"\n\"This lead to mistakes like duplicated definition of name and class for \"\n\"same number of component or redefined property value in other place of a \"\n\"configuration file - especially in cases where configuration was big.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:32\nmsgid \"\"\n\"In this configuration structure it was hard to tell where is \"\n\"configuration for particular component or what databases this \"\n\"installation uses. This could be defined all over the file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:34\nmsgid \"\"\n\"In this release we are introducing Tigase Kernel Framework, which allows \"\n\"to configure beans in configuration file and even define usage of new \"\n\"beans loaded from external jars which can modify behavior of Tigase \"\n\"components. This would make configuration file even more complex, \"\n\"difficult and less readable.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:37\nmsgid \"What is DSL?\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:39\nmsgid \"\"\n\"DSL stands for domain-specific language - in this case language created \"\n\"for storage of configuration.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:41\nmsgid \"\"\n\"Now we use domain based configuration which means that our configuration \"\n\"file is not a flat key=value storage but it defines objects, it's \"\n\"properties and assigned values.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:43\nmsgid \"\"\n\"To illustrate it better let's start with a simple example. In properties \"\n\"file in order to configure PubSub component named ``pubsub`` you would \"\n\"use following properties:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:51\nmsgid \"In DSL based configuration this would be replaced by following block\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:60\nmsgid \"\"\n\"in which we define bean with name `pubsub` and set it's class inside \"\n\"``()`` block to ``tigase.pubsub.PubSubComponent``. We also use block \"\n\"between ``{}`` chars to define properties which are related to bean. \"\n\"Which means this properties will be passed only to this instance of \"\n\"Tigase PubSub Component, same as it was before where we needed to add \"\n\"prefix. Entries after ``\\\\#`` are comments, to pass ``#`` you need to \"\n\"wrap whole part containing it in ``''``, ie. ``'test#242'``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:66\nmsgid \"\"\n\"If a string value assigned to a property contains any char from a \"\n\"following list ``=:,[]#+-*/`` it needs to be wrapped in a ``''``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:69\nmsgid \"Why DSL?\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:71\nmsgid \"\"\n\"DSL configuration format provides a number of advantages over the old \"\n\"system of configuration. All configurations for components are related in\"\n\" a single block, so they are not spread out over several different lines.\"\n\" No need for long property names, no longer have to invoke a long string \"\n\"of settings for multiple values. Support is provided for environment \"\n\"variables. No longer need to escape certain characters, making settings \"\n\"far more readable at a glance. Values may be set using basic \"\n\"calculations, such as ``100 * 200 * 2`` rather than ``40000``. Parameter \"\n\"type values are no longer necessary, no more [i], [S], [B] etc.. Comma \"\n\"separated values can now be simplified lists with separate entries being \"\n\"able to be in multiple lines.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:73\nmsgid \"\"\n\"Although the format may seem more complex, looking like a section of java\"\n\" code, the formatting is consistent and can be far more readable. After \"\n\"some experience with DSL format, you'll find it's far more intuitive and \"\n\"user friendly than it may appear. Of course if there's any real \"\n\"confusion, Tigase can automatically convert old style properties files to\"\n\" the DSL format using the following command:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:80\nmsgid \"Setting property\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:82\nmsgid \"\"\n\"To set property you just write property name followed by `=` and value to\"\n\" set. This is always done in context of bean which configuration property\"\n\" you want to set.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:88\nmsgid \"\"\n\"It is also possible to set property in main context by placing property \"\n\"outside of any context. This sets property which value is available to \"\n\"access by any bean.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:92\nmsgid \"Setting global property\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:94\nmsgid \"\"\n\"Like in properties file it is still possible to use property names \"\n\"starting with ``--`` without any context or any other properties at \"\n\"global scope. Format is the same as in case of setting property but they \"\n\"are defined without scope (in global scope). This properties are global \"\n\"and accessible by any bean but also set as system property in JVM.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:97\nmsgid \"Defining bean\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:99\nmsgid \"You can configure bean by using following format:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:107\nmsgid \"\"\n\"where ``beanName`` is name under which you want to configure bean. \"\n\"`beanName` must be wrapped in ``''``, if ``beanName`` contains characters\"\n\" like ``=:,[]#+-*/`` and is recommended, if ``beanName`` is numeric only.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:110\nmsgid \"Inside block between ``(` and `)`` you can define:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:112\nmsgid \"\"\n\"``class`` which will be used as a bean, in example above we set class as \"\n\"``className``. **(default: if you try to configure bean under name which \"\n\"has default class assigned with it in Tigase framework then this assigned\"\n\" class will be used. In other case you need to pass name of class to use \"\n\"as a bean)**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:113\nmsgid \"\"\n\"``active`` (boolean) whether you want the bean to be active or not (beans\"\n\" with ``active`` set to ``false`` are not loaded). **(default: true)**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:114\nmsgid \"\"\n\"``exportable`` (boolean) defines if this bean should be exported and \"\n\"available for use for beans in inner scopes. This is advanced option in \"\n\"most cases it is recommended to omit this field in configuration. \"\n\"**(default: false)**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:116\nmsgid \"\"\n\"Spaces between ``beanName`` and block between ``()`` is optional as well \"\n\"as space between block ``()`` and block ``{}``. It is recommended that \"\n\"properties of bean would be placed in separate lines with indentation and\"\n\" first property will be placed in new line.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:121\nmsgid \"\"\n\"Usage of ``()`` block is very important. When this block is used in \"\n\"configuration it automatically sets ``active`` property of bean \"\n\"definition for bean for which it is used to to `true`. This is done due \"\n\"to fact that default value of ``active`` is ``true``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:123\nmsgid \"\"\n\"If you omit it in configuration, you will set bean configuration but it \"\n\"may remain ``inactive``. In this state bean will not be loaded and as a \"\n\"result will not be used by Tigase XMPP Server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:126\nmsgid \"Configuring bean\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:128\nmsgid \"\"\n\"If you know that bean is defined and you do not want to change it's \"\n\"activity or class then you can just pass properties to configure bean in \"\n\"following way:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:138\nmsgid \"\"\n\"where ``beanName`` is name of bean to configure and `test` is name of \"\n\"property to set to ``true`` in this bean.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:141\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:148\nmsgid \"Format of values\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:143\nmsgid \"\"\n\"In properties based configuration file every property was defined as a \"\n\"string and only by defining expected format it was properly converted to \"\n\"expected value. In DSL it is possible to set values in two ways:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:146\nmsgid \"as an object\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:146\nmsgid \"Using this format you set list as a list and integer is set as an integer.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:151\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:205\nmsgid \"Type\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:151\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:205\nmsgid \"Description\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:153\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:207\nmsgid \"**string**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:153\nmsgid \"Wrap it in ``''``, ie. to set ``test`` as string you use ``'test'``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:155\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:209\nmsgid \"**integer**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:155\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:209\nmsgid \"Just put value, ie. to set ``543`` use ``543``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:157\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:211\nmsgid \"**long**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:157\nmsgid \"\"\n\"Put value and follow it with ``L``, ie. to set ``23645434`` as long use \"\n\"``23645434L``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:159\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:213\nmsgid \"**float**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:159\nmsgid \"\"\n\"Put value and follow it with ``f``, ie. to set ``231.342`` use \"\n\"``231.342f``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:161\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:215\nmsgid \"**boolean**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:161\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:215\nmsgid \"To set value just use ``true`` or ``false``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:163\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:217\nmsgid \"**list**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:163\nmsgid \"\"\n\"Lists can be of many types and to make it simple we decided to use as a \"\n\"comma separated list of values in proper format wrapped in ``[]``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:165\nmsgid \"of strings - ``[ 'alfa', 'beta', 'gamma' ]``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:167\nmsgid \"of integers - ``[ 1, 2, 3, 4]``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:169\nmsgid \"You can write it in multiple lines if you want:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:179\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:219\nmsgid \"**map**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:179\nmsgid \"\"\n\"Maps can be written as a block of properties wrapped in ``{}``. This \"\n\"format of map is the same as used for passing configuration to bean \"\n\"properties. Keys and values can be written in separate lines \"\n\"*(recommended)*:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:192\nmsgid \"or in single line *(separation with spaces is not required)*:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:200\nmsgid \"as a plain string\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:200\nmsgid \"\"\n\"Very similar to properties based configuration, in fact values are passed\"\n\" in same format and later are converted to correct type by checking type \"\n\"expected by bean. *(Not recommended)*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:202\nmsgid \"Types\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:207\nmsgid \"Just put value, ie. to set ``test`` use ``test``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:211\nmsgid \"Put value, ie. to set ``23645434`` as long use ``23645434``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:213\nmsgid \"Put value, ie. to set ``231.342`` use ``231.342``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:217\nmsgid \"\"\n\"List needs to be written as comma separated list of values, ie. \"\n\"``test,abc,efg`` or ``1,2,3``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:219\nmsgid \"Not possible\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:224\nmsgid \"Using values from System Properties and Environment Variables\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:226\nmsgid \"\"\n\"Now it is possible to use values of `system properties \"\n\"<https://docs.oracle.com/javase/tutorial/essential/environment/sysprop.html>`__\"\n\" and `environment variables \"\n\"<https://docs.oracle.com/javase/tutorial/essential/environment/env.html>`__\"\n\" and assign them to bean properties. For this purpose we added functions \"\n\"which can be used in DSL and which will return values of:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:229\nmsgid \"system property\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:229\nmsgid \"``prop('property-name')`` or ``prop('property-name','default value')``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:232\nmsgid \"environment variable\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:232\nmsgid \"``env('variable-name')``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:234\nmsgid \"\"\n\"**Example of setting value of system property and environment variable to\"\n\" bean ``user``.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:247\nmsgid \"\"\n\"For properties which accepts lists it is not allowed to set value using \"\n\"variable/property with comma separated values like ``value1,value2`` \"\n\"wrapped in ``[]``, ie. ``property = [ env('some-variable') ]``. It needs \"\n\"to be set in following way ``property = env('some-variable')``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:250\nmsgid \"Computed values\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:252\nmsgid \"\"\n\"With DSL configuration format we introduce support for computable values \"\n\"for properties. It is now possible to set value which is result of a \"\n\"computation, ie. concatenation of a strings or very simple mathematical \"\n\"expression. We currently support only following mathematical operations:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:254\nmsgid \"add\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:256\nmsgid \"subtract\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:258\nmsgid \"multiply\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:260\nmsgid \"divide\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:262\nmsgid \"\"\n\"**Example of setting environment variable related path and computed \"\n\"timeout.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:278\nmsgid \"\"\n\"For properties which accepts lists it is not allowed to set value using \"\n\"computed values with comma separated values like ``value1,value2`` \"\n\"wrapped in ``[]``, ie. ``property = [ env('some-variable') + ',other-\"\n\"value' ]``. It needs to be set in following way ``property = env('some-\"\n\"variable') + ',other-value'``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:283\nmsgid \"Period / Duration values\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:285\nmsgid \"\"\n\"Some configuration options allow control of execution of tasks with \"\n\"particular period or within certain duration. DSL file format accepts \"\n\"strings denoting particular amount of time, which follows Java’s native \"\n\"structures (see: `Period \"\n\"<https://docs.oracle.com/javase/8/docs/api/java/time/Period.html#parse-\"\n\"java.lang.CharSequence->`__ and `Duration \"\n\"<https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html#parse-\"\n\"java.lang.CharSequence->`__ for detailed explanation).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:287\nmsgid \"\"\n\"``Duration`` formats accepted are based on the ISO-8601 duration format \"\n\"``PnDTnHnMn.nS`` with days considered to be exactly 24 hours, for \"\n\"example:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:289\nmsgid \"``PT20.345S`` - 20.345 seconds\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:291\nmsgid \"``PT15M`` - 15 minutes (where a minute is 60 seconds)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:293\nmsgid \"``PT10H`` - 10 hours (where an hour is 3600 seconds)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:295\nmsgid \"``P2D`` - 2 days (where a day is 24 hours or 86400 seconds)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:297\nmsgid \"``P2DT3H4M`` - 2 days, 3 hours and 4 minutes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:299\nmsgid \"\"\n\"``Period`` format is based on the ISO-8601 period formats PnYnMnD and \"\n\"PnW, for example, the following are valid inputs:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:301\nmsgid \"``P2Y`` - 2 years\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:303\nmsgid \"``P3M`` - 3 months\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:305\nmsgid \"``P4W`` - 4 weeks\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:307\nmsgid \"``P5D`` - 5 days\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:309\nmsgid \"``P1Y2M3D`` - 1 year, 2 months, 3 days\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:311\nmsgid \"``P1Y2M3W4D`` - 1 year, 2 months, 3 weeks, 4 days\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:315\nmsgid \"Example configuration file in DSL\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:393\nmsgid \"Default configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:395\nmsgid \"\"\n\"Tigase XMPP Server is packaged with a basic ``config.tdsl`` file that \"\n\"tells the server to start up in setup mode.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:408\nmsgid \"\"\n\"This tells Tigase to operate in a setup mode, and tells the http \"\n\"component to allow login with the username and password admin/tigase. \"\n\"With this you can enter the setup process that is covered in `this \"\n\"section <#webinstall>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:410\nmsgid \"\"\n\"There are other options for config-type: ``default``, ``session-\"\n\"manager``, ``connection-managers``, and ``component``. For more \"\n\"information, visit `Config Type <#configType>`__ property description.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:4\nmsgid \"Startup File for tigase.sh - tigase.conf\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:6\nmsgid \"\"\n\"Property file names for ``tigase.sh`` startup script is a second \"\n\"parameter for the startup script. It can be skipped if environmental \"\n\"variables are set in different location or in different way.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:8\nmsgid \"\"\n\"Config file for startup script simply sets number of environment \"\n\"variables with the location of required components. Possible variables to\"\n\" set in this file are:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:10\nmsgid \"\"\n\"``JAVA_HOME`` - location of Java installation home directory. **Must be \"\n\"set**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:12\nmsgid \"\"\n\"``TIGASE_HOME`` - location of Tigase installation home directory. *By \"\n\"default script try to find this location by searching directories from \"\n\"the location where the script has been run.*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:14\nmsgid \"\"\n\"``TIGASE_CONSOLE_LOG`` - file to which all console messages will be \"\n\"redirected if server is run in background. By default it will be: \"\n\"``TIGASE_HOME/logs/tigase-console.log``. **If this file/directory is not \"\n\"writable by Tigase process all console messages will be redirected to \"\n\"/dev/null**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:16\nmsgid \"\"\n\"``TIGASE_PID`` location of the file with server PID number. By default it\"\n\" will be ``TIGASE_HOME/logs/tigase.pid``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:18\nmsgid \"\"\n\"``JAVA_OPTIONS`` - options for JVM like size of RAM allocated for the \"\n\"JVM, properties and so on.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:20\nmsgid \"\"\n\"``TIGASE_OPTIONS`` - (optional) additional options for Tigase server \"\n\"program. You can tweak initial parameters for your environment here. If \"\n\"you want to specify custom location of your configuration file you should\"\n\" use ``--config-file <path/to/config.tdsl>`` configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:22\nmsgid \"Sample file to run **Tigase** with **PostgreSQL** database may look like:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:33\nmsgid \"\"\n\"Please note encoding settings. JVM by default uses encoding set in \"\n\"operating system environment. XMPP protocol, however uses ``UTF-8`` for \"\n\"all data processing. So the ENC settings enforces ``UTF-8`` encoding for \"\n\"all operations.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:35\nmsgid \"\"\n\"Another significant setting is \\\\\\\\'**CLASSPATH**'. It is intentionally \"\n\"set to an empty string. The **tigase.sh** startup script builds the \"\n\"**CLASSPATH** on it’s own from files found in **jars/** and **libs/** \"\n\"directories. It is advised to set the **CLASSPATH** to the empty string \"\n\"because the Tigase server scans all available classes to find all \"\n\"components and plugins implementation. If the **CLASSPATH** contains lots\"\n\" of libraries which are not used anyway it can cause a long startup time \"\n\"and high system loads.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:4\nmsgid \"Linux Settings for High Load Systems\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:6\nmsgid \"\"\n\"There are a few basic settings you have to adjust for high load systems \"\n\"to make sure the server has enough resources to handle a big number of \"\n\"network connections.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:8\nmsgid \"\"\n\"The main parameter is a maximum number of opened files allowed for the \"\n\"process to keep at the same time. Each network connection uses a file \"\n\"handler, therefore if the limit is too low you can quickly run out of \"\n\"handlers and the server can not accept any more connections.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:10\nmsgid \"\"\n\"This limit is set on 2 levels - on the kernel level (``fs.file-max``) and\"\n\" on the system level (``nofile``).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:12\nmsgid \"\"\n\"Another kernel property which can be important in certain configurations \"\n\"(like transports installations or when you use proxy for Bosh \"\n\"connections) is: ``net.ipv4.ip_local_port_range``. This parameter can be \"\n\"set the same way as the ``fs.file-max`` property.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:15\nmsgid \"fs.file-max\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:17\nmsgid \"\"\n\"The ``fs.file-max`` kernel property is set via sysctl command. You can \"\n\"see current settings by executing the command:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:24\nmsgid \"\"\n\"If you plan to run high load service with large number of server \"\n\"connections, then this parameter should be at least as twice big as the \"\n\"number of network connections you expect to support. You can change this \"\n\"setting by executing the command:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:32\nmsgid \"net.ipv4.ip_local_port_range\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:34\nmsgid \"You can see current settings by executing the command:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:41\nmsgid \"You can change this setting by executing the command:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:49\nmsgid \"TCP_keepalive\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:51\nmsgid \"\"\n\"According to `Using TCP keepalive to Detect Network Errors \"\n\"<http://www.gnugk.org/keepalive.html>`__ and `TCP Keepalive HOWTO \"\n\"<https://tldp.org/HOWTO/TCP-Keepalive-HOWTO/usingkeepalive.html>`__ some \"\n\"keepalive settings should be changed to improve reliability - it will \"\n\"enable keep alive functionality (checking if the connection is \"\n\"established and valid) and, by decreasing times and interval - will make \"\n\"detection of broken connections faster.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:66\nmsgid \"/etc/sysctl.conf\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:68\nmsgid \"\"\n\"The above commands let the system remember new settings until the next \"\n\"system restart. If you want to make the change permanent you have to edit\"\n\" the file: ``/etc/sysctl.conf`` and add the property at the end of the \"\n\"file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:79\nmsgid \"It will be automatically loaded next time you start the server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:81\nmsgid \"Command:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:87\nmsgid \"\"\n\"Causes the ``/etc/systcl.conf`` to be reloaded which is useful when you \"\n\"have added more parameters to the file and don’t want to restart the \"\n\"server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:90\nmsgid \"nofile\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:92\nmsgid \"\"\n\"This is the property used by the system limits. For example running the \"\n\"command ``ulimit -a`` shows you all limits set for the current user:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:112\nmsgid \"\"\n\"To make it even more interesting and more complex, there are 2 types of \"\n\"system limits: **soft limit** which can be temporarily exceeded by the \"\n\"user and **hard limit** which can not be exceeded. To see your **hard \"\n\"limit** execute command:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:132\nmsgid \"\"\n\"The hard limits are usually bigger then the soft limits or sometimes the \"\n\"same.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:134\nmsgid \"\"\n\"For us the most important parameter is: **open files**. You can change \"\n\"the property in file: ``/etc/security/limits.conf``. You have to append 2\"\n\" following lines to the end of the file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:141\nmsgid \"\"\n\"Where the ``jabber`` is the user name of the account running you IM \"\n\"service. You can also set the limits for all users on the machine in a \"\n\"following way:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:148\nmsgid \"\"\n\"For those changes to make an effect you have to logout from the modified \"\n\"account and login again. New limits should be applied.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:152\nmsgid \"su and init script\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:154\nmsgid \"\"\n\"If one intends to use init scripts for startup purposes (or simply wants \"\n\"to be able to start the server utilizing su command) it’s necessary to \"\n\"adjust PAM configuration by modifying /etc/pam.d/su file and uncomment \"\n\"following line:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:160\nmsgid \"Afterwards the init scripts will respect configured limits.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:2\nmsgid \"JVM settings and recommendations\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:4\nmsgid \"\"\n\"Tigase configuration file ``tigase.conf`` (described in more detail in \"\n\"`Startup File for tigase.sh-tigase.conf <#manualconfig>`__) mentioned a \"\n\"couple of environmental variables which are related to the operation of \"\n\"the JVM. In this guide we would like to expound on those configuration \"\n\"options and provide hints for the optimal settings.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:6\nmsgid \"Settings included in the ``etc/tigase.conf`` are as follows:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:18\nmsgid \"\"\n\"And while this file utilizes bash variables, JVM configuration options \"\n\"can be used in the same manner on all operating systems.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:20\nmsgid \"\"\n\"The guide will consists of two main parts - memory settings and Garbage \"\n\"Collector tweaks descriptions and hints.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:22\nmsgid \"We recommend using ``-server`` JVM parameter in all cases.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:25\nmsgid \"Heap Sizing\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:27\nmsgid \"\"\n\"For the non-production deployments (development or stating environments) \"\n\"we recommend using default memory settings of the JVM (which depends on \"\n\"the underlaying operating system), which result i automatic memory \"\n\"allocation and, by the rule of thumb - are the safest in such \"\n\"environments.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:29\n#, python-format\nmsgid \"\"\n\"For the production environments we recommend a fixed size HEAP - both \"\n\"initial and maximum size, which can be set with (respectively)``-Xms`` \"\n\"and ``-Xmx`` JVM flags - ideally to the same value (which should be \"\n\"roughly 95% of the available memory, if Tigase will be the only service \"\n\"on the machine) to avoid allocation and deallocation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:31\nmsgid \"\"\n\"For convenience it’s possible to uncomment line with \"\n\"``PRODUCTION_HEAP_SETTINGS`` and adjust parameters accordingly.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:34\nmsgid \"Memory consideration - total usage\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:36\nmsgid \"\"\n\"The HEAP size is not the only thing that affects JVM memory usage. When \"\n\"trying to size accordingly for your usage and machine specification you \"\n\"have to consider other factors that count towards total: loaded classes, \"\n\"threads' stack, JIT code cache, garbage collector and others. In \"\n\"principle consider following equation:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:48\nmsgid \"In case of Tigase XMPP Server, apart from heap we limit remaining factors:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:50\nmsgid \"direct memory to **128** MB\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:52\nmsgid \"loaded classes to **128** MB\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:54\nmsgid \"\"\n\"single thread’s stack size to **228** KB (number of threads depends on \"\n\"number of CPU cores and may vary from 500 to couple of thousands)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:56\nmsgid \"\"\n\"In principle, in addition to HEAP’s maximum size defined by ``-Xmx`` you \"\n\"should add roughly **512** MB\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:58\nmsgid \"\"\n\"If you are interested in detailed tracking of memory take a look at \"\n\"[Memory footprint of the JVM](\\\\ https://spring.io/blog/2019/03/11\"\n\"/memory-footprint-of-the-jvm/), [Native Memory Tracking in JVM](\\\\ \"\n\"https://www.baeldung.com/native-memory-tracking-in-jvm) or [Why does my \"\n\"Java process consume more memory than Xmx?](\\\\ https://plumbr.io/blog\"\n\"/memory-leaks/why-does-my-java-process-consume-more-memory-than-xmx)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:60\nmsgid \"\"\n\"To facilitate getting information about complete memory usage we include \"\n\"this information in Tigase statistics, but it requires explicitly \"\n\"enabling it:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:62\nmsgid \"uncomment ``JVM_MEMORY`` line in ``etc/tigase.conf``:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:68\nmsgid \"\"\n\"enable ``detailed-memory-statistics`` in ``message-router`` bean in \"\n\"``etc/config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:78\nmsgid \"GC settings\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:80\nmsgid \"\"\n\"Let’s start with stating that there is no \\\"one to rule them all\\\" - each\"\n\" deployment and use-case is different, however we will try to give a \"\n\"couple of pointers and recommendations proceed with short introduction to\"\n\" GC itself.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:82\nmsgid \"\"\n\"XMPP is quite specific in terms of memory allocation - short-lived \"\n\"objects (various types of stanzas) usually exceed number of long-lived \"\n\"objects (user connections and related data). This is important bit of \"\n\"information in the context of how usually JVM HEAP is organized and how \"\n\"Garbage Collector works. On the most basic level Heap is separated into \"\n\"couple of regions:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:85\nmsgid \"Generations\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:87\nmsgid \"**Young Generation**, which is further divided in to:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:89\nmsgid \"\"\n\"**Eden** - the region when the objects are usually allocated when they \"\n\"are created;\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:91\nmsgid \"\"\n\"**Survivor Spaces** - (*to* and *from* - one of which is always empty) - \"\n\"responsible for storing all live object remaining after collecting \"\n\"**Young Generation** (process is repeated several times until objects are\"\n\" finally considered *old enough*);\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:93\nmsgid \"\"\n\"**Old Generation** - (*Tenured Space*) - responsible for live objects \"\n\"remaining after running GC on **Survivor Spaces** - those would be *long-\"\n\"lived* objects (usually user connections and associated data);\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:96\nmsgid \"Minor, Major and Full GC - optimizing\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:98\nmsgid \"General thinking suggests that:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:100\nmsgid \"**Minor GC** cleans Young generation;\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:102\nmsgid \"**Major GC** cleans Tenured space;\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:104\nmsgid \"**Full GC** cleans all heap.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:106\nmsgid \"\"\n\"However, while we can certainly state that Minor GC cleans Young \"\n\"generation it’s a bit more difficult to differentiate Major and Full GC, \"\n\"especially considering that Major GC can be quite often triggered by \"\n\"Minor GC and some garbage collectors can perform cleaning concurrently. \"\n\"Instead of focusing of distinguishing phases one should pay closer \"\n\"attention to actual operations of Garbage Collector itself - uncommenting\"\n\" the line ``GC_DEBUG=\\\" -XX:+PrintTenuringDistribution \"\n\"-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps \"\n\"-Xloggc:logs/jvm.log -verbose:gc \\\"`` in ``etc/tigase.conf`` (or adding \"\n\"same properties to the java commandline) and subsequently analyzing the \"\n\"results should prove more helpful. In addition monitoring GC operation \"\n\"using for example VisualVM (with VisualGC plugin) will also be helpful.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:109\nmsgid \"Settings for XMPP\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:111\nmsgid \"\"\n\"Ideally we should limit both number of GC pauses as well as their \"\n\"duration. After running rather tests following conclusions were made:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:113\nmsgid \"\"\n\"Garbage Collection is the faster the more dead objects occupies given \"\n\"space, therefore on high-traffic installation it’s better to have rather \"\n\"large YoungGen resulting in lower promotion of the objects to the OldGen;\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:115\nmsgid \"\"\n\"with JVM8 default sizing of Young / Old generation changed, even tho \"\n\"NewRatio is still defaulting to “2” - setting it explicitly to \\\"2\\\" \"\n\"brought back previous sizing;\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:117\nmsgid \"\"\n\"Concurrent Mark and Sweep (CMS) enabled (applies to Tenured space only) \"\n\"with explicit configuration of NewRatio set to default value of 2 (i.e. \"\n\"``-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:NewRatio=2``) in general \"\n\"behaves best;\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:119\nmsgid \"\"\n\"For small installations (few core CPU, less memory) with low traffic \"\n\"default Parallel collector may be a better solution;\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:121\nmsgid \"\"\n\"Using Heap size adjusted to the actual usage is better as the larger the \"\n\"heap the larger are spaces over which collection needs to be performed \"\n\"thus resulting in longer pauses; in case of huge heaps G1 collector may \"\n\"be better solution to avoid longer pauses;\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:123\nmsgid \"\"\n\"Considering all of the above using following options should be a good \"\n\"starting point toward further optimizing of Garbage Collection:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:125\nmsgid \"\"\n\"``GC=\\\"-XX:+UseBiasedLocking -XX:+UseConcMarkSweepGC -XX:+UseParNewGC \"\n\"-XX:+CMSIncrementalMode -XX:-ReduceInitialCardMarks \"\n\"-XX:CMSInitiatingOccupancyFraction=70 \"\n\"-XX:+UseCMSInitiatingOccupancyOnly\\\"``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:128\nmsgid \"GC settings worth considering\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:130\nmsgid \"\"\n\"In addition to the general recommendation to use CMS collector, following\"\n\" options (or changes to the options) may be worth considering:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:132\nmsgid \"\"\n\"``-XX:NewRatio=2`` - defines the ratio between the young and tenured \"\n\"generation is 1:2. In other words, the combined size of the eden and \"\n\"survivor spaces will be one-third of the total heap size. The parameters \"\n\"NewSize and MaxNewSize bound the young generation size from below and \"\n\"above. Setting these to the same value fixes the young generation, just \"\n\"as setting -Xms and -Xmx to the same value fixes the total heap size.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:134\nmsgid \"\"\n\"``-XX:CMSInitiatingOccupancyFraction=percent`` - sets the percentage of \"\n\"the old generation occupancy (0 to 100) at which to start a CMS \"\n\"collection cycle.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:136\nmsgid \"\"\n\"``-XX:+UseCMSInitiatingOccupancyOnly`` - instructs the JVM not to base \"\n\"its decision when to start a CMS cycle on run time statistics but instead\"\n\" it uses the value of CMSInitiatingOccupancyFraction for every CMS cycle.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:138\nmsgid \"\"\n\"``-XX:ParallelGCThreads=x`` - sets the number of threads used for \"\n\"parallel garbage collection in the young and old generations. The default\"\n\" value depends on the number of CPUs available to the JVM. If the Tigase \"\n\"JMV is the only one running on the installation default value is \"\n\"recommended.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:140\nmsgid \"\"\n\"``-XX:ConcGCThreads=x`` - sets the number of threads used for concurrent \"\n\"GC. The default value depends on the number of CPUs available to the JVM.\"\n\" If the Tigase JMV is the only one running on the installation default \"\n\"value is recommended.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:142\nmsgid \"\"\n\"``-XX:+UseBiasedLocking`` and ``-XX:+DoEscapeAnalysis`` - designed to \"\n\"eliminate locking overhead, however their effect on performance is \"\n\"unpredictable therefore testing is required; reduced locking should \"\n\"improve concurrency and, on current multi-core hardware, improve \"\n\"throughput.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:144\nmsgid \"\"\n\"``-XX:+OptimizeStringConcat`` - enables the optimization of String \"\n\"concatenation operations. This option is enabled by default.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:146\nmsgid \"\"\n\"``-XX:+UseNUMA`` - enables performance optimization of an application on \"\n\"a machine with nonuniform memory architecture (NUMA - most modern \"\n\"computers are based on NUMA architecture) by increasing the application’s\"\n\" use of lower latency memory. By default, this option is disabled and no \"\n\"optimization for NUMA is made. The option is only available when the \"\n\"parallel garbage collector is used (-XX:+UseParallelGC).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:148\nmsgid \"\"\n\"``-XX:-UseCompressedOops`` — disables the use of compressed pointers. By \"\n\"default, this option is enabled, and compressed pointers are used when \"\n\"Java heap sizes are less than 32 GB. When this option is enabled, object \"\n\"references are represented as 32-bit offsets instead of 64-bit pointers, \"\n\"which typically increases performance when running the application with \"\n\"Java heap sizes less than 32 GB. This option works only for 64-bit JVMs.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:152\nmsgid \"What to use with Machine x, y, z?\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:155\nmsgid \"Server class machine (non-VM), > 16GB, >= 8 core CPU\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:157\nmsgid \"\"\n\"For such setup enabling CMS garbage collector is recommended. Depending \"\n\"on the traffic usage and particular use-case adjusting NewRatio may be \"\n\"needed. Adjusting Xms and Xms sizes for actual available memory is needed\"\n\" (or better yet, for the actual traffic!). Following should be used:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:169\nmsgid \"\"\n\"For installation with lot of available memory and intention to utilize it\"\n\" all, using G1GC collector may be a better idea :\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:183\nmsgid \"VM machine, 8GB of RAM, 4 core CPU equivalent\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:185\nmsgid \"\"\n\"For such setup enabling CMS garbage collector is also recommended. \"\n\"Depending on the traffic usage and particular use-case adjusting NewRatio\"\n\" may be needed (and configuring NewRatio is a must!). Adjusting Xms and \"\n\"Xms sizes for actual available memory is needed (or better yet, for the \"\n\"actual traffic!). Following should be used:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:199\nmsgid \"VM machine with 4GB or less of RAM, and less than 4 core CPU equivalent\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:201\nmsgid \"\"\n\"Small installations with limited resources could operate better with \"\n\"default (for JVM versions up to 8, which is the most current at the \"\n\"moment of the writing). Again - depending on the traffic usage and \"\n\"particular use-case adjusting NewRatio may be needed. Adjusting Xms and \"\n\"Xms sizes for actual available memory is recommended (or better yet, for \"\n\"the actual traffic!). Following should be used (i.e. ``GC`` line should \"\n\"be commented so the defaults will be used):\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:215\nmsgid \"Additional resources\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:217\nmsgid \"\"\n\"`Sizing the Generations \"\n\"<https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/sizing.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:219\nmsgid \"\"\n\"`About Java, parallel garbage collection and processor sets \"\n\"<http://www.c0t0d0s0.org/archives/6617-About-Java,-parallel-garbage-\"\n\"collection-and-processor-sets.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:221\nmsgid \"\"\n\"`GC Threads <http://hiroshiyamauchi.blogspot.cl/2009/12/gc-\"\n\"threads.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:223\nmsgid \"`GCViewer readme <https://github.com/chewiebug/GCViewer#readme>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:225\nmsgid \"\"\n\"`Java HotSpot™ Virtual Machine Performance Enhancements \"\n\"<http://docs.oracle.com/javase/7/docs/technotes/guides/vm/performance-\"\n\"enhancements-7.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:227\nmsgid \"\"\n\"`Java Garbage Collection handbook <https://plumbr.eu/java-garbage-\"\n\"collection-handbook>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:229\nmsgid \"Useful JVM Flags\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:231\nmsgid \"\"\n\"`Part 1 - JVM Types and Compiler Modes \"\n\"<https://blog.codecentric.de/en/2012/07/useful-jvm-flags-part-1-jvm-\"\n\"types-and-compiler-modes/>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:233\nmsgid \"\"\n\"`Part 2 - Flag Categories and JIT Compiler Diagnostics) \"\n\"<https://blog.codecentric.de/en/2012/07/useful-jvm-flags-part-2-flag-\"\n\"categories-and-jit-compiler-diagnostics/>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:235\nmsgid \"\"\n\"`Part 3 - Printing all XX Flags and their Values \"\n\"<https://blog.codecentric.de/en/2012/07/useful-jvm-flags-part-3-printing-\"\n\"all-xx-flags-and-their-values/>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:237\nmsgid \"\"\n\"`Part 4 - Heap Tuning <https://blog.codecentric.de/en/2012/07/useful-jvm-\"\n\"flags-part-4-heap-tuning/>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:239\nmsgid \"\"\n\"`Part 5 - Young Generation Garbage Collection \"\n\"<https://blog.codecentric.de/en/2012/08/useful-jvm-flags-part-5-young-\"\n\"generation-garbage-collection/>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:241\nmsgid \"\"\n\"`Part 6 - Throughput Collector <https://blog.codecentric.de/en/2013/01\"\n\"/useful-jvm-flags-part-6-throughput-collector/>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:243\nmsgid \"\"\n\"`Part 7 - CMS Collector <https://blog.codecentric.de/en/2013/10/useful-\"\n\"jvm-flags-part-7-cms-collector/>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:245\nmsgid \"\"\n\"`Part 8 - GC Logging <https://blog.codecentric.de/en/2014/01/useful-jvm-\"\n\"flags-part-8-gc-logging/>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:2\nmsgid \"Session Manager\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:4\nmsgid \"\"\n\"Tigase Session Manager is where most of Tigase basic options can be \"\n\"configured, and where many operations are controlled from. Changes to \"\n\"session manager can effect operations throughout an entire XMPP \"\n\"installation, so care must be made when changing settings here.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:9\nmsgid \"Mobile Optimizations\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:11\nmsgid \"\"\n\"By default, Tigase employs XEP-0352 Client State Indication which allows \"\n\"for a more streamlined mobile experiencing by allowing the XMPP server to\"\n\" suppress or reduce the number of updates sent to a client thereby \"\n\"reducing the number of stanzas sent to a mobile client that is inactive. \"\n\"This employment is contained within the processor \"\n\"``ClientStateIndication`` and is independent from the MobileV1, MobileV2,\"\n\" MobileV3 settings.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:13\nmsgid \"\"\n\"However, this can be fine tuned by using mobile plugins from Tigase which\"\n\" can be used at the same time by adding the following line to the \"\n\"``config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:24\nmsgid \"Logic Options are:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:27\nmsgid \"MobileV1\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:29\nmsgid \"Keeps all presence stanzas in queue until client is active.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:37\nmsgid \"MobileV2\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:39\nmsgid \"\"\n\"This setting delays delivery of presences while client is in inactive \"\n\"state, but only keeps the last presence for each full jid. **This is the \"\n\"default setting for CSI logic**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:47\nmsgid \"MobileV3\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:49\nmsgid \"\"\n\"Keeps the same presence logic as MobileV2, but also queues Message \"\n\"Carbons. **Currently not supported by CSI processor, will cause issues**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:57\nmsgid \"Disabling CSI\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:59\nmsgid \"\"\n\"If you wish to not use the ``ClientStateIndication`` processor, set the \"\n\"following in your ``config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:69\nmsgid \"A note about Mobile Plugins\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:71\nmsgid \"\"\n\"Previously, you could enable Mobile optimization logic using by enabling \"\n\"``Mobile_V1 (){}`` bean to Session Manager: ``sess-man () {}`` bean.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:73\nmsgid \"\"\n\"If you have used these in the past, it is recommended you change your \"\n\"system to use the CSI processor with the appropriate mobile processing \"\n\"logic.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:75\nmsgid \"\"\n\"If you require v3 logic, or do not wish to use CSI, be sure to disable it\"\n\" using the above option.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:79\nmsgid \"threads-pool\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:81\nmsgid \"\"\n\"The ``threadsNo`` property allows you to fine-tune the SM plugin’s \"\n\"(processors) thread pool. With the default settings every plugin gets his\"\n\" own thread pool. This guarantees the best performance and optimal \"\n\"resource usage. The downside of this setting is that packets can arrive \"\n\"out of order if they are processed within different thread pools.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:83\nmsgid \"\"\n\"We can even fine tune this packet processing. Let’s say you want most of \"\n\"the plugins to be executed within a single thread pool to preserve packet\"\n\" ordering for them, but for some selected plugins that should execute \"\n\"within separate thread pools to improve performance. Let’s say, \"\n\"authentication packets and user registration can be actually executed in \"\n\"a separate thread pools as we do not worry about an order for them. Users\"\n\" cannot send or receive anything else before they authenticates anyway. \"\n\"The solution is to specify a number of threads for the selected plugin. \"\n\"For example, setting a common thread pool for all plugins but \"\n\"registration and authentication can be done with following configuration:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:96\nmsgid \"\"\n\"This replaces the old ``--sm-threads-pool`` property, as well as \"\n\"specifying thread pools in ``--sm-plugins``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:100\nmsgid \"Thread Pool factor\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:102\nmsgid \"\"\n\"Session manager can control the number of available thread pools for each\"\n\" processor. By adding the following line to the ``config.tdsl`` file, the\"\n\" global thread pool can be increased by a specified factor:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:110\nmsgid \"In this case, the global thread pools is increased by a factor or 3.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:114\nmsgid \"Strategy\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:116\nmsgid \"\"\n\"The ``Strategy`` property allows users to specify Clustering Strategy \"\n\"class which should be used for handling clustering environment; by \"\n\"default ``SMNonCachingAllNodes`` is used.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:118\nmsgid \"\"\n\"Any class implementing ``tigase.cluster.strategy.ClusteringStrategyIfc`` \"\n\"interface may be used for this setting.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:120\nmsgid \"Example:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:128\nmsgid \"This replaces the old ``--sm-cluster-strategy-class`` setting from v7.1.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:4\nmsgid \"Virtual Hosts in Tigase Server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:6\nmsgid \"\"\n\"Tigase server supports multiple virtual hosts in a single server \"\n\"installation. Virtual hosts can be added or removed, enabled or disabled \"\n\"during runtime without restarting the service or disrupting normal \"\n\"operation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:8\nmsgid \"\"\n\"This document describes how virtual hosts work in Tigase server and how \"\n\"to get the most out of this feature in your installation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:10\nmsgid \"\"\n\"The :ref:`'default-virtual-host'<virtHosts>` property allows to define \"\n\"name of the single vhost domain which will be considered a default vhost \"\n\"domain for this installation. It allows you just to configure the domain \"\n\"name. Any additional configuration needs to be configured using ad-hoc \"\n\"commands.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:12\nmsgid \"\"\n\"Virtual hosts should be managed using ad-hoc commands or admin ui, visit \"\n\":ref:`Add and Manage Domains<addManageDomain>` for description of vhosts \"\n\"management process or visit :ref:`Specification for ad-hoc Commands Used \"\n\"to Manage Virtual Domains<adhocCommands>` for more information about ad-\"\n\"hoc commands.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:14\nmsgid \"\"\n\"If you have components that may not be able to handle multiple vhosts or \"\n\"cluster mode, we have developed a virtual component solution as well, \"\n\"details in the :ref:`Virtual Components for the Tigase \"\n\"Cluster<virtualComponents>` section.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:16\nmsgid \"\"\n\"You may also want to reference the Vhosts API for additional information:\"\n\" - :ref:`API Description for Virtual Domains Management in Tigase \"\n\"Server<addManageDomain>`.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:24\nmsgid \"Default VHost configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:26\nmsgid \"\"\n\"It’s possible to specify initial default configuration for all Virtual \"\n\"Host in TDSL configuration file (i.e. ``etc/config.tdsl``) for selected \"\n\"parameters. To do so you should specify each configuration option within \"\n\"``defaults`` bean belonging to ``vhost-man`` bean:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:44\nmsgid \"\"\n\"After initial definition of default configuration or after first startup \"\n\"of Tigase XMPP Server it is possible to configure Virtual Host defaults \"\n\"using ad-hoc commands by modifying values for ``default`` using ad-hoc as\"\n\" described in :ref:`Specification for ad-hoc Commands Used to Manage \"\n\"Virtual Domains<adhocCommands>`.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:46\nmsgid \"\"\n\"Alternatively, you may edit default Virtual Host configuration \"\n\"(configuration for domain ``default``) using Admin UI which by default is\"\n\" available at http://localhost:8080/admin/.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:51\nmsgid \"Specification for ad-hoc Commands Used to Manage Virtual Domains\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:53\nmsgid \"\"\n\"There are 3 ad-hoc commands for virtual domains management in the Tigase \"\n\"server:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:55\nmsgid \"\"\n\"``VHOSTS_RELOAD`` used to reload virtual domains list from the repository\"\n\" (database).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:57\nmsgid \"\"\n\"``VHOSTS_UPDATE`` used to add a new virtual domain or update information \"\n\"for existing one.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:59\nmsgid \"\"\n\"``VHOSTS_REMOVE`` used to remove an existing virtual host from the \"\n\"running server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:61\nmsgid \"\"\n\"Syntax of the commands follows the specification described in `XEP-0050 \"\n\"<http://xmpp.org/extensions/xep-0050.html>`__. Extra information required\"\n\" to complete the command is carried as data forms described in `XEP-0004 \"\n\"<http://xmpp.org/extensions/xep-0004.html>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:63\nmsgid \"\"\n\"All commands are accepted by the server only when send by the \"\n\"installation administrator. If the command is sent from any other account\"\n\" ``<not-authorized />`` error is returned. To grant administrator rights \"\n\"to an account you have to set ``admins`` property in the ``config.tdsl`` \"\n\"configuration file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:65\nmsgid \"\"\n\"Commands are sent to the 'vhost-man' server component and the 'to' \"\n\"attribute of the stanza must contain a full JID of the VHostManager on \"\n\"the server. The full **JID** consists of the component name: 'vhost-man' \"\n\"and the local domain, that is domain which is already on the list of \"\n\"virtual domains and is active. Assuming 'existing.domain.com' one of \"\n\"domains already activated for the server installation the **JID** is: \"\n\"'vhost-man@existing.domain.com'.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:68\nmsgid \"Reloading the Domains List from the Database\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:70\nmsgid \"\"\n\"In order to reload virtual domains from the permanent repository other \"\n\"than configuration file, you have to send ``VHOSTS_RELOAD`` ad-hoc \"\n\"command to the VHostManager on the server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:72\nmsgid \"The reload command request is of the form:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:83\nmsgid \"\"\n\"The server sends a response upon successful completion of the command \"\n\"with current number of virtual domains server by the installation:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:102\nmsgid \"\"\n\"If the command is sent from an account other than admin, the server \"\n\"returns an error:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:121\nmsgid \"\"\n\"The response doesn’t have any special meaning other then end-user \"\n\"information. The client may ignore the response as it is sent after the \"\n\"command has been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:124\nmsgid \"Adding a New Domain or Updating Existing One\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:126\nmsgid \"\"\n\"In order to add a new domain or update existing one you have to send an \"\n\"ad-hoc command ``VHOSTS_UPDATE`` with at least one domain name in the \"\n\"command data form. You can also specify whether the domain is enabled or \"\n\"disabled but this is optional. Future releases may allow for setting \"\n\"additional parameters for the domain: maximum number of user accounts for\"\n\" this domain, anonymous login enabled/disabled for the domain, \"\n\"registration via XMPP enabled/disabled for this domain and some more \"\n\"parameters not specified yet.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:128\nmsgid \"The domain add/update command request is of the form:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:150\nmsgid \"\"\n\"Please note: Character case in the command field variable names does \"\n\"matter.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:152\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:194\nmsgid \"\"\n\"Upon successful completion of the command the server sends a response \"\n\"back to the client with information of the existing number of virtual \"\n\"hosts on the server:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:172\nmsgid \"Removing a Virtual Domain From the Server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:174\nmsgid \"\"\n\"In order to remove a virtual domain you have to send ``VHOSTS_REMOVE`` \"\n\"command to the server with the domain name.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:176\nmsgid \"The domain remove command is sent by the client:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:216\nmsgid \"Virtual Components for the Cluster Mode\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:218\nmsgid \"\"\n\"Let’s assume you have a cluster installation and you want to include a \"\n\"component in your installation which doesn’t support the cluster mode \"\n\"yet. If you put it on all nodes as a separate instances they will work \"\n\"out of sync and overall functionality might be useless. If you put on one\"\n\" node only it will work correctly but it will be visible to users \"\n\"connected to this one node only.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:220\nmsgid \"\"\n\"Ideally you would like to have a mechanism to install it on one node and \"\n\"put some redirections on other nodes to forward all packets for this \"\n\"component to a node where this component is working. Redirection on it’s \"\n\"own is not enough because the component must be visible in service \"\n\"discovery list and must be visible somehow to users connected to all \"\n\"nodes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:222\nmsgid \"\"\n\"This is where the virtual components are handy. They are visible to users\"\n\" as a local normal component, they seem to be a real local component but \"\n\"in fact they just forward all requests/packets to a cluster node where \"\n\"the real component is working.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:224\nmsgid \"\"\n\"Virtual component is a very lightweight ServerComponent implementation in\"\n\" Tigase server. It can pretend to be any kind of component and can \"\n\"redirect all packets to a given address. They can mimic native Tigase \"\n\"components as well as third-party components connected over external \"\n\"component protocol (XEP-0114).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:226\nmsgid \"\"\n\"Configuration is very simple and straightforward, in fact it is very \"\n\"similar to configuration of any Tigase component. You set a real \"\n\"component name as a name of the component and a virtual component class \"\n\"name to load. Let’s say we want to deploy MUC component this way. The MUC\"\n\" component is visible as ``muc.domain.oug`` in the installation. Thus the\"\n\" name of the component is: ``muc``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:232\nmsgid \"\"\n\"This is pretty much all you need to load a virtual component. A few other\"\n\" options are needed to point to correct destination addresses for packets\"\n\" forwarding and to set correct service discovery parameters:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:246\nmsgid \"That’s it.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:2\nmsgid \"Settings for Custom Logging in Tigase\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:4\nmsgid \"\"\n\"Logging can be an important tool to monitor your server’s health and \"\n\"performance. Logging may be controlled and customized on a per-component \"\n\"basis.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:6\nmsgid \"\"\n\"A ``logging`` bean has been implemented to allow more flexible \"\n\"configuration of logging in the Tigase XMPP Server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:9\nmsgid \"Configuring logging\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:12\nmsgid \"In the config file\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:14\nmsgid \"\"\n\"Default logging configuration for your installation is kept in the config\"\n\" file and it may be adjusted there.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:45\nmsgid \"\"\n\"You only need to specify the settings you wish to customize, otherwise \"\n\"they will be left as default.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:47\nmsgid \"\"\n\"``packet-debug-full`` - controls whether log entries should be obfuscated\"\n\" (all CData of all elements will be replaced by ``CData size: <length in \"\n\"bytes of the replaced string>``) or not; default: ``false``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:49\nmsgid \"\"\n\"``rootLevel`` - Defines the root level of logging for all components not \"\n\"otherwise defined. Default is CONFIG\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:51\nmsgid \"\"\n\"``loggers`` - Defines the level of logging for packages running in tigase\"\n\" server. This is similar to the --debug setting, however you must use \"\n\"``tigase.{package}`` format. Default is NONE.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:53\nmsgid \"\"\n\"``handlers`` - Defines the level of logging for File output and Console \"\n\"output.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:55\nmsgid \"\"\n\"``FileHandler`` - is the file output for log files, with the following \"\n\"options:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:57\nmsgid \"``level`` - specifies the level of logs to be written, default is ALL.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:59\nmsgid \"\"\n\"``append`` - whether to append to the log or replace it during restart. \"\n\"Default is true.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:61\nmsgid \"\"\n\"``count`` - number of individual log files to keep at set limit. Default \"\n\"is 5. (default settings will continue appending logs until 5 files at \"\n\"10MB are reached, then the oldest file will be overwritten.)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:63\n#: ../../Tigase_Administration/Configuration/Logging.inc:73\nmsgid \"\"\n\"``formatter`` - specifies the package to format logging output. Default \"\n\"is tigase.util.LogFormatter.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:65\nmsgid \"``limit`` - Byte limit for each log file. Default is 10000000 or 10MB.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:67\nmsgid \"\"\n\"``pattern`` - Directory and filename of the log file with respect to the \"\n\"Tigase installation directory. Default is logs/tigase.log.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:69\nmsgid \"\"\n\"``ConsoleHandler`` - Determines the formatting for Tigase output to \"\n\"console.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:71\nmsgid \"``level`` - specifies the level of logs to be written, default is WARNING.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:77\nmsgid \"Disabling colored output\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:79\nmsgid \"\"\n\"If for some reason you don’t want colored output in the logs you can \"\n\"disable it by setting ``disable_logger_color`` to ``true``. For \"\n\"convenience, you can uncomment in ``etc/tigase.conf`` following line:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:86\nmsgid \"Ad-hoc changes to the logging configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:88\nmsgid \"\"\n\"It is also possible to use ad-hoc command named **Set package logging** \"\n\"with id ``logging-set`` available at ``message-router@domain`` (where \"\n\"domain is your server name) to reconfigure logging level of packets at \"\n\"runtime without requirement of restarting the Tigase XMPP Server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:90\n#: ../../Tigase_Administration/Configuration/Logging.inc:124\nmsgid \"**Note**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:92\nmsgid \"Those changes will be applied to this single cluster node.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:98\nmsgid \"Using Admin UI\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:100\nmsgid \"\"\n\"If your Tigase XMPP Server is running with HTTP server and with Admin UI \"\n\"enabled, then the easiest way to change logging configuration is by using\"\n\" Admin UI. After logging into web interface, open ``Configuration`` \"\n\"section and select ``Set package logging`` command. This will bring to \"\n\"you a form which you need to fill in with following fields:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:102\n#: ../../Tigase_Administration/Configuration/Logging.inc:113\nmsgid \"\"\n\"``Package name`` - should contain Java package or class name for which \"\n\"you wish to change logging level\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:104\n#: ../../Tigase_Administration/Configuration/Logging.inc:115\nmsgid \"\"\n\"``Level`` - select a logging level you wish to apply to entered package \"\n\"name *(``OFF`` means that logging will be disabled)*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:106\nmsgid \"\"\n\"After pressing ``Submit`` your form will be passed to the server for \"\n\"validation and selected changes will be applied.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:109\nmsgid \"Using ad-hoc command\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:111\nmsgid \"\"\n\"If you have access to the XMPP admin account of Tigase XMPP Server and \"\n\"XMPP client which supports ad-hoc command execution, you may connect with\"\n\" your XMPP client to the Tigase XMPP Server and look for adh-hoc commands\"\n\" available at ``message-router@domain`` (where domain is your server \"\n\"name). Within found ad-hoc commands you should find command named ``Set \"\n\"package logging`` or ``logging-set`` (that depends what your XMPP client \"\n\"is showing, id or name of the command) and you should execute it. Tigase \"\n\"XMPP Server will return a form which you need to fill in with following \"\n\"fields:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:117\nmsgid \"\"\n\"After submitting the form, Tigase XMPP Server will validate your request \"\n\"and update logging configuration.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:120\nmsgid \"Using REST API\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:122\nmsgid \"\"\n\"If you have Tigase XMPP Server with REST API enabled, you can use it for \"\n\"configuring logging of Tigase XMPP Server as well.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:126\nmsgid \"\"\n\"As with all HTTP REST API requests you will require a valid API key and \"\n\"in this case a valid admin credentials to authenticate a HTTP request \"\n\"using Basic HTTP Authentication.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:128\nmsgid \"\"\n\"All you need to to is to send a HTTP POST request to ``/rest/adhoc\"\n\"/message-router@domain.com`` (where domain is your server name) with \"\n\"``Contect-Type`` set to ``application/xml`` and a following XML as a \"\n\"payload to set logging level of ``tigase.server`` package to ``ALL``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:151\nmsgid \"Alternate loggers in Tigase - Logback\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:153\nmsgid \"\"\n\"It’s possible to use Logback for logging purposes, which offers certain \"\n\"interesting features (async logging, better control over log rotation, on\"\n\" the fly changing logging configuration)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:155\nmsgid \"\"\n\"Requirements: \\\\* slf4j-api.jar (provided in ``-dist-max`` package) \\\\* \"\n\"jul-to-slf4j.jar (provided in ``-dist-max`` package) \\\\* desired logger \"\n\"libraries (for logback it’s ``logback-classic.jar`` and ``logback-\"\n\"core.jar`` (provided in -dist-max).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:157\nmsgid \"\"\n\"Configuration boils down to adding slf4j bridge handler to the list of \"\n\"build-in Java Logger handlers configuration, which in Tigase translates \"\n\"to adding following line to ``etc/config.tdsl``:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:165\nmsgid \"After that ``etc/logback.xml`` configuration file will be used.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:167\nmsgid \"\"\n\"As stated in [jul-to-slf4j bridge documentation](\\\\ \"\n\"http://www.slf4j.org/legacy.html#jul-to-slf4j) it’s essential to include \"\n\"``LevelChangePropagator`` to eliminate translation overhead for disabled \"\n\"log statements:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:176\nmsgid \"\"\n\"NOTE, that it may be prudent to remove configuration of all old JUL \"\n\"logger by appending following to ``etc/logback.xml`` configuration:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:2\nmsgid \"Tigase Advanced Options\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:4\nmsgid \"\"\n\"This section is designed to include a number of advanced configuration \"\n\"options available within Tigase, but may not have a relevant section yet \"\n\"to house them.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:7\nmsgid \"Using CAPTCHA for in-band registration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:9\nmsgid \"\"\n\"To reduce false or spam registrations to Tigase XMPP Server, there is now\"\n\" the ability to add CAPTCHA forms as a part of the in-band registration. \"\n\"The CAPTCHA will generate a random math equation and ask the user \"\n\"registering a new account to answer it. This may be enabled as a sub-\"\n\"option of enabling registration in config.tdsl:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:23\nmsgid \"\"\n\"3 unsuccessful attempts will result in the captcha being invalidated and \"\n\"a client will receive an error message.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:27\nmsgid \"Enabling Empty Nicknames\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:29\nmsgid \"\"\n\"Tigase can now support users with empty nicknames. This can be enabled by\"\n\" adding the following code in config.tdsl.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:41\nmsgid \"Enable Silent Ignore on Packets Delivered to Unavailable Resources\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:43\nmsgid \"\"\n\"You can now have Tigase ignore packets delivered to unavailable resources\"\n\" to avoid having a packet bounce around and create unnecessary traffic. \"\n\"You may set this globally, within standard message handling only, or \"\n\"within the AMP component using the following settings:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:45\nmsgid \"Globally:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:53\nmsgid \"Message Processing Only:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:63\nmsgid \"AMP Component:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:73\nmsgid \"Mechanism to count errors within Tigase\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:75\nmsgid \"\"\n\"A new processor within statistics has been added to count the number of \"\n\"errors that Tigase returns. This processor, named ``error-counter``, will\"\n\" count all errors returned by Tigase, however by default the number is \"\n\"always zero if it is not enabled. It can be found as an MBean object in \"\n\"JMX under ``ErrorStatistics`` and contains values for packets with \"\n\"``ERROR`` and grouped by type. To enable counting of these errors, you \"\n\"must ensure the processor is included in your ``sess-man`` configuration:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:84\nmsgid \"Including stream errors\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:86\nmsgid \"\"\n\"Stream ``ERROR`` packets are not included in the above counter by default\"\n\" as they are processed separately. To enable this to be added to the \"\n\"counter, the following line must be in your ``config.tdsl`` file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:97\nmsgid \"Stream resumption default & max-timeout\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:99\nmsgid \"\"\n\"``SteamManagementIOProcessor`` now has a setting that can be used to \"\n\"change the maximum timeout time it will wait for reconnection if a client\"\n\" does not send a time to wait. Two settings are now available:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:109\nmsgid \"\"\n\"The above setting in ``config.tdsl`` file will change the default timeout\"\n\" period to 90 seconds.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:119\nmsgid \"\"\n\"This setting will set the maximum time allowed for stream resumption to \"\n\"900 seconds. This can be handy if you expect a number of mobile phones to\"\n\" connect to your server and want to avoid duplicate messages being sent \"\n\"back and forth.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:122\nmsgid \"Automatic subscription approval\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:124\nmsgid \"\"\n\"You may setup a server to automatically approve presence subscriptions or\"\n\" roster authorizations for all users. Say you were hosting bots and \"\n\"wanted to automate the process. This can be done with the following \"\n\"settings:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:137\nmsgid \"\"\n\"Both of these settings are false by default, and you may use them \"\n\"together or separately.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:143\nmsgid \"The following behavior is followed when they are both activated:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:145\nmsgid \"\"\n\"Upon sending a subscription request - Both contacts will each others' \"\n\"subscription and be added to each others' roster. Presence information \"\n\"will immediately be exchanged between both parties.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:147\nmsgid \"\"\n\"Upon sending presence with type either unsubscribe or unsubscribed \"\n\"follows the rules defined in RFC regarding processing of these stanzas \"\n\"(i.e. adjusting subscription type of user/contact), but without \"\n\"forwarding those stanzas to the receiving entity to avoid any \"\n\"notifications to the client. However, a roster push is generated to \"\n\"reflect changes to presence in user roster in a seamless manner.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:149\nmsgid \"\"\n\"Simply adding an item to the roster (i.e. with <iq/> stanza with correct \"\n\"semantics) will also cause an automatic subscription between the user and\"\n\" the contact in a matter explained above.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:152\nmsgid \"Abuse Contacts\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:154\nmsgid \"\"\n\"Tigase has support for `XEP-0128: Service Discovery Extensions \"\n\"<https://xmpp.org/extensions/xep-0128.html>`__ for providing additional \"\n\"information to the server and component discovery information. One of the\"\n\" important usages for this feature is `XEP-0157: Contact Addresses for \"\n\"XMPP Services <https://xmpp.org/extensions/xep-0157.html>`__ which \"\n\"describes usage of this feature for providing contact information to \"\n\"server administrators or abuse response team.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:156\nmsgid \"\"\n\"To set abuse contact details you should set ``disco-extensions`` in \"\n\"property in ``etc/config.tdsl`` file with subproperty ``abuse-addresses``\"\n\" set to your abuse address URI (for email you need to prefix it with \"\n\"``mailto:`` and for XMPP address you need to prefix it with ``xmpp``):\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:165\nmsgid \"Push Notifications\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:167\nmsgid \"\"\n\"Tigase XMPP Server comes with support for `XEP-0357: Push Notifications \"\n\"<https://xmpp.org/extensions/xep-0357.html>`__ allowing user to receive \"\n\"notifications for messages received while his XMPP client is not \"\n\"connected enabled by default.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:170\nmsgid \"Disabling notifications\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:172\nmsgid \"You can disable this feature with following settings:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:180\nmsgid \"Removing body and sender from notifications\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:182\nmsgid \"\"\n\"If you wish Tigase XMPP Server not to forward body of the message or \"\n\"sender details in the push notification you can disable that with \"\n\"following settings:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:193\nmsgid \"Overriding body of notifications\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:195\nmsgid \"\"\n\"If you wish Tigase XMPP Server to override forward body of the encrypted \"\n\"message in the push notification (for example to avoid indicating that \"\n\"there is an \\\"error\\\") you can do that with following settings:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:206\nmsgid \"\"\n\"Enabling push notifications for messages received when all resources are \"\n\"AWAY/XA/DND\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:208\nmsgid \"\"\n\"Push notifications may also be sent by Tigase XMPP Server when new \"\n\"message is received and all resources of recipient are in AWAY/XA/DND \"\n\"state. To enable this type of notifications you need to enable additional\"\n\" push delivery extension named ``away`` in default push processor:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:218\nmsgid \"\"\n\"As this behaviour may not be expected by users and users need a \"\n\"compatible XMPP client to properly handle this notifications (XMPP client\"\n\" needs to retrieve message history from server to get actual message), in\"\n\" addition to enabling this plugin on the server, XMPP clients need to \"\n\"explicitly activate this feature. They can do that by including ``away`` \"\n\"attribute with value of ``true`` in push ``enable`` element send to the \"\n\"server, as in following example:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:220\nmsgid \"**Enabling Push notifications for away/xa/dnd account.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:232\nmsgid \"\"\n\"If later on, user decides to disable notification for account in \"\n\"away/xa/dnd state, it may disable push notifications or once again send \"\n\"stanza to enable push notification but without ``away`` attribute being \"\n\"set:\"\nmsgstr \"\"\n\n#~ msgid \"\"\n#~ \"`config.tdsl <#dslConfig>`__ file is a \"\n#~ \"simple text file with server parameters\"\n#~ \" in form: **key** = **value**. When\"\n#~ \" the XML configuration file is \"\n#~ \"missing the Tigase server reads \"\n#~ \"``config.tdsl`` file and uses parameters \"\n#~ \"found there as defaults for generation\"\n#~ \" of the XML file. Therefore if \"\n#~ \"you change the ``config.tdsl`` file you\"\n#~ \" normally have to stop the server,\"\n#~ \" remove the XML file and start \"\n#~ \"the server again. All the settings \"\n#~ \"from the ``config.tdsl`` are read and\"\n#~ \" applied to the XML configuration. \"\n#~ \"The properties file is easy to \"\n#~ \"read and very safe to modify. At\"\n#~ \" the moment this is the recommended\"\n#~ \" way change the server configuration.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`tigase.conf <#manualconfig>`__ is the Tigase\"\n#~ \" server startup configuration. It is \"\n#~ \"actually not used by the server \"\n#~ \"itself. It rather contains operating \"\n#~ \"system settings and environment parameters \"\n#~ \"to correctly run the `Java Virtual \"\n#~ \"Machine <http://java.sun.com/>`__. It is only\"\n#~ \" useful on the unix-like systems \"\n#~ \"with Bash shell. If you run the\"\n#~ \" server on MS Windows systems \"\n#~ \"``tigase.bat`` and ``wrapper.conf`` files are\"\n#~ \" used instead. The ``tigase.conf`` file \"\n#~ \"is read and loaded by the \"\n#~ \"``scripts/tigase.sh`` shell script which also\"\n#~ \" scans the operating system environment \"\n#~ \"for Java VM and other tools \"\n#~ \"needed.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"``class`` which will be used as a\"\n#~ \" bean, in example above we set \"\n#~ \"class as ``className``. *_(default: if \"\n#~ \"you try to configure bean under \"\n#~ \"name which has default class assigned\"\n#~ \" with it in Tigase framework then \"\n#~ \"this assigned class will be used. \"\n#~ \"In other case you need to pass \"\n#~ \"name of class to use as a \"\n#~ \"bean)_*\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"``active`` (boolean) whether you want \"\n#~ \"the bean to be active or not \"\n#~ \"(beans with ``active`` set to ``false``\"\n#~ \" are not loaded). *_(default: true)_*\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"``exportable`` (boolean) defines if this \"\n#~ \"bean should be exported and available\"\n#~ \" for use for beans in inner \"\n#~ \"scopes. This is advanced option in \"\n#~ \"most cases it is recommended to \"\n#~ \"omit this field in configuration. \"\n#~ \"*_(default: false)_*\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"Tigase configuration file ``tigase.conf`` \"\n#~ \"(described in more detail in `??? \"\n#~ \"<#manualconfig>`__) mentioned a couple of \"\n#~ \"environmental variables which are related \"\n#~ \"to the operation of the JVM. In\"\n#~ \" this guide we would like to \"\n#~ \"expound on those configuration options \"\n#~ \"and provide hints for the optimal \"\n#~ \"settings.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"The `'default-virtual-host' <#virtHosts>`_ \"\n#~ \"property allows to define name of \"\n#~ \"the single vhost domain which will \"\n#~ \"be considered a default vhost domain \"\n#~ \"for this installation. It allows you \"\n#~ \"just to configure the domain name. \"\n#~ \"Any additional configuration needs to be\"\n#~ \" configured using ad-hoc commands.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"Virtual hosts should be managed using\"\n#~ \" ad-hoc commands or admin ui, \"\n#~ \"visit `Add and Manage Domains \"\n#~ \"<#addManageDomain>` for description of vhosts\"\n#~ \" management process or visit `Specification\"\n#~ \" for ad-hoc Commands Used to \"\n#~ \"Manage Virtual Domains <#adhocCommands>` for\"\n#~ \" more information about ad-hoc \"\n#~ \"commands.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"If you have components that may \"\n#~ \"not be able to handle multiple \"\n#~ \"vhosts or cluster mode, we have \"\n#~ \"developed a virtual component solution \"\n#~ \"as well, details in the `Virtual \"\n#~ \"Components for the Tigase Cluster \"\n#~ \"<#virtualComponents>`__ section.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"You may also want to reference the\"\n#~ \" Vhosts API for additional information: \"\n#~ \"- `API Description for Virtual Domains\"\n#~ \" Management in Tigase Server \"\n#~ \"<#addManageDomain>`.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"After initial definition of default \"\n#~ \"configuration or after first startup of\"\n#~ \" Tigase XMPP Server it is possible\"\n#~ \" to configure Virtual Host defaults \"\n#~ \"using ad-hoc commands by modifying \"\n#~ \"values for ``default`` using ad-hoc \"\n#~ \"as described in `Specification for \"\n#~ \"ad-hoc Commands Used to Manage \"\n#~ \"Virtual Domains <#adhocCommands>`__.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"Alternatively, you may edit default \"\n#~ \"Virtual Host configuration (configuration for\"\n#~ \" domain ``default``) using Admin UI \"\n#~ \"which by default is available at \"\n#~ \"``http://localhost:8080/admin/``.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"Virtual hosts should be managed using\"\n#~ \" ad-hoc commands or admin ui, \"\n#~ \"visit :ref:`Add and Manage \"\n#~ \"Domains<addManageDomain>` for description of \"\n#~ \"vhosts management process or visit \"\n#~ \"`Specification for ad-hoc Commands Used\"\n#~ \" to Manage Virtual Domains<adhocCommands>` \"\n#~ \"for more information about ad-hoc \"\n#~ \"commands.\"\n#~ msgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Administration/Database_Management/Management.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2023-02-16 15:01-0800\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.11.0\\n\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:2\nmsgid \"Database Management\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:4\nmsgid \"\"\n\"Tigase is coded to perform with multiple database types and numbers. \"\n\"Owing to it’s versatile nature, there are some tools and procedures that \"\n\"may be of use to certain administrators.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:7\nmsgid \"Recommended database versions\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:9\nmsgid \"\"\n\"As of v8.0.0 here are the minimum and recommended versions of databases \"\n\"for use with Tigase:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:12\nmsgid \"Database\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:12\nmsgid \"Recommended Version\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:12\nmsgid \"Minimum Version\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:12\nmsgid \"Additional Information\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:14\nmsgid \"DerbyDB\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:14\nmsgid \"10.12.1.1\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:14\nmsgid \"Included with Tigase XMPP Server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:16\nmsgid \"MySQL\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:16\nmsgid \"5.7\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:16\nmsgid \"Required to properly support timestamp storage with millisecond precision\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:18\nmsgid \"SQLServer\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:18\nmsgid \"2014\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:18\nmsgid \"2012\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:18\nmsgid \"2012 needed so we can count use fetch-offset pagination feature.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:20\nmsgid \"PostgreSQL\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:20\nmsgid \"13.0\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:20\nmsgid \"9.4\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:20\nmsgid \"\"\n\"New UA schema requires at least 9.4; if using version older than 13 \"\n\"manual installation of ``uuid-ossp`` extension is required (1)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:22\nmsgid \"MongoDB\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:22\nmsgid \"3.2\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:22\nmsgid \"3.0\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:24\nmsgid \"MariaDB\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:24\nmsgid \"?\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:24\nmsgid \"10.0.12\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:24\nmsgid \"\"\n\"Basic features works with 10.0.12-MariaDB Homebrew, but is not fully \"\n\"tested.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:29\nmsgid \"\"\n\"For PostgreSQL version older than 13.0 manual installation of ``uuid-\"\n\"ossp`` by the superuser to the *created database* is required:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:35\nmsgid \"\"\n\"Although Tigase may support other versions of databases, these are the \"\n\"ones we are most familiar with in offering support and advice. Use of \"\n\"databases outside these guidelines may result in unforeseen errors.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:38\nmsgid \"Database Watchdog\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:40\nmsgid \"\"\n\"It is possible to have Tigase test availability and existence of database\"\n\" periodically only when db connections are idle. By default this ping is \"\n\"sent once every 60 minutes to each connected repository. However this can\"\n\" be overridden as a part of the dataSource property:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:54\nmsgid \"This setting changes frequency to 30 minutes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:65\nmsgid \"This one changes to 15 minutes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:69\nmsgid \"\"\n\"see :ref:`Period / Duration values<PeriodDurationvalues>` for format \"\n\"details\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:72\nmsgid \"Using modified database schema\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:74\nmsgid \"\"\n\"If you are using Tigase XMPP Server with modified schema (changed \"\n\"procedures or tables) and you do not want Tigase XMPP Server to maintain \"\n\"it and automatically upgrade, you can disable ``schema-management`` for \"\n\"any data source. If ``schema-management`` is disable for particular data \"\n\"source then Tigase XMPP Server will not update or modify database schema \"\n\"in any way. Moreover it will not check if schema version is correct or \"\n\"not.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:76\nmsgid \"**Disabling ``schema-management`` for ``default`` data source**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:89\nmsgid \"\"\n\"If ``schema-management`` is disabled, then it is administrator \"\n\"responsibility to maintain database schema and update it if needed (ie. \"\n\"if Tigase XMPP Server schema was changed).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:94\nmsgid \"Schema files maintenance\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:96\nmsgid \"\"\n\"This document describes schema files layout and assumptions about it. In \"\n\"addition it describes how and when it should be updated.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:99\nmsgid \"Assumptions\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:101\nmsgid \"Following assumptions are in place:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:103\nmsgid \"\"\n\"All schema files are *loadable* multiple times - this is by far most \"\n\"important assumptions and it’s allow to get away without explicit and \"\n\"detailed checking of loaded version (it’s already handled on the schema \"\n\"level as of version 8.0.0)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:105\nmsgid \"\"\n\"Required schema version is calculated from the component version (which \"\n\"is set in the project configuration file - usually ``pom.xml``, but it’s \"\n\"possible to override it in code via annotations - please see Developer \"\n\"Guild in Server documentation for details)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:107\nmsgid \"\"\n\"we will maintain *\\\"3 versions schema files\\\"*, i.e. in the distribution \"\n\"package we will provide schema versions for the ``current_version`` and \"\n\"two major versions behind (and all maintenance version schema files) - \"\n\"this will allow *quick upgrade* even from rather older versions\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:109\nmsgid \"\"\n\"``SNAPSHOT`` versions will print a log entry indicating that there may \"\n\"have been changes in schema and it’s recommended to run the upgrade (we \"\n\"are aiming at frequent releases thus mandatory schema version check will \"\n\"be done only with final version)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:112\nmsgid \"Checks\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:114\nmsgid \"We will check:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:116\nmsgid \"\"\n\"if it’s possible to upgrade the schema (based on the current schema \"\n\"version in the database and available SQL files and their respective \"\n\"versions - if );\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:118\nmsgid \"\"\n\"if it’s required to upgrade the schema during server startup (until 7.1.x\"\n\" [inclusive] it was done only for tigase-server, will be done by all \"\n\"components)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:120\nmsgid \"\"\n\"if it’s required to upgrade the schema during run of ``upgrade-schema``) \"\n\"(if schema is already in the latest required version, executing all SQL \"\n\"files is not required hence speeding up upgrade)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:122\nmsgid \"\"\n\"During startup of ``SNAPSHOT`` version, even if the schema version match,\"\n\" a prompt to re-run ``upgrade-schema`` will be printed in the ``logs\"\n\"/tigase-console.log``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:125\nmsgid \"Schema files layout\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:128\nmsgid \"Filename layout\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:130\nmsgid \"Basic schema filename layout consists of 3 basic parts:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:132\nmsgid \"\"\n\"name of relational database management system (RDBMS) for which it’s \"\n\"intended (e.g. ``derby``, ``mysql``, ``postgresql``, ``sqlserver``);\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:134\nmsgid \"name of the Tigase component for which it’s intended;\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:136\nmsgid \"version of the schema file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:138\nmsgid \"\"\n\"For each component and version it’s possible (but not mandatory) to split\"\n\" all database related functionality into multiple files but it’s \"\n\"essential that they would be linked/included in the base file for \"\n\"particular database/component/version file. This allows separating Stored\"\n\" Procedures (``-sp``), base schema (``-schema``) and setting properties \"\n\"(``-props``). In principle the filename pattern looks as follows\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:144\nmsgid \"\"\n\"For example schema file for version 7.0.0 of Tigase Server for Derby \"\n\"looks as follow:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:152\nmsgid \"Files structure\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:154\nmsgid \"\"\n\"As mentioned before, we should support all versions matching ``old-\"\n\"stable``, ``stable`` and ``master``, which translates to two main \"\n\"versions behind *current-version*, that is version: *current-version - \"\n\"2*). This results in having 3 versions of the schema in the repository at\"\n\" any given time (two of them being \\\\``upgrades'' to the oldest, base \"\n\"schema):\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:156\nmsgid \"``current-version`` *minus* 2: base schema\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:158\nmsgid \"\"\n\"``current-version`` *minus* 1: all changes from ``current-version`` \"\n\"*minus* 2 to ``current-version`` *minus* 1\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:160\nmsgid \"\"\n\"``current-version``: all changes from ``current-version`` *minus* 1 to \"\n\"``current-version``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:164\nmsgid \"\"\n\"``current-version`` *MUST* always match version of the component (defined\"\n\" in pom.xml).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:168\nmsgid \"\"\n\"It’s possible to have multiple files within version (related to smaller, \"\n\"maintenance upgrade) as the SchemaLoader would collect all files which \"\n\"version falls within range and .\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:170\nmsgid \"\"\n\"For example with the release of version 8.0.0 this will translate to \"\n\"following versions:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:172\nmsgid \"``7.0.0``: base schema\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:174\nmsgid \"``7.1.0``: all changes from ``7.0.0`` to ``7.1.0``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:176\nmsgid \"``8.0.0``: all changes from ``7.1.0`` to ``8.0.0``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:180\nmsgid \"All schema files must be stored under ``src/main/database/``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:183\nmsgid \"Handling of changes in the schema\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:185\nmsgid \"There are two main workflows defined\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:188\nmsgid \"During release of the version\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:190\nmsgid \"\"\n\"As we keep at the most only 3 versions of the schema, after release of \"\n\"the version we need to adjust (flatten) the files to maintain structure \"\n\"defined in *Files structure* (it may happen, that there wouldn’t be any \"\n\"changes in the schema for particular version which will result in \"\n\"relatively empty ``current-version`` schema file – only setting current \"\n\"version for component with ``setVersion('component','<current-version\"\n\"></current-version>');`` ).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:192\nmsgid \"\"\n\"For example we are about to release version ``8.0.0``. This results in \"\n\"the following versions of the schema (in the example for the server) in \"\n\"the repository:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:194\nmsgid \"``<database>-server-schema-7.0.0.sql``: base schema\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:196\nmsgid \"``<database>-server-schema-7.1.0.sql``: including changes for ``7.1.0``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:198\n#: ../../Tigase_Administration/Database_Management/Management.rst:208\nmsgid \"``<database>-server-schema-8.0.0.sql``: including changes for ``8.0.0``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:202\nmsgid \"\"\n\"It’s possible that there will be maintenance versions in the list as \"\n\"well, e.g.: ``<database>-server-schema-7.1.1.sql`` and ``<database\"\n\">-server-schema-7.1.2.sql``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:204\nmsgid \"\"\n\"After the release we specify the version of the next release in pom.xml \"\n\"(for example ``8.1.0`` and the same version will be the ``current-\"\n\"version`` making the oldest available version ``7.1.0``. Because of that \"\n\"we *MUST* incorporate all the changes in ``7.1.0`` onto ``7.0.0`` \"\n\"creating new base file with version ``7.1.0``, i.e.:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:206\nmsgid \"``<database>-server-schema-7.1.0.sql``: base schema\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:210\nmsgid \"``<database>-server-schema-8.1.0.sql``: including changes for ``8.1.0``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:213\nmsgid \"Maintenance releases\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:215\nmsgid \"\"\n\"Following cases will be discussed with solid-version examples. Comments \"\n\"will be provided in-line Following assumptions are made:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:217\nmsgid \"Version succession: ``5.1.0``, ``5.2.0``, ``7.0.0``, ``7.1.0``, ``8.0.0``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:219\nmsgid \"\"\n\"Versions mapping: ``master`` (``8.0.0``), ``stable`` (``7.1.0``), ``old-\"\n\"stable`` (``7.0.0``):\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:221\n#: ../../Tigase_Administration/Database_Management/Management.rst:257\n#: ../../Tigase_Administration/Database_Management/Management.rst:293\nmsgid \"schema files in ``old-stable`` branch\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:223\n#: ../../Tigase_Administration/Database_Management/Management.rst:259\n#: ../../Tigase_Administration/Database_Management/Management.rst:295\nmsgid \"5.1.0 (base)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:225\n#: ../../Tigase_Administration/Database_Management/Management.rst:261\n#: ../../Tigase_Administration/Database_Management/Management.rst:297\nmsgid \"5.2.0 (upgrade)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:227\n#: ../../Tigase_Administration/Database_Management/Management.rst:233\n#: ../../Tigase_Administration/Database_Management/Management.rst:263\n#: ../../Tigase_Administration/Database_Management/Management.rst:271\n#: ../../Tigase_Administration/Database_Management/Management.rst:299\n#: ../../Tigase_Administration/Database_Management/Management.rst:305\nmsgid \"7.0.0 (upgrade)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:229\n#: ../../Tigase_Administration/Database_Management/Management.rst:267\n#: ../../Tigase_Administration/Database_Management/Management.rst:301\nmsgid \"schema files in ``stable`` branch\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:231\n#: ../../Tigase_Administration/Database_Management/Management.rst:269\n#: ../../Tigase_Administration/Database_Management/Management.rst:303\nmsgid \"5.2.0 (base)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:235\n#: ../../Tigase_Administration/Database_Management/Management.rst:241\n#: ../../Tigase_Administration/Database_Management/Management.rst:275\n#: ../../Tigase_Administration/Database_Management/Management.rst:283\n#: ../../Tigase_Administration/Database_Management/Management.rst:307\n#: ../../Tigase_Administration/Database_Management/Management.rst:313\nmsgid \"7.1.0 (upgrade)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:237\n#: ../../Tigase_Administration/Database_Management/Management.rst:277\n#: ../../Tigase_Administration/Database_Management/Management.rst:309\nmsgid \"schema files in ``master`` branch\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:239\n#: ../../Tigase_Administration/Database_Management/Management.rst:279\n#: ../../Tigase_Administration/Database_Management/Management.rst:311\nmsgid \"7.0.0 (base)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:243\n#: ../../Tigase_Administration/Database_Management/Management.rst:285\nmsgid \"8.0.0 (upgrade)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:247\nmsgid \"Making a change in ``old-stable`` (and ``stable``)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:249\nmsgid \"\"\n\"If we made a schema change in ``old-stable`` version (and it’s branch) we\"\n\" must:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:251\nmsgid \"create a new file with upgraded version number;\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:253\nmsgid \"propagate the change to the ``stable`` and ``master`` branch.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:255\nmsgid \"Repository changes:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:265\nmsgid \"\"\n\"7.0.1 (upgrade) **←** making a *change* here results in the schema \"\n\"version being bumped to 7.0.1\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:273\n#: ../../Tigase_Administration/Database_Management/Management.rst:281\nmsgid \"7.0.1 (upgrade) **←** we must port the *change* here\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:289\nmsgid \"Making a change in ``master``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:291\nmsgid \"\"\n\"If we made a schema change in ``master`` version we don’t propagate the \"\n\"change to the ``stable`` and ``old-stable`` branch.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:315\nmsgid \"\"\n\"8.0.0 (upgrade) **←** we make the *change* here, as this is the \"\n\"development version schema version remains the same.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:319\nmsgid \"Implementation details\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:322\nmsgid \"In-file control\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:324\nmsgid \"There are two main control instructions (intended for ``SchemaLoader``):\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:326\nmsgid \"\"\n\"denoting Queries with ``-- QUERY START:`` and ``-- QUERY END:`` - each \"\n\"must be placed in own, separate file with the query being enclosed by the\"\n\" two of them, for example:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:334\nmsgid \"\"\n\"sourcing other file with ``-- LOAD FILE: <path to .sql file>`` - path \"\n\"must be on the same line, following control instruction, for example:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:341\nmsgid \"Storing version in the database\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:343\nmsgid \"\"\n\"Each repository will have a table ``tig_schema_versions`` with the \"\n\"information about all installed components and it’s versions in that \"\n\"particular repository. There will be an associated stored procedure to \"\n\"obtain and set version:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:345\nmsgid \"table:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:356\nmsgid \"stored procedures ``get/setVersion(‘component’,'version');``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:358\nmsgid \"\"\n\"It will be stored and maintained in the file named ``<RDBMS_name>-common-\"\n\"schema-<version>.sql``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation.inc:2\nmsgid \"Database Preparation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation.inc:4\nmsgid \"\"\n\"Tigase uses generally the same database schema and the same set of stored\"\n\" procedures and functions on every database. However, the schema creation\"\n\" scripts and code for stored procedures is different for each database. \"\n\"Therefore the manual process to prepare database is different for each \"\n\"database system.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation.inc:6\nmsgid \"\"\n\"Starting with v8.0.0, most of the database tasks have been automated and \"\n\"can be called using simple text, or using interactive question and answer\"\n\" style. We **DO NOT RECOMMEND** going through manual operation, however \"\n\"we have kept manual activation of different databases to the Appendix. If\"\n\" you are interested in how we manage and update our database schemas, you\"\n\" may visit the :ref:`Schema files maintenance<Schemafilesmaintenance>` \"\n\"section of our Redmine installation for more detailed information.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation.inc:8\nmsgid \":ref:`The DBSchemaLoader Utility<SchemaUtility>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation.inc:10\nmsgid \":ref:`Hashed User Passwords in Database<HashedUserPasswordsinDatabase>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation.inc:12\nmsgid \":ref:`Support for MongoDB<PreparingTigaseforMongoDB>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation.inc:14\nmsgid \"Appendix entries\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation.inc:16\nmsgid \"\"\n\":ref:`Manual installtion for MySQL<Prepare-the-MySQL-Database-for-the-\"\n\"Tigase-Server>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation.inc:18\nmsgid \"\"\n\":ref:`Manual installtion for Derby<Prepare-the-Derby-Database-for-the-\"\n\"Tigase-Server>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation.inc:20\nmsgid \"\"\n\":ref:`Manual installtion for SQLServer<Prepare-the-MS-SQL-Server-\"\n\"Database-for-the-Tigase-Server>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation.inc:22\nmsgid \"\"\n\":ref:`Manual installtion for PostGRESQL<Prepare-the-PostgreSQL-Database-\"\n\"for-the-Tigase-Server>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:4\nmsgid \"Schema Utility\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:6\nmsgid \"\"\n\"With the release of v8.0.0 calling the Tigase dbSchemaLoader utility now \"\n\"can be done using tasks instead of calling the specific method. Support \"\n\"for Derby, MySQL, PostgreSQL, MSSQL, and MongoDB is available.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:8\nmsgid \"\"\n\"In order to use this utility with any of the databases, you will need to \"\n\"first have the database environment up and running, and have established \"\n\"user credentials. You may use root or an account with administrator write\"\n\" privileges.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:11\nmsgid \"Operation & Variables\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:13\nmsgid \"Operation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:15\nmsgid \"\"\n\"Operating the schema utility is quite easy! To use it run this command \"\n\"from the installation directory:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:21\nmsgid \"\"\n\"Operations are now converted to tasks, of which there are now three: \"\n\"``install-schema``, ``upgrade-schema``, and ``destroy-schema``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:23\nmsgid \"\"\n\"``upgrade-schema``: Upgrade the schema of the database specified in your \"\n\"``config.tdsl`` configuration file. (options are ignored for this option)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:25\nmsgid \"``install-schema``: Install a schema to database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:27\nmsgid \"``destroy-schema``: Destroy database and schemas. **DANGEROUS**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:29\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:85\nmsgid \"Options\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:31\nmsgid \"\"\n\"Use the following options to customize. Options in bold are required, \"\n\"*{potential options are in brackets}*:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:33\nmsgid \"``--help`` Prints the help for the task.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:35\nmsgid \"\"\n\"``-I`` or ``--interactive`` - enables interactive mode which will prompt \"\n\"for parameters not defined.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:37\nmsgid \"\"\n\"``-T`` or ``--dbType`` - database type {derby, mongodb, mysql, \"\n\"postgresql, sqlserver}.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:39\nmsgid \"\"\n\"``-C`` or ``--components`` - Allows the specification of components for \"\n\"use when installing a schema.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:42\nmsgid \"Usage\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:44\nmsgid \"upgrade-schema\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:46\nmsgid \"\"\n\"This task will locate any schema versions above your current one, and \"\n\"will install them to the database configured in the ``config.tdsl`` file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:50\nmsgid \"\"\n\"To use this utility, you must have Tigase XMPP server fully setup with a \"\n\"configured configuration file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:56\nmsgid \"Windows users will need to run the command using the following command:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:65\nmsgid \"install-schema\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:67\nmsgid \"This task will install a schema using the parameters provided.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:69\nmsgid \"\"\n\"**If you are setting up a server manually, we HIGHLY recommend using this\"\n\" method**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:75\nmsgid \"\"\n\"This command will install tigase using a Derby database on one named \"\n\"``tigasedb`` hosted on ``localhost``. The username and password editing \"\n\"the database is ``tigase_pass`` and ``root``. Note that ``-J`` explicitly\"\n\" adds the administrator, this is highly recommended with the ``-N`` \"\n\"passing the password.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:77\nmsgid \"If you are using a windows system, you need to call the program directly:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:87\nmsgid \"\"\n\"Options for schema installation are as follows, required options are in \"\n\"bold\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:89\nmsgid \"``--help``, Outputs the help.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:91\nmsgid \"\"\n\"``-I``, ``--interactive`` - enables interactive mode, which will result \"\n\"in prompting for any missing parameters.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:93\nmsgid \"\"\n\"``-C``, ``--components=`` - list of enabled components identifiers (+/-),\"\n\" possible values: [``amp``, ``bosh``, ``c2s``, ``eventbus``, ``ext-\"\n\"disco``, ``http``, ``mdns``, ``message-archive``, ``monitor``, ``muc``, \"\n\"``pubsub``, ``push``, ``s2s``, ``socks5``, ``test``, ``unified-archive``,\"\n\" ``upload``, ``ws2s``] (default: amp,bosh,c2s,eventbus,http,message-\"\n\"archive,monitor,muc,pubsub,s2s,ws2s). **This is required for certain \"\n\"components like socks5.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:95\nmsgid \"\"\n\"``-T``, ``--dbType=`` - database server type, possible values are: \"\n\"[``derby``, ``mongodb``, ``mysql``, ``postgresql``, ``sqlserver``] \"\n\"(*required*)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:97\nmsgid \"\"\n\"``-D``, ``--dbName=`` - name of the database that will be created (by \"\n\"default it is ``tigasedb``). (*required*)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:99\nmsgid \"\"\n\"``-H``, ``--dbHostname=`` - address of the database instance (by default \"\n\"it is ``localhost``). (*required*)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:101\nmsgid \"\"\n\"``-U``, ``--dbUser=`` - name of the user that will be created \"\n\"specifically to access Tigase XMPP Server database (default is \"\n\"``tigase_user``). (*required*)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:103\nmsgid \"\"\n\"``-P``, ``--dbPass=`` - password of the user that will be created \"\n\"specifically to access Tigase XMPP Server database (default is \"\n\"``tigase_pass``). (*required*)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:105\nmsgid \"\"\n\"``-R``, ``--rootUser=`` - database root account username used to create \"\n\"user and database (default is ``root``). (*required*)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:107\nmsgid \"\"\n\"``-A``, ``--rootPass=`` - database root account password used to create \"\n\"user and database (default is ``root``). (*required*)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:109\nmsgid \"\"\n\"``-S``, ``--useSSL`` - enable SSL support for database connection (if the\"\n\" database supports it) (default is false).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:111\nmsgid \"\"\n\"``-F``, ``--file=`` - comma separated list of SQL files that will be \"\n\"processed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:113\nmsgid \"\"\n\"``-Q``, ``--query=`` - custom queries to be executed, see :ref:`Query \"\n\"function<queryschema>` for details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:115\nmsgid \"\"\n\"``-L``, ``--logLevel=`` - logger level used during loading process \"\n\"(default is ``CONFIG``).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:117\nmsgid \"``-J``, ``--adminJID=`` - comma separated list of administrator JID(s).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:119\nmsgid \"\"\n\"``-N``, ``--adminJIDpass=`` - password that will be used for the entered \"\n\"JID(s) - one password for all configured JIDs.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:121\nmsgid \"``--getURI=`` - generate database URI (default is ``false``).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:123\nmsgid \"\"\n\"``--ignoreMissingFiles=`` - force ignoring missing files errors (default \"\n\"is ``false``).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:127\nmsgid \"Query function\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:129\nmsgid \"\"\n\"Should you decide to customize your own functions, or have specific \"\n\"information you want to put into the database, you can use the -query \"\n\"function to perform a single query step.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:135\nmsgid \"\"\n\"Of course this would break the schema for tigasedb by adding an \"\n\"unexpected table, you will receive the following message:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:141\nmsgid \"\"\n\"But this is a demonstration how you may run a query through the database \"\n\"without the need to use another tool. Note that you will need to select \"\n\"the specific database for each query.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:143\nmsgid \"destroy-schema\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:146\nmsgid \"This will destroy the database specified in the configuration file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:150\nmsgid \"**THIS ACTION IS NOT REVERSIBLE**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:156\nmsgid \"\"\n\"Only use this if you wish to destroy a database and not have the \"\n\"information recoverable.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:158\nmsgid \"Windows users will need to call the method directly:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:165\nmsgid \"A note about MySQL\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:167\nmsgid \"If you are using these commands, you may result in the following error:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:173\nmsgid \"\"\n\"If this occurs, you will need to upgrade your version of MySQL using the \"\n\"following command:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:179\nmsgid \"\"\n\"After entering the password and upgrading MySQL the schema error should \"\n\"no longer show when working with Tigase databases.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:4\nmsgid \"Prepare the MySQL Database for the Tigase Server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:6\nmsgid \"\"\n\"This guide describes how to prepare MySQL database for connecting Tigase \"\n\"server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:8\nmsgid \"\"\n\"The MySQL database can be prepared in many ways. Most Linux distributions\"\n\" contain tools which allow you to go through all steps from the shell \"\n\"command line. To make sure it works on all platforms in the same way, we \"\n\"will first show how to do it under MySQL command line client.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:11\nmsgid \"Configuring from MySQL command line tool\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:13\nmsgid \"\"\n\"Run the MySQL command line client in either Linux or MS Windows \"\n\"environment and enter following instructions from the Tigase installation\"\n\" directory:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:19\nmsgid \"Once logged in, create the database for the Tigase server:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:25\nmsgid \"\"\n\"Add the ``tigase_user`` user and grant him access to the ``tigasedb`` \"\n\"database. Depending on how you plan to connect to the database (locally \"\n\"or over the network) use one of following commands or all if you are not \"\n\"sure:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:27\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:134\nmsgid \"Grant access to tigase_user connecting from any network address.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:34\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:143\nmsgid \"Grant access to tigase_user connecting from localhost.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:41\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:152\nmsgid \"Grant access to tigase_user connecting from local machine only.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:48\nmsgid \"And now you can update user permission changes in the database:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:56\nmsgid \"\"\n\"It’s essential to enable `log_bin_trust_function_creators \"\n\"<https://dev.mysql.com/doc/refman/8.0/en/replication-options-binary-\"\n\"log.html#sysvar_log_bin_trust_function_creators>`__ option in MySQL \"\n\"server, for example by running:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:62\nmsgid \"Installing Schemas\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:64\nmsgid \"\"\n\"Starting with v8.0.0 the Schemas are no longer linked, and will need to \"\n\"manually be installed in the following order.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:66\nmsgid \"Switch to the database you have created:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:74\nmsgid \"\"\n\"We are assuming you run the mysql client in Linux from the Tigase \"\n\"installation directory, so all file links will be relative.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:76\nmsgid \"Next install the schema files:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:82\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:171\nmsgid \"You will need to repeat this process for the following files:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:55\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:73\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:97\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:186\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:62\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:115\nmsgid \"Other components may require installation such as:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:107\nmsgid \"Windows instructions:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:109\nmsgid \"\"\n\"On Windows you have probably to enter the full path, assuming Tigase is \"\n\"installed in C:\\\\Program Files\\\\Tigase:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:120\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:72\nmsgid \"Configuring From the Linux Shell Command Line\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:122\nmsgid \"Follow steps below to prepare the MySQL database:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:124\nmsgid \"Create the database space for the Tigase server:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:130\nmsgid \"\"\n\"Add the ``tigase_user`` user and grant access to the tigasedb database. \"\n\"Depending on how you plan to connect to the database (locally or over the\"\n\" network) use one of following commands or all if you are not sure:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:132\nmsgid \"Selective access configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:161\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:33\nmsgid \"Schema Installation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:163\nmsgid \"Load the proper mysql schemas into the database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:197\nmsgid \"Configuring MySQL for UTF-8 Support\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:199\nmsgid \"In my.conf put following lines:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:216\nmsgid \"Then connect to the database from the command line shell check settings:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:223\nmsgid \"\"\n\"If any of these shows something else then 'utf8' then you need to fix it \"\n\"using the command:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:229\nmsgid \"\"\n\"You can now also test your database installation if it accepts UTF-8 \"\n\"data. The easiest way to ensure this is to just to create an account with\"\n\" UTF-8 characters:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:235\nmsgid \"And then check that the account has been created:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:241\nmsgid \"\"\n\"If the last command gives you no results it means there is still \"\n\"something wrong with your settings. You might also want to check your \"\n\"shell settings to make sure your command line shell supports UTF-8 \"\n\"characters and passes them correctly to MySQL:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:249\nmsgid \"\"\n\"It seems that MySQL 5.0.x also needs extra parameters in the connection \"\n\"string: '&useUnicode=true&characterEncoding=UTF-8' while MySQL 5.1.x \"\n\"seems to not need it but it doesn’t hurt to have it for both versions. \"\n\"You have to edit ``etc/config.tdsl`` file and append this to the database\"\n\" connection string.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:251\nmsgid \"\"\n\"For MySQL 5.1.x, however, you need to also update code for all database \"\n\"stored procedures and functions used by the Tigase. They are updated for \"\n\"Tigase version 4.4.x and up, however if you use an older version of the \"\n\"Tigase server, you can reload stored procedures using the file from SVN.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:254\nmsgid \"Other MySQL Settings Worth Considering\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:256\nmsgid \"\"\n\"There are a number of other useful options, especially for performance \"\n\"improvements. Please note, you will have to review them as some of them \"\n\"may impact data reliability and are useful for performance or load tests \"\n\"installations only.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:264\nmsgid \"Some the general MySQL settings which mainly affect performance:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:279\nmsgid \"InnoDB specific settings:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:302\nmsgid \"\"\n\"These settings may not be fully optimized for your system, and have been \"\n\"only tested on our systems. If you have found better settings for your \"\n\"systems, feel free to `let us know <http://tigase.net/contact>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:305\nmsgid \"Support for emoji and other icons\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:307\nmsgid \"\"\n\"Tigase Database Schema can support emojis and other icons, however by \"\n\"using UTF-8 in ``mysqld`` settings will not allow this. To employ \"\n\"settings to support emojis and other icons, we recommend you use the \"\n\"following in your MySQL configuration file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:316\nmsgid \"\"\n\"Doing this, Tigase XMPP Server Database will still use ``utf8`` character\"\n\" set, with ``utf8_general_ci`` as collation, and only fields which \"\n\"require support for emojis will be converted to ``utf8mb4``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:320\nmsgid \"\"\n\"If for some reason, with above settings applied to your MySQL instance, \"\n\"you still receive :literal:`java.sql.SQLException: Incorrect string \"\n\"value: ` you should add to your database URI passed in Tigase XMPP Server\"\n\" following configuration `&useUnicode=true&characterEncoding=UTF-8`. If \"\n\"even this fails too, then you may try adding \"\n\"``&connectionCollation=utf8mb4_bin`` as a last resort. This changes \"\n\"situation from previous versions that shipped older MySQL JDBC connector.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:324\nmsgid \"\"\n\"Tigase XMPP Server databases should be created with ``utf8_general_ci`` \"\n\"collation as it will work properly and is fastest from \"\n\"``utf8mb4_general_ci`` collations supported by MySQL\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:4\nmsgid \"Prepare the Derby Database for the Tigase Server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:6\nmsgid \"\"\n\"This guide describes how to prepare Derby database for connecting the \"\n\"Tigase server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:9\nmsgid \"Basic Setup\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:10\nmsgid \"\"\n\"Preparation of Derby database is quite simple, but the following \"\n\"assumptions are made\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:12\nmsgid \"``DerbyDB`` - Derby database name\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:14\nmsgid \"``database/`` directory contains all necessary schema files\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:16\nmsgid \"``jars/`` and ``libs/`` directories contains Tigase and Derby binaries\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:18\nmsgid \"General Approach\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:20\nmsgid \"\"\n\"From the main Tigase directory execute following commands (Linux and \"\n\"Windows accordingly)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:24\nmsgid \"You must use these sql files on order FIRST!\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:26\nmsgid \"**Linux**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:32\nmsgid \"**Windows**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:38\nmsgid \"\"\n\"This will create Derby database named DerbyDB in the main Tigase \"\n\"directory and load common version for common v0.1.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:40\nmsgid \"You will need to repeat this process again in for following order:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:64\nmsgid \"Connecting Tigase to database\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:66\nmsgid \"\"\n\"Once the database is setup, configure the ``config.tdsl`` file in Tigase \"\n\"and add the following configuration:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:4\nmsgid \"Prepare the MS SQL Server Database for the Tigase Server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:6\nmsgid \"\"\n\"This guide describes how to prepare the MS SQL Server database for \"\n\"connecting the Tigase server to it.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:8\nmsgid \"\"\n\"It’s expected that a working installation of Microsoft SQL Server is \"\n\"present. The following guide will describe the necessary configurations \"\n\"required for using MS SQL Server with Tigase XMPP Server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:11\nmsgid \"Preparing MS SQL Server Instance\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:13\nmsgid \"\"\n\"After installation of MS SQL Server an instance needs to be configure to \"\n\"handle incoming JDBC connections. For that purpose it’s required to open \"\n\"*SQL Server Configuration Manager*. In the left-hand side panel navigate \"\n\"to *SQL Server Configuration Manager*, then *SQL Server Network \"\n\"Configuration → Protocols for ${INSTANCE_NAME}*. After selecting instance\"\n\" in the right-hand side panel select TCP/IP and open *Properties*, in the\"\n\" Protocol tab in General section select Yes for Enabled property. In the \"\n\"IP Addresses tab select Yes for Active and Enabled properties of all IP \"\n\"Addresses that you want MS SQL Server to handle. Subsequently set the TCP\"\n\" Port property (if missing) to the default value - 1433. A restart of the\"\n\" instance may be required afterwards.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:16\nmsgid \"Configuration using MS SQL Server Management Studio\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:18\nmsgid \"\"\n\"In order to prepare the database you can use either a wizard or execute \"\n\"queries directly in the Query Editor. Firstly you need to establish a \"\n\"connection to the MS SQL Server instance. From Object Explorer select \"\n\"Connect and in the Connect to Server dialog enter administrator \"\n\"credentials.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:20\nmsgid \"Using Wizards\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:22\nmsgid \"Create Login\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:24\nmsgid \"\"\n\"In the left-hand side panel select Security → Logins and from context \"\n\"menu choose New Login, in the Wizard window enter desired Login name, \"\n\"select SQL Server authentication and enter desired password subsequently \"\n\"confirming action with OK\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:26\nmsgid \"Create Database\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:28\nmsgid \"\"\n\"From the Object Explorer select Databases node and from context menu \"\n\"select New Database; in the Wizard window enter desired Database name and\"\n\" enter previously created Login name into Owner field; subsequently \"\n\"confirming action with OK.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:31\nmsgid \"Using Queries\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:33\nmsgid \"\"\n\"From the Object Explorer root node’s context menu select New Query. In \"\n\"the Query windows execute following statements adjusting details to your \"\n\"liking:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:52\nmsgid \"Import Schema\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:54\nmsgid \"\"\n\"From the File menu Select Open → File (or use Ctrl+O) and then open \"\n\"following files:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:71\nmsgid \"\"\n\"These files must be done sequentially! They are not linked, and so may \"\n\"need to be done one at a time.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:82\nmsgid \"\"\n\"Subsequently select created database from the list of Available Databases\"\n\" (Ctrl+U) available on the toolbar and execute each of the opened files \"\n\"in the order listed above.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:85\nmsgid \"Configuring from command line tool\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:87\nmsgid \"\"\n\"Creation of the database and import of schema can be done from command \"\n\"line as well. In order to do that, execute following commands from the \"\n\"directory where Tigase XMPP Server is installed otherwise paths to the \"\n\"schema need to be adjusted accordingly:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:99\nmsgid \"\"\n\"Above can be automatized with provided script %tigase-server%\\\\scripts\"\n\"\\\\db-create-sqlserver.cmd (note: it needs to be executed from main Tigase\"\n\" XMPP Server directory due to maintain correct paths):\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:105\nmsgid \"If no parameters are provided then the following defaults are used:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:117\nmsgid \"Tigase configuration - config.tdsl\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:119\nmsgid \"Configuration of the MS SQL Server follows general database convention.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:129\nmsgid \"where any number of additional parameters can (and should) consist of:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:131\nmsgid \"``databaseName`` - name of the database\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:133\nmsgid \"``user`` - username configured to access database\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:135\nmsgid \"``password`` - password for the above username\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:137\nmsgid \"``schema`` - name of the database schema\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:139\nmsgid \"\"\n\"``lastUpdateCount`` - 'false' value causes all update counts to be \"\n\"returned, including those returned by server triggers\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:141\nmsgid \"Example:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:152\nmsgid \"JDBC: jTDS vs MS JDBC driver (obsolete)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:154\nmsgid \"\"\n\"Previously Tigase XMPP Server was shipped with the jTDS open source \"\n\"driver however since 8.3.0 we switched to the FOSS driver provided by the\"\n\" Microsoft itself. Previous jdbc url will fallback to the Microsoft \"\n\"driver automatically.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:156\nmsgid \"Microsoft driver:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:4\nmsgid \"Prepare the PostgreSQL Database for the Tigase Server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:6\nmsgid \"\"\n\"This guide describes how to prepare PostgreSQL database for connecting to\"\n\" Tigase server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:8\nmsgid \"\"\n\"The PostgreSQL database can be prepared in many ways. Below are presented\"\n\" two possible ways. The following assumptions apply to both methods:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:10\nmsgid \"``admin_db_user`` - database user with admin rights\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:12\nmsgid \"``tigase_user`` - database user for Tigase\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:14\nmsgid \"``tigasedb`` - database for Tigase\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:17\nmsgid \"Configuring from PostgreSQL Command Line Tool\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:19\nmsgid \"Run the PostgreSQL command line client and enter following instructions:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:21\nmsgid \"Add the ``tigase_user``:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:27\nmsgid \"\"\n\"Next, Create the database for the Tigase server with ``tigase_user`` as \"\n\"owner of database:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:35\nmsgid \"\"\n\"Load database schema to initialize the Tigase server from the file that \"\n\"corresponds to the version of Tigase you want to use. First you need to \"\n\"switch to ``tigasedb``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:41\nmsgid \"Begin by applying the basic Schema\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:47\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:100\nmsgid \"Continue by adding the schema files listed below:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:74\nmsgid \"Follow steps below to prepare the PostgreSQL database:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:76\nmsgid \"First, add the ``tigase_user``:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:82\nmsgid \"\"\n\"You will be asked for credentials for admin_db_user and password for new \"\n\"database user.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:84\nmsgid \"\"\n\"Create the database for the Tigase server with tigase_user as owner of \"\n\"database:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:90\nmsgid \"Database Schema Installation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:92\nmsgid \"Load database schema to initialize the Tigase server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:126\nmsgid \"\"\n\"The above commands should be executed from the main Tigase directory. The\"\n\" initialization schema file should be also available locally in database/\"\n\" directory of your Tigase installation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:4\nmsgid \"Preparing Tigase for MongoDB\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:6\nmsgid \"\"\n\"Tigase now supports MongoDB for auth, settings, and storage repositories.\"\n\" If you wish to use MongoDB for Tigase, please use this guide to help \"\n\"you.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:9\nmsgid \"Dependencies\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:11\nmsgid \"\"\n\"To run Tigase MongoDB support library requires drivers for MongoDB for \"\n\"Java which can be downloaded from `here <https://github.com/mongodb\"\n\"/mongo-java-driver/releases>`__. This driver needs to be placed in \"\n\"``jars/`` directory located in Tigase XMPP Server installation directory.\"\n\" If you are using a dist-max distribution, it is already included.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:14\nmsgid \"Configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:16\nmsgid \"\"\n\"Note that fresh installations of MongoDB do not come with users or \"\n\"databases installed. Once you have setup MongoDB you will need to create \"\n\"a user to be used with Tigase. To do this, bring up the mongo console by \"\n\"running mongo.exe in a cmd window for windows, or run mongo in linux. \"\n\"Once connected, enter then following:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:28\nmsgid \"\"\n\"Be sure to give this user a ``root`` role in order to properly write to \"\n\"the database. Once you receive a ``user successfully created`` message, \"\n\"you are ready to install tigase on MongoDB.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:30\nmsgid \"Configuration of user repository for Tigase XMPP Server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:32\nmsgid \"\"\n\"To configure Tigase XMPP Server to use MongoDB you need to set \"\n\"``dataSource`` in etc/config.tdsl file to proper MongoDB URI pointing to \"\n\"which MongoDB database should be used (it will be created by MongoDB if \"\n\"it does not exist). ``userRepository`` property should not be set to let \"\n\"Tigase XMPP Server auto-detect proper implementation of \"\n\"``UserRepository``. Tigase XMPP Server will create proper collections in \"\n\"MongoDB if they do not exist so no schema files are necessary.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:34\nmsgid \"\"\n\"Example configuration of XMPP Server pointing to MongoDB database \"\n\"``tigase_test`` in a local instance:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:50\nmsgid \"\"\n\"If Tigase Server is not able to detect a proper storage layer \"\n\"implementation, it can be forced to use one provided by Tigase using the \"\n\"following lines in ``etc/config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:65\nmsgid \"\"\n\"Every component should be able to use proper implementation to support \"\n\"MongoDB using this URI. Also MongoDB URI can be passed as any URI in \"\n\"configuration of any component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:67\nmsgid \"Configuration for MUC\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:69\nmsgid \"\"\n\"By default, MUC component will use MongoDB to store data if Tigase is \"\n\"configured to use it as a default store. However, if you would like to \"\n\"use a different MongoDB database to store MUC message archive, you can do\"\n\" this by adding the following lines to ``etc/config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:77\nmsgid \"\"\n\"If MUC components fails to detect and use a proper storage layer for \"\n\"MongoDB, you can force it to use one provided by Tigase by using the \"\n\"following line in the ``config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:86\nmsgid \"Configuration for PubSub\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:88\nmsgid \"\"\n\"By default, PubSub component will use MongoDB to store data if Tigase is \"\n\"configured to use it as a default store. However, if you would like to \"\n\"use a different MongoDB database to store PubSub component data, you can \"\n\"do this by adding the following lines to ``etc/config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:96\nmsgid \"\"\n\"If the PubSub components fails to detect and use a proper storage layer \"\n\"for MongoDB, you can force it to use one provided by Tigase by using the \"\n\"following line in the ``config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:105\nmsgid \"Configuration for Message Archiving\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:107\nmsgid \"\"\n\"By default, the Message Archiving component will use MongoDB to store \"\n\"data if Tigase is configured to use it as a default store. However, if \"\n\"you would like to use a different MongoDB database to store message \"\n\"archives, you can do this by adding the following lines to \"\n\"``etc/config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:115\nmsgid \"\"\n\"If Message Archiving component fails to detect and use a proper storage \"\n\"layer for MongoDB, you can force it to use one provided by Tigase by \"\n\"using the following line in the ``config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:125\nmsgid \"Schema Description\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:127\nmsgid \"\"\n\"This description contains only basic description of schema and only basic\"\n\" part of it. More collections may be created if additional components of \"\n\"Tigase XMPP Server are loaded and configured to use MongoDB.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:130\nmsgid \"Tigase XMPP Server Schema\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:132\nmsgid \"\"\n\"Basic schema for UserRespository and AuthRepository consists of two \"\n\"collections: . tig_users - contains list of users . tig_nodes - contains \"\n\"data related to users in tree-like way\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:134\nmsgid \"``tig_users`` collection contains the following fields:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:136\nmsgid \"Table 9. tig_users\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:139\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:155\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:173\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:195\nmsgid \"Name\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:139\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:155\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:173\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:195\nmsgid \"Description\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:141\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:157\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:197\nmsgid \"\\\\_id\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:141\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:159\nmsgid \"id of user which is SHA256 hash of users jid (raw byte array).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:143\nmsgid \"user_id\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:143\nmsgid \"contains full user jid.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:145\nmsgid \"domain\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:145\nmsgid \"domain to which user belongs for easier lookup of users by domain.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:147\nmsgid \"password\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:147\nmsgid \"password of user (will be removed after upgrade to 8.0.0).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:150\nmsgid \"``tig_nodes`` collection contains the following fields\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:152\nmsgid \"Table 10. tig_nodes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:157\nmsgid \"id of row auto-generated by MongoDB.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:159\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:199\nmsgid \"uid\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:161\nmsgid \"node\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:161\nmsgid \"full path of node in tree-like structure separated by / (may not exist).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:163\nmsgid \"key\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:163\nmsgid \"key for which value for node is set.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:165\nmsgid \"value\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:165\nmsgid \"value which is set for node key.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:168\nmsgid \"\"\n\"Tigase XMPP Server also uses additional collections for storage of \"\n\"Offline Messages\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:170\nmsgid \"Table 11. msg_history collection\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:175\nmsgid \"from\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:175\nmsgid \"full user jid of message sender.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:177\nmsgid \"from_hash\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:177\nmsgid \"SHA256 hash of message sender jid as raw byte array.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:179\nmsgid \"to\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:179\nmsgid \"full users jid of message recipient.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:181\nmsgid \"to_hash\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:181\nmsgid \"SHA256 hash of message recipient full jid as raw byte array.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:183\nmsgid \"ts\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:183\nmsgid \"timestamp of message as date.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:185\nmsgid \"message\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:185\nmsgid \"serialized XML stanza containing message.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:187\nmsgid \"expire-at\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:187\nmsgid \"\"\n\"timestamp of expiration of message (if message contains AMP expire-at \"\n\"set).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:190\nmsgid \"\"\n\"Due to changes in authentication and credentials storage in \"\n\"AuthRepository, we moved ``password`` field from ``tig_users`` collection\"\n\" to a newly created collection called ``tig_user_credentials``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:192\nmsgid \"This new collection has following fields:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:197\nmsgid \"id of document automatically generated by MongoDB\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:199\nmsgid \"SHA256 hash of a user for which credentails are stored\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:201\nmsgid \"username\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:201\nmsgid \"username provided during authentication (or ``default``)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:203\nmsgid \"account_status\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:203\nmsgid \"\"\n\"name of an account state (copy of value stored in user document \"\n\"from`tig_users`)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:206\nmsgid \"\"\n\"Additionally for each mechanism we store separate field in this object, \"\n\"so for:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:208\nmsgid \"``PLAIN`` we have ``PLAIN`` field with value for this mechanism\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:210\nmsgid \"\"\n\"``SCRAM-SHA-1`` we have ``SCRAM-SHA-1`` field with value for this \"\n\"mechanism\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:212\nmsgid \"etc…​\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:214\nmsgid \"\"\n\"Upgrade is not done in one step, and rather will be done once a \"\n\"particular user will log in. During authentication if there is no data in\"\n\" ``tig_user_credentials``, Tigase XMPP Server will check if ``password`` \"\n\"field in ``tig_user`` exists. If it does, and it is filled credentials \"\n\"will be migrated to the new collection.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:4\nmsgid \"Hashed User Passwords in Database\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:8\nmsgid \"\"\n\"This feature is still available, but passwords are stored encrypted by \"\n\"default since v8.0.0. We do not recommend using these settings.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:10\nmsgid \"\"\n\"By default, user passwords are stored in plain-text in the Tigase’s \"\n\"database. However, there is an easy way to have them encoded in either \"\n\"one of already supported ways or to even add a new encoding algorithm on \"\n\"your own.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:12\nmsgid \"\"\n\"Storing passwords in hashed format in the database makes it possible to \"\n\"avoid using a plain-text password authentication mechanism. You cannot \"\n\"have hashed passwords in the database and non-plain-text password \"\n\"authentication. On the other hand, the connection between the server and \"\n\"the client is almost always secured by SSL/TLS so the plain-text password\"\n\" authentication method is perhaps less of a problem than storing plain-\"\n\"text passwords in the database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:14\nmsgid \"Nevertheless, it is simple enough to adjust this in Tigase’s database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:17\nmsgid \"Shortcut\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:19\nmsgid \"\"\n\"Connect to your database from a command line and execute following \"\n\"statement for MySQL database:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:25\nmsgid \"Where encoding mode is one of the following:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:27\nmsgid \"\"\n\"``MD5-PASSWORD`` the database stores MD5 hash code from the user’s \"\n\"password.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:29\nmsgid \"\"\n\"``MD5-USERID-PASSWORD`` the database stores MD5 hash code from \"\n\"concatenated user’s bare JID and password.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:31\nmsgid \"\"\n\"``MD5-USERNAME-PASSWORD`` the database stores MD5 hash code from \"\n\"concatenated user’s name (localpart) and password.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:33\nmsgid \"For example:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:40\nmsgid \"Full Route\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:42\nmsgid \"\"\n\"The way passwords are stored in the DB is controlled by Tigase database \"\n\"schema property. Properties in the database schema can be set by a stored\"\n\" procedure called: ``TigPutDBProperty(key, value)``. Properties from the \"\n\"DB schema can be retrieved using another stored function called: \"\n\"``TigGetDBProperty(key)``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:44\nmsgid \"\"\n\"The simplest way to call them is via command-line interface to the \"\n\"database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:46\nmsgid \"\"\n\"For the purpose of this guide let’s say we have a MySQL database and a \"\n\"test account: ``test@example.com`` with password ``test77``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:48\nmsgid \"\"\n\"By default, most of DB actions for Tigase, are performed using stored \"\n\"procedures including user authentication. So, the first thing to do is to\"\n\" make sure the stored procedures are working correctly.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:51\nmsgid \"Create a Test User Account\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:53\nmsgid \"\"\n\"To add a new user account we use a stored procedure: \"\n\"``TigAddUserPlainPw(bareJid, password)``. As you can see there is this \"\n\"strange appendix to the procedure name: ``PlainPw``. This procedure \"\n\"accepts plain passwords regardless how it is stored in the database. So \"\n\"it is safe and easy to use either for plain-text passwords or hashed in \"\n\"the DB. There are also versions of procedures without this appendix but \"\n\"they are sensitive on the data format and always have to pass password in\"\n\" the exact format it is stored in the database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:55\nmsgid \"So, let’s add a new user account:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:61\nmsgid \"\"\n\"If the result was 'Query OK', then it means the user account has been \"\n\"successfully created.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:64\nmsgid \"Test User Authentication\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:66\nmsgid \"We can now test user authentication:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:72\nmsgid \"If authentication was successful the result looks like this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:85\nmsgid \"If authentication was unsuccessful, the result looks like this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:99\nmsgid \"Password Encoding Check\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:101\nmsgid \"\"\n\"``TigGetDBProperty`` is a function, not a procedure in MySQL database so \"\n\"we have to use select to call it:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:107\nmsgid \"Most likely output is this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:118\nmsgid \"\"\n\"Which means a default password encoding is used, in plain-text and thus \"\n\"no encoding. And we can actually check this in the database directly:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:124\nmsgid \"And expected result with plain-text password format would be:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:136\nmsgid \"Password Encoding Change\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:138\nmsgid \"Now let’s set password encoding to MD5 hash:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:144\nmsgid \"\"\n\"'Query OK', means the password encoding has been successfully changed. Of\"\n\" course we changed the property only. All the existing passwords in the \"\n\"database are still in plain-text format. Therefore we expect that attempt\"\n\" to authenticate the user would fail:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:158\nmsgid \"We can fix this by updating the user’s password in the database:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:2\nmsgid \"Tigase Server and Multiple Databases\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:4\nmsgid \"\"\n\"Splitting user authentication data from all other XMPP information such \"\n\"as roster, vcards, etc…​ was almost always possible in Tigase XMPP \"\n\"Server. Possible and quite simple thing to configure. Also it has been \"\n\"always possible and easy to assign a different database for each Tigase \"\n\"component (MUC, PubSub, AMP), for recording the server statistics. Almost\"\n\" every data type or component can store information in a different \"\n\"location, simple and easy to setup through the configuration file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:6\nmsgid \"\"\n\"However it is much less known that it is also possible to have a \"\n\"different database for each virtual domain. This applies to both the user\"\n\" repository and authentication repository. This allows for very \"\n\"interesting configuration such as user database sharing where each shard \"\n\"keeps users for a specific domain, or physically split data based on \"\n\"virtual domain if each domain refers to a different customer or group of \"\n\"people.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:8\nmsgid \"How can we do that then?\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:10\nmsgid \"This is very easy to do through the Tigase’s configuration file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:32\nmsgid \"\"\n\"This configuration defines just a default databases for both user \"\n\"repository and authentication repository. Default means it is used when \"\n\"there is no repository specified for a particular virtual domain. \"\n\"However, you can have a separate, both user and authentication repository\"\n\" for each virtual domain.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:34\nmsgid \"Here is, how it works:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:36\nmsgid \"First, let’s define our default database for all VHosts\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:58\nmsgid \"\"\n\"Now, we have VHost: domain1.com User authentication data for this VHost \"\n\"is stored in Drupal database\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:74\nmsgid \"All other user data is stored in Tigase’s standard database in MySQL\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:87\nmsgid \"\"\n\"Next VHost: domain2.com User authentication is in LDAP server but all \"\n\"other user data is stored in Tigase’s standard database\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:99\nmsgid \"\"\n\"Now is something new, we have a custom authentication repository and \"\n\"separate user settings for a single domain. Please note how we define the\"\n\" VHost for which we set custom parameters\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:109\nmsgid \"All other user data is stored in the same as default repository\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:122\nmsgid \"When combined, the DSL output should look like this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:142\nmsgid \"\"\n\"Next VHost: domain3.com Again user authentication is in LDAP server but \"\n\"pointing to a different LDAP server with different access credentials and\"\n\" parameters. User information is stored in a postgreSQL database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:162\nmsgid \"\"\n\"For VHost: domain4.com all the data, both authentication and user XMPP \"\n\"data are stored on a separate MySQL server with custom stored procedures \"\n\"for both user login and user logout processing.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:183\nmsgid \"\"\n\"As you can see, it requires some writing but flexibility is very \"\n\"extensive and you can setup as many separate databases as you need or \"\n\"want. If one database (recognized by the database connection string) is \"\n\"shared among different VHosts, Tigase still uses a single connection \"\n\"pool, so it won’t create an excessive number of connections to the \"\n\"database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Importing_User_Data.inc:2\nmsgid \"Importing User Data\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Importing_User_Data.inc:4\nmsgid \"\"\n\"You can easily copy data between Tigase compatible repositories that is \"\n\"repositories for which there is a database connector. However, it is not \"\n\"that easy to import data from an external source. Therefore a simple data\"\n\" import functionality has been added to repository utilities package.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Importing_User_Data.inc:6\nmsgid \"\"\n\"You can access repository utilities through command ``./bin/repo.sh`` or \"\n\"``./scripts/repo.sh`` depending on whether you use a binary package or \"\n\"source distribution.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Importing_User_Data.inc:8\nmsgid \"``-h`` parameter gives you a list of all possible parameters:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Importing_User_Data.inc:46\nmsgid \"\"\n\"The most critical parameters are the source repository class name and the\"\n\" initialization string. Therefore there are a few example preset \"\n\"parameters which you can use and adjust for your system. If you look \"\n\"inside the ``repo.sh`` script you can find at the end of the script \"\n\"following lines:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Importing_User_Data.inc:56\nmsgid \"\"\n\"You can see that the source repository has been set to MySQL database \"\n\"with ``tigase`` as the database name, ``root`` the database user and \"\n\"``mypass`` the user password.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Importing_User_Data.inc:58\nmsgid \"You can adjust these settings for your system.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Importing_User_Data.inc:60\nmsgid \"Now to import data to your repository simply execute the command:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Importing_User_Data.inc:66\nmsgid \"*Note, the import function is available from* **b895**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Importing_User_Data.inc:68\nmsgid \"\"\n\"The format of the import file is very simple. This is a flat file with \"\n\"comma separated values:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Importing_User_Data.inc:74\nmsgid \"\"\n\"To create such a file from MySQL database you will have to execute a \"\n\"command like this one:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:2\nmsgid \"Importing Existing Data\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:4\nmsgid \"Information about importing user data from other databases.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:7\nmsgid \"Connecting the Tigase Server to MySQL Database\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:9\nmsgid \"\"\n\"Please before continuing reading of this manual have a look at the \"\n\":ref:`initial MySQL database setup<Prepare-the-MySQL-Database-for-the-\"\n\"Tigase-Server>`. It will help you with database preparation for \"\n\"connecting with Tigase server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:11\nmsgid \"This guide describes MySQL database connection parameters.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:13\nmsgid \"\"\n\"This guide is actually very short as there are example configuration \"\n\"files which can be used and customized for your environment.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:29\nmsgid \"\"\n\"This is the basic setup for setting up an SQL repository for Tigase. \"\n\"dataSource contains the uri for ``default`` which is the mysql database. \"\n\"MySQL connector requires connection string in the following format: \"\n\"``jdbc:mysql://[hostname]/[database name]?user=[user name]&password=[user\"\n\" password]``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:31\nmsgid \"Edit the ``config.tdsl`` file for your environment.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:33\nmsgid \"Start the server using following command:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:40\nmsgid \"Integrating Tigase Server with Drupal\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:42\nmsgid \"\"\n\"First of all, Tigase can authenticate users against a Drupal database \"\n\"which means you have the same user account for both Drupal website and \"\n\"the XMPP server. Moreover in such a configuration all account management \"\n\"is done via Drupal web interface like account creation, password change \"\n\"update user details and so on. Administrator can temporarily disable user\"\n\" account and this is followed by Tigase server too.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:45\nmsgid \"Connecting to Drupal Database\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:47\nmsgid \"\"\n\"The best way to setup Tigase with Drupal database is via the \"\n\"``config.tdsl`` file where you can put initial setting for Tigase \"\n\"configuration.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:49\nmsgid \"\"\n\"If you look in ``etc/`` directory of your Tigase installation you should \"\n\"find a the file there.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:51\nmsgid \"All you need to connect to Drupal database is set the following:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:67\nmsgid \"\"\n\"Typically, you will need to have drupal for authentication, and another \"\n\"for user repository. In this case, we will use SQL for user DB.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:89\nmsgid \"\"\n\"In theory you can load Tigase database schema to Drupal database and then\"\n\" both ``db-uris`` would have the same database connection string. More \"\n\"details about setting up and connecting to MySQL database can be found in\"\n\" the :ref:`MySQL guide<Prepare-the-MySQL-Database-for-the-Tigase-\"\n\"Server>`.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:91\nmsgid \"Now run the Tigase server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:97\nmsgid \"\"\n\"Now you can register an account on your Drupal website and connect with \"\n\"an XMPP client using the account details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:101\nmsgid \"\"\n\"You have to enable plain password authentication in your XMPP client to \"\n\"connect to Tigase server with Drupal database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:104\nmsgid \"PostgreSQL Database Use\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:106\nmsgid \"\"\n\"This guide describes how to configure Tigase server to use `PostgreSQL \"\n\"<http://www.postgresql.org/>`__ database as a user repository.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:108\nmsgid \"\"\n\"If you used an XML based user repository before you can copy all user \"\n\"data to PostgreSQL database using repository management tool. All steps \"\n\"are described below.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:111\nmsgid \"PostgreSQL Database Preparation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:113\nmsgid \"\"\n\"Create new database user account which will be used to connect to your \"\n\"database:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:122\nmsgid \"Now using new database user account create database for your service:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:129\nmsgid \"Now you can load the database schema:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:135\nmsgid \"Now the database is ready for Tigase server to use.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:138\nmsgid \"Server Configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:140\nmsgid \"\"\n\"Server configuration is almost identical to MySQL database setup. The \"\n\"only difference is the connection string which usually looks like:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:2\nmsgid \"Schema Updates\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:4\nmsgid \"\"\n\"This is a repository for Schema updates in case you have to upgrade from \"\n\"older installations.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:6\nmsgid \"\"\n\"`Tigase Server Schema v7.1 Updates <#tigaseServer71>`__ Applies to v7.1.0\"\n\" and v8.0.0\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:9\nmsgid \"Changes to Schema in v8.0.0\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:11\nmsgid \"\"\n\"For version 8.0.0 of Tigase XMPP Server, we decided to improve \"\n\"authentication and security that was provided. In order to do this, \"\n\"implementation of repository and database schemas needed to be changed to\"\n\" achieve this goal. This document, as well one in the HTTP API, will \"\n\"describe the changes to the schemas in this new version.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:14\nmsgid \"Reasons\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:16\nmsgid \"\"\n\"Before version 8.0.0, user passwords were stored in plaintext in \"\n\"``user_pw`` database field within ``tig_users`` table, but in plaintext. \"\n\"It was possible to enable storage of the MD5 hash of the password \"\n\"instead, however this limited authentication mechanism SASL PLAIN only. \"\n\"However an MD5 hash of a password is not really a secure method as it is \"\n\"possible to revert this mechanism using rainbow tables.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:18\nmsgid \"\"\n\"Therefore, we decided to change this and store only encrypted versions of\"\n\" a password in ``PBKDF2`` form which can be easily used for ``SCRAM-\"\n\"SHA-1`` authentication mechanism or ``SCRAM-SHA-256``. SASL PLAIN \"\n\"mechanism can also used these encrypted passwords. The storage of \"\n\"encrypted passwords is now enabled **by default** in v8.0.0 of Tigase.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:21\nmsgid \"Summary of changes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:23\nmsgid \"Added support for storage of encrypted password\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:25\nmsgid \"Passwords are no longer stored in plaintext on any database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:27\nmsgid \"Using same salt for any subsequent authentications\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:29\nmsgid \"\"\n\"This allows clients to reuse calculated credentials and keep them instead\"\n\" of storing plaintext passwords.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:31\nmsgid \"Disabled usage of stored procedure for authentication\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:33\nmsgid \"\"\n\"In previous versions, Tigase used stored procedures \"\n\"``TigUserLoginPlainPw`` and ``TigUserLogin`` for SASL PLAIN \"\n\"authentication. From version 8.0.0, those procedures are no longer used, \"\n\"but they are updated to use passwords stored in ``tig_user_credentials`` \"\n\"table.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:35\nmsgid \"\"\n\"It is still possible to use this procedures for authentication, but to do\"\n\" that you need add:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:41\nmsgid \"to configuration block of **every** authentication repository.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:43\nmsgid \"\"\n\"To enable this for default repository, the ``authRepository`` \"\n\"configuration block will look like this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:54\nmsgid \"Deprecated API\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:56\nmsgid \"\"\n\"Some methods of ``AuthRepository`` API were deprecated and should not be \"\n\"used. Most of them were used for authentication using stored procedures, \"\n\"retrieval of password in plaintext or for password change.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:58\nmsgid \"\"\n\"For most of these methods, new versions based on ``tig_user_credentials``\"\n\" table and user credentials storage are provided where possible.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:60\nmsgid \"Deprecated storage procedures\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:62\nmsgid \"\"\n\"Stored procedures for authentication and password manipulation were \"\n\"updated to a new form, so that will be possible to use them by older \"\n\"versions of Tigase XMPP Server during rolling updates of a cluster. \"\n\"However, these procedures will not be used any more and will be \"\n\"depreciated and removed in future versions of Tigase XMPP Server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:64\nmsgid \"Usage of MD5 hashes of passwords\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:66\nmsgid \"\"\n\"If you have changed ``password-encoding`` database property in previous \"\n\"versions of Tigase XMPP Server, then you will need to modify your \"\n\"configuration to keep it working. If you wish only to allow access using \"\n\"old passwords and to store changed passwords in the new form, then you \"\n\"need to enable credentials decoder for the correct authentication \"\n\"repository. In this example we will provided changes required for \"\n\"``MD5-PASSWORD`` value of ``password-encoding`` database property. If you\"\n\" have used a different one, then just replace ``MD5-PASSWORD`` with ``MD5\"\n\"-USERNAME-PASSWORD`` or ``MD5-USERID-PASSWORD``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:68\nmsgid \"**Usage of MD5 decoder.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:80\nmsgid \"\"\n\"If you wish to store passwords in MD5 form then use following entries in \"\n\"your configuration file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:82\nmsgid \"**Usage of MD5 encoder.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:95\nmsgid \"Enabling and disabling credentials encoders/decoders\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:97\nmsgid \"\"\n\"You may enable which encoders and decoders used on your installation. By \"\n\"enabling encoders/decoders you are deciding in what form the password is \"\n\"stored in the database. Those changes may impact which SASL mechanisms \"\n\"may be allowed to use on your installation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:99\nmsgid \"**Enabling PLAIN decoder.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:111\nmsgid \"**Disabling SCRAM-SHA-1 encoder.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:126\nmsgid \"\"\n\"It is strongly recommended not to disable encoders if you have enabled \"\n\"decoder of the same type as it may lead to the authentication issues, if \"\n\"client tries to use a mechanism which that is not available.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:128\nmsgid \"Schema changes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:130\nmsgid \"\"\n\"This change resulted in a creation of the new table \"\n\"``tig_user_credentials`` with following fields:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:133\nmsgid \"**uid**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:133\nmsgid \"id of a user row in ``tig_users``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:136\nmsgid \"**username**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:136\nmsgid \"\"\n\"username used for authentication (if ``authzid`` is not provided or \"\n\"``authzid`` localpart is equal to ``authcid`` then row with ``default`` \"\n\"value will be used).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:139\nmsgid \"**mechanism**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:139\nmsgid \"\"\n\"name of mechanism for which this credentials will be used, ie. ``SCRAM-\"\n\"SHA-1`` or ``PLAIN``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:142\nmsgid \"**value**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:142\nmsgid \"serialized value required for mechanism to confirm that credentials match.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:146\nmsgid \"\"\n\"During execution of ``upgrade-schema`` task, passwords will be removed \"\n\"from ``tig_users`` table from ``user_pw`` field and moved to \"\n\"``tig_user_credentials`` table.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:148\nmsgid \"Added password reset mechanism\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:150\nmsgid \"\"\n\"As a part of Tigase HTTP API component and Tigase Extras, we developed a \"\n\"mechanism which allows user to reset their password. To use this \"\n\"mechanism HTTP API component and its REST module **must** to be enabled \"\n\"on Tigase XMPP Server installation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:154\nmsgid \"\"\n\"Additionally this mechanism need to be enabled in the configuration file.\"\n\" For more information about configuration of this mechanism please check \"\n\"Tigase HTTP API component documentation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:156\nmsgid \"\"\n\"Assuming that HTTP API component is configured to run on port 8080 \"\n\"*(default)*, then after accessing address \"\n\"http://localhost:8080/rest/user/resetPassword in the web browser it will \"\n\"present a web form. By filling and submitting this form, the user will \"\n\"initiate a password reset process. During this process, Tigase XMPP \"\n\"Server will send an email to the user’s email address (provided during \"\n\"registration) with a link to the password change form.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:159\nmsgid \"Upgrading from v7.1.x\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:161\nmsgid \"\"\n\"When upgrading from previous versions of Tigase, it is recommended that \"\n\"you first backup the database. Refer to the documentation of your \"\n\"database software to find out how to export a copy. Once the backup is \"\n\"made, it will be time to run the schema upgrade. Be sure that your schema\"\n\" is up to date, and should be v7.1.0 Schema.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:163\nmsgid \"To upgrade, use the new ``upgrade-schema`` task of SchemaManager:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:165\nmsgid \"In linux\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:171\nmsgid \"In Windows\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:177\nmsgid \"You will need to configure the following switches:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"``-T`` Specifies Database Type\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"\"\n\"Possible values are: ``mysql``, ``derby``, ``sqlserver``, ``postgresql``,\"\n\" ``mongodb``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"``-D`` Specifies Databse Name\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"The explicit name of the database you wish to upgrade.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"``-H`` Specifies Host address\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"\"\n\"By default, this is localhost, but may be set to IP address or FQDNS \"\n\"address.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"``-U`` Specifies Username\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"\"\n\"This is the username that is authorized to make changes to the database \"\n\"defined in -D.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"``-P`` Specifies Password\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"The password for username specified in -U.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:194\nmsgid \"``-R`` Password for Administrator or Root DB account.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:196\nmsgid \"``-A`` Password for Administrator or Root DB account.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:198\nmsgid \"``-J`` Jid of user authorized as admin user from Tigase.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:200\nmsgid \"``-N`` Password for user specified in -J.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"``-F`` Points to the file that will perform the upgrade.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"Will follow this form database/{dbname}-server-schema-8.0.0.sql\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:206\nmsgid \"Tigase Server Schema v7.2 Updates\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:209\nmsgid \"**FOR ALL USERS UPGRADING TO v8.0.0 FROM A v7.0.2 INSTALLATION**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:211\nmsgid \"\"\n\"The schema has changed for the main database, and the pubsub repository. \"\n\"In order to upgrade to the new schemas, you will need to do the \"\n\"following:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:213\nmsgid \"\"\n\"Upgrade the Main database schema to v7.1 using the ``database/${DB_TYPE\"\n\"}-schema-upgrade-to-7-1.sql`` file\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:215\nmsgid \"\"\n\"Upgrade the Pubsub Schema to v3.1.0 using the ``database/${DB_TYPE\"\n\"}-pubsub-schema-3.1.0.sql`` file\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:217\nmsgid \"\"\n\"Upgrade the Pubsub Schema to v3.2.0 using the ``database/${DB_TYPE\"\n\"}-pubsub-schema-3.2.0.sql`` file\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:219\nmsgid \"\"\n\"Upgrade the Pubsub Schema to v3.3.0 using the ``database/${DB_TYPE\"\n\"}-pubsub-schema-3.3.0.sql`` file\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:221\nmsgid \"\"\n\"All three commands may be done at the same time in that order, it is \"\n\"suggested you make a backup of your current database to prevent data \"\n\"loss.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:224\nmsgid \"Tigase Schema Change for v7.1\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:226\nmsgid \"\"\n\"Tigase has made changes to its database to include primary keys in the \"\n\"tig_pairs table to improve performance of the Tigase server. This is an \"\n\"auto-incremented column for Primary Key items appended to the previous \"\n\"schema.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:230\nmsgid \"\"\n\"**You MUST update your database to be compliant with the new schema. If \"\n\"you do not, Tigase will not function properly.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:234\nmsgid \"*This change will affect all users of Tigase using v7.1.0 and newer.*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:236\nmsgid \"\"\n\"If you are installing a new version of v8.0.0 on a new database, the \"\n\"schema should automatically be installed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:238\nmsgid \"\"\n\"First, shut down any running instances of Tigase to prevent conflicts \"\n\"with database editing. Then from command line use the DBSchemaLoader \"\n\"class to run the -schema-upgrade-to-7.1.sql file to the database. The \"\n\"command is as follows:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:240\nmsgid \"In a linux environment\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:246\nmsgid \"In a windows environment\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:252\nmsgid \"All variables will be required, they are as follows:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:254\nmsgid \"``${HOSTNAME}`` - Hostname of the database you wish to upgrade.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:256\nmsgid \"``${DB_TYPE}`` - Type of database [derby, mysql, postgresql, sqlserver].\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:258\nmsgid \"``${ROOT_USER}`` - Username of root user.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:260\nmsgid \"``${ROOT_USER_PASS}`` - Password of specified root user.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:262\nmsgid \"``${DB_USER}`` - Login of user that can edit database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:264\nmsgid \"``${DB_USER_PASS}`` - Password of the specified user.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:266\nmsgid \"``${DB_NAME}`` - Name of the database to be edited.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:268\nmsgid \"``${DB_VERSION}`` - In this case, we want this to be 7.1.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:270\nmsgid \"\"\n\"``${ADMIN_JID}`` - Bare JID of a database user with admin privileges. \"\n\"Must be contained within quotation marks.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:272\nmsgid \"``${ADMIN_JID_PASS}`` - Password of associated admin JID.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:274\nmsgid \"\"\n\"Please note that the SQL file for the update will have an associated \"\n\"database with the filename. i.e. postgresql-update-to-7.1.sql for \"\n\"postgresql database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:276\nmsgid \"A finalized command will look something like this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:282\nmsgid \"\"\n\"Once this has successfully executed, you may restart you server. Watch \"\n\"logs for any db errors that may indicate an incomplete schema upgrade.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:285\nmsgid \"Changes to Pubsub Schema\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:287\nmsgid \"\"\n\"Tigase has had a change to the PubSub Schema, to upgrade to PubSub Schema\"\n\" v7.1 without having to reform your databases, use this guide to update \"\n\"your databases to be compatible with the new version of Tigase.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:291\nmsgid \"\"\n\"Current PubSub Schema is v3.3.0, you will need to repeat these \"\n\"instructions for v3.1.0, v3.2.0 and then v3.3.0 before you run Tigase \"\n\"V7.1.0.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:293\nmsgid \"\"\n\"The PubSub Schema has been streamlined for better resource use, this \"\n\"change affects all users of Tigase. To prepare your database for the new \"\n\"schema, first be sure to create a backup! Then apply the appropriate \"\n\"PubSub schema to your MySQL and it will add the new storage procedure.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:295\nmsgid \"\"\n\"All these files should be in your /database folder within Tigase, however\"\n\" if you are missing the appropriate files, use the links below and place \"\n\"them into that folder.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:297\nmsgid \"\"\n\"The MySQL schema can be found `Here <https://github.com/tigase/tigase-\"\n\"pubsub/blob/master/src/main/database/mysql-pubsub-4.1.0.sql>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:299\nmsgid \"\"\n\"The Derby schema can be found `Here <https://github.com/tigase/tigase-\"\n\"pubsub/blob/master/src/main/database/derby-pubsub-4.1.0.sql>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:301\nmsgid \"\"\n\"The PostGRESQL schema can be found `Here <https://github.com/tigase\"\n\"/tigase-pubsub/blob/master/src/main/database/postgresql-\"\n\"pubsub-4.1.0.sql>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:303\nmsgid \"\"\n\"The same files are also included in all distributions of v8.0.0 in \"\n\"[tigaseroot]/database/ . All changes to database schema are meant to be \"\n\"backward compatible.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:305\nmsgid \"\"\n\"You can use a utility in Tigase to update the schema using the following \"\n\"command from the Tigase root:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:307\nmsgid \"Linux\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:313\nmsgid \"Windows:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:321\nmsgid \"\"\n\"**Some variation may be necessary depending on how your java build uses**\"\n\" ``-cp`` **option**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:323\nmsgid \"Use the following options to customize. Options in bold are required.:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:325\nmsgid \"\"\n\"``-dbType`` database_type {derby, mysql, postgresql, sqlserver} \"\n\"(*required*)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:327\nmsgid \"``-schemaVersion`` schema version {4, 5, 5-1}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:329\nmsgid \"``-dbName`` database name (*required*)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:331\nmsgid \"``-dbHostname`` database hostname (default is localhost)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:333\nmsgid \"``-dbUser`` tigase username\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:335\nmsgid \"``-dbPass`` tigase user password\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:337\nmsgid \"``-rootUser`` database root username (*required*)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:339\nmsgid \"``-rootPass`` database root password (*required*)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:341\nmsgid \"``-file path`` to sql schema file (*required*)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:343\nmsgid \"``-query`` sql query to execute\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:345\nmsgid \"``-logLevel`` java logger Level\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:347\nmsgid \"``-adminJID`` comma separated list of admin JIDs\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:349\nmsgid \"``-adminJIDpass`` password (one for all entered JIDs\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:353\nmsgid \"Arguments take following precedent: query, file, whole schema\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:355\nmsgid \"As a result your final command should look something like this:\"\nmsgstr \"\"\n\n#~ msgid \"\"\n#~ \"Starting with v8.0.0, most of the \"\n#~ \"database tasks have been automated and\"\n#~ \" can be called using simple text, \"\n#~ \"or using interactive question and answer\"\n#~ \" style. We **DO NOT RECOMMEND** going\"\n#~ \" through manual operation, however we \"\n#~ \"have kept manual activation of different\"\n#~ \" databases to the Appendix. If you\"\n#~ \" are interested in how we manage \"\n#~ \"and update our database schemas, you \"\n#~ \"may visit the ` Schema files \"\n#~ \"maintenance <#Schema-files-maintenance>`__ \"\n#~ \"section of our Redmine installation for\"\n#~ \" more detailed information.\"\n#~ msgstr \"\"\n\n#~ msgid \"`The DBSchemaLoader Utility <#Schema-Utility>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Hashed User Passwords in Database \"\n#~ \"<#Hashed-User-Passwords-in-Database>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Support for MongoDB <#Preparing-Tigase-for-MongoDB>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Manual installtion for MySQL <#Prepare-\"\n#~ \"the-MySQL-Database-for-the-Tigase-\"\n#~ \"Server>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Manual installtion for Derby <#Prepare-\"\n#~ \"the-Derby-Database-for-the-Tigase-\"\n#~ \"Server>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Manual installtion for SQLServer \"\n#~ \"<#Prepare-the-MS-SQL-Server-Database-\"\n#~ \"for-the-Tigase-Server>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Manual installtion for PostGRESQL \"\n#~ \"<#Prepare-the-PostgreSQL-Database-for-\"\n#~ \"the-Tigase-Server>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"see `Period / Duration values \"\n#~ \"<#Period-Duration-values>`__ for format \"\n#~ \"details\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"``-Q``, ``--query=`` - custom queries to\"\n#~ \" be executed, see `Query function \"\n#~ \"<#queryschema>`__ for details.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"Please before continuing reading of this\"\n#~ \" manual have a look at the \"\n#~ \"`initial MySQL database setup \"\n#~ \"<#prepareMysql>`__. It will help you \"\n#~ \"with database preparation for connecting \"\n#~ \"with Tigase server.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"In theory you can load Tigase \"\n#~ \"database schema to Drupal database and\"\n#~ \" then both ``db-uris`` would have \"\n#~ \"the same database connection string. \"\n#~ \"More details about setting up and \"\n#~ \"connecting to MySQL database can be \"\n#~ \"found in the `MySQL guide \"\n#~ \"<#prepareMysql>`__.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"You MUST update your database to \"\n#~ \"be compliant with the new schema. \"\n#~ \"If you do not, Tigase will not \"\n#~ \"function properly.**\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"**Some variation may be necessary \"\n#~ \"depending on how your java build \"\n#~ \"uses ``-cp`` option**\"\n#~ msgstr \"\"\n\n#~ msgid \"JDBC: jTDS vs MS JDBC driver\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"Tigase XMPP Server supports two JDBC \"\n#~ \"drivers intended to be used with \"\n#~ \"Microsoft SQL Server - one created \"\n#~ \"and provided by Microsoft itself and \"\n#~ \"the alternative implementation - jTDS. \"\n#~ \"Tigase is shipped with the latter \"\n#~ \"in the distribution packages. Starting \"\n#~ \"with the version 7.1.0 we recommend \"\n#~ \"using jDTS driver that is shipped \"\n#~ \"with Tigase as JDBC driver created \"\n#~ \"by Microsoft can cause problems with \"\n#~ \"some components in cluster installations. \"\n#~ \"MS driver can be downloaded form \"\n#~ \"the website: `JDBC Drivers 4.0, 4.1 \"\n#~ \"for SQL Server <http://www.microsoft.com/en-\"\n#~ \"us/download/details.aspx?displaylang=en&id=11774>`__ then \"\n#~ \"unpack the archive. Copy \"\n#~ \"sqljdbc_4.0/enu/sqljdbc4.jar file to ${tigase-\"\n#~ \"server}/jars directory.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"Depending on the driver used ``uri`` \"\n#~ \"needs to be configured accordingly.\"\n#~ msgstr \"\"\n\n#~ msgid \"jDTS driver\"\n#~ msgstr \"\"\n\n#~ msgid \"5.6.4\"\n#~ msgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Administration/Licensing_Open_Source.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-05-27 12:30-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:2\nmsgid \"Licensing and Open Source\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:4\nmsgid \"\"\n\"As mentioned previously, Tigase is open source under AGPLv3. If you are \"\n\"not familiar with open source software, or the environment, here are some\"\n\" frequently asked questions that might provide some answers.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:6\nmsgid \"**What does open source mean?**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:7\nmsgid \"\"\n\"This means that Tigase’s source code is available to the public to see \"\n\"how Tigase works. There are no 'black boxes' for packets where things \"\n\"just happen, everything is out in the open, whereas other companies may \"\n\"consider this propitiatory information. In addition, we have the benefit \"\n\"of many talented people working with Tigase to constantly improve Tigase \"\n\"server and related projects. These people not only include the Tigase \"\n\"development team, but other members of the community who submit code \"\n\"improvements, patches, enhancements, or other changes to Tigase.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:9\nmsgid \"**Does this mean that the binaries are open to malicious code?**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:10\nmsgid \"\"\n\"Although we accept patches from contributors, our repository does not \"\n\"accept them directly. Code may be submitted through our `tigase.tech \"\n\"<http://tigase.tech>`__ page and our developers will review the code \"\n\"before it is added. All builds are tested for functionality and security \"\n\"when they are built.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:12\nmsgid \"**Does this mean it is less secure?**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:13\nmsgid \"\"\n\"Not at all. Although anybody can see the source code, and know how Tigase\"\n\" works; your installation, connections, and settings are uniquely yours. \"\n\"Tigase is regularly tested and written to be as secure as possible using \"\n\"the latest encryption and secure connection protocols.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:15\nmsgid \"**Is Tigase free?**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:16\nmsgid \"\"\n\"Tigase is free for download and use in it’s unmodified state. Our \"\n\"commercial grade products such as Advanced Clustering Strategy is \"\n\"available for free use for testing & development.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:18\nmsgid \"\"\n\"**Does this mean I cannot use it in my product or commercial \"\n\"environment?**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:19\nmsgid \"\"\n\"Not necessarily, consult the Affero General Public License Agreement v3 \"\n\"to see if your use qualifies. Tigase is offered under commercial license \"\n\"if your use is not covered by AGPLv3.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:21\nmsgid \"**Are there options for closed code or extensions?**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:22\nmsgid \"\"\n\"Yes! Commercial licenses can be custom made for each client, and software\"\n\" written for your company may be made private or part of our open source \"\n\"distributions at your discretion.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:24\nmsgid \"**Can I contribute code?**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:25\nmsgid \"\"\n\"Sure! We accept code through GitHub pull-requests - submit them to one of\"\n\" our projects listed in our `GitHub organisation \"\n\"<https://github.com/tigase/>`__\"\nmsgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Administration/Properties/_properties.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Administration/Properties/_properties.rst:2\nmsgid \"Appendix II - Properties Guide\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:2\nmsgid \"General\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:7\nmsgid \"admins\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:9\nmsgid \"**Description:** Specifies a list of administrator accounts.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:11\nmsgid \"\"\n\"**Default value:** the administration account created when the server is \"\n\"setup. Typically it would be something like ``admins = \"\n\"['admin@server.com']``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:13\nmsgid \"**Example:** ``admins = [ 'admin@domain.com', 'user2@domain.com' ]``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:15\nmsgid \"**Possible values:** Comma seperated values of Bare JIDs.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:17\n#: ../../Tigase_Administration/Properties/_general.inc:144\n#: ../../Tigase_Administration/Properties/_general.inc:172\nmsgid \"**Available since:** 2.0.0\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:20\nmsgid \"Certificate Container\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:22\nmsgid \"\"\n\"The certificate container houses all configuration related to SSL \"\n\"certificate configuration. This container replaces a number of former — \"\n\"properties.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:25\nmsgid \"ssl-certs-location\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:27\nmsgid \"\"\n\"This option allows you to specify the location where SSL certificates are\"\n\" stored. The meaning of this property depends on the SSL container \"\n\":ref:`class implementation<sslContainerClass>`. By default it just points\"\n\" to the directory where the server SSL certificates are stored in files \"\n\"in PEM format.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:29\nmsgid \"\"\n\"Default location is ``/certs`` however it can be changed using the \"\n\"following setting:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:38\nmsgid \"This replaces the former ``--ssl-certs-location`` property.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:41\nmsgid \"ssl-def-cert-domain\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:43\nmsgid \"\"\n\"This property allows you to specify a default alias/domain name for \"\n\"certificate. It is mostly used to load certificates for unknown domain \"\n\"names during the SSL negotiation. Unlike in TLS protocol where the domain\"\n\" name is known at the handshaking time, for SSL domain name is not known,\"\n\" therefore, the server does not know which certificate to use. Specifying\"\n\" a domain name in this property allows you to use a certificate for a \"\n\"specific domain in such case. This property value is also sometimes used \"\n\"if there is no certificate for one of virtual domains and the container \"\n\"does not automatically generate a self-signed certificate, then it can \"\n\"use a default one.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:45\nmsgid \"This may be configured as follows:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:54\nmsgid \"This replaces the former ``--ssl-def-cert-domain`` property.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:57\nmsgid \"Component\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:59\nmsgid \"\"\n\"**Description:** Container specifying component configuration. All \"\n\"components if they require configuration must be called in the conf.tdsl \"\n\"file in the following manner:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:67\nmsgid \"\"\n\"DSL allows for custom naming of the component, and specifying of the \"\n\"class in the same line. This method replaces the old ``comp-class`` and \"\n\"``comp-name`` style of configuration.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:69\nmsgid \"For example, what used to be\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:78\nmsgid \"is now\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:85\nmsgid \"\"\n\"In fact, if you are using the default class & name for a component, you \"\n\"don’t need to specify it either, so MUC in this is now called by\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:91\nmsgid \"\"\n\"**Default value:** By default, component configuration runs of default, \"\n\"and does not need to be specified.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:93\nmsgid \"\"\n\"There are many many configuration options under each component, which are\"\n\" specified in :ref:`component documentation<Components>`.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:96\nmsgid \"Ports\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:98\nmsgid \"\"\n\"The ports property is a subclass of connections, which is used to set a \"\n\"ports list for a connection manager. 'list of ports' is a comma separated\"\n\" list of ports numbers. For example for the server to server connection \"\n\"manager named s2s the property would like like the example below:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:108\nmsgid \"Each port many be individually configured underneath ports\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:121\nmsgid \"this replaces the ``--cmpname-ports`` property.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:34\n#: ../../Tigase_Administration/Properties/_cluster.inc:47\n#: ../../Tigase_Administration/Properties/_cluster.inc:68\n#: ../../Tigase_Administration/Properties/_external.inc:30\n#: ../../Tigase_Administration/Properties/_general.inc:123\n#: ../../Tigase_Administration/Properties/_general.inc:203\n#: ../../Tigase_Administration/Properties/_general.inc:280\n#: ../../Tigase_Administration/Properties/_general.inc:558\n#: ../../Tigase_Administration/Properties/_general.inc:612\n#: ../../Tigase_Administration/Properties/_general.inc:631\n#: ../../Tigase_Administration/Properties/_general.inc:652\n#: ../../Tigase_Administration/Properties/_general.inc:665\n#: ../../Tigase_Administration/Properties/_performance.inc:15\n#: ../../Tigase_Administration/Properties/_performance.inc:137\n#: ../../Tigase_Administration/Properties/_repository.inc:80\n#: ../../Tigase_Administration/Properties/_repository.inc:185\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:17\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:41\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:172\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:209\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:246\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:283\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:296\n#: ../../Tigase_Administration/Properties/_vhost.inc:19\n#: ../../Tigase_Administration/Properties/_vhost.inc:34\n#: ../../Tigase_Administration/Properties/_vhost.inc:55\n#: ../../Tigase_Administration/Properties/_vhost.inc:74\n#: ../../Tigase_Administration/Properties/_vhost.inc:95\n#: ../../Tigase_Administration/Properties/_vhost.inc:116\n#: ../../Tigase_Administration/Properties/_vhost.inc:137\nmsgid \"**Available since:** 8.0.0\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:126\nmsgid \"config-type\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:128\nmsgid \"\"\n\"**Description:** This property sets the server type and determines what \"\n\"components are started up without needing to declare and configure all \"\n\"components. Possible values are listed below:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:130\nmsgid \"\"\n\"``setup`` - This setting will setup a basic server that is prepared for \"\n\"initial setup after unpacking. This is set by default, and starts up http\"\n\" component as well as basic server components. This should be changed \"\n\"after the server is configured.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:132\nmsgid \"\"\n\"``default`` - creates default configuration file. That is configuration \"\n\"which is most likely needed for a typical installation. Components \"\n\"included in configuration are: session manager, client-to-server \"\n\"connection manager and server-to-server connection manager.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:134\nmsgid \"\"\n\"``session-manager`` - creates configuration for instance with session \"\n\"manager and external component only. This is useful for distributed \"\n\"installation where you want to have session manager installed on separate\"\n\" machine and components managing network connections on different \"\n\"machines (one or more). Components included in configuration are: sm and \"\n\"ext2s.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:136\nmsgid \"\"\n\"``connection-managers`` - creates configuration for instance with \"\n\"components managing network connections. This is useful for distributed \"\n\"installation where you want to have session manager installed on separate\"\n\" machine and components managing network connections on different \"\n\"machines (one or more). Components included in configuration are: c2s, \"\n\"s2s, ext2s.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:138\nmsgid \"\"\n\"``component`` - generating a configuration with only one component - \"\n\"component managing external components connection, either XEP-0114 or \"\n\"XEP-0225. This is used to deploy a Tigase instance as external component \"\n\"connecting to the main server. You have to add more components handled by\"\n\" this instance, usually these are MUC, PubSub or any other custom \"\n\"components. You have to configure the external component connection, \"\n\"domain name, password, port, etc…​\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:140\nmsgid \"**Default value:** ``'config-type' = 'setup'``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:142\nmsgid \"\"\n\"**Possible values:** ``setup``\\\\ \\\\|\\\\ ``default``\\\\ \\\\|\\\\ ``connection-\"\n\"managers``\\\\ \\\\|\\\\ ``session-manager``\\\\ \\\\|\\\\ ``connection-managers``\\\\ \"\n\"\\\\|\\\\ ``component``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:149\nmsgid \"debug-packages\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:151\nmsgid \"\"\n\"**Default value:** No default as Tigase does not expect custom classes \"\n\"out of the box.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:153\nmsgid \"\"\n\"**Example:** ``'debug-packages' = [ 'com.company.CustomPlugin' , \"\n\"'com.company.custom' ]``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:155\nmsgid \"**Possible values:** comma separated list of Java packages or classes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:157\nmsgid \"\"\n\"**Description:** This property is used to turn debugging on for any \"\n\"package not located within the default Tigase packages. Be sure class \"\n\"case is correct.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:19\n#: ../../Tigase_Administration/Properties/_general.inc:159\n#: ../../Tigase_Administration/Properties/_general.inc:369\n#: ../../Tigase_Administration/Properties/_performance.inc:165\nmsgid \"**Available since:** 5.0.0\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:162\nmsgid \"debug\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:164\nmsgid \"\"\n\"**Description:** The ``debug`` property is used to turn on the debug log \"\n\"for the specified Tigase package. For example if you want to turn debug \"\n\"logs on for the ``tigase.server`` package, then you have to use the \"\n\"``server`` parameter. If you have any problems with your server the best \"\n\"way to get help from the Tigase team is to generate configuration with \"\n\"this enabled at a minimum and run the server. Then from the ``logs\"\n\"/tigase-console.log`` log file I can provide the best information for us \"\n\"to provide assistance. More details about server logging and adjusting \"\n\"logging level is described in the Debugging Tigase article in the admin \"\n\"guide. If you wish to debug packages not compiled with Tigase, use the \"\n\":ref:`debug-packages<debugPackages>` setting.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:166\n#: ../../Tigase_Administration/Properties/_general.inc:236\nmsgid \"**Default value:** 'none'\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:168\nmsgid \"**Example:** ``debug = [ 'server', 'xmpp.impl' ]``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:170\nmsgid \"**Possible values:** Comma separated list of Tigase’s package names.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:175\nmsgid \"monitoring\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:177\nmsgid \"\"\n\"**Description:** This property activates monitoring interfaces through \"\n\"selected protocols on selected TCP/IP port numbers. For more details \"\n\"please refer to the :ref:`monitoring guide<serverMonitoring>` in the user\"\n\" guide for details. Each monitoring protocol should be called in it’s own\"\n\" child bean under ``monitoring ()``. If a protocol is not specified, \"\n\"monitoring under that will not be available.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:179\nmsgid \"**Default value:** By default monitoring is disabled.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:11\n#: ../../Tigase_Administration/Properties/_general.inc:181\n#: ../../Tigase_Administration/Properties/_general.inc:238\n#: ../../Tigase_Administration/Properties/_general.inc:323\n#: ../../Tigase_Administration/Properties/_general.inc:584\n#: ../../Tigase_Administration/Properties/_repository.inc:16\n#: ../../Tigase_Administration/Properties/_repository.inc:100\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:65\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:160\nmsgid \"**Example:**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:199\nmsgid \"DO NOT CONFUSE monitoring with monitor component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:201\nmsgid \"**Possible values:** 'list of monitoring protocols with port numbers.'\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:206\nmsgid \"plugins\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:208\nmsgid \"\"\n\"**Description:** The former ``--sm-plugins`` property has been replaced \"\n\"by a new style of formatting with DSL. The former long unbroken string of\"\n\" plusses and minuses have been replaced by a compartmentalized style. \"\n\"Plugins controlled under session manager will now be children of the \"\n\"``'sess-man'`` bean. For example, to turn on the personal eventing \"\n\"protocol, the following may be used:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:216\nmsgid \"\"\n\"Should any plugin require configuration, those configurations will be \"\n\"under it’s own brackets. For example, this section not only turns on \"\n\"jabber:iq:auth but also sets the treads to 16.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:226\nmsgid \"\"\n\"As you may have noticed, beans or configuration options that require \"\n\"escape characters such as ``:`` or ``-`` will fall into single quotes to \"\n\"contain any special characters. If no special characters are in the bean \"\n\"name, then no single quotes are not required. If you need to disable \"\n\"certain plugins, you can do so after declaring the bean.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:234\nmsgid \"\"\n\"Typically if a bean is called, it is automatically active. Session \"\n\"manager plugins will typically look like a list of plugins without \"\n\"configurations. The example section will show what one will look like.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:278\nmsgid \"**Possible values:** DSL format plugins list and configurations.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:283\nmsgid \"priority-queue-implementation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:285\nmsgid \"**Default value:** ``tigase.util.PriorityQueueRelaxed``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:287\nmsgid \"\"\n\"**Example:** ``'priority-queue-implementation' = \"\n\"'tigase.util.PriorityQueueStrict``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:289\nmsgid \"\"\n\"**Possible values:** class name extending \"\n\"``tigase.util.PriorityQueueAbstract``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:291\nmsgid \"\"\n\"**Description:** The ``priority-queue-implementation`` property sets \"\n\"Tigase’s internal queue implementation. You can choose between already \"\n\"available and ready to use or you can create own queue implementation and\"\n\" let Tigase load it instead of the default one. Currently following queue\"\n\" implementations are available:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:293\nmsgid \"\"\n\"**tigase.util.workqueue.PriorityQueueRelaxed** - specialized priority \"\n\"queue designed to efficiently handle very high load and prevent packets \"\n\"loss for higher priority queues. This means that sometimes, under the \"\n\"system overload packets may arrive out of order in cases when they could \"\n\"have been dropped. Packets loss (drops) can typically happen for the \"\n\"lowest priority packets (presences) under a very high load.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:295\nmsgid \"\"\n\"**tigase.util.workqueue.PriorityQueueStrict** - specialized priority \"\n\"queue designed to efficiently handle very high load but prefers packet \"\n\"loss over packet reordering. It is suitable for systems with a very high \"\n\"load where the packets order is the critical to proper system \"\n\"functioning. This means that the packets of the same priority with the \"\n\"same source and destination address are never reordered. Packets loss \"\n\"(drops) can typically happen for all packets with the same probability, \"\n\"depending which priority queue is overloaded.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:297\nmsgid \"\"\n\"**tigase.util.workqueue.NonpriorityQueue** - specialized non-priority \"\n\"queue. All packets are stored in a single physical collection, hence they\"\n\" are never reordered. Packets are not prioritized, hence system critical \"\n\"packets may have to wait for low priority packets to be processed. This \"\n\"may impact the server functioning and performance in many cases. \"\n\"Therefore this queue type should be chosen very carefully. Packets of the\"\n\" same type are never reordered. Packets loss (drops) can typically happen\"\n\" for all packets which do not fit into the single queue.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:301\nmsgid \"\"\n\"*Since the packets are processed by plugins in the SessionManager \"\n\"component and each plugin has own thread-pool with own queues packet \"\n\"reordering may happen regardless what queue type you set. The reordering \"\n\"may only happen, however between different packet types. That is \"\n\"'message' may take over 'iq' packet or 'iq' packet may take over \"\n\"'presence' packet and so on…​ This is unpredictable.*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:303\n#: ../../Tigase_Administration/Properties/_performance.inc:94\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:120\nmsgid \"**Available since:** 5.1.0\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:306\nmsgid \"roster-implementation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:308\nmsgid \"**Default value:** ``RosterFlat.class.getCanonicalName()``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:310\nmsgid \"**Example:** ``'roster-implementation' = 'my.pack.CustomRosterImpl'``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:312\nmsgid \"\"\n\"**Possible values:** Class extending \"\n\"tigase.xmpp.impl.roster.RosterAbstract.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:314\nmsgid \"\"\n\"**Description:** The ``roster-implementation`` property allows you to \"\n\"specify a different RosterAbstract implementation. This might be useful \"\n\"for a customized roster storage, extended roster content, or in some \"\n\"cases for some custom logic for certain roster elements.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:316\n#: ../../Tigase_Administration/Properties/_general.inc:341\n#: ../../Tigase_Administration/Properties/_performance.inc:64\n#: ../../Tigase_Administration/Properties/_performance.inc:81\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:56\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:151\nmsgid \"**Available since:** 5.2.0\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:319\nmsgid \"s2s-secret\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:321\n#: ../../Tigase_Administration/Properties/_general.inc:657\nmsgid \"**Default value:** ``none``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:333\nmsgid \"**Possible values:** 'ascii string.'\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:335\nmsgid \"\"\n\"**Description:** This property is a global setting for s2s secrets to \"\n\"generate dialback keys on the Tigase installation. By default it is null,\"\n\" which means the secret is automatically generated for each s2s \"\n\"connection and handshake.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:337\nmsgid \"\"\n\"This is a global property which is overridden by settings for each VHost \"\n\"(see :ref:`Add and Manage Domains (VHosts)<addManageDomain>`)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:339\nmsgid \"\"\n\"As in the example provided, 'defaults' settings for all virtual hosts for\"\n\" which the configuration is not defined. This settings is useful mostly \"\n\"for installations with many virtual hosts listed in the init.property \"\n\"file for which there is no individual settings specified. It allows to \"\n\"configure a default values for all of them, instead of having to provide \"\n\"individual configuration for each vhost.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:344\nmsgid \"scripts-dir\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:346\nmsgid \"**Default value:** ``scripts/admin``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:348\nmsgid \"**Example:** ``'scripts-dir' = ''/opt/admin-scripts'``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:350\nmsgid \"**Possible values:** path to a directory on the file system.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:352\nmsgid \"\"\n\"**Description:** This property sets the directory where all administrator\"\n\" scripts for ad-hoc commands are stored.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:49\n#: ../../Tigase_Administration/Properties/_general.inc:354\n#: ../../Tigase_Administration/Properties/_performance.inc:109\n#: ../../Tigase_Administration/Properties/_performance.inc:124\nmsgid \"**Available since:** 4.3.0\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:359\nmsgid \"ssl-container-class\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:361\nmsgid \"*Default value:** ``tigase.io.SSLContextContainer``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:363\nmsgid \"\"\n\"**Example:** ``rootSslContextContainer (class: \"\n\"class.implementing.SSLContextContainerIFC) {}``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:365\nmsgid \"\"\n\"**Possible values:** a class implementing \"\n\"tigase.io.SSLContectContainerIfc.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:367\nmsgid \"\"\n\"**Description:** The ``rootSslContextContainer`` property allows you to \"\n\"specify a class implementing storage for SSL/TLS certificates. The class \"\n\"presented in the example to this description allows for loading \"\n\"certificates from PEM files which is a common storage used on many \"\n\"systems.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:372\nmsgid \"stats\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:374\nmsgid \"\"\n\"The stats block contains settings for statistics collection. To begin the\"\n\" stats block, use the following:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:380\nmsgid \"\"\n\"**Default value:** 'By default, stats is not listed in the \"\n\"``config.tdsl`` file'\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:382\nmsgid \"**Description**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:384\nmsgid \"\"\n\"Tigase XMPP Server can store server statistics internally for a given \"\n\"period of time. This allows you to connect to a running system and \"\n\"collect all the server metrics along with historic data which are stored \"\n\"on the server. This is very useful when something happens on your \"\n\"production system you can connect and see when exactly this happened and \"\n\"what other metrics looked around this time. **Please be aware that Tigase\"\n\" XMPP Server produces about 1,000 different metrics of the system. \"\n\"Therefore caching large number of statistics sets requires lots of \"\n\"memory.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:387\nmsgid \"stats-history-size\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:389\nmsgid \"\"\n\"Stats-history defines the size of the history buffer. That is how many \"\n\"complete sets of historic metrics to store in memory.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:398\nmsgid \"stats-history-interval\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:400\nmsgid \"Sets the interval for which statistics will be gathered from the server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:410\nmsgid \"stats-logger\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:412\nmsgid \"\"\n\"Allow enabling and configuring components responsible for storing \"\n\"statistic information. Note that this controls the logging system for \"\n\"retrieving using JMX, clients, or ad-hoc commands.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:422\nmsgid \"Currently following classes are available:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:424\nmsgid \"\"\n\"``tigase.stats.CounterDataArchivizer`` - every execution put current \"\n\"basic server metrics (CPU usage, memory usage, number of user \"\n\"connections, uptime) into database (overwrites previous entry)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:426\nmsgid \"\"\n\"``tigase.stats.CounterDataLogger`` - every execution insert new row with \"\n\"new set of number of server statistics (CPU usage, memory usage, number \"\n\"of user connections per connector, number of processed packets of \"\n\"different types, uptime, etc) into the database\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:428\nmsgid \"\"\n\"``tigase.stats.CounterDataFileLogger`` - every execution store all server\"\n\" statistics into separate file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:430\n#: ../../Tigase_Administration/Properties/_general.inc:463\nmsgid \"frequency\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:432\nmsgid \"\"\n\"stats-logger may also be controlled using frequency, which is the time \"\n\"interval between executions of the archiver ``.execute()`` method in \"\n\"seconds.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:449\nmsgid \"stats-file-logger\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:451\nmsgid \"\"\n\"This allows configuring of statistics gathering to an external file. This\"\n\" only has one class, and may be controlled independently from the \"\n\"internal statistics.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:465\nmsgid \"\"\n\"stats-file-logger may also be controlled using frequency, which is the \"\n\"time interval between executions of the archiver ``.execute()`` method in\"\n\" seconds.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:476\nmsgid \"file configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:478\nmsgid \"\"\n\"You can customize the file output for stats-file-logger using the \"\n\"following setting options, these are all optional.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:494\nmsgid \"**'stats-datetime'** - Whether to include date & time timestamp.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:496\nmsgid \"\"\n\"**'stats-datetime-format'** - Specifies the formatting of datetime \"\n\"timestamp.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:498\nmsgid \"\"\n\"**'stats-directory'** - The directory to which the statistics file should\"\n\" be saved.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:500\nmsgid \"\"\n\"**'stats-filename'** - The filename prefix to name the output statistics \"\n\"file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:502\nmsgid \"**'stats-level'** - Sets the level of statistics to be gathered.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:504\nmsgid \"\"\n\"**'stats-unixtime'** - Control the format of the timestamp to use java \"\n\"DateFormat pattern.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:506\nmsgid \"\"\n\"which configures accordingly: directory to which files should be saved, \"\n\"filename prefix, whether to include or not unix timestamp in filename, \"\n\"whether to include or not datetime timestamp, control format of timestamp\"\n\" (using java DateFormat pattern) and also set level of the statistics we \"\n\"want to save (using java Logger.Level)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:509\nmsgid \"Database logger\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:511\nmsgid \"\"\n\"This allows configuring of statistics gathering to a database. Without \"\n\"additional configuration ``default`` data source will be used but it’s \"\n\"possible to store statistics in any database - simply define new data \"\n\"source and configure logger with it’s name.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:513\n#: ../../Tigase_Administration/Properties/_repository.inc:44\nmsgid \"**Note**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:515\nmsgid \"\"\n\"After enabling the component it’s database schema should be loaded by \"\n\"executing ``./scripts/tigase.sh upgrade-schema etc/tigase.conf`` from the\"\n\" main Tigase directory\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:532\nmsgid \"Example configuration block\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:563\nmsgid \"stream-error-counter\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:565\nmsgid \"\"\n\"**Description:** Add stream-error-counter to comma separated processors \"\n\"of components for which you wish to count the number of stream errors \"\n\"made. Without enabling this, statistics will return 0. This setting turns\"\n\" on stream-error-counter for both c2s and ws2s:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:580\nmsgid \"\"\n\"You may if you wish turn off stream error counters by setting ``active = \"\n\"false``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:582\nmsgid \"\"\n\"**Default value:** Stream error counters are not turned on by default, \"\n\"thus no default value is set.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:593\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:97\nmsgid \"**Available since:** 7.1.0\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:596\nmsgid \"stringprep-processor\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:598\nmsgid \"\"\n\"**Description:** The ``'stringprep-processor'`` property sets the \"\n\"stringprep processor for all JIDs handled by Tigase. The default 'simple'\"\n\" implementation uses regular expressions to parse and check the user JID.\"\n\" Although it does not fulfill the RFC-3920 requirements, it also puts \"\n\"much less stress on the server CPU, hence impact on the performance is \"\n\"very low.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:600\nmsgid \"Other possible values are:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:602\nmsgid \"\"\n\"``'libidn'`` - provides full stringprep processing exactly as described \"\n\"in the RFC-3920. It requires lots of CPU power and significantly impacts \"\n\"performance.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:604\nmsgid \"\"\n\"``'empty'`` - doesn’t do anything to JIDs. JIDs are accepted in the form \"\n\"they are received. No impact on the performance and doesn’t use any CPU. \"\n\"This is suitable for use in automated systems where JIDs are generated by\"\n\" some algorithm, hence there is no way incorrect JIDs may enter the \"\n\"system.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:606\nmsgid \"**Default value:** ``simple``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:608\nmsgid \"**Example:** ``'stringprep-processor' = 'libidn'``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:610\nmsgid \"**Possible values:** ``simple|libidn|empty``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:615\nmsgid \"test\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:617\nmsgid \"**Default value:** By default test mode is disabled.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:619\nmsgid \"\"\n\"**Description:** This property sets the server for test mode, which means\"\n\" that all logging is turned off, offline message storage is off, and \"\n\"possibly some other changes to the system configuration are being made.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:621\nmsgid \"\"\n\"The idea behind this mode is to test Tigase XMPP Server with minimal \"\n\"performance impact from environment such as hard drive, database and \"\n\"others…​\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:623\nmsgid \"Test function has been replaced by the following setting:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:634\nmsgid \"tls-jdk-nss-bug-workaround-active\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:636\n#: ../../Tigase_Administration/Properties/_performance.inc:155\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:7\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:288\n#: ../../Tigase_Administration/Properties/_vhost.inc:24\n#: ../../Tigase_Administration/Properties/_vhost.inc:121\nmsgid \"**Default value:** ``false``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:638\nmsgid \"**Example:** ``'tls-jdk-nss-bug-workaround-active' = true``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:45\n#: ../../Tigase_Administration/Properties/_general.inc:640\n#: ../../Tigase_Administration/Properties/_performance.inc:159\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:11\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:292\n#: ../../Tigase_Administration/Properties/_vhost.inc:11\n#: ../../Tigase_Administration/Properties/_vhost.inc:28\n#: ../../Tigase_Administration/Properties/_vhost.inc:104\n#: ../../Tigase_Administration/Properties/_vhost.inc:125\nmsgid \"**Possible values:** ``true|false``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:642\nmsgid \"\"\n\"**Description:** This is a workaround for TLS/SSL bug in new JDK7 using \"\n\"the native library for keys generation and connection encryption used \"\n\"with new version of nss library.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:644\nmsgid \"\"\n\"This caused a number of problems with Tigase installed on systems with \"\n\"JDK7 and the new library installed, such as hanging connections, or \"\n\"broken SSL/TLS. Our earlier suggestion was to avoid using either JDK7 or \"\n\"the problematic native library. Now we have a proper fix/workaround which\"\n\" allows you to run Tigase with JDK7.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:646\nmsgid \"http://stackoverflow.com/q/10687200/427545\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:648\nmsgid \"http://bugs.sun.com/bugdatabase/view_bug.do;jsessionid=b509d9cb5d8164d90e6731f5fc44?bug_id=6928796\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:650\nmsgid \"\"\n\"Note, while this setting is still supported, the issues mentioned above \"\n\"are fixed in v8 JDK.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:655\nmsgid \"trusted\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:659\nmsgid \"**Example:** ``trusted = [ 'user@domain.com' , 'user-2@domain2.com' ]``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:661\nmsgid \"**Possible values:** comma separated list of user bare JIDs.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:663\nmsgid \"\"\n\"**Description:** The ``trusted`` property allows users to specify a list \"\n\"of accounts which are considered as trusted, thus whom can perform some \"\n\"specific actions on the server. They can execute some commands, send a \"\n\"broadcast message, set MOTD and so on. The configuration is similar to \"\n\":ref:`admins<admins>` setting.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:2\nmsgid \"Repository\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:4\nmsgid \"\"\n\"**Description:** Container specifying authentication repository. This \"\n\"container replaces the old ``auth-db`` property types, and may contain \"\n\"some other configuration values.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:6\n#: ../../Tigase_Administration/Properties/_repository.inc:89\nmsgid \"**Default value:**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:14\nmsgid \"\"\n\"This is the basic setup for authRepository, where <configuration> \"\n\"settings are global for all authentication databases. However, you may \"\n\"configure multiple databases individually.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:35\nmsgid \"**Configuration Values:**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:37\nmsgid \"Container has the following options\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:40\n#: ../../Tigase_Administration/Properties/_repository.inc:168\nmsgid \"pool-size\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:42\nmsgid \"\"\n\"This property sets the database connections pool size for the associated \"\n\"``UserRepository``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:46\nmsgid \"\"\n\"in some cases instead of default for this property setting for :ref\"\n\":`data-repo-pool-size<dataRepoPoolSize>` is used if pool-size is not \"\n\"defined in ``userRepository``. This depends on the repository \"\n\"implementation and the way it is initialized.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:55\nmsgid \"\"\n\"This is a global property that may be overridden by individual repository\"\n\" settings:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:70\nmsgid \"**cls**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:72\nmsgid \"\"\n\"Defines the class used for repository connection. You can use this to \"\n\"specify specific drivers for different DB types.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:74\nmsgid \"\"\n\"Unless specified, the pool class will use the one included with Tigase. \"\n\"You may configure individual repositories in the same way. This replaces \"\n\"the former ``--auth-repo-pool`` property.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:78\nmsgid \"\"\n\"File conversion will not remove and convert this property, it **MUST BE \"\n\"DONE MANUALLY**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:85\nmsgid \"authRepository\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:87\nmsgid \"\"\n\"**Description:** Container specifying repository URIs. This container \"\n\"replaces the old ``auth-db-uri`` and ``user-db-uri`` property types.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:98\nmsgid \"\"\n\"Once your configuration is setup, you will see the uri of your user \"\n\"database here. If other databases need to be defined, they will be listed\"\n\" in the same dataSource bean.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:113\nmsgid \"\"\n\"**Possible values:** Broken down list of customized names for DB URIs. \"\n\"Each name must have a defined uri property. DB name can be customized by \"\n\"the bean name.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:117\nmsgid \"\"\n\"URI name may be used as shorthand to define DB location URI in other \"\n\"containers, so be sure to name them uniquely.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:121\nmsgid \"\"\n\"default () URI setting replaces the ``user-db-uri`` as well as the \"\n\"``auth-repo-uri`` property.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:124\nmsgid \"MSSQL\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:126\nmsgid \"\"\n\"MSSql support works out of the box, however Tigase provides an open \"\n\"source driver for the database. We recommend using Microsoft’s own driver\"\n\" for better functionality.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:136\nmsgid \"\"\n\"Where the uri is divided as follows: jdbc:<driver>:sqlserver://<server \"\n\"address>;databaseName=<database name>;user=<username for \"\n\"db>;password=<password for \"\n\"db>;schema=dbo;lastUpdateCount=false;cacheMetaData=false We do not \"\n\"recommend modification of schema and onward unless you are explicit in \"\n\"your changes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:139\nmsgid \"MongoDb\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:141\nmsgid \"\"\n\"For using mongoDB as the repository, the setting will look slightly \"\n\"different:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:152\nmsgid \"MySQL\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:154\nmsgid \"\"\n\"MySQL support works out of the box, however Tigase uses prepared calls \"\n\"for calling procedures accessing data stored in database. While this \"\n\"works very fast, it takes time during Tigase XMPP Server startup to \"\n\"prepare those prepared calls. Since version 8.2.0, it is possible to \"\n\"enable workaround which will force Tigase XMPP Server to use prepared \"\n\"statements instead of prepared calls, that will improve startup time but \"\n\"may have slight impact on performance during execution of queries and \"\n\"disables startup verification checking if stored procedures and function \"\n\"in database exist and have correct parameter types. To enable this mode \"\n\"you need to set ``useCallableMysqlWorkaround`` to ``true``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:170\nmsgid \"\"\n\"``DataSource`` is an abstraction layer between any higher level data \"\n\"access repositories such as ``userRepository`` or ``authRepository`` and \"\n\"SQL database or JDBC driver to be more specific. Many implementations use\"\n\" ``DataSource`` for DB connections and in fact on many installations they\"\n\" also share the same DataRepository instance if they connect to the same \"\n\"DB. In this case it is desired to use a specific connection pool on this \"\n\"level to an avoid excessive number of connections to the database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:172\nmsgid \"\"\n\"To do so, specify the number of number of database connection as an \"\n\"integer:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:183\nmsgid \"By default, the number of connections is 10.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:2\nmsgid \"Cluster\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:5\nmsgid \"cl-comp\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:7\nmsgid \"**Description:** Container specifying cluster component configuration.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:9\nmsgid \"\"\n\"**Default value:** By default, the cl-comp container is not listed in the\"\n\" ``config.tdsl`` file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:20\nmsgid \"connect-all\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:22\nmsgid \"\"\n\"The ``cluster-connect-all`` property is used to open active connections \"\n\"to all nodes listed in the :ref:`cluster-nodes<clusterNodes>` \"\n\"configuration property. This property should be used only on the node \"\n\"which is added to the live cluster at later time. Normally this new \"\n\"cluster node is not listed in the configuration of the existing cluster \"\n\"nodes. This is why they can not open connections the new node. The new \"\n\"node opens connection to all existing nodes instead. False is the default\"\n\" value and you can skip this option if you want to have it switched off \"\n\"which it is by default.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:24\nmsgid \"**Example**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:32\nmsgid \"This replaces the ``--cluster-connect-all`` property.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:37\nmsgid \"cluster-mode\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:39\nmsgid \"\"\n\"**Description:** The property is used to switch cluster mode on. The \"\n\"default value is ``false`` so you can normally skip the parameter if you \"\n\"don’t want the server to run in cluster mode. You can run the server in \"\n\"the cluster mode even if there is only one node running. The performance \"\n\"impact is insignificant and you will have the opportunity to connect mode\"\n\" cluster nodes at any time without restarting the server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:41\nmsgid \"\"\n\"**Default value:** ``false`` Tigase by default does not run in clustered \"\n\"mode.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:43\nmsgid \"**Example:** ``'cluster-mode' = 'true'``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:52\nmsgid \"cluster-nodes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:54\n#: ../../Tigase_Administration/Properties/_external.inc:9\nmsgid \"**Default value:** none\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:56\nmsgid \"\"\n\"**Example:** ``'cluster-nodes' = [ 'node1.domain:pass:port' , \"\n\"'node2.domain:pass:port' , 'node3.domain:pass:port' ]``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:58\nmsgid \"**Possible values:** a comma separated list of hostnames.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:60\nmsgid \"\"\n\"**Description:** The property is used to specify a list of cluster nodes \"\n\"running on your installation. The node is the full DNS name of the \"\n\"machine running the node. Please note the proper DNS configuration is \"\n\"critical for the cluster to work correctly. Make sure the 'hostname' \"\n\"command returns a full DNS name on each cluster node. Nodes don’t have to\"\n\" be in the same network although good network connectivity is also a \"\n\"critical element for an effective cluster performance.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:62\nmsgid \"\"\n\"All cluster nodes must be connected with each other to maintain user \"\n\"session synchronization and exchange packets between users connected to \"\n\"different nodes. Therefore each cluster node opens a 'cluster port' on \"\n\"which it is listening for connections from different cluster nodes. As \"\n\"there is only one connection between each two nodes Tigase server has to \"\n\"decide which nodes to connect to and which has to accept the connection. \"\n\"If you put the same list of cluster nodes in the configuration for all \"\n\"nodes this is not a problem. Tigase server has a way to find and void any\"\n\" conflicts that are found. If you however want to add a new node later \"\n\"on, without restarting and changing configuration on old nodes, there is \"\n\"no way the old nodes will try to establish a connection to the new node \"\n\"they don’t know them. To solve this particular case the next parameter is\"\n\" used.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:66\nmsgid \"\"\n\"Cluster nodes are not required to be configured, as they can \"\n\"automatically find/add/remove cluster nodes. This is for installations \"\n\"where nodes will be limited and static!\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:2\nmsgid \"User connectivity\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:5\nmsgid \"bosh-close-connection\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:9\nmsgid \"**Example:** ``'bosh-close-connection' = true``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:13\nmsgid \"\"\n\"**Description:** This property globally disables Bosh keep-alive support \"\n\"for Tigase server. It causes the Bosh connection manager to force close \"\n\"the HTTP connection each time data is sent to the Bosh client. To \"\n\"continue communication the client must open a new HTTP connection.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:15\nmsgid \"\"\n\"This setting is rarely required but on installations where the client \"\n\"cannot control/disable keep-alive Bosh connections and keep-alive does \"\n\"not work correctly for some reason.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:20\nmsgid \"bosh-extra-headers-file\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:22\nmsgid \"**Default value:** ``'etc/bosh-extra-headers.txt'``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:24\nmsgid \"**Example:** ``'bosh-extra-headers-file' = ''/path/to/file.txt'``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:26\nmsgid \"**Possible values:** 'path to a file on the filesystem.'\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:28\nmsgid \"\"\n\"**Description:** This property allows you to specify a path to a text \"\n\"file with additional HTTP headers which will be sent to a Bosh client \"\n\"with each request. This gives some extra flexibility for Bosh clients \"\n\"running on some systems with special requirements for the HTTP headers \"\n\"and some additional settings.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:30\nmsgid \"\"\n\"By default a file distributed with the installation contains following \"\n\"content:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:39\nmsgid \"\"\n\"This can be modified, removed or replaced with a different content on \"\n\"your installation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:44\nmsgid \"client-access-policy-file\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:46\nmsgid \"**Default value:** ``etc/client-access-policy.xml``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:48\nmsgid \"\"\n\"**Example:** ``'client-access-policy-file' = ''/path/to/access-policy-\"\n\"file.xml'``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:50\nmsgid \"**Possible values:** path to a file on the filesystem.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:52\nmsgid \"\"\n\"**Description:** The ``client-access-policy-file`` property allows \"\n\"control of the cross domain access policy for Silverlight based web \"\n\"applications. The cross domain policy is controlled via XML file which \"\n\"contains the policy and rules.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:54\nmsgid \"\"\n\"By default Tigase is distributed with an example policy file which allows\"\n\" for full access from all sources to the whole installation. This is \"\n\"generally okay for most Bosh server installations. The configuration \"\n\"through the property and XML files allows for a very easy and flexible \"\n\"modification of the policy on any installation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:59\nmsgid \"client-port-delay-listening\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:61\nmsgid \"\"\n\"**Description:** The property allows to enabled or disable delaying of \"\n\"listening for client connections **in cluster mode** until the cluster is\"\n\" correctly connected.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:63\n#: ../../Tigase_Administration/Properties/_vhost.inc:7\n#: ../../Tigase_Administration/Properties/_vhost.inc:100\nmsgid \"**Default value:** ``true``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:73\nmsgid \"**Possible values:** ``true``, ``false``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:75\nmsgid \"\"\n\"In cluster mode, in order to ensure correct user status broadcast, we are\"\n\" delaying opening client ports (components: ``c2s``, ``ws2s``, ``bosh``) \"\n\"and enable those only after cluster is fully and correctly connected \"\n\"(i.e. either there is only single node or in case of multiple nodes all \"\n\"nodes connected correctly).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:77\nmsgid \"\"\n\"It’s possible to enable/disable this on per-component basis with the \"\n\"following configuration:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:91\nmsgid \"\"\n\"Maximum delay time depends on the component and it’s multiplication of \"\n\"``ConnectionManager`` default connection delay times ``30s`` - in case of\"\n\" client connection manager this delay equals 60s.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:95\nmsgid \"Only applicable if **Cluster Mode** is active!\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:100\nmsgid \"cross-domain-policy-file\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:102\nmsgid \"**Default value:** ``etc/cross-domain-policy.xml``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:104\nmsgid \"\"\n\"**Example:** ``'cross-domain-policy-file' = ''/path/to/cross-domain-\"\n\"policy.xml'``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:106\nmsgid \"**Possible values:** path to a file on the file system.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:108\nmsgid \"\"\n\"**Description:** This property allows you to set a path to a file with \"\n\"cross domain access policy for flash based clients. This is a standard \"\n\"XML file which is sent to the flash client upon request.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:110\nmsgid \"\"\n\"A default file distributed with Tigase installations allows for full \"\n\"access for all. This is good enough for most use cases but it can be \"\n\"changed by simply editing the file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:112\nmsgid \"\"\n\"This is a global property that can also be overridden by configuring \"\n\"connection managers [ c2s, s2s, ws2s, bosh, ext, etc] and they may all \"\n\"have their own policies.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:123\nmsgid \"domain-filter-policy\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:125\nmsgid \"**Default value:** ``ALL``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:127\nmsgid \"**Example:** ``domain-filter-policy' = 'LOCAL``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:129\nmsgid \"\"\n\"**Possible values:** \"\n\"``ALL|LOCAL|OWN|BLOCK|LIST=domain1;domain2|BLACKLIST=domain1;domain2``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:131\nmsgid \"\"\n\"**Description:** The ``domain-filter-policy`` property is a global \"\n\"setting for setting communication filtering for vhosts. This function is \"\n\"kind of an extension of the same property which could be set on a single \"\n\"user level. However, in many cases it is desired to control users \"\n\"communication not on per user-level but on the domain level. Domain \"\n\"filtering (communication filtering) allows you to specify with whom users\"\n\" can communicate for a particular domain. It enables restriction of \"\n\"communications for a selected domain or for the entire installation. A \"\n\"default value ``ALL`` renders users for the domain (by default for all \"\n\"domains) able to communicate with any user on any other domains. Other \"\n\"possible values are:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:133\nmsgid \"\"\n\"``ALL`` a default value allowing users to communicate with anybody on any\"\n\" other domain, including external servers.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:135\nmsgid \"\"\n\"``LOCAL`` allows users to communicate with all users on the same \"\n\"installation on any domain. It only blocks communication with external \"\n\"servers.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:137\nmsgid \"\"\n\"``OWN`` allows users to communicate with all other users on the same \"\n\"domain. Plus it allows users to communicate with subdomains such as \"\n\"**muc.domain**, **pubsub.domain**, etc…\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:139\nmsgid \"\"\n\"``BLOCK`` value completely blocks communication for the domain or for the\"\n\" user with anybody else. This could be used as a means to temporarily \"\n\"disable account or domain.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:141\nmsgid \"\"\n\"``LIST`` property allows to set a list of domains (users' JIDs) with \"\n\"which users on the domain can communicate (i.e. *whitelist*).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:143\nmsgid \"\"\n\"``BLACKLIST`` - user can communicate with everybody (like ``ALL``), \"\n\"except contacts on listed domains.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:145\nmsgid \"\"\n\"This is a global property which is overridden by settings for particular \"\n\"VHosts (see :ref:`Add and Manage Domains(VHosts)<addManageDomain>`).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:147\nmsgid \"\"\n\"A default settings for all virtual hosts for which the configuration is \"\n\"not defined. This settings is useful mostly for installations with many \"\n\"virtual hosts listed in the init.property file for which there is no \"\n\"individual settings specified. It allows default value for all of \"\n\"servers, instead of having to provide individual configuration for each \"\n\"vhost.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:149\nmsgid \"\"\n\"``ALL`` is also applied as a default value for all new vhosts added at \"\n\"run-time.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:154\nmsgid \"see-other-host\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:156\nmsgid \"\"\n\"--cmSeeOtherHost has been replaced with using ``seeOtherHost`` setting, \"\n\"and can be configured for each connection manager (c2s, s2s, etc..)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:158\nmsgid \"**Default value:** ``tigase.server.xmppclient.SeeOtherHostHashed``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:168\nmsgid \"**Possible values:** 'none' 'or class implementing SeeOtherHostIfc.'\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:170\nmsgid \"\"\n\"**Description:** Allows you to specify a load balancing mechanism by \"\n\"specifying SeeOtherHostIfc implementation. More details about \"\n\"functionality and implementation details can be found in Tigase Load \"\n\"Balancing documentation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:177\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:207\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:281\nmsgid \"watchdog_timeout\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:179\nmsgid \"**Default value:** ``1740000``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:181\nmsgid \"**Example:** ``watchdog_timeout=60000``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:183\nmsgid \"**Possible values:** ``any integer.``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:185\nmsgid \"\"\n\"**Description:** The ``watchdog_timeout`` property allows for fine-tuning\"\n\" ConnectionManager Watchdog (service responsible for detecting broken \"\n\"connections and closing them). Timeout property relates to the amount of \"\n\"time (in miliseconds) after which lack of response/activity on a given \"\n\"connection will considered such connection as broken an close it. In \"\n\"addition to global configuration presented above a per component \"\n\"configuration is possible:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:193\nmsgid \"for example (for C2SConnectionManager):\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:201\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:238\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:275\nmsgid \"All related configuration options:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:203\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:240\nmsgid \":ref:`watchdog_Ping_Type<watchdogPingType>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:205\nmsgid \":ref:`watchdog_delay<watchdogDelay>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:214\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:242\nmsgid \"watchdog_delay\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:216\nmsgid \"**Default value:** ``600000``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:218\nmsgid \"**Example:** ``watchdog_delay = '30000'``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:220\nmsgid \"**Possible values:** 'any integer.'\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:222\nmsgid \"\"\n\"**Description:** ``watchdog_delay`` configuration property allows \"\n\"configuring delay (in milliseconds) between subsequent checks that \"\n\"ConnectionManager Watchdog (service responsible for detecting broken \"\n\"connections and closing them) will use to verify the connection. In \"\n\"addition to global configuration presented above a per component \"\n\"configuration is possible:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:57\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:230\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:267\nmsgid \"for example (for ClusterConnectionManager):\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:244\nmsgid \":ref:`watchdog_timeout<watchdogTimeout>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:251\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:277\nmsgid \"watchdog_ping_type\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:253\nmsgid \"**Default value:** ``whitespace``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:255\nmsgid \"**Example:** ``watchdog_ping_type = 'XMPP'``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:257\nmsgid \"**Possible values:** ``WHITESPACE``,\\\\ ``XMPP``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:259\nmsgid \"\"\n\"**Description:** ``watchdog_ping_type`` configuration property allows \"\n\"configuring of the type of ping that ConnectionManager Watchdog (service \"\n\"responsible for detecting broken connections and closing them) will use \"\n\"to check the connection. In addition to global configuration presented \"\n\"above a per component configuration is possible:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:279\nmsgid \":ref:`watchdog_Delay<watchdogDelay>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:286\nmsgid \"ws-allow-unmasked-frames\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:290\nmsgid \"**Example:** ``'ws-allow-unmasked-frames' = true``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:294\nmsgid \"\"\n\"**Description:** RFC 6455 specifies that all clients must mask frames \"\n\"that it sends to the server over Websocket connections. If unmasked \"\n\"frames are sent, regardless of any encryption, the server must close the \"\n\"connection. Some clients however, may not support masking frames, or you \"\n\"may wish to bypass this security measure for development purposes. This \"\n\"setting, when enabled true, will allow connections over websocket to be \"\n\"unmasked to the server, and may operate without Tigase closing that \"\n\"connection.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:2\nmsgid \"External\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:7\nmsgid \"bind-ext-hostnames\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:11\nmsgid \"**Example:** ``'bind-ext-hostnames' = [ 'pubsub.host.domain' ]``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:13\nmsgid \"**Possible values:** comma separated list of domains.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:15\nmsgid \"\"\n\"**Description:** This property enables setting a list of domains to be \"\n\"bound to the external component connection. Let’s say we have a Tigase \"\n\"instance with only MUC and PubSub components loaded and we want to \"\n\"connect this instance to the main server via external component protocol.\"\n\" Using `--external property <#external>`__ we can define a domain \"\n\"(perhaps muc.devel.tigase.org), password, TCP/IP port, remote host \"\n\"address, connection type, etc…​ This would make one of our components \"\n\"(MUC) visible on the remote server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:17\nmsgid \"\"\n\"To make the second component (PubSub) visible we would need to open \"\n\"another connection with the domain name (pubsub.devel.tigase.org) for the\"\n\" other component. Of course the second connection is redundant as all \"\n\"communication could go through a single connection. This is what this \"\n\"property is used. In our example with 2 components you can just put the \"\n\"'pubsub.devel.tigase.org' domain as a value to this property and it will \"\n\"bind the second domain to a single connection on top of the domain which \"\n\"has been authenticated during protocol handshaking.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:24\nmsgid \"default-virtual-host\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:26\nmsgid \"\"\n\"**Description:** The ``default-virtual-host`` property allows setting of \"\n\"the name of default virtual host that is served by the installation. It \"\n\"is loaded during startup of the application and stored in the database. \"\n\"**It may only contain single domain name!**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:28\nmsgid \"\"\n\"Any additional configuration options or additional virtual hosts domains \"\n\"should be added and configured using ad-hoc commands such as ``Add new \"\n\"item``, ``Update item configuration`` and ``Remove an item`` available at\"\n\" the JID of the ``VHostManager`` component of your installation (``vhost-\"\n\"man@your-server-domain``).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:33\nmsgid \"ext\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:35\nmsgid \"\"\n\"**Description:** This property defines parameters for external component \"\n\"connections.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:37\nmsgid \"\"\n\"The component is loaded the same way as all other Tigase components. In \"\n\"your ``config.tdsl`` file you need to add the external class:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:43\nmsgid \"\"\n\"This will load the component with an empty configuration and is \"\n\"practically useless. You have to tell the component on what port to \"\n\"listen to (or on what port to connect to) and external domains list with \"\n\"passwords.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:45\nmsgid \"\"\n\"Those values need to be configured while the Tigase XMPP Server is \"\n\"running using XMPP ad-hoc commands such as ``Add new item``, ``Update \"\n\"item configuration`` and ``Remove an item`` available at the JID of the \"\n\"external component which you have just enabled (``ext@your-server-\"\n\"domain``).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:47\nmsgid \"**Possible values:** external domains parameters list.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:51\nmsgid \"**Removed in:** 8.0.0\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:2\nmsgid \"Performance\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:5\nmsgid \"cm-ht-traffic-throttling\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:7\nmsgid \"**Default value:** ``xmpp:25k:0:disc,bin:200m:0:disc``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:9\nmsgid \"\"\n\"**Example:** ``'cm-ht-traffic-throttling' = \"\n\"'xmpp:25k:0:disc,bin:200m:0:disc'``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:11\n#: ../../Tigase_Administration/Properties/_performance.inc:26\nmsgid \"**Possible values:** comma separated list of traffic limits settings.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:13\nmsgid \"\"\n\"**Description:** This property is used to specify traffic limit of non-\"\n\"user connections, that is s2s, external components and other high traffic\"\n\" server connections. The meaning of the property and values encoded are \"\n\"in the same way as for the :ref:`cm-traffic-throttling \"\n\"property<cmTrafficThrottling>`.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:20\nmsgid \"cm-traffic-throttling\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:22\nmsgid \"**Default value:** ``xmpp:2500:0:disc,bin:20m:0:disc``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:24\nmsgid \"\"\n\"**Example:** ``'cm-traffic-throttling' = \"\n\"'xmpp:2500:0:disc,bin:20m:0:disc'``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:28\nmsgid \"\"\n\"**Description:** The ``cm-traffic-throttling`` property allows you to \"\n\"limit traffic on user connections. These limits are applied to each user \"\n\"connection and if a limit is exceeded then a specified action is applied.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:30\nmsgid \"\"\n\"The property value is a comma separated list of traffic limits settings. \"\n\"For example the first part: ``xmpp:2500:0:disc`` specifies traffic limits\"\n\" for XMPP data to 2,500 packets allowed within last minute either sent to\"\n\" or received from a user and unlimited (0) total traffic on the user \"\n\"connection, in case any limit is exceeded the action is to **disconnect**\"\n\" the user.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:32\nmsgid \"\"\n\"**[xmpp|bin]** traffic type, xmpp - XMPP traffic, that is limits refer to\"\n\" a number of XMPP packets transmitted, bin - binary traffic, that is \"\n\"limits refer to a number of bytes transmitted.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:34\nmsgid \"\"\n\"**2500** maximum traffic allowed within 1 minute. 0 means unlimited, or \"\n\"no limits.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:36\nmsgid \"\"\n\"**0** maximum traffic allowed for the life span of the connection. 0 \"\n\"means unlimited or no limits.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:38\nmsgid \"\"\n\"**[disc|drop]** action performed on the connection if limits are \"\n\"exceeded. disc - means disconnect, drop - means drop data.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:40\nmsgid \"**Available since:** 5.1.3\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:43\nmsgid \"elements-number-limit\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:45\nmsgid \"**Default value:** ``1000``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:47\nmsgid \"**Possible values:** any integer.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:49\nmsgid \"\"\n\"**Description:** ``elements-number-limit`` configuration property allows \"\n\"configuring a Denial of Service protection mechanism which limits number \"\n\"of elements sent in stanza. It must be configured on a per \"\n\"ConnectionManager basis:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:69\nmsgid \"hardened-mode\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:71\nmsgid \"**Default value:** ``secure``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:73\nmsgid \"**Example:** ``'hardened-mode' = secure``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:75\nmsgid \"**Possible values:** ``relaxed|secure|strict``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:77\nmsgid \"\"\n\"**Description:** Adjusting hardened mode affects handling of security \"\n\"aspects within Tigase. The higher the level the more strict are the \"\n\"rules: \\\\* ``relaxed`` - uses default security capabilities from \"\n\"installed JVM; \\\\* ``secure`` - disables old SSLv2 and SSLv3, disables \"\n\"weak cyphers; \\\\* ``strict`` - in addition to ``secure`` level changes it\"\n\" also disables ``TLSv1`` and ``TLSv1.1`` as well as ciphers that don’t \"\n\"support Forward secrecy.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:79\nmsgid \"\"\n\"On older JVM versions it required `UnlimitedJCEPolicyJDK \"\n\"<http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html>`__\"\n\" installed. It’s not required with OpenJDK8 and newer an OracleJVM 11 and\"\n\" newer.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:84\nmsgid \"max-queue-size\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:86\nmsgid \"**Default value:** default queue size is variable depending on RAM size.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:88\nmsgid \"**Example:** ``'max-queue-size' = 10000``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:90\n#: ../../Tigase_Administration/Properties/_vhost.inc:43\nmsgid \"**Possible values:** integer number.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:92\nmsgid \"\"\n\"**Description:** The ``max-queue-size`` property sets internal queues \"\n\"maximum size to a specified value. By default Tigase sets the queue size \"\n\"depending on the maximum available memory to the Tigase server process. \"\n\"It set’s 1000 for each 100MB memory assigned for JVM. This is enough for \"\n\"most cases. If you have however, an extremely busy service with Pubsub or\"\n\" MUC component generating huge number of packets (presence or messages) \"\n\"this size should be equal or bigger to the maximum expected number of \"\n\"packets generated by the component in a single request. Otherwise Tigase \"\n\"may drop packets that it is unable to process.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:99\nmsgid \"net-buff-high-throughput\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:101\nmsgid \"**Default value:** ``64k``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:103\nmsgid \"**Example:** ``'net-buff-high-throughput' = '256k'``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:105\n#: ../../Tigase_Administration/Properties/_performance.inc:120\nmsgid \"**Possible values:** network buffer size as integer.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:107\nmsgid \"\"\n\"**Description:** The ``net-buff-high-throughput`` property sets the \"\n\"network buffer for high traffic connections like s2s or connections \"\n\"between cluster nodes. The default is ``64k`` and is optimal for medium \"\n\"traffic websites. If your cluster installation can not cope with traffic \"\n\"between nodes try to increase this number.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:114\nmsgid \"net-buff-standard\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:116\n#: ../../Tigase_Administration/Properties/_performance.inc:129\nmsgid \"**Default value:** ``2k``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:118\nmsgid \"**Example:** ``'net-buff-standard' = '16k'``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:122\nmsgid \"\"\n\"**Description:** This property sets the network buffer for standard \"\n\"(usually c2s) connections, default value is 2k and is optimal for most \"\n\"installations.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:127\nmsgid \"net-buffer\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:131\nmsgid \"**Example:** ``'net-buffer' = 16 * 1024``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:133\nmsgid \"**Possible values:** internal network buffer size as integer.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:135\nmsgid \"\"\n\"**Description:** Sets default size of a internal network buffer used by \"\n\"``ConnectionManager`` within the context in which it is set.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:140\nmsgid \"socket-buffer-size\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:142\nmsgid \"\"\n\"**Default value:** ``4k`` (for client-to-server connections) and ``64k`` \"\n\"(for server-to-server connections)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:144\nmsgid \"**Example:** ``'socket-buffer-size' = 16 * 1024``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:146\nmsgid \"**Possible values:** socket network buffer size as integer.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:148\nmsgid \"\"\n\"**Description:** Sets default size of a socket network buffer used by \"\n\"``ConnectionManager`` for each connection within the context in which it \"\n\"is set.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:150\nmsgid \"**Available since:** 8.3.0 (previously value of ``net-buffer`` was used)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:153\nmsgid \"nonpriority-queue\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:157\nmsgid \"**Example:** ``'nonpriority-queue' =  true``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:161\nmsgid \"\"\n\"**Description:** The ``nonpriority`` property can be used to switch to \"\n\"non-priority queues usage in Tigase server (value set to 'true'). Using \"\n\"non-priority queues prevents packets reordering. By default Tigase uses \"\n\"priority queues which means that packets with highest priority may take \"\n\"over packets with lower priority (presence updates) which may result in \"\n\"packets arriving out of order.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:163\nmsgid \"\"\n\"This may happen however only for packets of different types. That is, \"\n\"messages may take over presence packets. However, one message never takes\"\n\" over another message for the same user. Therefore, out of order packet \"\n\"delivery is not an issue for the most part.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:2\nmsgid \"VHost / domain\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:5\nmsgid \"vhost-anonymous-enabled\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:9\nmsgid \"**Example:** ``'vhost-anonymous-enabled' = 'false'``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:13\nmsgid \"\"\n\"**Description:** The ``vhost-anonymous-enabled`` property specifies \"\n\"whether anonymous user logins are allowed for the installation for all \"\n\"vhosts.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:15\n#: ../../Tigase_Administration/Properties/_vhost.inc:32\n#: ../../Tigase_Administration/Properties/_vhost.inc:53\n#: ../../Tigase_Administration/Properties/_vhost.inc:72\nmsgid \"\"\n\"This is a global property which is overridden by settings for particular \"\n\"VHost (see :ref:`Add and Manage Domains (VHosts)<addManageDomain>`).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:17\nmsgid \"\"\n\"Default settings for all virtual hosts are used when this property is not\"\n\" defined. This settings is useful mostly for installations with many \"\n\"virtual hosts listed in the ``config.tdsl`` file for which there is no \"\n\"individual settings specified. It allows the configuration of default \"\n\"values for all of them, instead of having to provide individual \"\n\"configuration for each VHost.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:22\nmsgid \"vhost-disable-dns-check\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:26\nmsgid \"**Example:** ``'vhost-disable-dns-check' = 'true'``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:30\nmsgid \"\"\n\"**Description:** This property disables DNS validation when adding or \"\n\"editing vhosts in Tigase server. This also exempts administrative \"\n\"accounts from validation. With this property enabled, you will not \"\n\"benefit from seeing if proper SRV records are set so other people can \"\n\"connect to specific vhosts from outside your network.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:37\nmsgid \"vhost-max-users\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:39\nmsgid \"**Default value:** ``0``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:41\nmsgid \"**Example:** ``'vhost-max-users' = '1000'``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:45\nmsgid \"\"\n\"**Description:** The ``vhost-max-users`` property specifies how many user\"\n\" accounts can be registered on the installations for all vhosts.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:47\nmsgid \"\"\n\"**0 - zero** means unlimited and this is a default. Otherwise greater \"\n\"than zero value specifies accounts number limit.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:49\n#: ../../Tigase_Administration/Properties/_vhost.inc:87\n#: ../../Tigase_Administration/Properties/_vhost.inc:108\n#: ../../Tigase_Administration/Properties/_vhost.inc:129\nmsgid \"\"\n\"This is a global property which is overridden by settings for particular \"\n\"vhost.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:51\nmsgid \"\"\n\"The default setting is used for all virtual hosts for which the \"\n\"configuration is not defined. This settings is most useful for \"\n\"installations with many virtual hosts listed in the ``init.property`` \"\n\"file for which there is no individual settings specified. It provides an \"\n\"ability to use default values for all of them, instead of having to \"\n\"provide individual configuration for each vhost.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:58\nmsgid \"vhost-message-forward-jid\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:60\nmsgid \"**Default value:** <null>\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:62\nmsgid \"**Example:** ``'vhost-message-forward-jid' = 'archive@domain.com'``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:64\nmsgid \"**Possible values:** 'valid JID'\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:66\nmsgid \"\"\n\"**Description:** This is a global property for message forwarding for the\"\n\" installation. This property is normally specified on the vhost \"\n\"configuration level, however if you want to forward all messages on your \"\n\"installation and you have many virtual domains this property allows to \"\n\"set message forwarding for all of them. A valid JID must be specified as \"\n\"the forwarding destination. Also a message forwarding plugin must be \"\n\"loaded and activated on the installation for the message forwarding to \"\n\"work.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:68\nmsgid \"\"\n\"The null value is used as a default when no configuration is set. This \"\n\"setting is mostly useful for installations with many virtual hosts listed\"\n\" in the ``init.property`` file for which there is no individual settings \"\n\"specified. It provides the ability to configure a default values for all \"\n\"of them, instead of having to provide individual configuration for each \"\n\"vhost.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:70\n#: ../../Tigase_Administration/Properties/_vhost.inc:91\n#: ../../Tigase_Administration/Properties/_vhost.inc:112\n#: ../../Tigase_Administration/Properties/_vhost.inc:133\nmsgid \"\"\n\"It is also applied as a default value for all new vhosts added at run-\"\n\"time.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:77\nmsgid \"vhost-presence-forward-jid\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:79\nmsgid \"**Default value:** ``<null>``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:81\nmsgid \"\"\n\"**Example:** ``'vhost-presence-forward-jid' = 'presence-\"\n\"collector@domain.com'``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:83\nmsgid \"**Possible values:** valid JID.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:85\nmsgid \"\"\n\"**Description:** This is a global property for presence forwarding \"\n\"function for the installation. All user status presences will be \"\n\"forwarded to given XMPP address which can be a component or any other \"\n\"XMPP entity. If the destination entity is a bot connected via c2s \"\n\"connection it probably should be addressed via full JID (with resource \"\n\"part) or the standard XMPP presence processing would refuse to deliver \"\n\"presences from users who are not in the contact list.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:89\nmsgid \"\"\n\"The null value is used as a default when no configuration is set. This \"\n\"settings is useful for installations with many virtual hosts listed in \"\n\"the ``init.property`` file for which there is no individual settings \"\n\"specified. It enables the ability to configure default values for all of \"\n\"them, instead of having to provide individual configuration for each \"\n\"vhost.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:93\n#: ../../Tigase_Administration/Properties/_vhost.inc:114\n#: ../../Tigase_Administration/Properties/_vhost.inc:135\nmsgid \"This may be used on a per-VHost (see `??? <#addManageDomain>`__).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:98\nmsgid \"vhost-register-enabled\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:102\nmsgid \"**Example:** ``'vhost-register-enabled' = false``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:106\nmsgid \"\"\n\"**Description:** ``vhost-register-enabled`` is a global property which \"\n\"allows you to switch on/off user registration on the installation. \"\n\"Setting this property to ``false`` does not disable the registration \"\n\"plugin on the server. You can enable registration for selected domains in\"\n\" the domain configuration settings.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:110\nmsgid \"\"\n\"The ``true`` value is used as a default when no configuration is set. \"\n\"This settings is useful for installations with many virtual hosts listed \"\n\"in the ``init.property`` file for which there is no individual settings \"\n\"specified. It allows admins to configure default values for all of them, \"\n\"instead of having to provide individual configuration for each vhost.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:119\nmsgid \"vhost-tls-required\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:123\nmsgid \"**Example:** ``'vhost-tls-required' = true``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:127\nmsgid \"\"\n\"**Description:** This property is a global settings to switch on/off TLS \"\n\"required mode on the Tigase installation. Setting this property to \"\n\"``false`` does not turn TLS off. TLS is still available on the server but\"\n\" as an option and this is the client’s decision whether to use encryption\"\n\" or not. If the property is set to true the server will not allow for \"\n\"user authentication or sending any other user data before TLS handshaking\"\n\" is completed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:131\nmsgid \"\"\n\"The ``false`` value is used as a default when no configuration is set. \"\n\"This settings is useful for installations with many virtual hosts listed \"\n\"in the ``init.property`` file for which there is no individual settings \"\n\"specified. It allows admins to configure default values for all of them, \"\n\"instead of having to provide individual configuration for each vhost.\"\nmsgstr \"\"\n\n#~ msgid \"\"\n#~ \"This option allows you to specify \"\n#~ \"the location where SSL certificates are\"\n#~ \" stored. The meaning of this property\"\n#~ \" depends on the SSL container `class\"\n#~ \" implementation <#sslContainerClass>`__. By \"\n#~ \"default it just points to the \"\n#~ \"directory where the server SSL \"\n#~ \"certificates are stored in files in \"\n#~ \"PEM format.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"There are many many configuration \"\n#~ \"options under each component, which are\"\n#~ \" specified in `component documentation \"\n#~ \"<#loadComponent>`__.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"**Description:** The ``debug`` property is \"\n#~ \"used to turn on the debug log \"\n#~ \"for the specified Tigase package. For\"\n#~ \" example if you want to turn \"\n#~ \"debug logs on for the ``tigase.server``\"\n#~ \" package, then you have to use \"\n#~ \"the ``server`` parameter. If you have\"\n#~ \" any problems with your server the\"\n#~ \" best way to get help from the\"\n#~ \" Tigase team is to generate \"\n#~ \"configuration with this enabled at a \"\n#~ \"minimum and run the server. Then \"\n#~ \"from the ``logs/tigase-console.log`` log \"\n#~ \"file I can provide the best \"\n#~ \"information for us to provide \"\n#~ \"assistance. More details about server \"\n#~ \"logging and adjusting logging level is\"\n#~ \" described in the Debugging Tigase \"\n#~ \"article in the admin guide. If you\"\n#~ \" wish to debug packages not compiled\"\n#~ \" with Tigase, use the `debug-packages\"\n#~ \" <#debugPackages>`__ setting.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"**Description:** This property activates \"\n#~ \"monitoring interfaces through selected \"\n#~ \"protocols on selected TCP/IP port \"\n#~ \"numbers. For more details please refer\"\n#~ \" to the `monitoring guide \"\n#~ \"<#serverMonitoring>`__ in the user guide \"\n#~ \"for details. Each monitoring protocol \"\n#~ \"should be called in it’s own child\"\n#~ \" bean under ``monitoring ()``. If a\"\n#~ \" protocol is not specified, monitoring \"\n#~ \"under that will not be available.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"This is a global property which is\"\n#~ \" overridden by settings for each \"\n#~ \"VHost (see `??? <#addManageDomain>`__)\"\n#~ msgstr \"\"\n\n#~ msgid \"*Default value:** By default test mode is disabled.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"**Description:** The ``trusted`` property \"\n#~ \"allows users to specify a list of\"\n#~ \" accounts which are considered as \"\n#~ \"trusted, thus whom can perform some \"\n#~ \"specific actions on the server. They \"\n#~ \"can execute some commands, send a \"\n#~ \"broadcast message, set MOTD and so \"\n#~ \"on. The configuration is similar to \"\n#~ \"```admins`` <#admins>`__ setting.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"in some cases instead of default \"\n#~ \"for this property setting for \"\n#~ \"```data-repo-pool-size`` <#dataRepoPoolSize>`__\"\n#~ \" is used if pool-size is not\"\n#~ \" defined in ``userRepository``. This \"\n#~ \"depends on the repository implementation \"\n#~ \"and the way it is initialized.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"The ``cluster-connect-all`` property is\"\n#~ \" used to open active connections to\"\n#~ \" all nodes listed in the `cluster-\"\n#~ \"nodes <#clusterNodes>`__ configuration property. \"\n#~ \"This property should be used only \"\n#~ \"on the node which is added to \"\n#~ \"the live cluster at later time. \"\n#~ \"Normally this new cluster node is \"\n#~ \"not listed in the configuration of \"\n#~ \"the existing cluster nodes. This is \"\n#~ \"why they can not open connections \"\n#~ \"the new node. The new node opens\"\n#~ \" connection to all existing nodes \"\n#~ \"instead. False is the default value \"\n#~ \"and you can skip this option if\"\n#~ \" you want to have it switched \"\n#~ \"off which it is by default.\"\n#~ msgstr \"\"\n\n#~ msgid \"*Default value:** ``'etc/bosh-extra-headers.txt'``\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"This is a global property which is\"\n#~ \" overridden by settings for particular \"\n#~ \"VHosts (see `??? <#addManageDomain>`__).\"\n#~ msgstr \"\"\n\n#~ msgid \"`watchdog_Ping_Type <#watchdogPingType>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`watchdog_delay <#watchdogDelay>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`watchdog_timeout <#watchdogTimeout>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`watchdog_Delay <#watchdogDelay>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"**Description:** This property is used \"\n#~ \"to specify traffic limit of non-\"\n#~ \"user connections, that is s2s, external\"\n#~ \" components and other high traffic \"\n#~ \"server connections. The meaning of the\"\n#~ \" property and values encoded are in\"\n#~ \" the same way as for the \"\n#~ \"`cm-traffic-throttling property \"\n#~ \"<#cmTrafficThrottling>`__.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"This is a global property which is\"\n#~ \" overridden by settings for particular \"\n#~ \"VHost (see ` Add and Manage \"\n#~ \"Domains (VHosts)`__).\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"This is a global property which is\"\n#~ \" overridden by settings for particular \"\n#~ \"VHost (see `??? <#addManageDomain>`__).\"\n#~ msgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Administration/Quick_Start_Guide/Intro.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2023-02-16 15:01-0800\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.11.0\\n\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:2\nmsgid \"Quick Start Guide\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:5\nmsgid \"Minimum Requirements\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:7\nmsgid \"\"\n\"Before you begin installing Tigase server onto your system, please make \"\n\"sure the minimum requirements are met first:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:9\nmsgid \"**Java Development Kit (JDK) 17 (LTS)** - We recommend OpenJDK\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:11\nmsgid \"\"\n\"**Administrator access** - We recommend that you install Tigase Server \"\n\"from a user login with administrator access.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:17\nmsgid \"\"\n\"You should always run the latest point/bugfix release of the recommended \"\n\"JDK.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:21\nmsgid \"\"\n\"While it should be possible to use newer versions of the JDK, we don’t \"\n\"guarantee it and we recommend using the one mentioned above.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:24\nmsgid \"Contents\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:26\nmsgid \"\"\n\"This is a set of documents allowing you to quickly start with our \"\n\"software. Every document provides an introduction to a single topic \"\n\"allowing you to start using/developing or just working on the subject. \"\n\"Please have a look at the documents list below to find a topic you are \"\n\"looking for. If you don’t find a document for the topic you need please \"\n\"`let us know <http://www.tigase.net/contact>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:28\nmsgid \":ref:`Installation Using Web Installer<webinstall>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:30\nmsgid \":ref:`Manual installation in console mode<manualinstall>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:32\nmsgid \":ref:`Installing Tigase on Windows <windowsInstallation>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:34\nmsgid \":ref:`Network settings for Tigase<setupTigaseServer>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:36\nmsgid \":ref:`Running Tigase XMPP Server as a service<tigaseScriptStart>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:4\nmsgid \"Installation Using Web Installer\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:6\nmsgid \"\"\n\"When Tigase XMPP Server starts up, it looks for the default configuration\"\n\" file: ``etc/config.tdsl``. If this file has not been modified you can \"\n\"run the web installer. Which will step you through the process of \"\n\"configuring Tigase. If you are installing Tigase in a Windows \"\n\"environment, please see the :ref:`Windows Installation<winWebInstall>` \"\n\"section.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:10\nmsgid \"Download and Extract\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:12\nmsgid \"\"\n\"First download Tigase XMPP Server and extract it. You can download the \"\n\"`official binaries <https://tigase.net/downloads>`__, or the latest and \"\n\"greatest `nightly builds <https://build.tigase.net/nightlies/dists/>`__. \"\n\"Once you have the distribution binary extract it and navigate to the \"\n\"directory:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:22\nmsgid \"Do not run as root user!\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:26\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:283\nmsgid \"Start the Server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:30\nmsgid \"Please make sure ``JAVA_HOME`` is set and points to your JVM installation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:38\nmsgid \"Verify Tigase is ready to start installation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:40\nmsgid \"\"\n\"Tigase should start listening on port 8080 - you can check it using \"\n\"``lsof`` command:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:48\nmsgid \"\"\n\"You can also check console log under ``logs/tigase-console.log``, which \"\n\"should point you directly to the installer.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:53\nmsgid \"Connect to the Web Installer\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:55\nmsgid \"Some points before you can connect:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:57\nmsgid \"\"\n\"This setup page is restricted access, however for first setup there is a \"\n\"default account set to setup Tigase: Username: ``admin`` Password: \"\n\"``tigase``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:59\nmsgid \"\"\n\"This combination will only be valid once as it will be removed from \"\n\"``config.tdsl`` file on completion of setup process. After this point the\"\n\" setup page will only be accessible using the following:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:61\nmsgid \"\"\n\"JID accounts listed as administrators in admins line in ``config.tdsl`` \"\n\"file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:63\nmsgid \"\"\n\"Username and password combinations added to ``config.tdsl`` file \"\n\"manually, or at the last page in this process.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:65\nmsgid \"\"\n\"Point your browser to http://localhost:8080/setup/ unless you are working\"\n\" remotely. You can also use the domain name, or IP address.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:67\nmsgid \"Enter the username and password above to gain access.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:71\nmsgid \"Step Through the Installation Process\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:73\nmsgid \"You will be greeted by the following \\\"About software\\\" page.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:75\nmsgid \"|web install 01|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:180\nmsgid \"web install 01\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:77\nmsgid \"Read it and then click \\\"Next\\\"\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:79\nmsgid \"\"\n\"The setup consists of several steps that help you configure your \"\n\"installation: selecting features and providing database configuration.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:83\nmsgid \"\"\n\"Order and design of the steps may slightly differ thus we only provide a \"\n\"broad overview of how to proceed:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:85\nmsgid \"**Advanced Clustering Strategy information**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:87\nmsgid \"\"\n\"You will see some information about our commercial products and \"\n\"licensing. Please read though the agreement, and as a confirmation of \"\n\"agreement type in your name or company and click \\\"Next\\\" to go to the \"\n\"next page.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:89\nmsgid \"**Basic Tigase server configuration**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:91\nmsgid \"\"\n\"This page will look over your basic configuration settings, those include\"\n\" the server type, domain you wish to use, and gives you a chance to \"\n\"specify an administrator for the domain. Also, you will be selecting what\"\n\" type of database Tigase server will be using (configuration comes \"\n\"later).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:93\nmsgid \"\"\n\"If you do not specify an administrator and password, one is made for you,\"\n\" which will be admin@yourdomain and password is tigase.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:95\nmsgid \"**Connectivity**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:97\nmsgid \"\"\n\"At this page you will be presented with a list of possible connectivity \"\n\"options supported by Tigase XMPP Server and a way to enable/disable each \"\n\"of them (desktop, mobile, http, websocket, federation, etc.). After \"\n\"making this decisions, click \\\"Next\\\".\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:99\nmsgid \"**Features**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:101\nmsgid \"\"\n\"Now you will be able to select which features of Tigase XMPP Server (such\"\n\" as MUC, PubSub, MIX, MAM, Push Notifications) should be enabled or \"\n\"disabled.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:103\nmsgid \"At this step will also be able to enable clustering on your installation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:105\nmsgid \"When you will be ready, click \\\"Next\\\".\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:107\nmsgid \"**Database configuration**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:109\nmsgid \"\"\n\"This is where the database is configured. The type of database selected \"\n\"in step 3 will influence available options. **BE SURE TO SPECIFY DATABASE\"\n\" ROOT USER ACCOUNT AND PASSWORD**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:111\nmsgid \"**Database connectivity check**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:113\nmsgid \"\"\n\"After database setup, you should see a page with executed actions and \"\n\"their results. All presented items should be \\\"green\\\", meaning that \"\n\"everything went well. If anything is presented in \\\"red\\\" or \\\"yellow\\\", \"\n\"please read description presented below this header to learn more about \"\n\"this issue. If setup is completed, click \\\"Next\\\".\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:115\nmsgid \"**Setup security**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:117\nmsgid \"\"\n\"The Setup Access Page will be locked from the admin/tigase user as \"\n\"specified above. This is your chance to have the setup pages add a \"\n\"specific user in addition to admin accounts to re-access this setup \"\n\"process later. If left blank, only JIDs listed in admin will be allowed \"\n\"to access.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:119\nmsgid \"**Saving configuration**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:121\nmsgid \"\"\n\"The installation is almost complete and you will be presented with a page\"\n\" showing what the resulting configuration (stored in ``config.tdsl`` \"\n\"file) will look like.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:123\nmsgid \"\"\n\"If you have a custom setup, or would like to put your own settings in, \"\n\"you may copy and past the contents here to edit the current \"\n\"``config.tdsl`` file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:125\nmsgid \"Click \\\"Save\\\" to write the file to disk.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:127\nmsgid \"**Finished**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:129\nmsgid \"\"\n\"You have now finished the installation, proceed to the next step to \"\n\"restart the server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:132\nmsgid \"Restart the Server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:134\nmsgid \"\"\n\"It is recommended at this point to stop the server manually and restart \"\n\"it using the proper script for your OS. From the Tigase base directory \"\n\"enter\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:145\nmsgid \"\"\n\"In order to make Tigase XMPP Server start automatically during system \"\n\"startup you should setup startup scripts as described in :ref:`Tigase \"\n\"Script Selection<tigaseScriptStart>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:147\nmsgid \"\"\n\"To further fine tune the server you should edit ``etc/tigase.conf``. \"\n\"Ensure ``JAVA_HOME`` path is correct, and increase memory if needed using\"\n\" ``JAVA_OPTIONS`` -Xmx (max), and -Xms (initial). You will need to direct\"\n\" Tigase to read settings from this file on startup as follows.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:149\nmsgid \"\"\n\"Everything should be running smooth at this point. Check the logfiles in \"\n\"``logs/`` if you experience any problems.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:152\nmsgid \"Verify Tigase is Running\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:154\nmsgid \"You should see a list of listening ports.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:170\nmsgid \"Windows Instructions for using Web Installer\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:172\nmsgid \"\"\n\"There are a few steps involved with setting up Tigase with the web \"\n\"installer in a Windows environment. Please follow this guide.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:174\nmsgid \"\"\n\"First step is to extract the distribution archive in it’s entirety to the\"\n\" intended running directory. Once there, run the ``Setup.bat`` file \"\n\"inside the ``win-stuff`` folder. This will move the necessary files to \"\n\"the correct folders before Tigase begins operation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:176\nmsgid \"\"\n\"From here, you have a few options how to run Tigase; ``run.bat`` will \"\n\"operate Tigase using a java command, or ``tigase.bat`` which will start \"\n\"Tigase using the wrapper. You may also install Tigase and run it as a \"\n\"service.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:178\nmsgid \"\"\n\"Once this setup is finished, web installer will continue the same from \"\n\":ref:`here<connecttoWebInstall>`.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:4\nmsgid \"Manual Installation in Console Mode\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:6\nmsgid \"\"\n\"Our preferred way to install the Tigase server is using `Web installer \"\n\"<#webinstall>`__ and configuration program which comes with one of the \"\n\"binary packages. Please pick up the latest version of the distribution \"\n\"archive in our `download section <https://tigase.net/downloads>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:8\nmsgid \"\"\n\"In many cases however it is not always possible to use the web installer.\"\n\" In many cases you have just an ssh access or even a direct access in \"\n\"console mode only. We are going to provide a text-only installer in one \"\n\"of the next releases but for the time being you can use our binary \"\n\"packages to install the server manually. Please continue reading to learn\"\n\" how to install and setup the server in a few easy steps…​\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:10\nmsgid \"\"\n\"If you have an old version of the Tigase server running and working and \"\n\"you intend to upgrade it please always backup the old version first.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:14\nmsgid \"\"\n\"Please note that these instructions are for \\\\*nix operating systems, and\"\n\" some modifications may be required for other Operating Systems!\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:18\nmsgid \"Get the Binary Package\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:20\nmsgid \"\"\n\"Have a look at our `download area <https://tigase.net/downloads>`__. \"\n\"Always pick the latest version of the package available. For manual \"\n\"installation either ``zip`` or ``tar.gz`` file is available. Pick one of \"\n\"files with filename looking like: ``tigase-\"\n\"server-<version>-b<build>-<type>.<archive>``, where ``<version>`` is in \"\n\"the form of ``major.minor.bugfix``, ``<type>`` can be either ``dist`` \"\n\"(basic package) or ``dist-max`` (extended set of components) and archive \"\n\"type can be eitehr ``tar.gz`` or ``zip``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:24\nmsgid \"Unpack the Package\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:26\nmsgid \"Unpack the file using command for the tar.gz file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:32\nmsgid \"or for the zip file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:38\nmsgid \"A new directory will be created: **tigase-server-x.y.z-bv/**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:40\nmsgid \"\"\n\"Sometimes after unpacking package on unix system startup script doesn’t \"\n\"have execution permissions. To fix the problem you have to run following \"\n\"command:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:48\nmsgid \"Prepare Configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:50\nmsgid \"If you look inside the new directory, it should like this output:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:70\nmsgid \"At the moment the most important is the etc/ directory with these files:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:88\nmsgid \"Configure tigase.conf\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:90\nmsgid \"\"\n\"Tigase.conf is a file that contains general program operating parameters,\"\n\" and java settings for Tigase to run. For now, the only setting we need \"\n\"to set is the **JAVA_HOME** directory.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:96\nmsgid \"\"\n\"Replace **${JDKPath}** with a path to Java JDK installation on your \"\n\"system.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:100\nmsgid \"Configure config.tdsl\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:102\nmsgid \"\"\n\"You need also to edit the ``config.tdsl`` file. It contains initial \"\n\"parameters normally set by the configuration program. As this is a manual\"\n\" installation, you will have to edit this document yourself. It contains \"\n\"already a few lines:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:115\nmsgid \"You will need to set a few things in order to get Tigase up and running.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:119\nmsgid \"Step 1: Change config-type\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:122\nmsgid \"\"\n\"Refer to `config-type <#configType>`__ property description for details, \"\n\"but for most operations, change ``setup`` to ``default``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:125\nmsgid \"Step 2: Set virtual host\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:127\nmsgid \"\"\n\"Without a virtual host, your XMPP server has no domain with which to \"\n\"operate. To set a virtual host use the following configuration:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:133\nmsgid \"\"\n\"You have to replace ``hostname`` with a domain name used for your XMPP \"\n\"installation. Let’s say this is **jabber.your-great.net**. Your setting \"\n\"should look like this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:139\nmsgid \"\"\n\"There are many other settings that can be configured :ref:`visit this \"\n\"section for details<tigase41virtualHosts>`.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:142\nmsgid \"Step 3: Set Administrators\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:145\nmsgid \"\"\n\"At least one administrator is required, and once the database is setup \"\n\"will have the default password of ``tigase``. Be sure to change this once\"\n\" you have finished setting up your server. To add admins, use the \"\n\"following line in the ``config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:151\nmsgid \"Step 4: Set databases\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:153\nmsgid \"\"\n\"You will also need to configure connection to the database. First you \"\n\"have to decide what database you want to use: ``Derby``, ``MySQL``, \"\n\"``PostgreSQL``, ``MSSQL``, or ``MondoDB``. Each database will have \"\n\"slightly different configurations. If we are using derby, in a directory \"\n\"called ``tigasedb``, your configuration would look like this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:163\nmsgid \"\"\n\"Consult :ref:`dataSource<dataSource>` property for more configuration \"\n\"info.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:165\nmsgid \"\"\n\"This is enough basic configuration to have your Tigase server \"\n\"installation running.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:169\nmsgid \"Install Database\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:171\nmsgid \"\"\n\"Creating the database is the next step. Previously, we had scripts to \"\n\"handle this process, but we now have the advantage of functions in the \"\n\"``tigase.sh`` script that can be used. Setting up the database can now be\"\n\" done using a single command.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:177\nmsgid \"\"\n\"This command will install tigase using a Derby database on one named \"\n\"``tigasedb`` hosted on ``localhost``. The username and password editing \"\n\"the database is ``tigase_pass`` and ``root``. Note that ``-J`` explicitly\"\n\" adds the administrator, this is highly recommended with the ``-N`` \"\n\"passing the password. You may customize this command as needed, refer to \"\n\"the :ref:`install-schema<install-schema>` section of the documentation \"\n\"for more information.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:179\nmsgid \"On a windows system, you need to call the program directly:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:185\nmsgid \"If this successfully passes, you should see some information printed out\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:279\nmsgid \"\"\n\"Note at the end, the script will output a recommended example file. You \"\n\"may use this in conjunction with your written config file, but some \"\n\"settings may not be set using this configuration. Again, it is only an \"\n\"**EXAMPLE**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:285\nmsgid \"\"\n\"You can start the server using the tigase file found in the scripts sub-\"\n\"directory of Tigase server base directory. There, select the type of \"\n\"linux you have, debian, gentoo, mendriva or redhat. In the root server \"\n\"directory type the following command:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:291\nmsgid \"Where {OS} is your \\\\*nix operating system.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:293\nmsgid \"and you should get the output like this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:302\nmsgid \"Check if it is Working\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:304\nmsgid \"\"\n\"The server is started already but how do you know if it is really working\"\n\" and there were no problems. Have a look in the ``logs/`` directory. \"\n\"There should be a few files in there:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:315\nmsgid \"\"\n\"The first 2 files are the most interesting for us: **tigase-console.log**\"\n\" and **tigase.log.0**. The first one contains very limited information \"\n\"and only the most important entries. Have a look inside and check if \"\n\"there are any **WARNING** or **SEVERE** entries. If not everything should\"\n\" be fine.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:317\nmsgid \"\"\n\"Now you can connect with an XMPP client of your choice with the \"\n\"administrator account you setup earlier.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:4\nmsgid \"Windows Installation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:6\nmsgid \"\"\n\"Tigase XMPP Server can also work on Microsoft Windows systems and \"\n\"servers, although some slight modifications may be necessary to get \"\n\"things ready to run.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:8\nmsgid \"\"\n\"Although you may wish to use command line, take note that commands \"\n\"entered in shell may require quotations in some cases.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:10\nmsgid \"\"\n\"Make sure that you have Java JDK v8 installed on your system prior to \"\n\"installing Tigase. It will also help to fully setup whatever database \"\n\"software you will be using as well.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:13\nmsgid \"Step 1: Initial Setup\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:15\nmsgid \"\"\n\"Download the Tigase XMPP Server archive from `our repository \"\n\"<https://tigase.net/downloads>`__ and extract it to a directory of your \"\n\"choice.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:17\nmsgid \"\"\n\"Once that is completed, enter the directory ``win-stuff`` and run the \"\n\"setup.bat program. This program when run, will extract the necessary \"\n\"files to appropriate places on your computer. The bat file should look \"\n\"like the following:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:30\nmsgid \"Step 2: Starting Server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:32\nmsgid \"\"\n\"To start the server you may use a command prompt from the installation \"\n\"directory\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:66\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:10\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:40\nmsgid \"**Note**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:42\nmsgid \"\"\n\"this may freeze the command window, and will only display output from \"\n\"Tigase.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:44\nmsgid \"Or you may run wrapper.exe or tigase.bat from the GUI.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:47\nmsgid \"2A: Installing as a service\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:49\nmsgid \"\"\n\"The cleanest way to operate Tigase in a Windows environment is to install\"\n\" Tigase as a Service by running the InstallTigaseService.bat program. \"\n\"This will install Tigase as a system service, and now the server can be \"\n\"controlled from the services.msc panel. This allows for stopping, \"\n\"starting, and pausing of Tigase XMPP Server and allowing for graceful \"\n\"shutdowns.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:51\nmsgid \"\"\n\"For a basic installation, MySQL is recommended over Derby DB. For that \"\n\"purpose, we have included a basic installation guide for MySQL on Windows\"\n\" systems here:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:54\nmsgid \"MySQL Database Installation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:56\nmsgid \"\"\n\"The section describes installation and configuration of the MySQL \"\n\"database to work with Tigase server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:58\nmsgid \"\"\n\"Download the binary package from MySQL download area at `mysql.com \"\n\"<http://dev.mysql.com/downloads/mysql/5.0.html#win32>`__. Make sure you \"\n\"select executable proper for your operating system.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:60\nmsgid \"\"\n\"Run the installation program and follow default installation steps. When \"\n\"the installation is complete find the MySQL elements in the Windows Start\"\n\" menu and run the MySQL Configuration Wizard. Follow the wizard and make \"\n\"sure to check settings against the screenshots in the guide below.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:62\nmsgid \"In Welcome window just press 'Next'.(pic.1)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:64\nmsgid \"|sql1|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:134\nmsgid \"sql1\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:66\nmsgid \"\"\n\"In the next window select option: 'Detailed Configuration' and press \"\n\"'Next' (pic. 2)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:68\nmsgid \"|sql2|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:135\nmsgid \"sql2\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:70\nmsgid \"\"\n\"On the next screen select option: 'Server Machine' and press 'Next' (pic.\"\n\" 3)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:72\nmsgid \"|sql3|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:136\nmsgid \"sql3\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:74\nmsgid \"\"\n\"On the forth windows leave the default\\\" 'Multi-functional Database' and \"\n\"press 'Next' (pic. 4)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:76\nmsgid \"|sql4|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:137\nmsgid \"sql4\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:78\nmsgid \"On the step number five just press 'Next' using defaults. (pic. 5)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:80\nmsgid \"|sql5|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:138\nmsgid \"sql5\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:82\nmsgid \"\"\n\"Again, on window 6 select the default - 'Decision Support (DSS)/OLAP' and\"\n\" press 'Next' (pic.6)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:84\nmsgid \"|sql6|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:139\nmsgid \"sql6\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:86\nmsgid \"Make sure you switch OFF the 'Strict mode' and and press 'Next' (pic. 7)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:88\nmsgid \"|sql7|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:140\nmsgid \"sql7\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:90\nmsgid \"\"\n\"On the character encoding page select: 'Manual Selected Default Character\"\n\" set/ Collation' and 'utf8', press 'Next' (pic.8)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:92\nmsgid \"|sql8|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:141\nmsgid \"sql8\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:94\nmsgid \"\"\n\"On next window select 'Include Bin Directory in Windows PATH' and press \"\n\"'Next' (pic.9)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:96\nmsgid \"|sql9|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:142\nmsgid \"sql9\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:98\nmsgid \"\"\n\"On this window just enter the database super user password and make sure \"\n\"you remember it. When ready press 'Next' (pic. 10)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:100\nmsgid \"|sql10|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:143\nmsgid \"sql10\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:102\nmsgid \"\"\n\"This is the last screen. Press 'Execute' to save the configuration \"\n\"parameters. (pic. 11)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:104\nmsgid \"|sql11|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:144\nmsgid \"sql11\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:106\nmsgid \"\"\n\"When the configuration is saved you can repeat all the steps and change \"\n\"settings at any time by running: **START ⇒ Programs ⇒ MYSQL⇒ MYSQL serwer\"\n\" machine⇒ MySQL Server Instance Config Wizard**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:108\nmsgid \"\"\n\"Now we have to setup Tigase database. From the Start menu run the MySQL \"\n\"console and enter all commands below finishing them with **<ENTER>**:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:110\nmsgid \"Create the database:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:116\nmsgid \"Add database user:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:125\nmsgid \"Load Tigase database schema:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:132\nmsgid \"\"\n\"When the system is up and running you can connect with any XMPP client \"\n\"(Psi for example) to your server to see if it is working.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:4\nmsgid \"Tigase Server Network Instructions\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:6\nmsgid \"\"\n\"One you have installed Tigase XMPP Server on a machine, you’re going to \"\n\"want to use it. If you are just using for local communications on a \"\n\"network behind a router, you’re all set. Enjoy and use!\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:8\nmsgid \"\"\n\"However, if you want to have people from other computers outside your \"\n\"network connect to your server, you’re going to have to go through a few \"\n\"more steps to show your server out to the public.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:12\nmsgid \"\"\n\"This guide is merely a recommendation of how to get a local server to be \"\n\"open to incoming communications. Any time you open ports, or take other \"\n\"security measures you risk compromising your network security. These are \"\n\"only recommendations, and may not be appropriate for all installations. \"\n\"Please consult your IT Security expert for securing your own \"\n\"installation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:14\nmsgid \"\"\n\"XMPP, being a decentralized communication method, relies on proper DNS \"\n\"records to figure out where and how an XMPP server is setup. Operating an\"\n\" XMPP Server will require you to properly setup DNS routing so not only \"\n\"can clients connect to you, but if you decide to run a federated server \"\n\"and enable server to server communication, you will need to do the same. \"\n\"If you already have a DNS server already, you should have little issue \"\n\"adding these records. If you do not have a DNS setup pointing to your \"\n\"server, you may use a free dynamic name service such as dynu.com.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:17\nmsgid \"A Records\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:19\nmsgid \"\"\n\"You will not be able to use an IP Address or a CNAME record to setup an \"\n\"XMPP Server. While it’s not required, an A record can provide some other \"\n\"benefits such serving as a backup in case the SRV record is not \"\n\"configured right.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:22\nmsgid \"SRV Records\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:24\nmsgid \"\"\n\"You will need to set SRV records both for client-to-server (c2s) \"\n\"communication and, if you plan to use it, server to server (s2s) \"\n\"communication. We recommend both records are entered for every server as \"\n\"some resources or clients will check for both records. For this example \"\n\"we will use tigase.org is our domain, and xmpp as the xmpp server \"\n\"subdomain.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:26\nmsgid \"SRV records have the following form:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:32\nmsgid \"The key is as follows:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:34\nmsgid \"\"\n\"``service``: is the symbolic name of the desired service, in this case it\"\n\" would be *xmpp-client* or *xmpp-server*.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:36\nmsgid \"\"\n\"``protocol``: is the transport protocol, either TCP or UDP, XMPP traffic \"\n\"will take place over *TCP*.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:38\nmsgid \"\"\n\"``name``: the domain name where the server resides, in this case \"\n\"*tigase.org*.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:40\nmsgid \"\"\n\"``TTL``: a numeric value for DNS time to live in milliseconds, by default\"\n\" use *86400*.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:42\nmsgid \"``class``: DNS class field, this is always *IN*.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:44\nmsgid \"\"\n\"``priority``: the priority of the target host with lower numbers being \"\n\"higher priority. Since we are not setting up multiple SRV records, we can\"\n\" use *0*.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:46\nmsgid \"\"\n\"``weight``: the relative weight for records with the same priority. We \"\n\"can use *5*.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:48\nmsgid \"\"\n\"``port``: the specific TCP or UDP port where the service can be found. In\"\n\" this case it will be *5222* or *5269*.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:50\nmsgid \"\"\n\"``target``: the hostname of the machine providing the service, here we \"\n\"will use *xmpp.tigase.org*.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:52\nmsgid \"For our example server, the SRV records will then look like this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:60\nmsgid \"Tigase and Vhosts\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:62\nmsgid \"\"\n\"If you are running multiple vhosts or subdomains that you wish to \"\n\"separate, you will need another record. In this case an A record will be \"\n\"all you need if you are using default ports. If you are using custom \"\n\"ports, you will need to have a new SRV record for each subdomain.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:65\nmsgid \"Hosting via Tigase.me\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:67\nmsgid \"\"\n\"If you don’t want to do all the hosting yourself, you can still have an \"\n\"XMPP service running in your own domain. The only condition right now is \"\n\"proper DNS service record (SRV) configuration that point to the following\"\n\" DNS address: ``tigase.me``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:69\nmsgid \"\"\n\"We highly encourage using SRV records. If you want to register: **your-\"\n\"domain.tld** on our XMPP service make sure that it resolves correctly:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:72\nmsgid \"Service\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:72\nmsgid \"DNS Type\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:72\nmsgid \"DNS record\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:72\nmsgid \"Comment\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:74\nmsgid \"``_xmpp-client._tcp.your-domain.tld``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:74\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:76\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:78\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:80\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:82\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:84\nmsgid \"SRV\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:74\nmsgid \"``10 0 5222 tigase.me.``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:74\nmsgid \"Basic XMPP\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:76\nmsgid \"``_xmpps-client._tcp.your-domain.tld``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:76\nmsgid \"``10 0 5223 tigase.me.``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:76\nmsgid \"DirectTLS\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:78\nmsgid \"``_xmpp-server._tcp.your-domain.tld``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:78\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:80\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:82\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:84\nmsgid \"``10 0 5269 tigase.me.``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:78\nmsgid \"Federation / s2s connection\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:80\nmsgid \"``_xmpp-server._tcp.muc.your-domain.tld``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:80\nmsgid \"Federation / s2s connection (MUC)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:82\nmsgid \"``_xmpp-server._tcp.mix.your-domain.tld``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:82\nmsgid \"Federation / s2s connection (MIX)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:84\nmsgid \"``_xmpp-server._tcp.pubsub.your-domain.tld``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:84\nmsgid \"Federation / s2s connection (PubSub)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:91\nmsgid \"\"\n\"You can check if the configuration is correct by issuing following \"\n\"commands:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:101\nmsgid \"Now, how do you register your domain with our service?\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:103\nmsgid \"\"\n\"There are a few ways. We recommend checking with the `Add and Manage \"\n\"Domains <#addManageDomain>`__ section of the documentation on setting \"\n\"that up. If you cannot or don’t want to do it on your own, the way \"\n\"described in the guide please send us a message, either via XMPP to \"\n\"admin@tigase.im or the contact form requesting new domain. User \"\n\"registration is available via in-band registration protocol. You can also\"\n\" specify whether you want to allow anonymous authentication to be \"\n\"available for your domain and you can specify maximum number of users for\"\n\" your domain.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:107\nmsgid \"Providing certificate\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:109\nmsgid \"\"\n\"It’s also encouraged to provide dedicated SSL certificate - there are \"\n\"various ways to do it and they are described in :ref:`Installing/Loading \"\n\"Certificate To the Tigase Server<InstallingSSLCertificate>`. You may want\"\n\" to take advantage of free Let’s Encrypt certificates and automate whole \"\n\"upload and renewal process as described in :ref:`Installing LetsEncrypt \"\n\"Certificates in Your Linux System<LetsEncryptCertificate>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:113\nmsgid \"Checking setup\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:115\nmsgid \"\"\n\"If you have a cell phone on a separate network with an XMPP client, you \"\n\"can now try to login to test the server. If that is not handy, you can \"\n\"use an online tool to check proper DNS records such as kingant’s: \"\n\"https://kingant.net/check_xmpp_dns/ and it will tell you if anything is \"\n\"missing.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:119\nmsgid \"Ports description\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:122\nmsgid \"\"\n\"Once your server is setup, you may need to open at least two ports. By \"\n\"default XMPP communication happens on ports 5222/5269, to which point SRV\"\n\" records. Other ports used by the server are:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:124\nmsgid \"``3478`` - TURN or STUN, plain socket, TCP and UDP\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:126\nmsgid \"``5349`` - TURN or STUN, over TLS, TCP and UDP\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:128\nmsgid \"``5222`` - incoming client to server XMPP connections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:130\nmsgid \"\"\n\"``5223`` - incoming client to server XMPP connections over TLS/SSL, \"\n\"including DirectTLS\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:132\nmsgid \"``5269`` - default s2s port, i.e.: federation support\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:134\nmsgid \"``5277`` - inter-cluster communication\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:136\nmsgid \"``5280`` - default BOSH connections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:138\nmsgid \"``5290`` - default WebSocket connections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:140\nmsgid \"``5291`` - default WebSocket connections over TLS/SSL\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:142\nmsgid \"\"\n\"``8080`` - for HTTP server (web-based setup, REST API, file upload \"\n\"extension, etc.)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:144\nmsgid \"``9050`` - JMX Monitoring\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:146\nmsgid \"\"\n\"If for any reason you can’t use default ports and have to change them \"\n\"it’s possible to point SRV records those ports. Please keep in mind, that\"\n\" you have to open those ports for incoming connections in your firewall. \"\n\"In case you are using ``iptables`` you can use following command to \"\n\"include those ports in your rules:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:159\nmsgid \"\"\n\"Both ports should be setup to use TCP only. If for any reason you want to\"\n\" make service available for different ports you can:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:161\nmsgid \"change ports in Tigase configuration and update DNS SRV records;\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:163\nmsgid \"\"\n\"forward those ports to default Tigase ports (this is especially useful \"\n\"under \\\\*nix operating system if you want to utilize ports lower than \"\n\"``1024`` while running, as recommended, Tigase service from user account \"\n\"- there is a limitation and user accounts can bind to ports lower than \"\n\"``1024``), for example using ``iptables`` rules (in following example we \"\n\"are making available Tigase SSL websocket port available under port \"\n\"``443``, which is usually opened in corporate firewalls):\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:4\nmsgid \"Tigase Script Selection\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:6\nmsgid \"\"\n\"As mentioned in each of the quick start sections, each distribution of \"\n\"Tigase XMPP server comes with a number of scripts that are customized for\"\n\" different versions of Linux.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:8\nmsgid \"init.d chart\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:11\nmsgid \"Operating system\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:11\nmsgid \"init.d file path\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:11\nmsgid \"Types of Operating Systems\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:13\nmsgid \"Systemd\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:13\nmsgid \"``tigase-server/scripts/systemd/*``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:13\nmsgid \"Systemd-based distributions\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:15\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:197\nmsgid \"Debian\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:15\nmsgid \"``tigase-server/scripts/debian/tigase.init.d``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:15\nmsgid \"Knoppix, Ubuntu (before v15.04), Raspbian or Duvian\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:17\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:90\nmsgid \"Gentoo\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:17\nmsgid \"``tigase-server/scripts/gentoo/init.d/tigase``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:17\nmsgid \"CoreOS (before v94.0.0), Tin Hat Linux or other \\\\*too based systems\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:19\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:110\nmsgid \"Mandriva\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:19\nmsgid \"``tigase-server/scripts/mandriva/init.d/tigase``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:19\nmsgid \"Specific init.d file for Mandriva Linux\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:21\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:138\nmsgid \"Redhat\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:21\nmsgid \"``tigase-server/scripts/redhat/init.d/tigase``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:21\nmsgid \"\"\n\"RedHat (before v7.0) and other RPM based linux derivatives like CentOS \"\n\"(before v.7.14), openSUSE (before v12.2)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:28\nmsgid \"\"\n\"If your operating system is a systemd-based linux distribution, we \"\n\"recommend to use systemd service scripts. It may be possible to use (in \"\n\"this case legacy) ``init.d`` startup files as before, but usage of \"\n\"systemd startup scripts will allow better control of the startup process \"\n\"and will even allow for automatic restart of the Tigase XMPP Server in \"\n\"the case of JVM crash.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:32\nmsgid \"Configuration: For Linux Distributions using systemd\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:34\nmsgid \"\"\n\"To set up Tigase XMPP Server as a system service it is required to copy \"\n\"``tigase-server.service`` file to ``/etc/systemd/system/`` directory\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:40\nmsgid \"This file contains following parameters which may need to be adjusted:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:42\nmsgid \"\"\n\"``User`` - Specifies the user that will run the program. This should be a\"\n\" user with SU permissions.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:44\nmsgid \"\"\n\"``WorkingDirectory`` - Specifies installation directory *(default: \"\n\"``/home/tigase/tigase-server``)*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:46\nmsgid \"\"\n\"``ExecStart`` - Specifies startup command *(default: runs \"\n\"``scripts/tigase.sh start etc/tigase.conf`` in the Tigase installation \"\n\"directory)*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:48\nmsgid \"\"\n\"``ExecStop`` - Specifies shutdown command *(default: runs \"\n\"``scripts/tigase.sh stop etc/tigase.conf`` in the Tigase installation \"\n\"directory)*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:50\nmsgid \"\"\n\"``PIDFile`` - Specifies location of the PID file *(default: \"\n\"``logs/tigase.pid`` file in the Tigase installation directory)*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:52\nmsgid \"\"\n\"It is also required to copy options file ``tigase-server`` to \"\n\"``/etc/default/`` directory\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:58\nmsgid \"With those files in place you need to reload ``systemctl`` daemon\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:68\nmsgid \"\"\n\"If you are upgrading from the previous version of the Tigase XMPP Server \"\n\"which was not running as the systemd system service it is required to \"\n\"uninstall old service and remove old service files.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:72\nmsgid \"Configuration: For All Linux Distributions\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:74\nmsgid \"\"\n\"Once you’ve located the appropriate distribution scripts (please take a \"\n\"look at the table above), copy it to your system’s init.d folder (usually\"\n\" it’s ``/etc/init.d/``):\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:80\nmsgid \"You may also need to make it executable:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:86\nmsgid \"\"\n\"It is recommended that you open the script files or configuration files \"\n\"as some have some parameters that you will need to specify.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:92\nmsgid \"The conf.d script must contain the following parameters:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:100\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:124\nmsgid \"The following should be configured:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:102\nmsgid \"``TIGASE_HOME`` - Specifies the Tigase Server installation directory.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:104\nmsgid \"\"\n\"``TIGASE_USER`` - Specifies the user that will run the program. This \"\n\"should be a user with SU permissions.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:106\nmsgid \"\"\n\"``TIGASE_CONF`` - The location of tigase.conf file, relative to the \"\n\"``TIGASE_HOME`` directory.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:112\nmsgid \"Mandriva has a single init.d file, however it should be configured:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:126\nmsgid \"``JAVA_HOME`` - The location of your JDK Installation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:128\nmsgid \"``TIGASE_DIR`` - Tigase Server installation directory.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:130\nmsgid \"\"\n\"``tigase`` - The location of your tigase.sh script. This should not need \"\n\"adjusting if you maintain the default file structure.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:132\nmsgid \"\"\n\"``config`` - The location of your tigase.conf file. This should not need \"\n\"adjusting if you maintain the default file structure.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:134\nmsgid \"``pid`` file will be stored in ``/var/run/ser.pid``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:140\nmsgid \"Similar to Mandriva, you will need to configure the init.d file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:162\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:218\nmsgid \"``USERNAME`` - Username running Tigase, should have su permissions.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:164\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:220\nmsgid \"``USERGROUP`` - The usergroup of the username.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:166\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:222\nmsgid \"``NAME`` - OS name for Tigase program.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:168\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:224\nmsgid \"``DESC`` - Optional description.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:170\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:226\nmsgid \"\"\n\"``TIGASE_HOME`` - The location of your Tigase Server installation \"\n\"directory.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:172\nmsgid \"\"\n\"``TIGASE_LIB`` - The location of your Tigase Jars folder, you should not \"\n\"need to adjust this if you set ``TIGASE_HOME`` properly, and maintain the\"\n\" default file structure.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:174\nmsgid \"\"\n\"``TIGASE_CONFIG`` - The location of your tigase.conf file relative to \"\n\"``TIGASE_HOME``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:176\nmsgid \"\"\n\"``TIGASE_OPTIONS`` - Legacy options for Tigase, most are now handled in \"\n\"``config.tdsl`` or tigase.conf.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:178\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:232\nmsgid \"\"\n\"``TIGASE_PARAMS`` - Parameters passed to command line when launching \"\n\"Tigase.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:180\nmsgid \"\"\n\"``PIDFILE`` - Location of Tigase PID file if you wish to use custom \"\n\"directory. Default will be located in /logs or /var/temp directory.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:182\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:236\nmsgid \"\"\n\"``TIGASE_CONSOLE_LOG`` - Location of Tigase Server console log file if \"\n\"you wish to use a custom directory. Default will be located in /logs \"\n\"directory, failing that /dev/null.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:184\nmsgid \"\"\n\"After you’ve copied the script, in order to install sysinit script you \"\n\"have to add it to the configuration:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:190\nmsgid \"Service can be enabled or disabled service with:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:199\nmsgid \"\"\n\"As with other distributions you should copy init.d script to the correct \"\n\"location. Afterwards it should be edited and correct values for variables\"\n\" need to be set:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:228\nmsgid \"\"\n\"``TIGASE_CONFIG`` - The location of your tigase-server.xml file relative \"\n\"(old configuration format)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:230\nmsgid \"\"\n\"``TIGASE_OPTIONS`` - command line arguments passed to Tigase server \"\n\"(which may include path to ``init.properies`` (if correct ``tigase.conf``\"\n\" configuration will be found then it will translate to \"\n\"``TIGASE_OPTIONS=\\\" --property-file etc/config.tdsl \\\"``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:234\nmsgid \"\"\n\"``PIDFILE`` - Location of Tigase PID file if you wish to use custom \"\n\"directory. Default will be located in ``/var/run/tigase/tigase.pid`` or \"\n\"under (in this case relative to tigase home directory)\\\\ \"\n\"``logs/tigase.pid``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:238\nmsgid \"\"\n\"Afterwards we need to install service in the system with following \"\n\"command:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:246\nmsgid \"Running Tigase as a system service\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:248\nmsgid \"\"\n\"There are a number of benefits to running Tigase as a service, one of \"\n\"which is to ensure that the program will run even in the event of a power\"\n\" outage or accidental server restart, Tigase will always be up and \"\n\"running.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:252\nmsgid \"For systemd-based linux distributions\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:254\nmsgid \"\"\n\"Once installation is complete you may start Tigase as a typical systemd \"\n\"service using following command:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:260\nmsgid \"To stop it, you may run following command:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:266\nmsgid \"\"\n\"It is also possible to enable service, to make it start during startup of\"\n\" the operating system:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:274\nmsgid \"For other linux distributions\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:276\nmsgid \"\"\n\"Once installation is complete, you should be able to start Tigase using \"\n\"the following command:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:282\nmsgid \"\"\n\"Tigase should begin running in the background. Since Tigase is now \"\n\"installed as a service, it can be controlled with any of the service \"\n\"commands, such as:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:284\nmsgid \"``service tigase stop``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:286\nmsgid \"``service tigase restart``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:2\nmsgid \"Shutting Down Tigase\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:4\nmsgid \"\"\n\"Although Tigase XMPP Server can be terminated by ending the process, it \"\n\"is preferred and recommended to use it’s own shutdown scripts instead. \"\n\"Not only does this allow for a proper purge of Tigase form the system, \"\n\"but allows for all shutdown functions to operate, such as amending logs \"\n\"and completing statistics. To trigger a shutdown of Tigase server, the \"\n\"following command can be used from the tigase directory:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:10\nmsgid \"\"\n\"You may specify the config file if you want, but it will make no \"\n\"differences\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:12\nmsgid \"This will:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:14\nmsgid \"Begin shutdown thread\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:16\nmsgid \"Stop accepting new connections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:18\nmsgid \"Close all current connections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:20\nmsgid \"Collect runtime statistics\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:22\nmsgid \"Write statistics to log\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:24\nmsgid \"Dump full stacktrace to a file\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:26\nmsgid \"Run GC and clear from memory\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:30\nmsgid \"Shutdown statistics\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:32\nmsgid \"\"\n\"Upon shutdown, statistics for the server’s runtime will be appended to \"\n\"the log file. For a description of the statistics and what they mean, \"\n\"refer to the :ref:`Statistics Description<statsticsDescription>` portion \"\n\"of the documentation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:35\nmsgid \"Shutdown StackTrace Dump\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:37\nmsgid \"\"\n\"To aid with troubleshooting purposes, the full stacktrace will be dumped \"\n\"to a seperate file located at $serverdir/logs/threads-dump.log.# \"\n\"Stacktrace logs will follow the same log file numbering scheme described \"\n\"in :ref:`Log file description<Tigase-Log-Guide>`.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:39\nmsgid \"\"\n\"This feature is enabled by default, however you may disable this by \"\n\"adding the following to your ``config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:46\nmsgid \"Shutting Down Cluster Nodes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:48\nmsgid \"\"\n\"Starting with v8.0.0 you can now shut down individual cluster nodes \"\n\"without shutting down the whole server. This command will use the \"\n\"*SeeOtherHost* strategy to direct traffic to other nodes and update the \"\n\"cluster map to gracefully shut down the single node\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:50\nmsgid \"\"\n\"Shutting down individual nodes can be done VIA Ad-hoc command and fill \"\n\"out the response forms. The command is available from message-router as \"\n\"http://jabber.org/protocol/admin#shutdown.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:2\nmsgid \"Upgrading Tigase to newer version\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:6\nmsgid \"\"\n\"Depending of the deployment we recommend installing Tigase XMPP Server \"\n\"next to the existing one and following with replace of the service once \"\n\"the upgrade finishes correctly.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:9\nmsgid \"Backup your data\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:11\nmsgid \"\"\n\"As with any migration it is **highly recommended** that you backup your \"\n\"repository before conducting any upgrade operations. It can be done via \"\n\"simple SQL dump od the database or more elaborate snapshot of the \"\n\"database offered by cloud providers.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:15\nmsgid \"Configuration files to migrate\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:17\nmsgid \"\"\n\"During the upgrade the most important files to migrate to newer versions \"\n\"are:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:19\nmsgid \"`etc/tigase.conf`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:20\nmsgid \"`etc/config.tdsl`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:21\nmsgid \"`etc/jmx.access` and `etc/jmx.password`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:22\nmsgid \"\"\n\"`certs/*` (if configured to use local filesystem, though we recommend to \"\n\"use database for storing certificates)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:25\nmsgid \"Upgrade Database schema\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:27\nmsgid \"\"\n\"Upgrading database schemas is now possible using the ``upgrade-schema`` \"\n\"option. Do this now.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:36\nmsgid \"Your database schema MUST be v8 or conversion will not occur properly!\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:38\nmsgid \"\"\n\"You will be asked for rood credentials. Those can be provided as \"\n\"parameters to `/scripts/tigase.sh upgrade-schema` - please check \"\n\"`./scripts/tigase.sh upgrade-schema --help` for more details\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:40\nmsgid \"Upon success, you should see the following:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:61\nmsgid \"Start Tigase!\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:65\nmsgid \"Help?\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:67\nmsgid \"\"\n\"Both ``upgrade`` commands also have a build in help function, they can be\"\n\" called if needed from the command line. You can also run these commands \"\n\"for help.\"\nmsgstr \"\"\n\n#~ msgid \"\"\n#~ \"There are many other settings that \"\n#~ \"can be configured `visit this section\"\n#~ \" for details <#tigase41virtualHosts>`__.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"Consult `dataSource <#dataSource>`__ property \"\n#~ \"for more configuration info.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"This command will install tigase using\"\n#~ \" a Derby database on one named \"\n#~ \"``tigasedb`` hosted on ``localhost``. The \"\n#~ \"username and password editing the \"\n#~ \"database is ``tigase_pass`` and ``root``. \"\n#~ \"Note that ``-J`` explicitly adds the \"\n#~ \"administrator, this is highly recommended \"\n#~ \"with the ``-N`` passing the password.\"\n#~ \" You may customize this command as\"\n#~ \" needed, refer to the `install-schema\"\n#~ \" <#install-schema>`__ section of the \"\n#~ \"documentation for more information.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"It’s also encouraged to provide \"\n#~ \"dedicated SSL certificate - there are\"\n#~ \" various ways to do it and they\"\n#~ \" are described in `??? \"\n#~ \"<#InstallingSSLCertificate>`__. You may want \"\n#~ \"to take advantage of free Let’s \"\n#~ \"Encrypt certificates and automate whole \"\n#~ \"upload and renewal process as described\"\n#~ \" in `??? <#LetsEncryptCertificate>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"To aid with troubleshooting purposes, \"\n#~ \"the full stacktrace will be dumped \"\n#~ \"to a seperate file located at \"\n#~ \"$serverdir/logs/threads-dump.log.# Stacktrace logs\"\n#~ \" will follow the same log file \"\n#~ \"numbering scheme described in :ref:`Log \"\n#~ \"file description<logs>`.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"To make upgrade process easier it’s \"\n#~ \"possible to utilize `tigase-upgrade.sh \"\n#~ \"<files/tigase-upgrade.sh>`__ \\\\*nix shell \"\n#~ \"script. It permits upgrading to new \"\n#~ \"version (supports downloading version from \"\n#~ \"provided URL).\"\n#~ msgstr \"\"\n\n#~ msgid \"**Java Development Kit (JDK) 11 (LTS)** - We recommend OpenJDK\"\n#~ msgstr \"\"\n\n#~ msgid \"Upgrading to v8.0.0 from v7.1.0\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"There have been a number of \"\n#~ \"changes to the user and auth \"\n#~ \"databases since v7.1.0. As a result, \"\n#~ \"if you are upgrading from older \"\n#~ \"versions, you will need to follow \"\n#~ \"this guide.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"We recommend installing Tigase XMPP \"\n#~ \"Server 8.0.0 in a separate directory.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"As with any migration it is highly\"\n#~ \" recommended that you backup your \"\n#~ \"repository before conducting any upgrade \"\n#~ \"operations.\"\n#~ msgstr \"\"\n\n#~ msgid \"For MySQL databases:\"\n#~ msgstr \"\"\n\n#~ msgid \"Setup Tigase XMPP Server 8.0.0\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"After downloading Tigase XMPP Server \"\n#~ \"8.0.0 from our website, or using \"\n#~ \"wget, extract the files to a \"\n#~ \"separate directory.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"Copy the ``tigase.conf`` and \"\n#~ \"``init.properties`` files from the old \"\n#~ \"directory to v8.0.0 directory.\"\n#~ msgstr \"\"\n\n#~ msgid \"Import the database.\"\n#~ msgstr \"\"\n\n#~ msgid \"Upgrade configuration file\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"Tigase XMPP Server has a utility \"\n#~ \"that can be called using ``upgrade-\"\n#~ \"config`` that will update your old \"\n#~ \"``init.properties`` file and create a \"\n#~ \"new file using DSL.\"\n#~ msgstr \"\"\n\n#~ msgid \"When everything is ready it will printout following information\"\n#~ msgstr \"\"\n\n#~ msgid \"Connect new database\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"Edit your new ``config.tdsl`` file to\"\n#~ \" connect to the new database you \"\n#~ \"created during the import step.\"\n#~ msgstr \"\"\n\n#~ msgid \"You will be asked the following prompts:\"\n#~ msgstr \"\"\n\n#~ msgid \"Upgrade/Restore with a script [experimental!]\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"To make upgrade process easier it’s \"\n#~ \"possible to utilize `tigase-upgrade.sh \"\n#~ \"<https://raw.githubusercontent.com/tigase/tigase-\"\n#~ \"server/master/src/main/restructured/files/tigase-upgrade.sh>`__\"\n#~ \" \\\\*nix shell script. It permits \"\n#~ \"upgrading to new version (supports \"\n#~ \"downloading version from provided URL).\"\n#~ msgstr \"\"\n\n#~ msgid \"It’s usage is as follows:\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"Where: \\\\* ``{upgrade|rollback}`` - defines\"\n#~ \" whether to perform upgrade or \"\n#~ \"rollback to previous version \\\\* \"\n#~ \"``install_package`` - package to which \"\n#~ \"perform upgrade (can be URL) in \"\n#~ \"case of upgrade or backed-up \"\n#~ \"installation (from which we want to \"\n#~ \"restore) in case of rollback \\\\* \"\n#~ \"``install_directory`` - destination directory \"\n#~ \"(both in upgrade and rollback); can \"\n#~ \"be symlink in which case it will\"\n#~ \" be preserved with upgraded/restored path\"\n#~ \" as target \\\\* ``[tar|dir]`` - \"\n#~ \"(optional) type of backup (either simply\"\n#~ \" copy directory or also archive it\"\n#~ \" using ``tar`` command); by default \"\n#~ \"``dir`` is used.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"To upgrade installation to version \"\n#~ \"``tigase-server-8.0.0-SNAPSHOT-b5285-dist-max.tar.gz`` \"\n#~ \"execute the script as follows:\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"To rollback from ``tigase-server-8.0.0\"\n#~ \"-SNAPSHOT-b5264_backup-18-11-05_1712`` backup execute\"\n#~ \" script as follows:\"\n#~ msgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Administration/Release_Notes/Tigase_Release_Notes.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2023-02-16 15:01-0800\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.11.0\\n\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_Release_Notes.rst:2\nmsgid \"Tigase 8.3.0 Release Notes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/tigase-server-current.inc:2\nmsgid \"Tigase XMPP Server 8.4.0 Change notes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:5\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:5\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:5\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:5\n#: ../../Tigase_Administration/Release_Notes/tigase-server-current.inc:5\nmsgid \"Major Changes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:16\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:15\n#: ../../Tigase_Administration/Release_Notes/tigase-server-current.inc:11\nmsgid \"All Minor Features & Behavior Changes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:2\nmsgid \"Tigase XMPP Server 8.3.0 Change notes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:7\nmsgid \"This version requires JDK17 to run\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:8\nmsgid \"Added support for mam2#extended [#mam-73]\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:9\nmsgid \"\"\n\"Rework certificate generation to utilise `keygen` tool instead of using \"\n\"`sun.*` API unavailable under JDK17\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:10\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:45\nmsgid \"\"\n\"Added support for XEP-0440 SASL Channel Binding Type Capability and fixed\"\n\" and reenabled `SCRAM-*-PLUS SASL` mechanisms [#server-1335]\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:11\nmsgid \"\"\n\"Added initial,preview support for SASL2 and Bind2 (disabled by default) -\"\n\" to enable, activate beans `'urn:xmpp:bind:0'` and `'urn:xmpp:sasl:2'` in\"\n\" `'sess-man'` [#server-1332]\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:17\nmsgid \"\"\n\"Increased network socket buffer from 2K to 4K to improve performance when\"\n\" reading data from socket. It can increase somewhat memory usage \"\n\"proportionally to number of concurrent user connections. It's possible to\"\n\" configure size of this buffer using `socket-buffer-size` property - \"\n\"please see documentation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:18\nmsgid \"\"\n\"Add configuration to log size generated by LoggerTask in Monitor and \"\n\"decrease default from 1M to 50K; Disable serialisation of monitor events;\"\n\" #servers-372\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:19\nmsgid \"Add DOAP file; update documentation with supported features; #server-1076\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:20\nmsgid \"\"\n\"Fix issue with NPE in JabberIqAuth plugin when no password was presented \"\n\"due to missing return statement; fixed similar issue where, after closing\"\n\" the connection, the execution of the code wasn't terminated in \"\n\"JabberIqAuth and SaslAuth plugins #server-1317\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:21\nmsgid \"\"\n\"Add support for XEP-0398 to feature list and updated list of supported \"\n\"features; #server-1316\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:22\nmsgid \"\"\n\"Change try-catch statement in database schema loader to better catch edge\"\n\" cases; #serverdist-10\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:23\nmsgid \"\"\n\"Remove wildcard certificate generation (as main DN) in certificate \"\n\"container to avoid issues that it entails (inability to override such \"\n\"self-signed certificate via ad-hoc commands!). Wildcards are now properly\"\n\" handled by CertificateGenerateor and are included correctly as SAN in \"\n\"addition to DN for main domain; Fix handling \\\"default\\\" certificates \"\n\"from repository; #server-1279\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:24\nmsgid \"\"\n\"Change default watchdog ping from (forbidden by RFC) whitespace to xmpp; \"\n\"add warning if someone configures it as whitespace either way; \"\n\"server-1318\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:25\nmsgid \"Improve XMPPDomBuilderHandler logging; #server-1323\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:26\nmsgid \"\"\n\"Improved Stream Management code responsible for generating `<r/>` \"\n\"requests #server-1324 (#150)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:27\nmsgid \"\"\n\"Added `socket-buffer-size` option to `ConnectionManager` to configure \"\n\"`SO_RCVBUF` separately from internal network buffers #server-1325\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:28\nmsgid \"\"\n\"Increased socket-buffer-size for client-to-server and intercluster \"\n\"connections and added documentation #server-1325\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:29\nmsgid \"Fix MAX_PAUSE property name; #server-1326\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:30\nmsgid \"Updated implementation of XEP-0377: Spam Reporting #server-1327\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:31\nmsgid \"\"\n\"Fixed issue with errors being sent for unexpected <iq type=result/> \"\n\"stanzas #server-1328\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:32\nmsgid \"Improved exceptions handling in StanzaProcessor #server-1328\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:33\nmsgid \"Switch from jtds to MS own jdbc driver; #serverdist-12\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:34\nmsgid \"\"\n\"Prevent re-delivery of certain S2S packets (sasl, features, dialback, \"\n\"etc) as it doesn't make sense; #server-1320\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:35\nmsgid \"\"\n\"Adjust log levels to avoid WARNINGS during startup for regular messages; \"\n\"#server-1115\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:36\nmsgid \"Add 'active in last x' statistic; #server-1281\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:37\nmsgid \"Include option to restart JVM on OOM (off by default)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:38\nmsgid \"\"\n\"Correctly process packets from mobile queue instead of re-adding \"\n\"currently filtered packet; #server-1331\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:39\nmsgid \"\"\n\"Improvements to NativeMemoryTracking implementation with units; \"\n\"documentation; #server-1330\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:40\nmsgid \"Improve MAM logging; #servers-384\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:41\nmsgid \"Only count stanzas in StreamManagement #server-1333\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:42\nmsgid \"Fixed advertisement stream features for unauthorized stream #server-1334\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:43\nmsgid \"\"\n\"Fixed NPE during preparing stream features when connection is already \"\n\"closed #server-1334\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:44\nmsgid \"\"\n\"Added initial support for SASL2 and Bind2 (preview feature, disabled by \"\n\"default) #server-1332\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:46\nmsgid \"Fixed NPE during enabling of stream resumption #server-1332\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:47\nmsgid \"\"\n\"Fixed sending block/unblock presences from blocking command for domain \"\n\"#server-1336\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:48\nmsgid \"\"\n\"Better default for lastXmppPacketReceivedTime member to avoid WatchDog \"\n\"closing connection before lastXmppPacketReceivedTime is set; #server-1337\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:49\nmsgid \"\"\n\"Add proper addressing validation in S2S connection and allow connections \"\n\"without 'from' set; #server-1338\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:2\nmsgid \"Tigase XMPP Server 8.2.0 Change notes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:7\nmsgid \"\"\n\"**Improvements to s2s connection**: Version 8.2.0 brings a lot of \"\n\"improvements related to s2s connectivity: support for TLS1.3, improved \"\n\"logic during authentication and stream negotiation solving connectivity \"\n\"issues with various deployments\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:9\nmsgid \"\"\n\"**Better handling of certificates**: It’s now possible to store \"\n\"certificates in the database making it easier to manage them in clustered\"\n\" environment.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:11\nmsgid \"Deprecation of ``Element`` based events in favour of Object based events\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:13\nmsgid \"Improved performance: reduced memory usage and decrease startup time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:18\nmsgid \"\"\n\"`#server-1050 <https://projects.tigase.net/issue/server-1050>`__: \"\n\"Database installation without root credentials\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:20\nmsgid \"\"\n\"`#server-1062 <https://projects.tigase.net/issue/server-1062>`__: \"\n\"Deprecate Element based Event-bus\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:22\nmsgid \"\"\n\"`#server-1097 <https://projects.tigase.net/issue/server-1097>`__: It’s \"\n\"not possible to configure additional PacketFilters\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:24\nmsgid \"\"\n\"`#server-1101 <https://projects.tigase.net/issue/server-1101>`__: \"\n\"Enabling TLS1.3 causes s2s connections to fail\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:26\nmsgid \"\"\n\"`#server-1102 <https://projects.tigase.net/issue/server-1102>`__: Add \"\n\"possibility to extend MAM to MAM:2\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:28\nmsgid \"\"\n\"`#server-1105 <https://projects.tigase.net/issue/server-1105>`__: Enhance\"\n\" Add SSL Certificate ad-hoc with option to set default\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:30\nmsgid \"\"\n\"`#server-1119 <https://projects.tigase.net/issue/server-1119>`__: Use \"\n\"database for certificate storage instead of filesystem\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:32\nmsgid \"\"\n\"`#server-1120 <https://projects.tigase.net/issue/server-1120>`__: \"\n\"JabberIqRegister should allow enforcing both CAPTCHA and e-mail\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:34\nmsgid \"\"\n\"`#server-1132 <https://projects.tigase.net/issue/server-1132>`__: Don’t \"\n\"use s2s socket if only one-direction works\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:36\nmsgid \"\"\n\"`#server-1142 <https://projects.tigase.net/issue/server-1142>`__: After \"\n\"registration inform the client that the account activation (email) is \"\n\"required\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:38\nmsgid \"\"\n\"`#server-1158 <https://projects.tigase.net/issue/server-1158>`__: \"\n\"Establishing JMX connection to the server causes excessive memory \"\n\"allocation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:40\nmsgid \"\"\n\"`#server-1162 <https://projects.tigase.net/issue/server-1162>`__: Allow \"\n\"interfaces in @ConfigField\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:42\nmsgid \"\"\n\"`#server-1170 <https://projects.tigase.net/issue/server-1170>`__: TLS \"\n\"infinity loop impacts Tigase XMPP Server performance\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:44\nmsgid \"\"\n\"`#server-1175 <https://projects.tigase.net/issue/server-1175>`__: \"\n\"Connection with diebesban.de stopped with invalid-namespace error\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:46\nmsgid \"\"\n\"`#server-1177 <https://projects.tigase.net/issue/server-1177>`__: Ability\"\n\" to change log level during runtime\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:48\nmsgid \"\"\n\"`#server-1178 <https://projects.tigase.net/issue/server-1178>`__: Remove \"\n\"``online_status`` from the repository\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:50\nmsgid \"\"\n\"`#server-1179 <https://projects.tigase.net/issue/server-1179>`__: Add \"\n\"support for {clusterNode} in XEP-0215 host field\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:52\nmsgid \"\"\n\"`#server-1181 <https://projects.tigase.net/issue/server-1181>`__: \"\n\"NoSuchElementException in MaxDailyCounterQueue\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:54\nmsgid \"\"\n\"`#server-1182 <https://projects.tigase.net/issue/server-1182>`__: NPE \"\n\"while processing <iq type=\\\"result\\\"/> without existing session\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:56\nmsgid \"\"\n\"`#server-1187 <https://projects.tigase.net/issue/server-1187>`__: \"\n\"SchemaLoader should not print passwords in the logs (URL logs)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:58\nmsgid \"\"\n\"`#server-1192 <https://projects.tigase.net/issue/server-1192>`__: \"\n\"Obfuscate repository passwords\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:60\nmsgid \"\"\n\"`#server-1190 <https://projects.tigase.net/issue/server-1190>`__: \"\n\"Executing EditUser on non-existen’t user causes creation of the user\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:62\nmsgid \"\"\n\"`#server-1193 <https://projects.tigase.net/issue/server-1193>`__: Push \"\n\"notifications are sent for groupchat messages without <body/>\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:64\nmsgid \"\"\n\"`#server-1197 <https://projects.tigase.net/issue/server-1197>`__: \"\n\"Infinite loop while cutting body of encrypted push notification to fit \"\n\"the push notifications limit\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:66\nmsgid \"\"\n\"`#server-1199 <https://projects.tigase.net/issue/server-1199>`__: Don’t \"\n\"send any packets until s2s stream negotiation is finished\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:68\nmsgid \"\"\n\"`#server-1200 <https://projects.tigase.net/issue/server-1200>`__: Use \"\n\"proper size of network buffers for high-throughput connections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:70\nmsgid \"\"\n\"`#server-1203 <https://projects.tigase.net/issue/server-1203>`__: Handing\"\n\" error packets in CIDConnections.sendPacketsBack\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:72\nmsgid \"\"\n\"`#server-1217 <https://projects.tigase.net/issue/server-1217>`__: Prevent\"\n\" performing schema upgrade concurrently\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:74\nmsgid \"\"\n\"`#server-1219 <https://projects.tigase.net/issue/server-1219>`__: Use all\"\n\" JDBC URI parameters from config.tdsl when performing database upgrade.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:76\nmsgid \"\"\n\"`#server-1222 <https://projects.tigase.net/issue/server-1222>`__: Add \"\n\"support for XEP-0377: Spam Reporting\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:78\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:80\nmsgid \"\"\n\"`#server-1229 <https://projects.tigase.net/issue/server-1229>`__: \"\n\"Enabling CAPTCHA or e-mail for JabberIqRegister breaks password changing \"\n\"functionality.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:82\nmsgid \"\"\n\"`#server-1233 <https://projects.tigase.net/issue/server-1233>`__: Add \"\n\"option to CertificateRepository to load certificates from the filesystem\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:84\nmsgid \"\"\n\"`#server-1234 <https://projects.tigase.net/issue/server-1234>`__: Roster \"\n\"API improvements\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:86\nmsgid \"\"\n\"`#server-1237 <https://projects.tigase.net/issue/server-1237>`__: Rework \"\n\"CertificateRepository so items are stored individually\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:88\nmsgid \"\"\n\"`#server-1238 <https://projects.tigase.net/issue/server-1238>`__: Can’t \"\n\"set MOTD via ad-hoc.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:90\nmsgid \"\"\n\"`#server-1243 <https://projects.tigase.net/issue/server-1243>`__: Include\"\n\" wait-for-it.sh script in base distribution\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:92\nmsgid \"\"\n\"`#server-1245 <https://projects.tigase.net/issue/server-1245>`__: \"\n\"MethodStatistics doesn’t work well for interfaces with overloaded methods\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:94\nmsgid \"\"\n\"`#server-1251 <https://projects.tigase.net/issue/server-1251>`__: Can’t \"\n\"initialise MAM processor with default installation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:96\nmsgid \"\"\n\"`#server-1252 <https://projects.tigase.net/issue/server-1252>`__: Remove \"\n\"select row_count() from Tig_OfflineMessages_DeleteMessage\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:98\nmsgid \"\"\n\"`#server-1253 <https://projects.tigase.net/issue/server-1253>`__: It \"\n\"seems that 'expired-processor' doesn’t remove periodically expired \"\n\"messages\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:100\nmsgid \"\"\n\"`#server-1254 <https://projects.tigase.net/issue/server-1254>`__: Fix \"\n\"slow startup and shutdown\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:102\nmsgid \"\"\n\"`#server-1258 <https://projects.tigase.net/issue/server-1258>`__: Allow \"\n\"beans to be instantiated without the requirement to reference/inject them\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:104\nmsgid \"\"\n\"`#server-1260 <https://projects.tigase.net/issue/server-1260>`__: \"\n\"UserConnectedEvent should be a cluster event\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:106\nmsgid \"\"\n\"`#server-1261 <https://projects.tigase.net/issue/server-1261>`__: Revise \"\n\"and improve EventBus developer guide\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:108\nmsgid \"\"\n\"`#server-1269 <https://projects.tigase.net/issue/server-1269>`__: SSL \"\n\"issues are hidden by default making it difficult to identify\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:110\nmsgid \"\"\n\"`#server-1273 <https://projects.tigase.net/issue/server-1273>`__: Add \"\n\"option to limit number of concurrently connected resources\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:112\nmsgid \"\"\n\"`#server-1277 <https://projects.tigase.net/issue/server-1277>`__: Fix \"\n\"HUGE out queue in StreamManagementIOProcessor\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:114\nmsgid \"\"\n\"`#server-1278 <https://projects.tigase.net/issue/server-1278>`__: NPE in \"\n\"StreamManagementIOProcessor.serviceStopped\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:116\nmsgid \"\"\n\"`#server-1282 <https://projects.tigase.net/issue/server-1282>`__: \"\n\"XMPPProcessorAbstract.processToUserPacket() responds to IQ result with \"\n\"error\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:118\nmsgid \"\"\n\"`#server-1284 <https://projects.tigase.net/issue/server-1284>`__: Add \"\n\"validation to JabberIqAuth\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:120\nmsgid \"\"\n\"`#server-1285 <https://projects.tigase.net/issue/server-1285>`__: Wrong \"\n\"field type for XEP-0157 entries\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:122\nmsgid \"\"\n\"`#server-1290 <https://projects.tigase.net/issue/server-1290>`__: Improve\"\n\" StringPrep to actually forbid space in localpart/domain as per rfc7622\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:124\nmsgid \"\"\n\"`#server-1292 <https://projects.tigase.net/issue/server-1292>`__: TLS \"\n\"connectivity issue with search.jabber.network\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:126\nmsgid \"\"\n\"`#server-1297 <https://projects.tigase.net/issue/server-1297>`__: Add \"\n\"option to push plugin that would allow to overwrite unencrypted part in \"\n\"(OMEMO) encrypted messages\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:128\nmsgid \"\"\n\"`#server-1303 <https://projects.tigase.net/issue/server-1303>`__: Better \"\n\"handling of \\\"The target is unavailable at this time.\\\" / \"\n\"PacketInvalidTypeException\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:130\nmsgid \"\"\n\"`#server-1305 <https://projects.tigase.net/issue/server-1305>`__: Allow \"\n\"creation of admin user (if not exist) during ``upgrade-schema`` task\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:132\nmsgid \"\"\n\"`#server-1306 <https://projects.tigase.net/issue/server-1306>`__: Fix \"\n\"farge amount of direct memory being used.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:134\nmsgid \"\"\n\"`#server-1307 <https://projects.tigase.net/issue/server-1307>`__: Fix \"\n\"disconnection on MAM sync\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:136\nmsgid \"\"\n\"`#extras-3 <https://projects.tigase.net/issue/extras-3>`__: Add AWS \"\n\"logback and documentation how to use it\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:138\nmsgid \"\"\n\"`#extras-4 <https://projects.tigase.net/issue/extras-4>`__: Unescape and \"\n\"normalise logs in mail notifications before sending them\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:140\nmsgid \"\"\n\"`#extras-7 <https://projects.tigase.net/issue/extras-7>`__: Add email \"\n\"validation during in-band-registration; better handling of mail sending \"\n\"exceptions regarding to non-existent addresses\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:142\nmsgid \"\"\n\"`#extras-9 <https://projects.tigase.net/issue/extras-9>`__: Deprecate \"\n\"mDNS implementation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:144\nmsgid \"\"\n\"`#serverdist-8 <https://projects.tigase.net/issue/serverdist-8>`__: \"\n\"Remove DNS resolution part from XEP-0156 implementation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:2\nmsgid \"Tigase XMPP Server 8.1.0 Change notes and announcement\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:8\nmsgid \"More XMPP extensions\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:10\nmsgid \"\"\n\"Following XMPP guidelines specified in *Compliance Suites* a number of \"\n\"extensions was included in this release:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:12\nmsgid \"\"\n\"**XEP-0157**: Contact Addresses for XMPP Services (`server-995 \"\n\"<https://projects.tigase.net/issue/server-995>`__) that can be configured\"\n\" on per VHost basis (`server-1015 \"\n\"<https://projects.tigase.net/issue/server-1015>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:14\nmsgid \"\"\n\"**XEP-0398**: User Avatar to vCard-Based Avatars Conversion (`server-1017\"\n\" <https://projects.tigase.net/issue/server-1017>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:16\nmsgid \"\"\n\"**XEP-0156**: Discovering Alternative XMPP Connection Methods - Tigase \"\n\"already supported handling DNS queries and standardised our \"\n\"``webservice`` to XEP-0156 (`http-76 \"\n\"<https://projects.tigase.net/issue/http-76>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:18\nmsgid \"\"\n\"**XEP-0410**: MUC Self-Ping (Schrödinger’s Chat) (`muc-122 \"\n\"<https://projects.tigase.net/issue/http-76>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:20\nmsgid \"\"\n\"**XEP-0153**: vCard-Based Avatars - added support for setting **vCard \"\n\"avatar for MUC rooms** (`muc-112 \"\n\"<https://projects.tigase.net/issue/http-76>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:22\nmsgid \"\"\n\"**XEP-0411**: Bookmarks Conversion (`pubsub-79 \"\n\"<https://projects.tigase.net/issue/http-76>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:24\nmsgid \"\"\n\"**XEP-0157**: Contact Addresses for XMPP Services (`server-995 \"\n\"<https://projects.tigase.net/issue/server-995>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:29\nmsgid \"Improved connectivity with other servers\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:31\nmsgid \"\"\n\"``SASL-EXTERNAL`` mechanism was added for server-to-server (federated, \"\n\"s2s) connections greatly improving compliance with XMPP network. It’s \"\n\"possible to use both SASL-EXTERNAL and Diallback depending on support in \"\n\"other servers.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:36\nmsgid \"Better security & privacy\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:38\nmsgid \"\"\n\"When it comes to connectivity, Tigase XMPP Server sported **Hardened \"\n\"Mode** that adjusted networking security settings (supported protocols, \"\n\"cipher suites and keys' length where applicable). We decided include \"\n\"3-level configuration option for **Hardened Mode** (roughly following \"\n\"*Mozilla’s SSL Configuration Generator*): ``relaxed``, ``secure`` \"\n\"(default) and ``strict`` and to further eliminate cipher suites that are \"\n\"currently considered insecure.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:40\nmsgid \"\"\n\"We also enabled by default our anti-spam plugin and because we like all-\"\n\"things-extensible we created a guide how to create your own pluggable \"\n\"filters for anti-spam-plugin.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:45\nmsgid \"Multiple domains (VHosts) support is even better\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:47\nmsgid \"\"\n\"It was always quite easy to configure and serve multiple domains in \"\n\"Tigase XMPP Server. In this release we made it even better! First of all \"\n\"- we included ``Default`` VHost item, which allows configuring global \"\n\"defaults for the installation on the fly without having to change \"\n\"configuration files and restart the instance.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:49\nmsgid \"\"\n\"Internally, we introduced *VHost Extensions* - a mechanism that allows \"\n\"easy addition of configurable options that can be set on per-domain \"\n\"basis.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:51\nmsgid \"\"\n\"On top of that we reworked how SSL certificates are handled (especially \"\n\"wildcard ones) and now they are loaded and assigned to correct domain \"\n\"automatically - no need to configure *star*-certificates manually \"\n\"anymore.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:56\nmsgid \"Mobile First\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:58\nmsgid \"\"\n\"Notifications send to mobile applications via Apple’s and Google’s push \"\n\"servers using **Tigase’s PUSH component** are now encrypted (`#push-25 \"\n\"<https://projects.tigase.net/issue/push-25>`__), requires compatible \"\n\"clients)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:60\nmsgid \"\"\n\"MUC component now allows users to register permanent nickname, which \"\n\"makes it possible to receive PUSH notifications even if our client \"\n\"disconnects and is offline (`#muc-115 \"\n\"<https://projects.tigase.net/issue/muc-115>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:65\nmsgid \"Installation & management\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:67\nmsgid \"\"\n\"The (web) installer was simplified making setting up and configuring \"\n\"Tigase even easier (`#http-78 \"\n\"<https://projects.tigase.net/issue/http-78>`__) - now it’s only needed to\"\n\" select desired database, provide it’s details and eventually adjust \"\n\"which components and plugins should be enabled or disabled, but we \"\n\"believe that provided defaults should work well in most of the cases.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:69\nmsgid \"\"\n\"After the installation and startup, it’s possible to see basic instance \"\n\"state via web browser either opening ``/server/`` endpoint (`#server-1164\"\n\" <https://projects.tigase.net/issue/server-1164>`__), or local file from \"\n\"``logs/server-info.html``) and manage the installation using Admin WebUI,\"\n\" that received slight visual face-lift (`#http-90 \"\n\"<https://projects.tigase.net/issue/http-90>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:74\nmsgid \"Noteworthy\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:76\nmsgid \"\"\n\"Startup time was significantly reduced due to improvements of creating \"\n\"repository pools (`#server-1149 \"\n\"<https://projects.tigase.net/issue/server-1149>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:78\nmsgid \"\"\n\"Multi-thread, highly concurrent script execution was improved \"\n\"(`#server-1154 <https://projects.tigase.net/issue/server-1154>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:80\nmsgid \"\"\n\"StreamManagement was available, but in this version we decided to enabled\"\n\" it by default.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:82\nmsgid \"\"\n\"More places offers support for `XEP-0059: Result Set Management \"\n\"<https://xmpp.org/extensions/xep-0059.html>`__ - namely PubSub nodes \"\n\"discovery and ``jabber:iq:serach``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:84\nmsgid \"\"\n\"`Publishing Options <https://xmpp.org/extensions/xep-0060.html#publisher-\"\n\"publish-options>`__ were added to PubSub (`#pubsub-75 \"\n\"<https://projects.tigase.net/issue/pubsub-75>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:304\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:87\nmsgid \"New Minor Features & Behavior Changes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:89\nmsgid \"\"\n\"`server-918 <https://projects.tigase.net/issue/server-918>`__: AWS obtain\"\n\" public IP and/or DNS address of the EC2 instance\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:91\nmsgid \"\"\n\"`server-985 <https://projects.tigase.net/issue/server-985>`__: Add \"\n\"support for SCRAM-SHA-512(-PLUS)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:93\nmsgid \"\"\n\"`spam-8 <https://projects.tigase.net/issue/spam-8>`__: Enable spam \"\n\"processor by default\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:95\nmsgid \"\"\n\"`server-1012 <https://projects.tigase.net/issue/server-1012>`__: \"\n\"UserDomainFilter.groovy fails to load\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:97\nmsgid \"\"\n\"`server-1014 <https://projects.tigase.net/issue/server-1014>`__: Can’t \"\n\"upgrade from 8.0.0GA to 8.1.0-SNAPSHOT\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:99\nmsgid \"\"\n\"`server-798 <https://projects.tigase.net/issue/server-798>`__: Limit \"\n\"number of messages that are stored in DB per user within a period of time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:101\nmsgid \"\"\n\"`server-827 <https://projects.tigase.net/issue/server-827>`__: Seperate \"\n\"Component-based statistics\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:103\nmsgid \"\"\n\"`server-1026 <https://projects.tigase.net/issue/server-1026>`__: NPE: in \"\n\"JabberIqRegister/EmailConfirmationSender\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:105\nmsgid \"\"\n\"`pubsub-82 <https://projects.tigase.net/issue/pubsub-82>`__: NPE in \"\n\"RetrieveItemsModule\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:107\nmsgid \"\"\n\"`tigaseim-78 <https://projects.tigase.net/issue/tigaseim-78>`__: IPv6 \"\n\"connectivity issue\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:109\nmsgid \"\"\n\"`server-239 <https://projects.tigase.net/issue/server-239>`__: OSGi mode \"\n\"- exceptions in logs\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:111\nmsgid \"\"\n\"`server-1020 <https://projects.tigase.net/issue/server-1020>`__: Enable \"\n\"stream management by default\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:113\nmsgid \"\"\n\"`pubsub-83 <https://projects.tigase.net/issue/pubsub-83>`__: NPE in \"\n\"PublishItemModule\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:115\nmsgid \"\"\n\"`pubsub-81 <https://projects.tigase.net/issue/pubsub-81>`__: Exception \"\n\"during execution of event: \"\n\"tigase.pubsub.modules.PresenceCollectorModule.PresenceChangeEvent\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:117\nmsgid \"\"\n\"`server-1021 <https://projects.tigase.net/issue/server-1021>`__: NPE: \"\n\"Cannot update BruteForceLocker\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:119\nmsgid \"\"\n\"`server-826 <https://projects.tigase.net/issue/server-826>`__: \"\n\"UserRepository caches force synchronization even if caching is disabled\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:121\nmsgid \"\"\n\"`server-958 <https://projects.tigase.net/issue/server-958>`__: Add \"\n\"timeout for opened TCP connections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:123\nmsgid \"\"\n\"`server-1029 <https://projects.tigase.net/issue/server-1029>`__: Read \"\n\"receipients are not copied via carbons\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:125\nmsgid \"\"\n\"`server-1015 <https://projects.tigase.net/issue/server-1015>`__: Allow \"\n\"configuring XEP-0157: Contact Addresses on per VHost basis\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:127\nmsgid \"\"\n\"`pubsub-65 <https://projects.tigase.net/issue/pubsub-65>`__: RSM and \"\n\"jabber:search for pubsub discovery\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:129\nmsgid \"\"\n\"`server-1030 <https://projects.tigase.net/issue/server-1030>`__: NPE in \"\n\"VCardTemp when processing initial presence\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:131\nmsgid \"\"\n\"`http-72 <https://projects.tigase.net/issue/http-72>`__: Change Content-\"\n\"Disposition from attachment to inline\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:133\nmsgid \"\"\n\"`server-1045 <https://projects.tigase.net/issue/server-1045>`__: NPE in \"\n\"DiscoExtensionsForm\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:135\nmsgid \"\"\n\"`server-1048 <https://projects.tigase.net/issue/server-1048>`__: Update \"\n\"parent pom and information about suggested JDK\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:137\nmsgid \"\"\n\"`push-23 <https://projects.tigase.net/issue/push-23>`__: [JDK12] Can’t \"\n\"establish encrypted connection with Push/FCM\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:139\nmsgid \"\"\n\"`server-978 <https://projects.tigase.net/issue/server-978>`__: Improve \"\n\"VHost configuration / extending\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:141\nmsgid \"\"\n\"`server-1068 <https://projects.tigase.net/issue/server-1068>`__: Improve \"\n\"LogFormat readability (and maybe performance)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:143\nmsgid \"\"\n\"`server-1070 <https://projects.tigase.net/issue/server-1070>`__: Improve \"\n\"privacy list loggging\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:145\nmsgid \"\"\n\"`server-1071 <https://projects.tigase.net/issue/server-1071>`__: NPE in \"\n\"IOService.accept\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:147\nmsgid \"\"\n\"`server-710 <https://projects.tigase.net/issue/server-710>`__: \"\n\"Registration improvements\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:149\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:249\nmsgid \"\"\n\"`pubsub-79 <https://projects.tigase.net/issue/pubsub-79>`__: XEP-0411: \"\n\"Bookmarks Conversion\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:151\nmsgid \"\"\n\"`pubsub-75 <https://projects.tigase.net/issue/pubsub-75>`__: Add support \"\n\"for Publishing Options\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:153\nmsgid \"\"\n\"`server-1017 <https://projects.tigase.net/issue/server-1017>`__: \"\n\"XEP-0398: User Avatar to vCard-Based Avatars Conversion\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:155\nmsgid \"\"\n\"`server-994 <https://projects.tigase.net/issue/server-994>`__: Add server\"\n\" support for Entity Capabilities: Stream Feature\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:157\nmsgid \"\"\n\"`server-995 <https://projects.tigase.net/issue/server-995>`__: XEP-0157: \"\n\"Contact Addresses for XMPP Services\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:159\nmsgid \"\"\n\"`http-76 <https://projects.tigase.net/issue/http-76>`__: Standardise DNS \"\n\"webservice to XEP-0156\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:161\nmsgid \"\"\n\"`server-1109 <https://projects.tigase.net/issue/server-1109>`__: Add \"\n\"recommended JDK version to documentation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:163\nmsgid \"\"\n\"`push-28 <https://projects.tigase.net/issue/push-28>`__: Non-tigase \"\n\"notifications should use high priority (APNS)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:165\nmsgid \"\"\n\"`server-1114 <https://projects.tigase.net/issue/server-1114>`__: Can’t \"\n\"register on sure.im with StorkIM\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:167\nmsgid \"\"\n\"`server-1005 <https://projects.tigase.net/issue/server-1005>`__: Flatten \"\n\"schema to match versioning document\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:169\nmsgid \"\"\n\"`server-1116 <https://projects.tigase.net/issue/server-1116>`__: \"\n\"account_status is not checked\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:171\nmsgid \"\"\n\"`server-1074 <https://projects.tigase.net/issue/server-1074>`__: Hardened\"\n\" Mode improvements\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:173\nmsgid \"\"\n\"`server-1125 <https://projects.tigase.net/issue/server-1125>`__: \"\n\"StatsDumper.groovy doesn’t work in documentation in 8.x\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:175\nmsgid \"\"\n\"`http-85 <https://projects.tigase.net/issue/http-85>`__: Pasword resset \"\n\"doesn’t work\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:177\nmsgid \"\"\n\"`server-1128 <https://projects.tigase.net/issue/server-1128>`__: Possible\"\n\" vulnerability in XML parser\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:179\nmsgid \"\"\n\"`server-1130 <https://projects.tigase.net/issue/server-1130>`__: NPE i \"\n\"JabberIqAuth\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:181\nmsgid \"\"\n\"`http-84 <https://projects.tigase.net/issue/http-84>`__: Configurable \"\n\"``resetPassword`` endpoint hostname\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:183\nmsgid \"\"\n\"`server-1129 <https://projects.tigase.net/issue/server-1129>`__: BOSH \"\n\"timeouts on GET requests\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:185\nmsgid \"\"\n\"`prv-436 <https://projects.tigase.net/issue/prv-436>`__: Conversations \"\n\"compliance - contact developers\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:187\nmsgid \"\"\n\"`server-1100 <https://projects.tigase.net/issue/server-1100>`__: CAAS and\"\n\" WS testers fail to connect to wss://tigase.im:5291\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:189\nmsgid \"\"\n\"`server-1047 <https://projects.tigase.net/issue/server-1047>`__: Add \"\n\"SASL-EXTERNAL on s2s conections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:191\nmsgid \"\"\n\"`server-1103 <https://projects.tigase.net/issue/server-1103>`__: High \"\n\"priority PUSH notifications are sent for all messages\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:193\nmsgid \"\"\n\"`pubsub-93 <https://projects.tigase.net/issue/pubsub-93>`__: NPE in \"\n\"CapsChangeEvent\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:195\nmsgid \"\"\n\"`server-1137 <https://projects.tigase.net/issue/server-1137>`__: Don’t \"\n\"require setting JAVA_HOME to start server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:197\nmsgid \"\"\n\"`server-1136 <https://projects.tigase.net/issue/server-1136>`__: upgrade-\"\n\"schema --help not available\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:199\nmsgid \"\"\n\"`utils-19 <https://projects.tigase.net/issue/utils-19>`__: tigase-utils \"\n\"doesn’t compile with JDK12\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:201\nmsgid \"\"\n\"`server-1138 <https://projects.tigase.net/issue/server-1138>`__: Schema \"\n\"files are not sorted correctly during loading\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:203\nmsgid \"\"\n\"`pubsub-98 <https://projects.tigase.net/issue/pubsub-98>`__: Resources \"\n\"with emoji chars are causing issues with MySQL backend\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:205\nmsgid \"\"\n\"`server-1110 <https://projects.tigase.net/issue/server-1110>`__: \"\n\"Disabling TLS in VHost configuration doesn’t work\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:207\nmsgid \"\"\n\"`server-1078 <https://projects.tigase.net/issue/server-1078>`__: Don’t \"\n\"send root CA certificate in chain\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:209\nmsgid \"\"\n\"`server-1113 <https://projects.tigase.net/issue/server-1113>`__: Don’t \"\n\"advertise SASL-EXTERNAL if own certificate is not valid\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:211\nmsgid \"\"\n\"`http-78 <https://projects.tigase.net/issue/http-78>`__: Simplify \"\n\"installer\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:213\nmsgid \"\"\n\"`server-1133 <https://projects.tigase.net/issue/server-1133>`__: Not able\"\n\" to connect via S2S to server with incorrect SSL certificate\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:215\nmsgid \"\"\n\"`serverdistribution-2 \"\n\"<https://projects.tigase.net/issue/serverdistribution-2>`__: MUC upgrade \"\n\"not linked correctly in global tigase guide\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:217\nmsgid \"\"\n\"`server-1149 <https://projects.tigase.net/issue/server-1149>`__: Reduce \"\n\"startup time with a lot of database connections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:219\nmsgid \"\"\n\"`server-1148 <https://projects.tigase.net/issue/server-1148>`__: \\\"ERROR!\"\n\" Component <x> schema version is not loaded in the database or it is \"\n\"old!\\\" during shutdown\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:221\nmsgid \"\"\n\"`server-1153 <https://projects.tigase.net/issue/server-1153>`__: Refactor\"\n\" Credentials related ``username`` to ``credentialId`` to avoid confussion\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:223\nmsgid \"\"\n\"`servers-312 <https://projects.tigase.net/issue/servers-312>`__: No \"\n\"cluster connection to send a packet\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:225\nmsgid \"\"\n\"`server-1154 <https://projects.tigase.net/issue/server-1154>`__: Multi-\"\n\"thread script execution yields wrong results\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:227\nmsgid \"\"\n\"`servers-294 <https://projects.tigase.net/issue/servers-294>`__: Can’t \"\n\"connect from tigase.im to rsocks.net\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:229\nmsgid \"\"\n\"`server-1111 <https://projects.tigase.net/issue/server-1111>`__: Can’t \"\n\"establish s2s to upload.pouet.ovh\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:231\nmsgid \"\"\n\"`server-1143 <https://projects.tigase.net/issue/server-1143>`__: S2S \"\n\"connectivity issue with OpenFire when SASL external is used\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:233\nmsgid \"\"\n\"`servers-309 <https://projects.tigase.net/issue/servers-309>`__: Issue \"\n\"when connecting to xabber.org: not-authorized: self signed certificate\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:235\nmsgid \"\"\n\"`tigaseim-80 <https://projects.tigase.net/issue/tigaseim-80>`__: Siskin \"\n\"IM push server is not accessible\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:237\nmsgid \"\"\n\"`server-1080 <https://projects.tigase.net/issue/server-1080>`__: After \"\n\"updating certificate via ad-hoc/rest only main certificate is updated\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:239\nmsgid \"\"\n\"`http-88 <https://projects.tigase.net/issue/http-88>`__: Improve REST \"\n\"documentation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:241\nmsgid \"\"\n\"`http-87 <https://projects.tigase.net/issue/http-87>`__: \\\"request accept\"\n\" time exceeded\\\" for every request when using \"\n\"``JavaStandaloneHttpServer``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:243\nmsgid \"\"\n\"`server-1151 <https://projects.tigase.net/issue/server-1151>`__: \"\n\"BruteForceLockerExtension (and possibly others) settings are not \"\n\"correctly retrieved\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:245\nmsgid \"\"\n\"`http-89 <https://projects.tigase.net/issue/http-89>`__: Drop \"\n\"result/error packages received by HTTP-API if no connection present to \"\n\"write response to\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:247\nmsgid \"\"\n\"`pubsub-99 <https://projects.tigase.net/issue/pubsub-99>`__: \"\n\"Notifications are not sent for +notify from nodes with whitelist access \"\n\"mode\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:251\nmsgid \"\"\n\"`server-1157 <https://projects.tigase.net/issue/server-1157>`__: SCRAM-\"\n\"SHA512 not working\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:253\nmsgid \"\"\n\"`server-1159 <https://projects.tigase.net/issue/server-1159>`__: Improve \"\n\"handling establishing and terminating of the session\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:255\nmsgid \"\"\n\"`server-1152 <https://projects.tigase.net/issue/server-1152>`__: Cleanup \"\n\"warnings from JDBCMsgRepository\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:257\nmsgid \"\"\n\"`server-1112 <https://projects.tigase.net/issue/server-1112>`__: Fallback\"\n\" to diallback if SASL-EXTERNAL fails\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:259\nmsgid \"\"\n\"`servers-292 <https://projects.tigase.net/issue/servers-292>`__: S2S \"\n\"connectivity issues\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:261\nmsgid \"\"\n\"`acspubsub-19 <https://projects.tigase.net/issue/acspubsub-19>`__: REST \"\n\"execution fails on other nodes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:263\nmsgid \"\"\n\"`server-1145 <https://projects.tigase.net/issue/server-1145>`__: Race \"\n\"condition during storing/loading of offline messages\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:265\nmsgid \"\"\n\"`http-90 <https://projects.tigase.net/issue/http-90>`__: Add direct links\"\n\" to most useful task in AdminUI main page\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:267\nmsgid \"\"\n\"`spam-10 <https://projects.tigase.net/issue/spam-10>`__: Add \"\n\"documentation for creation of a custom filter\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:269\nmsgid \"\"\n\"`server-1163 <https://projects.tigase.net/issue/server-1163>`__: Review \"\n\"and update ``SASL Custom Mechanisms and Configuration`` documentation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:271\nmsgid \"\"\n\"`server-1164 <https://projects.tigase.net/issue/server-1164>`__: After-\"\n\"installation report - installation status\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:273\nmsgid \"\"\n\"`systems-76 <https://projects.tigase.net/issue/systems-76>`__: Fix issue \"\n\"with StackOverflow due to recursive call in TLSIO; improve debug log\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:275\nmsgid \"\"\n\"`server-1082 <https://projects.tigase.net/issue/server-1082>`__: Sec-\"\n\"WebSocket-Accept not calculated correctly\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:277\nmsgid \"\"\n\"`server-1083 <https://projects.tigase.net/issue/server-1083>`__: Messages\"\n\" sent to full jid are returned with error\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:279\nmsgid \"\"\n\"`push-25 <https://projects.tigase.net/issue/push-25>`__: Add support for \"\n\"sending encrypted PUSHes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:281\nmsgid \"\"\n\"`server-1085 <https://projects.tigase.net/issue/server-1085>`__: Improve \"\n\"retrieval of values for all keys in a node in UserRepository\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:283\nmsgid \"\"\n\"`muc-115 <https://projects.tigase.net/issue/muc-115>`__: Add support for \"\n\"MUC and offline message delivery\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:285\nmsgid \"\"\n\"`muc-122 <https://projects.tigase.net/issue/muc-122>`__: XEP-0410: MUC \"\n\"Self-Ping (Schrödinger’s Chat)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:287\nmsgid \"\"\n\"`muc-112 <https://projects.tigase.net/issue/muc-112>`__: Support for \"\n\"setting vCard avatar for room\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:289\nmsgid \"\"\n\"`http-83 <https://projects.tigase.net/issue/http-83>`__: Issue with \"\n\"multithreading access to HttpExchange instance\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:291\nmsgid \"\"\n\"`httpapijetty-3 <https://projects.tigase.net/issue/httpapijetty-3>`__: \"\n\"Support for HTTP/2\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:293\nmsgid \"\"\n\"`httpapijetty-6 <https://projects.tigase.net/issue/httpapijetty-6>`__: \"\n\"Update Jetty version\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:2\nmsgid \"Tigase XMPP Server 8.0.0 Change notes and announcement\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:8\nmsgid \"Kernel and beans configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:10\nmsgid \"\"\n\"Tigase now operates using a Kernel and Beans style of programming. What \"\n\"does this mean for Tigase and You? Good news, really. Tigase XMPP Server \"\n\"is now working as a Kernel program, which will operate on it’s own and \"\n\"handle all the core functionality of the server. Component, and non-\"\n\"essential functionality will now be loaded as Beans. As a user, your \"\n\"experience will not change all that much. However, beans can be loaded \"\n\"and unloaded without having to restart Tigase, meaning that the program \"\n\"will behave more dynamically. This means a smaller footprint on memory on\"\n\" resources when components are not needed, and longer uptimes without \"\n\"having to rest art the program! This also allows for greater flexibility \"\n\"for Tigase XMPP Server to be better customized for unique solutions.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:14\nmsgid \"New Configuration File Format\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:16\nmsgid \"\"\n\"With the change of Tigase to a Kernel and Beans style of programming, we \"\n\"have also changed how the configuration file is managed. Although you \"\n\"will still edit the ``config.tdsl`` file like a plaintext file, a new \"\n\"style of formatting will be used known as DSL. Domain Specific Language \"\n\"may add more lines, but is a cleaner format, and provides a more secure \"\n\"configuration design since validation of the configuration is done at the\"\n\" domain level. For more information on this format and how to configure \"\n\"Tigase, visit `DSL Configuration Guide <#dslConfig>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:20\nmsgid \"Cluster Node Shutdown Changes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:22\nmsgid \"\"\n\"Starting with Tigase XMPP Server 8.0.0, users connected on clustered \"\n\"nodes will be able use a ``see-other-host`` strategy when a node is being\"\n\" shutdown. **Note: This may not be compatible with all clients.** The Ad-\"\n\"hoc command is designed for a graceful shutdown of cluster nodes as a \"\n\"groovy script ``Shutdown.groovy``. This script also allows for the \"\n\"-timeout setting which will delay shutdown of the node, and alert all \"\n\"users (via a headline message) that the server will be shutdown after a \"\n\"time. User clients that are compatible with the command will then detect \"\n\"other connected clusters and maintain their connections.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:24\nmsgid \"\"\n\"If the command is being sent to shut down the whole cluster, no ``see-\"\n\"other-host`` implementation will be sent, however timeout settings may \"\n\"still be used.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:26\nmsgid \"\"\n\"The script may be activated by an ad-hoc command, or sent using REST from\"\n\" remote or Tigase Admin UI.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:31\nmsgid \"Significant cleanup of code and repositories\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:33\nmsgid \"\"\n\"Multiple changes have been made to the structure and coding for v8, many \"\n\"related to trimming size of repositories and old calls. Some of these \"\n\"improvements are listed here:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:35\nmsgid \"Empty JavaDocs that do not convey values have been removed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:37\nmsgid \"\"\n\"All code is reformatted to be compliant with out `codestyle guidelines \"\n\"<#tigaseCodeStyle>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:39\nmsgid \"\"\n\"Calls to ``System.out.print*()`` and ``printStackTrace()`` have been \"\n\"removed from code.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:41\nmsgid \"Depreciated and unused classes have been removed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:46\nmsgid \"BouncyCastle being used for StartTLS\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:48\nmsgid \"\"\n\"`BouncyCastle <https://www.bouncycastle.org/java.html>`__ Crypto API has \"\n\"now been employed to handle StartTLS negotiation. By doing this, Tigase \"\n\"now supports ``tls-unique`` within the SCRAM PLUS authentication \"\n\"implementation. This API is may be employed by calling the class in your \"\n\"configuration file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:56\nmsgid \"The BouncyCastle classes are included in the dist-max archives.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:61\nmsgid \"default-virtual-host property changes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:63\nmsgid \"\"\n\"Default virtual hosts property is now able to be configured only as a \"\n\"domain name instead of the list of virtual host domains with options. \"\n\"Additional virtual host domains and their options need to be configured \"\n\"using ad-hoc commands or web AdminUI. Reference `Virtual-Hosts \"\n\"Configuration <#virtHosts>`__ for more details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:68\nmsgid \"All artifacts are signed\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:70\nmsgid \"\"\n\"Since work began on v8.0.0 Tigase has required that all changes to Tigase\"\n\" XMPP Server and dependencies be signed with known certificates. This \"\n\"version marks the first to be totally signed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:75\nmsgid \"Scaled Down Installation Methods\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:77\nmsgid \"\"\n\"We have cleaned up installation methods for Tigase and now recommend the \"\n\"use of web-installer method. IzPack installer (files ``tigase-\"\n\"server-<version>-b<build>.jar`` installation methods have been removed \"\n\"and will no longer be produced for v8.0.0 and later. Manual installation \"\n\"is still available for those unable to use HTTP or browser access. Visit \"\n\"our `Quick Start <#quickstart>`__ guide for instructions on these other \"\n\"methods.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:82\nmsgid \"Emojis now supported on Tigase XMPP Servers\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:84\nmsgid \"\"\n\"Emojis are now supported on MySQL databases, however some settings may be\"\n\" need to be changed, although they won’t affect existing databases. \"\n\"`Visit this section <#emojisupportSQL>`__ for details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:89\nmsgid \"XEP-0215 External Service Discovery now supported\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:91\nmsgid \"\"\n\"Tigase now supports `XEP-0215 - External Service Discovery \"\n\"<https://xmpp.org/extensions/xep-0215.html>`__ allowing Tigase to \"\n\"discover services that are not available VIA the XMPP Protocol. For setup\"\n\" and configuration information visit `External Service Discovery \"\n\"Component <#_tigase_external_service_discovery>`__ documentation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:96\nmsgid \"XEP-0313 Message Archive Management now supported\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:98\nmsgid \"\"\n\"`XEP-0313 - Message Archive Management \"\n\"<https://xmpp.org/extensions/xep-0313.html>`__ is now supported by Tigase\"\n\" featuring custom enhancements like full-text search and searching by \"\n\"tags. MAM requires Tigase’s message archive to be enabled in the \"\n\"``config.tdsl`` file, and the schema (XEP-0136 or XEP-0313) must be \"\n\"configured in session manager settings. To turn on MAM, see configuration\"\n\" guide `located here <#_support_for_mam>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:103\nmsgid \"XEP-0363 HTTP File Upload now supported\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:105\nmsgid \"\"\n\"`XEP-0363 - HTTP File Upload \"\n\"<https://xmpp.org/extensions/xep-0363.html>`__ is now supported using \"\n\"Tigase HTTP API component now allowing for a more robust one-to-many file\"\n\" uploading option. Configuration details are available at the `HTTP File \"\n\"Upload Component <#XEP0363>`__ section of documentation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:110\nmsgid \"Startup now uses bootstrapping\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:112\nmsgid \"\"\n\"Tigase now uses bootstrapping to startup, which will load configuration \"\n\"from ``config.tdsl`` file like before. Then Tigase will begin it’s normal\"\n\" operations with the configuration options. All startup functions for \"\n\"Tigase will now run under the ``bootstrap`` bean.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:117\nmsgid \"CAPTCHA system now available for in-band registration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:119\nmsgid \"\"\n\"`XEP-0077 In band registration \"\n\"<https://xmpp.org/extensions/xep-0077.html>`__ can use Data Forms as an \"\n\"option to process new registrations. Now you can secure these \"\n\"registrations by employing a CAPTCHA solution. By enabling this option \"\n\"you can reduce the number of potential spammers and bots on your server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:124\nmsgid \"Schema changes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:126\nmsgid \"\"\n\"Now each component has it’s own schema for databases, they are no longer \"\n\"tied into Tigase XMPP server versions making changes and updates to \"\n\"individual components easier, and may not disrupt all users not using \"\n\"certain components. See the `schema update section <#schemaChangev800>`__\"\n\" for more details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:131\nmsgid \"Shrinkable Statistics History\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:133\n#, python-format\nmsgid \"\"\n\"Statistics history can now be automatically made smaller if a systems \"\n\"memory resources are above a certain amount. By default this is enabled \"\n\"and will trigger when over 95% of memory is in use. Half of all existing \"\n\"entries will be removed at this time. The same pattern will continue to \"\n\"halve the available records every time the threshold is met. A hard-set \"\n\"minimum of 5 entries is set, so you will always have the last 5 entries. \"\n\"This setting may be adjusted by adding the following setting to your \"\n\"``config.tdsl`` file and adjusting the integer value:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:144\nmsgid \"Statistics now available for all modules\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:146\nmsgid \"For any bean, you may enable statistics by using the following\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:157\nmsgid \"Spam Protection\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:159\nmsgid \"\"\n\"Tigase XMPP Server v8.0.0 now includes some efforts to prevent spam bot \"\n\"accounts from running on servers.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:162\nmsgid \"Account Registration Limits Expanded\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:164\nmsgid \"\"\n\"Account registration limits have been expanded and now you can set \"\n\"separate counters, or configure components individually for their own \"\n\"limits. Visit `this section <#accountRegLimit>`__ for configuration \"\n\"details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:167\nmsgid \"\"\n\"Accounts created using in-band registration now will use confirmation \"\n\"E-mail\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:169\nmsgid \"\"\n\"In an effort to create a more secure method for implementing \"\n\"``JabberIqRegister`` Tigase XMPP Server will now require the use of a \"\n\"confirmation E-mail by default in the process. The E-mail must be valid, \"\n\"and accounts will be made into pending status until a user clicks the \"\n\"generated URI in the E-mail and activates the account. This is a plugin \"\n\"and must be enabled in the ``config.tdsl`` file by using the following \"\n\"code:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:176\nmsgid \"Further Spam prevention\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:178\nmsgid \"\"\n\"Tigase-spam component is now in ``dist-max`` distribution package, and \"\n\"has a number of features described here `in this section \"\n\"<#tigase_spam_filter>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:183\nmsgid \"Changes in password storage\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:185\nmsgid \"\"\n\"Before version 8.0.0, user passwords were stored in plaintext in the \"\n\"``user_pw`` database field within ``tig_users`` table, but in plaintext. \"\n\"It was possible to enable storage of the MD5 hash of the password \"\n\"instead, however this limited authentication mechanism SASL PLAIN only. \"\n\"However an MD5 hash of a password is not really a secure method as it is \"\n\"possible to revert this mechanism using rainbow tables.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:187\nmsgid \"\"\n\"Therefore, we decided to change this and store only encrypted versions of\"\n\" a password in ``PBKDF2`` form which can be easily used for ``SCRAM-\"\n\"SHA-1`` authentication mechanism or ``SCRAM-SHA-256``. ``SASL PLAIN`` \"\n\"mechanism can also use these encrypted passwords.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:189\nmsgid \"\"\n\"The storage of encrypted passwords is now enabled **by default** in \"\n\"v8.0.0 of Tigase.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:194\nmsgid \"Dynamic TLS Buffer\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:196\nmsgid \"\"\n\"Memory Buffer for TLS no longer remains at highest buffer size needed for\"\n\" the server session. Buffer will now free memory during idle connections.\"\n\" Thus drastically improving program footprint.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:201\nmsgid \"XEP-305 Quickstart now supported\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:203\nmsgid \"\"\n\"It’s now possible to establish connection faster due to implementation of\"\n\" `XEP-0305: XMPP Quickstart \"\n\"<https://xmpp.org/extensions/xep-0305.html>`__ (`#1936 \"\n\"<https://projects.tigase.net/issues?q=Redmine%20ID:%201936>`__). Feature \"\n\"is only available for ``c2s`` Connection Manager (i.e. connections on \"\n\"port 5222) and needs to be enabled in ``config.tdsl``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:214\nmsgid \"Database Timestamps\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:216\nmsgid \"Timestamps in database will be stored using UTC time.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:221\nmsgid \"Config-type properties have changed\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:223\nmsgid \"\"\n\"Config-type is now configured using DSL format. Visit `this section \"\n\"<#configType>`__ for more information. The names of different config-type\"\n\" properties have changed: ``default`` replaces ``--gen-config-def``, \"\n\"``--gen=config-all``, and ``--gen-config-default`` configuration types. \"\n\"``session-manager`` replaces ``--gen-config-sm``. ``connection-managers``\"\n\" replaces ``--gen-config-cs``. ``component`` replaces ``--gen-config-\"\n\"comp``. ``setup`` - is a new type of config created for initial \"\n\"configuration of Tigase XMPP Server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:227\nmsgid \"\"\n\"Old versions are no longer supported, you HAVE to replace old versions \"\n\"with the new ones manually when upgrading to v8.0.0.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:232\nmsgid \"Database Watchdog implemented\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:234\nmsgid \"\"\n\"It is now possible to set connection testing to databases when \"\n\"connections are idle and customize the frequency with which this is done.\"\n\" Visit `this section <#databaseWatchdog>`__ for more details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:239\nmsgid \"Packet statistics expanded\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:241\nmsgid \"\"\n\"Packet statistics both retrieved VIA XMPP and during graceful shutdown \"\n\"have now been separated to a per-XMLNS basis. This may be disabled by \"\n\"adding the following line to ``config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:250\nmsgid \"XEP-0016 Behavior changes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:252\nmsgid \"\"\n\"XEP states that Privacy lists should be used when no user session exists \"\n\"in addition to when there is. Previously, Tigase would only filter \"\n\"results when retrieving messages, allowing blocked users to store offline\"\n\" messages. This has now been changed to reflect the XEP properly, and \"\n\"messages will be filtered while there is no user session. If however, you\"\n\" wish to use the previous version, where offline messages are cached \"\n\"first and then filtered, you may use the following configuration:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:264\nmsgid \"\"\n\"By default, the cache has a limit of 10000 entries, that may be set by \"\n\"using size bean as seen above.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:269\nmsgid \"Access Control List has new ACL modifiers\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:271\nmsgid \"\"\n\"New permissions have been added to ACL including ``DOMAIN_OWNER`` and \"\n\"``DOMAIN_ADMIN`` to reduce permissions checking, and add another level of\"\n\" fine-grained permissions. For more details, please see `Tigase ACL \"\n\"<#accessControlList>`__ configuration for more details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:276\nmsgid \"Option to ignore schema-version check added\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:278\nmsgid \"\"\n\"You can now skip the schema check phase for individual databases. To do \"\n\"this, add the following do the datasource configuration block:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:288\nmsgid \"This will do the following:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:290\nmsgid \"Print a warning during repository startup.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:292\nmsgid \"Skip schema upgrades for the source.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:294\nmsgid \"Skip schema destruction for the source.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:299\nmsgid \"Protection against brute-force attacks\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:301\nmsgid \"\"\n\"Version 8.0.0 improves security by preventing brute-force attacks. \"\n\"Feature needs to be explicitly enabled and configured (on per VHost \"\n\"basis). Detailed configuration is described in `??? \"\n\"<#bruteForcePrevention>`__ (`#8160 \"\n\"<https://projects.tigase.net/issues?q=Redmine%20ID:%208160>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:306\nmsgid \"\"\n\"`#611 <https://projects.tigase.net/issues?q=Redmine%20ID:%20611>`__ \"\n\"Support for Message of the Day is now enabled in Tigase XMPP Server and \"\n\"can be administered using `XEP-0133 Service Administration \"\n\"<http://xmpp.org/extensions/xep-0133.html#set-motd>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:308\nmsgid \"\"\n\"`#1569 <https://projects.tigase.net/issues?q=Redmine%20ID:%201569>`__ Re-\"\n\"implemented XEP-0133 Service Administration Scripts ``4.3 Disable User`` \"\n\"and ``4.4 Re-enable User``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:310\nmsgid \"\"\n\"`#1449 <https://projects.tigase.net/issues?q=Redmine%20ID:%201449>`__ \"\n\"Monitoring modules now works in OSGi mode.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:312\nmsgid \"\"\n\"`#1706 <https://projects.tigase.net/issues?q=Redmine%20ID:%201706>`__ \"\n\"``auto-authorize`` of presence subscriptions can now be set for \"\n\"individual vhosts.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:314\nmsgid \"\"\n\"`#1968 <https://projects.tigase.net/issues?q=Redmine%20ID:%201968>`__ \"\n\"Added a Proxy Wrapper to handle reconnections to database connection pool\"\n\" to help prevent deadlocking threads.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:316\nmsgid \"\"\n\"`#3511 <https://projects.tigase.net/issues?q=Redmine%20ID:%203511>`__ \"\n\"Mechanism responsible for closing XMPP in SessionManager has been changed\"\n\" to process all packets from TCP connection before closing connection.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:318\nmsgid \"\"\n\"`#3802 <https://projects.tigase.net/issues?q=Redmine%20ID:%203802>`__ \"\n\"Implementation and API of LocalEventBus and ClusteredEventBus has been \"\n\"unified and is now available as EventBus.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:320\nmsgid \"\"\n\"`#3918 <https://projects.tigase.net/issues?q=Redmine%20ID:%203918>`__ \"\n\"Session Establishment Advertisement is now optional, bringing session \"\n\"establishment in line with `RFC 6121 \"\n\"<https://tools.ietf.org/html/rfc6121>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:322\nmsgid \"\"\n\"`#4111 <https://projects.tigase.net/issues?q=Redmine%20ID:%204111>`__ \"\n\"Changed input buffer sizing to use a ratio of 2 to 1 based on input \"\n\"capacity. No longer using a constant value.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:324\nmsgid \"\"\n\"`#4212 <https://projects.tigase.net/issues?q=Redmine%20ID:%204212>`__ \"\n\"Database schema files have been flattened and made for better \"\n\"organization.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:326\n#, python-format\nmsgid \"\"\n\"`#4501 <https://projects.tigase.net/issues?q=Redmine%20ID:%204501>`__ \"\n\"``CounterDataFileLogger`` now has an upper limit and will be default be \"\n\"shrunk to 75% if available disk space is 5% or less than 100MB.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:328\nmsgid \"\"\n\"`#4654 <https://projects.tigase.net/issues?q=Redmine%20ID:%204654>`__ \"\n\"PubSub component has been updated and new schema uses UTF-8 encoding when\"\n\" hashing database lookup.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:330\nmsgid \"\"\n\"`#4776 <https://projects.tigase.net/issues?q=Redmine%20ID:%204776>`__ \"\n\"Tigase ``DbSchemaLoader`` now prompts for password if one is missing from\"\n\" command line.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:332\nmsgid \"\"\n\"`#4788 <https://projects.tigase.net/issues?q=Redmine%20ID:%204788>`__ \"\n\"Push component added to dist-max archive.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:334\nmsgid \"\"\n\"`#4814 <https://projects.tigase.net/issues?q=Redmine%20ID:%204814>`__ \"\n\"SASL-SCRAM will now be automatically disabled if auth database uses \"\n\"encoded passwords.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:336\nmsgid \"\"\n\"`#4844 <https://projects.tigase.net/issues?q=Redmine%20ID:%204844>`__ \"\n\"External components can now have SSL socket connections assigned to them.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:338\nmsgid \"\"\n\"`#4859 <https://projects.tigase.net/issues?q=Redmine%20ID:%204859>`__ \"\n\"Tigase ``DbSchemaLoader`` now can support using SSL when connecting to \"\n\"databases.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:340\nmsgid \"\"\n\"`#4874 <https://projects.tigase.net/issues?q=Redmine%20ID:%204874>`__ \"\n\"Tigase Test Suite has been updated to correspond to all changes for \"\n\"v8.0.0.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:342\nmsgid \"\"\n\"`#4877 <https://projects.tigase.net/issues?q=Redmine%20ID:%204877>`__ In-\"\n\"memory repository implemented for **testing ONLY**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:344\nmsgid \"\"\n\"`#4880 <https://projects.tigase.net/issues?q=Redmine%20ID:%204880>`__ \"\n\"Tigase config-type settings have been reduced and changed. See `this \"\n\"section <#configType>`__ for more details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:346\nmsgid \"\"\n\"`#4908 <https://projects.tigase.net/issues?q=Redmine%20ID:%204908>`__ \"\n\"Limited Ad-hoc execution to admin only within monitor component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:348\nmsgid \"\"\n\"`#5005 <https://projects.tigase.net/issues?q=Redmine%20ID:%205005>`__ \"\n\"Detailed logging configuration is now available in DSL format. See \"\n\"xref:[customLogging] for more details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:350\nmsgid \"\"\n\"`#5069 <https://projects.tigase.net/issues?q=Redmine%20ID:%205069>`__ \"\n\"Packet processed statistics now separates results based on XML \"\n\"Namespaces.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:352\nmsgid \"\"\n\"`#5079 <https://projects.tigase.net/issues?q=Redmine%20ID:%205079>`__ \"\n\"Tigase ``DbSchemaLoader`` can now process multiple .sql files in one \"\n\"command by using a comma separated list when calling.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:354\nmsgid \"\"\n\"`#5086 <https://projects.tigase.net/issues?q=Redmine%20ID:%205086>`__ \"\n\"Tigase server monitor is loaded after delay to prevent NPE during \"\n\"startup.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:356\nmsgid \"\"\n\"`#5149 <https://projects.tigase.net/issues?q=Redmine%20ID:%205149>`__ \"\n\"``StanzaReceiver`` and ``StanzaSender`` Components have been deprecated \"\n\"and are no longer part of Tigase XMPP Server. Related SQL tables \"\n\"``xmpp_stanza`` and ``short_news`` have also been removed from schemas.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:358\nmsgid \"\"\n\"`#5150 <https://projects.tigase.net/issues?q=Redmine%20ID:%205150>`__ All\"\n\" TigaseDB tables now use the ``tig_`` prefix.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:360\nmsgid \"\"\n\"`#5214 <https://projects.tigase.net/issues?q=Redmine%20ID:%205214>`__ \"\n\"Check has been added if recipient exists before storing offline messages \"\n\"for local jid.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:362\nmsgid \"\"\n\"`#5293 <https://projects.tigase.net/issues?q=Redmine%20ID:%205293>`__ \"\n\"``DbSchemaLoader`` now will fail execution instead of skipping when \"\n\"encountering missing files.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:364\nmsgid \"\"\n\"`#5379 <https://projects.tigase.net/issues?q=Redmine%20ID:%205379>`__ \"\n\"Server ready detection has been improved in testrunner.sh.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:366\nmsgid \"\"\n\"`#5397 <https://projects.tigase.net/issues?q=Redmine%20ID:%205397>`__ \"\n\"Webhelp Documentation will no longer be built.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:368\nmsgid \"\"\n\"`#5422 <https://projects.tigase.net/issues?q=Redmine%20ID:%205422>`__ \"\n\"Errors with Beans will now result in compact and more readable StackTrace\"\n\" print in console log.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:370\nmsgid \"\"\n\"`#5423 <https://projects.tigase.net/issues?q=Redmine%20ID:%205423>`__ \"\n\"System configuration will now be printed to log file as \"\n\"``ConfigHolder.loadConfiguration`` output.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:372\nmsgid \"\"\n\"`#5425 <https://projects.tigase.net/issues?q=Redmine%20ID:%205425>`__ \"\n\"``GetAnyFile`` and ``GetConfigFile`` scripts moved to message-router \"\n\"instead of basic-conf.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:374\nmsgid \"\"\n\"`#5429 <https://projects.tigase.net/issues?q=Redmine%20ID:%205429>`__ \"\n\"Adjusted settings for Dynamic Rostering now can use separate beans for \"\n\"multiple implementations.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:376\nmsgid \"\"\n\"`#5430 <https://projects.tigase.net/issues?q=Redmine%20ID:%205430>`__ \"\n\"``BindResource`` is now set to FINER log level to reduce console output \"\n\"verbosity.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:378\nmsgid \"\"\n\"`#5475 <https://projects.tigase.net/issues?q=Redmine%20ID:%205475>`__ \"\n\"Setting default environment variables is now possible in ``config.tdsl`` \"\n\"file using ``env('env-1', 'def-value')`` lines. Details available `in DSL\"\n\" Configuration <#dslEnv>`__ section.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:380\nmsgid \"\"\n\"`#5496 <https://projects.tigase.net/issues?q=Redmine%20ID:%205496>`__ \"\n\"``Destroy Schema`` task now added to schema manager.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:382\nmsgid \"\"\n\"`#5583 <https://projects.tigase.net/issues?q=Redmine%20ID:%205583>`__ \"\n\"Error messages now properly sent when offline message storage is full.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:384\nmsgid \"\"\n\"`#5674 <https://projects.tigase.net/issues?q=Redmine%20ID:%205674>`__ All\"\n\" components now use UTC timestamp when interacting with databases.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:386\nmsgid \"\"\n\"`#5800 <https://projects.tigase.net/issues?q=Redmine%20ID:%205800>`__ \"\n\"Better annotation of deprecated code, cleanup and removal code previously\"\n\" marked as deprecated.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:388\nmsgid \"\"\n\"`#5964 <https://projects.tigase.net/issues?q=Redmine%20ID:%205964>`__ \"\n\"Server version is now added to JMX statistics.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:390\nmsgid \"\"\n\"`#5982 <https://projects.tigase.net/issues?q=Redmine%20ID:%205982>`__ \"\n\"Remote JVM debugging configuration added to tigase.conf file, commented \"\n\"by default.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:392\nmsgid \"\"\n\"`#6038 <https://projects.tigase.net/issues?q=Redmine%20ID:%206038>`__ \"\n\"Data Source pool connections are now initialized concurrently instead of \"\n\"one at a time, dropping initializing time.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:394\nmsgid \"\"\n\"`#6103 <https://projects.tigase.net/issues?q=Redmine%20ID:%206103>`__ \"\n\":literal:`RosterElement`no longer keeps `XMPPResourceConnection` instance\"\n\" as it is cached elsewhere. Removal results in net improvement in memory \"\n\"footprint.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:396\nmsgid \"\"\n\"`#6133 <https://projects.tigase.net/issues?q=Redmine%20ID:%206133>`__ \"\n\"Tigase now checks components against server version to ensure \"\n\"compatibility.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:398\nmsgid \"\"\n\"`#6163 <https://projects.tigase.net/issues?q=Redmine%20ID:%206163>`__ \"\n\"Groovy plugin updated to v2.4.12.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:400\nmsgid \"\"\n\"`#6206 <https://projects.tigase.net/issues?q=Redmine%20ID:%206206>`__ \"\n\"Separated TigaseXMLTools and TigaseUtil packages for better compatibility\"\n\" with JDK v9.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:402\nmsgid \"\"\n\"`#6216 <https://projects.tigase.net/issues?q=Redmine%20ID:%206216>`__ \"\n\"MongoDB Driver now updated to v3.5.0.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:404\nmsgid \"\"\n\"`#6560 <https://projects.tigase.net/issues?q=Redmine%20ID:%206560>`__ \"\n\"tigase anti-spam component now included in tigase dist-max archive.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:406\nmsgid \"\"\n\"`#6821 <https://projects.tigase.net/issues?q=Redmine%20ID:%206821>`__ \"\n\"Improved error reporting when errors from ``ConfigReader``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:408\nmsgid \"\"\n\"`#6842 <https://projects.tigase.net/issues?q=Redmine%20ID:%206842>`__ \"\n\"``DefaultTypesConverter`` no longer requires case sensitive enums.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:410\nmsgid \"\"\n\"`#7082 <https://projects.tigase.net/issues?q=Redmine%20ID:%207082>`__ \"\n\"``ClassUtilBean`` now handles packet filtering for packets part of Tigase\"\n\" Server but not containing beans, other improvements to mDNS.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:412\nmsgid \"\"\n\"`#7433 <https://projects.tigase.net/issues?q=Redmine%20ID:%207433>`__ \"\n\"``SeeOtherHost`` no longer uses ``PropertiesBeanConfigurator`` to parse \"\n\"configuration.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:414\nmsgid \"\"\n\"`#7446 <https://projects.tigase.net/issues?q=Redmine%20ID:%207446>`__ \"\n\"User credentials can now be managed with Ad-hoc commands.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:416\nmsgid \"\"\n\"`#7743 <https://projects.tigase.net/issues?q=Redmine%20ID:%207743>`__ \"\n\"Improved error message when repository is not found.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:418\nmsgid \"\"\n\"`#7773 <https://projects.tigase.net/issues?q=Redmine%20ID:%207773>`__ Ad-\"\n\"hoc commands can now by executed asynchronously.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:420\nmsgid \"\"\n\"`#2341 <https://projects.tigase.net/issues?q=Redmine%20ID:%202341>`__ \"\n\"allow specifying SubscriptionType when adding buddy to avoid calling \"\n\"separately .setBuddySubscription() thus eliminating saving roster twice \"\n\"to database if not needed\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:425\nmsgid \"Fixes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:427\nmsgid \"\"\n\"`#2750 <https://projects.tigase.net/issues?q=Redmine%20ID:%202750>`__ \"\n\"Multiple artifact and depreciated file cleanup. Massive code cleanup and \"\n\"javadoc cleaning.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:429\nmsgid \"\"\n\"`#3582 <https://projects.tigase.net/issues?q=Redmine%20ID:%203582>`__ \"\n\"Schema files streamlined, and no longer embedded in code.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:431\nmsgid \"\"\n\"`#3611 <https://projects.tigase.net/issues?q=Redmine%20ID:%203611>`__ \"\n\"Fixed TheadExceptionHandler caused by ACS unable to read PubSub schema \"\n\"changes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:433\nmsgid \"\"\n\"`#3686 <https://projects.tigase.net/issues?q=Redmine%20ID:%203686>`__ \"\n\"Issues with processing XHTML-IM have been fixed, and now render correctly\"\n\" messages with multiple CData items.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:435\nmsgid \"\"\n\"`#3689 <https://projects.tigase.net/issues?q=Redmine%20ID:%203689>`__ \"\n\"Packets returned from CM no longer bear the original senders' jid.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:437\nmsgid \"\"\n\"`#3803 <https://projects.tigase.net/issues?q=Redmine%20ID:%203803>`__ New\"\n\" call ``RouteEvent`` has been added to check to list and check events and\"\n\" determine which should be forwarded to other nodes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:439\nmsgid \"\"\n\"`#3822 <https://projects.tigase.net/issues?q=Redmine%20ID:%203822>`__ \"\n\"Error is now thrown if listener is registered for an event that is not \"\n\"found in EventBus.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:441\nmsgid \"\"\n\"`#3910 <https://projects.tigase.net/issues?q=Redmine%20ID:%203910>`__ \"\n\"Fixed NPE in SessionManager when session is closed during execution of \"\n\"everyMinute method.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:443\nmsgid \"\"\n\"`#3911 <https://projects.tigase.net/issues?q=Redmine%20ID:%203911>`__ \"\n\"Fixed issue of dropping connections during thread load distribution.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:445\nmsgid \"\"\n\"`#4185 <https://projects.tigase.net/issues?q=Redmine%20ID:%204185>`__ \"\n\"Fixed an error where messages would be duplicated on stream resumption \"\n\"due to a counter being reset upon reconnection.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:447\nmsgid \"\"\n\"`#4447 <https://projects.tigase.net/issues?q=Redmine%20ID:%204447>`__ \"\n\"Fixed condition where expired messages in offline store would cause \"\n\"locks.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:449\nmsgid \"\"\n\"`#4547 <https://projects.tigase.net/issues?q=Redmine%20ID:%204547>`__ \"\n\"config.dump file now is fully compatible with init.tdsl file and DSL file\"\n\" formatting.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:451\nmsgid \"\"\n\"`#4672 <https://projects.tigase.net/issues?q=Redmine%20ID:%204672>`__ \"\n\"Fixed ``UnsupportedOperationException`` occurring during configuration of\"\n\" ``WebSocketConnectionClustered``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:453\nmsgid \"\"\n\"`#4776 <https://projects.tigase.net/issues?q=Redmine%20ID:%204776>`__ \"\n\"``DBSchemaLoader`` now asks for user credentials if parameter is missing.\"\n\" Exceptions are no longer thrown if file specified is not found.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:455\nmsgid \"\"\n\"`#4885 <https://projects.tigase.net/issues?q=Redmine%20ID:%204885>`__ \"\n\"``client-port-delay-listening`` no longer causes exception when called.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:457\nmsgid \"\"\n\"`#4973 <https://projects.tigase.net/issues?q=Redmine%20ID:%204973>`__ \"\n\"Changed Message History query to now include a limit when selecting \"\n\"items, preventing an SQLTimeoutException.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:459\nmsgid \"\"\n\"`#5005 <https://projects.tigase.net/issues?q=Redmine%20ID:%205005>`__ \"\n\"Fixed an issue where disabling components would result in server \"\n\"shutdown.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:461\nmsgid \"\"\n\"`#5042 <https://projects.tigase.net/issues?q=Redmine%20ID:%205042>`__ \"\n\"Fixed issue when implementing custom SASL providers, mechanisms and \"\n\"callback handler factories.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:463\nmsgid \"\"\n\"`#5066 <https://projects.tigase.net/issues?q=Redmine%20ID:%205066>`__ \"\n\"Fixed issue initializing databases using MongoDB.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:465\nmsgid \"\"\n\"`#5076 <https://projects.tigase.net/issues?q=Redmine%20ID:%205076>`__ \"\n\"last_login and last_logout values are now properly updated while using \"\n\"SASL SCRAM authentication.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:467\nmsgid \"\"\n\"`#5084 <https://projects.tigase.net/issues?q=Redmine%20ID:%205084>`__ \"\n\"SCRAM now checks to see if account is disabled before retrieving \"\n\"password.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:469\nmsgid \"\"\n\"`#5085 <https://projects.tigase.net/issues?q=Redmine%20ID:%205085>`__ \"\n\"Fixed ``too many beans implemented`` error in Monitor Component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:471\nmsgid \"\"\n\"`#5088 <https://projects.tigase.net/issues?q=Redmine%20ID:%205088>`__ \"\n\"Removed unnecessary SASL request processing after session is closed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:473\nmsgid \"\"\n\"`#5118 <https://projects.tigase.net/issues?q=Redmine%20ID:%205118>`__ \"\n\"Fixed NPE during query of privacy lists then ``type`` is missing.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:475\nmsgid \"\"\n\"`#5303 <https://projects.tigase.net/issues?q=Redmine%20ID:%205303>`__ \"\n\"Fixed beans not being overridden by configuration if they were registered\"\n\" in ``RegistrarBean`` or ``AbstractKernelBasedComponent``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:477\nmsgid \"\"\n\"`#5311 <https://projects.tigase.net/issues?q=Redmine%20ID:%205311>`__ \"\n\"Offline messages are no longer dumped from MongoDB when restarting \"\n\"server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:479\nmsgid \"\"\n\"`#5394 <https://projects.tigase.net/issues?q=Redmine%20ID:%205394>`__ \"\n\"Loading main Derby schema no longer throws exceptions.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:481\nmsgid \"\"\n\"`#5428 <https://projects.tigase.net/issues?q=Redmine%20ID:%205428>`__ \"\n\"Fixed parsing of v-host per domain limit property.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:483\nmsgid \"\"\n\"`#5450 <https://projects.tigase.net/issues?q=Redmine%20ID:%205450>`__ \"\n\"Server no longer automatically shuts down when default or other db can \"\n\"not be found or accessed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:485\nmsgid \"\"\n\"`#5458 <https://projects.tigase.net/issues?q=Redmine%20ID:%205458>`__ \"\n\"Fixed potential timeout arising from \"\n\"``XMPPIOService::xmppStreamOpened()`` method.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:487\nmsgid \"\"\n\"`#5480 <https://projects.tigase.net/issues?q=Redmine%20ID:%205480>`__ \"\n\"Fixed issue in Derby DB where obtaining offline messages results in \"\n\"SQLException.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:489\nmsgid \"\"\n\"`#5525 <https://projects.tigase.net/issues?q=Redmine%20ID:%205525>`__ \"\n\"Fixed S2S ``invalid-namespace`` error being returned during connection \"\n\"establishment.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:491\nmsgid \"\"\n\"`#5587 <https://projects.tigase.net/issues?q=Redmine%20ID:%205587>`__ \"\n\"Fixed unclosed ``ResultSet`` when storing a message to AMP-offline \"\n\"database in Derby causing deadlock.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:493\nmsgid \"\"\n\"`#5645 <https://projects.tigase.net/issues?q=Redmine%20ID:%205645>`__ \"\n\"Added fix for possible NPE when failing to retrieve beans.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:495\nmsgid \"\"\n\"`#5670 <https://projects.tigase.net/issues?q=Redmine%20ID:%205670>`__ \"\n\"config-dump now prints configuration for inactive components and beans to\"\n\" log.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:497\nmsgid \"\"\n\"`#5692 <https://projects.tigase.net/issues?q=Redmine%20ID:%205692>`__ \"\n\"Messages sent with negative priority were being occasionally dropped and \"\n\"not processed to ``OfflineMessageHandler``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:499\nmsgid \"\"\n\"`#5727 <https://projects.tigase.net/issues?q=Redmine%20ID:%205727>`__ \"\n\"Fixed potential issue with MySQL procedures not being killed properly.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:501\nmsgid \"\"\n\"`#5750 <https://projects.tigase.net/issues?q=Redmine%20ID:%205750>`__ \"\n\"Statistics now filter out zero-value results unless FINEST level is \"\n\"requested.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:503\nmsgid \"\"\n\"`#5831 <https://projects.tigase.net/issues?q=Redmine%20ID:%205831>`__ \"\n\"Fixed occurrence of ``OutOfMemory`` error.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:505\nmsgid \"\"\n\"`#5864 <https://projects.tigase.net/issues?q=Redmine%20ID:%205864>`__ \"\n\"Fixed NPE when executing BOSH pre-bind script.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:507\nmsgid \"\"\n\"`#5867 <https://projects.tigase.net/issues?q=Redmine%20ID:%205867>`__ \"\n\"Fixed NPE occurring during configuration dump.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:509\nmsgid \"\"\n\"`#6000 <https://projects.tigase.net/issues?q=Redmine%20ID:%206000>`__ \"\n\"Fixed a few issues with dynamic rosters properly handling presence \"\n\"subscription requests.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:511\nmsgid \"\"\n\"`#6006 <https://projects.tigase.net/issues?q=Redmine%20ID:%206006>`__ \"\n\"Improved configuration file and DB Schema handling.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:513\nmsgid \"\"\n\"`#6041 <https://projects.tigase.net/issues?q=Redmine%20ID:%206041>`__ \"\n\"Fixed potential issue where vhosts DB could be overwritten by vhosts \"\n\"configuration in ``init.config``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:515\nmsgid \"\"\n\"`#6078 <https://projects.tigase.net/issues?q=Redmine%20ID:%206078>`__ \"\n\"Fixed ``ClusterConnectionManager`` to use custom_elements_limit instead \"\n\"of a fixed value.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:517\nmsgid \"\"\n\"`#6080 <https://projects.tigase.net/issues?q=Redmine%20ID:%206080>`__ \"\n\"Fixed Packet Filtering to not filter cluster node information requests.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:519\nmsgid \"\"\n\"`#6083 <https://projects.tigase.net/issues?q=Redmine%20ID:%206083>`__ \"\n\"Fixed clustered mode shutting down server when certain components are \"\n\"disabled.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:521\nmsgid \"\"\n\"`#6135 <https://projects.tigase.net/issues?q=Redmine%20ID:%206135>`__ \"\n\"Tigase now properly enabled selective TLS if not enabled globally.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:523\nmsgid \"\"\n\"`#6140 <https://projects.tigase.net/issues?q=Redmine%20ID:%206140>`__ \"\n\"Fixed issue while sending server welcome message.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:525\nmsgid \"\"\n\"`#6141 <https://projects.tigase.net/issues?q=Redmine%20ID:%206141>`__ \"\n\"Fixed NPE at startup.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:527\nmsgid \"\"\n\"`#6234 <https://projects.tigase.net/issues?q=Redmine%20ID:%206234>`__ \"\n\"Fixed an error where an error message would repeat unnecessarily.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:529\nmsgid \"\"\n\"`#6284 <https://projects.tigase.net/issues?q=Redmine%20ID:%206284>`__ Ad-\"\n\"hoc commands now refresh SSL Certificate, and restart is no longer \"\n\"required.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:531\nmsgid \"\"\n\"`#6293 <https://projects.tigase.net/issues?q=Redmine%20ID:%206293>`__ \"\n\"Server no longer sends no response upon setting empty photo in vCard.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:533\nmsgid \"\"\n\"`#6263 <https://projects.tigase.net/issues?q=Redmine%20ID:%206263>`__ \"\n\"Fixed missing namespaces in responses from adhoc commands.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:535\nmsgid \"\"\n\"`#6400 <https://projects.tigase.net/issues?q=Redmine%20ID:%206400>`__ \"\n\"Added a proper error when max-queue-size is too small and server cannot \"\n\"start.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:537\nmsgid \"\"\n\"`#6408 <https://projects.tigase.net/issues?q=Redmine%20ID:%206408>`__ \"\n\"Fixed an issue where single WebSocket frames contained multiple XML \"\n\"stanzas instead of one per frame.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:539\nmsgid \"\"\n\"`#6411 <https://projects.tigase.net/issues?q=Redmine%20ID:%206411>`__ \"\n\"Main kernel is now called to smooth shutdown. Further, timeout periods \"\n\"are opened up for large instances.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:541\nmsgid \"\"\n\"`#6574 <https://projects.tigase.net/issues?q=Redmine%20ID:%206574>`__ SSL\"\n\" certificate upload handling is now fixed within cluster mode.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:543\nmsgid \"\"\n\"`#6598 <https://projects.tigase.net/issues?q=Redmine%20ID:%206598>`__ \"\n\"Fixed EventBus Registration connection issues between cluster nodes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:545\nmsgid \"\"\n\"`#6658 <https://projects.tigase.net/issues?q=Redmine%20ID:%206658>`__ \"\n\"Cluster connections no longer potentially keep open connection after \"\n\"cluster is no longer connected or available.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:547\nmsgid \"\"\n\"`#6749 <https://projects.tigase.net/issues?q=Redmine%20ID:%206749>`__ \"\n\"Fixed schema parsing for DerbyDB.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:549\nmsgid \"\"\n\"`#6776 <https://projects.tigase.net/issues?q=Redmine%20ID:%206776>`__ \"\n\"Fixed failing Websocket connections if header contains more than one \"\n\"value.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:551\nmsgid \"\"\n\"`#6875 <https://projects.tigase.net/issues?q=Redmine%20ID:%206875>`__ \"\n\"Fixed an issue where C2S connections could be accepted before \"\n\"SessionManager was initialized.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:553\nmsgid \"\"\n\"`#7037 <https://projects.tigase.net/issues?q=Redmine%20ID:%207037>`__ \"\n\"Fixed error while parsing negative values from ``config.tdsl`` file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:555\nmsgid \"\"\n\"`#7055 <https://projects.tigase.net/issues?q=Redmine%20ID:%207055>`__ \"\n\"Improvements to metaspace use and other memory use tweaks.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:557\nmsgid \"\"\n\"`#7304 <https://projects.tigase.net/issues?q=Redmine%20ID:%207304>`__ \"\n\"Virtual host logs now properly follow log size limits.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:559\nmsgid \"\"\n\"`#7431 <https://projects.tigase.net/issues?q=Redmine%20ID:%207431>`__ \"\n\"AdHoc requests between the same user with different resources are no \"\n\"longer dropped with \\\\`NoConnectionIdExecption`error.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:561\nmsgid \"\"\n\"`#7434 <https://projects.tigase.net/issues?q=Redmine%20ID:%207434>`__ \"\n\"Adjusted ``SeeOtherHotDualIP`` to use new table name in cluster nodes \"\n\"database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:563\nmsgid \"\"\n\"`#7491 <https://projects.tigase.net/issues?q=Redmine%20ID:%207491>`__ \"\n\"Stacktraces from ``CertificateContainer`` are no longer printed to \"\n\"tigase-console.log, but will be printed to tigase.log.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:565\nmsgid \"\"\n\"`#7687 <https://projects.tigase.net/issues?q=Redmine%20ID:%207687>`__ \"\n\"Fixed an error where connections failed after authentication timeout were\"\n\" marked as active after cleanup.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:567\nmsgid \"\"\n\"`#7747 <https://projects.tigase.net/issues?q=Redmine%20ID:%207747>`__ \"\n\"Fixed ``ClusterRepoItemEvent`` serialization issues causing unsupported \"\n\"conversion error in cluster mode.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:569\nmsgid \"\"\n\"`#7495 <https://projects.tigase.net/issues?q=Redmine%20ID:%207495>`__ fix\"\n\" issue with not all logs being obfuscated, added testcase, documentation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:571\nmsgid \"\"\n\"`#8305 <https://projects.tigase.net/issues?q=Redmine%20ID:%208305>`__ fix\"\n\" issue with SeeOtherHostDualIP when using MongoDB\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:576\nmsgid \"Component Changes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:579\nmsgid \"AMP\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:581\nmsgid \"\"\n\"`#7301 <https://projects.tigase.net/issues?q=Redmine%20ID:%207301>`__ \"\n\"Tigase AMP component now uses multiple processing threads.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:586\nmsgid \"PubSub\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:588\nmsgid \"\"\n\"`#5033 <https://projects.tigase.net/issues?q=Redmine%20ID:%205033>`__ \"\n\"PubSub now compatible with using emojis in pubsub items.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:590\nmsgid \"\"\n\"`#5693 <https://projects.tigase.net/issues?q=Redmine%20ID:%205693>`__ \"\n\"Fixed parsing configuration of SessionManager processors.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:592\nmsgid \"\"\n\"`#5766 <https://projects.tigase.net/issues?q=Redmine%20ID:%205766>`__ \"\n\"PubSub now writes to all databases with UTC timestamp.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:594\nmsgid \"\"\n\"`#5953 <https://projects.tigase.net/issues?q=Redmine%20ID:%205953>`__ \"\n\"Fixed presences not being removed from ``presenceByService`` collection \"\n\"if client disconnects without ``<unavailable/>`` presence being sent.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:596\nmsgid \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"version changed to PubSub v4.0.0.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:598\nmsgid \"\"\n\"`#7707 <https://projects.tigase.net/issues?q=Redmine%20ID:%207707>`__ \"\n\"Fixed potential NPE in PubSub.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:603\nmsgid \"http-api\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:605\nmsgid \"\"\n\"`#4873 <https://projects.tigase.net/issues?q=Redmine%20ID:%204873>`__ \"\n\"Support added to display timestamp fields as data, time, and timezone \"\n\"fields.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:607\nmsgid \"\"\n\"`#4876 <https://projects.tigase.net/issues?q=Redmine%20ID:%204876>`__ \"\n\"Implemented using XML repository for new setups, and updated default \"\n\"config to use this.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:609\nmsgid \"\"\n\"`#4888 <https://projects.tigase.net/issues?q=Redmine%20ID:%204888>`__ \"\n\"``http-api`` now is enabled by default.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:611\nmsgid \"\"\n\"`#5209 <https://projects.tigase.net/issues?q=Redmine%20ID:%205209>`__ \"\n\"Updated visual styling of pages hosted by component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:613\nmsgid \"\"\n\"`#5290 <https://projects.tigase.net/issues?q=Redmine%20ID:%205290>`__ \"\n\"Fixed invalid property name.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:615\nmsgid \"\"\n\"`#5316 <https://projects.tigase.net/issues?q=Redmine%20ID:%205316>`__ \"\n\"Account Registration now can now require and send confirmation E-mails.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:617\nmsgid \"\"\n\"`#5415 <https://projects.tigase.net/issues?q=Redmine%20ID:%205415>`__ Web\"\n\" Setup now checks configuration for message archive conflicts.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:619\nmsgid \"\"\n\"`#5460 <https://projects.tigase.net/issues?q=Redmine%20ID:%205460>`__ \"\n\"MongoDB now supported through web-setup.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:621\nmsgid \"\"\n\"`#5717 <https://projects.tigase.net/issues?q=Redmine%20ID:%205717>`__ \"\n\"Fixed default values of check-boxes in admin UI not being shown.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:623\nmsgid \"\"\n\"`#5950 <https://projects.tigase.net/issues?q=Redmine%20ID:%205950>`__ \"\n\"Supported added for `XEP-0363: HTTP File Upload \"\n\"<https://xmpp.org/extensions/xep-0363.html>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:625\nmsgid \"\"\n\"`#6159 <https://projects.tigase.net/issues?q=Redmine%20ID:%206159>`__ \"\n\"Fixed NPE thrown if scripts directory is not present.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:627\nmsgid \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"version changed to tigase-http-api v2.0.0.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:629\nmsgid \"\"\n\"`#6212 <https://projects.tigase.net/issues?q=Redmine%20ID:%206212>`__ \"\n\"Added mechanism for password changing through HTTP API.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:631\nmsgid \"\"\n\"`#7307 <https://projects.tigase.net/issues?q=Redmine%20ID:%207307>`__ \"\n\"Fixed scripts returning 404 while handling rest/user/ requests even \"\n\"though user exists.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:633\nmsgid \"\"\n\"`#7178 <https://projects.tigase.net/issues?q=Redmine%20ID:%207178>`__ Ad-\"\n\"hoc commands are now categorized in groups for better organization.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:635\nmsgid \"\"\n\"`#7568 <https://projects.tigase.net/issues?q=Redmine%20ID:%207568>`__ \"\n\"Added timeout reading for HTTP request headers, added configurable \"\n\"``accept-timeout``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:640\nmsgid \"message-archive\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:642\nmsgid \"\"\n\"`#4867 <https://projects.tigase.net/issues?q=Redmine%20ID:%204867>`__ \"\n\"fixed issue when changing MA jid.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:644\nmsgid \"\"\n\"`#4888 <https://projects.tigase.net/issues?q=Redmine%20ID:%204888>`__ \"\n\"``message-archive`` is enabled by default.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:646\nmsgid \"\"\n\"`#5033 <https://projects.tigase.net/issues?q=Redmine%20ID:%205033>`__ \"\n\"Update message archive to be compatible with emojis.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:648\nmsgid \"\"\n\"`#5391 <https://projects.tigase.net/issues?q=Redmine%20ID:%205391>`__ \"\n\"Added missing query statement block starts and ends to be compatible with\"\n\" SQL Server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:650\nmsgid \"\"\n\"`#5604 <https://projects.tigase.net/issues?q=Redmine%20ID:%205604>`__ \"\n\"Modified access to static fields and functions.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:652\nmsgid \"\"\n\"`#5681 <https://projects.tigase.net/issues?q=Redmine%20ID:%205681>`__ \"\n\"Fixed duplication of groupchat messages with different ids by modifying \"\n\"hash algorithm.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:654\nmsgid \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"version changed to message-archive v2.0.0.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:656\nmsgid \"\"\n\"`#7615 <https://projects.tigase.net/issues?q=Redmine%20ID:%207615>`__ \"\n\"``feature-not-implemented`` response no longer occurs when removing \"\n\"stored messages.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:661\nmsgid \"MUC\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:663\nmsgid \"\"\n\"`#4888 <https://projects.tigase.net/issues?q=Redmine%20ID:%204888>`__ \"\n\"``muc`` now is enabled by default.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:665\nmsgid \"\"\n\"`#5033 <https://projects.tigase.net/issues?q=Redmine%20ID:%205033>`__ MUC\"\n\" component is now compatible with emojis.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:667\nmsgid \"\"\n\"`#5066 <https://projects.tigase.net/issues?q=Redmine%20ID:%205066>`__ \"\n\"Fixed issues working with MongoDB repository.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:669\nmsgid \"\"\n\"`#5085 <https://projects.tigase.net/issues?q=Redmine%20ID:%205085>`__ \"\n\"Removed invalid annotation parameter values.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:671\nmsgid \"\"\n\"`#5559 <https://projects.tigase.net/issues?q=Redmine%20ID:%205559>`__ \"\n\"Fixed NPE while changing default room configuration.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:673\nmsgid \"\"\n\"`#5666 <https://projects.tigase.net/issues?q=Redmine%20ID:%205666>`__ \"\n\"User may add more than one ``<item/>`` elements to query when querying \"\n\"room members.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:675\nmsgid \"\"\n\"`#5715 <https://projects.tigase.net/issues?q=Redmine%20ID:%205715>`__ \"\n\"Welcome messages may now be disabled globally, or in individual room \"\n\"configurations.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:677\nmsgid \"\"\n\"`#5736 <https://projects.tigase.net/issues?q=Redmine%20ID:%205736>`__ \"\n\"Rooms with no subject now return empty ``<subject/>`` element, as per \"\n\"`XEP-0048 7.2.16 <https://xmpp.org/extensions/xep-0045.html#enter-\"\n\"subject>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:679\nmsgid \"\"\n\"`#5813 <https://projects.tigase.net/issues?q=Redmine%20ID:%205813>`__ \"\n\"Fixed NPE during room creation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:681\nmsgid \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"version changed to tigase-muc v3.0.0.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:683\nmsgid \"\"\n\"`#6395 <https://projects.tigase.net/issues?q=Redmine%20ID:%206395>`__ \"\n\"Fixed ``tigase.db.UserNotFoundException`` during retrieval of MUC user.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:685\nmsgid \"\"\n\"`#6734 <https://projects.tigase.net/issues?q=Redmine%20ID:%206734>`__ \"\n\"Introduced ``muc#roomconfig_maxresources`` to allow configuration of max \"\n\"number of resources for a single occupant.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:687\nmsgid \"\"\n\"`#7443 <https://projects.tigase.net/issues?q=Redmine%20ID:%207443>`__ \"\n\"Disabled XEP-0091 by default, added history attribute validation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:692\nmsgid \"socks5 Proxy\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:694\nmsgid \"\"\n\"`#2750 <https://projects.tigase.net/issues?q=Redmine%20ID:%202750>`__ \"\n\"Cleanup of code and removal of empty javadocs.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:696\nmsgid \"\"\n\"`#5867 <https://projects.tigase.net/issues?q=Redmine%20ID:%205867>`__ \"\n\"Fixed NPE during configuration dump when component is disabled.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:698\nmsgid \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"version changed to tigase-socks5 v2.0.0.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:703\nmsgid \"stats\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:705\nmsgid \"\"\n\"`#5206 <https://projects.tigase.net/issues?q=Redmine%20ID:%205206>`__ \"\n\"Fixed exception causing duplicate error entry.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:707\nmsgid \"\"\n\"`#5728 <https://projects.tigase.net/issues?q=Redmine%20ID:%205728>`__ \"\n\"Fixed ``MySQLIntegrityConstraintViolationException`` in upload handler.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:709\nmsgid \"\"\n\"`#6161 <https://projects.tigase.net/issues?q=Redmine%20ID:%206161>`__ \"\n\"Removed usage of classes from javax.xml.ws package for JDKv9 \"\n\"compatibility.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:714\nmsgid \"STUN Server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:716\nmsgid \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"version changed to tigase-stun v2.0.0.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:721\nmsgid \"WebSocket\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:723\nmsgid \"\"\n\"`#6481 <https://projects.tigase.net/issues?q=Redmine%20ID:%206481>`__ \"\n\"Websocket component has been improved to be more compliant with `rfc6455 \"\n\"<https://tools.ietf.org/html/rfc6455>`__\"\nmsgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Administration/Security/_Security.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Administration/Security/_Security.rst:2\nmsgid \"Security\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/_Security.rst:4\nmsgid \"\"\n\"The articles here cover advanced security features built into to Tigase \"\n\"Server, and some options for adding your own levels of security.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/XEP-0191_Blocking_Command.inc:2\nmsgid \"XEP-0191: Blocking Command\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/XEP-0191_Blocking_Command.inc:4\nmsgid \"\"\n\"The simplest security feature, however, inside an XMPP server is the \"\n\"ability to block users and JIDS. `XEP-0191 \"\n\"<http://xmpp.org/extensions/xep-0191>`__ specifies the parameters of \"\n\"simple blocking without using privacy lists. Below is a breakdown and \"\n\"some sample commands you may find helpful. To enable this feature, be \"\n\"sure the following is in your ``config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/XEP-0191_Blocking_Command.inc:13\nmsgid \"\"\n\"If you have other plugins running, then just add ``'urn:xmpp:blocking' ()\"\n\" {}`` to the list to activate this feature.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/XEP-0191_Blocking_Command.inc:15\nmsgid \"\"\n\"To confirm if your installation of Tigase supports this feature, a quick \"\n\"disco#info of your server should reveal the following feature:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/XEP-0191_Blocking_Command.inc:21\nmsgid \"\"\n\"Blocked users are stored on the server on a per-JID basis, so one user \"\n\"may only see his or her blocked JIDs. Lists of blocked JIDs will return \"\n\"as an IQ stanza with a list of <item> fields. To retrieve the blocklist, \"\n\"the following command is issued:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/XEP-0191_Blocking_Command.inc:29\nmsgid \"The server responds:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/XEP-0191_Blocking_Command.inc:40\nmsgid \"\"\n\"To block a JID, a similar stanza to the one above is sent to the server \"\n\"with the items of the blocked JIDs you wish to add:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/XEP-0191_Blocking_Command.inc:50\nmsgid \"\"\n\"The server will then push an unavailable presence to blocked contacts. \"\n\"Communication between a contact that is blocked, and an entity that \"\n\"blocked it will result in a <not-acceptable> error:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/XEP-0191_Blocking_Command.inc:62\nmsgid \"\"\n\"Unblocking a contact is just as easy as blocking, send an unblock stanza \"\n\"to the server:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/XEP-0191_Blocking_Command.inc:72\nmsgid \"\"\n\"The server will begin pushing presence information to unblocked contacts \"\n\"and resources so long as permissions have not changed between.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/XEP-0191_Blocking_Command.inc:74\nmsgid \"\"\n\"You may also opt to unblock all contacts and essentially clear out your \"\n\"blocked list using the following command:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Account_Registration_Limits.inc:2\nmsgid \"Account Registration Limits\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Account_Registration_Limits.inc:4\nmsgid \"\"\n\"In order to protect Tigase servers from DOS attacks, a limit on number of\"\n\" account registrations per second has been implemented. This may be \"\n\"configured by adding the following line in the ``config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Account_Registration_Limits.inc:11\nmsgid \"\"\n\"This setting allows for 10 registrations from a single IP per second. If \"\n\"the limit is exceeded, a ``NOT_ALLOWED`` error will be returned.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Account_Registration_Limits.inc:13\nmsgid \"It is possible to create two separate counters as well:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Account_Registration_Limits.inc:26\nmsgid \"\"\n\"Here we have one for c2s with a limit of 3, and a global for all other \"\n\"connection managers set at 10.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Account_Registration_Limits.inc:28\nmsgid \"You can also set individual components limits as well:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:2\nmsgid \"Brute-force attack prevention\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:4\nmsgid \"\"\n\"Brute-force Prevention is designed to protect Tigase Server against user \"\n\"password guessing. It counts invalid login tries and when it is above \"\n\"limit, it locks login ability for specific time (soft ban). When invalid \"\n\"login counter reaches second level, account will be disabled permanently.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:7\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:49\nmsgid \"Configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:9\nmsgid \"\"\n\"Brute-force Prevention is configured by VHost. There is following lis of \"\n\"configuration parameters:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:12\nmsgid \"``brute-force-lock-enabled``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:12\nmsgid \"``boolean``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:12\nmsgid \"Brute Force Prevention Enabled\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:14\nmsgid \"``brute-force-lock-after-fails``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:14\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:16\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:18\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:20\nmsgid \"``long``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:14\nmsgid \"Number of allowed invalid login\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:16\nmsgid \"``brute-force-period-time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:16\nmsgid \"Time [sec] in what failed login tries are counted\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:18\nmsgid \"``brute-force-disable-after-fails``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:18\nmsgid \"Threshold beyond which account will be permanently disabled\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:20\nmsgid \"``brute-force-lock-time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:20\nmsgid \"Time [sec] of soft ban (first threshold)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:22\nmsgid \"``brute-force-mode``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:22\nmsgid \"``string``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:22\nmsgid \"Working mode (see :ref:`Working modes<WorkingModes>`)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:26\nmsgid \"Detailed statistics\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:28\nmsgid \"\"\n\"By default, in order not to pollute statistics, Brute-Force locker will \"\n\"only provide details about number of locker IPs and JIDs (and total \"\n\"number of locked attempts). In order to have detailed information about \"\n\"IPs and JIDs that has been locked in statistics you should use following \"\n\"configuration:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:41\nmsgid \"Working modes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:43\nmsgid \"There are three working modes:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:45\nmsgid \"\"\n\"``Ip`` - it counts invalid login tries from IP, and locks login ability \"\n\"(soft ban) for IP what reach the threshold\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:47\nmsgid \"\"\n\"``IpJid`` - it counts tries from IP to specific user account. Soft ban \"\n\"locks ability of login to specific JID from specific IP.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:49\nmsgid \"\"\n\"``Jid``- similar to ``IpJid`` but checks only JID. Soft ban locks ability\"\n\" of login to specific JID from all IPs.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:56\nmsgid \"Permanent ban\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:58\nmsgid \"\"\n\"In modes ``Jid`` and ``IpJid``, when invalid login counter reach \"\n\"threshold ``brute-force-disable-after-fails``, account status will be set\"\n\" o ``disabled``. To enable it again you should use `Re-Enable User \"\n\"<https://xmpp.org/extensions/xep-0133.html#reenable-users>`__ Ad-hoc \"\n\"Command.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:2\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:12\nmsgid \"Server Certificates\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:4\nmsgid \":ref:`Creating and Loading the Server Certificate in pem Files<certspem>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:9\nmsgid \"Creating and Loading the Server Certificate in pem Files\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:14\nmsgid \"\"\n\"Server certificates are needed when you use secure socket connections - \"\n\"SSL/TLS.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:16\nmsgid \"\"\n\"For secure socket connection a proper certificate is needed. You can \"\n\"either generate your own self-signed certificate or obtain certificate \"\n\"from trusted third party organization.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:18\nmsgid \"Here are steps how to obtain certificate from a trusted organization.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:20\nmsgid \"Generating your Own Certificates\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:22\nmsgid \"\"\n\"Self-signed certificates can be generated easily on a Linux system. \"\n\"Although it may not be considered a 'trusted' certificate authority, it \"\n\"can be useful to test server installations. **We do not recommend regular\"\n\" use of self-signed certificates**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:26\nmsgid \"\"\n\"that Tigase v5.0 and later can automatically create self signed PEM files\"\n\" if needed. However we will cover doing this process by hand.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:28\nmsgid \"\"\n\"This tutorial assumes you are running a Linux-based operating system with\"\n\" access to command shell, and the 'Openssl' package is installed on the \"\n\"system.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:30\nmsgid \"The process takes the following steps:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:31\nmsgid \"\"\n\"1. Create a local private key. This file ends with .key extension. It is \"\n\"recommended to create a new private key for the process.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:32\nmsgid \"\"\n\"2. Generate a certificate request. This file ends with the .csr extension\"\n\" and is the file sent to the Certificate Authority to be signed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:33\nmsgid \"\"\n\"3. CA signs private key. This can be done by your own computer, but can \"\n\"also be done by private CAs for a fee.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:34\nmsgid \"\"\n\"4. Results are obtained from the CA. This is a ``.crt`` file which \"\n\"contains a separate public certificate.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:35\nmsgid \"\"\n\"5. Combine the ``.csr`` and ``.crt`` file into a unified ``.pem`` file. \"\n\"Tigase requires keys to be non-password protected PEM files.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:37\nmsgid \"**Generate local private key.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:43\nmsgid \"\"\n\"This command generates a private key using a 1024 bit RSA algorithm. \"\n\"``-out`` designates the name of the file, in this case it will be \"\n\"``domain.com.key``. The exact name is not important, and the file will be\"\n\" created in whatever directory you are currently in.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:45\nmsgid \"**Generate a certificate request:.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:51\nmsgid \"\"\n\"This command generates a certificate request using the file specified \"\n\"after ``-key``, and the result file will be ``domain.com.csr``. You will \"\n\"be asked a series of questions to generate the request.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:68\nmsgid \"**Sign the Certificate Request:.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:70\nmsgid \"\"\n\"Now the .csr file will be signed by a Certificate Authority. In this \"\n\"tutorial, we will be self-signging our certificate. This practice however\"\n\" is generally not recommended, and you will receive notifications that \"\n\"your certificate is not trusted. There are commercial offers from \"\n\"companies to sign your certificate from trusted sources. Please see the \"\n\":ref:`Certificate From Other Providers<OtherSources>` section for more \"\n\"information.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:76\nmsgid \"\"\n\"This command signs the certificate for 365 days and generates the \"\n\"``domain.com.crt`` file. You can, of course use any number of days you \"\n\"like.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:78\nmsgid \"**Generate PEM file.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:80\nmsgid \"\"\n\"You should now have the following files in the working directory: ..\\\\\\\\ \"\n\"domain.com.key domain.com.csr domain.com.crt\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:86\nmsgid \"\"\n\"If the certificate is issued by third-party authority you will have to \"\n\"attach the certificate chain, that being certificate of the authority who\"\n\" has generated your certificate. You normally need to obtain certificates\"\n\" for your chain from the authority who has generated your certificate.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:88\nmsgid \"The result file should looks similar to:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:114\nmsgid \"\"\n\"For Tigase server as well as many other servers (Apache 2.x), the order \"\n\"is following; your domain certificate, your private key, authority \"\n\"issuing your certificate, root certificate.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:118\nmsgid \"\"\n\"**Tigase requires full certificate chain in PEM file (described above)! \"\n\"Different applications may require pem file with certificates and private\"\n\" key in different order. So the same file may not be necessarily used by \"\n\"other services like Web server or e-mail server. Currently, Tigase can \"\n\"automatically sort certificates in PEM file while loading it.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:123\nmsgid \"Installing/Loading Certificate To the Tigase Server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:125\nmsgid \"\"\n\"Installing and loading certificates is very easy. The server can load all\"\n\" certificates directly from **pem** files. You just need to create a \"\n\"separate pem file for each of your virtual domains and put the file in a \"\n\"directory accessible by the server. Tigase server can automatically load \"\n\"all **pem** files found in given directory. By default, and to make \"\n\"things easy, we recommend the ``Tigase/certs`` directory.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:127\nmsgid \"\"\n\"It’s also possible to use: \\\\* Admin ad-hoc command via XMPP client - you\"\n\" should navigate to Service Discovery of your server and in the list of \"\n\"commands for ``VHost Manager`` component select ``Add SSL Certificate`` \"\n\"and then follow instructions \\\\* Admin WebUI - open \"\n\"``http://<host>/admin``, navigate to ``Other`` category and in it select \"\n\"``Add SSL Certificate`` and then follow instructions \\\\* REST API - make \"\n\"a ``POST`` request to http://localhost:8080/rest/adhoc/vhost-\"\n\"man@domain.com with payload containing your certificate; to get the \"\n\"required form fields make ``GET`` request to the same endpoint\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:132\nmsgid \"Certificate From Other Providers\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:134\nmsgid \"\"\n\"There is number of certificate providers offering certificates either for\"\n\" free or for money. You can use any of them, however you have to be aware\"\n\" that sometimes certificates might not be recognized by all XMPP servers,\"\n\" especially if it’s one from a new provider. Here is an example list of \"\n\"providers:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:136\nmsgid \"\"\n\"LetsEncrypt - please see :ref:`Installing LetsEncrypt Certificates in \"\n\"Your Linux System<LetsEncryptCertificate>` for details\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:138\nmsgid \"\"\n\"`CAcert <https://www.cacert.org/>`__ - free certificates with Web GUI. \"\n\"(WARNING: it’s not widely accepted)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:140\nmsgid \"\"\n\"`Verisign <https://www.verisign.com/>`__ - very expensive certificates \"\n\"comparing to above provides but the provider is recognized by everybody. \"\n\"If you have a certificate from Verisign you can be sure it is identified \"\n\"as a valid certificate.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:142\nmsgid \"\"\n\"`Comodo Certificate Authority <http://www.comodo.com/business-security\"\n\"/digital-certificates/ssl-certificates.php>`__ offers different kind of \"\n\"commercial certificates\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:144\nmsgid \"\"\n\"To obtain certificate from a third party authority you have to go to its \"\n\"website and request the certificate using certificate request generated \"\n\"above. I cannot provide any instructions for this as each of the \"\n\"providers listed have different requirements and interfaces.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:146\nmsgid \"\"\n\"We **highly** recommend using LetsEncrypt keys to self-sign and secure \"\n\"your domain. Instructions are in the :ref:`next \"\n\"section<LetsEncryptCertificate>`.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:149\nmsgid \"Using one certificate for multiple domains\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:153\nmsgid \"\"\n\"Tigase tries to be *smart* and automatically detects wildcard domain and \"\n\"alternative domains so it’s not needed to duplicate same certificate in \"\n\"multiple files to match domains - same file will be loaded and make \"\n\"available for all domains (CNames) available in the certificate.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:158\nmsgid \"Installing LetsEncrypt Certificates in Your Linux System\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:160\nmsgid \"\"\n\"LetsEncrypt is a trusted CA that provides free security certificates. \"\n\"Unlike previously self-signed certificates, we can use LetsEncrypt \"\n\"Certificates to certify your domains from a trusted source.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:162\nmsgid \"\"\n\"Please refer to official `certbot User Guide \"\n\"<https://certbot.eff.org/docs/using.html>`__ for details how to install \"\n\"and operate the tool, choosing desired method of domain authentication \"\n\"(DNS or webserver). After successful execution the certificate with all \"\n\"related files will be stored under ``/etc/letsencrypt/live/$domain``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:169\nmsgid \"In that directory, you will find four files:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:171\nmsgid \"``privkey.pem`` - private key for the certificate\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:173\nmsgid \"``cert.pem`` - contains the server certificate by itself\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:175\nmsgid \"\"\n\"``chain.pem`` - contains the additional intermediate certificate or \"\n\"certificates\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:177\nmsgid \"\"\n\"``fullchain.pem`` - all certificates, including server certificate (aka \"\n\"leaf certificate or end-entity certificate). The server certificate is \"\n\"the first one in this file, followed by any intermediates.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:179\nmsgid \"\"\n\"For Tigase XMPP Server, we are only concerned with ``privkey.pem`` and \"\n\"``fullchain.pem`` (or ``chain.pem`` - please consider actual issuers and \"\n\"certification chain!).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:181\nmsgid \"\"\n\"At this point we will need to obtain the root and intermediate \"\n\"certificates, this can be done by downloading these certificates from the\"\n\" `LetsEncrypt Chain of Trust website \"\n\"<https://letsencrypt.org/certificates/>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:185\nmsgid \"\"\n\"Please pay utmost attention to the actual certificate issuers and make \"\n\"sure that the certification chain is maintained!\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:187\nmsgid \"\"\n\"On the time of the writing, LetsEncrypt was providing domain certificates\"\n\" issued by ``R3`` CertificateAuthorigy (CA). In order to provide complete\"\n\" chain to the root CA you should get Let’s Encrypt R3 (``RSA 2048, O = \"\n\"Let’s Encrypt, CN = R3``) certificate. Depending on desired certification\"\n\" chain you have two options: 1) (default and recommended) using own \"\n\"LetsEncrypt CA: a) ``R3`` certificate signed by ISRG Root X1: \"\n\"https://letsencrypt.org/certs/lets-encrypt-r3.pem b) ``ISRG Root X1`` \"\n\"root certificate: https://letsencrypt.org/certs/isrgrootx1.pem 2) \"\n\"(legacy, option more compatible with old systems): cross-signed \"\n\"certificate by IdenTrust: a) ``R3`` certificate cross-signed by \"\n\"IdenTrust: https://letsencrypt.org/certs/lets-encrypt-r3-cross-signed.pem\"\n\" b) ``TrustID X3 Root`` from IdenTrust: \"\n\"https://letsencrypt.org/certs/trustid-x3-root.pem.txt\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:189\nmsgid \"Considering first (recommended) option, you may obtain them using wget:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:196\nmsgid \"\"\n\"These are the root certificate, and the intermediate certificate signed \"\n\"by root certificate.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:200\nmsgid \"\"\n\"IdenTrust cross-signed certificate will not function properly in the \"\n\"future!\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:202\nmsgid \"\"\n\"Take the contents of your ``privkey.pem``, certificate, and combine them \"\n\"with the contents of ``isrgrootx1.pem`` and ``lets-encrypt-r3.pem`` into \"\n\"a single pem certificate.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:204\nmsgid \"\"\n\"Depending on your configuration you either need to name the file after \"\n\"your domain such as ``mydomain.com.pem`` and place it under ``certs/`` \"\n\"subdirectory of Tigase XMPP Server installation or update it using admin \"\n\"ad-hoc (see :ref:`Storing and managing certificates<certificateStorage>`)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:206\nmsgid \"\"\n\"If you moved all certs to a single directory, you may combine them using \"\n\"the following command under \\\\*nix operating systems:.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:215\nmsgid \"\"\n\"If you are using ``isrgrootx1`` root make sure you use ``cert.pem`` file \"\n\"instead of ``fullchain.pem``, which uses different intermediate \"\n\"certificate ( `Let’s Encrypt Authority X3 (IdenTrust cross-signed) \"\n\"<https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem.txt>`__ )\"\n\" and you will have to use `DST Root CA X3 \"\n\"<https://letsencrypt.org/certs/trustid-x3-root.pem.txt>`__ certificate!\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:217\nmsgid \"Your certificate should look something like this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:239\nmsgid \"\"\n\"LetsEncrypt certificates expire 90 days from issue and need to be renewed\"\n\" in order for them to remain valid!\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:241\nmsgid \"You can check your certificate with utility class:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:248\nmsgid \"Let’s encrypt and DNS verification\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:250\nmsgid \"\"\n\"The only way to obtain wildcard (``*.domain.com``) certificate is via DNS\"\n\" verification. Certbot support a number of DNS operators - you can check \"\n\"if your DNS provider is listed by executing ``$ certbot plugins``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:252\nmsgid \"AWS Route53\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:254\nmsgid \"If you want to use it with Amazon Cloud you should install plugin for AWS:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:262\nmsgid \"\"\n\"If you are using certbot under macOS and you installed it via brew then \"\n\"you should use: ``$( brew --prefix certbot )/libexec/bin/pip install \"\n\"certbot-dns-route53``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:264\nmsgid \"\"\n\"You should store your credentials in ``~/.aws/credentials`` (you may want\"\n\" to create dedicated policy for updating DNS as described in `plugin’s \"\n\"documentation <https://certbot-dns-route53.readthedocs.io/en/stable/>`__:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:272\nmsgid \"\"\n\"And afterward you should execute ``certbot`` with ``--dns-route53`` \"\n\"parameter\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:275\nmsgid \"Certbot update hook and Tigase API\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:277\nmsgid \"\"\n\"For greater automation it’s possible to automate updating certificate \"\n\"obtained with ``certbot`` in Tigase XMPP Server. You should use following\"\n\" deploy hook - either add it to ``/etc/letsencrypt/renewal-\"\n\"hooks/deploy/`` or use it directly in ``certboot`` commandline with \"\n\"``--deploy-hook`` parameter (in the latter case, it will be added to \"\n\"particular domain configuration so it’s not necessary to specify \"\n\"UPDATE_DOMAINS).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:281\nmsgid \"\"\n\"Please adjust account credentials used for deployment (``USER``, \"\n\"``PASS``, ``DOMAIN``) as well as paths to Let’s Encrypt certificates \"\n\"(*ISRG Root X1* named ``isrgrootx1.pem`` and *Let’s Encrypt Authority X3*\"\n\" named ``letsencryptauthorityx3.pem``)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:364\nmsgid \"\"\n\"If you are not using wildcard certificate when you have to provide \"\n\"certificate for main domain as well as certificates for subdomains that \"\n\"mach all components that you want to expose (muc, pubsub, push, etc…)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:369\nmsgid \"Storing and managing certificates\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:372\nmsgid \"Filesystem\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:374\nmsgid \"\"\n\"By default Tigase loads and stores certificates in ``certs/`` \"\n\"subdirectory. Each *domain* certificate should be stored in a file which \"\n\"filename consists of domain name and ``.pem`` extension, i.e. \"\n\"``<domain>.pem``. For example for domain tigase.net it would be \"\n\"``certs/tigase.net.pem``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:378\nmsgid \"\"\n\"Tigase tries to be *smart* and automatically detects wildcard domain and \"\n\"alternative domains so it’s not needed to duplicate same certificate in \"\n\"multiple files to match domains.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:381\nmsgid \"Database repository\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:383\nmsgid \"\"\n\"Alternatively it’s possible to use database as a storage for the \"\n\"certificates. Upon enabling it certificates won’t be read nor stored to \"\n\"the filesystem. You can enable it by adding ``repository () {}`` bean to \"\n\"``'certificate-container' () {}`` in your TDSL configuration file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:391\nmsgid \"\"\n\"If you are using database repository then you manage/update certificates \"\n\"using either ad-hoc command ``Add SSL certificate`` from *VHost Manager* \"\n\"or via HTTP REST API.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:4\nmsgid \"Custom Authentication Connectors\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:6\nmsgid \"\"\n\"This article presents configuration options available to the \"\n\"administrator and describe how to set Tigase server up to use user \"\n\"accounts data from a different database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:8\nmsgid \"\"\n\"The first thing to know is that Tigase server always opens 2 separate \"\n\"connections to the database. One connection is used for user login data \"\n\"and the other is for all other user data like the user roster, vCard, \"\n\"private data storage, privacy lists and so on…​\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:10\nmsgid \"\"\n\"In this article we still assume that Tigase server keeps user data in \"\n\"it’s own database and only login data is retrieved from the external \"\n\"database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:12\nmsgid \"At the moment Tigase offers following authentication connectors:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:14\nmsgid \"\"\n\"``mysql``, ``pgsql``, ``derby`` - standard authentication connector used \"\n\"to load user login data from the main user database used by the Tigase \"\n\"server. In fact the same physical implementation is used for all JDBC \"\n\"databases.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:16\nmsgid \"\"\n\"``drupal`` - is the authentication connector used to integrate the Tigase\"\n\" server with `Drupal CMS <http://drupal.org/>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:18\nmsgid \"\"\n\"``tigase-custom`` - is the authentication connector which can be used \"\n\"with any database. Unlike the 'tigase-auth' connector it allows you to \"\n\"define SQL queries in the configuration file. The advantage of this \"\n\"implementation is that you don’t have to touch your database. You can use\"\n\" either simple plain SQL queries or stored procedures. The configuration \"\n\"is more difficult as you have to enter carefully all SQL queries in the \"\n\"config file and changing the query usually involves restarting the \"\n\"server. For more details about this implementation and all configuration \"\n\"parameters please refer to :ref:`Tigase Custom Auth \"\n\"documentation<custonAuthConnector>`.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:20\nmsgid \"\"\n\"|ss| ``tigase-auth``\\\\ |se|\\\\  (**DEPRECATED**) - is the authentication \"\n\"connector which can be used with any database. It executes stored \"\n\"procedures to perform all actions. Therefore it is a very convenient way \"\n\"to integrate the server with an external database if you don’t want to \"\n\"expose the database structure. You just have to provide a set of stored \"\n\"procedures in the database. While implementing all stored procedures \"\n\"expected by the server might be a bit of work it allows you to hide the \"\n\"database structure and change the SP implementation at any time. You can \"\n\"add more actions on user login/logout without restarting or touching the \"\n\"server. And the configuration on the server side is very simple. For \"\n\"detailed description of this implementation please refer to :ref:`Tigase \"\n\"Auth documentation<tigaseAuthConnector>`.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:22\nmsgid \"\"\n\"As always the simplest way to configure the server is through the \"\n\"``config.tdsl`` file. In the article describing this file you can find \"\n\"long list with all available options and all details how to handle it. \"\n\"For the authentication connector setup however we only need 2 options:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:38\nmsgid \"\"\n\"For example if you store authentication data in a ``drupal`` database on \"\n\"``localhost`` your settings would be:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:53\nmsgid \"\"\n\"You have to use a class name if you want to attach your own \"\n\"authentication connector.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:55\nmsgid \"Default is:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:65\nmsgid \"\"\n\"In the same exact way you can setup connector for any different database \"\n\"type.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:67\nmsgid \"For example, drupal configuration is below\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:77\nmsgid \"Or tigase-custom authentication connector.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:87\nmsgid \"The different ``cls`` or classes are:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:89\nmsgid \"Drupal - ``tigase.db.jdbc.DrupalWPAuth``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:91\nmsgid \"\"\n\"MySQL, Derby, PostgreSQL, MS SQL Server - \"\n\"``tigase.db.jdbc.JDBCRepository``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:93\nmsgid \"\"\n\"You can normally skip configuring connectors for the default Tigase \"\n\"database format: ``mysql``, ``pgsql`` and ``derby``, ``sqlserver`` as \"\n\"they are applied automatically if the parameter is missing.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:95\nmsgid \"\"\n\"One more important thing to know is that you will have to modify \"\n\"``authRepository`` if you use a custom authentication connector. This is \"\n\"because if you retrieve user login data from the external database this \"\n\"external database is usually managed by an external system. User accounts\"\n\" are added without notifying Tigase server. Then, when the user logs in \"\n\"and tries to retrieve the user roster, the server can not find such a \"\n\"user in the roster database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:99\nmsgid \"\"\n\"To keep user accounts in sync between the authentication database and the\"\n\" main user database you have to add following option to the end of the \"\n\"database connection URL: ``autoCreateUser=true``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:101\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:97\nmsgid \"For example:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:111\nmsgid \"\"\n\"If you are interested in even further customizing your authentication \"\n\"connector by writing your own queries or stored procedures, please have a\"\n\" look at the following guides:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:113\nmsgid \":ref:`Tigase Auth guide<tigaseAuthConnector>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:115\nmsgid \":ref:`Tigase Custom Auth guide<custonAuthConnector>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:4\nmsgid \"Tigase Auth Connector (DEPRECATED)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:10\nmsgid \"\"\n\"The Tigase Auth connector with shortcut name: **tigase-auth** is \"\n\"implemented in the class: `tigase.db.jdbc.TigaseAuth \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/db/jdbc/TigaseAuth.java>`__. It \"\n\"allows you to connect to any external database to perform user \"\n\"authentication. You can find more details how to setup a custom connector\"\n\" in the :ref:`Custom Authentication Connectors<customAuthentication>` \"\n\"guide.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:12\nmsgid \"\"\n\"To make this connector working you have to prepare your database to offer\"\n\" set of stored procedures for Tigase server to perform all the \"\n\"authentication actions. The best description is the example schema with \"\n\"all the stored procedures defined - please refer to the Tigase \"\n\"repositories for the schema definition files (each component has it’s \"\n\"dedicated schema). For example:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:14\nmsgid \"\"\n\"`tigase-server <https://github.com/tigase/tigase-\"\n\"server/tree/master/src/main/database>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:16\nmsgid \"\"\n\"`tigase-pubsub <https://github.com/tigase/tigase-\"\n\"pubsub/tree/master/src/main/database>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:18\nmsgid \"\"\n\"`tigase-muc <https://github.com/tigase/tigase-\"\n\"muc/tree/master/src/main/database>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:20\nmsgid \"\"\n\"`tigase-message-archiving <https://github.com/tigase/tigase-message-\"\n\"archiving/tree/master/src/main/database>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:22\nmsgid \"\"\n\"`tigase-socks5 <https://github.com/tigase/tigase-\"\n\"socks5/tree/master/src/main/database>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:24\nmsgid \"The absolute minimum of stored procedures you have to implement is:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:26\nmsgid \"\"\n\"``TigUserLoginPlainPw`` - to perform user authentication. The procedure \"\n\"is always called when the user tries to login to the XMPP server. This is\"\n\" the only procedure which must be implemented and actually must work.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:28\nmsgid \"\"\n\"``TigUserLogout`` - to perform user logout. The procedure is always \"\n\"called when the user logouts or disconnects from the server. This \"\n\"procedure must be implemented but it can be empty and can do nothing. It \"\n\"just needs to exist because Tigase expect it to exist and attempts to \"\n\"call it.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:30\nmsgid \"\"\n\"With these 2 above stored procedures you can only perform user \"\n\"login/logouts on the external database. You can’t register a user \"\n\"account, change user password or remove the user. In many cases this is \"\n\"fine as all the user management is handled by the external system.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:32\nmsgid \"\"\n\"If you however want to allow for account management via XMPP you have to \"\n\"implement also following procedures:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:34\nmsgid \"``TigAddUserPlainPw`` - to add a new user account\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:36\nmsgid \"``TigRemoveUser`` - to remove existing user account\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:38\nmsgid \"\"\n\"``TigUpdatePasswordPlainPw`` - to change a user password for existing \"\n\"account\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:4\nmsgid \"Tigase Custom Auth Connector\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:6\nmsgid \"\"\n\"The Tigase Custom Auth connector with shortcut name: **tigase-custom** is\"\n\" implemented in the class: `tigase.db.jdbc.TigaseCustomAuth \"\n\"<https://github.com/tigase/tigase-\"\n\"server/tree/master/src/main/java/tigase/db/jdbc/TigaseCustomAuth.java>`__.\"\n\" It allows you to connect to any external database to perform user \"\n\"authentication and use a custom queries for all actions.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:8\nmsgid \"\"\n\"You can find more details how to setup a custom connector in the Custom \"\n\"Authentication Connectors guide.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:10\nmsgid \"The basic configuration is very simple:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:21\nmsgid \"That’s it.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:23\nmsgid \"\"\n\"The connector loads correctly and starts working using predefined, \"\n\"default list of queries. In most cases you also might want to define your\"\n\" own queries in the configuration file. The shortest possible description\"\n\" is the following example of the content from the ``config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:25\nmsgid \"\"\n\"This query is used to check connection to the database, whether it is \"\n\"still alive or not\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:35\nmsgid \"\"\n\"This is database initialization query, normally we do not use it, \"\n\"especially in clustered environment\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:47\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:127\nmsgid \"\"\n\"``online_status`` column does not exist and would need to be added for \"\n\"that query to work.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:49\nmsgid \"\"\n\"Below query performs user authentication on the database level. The \"\n\"Tigase server does not need to know authentication algorithm or password \"\n\"encoding type, it simply passes user id (BareJID) and password in form \"\n\"which was received from the client, to the stored procedure. If the \"\n\"authentication was successful the procedure returns user bare JID or null\"\n\" otherwise. Tigase checks whether the JID returned from the query matches\"\n\" JID passed as a parameter. If they match, the authentication is \"\n\"successful.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:62\nmsgid \"\"\n\"``TigUserLoginPlainPw`` is no longer part of a Tigase XMPP Server \"\n\"database schema and would need to be created.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:64\nmsgid \"\"\n\"Below query returns number of user accounts in the database, this is \"\n\"mainly used for the server metrics and monitoring components.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:74\nmsgid \"The Below query is used to add a new user account to the database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:84\nmsgid \"\"\n\"Below query is used to remove existing account with all user’s data from \"\n\"the database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:94\nmsgid \"\"\n\"This query is used for the user authentication if ``user-login-query`` is\"\n\" not defined, that is if there is no database level user authentication \"\n\"algorithm available. In such a case the Tigase server loads user’s \"\n\"password from the database and compares it with data received from the \"\n\"client.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:104\nmsgid \"\"\n\"Below query is used for user password update in case user decides to \"\n\"change his password.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:114\nmsgid \"\"\n\"This query is called on user logout event. Usually we use a stored \"\n\"procedure which records user logout time and marks user as offline in the\"\n\" database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:129\nmsgid \"\"\n\"This configuration specifies what non-sasl authentication mechanisms to \"\n\"expose to the client\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:139\nmsgid \"\"\n\"This setting to specify what sasl authentication mechanisms expose to the\"\n\" client\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:149\nmsgid \"\"\n\"Queries are defined in the configuration file and they can be either \"\n\"plain SQL queries or stored procedures. If the query starts with \"\n\"characters: ``{ call`` then the server assumes this is a stored procedure\"\n\" call, otherwise it is executed as a plain SQL query. Each configuration \"\n\"value is stripped from white characters on both ends before processing.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:151\nmsgid \"\"\n\"Please don’t use semicolon ``;`` at the end of the query as many JDBC \"\n\"drivers get confused and the query may not work.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:153\nmsgid \"\"\n\"Some queries can take arguments. Arguments are marked by question marks \"\n\"``?`` in the query. Refer to the configuration parameters description for\"\n\" more details about what parameters are expected in each query.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:155\nmsgid \"\"\n\"This example shows how to use a stored procedure to add a user as a query\"\n\" with 2 required parameters (username, and password).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:165\nmsgid \"The same query with plain SQL parameters instead:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:171\nmsgid \"\"\n\"The order of the query arguments is important and must be exactly as \"\n\"described in specification for each parameter.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:174\nmsgid \"Query Name\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:174\nmsgid \"Description\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:174\nmsgid \"Arguments\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:174\nmsgid \"Example Query\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:176\nmsgid \"``conn-valid-query``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:176\nmsgid \"Query executed periodically to ensure active connection with the database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:176\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:178\nmsgid \"Takes no arguments.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:176\nmsgid \"``select 1``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:178\nmsgid \"``init-db-query``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:178\nmsgid \"Database initialization query which is run after the server is started.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:178\nmsgid \"``update tig_users set online_status = 0``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:180\nmsgid \"``add-user-query``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:180\nmsgid \"Query adding a new user to the database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:180\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:188\nmsgid \"Takes 2 arguments: ``(user_id (JID), password)``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:180\nmsgid \"``insert into tig_users (user_id, user_pw) values (?, ?)``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:182\nmsgid \"``del-user-query``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:182\nmsgid \"Removes a user from the database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:182\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:184\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:190\nmsgid \"Takes 1 argument: ``(user_id (JID))``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:182\nmsgid \"``delete from tig_users where user_id = ?``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:184\nmsgid \"``get-password-query``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:184\nmsgid \"Retrieves user password from the database for given user_id (JID).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:184\nmsgid \"``select user_pw from tig_users where user_id = ?``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:186\nmsgid \"``update-password-query``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:186\nmsgid \"Updates (changes) password for a given user_id (JID).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:186\nmsgid \"Takes 2 arguments: ``(password, user_id (JID))``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:186\nmsgid \"``update tig_users set user_pw = ? where user_id = ?``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:188\nmsgid \"``user-login-query``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:188\nmsgid \"\"\n\"Performs user login. Normally used when there is a special SP used for \"\n\"this purpose. This is an alternative way to a method requiring retrieving\"\n\" user password. Therefore at least one of those queries must be defined: \"\n\"``user-login-query`` or ``get-password-query``. If both queries are \"\n\"defined then ``user-login-query`` is used. Normally this method should be\"\n\" only used with plain text password authentication or sasl-plain. Tigase \"\n\"expects a result set with user_id to be returned from the query if login \"\n\"is successful and empty results set if the login is unsuccessful.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:188\nmsgid \"``select user_id from tig_users where (user_id = ?) AND (user_pw = ?)``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:190\nmsgid \"``user-logout-query``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:190\nmsgid \"\"\n\"This query is called when user logs out or disconnects. It can record \"\n\"that event in the database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:190\nmsgid \"\"\n\"``update tig_users, set online_status = online_status - 1 where user_id =\"\n\" ?``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:192\nmsgid \"``non-sasl-mechs``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:192\nmsgid \"\"\n\"Comma separated list of NON-SASL authentication mechanisms. Possible \"\n\"mechanisms are: ``password`` and ``digest``. The digest mechanism can \"\n\"work only with ``get-password-query`` active and only when password are \"\n\"stored in plain text format in the database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:194\nmsgid \"``sasl-mechs``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:194\nmsgid \"\"\n\"Comma separated list of SASL authentication mechanisms. Possible \"\n\"mechanisms are all mechanisms supported by Java implementation. The most \"\n\"common are: ``PLAIN``, ``DIGEST-MD5``, ``CRAM-MD5``. \\\"Non-PLAIN\\\" \"\n\"mechanisms will work only with the ``get-password-query`` active and only\"\n\" when passwords are stored in plain text format in the database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Drupal_Auth.inc:2\nmsgid \"Drupal Authentication\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Drupal_Auth.inc:4\nmsgid \"\"\n\"Currently, we can only check authentication against a **Drupal** database\"\n\" at the moment. Full **Drupal** authentication is not implemented as of \"\n\"yet.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Drupal_Auth.inc:6\nmsgid \"\"\n\"As **Drupal** keeps encrypted passwords in database the only possible \"\n\"authorization protocols are those based on PLAIN passwords.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Drupal_Auth.inc:8\nmsgid \"\"\n\"To protect your passwords **Tigase** server must be used with SSL or TLS \"\n\"encryption.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Drupal_Auth.inc:10\nmsgid \"\"\n\"Implementation of a **Drupal** database based authorization is located in\"\n\" ``tigase.db.jdbc.DrupalAuth`` class. Although this class is capable of \"\n\"adding new users to the repository I recommend to switch in-band \"\n\"registration off due to the caching problems in **Drupal.** Changes in \"\n\"database are not synchronized with **Drupal** yet. Functionality for \"\n\"adding new users is implemented only to ease user accounts migration from\"\n\" different repository types from earlier **Tigase** server installations.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Drupal_Auth.inc:12\nmsgid \"\"\n\"The purpose of that implementation was to allow all accounts \"\n\"administration tasks from **Drupal** like: account creation, all accounts\"\n\" settings, like e-mail, full name, password changes and so on.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Drupal_Auth.inc:14\nmsgid \"\"\n\"**Tigase** server uses following fields from **Drupal** database: name \"\n\"(user account name), pass (user account password), status (status of the \"\n\"account). Server picks up all changes instantly. If user status is not 1 \"\n\"then server won’t allow user to login trough XMPP even if user provides \"\n\"valid password.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Drupal_Auth.inc:16\nmsgid \"\"\n\"There is no *Roster* management in **Drupal** yet. So Roster management \"\n\"have to be done from the XMPP client.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/LDAP_Auth.inc:2\nmsgid \"LDAP Authentication Connector\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/LDAP_Auth.inc:4\nmsgid \"\"\n\"Tigase XMPP Server offers support for authenticating users against an \"\n\"LDAP server in **Bind** **Authentication** mode.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/LDAP_Auth.inc:6\nmsgid \"\"\n\"Configuration for the LDAP support is really simple you just have to add \"\n\"a few lines to your ``config.tdsl`` file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/LDAP_Auth.inc:18\nmsgid \"\"\n\"Please note the ``USER_ID`` element, this is a special element of the \"\n\"configuration which is used to authenticate particular user. Tigase LDAP \"\n\"connector replaces it with appropriate data during authentication. You \"\n\"can control what Tigase should put into this part. In your configuration \"\n\"you must replace this string with one of the following:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/LDAP_Auth.inc:20\nmsgid \"``%1$s`` - use user name only for authentication (JabberID’s localpart)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/LDAP_Auth.inc:22\nmsgid \"\"\n\"``%2$s`` - use domain name only for authentication (JabberID’s domain \"\n\"part)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/LDAP_Auth.inc:24\nmsgid \"``%3$s`` - use the whole Jabber ID (JID) for authentication\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/LDAP_Auth.inc:28\nmsgid \"\"\n\"Please make sure that you included ``autoCreateUser=true`` in your main \"\n\"data source (UserRepository and **not** above AuthRepository) as outlined\"\n\" in `??? <#autoCreateUser>`__ - otherwise you may run into problems with \"\n\"data access.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/SASL_EXTERNAL.inc:2\nmsgid \"Configuration of SASL EXTERNAL\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/SASL_EXTERNAL.inc:4\nmsgid \"\"\n\"In order to enable SASL External set \\\"Client Certificate CA\\\" (``client-\"\n\"trust-extension-ca-cert-path``) to the path containing Certification \"\n\"Authority (CA) certificate in the VHost (domain) configuration, for \"\n\"example ``/path/to/cacert.pem``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/SASL_EXTERNAL.inc:6\nmsgid \"\"\n\"File ``cacert.pem`` contains Certificate Authority certificate which is \"\n\"used to sign clients certificate.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/SASL_EXTERNAL.inc:8\nmsgid \"\"\n\"Client certificate must include user’s Jabber ID as ``XmppAddr`` in \"\n\"``subjectAltName``:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/SASL_EXTERNAL.inc:10\nmsgid \"\"\n\"As specified in RFC 3920 and updated in RFC 6120, during the stream \"\n\"negotiation process an XMPP client can present a certificate (a “client \"\n\"certificate”). If a JabberID is included in a client certificate, it is \"\n\"encapsulated as an id-on-xmppAddr Object Identifier (“xmppAddr”), i.e., a\"\n\" subjectAltName entry of type otherName with an ASN.1 Object Identifier \"\n\"of “id-on-xmppAddr” as specified in Section 13.7.1.4 of RFC 6120, \"\n\"`XEP-0178 <http://xmpp.org/extensions/xep-0178.html#c2s>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/SASL_EXTERNAL.inc:12\nmsgid \"\"\n\"It is possible to make client certificate **required** using same VHost \"\n\"configuration and enabling option ``Client Certificate Required`` \"\n\"(``client-trust-extension-cert-required``).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/SASL_EXTERNAL.inc:14\nmsgid \"\"\n\"If this option will be enabled, then client **must provide** certificate.\"\n\" This certificate will be verified against ``clientCertCA``. If client \"\n\"does not provide certificate or certificate will be invalid, **TLS \"\n\"handshake will be interrupted and client will be disconnected**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/SASL_EXTERNAL.inc:16\nmsgid \"\"\n\"Using this options does not force client to use SASL EXTERNAL. Client \"\n\"still may authenticate with other SASL mechanisms.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:2\nmsgid \"SASL Mechanisms\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:4\nmsgid \"\"\n\"XMPP protocol supports many authentication methods, but most of them are \"\n\"used as `SASL <https://tools.ietf.org/html/rfc4422>`__ mechanisms. Tigase\"\n\" XMPP Server provides many SASL-based authentication mechanisms such as:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:6\nmsgid \"PLAIN *(enabled)*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:8\nmsgid \"ANONYMOUS\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:10\nmsgid \"EXTERNAL\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:12\nmsgid \"SCRAM-SHA-1 *(enabled)*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:14\nmsgid \"SCRAM-SHA-256 *(enabled)*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:16\nmsgid \"SCRAM-SHA-512\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:18\nmsgid \"\"\n\"Most of them are enabled by default on default Tigase XMPP Server \"\n\"installation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:21\nmsgid \"Enabling and disabling SASL mechanisms (credentials encoder/decoder)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:23\nmsgid \"\"\n\"If you want to enable or disable one of password-based authentication \"\n\"mechanism such as ``SCRAM-SHA-1``, ``SCRAM-SHA-256`` or ``SCRAM-SHA-512``\"\n\" you can do that by enabling or disabling encoders and decoders used on \"\n\"your installation. By enabling encoders/decoders you are deciding in what\"\n\" form the password is stored in the database. Those changes may (and in \"\n\"most cases will) impact which SASL mechanisms may be allowed to use on \"\n\"your installation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:27\nmsgid \"\"\n\"In most cases you should enable or disable both (credentials encoder and \"\n\"decoder) of the same type at the same time. The only exception of this \"\n\"rule is when you are changing those on already working installation. In \"\n\"this case you should only enable encoder of the type which you want to \"\n\"enable and request users to change their passwords. Then, after users \"\n\"will change their passwords, you should reconfigure server to enable \"\n\"decoder of the particular type. *(in other case user may loose a way to \"\n\"log in to your installation as system will reject their credentials as it\"\n\" may not have matching credentials for particular SASL mechanism)*.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:30\nmsgid \"**Enabling SCRAM-SHA-512 encoder**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:43\nmsgid \"**Disabling SCRAM-SHA-1 decoder**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:56\nmsgid \"\"\n\"It is strongly recommended not to disable encoders if you have enabled \"\n\"decoder of the same type as it may lead to the authentication issues, if \"\n\"client tries to use a mechanism which that is not available.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:2\nmsgid \"Application passwords\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:4\nmsgid \"\"\n\"In recent versions of Tigase XMPP Server it is possible to create and use\"\n\" multiple username and password pairs to authorize connection to the \"\n\"single XMPP account.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:6\nmsgid \"\"\n\"With that in place it is now possible to have multiple password for a \"\n\"multiple clients accessing the same account what can be used to increase \"\n\"security of the account as even if one password compromised you can still\"\n\" log in and block lost or compromised device.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:9\nmsgid \"Adding application password\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:11\nmsgid \"\"\n\"To add new username-password pair you need to execute ``Add user \"\n\"credentials`` ad-hoc command (command node ``auth-credentials-add`` at \"\n\"``sess-man``) while logged in the XMPP account for which you want to add \"\n\"a new application password.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:13\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:41\nmsgid \"\"\n\"During execution for a command you will be provided with a form to fill \"\n\"in with following fields:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:15\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:43\nmsgid \"The Jabber ID for the account (``jid``) - bare JID of your account\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:17\nmsgid \"\"\n\"Credential ID (``credentialId``) - username for the new application \"\n\"password\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:19\nmsgid \"Password (``password``) - a new password\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:21\nmsgid \"After submitting this form a new credential will be added.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:24\nmsgid \"Login in with application password\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:26\nmsgid \"\"\n\"To log in with new password the XMPP client can use any SASL mechanism \"\n\"but it needs to provide (in SASL message):\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:28\nmsgid \"``authzid`` - account JID\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:30\nmsgid \"``authcid`` - username for application password\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:32\nmsgid \"``passwd`` - application password\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:34\nmsgid \"\"\n\"With proper values, you application will be able to log in using \"\n\"application password.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:37\nmsgid \"Removing application password\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:39\nmsgid \"\"\n\"If your device is compromised or lost and you want to remove application \"\n\"password, you need to use a different device and log in on your XMPP \"\n\"account. Then you need to execute ``Delete user credentials`` ad-hoc \"\n\"command (command node ``auth-credentials-delete`` at ``sess-man``).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:45\nmsgid \"\"\n\"Credential ID (``credentialId``) - username for the application password \"\n\"which you want to remove\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:47\nmsgid \"After submitting this form a credential will be removed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:2\nmsgid \"Packet Filtering\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:4\nmsgid \"\"\n\"Tigase offers different ways to filter XMPP packets flying through the \"\n\"server. The most common use for packet filtering is to restrict users \"\n\"from sending or receiving packets based on the sender or received \"\n\"address.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:6\nmsgid \"\"\n\"There are also different possible scenarios: time based filtering, \"\n\"content filtering, volume filtering and so on.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:8\nmsgid \"All pages in this section describe different filtering strategies.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:11\nmsgid \"Domain Based Packet Filtering\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:13\nmsgid \"\"\n\"Domain based packet filtering is a simple filter allowing to restrict \"\n\"user communication based on the source/destination domain name. This is \"\n\"especially useful if we want to limit user communication within a single \"\n\"- own domain only or a list of domains.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:15\nmsgid \"\"\n\"A company might not wish to allow employers to chat during work hours \"\n\"with anybody in the world. A company may also have a few different \"\n\"domains used by different branches or departments. An administrator may \"\n\"restrict communication to a list of domains.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:18\nmsgid \"Introduction\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:20\nmsgid \"\"\n\"The restriction is on a per-user basis. So the administrator can set a \"\n\"different filtering rules for each user. There is also a per-domain \"\n\"configuration and global-installation setting (applied from most general \"\n\"to most specific, i.e. from installation to user).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:22\nmsgid \"\"\n\"Regular users can not change the settings. So this is not like a privacy \"\n\"list where the user control the filter. Domain filter can not be changed \"\n\"or controlled by the user. The system administrator can change the \"\n\"settings based on the company policy.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:24\nmsgid \"There are predefined rules for packet filtering:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:26\nmsgid \"``ALL`` - user can send and receive packets from anybody.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:28\nmsgid \"\"\n\"``LOCAL`` - user can send and receive packets within the server \"\n\"installation only and all it’s virtual domains.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:30\nmsgid \"``OWN`` - user can send and receive packets within his own domains only\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:32\nmsgid \"\"\n\"``BLOCK`` - user can’t communicate with anyone. This could be used as a \"\n\"means to temporarily disable account or domain.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:34\nmsgid \"\"\n\"``LIST`` - user can send and receive packets within listed domains only \"\n\"(i.e. *whitelist*).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:36\nmsgid \"\"\n\"``BLACKLIST`` - user can communicate with everybody (like ``ALL``), \"\n\"except contacts on listed domains.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:38\nmsgid \"``CUSTOM`` - user can communicate only within custom created rules set.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:40\nmsgid \"\"\n\"Whitelist (``LIST``) and blacklist (``BLACKLIST``) settings are mutually \"\n\"exclusive, i.e. at any given point of time only one of them can be used.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:42\nmsgid \"\"\n\"Those rules applicable to particular users are stored in the user \"\n\"repository and are loaded for each user session. If there are no rules \"\n\"stored for a particular user server tries to apply rules for a VHost of \"\n\"particular user, and if there is no VHost filtering policy server uses \"\n\"global server configuration. If there is no filtering policy altogether \"\n\"server applies defaults based on following criteria:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:44\nmsgid \"If this is **Anonymous** user then ``LOCAL`` rule is applied\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:46\nmsgid \"For all **other** users ``ALL`` rule is applied.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:51\nmsgid \"\"\n\"Filtering is performed by the domain filter plugin which must be loaded \"\n\"at startup time. It is loaded by default if the plugins list is not set \"\n\"in the configuration file. However if you have a list of loaded plugins \"\n\"in the configuration file make sure ``domain-filter`` is on the list.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:53\nmsgid \"There is no other configuration required for the plugin to work.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:56\nmsgid \"Administration, Rules Management\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:58\nmsgid \"\"\n\"Although controlling domain filtering rules is possible for each user \"\n\"separately, it is not practical for large installations. In most cases \"\n\"users are stored in the database and a third-party system keeps all the \"\n\"user information.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:60\nmsgid \"\"\n\"To change the rule for a single user you can use loadable administration \"\n\"scripts feature and load `UserDomainFilter.groovy \"\n\"<https://github.com/tigase/tigase-\"\n\"server/tree/master/src/main/groovy/tigase/admin/UserDomainFilter.groovy>`__\"\n\" script. It enables modifying rules for a given user JID.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:63\nmsgid \"Implementation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:65\nmsgid \"\"\n\"If you have a third party system which keeps and manages all user \"\n\"information than you probably have your own UserRepository implementation\"\n\" which allows the Tigase server to access user data. Filtering rules are \"\n\"loaded from user repository using following command:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:72\nmsgid \"\"\n\"Where ``user_id`` is user Jabber ID without resource part, \"\n\"``DomainFilter.ALLOWED_DOMAINS_KEY`` is a property key: ``allowed-\"\n\"domains``. The user repository MUST return one of following only:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:74\nmsgid \"``ALL`` - if the user is allowed to communicate with anybody\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:76\nmsgid \"\"\n\"``LOCAL`` - if the user is allowed to communicate with users on the same \"\n\"server installation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:78\nmsgid \"\"\n\"``OWN`` - if the user is allowed to communicate with users within his own\"\n\" domain only.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:80\nmsgid \"\"\n\"``LIST`` - list of domains within which the user is allowed to \"\n\"communicate with other users. No wild-cards are supported. User’s own \"\n\"domain should be included too.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:82\nmsgid \"\"\n\"``BLACKLIST`` - list of domains within which the user is NOT allowed to \"\n\"communicate with other users. No wild-cards are supported. User’s own \"\n\"domain should NOT be included.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:84\nmsgid \"\"\n\"``CUSTOM`` - list of rules defining custom communication permissions \"\n\"(server processes stanza according to first matched rule, similar to \"\n\"XEP-0016) in the following format:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:106\nmsgid \"``null`` - a java null if there are no settings for the user.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:108\nmsgid \"\"\n\"In case of ``LIST`` and ``BLACKLIST`` filtering options, it’s essential \"\n\"to provide list of domains for the whitelisting/blacklisting. \"\n\"``DomainFilter.ALLOWED_DOMAINS_LIST_KEY`` is a property key: \\\"allowed-\"\n\"domains-list\\\". The user repository MUST return semicolon separated list \"\n\"of domains: ``domain1.com;domain2.com,domain3.org``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:110\nmsgid \"\"\n\"The filtering is performed by the `tigase.xmpp.impl.DomainFilter \"\n\"<https://github.com/tigase/tigase-\"\n\"server/tree/master/src/main/java/tigase/xmpp/impl/DomainFilter.java>`__ \"\n\"plugin. Please refer to source code for more implementation details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:2\nmsgid \"Access Control Lists in Tigase\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:4\nmsgid \"\"\n\"Tigase offers support for **Access Control List (ACL)** to allow for fine\"\n\" grained access to administration commands on the server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:6\nmsgid \"\"\n\"By default, all administration commands are only accessible (visible \"\n\"through service discovery and can be executed) by the service \"\n\"administrators. Service administrators are existing accounts with JIDs \"\n\"(**BareJIDs**) listed in the ``config.tdsl`` file under ``admins = []`` \"\n\"(please see :ref:`admins<admins>` for details).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:8\nmsgid \"\"\n\"Additionally, other XMPP users and entities can be assigned permissions \"\n\"to execute a command or commands using Tigase’s ACL capabilities.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:10\nmsgid \"\"\n\"The following is a list of possible ACL modifiers for administrator \"\n\"command accessibility:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:12\nmsgid \"\"\n\"``ALL`` - Everybody can execute the command, even users from different \"\n\"federated servers.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:14\nmsgid \"\"\n\"``ADMIN`` - Local server administrators can execute the command, this is \"\n\"a default setting if no ACL is set for a command.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:16\nmsgid \"\"\n\"``LOCAL`` - All users with accounts on the local server can execute the \"\n\"command. Users from other, federated servers will not be able to execute \"\n\"the command.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:18\nmsgid \"``NONE`` - No one will be allowed to execute this command\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:20\nmsgid \"\"\n\"``DOMAIN_OWNER`` - Only user which is owner of the domain which items are\"\n\" being manipulated is allowed to execute the comment. If script is not \"\n\"checking permissions for the manipulated item, this value will behave in \"\n\"the same way as ``LOCAL``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:22\nmsgid \"\"\n\"``DOMAIN_ADMIN`` - Only user which is one of the domain administrators \"\n\"will be able to execute the command manipulating items related to the \"\n\"domain. If script is not checking permissions for the manipulated item, \"\n\"this value will behave in the same way as ``LOCAL``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:24\nmsgid \"\"\n\"``example.com`` - Only users with accounts on the selected domain will be\"\n\" able to execute the command. It may be useful to setup a domain \"\n\"specifically for admin accounts, and automatically all users within that \"\n\"domain would be able to run the command.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:26\nmsgid \"\"\n\"``user@example.com`` - Comma separated list of JIDs of users who can \"\n\"execute the command.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:28\nmsgid \"\"\n\"In any case, regardless of ACL settings, any command can be executed and \"\n\"accessed by the designated service wide administrators, that is accounts \"\n\"listed as admins in the ``config.tdsl`` file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:30\nmsgid \"\"\n\"Multiple ACL modifiers can be combined and applied for any command. This \"\n\"may not always makes sense. For example ALL supersedes all other \"\n\"settings, so it does not make sense to combine it with any other \"\n\"modifier. However, most others can be combined with JID to broaden access\"\n\" to specific accounts.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:32\nmsgid \"\"\n\"On Tigase server the Access Control List is checked for the first \"\n\"matching modifier. Therefore if you combine ALL with any other modifier, \"\n\"anybody from a local or remote service will always be able to execute the\"\n\" command, no matter what other modifiers are added.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:34\nmsgid \"\"\n\"Please note, the ACL lists work on the command framework level. Access is\"\n\" verified before the command is actually executed. There might be \"\n\"additional access restrictions within a command itself. In many cases, \"\n\"even if all local users are permitted to execute a command (LOCAL \"\n\"modifier), some commands allow only to be executed by a domain owner or a\"\n\" domain administrator (and of course by the service-wide administrators \"\n\"as well). All the commands related to a user management such as adding a \"\n\"new user, removing a user, password changes, etc… belong to this \"\n\"category. When conducting domain (vhost) management, \"\n\"creation/registration of a new domain can be done by any local user (if \"\n\"LOCAL ACL modifier is set) but then all subsequent domain management \"\n\"tasks such as removing the vhost, updating its configuration, setting SSL\"\n\" certificate can be done by the domain owner or administrator only.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:36\nmsgid \"\"\n\"The ACL list is set for a specific Tigase component and a specific \"\n\"command. Therefore the configuration property must specify all the \"\n\"details. So the general format for configuring ACL for a command is this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:46\nmsgid \"The breakdown is as such:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:48\nmsgid \"\"\n\"``comp-id`` is the Tigase server component ID such as: sess-man, vhost-\"\n\"man, c2s, etc..\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:50\nmsgid \"\"\n\"``commands`` is a static text which indicates that the property is for \"\n\"component’s command settings.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:52\nmsgid \"\"\n\"``command-id`` is a command ID for which we set the ACL such as query-\"\n\"dns, http://jabber.org/protocol/admin#add-user, user-roster-management, \"\n\"etc…\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:54\nmsgid \"Here are a few examples:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:56\nmsgid \"*Allowing local users to create and manage their own domains*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:69\nmsgid \"\"\n\"In fact all the commands except item-add can be executed by the domain \"\n\"owner or administrator.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:71\nmsgid \"*Allowing local users to execute user management commands:*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:87\nmsgid \"\"\n\"As in the previous example, the commands will by executed only by local \"\n\"users who are the specific domain administrators.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:89\nmsgid \"\"\n\"*Allowing users from a specific domain to execute query-dns command and \"\n\"some other users for given JIDs from other domains:*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:99\nmsgid \"\"\n\"To be able to set a correct ACL property you need to know component names\"\n\" and command IDs. Component IDs can be found in the service discovery \"\n\"information on running server or in the server logs during startup. A \"\n\"command ID can be found in the command script source code. Each script \"\n\"contains a list of metadata at the very beginning of it’s code. One of \"\n\"them is ``AS:CommandId`` which is what you have to use for the ACL \"\n\"setting.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:2\nmsgid \"TLS/SSL encryption features configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:4\nmsgid \"\"\n\"Tigase allows adjusting the most important parameters used when \"\n\"establishing TLS connections - set of protocols and ciphers that will be \"\n\"used during negotiation of the connection. The single most important is \"\n\"``hardened-mode`` - it’s the most general configuration and offers three-\"\n\"step adjustment of the settings - please see :ref:`hardened-\"\n\"mode<hardenedMode>` for details. ``hardened-mode`` can be configured both\"\n\" via TDSL configuration file (either on ``root`` level or for \"\n\"``sslContextContainer`` for particular connection managers) or on VHost \"\n\"level.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:6\nmsgid \"\"\n\"If you want to disable certain protocols or ciphers you can use two \"\n\"options: ``tls-disabled-protocols`` and ``tls-disabled-ciphers`` \"\n\"respectively. They allow, as name suggests, disabling certain items from \"\n\"default sets. They both takes an array of strings, which ten be removed \"\n\"from the lists.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:8\nmsgid \"\"\n\"Let’s say you’d like to remove support for ``SSL``, ``SSLv2`` and \"\n\"``SSLv3`` protocols. You should simply use following configuraiton: \"\n\"``'tls-disabled-protocols' = ['SSL', 'SSLv2', 'SSLv3']``. Complete list \"\n\"of protocols depends on particular Java version that you use - please \"\n\"refer to the documentation for details. For example for the default \"\n\"Java11 list you can check `SSLContext Algorithms \"\n\"<https://docs.oracle.com/en/java/javase/11/docs/specs/security/standard-\"\n\"names.html#sslcontext-algorithms>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:10\nmsgid \"\"\n\"``tls-disabled-ciphers`` follows same format and uses names defined in \"\n\"`JSSE Cipher Suite Names \"\n\"<https://docs.oracle.com/en/java/javase/11/docs/specs/security/standard-\"\n\"names.html#jsse-cipher-suite-names>`__. It’s also possible to use regular\"\n\" expressions to quickly eliminate groups of ciphers.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:12\nmsgid \"\"\n\"If you want to enable only specific protocols or ciphers irrespective of \"\n\"``hardened-mode`` or above disabling options you can use ``tls-enabled-\"\n\"protocols`` and ``tls-enabled-ciphers`` - those two options take arrays \"\n\"as well and they will configure Tigase to use only those protocols or \"\n\"ciphers that are provided (without support for regular expressions). \"\n\"Therefore if you configure Tigase with ``'tls-enabled-protocols' = [ \"\n\"'TLSv1.2' ]`` then **only** ``TLSv1.2`` will be supported by Tigase.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:14\nmsgid \"\"\n\"The last option that you may be interested in adjusting is ``ephemeral-\"\n\"key-size`` - it follows Java’s configuration capabilities outlined in \"\n\"`Customizing Size of Ephemeral Diffie-Hellman Keys \"\n\"<https://docs.oracle.com/en/java/javase/11/security/java-secure-socket-\"\n\"extension-jsse-reference-guide.html#GUID-D9B216E8-3EFC-4882-B76E-\"\n\"17A87D8F2F9D>`__. Tigase defaults Diffie-Hellman keys of 4096 bits.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:18\nmsgid \"\"\n\"We try to provide the best default set of options therefore **it’s \"\n\"recommendable to use defaults provided by Tigase**. If you want to make \"\n\"your extremely secure (considering possible connectivity issues with \"\n\"installations that may be less secure) then you should only adjust \"\n\"``hardened-mode`` setting (and switch it to ``strict``).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:21\nmsgid \"Testing hosts TLS capabilities\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:23\nmsgid \"\"\n\"If you run into issues with TLS connectivity it’s helpful to compare if \"\n\"both installations support same set of protocols and ciphers. One of the \"\n\"most versatile and helpful tools is `Mozilla’s CipherScan \"\n\"<https://github.com/mozilla/cipherscan>`__. For example for our \"\n\"installation ``tigase.im`` result would look like this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:56\nmsgid \"TLS 1.3 compatibility\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:58\nmsgid \"\"\n\"Due to compatibility issues, TLS 1.3 is currently (version 8.1.x) \"\n\"disabled by default. It can be enabled by setting property ``tls-disable-\"\n\"tls13`` of ``sslContextContainer`` bean to ``false``:\"\nmsgstr \"\"\n\n#~ msgid \"\"\n#~ \"Tigase allows adjusting the most \"\n#~ \"important parameters used when establishing\"\n#~ \" TLS connections - set of protocols\"\n#~ \" and ciphers that will be used \"\n#~ \"during negotiation of the connection. \"\n#~ \"The single most important is \"\n#~ \"``hardened-mode`` - it’s the most \"\n#~ \"general configuration and offers three-\"\n#~ \"step adjustment of the settings - \"\n#~ \"please see `??? <#hardenedMode>`__ for \"\n#~ \"details. ``hardened-mode`` can be \"\n#~ \"configured both via TDSL configuration \"\n#~ \"file (either on ``root`` level or \"\n#~ \"for ``sslContextContainer`` for particular \"\n#~ \"connection managers) or on VHost level.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"Working mode (see `Working modes \"\n#~ \"<#bruteForcePrevention_WorkingModes>`__)\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"Tigase requires full certificate chain \"\n#~ \"in PEM file (described above)! Different\"\n#~ \" applications may require pem file \"\n#~ \"with certificates and private key in \"\n#~ \"different order. So the same file \"\n#~ \"may not be necessarily used by \"\n#~ \"other services like Web server or \"\n#~ \"e-mail server. Currently, Tigase can \"\n#~ \"automatically sort certificates in PEM \"\n#~ \"file while loading it.**\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"It’s also possible to use: \\\\* \"\n#~ \"Admin ad-hoc command via XMPP \"\n#~ \"client - you should navigate to \"\n#~ \"Service Discovery of your server and \"\n#~ \"in the list of commands for \"\n#~ \"``VHost Manager`` component select ``Add \"\n#~ \"SSL Certificate`` and then follow \"\n#~ \"instructions \\\\* Admin WebUI - open \"\n#~ \"``http://<host>/admin``, navigate to ``Other`` \"\n#~ \"category and in it select ``Add \"\n#~ \"SSL Certificate`` and then follow \"\n#~ \"instructions \\\\* REST API - make a\"\n#~ \" ``POST`` request to \"\n#~ \"``http://localhost:8080/rest/adhoc/vhost-man@domain.com`` \"\n#~ \"with payload containing your certificate; \"\n#~ \"to get the required form fields \"\n#~ \"make ``GET`` request to the same \"\n#~ \"endpoint\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"LetsEncrypt - please see `Installing \"\n#~ \"LetsEncrypt Certificates in Your Linux \"\n#~ \"System<LetsEncryptCertificate>` for details\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"The filtering is performed by the \"\n#~ \"```tigase.xmpp.impl.DomainFilter`` <https://github.com/tigase\"\n#~ \"/tigase-\"\n#~ \"server/tree/master/src/main/java/tigase/xmpp/impl/DomainFilter.java>`__\"\n#~ \" plugin. Please refer to source code\"\n#~ \" for more implementation details.\"\n#~ msgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Administration/Statistics_Description.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:4\nmsgid \"Appendix I - Statistics description\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:6\nmsgid \"\"\n\"Statistics are divided between data sources, components and processors. \"\n\"You may see the same statistics collected for multiple components which \"\n\"are defined in common components section. Note that statistics are \"\n\"defined by {$component}/statistic so if you wanted Max queue size on \"\n\"pubsub, you would look for pubsub/Max queue size. Statistics will not be \"\n\"provided by components that are not enabled.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:9\nmsgid \"Data source statistics\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:11\nmsgid \"\"\n\"Data sources used to access data storages such as JDBC (databases) or \"\n\"MongoDB provide statistics related to stability of connections to data \"\n\"storage and number of open connections.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:14\n#: ../../Tigase_Administration/Statistics_Description.rst:33\n#: ../../Tigase_Administration/Statistics_Description.rst:50\n#: ../../Tigase_Administration/Statistics_Description.rst:84\n#: ../../Tigase_Administration/Statistics_Description.rst:104\n#: ../../Tigase_Administration/Statistics_Description.rst:188\n#: ../../Tigase_Administration/Statistics_Description.rst:206\n#: ../../Tigase_Administration/Statistics_Description.rst:248\n#: ../../Tigase_Administration/Statistics_Description.rst:257\n#: ../../Tigase_Administration/Statistics_Description.rst:295\n#: ../../Tigase_Administration/Statistics_Description.rst:306\n#: ../../Tigase_Administration/Statistics_Description.rst:321\n#: ../../Tigase_Administration/Statistics_Description.rst:336\n#: ../../Tigase_Administration/Statistics_Description.rst:485\n#: ../../Tigase_Administration/Statistics_Description.rst:506\n#: ../../Tigase_Administration/Statistics_Description.rst:539\n#: ../../Tigase_Administration/Statistics_Description.rst:682\nmsgid \"Statistics Name\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:14\n#: ../../Tigase_Administration/Statistics_Description.rst:33\n#: ../../Tigase_Administration/Statistics_Description.rst:50\n#: ../../Tigase_Administration/Statistics_Description.rst:84\n#: ../../Tigase_Administration/Statistics_Description.rst:104\n#: ../../Tigase_Administration/Statistics_Description.rst:188\n#: ../../Tigase_Administration/Statistics_Description.rst:206\n#: ../../Tigase_Administration/Statistics_Description.rst:248\n#: ../../Tigase_Administration/Statistics_Description.rst:257\n#: ../../Tigase_Administration/Statistics_Description.rst:295\n#: ../../Tigase_Administration/Statistics_Description.rst:306\n#: ../../Tigase_Administration/Statistics_Description.rst:321\n#: ../../Tigase_Administration/Statistics_Description.rst:336\n#: ../../Tigase_Administration/Statistics_Description.rst:485\n#: ../../Tigase_Administration/Statistics_Description.rst:506\n#: ../../Tigase_Administration/Statistics_Description.rst:539\n#: ../../Tigase_Administration/Statistics_Description.rst:682\nmsgid \"Description\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:14\n#: ../../Tigase_Administration/Statistics_Description.rst:33\n#: ../../Tigase_Administration/Statistics_Description.rst:50\n#: ../../Tigase_Administration/Statistics_Description.rst:84\n#: ../../Tigase_Administration/Statistics_Description.rst:104\n#: ../../Tigase_Administration/Statistics_Description.rst:188\n#: ../../Tigase_Administration/Statistics_Description.rst:206\n#: ../../Tigase_Administration/Statistics_Description.rst:248\n#: ../../Tigase_Administration/Statistics_Description.rst:257\n#: ../../Tigase_Administration/Statistics_Description.rst:295\n#: ../../Tigase_Administration/Statistics_Description.rst:306\n#: ../../Tigase_Administration/Statistics_Description.rst:321\n#: ../../Tigase_Administration/Statistics_Description.rst:336\n#: ../../Tigase_Administration/Statistics_Description.rst:485\n#: ../../Tigase_Administration/Statistics_Description.rst:506\n#: ../../Tigase_Administration/Statistics_Description.rst:539\n#: ../../Tigase_Administration/Statistics_Description.rst:682\nmsgid \"Statistics Level\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:14\n#: ../../Tigase_Administration/Statistics_Description.rst:33\n#: ../../Tigase_Administration/Statistics_Description.rst:50\n#: ../../Tigase_Administration/Statistics_Description.rst:84\n#: ../../Tigase_Administration/Statistics_Description.rst:104\n#: ../../Tigase_Administration/Statistics_Description.rst:188\n#: ../../Tigase_Administration/Statistics_Description.rst:206\n#: ../../Tigase_Administration/Statistics_Description.rst:248\n#: ../../Tigase_Administration/Statistics_Description.rst:257\n#: ../../Tigase_Administration/Statistics_Description.rst:295\n#: ../../Tigase_Administration/Statistics_Description.rst:306\n#: ../../Tigase_Administration/Statistics_Description.rst:321\n#: ../../Tigase_Administration/Statistics_Description.rst:336\n#: ../../Tigase_Administration/Statistics_Description.rst:485\n#: ../../Tigase_Administration/Statistics_Description.rst:506\n#: ../../Tigase_Administration/Statistics_Description.rst:539\n#: ../../Tigase_Administration/Statistics_Description.rst:682\nmsgid \"Format\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:14\n#: ../../Tigase_Administration/Statistics_Description.rst:33\n#: ../../Tigase_Administration/Statistics_Description.rst:50\n#: ../../Tigase_Administration/Statistics_Description.rst:84\n#: ../../Tigase_Administration/Statistics_Description.rst:104\n#: ../../Tigase_Administration/Statistics_Description.rst:188\n#: ../../Tigase_Administration/Statistics_Description.rst:206\n#: ../../Tigase_Administration/Statistics_Description.rst:248\n#: ../../Tigase_Administration/Statistics_Description.rst:257\n#: ../../Tigase_Administration/Statistics_Description.rst:295\n#: ../../Tigase_Administration/Statistics_Description.rst:306\n#: ../../Tigase_Administration/Statistics_Description.rst:321\n#: ../../Tigase_Administration/Statistics_Description.rst:336\n#: ../../Tigase_Administration/Statistics_Description.rst:485\n#: ../../Tigase_Administration/Statistics_Description.rst:506\n#: ../../Tigase_Administration/Statistics_Description.rst:539\n#: ../../Tigase_Administration/Statistics_Description.rst:682\nmsgid \"List of Possible Statistics\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:16\nmsgid \"Number of active data sources\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:16\nmsgid \"\"\n\"Number of defined and active data sources (i.e. connection pools). This \"\n\"is not a number of connections to data sources as it varies and is listed\"\n\" separately for every defined data source.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:16\n#: ../../Tigase_Administration/Statistics_Description.rst:18\n#: ../../Tigase_Administration/Statistics_Description.rst:20\n#: ../../Tigase_Administration/Statistics_Description.rst:22\n#: ../../Tigase_Administration/Statistics_Description.rst:24\n#: ../../Tigase_Administration/Statistics_Description.rst:35\n#: ../../Tigase_Administration/Statistics_Description.rst:37\n#: ../../Tigase_Administration/Statistics_Description.rst:41\n#: ../../Tigase_Administration/Statistics_Description.rst:52\n#: ../../Tigase_Administration/Statistics_Description.rst:54\n#: ../../Tigase_Administration/Statistics_Description.rst:58\n#: ../../Tigase_Administration/Statistics_Description.rst:86\n#: ../../Tigase_Administration/Statistics_Description.rst:88\n#: ../../Tigase_Administration/Statistics_Description.rst:90\n#: ../../Tigase_Administration/Statistics_Description.rst:138\n#: ../../Tigase_Administration/Statistics_Description.rst:140\n#: ../../Tigase_Administration/Statistics_Description.rst:153\n#: ../../Tigase_Administration/Statistics_Description.rst:224\n#: ../../Tigase_Administration/Statistics_Description.rst:226\n#: ../../Tigase_Administration/Statistics_Description.rst:230\n#: ../../Tigase_Administration/Statistics_Description.rst:250\n#: ../../Tigase_Administration/Statistics_Description.rst:261\n#: ../../Tigase_Administration/Statistics_Description.rst:265\n#: ../../Tigase_Administration/Statistics_Description.rst:267\n#: ../../Tigase_Administration/Statistics_Description.rst:269\n#: ../../Tigase_Administration/Statistics_Description.rst:273\n#: ../../Tigase_Administration/Statistics_Description.rst:277\n#: ../../Tigase_Administration/Statistics_Description.rst:279\n#: ../../Tigase_Administration/Statistics_Description.rst:287\n#: ../../Tigase_Administration/Statistics_Description.rst:374\n#: ../../Tigase_Administration/Statistics_Description.rst:487\n#: ../../Tigase_Administration/Statistics_Description.rst:489\n#: ../../Tigase_Administration/Statistics_Description.rst:692\nmsgid \"FINE\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:16\n#: ../../Tigase_Administration/Statistics_Description.rst:20\n#: ../../Tigase_Administration/Statistics_Description.rst:22\n#: ../../Tigase_Administration/Statistics_Description.rst:35\n#: ../../Tigase_Administration/Statistics_Description.rst:37\n#: ../../Tigase_Administration/Statistics_Description.rst:39\n#: ../../Tigase_Administration/Statistics_Description.rst:41\n#: ../../Tigase_Administration/Statistics_Description.rst:52\n#: ../../Tigase_Administration/Statistics_Description.rst:54\n#: ../../Tigase_Administration/Statistics_Description.rst:56\n#: ../../Tigase_Administration/Statistics_Description.rst:58\n#: ../../Tigase_Administration/Statistics_Description.rst:86\n#: ../../Tigase_Administration/Statistics_Description.rst:88\n#: ../../Tigase_Administration/Statistics_Description.rst:90\n#: ../../Tigase_Administration/Statistics_Description.rst:106\n#: ../../Tigase_Administration/Statistics_Description.rst:108\n#: ../../Tigase_Administration/Statistics_Description.rst:110\n#: ../../Tigase_Administration/Statistics_Description.rst:112\n#: ../../Tigase_Administration/Statistics_Description.rst:114\n#: ../../Tigase_Administration/Statistics_Description.rst:116\n#: ../../Tigase_Administration/Statistics_Description.rst:118\n#: ../../Tigase_Administration/Statistics_Description.rst:120\n#: ../../Tigase_Administration/Statistics_Description.rst:122\n#: ../../Tigase_Administration/Statistics_Description.rst:124\n#: ../../Tigase_Administration/Statistics_Description.rst:126\n#: ../../Tigase_Administration/Statistics_Description.rst:128\n#: ../../Tigase_Administration/Statistics_Description.rst:130\n#: ../../Tigase_Administration/Statistics_Description.rst:134\n#: ../../Tigase_Administration/Statistics_Description.rst:136\n#: ../../Tigase_Administration/Statistics_Description.rst:138\n#: ../../Tigase_Administration/Statistics_Description.rst:140\n#: ../../Tigase_Administration/Statistics_Description.rst:142\n#: ../../Tigase_Administration/Statistics_Description.rst:151\n#: ../../Tigase_Administration/Statistics_Description.rst:153\n#: ../../Tigase_Administration/Statistics_Description.rst:155\n#: ../../Tigase_Administration/Statistics_Description.rst:157\n#: ../../Tigase_Administration/Statistics_Description.rst:159\n#: ../../Tigase_Administration/Statistics_Description.rst:161\n#: ../../Tigase_Administration/Statistics_Description.rst:163\n#: ../../Tigase_Administration/Statistics_Description.rst:165\n#: ../../Tigase_Administration/Statistics_Description.rst:167\n#: ../../Tigase_Administration/Statistics_Description.rst:169\n#: ../../Tigase_Administration/Statistics_Description.rst:171\n#: ../../Tigase_Administration/Statistics_Description.rst:173\n#: ../../Tigase_Administration/Statistics_Description.rst:190\n#: ../../Tigase_Administration/Statistics_Description.rst:192\n#: ../../Tigase_Administration/Statistics_Description.rst:194\n#: ../../Tigase_Administration/Statistics_Description.rst:208\n#: ../../Tigase_Administration/Statistics_Description.rst:210\n#: ../../Tigase_Administration/Statistics_Description.rst:212\n#: ../../Tigase_Administration/Statistics_Description.rst:214\n#: ../../Tigase_Administration/Statistics_Description.rst:216\n#: ../../Tigase_Administration/Statistics_Description.rst:218\n#: ../../Tigase_Administration/Statistics_Description.rst:220\n#: ../../Tigase_Administration/Statistics_Description.rst:222\n#: ../../Tigase_Administration/Statistics_Description.rst:228\n#: ../../Tigase_Administration/Statistics_Description.rst:234\n#: ../../Tigase_Administration/Statistics_Description.rst:236\n#: ../../Tigase_Administration/Statistics_Description.rst:250\n#: ../../Tigase_Administration/Statistics_Description.rst:259\n#: ../../Tigase_Administration/Statistics_Description.rst:281\n#: ../../Tigase_Administration/Statistics_Description.rst:297\n#: ../../Tigase_Administration/Statistics_Description.rst:299\n#: ../../Tigase_Administration/Statistics_Description.rst:308\n#: ../../Tigase_Administration/Statistics_Description.rst:310\n#: ../../Tigase_Administration/Statistics_Description.rst:312\n#: ../../Tigase_Administration/Statistics_Description.rst:314\n#: ../../Tigase_Administration/Statistics_Description.rst:323\n#: ../../Tigase_Administration/Statistics_Description.rst:325\n#: ../../Tigase_Administration/Statistics_Description.rst:327\n#: ../../Tigase_Administration/Statistics_Description.rst:329\n#: ../../Tigase_Administration/Statistics_Description.rst:338\n#: ../../Tigase_Administration/Statistics_Description.rst:340\n#: ../../Tigase_Administration/Statistics_Description.rst:342\n#: ../../Tigase_Administration/Statistics_Description.rst:344\n#: ../../Tigase_Administration/Statistics_Description.rst:346\n#: ../../Tigase_Administration/Statistics_Description.rst:348\n#: ../../Tigase_Administration/Statistics_Description.rst:350\n#: ../../Tigase_Administration/Statistics_Description.rst:352\n#: ../../Tigase_Administration/Statistics_Description.rst:354\n#: ../../Tigase_Administration/Statistics_Description.rst:356\n#: ../../Tigase_Administration/Statistics_Description.rst:358\n#: ../../Tigase_Administration/Statistics_Description.rst:360\n#: ../../Tigase_Administration/Statistics_Description.rst:362\n#: ../../Tigase_Administration/Statistics_Description.rst:364\n#: ../../Tigase_Administration/Statistics_Description.rst:366\n#: ../../Tigase_Administration/Statistics_Description.rst:368\n#: ../../Tigase_Administration/Statistics_Description.rst:370\n#: ../../Tigase_Administration/Statistics_Description.rst:372\n#: ../../Tigase_Administration/Statistics_Description.rst:376\n#: ../../Tigase_Administration/Statistics_Description.rst:378\n#: ../../Tigase_Administration/Statistics_Description.rst:380\n#: ../../Tigase_Administration/Statistics_Description.rst:382\n#: ../../Tigase_Administration/Statistics_Description.rst:384\n#: ../../Tigase_Administration/Statistics_Description.rst:386\n#: ../../Tigase_Administration/Statistics_Description.rst:388\n#: ../../Tigase_Administration/Statistics_Description.rst:390\n#: ../../Tigase_Administration/Statistics_Description.rst:392\n#: ../../Tigase_Administration/Statistics_Description.rst:394\n#: ../../Tigase_Administration/Statistics_Description.rst:396\n#: ../../Tigase_Administration/Statistics_Description.rst:398\n#: ../../Tigase_Administration/Statistics_Description.rst:400\n#: ../../Tigase_Administration/Statistics_Description.rst:402\n#: ../../Tigase_Administration/Statistics_Description.rst:404\n#: ../../Tigase_Administration/Statistics_Description.rst:406\n#: ../../Tigase_Administration/Statistics_Description.rst:408\n#: ../../Tigase_Administration/Statistics_Description.rst:410\n#: ../../Tigase_Administration/Statistics_Description.rst:412\n#: ../../Tigase_Administration/Statistics_Description.rst:414\n#: ../../Tigase_Administration/Statistics_Description.rst:416\n#: ../../Tigase_Administration/Statistics_Description.rst:418\n#: ../../Tigase_Administration/Statistics_Description.rst:420\n#: ../../Tigase_Administration/Statistics_Description.rst:422\n#: ../../Tigase_Administration/Statistics_Description.rst:424\n#: ../../Tigase_Administration/Statistics_Description.rst:426\n#: ../../Tigase_Administration/Statistics_Description.rst:428\n#: ../../Tigase_Administration/Statistics_Description.rst:430\n#: ../../Tigase_Administration/Statistics_Description.rst:432\n#: ../../Tigase_Administration/Statistics_Description.rst:434\n#: ../../Tigase_Administration/Statistics_Description.rst:436\n#: ../../Tigase_Administration/Statistics_Description.rst:438\n#: ../../Tigase_Administration/Statistics_Description.rst:440\n#: ../../Tigase_Administration/Statistics_Description.rst:442\n#: ../../Tigase_Administration/Statistics_Description.rst:444\n#: ../../Tigase_Administration/Statistics_Description.rst:446\n#: ../../Tigase_Administration/Statistics_Description.rst:448\n#: ../../Tigase_Administration/Statistics_Description.rst:450\n#: ../../Tigase_Administration/Statistics_Description.rst:452\n#: ../../Tigase_Administration/Statistics_Description.rst:454\n#: ../../Tigase_Administration/Statistics_Description.rst:456\n#: ../../Tigase_Administration/Statistics_Description.rst:458\n#: ../../Tigase_Administration/Statistics_Description.rst:460\n#: ../../Tigase_Administration/Statistics_Description.rst:462\n#: ../../Tigase_Administration/Statistics_Description.rst:464\n#: ../../Tigase_Administration/Statistics_Description.rst:466\n#: ../../Tigase_Administration/Statistics_Description.rst:470\n#: ../../Tigase_Administration/Statistics_Description.rst:472\n#: ../../Tigase_Administration/Statistics_Description.rst:474\n#: ../../Tigase_Administration/Statistics_Description.rst:476\n#: ../../Tigase_Administration/Statistics_Description.rst:478\n#: ../../Tigase_Administration/Statistics_Description.rst:487\n#: ../../Tigase_Administration/Statistics_Description.rst:489\n#: ../../Tigase_Administration/Statistics_Description.rst:491\n#: ../../Tigase_Administration/Statistics_Description.rst:493\n#: ../../Tigase_Administration/Statistics_Description.rst:510\n#: ../../Tigase_Administration/Statistics_Description.rst:512\n#: ../../Tigase_Administration/Statistics_Description.rst:514\n#: ../../Tigase_Administration/Statistics_Description.rst:516\n#: ../../Tigase_Administration/Statistics_Description.rst:518\n#: ../../Tigase_Administration/Statistics_Description.rst:520\n#: ../../Tigase_Administration/Statistics_Description.rst:522\n#: ../../Tigase_Administration/Statistics_Description.rst:524\n#: ../../Tigase_Administration/Statistics_Description.rst:526\n#: ../../Tigase_Administration/Statistics_Description.rst:528\n#: ../../Tigase_Administration/Statistics_Description.rst:530\n#: ../../Tigase_Administration/Statistics_Description.rst:532\n#: ../../Tigase_Administration/Statistics_Description.rst:541\n#: ../../Tigase_Administration/Statistics_Description.rst:543\n#: ../../Tigase_Administration/Statistics_Description.rst:545\n#: ../../Tigase_Administration/Statistics_Description.rst:547\n#: ../../Tigase_Administration/Statistics_Description.rst:549\n#: ../../Tigase_Administration/Statistics_Description.rst:551\n#: ../../Tigase_Administration/Statistics_Description.rst:553\n#: ../../Tigase_Administration/Statistics_Description.rst:555\n#: ../../Tigase_Administration/Statistics_Description.rst:557\n#: ../../Tigase_Administration/Statistics_Description.rst:559\n#: ../../Tigase_Administration/Statistics_Description.rst:561\n#: ../../Tigase_Administration/Statistics_Description.rst:563\n#: ../../Tigase_Administration/Statistics_Description.rst:565\n#: ../../Tigase_Administration/Statistics_Description.rst:567\n#: ../../Tigase_Administration/Statistics_Description.rst:569\n#: ../../Tigase_Administration/Statistics_Description.rst:571\n#: ../../Tigase_Administration/Statistics_Description.rst:573\n#: ../../Tigase_Administration/Statistics_Description.rst:575\n#: ../../Tigase_Administration/Statistics_Description.rst:577\n#: ../../Tigase_Administration/Statistics_Description.rst:579\n#: ../../Tigase_Administration/Statistics_Description.rst:581\n#: ../../Tigase_Administration/Statistics_Description.rst:583\n#: ../../Tigase_Administration/Statistics_Description.rst:585\n#: ../../Tigase_Administration/Statistics_Description.rst:587\n#: ../../Tigase_Administration/Statistics_Description.rst:589\n#: ../../Tigase_Administration/Statistics_Description.rst:591\n#: ../../Tigase_Administration/Statistics_Description.rst:593\n#: ../../Tigase_Administration/Statistics_Description.rst:595\n#: ../../Tigase_Administration/Statistics_Description.rst:597\n#: ../../Tigase_Administration/Statistics_Description.rst:599\n#: ../../Tigase_Administration/Statistics_Description.rst:601\n#: ../../Tigase_Administration/Statistics_Description.rst:603\n#: ../../Tigase_Administration/Statistics_Description.rst:605\n#: ../../Tigase_Administration/Statistics_Description.rst:607\n#: ../../Tigase_Administration/Statistics_Description.rst:609\n#: ../../Tigase_Administration/Statistics_Description.rst:611\n#: ../../Tigase_Administration/Statistics_Description.rst:613\n#: ../../Tigase_Administration/Statistics_Description.rst:615\n#: ../../Tigase_Administration/Statistics_Description.rst:617\n#: ../../Tigase_Administration/Statistics_Description.rst:619\n#: ../../Tigase_Administration/Statistics_Description.rst:621\n#: ../../Tigase_Administration/Statistics_Description.rst:623\n#: ../../Tigase_Administration/Statistics_Description.rst:625\n#: ../../Tigase_Administration/Statistics_Description.rst:627\n#: ../../Tigase_Administration/Statistics_Description.rst:629\n#: ../../Tigase_Administration/Statistics_Description.rst:631\n#: ../../Tigase_Administration/Statistics_Description.rst:633\n#: ../../Tigase_Administration/Statistics_Description.rst:635\n#: ../../Tigase_Administration/Statistics_Description.rst:637\n#: ../../Tigase_Administration/Statistics_Description.rst:639\n#: ../../Tigase_Administration/Statistics_Description.rst:641\n#: ../../Tigase_Administration/Statistics_Description.rst:643\n#: ../../Tigase_Administration/Statistics_Description.rst:645\n#: ../../Tigase_Administration/Statistics_Description.rst:647\n#: ../../Tigase_Administration/Statistics_Description.rst:649\n#: ../../Tigase_Administration/Statistics_Description.rst:651\n#: ../../Tigase_Administration/Statistics_Description.rst:653\n#: ../../Tigase_Administration/Statistics_Description.rst:655\n#: ../../Tigase_Administration/Statistics_Description.rst:657\n#: ../../Tigase_Administration/Statistics_Description.rst:659\n#: ../../Tigase_Administration/Statistics_Description.rst:661\n#: ../../Tigase_Administration/Statistics_Description.rst:663\n#: ../../Tigase_Administration/Statistics_Description.rst:665\n#: ../../Tigase_Administration/Statistics_Description.rst:684\n#: ../../Tigase_Administration/Statistics_Description.rst:686\n#: ../../Tigase_Administration/Statistics_Description.rst:688\n#: ../../Tigase_Administration/Statistics_Description.rst:690\n#: ../../Tigase_Administration/Statistics_Description.rst:692\nmsgid \"Integer\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:16\nmsgid \"``dataSource/Number of data sources``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:18\nmsgid \"Number of connections for ``{dataSourceName}``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:18\nmsgid \"Number of connections for defined data source.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:18\n#: ../../Tigase_Administration/Statistics_Description.rst:24\n#: ../../Tigase_Administration/Statistics_Description.rst:265\n#: ../../Tigase_Administration/Statistics_Description.rst:267\n#: ../../Tigase_Administration/Statistics_Description.rst:271\n#: ../../Tigase_Administration/Statistics_Description.rst:275\n#: ../../Tigase_Administration/Statistics_Description.rst:277\n#: ../../Tigase_Administration/Statistics_Description.rst:283\n#: ../../Tigase_Administration/Statistics_Description.rst:285\n#: ../../Tigase_Administration/Statistics_Description.rst:287\n#: ../../Tigase_Administration/Statistics_Description.rst:508\nmsgid \"String\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:18\n#: ../../Tigase_Administration/Statistics_Description.rst:24\nmsgid \"``dataSource/{dataSourceName}/uri``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:20\nmsgid \"Number of failed reconnections for ``{dataSourceName}``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:20\nmsgid \"\"\n\"Number of reconnections that has failed since start to the defined data \"\n\"source.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:20\nmsgid \"``dataSource/{dataSourceName}/failed reconnections``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:22\nmsgid \"Number of reconnections for ``{dataSourceName}``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:22\nmsgid \"Number of reconnections for defined data source.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:22\nmsgid \"``dataSource/{dataSourceName}/reconnections``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:24\nmsgid \"URI of ``{dataSourceName}``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:24\nmsgid \"Returns URI of defined data source.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:28\nmsgid \"User repository statistics of {repo}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:30\nmsgid \"\"\n\"For every {method} declared in ``UserRepository`` we gather execution \"\n\"statistics. This statistics are collected separately for every data \"\n\"source for which user repository is defined.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:35\n#: ../../Tigase_Administration/Statistics_Description.rst:52\n#: ../../Tigase_Administration/Statistics_Description.rst:86\nmsgid \"Average processing time of {method}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:35\nmsgid \"\"\n\"Average time taken by call of {method} for this data source since \"\n\"creation of data source (most likely from server startup). It includes \"\n\"time taken by calls which thrown exception, etc.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:35\nmsgid \"``userRepository/{repo}/{method}/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:37\n#: ../../Tigase_Administration/Statistics_Description.rst:88\nmsgid \"Number of exceptions of a {method}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:37\nmsgid \"Number of exceptions the specified method has caused\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:37\nmsgid \"``userRepository/{repo}/{method}/Exceptions during execution``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:39\nmsgid \"Number of exceptions of a {method} in last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:39\nmsgid \"\"\n\"Number of exceptions the specified method has caused within the specified\"\n\" interval\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:39\n#: ../../Tigase_Administration/Statistics_Description.rst:56\n#: ../../Tigase_Administration/Statistics_Description.rst:106\n#: ../../Tigase_Administration/Statistics_Description.rst:108\n#: ../../Tigase_Administration/Statistics_Description.rst:110\n#: ../../Tigase_Administration/Statistics_Description.rst:116\n#: ../../Tigase_Administration/Statistics_Description.rst:118\n#: ../../Tigase_Administration/Statistics_Description.rst:120\n#: ../../Tigase_Administration/Statistics_Description.rst:122\n#: ../../Tigase_Administration/Statistics_Description.rst:124\n#: ../../Tigase_Administration/Statistics_Description.rst:126\n#: ../../Tigase_Administration/Statistics_Description.rst:128\n#: ../../Tigase_Administration/Statistics_Description.rst:134\n#: ../../Tigase_Administration/Statistics_Description.rst:142\n#: ../../Tigase_Administration/Statistics_Description.rst:155\n#: ../../Tigase_Administration/Statistics_Description.rst:157\n#: ../../Tigase_Administration/Statistics_Description.rst:159\n#: ../../Tigase_Administration/Statistics_Description.rst:161\n#: ../../Tigase_Administration/Statistics_Description.rst:163\n#: ../../Tigase_Administration/Statistics_Description.rst:165\n#: ../../Tigase_Administration/Statistics_Description.rst:167\n#: ../../Tigase_Administration/Statistics_Description.rst:190\n#: ../../Tigase_Administration/Statistics_Description.rst:192\n#: ../../Tigase_Administration/Statistics_Description.rst:194\n#: ../../Tigase_Administration/Statistics_Description.rst:208\n#: ../../Tigase_Administration/Statistics_Description.rst:210\n#: ../../Tigase_Administration/Statistics_Description.rst:212\n#: ../../Tigase_Administration/Statistics_Description.rst:214\n#: ../../Tigase_Administration/Statistics_Description.rst:216\n#: ../../Tigase_Administration/Statistics_Description.rst:218\n#: ../../Tigase_Administration/Statistics_Description.rst:220\n#: ../../Tigase_Administration/Statistics_Description.rst:222\n#: ../../Tigase_Administration/Statistics_Description.rst:234\n#: ../../Tigase_Administration/Statistics_Description.rst:236\n#: ../../Tigase_Administration/Statistics_Description.rst:259\n#: ../../Tigase_Administration/Statistics_Description.rst:281\n#: ../../Tigase_Administration/Statistics_Description.rst:297\n#: ../../Tigase_Administration/Statistics_Description.rst:299\n#: ../../Tigase_Administration/Statistics_Description.rst:308\n#: ../../Tigase_Administration/Statistics_Description.rst:310\n#: ../../Tigase_Administration/Statistics_Description.rst:312\n#: ../../Tigase_Administration/Statistics_Description.rst:314\n#: ../../Tigase_Administration/Statistics_Description.rst:323\n#: ../../Tigase_Administration/Statistics_Description.rst:325\n#: ../../Tigase_Administration/Statistics_Description.rst:327\n#: ../../Tigase_Administration/Statistics_Description.rst:329\n#: ../../Tigase_Administration/Statistics_Description.rst:338\n#: ../../Tigase_Administration/Statistics_Description.rst:340\n#: ../../Tigase_Administration/Statistics_Description.rst:342\n#: ../../Tigase_Administration/Statistics_Description.rst:344\n#: ../../Tigase_Administration/Statistics_Description.rst:346\n#: ../../Tigase_Administration/Statistics_Description.rst:348\n#: ../../Tigase_Administration/Statistics_Description.rst:350\n#: ../../Tigase_Administration/Statistics_Description.rst:352\n#: ../../Tigase_Administration/Statistics_Description.rst:354\n#: ../../Tigase_Administration/Statistics_Description.rst:356\n#: ../../Tigase_Administration/Statistics_Description.rst:358\n#: ../../Tigase_Administration/Statistics_Description.rst:360\n#: ../../Tigase_Administration/Statistics_Description.rst:362\n#: ../../Tigase_Administration/Statistics_Description.rst:364\n#: ../../Tigase_Administration/Statistics_Description.rst:366\n#: ../../Tigase_Administration/Statistics_Description.rst:368\n#: ../../Tigase_Administration/Statistics_Description.rst:370\n#: ../../Tigase_Administration/Statistics_Description.rst:372\n#: ../../Tigase_Administration/Statistics_Description.rst:376\n#: ../../Tigase_Administration/Statistics_Description.rst:378\n#: ../../Tigase_Administration/Statistics_Description.rst:380\n#: ../../Tigase_Administration/Statistics_Description.rst:382\n#: ../../Tigase_Administration/Statistics_Description.rst:384\n#: ../../Tigase_Administration/Statistics_Description.rst:386\n#: ../../Tigase_Administration/Statistics_Description.rst:388\n#: ../../Tigase_Administration/Statistics_Description.rst:390\n#: ../../Tigase_Administration/Statistics_Description.rst:392\n#: ../../Tigase_Administration/Statistics_Description.rst:394\n#: ../../Tigase_Administration/Statistics_Description.rst:396\n#: ../../Tigase_Administration/Statistics_Description.rst:398\n#: ../../Tigase_Administration/Statistics_Description.rst:400\n#: ../../Tigase_Administration/Statistics_Description.rst:402\n#: ../../Tigase_Administration/Statistics_Description.rst:404\n#: ../../Tigase_Administration/Statistics_Description.rst:406\n#: ../../Tigase_Administration/Statistics_Description.rst:408\n#: ../../Tigase_Administration/Statistics_Description.rst:410\n#: ../../Tigase_Administration/Statistics_Description.rst:412\n#: ../../Tigase_Administration/Statistics_Description.rst:414\n#: ../../Tigase_Administration/Statistics_Description.rst:416\n#: ../../Tigase_Administration/Statistics_Description.rst:418\n#: ../../Tigase_Administration/Statistics_Description.rst:420\n#: ../../Tigase_Administration/Statistics_Description.rst:422\n#: ../../Tigase_Administration/Statistics_Description.rst:424\n#: ../../Tigase_Administration/Statistics_Description.rst:426\n#: ../../Tigase_Administration/Statistics_Description.rst:428\n#: ../../Tigase_Administration/Statistics_Description.rst:430\n#: ../../Tigase_Administration/Statistics_Description.rst:432\n#: ../../Tigase_Administration/Statistics_Description.rst:434\n#: ../../Tigase_Administration/Statistics_Description.rst:436\n#: ../../Tigase_Administration/Statistics_Description.rst:438\n#: ../../Tigase_Administration/Statistics_Description.rst:440\n#: ../../Tigase_Administration/Statistics_Description.rst:442\n#: ../../Tigase_Administration/Statistics_Description.rst:444\n#: ../../Tigase_Administration/Statistics_Description.rst:446\n#: ../../Tigase_Administration/Statistics_Description.rst:448\n#: ../../Tigase_Administration/Statistics_Description.rst:450\n#: ../../Tigase_Administration/Statistics_Description.rst:452\n#: ../../Tigase_Administration/Statistics_Description.rst:454\n#: ../../Tigase_Administration/Statistics_Description.rst:456\n#: ../../Tigase_Administration/Statistics_Description.rst:458\n#: ../../Tigase_Administration/Statistics_Description.rst:460\n#: ../../Tigase_Administration/Statistics_Description.rst:462\n#: ../../Tigase_Administration/Statistics_Description.rst:464\n#: ../../Tigase_Administration/Statistics_Description.rst:466\n#: ../../Tigase_Administration/Statistics_Description.rst:468\n#: ../../Tigase_Administration/Statistics_Description.rst:470\n#: ../../Tigase_Administration/Statistics_Description.rst:472\n#: ../../Tigase_Administration/Statistics_Description.rst:474\n#: ../../Tigase_Administration/Statistics_Description.rst:476\n#: ../../Tigase_Administration/Statistics_Description.rst:478\n#: ../../Tigase_Administration/Statistics_Description.rst:491\n#: ../../Tigase_Administration/Statistics_Description.rst:493\n#: ../../Tigase_Administration/Statistics_Description.rst:508\n#: ../../Tigase_Administration/Statistics_Description.rst:510\n#: ../../Tigase_Administration/Statistics_Description.rst:512\n#: ../../Tigase_Administration/Statistics_Description.rst:514\n#: ../../Tigase_Administration/Statistics_Description.rst:516\n#: ../../Tigase_Administration/Statistics_Description.rst:518\n#: ../../Tigase_Administration/Statistics_Description.rst:520\n#: ../../Tigase_Administration/Statistics_Description.rst:522\n#: ../../Tigase_Administration/Statistics_Description.rst:524\n#: ../../Tigase_Administration/Statistics_Description.rst:526\n#: ../../Tigase_Administration/Statistics_Description.rst:528\n#: ../../Tigase_Administration/Statistics_Description.rst:530\n#: ../../Tigase_Administration/Statistics_Description.rst:532\n#: ../../Tigase_Administration/Statistics_Description.rst:543\n#: ../../Tigase_Administration/Statistics_Description.rst:545\n#: ../../Tigase_Administration/Statistics_Description.rst:547\n#: ../../Tigase_Administration/Statistics_Description.rst:549\n#: ../../Tigase_Administration/Statistics_Description.rst:551\n#: ../../Tigase_Administration/Statistics_Description.rst:553\n#: ../../Tigase_Administration/Statistics_Description.rst:555\n#: ../../Tigase_Administration/Statistics_Description.rst:557\n#: ../../Tigase_Administration/Statistics_Description.rst:559\n#: ../../Tigase_Administration/Statistics_Description.rst:561\n#: ../../Tigase_Administration/Statistics_Description.rst:563\n#: ../../Tigase_Administration/Statistics_Description.rst:565\n#: ../../Tigase_Administration/Statistics_Description.rst:567\n#: ../../Tigase_Administration/Statistics_Description.rst:569\n#: ../../Tigase_Administration/Statistics_Description.rst:571\n#: ../../Tigase_Administration/Statistics_Description.rst:573\n#: ../../Tigase_Administration/Statistics_Description.rst:575\n#: ../../Tigase_Administration/Statistics_Description.rst:577\n#: ../../Tigase_Administration/Statistics_Description.rst:579\n#: ../../Tigase_Administration/Statistics_Description.rst:581\n#: ../../Tigase_Administration/Statistics_Description.rst:583\n#: ../../Tigase_Administration/Statistics_Description.rst:585\n#: ../../Tigase_Administration/Statistics_Description.rst:587\n#: ../../Tigase_Administration/Statistics_Description.rst:589\n#: ../../Tigase_Administration/Statistics_Description.rst:591\n#: ../../Tigase_Administration/Statistics_Description.rst:593\n#: ../../Tigase_Administration/Statistics_Description.rst:595\n#: ../../Tigase_Administration/Statistics_Description.rst:597\n#: ../../Tigase_Administration/Statistics_Description.rst:599\n#: ../../Tigase_Administration/Statistics_Description.rst:601\n#: ../../Tigase_Administration/Statistics_Description.rst:603\n#: ../../Tigase_Administration/Statistics_Description.rst:605\n#: ../../Tigase_Administration/Statistics_Description.rst:607\n#: ../../Tigase_Administration/Statistics_Description.rst:609\n#: ../../Tigase_Administration/Statistics_Description.rst:611\n#: ../../Tigase_Administration/Statistics_Description.rst:613\n#: ../../Tigase_Administration/Statistics_Description.rst:615\n#: ../../Tigase_Administration/Statistics_Description.rst:617\n#: ../../Tigase_Administration/Statistics_Description.rst:619\n#: ../../Tigase_Administration/Statistics_Description.rst:621\n#: ../../Tigase_Administration/Statistics_Description.rst:623\n#: ../../Tigase_Administration/Statistics_Description.rst:625\n#: ../../Tigase_Administration/Statistics_Description.rst:627\n#: ../../Tigase_Administration/Statistics_Description.rst:629\n#: ../../Tigase_Administration/Statistics_Description.rst:631\n#: ../../Tigase_Administration/Statistics_Description.rst:633\n#: ../../Tigase_Administration/Statistics_Description.rst:635\n#: ../../Tigase_Administration/Statistics_Description.rst:637\n#: ../../Tigase_Administration/Statistics_Description.rst:639\n#: ../../Tigase_Administration/Statistics_Description.rst:641\n#: ../../Tigase_Administration/Statistics_Description.rst:643\n#: ../../Tigase_Administration/Statistics_Description.rst:645\n#: ../../Tigase_Administration/Statistics_Description.rst:647\n#: ../../Tigase_Administration/Statistics_Description.rst:649\n#: ../../Tigase_Administration/Statistics_Description.rst:655\n#: ../../Tigase_Administration/Statistics_Description.rst:667\n#: ../../Tigase_Administration/Statistics_Description.rst:684\n#: ../../Tigase_Administration/Statistics_Description.rst:690\nmsgid \"FINEST\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:39\nmsgid \"``userRepository/{repo}/{method}/Executions last {interval}``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:41\n#: ../../Tigase_Administration/Statistics_Description.rst:90\nmsgid \"Number of executions of a {method}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:41\nmsgid \"Number of times specified method has been executed\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:41\nmsgid \"``userRepository/{repo}/{method}/Executions``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:45\nmsgid \"Auth repository statistics of {repo}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:47\nmsgid \"\"\n\"For every {method} declared in ``AuthRepository`` we gather execution \"\n\"statistics. This statistics are collected separately for every data \"\n\"source for which authentication repository is defined.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:52\n#: ../../Tigase_Administration/Statistics_Description.rst:86\nmsgid \"Average time it takes to process {method}.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:52\nmsgid \"``authRepository/{repo}/{method}/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:54\nmsgid \"Number of exceptions of {method}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:54\nmsgid \"Number of times {method} has caused an exception.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:54\nmsgid \"``authRepository/{repo}/{method}/Exceptions during execution``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:56\nmsgid \"Number of exceptions of {method} in last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:56\nmsgid \"\"\n\"Number of times {method} has caused an exception within the specified \"\n\"interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:56\nmsgid \"``authRepository/{repo}/{method}/Executions last {interval}``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:58\nmsgid \"Number of executions of {method}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:58\n#: ../../Tigase_Administration/Statistics_Description.rst:90\nmsgid \"Number of times {method} has been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:58\nmsgid \"``authRepository/{repo}/{method}/Executions``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:62\nmsgid \"Statistics common to custom {compname} component repositories\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:64\nmsgid \"\"\n\"These statistics may be found in many components which are using \"\n\"repository implementations created just for them. An example of such \"\n\"components may be:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:67\nmsgid \"**amp**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:67\nmsgid \"with msgBroadcastRepository as {repo} name,\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:70\nmsgid \"**message-archive**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:70\nmsgid \"with repositoryPool as a {repo} name,\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:73\nmsgid \"**muc**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:73\nmsgid \"with muc-dao as a {repo} name,\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:76\nmsgid \"**pubsub**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:76\nmsgid \"with dao as a {repo} name,\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:79\nmsgid \"**sess-man**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:79\nmsgid \"with msgRepository as a {repo} name\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:81\nmsgid \"\"\n\"For custom component repositories we gather statistics in a same way as \"\n\"we do for user and authorization repositories. Statistics are collected \"\n\"on per {method} basis separately for every data source ({dataSourceName})\"\n\" for which repository is defined.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:86\nmsgid \"``{compname}/{repo}/{dataSourceName}/{method}/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:88\nmsgid \"Number of exceptions {method} has caused.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:88\nmsgid \"\"\n\"``{compname}/{repo}/{dataSourceName}/{method}/Exceptions during \"\n\"execution``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:90\nmsgid \"``{compname}/{repo}/{dataSourceName}/{method}/Executions``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:94\nmsgid \"Statistics common to components\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:96\nmsgid \"\"\n\"These statistics may be found in multiple components and may be seen \"\n\"multiple times. For example both s2s and c2s will have Bytes received \"\n\"statistic, so each can be found the following way:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:104\n#: ../../Tigase_Administration/Statistics_Description.rst:188\n#: ../../Tigase_Administration/Statistics_Description.rst:206\n#: ../../Tigase_Administration/Statistics_Description.rst:248\n#: ../../Tigase_Administration/Statistics_Description.rst:257\n#: ../../Tigase_Administration/Statistics_Description.rst:295\n#: ../../Tigase_Administration/Statistics_Description.rst:306\n#: ../../Tigase_Administration/Statistics_Description.rst:336\n#: ../../Tigase_Administration/Statistics_Description.rst:506\n#: ../../Tigase_Administration/Statistics_Description.rst:539\nmsgid \"Available {field}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:106\nmsgid \"add-script last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:106\nmsgid \"\"\n\"The number of times that ``add-script`` adhoc command has been run within\"\n\" the last interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:106\n#: ../../Tigase_Administration/Statistics_Description.rst:116\n#: ../../Tigase_Administration/Statistics_Description.rst:120\n#: ../../Tigase_Administration/Statistics_Description.rst:122\n#: ../../Tigase_Administration/Statistics_Description.rst:192\n#: ../../Tigase_Administration/Statistics_Description.rst:208\n#: ../../Tigase_Administration/Statistics_Description.rst:212\n#: ../../Tigase_Administration/Statistics_Description.rst:216\n#: ../../Tigase_Administration/Statistics_Description.rst:220\n#: ../../Tigase_Administration/Statistics_Description.rst:297\n#: ../../Tigase_Administration/Statistics_Description.rst:308\n#: ../../Tigase_Administration/Statistics_Description.rst:312\n#: ../../Tigase_Administration/Statistics_Description.rst:340\n#: ../../Tigase_Administration/Statistics_Description.rst:344\n#: ../../Tigase_Administration/Statistics_Description.rst:360\n#: ../../Tigase_Administration/Statistics_Description.rst:364\n#: ../../Tigase_Administration/Statistics_Description.rst:372\n#: ../../Tigase_Administration/Statistics_Description.rst:376\n#: ../../Tigase_Administration/Statistics_Description.rst:380\n#: ../../Tigase_Administration/Statistics_Description.rst:384\n#: ../../Tigase_Administration/Statistics_Description.rst:388\n#: ../../Tigase_Administration/Statistics_Description.rst:400\n#: ../../Tigase_Administration/Statistics_Description.rst:404\n#: ../../Tigase_Administration/Statistics_Description.rst:408\n#: ../../Tigase_Administration/Statistics_Description.rst:412\n#: ../../Tigase_Administration/Statistics_Description.rst:416\n#: ../../Tigase_Administration/Statistics_Description.rst:420\n#: ../../Tigase_Administration/Statistics_Description.rst:424\n#: ../../Tigase_Administration/Statistics_Description.rst:428\n#: ../../Tigase_Administration/Statistics_Description.rst:432\n#: ../../Tigase_Administration/Statistics_Description.rst:436\n#: ../../Tigase_Administration/Statistics_Description.rst:440\n#: ../../Tigase_Administration/Statistics_Description.rst:446\n#: ../../Tigase_Administration/Statistics_Description.rst:450\n#: ../../Tigase_Administration/Statistics_Description.rst:454\n#: ../../Tigase_Administration/Statistics_Description.rst:458\n#: ../../Tigase_Administration/Statistics_Description.rst:462\n#: ../../Tigase_Administration/Statistics_Description.rst:470\n#: ../../Tigase_Administration/Statistics_Description.rst:476\n#: ../../Tigase_Administration/Statistics_Description.rst:510\n#: ../../Tigase_Administration/Statistics_Description.rst:514\n#: ../../Tigase_Administration/Statistics_Description.rst:518\n#: ../../Tigase_Administration/Statistics_Description.rst:543\n#: ../../Tigase_Administration/Statistics_Description.rst:547\n#: ../../Tigase_Administration/Statistics_Description.rst:551\n#: ../../Tigase_Administration/Statistics_Description.rst:555\n#: ../../Tigase_Administration/Statistics_Description.rst:559\n#: ../../Tigase_Administration/Statistics_Description.rst:563\n#: ../../Tigase_Administration/Statistics_Description.rst:567\n#: ../../Tigase_Administration/Statistics_Description.rst:575\n#: ../../Tigase_Administration/Statistics_Description.rst:579\n#: ../../Tigase_Administration/Statistics_Description.rst:583\n#: ../../Tigase_Administration/Statistics_Description.rst:587\n#: ../../Tigase_Administration/Statistics_Description.rst:591\n#: ../../Tigase_Administration/Statistics_Description.rst:595\n#: ../../Tigase_Administration/Statistics_Description.rst:599\n#: ../../Tigase_Administration/Statistics_Description.rst:603\n#: ../../Tigase_Administration/Statistics_Description.rst:607\n#: ../../Tigase_Administration/Statistics_Description.rst:611\n#: ../../Tigase_Administration/Statistics_Description.rst:615\n#: ../../Tigase_Administration/Statistics_Description.rst:619\n#: ../../Tigase_Administration/Statistics_Description.rst:623\n#: ../../Tigase_Administration/Statistics_Description.rst:627\n#: ../../Tigase_Administration/Statistics_Description.rst:631\n#: ../../Tigase_Administration/Statistics_Description.rst:635\n#: ../../Tigase_Administration/Statistics_Description.rst:639\nmsgid \"hour minute second\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:106\nmsgid \"\"\n\"``{compname}/adhoc-command/add-script last hour`` ``{compname}/adhoc-\"\n\"command/add-script last minute`` ``{compname}/adhoc-command/add-script \"\n\"last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:108\nmsgid \"add-script/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:108\nmsgid \"The average processing time ``add-script`` takes to complete.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:108\nmsgid \"``add-script/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:110\nmsgid \"Average processing time on last 100 runs [ms]\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:110\nmsgid \"\"\n\"The average processing time in milliseconds for all commands and scripts \"\n\"for this component over the last 100 times component is called. This \"\n\"number will populate with less than 100 runs, and will continue averaging\"\n\" until 100 runs happens, at that point, it’s the most recent 100 \"\n\"instances. This statistic will reset every time the server shuts down or \"\n\"restarts.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:110\nmsgid \"``{compname}/Average processing time on last 100 runs [ms]``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:112\nmsgid \"Bytes received\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:112\nmsgid \"\"\n\"The total number of bytes that the component has received during the \"\n\"current server instance. This statistic resets at server shutdown or \"\n\"restart.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:112\n#: ../../Tigase_Administration/Statistics_Description.rst:114\nmsgid \"FINE or FINEST\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:112\nmsgid \"``{compname}/Bytes received``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:114\nmsgid \"Bytes sent\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:114\nmsgid \"\"\n\"The total number of bytes that the component has sent during the current \"\n\"server instance. This statistic resets at server shutdown or restart.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:114\nmsgid \"``{compname}/Bytes sent``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:116\nmsgid \"del-script last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:116\nmsgid \"\"\n\"The number of times that ``del-script`` adhoc command has been run within\"\n\" the last interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:116\nmsgid \"\"\n\"``{compname}/adhoc-command/del-script last hour`` ``{compname}/adhoc-\"\n\"command/del-script last minute`` ``{compname}/adhoc-command/del-script \"\n\"last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:118\nmsgid \"del-script Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:118\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``del-\"\n\"script`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:118\nmsgid \"``{compname}/adhoc-command/del-script/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:120\nmsgid \"Last {interval} packets\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:120\nmsgid \"\"\n\"The number of packets that have been handled by this component in the \"\n\"last interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:120\nmsgid \"\"\n\"``{compname}/last hour packets`` ``{compname}/last minute packets`` \"\n\"``{compname}/last second packets``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:122\nmsgid \"List-commands last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:122\nmsgid \"\"\n\"The number of ``list-commands`` requests sent to the component in the \"\n\"last interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:122\nmsgid \"\"\n\"``{compname}/list-commands last hour`` ``{compname}/list-commands last \"\n\"minute`` ``{compname}/list-commands last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:124\nmsgid \"List-commands Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:124\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``list-\"\n\"commands`` to execute on this component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:124\nmsgid \"``{compname}/list-commands/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:126\nmsgid \"{IN/OUT/Total} queue overflow\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:126\nmsgid \"\"\n\"The number of times the in or out queue has overflown for this component.\"\n\" That is there are more packets queues than the max queue size. A total \"\n\"statistic is also available that combines both results.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:126\nmsgid \"\"\n\"``{compname}/IN queue overflow`` ``{compname}/OUT queue overflow`` \"\n\"``{compname}/Total queue overflow``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:128\nmsgid \"{in/out} queue wait: {priority}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:128\nmsgid \"\"\n\"The number of packets with {priority} priority currently in the incoming \"\n\"or outgoing queue.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:128\nmsgid \"SYSTEM CLUSTER HIGH NORMAL LOW PRESENCE LOWEST\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:128\nmsgid \"\"\n\"``{compname}/In queue wait: SYSTEM`` ``{compname}/In queue wait: \"\n\"CLUSTER`` ``{compname}/In queue wait: HIGH`` ``{compname}/In queue wait: \"\n\"NORMAL`` ``{compname}/In queue wait: LOW`` ``{compname}/In queue wait: \"\n\"PRESENCE`` ``{compname}/In queue wait: LOWEST`` ``{compname}/Out queue \"\n\"wait: SYSTEM`` ``{compname}/Out queue wait: CLUSTER`` ``{compname}/Out \"\n\"queue wait: HIGH`` ``{compname}/Out queue wait: NORMAL`` ``{compname}/Out\"\n\" queue wait: LOW`` ``{compname}/Out queue wait: PRESENCE`` \"\n\"``{compname}/Out queue wait: LOWEST``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:130\nmsgid \"{IN/OUT}_QUEUE processed {type}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:130\nmsgid \"\"\n\"The number of stanzas of different types that have been processed VIA the\"\n\" In or Out Queue of this component. This number will reset at the end of \"\n\"the server instance. Each component will have a list of the different \"\n\"types of stanzas it can process.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:130\n#: ../../Tigase_Administration/Statistics_Description.rst:151\n#: ../../Tigase_Administration/Statistics_Description.rst:169\n#: ../../Tigase_Administration/Statistics_Description.rst:171\n#: ../../Tigase_Administration/Statistics_Description.rst:173\n#: ../../Tigase_Administration/Statistics_Description.rst:541\n#: ../../Tigase_Administration/Statistics_Description.rst:661\n#: ../../Tigase_Administration/Statistics_Description.rst:663\n#: ../../Tigase_Administration/Statistics_Description.rst:686\n#: ../../Tigase_Administration/Statistics_Description.rst:688\nmsgid \"FINER\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:130\nmsgid \"\"\n\"# messages presences cluster other IQ no XMLNS IQ \"\n\"http://jabber.org/protocol/disco#items IQ bind IQ jabber:iq:roster IQ \"\n\"session IQ vCard IQ command IQ jabber:iq:private IQ \"\n\"http://jabber.org/protocol/disco#info total IQ\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:130\nmsgid \"\"\n\"``{compname}/IN_QUEUE processed`` ``{compname}/IN_QUEUE processed \"\n\"messages`` ``{compname}/IN_QUEUE processed presences`` \"\n\"``{compname}/IN_QUEUE processed cluster`` ``{compname}/IN_QUEUE processed\"\n\" other`` ``{compname}/IN_QUEUE processed IQ no XMLNS`` \"\n\"``{compname}/IN_QUEUE processed IQ \"\n\"http://jabber.org/protocol/disco#items`` ``{compname}/IN_QUEUE processed \"\n\"IQ http://jabber.org/protocol/disco#info`` ``{compname}/IN_QUEUE \"\n\"processed IQ bind`` ``{compname}/IN_QUEUE processed IQ jabber:iq:roster``\"\n\" ``{compname}/IN_QUEUE processed IQ jabber:iq:private`` \"\n\"``{compname}/IN_QUEUE processed IQ session`` ``{compname}/IN_QUEUE \"\n\"processed IQ vCard`` ``{compname}/IN_QUEUE processed IQ command`` \"\n\"``{compname}/IN_QUEUE processed total IQ`` ``{compname}/OUT_QUEUE \"\n\"processed messages`` ``{compname}/OUT_QUEUE processed presences`` \"\n\"``{compname}/OUT_QUEUE processed cluster`` ``{compname}/OUT_QUEUE \"\n\"processed other`` ``{compname}/OUT_QUEUE processed IQ no XMLNS`` \"\n\"``{compname}/OUT_QUEUE processed IQ \"\n\"http://jabber.org/protocol/disco#items`` ``{compname}/OUT_QUEUE processed\"\n\" IQ http://jabber.org/protocol/disco#info`` ``{compname}/OUT_QUEUE \"\n\"processed IQ bind`` ``{compname}/OUT_QUEUE processed IQ \"\n\"jabber:iq:roster`` ``{compname}/OUT_QUEUE processed IQ \"\n\"jabber:iq:private`` ``{compname}/OUT_QUEUE processed IQ session`` \"\n\"``{compname}/OUT_QUEUE processed IQ vCard`` ``{compname}/OUT_QUEUE \"\n\"processed IQ command`` ``{compname}/OUT_QUEUE processed total IQ``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:132\nmsgid \"\"\n\"NOTE: Several statistics are only available from statistics component, \"\n\"shutdown thread will ONLY print the following: messages, presences, \"\n\"cluster, other, IQ no XLMNS, total IQ.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:134\nmsgid \"max queue size\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:134\nmsgid \"\"\n\"The maximum number of items allowed in the packet queue for this \"\n\"component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:134\nmsgid \"``{compname}/max queue size``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:136\nmsgid \"Open Connections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:136\nmsgid \"The number of open connections to the component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:136\n#: ../../Tigase_Administration/Statistics_Description.rst:653\nmsgid \"INFO/FINEST\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:136\nmsgid \"``{compname}/Open connections``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:138\nmsgid \"Packets received\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:138\nmsgid \"\"\n\"The total number of packets received by the component from external \"\n\"sources in the current instance. This number resets at server shutdown or\"\n\" restart.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:138\nmsgid \"``{compname}/Packets received``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:140\nmsgid \"Packets sent\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:140\nmsgid \"\"\n\"The total number of packets sent by the component in the current \"\n\"instance. This number resets at server shutdown or restart.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:140\nmsgid \"``{compname}/Packets sent``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:142\nmsgid \"Processed packets thread: {in/out}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:142\nmsgid \"How many packets have been processed in and out by each processing thread.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:142\nmsgid \"\"\n\"``{compname}/Processed packets thread: IN`` ``{compname}/Processed \"\n\"packets thread: OUT`` ``{compname}/Processed packets thread (outliers) \"\n\"IN`` ``{compname}/Processed packets thread (outliers) OUT``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"\"\n\"Statistics will provide an array for each processor, listed from 0, 1, 2,\"\n\" 3 etc.. Let’s say that we have 4 threads set for ws2s, a list will be \"\n\"seen like this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"``ws2s/Processed packets thread: IN=[2, 6, 4, 2]``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"``ws2s/Processed packets thread: OUT=[8, 0, 1, 3]``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"\"\n\"``ws2s/Processed packets thread (outliers) IN=mean: 79.0, deviation: 441,\"\n\" outliers: [in_10-ws2s: 2359]``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"\"\n\"``ws2s/Processed packets thread (outliers) OUT=mean: 16.5, deviation: \"\n\"23.2058941, outliers: [out_ws2s: 80]``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"\"\n\"Note that the processor arrray will only have as many threads as the \"\n\"component has as defined in {compname}/Processing threads.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:151\nmsgid \"processing threads\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:151\nmsgid \"The number of threads provided for the particular component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:151\nmsgid \"``{compname}/processing threads``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:153\nmsgid \"stream-error-counter\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:153\nmsgid \"\"\n\"The number of errors counted during the operation of the server for this \"\n\"component. Will only be available if :ref:`stream-error-counter<stream-\"\n\"error-counter>` is enabled in config.tdsl, otherwise will be 0.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:153\nmsgid \"``{compname}/processors/stream-error-counter``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:155\nmsgid \"Socket overflow\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:155\nmsgid \"\"\n\"The number of times that this component has experienced socket queue \"\n\"overflow and had to drop packets. The XMPP server queues packets which \"\n\"are being sent over connection if receiver is not able to read them fast \"\n\"enough or if the network connection too slow to the amount of data which \"\n\"needs to be sent. If the queue will over flow that will be counted. This \"\n\"does not include the number of dropped packets.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:155\nmsgid \"``{compname}/Socket overflow``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:157\nmsgid \"Total {in/out} queues wait\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:157\nmsgid \"\"\n\"The number of packets in the inbound or outbound queue that are currently\"\n\" waiting to be sent. This includes packets of all types. This is an \"\n\"instant statistics, in that the number in queue is only as many in the \"\n\"queue the moment statistics are gathered.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:157\nmsgid \"``{compname}/Total in queues wait`` ``{compname}/Total out queues wait``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:159\nmsgid \"Total queue wait\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:159\nmsgid \"\"\n\"A combined total of ``Total in queue wait`` and ``Total out queue wait`` \"\n\"statistics for this component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:159\nmsgid \"``{compname}/Total queue wait``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:161\nmsgid \"Total queues wait\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:161\nmsgid \"A combined total of all component queue wait statistics.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:161\nmsgid \"``Total queues wait``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:163\nmsgid \"Total queues overflow\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:163\nmsgid \"\"\n\"The number of times the component packet wait queue has overflown and had\"\n\" to drop packets. This statistic does not keep track of the number of \"\n\"dropped packets.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:163\nmsgid \"``{compname}/Total queues overflow``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:165\nmsgid \"Total/Total queues overflow\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:165\nmsgid \"The combined total of all queue overflow statistics for all components.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:165\nmsgid \"``total/Total queues overflow``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:167\nmsgid \"Waiting to send\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:167\nmsgid \"\"\n\"The number of packets in the component’s queue that are waiting to be \"\n\"sent. This number will usually be 0 however it will grow if a large \"\n\"number of packets are jamming up your system, or your queue sizes are set\"\n\" too low.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:167\nmsgid \"``{compname}/Waiting to send``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:169\nmsgid \"Watchdog runs\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:169\nmsgid \"\"\n\"The number of times watchdog has been run on this component to check for \"\n\"stale connections.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:169\nmsgid \"``{compname}/Watchdog runs``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:171\nmsgid \"Watchdog stopped\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:171\nmsgid \"\"\n\"The number of times watchdog identified and closed a connection it has \"\n\"found to be stale according to the settings in ``config.tdsl`` or by the \"\n\"defaults defined :ref:`in this section<watchdog>`.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:171\nmsgid \"``{compname}/Watchdog stopped``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:173\nmsgid \"Watchdog tests\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:173\nmsgid \"\"\n\"The number of times watchdog has found a potential stale connection and \"\n\"has conducted a test to determine whether or not to close the connection.\"\n\" This is per component in the current server instance.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:173\nmsgid \"``{compname}/Watchdog tests``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:177\nmsgid \"Component statistics\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:180\nmsgid \"AMP\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:182\nmsgid \"No exclusive amp specific statistics\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:185\nmsgid \"bosh\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:190\nmsgid \"Bosh sessions\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:190\nmsgid \"The number of currently open and running BOSH sessions to the server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:190\nmsgid \"``bosh/Bosh sessions``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:192\nmsgid \"pre-bind session last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:192\nmsgid \"\"\n\"The number of times the pre-bind-session command has been executed within\"\n\" the last specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:192\nmsgid \"\"\n\"``bosh/adhoc-command/pre-bind-session last hour`` ``bosh/adhoc-command\"\n\"/pre-bind-session last minute`` ``bosh/adhoc-command/pre-bind-session \"\n\"last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:194\nmsgid \"pre-bind-sessions/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:194\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``pre-bind-\"\n\"session`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:194\nmsgid \"``bosh/adhoc-command/pre-bind-session/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:198\nmsgid \"c2s\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:200\nmsgid \"No exclusive c2s specific statistics.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:203\nmsgid \"cl-comp\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:208\nmsgid \"adhoc-command/cluster-nodes-list last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:208\nmsgid \"\"\n\"The number of times per interval that the cluster-nodes-list command has \"\n\"been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:208\nmsgid \"\"\n\"``cl-comp/adhoc-command/cluster-nodes-list last hour`` ``cl-comp/adhoc-\"\n\"command/cluster-nodes-list last minute`` ``cl-comp/adhoc-command/cluster-\"\n\"nodes-list last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:210\nmsgid \"adhoc-command/cluster-nodes-list/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:210\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``cluster-\"\n\"nodes-list`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:210\nmsgid \"``cl-comp/adhoc-command/cluster-nodes-list/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:212\nmsgid \"adhoc-command/force-stop-service last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:212\nmsgid \"\"\n\"The number of times per interval that the force-stop-service command has \"\n\"been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:212\nmsgid \"\"\n\"``cl-comp/adhoc-command/force-stop-service last hour`` ``cl-comp/adhoc-\"\n\"command/force-stop-service last minute`` ``cl-comp/adhoc-command/force-\"\n\"stop-service last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:214\nmsgid \"Adhoc-command/force-stop-service/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:214\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``force-\"\n\"stop-service`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:214\nmsgid \"``cl-comp/adhoc-command/force-stop-service/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:216\nmsgid \"adhoc-command/service-keys last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:216\nmsgid \"\"\n\"The number of times per interval that the ``service-keys`` command has \"\n\"been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:216\nmsgid \"\"\n\"``cl-comp/adhoc-command/service-keys last hour`` ``cl-comp/adhoc-command\"\n\"/service-keys last minute`` ``cl-comp/adhoc-command/service-keys last \"\n\"second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:218\nmsgid \"Adhoc-command/service-keys/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:218\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``service-\"\n\"keys`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:218\nmsgid \"``cl-comp/adhoc-command/service-keys/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:220\nmsgid \"adhoc-command/sim-serv-stopped {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:220\nmsgid \"\"\n\"The number of times per interval that the ``sim-serv-stopped`` command \"\n\"has been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:220\nmsgid \"\"\n\"``cl-comp/adhoc-command/sim-serv-stopped last hour`` ``cl-comp/adhoc-\"\n\"command/sim-serv-stopped last minute`` ``cl-comp/adhoc-command/sim-serv-\"\n\"stopped last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:222\nmsgid \"Adhoc-command/sim-serv-stopped/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:222\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``sim-serv-\"\n\"stopped`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:222\nmsgid \"``cl-comp/adhoc-command/sim-serv-stopped/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:224\nmsgid \"Average compression ratio\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:224\nmsgid \"\"\n\"The average compression ratio of data sent to other clusters during the \"\n\"session.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:224\n#: ../../Tigase_Administration/Statistics_Description.rst:226\n#: ../../Tigase_Administration/Statistics_Description.rst:269\n#: ../../Tigase_Administration/Statistics_Description.rst:273\n#: ../../Tigase_Administration/Statistics_Description.rst:279\n#: ../../Tigase_Administration/Statistics_Description.rst:374\nmsgid \"Float\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:224\nmsgid \"``cl-comp/Average compression ratio``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:226\nmsgid \"Average decompression ratio\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:226\nmsgid \"\"\n\"The average compression ratio of data received from other clusters during\"\n\" the session.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:226\nmsgid \"``cl-comp/Average decompression ratio``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:228\nmsgid \"Known cluster nodes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:228\nmsgid \"The number of cluster nodes currently connected to the server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:228\n#: ../../Tigase_Administration/Statistics_Description.rst:271\n#: ../../Tigase_Administration/Statistics_Description.rst:275\n#: ../../Tigase_Administration/Statistics_Description.rst:283\n#: ../../Tigase_Administration/Statistics_Description.rst:285\n#: ../../Tigase_Administration/Statistics_Description.rst:651\n#: ../../Tigase_Administration/Statistics_Description.rst:657\n#: ../../Tigase_Administration/Statistics_Description.rst:659\n#: ../../Tigase_Administration/Statistics_Description.rst:665\nmsgid \"INFO\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:228\nmsgid \"``cl-comp/Known cluster nodes``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:230\nmsgid \"Last {interval} disconnects\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:230\nmsgid \"The number of cluster disconnections within the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:230\nmsgid \"Comma Separated Array\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:230\nmsgid \"day hour\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:230\nmsgid \"``cl-comp/Last day disconnects`` ``cl-comp/Last hour disconnects``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:232\nmsgid \"\"\n\"For day, each array is the number of disconnections each hour, most \"\n\"recent first. For hour each array is the number of disconnections each \"\n\"minute, most recent first.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:234\nmsgid \"Service connected time-outs\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:234\nmsgid \"The number of time-outs during connection initialization of cluster nodes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:234\nmsgid \"``cl-comp/Service connected time-outs``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:236\nmsgid \"Total disconnects\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:236\nmsgid \"The number of clusters that have disconnected during the current session.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:236\nmsgid \"``cl-comp/Total disconnects``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:240\nmsgid \"eventbus\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:242\nmsgid \"No exclusive eventbus specific statistics.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:245\nmsgid \"message-archive\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:250\nmsgid \"Removal time of expired messages (avg)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:250\nmsgid \"\"\n\"The average amount of time in milliseconds it takes to remove expired \"\n\"messages from the repository. This includes manual and automatic removal \"\n\"of messages.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:250\nmsgid \"``message-archive/Removal time of expired messages (avg)``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:254\nmsgid \"message-router\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:259\nmsgid \"CPUs no\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:259\nmsgid \"The number of CPUs available on the host machine.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:259\nmsgid \"``message-router/CPUs no``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:261\nmsgid \"CPU Usage\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:261\n#, python-format\nmsgid \"\"\n\"% of available CPU power used by Tigase Server at the moment statistics \"\n\"are taken.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:261\nmsgid \"Float/String\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:261\nmsgid \"``message-router/CPU usage [%]`` ``message-router/CPU usage``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:263\n#, python-format\nmsgid \"\"\n\"Two formats are available for CPU usage: A float integer which expresses \"\n\"a long decimal available from CPU Usage [%], and a string which provides \"\n\"a rounded number with a % sign from CPU usage.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:265\nmsgid \"Free Heap\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:265\nmsgid \"The amount of heap memory that is available for use, expressed in KB.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:265\nmsgid \"``message-router/Free Heap``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:267\nmsgid \"Free NonHeap\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:267\nmsgid \"The amount of non-heap memory that is available for use, expressed in KB.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:267\nmsgid \"``message-router/Free NonHeap``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:269\nmsgid \"HEAP usage [%]\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:269\nmsgid \"Total percent of HEAP memory in use by Tigase.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:269\nmsgid \"``message-router/HEAP usage [%]``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:271\nmsgid \"Local hostname\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:271\nmsgid \"The local hostname of the physical server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:271\nmsgid \"``message-router/Local hostname``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:273\nmsgid \"Load average\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:273\nmsgid \"\"\n\"The average system load for the previous minute. The way in which the \"\n\"load average is calculated is operating system specific but is typically \"\n\"a damped time-dependent average.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:273\nmsgid \"``message-router/Load average``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:275\nmsgid \"Max Heap mem\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:275\nmsgid \"\"\n\"Maximum amount of heap memory available as defined by JAVA_OPTIONS in \"\n\"tigase.conf, in Kb.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:275\nmsgid \"``message-router/Max Heap mem``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:277\nmsgid \"Max NonHeap mem\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:277\nmsgid \"\"\n\"Maximum amount of non-heap memory available as defined by JAVA_OPTIONS in\"\n\" tigase.conf, in Kb.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:277\nmsgid \"``message-router/Max NonHeap mem``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:279\nmsgid \"NONHEAP Usage [%]\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:279\nmsgid \"Total amount of NONHEAP memory in use expressed as a percentage.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:279\nmsgid \"``message-router/NONHEAP usage [%]``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:281\nmsgid \"Threads count\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:281\nmsgid \"The total number of processing threads available across all components.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:281\nmsgid \"``message-router/Threads count``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:283\nmsgid \"Uptime\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:283\nmsgid \"The total amount of time the server has been online for this session.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:283\nmsgid \"``message-router/Uptime``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:285\nmsgid \"Used Heap\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:285\nmsgid \"The amount of heap memory in use in KB.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:285\nmsgid \"``message-router/Used Heap``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:287\nmsgid \"Used NonHeap\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:287\nmsgid \"The amount of non-heap memory in use shown in KB.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:287\nmsgid \"``message-router/Used NonHeap``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:292\nmsgid \"monitor\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:297\nmsgid \"adhoc-command/load-errors last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:297\nmsgid \"\"\n\"The number of times per interval that the load-errors command has been \"\n\"executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:297\nmsgid \"\"\n\"``monitor/adhoc-command/load-errors last hour`` ``monitor/adhoc-command\"\n\"/load-errors last minute`` ``monitor/adhoc-command/load-errors last \"\n\"second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:299\nmsgid \"Adhoc-command/load-errors/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:299\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``load-\"\n\"errors`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:299\nmsgid \"``monitor/adhoc-command/load-errors/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:303\nmsgid \"muc\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:308\nmsgid \"adhoc-command/remove-room last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:308\nmsgid \"\"\n\"The number of times per interval that the remove-room command has been \"\n\"executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:308\nmsgid \"\"\n\"``monitor/adhoc-command/remove-room last hour`` ``monitor/adhoc-command\"\n\"/remove-room last minute`` ``monitor/adhoc-command/remove-room last \"\n\"second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:310\nmsgid \"Adhoc-command/remove-room/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:310\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``remove-\"\n\"room`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:310\nmsgid \"``monitor/adhoc-command/remove-room/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:312\nmsgid \"adhoc-command/default-room-config last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:312\nmsgid \"\"\n\"The number of times per interval that the default-room-command command \"\n\"has been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:312\nmsgid \"\"\n\"``muc/adhoc-command/default-room-config last hour`` ``muc/adhoc-command\"\n\"/default-room-config last minute`` ``muc/adhoc-command/default-room-\"\n\"config last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:314\nmsgid \"Adhoc-command/default-room-config/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:314\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``default-\"\n\"room-config`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:314\nmsgid \"``muc/adhoc-command/default-room-config/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:318\nmsgid \"proxy\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:323\nmsgid \"Average transfer size in KB\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:323\nmsgid \"\"\n\"Average size of packets sent through the proxy component during the \"\n\"current session.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:323\nmsgid \"``proxy/Average transfer size in KB``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:325\nmsgid \"KBytes transferred\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:325\nmsgid \"Total number of Kb transferred through the proxy component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:325\nmsgid \"``proxy/KBytes transferred``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:327\nmsgid \"Open streams\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:327\nmsgid \"Number of currently open proxy streams.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:327\nmsgid \"``proxy/Open streams``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:329\nmsgid \"Transfers completed\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:329\nmsgid \"Number of specific transfers completed through proxy component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:329\nmsgid \"``proxy/Transfers completed``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:333\nmsgid \"pubsub\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:338\nmsgid \"Added new nodes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:338\nmsgid \"\"\n\"The total number of new nodes that has been added in the current server \"\n\"instance. This statistic is reset when the server resets.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:338\nmsgid \"``pubsub/Added new nodes``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:340\nmsgid \"adhoc-command/delete-item last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:340\nmsgid \"\"\n\"The number of times per interval that the ``delete-item`` command has \"\n\"been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:340\nmsgid \"\"\n\"``pubsub/adhoc-command/delete-item last hour`` ``pubsub/adhoc-command\"\n\"/delete-item last minute`` ``pubsub/adhoc-command/delete-item last \"\n\"second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:342\nmsgid \"adhoc-command/delete-item/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:342\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``delete-\"\n\"item`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:342\nmsgid \"``pubsub/adhoc-command/delete-item/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:344\nmsgid \"adhoc-command/delete-node last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:344\nmsgid \"\"\n\"The number of times per interval that the ``delete-node`` command has \"\n\"been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:344\nmsgid \"\"\n\"``pubsub/adhoc-command/delete-node last hour`` ``pubsub/adhoc-command\"\n\"/delete-node last minute`` ``pubsub/adhoc-command/delete-node last \"\n\"second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:346\nmsgid \"adhoc-command/delete-node/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:346\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``delete-\"\n\"node`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:346\nmsgid \"``pubsub/adhoc-command/delete-node/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:348\nmsgid \"adhoc-command/list-items last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:348\nmsgid \"\"\n\"The number of times per interval that the ``list-items`` command has been\"\n\" executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:348\nmsgid \"\"\n\"``pubsub/adhoc-command/list-items last hour`` ``pubsub/adhoc-command\"\n\"/list-items last minute`` ``pubsub/adhoc-command/list-items last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:350\nmsgid \"adhoc-command/list-items/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:350\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``list-\"\n\"items`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:350\nmsgid \"``pubsub/adhoc-command/list-items/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:352\nmsgid \"adhoc-command/list-nodes last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:352\nmsgid \"\"\n\"The number of times per interval that the ``list-nodes`` command has been\"\n\" executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:352\nmsgid \"\"\n\"``pubsub/adhoc-command/list-nodes last hour`` ``pubsub/adhoc-command\"\n\"/list-nodes last minute`` ``pubsub/adhoc-command/list-nodes last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:354\nmsgid \"adhoc-command/list-nodes/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:354\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``list-\"\n\"nodes`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:354\nmsgid \"``pubsub/adhoc-command/list-nodes/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:356\nmsgid \"adhoc-command/publish-item last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:356\nmsgid \"\"\n\"The number of times per interval that the ``publish-item`` command has \"\n\"been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:356\nmsgid \"\"\n\"``pubsub/adhoc-command/publish-item last hour`` ``pubsub/adhoc-command\"\n\"/publish-item last minute`` ``pubsub/adhoc-command/publish-item last \"\n\"second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:358\nmsgid \"adhoc-command/publish-item/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:358\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``publish-\"\n\"item`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:358\nmsgid \"``pubsub/adhoc-command/publish-item/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:360\nmsgid \"adhoc-command/retrieve-item last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:360\nmsgid \"\"\n\"The number of times per interval that the ``retrieve-item`` command has \"\n\"been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:360\nmsgid \"\"\n\"``pubsub/adhoc-command/retrieve-item last hour`` ``pubsub/adhoc-command\"\n\"/retrieve-item last minute`` ``pubsub/adhoc-command/retrieve-item last \"\n\"second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:362\nmsgid \"adhoc-command/retrieve-item/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:362\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``retrieve-\"\n\"item`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:362\nmsgid \"``pubsub/adhoc-command/retrieve-item/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:364\nmsgid \"AdHocConfigCommandModule last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:364\nmsgid \"\"\n\"The number of times per interval that the ``AdHocConfigCommandModule`` \"\n\"command has been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:364\nmsgid \"\"\n\"``pubsub/AdHocConfigCommandModule last hour`` \"\n\"``pubsub/AdHocConfigCommandModule last minute`` \"\n\"``pubsub/AdHocConfigCommandModule last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:366\nmsgid \"AdHocConfigCommandModule/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:366\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``AdHocConfigCommandModule`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:366\nmsgid \"``pubsub/AdHocConfigCommandModule/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:368\nmsgid \"Affiliations count (in cache)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:368\nmsgid \"\"\n\"The total number of pubsub affiliations that are resident in cache \"\n\"memory. Affiliations include JIDs that are one of the following; Owner, \"\n\"Publisher, Publish-Only, Member, None, Outcast. This may not reflect \"\n\"total pubsub affiliations in repository.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:368\nmsgid \"``pubsub/Affiliations count (in cache)``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:370\nmsgid \"Average DB write time [ms]\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:370\nmsgid \"\"\n\"The average time of all DB writes from PubSub component. Average is \"\n\"calculated using two other statistics: (Total writing time / Database \"\n\"writes)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:370\nmsgid \"``pubsub/Average DB write time [ms]``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:372\nmsgid \"cache/hits last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:372\nmsgid \"\"\n\"The number of times the cache has achieved a hit within the last \"\n\"interval. A hit is when a request for information is matched to data that\"\n\" is inside the cache memory.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:372\nmsgid \"\"\n\"``pubsub/cache/hits last hour`` ``pubsub/cache/hits last minute`` \"\n\"``pubsub/cache/hits last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:374\nmsgid \"cache/hit-miss ratio per {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:374\nmsgid \"\"\n\"The ratio of cache hits to cache misses over the specified period. A \"\n\"cache hit is when a request for information from the cache is matched \"\n\"with information in the cache. A miss is when that information request \"\n\"cannot find a match in cache. A miss only indicates that that information\"\n\" was not found in the cache, not that it is not in the repository.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:374\nmsgid \"hour minute\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:374\nmsgid \"\"\n\"``pubsub/cache/hit-miss ratio per hour`` ``pubsub/cache/hit-miss ratio \"\n\"per minute``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:376\nmsgid \"cache/requests last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:376\nmsgid \"The number of memory cache requests made within the last interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:376\nmsgid \"\"\n\"``pubsub/cache/Requests last hour`` ``pubsub/cache/Requests last minute``\"\n\" ``pubsub/cache/Requests last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:378\nmsgid \"Cached nodes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:378\nmsgid \"The number of nodes that is currently in memory cache.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:378\nmsgid \"``pubsub/Cached nodes``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:380\nmsgid \"CapsModule\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:380\nmsgid \"\"\n\"The number of times per interval that the CapsModule command has been \"\n\"executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:380\nmsgid \"\"\n\"``pubsub/CapsModule last hour`` ``pubsub/CapsModule last minute`` \"\n\"``pubsub/CapsModule last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:382\nmsgid \"CapsModule/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:382\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``CapsModule`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:382\nmsgid \"``pubsub/CapsModule/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:384\nmsgid \"db/GetNodeItems requests last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:384\nmsgid \"\"\n\"The number of times ``GetNodeItems`` command has been run within the \"\n\"specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:384\nmsgid \"\"\n\"``pubsub/db/GetNodeItems last hour`` ``pubsub/db/GetNodeItems last \"\n\"minute`` ``pubsub/db/GetNodeItems last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:386\nmsgid \"db/GetNodeItems/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:386\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``GetNodeItems`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:386\nmsgid \"``pubsub/db/GetNodeItems/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:388\nmsgid \"DefaultConfigModule last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:388\nmsgid \"\"\n\"The number of times per interval that the ``DefaultConfigModule`` command\"\n\" has been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:388\nmsgid \"\"\n\"``pubsub/DefaultConfigModule last hour`` ``pubsub/DefaultConfigModule \"\n\"last minute`` ``pubsub/DefaultConfigModule last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:390\nmsgid \"DefaultConfigModule/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:390\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``DefaultConfigModule`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:390\nmsgid \"``pubsub/DefaultConfigModule/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:392\nmsgid \"DiscoverInfoModule last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:392\nmsgid \"\"\n\"The number of times per interval that the DiscoverInfoModule command has \"\n\"been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:392\nmsgid \"\"\n\"``pubsub/DiscoverInfoModule last hour`` ``pubsub/DiscoverInfoModule last \"\n\"minute`` ``pubsub/DiscoverInfoModule last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:394\nmsgid \"DiscoverInfoModule/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:394\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``DiscoverInfoModule`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:394\nmsgid \"``pubsub/DiscoverInfoModule/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:396\nmsgid \"DiscoverItemsModule last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:396\nmsgid \"\"\n\"The number of times per interval that the DiscoverItemsModule command has\"\n\" been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:396\nmsgid \"\"\n\"``pubsub/DiscoverItemsModule last hour`` ``pubsub/DiscoverItemsModule \"\n\"last minute`` ``pubsub/DiscoverItemsModule last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:398\nmsgid \"DiscoverItemsModule/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:398\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``DiscoverItemsModule`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:398\nmsgid \"``pubsub/DiscoverItemsModule/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:400\nmsgid \"JabberVersionModule last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:400\nmsgid \"\"\n\"The number of times per interval that the ``JabberVersionModule`` command\"\n\" has been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:400\nmsgid \"\"\n\"``pubsub/JabberVersionModule last hour`` ``pubsub/JabberVersionModule \"\n\"last minute`` ``pubsub/JabberVersionModule last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:402\nmsgid \"JabberVersionModule/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:402\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``JabberVersionModule`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:402\nmsgid \"``pubsub/JabberVersionModule/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:404\nmsgid \"ManageAffiiationsModule last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:404\nmsgid \"\"\n\"The number of times per interval that the ``ManageAffiliationsModule`` \"\n\"command has been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:404\nmsgid \"\"\n\"``pubsub/ManageAffiliationsModule last hour`` \"\n\"``pubsub/ManageAffiliationsModule last minute`` \"\n\"``pubsub/ManageAffiliationsModule last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:406\nmsgid \"ManageAffiliationsModule/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:406\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``ManageAffiliationsModule`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:406\nmsgid \"``pubsub/ManageAffiliationsModule/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:408\nmsgid \"ManageSubscriptionModule last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:408\nmsgid \"\"\n\"The number of times per interval that the ``ManageSubscriptionModule`` \"\n\"command has been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:408\nmsgid \"\"\n\"``pubsub/ManageSubscriptionModule last hour`` \"\n\"``pubsub/ManageSubscriptionModule last minute`` \"\n\"``pubsub/ManageSubscriptionModule last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:410\nmsgid \"ManageSubscriptionModule/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:410\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``ManageSubscriptionModule`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:410\nmsgid \"``pubsub/ManageSubscriptionModule/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:412\nmsgid \"NodeConfigModule last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:412\nmsgid \"\"\n\"The number of times per interval that the ``NodeConfigModule`` command \"\n\"has been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:412\nmsgid \"\"\n\"``pubsub/NodeConfigModule last hour`` ``pubsub/NodeConfigModule last \"\n\"minute`` ``pubsub/NodeConfigModule last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:414\nmsgid \"NodeConfigModule/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:414\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``NodeConfigModule`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:414\nmsgid \"``pubsub/NodeConfigModule/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:416\nmsgid \"NodeCreateModule last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:416\nmsgid \"\"\n\"The number of times per interval that the ``NodeCreateModule`` command \"\n\"has been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:416\nmsgid \"\"\n\"``pubsub/NodeCreateModule last hour`` ``pubsub/NodeCreateModule last \"\n\"minute`` ``pubsub/NodeCreateModule last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:418\nmsgid \"NodeCreateModule/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:418\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``NodeCreateModule`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:418\nmsgid \"``pubsub/NodeCreateModule/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:420\nmsgid \"NodeDeleteModule last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:420\nmsgid \"\"\n\"The number of times per interval that the ``NodeDeleteModule`` command \"\n\"has been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:420\nmsgid \"\"\n\"``pubsub/NodeDeleteModule last hour`` ``pubsub/NodeDeleteModule last \"\n\"minute`` ``pubsub/NodeDeleteModule last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:422\nmsgid \"NodeDeleteModule/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:422\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``NodeDeleteModule`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:422\nmsgid \"``pubsub/NodeDeleteModule/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:424\nmsgid \"PresenceCollectorModule last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:424\nmsgid \"\"\n\"The number of times per interval that the ``PresenceCollectorModule`` \"\n\"command has been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:424\nmsgid \"\"\n\"``pubsub/PresenceCollectorModule last hour`` \"\n\"``pubsub/PresenceCollectorModule last minute`` \"\n\"``pubsub/PresenceCollectorModule last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:426\nmsgid \"PresenceCollectorModule/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:426\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``PresenceCollectorModule`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:426\nmsgid \"``pubsub/PresenceCollectorModule/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:428\nmsgid \"PendingSubscriptionModule last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:428\nmsgid \"\"\n\"The number of times per interval that the ``PendingSubscriptionModule`` \"\n\"command has been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:428\nmsgid \"\"\n\"``pubsub/PendingSubscriptionModule last hour`` \"\n\"``pubsub/PendingSubscriptionModule last minute`` \"\n\"``pubsub/PendingSubscriptionModule last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:430\nmsgid \"PendingSubscriptionModule/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:430\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``PendingSubscriptionModule`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:430\nmsgid \"``pubsub/PendingSubscriptionModule/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:432\nmsgid \"PresenceNotifierModule last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:432\nmsgid \"\"\n\"The number of times per interval that the ``PresenceNotifierModule`` \"\n\"command has been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:432\nmsgid \"\"\n\"``pubsub/PresenceNotifierModule last hour`` \"\n\"``pubsub/PresenceNotifierModule last minute`` \"\n\"``pubsub/PresenceNotifierModule last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:434\nmsgid \"PresenceNotifierModule/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:434\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``PresenceNotifierModule`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:434\nmsgid \"``pubsub/PresenceNotifierModule/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:436\nmsgid \"PublishItemModule last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:436\nmsgid \"\"\n\"The number of times per interval that the ``PublishItemModule`` command \"\n\"has been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:436\nmsgid \"\"\n\"``pubsub/PublishItemModule last hour`` ``pubsub/PublishItemModule last \"\n\"minute`` ``pubsub/PublishItemModule last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:438\nmsgid \"PublishItemModule/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:438\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``PublishItemModule`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:438\nmsgid \"``pubsub/PublishItemModule/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:440\nmsgid \"PurgeItemsModule last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:440\nmsgid \"\"\n\"The number of times per interval that the ``PurgeItemsModule`` command \"\n\"has been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:440\nmsgid \"\"\n\"``pubsub/PurgeItemsModule last hour`` ``pubsub/PurgeItemsModule last \"\n\"minute`` ``pubsub/PurgeItemsModule last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:442\nmsgid \"PurgeItemsModule/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:442\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``PurgeItemsModule`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:442\nmsgid \"``pubsub/PurgeItemsModule/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:444\nmsgid \"Repository writes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:444\nmsgid \"\"\n\"Number of individual writes to Repository from the pubsub component since\"\n\" startup.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:444\nmsgid \"``pubsub/Repository writes``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:446\nmsgid \"RetractItemModule last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:446\nmsgid \"\"\n\"The number of times per interval that the ``RetractItemModule`` command \"\n\"has been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:446\nmsgid \"\"\n\"``pubsub/RetractItemModule last hour`` ``pubsub/RetractItemModule last \"\n\"minute`` ``pubsub/RetractItemModule last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:448\nmsgid \"RetractItemModule/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:448\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``RetractItemModule`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:448\nmsgid \"``pubsub/RetractItemModule/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:450\nmsgid \"RetrieveAffiliationsModule last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:450\nmsgid \"\"\n\"The number of times per interval that the ``RetrieveAffiliationsModule`` \"\n\"command has been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:450\nmsgid \"\"\n\"``pubsub/RetrieveAffiliationsModule last hour`` \"\n\"``pubsub/RetrieveAffiliationsModule last minute`` \"\n\"``pubsub/RetrieveAffiliationsModule last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:452\nmsgid \"RetrieveAffiliationsModule/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:452\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``RetrieveAffiliationsModule`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:452\nmsgid \"``pubsub/RetrieveAffiliationsModule/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:454\nmsgid \"RetrieveItemsModule last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:454\nmsgid \"\"\n\"The number of times per interval that the ``RetrieveItemsModule`` command\"\n\" has been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:454\nmsgid \"\"\n\"``pubsub/RetrieveItemsModule last hour`` ``pubsub/RetrieveItemsModule \"\n\"last minute`` ``pubsub/RetrieveItemsModule last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:456\nmsgid \"RetrieveItemsModule/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:456\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``RetrieveItemsModule`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:456\nmsgid \"``pubsub/RetrieveItemsModule/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:458\nmsgid \"RetrieveSubscriptionsModule last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:458\nmsgid \"\"\n\"The number of times per interval that the ``RetrieveSubscriptionsModule``\"\n\" command has been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:458\nmsgid \"\"\n\"``pubsub/RetrieveSubscriptionsModule last hour`` \"\n\"``pubsub/RetrieveSubscriptionsModule last minute`` \"\n\"``pubsub/RetrieveSubscriptionsModule last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:460\nmsgid \"RetrieveSubscriptionsModule/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:460\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``RetrieveSubscriptionsModule`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:460\nmsgid \"``pubsub/RetrieveSubscriptionsModule/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:462\nmsgid \"SubscribeNodeModule last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:462\nmsgid \"\"\n\"The number of times per interval that the ``SubscribeNodeModule`` command\"\n\" has been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:462\nmsgid \"\"\n\"``pubsub/SubscribeNodeModule last hour`` ``pubsub/SubscribeNodeModule \"\n\"last minute`` ``pubsub/SubscribeNodeModule last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:464\nmsgid \"SubscribeNodeModule/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:464\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``SubscribeNodeModule`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:464\nmsgid \"``pubsub/SubscribeNodeModule/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:466\nmsgid \"Subscription count (in cache)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:466\nmsgid \"\"\n\"The total number of pubsub subscriptions that are resident in cache \"\n\"memory. This may not reflect total pubsub subscriptions in repository.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:466\nmsgid \"``pubsub/Subscription count (in cache)``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:468\nmsgid \"Total writing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:468\nmsgid \"\"\n\"The cumulative total of time pubsub component has written to the database\"\n\" expressed in milliseconds.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:468\nmsgid \"String (###ms)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:468\nmsgid \"``pubsub/Total writing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:470\nmsgid \"UnsubscribeNodeModule last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:470\nmsgid \"\"\n\"The number of times per interval that the ``UnsubscribeNodeModule`` \"\n\"command has been executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:470\nmsgid \"\"\n\"``pubsub/UnsubscribeNodeModule last hour`` ``pubsub/UnsubscribeNodeModule\"\n\" last minute`` ``pubsub/UnsubscribeNodeModule last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:472\nmsgid \"UnsubscribeNodeModule/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:472\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``UnsubscribeNodeModule`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:472\nmsgid \"``pubsub/UnsubscribeNodeModule/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:474\nmsgid \"Update subscription calls\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:474\nmsgid \"\"\n\"Number of times Subscriptions have been updated (this includes new, \"\n\"deleted, and edited).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:474\nmsgid \"``pubsub/Update subscriptions calls``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:476\nmsgid \"XmppPingModule last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:476\nmsgid \"\"\n\"The number of times per interval that the XmppPingModule command has been\"\n\" executed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:476\nmsgid \"\"\n\"``pubsub/XmppPingModule last hour`` ``pubsub/XmppPingModule last minute``\"\n\" ``pubsub/XmppPingModule last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:478\nmsgid \"XmppPingModule/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:478\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``XmppPingModule`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:478\nmsgid \"``pubsub/XmppPingModule/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:482\nmsgid \"repo-factory\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:487\nmsgid \"Number of data repositories\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:487\nmsgid \"The number of data repositories setup for this XMPP server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:487\nmsgid \"``repo-factory/Number of data repositories``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:489\nmsgid \"Repository {jdbclocation} connections count\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:489\nmsgid \"The number of connections made to this database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:489\nmsgid \"``repo-factory/repository {jdbclocation} connections count``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:491\nmsgid \"repository {jdbclocation} reconnections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:491\nmsgid \"The number of reconnections made to this database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:491\nmsgid \"``repo-factory/repository {jdbclocation} reconnections``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:493\nmsgid \"repository {jdbclocation} failed reconnections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:493\nmsgid \"The number of reconnections that have failed to connect to this database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:493\nmsgid \"``repo-factory/repository {jdbclocation} failed reconnections``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:497\nmsgid \"rest\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:499\nmsgid \"No exclusive rest specific statistics\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:503\nmsgid \"s2s\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:508\nmsgid \"CIDs number\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:508\nmsgid \"\"\n\"ConnectionID for the server. This may include multiple CIDs if server is \"\n\"running multiple vhosts.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:508\nmsgid \"``s2s/CIDs number``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:510\nmsgid \"get-cid-connection last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:510\nmsgid \"\"\n\"The number of times get-cid-connection command has been executed within \"\n\"the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:510\nmsgid \"\"\n\"``s2s/adhoc-command/get-cid-connection last hour`` ``s2s/adhoc-command\"\n\"/get-cid-connection last minute`` ``s2s/adhoc-command/get-cid-connection \"\n\"last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:512\nmsgid \"get-cid-connection/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:512\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``get-cid-\"\n\"connection`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:512\nmsgid \"``s2s/adhoc-command/get-cid-connection/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:514\nmsgid \"s2s-bad-state-conns last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:514\nmsgid \"\"\n\"The number of times s2s-bad-state-conns command has been executed within \"\n\"the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:514\nmsgid \"\"\n\"``s2s/adhoc-command/s2s-bad-state-conns last hour`` ``s2s/adhoc-command\"\n\"/s2s-bad-state-conns last minute`` ``s2s/adhoc-command/s2s-bad-state-\"\n\"conns last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:516\nmsgid \"s2s-bad-state-conns/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:516\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``s2s-bad-\"\n\"state-conns`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:516\nmsgid \"``s2s/adhoc-command/s2s-bad-state-conns/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:518\nmsgid \"reset-bad-state-conns last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:518\nmsgid \"\"\n\"The number of times reset-bad-state-conns command has been executed \"\n\"within the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:518\nmsgid \"\"\n\"``s2s/adhoc-command/reset-bad-state-conns last hour`` ``s2s/adhoc-command\"\n\"/reset-bad-state-conns last minute`` ``s2s/adhoc-command/reset-bad-state-\"\n\"conns last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:520\nmsgid \"reset-bad-state-conns/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:520\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``reset-bad-\"\n\"state-conns`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:520\nmsgid \"``s2s/adhoc-command/reset-bad-state-conns/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:522\nmsgid \"Total DB keys\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:522\nmsgid \"Total number of database keys.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:522\nmsgid \"``s2s/Total DB keys``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:524\nmsgid \"Total {incoming/outgoing}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:524\nmsgid \"\"\n\"The total number of server-to-server connections, outgoing is local \"\n\"server connecting to other servers, and incoming is connections from \"\n\"other servers. The results may or may not be the same.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:524\nmsgid \"``s2s/Total incoming`` ``s2s/Total outgoing``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:526\nmsgid \"Total {incoming/outgoing} TLS\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:526\nmsgid \"\"\n\"The total number of server-to-server connections using TLS, outgoing is \"\n\"local server connecting to other servers, and incoming is connections \"\n\"from other servers. The results may or may not be the same.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:526\nmsgid \"``s2s/Total incoming TLS`` ``s2s/Total outgoing TLS``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:528\nmsgid \"Total outgoing handshaking\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:528\nmsgid \"\"\n\"Total number of outgoing connections that are currently handshaking to \"\n\"other servers.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:528\nmsgid \"``s2s/Total outgoing handshaking``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:530\nmsgid \"Total control waiting\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:530\nmsgid \"Total number of connections that were manually told to wait.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:530\nmsgid \"``s2s/Total control waiting``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:532\nmsgid \"Total waiting\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:532\nmsgid \"\"\n\"Total number of connections that are currently waiting for response from \"\n\"other server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:532\nmsgid \"``s2s/Total waiting``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:536\nmsgid \"sess-man\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:541\nmsgid \"Active user connections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:541\nmsgid \"\"\n\"Number of user connections that are considered active. An active user is \"\n\"a user that has sent stanzas to the server or through the server within \"\n\"the last 5 minutes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:541\nmsgid \"``sess-man/Active user connections``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:543\nmsgid \"adhoc-command/connection-time last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:543\nmsgid \"\"\n\"The number of times ``connection-time`` command has been executed within \"\n\"the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:543\nmsgid \"\"\n\"``sess-man/adhoc-command/connection-time last hour`` ``sess-man/adhoc-\"\n\"command/connection-time last minute`` ``sess-man/adhoc-command\"\n\"/connection-time last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:545\nmsgid \"adhoc-command/connection-time/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:545\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``connection-time`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:545\nmsgid \"``sess-man/adhoc-command/connection-time/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:547\nmsgid \"adhoc-command/http://jabber.org/protocol/admin#add-user last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:547\nmsgid \"\"\n\"The number of times ``admin#add-user`` command has been executed within \"\n\"the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:547\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-user last \"\n\"hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-user\"\n\" last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin\"\n\"#add-user last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:549\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#add-user/Average \"\n\"processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:549\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin#add-\"\n\"user`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:549\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-\"\n\"user/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:551\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#add-user-tracker last \"\n\"{interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:551\nmsgid \"\"\n\"The number of times ``admin#add-user-tracker`` command has been executed \"\n\"within the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:551\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-user-\"\n\"tracker last hour`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#add-user-tracker last minute`` \"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-user-\"\n\"tracker last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:553\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#add-user-tracker/Average \"\n\"processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:553\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin#add-\"\n\"user-tracker`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:553\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-user-\"\n\"tracker/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:555\nmsgid \"adhoc-command/http://jabber.org/protocol/admin#announce last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:555\nmsgid \"\"\n\"The number of times ``admin#announce`` command has been executed within \"\n\"the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:555\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#announce last \"\n\"hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#announce\"\n\" last minute`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#announce last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:557\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#announce/Average \"\n\"processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:557\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``admin#announce`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:557\nmsgid \"\"\n\"``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#announce/Average processing \"\n\"time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:559\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#change-user-password last \"\n\"{interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:559\nmsgid \"\"\n\"The number of times ``admin#change-user-password`` command has been \"\n\"executed within the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:559\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#change-user-\"\n\"password last hour`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#change-user-password last \"\n\"minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin\"\n\"#change-user-password last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:561\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#change-user-\"\n\"password/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:561\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin\"\n\"#change-user-password`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:561\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#change-user-\"\n\"password/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:563\nmsgid \"adhoc-command/http://jabber.org/protocol/admin#delete-user last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:563\nmsgid \"\"\n\"The number of times ``admin#delete-user`` command has been executed \"\n\"within the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:563\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#delete-user \"\n\"last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin\"\n\"#delete-user last minute`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#delete-user last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:565\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#delete-user/Average \"\n\"processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:565\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin\"\n\"#delete-user`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:565\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#delete-\"\n\"user/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:567\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#end-user-session last \"\n\"{interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:567\nmsgid \"\"\n\"The number of times ``admin#end-user-session`` command has been executed \"\n\"within the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:567\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#end-user-\"\n\"session last hour`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#end-user-session last minute`` \"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#end-user-\"\n\"session last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:569\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#end-user-session/Average \"\n\"processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:569\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin#end-\"\n\"user-session`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:569\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#end-user-\"\n\"session/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:571\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-active-users last \"\n\"{interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:571\nmsgid \"\"\n\"The number of times ``admin#get-active-users`` command has been executed \"\n\"within the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:571\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-active-\"\n\"users last hour`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#get-active-users last minute`` \"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-active-\"\n\"users last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:573\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-active-users/Average \"\n\"processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:573\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin#get-\"\n\"active-users`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:573\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-active-\"\n\"users/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:575\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-active-user-num last \"\n\"{interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:575\nmsgid \"\"\n\"The number of times ``admin#get-active-user-num`` command has been \"\n\"executed within the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:575\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-active-\"\n\"user-num last hour`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#get-active-user-num last \"\n\"minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-\"\n\"active-user-num last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:577\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-active-user-\"\n\"num/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:577\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin#get-\"\n\"active-user-num`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:577\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-active-\"\n\"user-num/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:579\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-idle-users last \"\n\"{interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:579\nmsgid \"\"\n\"The number of times ``admin#get-idle-users`` command has been executed \"\n\"within the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:579\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-idle-users \"\n\"last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin\"\n\"#get-idle-users last minute`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#get-idle-users last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:581\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-idle-users/Average \"\n\"processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:581\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin#get-\"\n\"idle-users`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:581\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-idle-\"\n\"users/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:583\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-idle-users-num last \"\n\"{interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:583\nmsgid \"\"\n\"The number of times ``admin#get-idle-users-num`` command has been \"\n\"executed within the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:583\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-idle-users-\"\n\"num last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin\"\n\"#get-idle-users-num last minute`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#get-idle-users-num last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:585\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-idle-users-num/Average\"\n\" processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:585\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin#get-\"\n\"idle-users-num`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:585\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-idle-users-\"\n\"num/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:587\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-online-users-list last\"\n\" {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:587\nmsgid \"\"\n\"The number of times ``admin#get-online-users-list`` command has been \"\n\"executed within the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:587\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-online-\"\n\"users-list last hour`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#get-online-users-list last \"\n\"minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-\"\n\"online-users-list last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:589\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-online-users-\"\n\"list/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:589\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin#get-\"\n\"online-users-list`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:589\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-online-\"\n\"users-list/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:591\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-top-active-users last \"\n\"{interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:591\nmsgid \"\"\n\"The number of times ``admin#get-top-active-users`` command has been \"\n\"executed within the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:591\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-top-active-\"\n\"users last hour`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#get-top-active-users last \"\n\"minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-\"\n\"top-active-users last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:593\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-top-active-\"\n\"users/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:593\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin#get-\"\n\"top-active-users`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:593\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-top-active-\"\n\"users/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:595\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-registered-users-list \"\n\"last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:595\nmsgid \"\"\n\"The number of times ``admin#get-registered-users-list`` command has been \"\n\"executed within the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:595\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-registered-\"\n\"users-list last hour`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#get-registered-users-list last \"\n\"minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-\"\n\"registered-users-list last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:597\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-registered-users-\"\n\"list/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:597\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin#get-\"\n\"registered-users-list`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:597\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-registered-\"\n\"users-list/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:599\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-user-roster last \"\n\"{interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:599\nmsgid \"\"\n\"The number of times ``admin#get-user-roster`` command has been executed \"\n\"within the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:599\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-user-roster\"\n\" last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin\"\n\"#get-user-roster last minute`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#get-user-roster last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:601\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-user-roster/Average \"\n\"processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:601\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin#get-\"\n\"user-roster`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:601\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-user-\"\n\"roster/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:603\nmsgid \"adhoc-command/http://jabber.org/protocol/admin#remove-user last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:603\nmsgid \"\"\n\"The number of times ``admin#remove-user`` command has been executed \"\n\"within the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:603\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#remove-user \"\n\"last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin\"\n\"#remove-user last minute`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#remove-user last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:605\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#remove-user/Average \"\n\"processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:605\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin\"\n\"#remove-user`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:605\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#remove-\"\n\"user/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:607\nmsgid \"adhoc-command/http://jabber.org/protocol/admin#user-stats last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:607\nmsgid \"\"\n\"The number of times ``admin#user-stats`` command has been executed within\"\n\" the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:607\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#user-stats last\"\n\" hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#user-\"\n\"stats last minute`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#user-stats last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:609\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#user-stats/Average \"\n\"processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:609\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin\"\n\"#user-stats`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:609\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#user-\"\n\"stats/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:611\nmsgid \"adhoc-command/get-user-info last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:611\nmsgid \"\"\n\"The number of times ``get-user-info command`` has been executed within \"\n\"the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:611\nmsgid \"\"\n\"``sess-man/adhoc-command/get-user-info last hour`` ``sess-man/adhoc-\"\n\"command/get-user-info last minute`` ``sess-man/adhoc-command/get-user-\"\n\"info last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:613\nmsgid \"adhoc-command/get-user-info/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:613\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``get-user-\"\n\"info`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:613\nmsgid \"``sess-man/adhoc-command/get-user-info/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:615\nmsgid \"adhoc-command/modify-user last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:615\nmsgid \"\"\n\"The number of times ``modify-user`` command has been executed within the \"\n\"specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:615\nmsgid \"\"\n\"``sess-man/adhoc-command/modify-user last hour`` ``sess-man/adhoc-command\"\n\"/modify-user last minute`` ``sess-man/adhoc-command/modify-user last \"\n\"second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:617\nmsgid \"adhoc-command/modify-user/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:617\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``modify-\"\n\"user`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:617\nmsgid \"``sess-man/adhoc-command/modify-user/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:619\nmsgid \"adhoc-command/oauth-credentials last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:619\nmsgid \"\"\n\"The number of times ``oauth-credentials`` command has been executed \"\n\"within the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:619\nmsgid \"\"\n\"``sess-man/adhoc-command/oauth-credentials last hour`` ``sess-man/adhoc-\"\n\"command/oauth-credentials last minute`` ``sess-man/adhoc-command/oauth-\"\n\"credentials last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:621\nmsgid \"adhoc-command/oauth-credentials/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:621\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``oauth-\"\n\"credentials`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:621\nmsgid \"``sess-man/adhoc-command/oauth-credentials/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:623\nmsgid \"adhoc-command/roster-fixer last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:623\nmsgid \"\"\n\"The number of times ``roster-fixer`` command has been executed within the\"\n\" specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:623\nmsgid \"\"\n\"``sess-man/adhoc-command/roster-fixer last hour`` ``sess-man/adhoc-\"\n\"command/roster-fixer last minute`` ``sess-man/adhoc-command/roster-fixer \"\n\"last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:625\nmsgid \"adhoc-command/roster-fixer/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:625\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``roster-\"\n\"fixer`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:625\nmsgid \"``sess-man/adhoc-command/roster-fixer/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:627\nmsgid \"adhoc-command/roster-fixer-cluster last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:627\nmsgid \"\"\n\"The number of times ``roster-fixer-cluster`` command has been executed \"\n\"within the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:627\nmsgid \"\"\n\"``sess-man/adhoc-command/roster-fixer-cluster last hour`` ``sess-man\"\n\"/adhoc-command/roster-fixer-cluster last minute`` ``sess-man/adhoc-\"\n\"command/roster-fixer-cluster last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:629\nmsgid \"adhoc-command/roster-fixer-cluster/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:629\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``roster-\"\n\"fixer-cluster`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:629\nmsgid \"``sess-man/adhoc-command/roster-fixer-cluster/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:631\nmsgid \"adhoc-command/user-domain-perm last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:631\nmsgid \"\"\n\"The number of times user-domain-perm command has been executed within the\"\n\" specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:631\nmsgid \"\"\n\"``sess-man/adhoc-command/user-domain-perm last hour`` ``sess-man/adhoc-\"\n\"command/user-domain-perm last minute`` ``sess-man/adhoc-command/user-\"\n\"domain-perm last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:633\nmsgid \"adhoc-command/user-domain-perm/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:633\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``user-\"\n\"domain-perm`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:633\nmsgid \"``sess-man/adhoc-command/user-domain-perm/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:635\nmsgid \"adhoc-command/user-roster-management last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:635\nmsgid \"\"\n\"The number of times ``user-roster-management`` command has been executed \"\n\"within the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:635\nmsgid \"\"\n\"``sess-man/adhoc-command/user-roster-management last hour`` ``sess-man\"\n\"/adhoc-command/user-roster-management last minute`` ``sess-man/adhoc-\"\n\"command/user-roster-management last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:637\nmsgid \"adhoc-command/user-roster-management/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:637\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``user-\"\n\"roster-management`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:637\nmsgid \"``sess-man/adhoc-command/user-roster-management/Average processing time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:639\nmsgid \"adhoc-command/user-roster-management-ext last {interval}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:639\nmsgid \"\"\n\"The number of times ``user-roster-management-ext`` command has been \"\n\"executed within the specified interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:639\nmsgid \"\"\n\"``sess-man/adhoc-command/user-roster-management-ext last hour`` ``sess-\"\n\"man/adhoc-command/user-roster-management-ext last minute`` ``sess-man\"\n\"/adhoc-command/user-roster-management-ext last second``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:641\nmsgid \"adhoc-command/user-roster-management-ext/Average processing time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:641\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``user-\"\n\"roster-management-ext`` to execute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:641\nmsgid \"\"\n\"``sess-man/adhoc-command/user-roster-management-ext/Average processing \"\n\"time``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:643\nmsgid \"Authentication timeouts\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:643\nmsgid \"\"\n\"The number of connections that have timed out during the authentication \"\n\"process. Default timeout is 2 minutes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:643\nmsgid \"``sess-man/Authentication timeouts``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:645\nmsgid \"Closed user connections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:645\nmsgid \"\"\n\"User connections that have been terminated by the user (as opposed to the\"\n\" server).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:645\nmsgid \"``sess-man/Closed user connections``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:647\nmsgid \"default-handler/Invalid registrations\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:647\nmsgid \"Number of invalid registrations attempted with the server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:647\nmsgid \"``sess-man/default-handler/Invalid registrations``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:649\nmsgid \"default-handler/Registered users\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:649\nmsgid \"Number of registered users for this server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:649\nmsgid \"``sess-man/default-handler/Registered users``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:651\nmsgid \"Maximum user connections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:651\nmsgid \"\"\n\"Maximum number of connections that have been made during server instance,\"\n\" this number includes users connecting multiple times.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:651\nmsgid \"``sess-man/Maximum user connections``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:653\nmsgid \"Maximum user sessions {today/yesterday}\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:653\nmsgid \"\"\n\"The number of most simultaneous sessions within the specified interval. \"\n\"Today = previous 24 hours, Yesterday = 24 hours after previous 24 hours \"\n\"(does not go by calendar date).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:653\nmsgid \"\"\n\"``sess-man/Maximum user sessions today`` ``sess-man/Maximum user sessions\"\n\" yesterday``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:655\nmsgid \"Registered accounts\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:655\nmsgid \"Sum total of registered accounts for the server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:655\nmsgid \"``sess-man/Registered accounts``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:657\nmsgid \"Open user connections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:657\nmsgid \"\"\n\"The current number of open user connections. This may be interpreted as \"\n\"number of connections from users, however a user can have more than one \"\n\"connection (connection from mobile and PC for example).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:657\nmsgid \"``sess-man/Open user connections``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:659\nmsgid \"Open user sessions\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:659\nmsgid \"The current number of open user sessions.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:659\nmsgid \"``sess-man/Open user sessions``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:661\nmsgid \"Total user connections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:661\nmsgid \"\"\n\"The cumulative number of connections that have been made to the server \"\n\"during the current instance.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:661\nmsgid \"``sess-man/Total user connections``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:663\nmsgid \"Total user sessions\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:663\nmsgid \"\"\n\"The cumulative number of sessions that this server has negotiated during \"\n\"the current instance.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:663\nmsgid \"``sess-man/Total user sessions``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:665\nmsgid \"presence/Users status changes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:665\nmsgid \"\"\n\"The number of presence changes for all users that have been conducted \"\n\"during the server instance.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:665\nmsgid \"\"\n\"``sess-man/presence/Users status changes`` ``sess-man/presence-\"\n\"state/Users status changes``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:667\nmsgid \"sess-man/Processor\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:667\nmsgid \"\"\n\"Processor statistics will result in a field of labels and values \"\n\"exclusive to that processor.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:667\nmsgid \"FIELD\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:667\nmsgid \"\"\n\"``sess-man/Processor: message carbons`` ``sess-man/Processor: \"\n\"http://jabber.org/protocol/stats`` ``sess-man/Processor: jabber:iq:auth``\"\n\" ``sess-man/Processor: vcard-temp`` ``sess-man/Processor: amp`` ``sess-\"\n\"man/Processor: presence-subscription`` ``sess-man/Processor: disco`` \"\n\"``sess-man/Processor: msgoffline`` ``sess-man/Processor: \"\n\"urn:xmpp:blocking`` ``sess-man/Processor: urn:xmpp:ping`` ``sess-\"\n\"man/Processor: jabber:iq:register`` ``sess-man/Processor: \"\n\"urn:ietf:params:xml:ns:xmpp-sasl`` ``sess-man/Processor: prp`` ``sess-\"\n\"man/Processor: presence`` ``sess-man/Processor: message-archive-\"\n\"xep-0136`` ``sess-man/Processor: default-handler`` ``sess-man/Processor: \"\n\"jabber:iq:roster`` ``sess-man/Processor: starttls`` ``sess-man/Processor:\"\n\" presence-state`` ``sess-man/Processor: jabber:iq:version`` ``sess-\"\n\"man/Processor: urn:xmpp:time`` ``sess-man/Processor: session-open`` \"\n\"``sess-man/Processor: jabber:iq:privacy`` ``sess-man/Processor: \"\n\"urn:ietf:params:xml:ns:xmpp-bind`` ``sess-man/Processor: \"\n\"http://jabber.org/protocol/commands`` ``sess-man/Processor: vcard-\"\n\"xep0292`` ``sess-man/Processor: session-close`` ``sess-man/Processor: \"\n\"urn:ietf:params:xml:ns:xmpp-session`` ``sess-man/Processor: \"\n\"jabber:iq:private`` ``sess-man/Processor: Average amp on last 100 runs \"\n\"[ms]`` ``sess-man/Processor: Average msgoffline on last 100 runs[ms]``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"The field shows as follows:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"``, Queue: 0, AvTime: 0, Runs: 0, Lost: 0``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"Where:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"Queue: Number of packets in process queue\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"AvTime: Average time in ms processor takes to conduct it’s operation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"Runs: Number of times Processor has been run.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"Lost: Number of packets lost during processing.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:679\nmsgid \"vhost-man\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:684\nmsgid \"Checks is anonymous domain\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:684\nmsgid \"Number of anonymous domain checks that have been run within vhost-man.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:684\nmsgid \"``vhost-man/Checks is anonymous domain``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:686\nmsgid \"Checks: is local domain\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:686\nmsgid \"Number of local domain checks that have been run within vhost-man.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:686\nmsgid \"``vhost-man/Checks: is local domain``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:688\nmsgid \"Get components for local domain\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:688\nmsgid \"Number of components loaded within local domain.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:688\nmsgid \"``vhost-man/Get components for local domain``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:690\nmsgid \"Get components for non-local domain\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:690\nmsgid \"Number of components loaded outside local domain.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:690\nmsgid \"``vhost-man/Get components for non-local domain``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:692\nmsgid \"Number of Vhosts\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:692\nmsgid \"Number of configured and running Virtual Hosts.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:692\nmsgid \"``vhost-man/Number of VHosts``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:696\nmsgid \"ws2s\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:698\nmsgid \"No exclusive ws2s specific statistics.\"\nmsgstr \"\"\n\n#~ msgid \"\"\n#~ \"The number of errors counted during \"\n#~ \"the operation of the server for \"\n#~ \"this component. Will only be available\"\n#~ \" if `stream-error-counter <#stream-\"\n#~ \"error-counter>`__ is enabled in \"\n#~ \"config.tdsl, otherwise will be 0.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"The number of times watchdog identified\"\n#~ \" and closed a connection it has \"\n#~ \"found to be stale according to the\"\n#~ \" settings in ``config.tdsl`` or by \"\n#~ \"the defaults defined `in this section\"\n#~ \" <#watchdog>`__.\"\n#~ msgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Administration/Tigase_Server_Binary_Updates.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-05-27 12:30-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:2\nmsgid \"Tigase Server Binary Updates\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:4\nmsgid \"\"\n\"Most open source projects try to make sure that the nightly builds \"\n\"compile correctly so that these builds can be used. However, we at Tigase\"\n\" believe that these builds should be separated until they are thoroughly \"\n\"tested and released. Although lots of installations out there we know of \"\n\"just run from our nightly builds, this puts an extra responsibility to \"\n\"make sure all code is functional and will constantly work. Therefore, our\"\n\" general approach is to run all basic functionality tests before each \"\n\"code commit to make sure it works correctly. This does not guarantee that\"\n\" there will never be a problem, but it is a precaution from preventing \"\n\"bad builds from arriving in the hands of our customers.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:6\nmsgid \"\"\n\"Some users on the other hand, like to be on the bleeding edge and \"\n\"regularly use our nightly builds exploring new code changes and playing \"\n\"with new features before they are put to a full release. Others prefer to\"\n\" stick to stable and fully tested public releases. Others however, want \"\n\"something from the middle, the most recent features, but bug fixes, \"\n\"something like a beta or a release-candidate state.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:8\nmsgid \"\"\n\"Should you choose to use the nightly builds, a few things you should \"\n\"consider:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:10\nmsgid \"Changes may be made to the code that can negatively affect performance.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:12\nmsgid \"Changes may be made to the code that can negatively affect security.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:14\nmsgid \"\"\n\"We **highly** recommend testing these builds in your environments before \"\n\"upgrading.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:16\nmsgid \"\"\n\"With these considerations in mind, we provide nightly builds at `this \"\n\"link <https://build.tigase.net/nightlies/dists/>`__ which provides \"\n\"directories by date.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:18\nmsgid \"\"\n\"Standard naming format is ``tigase-\"\n\"server-<version>-SNAPSHOT-b<build>-<type>`` where ``<version>`` is in the\"\n\" form of ``major.minor.bugfix``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:22\nmsgid \"\"\n\"individual days may have the same builds as noted by the byyyy section of\"\n\" the file.\\\\*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:24\nmsgid \"\"\n\"Just like the standard distributions, the builds are available with the \"\n\"following extensions (``<type>``):\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:26\nmsgid \"``javadoc.jar`` - Java installer for javadoc only\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:28\nmsgid \"``dist.zip`` - Compressed binaries with no dependencies.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:30\nmsgid \"``dist.tar.gz`` - tarball compressed binaries with no dependencies.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:32\nmsgid \"``dist-max.zip`` - Compressed binaries with all dependencies.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:34\nmsgid \"``dist-max.tar.gz`` - tarball compressed binaries with all dependencies.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:36\nmsgid \"\"\n\"We also provide automated testing of each of our nightly builds for each \"\n\"supported databases. Tests are done with both functional and low memory \"\n\"parameters in mind, and are available `at this link \"\n\"<https://build.tigase.net/nightlies/tests/>`__. These tests can provide a\"\n\" quick examination of function before upgrading your current build.\"\nmsgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Administration/Tigase_User_Guide/User_Guide.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-05-27 12:30-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/User_Guide.rst:3\nmsgid \"Tigase User Guide\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Introduction.inc:2\nmsgid \"Jabber/XMPP introduction\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Introduction.inc:5\nmsgid \"Jabber/XMPP is Instant Messaging Technology\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Introduction.inc:7\nmsgid \"\"\n\"All federated **XMPP** servers are connected in one global communications\"\n\" network allowing you to send messages to friends who have accounts on \"\n\"other Jabber servers.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Introduction.inc:9\nmsgid \"\"\n\"This is very much like sending e-mail but the difference between Jabber \"\n\"and e-mail is the same as the difference between sending a traditional \"\n\"mail and talking on the phone.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Introduction.inc:11\nmsgid \"\"\n\"All messages sent through Jabber are sent instantly and you also receive \"\n\"responses instantly. More over you can see whether your mate is online \"\n\"and available for talking or not.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Introduction.inc:13\nmsgid \"\"\n\"There exists similar technologies to Jabber like WhatsApp Messenger, \"\n\"Facebook Messenger, Signal, Telegram, WeChat, QQ and other. There are, \"\n\"however, quite a few differences.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Introduction.inc:15\nmsgid \"\"\n\"XMPP is an open standard which means everybody can know how it works, \"\n\"everybody can implement their own software connecting to the network both\"\n\" client and server side.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Introduction.inc:17\nmsgid \"\"\n\"The server side is actually the biggest difference and advantage. Many \"\n\"companies have offices in different locations, and such instant messaging\"\n\" technology could be very useful to employees for communication. \"\n\"Companies are not inclined to allow confidential discussions to go \"\n\"outside the company’s network. Especially if it is not very secure to \"\n\"leave such information on third party public servers.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Introduction.inc:19\nmsgid \"\"\n\"XMPP servers on the other hand, allows you to deploy server software on \"\n\"your own company network. Employees can then talk securely and all \"\n\"information remains on the company’s secure network. Of course if offices\"\n\" are located in different locations or countries then all messages are \"\n\"transmitted over the public network - the Internet. This is not a problem\"\n\" since XMPP supports SSL/TLS - secure encrypted connections which helps \"\n\"you protect your discussion.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Introduction.inc:21\nmsgid \"\"\n\"Then if your employees need to contact customers outside your company, \"\n\"the whole discussion can go through your server and a server located on \"\n\"the customer side.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Introduction.inc:23\nmsgid \"\"\n\"There are many other scenarios and use cases but I hope this brief \"\n\"introduction gives you an idea of the differences and advantages of XMPP \"\n\"technology.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:2\nmsgid \"How to Use Tigase Service\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:6\nmsgid \"\"\n\"This Article Describes How to use **tigase.im** Service for Instant \"\n\"Communications\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:8\nmsgid \"\"\n\"You have to install and run a Jabber client application to use the \"\n\"service.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:10\nmsgid \"\"\n\"There are multiple domains available: tigase.im, sure.im, xmpp.cloud (and\"\n\" you can host your own domain as well)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:14\nmsgid \"Short instructions:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:16\nmsgid \"\"\n\"Usually you just need to enter the user name of the form: user@tigase.im.\"\n\" Your XMPP client should take care of all other things as our service \"\n\"doesn’t need any special settings. If you don’t have an account on \"\n\"tigase.im server yet just tick the option to register new account. That’s\"\n\" it!\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:20\nmsgid \"**Long Instructions:**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:22\nmsgid \"\"\n\"Good news is that there are many programs to choose from which allow you \"\n\"to communicate through our server. So you can pick up your favorite \"\n\"application or use an existing one that is compatible and start using our\"\n\" service.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:24\nmsgid \"\"\n\"All clients presented below support multiple accounts on Jabber servers. \"\n\"What this means is that you can have a few Jabber accounts on different \"\n\"Jabber servers and you can still use just one program to connect to all \"\n\"of them at the same time.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:26\nmsgid \"\"\n\"The `full list of all known XMPP clients \"\n\"<https://xmpp.org/software/clients.html>`__ is very long. You can \"\n\"obviously try them all but below is a selection which is recommended by \"\n\"the Tigase team. The selected programs might not be the best choice for \"\n\"you, but these programs have been tested and we can offer help with using\"\n\" them. Here is a list of recommended instant messaging clients:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:28\nmsgid \"\"\n\"`Beagle.im <https://beagle.im/>`__ - macOS desktop client developed by \"\n\"Tigase team supporting all the latest and greatest features\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:30\nmsgid \"\"\n\"`Tigase Messenger for iOS <https://itunes.apple.com/us/app/tigase-\"\n\"messenger/id1153516838>`__ - lightweight, powerful XMPP client developed \"\n\"by Tigase, Inc. It provides an easy way to start using the XMPP Protocol \"\n\"(formerly known as Jabber) if you’ve never used it before. Veterans of \"\n\"the protocol will find many features here they are familiar with along \"\n\"with enhancements to reduce data use and extend battery life.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:32\nmsgid \"\"\n\"`Tigase Messenger for Android \"\n\"<https://play.google.com/store/apps/details?id=org.tigase.messenger.phone.pro>`__\"\n\" - mobile chat client to use with XMPP services and servers. The totally \"\n\"revamped v3.0 now has new features, a better design, and Google \"\n\"integration. Application supports any XMPP server, from free services \"\n\"like sure.im or Tigase.im, to a server you may host on your own.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:34\nmsgid \"\"\n\"tigase.im[Tigase.im] - web-based client allowing to easily chat with \"\n\"friends independently of platform.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:36\nmsgid \"\"\n\"`Psi <http://psi-im.org/>`__ Pure Jabber client. Although it supports \"\n\"only Jabber network it is a very user friendly and comfortable program. \"\n\"It works on most popular operating systems like Linux, MS Windows, and \"\n\"Apple MacOS X.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:38\nmsgid \"\"\n\"`Gajim <http://www.gajim.org/>`__ This is another Jabber only client. \"\n\"Very user friendly and works on most of Linux distributions, FreeBSD, and\"\n\" MS Windows.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:40\nmsgid \"\"\n\"`Pidgin <http://www.pidgin.im/>`__ (previously `Gaim \"\n\"<http://gaim.sourceforge.net/>`__) This is not just a Jabber client. This\"\n\" type of application is called multicommunicator as apart from Jabber it \"\n\"supports many other instant messaging networks/protocols such as: \"\n\"AIM/ICQ, MSN, Yahoo, Gadu-Gadu, IRC, and a few others. So it is \"\n\"especially convenient if you have friends using other messaging networks.\"\n\" Pidgin works on most Linux distributions, and on MS Windows.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:42\nmsgid \"\"\n\"`Kopete <http://kopete.kde.org/>`__ This is a `KDE \"\n\"<http://www.kde.org/>`__ component and although it only works on Linux \"\n\"based system it also supports many of the most popular instant messaging \"\n\"protocols apart from Jabber like: AIM, Gadu-Gadu, ICQ, IRC, MSN, Yahoo.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:44\nmsgid \"Install the Jabber client of your choice and set up for a Tigase account:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:2\nmsgid \"Configuration instructions for Psi\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:5\nmsgid \"Psi - Initial configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:7\nmsgid \"The first time you run Psi you see a screen like this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:9\nmsgid \"|Psi First Run|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:109\nmsgid \"Psi First Run\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:11\nmsgid \"\"\n\"To connect to tigase.org server we need to configure the program. Below \"\n\"are step-by-step instructions for novice users on how to setup Psi.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:13\nmsgid \"\"\n\"Psi can connect to many Jabber servers at the same time so we have to \"\n\"identify each connection somehow. The first thing to do is assign a name \"\n\"to the connection we just created. As we are going to define connection \"\n\"to tigase.org server let’s just name it: **Tigase**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:15\nmsgid \"|Psi Add Account|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:110\nmsgid \"Psi Add Account\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:17\nmsgid \"\"\n\"**Note!** At the moment you can register an account through the Web site \"\n\"only. This is a single account for both services: The Drupal website and \"\n\"Jabber/XMPP service on the tigase.org domain. If you want to have a \"\n\"Jabber account on the tigase.org server go to the registration page, un-\"\n\"tick \\\"Register new account\\\", and go to the point no 5. You can use \"\n\"guide points 2-4 to register a Jabber account on any other Jabber server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:19\nmsgid \"\"\n\"When you press the Add button you will see next window where you can \"\n\"enter your Jabber account details:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:21\nmsgid \"|Psi Empty Account|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:111\nmsgid \"Psi Empty Account\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:23\nmsgid \"\"\n\"Invent your user name for the account on Tigase server. Let’s assume your\"\n\" user name is: **frank**. Jabber ID’s however consist of 2 parts - your \"\n\"user name and server address. Exactly the same as an e-mail address. As \"\n\"you are registering an account on tigase.org server, you will have to \"\n\"enter in this field: **frank@tigase.org**. Next enter the password of \"\n\"your choice and click the Register button.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:25\nmsgid \"|Psi Register Account|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:112\nmsgid \"Psi Register Account\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:27\nmsgid \"\"\n\"On successful registration you will receive a confirmation message and \"\n\"you should see a window like this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:29\nmsgid \"|Register Account Success|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:113\nmsgid \"Register Account Success\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:31\nmsgid \"\"\n\"It may happen that somebody earlier registered an account with the same \"\n\"name you’ve selected for yourself. If so, you will receive error message.\"\n\" You will then have to select another user name and try to register \"\n\"again.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:33\nmsgid \"\"\n\"After clicking the **OK** button you will see a window with your \"\n\"connection and account setup. You can stick with default values for now.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:35\nmsgid \"|PSI After Registration|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:114\nmsgid \"PSI After Registration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:37\nmsgid \"Just click the **Save** button and this window closes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:39\nmsgid \"\"\n\"Now you have your account configured and ready to use but you are still \"\n\"off-line. You can find out whether you are on-line or off-line by looking\"\n\" at the bottom of main Psi window. There you can see **Offline** text.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:41\nmsgid \"\"\n\"Click on this **Offline** text and you will see a list of possible \"\n\"options. Just select **Online**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:43\nmsgid \"|PSI Connected|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:115\nmsgid \"PSI Connected\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:45\nmsgid \"Now you are connected!\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:47\nmsgid \"\"\n\"Well, you are now connected but how to talk to other people? How to add \"\n\"friends to the contact list? You can send a message to your friends \"\n\"straight away using the **Psi menu** option **New blank message**. It is \"\n\"much more convenient however, if you could see which of your friends is \"\n\"online and available for chatting and if you could start talking to your \"\n\"friend just by clicking on his name.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:51\nmsgid \"Short Instructions How to Add Your First Contact\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:53\nmsgid \"\"\n\"Click on Psi menu - the button next to the **Online** text. You will see \"\n\"something like this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:55\nmsgid \"|PSI Menu|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:116\nmsgid \"PSI Menu\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:57\nmsgid \"From all menu options select the top one - Add a contact:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:59\nmsgid \"|PSI Menu add Contact|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:117\nmsgid \"PSI Menu add Contact\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:61\nmsgid \"The next window will display where you can enter your contact details:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:63\nmsgid \"|PSI Add User Empty|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:118\nmsgid \"PSI Add User Empty\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:65\nmsgid \"\"\n\"You have to know the Jabber ID of the person you want to add to your \"\n\"contact list. Let’s assume, for example, you want to add Tigase server \"\n\"administrator’s Jabber ID to your contact list. So, after you enter these\"\n\" details the window will look like this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:67\nmsgid \"|PSI Add User Filled|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:119\nmsgid \"PSI Add User Filled\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:69\nmsgid \"Click the **Add** button.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:71\nmsgid \"\"\n\"Now you will see a confirmation window that a new person has been added \"\n\"to your contact list:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:73\nmsgid \"|PSI Kobit Added|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:120\nmsgid \"PSI Kobit Added\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:75\nmsgid \"\"\n\"But there is more behind the scenes. Adding a contact to your **Roster** \"\n\"(contact list) usually means you can see whether the person is online and\"\n\" available to talk or not. The person however, may not wish you to see \"\n\"his presence. So, to make sure the other person accepts you as a friend \"\n\"Psi sent a request to the address you just entered with the question of \"\n\"whether he agrees to show his presence to you.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:77\nmsgid \"\"\n\"You won’t be able to see the users availability until he sends \"\n\"confirmation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:79\nmsgid \"\"\n\"Once the other user sends confirmation back, you will usually receive 2 \"\n\"system events:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:81\nmsgid \"|PSI Kobit Auth Received|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:121\nmsgid \"PSI Kobit Auth Received\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:83\nmsgid \"Click on the contact to see a window with these messages:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:85\nmsgid \"|PSI Authorized Window|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:122\nmsgid \"PSI Authorized Window\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:87\nmsgid \"One message just says you have been authorized by the other user:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:89\nmsgid \"|PSI Authorized Window 2|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:123\nmsgid \"PSI Authorized Window 2\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:91\nmsgid \"So you simply click **Next** to see the second message.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:93\nmsgid \"\"\n\"The second message is a bit more interesting. It contains the question of\"\n\" whether you also authorize the other user to see your presence. If you \"\n\"want to accept this request just click **Add/Auth**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:95\nmsgid \"|PSI Authorized Window 3|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:124\nmsgid \"PSI Authorized Window 3\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:97\nmsgid \"Finally main Psi window with your new contact:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:99\nmsgid \"|PSI Kobit Added Authorized|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:125\nmsgid \"PSI Kobit Added Authorized\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:101\nmsgid \"Well done!\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:103\nmsgid \"You are ready to start Jabbering. Good luck.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:105\nmsgid \"\"\n\"Where to go next? For detailed Psi documentation refer to the program \"\n\"Wiki page: http://psi-im.org/wiki/Main_Page\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:107\nmsgid \"Welcome to the Tigase Administration Guide.\"\nmsgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Administration/Using_Tigase/_using_tigase.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:2\nmsgid \"Using Tigase\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:4\nmsgid \"\"\n\"This section keeps set of documents which apply to all the Tigase server \"\n\"version and contain more generic or introductory information on general \"\n\"use and features.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:6\nmsgid \":ref:`Tigase Log Guide<Tigase-Log-Guide>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:8\nmsgid \":ref:`Debugging Tigase<Debuging-Tigase>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:10\nmsgid \":ref:`Basic System Checks<Basic-System-Checks>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:12\nmsgid \":ref:`Add and Manage Domains<addManageDomain>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:14\nmsgid \":ref:`Presence Forwarding<Presence-Forwarding>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:16\nmsgid \":ref:`Watchdog<Watchdog>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:18\nmsgid \"\"\n\":ref:`Runtime Environment Tip<Tigase-Tip-Checking-the-Runtime-\"\n\"Environment>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:20\nmsgid \":ref:`Checking Cluster Connections<Checking-Cluster-Connections>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:22\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:10\nmsgid \"\"\n\":ref:`Best Practices for Connecting to Tigase XMPP server From Web \"\n\"Browser<Best-Practices-for-Connecting-to-Tigase-XMPP-server-From-Web-\"\n\"Browser>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:24\nmsgid \":ref:`Scripting Support in Tigase<Scripting-support-in-Tigase>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:26\nmsgid \"\"\n\":ref:`Scripting Introduction - Hello World!<Scripting-Introduction - \"\n\"Hello-World!>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:28\nmsgid \"\"\n\":ref:`Tigase Scripting Version 4.4.x Update for Administrators<Tigase-\"\n\"Scripting-Version-4.4.x-Update-for-Administrators>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:30\nmsgid \":ref:`Tigase and Python Scripting<Tigase-and-Python>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:32\nmsgid \":ref:`Configuration Wizards<tigase3xconfiguration>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:2\nmsgid \"Offline Messages\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:4\nmsgid \"\"\n\"Tigase like any XMPP server supports storing of messages for users who \"\n\"are offline so that they may receive messages sent to them while they \"\n\"were not logged in.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:6\nmsgid \"\"\n\"By default, Tigase ``MessageAmp`` processor is responsible for storing \"\n\"offline messages, and will automatically store offline messages. This \"\n\"guide has multiple sections for setting limits globally, per user, and \"\n\"others.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:8\nmsgid \"\"\n\"Many of the features listed here require the use of the Advanced Message \"\n\"Processor Plugin which is turned on by default. To ensure AMP is turned \"\n\"on your system, view your ``config.tdsl`` file and be sure the following \"\n\"is there in your plugins line:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:16\nmsgid \"\"\n\"Messages will be delivered to intended recipients when they first login \"\n\"after roster exchange.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:19\nmsgid \"Offline Message Limits\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:21\nmsgid \"\"\n\"Support for limiting number of stored offline messages on a per-user \"\n\"basis has now been added to Tigase as of v7.1.0. By default, Tigase comes\"\n\" with a limit of stored offline messages which is set for every user. \"\n\"This limit by default is 100 offline messages for barejid-barejid pair. \"\n\"This value can be changed by the ``store-limit`` property. To change to \"\n\"200 messages on barejid-barejid paid, add the following entries to the \"\n\"``config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:34\nmsgid \"This setting applies to every user.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:37\nmsgid \"User Limit\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:39\nmsgid \"\"\n\"Each user is able to configure the number of offline messages which \"\n\"should be stored for him. To enable this feature, the following lines \"\n\"need to be entered into the ``config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:52\nmsgid \"\"\n\"Values of user-specific limits will be stored in UserRepository under \"\n\"subnode of ``offline-msgs`` and key ``store-limit``. Data storage will be\"\n\" stored in ``tig_pairs`` key with the value and a proper record from \"\n\"``tig_nodes`` points to this record.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:55\nmsgid \"Handling of Offline Messages Exceeding Limits\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:57\nmsgid \"\"\n\"There are two possible ways to handle offline messages that exceed the \"\n\"limitations: . ``error`` sending message with error type back to sender. \"\n\". ``drop`` drop of message without notifications to sender.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:59\nmsgid \"\"\n\"By default, Tigase sends a message back to the original sender with an \"\n\"error type of ``service-unavailable`` with a proper description of error \"\n\"according to `XEP-0160 <http://www.xmpp.org/extensions/xep-0160.html>`__.\"\n\" However, it is possible to change this behavior to better suit your \"\n\"needs. This is done by adding the following line to your ``config.tdsl`` \"\n\"file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:69\nmsgid \"\"\n\"This will force Tigase to drop packets that exceed the offline message \"\n\"limit.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:72\nmsgid \"Setting the Limits by User\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:74\nmsgid \"\"\n\"Users wishing to set a custom limit of stored offline messages for \"\n\"barejid-barejid pairs needs to send the following XMPP stanza to the \"\n\"server:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:82\nmsgid \"\"\n\"Where: . ``${random-id}`` is a random ID of the stanza (can be any \"\n\"string). . ``${limit}`` is the integer value of the offline message \"\n\"limit. This can be set to ``false`` to disable offline message limits.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:84\nmsgid \"\"\n\"In response, the server will send back an ``iq`` stanza with a result \"\n\"type:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:92\nmsgid \"Example of Setting Limit of Stored Offline Messages to 10\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:94\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:112\nmsgid \"XMPP client sends the following to the server:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:102\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:120\nmsgid \"Server response:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:110\nmsgid \"Example of Disabling Offline Message Limit\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:129\nmsgid \"Storing offline messages without body content\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:131\nmsgid \"Tigase can now store offline messages without ``<body/>`` content.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:133\nmsgid \"\"\n\"See `XEP-0334 <http://xmpp.org/extensions/xep-0334.html>`__ for protocol \"\n\"details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:135\nmsgid \"\"\n\"This can include message receipts, and messages with specific ``do-not-\"\n\"store`` tags.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:137\nmsgid \"\"\n\"Support has been added to set a list of paths and xmlns to trigger and \"\n\"place storage of offline messages using the following settings in \"\n\"``config.tdsl``:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:147\nmsgid \"This example results in two settings:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:150\nmsgid \"``/message/received[urn:xmpp:receipts]``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:150\nmsgid \"\"\n\"Results in storage of messages with a ``recieved`` subelement and with \"\n\"the xlmns set to ``urn:xmpp:receipts``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:153\nmsgid \"``/message/store-offline``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:153\nmsgid \"\"\n\"Results in storing messages with a ``store-offline`` subelement without \"\n\"checking xmlns.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:156\nmsgid \"Filtering of offline storage\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:158\nmsgid \"It is possible to set storage of other types to save:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:168\nmsgid \"The above setting in the ``config.tdsl`` file will cause that:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:170\nmsgid \"\"\n\"messages with ``<store-offline>`` subelement will be stored without \"\n\"checking for associated xmlns.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:172\nmsgid \"messages with ``<do-not-store>`` element **will not** be saved.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:174\nmsgid \"\"\n\"Any of these can be adjusted for your installation, remember that a '-' \"\n\"will stop storage of messages with the indicated property. Messages will \"\n\"be checked by these matchers and if any of them result in a positive they\"\n\" will override default settings.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:176\nmsgid \"\"\n\"For example, if you wanted to store messages with <received> element, but\"\n\" not ones with <plain> element, your filter will look like this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:186\nmsgid \"However…​.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:190\nmsgid \"\"\n\"**THE ABOVE STATEMENT WILL NOT WORK** As it will just store all messages \"\n\"with <received> subelement.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:192\nmsgid \"The below statement will properly filter your results.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:202\nmsgid \"\"\n\"Filtering logic is done in order from left to right. Matches on the first\"\n\" statement will ignore or override matches listed afterwards.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:205\nmsgid \"Disabling Offline Messages\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:207\nmsgid \"\"\n\"If you wish to disable the storing of offline messages, use the following\"\n\" line in your ``config.tdsl`` file. This will not disable other features \"\n\"of the AMP plugin.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/LastActivity.inc:2\nmsgid \"Last Activity\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/LastActivity.inc:4\nmsgid \"\"\n\"Tigase XMPP Server supports `XEP-0012: Last Activity \"\n\"<https://xmpp.org/extensions/xep-0012.html>`__ extension, which allows \"\n\"retrieval information when particular contact was active last time. It’s \"\n\"not enabled by default.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/LastActivity.inc:6\nmsgid \"The functionality itself is split in two plugins:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/LastActivity.inc:8\nmsgid \"\"\n\"``jabber:iq:last-marker`` - responsible for updating information about \"\n\"last activity of user\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/LastActivity.inc:10\nmsgid \"\"\n\"``jabber:iq:last`` - responsible for handling requests to retrieve last \"\n\"activity information (it depends on ``jabber:iq:last-marker`` plugin).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/LastActivity.inc:12\nmsgid \"\"\n\"In order to enable functionality you should add both plugins to your \"\n\"configuration file\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/LastActivity.inc:23\nmsgid \"What updates last activity\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/LastActivity.inc:25\nmsgid \"\"\n\"By default marker plugin will only update last activity information on \"\n\"presence stanza. It’s possible to control whether ``<presence/>`` and/or \"\n\"``<message/>`` should update with respective options:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/LastActivity.inc:36\nmsgid \"\"\n\"Those settings will cause updating last activity information for both \"\n\"``<message/>`` and ``<presence/>`` stanzas\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/LastActivity.inc:39\nmsgid \"Persist everything to repository\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/LastActivity.inc:41\nmsgid \"\"\n\"To lower impact on performance, by default last activity information is \"\n\"persisted to repository less frequently. This can yield slightly less \"\n\"accurate results on installations with multiple cluster nodes with users \"\n\"having multiple resources connected. To get more accurate results you \"\n\"should set ``persistAllToRepository`` to ``true``, which will cause all \"\n\"update times to be persisted (please bear in mind that this could cause \"\n\"higher impact on the repository).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:4\nmsgid \"Tigase Log Guide\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:6\nmsgid \"\"\n\"Tigase has multiple levels of logging available to help provide targeted \"\n\"and detailed information on processes, components, or traffic. In these \"\n\"documents we will look at where tigase generates logs, what they contain,\"\n\" and how we can customize them to our needs.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:9\nmsgid \"install.log\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:11\nmsgid \"\"\n\"This log file is a basic list of files that are made on install of Tigase\"\n\" server. Although you may not need to use it, it can provide a handy list\"\n\" to see if any files were not written to your hard drive upon \"\n\"installation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:14\nmsgid \"derby.log\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:16\nmsgid \"\"\n\"If you are using the derby database installed with Tigase, this is the \"\n\"startup log for the database itself. Issues that might be related to the \"\n\"database, can be found in this file. Typically, if everything works okay,\"\n\" it’s a very small file with only 10 lines. It is overwritten on startup \"\n\"of the database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:19\nmsgid \"etc/config-dump.properties\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:21\nmsgid \"\"\n\"The config-dump.properties is dump file of all your properties listed for\"\n\" every option within Tigase and components. The structure of the log \"\n\"lines is the same as the structure of Tigase XMPP Server config file - \"\n\"TDSL. Lets take the value for admins, listing who is administrator for \"\n\"the server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:27\nmsgid \"The admin parameter which is an array of strings and has 3 users listed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:29\nmsgid \"This file is re-written every time tigase starts.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:32\nmsgid \"logs/tigase.log.#\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:34\nmsgid \"\"\n\"The tigase.log files are where the majority of logging will take place. \"\n\"The rules for writing to these longs can be manipulated by editing files \"\n\"in the int.properties file. To see how, see the :ref:`Debugging Tigase\"\n\"<Debuging-Tigase>` section of this manual for more details about how to \"\n\"turn on debug logging, and how to manipulate log settings. Entries to \"\n\"these logs are made in the following format:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:40\nmsgid \"\"\n\"The format of these logs is below: ``<timestamp> <thread_name> \"\n\"<class>.<method>    <log_level>: <message> <thread_name>``. This can vary\"\n\" - for components it would be ``<direction>_<int>_<component name>``, for\"\n\" plugins it will just be the plugin name.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:42\nmsgid \"Let’s look at another example from the log file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:48\nmsgid \"\"\n\"The process ID may sometimes come in a different format such as \"\n\"``[in_14-muc]`` which specifies the component (muc) along with the \"\n\"process thread identifier (14). As you can see, the format otherwise is \"\n\"nearly identical.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:50\nmsgid \"\"\n\"``tigase.log.#`` files are *rotated* - this means that server begins \"\n\"writing to tigase.log.0 when it is first run, and continues to dump \"\n\"information until the log size limit is hit. At this point, Tigase \"\n\"renames tigase.log.0 as tigase.log.1. A new tigase.log.0 will be created,\"\n\" and Tigase will begin logging to this file. When this file is full, \"\n\"tigase.log.1 will be renamed tigase.log.2 and tigase.log.0 will be \"\n\"renamed tigase.log.1. Using this scheme, tigase.log.0 will **always** be \"\n\"your most recent log.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:52\nmsgid \"\"\n\"By default, Tigase has a limit of 10000000 bytes or 10MB with a file \"\n\"rotation of 10 files. You can edit these values by editing the \"\n\"``config.tdsl`` file and adding the following lines.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:63\nmsgid \"\"\n\"This code, if entered into the ``config.tdsl`` file increases the size of\"\n\" the files to 15, and enlarges the maximum size to 20MB. Note the larger \"\n\"the collective log space is, the larger number of sectors on hard disk \"\n\"are active. Large log blocks may impact system performance.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:65\nmsgid \"\"\n\"*You may see a tigase.log.0.lck file in the directory while the server is\"\n\" running. This is a temporary file only and is deleted once Tigase is \"\n\"cleanly shut down.*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:68\nmsgid \"logs/statistics.log.#\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:70\nmsgid \"\"\n\"Statistics log will duplicate any information that is related to sending \"\n\"of statistics to Tigase if you are using an unlicensed copy of Tigase \"\n\"XMPP server. Mainly it will consist output of LicenceChecker. The \"\n\"numbering logic will be the same as ``tigase.log.#`` files.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:73\nmsgid \"logs/tigase.pid\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:75\nmsgid \"\"\n\"``tigase.pid`` is a file that just contains the Process ID or PID for the\"\n\" current run of Tigase. It is only valid for the current or most recent \"\n\"run cycle and is overwritten every time Tigase starts.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:78\nmsgid \"logs/tigase-console.log\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:82\nmsgid \"\"\n\"This is the most important log file containing the most essential \"\n\"information related to operation of the Tigase XMPP Server. Any errors or\"\n\" exceptions in this file indicate with high probability serious issues \"\n\"with server operation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:84\nmsgid \"\"\n\"This file contains information related to Tigase’s running environment, \"\n\"and is a dump from the server itself on what is being loaded, when, and \"\n\"if any issues are encountered. It will start by loading Java classes \"\n\"(consequently making sure the Java environment is present and \"\n\"functioning). Then it will begin loading the configuration file, and \"\n\"adding default values to settings that have not been customized. You can \"\n\"then see all the components being loaded, and settings added where \"\n\"default values are needed. Lastly you will see a log of any plugins that \"\n\"are loaded, and any parameters therein. You may see tags such as INFO or \"\n\"WARNING in the logs. Although they may contain important information, the\"\n\" program will continue to operate as normal are not of too great concern.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:86\nmsgid \"\"\n\"ERROR flags are issues you will want to pay attention as they may list \"\n\"problems that prevent Tigase or components from properly functioning.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:90\nmsgid \"\"\n\"Windows does not create this file, rather the output is shown in the \"\n\"command line and is not dumped to a file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:92\nmsgid \"\"\n\"If Tigase is gracefully shut down, tigase-console.log will add statistics\"\n\" from the server’s operation life in the following format.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:98\nmsgid \"\"\n\"*Any component that may have a statistic, whether used or not, will place\"\n\" a value here*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:100\nmsgid \"This file can be handy if you are tracking issues in the server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:102\nmsgid \"tigase-console.log is appended during each run session of the server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:105\nmsgid \"Log File Location\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:107\nmsgid \"\"\n\"You can also change the location of log files if you have a specific \"\n\"directory you wish to use. The configuration may be made by the following\"\n\" lines in your ``config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:117\nmsgid \"\"\n\"This setting changes the log file location to /var/log/tigase/ where all \"\n\"log files will be made. Files in the original location will be left.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:4\nmsgid \"Debuging Tigase\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:6\nmsgid \"\"\n\"If something goes wrong and you can’t find out why it is not working as \"\n\"expected, you might want more detailed debugging options switched on.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:8\nmsgid \"\"\n\"Tigase is a Java application and it uses Java logging library, this gives\"\n\" you the flexibility to switch logging on for selected Java packages or \"\n\"even for a single Java class.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:10\nmsgid \"\"\n\"Logs files are stored in ``logs/`` directory. ``tigase-console.log`` \"\n\"stores basic log data, but is the main log file. ``tigase.log.N`` files \"\n\"keep all the detailed logging entries. So this is the place where you \"\n\"should look in case of problems.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:25\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:13\nmsgid \"Configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:15\nmsgid \"\"\n\"By default, Tigase has the old ``debug = ['server']`` setting is turned \"\n\"on and does not need to be added.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:17\nmsgid \"\"\n\"However, people want to see what is going on the network level. That is \"\n\"what has been sent and what has been received by the server - the actual \"\n\"character data. The class which would print all received and sent \"\n\"character data is: ``tigase.xmpp.XMPPIOService``. To enable all debugging\"\n\" info for this class you have to modify the debug line:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:23\nmsgid \"\"\n\"You can also have debugging switched on for many packages/classes at the \"\n\"same time:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:29\nmsgid \"Other packages you might be interested in are:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:31\nmsgid \"\"\n\"``io`` can print out what is going on a very low level network level \"\n\"including TLS/SSL stuff.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:33\nmsgid \"``xml`` would print the XML parser debugging data.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:35\nmsgid \"``cluster`` would print all the clustering related stuff.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:37\nmsgid \"``xmpp.impl`` would print logs from all plugins loaded to Tigase server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:40\nmsgid \"Non-Tigase packages\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:42\nmsgid \"\"\n\"To enable logging for your own packages from those different than Tigase,\"\n\" you have to use another option which has been made available for this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Basic_System_Checks.inc:4\nmsgid \"Basic System Checks\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Basic_System_Checks.inc:6\nmsgid \"\"\n\"Previously, a configuration article is available about :ref:`Linux \"\n\"settings for high load systems<linuxhighload>`. This has a description of\"\n\" basic settings which are essential to successfully run XMPP service for \"\n\"hundreds or thousands of online users.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Basic_System_Checks.inc:8\nmsgid \"\"\n\"Of course, high load and high traffic systems require much more tuning \"\n\"and adjustments. If you use selinux you have to be careful as it can \"\n\"interfere with the service while it is under a high load. Also some \"\n\"firewall settings may cause problems as the system may decide it is under\"\n\" a DDOS attack and can start blocking incoming connections or throttle \"\n\"the traffic.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Basic_System_Checks.inc:10\nmsgid \"\"\n\"In any case, there are some basic checks to do every time you deploy XMPP\"\n\" service to make sure it will function properly. I am trying to keep the \"\n\"article mentioned above up to date and add all the settings and \"\n\"parameters I discover while working with different installations. *If you\"\n\" have some suggestions for different values or different parameters to \"\n\"add, please let me know.*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Basic_System_Checks.inc:12\nmsgid \"\"\n\"If you want to run a service on a few cluster nodes (5 or even 10), then \"\n\"manually checking every machine and adjusting these settings is time \"\n\"consuming and it is very easy to forget about.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Basic_System_Checks.inc:14\nmsgid \"\"\n\"To overcome this problem I started to work on a shell script which would \"\n\"run all the basic checks and report problems found. Ideally it should be \"\n\"also able to adjust some parameters for you.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Basic_System_Checks.inc:16\nmsgid \"\"\n\"Inside the Tigase server `scripts/ <https://github.com/tigase/tigase-\"\n\"server/blob/master/scripts/>`__ repository find a script called \"\n\"``machine-check.sh``. It performs all the basic checks from the article \"\n\"and also tries to adjust them when necessary. Have a `look at the code \"\n\"<https://github.com/tigase/tigase-server/blob/master/scripts/machine-\"\n\"check.sh>`__ and run for yourself.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Basic_System_Checks.inc:18\nmsgid \"Any comments or suggestions, as usual, are very much appreciated.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:4\nmsgid \"Add and Manage Domains (VHosts)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:6\nmsgid \"\"\n\"Tigase XMPP Server offers an easy to use and very flexible way to add and\"\n\" manage domains hosted on installation (vhosts).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:8\nmsgid \"There are two ways of managing domains you host on your server:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:10\nmsgid \"using web-based admin management console - :ref:`Admin UI<usingAdminUI>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:12\nmsgid \"\"\n\"using XMPP ad-hoc commands by XMPP client, ie. `Psi <http://psi-\"\n\"im.org/>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:16\nmsgid \"\"\n\"To use any of those ways, you need to be an administrator of the server, \"\n\"which means that you have a XMPP account created on this XMPP server and \"\n\"your account JID is added to :ref:`the list of the \"\n\"administrators<admins>` in the Tigase XMPP Server configuration file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:21\nmsgid \"Using Admin UI\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:23\nmsgid \"\"\n\"First, you need to open Admin UI web page. By default Admin UI is enabled\"\n\" and available at the port ``8080`` at path ``/admin/`` on the XMPP \"\n\"server. Assuming that your are logged on the same machine which hosts \"\n\"Tigase XMPP Server, it will be available at http://localhost:8080/admin/.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:25\nmsgid \"\"\n\"When you will be prompted for username and password to login to the Admin\"\n\" UI please fill username with full JID of your XMPP admin account and \"\n\"fill password field with password for this account. When you submit \"\n\"correct credentials you will get access to the Admin UI and Tigase XMPP \"\n\"Server configuration and management web-based interface.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:28\nmsgid \"Adding a new domain\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:30\nmsgid \"\"\n\"To add a new domain you need to open ``Configuration`` section of the \"\n\"Admin UI (by clicking on ``Configuration`` label and then selecting ``Add\"\n\" new item`` position which mentions ``vhost-man``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:32\nmsgid \"|adminui vhost add item button|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:138\nmsgid \"adminui vhost add item button\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:34\nmsgid \"\"\n\"After doing that, you will be presented with a form which you need to \"\n\"fill in. This form allows you to pass ``Domain name`` to add and other \"\n\"options (some of the are advanced options).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:36\nmsgid \"|adminui vhost add item form|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:139\nmsgid \"adminui vhost add item form\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:40\nmsgid \"\"\n\"All options with exception of ``Domain name`` may be changed later on by \"\n\"modifying vhost settings.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:42\nmsgid \"\"\n\"When you will be ready, please submit the form using button below the \"\n\"form. As a result you will be presented with a result of this operation. \"\n\"If it was successful it show ``Operation successful`` message and if \"\n\"something was not OK, it will display an error to help you fix this issue\"\n\" which you encountered.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:45\nmsgid \"Modifying domain settings\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:47\nmsgid \"\"\n\"Modifying a domain settings is very similar to adding a new domain. You \"\n\"need to open ``Configuration`` section of the Admin UI and then select \"\n\"``Update item configuration`` position which mentions ``vhost-man``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:49\nmsgid \"|adminui vhost update item button|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:140\nmsgid \"adminui vhost update item button\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:51\nmsgid \"\"\n\"You will be presented with a list of domains hosted on this Tigase XMPP \"\n\"Server installation. From them you should choose the one for which you \"\n\"wish to modify settings.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:53\nmsgid \"|adminui vhost update item domains list|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:141\nmsgid \"adminui vhost update item domains list\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:55\nmsgid \"\"\n\"After submitting this selection, you will be presented with a the same \"\n\"form as the one used during adding a new domain. It presents \"\n\"configuration options for this domain and currently used values.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:57\nmsgid \"|adminui vhost update item form|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:142\nmsgid \"adminui vhost update item form\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:59\nmsgid \"\"\n\"Now you should adjust them as you wish and submit this form using the \"\n\"button below the form.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:61\nmsgid \"\"\n\"As a result you will be presented with a result of this operation. If it \"\n\"was successful it show ``Operation successful`` message and if something \"\n\"was not OK, it will display an error to help you fix this issue which you\"\n\" encountered.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:64\nmsgid \"Removing a domain\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:66\nmsgid \"\"\n\"Removing a hosted domain from the Tigase XMPP Server installation is \"\n\"quite simple as well. You need to open ``Configuration`` section of the \"\n\"Admin UI and then select ``Remove an item`` position which mentions \"\n\"``vhost-man``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:68\nmsgid \"|adminui vhost remove item button|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:143\nmsgid \"adminui vhost remove item button\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:70\nmsgid \"\"\n\"You will be presented with a list of domains hosted on this Tigase XMPP \"\n\"Server installation. From them you should select the one which should be \"\n\"removed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:72\nmsgid \"|adminui vhost remove item domains list|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:144\nmsgid \"adminui vhost remove item domains list\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:74\nmsgid \"\"\n\"After submitting your selection, Tigase XMPP Server will try to remove \"\n\"this domain from the list of hosted domains and will present you with the\"\n\" result. If it was successful it show ``Operation successful`` message \"\n\"and if something was not OK, it will display an error to help you fix \"\n\"this issue which you encountered.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:77\nmsgid \"Using ad-hoc commands\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:79\nmsgid \"\"\n\"For everybody interested in using our service to host their own XMPP \"\n\"domain we have good news! You do not have to ask an administrator to add \"\n\"your domain or add users for your domain anymore. You can do it on your \"\n\"own.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:81\nmsgid \"\"\n\"Please note, this is very new stuff. Something may go wrong or may not be\"\n\" polished. Please report any problems, notices or suggestions.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:83\nmsgid \"\"\n\"This is the guide to walk you through the new functions and describes how\"\n\" to add a new domain and new users within your domain.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:85\nmsgid \"\"\n\"You can do everything from your XMPP client or you can use our web \"\n\"application that allows you to connect to the service and execute admin \"\n\"commands. I recommend `Psi <http://psi-im.org/>`__ because of its \"\n\"excellent support for parts of the XMPP protocol which are used for \"\n\"domains and user management. You may use other clients as well, but we \"\n\"can only offer support and help if you use Psi client.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:87\nmsgid \"\"\n\"Secondly, you need an account on the server. This is because all the \"\n\"commands and features described here are available to local users only. \"\n\"Therefore, if you do not have a registered domain with us yet, please go \"\n\"ahead and register an account on the website either the `Tigase.IM \"\n\"<http://www.tigase.im/>`__ or `Jabber.Today <http://jabber.today/>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:90\nmsgid \"Adding a New Domain\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:92\nmsgid \"\"\n\"Once you register an account on one of the websites, connect to the XMPP \"\n\"server using the account on the Psi client. We will be using the \"\n\"following account: green@tigase.im which is this guide.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:94\nmsgid \"\"\n\"When you are ready right click on the account name in Psi roster window \"\n\"to bring up context menu. Select **Service Discovery** element.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:96\nmsgid \"|service disco menu|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:145\nmsgid \"service disco menu\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:98\nmsgid \"\"\n\"A new windows pops up as in the example on the right. The service \"\n\"discovery window is where all the stuff installed on XMPP service should \"\n\"show up. Most of elements on the list are well known transports, MUC and \"\n\"PubSub components. The new stuff on the list, which we are interested in,\"\n\" are 2 elements: **VHost Manager** and **Session Manager**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:100\nmsgid \"|service disco window vhost|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:146\nmsgid \"service disco window vhost\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:102\nmsgid \"\"\n\"**VHost Manager** component in Tigase is responsible for managing and \"\n\"controlling virtual hosts on the installation. It provides virtual hosts \"\n\"information to all other parts of the system and also allows you to add \"\n\"new hosts and remove/update existing virtual hosts.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:104\nmsgid \"\"\n\"**Session Manager** component in Tigase is responsible for managing \"\n\"users. In most cases online users but it can also perform some actions on\"\n\" user repository where all user data is stored.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:106\nmsgid \"\"\n\"Select **VHost Manager** and double click on it. A new windows shows up \"\n\"(might be hidden behind the service discovery window). The window \"\n\"contains another menu with a few items: **Add…​, Remove…​** and \"\n\"**Update…​** . These are for adding, removing and updating VHost \"\n\"information. For now, just select the first element **Add…​.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:108\nmsgid \"|command menu add vhost|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:147\nmsgid \"command menu add vhost\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:110\nmsgid \"\"\n\"Click **Execute** and you get a new window where you can enter all of \"\n\"your VHost configuration details. All fields should be self explanatory. \"\n\"Leave a blank field for **Other parameters** for now. **Owner** is you, \"\n\"that is Jabber ID which controls the domain and can change the domain \"\n\"configuration settings or can remove the domain from the service. \"\n\"**Administrators** field can be left blank or can contain comma separated\"\n\" list of Jabber IDs for people who can manage users within the domain. \"\n\"You do not need to add your user name to the list as Owners can always \"\n\"manage users for the domain.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:112\nmsgid \"|add vhost window|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:148\nmsgid \"add vhost window\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:114\nmsgid \"\"\n\"When you are ready click the **Finish** button. All done, hopefully. You \"\n\"can get either a window confirming everything went well or a window \"\n\"printing an error message if something went wrong. What can be wrong? \"\n\"There are some restrictions I decided to put on the service to prevent \"\n\"abuse. One of the restrictions is the maximum number of domains a user \"\n\"can register for himself which is **25** right now. Another restriction \"\n\"is that the domain which you add must have a valid DNS entry pointing to \"\n\"our service. The XMPP guide describes all the details about DNS settings.\"\n\" Please refer to these instructions if you need more details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:117\nmsgid \"Adding a New User\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:119\nmsgid \"\"\n\"Adding a new user process is quite similar, almost identical to adding a \"\n\"new domain. This time, however we have to select **Session Manager** in \"\n\"the service discovery window.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:121\nmsgid \"|service disco window sm|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:149\nmsgid \"service disco window sm\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:123\nmsgid \"\"\n\"Double click on the **Session Manager** and a window with SM’s commands \"\n\"list shows up. Right now, there is only one command available to domain \"\n\"administrators - **Add user**. I am going to make available more commands\"\n\" in the future and I am waiting for your suggestions.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:125\nmsgid \"|command menu add user|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:150\nmsgid \"command menu add user\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:127\nmsgid \"\"\n\"If you click **Execute** a window presented on the left shows up. Fill \"\n\"all fields accordingly and press **Finish**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:129\nmsgid \"|add user window|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:151\nmsgid \"add user window\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:131\nmsgid \"\"\n\"If everything went well you have just added a new user and you should get\"\n\" a window confirming successful operation. If something went wrong, a \"\n\"window with an error message should show up. Possible errors may be you \"\n\"tried to add a user which is already present, or you may have tried to \"\n\"add a user for a domain to which you do not have permission or to non-\"\n\"existen domain.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:134\nmsgid \"SSL Certificate Management\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:136\nmsgid \"\"\n\"SSL Certificate Management has been implemented, and certificates can be \"\n\"manipulated when in a .pem form. For more details, see :ref:`Creating and\"\n\" Loading the Server Certificate in pem Files<certspem>` section of \"\n\"documentation for more information.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:4\nmsgid \"Presence Forwarding\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:6\nmsgid \"\"\n\"Have you ever thought of displaying your users presence status on the \"\n\"website? Or, maybe, you wanted to integrate XMPP service with your own \"\n\"system and share not only users' accounts but also presence status?\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:8\nmsgid \"\"\n\"Not only is it possible but also very simple. You have a new option in \"\n\"the domain control form.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:10\nmsgid \"Actually there are 2 new options:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:12\nmsgid \"Presence forward address\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:14\nmsgid \"Message forward address - not fully implemented yet\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:16\nmsgid \"\"\n\"Presence forward address can be any XMPP address. Usually you want it to \"\n\"be a bot address which can collect your users' presence information. Once\"\n\" this option is set to a valid XMPP address Tigase forwards user’s \"\n\"presence, every time the user changes his status. The presence is \"\n\"processed normally, of course, and distributed to all people from the \"\n\"contact list (roster), plus to this special address. It can be a \"\n\"component or a bot. If this is a bot connecting to a regular XMPP \"\n\"account, **Make sure the presence forward address contains resource part \"\n\"and the bot is connecting with this resource.** Otherwise the presence \"\n\"won’t be delivered to the bot.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:18\nmsgid \"|vhost presence forward|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:62\nmsgid \"vhost presence forward\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:20\nmsgid \"\"\n\"As the screenshot shows, there are new input lines with option for \"\n\"presence forwarding address and message forwarding address. As you can \"\n\"see this option can be specified separately for each domain, so you can \"\n\"have a different forward address for each domain.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:22\nmsgid \"\"\n\"If you have your own Tigase installation, the forwarding address can be \"\n\"also set globally and can be the same for all domains. However, for this \"\n\"website, we offer this feature to all our users who have own domains and \"\n\"this can be set on per-domain basis.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:24\nmsgid \"\"\n\"Now, the big question. How this can be used? I am attaching below an \"\n\"example code. With just a few lines of code you can connect a command \"\n\"line bot to the server as a client which would collect all presences from\"\n\" users. Code below is a simple Groovy script which receives presence \"\n\"packet and displays them on the console. However, it should be easy \"\n\"enough to store users' presence information in a database and then load \"\n\"it from a web application.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:26\nmsgid \"\"\n\"The bot/client uses our `JaXMPP2 <https://github.com/tigase/jaxmpp>`__ \"\n\"library which is included in current builds of Tigase XMPP Server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:28\nmsgid \"You should be able to find a few more code examples on the wiki page.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:4\nmsgid \"Watchdog\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:6\nmsgid \"\"\n\"Tigase’s Watchdog was implemented to help Tigase close connections that \"\n\"have become stale or inactive. Sometimes the connection is delayed, maybe\"\n\" dropped packets, or a service interruption. After a time, if that \"\n\"connection is re-established, both server and client (or server and \"\n\"server) will continue on as if nothing happened. However, these gaps in \"\n\"connection can last longer, and some installations will rely on the \"\n\"operating system to detect and close stale connections. Some operating \"\n\"systems or environments can take up to 2 hours or more to determine \"\n\"whether a connection is bad and wait for a response from a foreign entity\"\n\" and may not be configured. This can not only slow down performance, but \"\n\"can lead to security issues as well. To solve this problem, we have \"\n\"introduced Watchdog to monitor connections independent of operating \"\n\"system and environments to keep those broken connections from becoming a \"\n\"problem.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:9\nmsgid \"Setup\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:11\nmsgid \"\"\n\"No extra setup is necessary, Watchdog is already included with your build\"\n\" of Tigase (as long as it’s 7.1.0 or newer). Follow the steps in the \"\n\"configuration section.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:14\nmsgid \"Watchdog Configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:16\nmsgid \"\"\n\"To configure watchdog, the following lines need to be present or edited \"\n\"in ``config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:24\nmsgid \"The three settings are as follows:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:26\nmsgid \"\"\n\"``'watchdog-timeout'= 70000`` This setting sets the amount of time that \"\n\"watchdog will consider before it determines a connection may be stale. \"\n\"This setting sets the timeout at 70000ms or 70 seconds.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:28\nmsgid \"\"\n\"``'watchdog-delay' = 60000`` This setting sets how often the watchdog \"\n\"should conduct the check, the default delay at 60000ms or 60 seconds.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:30\nmsgid \"\"\n\"``'watchdog-ping-type'`` This setting determines the type of ping sent to\"\n\" components when watchdog is testing for activity.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:32\nmsgid \"\"\n\"You may, if you choose, to specify individual watchdog settings for \"\n\"specific components by adding them to the component settings, for example\"\n\" if we wanted to change the Client2Server settings to include watchdog, \"\n\"use the following lines in config.tdsl:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:41\nmsgid \"\"\n\"If any settings are not set, the global or settings will be used. \"\n\"``watchdog-delay`` default is set to 10 min ``watchdog-ping-type`` \"\n\"default is set to XMPP\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:44\nmsgid \"Logic\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:46\nmsgid \"\"\n\"Watchdog compares it’s own pings, and records the time it takes for a \"\n\"round trip to different components, clustered connections, and if one \"\n\"variable is larger than the other, watchdog will commence closing that \"\n\"stale connection. Here is a breakdown:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:48\nmsgid \"\"\n\"A check is performed of a connection(s) on every ``watchdog-delay`` \"\n\"interval.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:50\nmsgid \"During this check two things occur\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:52\nmsgid \"\"\n\"If the last transfer time exceeds ``max-inactivity-time`` a stop service \"\n\"command is given to terminate and broadcast unavailable presence.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:54\nmsgid \"\"\n\"If the last transfer time is lower than ``max-inactivity-time`` but \"\n\"exceeds ``watchdog-timeout`` watchdog will try to send a ping (of \"\n\"``watchdog-ping-type``). This ping may be one of two varieties (set in \"\n\"config.tdsl)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:56\nmsgid \"\"\n\"``WHITESPACE`` ping which will yield the time of the last data transfer \"\n\"in any direction.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:58\nmsgid \"``XMPP`` ping which will yield the time of the last received xmpp stanza.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:60\nmsgid \"\"\n\"If the 2nd option is true, the connection will remain open, and another \"\n\"check will begin after the ``watchdog-delay`` time has expired.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:62\nmsgid \"For example, lets draw this out and get a visual representation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:72\nmsgid \"\"\n\"This line represents how often the check is performed. Each ``-`` (dash) \"\n\"is 10 seconds, so the check is done every 60 seconds (``'watchdog-delay' \"\n\"= 60000``)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:74\nmsgid \"\"\n\"This line is client activity, here the client sent a message at 40 \"\n\"seconds (marked by ``+``) and has gone idle.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:76\nmsgid \"\"\n\"The following line represents the watchdog logic, with timeout at 120 \"\n\"seconds and max inactivity timeout at 180 seconds:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:86\nmsgid \"How the check is performed:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:88\nmsgid \"\"\n\"30 seconds - at this point *last transfer* or *last received* time is \"\n\"updated.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:90\nmsgid \"\"\n\"60 seconds - watchdog runs - it check the connection and says: \\\\_ok, \"\n\"last client transfer was 20s ago - but it’s lower than both inactivity \"\n\"(so don’t disconnect) and timeout (so don’t send ping).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:92\nmsgid \"\"\n\"120 seconds - 2nd check - last transfer was 100s ago - still lower than \"\n\"both values - do nothing.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:94\nmsgid \"\"\n\"180 seconds - 3rd check - last transfer was 160s ago - lower than \"\n\"inactivity but greater than delay - ping it sent.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:96\nmsgid \"\"\n\"240 seconds - 4th check - last transfer was 220s ago - client still \"\n\"hasn’t responded, watchdog compares idle time to ``max-inactivity-\"\n\"timeout`` and finds out that it is greater, connection is terminated.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:98\nmsgid \"\"\n\"300 seconds - watchdog is run again but given the connection was \"\n\"terminatet there is no XMPP session to check for that particular client.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:102\nmsgid \"\"\n\"It is possible that the connection is broken, and could be detected \"\n\"during the sending of a ping and the connection would be severed at step \"\n\"4 instead of waiting for step 5. **NOTE** This MAY cause JVM to throw an \"\n\"exception.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:106\nmsgid \"\"\n\"Global settings may not be ideal for every setup. Since each component \"\n\"has its own settings for ``max-inactivity-time`` you may find it \"\n\"necessary to design custom watchdog settings, or edit the inactivity \"\n\"times to better suit your needs. Below is a short list of components with\"\n\" thier default settings:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:128\nmsgid \"\"\n\"Again remember, for Watchdog to properly work, the ``max-inactivity-\"\n\"time`` MUST be longer than the ``watchdog-timeout`` setting\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:131\nmsgid \"Testing\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:133\nmsgid \"\"\n\"The tigase.log.0 file can reveal some information about watchdog and how \"\n\"it is working (or how it might be fighting your settings). To do so, \"\n\"enter the following line into your ``config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:139\nmsgid \"\"\n\"This will set debug mode for your log, and enable some more information \"\n\"about what Tigase is doing. These logs are truncated for simplicity. Lets\"\n\" look at the above scenario in terms of the logs:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:141\nmsgid \"**Stage Two.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:147\nmsgid \"**Stage Three.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:153\nmsgid \"**Stage Four.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:160\nmsgid \"**Stage Five.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:4\nmsgid \"Tips and Tricks\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:6\nmsgid \"\"\n\"The section contains some short tricks and tips helping in different \"\n\"kinds of issues related to the server administration and maintenance.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:8\nmsgid \":ref:`Runtime Environment Tip<tigaseTip_RuntimeEnvironment>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:15\nmsgid \"Tigase Tip: Checking the Runtime Environment\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:17\nmsgid \"\"\n\"It has happened recently that we have tried very hard to fix a few \"\n\"annoying problems on one of the Tigase installations. Whatever we did, \"\n\"the problem still existed after uploading a new version and restarting \"\n\"the server. It worked fine in our development environment and it just \"\n\"didn’t on the target system.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:19\nmsgid \"\"\n\"It turned out that due to specific environment settings on the target \"\n\"system, an old version of Tigase server was always started regardless of \"\n\"what updates uploaded. We finally located the problem by noticing that \"\n\"the logs were not being generated in the proper locations. This led us to\"\n\" finding the issue: improper environment settings.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:21\nmsgid \"\"\n\"The best way to check all the environment settings used to start the \"\n\"Tigase server is to use… ``check`` command line parameter:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:53\nmsgid \"\"\n\"In our case ``TIGASE_HOME`` was set to a fixed location pointing to an \"\n\"old version of the server files. The quick ``check`` command may be a \"\n\"real time saver.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:58\nmsgid \"Best Practices for Connecting to Tigase XMPP server From Web Browser\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:60\nmsgid \"\"\n\"Currently we have 2 ways to connect to Tigase XMPP Server from web \"\n\"browsers:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:62\nmsgid \"BOSH (Bidirectional-streams Over Synchronous HTTP)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:64\nmsgid \"WebSocket (XMPP over WebSocket)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:66\nmsgid \"\"\n\"You will find more information about these ways for connecting to Tigase \"\n\"XMPP Server with some useful tips below.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:69\nmsgid \"BOSH\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:71\nmsgid \"\"\n\"BOSH protocol specified in `XEP-0124 \"\n\"<http://xmpp.org/extensions/xep-0124.html>`__ is one of first protocols \"\n\"defined to allow to establish XMPP connection to XMPP servers from web \"\n\"browsers due to this protocol being widely supported and used. It is also\"\n\" easy to use in single server mode. It’s enabled by default in Tigase \"\n\"XMPP Server and available at port 5280.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:73\nmsgid \"\"\n\"In clustered mode we can deploy it with load balancer deployed with \"\n\"guarantees that each BOSH connection from web browser will be forwarded \"\n\"to same Tigase XMPP Server instance. So in clustered mode if we have two \"\n\"XMPP server ``t1`` and ``t2`` which are hosting domain ``example.com`` we\"\n\" would need to have load balancer which will respond for HTTP request to \"\n\"domain ``example.com`` and forward all requests from same IP address to \"\n\"same node of a cluster (i.e. all request from ``192.168.122.32`` should \"\n\"be forwarded always to node ``t1``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:76\nmsgid \"Tip #1 - BOSH in Cluster Mode Without Load Balancer\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:78\nmsgid \"\"\n\"There is also a way to use BOSH without load balancer enabled. In this \"\n\"case the XMPP client needs to have more logic and knowledge about all \"\n\"available cluster nodes (with names of nodes which will identify \"\n\"particular cluster nodes from internet). Using this knowledge XMPP client\"\n\" should select one random node from list of available nodes and always \"\n\"establish BOSH connections to this particular node. In case if BOSH \"\n\"connection fails due to network connection issues, the XMPP client should\"\n\" randomly pick other node from list of rest of available nodes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:80\nmsgid \"*Solution:*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:82\nmsgid \"\"\n\"Tigase XMPP Server by default provides server side solution for this \"\n\"issue by sending additional ``host`` attribute in ``body`` element of \"\n\"BOSH response. As value of this attribute Tigase XMPP Server sends domain\"\n\" name of server cluster node to which client connected and to which next \"\n\"connections of this session should be opened. It is possible to disable \"\n\"this custom feature by addition of of following line to \"\n\"``etc/config.tdsl`` config file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:90\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:130\nmsgid \"*Example:*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:92\nmsgid \"\"\n\"We have servers ``t1.example.com`` and ``t2.example.com`` which are nodes\"\n\" of a cluster hosting domain ``example.com``. Web client retrieves list \"\n\"of cluster nodes from web server and then when it needs to connect to the\"\n\" XMPP server it picks random host from list of retrieved cluster nodes \"\n\"(i.e. ``t2.example.com``) and tries to connect using BOSH protocol to \"\n\"host ``t2.example.com`` but it should send ``example.com`` as name of the\"\n\" server it tries to connect to (``example.com`` should be value of ``to``\"\n\" attribute of XMPP stream).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:95\nmsgid \"WebSocket\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:97\nmsgid \"\"\n\"WebSocket protocol is newly standardized protocol which is supported by \"\n\"many of current versions of browsers. Currently there is a draft of \"\n\"protocol `draft-ietf-xmpp-websocket-00 <https://datatracker.ietf.org/doc\"\n\"/draft-ietf-xmpp-websocket/>`__ which describes usage of WebSocket to \"\n\"connect to XMPP servers. Tigase XMPP Server implementation of WebSocket \"\n\"protocol to connect to XMPP server is very close to this draft of this \"\n\"specification. By default Tigase XMPP Server has XMPP-over-WebSocket \"\n\"protocol enabled without encryption on port 5290. To use this protocol \"\n\"you need to use library which supports XMPP-over-WebSocket protocol.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:100\nmsgid \"Tip #1 - Encrypted WebSocket Connection\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:102\nmsgid \"\"\n\"It is possible to enable encrypted WebSocket connection in Tigase XMPP \"\n\"Server. To do this you need to add following lines to ``etc/config.tdsl``\"\n\" config file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:120\nmsgid \"\"\n\"In this example we enabled WebSocket endpoint on port 5290 allowing \"\n\"unencrypted connections, and encrypted WebSocket endpoint on port 5291. \"\n\"As this is TLS/SSL connection (no STARTTLS) it uses default certificate \"\n\"installed in Tigase XMPP Server instance. This certificate is located in \"\n\"``certs/default.pem``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:124\nmsgid \"\"\n\"There is no default configuration for non-default ports. All ports \"\n\"outside 443 MUST be configured.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:126\nmsgid \"Tip #2 - Encrypted WebSocket Connection - Dealing With Multiple VHosts\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:128\nmsgid \"\"\n\"As mentioned in Tip #1 WebSocket endpoint is plain TLS/SSL port, so it \"\n\"always serves default certificate for Tigase XMPP Server instance. That \"\n\"is ok if we are hosting single domain and if default certificate matches \"\n\"matches our domain. But If we host multiple domain we cannot use \"\n\"``wss://example1.com:5291/`` connection URL, if our default certificate \"\n\"is for domain ``example2.com``. In this situation it is recommended to \"\n\"use the default certificate for the domain under which the server is \"\n\"accessible from the internet. This domain should identify this server, so\"\n\" this domain would not point to two nodes of a cluster. After we deploy \"\n\"separate certificate for each of cluster nodes, we should follow same tip\"\n\" as Tip #1 for BOSH. Our web-based XMPP client should have knowledge \"\n\"about each node of a cluster and when it needs to connect it should \"\n\"randomly select one node from list of available cluster nodes and try to \"\n\"connect using connection URL that would contain name of server under \"\n\"which it can be identified from internet.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:132\nmsgid \"\"\n\"We have servers ``t1.example1.com`` and ``t2.example1.com`` which are \"\n\"nodes of a cluster in hosting domain ``example2.com``. Each of our nodes \"\n\"contains default SSL certificate with domain names matching the cluster \"\n\"node. Web client retrieves list of cluster nodes from web server and then\"\n\" when it needs to connect to XMPP server it picks random host from list \"\n\"of retrieved cluster nodes (i.e. ``t2.example1.com``) and tries to \"\n\"connect using WebSocket encrypted protocol to host ``t2.example1.com`` \"\n\"using the following URL: ``wss://t2.example1.com:5291/``. Upon connection\"\n\" the client should still send example2.com as name of server to which it \"\n\"tries to connect (``example2.com`` should be value of to attribute of \"\n\"XMPP stream). This will allow browser to validate certificate as it will \"\n\"be for the same domain to which browser connects, and it will allow XMPP \"\n\"client to connect to domain ``example2.com``, which is one of hosted \"\n\"vhosts.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:2\nmsgid \"Licensing\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:4\nmsgid \"\"\n\"With the release of v7.1.0, users and commercial clients alike may now be\"\n\" able to register and request a license file from our servers on their \"\n\"own. This process makes it easier for everyone to obtain valid license \"\n\"file when needed. Users who do not wish to register will not be required \"\n\"to register. However, If you are using Tigase ACS or other commercial \"\n\"pieces of software, you will be required to register.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:8\nmsgid \"\"\n\"Tigase XMPP Server will shut down during license check if no \"\n\"installation-id or license is received within a given period of time.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:10\nmsgid \"\"\n\"**Again, Tigase XMPP Server will still be available free under AGPLv3, \"\n\"and free users will not need to register.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:14\nmsgid \"COMMERCIAL COMPONENTS REQUIRE THE USE OF A LICENSE.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:17\nmsgid \"Registering for a License\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:19\nmsgid \"\"\n\"There are currently two ways for registering for a license with Tigase \"\n\"commercial products. **The easiest and recommended method is using the \"\n\"built in automatic registration function**. However, you may also \"\n\"register via a web portal if your installation has limitations on network\"\n\" connectivity.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:24\nmsgid \"Automatic Registration (recommended)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:26\nmsgid \"\"\n\"Once a commercial component is activated on Tigase XMPP Server, the \"\n\"program will then retrieve an *Installation ID* from our servers, and \"\n\"make a file called ``installation-id`` in your ``etc/`` directory \"\n\"including the *Installation ID* for your instance. An installation ID is \"\n\"generated using the complete cluster map and all machines within the same\"\n\" cluster should have the same *Installation ID*. This *Installation ID* \"\n\"will then be sent along with server details to a license server, and \"\n\"appropriate license files will be made in your *tigasedir/etc* directory.\"\n\" When the license is due to be expired, this mechanism will update your \"\n\"license file automatically.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:29\nmsgid \"Manual\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:33\nmsgid \"\"\n\"This method should be used only in extreme cases when :ref:`Automatic \"\n\"Registration (recommended)<AutomaticLicenceRegistration>` can’t be used.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:35\nmsgid \"\"\n\"If you do not wish to use the automatic method, you may decide to \"\n\"generate a license file using our web portal. Offline installation may \"\n\"obtain *Installation IDs* from our web portal in a three-step process: \"\n\"registration, generating hash, and obtaining license file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:37\nmsgid \"Generating Installation ID\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:39\nmsgid \"\"\n\"For offline installations, you may obtain an *Installation ID* from this \"\n\"address: https://license.tigase.software/register.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:41\nmsgid \"Data Fields:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:43\nmsgid \"\"\n\"``Customer name``: Company or user name used to identify machines. \"\n\"Multiple clusters or servers can have the same customer name.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:45\nmsgid \"\"\n\"``VHosts``: Comma separated list of VHosts you will be using on this \"\n\"node. NOTE: these fields are case sensitive!\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:47\nmsgid \"\"\n\"``Legacy license hashes``: Copy the digest hash generated for all legacy \"\n\"licenses - it’s available in the ``etc/tigase-console.log`` after startup\"\n\" (if such licenses are present).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:49\nmsgid \"\"\n\"``Captcha question``: Enter the basic math answer for this form to prove \"\n\"you are not a robot.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:51\nmsgid \"The next page will provide you with an installation ID like the following:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:57\nmsgid \"Edit your ``config.tdsl`` file and add your installation-id\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:63\nmsgid \"\"\n\"Note that the ``installation-id`` file will be made automatically once \"\n\"the license file is installed and verified by the server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:65\nmsgid \"Obtaining a Server Code\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:67\nmsgid \"\"\n\"Once you have the *Installation ID*, you will need to generate a server \"\n\"code. This can be done by accessing the admin UI page and navigating to \"\n\"the License section. Once there, click on Retrieve code for license. \"\n\"Select the component you wish to generate a code for and click Submit. \"\n\"You will see a fields with installation-id, module, VHosts filled out \"\n\"based on your server’s configuration. Copy the contents of the Code field\"\n\" and proceed to the next section.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:69\nmsgid \"Obtaining license file\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:71\nmsgid \"\"\n\"Open a new browser and navigate to this address: \"\n\"https://license.tigase.software/retrieve once there, paste the generated \"\n\"code from the last step in the field and click submit. Afterwards you \"\n\"will be prompted to download a license file, place this file in your \"\n\"*etc/* folder and restart your server, your license is now activated and \"\n\"installed on your server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:73\nmsgid \"\"\n\"**If you are provided a manually produced license, you will need to place\"\n\" it in the same** ``etc/`` **directory with the name** \"\n\"``<component_name>.license`` (**e.g.:** ``etc/acs.license``)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:76\nmsgid \"What happens if I do not use a license file or it is expired?\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:78\nmsgid \"\"\n\"Tigase permits commercial products to be used without a license, but a \"\n\"validation process must complete otherwise the server will shutdown. \"\n\"Within the first hour of runtime, Tigase will check for the presence and \"\n\"validity of the license file. If none is found, or it is invalid or \"\n\"expired the server will then contact Tigase master server in order to \"\n\"obtain a valid one.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:80\nmsgid \"\"\n\"Communications will be made to license.tigase.software over https (port \"\n\"443) to verify the license or download a new one.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:83\nmsgid \"Demo mode\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:85\nmsgid \"\"\n\"If no valid license can be found, Tigase will revert to a demonstration \"\n\"mode. Most functions will be available and usable, but with a caveat. \"\n\"Statistics from that server will be sent to https://stats.tigase.software\"\n\" about your server and it’s usage. Details are in the next section. If \"\n\"this information cannot be sent, the server will assume unauthorized use \"\n\"and will shut down.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:88\nmsgid \"Statistics Sent\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:90\nmsgid \"\"\n\"Statistics of your server may be sent to Tigase server’s if the all of \"\n\"following happens:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:92\nmsgid \"You are using commercial Tigase components.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:94\nmsgid \"You have registered an ``installation-id``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:96\nmsgid \"You do not have a current license to run Tigase commercial components.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:98\nmsgid \"\"\n\"If these conditions exist, statistics will be sent to our servers and a \"\n\"warning will be posted in your logs. The following is an example of what \"\n\"information will be sent.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:102\nmsgid \"\"\n\"The text below has been better formatted for readability, but does not \"\n\"reflect the actual text being sent to Tigase.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:251\nmsgid \"Unauthorized use\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:253\nmsgid \"\"\n\"Tigase will consider itself unauthorized if the following conditions are \"\n\"met:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:255\nmsgid \"if Tigase XMPP Server does not have a valid license file and\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:257\nmsgid \"\"\n\"cannot contact the licensing server to obtain installation id and \"\n\"attached licenses.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:259\nmsgid \"Then the program will then attempt to send statistics.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:261\nmsgid \"if unable to sent statistics the server after a random number of retries.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:263\nmsgid \"\"\n\"if these retries are not successful within 10 attempts, the server will \"\n\"then shutdown.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:265\nmsgid \"If you are experiencing this condition, please contact Tigase.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:268\nmsgid \"Manual mode\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:270\nmsgid \"\"\n\"If you cannot open communication to ``stats.tigase.software`` or \"\n\"``license.tigase.software`` over the required ports (https over port \"\n\"443), you may request to use manual mode. Manual mode requires Tigase to \"\n\"create a license file to be used on your machine locally. This must be \"\n\"placed in the same folder as the above information, and the license check\"\n\" system will not seek communication unless the license is invalid or \"\n\"expired.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:2\nmsgid \"Tigase Clustering\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:4\nmsgid \"\"\n\"Tigase Clustering allows the use of a number of servers to be unified in \"\n\"delivering, from what a client or user sees, a single unified platform. \"\n\"There are two typical reasons why clustering should be employed:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:6\nmsgid \"High Availability\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:12\nmsgid \"Load Balancing\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:18\nmsgid \"With Tigase, you don’t have to choose between either/or!\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:20\nmsgid \"\"\n\"**Tigase Clustering** offers **Full Redundancy** and **Automatic Load \"\n\"Balancing** allowing addition of new nodes at runtime with a simple \"\n\"configuration. All without a severe tax on resource consumption.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:22\nmsgid \"\"\n\"All basic components support clustering configuration, and some may be \"\n\"turned on or off.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:27\nmsgid \"\"\n\"To enable Clustering on Tigase servers, use the following line in your \"\n\"``config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:33\nmsgid \"That’s it!\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:36\nmsgid \"Custom Ports\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:38\nmsgid \"\"\n\"You can customize ports for the cluster component, just be sure that each\"\n\" clustered server also has the same settings so they can communicate.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:49\nmsgid \"\"\n\"You can fine tune each port configuration, however this is not typically \"\n\"needed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:52\nmsgid \"Custom Port Configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:54\nmsgid \"\"\n\"Each port has it’s own details that can be manipulated via the following \"\n\"ports. Again **THIS IS OPTIONAL**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:76\nmsgid \"Multi-node configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:78\nmsgid \"\"\n\"Each node should have ``'cluster-mode' = true`` enabled that you wish to \"\n\"connect to the cluster. They will automatically discover other nodes to \"\n\"connect to VIA Server to Server traffic. Nodes that are added or removed \"\n\"will be periodically updated.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:81\nmsgid \"Traffic Control\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:83\nmsgid \"\"\n\"You can customize the traffic going between clustered servers with a few \"\n\"options.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:85\nmsgid \"cm-ht-traffic-throttling\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:87\nmsgid \"\"\n\"This setting will control the number of bytes sent over non-user \"\n\"connections. Namely, Server to Server or S2S connections.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:93\nmsgid \"\"\n\"The format is as follows: ``{traffic-type}:{maximum-traffic}:{max-\"\n\"lifespan-traffic}:{action}``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:96\nmsgid \"**traffic-type**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:96\nmsgid \"\"\n\"Specifies the type of traffic controlled. This can either be **XMPP** or \"\n\"**bin**. XMPP limits the number of packets transferred, whereas bin \"\n\"limits the number of bytes transferred.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:99\nmsgid \"**maximum-traffic**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:99\nmsgid \"Specifies how many bytes or packets may be sent within one minute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:102\nmsgid \"**max-lifespan-traffic**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:102\nmsgid \"\"\n\"Specifies how many bytes or packets may be sent within the lifetime of \"\n\"the connection. 0 means unlimited.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:105\nmsgid \"**action**\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:105\nmsgid \"\"\n\"Specifies the action to be taken which can be **disc** which disconnects \"\n\"the connection, or **drop** which will drop any data exceeding the \"\n\"thresholds.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:107\nmsgid \"cm-see-other-host\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:109\nmsgid \"\"\n\"This allows the specific use of a load balancing mechanism by selecting \"\n\"``SeeOtherHostIfc`` implementation. For more details, see :ref:`Tigase \"\n\"Load Balancing<loadBalancing>` documentation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:112\nmsgid \"Old configuration method\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:114\nmsgid \"\"\n\"While these options are still available these settings CAN be less \"\n\"reliable. **Use ONLY if you need specific setups that cannot be \"\n\"accommodated by the automatic cluster mode**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:118\nmsgid \"Specifying Specific nodes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:120\nmsgid \"\"\n\"You can still use the old method of specifying every node on each server.\"\n\" Server 3 needs the following set\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:126\nmsgid \"Server 2 needs\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:132\nmsgid \"and so on…​\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:134\nmsgid \"However, we do not recommend this.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:136\nmsgid \"Password and Port configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:138\nmsgid \"\"\n\"You may specify a password and port to specific cluster servers if that \"\n\"is required. To do so, you will need to add {password}:{port} to the \"\n\"domain, like this example:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:147\nmsgid \"Checking Cluster Connections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:149\nmsgid \"\"\n\"After setting up clustering you may want to verify that the clusters are \"\n\"operational. Right now it can be done in two manners - first by checking \"\n\"that there are actual network connections established between cluster \"\n\"nodes. The other is to check internal status of the server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:152\nmsgid \"Established connections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:154\nmsgid \"\"\n\"There are number of ways to check for opened connections, simplest one \"\n\"use command line. (Tigase uses port *5277* for cluster connections)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:156\nmsgid \"Linux\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:162\nmsgid \"Windows\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:170\nmsgid \"Cluster nodes connected (using XMPP)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:172\nmsgid \"\"\n\"Verifying clustering connectivity over XMPP protocol requires any XMPP \"\n\"client capable of `XEP-0030: Service Discovery \"\n\"<http://xmpp.org/extensions/xep-0030.html>`__. It’s essential to remember\"\n\" that only an administrator (a user whose JID is configured as \"\n\"administrative) has access.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:174\nmsgid \"Psi XMPP Client\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:176\nmsgid \"\"\n\"For the purpose of this guide a `Psi <http://psi-im.org/>`__ client will \"\n\"be used. After successfully configuring and connecting to account with \"\n\"administrative privileges we need to access *Service Discovery*, either \"\n\"from application menu or from context menu of the particular account \"\n\"account:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:178\nmsgid \"|roster-discovery|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:184\nmsgid \"roster-discovery\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:180\nmsgid \"\"\n\"In the *Service Discovery* window we need to find *Cluster Connection \"\n\"Manager* component. After expanding the tree node for the component a \"\n\"list of all cluster nodes will be presented with the current status \"\n\"(either *connected* or *disconnected*). Node column will contain actual \"\n\"hostname of the cluster node:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:182\nmsgid \"|discovery-nodes|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:185\nmsgid \"discovery-nodes\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:2\nmsgid \"Anonymous Users & Authentication\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:4\nmsgid \"\"\n\"To support anonymous users, you must first enable anonymous \"\n\"authentication on your server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:7\nmsgid \"Anonymous Authentication\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:9\nmsgid \"\"\n\"Tigase Server can support anonymous logins via SASL-ANONYMOUS in certain \"\n\"scenarios. This can be enabled on per-VHost basis by adjusting *Anonymous\"\n\" enabled* option as described in :ref:`Add and Manage Domains \"\n\"(VHosts)<addManageDomain>` This setting is false by default as SASL-\"\n\"ANONYMOUS may not be totally secure as users can connect without prior \"\n\"permission (username and password).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:12\nmsgid \"Anonymous User Features\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:14\nmsgid \"\"\n\"To connect to your server anonymously, you must use a client that \"\n\"supports anonymous authentication and users. Connect to the server with \"\n\"the name of the server as the username, and no password. For example, to \"\n\"connect anonymously to ``xmpp.example.com`` use the following \"\n\"credentials,\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:16\nmsgid \"Username: ``xmpp.example.com`` Password:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:18\nmsgid \"\"\n\"In this mode all login information is stored in memory, and cannot be \"\n\"retrieved at a later date.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:20\nmsgid \"Other features of Anonymous Authentication\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:22\nmsgid \"Temporary Jid is assigned and destroyed the moment of login/logout.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:24\nmsgid \"Anonymous users cannot access the database\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:26\nmsgid \"\"\n\"Anonymous users cannot communicate outside the server (use s2s \"\n\"connections)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:28\nmsgid \"Anonymous users have a default limit on traffic generated per user.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:31\nmsgid \"Reconnection on Anonymous\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:33\nmsgid \"\"\n\"On products such as our JaXMPP Server, users connected using SASL-\"\n\"ANONYMOUS can reconnect to existing sessions using cookie management. \"\n\"However, reconnection can be improved and extended using `Bosh Session \"\n\"Cache <http://docs.tigase.org/tigase-\"\n\"server/snapshot/Development_Guide/html/#boshsessioncache>`__ which allows\"\n\" for session storage in memory rather than using client-side data for \"\n\"reconnection.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Scripting_Support.inc:4\nmsgid \"Scripting support in Tigase\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Scripting_Support.inc:6\nmsgid \"\"\n\"Tigase server supports scripting languages in versions 4.3.1 and higher. \"\n\"These pages describe this feature in details how to create new scripts, \"\n\"upload them to the server, and execute them. The guide also contains API \"\n\"description with code examples.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Scripting_Support.inc:10\nmsgid \"\"\n\"Tigase server is known for it very low memory consumption and \"\n\"successfully runs with less then 10MB of RAM memory. However adding \"\n\"scripting support for any non-standard (default) language to Tigase \"\n\"server significantly increases memory requirements for the installation. \"\n\"You cannot expect Tigase server to run on 10MB RAM system if you enabled \"\n\"Python, Scala or any other non-standard language.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:4\nmsgid \"Scripting Introduction - Hello World!\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:6\nmsgid \"\"\n\"This document is the first in a series describing scripting support in \"\n\"the Tigase server showing how to load, install, update and call a script.\"\n\" It contains also an introduction to the scripting API with the first \"\n\"*Hello world!* example.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:8\nmsgid \"\"\n\"Since Tigase version 4.3.1 the server supports scripting for \"\n\"administrator commands as well as standard commands.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:10\nmsgid \"\"\n\"In theory many different languages can be used to write scripts and the \"\n\"only requirement is that support `JSR-223 \"\n\"<http://www.jcp.org/en/jsr/detail?id=223>`__ for the language is \"\n\"installed. More details can be found on the `Java scripting project site \"\n\"<https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/prog_guide/api.html>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:12\nmsgid \"\"\n\"In practice some languages are better supported than others, at the \"\n\"moment we recommend `Groovy <http://groovy-lang.org/>`__. However the \"\n\"following languages are also confirmed to be working: `Scala <http://www\"\n\".scala-lang.org/>`__, `Python <http://www.python.org/>`__ and `Ruby \"\n\"<http://www.ruby-lang.org/>`__. The `tigase-server GitHub \"\n\"<https://github.com/tigase/tigase-server/blob/master/src/main>`__ \"\n\"contains a few examples for these languages.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:16\nmsgid \"\"\n\"the default Tigase installation contains only libraries for Groovy. \"\n\"Adding support for a different language is as simple as copying a few JAR\"\n\" files to the Tigase ``libs/`` directory.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:18\nmsgid \"\"\n\"All the examples presented in this guide are also available as ready to \"\n\"use scripts in the Tigase SVN repository in directory: \"\n\"`src/main/groovy/tigase/admin <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/groovy/tigase/admin>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:20\nmsgid \"\"\n\"The scripting utilizes only standard XMPP extensions and is by no means \"\n\"specific to any particular solution. We use and prefer Psi client. The \"\n\"whole guide and all the screen-shots are created using Psi client. You \"\n\"can, however, use any other client which supports these extensions as \"\n\"well. As the whole thing is based on the service discovery and ad-hoc \"\n\"commands you need a XMPP client with a good support for both features.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:22\nmsgid \"\"\n\"To follow the guide and run all the examples you need will need to have \"\n\"installed Tigase server version 4.3.1 or newer and you have to connect to\"\n\" the server as administrator.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:25\nmsgid \"Loading Script at Run Time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:27\nmsgid \"\"\n\"All the scripting stuff is usually based on the service discovery and ad-\"\n\"hoc commands in the Tigase server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:29\nmsgid \"|service disco|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:186\nmsgid \"service disco\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:31\nmsgid \"\"\n\"The first thing to do, therefore, is to browse service discovery on the \"\n\"running server. The result you receive will depend on your installation \"\n\"and installed components.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:33\nmsgid \"\"\n\"The most interesting things right now are all items with \"\n\"\\\"http://jabber.org/protocol/admin\\\" in their node part. You may have a \"\n\"few scripts loaded already but there are two commands used for scripting \"\n\"management. Their names are descriptive enough: ``New command script`` \"\n\"and ``Remove command script``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:35\nmsgid \"\"\n\"The first is for adding a new script or updating existing and the second \"\n\"is for removing script from the server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:37\nmsgid \"\"\n\"To add a new script you have just to execute ``New command script``. In \"\n\"Psi this is done by double clicking on the element in service discovery \"\n\"list.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:39\nmsgid \"|hello1 new script|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:187\nmsgid \"hello1 new script\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:41\nmsgid \"\"\n\"The screenshot above shows a couple of options to set for the loaded \"\n\"script:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:44\nmsgid \"Description\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:44\nmsgid \"\"\n\"is what shows as the script name in the service discovery window. There \"\n\"are no special restrictions on what to put there.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:47\nmsgid \"Command id\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:47\nmsgid \"\"\n\"is a unique ID of the script (admin command). This is what shows after \"\n\"the \\\"http://jabber.org/protocol/admin\\\" in node part. This needs to be \"\n\"unique or existing script is overwritten.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:50\nmsgid \"Language\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:50\nmsgid \"\"\n\"a drop down list of all supported scripting languages for your \"\n\"installation. Tigase automatically detects all libraries for scripting \"\n\"languages and lists them here. So all you need is to select the correct \"\n\"language for your script.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:53\nmsgid \"Script text\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:53\nmsgid \"is just your script content.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:55\nmsgid \"\"\n\"When your script is ready and all fields are correctly set, simply press \"\n\"\\\"**Finish**\\\" button and you should receive a message confirming that \"\n\"the script has been loaded successfully.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:57\nmsgid \"|loaded ok small|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:188\nmsgid \"loaded ok small\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:59\nmsgid \"\"\n\"In this guide we are creating a simple \\\"Hello world\\\" script written in \"\n\"Groovy. What it does is displays a window (ad-hoc command result) with a \"\n\"message: \\\"*Hello admin, how are you?*\\\".\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:61\nmsgid \"It uses a basic scripting API which is described line by line below:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:63\nmsgid \"It imports basic Tigase classes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:65\nmsgid \"\"\n\"Sets a local variable ``p`` which points to a ``packet`` variable with \"\n\"data received from the client.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:67\nmsgid \"\"\n\"Creates a ``res`` variable which is response sent back to the client \"\n\"(administrator). The response to the client is of type ``result``. Other \"\n\"possible types will be introduced later.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:69\nmsgid \"\"\n\"We operate on ad-hoc commands here so the script uses Tigase utility \"\n\"class to set/retrieve command parameters. It sets the window title and a \"\n\"simple message displayed to the user (administrator).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:71\nmsgid \"The last line returns new packet as a script execution result.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:73\nmsgid \"The first, very simple version looks like this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:85\nmsgid \"Executing Script\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:87\nmsgid \"\"\n\"Once the script is successfully loaded you will have to reload/refresh \"\n\"the service discovery window which now should display one more element on\"\n\" the list.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:89\nmsgid \"|service disco with new hello|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:189\nmsgid \"service disco with new hello\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:91\nmsgid \"\"\n\"As you can see script name is set to what you have entered as \"\n\"\\\"Description\\\" in script loading window - \\\"*Hello world script*\\\". The \"\n\"command node is set to: \\\"http://jabber.org/protocol/admin#hello\\\" if \"\n\"\\\"**hello**\\\" is what is set as the script ID.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:93\nmsgid \"\"\n\"To execute the script you just have to double click on the script name \"\n\"(or click execute command if you use any other client).\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:95\nmsgid \"\"\n\"As a result you should see a simple window similar to the screenshot \"\n\"below displaying our message.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:97\nmsgid \"|hello1 result small|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:190\nmsgid \"hello1 result small\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:100\nmsgid \"Interaction in Scripts\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:102\nmsgid \"\"\n\"Displaying just a message is very nice but is not very useful in most \"\n\"cases. Normally you need to ask the user for some more data or parameters\"\n\" before you can perform any real processing.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:104\nmsgid \"\"\n\"Therefore in most cases the administrator script has to display a new \"\n\"window with input fields asking the user for some more data. In this \"\n\"document we present very simple examples, just an introduction so let’s \"\n\"ask about the administrator name before displaying a greeting.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:106\nmsgid \"|hello2 asking for name small|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:191\nmsgid \"hello2 asking for name small\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:108\nmsgid \"\"\n\"To ask the user for some more information we have to extend example above\"\n\" with some more code:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:133\nmsgid \"\"\n\"If you compare both scripts you see that they are quite similar. Before \"\n\"displaying greeting, however, the script tries to retrieve data from the \"\n\"``name`` input field. If the name had been provided the greeting is \"\n\"displayed, otherwise the script asks for the user name.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:135\nmsgid \"|hello2 result small|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:192\nmsgid \"hello2 result small\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:137\nmsgid \"\"\n\"Please note, in this case the packet sent back to the user is of type \"\n\"form instead of ``result``. The practical difference is that the type \"\n\"``result`` displays only **OK** button which when pressed doesn’t send \"\n\"any data to the server. The form packet displays more buttons - \"\n\"**Finish** and **Cancel**. Whichever you press some data is sent back to \"\n\"the server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:139\nmsgid \"\"\n\"This script demonstrates use of two new methods from the utility class \"\n\"\\\"Command\\\": getFieldValue and addFieldValue.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:141\nmsgid \"\"\n\"The first argument to all Command methods is the packet with ad-hoc \"\n\"command.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:143\nmsgid \"The second argument is usually the input field name\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:145\nmsgid \"\"\n\"These two method parameters are actually enough to read the ad-hoc \"\n\"command data. Methods creating input fields in the ad-hoc command need a \"\n\"few arguments more:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:147\nmsgid \"\"\n\"Next arguments sets a default value displayed to the user. The way to it \"\n\"is set in the example above is specific to Groovy language and is quite \"\n\"useful what will be apparent in later examples.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:149\nmsgid \"\"\n\"After that we have to specify the field type. All field types are defined\"\n\" in the `XEP-0004 <http://xmpp.org/extensions/xep-0004.html#protocol-\"\n\"fieldtypes>`__ article.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:151\nmsgid \"\"\n\"The last argument specifies the field label which is displayed to the \"\n\"user.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:153\nmsgid \"|hello2 new script|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:193\nmsgid \"hello2 new script\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:155\nmsgid \"\"\n\"There are a few other different utility methods in the Command class to \"\n\"set different types of input fields and they will be described in details\"\n\" later on.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:157\nmsgid \"\"\n\"To reload the script simply call \\\"New command script\\\" again, enter the \"\n\"script text and make sure you entered exactly the same command ID to \"\n\"replace the old script with the new one.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:159\nmsgid \"\"\n\"Or of course, you can enter a new command id to create a new command and \"\n\"make it available on your server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:161\nmsgid \"\"\n\"When the script is loaded on the server, try to execute it. You should \"\n\"get a new dialog window asking for your name as in the screenshot at the \"\n\"beginning of this section. When you have entered your name and clicked \"\n\"the \\\"Finish\\\" button you will see another window with a greeting message\"\n\" along with your name.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:164\nmsgid \"Automatic Scripts Loading at Startup Time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:166\nmsgid \"\"\n\"The last thing described in this guide is how to automatically load your \"\n\"scripts when the Tigase server starts. The ability to load scripts at run\"\n\" time, update and remove remove them is very useful, especially in \"\n\"emergency cases if something wrong is going on and you want to act \"\n\"without affecting the service.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:168\nmsgid \"\"\n\"If you, however have a few dozens scripts you don’t want to manually load\"\n\" them every time the server restarts.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:170\nmsgid \"\"\n\"Tigase server automatically loads all scripts at the startup time which \"\n\"are located in the admin scripts directory. Unless you set it differently\"\n\" in the configuration it is: \"\n\"**YourTigaseInstallationDir/scripts/admin/**. All you have to do is to \"\n\"copy all your scripts to this directory and they will be loaded next time\"\n\" the server starts.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:172\nmsgid \"\"\n\"But hold on. What about the script parameters: language, description, \"\n\"command id? How are you supposed to set them?\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:174\nmsgid \"\"\n\"Language is simple. It is detected automatically by the script file \"\n\"extension. So just make sure file extensions are correct and the language\"\n\" is sorted.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:176\nmsgid \"\"\n\"The script description and command id needs a little bit more work. You \"\n\"have to include in your script following lines:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:184\nmsgid \"\"\n\"Please note, there must be at least a single space after the \"\n\"``AS:Description:`` or ``AS:CommandId:`` string. Everything rest after \"\n\"that, until the end of the line, is treated as either the script \"\n\"description or command id. Put these in your script file and the loader \"\n\"will detect them and set correctly for your script.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:4\nmsgid \"Tigase Scripting Version 4.4.x Update for Administrators\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:6\nmsgid \"\"\n\"Scripting functionality is quite useful in Tigase server for all sorts of\"\n\" administrator tasks. The possibility to load new scripts or replace old \"\n\"ones at the server runtime opens quite new area for the service \"\n\"maintenance.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:8\nmsgid \"\"\n\"In earlier versions of the Tigase server scripting capabilities was \"\n\"available only in the session manager component while it might be very \"\n\"useful in many other places - connection managers, MUC, PubSub, \"\n\"VHostManager and what even more important in completely new, custom \"\n\"components created for specific needs. It would be quite wasteful to \"\n\"reinvent the wheel every time and implementing scripting capabilities for\"\n\" each component separately.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:10\nmsgid \"\"\n\"Therefore the scripting capabilities has been implemented in the core of \"\n\"the Tigase server. It is now part of the API and is automatically \"\n\"available to all components without any additional coding. A detailed \"\n\"developer guide will be published separately.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:12\nmsgid \"\"\n\"This document describes changes from the user/administrator perspective \"\n\"because there are some usability changes related to the new \"\n\"implementation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:14\nmsgid \"\"\n\"Please note. The description and screenshots are taken from the Psi \"\n\"client and most likely interface for ad-hoc commands and service \"\n\"discovery on other client looks different. I recommend to do some initial\"\n\" testing and experiments using Psi client and then switch to your \"\n\"preferred application for your day-to-day use.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:16\nmsgid \"\"\n\"As it always was in the Tigase you can access all the functions via XMPP \"\n\"service discovery on the server. However, as soon as you connect to the \"\n\"server you can see some changes there.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:18\nmsgid \"|new service disco admin|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:40\nmsgid \"new service disco admin\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:20\nmsgid \"\"\n\"There are no command on the list. They are hidden from the main service \"\n\"discovery list. You can see on the list only the server main components.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:22\nmsgid \"\"\n\"This had to be done for many reasons. One of them is, obviously, the \"\n\"cleaner access to the main server stuff. Another, probably more \"\n\"important, is to avoid a long list of commands for different components \"\n\"mixed together. Commands for different components can have the same \"\n\"name/description and they can even do similar things but they are \"\n\"executed on a different server component. To avoid any confusion and \"\n\"minimize opportunities for mistake the commands are now closely tight to \"\n\"their components. To access a list of commands for a particular component\"\n\" you have to double click on the component name on the list or click \"\n\"'Execute command\\\" icon on top of the window when your component is \"\n\"selected.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:24\nmsgid \"\"\n\"A new window should show up with drop-down list of available commands. \"\n\"All the commands are related to the selected component and are executed \"\n\"kind of \\\"inside the component environment\\\". You can of course add new \"\n\"command or delete existing one and of course execute any of the commands \"\n\"showing on the list.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:26\nmsgid \"|new command list|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:41\nmsgid \"new command list\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:28\nmsgid \"\"\n\"As a reminder, in the window title you can see the component ID and you \"\n\"should check it before running any command to make sure you accidentally \"\n\"don’t break your system.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:30\nmsgid \"|new add command|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:42\nmsgid \"new add command\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:32\nmsgid \"\"\n\"There has been also a small change made to the script adding window. As \"\n\"you can see on the screenshot there is one additional option added - \"\n\"\\\"Save to disk\\\". This means that once you submitted the script to the \"\n\"server it is written to the hard drive and will be automatically loaded \"\n\"at next startup time.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:34\nmsgid \"\"\n\"This option is enabled by default as this seems to be a logical choice \"\n\"that the administrator wants to save his new script for later reuse. \"\n\"This, however requires proper configuration of the server and give \"\n\"writing permission to the directory where all scripts are stored. \"\n\"Otherwise the server won’t be able to write script files on the hard \"\n\"drive.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:36\nmsgid \"\"\n\"As in previous version only users with administrator permissions can \"\n\"execute commands and access all the critical elements on the server. \"\n\"There has been, however, another change made, long time requested by \"\n\"users. In the new version all the administrator specific elements are \"\n\"hidden for the rest of users.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:38\nmsgid \"\"\n\"Server components don’t show up on the service discovery, the user can’t \"\n\"see administrator commands nor he can execute them. This hasn’t been \"\n\"implemented to improve the server security but to reduce confusion for \"\n\"general users who would otherwise see a lot of stuff which can’t be used \"\n\"by them anyway.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:4\nmsgid \"Tigase and Python\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:6\nmsgid \"\"\n\"This article describes how to get Python working as a scripting language \"\n\"for ad-hoc commands in Tigase server. The first part is installation, and\"\n\" the second shows a few code examples with explanation of the differences\"\n\" between Python usage and some other languages.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:8\nmsgid \"\"\n\"*Please note, we are not a Python developer, and by no means this is \"\n\"Python development guide. All the code examples are used only to present \"\n\"the API available and there are certainly better ways to do it in the \"\n\"proper Python style. If you have any suggestions or have a better code \"\n\"examples I am happy to include them in the guide.*\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:11\nmsgid \"Installation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:13\nmsgid \"\"\n\"In short, installation is extremely simple: just copy the file attached \"\n\"to this article to your Tigase installation, to the ``libs/`` directory. \"\n\"Restart the server and you are ready to start scripting and executing \"\n\"Python.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:15\nmsgid \"\"\n\"In theory the Tigase offers scripting support defined in `JSR-223 \"\n\"<http://www.jcp.org/en/jsr/detail?id=223>`__. You can use any language \"\n\"for which there is such support for JVM. This includes also stand-alone \"\n\"python implementations and the JSR-223 plugins acts just as a bridge. \"\n\"This, however, does not make much sense as you are not able to interact \"\n\"with JVM code (Tigase API). Therefore you need a language which is \"\n\"executed within JVM and can easily exchange data between the main \"\n\"application (Tigase server) and the script.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:17\nmsgid \"|lang list no python small|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:69\nmsgid \"lang list no python small\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:19\nmsgid \"\"\n\"The best way to go is to use Jython implementation. It works very well \"\n\"within JVM and more importantly, perfectly integrates with Tigase server.\"\n\" Tigase server is tested with **Jython-2.2.1** and is confirmed to work \"\n\"fine. Version **Jython-2.5.1** is recommended however, and all the \"\n\"examples are executed with this version installed. Please note, \"\n\"*Jython-2.5.0* does not work at all. Both supported versions can be \"\n\"downloaded from the `Jython website \"\n\"<http://wiki.python.org/jython/DownloadInstructions>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:21\nmsgid \"\"\n\"**Version 2.5.1** is a bit simpler to install. When you download and run \"\n\"the Jython installer, find ``jython.jar`` file in the directory where you\"\n\" installed Jython. Copy the file to the Tigase’s **libs/** directory and \"\n\"all is ready to go. Please note, this is the same file as the one \"\n\"attached to this article for your convenience.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:23\nmsgid \"\"\n\"**Version 2.2.1** needs a little bit more work. The first part is the \"\n\"same. It is not, however enough to copy the ``jython.jar`` file. One more\"\n\" file is necessary for the Jython to work with the Tigase server. You \"\n\"have to install JSR-223 engine separately. The binary file has to be \"\n\"unpacked and ``jython-engine.jar`` file needs to be copied to the Tigase \"\n\"``libs/`` directory.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:25\nmsgid \"\"\n\"The best way to check if the Jython is installed correctly and support \"\n\"for Python is enabled, is by trying to submit a new script to the Tigase \"\n\"server. Browser the server service discovery, select \\\"*Session \"\n\"manager*\\\" component and run \\\"*Execute command*\\\" function. A new window\"\n\" should show with a list of all available ad-hoc commands. Select \\\"*New \"\n\"command script*\\\" item and click \\\"*Execute*\\\". Ad-hoc command dialog \"\n\"windows should show up. One of the field is \\\"*Language*\\\" with pull down\"\n\" list of available scripting languages. If \\\"*python*\\\" is on the list it\"\n\" means everything is ok and support for Python is enabled.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:27\nmsgid \"|lang list with python small|\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:70\nmsgid \"lang list with python small\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:30\nmsgid \"Writing Python Scripts\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:32\nmsgid \"\"\n\"Python scripts work in a similar way to Groovy or other languages \"\n\"scripts, except one significant difference. You cannot call \\\"*return*\\\" \"\n\"from the script itself. Hence you cannot simply pass script results by \"\n\"calling \\\"*return*\\\" statement directly from the script.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:34\nmsgid \"\"\n\"To overcome the problem, Tigase offers another way to pass script \"\n\"execution results. It checks the value of a special variables on the \"\n\"script completion: “result” and “packet”. By assigning value to one of \"\n\"these variables the Python (or any other language) can pass execution \"\n\"results back to the Tigase server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:36\nmsgid \"\"\n\"``result`` allows to return simple text (or characters String) from the \"\n\"script.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:38\nmsgid \"\"\n\"``packet`` allows to return Packet instance which is send back to the \"\n\"user.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:40\nmsgid \"The simplest possible Python script may look like this one:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:42\nmsgid \"``result = \\\"Hello world!\\\"``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:44\nmsgid \"\"\n\"For instructions how to load and execute the script, please refer to the \"\n\":ref:`introductory article<Scripting-Introduction - Hello-World!>` for \"\n\"scripting in Tigase server. There were some minor changes in Tigase 4.4.0\"\n\" and later versions, so please have a look at the :ref:`article <Tigase-\"\n\"Scripting-Version-4.4.x-Update-for-Administrators>` describing new \"\n\"elements as well.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:46\nmsgid \"\"\n\"An example of a more advanced script asks the user for providing required\"\n\" parameters for the actual script execution:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:65\nmsgid \"\"\n\"Except this minor difference, the rest part of scripting in Python for \"\n\"the Tigase administrator commands is the same as all other languages. As \"\n\"all languages can return execution results via these special variables, \"\n\"it could be argued there is no difference at all.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:67\nmsgid \"\"\n\"In article *\\\"Component Implementation - Lesson 6 - Scripting Support\\\"* \"\n\"in Developer guide, I am going to present the Tigase server API available\"\n\" for scripting framework. My main language is Groovy as it offers the \"\n\"best integration with JVM and Tigase API, however I will try to include \"\n\"Python example code as well.\"\nmsgstr \"\"\n\n#~ msgid \"`Tigase Log Guide <#Tigase-Log-Guide>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Debugging Tigase <#Debuging-Tigase>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Basic System Checks <#Basic-System-Checks>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Add and Manage Domains <#Add-and-Manage-Domains-(VHosts)>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Presence Forwarding <#Presence-Forwarding>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Watchdog <#Watchdog>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Runtime Environment Tip <#Tigase-Tip-\"\n#~ \"Checking-the-Runtime-Environment>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Checking Cluster Connections <#Checking-Cluster-Connections>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Best Practices for Connecting to Tigase\"\n#~ \" XMPP server From Web Browser \"\n#~ \"<#Best-Practices-for-Connecting-to-\"\n#~ \"Tigase-XMPP-server-From-Web-Browser>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Scripting Support in Tigase <#Scripting-support-in-Tigase>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Scripting Introduction - Hello World! \"\n#~ \"<#Scripting-Introduction - Hello-World!>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Tigase Scripting Version 4.4.x Update \"\n#~ \"for Administrators <#Tigase-Scripting-\"\n#~ \"Version-4.4.x-Update-for-Administrators>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Tigase and Python Scripting <#Tigase-and-Python>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Configuration Wizards <#tigase3xconfiguration>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"using web-based admin management console - `Admin UI <#usingAdminUI>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"To use any of those ways, you \"\n#~ \"need to be an administrator of the\"\n#~ \" server, which means that you have\"\n#~ \" a XMPP account created on this \"\n#~ \"XMPP server and your account JID \"\n#~ \"is added to `the list of the \"\n#~ \"administrators <#admins>`__ in the Tigase \"\n#~ \"XMPP Server configuration file.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"This method should be used only in\"\n#~ \" extreme cases when `Automatic Registration\"\n#~ \" (recommended) <#AutomaticLicenceRegistration>`__ can’t\"\n#~ \" be used.\"\n#~ msgstr \"\"\n\n#~ msgid \"ref:`Debugging Tigase<Debuging-Tigase>`\"\n#~ msgstr \"\"\n\n#~ msgid \"ref:`Basic System Checks<Basic-System-Checks>`\"\n#~ msgstr \"\"\n\n#~ msgid \"ref:`Add and Manage Domains<addManageDomain>`\"\n#~ msgstr \"\"\n\n#~ msgid \"ref:`Presence Forwarding<Presence-Forwarding>`\"\n#~ msgstr \"\"\n\n#~ msgid \"ref:`Watchdog<Watchdog>`\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"ref:`Runtime Environment Tip<Tigase-Tip-\"\n#~ \"Checking-the-Runtime-Environment>`\"\n#~ msgstr \"\"\n\n#~ msgid \"ref:`Checking Cluster Connections<Checking-Cluster-Connections>`\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"ref:`Best Practices for Connecting to \"\n#~ \"Tigase XMPP server From Web Browser\"\n#~ \"<Best-Practices-for-Connecting-to-\"\n#~ \"Tigase-XMPP-server-From-Web-Browser>`\"\n#~ msgstr \"\"\n\n#~ msgid \"`Scripting Support in Tigase<Scripting-support-in-Tigase>`\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"ref:`Scripting Introduction - Hello World\"\n#~ \"!<Scripting-Introduction - Hello-World!>`\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"ref:`Tigase Scripting Version 4.4.x Update \"\n#~ \"for Administrators<Tigase-Scripting-Version-4.4.x\"\n#~ \"-Update-for-Administrators>`\"\n#~ msgstr \"\"\n\n#~ msgid \"ref:`Tigase and Python Scripting<Tigase-and-Python>`\"\n#~ msgstr \"\"\n\n#~ msgid \"ref:`Configuration Wizards<tigase3xconfiguration>`\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"The tigase.log files are where the \"\n#~ \"majority of logging will take place. \"\n#~ \"The rules for writing to these \"\n#~ \"longs can be manipulated by editing \"\n#~ \"files in the int.properties file. To \"\n#~ \"see how, see the `Debugging Tigase \"\n#~ \"<#debuggingTigase>`__ section of this manual\"\n#~ \" for more details about how to \"\n#~ \"turn on debug logging, and how to\"\n#~ \" manipulate log settings. Entries to \"\n#~ \"these logs are made in the \"\n#~ \"following format:\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"Previously, a configuration article is \"\n#~ \"available about `Linux settings for high\"\n#~ \" load systems <#linuxhighload>`__. This has\"\n#~ \" a description of basic settings \"\n#~ \"which are essential to successfully run\"\n#~ \" XMPP service for hundreds or \"\n#~ \"thousands of online users.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"SSL Certificate Management has been \"\n#~ \"implemented, and certificates can be \"\n#~ \"manipulated when in a .pem form. \"\n#~ \"For more details, see `Creating and \"\n#~ \"Loading the Server Certificate in pem\"\n#~ \" Files <#certspem>`__ section of \"\n#~ \"documentation for more information.\"\n#~ msgstr \"\"\n\n#~ msgid \"`Runtime Environment Tip <#tigaseTip_RuntimeEnvironment>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Best Practices for Connecting to Tigase\"\n#~ \" XMPP server From Web Browser \"\n#~ \"<#bestWebPrax>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"**If you are provided a manually \"\n#~ \"produced license, you will need to \"\n#~ \"place it in the same ``etc/`` \"\n#~ \"directory with the name \"\n#~ \"``<component_name>.license`` (e.g.: ``etc/acs.license``)**\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"This allows the specific use of a\"\n#~ \" load balancing mechanism by selecting \"\n#~ \"``SeeOtherHostIfc`` implementation. For more \"\n#~ \"details, see `Tigase Load Balancing \"\n#~ \"<#loadBalancing>`__ documentation.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"Tigase Server can support anonymous \"\n#~ \"logins via SASL-ANONYMOUS in certain \"\n#~ \"scenarios. This can be enabled on \"\n#~ \"per-VHost basis by adjusting *Anonymous \"\n#~ \"enabled* option as described in ` \"\n#~ \"Add and Manage Domains (VHosts)<#Add-\"\n#~ \"and-Manage-Domains-(VHosts)>`__ This setting \"\n#~ \"is false by default as SASL-\"\n#~ \"ANONYMOUS may not be totally secure \"\n#~ \"as users can connect without prior \"\n#~ \"permission (username and password).\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"For instructions how to load and \"\n#~ \"execute the script, please refer to \"\n#~ \"the `introductory article <#scriptingintro>`__ \"\n#~ \"for scripting in Tigase server. There\"\n#~ \" were some minor changes in Tigase\"\n#~ \" 4.4.0 and later versions, so please\"\n#~ \" have a look at the `article \"\n#~ \"<#newElements>`__ describing new elements as\"\n#~ \" well.\"\n#~ msgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-05-27 12:30-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:2\nmsgid \"About Tigase XMPP Server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:4\nmsgid \"\"\n\"**Tigase XMPP Server** is an **Open Source and Free (AGPLv3)** Java based\"\n\" server. The goals behind its design and implementation of the server \"\n\"are:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:6\nmsgid \"Make the server robust and reliable.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:8\nmsgid \"Make the server a secure communication platform.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:10\nmsgid \"Make a flexible server which can be applied to different use cases.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:12\nmsgid \"\"\n\"Make an extensible server which takes full advantage of XMPP protocol \"\n\"extensibility.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:14\nmsgid \"Make the server easy to setup and maintain.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:17\nmsgid \"Robust and reliable\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:19\nmsgid \"\"\n\"This means that the server can handle many concurrent \"\n\"requests/connections and can run for a long time reliably. The server is \"\n\"designed and implemented to handle millions of simultaneous connections.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:21\nmsgid \"\"\n\"It is not enough however to design and implement a high load server and \"\n\"hope it will run well. The main focus of the project is put in into \"\n\"testing. Tests are taken so seriously that a dedicated testing framework \"\n\"has been implemented. All server functions are considered as implemented \"\n\"only when they pass a rigorous testing cycle. The testing cycle consists \"\n\"of 3 fundamental tests:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:23\nmsgid \"**Functional tests** - Checking whether the function works at all.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:25\nmsgid \"\"\n\"**Performance tests** - Checking whether the function performs well \"\n\"enough.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:27\nmsgid \"\"\n\"**Stability tests** - Checking whether the function behaves well in long \"\n\"term run. It must handle hundreds of requests a second in a several hour \"\n\"server run.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:31\nmsgid \"Security\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:33\nmsgid \"\"\n\"There are a few elements of the security related to XMPP servers: secure \"\n\"data transmissions which is met by the implementation of **SSL** or \"\n\"**TLS** protocol, secure user authorization which is met by the \"\n\"implementation of **DIGEST** or **SASL** user authorization and secure \"\n\"deployment which is met by component architecture.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:35\nmsgid \"\"\n\"**Secure deployment** Tigase software installation does not impact \"\n\"network security. Companies usually have their networks divided into 2 \"\n\"parts: **DMZ** which is partially open to the outside world and the \"\n\"**Private network** which is closed to the outside world.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:37\nmsgid \"\"\n\"If the XMPP server is to provide an effective way of communication \"\n\"between company employees regardless if they are in a secure company \"\n\"office or outside (perhaps at a customer site), it needs to accept both \"\n\"internal and external connections. So the natural location for the server\"\n\" deployment is the **DMZ**. However, this solution has some \"\n\"considerations: each company has normally established network users base \"\n\"and integrated authorization mechanisms. However, that information should\"\n\" be stored outside the DMZ to protect internal security, so how to \"\n\"maintain ease of installation and system security?\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:39\nmsgid \"\"\n\"**Tigase server** offers a solution for such a case. With it’s component \"\n\"structure, Tigase can be easily deployed on any number machines and from \"\n\"the user’s point of view it is seen as a one logical XMPP server. In this\"\n\" case we can install a Session Manager module in the **private** network,\"\n\" and a Client Connection Manager with Server Connection Manager in the \"\n\"**DMZ**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:41\nmsgid \"\"\n\"Session Manager connects to **DMZ** and receives all packets from \"\n\"external users. Thus is can securely realize users authorization based on\"\n\" company authorization mechanisms.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:45\nmsgid \"Flexibility\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:46\nmsgid \"\"\n\"There are many different XMPP server implementations. The most prevalent \"\n\"are:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:48\nmsgid \"\"\n\"Used as a business communication platform in small and medium companies \"\n\"where the server is not under a heavy load. For such deployments security\"\n\" is a key feature.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:50\nmsgid \"\"\n\"For huge community websites or internet portal servers is, on the other \"\n\"hand, usually under very heavy load and has to support thousands or \"\n\"millions of simultaneous connections. For such a deployment we need a \"\n\"different level of security as most of the service is open to the public.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:52\nmsgid \"\"\n\"For very small community deployments or for small home networks the key \"\n\"factor is ease to deploy and maintain.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:54\nmsgid \"\"\n\"Architecture based on components provides the ability to run selected \"\n\"modules on separate machines so the server can be easily applied in any \"\n\"scenario.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:56\nmsgid \"\"\n\"For simple installation the server generates a config file which can be \"\n\"used straight away with very few modifications or none at all. For \"\n\"complex deployments though, you can tweak configurations to your needs \"\n\"and setup XMPP server on as many physical machines as you need.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:59\nmsgid \"Extensibility\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:61\nmsgid \"\"\n\"The world changes all the time as does user’s needs. The XMPP protocol \"\n\"has been designed to be extensible to make it easy to add new features \"\n\"and apply it to those different user’s needs. As a result, XMPP is a very\"\n\" effective platform not only for sending messages to other users, it can \"\n\"also be extended for sending instant notifications about events, a useful\"\n\" platform for on-line customer service, voice communication, and other \"\n\"cases where sending information instantly to other people is needed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:63\nmsgid \"\"\n\"**Tigase server** has been designed to be extensible using a modular \"\n\"architecture. You can easily replace components which do not fulfill your\"\n\" requirements with others better fitting your needs. But that is not all,\"\n\" another factor of extensibility is how easy is to replace or add new \"\n\"extensions. A great deal of focus has been put into the server design API\"\n\" to make it easy for other software developers to create extensions and \"\n\"implement new features.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:66\nmsgid \"Ease of Use\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:68\nmsgid \"\"\n\"Complex computer networks consisting of many servers with different \"\n\"services are hard to maintain. This requires employing professional staff\"\n\" to operate and maintain the network.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:70\nmsgid \"\"\n\"Not all networks are so complex however, most small companies have just a\"\n\" few servers for their needs with services like e-mail and a HTTP server.\"\n\" They might want to add an XMPP server to the collection of their \"\n\"services and don’t want to dedicate resources on setup and maintenance. \"\n\"For such users our default configuration is exactly what they need. If \"\n\"the operating system on the server is well configured, then Tigase should\"\n\" automatically pickup the correct hostname and be ready to operate \"\n\"immediately.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:72\nmsgid \"\"\n\"Tigase server is designed and implemented to allow dynamic \"\n\"reconfiguration during runtime so there is no need to restart the server \"\n\"each time you want to change configuration settings.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:74\nmsgid \"\"\n\"There are also interfaces and handlers available to make it easy to \"\n\"implement a web user interface for server monitoring and configuring.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:2\nmsgid \"XMPP Supported Extensions\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:4\nmsgid \"\"\n\"Based on `XEP-0387: XMPP Compliance Suites 2018 \"\n\"<https://xmpp.org/extensions/xep-0387.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:8\nmsgid \"Core Compliance Suite\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:10\nmsgid \"Table 1.Core Compliance Suite\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:10\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:58\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:14\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:39\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:54\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:89\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:106\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:301\nmsgid \"Support\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:14\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:39\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:54\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:89\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:106\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:301\nmsgid \"Specification\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:10\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:58\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:14\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:39\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:54\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:89\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:106\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:301\nmsgid \"Name\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:10\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:58\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:14\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:39\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:54\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:89\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:106\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:301\nmsgid \"Comment\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:14\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:16\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:18\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:20\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:22\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:24\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:26\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:28\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:30\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:32\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:34\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:36\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:38\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:40\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:42\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:44\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:46\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:48\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:60\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:62\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:64\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:66\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:68\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:70\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:72\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:74\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:76\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:16\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:20\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:22\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:24\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:26\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:28\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:30\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:41\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:43\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:45\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:56\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:58\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:60\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:62\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:64\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:66\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:68\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:70\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:72\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:74\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:76\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:78\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:80\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:91\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:93\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:95\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:97\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:108\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:110\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:112\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:114\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:116\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:118\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:120\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:122\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:124\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:126\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:128\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:130\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:132\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:134\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:136\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:138\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:140\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:142\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:144\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:146\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:148\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:150\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:152\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:154\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:156\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:158\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:160\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:162\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:164\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:166\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:168\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:170\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:172\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:174\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:176\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:178\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:180\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:182\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:186\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:188\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:190\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:192\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:194\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:196\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:198\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:200\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:202\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:204\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:206\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:208\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:210\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:212\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:214\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:216\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:218\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:220\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:222\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:224\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:226\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:228\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:230\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:232\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:234\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:236\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:238\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:240\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:242\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:244\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:246\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:248\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:250\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:252\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:254\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:256\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:258\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:260\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:262\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:264\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:266\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:268\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:270\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:272\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:274\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:276\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:278\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:280\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:282\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:284\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:286\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:288\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:290\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:292\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:294\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:303\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:305\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:309\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:311\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:313\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:315\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:317\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:319\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:321\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:323\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:325\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:327\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:329\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:331\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:333\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:335\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:337\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:339\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:341\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:343\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:345\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:347\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:349\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:351\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:353\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:355\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:357\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:359\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:361\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:363\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:365\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:367\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:369\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:371\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:373\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:375\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:377\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:379\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:381\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:383\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:385\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:387\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:389\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:391\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:393\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:395\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:397\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:399\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:401\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:403\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:405\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:407\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:409\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:411\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:413\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:415\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:417\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:419\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:421\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:423\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:425\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:427\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:429\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:431\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:433\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:435\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:437\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:439\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:441\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:443\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:445\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:447\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:449\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:451\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:453\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:455\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:457\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:459\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:461\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:463\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:465\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:467\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:469\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:471\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:473\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:475\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:477\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:479\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:481\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:483\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:485\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:487\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:489\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:491\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:493\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:495\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:497\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:499\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:501\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:503\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:505\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:507\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:509\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:511\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:513\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:515\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:517\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:519\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:521\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:523\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:525\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:527\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:529\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:531\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:533\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:535\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:537\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:539\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:541\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:543\nmsgid \"✓\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:16\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:56\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:303\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:305\nmsgid \"`RFC6120 <https://tools.ietf.org/html/rfc6120>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:16\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:303\nmsgid \"Extensible Messaging and Presence Protocol (XMPP): Core\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:18\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:307\nmsgid \"⍻\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:18\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:307\nmsgid \"`RFC7622 <https://tools.ietf.org/html/rfc7622>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:18\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:307\nmsgid \"Extensible Messaging and Presence Protocol (XMPP): Address Format\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:18\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:307\nmsgid \"We support previous version of the specification: RFC6122\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:20\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:313\nmsgid \"`RFC7590 <https://tools.ietf.org/html/rfc7590>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:20\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:313\nmsgid \"\"\n\"Use of Transport Layer Security (TLS) in the Extensible Messaging and \"\n\"Presence Protocol (XMPP)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:22\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:541\nmsgid \"`XEP-0368 <https://xmpp.org/extensions/xep-0368.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:22\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:541\nmsgid \"SRV records for XMPP over TLS\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:22\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:541\nmsgid \"Requires adding DNS entries pointing to port 5223\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:24\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:329\nmsgid \"`XEP-0030 <https://xmpp.org/extensions/xep-0030.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:24\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:329\nmsgid \"Service Discovery\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:26\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:391\nmsgid \"`XEP-0115 <https://xmpp.org/extensions/xep-0115.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:26\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:391\nmsgid \"Entity Capabilities\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:28\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:389\nmsgid \"`XEP-0114 <https://xmpp.org/extensions/xep-0114.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:28\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:389\nmsgid \"Jabber Component Protocol\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:30\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:429\nmsgid \"`XEP-0163 <https://xmpp.org/extensions/xep-0163.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:30\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:429\nmsgid \"Personal Eventing Protocol\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:34\nmsgid \"Web Compliance Suite\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:36\nmsgid \"Table 2.Web Compliance Suite\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:41\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:91\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:309\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:311\nmsgid \"`RFC7395 <https://tools.ietf.org/html/rfc7395>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:41\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:91\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:309\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:311\nmsgid \"\"\n\"An Extensible Messaging and Presence Protocol (XMPP) Subprotocol for \"\n\"WebSocket\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:43\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:477\nmsgid \"`XEP-0206 <https://xmpp.org/extensions/xep-0206.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:43\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:477\nmsgid \"XMPP Over BOSH\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:45\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:395\nmsgid \"`XEP-0124 <https://xmpp.org/extensions/xep-0124.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:45\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:395\nmsgid \"Bidirectional-streams Over Synchronous HTTP (BOSH)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:49\nmsgid \"IM Compliance Suite\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:51\nmsgid \"Table 3.Web Compliance Suite\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:56\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:305\nmsgid \"\"\n\"Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and \"\n\"Presence\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:58\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:369\nmsgid \"`XEP-0084 <https://xmpp.org/extensions/xep-0084.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:58\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:369\nmsgid \"User Avatar\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:60\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:419\nmsgid \"`XEP-0153 <https://xmpp.org/extensions/xep-0153.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:60\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:419\nmsgid \"vCard-Based Avatars\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:62\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:341\nmsgid \"`XEP-0054 <https://xmpp.org/extensions/xep-0054.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:62\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:341\nmsgid \"vcard-temp\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:64\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:515\nmsgid \"`XEP-0280 <https://xmpp.org/extensions/xep-0280.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:64\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:515\nmsgid \"Message Carbons\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:66\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:463\nmsgid \"`XEP-0191 <https://xmpp.org/extensions/xep-0191.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:66\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:463\nmsgid \"Blocking Command\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:68\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:331\nmsgid \"`XEP-0045 <https://xmpp.org/extensions/xep-0045.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:68\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:331\nmsgid \"Multi-User Chat\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:70\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:501\nmsgid \"`XEP-0249 <https://xmpp.org/extensions/xep-0249.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:70\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:501\nmsgid \"Direct MUC Invitations\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:72\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:335\nmsgid \"`XEP-0048 <https://xmpp.org/extensions/xep-0048.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:72\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:335\nmsgid \"Bookmarks\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:74\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:483\nmsgid \"`XEP-0223 <https://xmpp.org/extensions/xep-0223.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:74\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:483\nmsgid \"Persistent Storage of Private Data via PubSub\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:76\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:337\nmsgid \"`XEP-0049 <https://xmpp.org/extensions/xep-0049.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:76\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:337\nmsgid \"Private XML Storage\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:78\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:93\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:465\nmsgid \"`XEP-0198 <https://xmpp.org/extensions/xep-0198.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:78\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:93\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:465\nmsgid \"Stream Management\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:78\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:93\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:465\nmsgid \"Both ``Session Resumption`` and ``Stanza Acknowledgements``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:80\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:523\nmsgid \"`XEP-0313 <https://xmpp.org/extensions/xep-0313.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:80\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:523\nmsgid \"Message Archive Management\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:84\nmsgid \"Mobile Compliance Suite\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:86\nmsgid \"Table 4.Web Compliance Suite\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:95\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:535\nmsgid \"`XEP-0352 <https://xmpp.org/extensions/xep-0352.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:95\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:535\nmsgid \"Client State Indication\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:97\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:537\nmsgid \"`XEP-0357 <https://xmpp.org/extensions/xep-0357.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:97\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:537\nmsgid \"Push Notifications\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:101\nmsgid \"Non-Compliance Suite Extensions\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:103\nmsgid \"Table 5.Core Compliance Suite\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:108\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:315\nmsgid \"`XEP-0004 <https://xmpp.org/extensions/xep-0004.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:108\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:315\nmsgid \"Data Forms\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:110\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:317\nmsgid \"`XEP-0008 <https://xmpp.org/extensions/xep-0004.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:110\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:317\nmsgid \"IQ-Based Avatars\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:112\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:319\nmsgid \"`XEP-0012 <https://xmpp.org/extensions/xep-0012.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:112\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:319\nmsgid \"Last Activity\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:114\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:321\nmsgid \"`XEP-0013 <https://xmpp.org/extensions/xep-0013.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:114\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:321\nmsgid \"Flexible Offline Message Retrieval\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:116\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:323\nmsgid \"`XEP-0016 <https://xmpp.org/extensions/xep-0016.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:116\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:323\nmsgid \"Privacy Lists\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:118\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:325\nmsgid \"`XEP-0020 <https://xmpp.org/extensions/xep-0020.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:118\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:325\nmsgid \"Feature Negotiation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:120\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:327\nmsgid \"`XEP-0022 <https://xmpp.org/extensions/xep-0022.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:120\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:327\nmsgid \"Message Events\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:122\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:333\nmsgid \"`XEP-0047 <https://xmpp.org/extensions/xep-0047.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:122\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:333\nmsgid \"In-Band Bytestreams\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:124\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:339\nmsgid \"`XEP-0050 <https://xmpp.org/extensions/xep-0050.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:124\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:339\nmsgid \"Ad-Hoc Commands\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:126\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:343\nmsgid \"`XEP-0059 <https://xmpp.org/extensions/xep-0059.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:126\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:343\nmsgid \"Result Set Management\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:128\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:345\nmsgid \"`XEP-0060 <https://xmpp.org/extensions/xep-0060.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:128\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:345\nmsgid \"Publish-Subscribe\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:130\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:347\nmsgid \"`XEP-0065 <https://xmpp.org/extensions/xep-0065.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:130\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:347\nmsgid \"SOCKS5 Bytestreams\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:132\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:349\nmsgid \"`XEP-0066 <https://xmpp.org/extensions/xep-0066.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:132\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:349\nmsgid \"Out of Band Data\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:134\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:351\nmsgid \"`XEP-0068 <https://xmpp.org/extensions/xep-0068.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:134\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:351\nmsgid \"Field Standardization for Data Forms\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:136\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:353\nmsgid \"`XEP-0071 <https://xmpp.org/extensions/xep-0071.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:136\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:353\nmsgid \"XHTML-IM\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:138\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:355\nmsgid \"`XEP-0072 <https://xmpp.org/extensions/xep-0072.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:138\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:355\nmsgid \"SOAP Over XMPP\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:140\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:357\nmsgid \"`XEP-0077 <https://xmpp.org/extensions/xep-0077.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:140\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:357\nmsgid \"In-Band Registration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:142\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:359\nmsgid \"`XEP-0078 <https://xmpp.org/extensions/xep-0078.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:142\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:359\nmsgid \"Non-SASL Authentication\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:144\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:361\nmsgid \"`XEP-0079 <https://xmpp.org/extensions/xep-0079.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:144\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:361\nmsgid \"Advanced Message Processing\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:146\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:363\nmsgid \"`XEP-0080 <https://xmpp.org/extensions/xep-0080.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:146\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:363\nmsgid \"User Location\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:148\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:365\nmsgid \"`XEP-0082 <https://xmpp.org/extensions/xep-0082.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:148\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:365\nmsgid \"XMPP Date and Time Profiles\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:150\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:367\nmsgid \"`XEP-0083 <https://xmpp.org/extensions/xep-0083.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:150\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:367\nmsgid \"Nested Roster Groups\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:152\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:371\nmsgid \"`XEP-0085 <https://xmpp.org/extensions/xep-0085.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:152\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:371\nmsgid \"Chat State Notifications\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:154\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:373\nmsgid \"`XEP-0086 <https://xmpp.org/extensions/xep-0086.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:154\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:373\nmsgid \"Error Condition Mappings\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:156\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:375\nmsgid \"`XEP-0091 <https://xmpp.org/extensions/xep-0091.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:156\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:375\nmsgid \"Legacy Delayed Delivery\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:158\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:377\nmsgid \"`XEP-0092 <https://xmpp.org/extensions/xep-0092.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:158\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:377\nmsgid \"Software Version\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:160\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:379\nmsgid \"`XEP-0096 <https://xmpp.org/extensions/xep-0096.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:160\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:379\nmsgid \"File Transfer\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:162\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:381\nmsgid \"`XEP-0100 <https://xmpp.org/extensions/xep-0100.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:162\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:381\nmsgid \"Gateway Interaction\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:164\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:383\nmsgid \"`XEP-0106 <https://xmpp.org/extensions/xep-0106.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:164\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:383\nmsgid \"JID Escaping\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:166\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:385\nmsgid \"`XEP-0107 <https://xmpp.org/extensions/xep-0107.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:166\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:385\nmsgid \"User Mood\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:166\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:168\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:170\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:385\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:387\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:393\nmsgid \"Server support via ``Personal Eventing Protocol (XEP-0163)``\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:168\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:387\nmsgid \"`XEP-0108 <https://xmpp.org/extensions/xep-0108.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:168\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:387\nmsgid \"User Activity\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:170\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:393\nmsgid \"`XEP-0118 <https://xmpp.org/extensions/xep-0118.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:170\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:393\nmsgid \"User Tune\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:172\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:399\nmsgid \"`XEP-0127 <https://xmpp.org/extensions/xep-0127.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:172\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:399\nmsgid \"Common Alerting Protocol (CAP) Over XMPP\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:174\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:397\nmsgid \"`XEP-0128 <https://xmpp.org/extensions/xep-0128.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:174\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:397\nmsgid \"Service Discovery Extensions\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:176\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:401\nmsgid \"`XEP-0131 <https://xmpp.org/extensions/xep-0131.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:176\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:401\nmsgid \"Stanza Headers and Internet Metadata (SHIM)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:178\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:403\nmsgid \"`XEP-0133 <https://xmpp.org/extensions/xep-0133.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:178\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:403\nmsgid \"Service Administration\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:180\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:405\nmsgid \"`XEP-0136 <https://xmpp.org/extensions/xep-0136.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:180\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:405\nmsgid \"Message Archiving\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:182\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:407\nmsgid \"`XEP-0141 <https://xmpp.org/extensions/xep-0141.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:182\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:407\nmsgid \"Data Forms Layout\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:12\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:184\nmsgid \"✓ [#]_\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:184\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:409\nmsgid \"`XEP-0142 <https://xmpp.org/extensions/xep-0142.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:184\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:409\nmsgid \"Workgroup Queues\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:186\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:411\nmsgid \"`XEP-0144 <https://xmpp.org/extensions/xep-0144.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:186\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:411\nmsgid \"Roster Item Exchange\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:188\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:413\nmsgid \"`XEP-0145 <https://xmpp.org/extensions/xep-0145.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:188\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:413\nmsgid \"Annotations\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:190\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:415\nmsgid \"`XEP-0146 <https://xmpp.org/extensions/xep-0146.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:190\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:415\nmsgid \"Remote Controlling Clients\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:192\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:417\nmsgid \"`XEP-0152 <https://xmpp.org/extensions/xep-0152.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:192\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:417\nmsgid \"Reachability Addresses\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:194\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:421\nmsgid \"`XEP-0155 <https://xmpp.org/extensions/xep-0155.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:194\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:421\nmsgid \"Stanza Session Negotiation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:196\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:423\nmsgid \"`XEP-0156 <https://xmpp.org/extensions/xep-0156.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:196\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:423\nmsgid \"Discovering Alternative XMPP Connection Methods\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:196\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:423\nmsgid \"Uses DNS records, so will work with Tigase XMPP Server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:198\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:425\nmsgid \"`XEP-0157 <https://xmpp.org/extensions/xep-0157.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:198\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:425\nmsgid \"Contact Addresses for XMPP Services\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:200\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:427\nmsgid \"`XEP-0160 <https://xmpp.org/extensions/xep-0160.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:200\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:427\nmsgid \"Best Practices for Handling Offline Messages\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:202\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:431\nmsgid \"`XEP-0166 <https://xmpp.org/extensions/xep-0166.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:202\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:431\nmsgid \"Jingle\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:204\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:433\nmsgid \"`XEP-0167 <https://xmpp.org/extensions/xep-0167.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:204\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:433\nmsgid \"Jingle RTP Sessions\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:206\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:435\nmsgid \"`XEP-0170 <https://xmpp.org/extensions/xep-0170.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:206\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:435\nmsgid \"Recommended Order of Stream Feature Negotiation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:208\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:437\nmsgid \"`XEP-0171 <https://xmpp.org/extensions/xep-0171.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:208\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:437\nmsgid \"Language Translation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:210\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:439\nmsgid \"`XEP-0172 <https://xmpp.org/extensions/xep-0172.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:210\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:439\nmsgid \"User Nickname\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:212\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:441\nmsgid \"`XEP-0174 <https://xmpp.org/extensions/xep-0174.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:212\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:441\nmsgid \"Serverless Messaging\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:214\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:443\nmsgid \"`XEP-0175 <https://xmpp.org/extensions/xep-0175.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:214\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:443\nmsgid \"Best Practices for Use of SASL ANONYMOUS\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:216\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:445\nmsgid \"`XEP-0176 <https://xmpp.org/extensions/xep-0176.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:216\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:445\nmsgid \"Jingle ICE-UDP Transport Method\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:218\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:447\nmsgid \"`XEP-0177 <https://xmpp.org/extensions/xep-0177.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:218\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:447\nmsgid \"Jingle Raw UDP Transport Method\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:220\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:449\nmsgid \"`XEP-0178 <https://xmpp.org/extensions/xep-0178.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:220\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:449\nmsgid \"Best Practices for Use of SASL EXTERNAL with Certificates\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:222\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:451\nmsgid \"`XEP-0179 <https://xmpp.org/extensions/xep-0179.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:222\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:451\nmsgid \"Jingle IAX Transport Method\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:224\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:453\nmsgid \"`XEP-0180 <https://xmpp.org/extensions/xep-0180.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:224\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:453\nmsgid \"Jingle Video via RTP\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:226\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:455\nmsgid \"`XEP-0181 <https://xmpp.org/extensions/xep-0181.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:226\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:455\nmsgid \"Jingle DTMF\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:228\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:457\nmsgid \"`XEP-0184 <https://xmpp.org/extensions/xep-0184.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:228\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:457\nmsgid \"Message Receipts\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:230\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:459\nmsgid \"`XEP-0185 <https://xmpp.org/extensions/xep-0185.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:230\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:459\nmsgid \"Dialback Key Generation and Validation\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:232\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:461\nmsgid \"`XEP-0190 <https://xmpp.org/extensions/xep-0190.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:232\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:461\nmsgid \"Best Practice for Closing Idle Streams\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:234\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:467\nmsgid \"`XEP-0199 <https://xmpp.org/extensions/xep-0199.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:234\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:467\nmsgid \"XMPP Ping\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:236\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:469\nmsgid \"`XEP-0201 <https://xmpp.org/extensions/xep-0201.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:236\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:469\nmsgid \"Best Practices for Message Threads\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:238\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:471\nmsgid \"`XEP-0202 <https://xmpp.org/extensions/xep-0202.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:238\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:471\nmsgid \"Entity Time\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:240\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:473\nmsgid \"`XEP-0203 <https://xmpp.org/extensions/xep-0203.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:240\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:473\nmsgid \"Delayed Delivery\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:242\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:475\nmsgid \"`XEP-0205 <https://xmpp.org/extensions/xep-0205.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:242\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:475\nmsgid \"Best Practices to Discourage Denial of Service Attacks\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:244\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:479\nmsgid \"`XEP-0209 <https://xmpp.org/extensions/xep-0209.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:244\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:479\nmsgid \"Metacontacts\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:246\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:481\nmsgid \"`XEP-0220 <https://xmpp.org/extensions/xep-0220.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:246\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:481\nmsgid \"Server Dialback\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:248\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:485\nmsgid \"`XEP-0224 <https://xmpp.org/extensions/xep-0224.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:248\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:485\nmsgid \"Attention\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:250\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:487\nmsgid \"`XEP-0225 <https://xmpp.org/extensions/xep-0225.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:250\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:487\nmsgid \"Component Connections\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:252\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:489\nmsgid \"`XEP-0226 <https://xmpp.org/extensions/xep-0226.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:252\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:489\nmsgid \"Message Stanza Profiles\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:254\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:491\nmsgid \"`XEP-0231 <https://xmpp.org/extensions/xep-0231.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:254\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:491\nmsgid \"Bits of Binary\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:256\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:493\nmsgid \"`XEP-0234 <https://xmpp.org/extensions/xep-0234.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:256\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:493\nmsgid \"Jingle File Transfer\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:258\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:495\nmsgid \"`XEP-0245 <https://xmpp.org/extensions/xep-0245.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:258\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:495\nmsgid \"The /me Command\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:260\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:497\nmsgid \"`XEP-0246 <https://xmpp.org/extensions/xep-0246.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:260\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:497\nmsgid \"End-to-End XML Streams\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:262\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:499\nmsgid \"`XEP-0247 <https://xmpp.org/extensions/xep-0247.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:262\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:499\nmsgid \"Jingle XML Streams\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:264\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:503\nmsgid \"`XEP-0250 <https://xmpp.org/extensions/xep-0250.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:264\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:503\nmsgid \"C2C Authentication Using TLS\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:266\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:505\nmsgid \"`XEP-0251 <https://xmpp.org/extensions/xep-0251.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:266\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:505\nmsgid \"Jingle Session Transfer\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:268\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:507\nmsgid \"`XEP-0260 <https://xmpp.org/extensions/xep-0260.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:268\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:507\nmsgid \"Jingle SOCKS5 Bytestreams Transport Method\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:270\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:509\nmsgid \"`XEP-0261 <https://xmpp.org/extensions/xep-0261.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:270\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:509\nmsgid \"Jingle In-Band Bytestreams Transport\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:272\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:511\nmsgid \"`XEP-0262 <https://xmpp.org/extensions/xep-0262.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:272\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:511\nmsgid \"Use of ZRTP in Jingle RTP Sessions\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:274\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:513\nmsgid \"`XEP-0277 <https://xmpp.org/extensions/xep-0277.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:274\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:513\nmsgid \"Microblogging over XMPP\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:276\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:517\nmsgid \"`XEP-0292 <https://xmpp.org/extensions/xep-0292.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:276\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:517\nmsgid \"vCard4 Over XMPP\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:278\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:519\nmsgid \"`XEP-0301 <https://xmpp.org/extensions/xep-0301.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:278\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:519\nmsgid \"In-Band Real Time Text\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:280\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:521\nmsgid \"`XEP-0305 <https://xmpp.org/extensions/xep-0305.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:280\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:521\nmsgid \"XMPP Quickstart\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:282\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:525\nmsgid \"`XEP-0323 <https://xmpp.org/extensions/xep-0323.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:282\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:525\nmsgid \"Internet of Things - Sensor Data\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:284\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:527\nmsgid \"`XEP-0324 <https://xmpp.org/extensions/xep-0324.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:284\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:527\nmsgid \"Internet of Things - Provisioning\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:286\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:529\nmsgid \"`XEP-0325 <https://xmpp.org/extensions/xep-0325.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:286\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:529\nmsgid \"Internet of Things - Control\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:288\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:531\nmsgid \"`XEP-0326 <https://xmpp.org/extensions/xep-0326.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:288\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:531\nmsgid \"Internet of Things - Concentrators\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:290\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:533\nmsgid \"`XEP-0333 <https://xmpp.org/extensions/xep-0333.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:290\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:533\nmsgid \"Chat Markers\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:292\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:539\nmsgid \"`XEP-0363 <https://xmpp.org/extensions/xep-0363.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:292\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:539\nmsgid \"HTTP File Upload\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:294\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:543\nmsgid \"`XEP-0387 <https://xmpp.org/extensions/xep-0387.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:294\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:543\nmsgid \"XMPP Compliance Suites 2018\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:298\nmsgid \"Full, ordered list of supported RFCs and XEPs:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:80\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:547\nmsgid \"Requires commercial license\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:2\nmsgid \"Tigase Custom Extensions\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:5\nmsgid \"General features\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:7\nmsgid \"tabel 6.Monitoring\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:12\nmsgid \"AuditLog\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:12\nmsgid \"\"\n\"Ability functionality to log important events in a system (loggins, \"\n\"message exchanges, calls)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:14\nmsgid \"Anti Abuse\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:14\nmsgid \"Fight stanza SPAM, DoS, brute-force attacks and other threats\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:16\nmsgid \"Virtual domains\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:16\nmsgid \"\"\n\"Ability to create and manage multiple virtual domains from a single \"\n\"instance and restart-less management\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:18\nmsgid \"MUC subscribe for offline push\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:18\nmsgid \"\"\n\"Option to register permanently to the room to receive push notifications \"\n\"about new messages.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:20\nmsgid \"Scripting API\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:20\nmsgid \"Supports the Java Scripting API JSR-223\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:22\nmsgid \"JMX monitoring\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:22\nmsgid \"\"\n\"Advanced monitoring the server via JMX protocol with an API for \"\n\"connecting custom monitors and TCP/IP end-point for connecting general \"\n\"purpose JMX tools\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:24\nmsgid \"HTTP monitoring\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:24\nmsgid \"Basic monitoring via HTTP protocol\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:26\nmsgid \"XMPP Monitoring\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:26\nmsgid \"\"\n\"Pluggable, active monitoring via XMPP, retrieving detailed server \"\n\"statistics, receiving automatic notifications about possible problems \"\n\"discovered by the self-monitor mechanisms\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:28\nmsgid \"SNMP Monitoring\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:28\nmsgid \"Advanced server monitoring via SNMP.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:30\nmsgid \"Bosh Cache\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:30\nmsgid \"\"\n\"Bosh Session Cache - a feature to quickly reload user data - roster, \"\n\"presences and messages history by the web client (for example after web \"\n\"page reload)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:32\nmsgid \"Clustering\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:32\nmsgid \"\"\n\"Full clustering support for HA and LB with pluggabble clustering \"\n\"strategies for perfect optimising the cluster to the client’s system\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:34\nmsgid \"Advanced Clustering Strategy\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:34\nmsgid \"Dedicated, specialised clustering strategy for best possible performance\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:36\nmsgid \"MUC Clustered\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:36\nmsgid \"Support for clustering group chatrooms with various, pluggable strategies\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:38\nmsgid \"PubSub Clustered\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:38\nmsgid \"Support for clustering PubSub component with various, pluggable strategies\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:40\nmsgid \"Mobile optimisations\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:40\nmsgid \"Optimizations designed for Mobile Devices\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:42\nmsgid \"OSGi\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:42\nmsgid \"\"\n\"Support for running in OSGi environment, i.e. as embedded XMPP server in \"\n\"advanced application server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:44\nmsgid \"Dynamic rosters\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:44\nmsgid \"\"\n\"Ability to create users' rosters entries on the fly based on data \"\n\"retrieved from any sources\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:46\nmsgid \"Command line admin tools\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:46\nmsgid \"Commandline utility to manage server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:48\nmsgid \"Unified Archive\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:48\nmsgid \"\"\n\"An extension to XEP-0313 Message Archive Management, with greatly \"\n\"improved flexibility in terms of what can be archived.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:53\nmsgid \"Repositories/Databases\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:55\nmsgid \"Table 7.Repositories/Databases\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:60\nmsgid \"DB per domain\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:60\nmsgid \"Ability to have multiple databases for specific domains.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:62\nmsgid \"PostgreSQL\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:62\nmsgid \"\"\n\"Full support for PostgreSQL database with database schemas excluding \"\n\"dedicated DB schema for PubSub component\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:64\nmsgid \"MySQL\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:64\nmsgid \"\"\n\"Full support for MySQL database with database schemas, dedicated DB \"\n\"schema for PubSub component\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:66\nmsgid \"SQL Server\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:66\nmsgid \"\"\n\"Full support for MS SQL Server database with database schemas excluding \"\n\"dedicated DB schema for PubSub component, only in Tigase server version \"\n\"3.x\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:68\nmsgid \"Derby DB\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:68\nmsgid \"\"\n\"Full support for built-in Derby database with database schemas excluding \"\n\"dedicated DB schema for PubSub component\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:70\nmsgid \"JDBC\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:70\nmsgid \"\"\n\"Support for all JDBC enabled databases, although the database schemas are\"\n\" available for some databases\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:72\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:74\nmsgid \"Drupal Auth\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:72\nmsgid \"\"\n\"Drupal authentication - the Tigase server can share user authentication \"\n\"database with Drupal CMS and authenticate users agains Drupal user \"\n\"database\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:74\nmsgid \"\"\n\"Close integration with Drupal CMS, the Tigase can send notifications to \"\n\"subscribed users about new posts, comments and can also publish short \"\n\"news information via XMPP\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:76\nmsgid \"LDAP-Auth\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:76\nmsgid \"LDAP Authentication Connector Supported\"\nmsgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Administration/index.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2023-02-16 15:01-0800\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.11.0\\n\"\n\n#: ../../Tigase_Administration/index.rst:3\nmsgid \"Administration Guide\"\nmsgstr \"\"\n\n#~ msgid \"Tigase Administration Guide\"\n#~ msgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Development/Basic_Information.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-05-27 12:30-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Basic_Information.rst:2\nmsgid \"Basic Information\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:5\nmsgid \"Tigase Architecture\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:7\nmsgid \"\"\n\"The most important thing to understand is that Tigase is very modular and\"\n\" you can have multiple components running inside single instance. However\"\n\" one of the most important components is MessageRouter, which sits in the\"\n\" centre and serves as a, as name suggest, packet router directing packets\"\n\" to the appropriate components.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:9\nmsgid \"\"\n\"There is also a group of specialised component responsible for handling \"\n\"users connections: ``ConnectionManagers`` (``c2s``, ``s2s``, ``ws2s``, \"\n\"``bosh``). They receive packets from the incoming connection, then \"\n\"subsequently they forward processed packet to ``MessageRouter``. Most of \"\n\"the time, especially for packets coming from user connections, packet is \"\n\"routed to ``SessionManager`` component (with the session object referring\"\n\" to appropriate user in case of client to server connection). After \"\n\"processing in ``SessionManager`` packet goes back to ``MessageRouter`` \"\n\"and then, based on the stanza addressing\\\\` can go to different component\"\n\" (muc, pubsub) or if it’s addressed to another user it can go through:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:11\nmsgid \"\"\n\"``SessionManager`` (again), ``MessageRouter`` and then (user) \"\n\"``ConnectionManagers`` or,\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:13\nmsgid \"\"\n\"``s2s`` (*server to server connection manager*) if the user or component \"\n\"is on the different, federated, xmpp server;\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:15\nmsgid \"In a very broad view this can be depicted with a following graph:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:17\nmsgid \"|Tigase architecture|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:90\nmsgid \"Tigase architecture\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:20\nmsgid \"Tigase Server Elements\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:22\nmsgid \"\"\n\"To make it easier to get into the code below are defined basic terms in \"\n\"the Tigase server world and there is a brief explanation how the server \"\n\"is designed and implemented. This document also points you to basic \"\n\"interfaces and implementations which can be used as example code \"\n\"reference.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:24\nmsgid \"\"\n\"Logically all server code can be divided into 3 kinds of modules: \"\n\"**components**, **plug-ins** and **connectors**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:26\nmsgid \"\"\n\"**Components** are the main element of Tigase server. Components are a \"\n\"bigger piece of code which can have separate address, receive and send \"\n\"stanzas, and be configured to respond to numerous events. Sample \"\n\"components implemented for Tigase server are: *c2s connection manager*, \"\n\"*s2s connection manager*, *session manager*, *XEP-0114 - external \"\n\"component connection manager*, *MUC - multi user char rooms*.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:28\nmsgid \"\"\n\"**Plug-ins** are usually small pieces of code responsible for processing \"\n\"specific XMPP stanzas. They don’t have their own address. As a result of \"\n\"stanza processing they can produce new XMPP stanzas. Plug-ins are loaded \"\n\"by *session manager* component or the *c2s connection manager* component.\"\n\" Sample plug-ins are: *vCard* stanza processing, *jabber:iq:register* to \"\n\"register new user accounts, *presence* stanza processing, and \"\n\"*jabber:iq:auth* for non-sasl authentication.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:30\nmsgid \"\"\n\"**Connectors** are modules responsible for access to data repositories \"\n\"like databases or LDAP to store and retrieve user data. There are 2 kinds\"\n\" of connectors: authentication connectors and user data connectors. Both \"\n\"of them are independent and can connect to different data sources. Sample\"\n\" connectors are: *JDBC database* connector, *XMLDB - embedded database* \"\n\"connector, *Drupal database* connector.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:32\nmsgid \"\"\n\"There is an API defined for each kind of above modules and all you have \"\n\"to do is enable the implementation of that specific interface. Then the \"\n\"module can be loaded to the server based on it’s configuration settings. \"\n\"There is also abstract classes available, implementing these interfaces \"\n\"to make development easier.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:34\nmsgid \"\"\n\"Here is a brief list of all interfaces to look at and for more details \"\n\"you have to refer to the guide for specific kind of module.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:37\nmsgid \"Components\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:39\nmsgid \"This is list of interfaces to look at when you work on a new component:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:41\nmsgid \"\"\n\"**tigase.server.ServerComponent** - This is the very basic interface for \"\n\"component. All components must implement it.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:43\nmsgid \"\"\n\"**tigase.server.MessageReceiver** - This interface extends \"\n\"``ServerComponent`` and is required to implement by components which want\"\n\" to receive data packets like *session manager* and *c2s connection \"\n\"manager*.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:45\nmsgid \"\"\n\"**tigase.conf.Configurable** - Implementing this interface is required to\"\n\" make it configurable. For each object of this type, configuration is \"\n\"pushed to it at any time at runtime. This is necessary to make it \"\n\"possible to change configuration at runtime. Be careful to implement this\"\n\" properly as it can cause issues for modules that cannot be configured.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:47\nmsgid \"\"\n\"**tigase.disco.XMPPService** - Objects using this interface can respond \"\n\"to \\\"ServiceDiscovery\\\" requests.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:49\nmsgid \"\"\n\"**tigase.stats.StatisticsContainer** - Objects using this interface can \"\n\"return runtime statistics. Any object can collect job statistics and \"\n\"implementing this interface guarantees that statistics will be presented \"\n\"in consisted way to user who wants to see them.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:51\nmsgid \"\"\n\"Instead of implementing above interfaces directly, it is recommended to \"\n\"extend one of existing abstract classes which take care of the most of \"\n\"\\\"dirty and boring\\\" stuff. Here is a list the most useful abstract \"\n\"classes:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:53\nmsgid \"**tigase.server.AbstractMessageReceiver** - Implements 4 basic interfaces:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:55\nmsgid \"\"\n\"``ServerComponent``, ``MessageReceiver``, ``Configurable`` and \"\n\"``StatisticsContainer``. AbstractMessageReceiver also manages internal \"\n\"data queues using it’s own threads which prevents dead-locks from \"\n\"resource starvation. It offers even-driven data processing which means \"\n\"whenever packet arrives the ``abstract void processPacket(Packet \"\n\"packet);`` method is called to process it. You have to implement this \"\n\"abstract method in your component, if your component wants to send a \"\n\"packet (in response to data it received for example).\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:61\nmsgid \"\"\n\"**tigase.server.ConnectionManager** - This is an extension of \"\n\"``AbstractMessageReceiver`` abstract class. As the name says this class \"\n\"takes care of all network connection management stuff. If your component \"\n\"needs to send and receive data directly from the network (like c2s \"\n\"connection, s2s connection or external component) you should use this \"\n\"implementation as a basic class. It takes care of all things related to \"\n\"networking, I/O, reconnecting, listening on socket, connecting and so on.\"\n\" If you extend this class you have to expect data coming from to sources:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:63\nmsgid \"\"\n\"From the ``MessageRouter`` and this is when the ``abstract void \"\n\"processPacket(Packet packet);`` method is called and second, from network\"\n\" connection and then the ``abstract Queue processSocketData(XMPPIOService\"\n\" serv);`` method is called.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:66\nmsgid \"Plug-ins\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:68\nmsgid \"\"\n\"All Tigase plugins currently implemented are located in package: \"\n\"tigase.xmpp.impl. You can use this code as a sample code base. There are \"\n\"3 types of plug-ins and they are defined in interfaces located in \"\n\"``tigase.xmpp`` package:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:70\nmsgid \"\"\n\"**XMPPProcessorIfc** - The most important and basic plug-in. This is the \"\n\"most common plug-in type which just processes stanzas in normal mode. It \"\n\"receives packets, processes them on behalf of the user and returns \"\n\"resulting stanzas.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:72\nmsgid \"\"\n\"**XMPPPreprocessorIfc** - This plugin performs pre-processing of the \"\n\"packet, intended for the pre-processors to setup for packet blocking.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:74\nmsgid \"\"\n\"**XMPPPostprocessorIfc** - This plugin performs processing of packets for\"\n\" which there was no specific processor.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:77\nmsgid \"Connector\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:80\nmsgid \"Data, Stanzas, Packets - Data Flow and Processing\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:82\nmsgid \"\"\n\"Data received from the network are read from the network sockets as bytes\"\n\" by code in the ``tigase.io`` package. Bytes then are changed into \"\n\"characters in classes of ``tigase.net`` package and as characters they \"\n\"are sent to the XML parser (``tigase.xml``) which turns them to XML DOM \"\n\"structures.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:84\nmsgid \"\"\n\"All data inside the server is exchanged in XML DOM form as this is the \"\n\"format used by XMPP protocol. For basic XML data processing (parsing \"\n\"characters stream, building DOM, manipulate XML elements and attributes) \"\n\"we use `Tigase XML parser and DOM builder <https://github.com/tigase\"\n\"/tigase-xmltools>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:86\nmsgid \"\"\n\"Each stanza is stored in the ``tigase.xml.Element`` object. Every Element\"\n\" can contain any number of **Child Elements** and any number of \"\n\"attributes. You can access all these data through the class API.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Basic_Information.rst:88\nmsgid \"\"\n\"To simplify some, most common operations Element is wrapped in \"\n\"``tigase.server.Packet`` class which offers another level of API for the \"\n\"most common operations like preparation of response stanza based on the \"\n\"element it contains (swap to/from values, put type=result attribute and \"\n\"others).\"\nmsgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Development/Cluster_Map_Interface.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-05-27 12:30-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:2\nmsgid \"Cluster Map Interface\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:4\nmsgid \"\"\n\"Starting with v7.1.0, a cluster map interface has been implemented. The \"\n\"cluster map is aided by use of the distributed event bus system to \"\n\"communicate between all clusters.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:7\nmsgid \"Requirements\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:9\nmsgid \"\"\n\"Any full distribution of Tigase will support the Cluster Map API so long \"\n\"as the eventbus component is not disabled. JDK v8 is required for this \"\n\"feature, however since Tigase requires this, you should already have it \"\n\"installed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:11\nmsgid \"\"\n\"The cluster map is stored in memory and follows the \"\n\"``map.util.interface`` java standards can be used to improve cluster \"\n\"connections, and help clustered servers keep track of each other.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:14\nmsgid \"Map Creation\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:16\nmsgid \"Map must be created with the following command:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:22\nmsgid \"\"\n\"Where \\\"type\\\" is the map ID. This creates the map locally and then fires\"\n\" an event to all clustered servers. Each cluster server has an event \"\n\"handler waiting for, in this case, ``NewMapCreate`` event. Map Key class \"\n\"and Map Value class are used to type conversion. Arrays of strings are \"\n\"parameters, for example ID of user session. Once received, the \"\n\"distributed eventbus will create a local map.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:32\nmsgid \"A brief example of a map creation is shown here:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:38\nmsgid \"\"\n\"This will fire event ``MapCreatedEvent`` on all other cluster nodes. \"\n\"Strings \\\"Very_Important_Map_In_User_Session\\\" and \\\"user-session-\"\n\"identifier-123\\\" are given as parameters in :literal:`onMapCreated()\\\\`` \"\n\"method. The event consumer code must know what to do with map with type \"\n\"\\\"Very_Important_Map_In_User_Session\\\". It may retrieve user session \"\n\"\\\"user-session-identifier-123\\\" and put this map in this session. It \"\n\"should be used to tell other nodes how to treat the event with a newly \"\n\"created map, and it should be stored in user session.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:41\nmsgid \"Map Changes\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:43\nmsgid \"\"\n\"Changes to the map on one cluster will trigger ``AddValue`` or \"\n\"``RemoveValue`` events in eventbus. Stanzas sent between clusters will \"\n\"look something like this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:59\nmsgid \"Code to handle adding an item:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:68\nmsgid \"\"\n\"Where the element 'event' is the UID, and the name string is the name of \"\n\"the map key/value pair.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:70\nmsgid \"\"\n\"This example removes an element from the cluster map. Removal of items \"\n\"look similar:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:82\nmsgid \"with the code also being similar:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:93\nmsgid \"Map Destruction\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:95\nmsgid \"\"\n\"Java Garbage Collector will normally remove a local map if it is no \"\n\"longer used. Clustered maps however are not removed in this manner. These\"\n\" maps must be destroyed manually if they are no longer used:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:101\nmsgid \"Calling this, the map named clmap will be destroyed on each cluster node.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:103\nmsgid \"\"\n\"The event handler will catch event when map is destroyed on another \"\n\"cluster node:\"\nmsgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Development/CodeStyleGuide.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:2\nmsgid \"Tigase Code Style\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:5\nmsgid \"Introduction\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:8\nmsgid \"\"\n\"This documents defines and describes coding style and standard used in \"\n\"Tigase projects source code.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:10\nmsgid \"\"\n\"Examples should be considered as **non-normative**, that is formatting \"\n\"choices should not be treated as rules.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:13\nmsgid \"Source file basics\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:16\nmsgid \"Technicals details\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:18\nmsgid \"\"\n\"File name consists of the case-sensitive, camel-cased name of the top-\"\n\"level class it contains plus the ``.java`` extension.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:20\nmsgid \"Source files are encoded in **UTF-8**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:24\nmsgid \"Source file structure\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:26\nmsgid \"A source file consists of, **in order**:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:28\nmsgid \"License or copyright information, if present\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:30\nmsgid \"Package statement\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:32\n#: ../../Tigase_Development/CodeStyleGuide.rst:43\nmsgid \"Import statements\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:34\nmsgid \"Exactly one top-level class\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:36\nmsgid \"Additionally:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:38\nmsgid \"**Exactly one blank line** separates sections 2-4;\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:40\nmsgid \"\"\n\"The package statement is **not line-wrapped** (column limit does not \"\n\"apply);\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:45\nmsgid \"Wildcard imports can be used for:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:47\nmsgid \"more than 5 class imports;\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:49\nmsgid \"more than 3 name imports;\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:51\nmsgid \"import statements are **not line-wrapped** (column limit does not apply);\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:53\nmsgid \"following import ordering applies:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:55\nmsgid \"all imports not pertaining to any of the groups listed below\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:57\n#: ../../Tigase_Development/CodeStyleGuide.rst:63\nmsgid \"``blank line``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:59\nmsgid \"``javax.*`` classes\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:61\nmsgid \"``java.*`` classes\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:65\nmsgid \"all static imports in single block\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:67\nmsgid \"\"\n\"items in each block are ordered by names in ASCII sort order (since ``;``\"\n\" sorts before ``.``)\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:70\nmsgid \"Class declaration\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:72\nmsgid \"Each top-level class resides in a source file of its own.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:75\nmsgid \"Class contents order\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:77\nmsgid \"Following order of the elements of the class is mandatory:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:79\nmsgid \"``final``, ``static`` fields in following order:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:81\n#: ../../Tigase_Development/CodeStyleGuide.rst:93\n#: ../../Tigase_Development/CodeStyleGuide.rst:105\n#: ../../Tigase_Development/CodeStyleGuide.rst:115\nmsgid \"``public``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:83\n#: ../../Tigase_Development/CodeStyleGuide.rst:95\n#: ../../Tigase_Development/CodeStyleGuide.rst:107\n#: ../../Tigase_Development/CodeStyleGuide.rst:117\nmsgid \"``protected``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:85\n#: ../../Tigase_Development/CodeStyleGuide.rst:97\n#: ../../Tigase_Development/CodeStyleGuide.rst:109\n#: ../../Tigase_Development/CodeStyleGuide.rst:119\nmsgid \"``package-private``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:87\n#: ../../Tigase_Development/CodeStyleGuide.rst:99\n#: ../../Tigase_Development/CodeStyleGuide.rst:111\n#: ../../Tigase_Development/CodeStyleGuide.rst:121\nmsgid \"``private``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:89\nmsgid \"``public`` ``enum``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:91\nmsgid \"``static`` fields in following order:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:101\nmsgid \"``static`` initializer block\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:103\nmsgid \"``final`` fields in following order:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:113\nmsgid \"fields without modifiers in following order:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:123\nmsgid \"initializer block\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:125\nmsgid \"``static`` method(s)\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:127\nmsgid \"constructor(s)\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:129\nmsgid \"methods(s) without modifiers\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:131\nmsgid \"enums(s) without modifiers\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:133\nmsgid \"interfaces(s) without modifiers\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:135\nmsgid \"inner ``static`` classes\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:137\nmsgid \"inner classes\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:139\nmsgid \"In addition:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:141\nmsgid \"Getters and Setters are kept together\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:143\nmsgid \"\"\n\"Overloads are never split - multiple constructors or methods with the \"\n\"same name appear sequentially.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:146\nmsgid \"Formatting\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:149\nmsgid \"Braces\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:151\nmsgid \"\"\n\"Braces are mandatory in optional cases - for all syntax where braces use \"\n\"can be optional, Tigase mandate using braces even if the body is empty or\"\n\" contains only single statement.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:153\nmsgid \"\"\n\"Braces follow the Kernighan and Ritchie style (`Egyptian brackets \"\n\"<http://www.codinghorror.com/blog/2012/07/new-programming-\"\n\"jargon.html>`__):\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:155\nmsgid \"No line break before the opening brace.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:157\nmsgid \"Line break after the opening brace.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:159\nmsgid \"Line break before the closing brace.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:161\nmsgid \"\"\n\"Line break after the closing brace, *only if* that brace terminates a \"\n\"statement or terminates the body of a method, constructor, or *named* \"\n\"class. For example, there is *no* line break after the brace if it is \"\n\"followed by ``else`` or a comma.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:165\nmsgid \"Block indentation: tab\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:167\nmsgid \"\"\n\"All indentation (opening a new block of block-like construct) must be \"\n\"made with tabs. After the block, then indent returns to the previous.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:169\nmsgid \"Ideal tab-size: 4\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:172\nmsgid \"Column limit: 120\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:174\nmsgid \"\"\n\"Defined column limit is 120 characters and must be line-wrapped as \"\n\"described below Java code has a column limit of 100 characters. Except as\"\n\" noted below, any line that would exceed this limit must be line-wrapped,\"\n\" as explained in section :ref:`Line-wrapping<linewrapping>`.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:179\nmsgid \"Line-wrapping\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:181\nmsgid \"\"\n\"*line-wrapping* is a process of dividing long lines that would otherwise \"\n\"go over the defined Column Limit (above). It’s recommended to wrap lines \"\n\"whenever it’s possible even if they are not longer than defined limit.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:184\nmsgid \"Whitespace\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:187\nmsgid \"Vertical Whitespace\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:189\nmsgid \"A single blank line appears:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:191\nmsgid \"after package statement;\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:193\nmsgid \"before imports;\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:195\nmsgid \"after imports;\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:197\nmsgid \"around class;\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:199\nmsgid \"after class header;\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:201\nmsgid \"around field in interface;\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:203\nmsgid \"around method in interface;\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:205\nmsgid \"around method;\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:207\nmsgid \"around initializer;\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:209\nmsgid \"as required by other sections of this document.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:211\nmsgid \"Multiple blank lines are not permitted.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:214\nmsgid \"Horizontal whitespace\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:216\nmsgid \"\"\n\"Beyond where required by the language or other style rules, and apart \"\n\"from literals, comments and Javadoc, a single ASCII space also appears in\"\n\" the following places **only**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:218\nmsgid \"\"\n\"Separating any reserved word, such as ``if``, ``for``, ``while``, \"\n\"``switch``, ``try``, ``catch`` or ``synchronized``, from an open \"\n\"parenthesis (``(``) that follows it on that line\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:220\nmsgid \"\"\n\"Separating any reserved word, such as ``else`` or ``catch``, from a \"\n\"closing curly brace (``}``) that precedes it on that line\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:222\nmsgid \"Before any open curly brace (``{``), with two exceptions:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:224\nmsgid \"``@SomeAnnotation({a, b})`` (no space is used)\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:226\nmsgid \"\"\n\"``String[][] x = {{\\\"foo\\\"}};`` (no space is required between ``{{``, by \"\n\"item 8 below)\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:228\nmsgid \"\"\n\"On both sides of any binary or ternary operator. This also applies to the\"\n\" following \\\"operator-like\\\" symbols:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:230\nmsgid \"the ampersand in a conjunctive type bound: ``<T extends Foo & Bar>``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:232\nmsgid \"\"\n\"the pipe for a catch block that handles multiple exceptions: ``catch \"\n\"(FooException | BarException e)``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:234\nmsgid \"the colon (``:``) in an enhanced ``for`` (\\\"foreach\\\") statement\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:236\nmsgid \"the arrow in a lambda expression: ``(String str) → str.length()``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:238\nmsgid \"**but not:**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:240\nmsgid \"\"\n\"the two colons (``::``) of a method reference, which is written like \"\n\"``Object::toString``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:242\nmsgid \"the dot separator (``.``), which is written like ``object.toString()``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:244\nmsgid \"After ``,:;`` or the closing parenthesis (``)``) of a cast\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:246\nmsgid \"Between the type and variable of a declaration: ``List<String> list``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:249\nmsgid \"Horizontal alignment: never required\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:251\nmsgid \"\"\n\"*Horizontal alignment* is the practice of adding a variable number of \"\n\"additional spaces in your code with the goal of making certain tokens \"\n\"appear directly below certain other tokens on previous lines.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:253\nmsgid \"\"\n\"This practice is permitted, but is **never required**. It is not even \"\n\"required to *maintain* horizontal alignment in places where it was \"\n\"already used.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:256\nmsgid \"Specific constructs\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:259\nmsgid \"Enum classes\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:261\nmsgid \"After each comma that follows an enum constant, a line break is mandatory.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:264\nmsgid \"Variable declarations\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:266\nmsgid \"\"\n\"One variable per declaration - Every variable declaration (field or \"\n\"local) declares only one variable: declarations such as ``int a, b;`` are\"\n\" not used.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:268\nmsgid \"\"\n\"Declared when needed -Local variables are **not** habitually declared at \"\n\"the start of their containing block or block-like construct. Instead, \"\n\"local variables are declared close to the point they are first used \"\n\"(within reason), to minimize their scope. Local variable declarations \"\n\"typically have initializers, or are initialized immediately after \"\n\"declaration.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:271\nmsgid \"Arrays\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:273\nmsgid \"\"\n\"Any array initializer may *optionally* be formatted as if it were a \"\n\"\\\"block-like construct.\\\" (especially when line-wrapping need to be \"\n\"applied).\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:276\nmsgid \"Naming\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:279\nmsgid \"Rules common to all identifiers\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:281\nmsgid \"\"\n\"Identifiers use only ASCII letters and digits, and, in a small number of \"\n\"cases noted below, underscores. Thus each valid identifier name is \"\n\"matched by the regular expression ``\\\\w+`` .\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:284\nmsgid \"Specific Rules by identifier type\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:286\nmsgid \"\"\n\"Package names are all lowercase, with consecutive words simply \"\n\"concatenated together (no underscores, not camel-case).\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:288\nmsgid \"Class names are written in **UpperCamelCase**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:290\nmsgid \"Method names are written in **lowerCamelCase**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:292\nmsgid \"\"\n\"Constant names use ``CONSTANT_CASE``: all uppercase letters, with words \"\n\"separated by underscores.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:294\nmsgid \"\"\n\"Non-constant field names (static or otherwise) are written in \"\n\"**lowerCamelCase**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:296\nmsgid \"\"\n\"Parameter names are written in **lowerCamelCase** (one-character \"\n\"parameter names in public methods should be avoided).\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:298\nmsgid \"Local variable names are written in **lowerCamelCase**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:301\nmsgid \"Programming Practices\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:303\nmsgid \"\"\n\"A method is marked with the ``@Override`` annotation whenever it is \"\n\"legal. This includes a class method overriding a superclass method, a \"\n\"class method implementing an interface method, and an interface method \"\n\"re-specifying a super-interface method.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:305\nmsgid \"\"\n\"Caught exceptions should not be ignored (and if this is a must then a log\"\n\" entry is required).\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:308\nmsgid \"Javadoc\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:310\nmsgid \"blank lines should be inserted after:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:312\nmsgid \"description,\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:314\nmsgid \"parameter description,\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:316\nmsgid \"return tag;\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:318\nmsgid \"empty tag should be included for following tags:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:320\nmsgid \"``@params``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:322\nmsgid \"``@return``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:324\nmsgid \"``@throws``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:327\nmsgid \"Usage\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:329\nmsgid \"\"\n\"At the *minimum*, Javadoc is present for every ``public`` class, and \"\n\"every ``public`` or ``protected`` member of such a class, with a few \"\n\"exceptions:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:331\nmsgid \"\"\n\"is optional for \\\"simple, obvious\\\" methods like ``getFoo``, in cases \"\n\"where there *really and truly* is nothing else worthwhile to say but \"\n\"\\\"Returns the foo\\\".\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:333\nmsgid \"in methods that overrides a supertype method.\"\nmsgstr \"\"\n\n#~ msgid \"\"\n#~ \"Defined column limit is 120 characters\"\n#~ \" and must be line-wrapped as \"\n#~ \"described below Java code has a \"\n#~ \"column limit of 100 characters. Except\"\n#~ \" as noted below, any line that \"\n#~ \"would exceed this limit must be \"\n#~ \"line-wrapped, as explained in section \"\n#~ \"`Line-wrapping <#line-wrapping>`__.\"\n#~ msgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Development/Component_Implementation.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:2\nmsgid \"Component Development\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:4\nmsgid \"\"\n\"A component in the Tigase is an entity with its own JID address. It can \"\n\"receive packets, process them, and can also generate packets.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:6\nmsgid \"\"\n\"An example of the best known components is MUC or PubSub. In Tigase \"\n\"however, almost everything is actually a component: Session Manager, s2s \"\n\"connections manager, Message Router, etc…​ Components are loaded based on\"\n\" the server configuration, new components can be loaded and activated at \"\n\"run-time. You can easily replace a component implementation and the only \"\n\"change to make is a class name in the configuration entry.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:8\nmsgid \"\"\n\"Creating components for Tigase server is an essential part of the server \"\n\"development hence there is a lot of useful API and ready to use code \"\n\"available. This guide should help you to get familiar with the API and \"\n\"how to quickly and efficiently create your own component implementations.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:10\nmsgid \":ref:`Component implementation - Lesson 1 - Basics<cil1>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:12\nmsgid \":ref:`Component implementation - Lesson 2 - Configuration<cil2>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:14\nmsgid \":ref:`Component implementation - Lesson 3 - Multi-Threading<cil3>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:16\nmsgid \":ref:`Component implementation - Lesson 4 - Service Discovery<cil4>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:18\nmsgid \":ref:`Component implementation - Lesson 5 - Statistics<cil5>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:20\nmsgid \":ref:`Component implementation - Lesson 6 - Scripting Support<cil6>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:22\nmsgid \":ref:`Component implementation - Lesson 7 - Data Repository<cil7>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:24\nmsgid \":ref:`Component implementation - Lesson 8 - Startup Time<cil8>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:26\nmsgid \":ref:`Packet Filtering in Component<packetfiltering>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:31\nmsgid \"Component Implementation - Lesson 1 - Basics\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:33\nmsgid \"\"\n\"Creating a Tigase component is actually very simple and with broad API \"\n\"available you can create a powerful component with just a few lines of \"\n\"code. You can find detailed API description elsewhere. This series \"\n\"presents hands on lessons with code examples, teaching how to get desired\"\n\" results in the simplest possible code using existing Tigase API.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:35\nmsgid \"\"\n\"Even though all Tigase components are just implementations of the \"\n\"**ServerComponent** interface I will keep such a low level information to\"\n\" necessary minimum. Creating a new component based on just interfaces, \"\n\"while very possible, is not very effective. This guide intends to teach \"\n\"you how to make use of what is already there, ready to use with a minimal\"\n\" coding effort.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:37\nmsgid \"\"\n\"This is just the first lesson of the series where I cover basics of the \"\n\"component implementation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:39\nmsgid \"Let’s get started and create the Tigase component:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:69\nmsgid \"\"\n\"As you can see we have 3 mandatory methods when we extends \"\n\"**AbstractKernelBasedComponent**:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:71\nmsgid \"\"\n\"**String getComponentVersion()** which returns version of a component for\"\n\" logging purposes\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:73\nmsgid \"\"\n\"**boolean isDiscoNonAdmin()** which decides if component will be visible \"\n\"for users other that server administrators\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:75\nmsgid \"\"\n\"**void registerModules(Kernel kernel)** which allows you to register \"\n\"component modules responsible for actual processing of packets\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:79\nmsgid \"\"\n\"If you decide you do not want to use modules for processing packets (even\"\n\" though we strongly suggest to use them, as thanks to modules components \"\n\"are easily extendible) you can implement one more method **void \"\n\"processPacket(Packet packet)** which will be called for every packet sent\"\n\" to a component. This method is actually logical as the main task for \"\n\"your component is processing packets.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:81\nmsgid \"\"\n\"Class name for our new component is **TestComponent** and we have also \"\n\"initialized a separated logger for this class. Doing This is very useful \"\n\"as it allows us to easily find log entries created by our class.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:83\nmsgid \"\"\n\"With these a few lines of code you have a fully functional Tigase \"\n\"component which can be loaded to the Tigase server; it can receive and \"\n\"process packets, shows as an element on service discovery list (for \"\n\"administrators only), responds to administrator ad-hoc commands, supports\"\n\" scripting, generates statistics, can be deployed as an external \"\n\"component, and a few other things.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:85\nmsgid \"\"\n\"Next important step is to create modules responsible for processing \"\n\"packets. For now let’s create module responsible for handling messages by\"\n\" appending them to log file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:107\nmsgid \"\"\n\"Instance of ``Criteria`` class returned by ``Criteria \"\n\"getModuleCriteria()`` is used by component class to decide if packet \"\n\"should be processed by this module or not. In this case we returned \"\n\"instance which matches any packet which is a **message**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:109\nmsgid \"\"\n\"And finally we have a very important method ``void process(Packet \"\n\"packet)`` which is main processing method of a component. If component \"\n\"will receive packet that matches criteria returned by module - this \"\n\"method will be called.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:111\nmsgid \"\"\n\"But how we can send packet from a module? **AbstractModule** contains \"\n\"method **void write(Packet packet)** which you can use to send packets \"\n\"from a component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:113\nmsgid \"\"\n\"Before we go any further with the implementation let’s configure the \"\n\"component in Tigase server so it is loaded next time the server starts. \"\n\"Assuming our **init.tdsl** file looks like this one:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:129\nmsgid \"\"\n\"We can see that it already is configured to load two other components: \"\n\"**MUC** and **PubSub**. Let’s add a third - our new component to the \"\n\"configuration file by appending the following line in the properties \"\n\"file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:135\nmsgid \"Now we have to restart the server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:137\nmsgid \"\"\n\"There are a few ways to check whether our component has been loaded to \"\n\"the server. Probably the easiest is to connect to the server from an \"\n\"administrator account and look at the service discovery list.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:139\nmsgid \"|service disco test comp admin 300|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:178\nmsgid \"service disco test comp admin 300\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:141\nmsgid \"\"\n\"If everything goes well you should see an entry on the list similar to \"\n\"the highlighted one on the screenshot. The component description is \"\n\"\\\"*Undefined description*\\\" which is a default description and we can \"\n\"change it later on, the component default JID is: \"\n\"**test@devel.tigase.org**, where **devel.tigase.org** is the server \"\n\"domain and test is the component name.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:143\nmsgid \"\"\n\"Another way to find out if the component has been loaded is by looking at\"\n\" the log files. Getting yourself familiar with Tigase log files will be \"\n\"very useful thing if you plan on developing Tigase components. So let’s \"\n\"look at the log file **logs/tigase.log.0**, if the component has been \"\n\"loaded you should find following lines in the log:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:151\nmsgid \"\"\n\"If your component did not load you should first check configuration \"\n\"files. Maybe the Tigase could not find your class at startup time. Make \"\n\"sure your class is in **CLASSPATH** or copy a JAR file with your class to\"\n\" Tigase **jars/** directory.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:153\nmsgid \"\"\n\"Assuming everything went well and your component is loaded by the sever \"\n\"and it shows on the service discovery list as on the screenshot above you\"\n\" can double click on it to get a window with a list of ad-hoc commands - \"\n\"administrator scripts. A window on the screenshot shows only two basic \"\n\"commands for adding and removing script which is a good start.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:155\nmsgid \"|commands list test 200|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:179\nmsgid \"commands list test 200\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:157\nmsgid \"\"\n\"Moreover, you can browse the server statistics in the service discovery \"\n\"window to find your new test component on the list. If you click on the \"\n\"component it shows you a window with component statistics, very basic \"\n\"packets counters.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:159\nmsgid \"|service disco stats 200|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:180\nmsgid \"service disco stats 200\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:161\nmsgid \"\"\n\"As we can see with just a few lines of code our new component is quite \"\n\"mighty and can do a lot of things without much effort from the developer \"\n\"side.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:163\nmsgid \"\"\n\"Now, the time has come to the most important question. Can our new \"\n\"component do something useful, that is can it receive and process XMPP \"\n\"packets?\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:165\nmsgid \"\"\n\"Let’s try it out. Using you favorite client send a message to JID: \"\n\"**test@devel.tigase.org** (assuming your server is configured for \"\n\"**devel.tigase.org** domain). You can either use kind of XML console in \"\n\"your client or just send a plain message to the component JID. According \"\n\"to our code in **process(…​)** method it should log our message. For this\"\n\" test I have sent a message with subject: \\\"*test message*\\\" and body: \"\n\"\\\"*this is a test*\\\". The log file should contain following entry:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:176\nmsgid \"\"\n\"If this is a case we can be sure that everything works as expected and \"\n\"all we now have to do is to fill the **process(…​)** method with some \"\n\"useful code.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:185\nmsgid \"Component Implementation - Lesson 2 - Configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:187\nmsgid \"\"\n\"It might be hard to tell what the first important thing you should do \"\n\"with your new component implementation. Different developers may have a \"\n\"different view on this. It seems to me however that it is always a good \"\n\"idea to give to your component a way to configure it and provide some \"\n\"runtime settings.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:189\nmsgid \"This guide describes how to add configuration handling to your component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:191\nmsgid \"\"\n\"To demonstrate how to implement component configuration let’s say we want\"\n\" to configure which types of packets will be logged by the component. \"\n\"There are three possible packet types: **message**, **presence** and \"\n\"**iq** and we want to be able to configure logging of any combination of \"\n\"the three. Furthermore we also want to be able to configure the text \"\n\"which is prepended to the logged message and to optionally switch secure \"\n\"login. (Secure logging replaces all packet CData with text: *CData size: \"\n\"NN* to protect user privacy.)\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:193\nmsgid \"\"\n\"Let’s create the following private variables in our component \"\n\"**TestModule**:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:201\nmsgid \"\"\n\"To make them configurable we have to annote them with ``@ConfigField`` \"\n\"annotation. It requires ``desc`` field (describing configuration option) \"\n\"and has following optional properties: \\\\* ``alias`` - alternative name \"\n\"of the variable \\\\* ``allowAliasFromParent`` - specifies whether value \"\n\"from alias from parent bean should be allowed \\\\* ``type`` - specifies \"\n\"general type of the field, which impacts possible obfuscation of the \"\n\"value in the logs/\\\"config-dump\\\" file; possible values: ``Plain`` (no \"\n\"changes), ``Password`` (complete value will be obfuscated) and \"\n\"``JdbcUrl`` (only password part will be obfuscated from the URL)\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:212\nmsgid \"\"\n\"And this is it. Tigase Kernel will take care of this fields and will \"\n\"update them when configuration will change.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:214\nmsgid \"\"\n\"The syntax in ``config.tdsl`` file is very simple and is described in \"\n\"details in the *Admin Guide*. To set the configuration for your component\"\n\" in ``config.tdsl`` file you have to append following lines to the file \"\n\"inside test component configuration block:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:224\nmsgid \"\"\n\"The square brackets are used to mark that we set a list consisting of a \"\n\"few elements, have a look at the *Admin Guide* documentation for more \"\n\"details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:226\nmsgid \"\"\n\"And this is the complete code of the new component module with a modified\"\n\" ``process(…​)`` method taking advantage of configuration settings:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:264\nmsgid \"\"\n\"Of course we can do much more useful packet processing in the \"\n\"``process(…​)`` method. This is just an example code.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:268\nmsgid \"\"\n\"Here we used a setter **setPacketType(String[] packetTypes)** which is a \"\n\"setter for field **packetTypes**. Tigase Kernel will use it instead of \"\n\"assigning value directly to a field which gives up opportunity to convert\"\n\" value to different type and update other field - in our case we updated \"\n\"**CRITERIA** field which will result in change of packet types which for \"\n\"which method **void process(…​)** will be called.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:273\nmsgid \"Component Implementation - Lesson 3 - Multi-Threading\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:275\nmsgid \"\"\n\"Multi core and multi CPU machines are very common nowadays. Your new \"\n\"custom component however, processes all packets in a single thread.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:277\nmsgid \"\"\n\"This is especially important if the packet processing is CPU expensive \"\n\"like, for example, SPAM checking. In such a case you could experience \"\n\"single Core/CPU usage at 100% while other Cores/CPUs are idling. Ideally,\"\n\" you want your component to use all available CPUs.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:279\nmsgid \"\"\n\"Tigase API offers a very simple way to execute component’s \"\n\"``processPacket(Packet packet)`` method in multiple threads. Methods \"\n\"``int processingOutThreads()`` and ``int processingInThreads()`` returns \"\n\"number of threads assigned to the component. By default it returns just \"\n\"'1' as not all component implementations are prepared to process packets \"\n\"concurrently. By overwriting the method you can return any value you \"\n\"think is appropriate for the implementation. Please note, there are two \"\n\"methods, one is for a number of threads for incoming packets to the \"\n\"component and another for outgoing packets from the component. It used to\"\n\" be a single method but different components have different needs and the\"\n\" best performance can be achieved when the outgoing queues have a \"\n\"separate threads pool from incoming queues. Also some components only \"\n\"receive packets while other only send, therefore assigning an equal \"\n\"number of threads for both that could be a waste of resources.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:283\nmsgid \"\"\n\"Due to how Kernel works you MUST avoid using variables in those methods. \"\n\"If you would like to have this configurable at startup time you could \"\n\"simply set ``processing-in-threads`` and ``processing-out-threads`` in \"\n\"your component’s bean configuration.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:285\nmsgid \"\"\n\"If the packet processing is CPU bound only, you normally want to have as \"\n\"many threads as there are CPUs available:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:298\nmsgid \"\"\n\"If the processing is I/O bound (network or database) you probably want to\"\n\" have more threads to process requests. It is hard to guess the ideal \"\n\"number of threads right on the first try. Instead you should run a few \"\n\"tests to see how many threads is best for implementation of the \"\n\"component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:300\nmsgid \"\"\n\"Now you have many threads for processing your packets, but there is one \"\n\"slight problem with this. In many cases packet order is essential. If our\"\n\" ``processPacket(…​)`` method is executed concurrently by a few threads \"\n\"it is quite possible that a message sent to user can takeover the message\"\n\" sent earlier. Especially if the first message was large and the second \"\n\"was small. We can prevent this by adjusting the method responsible for \"\n\"packet distribution among threads.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:302\nmsgid \"The algorithm for packets distribution among threads is very simple:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:308\nmsgid \"\"\n\"So the key here is using the ``hashCodeForPacket(…​)`` method. By \"\n\"overwriting it we can make sure that all packets addressed to the same \"\n\"user will always be processed by the same thread:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:329\nmsgid \"\"\n\"The above two methods give control over the number of threads assigned to\"\n\" the packets processing in your component and to the packet distribution \"\n\"among threads. This is not all Tigase API has to offer in terms of multi-\"\n\"threading.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:331\nmsgid \"\"\n\"Sometimes you want to perform some periodic actions. You can of course \"\n\"create Timer instance and load it with TimerTasks. As there might be a \"\n\"need for this, every level of the Class hierarchy could end-up with \"\n\"multiple Timer (threads in fact) objects doing similar job and using \"\n\"resources. There are a few methods which allow you to reuse common Timer \"\n\"object to perform all sorts of actions.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:333\nmsgid \"\"\n\"First, you have three methods allowing your to perform some periodic \"\n\"actions:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:341\nmsgid \"\"\n\"An example implementation for periodic notifications sent to some address\"\n\" could look like this one:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:357\nmsgid \"\"\n\"This method sends every **notificationFrequency** minute a message to \"\n\"**abuseAddress** reporting how many spam messages have been detected \"\n\"during last period. Please note, you have to call ``super.everyMinute()``\"\n\" to make sure other actions are executed as well and you have to also \"\n\"remember to keep processing in this method to minimum, especially if you \"\n\"overwrite ``everySecond()`` method.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:359\nmsgid \"\"\n\"There is also a method which allow you to schedule tasks executed at \"\n\"certain time, it is very similar to the ``java.util.Timer`` API. The only\"\n\" difference is that we are using **ScheduledExecutorService** as a \"\n\"backend which is being reused among all levels of Class hierarchy. There \"\n\"is a separate ``ScheduledExecutorService`` for each Class instance \"\n\"though, to avoid interferences between separate components:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:365\nmsgid \"\"\n\"Here is a code of an example component and module which uses all the API \"\n\"discussed in this article:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:367\n#: ../../Tigase_Development/Component_Implementation.rst:620\n#: ../../Tigase_Development/Component_Implementation.rst:830\n#: ../../Tigase_Development/Component_Implementation.rst:1117\nmsgid \"**Example component code.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:429\n#: ../../Tigase_Development/Component_Implementation.rst:693\n#: ../../Tigase_Development/Component_Implementation.rst:912\n#: ../../Tigase_Development/Component_Implementation.rst:1209\nmsgid \"**Example module code.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:514\nmsgid \"Component Implementation - Lesson 4 - Service Discovery\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:516\nmsgid \"\"\n\"You component still shows in the service discovery list as an element \"\n\"with \\\"*Undefined description*\\\". It also doesn’t provide any interesting\"\n\" features or sub-nodes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:518\nmsgid \"\"\n\"In this article I will show how to, in a simple way, change the basic \"\n\"component information presented on the service discovery list and how to \"\n\"add some service disco features. As a bit more advanced feature the guide\"\n\" will teach you about adding/removing service discovery nodes at run-time\"\n\" and about updating existing elements.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:520\nmsgid \"\"\n\"In order for the component to properly respond to ``disco#info`` and \"\n\"``disco#items`` request you should register ``DiscoveryModule`` in your \"\n\"component:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:529\nmsgid \"\"\n\"NOTE It’s essential to **explicitly** register ``DiscoveryModule`` in \"\n\"your component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:531\nmsgid \"\"\n\"Component description and category type can be changed by overriding two \"\n\"following methods:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:545\nmsgid \"\"\n\"Please note, there is no such **'spam'** category type defined in the \"\n\"`Service Discovery Identities registry <http://xmpp.org/registrar/disco-\"\n\"categories.html>`__. It has been used here as a demonstration only. \"\n\"Please refer to the Service Discovery Identities registry document for a \"\n\"list of categories and types and pick the one most suitable for you.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:547\nmsgid \"\"\n\"After you have added the two above methods and restarted the server with \"\n\"updated code, have a look at the service discovery window. You should see\"\n\" something like on the screenshot.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:549\nmsgid \"|spam filtering disco small|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:788\nmsgid \"spam filtering disco small\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:551\nmsgid \"\"\n\"Now let’s add method which will allow our module ``TestModule`` to return\"\n\" supported features. This way our component will automatically report \"\n\"features supported by all it’s modules. To do so we need to implement a \"\n\"method **String[] getFeatures()** which returns array of ``String`` \"\n\"items. This items are used to generate a list of features supported by \"\n\"component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:553\nmsgid \"\"\n\"Although this was easy, this particular change doesn’t affect anything \"\n\"apart from just a visual appearance. Let’s get then to more advanced and \"\n\"more useful changes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:555\nmsgid \"\"\n\"One of the limitations of methods above is that you can not update or \"\n\"change component information at run-time with these methods. They are \"\n\"called only once during initialization of a component when component \"\n\"service discovery information is created and prepared for later use. \"\n\"Sometimes, however it is useful to be able to change the service \"\n\"discovery during run-time.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:557\nmsgid \"\"\n\"In our simple spam filtering component let’s show how many messages have \"\n\"been checked out as part of the service discovery description string. \"\n\"Every time we receive a message we can to call:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:563\nmsgid \"\"\n\"*A small performance note, in some cases calling \"\n\"``updateServiceDiscoveryItem(…​)`` might be an expensive operation so \"\n\"probably a better idea would be to call the method not every time we \"\n\"receive a message but maybe every 100 times or so.*\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:565\nmsgid \"\"\n\"The first parameter is the component JID presented on the service \"\n\"discovery list. However, Tigase server may work for many virtual hosts so\"\n\" the hostname part is added by the lower level functions and we only \"\n\"provide the component name here. The second parameter is the service \"\n\"discovery node which is usually '**null**' for top level disco elements. \"\n\"Third is the item description (which is actually called 'name' in the \"\n\"disco specification). The last parameter specifies if the element is \"\n\"visible to administrators only.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:567\nmsgid \"|spam filter counter small|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:789\nmsgid \"spam filter counter small\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:569\nmsgid \"\"\n\"The complete method code is presented below and the screenshot above \"\n\"shows how the element of the service discovery for our component can \"\n\"change if we apply our code and send a few messages to the component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:571\nmsgid \"\"\n\"Using the method we can also add submodes to our component element. The \"\n\"XMPP service discovery really is not for showing application counters, \"\n\"but this case it is good enough to demonstrate the API available in \"\n\"Tigase so we continue with presenting our counters via service discovery.\"\n\" This time, instead of using 'null' as a node we put some meaningful \"\n\"texts as in example below:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:584\nmsgid \"\"\n\"Again, have a look at the full method body below for a complete code \"\n\"example. Now if we send a few messages to the component and some of them \"\n\"are spam (contain words recognized as spam) we can browse the service \"\n\"discovery of the server. Your service discovery should show a list \"\n\"similar to the one presented on the screenshot on the left.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:586\nmsgid \"\"\n\"Of course depending on the implementation, initially there might be no \"\n\"sub-nodes under our component element if we call the \"\n\"``updateServiceDiscoveryItem(…​)`` method only when a message is \"\n\"processed. To make sure that sub-nodes of our component show from the \"\n\"very beginning you can call them in ``setProperties(…​)`` for the first \"\n\"time to populate the service discovery with initial sub-nodes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:588\nmsgid \"\"\n\"Please note, the ``updateServiceDiscoveryItem(…​)`` method is used for \"\n\"adding a new item and updating existing one. There is a separate method \"\n\"though to remove the item:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:595\nmsgid \"\"\n\"Actually only two first parameters are important: the **jid** and the \"\n\"**node** which must correspond to the existing, previously created \"\n\"service discovery item.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:597\nmsgid \"\"\n\"There are two additional variants of the *update* method which give you \"\n\"more control over the service discovery item created. Items can be of \"\n\"different categories and types and can also present a set of features.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:599\nmsgid \"\"\n\"The simpler is a variant which sets a set of features for the updated \"\n\"service discovery item. There is a `document <http://xmpp.org/registrar\"\n\"/disco-features.html>`__ describing existing, registered features. We are\"\n\" creating an example which is going to be a spam filter and there is no \"\n\"predefined feature for spam filtering but for purpose of this guide we \"\n\"can invent two feature identification strings and set it for our \"\n\"component. Let’s call ``update`` method with following parameters:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:606\nmsgid \"\"\n\"The best place to call this method is the ``setProperties(…​)`` method so\"\n\" our component gets a proper service discovery settings at startup time. \"\n\"We have set two features for the component disco: *tigase:x:spam-filter* \"\n\"and *tigase:x:spam-reporting*. This method accepts a variable set of \"\n\"arguments so we can pass to it as many features as we need or following \"\n\"Java spec we can just pass an array of **Strings**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:608\nmsgid \"\"\n\"Update your code with call presented above, and restart the server. Have \"\n\"a look at the service discovery for the component now.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:610\nmsgid \"\"\n\"The last functionality might be not very useful for our case of the spam \"\n\"filtering component, but it is for many other cases like MUC or PubSub \"\n\"for which it is setting proper category and type for the service \"\n\"discovery item. There is a document listing all currently registered \"\n\"service discovery identities (categories and types). Again there is entry\"\n\" for spam filtering. Let’s use the *automation* category and *spam-\"\n\"filter* type and set it for our component:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:618\nmsgid \"\"\n\"Of course all these setting can be applied to any service discovery \"\n\"create or update, including sub-nodes. And here is a complete code of the\"\n\" component:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:794\nmsgid \"Component Implementation - Lesson 5 - Statistics\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:796\nmsgid \"\"\n\"In most cases you’ll want to gather some run-time statistics from your \"\n\"component to see how it works, detect possible performance issues or \"\n\"congestion problems. All server statistics are exposed and are accessible\"\n\" via XMPP with ad-hoc commands, HTTP, JMX and some selected statistics \"\n\"are also available via SNMP. As a component developer you don’t have to \"\n\"do anything to expose your statistic via any of those protocols, you just\"\n\" have to provide your statistics and the admin will be able to access \"\n\"them any way he wants.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:798\nmsgid \"\"\n\"This lesson will teach you how to add your own statistics and how to make\"\n\" sure that the statistics generation doesn’t affect application \"\n\"performance.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:800\nmsgid \"|spam statitics small|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1015\nmsgid \"spam statitics small\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:802\nmsgid \"\"\n\"Your component from the very beginning generates some statistics by \"\n\"classes it inherits. Let’s add a few statistics to our spam filtering \"\n\"component:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:816\nmsgid \"The code should be pretty much self-explanatory.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:818\nmsgid \"\"\n\"You have to call ``super.getStatistics(…​)`` to update stats of the \"\n\"parent class. ``StatisticsList`` is a collection which keeps all the \"\n\"statistics in a way which is easy to update, search, and retrieve them. \"\n\"You actually don’t need to know all the implementation details but if you\"\n\" are interested please refer to the source code and JavaDoc \"\n\"documentation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:820\nmsgid \"\"\n\"The first parameter of the ``add(…​)`` method is the component name. All \"\n\"the statistics are grouped by the component names to make it easier to \"\n\"look at particular component data. Next is a description of the element. \"\n\"The third parameter is the element value which can be any number or \"\n\"string.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:822\nmsgid \"\"\n\"The last parameter is probably the most interesting. The idea has been \"\n\"borrowed from the logging framework. Each statistic item has importance \"\n\"level. Levels are exactly the same as for logging methods with **SEVERE**\"\n\" the most critical and **FINEST** the least important. This parameter has\"\n\" been added to improve performance and statistics retrieval. When the \"\n\"**StatisticsList** object is created it gets assigned a level requested \"\n\"by the user. If the ``add(…​)`` method is called with lower priority \"\n\"level then the element is not even added to the list. This saves network \"\n\"bandwidth, improves statistics retrieving speed and is also more clear to\"\n\" present to the end-user.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:824\nmsgid \"\"\n\"One thing which may be a bit confusing at first is that, if there is a \"\n\"numerical element added to statistics with **0** value then the Level is \"\n\"always forced to **FINEST**. The assumption is that the administrator is \"\n\"normally not interested **zero-value** statistics, therefore unless he \"\n\"intentionally request the lowest level statistics he won’t see elements \"\n\"with **zeros**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:826\nmsgid \"\"\n\"The **if** statement requires some explanation too. Normally adding a new\"\n\" statistics element is not a very expensive operation so passing it with \"\n\"``add(…​)`` method at an appropriate level is enough. Sometimes, however \"\n\"preparing statistics data may be quite expensive, like reading/counting \"\n\"some records from database. Statistics can be collected quite frequently \"\n\"therefore it doesn’t make sense to collect the statistics at all if there\"\n\" not going to be used as the current level is higher then the item we \"\n\"pass anyway. In such a case it is recommended to test whether the element\"\n\" level will be accepted by the collection and if not skip the whole \"\n\"processing altogether.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:828\nmsgid \"\"\n\"As you can see, the API for generating and presenting component \"\n\"statistics is very simple and straightforward. Just one method to \"\n\"overwrite and a simple way to pass your own counters. Below is the whole \"\n\"code of the example component:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1020\nmsgid \"Component Implementation - Lesson 6 - Scripting Support\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1022\nmsgid \"\"\n\"Scripting support is a basic API built-in to Tigase server and \"\n\"automatically available to any component at no extra resource cost. This \"\n\"framework, however, can only access existing component variables which \"\n\"are inherited by your code from parent classes. It can not access any \"\n\"data or any structures you added in your component. A little effort is \"\n\"needed to expose some of your data to the scripting API.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1024\nmsgid \"\"\n\"This guide shows how to extend existing scripting API with your component\"\n\" specific data structures.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1026\nmsgid \"\"\n\"Integrating your component implementation with the scripting API is as \"\n\"simple as the code below:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1040\nmsgid \"\"\n\"This way you expose two the component variables: ``badWords`` and \"\n\"``whiteList`` to scripts under names the same names - two defined \"\n\"constants. You could use different names of course but it is always a \"\n\"good idea to keep things straightforward, hence we use the same variable \"\n\"names in the component and in the script.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1042\nmsgid \"\"\n\"Almost done, almost…​ In our old implementation these two variables are \"\n\"Java arrays of ``String``. Therefore we can only change their elements \"\n\"but we can not add or remove elements from these structures inside the \"\n\"script. This is not very practical and it puts some serious limits on the\"\n\" script’s code. To overcome this problem I have changed the test \"\n\"component code to keep bad words and whitelist in ``java.util.Set`` \"\n\"collection. This gives us enough flexibility to manipulate data.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1044\nmsgid \"\"\n\"As our component is now ready to cooperate with the scripting API, I will\"\n\" demonstrate now how to add remove or change elements of these \"\n\"collections using a script and ad-hoc commands.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1046\nmsgid \"|test comp newscript|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1312\nmsgid \"test comp newscript\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1048\nmsgid \"\"\n\"First, browse the server service discovery and double click on the test \"\n\"component. If you use `Psi <http://psi-im.org/>`__ client this should \"\n\"bring to you a new window with ad-hoc commands list. Other clients may \"\n\"present available ad-hoc commands differently.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1050\nmsgid \"\"\n\"The screenshot below shows how this may look. You have to provide some \"\n\"description for the script and an ID string. We use Groovy in this guide \"\n\"but you can as well use any different scripting language.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1052\nmsgid \"|badwords list script|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1313\nmsgid \"badwords list script\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1054\nmsgid \"\"\n\"Please refer to the Tigase scripting documentation for all the details \"\n\"how to add support for more languages. From the Tigase API point of view \"\n\"it all looks the same. You have to select a proper language from the \"\n\"pull-down list on windows shown on the right. If your preferred language \"\n\"is not on the list, it means it is not installed properly and Tigase is \"\n\"unable to detect it.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1056\nmsgid \"\"\n\"The script to pull a list of current bad words can be as simple as the \"\n\"following Groovy code:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1065\nmsgid \"\"\n\"As you see from the code, you have to reference your component variables \"\n\"to a variables in your script to make sure a correct type is used. The \"\n\"rest is very simple and is a pure scripting language stuff.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1067\nmsgid \"\"\n\"Load the script on to the server and execute it. You should receive a new\"\n\" window with a list of all bad words currently used by the spam filter.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1069\nmsgid \"\"\n\"Below is another simple script which allows updating (adding/removing) \"\n\"bad words from the list.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1111\nmsgid \"\"\n\"These two scripts are just the beginning. The possibilities are endless \"\n\"and with the simple a few lines of code in your test component you can \"\n\"then extend your application at runtime with scripts doing various \"\n\"things; you can reload scripts, add and remove them, extending and \"\n\"modifying functionality as you need. No need to restart the server, no \"\n\"need to recompile the code and you can use whatever scripting language \"\n\"you like.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1113\nmsgid \"\"\n\"Of course, scripts for whitelist modifications would look exactly the \"\n\"same and it doesn’t make sense to attach them here.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1115\nmsgid \"\"\n\"Here is a complete code of the test component with the new method \"\n\"described at the beginning and data structures changed from array of \"\n\"**String*s to Java \\\\*Set**:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1318\nmsgid \"Component Implementation - Lesson 7 - Data Repository\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1321\nmsgid \"ConfigRepository\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1323\nmsgid \"\"\n\"There are cases when you want to store some data permanently by your \"\n\"component. You can of course use the component configuration to provide \"\n\"some database connection settings, implement your own database connector \"\n\"and store records you need. There is, however, a very simple and useful \"\n\"framework which allows you to read and store some data transparently in \"\n\"either a database or a disk file. The framework also supports ad-hoc \"\n\"command interface straight away so you can manipulate your component data\"\n\" using an XMPP client.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1325\nmsgid \"\"\n\"In order to use it one needs to extend \"\n\"``tigase.db.comp.ConfigRepository`` abstract class.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1328\nmsgid \"Accessing UserRepository or AuthRepository\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1330\nmsgid \"\"\n\"To use **AuthRepository** or **UserRepository** you need only to declare \"\n\"fields properly and annotated them with **@Inject**. This fields must be \"\n\"part of a class managed by Tigase Kernel - class of a component or any \"\n\"class annotated with **@Bean** annotation. For that classes proper \"\n\"instances of repositories will be injected by dependency injection.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1332\nmsgid \"**Example usage of AuthRepository and UserRepository.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1342\nmsgid \"Accessing other repositories\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1344\nmsgid \"\"\n\"In order to have more freedom while accessing repositories it’s possible \"\n\"to create and use custom repository implementation which implements \"\n\"**DataSourceAware** interface.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1346\nmsgid \"\"\n\"For our example let’s assume it will be class implementing \"\n\"**TestRepositoryIfc** and our implementation will be using JDBC. To make \"\n\"it work, we need to define ``TestRepositoryIfc`` as a generic interface \"\n\"extending ``DataSourceAware`` interface. ``DataSourceAware`` interface \"\n\"will provide definition for methods required by Tigase XMPP Server \"\n\"internals to initialize custom repository classes based on \"\n\"``TestRepositoryIfc``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1348\nmsgid \"**TestRepositoryIfc.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1357\nmsgid \"\"\n\"Next we need to prepare our actual implementation of repository - class \"\n\"responsible for execution of SQL statements. In this class we need to \"\n\"implement all of methods from our interface and method **void \"\n\"setDataSource(DataSource dataSource)** which comes from \"\n\"**DataSourceAware** interface. In this method we need to initialize data \"\n\"source, ie. create prepared statements. We should annotate our new class \"\n\"with ``@Repository.Meta`` annotation which will allow Tigase XMPP Server \"\n\"to find this class whenever class implementing ``TestRepositoryIfc`` and \"\n\"with support for data source with jdbc URI.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1392\nmsgid \"\"\n\"As you can see we defined type of a data source generic parameter for \"\n\"interface ``TestRepositoryIfc``. With that we make sure that only \"\n\"instance implementing ``DataRepository`` interface will be provided and \"\n\"thanks to that we do not need to cast provided instance of ``DataSource``\"\n\" to this interface before any access to data source.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1394\nmsgid \"\"\n\"With that in place we need to create class which will take care of adding\"\n\" support for multi-database setup. In our case it will be \"\n\"**TestRepositoryMDBean**, which will take care of discovery of repository\"\n\" class, initialization and re-injection of data source. It is required to\"\n\" do so, as it was just mentioned our ``TestRepositoryMDBean`` will be \"\n\"responsible for initialization of ``JDBCTestRepository`` (actually this \"\n\"will be done by ``MDRepositoryBean`` which is extended by \"\n\"``TestRepositoryMDBean``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1431\nmsgid \"\"\n\"Most of this code will be the same in all implementations based on \"\n\"``MDRepositoryBeanWithStatistics``. In our case only custom method is \"\n\"**void addItem(…​)** which uses **getRepository(String domain)** method \"\n\"to retrieve correct repository for a domain. This retrieval of actual \"\n\"repository instance for a domain will need to be done for every custom \"\n\"method of ``TestRepositoryIfc``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1435\nmsgid \"\"\n\"It is also possible to extend ``MDRepositoryBean`` or \"\n\"``SDRepositoryBean`` instead of ``MDRepositoryBeanWithStatistics``. \"\n\"However, if you decide to extend abstract repository bean classes without\"\n\" ``withStatistics`` suffix, then no statistics data related to usage of \"\n\"this repository will be gathered. The only change, will be that you will \"\n\"not need to pass interface class to constructor of a superclass as it is \"\n\"not needed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1439\nmsgid \"\"\n\"As mentioned above, it is also possible to extend ``SDRepostioryBean`` \"\n\"and ``SDRepositoryBeanWithStatistics``. Methods which you would need to \"\n\"implement are the same is in case of extending \"\n\"``MDRepositoryBeanWithStatistics``, however internally \"\n\"``SDRepositoryBean`` will not have support for using different repository\"\n\" for different domain. In fact ``SDRepositoryBeanWithStatistics`` has \"\n\"only one repository instance and uses only one data source for all \"\n\"domains. The same behavior is presented by \"\n\"``MDRepositoryBeanWithStatistics`` if only single ``default`` instance of\"\n\" repository is configured. However, ``MDRepositoryBeanWithStatistics`` \"\n\"gives better flexibility and due to that usage of ``SDRepositoryBean`` \"\n\"and ``SDRepositoryBeanWithStatistics`` is discouraged.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1441\nmsgid \"\"\n\"While this is more difficult to implement than in previous version, it \"\n\"gives you support for multi database setup and provides you with \"\n\"statistics of database query times which may be used for diagnosis.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1443\nmsgid \"\"\n\"As you can also see, we’ve annotated **TestRepositoryMDBean** with \"\n\"**@Bean** annotation which will force Tigase Kernel to load it every time\"\n\" **TestComponent** will be loaded. This way it is possible to inject \"\n\"instance of this class as a dependency to any bean used by this component\"\n\" (ie. component, module, etc.) by just creating a field and annotating \"\n\"it:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1452\nmsgid \"\"\n\"In **testRepository** field instance of **TestRepositoryMDBean** will be \"\n\"injected.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1456\nmsgid \"\"\n\"If the class in which we intend to use our repository is deeply nested \"\n\"within Kernel dependencies and we want to leverage automatic schema \"\n\"versioning we have to implement ``tigase.kernel.beans.RegistrarBean`` in \"\n\"our class!\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1459\nmsgid \"Configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1461\nmsgid \"\"\n\"Our class ``TestRepositoryMDBean`` is annotated with ``@Bean`` which sets\"\n\" its name as ``repository`` and sets parent as ``TestComponent``. \"\n\"Instance of this component was configured by use under name of ``test`` \"\n\"in Tigase XMPP Server configuration file. As a result, all configuration \"\n\"related to our repositories should be placed in ``repository`` section \"\n\"placed inside ``test`` section.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1463\nmsgid \"**Example.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1474\nmsgid \"Defaults\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1476\nmsgid \"\"\n\"As mentioned above, if we use ``MDRepositoryBeanWithStatistics`` as our \"\n\"base class for ``TestRepositoryMDBean``, then we may have different data \"\n\"sources used for different domains. By default, if we will not configure \"\n\"it otherwise, ``MDRepositoryBeanWithStatistics`` will create only single \"\n\"repository instance named ``default``. It will be used for all domains \"\n\"and it will, by default, use data source named the same as repository \"\n\"instance - it will use data source named ``default``. This defaults are \"\n\"equal to following configuration entered in the config file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1489\nmsgid \"Changing data source used by repository\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1491\nmsgid \"\"\n\"It is possible to make any repository use different data source than data\"\n\" source configured under the same name as repository instance. To do so, \"\n\"you need to set ``dataSourceName`` property of repository instance to the\"\n\" name of data source which it should use.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1493\nmsgid \"\"\n\"**Example setting repository ``default`` to use data source named \"\n\"``test``.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1506\nmsgid \"Configuring separate repository for domain\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1508\nmsgid \"\"\n\"To configure repository instance to be used for particular domain, you \"\n\"need to define repository with the same name as domain for which it \"\n\"should be used. It will, by default, use data source with name equal \"\n\"domain name.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1510\nmsgid \"\"\n\"**Separate repository for ``example.com`` using data source named \"\n\"``example.com``.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1533\nmsgid \"\"\n\"**Separate repository for ``example.com`` using data source named \"\n\"``test``.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1559\nmsgid \"\"\n\"In both examples presented above, for domains other than ``example.com``,\"\n\" repository instance named ``default`` will be used and it will use data \"\n\"source named ``default``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1562\nmsgid \"Repository Versioning\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1564\nmsgid \"\"\n\"It’s also possible to enable repository versioning capabilities when \"\n\"creating custom implementation. There are a couple of parts/steps to \"\n\"fully take advantage of this mechanism.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1566\nmsgid \"\"\n\"Each ``DataSource`` has a table ``tig_schema_versions`` which contains \"\n\"information about component schema version installed in the database \"\n\"associated with particular DataSource.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1569\nmsgid \"Enabling version checking in implementation\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1571\nmsgid \"\"\n\"First of all, repository implementation should implement \"\n\"``tigase.db.util.RepositoryVersionAware`` interface (all it’s methods are\"\n\" defined by default) and annotate it with \"\n\"``tigase.db.Repository.SchemaId``. For example .Repository annoted with \"\n\"``SchemaId`` and implementing ``RepositoryVersionAware``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1580\nmsgid \"\"\n\"This action alone will result in performing the check during Tigase XMPP \"\n\"Server startup and initialisation of repository whether tables, indexes, \"\n\"stored procedures and other elements are present in the configured data \"\n\"source in the required version. By default, required version matches the \"\n\"implementation version (obtained via call to \"\n\"``java.lang.Package.getImplementationVersion()``), however it’s possible \"\n\"to specify required version manually, either:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1582\nmsgid \"\"\n\"by utilizing ``tigase.db.util.RepositoryVersionAware.SchemaVersion`` \"\n\"annotation:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1593\nmsgid \"\"\n\"or by overriding ``tigase.db.util.RepositoryVersionAware.getVersion`` \"\n\"method:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1604\nmsgid \"Handling wrong version and the upgrade\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1606\nmsgid \"\"\n\"To detect that version information in database is inadequate following \"\n\"logic will take place:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1608\nmsgid \"\"\n\"if there is no version information in the database the service will be \"\n\"stopped completely prompting to install the schema (either via ``update-\"\n\"schema`` or ``install-schema`` depending on user preference);\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1610\nmsgid \"\"\n\"if there is an information about loaded component schema version in the \"\n\"repository and the base part of the required schema version (i.e. taking \"\n\"into account only *major.minor.bugfix* part) is different from the one \"\n\"present in the repository then:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1612\nmsgid \"\"\n\"if the required version of the component schema is *final* (i.e. non \"\n\"``SNAPSHOT``) the server will shutdown and print in the log file (namely \"\n\"``logs/tigase-console.log``) terminal error forcing the user to upgrade \"\n\"the schema;\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1614\nmsgid \"\"\n\"if the required version of the component schema is *non-final* (i.e. \"\n\"having ``SNAPSHOT`` part) then there will be a warning printed in the log\"\n\" file (namely ``logs/tigase-console.log``) prompting user to run the \"\n\"upgrade procedure due to possible changes in the schema but the \"\n\"*server*\\\\ **will not**\\\\ *stop*;\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1616\nmsgid \"\"\n\"Upgrade of the loaded schema in the database will be performed by \"\n\"executing:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1622\nmsgid \"\"\n\"The above command will load current configuration, information about all \"\n\"configured data sources and enabled components, and then perform upgrade \"\n\"of the schema of each configured component in the appropriate data \"\n\"source.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1624\nmsgid \"\"\n\"Depending on the type of the database (or specified annotation), how the \"\n\"upgrade procedure is handled internally is slightly different.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1629\nmsgid \"Relational databases (external handling)\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1631\nmsgid \"\"\n\"For all relational databases (MySQL, PostgreSQL, MS SQL Server, etc…) we \"\n\"highly recommend storing complete database schema in external files with \"\n\"following naming convention: \"\n\"``<database_type>-<component_name>-<version>.sql``, for example complete \"\n\"schema for our Test component version 0.0.5 intended for MySQL would be \"\n\"stored in file named ``mysql-test-0.0.5.sql``. What’s more - schema files\"\n\" must be stored under ``database/`` subdirectory in Tigase XMPP Server \"\n\"installation directory.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1635\nmsgid \"\"\n\"this can be controlled with ``external`` property of \"\n\"``Repository.SchemaId`` annotation, which defaults to \\\"true\\\", if set to\"\n\" ``false`` then handling will be done as described in :ref:`Relational \"\n\"databases (external handling)<relationalDatabases>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1637\nmsgid \"For example:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1639\nmsgid \"``database/mysql-test-0.0.1.sql``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1641\nmsgid \"``database/mysql-test-0.0.2.sql``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1643\nmsgid \"``database/mysql-test-0.0.3.sql``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1645\nmsgid \"``database/mysql-test-0.0.4.sql``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1647\nmsgid \"``database/mysql-test-0.0.5.sql``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1649\nmsgid \"\"\n\"During the upgrade process all required schema files will be loaded in \"\n\"the ascending version order. Version range will depend on the conditions \"\n\"and will follow simple rules:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1651\nmsgid \"\"\n\"Start of the range will start at the next version to the one currently \"\n\"loaded in the database (e.g. if the current version loaded to the \"\n\"database is ``0.0.3`` and we are deploying component version ``0.0.5`` \"\n\"then SchemaLoader will try to load schema from files: ``database/mysql-\"\n\"test-0.0.4.sql`` and ``database/mysql-test-0.0.5.sql``)\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1653\nmsgid \"\"\n\"If we are trying to deploy a *SNAPSTHOT* version of the component then \"\n\"schema file matching that version will always be included in the list of \"\n\"files to be loaded (e.g. if we are trying to deploy a nightly build with \"\n\"component version ``0.0.5-SNAPSHOT`` and currently loaded schema version \"\n\"in the database is ``0.0.5`` then SchemaLoader will include ``database\"\n\"/mysql-test-0.0.5.sql`` in the list of files to be loaded)\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1655\nmsgid \"\"\n\"It’s also possible to skip above filtering logic and force loading all \"\n\"schema files for particular component/database from ``database/`` \"\n\"directory by appending ``--forceReloadAllSchemaFiles=true`` parameter to \"\n\"the ``upgrade-schema``/``install-schema`` command.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1658\nmsgid \"Non-relational databases (internal handling)\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1660\nmsgid \"\"\n\"If there is a need to handle database schema internally (for example for \"\n\"cases like NoSQL databases or simply there is such preference) then it’s \"\n\"possible to do so by setting ``external`` attribute of \"\n\"``Repository.SchemaId`` annotation to ``false``:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1666\nmsgid \"\"\n\"In such case, ``updateSchema`` method from \"\n\"``tigase.db.util.RepositoryVersionAware`` interface should be implemented\"\n\" to handle installation/updating of the schema. It takes two arguments:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1668\nmsgid \"\"\n\"``Optional<Version> oldVersion`` - indicating current version of the \"\n\"schema loaded to the database (if it’s present)\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1670\nmsgid \"\"\n\"``Version newVersion`` - indicating required version (either version of \"\n\"component or specific version of the repository)\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1673\nmsgid \"Setting required repository version in database\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1675\nmsgid \"\"\n\"Each versioned schema file should consist at the end code responsible for\"\n\" setting appropriate version of the loaded schema in the form of Stored \"\n\"Procedure call with the name of the component and the version as \"\n\"parameters:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1677\nmsgid \"Postgresql\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1685\nmsgid \"MsSQL Server\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1694\nmsgid \"MySQL\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1702\nmsgid \"Derby\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1710\nmsgid \"\"\n\"In case of schema handled internally, after successful load (i.e. \"\n\"execution of the implemented \"\n\"``tigase.db.util.RepositoryVersionAware.updateSchema`` method returning \"\n\"``tigase.db.util.SchemaLoader.Result.ok``) the version in the database \"\n\"will be set to the current version of the component.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1712\nmsgid \"\"\n\"This allows (in case of schema handled externally) to load it by hand by \"\n\"directly importing ``.sql`` files into database.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1717\nmsgid \"Component Implementation - Lesson 8 - Lifecycle of a component\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1720\nmsgid \"Initialization of a component\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1722\nmsgid \"A startup hook in the Tigase is different from the shutdown hook.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1724\nmsgid \"\"\n\"This is because you cannot really tell when exactly the startup time is. \"\n\"Is it when the application started, is it when configuration is loaded, \"\n\"is it when all objects are initialized. And this might be even different \"\n\"for each component. Therefore, in fact, there is no startup hook in \"\n\"Tigase in the same sense as the shutdown hook.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1726\nmsgid \"\"\n\"There are a few methods which are called at startup time of a component \"\n\"in the following order:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1728\nmsgid \"\"\n\"**Constructor** - there is of course constructor which has no parameters.\"\n\" However it does not guarantee that this instance of the component will \"\n\"be used at all. The object could be created just to get default values of\"\n\" a config fields and may be destroyed afterwards.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1730\nmsgid \"\"\n\"**Getters/Setters** - at second step of initialization of a component, \"\n\"Kernel configures component by reading and setting values of fields \"\n\"annotated with ``@ConfigField()`` annotation. If there is a public getter\"\n\" or setter for the same name as an annotated field - it will be used.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1732\nmsgid \"\"\n\"**void beanConfigurationChanged(Collection<String> changedFields)** \"\n\"*(optional)* - if component implements ``ConfigurationChangedAware`` \"\n\"interface, this method will be called to notify component about fields \"\n\"which values were changed. It is useful if case in which component \"\n\"internals depends on configuration stored in more than one field, as it \"\n\"allows you to reconfigure component internals only once.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1734\nmsgid \"\"\n\"**void register(Kernel kernel)** *(optional)* - if component implements \"\n\"``RegistrarBean`` interface this method is called to allow registration \"\n\"of component private beans.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1736\nmsgid \"\"\n\"**Dependency Injection** - during this time Kernel injects beans to \"\n\"component fields annotated with ``@Inject``. If public getters or setters\"\n\" for this fields exist - kernel will use them.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1738\nmsgid \"\"\n\"**void initialized()** *(optional)* - called if component implements \"\n\"``Initializable`` interface to notify it that configuration is set and \"\n\"dependencies are injected.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1740\nmsgid \"\"\n\"**void start()** - during this call component starts it’s internal jobs \"\n\"or worker threads or whatever it needs for future activity. Component’s \"\n\"queues and threads are initialized at this point. **(after this method \"\n\"returns the component is ready)**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1742\nmsgid \"\"\n\"Therefore, the ``start()`` hook is the best point if you want to be sure \"\n\"that component is fully loaded, initialized and functional.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1746\nmsgid \"\"\n\"Component instance may be started and stopped only once, however new \"\n\"instances of the same component with the same name may be created during \"\n\"Tigase XMPP Server uptime, ie. as a result of a server reconfiguration.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1749\nmsgid \"Reconfiguration\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1751\nmsgid \"\"\n\"During lifecycle of a component instance it may happen that Tigase XMPP \"\n\"Server will be reconfigured. If change in configuration of this component\"\n\" will not be related to it’s activity, then Kernel will set values of \"\n\"changes fields annotated with ``@ConfigField()``. In this case public \"\n\"field setters may be used.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1755\nmsgid \"\"\n\"If component implements ``ConfigurationChangedAware`` interface, then \"\n\"method **void beanConfigurationChanged(Collection<String> \"\n\"changedFields)** will be called to notify component about fields which \"\n\"values were changed. It is useful if same component internal depends on \"\n\"configuration stored in more than one field, as it allows you to \"\n\"reconfigure this internal once.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1758\nmsgid \"Update of injected dependencies\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1760\nmsgid \"\"\n\"During lifecycle of a component instance it may happen that due to \"\n\"reconfiguration of a server other bean needs to be injected as a \"\n\"dependency to a component. In this case Tigase Kernel will inject \"\n\"dependencies to fields annotated with ``@Inject`` which value needs to be\"\n\" updated.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1763\nmsgid \"Stopping a component\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1765\nmsgid \"\"\n\"Component instance may be stopped at any point of Tigase XMPP Server \"\n\"runtime, ie. due to reconfiguration, or due to server graceful shutdown.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1767\nmsgid \"In both cases following methods of a component will be called:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1769\nmsgid \"**void stop()** - first method stops component internal processing queues.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1771\nmsgid \"\"\n\"**void beforeUnregister()** *(optional)* - if component implements \"\n\"@UnregisterAware@ interface this method is called to notify instance of a\"\n\" component that it is being unloaded.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1773\nmsgid \"\"\n\"**void unregister(Kernel kernel)** *(optional)* - if component implements\"\n\" ``RegistrarBean`` called to give component a way to unregister beans (if\"\n\" needed).\"\nmsgstr \"\"\n\n#~ msgid \"`Component implementation - Lesson 1 - Basics <#cil1>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Component implementation - Lesson 2 - Configuration <#cil2>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Component implementation - Lesson 3 - Multi-Threading <#cil3>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Component implementation - Lesson 4 - Service Discovery <#cil4>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Component implementation - Lesson 5 - Statistics <#cil5>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Component implementation - Lesson 6 - Scripting Support <#cil6>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Component implementation - Lesson 7 - Data Repository <#cil7>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Component implementation - Lesson 8 - Startup Time <#cil8>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Packet Filtering in Component <#packetfiltering>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"_ cil1\"\n#~ msgstr \"\"\n\n#~ msgid \": _cil2:\"\n#~ msgstr \"\"\n\n#~ msgid \"_ cil3\"\n#~ msgstr \"\"\n\n#~ msgid \"_ cil4\"\n#~ msgstr \"\"\n\n#~ msgid \"_ cil5\"\n#~ msgstr \"\"\n\n#~ msgid \": _cil6:\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"this can be controlled with ``external``\"\n#~ \" property of ``Repository.SchemaId`` annotation,\"\n#~ \" which defaults to \\\"true\\\", if set\"\n#~ \" to ``false`` then handling will be\"\n#~ \" done as described in `Relational \"\n#~ \"databases (external handling) <#update-\"\n#~ \"relational>`__\"\n#~ msgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Development/Data_Sources_And_Repositories.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-05-27 12:30-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:2\nmsgid \"Data Source and Repositories\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:4\nmsgid \"\"\n\"In Tigase XMPP Server 8.0.0 a new concept of data sources was introduced.\"\n\" It was introduced to create distinction between classes responsible for \"\n\"maintaining connection to actual data source and classes operating on \"\n\"this data source.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:7\nmsgid \"Data sources\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:9\nmsgid \"|Relations between DataSourceBean and DataSources|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:68\nmsgid \"Relations between DataSourceBean and DataSources\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:12\nmsgid \"DataSource\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:14\nmsgid \"\"\n\"``DataSource`` is an interface which should be implemented by all classes\"\n\" implementing access to data source, i.e. implementing access to database\"\n\" using JDBC connection or to MongoDB. Implementation of ``DataSource`` is\"\n\" automatically selected using uri provided in configuration and \"\n\"``@Repository.Meta`` annotation on classes implementing ``DataSource`` \"\n\"interface.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:17\nmsgid \"DataSourcePool\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:19\nmsgid \"\"\n\"``DataSourcePool`` is interface which should be implemented by classes \"\n\"acting as a pool of data sources for single domain. There is no \"\n\"requirement to create class implementing this interface, however if \"\n\"implementation of ``DataSource`` is blocking and does not support \"\n\"concurrent requests, then creation of ``DataSourcePool`` is recommended. \"\n\"An example of such case is implementation of ``DataRepositoryImpl`` which\"\n\" executes all requests using single connection and for this class there \"\n\"is ``DataRepositoryPool`` implementing ``DataSourcePool`` interface and \"\n\"improving performance. Implementation of ``DataSourcePool`` is \"\n\"automatically selected using uri provided in configuration and \"\n\"``@Repository.Meta`` annotation on classes implementing \"\n\"``DataSourcePool`` interface.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:22\nmsgid \"DataSourceBean\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:24\nmsgid \"\"\n\"This class is a helper class and provides support for handling multiple \"\n\"data sources. You can think of a ``DataSourceBean`` as a map of named \"\n\"``DataSource`` or ``DataSourcePool`` instances. This class is also \"\n\"responsible for initialization of data source. Moreover, if data source \"\n\"will change during runtime ``DataSourceBean`` is responsible for firing a\"\n\" ``DataSourceChangedEvent`` to notify other classes about this change.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:27\nmsgid \"User and authentication repositories\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:29\nmsgid \"\"\n\"This repositories may be using existing (configured and initialized) data\"\n\" sources. However, it is also possible to that they may have their own \"\n\"connections. Usage of data sources is recommended if possible.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:31\nmsgid \"|Relations between AuthRepositories and DataSources|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:69\nmsgid \"Relations between AuthRepositories and DataSources\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:33\nmsgid \"|Relations between UserRepositories and DataSources|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:70\nmsgid \"Relations between UserRepositories and DataSources\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:36\nmsgid \"AuthRepository and UserRepository\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:38\nmsgid \"\"\n\"This are a base interfaces which needs to be implemented by \"\n\"authentication repository (``AuthRepository``) and by repository of users\"\n\" (``UserRepository``). Classes implementing this interfaces should be \"\n\"only responsible for retrieving data from data sources.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:41\nmsgid \"AuthRepositoryPool and UserRepositoryPool\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:43\nmsgid \"\"\n\"If class implementing ``AuthRepositoryPool`` or ``UserRepositoryPool`` is\"\n\" not using data sources or contains blocking or is not good with \"\n\"concurrent access, then it should be wrapped within proper repository \"\n\"pool. Most of implementations provided as part of Tigase XMPP Server do \"\n\"not require to be wrapped within repository pool. If your implementation \"\n\"is blocking or not perform well with concurrent access (ie. due to \"\n\"synchronization), then it should be wrapped within this pool. To wrap \"\n\"implementation within a pool, you need to set ``pool-cls`` property of \"\n\"configured user or authentication repository in your configuration file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:46\nmsgid \"AuthRepositoryMDPoolBean and UserRepositoryMDPoolBean\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:48\nmsgid \"\"\n\"This classes are for classes implementing ``AuthRepository`` and \"\n\"``UserRepository`` what ``DataSourceBean`` is for classes implementing \"\n\"``DataSource`` interface. This classes holds map of named authentication \"\n\"or user repositories. They are also responsible for initialization of \"\n\"classes implementing this repositories.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:52\nmsgid \"Other repositories\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:54\nmsgid \"\"\n\"It is possible to implement repositories not implementing \"\n\"``AuthRepository`` or ``UserRepository``. Each type of custom repository \"\n\"should have its own API and its own interface.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:56\nmsgid \"|Relations between custom repositories and DataSources|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:71\nmsgid \"Relations between custom repositories and DataSources\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:59\nmsgid \"DataSourceAware\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:61\nmsgid \"\"\n\"Custom repositories should implement they own interface specifying its \"\n\"API. This interfaces should extend ``DataSourceAware`` interface which is\"\n\" base interface required to be implemented by custom repositories. \"\n\"``DataSourceAware`` has a method ``setDataSource()`` which will be called\"\n\" with instance of data source to initialize instance of custom \"\n\"repository. Implementations should be annotated with ``@Repository.Meta``\"\n\" implementation to make the automatically selected for proper type of \"\n\"``DataSource`` implementation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:64\nmsgid \"MDRepositoryBean\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:66\nmsgid \"\"\n\"It is required to create a class extending ``MDRepositoryBean`` \"\n\"implementing same custom interface as the custom repository. This class \"\n\"will be a multi domain pool, allowing you to have separate implementation\"\n\" of custom repository for each domain. Moreover, it will be responsible \"\n\"for creation and initialization of your custom repository instances.\"\nmsgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Development/EventBus_API.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/EventBus_API.rst:3\nmsgid \"EventBus API in Tigase\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:5\nmsgid \"\"\n\"EventBus is a custom publish-subscribe mechanism which allows for the use\"\n\" of Event Listener within Tigase Server. For a more detailed overview of \"\n\"EventBus and it’s features, please visit `The Administration Guide \"\n\"<http://docs.tigase.org/tigase-\"\n\"server/snapshot/Administration_Guide/html/#eventBus>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:8\nmsgid \"EventBus API\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:10\nmsgid \"To create instance of EventBus use the following code:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:18\nmsgid \"\"\n\"Remember, that EventBus is asynchronous. All handlers are called in a \"\n\"different thread than the thread that initially fired the event.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:21\nmsgid \"Events\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:23\nmsgid \"\"\n\"Events may be defined in two ways: as a class |ss| or as an XML \"\n\"element(XML/Element based events are deprecated since version 8.2 and \"\n\"will be removed in version 9.0)\\\\. |se|\\\\\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:25\nmsgid \"**Serialized event class.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:39\nmsgid \"**Event class.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:53\nmsgid \"|ss| **XML Element event(deprecated)**\\\\ |se|\\\\\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:63\nmsgid \"\"\n\"Events defined as XML element and class implementing ``Serializable`` \"\n\"interface will be distributed to all servers in cluster. Event \"\n\"``SampleEvent`` will be broadcast only in the same instance what fired \"\n\"the event.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:66\nmsgid \"Requirements for class-based events\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:68\nmsgid \"Default, explicit, public, paremeter-less constructor is mandatory.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:70\nmsgid \"\"\n\"If the event should be delivered to all cluster nodes then it **MUST** \"\n\"implement ``Serializable`` interface.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:72\nmsgid \"\"\n\"Variables serialisation follows ``Serializable`` semantics, which means \"\n\"that ``final``, ``static`` nor ``transient`` fields will be skipped. \"\n\"What’s more, fields with ``null`` value will not be serialised neither.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:75\nmsgid \"Serialisation of class-based events\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:77\nmsgid \"\"\n\"Class based events are serialized (if it is required and possible) to XML\"\n\" element. Name of XML element is taken from full name of class:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:79\nmsgid \"**Class based event serialized to XML.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:88\nmsgid \"Firing events\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:90\nmsgid \"To fire event, just get instance of EventBus and call method ``fire()``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:92\nmsgid \"**Firing serialized event.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:100\nmsgid \"**Firing simple event.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:108\nmsgid \"|ss| **Firing event based on XML Element(deprecated)** |se|\\\\\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:117\nmsgid \"Handling events\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:119\nmsgid \"\"\n\"To handle fired event, we have to register listener in EventBus. When \"\n\"listener is registered, EventBus automatically subscribes for this type \"\n\"of event in all instances in cluster.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:121\nmsgid \"\"\n\"Depends on expected event type, we have to decide what type of listener \"\n\"we should register.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:124\nmsgid \"Handling class based events\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:126\nmsgid \"\"\n\"This option is reserved for class based events only. It doesn’t matter if\"\n\" it is serialized class or not.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:137\nmsgid \"\"\n\"To make registering listeners more easy, you can use method \"\n\"``registerAll()`` from EventBus. This method registers all methods given \"\n\"class, annotated by ``@HandleEvent`` as listeners for event declared as \"\n\"the method argument.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:154\nmsgid \"|ss| Handling XML events |se|\\\\\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:156\nmsgid \"\"\n\"To handle XML events we have to register listener for specific event \"\n\"package and name. In our example, package is empty because event name has\"\n\" no package declared (see also :ref:`Filtering events<filteringEvents>`).\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:173\nmsgid \"\"\n\"Because serialized class events, ale transformed to XML elements, we are \"\n\"able to listen for XML representation of class based event. To do that, \"\n\"we have to register listener for specific package and class name:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:186\nmsgid \"**Important**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:188\nmsgid \"\"\n\"XML events created on others cluster node, will have attribute ``remote``\"\n\" set to ``true`` and attribute ``source`` set to event creator node name:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:199\nmsgid \"Filtering events\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:201\nmsgid \"\"\n\"Sometimes you may want to receive many kinds of events with the same \"\n\"handler. EventBus has very simple mechanism to generalization:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:208\nmsgid \"\"\n\"This listener will be called for each event with given package name (XML \"\n\"based, or serialized class based).\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:210\nmsgid \"\"\n\"This listener will be called for ALL events (XML based, or serialized \"\n\"class based).\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:212\nmsgid \"In case of class based events, EventBus is checking class inheritance.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:224\nmsgid \"Will be called, because this is listener stricte for ``SpecificEvent``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/EventBus_API.rst:226\nmsgid \"Will be called, because ``SpecificEvent`` extends ``MainEvent``.\"\nmsgstr \"\"\n\n#~ msgid \"\"\n#~ \"To handle XML events we have to\"\n#~ \" register listener for specific event \"\n#~ \"package and name. In our example, \"\n#~ \"package is empty because event name \"\n#~ \"has no package declared (see also \"\n#~ \"`Filtering events <#_filtering_events>`__).\"\n#~ msgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Development/Experimental.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Experimental.rst:2\nmsgid \"Experimental\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:4\nmsgid \"\"\n\"The guide contains description of non-standard or experimental \"\n\"functionality of the server. Some of them are based on never published \"\n\"extensions, some of them are just test implementation for new ideas or \"\n\"performance improvements.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:6\nmsgid \":ref:`Dynamic Rosters<dynamicRosters>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:8\nmsgid \":ref:`Mobile Optimizations<mobileoptimizations>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:10\nmsgid \":ref:`Bosh Session Cache<boshsessioncache>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:16\nmsgid \"Dynamic Rosters\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:19\n#: ../../Tigase_Development/Experimental.rst:153\n#: ../../Tigase_Development/Experimental.rst:226\nmsgid \"Problem Description\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:21\nmsgid \"\"\n\"Normal roster contacts stored and created as **dynamic roster parts** are\"\n\" delivered to the end user transparently. The XMPP client doesn’t really \"\n\"know what contacts come from its own **static** roster created manually \"\n\"by the user and what contacts come from a **dynamic** roster part; \"\n\"contacts and groups generated dynamically by the server logic.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:23\nmsgid \"\"\n\"Some specialized clients need to store extra bits of information about \"\n\"roster contacts. For the normal user **static** roster information can be\"\n\" stored as private data and is available only to this single user. In \"\n\"some cases however, clients need to store information about contacts from\"\n\" the dynamic roster part and this information must be available to all \"\n\"users accessing **dynamic** roster part.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:25\nmsgid \"\"\n\"The protocol defined here allows the exchange of information, saving and \"\n\"retrieving extra data about the contacts.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:28\nmsgid \"Syntax and Semantics\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:30\nmsgid \"\"\n\"Extra contact data is accessed using IQ stanzas, specifically by means of\"\n\" a child element qualified by the **jabber:iq:roster-dynamic** namespace.\"\n\" The child element MAY contain one or more children, each describing a \"\n\"unique contact item. Content of the element is not specified and is \"\n\"implementation dependent. From Tigase’s point of view it can contain any \"\n\"valid XML data. Whole element is passed to the DynamicRoster \"\n\"implementation class as is and without any verification. Upon retrieving \"\n\"the contact extra data the DynamicRoster implementation is supposed to \"\n\"provide a valid XML element with all the required data for requested \"\n\"**jid**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:32\nmsgid \"\"\n\"The **jid** attribute specifies the Jabber Identifier (JID) that uniquely\"\n\" identifies the roster item. Inclusion of the **jid** attribute is \"\n\"**REQUIRED**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:34\nmsgid \"Following actions on the extra contact data are allowed:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:36\nmsgid \"**set** - stores extra information about the contact\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:38\nmsgid \"**get** - retrieves extra information about the contact\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:41\nmsgid \"Retrieving Contact Data\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:43\nmsgid \"\"\n\"Upon connecting to the server and becoming an active resource, a client \"\n\"can request the extra contact data. This request can be made either \"\n\"before or after requesting the user roster. The client’s request for the \"\n\"extra contact data is **OPTIONAL**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:45\nmsgid \"\"\n\"Example: Client requests contact extra data from the server using **get**\"\n\" request:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:55\nmsgid \"\"\n\"Example: Client receives contact extra data from the server, but there \"\n\"was either no extra information for the user, or the user was not found \"\n\"in the dynamic roster:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:65\nmsgid \"\"\n\"Example: Client receives contact extra data from the server, and there \"\n\"was some extra information found about the contact:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:80\nmsgid \"Updating/Saving Extra Information About the Contact\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:82\nmsgid \"\"\n\"At any time, a client **MAY** update extra contact information on the \"\n\"server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:84\nmsgid \"Example: Client sends contact extra information using **set** request.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:97\n#: ../../Tigase_Development/Experimental.rst:126\nmsgid \"Client responds to the server:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:103\nmsgid \"\"\n\"A client **MAY** update contact extra information for more than a single \"\n\"item in one request:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:105\nmsgid \"\"\n\"Example: Client sends contact extra information using **set** request \"\n\"with many <item/> elements.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:133\n#: ../../Tigase_Development/Experimental.rst:204\nmsgid \"Configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:135\nmsgid \"\"\n\"DynamicRoster implementation class should be configured in the \"\n\"**config.tdsl** file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:145\nmsgid \"\"\n\"If you want to pass configuration to your implementation simply use \"\n\"``@ConfigField`` annotation on your variable (see :ref:`Component \"\n\"implementation - Lesson 2 - Configuration<cil2>` for more details).\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:150\nmsgid \"Mobile Optimizations\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:155\nmsgid \"\"\n\"In default configuration stanzas are sent to the client when processing \"\n\"is finished, but in mobile environment sending or receiving data drains \"\n\"battery due to use of the radio.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:157\nmsgid \"\"\n\"To save energy data should be sent to client only if it is important or \"\n\"client is waiting for it.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:160\nmsgid \"Solution\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:162\nmsgid \"\"\n\"When mobile client is entering inactive state it notifies server about it\"\n\" by sending following stanza:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:172\nmsgid \"\"\n\"After receiving stanza server starts queuing stanza which should be send \"\n\"to mobile client. What kind of queued stanzas depends on the plugins used\"\n\" and in case of **Mobile v3** presence stanzas are queued as well as \"\n\"message stanzas which are Message Carbons. Any other stanza (such as iq \"\n\"or plain message) is sent immediately to the client and every stanza from\"\n\" queue is also sent at this time.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:174\nmsgid \"\"\n\"When mobile client is entering active state it notifies server by sending\"\n\" following stanza:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:184\nmsgid \"After receiving stanza server sends all queued stanzas to the client.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:186\nmsgid \"\"\n\"Also all stanzas from queue will be sent if number of stanzas in queue \"\n\"will reach queue size limit. By default this limit is set to 50.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:189\nmsgid \"Queuing Algorithms\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:191\nmsgid \"There are three mobile optimization plugins for Tigase:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:193\nmsgid \"**Mobile v1** - all presence stanzas are kept in queue\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:195\nmsgid \"**Mobile v2** - only last presence from each source is kept in queue\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:197\nmsgid \"\"\n\"**Mobile v3** - only last presence from each source is kept in queue, \"\n\"also Message Carbons are queued\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:199\nmsgid \"\"\n\"If you wish to activate you Mobile v1 plugin you need to send presented \"\n\"above with xmlns attribute value replaced with \"\n\"http://tigase.org/protocol/mobile#v1\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:201\nmsgid \"\"\n\"If you wish to activate you Mobile v2 plugin you need to send presented \"\n\"above with xmlns attribute value replaced with \"\n\"http://tigase.org/protocol/mobile#v2\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:206\nmsgid \"\"\n\"Mobile plugins are not activated by default thus additional entry in the \"\n\"``config.tdsl`` is required:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:214\nmsgid \"\"\n\"You may substitute ``mobile_v1`` with ``mobile_v2`` or ``mobile_v3`` \"\n\"depending on which algorithm you wish to use.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:218\nmsgid \"USE ONLY ONE PLUGIN AT A TIME!\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:223\nmsgid \"Bosh Session Cache\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:228\nmsgid \"\"\n\"Web clients have no way to store any data locally, on the client side. \"\n\"Therefore after a web page reload the web clients loses all the context \"\n\"it was running in before the page reload.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:230\nmsgid \"\"\n\"Some elements of the context can be retrieved from the server like the \"\n\"roster and all contacts presence information. Some other data however, \"\n\"can not be restored easily like opened chat windows and the chat windows \"\n\"contents. Even if the roster restoring is possible, this operation is \"\n\"very expensive in terms of time and resources on the server side.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:232\nmsgid \"\"\n\"On of possible solutions is to allow web client to store some data in the\"\n\" Bosh component cache on the server side for the time while the Bosh \"\n\"session is active. After the page reloads, if the client can somehow \"\n\"retrieve SID (stored in cookie or provided by the web application running\"\n\" the web client) it is possible to reload all the data stored in the Bosh\"\n\" cache to the client.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:234\nmsgid \"\"\n\"Bosh session context data are: roster, contacts presence information, \"\n\"opened chat windows, chat windows content and some other minor data. \"\n\"Ideally the web client should be able to store any data in the Bosh \"\n\"component cache it wants.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:238\nmsgid \"Bosh Session Cache Description\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:240\nmsgid \"\"\n\"The Bosh Session Cache is divided into 2 parts - automatic cache and \"\n\"dynamic cache.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:242\nmsgid \"\"\n\"The reason for splitting the cache into 2 parts is that some data can be \"\n\"collected automatically by the Bosh component and it would be very \"\n\"inefficient to require the client to store the data in the Bosh cache. \"\n\"The best example for such data is the Roster and contacts presence \"\n\"information.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:244\nmsgid \"\"\n\"**automatic cache** - is the cache part which is created automatically by\"\n\" the Bosh component without any interaction with the client. The client, \"\n\"however, can access the cache at any time. I would say this is a read-\"\n\"only cache but I don’t want to stop client from manipulating the cache if\"\n\" it needs. The client usually, only retrieves data from this part of the \"\n\"cache as all changes should be automatically updated by the Bosh \"\n\"component. The general idea for the automatic cache is that the data \"\n\"stored there are accessible in the standard XMPP form. So no extra code \"\n\"is needed for processing them.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:246\nmsgid \"\"\n\"**dynamic cache** - is the cache part which is or can be modified at any \"\n\"time by the client. Client can store, retrieve, delete and modify data in\"\n\" this part of the cache.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:249\nmsgid \"Cache Protocol\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:251\nmsgid \"\"\n\"All the Bosh Session Cache actions are executed using additional \"\n\"``<body/>`` element attributes: ``cache`` and ``cache-id``. Attribute \"\n\"cache stores the action performed on the Bosh ``cache`` and the ``cache-\"\n\"id`` attribute refers to the ``cache`` element if the action attribute \"\n\"needs it. ``cache-id`` is optional. There is a default cache ID (empty \"\n\"one) associated with the elements for which the ``cache-id`` is not \"\n\"provided.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:253\nmsgid \"\"\n\"If the ``<body/>`` element contains the cache attribute it means that all\"\n\" data included in the ``<body/>`` refer to the cache action. It is not \"\n\"allowed, for example to send a message in the body and have the cache \"\n\"action set to **get**. The ``<body/>`` element with cache action **get**,\"\n\" **get_all**, **on**, **off**, **remove** must be empty. The ``<body/>`` \"\n\"element with actions **set** or **add** must contain data to store in the\"\n\" cache.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:256\nmsgid \"Cache Actions\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:258\nmsgid \"\"\n\"**on** or **off** - the client can switch the cache on or off at any time\"\n\" during the session. It is recommended, however that the client switches \"\n\"the cache **on** in the first body packet, otherwise some information \"\n\"from the automatic cache may be missing. The automatic cache is created \"\n\"from the stream of data passing the Bosh component. Therefore if the \"\n\"cache is switched on after the roster retrieval is completed then the \"\n\"roster information will be missing in the cache. If the cache is set to \"\n\"**off** (the default value) all requests to the cache are ignored. This \"\n\"is to ensure backward compatibility with the original Bosh specification \"\n\"and to make sure that in a default environment the Bosh component doesn’t\"\n\" consume any extra resources for cache processing and storing as the \"\n\"cache wouldn’t be used by the client anyway.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:260\nmsgid \"\"\n\"**get** - retrieves the cache element pointing by the cache-id from the \"\n\"Bosh cache. Note there is no **result** cache action. The ``<body/>`` \"\n\"sent as a response from the server to the client may contain cache \"\n\"results for a given cache-id and it may also contain other data received \"\n\"by the Bosh component for the client. It may also happen that large \"\n\"cached data are split into a few parts and each part can be sent in a \"\n\"separate ``<body/>`` element. It may usually happen for the Roster data.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:262\nmsgid \"\"\n\"**get_all** - retrieves all the elements kept in the Bosh cache. That \"\n\"action can can be performed after the page reload. The client doesn’t \"\n\"have to request every single cached item one by one. It can retrieve all \"\n\"cache items in one go. It doesn’t mean however the whole cache is sent to\"\n\" the client in a single ``<body/>`` element. The cache content will be \"\n\"divided into a smaller parts of a reasonable size and will be sent to the\"\n\" client in a separate ``<body/>`` elements. It may also happen that the \"\n\"**``<body/>``** element contain the cache elements as well as the new \"\n\"requests sent to the user like new messages or presence information.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:264\nmsgid \"\"\n\"**set** - sends data to the Bosh Session cache for later retrieval. The \"\n\"client can store any data it wants in the cache. The Bosh components \"\n\"stores in the cache under the selected ID all the data inside the \"\n\"``<body/>`` element. The only restriction is that the cached data must be\"\n\" a valid XML content. The data are returned to the client in exactly the \"\n\"same form as they were received from the server. The **set** action \"\n\"replaces any previously stored data under this ID.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:266\nmsgid \"\"\n\"**add** - adds new element to the cache under the given ID. This action \"\n\"might be useful for storing data for the opened chat window. The client \"\n\"can add new elements for the chat window, like new messages, icons and so\"\n\" on…​\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:268\nmsgid \"**remove** - removes the cached element for the given cache ID.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:271\nmsgid \"Cache ID\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:273\nmsgid \"\"\n\"Cache ID can be an any character string. There might be some IDs reserved\"\n\" for a special cases, like for the Roster content. To avoid any future ID\"\n\" conflicts reserved ID values starts with: **bosh** - string.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:275\nmsgid \"\"\n\"There is a default cache ID - en empty string. Thus cache-id attribute \"\n\"can be omitted and then the requests refers to data stored under the \"\n\"default (empty) ID.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:278\nmsgid \"Reserved Cache ID Names\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:280\nmsgid \"Here is a list of reserved Cache IDs:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:282\nmsgid \"\"\n\"**bosh-roster** - The user roster is cached in the Bosh component in \"\n\"exactly the same form as it was received from the core server. The Bosh \"\n\"Cache might or might not do optimizations on the roster like removing \"\n\"elements from the cached roster if the roster **remove** has been \"\n\"received or may just store all the roster requests and then send them all\"\n\" to the client. There is a one mandatory optimization the Bosh Cache must\"\n\" perform. It must remember the last (and only the last) presence status \"\n\"for each roster item. Upon roster retrieving from the cache the Bosh \"\n\"component must send the roster item first and then the presence for the \"\n\"item. If the presence is missing it means an offline presence. If the \"\n\"roster is small it can be sent to the client in a single packet but for a\"\n\" large roster it is recommended to split contact lists to batches of max \"\n\"100 elements. The Bosh component may send all roster contacts first and \"\n\"then all presences or it can send a part of the roster, presences for \"\n\"sent items, next part of the roster, presences for next items and so on.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Experimental.rst:284\nmsgid \"\"\n\"**bosh-resource-bind** - The user resource bind is also cached to allow \"\n\"the client quickly retrieve information about the full JID for the \"\n\"established Bosh session.\"\nmsgstr \"\"\n\n#~ msgid \"`Dynamic Rosters <#dynamicRosters>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Mobile Optimizations <#mobileoptimizations>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Bosh Session Cache <#boshsessioncache>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"If you want to pass configuration \"\n#~ \"to your implementation simply use \"\n#~ \"``@ConfigField`` annotation on your variable\"\n#~ \" (see `Component implementation - Lesson\"\n#~ \" 2 - Configuration <#cil2>`__ for \"\n#~ \"more details).\"\n#~ msgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:2\nmsgid \"Hack Tigase XMPP Server in Eclipse\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:4\nmsgid \"\"\n\"If you want to write code for **Tigase** server we recommend using \"\n\"`Eclipse IDE <//https://eclipse.org/downloads/>`__. Either the IDE for \"\n\"Java or Java EE developers will work.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:7\nmsgid \"Requirements\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:9\nmsgid \"\"\n\"Eclipse IDE currently requires the use of `Java Development Kit 8 \"\n\"<http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:11\nmsgid \"\"\n\"You will also need the M2E plugin for Maven integration, however this can\"\n\" be done inside Eclipse now, so refer to the :ref:`Plugin \"\n\"Installation<m2EPlugin>` section for that.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:14\nmsgid \"Installation\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:16\nmsgid \"\"\n\"Eclipse does not come as an installer, but rather an archive. Extract the\"\n\" directory to a working location wherever you would like. Now install the\"\n\" JDK software, location is not important as Eclipse will find it \"\n\"automatically.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:18\nmsgid \"Before we begin, we will need to clone the repository from git.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:21\nmsgid \"Linux\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:23\nmsgid \"\"\n\"For linux operating systems, navigate to a directory where you want the \"\n\"repository to be cloned to and type the following into terminal.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:30\nmsgid \"Windows\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:32\nmsgid \"\"\n\"Please see the Windows coding guide for instructions on how to obtain \"\n\"source code from git. If you don’t want to install git software \"\n\"specifically, you can use Eclipse’s git plugin to obtain the repository \"\n\"without any new software. First click on File, then Import…​ Next select \"\n\"from Git folder and the Projects from Git\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:34\nmsgid \"|win git1|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:89\nmsgid \"win git1\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:36\nmsgid \"Click next, and now select clone URI\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:38\nmsgid \"|win git2|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:90\nmsgid \"win git2\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:40\nmsgid \"Now click next, and in this window enter the following into the URI field\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:46\nmsgid \"The rest of the fields will populate automatically\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:48\nmsgid \"|win git3|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:91\nmsgid \"win git3\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:50\nmsgid \"\"\n\"Select the master branch, and any branches you wish to edit. **The master\"\n\" branch should be the only one you need, branches are used for specific \"\n\"code changes**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:52\nmsgid \"|win git4|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:92\nmsgid \"win git4\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:54\nmsgid \"\"\n\"Now select the directory where you wanted to clone the repository to. \"\n\"This was function as the project root directory you will use later on in \"\n\"the setup.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:56\nmsgid \"|win git5|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:93\nmsgid \"win git5\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:58\nmsgid \"\"\n\"Once you click next Eclipse will download the repository and any branches\"\n\" you selected to that directory. Note you will be unable to import this \"\n\"git directory since there are no git a project specific files downloaded.\"\n\" However, once downloading is complete you may click cancel, and the git \"\n\"repository will remain in the directory you have chosen.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:63\nmsgid \"Setup\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:65\nmsgid \"\"\n\"Once you have the main window open and have established a workspace \"\n\"(where most of your working files will be stored), click on Help and then\"\n\" Install New Software…​\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:67\nmsgid \"|Eclipse help|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:94\nmsgid \"Eclipse help\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:69\nmsgid \"\"\n\"Under the Work With field enter the following and press enter: \"\n\"http://download.eclipse.org/technology/m2e/releases/\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:71\nmsgid \"\"\n\"**Note: You may wish to click the Add…​ button and add the above location\"\n\" as a permanent software location to keep the location in memory**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:73\nmsgid \"|Eclipse m2Einstall|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:95\nmsgid \"Eclipse m2Einstall\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:75\nmsgid \"\"\n\"You should see the M2 Eclipse software packages show in the main window. \"\n\"Click the check-box and click Next. Once the installer is finished it \"\n\"will need to restart Eclipse.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:77\nmsgid \"Once that is done, lets connect Eclipse to the cloned repository.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:79\nmsgid \"\"\n\"Click File and Import…​ to bring up the import dialog window. Select \"\n\"Maven and then Existing Maven Project.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:81\nmsgid \"|Eclipse importMaven|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:96\nmsgid \"Eclipse importMaven\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:83\nmsgid \"\"\n\"Now click Next and point the root directory to where you cloned the git \"\n\"repository, Eclipse should automatically see the pom.xml file and show up\"\n\" in the next window.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:85\nmsgid \"|Eclipse importMaven2|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:97\nmsgid \"Eclipse importMaven2\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:87\nmsgid \"\"\n\"Once the import is finished, you are able to now begin working with \"\n\"Tigase’s code inside Eclipse! Happy coding!\"\nmsgstr \"\"\n\n#~ msgid \"\"\n#~ \"You will also need the M2E plugin\"\n#~ \" for Maven integration, however this \"\n#~ \"can be done inside Eclipse now, so\"\n#~ \" refer to the `Plugin Installation \"\n#~ \"<#m2EPlugin>`__ section for that.\"\n#~ msgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Development/Old_Stuff.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:2\nmsgid \"Old Stuff\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:4\nmsgid \"\"\n\"This contains sections on old features, or information pertaining to old \"\n\"builds of Tigase. It is kept here for archival purposes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:7\nmsgid \"Tigase DB Schema Explained\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:9\nmsgid \"\"\n\"The schema basics, how it looks like and brief explanation to all rows \"\n\"can be found in the `list of schema files <https://github.com/tigase\"\n\"/tigase-server/tree/master/src/main/database>`__. However, this is hardly\"\n\" enough to understand how it works and how all the data is accessed. \"\n\"There are only 3 basic tables which actually keep all the Tigase server \"\n\"users' data: **tig_users**, **tig_nodes** and **tig_pairs**. Therefore it\"\n\" is not clear at first how Tigase’s data is organized.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:11\nmsgid \"\"\n\"Before you can understand the Tigase XMPP Server database schema, how it \"\n\"works and how to use it, is it essential to know what were the goals of \"\n\"it’s development and why it works that way. Let’s start with the API as \"\n\"this gives you the best introduction.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:13\nmsgid \"Simplified access can be made through methods:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:20\nmsgid \"And more a complex version:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:27\nmsgid \"\"\n\"Even though the API contains more methods, the rest is more or less a \"\n\"variation of presented above. A complete API description for all access \"\n\"methods is available in JavaDoc documentation in the `UserRepository \"\n\"<https://github.com/tigase/tigase-\"\n\"server/tree/master/src/main/java/tigase/db/UserRepository.java>`__ \"\n\"interface. So we are not going into too much detail here except for the \"\n\"main idea.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:29\nmsgid \"\"\n\"Tigase operates on <*key*, **value**> pairs for the individual user data.\"\n\" The idea behind this was to make the API very simple and also at the \"\n\"same time very flexible, so adding a new plugin or component would not \"\n\"require a database schema change, adding new tables, or conversion of the\"\n\" DB schema to a new version.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:31\nmsgid \"\"\n\"As a result the **UserRepository** interface is exposed to all of \"\n\"Tigase’s code, mainly the components and plugins (let’s call all of them \"\n\"modules). These modules simply call set/get methods to store or access \"\n\"module specific data.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:33\nmsgid \"\"\n\"As plugins or components are developed independently it may easily happen\"\n\" that developer choses the same key name to store some information. To \"\n\"avoid key name conflicts in the database a 'node' concept has been \"\n\"introduced. Therefore, most modules when set/get key value they also \"\n\"provide a subnode part, which in most cases is just XMLNS or some other \"\n\"unique string.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:35\nmsgid \"\"\n\"The 'node' thing is a little bit like directory in a file system, it may \"\n\"contain subnodes which makes the Tigase database behave like a \"\n\"hierarchical structure. And the notation is also similar to file systems,\"\n\" you use just **/** to separate node levels. In practice you can have the\"\n\" database organized like this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:47\nmsgid \"\"\n\"So to access item’s 1 data from the roster you could call method like \"\n\"this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:53\nmsgid \"\"\n\"This is huge convenience for the developer, as he can focus on the module\"\n\" logic instead of worrying about data storage implementation and \"\n\"organization. Especially at the prototype phase it speeds development up \"\n\"and allows for a quick experiments with different solutions. In practice,\"\n\" accessing user’s roster in such a way would be highly inefficient so the\"\n\" roster is stored a bit differently but you get the idea. Also there is a\"\n\" more complex API used in some places allowing for more direct access to \"\n\"the database and store data in any format optimized for the scenario.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:55\nmsgid \"\"\n\"Right now such a hierarchical structure is implemented on top of SQL \"\n\"databases but initially Tigase’s database was implemented as an XML \"\n\"structure, so it was natural and simple.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:57\nmsgid \"In the SQL database we simulate hierarchical structure with three tables:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:59\nmsgid \"\"\n\"**tig_users** - with main users data, user id (JID), optional password, \"\n\"active flag, creation time and some other basic properties of the \"\n\"account. All of them could be actually stored in tig_pairs but for \"\n\"performance reasons they are in one place to quickly access them with a \"\n\"single, simple query.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:61\nmsgid \"\"\n\"**tig_nodes** - is a table where the hierarchy is implemented. When \"\n\"Tigase was storing data in XML database the hierarchy was quite complex. \"\n\"However, in a SQL database it resulted in a very slow access to the data \"\n\"and a now more flat structure is used by most components. Please note, \"\n\"every user’s entry has something called root node, which is represented \"\n\"by 'root' string;\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:63\nmsgid \"\"\n\"**tig_pairs** - this is the table where all the user’s information is \"\n\"stored in form of the <key, value> pairs.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:65\nmsgid \"\"\n\"So we now know how the data is organized. Now we are going to learn how \"\n\"to access the data directly in the database using SQL queries.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:67\nmsgid \"\"\n\"Let’s assume we have a user 'admin@test-d' for whom we want to retrieve \"\n\"the roster. We could simply execute query:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:77\nmsgid \"\"\n\"However, if multiple modules store data under the key 'roster' for a \"\n\"single user, we would receive multiple results. To access the correct \"\n\"'roster' we also have to know the node hierarchy for this particular key.\"\n\" The main users roster is stored under the 'root' node, so the query \"\n\"would look like:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:89\nmsgid \"\"\n\"How exactly the information is stored in the **tig_pairs** table depends \"\n\"on the particular module. For the roster it looks a bit like XML content:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:96\nmsgid \"Why the most recent JDK?\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:98\nmsgid \"\"\n\"There are many reasons but the main is that we are a small team working \"\n\"on source code. So the whole approach is to make life easier for us, make\"\n\" the project easier to maintain, and development more efficient.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:100\nmsgid \"Here is the list:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:102\nmsgid \"\"\n\"**Easy to maintain** - No third-party libraries are used for the project \"\n\"which makes this project much easier to maintain. This simplifies issues \"\n\"of compatibility between particular versions of libraries. This also \"\n\"unifies coding with a single library package without having to rely on \"\n\"specific versions that may not be supported.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:104\nmsgid \"\"\n\"**Easy to deploy** - Another reason to not use third-party tools is to \"\n\"make it easier for end-users to install and use the server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:106\nmsgid \"\"\n\"**Efficient development** - As no third-party libraries are used, Tigase \"\n\"needs either to implement many things on its own or use as much as \"\n\"possible of JDK functionality. We try to use as much as possible of \"\n\"existing library provided with JDK and the rest is custom coded.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:108\nmsgid \"\"\n\"What features of JDKv5 are critical for Tigase development? Why I can’t \"\n\"simply re-implement some code to make it compatible with earlier JDK \"\n\"versions?\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:110\nmsgid \"\"\n\"**Non-blocking I/O for SSL/TLS** - This is functionality which can’t be \"\n\"simply re-implemented in JDK-1.4. As the whole server uses NIO it doesn’t\"\n\" make sense to use blocking I/O for SSL and TLS.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:112\nmsgid \"**SASL** - This could be re-implemented for JDK-1.4 without much effort.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:114\nmsgid \"\"\n\"**Concurrent package** - This could be re-implemented for JDK-1.4 but \"\n\"takes a lot of work. This is a critical part of the server as it uses \"\n\"multi-threading and concurrent processing.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:116\nmsgid \"\"\n\"**Security package** - There number of extensions to the security package\"\n\" which otherwise would not work as easily with earlier versions of JDK.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:118\nmsgid \"\"\n\"**LinkedHashMap** - in JDKv6 is a basement for the Tigase cache \"\n\"implementation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:120\nmsgid \"\"\n\"**Light HTTP server** - JDKv6 offers built-in light HTTP server which is \"\n\"needed to implement HTTP binding (JEP-0124) and HTTP user interface to \"\n\"monitor server activity and work with the server configuration.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:122\nmsgid \"\"\n\"As the JDK improves, so does our programming as we gain the ability to \"\n\"use new methods, efficiencies, and sometimes shortcuts.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:124\nmsgid \"\"\n\"Currently Tigase requires **JDKv8** and we recommend updating it as often\"\n\" as needed!\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:127\nmsgid \"API Description for Virtual Domains Management in the Tigase Server\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:129\nmsgid \"\"\n\"The purpose of this guide is to introduce vhost management in Tigase \"\n\"server. Please refer to the JavaDoc documentation for all specific \"\n\"details not covered in this guide. All interfaces are well documented and\"\n\" you can use existing implementation as an example code base and \"\n\"reference point. The VHost management files are located in the repository\"\n\" and you can browse them using the `source viewer \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:131\nmsgid \"\"\n\"Virtual hosts management in Tigase can be adjusted in many ways through \"\n\"the flexible API. The core elements of the virtual domains management is \"\n\"interface `VHostManager <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostManager.java>`__ \"\n\"class. They are responsible for providing the virtual hosts information \"\n\"to the rest of the Tigase server components. In particular to the \"\n\"`MessageRouter <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/server/MessageRouter.java>`__ \"\n\"class which controls how XMPP packets flow inside the server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:133\nmsgid \"\"\n\"The class you most likely want to re-implement is `VHostJDBCRepository \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostJDBCRepository.java>`__\"\n\" used as a default virtual hosts storage and implementing the \"\n\"`VHostRepository <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostRepository.java>`__ \"\n\"interface. You might need to have your own implementation in order to \"\n\"store and access virtual hosts in other than Tigase’s own data storage. \"\n\"This is especially important if you are going to modify the virtual \"\n\"domains list through systems other than Tigase.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:135\nmsgid \"\"\n\"The very basic virtual hosts storage is provided by `VHostItem \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostItem.java>`__ class. \"\n\"This is read only storage and provides the server a bootstrap vhosts data\"\n\" at the first startup time when the database with virtual hosts is empty \"\n\"or is not accessible. Therefore it is advised that all `VHostItem \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostItem.java>`__ \"\n\"implementations extend this class. The example code is provided in the \"\n\"`VHostJDBCRepository <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostJDBCRepository.java>`__\"\n\" file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:137\nmsgid \"\"\n\"All components which may need virtual hosts information or want to \"\n\"interact with virtual hosts management subsystem should implement the \"\n\"`VHostListener <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostListener.java>`__ \"\n\"interface. In some cases implementing this interface is necessary to \"\n\"receive packets for processing.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:139\nmsgid \"\"\n\"Virtual host information is carried out in 2 forms inside the Tigase \"\n\"server:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:141\nmsgid \"As a **String** value with the domain name\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:143\nmsgid \"\"\n\"As a `VHostItem <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostItem.java>`__ which \"\n\"contains all the domain information including the domain name, maximum \"\n\"number of users for this domain, whether the domain is enabled or \"\n\"disabled and so on. The JavaDoc documentation contains all the details \"\n\"about all available fields and usage.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:145\nmsgid \"\"\n\"Here is a complete list of all interfaces and classes with a brief \"\n\"description for each of them:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:147\nmsgid \"\"\n\"`VHostManagerIfc <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostManagerIfc.java>`__ -\"\n\" is an interface used to access virtual hosts information in all other \"\n\"server components. There is one default implementation of the interface: \"\n\"`VHostManager <#vhostMgr>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:149\nmsgid \"\"\n\"`VHostListener <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostListener.java>`__ - \"\n\"is an interface which allows components to interact with the \"\n\"`VHostManager <#vhostMgr>`__. The interaction is in both ways. The \"\n\"VHostManager provides virtual hosts information to components and \"\n\"components provide some control data required to correctly route packets \"\n\"to components.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:151\nmsgid \"\"\n\"`VHostRepository <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostRepository.java>`__ -\"\n\" is an interface used to store and load virtual domains list from the \"\n\"database or any other storage media. There are 2 implementations for this\"\n\" interface: `VHostConfigRepository <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VhostConfigRepository.java>`__\"\n\" which loads vhosts information for the configuration file and provides \"\n\"read-only storage and - `VHostJDBCRepository <https://github.com/tigase\"\n\"/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostJDBCRepository.java>`__\"\n\" class which extends `VHostConfigRepository <https://github.com/tigase\"\n\"/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VhostConfigRepository.java>`__\"\n\" and allows for both - reading and saving virtual domains list. \"\n\"VHostJDBCRepository is loaded as a default repository by Tigase server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:153\nmsgid \"\"\n\"`VHostItem <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostItem.java>`__ - is an\"\n\" interface which allows for accessing all the virtual domain properties. \"\n\"Sometimes the domain name is not sufficient for data processing. The \"\n\"domain may be temporarily disabled, may have a limited number of users \"\n\"and so on. Instances of this class keep all the information about the \"\n\"domain which might be needed by the server components.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:155\nmsgid \"\"\n\"`VHostManager <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostManager.java>`__ - \"\n\"the default implementation of the VHostManagerIfc interface. It provides \"\n\"components with the virtual hosts information and manages the virtual \"\n\"hosts list. Processes ad-hoc commands for reloading, updating and \"\n\"removing domains.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:157\nmsgid \"\"\n\"`VHostConfirRepository <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VhostConfigRepository.java>`__\"\n\" - a very basic implementation of the `VHostRepository \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostRepository.java>`__ \"\n\"for loading domains list from the configuration file.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:159\nmsgid \"\"\n\"`VHostJDBCRepository <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostJDBCRepository.java>`__\"\n\" - the default implementation of the `VHostRepository \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostRepository.java>`__ \"\n\"loaded by Tigase server. It allows to read and store virtual domains list\"\n\" in the database accessible through UserRepository.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:162\nmsgid \"Extending Virtual Domain settings\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:164\nmsgid \"\"\n\"In some cases it is desired to extend Virtual Domain to add some \"\n\"additional settings. Since version 8.1.0 it is possible with use of \"\n\"``VHostItemExtension`` and VHostItemExtensionProvider`.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:166\nmsgid \"\"\n\"To do so, you need to create a class implementing ``VHostItemExtension``.\"\n\" This class will hold values of settings for each virtual host. It is \"\n\"required to make it serializable to ``Element`` and deserializable from \"\n\"``Element``. Moreover, it is required to make values of this class \"\n\"modifiable by ad-hoc commands.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:168\nmsgid \"\"\n\"It is recommended to provide additional methods allowing you to access \"\n\"values of this class.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:170\nmsgid \"\"\n\"Additionally, you need to implement ``VHostItemExtensionProvider`` \"\n\"interface as a bean and return a class of your implementation of \"\n\"``VHostItemExtension``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:172\nmsgid \"\"\n\"*Example VHostItemExtensionProvider implementation for* \"\n\"``SeeOtherHostVHostItemExtension``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:191\nmsgid \"Stanza Limitations\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:193\nmsgid \"\"\n\"Although XMPP is robust and can process stanzas of any size in bytes, \"\n\"there are some limitations to keep in mind for Tigase server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:195\nmsgid \"\"\n\"Please keep these in mind when using default Tigase settings and creating\"\n\" custom stanzas.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:197\nmsgid \"Limit to number of attributes of single element = 50 attributes\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:199\nmsgid \"Limit to number of elements = 1024 elements\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:201\nmsgid \"Limit to length of element name = 1024 characters\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:203\nmsgid \"Limit to length of attribute name = 1024 characters\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:205\nmsgid \"Limit to length of attribute value = 10240 characters\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:207\nmsgid \"Limit to length of content of single element CDATA = 1048576b or 1Mb\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:209\nmsgid \"These values may be changed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:211\nmsgid \"\"\n\"**Note that these limitations are to elements and attributes that may be \"\n\"within a stanza, but do not limit the overall stanza length.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:214\nmsgid \"Escape Characters\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:216\nmsgid \"\"\n\"There are special characters that need to be escaped if they are included\"\n\" in the stanza to avoid conflicts. The rules are similar to normal XML \"\n\"escaping. The following is a list of characters that need to be escaped \"\n\"and what to use to escape them:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:227\nmsgid \"API changes in the Tigase Server 5.x\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:229\nmsgid \"**THIS INFORMATION IS FOR OLDER VERSIONS OF TIGASE**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:231\nmsgid \"\"\n\"The API changes can effect you only if you develop own code to run inside\"\n\" Tigase server. The changes are not extensive but in some circumstances \"\n\"may require many simple changes in a few files.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:233\nmsgid \"\"\n\"All the changes are related to introducing tigase.xmpp.JID and \"\n\"tigase.xmpp.BareJID classes. It is recommended to use them for all \"\n\"operations performed on the user JID instead of the String class which \"\n\"was used before changes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:235\nmsgid \"\"\n\"There are a few advantages to using the new classes. First of all they do\"\n\" all the user JID checking and parsing, they also perform stringprep \"\n\"processing. Therefore if you use data kept by instance of the JID or \"\n\"BareJID you can be sure they are valid and correct.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:237\nmsgid \"\"\n\"These are not all advantages however. JID parsing code appears to use a \"\n\"lot of CPU power to conduct it’s operations. JIDs and parts of the JIDs \"\n\"are used in many places of the stanza processing and the parsing is \"\n\"performed over and over again in all these places, wasting CPU cycles, \"\n\"memory and time. Therefore, great performance benefits can be gained from\"\n\" these new class are in if, once parsed, JIDs are reused in all further \"\n\"stanza processing.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:239\nmsgid \"\"\n\"This is where the tigase.server.Packet class comes in handy. Instances of\"\n\" the Packet class encloses XML stanza and pre-parses some, the most \"\n\"commonly used elements of the stanza, stanza source and destination \"\n\"addresses among them. As an effect there are all new methods available in\"\n\" the class:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:250\nmsgid \"Whereas following methods are still available but have been deprecated:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:257\nmsgid \"\"\n\"Please refer to the JavaDoc documentation for the `Packet \"\n\"<http://docs.tigase.org/tigase-\"\n\"server/snapshot/javadoc/tigase/server/Packet.html>`__ class and methods \"\n\"to learn all the details of these methods and difference between them.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:259\nmsgid \"\"\n\"Another difference is that you can no longer create the ``Packet`` \"\n\"instance using a constructor. Instead there are a few factory methods \"\n\"available:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:267\nmsgid \"\"\n\"Again, please refer to the JavaDoc documentation for all the details. The\"\n\" main point of using these methods is that they actually return an \"\n\"instance of one of the following classes instead of the ``Packet`` class:\"\n\" ``Iq``, ``Presence`` or ``Message``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:269\nmsgid \"\"\n\"There is also a number of utility methods helping with creating a copy of\"\n\" the Packet instance preserving as much pre-parsed data as possible:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:279\nmsgid \"\"\n\"We try to keep the `JavaDoc <http://docs.tigase.org/tigase-\"\n\"server/snapshot/javadoc/>`__ documentation as complete as possible. \"\n\"Please contact us if you find missing or incorrect information.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:281\nmsgid \"\"\n\"The main point is to reuse ``JID`` or ``BareJID`` instances in your code \"\n\"as much as possible. You never know, your code may run in highly loaded \"\n\"systems with throughput of 100k XMPP packets per second.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:283\nmsgid \"\"\n\"Another change. This one a bit risky as it is very difficult to find all \"\n\"places where this could be used. There are several utility classes and \"\n\"methods which accept source and destination address of a stanza and \"\n\"produce something. There was a great confusion with them, as in some of \"\n\"them the first was the source address and in others the destination \"\n\"address. All the code has been re-factored to keep the parameter order \"\n\"the same in all places. Right now the policy is: **source address \"\n\"first**. Therefore in all places where there was a method:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:289\nmsgid \"it has been changed to:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:295\nmsgid \"\"\n\"As far as I know most of these method were used only by myself so I do \"\n\"not expect much trouble for other developers.\"\nmsgstr \"\"\n\n#~ msgid \"\"\n#~ \"**Example VHostItemExtensionProvider\\\\` implementation \"\n#~ \"for ``SeeOtherHostVHostItemExtension``..**\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"Please refer to the JavaDoc \"\n#~ \"documentation for the ```Packet`` \"\n#~ \"<http://docs.tigase.org/tigase-\"\n#~ \"server/snapshot/javadoc/tigase/server/Packet.html>`__ class \"\n#~ \"and methods to learn all the \"\n#~ \"details of these methods and difference\"\n#~ \" between them.\"\n#~ msgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Development/Packet_Filtering_in_Component.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-05-27 12:30-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:4\nmsgid \"Packet Filtering in Components\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:7\nmsgid \"The Packet Filter API\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:9\nmsgid \"\"\n\"Tigase server offers an API to filter packet traffic inside every \"\n\"component. You can separately filter incoming and outgoing packets.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:11\nmsgid \"\"\n\"By filtering we mean intercepting a packet and possibly making some \"\n\"changes to the packet or just blocking the packet completely. By blocking\"\n\" we mean stopping from any further processing and just dropping the \"\n\"packet.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:13\nmsgid \"\"\n\"The packet filtering is based on the `PacketFilterIfc \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/server/PacketFilterIfc.java>`__ \"\n\"interface. Please have a look in the JavaDoc documentation to this \"\n\"interface for all the details. The main filtering method is ``Packet \"\n\"filter(Packet packet);`` which takes packets as an input, processes it, \"\n\"possibly alerting the packet content (may add or remove some payloads) \"\n\"and returns a **Packet** for further processing. If it returns **null** \"\n\"it means the packet is blocked and no further processing is permitted \"\n\"otherwise it returns a **Packet** object which is either the same object \"\n\"it received as a parameter or a modified copy of the original object.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:15\nmsgid \"\"\n\"Please note, although **Packet** object is not an unmodifiable instance, \"\n\"it is recommended that changes to the existing object are not made. The \"\n\"same **Packet** might be processed at the same time by other components \"\n\"or threads, therefore modification of the **Packet** may lead to \"\n\"unpredictable results.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:17\nmsgid \"\"\n\"Please refer to an example code in `PacketCounter \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/server/filters/PacketCounter.java>`__\"\n\" which is a very simple filter counting different types of packets. This \"\n\"filter is by default loaded to all components which might be very helpful\"\n\" for assessing traffic shapes on newly deployed installation. You can get\"\n\" counters for all types of packets, where they are generated, where they \"\n\"flow, what component they put the most load on.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:19\nmsgid \"\"\n\"This is because packet filter can also generate and present its own \"\n\"statistics which are accessible via normal statistics monitoring \"\n\"mechanisms. To take advantage of the statistics functionality, the packet\"\n\" filter has to implement the ``void getStatistics(StatisticsList list);``\"\n\" method. Normally, the method is empty. However, you can generate and add\"\n\" statistics from the filter to the list. Please refer to `PacketCounter \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/server/filters/PacketCounter.java>`__\"\n\" for an example implementation code.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:22\nmsgid \"Configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:24\nmsgid \"\"\n\"Packet filters are configurable, that is a packet filters instances can \"\n\"be configured in Tigase server’s configuration for each component \"\n\"separately and for each traffic direction. This gives you a great \"\n\"flexibility and control over the data flow inside the Tigase server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:26\nmsgid \"\"\n\"You can for example, load specific packet filters to all connections \"\n\"managers to block specific traffic or specific packet source from sending\"\n\" messages to users on your server. You could also reduce the server \"\n\"overall load by removing certain payload from all packets. The \"\n\"possibilities are endless.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:28\nmsgid \"\"\n\"The default configuration is generated in such a way that each component \"\n\"loads a single packet filter - ``PacketCounter`` for each traffic \"\n\"direction:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:83\nmsgid \"\"\n\"Now, let’s say you have a packet filter implemented in class: \"\n\"**com.company.SpamBlocker**. You want to disable PacketCounter on most of\"\n\" the components leaving it only in the message router component and you \"\n\"want to install SpamBlocker in all connection managers.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:85\nmsgid \"\"\n\"*Please note, in case of the connection managers 'incoming' and \"\n\"'outgoing' traffic is probably somehow opposite from what you would \"\n\"normally expect.*\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:87\nmsgid \"\"\n\"**incoming** is traffic which is submitted to a component by message \"\n\"router and has to be further processed. For connection managers this \"\n\"further processing means sending it out to the network.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:89\nmsgid \"\"\n\"**outgoing** is traffic which is 'generated' by the component and goes \"\n\"out of the component. Such a packet is submitted to message router which \"\n\"then decides where to send it for further processing. For connection \"\n\"managers **outgoing** traffic is all the packets just received from the \"\n\"network.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:91\nmsgid \"\"\n\"According to that we have to apply the SpamBlocker filter to all \"\n\"'outgoing' traffic in all connection managers. You may also decide that \"\n\"it might be actually useful to compare traffic shape between Bosh \"\n\"connections and standard XMPP c2s connections. So let’s leave packet \"\n\"counters for this components too.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:93\nmsgid \"\"\n\"Here is our new configuration applying SpamBlocker to connection managers\"\n\" and PacketCounter to a few other components:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:151\nmsgid \"\"\n\"In case of ``incomingFilters`` ``outgoingFilters`` and ``packetCounter`` \"\n\"we were able to skip providing ``class`` parameter as those classes are \"\n\"properly annotated with ``@Bean`` annotation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:153\nmsgid \"\"\n\"The simplest way to apply the new configuration is via the \"\n\"``config.tdsl`` file which is in details described in the *Admin Guide*.\"\nmsgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Development/Plugin_Development.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:2\nmsgid \"Plugin Development\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:4\nmsgid \"\"\n\"This is a set of documents explaining details what is a plugin, how they \"\n\"are designed and how they work inside the Tigase server. The last part of\"\n\" the documentation explains step by step creating the code for a new \"\n\"plugin.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:6\nmsgid \":ref:`Writing Plugin Code<writePluginCode>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:8\nmsgid \":ref:`Plugin Configuration<pluginConf>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:10\nmsgid \":ref:`How Packets are Processed by the SM and Plugins<packetprocess>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:12\nmsgid \":ref:`SASL Custom Mechanisms and Configuration<saslcmac>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:17\nmsgid \"Writing Plugin Code\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:19\nmsgid \"\"\n\"Stanza processing takes place in 4 steps. A different kind of plugin is \"\n\"responsible for each step of processing:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:21\nmsgid \"\"\n\"`XMPPPreprocessorIfc <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/xmpp/XMPPPreprocessorIfc.java>`__\"\n\" - is the interface for packets pre-processing plugins.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:23\nmsgid \"\"\n\"`XMPPProcessorIfc <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/xmpp/XMPPProcessor.java>`__ - is \"\n\"the interface for packets processing plugins.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:25\nmsgid \"\"\n\"`XMPPPostprocessorIfc <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/xmpp/XMPPPostprocessorIfc.java>`__\"\n\" - is the interface for packets post-processing plugins.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:27\nmsgid \"\"\n\"`XMPPPacketFilterIfc <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/xmpp/XMPPPacketFilterIfc.java>`__\"\n\" - is the interface for processing results filtering.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:29\nmsgid \"\"\n\"If you look inside any of these interfaces you will only find a single \"\n\"method. This is where all the packet processing takes place. All of them \"\n\"take a similar set of parameters and below is a description for all of \"\n\"them:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:31\nmsgid \"\"\n\"**Packet packet** - packet is which being processed. This parameter may \"\n\"never be null. Even though this is not an immutable object it mustn’t be \"\n\"altered. None of it’s fields or attributes can be changed during \"\n\"processing.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:33\nmsgid \"\"\n\"**XMPPResourceConnection session** - user session which keeps all the \"\n\"user session data and also gives access to the user’s data repository. It\"\n\" allows for the storing of information in permanent storage or in memory \"\n\"only during the life of the session. This parameter can be null if there \"\n\"is no online user session at the time of the packet processing.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:35\nmsgid \"\"\n\"**NonAuthUserRepository repo** - this is a user data storage which is \"\n\"normally used when the user session (parameter above) is null. This \"\n\"repository allows for a very restricted access only. It allows for \"\n\"storing some user private data (but doesn’t allow overwriting existing \"\n\"data) like messages for offline users and it also allows for reading user\"\n\" public data like VCards.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:37\nmsgid \"\"\n\"**Queue<Packet> results** - this a collection with packets which have \"\n\"been generated as input packet processing results. Regardless a response \"\n\"to a user request is sent or the packet is forwarded to it’s destination \"\n\"it is always required that a copy of the input packet is created and \"\n\"stored in the **results** queue.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:39\nmsgid \"\"\n\"**Map<String, Object> settings** - this map keeps plugin specific \"\n\"settings loaded from the Tigase server configuration. In most cases it is\"\n\" unused, however if the plugin needs to access an external database that \"\n\"this is a way to pass the database connection string to the plugin.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:41\nmsgid \"\"\n\"After a closer look in some of the interfaces you can see that they \"\n\"extend another interface: `XMPPImplIfc <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/xmpp/XMPPImplIfc.java>`__ which \"\n\"provides a basic meta information about the plugin implementation. Please\"\n\" refer to `JavaDoc <http://docs.tigase.org/tigase-\"\n\"server/snapshot/javadoc/tigase/xmpp/impl/package-summary.html>`__ \"\n\"documentation for all details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:43\nmsgid \"\"\n\"For purpose of this guide we are implementing a simple plugin for \"\n\"handling all **<message/>** packets that is forwarding packets to the \"\n\"destination address. Incoming packets are forwarded to the user \"\n\"connection and outgoing packets are forwarded to the external destination\"\n\" address. This `message plugin <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/xmpp/impl/Message.java>`__ is \"\n\"actually implemented already and it is available in our Git repository. \"\n\"The code has some comments inside already but this guide goes deeper into\"\n\" the implementation details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:45\nmsgid \"\"\n\"First of all you have to choose what kind of plugin you want to \"\n\"implement. If this is going to be a packet processor you have to \"\n\"implement the **XMPPProcessorIfc** interface, if this is going to be a \"\n\"pre-processor then you have to implement the **XMPPPreprocessorIfc** \"\n\"interface. Of course your implementation can implement more than one \"\n\"interface, even all of them. There are also two abstract helper classes, \"\n\"one of which you should use as a base for all you plugins \"\n\"**XMPPProcessor** or use **AnnotatedXMPPProcessor** for annotation \"\n\"support.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:48\nmsgid \"Using annotation support\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:50\n#: ../../Tigase_Development/Plugin_Development.rst:85\nmsgid \"\"\n\"The class declaration should look like this (assuming you are \"\n\"implementing just the packet processor):\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:57\nmsgid \"\"\n\"The first thing to create is the plugin **ID**. This is a unique string \"\n\"which you put in the configuration file to tell the server to load and \"\n\"use the plugin. In most cases you can use XMLNS if the plugin wants \"\n\"packets with elements with a very specific name space. Of course there is\"\n\" no guarantee there is no other packet for this specific XML element too.\"\n\" As we want to process all messages and don’t want to spend whole day on \"\n\"thinking about a cool ID, let’s say our ID is: *message*.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:59\nmsgid \"\"\n\"A plugin informs about it’s presence using a static **ID** field and \"\n\"**@Id** annotation placed on class:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:69\nmsgid \"\"\n\"As mentioned before, this plugin receives only this kind of packets for \"\n\"processing which it is interested in. In this example, the plugin is \"\n\"interested only in packets with **<message/>** elements and only if they \"\n\"are in the \\\"**jabber:client**\\\" namespace. To indicate all supported \"\n\"elements and namespaces we have to add 2 more annotations:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:83\nmsgid \"Using older non-annotation based implementation\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:92\nmsgid \"The first thing to create is the plugin **ID** like above.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:94\nmsgid \"A plugin informs about it’s ID using following code:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:101\nmsgid \"\"\n\"As mentioned before this plugin receives only this kind of packets for \"\n\"processing which it is interested in. In this example, the plugin is \"\n\"interested only in packets with **<message/>** elements and only if they \"\n\"are in \\\"**jabber:client**\\\" namespace. To indicate all supported \"\n\"elements and namespaces we have to add 2 more methods:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:114\nmsgid \"Implementation of processing method\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:116\nmsgid \"\"\n\"Now we have our plugin prepared for loading in Tigase. The next step is \"\n\"the actual packet processing method. For the complete code, please refer \"\n\"to the plugin in the Git. I will only comment here on elements which \"\n\"might be confusing or add a few more lines of code which might be helpful\"\n\" in your case.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:215\nmsgid \"Plugin Configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:217\nmsgid \"Plugin configuration is straightforward.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:219\nmsgid \"\"\n\"Tell the Tigase server to load or not to load the plugins via the \"\n\"``config.tdsl`` file. Plugins fall within the ``'sess-man'`` container. \"\n\"To activate a plugin, simply list it among the sess-man plugins.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:221\nmsgid \"\"\n\"If you do not wish to use this method to find out what plugins are \"\n\"running, there are two ways you can identify if a plugin is running. One \"\n\"is the log file: logs/tigase-console.log. If you look inside you can find\"\n\" following output:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:240\nmsgid \"and this is a list of plugins which are loaded in your installation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:242\nmsgid \"\"\n\"Another way is to look inside the session manager source code which has \"\n\"the default list hardcoded:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:254\nmsgid \"\"\n\"In you wish to load a plugin outside these defaults, you have to edit the\"\n\" list and add your plugin IDs as a value to the plugin list under 'sess-\"\n\"man'. Let’s say our plugin ID is **message** as in our all examples:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:264\nmsgid \"\"\n\"Assuming your plugin class is in the classpath it will be loaded and used\"\n\" at the runtime. You may specify class by adding ``class: \"\n\"class.implementing.plugin`` within the parenthesis of the plugin.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:268\nmsgid \"\"\n\"If your plugin name has any special characters (-,:\\\\|/.) it needs to be \"\n\"encapsulated in single quotation marks.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:270\nmsgid \"\"\n\"There is another part of the plugin configuration though. If you looked \"\n\"at the :ref:`Writing Plugin Code <writePluginCode>` guide you can \"\n\"remember the **Map settings** processing parameter. This is a map of \"\n\"properties you can set in the configuration file and these setting will \"\n\"be passed to the plugin at the processing time.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:273\nmsgid \"\"\n\"Again **config.tdsl** is the place to put the stuff. These kind of \"\n\"properties start under your **plugin ID** and each key and value will be \"\n\"a child underneath:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:287\nmsgid \"\"\n\"From v8.0.0 you will no longer be able to specify one value for multiple \"\n\"keys, you must set each one individually.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:289\nmsgid \"Last but not least - in case you have **omitted plugin ID**:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:297\nmsgid \"\"\n\"then the configured key-value pair will be a global/common plugin setting\"\n\" available to all loaded plugins.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:302\nmsgid \"How Packets are Processed by the SM and Plugins\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:304\nmsgid \"\"\n\"For Tigase server plugin development it is important to understand how it\"\n\" all works. There are different kind of plugins responsible for \"\n\"processing packets at different stages of the data flow. Please read the \"\n\"introduction below before proceeding to the actual coding part.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:307\nmsgid \"Introduction\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:309\nmsgid \"\"\n\"In Tigase server **plugins** are pieces of code responsible for \"\n\"processing particular XMPP stanzas. A separate plugin might be \"\n\"responsible for processing messages, a different one for processing \"\n\"presences, a separate plugins responsible for iq roster, and a different \"\n\"one for iq version and so on.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:311\nmsgid \"\"\n\"A plugin provides information about what exact XML element(s) name(s) \"\n\"with xmlns it is interested in. So you can create a plugin which is \"\n\"interested in all packets containing caps child.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:313\nmsgid \"\"\n\"There might be no plugin for a particular stanza element, in this case \"\n\"the default action is used which is simple forwarding stanza to a \"\n\"destination address. There might be also more than one plugin for a \"\n\"specific XML element and then they all process the same stanza \"\n\"simultaneously in separate threads so there is no guarantee on the order \"\n\"in which the stanza is processed by a different plugins.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:315\nmsgid \"\"\n\"Each stanza goes through the Session Manager component which processes \"\n\"packets in a few steps. Have a look at the picture below:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:317\nmsgid \"|Consumer|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:357\nmsgid \"Consumer\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:319\nmsgid \"\"\n\"The picture shows that each stanza is processed by the session manager in\"\n\" 4 steps:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:321\nmsgid \"\"\n\"Pre-processing - All loaded pre-processors receive the packet for \"\n\"processing. They work within session manager thread and they have no \"\n\"internal queue for processing. As they work within Session Manager thread\"\n\" it is important that they limit processing time to absolute minimum as \"\n\"they may affect the Session Manager performance. The intention for the \"\n\"pre-processors is to use them for packet blocking. If the pre-processing \"\n\"result is 'true' then the packet is blocked and no further processing is \"\n\"performed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:323\nmsgid \"\"\n\"Processing - This is the next step the packet gets through if it wasn’t \"\n\"blocked by any of the pre-processors. It gets inserted to all processors \"\n\"queues with requested interest in this particular XML element. Each \"\n\"processor works in a separate thread and has own internal fixed size \"\n\"processing queue.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:325\nmsgid \"\"\n\"Post-processing - If there is no processor for the stanza then the packet\"\n\" goes through all post-processors. The last post-processor that is built \"\n\"into session manager post-processor tries to apply a default action to a \"\n\"packet which hasn’t been processed in step 2. Normally the default action\"\n\" is just forwarding the packet to a destination. Most commonly it is \"\n\"applied to <message/> packets.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:327\nmsgid \"\"\n\"Finally, if any of above 3 steps produced output/result packets all of \"\n\"them go through all filters which may or may not block them.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:329\nmsgid \"\"\n\"An important thing to note is that we have two kinds or two places where \"\n\"packets may be blocked or filtered out. One place is before packet is \"\n\"processed by the plugin and another place is after processing where \"\n\"filtering is applied to all results generated by the processor plugins.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:331\nmsgid \"\"\n\"It is also important to note that session manager and processor plugins \"\n\"act as packet consumers. The packet is taken for processing and once \"\n\"processing is finished the packet is destroyed. Therefore to forward a \"\n\"packet to a destination one of the processor must create a copy of the \"\n\"packet, set all properties and attributes and return it as a processing \"\n\"result. Of course processor can generate any number of packets as a \"\n\"result. Result packets can be generated in any of above 4 steps of the \"\n\"processing. Have a look at the picture below:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:333\nmsgid \"|User Send to Comp|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:358\nmsgid \"User Send to Comp\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:335\nmsgid \"\"\n\"If the packet P1 is sent from outside of the server, for example to a \"\n\"user on another server or to some component (MUC, PubSub, transport), \"\n\"then one of the processor must create a copy (P2) of the packet and set \"\n\"all attributes and destination addresses correctly. Packet P1 has been \"\n\"consumed by the session manager during processing and a new packet has \"\n\"been generated by one of the plugins.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:337\nmsgid \"The same of course happens on the way back from the component to the user:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:339\nmsgid \"|Comp Sends to User|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:359\nmsgid \"Comp Sends to User\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:341\nmsgid \"\"\n\"The packet from the component is processed and one of the plugins must \"\n\"generate a copy of the packet to deliver it to the user. Of course packet\"\n\" forwarding is a default action which is applied when there is no plugin \"\n\"for the particular packet.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:343\nmsgid \"\"\n\"It is implemented this way because the input packet P1 can be processed \"\n\"by many plugins at the same time therefore the packet should be in fact \"\n\"immutable and must not change once it got to the session manager for \"\n\"processing.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:345\nmsgid \"\"\n\"The most obvious processing work flow is when a user sends request to the\"\n\" server and expects a response from the server:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:347\nmsgid \"|User Request Response|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:360\nmsgid \"User Request Response\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:349\nmsgid \"\"\n\"This design has one surprising consequence though. If you look at the \"\n\"picture below showing communication between 2 users you can see that the \"\n\"packet is copied twice before it is delivered to a final destination:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:351\nmsgid \"|User Sends to User|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:361\nmsgid \"User Sends to User\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:353\nmsgid \"\"\n\"The packet has to be processed twice by the session manager. The first \"\n\"time it is processed on behalf of the User A as an outgoing packet and \"\n\"the second time it is processed on behalf of the User B as an incoming \"\n\"packet.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:355\nmsgid \"\"\n\"This is to make sure the User A has permission to send a packet out and \"\n\"all processing is applied to the packet and also to make sure that User B\"\n\" has permission to receive the packet and all processing is applied. If, \"\n\"for example, the User B is offline there is an offline message processor \"\n\"which should send the packet to a database instead of User B.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:366\nmsgid \"SASL Custom Mechanisms and Configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:368\nmsgid \"\"\n\"**This API is available from Tigase XMPP Server version 5.2.0 or later on\"\n\" our current master branch.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:370\nmsgid \"\"\n\"**In version 8.0.0 there was a major change to the API and configuration \"\n\"of custom SASL mechanisms.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:372\nmsgid \"\"\n\"*Note that API is under active development. This description may be \"\n\"updated at any time.*\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:375\nmsgid \"Basic SASL Configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:377\nmsgid \"\"\n\"SASL implementation in Tigase XMPP Server is compatible with Java API, \"\n\"the same exact interfaces are used.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:379\nmsgid \"The SASL implementation consists of following parts:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:381\nmsgid \"mechanism\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:383\nmsgid \"CallbackHandler\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:386\nmsgid \"Mechanisms Configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:388\nmsgid \"\"\n\"To add a new mechanism, a new factory for the mechanism has to be \"\n\"implemented and registered.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:390\nmsgid \"\"\n\"The simplest way to add register a new factory is to annotate its class \"\n\"with ``@Bean`` annotation:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:392\nmsgid \"\"\n\"**Example of the registration of a SASL mechanism factory with an \"\n\"annotation setting id of the factory to** ``customSaslFactory``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:399\nmsgid \"\"\n\"It can also be done by specifying the class directly for bean \"\n\"``customSaslFactory`` in the ``config.tdsl`` file like in the example \"\n\"below:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:401\nmsgid \"\"\n\"**Example of the registration of a SASL mechanism factory with TDSL \"\n\"setting id of the factory to ``customSaslFactory``.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:411\nmsgid \"\"\n\"The class must implement the ``SaslServerFactory`` interface and has \"\n\"public constructor without any arguments. All mechanisms returned by \"\n\"``getMechanismNames()`` method will be registered automatically.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:413\nmsgid \"\"\n\"The default factory that is available and registered by default is \"\n\"``tigase.auth.TigaseSaslServerFactory`` which provides ``PLAIN``, \"\n\"``ANONYMOUS``, ``EXTERNAL``, ``SCRAM-SHA-1``, ``SCRAM-SHA-256`` and \"\n\"``SCRAM-SHA-512`` mechanisms.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:416\nmsgid \"CallbackHandler Configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:418\nmsgid \"\"\n\"The ``CallbackHandler`` is a helper class used for loading/retrieving \"\n\"authentication data from data repository and providing them to a \"\n\"mechanism.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:420\nmsgid \"\"\n\"To register a new callback handler you need to create a new class \"\n\"extending ``tigase.auth.CallbackHandlerFactory`` (if you wish to keep \"\n\"existing SASL callback handlers) or implementing \"\n\"``tigase.auth.CallbackHandlerFactoryIfc``. You will need to override \"\n\"``create()`` method to return an instance of your custom \"\n\"``CallbackHandler`` when appropriate.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:422\nmsgid \"\"\n\"Next you need to register new implementation of \"\n\"``CallbackHandlerFactoryIfc``. The ``config.tdsl`` file should include:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:432\nmsgid \"\"\n\"During the authentication process, Tigase server always checks for asks \"\n\"callback handler factory for specific handler to selected mechanisms, and\"\n\" if there is no specific handler the default one is used.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:435\nmsgid \"Selecting Mechanisms Available in the Stream\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:437\nmsgid \"\"\n\"The ``tigase.auth.MechanismSelector`` interface is used for selecting \"\n\"mechanisms available in a stream. Method ``filterMechanisms()`` should \"\n\"return a collection with mechanisms available based on:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:439\nmsgid \"all registered SASL factories\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:441\nmsgid \"XMPP session data (from ``XMPPResourceConnection`` class)\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:443\nmsgid \"\"\n\"The default selector returns mechanisms from all mechanism factories \"\n\"registered in ``sasl-provider`` ``(TigaseSaslProvider)``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:445\nmsgid \"\"\n\"It is possible to use a custom selector by specifying it’s class int the \"\n\"``config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:456\nmsgid \"Logging/Authentication\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:458\nmsgid \"\"\n\"After the XMPP stream is opened by a client, the server checks which SASL\"\n\" mechanisms are available for the XMPP session. Depending on whether the \"\n\"stream is encrypted or not, depending on the domain, the server can \"\n\"present different available authentication mechanisms. \"\n\"``MechanismSelector`` is responsible for choosing mechanisms. List of \"\n\"allowed mechanisms is stored in the XMPP session object.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:460\nmsgid \"\"\n\"When the client/user begins the authentication procedure it uses one \"\n\"particular mechanism. It must use one of the mechanisms provided by the \"\n\"server as available for this session. The server checks whether \"\n\"mechanisms used by the client is on the list of allowed mechanisms. It \"\n\"the check is successful, the server creates ``SaslServer`` class instance\"\n\" and proceeds with exchanging authentication information. Authentication \"\n\"data is different depending on the mechanism used.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:462\nmsgid \"\"\n\"When the SASL authentication is completed without any error, Tigase \"\n\"server should have authorized user name or authorized BareJID. In the \"\n\"first case, the server automatically builds user’s JID based on the \"\n\"domain used in the stream opening element in ``to`` attribute.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:464\nmsgid \"\"\n\"If, after a successful authentication, method call: \"\n\"``getNegotiatedProperty(\\\"IS_ANONYMOUS\\\")`` returns ``Boolean.TRUE`` then\"\n\" the user session is marked as anonymous. For valid and registered users \"\n\"this can be used for cases when we do not want to load any user data such\"\n\" as roster, vcard, privacy lists and so on. This is a performance and \"\n\"resource usage implication and can be useful for use cases such as \"\n\"support chat. The authorization is performed based on the client database\"\n\" but we do not need to load any XMPP specific data for the user’s \"\n\"session.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:466\nmsgid \"\"\n\"More details about implementation can be found in the :ref:`custom \"\n\"mechanisms development<cmd>` section.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:471\nmsgid \"Custom Mechanisms Development\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:474\nmsgid \"**Mechanism**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:476\nmsgid \"\"\n\"``getAuthorizationID()`` method from ``SaslServer`` class **should** \"\n\"return bare JID authorized user. In case that the method returns only \"\n\"user name such as **romeo** for example, the server automatically appends\"\n\" domain name to generate a valid BareJID: *romeo@example.com*. In case \"\n\"the method returns a full, valid BareJID, the server does not change \"\n\"anything.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:478\nmsgid \"\"\n\"``handleLogin()`` method from ``SessionManagerHandler`` will be called \"\n\"with user’s Bare JID provided by ``getAuthorizationID()`` (or created \"\n\"later using stream domain name).\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:481\nmsgid \"**CallbackHandler**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:483\nmsgid \"\"\n\"For each session authorization, the server creates a new and separate \"\n\"empty handler. Factory which creates handler instance allows to inject \"\n\"different objects to the handler, depending on interfaces implemented by \"\n\"the handler class:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:485\nmsgid \"``AuthRepositoryAware`` - injects ``AuthRepository;``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:487\nmsgid \"\"\n\"``DomainAware`` - injects domain name within which the user attempts to \"\n\"authenticate\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:489\nmsgid \"``NonAuthUserRepositoryAware`` - injects ``NonAuthUserRepository``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:492\nmsgid \"General Remarks\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:494\nmsgid \"\"\n\"``JabberIqAuth`` used for non-SASL authentication mechanisms uses the \"\n\"same callback as the SASL mechanisms.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:496\nmsgid \"\"\n\"Methods ``auth`` in ``Repository`` interfaces will be deprecated. These \"\n\"interfaces will be treated as user details providers only. There will be \"\n\"new methods available which will allow for additional login operations on\"\n\" the database such as last successful login recording.\"\nmsgstr \"\"\n\n#~ msgid \"`Writing Plugin Code <#writePluginCode>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Plugin Configuration <#pluginConf>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`How Packets are Processed by the SM and Plugins <#packetprocess>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`SASL Custom Mechanisms and Configuration <#saslcmac>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"There is another part of the \"\n#~ \"plugin configuration though. If you \"\n#~ \"looked at the `Writing Plugin Code \"\n#~ \"<#writePluginCode>`__ guide you can remember\"\n#~ \" the **Map settings** processing parameter.\"\n#~ \" This is a map of properties \"\n#~ \"you can set in the configuration \"\n#~ \"file and these setting will be \"\n#~ \"passed to the plugin at the \"\n#~ \"processing time.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"**Example of the registration of a \"\n#~ \"SASL mechanism factory with an \"\n#~ \"annotation setting id of the factory \"\n#~ \"to ``customSaslFactory``.**\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"More details about implementation can be\"\n#~ \" found in the `custom mechanisms \"\n#~ \"development <#cmd>`__ section.\"\n#~ msgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Development/Server_Compilation.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-05-29 02:43-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:2\nmsgid \"Server Compilation\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:4\nmsgid \"\"\n\"Tigase XMPP Server Project uses Maven for compilation. For details on \"\n\"Maven and it’s use, please see the :ref:`Maven Guide.<usingMaven>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:7\nmsgid \"Distribution Packages\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:9\nmsgid \"Once Compiled, Tigase creates two separate distribution archives:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:11\nmsgid \"\"\n\"**-dist** is a minimal version containing only tigase-server, tigase-\"\n\"xmltools and tigase-utils, MUC, Pubsub, and HTTP.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:13\nmsgid \"\"\n\"**-dist-max** is a version containing all additional tigase components as\"\n\" well as dependencies required by those components.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:15\nmsgid \"They will be available as both zip and tarball.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:18\nmsgid \"Building Server and Generating Packages\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:21\nmsgid \"Server binary and it’s documentation\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:23\nmsgid \"After cloning tigase-server repository:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:30\nmsgid \"You compile server with maven :\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:36\nmsgid \"This will: - Build Tigase XMPP tigase-server jar in tigase-server/target.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:38\nmsgid \"\"\n\"If you wish to include compilation of the documentation use \"\n\"*distribution* profile:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:44\nmsgid \"\"\n\"This will - compile server binaries. - generate javadoc and manual \"\n\"documentation ``tigase-server/target/_docs`` directory.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:47\nmsgid \"Server distribution packages\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:49\nmsgid \"\"\n\"Distribution building is handled by separate project (`Tigase Server \"\n\"Distribution <https://github.com/tigase/tigase-server-distribution>`__)\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:51\nmsgid \"\"\n\"In order to build distribution packages \\\\* clone tigase-server-\"\n\"distribution repository:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:58\nmsgid \"and compile it using maven with *distribution* profile:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:64\nmsgid \"This will:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:66\nmsgid \"\"\n\"compile all documentation sources (including dependencies) and place them\"\n\" in ``tigase-server-distribution/target/_docs`` directory\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:68\nmsgid \"\"\n\"download all dependencies in defined versions and put them in ``tigase-\"\n\"server-distribution/target/dist/jars/`` directory.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:70\nmsgid \"\"\n\"create both types of distribution packages (-dist and -dist-max) and \"\n\"place them in ``tigase-server-distribution/target/_dist/`` directory.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:73\nmsgid \"Running Server\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:75\nmsgid \"\"\n\"Afterwards you can run the server with the regular shell script from \"\n\"within ``server`` module:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:82\nmsgid \"\"\n\"Please bear in mind, that you need to provide correct setup in \"\n\"etc/config.tdsl configuration files for the server to work correctly.\"\nmsgstr \"\"\n\n#~ msgid \"\"\n#~ \"Tigase XMPP Server Project uses Maven\"\n#~ \" for compilation. For details on \"\n#~ \"Maven and it’s use, please see the\"\n#~ \" `Maven Guide. <#usingMaven>`__\"\n#~ msgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Development/Tests.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-05-27 12:30-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Tests.rst:2 ../../Tigase_Development/Tests.rst:5\nmsgid \"Tests\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:7\nmsgid \"Tests are very important part of Tigase server development process.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:9\nmsgid \"\"\n\"Each release goes through fully automated testing process. All server \"\n\"functions are considered implemented only when they pass the testing \"\n\"cycle. Tigase test suite is used for all our automatic tests which allows\"\n\" to define different test scenarios.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:11\nmsgid \"\"\n\"There is no tweaking on databases for tests. All databases are installed \"\n\"in a standard way and run with default settings. Databases are cleared \"\n\"each time before the test cycle starts.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:13\nmsgid \"\"\n\"There are no modifications needed to be made to Tigase’s configuration \"\n\"file as well. All tests are performed on a default configuration \"\n\"generated by the configuration wizards.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:15\nmsgid \"The server is tested in all supported environments:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:17\nmsgid \"\"\n\"**XMLDB** - tests with built-in simple XML database. This is a simple and\"\n\" efficient solution for small installations. It is recommended for \"\n\"services with up to 100 user accounts although it has been successfully \"\n\"tested with 10,000 user accounts.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:19\nmsgid \"\"\n\"**MySQL** - tests with a `MySQL <http://www.mysql.com/>`__ database. Much\"\n\" slower than XMLDB but may handle many more user accounts.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:21\nmsgid \"\"\n\"**PostgreSQL** - tests with a `PostgreSQL <http://www.postgresql.org/>`__\"\n\" database. Again it is much slower than XMLDB but may handle much more \"\n\"user accounts. This is basically exactly the same code as for MySQL \"\n\"database (SQL Connector) but tests are executed to make sure the code is \"\n\"compatible with all supported SQL databases and to compare performance.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:23\nmsgid \"\"\n\"**Distributed** - is a test for distributed installation where c2s and \"\n\"s2s components run on separated machine which connects using external an \"\n\"component protocol (`XEP-0114 \"\n\"<http://www.xmpp.org/extensions/xep-0114.html>`__) to another machine \"\n\"with SessionManager running.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:27\nmsgid \"Functional Tests\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:29\nmsgid \"\"\n\"Basic checking to see if all the functions work at correctly. These tests\"\n\" are performed every time the code is sent to source repository.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:32 ../../Tigase_Development/Tests.rst:73\n#: ../../Tigase_Development/Tests.rst:114\nmsgid \"Version\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:32 ../../Tigase_Development/Tests.rst:73\n#: ../../Tigase_Development/Tests.rst:114\nmsgid \"XMLDB\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:32 ../../Tigase_Development/Tests.rst:73\n#: ../../Tigase_Development/Tests.rst:114\nmsgid \"MySQL\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:32 ../../Tigase_Development/Tests.rst:73\n#: ../../Tigase_Development/Tests.rst:114\nmsgid \"PGSQL\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:32 ../../Tigase_Development/Tests.rst:73\n#: ../../Tigase_Development/Tests.rst:114\nmsgid \"Distributed\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:34 ../../Tigase_Development/Tests.rst:75\nmsgid \"3.3.2-b889\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:34\nmsgid \"`00:00:12 <tests/3.3.2-b889/func/xmldb/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:34\nmsgid \"`00:00:17 <tests/3.3.2-b889/func/mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:34\nmsgid \"`00:00:17 <tests/3.3.2-b889/func/pgsql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:34 ../../Tigase_Development/Tests.rst:75\nmsgid \"none\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:36 ../../Tigase_Development/Tests.rst:77\nmsgid \"3.3.2-b880\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:36\nmsgid \"`00:00:13 <tests/3.3.2-b880/func/xmldb/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:36\nmsgid \"`00:00:15 <tests/3.3.2-b880/func/mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:36\nmsgid \"`00:00:15 <tests/3.3.2-b880/func/pgsql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:36 ../../Tigase_Development/Tests.rst:64\n#: ../../Tigase_Development/Tests.rst:77 ../../Tigase_Development/Tests.rst:105\n#: ../../Tigase_Development/Tests.rst:116\nmsgid \"None\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:38 ../../Tigase_Development/Tests.rst:79\nmsgid \"3.0.2-b700\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:38\nmsgid \"`00:00:22 <tests/3.0.2-b700/func/xmldb/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:38\nmsgid \"`00:00:24 <tests/3.0.2-b700/func/mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:38\nmsgid \"`00:00:25 <tests/3.0.2-b700/func/pgsql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:38\nmsgid \"`00:00:25 <tests/3.0.2-b700/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:40 ../../Tigase_Development/Tests.rst:81\nmsgid \"2.9.5-b606\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:40\nmsgid \"`00:00:22 <tests/2.9.5-b606/func/xmldb/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:40\nmsgid \"`00:00:24 <tests/2.9.5-b606/func/mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:40\nmsgid \"`00:00:24 <tests/2.9.5-b606/func/pgsql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:40\nmsgid \"`00:00:24 <tests/2.9.5-b606/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:42 ../../Tigase_Development/Tests.rst:83\nmsgid \"2.9.3-b548\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:42\nmsgid \"`00:00:22 <tests/2.9.3-b548/func/xmldb/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:42\nmsgid \"`00:00:23 <tests/2.9.3-b548/func/mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:42\nmsgid \"`00:00:25 <tests/2.9.3-b548/func/pgsql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:42\nmsgid \"`00:00:25 <tests/2.9.3-b548/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:44 ../../Tigase_Development/Tests.rst:85\nmsgid \"2.9.1-b528\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:44\nmsgid \"`00:00:21 <tests/2.9.1-b528/func/xmldb/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:44\nmsgid \"`00:00:23 <tests/2.9.1-b528/func/mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:44\nmsgid \"`00:00:24 <tests/2.9.1-b528/func/pgsql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:44\nmsgid \"`00:00:25 <tests/2.9.1-b528/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:46 ../../Tigase_Development/Tests.rst:87\nmsgid \"2.8.6-b434\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:46\nmsgid \"`00:00:21 <tests/2.8.6-b434/func/xmldb/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:46\nmsgid \"`00:00:24 <tests/2.8.6-b434/func/mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:46\nmsgid \"`00:00:24 <tests/2.8.6-b434/func/pgsql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:46\nmsgid \"`00:00:25 <tests/2.8.6-b434/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:48 ../../Tigase_Development/Tests.rst:89\nmsgid \"2.8.5-b422\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:48\nmsgid \"`00:00:21 <tests/2.8.5-b422/func/xmldb/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:48\nmsgid \"`00:00:24 <tests/2.8.5-b422/func/mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:48\nmsgid \"`00:00:24 <tests/2.8.5-b422/func/pgsql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:48\nmsgid \"`00:00:26 <tests/2.8.5-b422/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:50 ../../Tigase_Development/Tests.rst:91\nmsgid \"2.8.3-b409\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:50\nmsgid \"`00:00:27 <tests/2.8.3-b409/func/xmldb/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:50\nmsgid \"`00:00:29 <tests/2.8.3-b409/func/mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:50\nmsgid \"`00:00:29 <tests/2.8.3-b409/func/pgsql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:50\nmsgid \"`00:00:32 <tests/2.8.3-b409/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:52 ../../Tigase_Development/Tests.rst:93\nmsgid \"2.7.2-b378\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:52\nmsgid \"`00:00:30 <tests/2.7.2-b378/func/xmldb/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:52\nmsgid \"`00:00:34 <tests/2.7.2-b378/func/mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:52\nmsgid \"`00:00:33 <tests/2.7.2-b378/func/pgsql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:52\nmsgid \"`00:00:35 <tests/2.7.2-b378/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:54 ../../Tigase_Development/Tests.rst:95\nmsgid \"2.6.4-b300\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:54\nmsgid \"`00:00:30 <tests/2.6.4-b300/func/xmldb/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:54\nmsgid \"`00:00:32 <tests/2.6.4-b300/func/mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:54\nmsgid \"`00:00:35 <tests/2.6.4-b300/func/pgsql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:54\nmsgid \"`00:00:39 <tests/2.6.4-b300/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:56 ../../Tigase_Development/Tests.rst:97\nmsgid \"2.6.4-b295\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:56\nmsgid \"`00:00:29 <tests/2.6.4-b295/func/xmldb/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:56\nmsgid \"`00:00:32 <tests/2.6.4-b295/func/mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:56\nmsgid \"`00:00:45 <tests/2.6.4-b295/func/pgsql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:56\nmsgid \"`00:00:36 <tests/2.6.4-b295/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:58 ../../Tigase_Development/Tests.rst:99\nmsgid \"2.6.0-b287\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:58\nmsgid \"`00:00:31 <tests/2.6.0-b287/func/xmldb/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:58\nmsgid \"`00:00:34 <tests/2.6.0-b287/func/mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:58\nmsgid \"`00:00:47 <tests/2.6.0-b287/func/pgsql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:58\nmsgid \"`00:00:43 <tests/2.6.0-b287/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:60 ../../Tigase_Development/Tests.rst:101\nmsgid \"2.5.0-b279\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:60\nmsgid \"`00:00:30 <tests/2.5.0-b279/func/xmldb/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:60\nmsgid \"`00:00:34 <tests/2.5.0-b279/func/mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:60\nmsgid \"`00:00:45 <tests/2.5.0-b279/func/pgsql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:60\nmsgid \"`00:00:43 <tests/2.5.0-b279/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:62 ../../Tigase_Development/Tests.rst:103\nmsgid \"2.4.0-b263\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:62\nmsgid \"`00:00:29 <tests/2.4.0-b263/func/xmldb/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:62\nmsgid \"`00:00:33 <tests/2.4.0-b263/func/mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:62\nmsgid \"`00:00:45 <tests/2.4.0-b263/func/pgsql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:62\nmsgid \"`00:00:44 <tests/2.4.0-b263/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:64 ../../Tigase_Development/Tests.rst:105\n#: ../../Tigase_Development/Tests.rst:116\nmsgid \"2.3.4-b226\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:64\nmsgid \"`00:00:48 <tests/functional-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:68\nmsgid \"Performance Tests\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:70\nmsgid \"Checking to see whether the function performs well enough.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:75\nmsgid \"`00:12:17 <tests/3.3.2-b889/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:75\nmsgid \"`00:13:42 <tests/3.3.2-b889/perf/mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:75\nmsgid \"`00:17:10 <tests/3.3.2-b889/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:77\nmsgid \"`00:13:39 <tests/3.3.2-b880/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:77\nmsgid \"`00:14:09 <tests/3.3.2-b880/perf/mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:77\nmsgid \"`00:17:39 <tests/3.3.2-b880/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:79\nmsgid \"`00:10:26 <tests/3.0.2-b700/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:79\nmsgid \"`00:11:00 <tests/3.0.2-b700/perf/mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:79\nmsgid \"`00:12:08 <tests/3.0.2-b700/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:79\nmsgid \"`00:24:05 <tests/3.0.2-b700/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:81\nmsgid \"`00:09:54 <tests/2.9.5-b606/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:81\nmsgid \"`00:11:18 <tests/2.9.5-b606/perf/mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:81\nmsgid \"`00:37:08 <tests/2.9.5-b606/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:81\nmsgid \"`00:16:20 <tests/2.9.5-b606/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:83\nmsgid \"`00:10:00 <tests/2.9.3-b548/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:83\nmsgid \"`00:11:29 <tests/2.9.3-b548/perf/mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:83\nmsgid \"`00:36:43 <tests/2.9.3-b548/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:83\nmsgid \"`00:16:47 <tests/2.9.3-b548/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:85\nmsgid \"`00:09:46 <tests/2.9.1-b528/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:85\nmsgid \"`00:11:15 <tests/2.9.1-b528/perf/mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:85\nmsgid \"`00:36:12 <tests/2.9.1-b528/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:85\nmsgid \"`00:16:36 <tests/2.9.1-b528/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:87\nmsgid \"`00:10:02 <tests/2.8.6-b434/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:87\nmsgid \"`00:11:45 <tests/2.8.6-b434/perf/mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:87\nmsgid \"`00:36:36 <tests/2.8.6-b434/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:87\nmsgid \"`00:17:36 <tests/2.8.6-b434/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:89\nmsgid \"`00:12:37 <tests/2.8.5-b422/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:89\nmsgid \"`00:14:40 <tests/2.8.5-b422/perf/mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:89\nmsgid \"`00:38:59 <tests/2.8.5-b422/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:89\nmsgid \"`00:21:40 <tests/2.8.5-b422/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:91\nmsgid \"`00:12:32 <tests/2.8.3-b409/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:91\nmsgid \"`00:14:26 <tests/2.8.3-b409/perf/mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:91\nmsgid \"`00:37:57 <tests/2.8.3-b409/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:91\nmsgid \"`00:21:26 <tests/2.8.3-b409/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:93\nmsgid \"`00:12:28 <tests/2.7.2-b378/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:93\nmsgid \"`00:14:57 <tests/2.7.2-b378/perf/mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:93\nmsgid \"`00:37:09 <tests/2.7.2-b378/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:93\nmsgid \"`00:22:20 <tests/2.7.2-b378/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:95\nmsgid \"`00:12:46 <tests/2.6.4-b300/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:95\nmsgid \"`00:14:59 <tests/2.6.4-b300/perf/mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:95\nmsgid \"`00:36:56 <tests/2.6.4-b300/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:95\nmsgid \"`00:35:00 <tests/2.6.4-b300/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:97\nmsgid \"`00:12:23 <tests/2.6.4-b295/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:97\nmsgid \"`00:14:59 <tests/2.6.4-b295/perf/mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:97\nmsgid \"`00:42:24 <tests/2.6.4-b295/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:97\nmsgid \"`00:30:18 <tests/2.6.4-b295/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:99\nmsgid \"`00:13:50 <tests/2.6.0-b287/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:99\nmsgid \"`00:16:53 <tests/2.6.0-b287/perf/mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:99\nmsgid \"`00:48:17 <tests/2.6.0-b287/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:99\nmsgid \"`00:49:06 <tests/2.6.0-b287/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:101\nmsgid \"`00:13:29 <tests/2.5.0-b279/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:101\nmsgid \"`00:16:58 <tests/2.5.0-b279/perf/mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:101\nmsgid \"`00:47:15 <tests/2.5.0-b279/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:101\nmsgid \"`00:41:52 <tests/2.5.0-b279/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:103\nmsgid \"`00:13:20 <tests/2.4.0-b263/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:103\nmsgid \"`00:16:21 <tests/2.4.0-b263/perf/mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:103\nmsgid \"`00:43:56 <tests/2.4.0-b263/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:103\nmsgid \"`00:42:08 <tests/2.4.0-b263/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:105\nmsgid \"`01:23:30 <tests/performance-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:109\nmsgid \"Stability Tests\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:111\nmsgid \"\"\n\"Checking to see whether the function behaves well in long term run. It \"\n\"must handle hundreds of requests a second in a several hour server run.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:116\nmsgid \"`16:06:31 <tests/stability-tests.html>`__\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:120\nmsgid \"Tigase Test Suite\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:122\nmsgid \"\"\n\"Tigase Test Suite is an engine which allows you to run tests. Essentially\"\n\" it just executes **TestCase** implementations. The tests may depend on \"\n\"other tests which means they are executed in specific order. For example \"\n\"authentication test is executed after the stream open test which in turn \"\n\"is executed after network socket connection test.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:124\nmsgid \"\"\n\"Each **TestCase** implementation may have it’s own set of specific \"\n\"parameters. There is a set of common parameters which may be applied to \"\n\"any **TestCase**. As an example of the common parameter you can take \"\n\"**-loop = 10** which specified that the **TestCase** must be executed 10 \"\n\"times. The test specific parameter might be **-user-name = tester** which\"\n\" may set the user name for authentication test.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:126\nmsgid \"\"\n\"The engine is very generic and allows you to write any kind of tests but \"\n\"for the Tigase projects the current TestCase implementations mimic an \"\n\"XMPP client and are designed to test XMPP servers.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:128\nmsgid \"\"\n\"The suite contains a kind of scripting language which allows you to \"\n\"combine test cases into a test scenarios. The test scenario may contain \"\n\"full set of functional tests for example, another test scenario may \"\n\"contain performance tests and so on.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:131\nmsgid \"Running Tigase Test Suite (TTS)\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:133\nmsgid \"To obtain TTS, you will first need to clone the repository\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:139\nmsgid \"\"\n\"Once cloning is finished, navigate to the TTS root directory and compile \"\n\"with maven:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:145\nmsgid \"\"\n\"Maven will compile TTS and place jars in the necessary locations. From \"\n\"the same directory, you can begin running TTS using the following \"\n\"command:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:151\nmsgid \"\"\n\"You should see the following, which outlines the possible options to \"\n\"customize your test run\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:193\nmsgid \"Customizing Tigase Test Suite\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:195\nmsgid \"\"\n\"You may run the tests from a command line like above, however you may \"\n\"create and edit the /scripts/tests-runner-settings.sh file to fit your \"\n\"Tigase installation and avoid having to have long complex commands as \"\n\"this template shows:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:225\nmsgid \"\"\n\"This will allow you to maintain identical settings through multiple runs \"\n\"of TTS. See the next section for learning how the scripting language \"\n\"works and how you can create and run your own custom tests.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:228\nmsgid \"Test Suite Scripting Language\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:230\nmsgid \"\"\n\"The test suite contains scripting language which allows you to combine \"\n\"test cases into a test scenarios. On the lowest level, however the \"\n\"language is designed to allow you to describe the test by setting test \"\n\"parameters, test comments, identification and so on.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:232\nmsgid \"Let’s look at the example test description.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:244\nmsgid \"Meaning of all elements:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:246\nmsgid \"\"\n\"**Short name** is any descriptive name you want. It doesn’t need to be \"\n\"unique, just something which tells you what this test is about. @ is a \"\n\"separator between the short name and the test ids.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:248\nmsgid \"\"\n\"**test-id-1;test-id-2** is a semicolon separated of the test cases IDs. \"\n\"The tests cases are executed in the listed order. And listing them there \"\n\"means that the test-id-2 depends on test-id-1. Normally you don’t have to\"\n\" list all the dependencies because all mandatory dependencies are \"\n\"included automatically. Which means if you have an authentication test \"\n\"case the suite adds the network socket connection and stream opening \"\n\"tests automatically. Sometimes however, there are dependencies which are \"\n\"optional or multiple mandatory dependencies and you need to select which \"\n\"one has to be executed. As a good example is the authentications test \"\n\"case. There are many authentication tests: PLAIN-AUTH, SASL-DIGESTMD5, \"\n\"SASL-PLAIN, DIGEST-AUTH and they are all mandatory for most of other \"\n\"tests like roster, presence and so on. One of the authentication tests is\"\n\" a default dependency but if you put on the list different authentication\"\n\" it will be used instead of default one.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:250\nmsgid \"\"\n\"**:** is a separator between test cases ids list and the short test \"\n\"description.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:252\nmsgid \"\"\n\"**Short test description** is placed between : - colon and opening **{** \"\n\"- curly bracket. This is usually quite brief, single line test \"\n\"description.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:254\nmsgid \"\"\n\"**{ }** curly brackets contain all the test parameters, like how many \"\n\"times the test has to be executed or run the test in a separate thread, \"\n\"user name, host IP address for the network connection and many others.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:256\nmsgid \"\"\n\"**>> <<** inside the double greater than and double less than you put a \"\n\"very long, multiple line test description.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:258\nmsgid \"\"\n\"As for the testing script between open curly brackets { and close one } \"\n\"you can put all the test case parameters you wish. The format for it is:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:260\nmsgid \"**-parameter-name = value**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:262\nmsgid \"\"\n\"Parameter names always start with **-**. Note, some parameters don’t \"\n\"require any value. They can exist on their own without any value \"\n\"assigned:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:264\nmsgid \"**-debug-on-error**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:266\nmsgid \"This imitates if you were to put **yes** or **true** as the value.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:268\nmsgid \"\"\n\"The scripting language includes also support for variables which can be \"\n\"assigned any value and used multiple times later on. You assign a value \"\n\"to the variable the same way as you assign it to the parameter:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:270\nmsgid \"**$(variable-name) = value**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:272\nmsgid \"\"\n\"The variable name must be always enclosed with brackets **()** and start \"\n\"with **$**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:274\nmsgid \"\"\n\"The value may be enclosed within double quotes **\\\"\\\"** or double quotes \"\n\"may be omitted. If this is a simple string like a number or character \"\n\"string consisting only of digits, letters, underscore **\\\\_** and hyphen \"\n\"**-** then you can omit double quotes otherwise you must enclose the \"\n\"value.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:276\nmsgid \"\"\n\"The test case descriptions can be nested inside other test case \"\n\"descriptions. Nested test case descriptions inherit parameters and \"\n\"variables from outer test case description.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:279\nmsgid \"Writing Tests for Plugins\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:281\nmsgid \"\"\n\"You can write tests in a simple text file which is loaded during test \"\n\"suite runtime.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:283\nmsgid \"\"\n\"You simply specify what should be send to the server and what response \"\n\"should be expected from the server. No need to write Java code and \"\n\"recompile the whole test suite for new tests. It means new test cases can\"\n\" be now written easily and quickly which hopefully means more detailed \"\n\"tests for the server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:285\nmsgid \"How it works:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:287\nmsgid \"\"\n\"Let’s take `XEP-0049 <http://www.xmpp.org/extensions/xep-0049.html>`__ \"\n\"Private XML Storage. Looking into the spec we can see the first example:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:290\nmsgid \"Example: Client Stores Private Data\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:292\nmsgid \"**CLIENT:**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:304\nmsgid \"**SERVER:**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:310\nmsgid \"\"\n\"This is enough for the first simple test. I have to create text file \"\n\"``JabberIqPrivate.test`` looking like this:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:329\nmsgid \"And now I can execute the test:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:366\nmsgid \"\"\n\"If I just started working on this XEP and there is no code on the server \"\n\"side, the result is perfectly expected although maybe this is not what we\"\n\" want. After a while of working on the server code I can execute the test\"\n\" once again:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:396\nmsgid \"\"\n\"This is it. The result we want in a simple and efficient way. We can \"\n\"repeat it as many times we want which is especially important in longer \"\n\"term trials. Every time we change the server code we can re-run tests to \"\n\"make sure we get correct responses from the server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:398\nmsgid \"\"\n\"You can have a look in the current build, with more complete test cases, \"\n\"file for `JabberIqPrivate <https://github.com/tigase/tigase-\"\n\"testsuite/tree/master/tests/data/JabberIqPrivate.cot>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:400\nmsgid \"\"\n\"Now my server tests are no longer outdated. Of course not all cases are \"\n\"so simple. Some XEPs require calculations to be done before stanza is \"\n\"sent or to compare received results. A good example for this case is user\"\n\" authentication like SASL and even NON-SASL. But still, there are many \"\n\"cases which can be covered by simple tests: roster management, privacy \"\n\"lists management, vCard, private data storage and so on.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:403\nmsgid \"Test Case Parameters Description\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:405\n#: ../../Tigase_Development/Tests.rst:407\nmsgid \"\"\n\"There is long list of parameters which can be applied to any test case. \"\n\"Here is the description of all possible parameters which can be used to \"\n\"build test scenarios.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:410\nmsgid \"Test Report Configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:412\nmsgid \"\"\n\"There are test report parameters which must be set in the main script \"\n\"file in order to generate HTML report from the test. These parameters \"\n\"have no effect is they are set inside the test case description.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:414\nmsgid \"\"\n\"**-version = 2.0.0** sets the test script version. This is used to easily\"\n\" detect incompatibility issues when the test suite loads a script created\"\n\" for more recent version of the suite and may not work properly for this \"\n\"version.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:416\nmsgid \"\"\n\"**-output-format = (html \\\\| html-content)** sets the output format for \"\n\"the test report. There is actually only one format possible right now - \"\n\"HTML. The only difference between these 2 options is that the **html** \"\n\"format creates full HTML page with HTML header and body. The **html-\"\n\"content** format on the other hand creates only what is inside \"\n\"``<body/>`` element. And is used to embed test result inside other HTML \"\n\"content.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:418\nmsgid \"\"\n\"**-output-file = \\\"report-file.html\\\"** sets the file name for the test \"\n\"report.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:420\nmsgid \"\"\n\"**-output-history = (yes \\\\| no)** sets logging of the all protocol data \"\n\"sent between test suite and the XMPP server. Normally for functional \"\n\"tests it is recommended to set it to **yes** but for all other tests like\"\n\" performance or load tests it should be set to **no**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:422\nmsgid \"\"\n\"**-history-format = separate-file** sets protocol data logging to a \"\n\"separate file. Currently this is the only possible option.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:424\nmsgid \"**-output-cols = (5 \\\\| 7)** Only valid values are:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:431\nmsgid \"\"\n\"**-title** = \\\"The title of the report page\\\" This parameter sets the \"\n\"test report title which is placed in the HTML page in the ``<title/>`` \"\n\"element as well as in the first page header.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:434\nmsgid \"Basic Test Parameters\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:436\nmsgid \"\"\n\"These parameters can be set on per-test case basis but usually they are \"\n\"set in the main script file to apply them to all test cases.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:438\nmsgid \"\"\n\"**-base-ns = \\\"jabber:client\\\"** sets the XML name space used for the XML\"\n\" stream in the XMPP connection. Some test cases can be used to test \"\n\"client to server protocol as well as server to server protocol and \"\n\"possibly different protocols added in the future.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:440\nmsgid \"\"\n\"**-debug** switches debugging mode on. All the communication between the \"\n\"test suite and the server is printed out to the text console and all \"\n\"other debugging information including java exceptions are displayed as \"\n\"well. It is especially useful when some test fails and you want to find \"\n\"out why.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:442\nmsgid \"\"\n\"**-debug-on-error** switches on debugging mode on error detection. \"\n\"Normally debug output generates lots of message which makes the output \"\n\"very hard to read. Especially in the performance tests not only you can \"\n\"read fast scrolling lines of the protocol data but also it slows the test\"\n\" down. This option however turns debugging off if everything is working \"\n\"well and then generates debug output if any test error us detected.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:444\nmsgid \"\"\n\"**-def-auth = (auth-plain \\\\| auth-digest \\\\| auth-sasl)** sets the \"\n\"default authentication method for the user connection.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:446\nmsgid \"\"\n\"**-def-stream = (stream-client \\\\| stream-server \\\\| stream-component \\\\|\"\n\" stream-bosh)** sets the connection stream to be tested and the name \"\n\"space for the connection.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:448\nmsgid \"\"\n\"**-host = \\\"host.name\\\"** the vhost name the tested server runs for. It \"\n\"may be the real DNS name or just configured for testing purposes \"\n\"hostname. It must match however the server configuration.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:450\nmsgid \"\"\n\"**-keys-file = \\\"certs/keystore\\\"** sets the location of the keys store \"\n\"file. No need to touch it.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:452\nmsgid \"\"\n\"**-keys-file-password = keystore** sets the password for the keystore \"\n\"file. Normally you don’t have to touch it.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:454\nmsgid \"\"\n\"**-serverip = \\\"127.0.0.1\\\"** defines the XMPP server IP address. You may\"\n\" omit this parameter and then the IP address will be determined \"\n\"automatically based on the server DNS address. However if the DNS address\"\n\" can not be correctly resolved or if you run tests on the localhost you \"\n\"can use this parameter to enforce the IP address.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:456\nmsgid \"\"\n\"**-socket-wait = 10000** sets the network socket timeout in milliseconds \"\n\"that is maximum time the test suite will wait for the response from the \"\n\"server. You may want to increase the timeout for some specific tests \"\n\"which require lots of computation or database activity on the server. \"\n\"Normally 10 seconds is enough for most cases.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:458\nmsgid \"\"\n\"**-stop-on-fail = true** causes the script to terminate all actions on \"\n\"the first failed test case. It helps diagnosing the server state at the \"\n\"failure point.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:460\nmsgid \"\"\n\"**-trust-file = \\\"certs/client_truststore\\\"** sets the file name for the \"\n\"client trust store file. No need to change it.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:462\nmsgid \"\"\n\"**-trust-file-password = truststore** sets the password for the trust \"\n\"store file. Normally you don’t have to touch it.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:464\nmsgid \"\"\n\"**-user-name = tester** sets the user name used for the XMPP connections \"\n\"between the test suite and the XMPP server. It is usually set globally \"\n\"the same for all tests and for some tests like receiving the server \"\n\"configuration you may want to use a different account (with admin \"\n\"permissions). Then you can set a different user for this specific test \"\n\"case.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:466\nmsgid \"\"\n\"**-user-pass = tester-password** sets the password for the user used for \"\n\"the XMPP connection between the test suite and the XMPP server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:468\nmsgid \"\"\n\"**-user-resr = resource** sets the user JID resource part for the XMPP \"\n\"connection between the test suite and the XMPP server.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:471\nmsgid \"Test Case Parameters\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:473\nmsgid \"\"\n\"Test parameters which are normally set on per-test case basis and apply \"\n\"only to the test they are set for and all inherited tests. Some of the \"\n\"parameters though are applied only to inherited test cases. Please look \"\n\"in the description below to find more details.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:475\nmsgid \"\"\n\"**-active-connection** is a similar parameter to **-on-one-socket** \"\n\"option. If set the suite doesn’t close the network socket and if the test\"\n\" is run in loop each loop run re-uses the network connection. Unlike in \"\n\"the -on-one-socket mode the whole test is executed on each run including \"\n\"XMPP stream initialization and user authentication. This option is \"\n\"currently not recommended in a normal use. It is useful only to debug the\"\n\" server behavior in very special use cases.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:477\nmsgid \"\"\n\"**-background** executes the test in a separate thread in background and \"\n\"immediately returns control to the test suite program without waiting for\"\n\" the test to complete. Default behavior is to execute all tests \"\n\"sequentially and run next test when previous one has been completed. This\"\n\" parameter however allows to run tests concurrently. This a bit similar \"\n\"option to the **-daemon** parameter. The daemon test/task however is \"\n\"ignored completely and results from the daemon are not collected where \"\n\"the background test is a normal test which is run concurrently with \"\n\"another one or possibly many other tests.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:479\nmsgid \"\"\n\"**-daemon** creates a task running in background in a separate thread. \"\n\"Such a test runs infinitely as a daemon, it is not recorded in the test \"\n\"report and it’s result is not calculated. The purpose of such test/task \"\n\"is to work as a helper for other test cases. A good example of such \"\n\"daemon test is message responder - the test which runs under a different \"\n\"user name and waits for messages and responding to the sender.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:481\nmsgid \"\"\n\"**-delay = 1000** sets the waiting time in milliseconds after the test \"\n\"case is completed. You may use it if you want to introduce short delay \"\n\"between each test cases run in the loop or if you start the helper daemon\"\n\" thread and you have to add the delay to make sure it is ready to work \"\n\"before next real test starts sending requests to the daemon.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:483\nmsgid \"\"\n\"**-expect-type = error** sets the type for a packet expected as a \"\n\"response. Some test cases like message sender expects sometimes response \"\n\"with the same type it has sent the packet ( **chat** ) but in some other \"\n\"cases when it sends a message to a user who has privacy lists set to \"\n\"block messages the response should be with an error. This way we can use \"\n\"the same test cases for testing different responses scenarios.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:485\nmsgid \"\"\n\"**-loop = 10** sets the number of times the test (and all inherited \"\n\"tests) are repeated. You can use a **$(loop)** pseudo-variable to obtain \"\n\"and use the current loop run number. This is useful if you want to run \"\n\"every loop run for a different user name like registering 10 different \"\n\"user accounts. To do this you stick the $(loop) variable to the user name\"\n\" string: **-user-name = \\\"nick_name_$(loop)\\\"**.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:487\nmsgid \"\"\n\"**-loop-delay = 10** sets a delay in milliseconds between each individual\"\n\" loop run for the tests which is run multiple times. This is similar \"\n\"parameter to the **-delay** one but the **-delay** option introduces a \"\n\"delay after the whole test (or all loop runs) has been completed. The \"\n\"loop delay options adds waiting time between each run of the looped test.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:489\nmsgid \"\"\n\"**-loop-start = 5** sets the loop starting value. It doesn’t affect \"\n\"number of loop runs in a any way. It only affects the value of the \"\n\"**$(loop)** variable. Let’s say you want to run a load test for the \"\n\"server with 100k concurrent users and you want to run the test from 3 \"\n\"different machines. To make sure each machine uses distinct user accounts\"\n\" you have to set a different **-loop-start** parameter on each to prevent\"\n\" from overlapping.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:491\nmsgid \"\"\n\"**-messages = 10** sets the number of messages to send to the server. \"\n\"This is another way of looping the test. Instead of repeating the whole \"\n\"test with opening network connection, XMPP stream, authentication and so \"\n\"on it causes only to send the message this many times. This parameters is\"\n\" accepted by some test cases only which send messages. For the messages \"\n\"listeners - test cases which is supposed to respond to the messages the \"\n\"number set here specifies how many times the the response must be sent \"\n\"before the test successfully terminates it’s work.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:493\nmsgid \"\"\n\"**-multi-thread** option causes to run the test case and all inherited in\"\n\" all levels test cases in separate threads. Normally the test case where \"\n\"you put the parameter doesn’t have a test ID (what you put between '@' \"\n\"and ':' characters so it doesn’t run a test on it’s own. Instead it \"\n\"contains a series of test cases inside which are then run in a separate \"\n\"thread each. This is a key parameter to run tests for many concurrent \"\n\"users. (Not a load tests though.) For example you can see whether the \"\n\"server behaves correctly when 5 simultaneous modifies their roster. The \"\n\"execution time all inherited tests run in a separate threads is added \"\n\"together and also results from each individual test is calculated and \"\n\"added to the total main test results.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:495\nmsgid \"\"\n\"**-no-record** is used for kind of configuration tests (tasks) which are \"\n\"used to prepare the XMPP server or database for later tests. As an \"\n\"example can be creation of the test user account which is later on used \"\n\"for the roster tests. Usually you don’t want to include such tests in the\"\n\" test report and using this parameter you essentially exclude the test \"\n\"from the report. The test and the result however shows in the command \"\n\"line output so you can still track what is really going on.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:497\nmsgid \"\"\n\"**-on-one-socket** is a modifier for a looped test case. Normally when we\"\n\" switch looping on using **-loop** parameter the suite resets the state, \"\n\"closes the network socket and runs the test from the very beginning \"\n\"including opening network socket, XMPP stream, authentication and so on. \"\n\"This parameter however changes this behavior. The network socket is not \"\n\"closed when the test run is completed (successfully) and next run \"\n\"executes only the last part of the test omitting the XMPP stream \"\n\"initialization, authentication and all others but last. This is useful \"\n\"when you want to send many messages to the server (although this effect \"\n\"may be accomplished using **-messages** parameter as well) or registering\"\n\" many user accounts on the server, unregistering user accounts and any \"\n\"other which might make sense repeating many times.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:499\nmsgid \"\"\n\"**-port = 5223** this parameter is similar to the IP address setting and \"\n\"can be also set globally for all tests. Normally however you set it for a\"\n\" selected tests only to check SSL connection. For all other tests default\"\n\" port number is used. Therefore this parameters has been included in this\"\n\" section instead of \\\"Basic test parameters\\\".\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:501\nmsgid \"\"\n\"**-presence** this parameter enables sending initial presence with \"\n\"positive priority after connection and binding the session.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:503\nmsgid \"\"\n\"**-repeat-script = 100** and **-repeat-wait = 10** are 2 parameters are \"\n\"specific to the common test cases. (The test cases which reads the test \"\n\"input/output data from the pseudo-xml text file. The first parameter is \"\n\"another variation of test looping. It sets how many times the test has to\"\n\" be repeated. It works very much like the **-on-one-socket** parameter. \"\n\"The only difference is that the common test can preserve some internal \"\n\"states between runs and therefore it has more control over the data. The \"\n\"second parameter sets the timeout in milliseconds to wait/delay between \"\n\"each individual test run and it is a very similar parameter to the \"\n\"**-delay** one but it sets a timeout inside the common test instead.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:505\nmsgid \"\"\n\"**-source-file = \\\"dir/path/to/file.cot\\\"** is a parameter to set the \"\n\"\\\"common test\\\" script file. The common test is a test cases which \"\n\"depends on the authentication test case and can read data to send and \"\n\"responses to expect from the text file. The \\\"cot\\\" file is a pseudo-xml \"\n\"file with stanzas to send and stanzas to expect. The the test cases \"\n\"compares the received packets with those in the text file and reports the\"\n\" test result. This is usually a more convenient way to write a new test \"\n\"cases than coding them in Java.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:507\nmsgid \"\"\n\"**-time-out-ok** is set for a test case when we expect socket timeout as \"\n\"a correct result from the test case. Normally the timeout means that the \"\n\"test failed and there was no response from the server at all or the \"\n\"response was incorrect. For some tests however (like sending a message to\"\n\" the user who is blocking messages through privacy lists) the timeout is \"\n\"the desired correct test result.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tests.rst:509\nmsgid \"\"\n\"**-to-jid = \\\"**\\\\ user_name@host.name\\\\ **\\\"** sets the destination \"\n\"address for packets sending packets somewhere. As an example is the test \"\n\"case sending ``<message/>`` packet. You can set the destination address \"\n\"for the packet. Mind, normally every test expects some response for the \"\n\"data sent so make sure the destination end-point will send back the data \"\n\"expected by the test case.\"\nmsgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Development/Tigase_Kernel.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:2\nmsgid \"Tigase Kernel\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:4\nmsgid \"\"\n\"Tigase Kernel is an implementation of `IoC \"\n\"<https://en.wikipedia.org/wiki/Inversion_of_control>`__ created for \"\n\"Tigase XMPP Server. It is responsible for maintaining object lifecycle \"\n\"and provides mechanisms for dependency resolutions between beans.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:6\nmsgid \"\"\n\"Additionally, as and optional feature, Tigase Kernel is capable of \"\n\"configuring beans using a provided bean configurator.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:9\nmsgid \"Basics\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:12\nmsgid \"What is kernel?\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:14\nmsgid \"\"\n\"Kernel is an instance of the ``Kernel`` class which is responsible for \"\n\"managing scope and visibility of beans. Kernel handles bean:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:16\nmsgid \"registration of a bean\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:18\nmsgid \"unregistration of a bean\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:20\nmsgid \"initialization of a bean\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:22\nmsgid \"deinitialization of a bean\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:24\nmsgid \"dependency injection to the bean\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:26\nmsgid \"handling of bean lifecycle\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:28\nmsgid \"\"\n\"registration of additional beans based on annotations (*optionally using \"\n\"registered class implementing* ``BeanConfigurator`` *as* \"\n\"``defaultBeanConfigurator``)\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:30\nmsgid \"\"\n\"configuration of a bean (*optionally thru registered class implementing* \"\n\"``BeanConfigurator`` *as* ``defaultBeanConfigurator``)\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:32\nmsgid \"\"\n\"Kernel core is responsible for dependency resolution and maintaining \"\n\"lifecycle of beans. Other features, like proper configuration of beans \"\n\"are done by additional beans working inside the Kernel.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:34\nmsgid \"\"\n\"Kernel identifies beans by their name, so each kernel may have only one \"\n\"bean named ``abc``. If more than one bean has the same name, then the \"\n\"last one registered will be used as its registration will override \"\n\"previously registered beans. You may use whatever name you want to name a\"\n\" bean inside kernel but it cannot:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:36\nmsgid \"\"\n\"be ``service`` as this name is used by Tigase Kernel internally when \"\n\"RegistrarBean\\\\`s are in use (see :ref:`RegistrarBean<registrarBean>`)\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:38\nmsgid \"\"\n\"end with ``#KERNEL`` as this names are also used by Tigase Kernel \"\n\"internally\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:42\nmsgid \"\"\n\"Kernel initializes beans using lazy initialization. This means that if a \"\n\"bean is not required by any other beans, or not retrieved from the kernel\"\n\" manually, an instance will not be created.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:44\nmsgid \"\"\n\"During registration of a bean, the kernel checks if there is any beans \"\n\"which requires this newly registered bean and if so, then instance of a \"\n\"newly registered bean will be created and injected to fields which \"\n\"require it.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:47\nmsgid \"What is a kernel scope?\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:49\nmsgid \"\"\n\"Each kernel has its own scope in which it can look for beans. By default \"\n\"kernel while injecting dependencies may look for them only in the same \"\n\"kernel instance in which new instance of a bean is created or in the \"\n\"direct parent kernel. This way it is possible to have separate beans \"\n\"named the same in the different kernel scopes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:53\nmsgid \"\"\n\"If bean is marked as ``exportable``, it is also visible in all \"\n\"descendants kernel scopes.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:56\nmsgid \"What is a bean?\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:57\nmsgid \"\"\n\"A bean is a named instance of the class which has parameterless \"\n\"constructor and which is registered in the kernel.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:61\nmsgid \"\"\n\"Parameterless constructor is a required as it will be used by kernel to \"\n\"create an instance of the bean, see :ref:`bean lifecycle<beanLifecycle>`.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:66\nmsgid \"Lifecycle of a bean\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:69\nmsgid \"Creating instance of a bean\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:72\nmsgid \"Instantiation of a bean\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:74\nmsgid \"\"\n\"During this step, kernel creates instance of the class which was \"\n\"registered for this bean (for more details see **Registration of a \"\n\"bean**). Instance of a bean is created using paremeterless constructor of\"\n\" a class.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:78\nmsgid \"\"\n\"Bean instance is only created for required beans (i.e. beans that were \"\n\"injected somewhere).\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:82\nmsgid \"\"\n\"It’s possible to create bean instance without the need to inject it \"\n\"anywhere - such bean should be annoted with ``@Autostart`` annotation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:87\nmsgid \"Configuring a bean *(optional)*\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:89\nmsgid \"\"\n\"In this step kernel passes class instance of a bean to the configurator \"\n\"bean (an instance of ``BeanConfigurator`` if available), for configuring \"\n\"it. During this step, ``BeanConfigurator`` instance, which is aware of \"\n\"the configuration loaded from the file, injects this configuration to the\"\n\" bean fields annotated with ``@ConfigField`` annotation. By default \"\n\"configurator uses reflections to access those fields. However, if a bean \"\n\"has a corresponding public ``setter``/``getter`` methods for a field \"\n\"annotated with ``@ConfigField`` (method parameter/return type matches \"\n\"field type), then configurator will use them instead of accessing a field\"\n\" via reflection.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:93\nmsgid \"\"\n\"If there is no value for a field specified in the configuration or value \"\n\"is equal to the current value of the field, then configurator will skip \"\n\"setting value for this field (It will also not call ``setter`` method \"\n\"even if it exists).\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:95\nmsgid \"\"\n\"At the end of the configuration step, if bean implements \"\n\"``ConfigurationChangedAware`` interface, then method \"\n\"``beanConfigurationChanged(Collection<String> changedFields)`` is being \"\n\"called, to notify bean about field names which values has changed. This \"\n\"is useful, if you need to update bean configuration, when you have all \"\n\"configuration available inside bean.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:99\nmsgid \"\"\n\"Configuration of the bean may be changed at runtime and it will be \"\n\"applied in the same way as initial configuration is passed to the bean. \"\n\"So please keep in mind that ``getter``/``setter`` may be called multiple \"\n\"times - even for already configured and initialized bean.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:104\nmsgid \"Injecting dependencies\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:106\nmsgid \"\"\n\"At this point kernel looks for the bean class fields annotated with \"\n\"``@Inject`` and looks for a value for each of this fields. During this \"\n\"step, kernel checks list of available beans in this kernel, which matches\"\n\" field type and additional constraints specified in the annotation.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:108\nmsgid \"\"\n\"When a required value (instance of a bean) is found, then kernel tries to\"\n\" inject it using reflection. However, if there is a matching \"\n\"``getter``/``setter`` defined for that field it will be called instead of\"\n\" reflection.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:112\nmsgid \"\"\n\"If dependency changes, ie. due to reconfiguration, then value of the \"\n\"dependent field will change and ``setter`` will be called if it exists. \"\n\"So please keep in mind that ``getter``/``setter`` may be called multiple \"\n\"times - even for already configured and initialized bean.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:115\nmsgid \"Initialization of a bean\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:117\nmsgid \"\"\n\"When bean is configured and dependencies are set, then initialization of \"\n\"a bean is almost finished. At this point, if bean implements \"\n\"``Initializable`` interface, kernel calls ``initialize()`` method to \"\n\"allow bean initialize properly if needed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:120\nmsgid \"Destroying instance of a bean\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:122\nmsgid \"\"\n\"When bean is being unloaded, then reference to its instance is just \"\n\"dropped. However, if bean class implements ``UnregisterAware`` interface,\"\n\" then kernel calls ``beforeUnregister()`` method. This is very useful in \"\n\"case which bean acquires some resources during initialization and should \"\n\"release them now.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:126\nmsgid \"\"\n\"This method will not be called if bean was not initialized fully (bean \"\n\"initialization step was note passed)!\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:129\nmsgid \"Reconfiguration of a bean *(optional)*\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:131\nmsgid \"\"\n\"At any point in time bean may be reconfigured by default bean \"\n\"configurator (instance of ``BeanConfigurator``) registered in the kernel.\"\n\" This will happen in the same way as it described in :ref:`Configuring a \"\n\"bean<beanConfiguration>` in **Creating instace of a bean** section.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:134\nmsgid \"Updating dependencies\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:136\nmsgid \"\"\n\"It may happen, that due to reconfiguration or registration/unregistration\"\n\" or activation/deactivation of some other beans dependencies of a bean \"\n\"will change. As a result, Tigase Kernel will inject new dependencies as \"\n\"described in :ref:`Injecting dependencies<beanInjectingDependencies>`\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:139\nmsgid \"Registration of a bean\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:141\nmsgid \"There are few ways to register a bean.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:144\nmsgid \"Using annotation *(recommended but optional)*\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:146\nmsgid \"\"\n\"To register a bean using annotation you need to annotate it with \"\n\"``@Bean`` annotation and pass values for following properties:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:148\nmsgid \"``name`` - name under which item should be registered\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:150\nmsgid \"\"\n\"``active`` - ``true`` if bean should be enabled without enabling it in \"\n\"the configuration *(however it is still possible to disable it using \"\n\"configuration)*\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:152\nmsgid \"\"\n\"``parent`` - class of the parent bean which activation should trigger \"\n\"registration of your bean. **In most cases parent class should be \"\n\"implementing ``RegistrarBean``**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:154\nmsgid \"\"\n\"``parents`` - array of classes which should be threaten as ``parent`` \"\n\"classes if more than one parent class is required *(optional)*\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:156\nmsgid \"\"\n\"``exportable`` - ``true`` if bean should be visible in all descendant \"\n\"kernels (in other case default visibility rules will be applied) \"\n\"*(optional)*\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:158\nmsgid \"\"\n\"``selectors`` - array of selector classes which will decide whether class\"\n\" should be registered or not *(optional)*\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:162\nmsgid \"\"\n\"If ``parent`` is set to ``Kernel.class`` it tells kernel to register this\"\n\" bean in the root/main kernel (top-level kernel).\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:164\nmsgid \"\"\n\"If you want your bean ``SomeDependencyBean`` to be registered when \"\n\"another bean ``ParentBean`` is being registered (like a required \"\n\"dependency), you may annotate your bean ``SomeDependencyBean`` with \"\n\"``@Bean`` annotation like this example:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:175\nmsgid \"\"\n\"Works only if bean registered as ``defaultBeanConfigurator`` supports \"\n\"this feature. By default Tigase XMPP Server uses ``DSLBeanConfigurator`` \"\n\"which is subclass of ``AbstractBeanConfigurator`` which provides support \"\n\"for this feature.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:178\nmsgid \"Setting ``parent`` to class not implementing ``RegistrarBean`` interface\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:180\nmsgid \"\"\n\"If ``parent`` is set to the class which is not implementing \"\n\"``RegistrarBean`` interface, then your bean will be registered in the \"\n\"same kernel scope in which parent bean is registered. If you do so, ie. \"\n\"by setting parent to the class of the bean which is registered in the \"\n\"``kernel1`` and your bean will be also registered in ``kernel1``. As the \"\n\"result it will be exposed to other beans in the same kernel scope. This \"\n\"also means that if you will configure it in the same way as you would set\"\n\" ``parent`` to the ``parent`` of annotation of the class to which your \"\n\"``parent`` point to.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:182\n#: ../../Tigase_Development/Tigase_Kernel.rst:387\nmsgid \"**Example.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:200\nmsgid \"\"\n\"In this case it means that ``bean1`` is registered in the root/main \"\n\"kernel instance. At the same time, ``bean2`` is also registered to the \"\n\"root/main kernel as its value of ``parent`` property of annotation points\"\n\" to class not implementing ``RegistrarBean``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:202\nmsgid \"\"\n\"To configure value of ``field1`` in instance of ``bean1`` and ``field2`` \"\n\"in instance of ``bean2`` in DSL (for more information about DSL format \"\n\"please check section ``DSL file format`` of the ``Admin Guide``) you \"\n\"would need to use following entry in the config file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:213\nmsgid \"\"\n\"As you can see, this resulted in the ``bean2`` configuration being on the\"\n\" same level as ``bean1`` configuration.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:216\nmsgid \"Calling kernel methods\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:219\nmsgid \"As a class\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:221\nmsgid \"\"\n\"To register a bean as a class, you need to have an instance of a Tigase \"\n\"Kernel execute it’s ``registerBean()`` method passing your ``Bean1`` \"\n\"class.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:229\nmsgid \"\"\n\"To be able to use this method you will need to annotate ``Bean1`` class \"\n\"with ``@Bean`` annotation and provide a bean name which will be used for \"\n\"registration of the bean.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:232\nmsgid \"As a factory\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:234\nmsgid \"\"\n\"To do this you need to have an instance of a Tigase Kernel execute it’s \"\n\"``registerBean()`` method passing your bean ``Bean5`` class.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:241\nmsgid \"As an instance\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:243\nmsgid \"\"\n\"For this you need to have an instance of a Tigase Kernel execute it’s \"\n\"``registerBean()`` method passing your bean ``Bean41`` class instance.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:252\nmsgid \"\"\n\"Beans registered as an instance will not inject dependencies. As well \"\n\"this bean instances will not be configured by provided bean \"\n\"configurators.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:255\nmsgid \"Using config file *(optional)*\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:257\nmsgid \"\"\n\"If there is registered a bean ``defaultBeanConfigurator`` which supports \"\n\"registration in the config file, it is possible to do so. By default \"\n\"Tigase XMPP Server uses ``DSLBeanConfigurator`` which provides support \"\n\"for that and registration is possible in the config file in DSL. As \"\n\"registration of beans using a config file is part of the admin of the \"\n\"Tigase XMPP Server tasks, it is described in explained in the Admin Guide\"\n\" in subsection ``Defining bean`` of ``DSL file format`` section.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:261\nmsgid \"\"\n\"This way allows admin to select different class for a bean. This option \"\n\"should be used to provide alternative implementations to the default \"\n\"beans which should be registered using annotations.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:265\nmsgid \"\"\n\"Works only if bean registered as ``defaultBeanConfigurator`` supports \"\n\"this feature. By default Tigase XMPP Server uses ``DSLBeanConfigurator`` \"\n\"which provides support for that.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:268\nmsgid \"Defining dependencies\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:270\nmsgid \"All dependencies are defined with annotations:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:294\nmsgid \"\"\n\"Kernel automatically determines type of a required beans based on field \"\n\"type. As a result, there is no need to specify the type of a bean in case\"\n\" of ``bean4`` field.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:296\nmsgid \"\"\n\"When there are more than one bean instances matching required dependency \"\n\"fields, the type needs to be an array or collection. If kernel is unable \"\n\"to resolve dependencies, it will throw an exception unless ``@Inject`` \"\n\"annotation has ``nullAllowed`` set to ``true``. This is useful to make \"\n\"some dependencies optional. To help kernel select a single bean instance \"\n\"when more that one bean will match field dependency, you may set name of \"\n\"a required bean as shown in annotation to field ``bean3``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:298\nmsgid \"\"\n\"Dependencies are inserted using getters/setters if those methods exist, \"\n\"otherwise they are inserted directly to the fields. Thanks to usage of \"\n\"setters, it is possible to detect a change of dependency instance and \"\n\"react as required, i.e. clear internal cache.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:302\nmsgid \"\"\n\"Kernel is resolving dependencies during injection only using beans \"\n\"visible in its scope. This makes it unable to inject an instance of a \"\n\"class which is not registered in the same kernel as a bean or not visible\"\n\" in this kernel scope (see :ref:`Scope and visibility<kernelScope>`).\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:306\nmsgid \"\"\n\"If two beans have bidirectional dependencies, then it is required to \"\n\"allow at least one of them be ``null`` (make it an optional dependency). \"\n\"In other case it will create circular dependency which cannot be \"\n\"satisfied and kernel will throw exceptions at runtime.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:309\nmsgid \"Nested kernels and exported beans\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:311\nmsgid \"\"\n\"Tigase Kernel allows the usage of nested kernels. This allows you to \"\n\"create complex applications and maintain proper separation and visibility\"\n\" of beans in scopes as each module (subkernel) may work within its own \"\n\"scope.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:313\nmsgid \"Subkernels may be created using one of two ways:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:316\nmsgid \"Manual registration of new a new kernel\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:318\nmsgid \"\"\n\"You can create an instance of a new kernel and register it as a bean \"\n\"within the parent kernel.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:329\nmsgid \"Usage of RegistrarBean\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:331\nmsgid \"\"\n\"You may create a bean which implements the ``RegistrarBean`` interfaces. \"\n\"For all beans that implement this interface, subkernels are created. You \"\n\"can access this new kernel within an instance of ``RegistrarBean`` class \"\n\"as ``register(Kernel)`` and ``unregister(Kernel)`` methods are called \"\n\"once the ``RegistrarBean`` instance is created or destroyed.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:333\nmsgid \"\"\n\"There is also an interface named ``RegistrarBeanWithDefaultBeanClass``. \"\n\"This interface is very useful if you want or need to create a bean which \"\n\"would allow you to configure many subbeans which will have the same class\"\n\" but different names and you do not know names of those beans before \"\n\"configuration will be set. All you need to do is to implement this \"\n\"interface and in method ``getDefaultBeanClass()`` return class which \"\n\"should be used for all subbeans defined in configuration for which there \"\n\"will be no class configured.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:335\nmsgid \"\"\n\"As an example of such use case is ``dataSource`` bean, which allows \"\n\"administrator to easily configure many data sources without passing their\"\n\" class names, ie.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:345\nmsgid \"\"\n\"With this config we just defined 3 beans named ``default``, ``domain1`` \"\n\"and ``domain2``. All of those beans will be instances of a class returned\"\n\" by a ``getDefaultBeanClass()`` method of ``dataSource`` bean.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:350\nmsgid \"Scope and visibility\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:352\nmsgid \"\"\n\"Beans that are registered within a parent kernel are visible to beans \"\n\"registered within the first level of child kernels. However, **beans \"\n\"registered within child kernels are not available to beans registered in \"\n\"a parent kernel** with the exception that they are visible to bean that \"\n\"created the subkernel (an instance of ``RegistrarBean``).\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:354\nmsgid \"\"\n\"It is possible to export beans so they can be visible outside the first \"\n\"level of child kernels.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:356\nmsgid \"\"\n\"To do so, you need to mark the bean as exportable using annotations or by\"\n\" calling the ``exportable()`` method.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:358\nmsgid \"**Using annotation.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:366\nmsgid \"*Calling* ``exportable()``.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:373\nmsgid \"Dependency graph\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:375\nmsgid \"\"\n\"Kernel allows the creation of a dependency graph. The following lines \"\n\"will generate it in a format supported by `Graphviz \"\n\"<http://www.graphviz.org>`__.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:383\nmsgid \"Configuration\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:385\nmsgid \"\"\n\"The kernel core does not provide any way to configure created beans. Do \"\n\"do that you need to use the ``DSLBeanConfigurator`` class by providing \"\n\"its instance within configuration and registration of this instances \"\n\"within kernel.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:400\nmsgid \"DSL and kernel scopes\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:402\nmsgid \"\"\n\"DSL is a structure based format explained in `Tigase XMPP Server \"\n\"Administration Guide: DSL file format section <http://docs.tigase.org\"\n\"/tigase-server/snapshot/Administration_Guide/html/#dslConfig>`__. **It is\"\n\" important to know that kernel and beans structure have an impact on what\"\n\" the configuration in DSL will look like.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:404\nmsgid \"**Example kernel and beans classes.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:458\nmsgid \"Following classes will produce following structure of beans:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:460\nmsgid \"\\\"bean1\\\" of class ``Bean1``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:462\nmsgid \"\\\"bean1_1\\\" of class ``Bean11``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:464\nmsgid \"\\\"bean1_2\\\" of class ``Bean12``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:466\nmsgid \"\\\"bean4\\\" of class ``Bean2``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:468\nmsgid \"\\\"bean3\\\" of class ``Bean3``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:472\nmsgid \"\"\n\"This is a simplified structure, the actual structure is slightly more \"\n\"complex. However. this version makes it easier to explain structure of \"\n\"beans and impact on configuration file structure.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:476\nmsgid \"\"\n\"Even though ``Bean2`` was annotated with name ``bean2``, it was \"\n\"registered with name ``bean4`` as this name was passed during \"\n\"registration of a bean in ``main()`` method.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:480\nmsgid \"\"\n\"``Bean12`` was registered under name ``bean1_2`` as subbean of ``Bean1`` \"\n\"as a result of annotation of ``Bean12``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:482\nmsgid \"\"\n\"As mentioned DSL file structure depends on structure of beans, a file to \"\n\"set a config field in each bean to bean name should look like that:\"\nmsgstr \"\"\n\n#~ msgid \"\"\n#~ \"registration of additional beans based \"\n#~ \"on annotations *(optionally using registered\"\n#~ \" class implementing ``BeanConfigurator`` as \"\n#~ \"``defaultBeanConfigurator``)*\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"configuration of a bean *(optionally \"\n#~ \"thru registered class implementing \"\n#~ \"``BeanConfigurator`` as ``defaultBeanConfigurator``)*\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"be ``service`` as this name is \"\n#~ \"used by Tigase Kernel internally when\"\n#~ \" \\\\`RegistrarBean`s are in use (see \"\n#~ \"`RegistrarBean <#registrarBean>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"Parameterless constructor is a required \"\n#~ \"as it will be used by kernel \"\n#~ \"to create an instance of the bean,\"\n#~ \" see `bean lifecycle <#beanLifecycle>`__.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"At any point in time bean may \"\n#~ \"be reconfigured by default bean \"\n#~ \"configurator (instance of ``BeanConfigurator``) \"\n#~ \"registered in the kernel. This will \"\n#~ \"happen in the same way as it \"\n#~ \"described in `Configuring a bean \"\n#~ \"<#beanConfiguration>`__ in **Creating instace \"\n#~ \"of a bean** section.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"It may happen, that due to \"\n#~ \"reconfiguration or registration/unregistration or\"\n#~ \" activation/deactivation of some other \"\n#~ \"beans dependencies of a bean will \"\n#~ \"change. As a result, Tigase Kernel \"\n#~ \"will inject new dependencies as \"\n#~ \"described in `Injecting dependencies \"\n#~ \"<#beanInjectingDependencies>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"Kernel is resolving dependencies during \"\n#~ \"injection only using beans visible in\"\n#~ \" its scope. This makes it unable \"\n#~ \"to inject an instance of a class\"\n#~ \" which is not registered in the \"\n#~ \"same kernel as a bean or not \"\n#~ \"visible in this kernel scope (see \"\n#~ \"`Scope and visibility <#kernelScope>`__).\"\n#~ msgstr \"\"\n\n#~ msgid \"**Calling ``exportable()``.**\"\n#~ msgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Development/Using_Maven.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-05-27 12:30-0700\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Using_Maven.rst:2\nmsgid \"Using Maven\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:4\nmsgid \"Documents Describing Maven Use with the Tigase Projects\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:7\nmsgid \"Setting up Maven in Windows\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:9\nmsgid \"\"\n\"Here at Tigase, we employ Apache Maven to download latest builds, compile\"\n\" codes for export, and check for errors in the code during build. This \"\n\"guide will go over installing and running Maven from a Windows operating \"\n\"environment. We will consider windows versions 7, 8, and 8.1 for this \"\n\"guide. Because Maven does not come with an installer, there is a manual \"\n\"install process which might be a bit daunting for the new user, but setup\"\n\" and use is fairly simple.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:12\nmsgid \"Requirements\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:14\nmsgid \"\"\n\"Maven requires Java Development Kit (JDK) 6 or later. As Tigase requires \"\n\"the latest JDK to run, that will do for our purposes. If you haven’t \"\n\"installed it yet, download the installer from `this website \"\n\"<http://www.oracle.com/technetwork/java/javase/downloads/index.html>`__. \"\n\"Once you install JDK and restart your machine, be sure that you have the \"\n\"**JAVA_HOME** variable entered into Environment Variables so calls to \"\n\"Java will work from the command line.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:16\nmsgid \"\"\n\"Download the Maven package from `here \"\n\"<https://maven.apache.org/download.cgi>`__ and unpack it into a directory\"\n\" of your choice. For this guide we will use ``C:\\\\Maven\\\\`` .\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:19\nmsgid \"Setting up Environment Variables\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:21\nmsgid \"\"\n\"The Environment Variables panel is brought up from the Control Panel by \"\n\"clicking **System and Security** > **System** > **Advanced System \"\n\"Settings**. Now click the |Environment Variables| button at the bottom of\"\n\" the panel and the Environment Variables panel will show.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:70\nmsgid \"Environment Variables\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:23\nmsgid \"\"\n\"**IMPORTANT NOTICE: CHANGING THESE SETTINGS CAN BREAK OTHER FUNCTIONS IN \"\n\"THE OPERATING SYSTEM. DO NOT FOLLOW THIS GUIDE IF YOU DO NOT KNOW WHAT \"\n\"YOU ARE DOING!**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:25\nmsgid \"|Env Panel|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:71\nmsgid \"Env Panel\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:27\nmsgid \"\"\n\"We need to first add two variable paths to the System variables to \"\n\"account for Maven’s install location. As there are some programs that \"\n\"look for M2_HOME, and others that look for MAVEN_HOME, it’s easier to \"\n\"just add both and have all the bases covered.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:29\nmsgid \"Click on New…​\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:31\nmsgid \"|Env New|\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:72\nmsgid \"Env New\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:33\nmsgid \"\"\n\"For the Name, use M2_HOME, and for the variable enter the path to maven, \"\n\"which in this case is\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:39\nmsgid \"\"\n\"Create another new variable with the MAVEN_HOME name and add the same \"\n\"directory. **These variable values just point to where you have unpacked \"\n\"maven, so they do not have to be in the C directory.**\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:41\nmsgid \"\"\n\"Go down to the system variables dialog and select Path, then click on \"\n\"Edit. The Path variables are separated by semicolons, find the end of the\"\n\" Variable value string, and add the following after the last entry:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:47\n#, python-format\nmsgid \"\"\n\"We have added two variables using the %% wildcards surrounding our \"\n\"Variable names from earlier.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:50\nmsgid \"Testing Maven\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:52\nmsgid \"\"\n\"Now we must test the command line to be sure everything installed \"\n\"correctly. Bring up the command line either by typing ``cmd`` in search, \"\n\"or navigating the start menu.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:54\nmsgid \"\"\n\"From the prompt, you do not need to change directory as setting Path \"\n\"allows you to reference it. Type the following command: ``mvn -v``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:56\nmsgid \"something like this should show up\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:68\nmsgid \"\"\n\"If you see this message, success! You have finished installation and are \"\n\"ready to use Maven! If not, go back on your settings and insure that JDK \"\n\"is installed, and the JAVA_HOME, M2_HOME, and MAVEN_HOME variables are \"\n\"set properly.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:75\nmsgid \"A Very Short Maven Guide\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:77\nmsgid \"\"\n\"If you don’t use `Maven <http://maven.apache.org/>`__ at all or use it \"\n\"once a year you may find the document a useful maven commands reminder:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:80\nmsgid \"Snapshot Compilation and Snapshot Package Generation\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:82\nmsgid \"``mvn compile`` - compilation of the snapshot package\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:84\nmsgid \"``mvn package`` - create snapshot jar file\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:86\nmsgid \"``mvn install`` - install in local repository snapshot jar file\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:88\nmsgid \"``mvn deploy`` - deploy to the remote repository snapshot jar file\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:91\nmsgid \"Release Compilation, Generation\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:93\nmsgid \"``mvn release:prepare`` prepare the project for a new version release\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:95\nmsgid \"``mvn release:perform`` execute new version release generation\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:98\nmsgid \"Generating tar.gz, tar.bz2 File With Sources Only\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:100\nmsgid \"``mvn -DdescriptorId=src assembly:assembly``\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:102\nmsgid \"\"\n\"Any of these commands will work when your commandline is in a directory \"\n\"with a pom.xml file. This file will instruct what Maven will do.\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:105\nmsgid \"Profiles\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:107\nmsgid \"\"\n\"Maven uses profiles with the -P switch to tell what to compile and build.\"\n\" Tigase uses two different profiles:\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:109\nmsgid \"-Pdist - creates distribution archives\"\nmsgstr \"\"\n\n#: ../../Tigase_Development/Using_Maven.rst:111\nmsgid \"-Pdoc - creates documentation\"\nmsgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/Tigase_Development/index.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2023-02-16 15:01-0800\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.11.0\\n\"\n\n#: ../../Tigase_Development/index.rst:3\nmsgid \"Development Guide\"\nmsgstr \"\"\n\n#~ msgid \"Tigase Development Guide -Version 8.3.0\"\n#~ msgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/pl/LC_MESSAGES/index.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2023-02-22 15:39-0800\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.11.0\\n\"\n\n#: ../../index.rst:68\nmsgid \"Administration Guide\"\nmsgstr \"\"\n\n#: ../../index.rst:75\nmsgid \"Development Guide\"\nmsgstr \"\"\n\n#: ../../index.rst:4\nmsgid \"Tigase Documentation\"\nmsgstr \"\"\n\n#: ../../index.rst:7\nmsgid \"Tigase Projects documentations\"\nmsgstr \"\"\n\n#: ../../index.rst:10\nmsgid \"Server Components\"\nmsgstr \"\"\n\n#: ../../index.rst:12\nmsgid \"\"\n\"`Tigase MIX <../../projects/tigase-tigase-mix/en/latest/>`__ Next \"\n\"generation groupchat protocol\"\nmsgstr \"\"\n\n#: ../../index.rst:13\nmsgid \"\"\n\"`Tigase MUC <../../projects/tigase-tigase-muc/en/latest/>`__ Current \"\n\"generation groupchat protocol\"\nmsgstr \"\"\n\n#: ../../index.rst:14\nmsgid \"\"\n\"`Tigase Spam <../../projects/tigase-tigase-spam/en/latest/>`__ Project \"\n\"aiming at fighting spam\"\nmsgstr \"\"\n\n#: ../../index.rst:15\nmsgid \"\"\n\"`Tigase HTTP API <../../projects/tigase-tigase-http-api/en/latest/>`__ \"\n\"Operate and manage Tigase via HTTP... and more!\"\nmsgstr \"\"\n\n#: ../../index.rst:16\nmsgid \"\"\n\"`Tigase PubSub <../../projects/tigase-tigase-pubsub/en/latest/>`__ \"\n\"Implementation of XEP-0045: PubSub protocol\"\nmsgstr \"\"\n\n#: ../../index.rst:17\nmsgid \"\"\n\"`Tigase Message Archiving <../../projects/tigase-tigase-message-\"\n\"archiving/en/latest/>`__ Implementation of XEP-0136: PMessage archiving\"\nmsgstr \"\"\n\n#: ../../index.rst:18\nmsgid \"\"\n\"`Tigase Unified Archive <../../projects/tigase-tigase-unified-\"\n\"archive/en/latest/>`__ The extended version of Tigase Message Archiving \"\n\"Component\"\nmsgstr \"\"\n\n#: ../../index.rst:19\nmsgid \"\"\n\"`Tigase Socks5 <../../projects/tigase-socks5/en/latest/>`__ \"\n\"Implementation of XEP-0065 SOCKS5 Bytestreams\"\nmsgstr \"\"\n\n#: ../../index.rst:20\nmsgid \"\"\n\"`Tigase Push <../../projects/tigase-tigase-push-2/en/latest/>`__ A \"\n\"gateway between Push Notification services and XMPP servers\"\nmsgstr \"\"\n\n#: ../../index.rst:21\nmsgid \"\"\n\"`Tigase ACS <../../projects/tigase-tigase-acs-2/en/latest/>`__ General \"\n\"purpose, commercial clustering strategy\"\nmsgstr \"\"\n\n#: ../../index.rst:22\nmsgid \"\"\n\"`Tigase Auditlog <../../projects/tigase-tigase-auditlog-2/en/latest/>`__ \"\n\"An implementation of Audit Log pattern functionality to log important \"\n\"events\"\nmsgstr \"\"\n\n#: ../../index.rst:23\nmsgid \"\"\n\"`Tigase Workgroup Queues <../../projects/tigase-tigase-workgroup-\"\n\"queues/en/latest/>`__ Implementation of XEP-0142: Workgroup Queues\"\nmsgstr \"\"\n\n#: ../../index.rst:24\nmsgid \"\"\n\"`Tigase Databse Migrator <../../projects/tigase-database-\"\n\"migrator/en/latest/>`__ Component allowing migration of different types \"\n\"of data from various XMPP servers\"\nmsgstr \"\"\n\n#: ../../index.rst:25\nmsgid \"\"\n\"`Tigase Extras <../../projects/tigase-tigase-extras/en/latest/>`__ AWS \"\n\"provides you with support for additional features and integrations with \"\n\"Amazon AWS.\"\nmsgstr \"\"\n\n#: ../../index.rst:28\nmsgid \"XMPP Clients\"\nmsgstr \"\"\n\n#: ../../index.rst:30\nmsgid \"`SiskinIM <../../projects/tigase-siskin-im/en/latest/>`__ iOS XMPP Client\"\nmsgstr \"\"\n\n#: ../../index.rst:32\nmsgid \"\"\n\"`BeagleIM <../../projects/tigase-beagle-im/en/latest/>`__ macOS XMPP \"\n\"Client\"\nmsgstr \"\"\n\n#: ../../index.rst:34\nmsgid \"`StorkIM <../../projects/tigase-stork/en/latest/>`__ android XMPP Client\"\nmsgstr \"\"\n\n#: ../../index.rst:37\nmsgid \"XMPP Libraries\"\nmsgstr \"\"\n\n#: ../../index.rst:39\nmsgid \"\"\n\"`Martin <../../projects/tigase-tigase-swift/en/latest/>`__ Swift Lang \"\n\"XMPP Library\"\nmsgstr \"\"\n\n#: ../../index.rst:41\nmsgid \"\"\n\"`Halcyon <../../projects/tigase-halcyon/en/latest/>`__ a multiplatform, \"\n\"extensible XMPP client library\"\nmsgstr \"\"\n\n#: ../../index.rst:43\nmsgid \"\"\n\"`Tigase XML Tools <../../projects/tigase-xmltools/en/latest/>`__ A \"\n\"library providing support for working with XML documents\"\nmsgstr \"\"\n\n#: ../../index.rst:45\nmsgid \"\"\n\"`Tigase JaXMPP <../../projects/tigase-jaxmpp/en/latest/>`__ an extensible\"\n\" XMPP client library\"\nmsgstr \"\"\n\n#: ../../index.rst:49\nmsgid \"Javadoc\"\nmsgstr \"\"\n\n#: ../../index.rst:51\nmsgid \"\"\n\"`Tigase Mongodb <../../projects/tigase-tigase-mongodb/en/latest/>`__ Java\"\n\" documentation for tigase mangodb\"\nmsgstr \"\"\n\n#: ../../index.rst:53\nmsgid \"\"\n\"`Tigase Utils <../../projects/tigase-tigase-utils/en/latest/>`__ Java \"\n\"documentation for tigase utils\"\nmsgstr \"\"\n\n#: ../../index.rst:56\nmsgid \"Miscellaneous\"\nmsgstr \"\"\n\n#: ../../index.rst:58\nmsgid \"\"\n\"`Tigase TTS NG <../../projects/tigase-tigase-tts-ng/en/latest/>`__ \"\n\"Project intended to run automated funtionality tests\"\nmsgstr \"\"\n\n#: ../../index.rst:60\nmsgid \"\"\n\"`Tigase XEPs <https://xeps.tigase.net/>`__ XMPP extensions that we Tigase\"\n\" is working on with the goal to transition them to full, standard XMPP \"\n\"extensions.\"\nmsgstr \"\"\n\n#: ../../index.rst:64\nmsgid \"Legacy documentation website\"\nmsgstr \"\"\n\n#: ../../index.rst:66\nmsgid \"\"\n\"`Legacy documentation website <https://docs-legacy.tigase.net/>`__ Our \"\n\"legacy documentation website which contains documentation for older \"\n\"versions of componetns and applications\"\nmsgstr \"\"\n\n#~ msgid \"\"\n#~ \"`SiskinIM <../../projects/tigase-siskinim/en/latest/>`__\"\n#~ \" iOS XMPP Client\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`SiskinIM <../../projects/tigase-siskin-\"\n#~ \"im/en/latest/>`__ iOS XMPP Client\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Tigase Swift <../../projects/tigase-tigase-\"\n#~ \"swift/en/latest/>`__ Swift Lang XMPP Library\"\n#~ msgstr \"\"\n\n#~ msgid \"Tigase XMPP Server Distribution Administration Guide\"\n#~ msgstr \"\"\n\n#~ msgid \"Tigase Development Guide\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Tigase Unified Archive <../../projects/projects\"\n#~ \"/tigase-tigase-unified-archive/en/latest/>`__ The\"\n#~ \" extended version of Tigase Message \"\n#~ \"Archiving Component\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Tigase ACS <../../projects/projects/tigase-\"\n#~ \"tigase-acs-2/en/latest/>`__ General purpose, \"\n#~ \"commercial clustering strategy\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Tigase Auditlog <../../projects/projects/tigase-\"\n#~ \"tigase-auditlog-2/en/latest/>`__ An implementation \"\n#~ \"of Audit Log pattern functionality to\"\n#~ \" log important events\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Tigase Workgroup Queues <../../projects/projects\"\n#~ \"/tigase-tigase-workgroup-queues/en/latest/>`__ \"\n#~ \"Implementation of XEP-0142: Workgroup Queues\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`SiskinIM and BeagleIM <../../projects/tigase-\"\n#~ \"siskin-im/en/latest/>`__ iOS XMPP Client\"\n#~ msgstr \"\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Administration/Components/_Components.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: 2022-09-07 17:14+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/components/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Administration/Components/Components.inc:4\nmsgid \"Components\"\nmsgstr \"组件\"\n\n#: ../../Tigase_Administration/Components/Components.inc:6\nmsgid \"\"\n\"The only step is to tell the server what components to load, how to name \"\n\"them and optionally give some extra parameters. To do so open the \"\n\"``config.tdsl`` file you use in your installation.\"\nmsgstr \"唯一的步骤是告诉服务器要加载哪些组件，如何命名它们并提供一些可选的额外参数。为此，请打开您在安装中使用的 ``config.tdsl`` 文件。\"\n\n#: ../../Tigase_Administration/Components/Components.inc:8\nmsgid \"\"\n\"Let’s say you want to just add PubSub for now. All you need to do is add \"\n\"the following to the properties file:\"\nmsgstr \"假设您现在只想添加 PubSub。您需要做的就是将以下内容添加到属性文件中：\"\n\n#: ../../Tigase_Administration/Components/Components.inc:14\nmsgid \"\"\n\"Normally, this is not necessary since pubsub is loaded by default, \"\n\"however this is just an example of loading a class with the DSL format.\"\nmsgstr \"通常，这不是必需的，因为默认情况下会加载 pubsub，但这只是加载具有 DSL 格式的类的示例。\"\n\n#: ../../Tigase_Administration/Components/Components.inc:20\nmsgid \"\"\n\"As you can see, we can customize the name of a component in the \"\n\"deceleration, here we are using pubsub-priv.\"\nmsgstr \"如您所见，我们可以在减速中自定义一个组件的名称，这里我们使用的是pubsub-priv。\"\n\n#: ../../Tigase_Administration/Components/Components.inc:22\nmsgid \"\"\n\"Although this may be rare, it allows for wide compatibility and platform \"\n\"stability.\"\nmsgstr \"虽然这可能很少见，但它允许广泛的兼容性和平台稳定性。\"\n\n#: ../../Tigase_Administration/Components/Components.inc:24\nmsgid \"\"\n\"Normally, however we want to load few different components like PubSub, \"\n\"MUC, MSN Transport and so on…​. Therefore instead of the above second \"\n\"PubSub we can load the MUC component:\"\nmsgstr \"\"\n\"通常，我们希望加载几个不同的组件，例如 PubSub，MUC，MSN Transport 等等……。因此，我们可以加载 MUC \"\n\"组件，而不是上面的第二个 PubSub：\"\n\n#: ../../Tigase_Administration/Components/Components.inc:31\nmsgid \"Changes to the ``config.tdsl`` file will take effect upon server restart.\"\nmsgstr \"对 ``config.tdsl`` 文件的更改将在服务器重新启动时生效。\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:2\nmsgid \"Advanced Message Processing - AMP XEP-0079\"\nmsgstr \"高级消息处理 - AMP XEP-0079\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:4\nmsgid \"\"\n\"Tigase server offers support for `XEP-0079: Advanced Message Processing \"\n\"<http://xmpp.org/extensions/xep-0079.html>`__ (often abbreviated to AMP).\"\nmsgstr \"\"\n\"Tigase 服务器提供对 `XEP-0079: Advanced Message Processing \"\n\"<http://xmpp.org/extensions/xep-0079.html>`__ (通常缩写为 AMP）的支持。\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:6\nmsgid \"\"\n\"It is enabled by default but there are several configuration options that\"\n\" you may tweak.\"\nmsgstr \"它默认启用，但您可以调整几个配置选项。\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:8\nmsgid \"\"\n\"Configuration of AMP is not very complex, but as it is implemented as a \"\n\"component in the Tigase server it does needs a few settings to get it \"\n\"right.\"\nmsgstr \"AMP 的配置不是很复杂，但由于它是作为 Tigase 服务器中的一个组件实现的，因此确实需要一些设置才能使其正确。\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:10\nmsgid \"\"\n\"Here is a first, brief overview of the AMP configuration and later \"\n\"detailed explanation of each parameter.\"\nmsgstr \"这是 AMP 配置的第一个简要概述，随后是每个参数的详细说明。\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:24\nmsgid \"First of all: plugins\"\nmsgstr \"首先：插件\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:26\nmsgid \"\"\n\"Even though the whole functionality is implemented inside the component \"\n\"you need a way to forward messages with ``AMP`` payload to that \"\n\"component. This is what the ``amp`` plugin does. The ``amp`` plugin \"\n\"intercepts all ``<message/>`` packets even without AMP payload, \"\n\"redirecting some of the to the ``AMP`` component and others processing in\"\n\" a standard way. Therefore you no longer need ``message`` plugin or \"\n\"``msgoffline`` plugin. Those are all functions are offered by the ``amp``\"\n\" plugin now. Hence you have to switch ``message`` and ``msgoffline`` \"\n\"plugins off (the ``amp`` plugin is loaded by default):\"\nmsgstr \"\"\n\"即使整个功能是在组件内部实现的，您也需要一种将带有 ``AMP`` 有效负载的消息转发到该组件的方法。这就是 ``amp`` 插件的作用。 \"\n\"``amp`` 插件拦截所有 ``<message/>`` 数据包，即使没有 AMP 有效负载，将其中一些重定向到 ``AMP`` \"\n\"组件，而其他以标准方式处理。因此，您不再需要 ``message`` 插件或 ``msgoffline`` 插件。这些都是 ``amp`` \"\n\"插件现在提供的所有功能。因此，您必须关闭 ``message`` 和 ``msgoffline`` 插件（默认情况下会加载 ``amp`` \"\n\"插件）：\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:36\nmsgid \"\"\n\"The ``amp`` plugin needs to know where to forward all the ``AMP`` \"\n\"packets. By default plugin uses hostname of the given machine as this is \"\n\"true to the most installations. However, this is configured by the last \"\n\"line of the example configuration, which forwards all packets to the \"\n\"address ``amp@your-domain.tld``:\"\nmsgstr \"\"\n\"``amp`` 插件需要知道将所有 ``amp`` \"\n\"数据包转发到哪里。默认情况下，插件使用给定机器的主机名，因为大多数安装都是如此。但是，这是由示例配置的最后一行配置的，它将所有数据包转发到地址 \"\n\"``amp@your-domain.tld``:\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:47\nmsgid \"Secondly: component\"\nmsgstr \"第二：组件\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:49\nmsgid \"By default Tigase loads the component with the standard name ``amp``\"\nmsgstr \"默认情况下，Tigase 使用标准名称 ``amp`` 加载组件\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:52\nmsgid \"Optional parameters\"\nmsgstr \"可选参数\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:54\nmsgid \"\"\n\"There is also one parameter shared between the component and the plugin. \"\n\"Connection to the database where offline messages are stored. The AMP \"\n\"component has a dedicated schema for storing offline messages designed \"\n\"for a high traffic and high load installations. It does not use \"\n\"``UserRepository`` for storing messages.\"\nmsgstr \"\"\n\"组件和插件之间还共享一个参数。可连接到存储离线消息的数据库。 AMP 组件具有用于存储离线消息的专用架构，专为高流量和高负载安装而设计。它不使用 \"\n\"``UserRepository`` 来存储消息。\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:56\nmsgid \"\"\n\"By default the same physical database as for ``UserRepository`` is used \"\n\"but you can change it and store messages in a completely separate \"\n\"location to reduce performance degradation of rest of the system. You can\"\n\" set a database connection string using following property:\"\nmsgstr \"\"\n\"默认情况下，使用与 ``UserRepository`` \"\n\"相同的物理数据库，但您可以更改它并将消息存储在完全独立的位置，以减少系统其余部分性能的下降。您可以使用以下属性设置数据库连接字符串：\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:66\nmsgid \"\"\n\"The `XEP-0079 <http://xmpp.org/extensions/xep-0079.html>`__ specification\"\n\" has a `Section 9. - Security Considerations \"\n\"<http://xmpp.org/extensions/xep-0079.html#security>`__. As it describes, \"\n\"in some cases the AMP protocol can be used to reveal user’s presence \"\n\"information by other users who are not authorized for presence updates. \"\n\"There are a few possible ways to prevent this.\"\nmsgstr \"\"\n\"`XEP-0079 <http://xmpp.org/extensions/xep-0079.html>`__ 规范有一个 `Section 9.\"\n\" - Security Considerations \"\n\"<http://xmpp.org/extensions/xep-0079.html#security>`__。 \"\n\"正如其所描述的，在某些情况下，AMP 协议可用于向未授权进行状态更新的其他用户显示用户的状态信息。有几种可能的方法可以防止这种情况。\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:68\nmsgid \"\"\n\"Tigase’s implementation offers 3 modes to handle ``AMP`` requests to \"\n\"prevent revealing user’s status to non-authorized users:\"\nmsgstr \"Tigase 的实现提供了 3 种模式来处理 ``AMP`` 请求，以防止向非授权用户泄露用户的状态：\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:74\nmsgid \"\"\n\"In this mode the server performs strict checking. The ``AMP`` \"\n\"specification is fully handled. This however involves roster loading for \"\n\"each offline user, hence it may impact the service performance. It may \"\n\"not be feasible or possible to run in this mode for services under a high\"\n\" load with lots of AMP messages.\"\nmsgstr \"\"\n\"在这种模式下，服务器执行严格的检查。 ``AMP`` 规范已完全处理。然而，这涉及到每个离线用户的名册加载，因此它可能会影响服务性能。对于具有大量\"\n\" AMP 消息的高负载服务，在此模式下运行可能不可行或不可能。\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:76\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:86\nmsgid \"In the XEP this mode is described in the following way:\"\nmsgstr \"在 XEP 中，这种模式是这样描述的：\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:78\nmsgid \"\"\n\"*Accept the relevant condition only if the sender is authorized to \"\n\"receive the receiver’s presence, as a result of which the server MUST \"\n\"reply with a <not-acceptable/> error condition if the sender is not so \"\n\"authorized; this is the RECOMMENDED behavior. This is also the default in\"\n\" Tigase.*\"\nmsgstr \"\"\n\"*仅当发送者被授权接收接收者的存在时才接受相关条件，因此如果发送者未被授权，服务器必须回复 <not-acceptable/> \"\n\"错误条件；这是推荐的行为。这也是 Tigase 中的默认设置。*\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:84\nmsgid \"\"\n\"Dummy checking is performed efficiently by just returning an error \"\n\"response every time there is a chance that the default action may reveal \"\n\"user status without looking into the user’s roster. This does not affect \"\n\"performance but it does impact the ``AMP`` compliance.\"\nmsgstr \"\"\n\"只需返回错误响应即可有效地执行虚拟检查，这样每次默认操作可能会在不查看用户名册的情况下显示用户状态。这不会影响性能，但会影响 ``AMP`` \"\n\"合规性。\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:88\nmsgid \"\"\n\"*Accept the relevant condition only if the action is \\\"drop\\\", as a \"\n\"result of which the server MUST reply with a <not-acceptable/> error \"\n\"condition if the action is \\\"alert\\\", \\\"error\\\", or \\\"notify\\\"; this is \"\n\"slightly less restrictive but still unnecessarily restricts the \"\n\"functionality of the system, so is NOT RECOMMENDED.*\"\nmsgstr \"\"\n\"*仅当动作为 \\\"drop\\\" 时才接受相关条件，因此如果动作为 \\\"alert\\\", \\\"error\\\", 或 \\\"notify\\\"; \"\n\"服务器必须回复<not-acceptable/>错误条件；这限制性稍小，但仍不必要地限制了系统的功能，因此不推荐。*\"\n\n#: ../../Tigase_Administration/Components/Advanced_Message_Processing.inc:90\nmsgid \"\"\n\"It does not do any checking. It acts like all users are authorized to \"\n\"receive notifications, even if it may reveal user status to unauthorized \"\n\"users. It does not impact the server performance and it offers full AMP \"\n\"compliance.\"\nmsgstr \"它不做任何检查。它的作用就像所有用户都有权接收通知，即使它可能会向未经授权的用户透露用户状态。但它不会影响服务器性能，并提供完整的 AMP 合规性。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Monitoring.inc:4\nmsgid \"Server Monitoring\"\nmsgstr \"服务器监控\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Monitoring.inc:6\nmsgid \"\"\n\"All the documentation and resources related to the Tigase server \"\n\"monitoring.\"\nmsgstr \"与 Tigase 服务器监控相关的所有文档和资源。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Monitoring.inc:8\nmsgid \"\"\n\":ref:`Setting up Remote Monitoring in the Server<Setting-Up-Remote-\"\n\"Monitoring-in-the-Server>`\"\nmsgstr \":ref:`在服务器中设置远程监控<Setting-Up-Remote-Monitoring-in-the-Server>`\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Monitoring.inc:10\nmsgid \"\"\n\":ref:`Statistics Logger Configuration<Configuration-of-statistics-\"\n\"loggers>`\"\nmsgstr \":ref:`在服务器中设置远程监控<Setting-Up-Remote-Monitoring-in-the-Server>`\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Monitoring.inc:12\nmsgid \"\"\n\":ref:`Retrieving Statistics from the Server<Retrieving-statistics-from-\"\n\"the-server>`\"\nmsgstr \":ref:`从服务器检索统计信息<Retrieving-statistics-from-the-server>`\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Monitoring.inc:14\nmsgid \":ref:`Monitor Component<Monitor-Component>`\"\nmsgstr \":ref:`监控组件<Monitor-Component>`\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:4\nmsgid \"Setting Up Remote Monitoring in the Server\"\nmsgstr \"在服务器中设置远程监控\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:6\nmsgid \"\"\n\"Tigase server can be remotely monitored over following protocols: \"\n\"``JMX/RMI``, ``SNMP`` and ``HTTP``. Even though ``JMX`` offers the \"\n\"biggest control and visibility to the server states, all of the \"\n\"monitoring services give the same basic set of the server statistics:\"\nmsgstr \"\"\n\"Tigase 服务器可以通过以下协议进行远程监控：``JMX/RMI``, ``SNMP`` 和 ``HTTP``。尽管 ``JMX`` \"\n\"为服务器状态提供了最大的控制和可见性，但所有监视服务都提供了相同的基本服务器统计数据集：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:8\nmsgid \"Number of network connections for s2s, c2s and Bosh\"\nmsgstr \"s2s，c2s 和 Bosh 的网络连接数\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:10\nmsgid \"\"\n\"Last second, last minute and last hour load for all main components: SM, \"\n\"MR, c2s, s2s, Bosh, MUC and PubSub\"\nmsgstr \"所有主要组件的最后一秒, 最后一分钟和最后一小时负载：SM, MR, c2s, s2s, Bosh, MUC 和 PubSub\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:12\nmsgid \"\"\n\"System statistics - memory usage (heap and non heap) and the server \"\n\"uptime in milliseconds and human readable text.\"\nmsgstr \"系统统计信息 - 内存使用情况（堆和非堆）和服务器正常运行时间（以毫秒为单位）和人类可读的文本。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:14\nmsgid \"\"\n\"Users statistics - number of registered users and number of online user \"\n\"session.\"\nmsgstr \"用户统计 - 注册用户数和在线用户会话数。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:16\nmsgid \"\"\n\"JMX/RMI and SNMP servers offer basic security and can restrict access \"\n\"while the HTTP server doesn’t offer any access restriction mechanisms. \"\n\"Therefore HTTP monitoring is recommended to operate behind a firewall.\"\nmsgstr \"\"\n\"JMX/RMI 和 SNMP 服务器提供基本的安全性能并且可以限制访问，而 HTTP 服务器不提供任何访问限制机制。因此，建议 HTTP \"\n\"监控在防火墙后运行。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:18\nmsgid \"\"\n\"The monitoring itself causes very low overhead in terms of the resources \"\n\"and CPU consumption on top of the normal Tigase processing requirements \"\n\"so it can be left on without worrying about performance degradation.\"\nmsgstr \"在正常的 Tigase 处理要求之上，监视本身在资源和 CPU 消耗方面导致非常低的开销，因此可以保留它而不必担心性能下降。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:20\nmsgid \"\"\n\"NOTE This works with the Tigase server from version **4.2.0** or build \"\n\"**1418**.\"\nmsgstr \"注意 这适用于版本 **4.2.0** 或构建 **1418** 的 Tigase 服务器。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:23\nmsgid \"What You Need\"\nmsgstr \"你需要什么\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:25\nmsgid \"\"\n\"Statistics binaries are built-in ``-dist-max`` and no extra files are \"\n\"needed. If you have downloaded ``-dist`` file, you will need tigase-\"\n\"extras[https://github.com/tigase/tigase-extras] built and included in the\"\n\" ``jars/`` directory.\"\nmsgstr \"\"\n\"统计二进制文件是内置的 ``-dist-max`` ，不需要额外的文件。如果你已经下载了 ``-dist`` 文件，你需要构建 tigase-\"\n\"extras[https://github.com/tigase/tigase-extras] 并将其包含在 ``jars/`` 目录中。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:28\nmsgid \"Activation\"\nmsgstr \"激活\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:30\nmsgid \"\"\n\"You can either run the Tigase installer and use the configuration wizard \"\n\"to activate the monitoring or edit etc/config.tdsl file and add following\"\n\" lines:\"\nmsgstr \"您可以运行 Tigase 安装程序并使用配置向导来激活监控或编辑 etc/config.tdsl 文件并添加以下行：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:46\nmsgid \"\"\n\"As you see there is a separate block for each monitoring server you want \"\n\"to activate. Each server is responsible for activation of a different \"\n\"protocol and takes a single parameter - port number. There are following \"\n\"protocols supported right now:\"\nmsgstr \"如您所见，您要激活的每个监控服务器都有一个单独的块。每个服务器负责激活不同的协议并采用单个参数 - 端口号。目前支持以下协议：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:48\nmsgid \"``jmx`` - activating monitoring via JMX/RMI\"\nmsgstr \"``jmx`` - 通过 JMX/RMI 激活监控\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:50\nmsgid \"``http`` - activating monitoring over HTTP protocol\"\nmsgstr \"``http`` - 通过 HTTP 协议激活监控\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:52\nmsgid \"``snmp`` - activating monitoring over SNMP protocol\"\nmsgstr \"``snmp`` - 通过 SNMP 协议激活监控\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:54\nmsgid \"\"\n\"You can have all protocols active at the same time or any combination of \"\n\"them or none.\"\nmsgstr \"您可以同时激活所有协议或它们的任意组合或不激活。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:57\nmsgid \"Security\"\nmsgstr \"安全\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:59\nmsgid \"\"\n\"Both JMX and SNMP offer security protection to limit access to monitoring\"\n\" data. The security configuration is a bit different for both.\"\nmsgstr \"JMX 和 SNMP 都可提供安全保护来限制对监控数据的访问。两者的安全配置略有不同。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:62\nmsgid \"JMX\"\nmsgstr \"JMX\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:64\nmsgid \"\"\n\"After the server installation or in the SVN repository you can find 2 \"\n\"files in the ``etc/`` directory: ``jmx.access`` and ``jmx.password``.\"\nmsgstr \"\"\n\"服务器安装后或在 SVN 存储库中，您可以在 ``etc/`` 目录中找到 2 个文件：``jmx.access`` 和 \"\n\"``jmx.password``。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:66\nmsgid \"\"\n\"``jmx.access`` is a user permission file. You can use it to specify \"\n\"whether the user can access the monitoring data for reading only \"\n\"``readonly`` or with read-write ``readwrite`` access. There are example \"\n\"entries in the file already and the content may simply look like:\"\nmsgstr \"\"\n\"``jmx.access`` 是一个用户权限文件。您可以使用它来指定用户是否可以只读 ``readonly`` 或读写 ``readwrite``\"\n\" 访问监控数据。文件中已有示例条目，内容可能如下所示：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:73\nmsgid \"\"\n\"``jmx.password`` is a user password file. You can set user passwords here\"\n\" and the format again is very simple and the same as for jmx.access. \"\n\"There are example entries already provided for you convenience. Content \"\n\"of the file may look like the example below:\"\nmsgstr \"\"\n\"``jmx.password`` 是一个用户密码文件。您可以在此处设置用户密码，格式也是非常简单，与 jmx.access \"\n\"相同。为方便起见，已经提供了示例条目。该文件的内容可能类似于以下示例：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:80\nmsgid \"\"\n\"Using above to files you can control who and how can access the JMX \"\n\"monitoring services.\"\nmsgstr \"使用上述文件，您可以控制谁以及如何访问 JMX 监控服务。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:82\nmsgid \"SNMP\"\nmsgstr \"SNMP\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:84\nmsgid \"\"\n\"Access to SNMP monitoring is controlled using ACL (access control lists) \"\n\"which can be configured in the file ``snmp.acl`` located in ``etc/`` \"\n\"directory. It contains lots of detailed instructions how to setup ACL and\"\n\" restrict access per user, host and what kind access is allowed. The \"\n\"simplest possible configuration may look like this:\"\nmsgstr \"\"\n\"使用 ACL（访问控制列表）控制对 SNMP 监控的访问，该访问控制列表可以在位于 ``etc/`` 目录中的文件 ``snmp.acl`` \"\n\"中进行配置。它包含许多详细说明如何设置 ACL 和限制每个用户，主机的访问以及允许的访问类型。最简单的配置可能如下所示：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:101\nmsgid \"\"\n\"You might also need Tigase MIB definition: `TIGASE-MANAGEMENT-MIB.mib \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/resources/mib/JVM-MANAGEMENT-MIB.mib>`__ for \"\n\"the server specific statistics. The MIB contains definition for all the \"\n\"server statistics exposed via SNMP.\"\nmsgstr \"\"\n\"您可能还需要 Tigase MIB 定义：`TIGASE-MANAGEMENT-MIB.mib \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/resources/mib/JVM-MANAGEMENT-MIB.mib>`__ \"\n\"以用于服务器特定的统计信息。 MIB 包含通过 SNMP 公开的所有服务器统计信息的定义。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:103\nmsgid \"HTTP\"\nmsgstr \"HTTP\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Setting_up.inc:105\nmsgid \"\"\n\"Access the server at example.com:9080 and you will be presented with an \"\n\"Agent View.\"\nmsgstr \"访问 example.com:9080 上的服务器，您将看到一个代理视图。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:4\nmsgid \"Retrieving statistics from the server\"\nmsgstr \"从服务器检索统计信息\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:6\nmsgid \"\"\n\"By default we can retrieve server statistics using XMPP, no additional \"\n\"setup is necessary.\"\nmsgstr \"默认情况下，我们可以使用 XMPP 检索服务器统计信息，无需额外设置。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:9\nmsgid \"Retrieving statistics using XMPP\"\nmsgstr \"使用 XMPP 检索统计信息\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:11\nmsgid \"\"\n\"Accessing statistics over XMPP protocol requires any XMPP client capable \"\n\"of executing `XEP-0050: Ad-Hoc Commands \"\n\"<http://xmpp.org/extensions/xep-0050.html>`__. It’s essential to \"\n\"remember, that only administrator (a user whose JID is configured as \"\n\"administrative) can access the statistics.\"\nmsgstr \"\"\n\"通过 XMPP 协议访问统计数据需要任何 XMPP 客户端能够执行 `XEP-0050: Ad-Hoc Commands \"\n\"<http://xmpp.org/extensions/xep-0050.html>`__。必须记住，只有管理员（其 JID \"\n\"配置为管理的用户）才能访问统计信息。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:14\nmsgid \"Psi XMPP Client\"\nmsgstr \"Psi XMPP客户端\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:16\nmsgid \"\"\n\"For the purpose of this guide `Psi <http://psi-im.org/>`__ client will be\"\n\" used. After successfully configuring and connecting to account with \"\n\"administrative privileges we need to access *Service Discovery*, either \"\n\"from application menu or from context menu of the particular account \"\n\"account:\"\nmsgstr \"\"\n\"出于本指南的目的，将使用 `Psi <http://psi-im.org/>`__ \"\n\"客户端。成功配置并连接到具有管理权限的帐户后，我们需要从应用程序菜单或特定帐户的上下文菜单访问 *Service Discovery*：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:18\nmsgid \"|roster-discovery|\"\nmsgstr \"|roster-discovery|\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:74\nmsgid \"roster-discovery\"\nmsgstr \"roster-discovery\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:20\nmsgid \"\"\n\"In the *Service Discovery* window we need to find *Server Statistics* \"\n\"component:\"\nmsgstr \"在 *Service Discovery* 窗口中，我们需要找到 *Server Statistics* 组件：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:22\nmsgid \"|discovery-stats|\"\nmsgstr \"|discovery-stats|\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:75\nmsgid \"discovery-stats\"\nmsgstr \"discovery-stats\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:24\nmsgid \"\"\n\"We can either access statistics for all components or select particular \"\n\"component after expanding the tree. To execute ad-hoc command simply \"\n\"double click on the particular node which will open window with \"\n\"statistics:\"\nmsgstr \"我们可以访问所有组件的统计信息，也可以在展开树后选择特定组件。要执行临时命令，只需双击将打开统计窗口的特定节点：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:26\nmsgid \"|server-stats|\"\nmsgstr \"|server-stats|\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:76\nmsgid \"server-stats\"\nmsgstr \"server-stats\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:28\nmsgid \"\"\n\"In this window, in addition to see the statistics, we can adjust *Stats \"\n\"level* by selecting desired level from the list and confirm by clicking \"\n\"*Finish*.\"\nmsgstr \"在此窗口中，除了查看统计信息外，我们还可以通过从列表中选择所需级别来调整 *Stats level*，然后单击 *Finish* 进行确认。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:31\nmsgid \"Retrieving statistics using JMX\"\nmsgstr \"使用 JMX 检索统计信息\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:33\nmsgid \"\"\n\"In order to access statistics over JMX we need to enable support for it \"\n\"in Tigase - `Monitoring Activation <#monitoring_activation>`__. \"\n\"Afterwards we can use a number of tools to get to the statistics, for \"\n\"example the following:\"\nmsgstr \"\"\n\"为了通过 JMX 访问统计信息，我们需要在 Tigase 中启用对它的支持 - `Monitoring Activation \"\n\"<#monitoring_activation>`__。之后我们可以使用一些工具来获取统计信息，例如：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:36\nmsgid \"JConsole\"\nmsgstr \"JConsole\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:38\nmsgid \"\"\n\"After opening JConsole we either select local process or provide details \"\n\"of the remote process, including IP, port and credentials from \"\n\"**etc/jmx.**\\\\ \\\\* files:\"\nmsgstr \"\"\n\"打开 JConsole 后，我们要么选择本地进程，要么提供远程进程的详细信息，包括来自 **etc/jmx.**\\\\\\\\* 文件的 \"\n\"IP，端口和凭据：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:40\nmsgid \"|jconsole|\"\nmsgstr \"|jconsole|\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:77\nmsgid \"jconsole\"\nmsgstr \"jconsole\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:42\nmsgid \"\"\n\"Afterwards we navigate to the MBeans tab from where we can access the \"\n\"``tigase.stats`` MBean. It offers similar options to XMPP - either \"\n\"accessing statistics for all components or only for particular component \"\n\"as well as adjusting level for which we want to obtain statistics:\"\nmsgstr \"\"\n\"之后我们去找到 MBeans 选项卡，从中我们可以访问 ``tigase.stats`` MBean。它提供了与 XMPP 类似的选项 - \"\n\"访问所有组件的统计信息或仅访问特定组件的统计信息，以及调整我们想要获取统计信息的级别：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:44\nmsgid \"|jconsole-1|\"\nmsgstr \"|jconsole-1|\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:78\nmsgid \"jconsole-1\"\nmsgstr \"jconsole-1\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:48\nmsgid \"StatsDumper.groovy\"\nmsgstr \"StatsDumper.groovy\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:50\nmsgid \"\"\n\"In order to collect statistics over period of time following groovy \"\n\"script can be used: `StatsDumper.groovy \"\n\"<https://raw.githubusercontent.com/tigase/tigase-\"\n\"server/master/src/main/restructured/files/StatsDumper.groovy>`__. It’s a \"\n\"Simple JMX client that connects to Tigase and periodically saves all \"\n\"statistics to files.\"\nmsgstr \"\"\n\"为了收集一段时间内的统计信息，可以使用以下 groovy 脚本：`StatsDumper.groovy \"\n\"<https://raw.githubusercontent.com/tigase/tigase-\"\n\"server/master/src/main/restructured/files/StatsDumper.groovy>`__。它是一个简单的 \"\n\"JMX 客户端，连接到 Tigase 并定期将所有统计信息保存到文件中。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:52\nmsgid \"It takes following parameters:\"\nmsgstr \"它接受以下参数：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:58\nmsgid \"``hostname`` - address of the instance\"\nmsgstr \"``hostname`` - 实例地址\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:60\nmsgid \"``username`` - JMX username\"\nmsgstr \"``username`` - JMX 用户名\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:62\nmsgid \"``password`` - JMX username\"\nmsgstr \"``password`` - JMX 用户名\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:64\nmsgid \"``dir`` - directory to which save the files with statistics\"\nmsgstr \"``dir`` - 保存带有统计信息的文件的目录\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:66\nmsgid \"``port`` - port on which to make the connection\"\nmsgstr \"``port`` - 建立连接的端口\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:68\nmsgid \"\"\n\"``delay``\\\\ (ms) - initial delay in milliseconds after which statistics \"\n\"should be saved\"\nmsgstr \"``delay``\\\\ (ms) - 初始延迟（以毫秒为单位），之后应保存统计信息\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:70\nmsgid \"``interval``\\\\ (ms) - interval between each retrieval/saving of statistics\"\nmsgstr \"``interval``\\\\ (ms) - 每次检索/保存统计信息之间的间隔\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Retrieving_statistics.inc:72\nmsgid \"\"\n\"``loadhistory``\\\\ (bool) - indicates whether or not load statistics \"\n\"history from server (if such is enabled in Tigase)\"\nmsgstr \"``loadhistory``\\\\ (bool) - 表明是否从服务器加载统计历史记录（如果在 Tigase 中启用）\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:4\nmsgid \"Monitor Component\"\nmsgstr \"监控组件\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:6\nmsgid \"\"\n\"Tigase includes an **Monitor Component** to help with monitoring has been\"\n\" implemented. This allows you to set thresholds for certain predefined \"\n\"tasks and you or other JIDs can be sent a message when those thresholds \"\n\"are passed. You can even configure a mailer extension to have an E-mail \"\n\"sent to system administrators to let them know an event has occurred! \"\n\"Lets begin with setup and requirements.\"\nmsgstr \"\"\n\"Tigase 包含一个 **Monitor Component** \"\n\"以帮助进行监控。这允许您为某些预定义任务设置阈值，并且当超过这些阈值时，您或其他 JIDs \"\n\"可以收到一条消息。您甚至可以配置邮件程序扩展，将电子邮件发送给系统管理员，让他们知道发生了什么事情！让我们从设置和要求开始。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:8\nmsgid \"\"\n\"Monitor Component is based on eventbus which in turn is based on a \"\n\"limited `PubSub <http://www.xmpp.org/extensions/xep-0060.html>`__ \"\n\"specification. Events are delivered to subscribers as a normal PubSub \"\n\"notification.\"\nmsgstr \"\"\n\"Monitor 组件基于 eventbus，而 eventbus 又基于有限的 `PubSub \"\n\"<http://www.xmpp.org/extensions/xep-0060.html>`__ 规范。事件作为普通的 PubSub \"\n\"通知传递给订阅者。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:10\nmsgid \"\"\n\"Each component or client may subscribe for specific types of events. Only\"\n\" components on cluster nodes are allowed to publish events.\"\nmsgstr \"每个组件或客户端都可以订阅特定类型的事件。只允许集群节点上的组件发布事件。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:13\nmsgid \"Setup\"\nmsgstr \"设置\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:15\nmsgid \"\"\n\"Monitor Component is enabled by default on v7.1.0 b4001 and later, so no \"\n\"setup needed!\"\nmsgstr \"监视器组件在 v7.1.0 b4001 及更高版本上默认启用，因此无需设置！\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:18\nmsgid \"How it Works\"\nmsgstr \"这个怎么工作\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:20\nmsgid \"\"\n\"Events in Eventbus are identified by two elements: name of event and its \"\n\"namespace:\"\nmsgstr \"Eventbus 中的事件由两个元素标识：事件名称及其命名空间：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:28\nmsgid \"Where event name is ``EventName`` and namespace is ``tigase:demo``.\"\nmsgstr \"其中事件名称是 ``EventName``，命名空间是 ``tigase:demo``。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:30\nmsgid \"\"\n\"Listeners may subscribe for a specific event or for all events with \"\n\"specific a namespace. Because in pubsub, only one node name exists, so we\"\n\" have to add a way to convert the event name and namespace to a node \"\n\"name:\"\nmsgstr \"\"\n\"侦听器可以订阅特定事件或具有特定命名空间的所有事件。因为在 pubsub \"\n\"中，只有一个节点名存在，所以我们要添加一种方法，将事件名和命名空间转换为节点名：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:36\nmsgid \"\"\n\"So for example, to subscribe to ``<EventName xmlns=\\\"tigase:demo\\\">``, \"\n\"node must be: ``EventName|tigase:demo``. If you wish to subscribe to all \"\n\"events with a specific namespace, use an asterisk (``*``) instead of the \"\n\"event name: ``*|tigase:demo``.\"\nmsgstr \"\"\n\"例如，要订阅 ``<EventName \"\n\"xmlns=\\\"tigase:demo\\\">``，节点必须是：``EventName|tigase:demo``。如果您希望订阅具有特定命名空间的所有事件，请使用星号（``*``）而不是事件名称：``*|tigase:demo``。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:45\nmsgid \"Available Tasks\"\nmsgstr \"可用任务\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:47\nmsgid \"\"\n\"Monitor Component has several pre-defined tasks that can be monitored and\"\n\" set to trigger. What follows is the list of tasks with the options \"\n\"attributed to each task.\"\nmsgstr \"Monitor Component 有几个预定义的任务可以被监控并设置为触发。以下是任务列表，其中包含每个任务的选项。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc\nmsgid \"**disk-task** - Used to check disk usage.\"\nmsgstr \"**disk-task** - 用于检查磁盘使用情况。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc\nmsgid \"Available Options\"\nmsgstr \"可用选项\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:52\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:61\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:70\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:79\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:89\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:96\nmsgid \"``enabled`` - Enable or disable task, Boolean value.\"\nmsgstr \"``enabled`` - 启用或禁用任务，布尔值。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:54\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:63\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:72\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:81\nmsgid \"``period`` - Period of running check, Integer value.\"\nmsgstr \"``period`` - 运行检查的周期，整数值。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:56\nmsgid \"``threshold`` - Percentage of used space on disk, Float value.\"\nmsgstr \"``threshold`` - 磁盘上已用空间的百分比，浮点值。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc\nmsgid \"**cpu-temp-task** - Used to check CPU temperature.\"\nmsgstr \"**cpu-temp-task** - 用于检查 CPU 温度。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:65\nmsgid \"``cpuTempThreshold`` - Temperature threshold of CPU in °C.\"\nmsgstr \"``cpuTempThreshold`` - CPU 的温度阈值，以°C 为单位。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc\nmsgid \"**load-checker-task** - Used to check system load.\"\nmsgstr \"**load-checker-task** - 用于检查系统负载。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:74\nmsgid \"``averageLoadThreshold`` - Average percent load threshold, Long value.\"\nmsgstr \"``averageLoadThreshold`` - 平均百分比负载阈值，long 值。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc\nmsgid \"**memory-checker-task** - Used to check memory usage.\"\nmsgstr \"**memory-checker-task** - 用于检查内存使用情况。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:83\nmsgid \"\"\n\"``maxHeapMemUsagePercentThreshold`` - Alarm when percent of used Heap \"\n\"memory is larger than, Integer value.\"\nmsgstr \"``maxHeapMemUsagePercentThreshold`` - 已用堆内存百分比大于此值时发出警报，此值为整数值。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:85\nmsgid \"\"\n\"``maxNonHeapMemUsagePercentThreshold`` - Alarm when percent of used Non \"\n\"Heap memory is larger than, Integer value.\"\nmsgstr \"``maxNonHeapMemUsagePercentThreshold`` - 已用非堆内存百分比大于此值时发出警报，此值为整数值。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc\nmsgid \"**logger-task** - Used to transmit log entries depending on level entered.\"\nmsgstr \"**logger-task** - 用于根据输入级别传输的日志条目。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:91\nmsgid \"\"\n\"``levelThreshold`` - Minimal log level that will be the threshold. \"\n\"Possible values are SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, \"\n\"and ALL.\"\nmsgstr \"\"\n\"``levelThreshold`` - 将成为阈值的最小日志级别。可能的值为 \"\n\"SEVERE、WARNING、INFO、CONFIG、FINE、FINER、FINEST 和 ALL。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc\nmsgid \"**connections-task** - Used to check users disconnections.\"\nmsgstr \"**connections-task** - 用于检查用户断开连接。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc\nmsgid \"\"\n\"**NOTE: The event will be generated only if both thresholds (amount and \"\n\"percentage) will be fulfilled.**\"\nmsgstr \"**注意：仅当满足两个阈值（数量和百分比）时才会生成事件。**\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:98\nmsgid \"``period`` - Period of running check in ms, Integer value.\"\nmsgstr \"``period`` - 运行检查的周期，整数值。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:100\nmsgid \"\"\n\"``thresholdMinimal`` - Minimal amount of disconnected users required to \"\n\"generate alarm.\"\nmsgstr \"``thresholdMinimal`` - 生成警报所需断开连接用户的最小数量。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:102\nmsgid \"\"\n\"``threshold`` - Minimal percent of disconnected users required to \"\n\"generate alarm.\"\nmsgstr \"``threshold`` - 生成警报所需的断开连接用户的最小百分比。\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:7\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:105\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:308\nmsgid \"Configuration\"\nmsgstr \"配置\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:107\nmsgid \"\"\n\"Configuration of the monitor can be done one of two ways; either by lines\"\n\" in ``config.tdsl`` file, or by sending XMPP stanzas to the server. You \"\n\"may also send XMPP stanzas VIA HTTP REST. XMPP stanza configurations will\"\n\" override ones in config.tdsl, but they will only last until the server \"\n\"restarts.\"\nmsgstr \"\"\n\"监视器的配置可以通过以下两种方式之一完成；通过 ``config.tdsl`` 文件中的行，或者通过将 XMPP 节发送到服务器。您也可以通过 \"\n\"HTTP REST 发送 XMPP 节。 XMPP 节配置将覆盖 config.tdsl 中的配置，但它们只会持续到服务器重新启动。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:109\nmsgid \"config.tdsl\"\nmsgstr \"config.tdsl\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:111\nmsgid \"\"\n\"Tasks can be configured in the ``config.tdsl`` file. See :ref:`available \"\n\"tasks<availableTasks>` for the tasks that can be setup.\"\nmsgstr \"可以在 ``config.tdsl`` 文件中配置任务。有关可以设置的任务，请参阅 \"\n\":ref:`可用任务<availableTasks>`。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:113\nmsgid \"To enable a specific monitor task, use the following line:\"\nmsgstr \"要启用特定的监控任务，请使用以下行：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:123\nmsgid \"\"\n\"Where monitor is the component name for ``MonitorComponent``, and \"\n\"``$TASKNAME`` is one of the :ref:`available task names<availableTasks>`.\"\nmsgstr \"\"\n\"其中 monitor 是 ``MonitorComponent`` 的组件名称，而 ``$TASKNAME`` 是 \"\n\":ref:`可用任务<availableTasks>` 之一。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:125\nmsgid \"\"\n\"This format will be the same for other settings for tasks, and it’s best \"\n\"to group settings under one heading. For example:\"\nmsgstr \"此格式与任务的其他设置相同，最好将设置分组在一个标题下。例如：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:136\nmsgid \"\"\n\"sets the check period to 1000 milliseconds and enables ``connections-\"\n\"task``.\"\nmsgstr \"将检查周期设置为 1000 毫秒并启用 ``connections-task``。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:140\nmsgid \"\"\n\"Once triggers have been activated, they will become dormant. Think of \"\n\"these as one-shot settings.\"\nmsgstr \"一旦触发器被激活，它们就会进入休眠状态。将这些视为一次性设置。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:143\nmsgid \"Subscription Limitations\"\nmsgstr \"订阅限制\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:145\nmsgid \"To define list of JIDs allowed to subscribe for events:\"\nmsgstr \"要定义允许订阅事件的 JIDs 列表：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:155\nmsgid \"If this is not specified, all users can subscribe.\"\nmsgstr \"如果以上未指定，则所有用户都可以订阅。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:157\nmsgid \"Configuration via XMPP\"\nmsgstr \"通过 XMPP 配置\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:159\nmsgid \"\"\n\"We can also configure the eventbus monitor component using XMPP stanzas. \"\n\"This allows us to set and change configurations during server runtime. \"\n\"This is done using a series of ``iq`` stanzas send to the monitor \"\n\"component.\"\nmsgstr \"\"\n\"我们还可以使用 XMPP 节配置事件总线监视器组件。这允许我们在服务器运行时设置和更改配置。这是使用一系列发送到监视器组件的 ``iq`` \"\n\"节来完成的。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:161\nmsgid \"\"\n\"We can query each component for its current settings using the following \"\n\"stanza.\"\nmsgstr \"我们可以使用以下节查询每个组件的当前设置。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:169\nmsgid \"\"\n\"The server will return the component current settings which will make \"\n\"things easier if you wish to edit them. In this case, the server has \"\n\"returned the following to us\"\nmsgstr \"服务器将返回组件当前设置，如果您想编辑它们，这将变得更容易。在这种情况下，服务器已向我们返回以下内容\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:192\nmsgid \"\"\n\"This tells us that the disk-task setting is not active, has a period of \"\n\"60000ms, and will trigger when disk usage is over 80%.\"\nmsgstr \"这告诉我们，disk-task 设置不活跃，有 60000ms 的周期，当磁盘使用率超过 80% 时会触发。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:194\nmsgid \"\"\n\"To send new settings to the monitor component, we can send a similar \"\n\"stanza back to the monitor component.\"\nmsgstr \"要将新设置发送到监视器组件，我们可以将类似的节发送回监视器组件。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:215\nmsgid \"\"\n\"To which a successful update will give you an XMPP success stanza to let \"\n\"you know everything is set correctly.\"\nmsgstr \"成功的更新将为您提供 XMPP 成功节，让您知道一切设置正确。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:217\nmsgid \"\"\n\"Alternatively, you can update specific settings by editing a single field\"\n\" without adding anything else. For example, if we just wanted to turn the\"\n\" disk-task on we could send the following stanza:\"\nmsgstr \"或者，您可以通过编辑单个字段而不添加任何其他内容来更新特定设置。例如，如果我们只想打开磁盘任务，我们可以发送以下节：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:231\nmsgid \"\"\n\"To set any other values, do not forget that certain parts may need to be \"\n\"changed, specifically the ``<field type=\\\"boolean\\\" \"\n\"var=x-task#enabled\\\">`` fields:\"\nmsgstr \"\"\n\"要设置任何其他值，请不要忘记某些部分可能需要更改，特别是 ``<field type=\\\"boolean\\\" \"\n\"var=x-task#enabled\\\">`` 字段:\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc\nmsgid \"\"\n\"Your field type will be defined by the type of variable specified in the \"\n\":ref:`Available Tasks<availableTasks>` section.\"\nmsgstr \"您的字段类型将由 :ref:`可用任务<availableTasks>` 部分中指定的变量类型定义。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:235\nmsgid \"\"\n\"``var=x task#`` will be followed by the property value taken directly \"\n\"from the ref:`Available Tasks<availableTasks>` section.\"\nmsgstr \"``var=x task#`` 后跟直接取自 :ref:`可用任务<availableTasks>` 部分的属性值。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:238\nmsgid \"Getting the Message\"\nmsgstr \"获取消息\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:240\nmsgid \"\"\n\"Without a place to send messages to, monitor will just trigger and shut \"\n\"down. There are two different methods that monitor can deliver alarm \"\n\"messages and relevant data; XMPP messages and using the mailer extension.\"\nmsgstr \"如果没有发送消息的地方，监视器只会触发并关闭。监视器有两种不同的方法来传递警报消息和相关数据； XMPP 消息并使用邮件程序扩展。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:242\nmsgid \"XMPP notification\"\nmsgstr \"XMPP 通知\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:244\nmsgid \"\"\n\"In order to retrieve notifications, a subscription to the \"\n\"``eventbus@<VHost>`` user must be made. Keep in mind that subscriptions \"\n\"are not persistent across server restarts, or triggers.\"\nmsgstr \"为了检索通知，必须订阅 ``eventbus@<VHost>`` 用户。请记住，订阅在服务器重新启动或触发后是会变化的。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:245\nmsgid \"\"\n\"The monitor schema is very similar to most XMPP subscription requests but\"\n\" with a few tweaks to differentiate it if you wanted to subscribe to a \"\n\"certain task or all of them. Each task is considered a node, and each \"\n\"node has the following pattern: ``eventName|eventXMLNS``. Since each \"\n\"monitoring task has the ``tigase:monitor:event`` event XMLNS, we just \"\n\"need to pick the event name from the list of tasks. So like the above \"\n\"example, our event node for the disk task will be ``disk-\"\n\"task|tigase:monitor:event``. Applied to an XMPP stanza, it will look \"\n\"something like this:\"\nmsgstr \"\"\n\"监视器架构与大多数 XMPP \"\n\"订阅请求非常相似，但如果您想订阅某个任务或所有任务，请进行一些调整以区分它。每个任务都被视为一个节点，每个节点都有以下模式：``eventName|eventXMLNS``。由于每个监控任务都有\"\n\" ``tigase:monitor:event`` 事件 \"\n\"XMLNS，我们只需要从任务列表中选择事件名称。所以像上面的例子一样，我们的磁盘任务事件节点将是 ``disk-\"\n\"task|tigase:monitor:event`` 。其用于 XMPP 节，它看起来像这样：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:257\nmsgid \"\"\n\"Don’t forget to replace ``$USER_JID`` with the bare JID of the user you \"\n\"want to receive those messages. You can even have them sent to a MUC or \"\n\"any component with a JID.\"\nmsgstr \"不要忘记将 ``$USER_JID`` 替换为您想要接收这些消息的用户的裸 JID。您甚至可以将它们发送到 MUC 或任何具有 JID 的组件。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:259\nmsgid \"Available events are as follows:\"\nmsgstr \"可用的事件如下：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:261\nmsgid \"DiskUsageMonitorEvent for ``disk-task``\"\nmsgstr \"``disk-task`` 的 DiskUsageMonitorEvent\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:263\nmsgid \"LoggerMonitorEvent for ``logger-task``\"\nmsgstr \"``logger-task`` 的LoggerMonitorEvent\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:265\nmsgid \"HeapMemoryMonitorEvent for ``memory-checker-task``\"\nmsgstr \"``memory-checker-task`` 的HeapMemoryMonitorEvent\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:267\nmsgid \"LoadAverageMonitorEvent for ``load-checker-task``\"\nmsgstr \"``load-checker-task`` 的LoadAverageMonitorEvent\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:269\nmsgid \"CPUTempMonitorEvent for ``cpu-temp-task``\"\nmsgstr \"``cpu-temp-task`` 的 CPUTempMonitorEvent\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:271\nmsgid \"UsersDisconnected for ``connections-task``\"\nmsgstr \"``connections-task`` 的UsersDisconnected\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:273\nmsgid \"\"\n\"Alternatively, you can also subscribe to all events within the eventbus \"\n\"by using a wildcard \\\\* in place of the event XMLNS like this example:\"\nmsgstr \"或者，您还可以使用通配符 \\\\* 代替事件 XMLNS 来订阅事件总线中的所有事件，如下例所示：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:285\nmsgid \"Sample notification from Monitor\"\nmsgstr \"来自 Monitor 的示例通知\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:302\nmsgid \"Mailer Extension\"\nmsgstr \"邮件扩展\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:304\nmsgid \"\"\n\"*Tigase Server Monitor Mailer Extension* (TSMME) can send messages from \"\n\"the monitor component to a specified E-mail address so system \"\n\"administrators who are not logged into the XMPP server.\"\nmsgstr \"\"\n\"*Tigase Server Monitor Mailer Extension* (TSMME) \"\n\"可以将消息从监视器组件发送到指定的电子邮件地址，以方便那些未登录到 XMPP 服务器的系统管理员。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:306\nmsgid \"\"\n\"For v7.1.0 versions and later, TSMME is already included in your \"\n\"distribution package and no extra installation is needed.\"\nmsgstr \"对于 v7.1.0 及更高版本，TSMME 已包含在您的分发包中，无需额外安装。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:310\nmsgid \"\"\n\"Tigase Mailer Extension may be configured via the ``config.tdsl`` file in\"\n\" the following manner:\"\nmsgstr \"Tigase Mailer Extension 可以通过 ``config.tdsl`` 文件用以下方式配置：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:323\nmsgid \"Here is an explanation of those variables.\"\nmsgstr \"这是对这些变量的解释。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:325\nmsgid \"``mailer-smtp-host`` - SMTP Server hostname.\"\nmsgstr \"``mailer-smtp-host`` - SMTP 服务器主机名。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:327\nmsgid \"``mailer-smtp-port`` - SMTP Server port.\"\nmsgstr \"``mailer-smtp-port`` - SMTP服务器端口。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:329\nmsgid \"``mailer-smtp-usernam`` - name of sender account.\"\nmsgstr \"``mailer-smtp-usernam`` - 发件人帐户的名称。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:331\nmsgid \"``mailer-smtp-password`` - password of sender account.\"\nmsgstr \"``mailer-smtp-password`` - 发件人帐户的密码。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:333\nmsgid \"\"\n\"``mailer-from-address`` - sender email address. It will be set in field \"\n\"from in email.\"\nmsgstr \"``mailer-from-address`` - 发件人电子邮件地址。它将在电子邮件中的字段中设置。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:335\nmsgid \"\"\n\"``mailer-to-addresses`` - comma separated notification receivers email \"\n\"addresses.\"\nmsgstr \"``mailer-to-addresses`` - 逗号分隔的通知接收者电子邮件地址。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/MonitorComponent.inc:337\nmsgid \"\"\n\"It is recommended to create a specific e-mail address in your mail server\"\n\" for this purpose only, as the account settings are stored in plaintext \"\n\"without encryption.\"\nmsgstr \"建议仅出于此目的在您的邮件服务器中创建一个特定的电子邮件地址，因为帐户设置以明文形式存储而没有加密。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Statistics_Logger_Config.inc:4\nmsgid \"Configuration of statistics loggers\"\nmsgstr \"统计记录器的配置\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Statistics_Logger_Config.inc:6\nmsgid \"\"\n\"It is possible to enable and configure automatic storage of statistics \"\n\"information. To do that you need to configure any of following statistics\"\n\" loggers as a ``StatisticsCollector`` component sub-beans:\"\nmsgstr \"可以启用和配置统计信息的自动存储。为此，您需要将以下任何统计记录器配置为 ``StatisticsCollector`` 组件子 bean：\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Statistics_Logger_Config.inc:9\nmsgid \"``tigase.stats.CounterDataArchivizer``\"\nmsgstr \"``tigase.stats.CounterDataArchivizer``\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Statistics_Logger_Config.inc:9\nmsgid \"\"\n\"every execution put current basic server metrics (CPU usage, memory \"\n\"usage, number of user connections, uptime) into database (overwrites \"\n\"previous entry).\"\nmsgstr \"每次执行都会将当前的基本服务器指标（CPU 使用率，内存使用率，用户连接数，正常运行时间）放入数据库（覆盖先前的条目）。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Statistics_Logger_Config.inc:12\nmsgid \"``tigase.stats.CounterDataLogger``\"\nmsgstr \"``tigase.stats.CounterDataLogger``\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Statistics_Logger_Config.inc:12\nmsgid \"\"\n\"every execution insert new row with new set of number of server \"\n\"statistics (CPU usage, memory usage, number of user connections per \"\n\"connector, number of processed packets of different types, uptime, etc) \"\n\"into the database.\"\nmsgstr \"\"\n\"每次执行都会在数据库中插入新的行，其中包含一组新的服务器统计数据（CPU \"\n\"使用率，内存使用率，每个连接器的用户连接数，已处理的不同类型的数据包数，正常运行时间等）。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Statistics_Logger_Config.inc:15\nmsgid \"``tigase.stats.CounterDataFileLogger``\"\nmsgstr \"``tigase.stats.CounterDataFileLogger``\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Statistics_Logger_Config.inc:15\nmsgid \"every execution store all server statistics into separate file.\"\nmsgstr \"每次执行都将所有服务器统计信息存储到单独的文件中。\"\n\n#: ../../Tigase_Administration/Components/Monitoring/Statistics_Logger_Config.inc:17\nmsgid \"\"\n\"As an example to configure ``tigase.stats.CounterDataFileLogger`` to \"\n\"archive statistics data with level ``FINE`` every 60 seconds to file \"\n\"prefixed with ``stat`` and located in ``logs/server_statistics`` \"\n\"following entry is needed:\"\nmsgstr \"\"\n\"``tigase.stats.CounterDataFileLogger`` 以每 60 秒将级别为 ``FINE`` 的统计数据归档到以 \"\n\"``stat`` 为前缀并位于 ``logs/server_statistics`` 的文件中，以下条目是所需要的：\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:2\nmsgid \"Server to Server Protocol Settings\"\nmsgstr \"服务器到服务器协议设置\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:4\nmsgid \"\"\n\"Tigase server-to-server communication component facilitates communication\"\n\" with other XMPP servers (federation) and allows you to tweak it’s \"\n\"configuration to get a better performance in your installation.\"\nmsgstr \"Tigase 服务器到服务器通信组件促进了与其他 XMPP 服务器（联合）的通信，并允许您调整其配置以获得更好的安装性能。\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:6\nmsgid \"\"\n\"S2S (or server to server) protocol is enabled by default with optimal \"\n\"settings chosen. There are however, a set of configuration parameters you\"\n\" can adjust the server behavior to achieve optimal performance on your \"\n\"installation.\"\nmsgstr \"默认情况下启用 S2S（或服务器到服务器）协议并选择最佳设置。但是，您可以使用一组配置参数来调整服务器行为，以在您的安装中实现最佳性能。\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:8\nmsgid \"\"\n\"This documents describes following elements of the Tigase server \"\n\"configuration:\"\nmsgstr \"本文档描述了 Tigase 服务器配置的以下元素：\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:10\nmsgid \"Number of concurrent connections to external servers\"\nmsgstr \"与外部服务器的并发连接数\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:12\nmsgid \"The connection throughput parameters\"\nmsgstr \"连接吞吐量参数\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:14\nmsgid \"\"\n\"Maximum waiting time for packets addressed to external servers and the \"\n\"connection inactivity time\"\nmsgstr \"发往外部服务器的数据包的最长等待时间和连接不活动时间\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:16\nmsgid \"Custom plugins selecting connection to the remote server\"\nmsgstr \"自定义插件选择连接到远程服务器\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:19\nmsgid \"Number of Concurrent Connections\"\nmsgstr \"并发连接数\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:21\nmsgid \"\"\n\"Normally only one connection to the remote server is required to send \"\n\"XMPP stanza to that server. In some cases however, under a high load, you\"\n\" can get much better throughput and performance if you open multiple \"\n\"connections to the remote server.\"\nmsgstr \"\"\n\"通常只需要一个到远程服务器的连接就可以将 XMPP \"\n\"节发送到该服务器。然而，在某些情况下，在高负载下，如果您打开到远程服务器的多个连接，您可以获得更好的吞吐量和性能。\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:23\nmsgid \"\"\n\"This is especially true when the remote server works in a cluster mode. \"\n\"Ideally you want to open a connection to each of the cluster nodes on the\"\n\" remote server. This way you can spread the traffic evenly among cluster \"\n\"nodes and improve the performance for s2s connections.\"\nmsgstr \"\"\n\"当远程服务器以集群模式工作时尤其如此。理想情况下，您希望打开与远程服务器上每个集群节点的连接。这样，您可以在集群节点之间平均分配流量并提高 s2s\"\n\" 连接的性能。\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:25\nmsgid \"\"\n\"Tigase server offers 2 different parameters to tweak the number of \"\n\"concurrent, s2s connections:\"\nmsgstr \"Tigase 服务器提供 2 个不同的参数来调整并发 s2s 连接的数量：\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:27\nmsgid \"\"\n\"``max-out-total-conns`` - this property specifies the maximum outgoing \"\n\"connections the Tigase server opens to any remote XMPP server. This is a \"\n\"**per domain** limit, which means that this limit applies to each of the \"\n\"remote domains Tigase connects to. If it is set to ``4`` then Tigase \"\n\"opens a maximum of 4 connections to ``jabber.org`` plus maximum 4 \"\n\"connections to ``muc.jabber.org`` even if this is the same physical \"\n\"server behind the same IP address.\"\nmsgstr \"\"\n\"``max-out-total-conns`` - 此属性指定 Tigase 服务器打开到任何远程 XMPP 服务器的最大传出连接。这是 \"\n\"**per domain** 限制，这意味着此限制适用于 Tigase 连接到的每个远程域。如果设置为 ``4`` ，那么 Tigase 最多打开\"\n\" 4 个到 ``jabber.org`` 的连接加上最多 4 个到 ``muc.jabber.org`` 的连接，即使这是同一 IP \"\n\"地址后面的同一物理服务器。\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:29\nmsgid \"To adjust the limit you have to add following to the ``config.tdsl`` file:\"\nmsgstr \"要调整限制，您必须在 ``config.tdsl`` 文件中添加以下内容：\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:37\nmsgid \"\"\n\"``max-out-per-ip-conns`` - this property specifies the maximum outgoing \"\n\"connections Tigase server opens to any remote XMPP server to its single \"\n\"IP address. This too, is **per domain** limit, which means that this \"\n\"limit applies to each of the remote domains Tigase connects to. If it is \"\n\"set to ``1``, and the above limit is set to ``4``, and the remote server \"\n\"is visible behind 1 IP address, then Tigase opens a maximum of 1 \"\n\"connection to ``jabber.org`` plus a maximum of 1 connection to \"\n\"``muc.jabber.org`` and other subdomains.\"\nmsgstr \"\"\n\"``max-out-per-ip-conns`` - 此属性指定 Tigase 服务器打开到任何远程 XMPP 服务器到其单个 IP \"\n\"地址的最大传出连接。这也是 **per domain** 限制，这意味着此限制适用于 Tigase 连接到的每个远程域。如果设置为 \"\n\"``1``，并且上述限制设置为 ``4``，并且远程服务器在 1 个 IP 地址后面可见，那么 Tigase 最多打开 1 个到 \"\n\"``jabber.org`` 的连接 加上最多 1 个到 ``muc.jabber.org`` 和其他子域的连接。\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:39\nmsgid \"\"\n\"To adjust the limit you have to add following line to the ``config.tdsl``\"\n\" file:\"\nmsgstr \"要调整限制，您必须在 ``config.tdsl`` 文件中添加以下内容：\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:49\nmsgid \"Connection Throughput\"\nmsgstr \"连接吞吐量\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:51\nmsgid \"\"\n\"Of course everybody wants his server to run with maximum throughput. This\"\n\" comes with a cost on resources, usually increased memory usage. This is \"\n\"especially important if you have large number of s2s connections on your \"\n\"installations. High throughput means lots of memory for network buffers \"\n\"for every single s2s connection. You may soon run out of all available \"\n\"memory.\"\nmsgstr \"\"\n\"当然，每个人都希望他的服务器以最大吞吐量运行。这会带来资源成本，通常会增加内存使用量。如果您的安装中有大量 s2s \"\n\"连接，这一点尤其重要。高吞吐量意味着每个 s2s 连接都有大量内存用于网络缓冲区。您可能很快就会用完所有可用内存。\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:53\nmsgid \"\"\n\"There is one configuration property which allows you to adjust the \"\n\"network buffers for s2s connections to lower your memory usage or \"\n\"increase data throughput for s2s communication.\"\nmsgstr \"有一个配置属性允许您调整 s2s 连接的网络缓冲区，以降低内存使用量或增加 s2s 通信的数据吞吐量。\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:55\nmsgid \"\"\n\"More details about are available in the :ref:`net-buff-high-\"\n\"throughput<netBuffHighThroughput>` or :ref:`net-buff-\"\n\"Standard<netBuffStandard>` property descriptions.\"\nmsgstr \"\"\n\"有关更多详细信息，请参阅 :ref:`net-buff-high-\"\n\"throughput<netBuffHighThroughput>` 或 :ref:`net-buff-\"\n\"Standard<netBuffStandard>` 属性描述。\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:58\nmsgid \"Maximum Packet Waiting Time and Connection Inactivity Time\"\nmsgstr \"最大数据包等待时间和连接不活动时间\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:60\nmsgid \"\"\n\"There are 2 timeouts you can set for the component controlling s2s \"\n\"communication.\"\nmsgstr \"您可以为控制 s2s 通信的组件设置 2 个超时。\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:62\nmsgid \"\"\n\"``max-packet-waiting-time`` - this sets the maximum time for the packets \"\n\"waiting for sending to some remote server. Sometimes, due to networking \"\n\"problems or DNS problems it might be impossible to send message to remote\"\n\" server right away. Establishing a new connection may take time or there \"\n\"might be communication problems between servers or perhaps the remote \"\n\"server is restarted. Tigase will try a few times to connect to the remote\"\n\" server before giving up. This parameter specifies how long the packet is\"\n\" waiting for sending before it is returned to the sender with an error. \"\n\"The timeout is specified in seconds:\"\nmsgstr \"\"\n\"``max-packet-waiting-time`` - 这设置了等待发送到某个远程服务器的数据包的最长时间。有时，由于网络问题或 DNS \"\n\"问题，可能无法立即将消息发送到远程服务器。建立新连接可能需要一些时间，或者服务器之间可能存在通信问题，或者远程服务器可能已重新启动。 Tigase\"\n\" 在放弃之前会尝试几次连接到远程服务器。此参数指定数据包等待发送多长时间后返回给发送者并出现错误。超时以秒为单位指定：\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:70\nmsgid \"\"\n\"``max-inactivity-time`` - this parameters specifies the maximum s2s \"\n\"connection inactivity time before it is closed. If a connection is not in\"\n\" use for a long time, it doesn’t make sense to keep it open and tie \"\n\"resources up. Tigase closes s2s connection after specified period of time\"\n\" and reconnects when it is necessary. The timeout is specified in \"\n\"seconds:\"\nmsgstr \"\"\n\"``max-inactivity-time`` - 此参数指定关闭之前的最大 s2s \"\n\"连接不活动时间。如果一个连接长时间不使用，保持它打开并占用资源是没有意义的。 Tigase 在指定时间后关闭 s2s \"\n\"连接，并在必要时重新连接。超时以秒为单位指定：\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:79\nmsgid \"Custom Plugin: Selecting s2s Connection\"\nmsgstr \"自定义插件：选择 s2s 连接\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:81\nmsgid \"\"\n\"Sometimes for very large installations you may want to set larger number \"\n\"of s2s connections to remote servers, especially if they work in cluster \"\n\"of several nodes. In such a case you can also have a control over XMPP \"\n\"packets distribution among s2s connections to a single remote server.\"\nmsgstr \"\"\n\"有时对于非常大的安装，您可能希望设置更多到远程服务器的 s2s \"\n\"连接，尤其是当它们在多个节点的集群中工作时。在这种情况下，您还可以控制与单个远程服务器的 s2s 连接之间的 XMPP 数据包分发。\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:83\nmsgid \"\"\n\"This piece of code is pluggable and you can write your own connection \"\n\"selector. It is enough to implement ``S2SConnectionSelector`` interface \"\n\"and set your class name in the configuration using following parameter in\"\n\" ``config.tdsl`` file:\"\nmsgstr \"\"\n\"这段代码是可插拔的，您可以编写自己的连接选择器。实现 ``S2SConnectionSelector`` 接口并使用 \"\n\"``config.tdsl`` 文件中的以下参数在配置中设置您的类名就足够了：\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:91\nmsgid \"The default selector picks connections randomly.\"\nmsgstr \"默认选择器随机选择连接。\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:94\nmsgid \"skip-tls-hostnames\"\nmsgstr \"skip-tls-hostnames\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:96\nmsgid \"\"\n\"The ``s2s-skip-tls-hostnames`` property disables TLS handshaking for s2s \"\n\"connections to selected remote domains. Unfortunately some servers \"\n\"(certain versions of Openfire - [`1 \"\n\"<http://community.igniterealtime.org/thread/36206>`__] or [`2 \"\n\"<http://community.igniterealtime.org/thread/30578>`__]) have problems \"\n\"with TLS handshaking over s2s which prevents establishing a usable \"\n\"connection. This completely blocks any communication to these servers. As\"\n\" a workaround you can disable TLS for these domains to get communication \"\n\"back. Enabling this can be done on any vhost, but must be configured \"\n\"under the s2s component.\"\nmsgstr \"\"\n\"``s2s-skip-tls-hostnames`` 属性禁用 TLS 握手，以便 s2s 连接到选定的远程域。不幸的是，一些服务器（某些版本的 \"\n\"Openfire - [`1 <http://community.igniterealtime.org/thread/36206>`__] or \"\n\"[`2 <http://community.igniterealtime.org/thread/30578>`__]) 在 s2s 上进行 TLS\"\n\" 握手有问题，这会阻止建立可用的连接。这完全阻止了与这些服务器的任何通信。作为一种解决方法，您可以为这些域禁用 TLS 以恢复通信。可以在任何 \"\n\"vhost 上启用此功能，但必须在 s2s 组件下进行配置。\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:105\nmsgid \"ejabberd-bug-workaround\"\nmsgstr \"ejabberd-bug-workaround\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:107\nmsgid \"\"\n\"This property activates a workaround for a bug in EJabberd in it’s s2s \"\n\"implementation. EJabberd does not send dialback in stream features after \"\n\"TLS handshaking even if the dialback is expected/needed. This results in \"\n\"unusable connection as EJabberd does not accept any packets on this \"\n\"connection either. The workaround is enabled by default right now until \"\n\"the EJabberd version without the bug is popular enough. A disadvantage of\"\n\" the workaround is that dialback is always performed even if the SSL \"\n\"certificate is fully trusted and in theory this dialback could be \"\n\"avoided. By default, this is not enabled.\"\nmsgstr \"\"\n\"此属性激活 EJabberd 中的 s2s 实现中的错误的解决方法。即使回拨是预期/需要的， EJabberd 不会在 TLS \"\n\"握手后在流功能中发送回拨。这会导致连接不可用，因为 EJabberd 也不接受此连接上的任何数据包。解决方法现在默认启用，直到没有错误的 \"\n\"EJabberd 版本足够流行。该解决方法的一个缺点是即使 SSL \"\n\"证书完全受信任，也会始终执行回拨，理论上可以避免这种回拨。默认情况下，此功能未启用。\"\n\n#: ../../Tigase_Administration/Components/Server_to_Server_Protocol.inc:117\nmsgid \"This replaces the old ``--s2s-ejabberd-bug-workaround-active`` property.\"\nmsgstr \"这替换了旧的 ``--s2s-ejabberd-bug-workaround-active`` 属性。\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:4\nmsgid \"Tigase Load Balancing\"\nmsgstr \"Tigase 负载均衡\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:6\nmsgid \"\"\n\"Tigase includes load balancing functionality allowing users to be \"\n\"redirected to the most suitable cluster node. Functionality relies on a \"\n\"see-other-host XMPP stream error message. The basic principle behind the \"\n\"mechanism is that user will get redirect if the host returned by the \"\n\"implementation differ from the host to which user currently tries to \"\n\"connect. It is required that the user JID to be known for the redirection\"\n\" to work correctly.\"\nmsgstr \"\"\n\"Tigase 包含负载平衡功能，允许将用户重定向到最合适的集群节点。功能依赖于 see-other-host XMPP \"\n\"流错误消息。该机制背后的基本原理是，如果实现返回的主机与用户当前尝试连接的主机不同，则用户将获得重定向。需要知道用户 JID \"\n\"才能使重定向正常工作。\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:9\nmsgid \"Available Implementations\"\nmsgstr \"可用的实现\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:11\nmsgid \"\"\n\"Tigase implementation is, as usual, extensible and allows for different, \"\n\"pluggable redirection strategies that implement the ``SeeOtherHostIfc`` \"\n\"interface.\"\nmsgstr \"Tigase 实现像往常一样是可扩展的，并允许实现 ``SeeOtherHostIfc`` 接口的不同的，可插入的重定向策略。\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:13\nmsgid \"Currently there are three strategies available:\"\nmsgstr \"目前有三种可用的策略：\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:15\nmsgid \"\"\n\"``SeeOtherHost`` - most basic implementation returning either single host\"\n\" configured in ``config.tdsl`` file or name of the current host;\"\nmsgstr \"``SeeOtherHost`` - 最基本的实现返回在 ``config.tdsl`` 文件中配置的单个主机或当前主机的名称；\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:17\nmsgid \"\"\n\"``SeeOtherHostHashed`` (default) - default implementation for cluster \"\n\"environment of SeeOtherHostIfc returning redirect host based on the hash \"\n\"value of the user’s JID; list of the available nodes from which a \"\n\"selection would be made is by default composed and reflects all connected\"\n\" nodes, alternatively hosts list can be configured in the config.tdsl;\"\nmsgstr \"\"\n\"``SeeOtherHostHashed`` （默认） - SeeOtherHostIfc 基于用户 JID \"\n\"哈希值返回重定向主机的集群环境的默认实现；默认情况下，将从中进行选择的可用节点列表由所有连接的节点组成并反映所有连接的节点，或者可以在 \"\n\"config.tdsl 中配置主机列表；\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:19\nmsgid \"\"\n\"``SeeOtherHostDB`` - extended implementation of SeeOtherHost using \"\n\"redirect information from database in the form of pairs ``user_id`` and \"\n\"``node_id`` to which given user should be redirected.\"\nmsgstr \"\"\n\"``SeeOtherHostDB`` -SeeOtherHost 的扩展实现使用来自数据库的重定向信息，以 ``user_id`` 和 \"\n\"``node_id`` 对的形式，给定的用户应该被重定向到。\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:21\nmsgid \"\"\n\"``SeeOtherHostDualIP`` - matches internal Tigase cluster nodes against \"\n\"the lookup table to provide relevant redirection hostname/IP (by default \"\n\"internal Tigase tig_cluster_nodes table will be used)\"\nmsgstr \"\"\n\"``SeeOtherHostDualIP`` - 将内部 Tigase 集群节点与查找表匹配以提供相关的重定向主机名/IP（默认情况下将使用内部 \"\n\"Tigase tig_cluster_nodes 表）\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:24\nmsgid \"Configuration Options\"\nmsgstr \"配置选项\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:26\nmsgid \"\"\n\"The most basic configuration is related to the choice of actual \"\n\"redirection implementation by declaring class for each connector:\"\nmsgstr \"最基本的配置是通过为每个连接器声明类来选择实际的重定向实现：\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:40\nmsgid \"Possible values are:\"\nmsgstr \"可能的值为：\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:42\nmsgid \"``tigase.server.xmppclient.SeeOtherHost``\"\nmsgstr \"``tigase.server.xmppclient.SeeOtherHost``\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:44\nmsgid \"``tigase.server.xmppclient.SeeOtherHostHashed``\"\nmsgstr \"``tigase.server.xmppclient.SeeOtherHostHashed``\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:46\nmsgid \"``tigase.server.xmppclient.SeeOtherHostDB``\"\nmsgstr \"``tigase.server.xmppclient.SeeOtherHostDB``\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:48\nmsgid \"``tigase.server.xmppclient.SeeOtherHostDualIP``\"\nmsgstr \"``tigase.server.xmppclient.SeeOtherHostDualIP``\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:50\nmsgid \"``none`` - disables redirection\"\nmsgstr \"``none`` - 禁用重定向\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:52\nmsgid \"\"\n\"All options are configured on a per-connection-manager basis, thus all \"\n\"options need to be prefixed with the corresponding connection manager ID,\"\n\" i.e. c2s, bosh or ws; we will use c2s in the examples:\"\nmsgstr \"\"\n\"所有选项都是基于每个连接管理器配置的，因此所有选项都需要以相应的连接管理器 ID 为前缀，即 c2s、bosh 或 ws；我们将在示例中使用 \"\n\"c2s：\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:63\nmsgid \"\"\n\"``'default-host' = 'host1;host2;host3'`` - a semicolon separated list of \"\n\"hosts to be used for redirection.\"\nmsgstr \"``'default-host' = 'host1;host2;host3'`` - 用于重定向的主机的分号分隔列表。\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:65\nmsgid \"\"\n\"``'phases' = []`` - an array of phases in which redirection should be \"\n\"active, currently possible values are:\"\nmsgstr \"``'phases' = []`` - 重定向应该处于活动状态的阶段数组，当前可能的值是：\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:67\nmsgid \"``OPEN`` which enables redirection during opening of the XMPP stream;\"\nmsgstr \"``OPEN`` 在打开 XMPP 流期间启用重定向；\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:69\nmsgid \"``LOGIN`` which enables redirection upon authenticating user session;\"\nmsgstr \"``LOGIN`` 在验证用户会话时启用重定向；\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:71\nmsgid \"By default redirection is currently enabled only in the ``OPEN`` phase.\"\nmsgstr \"默认情况下，重定向当前仅在 ``OPEN`` 阶段启用。\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:74\nmsgid \"SeeOtherHostDB\"\nmsgstr \"SeeOtherHostDB\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:76\nmsgid \"For ``SeeOtherHostDB`` implementation there are additional options:\"\nmsgstr \"对于 ``SeeOtherHostDB`` 实现，还有其他选项：\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:87\nmsgid \"\"\n\"``db-url`` - a JDBC connection URI which should be used to query redirect\"\n\" information; if not configured the default ``dataSource`` will be used;\"\nmsgstr \"``db-url`` - 用于查询重定向信息的 JDBC 连接 URI；如果未配置，将使用默认的 ``dataSource``；\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:89\nmsgid \"``get-host-query`` - a SQL query which should return redirection hostname;\"\nmsgstr \"``get-host-query`` - 应返回重定向主机名的 SQL 查询；\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:91\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:120\nmsgid \"\"\n\"``get-all-data-query`` - a SQL helper query which should return all \"\n\"redirection data from database;\"\nmsgstr \"``get-all-data-query`` - 一个 SQL 辅助查询，它应该从数据库返回所有重定向数据；\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:93\nmsgid \"``get-all-query-timeout`` - allows to set timeout for executed queries.\"\nmsgstr \"``get-all-query-timeout`` - 允许为执行的查询设置超时。\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:96\nmsgid \"SeeOtherHostDualIP\"\nmsgstr \"SeeOtherHostDualIP\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:98\nmsgid \"\"\n\"This mechanisms matches internal Tigase cluster nodes against the lookup \"\n\"table to provide matching and relevant redirection hostname/IP. By \"\n\"default internal Tigase ``tig_cluster_nodes`` table is used (and \"\n\"appropriate repository implementation will be used).\"\nmsgstr \"\"\n\"这种机制将内部 Tigase 集群节点与查找表进行匹配，以提供匹配和相关的重定向主机名/IP。默认情况下，使用内部 Tigase \"\n\"``tig_cluster_nodes`` 表（并且将使用适当的存储库实现）。\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:100\nmsgid \"\"\n\"To enable this redirection mechanism following configuration / class \"\n\"should be used. Note that for global use, all connection managers must \"\n\"have the same class defined. You can define each connection manager \"\n\"individually.\"\nmsgstr \"要启用此重定向机制，应使用配置/类。请注意，对于全局使用，所有连接管理器必须定义相同的类。您可以单独定义每个连接管理器。\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:114\nmsgid \"It offers following configuration options:\"\nmsgstr \"它提供以下配置选项：\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:116\nmsgid \"\"\n\"``data-source`` - configuration of the source of redirection information \"\n\"- by default internal Tigase ``tig_cluster_nodes`` table will be used \"\n\"(and appropriate repository implementation will be used); alternatively \"\n\"it’s possible to use ``eventbus`` source;\"\nmsgstr \"\"\n\"``data-source`` - 重定向信息源的配置 - 默认情况下将使用内部 Tigase ``tig_cluster_nodes`` \"\n\"表（并将使用适当的存储库实现）；或者，可以使用 ``eventbus`` 源；\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:118\nmsgid \"\"\n\"``db-url`` - a JDBC connection URI which should be used to query redirect\"\n\" information; if not configured ``user-db-uri`` will be used;\"\nmsgstr \"``db-url`` - 用于查询重定向信息的 JDBC 连接 URI；如果未配置 ``user-db-uri`` 其将被使用；\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:122\nmsgid \"``get-all-query-timeout`` - allows to set timeout for executed queries;\"\nmsgstr \"``get-all-query-timeout`` - 允许为执行的查询设置超时；\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:124\nmsgid \"\"\n\"``fallback-redirection-host`` - if there is no redirection information \"\n\"present (i.e. secondary hostname is not configured for the particular \"\n\"node) redirection won’t be generated; with this it’s possible to \"\n\"configure fallback redirection address.\"\nmsgstr \"\"\n\"``fallback-redirection-host`` - \"\n\"如果不存在重定向信息（即未为特定节点配置辅助主机名），则不会生成重定向；这样就可以配置回退重定向地址。\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:126\nmsgid \"All options are configured or on per-component basis:\"\nmsgstr \"所有选项均已配置或基于每个组件：\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:140\nmsgid \"EventBus as a source of information\"\nmsgstr \"EventBus 作为信息源\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:142\nmsgid \"\"\n\"It’s possible to utilize EventBus and internal Tigase events as a source \"\n\"of redirection data. In order to do that ``eventbus-repository-\"\n\"notifications`` needs to be enabled in ClusterConnectionManager:\"\nmsgstr \"\"\n\"可以利用 EventBus 和内部 Tigase 事件作为重定向数据的来源。为此，需要在 ClusterConnectionManager 中启用\"\n\" ``eventbus-repository-notifications``：\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:152\nmsgid \"Auxiliary setup options\"\nmsgstr \"辅助设置选项\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:155\nmsgid \"Enforcing redirection\"\nmsgstr \"强制重定向\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:157\nmsgid \"\"\n\"It’s possible to enforce redirection of connections on the particular \"\n\"port of connection manager with ``force-redirect-to`` set to Integer with\"\n\" the following general setting option:\"\nmsgstr \"可以使用以下常规设置选项在连接管理器的特定端口上强制重定向连接，并将 ``force-redirect-to`` 设置为整数：\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:169\nmsgid \"\"\n\"for example, enable additional port ``5322`` for ``c2s`` connection \"\n\"manager and enforce all connections to be redirected to port ``5222`` (it\"\n\" will utilize hostname retrieved from ``SeeOtherHost`` implementation and\"\n\" will be only used when such value is returned):\"\nmsgstr \"\"\n\"例如，为 ``c2s`` 连接管理器启用额外的端口 ``5322``，并强制将所有连接重定向到端口 ``5222`` （它将利用从 \"\n\"``SeeOtherHost`` 实现中检索到的主机名，并且仅返回此类值时使用）：\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:186\nmsgid \"Configuring hostnames\"\nmsgstr \"配置主机名\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:188\nmsgid \"\"\n\"To fully utilize ``SeeOtherHostDualIP`` setup in automated fashion it’s \"\n\"now possible to provide both primary (*internal*) and secondary \"\n\"(*external*) hostname/IP (they need to be correct, \"\n\"``InetAddress.getByName( property );`` will be used to verify \"\n\"correctness). It can be done via JVM properties ``tigase-primary-\"\n\"address`` and ``tigase-secondary-address``. You can also utilize \"\n\"different implementation of DNS resolver by providing class implementing \"\n\"``tigase.util.DNSResolverIfc`` interface as value to ``resolver-class`` \"\n\"property. Those properties can be set via ``etc/tigase.conf`` \"\n\"(uncommenting following lines, or manually exposing in environment):\"\nmsgstr \"\"\n\"为了以自动化方式充分利用 ``SeeOtherHostDualIP`` \"\n\"设置，现在可以提供主要（*内部*）和辅助（*外部*）主机名/IP（它们需要正确，``InetAddress.getByName(property);``\"\n\" 将用于验证正确性）。它可以通过 JVM 属性 ``tigase-primary-address`` 和 ``tigase-secondary-\"\n\"address`` 来完成。您还可以通过提供实现 ``tigase.util.DNSResolverIfc`` 接口的类作为 \"\n\"``resolver-class`` 属性的值来利用DNS解析器的不同实现。这些属性可以通过 ``etc/tigase.conf`` \"\n\"设置（取消注释以下行，或在环境中手动公开）：\"\n\n#: ../../Tigase_Administration/Components/Load_Balancing.inc:197\nmsgid \"or in the ``etc/config.tdsl`` (they will be converted to JVM properties):\"\nmsgstr \"或在 ``etc/config.tdsl`` 中（它们将被转换为 JVM 属性）：\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:4\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Intro.inc:2\nmsgid \"External Component Configuration\"\nmsgstr \"外部组件配置\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Intro.inc:4\nmsgid \"\"\n\"Tigase can connect to external components, this guide will show you how \"\n\"this can be accomplished.\"\nmsgstr \"Tigase 可以连接到外部组件，本指南将向您展示如何实现这一点。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Intro.inc:6\nmsgid \"\"\n\"Configuration follows the same standards as all other components. It is \"\n\"also much more powerful as a single Tigase instance can control many \"\n\"TCP/IP ports and many external components on each port and even allows \"\n\"for multiple connections for the same component. It supports both \"\n\"XEP-0114 and XEP-0225 with protocol auto-detection mechanisms. Protocols \"\n\"are pluggable so more protocols can be supported or custom extensions to \"\n\"existing protocols can be added.\"\nmsgstr \"\"\n\"配置遵循与所有其他组件相同的标准。它也更强大，因为单个 Tigase 实例可以控制多个 TCP/IP \"\n\"端口和每个端口上的许多外部组件，甚至允许同一组件的多个连接。它支持具有协议自动检测机制的 XEP-0114 和 \"\n\"XEP-0225。协议是可插拔的，因此可以支持更多协议，或者可以添加对现有协议的自定义扩展。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Intro.inc:8\nmsgid \"\"\n\"The implementation also supports a scripting API and new domains with \"\n\"passwords can be added at run-time using ad-hoc commands. New scripts can\"\n\" be loaded to even further control all connected external components.\"\nmsgstr \"该实现还支持脚本 API，并且可以在运行时使用 ad-hoc 命令添加带有密码的新域。可以加载新脚本以进一步控制所有连接的外部组件。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Intro.inc:10\nmsgid \"\"\n\"Pages in this guide describe in details all the administration aspects of\"\n\" setting up and managing external components.\"\nmsgstr \"本指南中的页面详细描述了设置和管理外部组件的所有管理方面。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Intro.inc:12\nmsgid \":ref:`External Component Configuration<External-Component-Configuration>`\"\nmsgstr \":ref:`外部组件配置<External-Component-Configuration>`\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Intro.inc:14\nmsgid \":ref:`Tigase as an External Component<Tigase-as-an-External-Component>`\"\nmsgstr \":ref:`Tigase 作为外部组件<Tigase-as-an-External-Component>`\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Intro.inc:16\nmsgid \"\"\n\":ref:`Load Balancing External Components in Cluster Mode<Load-Balancing-\"\n\"External-Components-in-Cluster-Mode>`\"\nmsgstr \"\"\n\":ref:`集群模式下的外部组件负载均衡<Load-Balancing-External-Components-in-\"\n\"Cluster-Mode>`\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:6\nmsgid \"\"\n\"As for all Tigase components you can load and configure external \"\n\"components via the ``config.tdsl`` file described in details in the \"\n\":ref:`DSL configuration<dslConfig>` section. This document describes how \"\n\"to enable the component and set the initial configuration to accept or \"\n\"initiate connections for an external component.\"\nmsgstr \"\"\n\"对于所有 Tigase 组件，您可以通过 :ref:`DSL 配置<dslConfig>` 部分中详细描述的 \"\n\"``config.tdsl`` 文件加载和配置外部组件。本文档描述了如何启用组件并设置初始配\"\n\"置以接受或启动外部组件的连接。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:8\nmsgid \"\"\n\"First thing to do is to specify the component class and the component \"\n\"name which must be unique within the Tigase installation. The most \"\n\"commonly name used is ``ext`` and the class is \"\n\"``tigase.server.ext.ComponentProtocol`` (class doesn’t have to be \"\n\"specified when using default name).\"\nmsgstr \"\"\n\"首先要做的是指定组件类和组件名称，这在 Tigase 安装中必须是唯一的。最常用的名称是 ``ext`` ，类是 \"\n\"``tigase.server.ext.ComponentProtocol`` （使用默认名称时不必指定类）。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:10\nmsgid \"\"\n\"The following line in the ``config.tdsl`` will load the component during \"\n\"the server startup time:\"\nmsgstr \"``config.tdsl`` 中的以下行将在服务器启动期间加载组件：\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:16\nmsgid \"\"\n\"While this would load the component, without any additional \"\n\"configurations provided, the component would be practically useless. It \"\n\"is necessary to configure the virtual host domains of the external \"\n\"component during run-time via ad-hoc commands to make use of this \"\n\"component.\"\nmsgstr \"虽然这会加载组件，但没有提供任何额外的配置，该组件实际上是无用的。有必要在运行时通过 ad-hoc 命令配置外部组件的虚拟主机域以使用该组件。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:18\nmsgid \"\"\n\"You may additionally configure the :ref:`bind-ext-\"\n\"hostnames<bindExtHostnames>` property.\"\nmsgstr \"您可以另外配置 :ref:`bind-ext-hostnames<bindExtHostnames>` 属性。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:20\nmsgid \"\"\n\"To configure external component connections using Admin UI you need to \"\n\"open Admin UI web page (if you are logged in the same computer on which \"\n\"Tigase XMPP Server is running by default it should be available at \"\n\"http://localhost:8080/admin/). Then you should click on ``Configuration``\"\n\" on the left side of the Admin UI web page and then select ``Add new \"\n\"item`` on ``ext`` component or by execution corresponding ad-hoc command \"\n\"on ``ext`` component using ad-hoc capable XMPP client, ie. `Psi <http\"\n\"://psi-im.org>`__.\"\nmsgstr \"\"\n\"要使用 Admin UI 配置外部组件连接，您需要打开 Admin UI 网页（如果您登录到默认情况下运行 Tigase XMPP \"\n\"服务器的同一台计算机上，它应该在 http://localhost:8080/admin/ 可用）。然后您应该单击管理 UI 网页左侧的 \"\n\"``Configuration``，然后在 ``ext`` 组件上选择 ``Add new item`` 或在 ``ext`` 上使用具有 ad-\"\n\"hoc 能力的 XMPP 客户端的组件执行相应的 ad-hoc 命令 ，即 `Psi <http://psi-im.org>`__。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:22\nmsgid \"|adminui ext add item button|\"\nmsgstr \"|adminui ext add item button|\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:58\nmsgid \"adminui ext add item button\"\nmsgstr \"adminui ext add item button\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:24\nmsgid \"\"\n\"You will be presented with a form which you should fill to configure \"\n\"external component connection details:\"\nmsgstr \"您将看到一个表单，您应该填写该表单以配置外部组件连接详细信息：\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:26\nmsgid \"|adminui ext add item form|\"\nmsgstr \"|adminui ext add item form|\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:59\nmsgid \"adminui ext add item form\"\nmsgstr \"adminui ext add item form\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:28\nmsgid \"*Domain name* - external component domain name (``muc.devel.tigase.org``)\"\nmsgstr \"*域名* - 外部组件域名（``muc.devel.tigase.org``）\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:30\nmsgid \"\"\n\"*Domain password* - password for authentication of the external component\"\n\" connection (``muc-pass``)\"\nmsgstr \"*域密码* - 用于验证外部组件连接的密码 (``muc-pass``)\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:32\nmsgid \"\"\n\"*Connection type* - ``accept`` to make component wait for connection or \"\n\"``connect`` force component to connect to the server (``connect``)\"\nmsgstr \"*连接类型* - ``accept`` 使组件等待连接或 ``connect`` 强制组件连接到服务器（``connect``）\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:34\nmsgid \"\"\n\"*Port number* - port on which component should wait for connection or on \"\n\"which it try to connect (``5270``)\"\nmsgstr \"*端口号* - 组件应等待连接或尝试连接的端口（``5270``）\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:36\nmsgid \"\"\n\"*Remote host* - host to connect to (``devel.tigase.org``) *(may be left \"\n\"blank if component will only accept connections)*\"\nmsgstr \"*远程主机* - 连接到的主机（``devel.tigase.org``）(*如果组件只接受连接，可以留空*)\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:38\nmsgid \"*Protocol* - id of protocol used for establishing connection\"\nmsgstr \"*Protocol* - 用于建立连接的协议 ID\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:40\nmsgid \"if connection type is ``connect``:\"\nmsgstr \"如果连接类型是 ``connect``:\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:42\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:50\nmsgid \"\"\n\"``XEP-0114: Jabber Component Protocol (accept)`` - for `XEP-0114: Jabber \"\n\"Component Protocol <https://xmpp.org/extensions/xep-0114.html>`__\"\nmsgstr \"\"\n\"``XEP-0114: Jabber Component Protocol (accept)`` - 用于 `XEP-0114：Jabber \"\n\"组件协议 <https://xmpp.org/extensions/xep-0114.html>`__\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:44\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:52\nmsgid \"\"\n\"``XEP-0225: Component Connections`` - for `XEP-0225: Component \"\n\"Connections <https://xmpp.org/extensions/xep-0225.html>`__\"\nmsgstr \"\"\n\"``XEP-0225: Component Connections`` - 用于 `XEP-0225：组件连接 \"\n\"<https://xmpp.org/extensions/xep-0225.html>`__\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:46\nmsgid \"if connection type is ``accept``:\"\nmsgstr \"如果连接类型是 ``accept``:\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:48\nmsgid \"\"\n\"``Autodetect`` - for automatic detection of protocol used by incoming \"\n\"connection *(recommended)*\"\nmsgstr \"``Autodetect`` - 用于自动检测传入连接使用的协议(*推荐*)\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:54\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:94\nmsgid \"Additional options may be left with defaults.\"\nmsgstr \"其他选项可能会保留默认值。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Configuration.inc:56\nmsgid \"\"\n\"Later on if you would like to modify this values, you can do that using \"\n\"Admin UI by clicking on ``Configuration`` and ``Remove an item`` or \"\n\"``Update item configuration`` at ``ext`` component or by execution \"\n\"corresponding ad-hoc commands on ``ext`` component using ad-hoc capable \"\n\"XMPP client, ie. `Psi <http://psi-im.org>`__.\"\nmsgstr \"\"\n\"稍后，如果您想修改此值，可以使用管理 UI 通过单击在 ``ext`` 组件里的 ``Configuration`` 和 ``Remove an \"\n\"item`` 或 ``Update item configuration`` 或者通过使用具有 ad-hoc 能力的 XMPP 客户端在 \"\n\"``ext`` 组件上执行相应的 ad-hoc 命令，即 `Psi <http://psi-im.org>`__。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:4\nmsgid \"Tigase as an External Component\"\nmsgstr \"Tigase 作为外部组件\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:6\nmsgid \"\"\n\"There are cases when you want to deploy one or more Tigase components \"\n\"separately from the main server, or perhaps you want to run some Tigase \"\n\"components connecting to a different XMPP server, or perhaps you work on \"\n\"a component and you do not want to restart the main server every time you\"\n\" make a change.\"\nmsgstr \"\"\n\"在某些情况下，您希望将一个或多个 Tigase 组件与主服务器分开部署，或者您可能希望运行一些连接到不同 XMPP 服务器的 Tigase \"\n\"组件，或者您可能正在处理一个组件并且您不想重新启动每次进行更改时的主服务器。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:8\nmsgid \"\"\n\"There is a way to run the Tigase server in *external component mode*. In \"\n\"fact you can run any of Tigase’s components as an external component and \"\n\"connect them to the main XMPP server either via `XEP-0114 \"\n\"<http://xmpp.org/extensions/xep-0114.html>`__ or `XEP-0225 \"\n\"<http://xmpp.org/extensions/xep-0225.html>`__ connection.\"\nmsgstr \"\"\n\"有一种方法可以在 *外部组件模式* 下运行 Tigase 服务器。事实上，您可以将 Tigase 的任何组件作为外部组件运行，并通过 \"\n\"`XEP-0114 <http://xmpp.org/extensions/xep-0114.html>`__ 或 `XEP-0225 \"\n\"<http://xmpp.org/extensions/xep-0225.html>`__ 将它们连接到主 XMPP 服务器。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:10\nmsgid \"Let’s look at the examples…​\"\nmsgstr \"让我们看看例子……​\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:13\nmsgid \"Usage with shared database (since version 8.0.0)\"\nmsgstr \"与共享数据库一起使用（自 8.0.0 版起）\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:15\nmsgid \"\"\n\"When you are using Tigase server 8.0.0 or newer in the \\\"external \"\n\"component mode\\\" while using shared default \\\"user repository\\\" and you \"\n\"have main server also running Tigase XMPP Server 8.0.0 or newer, then you\"\n\" can benefit from the remote management of the component connections from\"\n\" the main server. To use that, you need to enable external component and \"\n\"external component manager on the main server by adding following line to\"\n\" the config file:\"\nmsgstr \"\"\n\"当您在 \\\"external component mode\\\" 下使用 Tigase 服务器 8.0.0 或更高版本，同时使用共享默认的 \"\n\"\\\"user repository\\\" 并且您的主服务器也运行 Tigase XMPP Server 8.0.0 \"\n\"或更高版本时，您可以从远程管理中受益来自主服务器的组件连接。要使用它，您需要通过在配置文件中添加以下行来在主服务器上启用外部组件和外部组件管理器：\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:22\nmsgid \"\"\n\"With that in place you can use Admin UI or ad-hoc commands available at \"\n\"``ext-man`` component of the main server to configure connection details \"\n\"of the servers running in the ``component`` mode.\"\nmsgstr \"\"\n\"有了它，您可以使用主服务器的 ``ext-man`` 组件中可用的管理 UI 或临时命令来配置 ``component`` \"\n\"模式下运行的服务器的连接详细信息。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:24\nmsgid \"\"\n\"In Admin UI you click on ``Configuration`` section and select ``Add new \"\n\"item`` at the ``ext-man`` component, which will present you with a \"\n\"following form to fill in external component connectivity details:\"\nmsgstr \"\"\n\"在管理 UI 中，您单击 ``Configuration`` 部分并在 ``ext-man`` 组件中选择 ``Add new \"\n\"item``，这将显示以下表单以填写外部组件连接详细信息：\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:26\nmsgid \"|adminui extman add item form|\"\nmsgstr \"|adminui extman add item form|\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:285\nmsgid \"adminui extman add item form\"\nmsgstr \"adminui extman add item form\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:29\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:161\nmsgid \"A Simple Case - MUC as an External Component\"\nmsgstr \"一个简单的案例 - MUC 作为外部组件\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:31\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:163\nmsgid \"A few assumptions:\"\nmsgstr \"几个假设：\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:33\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:165\nmsgid \"\"\n\"We want to run a MUC component for a domain: ``muc.devel.tigase.org`` and\"\n\" password ``muc-pass``\"\nmsgstr \"我们想为一个域运行一个 MUC 组件：``muc.devel.tigase.org`` 和密码 ``muc-pass``\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:35\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:167\nmsgid \"\"\n\"The main server works at an address: devel.tigase.org and for the same \"\n\"virtual domain\"\nmsgstr \"主服务器工作在一个地址：devel.tigase.org 和相同的虚拟域\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:37\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:169\nmsgid \"\"\n\"We want to connect to the server using `XEP-0114 \"\n\"<http://xmpp.org/extensions/xep-0114.html>`__ protocol and port ``5270``.\"\nmsgstr \"\"\n\"我们想使用 `XEP-0114 <http://xmpp.org/extensions/xep-0114.html>`__ 协议和端口 \"\n\"``5270`` 连接到服务器。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:39\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:171\nmsgid \"\"\n\"There is a special configuration type for this case which simplifies \"\n\"setting needed to run Tigase as an external component:\"\nmsgstr \"这种情况有一种特殊的配置类型，它简化了将 Tigase 作为外部组件运行所需的设置：\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:45\nmsgid \"\"\n\"Knowing that we can now create simple configuration file for Tigase XMPP \"\n\"Server:\"\nmsgstr \"知道我们现在可以为 Tigase XMPP 服务器创建简单的配置文件：\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:68\nmsgid \"\"\n\"where ``master_server_default_database_url`` is the same URL as the one \"\n\"used on the main server for default data source.\"\nmsgstr \"其中 ``master_server_default_database_url`` 与主服务器上用于默认数据源的 URL 相同。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:70\nmsgid \"\"\n\"With that in place we can use ad-hoc commands or Admin UI on the main \"\n\"server to configure Tigase XMPP Server to accept external component \"\n\"connections and to connect from the external component to the master \"\n\"server.\"\nmsgstr \"\"\n\"有了这些，我们可以在主服务器上使用 ad-hoc 命令或管理 UI 来配置 Tigase XMPP \"\n\"服务器以接受外部组件连接并从外部组件连接到主服务器。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:72\nmsgid \"\"\n\"**Adding external component connection settings to the manager (ext-man) \"\n\"using Admin UI.**\"\nmsgstr \"**使用管理 UI 将外部组件连接设置添加到管理器 (ext-man)。**\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:74\nmsgid \"|adminui extman add item form external muc|\"\nmsgstr \"|adminui extman add item form external muc|\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:286\nmsgid \"adminui extman add item form external muc\"\nmsgstr \"adminui extman add item form external muc\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:76\nmsgid \"You need to pass:\"\nmsgstr \"你需要通过：\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:78\nmsgid \"Domain name - external component domain name (``muc.devel.tigase.org``)\"\nmsgstr \"域名 - 外部组件域名（``muc.devel.tigase.org``）\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:80\nmsgid \"\"\n\"Domain password - password for authentication of the external component \"\n\"connection (``muc-pass``)\"\nmsgstr \"域密码 - 用于验证外部组件连接的密码 (``muc-pass``)\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:82\nmsgid \"\"\n\"Connection type - ``accept`` to make component wait for connection or \"\n\"``connect`` force component to connect to the server (``connect``)\"\nmsgstr \"连接类型 - ``accept`` 使组件等待连接或 ``connect`` 强制组件连接到服务器（``connect``）\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:84\nmsgid \"\"\n\"Port number - port on which component should wait for connection or on \"\n\"which it try to connect (``5270``)\"\nmsgstr \"端口号 - 组件应该等待连接或尝试连接的端口（``5270``）\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:86\nmsgid \"Remote host - host to connect to (``devel.tigase.org``)\"\nmsgstr \"远程主机 - 要连接的主机（``devel.tigase.org``）\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:88\nmsgid \"Protocol - id of protocol used for establishing connection\"\nmsgstr \"协议 - 用于建立连接的协议 ID\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:90\nmsgid \"\"\n\"``XEP-0114: Jabber Component Protocol (accept)`` - establish connection \"\n\"using `XEP-0114: Jabber Component Protocol \"\n\"<https://xmpp.org/extensions/xep-0114.html>`__\"\nmsgstr \"\"\n\"`XEP-0114：Jabber 组件协议（接受）` - 使用 `XEP-0114：Jabber 组件协议 \"\n\"<https://xmpp.org/extensions/xep-0114.html>`__ 建立连接\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:92\nmsgid \"\"\n\"``XEP-0225: Component Connections`` - establish connection using \"\n\"`XEP-0225: Component Connections \"\n\"<https://xmpp.org/extensions/xep-0225.html>`__\"\nmsgstr \"\"\n\"``XEP-0225: Component Connections`` - 使用 `XEP-0225: Component Connections\"\n\" <https://xmpp.org/extensions/xep-0225.html>`__ 建立连接\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:96\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:211\nmsgid \"More Components\"\nmsgstr \"更多组件\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:98\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:213\nmsgid \"\"\n\"Suppose you want to run more than one component as an external components\"\n\" within one Tigase instance. Let’s add another - PubSub component to the \"\n\"configuration above and see how to set it up.\"\nmsgstr \"假设您想在一个 Tigase 实例中运行多个组件作为外部组件。让我们在上面的配置中添加另一个 - PubSub 组件，看看如何设置它。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:100\nmsgid \"\"\n\"The most straightforward way is just to add another component to the \"\n\"server running in the component mode for the component domain\"\nmsgstr \"最直接的方法就是为组件域添加另一个组件到以组件模式运行的服务器\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:123\nmsgid \"\"\n\"and then to add new connection domain to the main server external \"\n\"component settings and to the external component manager settings. You \"\n\"basically do the same thing as you did while adding only MUC component as\"\n\" the external component.\"\nmsgstr \"然后将新的连接域添加到主服务器外部组件设置和外部组件管理器设置。在只添加 MUC 组件作为外部组件时，您基本上做了同样的事情。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:125\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:248\nmsgid \"\"\n\"Please note however that we are opening two connections to the same \"\n\"server. This can waste resources and over-complicate the system. For \"\n\"example, what if we want to run even more components? Opening a separate \"\n\"connection for each component is a tad overkill.\"\nmsgstr \"但是请注意，我们正在打开到同一服务器的两个连接。这会浪费资源并使系统过于复杂。例如，如果我们想运行更多的组件怎么办？为每个组件打开一个单独的连接有点矫枉过正。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:127\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:250\nmsgid \"\"\n\"In fact there is a way to reuse the same connection for all component \"\n\"domains running as an external component. The property ``bind-ext-\"\n\"hostnames`` contains a comma separated list of all hostnames (external \"\n\"domains) which should reuse the existing connection.\"\nmsgstr \"\"\n\"事实上，有一种方法可以为作为外部组件运行的所有组件域重复使用相同的连接。属性 ``bind-ext-hostnames`` \"\n\"包含一个逗号分隔的所有主机名（外部域）列表，这些主机名应该可以重复使用现有的连接。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:129\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:252\nmsgid \"\"\n\"There is one catch however. Since you are reusing connections (hostname \"\n\"binding is defined in `XEP-0225 \"\n\"<http://xmpp.org/extensions/xep-0225.html>`__ only), you must use this \"\n\"protocol for the functionality.\"\nmsgstr \"\"\n\"然而，有一个问题。由于您正在重复使用连接（主机名绑定仅在 `XEP-0225 \"\n\"<http://xmpp.org/extensions/xep-0225.html>`__ 中定义），因此您必须使用此协议来实现功能。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:131\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:254\nmsgid \"\"\n\"Here is an example configuration with a single connection over the \"\n\"`XEP-0225 <http://xmpp.org/extensions/xep-0225.html>`__ protocol used by \"\n\"both external domains:\"\nmsgstr \"\"\n\"这是一个示例配置，通过两个外部域使用的 `XEP-0225 \"\n\"<http://xmpp.org/extensions/xep-0225.html>`__ 协议进行单个连接：\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:156\nmsgid \"\"\n\"With this configuration you do not need to configure entries in ``ext-\"\n\"man`` for PubSub component, only for MUC component but you need to user \"\n\"``client`` as the value for protocol field.\"\nmsgstr \"\"\n\"使用此配置，您无需在 ``ext-man`` 中为 PubSub 组件配置条目，只需为 MUC 组件配置条目，但您需要使用 ``client`` \"\n\"作为协议字段的值。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:159\nmsgid \"Usage with a separate database\"\nmsgstr \"使用单独的数据库\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:177\nmsgid \"\"\n\"This generates a configuration for Tigase with only one component loaded \"\n\"by default - the component used for external component connection. If you\"\n\" use this configuration type, your ``config.tdsl`` file may look like \"\n\"this:\"\nmsgstr \"\"\n\"这会为 Tigase 生成一个配置，默认情况下只加载一个组件 - 用于外部组件连接的组件。如果您使用这种配置类型，您的 \"\n\"``config.tdsl`` 文件可能如下所示：\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:200\nmsgid \"\"\n\"To make this new instance connect to the Tigase XMPP Server, you need to \"\n\"create one more file with external connection configuration at \"\n\"``etc/externalComponentItems`` which will be loaded to the local database\"\n\" and then removed.\"\nmsgstr \"\"\n\"要使这个新实例连接到 Tigase XMPP 服务器，您需要在 ``etc/externalComponentItems`` \"\n\"处再创建一个具有外部连接配置的文件，该文件将被加载到本地数据库然后删除。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:208\nmsgid \"\"\n\"While loading configuration from ``etc/externalComponentItems`` file is \"\n\"supported, we recommend usage of shared database if possible. In future \"\n\"this method may be deprecated.\"\nmsgstr \"虽然支持从 ``etc/externalComponentItems`` 文件加载配置，但我们建议尽可能使用共享数据库。将来可能会弃用此方法。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:215\nmsgid \"\"\n\"The most straightforward way is just to add another external component \"\n\"connection to the main server for the component domain using Admin UI or \"\n\"ad-hoc command on the main server.\"\nmsgstr \"最直接的方法是在主服务器上使用 Admin UI 或 ad-hoc 命令为组件域添加另一个外部组件连接到主服务器。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:217\nmsgid \"\"\n\"Then we can use following configuration on the server running in the \"\n\"``component`` mode:\"\nmsgstr \"然后我们可以在以 ``component`` 模式运行的服务器上使用以下配置：\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:241\nmsgid \"\"\n\"and we need to create a file with configuration for external component \"\n\"connection which will be loaded to the internal database:\"\nmsgstr \"我们需要创建一个包含外部组件连接配置的文件，该文件将加载到内部数据库：\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Tigase_as_External.inc:279\nmsgid \"and example of the external connections configuration file:\"\nmsgstr \"以及外部连接配置文件的示例：\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:4\nmsgid \"Load Balancing External Components in Cluster Mode\"\nmsgstr \"集群模式下的外部组件负载均衡\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:6\nmsgid \"\"\n\"This document describes how to load balance any external components using\"\n\" Tigase XMPP Server and how to make Tigase’s components work as external \"\n\"components in a cluster mode.\"\nmsgstr \"\"\n\"本文档描述了如何使用 Tigase XMPP Server 对任何外部组件进行负载平衡，以及如何使 Tigase \"\n\"的组件在集群模式下作为外部组件工作。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:8\nmsgid \"\"\n\"*Please note, all configuration options described here apply to Tigase \"\n\"XMPP Server version 8.0.0 or later.*\"\nmsgstr \"*请注意，此处描述的所有配置选项都适用于 Tigase XMPP 服务器版本 8.0.0 或更高版本。*\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:10\nmsgid \"These are actually 2 separate topics:\"\nmsgstr \"这些实际上是 2 个独立的主题：\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:12\nmsgid \"\"\n\"One is to distribute load over many instances of a single component to \"\n\"handle larger traffic, or perhaps for high availability.\"\nmsgstr \"一种是将负载分布在单个组件的多个实例上以处理更大的流量，或者可能实现高可用性。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:14\nmsgid \"\"\n\"The second is to make Tigase’s components work as an external component \"\n\"and make it work in a cluster mode, even if the component itself does not\"\n\" support cluster mode.\"\nmsgstr \"第二是让 Tigase 的组件作为外部组件工作，使其工作在集群模式下，即使组件本身不支持集群模式。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:16\nmsgid \"\"\n\"Here are step by step instructions and configuration examples teaching \"\n\"how to achieve both goals.\"\nmsgstr \"以下是分步说明和配置示例，教您如何实现这两个目标。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:19\nmsgid \"Load Balancing External Component\"\nmsgstr \"负载平衡外部组件\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:21\nmsgid \"\"\n\"The first, and most simple scenario is to connect multiple instances of \"\n\"an external component to a single Tigase XMPP Server to distribute load.\"\nmsgstr \"第一个也是最简单的场景是将外部组件的多个实例连接到单个 Tigase XMPP 服务器以分配负载。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:23\nmsgid \"\"\n\"There are at least 2 reasons why this would be an optimal solution: one \"\n\"would be to spread load over more instances/machines and the second is to\"\n\" improve reliability in case one component fails the other one can take \"\n\"over the work.\"\nmsgstr \"至少有两个原因表明这将是最佳解决方案：一个是将负载分散到更多实例/机器上，第二个是提高可靠性，以防一个组件发生故障，另一个组件可以接管工作。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:25\nmsgid \"So here is a simple picture showing the use case.\"\nmsgstr \"所以这里有一张显示用例的简单图片。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:27\nmsgid \"|ExternalCompClustering002|\"\nmsgstr \"|ExternalCompClustering002|\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:82\nmsgid \"ExternalCompClustering002\"\nmsgstr \"ExternalCompClustering002\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:29\nmsgid \"\"\n\"We have a single machine running Tigase XMPP Server and 2 instances of \"\n\"the MUC component connecting to Tigase.\"\nmsgstr \"我们有一台运行 Tigase XMPP 服务器的机器和两个连接到 Tigase 的 MUC 组件实例。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:31\nmsgid \"\"\n\"On the server side we will enable ``ComponentProtocol`` component as we \"\n\"need to do to enable external component without clustering support.\"\nmsgstr \"在服务器端，我们将启用 ``ComponentProtocol`` 组件，因为我们需要在不支持集群的情况下启用外部组件。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:33\nmsgid \"\"\n\"Then using Admin UI we will add a new external component connection \"\n\"settings using ``Add item`` position for ``ext`` component in \"\n\"``Configuration`` section of the web page just as it is described in \"\n\"`External Component Configuration <#tigaseExternalComponent>`__ section.\"\nmsgstr \"\"\n\"然后使用 Admin UI，我们将在网页的 ``Configuration`` 部分中使用 ``Add item`` 位置为 ``ext`` \"\n\"组件添加新的外部组件连接设置，就像在 :ref:`External Component \"\n\"Configuration<tigaseExternalComponent>` 部分描述的那样。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:35\nmsgid \"|adminui ext add item form_1|\"\nmsgstr \"|adminui ext add item form_1|\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:83\nmsgid \"adminui ext add item form_1\"\nmsgstr \"adminui ext add item form_1\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:37\nmsgid \"\"\n\"The only change here is that we will specify value for field ``Load \"\n\"balancer class`` and we will use ``ReceiverBareJidLB`` as a value.\"\nmsgstr \"\"\n\"这里唯一的变化是我们将为字段 ``Load balancer class`` 指定值，我们将使用 ``ReceiverBareJidLB`` \"\n\"作为值。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:39\nmsgid \"\"\n\"The configuration for both instances of the MUC component (identical for \"\n\"both of them) can be done in the same way as it is done for a single \"\n\"instance of the MUC component. There is nothing to change here.\"\nmsgstr \"MUC 组件的两个实例的配置（两者相同）可以按照与 MUC 组件的单个实例相同的方式完成。这里没有什么可以改变的。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:41\nmsgid \"\"\n\"The difference is one small element in the server configuration. At the \"\n\"value of ``Load balancer class`` field in ``Add item`` form is set to \"\n\"**ReceiverBareJidLB**.\"\nmsgstr \"\"\n\"区别是服务器配置中的一个小元素。在 ``Add item`` 表单中的 ``Load balancer class`` 字段的值设置为 \"\n\"**ReceiverBareJidLB**。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:43\nmsgid \"\"\n\"This is the load balancing plugin class. Load balancing plugin decides \"\n\"how the traffic is distributed among different component connections that\"\n\" is different component instances. For the MUC component it makes sense \"\n\"to distribute the traffic based on the receiver bare JID because this is \"\n\"the MUC room address. This way we just distribute MUC rooms and traffic \"\n\"over different MUC component instances.\"\nmsgstr \"\"\n\"这是负载平衡插件类。负载平衡插件决定流量如何在不同组件连接（即不同组件实例）之间分配。对于 MUC 组件，基于接收者裸 JID \"\n\"分配流量是有意义的，因为这是 MUC 房间地址。这样，我们只需在不同的 MUC 组件实例上分配 MUC 房间和流量。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:45\nmsgid \"\"\n\"This distribution strategy does not always work for all possible \"\n\"components however. For transports for example this would not work at \"\n\"all. A better way to spread load for transports would be based on the \"\n\"source bare JID. And it is possible if you use plugin with class name: \"\n\"**SenderBareJidLB**.\"\nmsgstr \"\"\n\"然而，这种分配策略并不总是适用于所有可能的组件。例如，对于传输，这根本行不通。为传输分配负载的更好方法是基于源裸 \"\n\"JID。如果你使用类名的插件这是可能的：**SenderBareJidLB**。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:47\nmsgid \"\"\n\"This are two basic load distribution strategies available now. For some \"\n\"use cases none of them is good enough. If you have PubSub, then you \"\n\"probably want to distribute load based on the PubSub node. There is no \"\n\"plugin for that yet but it is easy enough to write one and put the class \"\n\"name in configuration.\"\nmsgstr \"\"\n\"这是现在可用的两种基本负载分配策略。对于某些用例，它们都不够好。如果您有 PubSub，那么您可能希望基于 PubSub \"\n\"节点分配负载。目前还没有插件，但很容易编写一个并将类名放入配置中。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:50\nmsgid \"External Component and Cluster\"\nmsgstr \"外部组件和集群\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:52\nmsgid \"\"\n\"If you want to use Tigase’s component in a cluster mode which does not \"\n\"have clustering implemented yet there is a way to make it kind of \"\n\"cluster-able. In the previous section we connected many MUC components to\"\n\" a single Tigase server. Now we want to connect a single MUC component to\"\n\" many Tigase servers (or many Tigase cluster nodes).\"\nmsgstr \"\"\n\"如果您想在没有实现集群的集群模式下使用 Tigase 的组件，有一种方法可以使其具有集群能力。在上一节中，我们将许多 MUC 组件连接到单个 \"\n\"Tigase 服务器。现在我们想将单个 MUC 组件连接到许多 Tigase 服务器（或许多 Tigase 集群节点）。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:54\nmsgid \"\"\n\"Let’s say we have Tigase XMPP Server working for domain: **xmpp-\"\n\"test.org** and the server is installed on three cluster nodes: **red\"\n\".xmpp-test.org,** **green.xmpp-test.org** and **blue.xmpp-test.org.**\"\nmsgstr \"\"\n\"假设我们有 Tigase XMPP 服务器为域工作：**xmpp-test.org** 并且服务器安装在三个集群节点上：**red.xmpp-\"\n\"test.org,** **green.xmpp-test. org** 和 **blue.xmpp-test.org.**\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:56\nmsgid \"|ExternalCompClustering003 0|\"\nmsgstr \"|ExternalCompClustering003 0|\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:84\nmsgid \"ExternalCompClustering003 0\"\nmsgstr \"ExternalCompClustering003 0\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:58\nmsgid \"\"\n\"We want to make it possible to connect the MUC component to all nodes. To\"\n\" do so, we are configuring Tigase XMPP Server to run in the cluster mode \"\n\"and on each of cluster nodes we need to enable ``ComponentProtocol`` \"\n\"component.\"\nmsgstr \"\"\n\"我们希望能够将 MUC 组件连接到所有节点。为此，我们将 Tigase XMPP 服务器配置为在集群模式下运行，并且在每个集群节点上我们需要启用 \"\n\"``ComponentProtocol`` 组件。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:60\nmsgid \"\"\n\"This can be simply done by adding following line to the server \"\n\"configuration file:\"\nmsgstr \"这可以通过在服务器配置文件中添加以下行来简单地完成：\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:66\nmsgid \"\"\n\"After this is done we need to add a new external component connection \"\n\"settings using ``Add item`` position for ``ext`` component in \"\n\"``Configuration`` section of the web page just as it is described in \"\n\":ref:`External Component Configuration<External-Component-Configuration>`\"\n\" section.\"\nmsgstr \"\"\n\"完成此操作后，我们需要在网页的 ``Configuration`` 部分中使用 ``Add item`` \"\n\"位置为 ``ext`` 组件添加新的外部组件连接设置，正如 :ref:`外部组件配置\"\n\"<External-Component-Configuration>` 部分中所述。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:68\nmsgid \"\"\n\"As you can see there is nothing special here. The most interesting part \"\n\"comes on the MUC side, but it is only a very small change from the \"\n\"configuration of the component to use with single node Tigase XMPP Server\"\n\" installation.\"\nmsgstr \"\"\n\"如您所见，这里没有什么特别之处。最有趣的部分来自 MUC 方面，但从组件配置到与单节点 Tigase XMPP \"\n\"服务器安装一起使用，这只是一个很小的变化。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:70\nmsgid \"\"\n\"When you are adding/configuring external component settings using Admin \"\n\"UI (``Add item`` or ``Update item configuration`` for ``ext-man`` \"\n\"component) or using separate configuration file (when you are not using \"\n\"shared database) then you need to pass as a value for ``Remote host`` \"\n\"field a semicolon separated list of all of the cluster nodes to which \"\n\"external component should connect.\"\nmsgstr \"\"\n\"当您使用管理 UI 添加/配置外部组件设置时（为 ``ext-man`` 组件 ``Add item`` 或 ``Update item \"\n\"configuration`` ）或使用单独的配置文件（当您不使用共享数据库时) 那么您需要将分号分隔的所有集群节点列表作为 ``Remote \"\n\"host`` 字段的值传递，外部组件应连接到这些节点。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:72\nmsgid \"In our case it would be:\"\nmsgstr \"在我们的例子中，它将是：\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:78\nmsgid \"\"\n\"As you can see remote host name is not a simple domain but a character \"\n\"string with a few comma separated parts. The first part is our remote \"\n\"domain and the rest are addresses of the host to connect to. This can be \"\n\"a list of domain names or IP addresses.\"\nmsgstr \"\"\n\"如您所见，远程主机名不是一个简单的域，而是一个带有几个逗号分隔部分的字符串。第一部分是我们的远程域，其余部分是要连接的主机地址。这可以是域名或 \"\n\"IP 地址列表。\"\n\n#: ../../Tigase_Administration/Components/External_Component_Configuration/Load_Balancing.inc:80\nmsgid \"\"\n\"Of course it is possible to connect multiple external component to all \"\n\"cluster nodes, this way the whole installation would be really working in\"\n\" the cluster and also load balanced.\"\nmsgstr \"当然，可以将多个外部组件连接到所有集群节点，这样整个安装将真正在集群中工作并且负载均衡。\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:2\nmsgid \"Client to Server Communication\"\nmsgstr \"客户端到服务器通信\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:4\nmsgid \"\"\n\"Client to server communication is an integral part of XMPP communication.\"\n\" C2S handles all client communication to the server, and is responsible \"\n\"for filtering and handling remote communications. C2S CAN be disabled, \"\n\"however doing so will only allow communication of internal components, \"\n\"and S2S communications.\"\nmsgstr \"\"\n\"客户端到服务器的通信是 XMPP 通信的一个组成部分。 C2S 处理与服务器的所有客户端通信，并负责过滤和处理远程通信。可以禁用 \"\n\"C2S，但是这样做只会允许内部组件的通信和 S2S 通信。\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:9\nmsgid \"To disable C2S, use the following line in ``config.tdsl`` folder.\"\nmsgstr \"要禁用 C2S，请在 ``config.tdsl`` 文件夹中使用以下行。\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:15\nmsgid \"Otherwise, C2S component is activated by default.\"\nmsgstr \"否则，默认激活 C2S 组件。\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:18\nmsgid \"Connections\"\nmsgstr \"连接\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:20\nmsgid \"\"\n\"The connections container houses all configuration related to connections\"\n\" with the component. Each port may be configured individually.\"\nmsgstr \"连接容器包含与组件连接相关的所有配置。每个端口都可以单独配置。\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:36\nmsgid \"new-connections-throttling\"\nmsgstr \"new-connections-throttling\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:38\nmsgid \"\"\n\"The property allows you to limit how many new users' connection per \"\n\"second the server accepts on a particular port. Connections established \"\n\"within the limit are processed normally, all others are simply \"\n\"disconnected. This allows you to avoid server overload in case there is a\"\n\" huge number of users trying to connect at the same time. Mostly this \"\n\"happens after a server restart.\"\nmsgstr \"该属性允许您限制服务器在特定端口上每秒接受的新用户连接数。在限制范围内建立的连接被正常处理，所有其他连接都被简单地断开。这使您可以避免服务器过载，以防有大量用户同时尝试连接。这通常发生在服务器重新启动后。\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:50\nmsgid \"\"\n\"Here, this limits the number to 150 connections per second before \"\n\"connection attempts are dropped.\"\nmsgstr \"在这里，这将连接尝试数限制为每秒 150 个连接，然后连接尝试被丢弃。\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:52\nmsgid \"This replaces the old ``--new-connections-throttling`` property.\"\nmsgstr \"这取代了旧的 ``--new-connections-throttling`` 属性。\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:55\nmsgid \"Resumption timeout\"\nmsgstr \"恢复超时\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:57\nmsgid \"\"\n\"It is now possible to set a default stream resumption timeout that the \"\n\"server uses. This allows control of how long a server will wait for a \"\n\"reconnection from a client. This can be particularly helpful to manage \"\n\"mobile clients connecting to your server as they may not have complete \"\n\"coverage, and you do not want to close the stream right away. By default,\"\n\" Tigase sets this value to 60 seconds.\"\nmsgstr \"\"\n\"现在可以设置服务器使用的默认流恢复超时。这允许控制服务器等待客户端重新连接的时间。这对于管理连接到您的服务器的移动客户端特别有用，因为它们可能没有完全覆盖，并且您不想立即关闭流。默认情况下，Tigase\"\n\" 将此值设置为 60 秒。\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:67\nmsgid \"\"\n\"This sets the default timeout to 90 seconds. You may, if you choose, \"\n\"specify a maximum timeout time, which will allow the server to wait \"\n\"between the default and maximum before a connection is closed.\"\nmsgstr \"这会将默认超时设置为 90 秒。如果您愿意，您可以指定最大超时时间，这将允许服务器在连接关闭之前在默认值和最大值之间等待。\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:79\nmsgid \"\"\n\"If the max-resumption-timeout is not set, it will always equal the \"\n\"resumption-timeout number, or default is none is set.\"\nmsgstr \"如果未设置 max-resumption-timeout，它将始终等于 resumption-timeout 数，或者默认为 none 设置。\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:81\nmsgid \"Available since v7.1.0\"\nmsgstr \"从 v7.1.0 开始可用\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:84\nmsgid \"Packet Redelivery\"\nmsgstr \"数据包重新投递\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:86\nmsgid \"\"\n\"Normally packets are handled by C2S and are typically processed in the \"\n\"first run, however if that fails to send, a retry of sending that packet \"\n\"will occur after 60 seconds. If that second try fails, the delay will \"\n\"increase by a factor of 1.5. This means that the next retry will occur at\"\n\" 90, 135, and so on until the retry count is reached. By default this \"\n\"count is 15, however it can be changed by using the following setting:\"\nmsgstr \"\"\n\"通常数据包由 C2S 处理，并且通常在第一次运行时处理，但是如果发送失败，将在 60 秒后重试发送该数据包。如果第二次尝试失败，延迟将增加 1.5\"\n\" 倍。这意味着下一次重试将发生在 90，135 ..，直到达到重试计数。默认情况下，此计数为 15，但可以使用以下设置进行更改：\"\n\n#: ../../Tigase_Administration/Components/C2S.inc:94\nmsgid \"\"\n\"This setting prevents packet redelivery attempts from continuing into \"\n\"infinity (or when the host machine runs out of memory).\"\nmsgstr \"此设置可防止数据包重新传递尝试持续到无穷大（或当主机内存不足时）。\"\n\n#: ../../Tigase_Administration/Components/External_Service_Discovery_Component.inc:2\nmsgid \"Tigase External Service Discovery\"\nmsgstr \"Tigase 外部服务发现\"\n\n#: ../../Tigase_Administration/Components/External_Service_Discovery_Component.inc:4\nmsgid \"\"\n\"Welcome to the Tigase External Service Discovery component user guide. \"\n\"Component provides support for `XEP-0215: External Service Discovery \"\n\"<http://xmpp.org/extensions/xep-0215.html>`__ which allows discovery of \"\n\"external services which are not accessible using XMPP protocol.\"\nmsgstr \"\"\n\"欢迎使用 Tigase 外部服务发现组件用户指南。组件提供对 `XEP-0215: External Service Discovery \"\n\"<http://xmpp.org/extensions/xep-0215.html>`__ 的支持，它允许发现使用 XMPP \"\n\"协议无法访问的外部服务。\"\n\n#: ../../Tigase_Administration/Components/External_Service_Discovery_Component.inc:7\nmsgid \"Setup & Configuration\"\nmsgstr \"设置和配置\"\n\n#: ../../Tigase_Administration/Components/External_Service_Discovery_Component.inc:9\nmsgid \"\"\n\"Component (which is implemented in class \"\n\"``tigase.server.extdisco.ExternalServiceDiscoveryComponent``) is by \"\n\"default registered under name ``ext-disco`` and disabled. To enable it \"\n\"you need to enable it in configuration. Example:\"\nmsgstr \"\"\n\"组件（在类 ``tigase.server.extdisco.ExternalServiceDiscoveryComponent`` \"\n\"中实现）默认注册在名称 ``ext-disco`` 下并禁用。要启用它，您需要在配置中启用它。例子：\"\n\n#: ../../Tigase_Administration/Components/External_Service_Discovery_Component.inc:11\nmsgid \"in DSL format:\"\nmsgstr \"DSL 格式：\"\n\n#: ../../Tigase_Administration/Components/External_Service_Discovery_Component.inc:17\nmsgid \"\"\n\"Additionally you need to activate ``urn:xmpp:extdisco:2`` XMPP processor \"\n\"in ``SessionManager`` by:\"\nmsgstr \"此外，您需要通过以下方式在 ``SessionManager`` 中激活 ``urn:xmpp:extdisco:2`` XMPP 处理器:\"\n\n#: ../../Tigase_Administration/Components/External_Service_Discovery_Component.inc:19\nmsgid \"in DSL - enable subbean of ``sess-man``:\"\nmsgstr \"在 DSL 中 - 启用 ``sess-man`` 的子 bean：\"\n\n#: ../../Tigase_Administration/Components/External_Service_Discovery_Component.inc:27\nmsgid \"\"\n\"List of external services returned by server is configurable using ad-hoc\"\n\" commands provided for this component. AdHoc commands are accessible only\"\n\" for server administrator using XMPP client with support for AdHoc \"\n\"commands or using Tigase Admin UI. Usage of AdHoc commands provides \"\n\"easiest and flexible way to add, modify or remove entries for services \"\n\"which will be returned by discovery.\"\nmsgstr \"\"\n\"服务器返回的外部服务列表可以使用为此组件提供的临时命令进行配置。只有使用支持 AdHoc 命令的 XMPP 客户端或使用 Tigase Admin\"\n\" UI 的服务器管理员才能访问 AdHoc 命令。 AdHoc 命令的使用提供了简单和灵活的方法来添加，修改或删除将由发现返回的服务条目。\"\n\n#~ msgid \"\"\n#~ \"`Setting up Remote Monitoring in the \"\n#~ \"Server <#Setting-Up-Remote-Monitoring-\"\n#~ \"in-the-Server>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Statistics Logger Configuration <#Configuration-\"\n#~ \"of-statistics-loggers>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Retrieving Statistics from the Server \"\n#~ \"<#Retrieving-statistics-from-the-server>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Monitor Component <#Monitor-Component>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"In order to collect statistics over \"\n#~ \"period of time following groovy script\"\n#~ \" can be used: `StatsDumper.groovy \"\n#~ \"<files/StatsDumper.groovy>`__. It’s a Simple \"\n#~ \"JMX client that connects to Tigase \"\n#~ \"and periodically saves all statistics to\"\n#~ \" files.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"ref:`Statistics Logger Configuration<Configuration-\"\n#~ \"of-statistics-loggers>`\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"ref:`Retrieving Statistics from the Server\"\n#~ \"<Retrieving-statistics-from-the-server>`\"\n#~ msgstr \"\"\n\n#~ msgid \"ref:`Monitor Component<Monitor-Component>`\"\n#~ msgstr \"\"\n\n#~ msgid \"**Note**\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"If client is subscribed to \"\n#~ \"``*|tigase:demo node``, then events will \"\n#~ \"not be sent from node ``*|tigase:demo``,\"\n#~ \" but from the **real** node (in \"\n#~ \"this case: ``EventName|tigase:demo``).\"\n#~ msgstr \"\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Administration/Configuration/_Configuration.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version:  TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-11-15 00:08-0800\\n\"\n\"PO-Revision-Date: 2022-09-07 17:14+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language: zh_CN\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects\"\n\"/tigase-xmpp-server/configuration/zh_Hans/>\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.10.3\\n\"\n\n#: ../../Tigase_Administration/Configuration/_Configuration.rst:2\nmsgid \"Configuration\"\nmsgstr \"配置\"\n\n#: ../../Tigase_Administration/Configuration/_Configuration.rst:4\nmsgid \"\"\n\"When the user tries to setup the client for the first time he comes \"\n\"across 2 configuration files: ``tigase.conf`` and ``config.tdsl`` in the \"\n\"``/etc`` folder. Here is a brief explanation what all those files are \"\n\"about and in other sections you can learn all the details needed to \"\n\"configure the server.\"\nmsgstr \"\"\n\"当用户第一次尝试设置客户端时，他遇到了 2 个配置文件：``/etc`` 文件夹中的 ``tigase.conf`` 和 \"\n\"``config.tdsl``。以下是所有这些文件的简要说明，在其他部分中，您可以了解配置服务器所需的所有详细信息。\"\n\n#: ../../Tigase_Administration/Configuration/_Configuration.rst:6\nmsgid \"\"\n\":ref:`config.tdsl<dslConfig>` file is a simple text file with server \"\n\"parameters in form: **key** = **value**. When the XML configuration file \"\n\"is missing the Tigase server reads ``config.tdsl`` file and uses \"\n\"parameters found there as defaults for generation of the XML file. \"\n\"Therefore if you change the ``config.tdsl`` file you normally have to \"\n\"stop the server, remove the XML file and start the server again. All the \"\n\"settings from the ``config.tdsl`` are read and applied to the XML \"\n\"configuration. The properties file is easy to read and very safe to \"\n\"modify. At the moment this is the recommended way change the server \"\n\"configuration.\"\nmsgstr \"\"\n\":ref:`config.tdsl<dslConfig>` 文件是一个简单的文本文件，其服务器参数格式为：**key** = \"\n\"**value**。当 XML 配置文件丢失时，Tigase 服务器读取 ``config.tdsl`` 文件并使用在那里找到的参数作为生成 \"\n\"XML 文件的默认值。因此，如果您更改 ``config.tdsl`` 文件，您通常必须停止服务器，删除 XML \"\n\"文件并重新启动服务器。``config.tdsl`` 中的所有设置都被读取并应用于 XML \"\n\"配置。属性文件易于阅读且修改起来非常安全。目前这是推荐的更改服务器配置的方法。\"\n\n#: ../../Tigase_Administration/Configuration/_Configuration.rst:8\nmsgid \"\"\n\":ref:`tigase.conf<manualconfig>` is the Tigase server startup \"\n\"configuration. It is actually not used by the server itself. It rather \"\n\"contains operating system settings and environment parameters to \"\n\"correctly run the `Java Virtual Machine \"\n\"<https://www.oracle.com/java/technologies/>`__. It is only useful on the \"\n\"unix-like systems with Bash shell. If you run the server on MS Windows \"\n\"systems ``tigase.bat`` and ``wrapper.conf`` files are used instead. The \"\n\"``tigase.conf`` file is read and loaded by the ``scripts/tigase.sh`` \"\n\"shell script which also scans the operating system environment for Java \"\n\"VM and other tools needed.\"\nmsgstr \"\"\n\":ref:`tigase.conf<manualconfig>` 是 Tigase \"\n\"服务器启动配置。它实际上不被服务器本身使用。它包含操作系统设置和环境参数以正确运行 `Java 虚拟机 \"\n\"<https://www.oracle.com/java/technologies/>`__。它仅在带有 Bash shell 的类 unix \"\n\"系统上有用。如果您在 MS Windows 系统上运行服务器，则需使用 ``tigase.bat`` 和 ``wrapper.conf`` 文件。\"\n\" ``tigase.conf`` 文件由 ``scripts/tigase.sh`` shell \"\n\"脚本读取和加载，该脚本还可以扫描操作系统环境以查找 Java VM 和其他所需工具。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:4\nmsgid \"DSL file format\"\nmsgstr \"DSL 文件格式\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:6\nmsgid \"\"\n\"In previous Tigase XMPP Server releases configuration was stored in \"\n\"properties based configuration file. From Tigase XMPP Server 8.0.0 \"\n\"release it will be required to use new DSL based configuration file \"\n\"format. This file format was inspired by Groovy language syntax and new \"\n\"core feature of Tigase XMPP Server - Tigase Kernel Framework.\"\nmsgstr \"\"\n\"在以前的 Tigase XMPP 服务器版本中，配置存储在基于配置文件的属性中。从 Tigase XMPP Server 8.0.0 \"\n\"版本开始，将需要使用新的基于 DSL 的配置文件格式。这种文件格式的灵感来自 Groovy 语言语法和 Tigase XMPP 服务器的新核心功能\"\n\" - Tigase 内核框架 。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:9\nmsgid \"why new format?\"\nmsgstr \"为什么是新格式？\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:11\nmsgid \"\"\n\"In properties configuration format each line contained key and value with\"\n\" optional definition of type of stored value:\"\nmsgstr \"在属性配置格式中，每一行都包含键和值以及存储值类型的可选定义：\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:17\nmsgid \"\"\n\"where ``c2s/ports`` was name of property, ``[i]`` defined that type of \"\n\"value is array of integers, and ``5222,5223`` was comma separated list of\"\n\" values.\"\nmsgstr \"其中 ``c2s/ports`` 是属性名称，``[i]`` 定义了值的类型是整数数组，``5222,5223`` 是逗号分隔的值列表。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:19\nmsgid \"\"\n\"This format worked but in fact ``c2s/ports`` was not name of property you\"\n\" configured but key which was later split on ``/`` char to parts which \"\n\"defined by names path to property which name was in last part.From that \"\n\"you can see that it was domain based setting of properties.\"\nmsgstr \"\"\n\"这种格式有效，但实际上 ``c2s/ports`` 不是您配置的属性的名称，而是后来在 ``/`` char \"\n\"上拆分为由名称路径定义的部分的键，该名称在最后一部分中。从这您可以看到它是基于域的属性设置。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:21\nmsgid \"\"\n\"Except from this multi-part keys we also used properties starting with \"\n\"``--`` which were global properties accessible for every part of \"\n\"application, i.e.: to add new component and set some properties you \"\n\"needed to write:\"\nmsgstr \"\"\n\"除了这个多部分键之外，我们还使用了以 ``--`` \"\n\"开头的属性，这些属性是应用程序的每个部分都可以访问的全局属性，即：添加新组件并设置一些您需要编写的属性：\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:30\nmsgid \"\"\n\"This lead to mistakes like duplicated definition of name and class for \"\n\"same number of component or redefined property value in other place of a \"\n\"configuration file - especially in cases where configuration was big.\"\nmsgstr \"这会导致错误，例如为相同数量的组件重复定义名称和类或在一个配置文件的其他位置重新定义属性值 - 特别是在配置很大的情况下。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:32\nmsgid \"\"\n\"In this configuration structure it was hard to tell where is \"\n\"configuration for particular component or what databases this \"\n\"installation uses. This could be defined all over the file.\"\nmsgstr \"在这种配置结构中，很难分辨特定组件的配置在哪里，或者这个安装使用什么数据库。这可以在整个文件中定义。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:34\nmsgid \"\"\n\"In this release we are introducing Tigase Kernel Framework, which allows \"\n\"to configure beans in configuration file and even define usage of new \"\n\"beans loaded from external jars which can modify behavior of Tigase \"\n\"components. This would make configuration file even more complex, \"\n\"difficult and less readable.\"\nmsgstr \"\"\n\"在这个版本中，我们引入了 Tigase 内核框架，它允许在配置文件中配置 bean，甚至定义从外部 jars 加载的新 bean 的使用，这些 \"\n\"jars 可以修改 Tigase 组件的行为。这将使配置文件更加复杂，困难且可读性更差。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:37\nmsgid \"What is DSL?\"\nmsgstr \"什么是 DSL？\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:39\nmsgid \"\"\n\"DSL stands for domain-specific language - in this case language created \"\n\"for storage of configuration.\"\nmsgstr \"DSL 代表特定领域的语言 - 在这种情况下，语言是为存储配置而创建的。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:41\nmsgid \"\"\n\"Now we use domain based configuration which means that our configuration \"\n\"file is not a flat key=value storage but it defines objects, it's \"\n\"properties and assigned values.\"\nmsgstr \"现在我们使用基于域的配置，这意味着我们的配置文件不是平面键=值存储，而是定义对象，其属性和分配值。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:43\nmsgid \"\"\n\"To illustrate it better let's start with a simple example. In properties \"\n\"file in order to configure PubSub component named ``pubsub`` you would \"\n\"use following properties:\"\nmsgstr \"为了更好地说明它，让我们从一个简单的例子开始。在属性文件中，为了配置名为 ``pubsub`` 的 PubSub 组件，您将使用以下属性：\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:51\nmsgid \"In DSL based configuration this would be replaced by following block\"\nmsgstr \"在基于 DSL 的配置中，这将被以下块替换\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:60\nmsgid \"\"\n\"in which we define bean with name `pubsub` and set it's class inside \"\n\"``()`` block to ``tigase.pubsub.PubSubComponent``. We also use block \"\n\"between ``{}`` chars to define properties which are related to bean. \"\n\"Which means this properties will be passed only to this instance of \"\n\"Tigase PubSub Component, same as it was before where we needed to add \"\n\"prefix. Entries after ``\\\\#`` are comments, to pass ``#`` you need to \"\n\"wrap whole part containing it in ``''``, ie. ``'test#242'``\"\nmsgstr \"\"\n\"在其中我们定义名为 `pubsub` 的 bean 并将其在 ``()`` 块中的类设置为 \"\n\"``tigase.pubsub.PubSubComponent``。我们还使用 ``{}`` 字符之间的块来定义与 bean \"\n\"相关的属性。这意味着此属性将仅传递给 Tigase PubSub 组件的此实例，与之前我们需要添加前缀的位置相同。 ``\\\\#`` \"\n\"之后的条目是注释，要传递 ``#`` 你需要将包含它的整个部分包裹在 ``''`` 中，即 ``'test#242'``\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:66\nmsgid \"\"\n\"If a string value assigned to a property contains any char from a \"\n\"following list ``=:,[]#+-*/`` it needs to be wrapped in a ``''``.\"\nmsgstr \"如果分配给属性的字符串值包含来自以下列表 ``=:,[]#+-*/`` 的任何字符，则需要将其包在 ``''`` 中。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:69\nmsgid \"Why DSL?\"\nmsgstr \"为什么是 DSL？\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:71\nmsgid \"\"\n\"DSL configuration format provides a number of advantages over the old \"\n\"system of configuration. All configurations for components are related in\"\n\" a single block, so they are not spread out over several different lines.\"\n\" No need for long property names, no longer have to invoke a long string \"\n\"of settings for multiple values. Support is provided for environment \"\n\"variables. No longer need to escape certain characters, making settings \"\n\"far more readable at a glance. Values may be set using basic \"\n\"calculations, such as ``100 * 200 * 2`` rather than ``40000``. Parameter \"\n\"type values are no longer necessary, no more [i], [S], [B] etc.. Comma \"\n\"separated values can now be simplified lists with separate entries being \"\n\"able to be in multiple lines.\"\nmsgstr \"\"\n\"与旧的配置系统相比，DSL \"\n\"配置格式有许多优势。组件的所有配置都在一个块中相关，因此它们不会分布在多个不同的行中。不需要长属性名称，不再需要为多个值调用一长串设置。为环境变量提供支持。不再需要转义某些字符，使设置一目了然。可以使用基本计算来设置值，例如\"\n\" ``100 * 200 * 2`` 而不是 ``40000``。不再需要参数类型值，不再需要 [i]、[S]、[B] \"\n\"等。逗号分隔的值现在可以简化为列表，单独的条目可以位于多行中。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:73\nmsgid \"\"\n\"Although the format may seem more complex, looking like a section of java\"\n\" code, the formatting is consistent and can be far more readable. After \"\n\"some experience with DSL format, you'll find it's far more intuitive and \"\n\"user friendly than it may appear. Of course if there's any real \"\n\"confusion, Tigase can automatically convert old style properties files to\"\n\" the DSL format using the following command:\"\nmsgstr \"\"\n\"尽管格式可能看起来更复杂，像一段 java 代码，但格式是一致的并且可读性更强。在对 DSL \"\n\"格式进行了一些体验之后，您会发现它远比看起来更直观和易于使用。当然，如果有任何真正的混淆，Tigase 可以使用以下命令自动将旧样式属性文件转换为\"\n\" DSL 格式：\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:80\nmsgid \"Setting property\"\nmsgstr \"设置属性\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:82\nmsgid \"\"\n\"To set property you just write property name followed by `=` and value to\"\n\" set. This is always done in context of bean which configuration property\"\n\" you want to set.\"\nmsgstr \"要设置属性，您只需编写属性名称，后跟 `=` 和要设置的值。这些总是在您要设置的配置属性的 bean 上下文中完成。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:88\nmsgid \"\"\n\"It is also possible to set property in main context by placing property \"\n\"outside of any context. This sets property which value is available to \"\n\"access by any bean.\"\nmsgstr \"也可以通过将属性放置在任何上下文之外的主上下文中设置属性。这设置了哪个值的属性可以被任何 bean 访问。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:92\nmsgid \"Setting global property\"\nmsgstr \"设置全局属性\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:94\nmsgid \"\"\n\"Like in properties file it is still possible to use property names \"\n\"starting with ``--`` without any context or any other properties at \"\n\"global scope. Format is the same as in case of setting property but they \"\n\"are defined without scope (in global scope). This properties are global \"\n\"and accessible by any bean but also set as system property in JVM.\"\nmsgstr \"\"\n\"就像在属性文件中一样，仍然可以在没有任何上下文或全局范围内的任何其他属性的情况下使用以 ``--`` \"\n\"开头的属性名称。格式与设置属性的情况相同，但它们是没有定义范围(在全局范围内）。此属性是全局的，任何 bean 都可以访问，但在 JVM \"\n\"中也设置为系统属性。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:97\nmsgid \"Defining bean\"\nmsgstr \"定义 bean\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:99\nmsgid \"You can configure bean by using following format:\"\nmsgstr \"您可以使用以下格式配置 bean：\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:107\nmsgid \"\"\n\"where ``beanName`` is name under which you want to configure bean. \"\n\"`beanName` must be wrapped in ``''``, if ``beanName`` contains characters\"\n\" like ``=:,[]#+-*/`` and is recommended, if ``beanName`` is numeric only.\"\nmsgstr \"\"\n\"其中 ``beanName`` 是您要配置 bean 的名称。 `beanName` 必须包在 ``''`` 中，如果 ``beanName`` \"\n\"包含像 ``=:,[]#+-*/`` 这样的字符并且被推荐，如果 ``beanName`` 只是数字。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:110\nmsgid \"Inside block between ``(` and `)`` you can define:\"\nmsgstr \"``(`and`)`` 之间的块内你可以定义：\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:112\nmsgid \"\"\n\"``class`` which will be used as a bean, in example above we set class as \"\n\"``className``. **(default: if you try to configure bean under name which \"\n\"has default class assigned with it in Tigase framework then this assigned\"\n\" class will be used. In other case you need to pass name of class to use \"\n\"as a bean)**\"\nmsgstr \"\"\n\"``class`` 将被用作 bean，在上面的示例中，我们将 class 设置为 ``className``。 **(默认值：如果您尝试在 \"\n\"Tigase 框架中配置默认类的名称下配置 bean，则将使用此分配的类。在其他情况下，您需要传递类的名称以用作 bean)**\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:113\nmsgid \"\"\n\"``active`` (boolean) whether you want the bean to be active or not (beans\"\n\" with ``active`` set to ``false`` are not loaded). **(default: true)**\"\nmsgstr \"\"\n\"``active`` (布尔值）是否希望 bean 处于活动状态（不加载 ``active`` 设置为 ``false`` 的 \"\n\"bean)。**(默认：true)**\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:114\nmsgid \"\"\n\"``exportable`` (boolean) defines if this bean should be exported and \"\n\"available for use for beans in inner scopes. This is advanced option in \"\n\"most cases it is recommended to omit this field in configuration. \"\n\"**(default: false)**\"\nmsgstr \"\"\n\"``exportable`` (boolean) 定义这个 bean 是否应该被导出并且可用于内部范围以内的 \"\n\"bean。这是大多数情况下的高级选项，建议在配置中忽略此字段。**(默认：false)**\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:116\nmsgid \"\"\n\"Spaces between ``beanName`` and block between ``()`` is optional as well \"\n\"as space between block ``()`` and block ``{}``. It is recommended that \"\n\"properties of bean would be placed in separate lines with indentation and\"\n\" first property will be placed in new line.\"\nmsgstr \"\"\n\"不仅 ``beanName`` 之间和 ``()`` 之间的空格是可选的，而且 ``()`` 之间和 ``{}`` 之间的块也是可选的。建议将 \"\n\"bean 的属性放置在带有缩进的单独行中，并将第一个属性放置在新行中。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:121\nmsgid \"\"\n\"Usage of ``()`` block is very important. When this block is used in \"\n\"configuration it automatically sets ``active`` property of bean \"\n\"definition for bean for which it is used to to `true`. This is done due \"\n\"to fact that default value of ``active`` is ``true``.\"\nmsgstr \"\"\n\"``()`` 块的使用非常重要。当在配置中使用这个块时，它会自动将 bean 定义的 ``active`` 属性设置为 \"\n\"`true`。这是因为“active”的默认值为“true”。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:123\nmsgid \"\"\n\"If you omit it in configuration, you will set bean configuration but it \"\n\"may remain ``inactive``. In this state bean will not be loaded and as a \"\n\"result will not be used by Tigase XMPP Server.\"\nmsgstr \"\"\n\"如果您在配置中忽略它，您将设置 bean 配置，但它可能保持 ``inactive``。在这种状态下，bean 不会被加载，因此 Tigase \"\n\"XMPP 服务器不会使用它。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:126\nmsgid \"Configuring bean\"\nmsgstr \"配置bean\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:128\nmsgid \"\"\n\"If you know that bean is defined and you do not want to change it's \"\n\"activity or class then you can just pass properties to configure bean in \"\n\"following way:\"\nmsgstr \"如果您知道 bean 已定义并且您不想更改它的活动或类，那么您可以通过以下方式传递属性来配置 bean：\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:138\nmsgid \"\"\n\"where ``beanName`` is name of bean to configure and `test` is name of \"\n\"property to set to ``true`` in this bean.\"\nmsgstr \"其中 ``beanName`` 是要配置的 bean 的名称，而 `test` 是要在这个 bean 中设置为 ``true`` 的属性的名称。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:141\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:148\nmsgid \"Format of values\"\nmsgstr \"值的格式\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:143\nmsgid \"\"\n\"In properties based configuration file every property was defined as a \"\n\"string and only by defining expected format it was properly converted to \"\n\"expected value. In DSL it is possible to set values in two ways:\"\nmsgstr \"在基于属性的配置文件中，每个属性都被定义为一个字符串，并且只有通过定义预期的格式，它才能正确转换为预期的值。在 DSL 中，可以通过两种方式设置值：\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:146\nmsgid \"as an object\"\nmsgstr \"作为一个对象\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:146\nmsgid \"Using this format you set list as a list and integer is set as an integer.\"\nmsgstr \"使用这种格式，您将列表设置为列表，并且将整数设置为整数。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:151\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:205\nmsgid \"Type\"\nmsgstr \"类型\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:151\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:205\nmsgid \"Description\"\nmsgstr \"描述\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:153\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:207\nmsgid \"**string**\"\nmsgstr \"**字符串**\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:153\nmsgid \"Wrap it in ``''``, ie. to set ``test`` as string you use ``'test'``\"\nmsgstr \"把它包在 ``''`` 中，也就是将 ``test`` 设置为使用 ``'test'`` 的字符串\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:155\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:209\nmsgid \"**integer**\"\nmsgstr \"**整数**\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:155\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:209\nmsgid \"Just put value, ie. to set ``543`` use ``543``\"\nmsgstr \"也就是，把值设置为 ``543`` 使用 ``543``\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:157\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:211\nmsgid \"**long**\"\nmsgstr \"**长整型**\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:157\nmsgid \"\"\n\"Put value and follow it with ``L``, ie. to set ``23645434`` as long use \"\n\"``23645434L``\"\nmsgstr \"输入数值并在其后面加上 ``L``，即设置 ``23645434`` 为长整型使用 ``23645434L``\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:159\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:213\nmsgid \"**float**\"\nmsgstr \"**浮点型**\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:159\nmsgid \"\"\n\"Put value and follow it with ``f``, ie. to set ``231.342`` use \"\n\"``231.342f``\"\nmsgstr \"输入数值并在其后面加上 ``f``，即将 ``231.342`` 设置为 ``231.342f``\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:161\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:215\nmsgid \"**boolean**\"\nmsgstr \"**布尔值**\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:161\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:215\nmsgid \"To set value just use ``true`` or ``false``\"\nmsgstr \"要设置值，只需使用 ``true`` 或 ``false``\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:163\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:217\nmsgid \"**list**\"\nmsgstr \"**列表**\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:163\nmsgid \"\"\n\"Lists can be of many types and to make it simple we decided to use as a \"\n\"comma separated list of values in proper format wrapped in ``[]``.\"\nmsgstr \"列表可以有多种类型，为了简单起见，我们决定使用逗号分隔的值列表，以正确的格式放在 ``[]`` 中。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:165\nmsgid \"of strings - ``[ 'alfa', 'beta', 'gamma' ]``\"\nmsgstr \"字符串列表 - ``[ 'alfa', 'beta', 'gamma' ]``\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:167\nmsgid \"of integers - ``[ 1, 2, 3, 4]``\"\nmsgstr \"整型列表 - ``[ 1, 2, 3, 4]``\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:169\nmsgid \"You can write it in multiple lines if you want:\"\nmsgstr \"如果需要，您可以将其写成多行：\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:179\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:219\nmsgid \"**map**\"\nmsgstr \"**映射**\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:179\nmsgid \"\"\n\"Maps can be written as a block of properties wrapped in ``{}``. This \"\n\"format of map is the same as used for passing configuration to bean \"\n\"properties. Keys and values can be written in separate lines \"\n\"*(recommended)*:\"\nmsgstr \"映射可以写成属性块并放在 ``{}`` 中。这种映射格式与用于将配置传递给 bean 属性的格式相同。键和值可以写在单独的行中 *(推荐)*：\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:192\nmsgid \"or in single line *(separation with spaces is not required)*:\"\nmsgstr \"或单行 *(不需要用空格分开)*：\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:200\nmsgid \"as a plain string\"\nmsgstr \"作为纯字符串\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:200\nmsgid \"\"\n\"Very similar to properties based configuration, in fact values are passed\"\n\" in same format and later are converted to correct type by checking type \"\n\"expected by bean. *(Not recommended)*\"\nmsgstr \"与基于属性的配置非常相似，实际上值以相同的格式传递，然后通过检查 bean 预期的类型将其转换为正确的类型。*(不建议)*\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:202\nmsgid \"Types\"\nmsgstr \"类型\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:207\nmsgid \"Just put value, ie. to set ``test`` use ``test``\"\nmsgstr \"只是放入值，即设置 ``test`` 为 ``test``\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:211\nmsgid \"Put value, ie. to set ``23645434`` as long use ``23645434``\"\nmsgstr \"把值放入，即设置 ``23645434`` 为长整型使用 ``23645434``\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:213\nmsgid \"Put value, ie. to set ``231.342`` use ``231.342``\"\nmsgstr \"放入数值，即设置 ``231.342`` 为 ``231.342``\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:217\nmsgid \"\"\n\"List needs to be written as comma separated list of values, ie. \"\n\"``test,abc,efg`` or ``1,2,3``\"\nmsgstr \"列表需要写为用逗号分隔的值列表，即 ``test,abc,efg`` 或 ``1,2,3``\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:219\nmsgid \"Not possible\"\nmsgstr \"不可能\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:224\nmsgid \"Using values from System Properties and Environment Variables\"\nmsgstr \"使用系统属性和环境变量中的值\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:226\nmsgid \"\"\n\"Now it is possible to use values of `system properties \"\n\"<https://docs.oracle.com/javase/tutorial/essential/environment/sysprop.html>`__\"\n\" and `environment variables \"\n\"<https://docs.oracle.com/javase/tutorial/essential/environment/env.html>`__\"\n\" and assign them to bean properties. For this purpose we added functions \"\n\"which can be used in DSL and which will return values of:\"\nmsgstr \"\"\n\"现在可以使用 `system properties \"\n\"<https://docs.oracle.com/javase/tutorial/essential/environment/sysprop.html>`__\"\n\" 和 `environment variables \"\n\"<https://docs.oracle.com/javase/tutorial/essential/environment/env.html>`__\"\n\" 并将它们分配给 bean 属性。为此，我们添加了可在 DSL 中使用的函数，这些函数将返回以下值：\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:229\nmsgid \"system property\"\nmsgstr \"系统属性\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:229\nmsgid \"``prop('property-name')`` or ``prop('property-name','default value')``\"\nmsgstr \"``prop('property-name')`` 或 ``prop('property-name','default value')``\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:232\nmsgid \"environment variable\"\nmsgstr \"环境变量\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:232\nmsgid \"``env('variable-name')``\"\nmsgstr \"``env('variable-name')``\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:234\nmsgid \"\"\n\"**Example of setting value of system property and environment variable to\"\n\" bean ``user``.**\"\nmsgstr \"**将系统属性和环境变量的值设置为 bean ``user`` 的示例。**\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:247\nmsgid \"\"\n\"For properties which accepts lists it is not allowed to set value using \"\n\"variable/property with comma separated values like ``value1,value2`` \"\n\"wrapped in ``[]``, ie. ``property = [ env('some-variable') ]``. It needs \"\n\"to be set in following way ``property = env('some-variable')``\"\nmsgstr \"\"\n\"对于列表接受的属性，不允许使用带有逗号分隔的变量/属性值，例如放在在 ``[]`` 中的 ``value1,value2``，即 \"\n\"``property = [ env('some-variable') ]``。它需要按以下方式设置 ``property = env\"\n\"('some-variable')``\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:250\nmsgid \"Computed values\"\nmsgstr \"计算值\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:252\nmsgid \"\"\n\"With DSL configuration format we introduce support for computable values \"\n\"for properties. It is now possible to set value which is result of a \"\n\"computation, ie. concatenation of a strings or very simple mathematical \"\n\"expression. We currently support only following mathematical operations:\"\nmsgstr \"\"\n\"通过 DSL \"\n\"配置格式，我们引入了对属性的可计算值的支持。现在可以把值设置为作为计算结果的值，即字符串或非常简单的数学表达式的连接。我们目前仅支持以下数学运算：\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:254\nmsgid \"add\"\nmsgstr \"加法\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:256\nmsgid \"subtract\"\nmsgstr \"减法\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:258\nmsgid \"multiply\"\nmsgstr \"乘法\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:260\nmsgid \"divide\"\nmsgstr \"除法\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:262\nmsgid \"\"\n\"**Example of setting environment variable related path and computed \"\n\"timeout.**\"\nmsgstr \"**设置环境变量相关路径和计算超时的示例。**\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:278\nmsgid \"\"\n\"For properties which accepts lists it is not allowed to set value using \"\n\"computed values with comma separated values like ``value1,value2`` \"\n\"wrapped in ``[]``, ie. ``property = [ env('some-variable') + ',other-\"\n\"value' ]``. It needs to be set in following way ``property = env('some-\"\n\"variable') + ',other-value'``.\"\nmsgstr \"\"\n\"对于列表所接受的属性，不允许使用带有逗号分隔值的计算值来设置值，例如放在 ``[]`` 中的``value1,value2``，即 \"\n\"``property = [ env('some-variable') + ',other-value' ]``。它需要按以下方式设置 \"\n\"``property = env('some-variable') + ',other-value'``。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:283\nmsgid \"Period / Duration values\"\nmsgstr \"Period / Duration 值\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:285\nmsgid \"\"\n\"Some configuration options allow control of execution of tasks with \"\n\"particular period or within certain duration. DSL file format accepts \"\n\"strings denoting particular amount of time, which follows Java’s native \"\n\"structures (see: `Period \"\n\"<https://docs.oracle.com/javase/8/docs/api/java/time/Period.html#parse-\"\n\"java.lang.CharSequence->`__ and `Duration \"\n\"<https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html#parse-\"\n\"java.lang.CharSequence->`__ for detailed explanation).\"\nmsgstr \"\"\n\"一些配置选项允许控制特定时期或特定持续时间内的任务执行。 DSL 文件格式接受表示特定时间量的字符串，它遵循 Java 的本地结构（请参阅: \"\n\"`Period <https://docs.oracle.com/javase/8/docs/api/java/time/Period.html\"\n\"#parse-java.lang.CharSequence->`__ 和 `Duration \"\n\"<https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html#parse-\"\n\"java.lang.CharSequence->`__ 详细解释).\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:287\nmsgid \"\"\n\"``Duration`` formats accepted are based on the ISO-8601 duration format \"\n\"``PnDTnHnMn.nS`` with days considered to be exactly 24 hours, for \"\n\"example:\"\nmsgstr \"``Duration`` 接受的格式是基于 ISO-8601的 持续时间格式 ``PnDTnHnMn.nS``，其中天数被认为是 24 小时，例如：\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:289\nmsgid \"``PT20.345S`` - 20.345 seconds\"\nmsgstr \"``PT20.345S`` - 20.345 秒\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:291\nmsgid \"``PT15M`` - 15 minutes (where a minute is 60 seconds)\"\nmsgstr \"``PT15M`` - 15 分钟（一分钟为 60 秒）\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:293\nmsgid \"``PT10H`` - 10 hours (where an hour is 3600 seconds)\"\nmsgstr \"``PT10H`` - 10 小时（其中 1 小时为 3600 秒）\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:295\nmsgid \"``P2D`` - 2 days (where a day is 24 hours or 86400 seconds)\"\nmsgstr \"``P2D`` - 2 天（一天是 24 小时或 86400 秒）\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:297\nmsgid \"``P2DT3H4M`` - 2 days, 3 hours and 4 minutes\"\nmsgstr \"``P2DT3H4M`` - 2天，3小时，4分钟\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:299\nmsgid \"\"\n\"``Period`` format is based on the ISO-8601 period formats PnYnMnD and \"\n\"PnW, for example, the following are valid inputs:\"\nmsgstr \"``Period`` 格式是基于 ISO-8601 的周期格式 PnYnMnD 和 PnW，例如，以下是有效输入：\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:301\nmsgid \"``P2Y`` - 2 years\"\nmsgstr \"``P2Y`` - 2 年\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:303\nmsgid \"``P3M`` - 3 months\"\nmsgstr \"``P3M`` - 3 月\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:305\nmsgid \"``P4W`` - 4 weeks\"\nmsgstr \"``P4W`` - 4周\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:307\nmsgid \"``P5D`` - 5 days\"\nmsgstr \"``P5D`` - 5天\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:309\nmsgid \"``P1Y2M3D`` - 1 year, 2 months, 3 days\"\nmsgstr \"``P1Y2M3D`` - 1年，2个月，3天\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:311\nmsgid \"``P1Y2M3W4D`` - 1 year, 2 months, 3 weeks, 4 days\"\nmsgstr \"``P1Y2M3W4D`` - 1年，2个月，3周，4天\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:315\nmsgid \"Example configuration file in DSL\"\nmsgstr \"DSL 中的示例配置文件\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:393\nmsgid \"Default configuration\"\nmsgstr \"默认配置\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:395\nmsgid \"\"\n\"Tigase XMPP Server is packaged with a basic ``config.tdsl`` file that \"\n\"tells the server to start up in setup mode.\"\nmsgstr \"Tigase XMPP 服务器包含一个基本的 ``config.tdsl`` 文件，该文件告诉服务器以设置模式启动。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:408\nmsgid \"\"\n\"This tells Tigase to operate in a setup mode, and tells the http \"\n\"component to allow login with the username and password admin/tigase. \"\n\"With this you can enter the setup process that is covered in `this \"\n\"section <#webinstall>`__.\"\nmsgstr \"\"\n\"这告诉 Tigase 在设置模式下运行，并告诉 http 组件允许使用用户名和密码 admin/tigase 登录。有了它，您可以进入 `this\"\n\" section <#webinstall>`__ 中介绍的设置过程。\"\n\n#: ../../Tigase_Administration/Configuration/DSL_configuration.inc:410\nmsgid \"\"\n\"There are other options for config-type: ``default``, ``session-\"\n\"manager``, ``connection-managers``, and ``component``. For more \"\n\"information, visit `Config Type <#configType>`__ property description.\"\nmsgstr \"\"\n\"配置类型还有其他选项: ``default``, ``session-manager``, ``connection-managers``, 和 \"\n\"``component`` 。如需更多信息，请访问 `Config Type <#configType>`__ 属性说明。\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:4\nmsgid \"Startup File for tigase.sh - tigase.conf\"\nmsgstr \"tigase.sh 的启动文件 - tigase.conf\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:6\nmsgid \"\"\n\"Property file names for ``tigase.sh`` startup script is a second \"\n\"parameter for the startup script. It can be skipped if environmental \"\n\"variables are set in different location or in different way.\"\nmsgstr \"``tigase.sh`` 启动脚本的属性文件名是启动脚本的第二个参数。如果环境变量设置在不同的位置或以不同的方式设置，则可以跳过它。\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:8\nmsgid \"\"\n\"Config file for startup script simply sets number of environment \"\n\"variables with the location of required components. Possible variables to\"\n\" set in this file are:\"\nmsgstr \"启动脚本的配置文件简单地设置环境变量的数量以及所需组件的位置。在此文件中设置的可能变量是：\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:10\nmsgid \"\"\n\"``JAVA_HOME`` - location of Java installation home directory. **Must be \"\n\"set**.\"\nmsgstr \"``JAVA_HOME`` - Java 安装主目录的位置。 **必须设置**。\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:12\nmsgid \"\"\n\"``TIGASE_HOME`` - location of Tigase installation home directory. *By \"\n\"default script try to find this location by searching directories from \"\n\"the location where the script has been run.*\"\nmsgstr \"``TIGASE_HOME`` - Tigase 安装主目录的位置。 *默认情况下，脚本尝试通过从脚本运行的位置搜索目录来找到此位置。*\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:14\nmsgid \"\"\n\"``TIGASE_CONSOLE_LOG`` - file to which all console messages will be \"\n\"redirected if server is run in background. By default it will be: \"\n\"``TIGASE_HOME/logs/tigase-console.log``. **If this file/directory is not \"\n\"writable by Tigase process all console messages will be redirected to \"\n\"/dev/null**\"\nmsgstr \"\"\n\"``TIGASE_CONSOLE_LOG`` - \"\n\"如果服务器在后台运行，所有控制台消息将被重定向到的文件。默认情况下，它将是：``TIGASE_HOME/logs/tigase-\"\n\"console.log``。 **如果 Tigase 进程无法写入此文件/目录，所有控制台消息将被重定向到 /dev/null**\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:16\nmsgid \"\"\n\"``TIGASE_PID`` location of the file with server PID number. By default it\"\n\" will be ``TIGASE_HOME/logs/tigase.pid``.\"\nmsgstr \"\"\n\"``TIGASE_PID`` 带有服务器 PID 号的文件的位置。默认情况下，它将是 \"\n\"``TIGASE_HOME/logs/tigase.pid``。\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:18\nmsgid \"\"\n\"``JAVA_OPTIONS`` - options for JVM like size of RAM allocated for the \"\n\"JVM, properties and so on.\"\nmsgstr \"``JAVA_OPTIONS`` - JVM 的选项，例如为 JVM 分配的 RAM 大小，属性等。\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:20\nmsgid \"\"\n\"``TIGASE_OPTIONS`` - (optional) additional options for Tigase server \"\n\"program. You can tweak initial parameters for your environment here. If \"\n\"you want to specify custom location of your configuration file you should\"\n\" use ``--config-file <path/to/config.tdsl>`` configuration\"\nmsgstr \"\"\n\"``TIGASE_OPTIONS`` - （可选）Tigase \"\n\"服务器程序的附加选项。您可以在此处为您的环境调整初始参数。如果你想为配置文件指定自定义位置，你应该使用 ``--config-file \"\n\"<path/to/config.tdsl>`` 配置\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:22\nmsgid \"Sample file to run **Tigase** with **PostgreSQL** database may look like:\"\nmsgstr \"使用 **PostgreSQL** 数据库运行 **Tigase** 的示例文件可能如下所示：\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:33\nmsgid \"\"\n\"Please note encoding settings. JVM by default uses encoding set in \"\n\"operating system environment. XMPP protocol, however uses ``UTF-8`` for \"\n\"all data processing. So the ENC settings enforces ``UTF-8`` encoding for \"\n\"all operations.\"\nmsgstr \"\"\n\"请注意编码设置。JVM 默认使用操作系统环境中设置的编码。然而，XMPP 协议使用 ``UTF-8`` 进行所有数据处理。因此 ENC \"\n\"设置对所有操作强制执行 ``UTF-8`` 编码。\"\n\n#: ../../Tigase_Administration/Configuration/Startup_Files.inc:35\nmsgid \"\"\n\"Another significant setting is \\\\\\\\'**CLASSPATH**'. It is intentionally \"\n\"set to an empty string. The **tigase.sh** startup script builds the \"\n\"**CLASSPATH** on it’s own from files found in **jars/** and **libs/** \"\n\"directories. It is advised to set the **CLASSPATH** to the empty string \"\n\"because the Tigase server scans all available classes to find all \"\n\"components and plugins implementation. If the **CLASSPATH** contains lots\"\n\" of libraries which are not used anyway it can cause a long startup time \"\n\"and high system loads.\"\nmsgstr \"\"\n\"另一个重要的设置是 \\\\\\\\'**CLASSPATH**'。它被有意设置为空字符串。 **tigase.sh** 启动脚本根据 **jars/**\"\n\" 和 **libs/** 目录中的文件自行构建 **CLASSPATH**。建议将 **CLASSPATH** 设置为空字符串，因为 Tigase\"\n\" 服务器会扫描所有可用的类以查找所有组件和插件实现。如果 **CLASSPATH** 包含大量未使用的库，则可能会导致启动时间长和系统负载高。\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:4\nmsgid \"Linux Settings for High Load Systems\"\nmsgstr \"高负载系统的 Linux 设置\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:6\nmsgid \"\"\n\"There are a few basic settings you have to adjust for high load systems \"\n\"to make sure the server has enough resources to handle a big number of \"\n\"network connections.\"\nmsgstr \"您必须针对高负载系统调整一些基本设置，以确保服务器有足够的资源来处理大量网络连接。\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:8\nmsgid \"\"\n\"The main parameter is a maximum number of opened files allowed for the \"\n\"process to keep at the same time. Each network connection uses a file \"\n\"handler, therefore if the limit is too low you can quickly run out of \"\n\"handlers and the server can not accept any more connections.\"\nmsgstr \"主要参数是允许进程同时保留的最大打开文件数。每个网络连接都使用一个文件处理程序，因此如果此限制数太低，您可能会很快用完处理程序，服务器将无法接受更多连接。\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:10\nmsgid \"\"\n\"This limit is set on 2 levels - on the kernel level (``fs.file-max``) and\"\n\" on the system level (``nofile``).\"\nmsgstr \"此限制设置在 2 个级别 - 内核级别（``fs.file-max``）和系统级别（``nofile``）。\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:12\nmsgid \"\"\n\"Another kernel property which can be important in certain configurations \"\n\"(like transports installations or when you use proxy for Bosh \"\n\"connections) is: ``net.ipv4.ip_local_port_range``. This parameter can be \"\n\"set the same way as the ``fs.file-max`` property.\"\nmsgstr \"\"\n\"另一个在某些配置中可能很重要的内核属性（例如传输安装或当您使用代理进行 Bosh \"\n\"连接时）是：``net.ipv4.ip_local_port_range``。此参数的设置方式与 ``fs.file-max`` 属性相同。\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:15\nmsgid \"fs.file-max\"\nmsgstr \"fs.file-max\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:17\nmsgid \"\"\n\"The ``fs.file-max`` kernel property is set via sysctl command. You can \"\n\"see current settings by executing the command:\"\nmsgstr \"``fs.file-max`` 内核属性是通过 sysctl 命令设置的。您可以通过执行以下命令查看当前设置：\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:24\nmsgid \"\"\n\"If you plan to run high load service with large number of server \"\n\"connections, then this parameter should be at least as twice big as the \"\n\"number of network connections you expect to support. You can change this \"\n\"setting by executing the command:\"\nmsgstr \"如果您计划运行具有大量服务器连接的高负载服务，则此参数应至少是您希望支持的网络连接数的两倍。您可以通过执行以下命令来更改此设置：\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:32\nmsgid \"net.ipv4.ip_local_port_range\"\nmsgstr \"net.ipv4.ip_local_port_range\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:34\nmsgid \"You can see current settings by executing the command:\"\nmsgstr \"您可以通过执行以下命令查看当前设置：\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:41\nmsgid \"You can change this setting by executing the command:\"\nmsgstr \"您可以通过执行以下命令来更改此设置：\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:49\nmsgid \"TCP_keepalive\"\nmsgstr \"TCP_keepalive\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:51\nmsgid \"\"\n\"According to `Using TCP keepalive to Detect Network Errors \"\n\"<http://www.gnugk.org/keepalive.html>`__ and `TCP Keepalive HOWTO \"\n\"<https://tldp.org/HOWTO/TCP-Keepalive-HOWTO/usingkeepalive.html>`__ some \"\n\"keepalive settings should be changed to improve reliability - it will \"\n\"enable keep alive functionality (checking if the connection is \"\n\"established and valid) and, by decreasing times and interval - will make \"\n\"detection of broken connections faster.\"\nmsgstr \"\"\n\"根据 `Using TCP keepalive to Detect Network Errors \"\n\"<http://www.gnugk.org/keepalive.html>`__ 和 `TCP Keepalive HOWTO \"\n\"<https://tldp.org/HOWTO/TCP-Keepalive-HOWTO/usingkeepalive .html>`__ \"\n\"应该更改一些 keepalive 设置以提高可靠性 - 它将启用 keep alive功能（检查连接是否已建立和有效），并且通过减少时间和间隔 -\"\n\" 将更快地检测断开的连接。\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:66\nmsgid \"/etc/sysctl.conf\"\nmsgstr \"/etc/sysctl.conf\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:68\nmsgid \"\"\n\"The above commands let the system remember new settings until the next \"\n\"system restart. If you want to make the change permanent you have to edit\"\n\" the file: ``/etc/sysctl.conf`` and add the property at the end of the \"\n\"file:\"\nmsgstr \"\"\n\"上述命令让系统记住新设置，直到下次系统重新启动。如果要使更改永久生效，则必须编辑文件：``/etc/sysctl.conf`` \"\n\"并在文件末尾添加属性：\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:79\nmsgid \"It will be automatically loaded next time you start the server.\"\nmsgstr \"下次启动服务器时会自动加载。\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:81\nmsgid \"Command:\"\nmsgstr \"命令：\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:87\nmsgid \"\"\n\"Causes the ``/etc/systcl.conf`` to be reloaded which is useful when you \"\n\"have added more parameters to the file and don’t want to restart the \"\n\"server.\"\nmsgstr \"导致 ``/etc/systcl.conf`` 被重新加载，当您向文件添加更多参数并且不想重新启动服务器时，这很有用。\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:90\nmsgid \"nofile\"\nmsgstr \"无文件\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:92\nmsgid \"\"\n\"This is the property used by the system limits. For example running the \"\n\"command ``ulimit -a`` shows you all limits set for the current user:\"\nmsgstr \"这是被系统限制所使用的属性。例如，运行命令 ``ulimit -a`` 会显示为当前用户设置的所有限制：\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:112\nmsgid \"\"\n\"To make it even more interesting and more complex, there are 2 types of \"\n\"system limits: **soft limit** which can be temporarily exceeded by the \"\n\"user and **hard limit** which can not be exceeded. To see your **hard \"\n\"limit** execute command:\"\nmsgstr \"\"\n\"为了让它更有趣和更复杂，有两种类型的系统限制：**软限制**，此时用户可以暂时超过，**硬限制**，此时用户不能超过。要查看您的 **硬限制** \"\n\"执行如下命令：\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:132\nmsgid \"\"\n\"The hard limits are usually bigger then the soft limits or sometimes the \"\n\"same.\"\nmsgstr \"硬限制通常大于软限制或有时相同。\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:134\nmsgid \"\"\n\"For us the most important parameter is: **open files**. You can change \"\n\"the property in file: ``/etc/security/limits.conf``. You have to append 2\"\n\" following lines to the end of the file:\"\nmsgstr \"\"\n\"对我们来说最重要的参数是：**打开文件**。您可以更改文件中的属性：``/etc/security/limits.conf``。您必须将以下 2 \"\n\"行附加到文件末尾：\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:141\nmsgid \"\"\n\"Where the ``jabber`` is the user name of the account running you IM \"\n\"service. You can also set the limits for all users on the machine in a \"\n\"following way:\"\nmsgstr \"其中 ``jabber`` 是运行您的 IM 服务的帐户的用户名。您还可以通过以下方式为机器上的所有用户设置限制：\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:148\nmsgid \"\"\n\"For those changes to make an effect you have to logout from the modified \"\n\"account and login again. New limits should be applied.\"\nmsgstr \"要使这些更改生效，您必须从修改后的帐户注销并再次登录。新的限制应该被运用。\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:152\nmsgid \"su and init script\"\nmsgstr \"su 和 init 脚本\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:154\nmsgid \"\"\n\"If one intends to use init scripts for startup purposes (or simply wants \"\n\"to be able to start the server utilizing su command) it’s necessary to \"\n\"adjust PAM configuration by modifying /etc/pam.d/su file and uncomment \"\n\"following line:\"\nmsgstr \"\"\n\"如果打算使用 init 脚本来启动（或者只是希望能够使用 su 命令启动服务器），则有必要通过修改 /etc/pam.d/su \"\n\"文件并取消以下行的注释来调整 PAM 配置：\"\n\n#: ../../Tigase_Administration/Configuration/Settings_for_High_Load_Systems.inc:160\nmsgid \"Afterwards the init scripts will respect configured limits.\"\nmsgstr \"之后， init 脚本将遵守配置的限制。\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:2\nmsgid \"JVM settings and recommendations\"\nmsgstr \"JVM 设置和建议\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:4\nmsgid \"\"\n\"Tigase configuration file ``tigase.conf`` (described in more detail in \"\n\"`Startup File for tigase.sh-tigase.conf <#manualconfig>`__) mentioned a \"\n\"couple of environmental variables which are related to the operation of \"\n\"the JVM. In this guide we would like to expound on those configuration \"\n\"options and provide hints for the optimal settings.\"\nmsgstr \"\"\n\"Tigase 配置文件 ``tigase.conf`` (在 `Startup File for tigase.sh-tigase.conf \"\n\"<#manualconfig>`__ 中有更详细的描述）提到了几个与 JVM \"\n\"的操作相关的环境变量。在本指南中，我们将阐述这些配置选项并提供最佳设置提示。\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:6\nmsgid \"Settings included in the ``etc/tigase.conf`` are as follows:\"\nmsgstr \"``etc/tigase.conf`` 中包含的设置如下：\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:18\nmsgid \"\"\n\"And while this file utilizes bash variables, JVM configuration options \"\n\"can be used in the same manner on all operating systems.\"\nmsgstr \"虽然此文件使用 bash 变量，但 JVM 配置选项可以在所有操作系统上以相同的方式使用。\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:20\nmsgid \"\"\n\"The guide will consists of two main parts - memory settings and Garbage \"\n\"Collector tweaks descriptions and hints.\"\nmsgstr \"该指南将包括两个主要部分 - 内存设置和垃圾收集器来调整描述和提示。\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:22\nmsgid \"We recommend using ``-server`` JVM parameter in all cases.\"\nmsgstr \"我们建议在所有情况下都使用 ``-server`` JVM 参数。\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:25\nmsgid \"Heap Sizing\"\nmsgstr \"堆的大小\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:27\nmsgid \"\"\n\"For the non-production deployments (development or stating environments) \"\n\"we recommend using default memory settings of the JVM (which depends on \"\n\"the underlaying operating system), which result i automatic memory \"\n\"allocation and, by the rule of thumb - are the safest in such \"\n\"environments.\"\nmsgstr \"\"\n\"对于非生产部署（开发或说明环境），我们建议使用 JVM 的默认内存设置（取决于底层操作系统），这会导致自动内存分配，并且根据经验法则 - \"\n\"在此类环境中是最安全的环境。\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:29\n#, python-format\nmsgid \"\"\n\"For the production environments we recommend a fixed size HEAP - both \"\n\"initial and maximum size, which can be set with (respectively)``-Xms`` \"\n\"and ``-Xmx`` JVM flags - ideally to the same value (which should be \"\n\"roughly 95% of the available memory, if Tigase will be the only service \"\n\"on the machine) to avoid allocation and deallocation.\"\nmsgstr \"\"\n\"对于生产环境，我们建议使用固定大小的 堆 - 初始大小和最大大小，可以（分别）使用 ``-Xms`` 和 ``-Xmx`` JVM 标志设置 - \"\n\"理想情况下为相同的值（如果 Tigase 将是机器上的唯一服务，应该是大约 95% 的可用内存）以避免分配和释放。\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:31\nmsgid \"\"\n\"For convenience it’s possible to uncomment line with \"\n\"``PRODUCTION_HEAP_SETTINGS`` and adjust parameters accordingly.\"\nmsgstr \"为方便起见，可以取消注释行 ``PRODUCTION_HEAP_SETTINGS`` 并相应地调整参数。\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:34\nmsgid \"Memory consideration - total usage\"\nmsgstr \"内存考虑 - 总使用量\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:36\nmsgid \"\"\n\"The HEAP size is not the only thing that affects JVM memory usage. When \"\n\"trying to size accordingly for your usage and machine specification you \"\n\"have to consider other factors that count towards total: loaded classes, \"\n\"threads' stack, JIT code cache, garbage collector and others. In \"\n\"principle consider following equation:\"\nmsgstr \"\"\n\"HEAP 大小并不是影响 JVM \"\n\"内存使用的唯一因素。在尝试根据您的使用情况和机器规格调整大小时，您必须考虑计入总数的其他因素：加载的类、线程的堆栈、JIT \"\n\"代码缓存、垃圾收集器等等。原则上考虑以下等式：\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:48\nmsgid \"In case of Tigase XMPP Server, apart from heap we limit remaining factors:\"\nmsgstr \"在 Tigase XMPP 服务器的情况下，除了堆之外，我们还限制了其余因素：\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:50\nmsgid \"direct memory to **128** MB\"\nmsgstr \"直接内存到 **128** MB\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:52\nmsgid \"loaded classes to **128** MB\"\nmsgstr \"加载类到 **128** MB\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:54\nmsgid \"\"\n\"single thread’s stack size to **228** KB (number of threads depends on \"\n\"number of CPU cores and may vary from 500 to couple of thousands)\"\nmsgstr \"单线程的栈大小为 **228** KB（线程数取决于 CPU 内核的数量，可能从 500 到几千不等）\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:56\nmsgid \"\"\n\"In principle, in addition to HEAP’s maximum size defined by ``-Xmx`` you \"\n\"should add roughly **512** MB\"\nmsgstr \"原则上，除了由 ``-Xmx`` 定义的堆的最大大小之外，您还应该添加大约 **512** MB\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:58\nmsgid \"\"\n\"If you are interested in detailed tracking of memory take a look at \"\n\"[Memory footprint of the JVM](\\\\ https://spring.io/blog/2019/03/11\"\n\"/memory-footprint-of-the-jvm/), [Native Memory Tracking in JVM](\\\\ \"\n\"https://www.baeldung.com/native-memory-tracking-in-jvm) or [Why does my \"\n\"Java process consume more memory than Xmx?](\\\\ https://plumbr.io/blog\"\n\"/memory-leaks/why-does-my-java-process-consume-more-memory-than-xmx)\"\nmsgstr \"\"\n\"如果您对内存的详细跟踪感兴趣，请查看 [JVM 的内存占用](\\\\ https://spring.io/blog/2019/03/11\"\n\"/memory-footprint-of-the-jvm/)， [JVM中的Native Memory Tracking](\\\\ \"\n\"https://www.baeldung.com/native-memory-tracking-in-\"\n\"jvm)或者[为什么我的Java进程消耗的内存比Xmx多？](\\\\ https://plumbr.io/blog/memory-leaks\"\n\"/why-does-my-java-process-consume-more-memory-than-xmx)\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:60\nmsgid \"\"\n\"To facilitate getting information about complete memory usage we include \"\n\"this information in Tigase statistics, but it requires explicitly \"\n\"enabling it:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:62\nmsgid \"uncomment ``JVM_MEMORY`` line in ``etc/tigase.conf``:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:68\nmsgid \"\"\n\"enable ``detailed-memory-statistics`` in ``message-router`` bean in \"\n\"``etc/config.tdsl`` file:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:78\nmsgid \"GC settings\"\nmsgstr \"GC 设置\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:80\nmsgid \"\"\n\"Let’s start with stating that there is no \\\"one to rule them all\\\" - each\"\n\" deployment and use-case is different, however we will try to give a \"\n\"couple of pointers and recommendations proceed with short introduction to\"\n\" GC itself.\"\nmsgstr \"让我们首先声明没有 \\\"一个人可以统治一切\\\" - 每个部署和用例都是不同的，但是我们将尝试给出一些指示和建议，并继续简要介绍 GC 本身。\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:82\nmsgid \"\"\n\"XMPP is quite specific in terms of memory allocation - short-lived \"\n\"objects (various types of stanzas) usually exceed number of long-lived \"\n\"objects (user connections and related data). This is important bit of \"\n\"information in the context of how usually JVM HEAP is organized and how \"\n\"Garbage Collector works. On the most basic level Heap is separated into \"\n\"couple of regions:\"\nmsgstr \"\"\n\"XMPP 在内存分配方面非常具体 - 短期对象（各种类型的节）通常超过长期对象（用户连接和相关数据）的数量。这是关于 JVM HEAP \"\n\"的组织方式和垃圾收集器如何工作的重要信息。在最基本的层面上，堆被分成几个区域：\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:85\nmsgid \"Generations\"\nmsgstr \"代\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:87\nmsgid \"**Young Generation**, which is further divided in to:\"\nmsgstr \"**年轻代**，其又分为：\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:89\nmsgid \"\"\n\"**Eden** - the region when the objects are usually allocated when they \"\n\"are created;\"\nmsgstr \"**Eden** - 创建对象时通常分配对象的区域；\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:91\nmsgid \"\"\n\"**Survivor Spaces** - (*to* and *from* - one of which is always empty) - \"\n\"responsible for storing all live object remaining after collecting \"\n\"**Young Generation** (process is repeated several times until objects are\"\n\" finally considered *old enough*);\"\nmsgstr \"\"\n\"**幸存者区** - (*to* 和 *from* - 其中一个始终为空) - 负责存储收集 **年轻代** (过程重复多次，直到最终考虑对象 \"\n\"*足够老*)后剩余的所有存活对象 ;\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:93\nmsgid \"\"\n\"**Old Generation** - (*Tenured Space*) - responsible for live objects \"\n\"remaining after running GC on **Survivor Spaces** - those would be *long-\"\n\"lived* objects (usually user connections and associated data);\"\nmsgstr \"\"\n\"**老年代** - (*养老区*) - 负责在 **幸存者区** 上运行 GC 后剩余的活动对象 - 这些将是 *长期* \"\n\"对象（通常是用户连接和相关数据)；\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:96\nmsgid \"Minor, Major and Full GC - optimizing\"\nmsgstr \"Minor，Major 和 Full GC - 优化\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:98\nmsgid \"General thinking suggests that:\"\nmsgstr \"一般认为：\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:100\nmsgid \"**Minor GC** cleans Young generation;\"\nmsgstr \"**Minor GC** 清理年轻代；\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:102\nmsgid \"**Major GC** cleans Tenured space;\"\nmsgstr \"**Major GC** 清理 Tenured 空间；\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:104\nmsgid \"**Full GC** cleans all heap.\"\nmsgstr \"**Full GC** 清理所有堆。\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:106\nmsgid \"\"\n\"However, while we can certainly state that Minor GC cleans Young \"\n\"generation it’s a bit more difficult to differentiate Major and Full GC, \"\n\"especially considering that Major GC can be quite often triggered by \"\n\"Minor GC and some garbage collectors can perform cleaning concurrently. \"\n\"Instead of focusing of distinguishing phases one should pay closer \"\n\"attention to actual operations of Garbage Collector itself - uncommenting\"\n\" the line ``GC_DEBUG=\\\" -XX:+PrintTenuringDistribution \"\n\"-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps \"\n\"-Xloggc:logs/jvm.log -verbose:gc \\\"`` in ``etc/tigase.conf`` (or adding \"\n\"same properties to the java commandline) and subsequently analyzing the \"\n\"results should prove more helpful. In addition monitoring GC operation \"\n\"using for example VisualVM (with VisualGC plugin) will also be helpful.\"\nmsgstr \"\"\n\"然而，虽然我们可以肯定地说 Minor GC 会清理年轻代，但区分 Major GC 和 Full GC 有点困难，特别是考虑到 Major GC\"\n\" 经常由 Minor GC 触发，并且一些垃圾收集器可以同时执行清理。与其关注区分阶段，不如更关注垃圾收集器本身的实际操作 - 取消在 \"\n\"``etc/tigase.conf`` 中的注释行 ``GC_DEBUG=\\\" -XX:+PrintTenuringDistribution \"\n\"-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps \"\n\"-Xloggc: logs/jvm.log -verbose:gc \\\"`` (或向 java \"\n\"命令行添加相同的属性)并随后分析结果应该会更有帮助。此外，使用示例 VisualVM（带有 VisualGC 插件）监控 GC 操作也将有所帮助。\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:109\nmsgid \"Settings for XMPP\"\nmsgstr \"XMPP 的设置\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:111\nmsgid \"\"\n\"Ideally we should limit both number of GC pauses as well as their \"\n\"duration. After running rather tests following conclusions were made:\"\nmsgstr \"理想情况下，我们应该限制 GC 暂停的次数及其持续时间。经过非同寻常的测试，得出以下结论：\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:113\nmsgid \"\"\n\"Garbage Collection is the faster the more dead objects occupies given \"\n\"space, therefore on high-traffic installation it’s better to have rather \"\n\"large YoungGen resulting in lower promotion of the objects to the OldGen;\"\nmsgstr \"垃圾回收速度越快，死对象占用给定空间越多，因此在高流量安装中，最好使用较大的 YoungGen，从而降低对象向 OldGen 的提升;\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:115\nmsgid \"\"\n\"with JVM8 default sizing of Young / Old generation changed, even tho \"\n\"NewRatio is still defaulting to “2” - setting it explicitly to \\\"2\\\" \"\n\"brought back previous sizing;\"\nmsgstr \"随着 JVM8 的年轻/老年代默认大小发生变化，即使 NewRatio 仍然默认为“2” - 将其显式设置为“2”会恢复以前的大小;\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:117\nmsgid \"\"\n\"Concurrent Mark and Sweep (CMS) enabled (applies to Tenured space only) \"\n\"with explicit configuration of NewRatio set to default value of 2 (i.e. \"\n\"``-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:NewRatio=2``) in general \"\n\"behaves best;\"\nmsgstr \"\"\n\"启用并发标记和扫描 (CMS)（仅适用于 Tenured 空间），通常将 NewRatio 的显式配置设置为默认值 2（即 \"\n\"``-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:NewRatio=2``）表现最好;\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:119\nmsgid \"\"\n\"For small installations (few core CPU, less memory) with low traffic \"\n\"default Parallel collector may be a better solution;\"\nmsgstr \"对于低流量的小型安装（核心 CPU 少，内存少）默认并行收集器可能是更好的解决方案;\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:121\nmsgid \"\"\n\"Using Heap size adjusted to the actual usage is better as the larger the \"\n\"heap the larger are spaces over which collection needs to be performed \"\n\"thus resulting in longer pauses; in case of huge heaps G1 collector may \"\n\"be better solution to avoid longer pauses;\"\nmsgstr \"\"\n\"使用根据实际使用情况调整的堆大小会更好，因为堆越大，需要执行收集的空间就越大，从而导致更长的暂停；如果堆很大，G1 \"\n\"收集器可能是避免更长暂停的更好解决方案;\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:123\nmsgid \"\"\n\"Considering all of the above using following options should be a good \"\n\"starting point toward further optimizing of Garbage Collection:\"\nmsgstr \"考虑到上述所有因素，使用以下选项应该是进一步优化垃圾收集的良好起点：\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:125\nmsgid \"\"\n\"``GC=\\\"-XX:+UseBiasedLocking -XX:+UseConcMarkSweepGC -XX:+UseParNewGC \"\n\"-XX:+CMSIncrementalMode -XX:-ReduceInitialCardMarks \"\n\"-XX:CMSInitiatingOccupancyFraction=70 \"\n\"-XX:+UseCMSInitiatingOccupancyOnly\\\"``\"\nmsgstr \"\"\n\"``GC=\\\"-XX:+UseBiasedLocking -XX:+UseConcMarkSweepGC -XX:+UseParNewGC \"\n\"-XX:+CMSIncrementalMode -XX:-ReduceInitialCardMarks \"\n\"-XX:CMSInitiatingOccupancyFraction=70 \"\n\"-XX:+UseCMSInitiatingOccupancyOnly\\\"``\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:128\nmsgid \"GC settings worth considering\"\nmsgstr \"值得考虑的 GC 设置\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:130\nmsgid \"\"\n\"In addition to the general recommendation to use CMS collector, following\"\n\" options (or changes to the options) may be worth considering:\"\nmsgstr \"除了使用 CMS 收集器的一般性建议外，以下选项(或对选项的更改)可能值得考虑：\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:132\nmsgid \"\"\n\"``-XX:NewRatio=2`` - defines the ratio between the young and tenured \"\n\"generation is 1:2. In other words, the combined size of the eden and \"\n\"survivor spaces will be one-third of the total heap size. The parameters \"\n\"NewSize and MaxNewSize bound the young generation size from below and \"\n\"above. Setting these to the same value fixes the young generation, just \"\n\"as setting -Xms and -Xmx to the same value fixes the total heap size.\"\nmsgstr \"\"\n\"``-XX:NewRatio=2`` - 定义年轻代和老生代的比例为 1:2。换句话说，eden和幸存者空间的组合大小将是总堆大小的三分之一。参数\"\n\" NewSize 和 MaxNewSize 从下方和上方限制了年轻代的大小。将它们设置为相同的值会修复年轻代，就像将 -Xms 和 -Xmx \"\n\"设置为相同的值会修复总堆大小一样。\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:134\nmsgid \"\"\n\"``-XX:CMSInitiatingOccupancyFraction=percent`` - sets the percentage of \"\n\"the old generation occupancy (0 to 100) at which to start a CMS \"\n\"collection cycle.\"\nmsgstr \"\"\n\"``-XX:CMSInitiatingOccupancyFraction=percent`` - 设置开始 CMS 收集周期的老年代占用率(0 到\"\n\" 100)。\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:136\nmsgid \"\"\n\"``-XX:+UseCMSInitiatingOccupancyOnly`` - instructs the JVM not to base \"\n\"its decision when to start a CMS cycle on run time statistics but instead\"\n\" it uses the value of CMSInitiatingOccupancyFraction for every CMS cycle.\"\nmsgstr \"\"\n\"``-XX:+UseCMSInitiatingOccupancyOnly`` - 指示 JVM 不要根据运行时统计信息，而是使用每个 CMS \"\n\"周期的 CMSInitiatingOccupancyFraction 的值来决定何时启动 CMS 周期。\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:138\nmsgid \"\"\n\"``-XX:ParallelGCThreads=x`` - sets the number of threads used for \"\n\"parallel garbage collection in the young and old generations. The default\"\n\" value depends on the number of CPUs available to the JVM. If the Tigase \"\n\"JMV is the only one running on the installation default value is \"\n\"recommended.\"\nmsgstr \"\"\n\"``-XX:ParallelGCThreads=x`` - 设置年轻代和老年代用于并行垃圾回收的线程数。默认值取决于 JVM 可用的 CPU \"\n\"数量。如果 Tigase JMV 是安装中唯一的运行，建议使用默认值。\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:140\nmsgid \"\"\n\"``-XX:ConcGCThreads=x`` - sets the number of threads used for concurrent \"\n\"GC. The default value depends on the number of CPUs available to the JVM.\"\n\" If the Tigase JMV is the only one running on the installation default \"\n\"value is recommended.\"\nmsgstr \"\"\n\"``-XX:ConcGCThreads=x`` - 设置用于并发 GC 的线程数。默认值取决于 JVM 可用的 CPU 数量。如果 Tigase \"\n\"JMV 是安装中唯一的运行，建议使用默认值。\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:142\nmsgid \"\"\n\"``-XX:+UseBiasedLocking`` and ``-XX:+DoEscapeAnalysis`` - designed to \"\n\"eliminate locking overhead, however their effect on performance is \"\n\"unpredictable therefore testing is required; reduced locking should \"\n\"improve concurrency and, on current multi-core hardware, improve \"\n\"throughput.\"\nmsgstr \"\"\n\"``-XX:+UseBiasedLocking`` 和 ``-XX:+DoEscapeAnalysis`` - \"\n\"旨在消除锁定开销，但是它们对性能的影响是不可预测的，因此需要进行测试；减少锁定应该可提高并发性，并且在当前的多核硬件上，提高吞吐量。\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:144\nmsgid \"\"\n\"``-XX:+OptimizeStringConcat`` - enables the optimization of String \"\n\"concatenation operations. This option is enabled by default.\"\nmsgstr \"``-XX:+OptimizeStringConcat`` - 启用字符串连接操作的优化。此选项默认被启用。\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:146\nmsgid \"\"\n\"``-XX:+UseNUMA`` - enables performance optimization of an application on \"\n\"a machine with nonuniform memory architecture (NUMA - most modern \"\n\"computers are based on NUMA architecture) by increasing the application’s\"\n\" use of lower latency memory. By default, this option is disabled and no \"\n\"optimization for NUMA is made. The option is only available when the \"\n\"parallel garbage collector is used (-XX:+UseParallelGC).\"\nmsgstr \"\"\n\"``-XX:+UseNUMA`` - 通过增加应用程序对低延迟内存的使用，在具有非统一内存架构（NUMA - 大多数现代计算机基于 NUMA \"\n\"架构）的机器上启用应用程序的性能优化。默认情况下，此选项处于禁用状态，并且不会对NUMA进行优化。该选项仅在使用并行垃圾收集器时可用（-XX:+UseParallelGC）。\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:148\nmsgid \"\"\n\"``-XX:-UseCompressedOops`` — disables the use of compressed pointers. By \"\n\"default, this option is enabled, and compressed pointers are used when \"\n\"Java heap sizes are less than 32 GB. When this option is enabled, object \"\n\"references are represented as 32-bit offsets instead of 64-bit pointers, \"\n\"which typically increases performance when running the application with \"\n\"Java heap sizes less than 32 GB. This option works only for 64-bit JVMs.\"\nmsgstr \"\"\n\"``-XX:-UseCompressedOops`` -  禁用压缩指针的使用。此选项被默认启用，当 Java 堆大小小于 32 GB \"\n\"时使用压缩指针。启用此选项后，对象引用将表示为 32 位偏移量而不是 64 位指针，这通常会在运行 Java 堆大小小于 32 GB \"\n\"的应用程序时提高性能。此选项仅适用于 64 位 JVM。\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:152\nmsgid \"What to use with Machine x, y, z?\"\nmsgstr \"机器 x, y, z 使用什么？\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:155\nmsgid \"Server class machine (non-VM), > 16GB, >= 8 core CPU\"\nmsgstr \"服务器类机器（非 VM) , > 16GB, >= 8 核 CPU\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:157\nmsgid \"\"\n\"For such setup enabling CMS garbage collector is recommended. Depending \"\n\"on the traffic usage and particular use-case adjusting NewRatio may be \"\n\"needed. Adjusting Xms and Xms sizes for actual available memory is needed\"\n\" (or better yet, for the actual traffic!). Following should be used:\"\nmsgstr \"\"\n\"对于此类设置，建议启用 CMS 垃圾收集器。根据流量使用情况和特定用例，可能需要调整 NewRatio。需要针对实际可用内存调整 Xms 和 \"\n\"Xms 大小（或者更好的是，针对实际流量！）。应使用以下内容：\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:169\nmsgid \"\"\n\"For installation with lot of available memory and intention to utilize it\"\n\" all, using G1GC collector may be a better idea :\"\nmsgstr \"对于有大量可用内存并打算利用这些内存的安装，使用 G1GC 收集器可能是一个更好的主意：\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:183\nmsgid \"VM machine, 8GB of RAM, 4 core CPU equivalent\"\nmsgstr \"VM 机器，8GB RAM，4 核 CPU 等效\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:185\nmsgid \"\"\n\"For such setup enabling CMS garbage collector is also recommended. \"\n\"Depending on the traffic usage and particular use-case adjusting NewRatio\"\n\" may be needed (and configuring NewRatio is a must!). Adjusting Xms and \"\n\"Xms sizes for actual available memory is needed (or better yet, for the \"\n\"actual traffic!). Following should be used:\"\nmsgstr \"\"\n\"对于此类设置，还建议启用 CMS 垃圾收集器。根据流量使用情况和特定用例，可能需要调整 NewRatio（并且必须配置 \"\n\"NewRatio！）。需要针对实际可用内存调整 Xms 和 Xms 大小（或者更好的是，针对实际流量！）。应使用以下内容：\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:199\nmsgid \"VM machine with 4GB or less of RAM, and less than 4 core CPU equivalent\"\nmsgstr \"具有 4GB 或更少 RAM 且少于 4 核 CPU 的 VM 机器\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:201\nmsgid \"\"\n\"Small installations with limited resources could operate better with \"\n\"default (for JVM versions up to 8, which is the most current at the \"\n\"moment of the writing). Again - depending on the traffic usage and \"\n\"particular use-case adjusting NewRatio may be needed. Adjusting Xms and \"\n\"Xms sizes for actual available memory is recommended (or better yet, for \"\n\"the actual traffic!). Following should be used (i.e. ``GC`` line should \"\n\"be commented so the defaults will be used):\"\nmsgstr \"\"\n\"资源有限的小型安装可以在默认情况下更好地运行（对于直到 8 的 JVM 版本，这是撰写本文时最新的版本）。同样 - \"\n\"根据流量使用情况和特定用例，可能需要调整 NewRatio。建议针对实际可用内存调整 Xms 和 Xms \"\n\"大小（或者更好的是，针对实际流量！）。应该使用以下内容（即应该注释 ``GC`` 行以便使用默认值）：\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:215\nmsgid \"Additional resources\"\nmsgstr \"其他资源\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:217\nmsgid \"\"\n\"`Sizing the Generations \"\n\"<https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/sizing.html>`__\"\nmsgstr \"\"\n\"`Generations的大小 \"\n\"<https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/sizing.html>`__\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:219\nmsgid \"\"\n\"`About Java, parallel garbage collection and processor sets \"\n\"<http://www.c0t0d0s0.org/archives/6617-About-Java,-parallel-garbage-\"\n\"collection-and-processor-sets.html>`__\"\nmsgstr \"\"\n\"`关于 Java, 并行垃圾收集和处理器集 <http://www.c0t0d0s0.org/archives/6617-About-Java\"\n\",-parallel-garbage-collection-and-processor-sets.html>`__\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:221\nmsgid \"\"\n\"`GC Threads <http://hiroshiyamauchi.blogspot.cl/2009/12/gc-\"\n\"threads.html>`__\"\nmsgstr \"`GC 线程 <http://hiroshiyamauchi.blogspot.cl/2009/12/gc-threads.html>`__\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:223\nmsgid \"`GCViewer readme <https://github.com/chewiebug/GCViewer#readme>`__\"\nmsgstr \"`GCViewer 自述文件 <https://github.com/chewiebug/GCViewer#readme>`__\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:225\nmsgid \"\"\n\"`Java HotSpot™ Virtual Machine Performance Enhancements \"\n\"<http://docs.oracle.com/javase/7/docs/technotes/guides/vm/performance-\"\n\"enhancements-7.html>`__\"\nmsgstr \"\"\n\"`Java HotSpot™ 虚拟机性能增强 \"\n\"<http://docs.oracle.com/javase/7/docs/technotes/guides/vm/performance-\"\n\"enhancements-7.html>`__\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:227\nmsgid \"\"\n\"`Java Garbage Collection handbook <https://plumbr.eu/java-garbage-\"\n\"collection-handbook>`__\"\nmsgstr \"`Java 垃圾收集手册 <https://plumbr.eu/java-garbage-collection-handbook>`__\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:229\nmsgid \"Useful JVM Flags\"\nmsgstr \"有用的 JVM 标志\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:231\nmsgid \"\"\n\"`Part 1 - JVM Types and Compiler Modes \"\n\"<https://blog.codecentric.de/en/2012/07/useful-jvm-flags-part-1-jvm-\"\n\"types-and-compiler-modes/>`__\"\nmsgstr \"\"\n\"`第 1 部分 - JVM 类型和编译器模式 <https://blog.codecentric.de/en/2012/07/useful-\"\n\"jvm-flags-part-1-jvm-types-and-compiler-modes/>`__\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:233\nmsgid \"\"\n\"`Part 2 - Flag Categories and JIT Compiler Diagnostics) \"\n\"<https://blog.codecentric.de/en/2012/07/useful-jvm-flags-part-2-flag-\"\n\"categories-and-jit-compiler-diagnostics/>`__\"\nmsgstr \"\"\n\"`第 2 部分 - 标志类别和 JIT 编译器诊断) <https://blog.codecentric.de/en/2012/07\"\n\"/useful-jvm-flags-part-2-flag-categories-and-jit-compiler-\"\n\"diagnostics/>`__\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:235\nmsgid \"\"\n\"`Part 3 - Printing all XX Flags and their Values \"\n\"<https://blog.codecentric.de/en/2012/07/useful-jvm-flags-part-3-printing-\"\n\"all-xx-flags-and-their-values/>`__\"\nmsgstr \"\"\n\"`第 3 部分 - 打印所有 XX 标志及其值 <https://blog.codecentric.de/en/2012/07/useful-\"\n\"jvm-flags-part-3-printing-all-xx-flags-and-their-values/>`__\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:237\nmsgid \"\"\n\"`Part 4 - Heap Tuning <https://blog.codecentric.de/en/2012/07/useful-jvm-\"\n\"flags-part-4-heap-tuning/>`__\"\nmsgstr \"\"\n\"`第 4 部分 - 堆调整 <https://blog.codecentric.de/en/2012/07/useful-jvm-flags-\"\n\"part-4-heap-tuning/>`__\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:239\nmsgid \"\"\n\"`Part 5 - Young Generation Garbage Collection \"\n\"<https://blog.codecentric.de/en/2012/08/useful-jvm-flags-part-5-young-\"\n\"generation-garbage-collection/>`__\"\nmsgstr \"\"\n\"`第 5 部分 - 年轻代垃圾回收 <https://blog.codecentric.de/en/2012/08/useful-jvm-\"\n\"flags-part-5-young-generation-garbage-collection/>`__\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:241\nmsgid \"\"\n\"`Part 6 - Throughput Collector <https://blog.codecentric.de/en/2013/01\"\n\"/useful-jvm-flags-part-6-throughput-collector/>`__\"\nmsgstr \"\"\n\"`第 6 部分 - 吞吐量收集器 <https://blog.codecentric.de/en/2013/01/useful-jvm-\"\n\"flags-part-6-throughput-collector/>`__\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:243\nmsgid \"\"\n\"`Part 7 - CMS Collector <https://blog.codecentric.de/en/2013/10/useful-\"\n\"jvm-flags-part-7-cms-collector/>`__\"\nmsgstr \"\"\n\"`第 7 部分 - CMS 收集器 <https://blog.codecentric.de/en/2013/10/useful-jvm-\"\n\"flags-part-7-cms-collector/>`__\"\n\n#: ../../Tigase_Administration/Configuration/JVM_settings.inc:245\nmsgid \"\"\n\"`Part 8 - GC Logging <https://blog.codecentric.de/en/2014/01/useful-jvm-\"\n\"flags-part-8-gc-logging/>`__\"\nmsgstr \"\"\n\"`第 8 部分 - GC 日志记录 <https://blog.codecentric.de/en/2014/01/useful-jvm-\"\n\"flags-part-8-gc-logging/>`__\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:2\nmsgid \"Session Manager\"\nmsgstr \"会话管理器\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:4\nmsgid \"\"\n\"Tigase Session Manager is where most of Tigase basic options can be \"\n\"configured, and where many operations are controlled from. Changes to \"\n\"session manager can effect operations throughout an entire XMPP \"\n\"installation, so care must be made when changing settings here.\"\nmsgstr \"\"\n\"Tigase 会话管理器可以配置大多数 Tigase 基本选项，也可以控制许多操作。对会话管理器的更改会影响整个 XMPP \"\n\"安装中的操作，因此在此处更改设置时必须小心。\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:9\nmsgid \"Mobile Optimizations\"\nmsgstr \"移动优化\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:11\nmsgid \"\"\n\"By default, Tigase employs XEP-0352 Client State Indication which allows \"\n\"for a more streamlined mobile experiencing by allowing the XMPP server to\"\n\" suppress or reduce the number of updates sent to a client thereby \"\n\"reducing the number of stanzas sent to a mobile client that is inactive. \"\n\"This employment is contained within the processor \"\n\"``ClientStateIndication`` and is independent from the MobileV1, MobileV2,\"\n\" MobileV3 settings.\"\nmsgstr \"\"\n\"默认情况下，Tigase 运用 XEP-0352 客户端状态指示，其通过允许 XMPP \"\n\"服务器抑制或减少发送到客户端的更新数量，从而减少发送到非活动移动客户端的节数，从而实现更流畅的移动体验。此应用包含在处理器 \"\n\"``ClientStateIndication`` 中，并且独立于 MobileV1、MobileV2、MobileV3 设置。\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:13\nmsgid \"\"\n\"However, this can be fine tuned by using mobile plugins from Tigase which\"\n\" can be used at the same time by adding the following line to the \"\n\"``config.tdsl`` file:\"\nmsgstr \"然而，这可以通过使用来自 Tigase 的移动插件进行微调，通过在 ``config.tdsl`` 文件中添加以下行并同时使用它：\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:24\nmsgid \"Logic Options are:\"\nmsgstr \"逻辑选项是：\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:27\nmsgid \"MobileV1\"\nmsgstr \"MobileV1\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:29\nmsgid \"Keeps all presence stanzas in queue until client is active.\"\nmsgstr \"将所有存在节保持在队列中直到客户端处于活动状态为止。\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:37\nmsgid \"MobileV2\"\nmsgstr \"MobileV2\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:39\nmsgid \"\"\n\"This setting delays delivery of presences while client is in inactive \"\n\"state, but only keeps the last presence for each full jid. **This is the \"\n\"default setting for CSI logic**.\"\nmsgstr \"当客户端处于非活动状态时，此设置会延迟呈现状态的传递，但仅保留每个完整 jid 的最后呈现状态。 **这是 CSI 逻辑的默认设置**。\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:47\nmsgid \"MobileV3\"\nmsgstr \"MobileV3\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:49\nmsgid \"\"\n\"Keeps the same presence logic as MobileV2, but also queues Message \"\n\"Carbons. **Currently not supported by CSI processor, will cause issues**.\"\nmsgstr \"保持与 MobileV2 相同的存在逻辑，但也把 Message Carbons 排队。**目前不支持 CSI 处理器，会导致问题**。\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:57\nmsgid \"Disabling CSI\"\nmsgstr \"禁用 CSI\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:59\nmsgid \"\"\n\"If you wish to not use the ``ClientStateIndication`` processor, set the \"\n\"following in your ``config.tdsl`` file:\"\nmsgstr \"如果您不想使用 ``ClientStateIndication`` 处理器，请在 ``config.tdsl`` 文件中设置以下内容：\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:69\nmsgid \"A note about Mobile Plugins\"\nmsgstr \"关于移动插件的说明\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:71\nmsgid \"\"\n\"Previously, you could enable Mobile optimization logic using by enabling \"\n\"``Mobile_V1 (){}`` bean to Session Manager: ``sess-man () {}`` bean.\"\nmsgstr \"\"\n\"之前，您可以通过启用 ``Mobile_V1 (){}`` bean 到会话管理器来启用移动优化逻辑：``sess-man () {}`` \"\n\"bean。\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:73\nmsgid \"\"\n\"If you have used these in the past, it is recommended you change your \"\n\"system to use the CSI processor with the appropriate mobile processing \"\n\"logic.\"\nmsgstr \"如果您过去使用过这些，建议您更改系统以使用具有适当移动处理逻辑的 CSI 处理器。\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:75\nmsgid \"\"\n\"If you require v3 logic, or do not wish to use CSI, be sure to disable it\"\n\" using the above option.\"\nmsgstr \"如果您需要 v3 逻辑，或者不希望使用 CSI，请务必使用上述选项禁用它。\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:79\nmsgid \"threads-pool\"\nmsgstr \"线程池\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:81\nmsgid \"\"\n\"The ``threadsNo`` property allows you to fine-tune the SM plugin’s \"\n\"(processors) thread pool. With the default settings every plugin gets his\"\n\" own thread pool. This guarantees the best performance and optimal \"\n\"resource usage. The downside of this setting is that packets can arrive \"\n\"out of order if they are processed within different thread pools.\"\nmsgstr \"\"\n\"``threadsNo`` 属性允许你微调 SM \"\n\"插件(处理器)的线程池。使用默认设置，每个插件都有自己的线程池。这保证了最佳性能和最佳资源使用。此设置的缺点是，如果在不同的线程池中处理数据包，它们可能会乱序到达。\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:83\nmsgid \"\"\n\"We can even fine tune this packet processing. Let’s say you want most of \"\n\"the plugins to be executed within a single thread pool to preserve packet\"\n\" ordering for them, but for some selected plugins that should execute \"\n\"within separate thread pools to improve performance. Let’s say, \"\n\"authentication packets and user registration can be actually executed in \"\n\"a separate thread pools as we do not worry about an order for them. Users\"\n\" cannot send or receive anything else before they authenticates anyway. \"\n\"The solution is to specify a number of threads for the selected plugin. \"\n\"For example, setting a common thread pool for all plugins but \"\n\"registration and authentication can be done with following configuration:\"\nmsgstr \"我们甚至可以微调这个数据包处理。比方说您希望大多数插件在单个线程池中执行以保留它们的数据包顺序，但对于一些选定插件应该在单独的线程池中执行以提高性能。比如说，身份验证数据包和用户注册实际上可以在单独的线程池中执行，因为我们不担心它们的顺序。无论如何，用户在进行身份验证之前都无法发送或接收任何其他内容。解决方案是为所选插件指定线程数。例如，为所有插件设置一个公共线程池，但注册和身份验证可以通过以下配置完成：\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:96\nmsgid \"\"\n\"This replaces the old ``--sm-threads-pool`` property, as well as \"\n\"specifying thread pools in ``--sm-plugins``.\"\nmsgstr \"这取代了旧的 ``--sm-threads-pool`` 属性，并在 ``--sm-plugins`` 中指定线程池。\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:100\nmsgid \"Thread Pool factor\"\nmsgstr \"线程池因子\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:102\nmsgid \"\"\n\"Session manager can control the number of available thread pools for each\"\n\" processor. By adding the following line to the ``config.tdsl`` file, the\"\n\" global thread pool can be increased by a specified factor:\"\nmsgstr \"会话管理器可以控制每个处理器的可用线程池数量。通过在 ``config.tdsl`` 文件中添加以下行，可以将全局线程池增加一个指定的因子：\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:110\nmsgid \"In this case, the global thread pools is increased by a factor or 3.\"\nmsgstr \"在这种情况下，全局线程池增加了 3 倍。\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:114\nmsgid \"Strategy\"\nmsgstr \"Strategy\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:116\nmsgid \"\"\n\"The ``Strategy`` property allows users to specify Clustering Strategy \"\n\"class which should be used for handling clustering environment; by \"\n\"default ``SMNonCachingAllNodes`` is used.\"\nmsgstr \"``Strategy`` 属性允许用户指定用于处理集群环境的集群策略类；默认情况下使用 ``SMNonCachingAllNodes`` 。\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:118\nmsgid \"\"\n\"Any class implementing ``tigase.cluster.strategy.ClusteringStrategyIfc`` \"\n\"interface may be used for this setting.\"\nmsgstr \"任何实现 ``tigase.cluster.strategy.ClusteringStrategyIfc`` 接口的类都可以用于此设置。\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:120\nmsgid \"Example:\"\nmsgstr \"例子：\"\n\n#: ../../Tigase_Administration/Configuration/Session_Manager.inc:128\nmsgid \"This replaces the old ``--sm-cluster-strategy-class`` setting from v7.1.\"\nmsgstr \"这替换了 v7.1 中的旧的 ``--sm-cluster-strategy-class`` 设置。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:4\nmsgid \"Virtual Hosts in Tigase Server\"\nmsgstr \"Tigase 服务器中的虚拟主机\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:6\nmsgid \"\"\n\"Tigase server supports multiple virtual hosts in a single server \"\n\"installation. Virtual hosts can be added or removed, enabled or disabled \"\n\"during runtime without restarting the service or disrupting normal \"\n\"operation.\"\nmsgstr \"Tigase 服务器在单个服务器安装中支持多个虚拟主机。可以在运行时添加或删除，启用或禁用虚拟主机，而无需重新启动服务或中断正常操作。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:8\nmsgid \"\"\n\"This document describes how virtual hosts work in Tigase server and how \"\n\"to get the most out of this feature in your installation.\"\nmsgstr \"本文档描述了虚拟主机在 Tigase 服务器中的工作方式以及如何在您的安装中充分利用此功能。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:10\nmsgid \"\"\n\"The :ref:`'default-virtual-host'<virtHosts>` property allows to define \"\n\"name of the single vhost domain which will be considered a default vhost \"\n\"domain for this installation. It allows you just to configure the domain \"\n\"name. Any additional configuration needs to be configured using ad-hoc \"\n\"commands.\"\nmsgstr \"\"\n\":ref:`'default-virtual-host'<virtHosts>` \"\n\"属性允许定义单个虚拟主机域的名称，该域将被视为此安装的默认虚拟主机域。它只允许您配置域名。任何额外的配置都需要使用 ad-hoc 命令进行配置。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:12\n#, fuzzy\nmsgid \"\"\n\"Virtual hosts should be managed using ad-hoc commands or admin ui, visit \"\n\":ref:`Add and Manage Domains<addManageDomain>` for description of vhosts \"\n\"management process or visit :ref:`Specification for ad-hoc Commands Used \"\n\"to Manage Virtual Domains<adhocCommands>` for more information about ad-\"\n\"hoc commands.\"\nmsgstr \"\"\n\"虚拟主机应使用 ad-hoc 命令或管理 ui 进行管理，请访问 :ref:`添加和管理域<addManageDomain>` \"\n\"以了解虚拟主机管理过程的说明或访问 :ref:`用于管理虚拟域的临时命令规范<adhocCommands>` 有关临时命令的更多信息。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:14\nmsgid \"\"\n\"If you have components that may not be able to handle multiple vhosts or \"\n\"cluster mode, we have developed a virtual component solution as well, \"\n\"details in the :ref:`Virtual Components for the Tigase \"\n\"Cluster<virtualComponents>` section.\"\nmsgstr \"\"\n\"如果您的组件可能无法处理多个虚拟主机或集群模式，我们也开发了一个虚拟组件解决方案，详细信息请参见 :ref:`Tigase \"\n\"集群的虚拟组件<virtualComponents>` 部分。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:16\nmsgid \"\"\n\"You may also want to reference the Vhosts API for additional information:\"\n\" - :ref:`API Description for Virtual Domains Management in Tigase \"\n\"Server<addManageDomain>`.\"\nmsgstr \"\"\n\"您可能还想参考 Vhosts API 以获取更多信息: - :ref:`Tigase 服务器中虚拟域管理的 API \"\n\"描述<addManageDomain>`。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:24\nmsgid \"Default VHost configuration\"\nmsgstr \"默认虚拟主机配置\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:26\nmsgid \"\"\n\"It’s possible to specify initial default configuration for all Virtual \"\n\"Host in TDSL configuration file (i.e. ``etc/config.tdsl``) for selected \"\n\"parameters. To do so you should specify each configuration option within \"\n\"``defaults`` bean belonging to ``vhost-man`` bean:\"\nmsgstr \"\"\n\"可以在 TDSL 配置文件（即 ``etc/config.tdsl``）中为所选参数指定所有虚拟主机的初始默认配置。为此，您应该在属于 \"\n\"``vhost-man`` bean 的 ``defaults`` bean 中指定每个配置选项：\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:44\nmsgid \"\"\n\"After initial definition of default configuration or after first startup \"\n\"of Tigase XMPP Server it is possible to configure Virtual Host defaults \"\n\"using ad-hoc commands by modifying values for ``default`` using ad-hoc as\"\n\" described in :ref:`Specification for ad-hoc Commands Used to Manage \"\n\"Virtual Domains<adhocCommands>`.\"\nmsgstr \"\"\n\"初始定义默认配置或首次启动 Tigase XMPP 服务器后，可以使用 ad-hoc 命令配置虚拟主机默认值，方法是使用 ad-hoc 修改 \"\n\"``default`` 的值，如 :ref:`用于管理虚拟域的临时命令规范<adhocCommands>` 中所述。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:46\nmsgid \"\"\n\"Alternatively, you may edit default Virtual Host configuration \"\n\"(configuration for domain ``default``) using Admin UI which by default is\"\n\" available at http://localhost:8080/admin/.\"\nmsgstr \"\"\n\"或者，您可以使用管理 UI 编辑默认虚拟主机配置（域 ``default`` 的配置），这些在默认情况下可通过\\n\"\n\"http://localhost:8080/admin/ 获得。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:51\nmsgid \"Specification for ad-hoc Commands Used to Manage Virtual Domains\"\nmsgstr \"用于管理虚拟域的临时命令规范\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:53\nmsgid \"\"\n\"There are 3 ad-hoc commands for virtual domains management in the Tigase \"\n\"server:\"\nmsgstr \"Tigase 服务器中有 3 个用于虚拟域管理的临时命令：\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:55\nmsgid \"\"\n\"``VHOSTS_RELOAD`` used to reload virtual domains list from the repository\"\n\" (database).\"\nmsgstr \"``VHOSTS_RELOAD`` 用于从存储库（数据库）重新加载虚拟域列表。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:57\nmsgid \"\"\n\"``VHOSTS_UPDATE`` used to add a new virtual domain or update information \"\n\"for existing one.\"\nmsgstr \"``VHOSTS_UPDATE`` 用于添加新的虚拟域或更新现有虚拟域的信息。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:59\nmsgid \"\"\n\"``VHOSTS_REMOVE`` used to remove an existing virtual host from the \"\n\"running server.\"\nmsgstr \"``VHOSTS_REMOVE`` 用于从正在运行的服务器中删除现有的虚拟主机。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:61\nmsgid \"\"\n\"Syntax of the commands follows the specification described in `XEP-0050 \"\n\"<http://xmpp.org/extensions/xep-0050.html>`__. Extra information required\"\n\" to complete the command is carried as data forms described in `XEP-0004 \"\n\"<http://xmpp.org/extensions/xep-0004.html>`__.\"\nmsgstr \"\"\n\"命令的语法遵循 `XEP-0050 <http://xmpp.org/extensions/xep-0050.html>`__ \"\n\"中描述的规范。完成命令所需的额外信息以 `XEP-0004 \"\n\"<http://xmpp.org/extensions/xep-0004.html>`__ 中描述的数据形式携带。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:63\nmsgid \"\"\n\"All commands are accepted by the server only when send by the \"\n\"installation administrator. If the command is sent from any other account\"\n\" ``<not-authorized />`` error is returned. To grant administrator rights \"\n\"to an account you have to set ``admins`` property in the ``config.tdsl`` \"\n\"configuration file.\"\nmsgstr \"\"\n\"仅当安装管理员发送所有命令时，服务器才会接受所有命令。如果命令是从任何其他帐户发送的，则返回 ``<not-authorized />`` \"\n\"错误。若要授予帐户管理员权限，您必须在 ``admins`` 配置文件中设置 ``admins`` 属性。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:65\nmsgid \"\"\n\"Commands are sent to the 'vhost-man' server component and the 'to' \"\n\"attribute of the stanza must contain a full JID of the VHostManager on \"\n\"the server. The full **JID** consists of the component name: 'vhost-man' \"\n\"and the local domain, that is domain which is already on the list of \"\n\"virtual domains and is active. Assuming 'existing.domain.com' one of \"\n\"domains already activated for the server installation the **JID** is: \"\n\"'vhost-man@existing.domain.com'.\"\nmsgstr \"\"\n\"命令被发送到 'vhost-man' 服务器组件，并且该节的 'to' 属性必须包含服务器上 VHostManager 的完整 JID。完整的 \"\n\"**JID** 包含组件名称：'vhost-man' 和本地域，即已经在虚拟域列表中并且处于活动状态的域。假设已经为服务器安装激活的域之一 \"\n\"'existing.domain.com' 的 **JID** 是：'vhost-man@existing.domain.com'。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:68\nmsgid \"Reloading the Domains List from the Database\"\nmsgstr \"从数据库重新加载域列表\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:70\nmsgid \"\"\n\"In order to reload virtual domains from the permanent repository other \"\n\"than configuration file, you have to send ``VHOSTS_RELOAD`` ad-hoc \"\n\"command to the VHostManager on the server.\"\nmsgstr \"为了从配置文件以外的永久存储库重新加载虚拟域，您必须向服务器上的 VHostManager 发送 ``VHOSTS_RELOAD`` 临时命令。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:72\nmsgid \"The reload command request is of the form:\"\nmsgstr \"重新加载命令请求的形式为：\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:83\nmsgid \"\"\n\"The server sends a response upon successful completion of the command \"\n\"with current number of virtual domains server by the installation:\"\nmsgstr \"服务器在成功完成命令后使用当前虚拟域服务器数量通过安装发送响应：\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:102\nmsgid \"\"\n\"If the command is sent from an account other than admin, the server \"\n\"returns an error:\"\nmsgstr \"如果命令是从 admin 以外的帐户发送的，则服务器会返回错误：\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:121\nmsgid \"\"\n\"The response doesn’t have any special meaning other then end-user \"\n\"information. The client may ignore the response as it is sent after the \"\n\"command has been executed.\"\nmsgstr \"除了最终用户信息之外，响应没有任何特殊含义。客户端可能会忽略响应，因为它是在命令执行后发送的。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:124\nmsgid \"Adding a New Domain or Updating Existing One\"\nmsgstr \"添加新域或更新现有域\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:126\nmsgid \"\"\n\"In order to add a new domain or update existing one you have to send an \"\n\"ad-hoc command ``VHOSTS_UPDATE`` with at least one domain name in the \"\n\"command data form. You can also specify whether the domain is enabled or \"\n\"disabled but this is optional. Future releases may allow for setting \"\n\"additional parameters for the domain: maximum number of user accounts for\"\n\" this domain, anonymous login enabled/disabled for the domain, \"\n\"registration via XMPP enabled/disabled for this domain and some more \"\n\"parameters not specified yet.\"\nmsgstr \"\"\n\"为了添加新域或更新现有域，您必须发送一个临时命令 ``VHOSTS_UPDATE`` \"\n\"，其命令数据形式中至少包含一个域名。您还可以指定此域是启用还是禁用，但这些是可选的。未来的版本可能允许为域设置其他参数：此域的最大用户帐户数，对域启用/禁用匿名登录、对该域启用/禁用通过\"\n\" XMPP 注册以及尚未指定的更多参数。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:128\nmsgid \"The domain add/update command request is of the form:\"\nmsgstr \"域添加/更新命令请求的形式为：\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:150\nmsgid \"\"\n\"Please note: Character case in the command field variable names does \"\n\"matter.\"\nmsgstr \"请注意：命令字段变量名称中的字符大小写确实很重要。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:152\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:194\nmsgid \"\"\n\"Upon successful completion of the command the server sends a response \"\n\"back to the client with information of the existing number of virtual \"\n\"hosts on the server:\"\nmsgstr \"成功完成命令后，服务器将响应返回给客户端，其中包含服务器上现有虚拟主机数量的信息：\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:172\nmsgid \"Removing a Virtual Domain From the Server\"\nmsgstr \"从服务器中删除虚拟域\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:174\nmsgid \"\"\n\"In order to remove a virtual domain you have to send ``VHOSTS_REMOVE`` \"\n\"command to the server with the domain name.\"\nmsgstr \"为了删除一个虚拟域，你必须发送 ``VHOSTS_REMOVE`` 命令到带有域名的服务器。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:176\nmsgid \"The domain remove command is sent by the client:\"\nmsgstr \"域删除命令由客户端发送：\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:216\nmsgid \"Virtual Components for the Cluster Mode\"\nmsgstr \"集群模式的虚拟组件\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:218\nmsgid \"\"\n\"Let’s assume you have a cluster installation and you want to include a \"\n\"component in your installation which doesn’t support the cluster mode \"\n\"yet. If you put it on all nodes as a separate instances they will work \"\n\"out of sync and overall functionality might be useless. If you put on one\"\n\" node only it will work correctly but it will be visible to users \"\n\"connected to this one node only.\"\nmsgstr \"假设您有一个集群安装，并且您希望在您的安装中还包含一个不支持集群模式的组件。如果您将它作为单独的实例放在所有节点上，它们将不同步，并且整体功能可能毫无用处。如果您只放置一个节点，它将正常工作，但其仅对连接到该节点的用户可见。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:220\nmsgid \"\"\n\"Ideally you would like to have a mechanism to install it on one node and \"\n\"put some redirections on other nodes to forward all packets for this \"\n\"component to a node where this component is working. Redirection on it’s \"\n\"own is not enough because the component must be visible in service \"\n\"discovery list and must be visible somehow to users connected to all \"\n\"nodes.\"\nmsgstr \"理想情况下，您希望有一种机制将其安装在一个节点上，并在其他节点上进行一些重定向，以将该组件的所有数据包转发到该组件正在工作的节点。它自己的重定向是不够的，因为该组件必须在服务发现列表中可见，并且必须以某种方式对连接到所有节点的用户可见。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:222\nmsgid \"\"\n\"This is where the virtual components are handy. They are visible to users\"\n\" as a local normal component, they seem to be a real local component but \"\n\"in fact they just forward all requests/packets to a cluster node where \"\n\"the real component is working.\"\nmsgstr \"这是虚拟组件很方便的地方。它们作为本地普通组件对用户可见，它们看起来似乎是真正的本地组件，但实际上它们只是将所有请求/数据包转发到真正组件上正在工作的集群节点。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:224\nmsgid \"\"\n\"Virtual component is a very lightweight ServerComponent implementation in\"\n\" Tigase server. It can pretend to be any kind of component and can \"\n\"redirect all packets to a given address. They can mimic native Tigase \"\n\"components as well as third-party components connected over external \"\n\"component protocol (XEP-0114).\"\nmsgstr \"\"\n\"虚拟组件是 Tigase 服务器中一个非常轻量级的 ServerComponent \"\n\"实现。它可以伪装成任何类型的组件，并且可以将所有数据包重定向到给定地址。它们可以模仿原生 Tigase 组件也可以作为通过外部组件协议 \"\n\"(XEP-0114) 连接的第三方组件。\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:226\nmsgid \"\"\n\"Configuration is very simple and straightforward, in fact it is very \"\n\"similar to configuration of any Tigase component. You set a real \"\n\"component name as a name of the component and a virtual component class \"\n\"name to load. Let’s say we want to deploy MUC component this way. The MUC\"\n\" component is visible as ``muc.domain.oug`` in the installation. Thus the\"\n\" name of the component is: ``muc``\"\nmsgstr \"\"\n\"配置非常简单明了，实际上它与任何 Tigase \"\n\"组件的配置非常相似。您将真实组件名称设置为组件名称和要加载的虚拟组件类名称。假设我们想以这种方式部署 MUC 组件。 MUC 组件在安装中显示为 \"\n\"``muc.domain.oug``。因此组件的名称是：``muc``\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:232\nmsgid \"\"\n\"This is pretty much all you need to load a virtual component. A few other\"\n\" options are needed to point to correct destination addresses for packets\"\n\" forwarding and to set correct service discovery parameters:\"\nmsgstr \"这几乎是加载虚拟组件所需的全部内容。需要一些其他选项来指向正确的数据包转发目标地址并设置正确的服务发现参数：\"\n\n#: ../../Tigase_Administration/Configuration/Vhosts.inc:246\nmsgid \"That’s it.\"\nmsgstr \"就这些\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:2\nmsgid \"Settings for Custom Logging in Tigase\"\nmsgstr \"Tigase 中的自定义日志设置\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:4\nmsgid \"\"\n\"Logging can be an important tool to monitor your server’s health and \"\n\"performance. Logging may be controlled and customized on a per-component \"\n\"basis.\"\nmsgstr \"日志记录可以成为监控服务器运行状况和性能的重要工具。日志记录可以在每个组件的基础上进行控制和定制。\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:6\nmsgid \"\"\n\"A ``logging`` bean has been implemented to allow more flexible \"\n\"configuration of logging in the Tigase XMPP Server.\"\nmsgstr \"实现了一个 ``logging`` bean，以允许在 Tigase XMPP 服务器中更灵活地配置日志记录。\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:9\nmsgid \"Configuring logging\"\nmsgstr \"配置日志记录\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:12\nmsgid \"In the config file\"\nmsgstr \"在配置文件中\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:14\nmsgid \"\"\n\"Default logging configuration for your installation is kept in the config\"\n\" file and it may be adjusted there.\"\nmsgstr \"您安装的默认日志配置保存在配置文件中，并且可以在那里进行调整。\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:45\nmsgid \"\"\n\"You only need to specify the settings you wish to customize, otherwise \"\n\"they will be left as default.\"\nmsgstr \"您只需要指定想要自定义的设置，否则它们将保留默认设置。\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:47\nmsgid \"\"\n\"``packet-debug-full`` - controls whether log entries should be obfuscated\"\n\" (all CData of all elements will be replaced by ``CData size: <length in \"\n\"bytes of the replaced string>``) or not; default: ``false``.\"\nmsgstr \"\"\n\"``packet-debug-full`` - 控制日志条目是否应该被混淆（所有元素的所有 CData 将被替换为 ``CData size: \"\n\"<length in bytes of the replaced string>``) ; 默认值: ``false``。\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:49\nmsgid \"\"\n\"``rootLevel`` - Defines the root level of logging for all components not \"\n\"otherwise defined. Default is CONFIG\"\nmsgstr \"``rootLevel`` - 为所有未另行定义的组件定义日志记录的根级别。默认是CONFIG\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:51\nmsgid \"\"\n\"``loggers`` - Defines the level of logging for packages running in tigase\"\n\" server. This is similar to the --debug setting, however you must use \"\n\"``tigase.{package}`` format. Default is NONE.\"\nmsgstr \"\"\n\"``loggers`` - 定义在 tigase 服务器中运行的包的日志记录级别。这类似于 --debug 设置，但是您必须使用 \"\n\"``tigase.{package}`` 格式。默认为NONE。\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:53\nmsgid \"\"\n\"``handlers`` - Defines the level of logging for File output and Console \"\n\"output.\"\nmsgstr \"``handlers`` - 定义文件输出和控制台输出的日志记录级别。\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:55\nmsgid \"\"\n\"``FileHandler`` - is the file output for log files, with the following \"\n\"options:\"\nmsgstr \"``FileHandler`` - 是日志文件的文件输出，有以下选项：\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:57\nmsgid \"``level`` - specifies the level of logs to be written, default is ALL.\"\nmsgstr \"``level`` - 指定要写入的日志级别，默认为 ALL。\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:59\nmsgid \"\"\n\"``append`` - whether to append to the log or replace it during restart. \"\n\"Default is true.\"\nmsgstr \"``append`` - 是追加到日志还是在重启期间替换它。默认是true。\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:61\nmsgid \"\"\n\"``count`` - number of individual log files to keep at set limit. Default \"\n\"is 5. (default settings will continue appending logs until 5 files at \"\n\"10MB are reached, then the oldest file will be overwritten.)\"\nmsgstr \"\"\n\"``count`` - 保持在设定限制的单个日志文件的数量。默认为 5。(默认设置将继续附加日志，直到达到 5 个 10MB \"\n\"的文件，然后最旧的文件将被覆盖。）\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:63\n#: ../../Tigase_Administration/Configuration/Logging.inc:73\nmsgid \"\"\n\"``formatter`` - specifies the package to format logging output. Default \"\n\"is tigase.util.LogFormatter.\"\nmsgstr \"``formatter`` - 指定要格式化日志输出的包。默认为 tigase.util.LogFormatter。\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:65\nmsgid \"``limit`` - Byte limit for each log file. Default is 10000000 or 10MB.\"\nmsgstr \"``limit`` - 每个日志文件的字节限制。默认值为 10000000 或 10MB。\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:67\nmsgid \"\"\n\"``pattern`` - Directory and filename of the log file with respect to the \"\n\"Tigase installation directory. Default is logs/tigase.log.\"\nmsgstr \"``pattern`` - 关于 Tigase 安装目录的日志文件的目录和文件名。默认为日志/tigase.log。\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:69\nmsgid \"\"\n\"``ConsoleHandler`` - Determines the formatting for Tigase output to \"\n\"console.\"\nmsgstr \"``ConsoleHandler`` - 决定 Tigase 输出到控制台的格式。\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:71\nmsgid \"``level`` - specifies the level of logs to be written, default is WARNING.\"\nmsgstr \"``level`` - 指定要写入的日志级别，默认为 WARNING。\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:77\nmsgid \"Disabling colored output\"\nmsgstr \"禁用彩色输出\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:79\nmsgid \"\"\n\"If for some reason you don’t want colored output in the logs you can \"\n\"disable it by setting ``disable_logger_color`` to ``true``. For \"\n\"convenience, you can uncomment in ``etc/tigase.conf`` following line:\"\nmsgstr \"\"\n\"如果由于某种原因您不想在日志中显示彩色输出，您可以通过将 ``disable_logger_color`` 设置为 ``true`` \"\n\"来禁用它。为方便起见，您可以在 ``etc/tigase.conf`` 以下行中取消注释：\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:86\nmsgid \"Ad-hoc changes to the logging configuration\"\nmsgstr \"对日志记录配置的临时更改\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:88\nmsgid \"\"\n\"It is also possible to use ad-hoc command named **Set package logging** \"\n\"with id ``logging-set`` available at ``message-router@domain`` (where \"\n\"domain is your server name) to reconfigure logging level of packets at \"\n\"runtime without requirement of restarting the Tigase XMPP Server.\"\nmsgstr \"\"\n\"也可以使用名为 **Set package logging** 的 ad-hoc 命令，在 ``message-router@domain`` \"\n\"(其中 domain 是您的服务器名称)用id ``logging-set`` 来重新配置日志记录级别运行时的数据包，且无需重新启动 Tigase\"\n\" XMPP 服务器。\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:90\n#: ../../Tigase_Administration/Configuration/Logging.inc:124\nmsgid \"**Note**\"\nmsgstr \"**注意**\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:92\nmsgid \"Those changes will be applied to this single cluster node.\"\nmsgstr \"这些更改将应用于此单个集群节点。\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:98\nmsgid \"Using Admin UI\"\nmsgstr \"使用Admin UI\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:100\nmsgid \"\"\n\"If your Tigase XMPP Server is running with HTTP server and with Admin UI \"\n\"enabled, then the easiest way to change logging configuration is by using\"\n\" Admin UI. After logging into web interface, open ``Configuration`` \"\n\"section and select ``Set package logging`` command. This will bring to \"\n\"you a form which you need to fill in with following fields:\"\nmsgstr \"\"\n\"如果您的 Tigase XMPP 服务器正在运行 HTTP 服务器并启用了 Admin UI，那么更改日志记录配置的最简单方法是使用 Admin \"\n\"UI。登录到 Web 界面后，打开 ``Configuration`` 部分并选择 ``Set package logging`` \"\n\"命令。这将为您带来一个表格，您需要填写以下字段：\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:102\n#: ../../Tigase_Administration/Configuration/Logging.inc:113\nmsgid \"\"\n\"``Package name`` - should contain Java package or class name for which \"\n\"you wish to change logging level\"\nmsgstr \"``Package name`` - 应包含您希望更改日志记录级别的 Java 包或类名\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:104\n#: ../../Tigase_Administration/Configuration/Logging.inc:115\nmsgid \"\"\n\"``Level`` - select a logging level you wish to apply to entered package \"\n\"name *(``OFF`` means that logging will be disabled)*\"\nmsgstr \"``Level`` - 选择您希望的输入包名称的日志记录级别 (* ``OFF`` 表示将禁用日志记录*)\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:106\nmsgid \"\"\n\"After pressing ``Submit`` your form will be passed to the server for \"\n\"validation and selected changes will be applied.\"\nmsgstr \"按下 ``Submit`` 后，您的表单将被传递到服务器进行验证，并将应用选定的更改。\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:109\nmsgid \"Using ad-hoc command\"\nmsgstr \"使用ad-hoc命令\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:111\nmsgid \"\"\n\"If you have access to the XMPP admin account of Tigase XMPP Server and \"\n\"XMPP client which supports ad-hoc command execution, you may connect with\"\n\" your XMPP client to the Tigase XMPP Server and look for adh-hoc commands\"\n\" available at ``message-router@domain`` (where domain is your server \"\n\"name). Within found ad-hoc commands you should find command named ``Set \"\n\"package logging`` or ``logging-set`` (that depends what your XMPP client \"\n\"is showing, id or name of the command) and you should execute it. Tigase \"\n\"XMPP Server will return a form which you need to fill in with following \"\n\"fields:\"\nmsgstr \"\"\n\"如果您有权访问 Tigase XMPP 服务器和支持执行 ad-hoc 命令的 XMPP 客户端的 XMPP 管理员帐户，您可以将 XMPP \"\n\"客户端连接到 Tigase XMPP 服务器并在 ``message-router@domain`` (其中 domain 是您的服务器名称\"\n\")中查找可用的adh-hoc命令 。在找到的adh-hoc命令中，您应该找到名为 ``Set package logging`` 或 \"\n\"``logging-set`` 的命令（这取决于您的 XMPP 客户端显示的内容，命令的 ID 或名称）并且您应该执行它。 Tigase XMPP\"\n\" 服务器将返回一个表单，您需要填写以下字段：\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:117\nmsgid \"\"\n\"After submitting the form, Tigase XMPP Server will validate your request \"\n\"and update logging configuration.\"\nmsgstr \"提交表单后，Tigase XMPP 服务器将验证您的请求并更新日志配置。\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:120\nmsgid \"Using REST API\"\nmsgstr \"使用 REST API\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:122\nmsgid \"\"\n\"If you have Tigase XMPP Server with REST API enabled, you can use it for \"\n\"configuring logging of Tigase XMPP Server as well.\"\nmsgstr \"如果您启用了 REST API 的 Tigase XMPP 服务器，您也可以使用它来配置 Tigase XMPP 服务器的日志记录。\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:126\nmsgid \"\"\n\"As with all HTTP REST API requests you will require a valid API key and \"\n\"in this case a valid admin credentials to authenticate a HTTP request \"\n\"using Basic HTTP Authentication.\"\nmsgstr \"\"\n\"与所有 HTTP REST API 请求一样，您将需要一个有效的 API 密钥，在这种情况下，需要一个有效的管理员凭据来使用基本 HTTP \"\n\"身份验证对 HTTP 请求进行身份验证。\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:128\nmsgid \"\"\n\"All you need to to is to send a HTTP POST request to ``/rest/adhoc\"\n\"/message-router@domain.com`` (where domain is your server name) with \"\n\"``Contect-Type`` set to ``application/xml`` and a following XML as a \"\n\"payload to set logging level of ``tigase.server`` package to ``ALL``.\"\nmsgstr \"\"\n\"您需要做的就是发送一个 HTTP POST 请求到 ``/rest/adhoc/message-router@domain.com`` \"\n\"(其中域是您的服务器名称)，并将 ``Contect-Type`` 设置为 ``application/xml`` 和以下 XML 作为有效负载将\"\n\" ``tigase.server`` 包的日志记录级别设置为 ``ALL``。\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:151\nmsgid \"Alternate loggers in Tigase - Logback\"\nmsgstr \"Tigase 中的备用记录器 - Logback\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:153\nmsgid \"\"\n\"It’s possible to use Logback for logging purposes, which offers certain \"\n\"interesting features (async logging, better control over log rotation, on\"\n\" the fly changing logging configuration)\"\nmsgstr \"可以将 Logback 用于日志记录目的，它提供了某些有趣的功能（异步日志记录，更好地控制日志轮换，动态更改日志记录配置）\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:155\nmsgid \"\"\n\"Requirements: \\\\* slf4j-api.jar (provided in ``-dist-max`` package) \\\\* \"\n\"jul-to-slf4j.jar (provided in ``-dist-max`` package) \\\\* desired logger \"\n\"libraries (for logback it’s ``logback-classic.jar`` and ``logback-\"\n\"core.jar`` (provided in -dist-max).\"\nmsgstr \"\"\n\"要求: \\\\* slf4j-api.jar (在 ``-dist-max`` 包中提供) \\\\* jul-to-slf4j.jar (在 \"\n\"``-dist-max`` 包中提供) \\\\* 所需的记录器库 (对于 logback 它是 ``logback-classic.jar`` 和 \"\n\"``logback-core.jar`` (在 -dist-max中提供)。\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:157\nmsgid \"\"\n\"Configuration boils down to adding slf4j bridge handler to the list of \"\n\"build-in Java Logger handlers configuration, which in Tigase translates \"\n\"to adding following line to ``etc/config.tdsl``:\"\nmsgstr \"\"\n\"配置归结为将 slf4j 桥处理程序添加到内置 Java Logger 处理程序配置列表中，在 Tigase 中，这转换为在 \"\n\"``etc/config.tdsl`` 中添加以下行：\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:165\nmsgid \"After that ``etc/logback.xml`` configuration file will be used.\"\nmsgstr \"之后将使用 ``etc/logback.xml`` 配置文件。\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:167\nmsgid \"\"\n\"As stated in [jul-to-slf4j bridge documentation](\\\\ \"\n\"http://www.slf4j.org/legacy.html#jul-to-slf4j) it’s essential to include \"\n\"``LevelChangePropagator`` to eliminate translation overhead for disabled \"\n\"log statements:\"\nmsgstr \"\"\n\"如 [jul-to-slf4j 桥接文档](\\\\ http://www.slf4j.org/legacy.html#jul-to-slf4j) \"\n\"中所述，必须包含 ``LevelChangePropagator`` 以消除禁用日志的翻译开销声明：\"\n\n#: ../../Tigase_Administration/Configuration/Logging.inc:176\nmsgid \"\"\n\"NOTE, that it may be prudent to remove configuration of all old JUL \"\n\"logger by appending following to ``etc/logback.xml`` configuration:\"\nmsgstr \"注意，通过将以下内容附加到 ``etc/logback.xml`` 配置来删除所有旧 JUL 记录器的配置可能是谨慎的：\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:2\nmsgid \"Tigase Advanced Options\"\nmsgstr \"Tigase 高级选项\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:4\nmsgid \"\"\n\"This section is designed to include a number of advanced configuration \"\n\"options available within Tigase, but may not have a relevant section yet \"\n\"to house them.\"\nmsgstr \"本部分旨在包括 Tigase 中可用的许多高级配置选项，但可能还没有相关部分来安置他们。\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:7\nmsgid \"Using CAPTCHA for in-band registration\"\nmsgstr \"使用 CAPTCHA 进行带内注册\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:9\nmsgid \"\"\n\"To reduce false or spam registrations to Tigase XMPP Server, there is now\"\n\" the ability to add CAPTCHA forms as a part of the in-band registration. \"\n\"The CAPTCHA will generate a random math equation and ask the user \"\n\"registering a new account to answer it. This may be enabled as a sub-\"\n\"option of enabling registration in config.tdsl:\"\nmsgstr \"\"\n\"为了减少对 Tigase XMPP 服务器的虚假或垃圾邮件注册，现在可以将 CAPTCHA 表单添加为带内注册的一部分。 CAPTCHA \"\n\"将生成一个随机数学方程式，并要求注册新帐户的用户回答它。这可以作为在 config.tdsl 中启用注册的子选项被启用：\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:23\nmsgid \"\"\n\"3 unsuccessful attempts will result in the captcha being invalidated and \"\n\"a client will receive an error message.\"\nmsgstr \"3 次不成功的尝试将导致验证码无效，并且客户端将收到错误消息。\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:27\nmsgid \"Enabling Empty Nicknames\"\nmsgstr \"启用空昵称\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:29\nmsgid \"\"\n\"Tigase can now support users with empty nicknames. This can be enabled by\"\n\" adding the following code in config.tdsl.\"\nmsgstr \"Tigase 现在可以支持昵称为空的用户。这可以通过在 config.tdsl 中添加以下代码来启用。\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:41\nmsgid \"Enable Silent Ignore on Packets Delivered to Unavailable Resources\"\nmsgstr \"对传送到不可用资源的数据包启用静默忽略\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:43\nmsgid \"\"\n\"You can now have Tigase ignore packets delivered to unavailable resources\"\n\" to avoid having a packet bounce around and create unnecessary traffic. \"\n\"You may set this globally, within standard message handling only, or \"\n\"within the AMP component using the following settings:\"\nmsgstr \"\"\n\"您现在可以让 Tigase 忽略传送到不可用资源的数据包，以避免数据包反弹并产生不必要的流量。您可以在全局范围内，仅在标准消息处理内或在 AMP \"\n\"组件内使用以下设置进行设置：\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:45\nmsgid \"Globally:\"\nmsgstr \"在全局范围内：\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:53\nmsgid \"Message Processing Only:\"\nmsgstr \"仅消息处理：\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:63\nmsgid \"AMP Component:\"\nmsgstr \"AMP 组件：\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:73\nmsgid \"Mechanism to count errors within Tigase\"\nmsgstr \"Tigase 中的错误计数机制\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:75\nmsgid \"\"\n\"A new processor within statistics has been added to count the number of \"\n\"errors that Tigase returns. This processor, named ``error-counter``, will\"\n\" count all errors returned by Tigase, however by default the number is \"\n\"always zero if it is not enabled. It can be found as an MBean object in \"\n\"JMX under ``ErrorStatistics`` and contains values for packets with \"\n\"``ERROR`` and grouped by type. To enable counting of these errors, you \"\n\"must ensure the processor is included in your ``sess-man`` configuration:\"\nmsgstr \"\"\n\"在统计信息中添加了一个新处理器来计算 Tigase 返回的错误数。这个名为 ``error-counter`` 的处理器将对 Tigase \"\n\"返回的所有错误进行计数，但默认情况下，如果未被启用，该数字始终为零。其可以在 JMX 中的 ``ErrorStatistics`` 下作为 \"\n\"MBean 对象找到，并包含带有 ``ERROR`` 且按类型分组的数据包的值。要启用这些对错误数的计数，您必须确保处理器包含在您的 \"\n\"``sess-man`` 配置中：\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:84\nmsgid \"Including stream errors\"\nmsgstr \"包括流错误\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:86\nmsgid \"\"\n\"Stream ``ERROR`` packets are not included in the above counter by default\"\n\" as they are processed separately. To enable this to be added to the \"\n\"counter, the following line must be in your ``config.tdsl`` file.\"\nmsgstr \"\"\n\"默认情况下，流 ``ERROR`` 数据包不包含在上述计数器中，因为它们是单独处理的。要将其添加到计数器中，以下行必须在您的 \"\n\"``config.tdsl`` 文件中。\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:97\nmsgid \"Stream resumption default & max-timeout\"\nmsgstr \"流恢复默认值和最大超时\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:99\nmsgid \"\"\n\"``SteamManagementIOProcessor`` now has a setting that can be used to \"\n\"change the maximum timeout time it will wait for reconnection if a client\"\n\" does not send a time to wait. Two settings are now available:\"\nmsgstr \"\"\n\"``SteamManagementIOProcessor`` \"\n\"现在有一个设置，如果客户端未发送等待时间，其可用于更改等待重新连接的最大超时时间。现在有两种设置可用：\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:109\nmsgid \"\"\n\"The above setting in ``config.tdsl`` file will change the default timeout\"\n\" period to 90 seconds.\"\nmsgstr \"``config.tdsl`` 文件中的上述设置会将默认超时时间更改为 90 秒。\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:119\nmsgid \"\"\n\"This setting will set the maximum time allowed for stream resumption to \"\n\"900 seconds. This can be handy if you expect a number of mobile phones to\"\n\" connect to your server and want to avoid duplicate messages being sent \"\n\"back and forth.\"\nmsgstr \"此设置会将流恢复允许的最长时间设置为 900 秒。如果您希望有许多手机连接到您的服务器并希望避免来回发送重复的消息，这可能会很方便。\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:122\nmsgid \"Automatic subscription approval\"\nmsgstr \"自动订阅批准\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:124\nmsgid \"\"\n\"You may setup a server to automatically approve presence subscriptions or\"\n\" roster authorizations for all users. Say you were hosting bots and \"\n\"wanted to automate the process. This can be done with the following \"\n\"settings:\"\nmsgstr \"您可以设置一个服务器来自动批准所有用户的状态订阅或名册授权。比如您正在托管机器人并希望自动化该过程。这可以通过以下设置来完成：\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:137\nmsgid \"\"\n\"Both of these settings are false by default, and you may use them \"\n\"together or separately.\"\nmsgstr \"这两个设置默认为 false，您可以一起使用，也可以单独使用。\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:143\nmsgid \"The following behavior is followed when they are both activated:\"\nmsgstr \"当它们都被激活时，将遵循以下行为：\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:145\nmsgid \"\"\n\"Upon sending a subscription request - Both contacts will each others' \"\n\"subscription and be added to each others' roster. Presence information \"\n\"will immediately be exchanged between both parties.\"\nmsgstr \"发送订阅请求后 - 双方联系人将彼此订阅并添加到彼此的名册中。存在信息将立即在双方之间交换。\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:147\nmsgid \"\"\n\"Upon sending presence with type either unsubscribe or unsubscribed \"\n\"follows the rules defined in RFC regarding processing of these stanzas \"\n\"(i.e. adjusting subscription type of user/contact), but without \"\n\"forwarding those stanzas to the receiving entity to avoid any \"\n\"notifications to the client. However, a roster push is generated to \"\n\"reflect changes to presence in user roster in a seamless manner.\"\nmsgstr \"\"\n\"在发送带有 unsubscribe 或 unsubscribed 类型的出席信息时，其遵循 RFC \"\n\"中定义的关于处理这些节的规则（即调整用户/联系人的订阅类型），但不将这些节转发给接收实体以避免向客户端发送任何通知。但是，这会生成名册推送以无缝方式反映用户名册中存在的变化。\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:149\nmsgid \"\"\n\"Simply adding an item to the roster (i.e. with <iq/> stanza with correct \"\n\"semantics) will also cause an automatic subscription between the user and\"\n\" the contact in a matter explained above.\"\nmsgstr \"简单地将一个项目添加到名册（即使用具有正确语义的 <iq/> 节）也将导致用户和联系人之间的自动订阅，正如上所述。\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:152\nmsgid \"Abuse Contacts\"\nmsgstr \"滥用联系人\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:154\nmsgid \"\"\n\"Tigase has support for `XEP-0128: Service Discovery Extensions \"\n\"<https://xmpp.org/extensions/xep-0128.html>`__ for providing additional \"\n\"information to the server and component discovery information. One of the\"\n\" important usages for this feature is `XEP-0157: Contact Addresses for \"\n\"XMPP Services <https://xmpp.org/extensions/xep-0157.html>`__ which \"\n\"describes usage of this feature for providing contact information to \"\n\"server administrators or abuse response team.\"\nmsgstr \"\"\n\"Tigase 支持 `XEP-0128: Service Discovery Extensions \"\n\"<https://xmpp.org/extensions/xep-0128.html>`__ \"\n\"为服务器和组件发现信息提供附加信息。此功能的重要用途之一是 `XEP-0157: Contact Addresses for XMPP \"\n\"Services <https://xmpp.org/extensions/xep-0157.html>`__ \"\n\"，其描述了使用此功能向服务器提供联系信息管理员或滥用响应小组。\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:156\nmsgid \"\"\n\"To set abuse contact details you should set ``disco-extensions`` in \"\n\"property in ``etc/config.tdsl`` file with subproperty ``abuse-addresses``\"\n\" set to your abuse address URI (for email you need to prefix it with \"\n\"``mailto:`` and for XMPP address you need to prefix it with ``xmpp``):\"\nmsgstr \"\"\n\"要设置滥用联系人详细信息，您应该在 ``etc/config.tdsl`` 文件的属性中设置 ``disco-extensions``，并将子属性\"\n\" ``abuse-addresses`` 设置为您的滥用地址 URI（对于电子邮件，您需要添加前缀使用 ``mailto:`` 并且对于 XMPP\"\n\" 地址，您需要使用 ``xmpp`` 作为前缀）：\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:165\nmsgid \"Push Notifications\"\nmsgstr \"推送通知\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:167\nmsgid \"\"\n\"Tigase XMPP Server comes with support for `XEP-0357: Push Notifications \"\n\"<https://xmpp.org/extensions/xep-0357.html>`__ allowing user to receive \"\n\"notifications for messages received while his XMPP client is not \"\n\"connected enabled by default.\"\nmsgstr \"\"\n\"Tigase XMPP 服务器支持 `XEP-0357: Push Notifications \"\n\"<https://xmpp.org/extensions/xep-0357.html>`__ 并默认允许用户在其 XMPP \"\n\"客户端未连接时接收消息通知。\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:170\nmsgid \"Disabling notifications\"\nmsgstr \"禁用通知\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:172\nmsgid \"You can disable this feature with following settings:\"\nmsgstr \"您可以使用以下设置禁用此功能：\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:180\nmsgid \"Removing body and sender from notifications\"\nmsgstr \"从通知中删除正文和发件人\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:182\nmsgid \"\"\n\"If you wish Tigase XMPP Server not to forward body of the message or \"\n\"sender details in the push notification you can disable that with \"\n\"following settings:\"\nmsgstr \"如果您希望 Tigase XMPP 服务器不在推送通知中转发消息正文或发件人详细信息，您可以使用以下设置禁用它：\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:193\nmsgid \"Overriding body of notifications\"\nmsgstr \"覆盖通知的正文\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:195\nmsgid \"\"\n\"If you wish Tigase XMPP Server to override forward body of the encrypted \"\n\"message in the push notification (for example to avoid indicating that \"\n\"there is an \\\"error\\\") you can do that with following settings:\"\nmsgstr \"如果您希望 Tigase XMPP 服务器在推送通知中覆盖加密消息的转发正文（例如，为了避免指示存在\\\"error\\\"），您可以使用以下设置来实现：\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:206\nmsgid \"\"\n\"Enabling push notifications for messages received when all resources are \"\n\"AWAY/XA/DND\"\nmsgstr \"当所有资源都是 AWAY/XA/DND 时为收到的消息启用推送通知\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:208\nmsgid \"\"\n\"Push notifications may also be sent by Tigase XMPP Server when new \"\n\"message is received and all resources of recipient are in AWAY/XA/DND \"\n\"state. To enable this type of notifications you need to enable additional\"\n\" push delivery extension named ``away`` in default push processor:\"\nmsgstr \"\"\n\"当收到新消息并且接收者的所有资源都处于 AWAY/XA/DND 状态时，Tigase XMPP Server \"\n\"也可以发送推送通知。要启用这种类型的通知，您需要在默认推送处理器中启用名为 ``away`` 的附加推送扩展：\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:218\nmsgid \"\"\n\"As this behaviour may not be expected by users and users need a \"\n\"compatible XMPP client to properly handle this notifications (XMPP client\"\n\" needs to retrieve message history from server to get actual message), in\"\n\" addition to enabling this plugin on the server, XMPP clients need to \"\n\"explicitly activate this feature. They can do that by including ``away`` \"\n\"attribute with value of ``true`` in push ``enable`` element send to the \"\n\"server, as in following example:\"\nmsgstr \"\"\n\"由于用户可能不期望这种行为，并且用户需要兼容的 XMPP 客户端来正确处理此通知（XMPP \"\n\"客户端需要从服务器检索消息历史记录以获取实际消息），除了在服务器上启用此插件外，XMPP 客户端还需要显式激活此功能。他们可以通过在推送 \"\n\"``enable`` 元素中包含值为 ``true`` 的 ``away`` 属性发送到服务器，如下例所示：\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:220\nmsgid \"**Enabling Push notifications for away/xa/dnd account.**\"\nmsgstr \"**为 away/xa/dnd 帐户启用推送通知。**\"\n\n#: ../../Tigase_Administration/Configuration/Advanced_Options.inc:232\nmsgid \"\"\n\"If later on, user decides to disable notification for account in \"\n\"away/xa/dnd state, it may disable push notifications or once again send \"\n\"stanza to enable push notification but without ``away`` attribute being \"\n\"set:\"\nmsgstr \"\"\n\"如果稍后，用户决定为 away/xa/dnd 状态的帐户禁用通知，它可能会禁用推送通知或再次发送节以启用推送通知，但没有设置 ``away`` \"\n\"属性：\"\n\n#~ msgid \"\"\n#~ \"`config.tdsl <#dslConfig>`__ file is a \"\n#~ \"simple text file with server parameters\"\n#~ \" in form: **key** = **value**. When\"\n#~ \" the XML configuration file is \"\n#~ \"missing the Tigase server reads \"\n#~ \"``config.tdsl`` file and uses parameters \"\n#~ \"found there as defaults for generation\"\n#~ \" of the XML file. Therefore if \"\n#~ \"you change the ``config.tdsl`` file you\"\n#~ \" normally have to stop the server,\"\n#~ \" remove the XML file and start \"\n#~ \"the server again. All the settings \"\n#~ \"from the ``config.tdsl`` are read and\"\n#~ \" applied to the XML configuration. \"\n#~ \"The properties file is easy to \"\n#~ \"read and very safe to modify. At\"\n#~ \" the moment this is the recommended\"\n#~ \" way change the server configuration.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`tigase.conf <#manualconfig>`__ is the Tigase\"\n#~ \" server startup configuration. It is \"\n#~ \"actually not used by the server \"\n#~ \"itself. It rather contains operating \"\n#~ \"system settings and environment parameters \"\n#~ \"to correctly run the `Java Virtual \"\n#~ \"Machine <http://java.sun.com/>`__. It is only\"\n#~ \" useful on the unix-like systems \"\n#~ \"with Bash shell. If you run the\"\n#~ \" server on MS Windows systems \"\n#~ \"``tigase.bat`` and ``wrapper.conf`` files are\"\n#~ \" used instead. The ``tigase.conf`` file \"\n#~ \"is read and loaded by the \"\n#~ \"``scripts/tigase.sh`` shell script which also\"\n#~ \" scans the operating system environment \"\n#~ \"for Java VM and other tools \"\n#~ \"needed.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"After initial definition of default \"\n#~ \"configuration or after first startup of\"\n#~ \" Tigase XMPP Server it is possible\"\n#~ \" to configure Virtual Host defaults \"\n#~ \"using ad-hoc commands by modifying \"\n#~ \"values for ``default`` using ad-hoc \"\n#~ \"as described in `Specification for \"\n#~ \"ad-hoc Commands Used to Manage \"\n#~ \"Virtual Domains <#adhocCommands>`__.\"\n#~ msgstr \"\"\n#~ \"在初始定义默认配置或首次启动 Tigase XMPP 服务器后，可以使用 ad-\"\n#~ \"hoc 命令配置虚拟主机默认值，方法是使用 ad-hoc 修改 \"\n#~ \"``default`` 的值，就如 `用于管理虚拟域的临时命令规范 \"\n#~ \"<#adhocCommands>`__ 中所描述。\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Administration/Database_Management/Management.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version:  TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2023-02-16 15:01-0800\\n\"\n\"PO-Revision-Date: 2022-09-07 17:14+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language: zh_CN\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects\"\n\"/tigase-xmpp-server/database_management/zh_Hans/>\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.11.0\\n\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:2\nmsgid \"Database Management\"\nmsgstr \"数据库管理\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:4\nmsgid \"\"\n\"Tigase is coded to perform with multiple database types and numbers. \"\n\"Owing to it’s versatile nature, there are some tools and procedures that \"\n\"may be of use to certain administrators.\"\nmsgstr \"Tigase 被编码以使用多种数据库类型和数字来执行。由于它的多功能性，有一些工具和程序可能对某些管理员有用。\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:7\nmsgid \"Recommended database versions\"\nmsgstr \"推荐的数据库版本\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:9\nmsgid \"\"\n\"As of v8.0.0 here are the minimum and recommended versions of databases \"\n\"for use with Tigase:\"\nmsgstr \"从 v8.0.0 开始，以下是用于 Tigase 的最低和推荐的数据库版本：\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:12\nmsgid \"Database\"\nmsgstr \"数据库\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:12\nmsgid \"Recommended Version\"\nmsgstr \"推荐版本\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:12\nmsgid \"Minimum Version\"\nmsgstr \"最低版本\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:12\nmsgid \"Additional Information\"\nmsgstr \"补充信息\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:14\nmsgid \"DerbyDB\"\nmsgstr \"DerbyDB\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:14\nmsgid \"10.12.1.1\"\nmsgstr \"10.12.1.1\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:14\nmsgid \"Included with Tigase XMPP Server\"\nmsgstr \"包含在 Tigase XMPP 服务器中\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:16\nmsgid \"MySQL\"\nmsgstr \"MySQL\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:16\nmsgid \"5.7\"\nmsgstr \"5.7\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:16\nmsgid \"Required to properly support timestamp storage with millisecond precision\"\nmsgstr \"需要以毫秒精度来正确支持时间戳存储\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:18\nmsgid \"SQLServer\"\nmsgstr \"SQLServer\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:18\nmsgid \"2014\"\nmsgstr \"2014\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:18\nmsgid \"2012\"\nmsgstr \"2012\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:18\nmsgid \"2012 needed so we can count use fetch-offset pagination feature.\"\nmsgstr \"需要 2012 ，这样我们可以计算使用 fetch-offset 分页功能。\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:20\nmsgid \"PostgreSQL\"\nmsgstr \"PostgreSQL\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:20\nmsgid \"13.0\"\nmsgstr \"13.0\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:20\nmsgid \"9.4\"\nmsgstr \"9.4\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:20\nmsgid \"\"\n\"New UA schema requires at least 9.4; if using version older than 13 \"\n\"manual installation of ``uuid-ossp`` extension is required (1)\"\nmsgstr \"新的 UA 架构至少需要 9.4；如果使用早于 13 的版本，则需要手动安装 ``uuid-ossp`` 扩展 (1)\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:22\nmsgid \"MongoDB\"\nmsgstr \"MongoDB\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:22\nmsgid \"3.2\"\nmsgstr \"3.2\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:22\nmsgid \"3.0\"\nmsgstr \"3.0\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:24\nmsgid \"MariaDB\"\nmsgstr \"MariaDB\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:24\nmsgid \"?\"\nmsgstr \"?\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:24\nmsgid \"10.0.12\"\nmsgstr \"10.0.12\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:24\nmsgid \"\"\n\"Basic features works with 10.0.12-MariaDB Homebrew, but is not fully \"\n\"tested.\"\nmsgstr \"基本功能适用于 10.0.12-MariaDB Homebrew，但尚未经过全面测试。\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:29\nmsgid \"\"\n\"For PostgreSQL version older than 13.0 manual installation of ``uuid-\"\n\"ossp`` by the superuser to the *created database* is required:\"\nmsgstr \"对于早于 13.0 的 PostgreSQL 版本，需要由超级用户手动安装 ``uuid-ossp`` 到 *创建的数据库*：\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:35\nmsgid \"\"\n\"Although Tigase may support other versions of databases, these are the \"\n\"ones we are most familiar with in offering support and advice. Use of \"\n\"databases outside these guidelines may result in unforeseen errors.\"\nmsgstr \"尽管 Tigase 可能支持其他版本的数据库，但这些是我们在提供支持和建议方面最熟悉的版本。在这些准则之外使用数据库可能会导致无法预料的错误。\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:38\nmsgid \"Database Watchdog\"\nmsgstr \"数据库监管\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:40\nmsgid \"\"\n\"It is possible to have Tigase test availability and existence of database\"\n\" periodically only when db connections are idle. By default this ping is \"\n\"sent once every 60 minutes to each connected repository. However this can\"\n\" be overridden as a part of the dataSource property:\"\nmsgstr \"\"\n\"只有当数据库连接空闲时，才可以定期测试 Tigase 的可用性和数据库的存在性。默认情况下，此 ping 每 60 \"\n\"分钟发送一次到每个连接的存储库。但是，这可以作为 dataSource 属性的一部分被覆盖：\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:54\nmsgid \"This setting changes frequency to 30 minutes.\"\nmsgstr \"此设置将频率更改为 30 分钟。\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:65\nmsgid \"This one changes to 15 minutes.\"\nmsgstr \"这个更改为 15 分钟。\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:69\nmsgid \"\"\n\"see :ref:`Period / Duration values<PeriodDurationvalues>` for format \"\n\"details\"\nmsgstr \"有关格式详细信息，请参阅 :ref:`Period / Duration values<PeriodDurationvalues>`\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:72\nmsgid \"Using modified database schema\"\nmsgstr \"使用修改后的数据库模式\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:74\nmsgid \"\"\n\"If you are using Tigase XMPP Server with modified schema (changed \"\n\"procedures or tables) and you do not want Tigase XMPP Server to maintain \"\n\"it and automatically upgrade, you can disable ``schema-management`` for \"\n\"any data source. If ``schema-management`` is disable for particular data \"\n\"source then Tigase XMPP Server will not update or modify database schema \"\n\"in any way. Moreover it will not check if schema version is correct or \"\n\"not.\"\nmsgstr \"\"\n\"如果您正在使用带有修改架构（更改的过程或表）的 Tigase XMPP Server，并且您不希望 Tigase XMPP Server \"\n\"维护它并自动升级，您可以对任何数据源禁用 ``schema-management``。如果对特定数据源禁用了 ``schema-\"\n\"management``，那么 Tigase XMPP 服务器将不会以任何方式更新或修改数据库模式。而且它将不会检查模式版本是否正确。\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:76\nmsgid \"**Disabling ``schema-management`` for ``default`` data source**\"\nmsgstr \"*为* ``default`` *数据源禁用* ``schema-management``\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:89\nmsgid \"\"\n\"If ``schema-management`` is disabled, then it is administrator \"\n\"responsibility to maintain database schema and update it if needed (ie. \"\n\"if Tigase XMPP Server schema was changed).\"\nmsgstr \"\"\n\"如果 ``schema-management`` 被禁用，那么管理员有责任维护数据库架构并在需要时对其进行更新（也就是：如果 Tigase \"\n\"XMPP 服务器架构已更改）。\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:94\nmsgid \"Schema files maintenance\"\nmsgstr \"架构文件维护\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:96\nmsgid \"\"\n\"This document describes schema files layout and assumptions about it. In \"\n\"addition it describes how and when it should be updated.\"\nmsgstr \"本文档描述了模式文件布局和有关它的假设。此外，它还描述了更新的方式和时间。\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:99\nmsgid \"Assumptions\"\nmsgstr \"假设\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:101\nmsgid \"Following assumptions are in place:\"\nmsgstr \"存在以下假设：\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:103\nmsgid \"\"\n\"All schema files are *loadable* multiple times - this is by far most \"\n\"important assumptions and it’s allow to get away without explicit and \"\n\"detailed checking of loaded version (it’s already handled on the schema \"\n\"level as of version 8.0.0)\"\nmsgstr \"\"\n\"所有模式文件都 *可加载* 多次 - 这是迄今为止最重要的假设，它允许在不明确和详细检查加载版本的情况下逃脱（它已经在版本 8.0.0 \"\n\"的模式级别上处理）\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:105\nmsgid \"\"\n\"Required schema version is calculated from the component version (which \"\n\"is set in the project configuration file - usually ``pom.xml``, but it’s \"\n\"possible to override it in code via annotations - please see Developer \"\n\"Guild in Server documentation for details)\"\nmsgstr \"\"\n\"所需的模式版本是根据组件版本计算的（其在项目配置文件中设置 - 通常是 ``pom.xml``，但可以通过注释在代码中覆盖它 - \"\n\"请参阅服务器文档中的开发者手册了解详细信息）\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:107\nmsgid \"\"\n\"we will maintain *\\\"3 versions schema files\\\"*, i.e. in the distribution \"\n\"package we will provide schema versions for the ``current_version`` and \"\n\"two major versions behind (and all maintenance version schema files) - \"\n\"this will allow *quick upgrade* even from rather older versions\"\nmsgstr \"\"\n\"我们将维护 *\\\"3 versions schema files\\\"*，即在分发包中，我们将为 ``current_version`` \"\n\"和后面两个主要版本（以及所有维护版本模式文件）提供模式版本 - 这将允许 *快速升级* 即使是从更旧的版本\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:109\nmsgid \"\"\n\"``SNAPSHOT`` versions will print a log entry indicating that there may \"\n\"have been changes in schema and it’s recommended to run the upgrade (we \"\n\"are aiming at frequent releases thus mandatory schema version check will \"\n\"be done only with final version)\"\nmsgstr \"\"\n\"``SNAPSHOT`` \"\n\"版本将打印一个日志条目，其表面架构可能已经更改，并建议运行升级（我们的目标是频繁发布，因此强制架构版本检查将仅对最终版本进行）\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:112\nmsgid \"Checks\"\nmsgstr \"检查\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:114\nmsgid \"We will check:\"\nmsgstr \"我们将检查：\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:116\nmsgid \"\"\n\"if it’s possible to upgrade the schema (based on the current schema \"\n\"version in the database and available SQL files and their respective \"\n\"versions - if );\"\nmsgstr \"是否可以升级模式（基于数据库中的当前模式版本和可用的 SQL 文件及其各自的版本 - 是否 ）；\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:118\nmsgid \"\"\n\"if it’s required to upgrade the schema during server startup (until 7.1.x\"\n\" [inclusive] it was done only for tigase-server, will be done by all \"\n\"components)\"\nmsgstr \"是否需要在服务器启动期间升级架构（直到 7.1.x [包括] 它仅用于 tigase-server，将由所有组件完成）\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:120\nmsgid \"\"\n\"if it’s required to upgrade the schema during run of ``upgrade-schema``) \"\n\"(if schema is already in the latest required version, executing all SQL \"\n\"files is not required hence speeding up upgrade)\"\nmsgstr \"\"\n\"是否在 ``upgrade-schema`` 运行期间需要升级架构（如果架构已经是所需的最新版本，则不需要执行所有 SQL \"\n\"文件，因此可以加快升级速度）\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:122\nmsgid \"\"\n\"During startup of ``SNAPSHOT`` version, even if the schema version match,\"\n\" a prompt to re-run ``upgrade-schema`` will be printed in the ``logs\"\n\"/tigase-console.log``\"\nmsgstr \"\"\n\"在 ``SNAPSHOT`` 版本启动期间，即使架构版本匹配，也会在 ``logs/tigase-console.log`` 中打印重新运行 \"\n\"``upgrade-schema`` 的提示\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:125\nmsgid \"Schema files layout\"\nmsgstr \"架构文件布局\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:128\nmsgid \"Filename layout\"\nmsgstr \"文件名布局\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:130\nmsgid \"Basic schema filename layout consists of 3 basic parts:\"\nmsgstr \"基本模式文件名布局由 3 个基本部分组成：\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:132\nmsgid \"\"\n\"name of relational database management system (RDBMS) for which it’s \"\n\"intended (e.g. ``derby``, ``mysql``, ``postgresql``, ``sqlserver``);\"\nmsgstr \"\"\n\"它所针对的关系数据库管理系统 (RDBMS) \"\n\"的名称（例如，``derby``，``mysql``，``postgresql``，``sqlserver``）；\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:134\nmsgid \"name of the Tigase component for which it’s intended;\"\nmsgstr \"预期的 Tigase 组件的名称；\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:136\nmsgid \"version of the schema file.\"\nmsgstr \"架构文件的版本。\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:138\nmsgid \"\"\n\"For each component and version it’s possible (but not mandatory) to split\"\n\" all database related functionality into multiple files but it’s \"\n\"essential that they would be linked/included in the base file for \"\n\"particular database/component/version file. This allows separating Stored\"\n\" Procedures (``-sp``), base schema (``-schema``) and setting properties \"\n\"(``-props``). In principle the filename pattern looks as follows\"\nmsgstr \"对于每个组件和版本，可以（但不是强制）将所有与数据库相关的功能拆分为多个文件，但必须将它们链接/包含在特定数据库/组件/版本文件的基础文件中。这允许分离存储过程（``-sp``）、基本模式（``-schema``）和设置属性（``-props``）。原则上，文件名模式如下所示\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:144\nmsgid \"\"\n\"For example schema file for version 7.0.0 of Tigase Server for Derby \"\n\"looks as follow:\"\nmsgstr \"例如，Tigase Server for Derby 版本 7.0.0 的模式文件如下所示：\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:152\nmsgid \"Files structure\"\nmsgstr \"文件结构\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:154\nmsgid \"\"\n\"As mentioned before, we should support all versions matching ``old-\"\n\"stable``, ``stable`` and ``master``, which translates to two main \"\n\"versions behind *current-version*, that is version: *current-version - \"\n\"2*). This results in having 3 versions of the schema in the repository at\"\n\" any given time (two of them being \\\\``upgrades'' to the oldest, base \"\n\"schema):\"\nmsgstr \"\"\n\"如前所述，我们应该支持所有匹配 ``old-stable``, ``stable`` 和 ``master`` 的版本，这意味着 \"\n\"*current-version* 后面的两个主要版本，即版本： *current-version - 2*)。这导致在任何给定时间在存储库中都有\"\n\" 3 个版本的模式（其中两个是 upgrades`` 到最旧的基本模式）：\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:156\nmsgid \"``current-version`` *minus* 2: base schema\"\nmsgstr \"``current-version`` *减* 2：基本架构\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:158\nmsgid \"\"\n\"``current-version`` *minus* 1: all changes from ``current-version`` \"\n\"*minus* 2 to ``current-version`` *minus* 1\"\nmsgstr \"\"\n\"``current-version`` *减* 1: 从 ``current-version`` *减* 2 到 ``current-\"\n\"version`` *减* 1 的所有更改\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:160\nmsgid \"\"\n\"``current-version``: all changes from ``current-version`` *minus* 1 to \"\n\"``current-version``\"\nmsgstr \"\"\n\"``current-version``: 所有从 ``current-version`` *减* 1 到 ``current-version`` \"\n\"的改变\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:164\nmsgid \"\"\n\"``current-version`` *MUST* always match version of the component (defined\"\n\" in pom.xml).\"\nmsgstr \"``current-version`` *必须* 总是匹配组件的版本（在 pom.xml 中定义）。\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:168\nmsgid \"\"\n\"It’s possible to have multiple files within version (related to smaller, \"\n\"maintenance upgrade) as the SchemaLoader would collect all files which \"\n\"version falls within range and .\"\nmsgstr \"版本中可能有多个文件（与较小的维护升级相关），因为 SchemaLoader 会收集版本范围内的所有文件。\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:170\nmsgid \"\"\n\"For example with the release of version 8.0.0 this will translate to \"\n\"following versions:\"\nmsgstr \"例如，随着版本 8.0.0 的发布，这将转换为以下版本：\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:172\nmsgid \"``7.0.0``: base schema\"\nmsgstr \"``7.0.0``: 基本架构\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:174\nmsgid \"``7.1.0``: all changes from ``7.0.0`` to ``7.1.0``\"\nmsgstr \"``7.1.0``：从 ``7.0.0`` 到 ``7.1.0`` 的所有变化\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:176\nmsgid \"``8.0.0``: all changes from ``7.1.0`` to ``8.0.0``\"\nmsgstr \"``8.0.0``：从 ``7.1.0`` 到 ``8.0.0`` 的所有变化\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:180\nmsgid \"All schema files must be stored under ``src/main/database/``\"\nmsgstr \"所有架构文件必须存储在 ``src/main/database/`` 下\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:183\nmsgid \"Handling of changes in the schema\"\nmsgstr \"处理架构中的更改\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:185\nmsgid \"There are two main workflows defined\"\nmsgstr \"定义了两个主要工作流\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:188\nmsgid \"During release of the version\"\nmsgstr \"在版本发布期间\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:190\nmsgid \"\"\n\"As we keep at the most only 3 versions of the schema, after release of \"\n\"the version we need to adjust (flatten) the files to maintain structure \"\n\"defined in *Files structure* (it may happen, that there wouldn’t be any \"\n\"changes in the schema for particular version which will result in \"\n\"relatively empty ``current-version`` schema file – only setting current \"\n\"version for component with ``setVersion('component','<current-version\"\n\"></current-version>');`` ).\"\nmsgstr \"\"\n\"由于我们最多只保留 3 个版本的架构，因此在版本发布后，我们需要调整（展平）文件以维护在 *Files structure* \"\n\"中定义的结构（这可能会发生，特定版本的架构不会有任何更改，这将导致相对空的 ``current-version`` 架构文件 - 仅使用 \"\n\"``setVersion('component','<current-version></current-version>');`` \"\n\"设置组件的当前版本)。\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:192\nmsgid \"\"\n\"For example we are about to release version ``8.0.0``. This results in \"\n\"the following versions of the schema (in the example for the server) in \"\n\"the repository:\"\nmsgstr \"例如，我们即将发布版本 ``8.0.0``。这会在存储库中生成以下架构版本（在服务器示例中）：\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:194\nmsgid \"``<database>-server-schema-7.0.0.sql``: base schema\"\nmsgstr \"``<database>-server-schema-7.0.0.sql``: 基本架构\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:196\nmsgid \"``<database>-server-schema-7.1.0.sql``: including changes for ``7.1.0``\"\nmsgstr \"``<database>-server-schema-7.1.0.sql``: 包括对 ``7.1.0`` 的更改\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:198\n#: ../../Tigase_Administration/Database_Management/Management.rst:208\nmsgid \"``<database>-server-schema-8.0.0.sql``: including changes for ``8.0.0``\"\nmsgstr \"``<database>-server-schema-8.0.0.sql``: 包括对 ``8.0.0`` 的更改\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:202\nmsgid \"\"\n\"It’s possible that there will be maintenance versions in the list as \"\n\"well, e.g.: ``<database>-server-schema-7.1.1.sql`` and ``<database\"\n\">-server-schema-7.1.2.sql``\"\nmsgstr \"\"\n\"列表中也可能会有维护版本，例如：``<database>-server-schema-7.1.1.sql`` 和 ``<database\"\n\">-server-schema-7.1.2.sql``\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:204\nmsgid \"\"\n\"After the release we specify the version of the next release in pom.xml \"\n\"(for example ``8.1.0`` and the same version will be the ``current-\"\n\"version`` making the oldest available version ``7.1.0``. Because of that \"\n\"we *MUST* incorporate all the changes in ``7.1.0`` onto ``7.0.0`` \"\n\"creating new base file with version ``7.1.0``, i.e.:\"\nmsgstr \"\"\n\"发布后，我们在 pom.xml 中指定下一个发布的版本（例如和 ``8.1.0`` 相同的版本将是 ``current-version`` \"\n\"这使得最旧的可用版本是 ``7.1.0``。因此，我们 *必须* 将 ``7.1.0`` 中的所有更改合并到 ``7.0.0`` 中，创建版本为 \"\n\"``7.1.0`` 的新基础文件，即：\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:206\nmsgid \"``<database>-server-schema-7.1.0.sql``: base schema\"\nmsgstr \"``<database>-server-schema-7.1.0.sql``: 基本架构\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:210\nmsgid \"``<database>-server-schema-8.1.0.sql``: including changes for ``8.1.0``\"\nmsgstr \"``<database>-server-schema-8.1.0.sql``: 包括对 ``8.1.0`` 的更改\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:213\nmsgid \"Maintenance releases\"\nmsgstr \"维护发布\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:215\nmsgid \"\"\n\"Following cases will be discussed with solid-version examples. Comments \"\n\"will be provided in-line Following assumptions are made:\"\nmsgstr \"以下案例将使用实体版本示例进行讨论。评论将在线提供并做以下假设：\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:217\nmsgid \"Version succession: ``5.1.0``, ``5.2.0``, ``7.0.0``, ``7.1.0``, ``8.0.0``\"\nmsgstr \"版本继承: ``5.1.0``, ``5.2.0``, ``7.0.0``, ``7.1.0``, ``8.0.0``\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:219\nmsgid \"\"\n\"Versions mapping: ``master`` (``8.0.0``), ``stable`` (``7.1.0``), ``old-\"\n\"stable`` (``7.0.0``):\"\nmsgstr \"\"\n\"版本映射：``master`` (``8.0.0``), ``stable`` (``7.1.0``), ``old-stable`` \"\n\"(``7.0.0``):\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:221\n#: ../../Tigase_Administration/Database_Management/Management.rst:257\n#: ../../Tigase_Administration/Database_Management/Management.rst:293\nmsgid \"schema files in ``old-stable`` branch\"\nmsgstr \"``old-stable`` 分支中的架构文件\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:223\n#: ../../Tigase_Administration/Database_Management/Management.rst:259\n#: ../../Tigase_Administration/Database_Management/Management.rst:295\nmsgid \"5.1.0 (base)\"\nmsgstr \"5.1.0 (基础版)\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:225\n#: ../../Tigase_Administration/Database_Management/Management.rst:261\n#: ../../Tigase_Administration/Database_Management/Management.rst:297\nmsgid \"5.2.0 (upgrade)\"\nmsgstr \"5.2.0 (升级版)\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:227\n#: ../../Tigase_Administration/Database_Management/Management.rst:233\n#: ../../Tigase_Administration/Database_Management/Management.rst:263\n#: ../../Tigase_Administration/Database_Management/Management.rst:271\n#: ../../Tigase_Administration/Database_Management/Management.rst:299\n#: ../../Tigase_Administration/Database_Management/Management.rst:305\nmsgid \"7.0.0 (upgrade)\"\nmsgstr \"7.0.0 (升级版)\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:229\n#: ../../Tigase_Administration/Database_Management/Management.rst:267\n#: ../../Tigase_Administration/Database_Management/Management.rst:301\nmsgid \"schema files in ``stable`` branch\"\nmsgstr \"``stable`` 分支中的架构文件\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:231\n#: ../../Tigase_Administration/Database_Management/Management.rst:269\n#: ../../Tigase_Administration/Database_Management/Management.rst:303\nmsgid \"5.2.0 (base)\"\nmsgstr \"5.2.0 (基础版)\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:235\n#: ../../Tigase_Administration/Database_Management/Management.rst:241\n#: ../../Tigase_Administration/Database_Management/Management.rst:275\n#: ../../Tigase_Administration/Database_Management/Management.rst:283\n#: ../../Tigase_Administration/Database_Management/Management.rst:307\n#: ../../Tigase_Administration/Database_Management/Management.rst:313\nmsgid \"7.1.0 (upgrade)\"\nmsgstr \"7.1.0 (升级版)\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:237\n#: ../../Tigase_Administration/Database_Management/Management.rst:277\n#: ../../Tigase_Administration/Database_Management/Management.rst:309\nmsgid \"schema files in ``master`` branch\"\nmsgstr \"``master`` 分支中的架构文件\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:239\n#: ../../Tigase_Administration/Database_Management/Management.rst:279\n#: ../../Tigase_Administration/Database_Management/Management.rst:311\nmsgid \"7.0.0 (base)\"\nmsgstr \"7.0.0 (基础版)\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:243\n#: ../../Tigase_Administration/Database_Management/Management.rst:285\nmsgid \"8.0.0 (upgrade)\"\nmsgstr \"8.0.0 (升级版)\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:247\nmsgid \"Making a change in ``old-stable`` (and ``stable``)\"\nmsgstr \"在 ``old-stable`` (和 ``stable``) 中进行更改\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:249\nmsgid \"\"\n\"If we made a schema change in ``old-stable`` version (and it’s branch) we\"\n\" must:\"\nmsgstr \"如果我们在 ``old-stable`` 版本（和它的分支）中进行了架构更改，我们必须：\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:251\nmsgid \"create a new file with upgraded version number;\"\nmsgstr \"创建具有升级版本号的新文件;\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:253\nmsgid \"propagate the change to the ``stable`` and ``master`` branch.\"\nmsgstr \"将更改传播到 ``stable`` 和 ``master`` 分支。\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:255\nmsgid \"Repository changes:\"\nmsgstr \"存储库更改：\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:265\nmsgid \"\"\n\"7.0.1 (upgrade) **←** making a *change* here results in the schema \"\n\"version being bumped to 7.0.1\"\nmsgstr \"7.0.1（升级) **←** 在此处进行 *更改* 会导致架构版本升级到 7.0.1\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:273\n#: ../../Tigase_Administration/Database_Management/Management.rst:281\nmsgid \"7.0.1 (upgrade) **←** we must port the *change* here\"\nmsgstr \"7.0.1（升级) **←** 我们必须在此处移植 *变化*\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:289\nmsgid \"Making a change in ``master``\"\nmsgstr \"在 ``master`` 中进行更改\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:291\nmsgid \"\"\n\"If we made a schema change in ``master`` version we don’t propagate the \"\n\"change to the ``stable`` and ``old-stable`` branch.\"\nmsgstr \"如果我们在 ``master`` 版本中进行了架构更改，我们不会将更改传播到 ``stable`` 和 ``old-stable`` 分支。\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:315\nmsgid \"\"\n\"8.0.0 (upgrade) **←** we make the *change* here, as this is the \"\n\"development version schema version remains the same.\"\nmsgstr \"8.0.0 (升级) **←** 我们在此处进行 *更改*，因为这是开发版本，架构版本保持不变。\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:319\nmsgid \"Implementation details\"\nmsgstr \"实施细节\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:322\nmsgid \"In-file control\"\nmsgstr \"文件内控制\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:324\nmsgid \"There are two main control instructions (intended for ``SchemaLoader``):\"\nmsgstr \"有两个主要的控制指令（用于 ``SchemaLoader``):\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:326\nmsgid \"\"\n\"denoting Queries with ``-- QUERY START:`` and ``-- QUERY END:`` - each \"\n\"must be placed in own, separate file with the query being enclosed by the\"\n\" two of them, for example:\"\nmsgstr \"\"\n\"用 ``-- QUERY START:`` 和 ``-- QUERY END:`` 表示查询 - \"\n\"每个都必须放在自己的单独文件中，查询由它们两个包围，例如：\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:334\nmsgid \"\"\n\"sourcing other file with ``-- LOAD FILE: <path to .sql file>`` - path \"\n\"must be on the same line, following control instruction, for example:\"\nmsgstr \"使用 ``-- LOAD FILE: <path to .sql file>`` 获取其他文件 - 路径必须在同一行，遵循控制指令，例如：\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:341\nmsgid \"Storing version in the database\"\nmsgstr \"在数据库中存储版本\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:343\nmsgid \"\"\n\"Each repository will have a table ``tig_schema_versions`` with the \"\n\"information about all installed components and it’s versions in that \"\n\"particular repository. There will be an associated stored procedure to \"\n\"obtain and set version:\"\nmsgstr \"\"\n\"每个存储库都有一个表 ``tig_schema_versions`` \"\n\"，其中包含了有关所有已安装组件的信息及其在该特定存储库中的版本。此时会有一个关联的存储过程来获取和设置版本：\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:345\nmsgid \"table:\"\nmsgstr \"表：\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:356\nmsgid \"stored procedures ``get/setVersion(‘component’,'version');``\"\nmsgstr \"存储过程 ``get/setVersion(‘component’,'version');``\"\n\n#: ../../Tigase_Administration/Database_Management/Management.rst:358\nmsgid \"\"\n\"It will be stored and maintained in the file named ``<RDBMS_name>-common-\"\n\"schema-<version>.sql``\"\nmsgstr \"它将在名为 ``<RDBMS_name>-common-schema-<version>.sql`` 的文件中存储和维护\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation.inc:2\nmsgid \"Database Preparation\"\nmsgstr \"数据库准备\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation.inc:4\nmsgid \"\"\n\"Tigase uses generally the same database schema and the same set of stored\"\n\" procedures and functions on every database. However, the schema creation\"\n\" scripts and code for stored procedures is different for each database. \"\n\"Therefore the manual process to prepare database is different for each \"\n\"database system.\"\nmsgstr \"\"\n\"Tigase \"\n\"通常在每个数据库上使用相同的数据库模式和同一组存储过程和函数。但是，每个数据库的存储过程的架构创建的脚本和代码是不同的。因此，每个数据库系统手动准备数据库的过程是不同的。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation.inc:6\nmsgid \"\"\n\"Starting with v8.0.0, most of the database tasks have been automated and \"\n\"can be called using simple text, or using interactive question and answer\"\n\" style. We **DO NOT RECOMMEND** going through manual operation, however \"\n\"we have kept manual activation of different databases to the Appendix. If\"\n\" you are interested in how we manage and update our database schemas, you\"\n\" may visit the :ref:`Schema files maintenance<Schemafilesmaintenance>` \"\n\"section of our Redmine installation for more detailed information.\"\nmsgstr \"\"\n\"从 v8.0.0 开始，大部分数据库任务已经自动化，其可以使用简单的文本调用，也可以使用交互式问答风格。我们 \"\n\"**不推荐**手动操作，但是我们将不同数据库的手动激活保留在附录中。如果您对我们如何管理和更新我们的数据库模式感兴趣，您可以访问我们 \"\n\"Redmine 安装的 :ref:`Schema 文件维护<Schemafilesmaintenance>` 部分以获取更多详细信息。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation.inc:8\nmsgid \":ref:`The DBSchemaLoader Utility<SchemaUtility>`\"\nmsgstr \":ref:`DBSchemaLoader 实用程序<SchemaUtility>`\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation.inc:10\nmsgid \":ref:`Hashed User Passwords in Database<HashedUserPasswordsinDatabase>`\"\nmsgstr \":ref:`数据库中的哈希用户密码<HashedUserPasswordsinDatabase>`\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation.inc:12\nmsgid \":ref:`Support for MongoDB<PreparingTigaseforMongoDB>`\"\nmsgstr \":ref:`支持 MongoDB<PreparingTigaseforMongoDB>`\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation.inc:14\nmsgid \"Appendix entries\"\nmsgstr \"附录条目\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation.inc:16\nmsgid \"\"\n\":ref:`Manual installtion for MySQL<Prepare-the-MySQL-Database-for-the-\"\n\"Tigase-Server>`\"\nmsgstr \":ref:`MySQL 的手动安装<Prepare-the-MySQL-Database-for-the-Tigase-Server>`\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation.inc:18\nmsgid \"\"\n\":ref:`Manual installtion for Derby<Prepare-the-Derby-Database-for-the-\"\n\"Tigase-Server>`\"\nmsgstr \":ref:`Derby 的手动安装<Prepare-the-Derby-Database-for-the-Tigase-Server>`\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation.inc:20\nmsgid \"\"\n\":ref:`Manual installtion for SQLServer<Prepare-the-MS-SQL-Server-\"\n\"Database-for-the-Tigase-Server>`\"\nmsgstr \"\"\n\":ref:`SQLServer 的手动安装<Prepare-the-MS-SQL-Server-Database-for-the-Tigase-\"\n\"Server>`\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation.inc:22\nmsgid \"\"\n\":ref:`Manual installtion for PostGRESQL<Prepare-the-PostgreSQL-Database-\"\n\"for-the-Tigase-Server>`\"\nmsgstr \"\"\n\":ref:`PostGRESQL的手动安装<Prepare-the-PostgreSQL-Database-for-the-Tigase-\"\n\"Server>`\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:4\nmsgid \"Schema Utility\"\nmsgstr \"架构实用程序\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:6\nmsgid \"\"\n\"With the release of v8.0.0 calling the Tigase dbSchemaLoader utility now \"\n\"can be done using tasks instead of calling the specific method. Support \"\n\"for Derby, MySQL, PostgreSQL, MSSQL, and MongoDB is available.\"\nmsgstr \"\"\n\"随着 v8.0.0 的发布，现在可以使用任务而不是调用特定方法来调用 Tigase dbSchemaLoader 实用程序。支持 \"\n\"Derby，MySQL，PostgreSQL，MSSQL 和 MongoDB。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:8\nmsgid \"\"\n\"In order to use this utility with any of the databases, you will need to \"\n\"first have the database environment up and running, and have established \"\n\"user credentials. You may use root or an account with administrator write\"\n\" privileges.\"\nmsgstr \"要将此实用程序与任何数据库一起使用，您首先需要启动并运行数据库环境，并建立用户凭据。您可以使用 root 或具有管理员写入权限的帐户。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:11\nmsgid \"Operation & Variables\"\nmsgstr \"操作和变量\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:13\nmsgid \"Operation\"\nmsgstr \"操作\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:15\nmsgid \"\"\n\"Operating the schema utility is quite easy! To use it run this command \"\n\"from the installation directory:\"\nmsgstr \"操作架构实用程序非常简单！要使用它，请从安装目录运行此命令：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:21\nmsgid \"\"\n\"Operations are now converted to tasks, of which there are now three: \"\n\"``install-schema``, ``upgrade-schema``, and ``destroy-schema``.\"\nmsgstr \"\"\n\"操作现在转换为任务，现在有其中三个：``install-schema``, ``upgrade-schema``, 和 ``destroy-\"\n\"schema``。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:23\nmsgid \"\"\n\"``upgrade-schema``: Upgrade the schema of the database specified in your \"\n\"``config.tdsl`` configuration file. (options are ignored for this option)\"\nmsgstr \"``upgrade-schema``: 升级 ``config.tdsl`` 配置文件中指定的数据库架构。（此选项在这里被忽略）\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:25\nmsgid \"``install-schema``: Install a schema to database.\"\nmsgstr \"``install-schema``：将架构安装到数据库。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:27\nmsgid \"``destroy-schema``: Destroy database and schemas. **DANGEROUS**\"\nmsgstr \"``destroy-schema``: 销毁数据库和架构。**危险**\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:29\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:85\nmsgid \"Options\"\nmsgstr \"选项\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:31\nmsgid \"\"\n\"Use the following options to customize. Options in bold are required, \"\n\"*{potential options are in brackets}*:\"\nmsgstr \"使用以下选项进行自定义。粗体选项是必需的，{*可能的选项在括号中*}：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:33\nmsgid \"``--help`` Prints the help for the task.\"\nmsgstr \"``--help`` 打印任务的帮助。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:35\nmsgid \"\"\n\"``-I`` or ``--interactive`` - enables interactive mode which will prompt \"\n\"for parameters not defined.\"\nmsgstr \"``-I`` 或 ``--interactive`` - 启用交互模式，该模式将提示输入未定义的参数。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:37\nmsgid \"\"\n\"``-T`` or ``--dbType`` - database type {derby, mongodb, mysql, \"\n\"postgresql, sqlserver}.\"\nmsgstr \"``-T`` 或 ``--dbType`` - 数据库类型 {derby，mongodb，mysql，postgresql，sqlserver}。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:39\nmsgid \"\"\n\"``-C`` or ``--components`` - Allows the specification of components for \"\n\"use when installing a schema.\"\nmsgstr \"``-C`` 或 ``--components`` - 允许指定在安装架构时使用的组件。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:42\nmsgid \"Usage\"\nmsgstr \"用法\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:44\nmsgid \"upgrade-schema\"\nmsgstr \"升级-架构\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:46\nmsgid \"\"\n\"This task will locate any schema versions above your current one, and \"\n\"will install them to the database configured in the ``config.tdsl`` file.\"\nmsgstr \"此任务将找到高于您当前版本的任何架构版本，并将它们安装到``config.tdsl`` 文件里配置的数据库中。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:50\nmsgid \"\"\n\"To use this utility, you must have Tigase XMPP server fully setup with a \"\n\"configured configuration file.\"\nmsgstr \"要使用此实用程序，您必须使用已配置的配置文件完全设置 Tigase XMPP 服务器。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:56\nmsgid \"Windows users will need to run the command using the following command:\"\nmsgstr \"Windows 用户需要使用以下命令运行该命令：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:65\nmsgid \"install-schema\"\nmsgstr \"安装架构\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:67\nmsgid \"This task will install a schema using the parameters provided.\"\nmsgstr \"此任务将使用提供的参数安装架构。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:69\nmsgid \"\"\n\"**If you are setting up a server manually, we HIGHLY recommend using this\"\n\" method**\"\nmsgstr \"**如果您手动设置服务器，我们强烈建议使用此方法**\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:75\nmsgid \"\"\n\"This command will install tigase using a Derby database on one named \"\n\"``tigasedb`` hosted on ``localhost``. The username and password editing \"\n\"the database is ``tigase_pass`` and ``root``. Note that ``-J`` explicitly\"\n\" adds the administrator, this is highly recommended with the ``-N`` \"\n\"passing the password.\"\nmsgstr \"\"\n\"此命令将使用 Derby 数据库在 ``localhost`` 上托管的名为 ``tigasedb`` 的数据库上安装 \"\n\"tigase。编辑数据库的用户名和密码是 ``tigase_pass`` 和 ``root``。请注意，``-J`` 显式添加管理员，强烈建议使用\"\n\" ``-N`` 传递密码。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:77\nmsgid \"If you are using a windows system, you need to call the program directly:\"\nmsgstr \"如果使用的是windows系统，需要直接调用程序：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:87\nmsgid \"\"\n\"Options for schema installation are as follows, required options are in \"\n\"bold\"\nmsgstr \"架构安装选项如下，必填选项以粗体显示\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:89\nmsgid \"``--help``, Outputs the help.\"\nmsgstr \"``--help``, 输出帮助。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:91\nmsgid \"\"\n\"``-I``, ``--interactive`` - enables interactive mode, which will result \"\n\"in prompting for any missing parameters.\"\nmsgstr \"``-I``, ``--interactive`` - 启用交互模式，这将导致提示任何缺少的参数。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:93\nmsgid \"\"\n\"``-C``, ``--components=`` - list of enabled components identifiers (+/-),\"\n\" possible values: [``amp``, ``bosh``, ``c2s``, ``eventbus``, ``ext-\"\n\"disco``, ``http``, ``mdns``, ``message-archive``, ``monitor``, ``muc``, \"\n\"``pubsub``, ``push``, ``s2s``, ``socks5``, ``test``, ``unified-archive``,\"\n\" ``upload``, ``ws2s``] (default: amp,bosh,c2s,eventbus,http,message-\"\n\"archive,monitor,muc,pubsub,s2s,ws2s). **This is required for certain \"\n\"components like socks5.**\"\nmsgstr \"\"\n\"``-C``, ``--components=`` - 启用的组件标识符列表（+/-），可能的值: [``amp``, ``bosh``, \"\n\"``c2s``, ``eventbus``, ``ext-disco``, ``http``, ``mdns``, ``message-\"\n\"archive``, ``monitor``, ``muc``, ``pubsub``, ``push``, ``s2s``, \"\n\"``socks5``, ``test``, ``unified-archive``, ``upload``, ``ws2s``] (默认: \"\n\"amp,bosh,c2s,eventbus,http,message-archive,monitor,muc,pubsub,s2s,ws2s) \"\n\"。**这对于某些组件（如 socks5）是必需的。**\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:95\nmsgid \"\"\n\"``-T``, ``--dbType=`` - database server type, possible values are: \"\n\"[``derby``, ``mongodb``, ``mysql``, ``postgresql``, ``sqlserver``] \"\n\"(*required*)\"\nmsgstr \"\"\n\"``-T``, ``--dbType=`` - 数据库服务器类型，可能的值为: [``derby``, ``mongodb``, \"\n\"``mysql``, ``postgresql``, ``sqlserver``] (*必需的*)\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:97\nmsgid \"\"\n\"``-D``, ``--dbName=`` - name of the database that will be created (by \"\n\"default it is ``tigasedb``). (*required*)\"\nmsgstr \"``-D``, ``--dbName=`` - 数据库的名称将被创建（默认为 tigasedb）。（*必需的*）\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:99\nmsgid \"\"\n\"``-H``, ``--dbHostname=`` - address of the database instance (by default \"\n\"it is ``localhost``). (*required*)\"\nmsgstr \"``-H``, ``--dbHostname=`` - 数据库实例的地址（默认为 ``localhost``）。（*必需的*）\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:101\nmsgid \"\"\n\"``-U``, ``--dbUser=`` - name of the user that will be created \"\n\"specifically to access Tigase XMPP Server database (default is \"\n\"``tigase_user``). (*required*)\"\nmsgstr \"\"\n\"``-U``, ``--dbUser=`` - 将专门创建用于访问 Tigase XMPP 服务器数据库的用户名（默认为 \"\n\"``tigase_user``）。（*必需的*）\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:103\nmsgid \"\"\n\"``-P``, ``--dbPass=`` - password of the user that will be created \"\n\"specifically to access Tigase XMPP Server database (default is \"\n\"``tigase_pass``). (*required*)\"\nmsgstr \"\"\n\"``-P``, ``--dbPass=`` - 将专门创建用于访问 Tigase XMPP 服务器数据库的用户密码（默认为 \"\n\"``tigase_pass``）。（*必需的*）\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:105\nmsgid \"\"\n\"``-R``, ``--rootUser=`` - database root account username used to create \"\n\"user and database (default is ``root``). (*required*)\"\nmsgstr \"\"\n\"``-R``, ``--rootUser=`` - 创建数据库 root 帐户用户名（默认为 \"\n\"``root``）其用于创建用户和数据库。（*必需的*）\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:107\nmsgid \"\"\n\"``-A``, ``--rootPass=`` - database root account password used to create \"\n\"user and database (default is ``root``). (*required*)\"\nmsgstr \"\"\n\"``-A``, ``--rootPass=`` - 创建数据库 root 帐户用户名（默认为 \"\n\"``root``）其用于创建用户和数据库。（*必需的*）\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:109\nmsgid \"\"\n\"``-S``, ``--useSSL`` - enable SSL support for database connection (if the\"\n\" database supports it) (default is false).\"\nmsgstr \"``-S``, ``--useSSL`` - 为数据库连接启用 SSL 支持（如果数据库支持)(默认为 false）。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:111\nmsgid \"\"\n\"``-F``, ``--file=`` - comma separated list of SQL files that will be \"\n\"processed.\"\nmsgstr \"``-F``, ``--file=`` - 用逗号分隔的将要被处理的 SQL 文件列表。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:113\nmsgid \"\"\n\"``-Q``, ``--query=`` - custom queries to be executed, see :ref:`Query \"\n\"function<queryschema>` for details.\"\nmsgstr \"\"\n\"``-Q``, ``--query=`` - 要执行的自定义查询，请参阅 :ref:`Query function<queryschema>` \"\n\"了解详细信息。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:115\nmsgid \"\"\n\"``-L``, ``--logLevel=`` - logger level used during loading process \"\n\"(default is ``CONFIG``).\"\nmsgstr \"``-L``, ``--logLevel=`` - 加载过程中使用的记录器级别（默认为 ``CONFIG``)。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:117\nmsgid \"``-J``, ``--adminJID=`` - comma separated list of administrator JID(s).\"\nmsgstr \"``-J``, ``--adminJID=`` - 用逗号分隔的管理员 JID(s) 的列表。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:119\nmsgid \"\"\n\"``-N``, ``--adminJIDpass=`` - password that will be used for the entered \"\n\"JID(s) - one password for all configured JIDs.\"\nmsgstr \"``-N``, ``--adminJIDpass=`` - 将用于输入JID(s) 的密码 - 一个密码可用于所有配置的JIDs。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:121\nmsgid \"``--getURI=`` - generate database URI (default is ``false``).\"\nmsgstr \"``--getURI=`` - 生成数据库 URI（默认为 ``false``）。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:123\nmsgid \"\"\n\"``--ignoreMissingFiles=`` - force ignoring missing files errors (default \"\n\"is ``false``).\"\nmsgstr \"``--ignoreMissingFiles=`` - 强制忽略丢失的文件错误（默认为 ``false``）。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:127\nmsgid \"Query function\"\nmsgstr \"查询功能\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:129\nmsgid \"\"\n\"Should you decide to customize your own functions, or have specific \"\n\"information you want to put into the database, you can use the -query \"\n\"function to perform a single query step.\"\nmsgstr \"如果您决定自定义自己的函数，或者想要将特定信息放入数据库，您可以使用 -query 函数执行单个查询步骤。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:135\nmsgid \"\"\n\"Of course this would break the schema for tigasedb by adding an \"\n\"unexpected table, you will receive the following message:\"\nmsgstr \"当然，这会添加一个意想不到的表其会破坏 tigasedb 的架构，您将收到以下消息：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:141\nmsgid \"\"\n\"But this is a demonstration how you may run a query through the database \"\n\"without the need to use another tool. Note that you will need to select \"\n\"the specific database for each query.\"\nmsgstr \"但这是一个示范，演示如何通过数据库运行查询而无需使用其他工具。请注意，您需要为每个查询选择特定的数据库。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:143\nmsgid \"destroy-schema\"\nmsgstr \"破坏架构\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:146\nmsgid \"This will destroy the database specified in the configuration file.\"\nmsgstr \"这将破坏配置文件中指定的数据库。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:150\nmsgid \"**THIS ACTION IS NOT REVERSIBLE**\"\nmsgstr \"**此操作不可逆**\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:156\nmsgid \"\"\n\"Only use this if you wish to destroy a database and not have the \"\n\"information recoverable.\"\nmsgstr \"仅当您希望破坏数据库并且没有可恢复信息时才使用此选项。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:158\nmsgid \"Windows users will need to call the method directly:\"\nmsgstr \"Windows 用户需要直接调用该方法：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:165\nmsgid \"A note about MySQL\"\nmsgstr \"关于 MySQL 的注意事项\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:167\nmsgid \"If you are using these commands, you may result in the following error:\"\nmsgstr \"如果您使用这些命令，可能会导致以下错误：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:173\nmsgid \"\"\n\"If this occurs, you will need to upgrade your version of MySQL using the \"\n\"following command:\"\nmsgstr \"如果发生这种情况，您将需要使用以下命令升级您的 MySQL 版本：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/DbSchemaLoader.inc:179\nmsgid \"\"\n\"After entering the password and upgrading MySQL the schema error should \"\n\"no longer show when working with Tigase databases.\"\nmsgstr \"输入密码并升级 MySQL 后，在使用 Tigase 数据库时不应再显示架构错误。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:4\nmsgid \"Prepare the MySQL Database for the Tigase Server\"\nmsgstr \"为 Tigase 服务器准备 MySQL 数据库\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:6\nmsgid \"\"\n\"This guide describes how to prepare MySQL database for connecting Tigase \"\n\"server.\"\nmsgstr \"本指南介绍如何准备 MySQL 数据库以连接 Tigase 服务器。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:8\nmsgid \"\"\n\"The MySQL database can be prepared in many ways. Most Linux distributions\"\n\" contain tools which allow you to go through all steps from the shell \"\n\"command line. To make sure it works on all platforms in the same way, we \"\n\"will first show how to do it under MySQL command line client.\"\nmsgstr \"\"\n\"MySQL 数据库可以通过多种方式进行准备。大多数 Linux 发行版都包含一些工具，其允许您从 shell \"\n\"命令行完成所有步骤。为了确保它以相同的方式在所有平台上工作，我们将首先展示如何在 MySQL 命令行客户端下进行操作。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:11\nmsgid \"Configuring from MySQL command line tool\"\nmsgstr \"从 MySQL 命令行工具配置\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:13\nmsgid \"\"\n\"Run the MySQL command line client in either Linux or MS Windows \"\n\"environment and enter following instructions from the Tigase installation\"\n\" directory:\"\nmsgstr \"在 Linux 或 MS Windows 环境中运行 MySQL 命令行客户端，并从 Tigase 安装目录输入以下指令：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:19\nmsgid \"Once logged in, create the database for the Tigase server:\"\nmsgstr \"登录后，为 Tigase 服务器创建数据库：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:25\nmsgid \"\"\n\"Add the ``tigase_user`` user and grant him access to the ``tigasedb`` \"\n\"database. Depending on how you plan to connect to the database (locally \"\n\"or over the network) use one of following commands or all if you are not \"\n\"sure:\"\nmsgstr \"\"\n\"添加 ``tigase_user`` 用户并授予它对 ``tigasedb`` \"\n\"数据库的访问权限。取决您计划如何连接到数据库（本地或通过网络），如果您不确定，请使用以下全部命令或命令之一：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:27\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:134\nmsgid \"Grant access to tigase_user connecting from any network address.\"\nmsgstr \"授予对从任何网络地址连接的 tigase_user 的访问权限。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:34\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:143\nmsgid \"Grant access to tigase_user connecting from localhost.\"\nmsgstr \"授予对从 localhost 连接的 tigase_user 的访问权限。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:41\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:152\nmsgid \"Grant access to tigase_user connecting from local machine only.\"\nmsgstr \"仅授予对从本地计算机连接的 tigase_user 的访问权限。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:48\nmsgid \"And now you can update user permission changes in the database:\"\nmsgstr \"现在您可以更新数据库中的用户权限变化：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:56\nmsgid \"\"\n\"It’s essential to enable `log_bin_trust_function_creators \"\n\"<https://dev.mysql.com/doc/refman/8.0/en/replication-options-binary-\"\n\"log.html#sysvar_log_bin_trust_function_creators>`__ option in MySQL \"\n\"server, for example by running:\"\nmsgstr \"\"\n\"必须在 MySQL 服务器中启用 `log_bin_trust_function_creators \"\n\"<https://dev.mysql.com/doc/refman/8.0/en/replication-options-binary-\"\n\"log.html#sysvar_log_bin_trust_function_creators>`__ 选项，例如通过运行：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:62\nmsgid \"Installing Schemas\"\nmsgstr \"安装架构\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:64\nmsgid \"\"\n\"Starting with v8.0.0 the Schemas are no longer linked, and will need to \"\n\"manually be installed in the following order.\"\nmsgstr \"从 v8.0.0 开始，架构不再链接，需要按照以下顺序手动安装。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:66\nmsgid \"Switch to the database you have created:\"\nmsgstr \"切换到您创建的数据库：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:74\nmsgid \"\"\n\"We are assuming you run the mysql client in Linux from the Tigase \"\n\"installation directory, so all file links will be relative.\"\nmsgstr \"我们假设您在 Linux 中从 Tigase 安装目录运行 mysql 客户端，因此所有文件链接都是相对的。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:76\nmsgid \"Next install the schema files:\"\nmsgstr \"接下来安装架构文件：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:82\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:171\nmsgid \"You will need to repeat this process for the following files:\"\nmsgstr \"您需要对以下文件重复此过程：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:55\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:73\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:97\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:186\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:62\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:115\nmsgid \"Other components may require installation such as:\"\nmsgstr \"其他组件可能需要安装，例如：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:107\nmsgid \"Windows instructions:\"\nmsgstr \"Windows 说明:\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:109\nmsgid \"\"\n\"On Windows you have probably to enter the full path, assuming Tigase is \"\n\"installed in C:\\\\Program Files\\\\Tigase:\"\nmsgstr \"在 Windows 上，您可能必须输入完整路径，假设 Tigase 安装在 C:\\\\Program Files\\\\Tigase 中：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:120\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:72\nmsgid \"Configuring From the Linux Shell Command Line\"\nmsgstr \"从 Linux Shell 命令行进行配置\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:122\nmsgid \"Follow steps below to prepare the MySQL database:\"\nmsgstr \"按照以下步骤准备 MySQL 数据库：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:124\nmsgid \"Create the database space for the Tigase server:\"\nmsgstr \"为 Tigase 服务器创建数据库空间：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:130\nmsgid \"\"\n\"Add the ``tigase_user`` user and grant access to the tigasedb database. \"\n\"Depending on how you plan to connect to the database (locally or over the\"\n\" network) use one of following commands or all if you are not sure:\"\nmsgstr \"\"\n\"添加 ``tigase_user`` 用户并授予它对 ``tigasedb`` \"\n\"数据库的访问权限。取决您计划如何连接到数据库（本地或通过网络），如果您不确定，请使用以下全部命令或命令之一：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:132\nmsgid \"Selective access configuration\"\nmsgstr \"选择性访问配置\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:161\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:33\nmsgid \"Schema Installation\"\nmsgstr \"架构安装\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:163\nmsgid \"Load the proper mysql schemas into the database.\"\nmsgstr \"将正确的 mysql 架构加载到数据库中。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:197\nmsgid \"Configuring MySQL for UTF-8 Support\"\nmsgstr \"为支持 UTF-8 配置 MySQL\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:199\nmsgid \"In my.conf put following lines:\"\nmsgstr \"在 my.conf 中放入以下几行：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:216\nmsgid \"Then connect to the database from the command line shell check settings:\"\nmsgstr \"然后从命令行shell检查设置连接到数据库：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:223\nmsgid \"\"\n\"If any of these shows something else then 'utf8' then you need to fix it \"\n\"using the command:\"\nmsgstr \"如果其中任何一个显示其他内容然后显示 'utf8'，那么您需要使用以下命令对其进行修复：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:229\nmsgid \"\"\n\"You can now also test your database installation if it accepts UTF-8 \"\n\"data. The easiest way to ensure this is to just to create an account with\"\n\" UTF-8 characters:\"\nmsgstr \"您现在还可以测试您的数据库安装是否接受 UTF-8 数据。确保这一点的最简单方法是创建一个带有 UTF-8 字符的帐户：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:235\nmsgid \"And then check that the account has been created:\"\nmsgstr \"然后检查该帐户是否已创建：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:241\nmsgid \"\"\n\"If the last command gives you no results it means there is still \"\n\"something wrong with your settings. You might also want to check your \"\n\"shell settings to make sure your command line shell supports UTF-8 \"\n\"characters and passes them correctly to MySQL:\"\nmsgstr \"\"\n\"如果最后一个命令没有给您任何结果，则意味着您的设置仍然存在问题。您可能还想检查您的 shell 设置以确保您的命令行 shell 支持 UTF-8\"\n\" 字符并将它们正确传递给 MySQL：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:249\nmsgid \"\"\n\"It seems that MySQL 5.0.x also needs extra parameters in the connection \"\n\"string: '&useUnicode=true&characterEncoding=UTF-8' while MySQL 5.1.x \"\n\"seems to not need it but it doesn’t hurt to have it for both versions. \"\n\"You have to edit ``etc/config.tdsl`` file and append this to the database\"\n\" connection string.\"\nmsgstr \"\"\n\"似乎 MySQL 5.0.x 在连接字符串中还需要额外的参数：'&useUnicode=true&characterEncoding=UTF-8'\"\n\" 而 MySQL 5.1.x 似乎不需要它，但对于两个版本都有此字符串并没有什么坏处。您必须编辑 ``etc/config.tdsl`` \"\n\"文件并将其附加到数据库连接字符串上。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:251\nmsgid \"\"\n\"For MySQL 5.1.x, however, you need to also update code for all database \"\n\"stored procedures and functions used by the Tigase. They are updated for \"\n\"Tigase version 4.4.x and up, however if you use an older version of the \"\n\"Tigase server, you can reload stored procedures using the file from SVN.\"\nmsgstr \"\"\n\"但是，对于 MySQL 5.1.x，您还需要更新 Tigase 使用的所有数据库存储过程和函数的代码。此更新针对 Tigase 版本 4.4.x \"\n\"及更高版本，但是如果您使用旧版本的 Tigase 服务器，您可以使用来自 SVN 的文件重新加载存储过程。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:254\nmsgid \"Other MySQL Settings Worth Considering\"\nmsgstr \"其他值得考虑的 MySQL 设置\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:256\nmsgid \"\"\n\"There are a number of other useful options, especially for performance \"\n\"improvements. Please note, you will have to review them as some of them \"\n\"may impact data reliability and are useful for performance or load tests \"\n\"installations only.\"\nmsgstr \"还有许多其他尤其是对于性能改进有用的选项。请注意，您必须查看它们，因为其中一些可能会影响数据可靠性，并且仅对性能或负载测试安装有用。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:264\nmsgid \"Some the general MySQL settings which mainly affect performance:\"\nmsgstr \"一些主要影响性能的通用MySQL 设置：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:279\nmsgid \"InnoDB specific settings:\"\nmsgstr \"InnoDB 特定设置：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:302\nmsgid \"\"\n\"These settings may not be fully optimized for your system, and have been \"\n\"only tested on our systems. If you have found better settings for your \"\n\"systems, feel free to `let us know <http://tigase.net/contact>`__.\"\nmsgstr \"\"\n\"这些设置可能未针对您的系统进行全面优化，并且仅在我们的系统上进行了测试。如果您为您的系统找到了更好的设置，请随时 `让我们知道 \"\n\"<http://tigase.net/contact>`__。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:305\nmsgid \"Support for emoji and other icons\"\nmsgstr \"支持表情符号和其他图标\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:307\nmsgid \"\"\n\"Tigase Database Schema can support emojis and other icons, however by \"\n\"using UTF-8 in ``mysqld`` settings will not allow this. To employ \"\n\"settings to support emojis and other icons, we recommend you use the \"\n\"following in your MySQL configuration file:\"\nmsgstr \"\"\n\"Tigase 数据库架构可以支持表情符号和其他图标，但是在 ``mysqld`` 设置中使用 UTF-8 \"\n\"将不允许这样做。要使用设置来支持表情符号和其他图标，我们建议您在 MySQL 配置文件中使用以下内容：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:316\nmsgid \"\"\n\"Doing this, Tigase XMPP Server Database will still use ``utf8`` character\"\n\" set, with ``utf8_general_ci`` as collation, and only fields which \"\n\"require support for emojis will be converted to ``utf8mb4``.\"\nmsgstr \"\"\n\"这样做，Tigase XMPP 服务器数据库仍将使用 ``utf8`` 字符集，以 ``utf8_general_ci`` \"\n\"作为排序规则，并且只有需要支持表情符号的字段才会转换为 ``utf8mb4``。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:320\nmsgid \"\"\n\"If for some reason, with above settings applied to your MySQL instance, \"\n\"you still receive :literal:`java.sql.SQLException: Incorrect string \"\n\"value: ` you should add to your database URI passed in Tigase XMPP Server\"\n\" following configuration `&useUnicode=true&characterEncoding=UTF-8`. If \"\n\"even this fails too, then you may try adding \"\n\"``&connectionCollation=utf8mb4_bin`` as a last resort. This changes \"\n\"situation from previous versions that shipped older MySQL JDBC connector.\"\nmsgstr \"\"\n\"如果由于某种原因，将上述设置应用于您的 MySQL 实例，您仍然会收到 :literal:`java.sql.SQLException: \"\n\"Incorrect string value: ` you should add to your database URI passed in \"\n\"Tigase XMPP Server following configuration \"\n\"`&useUnicode=true&characterEncoding=UTF-8`。如果这也失败了，那么你可以尝试添加 \"\n\"``&connectionCollation=utf8mb4_bin`` 作为最后一着。这改变了以前发布旧 MySQL JDBC \"\n\"连接器的版本的情况。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MySQL.inc:324\nmsgid \"\"\n\"Tigase XMPP Server databases should be created with ``utf8_general_ci`` \"\n\"collation as it will work properly and is fastest from \"\n\"``utf8mb4_general_ci`` collations supported by MySQL\"\nmsgstr \"\"\n\"Tigase XMPP 服务器数据库应该使用 ``utf8_general_ci`` 排序规则创建，因为它可以正常工作并且是 MySQL 支持的 \"\n\"``utf8mb4_general_ci`` 排序规则中最快的\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:4\nmsgid \"Prepare the Derby Database for the Tigase Server\"\nmsgstr \"为 Tigase 服务器准备 Derby 数据库\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:6\nmsgid \"\"\n\"This guide describes how to prepare Derby database for connecting the \"\n\"Tigase server.\"\nmsgstr \"本指南介绍如何准备 Derby 数据库以连接 Tigase 服务器。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:9\nmsgid \"Basic Setup\"\nmsgstr \"基本设置\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:10\nmsgid \"\"\n\"Preparation of Derby database is quite simple, but the following \"\n\"assumptions are made\"\nmsgstr \"Derby 数据库的准备非常简单，但是做了以下假设\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:12\nmsgid \"``DerbyDB`` - Derby database name\"\nmsgstr \"``DerbyDB`` - Derby 数据库名称\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:14\nmsgid \"``database/`` directory contains all necessary schema files\"\nmsgstr \"``database/`` 目录包含所有必要的架构文件\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:16\nmsgid \"``jars/`` and ``libs/`` directories contains Tigase and Derby binaries\"\nmsgstr \"``jars/`` 和 ``libs/`` 目录包含 Tigase 和 Derby 二进制文件\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:18\nmsgid \"General Approach\"\nmsgstr \"通常的做法\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:20\nmsgid \"\"\n\"From the main Tigase directory execute following commands (Linux and \"\n\"Windows accordingly)\"\nmsgstr \"从 Tigase 主目录执行以下命令（相应的 Linux 和 Windows）\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:24\nmsgid \"You must use these sql files on order FIRST!\"\nmsgstr \"您必须首先使用这些 sql 文件！\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:26\nmsgid \"**Linux**\"\nmsgstr \"**Linux**\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:32\nmsgid \"**Windows**\"\nmsgstr \"**Windows**\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:38\nmsgid \"\"\n\"This will create Derby database named DerbyDB in the main Tigase \"\n\"directory and load common version for common v0.1.\"\nmsgstr \"这将在 Tigase 主目录中创建名为 DerbyDB 的 Derby 数据库，并加载 common v0.1 的 common 版本。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:40\nmsgid \"You will need to repeat this process again in for following order:\"\nmsgstr \"您将需要按以下顺序再次重复此过程：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:64\nmsgid \"Connecting Tigase to database\"\nmsgstr \"将 Tigase 连接到数据库\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/Derby.inc:66\nmsgid \"\"\n\"Once the database is setup, configure the ``config.tdsl`` file in Tigase \"\n\"and add the following configuration:\"\nmsgstr \"设置数据库后，在 Tigase 中配置 ``config.tdsl`` 文件并添加以下配置：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:4\nmsgid \"Prepare the MS SQL Server Database for the Tigase Server\"\nmsgstr \"为 Tigase 服务器准备 MS SQL Server 数据库\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:6\nmsgid \"\"\n\"This guide describes how to prepare the MS SQL Server database for \"\n\"connecting the Tigase server to it.\"\nmsgstr \"本指南介绍如何准备 MS SQL Server 数据库以将 Tigase 服务器连接到它。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:8\nmsgid \"\"\n\"It’s expected that a working installation of Microsoft SQL Server is \"\n\"present. The following guide will describe the necessary configurations \"\n\"required for using MS SQL Server with Tigase XMPP Server.\"\nmsgstr \"\"\n\"预计存在 Microsoft SQL Server 的有效安装。以下指南将描述将 MS SQL Server 与 Tigase XMPP \"\n\"Server 一起使用所需的必要配置。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:11\nmsgid \"Preparing MS SQL Server Instance\"\nmsgstr \"准备 MS SQL Server 实例\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:13\nmsgid \"\"\n\"After installation of MS SQL Server an instance needs to be configure to \"\n\"handle incoming JDBC connections. For that purpose it’s required to open \"\n\"*SQL Server Configuration Manager*. In the left-hand side panel navigate \"\n\"to *SQL Server Configuration Manager*, then *SQL Server Network \"\n\"Configuration → Protocols for ${INSTANCE_NAME}*. After selecting instance\"\n\" in the right-hand side panel select TCP/IP and open *Properties*, in the\"\n\" Protocol tab in General section select Yes for Enabled property. In the \"\n\"IP Addresses tab select Yes for Active and Enabled properties of all IP \"\n\"Addresses that you want MS SQL Server to handle. Subsequently set the TCP\"\n\" Port property (if missing) to the default value - 1433. A restart of the\"\n\" instance may be required afterwards.\"\nmsgstr \"\"\n\"安装 MS SQL Server 后，需要配置实例以处理传入的 JDBC 连接。为此，需要打开 *SQL Server Configuration\"\n\" Manager*。在左侧面板中找到 *SQL Server Configuration Manager*，然后找到 *SQL Server \"\n\"Network Configuration → Protocols for ${INSTANCE_NAME}*。在右侧面板中选择实例后，选择 \"\n\"TCP/IP 并打开 *Properties*，在 General 部分的 Protocol 选项卡中为 Enabled 属性选择 Yes。在 \"\n\"IP 地址选项卡中，为您希望 MS SQL Server 处理的所有 IP 地址的活动和启用属性选择是。随后将 TCP \"\n\"端口属性（如果缺失）设置为默认值 - 1433。之后可能需要重新启动实例。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:16\nmsgid \"Configuration using MS SQL Server Management Studio\"\nmsgstr \"使用 MS SQL Server Management Studio 进行配置\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:18\nmsgid \"\"\n\"In order to prepare the database you can use either a wizard or execute \"\n\"queries directly in the Query Editor. Firstly you need to establish a \"\n\"connection to the MS SQL Server instance. From Object Explorer select \"\n\"Connect and in the Connect to Server dialog enter administrator \"\n\"credentials.\"\nmsgstr \"\"\n\"为了准备数据库，您可以使用向导或在查询编辑器中直接执行查询。首先，您需要建立与 MS SQL Server \"\n\"实例的连接。从对象资源管理器中选择连接并在连接到服务器对话框中输入管理员凭据。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:20\nmsgid \"Using Wizards\"\nmsgstr \"使用向导\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:22\nmsgid \"Create Login\"\nmsgstr \"创建登录\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:24\nmsgid \"\"\n\"In the left-hand side panel select Security → Logins and from context \"\n\"menu choose New Login, in the Wizard window enter desired Login name, \"\n\"select SQL Server authentication and enter desired password subsequently \"\n\"confirming action with OK\"\nmsgstr \"\"\n\"在左侧面板中选择 Security → Logins 并从上下文菜单中选择 New Login，在向导窗口中输入所需的登录名，选择 SQL \"\n\"Server 身份验证并输入所需的密码，然后单击 OK 确认操作\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:26\nmsgid \"Create Database\"\nmsgstr \"创建数据库\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:28\nmsgid \"\"\n\"From the Object Explorer select Databases node and from context menu \"\n\"select New Database; in the Wizard window enter desired Database name and\"\n\" enter previously created Login name into Owner field; subsequently \"\n\"confirming action with OK.\"\nmsgstr \"\"\n\"从对象资源管理器中选择数据库节点并从上下文菜单中选择新数据库；在向导窗口中输入所需的数据库名称，并在所有者字段中输入先前创建的登录名；随后用 OK\"\n\" 确认操作。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:31\nmsgid \"Using Queries\"\nmsgstr \"使用查询\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:33\nmsgid \"\"\n\"From the Object Explorer root node’s context menu select New Query. In \"\n\"the Query windows execute following statements adjusting details to your \"\n\"liking:\"\nmsgstr \"从 Object Explorer 根节点的上下文菜单中选择 New Query。在查询窗口中执行以下语句，根据您的喜好调整详细信息：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:52\nmsgid \"Import Schema\"\nmsgstr \"导入架构\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:54\nmsgid \"\"\n\"From the File menu Select Open → File (or use Ctrl+O) and then open \"\n\"following files:\"\nmsgstr \"从文件菜单中选择 Open → File（或使用 Ctrl+O），然后打开以下文件：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:71\nmsgid \"\"\n\"These files must be done sequentially! They are not linked, and so may \"\n\"need to be done one at a time.\"\nmsgstr \"这些文件必须按顺序完成！它们没有关联，因此可能需要一次完成一个。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:82\nmsgid \"\"\n\"Subsequently select created database from the list of Available Databases\"\n\" (Ctrl+U) available on the toolbar and execute each of the opened files \"\n\"in the order listed above.\"\nmsgstr \"随后从工具栏上可用的数据库 (Ctrl+U) 列表中选择创建的数据库，并按上面列出的顺序执行每个打开的文件。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:85\nmsgid \"Configuring from command line tool\"\nmsgstr \"从命令行工具配置\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:87\nmsgid \"\"\n\"Creation of the database and import of schema can be done from command \"\n\"line as well. In order to do that, execute following commands from the \"\n\"directory where Tigase XMPP Server is installed otherwise paths to the \"\n\"schema need to be adjusted accordingly:\"\nmsgstr \"数据库的创建和架构的导入也可以从命令行完成。为此，请从安装 Tigase XMPP 服务器的目录中执行以下命令，否则需要相应地调整架构的路径：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:99\nmsgid \"\"\n\"Above can be automatized with provided script %tigase-server%\\\\scripts\"\n\"\\\\db-create-sqlserver.cmd (note: it needs to be executed from main Tigase\"\n\" XMPP Server directory due to maintain correct paths):\"\nmsgstr \"\"\n\"以上可以使用提供的脚本 %tigase-server%\\\\scripts\\\\db-create-sqlserver.cmd \"\n\"自动化（注意：由于保持正确的路径，它需要从主 Tigase XMPP Server 目录执行）：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:105\nmsgid \"If no parameters are provided then the following defaults are used:\"\nmsgstr \"如果未提供参数，则使用以下默认值：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:117\nmsgid \"Tigase configuration - config.tdsl\"\nmsgstr \"Tigase 配置 - config.tdsl\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:119\nmsgid \"Configuration of the MS SQL Server follows general database convention.\"\nmsgstr \"MS SQL Server 的配置遵循普遍数据库约定。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:129\nmsgid \"where any number of additional parameters can (and should) consist of:\"\nmsgstr \"其中任何数量的附加参数可以（并且应该）包括：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:131\nmsgid \"``databaseName`` - name of the database\"\nmsgstr \"``databaseName`` - 数据库名称\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:133\nmsgid \"``user`` - username configured to access database\"\nmsgstr \"``user`` - 配置为访问数据库的用户名\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:135\nmsgid \"``password`` - password for the above username\"\nmsgstr \"``password`` - 上述用户名的密码\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:137\nmsgid \"``schema`` - name of the database schema\"\nmsgstr \"``schema`` - 数据库架构的名称\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:139\nmsgid \"\"\n\"``lastUpdateCount`` - 'false' value causes all update counts to be \"\n\"returned, including those returned by server triggers\"\nmsgstr \"``lastUpdateCount`` - 'false' value 导致返回所有更新计数，包括服务器触发器返回的更新计数\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:141\nmsgid \"Example:\"\nmsgstr \"例子：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:152\nmsgid \"JDBC: jTDS vs MS JDBC driver (obsolete)\"\nmsgstr \"JDBC：jTDS 与 MS JDBC 驱动程序（已过时）\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:154\nmsgid \"\"\n\"Previously Tigase XMPP Server was shipped with the jTDS open source \"\n\"driver however since 8.3.0 we switched to the FOSS driver provided by the\"\n\" Microsoft itself. Previous jdbc url will fallback to the Microsoft \"\n\"driver automatically.\"\nmsgstr \"\"\n\"以前 Tigase XMPP 服务器附带 jTDS 开源驱动程序，但是从 8.3.0 开始，我们切换到 Microsoft 自己提供的 FOSS \"\n\"驱动程序。以前的 jdbc url 将自动回退到 Microsoft 驱动程序。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MSSQL.inc:156\nmsgid \"Microsoft driver:\"\nmsgstr \"Microsoft 驱动:\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:4\nmsgid \"Prepare the PostgreSQL Database for the Tigase Server\"\nmsgstr \"为 Tigase 服务器准备 PostgreSQL 数据库\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:6\nmsgid \"\"\n\"This guide describes how to prepare PostgreSQL database for connecting to\"\n\" Tigase server.\"\nmsgstr \"本指南介绍如何准备 PostgreSQL 数据库以连接到 Tigase 服务器。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:8\nmsgid \"\"\n\"The PostgreSQL database can be prepared in many ways. Below are presented\"\n\" two possible ways. The following assumptions apply to both methods:\"\nmsgstr \"可以通过多种方式准备 PostgreSQL 数据库。下面介绍两种可能的方式。以下假设适用于这两种方法：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:10\nmsgid \"``admin_db_user`` - database user with admin rights\"\nmsgstr \"``admin_db_user`` - 具有管理员权限的数据库用户\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:12\nmsgid \"``tigase_user`` - database user for Tigase\"\nmsgstr \"``tigase_user`` - Tigase 的数据库用户\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:14\nmsgid \"``tigasedb`` - database for Tigase\"\nmsgstr \"``tigasedb`` - Tigase 数据库\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:17\nmsgid \"Configuring from PostgreSQL Command Line Tool\"\nmsgstr \"从 PostgreSQL 命令行工具配置\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:19\nmsgid \"Run the PostgreSQL command line client and enter following instructions:\"\nmsgstr \"运行 PostgreSQL 命令行客户端并输入以下指令：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:21\nmsgid \"Add the ``tigase_user``:\"\nmsgstr \"添加 ``tigase_user``:\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:27\nmsgid \"\"\n\"Next, Create the database for the Tigase server with ``tigase_user`` as \"\n\"owner of database:\"\nmsgstr \"接下来，为 Tigase 服务器创建数据库，并使用 ``tigase_user`` 作为数据库所有者：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:35\nmsgid \"\"\n\"Load database schema to initialize the Tigase server from the file that \"\n\"corresponds to the version of Tigase you want to use. First you need to \"\n\"switch to ``tigasedb``.\"\nmsgstr \"加载数据库架构以从您要使用的 Tigase 版本相对应的文件中初始化 Tigase 服务器。首先你需要切换到 ``tigasedb``。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:41\nmsgid \"Begin by applying the basic Schema\"\nmsgstr \"首先应用基本架构\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:47\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:100\nmsgid \"Continue by adding the schema files listed below:\"\nmsgstr \"继续添加下面列出的架构文件：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:74\nmsgid \"Follow steps below to prepare the PostgreSQL database:\"\nmsgstr \"按照以下步骤准备 PostgreSQL 数据库：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:76\nmsgid \"First, add the ``tigase_user``:\"\nmsgstr \"首先，添加 ``tigase_user``:\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:82\nmsgid \"\"\n\"You will be asked for credentials for admin_db_user and password for new \"\n\"database user.\"\nmsgstr \"系统将要求您提供 admin_db_user 的凭据和新数据库用户的密码。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:84\nmsgid \"\"\n\"Create the database for the Tigase server with tigase_user as owner of \"\n\"database:\"\nmsgstr \"使用 tigase_user 作为数据库所有者为 Tigase 服务器创建数据库：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:90\nmsgid \"Database Schema Installation\"\nmsgstr \"数据库架构安装\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:92\nmsgid \"Load database schema to initialize the Tigase server\"\nmsgstr \"加载数据库模式以初始化 Tigase 服务器\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/PostGRE.inc:126\nmsgid \"\"\n\"The above commands should be executed from the main Tigase directory. The\"\n\" initialization schema file should be also available locally in database/\"\n\" directory of your Tigase installation.\"\nmsgstr \"上述命令应从 Tigase 主目录执行。初始化架构文件也应该在 Tigase 安装的 database/ 目录中本地可用。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:4\nmsgid \"Preparing Tigase for MongoDB\"\nmsgstr \"为 MongoDB 准备 Tigase\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:6\nmsgid \"\"\n\"Tigase now supports MongoDB for auth, settings, and storage repositories.\"\n\" If you wish to use MongoDB for Tigase, please use this guide to help \"\n\"you.\"\nmsgstr \"Tigase 现在支持 MongoDB 进行身份验证，设置和存储库。如果您希望将 MongoDB 用于 Tigase，请使用本指南来帮助您。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:9\nmsgid \"Dependencies\"\nmsgstr \"依赖项\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:11\nmsgid \"\"\n\"To run Tigase MongoDB support library requires drivers for MongoDB for \"\n\"Java which can be downloaded from `here <https://github.com/mongodb\"\n\"/mongo-java-driver/releases>`__. This driver needs to be placed in \"\n\"``jars/`` directory located in Tigase XMPP Server installation directory.\"\n\" If you are using a dist-max distribution, it is already included.\"\nmsgstr \"\"\n\"要运行 Tigase MongoDB 支持库，需要适用于 Java 的 MongoDB 驱动程序，可以从 `这里 \"\n\"<https://github.com/mongodb/mongo-java-driver/releases>`__ 下载。此驱动程序需要放置在 \"\n\"Tigase XMPP Server 安装目录中的 ``jars/`` 目录中。如果您使用的是 dist-max 发行版，则它已经包含在内。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:14\nmsgid \"Configuration\"\nmsgstr \"配置\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:16\nmsgid \"\"\n\"Note that fresh installations of MongoDB do not come with users or \"\n\"databases installed. Once you have setup MongoDB you will need to create \"\n\"a user to be used with Tigase. To do this, bring up the mongo console by \"\n\"running mongo.exe in a cmd window for windows, or run mongo in linux. \"\n\"Once connected, enter then following:\"\nmsgstr \"\"\n\"请注意，全新安装的 MongoDB 没有安装用户或数据库。设置 MongoDB 后，您将需要创建一个用于 Tigase 的用户。为此，通过在 \"\n\"windows 的 cmd 窗口中运行 mongo.exe 来调出 mongo 控制台，或者在 linux 中运行 \"\n\"mongo。连接后，输入以下内容：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:28\nmsgid \"\"\n\"Be sure to give this user a ``root`` role in order to properly write to \"\n\"the database. Once you receive a ``user successfully created`` message, \"\n\"you are ready to install tigase on MongoDB.\"\nmsgstr \"\"\n\"一定要给这个用户一个 ``root`` 角色，以便正确地写入数据库。一旦收到 ``user successfully created`` \"\n\"消息，就可以在 MongoDB 上安装 tigase。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:30\nmsgid \"Configuration of user repository for Tigase XMPP Server\"\nmsgstr \"Tigase XMPP 服务器的用户存储库配置\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:32\nmsgid \"\"\n\"To configure Tigase XMPP Server to use MongoDB you need to set \"\n\"``dataSource`` in etc/config.tdsl file to proper MongoDB URI pointing to \"\n\"which MongoDB database should be used (it will be created by MongoDB if \"\n\"it does not exist). ``userRepository`` property should not be set to let \"\n\"Tigase XMPP Server auto-detect proper implementation of \"\n\"``UserRepository``. Tigase XMPP Server will create proper collections in \"\n\"MongoDB if they do not exist so no schema files are necessary.\"\nmsgstr \"\"\n\"要将 Tigase XMPP 服务器配置用于 MongoDB，您需要在 etc/config.tdsl 文件中将 ``dataSource`` \"\n\"设置为正确的 MongoDB URI，其指向应该使用的 MongoDB 数据库（如果不存在，它将由 MongoDB 创建）。 \"\n\"``userRepository`` 属性不应设置为让 Tigase XMPP 服务器自动检测 ``UserRepository`` 的正确实现。\"\n\" Tigase XMPP 服务器将在 MongoDB 中创建适当的集合（如果它们不存在），则不需要架构文件。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:34\nmsgid \"\"\n\"Example configuration of XMPP Server pointing to MongoDB database \"\n\"``tigase_test`` in a local instance:\"\nmsgstr \"XMPP 服务器在本地实例中指向 MongoDB 数据库 ``tigase_test`` 的示例配置：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:50\nmsgid \"\"\n\"If Tigase Server is not able to detect a proper storage layer \"\n\"implementation, it can be forced to use one provided by Tigase using the \"\n\"following lines in ``etc/config.tdsl`` file:\"\nmsgstr \"\"\n\"如果 Tigase 服务器无法检测到正确的存储层实现，则可以强制使用由Tigase通过 ``etc/config.tdsl`` \"\n\"文件中的以下行提供的其中之一：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:65\nmsgid \"\"\n\"Every component should be able to use proper implementation to support \"\n\"MongoDB using this URI. Also MongoDB URI can be passed as any URI in \"\n\"configuration of any component.\"\nmsgstr \"每个组件都应该能够使用正确的实现来支持使用此 URI 的 MongoDB。 MongoDB URI 也可以作为任何组件配置中的任何 URI 传递。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:67\nmsgid \"Configuration for MUC\"\nmsgstr \"MUC 的配置\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:69\nmsgid \"\"\n\"By default, MUC component will use MongoDB to store data if Tigase is \"\n\"configured to use it as a default store. However, if you would like to \"\n\"use a different MongoDB database to store MUC message archive, you can do\"\n\" this by adding the following lines to ``etc/config.tdsl`` file:\"\nmsgstr \"\"\n\"默认情况下，如果 Tigase 配置使用 MongoDB 作为默认存储，MUC 组件将使用 MongoDB 存储数据。但是，如果您想使用不同的 \"\n\"MongoDB 数据库来存储 MUC 消息存档，您可以通过在 ``etc/config.tdsl`` 文件中添加以下行来做到这一点：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:77\nmsgid \"\"\n\"If MUC components fails to detect and use a proper storage layer for \"\n\"MongoDB, you can force it to use one provided by Tigase by using the \"\n\"following line in the ``config.tdsl`` file:\"\nmsgstr \"\"\n\"如果 MUC 组件无法为 MongoDB 检测和使用适当的存储层，您可以通过在 ``config.tdsl`` 文件中使用以下行来强制它使用 \"\n\"Tigase 提供的存储层：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:86\nmsgid \"Configuration for PubSub\"\nmsgstr \"PubSub 的配置\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:88\nmsgid \"\"\n\"By default, PubSub component will use MongoDB to store data if Tigase is \"\n\"configured to use it as a default store. However, if you would like to \"\n\"use a different MongoDB database to store PubSub component data, you can \"\n\"do this by adding the following lines to ``etc/config.tdsl`` file:\"\nmsgstr \"\"\n\"默认情况下，如果 Tigase 配置将MongoDB 用作默认存储，则 PubSub 组件将使用 MongoDB \"\n\"存储数据。但是，如果您想使用不同的 MongoDB 数据库来存储 PubSub 组件数据，您可以通过在 ``etc/config.tdsl`` \"\n\"文件中添加以下行来实现：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:96\nmsgid \"\"\n\"If the PubSub components fails to detect and use a proper storage layer \"\n\"for MongoDB, you can force it to use one provided by Tigase by using the \"\n\"following line in the ``config.tdsl`` file:\"\nmsgstr \"\"\n\"如果 MUC 组件无法为 MongoDB 检测和使用适当的存储层，您可以通过在 ``config.tdsl`` 文件中使用以下行来强制它使用 \"\n\"Tigase 提供的存储层：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:105\nmsgid \"Configuration for Message Archiving\"\nmsgstr \"消息归档的配置\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:107\nmsgid \"\"\n\"By default, the Message Archiving component will use MongoDB to store \"\n\"data if Tigase is configured to use it as a default store. However, if \"\n\"you would like to use a different MongoDB database to store message \"\n\"archives, you can do this by adding the following lines to \"\n\"``etc/config.tdsl`` file:\"\nmsgstr \"\"\n\"默认情况下，如果将 Tigase 配置将MongoDB 用作默认存储，则消息存档组件将使用 MongoDB 存储数据。但是，如果您想使用不同的 \"\n\"MongoDB 数据库来存储消息存档，您可以通过在 ``etc/config.tdsl`` 文件中添加以下行来做到这一点：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:115\nmsgid \"\"\n\"If Message Archiving component fails to detect and use a proper storage \"\n\"layer for MongoDB, you can force it to use one provided by Tigase by \"\n\"using the following line in the ``config.tdsl`` file:\"\nmsgstr \"\"\n\"如果消息存档组件未能检测到并为 MongoDB 使用适当的存储层，您可以通过在 ``config.tdsl`` 文件中使用以下行来强制它使用 \"\n\"Tigase 提供的存储层：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:125\nmsgid \"Schema Description\"\nmsgstr \"架构描述\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:127\nmsgid \"\"\n\"This description contains only basic description of schema and only basic\"\n\" part of it. More collections may be created if additional components of \"\n\"Tigase XMPP Server are loaded and configured to use MongoDB.\"\nmsgstr \"\"\n\"该描述仅包含架构的基本描述，并且仅包含其基本部分。如果加载并配置 Tigase XMPP 服务器的其他组件以使用 \"\n\"MongoDB，则可能会创建更多集合。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:130\nmsgid \"Tigase XMPP Server Schema\"\nmsgstr \"Tigase XMPP 服务器架构\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:132\nmsgid \"\"\n\"Basic schema for UserRespository and AuthRepository consists of two \"\n\"collections: . tig_users - contains list of users . tig_nodes - contains \"\n\"data related to users in tree-like way\"\nmsgstr \"\"\n\"UserRespository 和 AuthRepository 的基本架构由两个集合组成： . tig_users - 包含用户列表。 \"\n\"tig_nodes - 以树状方式包含与用户相关的数据\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:134\nmsgid \"``tig_users`` collection contains the following fields:\"\nmsgstr \"``tig_users`` 集合包含以下字段：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:136\nmsgid \"Table 9. tig_users\"\nmsgstr \"Table 9. tig_users\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:139\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:155\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:173\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:195\nmsgid \"Name\"\nmsgstr \"名称\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:139\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:155\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:173\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:195\nmsgid \"Description\"\nmsgstr \"描述\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:141\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:157\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:197\nmsgid \"\\\\_id\"\nmsgstr \"\\\\_id\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:141\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:159\nmsgid \"id of user which is SHA256 hash of users jid (raw byte array).\"\nmsgstr \"用户 id 是用户 jid 的 SHA256 哈希（原始字节数组）。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:143\nmsgid \"user_id\"\nmsgstr \"user_id\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:143\nmsgid \"contains full user jid.\"\nmsgstr \"包含完整的用户 jid。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:145\nmsgid \"domain\"\nmsgstr \"域\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:145\nmsgid \"domain to which user belongs for easier lookup of users by domain.\"\nmsgstr \"用户所属的域，以便于按域查找用户。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:147\nmsgid \"password\"\nmsgstr \"密码\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:147\nmsgid \"password of user (will be removed after upgrade to 8.0.0).\"\nmsgstr \"用户密码（升级到 8.0.0 后将被删除）。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:150\nmsgid \"``tig_nodes`` collection contains the following fields\"\nmsgstr \"``tig_nodes`` 集合包含以下字段\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:152\nmsgid \"Table 10. tig_nodes\"\nmsgstr \"Table 10. tig_nodes\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:157\nmsgid \"id of row auto-generated by MongoDB.\"\nmsgstr \"由 MongoDB 自动生成的行的 id。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:159\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:199\nmsgid \"uid\"\nmsgstr \"uid\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:161\nmsgid \"node\"\nmsgstr \"节点\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:161\nmsgid \"full path of node in tree-like structure separated by / (may not exist).\"\nmsgstr \"树状结构中节点的完整路径，由 / 分隔（可能不存在）。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:163\nmsgid \"key\"\nmsgstr \"键\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:163\nmsgid \"key for which value for node is set.\"\nmsgstr \"为其设置节点值的键。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:165\nmsgid \"value\"\nmsgstr \"值\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:165\nmsgid \"value which is set for node key.\"\nmsgstr \"为节点键设置的值。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:168\nmsgid \"\"\n\"Tigase XMPP Server also uses additional collections for storage of \"\n\"Offline Messages\"\nmsgstr \"Tigase XMPP 服务器还使用其他的集合来存储离线消息\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:170\nmsgid \"Table 11. msg_history collection\"\nmsgstr \"Table 11. msg_history collection\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:175\nmsgid \"from\"\nmsgstr \"from\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:175\nmsgid \"full user jid of message sender.\"\nmsgstr \"消息发送者的完整用户 jid。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:177\nmsgid \"from_hash\"\nmsgstr \"from_hash\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:177\nmsgid \"SHA256 hash of message sender jid as raw byte array.\"\nmsgstr \"消息发送者 jid 的 SHA256 哈希作为原始字节数组。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:179\nmsgid \"to\"\nmsgstr \"to\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:179\nmsgid \"full users jid of message recipient.\"\nmsgstr \"消息接收者的完整用户 jid。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:181\nmsgid \"to_hash\"\nmsgstr \"to_hash\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:181\nmsgid \"SHA256 hash of message recipient full jid as raw byte array.\"\nmsgstr \"消息接收者完整 jid 的 SHA256 哈希作为原始字节数组。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:183\nmsgid \"ts\"\nmsgstr \"ts\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:183\nmsgid \"timestamp of message as date.\"\nmsgstr \"消息的时间戳作为日期。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:185\nmsgid \"message\"\nmsgstr \"消息\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:185\nmsgid \"serialized XML stanza containing message.\"\nmsgstr \"包含消息的序列化 XML 节。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:187\nmsgid \"expire-at\"\nmsgstr \"expire-at\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:187\nmsgid \"\"\n\"timestamp of expiration of message (if message contains AMP expire-at \"\n\"set).\"\nmsgstr \"消息过期的时间戳（如果消息包含 AMP expire-at set）。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:190\nmsgid \"\"\n\"Due to changes in authentication and credentials storage in \"\n\"AuthRepository, we moved ``password`` field from ``tig_users`` collection\"\n\" to a newly created collection called ``tig_user_credentials``.\"\nmsgstr \"\"\n\"由于 AuthRepository 中身份验证和凭据存储的变化，我们将 ``password`` 字段从 ``tig_users`` \"\n\"集合移动到了一个新创建的名为 ``tig_user_credentials`` 的集合。\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:192\nmsgid \"This new collection has following fields:\"\nmsgstr \"这个新集合有以下字段：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:197\nmsgid \"id of document automatically generated by MongoDB\"\nmsgstr \"MongoDB自动生成的文档id\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:199\nmsgid \"SHA256 hash of a user for which credentails are stored\"\nmsgstr \"存储凭据用户的 SHA256 哈希\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:201\nmsgid \"username\"\nmsgstr \"用户名\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:201\nmsgid \"username provided during authentication (or ``default``)\"\nmsgstr \"身份验证期间提供的用户名（或 ``default``）\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:203\nmsgid \"account_status\"\nmsgstr \"帐户状态\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:203\nmsgid \"\"\n\"name of an account state (copy of value stored in user document \"\n\"from`tig_users`)\"\nmsgstr \"帐户状态的名称（来自`tig_users`的用户文档中存储的值的副本）\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:206\nmsgid \"\"\n\"Additionally for each mechanism we store separate field in this object, \"\n\"so for:\"\nmsgstr \"此外，对于每种机制，我们在此对象中存储单独的字段，因此对于：\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:208\nmsgid \"``PLAIN`` we have ``PLAIN`` field with value for this mechanism\"\nmsgstr \"``PLAIN`` 我们有 ``PLAIN`` 字段，该字段具有此机制的值\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:210\nmsgid \"\"\n\"``SCRAM-SHA-1`` we have ``SCRAM-SHA-1`` field with value for this \"\n\"mechanism\"\nmsgstr \"``SCRAM-SHA-1`` 我们有 ``SCRAM-SHA-1`` 字段，该字段具有此机制的值\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:212\nmsgid \"etc…​\"\nmsgstr \"等等……​\"\n\n#: ../../Tigase_Administration/Database_Management/Database_Preparation/MongoDB.inc:214\nmsgid \"\"\n\"Upgrade is not done in one step, and rather will be done once a \"\n\"particular user will log in. During authentication if there is no data in\"\n\" ``tig_user_credentials``, Tigase XMPP Server will check if ``password`` \"\n\"field in ``tig_user`` exists. If it does, and it is filled credentials \"\n\"will be migrated to the new collection.\"\nmsgstr \"\"\n\"升级不是一步完成的，而是在特定用户登录后完成。在身份验证期间，如果 ``tig_user_credentials`` 中没有数据，Tigase \"\n\"XMPP Server 将检查 ``tig_user`` 中 ``password`` \"\n\"字段是否存在。如果确实存在，并且已填写凭据，则将迁移到新集合。\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:4\nmsgid \"Hashed User Passwords in Database\"\nmsgstr \"数据库中的哈希用户密码\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:8\nmsgid \"\"\n\"This feature is still available, but passwords are stored encrypted by \"\n\"default since v8.0.0. We do not recommend using these settings.\"\nmsgstr \"此功能仍然可用，但自 v8.0.0 起，密码默认加密存储。我们不建议使用这些设置。\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:10\nmsgid \"\"\n\"By default, user passwords are stored in plain-text in the Tigase’s \"\n\"database. However, there is an easy way to have them encoded in either \"\n\"one of already supported ways or to even add a new encoding algorithm on \"\n\"your own.\"\nmsgstr \"\"\n\"默认情况下，用户密码以纯文本形式存储在 Tigase \"\n\"的数据库中。但是，有一种简单的方法可以让它们以一种已经支持的方式进行编码，甚至可以自己添加新的编码算法。\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:12\nmsgid \"\"\n\"Storing passwords in hashed format in the database makes it possible to \"\n\"avoid using a plain-text password authentication mechanism. You cannot \"\n\"have hashed passwords in the database and non-plain-text password \"\n\"authentication. On the other hand, the connection between the server and \"\n\"the client is almost always secured by SSL/TLS so the plain-text password\"\n\" authentication method is perhaps less of a problem than storing plain-\"\n\"text passwords in the database.\"\nmsgstr \"\"\n\"在数据库中以哈希格式存储密码可以避免使用纯文本密码验证机制。您不能在数据库中使用哈希密码和非纯文本密码身份验证。另一方面，服务器和客户端之间的连接几乎总是由\"\n\" SSL/TLS 保护，因此纯文本密码验证方法可能比将纯文本密码存储在数据库中的问题更小。\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:14\nmsgid \"Nevertheless, it is simple enough to adjust this in Tigase’s database.\"\nmsgstr \"尽管如此，在 Tigase 的数据库中进行调整还是很简单的。\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:17\nmsgid \"Shortcut\"\nmsgstr \"快捷方式\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:19\nmsgid \"\"\n\"Connect to your database from a command line and execute following \"\n\"statement for MySQL database:\"\nmsgstr \"从命令行连接到您的数据库并对 MySQL 数据库执行以下语句：\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:25\nmsgid \"Where encoding mode is one of the following:\"\nmsgstr \"其中编码模式是以下之一：\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:27\nmsgid \"\"\n\"``MD5-PASSWORD`` the database stores MD5 hash code from the user’s \"\n\"password.\"\nmsgstr \"``MD5-PASSWORD`` 数据库存储来自用户密码的 MD5 哈希码。\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:29\nmsgid \"\"\n\"``MD5-USERID-PASSWORD`` the database stores MD5 hash code from \"\n\"concatenated user’s bare JID and password.\"\nmsgstr \"``MD5-USERID-PASSWORD`` 数据库存储来自连接用户的裸 JID 和密码的 MD5 哈希码。\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:31\nmsgid \"\"\n\"``MD5-USERNAME-PASSWORD`` the database stores MD5 hash code from \"\n\"concatenated user’s name (localpart) and password.\"\nmsgstr \"``MD5-USERNAME-PASSWORD`` 数据库存储来自连接用户名（localpart）和密码的 MD5 哈希码。\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:33\nmsgid \"For example:\"\nmsgstr \"例如：\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:40\nmsgid \"Full Route\"\nmsgstr \"全程路线\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:42\nmsgid \"\"\n\"The way passwords are stored in the DB is controlled by Tigase database \"\n\"schema property. Properties in the database schema can be set by a stored\"\n\" procedure called: ``TigPutDBProperty(key, value)``. Properties from the \"\n\"DB schema can be retrieved using another stored function called: \"\n\"``TigGetDBProperty(key)``.\"\nmsgstr \"\"\n\"密码存储在数据库中的方式由 Tigase 数据库架构属性控制。数据库架构中的属性可以通过一个名为 ``TigPutDBProperty(key, \"\n\"value)`` 的存储程序来设置。还可以用另一个名为 ``TigGetDBProperty(key)`` 的存储函数从 DB 架构中检索属性。\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:44\nmsgid \"\"\n\"The simplest way to call them is via command-line interface to the \"\n\"database.\"\nmsgstr \"调用它们的最简单方法是通过数据库的命令行界面。\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:46\nmsgid \"\"\n\"For the purpose of this guide let’s say we have a MySQL database and a \"\n\"test account: ``test@example.com`` with password ``test77``.\"\nmsgstr \"就本指南而言，假设我们有一个 MySQL 数据库和一个测试帐户：``test@example.com``，密码为 ``test77``。\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:48\nmsgid \"\"\n\"By default, most of DB actions for Tigase, are performed using stored \"\n\"procedures including user authentication. So, the first thing to do is to\"\n\" make sure the stored procedures are working correctly.\"\nmsgstr \"默认情况下，Tigase 的大多数 DB 操作包括用户身份验证都是使用存储程序执行的。因此，首先要做的是确保存储程序正常工作。\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:51\nmsgid \"Create a Test User Account\"\nmsgstr \"创建测试用户帐户\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:53\nmsgid \"\"\n\"To add a new user account we use a stored procedure: \"\n\"``TigAddUserPlainPw(bareJid, password)``. As you can see there is this \"\n\"strange appendix to the procedure name: ``PlainPw``. This procedure \"\n\"accepts plain passwords regardless how it is stored in the database. So \"\n\"it is safe and easy to use either for plain-text passwords or hashed in \"\n\"the DB. There are also versions of procedures without this appendix but \"\n\"they are sensitive on the data format and always have to pass password in\"\n\" the exact format it is stored in the database.\"\nmsgstr \"\"\n\"要添加新用户帐户，我们使用存储程序：``TigAddUserPlainPw(bareJid, \"\n\"password)``。如您所见，程序名称有一个奇怪的附录：``PlainPw``。不管它是如何存储在数据库中的，这个程序接受普通密码。因此，无论是明文密码还是在数据库中哈希码，它都是安全且易于使用的。也有没有此附录的程序版本，但它们对数据格式很敏感，并且始终必须以存储在数据库中的确切格式传递密码。\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:55\nmsgid \"So, let’s add a new user account:\"\nmsgstr \"所以，让我们添加一个新的用户帐户：\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:61\nmsgid \"\"\n\"If the result was 'Query OK', then it means the user account has been \"\n\"successfully created.\"\nmsgstr \"如果结果为'Query OK'，则表示用户帐户已成功创建。\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:64\nmsgid \"Test User Authentication\"\nmsgstr \"测试用户身份验证\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:66\nmsgid \"We can now test user authentication:\"\nmsgstr \"我们现在可以验证用户身份：\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:72\nmsgid \"If authentication was successful the result looks like this:\"\nmsgstr \"如果身份验证成功，结果如下所示：\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:85\nmsgid \"If authentication was unsuccessful, the result looks like this:\"\nmsgstr \"如果身份验证不成功，结果如下所示：\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:99\nmsgid \"Password Encoding Check\"\nmsgstr \"密码编码检查\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:101\nmsgid \"\"\n\"``TigGetDBProperty`` is a function, not a procedure in MySQL database so \"\n\"we have to use select to call it:\"\nmsgstr \"``TigGetDBProperty`` 是一个函数，而不是 MySQL 数据库中的程序，所以我们必须使用 select 来调用它：\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:107\nmsgid \"Most likely output is this:\"\nmsgstr \"最有可能的输出是这样的：\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:118\nmsgid \"\"\n\"Which means a default password encoding is used, in plain-text and thus \"\n\"no encoding. And we can actually check this in the database directly:\"\nmsgstr \"这意味着使用默认密码编码，以纯文本形式，因此没有编码。我们实际上可以直接在数据库中检查这个：\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:124\nmsgid \"And expected result with plain-text password format would be:\"\nmsgstr \"纯文本密码格式的预期结果是：\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:136\nmsgid \"Password Encoding Change\"\nmsgstr \"密码编码更改\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:138\nmsgid \"Now let’s set password encoding to MD5 hash:\"\nmsgstr \"现在让我们将密码编码设置为 MD5 哈希：\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:144\nmsgid \"\"\n\"'Query OK', means the password encoding has been successfully changed. Of\"\n\" course we changed the property only. All the existing passwords in the \"\n\"database are still in plain-text format. Therefore we expect that attempt\"\n\" to authenticate the user would fail:\"\nmsgstr \"\"\n\"'Query \"\n\"OK'，表示密码编码已成功更改。当然，我们只是改变了属性。数据库中所有现有的密码仍然是纯文本格式。因此，我们预计对用户进行身份验证的尝试会失败：\"\n\n#: ../../Tigase_Administration/Database_Management/Hashed_User_Passwords_in_Database.inc:158\nmsgid \"We can fix this by updating the user’s password in the database:\"\nmsgstr \"我们可以通过更新数据库中的用户密码来解决这个问题：\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:2\nmsgid \"Tigase Server and Multiple Databases\"\nmsgstr \"Tigase 服务器和多个数据库\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:4\nmsgid \"\"\n\"Splitting user authentication data from all other XMPP information such \"\n\"as roster, vcards, etc…​ was almost always possible in Tigase XMPP \"\n\"Server. Possible and quite simple thing to configure. Also it has been \"\n\"always possible and easy to assign a different database for each Tigase \"\n\"component (MUC, PubSub, AMP), for recording the server statistics. Almost\"\n\" every data type or component can store information in a different \"\n\"location, simple and easy to setup through the configuration file.\"\nmsgstr \"\"\n\"在 Tigase XMPP Server 中几乎总是可以从所有其他 XMPP \"\n\"信息（如花名册、电子名片等）中分离用户身份验证数据。配置可能而且非常简单。此外，始终可以容易地为每个 Tigase 组件（MUC, PubSub,\"\n\" AMP）分配不同的数据库，以记录服务器统计信息。几乎每种数据类型或组件都可以将信息存储在不同的位置，这些可通过配置文件简单易行地进行设置。\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:6\nmsgid \"\"\n\"However it is much less known that it is also possible to have a \"\n\"different database for each virtual domain. This applies to both the user\"\n\" repository and authentication repository. This allows for very \"\n\"interesting configuration such as user database sharing where each shard \"\n\"keeps users for a specific domain, or physically split data based on \"\n\"virtual domain if each domain refers to a different customer or group of \"\n\"people.\"\nmsgstr \"然而，鲜为人知的是，每个虚拟域也可以有不同的数据库。这可用于用户存储库和身份验证存储库。这也允许非常有趣的配置，例如用户数据库共享，其中每个分片保留特定域的用户，或者如果每个域指的是不同的客户或一组人，则基于虚拟域物理拆分数据。\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:8\nmsgid \"How can we do that then?\"\nmsgstr \"那我们怎么做呢？\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:10\nmsgid \"This is very easy to do through the Tigase’s configuration file.\"\nmsgstr \"这通过 Tigase 的配置文件很容易做到。\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:32\nmsgid \"\"\n\"This configuration defines just a default databases for both user \"\n\"repository and authentication repository. Default means it is used when \"\n\"there is no repository specified for a particular virtual domain. \"\n\"However, you can have a separate, both user and authentication repository\"\n\" for each virtual domain.\"\nmsgstr \"此配置仅为用户存储库和身份验证存储库定义了一个默认数据库。默认意味着当没有为特定虚拟域指定存储库时使用它。但是，对每个虚拟域您可以拥有一个单独的用户和身份验证存储库。\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:34\nmsgid \"Here is, how it works:\"\nmsgstr \"下面是它的工作原理：\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:36\nmsgid \"First, let’s define our default database for all VHosts\"\nmsgstr \"首先，让我们为所有 VHost 定义我们的默认数据库\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:58\nmsgid \"\"\n\"Now, we have VHost: domain1.com User authentication data for this VHost \"\n\"is stored in Drupal database\"\nmsgstr \"现在，我们有了 VHost: domain1.com 这个 VHost 的用户身份验证数据存储在 Drupal 数据库中\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:74\nmsgid \"All other user data is stored in Tigase’s standard database in MySQL\"\nmsgstr \"所有其他用户数据都存储在 Tigase 的 MySQL 标准数据库中\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:87\nmsgid \"\"\n\"Next VHost: domain2.com User authentication is in LDAP server but all \"\n\"other user data is stored in Tigase’s standard database\"\nmsgstr \"Next VHost: domain2.com 用户身份验证在 LDAP 服务器中，但所有其他用户数据都存储在 Tigase 的标准数据库中\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:99\nmsgid \"\"\n\"Now is something new, we have a custom authentication repository and \"\n\"separate user settings for a single domain. Please note how we define the\"\n\" VHost for which we set custom parameters\"\nmsgstr \"现在这是新事物，我们有一个自定义身份验证存储库和单个域的单独用户设置。请注意我们如何定义为其设置自定义参数的 VHost\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:109\nmsgid \"All other user data is stored in the same as default repository\"\nmsgstr \"所有其他用户数据存储在与默认存储库相同的位置\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:122\nmsgid \"When combined, the DSL output should look like this:\"\nmsgstr \"组合后，DSL 输出应如下所示：\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:142\nmsgid \"\"\n\"Next VHost: domain3.com Again user authentication is in LDAP server but \"\n\"pointing to a different LDAP server with different access credentials and\"\n\" parameters. User information is stored in a postgreSQL database.\"\nmsgstr \"\"\n\"Next VHost: domain3.com 用户身份验证再次在 LDAP 服务器中，但指向具有不同访问凭据和参数的不同 LDAP \"\n\"服务器。用户信息存储在 postgreSQL 数据库中。\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:162\nmsgid \"\"\n\"For VHost: domain4.com all the data, both authentication and user XMPP \"\n\"data are stored on a separate MySQL server with custom stored procedures \"\n\"for both user login and user logout processing.\"\nmsgstr \"\"\n\"对于 VHost: domain4.com 的所有数据，身份验证和用户 XMPP 数据都存储在单独的 MySQL \"\n\"服务器上，并带有用于用户登录和用户注销处理的自定义存储过程。\"\n\n#: ../../Tigase_Administration/Database_Management/Multiple_Databases.inc:183\nmsgid \"\"\n\"As you can see, it requires some writing but flexibility is very \"\n\"extensive and you can setup as many separate databases as you need or \"\n\"want. If one database (recognized by the database connection string) is \"\n\"shared among different VHosts, Tigase still uses a single connection \"\n\"pool, so it won’t create an excessive number of connections to the \"\n\"database.\"\nmsgstr \"\"\n\"如您所见，它需要一些编写，但灵活性非常广泛，您可以根据需要或想要设置任意数量的单独数据库。如果一个数据库（由数据库连接字符串识别）在不同的 \"\n\"VHost 之间共享，Tigase 仍然使用单个连接池，因此它不会创建过多的数据库连接。\"\n\n#: ../../Tigase_Administration/Database_Management/Importing_User_Data.inc:2\nmsgid \"Importing User Data\"\nmsgstr \"导入用户数据\"\n\n#: ../../Tigase_Administration/Database_Management/Importing_User_Data.inc:4\nmsgid \"\"\n\"You can easily copy data between Tigase compatible repositories that is \"\n\"repositories for which there is a database connector. However, it is not \"\n\"that easy to import data from an external source. Therefore a simple data\"\n\" import functionality has been added to repository utilities package.\"\nmsgstr \"\"\n\"您可以轻松地在 Tigase \"\n\"兼容存储库之间复制数据，这些存储库是具有数据库连接器的存储库。但是，从外部源导入数据并不容易。因此，一个简单的数据导入功能已添加到存储库实用程序包中。\"\n\n#: ../../Tigase_Administration/Database_Management/Importing_User_Data.inc:6\nmsgid \"\"\n\"You can access repository utilities through command ``./bin/repo.sh`` or \"\n\"``./scripts/repo.sh`` depending on whether you use a binary package or \"\n\"source distribution.\"\nmsgstr \"\"\n\"您可以通过命令 ``./bin/repo.sh`` 或 ``./scripts/repo.sh`` \"\n\"访问存储库实用程序，其取决于您使用的是二进制包还是源代码分发。\"\n\n#: ../../Tigase_Administration/Database_Management/Importing_User_Data.inc:8\nmsgid \"``-h`` parameter gives you a list of all possible parameters:\"\nmsgstr \"``-h`` 参数为您提供所有可能参数的列表：\"\n\n#: ../../Tigase_Administration/Database_Management/Importing_User_Data.inc:46\nmsgid \"\"\n\"The most critical parameters are the source repository class name and the\"\n\" initialization string. Therefore there are a few example preset \"\n\"parameters which you can use and adjust for your system. If you look \"\n\"inside the ``repo.sh`` script you can find at the end of the script \"\n\"following lines:\"\nmsgstr \"\"\n\"最关键的参数是源存储库类名和初始化字符串。因此，您可以使用一些示例预设参数并针对您的系统进行调整。如果您查看 ``repo.sh`` \"\n\"脚本，您可以在脚本末尾找到以下几行：\"\n\n#: ../../Tigase_Administration/Database_Management/Importing_User_Data.inc:56\nmsgid \"\"\n\"You can see that the source repository has been set to MySQL database \"\n\"with ``tigase`` as the database name, ``root`` the database user and \"\n\"``mypass`` the user password.\"\nmsgstr \"\"\n\"您可以看到源存储库已被设置为 MySQL 数据库，其数据库名称为 ``tigase`` ，数据库用户为 ``root`` ，用户密码为 \"\n\"``mypass``。\"\n\n#: ../../Tigase_Administration/Database_Management/Importing_User_Data.inc:58\nmsgid \"You can adjust these settings for your system.\"\nmsgstr \"您可以为您的系统调整这些设置。\"\n\n#: ../../Tigase_Administration/Database_Management/Importing_User_Data.inc:60\nmsgid \"Now to import data to your repository simply execute the command:\"\nmsgstr \"现在要将数据导入您的存储库，只需执行以下命令：\"\n\n#: ../../Tigase_Administration/Database_Management/Importing_User_Data.inc:66\nmsgid \"*Note, the import function is available from* **b895**\"\nmsgstr \"*注意，导入功能可从* **b895** *获得*\"\n\n#: ../../Tigase_Administration/Database_Management/Importing_User_Data.inc:68\nmsgid \"\"\n\"The format of the import file is very simple. This is a flat file with \"\n\"comma separated values:\"\nmsgstr \"导入文件的格式非常简单。这是一个带有逗号分隔值的平面文件：\"\n\n#: ../../Tigase_Administration/Database_Management/Importing_User_Data.inc:74\nmsgid \"\"\n\"To create such a file from MySQL database you will have to execute a \"\n\"command like this one:\"\nmsgstr \"要从 MySQL 数据库创建这样的文件，您必须执行如下命令：\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:2\nmsgid \"Importing Existing Data\"\nmsgstr \"导入现有数据\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:4\nmsgid \"Information about importing user data from other databases.\"\nmsgstr \"关于从其他数据库导入用户数据的信息。\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:7\nmsgid \"Connecting the Tigase Server to MySQL Database\"\nmsgstr \"将 Tigase 服务器连接到 MySQL 数据库\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:9\nmsgid \"\"\n\"Please before continuing reading of this manual have a look at the \"\n\":ref:`initial MySQL database setup<Prepare-the-MySQL-Database-for-the-\"\n\"Tigase-Server>`. It will help you with database preparation for \"\n\"connecting with Tigase server.\"\nmsgstr \"\"\n\"在继续阅读本手册之前，请先查看 :ref:`初始 MySQL 数据库设置<Prepare-the-MySQL-Database-for-the-\"\n\"Tigase-Server>`。它将帮助您准备用于连接 Tigase 服务器的数据库。\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:11\nmsgid \"This guide describes MySQL database connection parameters.\"\nmsgstr \"本指南介绍 MySQL 数据库连接参数。\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:13\nmsgid \"\"\n\"This guide is actually very short as there are example configuration \"\n\"files which can be used and customized for your environment.\"\nmsgstr \"本指南实际上非常短，因为有一些示例配置文件可用于您的环境并对其进行自定义。\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:29\nmsgid \"\"\n\"This is the basic setup for setting up an SQL repository for Tigase. \"\n\"dataSource contains the uri for ``default`` which is the mysql database. \"\n\"MySQL connector requires connection string in the following format: \"\n\"``jdbc:mysql://[hostname]/[database name]?user=[user name]&password=[user\"\n\" password]``\"\nmsgstr \"\"\n\"这是为 Tigase 设置 SQL 存储库的基本设置。 dataSource 包含 ``default`` 的 uri，它是 mysql 数据库。\"\n\" MySQL 连接器需要以下格式的连接字符串：``jdbc:mysql://[hostname]/[database \"\n\"name]?user=[user name]&password=[user password]``\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:31\nmsgid \"Edit the ``config.tdsl`` file for your environment.\"\nmsgstr \"为您的环境编辑 ``config.tdsl`` 文件。\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:33\nmsgid \"Start the server using following command:\"\nmsgstr \"使用以下命令启动服务器：\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:40\nmsgid \"Integrating Tigase Server with Drupal\"\nmsgstr \"将 Tigase 服务器与 Drupal 集成\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:42\nmsgid \"\"\n\"First of all, Tigase can authenticate users against a Drupal database \"\n\"which means you have the same user account for both Drupal website and \"\n\"the XMPP server. Moreover in such a configuration all account management \"\n\"is done via Drupal web interface like account creation, password change \"\n\"update user details and so on. Administrator can temporarily disable user\"\n\" account and this is followed by Tigase server too.\"\nmsgstr \"\"\n\"首先，Tigase 可以根据 Drupal 数据库对用户进行身份验证，这意味着您在 Drupal 网站和 XMPP \"\n\"服务器上拥有相同的用户帐户。此外，在这样的配置中，所有帐户管理都是通过 Drupal Web \"\n\"界面完成的，例如帐户创建，密码更改更新用户详细信息等。管理员可以暂时禁用用户帐户，随后 Tigase 服务器也可以这样做。\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:45\nmsgid \"Connecting to Drupal Database\"\nmsgstr \"连接到 Drupal 数据库\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:47\nmsgid \"\"\n\"The best way to setup Tigase with Drupal database is via the \"\n\"``config.tdsl`` file where you can put initial setting for Tigase \"\n\"configuration.\"\nmsgstr \"\"\n\"使用 Drupal 数据库设置 Tigase 的最佳方法是通过 ``config.tdsl`` 文件，您可以在其中放置 Tigase \"\n\"配置的初始设置。\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:49\nmsgid \"\"\n\"If you look in ``etc/`` directory of your Tigase installation you should \"\n\"find a the file there.\"\nmsgstr \"如果你查看 Tigase 安装的 ``etc/`` 目录，你应该会在那里找到一个文件。\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:51\nmsgid \"All you need to connect to Drupal database is set the following:\"\nmsgstr \"连接到 Drupal 数据库所需的所有设置如下：\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:67\nmsgid \"\"\n\"Typically, you will need to have drupal for authentication, and another \"\n\"for user repository. In this case, we will use SQL for user DB.\"\nmsgstr \"通常，您需要一个用于身份验证的 drupal，另一个用于用户存储库。在这种情况下，我们将对用户 DB 使用 SQL。\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:89\nmsgid \"\"\n\"In theory you can load Tigase database schema to Drupal database and then\"\n\" both ``db-uris`` would have the same database connection string. More \"\n\"details about setting up and connecting to MySQL database can be found in\"\n\" the :ref:`MySQL guide<Prepare-the-MySQL-Database-for-the-Tigase-\"\n\"Server>`.\"\nmsgstr \"\"\n\"理论上，您可以将 Tigase 数据库模式加载到 Drupal 数据库，然后两个 ``db-uris`` \"\n\"将具有相同的数据库连接字符串。有关设置和连接 MySQL 数据库的更多详细信息，请参阅 :ref:`MySQL 指南<Prepare-the-\"\n\"MySQL-Database-for-the-Tigase-Server>`。\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:91\nmsgid \"Now run the Tigase server.\"\nmsgstr \"现在运行 Tigase 服务器。\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:97\nmsgid \"\"\n\"Now you can register an account on your Drupal website and connect with \"\n\"an XMPP client using the account details.\"\nmsgstr \"现在您可以在您的 Drupal 网站上注册一个帐户，并使用帐户详细信息与 XMPP 客户端连接。\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:101\nmsgid \"\"\n\"You have to enable plain password authentication in your XMPP client to \"\n\"connect to Tigase server with Drupal database.\"\nmsgstr \"您必须在 XMPP 客户端中启用纯密码身份验证才能使 Drupal 数据库连接到 Tigase 服务器。\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:104\nmsgid \"PostgreSQL Database Use\"\nmsgstr \"PostgreSQL 数据库使用\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:106\nmsgid \"\"\n\"This guide describes how to configure Tigase server to use `PostgreSQL \"\n\"<http://www.postgresql.org/>`__ database as a user repository.\"\nmsgstr \"\"\n\"本指南介绍如何配置 Tigase 服务器以使用 `PostgreSQL <http://www.postgresql.org/>`__ \"\n\"数据库作为用户存储库。\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:108\nmsgid \"\"\n\"If you used an XML based user repository before you can copy all user \"\n\"data to PostgreSQL database using repository management tool. All steps \"\n\"are described below.\"\nmsgstr \"如果您使用基于 XML 的用户存储库，则可以使用存储库管理工具将所有用户数据复制到 PostgreSQL 数据库。所有步骤如下所述。\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:111\nmsgid \"PostgreSQL Database Preparation\"\nmsgstr \"PostgreSQL 数据库准备\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:113\nmsgid \"\"\n\"Create new database user account which will be used to connect to your \"\n\"database:\"\nmsgstr \"创建将用于连接到您的数据库的新数据库用户帐户：\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:122\nmsgid \"Now using new database user account create database for your service:\"\nmsgstr \"现在使用新的数据库用户帐户为您的服务创建数据库：\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:129\nmsgid \"Now you can load the database schema:\"\nmsgstr \"现在您可以加载数据库架构：\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:135\nmsgid \"Now the database is ready for Tigase server to use.\"\nmsgstr \"现在数据库已准备好供 Tigase 服务器使用。\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:138\nmsgid \"Server Configuration\"\nmsgstr \"服务器配置\"\n\n#: ../../Tigase_Administration/Database_Management/Existing_Databases.inc:140\nmsgid \"\"\n\"Server configuration is almost identical to MySQL database setup. The \"\n\"only difference is the connection string which usually looks like:\"\nmsgstr \"服务器配置几乎与 MySQL 数据库设置相同。唯一的区别是连接字符串通常看起来像这样：\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:2\nmsgid \"Schema Updates\"\nmsgstr \"架构更新\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:4\nmsgid \"\"\n\"This is a repository for Schema updates in case you have to upgrade from \"\n\"older installations.\"\nmsgstr \"这是架构更新的存储库，以防您必须从旧安装升级。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:6\nmsgid \"\"\n\"`Tigase Server Schema v7.1 Updates <#tigaseServer71>`__ Applies to v7.1.0\"\n\" and v8.0.0\"\nmsgstr \"\"\n\"`Tigase Server Schema v7.1 Updates <#tigaseServer71>`__ 适用于 v7.1.0 和 \"\n\"v8.0.0\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:9\nmsgid \"Changes to Schema in v8.0.0\"\nmsgstr \"v8.0.0 中对架构的更改\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:11\nmsgid \"\"\n\"For version 8.0.0 of Tigase XMPP Server, we decided to improve \"\n\"authentication and security that was provided. In order to do this, \"\n\"implementation of repository and database schemas needed to be changed to\"\n\" achieve this goal. This document, as well one in the HTTP API, will \"\n\"describe the changes to the schemas in this new version.\"\nmsgstr \"\"\n\"对于 Tigase XMPP Server 8.0.0 \"\n\"版，我们决定改进其所提供的身份验证和安全性。为此，需要更改存储库和数据库架构的实现以达到此目标。本文档以及 HTTP API \"\n\"中的文档将描述此新版本中架构的更改。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:14\nmsgid \"Reasons\"\nmsgstr \"原因\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:16\nmsgid \"\"\n\"Before version 8.0.0, user passwords were stored in plaintext in \"\n\"``user_pw`` database field within ``tig_users`` table, but in plaintext. \"\n\"It was possible to enable storage of the MD5 hash of the password \"\n\"instead, however this limited authentication mechanism SASL PLAIN only. \"\n\"However an MD5 hash of a password is not really a secure method as it is \"\n\"possible to revert this mechanism using rainbow tables.\"\nmsgstr \"\"\n\"在 8.0.0 版之前，用户密码存储在 ``tig_users`` 表中的 ``user_pw`` \"\n\"数据库字段中，但以明文形式存储。也可以将其改为启用存储密码的 MD5 哈希，但是这种有限的身份验证机制仅限 SASL PLAIN。然而，密码的 \"\n\"MD5 哈希并不是一种真正安全的方法，因为可以使用彩虹表恢复这种机制。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:18\nmsgid \"\"\n\"Therefore, we decided to change this and store only encrypted versions of\"\n\" a password in ``PBKDF2`` form which can be easily used for ``SCRAM-\"\n\"SHA-1`` authentication mechanism or ``SCRAM-SHA-256``. SASL PLAIN \"\n\"mechanism can also used these encrypted passwords. The storage of \"\n\"encrypted passwords is now enabled **by default** in v8.0.0 of Tigase.\"\nmsgstr \"\"\n\"因此，我们决定改变这一点，仅以 ``PBKDF2`` 形式存储密码的加密版本，这可以很容易地用于 ``SCRAM-SHA-1`` 身份验证机制或 \"\n\"``SCRAM-SHA-256``。 SASL PLAIN 机制也可以使用这些加密密码。现在在 Tigase v8.0.0 中 **默认情况下**\"\n\" 启用了加密密码的存储。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:21\nmsgid \"Summary of changes\"\nmsgstr \"变更摘要\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:23\nmsgid \"Added support for storage of encrypted password\"\nmsgstr \"增加了对存储加密密码的支持\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:25\nmsgid \"Passwords are no longer stored in plaintext on any database.\"\nmsgstr \"密码不再以明文形式存储在任何数据库中。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:27\nmsgid \"Using same salt for any subsequent authentications\"\nmsgstr \"对任何后续身份验证使用相同的盐\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:29\nmsgid \"\"\n\"This allows clients to reuse calculated credentials and keep them instead\"\n\" of storing plaintext passwords.\"\nmsgstr \"这允许客户端重新用计算的凭据并保留它们，而不是存储明文密码。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:31\nmsgid \"Disabled usage of stored procedure for authentication\"\nmsgstr \"禁止使用存储过程进行身份验证\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:33\nmsgid \"\"\n\"In previous versions, Tigase used stored procedures \"\n\"``TigUserLoginPlainPw`` and ``TigUserLogin`` for SASL PLAIN \"\n\"authentication. From version 8.0.0, those procedures are no longer used, \"\n\"but they are updated to use passwords stored in ``tig_user_credentials`` \"\n\"table.\"\nmsgstr \"\"\n\"在以前的版本中，Tigase 使用存储程序 ``TigUserLoginPlainPw`` 和 ``TigUserLogin`` 进行 SASL \"\n\"PLAIN 身份验证。从版本 8.0.0 开始，这些程序不再使用，但它们已被更新为使用存储在 ``tig_user_credentials`` \"\n\"表中的密码。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:35\nmsgid \"\"\n\"It is still possible to use this procedures for authentication, but to do\"\n\" that you need add:\"\nmsgstr \"仍然可以使用此程序进行身份验证，但要做到这一点，您需要添加：\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:41\nmsgid \"to configuration block of **every** authentication repository.\"\nmsgstr \"到 **every** 身份验证存储库的配置块。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:43\nmsgid \"\"\n\"To enable this for default repository, the ``authRepository`` \"\n\"configuration block will look like this:\"\nmsgstr \"要为默认存储库启用此功能，``authRepository`` 配置块将如下所示：\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:54\nmsgid \"Deprecated API\"\nmsgstr \"已弃用的 API\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:56\nmsgid \"\"\n\"Some methods of ``AuthRepository`` API were deprecated and should not be \"\n\"used. Most of them were used for authentication using stored procedures, \"\n\"retrieval of password in plaintext or for password change.\"\nmsgstr \"``AuthRepository`` API 的某些方法已被弃用，不应使用。它们中的大多数用于使用存储程序进行身份验证，以明文检索密码或更改密码。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:58\nmsgid \"\"\n\"For most of these methods, new versions based on ``tig_user_credentials``\"\n\" table and user credentials storage are provided where possible.\"\nmsgstr \"对于这些方法中的大多数，在可能的情况下提供了基于 ``tig_user_credentials`` 表和用户凭证存储的新版本。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:60\nmsgid \"Deprecated storage procedures\"\nmsgstr \"不推荐使用的存储程序\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:62\nmsgid \"\"\n\"Stored procedures for authentication and password manipulation were \"\n\"updated to a new form, so that will be possible to use them by older \"\n\"versions of Tigase XMPP Server during rolling updates of a cluster. \"\n\"However, these procedures will not be used any more and will be \"\n\"depreciated and removed in future versions of Tigase XMPP Server.\"\nmsgstr \"\"\n\"用于身份验证和密码操作的存储程序已被更新为新形式，从而旧版本的 Tigase XMPP \"\n\"服务器可以在集群滚动更新期间使用它们。但是，这些程序将不再被使用，并且将在 Tigase XMPP Server 的未来版本中不被推荐和删除。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:64\nmsgid \"Usage of MD5 hashes of passwords\"\nmsgstr \"密码的 MD5 哈希值的使用\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:66\nmsgid \"\"\n\"If you have changed ``password-encoding`` database property in previous \"\n\"versions of Tigase XMPP Server, then you will need to modify your \"\n\"configuration to keep it working. If you wish only to allow access using \"\n\"old passwords and to store changed passwords in the new form, then you \"\n\"need to enable credentials decoder for the correct authentication \"\n\"repository. In this example we will provided changes required for \"\n\"``MD5-PASSWORD`` value of ``password-encoding`` database property. If you\"\n\" have used a different one, then just replace ``MD5-PASSWORD`` with ``MD5\"\n\"-USERNAME-PASSWORD`` or ``MD5-USERID-PASSWORD``.\"\nmsgstr \"\"\n\"如果您在以前版本的 Tigase XMPP 服务器中更改了 ``password-encoding`` \"\n\"数据库属性，那么您将需要修改您的配置以使其正常工作。如果您只希望允许使用旧密码进行访问并以新形式存储更改的密码，那么您需要为正确的身份验证存储库来启用凭据解码器。在此示例中，我们将提供\"\n\" ``password-encoding`` 数据库属性的 ``MD5-PASSWORD`` 值所需的更改。如果您使用了不同值，那么只需将 \"\n\"``MD5-PASSWORD`` 替换为 ``MD5-USERNAME-PASSWORD`` 或 ``MD5-USERID-PASSWORD``。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:68\nmsgid \"**Usage of MD5 decoder.**\"\nmsgstr \"**使用 MD5 解码器。**\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:80\nmsgid \"\"\n\"If you wish to store passwords in MD5 form then use following entries in \"\n\"your configuration file:\"\nmsgstr \"如果您希望以 MD5 形式存储密码，请在配置文件中使用以下条目：\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:82\nmsgid \"**Usage of MD5 encoder.**\"\nmsgstr \"**使用 MD5 编码器。**\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:95\nmsgid \"Enabling and disabling credentials encoders/decoders\"\nmsgstr \"启用和禁用凭证编码器/解码器\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:97\nmsgid \"\"\n\"You may enable which encoders and decoders used on your installation. By \"\n\"enabling encoders/decoders you are deciding in what form the password is \"\n\"stored in the database. Those changes may impact which SASL mechanisms \"\n\"may be allowed to use on your installation.\"\nmsgstr \"\"\n\"您可以启用安装中使用的编码器和解码器。通过启用编码器/解码器，您可以决定密码以何种形式存储在数据库中。这些更改可能会影响允许在您的安装中使用哪些 \"\n\"SASL 机制。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:99\nmsgid \"**Enabling PLAIN decoder.**\"\nmsgstr \"**启用 PLAIN 解码器。**\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:111\nmsgid \"**Disabling SCRAM-SHA-1 encoder.**\"\nmsgstr \"**禁用 SCRAM-SHA-1 编码器。**\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:126\nmsgid \"\"\n\"It is strongly recommended not to disable encoders if you have enabled \"\n\"decoder of the same type as it may lead to the authentication issues, if \"\n\"client tries to use a mechanism which that is not available.\"\nmsgstr \"如果您启用了相同类型的解码器，强烈建议不要禁用编码器，因为如果客户端尝试使用不可用的机制，可能会导致身份验证问题。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:128\nmsgid \"Schema changes\"\nmsgstr \"架构更改\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:130\nmsgid \"\"\n\"This change resulted in a creation of the new table \"\n\"``tig_user_credentials`` with following fields:\"\nmsgstr \"此更改导致创建具有以下字段的新表 ``tig_user_credentials`` ：\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:133\nmsgid \"**uid**\"\nmsgstr \"**uid**\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:133\nmsgid \"id of a user row in ``tig_users``.\"\nmsgstr \"``tig_users`` 中用户行的 id。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:136\nmsgid \"**username**\"\nmsgstr \"**用户名**\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:136\nmsgid \"\"\n\"username used for authentication (if ``authzid`` is not provided or \"\n\"``authzid`` localpart is equal to ``authcid`` then row with ``default`` \"\n\"value will be used).\"\nmsgstr \"\"\n\"用于身份验证的用户名（如果未提供 ``authzid`` 或 ``authzid`` localpart 等于 \"\n\"``authcid``，则将使用具有 ``default`` 值的行）。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:139\nmsgid \"**mechanism**\"\nmsgstr \"**机制**\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:139\nmsgid \"\"\n\"name of mechanism for which this credentials will be used, ie. ``SCRAM-\"\n\"SHA-1`` or ``PLAIN``.\"\nmsgstr \"将使用此凭据的机制的名称，即 ``SCRAM-SHA-1`` 或 ``PLAIN``。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:142\nmsgid \"**value**\"\nmsgstr \"**值**\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:142\nmsgid \"serialized value required for mechanism to confirm that credentials match.\"\nmsgstr \"确认凭据匹配的机制所需的序列化值。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:146\nmsgid \"\"\n\"During execution of ``upgrade-schema`` task, passwords will be removed \"\n\"from ``tig_users`` table from ``user_pw`` field and moved to \"\n\"``tig_user_credentials`` table.\"\nmsgstr \"\"\n\"在执行 ``upgrade-schema`` 任务期间，密码将从 ``tig_users`` 表的 ``user_pw`` 字段中删除并移动到 \"\n\"``tig_user_credentials`` 表。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:148\nmsgid \"Added password reset mechanism\"\nmsgstr \"添加密码重置机制\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:150\nmsgid \"\"\n\"As a part of Tigase HTTP API component and Tigase Extras, we developed a \"\n\"mechanism which allows user to reset their password. To use this \"\n\"mechanism HTTP API component and its REST module **must** to be enabled \"\n\"on Tigase XMPP Server installation.\"\nmsgstr \"\"\n\"作为 Tigase HTTP API 组件和 Tigase Extras 的一部分，我们开发了一种允许用户重置密码的机制。要使用此机制， HTTP\"\n\" API 组件及其 REST 模块 **必须** 在Tigase XMPP 服务器安装上启用。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:154\nmsgid \"\"\n\"Additionally this mechanism need to be enabled in the configuration file.\"\n\" For more information about configuration of this mechanism please check \"\n\"Tigase HTTP API component documentation.\"\nmsgstr \"此外，需要在配置文件中启用此机制。有关此机制配置的更多信息，请查看 Tigase HTTP API 组件文档。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:156\nmsgid \"\"\n\"Assuming that HTTP API component is configured to run on port 8080 \"\n\"*(default)*, then after accessing address \"\n\"http://localhost:8080/rest/user/resetPassword in the web browser it will \"\n\"present a web form. By filling and submitting this form, the user will \"\n\"initiate a password reset process. During this process, Tigase XMPP \"\n\"Server will send an email to the user’s email address (provided during \"\n\"registration) with a link to the password change form.\"\nmsgstr \"\"\n\"假设 HTTP API 组件配置为在端口 8080 *(默认)* 上运行，那么在 Web 浏览器中访问地址 \"\n\"http://localhost:8080/rest/user/resetPassword 后，它将出现一个 Web \"\n\"表单。通过填写并提交此表格，用户将启动密码重置过程。在此过程中，Tigase XMPP \"\n\"服务器将向用户的电子邮件地址（在注册时提供）发送一封电子邮件，其中包含指向密码更改表单的链接。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:159\nmsgid \"Upgrading from v7.1.x\"\nmsgstr \"从 v7.1.x 升级\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:161\nmsgid \"\"\n\"When upgrading from previous versions of Tigase, it is recommended that \"\n\"you first backup the database. Refer to the documentation of your \"\n\"database software to find out how to export a copy. Once the backup is \"\n\"made, it will be time to run the schema upgrade. Be sure that your schema\"\n\" is up to date, and should be v7.1.0 Schema.\"\nmsgstr \"\"\n\"从以前版本的 Tigase \"\n\"升级时，建议您先备份数据库。请参阅数据库软件的文档以了解如何导出副本。备份完成后，就该运行架构升级了。确保您的架构是最新的，并且应该是 \"\n\"v7.1.0 架构。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:163\nmsgid \"To upgrade, use the new ``upgrade-schema`` task of SchemaManager:\"\nmsgstr \"要升级，请使用 SchemaManager 的 ``upgrade-schema`` 新任务：\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:165\nmsgid \"In linux\"\nmsgstr \"在linux中\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:171\nmsgid \"In Windows\"\nmsgstr \"在 Windows 中\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:177\nmsgid \"You will need to configure the following switches:\"\nmsgstr \"您将需要配置以下开关语句：\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"``-T`` Specifies Database Type\"\nmsgstr \"``-T`` 指定数据库类型\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"\"\n\"Possible values are: ``mysql``, ``derby``, ``sqlserver``, ``postgresql``,\"\n\" ``mongodb``\"\nmsgstr \"可能的值为：``mysql``, ``derby``, ``sqlserver``, ``postgresql``, ``mongodb``\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"``-D`` Specifies Databse Name\"\nmsgstr \"``-D`` 指定数据库名称\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"The explicit name of the database you wish to upgrade.\"\nmsgstr \"您要升级的数据库的显式名称。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"``-H`` Specifies Host address\"\nmsgstr \"``-H`` 指定主机地址\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"\"\n\"By default, this is localhost, but may be set to IP address or FQDNS \"\n\"address.\"\nmsgstr \"默认情况下，这是 localhost，但可以设置为 IP 地址或 FQDNS 地址。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"``-U`` Specifies Username\"\nmsgstr \"``-U`` 指定用户名\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"\"\n\"This is the username that is authorized to make changes to the database \"\n\"defined in -D.\"\nmsgstr \"这是被授权对 -D 中定义的数据库进行更改的用户名。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"``-P`` Specifies Password\"\nmsgstr \"``-P`` 指定密码\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"The password for username specified in -U.\"\nmsgstr \"-U 中指定的用户名密码。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:194\nmsgid \"``-R`` Password for Administrator or Root DB account.\"\nmsgstr \"``-R`` 管理员或 Root DB 帐户的密码。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:196\nmsgid \"``-A`` Password for Administrator or Root DB account.\"\nmsgstr \"``-A`` 管理员或 Root DB 帐户的密码。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:198\nmsgid \"``-J`` Jid of user authorized as admin user from Tigase.\"\nmsgstr \"``-J`` Tigase 授权为管理员用户的用户 Jid。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:200\nmsgid \"``-N`` Password for user specified in -J.\"\nmsgstr \"``-N`` 在 -J 中指定的用户密码。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"``-F`` Points to the file that will perform the upgrade.\"\nmsgstr \"``-F`` 指向将执行升级的文件。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc\nmsgid \"Will follow this form database/{dbname}-server-schema-8.0.0.sql\"\nmsgstr \"将遵循这种形式 database/{dbname}-server-schema-8.0.0.sql\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:206\nmsgid \"Tigase Server Schema v7.2 Updates\"\nmsgstr \"Tigase 服务器架构 v7.2 更新\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:209\nmsgid \"**FOR ALL USERS UPGRADING TO v8.0.0 FROM A v7.0.2 INSTALLATION**\"\nmsgstr \"**适用于从 v7.0.2 安装升级到 v8.0.0 的所有用户**\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:211\nmsgid \"\"\n\"The schema has changed for the main database, and the pubsub repository. \"\n\"In order to upgrade to the new schemas, you will need to do the \"\n\"following:\"\nmsgstr \"主数据库和 pubsub 存储库的架构已更改。为了升级到新模式，您需要执行以下操作：\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:213\nmsgid \"\"\n\"Upgrade the Main database schema to v7.1 using the ``database/${DB_TYPE\"\n\"}-schema-upgrade-to-7-1.sql`` file\"\nmsgstr \"使用 ``database/${DB_TYPE}-schema-upgrade-to-7-1.sql`` 文件将主数据库模式升级到 v7.1\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:215\nmsgid \"\"\n\"Upgrade the Pubsub Schema to v3.1.0 using the ``database/${DB_TYPE\"\n\"}-pubsub-schema-3.1.0.sql`` file\"\nmsgstr \"使用 ``database/${DB_TYPE}-pubsub-schema-3.1.0.sql`` 文件将 Pubsub 架构升级到 v3.1.0\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:217\nmsgid \"\"\n\"Upgrade the Pubsub Schema to v3.2.0 using the ``database/${DB_TYPE\"\n\"}-pubsub-schema-3.2.0.sql`` file\"\nmsgstr \"使用 ``database/${DB_TYPE}-pubsub-schema-3.2.0.sql`` 文件将 Pubsub 架构升级到 v3.2.0\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:219\nmsgid \"\"\n\"Upgrade the Pubsub Schema to v3.3.0 using the ``database/${DB_TYPE\"\n\"}-pubsub-schema-3.3.0.sql`` file\"\nmsgstr \"使用 ``database/${DB_TYPE}-pubsub-schema-3.3.0.sql`` 文件将 Pubsub 架构升级到 v3.3.0\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:221\nmsgid \"\"\n\"All three commands may be done at the same time in that order, it is \"\n\"suggested you make a backup of your current database to prevent data \"\n\"loss.\"\nmsgstr \"这三个命令可以按顺序同时执行，建议您对当前数据库进行备份，以防止数据丢失。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:224\nmsgid \"Tigase Schema Change for v7.1\"\nmsgstr \"v7.1 的 Tigase 架构更改\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:226\nmsgid \"\"\n\"Tigase has made changes to its database to include primary keys in the \"\n\"tig_pairs table to improve performance of the Tigase server. This is an \"\n\"auto-incremented column for Primary Key items appended to the previous \"\n\"schema.\"\nmsgstr \"\"\n\"Tigase 对其数据库进行了更改，以在 tig_pairs 表中包含主键，来提高 Tigase \"\n\"服务器的性能。这是附加到先前架构的主键项的自动递增列。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:230\nmsgid \"\"\n\"**You MUST update your database to be compliant with the new schema. If \"\n\"you do not, Tigase will not function properly.**\"\nmsgstr \"**您必须更新您的数据库以符合新架构。否则，Tigase 将无法正常工作。**\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:234\nmsgid \"*This change will affect all users of Tigase using v7.1.0 and newer.*\"\nmsgstr \"*此更改将影响所有使用 v7.1.0 及更高版本的 Tigase 用户。*\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:236\nmsgid \"\"\n\"If you are installing a new version of v8.0.0 on a new database, the \"\n\"schema should automatically be installed.\"\nmsgstr \"如果要在新数据库上安装新版本的 v8.0.0，则应自动安装架构。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:238\nmsgid \"\"\n\"First, shut down any running instances of Tigase to prevent conflicts \"\n\"with database editing. Then from command line use the DBSchemaLoader \"\n\"class to run the -schema-upgrade-to-7.1.sql file to the database. The \"\n\"command is as follows:\"\nmsgstr \"\"\n\"首先，关闭所有正在运行的 Tigase 实例，以防止与数据库编辑发生冲突。然后从命令行使用 DBSchemaLoader 类将 -schema-\"\n\"upgrade-to-7.1.sql 文件运行到数据库。命令如下：\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:240\nmsgid \"In a linux environment\"\nmsgstr \"在linux环境下\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:246\nmsgid \"In a windows environment\"\nmsgstr \"在windows环境下\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:252\nmsgid \"All variables will be required, they are as follows:\"\nmsgstr \"所有变量都是必需的，它们如下：\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:254\nmsgid \"``${HOSTNAME}`` - Hostname of the database you wish to upgrade.\"\nmsgstr \"``${HOSTNAME}`` - 您要升级的数据库的主机名。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:256\nmsgid \"``${DB_TYPE}`` - Type of database [derby, mysql, postgresql, sqlserver].\"\nmsgstr \"``${DB_TYPE}`` - 数据库类型 [derby, mysql, postgresql, sqlserver]。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:258\nmsgid \"``${ROOT_USER}`` - Username of root user.\"\nmsgstr \"``${ROOT_USER}`` - root 用户的用户名。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:260\nmsgid \"``${ROOT_USER_PASS}`` - Password of specified root user.\"\nmsgstr \"``${ROOT_USER_PASS}`` - 指定 root 用户的密码。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:262\nmsgid \"``${DB_USER}`` - Login of user that can edit database.\"\nmsgstr \"``${DB_USER}`` - 可以用来编辑数据库的用户的登录名。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:264\nmsgid \"``${DB_USER_PASS}`` - Password of the specified user.\"\nmsgstr \"``${DB_USER_PASS}`` - 指定用户的密码。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:266\nmsgid \"``${DB_NAME}`` - Name of the database to be edited.\"\nmsgstr \"``${DB_NAME}`` - 要编辑的数据库的名称。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:268\nmsgid \"``${DB_VERSION}`` - In this case, we want this to be 7.1.\"\nmsgstr \"``${DB_VERSION}`` - 在这种情况下，我们希望这是 7.1。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:270\nmsgid \"\"\n\"``${ADMIN_JID}`` - Bare JID of a database user with admin privileges. \"\n\"Must be contained within quotation marks.\"\nmsgstr \"``${ADMIN_JID}`` - 具有管理员权限的数据库用户的裸 JID。必须包含在引号内。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:272\nmsgid \"``${ADMIN_JID_PASS}`` - Password of associated admin JID.\"\nmsgstr \"``${ADMIN_JID_PASS}`` - 关联管理员 JID 的密码。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:274\nmsgid \"\"\n\"Please note that the SQL file for the update will have an associated \"\n\"database with the filename. i.e. postgresql-update-to-7.1.sql for \"\n\"postgresql database.\"\nmsgstr \"\"\n\"请注意，用于更新的 SQL 文件将有与文件名关联的数据库。即 postgresql-update-to-7.1.sql 用于 postgresql\"\n\" 数据库。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:276\nmsgid \"A finalized command will look something like this:\"\nmsgstr \"最终的命令将如下所示：\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:282\nmsgid \"\"\n\"Once this has successfully executed, you may restart you server. Watch \"\n\"logs for any db errors that may indicate an incomplete schema upgrade.\"\nmsgstr \"成功执行后，您可以重新启动服务器。观察任何可能表明架构升级不完整的数据库错误的日志。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:285\nmsgid \"Changes to Pubsub Schema\"\nmsgstr \"对 Pubsub 架构的更改\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:287\nmsgid \"\"\n\"Tigase has had a change to the PubSub Schema, to upgrade to PubSub Schema\"\n\" v7.1 without having to reform your databases, use this guide to update \"\n\"your databases to be compatible with the new version of Tigase.\"\nmsgstr \"\"\n\"Tigase 对 PubSub 架构进行了更改，要升级到 PubSub Schema v7.1 \"\n\"而无需改进您的数据库，请使用本指南更新您的数据库以使其与新版本的 Tigase 兼容。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:291\nmsgid \"\"\n\"Current PubSub Schema is v3.3.0, you will need to repeat these \"\n\"instructions for v3.1.0, v3.2.0 and then v3.3.0 before you run Tigase \"\n\"V7.1.0.\"\nmsgstr \"\"\n\"当前的 PubSub Schema 是 v3.3.0，在运行 Tigase V7.1.0 之前，您需要对 v3.1.0，v3.2.0 和 \"\n\"v3.3.0 重复这些说明。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:293\nmsgid \"\"\n\"The PubSub Schema has been streamlined for better resource use, this \"\n\"change affects all users of Tigase. To prepare your database for the new \"\n\"schema, first be sure to create a backup! Then apply the appropriate \"\n\"PubSub schema to your MySQL and it will add the new storage procedure.\"\nmsgstr \"\"\n\"PubSub Schema 已被简化以更好地利用资源，此更改会影响 Tigase \"\n\"的所有用户。要为新架构准备数据库，首先要确保创建备份！然后将适当的 PubSub 架构应用于您的 MySQL，它将添加新的存储程序。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:295\nmsgid \"\"\n\"All these files should be in your /database folder within Tigase, however\"\n\" if you are missing the appropriate files, use the links below and place \"\n\"them into that folder.\"\nmsgstr \"所有这些文件都应该在 Tigase 中的 /database 文件夹中，但是如果您缺少相应的文件，请使用下面的链接并将它们放入该文件夹中。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:297\nmsgid \"\"\n\"The MySQL schema can be found `Here <https://github.com/tigase/tigase-\"\n\"pubsub/blob/master/src/main/database/mysql-pubsub-4.1.0.sql>`__.\"\nmsgstr \"\"\n\"`这里 <https://github.com/tigase/tigase-\"\n\"pubsub/blob/master/src/main/database/mysql-pubsub-4.1.0.sql>`__ 可以找到 \"\n\"MySQL 架构。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:299\nmsgid \"\"\n\"The Derby schema can be found `Here <https://github.com/tigase/tigase-\"\n\"pubsub/blob/master/src/main/database/derby-pubsub-4.1.0.sql>`__.\"\nmsgstr \"\"\n\"`这里 <https://github.com/tigase/tigase-\"\n\"pubsub/blob/master/src/main/database/derby-pubsub-4.1.0.sql>`__ \"\n\"可以找到Derby架构。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:301\nmsgid \"\"\n\"The PostGRESQL schema can be found `Here <https://github.com/tigase\"\n\"/tigase-pubsub/blob/master/src/main/database/postgresql-\"\n\"pubsub-4.1.0.sql>`__.\"\nmsgstr \"\"\n\"`这里 <https://github.com/tigase/tigase-\"\n\"pubsub/blob/master/src/main/database/postgresql-pubsub-4.1.0.sql>`__ \"\n\"可以找到PostGRESQL 架构。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:303\nmsgid \"\"\n\"The same files are also included in all distributions of v8.0.0 in \"\n\"[tigaseroot]/database/ . All changes to database schema are meant to be \"\n\"backward compatible.\"\nmsgstr \"相同的文件也包含在 [tigaseroot]/database/ 的所有 v8.0.0 发行版中。对数据库模式的所有更改都是为了向后兼容。\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:305\nmsgid \"\"\n\"You can use a utility in Tigase to update the schema using the following \"\n\"command from the Tigase root:\"\nmsgstr \"您可以使用 Tigase 中的实用程序从 Tigase 根目录使用以下命令更新架构：\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:307\nmsgid \"Linux\"\nmsgstr \"Linux\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:313\nmsgid \"Windows:\"\nmsgstr \"Windows:\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:321\nmsgid \"\"\n\"**Some variation may be necessary depending on how your java build uses**\"\n\" ``-cp`` **option**\"\nmsgstr \"**可能需要一些变化，取决于您的 java 构建如何使用** ``-cp`` **选项**\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:323\nmsgid \"Use the following options to customize. Options in bold are required.:\"\nmsgstr \"使用以下选项进行自定义。粗体字为必填项：\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:325\nmsgid \"\"\n\"``-dbType`` database_type {derby, mysql, postgresql, sqlserver} \"\n\"(*required*)\"\nmsgstr \"``-dbType`` database_type {derby, mysql, postgresql, sqlserver} (*必需*)\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:327\nmsgid \"``-schemaVersion`` schema version {4, 5, 5-1}\"\nmsgstr \"``-schemaVersion`` 架构版本 {4, 5, 5-1}\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:329\nmsgid \"``-dbName`` database name (*required*)\"\nmsgstr \"``-dbName`` 数据库名称（*必需*）\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:331\nmsgid \"``-dbHostname`` database hostname (default is localhost)\"\nmsgstr \"``-dbHostname`` 数据库主机名（默认为 localhost）\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:333\nmsgid \"``-dbUser`` tigase username\"\nmsgstr \"``-dbUser`` tigase 用户名\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:335\nmsgid \"``-dbPass`` tigase user password\"\nmsgstr \"``-dbPass`` tigase 用户密码\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:337\nmsgid \"``-rootUser`` database root username (*required*)\"\nmsgstr \"``-rootUser`` 数据库根用户名（*必填*）\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:339\nmsgid \"``-rootPass`` database root password (*required*)\"\nmsgstr \"``-rootPass`` 数据库根密码（*必需*）\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:341\nmsgid \"``-file path`` to sql schema file (*required*)\"\nmsgstr \"``-file path`` 到 sql 架构文件（*必需*）\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:343\nmsgid \"``-query`` sql query to execute\"\nmsgstr \"``-query`` 要执行的sql查询\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:345\nmsgid \"``-logLevel`` java logger Level\"\nmsgstr \"``-logLevel`` java logger 级别\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:347\nmsgid \"``-adminJID`` comma separated list of admin JIDs\"\nmsgstr \"``-adminJID`` 逗号分隔的管理员 JID 列表\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:349\nmsgid \"``-adminJIDpass`` password (one for all entered JIDs\"\nmsgstr \"``-adminJIDpass`` 密码（一个其可用于所有输入的 JIDs ）\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:353\nmsgid \"Arguments take following precedent: query, file, whole schema\"\nmsgstr \"参数接受以下先例：查询，文件，整个架构\"\n\n#: ../../Tigase_Administration/Database_Management/Schema_Updates.inc:355\nmsgid \"As a result your final command should look something like this:\"\nmsgstr \"因此，您的最终命令应如下所示：\"\n\n#~ msgid \"\"\n#~ \"Starting with v8.0.0, most of the \"\n#~ \"database tasks have been automated and\"\n#~ \" can be called using simple text, \"\n#~ \"or using interactive question and answer\"\n#~ \" style. We **DO NOT RECOMMEND** going\"\n#~ \" through manual operation, however we \"\n#~ \"have kept manual activation of different\"\n#~ \" databases to the Appendix. If you\"\n#~ \" are interested in how we manage \"\n#~ \"and update our database schemas, you \"\n#~ \"may visit the ` Schema files \"\n#~ \"maintenance <#Schema-files-maintenance>`__ \"\n#~ \"section of our Redmine installation for\"\n#~ \" more detailed information.\"\n#~ msgstr \"\"\n\n#~ msgid \"`The DBSchemaLoader Utility <#Schema-Utility>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Hashed User Passwords in Database \"\n#~ \"<#Hashed-User-Passwords-in-Database>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Support for MongoDB <#Preparing-Tigase-for-MongoDB>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Manual installtion for MySQL <#Prepare-\"\n#~ \"the-MySQL-Database-for-the-Tigase-\"\n#~ \"Server>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Manual installtion for Derby <#Prepare-\"\n#~ \"the-Derby-Database-for-the-Tigase-\"\n#~ \"Server>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Manual installtion for SQLServer \"\n#~ \"<#Prepare-the-MS-SQL-Server-Database-\"\n#~ \"for-the-Tigase-Server>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Manual installtion for PostGRESQL \"\n#~ \"<#Prepare-the-PostgreSQL-Database-for-\"\n#~ \"the-Tigase-Server>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"In theory you can load Tigase \"\n#~ \"database schema to Drupal database and\"\n#~ \" then both ``db-uris`` would have \"\n#~ \"the same database connection string. \"\n#~ \"More details about setting up and \"\n#~ \"connecting to MySQL database can be \"\n#~ \"found in the `MySQL guide \"\n#~ \"<#prepareMysql>`__.\"\n#~ msgstr \"\"\n#~ \"理论上，您可以将 Tigase 数据库架构加载到 Drupal 数据库，然后两个 \"\n#~ \"``db-uris`` 将具有相同的数据库连接字符串。有关设置和连接 MySQL \"\n#~ \"数据库的更多详细信息，请参阅 :ref:`MySQL 指南<prepareMysql>`。\"\n\n#~ msgid \"JDBC: jTDS vs MS JDBC driver\"\n#~ msgstr \"JDBC：jTDS vs MS JDBC 驱动程序\"\n\n#~ msgid \"\"\n#~ \"Tigase XMPP Server supports two JDBC \"\n#~ \"drivers intended to be used with \"\n#~ \"Microsoft SQL Server - one created \"\n#~ \"and provided by Microsoft itself and \"\n#~ \"the alternative implementation - jTDS. \"\n#~ \"Tigase is shipped with the latter \"\n#~ \"in the distribution packages. Starting \"\n#~ \"with the version 7.1.0 we recommend \"\n#~ \"using jDTS driver that is shipped \"\n#~ \"with Tigase as JDBC driver created \"\n#~ \"by Microsoft can cause problems with \"\n#~ \"some components in cluster installations. \"\n#~ \"MS driver can be downloaded form \"\n#~ \"the website: `JDBC Drivers 4.0, 4.1 \"\n#~ \"for SQL Server <http://www.microsoft.com/en-\"\n#~ \"us/download/details.aspx?displaylang=en&id=11774>`__ then \"\n#~ \"unpack the archive. Copy \"\n#~ \"sqljdbc_4.0/enu/sqljdbc4.jar file to ${tigase-\"\n#~ \"server}/jars directory.\"\n#~ msgstr \"\"\n#~ \"Tigase XMPP Server 支持两个旨在与 Microsoft SQL\"\n#~ \" Server 一起使用的 JDBC 驱动程序 - 一个由 \"\n#~ \"Microsoft 自己创建和提供，另一个实现 - jTDS。 Tigase \"\n#~ \"在分发包中随后者一起提供。从版本 7.1.0 开始，我们建议使用 Tigase 附带的\"\n#~ \" jDTS 驱动程序，因为 Microsoft 创建的 JDBC \"\n#~ \"驱动程序可能会导致集群安装中的某些组件出现问题。 MS 驱动程序可以从网站下载：`JDBC \"\n#~ \"Drivers 4.0, 4.1 for SQL Server \"\n#~ \"<http://www.microsoft.com/en-\"\n#~ \"us/download/details.aspx?displaylang=en&id=11774>`__ 然后解压档案。将\"\n#~ \" sqljdbc_4.0/enu/sqljdbc4.jar 文件复制到 ${tigase-\"\n#~ \"server}/jars 目录。\"\n\n#~ msgid \"\"\n#~ \"Depending on the driver used ``uri`` \"\n#~ \"needs to be configured accordingly.\"\n#~ msgstr \"根据使用 ``uri`` 的驱动程序需要进行相应的配置。\"\n\n#~ msgid \"jDTS driver\"\n#~ msgstr \"jDTS 驱动\"\n\n#~ msgid \"5.6.4\"\n#~ msgstr \"5.6.4\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Administration/Licensing_Open_Source.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-05-27 12:30-0700\\n\"\n\"PO-Revision-Date: 2022-09-07 17:14+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/licensing_open_source/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:2\nmsgid \"Licensing and Open Source\"\nmsgstr \"许可和开源\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:4\nmsgid \"\"\n\"As mentioned previously, Tigase is open source under AGPLv3. If you are \"\n\"not familiar with open source software, or the environment, here are some\"\n\" frequently asked questions that might provide some answers.\"\nmsgstr \"如前所述，Tigase 在 AGPLv3 下是开源的。如果您不熟悉开源软件或环境，这里有一些\"\n\"常见问题可能会提供一些答案。\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:6\nmsgid \"**What does open source mean?**\"\nmsgstr \"**开源是什么意思？**\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:7\nmsgid \"\"\n\"This means that Tigase’s source code is available to the public to see \"\n\"how Tigase works. There are no 'black boxes' for packets where things \"\n\"just happen, everything is out in the open, whereas other companies may \"\n\"consider this propitiatory information. In addition, we have the benefit \"\n\"of many talented people working with Tigase to constantly improve Tigase \"\n\"server and related projects. These people not only include the Tigase \"\n\"development team, but other members of the community who submit code \"\n\"improvements, patches, enhancements, or other changes to Tigase.\"\nmsgstr \"\"\n\"这意味着 Tigase 的源代码可供公众使用以了解 Tigase 的工作原理。对于刚刚发生的\"\n\"事情的数据包没有“黑匣子”，一切都是公开的，而其他公司可能会参考这些信息。此外\"\n\"，我们有很多优秀的人才与 Tigase 合作，不断改进 Tigase 服务器和相关项目。\"\n\"这些人不仅包括 Tigase 开发团队，还包括向 Tigase \"\n\"提交代码改进，补丁，增强或其他更改的社区成员。\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:9\nmsgid \"**Does this mean that the binaries are open to malicious code?**\"\nmsgstr \"**这是否意味着二进制文件对恶意代码开放？**\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:10\nmsgid \"\"\n\"Although we accept patches from contributors, our repository does not \"\n\"accept them directly. Code may be submitted through our `tigase.tech \"\n\"<http://tigase.tech>`__ page and our developers will review the code \"\n\"before it is added. All builds are tested for functionality and security \"\n\"when they are built.\"\nmsgstr \"\"\n\"尽管我们接受来自他人贡献的补丁，但我们的存储库并不直接接受它们。\"\n\"代码可以通过我们的 `tigase.tech <http://tigase.tech>`__ 页面提交，我们的开发\"\n\"人员将在添加代码之前对其进行审核。所有这些在构建时都需经过功能和安全性测试。\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:12\nmsgid \"**Does this mean it is less secure?**\"\nmsgstr \"**这是否意味着它的安全性较低？**\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:13\nmsgid \"\"\n\"Not at all. Although anybody can see the source code, and know how Tigase\"\n\" works; your installation, connections, and settings are uniquely yours. \"\n\"Tigase is regularly tested and written to be as secure as possible using \"\n\"the latest encryption and secure connection protocols.\"\nmsgstr \"\"\n\"当然不是。尽管任何人都可以看到源代码，并且知道 Tigase \"\n\"是如何工作的；但是您的安装，连接和设置是您独有的。 Tigase \"\n\"使用最新的加密和安全连接协议定期测试和编写以尽可能保证安全。\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:15\nmsgid \"**Is Tigase free?**\"\nmsgstr \"** Tigase 是免费的吗？**\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:16\nmsgid \"\"\n\"Tigase is free for download and use in it’s unmodified state. Our \"\n\"commercial grade products such as Advanced Clustering Strategy is \"\n\"available for free use for testing & development.\"\nmsgstr \"\"\n\"Tigase 可以在未修改状态下免费下载和使用。我们的商业级产品（例如 Advanced \"\n\"Clustering Strategy）可免费用于测试和开发。\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:18\nmsgid \"\"\n\"**Does this mean I cannot use it in my product or commercial \"\n\"environment?**\"\nmsgstr \"**这是否意味着我不能在我的产品或商业环境中使用它？**\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:19\nmsgid \"\"\n\"Not necessarily, consult the Affero General Public License Agreement v3 \"\n\"to see if your use qualifies. Tigase is offered under commercial license \"\n\"if your use is not covered by AGPLv3.\"\nmsgstr \"\"\n\"不一定，请参阅 Affero 通用公共许可协议 v3 以查看您的使用是否符合条件。\"\n\"如果您的使用不在 AGPLv3 范围内，则将在商业许可下提供Tigase 。\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:21\nmsgid \"**Are there options for closed code or extensions?**\"\nmsgstr \"**是否有封闭代码或扩展的选项？**\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:22\nmsgid \"\"\n\"Yes! Commercial licenses can be custom made for each client, and software\"\n\" written for your company may be made private or part of our open source \"\n\"distributions at your discretion.\"\nmsgstr \"是的！商业许可证可以为每个客户定制，专为您的公司编写的软件可以由您自行决定私\"\n\"有化或成为我们开源发行版的一部分。\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:24\nmsgid \"**Can I contribute code?**\"\nmsgstr \"**我可以贡献代码吗？**\"\n\n#: ../../Tigase_Administration/Licensing_Open_Source.rst:25\nmsgid \"\"\n\"Sure! We accept code through GitHub pull-requests - submit them to one of\"\n\" our projects listed in our `GitHub organisation \"\n\"<https://github.com/tigase/>`__\"\nmsgstr \"\"\n\"当然！我们通过 GitHub 合并请求接受代码 - 将它们提交到我们的 `GitHub \"\n\"organisation<https://github.com/tigase/>` 中列出的其中一个项目\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Administration/Properties/_properties.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: 2022-09-07 17:14+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/properties_guide/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Administration/Properties/_properties.rst:2\nmsgid \"Appendix II - Properties Guide\"\nmsgstr \"附录 II - 属性指南\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:2\nmsgid \"General\"\nmsgstr \"通用\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:7\nmsgid \"admins\"\nmsgstr \"管理员\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:9\nmsgid \"**Description:** Specifies a list of administrator accounts.\"\nmsgstr \"**描述：** 指定管理员帐户列表。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:11\nmsgid \"\"\n\"**Default value:** the administration account created when the server is \"\n\"setup. Typically it would be something like ``admins = \"\n\"['admin@server.com']``.\"\nmsgstr \"**默认值：** 设置服务器时创建的管理帐户。通常它会类似于 ``admins = ['admin@server.com']``。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:13\nmsgid \"**Example:** ``admins = [ 'admin@domain.com', 'user2@domain.com' ]``\"\nmsgstr \"**示例：** ``admins = [ 'admin@domain.com', 'user2@domain.com' ]``\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:15\nmsgid \"**Possible values:** Comma seperated values of Bare JIDs.\"\nmsgstr \"**可能的值：** Bare JID的逗号分隔值。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:17\n#: ../../Tigase_Administration/Properties/_general.inc:144\n#: ../../Tigase_Administration/Properties/_general.inc:172\nmsgid \"**Available since:** 2.0.0\"\nmsgstr \"**从以下版本开始可用：** 2.0.0\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:20\nmsgid \"Certificate Container\"\nmsgstr \"证书容器\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:22\nmsgid \"\"\n\"The certificate container houses all configuration related to SSL \"\n\"certificate configuration. This container replaces a number of former — \"\n\"properties.\"\nmsgstr \"证书容器包含与 SSL 证书配置相关的所有配置。这个容器取代了许多以前的 — 属性。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:25\nmsgid \"ssl-certs-location\"\nmsgstr \"ssl-certs-location\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:27\nmsgid \"\"\n\"This option allows you to specify the location where SSL certificates are\"\n\" stored. The meaning of this property depends on the SSL container \"\n\":ref:`class implementation<sslContainerClass>`. By default it just points\"\n\" to the directory where the server SSL certificates are stored in files \"\n\"in PEM format.\"\nmsgstr \"\"\n\"此选项允许您指定存储SSL证书的位置。该属性的含义取决于SSL容器 \"\n\":ref:`类实现<sslContainerClass>`。默认情况下，它只指向服务器 \"\n\"SSL证书存储在PEM格式文件中的目录。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:29\nmsgid \"\"\n\"Default location is ``/certs`` however it can be changed using the \"\n\"following setting:\"\nmsgstr \"默认位置是 ``/certs``，但是可以使用以下设置进行更改：\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:38\nmsgid \"This replaces the former ``--ssl-certs-location`` property.\"\nmsgstr \"这取代了以前的 ``--ssl-certs-location`` 属性。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:41\nmsgid \"ssl-def-cert-domain\"\nmsgstr \"ssl-def-cert-domain\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:43\nmsgid \"\"\n\"This property allows you to specify a default alias/domain name for \"\n\"certificate. It is mostly used to load certificates for unknown domain \"\n\"names during the SSL negotiation. Unlike in TLS protocol where the domain\"\n\" name is known at the handshaking time, for SSL domain name is not known,\"\n\" therefore, the server does not know which certificate to use. Specifying\"\n\" a domain name in this property allows you to use a certificate for a \"\n\"specific domain in such case. This property value is also sometimes used \"\n\"if there is no certificate for one of virtual domains and the container \"\n\"does not automatically generate a self-signed certificate, then it can \"\n\"use a default one.\"\nmsgstr \"此属性允许您为证书指定默认别名/域名。它主要用于在SSL协商过程中加载未知域名的证书。与TLS协议中在握手时域名是已知的不同，对于SSL域名是未知的，因此服务器不知道使用哪个证书。在此属性中指定域名允许您在这种情况下使用特定域的证书。如果其中一个虚拟域没有证书并且容器不会自动生成自签名证书，则有时也会使用此属性值，那么它可以使用默认证书。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:45\nmsgid \"This may be configured as follows:\"\nmsgstr \"这可以配置如下：\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:54\nmsgid \"This replaces the former ``--ssl-def-cert-domain`` property.\"\nmsgstr \"这取代了以前的 ``--ssl-def-cert-domain`` 属性。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:57\nmsgid \"Component\"\nmsgstr \"组件\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:59\nmsgid \"\"\n\"**Description:** Container specifying component configuration. All \"\n\"components if they require configuration must be called in the conf.tdsl \"\n\"file in the following manner:\"\nmsgstr \"**描述：** 容器指定组件配置。所有需要配置的组件都必须按以下方式在conf.tdsl文件中调用：\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:67\nmsgid \"\"\n\"DSL allows for custom naming of the component, and specifying of the \"\n\"class in the same line. This method replaces the old ``comp-class`` and \"\n\"``comp-name`` style of configuration.\"\nmsgstr \"DSL允许对组件进行自定义命名，并在同一行中指定类。此方法取代了旧的 ``comp-class`` 和 ``comp-name`` 配置样式。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:69\nmsgid \"For example, what used to be\"\nmsgstr \"例如，其过去是这样\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:78\nmsgid \"is now\"\nmsgstr \"现在是\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:85\nmsgid \"\"\n\"In fact, if you are using the default class & name for a component, you \"\n\"don’t need to specify it either, so MUC in this is now called by\"\nmsgstr \"事实上，如果你使用一个组件的默认类和名称，你也不需要指定它，所以这里的MUC现在被如下调用\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:91\nmsgid \"\"\n\"**Default value:** By default, component configuration runs of default, \"\n\"and does not need to be specified.\"\nmsgstr \"**默认值：** 默认情况下，组件配置默认运行，无需指定。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:93\nmsgid \"\"\n\"There are many many configuration options under each component, which are\"\n\" specified in :ref:`component documentation<Components>`.\"\nmsgstr \"每个组件下有很多很多配置选项，具体在 :ref:`组件文档<Components>` 中指定。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:96\nmsgid \"Ports\"\nmsgstr \"端口\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:98\nmsgid \"\"\n\"The ports property is a subclass of connections, which is used to set a \"\n\"ports list for a connection manager. 'list of ports' is a comma separated\"\n\" list of ports numbers. For example for the server to server connection \"\n\"manager named s2s the property would like like the example below:\"\nmsgstr \"\"\n\"ports属性是连接的子类，用于为连接管理器设置端口列表。 'list of \"\n\"ports'是一个逗号分隔的端口号列表。例如，对于名为s2s的服务器到服务器连接管理器，该属性类似于以下示例：\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:108\nmsgid \"Each port many be individually configured underneath ports\"\nmsgstr \"每个端口都可以在端口下单独配置\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:121\nmsgid \"this replaces the ``--cmpname-ports`` property.\"\nmsgstr \"这替换了 ``--cmpname-ports`` 属性。\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:34\n#: ../../Tigase_Administration/Properties/_cluster.inc:47\n#: ../../Tigase_Administration/Properties/_cluster.inc:68\n#: ../../Tigase_Administration/Properties/_external.inc:30\n#: ../../Tigase_Administration/Properties/_general.inc:123\n#: ../../Tigase_Administration/Properties/_general.inc:203\n#: ../../Tigase_Administration/Properties/_general.inc:280\n#: ../../Tigase_Administration/Properties/_general.inc:558\n#: ../../Tigase_Administration/Properties/_general.inc:612\n#: ../../Tigase_Administration/Properties/_general.inc:631\n#: ../../Tigase_Administration/Properties/_general.inc:652\n#: ../../Tigase_Administration/Properties/_general.inc:665\n#: ../../Tigase_Administration/Properties/_performance.inc:15\n#: ../../Tigase_Administration/Properties/_performance.inc:137\n#: ../../Tigase_Administration/Properties/_repository.inc:80\n#: ../../Tigase_Administration/Properties/_repository.inc:185\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:17\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:41\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:172\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:209\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:246\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:283\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:296\n#: ../../Tigase_Administration/Properties/_vhost.inc:19\n#: ../../Tigase_Administration/Properties/_vhost.inc:34\n#: ../../Tigase_Administration/Properties/_vhost.inc:55\n#: ../../Tigase_Administration/Properties/_vhost.inc:74\n#: ../../Tigase_Administration/Properties/_vhost.inc:95\n#: ../../Tigase_Administration/Properties/_vhost.inc:116\n#: ../../Tigase_Administration/Properties/_vhost.inc:137\nmsgid \"**Available since:** 8.0.0\"\nmsgstr \"**从以下版本可用：** 8.0.0\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:126\nmsgid \"config-type\"\nmsgstr \"配置类型\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:128\nmsgid \"\"\n\"**Description:** This property sets the server type and determines what \"\n\"components are started up without needing to declare and configure all \"\n\"components. Possible values are listed below:\"\nmsgstr \"**说明：** 该属性设置服务器类型，决定启动哪些组件，且无需声明和配置所有组件。下面列出了可能的值：\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:130\nmsgid \"\"\n\"``setup`` - This setting will setup a basic server that is prepared for \"\n\"initial setup after unpacking. This is set by default, and starts up http\"\n\" component as well as basic server components. This should be changed \"\n\"after the server is configured.\"\nmsgstr \"\"\n\"``setup`` - 此设置将设置一个基本服务器，为解包后的初始设置做好准备。这是默认设置，会启动 http \"\n\"组件以及基本的服务器组件。这应该在配置服务器后更改。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:132\nmsgid \"\"\n\"``default`` - creates default configuration file. That is configuration \"\n\"which is most likely needed for a typical installation. Components \"\n\"included in configuration are: session manager, client-to-server \"\n\"connection manager and server-to-server connection manager.\"\nmsgstr \"\"\n\"``default`` - \"\n\"创建默认配置文件。这是典型安装最可能需要的配置。配置中包含的组件有：会话管理器、客户端到服务器连接管理器和服务器到服务器连接管理器。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:134\nmsgid \"\"\n\"``session-manager`` - creates configuration for instance with session \"\n\"manager and external component only. This is useful for distributed \"\n\"installation where you want to have session manager installed on separate\"\n\" machine and components managing network connections on different \"\n\"machines (one or more). Components included in configuration are: sm and \"\n\"ext2s.\"\nmsgstr \"\"\n\"``session-manager`` - \"\n\"例如仅使用会话管理器和外部组件创建配置。这对于分布式安装很有用，您希望将会话管理器安装在不同的机器上，而组件管理不同机器（一台或多台）上的网络连接。配置中包含的组件有：sm\"\n\" 和 ext2s。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:136\nmsgid \"\"\n\"``connection-managers`` - creates configuration for instance with \"\n\"components managing network connections. This is useful for distributed \"\n\"installation where you want to have session manager installed on separate\"\n\" machine and components managing network connections on different \"\n\"machines (one or more). Components included in configuration are: c2s, \"\n\"s2s, ext2s.\"\nmsgstr \"\"\n\"``connection-managers`` - \"\n\"例如使用管理网络连接的组件创建配置。这对于分布式安装很有用，您希望将会话管理器安装在不同的机器上，而组件管理不同机器（一台或多台）上的网络连接。配置中包含的组件有：c2s，s2s，ext2s。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:138\nmsgid \"\"\n\"``component`` - generating a configuration with only one component - \"\n\"component managing external components connection, either XEP-0114 or \"\n\"XEP-0225. This is used to deploy a Tigase instance as external component \"\n\"connecting to the main server. You have to add more components handled by\"\n\" this instance, usually these are MUC, PubSub or any other custom \"\n\"components. You have to configure the external component connection, \"\n\"domain name, password, port, etc…​\"\nmsgstr \"\"\n\"``component`` - 生成只有一个组件的配置 - 组件管理外部组件连接，XEP-0114 或 \"\n\"XEP-0225。这用于将Tigase实例部署为连接到主服务器的外部组件。您必须添加更多由该实例处理的组件，通常是MUC，PubSub \"\n\"或任何其他自定义组件。你必须配置外部组件连接，域名，密码，端口等…​\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:140\nmsgid \"**Default value:** ``'config-type' = 'setup'``\"\nmsgstr \"**默认值：** ``'config-type' = 'setup'``\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:142\nmsgid \"\"\n\"**Possible values:** ``setup``\\\\ \\\\|\\\\ ``default``\\\\ \\\\|\\\\ ``connection-\"\n\"managers``\\\\ \\\\|\\\\ ``session-manager``\\\\ \\\\|\\\\ ``connection-managers``\\\\ \"\n\"\\\\|\\\\ ``component``\"\nmsgstr \"\"\n\"**可能的值：** ``setup``\\\\ \\\\|\\\\ ``default``\\\\ \\\\|\\\\ ``connection-managers``\\\\\"\n\" \\\\|\\\\ ``session-manager``\\\\ \\\\|\\\\ ``connection-managers``\\\\ \\\\|\\\\ \"\n\"``component``\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:149\nmsgid \"debug-packages\"\nmsgstr \"调试包\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:151\nmsgid \"\"\n\"**Default value:** No default as Tigase does not expect custom classes \"\n\"out of the box.\"\nmsgstr \"**默认值：** 没有默认值，因为 Tigase 不希望自定义类开箱即用。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:153\nmsgid \"\"\n\"**Example:** ``'debug-packages' = [ 'com.company.CustomPlugin' , \"\n\"'com.company.custom' ]``\"\nmsgstr \"\"\n\"**示例：** ``'debug-packages' = [ 'com.company.CustomPlugin' , \"\n\"'com.company.custom' ]``\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:155\nmsgid \"**Possible values:** comma separated list of Java packages or classes.\"\nmsgstr \"**可能的值：** Java包或类的逗号分隔列表。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:157\nmsgid \"\"\n\"**Description:** This property is used to turn debugging on for any \"\n\"package not located within the default Tigase packages. Be sure class \"\n\"case is correct.\"\nmsgstr \"**描述：** 此属性用于为不在默认 Tigase 包中的任何包打开调试。确保类案例是正确的。\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:19\n#: ../../Tigase_Administration/Properties/_general.inc:159\n#: ../../Tigase_Administration/Properties/_general.inc:369\n#: ../../Tigase_Administration/Properties/_performance.inc:165\nmsgid \"**Available since:** 5.0.0\"\nmsgstr \"**从以下版本可用：** 5.0.0\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:162\nmsgid \"debug\"\nmsgstr \"调试\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:164\nmsgid \"\"\n\"**Description:** The ``debug`` property is used to turn on the debug log \"\n\"for the specified Tigase package. For example if you want to turn debug \"\n\"logs on for the ``tigase.server`` package, then you have to use the \"\n\"``server`` parameter. If you have any problems with your server the best \"\n\"way to get help from the Tigase team is to generate configuration with \"\n\"this enabled at a minimum and run the server. Then from the ``logs\"\n\"/tigase-console.log`` log file I can provide the best information for us \"\n\"to provide assistance. More details about server logging and adjusting \"\n\"logging level is described in the Debugging Tigase article in the admin \"\n\"guide. If you wish to debug packages not compiled with Tigase, use the \"\n\":ref:`debug-packages<debugPackages>` setting.\"\nmsgstr \"\"\n\"**描述：** ``debug`` 属性用于打开指定 Tigase 包的调试日志。例如，如果你想为 \"\n\"tigase.server 包打开调试日志，那么你必须使用 server \"\n\"参数。如果您的服务器有任何问题，从 Tigase \"\n\"团队获得帮助的最佳方式是生成至少启用此功能的配置并运行服务器。然后从 ``logs/\"\n\"tigase-console.log`` 日志文件中，可以为我们提供最好的信息来提供帮助。有关服务\"\n\"器日志记录和调整日志记录级别的更多详细信息，请参阅管理员指南中的调试 Tigase \"\n\"文章。如果您希望调试未使用 Tigase 编译的包，请使用 \"\n\":ref:`调试包<debugPackages>` 设置。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:166\n#: ../../Tigase_Administration/Properties/_general.inc:236\nmsgid \"**Default value:** 'none'\"\nmsgstr \"**默认值：** 'none'\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:168\nmsgid \"**Example:** ``debug = [ 'server', 'xmpp.impl' ]``\"\nmsgstr \"**示例：** ``debug = [ 'server', 'xmpp.impl' ]``\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:170\nmsgid \"**Possible values:** Comma separated list of Tigase’s package names.\"\nmsgstr \"**可能的值：** Tigase包名称的逗号分隔列表。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:175\nmsgid \"monitoring\"\nmsgstr \"监控\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:177\nmsgid \"\"\n\"**Description:** This property activates monitoring interfaces through \"\n\"selected protocols on selected TCP/IP port numbers. For more details \"\n\"please refer to the :ref:`monitoring guide<serverMonitoring>` in the user\"\n\" guide for details. Each monitoring protocol should be called in it’s own\"\n\" child bean under ``monitoring ()``. If a protocol is not specified, \"\n\"monitoring under that will not be available.\"\nmsgstr \"\"\n\"**描述：** 此属性通过选定 TCP/IP \"\n\"端口号上的选定协议激活监视接口。有关详细信息，请参阅用户指南中的 \"\n\":ref:`监控指南<serverMonitoring>`。每个监控协议都应该在 ``monitoring ()`` \"\n\"下的它自己的子 bean 中调用。如果未指定协议，则无法进行该协议下的监控。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:179\nmsgid \"**Default value:** By default monitoring is disabled.\"\nmsgstr \"**默认值：** 默认情况下禁用监控。\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:11\n#: ../../Tigase_Administration/Properties/_general.inc:181\n#: ../../Tigase_Administration/Properties/_general.inc:238\n#: ../../Tigase_Administration/Properties/_general.inc:323\n#: ../../Tigase_Administration/Properties/_general.inc:584\n#: ../../Tigase_Administration/Properties/_repository.inc:16\n#: ../../Tigase_Administration/Properties/_repository.inc:100\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:65\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:160\nmsgid \"**Example:**\"\nmsgstr \"**例子：**\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:199\nmsgid \"DO NOT CONFUSE monitoring with monitor component.\"\nmsgstr \"不要将监控与监控组件混淆。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:201\nmsgid \"**Possible values:** 'list of monitoring protocols with port numbers.'\"\nmsgstr \"**可能的值：**'list of monitoring protocols with port numbers.'\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:206\nmsgid \"plugins\"\nmsgstr \"插件\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:208\nmsgid \"\"\n\"**Description:** The former ``--sm-plugins`` property has been replaced \"\n\"by a new style of formatting with DSL. The former long unbroken string of\"\n\" plusses and minuses have been replaced by a compartmentalized style. \"\n\"Plugins controlled under session manager will now be children of the \"\n\"``'sess-man'`` bean. For example, to turn on the personal eventing \"\n\"protocol, the following may be used:\"\nmsgstr \"\"\n\"**描述：** 以前的 ``--sm-plugins`` 属性已被新的 DSL \"\n\"格式化样式所取代。以前一长串不间断的优点和缺点已被分隔的风格所取代。受会话管理器控制的插件现在将是 ``'sess-man'`` bean \"\n\"的子项。例如，要打开个人事件协议，可以使用以下内容：\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:216\nmsgid \"\"\n\"Should any plugin require configuration, those configurations will be \"\n\"under it’s own brackets. For example, this section not only turns on \"\n\"jabber:iq:auth but also sets the treads to 16.\"\nmsgstr \"如果任何插件需要配置，这些配置将在它自己的括号下。例如，此部分不仅打开了 jabber:iq:auth，而且还将线程数设置为 16。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:226\nmsgid \"\"\n\"As you may have noticed, beans or configuration options that require \"\n\"escape characters such as ``:`` or ``-`` will fall into single quotes to \"\n\"contain any special characters. If no special characters are in the bean \"\n\"name, then no single quotes are not required. If you need to disable \"\n\"certain plugins, you can do so after declaring the bean.\"\nmsgstr \"\"\n\"您可能已经注意到，需要转义字符（例如 ``:`` 或 ``-`` ）的 bean 或配置选项将落入单引号以包含任何特殊字符。如果 bean \"\n\"名称中没有特殊字符，则不需要单引号。如果你需要禁用某些插件，你可以在声明 bean 之后这样做。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:234\nmsgid \"\"\n\"Typically if a bean is called, it is automatically active. Session \"\n\"manager plugins will typically look like a list of plugins without \"\n\"configurations. The example section will show what one will look like.\"\nmsgstr \"通常，如果调用 bean，它会自动激活。会话管理器插件通常看起来像一个没有配置的插件列表。示例部分将显示其会是什么样子。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:278\nmsgid \"**Possible values:** DSL format plugins list and configurations.\"\nmsgstr \"**可能的值：** DSL格式插件列表和配置。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:283\nmsgid \"priority-queue-implementation\"\nmsgstr \"优先队列实现\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:285\nmsgid \"**Default value:** ``tigase.util.PriorityQueueRelaxed``\"\nmsgstr \"**默认值：** ``tigase.util.PriorityQueueRelaxed``\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:287\nmsgid \"\"\n\"**Example:** ``'priority-queue-implementation' = \"\n\"'tigase.util.PriorityQueueStrict``\"\nmsgstr \"\"\n\"**例子：** ``'priority-queue-implementation' = \"\n\"'tigase.util.PriorityQueueStrict``\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:289\nmsgid \"\"\n\"**Possible values:** class name extending \"\n\"``tigase.util.PriorityQueueAbstract``.\"\nmsgstr \"**可能的值：** 类名扩展 ``tigase.util.PriorityQueueAbstract``。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:291\nmsgid \"\"\n\"**Description:** The ``priority-queue-implementation`` property sets \"\n\"Tigase’s internal queue implementation. You can choose between already \"\n\"available and ready to use or you can create own queue implementation and\"\n\" let Tigase load it instead of the default one. Currently following queue\"\n\" implementations are available:\"\nmsgstr \"\"\n\"**描述：** ``priority-queue-implementation`` 属性设置 Tigase \"\n\"的内部队列实现。您可以在已经可用和准备使用之间进行选择，或者您可以创建自己的队列实现并让 Tigase \"\n\"加载它而不用默认设置。目前有以下队列实现可用：\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:293\nmsgid \"\"\n\"**tigase.util.workqueue.PriorityQueueRelaxed** - specialized priority \"\n\"queue designed to efficiently handle very high load and prevent packets \"\n\"loss for higher priority queues. This means that sometimes, under the \"\n\"system overload packets may arrive out of order in cases when they could \"\n\"have been dropped. Packets loss (drops) can typically happen for the \"\n\"lowest priority packets (presences) under a very high load.\"\nmsgstr \"\"\n\"**tigase.util.workqueue.PriorityQueueRelaxed** - \"\n\"专门的优先级队列，旨在有效处理非常高的负载并防止更高优先级队列的数据包丢失。这意味着有时候，在系统过载的情况下，数据包有可能会在被丢弃的情况下乱序到达。在非常高的负载下，优先级最低的数据包（存在）通常会发生数据包丢失（丢弃）。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:295\nmsgid \"\"\n\"**tigase.util.workqueue.PriorityQueueStrict** - specialized priority \"\n\"queue designed to efficiently handle very high load but prefers packet \"\n\"loss over packet reordering. It is suitable for systems with a very high \"\n\"load where the packets order is the critical to proper system \"\n\"functioning. This means that the packets of the same priority with the \"\n\"same source and destination address are never reordered. Packets loss \"\n\"(drops) can typically happen for all packets with the same probability, \"\n\"depending which priority queue is overloaded.\"\nmsgstr \"\"\n\"**tigase.util.workqueue.PriorityQueueStrict** - \"\n\"专门的优先级队列，旨在有效处理非常高的负载，但更喜欢丢失包而不是包重新排序。它适用于负载非常高的系统，其中数据包顺序对系统正常运行至关重要。这意味着具有相同源和目标地址的相同优先级的数据包永远不会重新排序。数据包丢失（丢弃）通常会以相同的概率发生在所有数据包上，具体将取决于哪个优先级队列过载。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:297\nmsgid \"\"\n\"**tigase.util.workqueue.NonpriorityQueue** - specialized non-priority \"\n\"queue. All packets are stored in a single physical collection, hence they\"\n\" are never reordered. Packets are not prioritized, hence system critical \"\n\"packets may have to wait for low priority packets to be processed. This \"\n\"may impact the server functioning and performance in many cases. \"\n\"Therefore this queue type should be chosen very carefully. Packets of the\"\n\" same type are never reordered. Packets loss (drops) can typically happen\"\n\" for all packets which do not fit into the single queue.\"\nmsgstr \"\"\n\"**tigase.util.workqueue.NonpriorityQueue** - \"\n\"专门的非优先级队列。所有数据包都存储在一个物理集合中，因此它们永远不会重新排序。数据包没有优先级，因此系统关键数据包可能必须等待低优先级数据包被处理。在许多情况下，这可能会影响服务器的功能和性能。因此，应该非常谨慎地选择这种队列类型。相同类型的数据包永远不会重新排序。对于不适合单个队列的所有数据包，通常会发生数据包丢失（丢弃）。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:301\nmsgid \"\"\n\"*Since the packets are processed by plugins in the SessionManager \"\n\"component and each plugin has own thread-pool with own queues packet \"\n\"reordering may happen regardless what queue type you set. The reordering \"\n\"may only happen, however between different packet types. That is \"\n\"'message' may take over 'iq' packet or 'iq' packet may take over \"\n\"'presence' packet and so on…​ This is unpredictable.*\"\nmsgstr \"\"\n\"*由于数据包由 SessionManager \"\n\"组件中的插件处理，并且每个插件都有自己的线程池和自己的队列，无论您设置什么队列类型，都可能发生数据包重新排序。重新排序可能只发生在不同的数据包类型之间。也就是说'message'可能会接管'iq'数据包或'iq'数据包可能会接管'presence'数据包等等...\"\n\" 这是不可预测的。*\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:303\n#: ../../Tigase_Administration/Properties/_performance.inc:94\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:120\nmsgid \"**Available since:** 5.1.0\"\nmsgstr \"**从以下版本开始可用：** 5.1.0\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:306\nmsgid \"roster-implementation\"\nmsgstr \"名册实现\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:308\nmsgid \"**Default value:** ``RosterFlat.class.getCanonicalName()``\"\nmsgstr \"**默认值：** ``RosterFlat.class.getCanonicalName()``\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:310\nmsgid \"**Example:** ``'roster-implementation' = 'my.pack.CustomRosterImpl'``\"\nmsgstr \"**示例：** ``'roster-implementation' = 'my.pack.CustomRosterImpl'``\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:312\nmsgid \"\"\n\"**Possible values:** Class extending \"\n\"tigase.xmpp.impl.roster.RosterAbstract.\"\nmsgstr \"**可能的值：** 扩展 tigase.xmpp.impl.roster.RosterAbstract 的类。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:314\nmsgid \"\"\n\"**Description:** The ``roster-implementation`` property allows you to \"\n\"specify a different RosterAbstract implementation. This might be useful \"\n\"for a customized roster storage, extended roster content, or in some \"\n\"cases for some custom logic for certain roster elements.\"\nmsgstr \"\"\n\"**描述：** ``roster-implementation`` 属性允许您指定不同的 RosterAbstract \"\n\"实现。这可能对自定义名册存储，扩展名册内容或在某些情况下对于某些名册元素的某些自定义逻辑很有用。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:316\n#: ../../Tigase_Administration/Properties/_general.inc:341\n#: ../../Tigase_Administration/Properties/_performance.inc:64\n#: ../../Tigase_Administration/Properties/_performance.inc:81\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:56\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:151\nmsgid \"**Available since:** 5.2.0\"\nmsgstr \"**从以下版本可用：** 5.2.0\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:319\nmsgid \"s2s-secret\"\nmsgstr \"s2s-secret\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:321\n#: ../../Tigase_Administration/Properties/_general.inc:657\nmsgid \"**Default value:** ``none``\"\nmsgstr \"**默认值：** ``无``\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:333\nmsgid \"**Possible values:** 'ascii string.'\"\nmsgstr \"**可能的值：**'ascii 字符串。'\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:335\nmsgid \"\"\n\"**Description:** This property is a global setting for s2s secrets to \"\n\"generate dialback keys on the Tigase installation. By default it is null,\"\n\" which means the secret is automatically generated for each s2s \"\n\"connection and handshake.\"\nmsgstr \"\"\n\"**描述：** 此属性是s2s机密的全局设置，用于在 Tigase \"\n\"安装上生成回拨密钥。默认为null，表示每次s2s连接和握手都会自动生成secret。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:337\nmsgid \"\"\n\"This is a global property which is overridden by settings for each VHost \"\n\"(see :ref:`Add and Manage Domains (VHosts)<addManageDomain>`)\"\nmsgstr \"这是一个全局属性，被特定 VHost 的设置覆盖（请参阅 :ref:`添加和管理域 \"\n\"(VHosts)<addManageDomain>`）。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:339\nmsgid \"\"\n\"As in the example provided, 'defaults' settings for all virtual hosts for\"\n\" which the configuration is not defined. This settings is useful mostly \"\n\"for installations with many virtual hosts listed in the init.property \"\n\"file for which there is no individual settings specified. It allows to \"\n\"configure a default values for all of them, instead of having to provide \"\n\"individual configuration for each vhost.\"\nmsgstr \"如提供的示例中所示，未定义配置的所有虚拟主机的'defaults'设置。此设置主要适用于init.property文件中列出的许多虚拟主机的安装，这些主机没有指定单独的设置。它允许为所有这些配置默认值，而不必为每个虚拟主机提供单独的配置。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:344\nmsgid \"scripts-dir\"\nmsgstr \"脚本目录\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:346\nmsgid \"**Default value:** ``scripts/admin``\"\nmsgstr \"**默认值：** ``scripts/admin``\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:348\nmsgid \"**Example:** ``'scripts-dir' = ''/opt/admin-scripts'``\"\nmsgstr \"**例子：** ``'scripts-dir' = ''/opt/admin-scripts'``\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:350\nmsgid \"**Possible values:** path to a directory on the file system.\"\nmsgstr \"**可能的值：** 文件系统上目录的路径。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:352\nmsgid \"\"\n\"**Description:** This property sets the directory where all administrator\"\n\" scripts for ad-hoc commands are stored.\"\nmsgstr \"**描述：** 此属性设置存储临时命令的所有管理员脚本的目录。\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:49\n#: ../../Tigase_Administration/Properties/_general.inc:354\n#: ../../Tigase_Administration/Properties/_performance.inc:109\n#: ../../Tigase_Administration/Properties/_performance.inc:124\nmsgid \"**Available since:** 4.3.0\"\nmsgstr \"**从以下版本可用：** 4.3.0\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:359\nmsgid \"ssl-container-class\"\nmsgstr \"ssl 容器类\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:361\nmsgid \"*Default value:** ``tigase.io.SSLContextContainer``\"\nmsgstr \"**默认值：** ``tigase.io.SSLContextContainer``\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:363\nmsgid \"\"\n\"**Example:** ``rootSslContextContainer (class: \"\n\"class.implementing.SSLContextContainerIFC) {}``\"\nmsgstr \"\"\n\"**例子：** ``rootSslContextContainer (class: \"\n\"class.implementing.SSLContextContainerIFC) {}``\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:365\nmsgid \"\"\n\"**Possible values:** a class implementing \"\n\"tigase.io.SSLContectContainerIfc.\"\nmsgstr \"**可能的值：** 实现 tigase.io.SSLContectContainerIfc 的类。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:367\nmsgid \"\"\n\"**Description:** The ``rootSslContextContainer`` property allows you to \"\n\"specify a class implementing storage for SSL/TLS certificates. The class \"\n\"presented in the example to this description allows for loading \"\n\"certificates from PEM files which is a common storage used on many \"\n\"systems.\"\nmsgstr \"\"\n\"**描述：** ``rootSslContextContainer`` 属性允许您指定一个实现 SSL/TLS \"\n\"证书存储的类。本描述示例中提供的类允许从 PEM 文件加载证书，这是许多系统上使用的通用存储。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:372\nmsgid \"stats\"\nmsgstr \"统计数据\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:374\nmsgid \"\"\n\"The stats block contains settings for statistics collection. To begin the\"\n\" stats block, use the following:\"\nmsgstr \"数据块包含统计信息收集的设置。要开始统计数据块，请使用以下命令：\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:380\nmsgid \"\"\n\"**Default value:** 'By default, stats is not listed in the \"\n\"``config.tdsl`` file'\"\nmsgstr \"**默认值：**'By default, stats is not listed in the ``config.tdsl`` file'\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:382\nmsgid \"**Description**\"\nmsgstr \"**描述**\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:384\nmsgid \"\"\n\"Tigase XMPP Server can store server statistics internally for a given \"\n\"period of time. This allows you to connect to a running system and \"\n\"collect all the server metrics along with historic data which are stored \"\n\"on the server. This is very useful when something happens on your \"\n\"production system you can connect and see when exactly this happened and \"\n\"what other metrics looked around this time. **Please be aware that Tigase\"\n\" XMPP Server produces about 1,000 different metrics of the system. \"\n\"Therefore caching large number of statistics sets requires lots of \"\n\"memory.**\"\nmsgstr \"\"\n\"Tigase XMPP Server \"\n\"可以在给定时间段内其内部存储服务器的统计信息。这允许您连接到正在运行的系统并收集所有服务器指标以及存储在服务器上的历史数据。当您的生产系统上发生某些事情时，这非常有用，您可以连接并查看这究竟是什么时候发生的，以及其这次周围的其他指标。**请注意，Tigase\"\n\" XMPP 服务器会生成大约 1,000 个不同的系统指标。因此缓存大量统计数据集需要大量内存。**\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:387\nmsgid \"stats-history-size\"\nmsgstr \"统计历史大小\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:389\nmsgid \"\"\n\"Stats-history defines the size of the history buffer. That is how many \"\n\"complete sets of historic metrics to store in memory.\"\nmsgstr \"Stats-history 定义了历史缓冲区的大小。这就是要存储在内存中的完整历史指标集的数量。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:398\nmsgid \"stats-history-interval\"\nmsgstr \"统计历史间隔\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:400\nmsgid \"Sets the interval for which statistics will be gathered from the server.\"\nmsgstr \"设置从服务器收集统计信息的时间间隔。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:410\nmsgid \"stats-logger\"\nmsgstr \"统计记录器\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:412\nmsgid \"\"\n\"Allow enabling and configuring components responsible for storing \"\n\"statistic information. Note that this controls the logging system for \"\n\"retrieving using JMX, clients, or ad-hoc commands.\"\nmsgstr \"允许启用和配置负责存储统计信息的组件。请注意，这控制了使用 JMX，客户端或 ad-hoc 命令检索的日志记录系统。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:422\nmsgid \"Currently following classes are available:\"\nmsgstr \"目前提供以下类：\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:424\nmsgid \"\"\n\"``tigase.stats.CounterDataArchivizer`` - every execution put current \"\n\"basic server metrics (CPU usage, memory usage, number of user \"\n\"connections, uptime) into database (overwrites previous entry)\"\nmsgstr \"\"\n\"``tigase.stats.CounterDataArchivizer`` - 每次执行都会将当前的基本服务器指标（CPU \"\n\"使用率，内存使用率，用户连接数，正常运行时间）放入数据库（覆盖先前的条目）\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:426\nmsgid \"\"\n\"``tigase.stats.CounterDataLogger`` - every execution insert new row with \"\n\"new set of number of server statistics (CPU usage, memory usage, number \"\n\"of user connections per connector, number of processed packets of \"\n\"different types, uptime, etc) into the database\"\nmsgstr \"\"\n\"``tigase.stats.CounterDataLogger`` - \"\n\"每次执行都会在数据库中插入新的行，其中包含一组新的服务器统计信息（CPU使用率，内存使用率，每个连接器的用户连接数，已处理的不同类型的数据包数，正常运行时间等）\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:428\nmsgid \"\"\n\"``tigase.stats.CounterDataFileLogger`` - every execution store all server\"\n\" statistics into separate file.\"\nmsgstr \"``tigase.stats.CounterDataFileLogger`` - 每次执行都将所有服务器统计信息存储到单独的文件中。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:430\n#: ../../Tigase_Administration/Properties/_general.inc:463\nmsgid \"frequency\"\nmsgstr \"频率\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:432\nmsgid \"\"\n\"stats-logger may also be controlled using frequency, which is the time \"\n\"interval between executions of the archiver ``.execute()`` method in \"\n\"seconds.\"\nmsgstr \"统计记录器也可以使用频率来控制，频率是归档器 ``.execute()`` 方法执行之间的时间间隔，以秒为单位。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:449\nmsgid \"stats-file-logger\"\nmsgstr \"统计文件记录器\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:451\nmsgid \"\"\n\"This allows configuring of statistics gathering to an external file. This\"\n\" only has one class, and may be controlled independently from the \"\n\"internal statistics.\"\nmsgstr \"这允许将统计信息收集配置到外部文件。这只有一个类，并且可以独立于内部统计来控制。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:465\nmsgid \"\"\n\"stats-file-logger may also be controlled using frequency, which is the \"\n\"time interval between executions of the archiver ``.execute()`` method in\"\n\" seconds.\"\nmsgstr \"统计文件记录器也可以使用频率来控制，频率是归档器 ``.execute()`` 方法执行之间的时间间隔，以秒为单位。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:476\nmsgid \"file configuration\"\nmsgstr \"文件配置\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:478\nmsgid \"\"\n\"You can customize the file output for stats-file-logger using the \"\n\"following setting options, these are all optional.\"\nmsgstr \"您可以使用以下设置选项自定义统计文件记录器的文件输出，这些都是可选的。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:494\nmsgid \"**'stats-datetime'** - Whether to include date & time timestamp.\"\nmsgstr \"**'stats-datetime'** - 是否包含日期和时间戳。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:496\nmsgid \"\"\n\"**'stats-datetime-format'** - Specifies the formatting of datetime \"\n\"timestamp.\"\nmsgstr \"**'stats-datetime-format'** - 指定日期时间戳的格式。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:498\nmsgid \"\"\n\"**'stats-directory'** - The directory to which the statistics file should\"\n\" be saved.\"\nmsgstr \"**'stats-directory'** - 应该保存统计文件的目录。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:500\nmsgid \"\"\n\"**'stats-filename'** - The filename prefix to name the output statistics \"\n\"file.\"\nmsgstr \"**'stats-filename'** - 命名输出统计文件的文件名前缀。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:502\nmsgid \"**'stats-level'** - Sets the level of statistics to be gathered.\"\nmsgstr \"**'stats-level'** - 设置要收集的统计信息的级别。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:504\nmsgid \"\"\n\"**'stats-unixtime'** - Control the format of the timestamp to use java \"\n\"DateFormat pattern.\"\nmsgstr \"**'stats-unixtime'** - 控制时间戳的格式以使用java DateFormat模式。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:506\nmsgid \"\"\n\"which configures accordingly: directory to which files should be saved, \"\n\"filename prefix, whether to include or not unix timestamp in filename, \"\n\"whether to include or not datetime timestamp, control format of timestamp\"\n\" (using java DateFormat pattern) and also set level of the statistics we \"\n\"want to save (using java Logger.Level)\"\nmsgstr \"\"\n\"其相应配置：文件应该保存到的目录，文件名前缀，文件名中是否包含unix时间戳，是否包含日期时间戳，时间戳控制格式（使用java \"\n\"DateFormat模式）以及设置我们要保存的统计信息的级别（使用java Logger.Level）\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:509\nmsgid \"Database logger\"\nmsgstr \"数据库记录器\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:511\nmsgid \"\"\n\"This allows configuring of statistics gathering to a database. Without \"\n\"additional configuration ``default`` data source will be used but it’s \"\n\"possible to store statistics in any database - simply define new data \"\n\"source and configure logger with it’s name.\"\nmsgstr \"\"\n\"这允许配置收集到数据库的统计信息。无需额外配置 ``default`` 数据源其将被使用，但可以在任何数据库中存储统计信息 - \"\n\"只需定义新数据源并使用其名称配置记录器。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:513\n#: ../../Tigase_Administration/Properties/_repository.inc:44\nmsgid \"**Note**\"\nmsgstr \"**注意**\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:515\nmsgid \"\"\n\"After enabling the component it’s database schema should be loaded by \"\n\"executing ``./scripts/tigase.sh upgrade-schema etc/tigase.conf`` from the\"\n\" main Tigase directory\"\nmsgstr \"\"\n\"启用组件后，应通过从Tigase主目录执行 ``./scripts/tigase.sh upgrade-schema \"\n\"etc/tigase.conf`` 来加载它的数据库架构\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:532\nmsgid \"Example configuration block\"\nmsgstr \"示例配置块\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:563\nmsgid \"stream-error-counter\"\nmsgstr \"流错误计数器\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:565\nmsgid \"\"\n\"**Description:** Add stream-error-counter to comma separated processors \"\n\"of components for which you wish to count the number of stream errors \"\n\"made. Without enabling this, statistics will return 0. This setting turns\"\n\" on stream-error-counter for both c2s and ws2s:\"\nmsgstr \"\"\n\"**描述：** 将流错误计数器添加到您希望计算流错误数的用逗号分隔的组件处理器。如果不启用此功能，统计信息将返回 \"\n\"0。此设置为c2s和ws2s打开流错误计数器：\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:580\nmsgid \"\"\n\"You may if you wish turn off stream error counters by setting ``active = \"\n\"false``.\"\nmsgstr \"如果您希望通过设置 ``active = false`` 来关闭流错误计数器，也是可以的。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:582\nmsgid \"\"\n\"**Default value:** Stream error counters are not turned on by default, \"\n\"thus no default value is set.\"\nmsgstr \"**默认值：** 流错误计数器默认不开启，因此没有设置默认值。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:593\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:97\nmsgid \"**Available since:** 7.1.0\"\nmsgstr \"**从以下版本开始可用：** 7.1.0\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:596\nmsgid \"stringprep-processor\"\nmsgstr \"stringprep-processor\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:598\nmsgid \"\"\n\"**Description:** The ``'stringprep-processor'`` property sets the \"\n\"stringprep processor for all JIDs handled by Tigase. The default 'simple'\"\n\" implementation uses regular expressions to parse and check the user JID.\"\n\" Although it does not fulfill the RFC-3920 requirements, it also puts \"\n\"much less stress on the server CPU, hence impact on the performance is \"\n\"very low.\"\nmsgstr \"\"\n\"**描述：** ``'stringprep-processor'`` 属性为Tigase处理的所有JID设置stringprep处理器。默认的 \"\n\"'simple'实现使用正则表达式来解析和检查用户JID。虽然它不满足RFC-3920的要求，但它对服务器CPU的压力也小得多，因此对性能的影响非常小。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:600\nmsgid \"Other possible values are:\"\nmsgstr \"其他可能的值是：\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:602\nmsgid \"\"\n\"``'libidn'`` - provides full stringprep processing exactly as described \"\n\"in the RFC-3920. It requires lots of CPU power and significantly impacts \"\n\"performance.\"\nmsgstr \"``'libidn'`` - 完全按照RFC-3920中的描述提供完整的stringprep处理。它需要大量的CPU功率并显著影响性能。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:604\nmsgid \"\"\n\"``'empty'`` - doesn’t do anything to JIDs. JIDs are accepted in the form \"\n\"they are received. No impact on the performance and doesn’t use any CPU. \"\n\"This is suitable for use in automated systems where JIDs are generated by\"\n\" some algorithm, hence there is no way incorrect JIDs may enter the \"\n\"system.\"\nmsgstr \"\"\n\"``'empty'`` - 对JIDs没有任何作用。 \"\n\"JIDs以收到的形式被接受。对性能没有影响，不使用任何CPU。这适用于通过某种算法生成JID的自动化系统，因此不正确的JID不可能进入系统。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:606\nmsgid \"**Default value:** ``simple``\"\nmsgstr \"**默认值：** ``simple``\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:608\nmsgid \"**Example:** ``'stringprep-processor' = 'libidn'``\"\nmsgstr \"** 示例：** ``'stringprep-processor' = 'libidn'``\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:610\nmsgid \"**Possible values:** ``simple|libidn|empty``\"\nmsgstr \"**可能的值：** ``simple|libidn|empty``\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:615\nmsgid \"test\"\nmsgstr \"测试\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:617\nmsgid \"**Default value:** By default test mode is disabled.\"\nmsgstr \"**默认值:** 默认测试模式被禁用。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:619\nmsgid \"\"\n\"**Description:** This property sets the server for test mode, which means\"\n\" that all logging is turned off, offline message storage is off, and \"\n\"possibly some other changes to the system configuration are being made.\"\nmsgstr \"**描述：** 此属性将服务器设置为测试模式，这意味着所有日志记录已关闭，离线消息存储已关闭，并且可能正在对系统配置进行一些其他更改。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:621\nmsgid \"\"\n\"The idea behind this mode is to test Tigase XMPP Server with minimal \"\n\"performance impact from environment such as hard drive, database and \"\n\"others…​\"\nmsgstr \"这种模式背后的想法是测试Tigase XMPP服务器，同时将硬盘，数据库等环境对性能的影响降到最低…..\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:623\nmsgid \"Test function has been replaced by the following setting:\"\nmsgstr \"测试功能已被以下设置取代：\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:634\nmsgid \"tls-jdk-nss-bug-workaround-active\"\nmsgstr \"tls-jdk-nss-bug-workaround-active\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:636\n#: ../../Tigase_Administration/Properties/_performance.inc:155\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:7\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:288\n#: ../../Tigase_Administration/Properties/_vhost.inc:24\n#: ../../Tigase_Administration/Properties/_vhost.inc:121\nmsgid \"**Default value:** ``false``\"\nmsgstr \"**默认值：** ``false``\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:638\nmsgid \"**Example:** ``'tls-jdk-nss-bug-workaround-active' = true``\"\nmsgstr \"**示例：** ``'tls-jdk-nss-bug-workaround-active' = true``\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:45\n#: ../../Tigase_Administration/Properties/_general.inc:640\n#: ../../Tigase_Administration/Properties/_performance.inc:159\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:11\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:292\n#: ../../Tigase_Administration/Properties/_vhost.inc:11\n#: ../../Tigase_Administration/Properties/_vhost.inc:28\n#: ../../Tigase_Administration/Properties/_vhost.inc:104\n#: ../../Tigase_Administration/Properties/_vhost.inc:125\nmsgid \"**Possible values:** ``true|false``\"\nmsgstr \"**可能的值：** ``true|false``\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:642\nmsgid \"\"\n\"**Description:** This is a workaround for TLS/SSL bug in new JDK7 using \"\n\"the native library for keys generation and connection encryption used \"\n\"with new version of nss library.\"\nmsgstr \"**描述：** 这是新JDK7中TLS/SSL错误的解决方法，使用本机库生成密钥和与新版本nss库一起使用的连接加密。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:644\nmsgid \"\"\n\"This caused a number of problems with Tigase installed on systems with \"\n\"JDK7 and the new library installed, such as hanging connections, or \"\n\"broken SSL/TLS. Our earlier suggestion was to avoid using either JDK7 or \"\n\"the problematic native library. Now we have a proper fix/workaround which\"\n\" allows you to run Tigase with JDK7.\"\nmsgstr \"这导致在安装了JDK7和新库的系统上安装Tigase时出现许多问题，例如连接挂起或SSL/TLS损坏。我们之前的建议是避免使用JDK7或有问题的本机库。现在我们有了一个适当的修复/解决方法，它允许您使用JDK7运行Tigase。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:646\nmsgid \"http://stackoverflow.com/q/10687200/427545\"\nmsgstr \"http://stackoverflow.com/q/10687200/427545\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:648\nmsgid \"http://bugs.sun.com/bugdatabase/view_bug.do;jsessionid=b509d9cb5d8164d90e6731f5fc44?bug_id=6928796\"\nmsgstr \"\"\n\"http://bugs.sun.com/bugdatabase/view_bug.\"\n\"do;jsessionid=b509d9cb5d8164d90e6731f5fc44?bug_id=6928796\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:650\nmsgid \"\"\n\"Note, while this setting is still supported, the issues mentioned above \"\n\"are fixed in v8 JDK.\"\nmsgstr \"请注意，虽然仍支持此设置，但上述问题已在v8 JDK中修复。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:655\nmsgid \"trusted\"\nmsgstr \"trusted\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:659\nmsgid \"**Example:** ``trusted = [ 'user@domain.com' , 'user-2@domain2.com' ]``\"\nmsgstr \"**例子：** ``trusted = [ 'user@domain.com' , 'user-2@domain2.com' ]``\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:661\nmsgid \"**Possible values:** comma separated list of user bare JIDs.\"\nmsgstr \"**可能的值：** 逗号分隔的用户裸JID列表。\"\n\n#: ../../Tigase_Administration/Properties/_general.inc:663\nmsgid \"\"\n\"**Description:** The ``trusted`` property allows users to specify a list \"\n\"of accounts which are considered as trusted, thus whom can perform some \"\n\"specific actions on the server. They can execute some commands, send a \"\n\"broadcast message, set MOTD and so on. The configuration is similar to \"\n\":ref:`admins<admins>` setting.\"\nmsgstr \"\"\n\"**描述：** ``trusted`` 属性允许用户指定被认为是受信任的帐户列表，从而可以在服\"\n\"务器上执行某些特定操作。他们可以执行一些命令，发送广播消息，设置MOTD等等。\"\n\"配置和 :ref:`admins<admins>` 设置类似。\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:2\nmsgid \"Repository\"\nmsgstr \"存储库\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:4\nmsgid \"\"\n\"**Description:** Container specifying authentication repository. This \"\n\"container replaces the old ``auth-db`` property types, and may contain \"\n\"some other configuration values.\"\nmsgstr \"**描述：** 容器指定身份验证存储库。这个容器替换了旧的 ``auth-db`` 属性类型，并且可能包含一些其他的配置值。\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:6\n#: ../../Tigase_Administration/Properties/_repository.inc:89\nmsgid \"**Default value:**\"\nmsgstr \"**默认值：**\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:14\nmsgid \"\"\n\"This is the basic setup for authRepository, where <configuration> \"\n\"settings are global for all authentication databases. However, you may \"\n\"configure multiple databases individually.\"\nmsgstr \"\"\n\"这是 authRepository 的基本设置，其中 <configuration> \"\n\"设置对于所有身份验证数据库都是全局的。但是，您可以单独配置多个数据库。\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:35\nmsgid \"**Configuration Values:**\"\nmsgstr \"**配置值：**\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:37\nmsgid \"Container has the following options\"\nmsgstr \"容器有以下选项\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:40\n#: ../../Tigase_Administration/Properties/_repository.inc:168\nmsgid \"pool-size\"\nmsgstr \"池大小\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:42\nmsgid \"\"\n\"This property sets the database connections pool size for the associated \"\n\"``UserRepository``.\"\nmsgstr \"此属性设置关联的 ``UserRepository`` 的数据库连接池大小。\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:46\nmsgid \"\"\n\"in some cases instead of default for this property setting for :ref\"\n\":`data-repo-pool-size<dataRepoPoolSize>` is used if pool-size is not \"\n\"defined in ``userRepository``. This depends on the repository \"\n\"implementation and the way it is initialized.\"\nmsgstr \"\"\n\"在某些情况下，如果 ``userRepository`` 中未定义池大小，则使用 :ref:`data-repo-\"\n\"pool-size<dataRepoPoolSize>` \"\n\"的属性设置而不是默认值。这取决于存储库实现及其初始化方式。\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:55\nmsgid \"\"\n\"This is a global property that may be overridden by individual repository\"\n\" settings:\"\nmsgstr \"这是一个全局属性，可能会被单独的存储库设置覆盖：\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:70\nmsgid \"**cls**\"\nmsgstr \"**cls**\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:72\nmsgid \"\"\n\"Defines the class used for repository connection. You can use this to \"\n\"specify specific drivers for different DB types.\"\nmsgstr \"定义用于存储库连接的类。您可以使用它为不同的数据库类型指定特定的驱动程序。\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:74\nmsgid \"\"\n\"Unless specified, the pool class will use the one included with Tigase. \"\n\"You may configure individual repositories in the same way. This replaces \"\n\"the former ``--auth-repo-pool`` property.\"\nmsgstr \"除非指定，否则池类将使用Tigase中包含的类。您可以以相同的方式配置各个存储库。这取代了以前的 ``--auth-repo-pool`` 属性。\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:78\nmsgid \"\"\n\"File conversion will not remove and convert this property, it **MUST BE \"\n\"DONE MANUALLY**.\"\nmsgstr \"文件转换不会删除和转换此属性，它 **必须手动完成**。\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:85\nmsgid \"authRepository\"\nmsgstr \"authRepository\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:87\nmsgid \"\"\n\"**Description:** Container specifying repository URIs. This container \"\n\"replaces the old ``auth-db-uri`` and ``user-db-uri`` property types.\"\nmsgstr \"**描述：** 容器指定存储库URIs。此容器替换了旧的 ``auth-db-uri`` 和 ``user-db-uri`` 属性类型.\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:98\nmsgid \"\"\n\"Once your configuration is setup, you will see the uri of your user \"\n\"database here. If other databases need to be defined, they will be listed\"\n\" in the same dataSource bean.\"\nmsgstr \"设置好配置后，您将在此处看到用户数据库的uri。如果需要定义其他数据库，它们将列在同一个dataSource bean中。\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:113\nmsgid \"\"\n\"**Possible values:** Broken down list of customized names for DB URIs. \"\n\"Each name must have a defined uri property. DB name can be customized by \"\n\"the bean name.\"\nmsgstr \"**可能的值：** 数据库URI的自定义名称的细分列表。每个名称都必须有一个已定义的uri属性。 数据库名称可以通过bean名称自定义。\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:117\nmsgid \"\"\n\"URI name may be used as shorthand to define DB location URI in other \"\n\"containers, so be sure to name them uniquely.\"\nmsgstr \"URI名称可以用作在其他容器中定义数据库位置URI的简写，因此请确保将它们命名为唯一。\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:121\nmsgid \"\"\n\"default () URI setting replaces the ``user-db-uri`` as well as the \"\n\"``auth-repo-uri`` property.\"\nmsgstr \"default () URI 设置替换了 ``user-db-uri`` 以及 ``auth-repo-uri`` 属性。\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:124\nmsgid \"MSSQL\"\nmsgstr \"MSSQL\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:126\nmsgid \"\"\n\"MSSql support works out of the box, however Tigase provides an open \"\n\"source driver for the database. We recommend using Microsoft’s own driver\"\n\" for better functionality.\"\nmsgstr \"MSSql 支持开箱即用，但是Tigase为数据库提供了一个开源驱动程序。我们建议使用Microsoft自己的驱动程序以获得更好的功能。\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:136\nmsgid \"\"\n\"Where the uri is divided as follows: jdbc:<driver>:sqlserver://<server \"\n\"address>;databaseName=<database name>;user=<username for \"\n\"db>;password=<password for \"\n\"db>;schema=dbo;lastUpdateCount=false;cacheMetaData=false We do not \"\n\"recommend modification of schema and onward unless you are explicit in \"\n\"your changes.\"\nmsgstr \"\"\n\"其中uri划分如下： jdbc:<driver>:sqlserver://<server \"\n\"address>;databaseName=<database name>;user=<username for \"\n\"db>;password=<password for \"\n\"db>;schema=dbo;lastUpdateCount=false;cacheMetaData=false \"\n\"除非您在更改中明确表示，否则我们不建议现在及以后修改架构。\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:139\nmsgid \"MongoDb\"\nmsgstr \"MongoDb\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:141\nmsgid \"\"\n\"For using mongoDB as the repository, the setting will look slightly \"\n\"different:\"\nmsgstr \"对于使用mongoDB作为存储库，设置看起来会略有不同：\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:152\nmsgid \"MySQL\"\nmsgstr \"MySQL\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:154\nmsgid \"\"\n\"MySQL support works out of the box, however Tigase uses prepared calls \"\n\"for calling procedures accessing data stored in database. While this \"\n\"works very fast, it takes time during Tigase XMPP Server startup to \"\n\"prepare those prepared calls. Since version 8.2.0, it is possible to \"\n\"enable workaround which will force Tigase XMPP Server to use prepared \"\n\"statements instead of prepared calls, that will improve startup time but \"\n\"may have slight impact on performance during execution of queries and \"\n\"disables startup verification checking if stored procedures and function \"\n\"in database exist and have correct parameter types. To enable this mode \"\n\"you need to set ``useCallableMysqlWorkaround`` to ``true``.\"\nmsgstr \"\"\n\"MySQL支持开箱即用，但是Tigase使用准备好的调用来调用程序访问存储在数据库中的数据。虽然这工作得非常快，但在Tigase \"\n\"XMPP服务器启动期间准备这些准备好的调用需要时间。从版本 8.2.0 开始，可以启用变通方法，强制Tigase \"\n\"XMPP服务器使用准备好的语句而不是准备好的调用，这将缩短启动时间，但可能会对执行查询期间的性能产生轻微影响，并且如果数据库中的存储过程和函数存在并且具有正确的参数类型，则会禁用启动验证检查。要启用此模式，您需要将\"\n\" ``useCallableMysqlWorkaround`` 设置为 ``true``。\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:170\nmsgid \"\"\n\"``DataSource`` is an abstraction layer between any higher level data \"\n\"access repositories such as ``userRepository`` or ``authRepository`` and \"\n\"SQL database or JDBC driver to be more specific. Many implementations use\"\n\" ``DataSource`` for DB connections and in fact on many installations they\"\n\" also share the same DataRepository instance if they connect to the same \"\n\"DB. In this case it is desired to use a specific connection pool on this \"\n\"level to an avoid excessive number of connections to the database.\"\nmsgstr \"\"\n\"`DataSource`` 是任何更高级别的数据访问存储库（例如 ``userRepository`` 或 \"\n\"``authRepository``）和更具体的SQL数据库或JDBC驱动程序之间的抽象层。许多实现使用 ``DataSource`` \"\n\"进行数据库连接，事实上，在许多安装中，如果它们连接到同一个数据库，它们也共享同一个DataRepository实例。在这种情况下，希望在此级别上使用特定的连接池，以避免与数据库的连接数量过多。\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:172\nmsgid \"\"\n\"To do so, specify the number of number of database connection as an \"\n\"integer:\"\nmsgstr \"为此，请将数据库连接数指定为整数：\"\n\n#: ../../Tigase_Administration/Properties/_repository.inc:183\nmsgid \"By default, the number of connections is 10.\"\nmsgstr \"默认情况下，连接数为 10。\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:2\nmsgid \"Cluster\"\nmsgstr \"集群\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:5\nmsgid \"cl-comp\"\nmsgstr \"cl-comp\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:7\nmsgid \"**Description:** Container specifying cluster component configuration.\"\nmsgstr \"**描述：** 容器指定集群组件配置。\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:9\nmsgid \"\"\n\"**Default value:** By default, the cl-comp container is not listed in the\"\n\" ``config.tdsl`` file.\"\nmsgstr \"**默认值：** 默认情况下， cl-comp 容器未列在 ``config.tdsl`` 文件中。\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:20\nmsgid \"connect-all\"\nmsgstr \"connect-all\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:22\nmsgid \"\"\n\"The ``cluster-connect-all`` property is used to open active connections \"\n\"to all nodes listed in the :ref:`cluster-nodes<clusterNodes>` \"\n\"configuration property. This property should be used only on the node \"\n\"which is added to the live cluster at later time. Normally this new \"\n\"cluster node is not listed in the configuration of the existing cluster \"\n\"nodes. This is why they can not open connections the new node. The new \"\n\"node opens connection to all existing nodes instead. False is the default\"\n\" value and you can skip this option if you want to have it switched off \"\n\"which it is by default.\"\nmsgstr \"\"\n\"``cluster-connect-all`` 属性用于打开在 :ref:`cluster-nodes<clusterNodes>` 配\"\n\"置属性中列出的所有节点的活动连接。此属性应仅在稍后添加到活动集群的节点上使用\"\n\"。通常，这个新的集群节点不会在现有集群节点的配置中列出。这就是为什么他们无法\"\n\"打开新节点的连接。新节点改为打开与所有现有节点的连接。 False \"\n\"是默认值，如果你想关闭它，你可以跳过这个选项，它是默认的。\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:24\nmsgid \"**Example**\"\nmsgstr \"**示例**\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:32\nmsgid \"This replaces the ``--cluster-connect-all`` property.\"\nmsgstr \"这替换了 ``--cluster-connect-all`` 属性。\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:37\nmsgid \"cluster-mode\"\nmsgstr \"cluster-mode\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:39\nmsgid \"\"\n\"**Description:** The property is used to switch cluster mode on. The \"\n\"default value is ``false`` so you can normally skip the parameter if you \"\n\"don’t want the server to run in cluster mode. You can run the server in \"\n\"the cluster mode even if there is only one node running. The performance \"\n\"impact is insignificant and you will have the opportunity to connect mode\"\n\" cluster nodes at any time without restarting the server.\"\nmsgstr \"\"\n\"**描述：** 该属性用于打开启集群模式。默认值为 \"\n\"``false``，因此如果您不希望服务器在集群模式下运行，通常可以跳过该参数。即使只有一个节点在运行，您也可以在集群模式下运行服务器。性能影响微不足道，您将有机会随时连接模式集群节点而无需重新启动服务器。\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:41\nmsgid \"\"\n\"**Default value:** ``false`` Tigase by default does not run in clustered \"\n\"mode.\"\nmsgstr \"**默认值：** ``false`` Tigase 默认不在集群模式下运行。\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:43\nmsgid \"**Example:** ``'cluster-mode' = 'true'``\"\nmsgstr \"**示例：** ``'cluster-mode' = 'true'``\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:52\nmsgid \"cluster-nodes\"\nmsgstr \"集群节点\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:54\n#: ../../Tigase_Administration/Properties/_external.inc:9\nmsgid \"**Default value:** none\"\nmsgstr \"**默认值：** 无\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:56\nmsgid \"\"\n\"**Example:** ``'cluster-nodes' = [ 'node1.domain:pass:port' , \"\n\"'node2.domain:pass:port' , 'node3.domain:pass:port' ]``\"\nmsgstr \"\"\n\"**示例：** ``'cluster-nodes' = [ 'node1.domain:pass:port' , \"\n\"'node2.domain:pass:port' , 'node3.domain:pass:port' ]``\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:58\nmsgid \"**Possible values:** a comma separated list of hostnames.\"\nmsgstr \"**可能的值：** 以逗号分隔的主机名列表。\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:60\nmsgid \"\"\n\"**Description:** The property is used to specify a list of cluster nodes \"\n\"running on your installation. The node is the full DNS name of the \"\n\"machine running the node. Please note the proper DNS configuration is \"\n\"critical for the cluster to work correctly. Make sure the 'hostname' \"\n\"command returns a full DNS name on each cluster node. Nodes don’t have to\"\n\" be in the same network although good network connectivity is also a \"\n\"critical element for an effective cluster performance.\"\nmsgstr \"\"\n\"**描述：** \"\n\"该属性用于指定在您的安装上运行的集群节点列表。节点是运行该节点的机器的完整DNS名称。请注意，正确的DNS配置对于集群正常工作至关重要。确保 \"\n\"'hostname' 命令在每个集群节点上返回完整的DNS名称。尽管良好的网络连接性也是有效集群性能的关键要素，但节点不必位于同一网络中。\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:62\nmsgid \"\"\n\"All cluster nodes must be connected with each other to maintain user \"\n\"session synchronization and exchange packets between users connected to \"\n\"different nodes. Therefore each cluster node opens a 'cluster port' on \"\n\"which it is listening for connections from different cluster nodes. As \"\n\"there is only one connection between each two nodes Tigase server has to \"\n\"decide which nodes to connect to and which has to accept the connection. \"\n\"If you put the same list of cluster nodes in the configuration for all \"\n\"nodes this is not a problem. Tigase server has a way to find and void any\"\n\" conflicts that are found. If you however want to add a new node later \"\n\"on, without restarting and changing configuration on old nodes, there is \"\n\"no way the old nodes will try to establish a connection to the new node \"\n\"they don’t know them. To solve this particular case the next parameter is\"\n\" used.\"\nmsgstr \"\"\n\"所有集群节点必须相互连接，以保持用户会话同步并在连接到不同节点的用户之间交换数据包。因此，每个集群节点都会打开一个 'cluster \"\n\"port'，在该端口上它正在侦听来自不同集群节点的连接。由于每两个节点之间只有一个连接，因此Tigase服务器必须决定连接到哪些节点以及哪些节点必须接受连接。如果您将相同的集群节点列表放入所有节点的配置中，这不是问题。\"\n\" \"\n\"Tigase服务器有一种方法可以找到并取消发现的任何冲突。但是，如果您稍后想添加一个新节点，而不是重新启动和更改旧节点上的配置，那么旧节点将无法尝试与他们不认识的新节点建立连接。为了解决这种特殊情况，使用了下一个参数。\"\n\n#: ../../Tigase_Administration/Properties/_cluster.inc:66\nmsgid \"\"\n\"Cluster nodes are not required to be configured, as they can \"\n\"automatically find/add/remove cluster nodes. This is for installations \"\n\"where nodes will be limited and static!\"\nmsgstr \"不需要配置集群节点，因为它们可以自动查找/添加/删除集群节点。这适用于节点受限且静态的安装！\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:2\nmsgid \"User connectivity\"\nmsgstr \"用户连接\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:5\nmsgid \"bosh-close-connection\"\nmsgstr \"bosh-close-connection\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:9\nmsgid \"**Example:** ``'bosh-close-connection' = true``\"\nmsgstr \"**示例：** ``'bosh-close-connection' = true``\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:13\nmsgid \"\"\n\"**Description:** This property globally disables Bosh keep-alive support \"\n\"for Tigase server. It causes the Bosh connection manager to force close \"\n\"the HTTP connection each time data is sent to the Bosh client. To \"\n\"continue communication the client must open a new HTTP connection.\"\nmsgstr \"\"\n\"**描述：** \"\n\"此属性全局禁用Bosh对Tigase服务器的保持活动支持。每次将数据发送到Bosh客户端时，它都会导致Bosh连接管理器强制关闭HTTP连接。要继续通信，客户端必须打开一个新的HTTP连接。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:15\nmsgid \"\"\n\"This setting is rarely required but on installations where the client \"\n\"cannot control/disable keep-alive Bosh connections and keep-alive does \"\n\"not work correctly for some reason.\"\nmsgstr \"此设置很少需要，但在客户端无法控制/禁用保持活动Bosh连接的安装中，由于某种原因，keep-alive 无法正常工作。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:20\nmsgid \"bosh-extra-headers-file\"\nmsgstr \"bosh-extra-headers-file\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:22\nmsgid \"**Default value:** ``'etc/bosh-extra-headers.txt'``\"\nmsgstr \"**默认值：** ``'etc/bosh-extra-headers.txt'``\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:24\nmsgid \"**Example:** ``'bosh-extra-headers-file' = ''/path/to/file.txt'``\"\nmsgstr \"**示例：** ``'bosh-extra-headers-file' = ''/path/to/file.txt'``\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:26\nmsgid \"**Possible values:** 'path to a file on the filesystem.'\"\nmsgstr \"**可能的值：** 'path to a file on the filesystem.'\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:28\nmsgid \"\"\n\"**Description:** This property allows you to specify a path to a text \"\n\"file with additional HTTP headers which will be sent to a Bosh client \"\n\"with each request. This gives some extra flexibility for Bosh clients \"\n\"running on some systems with special requirements for the HTTP headers \"\n\"and some additional settings.\"\nmsgstr \"\"\n\"**描述：** \"\n\"此属性允许您指定带有附加HTTP标头的文本文件的路径，这些标头将随每个请求一起发送到Bosh客户端。这为在某些对HTTP标头和一些附加设置有特殊要求的系统上运行的Bosh客户端提供了一些额外的灵活性。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:30\nmsgid \"\"\n\"By default a file distributed with the installation contains following \"\n\"content:\"\nmsgstr \"默认情况下，随安装分发的文件包含以下内容：\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:39\nmsgid \"\"\n\"This can be modified, removed or replaced with a different content on \"\n\"your installation.\"\nmsgstr \"这可以在您的安装中修改，删除或替换为不同的内容。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:44\nmsgid \"client-access-policy-file\"\nmsgstr \"client-access-policy-file\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:46\nmsgid \"**Default value:** ``etc/client-access-policy.xml``\"\nmsgstr \"**默认值：** ``etc/client-access-policy.xml``\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:48\nmsgid \"\"\n\"**Example:** ``'client-access-policy-file' = ''/path/to/access-policy-\"\n\"file.xml'``\"\nmsgstr \"\"\n\"**示例：** ``'client-access-policy-file' = ''/path/to/access-policy-\"\n\"file.xml'``\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:50\nmsgid \"**Possible values:** path to a file on the filesystem.\"\nmsgstr \"**可能的值：** 文件系统上文件的路径。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:52\nmsgid \"\"\n\"**Description:** The ``client-access-policy-file`` property allows \"\n\"control of the cross domain access policy for Silverlight based web \"\n\"applications. The cross domain policy is controlled via XML file which \"\n\"contains the policy and rules.\"\nmsgstr \"\"\n\"**描述：** ``client-access-policy-file`` \"\n\"属性允许控制基于Silverlight的Web应用程序的跨域访问策略。跨域策略通过包含策略和规则的XML文件进行控制。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:54\nmsgid \"\"\n\"By default Tigase is distributed with an example policy file which allows\"\n\" for full access from all sources to the whole installation. This is \"\n\"generally okay for most Bosh server installations. The configuration \"\n\"through the property and XML files allows for a very easy and flexible \"\n\"modification of the policy on any installation.\"\nmsgstr \"默认情况下，Tigase与一个示例策略文件一起分发，该文件允许从所有来源完全访问整个安装。对于大多数Bosh服务器安装来说，这通常是可以的。通过属性和XML文件进行的配置允许在任何安装上轻松灵活地修改策略。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:59\nmsgid \"client-port-delay-listening\"\nmsgstr \"client-port-delay-listening\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:61\nmsgid \"\"\n\"**Description:** The property allows to enabled or disable delaying of \"\n\"listening for client connections **in cluster mode** until the cluster is\"\n\" correctly connected.\"\nmsgstr \"**描述：** 该属性允许 **在集群模式下** 启用或禁用延迟监听客户端连接 ，直到集群正确连接。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:63\n#: ../../Tigase_Administration/Properties/_vhost.inc:7\n#: ../../Tigase_Administration/Properties/_vhost.inc:100\nmsgid \"**Default value:** ``true``\"\nmsgstr \"**默认值：** ``true``\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:73\nmsgid \"**Possible values:** ``true``, ``false``\"\nmsgstr \"**可能的值：** ``true``, ``false``\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:75\nmsgid \"\"\n\"In cluster mode, in order to ensure correct user status broadcast, we are\"\n\" delaying opening client ports (components: ``c2s``, ``ws2s``, ``bosh``) \"\n\"and enable those only after cluster is fully and correctly connected \"\n\"(i.e. either there is only single node or in case of multiple nodes all \"\n\"nodes connected correctly).\"\nmsgstr \"在集群模式下，为了确保正确的用户状态广播，我们延迟打开客户端端口（组件：``c2s``，``ws2s``，``bosh``），并且仅在集群完全正确连接后启用（即要么只有单个节点，要么在多个节点的情况下所有节点都正确连接）。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:77\nmsgid \"\"\n\"It’s possible to enable/disable this on per-component basis with the \"\n\"following configuration:\"\nmsgstr \"可以使用以下配置在每个组件的基础上启用/禁用此功能：\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:91\nmsgid \"\"\n\"Maximum delay time depends on the component and it’s multiplication of \"\n\"``ConnectionManager`` default connection delay times ``30s`` - in case of\"\n\" client connection manager this delay equals 60s.\"\nmsgstr \"\"\n\"最大延迟时间取决于组件，它是 ``ConnectionManager`` 默认连接延迟时间和 ``30s`` 的乘积 - \"\n\"在客户端连接管理器的情况下，此延迟等于 60s。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:95\nmsgid \"Only applicable if **Cluster Mode** is active!\"\nmsgstr \"仅当 **集群模式** 处于活动状态时才适用！\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:100\nmsgid \"cross-domain-policy-file\"\nmsgstr \"cross-domain-policy-file\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:102\nmsgid \"**Default value:** ``etc/cross-domain-policy.xml``\"\nmsgstr \"**默认值：** ``etc/cross-domain-policy.xml``\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:104\nmsgid \"\"\n\"**Example:** ``'cross-domain-policy-file' = ''/path/to/cross-domain-\"\n\"policy.xml'``\"\nmsgstr \"\"\n\"**示例：** ``'cross-domain-policy-file' = ''/path/to/cross-domain-\"\n\"policy.xml'``\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:106\nmsgid \"**Possible values:** path to a file on the file system.\"\nmsgstr \"**可能的值：** 文件系统上文件的路径。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:108\nmsgid \"\"\n\"**Description:** This property allows you to set a path to a file with \"\n\"cross domain access policy for flash based clients. This is a standard \"\n\"XML file which is sent to the flash client upon request.\"\nmsgstr \"**描述：** 此属性允许您为基于闪存的客户端设置具有跨域访问策略的文件的路径。这是一个标准XML文件，根据请求发送到Flash客户端。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:110\nmsgid \"\"\n\"A default file distributed with Tigase installations allows for full \"\n\"access for all. This is good enough for most use cases but it can be \"\n\"changed by simply editing the file.\"\nmsgstr \"随着Tigase安装分发的默认文件允许所有人完全访问。这对于大多数用例来说已经足够了，但可以通过简单地编辑文件来更改它。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:112\nmsgid \"\"\n\"This is a global property that can also be overridden by configuring \"\n\"connection managers [ c2s, s2s, ws2s, bosh, ext, etc] and they may all \"\n\"have their own policies.\"\nmsgstr \"这是一个全局属性，也可以通过配置连接管理器 [c2s，s2s，ws2s，bosh，ext 等] 来覆盖，它们可能都有自己的策略。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:123\nmsgid \"domain-filter-policy\"\nmsgstr \"domain-filter-policy\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:125\nmsgid \"**Default value:** ``ALL``\"\nmsgstr \"**默认值：** ``ALL``\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:127\nmsgid \"**Example:** ``domain-filter-policy' = 'LOCAL``\"\nmsgstr \"**示例：** ``domain-filter-policy' = 'LOCAL``\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:129\nmsgid \"\"\n\"**Possible values:** \"\n\"``ALL|LOCAL|OWN|BLOCK|LIST=domain1;domain2|BLACKLIST=domain1;domain2``\"\nmsgstr \"\"\n\"**可能的值：** \"\n\"``ALL|LOCAL|OWN|BLOCK|LIST=domain1;domain2|BLACKLIST=domain1;domain2``\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:131\nmsgid \"\"\n\"**Description:** The ``domain-filter-policy`` property is a global \"\n\"setting for setting communication filtering for vhosts. This function is \"\n\"kind of an extension of the same property which could be set on a single \"\n\"user level. However, in many cases it is desired to control users \"\n\"communication not on per user-level but on the domain level. Domain \"\n\"filtering (communication filtering) allows you to specify with whom users\"\n\" can communicate for a particular domain. It enables restriction of \"\n\"communications for a selected domain or for the entire installation. A \"\n\"default value ``ALL`` renders users for the domain (by default for all \"\n\"domains) able to communicate with any user on any other domains. Other \"\n\"possible values are:\"\nmsgstr \"\"\n\"**描述：** ``domain-filter-policy`` \"\n\"属性是用于设置虚拟主机通信过滤的全局设置。此功能是可以在单个用户级别上设置的相同属性的一种扩展。然而，在许多情况下，希望不是在每个用户级别上而是在域级别上控制用户通信。域过滤（通信过滤）允许您指定特定域的用户可以与谁通信。它可以限制选定域或整个安装的通信。默认值\"\n\" ``ALL`` 使域的用户（默认情况下所有域）能够与任何其他域上的任何用户进行通信。其他可能的值是：\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:133\nmsgid \"\"\n\"``ALL`` a default value allowing users to communicate with anybody on any\"\n\" other domain, including external servers.\"\nmsgstr \"``ALL`` 允许用户与任何其他域上的任何人（包括外部服务器）进行通信的默认值。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:135\nmsgid \"\"\n\"``LOCAL`` allows users to communicate with all users on the same \"\n\"installation on any domain. It only blocks communication with external \"\n\"servers.\"\nmsgstr \"``LOCAL`` 允许用户与任何域上相同安装的所有用户进行通信。它只阻止与外部服务器的通信。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:137\nmsgid \"\"\n\"``OWN`` allows users to communicate with all other users on the same \"\n\"domain. Plus it allows users to communicate with subdomains such as \"\n\"**muc.domain**, **pubsub.domain**, etc…\"\nmsgstr \"\"\n\"``OWN`` 允许用户与同一域中的所有其他用户进行通信。此外，它还允许用户与 **muc.domain**，**pubsub.domain** \"\n\"等子域进行通信…\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:139\nmsgid \"\"\n\"``BLOCK`` value completely blocks communication for the domain or for the\"\n\" user with anybody else. This could be used as a means to temporarily \"\n\"disable account or domain.\"\nmsgstr \"``BLOCK`` 此值完全阻止域或用户与其他任何人的通信。这可以用作暂时禁用帐户或域的一种手段。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:141\nmsgid \"\"\n\"``LIST`` property allows to set a list of domains (users' JIDs) with \"\n\"which users on the domain can communicate (i.e. *whitelist*).\"\nmsgstr \"``LIST`` 属性允许设置域中的用户可以与之通信的域列表（用户的JID）（即 *白名单*）。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:143\nmsgid \"\"\n\"``BLACKLIST`` - user can communicate with everybody (like ``ALL``), \"\n\"except contacts on listed domains.\"\nmsgstr \"``BLACKLIST`` - 用户可以与所有人（如 ``ALL``）通信，除了列出域上的联系人。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:145\nmsgid \"\"\n\"This is a global property which is overridden by settings for particular \"\n\"VHosts (see :ref:`Add and Manage Domains(VHosts)<addManageDomain>`).\"\nmsgstr \"这是一个全局属性，被特定 VHost 的设置覆盖（请参阅 :ref:`添加和管理域 \"\n\"(VHosts)<addManageDomain>`）。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:147\nmsgid \"\"\n\"A default settings for all virtual hosts for which the configuration is \"\n\"not defined. This settings is useful mostly for installations with many \"\n\"virtual hosts listed in the init.property file for which there is no \"\n\"individual settings specified. It allows default value for all of \"\n\"servers, instead of having to provide individual configuration for each \"\n\"vhost.\"\nmsgstr \"\"\n\"未定义配置的所有虚拟主机的默认设置。此设置主要用于具有许多在 init.property \"\n\"文件中列出的虚拟主机的安装，这些主机没有指定单独的设置。它允许所有服务器的默认值，而不必为每个虚拟主机提供单独的配置。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:149\nmsgid \"\"\n\"``ALL`` is also applied as a default value for all new vhosts added at \"\n\"run-time.\"\nmsgstr \"``ALL`` 也可以作为在运行时添加的所有新虚拟主机的默认值。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:154\nmsgid \"see-other-host\"\nmsgstr \"see-other-host\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:156\nmsgid \"\"\n\"--cmSeeOtherHost has been replaced with using ``seeOtherHost`` setting, \"\n\"and can be configured for each connection manager (c2s, s2s, etc..)\"\nmsgstr \"--cmSeeOtherHost已被替换为使用 ``seeOtherHost`` 设置，并且可以为每个连接管理器（c2s，s2s 等...）配置。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:158\nmsgid \"**Default value:** ``tigase.server.xmppclient.SeeOtherHostHashed``\"\nmsgstr \"**默认值：** ``tigase.server.xmppclient.SeeOtherHostHashed``\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:168\nmsgid \"**Possible values:** 'none' 'or class implementing SeeOtherHostIfc.'\"\nmsgstr \"**可能的值：**'none'或实现'SeeOtherHostIfc'的类。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:170\nmsgid \"\"\n\"**Description:** Allows you to specify a load balancing mechanism by \"\n\"specifying SeeOtherHostIfc implementation. More details about \"\n\"functionality and implementation details can be found in Tigase Load \"\n\"Balancing documentation.\"\nmsgstr \"\"\n\"**描述：** \"\n\"允许您通过指定SeeOtherHostIfc实现来指定加载平衡机制。关于功能和实现细节的更多细节可以在Tigase加载平衡文档中找到。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:177\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:207\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:281\nmsgid \"watchdog_timeout\"\nmsgstr \"监视器超时\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:179\nmsgid \"**Default value:** ``1740000``\"\nmsgstr \"**默认值：** ``1740000``\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:181\nmsgid \"**Example:** ``watchdog_timeout=60000``\"\nmsgstr \"**示例：** ``watchdog_timeout=60000``\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:183\nmsgid \"**Possible values:** ``any integer.``\"\nmsgstr \"**可能的值：** ``any integer``。\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:185\nmsgid \"\"\n\"**Description:** The ``watchdog_timeout`` property allows for fine-tuning\"\n\" ConnectionManager Watchdog (service responsible for detecting broken \"\n\"connections and closing them). Timeout property relates to the amount of \"\n\"time (in miliseconds) after which lack of response/activity on a given \"\n\"connection will considered such connection as broken an close it. In \"\n\"addition to global configuration presented above a per component \"\n\"configuration is possible:\"\nmsgstr \"\"\n\"**描述：** ``watchdog_timeout`` \"\n\"属性允许微调ConnectionManager监视器（这是负责检测断开连接并关闭它们的服务）。超时属性与时间长短（以毫秒为单位）有关，之后，在给定连接上缺乏响应/活动将认为这种连接断开并关闭它。除了上面介绍的全局配置之外，每个组件的配置来实现也是可能的：\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:193\nmsgid \"for example (for C2SConnectionManager):\"\nmsgstr \"例如（对于 C2SConnectionManager）：\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:201\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:238\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:275\nmsgid \"All related configuration options:\"\nmsgstr \"所有相关的配置选项：\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:203\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:240\nmsgid \":ref:`watchdog_Ping_Type<watchdogPingType>`\"\nmsgstr \":ref:`监视器的ping类型<watchdogPingType>`\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:205\nmsgid \":ref:`watchdog_delay<watchdogDelay>`\"\nmsgstr \":ref:`监视器延迟<watchdogDelay>`\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:214\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:242\nmsgid \"watchdog_delay\"\nmsgstr \"监视器延迟\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:216\nmsgid \"**Default value:** ``600000``\"\nmsgstr \"**默认值：** ``600000``\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:218\nmsgid \"**Example:** ``watchdog_delay = '30000'``\"\nmsgstr \"**示例：** ``watchdog_delay = '30000'``\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:220\nmsgid \"**Possible values:** 'any integer.'\"\nmsgstr \"**可能的值：** ``any integer``\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:222\nmsgid \"\"\n\"**Description:** ``watchdog_delay`` configuration property allows \"\n\"configuring delay (in milliseconds) between subsequent checks that \"\n\"ConnectionManager Watchdog (service responsible for detecting broken \"\n\"connections and closing them) will use to verify the connection. In \"\n\"addition to global configuration presented above a per component \"\n\"configuration is possible:\"\nmsgstr \"\"\n\"**描述：** ``watchdog_delay`` \"\n\"配置属性允许在ConnectionManager监视器（负责检测断开连接并关闭它们的服务）将用于验证连接的后续检查之间配置延迟（以毫秒为单位）。除了上面介绍的全局配置之外，每个组件的配置也是可能的：\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:57\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:230\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:267\nmsgid \"for example (for ClusterConnectionManager):\"\nmsgstr \"例如（对于 ClusterConnectionManager）：\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:244\nmsgid \":ref:`watchdog_timeout<watchdogTimeout>`\"\nmsgstr \":ref:`监视器超时<watchdogTimeout>`\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:251\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:277\nmsgid \"watchdog_ping_type\"\nmsgstr \"监视器ping类型\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:253\nmsgid \"**Default value:** ``whitespace``\"\nmsgstr \"**默认值：** ``whitespace``\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:255\nmsgid \"**Example:** ``watchdog_ping_type = 'XMPP'``\"\nmsgstr \"**示例：** ``watchdog_ping_type = 'XMPP'``\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:257\nmsgid \"**Possible values:** ``WHITESPACE``,\\\\ ``XMPP``\"\nmsgstr \"**可能值：** ``WHITESPACE``,\\\\ ``XMPP``\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:259\nmsgid \"\"\n\"**Description:** ``watchdog_ping_type`` configuration property allows \"\n\"configuring of the type of ping that ConnectionManager Watchdog (service \"\n\"responsible for detecting broken connections and closing them) will use \"\n\"to check the connection. In addition to global configuration presented \"\n\"above a per component configuration is possible:\"\nmsgstr \"\"\n\"**描述：** ``watchdog_ping_type`` \"\n\"配置属性允许配置ConnectionManager监视器（此服务负责检测断开连接并关闭它们）将用于检查连接的ping的类型。除了上面介绍的全局配置之外，每个组件的配置也是可能的：\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:279\nmsgid \":ref:`watchdog_Delay<watchdogDelay>`\"\nmsgstr \":ref:`监视器延迟<watchdogDelay>`\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:286\nmsgid \"ws-allow-unmasked-frames\"\nmsgstr \"ws-allow-unmasked-frames\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:290\nmsgid \"**Example:** ``'ws-allow-unmasked-frames' = true``\"\nmsgstr \"**示例：** ``'ws-allow-unmasked-frames' = true``\"\n\n#: ../../Tigase_Administration/Properties/_user_connectivity.inc:294\nmsgid \"\"\n\"**Description:** RFC 6455 specifies that all clients must mask frames \"\n\"that it sends to the server over Websocket connections. If unmasked \"\n\"frames are sent, regardless of any encryption, the server must close the \"\n\"connection. Some clients however, may not support masking frames, or you \"\n\"may wish to bypass this security measure for development purposes. This \"\n\"setting, when enabled true, will allow connections over websocket to be \"\n\"unmasked to the server, and may operate without Tigase closing that \"\n\"connection.\"\nmsgstr \"\"\n\"**描述：** RFC \"\n\"6455指定所有客户端必须屏蔽它通过Websocket连接发送到服务器的帧。如果发送了未屏蔽的帧，无论任何加密，服务器都必须关闭连接。但是，某些客户端可能不支持屏蔽帧，或者您可能希望绕过此安全措施以用于开发目的。此设置启用true时，将允许通过websocket连接到服务器，并且可以在Tigase关闭该连接的情况下运行。\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:2\nmsgid \"External\"\nmsgstr \"外部\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:7\nmsgid \"bind-ext-hostnames\"\nmsgstr \"bind-ext-hostnames\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:11\nmsgid \"**Example:** ``'bind-ext-hostnames' = [ 'pubsub.host.domain' ]``\"\nmsgstr \"**示例：** ``'bind-ext-hostnames' = [ 'pubsub.host.domain' ]``\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:13\nmsgid \"**Possible values:** comma separated list of domains.\"\nmsgstr \"**可能的值：** 逗号分隔的域列表。\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:15\nmsgid \"\"\n\"**Description:** This property enables setting a list of domains to be \"\n\"bound to the external component connection. Let’s say we have a Tigase \"\n\"instance with only MUC and PubSub components loaded and we want to \"\n\"connect this instance to the main server via external component protocol.\"\n\" Using `--external property <#external>`__ we can define a domain \"\n\"(perhaps muc.devel.tigase.org), password, TCP/IP port, remote host \"\n\"address, connection type, etc…​ This would make one of our components \"\n\"(MUC) visible on the remote server.\"\nmsgstr \"\"\n\"**描述：** \"\n\"此属性允许设置要绑定到外部组件连接的域列表。假设我们有一个仅加载了MUC和PubSub组件的Tigase实例，我们希望通过外部组件协议将此实例连接到主服务器。使用\"\n\" :ref:`--external property<external>` 我们可以定义一个域（可能是 \"\n\"muc.devel.tigase.org），密码，TCP/IP 端口，远程主机地址，连接类型等... \"\n\"这使得我们的组件（MUC）在远程服务器上可见。\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:17\nmsgid \"\"\n\"To make the second component (PubSub) visible we would need to open \"\n\"another connection with the domain name (pubsub.devel.tigase.org) for the\"\n\" other component. Of course the second connection is redundant as all \"\n\"communication could go through a single connection. This is what this \"\n\"property is used. In our example with 2 components you can just put the \"\n\"'pubsub.devel.tigase.org' domain as a value to this property and it will \"\n\"bind the second domain to a single connection on top of the domain which \"\n\"has been authenticated during protocol handshaking.\"\nmsgstr \"\"\n\"为了使第二个组件 (PubSub) 可见，我们需要为另一个组件打开另一个具有域名 (pubsub.devel.tigase.org) \"\n\"的连接。当然，第二个连接是多余的，因为所有通信都可以通过单个连接进行。这就是这个属性的用途。在我们的带有2个组件的示例中，您可以将 \"\n\"'pubsub.devel.tigase.org' 域作为该属性的值，它会将第二个域绑定到在协议握手期间已通过身份验证的域顶部的单个连接。\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:24\nmsgid \"default-virtual-host\"\nmsgstr \"default-virtual-host\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:26\nmsgid \"\"\n\"**Description:** The ``default-virtual-host`` property allows setting of \"\n\"the name of default virtual host that is served by the installation. It \"\n\"is loaded during startup of the application and stored in the database. \"\n\"**It may only contain single domain name!**\"\nmsgstr \"\"\n\"**描述：** ``default-virtual-host`` \"\n\"属性允许设置安装提供的默认虚拟主机的名称。它在应用程序启动期间加载并存储在数据库中。 **它可能只包含一个域名！**\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:28\nmsgid \"\"\n\"Any additional configuration options or additional virtual hosts domains \"\n\"should be added and configured using ad-hoc commands such as ``Add new \"\n\"item``, ``Update item configuration`` and ``Remove an item`` available at\"\n\" the JID of the ``VHostManager`` component of your installation (``vhost-\"\n\"man@your-server-domain``).\"\nmsgstr \"\"\n\"任何额外的配置选项或额外的虚拟主机域都应该使用临时命令添加和配置，例如在 ``VHostManager`` 组件安装的JID (``vhost-\"\n\"man@your-server-domain``) 中可用的命令 ``Add new item``, ``Update item \"\n\"configuration`` 和 ``Remove an item``。\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:33\nmsgid \"ext\"\nmsgstr \"ext\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:35\nmsgid \"\"\n\"**Description:** This property defines parameters for external component \"\n\"connections.\"\nmsgstr \"**描述：** 此属性定义外部组件连接的参数。\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:37\nmsgid \"\"\n\"The component is loaded the same way as all other Tigase components. In \"\n\"your ``config.tdsl`` file you need to add the external class:\"\nmsgstr \"该组件的加载方式与所有其他Tigase组件相同。在您的 ``config.tdsl`` 文件中，您需要添加外部类：\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:43\nmsgid \"\"\n\"This will load the component with an empty configuration and is \"\n\"practically useless. You have to tell the component on what port to \"\n\"listen to (or on what port to connect to) and external domains list with \"\n\"passwords.\"\nmsgstr \"这将加载具有空配置的组件，实际上是无用的。您必须告诉组件在哪个端口上侦听（或在哪个端口上连接）以及带有密码的外部域列表。\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:45\nmsgid \"\"\n\"Those values need to be configured while the Tigase XMPP Server is \"\n\"running using XMPP ad-hoc commands such as ``Add new item``, ``Update \"\n\"item configuration`` and ``Remove an item`` available at the JID of the \"\n\"external component which you have just enabled (``ext@your-server-\"\n\"domain``).\"\nmsgstr \"\"\n\"这些值需要在Tigase XMPP服务器运行时使用XMPP ad-hoc命令配置，例如 在您刚刚启用的外部组件（``ext@your-\"\n\"server-domain``）可用的命令 ``Add new item``，``Update item configuration`` 和 \"\n\"``Remove an item``。\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:47\nmsgid \"**Possible values:** external domains parameters list.\"\nmsgstr \"**可能的值：** 外部域参数列表。\"\n\n#: ../../Tigase_Administration/Properties/_external.inc:51\nmsgid \"**Removed in:** 8.0.0\"\nmsgstr \"**以下版本中删除：** 8.0.0\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:2\nmsgid \"Performance\"\nmsgstr \"表现\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:5\nmsgid \"cm-ht-traffic-throttling\"\nmsgstr \"cm-ht-traffic-throttling\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:7\nmsgid \"**Default value:** ``xmpp:25k:0:disc,bin:200m:0:disc``\"\nmsgstr \"**默认值：** ``xmpp:25k:0:disc,bin:200m:0:disc``\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:9\nmsgid \"\"\n\"**Example:** ``'cm-ht-traffic-throttling' = \"\n\"'xmpp:25k:0:disc,bin:200m:0:disc'``\"\nmsgstr \"**示例：** ``'cm-ht-traffic-throttling' = 'xmpp:25k:0:disc,bin:200m:0:disc'``\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:11\n#: ../../Tigase_Administration/Properties/_performance.inc:26\nmsgid \"**Possible values:** comma separated list of traffic limits settings.\"\nmsgstr \"**可能的值：** 以逗号分隔的流量限制设置列表。\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:13\nmsgid \"\"\n\"**Description:** This property is used to specify traffic limit of non-\"\n\"user connections, that is s2s, external components and other high traffic\"\n\" server connections. The meaning of the property and values encoded are \"\n\"in the same way as for the :ref:`cm-traffic-throttling \"\n\"property<cmTrafficThrottling>`.\"\nmsgstr \"\"\n\"**说明：** \"\n\"该属性用于指定非用户连接的流量限制，即s2s，外部组件等高流量服务器连接。\"\n\"编码的属性和值的含义与 :ref:`cm-traffic-throttling \"\n\"property<cmTrafficThrottling>` 的含义相同。\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:20\nmsgid \"cm-traffic-throttling\"\nmsgstr \"cm-traffic-throttling\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:22\nmsgid \"**Default value:** ``xmpp:2500:0:disc,bin:20m:0:disc``\"\nmsgstr \"**默认值：** ``xmpp:2500:0:disc,bin:20m:0:disc``\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:24\nmsgid \"\"\n\"**Example:** ``'cm-traffic-throttling' = \"\n\"'xmpp:2500:0:disc,bin:20m:0:disc'``\"\nmsgstr \"**示例：** ``'cm-traffic-throttling' = 'xmpp:2500:0:disc,bin:20m:0:disc'``\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:28\nmsgid \"\"\n\"**Description:** The ``cm-traffic-throttling`` property allows you to \"\n\"limit traffic on user connections. These limits are applied to each user \"\n\"connection and if a limit is exceeded then a specified action is applied.\"\nmsgstr \"\"\n\"**描述：** ``cm-traffic-throttling`` \"\n\"属性允许您限制用户连接上的流量。这些限制适用于每个用户连接，如果超出限制，则会应用指定的操作。\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:30\nmsgid \"\"\n\"The property value is a comma separated list of traffic limits settings. \"\n\"For example the first part: ``xmpp:2500:0:disc`` specifies traffic limits\"\n\" for XMPP data to 2,500 packets allowed within last minute either sent to\"\n\" or received from a user and unlimited (0) total traffic on the user \"\n\"connection, in case any limit is exceeded the action is to **disconnect**\"\n\" the user.\"\nmsgstr \"\"\n\"属性值是以逗号分隔的流量限制设置列表。例如第一部分：``xmpp:2500:0:disc`` 将 XMPP \"\n\"数据的流量限制指定为在最后一分钟内允许发送给用户或从用户接收的2,500个数据包，并且用户连接上的总流量不受限制（0），如果超过任何限制，则操作是\"\n\" **断开** 用户。\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:32\nmsgid \"\"\n\"**[xmpp|bin]** traffic type, xmpp - XMPP traffic, that is limits refer to\"\n\" a number of XMPP packets transmitted, bin - binary traffic, that is \"\n\"limits refer to a number of bytes transmitted.\"\nmsgstr \"\"\n\"**[xmpp|bin]** 流量类型，xmpp - XMPP 流量，即限制是指传输的XMPP数据包的数量，bin - \"\n\"二进制流量，即限制是指传输的字节数。\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:34\nmsgid \"\"\n\"**2500** maximum traffic allowed within 1 minute. 0 means unlimited, or \"\n\"no limits.\"\nmsgstr \"**2500** 1 分钟内允许的最大流量。 0 表示无限或没有限制。\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:36\nmsgid \"\"\n\"**0** maximum traffic allowed for the life span of the connection. 0 \"\n\"means unlimited or no limits.\"\nmsgstr \"**0** 连接生命周期内允许的最大流量。 0 表示无限或没有限制。\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:38\nmsgid \"\"\n\"**[disc|drop]** action performed on the connection if limits are \"\n\"exceeded. disc - means disconnect, drop - means drop data.\"\nmsgstr \"**[disc|drop]** 超出限制时对连接执行的操作。 disc - 表示断开连接，drop - 表示丢弃数据。\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:40\nmsgid \"**Available since:** 5.1.3\"\nmsgstr \"**从以下时间开始可用：** 5.1.3\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:43\nmsgid \"elements-number-limit\"\nmsgstr \"elements-number-limit\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:45\nmsgid \"**Default value:** ``1000``\"\nmsgstr \"**默认值：** ``1000``\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:47\nmsgid \"**Possible values:** any integer.\"\nmsgstr \"**可能的值：** 任何整数。\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:49\nmsgid \"\"\n\"**Description:** ``elements-number-limit`` configuration property allows \"\n\"configuring a Denial of Service protection mechanism which limits number \"\n\"of elements sent in stanza. It must be configured on a per \"\n\"ConnectionManager basis:\"\nmsgstr \"\"\n\"**描述：** ``elements-number-limit`` \"\n\"配置属性允许配置拒绝服务保护机制，该机制限制在节中发送的元素数量。它必须基于每个ConnectionManager进行配置：\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:69\nmsgid \"hardened-mode\"\nmsgstr \"hardened-mode\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:71\nmsgid \"**Default value:** ``secure``\"\nmsgstr \"**默认值：** ``secure``\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:73\nmsgid \"**Example:** ``'hardened-mode' = secure``\"\nmsgstr \"**示例：** ``'hardened-mode' = secure``\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:75\nmsgid \"**Possible values:** ``relaxed|secure|strict``\"\nmsgstr \"**可能的值：** ``relaxed|secure|strict``\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:77\nmsgid \"\"\n\"**Description:** Adjusting hardened mode affects handling of security \"\n\"aspects within Tigase. The higher the level the more strict are the \"\n\"rules: \\\\* ``relaxed`` - uses default security capabilities from \"\n\"installed JVM; \\\\* ``secure`` - disables old SSLv2 and SSLv3, disables \"\n\"weak cyphers; \\\\* ``strict`` - in addition to ``secure`` level changes it\"\n\" also disables ``TLSv1`` and ``TLSv1.1`` as well as ciphers that don’t \"\n\"support Forward secrecy.\"\nmsgstr \"\"\n\"**描述：** 调整强化模式会影响Tigase中安全方面的处理。级别越高，规则越严格： \\\\* ``relaxed`` - \"\n\"使用已安装JVM的默认安全功能； \\\\* ``secure`` - 禁用旧的 SSLv2 和 SSLv3，禁用弱密码； \\\\* \"\n\"``strict`` - 除了 ``secure`` 级别更改之外，它还禁用 ``TLSv1`` 和 ``TLSv1.1`` \"\n\"以及不支持前向保密的密码。\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:79\nmsgid \"\"\n\"On older JVM versions it required `UnlimitedJCEPolicyJDK \"\n\"<http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html>`__\"\n\" installed. It’s not required with OpenJDK8 and newer an OracleJVM 11 and\"\n\" newer.\"\nmsgstr \"\"\n\"在较旧的JVM版本上，它需要安装 `UnlimitedJCEPolicyJDK \"\n\"<http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html>`__。\"\n\" OpenJDK8和高版本的OracleJVM 11以及更高版本不需要它。\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:84\nmsgid \"max-queue-size\"\nmsgstr \"max-queue-size\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:86\nmsgid \"**Default value:** default queue size is variable depending on RAM size.\"\nmsgstr \"**默认值：** 默认队列大小取决于RAM大小。\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:88\nmsgid \"**Example:** ``'max-queue-size' = 10000``\"\nmsgstr \"**示例：** ``'max-queue-size' = 10000``\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:90\n#: ../../Tigase_Administration/Properties/_vhost.inc:43\nmsgid \"**Possible values:** integer number.\"\nmsgstr \"**可能的值：** 整数。\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:92\nmsgid \"\"\n\"**Description:** The ``max-queue-size`` property sets internal queues \"\n\"maximum size to a specified value. By default Tigase sets the queue size \"\n\"depending on the maximum available memory to the Tigase server process. \"\n\"It set’s 1000 for each 100MB memory assigned for JVM. This is enough for \"\n\"most cases. If you have however, an extremely busy service with Pubsub or\"\n\" MUC component generating huge number of packets (presence or messages) \"\n\"this size should be equal or bigger to the maximum expected number of \"\n\"packets generated by the component in a single request. Otherwise Tigase \"\n\"may drop packets that it is unable to process.\"\nmsgstr \"\"\n\"**描述：** ``max-queue-size`` \"\n\"属性将内部队列的最大大小设置为指定值。默认情况下，Tigase根据Tigase服务器进程的最大可用内存设置队列大小。对于分配给JVM的每100MB内存，它设置为1000。这对于大多数情况来说已经足够了。但是，如果您有一个非常繁忙的服务，其中Pubsub\"\n\" \"\n\"或MUC组件生成大量数据包（存在或消息），则此大小应等于或大于组件在单个请求中生成的最大预期数据包数。否则Tigase可能会丢弃它无法处理的数据包。\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:99\nmsgid \"net-buff-high-throughput\"\nmsgstr \"net-buff-high-throughput\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:101\nmsgid \"**Default value:** ``64k``\"\nmsgstr \"**默认值：** ``64k``\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:103\nmsgid \"**Example:** ``'net-buff-high-throughput' = '256k'``\"\nmsgstr \"**示例：** ``'net-buff-high-throughput' = '256k'``\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:105\n#: ../../Tigase_Administration/Properties/_performance.inc:120\nmsgid \"**Possible values:** network buffer size as integer.\"\nmsgstr \"**可能的值：** 网络缓冲区大小为整数。\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:107\nmsgid \"\"\n\"**Description:** The ``net-buff-high-throughput`` property sets the \"\n\"network buffer for high traffic connections like s2s or connections \"\n\"between cluster nodes. The default is ``64k`` and is optimal for medium \"\n\"traffic websites. If your cluster installation can not cope with traffic \"\n\"between nodes try to increase this number.\"\nmsgstr \"\"\n\"**描述：** ``net-buff-high-throughput`` 属性为高流量连接（如 \"\n\"s2s）或集群节点之间的连接设置网络缓冲区。默认值为 \"\n\"``64k``，最适合中等流量的网站。如果您的集群安装无法应对节点之间的流量，请尝试增加此数量。\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:114\nmsgid \"net-buff-standard\"\nmsgstr \"net-buff-standard\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:116\n#: ../../Tigase_Administration/Properties/_performance.inc:129\nmsgid \"**Default value:** ``2k``\"\nmsgstr \"**默认值：** ``2k``\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:118\nmsgid \"**Example:** ``'net-buff-standard' = '16k'``\"\nmsgstr \"**例子：** ``'net-buff-standard' = '16k'``\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:122\nmsgid \"\"\n\"**Description:** This property sets the network buffer for standard \"\n\"(usually c2s) connections, default value is 2k and is optimal for most \"\n\"installations.\"\nmsgstr \"**描述：** 此属性设置标准（通常是 c2s）连接的网络缓冲区，默认值为2k，最适合大多数安装。\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:127\nmsgid \"net-buffer\"\nmsgstr \"net-buffer\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:131\nmsgid \"**Example:** ``'net-buffer' = 16 * 1024``\"\nmsgstr \"**示例：** ``'net-buffer' = 16 * 1024``\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:133\nmsgid \"**Possible values:** internal network buffer size as integer.\"\nmsgstr \"**可能的值：** 内部网络缓冲区大小为整数。\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:135\nmsgid \"\"\n\"**Description:** Sets default size of a internal network buffer used by \"\n\"``ConnectionManager`` within the context in which it is set.\"\nmsgstr \"**描述：** 设置 ``ConnectionManager`` 在其设置的上下文中使用的内部网络缓冲区的默认大小。\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:140\nmsgid \"socket-buffer-size\"\nmsgstr \"socket-buffer-size\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:142\nmsgid \"\"\n\"**Default value:** ``4k`` (for client-to-server connections) and ``64k`` \"\n\"(for server-to-server connections)\"\nmsgstr \"**默认值：** ``4k`` （用于客户端到服务器的连接）和 ``64k`` （用于服务器到服务器的连接）\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:144\nmsgid \"**Example:** ``'socket-buffer-size' = 16 * 1024``\"\nmsgstr \"**示例：** ``'socket-buffer-size' = 16 * 1024``\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:146\nmsgid \"**Possible values:** socket network buffer size as integer.\"\nmsgstr \"**可能的值：** 套接字网络缓冲区大小为整数。\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:148\nmsgid \"\"\n\"**Description:** Sets default size of a socket network buffer used by \"\n\"``ConnectionManager`` for each connection within the context in which it \"\n\"is set.\"\nmsgstr \"**描述：** 设置 ``ConnectionManager`` 使用的套接字网络缓冲区的默认大小，用于设置它的上下文中的每个连接。\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:150\nmsgid \"**Available since:** 8.3.0 (previously value of ``net-buffer`` was used)\"\nmsgstr \"**从以下版本开始可用：** 8.3.0（之前使用了 ``net-buffer`` 的值）\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:153\nmsgid \"nonpriority-queue\"\nmsgstr \"nonpriority-queue\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:157\nmsgid \"**Example:** ``'nonpriority-queue' =  true``\"\nmsgstr \"**示例：** ``'nonpriority-queue' =  true``\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:161\nmsgid \"\"\n\"**Description:** The ``nonpriority`` property can be used to switch to \"\n\"non-priority queues usage in Tigase server (value set to 'true'). Using \"\n\"non-priority queues prevents packets reordering. By default Tigase uses \"\n\"priority queues which means that packets with highest priority may take \"\n\"over packets with lower priority (presence updates) which may result in \"\n\"packets arriving out of order.\"\nmsgstr \"\"\n\"**描述：** ``nonpriority`` 属性可用于切换到Tigase服务器中的非优先队列使用（值设置为 \"\n\"'true'）。使用非优先队列可防止数据包重新排序。默认情况下，Tigase使用优先级队列，这意味着具有最高优先级的数据包可能会接管具有较低优先级的数据包（存在更新），这可能会导致数据包无序到达。\"\n\n#: ../../Tigase_Administration/Properties/_performance.inc:163\nmsgid \"\"\n\"This may happen however only for packets of different types. That is, \"\n\"messages may take over presence packets. However, one message never takes\"\n\" over another message for the same user. Therefore, out of order packet \"\n\"delivery is not an issue for the most part.\"\nmsgstr \"然而，这可能只发生在不同类型的数据包中。也就是说，消息可以接管存在数据包。但是，一条消息永远不会为同一用户接管另一条消息。因此，在大多数情况下，无序数据包传送不是问题。\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:2\nmsgid \"VHost / domain\"\nmsgstr \"虚拟主机/域\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:5\nmsgid \"vhost-anonymous-enabled\"\nmsgstr \"vhost-anonymous-enabled\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:9\nmsgid \"**Example:** ``'vhost-anonymous-enabled' = 'false'``\"\nmsgstr \"**示例：** ``'vhost-anonymous-enabled' = 'false'``\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:13\nmsgid \"\"\n\"**Description:** The ``vhost-anonymous-enabled`` property specifies \"\n\"whether anonymous user logins are allowed for the installation for all \"\n\"vhosts.\"\nmsgstr \"**描述：** ``vhost-anonymous-enabled`` 属性指定是否允许匿名用户登录所有虚拟主机的安装。\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:15\n#: ../../Tigase_Administration/Properties/_vhost.inc:32\n#: ../../Tigase_Administration/Properties/_vhost.inc:53\n#: ../../Tigase_Administration/Properties/_vhost.inc:72\nmsgid \"\"\n\"This is a global property which is overridden by settings for particular \"\n\"VHost (see :ref:`Add and Manage Domains (VHosts)<addManageDomain>`).\"\nmsgstr \"这是一个全局属性，被特定 VHost 的设置覆盖（请参阅 :ref:`添加和管理域 \"\n\"(VHosts)<addManageDomain>`）。\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:17\nmsgid \"\"\n\"Default settings for all virtual hosts are used when this property is not\"\n\" defined. This settings is useful mostly for installations with many \"\n\"virtual hosts listed in the ``config.tdsl`` file for which there is no \"\n\"individual settings specified. It allows the configuration of default \"\n\"values for all of them, instead of having to provide individual \"\n\"configuration for each VHost.\"\nmsgstr \"\"\n\"如果未定义此属性，则使用所有虚拟主机的默认设置。此设置主要用于在 ``config.tdsl`` \"\n\"文件中列出的许多虚拟主机的安装，这些主机没有指定单独的设置。它允许为所有这些配置默认值，而不必为每个VHost提供单独的配置。\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:22\nmsgid \"vhost-disable-dns-check\"\nmsgstr \"vhost-disable-dns-check\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:26\nmsgid \"**Example:** ``'vhost-disable-dns-check' = 'true'``\"\nmsgstr \"**示例：** ``'vhost-disable-dns-check' = 'true'``\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:30\nmsgid \"\"\n\"**Description:** This property disables DNS validation when adding or \"\n\"editing vhosts in Tigase server. This also exempts administrative \"\n\"accounts from validation. With this property enabled, you will not \"\n\"benefit from seeing if proper SRV records are set so other people can \"\n\"connect to specific vhosts from outside your network.\"\nmsgstr \"\"\n\"**描述：** 在Tigase服务器中添加或编辑虚拟主机时，此属性会禁用DNS验证。这也使管理帐户免于验证。启用此属性后，可查看是否设置了正确的 \"\n\"SRV 记录，以便其他人可以从您的网络外部连接到特定的虚拟主机， 但你不会从中受益。\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:37\nmsgid \"vhost-max-users\"\nmsgstr \"vhost-max-users\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:39\nmsgid \"**Default value:** ``0``\"\nmsgstr \"**默认值：** ``0``\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:41\nmsgid \"**Example:** ``'vhost-max-users' = '1000'``\"\nmsgstr \"**示例：** ``'vhost-max-users' = '1000'``\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:45\nmsgid \"\"\n\"**Description:** The ``vhost-max-users`` property specifies how many user\"\n\" accounts can be registered on the installations for all vhosts.\"\nmsgstr \"**描述：** ``vhost-max-users`` 属性指定可以在所有虚拟主机的安装中注册多少用户帐户。\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:47\nmsgid \"\"\n\"**0 - zero** means unlimited and this is a default. Otherwise greater \"\n\"than zero value specifies accounts number limit.\"\nmsgstr \"**0 - zero** 表示无限制，这是默认设置。否则大于零值指定帐号限制。\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:49\n#: ../../Tigase_Administration/Properties/_vhost.inc:87\n#: ../../Tigase_Administration/Properties/_vhost.inc:108\n#: ../../Tigase_Administration/Properties/_vhost.inc:129\nmsgid \"\"\n\"This is a global property which is overridden by settings for particular \"\n\"vhost.\"\nmsgstr \"这是一个全局属性，被特定虚拟主机的设置覆盖。\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:51\nmsgid \"\"\n\"The default setting is used for all virtual hosts for which the \"\n\"configuration is not defined. This settings is most useful for \"\n\"installations with many virtual hosts listed in the ``init.property`` \"\n\"file for which there is no individual settings specified. It provides an \"\n\"ability to use default values for all of them, instead of having to \"\n\"provide individual configuration for each vhost.\"\nmsgstr \"\"\n\"默认设置用于未定义配置的所有虚拟主机。此设置对于在 ``init.property`` \"\n\"文件中列出的没有指定单独设置的许多虚拟主机的安装最有用。它提供了对所有这些都使用默认值的能力，而不必为每个虚拟主机提供单独的配置。\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:58\nmsgid \"vhost-message-forward-jid\"\nmsgstr \"vhost-message-forward-jid\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:60\nmsgid \"**Default value:** <null>\"\nmsgstr \"**默认值：** <null>\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:62\nmsgid \"**Example:** ``'vhost-message-forward-jid' = 'archive@domain.com'``\"\nmsgstr \"**示例：** ``'vhost-message-forward-jid' = 'archive@domain.com'``\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:64\nmsgid \"**Possible values:** 'valid JID'\"\nmsgstr \"**可能值：** 'valid JID'\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:66\nmsgid \"\"\n\"**Description:** This is a global property for message forwarding for the\"\n\" installation. This property is normally specified on the vhost \"\n\"configuration level, however if you want to forward all messages on your \"\n\"installation and you have many virtual domains this property allows to \"\n\"set message forwarding for all of them. A valid JID must be specified as \"\n\"the forwarding destination. Also a message forwarding plugin must be \"\n\"loaded and activated on the installation for the message forwarding to \"\n\"work.\"\nmsgstr \"\"\n\"**描述：** \"\n\"这是安装的消息转发的全局属性。此属性通常在vhost配置级别上指定，但是如果您想转发安装中的所有消息并且您有许多虚拟域，则此属性允许为所有这些域设置消息转发。必须将有效的JID指定为转发目的地。此外，必须在安装时加载并激活消息转发插件才能使消息转发工作。\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:68\nmsgid \"\"\n\"The null value is used as a default when no configuration is set. This \"\n\"setting is mostly useful for installations with many virtual hosts listed\"\n\" in the ``init.property`` file for which there is no individual settings \"\n\"specified. It provides the ability to configure a default values for all \"\n\"of them, instead of having to provide individual configuration for each \"\n\"vhost.\"\nmsgstr \"\"\n\"未设置配置时，将使用null值作为默认值。此设置对于在 ``init.property`` \"\n\"文件中列出的没有指定单独设置的许多虚拟主机的安装非常有用。它提供了为所有这些配置默认值的能力，而不必为每个虚拟主机提供单独的配置。\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:70\n#: ../../Tigase_Administration/Properties/_vhost.inc:91\n#: ../../Tigase_Administration/Properties/_vhost.inc:112\n#: ../../Tigase_Administration/Properties/_vhost.inc:133\nmsgid \"\"\n\"It is also applied as a default value for all new vhosts added at run-\"\n\"time.\"\nmsgstr \"它也被用作在运行时添加的所有新虚拟主机的默认值。\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:77\nmsgid \"vhost-presence-forward-jid\"\nmsgstr \"vhost-presence-forward-jid\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:79\nmsgid \"**Default value:** ``<null>``\"\nmsgstr \"**默认值：** ``<null>``\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:81\nmsgid \"\"\n\"**Example:** ``'vhost-presence-forward-jid' = 'presence-\"\n\"collector@domain.com'``\"\nmsgstr \"**示例：** ``'vhost-presence-forward-jid' = 'presence-collector@domain.com'``\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:83\nmsgid \"**Possible values:** valid JID.\"\nmsgstr \"**可能的值：** 有效的 JID。\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:85\nmsgid \"\"\n\"**Description:** This is a global property for presence forwarding \"\n\"function for the installation. All user status presences will be \"\n\"forwarded to given XMPP address which can be a component or any other \"\n\"XMPP entity. If the destination entity is a bot connected via c2s \"\n\"connection it probably should be addressed via full JID (with resource \"\n\"part) or the standard XMPP presence processing would refuse to deliver \"\n\"presences from users who are not in the contact list.\"\nmsgstr \"\"\n\"**描述：** \"\n\"这是安装的状态转发功能的全局属性。所有用户状态都将被转发到给定的XMPP地址，该地址可以是一个组件或任何其他XMPP实体。如果目标实体是通过c2s连接的机器人，它可能应该通过完整的JID（带有资源部分）来解决，或者标准的XMPP状态处理将拒绝发送不在联系人列表中的用户的状态。\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:89\nmsgid \"\"\n\"The null value is used as a default when no configuration is set. This \"\n\"settings is useful for installations with many virtual hosts listed in \"\n\"the ``init.property`` file for which there is no individual settings \"\n\"specified. It enables the ability to configure default values for all of \"\n\"them, instead of having to provide individual configuration for each \"\n\"vhost.\"\nmsgstr \"\"\n\"未设置配置时，将使用null值作为默认值。此设置对于在 ``init.property`` \"\n\"文件中列出的没有指定单独设置的许多虚拟主机的安装很有用。它可以为所有这些配置默认值，而不必为每个虚拟主机提供单独的配置。\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:93\n#: ../../Tigase_Administration/Properties/_vhost.inc:114\n#: ../../Tigase_Administration/Properties/_vhost.inc:135\nmsgid \"This may be used on a per-VHost (see `??? <#addManageDomain>`__).\"\nmsgstr \"这可以在每个 VHost 上使用（请参阅 :ref:`添加和管理域(VHosts)<addManageDomain>`）。\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:98\nmsgid \"vhost-register-enabled\"\nmsgstr \"vhost-register-enabled\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:102\nmsgid \"**Example:** ``'vhost-register-enabled' = false``\"\nmsgstr \"**示例：** ``'vhost-register-enabled' = false``\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:106\nmsgid \"\"\n\"**Description:** ``vhost-register-enabled`` is a global property which \"\n\"allows you to switch on/off user registration on the installation. \"\n\"Setting this property to ``false`` does not disable the registration \"\n\"plugin on the server. You can enable registration for selected domains in\"\n\" the domain configuration settings.\"\nmsgstr \"\"\n\"**描述：** ``vhost-register-enabled`` 是一个全局属性，允许您在安装时打开/关闭用户注册。将此属性设置为 \"\n\"``false`` 不会禁用服务器上的注册插件。您可以在域配置设置中启用所选域的注册。\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:110\nmsgid \"\"\n\"The ``true`` value is used as a default when no configuration is set. \"\n\"This settings is useful for installations with many virtual hosts listed \"\n\"in the ``init.property`` file for which there is no individual settings \"\n\"specified. It allows admins to configure default values for all of them, \"\n\"instead of having to provide individual configuration for each vhost.\"\nmsgstr \"\"\n\"当没有设置配置时，``true`` 值用作默认值。此设置对于在 ``init.property`` \"\n\"文件中列出的没有指定单独设置的许多虚拟主机的安装很有用。它允许管理员为所有这些配置默认值，而不必为每个虚拟主机提供单独的配置。\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:119\nmsgid \"vhost-tls-required\"\nmsgstr \"vhost-tls-required\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:123\nmsgid \"**Example:** ``'vhost-tls-required' = true``\"\nmsgstr \"**示例：** ``'vhost-tls-required' = true``\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:127\nmsgid \"\"\n\"**Description:** This property is a global settings to switch on/off TLS \"\n\"required mode on the Tigase installation. Setting this property to \"\n\"``false`` does not turn TLS off. TLS is still available on the server but\"\n\" as an option and this is the client’s decision whether to use encryption\"\n\" or not. If the property is set to true the server will not allow for \"\n\"user authentication or sending any other user data before TLS handshaking\"\n\" is completed.\"\nmsgstr \"\"\n\"**描述：** 此属性是在Tigase安装上打开/关闭TLS所需模式的全局设置。将此属性设置为 ``false`` 不会关闭TLS。 \"\n\"TLS在服务器上仍然可用，但作为一个选项，这是客户端决定是否使用加密。如果该属性设置为true，则服务器将不允许在 TLS \"\n\"握手完成之前进行用户身份验证或发送任何其他用户数据。\"\n\n#: ../../Tigase_Administration/Properties/_vhost.inc:131\nmsgid \"\"\n\"The ``false`` value is used as a default when no configuration is set. \"\n\"This settings is useful for installations with many virtual hosts listed \"\n\"in the ``init.property`` file for which there is no individual settings \"\n\"specified. It allows admins to configure default values for all of them, \"\n\"instead of having to provide individual configuration for each vhost.\"\nmsgstr \"\"\n\"当没有设置配置时，``false`` 值用作默认值。此设置对于在 ``init.property`` \"\n\"文件中列出的没有指定单独设置的许多虚拟主机的安装很有用。它允许管理员为所有这些配置默认值，而不必为每个虚拟主机提供单独的配置。\"\n\n#~ msgid \"\"\n#~ \"This is a global property which is\"\n#~ \" overridden by settings for each \"\n#~ \"VHost (see `??? <#addManageDomain>`__)\"\n#~ msgstr \"这是一个全局属性，被每个 VHost 的设置覆盖 (参见 :ref:`添加和管理域(VHosts)<addManageDomain>`)\"\n\n#~ msgid \"\"\n#~ \"This is a global property which is\"\n#~ \" overridden by settings for particular \"\n#~ \"VHosts (see `??? <#addManageDomain>`__).\"\n#~ msgstr \"\"\n#~ \"这是一个全局属性，被特定VHost的设置覆盖（请参阅 :ref:`Add and Manage \"\n#~ \"Domains (VHosts)<addManageDomain>`）。\"\n\n#~ msgid \"`watchdog_Delay <#watchdogDelay>`__\"\n#~ msgstr \":ref:`监视器延迟<watchdogDelay>`\"\n\n#~ msgid \"\"\n#~ \"This is a global property which is\"\n#~ \" overridden by settings for particular \"\n#~ \"VHost (see `??? <#addManageDomain>`__).\"\n#~ msgstr \"这是一个全局属性，被特定VHost的设置覆盖 (参见 :ref:`添加和管理域(VHosts)<addManageDomain>`)。\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Administration/Quick_Start_Guide/Intro.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version:  TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2023-02-16 15:01-0800\\n\"\n\"PO-Revision-Date: 2022-09-07 17:14+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language: zh_CN\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects\"\n\"/tigase-xmpp-server/quick_start_guide/zh_Hans/>\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.11.0\\n\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:2\nmsgid \"Quick Start Guide\"\nmsgstr \"快速入门指南\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:5\nmsgid \"Minimum Requirements\"\nmsgstr \"最低要求\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:7\nmsgid \"\"\n\"Before you begin installing Tigase server onto your system, please make \"\n\"sure the minimum requirements are met first:\"\nmsgstr \"在将 Tigase 服务器开始安装到你的系统之前，请确保首先满足系统最低要求：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:9\n#, fuzzy\nmsgid \"**Java Development Kit (JDK) 17 (LTS)** - We recommend OpenJDK\"\nmsgstr \"**Java开发工具包（JDK)11(LTS)** - 我们推荐OpenJDK\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:11\nmsgid \"\"\n\"**Administrator access** - We recommend that you install Tigase Server \"\n\"from a user login with administrator access.\"\nmsgstr \"**管理员访问权限** - 我们建议您从有管理员访问权限的用户登录安装Tigase Server。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:17\nmsgid \"\"\n\"You should always run the latest point/bugfix release of the recommended \"\n\"JDK.\"\nmsgstr \"您应该始终运行推荐的JDK 的最新版本/错误修复版本。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:21\nmsgid \"\"\n\"While it should be possible to use newer versions of the JDK, we don’t \"\n\"guarantee it and we recommend using the one mentioned above.\"\nmsgstr \"虽然应该可以使用更新版本的JDK，但我们不保证这一点，我们建议使用上述版本。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:24\nmsgid \"Contents\"\nmsgstr \"内容\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:26\nmsgid \"\"\n\"This is a set of documents allowing you to quickly start with our \"\n\"software. Every document provides an introduction to a single topic \"\n\"allowing you to start using/developing or just working on the subject. \"\n\"Please have a look at the documents list below to find a topic you are \"\n\"looking for. If you don’t find a document for the topic you need please \"\n\"`let us know <http://www.tigase.net/contact>`__.\"\nmsgstr \"\"\n\"这是可帮您快速开始使用我们软件的一组文档。每个文档都提供了对单个主题的介绍，从而使您可以开始使用/开发或开始研究该主题。请查看下面的文档列表以找到您正在寻找的主题。如果您没有找到所需主题的文档，请\"\n\" `让我们知道 <http://www.tigase.net/contact>`__。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:28\nmsgid \":ref:`Installation Using Web Installer<webinstall>`\"\nmsgstr \":ref:`使用 Web 安装程序进行安装<webinstall>`\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:30\nmsgid \":ref:`Manual installation in console mode<manualinstall>`\"\nmsgstr \":ref:`在控制台模式下手动安装<manualinstall>`\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:32\nmsgid \":ref:`Installing Tigase on Windows <windowsInstallation>`\"\nmsgstr \":ref:`在 Windows 上安装 Tigase<windowsInstallation>`\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:34\nmsgid \":ref:`Network settings for Tigase<setupTigaseServer>`\"\nmsgstr \":ref:`Tigase的网络设置<setupTigaseServer>`\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Intro.rst:36\nmsgid \":ref:`Running Tigase XMPP Server as a service<tigaseScriptStart>`\"\nmsgstr \":ref:`将 Tigase XMPP 服务器作为服务运行<tigaseScriptStart>`\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:4\nmsgid \"Installation Using Web Installer\"\nmsgstr \"使用 Web 安装程序安装\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:6\nmsgid \"\"\n\"When Tigase XMPP Server starts up, it looks for the default configuration\"\n\" file: ``etc/config.tdsl``. If this file has not been modified you can \"\n\"run the web installer. Which will step you through the process of \"\n\"configuring Tigase. If you are installing Tigase in a Windows \"\n\"environment, please see the :ref:`Windows Installation<winWebInstall>` \"\n\"section.\"\nmsgstr \"\"\n\"当Tigase XMPP服务器启动时，它会查找默认配置文件: \"\n\"``etc/config.tdsl``。如果此文件未被修改，您可以运行Web安装程序。这将引导您完成Tigase的配置。如果您在Windows环境中安装Tigase，请参阅\"\n\" :ref:`Windows安装<winWebInstall>` 部分。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:10\nmsgid \"Download and Extract\"\nmsgstr \"下载并解压\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:12\nmsgid \"\"\n\"First download Tigase XMPP Server and extract it. You can download the \"\n\"`official binaries <https://tigase.net/downloads>`__, or the latest and \"\n\"greatest `nightly builds <https://build.tigase.net/nightlies/dists/>`__. \"\n\"Once you have the distribution binary extract it and navigate to the \"\n\"directory:\"\nmsgstr \"\"\n\"首先下载Tigase XMPP Server并解压。你可以下载 `官方二进制文件 \"\n\"<https://tigase.net/downloads>`__，或者最新最好的 `试运行 \"\n\"<https://build.tigase.net/nightlies/dists/>`__。一旦获得分发二进制文件后，可将其解压缩并浏览此目录：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:22\nmsgid \"Do not run as root user!\"\nmsgstr \"不要以root用户身份运行!\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:26\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:283\nmsgid \"Start the Server\"\nmsgstr \"启动服务器\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:30\nmsgid \"Please make sure ``JAVA_HOME`` is set and points to your JVM installation\"\nmsgstr \"请确保 ``JAVA_HOME`` 已设置并指向您的JVM安装\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:38\nmsgid \"Verify Tigase is ready to start installation\"\nmsgstr \"确保Tigase已准备好开始安装\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:40\nmsgid \"\"\n\"Tigase should start listening on port 8080 - you can check it using \"\n\"``lsof`` command:\"\nmsgstr \"Tigase开始监听端口8080 - 你可以使用 ``lsof``命令检查它：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:48\nmsgid \"\"\n\"You can also check console log under ``logs/tigase-console.log``, which \"\n\"should point you directly to the installer.\"\nmsgstr \"您还可以检查 ``logs/tigase-console.log`` 下的控制台日志，它应该直接指向安装程序。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:53\nmsgid \"Connect to the Web Installer\"\nmsgstr \"连接到Web安装程序\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:55\nmsgid \"Some points before you can connect:\"\nmsgstr \"连接前的几个要点：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:57\nmsgid \"\"\n\"This setup page is restricted access, however for first setup there is a \"\n\"default account set to setup Tigase: Username: ``admin`` Password: \"\n\"``tigase``\"\nmsgstr \"此设置页面是受限访问的，但是第一次设置的时候，有一个默认帐户来设置Tigase：用户名：``admin`` 密码：``tigase``\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:59\nmsgid \"\"\n\"This combination will only be valid once as it will be removed from \"\n\"``config.tdsl`` file on completion of setup process. After this point the\"\n\" setup page will only be accessible using the following:\"\nmsgstr \"上面的设置仅一次有效，因为在安装过程完成后它将被从config.tdsl文件中删除。在此之后，只能使用以下方式访问设置页面：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:61\nmsgid \"\"\n\"JID accounts listed as administrators in admins line in ``config.tdsl`` \"\n\"file.\"\nmsgstr \"JID帐户在config.tdsl 文件中的admins行中被列为管理员。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:63\nmsgid \"\"\n\"Username and password combinations added to ``config.tdsl`` file \"\n\"manually, or at the last page in this process.\"\nmsgstr \"手动把用户名和密码组合添加到 ``config.tdsl`` 文件中或添加在此过程的最后一页。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:65\nmsgid \"\"\n\"Point your browser to http://localhost:8080/setup/ unless you are working\"\n\" remotely. You can also use the domain name, or IP address.\"\nmsgstr \"除非您在远程工作，否则您的浏览器将指向http://localhost:8080/setup/。您也可以使用域名或IP地址。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:67\nmsgid \"Enter the username and password above to gain access.\"\nmsgstr \"输入上面的用户名和密码以获得访问权限。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:71\nmsgid \"Step Through the Installation Process\"\nmsgstr \"逐步完成安装过程\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:73\nmsgid \"You will be greeted by the following \\\"About software\\\" page.\"\nmsgstr \"您将看到以下\\\"About software\\\"的页面。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:75\nmsgid \"|web install 01|\"\nmsgstr \"|web install 01|\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:180\nmsgid \"web install 01\"\nmsgstr \"web install 01\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:77\nmsgid \"Read it and then click \\\"Next\\\"\"\nmsgstr \"阅读它，然后单击\\\"Next\\\"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:79\nmsgid \"\"\n\"The setup consists of several steps that help you configure your \"\n\"installation: selecting features and providing database configuration.\"\nmsgstr \"此设置包括了帮助您配置安装的几个步骤: 功能选择和数据库配置。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:83\nmsgid \"\"\n\"Order and design of the steps may slightly differ thus we only provide a \"\n\"broad overview of how to proceed:\"\nmsgstr \"步骤的顺序和设计可能略有不同，因此我们仅提供如何继续安装的大致概述:\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:85\nmsgid \"**Advanced Clustering Strategy information**\"\nmsgstr \"**高级聚类策略信息**\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:87\nmsgid \"\"\n\"You will see some information about our commercial products and \"\n\"licensing. Please read though the agreement, and as a confirmation of \"\n\"agreement type in your name or company and click \\\"Next\\\" to go to the \"\n\"next page.\"\nmsgstr \"您将看到有关我们的商业产品和许可的一些信息。请仔细阅读协议，并用您的姓名或公司确认该协议，然后单击\\\"Next\\\"进入下一页。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:89\nmsgid \"**Basic Tigase server configuration**\"\nmsgstr \"**基本Tigase服务器配置**\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:91\nmsgid \"\"\n\"This page will look over your basic configuration settings, those include\"\n\" the server type, domain you wish to use, and gives you a chance to \"\n\"specify an administrator for the domain. Also, you will be selecting what\"\n\" type of database Tigase server will be using (configuration comes \"\n\"later).\"\nmsgstr \"此页面将查看您的基本配置设置，其中包括服务器类型，您希望使用的域，并让您有机会为域指定管理员。此外，您将选择Tigase服务器使用的数据库类型(稍后进行配置)。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:93\nmsgid \"\"\n\"If you do not specify an administrator and password, one is made for you,\"\n\" which will be admin@yourdomain and password is tigase.\"\nmsgstr \"如果您没有指定管理员和密码，则会为您自动设置一个:admin@yourdomain，其密码为tigase。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:95\nmsgid \"**Connectivity**\"\nmsgstr \"**连接**\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:97\nmsgid \"\"\n\"At this page you will be presented with a list of possible connectivity \"\n\"options supported by Tigase XMPP Server and a way to enable/disable each \"\n\"of them (desktop, mobile, http, websocket, federation, etc.). After \"\n\"making this decisions, click \\\"Next\\\".\"\nmsgstr \"\"\n\"在此页面上，您将看到Tigase XMPP服务器所支持的可能连接选项列表以及启用/禁用每个选项的方法(桌面，移动， \"\n\"http，websocket，联合等)。勾选相应选项后，单击\\\"Next\\\"。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:99\nmsgid \"**Features**\"\nmsgstr \"**特征**\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:101\nmsgid \"\"\n\"Now you will be able to select which features of Tigase XMPP Server (such\"\n\" as MUC, PubSub, MIX, MAM, Push Notifications) should be enabled or \"\n\"disabled.\"\nmsgstr \"现在您将可以选择Tigase XMPP服务器的哪些相关功能(例如MUC, PubSub, MIX, MAM,推送通知)应该被启用或被禁用。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:103\nmsgid \"At this step will also be able to enable clustering on your installation\"\nmsgstr \"在这一步也将能够在您的安装上启用集群\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:105\nmsgid \"When you will be ready, click \\\"Next\\\".\"\nmsgstr \"选择好以后，单击\\\"Next\\\"。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:107\nmsgid \"**Database configuration**\"\nmsgstr \"**数据库配置**\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:109\nmsgid \"\"\n\"This is where the database is configured. The type of database selected \"\n\"in step 3 will influence available options. **BE SURE TO SPECIFY DATABASE\"\n\" ROOT USER ACCOUNT AND PASSWORD**\"\nmsgstr \"在这里可以配置数据库。步骤3中选择的数据库类型将影响可用的选项。**请务必指定数据库根用户帐号和密码**\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:111\nmsgid \"**Database connectivity check**\"\nmsgstr \"**数据库连接检查**\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:113\nmsgid \"\"\n\"After database setup, you should see a page with executed actions and \"\n\"their results. All presented items should be \\\"green\\\", meaning that \"\n\"everything went well. If anything is presented in \\\"red\\\" or \\\"yellow\\\", \"\n\"please read description presented below this header to learn more about \"\n\"this issue. If setup is completed, click \\\"Next\\\".\"\nmsgstr \"数据库设置后，您应该会看到一个包含已执行操作及其结果的页面。所有出现的项目都应该是\\\"绿色\\\"，这意味着一切顺利。如果任何内容显示为\\\"红色\\\"或\\\"黄色\\\"，请阅读此标题下方的说明以了解有关此问题的更多信息。如果设置完成，请单击\\\"Next\\\"。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:115\nmsgid \"**Setup security**\"\nmsgstr \"**设置安全性**\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:117\nmsgid \"\"\n\"The Setup Access Page will be locked from the admin/tigase user as \"\n\"specified above. This is your chance to have the setup pages add a \"\n\"specific user in addition to admin accounts to re-access this setup \"\n\"process later. If left blank, only JIDs listed in admin will be allowed \"\n\"to access.\"\nmsgstr \"如上所述，设置访问页面将被admin/tigase用户锁定。此时你可以在设置页面添加除管理员帐户之外的特定用户，以便稍后重新访问此设置。如果留空，则仅允许admin中列出的JIDs访问。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:119\nmsgid \"**Saving configuration**\"\nmsgstr \"**保存配置**\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:121\nmsgid \"\"\n\"The installation is almost complete and you will be presented with a page\"\n\" showing what the resulting configuration (stored in ``config.tdsl`` \"\n\"file) will look like.\"\nmsgstr \"此时安装几乎快要完成，您将看到一个页面显示生成的配置(存储在 ``config.tdsl`` 文件中)将是什么样子。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:123\nmsgid \"\"\n\"If you have a custom setup, or would like to put your own settings in, \"\n\"you may copy and past the contents here to edit the current \"\n\"``config.tdsl`` file.\"\nmsgstr \"如果您是自定义设置，或者想自己进行设置，您可以复制并粘贴此处的内容以编辑当前的 ``config.tdsl`` 文件。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:125\nmsgid \"Click \\\"Save\\\" to write the file to disk.\"\nmsgstr \"单击\\\"Save\\\"将文件写入磁盘。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:127\nmsgid \"**Finished**\"\nmsgstr \"**已完成**\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:129\nmsgid \"\"\n\"You have now finished the installation, proceed to the next step to \"\n\"restart the server.\"\nmsgstr \"现在已经完成安装，继续下一步以重启服务器。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:132\nmsgid \"Restart the Server\"\nmsgstr \"重启服务器\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:134\nmsgid \"\"\n\"It is recommended at this point to stop the server manually and restart \"\n\"it using the proper script for your OS. From the Tigase base directory \"\n\"enter\"\nmsgstr \"此时，建议手动停止服务器并使用适合您操作系统的脚本重新启动它。从Tigase基本目录输入如下命令\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:145\nmsgid \"\"\n\"In order to make Tigase XMPP Server start automatically during system \"\n\"startup you should setup startup scripts as described in :ref:`Tigase \"\n\"Script Selection<tigaseScriptStart>`\"\nmsgstr \"\"\n\"为了使Tigase XMPP服务器在系统启动时自动启动，您应该按照 :ref:`Tigase脚本选择<tigaseScriptStart>` \"\n\"中的说明设置启动脚本\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:147\nmsgid \"\"\n\"To further fine tune the server you should edit ``etc/tigase.conf``. \"\n\"Ensure ``JAVA_HOME`` path is correct, and increase memory if needed using\"\n\" ``JAVA_OPTIONS`` -Xmx (max), and -Xms (initial). You will need to direct\"\n\" Tigase to read settings from this file on startup as follows.\"\nmsgstr \"\"\n\"为了进一步微调服务器 ，需要编辑 ``etc/tigase.conf``。同时确保 ``JAVA_HOME`` 的路径是正确的 , 并在需要时使用\"\n\" ``JAVA_OPTIONS`` -Xmx（最大）和 -Xms（初始）时以增加内存。您需要告诉Tigase启动时按照如下所示从该文件中读取设置。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:149\nmsgid \"\"\n\"Everything should be running smooth at this point. Check the logfiles in \"\n\"``logs/`` if you experience any problems.\"\nmsgstr \"此时一切都应该运行顺利。如果遇到任何问题，请检查 ``logs/`` 中的日志文件。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:152\nmsgid \"Verify Tigase is Running\"\nmsgstr \"确认Tigase是否正在运行\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:154\nmsgid \"You should see a list of listening ports.\"\nmsgstr \"您应该会看到一个监听端口列表。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:170\nmsgid \"Windows Instructions for using Web Installer\"\nmsgstr \"Windows使用Web安装程序的说明\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:172\nmsgid \"\"\n\"There are a few steps involved with setting up Tigase with the web \"\n\"installer in a Windows environment. Please follow this guide.\"\nmsgstr \"在Windows环境中使用Web安装程序设置Tigase涉及几个步骤。请遵循本指南。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:174\nmsgid \"\"\n\"First step is to extract the distribution archive in it’s entirety to the\"\n\" intended running directory. Once there, run the ``Setup.bat`` file \"\n\"inside the ``win-stuff`` folder. This will move the necessary files to \"\n\"the correct folders before Tigase begins operation.\"\nmsgstr \"\"\n\"第一步是将分发存档完整地提取到想要的运行目录下。在那里，运行 ``win-stuff`` 文件夹里的 ``Setup.bat`` \"\n\"文件。在Tigase开始操作之前以上操作会将需要的文件移动到正确的文件夹中。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:176\nmsgid \"\"\n\"From here, you have a few options how to run Tigase; ``run.bat`` will \"\n\"operate Tigase using a java command, or ``tigase.bat`` which will start \"\n\"Tigase using the wrapper. You may also install Tigase and run it as a \"\n\"service.\"\nmsgstr \"\"\n\"从这里开始，您可以选择不同方法来运行Tigase； ``run.bat`` 将使用java命令执行Tigase，或 ``tigase.bat`` \"\n\"将使用包装器启动Tigase。您也可以安装Tigase并将其作为服务运行。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Installation_Using_Web_Installer.inc:178\nmsgid \"\"\n\"Once this setup is finished, web installer will continue the same from \"\n\":ref:`here<connecttoWebInstall>`.\"\nmsgstr \"完成此设置后，Web安装程序将从 :ref:`这里<connecttoWebInstall>` 继续执行相同的操作。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:4\nmsgid \"Manual Installation in Console Mode\"\nmsgstr \"在控制台模式下手动安装\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:6\nmsgid \"\"\n\"Our preferred way to install the Tigase server is using `Web installer \"\n\"<#webinstall>`__ and configuration program which comes with one of the \"\n\"binary packages. Please pick up the latest version of the distribution \"\n\"archive in our `download section <https://tigase.net/downloads>`__.\"\nmsgstr \"\"\n\"我们首选的安装Tigase服务器的方法是使用 :ref:`Web安装程序<webinstall>` 和其中一个二进制包附带的配置程序。请在我们的 \"\n\"`下载部分 <https://tigase.net/downloads>`__ 中获取最新版本的分发存档。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:8\nmsgid \"\"\n\"In many cases however it is not always possible to use the web installer.\"\n\" In many cases you have just an ssh access or even a direct access in \"\n\"console mode only. We are going to provide a text-only installer in one \"\n\"of the next releases but for the time being you can use our binary \"\n\"packages to install the server manually. Please continue reading to learn\"\n\" how to install and setup the server in a few easy steps…​\"\nmsgstr \"然而，在许多情况下，并不总是可以使用Web安装程序。在许多情况下，您只有ssh访问权限，甚至只有控制台模式下的直接访问权限。我们将在下一个版本中提供纯文本安装程序，但目前您可以使用我们的二进制包手动安装服务器。请继续阅读以了解如何通过几个简单的步骤安装和设置服务器……​\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:10\nmsgid \"\"\n\"If you have an old version of the Tigase server running and working and \"\n\"you intend to upgrade it please always backup the old version first.\"\nmsgstr \"如果您有旧版本的Tigase服务器正在运行和工作，并且您打算升级它，请先备份旧版本。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:14\nmsgid \"\"\n\"Please note that these instructions are for \\\\*nix operating systems, and\"\n\" some modifications may be required for other Operating Systems!\"\nmsgstr \"请注意，这些说明适用于 \\\\*nix 操作系统，其他操作系统可能需要进行一些修改！\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:18\nmsgid \"Get the Binary Package\"\nmsgstr \"获取二进制包\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:20\nmsgid \"\"\n\"Have a look at our `download area <https://tigase.net/downloads>`__. \"\n\"Always pick the latest version of the package available. For manual \"\n\"installation either ``zip`` or ``tar.gz`` file is available. Pick one of \"\n\"files with filename looking like: ``tigase-\"\n\"server-<version>-b<build>-<type>.<archive>``, where ``<version>`` is in \"\n\"the form of ``major.minor.bugfix``, ``<type>`` can be either ``dist`` \"\n\"(basic package) or ``dist-max`` (extended set of components) and archive \"\n\"type can be eitehr ``tar.gz`` or ``zip``.\"\nmsgstr \"\"\n\"看看我们的 `下载区 <https://tigase.net/downloads>`__。始终选择可用的最新版本的软件包。对于手动安装，可以使用 \"\n\"``zip`` 或 ``tar.gz`` 文件。选择文件名如下的文件之一： ``tigase-\"\n\"server-<version>-b<build>-<type>.<archive>``，其中 ``<version>`` 的形式为 \"\n\"``major .minor.bugfix``, ``<type>`` 可以是 ``dist`` （基本包）或 ``dist-max`` \"\n\"（扩展的组件集），归档类型可以是  ``tar.gz`` or ``zip``。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:24\nmsgid \"Unpack the Package\"\nmsgstr \"打开包装\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:26\nmsgid \"Unpack the file using command for the tar.gz file:\"\nmsgstr \"使用 tar.gz 文件的命令解压缩文件：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:32\nmsgid \"or for the zip file:\"\nmsgstr \"或 zip 文件命令：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:38\nmsgid \"A new directory will be created: **tigase-server-x.y.z-bv/**.\"\nmsgstr \"将创建一个新目录：**tigase-server-x.y.z-bv/**。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:40\nmsgid \"\"\n\"Sometimes after unpacking package on unix system startup script doesn’t \"\n\"have execution permissions. To fix the problem you have to run following \"\n\"command:\"\nmsgstr \"有时在 unix 系统启动脚本上解包后没有执行权限。要解决此问题，您必须运行以下命令：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:48\nmsgid \"Prepare Configuration\"\nmsgstr \"准备配置\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:50\nmsgid \"If you look inside the new directory, it should like this output:\"\nmsgstr \"如果您查看新目录，它应该像这样的输出：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:70\nmsgid \"At the moment the most important is the etc/ directory with these files:\"\nmsgstr \"目前最重要的是包含这些文件的 etc/ 目录：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:88\nmsgid \"Configure tigase.conf\"\nmsgstr \"配置tigase.conf\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:90\nmsgid \"\"\n\"Tigase.conf is a file that contains general program operating parameters,\"\n\" and java settings for Tigase to run. For now, the only setting we need \"\n\"to set is the **JAVA_HOME** directory.\"\nmsgstr \"\"\n\"Tigase.conf 是一个文件，其中包含了普遍程序的运行参数，以及运行 Tigase 的 java 设置。目前，我们需要配置的唯一设置是 \"\n\"**JAVA_HOME** 目录。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:96\nmsgid \"\"\n\"Replace **${JDKPath}** with a path to Java JDK installation on your \"\n\"system.\"\nmsgstr \"将 **${JDKPath}** 替换为系统上安装 Java JDK 的路径。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:100\nmsgid \"Configure config.tdsl\"\nmsgstr \"配置 config.tdsl\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:102\nmsgid \"\"\n\"You need also to edit the ``config.tdsl`` file. It contains initial \"\n\"parameters normally set by the configuration program. As this is a manual\"\n\" installation, you will have to edit this document yourself. It contains \"\n\"already a few lines:\"\nmsgstr \"您还需要编辑``config.tdsl``文件。它包含通常由配置程序设置的初始参数。由于这是手动安装，您必须自己编辑此文档。它已经包含如下几行：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:115\nmsgid \"You will need to set a few things in order to get Tigase up and running.\"\nmsgstr \"您需要设置一些东西才能启动并运行 Tigase。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:119\nmsgid \"Step 1: Change config-type\"\nmsgstr \"第 一 步：更改配置类型\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:122\nmsgid \"\"\n\"Refer to `config-type <#configType>`__ property description for details, \"\n\"but for most operations, change ``setup`` to ``default``.\"\nmsgstr \"详情参考 `config-type <#configType>`__ 属性说明，但对于大多数操作，将``setup``更改为``default``。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:125\nmsgid \"Step 2: Set virtual host\"\nmsgstr \"第二步：设置虚拟主机\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:127\nmsgid \"\"\n\"Without a virtual host, your XMPP server has no domain with which to \"\n\"operate. To set a virtual host use the following configuration:\"\nmsgstr \"如果没有虚拟主机，您的 XMPP 服务器就没有可操作的域。要设置虚拟主机，请使用以下配置：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:133\nmsgid \"\"\n\"You have to replace ``hostname`` with a domain name used for your XMPP \"\n\"installation. Let’s say this is **jabber.your-great.net**. Your setting \"\n\"should look like this:\"\nmsgstr \"\"\n\"您必须将 ``hostname`` 替换为用于 XMPP 安装的域名。假设这个是 **jabber.your-\"\n\"great.net**。您的设置应如下所示：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:139\nmsgid \"\"\n\"There are many other settings that can be configured :ref:`visit this \"\n\"section for details<tigase41virtualHosts>`.\"\nmsgstr \"还可以配置许多其他设置， :ref:`访问此部分了解详细信息<tigase41virtualHosts>`\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:142\nmsgid \"Step 3: Set Administrators\"\nmsgstr \"第三步：设置管理员\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:145\nmsgid \"\"\n\"At least one administrator is required, and once the database is setup \"\n\"will have the default password of ``tigase``. Be sure to change this once\"\n\" you have finished setting up your server. To add admins, use the \"\n\"following line in the ``config.tdsl`` file:\"\nmsgstr \"\"\n\"至少需要一名管理员，并且一旦数据库设置完毕，默认密码将是 ``tigase``。完成设置服务器后，请务必更改此设置。要添加管理员，请在 \"\n\"``config.tdsl`` 文件中使用以下行：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:151\nmsgid \"Step 4: Set databases\"\nmsgstr \"第四步：设置数据库\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:153\nmsgid \"\"\n\"You will also need to configure connection to the database. First you \"\n\"have to decide what database you want to use: ``Derby``, ``MySQL``, \"\n\"``PostgreSQL``, ``MSSQL``, or ``MondoDB``. Each database will have \"\n\"slightly different configurations. If we are using derby, in a directory \"\n\"called ``tigasedb``, your configuration would look like this:\"\nmsgstr \"\"\n\"您还需要配置与数据库的连接。首先，您必须决定要使用什么数据库：``Derby``，``MySQL``，``PostgreSQL``，``MSSQL``\"\n\" 或 ``MondoDB``。每个数据库的配置都会略有不同。如果我们使用 derby，在名为 ``tigasedb`` \"\n\"的目录中，您的配置将如下所示：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:163\nmsgid \"\"\n\"Consult :ref:`dataSource<dataSource>` property for more configuration \"\n\"info.\"\nmsgstr \"有关更多配置信息，请参阅 :ref:`dataSource<dataSource>` 属性。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:165\nmsgid \"\"\n\"This is enough basic configuration to have your Tigase server \"\n\"installation running.\"\nmsgstr \"这是足以让您的 Tigase 服务器安装运行的基本配置。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:169\nmsgid \"Install Database\"\nmsgstr \"安装数据库\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:171\nmsgid \"\"\n\"Creating the database is the next step. Previously, we had scripts to \"\n\"handle this process, but we now have the advantage of functions in the \"\n\"``tigase.sh`` script that can be used. Setting up the database can now be\"\n\" done using a single command.\"\nmsgstr \"\"\n\"下一步是创建数据库。以前，我们有脚本来处理这个过程，但我们现在拥有函数的优势，其可以在 ``tigase.sh`` \"\n\"脚本中使用。现在设置数据库可以使用单个命令来完成。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:177\nmsgid \"\"\n\"This command will install tigase using a Derby database on one named \"\n\"``tigasedb`` hosted on ``localhost``. The username and password editing \"\n\"the database is ``tigase_pass`` and ``root``. Note that ``-J`` explicitly\"\n\" adds the administrator, this is highly recommended with the ``-N`` \"\n\"passing the password. You may customize this command as needed, refer to \"\n\"the :ref:`install-schema<install-schema>` section of the documentation \"\n\"for more information.\"\nmsgstr \"\"\n\"此命令将使用 在 ``localhost`` 上托管的名为 ``tigasedb`` 的Derby \"\n\"数据库上安装tigase。编辑数据库的用户名和密码是 ``tigase_pass`` 和 ``root``。请注意，``-J`` \"\n\"显式添加管理员，强烈建议使用 ``-N`` 传递密码。您可以根据需要自定义此命令，有关详细信息，请参阅文档的 :ref:`install-\"\n\"schema<install-schema>` 部分。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:179\nmsgid \"On a windows system, you need to call the program directly:\"\nmsgstr \"在windows系统上，需要直接调用程序：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:185\nmsgid \"If this successfully passes, you should see some information printed out\"\nmsgstr \"如果成功通过，您应该会看到一些信息被打印出来\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:279\nmsgid \"\"\n\"Note at the end, the script will output a recommended example file. You \"\n\"may use this in conjunction with your written config file, but some \"\n\"settings may not be set using this configuration. Again, it is only an \"\n\"**EXAMPLE**.\"\nmsgstr \"\"\n\"最后注意，脚本会输出一个推荐的示例文件。您可以将其与您编写的配置文件结合使用，但某些设置可能无法使用此配置进行设置。再说一遍，它只是一个 \"\n\"**示例**。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:285\nmsgid \"\"\n\"You can start the server using the tigase file found in the scripts sub-\"\n\"directory of Tigase server base directory. There, select the type of \"\n\"linux you have, debian, gentoo, mendriva or redhat. In the root server \"\n\"directory type the following command:\"\nmsgstr \"您可以使用Tigase服务器基本目录的scripts子目录中的tigase文件来启动服务器。在那里，选择您拥有的linux类型，debian，gentoo，mendriva或redhat。在根服务器目录中键入以下命令：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:291\nmsgid \"Where {OS} is your \\\\*nix operating system.\"\nmsgstr \"其中 {OS} 是您的 \\\\*nix 操作系统。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:293\nmsgid \"and you should get the output like this:\"\nmsgstr \"你应该得到这样的输出：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:302\nmsgid \"Check if it is Working\"\nmsgstr \"检查它是否工作\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:304\nmsgid \"\"\n\"The server is started already but how do you know if it is really working\"\n\" and there were no problems. Have a look in the ``logs/`` directory. \"\n\"There should be a few files in there:\"\nmsgstr \"服务器已经启动，但是你怎么知道它是否真的在工作并且没有问题。查看 ``logs/`` 目录。里面应该有以下几个文件：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:315\nmsgid \"\"\n\"The first 2 files are the most interesting for us: **tigase-console.log**\"\n\" and **tigase.log.0**. The first one contains very limited information \"\n\"and only the most important entries. Have a look inside and check if \"\n\"there are any **WARNING** or **SEVERE** entries. If not everything should\"\n\" be fine.\"\nmsgstr \"\"\n\"前 2 个文件对我们来说是最有趣的：**tigase-console.log** 和 \"\n\"**tigase.log.0**。第一个包含非常有限的信息，其中只有最重要的条目。查看内部并检查是否有任何 **WARNING** 或 \"\n\"**SEVERE** 条目。如果不是一切都应该没问题。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Manual_Installation_in_Console_Mode.inc:317\nmsgid \"\"\n\"Now you can connect with an XMPP client of your choice with the \"\n\"administrator account you setup earlier.\"\nmsgstr \"现在，您可以使用之前设置的管理员帐户连接您选择的 XMPP 客户端。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:4\nmsgid \"Windows Installation\"\nmsgstr \"Windows安装\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:6\nmsgid \"\"\n\"Tigase XMPP Server can also work on Microsoft Windows systems and \"\n\"servers, although some slight modifications may be necessary to get \"\n\"things ready to run.\"\nmsgstr \"\"\n\"尽管可能需要进行一些细微的修改才能让一切准备好运行，但是Tigase XMPP Server 也可以在 Microsoft Windows \"\n\"系统和服务器上运行。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:8\nmsgid \"\"\n\"Although you may wish to use command line, take note that commands \"\n\"entered in shell may require quotations in some cases.\"\nmsgstr \"尽管您可能希望使用命令行，但请注意，在 shell 中输入的命令在某些情况下可能需要引号。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:10\nmsgid \"\"\n\"Make sure that you have Java JDK v8 installed on your system prior to \"\n\"installing Tigase. It will also help to fully setup whatever database \"\n\"software you will be using as well.\"\nmsgstr \"在安装 Tigase 之前，请确保您的系统上安装了 Java JDK v8。它还将有助于完全设置您将使用的任何数据库软件。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:13\nmsgid \"Step 1: Initial Setup\"\nmsgstr \"第 一步：初始设置\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:15\nmsgid \"\"\n\"Download the Tigase XMPP Server archive from `our repository \"\n\"<https://tigase.net/downloads>`__ and extract it to a directory of your \"\n\"choice.\"\nmsgstr \"\"\n\"从 `我们的存储库 <https://tigase.net/downloads>`__ 下载 Tigase XMPP \"\n\"服务器存档并将其解压缩到您选择的目录。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:17\nmsgid \"\"\n\"Once that is completed, enter the directory ``win-stuff`` and run the \"\n\"setup.bat program. This program when run, will extract the necessary \"\n\"files to appropriate places on your computer. The bat file should look \"\n\"like the following:\"\nmsgstr \"\"\n\"一旦完成，进入目录 ``win-stuff`` 并运行 setup.bat 程序。该程序在运行时会将必要的文件提取到计算机上的适当位置。 bat \"\n\"文件应如下所示：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:30\nmsgid \"Step 2: Starting Server\"\nmsgstr \"第 二 步：启动服务器\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:32\nmsgid \"\"\n\"To start the server you may use a command prompt from the installation \"\n\"directory\"\nmsgstr \"要启动服务器，您可以使用安装目录中的命令提示符\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:66\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:10\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:40\nmsgid \"**Note**\"\nmsgstr \"**注意**\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:42\nmsgid \"\"\n\"this may freeze the command window, and will only display output from \"\n\"Tigase.\"\nmsgstr \"这可能会使得命令窗口停住不动，并且只会显示 Tigase 的输出。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:44\nmsgid \"Or you may run wrapper.exe or tigase.bat from the GUI.\"\nmsgstr \"或者，您可以从 GUI 运行 wrapper.exe 或 tigase.bat。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:47\nmsgid \"2A: Installing as a service\"\nmsgstr \"2A：作为服务安装\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:49\nmsgid \"\"\n\"The cleanest way to operate Tigase in a Windows environment is to install\"\n\" Tigase as a Service by running the InstallTigaseService.bat program. \"\n\"This will install Tigase as a system service, and now the server can be \"\n\"controlled from the services.msc panel. This allows for stopping, \"\n\"starting, and pausing of Tigase XMPP Server and allowing for graceful \"\n\"shutdowns.\"\nmsgstr \"\"\n\"在 Windows 环境中操作 Tigase 最简洁的方法是通过运行 InstallTigaseService.bat 程序安装 \"\n\"Tigase。这会将 Tigase 安装为系统服务，现在可以从 services.msc 面板控制服务器。从而允许停止，启动和暂停 Tigase \"\n\"XMPP 服务器并允许正常关闭。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:51\nmsgid \"\"\n\"For a basic installation, MySQL is recommended over Derby DB. For that \"\n\"purpose, we have included a basic installation guide for MySQL on Windows\"\n\" systems here:\"\nmsgstr \"对于基本安装，建议使用 MySQL 而不是 Derby DB。为此，我们在此处包括了 Windows 系统上 MySQL 的基本安装指南：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:54\nmsgid \"MySQL Database Installation\"\nmsgstr \"MySQL 数据库安装\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:56\nmsgid \"\"\n\"The section describes installation and configuration of the MySQL \"\n\"database to work with Tigase server.\"\nmsgstr \"本节介绍 MySQL 数据库的安装和配置以使其与 Tigase 服务器一起使用。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:58\nmsgid \"\"\n\"Download the binary package from MySQL download area at `mysql.com \"\n\"<http://dev.mysql.com/downloads/mysql/5.0.html#win32>`__. Make sure you \"\n\"select executable proper for your operating system.\"\nmsgstr \"\"\n\"从 `mysql.com <http://dev.mysql.com/downloads/mysql/5.0.html#win32>`__ 的 \"\n\"MySQL 下载区下载二进制包。确保选择适合您的操作系统的可执行文件。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:60\nmsgid \"\"\n\"Run the installation program and follow default installation steps. When \"\n\"the installation is complete find the MySQL elements in the Windows Start\"\n\" menu and run the MySQL Configuration Wizard. Follow the wizard and make \"\n\"sure to check settings against the screenshots in the guide below.\"\nmsgstr \"\"\n\"运行安装程序并遵循默认安装步骤。安装完成后，在 Windows 开始菜单中找到 MySQL 元素并运行 MySQL \"\n\"配置向导。按照向导进行操作，并确保根据以下指南中的屏幕截图检查设置。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:62\nmsgid \"In Welcome window just press 'Next'.(pic.1)\"\nmsgstr \"在欢迎窗口中，只需按'Next'。（图 1）\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:64\nmsgid \"|sql1|\"\nmsgstr \"|sql1|\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:134\nmsgid \"sql1\"\nmsgstr \"sql1\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:66\nmsgid \"\"\n\"In the next window select option: 'Detailed Configuration' and press \"\n\"'Next' (pic. 2)\"\nmsgstr \"在下一个窗口中选择 'Detailed Configuration' 并点击 'Next' (pic. 2)\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:68\nmsgid \"|sql2|\"\nmsgstr \"|sql2|\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:135\nmsgid \"sql2\"\nmsgstr \"sql2\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:70\nmsgid \"\"\n\"On the next screen select option: 'Server Machine' and press 'Next' (pic.\"\n\" 3)\"\nmsgstr \"在下一个屏幕上选择选项: 'Server Machine' 并点击 'Next' (pic. 3)\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:72\nmsgid \"|sql3|\"\nmsgstr \"|sql3|\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:136\nmsgid \"sql3\"\nmsgstr \"sql3\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:74\nmsgid \"\"\n\"On the forth windows leave the default\\\" 'Multi-functional Database' and \"\n\"press 'Next' (pic. 4)\"\nmsgstr \"在第四个窗口保留默认选项 'Multi-functional Database' 并点击 'Next' (pic. 4)\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:76\nmsgid \"|sql4|\"\nmsgstr \"|sql4|\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:137\nmsgid \"sql4\"\nmsgstr \"sql4\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:78\nmsgid \"On the step number five just press 'Next' using defaults. (pic. 5)\"\nmsgstr \"在第五步上，只需使用默认值按 'Next' . (pic. 5)\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:80\nmsgid \"|sql5|\"\nmsgstr \"|sql5|\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:138\nmsgid \"sql5\"\nmsgstr \"sql5\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:82\nmsgid \"\"\n\"Again, on window 6 select the default - 'Decision Support (DSS)/OLAP' and\"\n\" press 'Next' (pic.6)\"\nmsgstr \"同样，在窗口 6 上选择默认值 - 'Decision Support (DSS)/OLAP' 并点击 'Next'（图 6）\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:84\nmsgid \"|sql6|\"\nmsgstr \"|sql6|\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:139\nmsgid \"sql6\"\nmsgstr \"sql6\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:86\nmsgid \"Make sure you switch OFF the 'Strict mode' and and press 'Next' (pic. 7)\"\nmsgstr \"确保关闭 'Strict mode' ，然后点击'Next'（图 7）\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:88\nmsgid \"|sql7|\"\nmsgstr \"|sql7|\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:140\nmsgid \"sql7\"\nmsgstr \"sql7\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:90\nmsgid \"\"\n\"On the character encoding page select: 'Manual Selected Default Character\"\n\" set/ Collation' and 'utf8', press 'Next' (pic.8)\"\nmsgstr \"\"\n\"在字符编码页面上选择 'Manual Selected Default Character set/ Collation' 和 'utf8', \"\n\"然后点击 'Next' (pic.8)\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:92\nmsgid \"|sql8|\"\nmsgstr \"|sql8|\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:141\nmsgid \"sql8\"\nmsgstr \"sql8\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:94\nmsgid \"\"\n\"On next window select 'Include Bin Directory in Windows PATH' and press \"\n\"'Next' (pic.9)\"\nmsgstr \"在下一个窗口中选择 'Include Bin Directory in Windows PATH' 然后点击 'Next' (pic.9)\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:96\nmsgid \"|sql9|\"\nmsgstr \"|sql9|\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:142\nmsgid \"sql9\"\nmsgstr \"sql9\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:98\nmsgid \"\"\n\"On this window just enter the database super user password and make sure \"\n\"you remember it. When ready press 'Next' (pic. 10)\"\nmsgstr \"在此窗口中，只需输入数据库超级用户密码并确保记住它。准备好后点击'Next' (pic. 10)\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:100\nmsgid \"|sql10|\"\nmsgstr \"|sql10|\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:143\nmsgid \"sql10\"\nmsgstr \"sql10\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:102\nmsgid \"\"\n\"This is the last screen. Press 'Execute' to save the configuration \"\n\"parameters. (pic. 11)\"\nmsgstr \"这是最后一个画面。点击 'Execute' 并保存配置参数 (pic. 11)。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:104\nmsgid \"|sql11|\"\nmsgstr \"|sql11|\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:144\nmsgid \"sql11\"\nmsgstr \"sql11\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:106\nmsgid \"\"\n\"When the configuration is saved you can repeat all the steps and change \"\n\"settings at any time by running: **START ⇒ Programs ⇒ MYSQL⇒ MYSQL serwer\"\n\" machine⇒ MySQL Server Instance Config Wizard**\"\nmsgstr \"\"\n\"保存配置后，您可以通过运行重复所有的步骤并随时更改设置：**START ⇒ Programs ⇒ MYSQL⇒ MYSQL serwer \"\n\"machine⇒ MySQL Server Instance Config Wizard**\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:108\nmsgid \"\"\n\"Now we have to setup Tigase database. From the Start menu run the MySQL \"\n\"console and enter all commands below finishing them with **<ENTER>**:\"\nmsgstr \"现在我们必须设置 Tigase 数据库。从开始菜单运行 MySQL 控制台并输入下面的所有命令，并用 **<ENTER>** 完成它们：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:110\nmsgid \"Create the database:\"\nmsgstr \"创建数据库：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:116\nmsgid \"Add database user:\"\nmsgstr \"添加数据库用户：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:125\nmsgid \"Load Tigase database schema:\"\nmsgstr \"加载 Tigase 数据库架构：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Windows_Installation.inc:132\nmsgid \"\"\n\"When the system is up and running you can connect with any XMPP client \"\n\"(Psi for example) to your server to see if it is working.\"\nmsgstr \"当系统启动并运行时，您可以将任何 XMPP 客户端（例如 Psi）连接到您的服务器，以查看它是否正常工作。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:4\nmsgid \"Tigase Server Network Instructions\"\nmsgstr \"Tigase 服务器网络说明\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:6\nmsgid \"\"\n\"One you have installed Tigase XMPP Server on a machine, you’re going to \"\n\"want to use it. If you are just using for local communications on a \"\n\"network behind a router, you’re all set. Enjoy and use!\"\nmsgstr \"\"\n\"一旦你已经在一台机器上安装了 Tigase XMPP \"\n\"服务器，你j就会想要使用它。如果您只是在路由器后面的网络上用于本地交流，那么您已经准备就绪。享受和使用吧！\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:8\nmsgid \"\"\n\"However, if you want to have people from other computers outside your \"\n\"network connect to your server, you’re going to have to go through a few \"\n\"more steps to show your server out to the public.\"\nmsgstr \"但是，如果您想让你的网络外其他计算机的人连接到您的服务器，您将不得不通过几个其他步骤将您的服务器向公众展示。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:12\nmsgid \"\"\n\"This guide is merely a recommendation of how to get a local server to be \"\n\"open to incoming communications. Any time you open ports, or take other \"\n\"security measures you risk compromising your network security. These are \"\n\"only recommendations, and may not be appropriate for all installations. \"\n\"Please consult your IT Security expert for securing your own \"\n\"installation.\"\nmsgstr \"\"\n\"本指南只是仅仅给出了如何把本地服务器开放给传入的通信的建议。每当您打开端口或采取其他安全措施时，您就有可能危及网络安全。这些只是建议，可能并不适用于所有安装。请咨询您的\"\n\" IT 安全专家以保护您自己的安装。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:14\nmsgid \"\"\n\"XMPP, being a decentralized communication method, relies on proper DNS \"\n\"records to figure out where and how an XMPP server is setup. Operating an\"\n\" XMPP Server will require you to properly setup DNS routing so not only \"\n\"can clients connect to you, but if you decide to run a federated server \"\n\"and enable server to server communication, you will need to do the same. \"\n\"If you already have a DNS server already, you should have little issue \"\n\"adding these records. If you do not have a DNS setup pointing to your \"\n\"server, you may use a free dynamic name service such as dynu.com.\"\nmsgstr \"\"\n\"XMPP 是一种去中心化的通信方法，其依赖于适当的 DNS 记录以确定 XMPP 服务器的设置位置和方式。操作 XMPP 服务器需要您正确设置 \"\n\"DNS 路由器，以便客户端不仅可以连接到您，而且如果您决定运行联合服务器并启用服务器到服务器的通信，您也需要这样做。如果您已经有一个 DNS \"\n\"服务器，那么添加这些记录应该没什么问题。如果您没有指向您的服务器的 DNS 设置，您可以使用免费的动态名称服务，例如 dynu.com。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:17\nmsgid \"A Records\"\nmsgstr \"A记录\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:19\nmsgid \"\"\n\"You will not be able to use an IP Address or a CNAME record to setup an \"\n\"XMPP Server. While it’s not required, an A record can provide some other \"\n\"benefits such serving as a backup in case the SRV record is not \"\n\"configured right.\"\nmsgstr \"\"\n\"您将无法使用 IP 地址或 CNAME 记录来设置 XMPP 服务器。虽然这不是必需的，但 是A 记录可以提供一些其他好处，例如在 SRV \"\n\"记录配置不正确的情况下用作备份。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:22\nmsgid \"SRV Records\"\nmsgstr \"SRV 记录\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:24\nmsgid \"\"\n\"You will need to set SRV records both for client-to-server (c2s) \"\n\"communication and, if you plan to use it, server to server (s2s) \"\n\"communication. We recommend both records are entered for every server as \"\n\"some resources or clients will check for both records. For this example \"\n\"we will use tigase.org is our domain, and xmpp as the xmpp server \"\n\"subdomain.\"\nmsgstr \"\"\n\"您需要为客户端到服务器 (c2s) 通信设置 SRV 记录，如果您打算使用它，还需要为服务器到服务器 (s2s) 通信设置 SRV \"\n\"记录。我们建议为每台服务器输入两条记录，因为某些资源或客户端会检查两条记录。对于这个例子，我们将用 tigase.org 作为我们的域，而 \"\n\"xmpp 作为 xmpp 服务器子域。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:26\nmsgid \"SRV records have the following form:\"\nmsgstr \"SRV 记录具有以下形式：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:32\nmsgid \"The key is as follows:\"\nmsgstr \"以下是关键点：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:34\nmsgid \"\"\n\"``service``: is the symbolic name of the desired service, in this case it\"\n\" would be *xmpp-client* or *xmpp-server*.\"\nmsgstr \"``service``: 是所需服务的符号名称，在本例中为 *xmpp-client* 或 *xmpp-server*。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:36\nmsgid \"\"\n\"``protocol``: is the transport protocol, either TCP or UDP, XMPP traffic \"\n\"will take place over *TCP*.\"\nmsgstr \"``protocol``: 是传输协议，无论是 TCP 还是 UDP，XMPP 流量将通过 *TCP* 进行。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:38\nmsgid \"\"\n\"``name``: the domain name where the server resides, in this case \"\n\"*tigase.org*.\"\nmsgstr \"``name``: 服务器所在的域名，在本例中为 *tigase.org*。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:40\nmsgid \"\"\n\"``TTL``: a numeric value for DNS time to live in milliseconds, by default\"\n\" use *86400*.\"\nmsgstr \"``TTL``: DNS 存在时间的数值，以毫秒为单位，默认使用 *86400*。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:42\nmsgid \"``class``: DNS class field, this is always *IN*.\"\nmsgstr \"``class``: DNS 类字段，始终为 *IN*。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:44\nmsgid \"\"\n\"``priority``: the priority of the target host with lower numbers being \"\n\"higher priority. Since we are not setting up multiple SRV records, we can\"\n\" use *0*.\"\nmsgstr \"``priority``: 目标主机的优先级，数字越小优先级越高。由于我们并没有设置多个 SRV 记录，我们可以使用 *0*。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:46\nmsgid \"\"\n\"``weight``: the relative weight for records with the same priority. We \"\n\"can use *5*.\"\nmsgstr \"``weight``：具有相同优先级的记录的相对权重。我们可以使用*5*。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:48\nmsgid \"\"\n\"``port``: the specific TCP or UDP port where the service can be found. In\"\n\" this case it will be *5222* or *5269*.\"\nmsgstr \"``port``: 可以找到服务的特定 TCP 或 UDP 端口。在这种情况下，它将是 *5222* 或 *5269*。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:50\nmsgid \"\"\n\"``target``: the hostname of the machine providing the service, here we \"\n\"will use *xmpp.tigase.org*.\"\nmsgstr \"``target``：提供服务的机器的主机名，这里我们将使用 *xmpp.tigase.org*。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:52\nmsgid \"For our example server, the SRV records will then look like this:\"\nmsgstr \"对于我们的示例服务器，SRV 记录将如下所示：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:60\nmsgid \"Tigase and Vhosts\"\nmsgstr \"Tigase 和 Vhosts\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:62\nmsgid \"\"\n\"If you are running multiple vhosts or subdomains that you wish to \"\n\"separate, you will need another record. In this case an A record will be \"\n\"all you need if you are using default ports. If you are using custom \"\n\"ports, you will need to have a new SRV record for each subdomain.\"\nmsgstr \"\"\n\"如果您正在运行多个要分离的虚拟主机或子域，则需要另一条记录。在这种情况下，如果您使用默认端口，您将只需要一条 A \"\n\"记录。如果您使用自定义端口，则需要为每个子域创建一条新的 SRV 记录。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:65\nmsgid \"Hosting via Tigase.me\"\nmsgstr \"通过 Tigase.me 托管\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:67\nmsgid \"\"\n\"If you don’t want to do all the hosting yourself, you can still have an \"\n\"XMPP service running in your own domain. The only condition right now is \"\n\"proper DNS service record (SRV) configuration that point to the following\"\n\" DNS address: ``tigase.me``.\"\nmsgstr \"\"\n\"如果您不想自己进行所有托管，您仍然可以在自己的域中运行 XMPP 服务。现在唯一的条件是将正确的 DNS 服务记录 (SRV) 配置指向以下 \"\n\"DNS 地址：``tigase.me``。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:69\nmsgid \"\"\n\"We highly encourage using SRV records. If you want to register: **your-\"\n\"domain.tld** on our XMPP service make sure that it resolves correctly:\"\nmsgstr \"我们强烈鼓励使用 SRV 记录。如果您想在我们的 XMPP 服务上注册：**your-domain.tld**，请确保它正确解析：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:72\nmsgid \"Service\"\nmsgstr \"服务\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:72\nmsgid \"DNS Type\"\nmsgstr \"DNS 类型\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:72\nmsgid \"DNS record\"\nmsgstr \"DNS 记录\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:72\nmsgid \"Comment\"\nmsgstr \"评论\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:74\nmsgid \"``_xmpp-client._tcp.your-domain.tld``\"\nmsgstr \"``_xmpp-client._tcp.your-domain.tld``\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:74\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:76\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:78\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:80\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:82\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:84\nmsgid \"SRV\"\nmsgstr \"SRV\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:74\nmsgid \"``10 0 5222 tigase.me.``\"\nmsgstr \"``10 0 5222 tigase.me.``\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:74\nmsgid \"Basic XMPP\"\nmsgstr \"基本 XMPP\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:76\nmsgid \"``_xmpps-client._tcp.your-domain.tld``\"\nmsgstr \"``_xmpps-client._tcp.your-domain.tld``\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:76\nmsgid \"``10 0 5223 tigase.me.``\"\nmsgstr \"``10 0 5223 tigase.me.``\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:76\nmsgid \"DirectTLS\"\nmsgstr \"DirectTLS\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:78\nmsgid \"``_xmpp-server._tcp.your-domain.tld``\"\nmsgstr \"``_xmpp-server._tcp.your-domain.tld``\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:78\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:80\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:82\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:84\nmsgid \"``10 0 5269 tigase.me.``\"\nmsgstr \"``10 0 5269 tigase.me.``\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:78\nmsgid \"Federation / s2s connection\"\nmsgstr \"联盟/s2s 连接\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:80\nmsgid \"``_xmpp-server._tcp.muc.your-domain.tld``\"\nmsgstr \"``_xmpp-server._tcp.muc.your-domain.tld``\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:80\nmsgid \"Federation / s2s connection (MUC)\"\nmsgstr \"联盟/s2s 连接 (MUC)\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:82\nmsgid \"``_xmpp-server._tcp.mix.your-domain.tld``\"\nmsgstr \"``_xmpp-server._tcp.mix.your-domain.tld``\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:82\nmsgid \"Federation / s2s connection (MIX)\"\nmsgstr \"联盟/s2s 连接 (MIX)\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:84\nmsgid \"``_xmpp-server._tcp.pubsub.your-domain.tld``\"\nmsgstr \"``_xmpp-server._tcp.pubsub.your-domain.tld``\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:84\nmsgid \"Federation / s2s connection (PubSub)\"\nmsgstr \"联盟/s2s 连接 (PubSub)\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:91\nmsgid \"\"\n\"You can check if the configuration is correct by issuing following \"\n\"commands:\"\nmsgstr \"您可以通过发出以下命令来检查配置是否正确：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:101\nmsgid \"Now, how do you register your domain with our service?\"\nmsgstr \"现在，您如何使用我们的服务注册您的域名？\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:103\nmsgid \"\"\n\"There are a few ways. We recommend checking with the `Add and Manage \"\n\"Domains <#addManageDomain>`__ section of the documentation on setting \"\n\"that up. If you cannot or don’t want to do it on your own, the way \"\n\"described in the guide please send us a message, either via XMPP to \"\n\"admin@tigase.im or the contact form requesting new domain. User \"\n\"registration is available via in-band registration protocol. You can also\"\n\" specify whether you want to allow anonymous authentication to be \"\n\"available for your domain and you can specify maximum number of users for\"\n\" your domain.\"\nmsgstr \"\"\n\"有几种方法。我们建议您查看文档中的 `添加和管理域 <#addManageDomain>`__ \"\n\"部分以了解设置。如果您不能或不想自己做，请按照指南中描述的方式向我们发送消息，或通过 XMPP 到 admin@tigase.im \"\n\"或联系表格请求新域。用户注册可通过带内注册协议获得。您还可以指定是否允许匿名身份验证可用于您的域，并且可以指定域的最大用户数。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:107\nmsgid \"Providing certificate\"\nmsgstr \"提供证书\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:109\nmsgid \"\"\n\"It’s also encouraged to provide dedicated SSL certificate - there are \"\n\"various ways to do it and they are described in :ref:`Installing/Loading \"\n\"Certificate To the Tigase Server<InstallingSSLCertificate>`. You may want\"\n\" to take advantage of free Let’s Encrypt certificates and automate whole \"\n\"upload and renewal process as described in :ref:`Installing LetsEncrypt \"\n\"Certificates in Your Linux System<LetsEncryptCertificate>`\"\nmsgstr \"\"\n\"还鼓励提供专用的 SSL 证书 - 有多种方法可以做到这一点，它们在 :ref:`向 Tigase \"\n\"服务器安装/加载证书<InstallingSSLCertificate>` 中进行了描述。您可能希望利用免费的 Let's Encrypt \"\n\"证书并自动化整个上传和续订过程，如 :ref:`在 Linux 系统中安装 LetsEncrypt \"\n\"证书<LetsEncryptCertificate>`\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:113\nmsgid \"Checking setup\"\nmsgstr \"检查设置\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:115\nmsgid \"\"\n\"If you have a cell phone on a separate network with an XMPP client, you \"\n\"can now try to login to test the server. If that is not handy, you can \"\n\"use an online tool to check proper DNS records such as kingant’s: \"\n\"https://kingant.net/check_xmpp_dns/ and it will tell you if anything is \"\n\"missing.\"\nmsgstr \"\"\n\"如果您在单独的网络上使用带有 XMPP 客户端的手机，您现在可以尝试登录以测试服务器。如果这不方便，您可以使用在线工具检查正确的 DNS \"\n\"记录，例如 kingant 的：https://kingant.net/check_xmpp_dns/，它会告诉您是否缺少任何内容。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:119\nmsgid \"Ports description\"\nmsgstr \"端口说明\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:122\nmsgid \"\"\n\"Once your server is setup, you may need to open at least two ports. By \"\n\"default XMPP communication happens on ports 5222/5269, to which point SRV\"\n\" records. Other ports used by the server are:\"\nmsgstr \"\"\n\"一旦设置服务器，您可能需要打开至少两个端口。默认情况下，XMPP 通信发生在端口 5222/5269 上，SRV \"\n\"记录到该端口。服务器使用的其他端口如下：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:124\nmsgid \"``3478`` - TURN or STUN, plain socket, TCP and UDP\"\nmsgstr \"``3478`` - TURN 或 STUN，普通套接字，TCP 和 UDP\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:126\nmsgid \"``5349`` - TURN or STUN, over TLS, TCP and UDP\"\nmsgstr \"``5349`` — TURN 或 STUN，通过 TLS、TCP 和 UDP\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:128\nmsgid \"``5222`` - incoming client to server XMPP connections\"\nmsgstr \"``5222`` - 进来的客户端到服务器 XMPP 连接\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:130\nmsgid \"\"\n\"``5223`` - incoming client to server XMPP connections over TLS/SSL, \"\n\"including DirectTLS\"\nmsgstr \"``5223`` - 通过 TLS/SSL 进入客户端到服务器 XMPP 连接，包括 DirectTLS\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:132\nmsgid \"``5269`` - default s2s port, i.e.: federation support\"\nmsgstr \"``5269`` - 默认 s2s 端口，即：联合支持\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:134\nmsgid \"``5277`` - inter-cluster communication\"\nmsgstr \"``5277`` - 集群间通信\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:136\nmsgid \"``5280`` - default BOSH connections\"\nmsgstr \"``5280`` - 默认 BOSH 连接\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:138\nmsgid \"``5290`` - default WebSocket connections\"\nmsgstr \"``5290`` - 默认 WebSocket 连接\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:140\nmsgid \"``5291`` - default WebSocket connections over TLS/SSL\"\nmsgstr \"``5291`` - 通过 TLS/SSL 的默认 WebSocket 连接\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:142\nmsgid \"\"\n\"``8080`` - for HTTP server (web-based setup, REST API, file upload \"\n\"extension, etc.)\"\nmsgstr \"``8080`` - 用于 HTTP 服务器（基于 Web 的设置，REST API，文件上传扩展等）\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:144\nmsgid \"``9050`` - JMX Monitoring\"\nmsgstr \"``9050`` - JMX 监控\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:146\nmsgid \"\"\n\"If for any reason you can’t use default ports and have to change them \"\n\"it’s possible to point SRV records those ports. Please keep in mind, that\"\n\" you have to open those ports for incoming connections in your firewall. \"\n\"In case you are using ``iptables`` you can use following command to \"\n\"include those ports in your rules:\"\nmsgstr \"\"\n\"如果由于任何原因您不能使用默认端口并且必须更改它们，则可以指向 SRV 记录这些端口。请记住，您必须为防火墙中的传入连接打开这些端口。如果您使用 \"\n\"``iptables`` ，您可以使用以下命令将这些端口包含在您的规则中：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:159\nmsgid \"\"\n\"Both ports should be setup to use TCP only. If for any reason you want to\"\n\" make service available for different ports you can:\"\nmsgstr \"两个端口都应设置为仅使用 TCP。如果出于任何原因您希望为不同的端口提供服务，您可以：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:161\nmsgid \"change ports in Tigase configuration and update DNS SRV records;\"\nmsgstr \"更改 Tigase 配置中的端口并更新 DNS SRV 记录；\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Tigase_Server_Network_Instructions.inc:163\nmsgid \"\"\n\"forward those ports to default Tigase ports (this is especially useful \"\n\"under \\\\*nix operating system if you want to utilize ports lower than \"\n\"``1024`` while running, as recommended, Tigase service from user account \"\n\"- there is a limitation and user accounts can bind to ports lower than \"\n\"``1024``), for example using ``iptables`` rules (in following example we \"\n\"are making available Tigase SSL websocket port available under port \"\n\"``443``, which is usually opened in corporate firewalls):\"\nmsgstr \"\"\n\"将这些端口转发到默认 Tigase 端口（如果您想在运行时使用低于 ``1024`` 的端口，这在 \\\\*nix \"\n\"操作系统下特别有用，根据推荐，来自用户帐户的 Tigase 服务 — 有一个限制并且用户帐户可以绑定到低于 ``1024`` 的端口），例如使用 \"\n\"``iptables`` 规则（在以下示例中，我们在端口 443 下提供可用的 Tigase SSL websocket \"\n\"端口，该端口通常在公司防火墙中被打开):\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:4\nmsgid \"Tigase Script Selection\"\nmsgstr \"Tigase 脚本选择\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:6\nmsgid \"\"\n\"As mentioned in each of the quick start sections, each distribution of \"\n\"Tigase XMPP server comes with a number of scripts that are customized for\"\n\" different versions of Linux.\"\nmsgstr \"正如每个快速入门部分所述，Tigase XMPP 服务器的每个发行版都带有许多针对不同版本的 Linux 定制的脚本。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:8\nmsgid \"init.d chart\"\nmsgstr \"init.d 图表\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:11\nmsgid \"Operating system\"\nmsgstr \"操作系统\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:11\nmsgid \"init.d file path\"\nmsgstr \"init.d 文件路径\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:11\nmsgid \"Types of Operating Systems\"\nmsgstr \"操作系统的类型\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:13\nmsgid \"Systemd\"\nmsgstr \"Systemd\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:13\nmsgid \"``tigase-server/scripts/systemd/*``\"\nmsgstr \"``tigase-server/scripts/systemd/*``\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:13\nmsgid \"Systemd-based distributions\"\nmsgstr \"基于Systemd的发行版\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:15\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:197\nmsgid \"Debian\"\nmsgstr \"Debian\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:15\nmsgid \"``tigase-server/scripts/debian/tigase.init.d``\"\nmsgstr \"``tigase-server/scripts/debian/tigase.init.d``\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:15\nmsgid \"Knoppix, Ubuntu (before v15.04), Raspbian or Duvian\"\nmsgstr \"Knoppix, Ubuntu (before v15.04), Raspbian or Duvian\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:17\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:90\nmsgid \"Gentoo\"\nmsgstr \"Gentoo\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:17\nmsgid \"``tigase-server/scripts/gentoo/init.d/tigase``\"\nmsgstr \"``tigase-server/scripts/gentoo/init.d/tigase``\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:17\nmsgid \"CoreOS (before v94.0.0), Tin Hat Linux or other \\\\*too based systems\"\nmsgstr \"CoreOS（v94.0.0 之前），Tin Hat Linux 或其他基于 \\\\*too 的系统\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:19\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:110\nmsgid \"Mandriva\"\nmsgstr \"Mandriva\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:19\nmsgid \"``tigase-server/scripts/mandriva/init.d/tigase``\"\nmsgstr \"``tigase-server/scripts/mandriva/init.d/tigase``\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:19\nmsgid \"Specific init.d file for Mandriva Linux\"\nmsgstr \"Mandriva Linux 的特定 init.d 文件\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:21\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:138\nmsgid \"Redhat\"\nmsgstr \"Redhat\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:21\nmsgid \"``tigase-server/scripts/redhat/init.d/tigase``\"\nmsgstr \"``tigase-server/scripts/redhat/init.d/tigase``\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:21\nmsgid \"\"\n\"RedHat (before v7.0) and other RPM based linux derivatives like CentOS \"\n\"(before v.7.14), openSUSE (before v12.2)\"\nmsgstr \"\"\n\"RedHat（v7.0 之前）和其他基于 RPM 的 Linux 衍生产品，如 CentOS（v.7.14 之前），openSUSE（v12.2 \"\n\"之前）\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:28\nmsgid \"\"\n\"If your operating system is a systemd-based linux distribution, we \"\n\"recommend to use systemd service scripts. It may be possible to use (in \"\n\"this case legacy) ``init.d`` startup files as before, but usage of \"\n\"systemd startup scripts will allow better control of the startup process \"\n\"and will even allow for automatic restart of the Tigase XMPP Server in \"\n\"the case of JVM crash.\"\nmsgstr \"\"\n\"如果您的操作系统是基于 systemd 的 linux 发行版，我们建议使用 systemd 服务脚本。可以像以前一样使用（在这种情况下是旧的) \"\n\"``init.d`` 启动文件，但是使用 systemd 启动脚本可以更好地控制启动过程，甚至可以在JVM崩溃的情况下自动重启 Tigase \"\n\"XMPP 服务器。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:32\nmsgid \"Configuration: For Linux Distributions using systemd\"\nmsgstr \"配置：对于使用 systemd 的 Linux 发行版\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:34\nmsgid \"\"\n\"To set up Tigase XMPP Server as a system service it is required to copy \"\n\"``tigase-server.service`` file to ``/etc/systemd/system/`` directory\"\nmsgstr \"\"\n\"要将 Tigase XMPP 服务器设置为系统服务，需要将 ``tigase-server.service`` 文件复制到 \"\n\"``/etc/systemd/system/`` 目录\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:40\nmsgid \"This file contains following parameters which may need to be adjusted:\"\nmsgstr \"该文件包含以下可能需要调整的参数：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:42\nmsgid \"\"\n\"``User`` - Specifies the user that will run the program. This should be a\"\n\" user with SU permissions.\"\nmsgstr \"``User`` - 指定将运行程序的用户。这应该是具有 SU 权限的用户。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:44\nmsgid \"\"\n\"``WorkingDirectory`` - Specifies installation directory *(default: \"\n\"``/home/tigase/tigase-server``)*\"\nmsgstr \"``WorkingDirectory`` - 指定安装目录 (*默认*: ``/home/tigase/tigase-server`` )\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:46\nmsgid \"\"\n\"``ExecStart`` - Specifies startup command *(default: runs \"\n\"``scripts/tigase.sh start etc/tigase.conf`` in the Tigase installation \"\n\"directory)*\"\nmsgstr \"\"\n\"``ExecStart`` - 指定启动命令 (*默认：运行 Tigase 安装目录中的* ``scripts/tigase.sh start \"\n\"etc/tigase.conf``）\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:48\nmsgid \"\"\n\"``ExecStop`` - Specifies shutdown command *(default: runs \"\n\"``scripts/tigase.sh stop etc/tigase.conf`` in the Tigase installation \"\n\"directory)*\"\nmsgstr \"\"\n\"``ExecStop`` - 指定关闭命令 (*默认：在 Tigase 安装目录中运行* ``scripts/tigase.sh stop \"\n\"etc/tigase.conf``）\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:50\nmsgid \"\"\n\"``PIDFile`` - Specifies location of the PID file *(default: \"\n\"``logs/tigase.pid`` file in the Tigase installation directory)*\"\nmsgstr \"``PIDFile`` - 指定 PID 文件的位置 (*默认：Tigase 安装目录中的* ``logs/tigase.pid`` *文件*)\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:52\nmsgid \"\"\n\"It is also required to copy options file ``tigase-server`` to \"\n\"``/etc/default/`` directory\"\nmsgstr \"还需要将选项文件 ``tigase-server`` 复制到 ``/etc/default/`` 目录下\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:58\nmsgid \"With those files in place you need to reload ``systemctl`` daemon\"\nmsgstr \"有了这些文件，你需要重新加载 ``systemctl`` 守护进程\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:68\nmsgid \"\"\n\"If you are upgrading from the previous version of the Tigase XMPP Server \"\n\"which was not running as the systemd system service it is required to \"\n\"uninstall old service and remove old service files.\"\nmsgstr \"如果您从旧版本的 Tigase XMPP 服务器升级，它不是作为 systemd 系统服务运行的，因此需要卸载旧服务并删除旧服务文件。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:72\nmsgid \"Configuration: For All Linux Distributions\"\nmsgstr \"配置：适用于所有 Linux 发行版\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:74\nmsgid \"\"\n\"Once you’ve located the appropriate distribution scripts (please take a \"\n\"look at the table above), copy it to your system’s init.d folder (usually\"\n\" it’s ``/etc/init.d/``):\"\nmsgstr \"一旦找到合适的分发脚本后（请查看上表），将其复制到系统的 init.d 文件夹（通常是 ``/etc/init.d/``）：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:80\nmsgid \"You may also need to make it executable:\"\nmsgstr \"您可能还需要使其可执行：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:86\nmsgid \"\"\n\"It is recommended that you open the script files or configuration files \"\n\"as some have some parameters that you will need to specify.\"\nmsgstr \"建议您打开脚本文件或配置文件，因为有些文件中有一些参数需要您指定。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:92\nmsgid \"The conf.d script must contain the following parameters:\"\nmsgstr \"conf.d 脚本必须包含以下参数：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:100\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:124\nmsgid \"The following should be configured:\"\nmsgstr \"应配置以下内容：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:102\nmsgid \"``TIGASE_HOME`` - Specifies the Tigase Server installation directory.\"\nmsgstr \"``TIGASE_HOME`` - 指定 Tigase 服务器安装目录。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:104\nmsgid \"\"\n\"``TIGASE_USER`` - Specifies the user that will run the program. This \"\n\"should be a user with SU permissions.\"\nmsgstr \"``TIGASE_USER`` - 指定哪些用户将运行程序。这应该是具有 SU 权限的用户。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:106\nmsgid \"\"\n\"``TIGASE_CONF`` - The location of tigase.conf file, relative to the \"\n\"``TIGASE_HOME`` directory.\"\nmsgstr \"``TIGASE_CONF`` - tigase.conf 文件的位置，其是 ``TIGASE_HOME`` 目录的相对位置。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:112\nmsgid \"Mandriva has a single init.d file, however it should be configured:\"\nmsgstr \"Mandriva 有一个 init.d 文件，但是应该对其进行配置：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:126\nmsgid \"``JAVA_HOME`` - The location of your JDK Installation.\"\nmsgstr \"``JAVA_HOME`` - JDK 安装的位置。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:128\nmsgid \"``TIGASE_DIR`` - Tigase Server installation directory.\"\nmsgstr \"``TIGASE_DIR`` - Tigase 服务器安装目录。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:130\nmsgid \"\"\n\"``tigase`` - The location of your tigase.sh script. This should not need \"\n\"adjusting if you maintain the default file structure.\"\nmsgstr \"``tigase`` - 你的 tigase.sh 脚本的位置。如果您保持默认文件结构，则不需要调整。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:132\nmsgid \"\"\n\"``config`` - The location of your tigase.conf file. This should not need \"\n\"adjusting if you maintain the default file structure.\"\nmsgstr \"``config`` - tigase.conf 文件的位置。如果您保持默认文件结构，则不需要调整。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:134\nmsgid \"``pid`` file will be stored in ``/var/run/ser.pid``\"\nmsgstr \"``pid`` 文件将存储在 ``/var/run/ser.pid``\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:140\nmsgid \"Similar to Mandriva, you will need to configure the init.d file:\"\nmsgstr \"与 Mandriva 类似，您需要配置 init.d 文件：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:162\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:218\nmsgid \"``USERNAME`` - Username running Tigase, should have su permissions.\"\nmsgstr \"``USERNAME`` - 运行 Tigase 的用户名，应该有 su 权限。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:164\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:220\nmsgid \"``USERGROUP`` - The usergroup of the username.\"\nmsgstr \"``USERGROUP`` - 用户名的用户组。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:166\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:222\nmsgid \"``NAME`` - OS name for Tigase program.\"\nmsgstr \"``NAME`` - Tigase 程序的操作系统名称。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:168\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:224\nmsgid \"``DESC`` - Optional description.\"\nmsgstr \"``DESC`` - 可选说明。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:170\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:226\nmsgid \"\"\n\"``TIGASE_HOME`` - The location of your Tigase Server installation \"\n\"directory.\"\nmsgstr \"``TIGASE_HOME`` - 你的 Tigase 服务器安装目录的位置。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:172\nmsgid \"\"\n\"``TIGASE_LIB`` - The location of your Tigase Jars folder, you should not \"\n\"need to adjust this if you set ``TIGASE_HOME`` properly, and maintain the\"\n\" default file structure.\"\nmsgstr \"\"\n\"``TIGASE_LIB`` - 您的 Tigase Jars 文件夹的位置，如果您正确设置 ``TIGASE_HOME`` \"\n\"并保持默认文件结构，则不需要调整它。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:174\nmsgid \"\"\n\"``TIGASE_CONFIG`` - The location of your tigase.conf file relative to \"\n\"``TIGASE_HOME``\"\nmsgstr \"``TIGASE_CONFIG`` - 你的 tigase.conf 文件相对于 ``TIGASE_HOME`` 的位置\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:176\nmsgid \"\"\n\"``TIGASE_OPTIONS`` - Legacy options for Tigase, most are now handled in \"\n\"``config.tdsl`` or tigase.conf.\"\nmsgstr \"``TIGASE_OPTIONS`` - Tigase 的旧选项，现在大部分在 ``config.tdsl`` 或 tigase.conf 中处理。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:178\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:232\nmsgid \"\"\n\"``TIGASE_PARAMS`` - Parameters passed to command line when launching \"\n\"Tigase.\"\nmsgstr \"``TIGASE_PARAMS`` - 启动 Tigase 时传递给命令行的参数。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:180\nmsgid \"\"\n\"``PIDFILE`` - Location of Tigase PID file if you wish to use custom \"\n\"directory. Default will be located in /logs or /var/temp directory.\"\nmsgstr \"``PIDFILE`` - 如果你想使用自定义目录，Tigase PID 文件的位置。默认将位于 /logs 或 /var/temp 目录中。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:182\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:236\nmsgid \"\"\n\"``TIGASE_CONSOLE_LOG`` - Location of Tigase Server console log file if \"\n\"you wish to use a custom directory. Default will be located in /logs \"\n\"directory, failing that /dev/null.\"\nmsgstr \"\"\n\"``TIGASE_CONSOLE_LOG`` - 如果你想使用自定义目录，Tigase 服务器控制台日志文件的位置。如果 /dev/null \"\n\"失败，其默认将位于 /logs 目录中。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:184\nmsgid \"\"\n\"After you’ve copied the script, in order to install sysinit script you \"\n\"have to add it to the configuration:\"\nmsgstr \"复制脚本后，为了安装 sysinit 脚本，您必须将其添加到配置中：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:190\nmsgid \"Service can be enabled or disabled service with:\"\nmsgstr \"可以通过以下方式启用或禁用服务：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:199\nmsgid \"\"\n\"As with other distributions you should copy init.d script to the correct \"\n\"location. Afterwards it should be edited and correct values for variables\"\n\" need to be set:\"\nmsgstr \"与其他发行版一样，您应该将 init.d 脚本复制到正确的位置。之后应该对其进行编辑并需要设置变量的正确值：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:228\nmsgid \"\"\n\"``TIGASE_CONFIG`` - The location of your tigase-server.xml file relative \"\n\"(old configuration format)\"\nmsgstr \"``TIGASE_CONFIG`` - 你的 tigase-server.xml 文件的相对位置（旧配置格式）\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:230\nmsgid \"\"\n\"``TIGASE_OPTIONS`` - command line arguments passed to Tigase server \"\n\"(which may include path to ``init.properies`` (if correct ``tigase.conf``\"\n\" configuration will be found then it will translate to \"\n\"``TIGASE_OPTIONS=\\\" --property-file etc/config.tdsl \\\"``\"\nmsgstr \"\"\n\"``TIGASE_OPTIONS`` - 传递给 Tigase 服务器的命令行参数（可能包括``init.properies`` \"\n\"的路径（如果找到正确的 ``tigase.conf`` 配置，那么它将转换为 ``TIGASE_OPTIONS=\\\" --property-\"\n\"file etc/config.tdsl \\\"``\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:234\nmsgid \"\"\n\"``PIDFILE`` - Location of Tigase PID file if you wish to use custom \"\n\"directory. Default will be located in ``/var/run/tigase/tigase.pid`` or \"\n\"under (in this case relative to tigase home directory)\\\\ \"\n\"``logs/tigase.pid``.\"\nmsgstr \"\"\n\"``PIDFILE`` - 如果你想使用自定义目录，Tigase PID 文件的位置。默认将位于 \"\n\"/var/run/tigase/tigase.pid 或在（在这种情况下相对于 tigase 主目录）\\\\ ``logs/tigase.pid``\"\n\" 下面。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:238\nmsgid \"\"\n\"Afterwards we need to install service in the system with following \"\n\"command:\"\nmsgstr \"之后我们需要使用以下命令在系统中安装服务：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:246\nmsgid \"Running Tigase as a system service\"\nmsgstr \"将 Tigase 作为系统服务运行\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:248\nmsgid \"\"\n\"There are a number of benefits to running Tigase as a service, one of \"\n\"which is to ensure that the program will run even in the event of a power\"\n\" outage or accidental server restart, Tigase will always be up and \"\n\"running.\"\nmsgstr \"将 Tigase 作为服务运行有很多好处，其中之一是确保即使在断电或服务器意外重启的情况下程序也能运行，Tigase 将始终启动并运行。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:252\nmsgid \"For systemd-based linux distributions\"\nmsgstr \"对于基于 systemd 的 linux 发行版\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:254\nmsgid \"\"\n\"Once installation is complete you may start Tigase as a typical systemd \"\n\"service using following command:\"\nmsgstr \"一旦安装完成后，您可以使用以下命令将 Tigase 作为一般的 systemd 服务启动：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:260\nmsgid \"To stop it, you may run following command:\"\nmsgstr \"要停止它，您可以运行以下命令：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:266\nmsgid \"\"\n\"It is also possible to enable service, to make it start during startup of\"\n\" the operating system:\"\nmsgstr \"也可以启用服务，使其在操作系统启动期间启用：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:274\nmsgid \"For other linux distributions\"\nmsgstr \"对于其他 linux 发行版\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:276\nmsgid \"\"\n\"Once installation is complete, you should be able to start Tigase using \"\n\"the following command:\"\nmsgstr \"一旦安装完成后，您应该能够使用以下命令启动 Tigase：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:282\nmsgid \"\"\n\"Tigase should begin running in the background. Since Tigase is now \"\n\"installed as a service, it can be controlled with any of the service \"\n\"commands, such as:\"\nmsgstr \"Tigase 应该开始在后台运行。由于 Tigase 现在已作为服务安装，因此可以使用任何服务命令对其进行控制，例如：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:284\nmsgid \"``service tigase stop``\"\nmsgstr \"``service tigase stop``\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Script_Selection.inc:286\nmsgid \"``service tigase restart``\"\nmsgstr \"``service tigase restart``\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:2\nmsgid \"Shutting Down Tigase\"\nmsgstr \"关闭 Tigase\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:4\nmsgid \"\"\n\"Although Tigase XMPP Server can be terminated by ending the process, it \"\n\"is preferred and recommended to use it’s own shutdown scripts instead. \"\n\"Not only does this allow for a proper purge of Tigase form the system, \"\n\"but allows for all shutdown functions to operate, such as amending logs \"\n\"and completing statistics. To trigger a shutdown of Tigase server, the \"\n\"following command can be used from the tigase directory:\"\nmsgstr \"\"\n\"虽然 Tigase XMPP Server 可以通过结束进程来终止，但建议最好使用它自己的关闭脚本。这不仅允许从系统中正确清除 \"\n\"Tigase，而且允许所有关闭功能运行，例如修改日志和完成统计。要触发 Tigase 服务器的关闭，可以从 tigase 目录使用以下命令：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:10\nmsgid \"\"\n\"You may specify the config file if you want, but it will make no \"\n\"differences\"\nmsgstr \"如果您需要可以指定配置文件，但不会有任何区别\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:12\nmsgid \"This will:\"\nmsgstr \"这将：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:14\nmsgid \"Begin shutdown thread\"\nmsgstr \"开始关闭线程\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:16\nmsgid \"Stop accepting new connections\"\nmsgstr \"停止接受新连接\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:18\nmsgid \"Close all current connections\"\nmsgstr \"关闭所有当前连接\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:20\nmsgid \"Collect runtime statistics\"\nmsgstr \"收集运行时统计信息\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:22\nmsgid \"Write statistics to log\"\nmsgstr \"将统计信息写入日志\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:24\nmsgid \"Dump full stacktrace to a file\"\nmsgstr \"将完整的堆栈跟踪转储到文件\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:26\nmsgid \"Run GC and clear from memory\"\nmsgstr \"运行 GC 并从内存中清除\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:30\nmsgid \"Shutdown statistics\"\nmsgstr \"关机统计\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:32\nmsgid \"\"\n\"Upon shutdown, statistics for the server’s runtime will be appended to \"\n\"the log file. For a description of the statistics and what they mean, \"\n\"refer to the :ref:`Statistics Description<statsticsDescription>` portion \"\n\"of the documentation.\"\nmsgstr \"\"\n\"关闭后，服务器运行时的统计信息将附加到日志文件中。有关统计信息的描述及其含义，请参阅文档的 :ref:`Statistics \"\n\"Description<statsticsDescription>` 部分。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:35\nmsgid \"Shutdown StackTrace Dump\"\nmsgstr \"关闭 StackTrace 转储\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:37\nmsgid \"\"\n\"To aid with troubleshooting purposes, the full stacktrace will be dumped \"\n\"to a seperate file located at $serverdir/logs/threads-dump.log.# \"\n\"Stacktrace logs will follow the same log file numbering scheme described \"\n\"in :ref:`Log file description<Tigase-Log-Guide>`.\"\nmsgstr \"\"\n\"为了帮助故障排除，完整的堆栈跟踪将转储到位于$serverdir/logs/threads-dump.log.# \"\n\"的单独文件中。Stacktrace 日志将遵循 :ref:`Log file description<Tigase-Log-Guide>` \"\n\"中描述的相同日志文件编号方案。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:39\nmsgid \"\"\n\"This feature is enabled by default, however you may disable this by \"\n\"adding the following to your ``config.tdsl`` file:\"\nmsgstr \"默认情况下启用此功能，但是您可以通过将以下内容添加到您的 ``config.tdsl`` 文件来禁用此功能：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:46\nmsgid \"Shutting Down Cluster Nodes\"\nmsgstr \"关闭集群节点\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:48\nmsgid \"\"\n\"Starting with v8.0.0 you can now shut down individual cluster nodes \"\n\"without shutting down the whole server. This command will use the \"\n\"*SeeOtherHost* strategy to direct traffic to other nodes and update the \"\n\"cluster map to gracefully shut down the single node\"\nmsgstr \"\"\n\"从 v8.0.0 开始，您现在可以关闭单个集群节点而无需关闭整个服务器。此命令将使用 *SeeOtherHost* \"\n\"策略将流量引导到其他节点并更新集群映射以顺利地关闭单个节点\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Shutting_Down.inc:50\nmsgid \"\"\n\"Shutting down individual nodes can be done VIA Ad-hoc command and fill \"\n\"out the response forms. The command is available from message-router as \"\n\"http://jabber.org/protocol/admin#shutdown.\"\nmsgstr \"\"\n\"可以通过 Ad-hoc 命令关闭单个节点并填写响应表。该命令可从 \"\n\"http://jabber.org/protocol/admin#shutdown 的 message-router 获得。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:2\nmsgid \"Upgrading Tigase to newer version\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:6\nmsgid \"\"\n\"Depending of the deployment we recommend installing Tigase XMPP Server \"\n\"next to the existing one and following with replace of the service once \"\n\"the upgrade finishes correctly.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:9\nmsgid \"Backup your data\"\nmsgstr \"备份您的数据\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:11\nmsgid \"\"\n\"As with any migration it is **highly recommended** that you backup your \"\n\"repository before conducting any upgrade operations. It can be done via \"\n\"simple SQL dump od the database or more elaborate snapshot of the \"\n\"database offered by cloud providers.\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:15\n#, fuzzy\nmsgid \"Configuration files to migrate\"\nmsgstr \"升级配置文件\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:17\nmsgid \"\"\n\"During the upgrade the most important files to migrate to newer versions \"\n\"are:\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:19\n#, fuzzy\nmsgid \"`etc/tigase.conf`\"\nmsgstr \"配置tigase.conf\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:20\n#, fuzzy\nmsgid \"`etc/config.tdsl`\"\nmsgstr \"配置 config.tdsl\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:21\nmsgid \"`etc/jmx.access` and `etc/jmx.password`\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:22\nmsgid \"\"\n\"`certs/*` (if configured to use local filesystem, though we recommend to \"\n\"use database for storing certificates)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:25\nmsgid \"Upgrade Database schema\"\nmsgstr \"升级数据库架构\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:27\nmsgid \"\"\n\"Upgrading database schemas is now possible using the ``upgrade-schema`` \"\n\"option. Do this now.\"\nmsgstr \"现在可以使用 ``upgrade-schema`` 选项升级数据库模式。现在就这样做。\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:36\nmsgid \"Your database schema MUST be v8 or conversion will not occur properly!\"\nmsgstr \"您的数据库架构必须是 v8，否则转换将无法正常进行！\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:38\nmsgid \"\"\n\"You will be asked for rood credentials. Those can be provided as \"\n\"parameters to `/scripts/tigase.sh upgrade-schema` - please check \"\n\"`./scripts/tigase.sh upgrade-schema --help` for more details\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:40\nmsgid \"Upon success, you should see the following:\"\nmsgstr \"成功后，您应该看到以下内容：\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:61\nmsgid \"Start Tigase!\"\nmsgstr \"开始 Tigase！\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:65\nmsgid \"Help?\"\nmsgstr \"Help?\"\n\n#: ../../Tigase_Administration/Quick_Start_Guide/Upgrading_from_Older_versions.inc:67\nmsgid \"\"\n\"Both ``upgrade`` commands also have a build in help function, they can be\"\n\" called if needed from the command line. You can also run these commands \"\n\"for help.\"\nmsgstr \"两个 ``upgrade`` 命令也有内置帮助功能，如果需要，可以从命令行调用它们。您还可以运行这些命令来寻求帮助。\"\n\n#~ msgid \"\"\n#~ \"There are many other settings that \"\n#~ \"can be configured `visit this section\"\n#~ \" for details <#tigase41virtualHosts>`__.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"Consult `dataSource <#dataSource>`__ property \"\n#~ \"for more configuration info.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"This command will install tigase using\"\n#~ \" a Derby database on one named \"\n#~ \"``tigasedb`` hosted on ``localhost``. The \"\n#~ \"username and password editing the \"\n#~ \"database is ``tigase_pass`` and ``root``. \"\n#~ \"Note that ``-J`` explicitly adds the \"\n#~ \"administrator, this is highly recommended \"\n#~ \"with the ``-N`` passing the password.\"\n#~ \" You may customize this command as\"\n#~ \" needed, refer to the `install-schema\"\n#~ \" <#install-schema>`__ section of the \"\n#~ \"documentation for more information.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"It’s also encouraged to provide \"\n#~ \"dedicated SSL certificate - there are\"\n#~ \" various ways to do it and they\"\n#~ \" are described in `??? \"\n#~ \"<#InstallingSSLCertificate>`__. You may want \"\n#~ \"to take advantage of free Let’s \"\n#~ \"Encrypt certificates and automate whole \"\n#~ \"upload and renewal process as described\"\n#~ \" in `??? <#LetsEncryptCertificate>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"To aid with troubleshooting purposes, \"\n#~ \"the full stacktrace will be dumped \"\n#~ \"to a seperate file located at \"\n#~ \"$serverdir/logs/threads-dump.log.# Stacktrace logs\"\n#~ \" will follow the same log file \"\n#~ \"numbering scheme described in :ref:`Log \"\n#~ \"file description<logs>`.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"To make upgrade process easier it’s \"\n#~ \"possible to utilize `tigase-upgrade.sh \"\n#~ \"<files/tigase-upgrade.sh>`__ \\\\*nix shell \"\n#~ \"script. It permits upgrading to new \"\n#~ \"version (supports downloading version from \"\n#~ \"provided URL).\"\n#~ msgstr \"\"\n\n#~ msgid \"Upgrading to v8.0.0 from v7.1.0\"\n#~ msgstr \"从 v7.1.0 升级到 v8.0.0\"\n\n#~ msgid \"\"\n#~ \"There have been a number of \"\n#~ \"changes to the user and auth \"\n#~ \"databases since v7.1.0. As a result, \"\n#~ \"if you are upgrading from older \"\n#~ \"versions, you will need to follow \"\n#~ \"this guide.\"\n#~ msgstr \"自 v7.1.0 以来，用户和身份验证数据库发生了许多更改。因此，如果您从旧版本升级，则需要遵循本指南。\"\n\n#~ msgid \"\"\n#~ \"We recommend installing Tigase XMPP \"\n#~ \"Server 8.0.0 in a separate directory.\"\n#~ msgstr \"我们建议将 Tigase XMPP Server 8.0.0 安装在单独的目录中。\"\n\n#~ msgid \"\"\n#~ \"As with any migration it is highly\"\n#~ \" recommended that you backup your \"\n#~ \"repository before conducting any upgrade \"\n#~ \"operations.\"\n#~ msgstr \"与任何迁移一样，强烈建议您在执行任何升级操作之前备份您的存储库。\"\n\n#~ msgid \"For MySQL databases:\"\n#~ msgstr \"对于 MySQL 数据库：\"\n\n#~ msgid \"Setup Tigase XMPP Server 8.0.0\"\n#~ msgstr \"设置 Tigase XMPP 服务器 8.0.0\"\n\n#~ msgid \"\"\n#~ \"After downloading Tigase XMPP Server \"\n#~ \"8.0.0 from our website, or using \"\n#~ \"wget, extract the files to a \"\n#~ \"separate directory.\"\n#~ msgstr \"从我们的网站下载 Tigase XMPP Server 8.0.0 或使用 wget 后，将文件解压缩到单独的目录。\"\n\n#~ msgid \"\"\n#~ \"Copy the ``tigase.conf`` and \"\n#~ \"``init.properties`` files from the old \"\n#~ \"directory to v8.0.0 directory.\"\n#~ msgstr \"将 ``tigase.conf`` 和 ``init.properties`` 文件从旧目录复制到 v8.0.0 目录。\"\n\n#~ msgid \"Import the database.\"\n#~ msgstr \"导入数据库。\"\n\n#~ msgid \"\"\n#~ \"Tigase XMPP Server has a utility \"\n#~ \"that can be called using ``upgrade-\"\n#~ \"config`` that will update your old \"\n#~ \"``init.properties`` file and create a \"\n#~ \"new file using DSL.\"\n#~ msgstr \"\"\n#~ \"Tigase XMPP Server 有一个可以使用 ``upgrade-\"\n#~ \"config`` 调用的实用程序，它将更新旧的 ``init.properties`` 文件并使用\"\n#~ \" DSL 创建一个新文件。\"\n\n#~ msgid \"When everything is ready it will printout following information\"\n#~ msgstr \"一切准备就绪后，它将打印出以下信息\"\n\n#~ msgid \"Connect new database\"\n#~ msgstr \"连接新数据库\"\n\n#~ msgid \"\"\n#~ \"Edit your new ``config.tdsl`` file to\"\n#~ \" connect to the new database you \"\n#~ \"created during the import step.\"\n#~ msgstr \"编辑新的 ``config.tdsl`` 文件以连接到您在导入步骤中创建的新数据库。\"\n\n#~ msgid \"You will be asked the following prompts:\"\n#~ msgstr \"您将被询问以下提示：\"\n\n#~ msgid \"Upgrade/Restore with a script [experimental!]\"\n#~ msgstr \"使用脚本升级/恢复 [实验性！]\"\n\n#~ msgid \"\"\n#~ \"To make upgrade process easier it’s \"\n#~ \"possible to utilize `tigase-upgrade.sh \"\n#~ \"<https://raw.githubusercontent.com/tigase/tigase-\"\n#~ \"server/master/src/main/restructured/files/tigase-upgrade.sh>`__\"\n#~ \" \\\\*nix shell script. It permits \"\n#~ \"upgrading to new version (supports \"\n#~ \"downloading version from provided URL).\"\n#~ msgstr \"\"\n#~ \"为了使升级过程更容易，可以使用 `tigase-upgrade.sh \"\n#~ \"<https://raw.githubusercontent.com/tigase/tigase-\"\n#~ \"server/master/src/main/restructured/files/tigase-upgrade.sh>`__\"\n#~ \" \\\\*nix shell 脚本。它允许升级到新版本（支持从提供的 URL \"\n#~ \"下载版本）。\"\n\n#~ msgid \"It’s usage is as follows:\"\n#~ msgstr \"它的用法如下：\"\n\n#~ msgid \"\"\n#~ \"Where: \\\\* ``{upgrade|rollback}`` - defines\"\n#~ \" whether to perform upgrade or \"\n#~ \"rollback to previous version \\\\* \"\n#~ \"``install_package`` - package to which \"\n#~ \"perform upgrade (can be URL) in \"\n#~ \"case of upgrade or backed-up \"\n#~ \"installation (from which we want to \"\n#~ \"restore) in case of rollback \\\\* \"\n#~ \"``install_directory`` - destination directory \"\n#~ \"(both in upgrade and rollback); can \"\n#~ \"be symlink in which case it will\"\n#~ \" be preserved with upgraded/restored path\"\n#~ \" as target \\\\* ``[tar|dir]`` - \"\n#~ \"(optional) type of backup (either simply\"\n#~ \" copy directory or also archive it\"\n#~ \" using ``tar`` command); by default \"\n#~ \"``dir`` is used.\"\n#~ msgstr \"\"\n#~ \"其中： \\\\* ``{upgrade|rollback}`` - \"\n#~ \"定义是执行升级还是回滚到以前的版本 \\\\* ``install_package`` - \"\n#~ \"在升级或备份的情况下执行升级的包（可以是 URL）在回滚的情况下安装（我们要从中恢复）\\\\ * \"\n#~ \"``install_directory``-目标目录（在升级和回滚中）；可以是符号链接，在这种情况下，它将使用升级/恢复的路径作为目标\"\n#~ \" \\\\* ``[tar|dir]`` - （可选）备份类型（简单地复制目录或使用 \"\n#~ \"``tar`` 命令将其存档）；默认情况下使用 ``dir``。\"\n\n#~ msgid \"\"\n#~ \"To upgrade installation to version \"\n#~ \"``tigase-server-8.0.0-SNAPSHOT-b5285-dist-max.tar.gz`` \"\n#~ \"execute the script as follows:\"\n#~ msgstr \"\"\n#~ \"要将安装升级到版本 ``tigase-server-8.0.0-SNAPSHOT-b5285-dist-\"\n#~ \"max.tar.gz`` ，请执行以下脚本：\"\n\n#~ msgid \"\"\n#~ \"To rollback from ``tigase-server-8.0.0\"\n#~ \"-SNAPSHOT-b5264_backup-18-11-05_1712`` backup execute\"\n#~ \" script as follows:\"\n#~ msgstr \"\"\n#~ \"要从 ``tigase-server-8.0.0-SNAPSHOT-\"\n#~ \"b5264_backup-18-11-05_1712`` 备份回滚执行如下所示脚本：\"\n\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Administration/Release_Notes/Tigase_Release_Notes.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2023-02-16 15:01-0800\\n\"\n\"PO-Revision-Date: 2024-06-12 11:40+0000\\n\"\n\"Last-Translator: Lavender4970 <postage_quizzical060@simplelogin.com>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/release_notes/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.11.0\\n\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_Release_Notes.rst:2\nmsgid \"Tigase 8.3.0 Release Notes\"\nmsgstr \"Tigase 8.3.0 发行说明\"\n\n#: ../../Tigase_Administration/Release_Notes/tigase-server-current.inc:2\nmsgid \"Tigase XMPP Server 8.4.0 Change notes\"\nmsgstr \"Tigase XMPP 服务器 8.4.0 变更说明\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:5\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:5\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:5\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:5\n#: ../../Tigase_Administration/Release_Notes/tigase-server-current.inc:5\nmsgid \"Major Changes\"\nmsgstr \"主要变化\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:16\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:15\n#: ../../Tigase_Administration/Release_Notes/tigase-server-current.inc:11\nmsgid \"All Minor Features & Behavior Changes\"\nmsgstr \"所有次要功能和行为变化\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:2\nmsgid \"Tigase XMPP Server 8.3.0 Change notes\"\nmsgstr \"Tigase XMPP 服务器 8.3.0 变更说明\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:7\nmsgid \"This version requires JDK17 to run\"\nmsgstr \"此版本需要 JDK17 才能运行\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:8\nmsgid \"Added support for mam2#extended [#mam-73]\"\nmsgstr \"添加了对 mam2#extended 的支持 [#mam-73]\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:9\nmsgid \"\"\n\"Rework certificate generation to utilise `keygen` tool instead of using \"\n\"`sun.*` API unavailable under JDK17\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:10\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:45\nmsgid \"\"\n\"Added support for XEP-0440 SASL Channel Binding Type Capability and fixed\"\n\" and reenabled `SCRAM-*-PLUS SASL` mechanisms [#server-1335]\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:11\nmsgid \"\"\n\"Added initial,preview support for SASL2 and Bind2 (disabled by default) -\"\n\" to enable, activate beans `'urn:xmpp:bind:0'` and `'urn:xmpp:sasl:2'` in\"\n\" `'sess-man'` [#server-1332]\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:17\nmsgid \"\"\n\"Increased network socket buffer from 2K to 4K to improve performance when\"\n\" reading data from socket. It can increase somewhat memory usage \"\n\"proportionally to number of concurrent user connections. It's possible to\"\n\" configure size of this buffer using `socket-buffer-size` property - \"\n\"please see documentation.\"\nmsgstr \"\"\n\"网络套接字缓冲区从 2K 增加到 4K，从而以提高从套接字读取数据时的性能。当并发用户连接的数量增加时， 它可以成比例地增加内存使用量。也可以使用\"\n\" `socket-buffer-size` 属性配置此缓冲区的大小 - 请参阅文档。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:18\nmsgid \"\"\n\"Add configuration to log size generated by LoggerTask in Monitor and \"\n\"decrease default from 1M to 50K; Disable serialisation of monitor events;\"\n\" #servers-372\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:19\nmsgid \"Add DOAP file; update documentation with supported features; #server-1076\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:20\nmsgid \"\"\n\"Fix issue with NPE in JabberIqAuth plugin when no password was presented \"\n\"due to missing return statement; fixed similar issue where, after closing\"\n\" the connection, the execution of the code wasn't terminated in \"\n\"JabberIqAuth and SaslAuth plugins #server-1317\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:21\nmsgid \"\"\n\"Add support for XEP-0398 to feature list and updated list of supported \"\n\"features; #server-1316\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:22\nmsgid \"\"\n\"Change try-catch statement in database schema loader to better catch edge\"\n\" cases; #serverdist-10\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:23\nmsgid \"\"\n\"Remove wildcard certificate generation (as main DN) in certificate \"\n\"container to avoid issues that it entails (inability to override such \"\n\"self-signed certificate via ad-hoc commands!). Wildcards are now properly\"\n\" handled by CertificateGenerateor and are included correctly as SAN in \"\n\"addition to DN for main domain; Fix handling \\\"default\\\" certificates \"\n\"from repository; #server-1279\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:24\nmsgid \"\"\n\"Change default watchdog ping from (forbidden by RFC) whitespace to xmpp; \"\n\"add warning if someone configures it as whitespace either way; \"\n\"server-1318\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:25\nmsgid \"Improve XMPPDomBuilderHandler logging; #server-1323\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:26\nmsgid \"\"\n\"Improved Stream Management code responsible for generating `<r/>` \"\n\"requests #server-1324 (#150)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:27\nmsgid \"\"\n\"Added `socket-buffer-size` option to `ConnectionManager` to configure \"\n\"`SO_RCVBUF` separately from internal network buffers #server-1325\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:28\nmsgid \"\"\n\"Increased socket-buffer-size for client-to-server and intercluster \"\n\"connections and added documentation #server-1325\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:29\nmsgid \"Fix MAX_PAUSE property name; #server-1326\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:30\nmsgid \"Updated implementation of XEP-0377: Spam Reporting #server-1327\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:31\nmsgid \"\"\n\"Fixed issue with errors being sent for unexpected <iq type=result/> \"\n\"stanzas #server-1328\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:32\nmsgid \"Improved exceptions handling in StanzaProcessor #server-1328\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:33\nmsgid \"Switch from jtds to MS own jdbc driver; #serverdist-12\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:34\nmsgid \"\"\n\"Prevent re-delivery of certain S2S packets (sasl, features, dialback, \"\n\"etc) as it doesn't make sense; #server-1320\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:35\nmsgid \"\"\n\"Adjust log levels to avoid WARNINGS during startup for regular messages; \"\n\"#server-1115\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:36\nmsgid \"Add 'active in last x' statistic; #server-1281\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:37\nmsgid \"Include option to restart JVM on OOM (off by default)\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:38\nmsgid \"\"\n\"Correctly process packets from mobile queue instead of re-adding \"\n\"currently filtered packet; #server-1331\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:39\nmsgid \"\"\n\"Improvements to NativeMemoryTracking implementation with units; \"\n\"documentation; #server-1330\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:40\nmsgid \"Improve MAM logging; #servers-384\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:41\nmsgid \"Only count stanzas in StreamManagement #server-1333\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:42\nmsgid \"Fixed advertisement stream features for unauthorized stream #server-1334\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:43\nmsgid \"\"\n\"Fixed NPE during preparing stream features when connection is already \"\n\"closed #server-1334\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:44\nmsgid \"\"\n\"Added initial support for SASL2 and Bind2 (preview feature, disabled by \"\n\"default) #server-1332\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:46\nmsgid \"Fixed NPE during enabling of stream resumption #server-1332\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:47\nmsgid \"\"\n\"Fixed sending block/unblock presences from blocking command for domain \"\n\"#server-1336\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:48\nmsgid \"\"\n\"Better default for lastXmppPacketReceivedTime member to avoid WatchDog \"\n\"closing connection before lastXmppPacketReceivedTime is set; #server-1337\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.3.0.inc:49\nmsgid \"\"\n\"Add proper addressing validation in S2S connection and allow connections \"\n\"without 'from' set; #server-1338\"\nmsgstr \"\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:2\nmsgid \"Tigase XMPP Server 8.2.0 Change notes\"\nmsgstr \"Tigase XMPP Server 8.2.0 变更说明\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:7\nmsgid \"\"\n\"**Improvements to s2s connection**: Version 8.2.0 brings a lot of \"\n\"improvements related to s2s connectivity: support for TLS1.3, improved \"\n\"logic during authentication and stream negotiation solving connectivity \"\n\"issues with various deployments\"\nmsgstr \"\"\n\"**对 s2s 连接的改进**：版本 8.2.0 带来了许多与 s2s 连接相关的改进：支持 \"\n\"TLS1.3，改进了身份验证和流协商期间的逻辑，解决了各种部署的连接问题\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:9\nmsgid \"\"\n\"**Better handling of certificates**: It’s now possible to store \"\n\"certificates in the database making it easier to manage them in clustered\"\n\" environment.\"\nmsgstr \"**更好地处理证书**：现在可以将证书存储在数据库中，从而更容易在集群环境中管理它们。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:11\nmsgid \"Deprecation of ``Element`` based events in favour of Object based events\"\nmsgstr \"弃用基于``Element`` 的事件，支持基于对象的事件\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:13\nmsgid \"Improved performance: reduced memory usage and decrease startup time\"\nmsgstr \"提高性能：减少内存使用并缩短启动时间\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:18\nmsgid \"\"\n\"`#server-1050 <https://projects.tigase.net/issue/server-1050>`__: \"\n\"Database installation without root credentials\"\nmsgstr \"\"\n\"`#server-1050 <https://projects.tigase.net/issue/server-1050>`__:没有 \"\n\"根凭证的数据库安装\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:20\nmsgid \"\"\n\"`#server-1062 <https://projects.tigase.net/issue/server-1062>`__: \"\n\"Deprecate Element based Event-bus\"\nmsgstr \"\"\n\"`#server-1062 \"\n\"<https://projects.tigase.net/issue/server-1062>`__:弃用基于事件总线的元素\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:22\nmsgid \"\"\n\"`#server-1097 <https://projects.tigase.net/issue/server-1097>`__: It’s \"\n\"not possible to configure additional PacketFilters\"\nmsgstr \"\"\n\"`#server-1097 <https://projects.tigase.net/issue/server-1097>`__: \"\n\"无法配置额外的包过滤器\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:24\nmsgid \"\"\n\"`#server-1101 <https://projects.tigase.net/issue/server-1101>`__: \"\n\"Enabling TLS1.3 causes s2s connections to fail\"\nmsgstr \"\"\n\"`#server-1101 <https://projects.tigase.net/issue/server-1101>`__: 启用 \"\n\"TLS1.3 会导致 s2s 连接失败\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:26\nmsgid \"\"\n\"`#server-1102 <https://projects.tigase.net/issue/server-1102>`__: Add \"\n\"possibility to extend MAM to MAM:2\"\nmsgstr \"\"\n\"`#server-1102 <https://projects.tigase.net/issue/server-1102>`__:增加将 MAM \"\n\"扩展到 MAM:2 的可能性\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:28\nmsgid \"\"\n\"`#server-1105 <https://projects.tigase.net/issue/server-1105>`__: Enhance\"\n\" Add SSL Certificate ad-hoc with option to set default\"\nmsgstr \"\"\n\"`#server-1105 <https://projects.tigase.net/issue/server-1105>`__: 增强添加 \"\n\"SSL 证书临时选项以设置默认值\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:30\nmsgid \"\"\n\"`#server-1119 <https://projects.tigase.net/issue/server-1119>`__: Use \"\n\"database for certificate storage instead of filesystem\"\nmsgstr \"\"\n\"`#server-1119 <https://projects.tigase.net/issue/server-1119>`__: \"\n\"用数据库替代文件系统来存储证书\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:32\nmsgid \"\"\n\"`#server-1120 <https://projects.tigase.net/issue/server-1120>`__: \"\n\"JabberIqRegister should allow enforcing both CAPTCHA and e-mail\"\nmsgstr \"\"\n\"`#server-1120 <https://projects.tigase.net/issue/server-1120>`__: \"\n\"JabberIqRegister 应该允许同时执行 CAPTCHA 和电子邮件\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:34\nmsgid \"\"\n\"`#server-1132 <https://projects.tigase.net/issue/server-1132>`__: Don’t \"\n\"use s2s socket if only one-direction works\"\nmsgstr \"\"\n\"`#server-1132 \"\n\"<https://projects.tigase.net/issue/server-1132>`__:如果只有一个方向有效，请不要使用 s2s \"\n\"套接字\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:36\nmsgid \"\"\n\"`#server-1142 <https://projects.tigase.net/issue/server-1142>`__: After \"\n\"registration inform the client that the account activation (email) is \"\n\"required\"\nmsgstr \"\"\n\"`#server-1142 <https://projects.tigase.net/issue/server-1142>`__: \"\n\"注册后要求通过电子邮件通知客户激活帐户\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:38\nmsgid \"\"\n\"`#server-1158 <https://projects.tigase.net/issue/server-1158>`__: \"\n\"Establishing JMX connection to the server causes excessive memory \"\n\"allocation\"\nmsgstr \"\"\n\"`#server-1158 <https://projects.tigase.net/issue/server-1158>`__: 与服务器建立 \"\n\"JMX 连接会导致内存分配过多\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:40\nmsgid \"\"\n\"`#server-1162 <https://projects.tigase.net/issue/server-1162>`__: Allow \"\n\"interfaces in @ConfigField\"\nmsgstr \"\"\n\"`#server-1162 <https://projects.tigase.net/issue/server-1162>`__: \"\n\"在@ConfigField 中允许接口\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:42\nmsgid \"\"\n\"`#server-1170 <https://projects.tigase.net/issue/server-1170>`__: TLS \"\n\"infinity loop impacts Tigase XMPP Server performance\"\nmsgstr \"\"\n\"`#server-1170 <https://projects.tigase.net/issue/server-1170>`__: TLS \"\n\"无限循环影响 Tigase XMPP 服务器性能\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:44\nmsgid \"\"\n\"`#server-1175 <https://projects.tigase.net/issue/server-1175>`__: \"\n\"Connection with diebesban.de stopped with invalid-namespace error\"\nmsgstr \"\"\n\"`#server-1175 <https://projects.tigase.net/issue/server-1175>`__: 与 \"\n\"diebesban.de 的连接因无效命名空间错误而停止\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:46\nmsgid \"\"\n\"`#server-1177 <https://projects.tigase.net/issue/server-1177>`__: Ability\"\n\" to change log level during runtime\"\nmsgstr \"\"\n\"`#server-1177 <https://projects.tigase.net/issue/server-1177>`__: \"\n\"能够在运行时更改日志级别\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:48\nmsgid \"\"\n\"`#server-1178 <https://projects.tigase.net/issue/server-1178>`__: Remove \"\n\"``online_status`` from the repository\"\nmsgstr \"\"\n\"`#server-1178 <https://projects.tigase.net/issue/server-1178>`__: 从存储库中删除\"\n\" ``online_status``（在线状态）\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:50\nmsgid \"\"\n\"`#server-1179 <https://projects.tigase.net/issue/server-1179>`__: Add \"\n\"support for {clusterNode} in XEP-0215 host field\"\nmsgstr \"\"\n\"`#server-1179 <https://projects.tigase.net/issue/server-1179>`__: 在 \"\n\"XEP-0215 主机字段中添加对 {clusterNode（集群节点）} 的支持\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:52\nmsgid \"\"\n\"`#server-1181 <https://projects.tigase.net/issue/server-1181>`__: \"\n\"NoSuchElementException in MaxDailyCounterQueue\"\nmsgstr \"\"\n\"`#server-1181 <https://projects.tigase.net/issue/server-1181>`__ \"\n\"MaxDailyCounterQueue 中的 NoSuchElementException\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:54\nmsgid \"\"\n\"`#server-1182 <https://projects.tigase.net/issue/server-1182>`__: NPE \"\n\"while processing <iq type=\\\"result\\\"/> without existing session\"\nmsgstr \"\"\n\"`#server-1182 <https://projects.tigase.net/issue/server-1182>`__: \"\n\"在没有现有会话的情况下处理 <iq type=\\\"result\\\"/> 时的 NPE\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:56\nmsgid \"\"\n\"`#server-1187 <https://projects.tigase.net/issue/server-1187>`__: \"\n\"SchemaLoader should not print passwords in the logs (URL logs)\"\nmsgstr \"\"\n\"`#server-1187 <https://projects.tigase.net/issue/server-1187>`__: \"\n\"SchemaLoader 不应在日志（URL 日志）中打印密码\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:58\nmsgid \"\"\n\"`#server-1192 <https://projects.tigase.net/issue/server-1192>`__: \"\n\"Obfuscate repository passwords\"\nmsgstr \"`#server-1192 <https://projects.tigase.net/issue/server-1192>`__: 混淆存储库密码\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:60\nmsgid \"\"\n\"`#server-1190 <https://projects.tigase.net/issue/server-1190>`__: \"\n\"Executing EditUser on non-existen’t user causes creation of the user\"\nmsgstr \"\"\n\"`#server-1190 <https://projects.tigase.net/issue/server-1190>`__: \"\n\"对不存在的用户执行 EditUser 会导致创建新用户\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:62\nmsgid \"\"\n\"`#server-1193 <https://projects.tigase.net/issue/server-1193>`__: Push \"\n\"notifications are sent for groupchat messages without <body/>\"\nmsgstr \"\"\n\"`#server-1193 <https://projects.tigase.net/issue/server-1193>`__: \"\n\"对没有<body/> 的群聊消息发送推送通知\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:64\nmsgid \"\"\n\"`#server-1197 <https://projects.tigase.net/issue/server-1197>`__: \"\n\"Infinite loop while cutting body of encrypted push notification to fit \"\n\"the push notifications limit\"\nmsgstr \"\"\n\"`#server-1197 <https://projects.tigase.net/issue/server-1197>`__: \"\n\"无限循环，同时删减加密推送通知的主体以适应推送通知限制\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:66\nmsgid \"\"\n\"`#server-1199 <https://projects.tigase.net/issue/server-1199>`__: Don’t \"\n\"send any packets until s2s stream negotiation is finished\"\nmsgstr \"\"\n\"`#server-1199 <https://projects.tigase.net/issue/server-1199>`__: 在 s2s \"\n\"流协商完成之前不要发送任何数据包\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:68\nmsgid \"\"\n\"`#server-1200 <https://projects.tigase.net/issue/server-1200>`__: Use \"\n\"proper size of network buffers for high-throughput connections\"\nmsgstr \"\"\n\"`#server-1200 <https://projects.tigase.net/issue/server-1200>`__: \"\n\"对高通量的连接使用适当大小的网络缓冲区\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:70\nmsgid \"\"\n\"`#server-1203 <https://projects.tigase.net/issue/server-1203>`__: Handing\"\n\" error packets in CIDConnections.sendPacketsBack\"\nmsgstr \"\"\n\"`#server-1203 <https://projects.tigase.net/issue/server-1203>`__: 在 \"\n\"CIDConnections.sendPacketsBack 中处理错误数据包\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:72\nmsgid \"\"\n\"`#server-1217 <https://projects.tigase.net/issue/server-1217>`__: Prevent\"\n\" performing schema upgrade concurrently\"\nmsgstr \"\"\n\"`#server-1217 <https://projects.tigase.net/issue/server-1217>`__: \"\n\"防止同时执行架构升级\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:74\nmsgid \"\"\n\"`#server-1219 <https://projects.tigase.net/issue/server-1219>`__: Use all\"\n\" JDBC URI parameters from config.tdsl when performing database upgrade.\"\nmsgstr \"\"\n\"`#server-1219 <https://projects.tigase.net/issue/server-1219>`__: \"\n\"执行数据库升级时，使用 config.tdsl 中的所有 JDBC URI 参数。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:76\nmsgid \"\"\n\"`#server-1222 <https://projects.tigase.net/issue/server-1222>`__: Add \"\n\"support for XEP-0377: Spam Reporting\"\nmsgstr \"\"\n\"`#server-1222 <https://projects.tigase.net/issue/server-1222>`__: 添加对 \"\n\"XEP-0377：垃圾邮件报告的支持\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:78\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:80\nmsgid \"\"\n\"`#server-1229 <https://projects.tigase.net/issue/server-1229>`__: \"\n\"Enabling CAPTCHA or e-mail for JabberIqRegister breaks password changing \"\n\"functionality.\"\nmsgstr \"\"\n\"`#server-1229 <https://projects.tigase.net/issue/server-1229>`__: 为 \"\n\"JabberIqRegister 启用 CAPTCHA 或电子邮件会损害密码更改功能。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:82\nmsgid \"\"\n\"`#server-1233 <https://projects.tigase.net/issue/server-1233>`__: Add \"\n\"option to CertificateRepository to load certificates from the filesystem\"\nmsgstr \"\"\n\"`#server-1233 <https://projects.tigase.net/issue/server-1233>`__: 向 \"\n\"CertificateRepository 添加选项以从文件系统加载证书\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:84\nmsgid \"\"\n\"`#server-1234 <https://projects.tigase.net/issue/server-1234>`__: Roster \"\n\"API improvements\"\nmsgstr \"\"\n\"`#server-1234 <https://projects.tigase.net/issue/server-1234>`__: 名册 API \"\n\"改进\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:86\nmsgid \"\"\n\"`#server-1237 <https://projects.tigase.net/issue/server-1237>`__: Rework \"\n\"CertificateRepository so items are stored individually\"\nmsgstr \"\"\n\"`#server-1237 <https://projects.tigase.net/issue/server-1237>`__: 修改 \"\n\"CertificateRepository，以便单独存储项目\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:88\nmsgid \"\"\n\"`#server-1238 <https://projects.tigase.net/issue/server-1238>`__: Can’t \"\n\"set MOTD via ad-hoc.\"\nmsgstr \"\"\n\"`#server-1238 <https://projects.tigase.net/issue/server-1238>`__: 无法通过 \"\n\"ad-hoc 设置 MOTD。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:90\nmsgid \"\"\n\"`#server-1243 <https://projects.tigase.net/issue/server-1243>`__: Include\"\n\" wait-for-it.sh script in base distribution\"\nmsgstr \"\"\n\"`#server-1243 <https://projects.tigase.net/issue/server-1243>`__: \"\n\"在基本发行版中包含 wait-for-it.sh 脚本\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:92\nmsgid \"\"\n\"`#server-1245 <https://projects.tigase.net/issue/server-1245>`__: \"\n\"MethodStatistics doesn’t work well for interfaces with overloaded methods\"\nmsgstr \"\"\n\"`#server-1245 <https://projects.tigase.net/issue/server-1245>`__: \"\n\"MethodStatistics 不适用于有重载方法的接口\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:94\nmsgid \"\"\n\"`#server-1251 <https://projects.tigase.net/issue/server-1251>`__: Can’t \"\n\"initialise MAM processor with default installation\"\nmsgstr \"\"\n\"`#server-1251 <https://projects.tigase.net/issue/server-1251>`__: \"\n\"无法使用默认安装初始化 MAM 处理器\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:96\nmsgid \"\"\n\"`#server-1252 <https://projects.tigase.net/issue/server-1252>`__: Remove \"\n\"select row_count() from Tig_OfflineMessages_DeleteMessage\"\nmsgstr \"\"\n\"`#server-1252 <https://projects.tigase.net/issue/server-1252>`__: 从 \"\n\"Tig_OfflineMessages_DeleteMessage 中删除选择的 row_count()\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:98\nmsgid \"\"\n\"`#server-1253 <https://projects.tigase.net/issue/server-1253>`__: It \"\n\"seems that 'expired-processor' doesn’t remove periodically expired \"\n\"messages\"\nmsgstr \"\"\n\"`#server-1253 <https://projects.tigase.net/issue/server-1253>`__: \"\n\"'expired-processor' 疑似不会定期删除过期的消息\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:100\nmsgid \"\"\n\"`#server-1254 <https://projects.tigase.net/issue/server-1254>`__: Fix \"\n\"slow startup and shutdown\"\nmsgstr \"\"\n\"`#server-1254 <https://projects.tigase.net/issue/server-1254>`__: \"\n\"修复启动和关闭缓慢的问题\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:102\nmsgid \"\"\n\"`#server-1258 <https://projects.tigase.net/issue/server-1258>`__: Allow \"\n\"beans to be instantiated without the requirement to reference/inject them\"\nmsgstr \"\"\n\"`#server-1258 <https://projects.tigase.net/issue/server-1258>`__: \"\n\"无需引用/注入bean,但是允许实例化它们\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:104\nmsgid \"\"\n\"`#server-1260 <https://projects.tigase.net/issue/server-1260>`__: \"\n\"UserConnectedEvent should be a cluster event\"\nmsgstr \"\"\n\"`#server-1260 <https://projects.tigase.net/issue/server-1260>`__: \"\n\"UserConnectedEvent 应该是一个集群事件\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:106\nmsgid \"\"\n\"`#server-1261 <https://projects.tigase.net/issue/server-1261>`__: Revise \"\n\"and improve EventBus developer guide\"\nmsgstr \"\"\n\"`#server-1261 <https://projects.tigase.net/issue/server-1261>`__: 修订和改进 \"\n\"EventBus 开发者指南\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:108\nmsgid \"\"\n\"`#server-1269 <https://projects.tigase.net/issue/server-1269>`__: SSL \"\n\"issues are hidden by default making it difficult to identify\"\nmsgstr \"\"\n\"`#server-1269 <https://projects.tigase.net/issue/server-1269>`__: SSL \"\n\"问题被默认隐藏，难以识别\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:110\nmsgid \"\"\n\"`#server-1273 <https://projects.tigase.net/issue/server-1273>`__: Add \"\n\"option to limit number of concurrently connected resources\"\nmsgstr \"\"\n\"`#server-1273 <https://projects.tigase.net/issue/server-1273>`__: \"\n\"添加选项以限制并发连接的资源数量\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:112\nmsgid \"\"\n\"`#server-1277 <https://projects.tigase.net/issue/server-1277>`__: Fix \"\n\"HUGE out queue in StreamManagementIOProcessor\"\nmsgstr \"\"\n\"`#server-1277 <https://projects.tigase.net/issue/server-1277>`__: 修复 \"\n\"StreamManagementIOProcessor 中的 HUGE out queue\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:114\nmsgid \"\"\n\"`#server-1278 <https://projects.tigase.net/issue/server-1278>`__: NPE in \"\n\"StreamManagementIOProcessor.serviceStopped\"\nmsgstr \"\"\n\"`#server-1278 <https://projects.tigase.net/issue/server-1278>`__: \"\n\"在StreamManagementIOProcessor.serviceStopped 中的 NPE\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:116\nmsgid \"\"\n\"`#server-1282 <https://projects.tigase.net/issue/server-1282>`__: \"\n\"XMPPProcessorAbstract.processToUserPacket() responds to IQ result with \"\n\"error\"\nmsgstr \"\"\n\"`#server-1282 <https://projects.tigase.net/issue/server-1282>`__: \"\n\"XMPPProcessorAbstract.processToUserPacket() 以错误结果响应 IQ\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:118\nmsgid \"\"\n\"`#server-1284 <https://projects.tigase.net/issue/server-1284>`__: Add \"\n\"validation to JabberIqAuth\"\nmsgstr \"\"\n\"`#server-1284 <https://projects.tigase.net/issue/server-1284>`__: 向 \"\n\"JabberIqAuth 添加验证\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:120\nmsgid \"\"\n\"`#server-1285 <https://projects.tigase.net/issue/server-1285>`__: Wrong \"\n\"field type for XEP-0157 entries\"\nmsgstr \"\"\n\"`#server-1285 <https://projects.tigase.net/issue/server-1285>`__: \"\n\"XEP-0157 条目中的错误字段类型\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:122\nmsgid \"\"\n\"`#server-1290 <https://projects.tigase.net/issue/server-1290>`__: Improve\"\n\" StringPrep to actually forbid space in localpart/domain as per rfc7622\"\nmsgstr \"\"\n\"`#server-1290 <https://projects.tigase.net/issue/server-1290>`__: 根据 \"\n\"rfc7622 改进 StringPrep 以禁止 localpart/domain 中的空间\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:124\nmsgid \"\"\n\"`#server-1292 <https://projects.tigase.net/issue/server-1292>`__: TLS \"\n\"connectivity issue with search.jabber.network\"\nmsgstr \"\"\n\"`#server-1292 <https://projects.tigase.net/issue/server-1292>`__: \"\n\"search.jabber.network 的 TLS 连接问题\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:126\nmsgid \"\"\n\"`#server-1297 <https://projects.tigase.net/issue/server-1297>`__: Add \"\n\"option to push plugin that would allow to overwrite unencrypted part in \"\n\"(OMEMO) encrypted messages\"\nmsgstr \"\"\n\"`#server-1297 <https://projects.tigase.net/issue/server-1297>`__: \"\n\"添加选项以推送插件，从而允许覆盖（OMEMO）加密消息中的未加密部分\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:128\nmsgid \"\"\n\"`#server-1303 <https://projects.tigase.net/issue/server-1303>`__: Better \"\n\"handling of \\\"The target is unavailable at this time.\\\" / \"\n\"PacketInvalidTypeException\"\nmsgstr \"\"\n\"`#server-1303 <https://projects.tigase.net/issue/server-1303>`__: 更好地处理 \"\n\"\\\"The target is unavailable at this time.\\\" / PacketInvalidTypeException\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:130\nmsgid \"\"\n\"`#server-1305 <https://projects.tigase.net/issue/server-1305>`__: Allow \"\n\"creation of admin user (if not exist) during ``upgrade-schema`` task\"\nmsgstr \"\"\n\"`#server-1305 <https://projects.tigase.net/issue/server-1305>`__: 允许在\"\n\"``upgrade-schema``任务期间创建管理员（如果不存在）用户\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:132\nmsgid \"\"\n\"`#server-1306 <https://projects.tigase.net/issue/server-1306>`__: Fix \"\n\"farge amount of direct memory being used.\"\nmsgstr \"\"\n\"`#server-1306 <https://projects.tigase.net/issue/server-1306>`__: \"\n\"修复正在使用的大量直接内存。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:134\nmsgid \"\"\n\"`#server-1307 <https://projects.tigase.net/issue/server-1307>`__: Fix \"\n\"disconnection on MAM sync\"\nmsgstr \"\"\n\"`#server-1307 <https://projects.tigase.net/issue/server-1307>`__: 修复 MAM \"\n\"同步断开连接问题\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:136\nmsgid \"\"\n\"`#extras-3 <https://projects.tigase.net/issue/extras-3>`__: Add AWS \"\n\"logback and documentation how to use it\"\nmsgstr \"\"\n\"`#extras-3 <https://projects.tigase.net/issue/extras-3>`__: 添加 AWS \"\n\"logback 和如何使用它的文档\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:138\nmsgid \"\"\n\"`#extras-4 <https://projects.tigase.net/issue/extras-4>`__: Unescape and \"\n\"normalise logs in mail notifications before sending them\"\nmsgstr \"\"\n\"`#extras-4 <https://projects.tigase.net/issue/extras-4>`__: \"\n\"在发送邮件通知之前取消转义和规范化日志\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:140\nmsgid \"\"\n\"`#extras-7 <https://projects.tigase.net/issue/extras-7>`__: Add email \"\n\"validation during in-band-registration; better handling of mail sending \"\n\"exceptions regarding to non-existent addresses\"\nmsgstr \"\"\n\"`#extras-7 <https://projects.tigase.net/issue/extras-7>`__: \"\n\"在带内注册期间添加电子邮件验证；更好地处理给不存在的邮件地址发送邮件所产生的异常问题\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:142\nmsgid \"\"\n\"`#extras-9 <https://projects.tigase.net/issue/extras-9>`__: Deprecate \"\n\"mDNS implementation\"\nmsgstr \"`#extras-9 <https://projects.tigase.net/issue/extras-9>`__: 弃用 mDNS 实施\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.2.0.inc:144\nmsgid \"\"\n\"`#serverdist-8 <https://projects.tigase.net/issue/serverdist-8>`__: \"\n\"Remove DNS resolution part from XEP-0156 implementation\"\nmsgstr \"\"\n\"`#serverdist-8 <https://projects.tigase.net/issue/serverdist-8>`__:从 \"\n\"XEP-0156 实现中删除 DNS 解析部分\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:2\nmsgid \"Tigase XMPP Server 8.1.0 Change notes and announcement\"\nmsgstr \"Tigase XMPP Server 8.1.0 变更说明和公告\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:8\nmsgid \"More XMPP extensions\"\nmsgstr \"XMPP更多 扩展\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:10\nmsgid \"\"\n\"Following XMPP guidelines specified in *Compliance Suites* a number of \"\n\"extensions was included in this release:\"\nmsgstr \"遵循 *Compliance Suites* 中指定的 XMPP 指南，此版本中包含了许多扩展：\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:12\nmsgid \"\"\n\"**XEP-0157**: Contact Addresses for XMPP Services (`server-995 \"\n\"<https://projects.tigase.net/issue/server-995>`__) that can be configured\"\n\" on per VHost basis (`server-1015 \"\n\"<https://projects.tigase.net/issue/server-1015>`__)\"\nmsgstr \"\"\n\"**XEP-0157**: XMPP 服务的联系地址 (`server-995 \"\n\"<https://projects.tigase.net/issue/server-995>`__)可以在每个 VHost \"\n\"基础上进行配置(`server-1015 <https://projects.tigase.net/issue/server-1015>`__)\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:14\nmsgid \"\"\n\"**XEP-0398**: User Avatar to vCard-Based Avatars Conversion (`server-1017\"\n\" <https://projects.tigase.net/issue/server-1017>`__)\"\nmsgstr \"\"\n\"**XEP-0398**: 用户头像到基于 vCard 的头像转换 (`server-1017 \"\n\"<https://projects.tigase.net/issue/server-1017>`__)\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:16\nmsgid \"\"\n\"**XEP-0156**: Discovering Alternative XMPP Connection Methods - Tigase \"\n\"already supported handling DNS queries and standardised our \"\n\"``webservice`` to XEP-0156 (`http-76 \"\n\"<https://projects.tigase.net/issue/http-76>`__)\"\nmsgstr \"\"\n\"**XEP-0156**:发现另外一种 XMPP 连接方法 - Tigase 已经支持处理 DNS \"\n\"查询并将我们的``webservice``标准化为 XEP-0156 (`http-76 \"\n\"<https://projects.tigase.net/issue/http-76>`__)\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:18\nmsgid \"\"\n\"**XEP-0410**: MUC Self-Ping (Schrödinger’s Chat) (`muc-122 \"\n\"<https://projects.tigase.net/issue/http-76>`__)\"\nmsgstr \"\"\n\"**XEP-0410**: MUC Self-Ping (薛定谔的聊天) (`muc-122 \"\n\"<https://projects.tigase.net/issue/http-76>`__)\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:20\nmsgid \"\"\n\"**XEP-0153**: vCard-Based Avatars - added support for setting **vCard \"\n\"avatar for MUC rooms** (`muc-112 \"\n\"<https://projects.tigase.net/issue/http-76>`__)\"\nmsgstr \"\"\n\"**XEP-0153**: 基于 vCard 的头像 - 添加了对设置 **MUC 房间的 vCard 头像** 的支持 (`muc-112 \"\n\"<https://projects.tigase.net/issue/http-76>`__)\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:22\nmsgid \"\"\n\"**XEP-0411**: Bookmarks Conversion (`pubsub-79 \"\n\"<https://projects.tigase.net/issue/http-76>`__)\"\nmsgstr \"\"\n\"**XEP-0411**: 书签转换 (`pubsub-79 \"\n\"<https://projects.tigase.net/issue/http-76>`__)\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:24\nmsgid \"\"\n\"**XEP-0157**: Contact Addresses for XMPP Services (`server-995 \"\n\"<https://projects.tigase.net/issue/server-995>`__)\"\nmsgstr \"\"\n\"**XEP-0157**:XMPP 服务的联系地址 (`server-995 \"\n\"<https://projects.tigase.net/issue/server-995>`__)\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:29\nmsgid \"Improved connectivity with other servers\"\nmsgstr \"改进了与其他服务器的连接\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:31\nmsgid \"\"\n\"``SASL-EXTERNAL`` mechanism was added for server-to-server (federated, \"\n\"s2s) connections greatly improving compliance with XMPP network. It’s \"\n\"possible to use both SASL-EXTERNAL and Diallback depending on support in \"\n\"other servers.\"\nmsgstr \"\"\n\"为服务器到服务器（联合，s2s）连接添加了“SASL-EXTERNAL”机制，极大地提高了对 XMPP 网络的适应性。可能可以同时使用 SASL-\"\n\"EXTERNAL 和回拨，这取决于其他服务器是否支持。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:36\nmsgid \"Better security & privacy\"\nmsgstr \"更好的安全性和隐私性\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:38\nmsgid \"\"\n\"When it comes to connectivity, Tigase XMPP Server sported **Hardened \"\n\"Mode** that adjusted networking security settings (supported protocols, \"\n\"cipher suites and keys' length where applicable). We decided include \"\n\"3-level configuration option for **Hardened Mode** (roughly following \"\n\"*Mozilla’s SSL Configuration Generator*): ``relaxed``, ``secure`` \"\n\"(default) and ``strict`` and to further eliminate cipher suites that are \"\n\"currently considered insecure.\"\nmsgstr \"\"\n\"在连接方面，Tigase XMPP服务器 采用**强化模式**来调整网络安全设置（支持的协议，密码套件和适用的密钥长度）。我们决定让 \"\n\"**强化模式** 包含 3 级配置选项（基本遵循 *Mozilla 的 SSL 配置生成器*）：``relaxed``, \"\n\"``secure``（默认）和 ``strict``，并进一步消除当前被认为不安全的密码套件。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:40\nmsgid \"\"\n\"We also enabled by default our anti-spam plugin and because we like all-\"\n\"things-extensible we created a guide how to create your own pluggable \"\n\"filters for anti-spam-plugin.\"\nmsgstr \"我们还默认启用了我们的反垃圾邮件插件，因为我们喜欢万物可扩展，所以我们创建了一个指南，指导用户如何为反垃圾邮件插件创建自己的可插入过滤器。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:45\nmsgid \"Multiple domains (VHosts) support is even better\"\nmsgstr \"多域（VHosts）支持更好\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:47\nmsgid \"\"\n\"It was always quite easy to configure and serve multiple domains in \"\n\"Tigase XMPP Server. In this release we made it even better! First of all \"\n\"- we included ``Default`` VHost item, which allows configuring global \"\n\"defaults for the installation on the fly without having to change \"\n\"configuration files and restart the instance.\"\nmsgstr \"\"\n\"在 Tigase XMPP Server 中配置和服务多个域总是很容易的。在这个新发布的版本中，我们做得更好！首先 - \"\n\"我们包括了``Default`` VHost 项，它可以为即时安装配置全局默认值，而无需更改配置文件并重启实例。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:49\nmsgid \"\"\n\"Internally, we introduced *VHost Extensions* - a mechanism that allows \"\n\"easy addition of configurable options that can be set on per-domain \"\n\"basis.\"\nmsgstr \"在内部，我们引入了*VHost Extensions* - 一种允许轻松添加并可在每个域基础上设置的可配置选项的机制。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:51\nmsgid \"\"\n\"On top of that we reworked how SSL certificates are handled (especially \"\n\"wildcard ones) and now they are loaded and assigned to correct domain \"\n\"automatically - no need to configure *star*-certificates manually \"\n\"anymore.\"\nmsgstr \"\"\n\"最重要的是，我们重新设计了 SSL 证书的处理方式（尤其是通配符证书），现在它们会自动加载并分配给正确的域 - 不再需要手动配置 \"\n\"*star*-certificates。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:56\nmsgid \"Mobile First\"\nmsgstr \"移动优先\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:58\nmsgid \"\"\n\"Notifications send to mobile applications via Apple’s and Google’s push \"\n\"servers using **Tigase’s PUSH component** are now encrypted (`#push-25 \"\n\"<https://projects.tigase.net/issue/push-25>`__), requires compatible \"\n\"clients)\"\nmsgstr \"\"\n\"使用 **Tigase 的 PUSH 组件**并 通过 Apple 和 Google \"\n\"的推送服务器发送到移动应用程序的通知现在已加密（`#push-25 \"\n\"<https://projects.tigase.net/issue/push-25>`__），需要兼容的客户端）\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:60\nmsgid \"\"\n\"MUC component now allows users to register permanent nickname, which \"\n\"makes it possible to receive PUSH notifications even if our client \"\n\"disconnects and is offline (`#muc-115 \"\n\"<https://projects.tigase.net/issue/muc-115>`__)\"\nmsgstr \"\"\n\"MUC 组件现在允许用户注册永久昵称，这使得即使我们的客户端断开连接和离线也可以接收推送通知（`#muc-115 \"\n\"<https://projects.tigase.net/issue/muc-115>` __)\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:65\nmsgid \"Installation & management\"\nmsgstr \"安装与管理\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:67\nmsgid \"\"\n\"The (web) installer was simplified making setting up and configuring \"\n\"Tigase even easier (`#http-78 \"\n\"<https://projects.tigase.net/issue/http-78>`__) - now it’s only needed to\"\n\" select desired database, provide it’s details and eventually adjust \"\n\"which components and plugins should be enabled or disabled, but we \"\n\"believe that provided defaults should work well in most of the cases.\"\nmsgstr \"\"\n\"(web) 安装程序已简化，使得设置和配置 Tigase 更加容易（`#http-78 \"\n\"<https://projects.tigase.net/issue/http-78>`__） - \"\n\"现在只需要选择所需的数据库，提供详细信息并最后调整启用或禁用的组件和插件即可，但我们相信在提供的默认值下程序在大多数情况下可以正常工作。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:69\nmsgid \"\"\n\"After the installation and startup, it’s possible to see basic instance \"\n\"state via web browser either opening ``/server/`` endpoint (`#server-1164\"\n\" <https://projects.tigase.net/issue/server-1164>`__), or local file from \"\n\"``logs/server-info.html``) and manage the installation using Admin WebUI,\"\n\" that received slight visual face-lift (`#http-90 \"\n\"<https://projects.tigase.net/issue/http-90>`__)\"\nmsgstr \"\"\n\"安装和启动后，可以通过 Web 浏览器查看基本实例状态，或者打开 ``/server/`` 端点（`#server-1164 \"\n\"<https://projects.tigase.net/issue/server-1164>` __)，或来自 ``logs/server-\"\n\"info.html`` 的本地文件，并使用 Admin WebUI 管理安装，该安装得到了轻微的视觉提升（`#http-90 \"\n\"<https://projects.tigase.net /issue/http-90>`__)\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:74\nmsgid \"Noteworthy\"\nmsgstr \"显著不同点\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:76\nmsgid \"\"\n\"Startup time was significantly reduced due to improvements of creating \"\n\"repository pools (`#server-1149 \"\n\"<https://projects.tigase.net/issue/server-1149>`__)\"\nmsgstr \"\"\n\"由于创建存储库池的改进（`#server-1149 \"\n\"<https://projects.tigase.net/issue/server-1149>`__），启动时间显著减少\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:78\nmsgid \"\"\n\"Multi-thread, highly concurrent script execution was improved \"\n\"(`#server-1154 <https://projects.tigase.net/issue/server-1154>`__)\"\nmsgstr \"\"\n\"改进了多线程，高并发脚本执行（`#server-1154 \"\n\"<https://projects.tigase.net/issue/server-1154>`__）\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:80\nmsgid \"\"\n\"StreamManagement was available, but in this version we decided to enabled\"\n\" it by default.\"\nmsgstr \"StreamManagement 还是可用的，但在这个版本中，我们决定默认启用它。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:82\nmsgid \"\"\n\"More places offers support for `XEP-0059: Result Set Management \"\n\"<https://xmpp.org/extensions/xep-0059.html>`__ - namely PubSub nodes \"\n\"discovery and ``jabber:iq:serach``\"\nmsgstr \"\"\n\"更多地方提供了对`XEP-0059: Result Set Management \"\n\"<https://xmpp.org/extensions/xep-0059.html>`__的支持 - 即PubSub节点发现和 \"\n\"``jabber:iq:serach``\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:84\nmsgid \"\"\n\"`Publishing Options <https://xmpp.org/extensions/xep-0060.html#publisher-\"\n\"publish-options>`__ were added to PubSub (`#pubsub-75 \"\n\"<https://projects.tigase.net/issue/pubsub-75>`__)\"\nmsgstr \"\"\n\"`发布选项 <https://xmpp.org/extensions/xep-0060.html#publisher-publish-\"\n\"options>`__ 已被添加到 PubSub (`#pubsub-75 \"\n\"<https://projects.tigase.net/issue/pubsub-75>`__)\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:304\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:87\nmsgid \"New Minor Features & Behavior Changes\"\nmsgstr \"新的次要功能和行为变化\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:89\nmsgid \"\"\n\"`server-918 <https://projects.tigase.net/issue/server-918>`__: AWS obtain\"\n\" public IP and/or DNS address of the EC2 instance\"\nmsgstr \"\"\n\"`server-918 <https://projects.tigase.net/issue/server-918>`__:AWS 获取 EC2 \"\n\"实例的公共 IP 和/或 DNS 地址\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:91\nmsgid \"\"\n\"`server-985 <https://projects.tigase.net/issue/server-985>`__: Add \"\n\"support for SCRAM-SHA-512(-PLUS)\"\nmsgstr \"\"\n\"`server-985 <https://projects.tigase.net/issue/server-985>`__:添加对 SCRAM-\"\n\"SHA-512(-PLUS) 的支持\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:93\nmsgid \"\"\n\"`spam-8 <https://projects.tigase.net/issue/spam-8>`__: Enable spam \"\n\"processor by default\"\nmsgstr \"`spam-8 <https://projects.tigase.net/issue/spam-8>`__: 默认启用垃圾邮件处理器\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:95\nmsgid \"\"\n\"`server-1012 <https://projects.tigase.net/issue/server-1012>`__: \"\n\"UserDomainFilter.groovy fails to load\"\nmsgstr \"\"\n\"`server-1012 \"\n\"<https://projects.tigase.net/issue/server-1012>`__:UserDomainFilter.groovy\"\n\" 加载失败\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:97\nmsgid \"\"\n\"`server-1014 <https://projects.tigase.net/issue/server-1014>`__: Can’t \"\n\"upgrade from 8.0.0GA to 8.1.0-SNAPSHOT\"\nmsgstr \"\"\n\"`server-1014 <https://projects.tigase.net/issue/server-1014>`__:无法从 \"\n\"8.0.0GA 升级到 8.1.0-SNAPSHOT\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:99\nmsgid \"\"\n\"`server-798 <https://projects.tigase.net/issue/server-798>`__: Limit \"\n\"number of messages that are stored in DB per user within a period of time\"\nmsgstr \"\"\n\"`server-798 \"\n\"<https://projects.tigase.net/issue/server-798>`__:限制每个用户在一定时间段内存储在数据库中的消息数\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:101\nmsgid \"\"\n\"`server-827 <https://projects.tigase.net/issue/server-827>`__: Seperate \"\n\"Component-based statistics\"\nmsgstr \"`server-827 <https://projects.tigase.net/issue/server-827>`__: 单独基于组件的统计信息\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:103\nmsgid \"\"\n\"`server-1026 <https://projects.tigase.net/issue/server-1026>`__: NPE: in \"\n\"JabberIqRegister/EmailConfirmationSender\"\nmsgstr \"\"\n\"`server-1026 <https://projects.tigase.net/issue/server-1026>`__:NPE：在 \"\n\"JabberIqRegister/EmailConfirmationSender 中\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:105\nmsgid \"\"\n\"`pubsub-82 <https://projects.tigase.net/issue/pubsub-82>`__: NPE in \"\n\"RetrieveItemsModule\"\nmsgstr \"\"\n\"`pubsub-82 <https://projects.tigase.net/issue/pubsub-82>`__: \"\n\"RetrieveItemsModule 中的 NPE\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:107\nmsgid \"\"\n\"`tigaseim-78 <https://projects.tigase.net/issue/tigaseim-78>`__: IPv6 \"\n\"connectivity issue\"\nmsgstr \"`tigaseim-78 <https://projects.tigase.net/issue/tigaseim-78>`__:IPv6 连接问题\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:109\nmsgid \"\"\n\"`server-239 <https://projects.tigase.net/issue/server-239>`__: OSGi mode \"\n\"- exceptions in logs\"\nmsgstr \"\"\n\"`server-239 <https://projects.tigase.net/issue/server-239>`__:OSGi 模式 - \"\n\"日志中的异常\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:111\nmsgid \"\"\n\"`server-1020 <https://projects.tigase.net/issue/server-1020>`__: Enable \"\n\"stream management by default\"\nmsgstr \"`server-1020 <https://projects.tigase.net/issue/server-1020>`__:默认启用流管理\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:113\nmsgid \"\"\n\"`pubsub-83 <https://projects.tigase.net/issue/pubsub-83>`__: NPE in \"\n\"PublishItemModule\"\nmsgstr \"\"\n\"`pubsub-83 \"\n\"<https://projects.tigase.net/issue/pubsub-83>`__:PublishItemModule 中的 NPE\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:115\nmsgid \"\"\n\"`pubsub-81 <https://projects.tigase.net/issue/pubsub-81>`__: Exception \"\n\"during execution of event: \"\n\"tigase.pubsub.modules.PresenceCollectorModule.PresenceChangeEvent\"\nmsgstr \"\"\n\"`pubsub-81 <https://projects.tigase.net/issue/pubsub-81>`__:事件执行期间的异常: \"\n\"tigase.pubsub.modules.PresenceCollectorModule.PresenceChangeEvent\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:117\nmsgid \"\"\n\"`server-1021 <https://projects.tigase.net/issue/server-1021>`__: NPE: \"\n\"Cannot update BruteForceLocker\"\nmsgstr \"\"\n\"`server-1021 <https://projects.tigase.net/issue/server-1021>`__:NPE：无法更新 \"\n\"BruteForceLocker\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:119\nmsgid \"\"\n\"`server-826 <https://projects.tigase.net/issue/server-826>`__: \"\n\"UserRepository caches force synchronization even if caching is disabled\"\nmsgstr \"\"\n\"`server-826 \"\n\"<https://projects.tigase.net/issue/server-826>`__:即使缓存被禁用，UserRepository \"\n\"缓存也会强制同步\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:121\nmsgid \"\"\n\"`server-958 <https://projects.tigase.net/issue/server-958>`__: Add \"\n\"timeout for opened TCP connections\"\nmsgstr \"\"\n\"`server-958 <https://projects.tigase.net/issue/server-958>`__:打开的 TCP \"\n\"连接添加超时\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:123\nmsgid \"\"\n\"`server-1029 <https://projects.tigase.net/issue/server-1029>`__: Read \"\n\"receipients are not copied via carbons\"\nmsgstr \"\"\n\"`server-1029 \"\n\"<https://projects.tigase.net/issue/server-1029>`__:阅读收件人不通过碳复制\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:125\nmsgid \"\"\n\"`server-1015 <https://projects.tigase.net/issue/server-1015>`__: Allow \"\n\"configuring XEP-0157: Contact Addresses on per VHost basis\"\nmsgstr \"\"\n\"`server-1015 <https://projects.tigase.net/issue/server-1015>`__:允许配置 \"\n\"XEP-0157：基于每个 VHost 的联系地址\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:127\nmsgid \"\"\n\"`pubsub-65 <https://projects.tigase.net/issue/pubsub-65>`__: RSM and \"\n\"jabber:search for pubsub discovery\"\nmsgstr \"\"\n\"`pubsub-65 <https://projects.tigase.net/issue/pubsub-65>`__: RSM 和 \"\n\"jabber：搜索 pubsub 发现\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:129\nmsgid \"\"\n\"`server-1030 <https://projects.tigase.net/issue/server-1030>`__: NPE in \"\n\"VCardTemp when processing initial presence\"\nmsgstr \"\"\n\"`server-1030 <https://projects.tigase.net/issue/server-1030>`__:处理初始存在时 \"\n\"VCardTemp 中的 NPE\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:131\nmsgid \"\"\n\"`http-72 <https://projects.tigase.net/issue/http-72>`__: Change Content-\"\n\"Disposition from attachment to inline\"\nmsgstr \"\"\n\"`http-72 <https://projects.tigase.net/issue/http-72>`__:将 Content-\"\n\"Disposition 从附件更改为内联\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:133\nmsgid \"\"\n\"`server-1045 <https://projects.tigase.net/issue/server-1045>`__: NPE in \"\n\"DiscoExtensionsForm\"\nmsgstr \"\"\n\"`server-1045 \"\n\"<https://projects.tigase.net/issue/server-1045>`__:DiscoExtensionsForm 中的\"\n\" NPE\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:135\nmsgid \"\"\n\"`server-1048 <https://projects.tigase.net/issue/server-1048>`__: Update \"\n\"parent pom and information about suggested JDK\"\nmsgstr \"\"\n\"`server-1048 \"\n\"<https://projects.tigase.net/issue/server-1048>`__:更新相关建议的JDK 的父 pom 和信息\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:137\nmsgid \"\"\n\"`push-23 <https://projects.tigase.net/issue/push-23>`__: [JDK12] Can’t \"\n\"establish encrypted connection with Push/FCM\"\nmsgstr \"\"\n\"`push-23 <https://projects.tigase.net/issue/push-23>`__: [JDK12] 无法与 \"\n\"Push/FCM 建立加密连接\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:139\nmsgid \"\"\n\"`server-978 <https://projects.tigase.net/issue/server-978>`__: Improve \"\n\"VHost configuration / extending\"\nmsgstr \"\"\n\"`server-978 <https://projects.tigase.net/issue/server-978>`__:改进 VHost \"\n\"配置/扩展\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:141\nmsgid \"\"\n\"`server-1068 <https://projects.tigase.net/issue/server-1068>`__: Improve \"\n\"LogFormat readability (and maybe performance)\"\nmsgstr \"\"\n\"`server-1068 <https://projects.tigase.net/issue/server-1068>`__: 提高 \"\n\"LogFormat 的可读性（也许还有表现性能）\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:143\nmsgid \"\"\n\"`server-1070 <https://projects.tigase.net/issue/server-1070>`__: Improve \"\n\"privacy list loggging\"\nmsgstr \"`server-1070 <https://projects.tigase.net/issue/server-1070>`__:改进隐私列表记录\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:145\nmsgid \"\"\n\"`server-1071 <https://projects.tigase.net/issue/server-1071>`__: NPE in \"\n\"IOService.accept\"\nmsgstr \"\"\n\"`server-1071 <https://projects.tigase.net/issue/server-1071>`__: \"\n\"IOService.accept 中的 NPE\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:147\nmsgid \"\"\n\"`server-710 <https://projects.tigase.net/issue/server-710>`__: \"\n\"Registration improvements\"\nmsgstr \"`server-710 <https://projects.tigase.net/issue/server-710>`__:注册的改进\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:149\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:249\nmsgid \"\"\n\"`pubsub-79 <https://projects.tigase.net/issue/pubsub-79>`__: XEP-0411: \"\n\"Bookmarks Conversion\"\nmsgstr \"`pubsub-79 <https://projects.tigase.net/issue/pubsub-79>`__: XEP-0411:书签转换\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:151\nmsgid \"\"\n\"`pubsub-75 <https://projects.tigase.net/issue/pubsub-75>`__: Add support \"\n\"for Publishing Options\"\nmsgstr \"`pubsub-75 <https://projects.tigase.net/issue/pubsub-75>`__: 添加对发布选项的支持\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:153\nmsgid \"\"\n\"`server-1017 <https://projects.tigase.net/issue/server-1017>`__: \"\n\"XEP-0398: User Avatar to vCard-Based Avatars Conversion\"\nmsgstr \"\"\n\"`server-1017 <https://projects.tigase.net/issue/server-1017>`__: \"\n\"XEP-0398: 用户头像到基于 vCard 的头像转换\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:155\nmsgid \"\"\n\"`server-994 <https://projects.tigase.net/issue/server-994>`__: Add server\"\n\" support for Entity Capabilities: Stream Feature\"\nmsgstr \"\"\n\"`server-994 <https://projects.tigase.net/issue/server-994>`__: \"\n\"添加对实体功能的服务器支持：流功能\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:157\nmsgid \"\"\n\"`server-995 <https://projects.tigase.net/issue/server-995>`__: XEP-0157: \"\n\"Contact Addresses for XMPP Services\"\nmsgstr \"\"\n\"`server-995 <https://projects.tigase.net/issue/server-995>`__: XEP-0157: \"\n\"XMPP 服务的联系地址\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:159\nmsgid \"\"\n\"`http-76 <https://projects.tigase.net/issue/http-76>`__: Standardise DNS \"\n\"webservice to XEP-0156\"\nmsgstr \"\"\n\"`http-76 <https://projects.tigase.net/issue/http-76>`__:将 DNS 网络服务标准化为 \"\n\"XEP-0156\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:161\nmsgid \"\"\n\"`server-1109 <https://projects.tigase.net/issue/server-1109>`__: Add \"\n\"recommended JDK version to documentation\"\nmsgstr \"\"\n\"`server-1109 <https://projects.tigase.net/issue/server-1109>`__:将推荐的 JDK \"\n\"版本添加到文档中\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:163\nmsgid \"\"\n\"`push-28 <https://projects.tigase.net/issue/push-28>`__: Non-tigase \"\n\"notifications should use high priority (APNS)\"\nmsgstr \"\"\n\"`push-28 <https://projects.tigase.net/issue/push-28>`__:非 tigase \"\n\"通知应该使用高优先级 (APNS)\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:165\nmsgid \"\"\n\"`server-1114 <https://projects.tigase.net/issue/server-1114>`__: Can’t \"\n\"register on sure.im with StorkIM\"\nmsgstr \"\"\n\"`server-1114 <https://projects.tigase.net/issue/server-1114>`__:无法使用 \"\n\"StorkIM 在sure.im 上注册\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:167\nmsgid \"\"\n\"`server-1005 <https://projects.tigase.net/issue/server-1005>`__: Flatten \"\n\"schema to match versioning document\"\nmsgstr \"\"\n\"`server-1005 \"\n\"<https://projects.tigase.net/issue/server-1005>`__:拼合模式以匹配版本控制文档\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:169\nmsgid \"\"\n\"`server-1116 <https://projects.tigase.net/issue/server-1116>`__: \"\n\"account_status is not checked\"\nmsgstr \"\"\n\"`server-1116 \"\n\"<https://projects.tigase.net/issue/server-1116>`__:未检查account_status\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:171\nmsgid \"\"\n\"`server-1074 <https://projects.tigase.net/issue/server-1074>`__: Hardened\"\n\" Mode improvements\"\nmsgstr \"`server-1074 <https://projects.tigase.net/issue/server-1074>`__: 强化模式的改进\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:173\nmsgid \"\"\n\"`server-1125 <https://projects.tigase.net/issue/server-1125>`__: \"\n\"StatsDumper.groovy doesn’t work in documentation in 8.x\"\nmsgstr \"\"\n\"`server-1125 <https://projects.tigase.net/issue/server-1125>`__: \"\n\"StatsDumper.groovy 在 8.x 版本的文档中不起作用\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:175\nmsgid \"\"\n\"`http-85 <https://projects.tigase.net/issue/http-85>`__: Pasword resset \"\n\"doesn’t work\"\nmsgstr \"`http-85 <https://projects.tigase.net/issue/http-85>`__:密码重置不工作\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:177\nmsgid \"\"\n\"`server-1128 <https://projects.tigase.net/issue/server-1128>`__: Possible\"\n\" vulnerability in XML parser\"\nmsgstr \"\"\n\"`server-1128 <https://projects.tigase.net/issue/server-1128>`__:在XML \"\n\"解析器中可能存在的漏洞\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:179\nmsgid \"\"\n\"`server-1130 <https://projects.tigase.net/issue/server-1130>`__: NPE i \"\n\"JabberIqAuth\"\nmsgstr \"\"\n\"`server-1130 <https://projects.tigase.net/issue/server-1130>`__: NPE i \"\n\"JabberIqAuth\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:181\nmsgid \"\"\n\"`http-84 <https://projects.tigase.net/issue/http-84>`__: Configurable \"\n\"``resetPassword`` endpoint hostname\"\nmsgstr \"\"\n\"`http-84 <https://projects.tigase.net/issue/http-84>`__: 可配置的 \"\n\"``resetPassword`` 端点主机名\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:183\nmsgid \"\"\n\"`server-1129 <https://projects.tigase.net/issue/server-1129>`__: BOSH \"\n\"timeouts on GET requests\"\nmsgstr \"\"\n\"`server-1129 <https://projects.tigase.net/issue/server-1129>`__: GET 请求的 \"\n\"BOSH 超时\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:185\nmsgid \"\"\n\"`prv-436 <https://projects.tigase.net/issue/prv-436>`__: Conversations \"\n\"compliance - contact developers\"\nmsgstr \"`prv-436 <https://projects.tigase.net/issue/prv-436>`__: 对话的合规性 - 联系开发人员\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:187\nmsgid \"\"\n\"`server-1100 <https://projects.tigase.net/issue/server-1100>`__: CAAS and\"\n\" WS testers fail to connect to wss://tigase.im:5291\"\nmsgstr \"\"\n\"`server-1100 <https://projects.tigase.net/issue/server-1100>`__:CAAS 和 WS\"\n\" 测试人员无法连接到 wss://tigase.im:5291\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:189\nmsgid \"\"\n\"`server-1047 <https://projects.tigase.net/issue/server-1047>`__: Add \"\n\"SASL-EXTERNAL on s2s conections\"\nmsgstr \"\"\n\"`server-1047 <https://projects.tigase.net/issue/server-1047>`__:在 s2s \"\n\"连接上添加 SASL-EXTERNAL\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:191\nmsgid \"\"\n\"`server-1103 <https://projects.tigase.net/issue/server-1103>`__: High \"\n\"priority PUSH notifications are sent for all messages\"\nmsgstr \"\"\n\"`server-1103 \"\n\"<https://projects.tigase.net/issue/server-1103>`__:为所有消息发送高优先级 PUSH 通知\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:193\nmsgid \"\"\n\"`pubsub-93 <https://projects.tigase.net/issue/pubsub-93>`__: NPE in \"\n\"CapsChangeEvent\"\nmsgstr \"\"\n\"`pubsub-93 \"\n\"<https://projects.tigase.net/issue/pubsub-93>`__:CapsChangeEvent 中的 NPE\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:195\nmsgid \"\"\n\"`server-1137 <https://projects.tigase.net/issue/server-1137>`__: Don’t \"\n\"require setting JAVA_HOME to start server\"\nmsgstr \"\"\n\"`server-1137 <https://projects.tigase.net/issue/server-1137>`__:不需要设置 \"\n\"JAVA_HOME 来启动服务器\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:197\nmsgid \"\"\n\"`server-1136 <https://projects.tigase.net/issue/server-1136>`__: upgrade-\"\n\"schema --help not available\"\nmsgstr \"\"\n\"`server-1136 <https://projects.tigase.net/issue/server-1136>`__:升级模式 \"\n\"--help 不可用\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:199\nmsgid \"\"\n\"`utils-19 <https://projects.tigase.net/issue/utils-19>`__: tigase-utils \"\n\"doesn’t compile with JDK12\"\nmsgstr \"\"\n\"`utils-19 <https://projects.tigase.net/issue/utils-19>`__:tigase-utils \"\n\"不能与 JDK12 一起编译\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:201\nmsgid \"\"\n\"`server-1138 <https://projects.tigase.net/issue/server-1138>`__: Schema \"\n\"files are not sorted correctly during loading\"\nmsgstr \"\"\n\"`server-1138 <https://projects.tigase.net/issue/server-1138>`__: \"\n\"加载过程中模式文件未被正确排序\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:203\nmsgid \"\"\n\"`pubsub-98 <https://projects.tigase.net/issue/pubsub-98>`__: Resources \"\n\"with emoji chars are causing issues with MySQL backend\"\nmsgstr \"\"\n\"`pubsub-98 <https://projects.tigase.net/issue/pubsub-98>`__: \"\n\"带有表情符号字符的资源导致 MySQL 后端出现问题\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:205\nmsgid \"\"\n\"`server-1110 <https://projects.tigase.net/issue/server-1110>`__: \"\n\"Disabling TLS in VHost configuration doesn’t work\"\nmsgstr \"\"\n\"`server-1110 <https://projects.tigase.net/issue/server-1110>`__:在 VHost \"\n\"配置中禁用 TLS 不起作用\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:207\nmsgid \"\"\n\"`server-1078 <https://projects.tigase.net/issue/server-1078>`__: Don’t \"\n\"send root CA certificate in chain\"\nmsgstr \"\"\n\"`server-1078 <https://projects.tigase.net/issue/server-1078>`__: 不要在链中发送根\"\n\" CA 证书\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:209\nmsgid \"\"\n\"`server-1113 <https://projects.tigase.net/issue/server-1113>`__: Don’t \"\n\"advertise SASL-EXTERNAL if own certificate is not valid\"\nmsgstr \"\"\n\"`server-1113 <https://projects.tigase.net/issue/server-1113>`__: \"\n\"如果自己的证书无效，请勿宣传 SASL-EXTERNAL\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:211\nmsgid \"\"\n\"`http-78 <https://projects.tigase.net/issue/http-78>`__: Simplify \"\n\"installer\"\nmsgstr \"`http-78 <https://projects.tigase.net/issue/http-78>`__:简化安装程序\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:213\nmsgid \"\"\n\"`server-1133 <https://projects.tigase.net/issue/server-1133>`__: Not able\"\n\" to connect via S2S to server with incorrect SSL certificate\"\nmsgstr \"\"\n\"`server-1133 <https://projects.tigase.net/issue/server-1133>`__: 无法通过 S2S\"\n\" 连接到 不正确的SSL服务器\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:215\nmsgid \"\"\n\"`serverdistribution-2 \"\n\"<https://projects.tigase.net/issue/serverdistribution-2>`__: MUC upgrade \"\n\"not linked correctly in global tigase guide\"\nmsgstr \"\"\n\"`serverdistribution-2 \"\n\"<https://projects.tigase.net/issue/serverdistribution-2>`__:全球 tigase \"\n\"指南中未正确链接 MUC 升级\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:217\nmsgid \"\"\n\"`server-1149 <https://projects.tigase.net/issue/server-1149>`__: Reduce \"\n\"startup time with a lot of database connections\"\nmsgstr \"\"\n\"`server-1149 \"\n\"<https://projects.tigase.net/issue/server-1149>`__:通过大量数据库连接减少启动时间\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:219\nmsgid \"\"\n\"`server-1148 <https://projects.tigase.net/issue/server-1148>`__: \\\"ERROR!\"\n\" Component <x> schema version is not loaded in the database or it is \"\n\"old!\\\" during shutdown\"\nmsgstr \"\"\n\"`server-1148 <https://projects.tigase.net/issue/server-1148>`__: 在关机期间 \"\n\"“错误！组件 <x> 架构版本未被加载到数据库中，或者它是旧版本！”\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:221\nmsgid \"\"\n\"`server-1153 <https://projects.tigase.net/issue/server-1153>`__: Refactor\"\n\" Credentials related ``username`` to ``credentialId`` to avoid confussion\"\nmsgstr \"\"\n\"`server-1153 <https://projects.tigase.net/issue/server-1153>`__: 将与 \"\n\"``username`` 相关的凭据重构为 ``credentialId`` 以避免混淆\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:223\nmsgid \"\"\n\"`servers-312 <https://projects.tigase.net/issue/servers-312>`__: No \"\n\"cluster connection to send a packet\"\nmsgstr \"\"\n\"`servers-312 <https://projects.tigase.net/issue/servers-312>`__: \"\n\"没有集群连接发送数据包\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:225\nmsgid \"\"\n\"`server-1154 <https://projects.tigase.net/issue/server-1154>`__: Multi-\"\n\"thread script execution yields wrong results\"\nmsgstr \"\"\n\"`server-1154 <https://projects.tigase.net/issue/server-1154>`__: \"\n\"多线程脚本执行产生错误结果\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:227\nmsgid \"\"\n\"`servers-294 <https://projects.tigase.net/issue/servers-294>`__: Can’t \"\n\"connect from tigase.im to rsocks.net\"\nmsgstr \"\"\n\"`servers-294 <https://projects.tigase.net/issue/servers-294>`__:无法从 \"\n\"tigase.im 连接到 rsocks.net\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:229\nmsgid \"\"\n\"`server-1111 <https://projects.tigase.net/issue/server-1111>`__: Can’t \"\n\"establish s2s to upload.pouet.ovh\"\nmsgstr \"\"\n\"`server-1111 <https://projects.tigase.net/issue/server-1111>`__:无法建立 s2s \"\n\"到 upload.pouet.ovh\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:231\nmsgid \"\"\n\"`server-1143 <https://projects.tigase.net/issue/server-1143>`__: S2S \"\n\"connectivity issue with OpenFire when SASL external is used\"\nmsgstr \"\"\n\"`server-1143 <https://projects.tigase.net/issue/server-1143>`__: SASL \"\n\"外部被使用时和OpenFire 的 S2S 连接问题\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:233\nmsgid \"\"\n\"`servers-309 <https://projects.tigase.net/issue/servers-309>`__: Issue \"\n\"when connecting to xabber.org: not-authorized: self signed certificate\"\nmsgstr \"\"\n\"`servers-309 <https://projects.tigase.net/issue/servers-309>`__: 连接到 \"\n\"xabber.org 时出现问题：未授权：自签名证书\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:235\nmsgid \"\"\n\"`tigaseim-80 <https://projects.tigase.net/issue/tigaseim-80>`__: Siskin \"\n\"IM push server is not accessible\"\nmsgstr \"\"\n\"`tigaseim-80 <https://projects.tigase.net/issue/tigaseim-80>`__: Siskin \"\n\"IM 推送服务器无法访问\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:237\nmsgid \"\"\n\"`server-1080 <https://projects.tigase.net/issue/server-1080>`__: After \"\n\"updating certificate via ad-hoc/rest only main certificate is updated\"\nmsgstr \"\"\n\"`server-1080 <https://projects.tigase.net/issue/server-1080>`__: 通过 ad-\"\n\"hoc/rest 更新证书后，仅更新主证书\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:239\nmsgid \"\"\n\"`http-88 <https://projects.tigase.net/issue/http-88>`__: Improve REST \"\n\"documentation\"\nmsgstr \"`http-88 <https://projects.tigase.net/issue/http-88>`__:改进 REST 文档\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:241\nmsgid \"\"\n\"`http-87 <https://projects.tigase.net/issue/http-87>`__: \\\"request accept\"\n\" time exceeded\\\" for every request when using \"\n\"``JavaStandaloneHttpServer``\"\nmsgstr \"\"\n\"`http-87 <https://projects.tigase.net/issue/http-87>`__: \"\n\"对每个请求使用``JavaStandaloneHttpServer``时的“超过请求接受时间”\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:243\nmsgid \"\"\n\"`server-1151 <https://projects.tigase.net/issue/server-1151>`__: \"\n\"BruteForceLockerExtension (and possibly others) settings are not \"\n\"correctly retrieved\"\nmsgstr \"\"\n\"`server-1151 <https://projects.tigase.net/issue/server-1151>`__: \"\n\"BruteForceLockerExtension（可能还有其他）设置未正确检索\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:245\nmsgid \"\"\n\"`http-89 <https://projects.tigase.net/issue/http-89>`__: Drop \"\n\"result/error packages received by HTTP-API if no connection present to \"\n\"write response to\"\nmsgstr \"\"\n\"`http-89 \"\n\"<https://projects.tigase.net/issue/http-89>`__:如果要写入响应的连接不存在，则可丢弃 HTTP-\"\n\"API 收到的结果/错误包\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:247\nmsgid \"\"\n\"`pubsub-99 <https://projects.tigase.net/issue/pubsub-99>`__: \"\n\"Notifications are not sent for +notify from nodes with whitelist access \"\n\"mode\"\nmsgstr \"\"\n\"`pubsub-99 \"\n\"<https://projects.tigase.net/issue/pubsub-99>`__:不会从具有白名单访问模式的节点发送 \"\n\"+notify 通知\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:251\nmsgid \"\"\n\"`server-1157 <https://projects.tigase.net/issue/server-1157>`__: SCRAM-\"\n\"SHA512 not working\"\nmsgstr \"\"\n\"`server-1157 <https://projects.tigase.net/issue/server-1157>`__: SCRAM-\"\n\"SHA512 不工作\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:253\nmsgid \"\"\n\"`server-1159 <https://projects.tigase.net/issue/server-1159>`__: Improve \"\n\"handling establishing and terminating of the session\"\nmsgstr \"\"\n\"`server-1159 \"\n\"<https://projects.tigase.net/issue/server-1159>`__:改进会话的建立和终止的处理\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:255\nmsgid \"\"\n\"`server-1152 <https://projects.tigase.net/issue/server-1152>`__: Cleanup \"\n\"warnings from JDBCMsgRepository\"\nmsgstr \"\"\n\"`server-1152 <https://projects.tigase.net/issue/server-1152>`__: 来自 \"\n\"JDBCMsgRepository 的清理警告\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:257\nmsgid \"\"\n\"`server-1112 <https://projects.tigase.net/issue/server-1112>`__: Fallback\"\n\" to diallback if SASL-EXTERNAL fails\"\nmsgstr \"\"\n\"`server-1112 <https://projects.tigase.net/issue/server-1112>`__:如果 SASL-\"\n\"EXTERNAL 失败，则回退到回拨\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:259\nmsgid \"\"\n\"`servers-292 <https://projects.tigase.net/issue/servers-292>`__: S2S \"\n\"connectivity issues\"\nmsgstr \"`servers-292 <https://projects.tigase.net/issue/servers-292>`__: S2S 的连接问题\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:261\nmsgid \"\"\n\"`acspubsub-19 <https://projects.tigase.net/issue/acspubsub-19>`__: REST \"\n\"execution fails on other nodes\"\nmsgstr \"\"\n\"`acspubsub-19 <https://projects.tigase.net/issue/acspubsub-19>`__: REST \"\n\"执行在其他节点上失败\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:263\nmsgid \"\"\n\"`server-1145 <https://projects.tigase.net/issue/server-1145>`__: Race \"\n\"condition during storing/loading of offline messages\"\nmsgstr \"\"\n\"`server-1145 <https://projects.tigase.net/issue/server-1145>`__: \"\n\"存储/加载离线消息期间的竞争条件\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:265\nmsgid \"\"\n\"`http-90 <https://projects.tigase.net/issue/http-90>`__: Add direct links\"\n\" to most useful task in AdminUI main page\"\nmsgstr \"\"\n\"`http-90 <https://projects.tigase.net/issue/http-90>`__: 在 AdminUI \"\n\"主页中添加直接链接并指向最有用任务。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:267\nmsgid \"\"\n\"`spam-10 <https://projects.tigase.net/issue/spam-10>`__: Add \"\n\"documentation for creation of a custom filter\"\nmsgstr \"`spam-10 <https://projects.tigase.net/issue/spam-10>`__:添加用于创建自定义过滤器的文档\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:269\nmsgid \"\"\n\"`server-1163 <https://projects.tigase.net/issue/server-1163>`__: Review \"\n\"and update ``SASL Custom Mechanisms and Configuration`` documentation\"\nmsgstr \"\"\n\"`server-1163 <https://projects.tigase.net/issue/server-1163>`__: 审核和更新 \"\n\"``SASL Custom Mechanisms and Configuration`` 文档\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:271\nmsgid \"\"\n\"`server-1164 <https://projects.tigase.net/issue/server-1164>`__: After-\"\n\"installation report - installation status\"\nmsgstr \"\"\n\"`server-1164 <https://projects.tigase.net/issue/server-1164>`__: \"\n\"安装后报告-安装状态\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:273\nmsgid \"\"\n\"`systems-76 <https://projects.tigase.net/issue/systems-76>`__: Fix issue \"\n\"with StackOverflow due to recursive call in TLSIO; improve debug log\"\nmsgstr \"\"\n\"`systems-76 <https://projects.tigase.net/issue/systems-76>`__:修复由于 TLSIO \"\n\"中递归调用导致 StackOverflow 的问题；改进调试日志\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:275\nmsgid \"\"\n\"`server-1082 <https://projects.tigase.net/issue/server-1082>`__: Sec-\"\n\"WebSocket-Accept not calculated correctly\"\nmsgstr \"\"\n\"`server-1082 <https://projects.tigase.net/issue/server-1082>`__: Sec-\"\n\"WebSocket-Accept 计算不正确\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:277\nmsgid \"\"\n\"`server-1083 <https://projects.tigase.net/issue/server-1083>`__: Messages\"\n\" sent to full jid are returned with error\"\nmsgstr \"\"\n\"`server-1083 <https://projects.tigase.net/issue/server-1083>`__: 发送到完整 \"\n\"jid 的消息返回错误\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:279\nmsgid \"\"\n\"`push-25 <https://projects.tigase.net/issue/push-25>`__: Add support for \"\n\"sending encrypted PUSHes\"\nmsgstr \"`push-25 <https://projects.tigase.net/issue/push-25>`__: 添加对加密推送的支持\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:281\nmsgid \"\"\n\"`server-1085 <https://projects.tigase.net/issue/server-1085>`__: Improve \"\n\"retrieval of values for all keys in a node in UserRepository\"\nmsgstr \"\"\n\"`server-1085 <https://projects.tigase.net/issue/server-1085>`__: 改进对 \"\n\"UserRepository 节点中所有键值的检索\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:283\nmsgid \"\"\n\"`muc-115 <https://projects.tigase.net/issue/muc-115>`__: Add support for \"\n\"MUC and offline message delivery\"\nmsgstr \"\"\n\"`muc-115 <https://projects.tigase.net/issue/muc-115>`__: 添加对 MUC \"\n\"和离线消息传递的支持\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:285\nmsgid \"\"\n\"`muc-122 <https://projects.tigase.net/issue/muc-122>`__: XEP-0410: MUC \"\n\"Self-Ping (Schrödinger’s Chat)\"\nmsgstr \"\"\n\"`muc-122 <https://projects.tigase.net/issue/muc-122>`__: XEP-0410: MUC \"\n\"Self-Ping (薛定谔聊天)\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:287\nmsgid \"\"\n\"`muc-112 <https://projects.tigase.net/issue/muc-112>`__: Support for \"\n\"setting vCard avatar for room\"\nmsgstr \"`muc-112 <https://projects.tigase.net/issue/muc-112>`__: 支持为房间设置 vCard 头像\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:289\nmsgid \"\"\n\"`http-83 <https://projects.tigase.net/issue/http-83>`__: Issue with \"\n\"multithreading access to HttpExchange instance\"\nmsgstr \"\"\n\"`http-83 <https://projects.tigase.net/issue/http-83>`__:多线程访问 \"\n\"HttpExchange 实例的问题\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:291\nmsgid \"\"\n\"`httpapijetty-3 <https://projects.tigase.net/issue/httpapijetty-3>`__: \"\n\"Support for HTTP/2\"\nmsgstr \"\"\n\"`httpapijetty-3 <https://projects.tigase.net/issue/httpapijetty-3>`__: 支持\"\n\" HTTP/2\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.1.0.inc:293\nmsgid \"\"\n\"`httpapijetty-6 <https://projects.tigase.net/issue/httpapijetty-6>`__: \"\n\"Update Jetty version\"\nmsgstr \"\"\n\"`httpapijetty-6 <https://projects.tigase.net/issue/httpapijetty-6>`__: \"\n\"更新Jetty 版本\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:2\nmsgid \"Tigase XMPP Server 8.0.0 Change notes and announcement\"\nmsgstr \"Tigase XMPP Server 8.0.0 变更说明和公告\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:8\nmsgid \"Kernel and beans configuration\"\nmsgstr \"内核和 bean 配置\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:10\nmsgid \"\"\n\"Tigase now operates using a Kernel and Beans style of programming. What \"\n\"does this mean for Tigase and You? Good news, really. Tigase XMPP Server \"\n\"is now working as a Kernel program, which will operate on it’s own and \"\n\"handle all the core functionality of the server. Component, and non-\"\n\"essential functionality will now be loaded as Beans. As a user, your \"\n\"experience will not change all that much. However, beans can be loaded \"\n\"and unloaded without having to restart Tigase, meaning that the program \"\n\"will behave more dynamically. This means a smaller footprint on memory on\"\n\" resources when components are not needed, and longer uptimes without \"\n\"having to rest art the program! This also allows for greater flexibility \"\n\"for Tigase XMPP Server to be better customized for unique solutions.\"\nmsgstr \"\"\n\"Tigase 现在使用内核和 Beans 编程风格进行操作。这对 Tigase 和你意味着什么呢？这真的是一个好消息。 Tigase XMPP \"\n\"服务器现在作为一个内核程序运行，它将独立运行并处理服务器的所有核心功能。而组件和非必要功能现在将作为 Bean \"\n\"加载。作为用户，您的体验不会发生太大变化。但是，无需重新启动 Tigase 即可加载和卸载 \"\n\"bean，这意味着程序将更加动态化。这意味着当不需要组件时，无需重新启动程序，内存占用的资源更少，正常运行时间更长！这也为 Tigase XMPP\"\n\" 服务器提供了更大的灵活性，从而可以更好的定制独特的解决方案。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:14\nmsgid \"New Configuration File Format\"\nmsgstr \"新的配置文件格式\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:16\nmsgid \"\"\n\"With the change of Tigase to a Kernel and Beans style of programming, we \"\n\"have also changed how the configuration file is managed. Although you \"\n\"will still edit the ``config.tdsl`` file like a plaintext file, a new \"\n\"style of formatting will be used known as DSL. Domain Specific Language \"\n\"may add more lines, but is a cleaner format, and provides a more secure \"\n\"configuration design since validation of the configuration is done at the\"\n\" domain level. For more information on this format and how to configure \"\n\"Tigase, visit `DSL Configuration Guide <#dslConfig>`__.\"\nmsgstr \"\"\n\"随着 Tigase 对内核和 Beans 编程风格的变化，我们也改变了配置文件的管理方式。尽管您仍将像编辑纯文本文件一样编辑 \"\n\"config.tdsl 文件，但一种称为 DSL \"\n\"的新格式将被使用。域特定语言可能会添加更多行，但格式更简洁，并且提供了更安全的配置设计，因为配置验证是在域级别完成的。有关此格式以及如何配置 \"\n\"Tigase 的更多信息，请访问`DSL 配置指南 <#dslConfig>`__。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:20\nmsgid \"Cluster Node Shutdown Changes\"\nmsgstr \"集群节点关闭更改\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:22\nmsgid \"\"\n\"Starting with Tigase XMPP Server 8.0.0, users connected on clustered \"\n\"nodes will be able use a ``see-other-host`` strategy when a node is being\"\n\" shutdown. **Note: This may not be compatible with all clients.** The Ad-\"\n\"hoc command is designed for a graceful shutdown of cluster nodes as a \"\n\"groovy script ``Shutdown.groovy``. This script also allows for the \"\n\"-timeout setting which will delay shutdown of the node, and alert all \"\n\"users (via a headline message) that the server will be shutdown after a \"\n\"time. User clients that are compatible with the command will then detect \"\n\"other connected clusters and maintain their connections.\"\nmsgstr \"\"\n\"从 Tigase XMPP Server 8.0.0 开始，连接到集群节点上的用户将能够在节点关闭时使用``see-other-host``。 \"\n\"**注意：这可能与所有客户端都不兼容。** Ad-hoc 命令旨在作为 groovy 脚本 ``Shutdown.groovy``可以 \"\n\"顺利地关闭集群节点。该脚本还允许 -timeout \"\n\"设置延迟节点关闭，并提醒所有用户（通过标题消息）服务器将在一段时间后关闭。与该命令兼容的用户客户端将检测其他连接的集群并保持它们的连接。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:24\nmsgid \"\"\n\"If the command is being sent to shut down the whole cluster, no ``see-\"\n\"other-host`` implementation will be sent, however timeout settings may \"\n\"still be used.\"\nmsgstr \"如果发送命令来关闭整个集群，则``see-other-host``实现不会被发送，但超时设置仍可以被使用。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:26\nmsgid \"\"\n\"The script may be activated by an ad-hoc command, or sent using REST from\"\n\" remote or Tigase Admin UI.\"\nmsgstr \"该脚本可以通过临时命令激活，或者使用 REST 从远程或 Tigase Admin UI 发送。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:31\nmsgid \"Significant cleanup of code and repositories\"\nmsgstr \"大量清理代码和存储库\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:33\nmsgid \"\"\n\"Multiple changes have been made to the structure and coding for v8, many \"\n\"related to trimming size of repositories and old calls. Some of these \"\n\"improvements are listed here:\"\nmsgstr \"对 v8 的结构和编码进行了多项更改，其中许多改变都与存储库的大小和旧调用有关。此处列出了其中一些改进：\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:35\nmsgid \"Empty JavaDocs that do not convey values have been removed.\"\nmsgstr \"不传递值的空 JavaDocs 已被删除。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:37\nmsgid \"\"\n\"All code is reformatted to be compliant with out `codestyle guidelines \"\n\"<#tigaseCodeStyle>`__.\"\nmsgstr \"所有代码都被重新格式化以符合`codestyle指南 <#tigaseCodeStyle>`__。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:39\nmsgid \"\"\n\"Calls to ``System.out.print*()`` and ``printStackTrace()`` have been \"\n\"removed from code.\"\nmsgstr \"对 ``System.out.print*()`` 和 ``printStackTrace()`` 的调用已从代码中删除。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:41\nmsgid \"Depreciated and unused classes have been removed.\"\nmsgstr \"删除已弃用和未使用的类。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:46\nmsgid \"BouncyCastle being used for StartTLS\"\nmsgstr \"用于 StartTLS 的 BouncyCastle\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:48\nmsgid \"\"\n\"`BouncyCastle <https://www.bouncycastle.org/java.html>`__ Crypto API has \"\n\"now been employed to handle StartTLS negotiation. By doing this, Tigase \"\n\"now supports ``tls-unique`` within the SCRAM PLUS authentication \"\n\"implementation. This API is may be employed by calling the class in your \"\n\"configuration file:\"\nmsgstr \"\"\n\"`BouncyCastle <https://www.bouncycastle.org/java.html>`__ Crypto API \"\n\"现在已被用于处理 StartTLS 协商。通过这样做，Tigase 现在在 SCRAM PLUS 身份验证实现中支持``tls-unique`` \"\n\"。可以通过调用配置文件中的类来使用此 API：\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:56\nmsgid \"The BouncyCastle classes are included in the dist-max archives.\"\nmsgstr \"BouncyCastle 类包含在 dist-max 档案中。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:61\nmsgid \"default-virtual-host property changes\"\nmsgstr \"默认虚拟主机属性更改\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:63\nmsgid \"\"\n\"Default virtual hosts property is now able to be configured only as a \"\n\"domain name instead of the list of virtual host domains with options. \"\n\"Additional virtual host domains and their options need to be configured \"\n\"using ad-hoc commands or web AdminUI. Reference `Virtual-Hosts \"\n\"Configuration <#virtHosts>`__ for more details.\"\nmsgstr \"\"\n\"默认虚拟主机属性现在只能配置为域名，而不是带有选项的虚拟主机域列表。需要使用 ad-hoc 命令或 web AdminUI \"\n\"配置其他虚拟主机域及其选项。参考 `Virtual-Hosts 配置 <#virtHosts>`__ 了解更多详情。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:68\nmsgid \"All artifacts are signed\"\nmsgstr \"所有生成物均已签名\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:70\nmsgid \"\"\n\"Since work began on v8.0.0 Tigase has required that all changes to Tigase\"\n\" XMPP Server and dependencies be signed with known certificates. This \"\n\"version marks the first to be totally signed.\"\nmsgstr \"\"\n\"自从 v8.0.0 开始工作以来，Tigase 要求对 Tigase XMPP \"\n\"服务器和依赖项的所有更改都使用已知证书进行签名。这个版本标志着第一个完全签名的版本。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:75\nmsgid \"Scaled Down Installation Methods\"\nmsgstr \"按比例缩小的安装方法\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:77\nmsgid \"\"\n\"We have cleaned up installation methods for Tigase and now recommend the \"\n\"use of web-installer method. IzPack installer (files ``tigase-\"\n\"server-<version>-b<build>.jar`` installation methods have been removed \"\n\"and will no longer be produced for v8.0.0 and later. Manual installation \"\n\"is still available for those unable to use HTTP or browser access. Visit \"\n\"our `Quick Start <#quickstart>`__ guide for instructions on these other \"\n\"methods.\"\nmsgstr \"\"\n\"我们已经整理了 Tigase 的安装方法，现在推荐使用 web-installer 方法。 IzPack 安装程序（文件 ``tigase-\"\n\"server-<version>-b<build>.jar`` 安装方法已被删除)将不再为 v8.0.0 及更高版本生成。无法使用HTTP \"\n\"或浏览器访问的人仍然可以手动安装。访问我们的`快速入门 <#quickstart>`__ 指南以获取有关其他方法的说明。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:82\nmsgid \"Emojis now supported on Tigase XMPP Servers\"\nmsgstr \"Tigase XMPP 服务器现在支持表情符号\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:84\nmsgid \"\"\n\"Emojis are now supported on MySQL databases, however some settings may be\"\n\" need to be changed, although they won’t affect existing databases. \"\n\"`Visit this section <#emojisupportSQL>`__ for details.\"\nmsgstr \"\"\n\"MySQL 数据库现在支持表情符号，但可能需要更改某些设置，尽管它们不会影响现有数据库。 `访问此部分 \"\n\"<#emojisupportSQL>`__了解详细信息。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:89\nmsgid \"XEP-0215 External Service Discovery now supported\"\nmsgstr \"现在支持 XEP-0215 外部服务发现\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:91\nmsgid \"\"\n\"Tigase now supports `XEP-0215 - External Service Discovery \"\n\"<https://xmpp.org/extensions/xep-0215.html>`__ allowing Tigase to \"\n\"discover services that are not available VIA the XMPP Protocol. For setup\"\n\" and configuration information visit `External Service Discovery \"\n\"Component <#_tigase_external_service_discovery>`__ documentation.\"\nmsgstr \"\"\n\"Tigase 现在支持`XEP-0215 - External Service Discovery \"\n\"<https://xmpp.org/extensions/xep-0215.html>`__ 并允许 Tigase 发现通过 XMPP \"\n\"协议不可用的服务。有关设置和配置信息，请访问`外部服务发现组件 <#_tigase_external_service_discovery>`__ \"\n\"文档。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:96\nmsgid \"XEP-0313 Message Archive Management now supported\"\nmsgstr \"现在支持 XEP-0313 消息存档管理\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:98\nmsgid \"\"\n\"`XEP-0313 - Message Archive Management \"\n\"<https://xmpp.org/extensions/xep-0313.html>`__ is now supported by Tigase\"\n\" featuring custom enhancements like full-text search and searching by \"\n\"tags. MAM requires Tigase’s message archive to be enabled in the \"\n\"``config.tdsl`` file, and the schema (XEP-0136 or XEP-0313) must be \"\n\"configured in session manager settings. To turn on MAM, see configuration\"\n\" guide `located here <#_support_for_mam>`__.\"\nmsgstr \"\"\n\"`XEP-0313 - 消息存档管理 <https://xmpp.org/extensions/xep-0313.html>`__ 现在由 \"\n\"Tigase 支持，具有自定义增强功能，比如全文搜索和按标签搜索。 MAM 要求在``config.tdsl``文件中启用 Tigase \"\n\"的消息存档，并且必须在会话管理器设置中配置模式（XEP-0136 或 XEP-0313）。要打开 MAM，请参阅`位于此处 \"\n\"<#_support_for_mam>`__的配置指南。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:103\nmsgid \"XEP-0363 HTTP File Upload now supported\"\nmsgstr \"XEP-0363 现在支持 HTTP 文件上传\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:105\nmsgid \"\"\n\"`XEP-0363 - HTTP File Upload \"\n\"<https://xmpp.org/extensions/xep-0363.html>`__ is now supported using \"\n\"Tigase HTTP API component now allowing for a more robust one-to-many file\"\n\" uploading option. Configuration details are available at the `HTTP File \"\n\"Upload Component <#XEP0363>`__ section of documentation.\"\nmsgstr \"\"\n\"`XEP-0363 - HTTP 文件上传 <https://xmpp.org/extensions/xep-0363.html>`__ \"\n\"现在支持使用 Tigase HTTP API 组件，现在允许更强大的一对多文件上传选项。配置详细信息可在文档的`HTTP 文件上传组件 \"\n\"<#XEP0363>`__ 部分获得。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:110\nmsgid \"Startup now uses bootstrapping\"\nmsgstr \"启动现在使用自助法\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:112\nmsgid \"\"\n\"Tigase now uses bootstrapping to startup, which will load configuration \"\n\"from ``config.tdsl`` file like before. Then Tigase will begin it’s normal\"\n\" operations with the configuration options. All startup functions for \"\n\"Tigase will now run under the ``bootstrap`` bean.\"\nmsgstr \"\"\n\"Tigase 现在使用自助法启动，这将像以前一样从``config.tdsl``文件加载配置。然后 Tigase 将使用配置选项开始正常操作。 \"\n\"Tigase 的所有启动功能现在都将在 ``bootstrap`` bean 下运行。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:117\nmsgid \"CAPTCHA system now available for in-band registration\"\nmsgstr \"CAPTCHA 系统现在可用于带内注册\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:119\nmsgid \"\"\n\"`XEP-0077 In band registration \"\n\"<https://xmpp.org/extensions/xep-0077.html>`__ can use Data Forms as an \"\n\"option to process new registrations. Now you can secure these \"\n\"registrations by employing a CAPTCHA solution. By enabling this option \"\n\"you can reduce the number of potential spammers and bots on your server.\"\nmsgstr \"\"\n\"`XEP-0077 带内注册 <https://xmpp.org/extensions/xep-0077.html>`__ \"\n\"可以选择使用数据表单来处理新注册。现在，您可以通过使用 CAPTCHA \"\n\"解决方案来保护这些注册。通过启用此选项，您可以减少服务器上潜在的垃圾邮件发送者和机器人的数量。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:124\nmsgid \"Schema changes\"\nmsgstr \"架构更改\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:126\nmsgid \"\"\n\"Now each component has it’s own schema for databases, they are no longer \"\n\"tied into Tigase XMPP server versions making changes and updates to \"\n\"individual components easier, and may not disrupt all users not using \"\n\"certain components. See the `schema update section <#schemaChangev800>`__\"\n\" for more details.\"\nmsgstr \"\"\n\"现在每个组件都有自己的数据库架构，它们不再和 Tigase XMPP \"\n\"服务器版本绑定，从而更容易更改和更新单个组件，并且可能不会扰乱不使用某些组件的所有用户。有关更多详细信息，请参阅`架构更新部分 \"\n\"<#schemaChangev800>`__。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:131\nmsgid \"Shrinkable Statistics History\"\nmsgstr \"可收缩统计历史\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:133\n#, python-format\nmsgid \"\"\n\"Statistics history can now be automatically made smaller if a systems \"\n\"memory resources are above a certain amount. By default this is enabled \"\n\"and will trigger when over 95% of memory is in use. Half of all existing \"\n\"entries will be removed at this time. The same pattern will continue to \"\n\"halve the available records every time the threshold is met. A hard-set \"\n\"minimum of 5 entries is set, so you will always have the last 5 entries. \"\n\"This setting may be adjusted by adding the following setting to your \"\n\"``config.tdsl`` file and adjusting the integer value:\"\nmsgstr \"\"\n\"现在如果系统内存资源超过一定数量，可以自动缩小统计历史记录。默认情况下，这是启用的，并且会在超过 95% \"\n\"的内存使用时触发。此时将删除所有现有条目的一半。每次达到阈值时，相同的模式将继续把可用记录减半。因为设置了最少 5 \"\n\"个条目的硬设置，所以您将始终拥有最后 5 个条目。此设置可以通过将以下设置添加到您的 config.tdsl 文件并调整整数值来加以调整：\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:144\nmsgid \"Statistics now available for all modules\"\nmsgstr \"统计信息现在可用于所有模块\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:146\nmsgid \"For any bean, you may enable statistics by using the following\"\nmsgstr \"对于任何 bean，您可以使用以下命令启用统计信息\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:157\nmsgid \"Spam Protection\"\nmsgstr \"垃圾邮件防护\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:159\nmsgid \"\"\n\"Tigase XMPP Server v8.0.0 now includes some efforts to prevent spam bot \"\n\"accounts from running on servers.\"\nmsgstr \"Tigase XMPP Server v8.0.0 现在包括一些措施可以防止垃圾邮件机器人帐户在服务器上运行。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:162\nmsgid \"Account Registration Limits Expanded\"\nmsgstr \"扩大了帐户注册限制\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:164\nmsgid \"\"\n\"Account registration limits have been expanded and now you can set \"\n\"separate counters, or configure components individually for their own \"\n\"limits. Visit `this section <#accountRegLimit>`__ for configuration \"\n\"details.\"\nmsgstr \"\"\n\"已扩展了帐户注册限制，现在您可以设置单独的计数器，或单独配置组件以实现自己的限制。访问 `this section \"\n\"<#accountRegLimit>`__ 了解配置详情。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:167\nmsgid \"\"\n\"Accounts created using in-band registration now will use confirmation \"\n\"E-mail\"\nmsgstr \"使用带内注册创建的帐户现在将使用电子邮件来确认\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:169\nmsgid \"\"\n\"In an effort to create a more secure method for implementing \"\n\"``JabberIqRegister`` Tigase XMPP Server will now require the use of a \"\n\"confirmation E-mail by default in the process. The E-mail must be valid, \"\n\"and accounts will be made into pending status until a user clicks the \"\n\"generated URI in the E-mail and activates the account. This is a plugin \"\n\"and must be enabled in the ``config.tdsl`` file by using the following \"\n\"code:\"\nmsgstr \"\"\n\"为了创建一种更安全的方法来实现``JabberIqRegister``，Tigase XMPP 服务器现在需要在此过程中默认使用电子邮件来确认。 \"\n\"E-mail 必须是有效的，直到用户点击 E-mail 中生成的 URI 并激活帐户后，帐户才会进入待处理状态。这是一个插件，必须通过以下代码在 \"\n\"config.tdsl 文件中启用：\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:176\nmsgid \"Further Spam prevention\"\nmsgstr \"进一步防止垃圾邮件\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:178\nmsgid \"\"\n\"Tigase-spam component is now in ``dist-max`` distribution package, and \"\n\"has a number of features described here `in this section \"\n\"<#tigase_spam_filter>`__.\"\nmsgstr \"\"\n\"Tigase-spam 组件现在位于 ``dist-max`` 分发包中，并且具有此处描述的许多功能`in this section \"\n\"<#tigase_spam_filter>`__。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:183\nmsgid \"Changes in password storage\"\nmsgstr \"密码存储的变化\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:185\nmsgid \"\"\n\"Before version 8.0.0, user passwords were stored in plaintext in the \"\n\"``user_pw`` database field within ``tig_users`` table, but in plaintext. \"\n\"It was possible to enable storage of the MD5 hash of the password \"\n\"instead, however this limited authentication mechanism SASL PLAIN only. \"\n\"However an MD5 hash of a password is not really a secure method as it is \"\n\"possible to revert this mechanism using rainbow tables.\"\nmsgstr \"\"\n\"在 8.0.0 版之前，用户密码以明文形式存储在 ``tig_users`` 表中的 ``user_pw`` 数据库字段中。可以启用存储密码的 \"\n\"MD5 哈希值，但是这种有限的身份验证机制仅限 SASL PLAIN。然而，密码的 \"\n\"MD5哈希并不是真正安全的方法，因为可以使用彩虹表恢复这种机制。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:187\nmsgid \"\"\n\"Therefore, we decided to change this and store only encrypted versions of\"\n\" a password in ``PBKDF2`` form which can be easily used for ``SCRAM-\"\n\"SHA-1`` authentication mechanism or ``SCRAM-SHA-256``. ``SASL PLAIN`` \"\n\"mechanism can also use these encrypted passwords.\"\nmsgstr \"\"\n\"因此，我们决定改变这一点，仅以 ``PBKDF2`` 形式存储密码的加密版本，这可以很容易地用于``SCRAM-SHA-1`` 身份验证机制或\"\n\"``SCRAM-SHA-256``。 ``SASL PLAIN`` 机制也可以使用这些加密密码。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:189\nmsgid \"\"\n\"The storage of encrypted passwords is now enabled **by default** in \"\n\"v8.0.0 of Tigase.\"\nmsgstr \"现在在 Tigase v8.0.0 中**默认情况下**启用了加密密码的存储。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:194\nmsgid \"Dynamic TLS Buffer\"\nmsgstr \"动态 TLS 缓冲区\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:196\nmsgid \"\"\n\"Memory Buffer for TLS no longer remains at highest buffer size needed for\"\n\" the server session. Buffer will now free memory during idle connections.\"\n\" Thus drastically improving program footprint.\"\nmsgstr \"TLS 的内存缓冲区不再保持服务器会话所需的最高缓冲区大小。缓冲区现在将在空闲连接期间释放内存。从而大大改善了程序占用空间。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:201\nmsgid \"XEP-305 Quickstart now supported\"\nmsgstr \"现在支持 XEP-305 快速入门\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:203\nmsgid \"\"\n\"It’s now possible to establish connection faster due to implementation of\"\n\" `XEP-0305: XMPP Quickstart \"\n\"<https://xmpp.org/extensions/xep-0305.html>`__ (`#1936 \"\n\"<https://projects.tigase.net/issues?q=Redmine%20ID:%201936>`__). Feature \"\n\"is only available for ``c2s`` Connection Manager (i.e. connections on \"\n\"port 5222) and needs to be enabled in ``config.tdsl``\"\nmsgstr \"\"\n\"由于实施了`XEP-0305: XMPP Quickstart \"\n\"<https://xmpp.org/extensions/xep-0305.html>`__ (`#1936 \"\n\"<https://projects.tigase. \"\n\"net/issues?q=Redmine%20ID:%201936>`__)，建立连接会变快。该功能仅适用于``c2s`` 连接管理器（即端口 \"\n\"5222 上的连接），并需要在``c2s`` 中启用\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:214\nmsgid \"Database Timestamps\"\nmsgstr \"数据库时间戳\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:216\nmsgid \"Timestamps in database will be stored using UTC time.\"\nmsgstr \"数据库中的时间戳将使用 UTC 时间存储。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:221\nmsgid \"Config-type properties have changed\"\nmsgstr \"配置类型属性已更改\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:223\nmsgid \"\"\n\"Config-type is now configured using DSL format. Visit `this section \"\n\"<#configType>`__ for more information. The names of different config-type\"\n\" properties have changed: ``default`` replaces ``--gen-config-def``, \"\n\"``--gen=config-all``, and ``--gen-config-default`` configuration types. \"\n\"``session-manager`` replaces ``--gen-config-sm``. ``connection-managers``\"\n\" replaces ``--gen-config-cs``. ``component`` replaces ``--gen-config-\"\n\"comp``. ``setup`` - is a new type of config created for initial \"\n\"configuration of Tigase XMPP Server.\"\nmsgstr \"\"\n\"Config-type 现在使用 DSL 格式进行配置。访问 `this section <#configType>`__ \"\n\"了解更多信息。不同配置类型属性的名称已更改：``default`` 替换 ``--gen-config-def``、``--gen=config-\"\n\"all`` 和 ``--gen-config-default``配置类型。 ``session-manager`` 替换 ``--gen-\"\n\"config-sm``。 ``connection-managers`` 替换 ``--gen-config-cs``。 \"\n\"``component`` 替换 ``--gen-config-comp``。 ``setup`` - 是为 Tigase XMPP \"\n\"服务器的初始配置创建的一种新型配置。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:227\nmsgid \"\"\n\"Old versions are no longer supported, you HAVE to replace old versions \"\n\"with the new ones manually when upgrading to v8.0.0.\"\nmsgstr \"不再支持旧版本，升级到 v8.0.0 时必须手动将旧版本替换为新版本。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:232\nmsgid \"Database Watchdog implemented\"\nmsgstr \"数据库监视器实现\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:234\nmsgid \"\"\n\"It is now possible to set connection testing to databases when \"\n\"connections are idle and customize the frequency with which this is done.\"\n\" Visit `this section <#databaseWatchdog>`__ for more details.\"\nmsgstr \"\"\n\"现在在连接空闲时可以设置对数据库的连接测试，并自定义完成此操作的频率。访问 `this section \"\n\"<#databaseWatchdog>`__ 了解更多详情。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:239\nmsgid \"Packet statistics expanded\"\nmsgstr \"数据包统计扩展\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:241\nmsgid \"\"\n\"Packet statistics both retrieved VIA XMPP and during graceful shutdown \"\n\"have now been separated to a per-XMLNS basis. This may be disabled by \"\n\"adding the following line to ``config.tdsl`` file:\"\nmsgstr \"\"\n\"通过 XMPP 和正常关闭期间检索到的数据包统计信息现在已被分散到每个 XMLNS 的基础上。这些可以通过在``config.tdsl`` \"\n\"文件中添加以下行来禁用：\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:250\nmsgid \"XEP-0016 Behavior changes\"\nmsgstr \"XEP-0016 行为变化\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:252\nmsgid \"\"\n\"XEP states that Privacy lists should be used when no user session exists \"\n\"in addition to when there is. Previously, Tigase would only filter \"\n\"results when retrieving messages, allowing blocked users to store offline\"\n\" messages. This has now been changed to reflect the XEP properly, and \"\n\"messages will be filtered while there is no user session. If however, you\"\n\" wish to use the previous version, where offline messages are cached \"\n\"first and then filtered, you may use the following configuration:\"\nmsgstr \"\"\n\"XEP 指出，除了用户会话存在的时候，用户会话不存在时隐私列表也应该可以被使用。之前，Tigase \"\n\"只会在检索消息时过滤结果，允许被阻止的用户存储离线消息。现在为正确反映 \"\n\"XEP已作更改，并且在没有用户会话时消息将被过滤。但是，如果您希望使用以前的版本，在此版本中离线消息先缓存然后过滤，您可以使用以下配置：\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:264\nmsgid \"\"\n\"By default, the cache has a limit of 10000 entries, that may be set by \"\n\"using size bean as seen above.\"\nmsgstr \"默认情况下，缓存有 10000 个条目的限制，可以通过 size bean 设置，如上所示。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:269\nmsgid \"Access Control List has new ACL modifiers\"\nmsgstr \"访问控制列表有新的 ACL 修饰符\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:271\nmsgid \"\"\n\"New permissions have been added to ACL including ``DOMAIN_OWNER`` and \"\n\"``DOMAIN_ADMIN`` to reduce permissions checking, and add another level of\"\n\" fine-grained permissions. For more details, please see `Tigase ACL \"\n\"<#accessControlList>`__ configuration for more details.\"\nmsgstr \"\"\n\"ACL 中添加了新的权限，包括 了``DOMAIN_OWNER`` 和 \"\n\"``DOMAIN_ADMIN``以减少权限检查，并添加了另一个级别的细化权限。有关详细信息，请参阅 `Tigase ACL \"\n\"<#accessControlList>`__ 配置以获取更多详细信息。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:276\nmsgid \"Option to ignore schema-version check added\"\nmsgstr \"添加了忽略模式版本检查的选项\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:278\nmsgid \"\"\n\"You can now skip the schema check phase for individual databases. To do \"\n\"this, add the following do the datasource configuration block:\"\nmsgstr \"您现在可以跳过单个数据库的模式检查阶段。为此，请在数据源配置块中添加以下内容：\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:288\nmsgid \"This will do the following:\"\nmsgstr \"这将执行以下操作：\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:290\nmsgid \"Print a warning during repository startup.\"\nmsgstr \"在存储库启动期间打印警告。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:292\nmsgid \"Skip schema upgrades for the source.\"\nmsgstr \"跳过源的架构升级。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:294\nmsgid \"Skip schema destruction for the source.\"\nmsgstr \"跳过源的模式破坏。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:299\nmsgid \"Protection against brute-force attacks\"\nmsgstr \"防止暴力攻击\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:301\nmsgid \"\"\n\"Version 8.0.0 improves security by preventing brute-force attacks. \"\n\"Feature needs to be explicitly enabled and configured (on per VHost \"\n\"basis). Detailed configuration is described in `??? \"\n\"<#bruteForcePrevention>`__ (`#8160 \"\n\"<https://projects.tigase.net/issues?q=Redmine%20ID:%208160>`__)\"\nmsgstr \"\"\n\"8.0.0 版通过防止暴力攻击提高了安全性。需要显式启用和配置功能（基于每个 VHost）。详细配置在`??? \"\n\"<#bruteForcePrevention>`__ (`#8160 \"\n\"<https://projects.tigase.net/issues?q=Redmine%20ID:%208160>`__)\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:306\nmsgid \"\"\n\"`#611 <https://projects.tigase.net/issues?q=Redmine%20ID:%20611>`__ \"\n\"Support for Message of the Day is now enabled in Tigase XMPP Server and \"\n\"can be administered using `XEP-0133 Service Administration \"\n\"<http://xmpp.org/extensions/xep-0133.html#set-motd>`__.\"\nmsgstr \"\"\n\"`#611 <https://projects.tigase.net/issues?q=Redmine%20ID:%20611>`__ 现在在 \"\n\"Tigase XMPP 服务器中启用了对每日消息的支持，并且可以使用 `XEP-0133 服务管理 \"\n\"<http://xmpp.org/extensions/xep-0133.html#set-motd>`__ 进行管理。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:308\nmsgid \"\"\n\"`#1569 <https://projects.tigase.net/issues?q=Redmine%20ID:%201569>`__ Re-\"\n\"implemented XEP-0133 Service Administration Scripts ``4.3 Disable User`` \"\n\"and ``4.4 Re-enable User``.\"\nmsgstr \"\"\n\"`#1569 <https://projects.tigase.net/issues?q=Redmine%20ID:%201569>`__ \"\n\"重新实现 XEP-0133 服务管理脚本 ``4.3 Disable User`` 和``4.4 Re-enable User``。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:310\nmsgid \"\"\n\"`#1449 <https://projects.tigase.net/issues?q=Redmine%20ID:%201449>`__ \"\n\"Monitoring modules now works in OSGi mode.\"\nmsgstr \"\"\n\"`#1449 <https://projects.tigase.net/issues?q=Redmine%20ID:%201449>`__ \"\n\"监控模块现在在 OSGi 模式下工作。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:312\nmsgid \"\"\n\"`#1706 <https://projects.tigase.net/issues?q=Redmine%20ID:%201706>`__ \"\n\"``auto-authorize`` of presence subscriptions can now be set for \"\n\"individual vhosts.\"\nmsgstr \"\"\n\"`#1706 <https://projects.tigase.net/issues?q=Redmine%20ID:%201706>`__ \"\n\"现在可以为单个虚拟主机设置状态订阅``auto-authorize``。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:314\nmsgid \"\"\n\"`#1968 <https://projects.tigase.net/issues?q=Redmine%20ID:%201968>`__ \"\n\"Added a Proxy Wrapper to handle reconnections to database connection pool\"\n\" to help prevent deadlocking threads.\"\nmsgstr \"\"\n\"`#1968 <https://projects.tigase.net/issues?q=Redmine%20ID:%201968>`__ \"\n\"添加了代理包装器来处理与数据库连接池的重新连接，以帮助防止死锁线程。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:316\nmsgid \"\"\n\"`#3511 <https://projects.tigase.net/issues?q=Redmine%20ID:%203511>`__ \"\n\"Mechanism responsible for closing XMPP in SessionManager has been changed\"\n\" to process all packets from TCP connection before closing connection.\"\nmsgstr \"\"\n\"`#3511 <https://projects.tigase.net/issues?q=Redmine%20ID:%203511>`__ \"\n\"SessionManager 中负责关闭 XMPP 的机制已更改为在关闭连接之前处理来自 TCP 连接的所有数据包。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:318\nmsgid \"\"\n\"`#3802 <https://projects.tigase.net/issues?q=Redmine%20ID:%203802>`__ \"\n\"Implementation and API of LocalEventBus and ClusteredEventBus has been \"\n\"unified and is now available as EventBus.\"\nmsgstr \"\"\n\"`#3802 <https://projects.tigase.net/issues?q=Redmine%20ID:%203802>`__ \"\n\"LocalEventBus 和 ClusteredEventBus 的实现已经和 API 统一，现在可以作为 EventBus 使用。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:320\nmsgid \"\"\n\"`#3918 <https://projects.tigase.net/issues?q=Redmine%20ID:%203918>`__ \"\n\"Session Establishment Advertisement is now optional, bringing session \"\n\"establishment in line with `RFC 6121 \"\n\"<https://tools.ietf.org/html/rfc6121>`__.\"\nmsgstr \"\"\n\"`#3918 <https://projects.tigase.net/issues?q=Redmine%20ID:%203918>`__ \"\n\"现在会话建立广告是可选的，使会话建立符合 `RFC 6121 <https://tools.ietf.org/html/rfc6121>`__。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:322\nmsgid \"\"\n\"`#4111 <https://projects.tigase.net/issues?q=Redmine%20ID:%204111>`__ \"\n\"Changed input buffer sizing to use a ratio of 2 to 1 based on input \"\n\"capacity. No longer using a constant value.\"\nmsgstr \"\"\n\"`#4111 <https://projects.tigase.net/issues?q=Redmine%20ID:%204111>`__ \"\n\"根据输入容量更改输入缓冲区大小以使用 2 比 1 的比率。不再使用常量值。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:324\nmsgid \"\"\n\"`#4212 <https://projects.tigase.net/issues?q=Redmine%20ID:%204212>`__ \"\n\"Database schema files have been flattened and made for better \"\n\"organization.\"\nmsgstr \"\"\n\"`#4212 <https://projects.tigase.net/issues?q=Redmine%20ID:%204212>`__ \"\n\"数据库模式文件已被平化并进行了更好的整理。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:326\n#, python-format\nmsgid \"\"\n\"`#4501 <https://projects.tigase.net/issues?q=Redmine%20ID:%204501>`__ \"\n\"``CounterDataFileLogger`` now has an upper limit and will be default be \"\n\"shrunk to 75% if available disk space is 5% or less than 100MB.\"\nmsgstr \"\"\n\"`#4501 <https://projects.tigase.net/issues?q=Redmine%20ID:%204501>`__ \"\n\"``CounterDataFileLogger`` 现在有一个上限，如果可用磁盘空间还剩下5% 或小于 100MB，其默认会缩小到 75%。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:328\nmsgid \"\"\n\"`#4654 <https://projects.tigase.net/issues?q=Redmine%20ID:%204654>`__ \"\n\"PubSub component has been updated and new schema uses UTF-8 encoding when\"\n\" hashing database lookup.\"\nmsgstr \"\"\n\"`#4654 <https://projects.tigase.net/issues?q=Redmine%20ID:%204654>`__ \"\n\"PubSub 组件已被更新，新架构在哈希数据库查找时使用 UTF-8 编码。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:330\nmsgid \"\"\n\"`#4776 <https://projects.tigase.net/issues?q=Redmine%20ID:%204776>`__ \"\n\"Tigase ``DbSchemaLoader`` now prompts for password if one is missing from\"\n\" command line.\"\nmsgstr \"\"\n\"`#4776 <https://projects.tigase.net/issues?q=Redmine%20ID:%204776>`__ 现在 \"\n\"``DbSchemaLoader`` 如果命令行中缺少密码会提示输入密码。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:332\nmsgid \"\"\n\"`#4788 <https://projects.tigase.net/issues?q=Redmine%20ID:%204788>`__ \"\n\"Push component added to dist-max archive.\"\nmsgstr \"\"\n\"`#4788 <https://projects.tigase.net/issues?q=Redmine%20ID:%204788>`__ \"\n\"推送组件添加到 dist-max 存档。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:334\nmsgid \"\"\n\"`#4814 <https://projects.tigase.net/issues?q=Redmine%20ID:%204814>`__ \"\n\"SASL-SCRAM will now be automatically disabled if auth database uses \"\n\"encoded passwords.\"\nmsgstr \"\"\n\"`#4814 <https://projects.tigase.net/issues?q=Redmine%20ID:%204814>`__ 如果 \"\n\"auth 数据库使用编码密码，现在将自动禁用 SASL-SCRAM。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:336\nmsgid \"\"\n\"`#4844 <https://projects.tigase.net/issues?q=Redmine%20ID:%204844>`__ \"\n\"External components can now have SSL socket connections assigned to them.\"\nmsgstr \"\"\n\"`#4844 <https://projects.tigase.net/issues?q=Redmine%20ID:%204844>`__ \"\n\"现在外部组件可以有分配给它们的 SSL 套接字连接。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:338\nmsgid \"\"\n\"`#4859 <https://projects.tigase.net/issues?q=Redmine%20ID:%204859>`__ \"\n\"Tigase ``DbSchemaLoader`` now can support using SSL when connecting to \"\n\"databases.\"\nmsgstr \"\"\n\"`#4859 <https://projects.tigase.net/issues?q=Redmine%20ID:%204859>`__ \"\n\"Tigase ``DbSchemaLoader``现在可以在连接数据库时支持使用 SSL。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:340\nmsgid \"\"\n\"`#4874 <https://projects.tigase.net/issues?q=Redmine%20ID:%204874>`__ \"\n\"Tigase Test Suite has been updated to correspond to all changes for \"\n\"v8.0.0.\"\nmsgstr \"\"\n\"`#4874 <https://projects.tigase.net/issues?q=Redmine%20ID:%204874>`__ \"\n\"Tigase 测试套件已更新相应的 v8.0.0 的所有变化。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:342\nmsgid \"\"\n\"`#4877 <https://projects.tigase.net/issues?q=Redmine%20ID:%204877>`__ In-\"\n\"memory repository implemented for **testing ONLY**.\"\nmsgstr \"\"\n\"`#4877 <https://projects.tigase.net/issues?q=Redmine%20ID:%204877>`__ \"\n\"为**测试用**而实现的内存存储库。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:344\nmsgid \"\"\n\"`#4880 <https://projects.tigase.net/issues?q=Redmine%20ID:%204880>`__ \"\n\"Tigase config-type settings have been reduced and changed. See `this \"\n\"section <#configType>`__ for more details.\"\nmsgstr \"\"\n\"`#4880 <https://projects.tigase.net/issues?q=Redmine%20ID:%204880>`__ \"\n\"Tigase 配置类型设置已减少和更改。有关更多详细信息，请参阅`此节 <#configType>`__。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:346\nmsgid \"\"\n\"`#4908 <https://projects.tigase.net/issues?q=Redmine%20ID:%204908>`__ \"\n\"Limited Ad-hoc execution to admin only within monitor component.\"\nmsgstr \"\"\n\"`#4908 \"\n\"<https://projects.tigase.net/issues?q=Redmine%20ID:%204908>`__仅在监视器组件内限制管理员的临时执行。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:348\nmsgid \"\"\n\"`#5005 <https://projects.tigase.net/issues?q=Redmine%20ID:%205005>`__ \"\n\"Detailed logging configuration is now available in DSL format. See \"\n\"xref:[customLogging] for more details.\"\nmsgstr \"\"\n\"`#5005 <https://projects.tigase.net/issues?q=Redmine%20ID:%205005>`__ \"\n\"详细的日志记录配置现在以 DSL 格式提供。有关详细信息，请参阅外部参照：[customLogging]。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:350\nmsgid \"\"\n\"`#5069 <https://projects.tigase.net/issues?q=Redmine%20ID:%205069>`__ \"\n\"Packet processed statistics now separates results based on XML \"\n\"Namespaces.\"\nmsgstr \"\"\n\"`#5069 <https://projects.tigase.net/issues?q=Redmine%20ID:%205069>`__ \"\n\"数据包处理统计信息现在根据 XML 命名空间划分结果。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:352\nmsgid \"\"\n\"`#5079 <https://projects.tigase.net/issues?q=Redmine%20ID:%205079>`__ \"\n\"Tigase ``DbSchemaLoader`` can now process multiple .sql files in one \"\n\"command by using a comma separated list when calling.\"\nmsgstr \"\"\n\"`#5079 <https://projects.tigase.net/issues?q=Redmine%20ID:%205079>`__ \"\n\"Tigase ``DbSchemaLoader`` 现在可以在调用时在一个命令中通过运用逗号分隔的列来处理多个 .sql 文件。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:354\nmsgid \"\"\n\"`#5086 <https://projects.tigase.net/issues?q=Redmine%20ID:%205086>`__ \"\n\"Tigase server monitor is loaded after delay to prevent NPE during \"\n\"startup.\"\nmsgstr \"\"\n\"`#5086 <https://projects.tigase.net/issues?q=Redmine%20ID:%205086>`__ \"\n\"Tigase 服务监视器在延迟后加载，以防止启动期间出现 NPE。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:356\nmsgid \"\"\n\"`#5149 <https://projects.tigase.net/issues?q=Redmine%20ID:%205149>`__ \"\n\"``StanzaReceiver`` and ``StanzaSender`` Components have been deprecated \"\n\"and are no longer part of Tigase XMPP Server. Related SQL tables \"\n\"``xmpp_stanza`` and ``short_news`` have also been removed from schemas.\"\nmsgstr \"\"\n\"`#5149 <https://projects.tigase.net/issues?q=Redmine%20ID:%205149>`__ \"\n\"``StanzaReceiver`` 和 ``StanzaSender`` 组件已被弃用，其不再是 Tigase XMPP 服务器的一部分。相关的\"\n\" SQL 表 ``xmpp_stanza`` 和 ``short_news`` 也已从模式中删除。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:358\nmsgid \"\"\n\"`#5150 <https://projects.tigase.net/issues?q=Redmine%20ID:%205150>`__ All\"\n\" TigaseDB tables now use the ``tig_`` prefix.\"\nmsgstr \"\"\n\"`#5150 <https://projects.tigase.net/issues?q=Redmine%20ID:%205150>`__ 所有 \"\n\"TigaseDB 表现在都使用 ``tig_`` 前缀。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:360\nmsgid \"\"\n\"`#5214 <https://projects.tigase.net/issues?q=Redmine%20ID:%205214>`__ \"\n\"Check has been added if recipient exists before storing offline messages \"\n\"for local jid.\"\nmsgstr \"\"\n\"`#5214 <https://projects.tigase.net/issues?q=Redmine%20ID:%205214>`__ \"\n\"在为本地 jid 存储离线消息之前，检查是否存在收件人。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:362\nmsgid \"\"\n\"`#5293 <https://projects.tigase.net/issues?q=Redmine%20ID:%205293>`__ \"\n\"``DbSchemaLoader`` now will fail execution instead of skipping when \"\n\"encountering missing files.\"\nmsgstr \"\"\n\"`#5293 <https://projects.tigase.net/issues?q=Redmine%20ID:%205293>`__ \"\n\"现在``DbSchemaLoader`` 遇到丢失的文件时将执行失败而不是跳过。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:364\nmsgid \"\"\n\"`#5379 <https://projects.tigase.net/issues?q=Redmine%20ID:%205379>`__ \"\n\"Server ready detection has been improved in testrunner.sh.\"\nmsgstr \"\"\n\"`#5379 <https://projects.tigase.net/issues?q=Redmine%20ID:%205379>`__ 在 \"\n\"testrunner.sh 中改进了服务器就绪检测。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:366\nmsgid \"\"\n\"`#5397 <https://projects.tigase.net/issues?q=Redmine%20ID:%205397>`__ \"\n\"Webhelp Documentation will no longer be built.\"\nmsgstr \"\"\n\"`#5397 <https://projects.tigase.net/issues?q=Redmine%20ID:%205397>`__ \"\n\"将不再创建 Webhelp 文档。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:368\nmsgid \"\"\n\"`#5422 <https://projects.tigase.net/issues?q=Redmine%20ID:%205422>`__ \"\n\"Errors with Beans will now result in compact and more readable StackTrace\"\n\" print in console log.\"\nmsgstr \"\"\n\"`#5422 <https://projects.tigase.net/issues?q=Redmine%20ID:%205422>`__ \"\n\"Beans 的错误现在将导致控制台日志中的 StackTrace 打印紧凑且更具可读性。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:370\nmsgid \"\"\n\"`#5423 <https://projects.tigase.net/issues?q=Redmine%20ID:%205423>`__ \"\n\"System configuration will now be printed to log file as \"\n\"``ConfigHolder.loadConfiguration`` output.\"\nmsgstr \"\"\n\"`#5423 <https://projects.tigase.net/issues?q=Redmine%20ID:%205423>`__ \"\n\"系统配置现在将作为``ConfigHolder.loadConfiguration`` 输出打印到日志文件中。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:372\nmsgid \"\"\n\"`#5425 <https://projects.tigase.net/issues?q=Redmine%20ID:%205425>`__ \"\n\"``GetAnyFile`` and ``GetConfigFile`` scripts moved to message-router \"\n\"instead of basic-conf.\"\nmsgstr \"\"\n\"`#5425 <https://projects.tigase.net/issues?q=Redmine%20ID:%205425>`__ \"\n\"``GetAnyFile`` 和 ``GetConfigFile``脚本移至消息路由器而不是基本配置。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:374\nmsgid \"\"\n\"`#5429 <https://projects.tigase.net/issues?q=Redmine%20ID:%205429>`__ \"\n\"Adjusted settings for Dynamic Rostering now can use separate beans for \"\n\"multiple implementations.\"\nmsgstr \"\"\n\"`#5429 <https://projects.tigase.net/issues?q=Redmine%20ID:%205429>`__ \"\n\"现在调整后的动态排班设置使得单独的 bean可 用于多个实现。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:376\nmsgid \"\"\n\"`#5430 <https://projects.tigase.net/issues?q=Redmine%20ID:%205430>`__ \"\n\"``BindResource`` is now set to FINER log level to reduce console output \"\n\"verbosity.\"\nmsgstr \"\"\n\"`#5430 <https://projects.tigase.net/issues?q=Redmine%20ID:%205430>`__ \"\n\"现在``BindResource``设置为 FINER 日志级别以减少控制台输出的冗长度。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:378\nmsgid \"\"\n\"`#5475 <https://projects.tigase.net/issues?q=Redmine%20ID:%205475>`__ \"\n\"Setting default environment variables is now possible in ``config.tdsl`` \"\n\"file using ``env('env-1', 'def-value')`` lines. Details available `in DSL\"\n\" Configuration <#dslEnv>`__ section.\"\nmsgstr \"\"\n\"`#5475 <https://projects.tigase.net/issues?q=Redmine%20ID:%205475>`__ \"\n\"现在可以在 ``config.tdsl``文件中使用``env('env-1', 'def-value')``行设置默认环境变量。 `DSL 配置\"\n\" <#dslEnv>`__ 部分中提供了详细信息。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:380\nmsgid \"\"\n\"`#5496 <https://projects.tigase.net/issues?q=Redmine%20ID:%205496>`__ \"\n\"``Destroy Schema`` task now added to schema manager.\"\nmsgstr \"\"\n\"`#5496 <https://projects.tigase.net/issues?q=Redmine%20ID:%205496>`__ \"\n\"``Destroy Schema``任务现在添加到模式管理器。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:382\nmsgid \"\"\n\"`#5583 <https://projects.tigase.net/issues?q=Redmine%20ID:%205583>`__ \"\n\"Error messages now properly sent when offline message storage is full.\"\nmsgstr \"\"\n\"`#5583 <https://projects.tigase.net/issues?q=Redmine%20ID:%205583>`__ \"\n\"现在，当离线消息存储已满时，错误消息会正常发送。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:384\nmsgid \"\"\n\"`#5674 <https://projects.tigase.net/issues?q=Redmine%20ID:%205674>`__ All\"\n\" components now use UTC timestamp when interacting with databases.\"\nmsgstr \"\"\n\"`#5674 <https://projects.tigase.net/issues?q=Redmine%20ID:%205674>`__ \"\n\"现在，所有组件在与数据库交互时都使用 UTC 时间戳。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:386\nmsgid \"\"\n\"`#5800 <https://projects.tigase.net/issues?q=Redmine%20ID:%205800>`__ \"\n\"Better annotation of deprecated code, cleanup and removal code previously\"\n\" marked as deprecated.\"\nmsgstr \"\"\n\"`#5800 <https://projects.tigase.net/issues?q=Redmine%20ID:%205800>`__ \"\n\"更好地注释已弃用的代码，清理和删除以前标记为已弃用的代码。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:388\nmsgid \"\"\n\"`#5964 <https://projects.tigase.net/issues?q=Redmine%20ID:%205964>`__ \"\n\"Server version is now added to JMX statistics.\"\nmsgstr \"\"\n\"`#5964 <https://projects.tigase.net/issues?q=Redmine%20ID:%205964>`__ \"\n\"服务器版本现已添加到 JMX 统计信息中。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:390\nmsgid \"\"\n\"`#5982 <https://projects.tigase.net/issues?q=Redmine%20ID:%205982>`__ \"\n\"Remote JVM debugging configuration added to tigase.conf file, commented \"\n\"by default.\"\nmsgstr \"\"\n\"`#5982 <https://projects.tigase.net/issues?q=Redmine%20ID:%205982>`__ \"\n\"tigase.conf 文件中添加了远程 JVM 调试配置，默认注释。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:392\nmsgid \"\"\n\"`#6038 <https://projects.tigase.net/issues?q=Redmine%20ID:%206038>`__ \"\n\"Data Source pool connections are now initialized concurrently instead of \"\n\"one at a time, dropping initializing time.\"\nmsgstr \"\"\n\"`#6038 <https://projects.tigase.net/issues?q=Redmine%20ID:%206038>`__ \"\n\"数据源池连接现在是同时初始化的，而不是一次一个，从而减少了初始化时间。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:394\nmsgid \"\"\n\"`#6103 <https://projects.tigase.net/issues?q=Redmine%20ID:%206103>`__ \"\n\":literal:`RosterElement`no longer keeps `XMPPResourceConnection` instance\"\n\" as it is cached elsewhere. Removal results in net improvement in memory \"\n\"footprint.\"\nmsgstr \"\"\n\"`#6103 <https://projects.tigase.net/issues?q=Redmine%20ID:%206103>`__ \"\n\":literal:`RosterElement`不再保留`XMPPResourceConnection`实例，因为它缓存在其他地方。删除导致了所占内存量的净改善。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:396\nmsgid \"\"\n\"`#6133 <https://projects.tigase.net/issues?q=Redmine%20ID:%206133>`__ \"\n\"Tigase now checks components against server version to ensure \"\n\"compatibility.\"\nmsgstr \"\"\n\"`#6133 <https://projects.tigase.net/issues?q=Redmine%20ID:%206133>`__ \"\n\"Tigase 现在根据服务器版本检查组件以确保兼容性。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:398\nmsgid \"\"\n\"`#6163 <https://projects.tigase.net/issues?q=Redmine%20ID:%206163>`__ \"\n\"Groovy plugin updated to v2.4.12.\"\nmsgstr \"\"\n\"`#6163 <https://projects.tigase.net/issues?q=Redmine%20ID:%206163>`__ \"\n\"Groovy 插件更新到 v2.4.12。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:400\nmsgid \"\"\n\"`#6206 <https://projects.tigase.net/issues?q=Redmine%20ID:%206206>`__ \"\n\"Separated TigaseXMLTools and TigaseUtil packages for better compatibility\"\n\" with JDK v9.\"\nmsgstr \"\"\n\"`#6206 <https://projects.tigase.net/issues?q=Redmine%20ID:%206206>`__ 分开的\"\n\" TigaseXMLTools 和 TigaseUtil 包以更好地与 JDK v9 兼容。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:402\nmsgid \"\"\n\"`#6216 <https://projects.tigase.net/issues?q=Redmine%20ID:%206216>`__ \"\n\"MongoDB Driver now updated to v3.5.0.\"\nmsgstr \"\"\n\"`#6216 <https://projects.tigase.net/issues?q=Redmine%20ID:%206216>`__ \"\n\"MongoDB 驱动程序现已更新到 v3.5.0。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:404\nmsgid \"\"\n\"`#6560 <https://projects.tigase.net/issues?q=Redmine%20ID:%206560>`__ \"\n\"tigase anti-spam component now included in tigase dist-max archive.\"\nmsgstr \"\"\n\"`#6560 <https://projects.tigase.net/issues?q=Redmine%20ID:%206560>`__ \"\n\"tigase 反垃圾邮件组件现在包含在 tigase dist-max 存档中。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:406\nmsgid \"\"\n\"`#6821 <https://projects.tigase.net/issues?q=Redmine%20ID:%206821>`__ \"\n\"Improved error reporting when errors from ``ConfigReader``.\"\nmsgstr \"\"\n\"`#6821 <https://projects.tigase.net/issues?q=Redmine%20ID:%206821>`__ \"\n\"改进了来自 ``ConfigReader`` 错误的错误报告。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:408\nmsgid \"\"\n\"`#6842 <https://projects.tigase.net/issues?q=Redmine%20ID:%206842>`__ \"\n\"``DefaultTypesConverter`` no longer requires case sensitive enums.\"\nmsgstr \"\"\n\"`#6842 <https://projects.tigase.net/issues?q=Redmine%20ID:%206842>`__ \"\n\"``DefaultTypesConverter`` 不再需要区分大小写的枚举。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:410\nmsgid \"\"\n\"`#7082 <https://projects.tigase.net/issues?q=Redmine%20ID:%207082>`__ \"\n\"``ClassUtilBean`` now handles packet filtering for packets part of Tigase\"\n\" Server but not containing beans, other improvements to mDNS.\"\nmsgstr \"\"\n\"`#7082 <https://projects.tigase.net/issues?q=Redmine%20ID:%207082>`__ \"\n\"现在``ClassUtilBean``处理 Tigase 服务器的数据包部分的数据包过滤，但不包含 beans，mDNS 的其他改进。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:412\nmsgid \"\"\n\"`#7433 <https://projects.tigase.net/issues?q=Redmine%20ID:%207433>`__ \"\n\"``SeeOtherHost`` no longer uses ``PropertiesBeanConfigurator`` to parse \"\n\"configuration.\"\nmsgstr \"\"\n\"`#7433 <https://projects.tigase.net/issues?q=Redmine%20ID:%207433>`__ \"\n\"``SeeOtherHost`` 不再使用 ``PropertiesBeanConfigurator`` 来解析配置。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:414\nmsgid \"\"\n\"`#7446 <https://projects.tigase.net/issues?q=Redmine%20ID:%207446>`__ \"\n\"User credentials can now be managed with Ad-hoc commands.\"\nmsgstr \"\"\n\"`#7446 <https://projects.tigase.net/issues?q=Redmine%20ID:%207446>`__ \"\n\"现在可以使用 Ad-hoc 命令管理用户凭证。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:416\nmsgid \"\"\n\"`#7743 <https://projects.tigase.net/issues?q=Redmine%20ID:%207743>`__ \"\n\"Improved error message when repository is not found.\"\nmsgstr \"\"\n\"`#7743 <https://projects.tigase.net/issues?q=Redmine%20ID:%207743>`__ \"\n\"当未找到存储库时改进的错误消息。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:418\nmsgid \"\"\n\"`#7773 <https://projects.tigase.net/issues?q=Redmine%20ID:%207773>`__ Ad-\"\n\"hoc commands can now by executed asynchronously.\"\nmsgstr \"\"\n\"`#7773 <https://projects.tigase.net/issues?q=Redmine%20ID:%207773>`__ Ad-\"\n\"hoc 命令现在可以不同步执行。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:420\nmsgid \"\"\n\"`#2341 <https://projects.tigase.net/issues?q=Redmine%20ID:%202341>`__ \"\n\"allow specifying SubscriptionType when adding buddy to avoid calling \"\n\"separately .setBuddySubscription() thus eliminating saving roster twice \"\n\"to database if not needed\"\nmsgstr \"\"\n\"`#2341 <https://projects.tigase.net/issues?q=Redmine%20ID:%202341>`__ \"\n\"允许在添加好友时指定 SubscriptionType 以避免单独调用 .setBuddySubscription() \"\n\"从而避免在不需要时将名册两次保存到数据库。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:425\nmsgid \"Fixes\"\nmsgstr \"修复\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:427\nmsgid \"\"\n\"`#2750 <https://projects.tigase.net/issues?q=Redmine%20ID:%202750>`__ \"\n\"Multiple artifact and depreciated file cleanup. Massive code cleanup and \"\n\"javadoc cleaning.\"\nmsgstr \"\"\n\"`#2750 \"\n\"<https://projects.tigase.net/issues?q=Redmine%20ID:%202750>`__多个工件和折旧文件清理。大量代码清理和\"\n\" javadoc 清理。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:429\nmsgid \"\"\n\"`#3582 <https://projects.tigase.net/issues?q=Redmine%20ID:%203582>`__ \"\n\"Schema files streamlined, and no longer embedded in code.\"\nmsgstr \"\"\n\"`#3582 <https://projects.tigase.net/issues?q=Redmine%20ID:%203582>`__ \"\n\"架构文件经过简化，不再嵌入代码中。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:431\nmsgid \"\"\n\"`#3611 <https://projects.tigase.net/issues?q=Redmine%20ID:%203611>`__ \"\n\"Fixed TheadExceptionHandler caused by ACS unable to read PubSub schema \"\n\"changes.\"\nmsgstr \"\"\n\"`#3611 \"\n\"<https://projects.tigase.net/issues?q=Redmine%20ID:%203611>`__修复了因为 ACS \"\n\"无法读取 PubSub 架构更改而导致的 TheadExceptionHandler。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:433\nmsgid \"\"\n\"`#3686 <https://projects.tigase.net/issues?q=Redmine%20ID:%203686>`__ \"\n\"Issues with processing XHTML-IM have been fixed, and now render correctly\"\n\" messages with multiple CData items.\"\nmsgstr \"\"\n\"`#3686 <https://projects.tigase.net/issues?q=Redmine%20ID:%203686>`__处理 \"\n\"XHTML-IM 的问题已得到修复，现在可以正确呈现具有多个 CData 项的消息。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:435\nmsgid \"\"\n\"`#3689 <https://projects.tigase.net/issues?q=Redmine%20ID:%203689>`__ \"\n\"Packets returned from CM no longer bear the original senders' jid.\"\nmsgstr \"\"\n\"`#3689 <https://projects.tigase.net/issues?q=Redmine%20ID:%203689>`__ 从 \"\n\"CM 返回的数据包不再带有原始发送者的 jid。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:437\nmsgid \"\"\n\"`#3803 <https://projects.tigase.net/issues?q=Redmine%20ID:%203803>`__ New\"\n\" call ``RouteEvent`` has been added to check to list and check events and\"\n\" determine which should be forwarded to other nodes.\"\nmsgstr \"\"\n\"`#3803 <https://projects.tigase.net/issues?q=Redmine%20ID:%203803>`__ \"\n\"添加了新调用``RouteEvent``来检查列表和检查事件，并确定哪些应该被转发到其他节点。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:439\nmsgid \"\"\n\"`#3822 <https://projects.tigase.net/issues?q=Redmine%20ID:%203822>`__ \"\n\"Error is now thrown if listener is registered for an event that is not \"\n\"found in EventBus.\"\nmsgstr \"\"\n\"`#3822 <https://projects.tigase.net/issues?q=Redmine%20ID:%203822>`__ \"\n\"现在，如果为 EventBus 中未找到的事件注册了侦听器，则会引发错误。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:441\nmsgid \"\"\n\"`#3910 <https://projects.tigase.net/issues?q=Redmine%20ID:%203910>`__ \"\n\"Fixed NPE in SessionManager when session is closed during execution of \"\n\"everyMinute method.\"\nmsgstr \"\"\n\"`#3910 <https://projects.tigase.net/issues?q=Redmine%20ID:%203910>`__ \"\n\"修复了在执行 everyMinute 方法期间会话关闭时 SessionManager 中的 NPE。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:443\nmsgid \"\"\n\"`#3911 <https://projects.tigase.net/issues?q=Redmine%20ID:%203911>`__ \"\n\"Fixed issue of dropping connections during thread load distribution.\"\nmsgstr \"\"\n\"`#3911 <https://projects.tigase.net/issues?q=Redmine%20ID:%203911>`__ \"\n\"修复了在线程负载分配期间连接断开的问题。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:445\nmsgid \"\"\n\"`#4185 <https://projects.tigase.net/issues?q=Redmine%20ID:%204185>`__ \"\n\"Fixed an error where messages would be duplicated on stream resumption \"\n\"due to a counter being reset upon reconnection.\"\nmsgstr \"\"\n\"`#4185 <https://projects.tigase.net/issues?q=Redmine%20ID:%204185>`__ \"\n\"修复了由于重新连接时重置计数器而导致消息在流恢复时重复的错误。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:447\nmsgid \"\"\n\"`#4447 <https://projects.tigase.net/issues?q=Redmine%20ID:%204447>`__ \"\n\"Fixed condition where expired messages in offline store would cause \"\n\"locks.\"\nmsgstr \"\"\n\"`#4447 <https://projects.tigase.net/issues?q=Redmine%20ID:%204447>`__ \"\n\"修复了离线存储中过期消息会导致锁定的情况。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:449\nmsgid \"\"\n\"`#4547 <https://projects.tigase.net/issues?q=Redmine%20ID:%204547>`__ \"\n\"config.dump file now is fully compatible with init.tdsl file and DSL file\"\n\" formatting.\"\nmsgstr \"\"\n\"`#4547 <https://projects.tigase.net/issues?q=Redmine%20ID:%204547>`__ \"\n\"config.dump 文件现在与 init.tdsl 文件和 DSL 文件格式完全兼容。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:451\nmsgid \"\"\n\"`#4672 <https://projects.tigase.net/issues?q=Redmine%20ID:%204672>`__ \"\n\"Fixed ``UnsupportedOperationException`` occurring during configuration of\"\n\" ``WebSocketConnectionClustered``.\"\nmsgstr \"\"\n\"`#4672 <https://projects.tigase.net/issues?q=Redmine%20ID:%204672>`__ \"\n\"修复了在配置``UnsupportedOperationException``期间出现的“UnsupportedOperationException”。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:453\nmsgid \"\"\n\"`#4776 <https://projects.tigase.net/issues?q=Redmine%20ID:%204776>`__ \"\n\"``DBSchemaLoader`` now asks for user credentials if parameter is missing.\"\n\" Exceptions are no longer thrown if file specified is not found.\"\nmsgstr \"\"\n\"`#4776 <https://projects.tigase.net/issues?q=Redmine%20ID:%204776>`__ \"\n\"如果参数丢失，``DBSchemaLoader``现在会询问用户凭据。如果找不到指定的文件，则不再引发异常。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:455\nmsgid \"\"\n\"`#4885 <https://projects.tigase.net/issues?q=Redmine%20ID:%204885>`__ \"\n\"``client-port-delay-listening`` no longer causes exception when called.\"\nmsgstr \"\"\n\"`#4885 <https://projects.tigase.net/issues?q=Redmine%20ID:%204885>`__ \"\n\"``client-port-delay-listening`` 调用时不再导致异常。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:457\nmsgid \"\"\n\"`#4973 <https://projects.tigase.net/issues?q=Redmine%20ID:%204973>`__ \"\n\"Changed Message History query to now include a limit when selecting \"\n\"items, preventing an SQLTimeoutException.\"\nmsgstr \"\"\n\"`#4973 <https://projects.tigase.net/issues?q=Redmine%20ID:%204973>`__ \"\n\"将消息历史查询更改为现在在选择项目时包含了限制，从而防止 SQLTimeoutException。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:459\nmsgid \"\"\n\"`#5005 <https://projects.tigase.net/issues?q=Redmine%20ID:%205005>`__ \"\n\"Fixed an issue where disabling components would result in server \"\n\"shutdown.\"\nmsgstr \"\"\n\"`#5005 \"\n\"<https://projects.tigase.net/issues?q=Redmine%20ID:%205005>`__修复了禁用组件会导致服务器关闭的问题。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:461\nmsgid \"\"\n\"`#5042 <https://projects.tigase.net/issues?q=Redmine%20ID:%205042>`__ \"\n\"Fixed issue when implementing custom SASL providers, mechanisms and \"\n\"callback handler factories.\"\nmsgstr \"\"\n\"`#5042 <https://projects.tigase.net/issues?q=Redmine%20ID:%205042>`__ \"\n\"修复了实现自定义 SASL 提供程序，机制和回调处理程序工厂时的问题。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:463\nmsgid \"\"\n\"`#5066 <https://projects.tigase.net/issues?q=Redmine%20ID:%205066>`__ \"\n\"Fixed issue initializing databases using MongoDB.\"\nmsgstr \"\"\n\"`#5066 <https://projects.tigase.net/issues?q=Redmine%20ID:%205066>`__ \"\n\"修复了使用 MongoDB 初始化数据库的问题。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:465\nmsgid \"\"\n\"`#5076 <https://projects.tigase.net/issues?q=Redmine%20ID:%205076>`__ \"\n\"last_login and last_logout values are now properly updated while using \"\n\"SASL SCRAM authentication.\"\nmsgstr \"\"\n\"`#5076 <https://projects.tigase.net/issues?q=Redmine%20ID:%205076>`__ \"\n\"last_login 和 last_logout 值现在在使用 SASL SCRAM 身份验证时被正确更新。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:467\nmsgid \"\"\n\"`#5084 <https://projects.tigase.net/issues?q=Redmine%20ID:%205084>`__ \"\n\"SCRAM now checks to see if account is disabled before retrieving \"\n\"password.\"\nmsgstr \"\"\n\"`#5084 <https://projects.tigase.net/issues?q=Redmine%20ID:%205084>`__ \"\n\"SCRAM 现在会在检索密码之前检查帐户是否被禁用。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:469\nmsgid \"\"\n\"`#5085 <https://projects.tigase.net/issues?q=Redmine%20ID:%205085>`__ \"\n\"Fixed ``too many beans implemented`` error in Monitor Component.\"\nmsgstr \"\"\n\"`#5085 <https://projects.tigase.net/issues?q=Redmine%20ID:%205085>`__ 修复了\"\n\" Monitor Component 中的``too many beans implemented``错误。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:471\nmsgid \"\"\n\"`#5088 <https://projects.tigase.net/issues?q=Redmine%20ID:%205088>`__ \"\n\"Removed unnecessary SASL request processing after session is closed.\"\nmsgstr \"\"\n\"`#5088 <https://projects.tigase.net/issues?q=Redmine%20ID:%205088>`__ \"\n\"会话关闭后删除了不必要的 SASL 处理请求。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:473\nmsgid \"\"\n\"`#5118 <https://projects.tigase.net/issues?q=Redmine%20ID:%205118>`__ \"\n\"Fixed NPE during query of privacy lists then ``type`` is missing.\"\nmsgstr \"\"\n\"`#5118 <https://projects.tigase.net/issues?q=Redmine%20ID:%205118>`__ \"\n\"修复了查询隐私列表期间的 NPE，然后缺少``type``。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:475\nmsgid \"\"\n\"`#5303 <https://projects.tigase.net/issues?q=Redmine%20ID:%205303>`__ \"\n\"Fixed beans not being overridden by configuration if they were registered\"\n\" in ``RegistrarBean`` or ``AbstractKernelBasedComponent``.\"\nmsgstr \"\"\n\"`#5303 <https://projects.tigase.net/issues?q=Redmine%20ID:%205303>`__ \"\n\"修复了在 ``RegistrarBean`` \"\n\"或``AbstractKernelBasedComponent``中注册的bean不会被配置覆盖的问题。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:477\nmsgid \"\"\n\"`#5311 <https://projects.tigase.net/issues?q=Redmine%20ID:%205311>`__ \"\n\"Offline messages are no longer dumped from MongoDB when restarting \"\n\"server.\"\nmsgstr \"\"\n\"`#5311 <https://projects.tigase.net/issues?q=Redmine%20ID:%205311>`__ \"\n\"重新启动服务器时，不再从 MongoDB 转储离线消息。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:479\nmsgid \"\"\n\"`#5394 <https://projects.tigase.net/issues?q=Redmine%20ID:%205394>`__ \"\n\"Loading main Derby schema no longer throws exceptions.\"\nmsgstr \"\"\n\"`#5394 <https://projects.tigase.net/issues?q=Redmine%20ID:%205394>`__加载主 \"\n\"Derby 模式不再引发异常。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:481\nmsgid \"\"\n\"`#5428 <https://projects.tigase.net/issues?q=Redmine%20ID:%205428>`__ \"\n\"Fixed parsing of v-host per domain limit property.\"\nmsgstr \"\"\n\"`#5428 <https://projects.tigase.net/issues?q=Redmine%20ID:%205428>`__ \"\n\"修复了每个域限制属性的 v-host 解析。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:483\nmsgid \"\"\n\"`#5450 <https://projects.tigase.net/issues?q=Redmine%20ID:%205450>`__ \"\n\"Server no longer automatically shuts down when default or other db can \"\n\"not be found or accessed.\"\nmsgstr \"\"\n\"`#5450 \"\n\"<https://projects.tigase.net/issues?q=Redmine%20ID:%205450>`__当无法找到或访问默认或其他数据库时，服务器不再自动关闭。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:485\nmsgid \"\"\n\"`#5458 <https://projects.tigase.net/issues?q=Redmine%20ID:%205458>`__ \"\n\"Fixed potential timeout arising from \"\n\"``XMPPIOService::xmppStreamOpened()`` method.\"\nmsgstr \"\"\n\"`#5458 <https://projects.tigase.net/issues?q=Redmine%20ID:%205458>`__ \"\n\"修复了由``XMPPIOService::xmppStreamOpened()``方法引起的潜在超时。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:487\nmsgid \"\"\n\"`#5480 <https://projects.tigase.net/issues?q=Redmine%20ID:%205480>`__ \"\n\"Fixed issue in Derby DB where obtaining offline messages results in \"\n\"SQLException.\"\nmsgstr \"\"\n\"`#5480 <https://projects.tigase.net/issues?q=Redmine%20ID:%205480>`__ 修复了\"\n\" Derby DB 中获取离线消息而导致 SQLException 的问题。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:489\nmsgid \"\"\n\"`#5525 <https://projects.tigase.net/issues?q=Redmine%20ID:%205525>`__ \"\n\"Fixed S2S ``invalid-namespace`` error being returned during connection \"\n\"establishment.\"\nmsgstr \"\"\n\"`#5525 <https://projects.tigase.net/issues?q=Redmine%20ID:%205525>`__ \"\n\"修复了连接建立期间返回的 S2S ``invalid-namespace`` 错误。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:491\nmsgid \"\"\n\"`#5587 <https://projects.tigase.net/issues?q=Redmine%20ID:%205587>`__ \"\n\"Fixed unclosed ``ResultSet`` when storing a message to AMP-offline \"\n\"database in Derby causing deadlock.\"\nmsgstr \"\"\n\"`#5587 <https://projects.tigase.net/issues?q=Redmine%20ID:%205587>`__ \"\n\"修复了在 Derby 中将消息存储到 AMP 离线数据库时由未关闭的 ``ResultSet`` 导致死锁的问题。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:493\nmsgid \"\"\n\"`#5645 <https://projects.tigase.net/issues?q=Redmine%20ID:%205645>`__ \"\n\"Added fix for possible NPE when failing to retrieve beans.\"\nmsgstr \"\"\n\"`#5645 <https://projects.tigase.net/issues?q=Redmine%20ID:%205645>`__ \"\n\"添加了在检索 bean 失败时对可能出现的 NPE 的修复。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:495\nmsgid \"\"\n\"`#5670 <https://projects.tigase.net/issues?q=Redmine%20ID:%205670>`__ \"\n\"config-dump now prints configuration for inactive components and beans to\"\n\" log.\"\nmsgstr \"\"\n\"`#5670 <https://projects.tigase.net/issues?q=Redmine%20ID:%205670>`__ \"\n\"config-dump 现在打印非活动组件和 bean 的配置来做记录。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:497\nmsgid \"\"\n\"`#5692 <https://projects.tigase.net/issues?q=Redmine%20ID:%205692>`__ \"\n\"Messages sent with negative priority were being occasionally dropped and \"\n\"not processed to ``OfflineMessageHandler``.\"\nmsgstr \"\"\n\"`#5692 <https://projects.tigase.net/issues?q=Redmine%20ID:%205692>`__ \"\n\"以负优先级发送的消息偶尔会被丢弃并且未处理到``OfflineMessageHandler``。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:499\nmsgid \"\"\n\"`#5727 <https://projects.tigase.net/issues?q=Redmine%20ID:%205727>`__ \"\n\"Fixed potential issue with MySQL procedures not being killed properly.\"\nmsgstr \"\"\n\"`#5727 <https://projects.tigase.net/issues?q=Redmine%20ID:%205727>`__ 修复了\"\n\" MySQL 程序未正确终止的潜在问题。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:501\nmsgid \"\"\n\"`#5750 <https://projects.tigase.net/issues?q=Redmine%20ID:%205750>`__ \"\n\"Statistics now filter out zero-value results unless FINEST level is \"\n\"requested.\"\nmsgstr \"\"\n\"`#5750 <https://projects.tigase.net/issues?q=Redmine%20ID:%205750>`__ \"\n\"除非请求 FINEST 级别，否则统计信息现在会过滤掉零值结果。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:503\nmsgid \"\"\n\"`#5831 <https://projects.tigase.net/issues?q=Redmine%20ID:%205831>`__ \"\n\"Fixed occurrence of ``OutOfMemory`` error.\"\nmsgstr \"\"\n\"`#5831 <https://projects.tigase.net/issues?q=Redmine%20ID:%205831>`__ \"\n\"修复了``OutOfMemory``错误的发生。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:505\nmsgid \"\"\n\"`#5864 <https://projects.tigase.net/issues?q=Redmine%20ID:%205864>`__ \"\n\"Fixed NPE when executing BOSH pre-bind script.\"\nmsgstr \"\"\n\"`#5864 <https://projects.tigase.net/issues?q=Redmine%20ID:%205864>`__ \"\n\"修复了执行 BOSH 预绑定脚本时的 NPE。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:507\nmsgid \"\"\n\"`#5867 <https://projects.tigase.net/issues?q=Redmine%20ID:%205867>`__ \"\n\"Fixed NPE occurring during configuration dump.\"\nmsgstr \"\"\n\"`#5867 <https://projects.tigase.net/issues?q=Redmine%20ID:%205867>`__ \"\n\"修复了配置转储期间发生的 NPE。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:509\nmsgid \"\"\n\"`#6000 <https://projects.tigase.net/issues?q=Redmine%20ID:%206000>`__ \"\n\"Fixed a few issues with dynamic rosters properly handling presence \"\n\"subscription requests.\"\nmsgstr \"\"\n\"`#6000 <https://projects.tigase.net/issues?q=Redmine%20ID:%206000>`__ \"\n\"修复了动态名册正确处理订阅请求状态的一些问题。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:511\nmsgid \"\"\n\"`#6006 <https://projects.tigase.net/issues?q=Redmine%20ID:%206006>`__ \"\n\"Improved configuration file and DB Schema handling.\"\nmsgstr \"\"\n\"`#6006 <https://projects.tigase.net/issues?q=Redmine%20ID:%206006>`__ \"\n\"改进的配置文件和数据库模式处理。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:513\nmsgid \"\"\n\"`#6041 <https://projects.tigase.net/issues?q=Redmine%20ID:%206041>`__ \"\n\"Fixed potential issue where vhosts DB could be overwritten by vhosts \"\n\"configuration in ``init.config``.\"\nmsgstr \"\"\n\"`#6041 <https://projects.tigase.net/issues?q=Redmine%20ID:%206041>`__ 修复了\"\n\" vhosts DB 可能被``init.config``中的 vhosts 配置覆盖的潜在问题。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:515\nmsgid \"\"\n\"`#6078 <https://projects.tigase.net/issues?q=Redmine%20ID:%206078>`__ \"\n\"Fixed ``ClusterConnectionManager`` to use custom_elements_limit instead \"\n\"of a fixed value.\"\nmsgstr \"\"\n\"`#6078 <https://projects.tigase.net/issues?q=Redmine%20ID:%206078>`__ 修复了\"\n\" ``ClusterConnectionManager`` 以使用 custom_elements_limit 而不是固定值。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:517\nmsgid \"\"\n\"`#6080 <https://projects.tigase.net/issues?q=Redmine%20ID:%206080>`__ \"\n\"Fixed Packet Filtering to not filter cluster node information requests.\"\nmsgstr \"\"\n\"`#6080 <https://projects.tigase.net/issues?q=Redmine%20ID:%206080>`__ \"\n\"修复了数据包过滤从而不过滤集群节点信息请求。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:519\nmsgid \"\"\n\"`#6083 <https://projects.tigase.net/issues?q=Redmine%20ID:%206083>`__ \"\n\"Fixed clustered mode shutting down server when certain components are \"\n\"disabled.\"\nmsgstr \"\"\n\"`#6083 <https://projects.tigase.net/issues?q=Redmine%20ID:%206083>`__ \"\n\"修复了禁用某些组件时集群模式关闭服务器的问题。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:521\nmsgid \"\"\n\"`#6135 <https://projects.tigase.net/issues?q=Redmine%20ID:%206135>`__ \"\n\"Tigase now properly enabled selective TLS if not enabled globally.\"\nmsgstr \"\"\n\"`#6135 <https://projects.tigase.net/issues?q=Redmine%20ID:%206135>`__ \"\n\"如果未被全局启用，Tigase 现在可以正确选择性启用 TLS。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:523\nmsgid \"\"\n\"`#6140 <https://projects.tigase.net/issues?q=Redmine%20ID:%206140>`__ \"\n\"Fixed issue while sending server welcome message.\"\nmsgstr \"\"\n\"`#6140 <https://projects.tigase.net/issues?q=Redmine%20ID:%206140>`__ \"\n\"修复了发送服务器欢迎消息时的问题。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:525\nmsgid \"\"\n\"`#6141 <https://projects.tigase.net/issues?q=Redmine%20ID:%206141>`__ \"\n\"Fixed NPE at startup.\"\nmsgstr \"\"\n\"`#6141 <https://projects.tigase.net/issues?q=Redmine%20ID:%206141>`__ \"\n\"修复了启动时的 NPE。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:527\nmsgid \"\"\n\"`#6234 <https://projects.tigase.net/issues?q=Redmine%20ID:%206234>`__ \"\n\"Fixed an error where an error message would repeat unnecessarily.\"\nmsgstr \"\"\n\"`#6234 <https://projects.tigase.net/issues?q=Redmine%20ID:%206234>`__ \"\n\"修复了不必要地重复错误消息的问题。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:529\nmsgid \"\"\n\"`#6284 <https://projects.tigase.net/issues?q=Redmine%20ID:%206284>`__ Ad-\"\n\"hoc commands now refresh SSL Certificate, and restart is no longer \"\n\"required.\"\nmsgstr \"\"\n\"`#6284 <https://projects.tigase.net/issues?q=Redmine%20ID:%206284>`__ \"\n\"现在Ad-hoc 命令刷新 SSL 证书，并且不再需要重新启动。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:531\nmsgid \"\"\n\"`#6293 <https://projects.tigase.net/issues?q=Redmine%20ID:%206293>`__ \"\n\"Server no longer sends no response upon setting empty photo in vCard.\"\nmsgstr \"\"\n\"`#6293 <https://projects.tigase.net/issues?q=Redmine%20ID:%206293>`__ 在 \"\n\"vCard 中设置空照片时，服务器不再发送不响应。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:533\nmsgid \"\"\n\"`#6263 <https://projects.tigase.net/issues?q=Redmine%20ID:%206263>`__ \"\n\"Fixed missing namespaces in responses from adhoc commands.\"\nmsgstr \"\"\n\"`#6263 <https://projects.tigase.net/issues?q=Redmine%20ID:%206263>`__ \"\n\"修复了临时命令响应中缺少的命名空间。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:535\nmsgid \"\"\n\"`#6400 <https://projects.tigase.net/issues?q=Redmine%20ID:%206400>`__ \"\n\"Added a proper error when max-queue-size is too small and server cannot \"\n\"start.\"\nmsgstr \"\"\n\"`#6400 <https://projects.tigase.net/issues?q=Redmine%20ID:%206400>`__ 当 \"\n\"max-queue-size 太小并且服务器无法启动时添加了相应的错误。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:537\nmsgid \"\"\n\"`#6408 <https://projects.tigase.net/issues?q=Redmine%20ID:%206408>`__ \"\n\"Fixed an issue where single WebSocket frames contained multiple XML \"\n\"stanzas instead of one per frame.\"\nmsgstr \"\"\n\"`#6408 <https://projects.tigase.net/issues?q=Redmine%20ID:%206408>`__ \"\n\"修复了单个 WebSocket 帧包含多个 XML 节而不是每帧一个的问题。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:539\nmsgid \"\"\n\"`#6411 <https://projects.tigase.net/issues?q=Redmine%20ID:%206411>`__ \"\n\"Main kernel is now called to smooth shutdown. Further, timeout periods \"\n\"are opened up for large instances.\"\nmsgstr \"\"\n\"`#6411 <https://projects.tigase.net/issues?q=Redmine%20ID:%206411>`__ \"\n\"现在调用主内核以顺利关闭。此外，为大型实例开放了超时期限。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:541\nmsgid \"\"\n\"`#6574 <https://projects.tigase.net/issues?q=Redmine%20ID:%206574>`__ SSL\"\n\" certificate upload handling is now fixed within cluster mode.\"\nmsgstr \"\"\n\"`#6574 <https://projects.tigase.net/issues?q=Redmine%20ID:%206574>`__ SSL\"\n\" 证书上传处理现在已在集群模式中修复。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:543\nmsgid \"\"\n\"`#6598 <https://projects.tigase.net/issues?q=Redmine%20ID:%206598>`__ \"\n\"Fixed EventBus Registration connection issues between cluster nodes.\"\nmsgstr \"\"\n\"`#6598 <https://projects.tigase.net/issues?q=Redmine%20ID:%206598>`__ \"\n\"修复了集群节点之间的 EventBus 注册连接问题。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:545\nmsgid \"\"\n\"`#6658 <https://projects.tigase.net/issues?q=Redmine%20ID:%206658>`__ \"\n\"Cluster connections no longer potentially keep open connection after \"\n\"cluster is no longer connected or available.\"\nmsgstr \"\"\n\"`#6658 <https://projects.tigase.net/issues?q=Redmine%20ID:%206658>`__ \"\n\"在集群不再连接或可用后，集群连接不再可能保持打开连接。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:547\nmsgid \"\"\n\"`#6749 <https://projects.tigase.net/issues?q=Redmine%20ID:%206749>`__ \"\n\"Fixed schema parsing for DerbyDB.\"\nmsgstr \"\"\n\"`#6749 <https://projects.tigase.net/issues?q=Redmine%20ID:%206749>`__ 修复了\"\n\" DerbyDB 的模式解析。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:549\nmsgid \"\"\n\"`#6776 <https://projects.tigase.net/issues?q=Redmine%20ID:%206776>`__ \"\n\"Fixed failing Websocket connections if header contains more than one \"\n\"value.\"\nmsgstr \"\"\n\"`#6776 <https://projects.tigase.net/issues?q=Redmine%20ID:%206776>`__ \"\n\"如果标头包含多个值，则修复失败的 Websocket 连接。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:551\nmsgid \"\"\n\"`#6875 <https://projects.tigase.net/issues?q=Redmine%20ID:%206875>`__ \"\n\"Fixed an issue where C2S connections could be accepted before \"\n\"SessionManager was initialized.\"\nmsgstr \"\"\n\"`#6875 <https://projects.tigase.net/issues?q=Redmine%20ID:%206875>`__ \"\n\"修复了在初始化 SessionManager 之前哪里可以接受 C2S 连接的问题。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:553\nmsgid \"\"\n\"`#7037 <https://projects.tigase.net/issues?q=Redmine%20ID:%207037>`__ \"\n\"Fixed error while parsing negative values from ``config.tdsl`` file.\"\nmsgstr \"\"\n\"`#7037 <https://projects.tigase.net/issues?q=Redmine%20ID:%207037>`__ \"\n\"修复了从 ``config.tdsl``文件解析负值时的错误。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:555\nmsgid \"\"\n\"`#7055 <https://projects.tigase.net/issues?q=Redmine%20ID:%207055>`__ \"\n\"Improvements to metaspace use and other memory use tweaks.\"\nmsgstr \"\"\n\"`#7055 <https://projects.tigase.net/issues?q=Redmine%20ID:%207055>`__ \"\n\"改进元空间使用和其他内存使用调整。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:557\nmsgid \"\"\n\"`#7304 <https://projects.tigase.net/issues?q=Redmine%20ID:%207304>`__ \"\n\"Virtual host logs now properly follow log size limits.\"\nmsgstr \"\"\n\"`#7304 <https://projects.tigase.net/issues?q=Redmine%20ID:%207304>`__ \"\n\"现在虚拟主机日志正确遵循日志大小限制。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:559\nmsgid \"\"\n\"`#7431 <https://projects.tigase.net/issues?q=Redmine%20ID:%207431>`__ \"\n\"AdHoc requests between the same user with different resources are no \"\n\"longer dropped with \\\\`NoConnectionIdExecption`error.\"\nmsgstr \"\"\n\"`#7431 <https://projects.tigase.net/issues?q=Redmine%20ID:%207431>`__ \"\n\"具有不同资源的同一用户之间的 AdHoc 请求不再因 \\\\`NoConnectionIdExecption` 错误而被丢弃。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:561\nmsgid \"\"\n\"`#7434 <https://projects.tigase.net/issues?q=Redmine%20ID:%207434>`__ \"\n\"Adjusted ``SeeOtherHotDualIP`` to use new table name in cluster nodes \"\n\"database.\"\nmsgstr \"\"\n\"`#7434 <https://projects.tigase.net/issues?q=Redmine%20ID:%207434>`__ 调整 \"\n\"``SeeOtherHotDualIP`` 以在集群节点数据库中使用新的表名。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:563\nmsgid \"\"\n\"`#7491 <https://projects.tigase.net/issues?q=Redmine%20ID:%207491>`__ \"\n\"Stacktraces from ``CertificateContainer`` are no longer printed to \"\n\"tigase-console.log, but will be printed to tigase.log.\"\nmsgstr \"\"\n\"`#7491 <https://projects.tigase.net/issues?q=Redmine%20ID:%207491>`__ 来自 \"\n\"``CertificateContainer`` 的堆栈跟踪不再打印到 tigase-console.log，但将打印到 tigase.log。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:565\nmsgid \"\"\n\"`#7687 <https://projects.tigase.net/issues?q=Redmine%20ID:%207687>`__ \"\n\"Fixed an error where connections failed after authentication timeout were\"\n\" marked as active after cleanup.\"\nmsgstr \"\"\n\"`#7687 <https://projects.tigase.net/issues?q=Redmine%20ID:%207687>`__ \"\n\"修复了身份验证超时后连接失败在清理后标记为活跃的错误。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:567\nmsgid \"\"\n\"`#7747 <https://projects.tigase.net/issues?q=Redmine%20ID:%207747>`__ \"\n\"Fixed ``ClusterRepoItemEvent`` serialization issues causing unsupported \"\n\"conversion error in cluster mode.\"\nmsgstr \"\"\n\"`#7747 <https://projects.tigase.net/issues?q=Redmine%20ID:%207747>`__ \"\n\"修复了``ClusterRepoItemEvent``序列化问题导致的在集群模式下产生的不支持转换错误。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:569\nmsgid \"\"\n\"`#7495 <https://projects.tigase.net/issues?q=Redmine%20ID:%207495>`__ fix\"\n\" issue with not all logs being obfuscated, added testcase, documentation\"\nmsgstr \"\"\n\"`#7495 <https://projects.tigase.net/issues?q=Redmine%20ID:%207495>`__ \"\n\"修复部分日志都被混淆的问题，添加了测试用例，文档\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:571\nmsgid \"\"\n\"`#8305 <https://projects.tigase.net/issues?q=Redmine%20ID:%208305>`__ fix\"\n\" issue with SeeOtherHostDualIP when using MongoDB\"\nmsgstr \"\"\n\"`#8305 <https://projects.tigase.net/issues?q=Redmine%20ID:%208305>`__ \"\n\"修复使用 MongoDB 时 SeeOtherHostDualIP 的问题\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:576\nmsgid \"Component Changes\"\nmsgstr \"组件更改\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:579\nmsgid \"AMP\"\nmsgstr \"AMP\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:581\nmsgid \"\"\n\"`#7301 <https://projects.tigase.net/issues?q=Redmine%20ID:%207301>`__ \"\n\"Tigase AMP component now uses multiple processing threads.\"\nmsgstr \"\"\n\"`#7301 <https://projects.tigase.net/issues?q=Redmine%20ID:%207301>`__ \"\n\"Tigase AMP 组件现在使用多个处理线程。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:586\nmsgid \"PubSub\"\nmsgstr \"PubSub\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:588\nmsgid \"\"\n\"`#5033 <https://projects.tigase.net/issues?q=Redmine%20ID:%205033>`__ \"\n\"PubSub now compatible with using emojis in pubsub items.\"\nmsgstr \"\"\n\"`#5033 <https://projects.tigase.net/issues?q=Redmine%20ID:%205033>`__ \"\n\"现在PubSub 可以在 pubsub 项目中使用表情符号。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:590\nmsgid \"\"\n\"`#5693 <https://projects.tigase.net/issues?q=Redmine%20ID:%205693>`__ \"\n\"Fixed parsing configuration of SessionManager processors.\"\nmsgstr \"\"\n\"`#5693 <https://projects.tigase.net/issues?q=Redmine%20ID:%205693>`__ 修复了\"\n\" SessionManager 处理器的解析配置。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:592\nmsgid \"\"\n\"`#5766 <https://projects.tigase.net/issues?q=Redmine%20ID:%205766>`__ \"\n\"PubSub now writes to all databases with UTC timestamp.\"\nmsgstr \"\"\n\"`#5766 <https://projects.tigase.net/issues?q=Redmine%20ID:%205766>`__ \"\n\"现在PubSub所有数据库使用 UTC 时间戳。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:594\nmsgid \"\"\n\"`#5953 <https://projects.tigase.net/issues?q=Redmine%20ID:%205953>`__ \"\n\"Fixed presences not being removed from ``presenceByService`` collection \"\n\"if client disconnects without ``<unavailable/>`` presence being sent.\"\nmsgstr \"\"\n\"`#5953 <https://projects.tigase.net/issues?q=Redmine%20ID:%205953>`__ \"\n\"如果客户端在没有发送 ``<unavailable/>`` \"\n\"存在的情况下断开连接，则固定存在不会从``presenceByService``集合中删除。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:596\nmsgid \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"version changed to PubSub v4.0.0.\"\nmsgstr \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"版本更改为 PubSub v4.0.0。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:598\nmsgid \"\"\n\"`#7707 <https://projects.tigase.net/issues?q=Redmine%20ID:%207707>`__ \"\n\"Fixed potential NPE in PubSub.\"\nmsgstr \"\"\n\"`#7707 <https://projects.tigase.net/issues?q=Redmine%20ID:%207707>`__ 修复了\"\n\" PubSub 中潜在的 NPE。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:603\nmsgid \"http-api\"\nmsgstr \"http-api\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:605\nmsgid \"\"\n\"`#4873 <https://projects.tigase.net/issues?q=Redmine%20ID:%204873>`__ \"\n\"Support added to display timestamp fields as data, time, and timezone \"\n\"fields.\"\nmsgstr \"\"\n\"`#4873 <https://projects.tigase.net/issues?q=Redmine%20ID:%204873>`__ \"\n\"添加支持其可以将时间戳字段显示为数据，时间和时区字段。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:607\nmsgid \"\"\n\"`#4876 <https://projects.tigase.net/issues?q=Redmine%20ID:%204876>`__ \"\n\"Implemented using XML repository for new setups, and updated default \"\n\"config to use this.\"\nmsgstr \"\"\n\"`#4876 <https://projects.tigase.net/issues?q=Redmine%20ID:%204876>`__ 使用 \"\n\"XML 存储库实现新设置，并更新默认配置以使用它。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:609\nmsgid \"\"\n\"`#4888 <https://projects.tigase.net/issues?q=Redmine%20ID:%204888>`__ \"\n\"``http-api`` now is enabled by default.\"\nmsgstr \"\"\n\"`#4888 <https://projects.tigase.net/issues?q=Redmine%20ID:%204888>`__ 现在\"\n\"``http-api``默认启用。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:611\nmsgid \"\"\n\"`#5209 <https://projects.tigase.net/issues?q=Redmine%20ID:%205209>`__ \"\n\"Updated visual styling of pages hosted by component.\"\nmsgstr \"\"\n\"`#5209 <https://projects.tigase.net/issues?q=Redmine%20ID:%205209>`__ \"\n\"更新了由组件托管的页面的视觉样式。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:613\nmsgid \"\"\n\"`#5290 <https://projects.tigase.net/issues?q=Redmine%20ID:%205290>`__ \"\n\"Fixed invalid property name.\"\nmsgstr \"\"\n\"`#5290 <https://projects.tigase.net/issues?q=Redmine%20ID:%205290>`__ \"\n\"修复了无效的属性名称。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:615\nmsgid \"\"\n\"`#5316 <https://projects.tigase.net/issues?q=Redmine%20ID:%205316>`__ \"\n\"Account Registration now can now require and send confirmation E-mails.\"\nmsgstr \"\"\n\"`#5316 <https://projects.tigase.net/issues?q=Redmine%20ID:%205316>`__ \"\n\"帐户注册现在可以要求并发送确认电子邮件。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:617\nmsgid \"\"\n\"`#5415 <https://projects.tigase.net/issues?q=Redmine%20ID:%205415>`__ Web\"\n\" Setup now checks configuration for message archive conflicts.\"\nmsgstr \"\"\n\"`#5415 <https://projects.tigase.net/issues?q=Redmine%20ID:%205415>`__ Web\"\n\" 安装程序现在检查配置中的消息存档冲突。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:619\nmsgid \"\"\n\"`#5460 <https://projects.tigase.net/issues?q=Redmine%20ID:%205460>`__ \"\n\"MongoDB now supported through web-setup.\"\nmsgstr \"\"\n\"`#5460 <https://projects.tigase.net/issues?q=Redmine%20ID:%205460>`__ \"\n\"现在通过网络设置支持 MongoDB。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:621\nmsgid \"\"\n\"`#5717 <https://projects.tigase.net/issues?q=Redmine%20ID:%205717>`__ \"\n\"Fixed default values of check-boxes in admin UI not being shown.\"\nmsgstr \"\"\n\"`#5717 <https://projects.tigase.net/issues?q=Redmine%20ID:%205717>`__ \"\n\"修复了在未显示管理 UI 中复选框的默认值。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:623\nmsgid \"\"\n\"`#5950 <https://projects.tigase.net/issues?q=Redmine%20ID:%205950>`__ \"\n\"Supported added for `XEP-0363: HTTP File Upload \"\n\"<https://xmpp.org/extensions/xep-0363.html>`__.\"\nmsgstr \"\"\n\"`#5950 <https://projects.tigase.net/issues?q=Redmine%20ID:%205950>`__ \"\n\"支持添加 `XEP-0363: HTTP File Upload \"\n\"<https://xmpp.org/extensions/xep-0363.html>`__.\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:625\nmsgid \"\"\n\"`#6159 <https://projects.tigase.net/issues?q=Redmine%20ID:%206159>`__ \"\n\"Fixed NPE thrown if scripts directory is not present.\"\nmsgstr \"\"\n\"`#6159 <https://projects.tigase.net/issues?q=Redmine%20ID:%206159>`__ \"\n\"修复了脚本目录不存在时抛出的 NPE。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:627\nmsgid \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"version changed to tigase-http-api v2.0.0.\"\nmsgstr \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"版本更改为 tigase-http-api v2.0.0。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:629\nmsgid \"\"\n\"`#6212 <https://projects.tigase.net/issues?q=Redmine%20ID:%206212>`__ \"\n\"Added mechanism for password changing through HTTP API.\"\nmsgstr \"\"\n\"`#6212 <https://projects.tigase.net/issues?q=Redmine%20ID:%206212>`__ \"\n\"添加了通过 HTTP API 更改密码的机制。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:631\nmsgid \"\"\n\"`#7307 <https://projects.tigase.net/issues?q=Redmine%20ID:%207307>`__ \"\n\"Fixed scripts returning 404 while handling rest/user/ requests even \"\n\"though user exists.\"\nmsgstr \"\"\n\"`#7307 <https://projects.tigase.net/issues?q=Redmine%20ID:%207307>`__ \"\n\"修复了即使用户存在，脚本在处理 rest/user/ 请求时返回 404问题。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:633\nmsgid \"\"\n\"`#7178 <https://projects.tigase.net/issues?q=Redmine%20ID:%207178>`__ Ad-\"\n\"hoc commands are now categorized in groups for better organization.\"\nmsgstr \"\"\n\"`#7178 <https://projects.tigase.net/issues?q=Redmine%20ID:%207178>`__ \"\n\"临时命令现在按组分类，以便更好地组织整理。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:635\nmsgid \"\"\n\"`#7568 <https://projects.tigase.net/issues?q=Redmine%20ID:%207568>`__ \"\n\"Added timeout reading for HTTP request headers, added configurable \"\n\"``accept-timeout``.\"\nmsgstr \"\"\n\"`#7568 <https://projects.tigase.net/issues?q=Redmine%20ID:%207568>`__ 添加了\"\n\" HTTP 请求标头的超时读取，添加了可配置的``accept-timeout``。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:640\nmsgid \"message-archive\"\nmsgstr \"消息存档\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:642\nmsgid \"\"\n\"`#4867 <https://projects.tigase.net/issues?q=Redmine%20ID:%204867>`__ \"\n\"fixed issue when changing MA jid.\"\nmsgstr \"\"\n\"`#4867 <https://projects.tigase.net/issues?q=Redmine%20ID:%204867>`__ \"\n\"修复了更改 MA jid 时的问题。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:644\nmsgid \"\"\n\"`#4888 <https://projects.tigase.net/issues?q=Redmine%20ID:%204888>`__ \"\n\"``message-archive`` is enabled by default.\"\nmsgstr \"\"\n\"`#4888 <https://projects.tigase.net/issues?q=Redmine%20ID:%204888>`__ \"\n\"``message-archive``默认启用。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:646\nmsgid \"\"\n\"`#5033 <https://projects.tigase.net/issues?q=Redmine%20ID:%205033>`__ \"\n\"Update message archive to be compatible with emojis.\"\nmsgstr \"\"\n\"`#5033 <https://projects.tigase.net/issues?q=Redmine%20ID:%205033>`__ \"\n\"更新消息存档以其可以与表情符号兼容。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:648\nmsgid \"\"\n\"`#5391 <https://projects.tigase.net/issues?q=Redmine%20ID:%205391>`__ \"\n\"Added missing query statement block starts and ends to be compatible with\"\n\" SQL Server.\"\nmsgstr \"\"\n\"`#5391 <https://projects.tigase.net/issues?q=Redmine%20ID:%205391>`__ \"\n\"添加了开始和结束所缺少的查询语句块以与 SQL Server 兼容。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:650\nmsgid \"\"\n\"`#5604 <https://projects.tigase.net/issues?q=Redmine%20ID:%205604>`__ \"\n\"Modified access to static fields and functions.\"\nmsgstr \"\"\n\"`#5604 <https://projects.tigase.net/issues?q=Redmine%20ID:%205604>`__ \"\n\"修改了对静态字段和函数的访问。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:652\nmsgid \"\"\n\"`#5681 <https://projects.tigase.net/issues?q=Redmine%20ID:%205681>`__ \"\n\"Fixed duplication of groupchat messages with different ids by modifying \"\n\"hash algorithm.\"\nmsgstr \"\"\n\"`#5681 <https://projects.tigase.net/issues?q=Redmine%20ID:%205681>`__ \"\n\"通过修改哈希算法修复了具有不同 id 的群聊消息的重复的问题。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:654\nmsgid \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"version changed to message-archive v2.0.0.\"\nmsgstr \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"版本更改为消息存档 v2.0.0。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:656\nmsgid \"\"\n\"`#7615 <https://projects.tigase.net/issues?q=Redmine%20ID:%207615>`__ \"\n\"``feature-not-implemented`` response no longer occurs when removing \"\n\"stored messages.\"\nmsgstr \"\"\n\"`#7615 <https://projects.tigase.net/issues?q=Redmine%20ID:%207615>`__ \"\n\"``feature-not-implemented``对删除存储的消息时不再发生响应。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:661\nmsgid \"MUC\"\nmsgstr \"MUC\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:663\nmsgid \"\"\n\"`#4888 <https://projects.tigase.net/issues?q=Redmine%20ID:%204888>`__ \"\n\"``muc`` now is enabled by default.\"\nmsgstr \"\"\n\"`#4888 <https://projects.tigase.net/issues?q=Redmine%20ID:%204888>`__ \"\n\"``muc`` 现在默认启用。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:665\nmsgid \"\"\n\"`#5033 <https://projects.tigase.net/issues?q=Redmine%20ID:%205033>`__ MUC\"\n\" component is now compatible with emojis.\"\nmsgstr \"\"\n\"`#5033 <https://projects.tigase.net/issues?q=Redmine%20ID:%205033>`__ MUC\"\n\" 组件现在与表情符号兼容。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:667\nmsgid \"\"\n\"`#5066 <https://projects.tigase.net/issues?q=Redmine%20ID:%205066>`__ \"\n\"Fixed issues working with MongoDB repository.\"\nmsgstr \"\"\n\"`#5066 <https://projects.tigase.net/issues?q=Redmine%20ID:%205066>`__ \"\n\"修复了使用 MongoDB 存储库的问题。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:669\nmsgid \"\"\n\"`#5085 <https://projects.tigase.net/issues?q=Redmine%20ID:%205085>`__ \"\n\"Removed invalid annotation parameter values.\"\nmsgstr \"\"\n\"`#5085 <https://projects.tigase.net/issues?q=Redmine%20ID:%205085>`__ \"\n\"删除了无效的注释参数值。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:671\nmsgid \"\"\n\"`#5559 <https://projects.tigase.net/issues?q=Redmine%20ID:%205559>`__ \"\n\"Fixed NPE while changing default room configuration.\"\nmsgstr \"\"\n\"`#5559 <https://projects.tigase.net/issues?q=Redmine%20ID:%205559>`__ \"\n\"在更改默认房间配置时修复了 NPE。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:673\nmsgid \"\"\n\"`#5666 <https://projects.tigase.net/issues?q=Redmine%20ID:%205666>`__ \"\n\"User may add more than one ``<item/>`` elements to query when querying \"\n\"room members.\"\nmsgstr \"\"\n\"`#5666 <https://projects.tigase.net/issues?q=Redmine%20ID:%205666>`__ \"\n\"用户在查询房间成员时可以添加多个``<item/>``元素进行查询。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:675\nmsgid \"\"\n\"`#5715 <https://projects.tigase.net/issues?q=Redmine%20ID:%205715>`__ \"\n\"Welcome messages may now be disabled globally, or in individual room \"\n\"configurations.\"\nmsgstr \"\"\n\"`#5715 <https://projects.tigase.net/issues?q=Redmine%20ID:%205715>`__ \"\n\"欢迎消息现在可以全部都禁用，也可以在单个房间配置中禁用。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:677\nmsgid \"\"\n\"`#5736 <https://projects.tigase.net/issues?q=Redmine%20ID:%205736>`__ \"\n\"Rooms with no subject now return empty ``<subject/>`` element, as per \"\n\"`XEP-0048 7.2.16 <https://xmpp.org/extensions/xep-0045.html#enter-\"\n\"subject>`__.\"\nmsgstr \"\"\n\"`#5736 <https://projects.tigase.net/issues?q=Redmine%20ID:%205736>`__ \"\n\"根据`XEP-0048 7.2.16 <https://xmpp.org/extensions/xep-0045.html#enter-\"\n\"subject>`__，没有主题的房间现在返回空的 ``<subject/>`` 元素。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:679\nmsgid \"\"\n\"`#5813 <https://projects.tigase.net/issues?q=Redmine%20ID:%205813>`__ \"\n\"Fixed NPE during room creation.\"\nmsgstr \"\"\n\"`#5813 <https://projects.tigase.net/issues?q=Redmine%20ID:%205813>`__ \"\n\"修复了房间创建期间的 NPE。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:681\nmsgid \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"version changed to tigase-muc v3.0.0.\"\nmsgstr \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"版本更改为 tigase-muc v3.0.0。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:683\nmsgid \"\"\n\"`#6395 <https://projects.tigase.net/issues?q=Redmine%20ID:%206395>`__ \"\n\"Fixed ``tigase.db.UserNotFoundException`` during retrieval of MUC user.\"\nmsgstr \"\"\n\"`#6395 <https://projects.tigase.net/issues?q=Redmine%20ID:%206395>`__ \"\n\"修复了检索 MUC 用户期间的``tigase.db.UserNotFoundException`` 。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:685\nmsgid \"\"\n\"`#6734 <https://projects.tigase.net/issues?q=Redmine%20ID:%206734>`__ \"\n\"Introduced ``muc#roomconfig_maxresources`` to allow configuration of max \"\n\"number of resources for a single occupant.\"\nmsgstr \"\"\n\"`#6734 <https://projects.tigase.net/issues?q=Redmine%20ID:%206734>`__ 引入了\"\n\" ``muc#roomconfig_maxresources`` 以允许为单个占用者配置最大资源数。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:687\nmsgid \"\"\n\"`#7443 <https://projects.tigase.net/issues?q=Redmine%20ID:%207443>`__ \"\n\"Disabled XEP-0091 by default, added history attribute validation.\"\nmsgstr \"\"\n\"`#7443 <https://projects.tigase.net/issues?q=Redmine%20ID:%207443>`__ \"\n\"默认禁用 XEP-0091，添加历史属性验证。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:692\nmsgid \"socks5 Proxy\"\nmsgstr \"socks5 代理\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:694\nmsgid \"\"\n\"`#2750 <https://projects.tigase.net/issues?q=Redmine%20ID:%202750>`__ \"\n\"Cleanup of code and removal of empty javadocs.\"\nmsgstr \"\"\n\"`#2750 <https://projects.tigase.net/issues?q=Redmine%20ID:%202750>`__ \"\n\"清理代码并删除空的 javadocs。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:696\nmsgid \"\"\n\"`#5867 <https://projects.tigase.net/issues?q=Redmine%20ID:%205867>`__ \"\n\"Fixed NPE during configuration dump when component is disabled.\"\nmsgstr \"\"\n\"`#5867 <https://projects.tigase.net/issues?q=Redmine%20ID:%205867>`__ \"\n\"修复了禁用组件时配置转储期间的 NPE。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:698\nmsgid \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"version changed to tigase-socks5 v2.0.0.\"\nmsgstr \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"版本更改为 tigase-socks5 v2.0.0。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:703\nmsgid \"stats\"\nmsgstr \"统计数据\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:705\nmsgid \"\"\n\"`#5206 <https://projects.tigase.net/issues?q=Redmine%20ID:%205206>`__ \"\n\"Fixed exception causing duplicate error entry.\"\nmsgstr \"\"\n\"`#5206 <https://projects.tigase.net/issues?q=Redmine%20ID:%205206>`__ \"\n\"修复了导致重复错误条目的异常。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:707\nmsgid \"\"\n\"`#5728 <https://projects.tigase.net/issues?q=Redmine%20ID:%205728>`__ \"\n\"Fixed ``MySQLIntegrityConstraintViolationException`` in upload handler.\"\nmsgstr \"\"\n\"`#5728 <https://projects.tigase.net/issues?q=Redmine%20ID:%205728>`__ \"\n\"修复了上传处理程序中的``MySQLIntegrityConstraintViolationException``。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:709\nmsgid \"\"\n\"`#6161 <https://projects.tigase.net/issues?q=Redmine%20ID:%206161>`__ \"\n\"Removed usage of classes from javax.xml.ws package for JDKv9 \"\n\"compatibility.\"\nmsgstr \"\"\n\"`#6161 <https://projects.tigase.net/issues?q=Redmine%20ID:%206161>`__ 从 \"\n\"javax.xml.ws 包中删除了类的使用，以实现 JDKv9 兼容性。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:714\nmsgid \"STUN Server\"\nmsgstr \"STUN 服务器\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:716\nmsgid \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"version changed to tigase-stun v2.0.0.\"\nmsgstr \"\"\n\"`#6176 <https://projects.tigase.net/issues?q=Redmine%20ID:%206176>`__ \"\n\"版本更改为 tigase-stun v2.0.0。\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:721\nmsgid \"WebSocket\"\nmsgstr \"WebSocket\"\n\n#: ../../Tigase_Administration/Release_Notes/Tigase_v8.0.0.inc:723\nmsgid \"\"\n\"`#6481 <https://projects.tigase.net/issues?q=Redmine%20ID:%206481>`__ \"\n\"Websocket component has been improved to be more compliant with `rfc6455 \"\n\"<https://tools.ietf.org/html/rfc6455>`__\"\nmsgstr \"\"\n\"`#6481 <https://projects.tigase.net/issues?q=Redmine%20ID:%206481>`__ \"\n\"Websocket 组件已改进这样更符合`rfc6455 <https://tools.ietf.org/html/rfc6455>`__\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Administration/Security/_Security.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: 2022-09-07 17:14+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/security/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Administration/Security/_Security.rst:2\nmsgid \"Security\"\nmsgstr \"安全\"\n\n#: ../../Tigase_Administration/Security/_Security.rst:4\nmsgid \"\"\n\"The articles here cover advanced security features built into to Tigase \"\n\"Server, and some options for adding your own levels of security.\"\nmsgstr \"此处的文章涵盖了 Tigase Server 内置的高级安全功能，以及一些用于添加您自己的安全级别的选项。\"\n\n#: ../../Tigase_Administration/Security/XEP-0191_Blocking_Command.inc:2\nmsgid \"XEP-0191: Blocking Command\"\nmsgstr \"XEP-0191：阻止命令\"\n\n#: ../../Tigase_Administration/Security/XEP-0191_Blocking_Command.inc:4\nmsgid \"\"\n\"The simplest security feature, however, inside an XMPP server is the \"\n\"ability to block users and JIDS. `XEP-0191 \"\n\"<http://xmpp.org/extensions/xep-0191>`__ specifies the parameters of \"\n\"simple blocking without using privacy lists. Below is a breakdown and \"\n\"some sample commands you may find helpful. To enable this feature, be \"\n\"sure the following is in your ``config.tdsl`` file:\"\nmsgstr \"\"\n\"然而，XMPP 服务器中最简单的安全功能是阻止用户和 JIDS 的能力。 `XEP-0191 \"\n\"<http://xmpp.org/extensions/xep-0191>`__ \"\n\"指定不使用隐私列表的简单屏蔽的参数。以下是细分和一些您可能会发现有用的示例命令。要启用此功能，请确保您的 ``config.tdsl`` \"\n\"文件中包含以下内容：\"\n\n#: ../../Tigase_Administration/Security/XEP-0191_Blocking_Command.inc:13\nmsgid \"\"\n\"If you have other plugins running, then just add ``'urn:xmpp:blocking' ()\"\n\" {}`` to the list to activate this feature.\"\nmsgstr \"如果您有其他插件正在运行，那么只需将 ``'urn:xmpp:blocking' () {}`` 添加到列表中以激活此功能。\"\n\n#: ../../Tigase_Administration/Security/XEP-0191_Blocking_Command.inc:15\nmsgid \"\"\n\"To confirm if your installation of Tigase supports this feature, a quick \"\n\"disco#info of your server should reveal the following feature:\"\nmsgstr \"要确认您安装的 Tigase 是否支持此功能，您的服务器的快速 disco#info 应显示以下功能：\"\n\n#: ../../Tigase_Administration/Security/XEP-0191_Blocking_Command.inc:21\nmsgid \"\"\n\"Blocked users are stored on the server on a per-JID basis, so one user \"\n\"may only see his or her blocked JIDs. Lists of blocked JIDs will return \"\n\"as an IQ stanza with a list of <item> fields. To retrieve the blocklist, \"\n\"the following command is issued:\"\nmsgstr \"\"\n\"被阻止的用户以每个 JID 为基础存储在服务器上，因此一个用户可能只能看到他或她被阻止的 JIDs。被阻止的 JIDs 列表将作为带有 \"\n\"<item> 字段列表的 IQ 节返回。要检索阻止列表，发出以下命令：\"\n\n#: ../../Tigase_Administration/Security/XEP-0191_Blocking_Command.inc:29\nmsgid \"The server responds:\"\nmsgstr \"服务器响应：\"\n\n#: ../../Tigase_Administration/Security/XEP-0191_Blocking_Command.inc:40\nmsgid \"\"\n\"To block a JID, a similar stanza to the one above is sent to the server \"\n\"with the items of the blocked JIDs you wish to add:\"\nmsgstr \"要阻止 JID，将与上述类似的节发送到服务器，其中包含您希望添加的被阻止 JID 的项目：\"\n\n#: ../../Tigase_Administration/Security/XEP-0191_Blocking_Command.inc:50\nmsgid \"\"\n\"The server will then push an unavailable presence to blocked contacts. \"\n\"Communication between a contact that is blocked, and an entity that \"\n\"blocked it will result in a <not-acceptable> error:\"\nmsgstr \"然后，服务器会将不可用的状态推送给被阻止的联系人。被阻止的联系人与阻止它的实体之间的通信将导致 <not-acceptable> 错误：\"\n\n#: ../../Tigase_Administration/Security/XEP-0191_Blocking_Command.inc:62\nmsgid \"\"\n\"Unblocking a contact is just as easy as blocking, send an unblock stanza \"\n\"to the server:\"\nmsgstr \"取消阻止联系人就像阻止一样简单，向服务器发送取消阻止节：\"\n\n#: ../../Tigase_Administration/Security/XEP-0191_Blocking_Command.inc:72\nmsgid \"\"\n\"The server will begin pushing presence information to unblocked contacts \"\n\"and resources so long as permissions have not changed between.\"\nmsgstr \"只要权限没有改变，服务器就会开始向未阻止的联系人和资源推送存在信息。\"\n\n#: ../../Tigase_Administration/Security/XEP-0191_Blocking_Command.inc:74\nmsgid \"\"\n\"You may also opt to unblock all contacts and essentially clear out your \"\n\"blocked list using the following command:\"\nmsgstr \"您也可以选择取消阻止所有联系人，并使用以下命令从根本上清除您的阻止列表：\"\n\n#: ../../Tigase_Administration/Security/Account_Registration_Limits.inc:2\nmsgid \"Account Registration Limits\"\nmsgstr \"帐户注册限制\"\n\n#: ../../Tigase_Administration/Security/Account_Registration_Limits.inc:4\nmsgid \"\"\n\"In order to protect Tigase servers from DOS attacks, a limit on number of\"\n\" account registrations per second has been implemented. This may be \"\n\"configured by adding the following line in the ``config.tdsl`` file:\"\nmsgstr \"\"\n\"为了保护 Tigase 服务器免受 DOS 攻击，已经限制了每秒帐户的注册数量。这可以通过在 ``config.tdsl`` \"\n\"文件中添加以下行来配置：\"\n\n#: ../../Tigase_Administration/Security/Account_Registration_Limits.inc:11\nmsgid \"\"\n\"This setting allows for 10 registrations from a single IP per second. If \"\n\"the limit is exceeded, a ``NOT_ALLOWED`` error will be returned.\"\nmsgstr \"此设置允许每秒从单个 IP 进行 10 次注册。如果超出限制，将返回 ``NOT_ALLOWED`` 错误。\"\n\n#: ../../Tigase_Administration/Security/Account_Registration_Limits.inc:13\nmsgid \"It is possible to create two separate counters as well:\"\nmsgstr \"也可以创建两个单独的计数器：\"\n\n#: ../../Tigase_Administration/Security/Account_Registration_Limits.inc:26\nmsgid \"\"\n\"Here we have one for c2s with a limit of 3, and a global for all other \"\n\"connection managers set at 10.\"\nmsgstr \"在这里，我们有一个用于 c2s 的计数器其限制为 3，而另外一个用于所有其他连接管理器的全局设置为 10。\"\n\n#: ../../Tigase_Administration/Security/Account_Registration_Limits.inc:28\nmsgid \"You can also set individual components limits as well:\"\nmsgstr \"您还可以设置单个组件限制：\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:2\nmsgid \"Brute-force attack prevention\"\nmsgstr \"暴力攻击预防\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:4\nmsgid \"\"\n\"Brute-force Prevention is designed to protect Tigase Server against user \"\n\"password guessing. It counts invalid login tries and when it is above \"\n\"limit, it locks login ability for specific time (soft ban). When invalid \"\n\"login counter reaches second level, account will be disabled permanently.\"\nmsgstr \"\"\n\"暴力预防旨在保护 Tigase \"\n\"服务器不会被猜测的用户密码破解。它计算无效的登录尝试，当它超过限制时，它会锁定特定时间的登录能力（软禁令）。当无效登录计数器达到第二级时，帐户将被永久禁用。\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:7\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:49\nmsgid \"Configuration\"\nmsgstr \"配置\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:9\nmsgid \"\"\n\"Brute-force Prevention is configured by VHost. There is following lis of \"\n\"configuration parameters:\"\nmsgstr \"暴力防护由 VHost 配置。有以下配置参数列表：\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:12\nmsgid \"``brute-force-lock-enabled``\"\nmsgstr \"``brute-force-lock-enabled``\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:12\nmsgid \"``boolean``\"\nmsgstr \"``boolean``\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:12\nmsgid \"Brute Force Prevention Enabled\"\nmsgstr \"启用暴力预防\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:14\nmsgid \"``brute-force-lock-after-fails``\"\nmsgstr \"``brute-force-lock-after-fails``\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:14\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:16\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:18\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:20\nmsgid \"``long``\"\nmsgstr \"``long``\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:14\nmsgid \"Number of allowed invalid login\"\nmsgstr \"允许的无效登录次数\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:16\nmsgid \"``brute-force-period-time``\"\nmsgstr \"``brute-force-period-time``\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:16\nmsgid \"Time [sec] in what failed login tries are counted\"\nmsgstr \"计算失败登录尝试的时间 [秒]\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:18\nmsgid \"``brute-force-disable-after-fails``\"\nmsgstr \"``brute-force-disable-after-fails``\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:18\nmsgid \"Threshold beyond which account will be permanently disabled\"\nmsgstr \"帐户将被永久禁用的阈值\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:20\nmsgid \"``brute-force-lock-time``\"\nmsgstr \"``brute-force-lock-time``\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:20\nmsgid \"Time [sec] of soft ban (first threshold)\"\nmsgstr \"软禁令的时间[秒]（第一个阈值）\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:22\nmsgid \"``brute-force-mode``\"\nmsgstr \"``brute-force-mode``\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:22\nmsgid \"``string``\"\nmsgstr \"``string``\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:22\nmsgid \"Working mode (see :ref:`Working modes<WorkingModes>`)\"\nmsgstr \"工作模式（参见 :ref:`工作模式<WorkingModes>`）\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:26\nmsgid \"Detailed statistics\"\nmsgstr \"详细统计\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:28\nmsgid \"\"\n\"By default, in order not to pollute statistics, Brute-Force locker will \"\n\"only provide details about number of locker IPs and JIDs (and total \"\n\"number of locked attempts). In order to have detailed information about \"\n\"IPs and JIDs that has been locked in statistics you should use following \"\n\"configuration:\"\nmsgstr \"\"\n\"默认情况下，为了不污染统计数据，Brute-Force locker 将仅提供有关 locker IP 和 JID \"\n\"数量（以及锁定尝试总数）的详细信息。为了获得相关已锁定在统计信息中的 IP 和 JID 的详细信息，您应该使用以下配置：\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:41\nmsgid \"Working modes\"\nmsgstr \"工作模式\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:43\nmsgid \"There are three working modes:\"\nmsgstr \"共有三种工作模式：\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:45\nmsgid \"\"\n\"``Ip`` - it counts invalid login tries from IP, and locks login ability \"\n\"(soft ban) for IP what reach the threshold\"\nmsgstr \"``Ip`` - 它计算来自 IP 的无效登录尝试，并锁定达到阈值的 IP 的登录（软禁令）\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:47\nmsgid \"\"\n\"``IpJid`` - it counts tries from IP to specific user account. Soft ban \"\n\"locks ability of login to specific JID from specific IP.\"\nmsgstr \"``IpJid`` - 它计算从 IP 到特定用户帐户的尝试。软禁令锁定从特定 IP 登录特定 JID 的能力。\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:49\nmsgid \"\"\n\"``Jid``- similar to ``IpJid`` but checks only JID. Soft ban locks ability\"\n\" of login to specific JID from all IPs.\"\nmsgstr \"``Jid``- 类似于 ``IpJid`` 但只检查 JID。软禁令锁定从所有 IP 登录到特定 JID 的能力。\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:56\nmsgid \"Permanent ban\"\nmsgstr \"永久禁令\"\n\n#: ../../Tigase_Administration/Security/Brute-force_attack_prevention.inc:58\nmsgid \"\"\n\"In modes ``Jid`` and ``IpJid``, when invalid login counter reach \"\n\"threshold ``brute-force-disable-after-fails``, account status will be set\"\n\" o ``disabled``. To enable it again you should use `Re-Enable User \"\n\"<https://xmpp.org/extensions/xep-0133.html#reenable-users>`__ Ad-hoc \"\n\"Command.\"\nmsgstr \"\"\n\"在 ``Jid`` 和 ``IpJid`` 模式下，当无效登录计数器达到 ``brute-force-disable-after-fails`` \"\n\"阈值时，帐户状态将设置为 ``disabled``。要再次启用它，您应该使用 `Re-Enable User \"\n\"<https://xmpp.org/extensions/xep-0133.html#reenable-users>`__ Ad-hoc 命令。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:2\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:12\nmsgid \"Server Certificates\"\nmsgstr \"服务器证书\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:4\nmsgid \":ref:`Creating and Loading the Server Certificate in pem Files<certspem>`\"\nmsgstr \":ref:`在 pem 文件中创建和加载服务器证书<certspem>`\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:9\nmsgid \"Creating and Loading the Server Certificate in pem Files\"\nmsgstr \"在 pem 文件中创建和加载服务器证书\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:14\nmsgid \"\"\n\"Server certificates are needed when you use secure socket connections - \"\n\"SSL/TLS.\"\nmsgstr \"使用安全套接字连接时需要服务器证书 - SSL/TLS。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:16\nmsgid \"\"\n\"For secure socket connection a proper certificate is needed. You can \"\n\"either generate your own self-signed certificate or obtain certificate \"\n\"from trusted third party organization.\"\nmsgstr \"对于安全套接字连接，需要适当的证书。您可以生成自己的自签名证书或从受信任的第三方组织获取证书。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:18\nmsgid \"Here are steps how to obtain certificate from a trusted organization.\"\nmsgstr \"以下是如何从受信任的组织获取证书的步骤。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:20\nmsgid \"Generating your Own Certificates\"\nmsgstr \"生成您自己的证书\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:22\nmsgid \"\"\n\"Self-signed certificates can be generated easily on a Linux system. \"\n\"Although it may not be considered a 'trusted' certificate authority, it \"\n\"can be useful to test server installations. **We do not recommend regular\"\n\" use of self-signed certificates**.\"\nmsgstr \"\"\n\"在 Linux 系统上可以轻松生成自签名证书。尽管它可能不被视为'信任的'证书颁发机构，但它对测试服务器安装很有用。 \"\n\"**我们不建议经常使用自签名证书**。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:26\nmsgid \"\"\n\"that Tigase v5.0 and later can automatically create self signed PEM files\"\n\" if needed. However we will cover doing this process by hand.\"\nmsgstr \"Tigase v5.0 及更高版本可以在需要时自动创建自签名 PEM 文件。但是，我们将介绍手动如何执行此过程。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:28\nmsgid \"\"\n\"This tutorial assumes you are running a Linux-based operating system with\"\n\" access to command shell, and the 'Openssl' package is installed on the \"\n\"system.\"\nmsgstr \"本教程假设您正在运行基于 Linux 的操作系统，可以访问命令 shell，并且系统上安装了 'Openssl' 包。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:30\nmsgid \"The process takes the following steps:\"\nmsgstr \"该过程采取以下步骤：\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:31\nmsgid \"\"\n\"1. Create a local private key. This file ends with .key extension. It is \"\n\"recommended to create a new private key for the process.\"\nmsgstr \"1. 创建本地私钥。此文件以 .key 扩展名结尾。建议为该进程创建一个新的私钥。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:32\nmsgid \"\"\n\"2. Generate a certificate request. This file ends with the .csr extension\"\n\" and is the file sent to the Certificate Authority to be signed.\"\nmsgstr \"2. 生成证书请求。此文件以 .csr 扩展名结尾，是发送给证书颁发机构以进行签名的文件。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:33\nmsgid \"\"\n\"3. CA signs private key. This can be done by your own computer, but can \"\n\"also be done by private CAs for a fee.\"\nmsgstr \"3. CA 签署私钥。这可以通过您自己的计算机完成，但也可以由私人 CAs 完成，但需要付费。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:34\nmsgid \"\"\n\"4. Results are obtained from the CA. This is a ``.crt`` file which \"\n\"contains a separate public certificate.\"\nmsgstr \"4. 结果来自 CA。这是一个 ``.crt`` 文件，其中包含一个单独的公共证书。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:35\nmsgid \"\"\n\"5. Combine the ``.csr`` and ``.crt`` file into a unified ``.pem`` file. \"\n\"Tigase requires keys to be non-password protected PEM files.\"\nmsgstr \"\"\n\"5. 将 ``.csr`` 和 ``.crt`` 文件合并成一个统一的 ``.pem`` 文件。 Tigase 要求密钥是不受密码保护的 PEM \"\n\"文件。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:37\nmsgid \"**Generate local private key.**\"\nmsgstr \"**生成本地私钥。**\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:43\nmsgid \"\"\n\"This command generates a private key using a 1024 bit RSA algorithm. \"\n\"``-out`` designates the name of the file, in this case it will be \"\n\"``domain.com.key``. The exact name is not important, and the file will be\"\n\" created in whatever directory you are currently in.\"\nmsgstr \"\"\n\"此命令使用 1024 位 RSA 算法生成私钥。 ``-out`` 指定文件的名称，在这种情况下它将是 \"\n\"``domain.com.key``。确切的名称并不重要，文件将在您当前所在的任何目录中创建。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:45\nmsgid \"**Generate a certificate request:.**\"\nmsgstr \"**生成证书请求：**\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:51\nmsgid \"\"\n\"This command generates a certificate request using the file specified \"\n\"after ``-key``, and the result file will be ``domain.com.csr``. You will \"\n\"be asked a series of questions to generate the request.\"\nmsgstr \"该命令使用 ``-key`` 之后指定的文件生成证书请求，结果文件将是 ``domain.com.csr``。您将被问到一系列问题以生成请求。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:68\nmsgid \"**Sign the Certificate Request:.**\"\nmsgstr \"**签署证书请求：**\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:70\nmsgid \"\"\n\"Now the .csr file will be signed by a Certificate Authority. In this \"\n\"tutorial, we will be self-signging our certificate. This practice however\"\n\" is generally not recommended, and you will receive notifications that \"\n\"your certificate is not trusted. There are commercial offers from \"\n\"companies to sign your certificate from trusted sources. Please see the \"\n\":ref:`Certificate From Other Providers<OtherSources>` section for more \"\n\"information.\"\nmsgstr \"\"\n\"现在 .csr \"\n\"文件将由证书颁发机构签名。在本教程中，我们将自签名我们的证书。但是，通常不建议采用这种做法，并且您会收到有关您的证书不受信任的通知。公司提供商业报价以从受信任的来源签署您的证书。有关更多信息，请参阅\"\n\" :ref:`来自其他提供商的证书<OtherSources>` 部分。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:76\nmsgid \"\"\n\"This command signs the certificate for 365 days and generates the \"\n\"``domain.com.crt`` file. You can, of course use any number of days you \"\n\"like.\"\nmsgstr \"此命令证书签名 365 天并生成 ``domain.com.crt`` 文件。当然，您可以使用任意天数。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:78\nmsgid \"**Generate PEM file.**\"\nmsgstr \"**生成 PEM 文件。**\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:80\nmsgid \"\"\n\"You should now have the following files in the working directory: ..\\\\\\\\ \"\n\"domain.com.key domain.com.csr domain.com.crt\"\nmsgstr \"您现在应该在工作目录中有以下文件：..\\\\\\\\ domain.com.key domain.com.csr domain.com.crt\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:86\nmsgid \"\"\n\"If the certificate is issued by third-party authority you will have to \"\n\"attach the certificate chain, that being certificate of the authority who\"\n\" has generated your certificate. You normally need to obtain certificates\"\n\" for your chain from the authority who has generated your certificate.\"\nmsgstr \"如果证书是由第三方机构颁发的，您必须附加证书链，即生成您的证书的机构的证书。您通常需要从生成您的证书的机构获取您的链的证书。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:88\nmsgid \"The result file should looks similar to:\"\nmsgstr \"结果文件应类似于：\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:114\nmsgid \"\"\n\"For Tigase server as well as many other servers (Apache 2.x), the order \"\n\"is following; your domain certificate, your private key, authority \"\n\"issuing your certificate, root certificate.\"\nmsgstr \"对于 Tigase 服务器以及许多其他服务器（Apache 2.x），顺序如下；您的域证书，您的私钥，颁发证书的机构，根证书。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:118\nmsgid \"\"\n\"**Tigase requires full certificate chain in PEM file (described above)! \"\n\"Different applications may require pem file with certificates and private\"\n\" key in different order. So the same file may not be necessarily used by \"\n\"other services like Web server or e-mail server. Currently, Tigase can \"\n\"automatically sort certificates in PEM file while loading it.**\"\nmsgstr \"\"\n\"**Tigase 需要 PEM 文件中的完整证书链（如上所述）！\"\n\"不同的应用程序可能需要具有不同顺序的证书和私钥的 pem 文件。因此，Web \"\n\"服务器或电子邮件服务器等其他服务不一定会使用相同的文件。目前，Tigase \"\n\"可以在加载 PEM 文件时自动对证书进行排序。**\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:123\nmsgid \"Installing/Loading Certificate To the Tigase Server\"\nmsgstr \"将证书安装/加载到 Tigase 服务器\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:125\nmsgid \"\"\n\"Installing and loading certificates is very easy. The server can load all\"\n\" certificates directly from **pem** files. You just need to create a \"\n\"separate pem file for each of your virtual domains and put the file in a \"\n\"directory accessible by the server. Tigase server can automatically load \"\n\"all **pem** files found in given directory. By default, and to make \"\n\"things easy, we recommend the ``Tigase/certs`` directory.\"\nmsgstr \"\"\n\"安装和加载证书非常容易。服务器可以直接从 **pem** 文件加载所有证书。您只需为每个虚拟域创建一个单独的 pem \"\n\"文件，并将该文件放在服务器可访问的目录中。 Tigase 服务器可以自动加载在给定目录中找到的所有 **pem** \"\n\"文件。默认情况下，为了方便起见，我们推荐使用 ``Tigase/certs`` 目录。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:127\nmsgid \"\"\n\"It’s also possible to use: \\\\* Admin ad-hoc command via XMPP client - you\"\n\" should navigate to Service Discovery of your server and in the list of \"\n\"commands for ``VHost Manager`` component select ``Add SSL Certificate`` \"\n\"and then follow instructions \\\\* Admin WebUI - open \"\n\"``http://<host>/admin``, navigate to ``Other`` category and in it select \"\n\"``Add SSL Certificate`` and then follow instructions \\\\* REST API - make \"\n\"a ``POST`` request to http://localhost:8080/rest/adhoc/vhost-\"\n\"man@domain.com with payload containing your certificate; to get the \"\n\"required form fields make ``GET`` request to the same endpoint\"\nmsgstr \"\"\n\"也可以使用：\\\\* Admin ad-hoc 命令通过 XMPP 客户端 - \"\n\"您应该访问到服务器的服务发现并在 ``VHost Manager`` 组件的命令列表中选择 ``\"\n\"Add SSL Certificate``，然后按照说明\\\\* Admin WebUI - 打开 \"\n\"``http://<host>/admin``，访问 ``Other``类别并在其中选择 ``Add SSL \"\n\"Certificate``，然后按照说明\\\\* REST API -使用包含您的证书的有效负载向 \"\n\"http://localhost:8080/rest/adhoc/vhost-man@domain.com 发出 ``POST`` \"\n\"请求；为获取所需的表单字段，请向同一端点发出 ``GET`` 请求\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:132\nmsgid \"Certificate From Other Providers\"\nmsgstr \"来自其他提供商的证书\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:134\nmsgid \"\"\n\"There is number of certificate providers offering certificates either for\"\n\" free or for money. You can use any of them, however you have to be aware\"\n\" that sometimes certificates might not be recognized by all XMPP servers,\"\n\" especially if it’s one from a new provider. Here is an example list of \"\n\"providers:\"\nmsgstr \"\"\n\"有许多证书提供商免费或有偿提供证书。您可以使用其中的任何一个，但是您必须注意，有时证书可能无法被所有 XMPP \"\n\"服务器识别，特别是如果它是来自新提供商的证书。以下是提供商的示例列表：\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:136\nmsgid \"\"\n\"LetsEncrypt - please see :ref:`Installing LetsEncrypt Certificates in \"\n\"Your Linux System<LetsEncryptCertificate>` for details\"\nmsgstr \"\"\n\"LetsEncrypt - 有关详细信息，请参阅 :ref:`在 Linux 系统中安装 LetsEncrypt \"\n\"证书<LetsEncryptCertificate>`\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:138\nmsgid \"\"\n\"`CAcert <https://www.cacert.org/>`__ - free certificates with Web GUI. \"\n\"(WARNING: it’s not widely accepted)\"\nmsgstr \"`CAcert <https://www.cacert.org/>`__ - 带有 Web GUI 的免费证书。(警告：它没有被广泛接受）\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:140\nmsgid \"\"\n\"`Verisign <https://www.verisign.com/>`__ - very expensive certificates \"\n\"comparing to above provides but the provider is recognized by everybody. \"\n\"If you have a certificate from Verisign you can be sure it is identified \"\n\"as a valid certificate.\"\nmsgstr \"\"\n\"`Verisign <https://www.verisign.com/>`__ - \"\n\"与上述提供的证书相比，此证书非常昂贵，但此证书的提供者得到了所有人的认可。如果您拥有来自 Verisign 的证书，则可以确定此为有效证书。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:142\nmsgid \"\"\n\"`Comodo Certificate Authority <http://www.comodo.com/business-security\"\n\"/digital-certificates/ssl-certificates.php>`__ offers different kind of \"\n\"commercial certificates\"\nmsgstr \"\"\n\"`Comodo 证书颁发机构 <http://www.comodo.com/business-security/digital-\"\n\"certificates/ssl-certificates.php>`__ 提供不同类型的商业证书\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:144\nmsgid \"\"\n\"To obtain certificate from a third party authority you have to go to its \"\n\"website and request the certificate using certificate request generated \"\n\"above. I cannot provide any instructions for this as each of the \"\n\"providers listed have different requirements and interfaces.\"\nmsgstr \"要从第三方机构获取证书，您必须访问其网站并使用上面生成的证书请求来请求证书。我无法为此提供任何说明，因为列出的每个提供商都有不同的要求和接口。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:146\nmsgid \"\"\n\"We **highly** recommend using LetsEncrypt keys to self-sign and secure \"\n\"your domain. Instructions are in the :ref:`next \"\n\"section<LetsEncryptCertificate>`.\"\nmsgstr \"\"\n\"我们 **强烈** 建议使用 LetsEncrypt 密钥进行自签名并保护您的域。说明在 \"\n\":ref:`下一节<LetsEncryptCertificate>`\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:149\nmsgid \"Using one certificate for multiple domains\"\nmsgstr \"对多个域使用一个证书\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:153\nmsgid \"\"\n\"Tigase tries to be *smart* and automatically detects wildcard domain and \"\n\"alternative domains so it’s not needed to duplicate same certificate in \"\n\"multiple files to match domains - same file will be loaded and make \"\n\"available for all domains (CNames) available in the certificate.\"\nmsgstr \"\"\n\"Tigase 尝试变得 *智能*，并自动检测通配符域和备用域，因此无需在多个文件中复制相同的证书以匹配域 - \"\n\"相同的文件将被加载并使其可用于证书中可用的所有域 (CNames)。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:158\nmsgid \"Installing LetsEncrypt Certificates in Your Linux System\"\nmsgstr \"在 Linux 系统中安装 LetsEncrypt 证书\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:160\nmsgid \"\"\n\"LetsEncrypt is a trusted CA that provides free security certificates. \"\n\"Unlike previously self-signed certificates, we can use LetsEncrypt \"\n\"Certificates to certify your domains from a trusted source.\"\nmsgstr \"\"\n\"LetsEncrypt 是一个可信任的 CA，提供免费的安全证书。与以前的自签名证书不同，我们可以使用 LetsEncrypt \"\n\"证书从受信任的来源认证您的域。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:162\nmsgid \"\"\n\"Please refer to official `certbot User Guide \"\n\"<https://certbot.eff.org/docs/using.html>`__ for details how to install \"\n\"and operate the tool, choosing desired method of domain authentication \"\n\"(DNS or webserver). After successful execution the certificate with all \"\n\"related files will be stored under ``/etc/letsencrypt/live/$domain``\"\nmsgstr \"\"\n\"请参考官方 `certbot用户指南 <https://certbot.eff.org/docs/using.html>`__ \"\n\"了解如何安装和操作该工具，选择所需的域认证方法（DNS或网络服务器）。成功执行后，所有相关文件的证书将存储在 \"\n\"``/etc/letsencrypt/live/$domain`` 下\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:169\nmsgid \"In that directory, you will find four files:\"\nmsgstr \"在该目录中，您将找到四个文件：\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:171\nmsgid \"``privkey.pem`` - private key for the certificate\"\nmsgstr \"``privkey.pem`` - 证书的私钥\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:173\nmsgid \"``cert.pem`` - contains the server certificate by itself\"\nmsgstr \"``cert.pem`` - 本身包含服务器证书\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:175\nmsgid \"\"\n\"``chain.pem`` - contains the additional intermediate certificate or \"\n\"certificates\"\nmsgstr \"``chain.pem`` - 包含额外的中间证书或证书\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:177\nmsgid \"\"\n\"``fullchain.pem`` - all certificates, including server certificate (aka \"\n\"leaf certificate or end-entity certificate). The server certificate is \"\n\"the first one in this file, followed by any intermediates.\"\nmsgstr \"\"\n\"``fullchain.pem`` - \"\n\"所有证书，包括服务器证书（又名叶子证书或最终实体证书）。服务器证书是此文件中的第一个证书，然后是任何中间证书。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:179\nmsgid \"\"\n\"For Tigase XMPP Server, we are only concerned with ``privkey.pem`` and \"\n\"``fullchain.pem`` (or ``chain.pem`` - please consider actual issuers and \"\n\"certification chain!).\"\nmsgstr \"\"\n\"对于 Tigase XMPP 服务器，我们只关心 ``privkey.pem`` 和 ``fullchain.pem`` (或 \"\n\"``chain.pem`` - 请考虑实际的颁发者和认证链！)。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:181\nmsgid \"\"\n\"At this point we will need to obtain the root and intermediate \"\n\"certificates, this can be done by downloading these certificates from the\"\n\" `LetsEncrypt Chain of Trust website \"\n\"<https://letsencrypt.org/certificates/>`__.\"\nmsgstr \"\"\n\"此时我们需要获取根证书和中间证书，这可以通过从 `LetsEncrypt Chain of Trust website \"\n\"<https://letsencrypt.org/certificates/>`__ 下载这些证书来完成。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:185\nmsgid \"\"\n\"Please pay utmost attention to the actual certificate issuers and make \"\n\"sure that the certification chain is maintained!\"\nmsgstr \"请高度关注实际的证书颁发者，并确保维护证书链！\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:187\nmsgid \"\"\n\"On the time of the writing, LetsEncrypt was providing domain certificates\"\n\" issued by ``R3`` CertificateAuthorigy (CA). In order to provide complete\"\n\" chain to the root CA you should get Let’s Encrypt R3 (``RSA 2048, O = \"\n\"Let’s Encrypt, CN = R3``) certificate. Depending on desired certification\"\n\" chain you have two options: 1) (default and recommended) using own \"\n\"LetsEncrypt CA: a) ``R3`` certificate signed by ISRG Root X1: \"\n\"https://letsencrypt.org/certs/lets-encrypt-r3.pem b) ``ISRG Root X1`` \"\n\"root certificate: https://letsencrypt.org/certs/isrgrootx1.pem 2) \"\n\"(legacy, option more compatible with old systems): cross-signed \"\n\"certificate by IdenTrust: a) ``R3`` certificate cross-signed by \"\n\"IdenTrust: https://letsencrypt.org/certs/lets-encrypt-r3-cross-signed.pem\"\n\" b) ``TrustID X3 Root`` from IdenTrust: \"\n\"https://letsencrypt.org/certs/trustid-x3-root.pem.txt\"\nmsgstr \"\"\n\"在撰写本文时，LetsEncrypt 提供由 ``R3`` CertificateAuthorigy (CA) 颁发的域证书。为了向根 CA \"\n\"提供完整的链，您应该获得 Let's Encrypt R3 (``RSA 2048, O = Let's Encrypt, CN = R3``) \"\n\"证书。根据所需的证书链，您有两个选择：1）（默认和推荐）使用自己的 LetsEncrypt CA：a）由 ISRG 根 X1 签名的 ``R3``\"\n\" 证书：https://letsencrypt.org/certs/lets-encrypt-r3.pem b) ``ISRG Root X1``\"\n\" 根证书：https://letsencrypt.org/certs/isrgrootx1.pem \"\n\"2)（旧版，与旧系统更兼容的选项）：IdenTrust 的交叉签名证书：a)由 IdenTrust 交叉签名的 ``R3`` \"\n\"证书：https://letsencrypt.org/certs/lets-encrypt-r3-cross-signed.pem b) 来自 \"\n\"IdenTrust 的 ``TrustID X3 Root`` \"\n\"：https://letsencrypt.org/certs/trustid-x3-root.pem.txt\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:189\nmsgid \"Considering first (recommended) option, you may obtain them using wget:\"\nmsgstr \"考虑到第一个（推荐）选项，您可以使用 wget 获取它们：\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:196\nmsgid \"\"\n\"These are the root certificate, and the intermediate certificate signed \"\n\"by root certificate.\"\nmsgstr \"这些是根证书，以及由根证书签名的中间证书。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:200\nmsgid \"\"\n\"IdenTrust cross-signed certificate will not function properly in the \"\n\"future!\"\nmsgstr \"IdenTrust 交叉签名证书以后将无法正常运行！\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:202\nmsgid \"\"\n\"Take the contents of your ``privkey.pem``, certificate, and combine them \"\n\"with the contents of ``isrgrootx1.pem`` and ``lets-encrypt-r3.pem`` into \"\n\"a single pem certificate.\"\nmsgstr \"\"\n\"获取您的 ``privkey.pem`` 证书的内容，并将它们与 ``isrgrootx1.pem`` 和 ``lets-\"\n\"encrypt-r3.pem`` 的内容组合成一个单独的pem证书。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:204\nmsgid \"\"\n\"Depending on your configuration you either need to name the file after \"\n\"your domain such as ``mydomain.com.pem`` and place it under ``certs/`` \"\n\"subdirectory of Tigase XMPP Server installation or update it using admin \"\n\"ad-hoc (see :ref:`Storing and managing certificates<certificateStorage>`)\"\nmsgstr \"\"\n\"根据您的配置，您或者需要在您的域之后命名文件，例如 ``mydomain.com.pem`` 并将其放在 Tigase XMPP 服务器安装的 \"\n\"``certs/`` 子目录下，或者使用管理员 ad-hoc 更新它（请参阅 \"\n\":ref:`存储和管理证书<certificateStorage>`)\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:206\nmsgid \"\"\n\"If you moved all certs to a single directory, you may combine them using \"\n\"the following command under \\\\*nix operating systems:.\"\nmsgstr \"如果您将所有证书移动到单个目录，您可以在 \\\\*nix 操作系统下使用以下命令将它们组合起来：\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:215\nmsgid \"\"\n\"If you are using ``isrgrootx1`` root make sure you use ``cert.pem`` file \"\n\"instead of ``fullchain.pem``, which uses different intermediate \"\n\"certificate ( `Let’s Encrypt Authority X3 (IdenTrust cross-signed) \"\n\"<https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem.txt>`__ )\"\n\" and you will have to use `DST Root CA X3 \"\n\"<https://letsencrypt.org/certs/trustid-x3-root.pem.txt>`__ certificate!\"\nmsgstr \"\"\n\"如果你使用的是 ``isrgrootx1`` 根确保你使用 ``cert.pem`` 文件而不是 \"\n\"``fullchain.pem``，若使用不同的中间证书（`Let's Encrypt Authority X3 (IdenTrust \"\n\"cross-signed) <https://letsencrypt.org/certs/lets-encrypt-x3-cross-\"\n\"signed.pem.txt>`__ ），你将不得不使用 `DST Root CA X3 \"\n\"<https://letsencrypt.org/certs/trustid -x3-root.pem.txt>`__ 证书！\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:217\nmsgid \"Your certificate should look something like this:\"\nmsgstr \"您的证书应如下所示：\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:239\nmsgid \"\"\n\"LetsEncrypt certificates expire 90 days from issue and need to be renewed\"\n\" in order for them to remain valid!\"\nmsgstr \"LetsEncrypt 证书在颁发后 90 天到期，需要更新才能保持有效！\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:241\nmsgid \"You can check your certificate with utility class:\"\nmsgstr \"您可以使用实用程序类检查您的证书：\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:248\nmsgid \"Let’s encrypt and DNS verification\"\nmsgstr \"让我们加密和DNS验证\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:250\nmsgid \"\"\n\"The only way to obtain wildcard (``*.domain.com``) certificate is via DNS\"\n\" verification. Certbot support a number of DNS operators - you can check \"\n\"if your DNS provider is listed by executing ``$ certbot plugins``\"\nmsgstr \"\"\n\"获得通配符（``*.domain.com``）证书的唯一方法是通过 DNS 验证。 Certbot 支持许多 DNS 运营商 - 您可以通过执行 \"\n\"``$ certbot plugins`` 来检查您的 DNS 提供商是否在列表中\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:252\nmsgid \"AWS Route53\"\nmsgstr \"AWS Route53\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:254\nmsgid \"If you want to use it with Amazon Cloud you should install plugin for AWS:\"\nmsgstr \"如果您想将它与 Amazon Cloud 一起使用，您应该为 AWS 安装插件：\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:262\nmsgid \"\"\n\"If you are using certbot under macOS and you installed it via brew then \"\n\"you should use: ``$( brew --prefix certbot )/libexec/bin/pip install \"\n\"certbot-dns-route53``\"\nmsgstr \"\"\n\"如果您在 macOS 下使用 certbot 并通过 brew 安装它，那么您应该使用：``$( brew --prefix certbot \"\n\")/libexec/bin/pip install certbot-dns-route53``\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:264\nmsgid \"\"\n\"You should store your credentials in ``~/.aws/credentials`` (you may want\"\n\" to create dedicated policy for updating DNS as described in `plugin’s \"\n\"documentation <https://certbot-dns-route53.readthedocs.io/en/stable/>`__:\"\nmsgstr \"\"\n\"您应该将您的凭据存储在 ``~/.aws/credentials`` 中（您可能希望创建用于更新 DNS 的专用策略，如 `plugin’s \"\n\"documentation <https://certbot-dns-route53.readthedocs.io/en/stable/>`__ \"\n\"所描述:\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:272\nmsgid \"\"\n\"And afterward you should execute ``certbot`` with ``--dns-route53`` \"\n\"parameter\"\nmsgstr \"之后你应该使用 ``--dns-route53`` 参数执行 ``certbot``\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:275\nmsgid \"Certbot update hook and Tigase API\"\nmsgstr \"Certbot 更新钩子和 Tigase API\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:277\nmsgid \"\"\n\"For greater automation it’s possible to automate updating certificate \"\n\"obtained with ``certbot`` in Tigase XMPP Server. You should use following\"\n\" deploy hook - either add it to ``/etc/letsencrypt/renewal-\"\n\"hooks/deploy/`` or use it directly in ``certboot`` commandline with \"\n\"``--deploy-hook`` parameter (in the latter case, it will be added to \"\n\"particular domain configuration so it’s not necessary to specify \"\n\"UPDATE_DOMAINS).\"\nmsgstr \"\"\n\"为了实现更高的自动化，可以自动更新通过 Tigase XMPP 服务器中的 ``certbot`` 获得的证书。您应该使用以下部署挂钩 - \"\n\"或将其添加到 ``/etc/letsencrypt/renewal-hooks/deploy/`` 或直接在带有 ``--deploy-\"\n\"hook`` 参数的 ``certboot`` 命令行中使用它（在后一种情况下，它将被添加到特定的域配置中，因此没有必要指定 \"\n\"UPDATE_DOMAINS）。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:281\nmsgid \"\"\n\"Please adjust account credentials used for deployment (``USER``, \"\n\"``PASS``, ``DOMAIN``) as well as paths to Let’s Encrypt certificates \"\n\"(*ISRG Root X1* named ``isrgrootx1.pem`` and *Let’s Encrypt Authority X3*\"\n\" named ``letsencryptauthorityx3.pem``)\"\nmsgstr \"\"\n\"请调整用于部署的帐户凭据（``USER``, ``PASS``, ``DOMAIN``）以及 Let's Encrypt 证书的路径（*ISRG \"\n\"Root X1* 名为 ``isrgrootx1.pem`` 和 *Let’s Encrypt Authority X3* 名为 \"\n\"``letsencryptauthorityx3.pem``）\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:364\nmsgid \"\"\n\"If you are not using wildcard certificate when you have to provide \"\n\"certificate for main domain as well as certificates for subdomains that \"\n\"mach all components that you want to expose (muc, pubsub, push, etc…)\"\nmsgstr \"如果您没有使用通配符证书，则必须为主域提供证书以及为要公开的所有组件（muc, pubsub, push 等）的子域提供证书\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:369\nmsgid \"Storing and managing certificates\"\nmsgstr \"存储和管理证书\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:372\nmsgid \"Filesystem\"\nmsgstr \"文件系统\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:374\nmsgid \"\"\n\"By default Tigase loads and stores certificates in ``certs/`` \"\n\"subdirectory. Each *domain* certificate should be stored in a file which \"\n\"filename consists of domain name and ``.pem`` extension, i.e. \"\n\"``<domain>.pem``. For example for domain tigase.net it would be \"\n\"``certs/tigase.net.pem``.\"\nmsgstr \"\"\n\"默认情况下，Tigase 在 ``certs/`` 子目录中加载和存储证书。每个 *domain* 证书都应该存储在一个文件中，此文件名由域名和 \"\n\"``.pem`` 扩展名组成，即 ``<domain>.pem``。例如对于域 tigase.net，它将是 \"\n\"``certs/tigase.net.pem``。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:378\nmsgid \"\"\n\"Tigase tries to be *smart* and automatically detects wildcard domain and \"\n\"alternative domains so it’s not needed to duplicate same certificate in \"\n\"multiple files to match domains.\"\nmsgstr \"Tigase 尝试变得 *智能*，并自动检测通配符域和备用域，因此无需在多个文件中复制相同的证书来匹配域。\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:381\nmsgid \"Database repository\"\nmsgstr \"数据库存储库\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:383\nmsgid \"\"\n\"Alternatively it’s possible to use database as a storage for the \"\n\"certificates. Upon enabling it certificates won’t be read nor stored to \"\n\"the filesystem. You can enable it by adding ``repository () {}`` bean to \"\n\"``'certificate-container' () {}`` in your TDSL configuration file:\"\nmsgstr \"\"\n\"或者，可以使用数据库作为证书的存储。启用它后，证书将不会被读取或存储到文件系统中。您可以通过将 ``repository () {}`` bean\"\n\" 添加到 TDSL 配置文件中的 ``'certificate-container' () {}`` 来启用它：\"\n\n#: ../../Tigase_Administration/Security/Server_Certificates.inc:391\nmsgid \"\"\n\"If you are using database repository then you manage/update certificates \"\n\"using either ad-hoc command ``Add SSL certificate`` from *VHost Manager* \"\n\"or via HTTP REST API.\"\nmsgstr \"\"\n\"如果您使用的是数据库存储库，那么您可以使用来自 *VHost Manager* 的临时命令 ``Add SSL certificate`` 或通过\"\n\" HTTP REST API 来管理/更新证书。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:4\nmsgid \"Custom Authentication Connectors\"\nmsgstr \"自定义身份验证连接器\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:6\nmsgid \"\"\n\"This article presents configuration options available to the \"\n\"administrator and describe how to set Tigase server up to use user \"\n\"accounts data from a different database.\"\nmsgstr \"本文介绍了管理员可用的配置选项，并描述了如何设置 Tigase 服务器以使用来自不同数据库的用户帐户数据。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:8\nmsgid \"\"\n\"The first thing to know is that Tigase server always opens 2 separate \"\n\"connections to the database. One connection is used for user login data \"\n\"and the other is for all other user data like the user roster, vCard, \"\n\"private data storage, privacy lists and so on…​\"\nmsgstr \"\"\n\"首先要知道的是 Tigase 服务器总是打开 2 \"\n\"个独立的数据库连接。一个连接用于用户登录数据，另一个用于所有其他用户数据，如用户名册、vCard、私人数据存储、隐私列表等……​\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:10\nmsgid \"\"\n\"In this article we still assume that Tigase server keeps user data in \"\n\"it’s own database and only login data is retrieved from the external \"\n\"database.\"\nmsgstr \"在本文中，我们仍然假设 Tigase 服务器将用户数据保存在自己的数据库中，并且仅从外部数据库中检索登录数据。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:12\nmsgid \"At the moment Tigase offers following authentication connectors:\"\nmsgstr \"目前 Tigase 提供以下身份验证连接器：\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:14\nmsgid \"\"\n\"``mysql``, ``pgsql``, ``derby`` - standard authentication connector used \"\n\"to load user login data from the main user database used by the Tigase \"\n\"server. In fact the same physical implementation is used for all JDBC \"\n\"databases.\"\nmsgstr \"\"\n\"``mysql``, ``pgsql``, ``derby`` - 标准身份验证连接器，其用于从 Tigase \"\n\"服务器使用的主用户数据库加载用户登录数据。实际上，所有 JDBC 数据库都使用相同的物理实现。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:16\nmsgid \"\"\n\"``drupal`` - is the authentication connector used to integrate the Tigase\"\n\" server with `Drupal CMS <http://drupal.org/>`__.\"\nmsgstr \"\"\n\"``drupal`` - 是用于将 Tigase 服务器与 `Drupal CMS <http://drupal.org/>`__ \"\n\"集成的身份验证连接器。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:18\nmsgid \"\"\n\"``tigase-custom`` - is the authentication connector which can be used \"\n\"with any database. Unlike the 'tigase-auth' connector it allows you to \"\n\"define SQL queries in the configuration file. The advantage of this \"\n\"implementation is that you don’t have to touch your database. You can use\"\n\" either simple plain SQL queries or stored procedures. The configuration \"\n\"is more difficult as you have to enter carefully all SQL queries in the \"\n\"config file and changing the query usually involves restarting the \"\n\"server. For more details about this implementation and all configuration \"\n\"parameters please refer to :ref:`Tigase Custom Auth \"\n\"documentation<custonAuthConnector>`.\"\nmsgstr \"\"\n\"``tigase-custom`` - 是可用于任何数据库的身份验证连接器。与 'tigase-auth' 连接器不同，它允许您在配置文件中定义 \"\n\"SQL 查询。这种实现的优点是您不必接触数据库。您可以使用简单的普通 SQL 查询或存储过程。此时配置更加困难，因为您必须在配置文件中仔细输入所有\"\n\" SQL 查询，并且更改查询通常涉及重新启动服务器。有关此实现和所有配置参数的更多详细信息，请参阅 :ref:`Tigase \"\n\"自定义身份验证文档<custonAuthConnector>`。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:20\nmsgid \"\"\n\"|ss| ``tigase-auth``\\\\ |se|\\\\  (**DEPRECATED**) - is the authentication \"\n\"connector which can be used with any database. It executes stored \"\n\"procedures to perform all actions. Therefore it is a very convenient way \"\n\"to integrate the server with an external database if you don’t want to \"\n\"expose the database structure. You just have to provide a set of stored \"\n\"procedures in the database. While implementing all stored procedures \"\n\"expected by the server might be a bit of work it allows you to hide the \"\n\"database structure and change the SP implementation at any time. You can \"\n\"add more actions on user login/logout without restarting or touching the \"\n\"server. And the configuration on the server side is very simple. For \"\n\"detailed description of this implementation please refer to :ref:`Tigase \"\n\"Auth documentation<tigaseAuthConnector>`.\"\nmsgstr \"\"\n\"|ss| ``tigase-auth``\\\\ |se|\\\\  (**DEPRECATED**) - \"\n\"是可用于任何数据库的身份验证连接器。它执行存储过程以执行所有操作。因此，如果您不想暴露数据库结构，将服务器与外部数据库集成是一种非常方便的方法。您只需在数据库中提供一组存储过程。虽然实现服务器预期的所有存储过程可能需要做一些工作，但它允许您隐藏数据库结构并随时更改\"\n\" SP 实现。您可以在用户登录/注销时添加更多操作而无需重新启动或触摸服务器。并且服务器端的配置非常简单。有关此实现的详细说明，请参阅 \"\n\":ref:`Tigase Auth 文档<tigaseAuthConnector>`。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:22\nmsgid \"\"\n\"As always the simplest way to configure the server is through the \"\n\"``config.tdsl`` file. In the article describing this file you can find \"\n\"long list with all available options and all details how to handle it. \"\n\"For the authentication connector setup however we only need 2 options:\"\nmsgstr \"\"\n\"与往常一样，配置服务器的最简单方法是通过 ``config.tdsl`` \"\n\"文件。在描述此文件的文章中，您可以找到包含所有可用选项的长列表以及如何处理它的所有详细信息。但是，对于身份验证连接器设置，我们只需要 2 个选项：\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:38\nmsgid \"\"\n\"For example if you store authentication data in a ``drupal`` database on \"\n\"``localhost`` your settings would be:\"\nmsgstr \"例如，如果您将身份验证数据存储在 ``localhost`` 上的 ``drupal`` 数据库中，您的设置将是：\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:53\nmsgid \"\"\n\"You have to use a class name if you want to attach your own \"\n\"authentication connector.\"\nmsgstr \"如果要附加自己的身份验证连接器，则必须使用类名。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:55\nmsgid \"Default is:\"\nmsgstr \"默认为：\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:65\nmsgid \"\"\n\"In the same exact way you can setup connector for any different database \"\n\"type.\"\nmsgstr \"以同样的方式，您可以为任何不同的数据库类型设置连接器。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:67\nmsgid \"For example, drupal configuration is below\"\nmsgstr \"例如，drupal 配置如下\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:77\nmsgid \"Or tigase-custom authentication connector.\"\nmsgstr \"或 tigase-custom 身份验证连接器。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:87\nmsgid \"The different ``cls`` or classes are:\"\nmsgstr \"不同的 ``cls`` 或类是:\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:89\nmsgid \"Drupal - ``tigase.db.jdbc.DrupalWPAuth``\"\nmsgstr \"Drupal - ``tigase.db.jdbc.DrupalWPAuth``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:91\nmsgid \"\"\n\"MySQL, Derby, PostgreSQL, MS SQL Server - \"\n\"``tigase.db.jdbc.JDBCRepository``\"\nmsgstr \"\"\n\"MySQL, Derby, PostgreSQL, MS SQL Server - ``tigase.db.jdbc.JDBCRepository``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:93\nmsgid \"\"\n\"You can normally skip configuring connectors for the default Tigase \"\n\"database format: ``mysql``, ``pgsql`` and ``derby``, ``sqlserver`` as \"\n\"they are applied automatically if the parameter is missing.\"\nmsgstr \"\"\n\"您通常可以跳过默认 Tigase 数据库格式的配置连接器：``mysql``, ``pgsql`` 和 ``derby``, \"\n\"``sqlserver`` ，因为如果缺少参数，它们会自动应用。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:95\nmsgid \"\"\n\"One more important thing to know is that you will have to modify \"\n\"``authRepository`` if you use a custom authentication connector. This is \"\n\"because if you retrieve user login data from the external database this \"\n\"external database is usually managed by an external system. User accounts\"\n\" are added without notifying Tigase server. Then, when the user logs in \"\n\"and tries to retrieve the user roster, the server can not find such a \"\n\"user in the roster database.\"\nmsgstr \"\"\n\"要知道的另一件重要的事情是，如果您使用自定义身份验证连接器，则必须修改 ``authRepository`` \"\n\"。这是因为如果您从外部数据库检索用户登录数据，该外部数据库通常由外部系统管理。用户帐户在不通知 Tigase \"\n\"服务器的情况下被添加。然后，当用户登录并尝试检索用户名册时，服务器在名册数据库中找不到这样的用户。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:99\nmsgid \"\"\n\"To keep user accounts in sync between the authentication database and the\"\n\" main user database you have to add following option to the end of the \"\n\"database connection URL: ``autoCreateUser=true``.\"\nmsgstr \"\"\n\"要使用户帐户在身份验证数据库和主用户数据库之间保持同步，您必须在数据库连接 URL \"\n\"的末尾添加以下选项：``autoCreateUser=true``。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:101\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:97\nmsgid \"For example:\"\nmsgstr \"例如：\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:111\nmsgid \"\"\n\"If you are interested in even further customizing your authentication \"\n\"connector by writing your own queries or stored procedures, please have a\"\n\" look at the following guides:\"\nmsgstr \"如果您有兴趣通过编写自己的查询或存储过程来进一步自定义身份验证连接器，请查看以下指南：\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:113\nmsgid \":ref:`Tigase Auth guide<tigaseAuthConnector>`\"\nmsgstr \":ref:`Tigase Auth guide<tigaseAuthConnector>`\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors.inc:115\nmsgid \":ref:`Tigase Custom Auth guide<custonAuthConnector>`\"\nmsgstr \":ref:`Tigase Custom Auth guide<custonAuthConnector>`\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:4\nmsgid \"Tigase Auth Connector (DEPRECATED)\"\nmsgstr \"Tigase 身份验证连接器（已弃用）\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:10\nmsgid \"\"\n\"The Tigase Auth connector with shortcut name: **tigase-auth** is \"\n\"implemented in the class: `tigase.db.jdbc.TigaseAuth \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/db/jdbc/TigaseAuth.java>`__. It \"\n\"allows you to connect to any external database to perform user \"\n\"authentication. You can find more details how to setup a custom connector\"\n\" in the :ref:`Custom Authentication Connectors<customAuthentication>` \"\n\"guide.\"\nmsgstr \"\"\n\"快捷方式名为 **tigase-auth** 的 Tigase Auth 连接器在类中实现：`tigase.db.jdbc.TigaseAuth \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/db/jdbc/TigaseAuth.java>`__ \"\n\"。它允许您连接到任何外部数据库以执行用户身份验证。您可以在 :ref:`Custom Authentication \"\n\"Connectors<customAuthentication>` 指南中找到如何设置自定义连接器的更多详细信息。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:12\nmsgid \"\"\n\"To make this connector working you have to prepare your database to offer\"\n\" set of stored procedures for Tigase server to perform all the \"\n\"authentication actions. The best description is the example schema with \"\n\"all the stored procedures defined - please refer to the Tigase \"\n\"repositories for the schema definition files (each component has it’s \"\n\"dedicated schema). For example:\"\nmsgstr \"\"\n\"要使此连接器正常工作，您必须准备数据库以提供一组存储过程供 Tigase 服务器执行所有身份验证操作。最好的描述是定义了所有存储过程的示例模式 -\"\n\" 请参阅 Tigase 存储库以获取模式定义文件（每个组件都有其专用的模式）。例如：\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:14\nmsgid \"\"\n\"`tigase-server <https://github.com/tigase/tigase-\"\n\"server/tree/master/src/main/database>`__\"\nmsgstr \"\"\n\"`tigase-server <https://github.com/tigase/tigase-server/tree/master/src/main/\"\n\"database>`__\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:16\nmsgid \"\"\n\"`tigase-pubsub <https://github.com/tigase/tigase-\"\n\"pubsub/tree/master/src/main/database>`__\"\nmsgstr \"\"\n\"`tigase-pubsub <https://github.com/tigase/tigase-pubsub/tree/master/src/main/\"\n\"database>`__\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:18\nmsgid \"\"\n\"`tigase-muc <https://github.com/tigase/tigase-\"\n\"muc/tree/master/src/main/database>`__\"\nmsgstr \"\"\n\"`tigase-muc <https://github.com/tigase/tigase-muc/tree/master/src/main/\"\n\"database>`__\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:20\nmsgid \"\"\n\"`tigase-message-archiving <https://github.com/tigase/tigase-message-\"\n\"archiving/tree/master/src/main/database>`__\"\nmsgstr \"\"\n\"`tigase-message-archiving <https://github.com/tigase/\"\n\"tigase-message-archiving/tree/master/src/main/database>`__\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:22\nmsgid \"\"\n\"`tigase-socks5 <https://github.com/tigase/tigase-\"\n\"socks5/tree/master/src/main/database>`__\"\nmsgstr \"\"\n\"`tigase-socks5 <https://github.com/tigase/tigase-socks5/tree/master/src/main/\"\n\"database>`__\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:24\nmsgid \"The absolute minimum of stored procedures you have to implement is:\"\nmsgstr \"您必须实现的存储过程的绝对最小值是：\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:26\nmsgid \"\"\n\"``TigUserLoginPlainPw`` - to perform user authentication. The procedure \"\n\"is always called when the user tries to login to the XMPP server. This is\"\n\" the only procedure which must be implemented and actually must work.\"\nmsgstr \"\"\n\"``TigUserLoginPlainPw`` - 执行用户身份验证。当用户尝试登录 XMPP \"\n\"服务器时，总是会调用该程序。这是唯一必须实施并且必须切实可行的程序。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:28\nmsgid \"\"\n\"``TigUserLogout`` - to perform user logout. The procedure is always \"\n\"called when the user logouts or disconnects from the server. This \"\n\"procedure must be implemented but it can be empty and can do nothing. It \"\n\"just needs to exist because Tigase expect it to exist and attempts to \"\n\"call it.\"\nmsgstr \"\"\n\"``TigUserLogout`` - \"\n\"执行用户注销。当用户注销或与服务器断开连接时，始终调用该程序。这个程序必须执行，但它可以是空的并且什么也不做。它只需要存在，因为 Tigase \"\n\"期望它存在并尝试调用它。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:30\nmsgid \"\"\n\"With these 2 above stored procedures you can only perform user \"\n\"login/logouts on the external database. You can’t register a user \"\n\"account, change user password or remove the user. In many cases this is \"\n\"fine as all the user management is handled by the external system.\"\nmsgstr \"\"\n\"使用上述 2 \"\n\"个存储程序，您只能在外部数据库上执行用户登录/注销。您不能注册用户帐户，更改用户密码或删除用户。在许多情况下，这是可以的，因为所有用户管理都由外部系统处理。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:32\nmsgid \"\"\n\"If you however want to allow for account management via XMPP you have to \"\n\"implement also following procedures:\"\nmsgstr \"但是，如果您想允许通过 XMPP 进行帐户管理，您还必须执行以下程序：\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:34\nmsgid \"``TigAddUserPlainPw`` - to add a new user account\"\nmsgstr \"``TigAddUserPlainPw`` - 添加新用户帐户\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:36\nmsgid \"``TigRemoveUser`` - to remove existing user account\"\nmsgstr \"``TigRemoveUser`` - 删除现有用户帐户\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Tigase_Auth_Connector.inc:38\nmsgid \"\"\n\"``TigUpdatePasswordPlainPw`` - to change a user password for existing \"\n\"account\"\nmsgstr \"``TigUpdatePasswordPlainPw`` - 更改现有帐户的用户密码\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:4\nmsgid \"Tigase Custom Auth Connector\"\nmsgstr \"Tigase 自定义身份验证连接器\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:6\nmsgid \"\"\n\"The Tigase Custom Auth connector with shortcut name: **tigase-custom** is\"\n\" implemented in the class: `tigase.db.jdbc.TigaseCustomAuth \"\n\"<https://github.com/tigase/tigase-\"\n\"server/tree/master/src/main/java/tigase/db/jdbc/TigaseCustomAuth.java>`__.\"\n\" It allows you to connect to any external database to perform user \"\n\"authentication and use a custom queries for all actions.\"\nmsgstr \"\"\n\"具有快捷方式名称的 Tigase 自定义身份验证连接器：**tigase-custom** \"\n\"在类中实现：`tigase.db.jdbc.TigaseCustomAuth <https://github.com/tigase/tigase-\"\n\"server/tree/master/src \"\n\"/main/java/tigase/db/jdbc/TigaseCustomAuth.java>`__。它允许您连接到任何外部数据库来执行用户身份验证并对所有操作使用自定义查询。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:8\nmsgid \"\"\n\"You can find more details how to setup a custom connector in the Custom \"\n\"Authentication Connectors guide.\"\nmsgstr \"您可以在自定义身份验证连接器指南中找到有关如何设置自定义连接器的更多详细信息。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:10\nmsgid \"The basic configuration is very simple:\"\nmsgstr \"基本配置非常简单：\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:21\nmsgid \"That’s it.\"\nmsgstr \"就这些\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:23\nmsgid \"\"\n\"The connector loads correctly and starts working using predefined, \"\n\"default list of queries. In most cases you also might want to define your\"\n\" own queries in the configuration file. The shortest possible description\"\n\" is the following example of the content from the ``config.tdsl`` file:\"\nmsgstr \"\"\n\"连接器正确加载并使用预定义的默认查询列表开始工作。在大多数情况下，您可能还想在配置文件中定义自己的查询。最短的可能描述是来自 \"\n\"``config.tdsl`` 文件中的以下内容示例：\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:25\nmsgid \"\"\n\"This query is used to check connection to the database, whether it is \"\n\"still alive or not\"\nmsgstr \"此查询用于检查与数据库的连接是否还持活\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:35\nmsgid \"\"\n\"This is database initialization query, normally we do not use it, \"\n\"especially in clustered environment\"\nmsgstr \"这是数据库初始化查询，一般我们不用，尤其是集群环境\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:47\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:127\nmsgid \"\"\n\"``online_status`` column does not exist and would need to be added for \"\n\"that query to work.\"\nmsgstr \"``online_status`` 列不存在，需要添加该查询才能工作。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:49\nmsgid \"\"\n\"Below query performs user authentication on the database level. The \"\n\"Tigase server does not need to know authentication algorithm or password \"\n\"encoding type, it simply passes user id (BareJID) and password in form \"\n\"which was received from the client, to the stored procedure. If the \"\n\"authentication was successful the procedure returns user bare JID or null\"\n\" otherwise. Tigase checks whether the JID returned from the query matches\"\n\" JID passed as a parameter. If they match, the authentication is \"\n\"successful.\"\nmsgstr \"\"\n\"下面的查询在数据库级别执行用户身份验证。 Tigase 服务器不需要知道身份验证算法或密码编码类型，它只需将从客户端接收到的用户 ID \"\n\"(BareJID) 和密码传递给存储过程。如果身份验证成功，则该过程返回用户裸 JID，否则返回 null。 Tigase 检查从查询返回的 \"\n\"JID 是否与作为参数传递的 JID 匹配。如果它们匹配，则认证成功。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:62\nmsgid \"\"\n\"``TigUserLoginPlainPw`` is no longer part of a Tigase XMPP Server \"\n\"database schema and would need to be created.\"\nmsgstr \"``TigUserLoginPlainPw`` 不再是 Tigase XMPP 服务器数据库模式的一部分，需要创建。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:64\nmsgid \"\"\n\"Below query returns number of user accounts in the database, this is \"\n\"mainly used for the server metrics and monitoring components.\"\nmsgstr \"下面的查询返回数据库中用户帐户的数量，这主要用于服务器指标和监控组件。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:74\nmsgid \"The Below query is used to add a new user account to the database.\"\nmsgstr \"下面的查询用于向数据库添加新的用户帐户。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:84\nmsgid \"\"\n\"Below query is used to remove existing account with all user’s data from \"\n\"the database.\"\nmsgstr \"下面的查询用于从数据库中删除包含所有用户数据的现有帐户。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:94\nmsgid \"\"\n\"This query is used for the user authentication if ``user-login-query`` is\"\n\" not defined, that is if there is no database level user authentication \"\n\"algorithm available. In such a case the Tigase server loads user’s \"\n\"password from the database and compares it with data received from the \"\n\"client.\"\nmsgstr \"\"\n\"如果未定义 ``user-login-query``，则此查询用于用户身份验证，即没有可用的数据库级用户身份验证算法。在这种情况下，Tigase \"\n\"服务器从数据库中加载用户密码，并将其与从客户端接收到的数据进行比较。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:104\nmsgid \"\"\n\"Below query is used for user password update in case user decides to \"\n\"change his password.\"\nmsgstr \"以下查询用于用户密码更新，以防用户决定更改密码。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:114\nmsgid \"\"\n\"This query is called on user logout event. Usually we use a stored \"\n\"procedure which records user logout time and marks user as offline in the\"\n\" database.\"\nmsgstr \"此查询在用户注销时被调用。通常我们使用一个存储过程来记录用户注销时间并在数据库中将用户标记为离线。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:129\nmsgid \"\"\n\"This configuration specifies what non-sasl authentication mechanisms to \"\n\"expose to the client\"\nmsgstr \"此配置指定向客户端公开哪些非 SASL 身份验证机制\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:139\nmsgid \"\"\n\"This setting to specify what sasl authentication mechanisms expose to the\"\n\" client\"\nmsgstr \"此设置指定向客户端公开哪些 sasl 身份验证机制\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:149\nmsgid \"\"\n\"Queries are defined in the configuration file and they can be either \"\n\"plain SQL queries or stored procedures. If the query starts with \"\n\"characters: ``{ call`` then the server assumes this is a stored procedure\"\n\" call, otherwise it is executed as a plain SQL query. Each configuration \"\n\"value is stripped from white characters on both ends before processing.\"\nmsgstr \"\"\n\"查询在配置文件中定义，它们可以是普通的 SQL 查询或存储过程。如果查询以字符 ``{ call`` 开头， \"\n\"则服务器假定这是一个存储过程的调用，否则它作为普通 SQL 查询执行。在处理之前，每个配置值都从两端的白色字符中去除。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:151\nmsgid \"\"\n\"Please don’t use semicolon ``;`` at the end of the query as many JDBC \"\n\"drivers get confused and the query may not work.\"\nmsgstr \"请不要在查询末尾使用分号 ``;``，因为许多 JDBC 驱动程序会混淆，查询可能无法正常工作。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:153\nmsgid \"\"\n\"Some queries can take arguments. Arguments are marked by question marks \"\n\"``?`` in the query. Refer to the configuration parameters description for\"\n\" more details about what parameters are expected in each query.\"\nmsgstr \"一些查询可以带参数。参数在查询中用问号 ``?`` 标记。有关每个查询中预期的参数的更多详细信息，请参阅配置参数说明。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:155\nmsgid \"\"\n\"This example shows how to use a stored procedure to add a user as a query\"\n\" with 2 required parameters (username, and password).\"\nmsgstr \"此示例说明如何使用存储过程将用户添加为具有 2 个必需参数（用户名和密码）的查询。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:165\nmsgid \"The same query with plain SQL parameters instead:\"\nmsgstr \"使用普通 SQL 参数的相同查询：\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:171\nmsgid \"\"\n\"The order of the query arguments is important and must be exactly as \"\n\"described in specification for each parameter.\"\nmsgstr \"查询参数的顺序很重要，并且必须与每个参数的规范中描述的完全相同。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:174\nmsgid \"Query Name\"\nmsgstr \"查询名称\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:174\nmsgid \"Description\"\nmsgstr \"描述\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:174\nmsgid \"Arguments\"\nmsgstr \"参数\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:174\nmsgid \"Example Query\"\nmsgstr \"示例查询\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:176\nmsgid \"``conn-valid-query``\"\nmsgstr \"``conn-valid-query``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:176\nmsgid \"Query executed periodically to ensure active connection with the database.\"\nmsgstr \"定期执行查询以确保与数据库保持连接。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:176\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:178\nmsgid \"Takes no arguments.\"\nmsgstr \"不接受任何参数。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:176\nmsgid \"``select 1``\"\nmsgstr \"``select 1``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:178\nmsgid \"``init-db-query``\"\nmsgstr \"``init-db-query``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:178\nmsgid \"Database initialization query which is run after the server is started.\"\nmsgstr \"服务器启动后运行的数据库初始化查询。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:178\nmsgid \"``update tig_users set online_status = 0``\"\nmsgstr \"``update tig_users set online_status = 0``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:180\nmsgid \"``add-user-query``\"\nmsgstr \"``add-user-query``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:180\nmsgid \"Query adding a new user to the database.\"\nmsgstr \"向数据库添加新用户查询。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:180\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:188\nmsgid \"Takes 2 arguments: ``(user_id (JID), password)``\"\nmsgstr \"接受 2 个参数： ``(user_id (JID), password)``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:180\nmsgid \"``insert into tig_users (user_id, user_pw) values (?, ?)``\"\nmsgstr \"``insert into tig_users (user_id, user_pw) values (?, ?)``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:182\nmsgid \"``del-user-query``\"\nmsgstr \"``del-user-query``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:182\nmsgid \"Removes a user from the database.\"\nmsgstr \"从数据库中删除用户。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:182\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:184\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:190\nmsgid \"Takes 1 argument: ``(user_id (JID))``\"\nmsgstr \"接受 1 个参数： ``(user_id (JID))``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:182\nmsgid \"``delete from tig_users where user_id = ?``\"\nmsgstr \"``delete from tig_users where user_id = ?``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:184\nmsgid \"``get-password-query``\"\nmsgstr \"``get-password-query``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:184\nmsgid \"Retrieves user password from the database for given user_id (JID).\"\nmsgstr \"从数据库中找回给定 user_id (JID) 的用户密码。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:184\nmsgid \"``select user_pw from tig_users where user_id = ?``\"\nmsgstr \"``select user_pw from tig_users where user_id = ?``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:186\nmsgid \"``update-password-query``\"\nmsgstr \"``update-password-query``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:186\nmsgid \"Updates (changes) password for a given user_id (JID).\"\nmsgstr \"更新（更改）给定 user_id (JID) 的密码。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:186\nmsgid \"Takes 2 arguments: ``(password, user_id (JID))``\"\nmsgstr \"接受 2 个参数： ``(password, user_id (JID))``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:186\nmsgid \"``update tig_users set user_pw = ? where user_id = ?``\"\nmsgstr \"``update tig_users set user_pw = ? where user_id = ?``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:188\nmsgid \"``user-login-query``\"\nmsgstr \"``user-login-query``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:188\nmsgid \"\"\n\"Performs user login. Normally used when there is a special SP used for \"\n\"this purpose. This is an alternative way to a method requiring retrieving\"\n\" user password. Therefore at least one of those queries must be defined: \"\n\"``user-login-query`` or ``get-password-query``. If both queries are \"\n\"defined then ``user-login-query`` is used. Normally this method should be\"\n\" only used with plain text password authentication or sasl-plain. Tigase \"\n\"expects a result set with user_id to be returned from the query if login \"\n\"is successful and empty results set if the login is unsuccessful.\"\nmsgstr \"\"\n\"执行用户登录。通常在有用于此目的的特殊 SP 时使用。这是需要找回用户密码的替代方法。因此，必须至少定义其中一个查询：``user-login-\"\n\"query`` 或 ``get-password-query``。如果定义了两个查询，则使用 ``user-login-\"\n\"query``。通常，此方法应仅与纯文本密码身份验证或 sasl-plain 一起使用。如果登录成功，Tigase 期望从查询返回一个带有 \"\n\"user_id 的结果集，如果登录不成功，则返回空结果集。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:188\nmsgid \"``select user_id from tig_users where (user_id = ?) AND (user_pw = ?)``\"\nmsgstr \"``select user_id from tig_users where (user_id = ?) AND (user_pw = ?)``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:190\nmsgid \"``user-logout-query``\"\nmsgstr \"``user-logout-query``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:190\nmsgid \"\"\n\"This query is called when user logs out or disconnects. It can record \"\n\"that event in the database.\"\nmsgstr \"当用户注销或断开连接时调用此查询。它可以在数据库中记录该事件。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:190\nmsgid \"\"\n\"``update tig_users, set online_status = online_status - 1 where user_id =\"\n\" ?``\"\nmsgstr \"\"\n\"``update tig_users, set online_status = online_status - 1 where user_id = ?``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:192\nmsgid \"``non-sasl-mechs``\"\nmsgstr \"``non-sasl-mechs``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:192\nmsgid \"\"\n\"Comma separated list of NON-SASL authentication mechanisms. Possible \"\n\"mechanisms are: ``password`` and ``digest``. The digest mechanism can \"\n\"work only with ``get-password-query`` active and only when password are \"\n\"stored in plain text format in the database.\"\nmsgstr \"\"\n\"逗号分隔的非 SASL 身份验证机制列表。可能的机制是：``password`` 和 ``digest``。摘要机制只能在 ``get-\"\n\"password-query`` 激活且密码以纯文本格式存储在数据库中时起作用。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:194\nmsgid \"``sasl-mechs``\"\nmsgstr \"``sasl-mechs``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Custom_Auth_Connector.inc:194\nmsgid \"\"\n\"Comma separated list of SASL authentication mechanisms. Possible \"\n\"mechanisms are all mechanisms supported by Java implementation. The most \"\n\"common are: ``PLAIN``, ``DIGEST-MD5``, ``CRAM-MD5``. \\\"Non-PLAIN\\\" \"\n\"mechanisms will work only with the ``get-password-query`` active and only\"\n\" when passwords are stored in plain text format in the database.\"\nmsgstr \"\"\n\"逗号分隔的 SASL 身份验证机制列表。可能的机制都是 Java 实现支持的机制。最常见的是：``PLAIN``, ``DIGEST-MD5``,\"\n\" ``CRAM-MD5``。\\\"Non-PLAIN\\\" 机制仅在 ``get-password-query`` \"\n\"激活且密码以纯文本格式存储在数据库中时才有效。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Drupal_Auth.inc:2\nmsgid \"Drupal Authentication\"\nmsgstr \"Drupal 身份验证\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Drupal_Auth.inc:4\nmsgid \"\"\n\"Currently, we can only check authentication against a **Drupal** database\"\n\" at the moment. Full **Drupal** authentication is not implemented as of \"\n\"yet.\"\nmsgstr \"目前，我们目前只能针对 **Drupal** 数据库检查身份验证。完整的 **Drupal** 身份验证尚未被实现。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Drupal_Auth.inc:6\nmsgid \"\"\n\"As **Drupal** keeps encrypted passwords in database the only possible \"\n\"authorization protocols are those based on PLAIN passwords.\"\nmsgstr \"由于 **Drupal** 将加密密码保存在数据库中，唯一可能的授权协议是那些基于 PLAIN 密码的协议。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Drupal_Auth.inc:8\nmsgid \"\"\n\"To protect your passwords **Tigase** server must be used with SSL or TLS \"\n\"encryption.\"\nmsgstr \"为了保护您的密码 **Tigase** 服务器必须使用 SSL 或 TLS 加密。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Drupal_Auth.inc:10\nmsgid \"\"\n\"Implementation of a **Drupal** database based authorization is located in\"\n\" ``tigase.db.jdbc.DrupalAuth`` class. Although this class is capable of \"\n\"adding new users to the repository I recommend to switch in-band \"\n\"registration off due to the caching problems in **Drupal.** Changes in \"\n\"database are not synchronized with **Drupal** yet. Functionality for \"\n\"adding new users is implemented only to ease user accounts migration from\"\n\" different repository types from earlier **Tigase** server installations.\"\nmsgstr \"\"\n\"**Drupal** 基于数据库的授权的实现位于 ``tigase.db.jdbc.DrupalAuth`` \"\n\"类中。尽管此类能够向存储库添加新用户，但由于 **Drupal** 中的缓存问题，我建议关闭带内注册。数据库中的更改尚未与 **Drupal** \"\n\"同步。添加新用户的功能仅用于简化从早期 **Tigase** 服务器安装的不同存储库类型迁移用户帐户。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Drupal_Auth.inc:12\nmsgid \"\"\n\"The purpose of that implementation was to allow all accounts \"\n\"administration tasks from **Drupal** like: account creation, all accounts\"\n\" settings, like e-mail, full name, password changes and so on.\"\nmsgstr \"该实现的目的是允许来自 **Drupal** 的所有帐户管理任务，例如：帐户创建，所有帐户设置，比如电子邮件，全名，密码更改等。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Drupal_Auth.inc:14\nmsgid \"\"\n\"**Tigase** server uses following fields from **Drupal** database: name \"\n\"(user account name), pass (user account password), status (status of the \"\n\"account). Server picks up all changes instantly. If user status is not 1 \"\n\"then server won’t allow user to login trough XMPP even if user provides \"\n\"valid password.\"\nmsgstr \"\"\n\"**Tigase** 服务器使用来自 **Drupal** \"\n\"数据库的以下字段：name（用户帐户名），pass（用户帐户密码），status（帐户状态）。服务器立即获取所有更改。如果用户状态不是 \"\n\"1，那么即使用户提供了有效的密码，服务器也不会允许用户通过 XMPP 登录。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/Drupal_Auth.inc:16\nmsgid \"\"\n\"There is no *Roster* management in **Drupal** yet. So Roster management \"\n\"have to be done from the XMPP client.\"\nmsgstr \"**Drupal** 中还没有 *Roster* 管理。所以名册管理必须从 XMPP 客户端完成。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/LDAP_Auth.inc:2\nmsgid \"LDAP Authentication Connector\"\nmsgstr \"LDAP 身份验证连接器\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/LDAP_Auth.inc:4\nmsgid \"\"\n\"Tigase XMPP Server offers support for authenticating users against an \"\n\"LDAP server in **Bind** **Authentication** mode.\"\nmsgstr \"Tigase XMPP 服务器支持在 **Bind** **Authentication** 模式下针对 LDAP 服务器对用户进行身份验证。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/LDAP_Auth.inc:6\nmsgid \"\"\n\"Configuration for the LDAP support is really simple you just have to add \"\n\"a few lines to your ``config.tdsl`` file.\"\nmsgstr \"LDAP 支持的配置非常简单，您只需在 ``config.tdsl`` 文件中添加几行即可。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/LDAP_Auth.inc:18\nmsgid \"\"\n\"Please note the ``USER_ID`` element, this is a special element of the \"\n\"configuration which is used to authenticate particular user. Tigase LDAP \"\n\"connector replaces it with appropriate data during authentication. You \"\n\"can control what Tigase should put into this part. In your configuration \"\n\"you must replace this string with one of the following:\"\nmsgstr \"\"\n\"请注意 ``USER_ID`` 元素，这是用于验证特定用户配置的特殊元素。 Tigase LDAP \"\n\"连接器在身份验证期间将其替换为适当的数据。您可以控制 Tigase 应该在这部分中添加什么内容。在您的配置中，您必须将此字符串替换为以下之一：\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/LDAP_Auth.inc:20\nmsgid \"``%1$s`` - use user name only for authentication (JabberID’s localpart)\"\nmsgstr \"``%1$s`` - 仅使用用户名进行身份验证（JabberID 的本地部分）\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/LDAP_Auth.inc:22\nmsgid \"\"\n\"``%2$s`` - use domain name only for authentication (JabberID’s domain \"\n\"part)\"\nmsgstr \"``%2$s`` - 仅使用域名进行身份验证（JabberID 的域部分）\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/LDAP_Auth.inc:24\nmsgid \"``%3$s`` - use the whole Jabber ID (JID) for authentication\"\nmsgstr \"``%3$s`` - 使用整个 Jabber ID (JID) 进行身份验证\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/LDAP_Auth.inc:28\nmsgid \"\"\n\"Please make sure that you included ``autoCreateUser=true`` in your main \"\n\"data source (UserRepository and **not** above AuthRepository) as outlined\"\n\" in `??? <#autoCreateUser>`__ - otherwise you may run into problems with \"\n\"data access.\"\nmsgstr \"\"\n\"请确保您在主数据源（UserRepository 和 **不是** 以上 AuthRepository）中包含 \"\n\"``autoCreateUser=true``，如 `[autoCreateUser] <#autoCreateUser>`__ - \"\n\"否则您可能会遇到数据访问问题。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/SASL_EXTERNAL.inc:2\nmsgid \"Configuration of SASL EXTERNAL\"\nmsgstr \"SASL 外部配置\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/SASL_EXTERNAL.inc:4\nmsgid \"\"\n\"In order to enable SASL External set \\\"Client Certificate CA\\\" (``client-\"\n\"trust-extension-ca-cert-path``) to the path containing Certification \"\n\"Authority (CA) certificate in the VHost (domain) configuration, for \"\n\"example ``/path/to/cacert.pem``\"\nmsgstr \"\"\n\"为了启用 SASL 外部设置 \\\"Client Certificate CA\\\" (``client-trust-extension-ca-\"\n\"cert-path``) 到包含 VHost（域）配置中的证书颁发机构 (CA) 证书的路径，例如 ``/path/to/cacert.pem``\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/SASL_EXTERNAL.inc:6\nmsgid \"\"\n\"File ``cacert.pem`` contains Certificate Authority certificate which is \"\n\"used to sign clients certificate.\"\nmsgstr \"文件 ``cacert.pem`` 包含用于签署客户端证书的证书颁发机构证书。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/SASL_EXTERNAL.inc:8\nmsgid \"\"\n\"Client certificate must include user’s Jabber ID as ``XmppAddr`` in \"\n\"``subjectAltName``:\"\nmsgstr \"客户端证书必须在 ``subjectAltName`` 中包含用户作为 ``XmppAddr`` 的 Jabber ID ：\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/SASL_EXTERNAL.inc:10\nmsgid \"\"\n\"As specified in RFC 3920 and updated in RFC 6120, during the stream \"\n\"negotiation process an XMPP client can present a certificate (a “client \"\n\"certificate”). If a JabberID is included in a client certificate, it is \"\n\"encapsulated as an id-on-xmppAddr Object Identifier (“xmppAddr”), i.e., a\"\n\" subjectAltName entry of type otherName with an ASN.1 Object Identifier \"\n\"of “id-on-xmppAddr” as specified in Section 13.7.1.4 of RFC 6120, \"\n\"`XEP-0178 <http://xmpp.org/extensions/xep-0178.html#c2s>`__.\"\nmsgstr \"\"\n\"正如 RFC 3920 中指定和在 RFC 6120 中更新的那样，在流协商过程中，XMPP 客户端可以提供证书（“client \"\n\"certificate”）。如果 JabberID 包含在客户端证书中，则将其封装为 id-on-xmppAddr \"\n\"对象标识符（“xmppAddr”），即 otherName 类型的 subjectAltName 条目，其 ASN.1 对象标识符为“id-on-\"\n\"xmppAddr”，正如 RFC 6120 的第 13.7.1.4 节，`XEP-0178 \"\n\"<http://xmpp.org/extensions/xep-0178.html#c2s>`__ 中所述。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/SASL_EXTERNAL.inc:12\nmsgid \"\"\n\"It is possible to make client certificate **required** using same VHost \"\n\"configuration and enabling option ``Client Certificate Required`` \"\n\"(``client-trust-extension-cert-required``).\"\nmsgstr \"\"\n\"可以使制作客户端证书 **必需** 使用相同的 VHost 配置并启用选项 ``Client Certificate Required`` \"\n\"(``client-trust-extension-cert-required``)。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/SASL_EXTERNAL.inc:14\nmsgid \"\"\n\"If this option will be enabled, then client **must provide** certificate.\"\n\" This certificate will be verified against ``clientCertCA``. If client \"\n\"does not provide certificate or certificate will be invalid, **TLS \"\n\"handshake will be interrupted and client will be disconnected**.\"\nmsgstr \"\"\n\"如果启用此选项，则客户端 **必须提供** 证书。该证书将根据 ``clientCertCA`` \"\n\"进行验证。如果客户端不提供证书或证书无效，**TLS 握手将被中断并将客户端连接断开**。\"\n\n#: ../../Tigase_Administration/Security/Auth_Connectors/SASL_EXTERNAL.inc:16\nmsgid \"\"\n\"Using this options does not force client to use SASL EXTERNAL. Client \"\n\"still may authenticate with other SASL mechanisms.\"\nmsgstr \"使用此选项不会强制客户端使用 SASL EXTERNAL。客户端仍然可以使用其他 SASL 机制进行身份验证。\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:2\nmsgid \"SASL Mechanisms\"\nmsgstr \"SASL 机制\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:4\nmsgid \"\"\n\"XMPP protocol supports many authentication methods, but most of them are \"\n\"used as `SASL <https://tools.ietf.org/html/rfc4422>`__ mechanisms. Tigase\"\n\" XMPP Server provides many SASL-based authentication mechanisms such as:\"\nmsgstr \"\"\n\"XMPP 协议支持多种身份验证方式，但大多用作 `SASL <https://tools.ietf.org/html/rfc4422>`__ \"\n\"机制。 Tigase XMPP Server 提供了许多基于 SASL 的身份验证机制，例如：\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:6\nmsgid \"PLAIN *(enabled)*\"\nmsgstr \"PLAIN *(启用)*\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:8\nmsgid \"ANONYMOUS\"\nmsgstr \"ANONYMOUS\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:10\nmsgid \"EXTERNAL\"\nmsgstr \"EXTERNAL\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:12\nmsgid \"SCRAM-SHA-1 *(enabled)*\"\nmsgstr \"SCRAM-SHA-1 *(启用)*\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:14\nmsgid \"SCRAM-SHA-256 *(enabled)*\"\nmsgstr \"SCRAM-SHA-256 *(启用)*\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:16\nmsgid \"SCRAM-SHA-512\"\nmsgstr \"SCRAM-SHA-512\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:18\nmsgid \"\"\n\"Most of them are enabled by default on default Tigase XMPP Server \"\n\"installation.\"\nmsgstr \"它们中的大多数在默认的 Tigase XMPP 服务器安装中默认启用。\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:21\nmsgid \"Enabling and disabling SASL mechanisms (credentials encoder/decoder)\"\nmsgstr \"启用和禁用 SASL 机制（凭证编码器/解码器）\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:23\nmsgid \"\"\n\"If you want to enable or disable one of password-based authentication \"\n\"mechanism such as ``SCRAM-SHA-1``, ``SCRAM-SHA-256`` or ``SCRAM-SHA-512``\"\n\" you can do that by enabling or disabling encoders and decoders used on \"\n\"your installation. By enabling encoders/decoders you are deciding in what\"\n\" form the password is stored in the database. Those changes may (and in \"\n\"most cases will) impact which SASL mechanisms may be allowed to use on \"\n\"your installation.\"\nmsgstr \"\"\n\"如果您想启用或禁用基于密码的身份验证机制之一，例如 ``SCRAM-SHA-1``, ``SCRAM-SHA-256`` 或 ``SCRAM-\"\n\"SHA-512``，您可以通过启用或禁用安装中使用的编码器和解码器。通过启用编码器/解码器，您可以决定密码以何种形式存储在数据库中。这些更改可能（并且在大多数情况下）会影响哪些\"\n\" SASL 机制允许在您的安装中使用。\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:27\nmsgid \"\"\n\"In most cases you should enable or disable both (credentials encoder and \"\n\"decoder) of the same type at the same time. The only exception of this \"\n\"rule is when you are changing those on already working installation. In \"\n\"this case you should only enable encoder of the type which you want to \"\n\"enable and request users to change their passwords. Then, after users \"\n\"will change their passwords, you should reconfigure server to enable \"\n\"decoder of the particular type. *(in other case user may loose a way to \"\n\"log in to your installation as system will reject their credentials as it\"\n\" may not have matching credentials for particular SASL mechanism)*.\"\nmsgstr \"\"\n\"在大多数情况下，您应该同时启用或禁用这两种相同类型（凭证编码器和解码器）。此规则的唯一例外是当您更改那些已经工作的安装时。在这种情况下，您应该只启用您想要启用的类型的编码器并要求用户更改他们的密码。在用户更改密码后，您应该重新配置服务器以启用特定类型的解码器。\"\n\" *（在其他情况下，用户可能无法登录到您的安装，因为系统将拒绝他们的凭据，因为它可能没有与特定 SASL 机制匹配的凭据）*。\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:30\nmsgid \"**Enabling SCRAM-SHA-512 encoder**\"\nmsgstr \"**启用 SCRAM-SHA-512 编码器**\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:43\nmsgid \"**Disabling SCRAM-SHA-1 decoder**\"\nmsgstr \"**禁用 SCRAM-SHA-1 解码器**\"\n\n#: ../../Tigase_Administration/Security/SASL_Mechanisms.inc:56\nmsgid \"\"\n\"It is strongly recommended not to disable encoders if you have enabled \"\n\"decoder of the same type as it may lead to the authentication issues, if \"\n\"client tries to use a mechanism which that is not available.\"\nmsgstr \"如果您启用了相同类型的解码器，强烈建议不要禁用编码器，因为如果客户端尝试使用不可用的机制，可能会导致身份验证问题。\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:2\nmsgid \"Application passwords\"\nmsgstr \"应用程序密码\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:4\nmsgid \"\"\n\"In recent versions of Tigase XMPP Server it is possible to create and use\"\n\" multiple username and password pairs to authorize connection to the \"\n\"single XMPP account.\"\nmsgstr \"在最新版本的 Tigase XMPP 服务器中，可以创建和使用多个用户名和密码对来授权连接到单个 XMPP 帐户。\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:6\nmsgid \"\"\n\"With that in place it is now possible to have multiple password for a \"\n\"multiple clients accessing the same account what can be used to increase \"\n\"security of the account as even if one password compromised you can still\"\n\" log in and block lost or compromised device.\"\nmsgstr \"有了这个，现在可以为访问同一帐户的多个客户端设置多个密码，这可用于提高帐户的安全性，因为即使一个密码被泄露，您仍然可以登录并阻止丢失或受损的设备。\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:9\nmsgid \"Adding application password\"\nmsgstr \"添加应用程序密码\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:11\nmsgid \"\"\n\"To add new username-password pair you need to execute ``Add user \"\n\"credentials`` ad-hoc command (command node ``auth-credentials-add`` at \"\n\"``sess-man``) while logged in the XMPP account for which you want to add \"\n\"a new application password.\"\nmsgstr \"\"\n\"要添加新的用户名-密码对，您需要在登录XMPP 帐户并为其添加新应用程序密码时执行 ``Add user credentials`` 临时命令（ \"\n\"``sess-man`` 的命令节点 ``auth-credentials-delete``）。\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:13\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:41\nmsgid \"\"\n\"During execution for a command you will be provided with a form to fill \"\n\"in with following fields:\"\nmsgstr \"在执行命令期间，您将获得一个表格，用于填写以下字段：\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:15\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:43\nmsgid \"The Jabber ID for the account (``jid``) - bare JID of your account\"\nmsgstr \"帐户的 Jabber ID (``jid``) - 您帐户的裸 JID\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:17\nmsgid \"\"\n\"Credential ID (``credentialId``) - username for the new application \"\n\"password\"\nmsgstr \"凭据 ID (``credentialId``) - 新应用程序密码的用户名\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:19\nmsgid \"Password (``password``) - a new password\"\nmsgstr \"密码 (``password``) - 新密码\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:21\nmsgid \"After submitting this form a new credential will be added.\"\nmsgstr \"提交此表格后，将添加新的凭据。\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:24\nmsgid \"Login in with application password\"\nmsgstr \"使用应用程序密码登录\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:26\nmsgid \"\"\n\"To log in with new password the XMPP client can use any SASL mechanism \"\n\"but it needs to provide (in SASL message):\"\nmsgstr \"要使用新密码登录，XMPP 客户端可以使用任何 SASL 机制，但需要提供（在 SASL 消息中）：\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:28\nmsgid \"``authzid`` - account JID\"\nmsgstr \"``authzid`` - 帐号 JID\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:30\nmsgid \"``authcid`` - username for application password\"\nmsgstr \"``authcid`` - 应用程序密码的用户名\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:32\nmsgid \"``passwd`` - application password\"\nmsgstr \"``passwd`` - 申请密码\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:34\nmsgid \"\"\n\"With proper values, you application will be able to log in using \"\n\"application password.\"\nmsgstr \"使用正确的值，您的应用程序将能够用应用程序密码登录。\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:37\nmsgid \"Removing application password\"\nmsgstr \"删除应用程序密码\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:39\nmsgid \"\"\n\"If your device is compromised or lost and you want to remove application \"\n\"password, you need to use a different device and log in on your XMPP \"\n\"account. Then you need to execute ``Delete user credentials`` ad-hoc \"\n\"command (command node ``auth-credentials-delete`` at ``sess-man``).\"\nmsgstr \"\"\n\"如果您的设备被盗用或丢失并且您想删除应用程序密码，您需要使用其他不同设备并登录您的 XMPP 帐户。然后你需要执行 ``Delete user \"\n\"credentials`` 临时命令（``sess-man`` 的命令节点 ``auth-credentials-delete``）。\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:45\nmsgid \"\"\n\"Credential ID (``credentialId``) - username for the application password \"\n\"which you want to remove\"\nmsgstr \"凭据 ID (``credentialId``) - 要删除的应用程序密码的用户名\"\n\n#: ../../Tigase_Administration/Security/Application_Passwords.inc:47\nmsgid \"After submitting this form a credential will be removed.\"\nmsgstr \"提交此表格后，证书将被删除。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:2\nmsgid \"Packet Filtering\"\nmsgstr \"包过滤\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:4\nmsgid \"\"\n\"Tigase offers different ways to filter XMPP packets flying through the \"\n\"server. The most common use for packet filtering is to restrict users \"\n\"from sending or receiving packets based on the sender or received \"\n\"address.\"\nmsgstr \"Tigase 提供了不同的方法来过滤通过服务器的 XMPP 数据包。包过滤最常见的用途是根据发送者或接收地址限制用户发送或接收数据包。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:6\nmsgid \"\"\n\"There are also different possible scenarios: time based filtering, \"\n\"content filtering, volume filtering and so on.\"\nmsgstr \"还有不同的可能场景：基于时间的过滤，内容过滤，音量过滤等。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:8\nmsgid \"All pages in this section describe different filtering strategies.\"\nmsgstr \"本节中的所有页面都描述了不同的过滤策略。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:11\nmsgid \"Domain Based Packet Filtering\"\nmsgstr \"基于域的包过滤\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:13\nmsgid \"\"\n\"Domain based packet filtering is a simple filter allowing to restrict \"\n\"user communication based on the source/destination domain name. This is \"\n\"especially useful if we want to limit user communication within a single \"\n\"- own domain only or a list of domains.\"\nmsgstr \"基于域的数据包过滤是一个简单的过滤器，其允许根据源/目标域名限制用户通信。如果我们希望仅在单个自己的域或域列表中限制用户通信，这将特别有用。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:15\nmsgid \"\"\n\"A company might not wish to allow employers to chat during work hours \"\n\"with anybody in the world. A company may also have a few different \"\n\"domains used by different branches or departments. An administrator may \"\n\"restrict communication to a list of domains.\"\nmsgstr \"公司可能不希望雇主在工作时间与世界上任何人聊天。一家公司也可能有几个不同的域被不同的分支机构或部门使用。管理员可以将通信限制为域列表。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:18\nmsgid \"Introduction\"\nmsgstr \"介绍\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:20\nmsgid \"\"\n\"The restriction is on a per-user basis. So the administrator can set a \"\n\"different filtering rules for each user. There is also a per-domain \"\n\"configuration and global-installation setting (applied from most general \"\n\"to most specific, i.e. from installation to user).\"\nmsgstr \"限制是基于每个用户的。因此管理员可以为每个用户设置不同的过滤规则。还有一个按域配置和全局安装设置（从最一般到最具体的应用，即从安装到用户）。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:22\nmsgid \"\"\n\"Regular users can not change the settings. So this is not like a privacy \"\n\"list where the user control the filter. Domain filter can not be changed \"\n\"or controlled by the user. The system administrator can change the \"\n\"settings based on the company policy.\"\nmsgstr \"普通用户无法更改设置。所以这不像用户控制过滤器的隐私列表。域过滤器不能由用户更改或控制。系统管理员可以根据公司政策更改设置。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:24\nmsgid \"There are predefined rules for packet filtering:\"\nmsgstr \"包过滤有预定义的规则：\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:26\nmsgid \"``ALL`` - user can send and receive packets from anybody.\"\nmsgstr \"``ALL`` - 用户可以发送和接收来自任何人的数据包。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:28\nmsgid \"\"\n\"``LOCAL`` - user can send and receive packets within the server \"\n\"installation only and all it’s virtual domains.\"\nmsgstr \"``LOCAL`` - 用户只能在服务器安装及其所有虚拟域内发送和接收数据包。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:30\nmsgid \"``OWN`` - user can send and receive packets within his own domains only\"\nmsgstr \"``OWN`` - 用户只能在自己的域内发送和接收数据包\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:32\nmsgid \"\"\n\"``BLOCK`` - user can’t communicate with anyone. This could be used as a \"\n\"means to temporarily disable account or domain.\"\nmsgstr \"``BLOCK`` - 用户无法与任何人交流。这可以用作暂时禁用帐户或域的一种手段。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:34\nmsgid \"\"\n\"``LIST`` - user can send and receive packets within listed domains only \"\n\"(i.e. *whitelist*).\"\nmsgstr \"``LIST`` - 用户只能在列出的域内发送和接收数据包（即 *白名单*）。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:36\nmsgid \"\"\n\"``BLACKLIST`` - user can communicate with everybody (like ``ALL``), \"\n\"except contacts on listed domains.\"\nmsgstr \"``BLACKLIST`` - 用户可以与所有人（如 ``ALL``）通信，除了列出域上的联系人。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:38\nmsgid \"``CUSTOM`` - user can communicate only within custom created rules set.\"\nmsgstr \"``CUSTOM`` - 用户只能在自定义创建的规则集中进行通信。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:40\nmsgid \"\"\n\"Whitelist (``LIST``) and blacklist (``BLACKLIST``) settings are mutually \"\n\"exclusive, i.e. at any given point of time only one of them can be used.\"\nmsgstr \"白名单 ( ``LIST``) 和黑名单 ( ``BLACKLIST``) 设置是互斥的，即在任何给定时间点，只能使用其中一个。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:42\nmsgid \"\"\n\"Those rules applicable to particular users are stored in the user \"\n\"repository and are loaded for each user session. If there are no rules \"\n\"stored for a particular user server tries to apply rules for a VHost of \"\n\"particular user, and if there is no VHost filtering policy server uses \"\n\"global server configuration. If there is no filtering policy altogether \"\n\"server applies defaults based on following criteria:\"\nmsgstr \"\"\n\"那些适用于特定用户的规则存储在用户存储库中，并为每个用户会话加载。如果没有为特定用户存储规则，则服务器尝试为特定用户的 VHost \"\n\"应用规则，并且如果没有 VHost 过滤策略，服务器使用全局服务器配置。如果完全没有过滤策略，服务器将根据以下条件应用默认值：\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:44\nmsgid \"If this is **Anonymous** user then ``LOCAL`` rule is applied\"\nmsgstr \"如果这是 **Anonymous** 用户，则应用 ``LOCAL`` 规则\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:46\nmsgid \"For all **other** users ``ALL`` rule is applied.\"\nmsgstr \"对于所有 **other** 用户，应用 ``ALL`` 规则。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:51\nmsgid \"\"\n\"Filtering is performed by the domain filter plugin which must be loaded \"\n\"at startup time. It is loaded by default if the plugins list is not set \"\n\"in the configuration file. However if you have a list of loaded plugins \"\n\"in the configuration file make sure ``domain-filter`` is on the list.\"\nmsgstr \"\"\n\"过滤由必须在启动时加载的域过滤器插件执行。如果配置文件中未设置插件列表，则默认加载。但是，如果您在配置文件中有加载插件的列表，请确保 \"\n\"``domain-filter`` 在列表中。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:53\nmsgid \"There is no other configuration required for the plugin to work.\"\nmsgstr \"该插件无需其他配置即可工作。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:56\nmsgid \"Administration, Rules Management\"\nmsgstr \"行政，规则管理\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:58\nmsgid \"\"\n\"Although controlling domain filtering rules is possible for each user \"\n\"separately, it is not practical for large installations. In most cases \"\n\"users are stored in the database and a third-party system keeps all the \"\n\"user information.\"\nmsgstr \"尽管可以为每个用户单独控制域过滤规则，但对于大型安装来说并不实用。在大多数情况下，用户存储在数据库中，第三方系统保存所有用户信息。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:60\nmsgid \"\"\n\"To change the rule for a single user you can use loadable administration \"\n\"scripts feature and load `UserDomainFilter.groovy \"\n\"<https://github.com/tigase/tigase-\"\n\"server/tree/master/src/main/groovy/tigase/admin/UserDomainFilter.groovy>`__\"\n\" script. It enables modifying rules for a given user JID.\"\nmsgstr \"\"\n\"要更改单个用户的规则，您可以使用可加载的管理脚本功能并加载 `UserDomainFilter.groovy \"\n\"<https://github.com/tigase/tigase-\"\n\"server/tree/master/src/main/groovy/tigase/admin/UserDomainFilter.groovy>`__\"\n\" 脚本。它可以修改给定用户 JID 的规则。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:63\nmsgid \"Implementation\"\nmsgstr \"执行\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:65\nmsgid \"\"\n\"If you have a third party system which keeps and manages all user \"\n\"information than you probably have your own UserRepository implementation\"\n\" which allows the Tigase server to access user data. Filtering rules are \"\n\"loaded from user repository using following command:\"\nmsgstr \"\"\n\"如果您有一个保存和管理所有用户信息的第三方系统，那么您可能拥有自己的 UserRepository 实现，它允许 Tigase \"\n\"服务器访问用户数据。使用以下命令从用户存储库加载过滤规则：\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:72\nmsgid \"\"\n\"Where ``user_id`` is user Jabber ID without resource part, \"\n\"``DomainFilter.ALLOWED_DOMAINS_KEY`` is a property key: ``allowed-\"\n\"domains``. The user repository MUST return one of following only:\"\nmsgstr \"\"\n\"其中 ``user_id`` 是没有资源部分的用户 Jabber ID，``DomainFilter.ALLOWED_DOMAINS_KEY`` \"\n\"是属性键：``allowed-domains``。用户存储库必须仅返回以下之一：\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:74\nmsgid \"``ALL`` - if the user is allowed to communicate with anybody\"\nmsgstr \"``ALL`` - 如果允许用户与任何人通信\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:76\nmsgid \"\"\n\"``LOCAL`` - if the user is allowed to communicate with users on the same \"\n\"server installation.\"\nmsgstr \"``LOCAL`` - 如果允许用户与同一服务器安装上的用户进行通信。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:78\nmsgid \"\"\n\"``OWN`` - if the user is allowed to communicate with users within his own\"\n\" domain only.\"\nmsgstr \"``OWN`` - 如果只允许用户与他自己域内的用户进行通信。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:80\nmsgid \"\"\n\"``LIST`` - list of domains within which the user is allowed to \"\n\"communicate with other users. No wild-cards are supported. User’s own \"\n\"domain should be included too.\"\nmsgstr \"``LIST`` - 允许用户与其他用户通信的域列表。不支持通配符。用户自己的域也应该包括在内。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:82\nmsgid \"\"\n\"``BLACKLIST`` - list of domains within which the user is NOT allowed to \"\n\"communicate with other users. No wild-cards are supported. User’s own \"\n\"domain should NOT be included.\"\nmsgstr \"``BLACKLIST`` - 不允许用户与其他用户通信的域列表。不支持通配符。不应包含用户自己的域。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:84\nmsgid \"\"\n\"``CUSTOM`` - list of rules defining custom communication permissions \"\n\"(server processes stanza according to first matched rule, similar to \"\n\"XEP-0016) in the following format:\"\nmsgstr \"``CUSTOM`` - 定义自定义通信权限的规则列表（服务器根据第一个匹配的规则处理节，类似于 XEP-0016），格式如下：\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:106\nmsgid \"``null`` - a java null if there are no settings for the user.\"\nmsgstr \"``null`` - 如果用户没有设置，则返回 java null。\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:108\nmsgid \"\"\n\"In case of ``LIST`` and ``BLACKLIST`` filtering options, it’s essential \"\n\"to provide list of domains for the whitelisting/blacklisting. \"\n\"``DomainFilter.ALLOWED_DOMAINS_LIST_KEY`` is a property key: \\\"allowed-\"\n\"domains-list\\\". The user repository MUST return semicolon separated list \"\n\"of domains: ``domain1.com;domain2.com,domain3.org``\"\nmsgstr \"\"\n\"在 ``LIST`` 和 ``BLACKLIST`` 过滤选项的情况下，为白名单/黑名单提供域列表是必不可少的。 \"\n\"``DomainFilter.ALLOWED_DOMAINS_LIST_KEY`` 是一个属性键：“allowed-domains-\"\n\"list”。用户存储库必须返回分号分隔的域列表：``domain1.com;domain2.com,domain3.org``\"\n\n#: ../../Tigase_Administration/Security/Packet_Filtering.inc:110\nmsgid \"\"\n\"The filtering is performed by the `tigase.xmpp.impl.DomainFilter \"\n\"<https://github.com/tigase/tigase-\"\n\"server/tree/master/src/main/java/tigase/xmpp/impl/DomainFilter.java>`__ \"\n\"plugin. Please refer to source code for more implementation details.\"\nmsgstr \"\"\n\"过滤由 `tigase.xmpp.impl.DomainFilter <https://github.com/tigase/\"\n\"tigase-server/tree/master/src/main/java/tigase/xmpp/impl/DomainFilter 执行.\"\n\"java>`__ 插件实现。更多实现细节请参考源代码。\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:2\nmsgid \"Access Control Lists in Tigase\"\nmsgstr \"Tigase 中的访问控制列表\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:4\nmsgid \"\"\n\"Tigase offers support for **Access Control List (ACL)** to allow for fine\"\n\" grained access to administration commands on the server.\"\nmsgstr \"Tigase 提供对 **访问控制列表 (ACL)** 的支持，以允许对服务器上的管理命令进行细粒度访问。\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:6\nmsgid \"\"\n\"By default, all administration commands are only accessible (visible \"\n\"through service discovery and can be executed) by the service \"\n\"administrators. Service administrators are existing accounts with JIDs \"\n\"(**BareJIDs**) listed in the ``config.tdsl`` file under ``admins = []`` \"\n\"(please see :ref:`admins<admins>` for details).\"\nmsgstr \"\"\n\"默认情况下，所有管理命令只能由服务管理员访问（通过服务发现可见并且可以执行）。服务管理员是在 ``admins = []`` 下的 \"\n\"``config.tdsl`` 文件中列出的具有 JID (**BareJIDs**) 的现有帐户（有关详细信息，请参阅 \"\n\":ref:`admins<admins>`）。\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:8\nmsgid \"\"\n\"Additionally, other XMPP users and entities can be assigned permissions \"\n\"to execute a command or commands using Tigase’s ACL capabilities.\"\nmsgstr \"此外，可以为其他 XMPP 用户和实体分配权限以使用 Tigase 的 ACL 功能执行一个或多个命令。\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:10\nmsgid \"\"\n\"The following is a list of possible ACL modifiers for administrator \"\n\"command accessibility:\"\nmsgstr \"以下是管理员命令可访问的可能的 ACL 修饰符列表：\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:12\nmsgid \"\"\n\"``ALL`` - Everybody can execute the command, even users from different \"\n\"federated servers.\"\nmsgstr \"``ALL`` - 每个人都可以执行该命令，即使是来自不同联合服务器的用户。\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:14\nmsgid \"\"\n\"``ADMIN`` - Local server administrators can execute the command, this is \"\n\"a default setting if no ACL is set for a command.\"\nmsgstr \"``ADMIN`` - 本地服务器管理员可以执行命令，如果没有为命令设置 ACL，此为默认设置。\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:16\nmsgid \"\"\n\"``LOCAL`` - All users with accounts on the local server can execute the \"\n\"command. Users from other, federated servers will not be able to execute \"\n\"the command.\"\nmsgstr \"``LOCAL`` - 在本地服务器上拥有帐户的所有用户都可以执行该命令。来自其他联合服务器的用户将无法执行该命令。\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:18\nmsgid \"``NONE`` - No one will be allowed to execute this command\"\nmsgstr \"``NONE`` - 不允许任何人执行此命令\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:20\nmsgid \"\"\n\"``DOMAIN_OWNER`` - Only user which is owner of the domain which items are\"\n\" being manipulated is allowed to execute the comment. If script is not \"\n\"checking permissions for the manipulated item, this value will behave in \"\n\"the same way as ``LOCAL``.\"\nmsgstr \"\"\n\"``DOMAIN_OWNER`` - 只有作为正在操作项目的域的所有者的用户才被允许执行评论。如果脚本没有检查被操作项的权限，则此值的行为方式与 \"\n\"``LOCAL`` 相同。\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:22\nmsgid \"\"\n\"``DOMAIN_ADMIN`` - Only user which is one of the domain administrators \"\n\"will be able to execute the command manipulating items related to the \"\n\"domain. If script is not checking permissions for the manipulated item, \"\n\"this value will behave in the same way as ``LOCAL``.\"\nmsgstr \"\"\n\"``DOMAIN_ADMIN`` - \"\n\"只有作为域管理员之一的用户才能执行操作与域相关的项目的命令。如果脚本没有检查被操作项的权限，则此值的行为方式与 ``LOCAL`` 相同。\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:24\nmsgid \"\"\n\"``example.com`` - Only users with accounts on the selected domain will be\"\n\" able to execute the command. It may be useful to setup a domain \"\n\"specifically for admin accounts, and automatically all users within that \"\n\"domain would be able to run the command.\"\nmsgstr \"\"\n\"``example.com`` - \"\n\"只有在所选域中拥有帐户的用户才能执行该命令。专门为管理员帐户设置域可能很有用，并且该域中的所有用户都将能够自动运行该命令。\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:26\nmsgid \"\"\n\"``user@example.com`` - Comma separated list of JIDs of users who can \"\n\"execute the command.\"\nmsgstr \"``user@example.com`` - 可以执行命令的用户的 JID 的逗号分隔列表。\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:28\nmsgid \"\"\n\"In any case, regardless of ACL settings, any command can be executed and \"\n\"accessed by the designated service wide administrators, that is accounts \"\n\"listed as admins in the ``config.tdsl`` file.\"\nmsgstr \"在任何情况下，无论 ACL 设置如何，任何命令都可以由指定的服务范围管理员执行和访问，即在 ``config.tdsl`` 文件中列为管理员的帐户。\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:30\nmsgid \"\"\n\"Multiple ACL modifiers can be combined and applied for any command. This \"\n\"may not always makes sense. For example ALL supersedes all other \"\n\"settings, so it does not make sense to combine it with any other \"\n\"modifier. However, most others can be combined with JID to broaden access\"\n\" to specific accounts.\"\nmsgstr \"\"\n\"多个 ACL 修饰符可以组合并应用于任何命令。这可能并不总是有意义的。例如 ALL \"\n\"取代所有其他设置，因此将其与任何其他修饰符结合使用是没有意义的。但是，大多数其他修饰符可以与 JID 结合以扩大对特定帐户的访问。\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:32\nmsgid \"\"\n\"On Tigase server the Access Control List is checked for the first \"\n\"matching modifier. Therefore if you combine ALL with any other modifier, \"\n\"anybody from a local or remote service will always be able to execute the\"\n\" command, no matter what other modifiers are added.\"\nmsgstr \"\"\n\"在 Tigase 服务器上，检查访问控制列表中的第一个匹配修饰符。因此，如果您将 ALL \"\n\"与任何其他修饰符结合使用，则无论添加了哪些其他修饰符，本地或远程服务中的任何人都将始终能够执行该命令。\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:34\nmsgid \"\"\n\"Please note, the ACL lists work on the command framework level. Access is\"\n\" verified before the command is actually executed. There might be \"\n\"additional access restrictions within a command itself. In many cases, \"\n\"even if all local users are permitted to execute a command (LOCAL \"\n\"modifier), some commands allow only to be executed by a domain owner or a\"\n\" domain administrator (and of course by the service-wide administrators \"\n\"as well). All the commands related to a user management such as adding a \"\n\"new user, removing a user, password changes, etc… belong to this \"\n\"category. When conducting domain (vhost) management, \"\n\"creation/registration of a new domain can be done by any local user (if \"\n\"LOCAL ACL modifier is set) but then all subsequent domain management \"\n\"tasks such as removing the vhost, updating its configuration, setting SSL\"\n\" certificate can be done by the domain owner or administrator only.\"\nmsgstr \"\"\n\"请注意，ACL \"\n\"列表在命令框架级别上工作。在实际执行命令之前验证访问。命令本身可能存在其他访问限制。在许多情况下，即使允许所有本地用户执行命令（LOCAL \"\n\"修饰符），某些命令也只允许由域所有者或域管理员（当然也包括服务范围的管理员）执行。所有与用户管理相关的命令，例如添加新用户，删除用户，更改密码等……都属于此类别。进行域\"\n\" (vhost) 管理时，任何本地用户都可以创建/注册新域（如果设置了 LOCAL ACL 修饰符），但随后的所有域管理任务，例如删除 \"\n\"vhost、更新其配置、设置 SSL 证书只能由域所有者或管理员完成。\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:36\nmsgid \"\"\n\"The ACL list is set for a specific Tigase component and a specific \"\n\"command. Therefore the configuration property must specify all the \"\n\"details. So the general format for configuring ACL for a command is this:\"\nmsgstr \"ACL 列表是为特定 Tigase 组件和特定命令设置的。因此配置属性必须指定所有细节。所以为命令配置 ACL 的一般格式是这样的：\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:46\nmsgid \"The breakdown is as such:\"\nmsgstr \"细分是这样的：\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:48\nmsgid \"\"\n\"``comp-id`` is the Tigase server component ID such as: sess-man, vhost-\"\n\"man, c2s, etc..\"\nmsgstr \"``comp-id`` 是 Tigase 服务器组件 ID 如：sess-man、vhost-man、c2s 等。\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:50\nmsgid \"\"\n\"``commands`` is a static text which indicates that the property is for \"\n\"component’s command settings.\"\nmsgstr \"``commands`` 是一个静态文本，以表示该属性用于组件的命令设置。\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:52\nmsgid \"\"\n\"``command-id`` is a command ID for which we set the ACL such as query-\"\n\"dns, http://jabber.org/protocol/admin#add-user, user-roster-management, \"\n\"etc…\"\nmsgstr \"\"\n\"``command-id`` 是我们为其设置 ACL 的命令 ID，例如 query-\"\n\"dns、http://jabber.org/protocol/admin#add-user、user-roster-management 等……\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:54\nmsgid \"Here are a few examples:\"\nmsgstr \"这里有一些例子：\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:56\nmsgid \"*Allowing local users to create and manage their own domains*\"\nmsgstr \"*允许本地用户创建和管理自己的域*\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:69\nmsgid \"\"\n\"In fact all the commands except item-add can be executed by the domain \"\n\"owner or administrator.\"\nmsgstr \"事实上，除了 item-add 之外的所有命令都可以由域所有者或管理员执行。\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:71\nmsgid \"*Allowing local users to execute user management commands:*\"\nmsgstr \"*允许本地用户执行用户管理命令：*\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:87\nmsgid \"\"\n\"As in the previous example, the commands will by executed only by local \"\n\"users who are the specific domain administrators.\"\nmsgstr \"与前面的示例一样，这些命令将仅由作为特定域管理员的本地用户执行。\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:89\nmsgid \"\"\n\"*Allowing users from a specific domain to execute query-dns command and \"\n\"some other users for given JIDs from other domains:*\"\nmsgstr \"*允许来自特定域的用户执行 query-dns 以及来自其他域的给定 JID 的其他一些用户：*\"\n\n#: ../../Tigase_Administration/Security/Access_Control_List.inc:99\nmsgid \"\"\n\"To be able to set a correct ACL property you need to know component names\"\n\" and command IDs. Component IDs can be found in the service discovery \"\n\"information on running server or in the server logs during startup. A \"\n\"command ID can be found in the command script source code. Each script \"\n\"contains a list of metadata at the very beginning of it’s code. One of \"\n\"them is ``AS:CommandId`` which is what you have to use for the ACL \"\n\"setting.\"\nmsgstr \"\"\n\"为了能够设置正确的 ACL 属性，您需要知道组件名称和命令 ID。组件 ID \"\n\"可以在正在运行的服务器上的服务发现信息或启动期间的服务器日志中找到。可以在命令脚本源代码中找到命令 \"\n\"ID。每个脚本在其代码的开头都包含一个元数据列表。其中之一是 ``AS:CommandId`` ，这是您必须用于 ACL 设置的内容。\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:2\nmsgid \"TLS/SSL encryption features configuration\"\nmsgstr \"TLS/SSL 加密功能配置\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:4\nmsgid \"\"\n\"Tigase allows adjusting the most important parameters used when \"\n\"establishing TLS connections - set of protocols and ciphers that will be \"\n\"used during negotiation of the connection. The single most important is \"\n\"``hardened-mode`` - it’s the most general configuration and offers three-\"\n\"step adjustment of the settings - please see :ref:`hardened-\"\n\"mode<hardenedMode>` for details. ``hardened-mode`` can be configured both\"\n\" via TDSL configuration file (either on ``root`` level or for \"\n\"``sslContextContainer`` for particular connection managers) or on VHost \"\n\"level.\"\nmsgstr \"\"\n\"Tigase 允许调整在建立 TLS 连接时使用的最重要的参数 - 在连接协商期间将使用的一组协议和密码。最重要的一个是 ``hardened-\"\n\"mode`` - 它是最通用的配置，其提供了三步调整设置 - 详情请参阅 :ref:`hardened-mode<hardenedMode>`。 \"\n\"``hardened-mode`` 可以通过 TDSL 配置文件（在 ``root`` 级别或对于特定连接管理器的 \"\n\"``sslContextContainer``）或 VHost 级别进行配置。\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:6\nmsgid \"\"\n\"If you want to disable certain protocols or ciphers you can use two \"\n\"options: ``tls-disabled-protocols`` and ``tls-disabled-ciphers`` \"\n\"respectively. They allow, as name suggests, disabling certain items from \"\n\"default sets. They both takes an array of strings, which ten be removed \"\n\"from the lists.\"\nmsgstr \"\"\n\"如果你想禁用某些协议或密码，你可以使用两个选项：分别是 ``tls-disabled-protocols`` 和 ``tls-disabled-\"\n\"ciphers``。顾名思义，它们允许禁用默认集中的某些项目。他们都接受一个字符串数组，从列表中删除哪十个。\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:8\nmsgid \"\"\n\"Let’s say you’d like to remove support for ``SSL``, ``SSLv2`` and \"\n\"``SSLv3`` protocols. You should simply use following configuraiton: \"\n\"``'tls-disabled-protocols' = ['SSL', 'SSLv2', 'SSLv3']``. Complete list \"\n\"of protocols depends on particular Java version that you use - please \"\n\"refer to the documentation for details. For example for the default \"\n\"Java11 list you can check `SSLContext Algorithms \"\n\"<https://docs.oracle.com/en/java/javase/11/docs/specs/security/standard-\"\n\"names.html#sslcontext-algorithms>`__\"\nmsgstr \"\"\n\"假设您想删除对 ``SSL``, ``SSLv2`` 和 ``SSLv3`` 协议的支持。您应该简单地使用以下配置：``'tls-\"\n\"disabled-protocols' = ['SSL', 'SSLv2', 'SSLv3']``。协议的完整列表取决于您使用的特定 Java \"\n\"版本 - 请参阅文档了解详细信息。例如对于默认的 Java11 列表，您可以查看 `SSLContext Algorithms \"\n\"<https://docs.oracle.com/en/java/javase/11/docs/specs/security/standard-\"\n\"names.html#sslcontext-algorithms>`__\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:10\nmsgid \"\"\n\"``tls-disabled-ciphers`` follows same format and uses names defined in \"\n\"`JSSE Cipher Suite Names \"\n\"<https://docs.oracle.com/en/java/javase/11/docs/specs/security/standard-\"\n\"names.html#jsse-cipher-suite-names>`__. It’s also possible to use regular\"\n\" expressions to quickly eliminate groups of ciphers.\"\nmsgstr \"\"\n\"``tls-disabled-ciphers`` 遵循相同的格式并使用 `JSSE Cipher Suite Names \"\n\"<https://docs.oracle.com/en/java/javase/11/docs/specs/security/standard-\"\n\"names.html#jsse-cipher-suite-names>`__ 中定义的名称。也可以使用正则表达式来快速消除密码组。\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:12\nmsgid \"\"\n\"If you want to enable only specific protocols or ciphers irrespective of \"\n\"``hardened-mode`` or above disabling options you can use ``tls-enabled-\"\n\"protocols`` and ``tls-enabled-ciphers`` - those two options take arrays \"\n\"as well and they will configure Tigase to use only those protocols or \"\n\"ciphers that are provided (without support for regular expressions). \"\n\"Therefore if you configure Tigase with ``'tls-enabled-protocols' = [ \"\n\"'TLSv1.2' ]`` then **only** ``TLSv1.2`` will be supported by Tigase.\"\nmsgstr \"\"\n\"如果您只想启用特定的协议或密码，而不考虑 ``hardened-mode`` 或以上禁用选项，您可以使用 ``tls-enabled-\"\n\"protocols`` 和 ``tls-enabled-ciphers`` - 这两个选项也接受数组，它们将会配置Tigase \"\n\"以仅使用提供的那些协议或密码（不支持正则表达式）。因此，如果您使用 ``'tls-enabled-protocols' = [ 'TLSv1.2'\"\n\" ]`` 配置 Tigase，那么 Tigase 将 **仅** 支持 ``TLSv1.2``。\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:14\nmsgid \"\"\n\"The last option that you may be interested in adjusting is ``ephemeral-\"\n\"key-size`` - it follows Java’s configuration capabilities outlined in \"\n\"`Customizing Size of Ephemeral Diffie-Hellman Keys \"\n\"<https://docs.oracle.com/en/java/javase/11/security/java-secure-socket-\"\n\"extension-jsse-reference-guide.html#GUID-D9B216E8-3EFC-4882-B76E-\"\n\"17A87D8F2F9D>`__. Tigase defaults Diffie-Hellman keys of 4096 bits.\"\nmsgstr \"\"\n\"您可能有兴趣调整的最后一个选项是 ``ephemeral-key-size`` - 它遵循 `Customizing Size of \"\n\"Ephemeral Diffie-Hellman Keys \"\n\"<https://docs.oracle.com/en/java/javase/11/security/java-secure-socket-\"\n\"extension-jsse-reference-guide.html#GUID-D9B216E8-3EFC-4882-B76E-\"\n\"17A87D8F2F9D>`__ 中概述的 Java 配置功能 。Tigase 默认 Diffie-Hellman 密钥为 4096 位元。\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:18\nmsgid \"\"\n\"We try to provide the best default set of options therefore **it’s \"\n\"recommendable to use defaults provided by Tigase**. If you want to make \"\n\"your extremely secure (considering possible connectivity issues with \"\n\"installations that may be less secure) then you should only adjust \"\n\"``hardened-mode`` setting (and switch it to ``strict``).\"\nmsgstr \"\"\n\"我们尝试提供最佳的默认选项集，因此 **建议使用 Tigase \"\n\"提供的默认值**。如果你想让你的系统非常安全（考虑到安装可能不太安全的连接问题），那么你应该只调整 ``hardened-mode`` \"\n\"设置（并将其切换为 ``strict``）。\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:21\nmsgid \"Testing hosts TLS capabilities\"\nmsgstr \"测试主机 TLS 功能\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:23\nmsgid \"\"\n\"If you run into issues with TLS connectivity it’s helpful to compare if \"\n\"both installations support same set of protocols and ciphers. One of the \"\n\"most versatile and helpful tools is `Mozilla’s CipherScan \"\n\"<https://github.com/mozilla/cipherscan>`__. For example for our \"\n\"installation ``tigase.im`` result would look like this:\"\nmsgstr \"\"\n\"如果您遇到 TLS 连接问题，比较两个安装是否支持相同的协议和密码集会很有帮助。最通用和最有用的工具之一是 `Mozilla’s \"\n\"CipherScan <https://github.com/mozilla/cipherscan>`__。例如，对于我们的安装 \"\n\"``tigase.im`` 结果将如下所示：\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:56\nmsgid \"TLS 1.3 compatibility\"\nmsgstr \"TLS 1.3 兼容性\"\n\n#: ../../Tigase_Administration/Security/TLS_Features_Configuration.inc:58\nmsgid \"\"\n\"Due to compatibility issues, TLS 1.3 is currently (version 8.1.x) \"\n\"disabled by default. It can be enabled by setting property ``tls-disable-\"\n\"tls13`` of ``sslContextContainer`` bean to ``false``:\"\nmsgstr \"\"\n\"由于兼容性问题，目前默认禁用 TLS 1.3（版本 8.1.x）。可以通过将 ``sslContextContainer`` bean 的属性 \"\n\"``tls-disable-tls13`` 设置为 ``false`` 来启用它：\"\n\n#~ msgid \"\"\n#~ \"Tigase allows adjusting the most \"\n#~ \"important parameters used when establishing\"\n#~ \" TLS connections - set of protocols\"\n#~ \" and ciphers that will be used \"\n#~ \"during negotiation of the connection. \"\n#~ \"The single most important is \"\n#~ \"``hardened-mode`` - it’s the most \"\n#~ \"general configuration and offers three-\"\n#~ \"step adjustment of the settings - \"\n#~ \"please see `??? <#hardenedMode>`__ for \"\n#~ \"details. ``hardened-mode`` can be \"\n#~ \"configured both via TDSL configuration \"\n#~ \"file (either on ``root`` level or \"\n#~ \"for ``sslContextContainer`` for particular \"\n#~ \"connection managers) or on VHost level.\"\n#~ msgstr \"\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Administration/Statistics_Description.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: 2022-09-07 17:14+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/statistics_description/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:4\nmsgid \"Appendix I - Statistics description\"\nmsgstr \"附录 I - 统计说明\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:6\nmsgid \"\"\n\"Statistics are divided between data sources, components and processors. \"\n\"You may see the same statistics collected for multiple components which \"\n\"are defined in common components section. Note that statistics are \"\n\"defined by {$component}/statistic so if you wanted Max queue size on \"\n\"pubsub, you would look for pubsub/Max queue size. Statistics will not be \"\n\"provided by components that are not enabled.\"\nmsgstr \"\"\n\"统计数据分为数据源，组件和处理器。您可能会看到为公共组件部分中定义的多个组件收集的相同统计信息。请注意，统计信息由 \"\n\"{$component}/statistic 定义，因此如果您想要 pubsub 上的 Max queue size，您将查找 \"\n\"pubsub/Max queue size。未启用的组件不会提供统计信息。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:9\nmsgid \"Data source statistics\"\nmsgstr \"数据源统计\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:11\nmsgid \"\"\n\"Data sources used to access data storages such as JDBC (databases) or \"\n\"MongoDB provide statistics related to stability of connections to data \"\n\"storage and number of open connections.\"\nmsgstr \"用于访问JDBC（数据库）或MongoDB等数据存储的数据源提供与数据存储连接稳定性和打开连接数相关的统计信息。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:14\n#: ../../Tigase_Administration/Statistics_Description.rst:33\n#: ../../Tigase_Administration/Statistics_Description.rst:50\n#: ../../Tigase_Administration/Statistics_Description.rst:84\n#: ../../Tigase_Administration/Statistics_Description.rst:104\n#: ../../Tigase_Administration/Statistics_Description.rst:188\n#: ../../Tigase_Administration/Statistics_Description.rst:206\n#: ../../Tigase_Administration/Statistics_Description.rst:248\n#: ../../Tigase_Administration/Statistics_Description.rst:257\n#: ../../Tigase_Administration/Statistics_Description.rst:295\n#: ../../Tigase_Administration/Statistics_Description.rst:306\n#: ../../Tigase_Administration/Statistics_Description.rst:321\n#: ../../Tigase_Administration/Statistics_Description.rst:336\n#: ../../Tigase_Administration/Statistics_Description.rst:485\n#: ../../Tigase_Administration/Statistics_Description.rst:506\n#: ../../Tigase_Administration/Statistics_Description.rst:539\n#: ../../Tigase_Administration/Statistics_Description.rst:682\nmsgid \"Statistics Name\"\nmsgstr \"统计名称\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:14\n#: ../../Tigase_Administration/Statistics_Description.rst:33\n#: ../../Tigase_Administration/Statistics_Description.rst:50\n#: ../../Tigase_Administration/Statistics_Description.rst:84\n#: ../../Tigase_Administration/Statistics_Description.rst:104\n#: ../../Tigase_Administration/Statistics_Description.rst:188\n#: ../../Tigase_Administration/Statistics_Description.rst:206\n#: ../../Tigase_Administration/Statistics_Description.rst:248\n#: ../../Tigase_Administration/Statistics_Description.rst:257\n#: ../../Tigase_Administration/Statistics_Description.rst:295\n#: ../../Tigase_Administration/Statistics_Description.rst:306\n#: ../../Tigase_Administration/Statistics_Description.rst:321\n#: ../../Tigase_Administration/Statistics_Description.rst:336\n#: ../../Tigase_Administration/Statistics_Description.rst:485\n#: ../../Tigase_Administration/Statistics_Description.rst:506\n#: ../../Tigase_Administration/Statistics_Description.rst:539\n#: ../../Tigase_Administration/Statistics_Description.rst:682\nmsgid \"Description\"\nmsgstr \"描述\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:14\n#: ../../Tigase_Administration/Statistics_Description.rst:33\n#: ../../Tigase_Administration/Statistics_Description.rst:50\n#: ../../Tigase_Administration/Statistics_Description.rst:84\n#: ../../Tigase_Administration/Statistics_Description.rst:104\n#: ../../Tigase_Administration/Statistics_Description.rst:188\n#: ../../Tigase_Administration/Statistics_Description.rst:206\n#: ../../Tigase_Administration/Statistics_Description.rst:248\n#: ../../Tigase_Administration/Statistics_Description.rst:257\n#: ../../Tigase_Administration/Statistics_Description.rst:295\n#: ../../Tigase_Administration/Statistics_Description.rst:306\n#: ../../Tigase_Administration/Statistics_Description.rst:321\n#: ../../Tigase_Administration/Statistics_Description.rst:336\n#: ../../Tigase_Administration/Statistics_Description.rst:485\n#: ../../Tigase_Administration/Statistics_Description.rst:506\n#: ../../Tigase_Administration/Statistics_Description.rst:539\n#: ../../Tigase_Administration/Statistics_Description.rst:682\nmsgid \"Statistics Level\"\nmsgstr \"统计级别\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:14\n#: ../../Tigase_Administration/Statistics_Description.rst:33\n#: ../../Tigase_Administration/Statistics_Description.rst:50\n#: ../../Tigase_Administration/Statistics_Description.rst:84\n#: ../../Tigase_Administration/Statistics_Description.rst:104\n#: ../../Tigase_Administration/Statistics_Description.rst:188\n#: ../../Tigase_Administration/Statistics_Description.rst:206\n#: ../../Tigase_Administration/Statistics_Description.rst:248\n#: ../../Tigase_Administration/Statistics_Description.rst:257\n#: ../../Tigase_Administration/Statistics_Description.rst:295\n#: ../../Tigase_Administration/Statistics_Description.rst:306\n#: ../../Tigase_Administration/Statistics_Description.rst:321\n#: ../../Tigase_Administration/Statistics_Description.rst:336\n#: ../../Tigase_Administration/Statistics_Description.rst:485\n#: ../../Tigase_Administration/Statistics_Description.rst:506\n#: ../../Tigase_Administration/Statistics_Description.rst:539\n#: ../../Tigase_Administration/Statistics_Description.rst:682\nmsgid \"Format\"\nmsgstr \"格式\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:14\n#: ../../Tigase_Administration/Statistics_Description.rst:33\n#: ../../Tigase_Administration/Statistics_Description.rst:50\n#: ../../Tigase_Administration/Statistics_Description.rst:84\n#: ../../Tigase_Administration/Statistics_Description.rst:104\n#: ../../Tigase_Administration/Statistics_Description.rst:188\n#: ../../Tigase_Administration/Statistics_Description.rst:206\n#: ../../Tigase_Administration/Statistics_Description.rst:248\n#: ../../Tigase_Administration/Statistics_Description.rst:257\n#: ../../Tigase_Administration/Statistics_Description.rst:295\n#: ../../Tigase_Administration/Statistics_Description.rst:306\n#: ../../Tigase_Administration/Statistics_Description.rst:321\n#: ../../Tigase_Administration/Statistics_Description.rst:336\n#: ../../Tigase_Administration/Statistics_Description.rst:485\n#: ../../Tigase_Administration/Statistics_Description.rst:506\n#: ../../Tigase_Administration/Statistics_Description.rst:539\n#: ../../Tigase_Administration/Statistics_Description.rst:682\nmsgid \"List of Possible Statistics\"\nmsgstr \"可能的统计数据列表\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:16\nmsgid \"Number of active data sources\"\nmsgstr \"活动数据源的数量\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:16\nmsgid \"\"\n\"Number of defined and active data sources (i.e. connection pools). This \"\n\"is not a number of connections to data sources as it varies and is listed\"\n\" separately for every defined data source.\"\nmsgstr \"已定义和活动数据源（即连接池）的数量。这不是与数据源的连接数，因为它会有所不同，并且针对每个定义的数据源单独列出。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:16\n#: ../../Tigase_Administration/Statistics_Description.rst:18\n#: ../../Tigase_Administration/Statistics_Description.rst:20\n#: ../../Tigase_Administration/Statistics_Description.rst:22\n#: ../../Tigase_Administration/Statistics_Description.rst:24\n#: ../../Tigase_Administration/Statistics_Description.rst:35\n#: ../../Tigase_Administration/Statistics_Description.rst:37\n#: ../../Tigase_Administration/Statistics_Description.rst:41\n#: ../../Tigase_Administration/Statistics_Description.rst:52\n#: ../../Tigase_Administration/Statistics_Description.rst:54\n#: ../../Tigase_Administration/Statistics_Description.rst:58\n#: ../../Tigase_Administration/Statistics_Description.rst:86\n#: ../../Tigase_Administration/Statistics_Description.rst:88\n#: ../../Tigase_Administration/Statistics_Description.rst:90\n#: ../../Tigase_Administration/Statistics_Description.rst:138\n#: ../../Tigase_Administration/Statistics_Description.rst:140\n#: ../../Tigase_Administration/Statistics_Description.rst:153\n#: ../../Tigase_Administration/Statistics_Description.rst:224\n#: ../../Tigase_Administration/Statistics_Description.rst:226\n#: ../../Tigase_Administration/Statistics_Description.rst:230\n#: ../../Tigase_Administration/Statistics_Description.rst:250\n#: ../../Tigase_Administration/Statistics_Description.rst:261\n#: ../../Tigase_Administration/Statistics_Description.rst:265\n#: ../../Tigase_Administration/Statistics_Description.rst:267\n#: ../../Tigase_Administration/Statistics_Description.rst:269\n#: ../../Tigase_Administration/Statistics_Description.rst:273\n#: ../../Tigase_Administration/Statistics_Description.rst:277\n#: ../../Tigase_Administration/Statistics_Description.rst:279\n#: ../../Tigase_Administration/Statistics_Description.rst:287\n#: ../../Tigase_Administration/Statistics_Description.rst:374\n#: ../../Tigase_Administration/Statistics_Description.rst:487\n#: ../../Tigase_Administration/Statistics_Description.rst:489\n#: ../../Tigase_Administration/Statistics_Description.rst:692\nmsgid \"FINE\"\nmsgstr \"精细\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:16\n#: ../../Tigase_Administration/Statistics_Description.rst:20\n#: ../../Tigase_Administration/Statistics_Description.rst:22\n#: ../../Tigase_Administration/Statistics_Description.rst:35\n#: ../../Tigase_Administration/Statistics_Description.rst:37\n#: ../../Tigase_Administration/Statistics_Description.rst:39\n#: ../../Tigase_Administration/Statistics_Description.rst:41\n#: ../../Tigase_Administration/Statistics_Description.rst:52\n#: ../../Tigase_Administration/Statistics_Description.rst:54\n#: ../../Tigase_Administration/Statistics_Description.rst:56\n#: ../../Tigase_Administration/Statistics_Description.rst:58\n#: ../../Tigase_Administration/Statistics_Description.rst:86\n#: ../../Tigase_Administration/Statistics_Description.rst:88\n#: ../../Tigase_Administration/Statistics_Description.rst:90\n#: ../../Tigase_Administration/Statistics_Description.rst:106\n#: ../../Tigase_Administration/Statistics_Description.rst:108\n#: ../../Tigase_Administration/Statistics_Description.rst:110\n#: ../../Tigase_Administration/Statistics_Description.rst:112\n#: ../../Tigase_Administration/Statistics_Description.rst:114\n#: ../../Tigase_Administration/Statistics_Description.rst:116\n#: ../../Tigase_Administration/Statistics_Description.rst:118\n#: ../../Tigase_Administration/Statistics_Description.rst:120\n#: ../../Tigase_Administration/Statistics_Description.rst:122\n#: ../../Tigase_Administration/Statistics_Description.rst:124\n#: ../../Tigase_Administration/Statistics_Description.rst:126\n#: ../../Tigase_Administration/Statistics_Description.rst:128\n#: ../../Tigase_Administration/Statistics_Description.rst:130\n#: ../../Tigase_Administration/Statistics_Description.rst:134\n#: ../../Tigase_Administration/Statistics_Description.rst:136\n#: ../../Tigase_Administration/Statistics_Description.rst:138\n#: ../../Tigase_Administration/Statistics_Description.rst:140\n#: ../../Tigase_Administration/Statistics_Description.rst:142\n#: ../../Tigase_Administration/Statistics_Description.rst:151\n#: ../../Tigase_Administration/Statistics_Description.rst:153\n#: ../../Tigase_Administration/Statistics_Description.rst:155\n#: ../../Tigase_Administration/Statistics_Description.rst:157\n#: ../../Tigase_Administration/Statistics_Description.rst:159\n#: ../../Tigase_Administration/Statistics_Description.rst:161\n#: ../../Tigase_Administration/Statistics_Description.rst:163\n#: ../../Tigase_Administration/Statistics_Description.rst:165\n#: ../../Tigase_Administration/Statistics_Description.rst:167\n#: ../../Tigase_Administration/Statistics_Description.rst:169\n#: ../../Tigase_Administration/Statistics_Description.rst:171\n#: ../../Tigase_Administration/Statistics_Description.rst:173\n#: ../../Tigase_Administration/Statistics_Description.rst:190\n#: ../../Tigase_Administration/Statistics_Description.rst:192\n#: ../../Tigase_Administration/Statistics_Description.rst:194\n#: ../../Tigase_Administration/Statistics_Description.rst:208\n#: ../../Tigase_Administration/Statistics_Description.rst:210\n#: ../../Tigase_Administration/Statistics_Description.rst:212\n#: ../../Tigase_Administration/Statistics_Description.rst:214\n#: ../../Tigase_Administration/Statistics_Description.rst:216\n#: ../../Tigase_Administration/Statistics_Description.rst:218\n#: ../../Tigase_Administration/Statistics_Description.rst:220\n#: ../../Tigase_Administration/Statistics_Description.rst:222\n#: ../../Tigase_Administration/Statistics_Description.rst:228\n#: ../../Tigase_Administration/Statistics_Description.rst:234\n#: ../../Tigase_Administration/Statistics_Description.rst:236\n#: ../../Tigase_Administration/Statistics_Description.rst:250\n#: ../../Tigase_Administration/Statistics_Description.rst:259\n#: ../../Tigase_Administration/Statistics_Description.rst:281\n#: ../../Tigase_Administration/Statistics_Description.rst:297\n#: ../../Tigase_Administration/Statistics_Description.rst:299\n#: ../../Tigase_Administration/Statistics_Description.rst:308\n#: ../../Tigase_Administration/Statistics_Description.rst:310\n#: ../../Tigase_Administration/Statistics_Description.rst:312\n#: ../../Tigase_Administration/Statistics_Description.rst:314\n#: ../../Tigase_Administration/Statistics_Description.rst:323\n#: ../../Tigase_Administration/Statistics_Description.rst:325\n#: ../../Tigase_Administration/Statistics_Description.rst:327\n#: ../../Tigase_Administration/Statistics_Description.rst:329\n#: ../../Tigase_Administration/Statistics_Description.rst:338\n#: ../../Tigase_Administration/Statistics_Description.rst:340\n#: ../../Tigase_Administration/Statistics_Description.rst:342\n#: ../../Tigase_Administration/Statistics_Description.rst:344\n#: ../../Tigase_Administration/Statistics_Description.rst:346\n#: ../../Tigase_Administration/Statistics_Description.rst:348\n#: ../../Tigase_Administration/Statistics_Description.rst:350\n#: ../../Tigase_Administration/Statistics_Description.rst:352\n#: ../../Tigase_Administration/Statistics_Description.rst:354\n#: ../../Tigase_Administration/Statistics_Description.rst:356\n#: ../../Tigase_Administration/Statistics_Description.rst:358\n#: ../../Tigase_Administration/Statistics_Description.rst:360\n#: ../../Tigase_Administration/Statistics_Description.rst:362\n#: ../../Tigase_Administration/Statistics_Description.rst:364\n#: ../../Tigase_Administration/Statistics_Description.rst:366\n#: ../../Tigase_Administration/Statistics_Description.rst:368\n#: ../../Tigase_Administration/Statistics_Description.rst:370\n#: ../../Tigase_Administration/Statistics_Description.rst:372\n#: ../../Tigase_Administration/Statistics_Description.rst:376\n#: ../../Tigase_Administration/Statistics_Description.rst:378\n#: ../../Tigase_Administration/Statistics_Description.rst:380\n#: ../../Tigase_Administration/Statistics_Description.rst:382\n#: ../../Tigase_Administration/Statistics_Description.rst:384\n#: ../../Tigase_Administration/Statistics_Description.rst:386\n#: ../../Tigase_Administration/Statistics_Description.rst:388\n#: ../../Tigase_Administration/Statistics_Description.rst:390\n#: ../../Tigase_Administration/Statistics_Description.rst:392\n#: ../../Tigase_Administration/Statistics_Description.rst:394\n#: ../../Tigase_Administration/Statistics_Description.rst:396\n#: ../../Tigase_Administration/Statistics_Description.rst:398\n#: ../../Tigase_Administration/Statistics_Description.rst:400\n#: ../../Tigase_Administration/Statistics_Description.rst:402\n#: ../../Tigase_Administration/Statistics_Description.rst:404\n#: ../../Tigase_Administration/Statistics_Description.rst:406\n#: ../../Tigase_Administration/Statistics_Description.rst:408\n#: ../../Tigase_Administration/Statistics_Description.rst:410\n#: ../../Tigase_Administration/Statistics_Description.rst:412\n#: ../../Tigase_Administration/Statistics_Description.rst:414\n#: ../../Tigase_Administration/Statistics_Description.rst:416\n#: ../../Tigase_Administration/Statistics_Description.rst:418\n#: ../../Tigase_Administration/Statistics_Description.rst:420\n#: ../../Tigase_Administration/Statistics_Description.rst:422\n#: ../../Tigase_Administration/Statistics_Description.rst:424\n#: ../../Tigase_Administration/Statistics_Description.rst:426\n#: ../../Tigase_Administration/Statistics_Description.rst:428\n#: ../../Tigase_Administration/Statistics_Description.rst:430\n#: ../../Tigase_Administration/Statistics_Description.rst:432\n#: ../../Tigase_Administration/Statistics_Description.rst:434\n#: ../../Tigase_Administration/Statistics_Description.rst:436\n#: ../../Tigase_Administration/Statistics_Description.rst:438\n#: ../../Tigase_Administration/Statistics_Description.rst:440\n#: ../../Tigase_Administration/Statistics_Description.rst:442\n#: ../../Tigase_Administration/Statistics_Description.rst:444\n#: ../../Tigase_Administration/Statistics_Description.rst:446\n#: ../../Tigase_Administration/Statistics_Description.rst:448\n#: ../../Tigase_Administration/Statistics_Description.rst:450\n#: ../../Tigase_Administration/Statistics_Description.rst:452\n#: ../../Tigase_Administration/Statistics_Description.rst:454\n#: ../../Tigase_Administration/Statistics_Description.rst:456\n#: ../../Tigase_Administration/Statistics_Description.rst:458\n#: ../../Tigase_Administration/Statistics_Description.rst:460\n#: ../../Tigase_Administration/Statistics_Description.rst:462\n#: ../../Tigase_Administration/Statistics_Description.rst:464\n#: ../../Tigase_Administration/Statistics_Description.rst:466\n#: ../../Tigase_Administration/Statistics_Description.rst:470\n#: ../../Tigase_Administration/Statistics_Description.rst:472\n#: ../../Tigase_Administration/Statistics_Description.rst:474\n#: ../../Tigase_Administration/Statistics_Description.rst:476\n#: ../../Tigase_Administration/Statistics_Description.rst:478\n#: ../../Tigase_Administration/Statistics_Description.rst:487\n#: ../../Tigase_Administration/Statistics_Description.rst:489\n#: ../../Tigase_Administration/Statistics_Description.rst:491\n#: ../../Tigase_Administration/Statistics_Description.rst:493\n#: ../../Tigase_Administration/Statistics_Description.rst:510\n#: ../../Tigase_Administration/Statistics_Description.rst:512\n#: ../../Tigase_Administration/Statistics_Description.rst:514\n#: ../../Tigase_Administration/Statistics_Description.rst:516\n#: ../../Tigase_Administration/Statistics_Description.rst:518\n#: ../../Tigase_Administration/Statistics_Description.rst:520\n#: ../../Tigase_Administration/Statistics_Description.rst:522\n#: ../../Tigase_Administration/Statistics_Description.rst:524\n#: ../../Tigase_Administration/Statistics_Description.rst:526\n#: ../../Tigase_Administration/Statistics_Description.rst:528\n#: ../../Tigase_Administration/Statistics_Description.rst:530\n#: ../../Tigase_Administration/Statistics_Description.rst:532\n#: ../../Tigase_Administration/Statistics_Description.rst:541\n#: ../../Tigase_Administration/Statistics_Description.rst:543\n#: ../../Tigase_Administration/Statistics_Description.rst:545\n#: ../../Tigase_Administration/Statistics_Description.rst:547\n#: ../../Tigase_Administration/Statistics_Description.rst:549\n#: ../../Tigase_Administration/Statistics_Description.rst:551\n#: ../../Tigase_Administration/Statistics_Description.rst:553\n#: ../../Tigase_Administration/Statistics_Description.rst:555\n#: ../../Tigase_Administration/Statistics_Description.rst:557\n#: ../../Tigase_Administration/Statistics_Description.rst:559\n#: ../../Tigase_Administration/Statistics_Description.rst:561\n#: ../../Tigase_Administration/Statistics_Description.rst:563\n#: ../../Tigase_Administration/Statistics_Description.rst:565\n#: ../../Tigase_Administration/Statistics_Description.rst:567\n#: ../../Tigase_Administration/Statistics_Description.rst:569\n#: ../../Tigase_Administration/Statistics_Description.rst:571\n#: ../../Tigase_Administration/Statistics_Description.rst:573\n#: ../../Tigase_Administration/Statistics_Description.rst:575\n#: ../../Tigase_Administration/Statistics_Description.rst:577\n#: ../../Tigase_Administration/Statistics_Description.rst:579\n#: ../../Tigase_Administration/Statistics_Description.rst:581\n#: ../../Tigase_Administration/Statistics_Description.rst:583\n#: ../../Tigase_Administration/Statistics_Description.rst:585\n#: ../../Tigase_Administration/Statistics_Description.rst:587\n#: ../../Tigase_Administration/Statistics_Description.rst:589\n#: ../../Tigase_Administration/Statistics_Description.rst:591\n#: ../../Tigase_Administration/Statistics_Description.rst:593\n#: ../../Tigase_Administration/Statistics_Description.rst:595\n#: ../../Tigase_Administration/Statistics_Description.rst:597\n#: ../../Tigase_Administration/Statistics_Description.rst:599\n#: ../../Tigase_Administration/Statistics_Description.rst:601\n#: ../../Tigase_Administration/Statistics_Description.rst:603\n#: ../../Tigase_Administration/Statistics_Description.rst:605\n#: ../../Tigase_Administration/Statistics_Description.rst:607\n#: ../../Tigase_Administration/Statistics_Description.rst:609\n#: ../../Tigase_Administration/Statistics_Description.rst:611\n#: ../../Tigase_Administration/Statistics_Description.rst:613\n#: ../../Tigase_Administration/Statistics_Description.rst:615\n#: ../../Tigase_Administration/Statistics_Description.rst:617\n#: ../../Tigase_Administration/Statistics_Description.rst:619\n#: ../../Tigase_Administration/Statistics_Description.rst:621\n#: ../../Tigase_Administration/Statistics_Description.rst:623\n#: ../../Tigase_Administration/Statistics_Description.rst:625\n#: ../../Tigase_Administration/Statistics_Description.rst:627\n#: ../../Tigase_Administration/Statistics_Description.rst:629\n#: ../../Tigase_Administration/Statistics_Description.rst:631\n#: ../../Tigase_Administration/Statistics_Description.rst:633\n#: ../../Tigase_Administration/Statistics_Description.rst:635\n#: ../../Tigase_Administration/Statistics_Description.rst:637\n#: ../../Tigase_Administration/Statistics_Description.rst:639\n#: ../../Tigase_Administration/Statistics_Description.rst:641\n#: ../../Tigase_Administration/Statistics_Description.rst:643\n#: ../../Tigase_Administration/Statistics_Description.rst:645\n#: ../../Tigase_Administration/Statistics_Description.rst:647\n#: ../../Tigase_Administration/Statistics_Description.rst:649\n#: ../../Tigase_Administration/Statistics_Description.rst:651\n#: ../../Tigase_Administration/Statistics_Description.rst:653\n#: ../../Tigase_Administration/Statistics_Description.rst:655\n#: ../../Tigase_Administration/Statistics_Description.rst:657\n#: ../../Tigase_Administration/Statistics_Description.rst:659\n#: ../../Tigase_Administration/Statistics_Description.rst:661\n#: ../../Tigase_Administration/Statistics_Description.rst:663\n#: ../../Tigase_Administration/Statistics_Description.rst:665\n#: ../../Tigase_Administration/Statistics_Description.rst:684\n#: ../../Tigase_Administration/Statistics_Description.rst:686\n#: ../../Tigase_Administration/Statistics_Description.rst:688\n#: ../../Tigase_Administration/Statistics_Description.rst:690\n#: ../../Tigase_Administration/Statistics_Description.rst:692\nmsgid \"Integer\"\nmsgstr \"整数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:16\nmsgid \"``dataSource/Number of data sources``\"\nmsgstr \"``dataSource/Number of data sources``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:18\nmsgid \"Number of connections for ``{dataSourceName}``\"\nmsgstr \"``{dataSourceName}`` 的连接数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:18\nmsgid \"Number of connections for defined data source.\"\nmsgstr \"已定义数据源的连接数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:18\n#: ../../Tigase_Administration/Statistics_Description.rst:24\n#: ../../Tigase_Administration/Statistics_Description.rst:265\n#: ../../Tigase_Administration/Statistics_Description.rst:267\n#: ../../Tigase_Administration/Statistics_Description.rst:271\n#: ../../Tigase_Administration/Statistics_Description.rst:275\n#: ../../Tigase_Administration/Statistics_Description.rst:277\n#: ../../Tigase_Administration/Statistics_Description.rst:283\n#: ../../Tigase_Administration/Statistics_Description.rst:285\n#: ../../Tigase_Administration/Statistics_Description.rst:287\n#: ../../Tigase_Administration/Statistics_Description.rst:508\nmsgid \"String\"\nmsgstr \"字符串\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:18\n#: ../../Tigase_Administration/Statistics_Description.rst:24\nmsgid \"``dataSource/{dataSourceName}/uri``\"\nmsgstr \"``dataSource/{dataSourceName}/uri``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:20\nmsgid \"Number of failed reconnections for ``{dataSourceName}``\"\nmsgstr \"``{dataSourceName}`` 重新连接失败的次数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:20\nmsgid \"\"\n\"Number of reconnections that has failed since start to the defined data \"\n\"source.\"\nmsgstr \"自定义的数据源启动以来失败的重新连接数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:20\nmsgid \"``dataSource/{dataSourceName}/failed reconnections``\"\nmsgstr \"``dataSource/{dataSourceName}/failed reconnections``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:22\nmsgid \"Number of reconnections for ``{dataSourceName}``\"\nmsgstr \"``{dataSourceName}`` 的重新连接次数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:22\nmsgid \"Number of reconnections for defined data source.\"\nmsgstr \"已定义数据源的重新连接次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:22\nmsgid \"``dataSource/{dataSourceName}/reconnections``\"\nmsgstr \"``dataSource/{dataSourceName}/reconnections``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:24\nmsgid \"URI of ``{dataSourceName}``\"\nmsgstr \"``{dataSourceName}`` 的URI\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:24\nmsgid \"Returns URI of defined data source.\"\nmsgstr \"返回已定义数据源的URI。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:28\nmsgid \"User repository statistics of {repo}\"\nmsgstr \"{repo} 的用户存储库统计信息\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:30\nmsgid \"\"\n\"For every {method} declared in ``UserRepository`` we gather execution \"\n\"statistics. This statistics are collected separately for every data \"\n\"source for which user repository is defined.\"\nmsgstr \"对于 ``UserRepository`` 中声明的每个{method}，我们都会收集执行统计信息。为定义用户存储库的每个数据源单独收集此统计信息。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:35\n#: ../../Tigase_Administration/Statistics_Description.rst:52\n#: ../../Tigase_Administration/Statistics_Description.rst:86\nmsgid \"Average processing time of {method}\"\nmsgstr \"{method} 的平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:35\nmsgid \"\"\n\"Average time taken by call of {method} for this data source since \"\n\"creation of data source (most likely from server startup). It includes \"\n\"time taken by calls which thrown exception, etc.\"\nmsgstr \"自创建数据源以来，为此数据源调用 {method} 所用的平均时间（很可能从服务器启动开始）。它包括引发异常的调用所花费的时间等。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:35\nmsgid \"``userRepository/{repo}/{method}/Average processing time``\"\nmsgstr \"``userRepository/{repo}/{method}/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:37\n#: ../../Tigase_Administration/Statistics_Description.rst:88\nmsgid \"Number of exceptions of a {method}\"\nmsgstr \"{method} 的异常数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:37\nmsgid \"Number of exceptions the specified method has caused\"\nmsgstr \"指定方法引起的异常数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:37\nmsgid \"``userRepository/{repo}/{method}/Exceptions during execution``\"\nmsgstr \"``userRepository/{repo}/{method}/Exceptions during execution``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:39\nmsgid \"Number of exceptions of a {method} in last {interval}\"\nmsgstr \"最后的 {interval} 中 {method} 的异常数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:39\nmsgid \"\"\n\"Number of exceptions the specified method has caused within the specified\"\n\" interval\"\nmsgstr \"指定方法在指定时间间隔内导致的异常数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:39\n#: ../../Tigase_Administration/Statistics_Description.rst:56\n#: ../../Tigase_Administration/Statistics_Description.rst:106\n#: ../../Tigase_Administration/Statistics_Description.rst:108\n#: ../../Tigase_Administration/Statistics_Description.rst:110\n#: ../../Tigase_Administration/Statistics_Description.rst:116\n#: ../../Tigase_Administration/Statistics_Description.rst:118\n#: ../../Tigase_Administration/Statistics_Description.rst:120\n#: ../../Tigase_Administration/Statistics_Description.rst:122\n#: ../../Tigase_Administration/Statistics_Description.rst:124\n#: ../../Tigase_Administration/Statistics_Description.rst:126\n#: ../../Tigase_Administration/Statistics_Description.rst:128\n#: ../../Tigase_Administration/Statistics_Description.rst:134\n#: ../../Tigase_Administration/Statistics_Description.rst:142\n#: ../../Tigase_Administration/Statistics_Description.rst:155\n#: ../../Tigase_Administration/Statistics_Description.rst:157\n#: ../../Tigase_Administration/Statistics_Description.rst:159\n#: ../../Tigase_Administration/Statistics_Description.rst:161\n#: ../../Tigase_Administration/Statistics_Description.rst:163\n#: ../../Tigase_Administration/Statistics_Description.rst:165\n#: ../../Tigase_Administration/Statistics_Description.rst:167\n#: ../../Tigase_Administration/Statistics_Description.rst:190\n#: ../../Tigase_Administration/Statistics_Description.rst:192\n#: ../../Tigase_Administration/Statistics_Description.rst:194\n#: ../../Tigase_Administration/Statistics_Description.rst:208\n#: ../../Tigase_Administration/Statistics_Description.rst:210\n#: ../../Tigase_Administration/Statistics_Description.rst:212\n#: ../../Tigase_Administration/Statistics_Description.rst:214\n#: ../../Tigase_Administration/Statistics_Description.rst:216\n#: ../../Tigase_Administration/Statistics_Description.rst:218\n#: ../../Tigase_Administration/Statistics_Description.rst:220\n#: ../../Tigase_Administration/Statistics_Description.rst:222\n#: ../../Tigase_Administration/Statistics_Description.rst:234\n#: ../../Tigase_Administration/Statistics_Description.rst:236\n#: ../../Tigase_Administration/Statistics_Description.rst:259\n#: ../../Tigase_Administration/Statistics_Description.rst:281\n#: ../../Tigase_Administration/Statistics_Description.rst:297\n#: ../../Tigase_Administration/Statistics_Description.rst:299\n#: ../../Tigase_Administration/Statistics_Description.rst:308\n#: ../../Tigase_Administration/Statistics_Description.rst:310\n#: ../../Tigase_Administration/Statistics_Description.rst:312\n#: ../../Tigase_Administration/Statistics_Description.rst:314\n#: ../../Tigase_Administration/Statistics_Description.rst:323\n#: ../../Tigase_Administration/Statistics_Description.rst:325\n#: ../../Tigase_Administration/Statistics_Description.rst:327\n#: ../../Tigase_Administration/Statistics_Description.rst:329\n#: ../../Tigase_Administration/Statistics_Description.rst:338\n#: ../../Tigase_Administration/Statistics_Description.rst:340\n#: ../../Tigase_Administration/Statistics_Description.rst:342\n#: ../../Tigase_Administration/Statistics_Description.rst:344\n#: ../../Tigase_Administration/Statistics_Description.rst:346\n#: ../../Tigase_Administration/Statistics_Description.rst:348\n#: ../../Tigase_Administration/Statistics_Description.rst:350\n#: ../../Tigase_Administration/Statistics_Description.rst:352\n#: ../../Tigase_Administration/Statistics_Description.rst:354\n#: ../../Tigase_Administration/Statistics_Description.rst:356\n#: ../../Tigase_Administration/Statistics_Description.rst:358\n#: ../../Tigase_Administration/Statistics_Description.rst:360\n#: ../../Tigase_Administration/Statistics_Description.rst:362\n#: ../../Tigase_Administration/Statistics_Description.rst:364\n#: ../../Tigase_Administration/Statistics_Description.rst:366\n#: ../../Tigase_Administration/Statistics_Description.rst:368\n#: ../../Tigase_Administration/Statistics_Description.rst:370\n#: ../../Tigase_Administration/Statistics_Description.rst:372\n#: ../../Tigase_Administration/Statistics_Description.rst:376\n#: ../../Tigase_Administration/Statistics_Description.rst:378\n#: ../../Tigase_Administration/Statistics_Description.rst:380\n#: ../../Tigase_Administration/Statistics_Description.rst:382\n#: ../../Tigase_Administration/Statistics_Description.rst:384\n#: ../../Tigase_Administration/Statistics_Description.rst:386\n#: ../../Tigase_Administration/Statistics_Description.rst:388\n#: ../../Tigase_Administration/Statistics_Description.rst:390\n#: ../../Tigase_Administration/Statistics_Description.rst:392\n#: ../../Tigase_Administration/Statistics_Description.rst:394\n#: ../../Tigase_Administration/Statistics_Description.rst:396\n#: ../../Tigase_Administration/Statistics_Description.rst:398\n#: ../../Tigase_Administration/Statistics_Description.rst:400\n#: ../../Tigase_Administration/Statistics_Description.rst:402\n#: ../../Tigase_Administration/Statistics_Description.rst:404\n#: ../../Tigase_Administration/Statistics_Description.rst:406\n#: ../../Tigase_Administration/Statistics_Description.rst:408\n#: ../../Tigase_Administration/Statistics_Description.rst:410\n#: ../../Tigase_Administration/Statistics_Description.rst:412\n#: ../../Tigase_Administration/Statistics_Description.rst:414\n#: ../../Tigase_Administration/Statistics_Description.rst:416\n#: ../../Tigase_Administration/Statistics_Description.rst:418\n#: ../../Tigase_Administration/Statistics_Description.rst:420\n#: ../../Tigase_Administration/Statistics_Description.rst:422\n#: ../../Tigase_Administration/Statistics_Description.rst:424\n#: ../../Tigase_Administration/Statistics_Description.rst:426\n#: ../../Tigase_Administration/Statistics_Description.rst:428\n#: ../../Tigase_Administration/Statistics_Description.rst:430\n#: ../../Tigase_Administration/Statistics_Description.rst:432\n#: ../../Tigase_Administration/Statistics_Description.rst:434\n#: ../../Tigase_Administration/Statistics_Description.rst:436\n#: ../../Tigase_Administration/Statistics_Description.rst:438\n#: ../../Tigase_Administration/Statistics_Description.rst:440\n#: ../../Tigase_Administration/Statistics_Description.rst:442\n#: ../../Tigase_Administration/Statistics_Description.rst:444\n#: ../../Tigase_Administration/Statistics_Description.rst:446\n#: ../../Tigase_Administration/Statistics_Description.rst:448\n#: ../../Tigase_Administration/Statistics_Description.rst:450\n#: ../../Tigase_Administration/Statistics_Description.rst:452\n#: ../../Tigase_Administration/Statistics_Description.rst:454\n#: ../../Tigase_Administration/Statistics_Description.rst:456\n#: ../../Tigase_Administration/Statistics_Description.rst:458\n#: ../../Tigase_Administration/Statistics_Description.rst:460\n#: ../../Tigase_Administration/Statistics_Description.rst:462\n#: ../../Tigase_Administration/Statistics_Description.rst:464\n#: ../../Tigase_Administration/Statistics_Description.rst:466\n#: ../../Tigase_Administration/Statistics_Description.rst:468\n#: ../../Tigase_Administration/Statistics_Description.rst:470\n#: ../../Tigase_Administration/Statistics_Description.rst:472\n#: ../../Tigase_Administration/Statistics_Description.rst:474\n#: ../../Tigase_Administration/Statistics_Description.rst:476\n#: ../../Tigase_Administration/Statistics_Description.rst:478\n#: ../../Tigase_Administration/Statistics_Description.rst:491\n#: ../../Tigase_Administration/Statistics_Description.rst:493\n#: ../../Tigase_Administration/Statistics_Description.rst:508\n#: ../../Tigase_Administration/Statistics_Description.rst:510\n#: ../../Tigase_Administration/Statistics_Description.rst:512\n#: ../../Tigase_Administration/Statistics_Description.rst:514\n#: ../../Tigase_Administration/Statistics_Description.rst:516\n#: ../../Tigase_Administration/Statistics_Description.rst:518\n#: ../../Tigase_Administration/Statistics_Description.rst:520\n#: ../../Tigase_Administration/Statistics_Description.rst:522\n#: ../../Tigase_Administration/Statistics_Description.rst:524\n#: ../../Tigase_Administration/Statistics_Description.rst:526\n#: ../../Tigase_Administration/Statistics_Description.rst:528\n#: ../../Tigase_Administration/Statistics_Description.rst:530\n#: ../../Tigase_Administration/Statistics_Description.rst:532\n#: ../../Tigase_Administration/Statistics_Description.rst:543\n#: ../../Tigase_Administration/Statistics_Description.rst:545\n#: ../../Tigase_Administration/Statistics_Description.rst:547\n#: ../../Tigase_Administration/Statistics_Description.rst:549\n#: ../../Tigase_Administration/Statistics_Description.rst:551\n#: ../../Tigase_Administration/Statistics_Description.rst:553\n#: ../../Tigase_Administration/Statistics_Description.rst:555\n#: ../../Tigase_Administration/Statistics_Description.rst:557\n#: ../../Tigase_Administration/Statistics_Description.rst:559\n#: ../../Tigase_Administration/Statistics_Description.rst:561\n#: ../../Tigase_Administration/Statistics_Description.rst:563\n#: ../../Tigase_Administration/Statistics_Description.rst:565\n#: ../../Tigase_Administration/Statistics_Description.rst:567\n#: ../../Tigase_Administration/Statistics_Description.rst:569\n#: ../../Tigase_Administration/Statistics_Description.rst:571\n#: ../../Tigase_Administration/Statistics_Description.rst:573\n#: ../../Tigase_Administration/Statistics_Description.rst:575\n#: ../../Tigase_Administration/Statistics_Description.rst:577\n#: ../../Tigase_Administration/Statistics_Description.rst:579\n#: ../../Tigase_Administration/Statistics_Description.rst:581\n#: ../../Tigase_Administration/Statistics_Description.rst:583\n#: ../../Tigase_Administration/Statistics_Description.rst:585\n#: ../../Tigase_Administration/Statistics_Description.rst:587\n#: ../../Tigase_Administration/Statistics_Description.rst:589\n#: ../../Tigase_Administration/Statistics_Description.rst:591\n#: ../../Tigase_Administration/Statistics_Description.rst:593\n#: ../../Tigase_Administration/Statistics_Description.rst:595\n#: ../../Tigase_Administration/Statistics_Description.rst:597\n#: ../../Tigase_Administration/Statistics_Description.rst:599\n#: ../../Tigase_Administration/Statistics_Description.rst:601\n#: ../../Tigase_Administration/Statistics_Description.rst:603\n#: ../../Tigase_Administration/Statistics_Description.rst:605\n#: ../../Tigase_Administration/Statistics_Description.rst:607\n#: ../../Tigase_Administration/Statistics_Description.rst:609\n#: ../../Tigase_Administration/Statistics_Description.rst:611\n#: ../../Tigase_Administration/Statistics_Description.rst:613\n#: ../../Tigase_Administration/Statistics_Description.rst:615\n#: ../../Tigase_Administration/Statistics_Description.rst:617\n#: ../../Tigase_Administration/Statistics_Description.rst:619\n#: ../../Tigase_Administration/Statistics_Description.rst:621\n#: ../../Tigase_Administration/Statistics_Description.rst:623\n#: ../../Tigase_Administration/Statistics_Description.rst:625\n#: ../../Tigase_Administration/Statistics_Description.rst:627\n#: ../../Tigase_Administration/Statistics_Description.rst:629\n#: ../../Tigase_Administration/Statistics_Description.rst:631\n#: ../../Tigase_Administration/Statistics_Description.rst:633\n#: ../../Tigase_Administration/Statistics_Description.rst:635\n#: ../../Tigase_Administration/Statistics_Description.rst:637\n#: ../../Tigase_Administration/Statistics_Description.rst:639\n#: ../../Tigase_Administration/Statistics_Description.rst:641\n#: ../../Tigase_Administration/Statistics_Description.rst:643\n#: ../../Tigase_Administration/Statistics_Description.rst:645\n#: ../../Tigase_Administration/Statistics_Description.rst:647\n#: ../../Tigase_Administration/Statistics_Description.rst:649\n#: ../../Tigase_Administration/Statistics_Description.rst:655\n#: ../../Tigase_Administration/Statistics_Description.rst:667\n#: ../../Tigase_Administration/Statistics_Description.rst:684\n#: ../../Tigase_Administration/Statistics_Description.rst:690\nmsgid \"FINEST\"\nmsgstr \"最细微的\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:39\nmsgid \"``userRepository/{repo}/{method}/Executions last {interval}``\"\nmsgstr \"``userRepository/{repo}/{method}/Executions last {interval}``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:41\n#: ../../Tigase_Administration/Statistics_Description.rst:90\nmsgid \"Number of executions of a {method}\"\nmsgstr \"{method} 的执行次数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:41\nmsgid \"Number of times specified method has been executed\"\nmsgstr \"指定方法已执行的次数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:41\nmsgid \"``userRepository/{repo}/{method}/Executions``\"\nmsgstr \"``userRepository/{repo}/{method}/Executions``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:45\nmsgid \"Auth repository statistics of {repo}\"\nmsgstr \"{repo} 的Auth存储库统计信息\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:47\nmsgid \"\"\n\"For every {method} declared in ``AuthRepository`` we gather execution \"\n\"statistics. This statistics are collected separately for every data \"\n\"source for which authentication repository is defined.\"\nmsgstr \"\"\n\"对于 ``AuthRepository`` 中声明的每个 \"\n\"{method}，我们都会收集执行统计信息。为定义了身份验证存储库的每个数据源单独收集此统计信息。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:52\n#: ../../Tigase_Administration/Statistics_Description.rst:86\nmsgid \"Average time it takes to process {method}.\"\nmsgstr \"处理 {method} 所需的平均时间。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:52\nmsgid \"``authRepository/{repo}/{method}/Average processing time``\"\nmsgstr \"``authRepository/{repo}/{method}/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:54\nmsgid \"Number of exceptions of {method}\"\nmsgstr \"{method} 的异常数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:54\nmsgid \"Number of times {method} has caused an exception.\"\nmsgstr \"{method} 导致异常的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:54\nmsgid \"``authRepository/{repo}/{method}/Exceptions during execution``\"\nmsgstr \"``authRepository/{repo}/{method}/Exceptions during execution``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:56\nmsgid \"Number of exceptions of {method} in last {interval}\"\nmsgstr \"最后的 {interval} 中 {method} 的异常数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:56\nmsgid \"\"\n\"Number of times {method} has caused an exception within the specified \"\n\"interval.\"\nmsgstr \"{method} 在指定时间间隔内导致异常的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:56\nmsgid \"``authRepository/{repo}/{method}/Executions last {interval}``\"\nmsgstr \"``authRepository/{repo}/{method}/Executions last {interval}``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:58\nmsgid \"Number of executions of {method}\"\nmsgstr \"{method} 的执行次数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:58\n#: ../../Tigase_Administration/Statistics_Description.rst:90\nmsgid \"Number of times {method} has been executed.\"\nmsgstr \"{method} 已执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:58\nmsgid \"``authRepository/{repo}/{method}/Executions``\"\nmsgstr \"``authRepository/{repo}/{method}/Executions``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:62\nmsgid \"Statistics common to custom {compname} component repositories\"\nmsgstr \"自定义 {compname} 组件存储库共有的统计信息\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:64\nmsgid \"\"\n\"These statistics may be found in many components which are using \"\n\"repository implementations created just for them. An example of such \"\n\"components may be:\"\nmsgstr \"这些统计信息可以在许多使用仅为它们创建的存储库实现的组件中找到。此类组件的示例可能是：\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:67\nmsgid \"**amp**\"\nmsgstr \"**amp**\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:67\nmsgid \"with msgBroadcastRepository as {repo} name,\"\nmsgstr \"以 msgBroadcastRepository 作为 {repo} 名称，\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:70\nmsgid \"**message-archive**\"\nmsgstr \"**消息存档**\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:70\nmsgid \"with repositoryPool as a {repo} name,\"\nmsgstr \"使用 repositoryPool 作为 {repo} 名称，\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:73\nmsgid \"**muc**\"\nmsgstr \"**muc**\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:73\nmsgid \"with muc-dao as a {repo} name,\"\nmsgstr \"以 muc-dao 作为 {repo} 名称，\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:76\nmsgid \"**pubsub**\"\nmsgstr \"**pubsub**\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:76\nmsgid \"with dao as a {repo} name,\"\nmsgstr \"以 dao 作为 {repo} 名称，\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:79\nmsgid \"**sess-man**\"\nmsgstr \"**sess-man**\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:79\nmsgid \"with msgRepository as a {repo} name\"\nmsgstr \"以 msgRepository 作为 {repo} 名称\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:81\nmsgid \"\"\n\"For custom component repositories we gather statistics in a same way as \"\n\"we do for user and authorization repositories. Statistics are collected \"\n\"on per {method} basis separately for every data source ({dataSourceName})\"\n\" for which repository is defined.\"\nmsgstr \"\"\n\"对于自定义组件存储库，我们以与用户和授权存储库相同的方式收集统计信息。针对定义了存储库的每个数据源 ({dataSourceName})，按 \"\n\"{method} 分别收集统计信息。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:86\nmsgid \"``{compname}/{repo}/{dataSourceName}/{method}/Average processing time``\"\nmsgstr \"``{compname}/{repo}/{dataSourceName}/{method}/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:88\nmsgid \"Number of exceptions {method} has caused.\"\nmsgstr \"{method} 引起的异常数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:88\nmsgid \"\"\n\"``{compname}/{repo}/{dataSourceName}/{method}/Exceptions during \"\n\"execution``\"\nmsgstr \"\"\n\"``{compname}/{repo}/{dataSourceName}/{method}/Exceptions during execution``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:90\nmsgid \"``{compname}/{repo}/{dataSourceName}/{method}/Executions``\"\nmsgstr \"``{compname}/{repo}/{dataSourceName}/{method}/Executions``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:94\nmsgid \"Statistics common to components\"\nmsgstr \"组件共有的统计信息\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:96\nmsgid \"\"\n\"These statistics may be found in multiple components and may be seen \"\n\"multiple times. For example both s2s and c2s will have Bytes received \"\n\"statistic, so each can be found the following way:\"\nmsgstr \"这些统计数据可以在多个组件中找到，并且可以多次看到。例如s2s和c2s都会有接收字节数统计，所以每个都可以通过以下方式找到：\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:104\n#: ../../Tigase_Administration/Statistics_Description.rst:188\n#: ../../Tigase_Administration/Statistics_Description.rst:206\n#: ../../Tigase_Administration/Statistics_Description.rst:248\n#: ../../Tigase_Administration/Statistics_Description.rst:257\n#: ../../Tigase_Administration/Statistics_Description.rst:295\n#: ../../Tigase_Administration/Statistics_Description.rst:306\n#: ../../Tigase_Administration/Statistics_Description.rst:336\n#: ../../Tigase_Administration/Statistics_Description.rst:506\n#: ../../Tigase_Administration/Statistics_Description.rst:539\nmsgid \"Available {field}\"\nmsgstr \"可用{字段}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:106\nmsgid \"add-script last {interval}\"\nmsgstr \"添加脚本最后的 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:106\nmsgid \"\"\n\"The number of times that ``add-script`` adhoc command has been run within\"\n\" the last interval.\"\nmsgstr \"``add-script`` adhoc 命令在最后一个时间间隔内运行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:106\n#: ../../Tigase_Administration/Statistics_Description.rst:116\n#: ../../Tigase_Administration/Statistics_Description.rst:120\n#: ../../Tigase_Administration/Statistics_Description.rst:122\n#: ../../Tigase_Administration/Statistics_Description.rst:192\n#: ../../Tigase_Administration/Statistics_Description.rst:208\n#: ../../Tigase_Administration/Statistics_Description.rst:212\n#: ../../Tigase_Administration/Statistics_Description.rst:216\n#: ../../Tigase_Administration/Statistics_Description.rst:220\n#: ../../Tigase_Administration/Statistics_Description.rst:297\n#: ../../Tigase_Administration/Statistics_Description.rst:308\n#: ../../Tigase_Administration/Statistics_Description.rst:312\n#: ../../Tigase_Administration/Statistics_Description.rst:340\n#: ../../Tigase_Administration/Statistics_Description.rst:344\n#: ../../Tigase_Administration/Statistics_Description.rst:360\n#: ../../Tigase_Administration/Statistics_Description.rst:364\n#: ../../Tigase_Administration/Statistics_Description.rst:372\n#: ../../Tigase_Administration/Statistics_Description.rst:376\n#: ../../Tigase_Administration/Statistics_Description.rst:380\n#: ../../Tigase_Administration/Statistics_Description.rst:384\n#: ../../Tigase_Administration/Statistics_Description.rst:388\n#: ../../Tigase_Administration/Statistics_Description.rst:400\n#: ../../Tigase_Administration/Statistics_Description.rst:404\n#: ../../Tigase_Administration/Statistics_Description.rst:408\n#: ../../Tigase_Administration/Statistics_Description.rst:412\n#: ../../Tigase_Administration/Statistics_Description.rst:416\n#: ../../Tigase_Administration/Statistics_Description.rst:420\n#: ../../Tigase_Administration/Statistics_Description.rst:424\n#: ../../Tigase_Administration/Statistics_Description.rst:428\n#: ../../Tigase_Administration/Statistics_Description.rst:432\n#: ../../Tigase_Administration/Statistics_Description.rst:436\n#: ../../Tigase_Administration/Statistics_Description.rst:440\n#: ../../Tigase_Administration/Statistics_Description.rst:446\n#: ../../Tigase_Administration/Statistics_Description.rst:450\n#: ../../Tigase_Administration/Statistics_Description.rst:454\n#: ../../Tigase_Administration/Statistics_Description.rst:458\n#: ../../Tigase_Administration/Statistics_Description.rst:462\n#: ../../Tigase_Administration/Statistics_Description.rst:470\n#: ../../Tigase_Administration/Statistics_Description.rst:476\n#: ../../Tigase_Administration/Statistics_Description.rst:510\n#: ../../Tigase_Administration/Statistics_Description.rst:514\n#: ../../Tigase_Administration/Statistics_Description.rst:518\n#: ../../Tigase_Administration/Statistics_Description.rst:543\n#: ../../Tigase_Administration/Statistics_Description.rst:547\n#: ../../Tigase_Administration/Statistics_Description.rst:551\n#: ../../Tigase_Administration/Statistics_Description.rst:555\n#: ../../Tigase_Administration/Statistics_Description.rst:559\n#: ../../Tigase_Administration/Statistics_Description.rst:563\n#: ../../Tigase_Administration/Statistics_Description.rst:567\n#: ../../Tigase_Administration/Statistics_Description.rst:575\n#: ../../Tigase_Administration/Statistics_Description.rst:579\n#: ../../Tigase_Administration/Statistics_Description.rst:583\n#: ../../Tigase_Administration/Statistics_Description.rst:587\n#: ../../Tigase_Administration/Statistics_Description.rst:591\n#: ../../Tigase_Administration/Statistics_Description.rst:595\n#: ../../Tigase_Administration/Statistics_Description.rst:599\n#: ../../Tigase_Administration/Statistics_Description.rst:603\n#: ../../Tigase_Administration/Statistics_Description.rst:607\n#: ../../Tigase_Administration/Statistics_Description.rst:611\n#: ../../Tigase_Administration/Statistics_Description.rst:615\n#: ../../Tigase_Administration/Statistics_Description.rst:619\n#: ../../Tigase_Administration/Statistics_Description.rst:623\n#: ../../Tigase_Administration/Statistics_Description.rst:627\n#: ../../Tigase_Administration/Statistics_Description.rst:631\n#: ../../Tigase_Administration/Statistics_Description.rst:635\n#: ../../Tigase_Administration/Statistics_Description.rst:639\nmsgid \"hour minute second\"\nmsgstr \"时分秒\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:106\nmsgid \"\"\n\"``{compname}/adhoc-command/add-script last hour`` ``{compname}/adhoc-\"\n\"command/add-script last minute`` ``{compname}/adhoc-command/add-script \"\n\"last second``\"\nmsgstr \"\"\n\"``{compname}/adhoc-command/add-script last hour`` ``{compname}/adhoc-command/\"\n\"add-script last minute`` ``{compname}/adhoc-command/add-script last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:108\nmsgid \"add-script/Average processing time\"\nmsgstr \"添加脚本/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:108\nmsgid \"The average processing time ``add-script`` takes to complete.\"\nmsgstr \"``add-script`` 完成的平均处理时间。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:108\nmsgid \"``add-script/Average processing time``\"\nmsgstr \"``add-script/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:110\nmsgid \"Average processing time on last 100 runs [ms]\"\nmsgstr \"最近100次运行的平均处理时间[ms]\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:110\nmsgid \"\"\n\"The average processing time in milliseconds for all commands and scripts \"\n\"for this component over the last 100 times component is called. This \"\n\"number will populate with less than 100 runs, and will continue averaging\"\n\" until 100 runs happens, at that point, it’s the most recent 100 \"\n\"instances. This statistic will reset every time the server shuts down or \"\n\"restarts.\"\nmsgstr \"\"\n\"在过去100次组件调用中，此组件的所有命令和脚本的平均处理时间（以毫秒为单位）。此数字将填充少于 100 \"\n\"次运行，并将继续平均，直到发生100次运行，此时，它是最近的 100 个实例。每次服务器关闭或重新启动时，此统计信息都会重置。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:110\nmsgid \"``{compname}/Average processing time on last 100 runs [ms]``\"\nmsgstr \"``{compname}/Average processing time on last 100 runs [ms]``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:112\nmsgid \"Bytes received\"\nmsgstr \"收到的字节数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:112\nmsgid \"\"\n\"The total number of bytes that the component has received during the \"\n\"current server instance. This statistic resets at server shutdown or \"\n\"restart.\"\nmsgstr \"组件在当前服务器实例期间收到的总字节数。此统计信息在服务器关闭或重新启动时重置。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:112\n#: ../../Tigase_Administration/Statistics_Description.rst:114\nmsgid \"FINE or FINEST\"\nmsgstr \"精细或者最精细\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:112\nmsgid \"``{compname}/Bytes received``\"\nmsgstr \"``{compname}/Bytes received``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:114\nmsgid \"Bytes sent\"\nmsgstr \"发送的字节数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:114\nmsgid \"\"\n\"The total number of bytes that the component has sent during the current \"\n\"server instance. This statistic resets at server shutdown or restart.\"\nmsgstr \"组件在当前服务器实例期间发送的总字节数。此统计信息在服务器关闭或重新启动时重置。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:114\nmsgid \"``{compname}/Bytes sent``\"\nmsgstr \"``{compname}/Bytes sent``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:116\nmsgid \"del-script last {interval}\"\nmsgstr \"删除脚本最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:116\nmsgid \"\"\n\"The number of times that ``del-script`` adhoc command has been run within\"\n\" the last interval.\"\nmsgstr \"``del-script`` adhoc 命令在最后一个时间间隔内运行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:116\nmsgid \"\"\n\"``{compname}/adhoc-command/del-script last hour`` ``{compname}/adhoc-\"\n\"command/del-script last minute`` ``{compname}/adhoc-command/del-script \"\n\"last second``\"\nmsgstr \"\"\n\"``{compname}/adhoc-command/del-script last hour`` ``{compname}/adhoc-command/\"\n\"del-script last minute`` ``{compname}/adhoc-command/del-script last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:118\nmsgid \"del-script Average processing time\"\nmsgstr \"del-script 平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:118\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``del-\"\n\"script`` to execute.\"\nmsgstr \"``del-script`` 执行的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:118\nmsgid \"``{compname}/adhoc-command/del-script/Average processing time``\"\nmsgstr \"``{compname}/adhoc-command/del-script/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:120\nmsgid \"Last {interval} packets\"\nmsgstr \"最后 {interval} 数据包\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:120\nmsgid \"\"\n\"The number of packets that have been handled by this component in the \"\n\"last interval.\"\nmsgstr \"该组件在上一个时间间隔内已处理的数据包数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:120\nmsgid \"\"\n\"``{compname}/last hour packets`` ``{compname}/last minute packets`` \"\n\"``{compname}/last second packets``\"\nmsgstr \"\"\n\"``{compname}/last hour packets`` ``{compname}/last minute packets`` \"\n\"``{compname}/last second packets``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:122\nmsgid \"List-commands last {interval}\"\nmsgstr \"列表命令最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:122\nmsgid \"\"\n\"The number of ``list-commands`` requests sent to the component in the \"\n\"last interval.\"\nmsgstr \"在最后一个时间间隔内发送到组件的 ``list-commands`` 请求数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:122\nmsgid \"\"\n\"``{compname}/list-commands last hour`` ``{compname}/list-commands last \"\n\"minute`` ``{compname}/list-commands last second``\"\nmsgstr \"\"\n\"``{compname}/list-commands last hour`` ``{compname}/list-commands last \"\n\"minute`` ``{compname}/list-commands last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:124\nmsgid \"List-commands Average processing time\"\nmsgstr \"列表命令平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:124\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``list-\"\n\"commands`` to execute on this component.\"\nmsgstr \"以毫秒为单位的平均时间，以整数形式返回，``list-commands`` 在此组件上执行所需的时间。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:124\nmsgid \"``{compname}/list-commands/Average processing time``\"\nmsgstr \"``{compname}/list-commands/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:126\nmsgid \"{IN/OUT/Total} queue overflow\"\nmsgstr \"{IN/OUT/Total} 队列溢出\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:126\nmsgid \"\"\n\"The number of times the in or out queue has overflown for this component.\"\n\" That is there are more packets queues than the max queue size. A total \"\n\"statistic is also available that combines both results.\"\nmsgstr \"此组件的进出队列溢出的次数。即有比最大队列的大小更多的数据包队列。还提供了结合这两个结果的总体统计数据。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:126\nmsgid \"\"\n\"``{compname}/IN queue overflow`` ``{compname}/OUT queue overflow`` \"\n\"``{compname}/Total queue overflow``\"\nmsgstr \"\"\n\"``{compname}/IN queue overflow`` ``{compname}/OUT queue overflow`` \"\n\"``{compname}/Total queue overflow``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:128\nmsgid \"{in/out} queue wait: {priority}\"\nmsgstr \"{in/out} 队列等待：{priority}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:128\nmsgid \"\"\n\"The number of packets with {priority} priority currently in the incoming \"\n\"or outgoing queue.\"\nmsgstr \"当前在传入或传出队列中具有 {priority} 优先级的数据包数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:128\nmsgid \"SYSTEM CLUSTER HIGH NORMAL LOW PRESENCE LOWEST\"\nmsgstr \"系统集群 高 正常 低 存在 最低\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:128\nmsgid \"\"\n\"``{compname}/In queue wait: SYSTEM`` ``{compname}/In queue wait: \"\n\"CLUSTER`` ``{compname}/In queue wait: HIGH`` ``{compname}/In queue wait: \"\n\"NORMAL`` ``{compname}/In queue wait: LOW`` ``{compname}/In queue wait: \"\n\"PRESENCE`` ``{compname}/In queue wait: LOWEST`` ``{compname}/Out queue \"\n\"wait: SYSTEM`` ``{compname}/Out queue wait: CLUSTER`` ``{compname}/Out \"\n\"queue wait: HIGH`` ``{compname}/Out queue wait: NORMAL`` ``{compname}/Out\"\n\" queue wait: LOW`` ``{compname}/Out queue wait: PRESENCE`` \"\n\"``{compname}/Out queue wait: LOWEST``\"\nmsgstr \"\"\n\"``{compname}/In queue wait: SYSTEM`` ``{compname}/In queue wait: CLUSTER`` \"\n\"``{compname}/In queue wait: HIGH`` ``{compname}/In queue wait: NORMAL`` \"\n\"``{compname}/In queue wait: LOW`` ``{compname}/In queue wait: PRESENCE`` \"\n\"``{compname}/In queue wait: LOWEST`` ``{compname}/Out queue wait: SYSTEM`` \"\n\"``{compname}/Out queue wait: CLUSTER`` ``{compname}/Out queue wait: HIGH`` \"\n\"``{compname}/Out queue wait: NORMAL`` ``{compname}/Out queue wait: LOW`` \"\n\"``{compname}/Out queue wait: PRESENCE`` ``{compname}/Out queue wait: LOWEST``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:130\nmsgid \"{IN/OUT}_QUEUE processed {type}\"\nmsgstr \"{IN/OUT}_QUEUE 已处理 {type}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:130\nmsgid \"\"\n\"The number of stanzas of different types that have been processed VIA the\"\n\" In or Out Queue of this component. This number will reset at the end of \"\n\"the server instance. Each component will have a list of the different \"\n\"types of stanzas it can process.\"\nmsgstr \"通过此组件的In或Out Queue处理的不同类型的节数。此数字将在服务器实例结束时重置。每个组件都有一个它可以处理的不同类型节的列表。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:130\n#: ../../Tigase_Administration/Statistics_Description.rst:151\n#: ../../Tigase_Administration/Statistics_Description.rst:169\n#: ../../Tigase_Administration/Statistics_Description.rst:171\n#: ../../Tigase_Administration/Statistics_Description.rst:173\n#: ../../Tigase_Administration/Statistics_Description.rst:541\n#: ../../Tigase_Administration/Statistics_Description.rst:661\n#: ../../Tigase_Administration/Statistics_Description.rst:663\n#: ../../Tigase_Administration/Statistics_Description.rst:686\n#: ../../Tigase_Administration/Statistics_Description.rst:688\nmsgid \"FINER\"\nmsgstr \"更精细\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:130\nmsgid \"\"\n\"# messages presences cluster other IQ no XMLNS IQ \"\n\"http://jabber.org/protocol/disco#items IQ bind IQ jabber:iq:roster IQ \"\n\"session IQ vCard IQ command IQ jabber:iq:private IQ \"\n\"http://jabber.org/protocol/disco#info total IQ\"\nmsgstr \"\"\n\"# messages presences cluster other IQ no XMLNS IQ http://jabber.org/protocol/\"\n\"disco#items IQ bind IQ jabber:iq:roster IQ session IQ vCard IQ command IQ \"\n\"jabber:iq:private IQ http://jabber.org/protocol/disco#info total IQ\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:130\nmsgid \"\"\n\"``{compname}/IN_QUEUE processed`` ``{compname}/IN_QUEUE processed \"\n\"messages`` ``{compname}/IN_QUEUE processed presences`` \"\n\"``{compname}/IN_QUEUE processed cluster`` ``{compname}/IN_QUEUE processed\"\n\" other`` ``{compname}/IN_QUEUE processed IQ no XMLNS`` \"\n\"``{compname}/IN_QUEUE processed IQ \"\n\"http://jabber.org/protocol/disco#items`` ``{compname}/IN_QUEUE processed \"\n\"IQ http://jabber.org/protocol/disco#info`` ``{compname}/IN_QUEUE \"\n\"processed IQ bind`` ``{compname}/IN_QUEUE processed IQ jabber:iq:roster``\"\n\" ``{compname}/IN_QUEUE processed IQ jabber:iq:private`` \"\n\"``{compname}/IN_QUEUE processed IQ session`` ``{compname}/IN_QUEUE \"\n\"processed IQ vCard`` ``{compname}/IN_QUEUE processed IQ command`` \"\n\"``{compname}/IN_QUEUE processed total IQ`` ``{compname}/OUT_QUEUE \"\n\"processed messages`` ``{compname}/OUT_QUEUE processed presences`` \"\n\"``{compname}/OUT_QUEUE processed cluster`` ``{compname}/OUT_QUEUE \"\n\"processed other`` ``{compname}/OUT_QUEUE processed IQ no XMLNS`` \"\n\"``{compname}/OUT_QUEUE processed IQ \"\n\"http://jabber.org/protocol/disco#items`` ``{compname}/OUT_QUEUE processed\"\n\" IQ http://jabber.org/protocol/disco#info`` ``{compname}/OUT_QUEUE \"\n\"processed IQ bind`` ``{compname}/OUT_QUEUE processed IQ \"\n\"jabber:iq:roster`` ``{compname}/OUT_QUEUE processed IQ \"\n\"jabber:iq:private`` ``{compname}/OUT_QUEUE processed IQ session`` \"\n\"``{compname}/OUT_QUEUE processed IQ vCard`` ``{compname}/OUT_QUEUE \"\n\"processed IQ command`` ``{compname}/OUT_QUEUE processed total IQ``\"\nmsgstr \"\"\n\"``{compname}/IN_QUEUE processed`` ``{compname}/IN_QUEUE processed messages`` \"\n\"``{compname}/IN_QUEUE processed presences`` ``{compname}/IN_QUEUE processed \"\n\"cluster`` ``{compname}/IN_QUEUE processed other`` ``{compname}/IN_QUEUE \"\n\"processed IQ no XMLNS`` ``{compname}/IN_QUEUE processed IQ http://jabber.org/\"\n\"protocol/disco#items`` ``{compname}/IN_QUEUE processed IQ http://jabber.org/\"\n\"protocol/disco#info`` ``{compname}/IN_QUEUE processed IQ bind`` ``{compname}/\"\n\"IN_QUEUE processed IQ jabber:iq:roster`` ``{compname}/IN_QUEUE processed IQ \"\n\"jabber:iq:private`` ``{compname}/IN_QUEUE processed IQ session`` \"\n\"``{compname}/IN_QUEUE processed IQ vCard`` ``{compname}/IN_QUEUE processed \"\n\"IQ command`` ``{compname}/IN_QUEUE processed total IQ`` ``{compname}/\"\n\"OUT_QUEUE processed messages`` ``{compname}/OUT_QUEUE processed presences`` \"\n\"``{compname}/OUT_QUEUE processed cluster`` ``{compname}/OUT_QUEUE processed \"\n\"other`` ``{compname}/OUT_QUEUE processed IQ no XMLNS`` ``{compname}/\"\n\"OUT_QUEUE processed IQ http://jabber.org/protocol/disco#items`` ``{compname}/\"\n\"OUT_QUEUE processed IQ http://jabber.org/protocol/disco#info`` ``{compname}/\"\n\"OUT_QUEUE processed IQ bind`` ``{compname}/OUT_QUEUE processed IQ \"\n\"jabber:iq:roster`` ``{compname}/OUT_QUEUE processed IQ jabber:iq:private`` \"\n\"``{compname}/OUT_QUEUE processed IQ session`` ``{compname}/OUT_QUEUE \"\n\"processed IQ vCard`` ``{compname}/OUT_QUEUE processed IQ command`` \"\n\"``{compname}/OUT_QUEUE processed total IQ``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:132\nmsgid \"\"\n\"NOTE: Several statistics are only available from statistics component, \"\n\"shutdown thread will ONLY print the following: messages, presences, \"\n\"cluster, other, IQ no XLMNS, total IQ.\"\nmsgstr \"注意：一些统计信息仅可从统计组件获得，关闭线程将仅打印以下内容：消息，存在，集群，其他，IQ无XLMNS，总IQ。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:134\nmsgid \"max queue size\"\nmsgstr \"最大队列大小\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:134\nmsgid \"\"\n\"The maximum number of items allowed in the packet queue for this \"\n\"component.\"\nmsgstr \"此组件的数据包队列中允许的最大项目数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:134\nmsgid \"``{compname}/max queue size``\"\nmsgstr \"``{compname}/max queue size``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:136\nmsgid \"Open Connections\"\nmsgstr \"打开连接\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:136\nmsgid \"The number of open connections to the component.\"\nmsgstr \"与组件的打开连接数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:136\n#: ../../Tigase_Administration/Statistics_Description.rst:653\nmsgid \"INFO/FINEST\"\nmsgstr \"信息/最精细\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:136\nmsgid \"``{compname}/Open connections``\"\nmsgstr \"``{compname}/Open connections``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:138\nmsgid \"Packets received\"\nmsgstr \"收到的数据包\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:138\nmsgid \"\"\n\"The total number of packets received by the component from external \"\n\"sources in the current instance. This number resets at server shutdown or\"\n\" restart.\"\nmsgstr \"当前实例中组件从外部源接收的数据包总数。此数字在服务器关闭或重新启动时重置。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:138\nmsgid \"``{compname}/Packets received``\"\nmsgstr \"``{compname}/Packets received``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:140\nmsgid \"Packets sent\"\nmsgstr \"发送的数据包\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:140\nmsgid \"\"\n\"The total number of packets sent by the component in the current \"\n\"instance. This number resets at server shutdown or restart.\"\nmsgstr \"当前实例中组件发送的数据包总数。此数字在服务器关闭或重新启动时重置。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:140\nmsgid \"``{compname}/Packets sent``\"\nmsgstr \"``{compname}/Packets sent``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:142\nmsgid \"Processed packets thread: {in/out}\"\nmsgstr \"处理数据包线程：{in/out}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:142\nmsgid \"How many packets have been processed in and out by each processing thread.\"\nmsgstr \"每个处理线程已处理了进出多少数据包。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:142\nmsgid \"\"\n\"``{compname}/Processed packets thread: IN`` ``{compname}/Processed \"\n\"packets thread: OUT`` ``{compname}/Processed packets thread (outliers) \"\n\"IN`` ``{compname}/Processed packets thread (outliers) OUT``\"\nmsgstr \"\"\n\"``{compname}/Processed packets thread: IN`` ``{compname}/Processed packets \"\n\"thread: OUT`` ``{compname}/Processed packets thread (outliers) IN`` \"\n\"``{compname}/Processed packets thread (outliers) OUT``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"\"\n\"Statistics will provide an array for each processor, listed from 0, 1, 2,\"\n\" 3 etc.. Let’s say that we have 4 threads set for ws2s, a list will be \"\n\"seen like this:\"\nmsgstr \"统计信息将为每个处理器提供一个数组，从 0，1，2，3 等开始列出。假设我们为ws2s设置了4个线程，一个列表将如下所示：\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"``ws2s/Processed packets thread: IN=[2, 6, 4, 2]``\"\nmsgstr \"``ws2s/Processed packets thread: IN=[2, 6, 4, 2]``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"``ws2s/Processed packets thread: OUT=[8, 0, 1, 3]``\"\nmsgstr \"``ws2s/Processed packets thread: OUT=[8, 0, 1, 3]``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"\"\n\"``ws2s/Processed packets thread (outliers) IN=mean: 79.0, deviation: 441,\"\n\" outliers: [in_10-ws2s: 2359]``\"\nmsgstr \"\"\n\"``ws2s/Processed packets thread (outliers) IN=mean: 79.0, deviation: 441, \"\n\"outliers: [in_10-ws2s: 2359]``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"\"\n\"``ws2s/Processed packets thread (outliers) OUT=mean: 16.5, deviation: \"\n\"23.2058941, outliers: [out_ws2s: 80]``\"\nmsgstr \"\"\n\"``ws2s/Processed packets thread (outliers) OUT=mean: 16.5, deviation: \"\n\"23.2058941, outliers: [out_ws2s: 80]``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"\"\n\"Note that the processor arrray will only have as many threads as the \"\n\"component has as defined in {compname}/Processing threads.\"\nmsgstr \"请注意，处理器阵列将仅具有与 {compname}/Processing 线程中定义的组件一样多的线程。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:151\nmsgid \"processing threads\"\nmsgstr \"处理线程\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:151\nmsgid \"The number of threads provided for the particular component.\"\nmsgstr \"为特定组件提供的线程数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:151\nmsgid \"``{compname}/processing threads``\"\nmsgstr \"``{compname}/processing threads``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:153\nmsgid \"stream-error-counter\"\nmsgstr \"流错误计数器\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:153\nmsgid \"\"\n\"The number of errors counted during the operation of the server for this \"\n\"component. Will only be available if :ref:`stream-error-counter<stream-\"\n\"error-counter>` is enabled in config.tdsl, otherwise will be 0.\"\nmsgstr \"\"\n\"在此组件的服务器操作期间计数的错误数。只有在config.tdsl中启用了 :ref:`stream-\"\n\"error-counter<stream-error-counter>` 时才可用，否则为0。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:153\nmsgid \"``{compname}/processors/stream-error-counter``\"\nmsgstr \"``{compname}/processors/stream-error-counter``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:155\nmsgid \"Socket overflow\"\nmsgstr \"套接字溢出\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:155\nmsgid \"\"\n\"The number of times that this component has experienced socket queue \"\n\"overflow and had to drop packets. The XMPP server queues packets which \"\n\"are being sent over connection if receiver is not able to read them fast \"\n\"enough or if the network connection too slow to the amount of data which \"\n\"needs to be sent. If the queue will over flow that will be counted. This \"\n\"does not include the number of dropped packets.\"\nmsgstr \"此组件经历套接字队列溢出并不得不丢弃数据包的次数。如果接收方无法足够快地读取数据包或网络连接速度太慢而无法满足需要发送的数据量，XMPP服务器会将通过连接发送的数据包排队。如果队列将溢出将被计算在内。这不包括丢弃数据包的数量。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:155\nmsgid \"``{compname}/Socket overflow``\"\nmsgstr \"``{compname}/Socket overflow``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:157\nmsgid \"Total {in/out} queues wait\"\nmsgstr \"总 {in/out} 队列等待\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:157\nmsgid \"\"\n\"The number of packets in the inbound or outbound queue that are currently\"\n\" waiting to be sent. This includes packets of all types. This is an \"\n\"instant statistics, in that the number in queue is only as many in the \"\n\"queue the moment statistics are gathered.\"\nmsgstr \"当前等待发送的入站或出站队列中的数据包数。这包括所有类型的数据包。这也是一个即时统计信息，因为队列中的人数仅与收集统计信息时队列中的人数一样多。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:157\nmsgid \"``{compname}/Total in queues wait`` ``{compname}/Total out queues wait``\"\nmsgstr \"\"\n\"``{compname}/Total in queues wait`` ``{compname}/Total out queues wait``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:159\nmsgid \"Total queue wait\"\nmsgstr \"总队列等待\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:159\nmsgid \"\"\n\"A combined total of ``Total in queue wait`` and ``Total out queue wait`` \"\n\"statistics for this component.\"\nmsgstr \"此组件的 ``Total in queue wait`` 和 ``Total out queue wait`` 统计信息的总和。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:159\nmsgid \"``{compname}/Total queue wait``\"\nmsgstr \"``{compname}/Total queue wait``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:161\nmsgid \"Total queues wait\"\nmsgstr \"总队列等待\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:161\nmsgid \"A combined total of all component queue wait statistics.\"\nmsgstr \"所有组件队列等待统计信息的总和。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:161\nmsgid \"``Total queues wait``\"\nmsgstr \"``Total queues wait``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:163\nmsgid \"Total queues overflow\"\nmsgstr \"总队列溢出\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:163\nmsgid \"\"\n\"The number of times the component packet wait queue has overflown and had\"\n\" to drop packets. This statistic does not keep track of the number of \"\n\"dropped packets.\"\nmsgstr \"组件数据包等待队列溢出并不得不丢弃数据包的次数。此统计信息不跟踪丢弃数据包的数量。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:163\nmsgid \"``{compname}/Total queues overflow``\"\nmsgstr \"``{compname}/Total queues overflow``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:165\nmsgid \"Total/Total queues overflow\"\nmsgstr \"总/总队列溢出\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:165\nmsgid \"The combined total of all queue overflow statistics for all components.\"\nmsgstr \"所有组件的所有队列溢出统计信息的总和。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:165\nmsgid \"``total/Total queues overflow``\"\nmsgstr \"``total/Total queues overflow``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:167\nmsgid \"Waiting to send\"\nmsgstr \"等待发送\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:167\nmsgid \"\"\n\"The number of packets in the component’s queue that are waiting to be \"\n\"sent. This number will usually be 0 however it will grow if a large \"\n\"number of packets are jamming up your system, or your queue sizes are set\"\n\" too low.\"\nmsgstr \"组件队列中等待发送的数据包数量。这个数字通常为0，但是如果大量数据包阻塞了您的系统，或者您的队列大小设置得太低，它会增加。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:167\nmsgid \"``{compname}/Waiting to send``\"\nmsgstr \"``{compname}/Waiting to send``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:169\nmsgid \"Watchdog runs\"\nmsgstr \"监视器运行\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:169\nmsgid \"\"\n\"The number of times watchdog has been run on this component to check for \"\n\"stale connections.\"\nmsgstr \"在此组件上运行监视器以检查旧连接的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:169\nmsgid \"``{compname}/Watchdog runs``\"\nmsgstr \"``{compname}/Watchdog runs``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:171\nmsgid \"Watchdog stopped\"\nmsgstr \"监视器停止\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:171\nmsgid \"\"\n\"The number of times watchdog identified and closed a connection it has \"\n\"found to be stale according to the settings in ``config.tdsl`` or by the \"\n\"defaults defined :ref:`in this section<watchdog>`.\"\nmsgstr \"根据 ``config.tdsl`` 中的设置或 :ref:`此小节<watchdog>` \"\n\"中定义的默认值，监视器识别并关闭过时连接的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:171\nmsgid \"``{compname}/Watchdog stopped``\"\nmsgstr \"``{compname}/Watchdog stopped``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:173\nmsgid \"Watchdog tests\"\nmsgstr \"监视器测试\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:173\nmsgid \"\"\n\"The number of times watchdog has found a potential stale connection and \"\n\"has conducted a test to determine whether or not to close the connection.\"\n\" This is per component in the current server instance.\"\nmsgstr \"监视器发现潜在陈旧的连接并已经进行了测试以确定是否关闭连接。这是当前服务器实例中的每个组件。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:173\nmsgid \"``{compname}/Watchdog tests``\"\nmsgstr \"``{compname}/Watchdog tests``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:177\nmsgid \"Component statistics\"\nmsgstr \"组件统计\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:180\nmsgid \"AMP\"\nmsgstr \"AMP\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:182\nmsgid \"No exclusive amp specific statistics\"\nmsgstr \"没有专属的amp特定统计数据\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:185\nmsgid \"bosh\"\nmsgstr \"bosh\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:190\nmsgid \"Bosh sessions\"\nmsgstr \"Bosh会话\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:190\nmsgid \"The number of currently open and running BOSH sessions to the server.\"\nmsgstr \"当前打开和运行的与服务器的BOSH会话数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:190\nmsgid \"``bosh/Bosh sessions``\"\nmsgstr \"``bosh/Bosh sessions``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:192\nmsgid \"pre-bind session last {interval}\"\nmsgstr \"上次预绑定会话{interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:192\nmsgid \"\"\n\"The number of times the pre-bind-session command has been executed within\"\n\" the last specified interval.\"\nmsgstr \"pre-bind-session命令在最后指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:192\nmsgid \"\"\n\"``bosh/adhoc-command/pre-bind-session last hour`` ``bosh/adhoc-command\"\n\"/pre-bind-session last minute`` ``bosh/adhoc-command/pre-bind-session \"\n\"last second``\"\nmsgstr \"\"\n\"``bosh/adhoc-command/pre-bind-session last hour`` ``bosh/adhoc-command/pre-\"\n\"bind-session last minute`` ``bosh/adhoc-command/pre-bind-session last \"\n\"second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:194\nmsgid \"pre-bind-sessions/Average processing time\"\nmsgstr \"预绑定会话/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:194\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``pre-bind-\"\n\"session`` to execute.\"\nmsgstr \"执行 ``pre-bind-session`` 所需的平均时间（以毫秒为单位），以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:194\nmsgid \"``bosh/adhoc-command/pre-bind-session/Average processing time``\"\nmsgstr \"``bosh/adhoc-command/pre-bind-session/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:198\nmsgid \"c2s\"\nmsgstr \"c2s\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:200\nmsgid \"No exclusive c2s specific statistics.\"\nmsgstr \"没有独家的c2s特定统计数据。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:203\nmsgid \"cl-comp\"\nmsgstr \"cl-comp\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:208\nmsgid \"adhoc-command/cluster-nodes-list last {interval}\"\nmsgstr \"adhoc-command/cluster-nodes-list 最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:208\nmsgid \"\"\n\"The number of times per interval that the cluster-nodes-list command has \"\n\"been executed.\"\nmsgstr \"每个间隔时间执行 cluster-nodes-list 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:208\nmsgid \"\"\n\"``cl-comp/adhoc-command/cluster-nodes-list last hour`` ``cl-comp/adhoc-\"\n\"command/cluster-nodes-list last minute`` ``cl-comp/adhoc-command/cluster-\"\n\"nodes-list last second``\"\nmsgstr \"\"\n\"``cl-comp/adhoc-command/cluster-nodes-list last hour`` ``cl-comp/\"\n\"adhoc-command/cluster-nodes-list last minute`` ``cl-comp/adhoc-command/\"\n\"cluster-nodes-list last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:210\nmsgid \"adhoc-command/cluster-nodes-list/Average processing time\"\nmsgstr \"adhoc-command/cluster-nodes-list/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:210\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``cluster-\"\n\"nodes-list`` to execute.\"\nmsgstr \"以 ms 为单位的平均时间，以整数形式返回，执行 ``cluster-nodes-list`` 所需的时间。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:210\nmsgid \"``cl-comp/adhoc-command/cluster-nodes-list/Average processing time``\"\nmsgstr \"``cl-comp/adhoc-command/cluster-nodes-list/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:212\nmsgid \"adhoc-command/force-stop-service last {interval}\"\nmsgstr \"adhoc-command/force-stop-service 最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:212\nmsgid \"\"\n\"The number of times per interval that the force-stop-service command has \"\n\"been executed.\"\nmsgstr \"每个间隔时间执行 force-stop-service 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:212\nmsgid \"\"\n\"``cl-comp/adhoc-command/force-stop-service last hour`` ``cl-comp/adhoc-\"\n\"command/force-stop-service last minute`` ``cl-comp/adhoc-command/force-\"\n\"stop-service last second``\"\nmsgstr \"\"\n\"``cl-comp/adhoc-command/force-stop-service last hour`` ``cl-comp/\"\n\"adhoc-command/force-stop-service last minute`` ``cl-comp/adhoc-command/force-\"\n\"stop-service last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:214\nmsgid \"Adhoc-command/force-stop-service/Average processing time\"\nmsgstr \"Adhoc-command/force-stop-service/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:214\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``force-\"\n\"stop-service`` to execute.\"\nmsgstr \"执行 ``force-stop-service`` 所需的平均时间（以毫秒为单位），以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:214\nmsgid \"``cl-comp/adhoc-command/force-stop-service/Average processing time``\"\nmsgstr \"``cl-comp/adhoc-command/force-stop-service/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:216\nmsgid \"adhoc-command/service-keys last {interval}\"\nmsgstr \"adhoc-command/service-keys 最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:216\nmsgid \"\"\n\"The number of times per interval that the ``service-keys`` command has \"\n\"been executed.\"\nmsgstr \"每个时间间隔执行 ``service-keys`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:216\nmsgid \"\"\n\"``cl-comp/adhoc-command/service-keys last hour`` ``cl-comp/adhoc-command\"\n\"/service-keys last minute`` ``cl-comp/adhoc-command/service-keys last \"\n\"second``\"\nmsgstr \"\"\n\"``cl-comp/adhoc-command/service-keys last hour`` ``cl-comp/adhoc-command/\"\n\"service-keys last minute`` ``cl-comp/adhoc-command/service-keys last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:218\nmsgid \"Adhoc-command/service-keys/Average processing time\"\nmsgstr \"Adhoc-command/service-keys/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:218\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``service-\"\n\"keys`` to execute.\"\nmsgstr \"执行 ``service-keys`` 所需的平均时间（以毫秒为单位），以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:218\nmsgid \"``cl-comp/adhoc-command/service-keys/Average processing time``\"\nmsgstr \"``cl-comp/adhoc-command/service-keys/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:220\nmsgid \"adhoc-command/sim-serv-stopped {interval}\"\nmsgstr \"adhoc-command/sim-serv-stopped {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:220\nmsgid \"\"\n\"The number of times per interval that the ``sim-serv-stopped`` command \"\n\"has been executed.\"\nmsgstr \"每个时间间隔执行 ``sim-serv-stopped`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:220\nmsgid \"\"\n\"``cl-comp/adhoc-command/sim-serv-stopped last hour`` ``cl-comp/adhoc-\"\n\"command/sim-serv-stopped last minute`` ``cl-comp/adhoc-command/sim-serv-\"\n\"stopped last second``\"\nmsgstr \"\"\n\"``cl-comp/adhoc-command/sim-serv-stopped last hour`` ``cl-comp/adhoc-command/\"\n\"sim-serv-stopped last minute`` ``cl-comp/adhoc-command/sim-serv-stopped last \"\n\"second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:222\nmsgid \"Adhoc-command/sim-serv-stopped/Average processing time\"\nmsgstr \"Adhoc-command/sim-serv-stopped/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:222\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``sim-serv-\"\n\"stopped`` to execute.\"\nmsgstr \"执行 ``sim-serv-stopped`` 所需的平均时间（以毫秒为单位），以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:222\nmsgid \"``cl-comp/adhoc-command/sim-serv-stopped/Average processing time``\"\nmsgstr \"``cl-comp/adhoc-command/sim-serv-stopped/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:224\nmsgid \"Average compression ratio\"\nmsgstr \"平均压缩比\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:224\nmsgid \"\"\n\"The average compression ratio of data sent to other clusters during the \"\n\"session.\"\nmsgstr \"会话期间发送到其他集群的数据的平均压缩率。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:224\n#: ../../Tigase_Administration/Statistics_Description.rst:226\n#: ../../Tigase_Administration/Statistics_Description.rst:269\n#: ../../Tigase_Administration/Statistics_Description.rst:273\n#: ../../Tigase_Administration/Statistics_Description.rst:279\n#: ../../Tigase_Administration/Statistics_Description.rst:374\nmsgid \"Float\"\nmsgstr \"浮点数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:224\nmsgid \"``cl-comp/Average compression ratio``\"\nmsgstr \"``cl-comp/Average compression ratio``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:226\nmsgid \"Average decompression ratio\"\nmsgstr \"平均减压比\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:226\nmsgid \"\"\n\"The average compression ratio of data received from other clusters during\"\n\" the session.\"\nmsgstr \"会话期间从其他集群接收到的数据的平均压缩率。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:226\nmsgid \"``cl-comp/Average decompression ratio``\"\nmsgstr \"``cl-comp/Average decompression ratio``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:228\nmsgid \"Known cluster nodes\"\nmsgstr \"已知集群节点\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:228\nmsgid \"The number of cluster nodes currently connected to the server.\"\nmsgstr \"当前连接到服务器的集群节点数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:228\n#: ../../Tigase_Administration/Statistics_Description.rst:271\n#: ../../Tigase_Administration/Statistics_Description.rst:275\n#: ../../Tigase_Administration/Statistics_Description.rst:283\n#: ../../Tigase_Administration/Statistics_Description.rst:285\n#: ../../Tigase_Administration/Statistics_Description.rst:651\n#: ../../Tigase_Administration/Statistics_Description.rst:657\n#: ../../Tigase_Administration/Statistics_Description.rst:659\n#: ../../Tigase_Administration/Statistics_Description.rst:665\nmsgid \"INFO\"\nmsgstr \"信息\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:228\nmsgid \"``cl-comp/Known cluster nodes``\"\nmsgstr \"``cl-comp/Known cluster nodes``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:230\nmsgid \"Last {interval} disconnects\"\nmsgstr \"上次 {interval} 断开连接\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:230\nmsgid \"The number of cluster disconnections within the specified interval.\"\nmsgstr \"指定时间间隔内的集群断开连接数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:230\nmsgid \"Comma Separated Array\"\nmsgstr \"逗号分隔数组\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:230\nmsgid \"day hour\"\nmsgstr \"白天时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:230\nmsgid \"``cl-comp/Last day disconnects`` ``cl-comp/Last hour disconnects``\"\nmsgstr \"``cl-comp/Last day disconnects`` ``cl-comp/Last hour disconnects``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:232\nmsgid \"\"\n\"For day, each array is the number of disconnections each hour, most \"\n\"recent first. For hour each array is the number of disconnections each \"\n\"minute, most recent first.\"\nmsgstr \"对于一天，每个数组是每小时的断开连接数，最近的在前面。对于小时，每个数组是每分钟的断开连接数，最近的在前面。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:234\nmsgid \"Service connected time-outs\"\nmsgstr \"服务连接超时\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:234\nmsgid \"The number of time-outs during connection initialization of cluster nodes.\"\nmsgstr \"集群节点连接初始化期间的超时次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:234\nmsgid \"``cl-comp/Service connected time-outs``\"\nmsgstr \"``cl-comp/Service connected time-outs``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:236\nmsgid \"Total disconnects\"\nmsgstr \"断开连接总数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:236\nmsgid \"The number of clusters that have disconnected during the current session.\"\nmsgstr \"当前会话期间断开连接的集群数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:236\nmsgid \"``cl-comp/Total disconnects``\"\nmsgstr \"``cl-comp/Total disconnects``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:240\nmsgid \"eventbus\"\nmsgstr \"事件总线\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:242\nmsgid \"No exclusive eventbus specific statistics.\"\nmsgstr \"没有专属的事件总线特定的统计数据。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:245\nmsgid \"message-archive\"\nmsgstr \"消息存档\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:250\nmsgid \"Removal time of expired messages (avg)\"\nmsgstr \"过期消息的删除时间（平均）\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:250\nmsgid \"\"\n\"The average amount of time in milliseconds it takes to remove expired \"\n\"messages from the repository. This includes manual and automatic removal \"\n\"of messages.\"\nmsgstr \"从存储库中删除过期消息所需的平均时间（以毫秒为单位）。这包括手动和自动删除消息。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:250\nmsgid \"``message-archive/Removal time of expired messages (avg)``\"\nmsgstr \"``message-archive/Removal time of expired messages (avg)``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:254\nmsgid \"message-router\"\nmsgstr \"消息路由器\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:259\nmsgid \"CPUs no\"\nmsgstr \"CPUs数量\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:259\nmsgid \"The number of CPUs available on the host machine.\"\nmsgstr \"主机上可用的CPU数量。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:259\nmsgid \"``message-router/CPUs no``\"\nmsgstr \"``message-router/CPUs no``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:261\nmsgid \"CPU Usage\"\nmsgstr \"CPU使用率\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:261\n#, python-format\nmsgid \"\"\n\"% of available CPU power used by Tigase Server at the moment statistics \"\n\"are taken.\"\nmsgstr \"获取统计信息时Tigase服务器可用的CPU功率百分比%。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:261\nmsgid \"Float/String\"\nmsgstr \"浮点数/字符串\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:261\nmsgid \"``message-router/CPU usage [%]`` ``message-router/CPU usage``\"\nmsgstr \"``message-router/CPU usage [%]`` ``message-router/CPU usage``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:263\n#, python-format\nmsgid \"\"\n\"Two formats are available for CPU usage: A float integer which expresses \"\n\"a long decimal available from CPU Usage [%], and a string which provides \"\n\"a rounded number with a % sign from CPU usage.\"\nmsgstr \"\"\n\"CPU使用率有两种可用格式：一个浮点整数，表示 CPU 使用率 [%] 中可用的长小数，以及一个字符串，它提供CPU使用率带有 % \"\n\"符号的四舍五入数字。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:265\nmsgid \"Free Heap\"\nmsgstr \"空闲堆\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:265\nmsgid \"The amount of heap memory that is available for use, expressed in KB.\"\nmsgstr \"可供使用的堆内存量，以KB表示。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:265\nmsgid \"``message-router/Free Heap``\"\nmsgstr \"``message-router/Free Heap``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:267\nmsgid \"Free NonHeap\"\nmsgstr \"空闲的非堆\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:267\nmsgid \"The amount of non-heap memory that is available for use, expressed in KB.\"\nmsgstr \"可供使用的非堆内存量，以KB表示。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:267\nmsgid \"``message-router/Free NonHeap``\"\nmsgstr \"``message-router/Free NonHeap``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:269\nmsgid \"HEAP usage [%]\"\nmsgstr \"堆使用率 [%]\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:269\nmsgid \"Total percent of HEAP memory in use by Tigase.\"\nmsgstr \"Tigase使用的HEAP内存的总百分比。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:269\nmsgid \"``message-router/HEAP usage [%]``\"\nmsgstr \"``message-router/HEAP usage [%]``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:271\nmsgid \"Local hostname\"\nmsgstr \"本地主机名\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:271\nmsgid \"The local hostname of the physical server.\"\nmsgstr \"物理服务器的本地主机名。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:271\nmsgid \"``message-router/Local hostname``\"\nmsgstr \"``message-router/Local hostname``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:273\nmsgid \"Load average\"\nmsgstr \"平均负载\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:273\nmsgid \"\"\n\"The average system load for the previous minute. The way in which the \"\n\"load average is calculated is operating system specific but is typically \"\n\"a damped time-dependent average.\"\nmsgstr \"前一分钟的平均系统负载。计算负载平均值的方式是特定于操作系统的，但通常是阻尼的时间相关平均值。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:273\nmsgid \"``message-router/Load average``\"\nmsgstr \"``message-router/Load average``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:275\nmsgid \"Max Heap mem\"\nmsgstr \"最大堆内存\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:275\nmsgid \"\"\n\"Maximum amount of heap memory available as defined by JAVA_OPTIONS in \"\n\"tigase.conf, in Kb.\"\nmsgstr \"由 tigase.conf 中的 JAVA_OPTIONS 定义的可用堆内存的最大量，以Kb为单位。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:275\nmsgid \"``message-router/Max Heap mem``\"\nmsgstr \"``message-router/Max Heap mem``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:277\nmsgid \"Max NonHeap mem\"\nmsgstr \"最大非堆内存\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:277\nmsgid \"\"\n\"Maximum amount of non-heap memory available as defined by JAVA_OPTIONS in\"\n\" tigase.conf, in Kb.\"\nmsgstr \"由 tigase.conf 中的 JAVA_OPTIONS 定义的可用非堆内存的最大数量，以 Kb 为单位。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:277\nmsgid \"``message-router/Max NonHeap mem``\"\nmsgstr \"``message-router/Max NonHeap mem``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:279\nmsgid \"NONHEAP Usage [%]\"\nmsgstr \"非堆使用率 [%]\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:279\nmsgid \"Total amount of NONHEAP memory in use expressed as a percentage.\"\nmsgstr \"正在使用的非堆内存总量以百分比表示。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:279\nmsgid \"``message-router/NONHEAP usage [%]``\"\nmsgstr \"``message-router/NONHEAP usage [%]``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:281\nmsgid \"Threads count\"\nmsgstr \"线程数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:281\nmsgid \"The total number of processing threads available across all components.\"\nmsgstr \"所有组件中可用的处理线程总数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:281\nmsgid \"``message-router/Threads count``\"\nmsgstr \"``message-router/Threads count``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:283\nmsgid \"Uptime\"\nmsgstr \"正常运行时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:283\nmsgid \"The total amount of time the server has been online for this session.\"\nmsgstr \"服务器在此会话中在线的总时间。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:283\nmsgid \"``message-router/Uptime``\"\nmsgstr \"``message-router/Uptime``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:285\nmsgid \"Used Heap\"\nmsgstr \"已用堆的量\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:285\nmsgid \"The amount of heap memory in use in KB.\"\nmsgstr \"正在使用的堆内存量（以 KB 为单位）。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:285\nmsgid \"``message-router/Used Heap``\"\nmsgstr \"``message-router/Used Heap``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:287\nmsgid \"Used NonHeap\"\nmsgstr \"非堆的使用量\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:287\nmsgid \"The amount of non-heap memory in use shown in KB.\"\nmsgstr \"以KB显示的正在使用的非堆内存量。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:287\nmsgid \"``message-router/Used NonHeap``\"\nmsgstr \"``message-router/Used NonHeap``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:292\nmsgid \"monitor\"\nmsgstr \"监视器\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:297\nmsgid \"adhoc-command/load-errors last {interval}\"\nmsgstr \"adhoc-command/load-errors last {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:297\nmsgid \"\"\n\"The number of times per interval that the load-errors command has been \"\n\"executed.\"\nmsgstr \"每个时间间隔执行 load-errors 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:297\nmsgid \"\"\n\"``monitor/adhoc-command/load-errors last hour`` ``monitor/adhoc-command\"\n\"/load-errors last minute`` ``monitor/adhoc-command/load-errors last \"\n\"second``\"\nmsgstr \"\"\n\"``monitor/adhoc-command/load-errors last hour`` ``monitor/adhoc-command/load-\"\n\"errors last minute`` ``monitor/adhoc-command/load-errors last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:299\nmsgid \"Adhoc-command/load-errors/Average processing time\"\nmsgstr \"即席命令/加载错误/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:299\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``load-\"\n\"errors`` to execute.\"\nmsgstr \"``load-errors`` 执行的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:299\nmsgid \"``monitor/adhoc-command/load-errors/Average processing time``\"\nmsgstr \"``monitor/adhoc-command/load-errors/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:303\nmsgid \"muc\"\nmsgstr \"muc\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:308\nmsgid \"adhoc-command/remove-room last {interval}\"\nmsgstr \"adhoc-command/remove-room last {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:308\nmsgid \"\"\n\"The number of times per interval that the remove-room command has been \"\n\"executed.\"\nmsgstr \"每个间隔执行 remove-room 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:308\nmsgid \"\"\n\"``monitor/adhoc-command/remove-room last hour`` ``monitor/adhoc-command\"\n\"/remove-room last minute`` ``monitor/adhoc-command/remove-room last \"\n\"second``\"\nmsgstr \"\"\n\"``monitor/adhoc-command/remove-room last hour`` ``monitor/adhoc-command/\"\n\"remove-room last minute`` ``monitor/adhoc-command/remove-room last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:310\nmsgid \"Adhoc-command/remove-room/Average processing time\"\nmsgstr \"即席命令/remove-room/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:310\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``remove-\"\n\"room`` to execute.\"\nmsgstr \"``remove-room`` 执行所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:310\nmsgid \"``monitor/adhoc-command/remove-room/Average processing time``\"\nmsgstr \"``monitor/adhoc-command/remove-room/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:312\nmsgid \"adhoc-command/default-room-config last {interval}\"\nmsgstr \"adhoc-command/default-room-config last {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:312\nmsgid \"\"\n\"The number of times per interval that the default-room-command command \"\n\"has been executed.\"\nmsgstr \"每个时间间隔执行 default-room-command 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:312\nmsgid \"\"\n\"``muc/adhoc-command/default-room-config last hour`` ``muc/adhoc-command\"\n\"/default-room-config last minute`` ``muc/adhoc-command/default-room-\"\n\"config last second``\"\nmsgstr \"\"\n\"``muc/adhoc-command/default-room-config last hour`` ``muc/adhoc-command/\"\n\"default-room-config last minute`` ``muc/adhoc-command/default-room-config \"\n\"last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:314\nmsgid \"Adhoc-command/default-room-config/Average processing time\"\nmsgstr \"即席命令/default-room-config/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:314\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``default-\"\n\"room-config`` to execute.\"\nmsgstr \"执行 ``default-room-config`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:314\nmsgid \"``muc/adhoc-command/default-room-config/Average processing time``\"\nmsgstr \"``muc/adhoc-command/default-room-config/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:318\nmsgid \"proxy\"\nmsgstr \"代理\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:323\nmsgid \"Average transfer size in KB\"\nmsgstr \"以KB为单位的平均传输大小\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:323\nmsgid \"\"\n\"Average size of packets sent through the proxy component during the \"\n\"current session.\"\nmsgstr \"当前会话期间通过代理组件发送的数据包的平均大小。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:323\nmsgid \"``proxy/Average transfer size in KB``\"\nmsgstr \"``proxy/Average transfer size in KB``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:325\nmsgid \"KBytes transferred\"\nmsgstr \"传输的千字节\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:325\nmsgid \"Total number of Kb transferred through the proxy component.\"\nmsgstr \"通过代理组件传输的Kb总数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:325\nmsgid \"``proxy/KBytes transferred``\"\nmsgstr \"``proxy/KBytes transferred``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:327\nmsgid \"Open streams\"\nmsgstr \"打开流\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:327\nmsgid \"Number of currently open proxy streams.\"\nmsgstr \"当前打开的代理流的数量。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:327\nmsgid \"``proxy/Open streams``\"\nmsgstr \"``proxy/Open streams``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:329\nmsgid \"Transfers completed\"\nmsgstr \"传输完成\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:329\nmsgid \"Number of specific transfers completed through proxy component.\"\nmsgstr \"通过代理组件完成的特定传输数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:329\nmsgid \"``proxy/Transfers completed``\"\nmsgstr \"``proxy/Transfers completed``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:333\nmsgid \"pubsub\"\nmsgstr \"pubsub\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:338\nmsgid \"Added new nodes\"\nmsgstr \"添加了新节点\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:338\nmsgid \"\"\n\"The total number of new nodes that has been added in the current server \"\n\"instance. This statistic is reset when the server resets.\"\nmsgstr \"当前服务器实例中已添加的新节点总数。服务器重置时会重置此统计信息。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:338\nmsgid \"``pubsub/Added new nodes``\"\nmsgstr \"``pubsub/Added new nodes``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:340\nmsgid \"adhoc-command/delete-item last {interval}\"\nmsgstr \"即席命令/删除项目最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:340\nmsgid \"\"\n\"The number of times per interval that the ``delete-item`` command has \"\n\"been executed.\"\nmsgstr \"每个时间间隔执行 ``delete-item`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:340\nmsgid \"\"\n\"``pubsub/adhoc-command/delete-item last hour`` ``pubsub/adhoc-command\"\n\"/delete-item last minute`` ``pubsub/adhoc-command/delete-item last \"\n\"second``\"\nmsgstr \"\"\n\"``pubsub/adhoc-command/delete-item last hour`` ``pubsub/adhoc-command/delete-\"\n\"item last minute`` ``pubsub/adhoc-command/delete-item last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:342\nmsgid \"adhoc-command/delete-item/Average processing time\"\nmsgstr \"即席命令/删除项目/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:342\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``delete-\"\n\"item`` to execute.\"\nmsgstr \"执行 ``delete-item`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:342\nmsgid \"``pubsub/adhoc-command/delete-item/Average processing time``\"\nmsgstr \"``pubsub/adhoc-command/delete-item/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:344\nmsgid \"adhoc-command/delete-node last {interval}\"\nmsgstr \"即席命令/删除节点最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:344\nmsgid \"\"\n\"The number of times per interval that the ``delete-node`` command has \"\n\"been executed.\"\nmsgstr \"每个时间间隔执行 ``delete-node`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:344\nmsgid \"\"\n\"``pubsub/adhoc-command/delete-node last hour`` ``pubsub/adhoc-command\"\n\"/delete-node last minute`` ``pubsub/adhoc-command/delete-node last \"\n\"second``\"\nmsgstr \"\"\n\"``pubsub/adhoc-command/delete-node last hour`` ``pubsub/adhoc-command/delete-\"\n\"node last minute`` ``pubsub/adhoc-command/delete-node last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:346\nmsgid \"adhoc-command/delete-node/Average processing time\"\nmsgstr \"即席命令/删除节点/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:346\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``delete-\"\n\"node`` to execute.\"\nmsgstr \"执行 ``delete-node`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:346\nmsgid \"``pubsub/adhoc-command/delete-node/Average processing time``\"\nmsgstr \"``pubsub/adhoc-command/delete-node/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:348\nmsgid \"adhoc-command/list-items last {interval}\"\nmsgstr \"即席命令/列表项最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:348\nmsgid \"\"\n\"The number of times per interval that the ``list-items`` command has been\"\n\" executed.\"\nmsgstr \"每个时间间隔执行 ``list-items`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:348\nmsgid \"\"\n\"``pubsub/adhoc-command/list-items last hour`` ``pubsub/adhoc-command\"\n\"/list-items last minute`` ``pubsub/adhoc-command/list-items last second``\"\nmsgstr \"\"\n\"``pubsub/adhoc-command/list-items last hour`` ``pubsub/adhoc-command/list-\"\n\"items last minute`` ``pubsub/adhoc-command/list-items last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:350\nmsgid \"adhoc-command/list-items/Average processing time\"\nmsgstr \"即席命令/列表项/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:350\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``list-\"\n\"items`` to execute.\"\nmsgstr \"执行 ``list-items`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:350\nmsgid \"``pubsub/adhoc-command/list-items/Average processing time``\"\nmsgstr \"``pubsub/adhoc-command/list-items/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:352\nmsgid \"adhoc-command/list-nodes last {interval}\"\nmsgstr \"即席命令/列表节点最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:352\nmsgid \"\"\n\"The number of times per interval that the ``list-nodes`` command has been\"\n\" executed.\"\nmsgstr \"每个时间间隔执行 ``list-nodes`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:352\nmsgid \"\"\n\"``pubsub/adhoc-command/list-nodes last hour`` ``pubsub/adhoc-command\"\n\"/list-nodes last minute`` ``pubsub/adhoc-command/list-nodes last second``\"\nmsgstr \"\"\n\"``pubsub/adhoc-command/list-nodes last hour`` ``pubsub/adhoc-command/list-\"\n\"nodes last minute`` ``pubsub/adhoc-command/list-nodes last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:354\nmsgid \"adhoc-command/list-nodes/Average processing time\"\nmsgstr \"即席命令/列表节点/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:354\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``list-\"\n\"nodes`` to execute.\"\nmsgstr \"执行 ``list-nodes`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:354\nmsgid \"``pubsub/adhoc-command/list-nodes/Average processing time``\"\nmsgstr \"``pubsub/adhoc-command/list-nodes/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:356\nmsgid \"adhoc-command/publish-item last {interval}\"\nmsgstr \"即席命令/发布项目最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:356\nmsgid \"\"\n\"The number of times per interval that the ``publish-item`` command has \"\n\"been executed.\"\nmsgstr \"每个时间间隔执行 ``publish-item`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:356\nmsgid \"\"\n\"``pubsub/adhoc-command/publish-item last hour`` ``pubsub/adhoc-command\"\n\"/publish-item last minute`` ``pubsub/adhoc-command/publish-item last \"\n\"second``\"\nmsgstr \"\"\n\"``pubsub/adhoc-command/publish-item last hour`` ``pubsub/adhoc-command/\"\n\"publish-item last minute`` ``pubsub/adhoc-command/publish-item last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:358\nmsgid \"adhoc-command/publish-item/Average processing time\"\nmsgstr \"即席命令/发布项目/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:358\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``publish-\"\n\"item`` to execute.\"\nmsgstr \"执行 ``publish-item`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:358\nmsgid \"``pubsub/adhoc-command/publish-item/Average processing time``\"\nmsgstr \"``pubsub/adhoc-command/publish-item/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:360\nmsgid \"adhoc-command/retrieve-item last {interval}\"\nmsgstr \"即席命令/检索项目最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:360\nmsgid \"\"\n\"The number of times per interval that the ``retrieve-item`` command has \"\n\"been executed.\"\nmsgstr \"每个时间间隔执行 ``retrieve-item`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:360\nmsgid \"\"\n\"``pubsub/adhoc-command/retrieve-item last hour`` ``pubsub/adhoc-command\"\n\"/retrieve-item last minute`` ``pubsub/adhoc-command/retrieve-item last \"\n\"second``\"\nmsgstr \"\"\n\"``pubsub/adhoc-command/retrieve-item last hour`` ``pubsub/adhoc-command/\"\n\"retrieve-item last minute`` ``pubsub/adhoc-command/retrieve-item last \"\n\"second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:362\nmsgid \"adhoc-command/retrieve-item/Average processing time\"\nmsgstr \"即席命令/检索项目/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:362\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``retrieve-\"\n\"item`` to execute.\"\nmsgstr \"执行 ``retrieve-item`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:362\nmsgid \"``pubsub/adhoc-command/retrieve-item/Average processing time``\"\nmsgstr \"``pubsub/adhoc-command/retrieve-item/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:364\nmsgid \"AdHocConfigCommandModule last {interval}\"\nmsgstr \"AdHocConfigCommandModule最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:364\nmsgid \"\"\n\"The number of times per interval that the ``AdHocConfigCommandModule`` \"\n\"command has been executed.\"\nmsgstr \"每个时间间隔执行 ``AdHocConfigCommandModule`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:364\nmsgid \"\"\n\"``pubsub/AdHocConfigCommandModule last hour`` \"\n\"``pubsub/AdHocConfigCommandModule last minute`` \"\n\"``pubsub/AdHocConfigCommandModule last second``\"\nmsgstr \"\"\n\"``pubsub/AdHocConfigCommandModule last hour`` ``pubsub/\"\n\"AdHocConfigCommandModule last minute`` ``pubsub/AdHocConfigCommandModule \"\n\"last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:366\nmsgid \"AdHocConfigCommandModule/Average processing time\"\nmsgstr \"AdHocConfigCommandModule/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:366\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``AdHocConfigCommandModule`` to execute.\"\nmsgstr \"执行 ``AdHocConfigCommandModule`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:366\nmsgid \"``pubsub/AdHocConfigCommandModule/Average processing time``\"\nmsgstr \"``pubsub/AdHocConfigCommandModule/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:368\nmsgid \"Affiliations count (in cache)\"\nmsgstr \"隶属关系计数（在缓存中）\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:368\nmsgid \"\"\n\"The total number of pubsub affiliations that are resident in cache \"\n\"memory. Affiliations include JIDs that are one of the following; Owner, \"\n\"Publisher, Publish-Only, Member, None, Outcast. This may not reflect \"\n\"total pubsub affiliations in repository.\"\nmsgstr \"驻留在高速缓存内存中的pubsub关联的总数。隶属关系包括属于以下之一的JID；所有者，发布者，仅发布，成员，无，弃儿。这可能无法反映存储库中的总pubsub隶属关系。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:368\nmsgid \"``pubsub/Affiliations count (in cache)``\"\nmsgstr \"``pubsub/Affiliations count (in cache)``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:370\nmsgid \"Average DB write time [ms]\"\nmsgstr \"数据库平均写入时间 [ms]\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:370\nmsgid \"\"\n\"The average time of all DB writes from PubSub component. Average is \"\n\"calculated using two other statistics: (Total writing time / Database \"\n\"writes)\"\nmsgstr \"来自PubSub组件的所有数据库写入的平均时间。使用其他两个统计数据计算平均值：（总写入时间/数据库写入时间）\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:370\nmsgid \"``pubsub/Average DB write time [ms]``\"\nmsgstr \"``pubsub/Average DB write time [ms]``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:372\nmsgid \"cache/hits last {interval}\"\nmsgstr \"缓存/命中的最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:372\nmsgid \"\"\n\"The number of times the cache has achieved a hit within the last \"\n\"interval. A hit is when a request for information is matched to data that\"\n\" is inside the cache memory.\"\nmsgstr \"缓存在最后一个时间间隔内达到命中的次数。当信息请求与高速缓存内存中的数据相匹配时，就会发生命中。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:372\nmsgid \"\"\n\"``pubsub/cache/hits last hour`` ``pubsub/cache/hits last minute`` \"\n\"``pubsub/cache/hits last second``\"\nmsgstr \"\"\n\"``pubsub/cache/hits last hour`` ``pubsub/cache/hits last minute`` ``pubsub/\"\n\"cache/hits last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:374\nmsgid \"cache/hit-miss ratio per {interval}\"\nmsgstr \"每个 {interval} 的缓存/命中率\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:374\nmsgid \"\"\n\"The ratio of cache hits to cache misses over the specified period. A \"\n\"cache hit is when a request for information from the cache is matched \"\n\"with information in the cache. A miss is when that information request \"\n\"cannot find a match in cache. A miss only indicates that that information\"\n\" was not found in the cache, not that it is not in the repository.\"\nmsgstr \"指定时间段内缓存命中与缓存未命中的比率。缓存命中是指从缓存中获取信息的请求与缓存中的信息相匹配。未命中是指该信息请求在缓存中找不到匹配项。未命中仅表示在缓存中未找到该信息，而不是不在存储库中。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:374\nmsgid \"hour minute\"\nmsgstr \"小时 分钟\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:374\nmsgid \"\"\n\"``pubsub/cache/hit-miss ratio per hour`` ``pubsub/cache/hit-miss ratio \"\n\"per minute``\"\nmsgstr \"\"\n\"``pubsub/cache/hit-miss ratio per hour`` ``pubsub/cache/hit-miss ratio per \"\n\"minute``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:376\nmsgid \"cache/requests last {interval}\"\nmsgstr \"最后 {interval} 缓存/请求\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:376\nmsgid \"The number of memory cache requests made within the last interval.\"\nmsgstr \"在最后一个时间间隔内发出的内存缓存请求数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:376\nmsgid \"\"\n\"``pubsub/cache/Requests last hour`` ``pubsub/cache/Requests last minute``\"\n\" ``pubsub/cache/Requests last second``\"\nmsgstr \"\"\n\"``pubsub/cache/Requests last hour`` ``pubsub/cache/Requests last minute`` ``\"\n\"pubsub/cache/Requests last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:378\nmsgid \"Cached nodes\"\nmsgstr \"缓存节点\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:378\nmsgid \"The number of nodes that is currently in memory cache.\"\nmsgstr \"当前在内存缓存中的节点数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:378\nmsgid \"``pubsub/Cached nodes``\"\nmsgstr \"``pubsub/Cached nodes``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:380\nmsgid \"CapsModule\"\nmsgstr \"CapsModule\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:380\nmsgid \"\"\n\"The number of times per interval that the CapsModule command has been \"\n\"executed.\"\nmsgstr \"CapsModule 命令在每个时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:380\nmsgid \"\"\n\"``pubsub/CapsModule last hour`` ``pubsub/CapsModule last minute`` \"\n\"``pubsub/CapsModule last second``\"\nmsgstr \"\"\n\"``pubsub/CapsModule last hour`` ``pubsub/CapsModule last minute`` ``pubsub/\"\n\"CapsModule last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:382\nmsgid \"CapsModule/Average processing time\"\nmsgstr \"CapsModule/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:382\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``CapsModule`` to execute.\"\nmsgstr \"执行 ``CapsModule`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:382\nmsgid \"``pubsub/CapsModule/Average processing time``\"\nmsgstr \"``pubsub/CapsModule/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:384\nmsgid \"db/GetNodeItems requests last {interval}\"\nmsgstr \"db/GetNodeItems请求最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:384\nmsgid \"\"\n\"The number of times ``GetNodeItems`` command has been run within the \"\n\"specified interval.\"\nmsgstr \"``GetNodeItems`` 命令在指定时间间隔内运行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:384\nmsgid \"\"\n\"``pubsub/db/GetNodeItems last hour`` ``pubsub/db/GetNodeItems last \"\n\"minute`` ``pubsub/db/GetNodeItems last second``\"\nmsgstr \"\"\n\"``pubsub/db/GetNodeItems last hour`` ``pubsub/db/GetNodeItems last minute`` \"\n\"``pubsub/db/GetNodeItems last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:386\nmsgid \"db/GetNodeItems/Average processing time\"\nmsgstr \"db/GetNodeItems/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:386\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``GetNodeItems`` to execute.\"\nmsgstr \"执行 ``GetNodeItems`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:386\nmsgid \"``pubsub/db/GetNodeItems/Average processing time``\"\nmsgstr \"``pubsub/db/GetNodeItems/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:388\nmsgid \"DefaultConfigModule last {interval}\"\nmsgstr \"DefaultConfigModule 最后{interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:388\nmsgid \"\"\n\"The number of times per interval that the ``DefaultConfigModule`` command\"\n\" has been executed.\"\nmsgstr \"每个时间间隔执行 ``DefaultConfigModule`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:388\nmsgid \"\"\n\"``pubsub/DefaultConfigModule last hour`` ``pubsub/DefaultConfigModule \"\n\"last minute`` ``pubsub/DefaultConfigModule last second``\"\nmsgstr \"\"\n\"``pubsub/DefaultConfigModule last hour`` ``pubsub/DefaultConfigModule last \"\n\"minute`` ``pubsub/DefaultConfigModule last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:390\nmsgid \"DefaultConfigModule/Average processing time\"\nmsgstr \"DefaultConfigModule/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:390\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``DefaultConfigModule`` to execute.\"\nmsgstr \"执行 ``DefaultConfigModule`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:390\nmsgid \"``pubsub/DefaultConfigModule/Average processing time``\"\nmsgstr \"``pubsub/DefaultConfigModule/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:392\nmsgid \"DiscoverInfoModule last {interval}\"\nmsgstr \"DiscoverInfoModule 最后{interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:392\nmsgid \"\"\n\"The number of times per interval that the DiscoverInfoModule command has \"\n\"been executed.\"\nmsgstr \"每个间隔执行DiscoverInfoModule命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:392\nmsgid \"\"\n\"``pubsub/DiscoverInfoModule last hour`` ``pubsub/DiscoverInfoModule last \"\n\"minute`` ``pubsub/DiscoverInfoModule last second``\"\nmsgstr \"\"\n\"``pubsub/DiscoverInfoModule last hour`` ``pubsub/DiscoverInfoModule last \"\n\"minute`` ``pubsub/DiscoverInfoModule last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:394\nmsgid \"DiscoverInfoModule/Average processing time\"\nmsgstr \"DiscoverInfoModule/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:394\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``DiscoverInfoModule`` to execute.\"\nmsgstr \"执行 ``DiscoverInfoModule`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:394\nmsgid \"``pubsub/DiscoverInfoModule/Average processing time``\"\nmsgstr \"``pubsub/DiscoverInfoModule/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:396\nmsgid \"DiscoverItemsModule last {interval}\"\nmsgstr \"DiscoverItemsModule 最后{interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:396\nmsgid \"\"\n\"The number of times per interval that the DiscoverItemsModule command has\"\n\" been executed.\"\nmsgstr \"每个间隔执行 DiscoverItemsModule 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:396\nmsgid \"\"\n\"``pubsub/DiscoverItemsModule last hour`` ``pubsub/DiscoverItemsModule \"\n\"last minute`` ``pubsub/DiscoverItemsModule last second``\"\nmsgstr \"\"\n\"``pubsub/DiscoverItemsModule last hour`` ``pubsub/DiscoverItemsModule last \"\n\"minute`` ``pubsub/DiscoverItemsModule last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:398\nmsgid \"DiscoverItemsModule/Average processing time\"\nmsgstr \"DiscoverItemsModule/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:398\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``DiscoverItemsModule`` to execute.\"\nmsgstr \"执行 ``DiscoverItemsModule`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:398\nmsgid \"``pubsub/DiscoverItemsModule/Average processing time``\"\nmsgstr \"``pubsub/DiscoverItemsModule/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:400\nmsgid \"JabberVersionModule last {interval}\"\nmsgstr \"JabberVersionModule 最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:400\nmsgid \"\"\n\"The number of times per interval that the ``JabberVersionModule`` command\"\n\" has been executed.\"\nmsgstr \"每个时间间隔执行 ``JabberVersionModule`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:400\nmsgid \"\"\n\"``pubsub/JabberVersionModule last hour`` ``pubsub/JabberVersionModule \"\n\"last minute`` ``pubsub/JabberVersionModule last second``\"\nmsgstr \"\"\n\"``pubsub/JabberVersionModule last hour`` ``pubsub/JabberVersionModule last \"\n\"minute`` ``pubsub/JabberVersionModule last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:402\nmsgid \"JabberVersionModule/Average processing time\"\nmsgstr \"JabberVersionModule/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:402\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``JabberVersionModule`` to execute.\"\nmsgstr \"执行 ``JabberVersionModule`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:402\nmsgid \"``pubsub/JabberVersionModule/Average processing time``\"\nmsgstr \"``pubsub/JabberVersionModule/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:404\nmsgid \"ManageAffiiationsModule last {interval}\"\nmsgstr \"ManageAffiiationsModule 最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:404\nmsgid \"\"\n\"The number of times per interval that the ``ManageAffiliationsModule`` \"\n\"command has been executed.\"\nmsgstr \"每个时间间隔执行 ``ManageAffiliationsModule`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:404\nmsgid \"\"\n\"``pubsub/ManageAffiliationsModule last hour`` \"\n\"``pubsub/ManageAffiliationsModule last minute`` \"\n\"``pubsub/ManageAffiliationsModule last second``\"\nmsgstr \"\"\n\"``pubsub/ManageAffiliationsModule last hour`` ``pubsub/\"\n\"ManageAffiliationsModule last minute`` ``pubsub/ManageAffiliationsModule \"\n\"last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:406\nmsgid \"ManageAffiliationsModule/Average processing time\"\nmsgstr \"ManageAffiliationsModule/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:406\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``ManageAffiliationsModule`` to execute.\"\nmsgstr \"执行 ``ManageAffiliationsModule`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:406\nmsgid \"``pubsub/ManageAffiliationsModule/Average processing time``\"\nmsgstr \"``pubsub/ManageAffiliationsModule/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:408\nmsgid \"ManageSubscriptionModule last {interval}\"\nmsgstr \"ManageSubscriptionModule 最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:408\nmsgid \"\"\n\"The number of times per interval that the ``ManageSubscriptionModule`` \"\n\"command has been executed.\"\nmsgstr \"每个时间间隔执行 ``ManageSubscriptionModule`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:408\nmsgid \"\"\n\"``pubsub/ManageSubscriptionModule last hour`` \"\n\"``pubsub/ManageSubscriptionModule last minute`` \"\n\"``pubsub/ManageSubscriptionModule last second``\"\nmsgstr \"\"\n\"``pubsub/ManageSubscriptionModule last hour`` ``pubsub/\"\n\"ManageSubscriptionModule last minute`` ``pubsub/ManageSubscriptionModule \"\n\"last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:410\nmsgid \"ManageSubscriptionModule/Average processing time\"\nmsgstr \"ManageSubscriptionModule/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:410\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``ManageSubscriptionModule`` to execute.\"\nmsgstr \"执行 ``ManageSubscriptionModule`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:410\nmsgid \"``pubsub/ManageSubscriptionModule/Average processing time``\"\nmsgstr \"``pubsub/ManageSubscriptionModule/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:412\nmsgid \"NodeConfigModule last {interval}\"\nmsgstr \"NodeConfigModule 最后{interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:412\nmsgid \"\"\n\"The number of times per interval that the ``NodeConfigModule`` command \"\n\"has been executed.\"\nmsgstr \"每个时间间隔执行 ``NodeConfigModule`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:412\nmsgid \"\"\n\"``pubsub/NodeConfigModule last hour`` ``pubsub/NodeConfigModule last \"\n\"minute`` ``pubsub/NodeConfigModule last second``\"\nmsgstr \"\"\n\"``pubsub/NodeConfigModule last hour`` ``pubsub/NodeConfigModule last minute``\"\n\" ``pubsub/NodeConfigModule last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:414\nmsgid \"NodeConfigModule/Average processing time\"\nmsgstr \"NodeConfigModule/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:414\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``NodeConfigModule`` to execute.\"\nmsgstr \"执行 ``NodeConfigModule`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:414\nmsgid \"``pubsub/NodeConfigModule/Average processing time``\"\nmsgstr \"``pubsub/NodeConfigModule/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:416\nmsgid \"NodeCreateModule last {interval}\"\nmsgstr \"NodeCreateModule 最后{interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:416\nmsgid \"\"\n\"The number of times per interval that the ``NodeCreateModule`` command \"\n\"has been executed.\"\nmsgstr \"每个时间间隔执行 ``NodeCreateModule`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:416\nmsgid \"\"\n\"``pubsub/NodeCreateModule last hour`` ``pubsub/NodeCreateModule last \"\n\"minute`` ``pubsub/NodeCreateModule last second``\"\nmsgstr \"\"\n\"``pubsub/NodeCreateModule last hour`` ``pubsub/NodeCreateModule last minute``\"\n\" ``pubsub/NodeCreateModule last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:418\nmsgid \"NodeCreateModule/Average processing time\"\nmsgstr \"NodeCreateModule/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:418\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``NodeCreateModule`` to execute.\"\nmsgstr \"执行 ``NodeCreateModule`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:418\nmsgid \"``pubsub/NodeCreateModule/Average processing time``\"\nmsgstr \"``pubsub/NodeCreateModule/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:420\nmsgid \"NodeDeleteModule last {interval}\"\nmsgstr \"NodeDeleteModule 最后{interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:420\nmsgid \"\"\n\"The number of times per interval that the ``NodeDeleteModule`` command \"\n\"has been executed.\"\nmsgstr \"每个时间间隔执行 ``NodeDeleteModule`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:420\nmsgid \"\"\n\"``pubsub/NodeDeleteModule last hour`` ``pubsub/NodeDeleteModule last \"\n\"minute`` ``pubsub/NodeDeleteModule last second``\"\nmsgstr \"\"\n\"``pubsub/NodeDeleteModule last hour`` ``pubsub/NodeDeleteModule last minute``\"\n\" ``pubsub/NodeDeleteModule last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:422\nmsgid \"NodeDeleteModule/Average processing time\"\nmsgstr \"NodeDeleteModule/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:422\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``NodeDeleteModule`` to execute.\"\nmsgstr \"执行 ``NodeDeleteModule`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:422\nmsgid \"``pubsub/NodeDeleteModule/Average processing time``\"\nmsgstr \"``pubsub/NodeDeleteModule/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:424\nmsgid \"PresenceCollectorModule last {interval}\"\nmsgstr \"PresenceCollectorModule 最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:424\nmsgid \"\"\n\"The number of times per interval that the ``PresenceCollectorModule`` \"\n\"command has been executed.\"\nmsgstr \"每个时间间隔执行 ``PresenceCollectorModule`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:424\nmsgid \"\"\n\"``pubsub/PresenceCollectorModule last hour`` \"\n\"``pubsub/PresenceCollectorModule last minute`` \"\n\"``pubsub/PresenceCollectorModule last second``\"\nmsgstr \"\"\n\"``pubsub/PresenceCollectorModule last hour`` ``pubsub/\"\n\"PresenceCollectorModule last minute`` ``pubsub/PresenceCollectorModule last \"\n\"second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:426\nmsgid \"PresenceCollectorModule/Average processing time\"\nmsgstr \"PresenceCollectorModule/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:426\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``PresenceCollectorModule`` to execute.\"\nmsgstr \"执行 ``PresenceCollectorModule`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:426\nmsgid \"``pubsub/PresenceCollectorModule/Average processing time``\"\nmsgstr \"``pubsub/PresenceCollectorModule/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:428\nmsgid \"PendingSubscriptionModule last {interval}\"\nmsgstr \"PendingSubscriptionModule 最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:428\nmsgid \"\"\n\"The number of times per interval that the ``PendingSubscriptionModule`` \"\n\"command has been executed.\"\nmsgstr \"每个时间间隔执行 ``PendingSubscriptionModule`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:428\nmsgid \"\"\n\"``pubsub/PendingSubscriptionModule last hour`` \"\n\"``pubsub/PendingSubscriptionModule last minute`` \"\n\"``pubsub/PendingSubscriptionModule last second``\"\nmsgstr \"\"\n\"``pubsub/PendingSubscriptionModule last hour`` ``pubsub/\"\n\"PendingSubscriptionModule last minute`` ``pubsub/PendingSubscriptionModule \"\n\"last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:430\nmsgid \"PendingSubscriptionModule/Average processing time\"\nmsgstr \"PendingSubscriptionModule/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:430\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``PendingSubscriptionModule`` to execute.\"\nmsgstr \"执行 ``PendingSubscriptionModule`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:430\nmsgid \"``pubsub/PendingSubscriptionModule/Average processing time``\"\nmsgstr \"``pubsub/PendingSubscriptionModule/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:432\nmsgid \"PresenceNotifierModule last {interval}\"\nmsgstr \"PresenceNotifierModule 最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:432\nmsgid \"\"\n\"The number of times per interval that the ``PresenceNotifierModule`` \"\n\"command has been executed.\"\nmsgstr \"每个时间间隔执行 ``PresenceNotifierModule`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:432\nmsgid \"\"\n\"``pubsub/PresenceNotifierModule last hour`` \"\n\"``pubsub/PresenceNotifierModule last minute`` \"\n\"``pubsub/PresenceNotifierModule last second``\"\nmsgstr \"\"\n\"``pubsub/PresenceNotifierModule last hour`` ``pubsub/PresenceNotifierModule \"\n\"last minute`` ``pubsub/PresenceNotifierModule last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:434\nmsgid \"PresenceNotifierModule/Average processing time\"\nmsgstr \"PresenceNotifierModule/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:434\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``PresenceNotifierModule`` to execute.\"\nmsgstr \"执行 ``PresenceNotifierModule`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:434\nmsgid \"``pubsub/PresenceNotifierModule/Average processing time``\"\nmsgstr \"``pubsub/PresenceNotifierModule/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:436\nmsgid \"PublishItemModule last {interval}\"\nmsgstr \"PublishItemModule最后{interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:436\nmsgid \"\"\n\"The number of times per interval that the ``PublishItemModule`` command \"\n\"has been executed.\"\nmsgstr \"每个时间间隔执行 ``PublishItemModule`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:436\nmsgid \"\"\n\"``pubsub/PublishItemModule last hour`` ``pubsub/PublishItemModule last \"\n\"minute`` ``pubsub/PublishItemModule last second``\"\nmsgstr \"\"\n\"``pubsub/PublishItemModule last hour`` ``pubsub/PublishItemModule last \"\n\"minute`` ``pubsub/PublishItemModule last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:438\nmsgid \"PublishItemModule/Average processing time\"\nmsgstr \"PublishItemModule/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:438\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``PublishItemModule`` to execute.\"\nmsgstr \"执行 ``PublishItemModule`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:438\nmsgid \"``pubsub/PublishItemModule/Average processing time``\"\nmsgstr \"``pubsub/PublishItemModule/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:440\nmsgid \"PurgeItemsModule last {interval}\"\nmsgstr \"PurgeItemsModule 最后{interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:440\nmsgid \"\"\n\"The number of times per interval that the ``PurgeItemsModule`` command \"\n\"has been executed.\"\nmsgstr \"每个时间间隔执行 ``PurgeItemsModule`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:440\nmsgid \"\"\n\"``pubsub/PurgeItemsModule last hour`` ``pubsub/PurgeItemsModule last \"\n\"minute`` ``pubsub/PurgeItemsModule last second``\"\nmsgstr \"\"\n\"``pubsub/PurgeItemsModule last hour`` ``pubsub/PurgeItemsModule last minute``\"\n\" ``pubsub/PurgeItemsModule last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:442\nmsgid \"PurgeItemsModule/Average processing time\"\nmsgstr \"PurgeItemsModule/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:442\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``PurgeItemsModule`` to execute.\"\nmsgstr \"执行 ``PurgeItemsModule`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:442\nmsgid \"``pubsub/PurgeItemsModule/Average processing time``\"\nmsgstr \"``pubsub/PurgeItemsModule/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:444\nmsgid \"Repository writes\"\nmsgstr \"存储库写入\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:444\nmsgid \"\"\n\"Number of individual writes to Repository from the pubsub component since\"\n\" startup.\"\nmsgstr \"自启动以来从pubsub组件对存储库的单个写入次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:444\nmsgid \"``pubsub/Repository writes``\"\nmsgstr \"``pubsub/Repository writes``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:446\nmsgid \"RetractItemModule last {interval}\"\nmsgstr \"RetractItemModule 最后{interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:446\nmsgid \"\"\n\"The number of times per interval that the ``RetractItemModule`` command \"\n\"has been executed.\"\nmsgstr \"每个时间间隔执行 ``RetractItemModule`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:446\nmsgid \"\"\n\"``pubsub/RetractItemModule last hour`` ``pubsub/RetractItemModule last \"\n\"minute`` ``pubsub/RetractItemModule last second``\"\nmsgstr \"\"\n\"``pubsub/RetractItemModule last hour`` ``pubsub/RetractItemModule last \"\n\"minute`` ``pubsub/RetractItemModule last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:448\nmsgid \"RetractItemModule/Average processing time\"\nmsgstr \"RetractItemModule/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:448\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``RetractItemModule`` to execute.\"\nmsgstr \"执行 ``RetractItemModule`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:448\nmsgid \"``pubsub/RetractItemModule/Average processing time``\"\nmsgstr \"``pubsub/RetractItemModule/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:450\nmsgid \"RetrieveAffiliationsModule last {interval}\"\nmsgstr \"RetrieveAffiliationsModule最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:450\nmsgid \"\"\n\"The number of times per interval that the ``RetrieveAffiliationsModule`` \"\n\"command has been executed.\"\nmsgstr \"每个时间间隔执行 ``RetrieveAffiliationsModule`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:450\nmsgid \"\"\n\"``pubsub/RetrieveAffiliationsModule last hour`` \"\n\"``pubsub/RetrieveAffiliationsModule last minute`` \"\n\"``pubsub/RetrieveAffiliationsModule last second``\"\nmsgstr \"\"\n\"``pubsub/RetrieveAffiliationsModule last hour`` ``pubsub/\"\n\"RetrieveAffiliationsModule last minute`` ``pubsub/RetrieveAffiliationsModule \"\n\"last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:452\nmsgid \"RetrieveAffiliationsModule/Average processing time\"\nmsgstr \"RetrieveAffiliationsModule/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:452\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``RetrieveAffiliationsModule`` to execute.\"\nmsgstr \"执行 ``RetrieveAffiliationsModule`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:452\nmsgid \"``pubsub/RetrieveAffiliationsModule/Average processing time``\"\nmsgstr \"``pubsub/RetrieveAffiliationsModule/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:454\nmsgid \"RetrieveItemsModule last {interval}\"\nmsgstr \"RetrieveItemsModule 最后{interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:454\nmsgid \"\"\n\"The number of times per interval that the ``RetrieveItemsModule`` command\"\n\" has been executed.\"\nmsgstr \"每个时间间隔执行 ``RetrieveItemsModule`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:454\nmsgid \"\"\n\"``pubsub/RetrieveItemsModule last hour`` ``pubsub/RetrieveItemsModule \"\n\"last minute`` ``pubsub/RetrieveItemsModule last second``\"\nmsgstr \"\"\n\"``pubsub/RetrieveItemsModule last hour`` ``pubsub/RetrieveItemsModule last \"\n\"minute`` ``pubsub/RetrieveItemsModule last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:456\nmsgid \"RetrieveItemsModule/Average processing time\"\nmsgstr \"RetrieveItemsModule/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:456\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``RetrieveItemsModule`` to execute.\"\nmsgstr \"执行 ``RetrieveItemsModule`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:456\nmsgid \"``pubsub/RetrieveItemsModule/Average processing time``\"\nmsgstr \"``pubsub/RetrieveItemsModule/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:458\nmsgid \"RetrieveSubscriptionsModule last {interval}\"\nmsgstr \"RetrieveSubscriptionsModule最后{interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:458\nmsgid \"\"\n\"The number of times per interval that the ``RetrieveSubscriptionsModule``\"\n\" command has been executed.\"\nmsgstr \"每个时间间隔执行 ``RetrieveSubscriptionsModule`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:458\nmsgid \"\"\n\"``pubsub/RetrieveSubscriptionsModule last hour`` \"\n\"``pubsub/RetrieveSubscriptionsModule last minute`` \"\n\"``pubsub/RetrieveSubscriptionsModule last second``\"\nmsgstr \"\"\n\"``pubsub/RetrieveSubscriptionsModule last hour`` ``pubsub/\"\n\"RetrieveSubscriptionsModule last minute`` ``pubsub/\"\n\"RetrieveSubscriptionsModule last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:460\nmsgid \"RetrieveSubscriptionsModule/Average processing time\"\nmsgstr \"RetrieveSubscriptionsModule/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:460\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``RetrieveSubscriptionsModule`` to execute.\"\nmsgstr \"执行 ``RetrieveSubscriptionsModule`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:460\nmsgid \"``pubsub/RetrieveSubscriptionsModule/Average processing time``\"\nmsgstr \"``pubsub/RetrieveSubscriptionsModule/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:462\nmsgid \"SubscribeNodeModule last {interval}\"\nmsgstr \"SubscribeNodeModule 最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:462\nmsgid \"\"\n\"The number of times per interval that the ``SubscribeNodeModule`` command\"\n\" has been executed.\"\nmsgstr \"每个时间间隔执行 ``SubscribeNodeModule`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:462\nmsgid \"\"\n\"``pubsub/SubscribeNodeModule last hour`` ``pubsub/SubscribeNodeModule \"\n\"last minute`` ``pubsub/SubscribeNodeModule last second``\"\nmsgstr \"\"\n\"``pubsub/SubscribeNodeModule last hour`` ``pubsub/SubscribeNodeModule last \"\n\"minute`` ``pubsub/SubscribeNodeModule last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:464\nmsgid \"SubscribeNodeModule/Average processing time\"\nmsgstr \"SubscribeNodeModule/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:464\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``SubscribeNodeModule`` to execute.\"\nmsgstr \"执行 ``SubscribeNodeModule`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:464\nmsgid \"``pubsub/SubscribeNodeModule/Average processing time``\"\nmsgstr \"``pubsub/SubscribeNodeModule/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:466\nmsgid \"Subscription count (in cache)\"\nmsgstr \"订阅计数（在缓存中）\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:466\nmsgid \"\"\n\"The total number of pubsub subscriptions that are resident in cache \"\n\"memory. This may not reflect total pubsub subscriptions in repository.\"\nmsgstr \"驻留在高速缓存内存中的 pubsub 订阅总数。这可能无法反映存储库中的总 pubsub 订阅数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:466\nmsgid \"``pubsub/Subscription count (in cache)``\"\nmsgstr \"``pubsub/Subscription count (in cache)``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:468\nmsgid \"Total writing time\"\nmsgstr \"总的写入时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:468\nmsgid \"\"\n\"The cumulative total of time pubsub component has written to the database\"\n\" expressed in milliseconds.\"\nmsgstr \"pubsub 组件写入数据库的累计总时间，以毫秒为单位。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:468\nmsgid \"String (###ms)\"\nmsgstr \"字符串 (###ms)\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:468\nmsgid \"``pubsub/Total writing time``\"\nmsgstr \"``pubsub/Total writing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:470\nmsgid \"UnsubscribeNodeModule last {interval}\"\nmsgstr \"UnsubscribeNodeModule最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:470\nmsgid \"\"\n\"The number of times per interval that the ``UnsubscribeNodeModule`` \"\n\"command has been executed.\"\nmsgstr \"每个时间间隔执行 ``UnsubscribeNodeModule`` 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:470\nmsgid \"\"\n\"``pubsub/UnsubscribeNodeModule last hour`` ``pubsub/UnsubscribeNodeModule\"\n\" last minute`` ``pubsub/UnsubscribeNodeModule last second``\"\nmsgstr \"\"\n\"``pubsub/UnsubscribeNodeModule last hour`` ``pubsub/UnsubscribeNodeModule \"\n\"last minute`` ``pubsub/UnsubscribeNodeModule last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:472\nmsgid \"UnsubscribeNodeModule/Average processing time\"\nmsgstr \"UnsubscribeNodeModule/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:472\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``UnsubscribeNodeModule`` to execute.\"\nmsgstr \"执行 ``UnsubscribeNodeModule`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:472\nmsgid \"``pubsub/UnsubscribeNodeModule/Average processing time``\"\nmsgstr \"``pubsub/UnsubscribeNodeModule/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:474\nmsgid \"Update subscription calls\"\nmsgstr \"更新订阅电话\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:474\nmsgid \"\"\n\"Number of times Subscriptions have been updated (this includes new, \"\n\"deleted, and edited).\"\nmsgstr \"订阅的已更新次数（包括新建，删除和编辑）。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:474\nmsgid \"``pubsub/Update subscriptions calls``\"\nmsgstr \"``pubsub/Update subscriptions calls``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:476\nmsgid \"XmppPingModule last {interval}\"\nmsgstr \"XmppPingModule 最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:476\nmsgid \"\"\n\"The number of times per interval that the XmppPingModule command has been\"\n\" executed.\"\nmsgstr \"XmppPingModule 命令在每个时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:476\nmsgid \"\"\n\"``pubsub/XmppPingModule last hour`` ``pubsub/XmppPingModule last minute``\"\n\" ``pubsub/XmppPingModule last second``\"\nmsgstr \"\"\n\"``pubsub/XmppPingModule last hour`` ``pubsub/XmppPingModule last minute`` ``\"\n\"pubsub/XmppPingModule last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:478\nmsgid \"XmppPingModule/Average processing time\"\nmsgstr \"XmppPingModule/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:478\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``XmppPingModule`` to execute.\"\nmsgstr \"执行 ``XmppPingModule`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:478\nmsgid \"``pubsub/XmppPingModule/Average processing time``\"\nmsgstr \"``pubsub/XmppPingModule/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:482\nmsgid \"repo-factory\"\nmsgstr \"repo-factory\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:487\nmsgid \"Number of data repositories\"\nmsgstr \"数据存储库的数量\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:487\nmsgid \"The number of data repositories setup for this XMPP server.\"\nmsgstr \"为此 XMPP 服务器设置的数据存储库的数量。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:487\nmsgid \"``repo-factory/Number of data repositories``\"\nmsgstr \"``repo-factory/Number of data repositories``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:489\nmsgid \"Repository {jdbclocation} connections count\"\nmsgstr \"存储库 {jdbclocation} 连接数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:489\nmsgid \"The number of connections made to this database.\"\nmsgstr \"与此数据库建立的连接数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:489\nmsgid \"``repo-factory/repository {jdbclocation} connections count``\"\nmsgstr \"``repo-factory/repository {jdbclocation} connections count``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:491\nmsgid \"repository {jdbclocation} reconnections\"\nmsgstr \"存储库 {jdbclocation} 重新连接\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:491\nmsgid \"The number of reconnections made to this database.\"\nmsgstr \"对此数据库进行的重新连接次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:491\nmsgid \"``repo-factory/repository {jdbclocation} reconnections``\"\nmsgstr \"``repo-factory/repository {jdbclocation} reconnections``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:493\nmsgid \"repository {jdbclocation} failed reconnections\"\nmsgstr \"存储库 {jdbclocation} 重新连接失败\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:493\nmsgid \"The number of reconnections that have failed to connect to this database.\"\nmsgstr \"未能连接到此数据库的重新连接数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:493\nmsgid \"``repo-factory/repository {jdbclocation} failed reconnections``\"\nmsgstr \"``repo-factory/repository {jdbclocation} failed reconnections``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:497\nmsgid \"rest\"\nmsgstr \"rest\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:499\nmsgid \"No exclusive rest specific statistics\"\nmsgstr \"没有排他性rest特定的统计数据\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:503\nmsgid \"s2s\"\nmsgstr \"s2s\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:508\nmsgid \"CIDs number\"\nmsgstr \"CIDs号码\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:508\nmsgid \"\"\n\"ConnectionID for the server. This may include multiple CIDs if server is \"\n\"running multiple vhosts.\"\nmsgstr \"服务器的ConnectionID。如果服务器运行多个虚拟主机，这可能包括多个 CID。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:508\nmsgid \"``s2s/CIDs number``\"\nmsgstr \"``s2s/CIDs number``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:510\nmsgid \"get-cid-connection last {interval}\"\nmsgstr \"get-cid-connection 最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:510\nmsgid \"\"\n\"The number of times get-cid-connection command has been executed within \"\n\"the specified interval.\"\nmsgstr \"get-cid-connection 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:510\nmsgid \"\"\n\"``s2s/adhoc-command/get-cid-connection last hour`` ``s2s/adhoc-command\"\n\"/get-cid-connection last minute`` ``s2s/adhoc-command/get-cid-connection \"\n\"last second``\"\nmsgstr \"\"\n\"``s2s/adhoc-command/get-cid-connection last hour`` ``s2s/adhoc-command/get-\"\n\"cid-connection last minute`` ``s2s/adhoc-command/get-cid-connection last \"\n\"second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:512\nmsgid \"get-cid-connection/Average processing time\"\nmsgstr \"get-cid-connection/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:512\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``get-cid-\"\n\"connection`` to execute.\"\nmsgstr \"执行 ``get-cid-connection`` 所需的平均时间（以毫秒为单位），以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:512\nmsgid \"``s2s/adhoc-command/get-cid-connection/Average processing time``\"\nmsgstr \"``s2s/adhoc-command/get-cid-connection/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:514\nmsgid \"s2s-bad-state-conns last {interval}\"\nmsgstr \"s2s-bad-state-conns 最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:514\nmsgid \"\"\n\"The number of times s2s-bad-state-conns command has been executed within \"\n\"the specified interval.\"\nmsgstr \"s2s-bad-state-conns 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:514\nmsgid \"\"\n\"``s2s/adhoc-command/s2s-bad-state-conns last hour`` ``s2s/adhoc-command\"\n\"/s2s-bad-state-conns last minute`` ``s2s/adhoc-command/s2s-bad-state-\"\n\"conns last second``\"\nmsgstr \"\"\n\"``s2s/adhoc-command/s2s-bad-state-conns last hour`` ``s2s/adhoc-command/s2s-\"\n\"bad-state-conns last minute`` ``s2s/adhoc-command/s2s-bad-state-conns last \"\n\"second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:516\nmsgid \"s2s-bad-state-conns/Average processing time\"\nmsgstr \"s2s-bad-state-conns/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:516\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``s2s-bad-\"\n\"state-conns`` to execute.\"\nmsgstr \"执行 ``s2s-bad-state-conns`` 所需的平均时间（以毫秒为单位），以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:516\nmsgid \"``s2s/adhoc-command/s2s-bad-state-conns/Average processing time``\"\nmsgstr \"``s2s/adhoc-command/s2s-bad-state-conns/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:518\nmsgid \"reset-bad-state-conns last {interval}\"\nmsgstr \"reset-bad-state-conns 最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:518\nmsgid \"\"\n\"The number of times reset-bad-state-conns command has been executed \"\n\"within the specified interval.\"\nmsgstr \"在指定时间间隔内执行 reset-bad-state-conns 命令的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:518\nmsgid \"\"\n\"``s2s/adhoc-command/reset-bad-state-conns last hour`` ``s2s/adhoc-command\"\n\"/reset-bad-state-conns last minute`` ``s2s/adhoc-command/reset-bad-state-\"\n\"conns last second``\"\nmsgstr \"\"\n\"``s2s/adhoc-command/reset-bad-state-conns last hour`` ``s2s/adhoc-command/\"\n\"reset-bad-state-conns last minute`` ``s2s/adhoc-command/reset-bad-state-\"\n\"conns last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:520\nmsgid \"reset-bad-state-conns/Average processing time\"\nmsgstr \"reset-bad-state-conns/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:520\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``reset-bad-\"\n\"state-conns`` to execute.\"\nmsgstr \"执行 ``reset-bad-state-conns`` 所需的平均时间（以毫秒为单位），以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:520\nmsgid \"``s2s/adhoc-command/reset-bad-state-conns/Average processing time``\"\nmsgstr \"``s2s/adhoc-command/reset-bad-state-conns/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:522\nmsgid \"Total DB keys\"\nmsgstr \"数据库键总数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:522\nmsgid \"Total number of database keys.\"\nmsgstr \"数据库键的总数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:522\nmsgid \"``s2s/Total DB keys``\"\nmsgstr \"``s2s/Total DB keys``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:524\nmsgid \"Total {incoming/outgoing}\"\nmsgstr \"{incoming/outgoing}总计\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:524\nmsgid \"\"\n\"The total number of server-to-server connections, outgoing is local \"\n\"server connecting to other servers, and incoming is connections from \"\n\"other servers. The results may or may not be the same.\"\nmsgstr \"服务器到服务器的连接总数，传出是本地服务器连接到其他服务器，传入是来自其他服务器的连接。结果可能相同也可能不同。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:524\nmsgid \"``s2s/Total incoming`` ``s2s/Total outgoing``\"\nmsgstr \"``s2s/Total incoming`` ``s2s/Total outgoing``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:526\nmsgid \"Total {incoming/outgoing} TLS\"\nmsgstr \"{incoming/outgoing} TLS 总数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:526\nmsgid \"\"\n\"The total number of server-to-server connections using TLS, outgoing is \"\n\"local server connecting to other servers, and incoming is connections \"\n\"from other servers. The results may or may not be the same.\"\nmsgstr \"使用 TLS 的服务器到服务器连接的总数，传出是本地服务器连接到其他服务器，传入是来自其他服务器的连接。结果可能相同也可能不同。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:526\nmsgid \"``s2s/Total incoming TLS`` ``s2s/Total outgoing TLS``\"\nmsgstr \"``s2s/Total incoming TLS`` ``s2s/Total outgoing TLS``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:528\nmsgid \"Total outgoing handshaking\"\nmsgstr \"总传出握手\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:528\nmsgid \"\"\n\"Total number of outgoing connections that are currently handshaking to \"\n\"other servers.\"\nmsgstr \"当前与其他服务器握手的传出连接总数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:528\nmsgid \"``s2s/Total outgoing handshaking``\"\nmsgstr \"``s2s/Total outgoing handshaking``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:530\nmsgid \"Total control waiting\"\nmsgstr \"总控制等待\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:530\nmsgid \"Total number of connections that were manually told to wait.\"\nmsgstr \"手动告知等待的连接总数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:530\nmsgid \"``s2s/Total control waiting``\"\nmsgstr \"``s2s/Total control waiting``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:532\nmsgid \"Total waiting\"\nmsgstr \"总等待\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:532\nmsgid \"\"\n\"Total number of connections that are currently waiting for response from \"\n\"other server.\"\nmsgstr \"当前等待其他服务器响应的连接总数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:532\nmsgid \"``s2s/Total waiting``\"\nmsgstr \"``s2s/Total waiting``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:536\nmsgid \"sess-man\"\nmsgstr \"sess-man\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:541\nmsgid \"Active user connections\"\nmsgstr \"活跃用户连接\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:541\nmsgid \"\"\n\"Number of user connections that are considered active. An active user is \"\n\"a user that has sent stanzas to the server or through the server within \"\n\"the last 5 minutes.\"\nmsgstr \"被视为活动的用户连接数。活跃用户是在过去 5 分钟内向服务器或通过服务器发送节的用户。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:541\nmsgid \"``sess-man/Active user connections``\"\nmsgstr \"``sess-man/Active user connections``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:543\nmsgid \"adhoc-command/connection-time last {interval}\"\nmsgstr \"即席命令/连接时间最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:543\nmsgid \"\"\n\"The number of times ``connection-time`` command has been executed within \"\n\"the specified interval.\"\nmsgstr \"``connection-time`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:543\nmsgid \"\"\n\"``sess-man/adhoc-command/connection-time last hour`` ``sess-man/adhoc-\"\n\"command/connection-time last minute`` ``sess-man/adhoc-command\"\n\"/connection-time last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/connection-time last hour`` ``sess-man/\"\n\"adhoc-command/connection-time last minute`` ``sess-man/adhoc-command/\"\n\"connection-time last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:545\nmsgid \"adhoc-command/connection-time/Average processing time\"\nmsgstr \"即席命令/连接时间/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:545\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``connection-time`` to execute.\"\nmsgstr \"执行 ``connection-time`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:545\nmsgid \"``sess-man/adhoc-command/connection-time/Average processing time``\"\nmsgstr \"``sess-man/adhoc-command/connection-time/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:547\nmsgid \"adhoc-command/http://jabber.org/protocol/admin#add-user last {interval}\"\nmsgstr \"adhoc-command/http://jabber.org/protocol/admin#add-user last {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:547\nmsgid \"\"\n\"The number of times ``admin#add-user`` command has been executed within \"\n\"the specified interval.\"\nmsgstr \"``admin#add-user`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:547\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-user last \"\n\"hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-user\"\n\" last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin\"\n\"#add-user last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-user last \"\n\"hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-user \"\n\"last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-\"\n\"user last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:549\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#add-user/Average \"\n\"processing time\"\nmsgstr \"adhoc-command/http://jabber.org/protocol/admin#add-user/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:549\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin#add-\"\n\"user`` to execute.\"\nmsgstr \"执行 ``admin#add-user`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:549\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-\"\n\"user/Average processing time``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-user/Average \"\n\"processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:551\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#add-user-tracker last \"\n\"{interval}\"\nmsgstr \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#add-user-tracker last \"\n\"{interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:551\nmsgid \"\"\n\"The number of times ``admin#add-user-tracker`` command has been executed \"\n\"within the specified interval.\"\nmsgstr \"``admin#add-user-tracker`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:551\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-user-\"\n\"tracker last hour`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#add-user-tracker last minute`` \"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-user-\"\n\"tracker last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-user-tracker \"\n\"last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-\"\n\"user-tracker last minute`` ``sess-man/adhoc-command/http://jabber.org/\"\n\"protocol/admin#add-user-tracker last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:553\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#add-user-tracker/Average \"\n\"processing time\"\nmsgstr \"adhoc-command/http://jabber.org/protocol/admin#add-user-tracker/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:553\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin#add-\"\n\"user-tracker`` to execute.\"\nmsgstr \"执行 ``admin#add-user-tracker`` 所需的平均时间（以毫秒为单位），以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:553\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-user-\"\n\"tracker/Average processing time``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#add-user-tracker/\"\n\"Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:555\nmsgid \"adhoc-command/http://jabber.org/protocol/admin#announce last {interval}\"\nmsgstr \"adhoc-command/http://jabber.org/protocol/admin#announce last {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:555\nmsgid \"\"\n\"The number of times ``admin#announce`` command has been executed within \"\n\"the specified interval.\"\nmsgstr \"``admin#announce`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:555\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#announce last \"\n\"hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#announce\"\n\" last minute`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#announce last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#announce last \"\n\"hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#announce \"\n\"last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#\"\n\"announce last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:557\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#announce/Average \"\n\"processing time\"\nmsgstr \"adhoc-command/http://jabber.org/protocol/admin#announce/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:557\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for \"\n\"``admin#announce`` to execute.\"\nmsgstr \"执行 ``admin#announce`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:557\nmsgid \"\"\n\"``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#announce/Average processing \"\n\"time``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#announce/Average \"\n\"processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:559\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#change-user-password last \"\n\"{interval}\"\nmsgstr \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#change-user-password last \"\n\"{interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:559\nmsgid \"\"\n\"The number of times ``admin#change-user-password`` command has been \"\n\"executed within the specified interval.\"\nmsgstr \"``admin#change-user-password`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:559\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#change-user-\"\n\"password last hour`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#change-user-password last \"\n\"minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin\"\n\"#change-user-password last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#change-user-\"\n\"password last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/\"\n\"admin#change-user-password last minute`` ``sess-man/adhoc-command/\"\n\"http://jabber.org/protocol/admin#change-user-password last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:561\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#change-user-\"\n\"password/Average processing time\"\nmsgstr \"adhoc-command/http://jabber.org/protocol/admin#change-user-password/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:561\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin\"\n\"#change-user-password`` to execute.\"\nmsgstr \"执行 ``admin#change-user-password`` 所需的平均时间（以毫秒为单位），以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:561\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#change-user-\"\n\"password/Average processing time``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#change-\"\n\"user-password/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:563\nmsgid \"adhoc-command/http://jabber.org/protocol/admin#delete-user last {interval}\"\nmsgstr \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#delete-user last {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:563\nmsgid \"\"\n\"The number of times ``admin#delete-user`` command has been executed \"\n\"within the specified interval.\"\nmsgstr \"``admin#delete-user`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:563\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#delete-user \"\n\"last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin\"\n\"#delete-user last minute`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#delete-user last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#delete-user last \"\n\"hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#delete-user \"\n\"last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin\"\n\"#delete-user last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:565\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#delete-user/Average \"\n\"processing time\"\nmsgstr \"adhoc-command/http://jabber.org/protocol/admin#delete-user/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:565\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin\"\n\"#delete-user`` to execute.\"\nmsgstr \"执行 ``admin#delete-user`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:565\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#delete-\"\n\"user/Average processing time``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#delete-user/\"\n\"Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:567\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#end-user-session last \"\n\"{interval}\"\nmsgstr \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#end-user-session last \"\n\"{interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:567\nmsgid \"\"\n\"The number of times ``admin#end-user-session`` command has been executed \"\n\"within the specified interval.\"\nmsgstr \"``admin#end-user-session`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:567\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#end-user-\"\n\"session last hour`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#end-user-session last minute`` \"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#end-user-\"\n\"session last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#end-user-session \"\n\"last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#end-\"\n\"user-session last minute`` ``sess-man/adhoc-command/http://jabber.org/\"\n\"protocol/admin#end-user-session last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:569\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#end-user-session/Average \"\n\"processing time\"\nmsgstr \"adhoc-command/http://jabber.org/protocol/admin#end-user-session/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:569\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin#end-\"\n\"user-session`` to execute.\"\nmsgstr \"执行 ``admin#end-user-session`` 所需的平均时间（以毫秒为单位），以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:569\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#end-user-\"\n\"session/Average processing time``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#end-user-session/\"\n\"Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:571\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-active-users last \"\n\"{interval}\"\nmsgstr \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-active-users last \"\n\"{interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:571\nmsgid \"\"\n\"The number of times ``admin#get-active-users`` command has been executed \"\n\"within the specified interval.\"\nmsgstr \"``admin#get-active-users`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:571\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-active-\"\n\"users last hour`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#get-active-users last minute`` \"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-active-\"\n\"users last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-active-users \"\n\"last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-\"\n\"active-users last minute`` ``sess-man/adhoc-command/http://jabber.org/\"\n\"protocol/admin#get-active-users last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:573\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-active-users/Average \"\n\"processing time\"\nmsgstr \"adhoc-command/http://jabber.org/protocol/admin#get-active-users/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:573\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin#get-\"\n\"active-users`` to execute.\"\nmsgstr \"执行 ``admin#get-active-users`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:573\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-active-\"\n\"users/Average processing time``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-active-users/\"\n\"Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:575\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-active-user-num last \"\n\"{interval}\"\nmsgstr \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-active-user-num last \"\n\"{interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:575\nmsgid \"\"\n\"The number of times ``admin#get-active-user-num`` command has been \"\n\"executed within the specified interval.\"\nmsgstr \"``admin#get-active-user-num`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:575\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-active-\"\n\"user-num last hour`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#get-active-user-num last \"\n\"minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-\"\n\"active-user-num last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-active-user-\"\n\"num last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin\"\n\"#get-active-user-num last minute`` ``sess-man/adhoc-command/http://jabber.\"\n\"org/protocol/admin#get-active-user-num last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:577\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-active-user-\"\n\"num/Average processing time\"\nmsgstr \"adhoc-command/http://jabber.org/protocol/admin#get-active-user-num/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:577\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin#get-\"\n\"active-user-num`` to execute.\"\nmsgstr \"执行 ``admin#get-active-user-num`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:577\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-active-\"\n\"user-num/Average processing time``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-\"\n\"active-user-num/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:579\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-idle-users last \"\n\"{interval}\"\nmsgstr \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-idle-users last {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:579\nmsgid \"\"\n\"The number of times ``admin#get-idle-users`` command has been executed \"\n\"within the specified interval.\"\nmsgstr \"``admin#get-idle-users`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:579\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-idle-users \"\n\"last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin\"\n\"#get-idle-users last minute`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#get-idle-users last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-idle-users \"\n\"last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-\"\n\"idle-users last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/\"\n\"admin#get-idle-users last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:581\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-idle-users/Average \"\n\"processing time\"\nmsgstr \"adhoc-command/http://jabber.org/protocol/admin#get-idle-users/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:581\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin#get-\"\n\"idle-users`` to execute.\"\nmsgstr \"执行 ``admin#get-idle-users`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:581\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-idle-\"\n\"users/Average processing time``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-idle-users/\"\n\"Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:583\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-idle-users-num last \"\n\"{interval}\"\nmsgstr \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-idle-users-num last \"\n\"{interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:583\nmsgid \"\"\n\"The number of times ``admin#get-idle-users-num`` command has been \"\n\"executed within the specified interval.\"\nmsgstr \"``admin#get-idle-users-num`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:583\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-idle-users-\"\n\"num last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin\"\n\"#get-idle-users-num last minute`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#get-idle-users-num last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-idle-users-num \"\n\"last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-\"\n\"idle-users-num last minute`` ``sess-man/adhoc-command/http://jabber.org/\"\n\"protocol/admin#get-idle-users-num last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:585\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-idle-users-num/Average\"\n\" processing time\"\nmsgstr \"adhoc-command/http://jabber.org/protocol/admin#get-idle-users-num/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:585\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin#get-\"\n\"idle-users-num`` to execute.\"\nmsgstr \"执行 ``admin#get-idle-users-num`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:585\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-idle-users-\"\n\"num/Average processing time``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-idle-users-num/\"\n\"Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:587\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-online-users-list last\"\n\" {interval}\"\nmsgstr \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-online-users-list last \"\n\"{interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:587\nmsgid \"\"\n\"The number of times ``admin#get-online-users-list`` command has been \"\n\"executed within the specified interval.\"\nmsgstr \"``admin#get-online-users-list`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:587\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-online-\"\n\"users-list last hour`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#get-online-users-list last \"\n\"minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-\"\n\"online-users-list last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-online-users-\"\n\"list last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin\"\n\"#get-online-users-list last minute`` ``sess-man/adhoc-command/http://jabber.\"\n\"org/protocol/admin#get-online-users-list last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:589\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-online-users-\"\n\"list/Average processing time\"\nmsgstr \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-online-users-\"\n\"list/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:589\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin#get-\"\n\"online-users-list`` to execute.\"\nmsgstr \"执行 ``admin#get-online-users-list`` 所需的平均时间（以毫秒为单位），以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:589\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-online-\"\n\"users-list/Average processing time``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-\"\n\"online-users-list/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:591\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-top-active-users last \"\n\"{interval}\"\nmsgstr \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-top-active-users last \"\n\"{interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:591\nmsgid \"\"\n\"The number of times ``admin#get-top-active-users`` command has been \"\n\"executed within the specified interval.\"\nmsgstr \"``admin#get-top-active-users`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:591\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-top-active-\"\n\"users last hour`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#get-top-active-users last \"\n\"minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-\"\n\"top-active-users last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-top-active-\"\n\"users last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin\"\n\"#get-top-active-users last minute`` ``sess-man/adhoc-command/http://jabber.\"\n\"org/protocol/admin#get-top-active-users last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:593\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-top-active-\"\n\"users/Average processing time\"\nmsgstr \"adhoc-command/http://jabber.org/protocol/admin#get-top-active-users/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:593\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin#get-\"\n\"top-active-users`` to execute.\"\nmsgstr \"执行 ``admin#get-top-active-users`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:593\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-top-active-\"\n\"users/Average processing time``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-\"\n\"top-active-users/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:595\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-registered-users-list \"\n\"last {interval}\"\nmsgstr \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-registered-users-list \"\n\"last {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:595\nmsgid \"\"\n\"The number of times ``admin#get-registered-users-list`` command has been \"\n\"executed within the specified interval.\"\nmsgstr \"``admin#get-registered-users-list`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:595\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-registered-\"\n\"users-list last hour`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#get-registered-users-list last \"\n\"minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-\"\n\"registered-users-list last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-registered-\"\n\"users-list last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/\"\n\"admin#get-registered-users-list last minute`` ``sess-man/adhoc-command/\"\n\"http://jabber.org/protocol/admin#get-registered-users-list last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:597\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-registered-users-\"\n\"list/Average processing time\"\nmsgstr \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-registered-users-\"\n\"list/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:597\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin#get-\"\n\"registered-users-list`` to execute.\"\nmsgstr \"执行 ``admin#get-registered-users-list`` 所需的平均时间（以毫秒为单位），以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:597\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-registered-\"\n\"users-list/Average processing time``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-\"\n\"registered-users-list/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:599\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-user-roster last \"\n\"{interval}\"\nmsgstr \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-user-roster last \"\n\"{interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:599\nmsgid \"\"\n\"The number of times ``admin#get-user-roster`` command has been executed \"\n\"within the specified interval.\"\nmsgstr \"``admin#get-user-roster`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:599\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-user-roster\"\n\" last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin\"\n\"#get-user-roster last minute`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#get-user-roster last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-user-roster \"\n\"last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-\"\n\"user-roster last minute`` ``sess-man/adhoc-command/http://jabber.org/\"\n\"protocol/admin#get-user-roster last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:601\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#get-user-roster/Average \"\n\"processing time\"\nmsgstr \"adhoc-command/http://jabber.org/protocol/admin#get-user-roster/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:601\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin#get-\"\n\"user-roster`` to execute.\"\nmsgstr \"执行 ``admin#get-user-roster`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:601\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-user-\"\n\"roster/Average processing time``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#get-user-roster/\"\n\"Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:603\nmsgid \"adhoc-command/http://jabber.org/protocol/admin#remove-user last {interval}\"\nmsgstr \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#remove-user last {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:603\nmsgid \"\"\n\"The number of times ``admin#remove-user`` command has been executed \"\n\"within the specified interval.\"\nmsgstr \"``admin#remove-user`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:603\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#remove-user \"\n\"last hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin\"\n\"#remove-user last minute`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#remove-user last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#remove-user last \"\n\"hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#remove-user \"\n\"last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin\"\n\"#remove-user last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:605\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#remove-user/Average \"\n\"processing time\"\nmsgstr \"adhoc-command/http://jabber.org/protocol/admin#remove-user/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:605\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin\"\n\"#remove-user`` to execute.\"\nmsgstr \"执行 ``admin#remove-user`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:605\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#remove-\"\n\"user/Average processing time``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#remove-user/\"\n\"Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:607\nmsgid \"adhoc-command/http://jabber.org/protocol/admin#user-stats last {interval}\"\nmsgstr \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#user-stats last {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:607\nmsgid \"\"\n\"The number of times ``admin#user-stats`` command has been executed within\"\n\" the specified interval.\"\nmsgstr \"``admin#user-stats`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:607\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#user-stats last\"\n\" hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#user-\"\n\"stats last minute`` ``sess-man/adhoc-\"\n\"command/http://jabber.org/protocol/admin#user-stats last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#user-stats last \"\n\"hour`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#user-stats \"\n\"last minute`` ``sess-man/adhoc-command/http://jabber.org/protocol/admin#user-\"\n\"stats last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:609\nmsgid \"\"\n\"adhoc-command/http://jabber.org/protocol/admin#user-stats/Average \"\n\"processing time\"\nmsgstr \"adhoc-command/http://jabber.org/protocol/admin#user-stats/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:609\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``admin\"\n\"#user-stats`` to execute.\"\nmsgstr \"执行 ``admin#user-stats`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:609\nmsgid \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#user-\"\n\"stats/Average processing time``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/http://jabber.org/protocol/admin#user-stats/Average \"\n\"processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:611\nmsgid \"adhoc-command/get-user-info last {interval}\"\nmsgstr \"即席命令/获取用户信息最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:611\nmsgid \"\"\n\"The number of times ``get-user-info command`` has been executed within \"\n\"the specified interval.\"\nmsgstr \"``get-user-info command`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:611\nmsgid \"\"\n\"``sess-man/adhoc-command/get-user-info last hour`` ``sess-man/adhoc-\"\n\"command/get-user-info last minute`` ``sess-man/adhoc-command/get-user-\"\n\"info last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/get-user-info last hour`` ``sess-man/adhoc-command/\"\n\"get-user-info last minute`` ``sess-man/adhoc-command/get-user-info last \"\n\"second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:613\nmsgid \"adhoc-command/get-user-info/Average processing time\"\nmsgstr \"即席命令/获取用户信息/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:613\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``get-user-\"\n\"info`` to execute.\"\nmsgstr \"执行 ``get-user-info`` 所需的平均时间（以毫秒为单位），以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:613\nmsgid \"``sess-man/adhoc-command/get-user-info/Average processing time``\"\nmsgstr \"``sess-man/adhoc-command/get-user-info/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:615\nmsgid \"adhoc-command/modify-user last {interval}\"\nmsgstr \"即席命令/修改用户最后{interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:615\nmsgid \"\"\n\"The number of times ``modify-user`` command has been executed within the \"\n\"specified interval.\"\nmsgstr \"``modify-user`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:615\nmsgid \"\"\n\"``sess-man/adhoc-command/modify-user last hour`` ``sess-man/adhoc-command\"\n\"/modify-user last minute`` ``sess-man/adhoc-command/modify-user last \"\n\"second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/modify-user last hour`` ``sess-man/adhoc-command/\"\n\"modify-user last minute`` ``sess-man/adhoc-command/modify-user last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:617\nmsgid \"adhoc-command/modify-user/Average processing time\"\nmsgstr \"即席命令/修改用户/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:617\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``modify-\"\n\"user`` to execute.\"\nmsgstr \"执行 ``modify-user`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:617\nmsgid \"``sess-man/adhoc-command/modify-user/Average processing time``\"\nmsgstr \"``sess-man/adhoc-command/modify-user/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:619\nmsgid \"adhoc-command/oauth-credentials last {interval}\"\nmsgstr \"adhoc-command/oauth-credentials last {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:619\nmsgid \"\"\n\"The number of times ``oauth-credentials`` command has been executed \"\n\"within the specified interval.\"\nmsgstr \"``oauth-credentials`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:619\nmsgid \"\"\n\"``sess-man/adhoc-command/oauth-credentials last hour`` ``sess-man/adhoc-\"\n\"command/oauth-credentials last minute`` ``sess-man/adhoc-command/oauth-\"\n\"credentials last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/oauth-credentials last hour`` ``sess-man/\"\n\"adhoc-command/oauth-credentials last minute`` ``sess-man/adhoc-command/oauth-\"\n\"credentials last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:621\nmsgid \"adhoc-command/oauth-credentials/Average processing time\"\nmsgstr \"即席命令/oauth凭证/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:621\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``oauth-\"\n\"credentials`` to execute.\"\nmsgstr \"执行 ``oauth-credentials`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:621\nmsgid \"``sess-man/adhoc-command/oauth-credentials/Average processing time``\"\nmsgstr \"``sess-man/adhoc-command/oauth-credentials/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:623\nmsgid \"adhoc-command/roster-fixer last {interval}\"\nmsgstr \"即席命令/roster-fixer最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:623\nmsgid \"\"\n\"The number of times ``roster-fixer`` command has been executed within the\"\n\" specified interval.\"\nmsgstr \"``roster-fixer`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:623\nmsgid \"\"\n\"``sess-man/adhoc-command/roster-fixer last hour`` ``sess-man/adhoc-\"\n\"command/roster-fixer last minute`` ``sess-man/adhoc-command/roster-fixer \"\n\"last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/roster-fixer last hour`` ``sess-man/adhoc-command/\"\n\"roster-fixer last minute`` ``sess-man/adhoc-command/roster-fixer last \"\n\"second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:625\nmsgid \"adhoc-command/roster-fixer/Average processing time\"\nmsgstr \"即席命令/roster-fixer/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:625\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``roster-\"\n\"fixer`` to execute.\"\nmsgstr \"执行 ``roster-fixer`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:625\nmsgid \"``sess-man/adhoc-command/roster-fixer/Average processing time``\"\nmsgstr \"``sess-man/adhoc-command/roster-fixer/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:627\nmsgid \"adhoc-command/roster-fixer-cluster last {interval}\"\nmsgstr \"即席命令/roster-fixer-cluster最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:627\nmsgid \"\"\n\"The number of times ``roster-fixer-cluster`` command has been executed \"\n\"within the specified interval.\"\nmsgstr \"``roster-fixer-cluster`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:627\nmsgid \"\"\n\"``sess-man/adhoc-command/roster-fixer-cluster last hour`` ``sess-man\"\n\"/adhoc-command/roster-fixer-cluster last minute`` ``sess-man/adhoc-\"\n\"command/roster-fixer-cluster last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/roster-fixer-cluster last hour`` ``sess-man/\"\n\"adhoc-command/roster-fixer-cluster last minute`` ``sess-man/adhoc-command/\"\n\"roster-fixer-cluster last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:629\nmsgid \"adhoc-command/roster-fixer-cluster/Average processing time\"\nmsgstr \"即席命令/roster-fixer-cluster/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:629\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``roster-\"\n\"fixer-cluster`` to execute.\"\nmsgstr \"执行 ``roster-fixer-cluster`` 所需的时间，以毫秒为单位的平均时间，以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:629\nmsgid \"``sess-man/adhoc-command/roster-fixer-cluster/Average processing time``\"\nmsgstr \"``sess-man/adhoc-command/roster-fixer-cluster/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:631\nmsgid \"adhoc-command/user-domain-perm last {interval}\"\nmsgstr \"即席命令/user-domain-perm最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:631\nmsgid \"\"\n\"The number of times user-domain-perm command has been executed within the\"\n\" specified interval.\"\nmsgstr \"user-domain-perm 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:631\nmsgid \"\"\n\"``sess-man/adhoc-command/user-domain-perm last hour`` ``sess-man/adhoc-\"\n\"command/user-domain-perm last minute`` ``sess-man/adhoc-command/user-\"\n\"domain-perm last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/user-domain-perm last hour`` ``sess-man/\"\n\"adhoc-command/user-domain-perm last minute`` ``sess-man/adhoc-command/user-\"\n\"domain-perm last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:633\nmsgid \"adhoc-command/user-domain-perm/Average processing time\"\nmsgstr \"即席命令/user-domain-perm/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:633\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``user-\"\n\"domain-perm`` to execute.\"\nmsgstr \"执行 ``user-domain-perm`` 所需的平均时间（以毫秒为单位），以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:633\nmsgid \"``sess-man/adhoc-command/user-domain-perm/Average processing time``\"\nmsgstr \"``sess-man/adhoc-command/user-domain-perm/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:635\nmsgid \"adhoc-command/user-roster-management last {interval}\"\nmsgstr \"即席命令/用户名册管理最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:635\nmsgid \"\"\n\"The number of times ``user-roster-management`` command has been executed \"\n\"within the specified interval.\"\nmsgstr \"``user-roster-management`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:635\nmsgid \"\"\n\"``sess-man/adhoc-command/user-roster-management last hour`` ``sess-man\"\n\"/adhoc-command/user-roster-management last minute`` ``sess-man/adhoc-\"\n\"command/user-roster-management last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/user-roster-management last hour`` ``sess-man/\"\n\"adhoc-command/user-roster-management last minute`` ``sess-man/adhoc-command/\"\n\"user-roster-management last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:637\nmsgid \"adhoc-command/user-roster-management/Average processing time\"\nmsgstr \"即席命令/用户名册管理/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:637\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``user-\"\n\"roster-management`` to execute.\"\nmsgstr \"执行 ``user-roster-management`` 所需的平均时间（以毫秒为单位），以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:637\nmsgid \"``sess-man/adhoc-command/user-roster-management/Average processing time``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/user-roster-management/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:639\nmsgid \"adhoc-command/user-roster-management-ext last {interval}\"\nmsgstr \"即席命令/user-roster-management-ext 最后 {interval}\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:639\nmsgid \"\"\n\"The number of times ``user-roster-management-ext`` command has been \"\n\"executed within the specified interval.\"\nmsgstr \"``user-roster-management-ext`` 命令在指定时间间隔内执行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:639\nmsgid \"\"\n\"``sess-man/adhoc-command/user-roster-management-ext last hour`` ``sess-\"\n\"man/adhoc-command/user-roster-management-ext last minute`` ``sess-man\"\n\"/adhoc-command/user-roster-management-ext last second``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/user-roster-management-ext last hour`` ``sess-man/\"\n\"adhoc-command/user-roster-management-ext last minute`` ``sess-man/\"\n\"adhoc-command/user-roster-management-ext last second``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:641\nmsgid \"adhoc-command/user-roster-management-ext/Average processing time\"\nmsgstr \"即席命令/user-roster-management-ext/平均处理时间\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:641\nmsgid \"\"\n\"The average time in ms, returned as an integer, it takes for ``user-\"\n\"roster-management-ext`` to execute.\"\nmsgstr \"执行 ``user-roster-management-ext`` 所需的平均时间（以毫秒为单位），以整数形式返回。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:641\nmsgid \"\"\n\"``sess-man/adhoc-command/user-roster-management-ext/Average processing \"\n\"time``\"\nmsgstr \"\"\n\"``sess-man/adhoc-command/user-roster-management-ext/Average processing time``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:643\nmsgid \"Authentication timeouts\"\nmsgstr \"身份验证超时\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:643\nmsgid \"\"\n\"The number of connections that have timed out during the authentication \"\n\"process. Default timeout is 2 minutes.\"\nmsgstr \"在身份验证过程中超时的连接数。默认超时为 2 分钟。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:643\nmsgid \"``sess-man/Authentication timeouts``\"\nmsgstr \"``sess-man/Authentication timeouts``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:645\nmsgid \"Closed user connections\"\nmsgstr \"关闭的用户连接\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:645\nmsgid \"\"\n\"User connections that have been terminated by the user (as opposed to the\"\n\" server).\"\nmsgstr \"已被用户（与服务器相反）终止的用户连接。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:645\nmsgid \"``sess-man/Closed user connections``\"\nmsgstr \"``sess-man/Closed user connections``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:647\nmsgid \"default-handler/Invalid registrations\"\nmsgstr \"默认处理程序/无效注册\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:647\nmsgid \"Number of invalid registrations attempted with the server.\"\nmsgstr \"尝试使用服务器的无效注册数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:647\nmsgid \"``sess-man/default-handler/Invalid registrations``\"\nmsgstr \"``sess-man/default-handler/Invalid registrations``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:649\nmsgid \"default-handler/Registered users\"\nmsgstr \"默认处理程序/注册用户\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:649\nmsgid \"Number of registered users for this server.\"\nmsgstr \"此服务器的注册用户数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:649\nmsgid \"``sess-man/default-handler/Registered users``\"\nmsgstr \"``sess-man/default-handler/Registered users``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:651\nmsgid \"Maximum user connections\"\nmsgstr \"最大用户连接数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:651\nmsgid \"\"\n\"Maximum number of connections that have been made during server instance,\"\n\" this number includes users connecting multiple times.\"\nmsgstr \"服务器实例期间已建立的最大连接数，此数字包括多次连接的用户。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:651\nmsgid \"``sess-man/Maximum user connections``\"\nmsgstr \"``sess-man/Maximum user connections``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:653\nmsgid \"Maximum user sessions {today/yesterday}\"\nmsgstr \"{today/yesterday}的最大用户会话数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:653\nmsgid \"\"\n\"The number of most simultaneous sessions within the specified interval. \"\n\"Today = previous 24 hours, Yesterday = 24 hours after previous 24 hours \"\n\"(does not go by calendar date).\"\nmsgstr \"指定时间间隔内最多同时会话的数量。今天 = 前 24 小时，昨天 = 前24小时后的24小时（不按日历日期计算）。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:653\nmsgid \"\"\n\"``sess-man/Maximum user sessions today`` ``sess-man/Maximum user sessions\"\n\" yesterday``\"\nmsgstr \"\"\n\"``sess-man/Maximum user sessions today`` ``sess-man/Maximum user sessions \"\n\"yesterday``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:655\nmsgid \"Registered accounts\"\nmsgstr \"注册账户\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:655\nmsgid \"Sum total of registered accounts for the server.\"\nmsgstr \"服务器的注册帐户总数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:655\nmsgid \"``sess-man/Registered accounts``\"\nmsgstr \"``sess-man/Registered accounts``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:657\nmsgid \"Open user connections\"\nmsgstr \"打开用户连接\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:657\nmsgid \"\"\n\"The current number of open user connections. This may be interpreted as \"\n\"number of connections from users, however a user can have more than one \"\n\"connection (connection from mobile and PC for example).\"\nmsgstr \"当前打开的用户连接数。这可以解释为来自用户的连接数，但是一个用户可以拥有多个连接（例如来自移动设备和 PC 的连接）。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:657\nmsgid \"``sess-man/Open user connections``\"\nmsgstr \"``sess-man/Open user connections``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:659\nmsgid \"Open user sessions\"\nmsgstr \"打开用户会话\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:659\nmsgid \"The current number of open user sessions.\"\nmsgstr \"当前打开的用户会话数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:659\nmsgid \"``sess-man/Open user sessions``\"\nmsgstr \"``sess-man/Open user sessions``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:661\nmsgid \"Total user connections\"\nmsgstr \"用户连接总数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:661\nmsgid \"\"\n\"The cumulative number of connections that have been made to the server \"\n\"during the current instance.\"\nmsgstr \"当前实例期间已与服务器建立的累积连接数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:661\nmsgid \"``sess-man/Total user connections``\"\nmsgstr \"``sess-man/Total user connections``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:663\nmsgid \"Total user sessions\"\nmsgstr \"用户会话总数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:663\nmsgid \"\"\n\"The cumulative number of sessions that this server has negotiated during \"\n\"the current instance.\"\nmsgstr \"此服务器在当前实例期间已协商的累积会话数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:663\nmsgid \"``sess-man/Total user sessions``\"\nmsgstr \"``sess-man/Total user sessions``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:665\nmsgid \"presence/Users status changes\"\nmsgstr \"存在/用户状态更改\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:665\nmsgid \"\"\n\"The number of presence changes for all users that have been conducted \"\n\"during the server instance.\"\nmsgstr \"在服务器实例期间执行的所有用户的状态更改次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:665\nmsgid \"\"\n\"``sess-man/presence/Users status changes`` ``sess-man/presence-\"\n\"state/Users status changes``\"\nmsgstr \"\"\n\"``sess-man/presence/Users status changes`` ``sess-man/presence-state/Users \"\n\"status changes``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:667\nmsgid \"sess-man/Processor\"\nmsgstr \"sess-man/处理器\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:667\nmsgid \"\"\n\"Processor statistics will result in a field of labels and values \"\n\"exclusive to that processor.\"\nmsgstr \"处理器统计信息将生成该处理器专有的标签和值字段。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:667\nmsgid \"FIELD\"\nmsgstr \"字段\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:667\nmsgid \"\"\n\"``sess-man/Processor: message carbons`` ``sess-man/Processor: \"\n\"http://jabber.org/protocol/stats`` ``sess-man/Processor: jabber:iq:auth``\"\n\" ``sess-man/Processor: vcard-temp`` ``sess-man/Processor: amp`` ``sess-\"\n\"man/Processor: presence-subscription`` ``sess-man/Processor: disco`` \"\n\"``sess-man/Processor: msgoffline`` ``sess-man/Processor: \"\n\"urn:xmpp:blocking`` ``sess-man/Processor: urn:xmpp:ping`` ``sess-\"\n\"man/Processor: jabber:iq:register`` ``sess-man/Processor: \"\n\"urn:ietf:params:xml:ns:xmpp-sasl`` ``sess-man/Processor: prp`` ``sess-\"\n\"man/Processor: presence`` ``sess-man/Processor: message-archive-\"\n\"xep-0136`` ``sess-man/Processor: default-handler`` ``sess-man/Processor: \"\n\"jabber:iq:roster`` ``sess-man/Processor: starttls`` ``sess-man/Processor:\"\n\" presence-state`` ``sess-man/Processor: jabber:iq:version`` ``sess-\"\n\"man/Processor: urn:xmpp:time`` ``sess-man/Processor: session-open`` \"\n\"``sess-man/Processor: jabber:iq:privacy`` ``sess-man/Processor: \"\n\"urn:ietf:params:xml:ns:xmpp-bind`` ``sess-man/Processor: \"\n\"http://jabber.org/protocol/commands`` ``sess-man/Processor: vcard-\"\n\"xep0292`` ``sess-man/Processor: session-close`` ``sess-man/Processor: \"\n\"urn:ietf:params:xml:ns:xmpp-session`` ``sess-man/Processor: \"\n\"jabber:iq:private`` ``sess-man/Processor: Average amp on last 100 runs \"\n\"[ms]`` ``sess-man/Processor: Average msgoffline on last 100 runs[ms]``\"\nmsgstr \"\"\n\"``sess-man/Processor: message carbons`` ``sess-man/Processor: http://jabber.\"\n\"org/protocol/stats`` ``sess-man/Processor: jabber:iq:auth`` ``sess-man/\"\n\"Processor: vcard-temp`` ``sess-man/Processor: amp`` ``sess-man/Processor: \"\n\"presence-subscription`` ``sess-man/Processor: disco`` ``sess-man/Processor: \"\n\"msgoffline`` ``sess-man/Processor: urn:xmpp:blocking`` ``sess-man/Processor: \"\n\"urn:xmpp:ping`` ``sess-man/Processor: jabber:iq:register`` ``sess-man/\"\n\"Processor: urn:ietf:params:xml:ns:xmpp-sasl`` ``sess-man/Processor: prp`` \"\n\"``sess-man/Processor: presence`` ``sess-man/Processor: message-archive-\"\n\"xep-0136`` ``sess-man/Processor: default-handler`` ``sess-man/Processor: \"\n\"jabber:iq:roster`` ``sess-man/Processor: starttls`` ``sess-man/Processor: \"\n\"presence-state`` ``sess-man/Processor: jabber:iq:version`` ``sess-man/\"\n\"Processor: urn:xmpp:time`` ``sess-man/Processor: session-open`` ``sess-man/\"\n\"Processor: jabber:iq:privacy`` ``sess-man/Processor: urn:ietf:params:xml:ns\"\n\":xmpp-bind`` ``sess-man/Processor: http://jabber.org/protocol/commands`` \"\n\"``sess-man/Processor: vcard-xep0292`` ``sess-man/Processor: session-close`` \"\n\"``sess-man/Processor: urn:ietf:params:xml:ns:xmpp-session`` ``sess-man/\"\n\"Processor: jabber:iq:private`` ``sess-man/Processor: Average amp on last 100 \"\n\"runs [ms]`` ``sess-man/Processor: Average msgoffline on last 100 runs[ms]``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"The field shows as follows:\"\nmsgstr \"该字段显示如下：\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"``, Queue: 0, AvTime: 0, Runs: 0, Lost: 0``\"\nmsgstr \"``, Queue: 0, AvTime: 0, Runs: 0, Lost: 0``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"Where:\"\nmsgstr \"在哪里：\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"Queue: Number of packets in process queue\"\nmsgstr \"队列：进程队列中的数据包数\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"AvTime: Average time in ms processor takes to conduct it’s operation.\"\nmsgstr \"AvTime：以毫秒为单位的处理器执行其操作的平均时间。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"Runs: Number of times Processor has been run.\"\nmsgstr \"Runs：处理器已运行的次数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst\nmsgid \"Lost: Number of packets lost during processing.\"\nmsgstr \"Lost: 处理过程中丢失的数据包数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:679\nmsgid \"vhost-man\"\nmsgstr \"vhost-man\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:684\nmsgid \"Checks is anonymous domain\"\nmsgstr \"Checks 是匿名域\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:684\nmsgid \"Number of anonymous domain checks that have been run within vhost-man.\"\nmsgstr \"在 vhost-man 中运行的匿名域检查的数量。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:684\nmsgid \"``vhost-man/Checks is anonymous domain``\"\nmsgstr \"``vhost-man/Checks is anonymous domain``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:686\nmsgid \"Checks: is local domain\"\nmsgstr \"Checks: 是本地域\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:686\nmsgid \"Number of local domain checks that have been run within vhost-man.\"\nmsgstr \"在 vhost-man 中运行的本地域检查的数量。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:686\nmsgid \"``vhost-man/Checks: is local domain``\"\nmsgstr \"``vhost-man/Checks: is local domain``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:688\nmsgid \"Get components for local domain\"\nmsgstr \"获取本地域的组件\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:688\nmsgid \"Number of components loaded within local domain.\"\nmsgstr \"在本地域中加载的组件数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:688\nmsgid \"``vhost-man/Get components for local domain``\"\nmsgstr \"``vhost-man/Get components for local domain``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:690\nmsgid \"Get components for non-local domain\"\nmsgstr \"获取非本地域的组件\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:690\nmsgid \"Number of components loaded outside local domain.\"\nmsgstr \"在本地域之外加载的组件数。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:690\nmsgid \"``vhost-man/Get components for non-local domain``\"\nmsgstr \"``vhost-man/Get components for non-local domain``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:692\nmsgid \"Number of Vhosts\"\nmsgstr \"虚拟主机数量\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:692\nmsgid \"Number of configured and running Virtual Hosts.\"\nmsgstr \"已配置和正在运行的虚拟主机的数量。\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:692\nmsgid \"``vhost-man/Number of VHosts``\"\nmsgstr \"``vhost-man/Number of VHosts``\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:696\nmsgid \"ws2s\"\nmsgstr \"ws2s\"\n\n#: ../../Tigase_Administration/Statistics_Description.rst:698\nmsgid \"No exclusive ws2s specific statistics.\"\nmsgstr \"没有专有的 ws2s 特定统计信息。\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Administration/Tigase_Server_Binary_Updates.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-05-27 12:30-0700\\n\"\n\"PO-Revision-Date: 2022-09-07 17:14+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/tigase_server_binary_updates/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:2\nmsgid \"Tigase Server Binary Updates\"\nmsgstr \"Tigase 服务器二进制更新\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:4\nmsgid \"\"\n\"Most open source projects try to make sure that the nightly builds \"\n\"compile correctly so that these builds can be used. However, we at Tigase\"\n\" believe that these builds should be separated until they are thoroughly \"\n\"tested and released. Although lots of installations out there we know of \"\n\"just run from our nightly builds, this puts an extra responsibility to \"\n\"make sure all code is functional and will constantly work. Therefore, our\"\n\" general approach is to run all basic functionality tests before each \"\n\"code commit to make sure it works correctly. This does not guarantee that\"\n\" there will never be a problem, but it is a precaution from preventing \"\n\"bad builds from arriving in the hands of our customers.\"\nmsgstr \"\"\n\"大多数开源项目都试图确保每夜构建正确编译，以方便其使用。但是，我们 Tigase 认\"\n\"为，在对这些构建未经过彻底测试和发布之前，应该将其分开。尽管我们知道很多安装\"\n\"都是从我们的每夜构建中运行的，但这也带来了额外的责任，以确保所有代码都能正常\"\n\"并且持续工作。因此，我们一般的做法是在每次代码提交之前运行所有基本功能测试，\"\n\"以确保其正常工作。虽然这并不能保证永远不会出现问题，但这是一种预防措施，可防\"\n\"止不良构建到达我们的客户手中。\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:6\nmsgid \"\"\n\"Some users on the other hand, like to be on the bleeding edge and \"\n\"regularly use our nightly builds exploring new code changes and playing \"\n\"with new features before they are put to a full release. Others prefer to\"\n\" stick to stable and fully tested public releases. Others however, want \"\n\"something from the middle, the most recent features, but bug fixes, \"\n\"something like a beta or a release-candidate state.\"\nmsgstr \"\"\n\"另一方面，一些用户喜欢站在最前沿，并定期使用我们的夜间构建来探索新的代码并在\"\n\"新功能发布之前使用它们。而其他人更喜欢使用稳定且经过全面测试的公开版本。除此\"\n\"以外，还有些人则介于前面两者之间，想要最新的功能，但是比如错误修复这些，更喜\"\n\"欢测试版或最终测试版本。\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:8\nmsgid \"\"\n\"Should you choose to use the nightly builds, a few things you should \"\n\"consider:\"\nmsgstr \"如果您选择使用每夜构建，您应该考虑以下几点：\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:10\nmsgid \"Changes may be made to the code that can negatively affect performance.\"\nmsgstr \"可能会对性能产生负面影响的代码进行更改。\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:12\nmsgid \"Changes may be made to the code that can negatively affect security.\"\nmsgstr \"可能会对安全性产生负面影响的代码进行更改。\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:14\nmsgid \"\"\n\"We **highly** recommend testing these builds in your environments before \"\n\"upgrading.\"\nmsgstr \"我们 **强烈** 建议在升级之前在您的环境中测试这些构建。\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:16\nmsgid \"\"\n\"With these considerations in mind, we provide nightly builds at `this \"\n\"link <https://build.tigase.net/nightlies/dists/>`__ which provides \"\n\"directories by date.\"\nmsgstr \"\"\n\"考虑到这些考虑，我们在 `此链接 <https://build.tigase.net/nightlies/dists/>`\"\n\"__ 提供了每夜构建，它按日期提供目录。\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:18\nmsgid \"\"\n\"Standard naming format is ``tigase-\"\n\"server-<version>-SNAPSHOT-b<build>-<type>`` where ``<version>`` is in the\"\n\" form of ``major.minor.bugfix``\"\nmsgstr \"\"\n\"标准命名格式为 ``tigase-server-<version>-SNAPSHOT-b<build>-<type>`` 其中 \"\n\"``<version>`` 的格式为 ``major.minor.bugfix``\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:22\nmsgid \"\"\n\"individual days may have the same builds as noted by the byyyy section of\"\n\" the file.\\\\*\"\nmsgstr \"个别日期可能具有与文件的 byyyy 部分所述相同的构建。\\\\*\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:24\nmsgid \"\"\n\"Just like the standard distributions, the builds are available with the \"\n\"following extensions (``<type>``):\"\nmsgstr \"就像标准发行版一样，构建可以使用以下扩展名(``<type>``):\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:26\nmsgid \"``javadoc.jar`` - Java installer for javadoc only\"\nmsgstr \"``javadoc.jar`` - 仅适用于 javadoc 的 Java 安装程序\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:28\nmsgid \"``dist.zip`` - Compressed binaries with no dependencies.\"\nmsgstr \"``dist.zip`` - 没有依赖关系的压缩二进制文件。\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:30\nmsgid \"``dist.tar.gz`` - tarball compressed binaries with no dependencies.\"\nmsgstr \"``dist.tar.gz`` - tarball 压缩的没有依赖项的二进制文件。\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:32\nmsgid \"``dist-max.zip`` - Compressed binaries with all dependencies.\"\nmsgstr \"``dist-max.zip`` - 具有所有依赖项的压缩二进制文件。\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:34\nmsgid \"``dist-max.tar.gz`` - tarball compressed binaries with all dependencies.\"\nmsgstr \"``dist-max.tar.gz`` - 具有所有依赖项的 tarball 压缩二进制文件。\"\n\n#: ../../Tigase_Administration/Tigase_Server_Binary_Updates.rst:36\nmsgid \"\"\n\"We also provide automated testing of each of our nightly builds for each \"\n\"supported databases. Tests are done with both functional and low memory \"\n\"parameters in mind, and are available `at this link \"\n\"<https://build.tigase.net/nightlies/tests/>`__. These tests can provide a\"\n\" quick examination of function before upgrading your current build.\"\nmsgstr \"\"\n\"我们还为每个受支持的数据库的每夜构建提供自动化测试。这些测试是在考虑功能和低\"\n\"内存参数的情况下完成的，并且可以在`此链接 <https://build.tigase.net/\"\n\"nightlies/tests/>`__ \"\n\"上获得。这些测试可以在升级当前版本之前提供对功能的快速检查。\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Administration/Tigase_User_Guide/User_Guide.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-05-27 12:30-0700\\n\"\n\"PO-Revision-Date: 2022-09-07 17:14+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/user_guide/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/User_Guide.rst:3\nmsgid \"Tigase User Guide\"\nmsgstr \"Tigase 用户指南\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Introduction.inc:2\nmsgid \"Jabber/XMPP introduction\"\nmsgstr \"Jabber/XMPP介绍\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Introduction.inc:5\nmsgid \"Jabber/XMPP is Instant Messaging Technology\"\nmsgstr \"Jabber/XMPP 是即时通讯技术\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Introduction.inc:7\nmsgid \"\"\n\"All federated **XMPP** servers are connected in one global communications\"\n\" network allowing you to send messages to friends who have accounts on \"\n\"other Jabber servers.\"\nmsgstr \"所有联合的 **XMPP** 服务器都连接在一个全球通信网络中，以允许您向在其他 \"\n\"Jabber 服务器上拥有帐户的朋友发送消息。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Introduction.inc:9\nmsgid \"\"\n\"This is very much like sending e-mail but the difference between Jabber \"\n\"and e-mail is the same as the difference between sending a traditional \"\n\"mail and talking on the phone.\"\nmsgstr \"这很像发送电子邮件，但 Jabber \"\n\"和电子邮件之间的区别就像发送传统信件和打电话之间的区别一样。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Introduction.inc:11\nmsgid \"\"\n\"All messages sent through Jabber are sent instantly and you also receive \"\n\"responses instantly. More over you can see whether your mate is online \"\n\"and available for talking or not.\"\nmsgstr \"通过 Jabber 发送的所有消息都会即时发送，您也会即时收到回复。此外，您还可以查\"\n\"看对方是否在线和是否可以交谈。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Introduction.inc:13\nmsgid \"\"\n\"There exists similar technologies to Jabber like WhatsApp Messenger, \"\n\"Facebook Messenger, Signal, Telegram, WeChat, QQ and other. There are, \"\n\"however, quite a few differences.\"\nmsgstr \"\"\n\"虽然当今有很多与 Jabber 类似的软件，比如 WhatsApp Messenger, Facebook \"\n\"Messenger, Signal,Telegram, 微信,QQ 等。但是他们之间也存在有很多不同之处。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Introduction.inc:15\nmsgid \"\"\n\"XMPP is an open standard which means everybody can know how it works, \"\n\"everybody can implement their own software connecting to the network both\"\n\" client and server side.\"\nmsgstr \"XMPP 是一个开放的标准，这意味着每个人都可以知道它是如何工作的，每个人都可以把\"\n\"自己的软件连接到客户端和服务器端的网络。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Introduction.inc:17\nmsgid \"\"\n\"The server side is actually the biggest difference and advantage. Many \"\n\"companies have offices in different locations, and such instant messaging\"\n\" technology could be very useful to employees for communication. \"\n\"Companies are not inclined to allow confidential discussions to go \"\n\"outside the company’s network. Especially if it is not very secure to \"\n\"leave such information on third party public servers.\"\nmsgstr \"\"\n\"服务器端其实是最大的区别和优势。许多公司在不同的地点设有办事处，这种即时通讯\"\n\"技术对员工之间的交流非常有用。公司不倾向于在公司网络之外进行机密讨论。特别是\"\n\"如果此类机密信息被保留在第三方公共服务器上，这样并不是很安全。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Introduction.inc:19\nmsgid \"\"\n\"XMPP servers on the other hand, allows you to deploy server software on \"\n\"your own company network. Employees can then talk securely and all \"\n\"information remains on the company’s secure network. Of course if offices\"\n\" are located in different locations or countries then all messages are \"\n\"transmitted over the public network - the Internet. This is not a problem\"\n\" since XMPP supports SSL/TLS - secure encrypted connections which helps \"\n\"you protect your discussion.\"\nmsgstr \"\"\n\"另一方面，XMPP 服务器允许您在自己的公司网络上部署服务器软件。这样员工可以安全\"\n\"地交谈，所有交流信息都保留在公司的安全网络上。当然，如果办公室位于不同的地方\"\n\"或国家，那么所有消息都需要通过公共网络——互联网传输。但是这也不是问题，因为 \"\n\"XMPP 支持 SSL/TLS - 安全加密连接，可帮助您保护您的信息。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Introduction.inc:21\nmsgid \"\"\n\"Then if your employees need to contact customers outside your company, \"\n\"the whole discussion can go through your server and a server located on \"\n\"the customer side.\"\nmsgstr \"除此之外，如果您的员工需要联系公司以外的客户，整个讨论也可以通过您的服务器和\"\n\"位于客户端的服务器进行。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Introduction.inc:23\nmsgid \"\"\n\"There are many other scenarios and use cases but I hope this brief \"\n\"introduction gives you an idea of the differences and advantages of XMPP \"\n\"technology.\"\nmsgstr \"还有很多其他的应用场景和实例，但我希望这个简短的介绍能让您更了解 XMPP \"\n\"技术的不同和优势。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:2\nmsgid \"How to Use Tigase Service\"\nmsgstr \"如何使用 Tigase 服务\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:6\nmsgid \"\"\n\"This Article Describes How to use **tigase.im** Service for Instant \"\n\"Communications\"\nmsgstr \"本文介绍如何使用 **tigase.im** 服务进行即时交流\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:8\nmsgid \"\"\n\"You have to install and run a Jabber client application to use the \"\n\"service.\"\nmsgstr \"首先必须安装并运行 Jabber 客户端应用程序才能使用该服务。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:10\nmsgid \"\"\n\"There are multiple domains available: tigase.im, sure.im, xmpp.cloud (and\"\n\" you can host your own domain as well)\"\nmsgstr \"有多个可用域：tigase.im, sure.im, xmpp.cloud（您也可以托管自己的域）\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:14\nmsgid \"Short instructions:\"\nmsgstr \"简短说明：\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:16\nmsgid \"\"\n\"Usually you just need to enter the user name of the form: user@tigase.im.\"\n\" Your XMPP client should take care of all other things as our service \"\n\"doesn’t need any special settings. If you don’t have an account on \"\n\"tigase.im server yet just tick the option to register new account. That’s\"\n\" it!\"\nmsgstr \"\"\n\"通常只需要输入如下格式的用户名：user@tigase.im。不需要任何特殊设置，XMPP \"\n\"客户端就应该可以在我们的服务器处理所有其他事情。如果您在 tigase.im \"\n\"服务器上没有帐户，只需勾选注册新帐户的选项即可。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:20\nmsgid \"**Long Instructions:**\"\nmsgstr \"**长指令说明:**\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:22\nmsgid \"\"\n\"Good news is that there are many programs to choose from which allow you \"\n\"to communicate through our server. So you can pick up your favorite \"\n\"application or use an existing one that is compatible and start using our\"\n\" service.\"\nmsgstr \"\"\n\"优点之一是有很多不同的程序可供您选择，并通过我们的服务器进行交流。因此，您可\"\n\"以选择自己喜欢的应用程序或使用现有的可兼容应用程序，然后开始使用我们的服务。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:24\nmsgid \"\"\n\"All clients presented below support multiple accounts on Jabber servers. \"\n\"What this means is that you can have a few Jabber accounts on different \"\n\"Jabber servers and you can still use just one program to connect to all \"\n\"of them at the same time.\"\nmsgstr \"\"\n\"下面介绍的所有客户端都支持在 Jabber 服务器上有多个账户。\"\n\"这意味着您可以在不同的 Jabber 服务器上拥有多个 Jabber \"\n\"帐户，并且您仍然可以只使用一个程序同时连接所有这些帐户。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:26\nmsgid \"\"\n\"The `full list of all known XMPP clients \"\n\"<https://xmpp.org/software/clients.html>`__ is very long. You can \"\n\"obviously try them all but below is a selection which is recommended by \"\n\"the Tigase team. The selected programs might not be the best choice for \"\n\"you, but these programs have been tested and we can offer help with using\"\n\" them. Here is a list of recommended instant messaging clients:\"\nmsgstr \"\"\n\"`所有已知 XMPP 客户端的完整列表 <https://xmpp.org/software/clients.html>`__ \"\n\"非常长。您可以尝试所有的这些，但以下是 Tigase 团队的推荐程序。所选程序也许不\"\n\"是您的最佳选择，但这些程序已经过测试，并且我们可以提供使用帮助。以下是推荐的\"\n\"即时消息客户端列表：\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:28\nmsgid \"\"\n\"`Beagle.im <https://beagle.im/>`__ - macOS desktop client developed by \"\n\"Tigase team supporting all the latest and greatest features\"\nmsgstr \"\"\n\"`Beagle.im <https://beagle.im/>`__ - 由 Tigase 团队开发的 macOS \"\n\"桌面客户端，支持所有最新和最强大的功能\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:30\nmsgid \"\"\n\"`Tigase Messenger for iOS <https://itunes.apple.com/us/app/tigase-\"\n\"messenger/id1153516838>`__ - lightweight, powerful XMPP client developed \"\n\"by Tigase, Inc. It provides an easy way to start using the XMPP Protocol \"\n\"(formerly known as Jabber) if you’ve never used it before. Veterans of \"\n\"the protocol will find many features here they are familiar with along \"\n\"with enhancements to reduce data use and extend battery life.\"\nmsgstr \"\"\n\"`Tigase Messenger for iOS <https://itunes.apple.com/us/app/tigase-messenger/\"\n\"id1153516838>`__ - 由 Tigase, Inc 开发的轻量级、强大的 XMPP \"\n\"客户端。如果您以前从未使用过协议（以前称为 Jabber），它提供了一种初始使用 \"\n\"XMPP 的简单方法。该协议的资深用户会在这里找到许多熟悉的功能，以及可以减少数据\"\n\"使用和延长电池寿命的进阶功能。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:32\nmsgid \"\"\n\"`Tigase Messenger for Android \"\n\"<https://play.google.com/store/apps/details?id=org.tigase.messenger.phone.pro>`__\"\n\" - mobile chat client to use with XMPP services and servers. The totally \"\n\"revamped v3.0 now has new features, a better design, and Google \"\n\"integration. Application supports any XMPP server, from free services \"\n\"like sure.im or Tigase.im, to a server you may host on your own.\"\nmsgstr \"\"\n\"`Tigase Messenger for Android <https://play.google.com/store/apps/\"\n\"details?id=org.tigase.messenger.phone.pro>`__ - 与 XMPP \"\n\"服务和服务器一起使用的移动聊天客户端。现在完全改版的 v3.0 具有更新功能，\"\n\"更好的设计和 Google 集成。此应用程序支持任何 XMPP 服务器，包括使用sure.im 或 \"\n\"Tigase.im 等免费服务和可以自己托管的服务器。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:34\nmsgid \"\"\n\"tigase.im[Tigase.im] - web-based client allowing to easily chat with \"\n\"friends independently of platform.\"\nmsgstr \"tigase.im[Tigase.im] - 基于网络的客户端，允许独立于平台轻松地与朋友聊天。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:36\nmsgid \"\"\n\"`Psi <http://psi-im.org/>`__ Pure Jabber client. Although it supports \"\n\"only Jabber network it is a very user friendly and comfortable program. \"\n\"It works on most popular operating systems like Linux, MS Windows, and \"\n\"Apple MacOS X.\"\nmsgstr \"\"\n\"`Psi <http://psi-im.org/>`__ 纯 Jabber 客户端。虽然它只支持 Jabber \"\n\"网络，但它是一个对用户友好和舒适的程序。它适用于大多数流行的操作系统，如 \"\n\"Linux、MS Windows 和 Apple MacOS X。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:38\nmsgid \"\"\n\"`Gajim <http://www.gajim.org/>`__ This is another Jabber only client. \"\n\"Very user friendly and works on most of Linux distributions, FreeBSD, and\"\n\" MS Windows.\"\nmsgstr \"\"\n\"`Gajim <http://www.gajim.org/>`__ 这是另一个只有 Jabber \"\n\"的客户端。非常人性化，适用于大多数 Linux 发行版，FreeBSD 和 MS Windows。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:40\nmsgid \"\"\n\"`Pidgin <http://www.pidgin.im/>`__ (previously `Gaim \"\n\"<http://gaim.sourceforge.net/>`__) This is not just a Jabber client. This\"\n\" type of application is called multicommunicator as apart from Jabber it \"\n\"supports many other instant messaging networks/protocols such as: \"\n\"AIM/ICQ, MSN, Yahoo, Gadu-Gadu, IRC, and a few others. So it is \"\n\"especially convenient if you have friends using other messaging networks.\"\n\" Pidgin works on most Linux distributions, and on MS Windows.\"\nmsgstr \"\"\n\"`Pidgin <http://www.pidgin.im/>`__ （以前的 `Gaim <http://gaim.sourceforge.\"\n\"net/>`__）这不仅仅是一个Jabber客户端。这种类型的应用程序被称为多通信器，\"\n\"因为除了 Jabber 之外，它还支持许多其他即时消息传递网络/协议，例如：AIM/\"\n\"ICQ、MSN、Yahoo、Gadu-Gadu、IRC \"\n\"等等。因此，如果您有使用其他消息网络的朋友，就会特别方便。 Pidgin \"\n\"适用于大多数 Linux 发行版和 MS Windows。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:42\nmsgid \"\"\n\"`Kopete <http://kopete.kde.org/>`__ This is a `KDE \"\n\"<http://www.kde.org/>`__ component and although it only works on Linux \"\n\"based system it also supports many of the most popular instant messaging \"\n\"protocols apart from Jabber like: AIM, Gadu-Gadu, ICQ, IRC, MSN, Yahoo.\"\nmsgstr \"\"\n\"`Kopete <http://kopete.kde.org/>`__ 这是一个 `KDE <http://www.kde.org/>`__ \"\n\"组件，虽然它只适用于基于 Linux 的系统，但它也支持许多除了 Jabber \"\n\"之外的最流行的即时通讯协议，比如：AIM、Gadu-Gadu、ICQ、IRC、MSN、Yahoo。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/How_to_Use.inc:44\nmsgid \"Install the Jabber client of your choice and set up for a Tigase account:\"\nmsgstr \"安装您选择的 Jabber 客户端并设置 Tigase 帐户：\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:2\nmsgid \"Configuration instructions for Psi\"\nmsgstr \"Psi 的配置说明\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:5\nmsgid \"Psi - Initial configuration\"\nmsgstr \"Psi - 初始配置\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:7\nmsgid \"The first time you run Psi you see a screen like this:\"\nmsgstr \"第一次运行 Psi 时，您会看到如下屏幕：\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:9\nmsgid \"|Psi First Run|\"\nmsgstr \"|Psi First Run|\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:109\nmsgid \"Psi First Run\"\nmsgstr \"Psi First Run\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:11\nmsgid \"\"\n\"To connect to tigase.org server we need to configure the program. Below \"\n\"are step-by-step instructions for novice users on how to setup Psi.\"\nmsgstr \"要连接到 tigase.org 服务器，我们需要配置程序。以下是针对新手用户如何设置 Psi \"\n\"的分步说明。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:13\nmsgid \"\"\n\"Psi can connect to many Jabber servers at the same time so we have to \"\n\"identify each connection somehow. The first thing to do is assign a name \"\n\"to the connection we just created. As we are going to define connection \"\n\"to tigase.org server let’s just name it: **Tigase**.\"\nmsgstr \"\"\n\"Psi 可以同时连接到多个 Jabber 服务器，因此我们必须以特定方式识别每个连接。首\"\n\"先要做的是为我们刚刚创建的连接分配一个名称。当我们要定义与 tigase.org \"\n\"服务器的连接时，我们将其命名为：**Tigase**。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:15\nmsgid \"|Psi Add Account|\"\nmsgstr \"|Psi Add Account|\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:110\nmsgid \"Psi Add Account\"\nmsgstr \"Psi Add Account\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:17\nmsgid \"\"\n\"**Note!** At the moment you can register an account through the Web site \"\n\"only. This is a single account for both services: The Drupal website and \"\n\"Jabber/XMPP service on the tigase.org domain. If you want to have a \"\n\"Jabber account on the tigase.org server go to the registration page, un-\"\n\"tick \\\"Register new account\\\", and go to the point no 5. You can use \"\n\"guide points 2-4 to register a Jabber account on any other Jabber server.\"\nmsgstr \"\"\n\"**注意！** 目前您只能通过网站注册帐户。这是拥有两个服务的一个帐户：Drupal \"\n\"网站和 tigase.org 域上的 Jabber/XMPP 服务。如果您想在 tigase.org \"\n\"服务器上拥有 Jabber 帐户，请转到注册页面，取消勾选 \\\"注册新帐户 \\\"，\"\n\"然后转到第 5 点。您可以参考使用指南的 2-4点 在任何其他 Jabber 服务器上注册 \"\n\"Jabber 帐户。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:19\nmsgid \"\"\n\"When you press the Add button you will see next window where you can \"\n\"enter your Jabber account details:\"\nmsgstr \"当您按下添加按钮时，您将看到下一个窗口，您可以在其中输入您的 Jabber \"\n\"帐户详细信息：\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:21\nmsgid \"|Psi Empty Account|\"\nmsgstr \"|Psi Empty Account|\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:111\nmsgid \"Psi Empty Account\"\nmsgstr \"Psi Empty Account\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:23\nmsgid \"\"\n\"Invent your user name for the account on Tigase server. Let’s assume your\"\n\" user name is: **frank**. Jabber ID’s however consist of 2 parts - your \"\n\"user name and server address. Exactly the same as an e-mail address. As \"\n\"you are registering an account on tigase.org server, you will have to \"\n\"enter in this field: **frank@tigase.org**. Next enter the password of \"\n\"your choice and click the Register button.\"\nmsgstr \"\"\n\"在 Tigase 服务器上的帐户上创建您的用户名。假设您的用户名是：**frank**。\"\n\"Jabber ID 由两部分组成 - 用户名和服务器地址。这个与电子邮件地址完全相同。\"\n\"当您在 tigase.org 服务器上注册帐户时，您必须在此字段中输入：**frank@tigase.\"\n\"org**。接下来输入您选择的密码，然后单击“注册”按钮。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:25\nmsgid \"|Psi Register Account|\"\nmsgstr \"|Psi Register Account|\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:112\nmsgid \"Psi Register Account\"\nmsgstr \"Psi 注册帐户\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:27\nmsgid \"\"\n\"On successful registration you will receive a confirmation message and \"\n\"you should see a window like this:\"\nmsgstr \"注册成功后，您将收到一条确认消息，您应该会看到如下窗口：\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:29\nmsgid \"|Register Account Success|\"\nmsgstr \"|Register Account Success|\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:113\nmsgid \"Register Account Success\"\nmsgstr \"Register Account Success\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:31\nmsgid \"\"\n\"It may happen that somebody earlier registered an account with the same \"\n\"name you’ve selected for yourself. If so, you will receive error message.\"\n\" You will then have to select another user name and try to register \"\n\"again.\"\nmsgstr \"有时候可能您为自己选择的用户名已经被其他人使用和注册了。如果是这样，您将收到\"\n\"错误消息。此时您必须选择另一个用户名并再次注册。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:33\nmsgid \"\"\n\"After clicking the **OK** button you will see a window with your \"\n\"connection and account setup. You can stick with default values for now.\"\nmsgstr \"单击 **OK** 按钮后，您将看到一个包含有您的连接和帐户设置的窗口。您现在可以使\"\n\"用这些默认值。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:35\nmsgid \"|PSI After Registration|\"\nmsgstr \"|PSI After Registration|\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:114\nmsgid \"PSI After Registration\"\nmsgstr \"PSI After Registration\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:37\nmsgid \"Just click the **Save** button and this window closes.\"\nmsgstr \"只需单击 **Save** 按钮，此窗口就会关闭。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:39\nmsgid \"\"\n\"Now you have your account configured and ready to use but you are still \"\n\"off-line. You can find out whether you are on-line or off-line by looking\"\n\" at the bottom of main Psi window. There you can see **Offline** text.\"\nmsgstr \"\"\n\"现在您已设置好帐户并可以使用，但此时您仍处于离线状态。您可以通过查看主 Psi \"\n\"窗口的底部来确定您是在线还是离线。在那里您可以看到 显示 **Offline** 。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:41\nmsgid \"\"\n\"Click on this **Offline** text and you will see a list of possible \"\n\"options. Just select **Online**.\"\nmsgstr \"单击 **Offline** ，您将看到一系列可选择的列表选项。选择 **Online**。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:43\nmsgid \"|PSI Connected|\"\nmsgstr \"|PSI Connected|\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:115\nmsgid \"PSI Connected\"\nmsgstr \"PSI Connected\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:45\nmsgid \"Now you are connected!\"\nmsgstr \"现在你已被连接！\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:47\nmsgid \"\"\n\"Well, you are now connected but how to talk to other people? How to add \"\n\"friends to the contact list? You can send a message to your friends \"\n\"straight away using the **Psi menu** option **New blank message**. It is \"\n\"much more convenient however, if you could see which of your friends is \"\n\"online and available for chatting and if you could start talking to your \"\n\"friend just by clicking on his name.\"\nmsgstr \"\"\n\"您现在已连接上，但如何与其他人交谈呢？如何将好友添加到联系人列表呢？\"\n\"您可以使用 **Psi menu** 选择 **New blank message** 立即向您的朋友发送消息。但\"\n\"是如果您可以看到哪些朋友在线并且可以聊天，并且您可以通过单击他的名字开始与之\"\n\"交谈，那么这样沟通会更方便。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:51\nmsgid \"Short Instructions How to Add Your First Contact\"\nmsgstr \"简短说明如何添加您的第一个联系人\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:53\nmsgid \"\"\n\"Click on Psi menu - the button next to the **Online** text. You will see \"\n\"something like this:\"\nmsgstr \"单击 Psi 菜单 - **Online** 文字旁边的按钮。你会看到如下界面：\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:55\nmsgid \"|PSI Menu|\"\nmsgstr \"|PSI Menu|\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:116\nmsgid \"PSI Menu\"\nmsgstr \"PSI Menu\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:57\nmsgid \"From all menu options select the top one - Add a contact:\"\nmsgstr \"从所有菜单选项中选择最上面的选项 - 添加联系人：\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:59\nmsgid \"|PSI Menu add Contact|\"\nmsgstr \"|PSI Menu add Contact|\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:117\nmsgid \"PSI Menu add Contact\"\nmsgstr \"PSI Menu add Contact\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:61\nmsgid \"The next window will display where you can enter your contact details:\"\nmsgstr \"下一个窗口将显示您可以在哪里输入您的联系方式：\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:63\nmsgid \"|PSI Add User Empty|\"\nmsgstr \"|PSI Add User Empty|\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:118\nmsgid \"PSI Add User Empty\"\nmsgstr \"PSI Add User Empty\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:65\nmsgid \"\"\n\"You have to know the Jabber ID of the person you want to add to your \"\n\"contact list. Let’s assume, for example, you want to add Tigase server \"\n\"administrator’s Jabber ID to your contact list. So, after you enter these\"\n\" details the window will look like this:\"\nmsgstr \"\"\n\"您必须要知道想要添加的联系人的 Jabber ID。例如，您想将 Tigase 服务器管理员的 \"\n\"Jabber ID 添加到您的联系人列表中。在输入这些详细信息后，窗口将如下所示：\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:67\nmsgid \"|PSI Add User Filled|\"\nmsgstr \"|PSI Add User Filled|\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:119\nmsgid \"PSI Add User Filled\"\nmsgstr \"PSI Add User Filled\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:69\nmsgid \"Click the **Add** button.\"\nmsgstr \"单击**Add**按钮。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:71\nmsgid \"\"\n\"Now you will see a confirmation window that a new person has been added \"\n\"to your contact list:\"\nmsgstr \"现在您将看到一个确认窗口表明您添加了一个新的联系人：\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:73\nmsgid \"|PSI Kobit Added|\"\nmsgstr \"|PSI Kobit Added|\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:120\nmsgid \"PSI Kobit Added\"\nmsgstr \"PSI Kobit Added\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:75\nmsgid \"\"\n\"But there is more behind the scenes. Adding a contact to your **Roster** \"\n\"(contact list) usually means you can see whether the person is online and\"\n\" available to talk or not. The person however, may not wish you to see \"\n\"his presence. So, to make sure the other person accepts you as a friend \"\n\"Psi sent a request to the address you just entered with the question of \"\n\"whether he agrees to show his presence to you.\"\nmsgstr \"\"\n\"但幕后还有更多。将联系人添加到您的**名册**（联系人列表）通常意味着您可以查看\"\n\"此人是否在线并且是否可以交谈。但是，此人可能不希望您看到他的存在。因此，为了\"\n\"确保对方接受你为朋友，Psi \"\n\"向你刚刚输入的地址发送了一个请求，询问他是否同意向你表示他的存在。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:77\nmsgid \"\"\n\"You won’t be able to see the users availability until he sends \"\n\"confirmation.\"\nmsgstr \"在他发送确认之前，您将无法看到用户的可用性。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:79\nmsgid \"\"\n\"Once the other user sends confirmation back, you will usually receive 2 \"\n\"system events:\"\nmsgstr \"一旦其他用户发回确认，您通常会收到 2 个系统事件：\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:81\nmsgid \"|PSI Kobit Auth Received|\"\nmsgstr \"|PSI Kobit Auth Received|\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:121\nmsgid \"PSI Kobit Auth Received\"\nmsgstr \"PSI Kobit Auth Received\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:83\nmsgid \"Click on the contact to see a window with these messages:\"\nmsgstr \"单击联系人查看包含以下消息的窗口：\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:85\nmsgid \"|PSI Authorized Window|\"\nmsgstr \"|PSI Authorized Window|\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:122\nmsgid \"PSI Authorized Window\"\nmsgstr \"PSI Authorized Window\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:87\nmsgid \"One message just says you have been authorized by the other user:\"\nmsgstr \"一条消息只是说您已被其他用户授权：\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:89\nmsgid \"|PSI Authorized Window 2|\"\nmsgstr \"|PSI Authorized Window 2|\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:123\nmsgid \"PSI Authorized Window 2\"\nmsgstr \"PSI Authorized Window 2\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:91\nmsgid \"So you simply click **Next** to see the second message.\"\nmsgstr \"因此，您只需单击 **Next** 即可查看第二条消息。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:93\nmsgid \"\"\n\"The second message is a bit more interesting. It contains the question of\"\n\" whether you also authorize the other user to see your presence. If you \"\n\"want to accept this request just click **Add/Auth**.\"\nmsgstr \"第二条消息更有趣。它包含您是否还授权其他用户看到您的存在的问题。如果您想接受\"\n\"此请求，只需单击 **Add/Auth**。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:95\nmsgid \"|PSI Authorized Window 3|\"\nmsgstr \"|PSI Authorized Window 3|\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:124\nmsgid \"PSI Authorized Window 3\"\nmsgstr \"PSI Authorized Window 3\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:97\nmsgid \"Finally main Psi window with your new contact:\"\nmsgstr \"最后这是您的新联系人的主 Psi 窗口：\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:99\nmsgid \"|PSI Kobit Added Authorized|\"\nmsgstr \"|PSI Kobit Added Authorized|\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:125\nmsgid \"PSI Kobit Added Authorized\"\nmsgstr \"PSI Kobit Added Authorized\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:101\nmsgid \"Well done!\"\nmsgstr \"做得好！\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:103\nmsgid \"You are ready to start Jabbering. Good luck.\"\nmsgstr \"您已准备好开始 Jabbering。祝你好运。\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:105\nmsgid \"\"\n\"Where to go next? For detailed Psi documentation refer to the program \"\n\"Wiki page: http://psi-im.org/wiki/Main_Page\"\nmsgstr \"下一步去哪里？有关详细的 Psi 文档，请参阅程序 Wiki 页面：http://psi-im.org/\"\n\"wiki/Main_Page\"\n\n#: ../../Tigase_Administration/Tigase_User_Guide/Configuration_Instructions.inc:107\nmsgid \"Welcome to the Tigase Administration Guide.\"\nmsgstr \"欢迎使用 Tigase 管理指南。\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Administration/Using_Tigase/_using_tigase.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: 2022-09-20 00:37+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/using_tigasse/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:2\nmsgid \"Using Tigase\"\nmsgstr \"使用 Tigase\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:4\nmsgid \"\"\n\"This section keeps set of documents which apply to all the Tigase server \"\n\"version and contain more generic or introductory information on general \"\n\"use and features.\"\nmsgstr \"本节保留了适用于所有 Tigase 服务器版本的文档集，并包含有关一般用途和功能的更多通用或介绍性信息。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:6\nmsgid \":ref:`Tigase Log Guide<Tigase-Log-Guide>`\"\nmsgstr \":ref:`Tigase 日志指南<Tigase-Log-Guide>`\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:8\nmsgid \":ref:`Debugging Tigase<Debuging-Tigase>`\"\nmsgstr \":ref:`调试Tigase<Debuging-Tigase>`\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:10\nmsgid \":ref:`Basic System Checks<Basic-System-Checks>`\"\nmsgstr \":ref:`基本系统检查<Basic-System-Checks>`\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:12\nmsgid \":ref:`Add and Manage Domains<addManageDomain>`\"\nmsgstr \":ref:`添加和管理域<addManageDomain>`\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:14\nmsgid \":ref:`Presence Forwarding<Presence-Forwarding>`\"\nmsgstr \":ref:`Presence转发<Presence-Forwarding>`\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:16\nmsgid \":ref:`Watchdog<Watchdog>`\"\nmsgstr \":ref:`监视器<Watchdog>`\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:18\nmsgid \"\"\n\":ref:`Runtime Environment Tip<Tigase-Tip-Checking-the-Runtime-\"\n\"Environment>`\"\nmsgstr \":ref:`运行环境提示<Tigase-Tip-Checking-the-Runtime-Environment>`\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:20\nmsgid \":ref:`Checking Cluster Connections<Checking-Cluster-Connections>`\"\nmsgstr \":ref:`检查集群连接<Checking-Cluster-Connections>`\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:22\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:10\nmsgid \"\"\n\":ref:`Best Practices for Connecting to Tigase XMPP server From Web \"\n\"Browser<Best-Practices-for-Connecting-to-Tigase-XMPP-server-From-Web-\"\n\"Browser>`\"\nmsgstr \"\"\n\":ref:`从 Web 浏览器连接到 Tigase XMPP 服务器的最佳实践<Best-Practices-for-\"\n\"Connecting-to-Tigase-XMPP-server-From-Web-Browser>`\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:24\nmsgid \":ref:`Scripting Support in Tigase<Scripting-support-in-Tigase>`\"\nmsgstr \":ref:`Tigase 中的脚本支持<Scripting-support-in-Tigase>`\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:26\nmsgid \"\"\n\":ref:`Scripting Introduction - Hello World!<Scripting-Introduction - \"\n\"Hello-World!>`\"\nmsgstr \":ref:`脚本介绍- Hello World!<Scripting-Introduction - Hello-World!>`\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:28\nmsgid \"\"\n\":ref:`Tigase Scripting Version 4.4.x Update for Administrators<Tigase-\"\n\"Scripting-Version-4.4.x-Update-for-Administrators>`\"\nmsgstr \"\"\n\":ref:`面向管理员的 Tigase 脚本版本 4.4.x 更新<Tigase-Scripting-Version-4.4.x\"\n\"-Update-for-Administrators>`\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:30\nmsgid \":ref:`Tigase and Python Scripting<Tigase-and-Python>`\"\nmsgstr \":ref:`Tigase 和 Python 脚本<Tigase-and-Python>`\"\n\n#: ../../Tigase_Administration/Using_Tigase/Intro.inc:32\nmsgid \":ref:`Configuration Wizards<tigase3xconfiguration>`\"\nmsgstr \":ref:`配置向导<tigase3xconfiguration>`\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:2\nmsgid \"Offline Messages\"\nmsgstr \"离线消息\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:4\nmsgid \"\"\n\"Tigase like any XMPP server supports storing of messages for users who \"\n\"are offline so that they may receive messages sent to them while they \"\n\"were not logged in.\"\nmsgstr \"Tigase 像任何 XMPP 服务器一样支持为离线用户存储消息，以便他们可以在未登录时接收发送给他们的消息。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:6\nmsgid \"\"\n\"By default, Tigase ``MessageAmp`` processor is responsible for storing \"\n\"offline messages, and will automatically store offline messages. This \"\n\"guide has multiple sections for setting limits globally, per user, and \"\n\"others.\"\nmsgstr \"\"\n\"默认情况下，Tigase ``MessageAmp`` \"\n\"处理器负责存储离线消息，并且会自动存储离线消息。本指南包含多个部分，用于在全局，每个用户等其他方面设置限制。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:8\nmsgid \"\"\n\"Many of the features listed here require the use of the Advanced Message \"\n\"Processor Plugin which is turned on by default. To ensure AMP is turned \"\n\"on your system, view your ``config.tdsl`` file and be sure the following \"\n\"is there in your plugins line:\"\nmsgstr \"\"\n\"此处列出的许多功能都需要使用默认打开的高级消息处理器插件。为确保 AMP 已打开您的系统，请查看您的 ``config.tdsl`` \"\n\"文件并确保您的 plugins 行中有以下内容：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:16\nmsgid \"\"\n\"Messages will be delivered to intended recipients when they first login \"\n\"after roster exchange.\"\nmsgstr \"当他们在名册交换后首次登录时，消息将被传递给预期的收件人。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:19\nmsgid \"Offline Message Limits\"\nmsgstr \"离线消息限制\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:21\nmsgid \"\"\n\"Support for limiting number of stored offline messages on a per-user \"\n\"basis has now been added to Tigase as of v7.1.0. By default, Tigase comes\"\n\" with a limit of stored offline messages which is set for every user. \"\n\"This limit by default is 100 offline messages for barejid-barejid pair. \"\n\"This value can be changed by the ``store-limit`` property. To change to \"\n\"200 messages on barejid-barejid paid, add the following entries to the \"\n\"``config.tdsl`` file:\"\nmsgstr \"\"\n\"自 v7.1.0 起，Tigase 现已添加对基于每个用户限制存储的离线消息数量的支持。默认情况下，Tigase \"\n\"带有为每个用户设置的存储离线消息的限制。默认情况下，barejid-barejid 对的此限制为 100 条脱机消息。这个值可以通过 \"\n\"``store-limit`` 属性改变。要在 barejid-barejid 上付费更改为 200 条消息，请将以下条目添加到 \"\n\"``config.tdsl`` 文件中：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:34\nmsgid \"This setting applies to every user.\"\nmsgstr \"此设置适用于每个用户。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:37\nmsgid \"User Limit\"\nmsgstr \"用户限制\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:39\nmsgid \"\"\n\"Each user is able to configure the number of offline messages which \"\n\"should be stored for him. To enable this feature, the following lines \"\n\"need to be entered into the ``config.tdsl`` file:\"\nmsgstr \"每个用户都可以配置应该为他存储的离线消息的数量。要启用此功能，需要在 ``config.tdsl`` 文件中输入以下行：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:52\nmsgid \"\"\n\"Values of user-specific limits will be stored in UserRepository under \"\n\"subnode of ``offline-msgs`` and key ``store-limit``. Data storage will be\"\n\" stored in ``tig_pairs`` key with the value and a proper record from \"\n\"``tig_nodes`` points to this record.\"\nmsgstr \"\"\n\"用户特定限制的值将存储在 ``offline-msgs`` 子节点和 ``store-limit`` 键下的 UserRepository \"\n\"中。数据存储将存储在 ``tig_pairs`` 键中，其值和来自 ``tig_nodes`` 的适当记录指向该记录。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:55\nmsgid \"Handling of Offline Messages Exceeding Limits\"\nmsgstr \"离线消息超限处理\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:57\nmsgid \"\"\n\"There are two possible ways to handle offline messages that exceed the \"\n\"limitations: . ``error`` sending message with error type back to sender. \"\n\". ``drop`` drop of message without notifications to sender.\"\nmsgstr \"\"\n\"有两种可能的方式来处理超出限制的离线消息： . ``error`` 将带有错误类型的消息发送回发件人。 . ``drop`` \"\n\"在没有通知发件人的情况下丢弃消息。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:59\nmsgid \"\"\n\"By default, Tigase sends a message back to the original sender with an \"\n\"error type of ``service-unavailable`` with a proper description of error \"\n\"according to `XEP-0160 <http://www.xmpp.org/extensions/xep-0160.html>`__.\"\n\" However, it is possible to change this behavior to better suit your \"\n\"needs. This is done by adding the following line to your ``config.tdsl`` \"\n\"file.\"\nmsgstr \"\"\n\"默认情况下，Tigase 向原始发件人发送一条错误类型为 ``service-unavailable`` 的消息，并根据 `XEP-0160 \"\n\"<http://www.xmpp.org/extensions/xep-0160.html>`__ \"\n\"正确描述错误。但是，可以更改此行为以更好地满足您的需求。这是通过将以下行添加到您的 ``config.tdsl`` 文件来完成的。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:69\nmsgid \"\"\n\"This will force Tigase to drop packets that exceed the offline message \"\n\"limit.\"\nmsgstr \"这将强制 Tigase 丢弃超过离线消息限制的数据包。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:72\nmsgid \"Setting the Limits by User\"\nmsgstr \"按用户设置限制\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:74\nmsgid \"\"\n\"Users wishing to set a custom limit of stored offline messages for \"\n\"barejid-barejid pairs needs to send the following XMPP stanza to the \"\n\"server:\"\nmsgstr \"希望为 barejid-barejid 对设置存储离线消息的自定义限制的用户需要将以下 XMPP 节发送到服务器：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:82\nmsgid \"\"\n\"Where: . ``${random-id}`` is a random ID of the stanza (can be any \"\n\"string). . ``${limit}`` is the integer value of the offline message \"\n\"limit. This can be set to ``false`` to disable offline message limits.\"\nmsgstr \"\"\n\"其中：. ``${random-id}`` 是节的随机 ID（可以是任何字符串）。. ``${limit}`` 是离线消息限制的整数值。 \"\n\"其可以设置为 ``false`` 以禁用离线消息限制。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:84\nmsgid \"\"\n\"In response, the server will send back an ``iq`` stanza with a result \"\n\"type:\"\nmsgstr \"作为响应，服务器将返回一个带有结果类型的 ``iq`` 节：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:92\nmsgid \"Example of Setting Limit of Stored Offline Messages to 10\"\nmsgstr \"将存储的离线消息限制设置为 10 的示例\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:94\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:112\nmsgid \"XMPP client sends the following to the server:\"\nmsgstr \"XMPP 客户端向服务器发送以下内容：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:102\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:120\nmsgid \"Server response:\"\nmsgstr \"服务器响应：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:110\nmsgid \"Example of Disabling Offline Message Limit\"\nmsgstr \"禁用离线消息限制示例\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:129\nmsgid \"Storing offline messages without body content\"\nmsgstr \"存储没有正文内容的离线消息\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:131\nmsgid \"Tigase can now store offline messages without ``<body/>`` content.\"\nmsgstr \"Tigase 现在可以存储没有 ``<body/>`` 内容的离线消息。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:133\nmsgid \"\"\n\"See `XEP-0334 <http://xmpp.org/extensions/xep-0334.html>`__ for protocol \"\n\"details.\"\nmsgstr \"有关协议详细信息，请参阅 `XEP-0334 <http://xmpp.org/extensions/xep-0334.html>`__。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:135\nmsgid \"\"\n\"This can include message receipts, and messages with specific ``do-not-\"\n\"store`` tags.\"\nmsgstr \"这可以包括消息回执和带有特定 ``do-not-store`` 标签的消息。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:137\nmsgid \"\"\n\"Support has been added to set a list of paths and xmlns to trigger and \"\n\"place storage of offline messages using the following settings in \"\n\"``config.tdsl``:\"\nmsgstr \"添加了支持以设置路径列表和 xmlns 以使用 ``config.tdsl`` 中的以下设置触发和放置离线消息的存储：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:147\nmsgid \"This example results in two settings:\"\nmsgstr \"此示例产生两个设置：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:150\nmsgid \"``/message/received[urn:xmpp:receipts]``\"\nmsgstr \"``/message/received[urn:xmpp:receipts]``\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:150\nmsgid \"\"\n\"Results in storage of messages with a ``recieved`` subelement and with \"\n\"the xlmns set to ``urn:xmpp:receipts``\"\nmsgstr \"结果存储带有 ``recieved`` 子元素和xlmns设置为 ``urn:xmpp:receipts`` 的消息\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:153\nmsgid \"``/message/store-offline``\"\nmsgstr \"``/message/store-offline``\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:153\nmsgid \"\"\n\"Results in storing messages with a ``store-offline`` subelement without \"\n\"checking xmlns.\"\nmsgstr \"导致在不检查 xmlns 的情况下使用 ``store-offline`` 子元素存储消息。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:156\nmsgid \"Filtering of offline storage\"\nmsgstr \"过滤离线存储\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:158\nmsgid \"It is possible to set storage of other types to save:\"\nmsgstr \"可以设置其他类型的存储来保存：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:168\nmsgid \"The above setting in the ``config.tdsl`` file will cause that:\"\nmsgstr \"``config.tdsl`` 文件中的上述设置将导致：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:170\nmsgid \"\"\n\"messages with ``<store-offline>`` subelement will be stored without \"\n\"checking for associated xmlns.\"\nmsgstr \"带有 ``<store-offline>`` 子元素的消息将被存储而不检查关联的 xmlns。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:172\nmsgid \"messages with ``<do-not-store>`` element **will not** be saved.\"\nmsgstr \"带有 ``<do-not-store>`` 元素的消息 **不会** 被保存。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:174\nmsgid \"\"\n\"Any of these can be adjusted for your installation, remember that a '-' \"\n\"will stop storage of messages with the indicated property. Messages will \"\n\"be checked by these matchers and if any of them result in a positive they\"\n\" will override default settings.\"\nmsgstr \"\"\n\"这些中的任何一个都可以根据您的安装进行调整，请记住，'-' \"\n\"将停止存储具有指定属性的消息。这些匹配器将检查消息，如果其中任何一个结果为肯定，它们将覆盖默认设置。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:176\nmsgid \"\"\n\"For example, if you wanted to store messages with <received> element, but\"\n\" not ones with <plain> element, your filter will look like this:\"\nmsgstr \"例如，如果您想存储带有 <received> 元素的消息，而不是带有 <plain> 元素的消息，您的过滤器将如下所示：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:186\nmsgid \"However…​.\"\nmsgstr \"然而…​.\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:190\nmsgid \"\"\n\"**THE ABOVE STATEMENT WILL NOT WORK** As it will just store all messages \"\n\"with <received> subelement.\"\nmsgstr \"**上述声明将不起作用** 因为它只会存储所有带有 <received> 子元素的消息。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:192\nmsgid \"The below statement will properly filter your results.\"\nmsgstr \"以下语句将正确过滤您的结果。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:202\nmsgid \"\"\n\"Filtering logic is done in order from left to right. Matches on the first\"\n\" statement will ignore or override matches listed afterwards.\"\nmsgstr \"过滤逻辑按从左到右的顺序完成。第一条语句上的匹配项将忽略或覆盖后面列出的匹配项。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:205\nmsgid \"Disabling Offline Messages\"\nmsgstr \"禁用离线消息\"\n\n#: ../../Tigase_Administration/Using_Tigase/Offline_Messages.inc:207\nmsgid \"\"\n\"If you wish to disable the storing of offline messages, use the following\"\n\" line in your ``config.tdsl`` file. This will not disable other features \"\n\"of the AMP plugin.\"\nmsgstr \"如果您希望禁用离线消息的存储，请在 ``config.tdsl`` 文件中使用以下行。这不会禁用 AMP 插件的其他功能。\"\n\n#: ../../Tigase_Administration/Using_Tigase/LastActivity.inc:2\nmsgid \"Last Activity\"\nmsgstr \"最后的活动\"\n\n#: ../../Tigase_Administration/Using_Tigase/LastActivity.inc:4\nmsgid \"\"\n\"Tigase XMPP Server supports `XEP-0012: Last Activity \"\n\"<https://xmpp.org/extensions/xep-0012.html>`__ extension, which allows \"\n\"retrieval information when particular contact was active last time. It’s \"\n\"not enabled by default.\"\nmsgstr \"\"\n\"Tigase XMPP 服务器支持 `XEP-0012: Last Activity \"\n\"<https://xmpp.org/extensions/xep-0012.html>`__ \"\n\"扩展，允许在特定联系人最后处于活动状态时检索信息。默认情况下未启用。\"\n\n#: ../../Tigase_Administration/Using_Tigase/LastActivity.inc:6\nmsgid \"The functionality itself is split in two plugins:\"\nmsgstr \"功能本身分为两个插件：\"\n\n#: ../../Tigase_Administration/Using_Tigase/LastActivity.inc:8\nmsgid \"\"\n\"``jabber:iq:last-marker`` - responsible for updating information about \"\n\"last activity of user\"\nmsgstr \"``jabber:iq:last-marker`` - 负责更新有关用户最后活动的信息\"\n\n#: ../../Tigase_Administration/Using_Tigase/LastActivity.inc:10\nmsgid \"\"\n\"``jabber:iq:last`` - responsible for handling requests to retrieve last \"\n\"activity information (it depends on ``jabber:iq:last-marker`` plugin).\"\nmsgstr \"``jabber:iq:last`` - 负责处理请求以检索最后活动信息 (这取决于 ``jabber:iq:last-marker`` 插件)。\"\n\n#: ../../Tigase_Administration/Using_Tigase/LastActivity.inc:12\nmsgid \"\"\n\"In order to enable functionality you should add both plugins to your \"\n\"configuration file\"\nmsgstr \"为了启用功能，您应该将两个插件都添加到配置文件中\"\n\n#: ../../Tigase_Administration/Using_Tigase/LastActivity.inc:23\nmsgid \"What updates last activity\"\nmsgstr \"更新最后活动的内容\"\n\n#: ../../Tigase_Administration/Using_Tigase/LastActivity.inc:25\nmsgid \"\"\n\"By default marker plugin will only update last activity information on \"\n\"presence stanza. It’s possible to control whether ``<presence/>`` and/or \"\n\"``<message/>`` should update with respective options:\"\nmsgstr \"\"\n\"默认情况下，标记插件只会更新存在节上的最后一个活动信息。可以控制 ``<presence/>`` 和/或 ``<message/>`` \"\n\"是否应该使用各自的选项进行更新：\"\n\n#: ../../Tigase_Administration/Using_Tigase/LastActivity.inc:36\nmsgid \"\"\n\"Those settings will cause updating last activity information for both \"\n\"``<message/>`` and ``<presence/>`` stanzas\"\nmsgstr \"这些设置将导致更新 ``<message/>`` 和 ``<presence/>`` 节的最后活动信息\"\n\n#: ../../Tigase_Administration/Using_Tigase/LastActivity.inc:39\nmsgid \"Persist everything to repository\"\nmsgstr \"将所有内容持久化到存储库\"\n\n#: ../../Tigase_Administration/Using_Tigase/LastActivity.inc:41\nmsgid \"\"\n\"To lower impact on performance, by default last activity information is \"\n\"persisted to repository less frequently. This can yield slightly less \"\n\"accurate results on installations with multiple cluster nodes with users \"\n\"having multiple resources connected. To get more accurate results you \"\n\"should set ``persistAllToRepository`` to ``true``, which will cause all \"\n\"update times to be persisted (please bear in mind that this could cause \"\n\"higher impact on the repository).\"\nmsgstr \"\"\n\"为了降低对性能的影响，默认情况下最后一个活动信息不那么频繁地保存到存储库。在具有多个集群节点且用户连接了多个资源的安装中，这可能会产生稍微不太准确的结果。为了获得更准确的结果，您应该将\"\n\" ``persistAllToRepository`` 设置为 \"\n\"``true``，这将导致所有更新时间都被持久化（请记住，这可能会对存储库造成更大的影响）。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:4\nmsgid \"Tigase Log Guide\"\nmsgstr \"Tigase 日志指南\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:6\nmsgid \"\"\n\"Tigase has multiple levels of logging available to help provide targeted \"\n\"and detailed information on processes, components, or traffic. In these \"\n\"documents we will look at where tigase generates logs, what they contain,\"\n\" and how we can customize them to our needs.\"\nmsgstr \"\"\n\"Tigase 具有多个级别的日志记录，可帮助提供有关进程，组件或流量的有针对性的详细信息。在这些文档中，我们将了解 tigase \"\n\"生成日志的位置，它们包含的内容，以及我们如何根据需要自定义它们。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:9\nmsgid \"install.log\"\nmsgstr \"install.log\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:11\nmsgid \"\"\n\"This log file is a basic list of files that are made on install of Tigase\"\n\" server. Although you may not need to use it, it can provide a handy list\"\n\" to see if any files were not written to your hard drive upon \"\n\"installation.\"\nmsgstr \"\"\n\"此日志文件是安装 Tigase \"\n\"服务器时生成的文件的基本列表。尽管您可能不需要使用它，但它可以提供一个方便的列表，以查看安装时是否有任何文件未写入您的硬盘驱动器。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:14\nmsgid \"derby.log\"\nmsgstr \"derby.log\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:16\nmsgid \"\"\n\"If you are using the derby database installed with Tigase, this is the \"\n\"startup log for the database itself. Issues that might be related to the \"\n\"database, can be found in this file. Typically, if everything works okay,\"\n\" it’s a very small file with only 10 lines. It is overwritten on startup \"\n\"of the database.\"\nmsgstr \"\"\n\"如果您使用的是与 Tigase 一起安装的 derby \"\n\"数据库，这则是数据库本身的启动日志。可以在此文件中找到可能与数据库相关的问题。通常，如果一切正常，它就是一个只有 10 \"\n\"行的非常小的文件。它在数据库启动时被覆盖。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:19\nmsgid \"etc/config-dump.properties\"\nmsgstr \"etc/config-dump.properties\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:21\nmsgid \"\"\n\"The config-dump.properties is dump file of all your properties listed for\"\n\" every option within Tigase and components. The structure of the log \"\n\"lines is the same as the structure of Tigase XMPP Server config file - \"\n\"TDSL. Lets take the value for admins, listing who is administrator for \"\n\"the server.\"\nmsgstr \"\"\n\"config-dump.properties 是为 Tigase 和组件中的每个选项列出的所有属性的转储文件。日志行的结构与 Tigase \"\n\"XMPP Server 配置文件 - TDSL 的结构相同。让我们取 admins 的值，列出谁是服务器的管理员。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:27\nmsgid \"The admin parameter which is an array of strings and has 3 users listed.\"\nmsgstr \"admin 参数是一个字符串数组，列出了 3 个用户。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:29\nmsgid \"This file is re-written every time tigase starts.\"\nmsgstr \"每次启动 tigase 时都会重写此文件。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:32\nmsgid \"logs/tigase.log.#\"\nmsgstr \"logs/tigase.log.#\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:34\nmsgid \"\"\n\"The tigase.log files are where the majority of logging will take place. \"\n\"The rules for writing to these longs can be manipulated by editing files \"\n\"in the int.properties file. To see how, see the :ref:`Debugging Tigase\"\n\"<Debuging-Tigase>` section of this manual for more details about how to \"\n\"turn on debug logging, and how to manipulate log settings. Entries to \"\n\"these logs are made in the following format:\"\nmsgstr \"\"\n\"tigase.log 文件是进行大部分日志记录的地方。可以通过编辑 int.properties \"\n\"文件中的文件来操作写入这些 long 的规则。要了解如何操作，请参阅本手册的 \"\n\":ref:`调试Tigase<Debuging-Tigase>` 部分，了解有关如何打开调试日志记录以及如何\"\n\"操作日志设置的更多详细信息。这些日志的条目采用以下格式：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:40\nmsgid \"\"\n\"The format of these logs is below: ``<timestamp> <thread_name> \"\n\"<class>.<method>    <log_level>: <message> <thread_name>``. This can vary\"\n\" - for components it would be ``<direction>_<int>_<component name>``, for\"\n\" plugins it will just be the plugin name.\"\nmsgstr \"\"\n\"这些日志的格式如下：``<timestamp> <thread_name> <class>.<method>    <log_level>: \"\n\"<message> <thread_name>``。这可能会有所不同 - 对于组件，它将是 \"\n\"``<direction>_<int>_<component name>``，对于插件，它将只是插件名称。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:42\nmsgid \"Let’s look at another example from the log file.\"\nmsgstr \"让我们看一下日志文件中的另一个示例。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:48\nmsgid \"\"\n\"The process ID may sometimes come in a different format such as \"\n\"``[in_14-muc]`` which specifies the component (muc) along with the \"\n\"process thread identifier (14). As you can see, the format otherwise is \"\n\"nearly identical.\"\nmsgstr \"\"\n\"进程 ID 有时可能以不同的格式出现，例如 ``[in_14-muc]`` ，它指定组件 (muc) 以及进程线程标识符 \"\n\"(14)。如您所见，其他方面的格式几乎相同。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:50\nmsgid \"\"\n\"``tigase.log.#`` files are *rotated* - this means that server begins \"\n\"writing to tigase.log.0 when it is first run, and continues to dump \"\n\"information until the log size limit is hit. At this point, Tigase \"\n\"renames tigase.log.0 as tigase.log.1. A new tigase.log.0 will be created,\"\n\" and Tigase will begin logging to this file. When this file is full, \"\n\"tigase.log.1 will be renamed tigase.log.2 and tigase.log.0 will be \"\n\"renamed tigase.log.1. Using this scheme, tigase.log.0 will **always** be \"\n\"your most recent log.\"\nmsgstr \"\"\n\"``tigase.log.#`` 文件是 *rotated* - 这意味着服务器在第一次运行时开始写入 \"\n\"tigase.log.0，并继续转储信息，直到达到日志大小限制。此时，Tigase 将 tigase.log.0 重命名为 \"\n\"tigase.log.1。将创建一个新的 tigase.log.0，并且 Tigase \"\n\"将开始记录到该文件。当此文件已满时，tigase.log.1 将重命名为 tigase.log.2，tigase.log.0 将重命名为 \"\n\"tigase.log.1。使用此方案，tigase.log.0 将 **始终** 是您最近的日志。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:52\nmsgid \"\"\n\"By default, Tigase has a limit of 10000000 bytes or 10MB with a file \"\n\"rotation of 10 files. You can edit these values by editing the \"\n\"``config.tdsl`` file and adding the following lines.\"\nmsgstr \"\"\n\"默认情况下，Tigase 的限制为 10000000 字节或 10MB，文件轮换为 10 个文件。您可以通过编辑 ``config.tdsl`` \"\n\"文件并添加以下行来编辑这些值。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:63\nmsgid \"\"\n\"This code, if entered into the ``config.tdsl`` file increases the size of\"\n\" the files to 15, and enlarges the maximum size to 20MB. Note the larger \"\n\"the collective log space is, the larger number of sectors on hard disk \"\n\"are active. Large log blocks may impact system performance.\"\nmsgstr \"\"\n\"这段代码，如果输入 ``config.tdsl`` 文件，将文件大小增加到 15，最大文件大小增加到 \"\n\"20MB。请注意，集体日志空间越大，硬盘上活动的扇区数就越多。大的日志块可能会影响系统性能。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:65\nmsgid \"\"\n\"*You may see a tigase.log.0.lck file in the directory while the server is\"\n\" running. This is a temporary file only and is deleted once Tigase is \"\n\"cleanly shut down.*\"\nmsgstr \"*当服务器运行时，您可能会在目录中看到一个 tigase.log.0.lck 文件。这只是一个临时文件，一旦完全关闭 Tigase 就会被删除。*\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:68\nmsgid \"logs/statistics.log.#\"\nmsgstr \"logs/statistics.log.#\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:70\nmsgid \"\"\n\"Statistics log will duplicate any information that is related to sending \"\n\"of statistics to Tigase if you are using an unlicensed copy of Tigase \"\n\"XMPP server. Mainly it will consist output of LicenceChecker. The \"\n\"numbering logic will be the same as ``tigase.log.#`` files.\"\nmsgstr \"\"\n\"如果您使用的是未经许可的 Tigase XMPP 服务器副本，统计日志将复制与向 Tigase \"\n\"发送统计信息相关的任何信息。主要包括LicenceChecker 的输出。编号逻辑将与 ``tigase.log.#`` 文件相同。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:73\nmsgid \"logs/tigase.pid\"\nmsgstr \"logs/tigase.pid\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:75\nmsgid \"\"\n\"``tigase.pid`` is a file that just contains the Process ID or PID for the\"\n\" current run of Tigase. It is only valid for the current or most recent \"\n\"run cycle and is overwritten every time Tigase starts.\"\nmsgstr \"\"\n\"``tigase.pid`` 是一个仅包含当前运行 Tigase 的进程 ID 或 PID 的文件。它仅对当前或最近的运行周期有效，并在每次 \"\n\"Tigase 启动时被覆盖。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:78\nmsgid \"logs/tigase-console.log\"\nmsgstr \"logs/tigase-console.log\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:82\nmsgid \"\"\n\"This is the most important log file containing the most essential \"\n\"information related to operation of the Tigase XMPP Server. Any errors or\"\n\" exceptions in this file indicate with high probability serious issues \"\n\"with server operation.\"\nmsgstr \"这是最重要的日志文件，其中包含与 Tigase XMPP 服务器操作相关的最基本信息。此文件中的任何错误或异常都极有可能表明服务器操作存在严重问题。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:84\nmsgid \"\"\n\"This file contains information related to Tigase’s running environment, \"\n\"and is a dump from the server itself on what is being loaded, when, and \"\n\"if any issues are encountered. It will start by loading Java classes \"\n\"(consequently making sure the Java environment is present and \"\n\"functioning). Then it will begin loading the configuration file, and \"\n\"adding default values to settings that have not been customized. You can \"\n\"then see all the components being loaded, and settings added where \"\n\"default values are needed. Lastly you will see a log of any plugins that \"\n\"are loaded, and any parameters therein. You may see tags such as INFO or \"\n\"WARNING in the logs. Although they may contain important information, the\"\n\" program will continue to operate as normal are not of too great concern.\"\nmsgstr \"\"\n\"该文件包含与 Tigase 的运行环境相关的信息，并且是服务器本身关于正在加载的内容，何时以及是否遇到任何问题的转储。它将首先加载 Java \"\n\"类（随后确保 Java \"\n\"环境存在且正常运行）。然后它将开始加载配置文件，并将默认值添加到尚未自定义的设置中。然后，您可以看到正在加载的所有组件，以及在需要默认值的地方添加的设置。最后，您将看到已加载的所有插件以及其中的任何参数的日志。您可能会在日志中看到诸如\"\n\" INFO 或 WARNING 之类的标签。尽管它们可能包含重要信息，但程序将继续正常运行其并不太值得关注。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:86\nmsgid \"\"\n\"ERROR flags are issues you will want to pay attention as they may list \"\n\"problems that prevent Tigase or components from properly functioning.\"\nmsgstr \"错误标志是您需要注意的问题，因为它们可能会列出阻止 Tigase 或组件正常运行的问题。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:90\nmsgid \"\"\n\"Windows does not create this file, rather the output is shown in the \"\n\"command line and is not dumped to a file.\"\nmsgstr \"Windows 不会创建此文件，而是在命令行中显示输出并且不会转储到文件中。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:92\nmsgid \"\"\n\"If Tigase is gracefully shut down, tigase-console.log will add statistics\"\n\" from the server’s operation life in the following format.\"\nmsgstr \"如果 Tigase 正常关闭，tigase-console.log 将按以下格式添加服务器运行寿命的统计信息。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:98\nmsgid \"\"\n\"*Any component that may have a statistic, whether used or not, will place\"\n\" a value here*\"\nmsgstr \"*任何可能具有统计信息的组件，无论是否使用，都会在此处放置一个值*\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:100\nmsgid \"This file can be handy if you are tracking issues in the server.\"\nmsgstr \"如果您要跟踪服务器中的问题，此文件会很方便。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:102\nmsgid \"tigase-console.log is appended during each run session of the server.\"\nmsgstr \"tigase-console.log 在服务器的每个运行会话期间附加。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:105\nmsgid \"Log File Location\"\nmsgstr \"日志文件位置\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:107\nmsgid \"\"\n\"You can also change the location of log files if you have a specific \"\n\"directory you wish to use. The configuration may be made by the following\"\n\" lines in your ``config.tdsl`` file:\"\nmsgstr \"如果您有想要使用的特定目录，也可以更改日志文件的位置。可以通过 ``config.tdsl`` 文件中的以下行进行配置：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Log_Guide.inc:117\nmsgid \"\"\n\"This setting changes the log file location to /var/log/tigase/ where all \"\n\"log files will be made. Files in the original location will be left.\"\nmsgstr \"此设置将日志文件位置更改为 /var/log/tigase/，所有日志文件都将在其中生成。原始位置的文件将被保留。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:4\nmsgid \"Debuging Tigase\"\nmsgstr \"调试 Tigase\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:6\nmsgid \"\"\n\"If something goes wrong and you can’t find out why it is not working as \"\n\"expected, you might want more detailed debugging options switched on.\"\nmsgstr \"如果出现问题并且您无法找出它没有按预期工作的原因，您可能需要打开更详细的调试选项。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:8\nmsgid \"\"\n\"Tigase is a Java application and it uses Java logging library, this gives\"\n\" you the flexibility to switch logging on for selected Java packages or \"\n\"even for a single Java class.\"\nmsgstr \"Tigase 是一个 Java 应用程序，它使用 Java 日志库，这使您可以灵活地为选定的 Java 包甚至单个 Java 类切换日志记录。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:10\nmsgid \"\"\n\"Logs files are stored in ``logs/`` directory. ``tigase-console.log`` \"\n\"stores basic log data, but is the main log file. ``tigase.log.N`` files \"\n\"keep all the detailed logging entries. So this is the place where you \"\n\"should look in case of problems.\"\nmsgstr \"\"\n\"日志文件存储在 ``logs/`` 目录中。 ``tigase-console.log`` 存储基本的日志数据，但是是主要的日志文件。 \"\n\"``tigase.log.N`` 文件保存所有详细的日志条目。因此，这是您在出现问题时应该查看的地方。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:25\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:13\nmsgid \"Configuration\"\nmsgstr \"配置\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:15\nmsgid \"\"\n\"By default, Tigase has the old ``debug = ['server']`` setting is turned \"\n\"on and does not need to be added.\"\nmsgstr \"默认情况下，Tigase 已打开旧的 ``debug = ['server']`` 设置，不需要再添加。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:17\nmsgid \"\"\n\"However, people want to see what is going on the network level. That is \"\n\"what has been sent and what has been received by the server - the actual \"\n\"character data. The class which would print all received and sent \"\n\"character data is: ``tigase.xmpp.XMPPIOService``. To enable all debugging\"\n\" info for this class you have to modify the debug line:\"\nmsgstr \"\"\n\"然而，人们想看看网络层面发生了什么。那就是服务器发送和接收的内容 - \"\n\"实际的字符数据。打印所有接收和发送的字符数据的类是：``tigase.xmpp.XMPPIOService``。要启用此类的所有调试信息，您必须修改调试行：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:23\nmsgid \"\"\n\"You can also have debugging switched on for many packages/classes at the \"\n\"same time:\"\nmsgstr \"您还可以同时为许多包/类打开调试：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:29\nmsgid \"Other packages you might be interested in are:\"\nmsgstr \"您可能感兴趣的其他软件包是：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:31\nmsgid \"\"\n\"``io`` can print out what is going on a very low level network level \"\n\"including TLS/SSL stuff.\"\nmsgstr \"``io`` 可以打印出在非常低级别的网络级别上发生的事情，包括 TLS/SSL 的东西。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:33\nmsgid \"``xml`` would print the XML parser debugging data.\"\nmsgstr \"``xml`` 将打印 XML 解析器调试数据。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:35\nmsgid \"``cluster`` would print all the clustering related stuff.\"\nmsgstr \"``cluster`` 将打印所有与聚类相关的东西。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:37\nmsgid \"``xmpp.impl`` would print logs from all plugins loaded to Tigase server.\"\nmsgstr \"``xmpp.impl`` 将打印加载到 Tigase 服务器的所有插件的日志。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:40\nmsgid \"Non-Tigase packages\"\nmsgstr \"非 Tigase 包\"\n\n#: ../../Tigase_Administration/Using_Tigase/Debugging_Tigase.inc:42\nmsgid \"\"\n\"To enable logging for your own packages from those different than Tigase,\"\n\" you have to use another option which has been made available for this:\"\nmsgstr \"要为您自己的包从不同于 Tigase 的包中启用日志记录，您必须使用另一个可用的选项：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Basic_System_Checks.inc:4\nmsgid \"Basic System Checks\"\nmsgstr \"基本系统检查\"\n\n#: ../../Tigase_Administration/Using_Tigase/Basic_System_Checks.inc:6\nmsgid \"\"\n\"Previously, a configuration article is available about :ref:`Linux \"\n\"settings for high load systems<linuxhighload>`. This has a description of\"\n\" basic settings which are essential to successfully run XMPP service for \"\n\"hundreds or thousands of online users.\"\nmsgstr \"\"\n\"以前，有一篇关于 :ref:`高负载系统的 Linux 设置<linuxhighload>` 的配置文章。\"\n\"其描述了为成百上千的在线用户成功运行 XMPP 服务所必需的基本设置。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Basic_System_Checks.inc:8\nmsgid \"\"\n\"Of course, high load and high traffic systems require much more tuning \"\n\"and adjustments. If you use selinux you have to be careful as it can \"\n\"interfere with the service while it is under a high load. Also some \"\n\"firewall settings may cause problems as the system may decide it is under\"\n\" a DDOS attack and can start blocking incoming connections or throttle \"\n\"the traffic.\"\nmsgstr \"\"\n\"当然，高负载和高流量系统需要更多的调整和调整。如果您使用 \"\n\"selinux，则必须小心，因为它可能会在高负载下干扰服务。此外，某些防火墙设置可能会导致问题，因为系统可能会确定它受到 DDOS \"\n\"攻击，并且可以开始阻止传入连接或限制流量。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Basic_System_Checks.inc:10\nmsgid \"\"\n\"In any case, there are some basic checks to do every time you deploy XMPP\"\n\" service to make sure it will function properly. I am trying to keep the \"\n\"article mentioned above up to date and add all the settings and \"\n\"parameters I discover while working with different installations. *If you\"\n\" have some suggestions for different values or different parameters to \"\n\"add, please let me know.*\"\nmsgstr \"\"\n\"无论如何，每次部署 XMPP \"\n\"服务时都需要进行一些基本检查，以确保它能够正常运行。我正在尝试让上面提到的文章保持最新，并添加在使用不同安装时发现的所有设置和参数。 \"\n\"*如果您对添加不同的值或不同的参数有一些建议，请告诉我。*\"\n\n#: ../../Tigase_Administration/Using_Tigase/Basic_System_Checks.inc:12\nmsgid \"\"\n\"If you want to run a service on a few cluster nodes (5 or even 10), then \"\n\"manually checking every machine and adjusting these settings is time \"\n\"consuming and it is very easy to forget about.\"\nmsgstr \"如果您想在几个集群节点（5 个甚至 10 个）上运行服务，那么手动检查每台机器并调整这些设置非常耗时，而且很容易忘记。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Basic_System_Checks.inc:14\nmsgid \"\"\n\"To overcome this problem I started to work on a shell script which would \"\n\"run all the basic checks and report problems found. Ideally it should be \"\n\"also able to adjust some parameters for you.\"\nmsgstr \"为了克服这个问题，我开始编写一个 shell 脚本，该脚本将运行所有基本检查并报告发现的问题。理想情况下，它还应该能够为您调整一些参数。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Basic_System_Checks.inc:16\nmsgid \"\"\n\"Inside the Tigase server `scripts/ <https://github.com/tigase/tigase-\"\n\"server/blob/master/scripts/>`__ repository find a script called \"\n\"``machine-check.sh``. It performs all the basic checks from the article \"\n\"and also tries to adjust them when necessary. Have a `look at the code \"\n\"<https://github.com/tigase/tigase-server/blob/master/scripts/machine-\"\n\"check.sh>`__ and run for yourself.\"\nmsgstr \"\"\n\"在 Tigase 服务器 `scripts/ <https://github.com/tigase/tigase-\"\n\"server/blob/master/scripts/>`__ 存储库中找到一个名为 ``machine-check.sh`` \"\n\"的脚本。它执行文章中的所有基本检查，并在必要时尝试调整它们。 `查看代码 <https://github.com/tigase/tigase-\"\n\"server/blob/master/scripts/machine-check.sh>`__ 并自己运行。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Basic_System_Checks.inc:18\nmsgid \"Any comments or suggestions, as usual, are very much appreciated.\"\nmsgstr \"像往常一样，任何意见或建议都非常感谢。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:4\nmsgid \"Add and Manage Domains (VHosts)\"\nmsgstr \"添加和管理域 (VHosts)\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:6\nmsgid \"\"\n\"Tigase XMPP Server offers an easy to use and very flexible way to add and\"\n\" manage domains hosted on installation (vhosts).\"\nmsgstr \"Tigase XMPP Server 提供了一种易于使用且非常灵活的方式来添加和管理安装时托管的域（vhosts）。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:8\nmsgid \"There are two ways of managing domains you host on your server:\"\nmsgstr \"有两种方法可以管理您在服务器上托管的域：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:10\nmsgid \"using web-based admin management console - :ref:`Admin UI<usingAdminUI>`\"\nmsgstr \"使用基于 Web 的管理控制台 - :ref:`Admin UI<usingAdminUI>`\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:12\nmsgid \"\"\n\"using XMPP ad-hoc commands by XMPP client, ie. `Psi <http://psi-\"\n\"im.org/>`__\"\nmsgstr \"通过 XMPP 客户端使用 XMPP ad-hoc 命令，即 `Psi <http://psi-im.org/>`__\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:16\nmsgid \"\"\n\"To use any of those ways, you need to be an administrator of the server, \"\n\"which means that you have a XMPP account created on this XMPP server and \"\n\"your account JID is added to :ref:`the list of the \"\n\"administrators<admins>` in the Tigase XMPP Server configuration file.\"\nmsgstr \"\"\n\"要使用其中任何一种方式，您需要成为服务器的管理员，这意味着您在此 XMPP 服务器上创建了一个 XMPP 帐户，并且您的帐户JID已添加到 \"\n\"Tigase XMPP服务器配置文件中的 :ref:`管理员列表<admins>` 中。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:21\nmsgid \"Using Admin UI\"\nmsgstr \"使用Admin UI\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:23\nmsgid \"\"\n\"First, you need to open Admin UI web page. By default Admin UI is enabled\"\n\" and available at the port ``8080`` at path ``/admin/`` on the XMPP \"\n\"server. Assuming that your are logged on the same machine which hosts \"\n\"Tigase XMPP Server, it will be available at http://localhost:8080/admin/.\"\nmsgstr \"\"\n\"首先，您需要打开 Admin UI 网页。默认情况下，管理 UI 已启用并且可在 XMPP 服务器上的路径 ``/admin/`` 的端口 \"\n\"``8080`` 上使用。假设您登录到托管 Tigase XMPP 服务器的同一台机器上，它将在 \"\n\"http://localhost:8080/admin/ 上可用。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:25\nmsgid \"\"\n\"When you will be prompted for username and password to login to the Admin\"\n\" UI please fill username with full JID of your XMPP admin account and \"\n\"fill password field with password for this account. When you submit \"\n\"correct credentials you will get access to the Admin UI and Tigase XMPP \"\n\"Server configuration and management web-based interface.\"\nmsgstr \"\"\n\"当系统提示您输入用户名和密码以登录管理 UI 时，请使用您的 XMPP 管理员帐户的完整 JID \"\n\"填写用户名，并使用该帐户的密码填写密码字段。提交正确的凭据后，您将可以访问 Admin UI 和 Tigase XMPP Server \"\n\"配置和管理基于 Web 的界面。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:28\nmsgid \"Adding a new domain\"\nmsgstr \"添加新域\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:30\nmsgid \"\"\n\"To add a new domain you need to open ``Configuration`` section of the \"\n\"Admin UI (by clicking on ``Configuration`` label and then selecting ``Add\"\n\" new item`` position which mentions ``vhost-man``.\"\nmsgstr \"\"\n\"要添加新域，您需要打开管理 UI 的 ``Configuration`` 部分（通过单击 ``Configuration`` 标签，然后选择提及 \"\n\"``vhost-man`` 的 ``Add new item`` 位置）。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:32\nmsgid \"|adminui vhost add item button|\"\nmsgstr \"|adminui vhost add item button|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:138\nmsgid \"adminui vhost add item button\"\nmsgstr \"adminui vhost add item button\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:34\nmsgid \"\"\n\"After doing that, you will be presented with a form which you need to \"\n\"fill in. This form allows you to pass ``Domain name`` to add and other \"\n\"options (some of the are advanced options).\"\nmsgstr \"完成此操作后，您将看到一个需要填写的表格。此表格允许您传递 ``Domain name`` 以添加和其他选项（其中一些是高级选项）。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:36\nmsgid \"|adminui vhost add item form|\"\nmsgstr \"|adminui vhost add item form|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:139\nmsgid \"adminui vhost add item form\"\nmsgstr \"adminui vhost add item form\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:40\nmsgid \"\"\n\"All options with exception of ``Domain name`` may be changed later on by \"\n\"modifying vhost settings.\"\nmsgstr \"以后可以通过修改虚拟主机设置来更改除 ``Domain name`` 之外的所有选项。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:42\nmsgid \"\"\n\"When you will be ready, please submit the form using button below the \"\n\"form. As a result you will be presented with a result of this operation. \"\n\"If it was successful it show ``Operation successful`` message and if \"\n\"something was not OK, it will display an error to help you fix this issue\"\n\" which you encountered.\"\nmsgstr \"\"\n\"当您准备好时，请使用表格下方的按钮提交表格。因此，您将看到此操作的结果。如果成功，它会显示 ``Operation successful`` \"\n\"消息，如果出现问题，它会显示错误以帮助您解决遇到的问题。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:45\nmsgid \"Modifying domain settings\"\nmsgstr \"修改域设置\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:47\nmsgid \"\"\n\"Modifying a domain settings is very similar to adding a new domain. You \"\n\"need to open ``Configuration`` section of the Admin UI and then select \"\n\"``Update item configuration`` position which mentions ``vhost-man``.\"\nmsgstr \"\"\n\"修改域设置与添加新域非常相似。您需要打开管理 UI 的 ``Configuration`` 部分，然后选择提及 ``vhost-man`` 的 \"\n\"``Update item configuration`` 位置。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:49\nmsgid \"|adminui vhost update item button|\"\nmsgstr \"|adminui vhost update item button|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:140\nmsgid \"adminui vhost update item button\"\nmsgstr \"adminui vhost update item button\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:51\nmsgid \"\"\n\"You will be presented with a list of domains hosted on this Tigase XMPP \"\n\"Server installation. From them you should choose the one for which you \"\n\"wish to modify settings.\"\nmsgstr \"您将看到托管在此 Tigase XMPP 服务器安装上的域列表。从它们中，您应该选择要修改设置的那个。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:53\nmsgid \"|adminui vhost update item domains list|\"\nmsgstr \"|adminui vhost update item domains list|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:141\nmsgid \"adminui vhost update item domains list\"\nmsgstr \"adminui vhost update item domains list\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:55\nmsgid \"\"\n\"After submitting this selection, you will be presented with a the same \"\n\"form as the one used during adding a new domain. It presents \"\n\"configuration options for this domain and currently used values.\"\nmsgstr \"提交此选择后，您将看到与添加新域时使用的相同的表单。它提供了该域的配置选项和当前使用的值。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:57\nmsgid \"|adminui vhost update item form|\"\nmsgstr \"|adminui vhost update item form|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:142\nmsgid \"adminui vhost update item form\"\nmsgstr \"adminui vhost update item form\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:59\nmsgid \"\"\n\"Now you should adjust them as you wish and submit this form using the \"\n\"button below the form.\"\nmsgstr \"现在您应该根据需要调整它们并使用表单下方的按钮提交此表单。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:61\nmsgid \"\"\n\"As a result you will be presented with a result of this operation. If it \"\n\"was successful it show ``Operation successful`` message and if something \"\n\"was not OK, it will display an error to help you fix this issue which you\"\n\" encountered.\"\nmsgstr \"\"\n\"因此，您将看到此操作的结果。如果成功，它会显示 ``Operation successful`` \"\n\"消息，如果出现问题，它会显示错误以帮助您解决遇到的问题。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:64\nmsgid \"Removing a domain\"\nmsgstr \"删除域\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:66\nmsgid \"\"\n\"Removing a hosted domain from the Tigase XMPP Server installation is \"\n\"quite simple as well. You need to open ``Configuration`` section of the \"\n\"Admin UI and then select ``Remove an item`` position which mentions \"\n\"``vhost-man``.\"\nmsgstr \"\"\n\"从 Tigase XMPP 服务器安装中删除托管域也非常简单。您需要打开管理 UI 的 ``Configuration`` 部分，然后选择提及 \"\n\"``vhost-man`` 的 ``Remove an item`` 位置。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:68\nmsgid \"|adminui vhost remove item button|\"\nmsgstr \"|adminui vhost remove item button|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:143\nmsgid \"adminui vhost remove item button\"\nmsgstr \"adminui vhost remove item button\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:70\nmsgid \"\"\n\"You will be presented with a list of domains hosted on this Tigase XMPP \"\n\"Server installation. From them you should select the one which should be \"\n\"removed.\"\nmsgstr \"您将看到托管在此 Tigase XMPP 服务器安装上的域列表。从它们中，选择应该删除的那个。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:72\nmsgid \"|adminui vhost remove item domains list|\"\nmsgstr \"|adminui vhost remove item domains list|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:144\nmsgid \"adminui vhost remove item domains list\"\nmsgstr \"adminui vhost remove item domains list\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:74\nmsgid \"\"\n\"After submitting your selection, Tigase XMPP Server will try to remove \"\n\"this domain from the list of hosted domains and will present you with the\"\n\" result. If it was successful it show ``Operation successful`` message \"\n\"and if something was not OK, it will display an error to help you fix \"\n\"this issue which you encountered.\"\nmsgstr \"\"\n\"提交您的选择后，Tigase XMPP 服务器将尝试从托管域列表中删除该域，并将结果显示给您。如果成功，它会显示 ``Operation \"\n\"successful`` 消息，如果出现问题，它会显示错误以帮助您解决遇到的问题。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:77\nmsgid \"Using ad-hoc commands\"\nmsgstr \"使用ad-hoc命令\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:79\nmsgid \"\"\n\"For everybody interested in using our service to host their own XMPP \"\n\"domain we have good news! You do not have to ask an administrator to add \"\n\"your domain or add users for your domain anymore. You can do it on your \"\n\"own.\"\nmsgstr \"对于有兴趣使用我们的服务来托管自己的 XMPP 域的每个人，我们都有好消息！您不必再要求管理员添加您的域或为您的域添加用户。你可以自己做。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:81\nmsgid \"\"\n\"Please note, this is very new stuff. Something may go wrong or may not be\"\n\" polished. Please report any problems, notices or suggestions.\"\nmsgstr \"请注意，这是非常新的东西。某些事情可能会出错或可能无法完善。请报告任何问题，通知或建议。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:83\nmsgid \"\"\n\"This is the guide to walk you through the new functions and describes how\"\n\" to add a new domain and new users within your domain.\"\nmsgstr \"这是指导您完成新功能的指南，并描述了如何在您的域中添加新域和新用户。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:85\nmsgid \"\"\n\"You can do everything from your XMPP client or you can use our web \"\n\"application that allows you to connect to the service and execute admin \"\n\"commands. I recommend `Psi <http://psi-im.org/>`__ because of its \"\n\"excellent support for parts of the XMPP protocol which are used for \"\n\"domains and user management. You may use other clients as well, but we \"\n\"can only offer support and help if you use Psi client.\"\nmsgstr \"\"\n\"您可以从 XMPP 客户端执行所有操作，也可以使用我们的 Web 应用程序连接到服务并执行管理命令。我推荐 `Psi <http://psi-\"\n\"im.org/>`__ 因为它对用于域和用户管理的 XMPP 协议部分提供了出色的支持。您也可以使用其他客户端，但我们只能在您使用 Psi \"\n\"客户端时提供支持和帮助。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:87\nmsgid \"\"\n\"Secondly, you need an account on the server. This is because all the \"\n\"commands and features described here are available to local users only. \"\n\"Therefore, if you do not have a registered domain with us yet, please go \"\n\"ahead and register an account on the website either the `Tigase.IM \"\n\"<http://www.tigase.im/>`__ or `Jabber.Today <http://jabber.today/>`__.\"\nmsgstr \"\"\n\"其次，您需要在服务器上拥有一个帐户。这是因为此处描述的所有命令和功能仅对本地用户可用。因此，如果您还没有在我们这里注册域名，请去网站 \"\n\"`Tigase.IM <http://www.tigase.im/>`__ 或 `Jabber.Today \"\n\"<http://jabber.today/>`__ 上注册一个帐户 。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:90\nmsgid \"Adding a New Domain\"\nmsgstr \"添加新域\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:92\nmsgid \"\"\n\"Once you register an account on one of the websites, connect to the XMPP \"\n\"server using the account on the Psi client. We will be using the \"\n\"following account: green@tigase.im which is this guide.\"\nmsgstr \"在其中一个网站上注册帐户后，使用 Psi 客户端上的帐户连接到 XMPP 服务器。我们将使用以下帐户：green@tigase.im，这用于本指南。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:94\nmsgid \"\"\n\"When you are ready right click on the account name in Psi roster window \"\n\"to bring up context menu. Select **Service Discovery** element.\"\nmsgstr \"准备好后，右键单击 Psi 名册窗口中的帐户名称以显示上下文菜单。选择 **服务发现** 元素。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:96\nmsgid \"|service disco menu|\"\nmsgstr \"|service disco menu|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:145\nmsgid \"service disco menu\"\nmsgstr \"service disco menu\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:98\nmsgid \"\"\n\"A new windows pops up as in the example on the right. The service \"\n\"discovery window is where all the stuff installed on XMPP service should \"\n\"show up. Most of elements on the list are well known transports, MUC and \"\n\"PubSub components. The new stuff on the list, which we are interested in,\"\n\" are 2 elements: **VHost Manager** and **Session Manager**.\"\nmsgstr \"\"\n\"如右侧示例所示，会弹出一个新窗口。服务发现窗口是 XMPP 服务上安装的所有内容应该显示的地方。列表中的大多数元素都是众所周知的传输，MUC 和 \"\n\"PubSub 组件。我们感兴趣的列表中的新内容是 2 个元素：**VHost Manager** 和 **Session Manager**。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:100\nmsgid \"|service disco window vhost|\"\nmsgstr \"|service disco window vhost|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:146\nmsgid \"service disco window vhost\"\nmsgstr \"service disco window vhost\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:102\nmsgid \"\"\n\"**VHost Manager** component in Tigase is responsible for managing and \"\n\"controlling virtual hosts on the installation. It provides virtual hosts \"\n\"information to all other parts of the system and also allows you to add \"\n\"new hosts and remove/update existing virtual hosts.\"\nmsgstr \"\"\n\"Tigase 中的 **VHost Manager** \"\n\"组件负责管理和控制安装中的虚拟主机。它为系统的所有其他部分提供虚拟主机信息，还允许您添加新主机和删除/更新现有虚拟主机。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:104\nmsgid \"\"\n\"**Session Manager** component in Tigase is responsible for managing \"\n\"users. In most cases online users but it can also perform some actions on\"\n\" user repository where all user data is stored.\"\nmsgstr \"\"\n\"Tigase 中的 **Session Manager** \"\n\"组件负责管理用户。在大多数情况下是管理在线用户，但它也可以对存储所有用户数据的用户存储库执行一些操作。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:106\nmsgid \"\"\n\"Select **VHost Manager** and double click on it. A new windows shows up \"\n\"(might be hidden behind the service discovery window). The window \"\n\"contains another menu with a few items: **Add…​, Remove…​** and \"\n\"**Update…​** . These are for adding, removing and updating VHost \"\n\"information. For now, just select the first element **Add…​.**\"\nmsgstr \"\"\n\"选择 **VHost Manager** \"\n\"并双击它。出现一个新窗口（可能隐藏在服务发现窗口后面）。该窗口包含另一个菜单，其中包含一些项目：**Add…​, Remove…​** and \"\n\"**Update…​**。这些用于添加，删除和更新 VHost 信息。现在，只需选择第一个元素 **Add...​.**\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:108\nmsgid \"|command menu add vhost|\"\nmsgstr \"|command menu add vhost|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:147\nmsgid \"command menu add vhost\"\nmsgstr \"command menu add vhost\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:110\nmsgid \"\"\n\"Click **Execute** and you get a new window where you can enter all of \"\n\"your VHost configuration details. All fields should be self explanatory. \"\n\"Leave a blank field for **Other parameters** for now. **Owner** is you, \"\n\"that is Jabber ID which controls the domain and can change the domain \"\n\"configuration settings or can remove the domain from the service. \"\n\"**Administrators** field can be left blank or can contain comma separated\"\n\" list of Jabber IDs for people who can manage users within the domain. \"\n\"You do not need to add your user name to the list as Owners can always \"\n\"manage users for the domain.\"\nmsgstr \"\"\n\"单击 **Execute**，您会看到一个新窗口，您可以在其中输入所有 VHost 配置详细信息。所有字段都应该是不言自明的。暂时为 \"\n\"**Other parameters** 留一个空白字段。 **Owner** 是您，即控制域并可以更改域配置设置或可以从服务中删除域的 \"\n\"Jabber ID。 **Administrators** 字段可以留空，也可以包含逗号分隔的 Jabber ID \"\n\"列表，供可以管理域内用户的人员使用。您无需将用户名添加到列表中，因为所有者始终可以管理域的用户。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:112\nmsgid \"|add vhost window|\"\nmsgstr \"|add vhost window|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:148\nmsgid \"add vhost window\"\nmsgstr \"add vhost window\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:114\nmsgid \"\"\n\"When you are ready click the **Finish** button. All done, hopefully. You \"\n\"can get either a window confirming everything went well or a window \"\n\"printing an error message if something went wrong. What can be wrong? \"\n\"There are some restrictions I decided to put on the service to prevent \"\n\"abuse. One of the restrictions is the maximum number of domains a user \"\n\"can register for himself which is **25** right now. Another restriction \"\n\"is that the domain which you add must have a valid DNS entry pointing to \"\n\"our service. The XMPP guide describes all the details about DNS settings.\"\n\" Please refer to these instructions if you need more details.\"\nmsgstr \"\"\n\"准备就绪后，单击 **Finish** \"\n\"按钮。希望一切都完成了。如果出现问题，您可以获得确认一切正常的窗口或打印错误消息的窗口。有什么问题？我决定对服务施加一些限制以防止滥用。限制之一是用户现在可以为自己注册的最大域数为\"\n\" **25**。另一个限制是您添加的域必须具有指向我们服务的有效DNS条目。 XMPP \"\n\"指南描述了有关DNS设置的所有详细信息。如果您需要更多详细信息，请参阅这些说明。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:117\nmsgid \"Adding a New User\"\nmsgstr \"添加新用户\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:119\nmsgid \"\"\n\"Adding a new user process is quite similar, almost identical to adding a \"\n\"new domain. This time, however we have to select **Session Manager** in \"\n\"the service discovery window.\"\nmsgstr \"添加新用户进程非常相似，几乎与添加新域相同。但是这一次，我们必须在服务发现窗口中选择 **Session Manager**。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:121\nmsgid \"|service disco window sm|\"\nmsgstr \"|service disco window sm|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:149\nmsgid \"service disco window sm\"\nmsgstr \"service disco window sm\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:123\nmsgid \"\"\n\"Double click on the **Session Manager** and a window with SM’s commands \"\n\"list shows up. Right now, there is only one command available to domain \"\n\"administrators - **Add user**. I am going to make available more commands\"\n\" in the future and I am waiting for your suggestions.\"\nmsgstr \"\"\n\"双击 **Session Manager** 会出现一个带有 SM 命令列表的窗口。目前，只有一个命令可供域管理员使用 - **Add \"\n\"user**。我将在未来提供更多命令，我正在等待您的建议。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:125\nmsgid \"|command menu add user|\"\nmsgstr \"|command menu add user|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:150\nmsgid \"command menu add user\"\nmsgstr \"command menu add user\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:127\nmsgid \"\"\n\"If you click **Execute** a window presented on the left shows up. Fill \"\n\"all fields accordingly and press **Finish**.\"\nmsgstr \"如果您单击 **Execute**，则会显示左侧的窗口。相应地填写所有字段，然后按 **Finish**。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:129\nmsgid \"|add user window|\"\nmsgstr \"|add user window|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:151\nmsgid \"add user window\"\nmsgstr \"add user window\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:131\nmsgid \"\"\n\"If everything went well you have just added a new user and you should get\"\n\" a window confirming successful operation. If something went wrong, a \"\n\"window with an error message should show up. Possible errors may be you \"\n\"tried to add a user which is already present, or you may have tried to \"\n\"add a user for a domain to which you do not have permission or to non-\"\n\"existen domain.\"\nmsgstr \"如果一切顺利，您刚刚添加了一个新用户，您应该会看到一个确认操作成功的窗口。如果出现问题，应该会显示一个带有错误消息的窗口。可能的错误也许是您尝试添加已存在的用户，或者您可能尝试为您没有权限的域或不存在的域添加用户。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:134\nmsgid \"SSL Certificate Management\"\nmsgstr \"SSL 证书管理\"\n\n#: ../../Tigase_Administration/Using_Tigase/Virtual_Domains.inc:136\nmsgid \"\"\n\"SSL Certificate Management has been implemented, and certificates can be \"\n\"manipulated when in a .pem form. For more details, see :ref:`Creating and\"\n\" Loading the Server Certificate in pem Files<certspem>` section of \"\n\"documentation for more information.\"\nmsgstr \"\"\n\"SSL 证书管理已实施，并且可以在 .pem 格式中操作证书。有关更多详细信息，\"\n\"请参阅文档的 :ref:`在pem文件中创建和加载服务器证书<certspem>` \"\n\"部分以获取更多信息。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:4\nmsgid \"Presence Forwarding\"\nmsgstr \"Presence转发\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:6\nmsgid \"\"\n\"Have you ever thought of displaying your users presence status on the \"\n\"website? Or, maybe, you wanted to integrate XMPP service with your own \"\n\"system and share not only users' accounts but also presence status?\"\nmsgstr \"你有没有想过在网站上显示你的用户存在状态？或者，也许，您想将 XMPP 服务与您自己的系统集成，并且不仅共享用户的帐户，还共享在线状态？\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:8\nmsgid \"\"\n\"Not only is it possible but also very simple. You have a new option in \"\n\"the domain control form.\"\nmsgstr \"不仅有可能而且非常简单。您在域控制表单中有一个新选项。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:10\nmsgid \"Actually there are 2 new options:\"\nmsgstr \"实际上有两个新选项：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:12\nmsgid \"Presence forward address\"\nmsgstr \"Presence转发地址\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:14\nmsgid \"Message forward address - not fully implemented yet\"\nmsgstr \"消息转发地址 - 尚未完全实现\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:16\nmsgid \"\"\n\"Presence forward address can be any XMPP address. Usually you want it to \"\n\"be a bot address which can collect your users' presence information. Once\"\n\" this option is set to a valid XMPP address Tigase forwards user’s \"\n\"presence, every time the user changes his status. The presence is \"\n\"processed normally, of course, and distributed to all people from the \"\n\"contact list (roster), plus to this special address. It can be a \"\n\"component or a bot. If this is a bot connecting to a regular XMPP \"\n\"account, **Make sure the presence forward address contains resource part \"\n\"and the bot is connecting with this resource.** Otherwise the presence \"\n\"won’t be delivered to the bot.\"\nmsgstr \"\"\n\"Presence 转发地址可以是任何 XMPP 地址。通常你希望它是一个可以收集用户存在信息的机器人地址。一旦将此选项设置为有效的 XMPP \"\n\"地址，Tigase \"\n\"就会在每次用户更改其状态时转发用户的状态。当然，状态会被正常处理，并从联系人列表（名册）中分发给所有人，以及这个特殊地址。它可以是组件或机器人。如果这是连接到常规\"\n\" XMPP 帐户的机器人，**确保 presence 转发地址包含资源部分，并且机器人正在与此资源连接。** 否则 presence \"\n\"将不会传递给机器人。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:18\nmsgid \"|vhost presence forward|\"\nmsgstr \"|vhost presence forward|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:62\nmsgid \"vhost presence forward\"\nmsgstr \"vhost presence forward\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:20\nmsgid \"\"\n\"As the screenshot shows, there are new input lines with option for \"\n\"presence forwarding address and message forwarding address. As you can \"\n\"see this option can be specified separately for each domain, so you can \"\n\"have a different forward address for each domain.\"\nmsgstr \"\"\n\"如屏幕截图所示，有新的输入行，其中包含 presence \"\n\"转发地址和消息转发地址选项。如您所见，可以为每个域单独指定此选项，因此您可以为每个域设置不同的转发地址。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:22\nmsgid \"\"\n\"If you have your own Tigase installation, the forwarding address can be \"\n\"also set globally and can be the same for all domains. However, for this \"\n\"website, we offer this feature to all our users who have own domains and \"\n\"this can be set on per-domain basis.\"\nmsgstr \"\"\n\"如果您有自己的 Tigase \"\n\"安装，也可以全局设置转发地址，并且所有域都可以相同。但是，对于本网站，我们向所有拥有自己域的用户提供此功能，并且可以在每个域的基础上进行设置。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:24\nmsgid \"\"\n\"Now, the big question. How this can be used? I am attaching below an \"\n\"example code. With just a few lines of code you can connect a command \"\n\"line bot to the server as a client which would collect all presences from\"\n\" users. Code below is a simple Groovy script which receives presence \"\n\"packet and displays them on the console. However, it should be easy \"\n\"enough to store users' presence information in a database and then load \"\n\"it from a web application.\"\nmsgstr \"\"\n\"现在，最大的问题。这个怎么用？我在下面附上一个示例代码。只需几行代码，您就可以将命令行机器人作为客户端连接到服务器，该客户端将收集用户的所有存在。下面的代码是一个简单的\"\n\" Groovy 脚本，它接收状态数据包并将它们显示在控制台上。但是，将用户的存在信息存储在数据库中然后从 Web 应用程序加载它应该很容易。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:26\nmsgid \"\"\n\"The bot/client uses our `JaXMPP2 <https://github.com/tigase/jaxmpp>`__ \"\n\"library which is included in current builds of Tigase XMPP Server.\"\nmsgstr \"\"\n\"bot/client 用户使用我们的 `JaXMPP2 <https://github.com/tigase/jaxmpp>`__ 库，该库包含在\"\n\" Tigase XMPP 服务器的当前版本中。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Presence_Forwarding.inc:28\nmsgid \"You should be able to find a few more code examples on the wiki page.\"\nmsgstr \"您应该能够在 wiki 页面上找到更多代码示例。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:4\nmsgid \"Watchdog\"\nmsgstr \"监视器\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:6\nmsgid \"\"\n\"Tigase’s Watchdog was implemented to help Tigase close connections that \"\n\"have become stale or inactive. Sometimes the connection is delayed, maybe\"\n\" dropped packets, or a service interruption. After a time, if that \"\n\"connection is re-established, both server and client (or server and \"\n\"server) will continue on as if nothing happened. However, these gaps in \"\n\"connection can last longer, and some installations will rely on the \"\n\"operating system to detect and close stale connections. Some operating \"\n\"systems or environments can take up to 2 hours or more to determine \"\n\"whether a connection is bad and wait for a response from a foreign entity\"\n\" and may not be configured. This can not only slow down performance, but \"\n\"can lead to security issues as well. To solve this problem, we have \"\n\"introduced Watchdog to monitor connections independent of operating \"\n\"system and environments to keep those broken connections from becoming a \"\n\"problem.\"\nmsgstr \"\"\n\"实施 Tigase 的监视器是为了帮助 Tigase \"\n\"关闭已经过时或不活动的连接。有时连接会延迟，可能会丢失数据包或服务中断。一段时间后，如果重新建立该连接，服务器和客户端（或服务器和服务器）将继续运行，就好像什么都没发生一样。但是，这些连接间隙可能会持续更长时间，并且某些安装将依赖操作系统来检测和关闭过时的连接。某些操作系统或环境可能需要长达\"\n\" 2 \"\n\"小时或更长时间才能确定连接是否不良并等待来自外部实体的响应，并且可能未被配置。这不仅会降低性能，还会导致安全问题。为了解决这个问题，我们引入了监视器来监控独立于操作系统和环境的连接，以防止那些断开的连接成为问题。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:9\nmsgid \"Setup\"\nmsgstr \"设置\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:11\nmsgid \"\"\n\"No extra setup is necessary, Watchdog is already included with your build\"\n\" of Tigase (as long as it’s 7.1.0 or newer). Follow the steps in the \"\n\"configuration section.\"\nmsgstr \"无需额外设置，您的 Tigase 版本中已经包含 监视器（只要它是 7.1.0 或更高版本）。按照配置部分中的步骤操作。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:14\nmsgid \"Watchdog Configuration\"\nmsgstr \"监视器配置\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:16\nmsgid \"\"\n\"To configure watchdog, the following lines need to be present or edited \"\n\"in ``config.tdsl`` file:\"\nmsgstr \"要配置监视器，需要在 ``config.tdsl`` 文件中出现或编辑以下行：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:24\nmsgid \"The three settings are as follows:\"\nmsgstr \"三个设置如下：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:26\nmsgid \"\"\n\"``'watchdog-timeout'= 70000`` This setting sets the amount of time that \"\n\"watchdog will consider before it determines a connection may be stale. \"\n\"This setting sets the timeout at 70000ms or 70 seconds.\"\nmsgstr \"\"\n\"``'watchdog-timeout'= 70000`` 此设置设置监视器在确定连接可能过时之前将考虑的时间量。此设置将超时设置为 70000 \"\n\"毫秒或 70 秒。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:28\nmsgid \"\"\n\"``'watchdog-delay' = 60000`` This setting sets how often the watchdog \"\n\"should conduct the check, the default delay at 60000ms or 60 seconds.\"\nmsgstr \"``'watchdog-delay' = 60000`` 此设置设置监视器执行检查的频率，默认延迟为 60000 毫秒或 60 秒。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:30\nmsgid \"\"\n\"``'watchdog-ping-type'`` This setting determines the type of ping sent to\"\n\" components when watchdog is testing for activity.\"\nmsgstr \"``'watchdog-ping-type'`` 此设置决定当监视器测试活动时发送到组件的 ping 类型。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:32\nmsgid \"\"\n\"You may, if you choose, to specify individual watchdog settings for \"\n\"specific components by adding them to the component settings, for example\"\n\" if we wanted to change the Client2Server settings to include watchdog, \"\n\"use the following lines in config.tdsl:\"\nmsgstr \"\"\n\"如果您选择，您可以通过将特定组件添加到组件设置来为特定组件指定单独的监视器设置，例如，如果我们想要更改 Client2Server \"\n\"设置以包含监视器，请在 config.tdsl 中使用以下行：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:41\nmsgid \"\"\n\"If any settings are not set, the global or settings will be used. \"\n\"``watchdog-delay`` default is set to 10 min ``watchdog-ping-type`` \"\n\"default is set to XMPP\"\nmsgstr \"\"\n\"如果未设置任何设置，则将使用全局或设置。 ``watchdog-delay`` 默认设置为 10 分钟 ``watchdog-ping-\"\n\"type`` 默认设置为 XMPP\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:44\nmsgid \"Logic\"\nmsgstr \"逻辑\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:46\nmsgid \"\"\n\"Watchdog compares it’s own pings, and records the time it takes for a \"\n\"round trip to different components, clustered connections, and if one \"\n\"variable is larger than the other, watchdog will commence closing that \"\n\"stale connection. Here is a breakdown:\"\nmsgstr \"监视器比较它自己的 ping，并记录往返不同组件，集群连接所需的时间，如果一个变量大于另一个变量，监视器将开始关闭该陈旧连接。这是一个细分：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:48\nmsgid \"\"\n\"A check is performed of a connection(s) on every ``watchdog-delay`` \"\n\"interval.\"\nmsgstr \"在每个 ``watchdog-delay`` 间隔上执行一次连接检查。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:50\nmsgid \"During this check two things occur\"\nmsgstr \"在此检查期间，会发生两件事\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:52\nmsgid \"\"\n\"If the last transfer time exceeds ``max-inactivity-time`` a stop service \"\n\"command is given to terminate and broadcast unavailable presence.\"\nmsgstr \"如果最后一次传输时间超过了 ``max-inactivity-time``，则会给出停止服务命令来终止并广播不可用的存在。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:54\nmsgid \"\"\n\"If the last transfer time is lower than ``max-inactivity-time`` but \"\n\"exceeds ``watchdog-timeout`` watchdog will try to send a ping (of \"\n\"``watchdog-ping-type``). This ping may be one of two varieties (set in \"\n\"config.tdsl)\"\nmsgstr \"\"\n\"如果最后一次传输时间低于 ``max-inactivity-time`` 但超过 ``watchdog-\"\n\"timeout``，监视器将尝试发送ping (of ``watchdog-ping-type``)。此 ping 可能是两种类型之一（在 \"\n\"config.tdsl 中设置）\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:56\nmsgid \"\"\n\"``WHITESPACE`` ping which will yield the time of the last data transfer \"\n\"in any direction.\"\nmsgstr \"``WHITESPACE`` ping 将产生在任何方向上的最后一次数据传输的时间。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:58\nmsgid \"``XMPP`` ping which will yield the time of the last received xmpp stanza.\"\nmsgstr \"``XMPP`` ping 将产生最后收到的 xmpp 节的时间。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:60\nmsgid \"\"\n\"If the 2nd option is true, the connection will remain open, and another \"\n\"check will begin after the ``watchdog-delay`` time has expired.\"\nmsgstr \"如果第二个选项为真，则连接将保持打开状态，并且在 ``watchdog-delay`` 时间到期后将开始另一次检查。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:62\nmsgid \"For example, lets draw this out and get a visual representation\"\nmsgstr \"例如，让我们把它画出来并得到一个视觉表示\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:72\nmsgid \"\"\n\"This line represents how often the check is performed. Each ``-`` (dash) \"\n\"is 10 seconds, so the check is done every 60 seconds (``'watchdog-delay' \"\n\"= 60000``)\"\nmsgstr \"\"\n\"此行表示执行检查的频率。每个 ``-`` （破折号）为 10 秒，因此每 60 秒检查一次（ ``'watchdog-delay' = \"\n\"60000`` ）\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:74\nmsgid \"\"\n\"This line is client activity, here the client sent a message at 40 \"\n\"seconds (marked by ``+``) and has gone idle.\"\nmsgstr \"这一行是客户端活动，这里客户端在 40 秒时发送了一条消息（由 ``+`` 标记）并且已经空闲。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:76\nmsgid \"\"\n\"The following line represents the watchdog logic, with timeout at 120 \"\n\"seconds and max inactivity timeout at 180 seconds:\"\nmsgstr \"以下行表示监视器逻辑，超时时间为 120 秒，最大不活动超时时间为 180 秒：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:86\nmsgid \"How the check is performed:\"\nmsgstr \"如何执行检查：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:88\nmsgid \"\"\n\"30 seconds - at this point *last transfer* or *last received* time is \"\n\"updated.\"\nmsgstr \"30 秒 - 此时 *上次传输* 或 *上次接收* 时间已更新。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:90\nmsgid \"\"\n\"60 seconds - watchdog runs - it check the connection and says: \\\\_ok, \"\n\"last client transfer was 20s ago - but it’s lower than both inactivity \"\n\"(so don’t disconnect) and timeout (so don’t send ping).\"\nmsgstr \"\"\n\"60 秒 - 监视器运行 - 它检查连接并说：\\\\_ok，上次客户端传输是 20 秒前 - 但其低于不活动（所以不要断开连接）和超时（所以不要发送\"\n\" ping）值。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:92\nmsgid \"\"\n\"120 seconds - 2nd check - last transfer was 100s ago - still lower than \"\n\"both values - do nothing.\"\nmsgstr \"120 秒 - 第二次检查 - 上次传输是 100 秒前 - 仍然低于两个值 - 什么都不做。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:94\nmsgid \"\"\n\"180 seconds - 3rd check - last transfer was 160s ago - lower than \"\n\"inactivity but greater than delay - ping it sent.\"\nmsgstr \"180 秒 - 第三次检查 - 上次传输是 160 秒前 - 低于不活动但大于延迟 - ping 它已发送。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:96\nmsgid \"\"\n\"240 seconds - 4th check - last transfer was 220s ago - client still \"\n\"hasn’t responded, watchdog compares idle time to ``max-inactivity-\"\n\"timeout`` and finds out that it is greater, connection is terminated.\"\nmsgstr \"\"\n\"240 秒 - 第 4 次检查 - 上次传输是 220 秒前 - 客户端仍然没有响应，监视器将空闲时间与 ``max-inactivity-\"\n\"timeout`` 进行比较，发现它更大，连接终止。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:98\nmsgid \"\"\n\"300 seconds - watchdog is run again but given the connection was \"\n\"terminatet there is no XMPP session to check for that particular client.\"\nmsgstr \"300 秒 - 再次运行监视器，但由于连接已终止，因此没有 XMPP 会话来检查该特定客户端。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:102\nmsgid \"\"\n\"It is possible that the connection is broken, and could be detected \"\n\"during the sending of a ping and the connection would be severed at step \"\n\"4 instead of waiting for step 5. **NOTE** This MAY cause JVM to throw an \"\n\"exception.\"\nmsgstr \"\"\n\"连接可能已断开，这可以在发送 ping 期间检测到，并且连接将在第 4 步被切断，而不是等待第 5 步。**注意** 这可能会导致 JVM \"\n\"抛出异常。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:106\nmsgid \"\"\n\"Global settings may not be ideal for every setup. Since each component \"\n\"has its own settings for ``max-inactivity-time`` you may find it \"\n\"necessary to design custom watchdog settings, or edit the inactivity \"\n\"times to better suit your needs. Below is a short list of components with\"\n\" thier default settings:\"\nmsgstr \"\"\n\"全局设置可能并不适合每种设置。由于每个组件都有自己的 ``max-inactivity-time`` \"\n\"设置，您可能会发现有必要设计自定义监视器设置，或编辑不活动时间以更好地满足您的需求。以下是具有默认设置的组件的简短列表：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:128\nmsgid \"\"\n\"Again remember, for Watchdog to properly work, the ``max-inactivity-\"\n\"time`` MUST be longer than the ``watchdog-timeout`` setting\"\nmsgstr \"再次记住，为了让监视器正常工作，``max-inactivity-time`` 必须比 ``watchdog-timeout`` 设置长\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:131\nmsgid \"Testing\"\nmsgstr \"测试\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:133\nmsgid \"\"\n\"The tigase.log.0 file can reveal some information about watchdog and how \"\n\"it is working (or how it might be fighting your settings). To do so, \"\n\"enter the following line into your ``config.tdsl`` file:\"\nmsgstr \"\"\n\"tigase.log.0 文件可以透露一些关于监视器的信息以及它是如何工作的（或者它可能如何与您的设置发生冲突）。为此，请在 \"\n\"``config.tdsl`` 文件中输入以下行：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:139\nmsgid \"\"\n\"This will set debug mode for your log, and enable some more information \"\n\"about what Tigase is doing. These logs are truncated for simplicity. Lets\"\n\" look at the above scenario in terms of the logs:\"\nmsgstr \"这将为您的日志设置调试模式，并启用有关 Tigase 正在做什么的更多信息。为简单起见，这些日志被截断。让我们根据日志看一下上述场景：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:141\nmsgid \"**Stage Two.**\"\nmsgstr \"**第二阶段。**\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:147\nmsgid \"**Stage Three.**\"\nmsgstr \"**第三阶段。**\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:153\nmsgid \"**Stage Four.**\"\nmsgstr \"**第四阶段。**\"\n\n#: ../../Tigase_Administration/Using_Tigase/Watchdog.inc:160\nmsgid \"**Stage Five.**\"\nmsgstr \"**第五阶段。**\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:4\nmsgid \"Tips and Tricks\"\nmsgstr \"技巧和窍门\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:6\nmsgid \"\"\n\"The section contains some short tricks and tips helping in different \"\n\"kinds of issues related to the server administration and maintenance.\"\nmsgstr \"该部分包含一些简短的技巧和技巧，可帮助解决与服务器管理和维护相关的不同类型的问题。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:8\nmsgid \":ref:`Runtime Environment Tip<tigaseTip_RuntimeEnvironment>`\"\nmsgstr \":ref:`运行环境提示<tigaseTip_RuntimeEnvironment>`\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:15\nmsgid \"Tigase Tip: Checking the Runtime Environment\"\nmsgstr \"Tigase 提示：检查运行时环境\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:17\nmsgid \"\"\n\"It has happened recently that we have tried very hard to fix a few \"\n\"annoying problems on one of the Tigase installations. Whatever we did, \"\n\"the problem still existed after uploading a new version and restarting \"\n\"the server. It worked fine in our development environment and it just \"\n\"didn’t on the target system.\"\nmsgstr \"\"\n\"最近发生的事情是，我们非常努力地解决了其中一个 Tigase \"\n\"安装上的一些烦人的问题。但是不管我们怎么做，上传新版本并重新启动服务器后问题仍然存在。它在我们的开发环境中运行良好，但在目标系统上却没有。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:19\nmsgid \"\"\n\"It turned out that due to specific environment settings on the target \"\n\"system, an old version of Tigase server was always started regardless of \"\n\"what updates uploaded. We finally located the problem by noticing that \"\n\"the logs were not being generated in the proper locations. This led us to\"\n\" finding the issue: improper environment settings.\"\nmsgstr \"\"\n\"事实证明，由于目标系统上的特定环境设置，无论上传什么更新，总是启动旧版本的 Tigase \"\n\"服务器。我们最终通过注意到日志没有在正确的位置生成定位了问题。这导致我们发现了问题：环境设置不当。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:21\nmsgid \"\"\n\"The best way to check all the environment settings used to start the \"\n\"Tigase server is to use… ``check`` command line parameter:\"\nmsgstr \"检查用于启动 Tigase 服务器的所有环境设置的最佳方法是使用... ``check`` 命令行参数：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:53\nmsgid \"\"\n\"In our case ``TIGASE_HOME`` was set to a fixed location pointing to an \"\n\"old version of the server files. The quick ``check`` command may be a \"\n\"real time saver.\"\nmsgstr \"\"\n\"在我们的例子中，``TIGASE_HOME`` 被设置为一个指向旧版本服务器文件的固定位置。快速的 ``check`` \"\n\"命令可能是一个实时的节省程序。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:58\nmsgid \"Best Practices for Connecting to Tigase XMPP server From Web Browser\"\nmsgstr \"从 Web 浏览器连接到 Tigase XMPP 服务器的最佳实践\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:60\nmsgid \"\"\n\"Currently we have 2 ways to connect to Tigase XMPP Server from web \"\n\"browsers:\"\nmsgstr \"目前我们有两种方法可以从 Web 浏览器连接到 Tigase XMPP 服务器：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:62\nmsgid \"BOSH (Bidirectional-streams Over Synchronous HTTP)\"\nmsgstr \"BOSH（同步 HTTP 上的双向流）\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:64\nmsgid \"WebSocket (XMPP over WebSocket)\"\nmsgstr \"WebSocket（基于 WebSocket 的 XMPP）\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:66\nmsgid \"\"\n\"You will find more information about these ways for connecting to Tigase \"\n\"XMPP Server with some useful tips below.\"\nmsgstr \"您将找到有关这些连接到 Tigase XMPP 服务器的方法的更多信息，其中包含一些有用的提示。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:69\nmsgid \"BOSH\"\nmsgstr \"BOSH\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:71\nmsgid \"\"\n\"BOSH protocol specified in `XEP-0124 \"\n\"<http://xmpp.org/extensions/xep-0124.html>`__ is one of first protocols \"\n\"defined to allow to establish XMPP connection to XMPP servers from web \"\n\"browsers due to this protocol being widely supported and used. It is also\"\n\" easy to use in single server mode. It’s enabled by default in Tigase \"\n\"XMPP Server and available at port 5280.\"\nmsgstr \"\"\n\"`XEP-0124 <http://xmpp.org/extensions/xep-0124.html>`__ 中指定的 BOSH \"\n\"协议是第一个被定义允许从 Web 浏览器与 XMPP 服务器建立 XMPP \"\n\"连接的协议，因为该协议广泛被支持和使用。在单服务器模式下也很容易使用。它在 Tigase XMPP 服务器中默认启用，可在端口 5280 使用。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:73\nmsgid \"\"\n\"In clustered mode we can deploy it with load balancer deployed with \"\n\"guarantees that each BOSH connection from web browser will be forwarded \"\n\"to same Tigase XMPP Server instance. So in clustered mode if we have two \"\n\"XMPP server ``t1`` and ``t2`` which are hosting domain ``example.com`` we\"\n\" would need to have load balancer which will respond for HTTP request to \"\n\"domain ``example.com`` and forward all requests from same IP address to \"\n\"same node of a cluster (i.e. all request from ``192.168.122.32`` should \"\n\"be forwarded always to node ``t1``.\"\nmsgstr \"\"\n\"在集群模式下，我们可以使用负载均衡器部署它，保证来自 Web 浏览器的每个 BOSH 连接都将转发到同一个 Tigase XMPP \"\n\"服务器实例。所以在集群模式下，如果我们有两个 XMPP 服务器 ``t1`` 和 ``t2``，其托管域是 \"\n\"``example.com``，我们需要有一个负载均衡器，它将响应对域 ``example.com`` 的 HTTP 请求，并将来自同一 IP \"\n\"地址的所有请求转发到集群的同一节点（即来自 ``192.168.122.32`` 的所有请求应始终转发到节点 ``t1``） 。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:76\nmsgid \"Tip #1 - BOSH in Cluster Mode Without Load Balancer\"\nmsgstr \"提示 #1 - 没有负载均衡器的集群模式下的 BOSH\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:78\nmsgid \"\"\n\"There is also a way to use BOSH without load balancer enabled. In this \"\n\"case the XMPP client needs to have more logic and knowledge about all \"\n\"available cluster nodes (with names of nodes which will identify \"\n\"particular cluster nodes from internet). Using this knowledge XMPP client\"\n\" should select one random node from list of available nodes and always \"\n\"establish BOSH connections to this particular node. In case if BOSH \"\n\"connection fails due to network connection issues, the XMPP client should\"\n\" randomly pick other node from list of rest of available nodes.\"\nmsgstr \"\"\n\"还有一种方法可以在不启用负载平衡器的情况下使用 BOSH。在这种情况下，XMPP \"\n\"客户端需要有更多关于所有可用集群节点的逻辑和知识（节点名称将识别来自 Internet 的特定集群节点）。利用这些知识，XMPP \"\n\"客户端应该从可用节点列表中随机选择一个节点，并始终与该特定节点建立 BOSH 连接。如果由于网络连接问题导致 BOSH 连接失败，XMPP \"\n\"客户端应从剩余可用节点列表中随机选择其他节点。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:80\nmsgid \"*Solution:*\"\nmsgstr \"*解决方案：*\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:82\nmsgid \"\"\n\"Tigase XMPP Server by default provides server side solution for this \"\n\"issue by sending additional ``host`` attribute in ``body`` element of \"\n\"BOSH response. As value of this attribute Tigase XMPP Server sends domain\"\n\" name of server cluster node to which client connected and to which next \"\n\"connections of this session should be opened. It is possible to disable \"\n\"this custom feature by addition of of following line to \"\n\"``etc/config.tdsl`` config file:\"\nmsgstr \"\"\n\"Tigase XMPP 服务器默认通过在 BOSH 响应的 ``body`` 元素中发送额外的 ``host`` \"\n\"属性来为此问题提供服务器端解决方案。作为该属性的值 Tigase XMPP Server \"\n\"将服务器集群节点的域名发送到客户端连接到的以及应该打开该会话的下一个连接。可以通过在 ``etc/config.tdsl`` \"\n\"配置文件中添加以下行来禁用此自定义功能：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:90\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:130\nmsgid \"*Example:*\"\nmsgstr \"*例子：*\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:92\nmsgid \"\"\n\"We have servers ``t1.example.com`` and ``t2.example.com`` which are nodes\"\n\" of a cluster hosting domain ``example.com``. Web client retrieves list \"\n\"of cluster nodes from web server and then when it needs to connect to the\"\n\" XMPP server it picks random host from list of retrieved cluster nodes \"\n\"(i.e. ``t2.example.com``) and tries to connect using BOSH protocol to \"\n\"host ``t2.example.com`` but it should send ``example.com`` as name of the\"\n\" server it tries to connect to (``example.com`` should be value of ``to``\"\n\" attribute of XMPP stream).\"\nmsgstr \"\"\n\"我们有服务器 ``t1.example.com`` and ``t2.example.com``，它们是集群托管域 ``example.com``\"\n\" 的节点。 Web 客户端从 Web 服务器检索集群节点列表，然后当它需要连接到 XMPP 服务器时，它从检索到的集群节点列表（即 \"\n\"``t2.example.com``）中随机选择主机并尝试使用 BOSH 协议进行连接托管 ``t2.example.com``，但它应该发送 \"\n\"``example.com`` 作为它尝试连接的服务器的名称（``example.com`` 应该是 XMPP 流的 ``to`` 属性的值）。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:95\nmsgid \"WebSocket\"\nmsgstr \"WebSocket\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:97\nmsgid \"\"\n\"WebSocket protocol is newly standardized protocol which is supported by \"\n\"many of current versions of browsers. Currently there is a draft of \"\n\"protocol `draft-ietf-xmpp-websocket-00 <https://datatracker.ietf.org/doc\"\n\"/draft-ietf-xmpp-websocket/>`__ which describes usage of WebSocket to \"\n\"connect to XMPP servers. Tigase XMPP Server implementation of WebSocket \"\n\"protocol to connect to XMPP server is very close to this draft of this \"\n\"specification. By default Tigase XMPP Server has XMPP-over-WebSocket \"\n\"protocol enabled without encryption on port 5290. To use this protocol \"\n\"you need to use library which supports XMPP-over-WebSocket protocol.\"\nmsgstr \"\"\n\"WebSocket 协议是一种新的标准化协议，当前许多版本的浏览器都支持它。目前有一个协议草案 `draft-ietf-xmpp-\"\n\"websocket-00 <https://datatracker.ietf.org/doc/draft-ietf-xmpp-\"\n\"websocket/>`__ 其描述了使用 WebSocket 连接到 XMPP服务器。 WebSocket 协议连接到 XMPP 服务器的 \"\n\"Tigase XMPP Server 实现非常接近本规范草案。默认情况下，Tigase XMPP 服务器启用了 XMPP-over-\"\n\"WebSocket 协议，但未在端口 5290 上加密。要使用此协议，您需要使用支持 XMPP-over-WebSocket 协议的库。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:100\nmsgid \"Tip #1 - Encrypted WebSocket Connection\"\nmsgstr \"提示 #1 - 加密的 WebSocket 连接\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:102\nmsgid \"\"\n\"It is possible to enable encrypted WebSocket connection in Tigase XMPP \"\n\"Server. To do this you need to add following lines to ``etc/config.tdsl``\"\n\" config file:\"\nmsgstr \"\"\n\"可以在 Tigase XMPP 服务器中启用加密的 WebSocket 连接。为此，您需要将以下行添加到 ``etc/config.tdsl`` \"\n\"配置文件：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:120\nmsgid \"\"\n\"In this example we enabled WebSocket endpoint on port 5290 allowing \"\n\"unencrypted connections, and encrypted WebSocket endpoint on port 5291. \"\n\"As this is TLS/SSL connection (no STARTTLS) it uses default certificate \"\n\"installed in Tigase XMPP Server instance. This certificate is located in \"\n\"``certs/default.pem``.\"\nmsgstr \"\"\n\"在此示例中，我们在端口 5290 上启用了 WebSocket 端点，其允许未加密的连接，并在端口 5291 上启用了加密的 WebSocket \"\n\"端点。由于这是 TLS/SSL 连接（无 STARTTLS），它使用安装在 Tigase XMPP 服务器实例中的默认证书。该证书位于 \"\n\"``certs/default.pem`` 中。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:124\nmsgid \"\"\n\"There is no default configuration for non-default ports. All ports \"\n\"outside 443 MUST be configured.\"\nmsgstr \"非默认端口没有默认配置。必须配置 443 以外的所有端口。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:126\nmsgid \"Tip #2 - Encrypted WebSocket Connection - Dealing With Multiple VHosts\"\nmsgstr \"提示 #2 - 加密的 WebSocket 连接 - 处理多个虚拟主机\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:128\nmsgid \"\"\n\"As mentioned in Tip #1 WebSocket endpoint is plain TLS/SSL port, so it \"\n\"always serves default certificate for Tigase XMPP Server instance. That \"\n\"is ok if we are hosting single domain and if default certificate matches \"\n\"matches our domain. But If we host multiple domain we cannot use \"\n\"``wss://example1.com:5291/`` connection URL, if our default certificate \"\n\"is for domain ``example2.com``. In this situation it is recommended to \"\n\"use the default certificate for the domain under which the server is \"\n\"accessible from the internet. This domain should identify this server, so\"\n\" this domain would not point to two nodes of a cluster. After we deploy \"\n\"separate certificate for each of cluster nodes, we should follow same tip\"\n\" as Tip #1 for BOSH. Our web-based XMPP client should have knowledge \"\n\"about each node of a cluster and when it needs to connect it should \"\n\"randomly select one node from list of available cluster nodes and try to \"\n\"connect using connection URL that would contain name of server under \"\n\"which it can be identified from internet.\"\nmsgstr \"\"\n\"如提示 #1 中所述，WebSocket 端点是普通的 TLS/SSL 端口，因此它始终为 Tigase XMPP \"\n\"服务器实例提供默认证书。如果我们托管单个域并且默认证书匹配我们的域，那也没关系。但是如果我们托管多个域，如果我们的默认证书是域 \"\n\"``example2.com`` ，我们不能使用 ``wss://example1.com:5291/`` 连接 \"\n\"URL。在这种情况下，建议对可以从 Internet \"\n\"访问服务器的域使用默认证书。此域应标识此服务器，这样此域不会指向群集的两个节点。在我们为每个集群节点部署单独的证书之后，我们应该遵循与 BOSH \"\n\"的提示 #1 相同的提示。我们基于 Web 的 XMPP \"\n\"客户端应该了解集群的每个节点，当它需要连接时，它应该从可用集群节点列表中随机选择一个节点，并尝试使用包含服务器名称的连接 URL \"\n\"进行连接从互联网上识别。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Tips_and_Tricks.inc:132\nmsgid \"\"\n\"We have servers ``t1.example1.com`` and ``t2.example1.com`` which are \"\n\"nodes of a cluster in hosting domain ``example2.com``. Each of our nodes \"\n\"contains default SSL certificate with domain names matching the cluster \"\n\"node. Web client retrieves list of cluster nodes from web server and then\"\n\" when it needs to connect to XMPP server it picks random host from list \"\n\"of retrieved cluster nodes (i.e. ``t2.example1.com``) and tries to \"\n\"connect using WebSocket encrypted protocol to host ``t2.example1.com`` \"\n\"using the following URL: ``wss://t2.example1.com:5291/``. Upon connection\"\n\" the client should still send example2.com as name of server to which it \"\n\"tries to connect (``example2.com`` should be value of to attribute of \"\n\"XMPP stream). This will allow browser to validate certificate as it will \"\n\"be for the same domain to which browser connects, and it will allow XMPP \"\n\"client to connect to domain ``example2.com``, which is one of hosted \"\n\"vhosts.\"\nmsgstr \"\"\n\"我们有服务器 ``t1.example1.com`` 和 ``t2.example1.com``，它们是托管域 ``example2.com`` \"\n\"中集群的节点。我们的每个节点都包含默认 SSL 证书，其域名与集群节点匹配。 Web 客户端从 Web 服务器检索集群节点列表，然后当它需要连接到\"\n\" XMPP 服务器时，它从检索到的集群节点列表（即 ``t2.example1.com`` ）中随机选择主机并尝试使用 WebSocket \"\n\"加密协议进行连接使用以下 URL : ``wss://t2.example1.com:5291/`` 托管 ``t2.example1.com``\"\n\" 。连接后，客户端仍应发送 example2.com 作为它尝试连接的服务器的名称（``example2.com`` 应该是 XMPP \"\n\"流的属性值）。这将允许浏览器验证证书，因为它将与浏览器连接到同一个域，并且它将允许 XMPP 客户端连接到域 \"\n\"``example2.com``，这是托管的虚拟主机之一。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:2\nmsgid \"Licensing\"\nmsgstr \"许可\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:4\nmsgid \"\"\n\"With the release of v7.1.0, users and commercial clients alike may now be\"\n\" able to register and request a license file from our servers on their \"\n\"own. This process makes it easier for everyone to obtain valid license \"\n\"file when needed. Users who do not wish to register will not be required \"\n\"to register. However, If you are using Tigase ACS or other commercial \"\n\"pieces of software, you will be required to register.\"\nmsgstr \"\"\n\"随着 v7.1.0 \"\n\"的发布，用户和商业客户现在可以自己注册并从我们的服务器请求许可证文件。此过程使每个人都可以在需要时更轻松地获得有效的许可证文件。不想注册的用户无需注册。但是，如果您使用\"\n\" Tigase ACS 或其他商业软件，则需要注册。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:8\nmsgid \"\"\n\"Tigase XMPP Server will shut down during license check if no \"\n\"installation-id or license is received within a given period of time.\"\nmsgstr \"如果在给定的时间段内没有收到安装 ID 或许可证，Tigase XMPP 服务器将在许可证检查期间关闭。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:10\nmsgid \"\"\n\"**Again, Tigase XMPP Server will still be available free under AGPLv3, \"\n\"and free users will not need to register.**\"\nmsgstr \"**再次重申，Tigase XMPP Server 在 AGPLv3 下仍可免费使用，免费用户无需注册。**\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:14\nmsgid \"COMMERCIAL COMPONENTS REQUIRE THE USE OF A LICENSE.\"\nmsgstr \"商业组件需要使用许可证。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:17\nmsgid \"Registering for a License\"\nmsgstr \"注册许可证\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:19\nmsgid \"\"\n\"There are currently two ways for registering for a license with Tigase \"\n\"commercial products. **The easiest and recommended method is using the \"\n\"built in automatic registration function**. However, you may also \"\n\"register via a web portal if your installation has limitations on network\"\n\" connectivity.\"\nmsgstr \"\"\n\"目前有两种方法可以注册 Tigase 商业产品的许可证。 \"\n\"**最简单和推荐的方法是使用内置的自动注册功能**。但是，如果您的安装对网络连接有限制，您也可以通过门户网站注册。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:24\nmsgid \"Automatic Registration (recommended)\"\nmsgstr \"自动注册（推荐）\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:26\nmsgid \"\"\n\"Once a commercial component is activated on Tigase XMPP Server, the \"\n\"program will then retrieve an *Installation ID* from our servers, and \"\n\"make a file called ``installation-id`` in your ``etc/`` directory \"\n\"including the *Installation ID* for your instance. An installation ID is \"\n\"generated using the complete cluster map and all machines within the same\"\n\" cluster should have the same *Installation ID*. This *Installation ID* \"\n\"will then be sent along with server details to a license server, and \"\n\"appropriate license files will be made in your *tigasedir/etc* directory.\"\n\" When the license is due to be expired, this mechanism will update your \"\n\"license file automatically.\"\nmsgstr \"\"\n\"一旦在 Tigase XMPP 服务器上激活了商业组件，程序就会从我们的服务器中检索 *Installation ID*，并在您的 \"\n\"``etc/`` 目录中创建一个名为 ``installation-id`` 的文件，其中包含您实例的 *安装 ID* 的目录。安装 ID \"\n\"是使用完整的集群映射生成的，并且同一集群中的所有机器都应具有相同的 *安装 ID*。然后，此 *安装 ID* \"\n\"将与服务器详细信息一起发送到许可证服务器，并且将在您的 *tigasedir/etc* \"\n\"目录中制作适当的许可证文件。当许可证到期时，此机制将自动更新您的许可证文件。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:29\nmsgid \"Manual\"\nmsgstr \"手动\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:33\nmsgid \"\"\n\"This method should be used only in extreme cases when :ref:`Automatic \"\n\"Registration (recommended)<AutomaticLicenceRegistration>` can’t be used.\"\nmsgstr \"此方法应仅在无法使用 :ref:`自动注册(推荐)<AutomaticLicenceRegistration>` 的极端情况下使用。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:35\nmsgid \"\"\n\"If you do not wish to use the automatic method, you may decide to \"\n\"generate a license file using our web portal. Offline installation may \"\n\"obtain *Installation IDs* from our web portal in a three-step process: \"\n\"registration, generating hash, and obtaining license file.\"\nmsgstr \"\"\n\"如果您不想使用自动方法，您可能决定使用我们的门户网站生成许可文件。离线安装可以通过三个步骤从我们的门户网站获取 \"\n\"*安装ID*：注册、生成哈希和获取许可证文件。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:37\nmsgid \"Generating Installation ID\"\nmsgstr \"生成安装 ID\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:39\nmsgid \"\"\n\"For offline installations, you may obtain an *Installation ID* from this \"\n\"address: https://license.tigase.software/register.\"\nmsgstr \"对于离线安装，您可以从以下地址获取 *安装 ID*：https://license.tigase.software/register。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:41\nmsgid \"Data Fields:\"\nmsgstr \"数据字段：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:43\nmsgid \"\"\n\"``Customer name``: Company or user name used to identify machines. \"\n\"Multiple clusters or servers can have the same customer name.\"\nmsgstr \"``Customer name``: 用于识别机器的公司或用户名。多个集群或服务器可以具有相同的客户名称。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:45\nmsgid \"\"\n\"``VHosts``: Comma separated list of VHosts you will be using on this \"\n\"node. NOTE: these fields are case sensitive!\"\nmsgstr \"``VHosts``: 您将在此节点上使用的VHosts的逗号分隔列表。注意：这些字段区分大小写！\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:47\nmsgid \"\"\n\"``Legacy license hashes``: Copy the digest hash generated for all legacy \"\n\"licenses - it’s available in the ``etc/tigase-console.log`` after startup\"\n\" (if such licenses are present).\"\nmsgstr \"\"\n\"``Legacy license hashes``: 复制为所有旧版许可证生成的摘要哈希 - 它在启动后在 ``etc/tigase-\"\n\"console.log`` 中可用（如果存在此类许可证）。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:49\nmsgid \"\"\n\"``Captcha question``: Enter the basic math answer for this form to prove \"\n\"you are not a robot.\"\nmsgstr \"``Captcha question``: 输入此表格的基本数学答案以证明您不是机器人。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:51\nmsgid \"The next page will provide you with an installation ID like the following:\"\nmsgstr \"下一页将为您提供安装 ID，如下所示：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:57\nmsgid \"Edit your ``config.tdsl`` file and add your installation-id\"\nmsgstr \"编辑您的 ``config.tdsl`` 文件并添加您的安装 ID\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:63\nmsgid \"\"\n\"Note that the ``installation-id`` file will be made automatically once \"\n\"the license file is installed and verified by the server.\"\nmsgstr \"请注意，一旦安装许可证文件被安装并由服务器验证，``installation-id`` 文件将自动生成。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:65\nmsgid \"Obtaining a Server Code\"\nmsgstr \"获取服务器代码\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:67\nmsgid \"\"\n\"Once you have the *Installation ID*, you will need to generate a server \"\n\"code. This can be done by accessing the admin UI page and navigating to \"\n\"the License section. Once there, click on Retrieve code for license. \"\n\"Select the component you wish to generate a code for and click Submit. \"\n\"You will see a fields with installation-id, module, VHosts filled out \"\n\"based on your server’s configuration. Copy the contents of the Code field\"\n\" and proceed to the next section.\"\nmsgstr \"\"\n\"获得 *安装ID* \"\n\"后，您将需要生成服务器代码。这可以通过访问管理UI页面并导航到许可证部分来完成。到达那里后，单击检索代码以获取许可证。选择您要为其生成代码的组件，然后单击提交。您将看到根据您的服务器配置填写的安装ID，模块，VHosts字段。复制代码字段的内容并继续下一部分。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:69\nmsgid \"Obtaining license file\"\nmsgstr \"获取许可证文件\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:71\nmsgid \"\"\n\"Open a new browser and navigate to this address: \"\n\"https://license.tigase.software/retrieve once there, paste the generated \"\n\"code from the last step in the field and click submit. Afterwards you \"\n\"will be prompted to download a license file, place this file in your \"\n\"*etc/* folder and restart your server, your license is now activated and \"\n\"installed on your server.\"\nmsgstr \"\"\n\"打开一个新的浏览器并导航到这个地址：https://license.tigase.software/retrieve，在那里，将最后一步生成的代码粘贴到该字段中，然后单击提交。之后，系统会提示您下载许可证文件，将此文件放在您的\"\n\" *etc/* 文件夹中并重新启动服务器，您的许可证现在已激活并安装在您的服务器上。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:73\nmsgid \"\"\n\"**If you are provided a manually produced license, you will need to place\"\n\" it in the same** ``etc/`` **directory with the name** \"\n\"``<component_name>.license`` (**e.g.:** ``etc/acs.license``)\"\nmsgstr \"\"\n\"**如果您被提供手动生成的许可证，您需要将其放在同一个** ``etc/`` \"\n\"**目录中，命名为** ``<component_name>.license`` (**例如**：``etc/acs.\"\n\"license``)\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:76\nmsgid \"What happens if I do not use a license file or it is expired?\"\nmsgstr \"如果我不使用许可证文件或它已过期会怎样？\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:78\nmsgid \"\"\n\"Tigase permits commercial products to be used without a license, but a \"\n\"validation process must complete otherwise the server will shutdown. \"\n\"Within the first hour of runtime, Tigase will check for the presence and \"\n\"validity of the license file. If none is found, or it is invalid or \"\n\"expired the server will then contact Tigase master server in order to \"\n\"obtain a valid one.\"\nmsgstr \"Tigase允许在没有许可证的情况下使用商业产品，但必须完成验证过程，否则服务器将关闭。在运行的第一个小时内，Tigase将检查许可证文件的存在和有效性。如果没有找到，或者它无效或过期，服务器将联系Tigase主服务器以获得一个有效的文件。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:80\nmsgid \"\"\n\"Communications will be made to license.tigase.software over https (port \"\n\"443) to verify the license or download a new one.\"\nmsgstr \"将通过 https（端口 443）与 license.tigase.software 进行通信，以验证许可证或下载新许可证。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:83\nmsgid \"Demo mode\"\nmsgstr \"演示模式\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:85\nmsgid \"\"\n\"If no valid license can be found, Tigase will revert to a demonstration \"\n\"mode. Most functions will be available and usable, but with a caveat. \"\n\"Statistics from that server will be sent to https://stats.tigase.software\"\n\" about your server and it’s usage. Details are in the next section. If \"\n\"this information cannot be sent, the server will assume unauthorized use \"\n\"and will shut down.\"\nmsgstr \"\"\n\"如果找不到有效的许可证，Tigase \"\n\"将恢复为演示模式。大多数功能将可以被使用，但有一个事先声明。该服务器的统计信息有关您的服务器及其使用情况将发送到 \"\n\"https://stats.tigase.software。详细信息在下一节中。如果无法发送此信息，服务器将假定未经授权使用并关闭。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:88\nmsgid \"Statistics Sent\"\nmsgstr \"已发送统计信息\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:90\nmsgid \"\"\n\"Statistics of your server may be sent to Tigase server’s if the all of \"\n\"following happens:\"\nmsgstr \"如果发生以下所有情况，您的服务器的统计信息可能会发送到 Tigase 服务器：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:92\nmsgid \"You are using commercial Tigase components.\"\nmsgstr \"您正在使用商业 Tigase 组件。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:94\nmsgid \"You have registered an ``installation-id``.\"\nmsgstr \"您已经注册了一个 ``installation-id``。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:96\nmsgid \"You do not have a current license to run Tigase commercial components.\"\nmsgstr \"您当前没有运行 Tigase 商业组件的许可证。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:98\nmsgid \"\"\n\"If these conditions exist, statistics will be sent to our servers and a \"\n\"warning will be posted in your logs. The following is an example of what \"\n\"information will be sent.\"\nmsgstr \"如果存在这些情况，统计数据将发送到我们的服务器，并在您的日志中发布警告。以下是一个示例告诉你哪些信息将被发送。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:102\nmsgid \"\"\n\"The text below has been better formatted for readability, but does not \"\n\"reflect the actual text being sent to Tigase.\"\nmsgstr \"下面的文本已被更好地格式化以提高可读性，但并不反映发送到 Tigase 的实际文本。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:251\nmsgid \"Unauthorized use\"\nmsgstr \"未经授权的使用\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:253\nmsgid \"\"\n\"Tigase will consider itself unauthorized if the following conditions are \"\n\"met:\"\nmsgstr \"如果满足以下条件，Tigase 将认为自己未经授权：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:255\nmsgid \"if Tigase XMPP Server does not have a valid license file and\"\nmsgstr \"如果Tigase XMPP服务器没有有效的许可证文件并且\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:257\nmsgid \"\"\n\"cannot contact the licensing server to obtain installation id and \"\n\"attached licenses.\"\nmsgstr \"无法联系许可服务器以获取安装ID和附加的许可证。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:259\nmsgid \"Then the program will then attempt to send statistics.\"\nmsgstr \"然后程序将尝试发送统计信息。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:261\nmsgid \"if unable to sent statistics the server after a random number of retries.\"\nmsgstr \"如果在随机重试后无法向服务器发送统计信息。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:263\nmsgid \"\"\n\"if these retries are not successful within 10 attempts, the server will \"\n\"then shutdown.\"\nmsgstr \"如果这些重试在 10 次尝试中未成功，则服务器将关闭。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:265\nmsgid \"If you are experiencing this condition, please contact Tigase.\"\nmsgstr \"如果您遇到这种情况，请联系Tigase。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:268\nmsgid \"Manual mode\"\nmsgstr \"手动模式\"\n\n#: ../../Tigase_Administration/Using_Tigase/Licensing.inc:270\nmsgid \"\"\n\"If you cannot open communication to ``stats.tigase.software`` or \"\n\"``license.tigase.software`` over the required ports (https over port \"\n\"443), you may request to use manual mode. Manual mode requires Tigase to \"\n\"create a license file to be used on your machine locally. This must be \"\n\"placed in the same folder as the above information, and the license check\"\n\" system will not seek communication unless the license is invalid or \"\n\"expired.\"\nmsgstr \"\"\n\"如果您无法通过所需端口（端口 443 上的https）打开与 ``stats.tigase.software`` 或 \"\n\"``license.tigase.software`` \"\n\"的通信，您可以请求使用手动模式。手动模式需要Tigase创建要在本地机器上使用的许可证文件。这个必须和上面的信息放在同一个文件夹里，除非License失效或过期，否则License检查系统不会寻求通信。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:2\nmsgid \"Tigase Clustering\"\nmsgstr \"Tigase聚类\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:4\nmsgid \"\"\n\"Tigase Clustering allows the use of a number of servers to be unified in \"\n\"delivering, from what a client or user sees, a single unified platform. \"\n\"There are two typical reasons why clustering should be employed:\"\nmsgstr \"Tigase聚类允许使用多个服务器来统一交付，从客户或用户的角度来看，是一个统一的平台。为何应该使用集群有两个典型的原因：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:6\nmsgid \"High Availability\"\nmsgstr \"高可用性\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:12\nmsgid \"Load Balancing\"\nmsgstr \"负载均衡\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:18\nmsgid \"With Tigase, you don’t have to choose between either/or!\"\nmsgstr \"使用Tigase，您不必在两者之间做出选择！\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:20\nmsgid \"\"\n\"**Tigase Clustering** offers **Full Redundancy** and **Automatic Load \"\n\"Balancing** allowing addition of new nodes at runtime with a simple \"\n\"configuration. All without a severe tax on resource consumption.\"\nmsgstr \"\"\n\"**Tigase 聚类** 提供 **完全冗余** 和 **自动负载平衡** \"\n\"其允许在运行时通过简单的配置添加新节点。所有这些都没有对资源消耗造成严重负担。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:22\nmsgid \"\"\n\"All basic components support clustering configuration, and some may be \"\n\"turned on or off.\"\nmsgstr \"所有基础组件都支持集群配置，并且部分组件可以开启或关闭。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:27\nmsgid \"\"\n\"To enable Clustering on Tigase servers, use the following line in your \"\n\"``config.tdsl`` file:\"\nmsgstr \"要在Tigase服务器上启用集群，请在 ``config.tdsl`` 文件中使用以下行：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:33\nmsgid \"That’s it!\"\nmsgstr \"就这些！\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:36\nmsgid \"Custom Ports\"\nmsgstr \"自定义端口\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:38\nmsgid \"\"\n\"You can customize ports for the cluster component, just be sure that each\"\n\" clustered server also has the same settings so they can communicate.\"\nmsgstr \"您可以为集群组件自定义端口，只需确保每个集群服务器也具有相同的设置，以便它们可以通信。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:49\nmsgid \"\"\n\"You can fine tune each port configuration, however this is not typically \"\n\"needed.\"\nmsgstr \"您可以微调每个端口配置，但这通常不是必需的。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:52\nmsgid \"Custom Port Configuration\"\nmsgstr \"自定义端口配置\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:54\nmsgid \"\"\n\"Each port has it’s own details that can be manipulated via the following \"\n\"ports. Again **THIS IS OPTIONAL**\"\nmsgstr \"每个端口都有自己的详细信息，可以通过以下端口进行操作。再次强调 **这是可选的**\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:76\nmsgid \"Multi-node configuration\"\nmsgstr \"多节点配置\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:78\nmsgid \"\"\n\"Each node should have ``'cluster-mode' = true`` enabled that you wish to \"\n\"connect to the cluster. They will automatically discover other nodes to \"\n\"connect to VIA Server to Server traffic. Nodes that are added or removed \"\n\"will be periodically updated.\"\nmsgstr \"\"\n\"每个节点都应该有 ``'cluster-mode' = true`` 以启用您希望连接到的集群。他们将自动发现其他节点以连接到VIA \"\n\"Server到Server的流量。添加或删除的节点将定期更新。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:81\nmsgid \"Traffic Control\"\nmsgstr \"流量管制\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:83\nmsgid \"\"\n\"You can customize the traffic going between clustered servers with a few \"\n\"options.\"\nmsgstr \"您可以使用几个选项自定义集群服务器之间的流量。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:85\nmsgid \"cm-ht-traffic-throttling\"\nmsgstr \"cm-ht-traffic-throttling\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:87\nmsgid \"\"\n\"This setting will control the number of bytes sent over non-user \"\n\"connections. Namely, Server to Server or S2S connections.\"\nmsgstr \"此设置将控制通过非用户连接发送的字节数。即服务器到服务器或S2S连接。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:93\nmsgid \"\"\n\"The format is as follows: ``{traffic-type}:{maximum-traffic}:{max-\"\n\"lifespan-traffic}:{action}``\"\nmsgstr \"格式如下：``{traffic-type}:{maximum-traffic}:{max-lifespan-traffic}:{action}``\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:96\nmsgid \"**traffic-type**\"\nmsgstr \"**流量类型**\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:96\nmsgid \"\"\n\"Specifies the type of traffic controlled. This can either be **XMPP** or \"\n\"**bin**. XMPP limits the number of packets transferred, whereas bin \"\n\"limits the number of bytes transferred.\"\nmsgstr \"指定控制的流量类型。这可以是 **XMPP** 或 **bin**。XMPP限制传输的数据包数量，而bin限制传输的字节数。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:99\nmsgid \"**maximum-traffic**\"\nmsgstr \"**最大流量**\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:99\nmsgid \"Specifies how many bytes or packets may be sent within one minute.\"\nmsgstr \"指定一分钟内可以发送多少字节或数据包。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:102\nmsgid \"**max-lifespan-traffic**\"\nmsgstr \"**流量最大寿命**\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:102\nmsgid \"\"\n\"Specifies how many bytes or packets may be sent within the lifetime of \"\n\"the connection. 0 means unlimited.\"\nmsgstr \"指定在连接的生命周期内可以发送多少字节或数据包。0表示无限制。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:105\nmsgid \"**action**\"\nmsgstr \"**操作**\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:105\nmsgid \"\"\n\"Specifies the action to be taken which can be **disc** which disconnects \"\n\"the connection, or **drop** which will drop any data exceeding the \"\n\"thresholds.\"\nmsgstr \"指定要采取的操作，可以是 **disc** 断开连接，或 **drop** 其将丢弃任何超过阈值的数据。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:107\nmsgid \"cm-see-other-host\"\nmsgstr \"cm-see-other-host\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:109\nmsgid \"\"\n\"This allows the specific use of a load balancing mechanism by selecting \"\n\"``SeeOtherHostIfc`` implementation. For more details, see :ref:`Tigase \"\n\"Load Balancing<loadBalancing>` documentation.\"\nmsgstr \"\"\n\"这允许通过选择 ``SeeOtherHostIfc`` \"\n\"实现来特定使用负载平衡机制。有关更多详细信息，请参阅 \"\n\":ref:`Tigase负载平衡<loadBalancing>` 文档。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:112\nmsgid \"Old configuration method\"\nmsgstr \"旧配置方法\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:114\nmsgid \"\"\n\"While these options are still available these settings CAN be less \"\n\"reliable. **Use ONLY if you need specific setups that cannot be \"\n\"accommodated by the automatic cluster mode**.\"\nmsgstr \"虽然这些选项仍然可用，但这些设置可能不太可靠。**仅当您需要自动集群模式无法满足的特定设置时使用**。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:118\nmsgid \"Specifying Specific nodes\"\nmsgstr \"指定特定节点\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:120\nmsgid \"\"\n\"You can still use the old method of specifying every node on each server.\"\n\" Server 3 needs the following set\"\nmsgstr \"您仍然可以使用在每台服务器上指定每个节点的旧方法。服务器3需要以下设置\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:126\nmsgid \"Server 2 needs\"\nmsgstr \"服务器2需要\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:132\nmsgid \"and so on…​\"\nmsgstr \"等等…\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:134\nmsgid \"However, we do not recommend this.\"\nmsgstr \"但是，我们不建议这样做。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:136\nmsgid \"Password and Port configuration\"\nmsgstr \"密码和端口配置\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:138\nmsgid \"\"\n\"You may specify a password and port to specific cluster servers if that \"\n\"is required. To do so, you will need to add {password}:{port} to the \"\n\"domain, like this example:\"\nmsgstr \"如果需要，您可以指定特定集群服务器的密码和端口。为此，您需要将{password}:{port}添加到域中，如下例所示：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:147\nmsgid \"Checking Cluster Connections\"\nmsgstr \"检查集群连接\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:149\nmsgid \"\"\n\"After setting up clustering you may want to verify that the clusters are \"\n\"operational. Right now it can be done in two manners - first by checking \"\n\"that there are actual network connections established between cluster \"\n\"nodes. The other is to check internal status of the server.\"\nmsgstr \"\"\n\"设置集群后，您可能需要验证集群是否正常运行。现在可以通过两种方式完成 - \"\n\"首先检查集群节点之间是否建立了实际的网络连接。另一种是检查服务器的内部状态。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:152\nmsgid \"Established connections\"\nmsgstr \"已建立的连接\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:154\nmsgid \"\"\n\"There are number of ways to check for opened connections, simplest one \"\n\"use command line. (Tigase uses port *5277* for cluster connections)\"\nmsgstr \"有多种方法可以检查打开的连接，最简单的一种是使用命令行。（Tigase使用端口 *5277* 进行集群连接）\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:156\nmsgid \"Linux\"\nmsgstr \"Linux\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:162\nmsgid \"Windows\"\nmsgstr \"Windows\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:170\nmsgid \"Cluster nodes connected (using XMPP)\"\nmsgstr \"连接的集群节点（使用XMPP）\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:172\nmsgid \"\"\n\"Verifying clustering connectivity over XMPP protocol requires any XMPP \"\n\"client capable of `XEP-0030: Service Discovery \"\n\"<http://xmpp.org/extensions/xep-0030.html>`__. It’s essential to remember\"\n\" that only an administrator (a user whose JID is configured as \"\n\"administrative) has access.\"\nmsgstr \"\"\n\"通过XMPP协议验证集群连接性需要任何 XMPP客户端能够使用 `XEP-0030: Service Discovery \"\n\"<http://xmpp.org/extensions/xep-0030.html>`__。请务必记住，只有管理员（其JID配置为管理的用户）才有访问权限。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:174\nmsgid \"Psi XMPP Client\"\nmsgstr \"Psi XMPP客户端\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:176\nmsgid \"\"\n\"For the purpose of this guide a `Psi <http://psi-im.org/>`__ client will \"\n\"be used. After successfully configuring and connecting to account with \"\n\"administrative privileges we need to access *Service Discovery*, either \"\n\"from application menu or from context menu of the particular account \"\n\"account:\"\nmsgstr \"\"\n\"出于本指南的目的，将使用 `Psi <http://psi-im.org/>`__ \"\n\"客户端。成功配置并连接到具有管理权限的帐户后，我们需要从应用程序菜单或特定帐户的上下文菜单访问 *Service Discovery*：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:178\nmsgid \"|roster-discovery|\"\nmsgstr \"|roster-discovery|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:184\nmsgid \"roster-discovery\"\nmsgstr \"roster-discovery\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:180\nmsgid \"\"\n\"In the *Service Discovery* window we need to find *Cluster Connection \"\n\"Manager* component. After expanding the tree node for the component a \"\n\"list of all cluster nodes will be presented with the current status \"\n\"(either *connected* or *disconnected*). Node column will contain actual \"\n\"hostname of the cluster node:\"\nmsgstr \"\"\n\"在 *Service Discovery* 窗口中，我们需要找到 *Cluster Connection Manager* \"\n\"组件。展开组件的树节点后，所有集群节点的列表将显示当前状态（*已连接* 或 *已断开*）。节点列将包含集群节点的实际主机名：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:182\nmsgid \"|discovery-nodes|\"\nmsgstr \"|discovery-nodes|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Clustering.inc:185\nmsgid \"discovery-nodes\"\nmsgstr \"discovery-nodes\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:2\nmsgid \"Anonymous Users & Authentication\"\nmsgstr \"匿名用户和身份验证\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:4\nmsgid \"\"\n\"To support anonymous users, you must first enable anonymous \"\n\"authentication on your server.\"\nmsgstr \"要支持匿名用户，您必须首先在您的服务器上启用匿名身份验证。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:7\nmsgid \"Anonymous Authentication\"\nmsgstr \"匿名认证\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:9\nmsgid \"\"\n\"Tigase Server can support anonymous logins via SASL-ANONYMOUS in certain \"\n\"scenarios. This can be enabled on per-VHost basis by adjusting *Anonymous\"\n\" enabled* option as described in :ref:`Add and Manage Domains \"\n\"(VHosts)<addManageDomain>` This setting is false by default as SASL-\"\n\"ANONYMOUS may not be totally secure as users can connect without prior \"\n\"permission (username and password).\"\nmsgstr \"\"\n\"Tigase Server在某些场景下可以通过SASL-ANONYMOUS支持匿名登录。这可以通过调整 *\"\n\"Anonymous enabled* 选项在每个VHost基础上启用，正如 \"\n\":ref:`添加和管理域(VHosts)<addManageDomain>` \"\n\"中所述，默认情况下此设置为false，因为 SASL-\"\n\"ANONYMOUS可能并不完全安全，因为用户无需事先许可（用户名和密码）即可连接。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:12\nmsgid \"Anonymous User Features\"\nmsgstr \"匿名用户功能\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:14\nmsgid \"\"\n\"To connect to your server anonymously, you must use a client that \"\n\"supports anonymous authentication and users. Connect to the server with \"\n\"the name of the server as the username, and no password. For example, to \"\n\"connect anonymously to ``xmpp.example.com`` use the following \"\n\"credentials,\"\nmsgstr \"\"\n\"要匿名连接到您的服务器，您必须使用支持匿名身份验证和用户的客户端。使用服务器名称作为用户名连接到服务器，没有密码。例如，要匿名连接到 \"\n\"``xmpp.example.com``，请使用以下凭据，\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:16\nmsgid \"Username: ``xmpp.example.com`` Password:\"\nmsgstr \"用户名：``xmpp.example.com`` 密码：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:18\nmsgid \"\"\n\"In this mode all login information is stored in memory, and cannot be \"\n\"retrieved at a later date.\"\nmsgstr \"在这种模式下，所有登录信息都存储在内存中，以后无法检索。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:20\nmsgid \"Other features of Anonymous Authentication\"\nmsgstr \"匿名身份验证的其他功能\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:22\nmsgid \"Temporary Jid is assigned and destroyed the moment of login/logout.\"\nmsgstr \"临时Jid在登录/注销时被分配和销毁。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:24\nmsgid \"Anonymous users cannot access the database\"\nmsgstr \"匿名用户无法访问数据库\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:26\nmsgid \"\"\n\"Anonymous users cannot communicate outside the server (use s2s \"\n\"connections)\"\nmsgstr \"匿名用户无法在服务器外部进行通信（使用s2s连接）\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:28\nmsgid \"Anonymous users have a default limit on traffic generated per user.\"\nmsgstr \"匿名用户对每个用户生成的流量有默认限制。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:31\nmsgid \"Reconnection on Anonymous\"\nmsgstr \"匿名重新连接\"\n\n#: ../../Tigase_Administration/Using_Tigase/Anonymous_Users.inc:33\nmsgid \"\"\n\"On products such as our JaXMPP Server, users connected using SASL-\"\n\"ANONYMOUS can reconnect to existing sessions using cookie management. \"\n\"However, reconnection can be improved and extended using `Bosh Session \"\n\"Cache <http://docs.tigase.org/tigase-\"\n\"server/snapshot/Development_Guide/html/#boshsessioncache>`__ which allows\"\n\" for session storage in memory rather than using client-side data for \"\n\"reconnection.\"\nmsgstr \"\"\n\"在我们的JaXMPP服务器等产品上，使用SASL-ANONYMOUS连接的用户可以使用cookie管理重新连接到现有会话。但是，可以使用 \"\n\"`Bosh Session Cache <http://docs.tigase.org/tigase-\"\n\"server/snapshot/Development_Guide/html/#boshsessioncache>`__ \"\n\"改进和扩展重新连接，它允许将会话存储在内存中而不是使用客户端数据来重新连接。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Scripting_Support.inc:4\nmsgid \"Scripting support in Tigase\"\nmsgstr \"Tigase中的脚本支持\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Scripting_Support.inc:6\nmsgid \"\"\n\"Tigase server supports scripting languages in versions 4.3.1 and higher. \"\n\"These pages describe this feature in details how to create new scripts, \"\n\"upload them to the server, and execute them. The guide also contains API \"\n\"description with code examples.\"\nmsgstr \"Tigase服务器支持4.3.1及更高版本的脚本语言。这些页面详细描述了此功能如何创建新脚本，将它们上传到服务器并执行它们。该指南还包含带有代码示例的API描述。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Scripting_Support.inc:10\nmsgid \"\"\n\"Tigase server is known for it very low memory consumption and \"\n\"successfully runs with less then 10MB of RAM memory. However adding \"\n\"scripting support for any non-standard (default) language to Tigase \"\n\"server significantly increases memory requirements for the installation. \"\n\"You cannot expect Tigase server to run on 10MB RAM system if you enabled \"\n\"Python, Scala or any other non-standard language.\"\nmsgstr \"\"\n\"Tigase服务器以其非常低的内存消耗而闻名，并且可以在不到10MB的RAM内存下成功运行。但是，向Tigase服务器添加对任何非标准（默认）语言的脚本支持会显著增加安装的内存需求。如果您启用Python，Scala或任何其他非标准语言，您不能期望Tigase服务器在10MB\"\n\" RAM系统上运行。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:4\nmsgid \"Scripting Introduction - Hello World!\"\nmsgstr \"脚本介绍 - Hello World!\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:6\nmsgid \"\"\n\"This document is the first in a series describing scripting support in \"\n\"the Tigase server showing how to load, install, update and call a script.\"\n\" It contains also an introduction to the scripting API with the first \"\n\"*Hello world!* example.\"\nmsgstr \"\"\n\"本文档是描述Tigase服务器中的脚本支持系列的第一篇，展示了如何加载，安装，更新和调用脚本。它还包含对脚本API的介绍以及第一个 *Hello \"\n\"world!* 示例。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:8\nmsgid \"\"\n\"Since Tigase version 4.3.1 the server supports scripting for \"\n\"administrator commands as well as standard commands.\"\nmsgstr \"自Tigase版本 4.3.1 起，服务器支持管理员命令和标准命令的脚本。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:10\nmsgid \"\"\n\"In theory many different languages can be used to write scripts and the \"\n\"only requirement is that support `JSR-223 \"\n\"<http://www.jcp.org/en/jsr/detail?id=223>`__ for the language is \"\n\"installed. More details can be found on the `Java scripting project site \"\n\"<https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/prog_guide/api.html>`__.\"\nmsgstr \"\"\n\"理论上可以使用许多不同的语言编写脚本，唯一的要求是安装该语言的支持 `JSR-223 \"\n\"<http://www.jcp.org/en/jsr/detail?id=223>`__ 。更多详细信息可在 `Java scripting \"\n\"project site \"\n\"<https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/prog_guide/api.html>`__\"\n\" 上找到。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:12\nmsgid \"\"\n\"In practice some languages are better supported than others, at the \"\n\"moment we recommend `Groovy <http://groovy-lang.org/>`__. However the \"\n\"following languages are also confirmed to be working: `Scala <http://www\"\n\".scala-lang.org/>`__, `Python <http://www.python.org/>`__ and `Ruby \"\n\"<http://www.ruby-lang.org/>`__. The `tigase-server GitHub \"\n\"<https://github.com/tigase/tigase-server/blob/master/src/main>`__ \"\n\"contains a few examples for these languages.\"\nmsgstr \"\"\n\"在实践中，某些语言比其他语言得到更好的支持，目前我们推荐 `Groovy <http://groovy-\"\n\"lang.org/>`__。然而，以下语言也被证实可以工作: `Scala <http://www.scala-lang.org/>`__, \"\n\"`Python <http://www.python.org/>`__ 和 `Ruby <http://www.ruby-\"\n\"lang.org/>`__。 `tigase-server GitHub <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main>`__ 包含这些语言的一些示例。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:16\nmsgid \"\"\n\"the default Tigase installation contains only libraries for Groovy. \"\n\"Adding support for a different language is as simple as copying a few JAR\"\n\" files to the Tigase ``libs/`` directory.\"\nmsgstr \"默认的Tigase安装只包含Groovy的库。添加对不同语言的支持就像将几个JAR 文件复制到Tigase ``libs/`` 目录一样简单。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:18\nmsgid \"\"\n\"All the examples presented in this guide are also available as ready to \"\n\"use scripts in the Tigase SVN repository in directory: \"\n\"`src/main/groovy/tigase/admin <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/groovy/tigase/admin>`__.\"\nmsgstr \"\"\n\"本指南中提供的所有示例也可以在Tigase SVN存储库中的目录中作为即用型脚本使用：`src/main/groovy/tigase/admin \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/groovy/tigase/admin>`__。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:20\nmsgid \"\"\n\"The scripting utilizes only standard XMPP extensions and is by no means \"\n\"specific to any particular solution. We use and prefer Psi client. The \"\n\"whole guide and all the screen-shots are created using Psi client. You \"\n\"can, however, use any other client which supports these extensions as \"\n\"well. As the whole thing is based on the service discovery and ad-hoc \"\n\"commands you need a XMPP client with a good support for both features.\"\nmsgstr \"\"\n\"该脚本仅使用标准XMPP扩展，绝不具体到任何特定解决方案。我们使用并更喜欢Psi客户端。整个指南和所有屏幕截图都是使用Psi客户端创建的。但是，您也可以使用任何其他支持这些扩展的客户端。由于整个事情都是基于服务发现和临时命令，因此您需要一个对这两个功能都有良好支持\"\n\" XMPP客户端。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:22\nmsgid \"\"\n\"To follow the guide and run all the examples you need will need to have \"\n\"installed Tigase server version 4.3.1 or newer and you have to connect to\"\n\" the server as administrator.\"\nmsgstr \"要遵循该指南并运行您需要的所有示例，您需要安装Tigase服务器版本4.3.1或更高版本，并且您必须以管理员身份连接到服务器。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:25\nmsgid \"Loading Script at Run Time\"\nmsgstr \"在运行时加载脚本\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:27\nmsgid \"\"\n\"All the scripting stuff is usually based on the service discovery and ad-\"\n\"hoc commands in the Tigase server.\"\nmsgstr \"所有脚本内容通常基于Tigase服务器中的服务发现和临时命令。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:29\nmsgid \"|service disco|\"\nmsgstr \"|service disco|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:186\nmsgid \"service disco\"\nmsgstr \"service disco\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:31\nmsgid \"\"\n\"The first thing to do, therefore, is to browse service discovery on the \"\n\"running server. The result you receive will depend on your installation \"\n\"and installed components.\"\nmsgstr \"因此，首先要做的是浏览正在运行的服务器上的服务发现。您收到的结果将取决于您的安装和已安装的组件。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:33\nmsgid \"\"\n\"The most interesting things right now are all items with \"\n\"\\\"http://jabber.org/protocol/admin\\\" in their node part. You may have a \"\n\"few scripts loaded already but there are two commands used for scripting \"\n\"management. Their names are descriptive enough: ``New command script`` \"\n\"and ``Remove command script``.\"\nmsgstr \"\"\n\"现在最有趣的是所有在其节点部分带有\\\"http://jabber.org/protocol/admin\\\"的项目。您可能已经加载了一些脚本，但是有两个用于脚本管理的命令。它们的名称足以描述自己：``New\"\n\" command script`` 和 ``Remove command script``。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:35\nmsgid \"\"\n\"The first is for adding a new script or updating existing and the second \"\n\"is for removing script from the server.\"\nmsgstr \"第一个用于添加新脚本或更新现有脚本，第二个用于从服务器中删除脚本。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:37\nmsgid \"\"\n\"To add a new script you have just to execute ``New command script``. In \"\n\"Psi this is done by double clicking on the element in service discovery \"\n\"list.\"\nmsgstr \"要添加新脚本，您只需执行 ``New command script``。在Psi中，这是通过双击服务发现列表中的元素来完成的。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:39\nmsgid \"|hello1 new script|\"\nmsgstr \"|hello1 new script|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:187\nmsgid \"hello1 new script\"\nmsgstr \"hello1 new script\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:41\nmsgid \"\"\n\"The screenshot above shows a couple of options to set for the loaded \"\n\"script:\"\nmsgstr \"上面的屏幕截图显示了为加载脚本设置的几个选项：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:44\nmsgid \"Description\"\nmsgstr \"描述\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:44\nmsgid \"\"\n\"is what shows as the script name in the service discovery window. There \"\n\"are no special restrictions on what to put there.\"\nmsgstr \"是在服务发现窗口中显示为脚本名称的内容。其中放什么东西没有特别的限制。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:47\nmsgid \"Command id\"\nmsgstr \"命令id\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:47\nmsgid \"\"\n\"is a unique ID of the script (admin command). This is what shows after \"\n\"the \\\"http://jabber.org/protocol/admin\\\" in node part. This needs to be \"\n\"unique or existing script is overwritten.\"\nmsgstr \"\"\n\"是脚本的唯一 \"\n\"ID（管理命令）。这是节点部分中\\\"http://jabber.org/protocol/admin\\\"之后显示的内容。这需要是独一无二的，否则现有的脚本会被覆盖。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:50\nmsgid \"Language\"\nmsgstr \"语言\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:50\nmsgid \"\"\n\"a drop down list of all supported scripting languages for your \"\n\"installation. Tigase automatically detects all libraries for scripting \"\n\"languages and lists them here. So all you need is to select the correct \"\n\"language for your script.\"\nmsgstr \"您的安装支持的所有脚本语言的下拉列表。 Tigase自动检测所有脚本语言库并在此处列出。因此，您只需为您的脚本选择正确的语言。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:53\nmsgid \"Script text\"\nmsgstr \"脚本文本\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:53\nmsgid \"is just your script content.\"\nmsgstr \"只是您的脚本内容。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:55\nmsgid \"\"\n\"When your script is ready and all fields are correctly set, simply press \"\n\"\\\"**Finish**\\\" button and you should receive a message confirming that \"\n\"the script has been loaded successfully.\"\nmsgstr \"当您的脚本准备好并且所有字段都设置正确时，只需按\\\"**Finish**\\\"按钮，您应该会收到一条消息，确认脚本已成功加载。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:57\nmsgid \"|loaded ok small|\"\nmsgstr \"|loaded ok small|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:188\nmsgid \"loaded ok small\"\nmsgstr \"loaded ok small\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:59\nmsgid \"\"\n\"In this guide we are creating a simple \\\"Hello world\\\" script written in \"\n\"Groovy. What it does is displays a window (ad-hoc command result) with a \"\n\"message: \\\"*Hello admin, how are you?*\\\".\"\nmsgstr \"\"\n\"在本指南中，我们将创建一个用Groovy编写的简单\\\"Hello \"\n\"world\\\"脚本。它的作用是显示一个带有消息的窗口（临时命令结果）：\\\"*Hello admin, how are you?*\\\"。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:61\nmsgid \"It uses a basic scripting API which is described line by line below:\"\nmsgstr \"它使用下面逐行描述的基本脚本API：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:63\nmsgid \"It imports basic Tigase classes.\"\nmsgstr \"它导入基本的Tigase类。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:65\nmsgid \"\"\n\"Sets a local variable ``p`` which points to a ``packet`` variable with \"\n\"data received from the client.\"\nmsgstr \"设置一个局部变量 ``p`` ，它指向一个带有从客户端接收到的数据的 ``packet`` 变量。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:67\nmsgid \"\"\n\"Creates a ``res`` variable which is response sent back to the client \"\n\"(administrator). The response to the client is of type ``result``. Other \"\n\"possible types will be introduced later.\"\nmsgstr \"创建一个 ``res`` 变量，它是发送回客户端（管理员）的响应。对客户端的响应是 ``result`` 类型。其他可能的类型将在后面介绍。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:69\nmsgid \"\"\n\"We operate on ad-hoc commands here so the script uses Tigase utility \"\n\"class to set/retrieve command parameters. It sets the window title and a \"\n\"simple message displayed to the user (administrator).\"\nmsgstr \"我们在这里对临时命令进行操作，因此脚本使用Tigase实用程序类来设置/检索命令参数。它设置窗口标题和显示给用户（管理员）的简单消息。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:71\nmsgid \"The last line returns new packet as a script execution result.\"\nmsgstr \"最后一行返回新数据包作为脚本执行结果。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:73\nmsgid \"The first, very simple version looks like this:\"\nmsgstr \"第一个非常简单的版本如下所示：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:85\nmsgid \"Executing Script\"\nmsgstr \"执行脚本\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:87\nmsgid \"\"\n\"Once the script is successfully loaded you will have to reload/refresh \"\n\"the service discovery window which now should display one more element on\"\n\" the list.\"\nmsgstr \"成功加载脚本后，您将不得不重新加载/刷新服务发现窗口，该窗口现在应该在列表中再显示一个元素。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:89\nmsgid \"|service disco with new hello|\"\nmsgstr \"|service disco with new hello|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:189\nmsgid \"service disco with new hello\"\nmsgstr \"service disco with new hello\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:91\nmsgid \"\"\n\"As you can see script name is set to what you have entered as \"\n\"\\\"Description\\\" in script loading window - \\\"*Hello world script*\\\". The \"\n\"command node is set to: \\\"http://jabber.org/protocol/admin#hello\\\" if \"\n\"\\\"**hello**\\\" is what is set as the script ID.\"\nmsgstr \"\"\n\"如您所见，脚本名称设置为您在脚本加载窗口中输入的 \\\"Description\\\" - \\\"*Hello world \"\n\"script*\\\"。如果脚本ID设置为\\\"**hello**\\\"，则命令节点设置为：\\\"http://jabber.org/protocol/admin#hello\\\"。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:93\nmsgid \"\"\n\"To execute the script you just have to double click on the script name \"\n\"(or click execute command if you use any other client).\"\nmsgstr \"要执行脚本，您只需双击脚本名称（如果您使用任何其他客户端，请单击执行命令）。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:95\nmsgid \"\"\n\"As a result you should see a simple window similar to the screenshot \"\n\"below displaying our message.\"\nmsgstr \"结果，您应该会看到一个类似于下面显示我们消息的屏幕截图的简单窗口。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:97\nmsgid \"|hello1 result small|\"\nmsgstr \"|hello1 result small|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:190\nmsgid \"hello1 result small\"\nmsgstr \"hello1 result small\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:100\nmsgid \"Interaction in Scripts\"\nmsgstr \"脚本中的交互\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:102\nmsgid \"\"\n\"Displaying just a message is very nice but is not very useful in most \"\n\"cases. Normally you need to ask the user for some more data or parameters\"\n\" before you can perform any real processing.\"\nmsgstr \"只显示一条消息非常好，但在大多数情况下并不是很有用。通常，您需要向用户询问更多数据或参数，然后才能执行任何实际程序。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:104\nmsgid \"\"\n\"Therefore in most cases the administrator script has to display a new \"\n\"window with input fields asking the user for some more data. In this \"\n\"document we present very simple examples, just an introduction so let’s \"\n\"ask about the administrator name before displaying a greeting.\"\nmsgstr \"因此，在大多数情况下，管理员脚本必须显示一个带有输入字段的新窗口，要求用户提供更多数据。在本文档中，我们提供了非常简单的示例，这只是一个介绍，所以让我们在显示问候语之前询问管理员名称。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:106\nmsgid \"|hello2 asking for name small|\"\nmsgstr \"|hello2 asking for name small|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:191\nmsgid \"hello2 asking for name small\"\nmsgstr \"hello2 asking for name small\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:108\nmsgid \"\"\n\"To ask the user for some more information we have to extend example above\"\n\" with some more code:\"\nmsgstr \"要向用户询问更多信息，我们必须使用更多代码扩展上面的示例：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:133\nmsgid \"\"\n\"If you compare both scripts you see that they are quite similar. Before \"\n\"displaying greeting, however, the script tries to retrieve data from the \"\n\"``name`` input field. If the name had been provided the greeting is \"\n\"displayed, otherwise the script asks for the user name.\"\nmsgstr \"\"\n\"如果您比较这两个脚本，您会发现它们非常相似。然而，在显示问候语之前，脚本会尝试从 ``name`` \"\n\"输入字段中检索数据。如果提供了名称，则会显示问候语，否则脚本会询问用户名。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:135\nmsgid \"|hello2 result small|\"\nmsgstr \"|hello2 result small|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:192\nmsgid \"hello2 result small\"\nmsgstr \"hello2 result small\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:137\nmsgid \"\"\n\"Please note, in this case the packet sent back to the user is of type \"\n\"form instead of ``result``. The practical difference is that the type \"\n\"``result`` displays only **OK** button which when pressed doesn’t send \"\n\"any data to the server. The form packet displays more buttons - \"\n\"**Finish** and **Cancel**. Whichever you press some data is sent back to \"\n\"the server.\"\nmsgstr \"\"\n\"请注意，在这种情况下，发回给用户的数据包的类型是表单而不是 ``result``。实际的区别是 ``result`` 类型只显示 **OK** \"\n\"按钮，按下该按钮时不会向服务器发送任何数据。表单数据包显示更多按钮 - **Finish** 和 \"\n\"**Cancel**。无论您按哪个，都会将一些数据发送回服务器。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:139\nmsgid \"\"\n\"This script demonstrates use of two new methods from the utility class \"\n\"\\\"Command\\\": getFieldValue and addFieldValue.\"\nmsgstr \"该脚本演示了如何使用实用程序类\\\"Command\\\"中的两个新方法：getFieldValue 和 addFieldValue。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:141\nmsgid \"\"\n\"The first argument to all Command methods is the packet with ad-hoc \"\n\"command.\"\nmsgstr \"所有Command方法的第一个参数是带有 ad-hoc 命令的数据包。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:143\nmsgid \"The second argument is usually the input field name\"\nmsgstr \"第二个参数通常是输入字段名称\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:145\nmsgid \"\"\n\"These two method parameters are actually enough to read the ad-hoc \"\n\"command data. Methods creating input fields in the ad-hoc command need a \"\n\"few arguments more:\"\nmsgstr \"这两个方法参数实际上足以读取ad-hoc命令数据。在ad-hoc命令中创建输入字段的方法还需要一些参数：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:147\nmsgid \"\"\n\"Next arguments sets a default value displayed to the user. The way to it \"\n\"is set in the example above is specific to Groovy language and is quite \"\n\"useful what will be apparent in later examples.\"\nmsgstr \"下一个参数设置显示给用户的默认值。上面示例中设置的方法是特定于Groovy语言的，并且非常有用，这将在后面的示例中显而易见。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:149\nmsgid \"\"\n\"After that we have to specify the field type. All field types are defined\"\n\" in the `XEP-0004 <http://xmpp.org/extensions/xep-0004.html#protocol-\"\n\"fieldtypes>`__ article.\"\nmsgstr \"\"\n\"之后，我们必须指定字段类型。所有字段类型都在 `XEP-0004 \"\n\"<http://xmpp.org/extensions/xep-0004.html#protocol-fieldtypes>`__ 文章中定义。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:151\nmsgid \"\"\n\"The last argument specifies the field label which is displayed to the \"\n\"user.\"\nmsgstr \"最后一个参数指定显示给用户的字段标签。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:153\nmsgid \"|hello2 new script|\"\nmsgstr \"|hello2 new script|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:193\nmsgid \"hello2 new script\"\nmsgstr \"hello2 new script\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:155\nmsgid \"\"\n\"There are a few other different utility methods in the Command class to \"\n\"set different types of input fields and they will be described in details\"\n\" later on.\"\nmsgstr \"Command类中还有其他一些不同的实用方法来设置不同类型的输入字段，稍后将详细介绍。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:157\nmsgid \"\"\n\"To reload the script simply call \\\"New command script\\\" again, enter the \"\n\"script text and make sure you entered exactly the same command ID to \"\n\"replace the old script with the new one.\"\nmsgstr \"要重新加载脚本，只需再次调用\\\"New command script\\\"，输入脚本文本并确保输入的命令ID完全相同，以用新脚本替换旧脚本。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:159\nmsgid \"\"\n\"Or of course, you can enter a new command id to create a new command and \"\n\"make it available on your server.\"\nmsgstr \"或者，当然，您可以输入新的命令ID来创建新命令并使其在您的服务器上可用。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:161\nmsgid \"\"\n\"When the script is loaded on the server, try to execute it. You should \"\n\"get a new dialog window asking for your name as in the screenshot at the \"\n\"beginning of this section. When you have entered your name and clicked \"\n\"the \\\"Finish\\\" button you will see another window with a greeting message\"\n\" along with your name.\"\nmsgstr \"当脚本加载到服务器上时，尝试执行它。您应该得到一个新的对话框窗口，询问您的姓名，如本节开头的屏幕截图所示。当您输入您的姓名并单击\\\"Finish\\\"按钮后，您将看到另一个窗口，其中包含您的姓名和问候消息。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:164\nmsgid \"Automatic Scripts Loading at Startup Time\"\nmsgstr \"启动时自动加载脚本\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:166\nmsgid \"\"\n\"The last thing described in this guide is how to automatically load your \"\n\"scripts when the Tigase server starts. The ability to load scripts at run\"\n\" time, update and remove remove them is very useful, especially in \"\n\"emergency cases if something wrong is going on and you want to act \"\n\"without affecting the service.\"\nmsgstr \"本指南中描述的最后一件事是如何在Tigase服务器启动时自动加载脚本。在运行时加载脚本，更新和删除脚本的能力非常有用，特别是在紧急情况下，如果出现问题并且您想在不影响服务的情况下采取此行动。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:168\nmsgid \"\"\n\"If you, however have a few dozens scripts you don’t want to manually load\"\n\" them every time the server restarts.\"\nmsgstr \"但是，如果您有几十个脚本，您不想在每次服务器重新启动时手动加载它们。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:170\nmsgid \"\"\n\"Tigase server automatically loads all scripts at the startup time which \"\n\"are located in the admin scripts directory. Unless you set it differently\"\n\" in the configuration it is: \"\n\"**YourTigaseInstallationDir/scripts/admin/**. All you have to do is to \"\n\"copy all your scripts to this directory and they will be loaded next time\"\n\" the server starts.\"\nmsgstr \"Tigase服务器在启动时自动加载位于管理脚本目录中的所有脚本。除非您在配置中设置不同，否则它在此：**YourTigaseInstallationDir/scripts/admin/**。您所要做的就是将所有脚本复制到此目录，它们将在下次服务器启动时加载。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:172\nmsgid \"\"\n\"But hold on. What about the script parameters: language, description, \"\n\"command id? How are you supposed to set them?\"\nmsgstr \"但是等一等。脚本参数呢？语言，描述，命令ID？你应该如何设置它们？\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:174\nmsgid \"\"\n\"Language is simple. It is detected automatically by the script file \"\n\"extension. So just make sure file extensions are correct and the language\"\n\" is sorted.\"\nmsgstr \"语言很简单。它由脚本文件扩展名自动检测。因此，只需确保文件扩展名正确且语言已排序。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:176\nmsgid \"\"\n\"The script description and command id needs a little bit more work. You \"\n\"have to include in your script following lines:\"\nmsgstr \"脚本描述和命令ID需要更多的工作。您必须在脚本中包含以下几行：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Introduction.inc:184\nmsgid \"\"\n\"Please note, there must be at least a single space after the \"\n\"``AS:Description:`` or ``AS:CommandId:`` string. Everything rest after \"\n\"that, until the end of the line, is treated as either the script \"\n\"description or command id. Put these in your script file and the loader \"\n\"will detect them and set correctly for your script.\"\nmsgstr \"\"\n\"请注意，在 ``AS:Description:`` 或 ``AS:CommandId:`` \"\n\"字符串之后必须至少有一个空格。之后的所有内容，直到行尾，都被视为脚本描述或命令ID。将它们放入您的脚本文件中，加载器将检测它们并为您的脚本正确设置。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:4\nmsgid \"Tigase Scripting Version 4.4.x Update for Administrators\"\nmsgstr \"面向管理员的Tigase脚本版本4.4.x更新\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:6\nmsgid \"\"\n\"Scripting functionality is quite useful in Tigase server for all sorts of\"\n\" administrator tasks. The possibility to load new scripts or replace old \"\n\"ones at the server runtime opens quite new area for the service \"\n\"maintenance.\"\nmsgstr \"脚本功能在Tigase服务器中对于各种管理员任务非常有用。在服务器运行时加载新脚本或替换旧脚本的可能性为服务维护开辟了一个全新的领域。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:8\nmsgid \"\"\n\"In earlier versions of the Tigase server scripting capabilities was \"\n\"available only in the session manager component while it might be very \"\n\"useful in many other places - connection managers, MUC, PubSub, \"\n\"VHostManager and what even more important in completely new, custom \"\n\"components created for specific needs. It would be quite wasteful to \"\n\"reinvent the wheel every time and implementing scripting capabilities for\"\n\" each component separately.\"\nmsgstr \"\"\n\"在早期版本的Tigase服务器脚本功能仅在会话管理器组件中可用，而它在许多其他地方可能非常有用 - \"\n\"连接管理器，MUC，PubSub，VHostManager \"\n\"以及在为特定对象创建的全新自定义组件中更为重要。每次都重新发明wheel并为每个组件单独实现脚本功能是非常浪费的。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:10\nmsgid \"\"\n\"Therefore the scripting capabilities has been implemented in the core of \"\n\"the Tigase server. It is now part of the API and is automatically \"\n\"available to all components without any additional coding. A detailed \"\n\"developer guide will be published separately.\"\nmsgstr \"因此，脚本功能已在Tigase服务器的核心中实现。它现在是API的一部分，无需任何额外编码即可自动用于所有组件。详细的开发者指南将单独发布。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:12\nmsgid \"\"\n\"This document describes changes from the user/administrator perspective \"\n\"because there are some usability changes related to the new \"\n\"implementation.\"\nmsgstr \"本文档从用户/管理员的角度描述了更改，因为有一些与新实现相关的可用性更改。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:14\nmsgid \"\"\n\"Please note. The description and screenshots are taken from the Psi \"\n\"client and most likely interface for ad-hoc commands and service \"\n\"discovery on other client looks different. I recommend to do some initial\"\n\" testing and experiments using Psi client and then switch to your \"\n\"preferred application for your day-to-day use.\"\nmsgstr \"\"\n\"请注意。描述和屏幕截图取自Psi客户端，很可能在其他客户端上用于临时命令和服务发现的界面看起来不同。我建议使用 \"\n\"Psi客户端进行一些初始测试和实验，然后切换到您喜欢的应用程序以供您日常使用。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:16\nmsgid \"\"\n\"As it always was in the Tigase you can access all the functions via XMPP \"\n\"service discovery on the server. However, as soon as you connect to the \"\n\"server you can see some changes there.\"\nmsgstr \"就像在Tigase中一样，您可以通过服务器上的XMPP服务发现访问所有功能。但是，一旦您连接到服务器，您就可以在那里看到一些变化。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:18\nmsgid \"|new service disco admin|\"\nmsgstr \"|new service disco admin|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:40\nmsgid \"new service disco admin\"\nmsgstr \"new service disco admin\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:20\nmsgid \"\"\n\"There are no command on the list. They are hidden from the main service \"\n\"discovery list. You can see on the list only the server main components.\"\nmsgstr \"列表中没有命令。它们隐藏在主服务发现列表中。您只能在列表中看到服务器主要组件。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:22\nmsgid \"\"\n\"This had to be done for many reasons. One of them is, obviously, the \"\n\"cleaner access to the main server stuff. Another, probably more \"\n\"important, is to avoid a long list of commands for different components \"\n\"mixed together. Commands for different components can have the same \"\n\"name/description and they can even do similar things but they are \"\n\"executed on a different server component. To avoid any confusion and \"\n\"minimize opportunities for mistake the commands are now closely tight to \"\n\"their components. To access a list of commands for a particular component\"\n\" you have to double click on the component name on the list or click \"\n\"'Execute command\\\" icon on top of the window when your component is \"\n\"selected.\"\nmsgstr \"\"\n\"出于多种原因，必须这样做。其中之一显然是对主服务器内容的更干净的访问。另一个可能更重要的是避免将不同组件的一长串命令混合在一起。不同组件的命令可以具有相同的名称/描述，它们甚至可以做类似的事情，但它们在不同的服务器组件上执行。为了避免任何混淆并尽量减少出错的机会，这些命令现在与其组件紧密相连。要访问特定组件的命令列表，您必须双击列表中的组件名称或在选择组件时单击窗口顶部的\\\"Execute\"\n\" command\\\"图标。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:24\nmsgid \"\"\n\"A new window should show up with drop-down list of available commands. \"\n\"All the commands are related to the selected component and are executed \"\n\"kind of \\\"inside the component environment\\\". You can of course add new \"\n\"command or delete existing one and of course execute any of the commands \"\n\"showing on the list.\"\nmsgstr \"\"\n\"应显示一个新窗口，其中包含可用命令的下拉列表。所有命令都与所选组件相关，并在\\\"inside the component \"\n\"environment\\\"执行。您当然可以添加新命令或删除现有命令，当然还可以执行列表中显示的任何命令。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:26\nmsgid \"|new command list|\"\nmsgstr \"|new command list|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:41\nmsgid \"new command list\"\nmsgstr \"new command list\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:28\nmsgid \"\"\n\"As a reminder, in the window title you can see the component ID and you \"\n\"should check it before running any command to make sure you accidentally \"\n\"don’t break your system.\"\nmsgstr \"提醒一下，在窗口标题中您可以看到组件ID，您应该在运行任何命令之前检查它，以确保您不会意外破坏系统。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:30\nmsgid \"|new add command|\"\nmsgstr \"|new add command|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:42\nmsgid \"new add command\"\nmsgstr \"new add command\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:32\nmsgid \"\"\n\"There has been also a small change made to the script adding window. As \"\n\"you can see on the screenshot there is one additional option added - \"\n\"\\\"Save to disk\\\". This means that once you submitted the script to the \"\n\"server it is written to the hard drive and will be automatically loaded \"\n\"at next startup time.\"\nmsgstr \"\"\n\"脚本添加窗口也做了一些小的改动。正如您在屏幕截图中看到的那样，添加了一个附加选项 - \\\"Save to \"\n\"disk\\\"。这意味着一旦您将脚本提交到服务器，它就会被写入硬盘驱动器，并将在下次启动时自动加载。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:34\nmsgid \"\"\n\"This option is enabled by default as this seems to be a logical choice \"\n\"that the administrator wants to save his new script for later reuse. \"\n\"This, however requires proper configuration of the server and give \"\n\"writing permission to the directory where all scripts are stored. \"\n\"Otherwise the server won’t be able to write script files on the hard \"\n\"drive.\"\nmsgstr \"默认情况下启用此选项，因为这似乎是管理员希望保存他的新脚本以供以后重用的逻辑选择。但是，这需要正确配置服务器并授予对存储所有脚本的目录的写入权限。否则服务器将无法在硬盘上写入脚本文件。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:36\nmsgid \"\"\n\"As in previous version only users with administrator permissions can \"\n\"execute commands and access all the critical elements on the server. \"\n\"There has been, however, another change made, long time requested by \"\n\"users. In the new version all the administrator specific elements are \"\n\"hidden for the rest of users.\"\nmsgstr \"与以前的版本一样，只有具有管理员权限的用户才能执行命令并访问服务器上的所有关键元素。但是，用户长时间要求进行另一项更改。在新版本中，所有管理员特定的元素都对其余用户隐藏。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/v4.4.x_Update.inc:38\nmsgid \"\"\n\"Server components don’t show up on the service discovery, the user can’t \"\n\"see administrator commands nor he can execute them. This hasn’t been \"\n\"implemented to improve the server security but to reduce confusion for \"\n\"general users who would otherwise see a lot of stuff which can’t be used \"\n\"by them anyway.\"\nmsgstr \"服务器组件不会出现在服务发现中，用户看不到管理员命令，也无法执行它们。这并不是为了提高服务器的安全性，而是为了减少普通用户的困惑，否则他们会看到很多他们无论如何都无法使用的东西。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:4\nmsgid \"Tigase and Python\"\nmsgstr \"Tigase和Python\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:6\nmsgid \"\"\n\"This article describes how to get Python working as a scripting language \"\n\"for ad-hoc commands in Tigase server. The first part is installation, and\"\n\" the second shows a few code examples with explanation of the differences\"\n\" between Python usage and some other languages.\"\nmsgstr \"本文介绍了如何让Python在Tigase服务器中作为临时命令的脚本语言工作。第一部分是安装，第二部分是一些代码示例，解释了Python使用和其他一些语言之间的差异。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:8\nmsgid \"\"\n\"*Please note, we are not a Python developer, and by no means this is \"\n\"Python development guide. All the code examples are used only to present \"\n\"the API available and there are certainly better ways to do it in the \"\n\"proper Python style. If you have any suggestions or have a better code \"\n\"examples I am happy to include them in the guide.*\"\nmsgstr \"*请注意，我们不是Python开发人员，这绝不是Python开发指南。所有代码示例仅用于展示可用的API，当然还有更好的方法以正确的Python风格来实现。如果您有任何建议或有更好的代码示例，我很乐意将它们包含在指南中。*\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:11\nmsgid \"Installation\"\nmsgstr \"安装\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:13\nmsgid \"\"\n\"In short, installation is extremely simple: just copy the file attached \"\n\"to this article to your Tigase installation, to the ``libs/`` directory. \"\n\"Restart the server and you are ready to start scripting and executing \"\n\"Python.\"\nmsgstr \"\"\n\"简而言之，安装非常简单：只需将本文附带的文件复制到您的Tigase安装中，将其复制到 ``libs/`` \"\n\"目录中。重新启动服务器，您就可以开始编写脚本和执行Python。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:15\nmsgid \"\"\n\"In theory the Tigase offers scripting support defined in `JSR-223 \"\n\"<http://www.jcp.org/en/jsr/detail?id=223>`__. You can use any language \"\n\"for which there is such support for JVM. This includes also stand-alone \"\n\"python implementations and the JSR-223 plugins acts just as a bridge. \"\n\"This, however, does not make much sense as you are not able to interact \"\n\"with JVM code (Tigase API). Therefore you need a language which is \"\n\"executed within JVM and can easily exchange data between the main \"\n\"application (Tigase server) and the script.\"\nmsgstr \"\"\n\"理论上，Tigase提供在 `JSR-223 <http://www.jcp.org/en/jsr/detail?id=223>`__ \"\n\"中定义的脚本支持。您可以使用任何支持JVM的语言。这还包括独立的python实现，JSR-223 \"\n\"插件充当桥梁。但是，这没有多大意义，因为您无法与JVM代码（Tigase API）进行交互。因此，您需要一种在 JVM \"\n\"中执行的语言，并且可以轻松地在主应用程序（Tigase服务器）和脚本之间交换数据。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:17\nmsgid \"|lang list no python small|\"\nmsgstr \"|lang list no python small|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:69\nmsgid \"lang list no python small\"\nmsgstr \"lang list no python small\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:19\nmsgid \"\"\n\"The best way to go is to use Jython implementation. It works very well \"\n\"within JVM and more importantly, perfectly integrates with Tigase server.\"\n\" Tigase server is tested with **Jython-2.2.1** and is confirmed to work \"\n\"fine. Version **Jython-2.5.1** is recommended however, and all the \"\n\"examples are executed with this version installed. Please note, \"\n\"*Jython-2.5.0* does not work at all. Both supported versions can be \"\n\"downloaded from the `Jython website \"\n\"<http://wiki.python.org/jython/DownloadInstructions>`__.\"\nmsgstr \"\"\n\"最好的方法是使用Jython实现。它在JVM中运行良好，更重要的是，它与Tigase服务器完美集成。 Tigase服务器使用 \"\n\"Jython-2.2.1 进行了测试，并确认可以正常工作。但是，建议使用 Jython-2.5.1 \"\n\"版本，并且所有示例都在安装此版本的情况下执行。请注意，Jython-2.5.0根本不工作。两个受支持的版本都可以从 Jython网站 下载。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:21\nmsgid \"\"\n\"**Version 2.5.1** is a bit simpler to install. When you download and run \"\n\"the Jython installer, find ``jython.jar`` file in the directory where you\"\n\" installed Jython. Copy the file to the Tigase’s **libs/** directory and \"\n\"all is ready to go. Please note, this is the same file as the one \"\n\"attached to this article for your convenience.\"\nmsgstr \"\"\n\"**版本 2.5.1** 安装起来更简单一些。当您下载并运行Jython安装程序时，在您安装Jython的目录中找到 ``jython.jar``\"\n\" 文件。将文件复制到Tigase的 **libs/** 目录，然后一切准备就绪。请注意，为方便起见，此文件与本文所附文件相同。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:23\nmsgid \"\"\n\"**Version 2.2.1** needs a little bit more work. The first part is the \"\n\"same. It is not, however enough to copy the ``jython.jar`` file. One more\"\n\" file is necessary for the Jython to work with the Tigase server. You \"\n\"have to install JSR-223 engine separately. The binary file has to be \"\n\"unpacked and ``jython-engine.jar`` file needs to be copied to the Tigase \"\n\"``libs/`` directory.\"\nmsgstr \"\"\n\"**版本 2.2.1** 需要更多的工作。第一部分是一样的。然而，复制 ``jython.jar`` \"\n\"文件是不够的。要让Jython与Tigase服务器一起工作，还需要一个文件。您必须单独安装JSR-223引擎。二进制文件必须解压缩，并且需要将 \"\n\"``jython-engine.jar`` 文件复制到Tigase ``libs/`` 目录。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:25\nmsgid \"\"\n\"The best way to check if the Jython is installed correctly and support \"\n\"for Python is enabled, is by trying to submit a new script to the Tigase \"\n\"server. Browser the server service discovery, select \\\"*Session \"\n\"manager*\\\" component and run \\\"*Execute command*\\\" function. A new window\"\n\" should show with a list of all available ad-hoc commands. Select \\\"*New \"\n\"command script*\\\" item and click \\\"*Execute*\\\". Ad-hoc command dialog \"\n\"windows should show up. One of the field is \\\"*Language*\\\" with pull down\"\n\" list of available scripting languages. If \\\"*python*\\\" is on the list it\"\n\" means everything is ok and support for Python is enabled.\"\nmsgstr \"\"\n\"检查Jython是否已正确安装以及是否启用了对Python的支持的最佳方法，即尝试向 Tigase \"\n\"服务器提交新脚本。浏览服务器服务发现，选择\\\"*Session manager*\\\"组件并运行\\\"*Execute \"\n\"command*\\\"功能。此时应该出现一个新窗口，其中包含所有可用的临时命令的列表。选择\\\"*New command script*\\\"项并单击 \"\n\"\\\"*Execute*\\\"。此时应该出现临时命令对话框窗口。其中一个字段是\\\"*Language*\\\"，其中包含可用脚本语言的下拉列表。如果\\\"*python*\\\"在列表中，则表示一切正常并且启用了对Python的支持。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:27\nmsgid \"|lang list with python small|\"\nmsgstr \"|lang list with python small|\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:70\nmsgid \"lang list with python small\"\nmsgstr \"lang list with python small\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:30\nmsgid \"Writing Python Scripts\"\nmsgstr \"写Python脚本\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:32\nmsgid \"\"\n\"Python scripts work in a similar way to Groovy or other languages \"\n\"scripts, except one significant difference. You cannot call \\\"*return*\\\" \"\n\"from the script itself. Hence you cannot simply pass script results by \"\n\"calling \\\"*return*\\\" statement directly from the script.\"\nmsgstr \"\"\n\"Python脚本的工作方式与Groovy或其他语言脚本类似，但有一个显著差异。您不能从脚本本身调用\\\"*return*\\\" \"\n\"。因此，您不能通过直接从脚本调用\\\"*return*\\\"语句来简单地传递脚本结果。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:34\nmsgid \"\"\n\"To overcome the problem, Tigase offers another way to pass script \"\n\"execution results. It checks the value of a special variables on the \"\n\"script completion: “result” and “packet”. By assigning value to one of \"\n\"these variables the Python (or any other language) can pass execution \"\n\"results back to the Tigase server.\"\nmsgstr \"为了克服这个问题，Tigase提供了另一种传递脚本执行结果的方法。它在脚本完成时检查一个特殊变量的值：“result”和“packet”。通过为这些变量之一赋值，Python（或任何其他语言）可以将执行结果传回Tigase服务器。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:36\nmsgid \"\"\n\"``result`` allows to return simple text (or characters String) from the \"\n\"script.\"\nmsgstr \"``result`` 允许从脚本返回简单的文本（或字符串）。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:38\nmsgid \"\"\n\"``packet`` allows to return Packet instance which is send back to the \"\n\"user.\"\nmsgstr \"``packet`` 允许返回发送回给用户的Packet实例。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:40\nmsgid \"The simplest possible Python script may look like this one:\"\nmsgstr \"最简单的Python脚本可能如下所示：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:42\nmsgid \"``result = \\\"Hello world!\\\"``\"\nmsgstr \"``result = \\\"Hello world!\\\"``\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:44\nmsgid \"\"\n\"For instructions how to load and execute the script, please refer to the \"\n\":ref:`introductory article<Scripting-Introduction - Hello-World!>` for \"\n\"scripting in Tigase server. There were some minor changes in Tigase 4.4.0\"\n\" and later versions, so please have a look at the :ref:`article <Tigase-\"\n\"Scripting-Version-4.4.x-Update-for-Administrators>` describing new \"\n\"elements as well.\"\nmsgstr \"\"\n\"有关如何加载和执行脚本的说明，请参阅在Tigase服务器中编写脚本的 \"\n\":ref:`介绍性文章<Scripting-Introduction - Hello-World!>`。 Tigase 4.4.\"\n\"0及更高版本有一些小的变化，所以请查看描述新元素的 :ref:`文章<Tigase-\"\n\"Scripting-Version-4.4.x-Update-for-Administrators>`。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:46\nmsgid \"\"\n\"An example of a more advanced script asks the user for providing required\"\n\" parameters for the actual script execution:\"\nmsgstr \"一个更高级的脚本示例要求用户提供实际脚本执行所需的参数：\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:65\nmsgid \"\"\n\"Except this minor difference, the rest part of scripting in Python for \"\n\"the Tigase administrator commands is the same as all other languages. As \"\n\"all languages can return execution results via these special variables, \"\n\"it could be argued there is no difference at all.\"\nmsgstr \"除了这个微小的差异之外，Tigase管理员命令的Python脚本的其余部分与所有其他语言相同。由于所有语言都可以通过这些特殊变量返回执行结果，因此可以说根本没有区别。\"\n\n#: ../../Tigase_Administration/Using_Tigase/Scripting/Python.inc:67\nmsgid \"\"\n\"In article *\\\"Component Implementation - Lesson 6 - Scripting Support\\\"* \"\n\"in Developer guide, I am going to present the Tigase server API available\"\n\" for scripting framework. My main language is Groovy as it offers the \"\n\"best integration with JVM and Tigase API, however I will try to include \"\n\"Python example code as well.\"\nmsgstr \"\"\n\"在开发人员指南中的 *\\\"组件实现 - 第6课 - 脚本支持\\\"* 文章中，我将介绍可用于脚本框架的Tigase服务器API。我的主要语言是 \"\n\"Groovy，因为它提供了与JVM 和Tigase API的最佳集成，但我也会尝试包含Python示例代码。\"\n\n#~ msgid \"`Tigase Log Guide <#Tigase-Log-Guide>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Debugging Tigase <#Debuging-Tigase>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Basic System Checks <#Basic-System-Checks>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Add and Manage Domains <#Add-and-Manage-Domains-(VHosts)>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Presence Forwarding <#Presence-Forwarding>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Watchdog <#Watchdog>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Runtime Environment Tip <#Tigase-Tip-\"\n#~ \"Checking-the-Runtime-Environment>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Checking Cluster Connections <#Checking-Cluster-Connections>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Best Practices for Connecting to Tigase\"\n#~ \" XMPP server From Web Browser \"\n#~ \"<#Best-Practices-for-Connecting-to-\"\n#~ \"Tigase-XMPP-server-From-Web-Browser>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Scripting Support in Tigase <#Scripting-support-in-Tigase>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Scripting Introduction - Hello World! \"\n#~ \"<#Scripting-Introduction - Hello-World!>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Tigase Scripting Version 4.4.x Update \"\n#~ \"for Administrators <#Tigase-Scripting-\"\n#~ \"Version-4.4.x-Update-for-Administrators>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Tigase and Python Scripting <#Tigase-and-Python>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Configuration Wizards <#tigase3xconfiguration>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"using web-based admin management console - `Admin UI <#usingAdminUI>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"To use any of those ways, you \"\n#~ \"need to be an administrator of the\"\n#~ \" server, which means that you have\"\n#~ \" a XMPP account created on this \"\n#~ \"XMPP server and your account JID \"\n#~ \"is added to `the list of the \"\n#~ \"administrators <#admins>`__ in the Tigase \"\n#~ \"XMPP Server configuration file.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"This method should be used only in\"\n#~ \" extreme cases when `Automatic Registration\"\n#~ \" (recommended) <#AutomaticLicenceRegistration>`__ can’t\"\n#~ \" be used.\"\n#~ msgstr \"\"\n\n#~ msgid \"\"\n#~ \"`Best Practices for Connecting to Tigase\"\n#~ \" XMPP server From Web Browser \"\n#~ \"<#bestWebPrax>`__\"\n#~ msgstr \"\"\n#~ \":ref:`从Web 浏览器连接到Tigase XMPP服务器的最佳实践<Best-\"\n#~ \"Practices-for-Connecting-to-Tigase-XMPP-\"\n#~ \"server-From-Web-Browser>`\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-05-27 12:30-0700\\n\"\n\"PO-Revision-Date: 2022-09-07 17:13+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/about_tigase_xmpp_server/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:2\nmsgid \"About Tigase XMPP Server\"\nmsgstr \"关于 Tigase XMPP 服务器\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:4\nmsgid \"\"\n\"**Tigase XMPP Server** is an **Open Source and Free (AGPLv3)** Java based\"\n\" server. The goals behind its design and implementation of the server \"\n\"are:\"\nmsgstr \"*Tigase XMPP 服务器** 是一个基于 Java的**开源和免费 (AGPLv3)** 的服务器 \"\n\"。其服务器设计和背后实施的目标是：\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:6\nmsgid \"Make the server robust and reliable.\"\nmsgstr \"使服务器稳健可靠。\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:8\nmsgid \"Make the server a secure communication platform.\"\nmsgstr \"使服务器成为一个安全的交流平台。\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:10\nmsgid \"Make a flexible server which can be applied to different use cases.\"\nmsgstr \"做一个可以应用于不同场景的灵活的服务器。\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:12\nmsgid \"\"\n\"Make an extensible server which takes full advantage of XMPP protocol \"\n\"extensibility.\"\nmsgstr \"做一个可以充分利用 XMPP 可扩展性协议的可扩展服务器。\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:14\nmsgid \"Make the server easy to setup and maintain.\"\nmsgstr \"使服务器易于设置和维护。\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:17\nmsgid \"Robust and reliable\"\nmsgstr \"稳固可靠\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:19\nmsgid \"\"\n\"This means that the server can handle many concurrent \"\n\"requests/connections and can run for a long time reliably. The server is \"\n\"designed and implemented to handle millions of simultaneous connections.\"\nmsgstr \"这意味着服务器可以同时处理许多请求/连接，并且可以长时间可靠运行。该服务器的设\"\n\"计和实现是为了同时处理数百万个连接。\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:21\nmsgid \"\"\n\"It is not enough however to design and implement a high load server and \"\n\"hope it will run well. The main focus of the project is put in into \"\n\"testing. Tests are taken so seriously that a dedicated testing framework \"\n\"has been implemented. All server functions are considered as implemented \"\n\"only when they pass a rigorous testing cycle. The testing cycle consists \"\n\"of 3 fundamental tests:\"\nmsgstr \"\"\n\"然而，仅仅设计和实现一个高负载服务器并希望它良好运行是不够的。该项目的重点是\"\n\"投入测试。如此认真勤勉的测试以至于其已经拥有了一个专门的测试框架。所有服务器\"\n\"功能只有在通过严格的测试周期后才被视之为已实现。测试周期包括 3 个基本测试：\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:23\nmsgid \"**Functional tests** - Checking whether the function works at all.\"\nmsgstr \"**功能测试** - 检查功能是否有效。\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:25\nmsgid \"\"\n\"**Performance tests** - Checking whether the function performs well \"\n\"enough.\"\nmsgstr \"**性能测试** - 检查功能是否足够好。\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:27\nmsgid \"\"\n\"**Stability tests** - Checking whether the function behaves well in long \"\n\"term run. It must handle hundreds of requests a second in a several hour \"\n\"server run.\"\nmsgstr \"**稳定性测试** - 检查功能是否在长期运行中表现良好。它必须在服务器运行的几个小\"\n\"时内每秒处理数百个请求。\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:31\nmsgid \"Security\"\nmsgstr \"安全\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:33\nmsgid \"\"\n\"There are a few elements of the security related to XMPP servers: secure \"\n\"data transmissions which is met by the implementation of **SSL** or \"\n\"**TLS** protocol, secure user authorization which is met by the \"\n\"implementation of **DIGEST** or **SASL** user authorization and secure \"\n\"deployment which is met by component architecture.\"\nmsgstr \"\"\n\"与 XMPP 服务器相关的安全性有几个要素：通过 **SSL** 或 **TLS** \"\n\"协议实现的安全数据传输，通过 **DIGEST* 实现的安全用户授权或通过 **SASL** \"\n\"用户授权和组件架构实现的安全部署。\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:35\nmsgid \"\"\n\"**Secure deployment** Tigase software installation does not impact \"\n\"network security. Companies usually have their networks divided into 2 \"\n\"parts: **DMZ** which is partially open to the outside world and the \"\n\"**Private network** which is closed to the outside world.\"\nmsgstr \"\"\n\"*安全部署** Tigase \"\n\"软件安装不会影响网络安全。公司通常将其网络分为两部分：**DMZ** \"\n\"部分对外界开放，**私有网络** 对外界关闭。\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:37\nmsgid \"\"\n\"If the XMPP server is to provide an effective way of communication \"\n\"between company employees regardless if they are in a secure company \"\n\"office or outside (perhaps at a customer site), it needs to accept both \"\n\"internal and external connections. So the natural location for the server\"\n\" deployment is the **DMZ**. However, this solution has some \"\n\"considerations: each company has normally established network users base \"\n\"and integrated authorization mechanisms. However, that information should\"\n\" be stored outside the DMZ to protect internal security, so how to \"\n\"maintain ease of installation and system security?\"\nmsgstr \"\"\n\"如果 XMPP 服务器要在公司员工之间提供一种有效的通信方式，那么无论他们是在安全\"\n\"的公司办公室还是在外部（可能在客户站点），它都需要接受内部和外部连接。所以服\"\n\"务器部署的自然位置是**DMZ**。但是，这种解决方案有一些顾虑：每个公司通常都建立\"\n\"了网络用户基础和集成的授权机制。但是，这些信息应该存储在 DMZ \"\n\"之外以保护内部安全，那么如何保持安装的便利性和系统的安全性呢？\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:39\nmsgid \"\"\n\"**Tigase server** offers a solution for such a case. With it’s component \"\n\"structure, Tigase can be easily deployed on any number machines and from \"\n\"the user’s point of view it is seen as a one logical XMPP server. In this\"\n\" case we can install a Session Manager module in the **private** network,\"\n\" and a Client Connection Manager with Server Connection Manager in the \"\n\"**DMZ**.\"\nmsgstr \"\"\n\"**Tigase 服务器** 为这种情况提供了解决方案。凭借其组件结构，Tigase \"\n\"可以轻松部署在任意数量的机器上，并且从用户的角度来看，它被视为一个逻辑 XMPP \"\n\"服务器。在这种情况下，我们可以在**私人** \"\n\"网络中安装一个会话管理器模块，并在**DMZ** \"\n\"中安装一个带有服务连接管理器的客户端连接管理器。\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:41\nmsgid \"\"\n\"Session Manager connects to **DMZ** and receives all packets from \"\n\"external users. Thus is can securely realize users authorization based on\"\n\" company authorization mechanisms.\"\nmsgstr \"会话管理器连接到 **DMZ** 并接收来自外部用户的所有数据包。从而可以安全地实现基\"\n\"于公司授权机制的用户授权。\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:45\nmsgid \"Flexibility\"\nmsgstr \"灵活性\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:46\nmsgid \"\"\n\"There are many different XMPP server implementations. The most prevalent \"\n\"are:\"\nmsgstr \"有许多不同 XMPP的 服务器实现。最流行的如下：\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:48\nmsgid \"\"\n\"Used as a business communication platform in small and medium companies \"\n\"where the server is not under a heavy load. For such deployments security\"\n\" is a key feature.\"\nmsgstr \"用于服务器负载不重的中小型公司的业务交流平台。对于此类部署，安全性是一个关键\"\n\"特性。\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:50\nmsgid \"\"\n\"For huge community websites or internet portal servers is, on the other \"\n\"hand, usually under very heavy load and has to support thousands or \"\n\"millions of simultaneous connections. For such a deployment we need a \"\n\"different level of security as most of the service is open to the public.\"\nmsgstr \"\"\n\"另一方面，对于大型社区网站或互联网门户服务器而言，通常其负载非常重，并且必须\"\n\"同时支持数千或数百万个连接。对于这样的部署，因为大多数服务都是对公众开放的，\"\n\"所以我们需要不同级别的安全性。\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:52\nmsgid \"\"\n\"For very small community deployments or for small home networks the key \"\n\"factor is ease to deploy and maintain.\"\nmsgstr \"对于非常小的社区部署或小型家庭网络，关键因素是易于部署和维护。\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:54\nmsgid \"\"\n\"Architecture based on components provides the ability to run selected \"\n\"modules on separate machines so the server can be easily applied in any \"\n\"scenario.\"\nmsgstr \"基于组件的架构让服务器在不同机器上可以运行选定模块，因此其可以轻松应用于任何\"\n\"场景。\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:56\nmsgid \"\"\n\"For simple installation the server generates a config file which can be \"\n\"used straight away with very few modifications or none at all. For \"\n\"complex deployments though, you can tweak configurations to your needs \"\n\"and setup XMPP server on as many physical machines as you need.\"\nmsgstr \"\"\n\"对于简单的安装，服务器会生成一个配置文件，此配置文件只需少许修改或根本不需要\"\n\"修改即可立即使用。但是，对于复杂的部署，\"\n\"您可以根据需要调整配置并在任意你需要数量的物理机器上设置 XMPP 服务器。\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:59\nmsgid \"Extensibility\"\nmsgstr \"可扩展性\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:61\nmsgid \"\"\n\"The world changes all the time as does user’s needs. The XMPP protocol \"\n\"has been designed to be extensible to make it easy to add new features \"\n\"and apply it to those different user’s needs. As a result, XMPP is a very\"\n\" effective platform not only for sending messages to other users, it can \"\n\"also be extended for sending instant notifications about events, a useful\"\n\" platform for on-line customer service, voice communication, and other \"\n\"cases where sending information instantly to other people is needed.\"\nmsgstr \"\"\n\"世界一直在变化，用户的需求也在变化。被设计为可扩展的 XMPP \"\n\"协议使其可以轻松添加新功能并使其满足不同用户的需求。因此，XMPP 是一个非常有效\"\n\"的平台，不仅可以用于向其他用户发送消息，还可以用于发送事件的即时通知，是在线\"\n\"客户服务，语音通话和当需要时可发送即时信息给他人的有用平台。\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:63\nmsgid \"\"\n\"**Tigase server** has been designed to be extensible using a modular \"\n\"architecture. You can easily replace components which do not fulfill your\"\n\" requirements with others better fitting your needs. But that is not all,\"\n\" another factor of extensibility is how easy is to replace or add new \"\n\"extensions. A great deal of focus has been put into the server design API\"\n\" to make it easy for other software developers to create extensions and \"\n\"implement new features.\"\nmsgstr \"\"\n\"**Tigase 服务器** 设计为可使用模块化架构进行扩展。您可以轻松地将不符合您要求\"\n\"的组件替换成更适合您需求的其他组件。不仅如此，可扩展性的另一个因素是替换或添\"\n\"加新扩展非常容易。大量精力投入服务器设计 API \"\n\"，以使其他软件开发人员可以轻松创建扩展并实现新功能。\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:66\nmsgid \"Ease of Use\"\nmsgstr \"使用方便\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:68\nmsgid \"\"\n\"Complex computer networks consisting of many servers with different \"\n\"services are hard to maintain. This requires employing professional staff\"\n\" to operate and maintain the network.\"\nmsgstr \"由许多具有不同服务的服务器组成的复杂计算机网络很难维护。这就需要雇佣专业人员\"\n\"来操作和维护网络。\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:70\nmsgid \"\"\n\"Not all networks are so complex however, most small companies have just a\"\n\" few servers for their needs with services like e-mail and a HTTP server.\"\n\" They might want to add an XMPP server to the collection of their \"\n\"services and don’t want to dedicate resources on setup and maintenance. \"\n\"For such users our default configuration is exactly what they need. If \"\n\"the operating system on the server is well configured, then Tigase should\"\n\" automatically pickup the correct hostname and be ready to operate \"\n\"immediately.\"\nmsgstr \"\"\n\"然而，并非所有网络都如此复杂，大多数小公司只需要几台服务器来满足他们的需求，\"\n\"比如电子邮件和 HTTP 服务器。他们可能希望将 XMPP 服务器添加到他们的服务集合中\"\n\"，但是并不想投入资源来专用于设置和维护。对于此类用户，我们的默认配置正是他们\"\n\"所需要的。如果服务器上的操作系统配置良好，那么 Tigase \"\n\"应该会自动获取正确的主机名并准备好立即运行。\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:72\nmsgid \"\"\n\"Tigase server is designed and implemented to allow dynamic \"\n\"reconfiguration during runtime so there is no need to restart the server \"\n\"each time you want to change configuration settings.\"\nmsgstr \"Tigase 服务器的设计和实现允许在运行时动态重新配置，因此每次您想要更改配置的设\"\n\"置时都无需重启服务器。\"\n\n#: ../../Tigase_Administration/XMPP_Server/About_Tigase_XMPP_Server.rst:74\nmsgid \"\"\n\"There are also interfaces and handlers available to make it easy to \"\n\"implement a web user interface for server monitoring and configuring.\"\nmsgstr \"还有一些接口和处理程序可轻松用于实现由服务器来监视和配置 Web 用户界面。\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:2\nmsgid \"XMPP Supported Extensions\"\nmsgstr \"XMPP 支持的扩展\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:4\nmsgid \"\"\n\"Based on `XEP-0387: XMPP Compliance Suites 2018 \"\n\"<https://xmpp.org/extensions/xep-0387.html>`__\"\nmsgstr \"\"\n\"基于 `XEP-0387: XMPP Compliance Suites 2018 <https://xmpp.org/extensions/\"\n\"xep-0387.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:8\nmsgid \"Core Compliance Suite\"\nmsgstr \"核心合规套件\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:10\nmsgid \"Table 1.Core Compliance Suite\"\nmsgstr \"Table 1.核心合规套件\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:10\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:58\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:14\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:39\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:54\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:89\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:106\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:301\nmsgid \"Support\"\nmsgstr \"支持\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:14\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:39\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:54\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:89\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:106\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:301\nmsgid \"Specification\"\nmsgstr \"规格\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:10\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:58\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:14\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:39\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:54\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:89\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:106\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:301\nmsgid \"Name\"\nmsgstr \"名称\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:10\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:58\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:14\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:39\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:54\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:89\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:106\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:301\nmsgid \"Comment\"\nmsgstr \"评论\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:14\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:16\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:18\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:20\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:22\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:24\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:26\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:28\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:30\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:32\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:34\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:36\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:38\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:40\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:42\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:44\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:46\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:48\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:60\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:62\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:64\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:66\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:68\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:70\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:72\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:74\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:76\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:16\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:20\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:22\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:24\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:26\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:28\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:30\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:41\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:43\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:45\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:56\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:58\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:60\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:62\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:64\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:66\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:68\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:70\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:72\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:74\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:76\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:78\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:80\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:91\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:93\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:95\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:97\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:108\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:110\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:112\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:114\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:116\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:118\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:120\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:122\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:124\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:126\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:128\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:130\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:132\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:134\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:136\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:138\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:140\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:142\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:144\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:146\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:148\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:150\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:152\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:154\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:156\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:158\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:160\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:162\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:164\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:166\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:168\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:170\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:172\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:174\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:176\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:178\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:180\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:182\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:186\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:188\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:190\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:192\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:194\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:196\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:198\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:200\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:202\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:204\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:206\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:208\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:210\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:212\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:214\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:216\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:218\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:220\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:222\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:224\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:226\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:228\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:230\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:232\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:234\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:236\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:238\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:240\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:242\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:244\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:246\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:248\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:250\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:252\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:254\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:256\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:258\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:260\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:262\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:264\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:266\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:268\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:270\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:272\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:274\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:276\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:278\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:280\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:282\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:284\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:286\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:288\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:290\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:292\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:294\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:303\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:305\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:309\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:311\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:313\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:315\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:317\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:319\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:321\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:323\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:325\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:327\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:329\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:331\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:333\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:335\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:337\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:339\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:341\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:343\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:345\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:347\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:349\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:351\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:353\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:355\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:357\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:359\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:361\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:363\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:365\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:367\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:369\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:371\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:373\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:375\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:377\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:379\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:381\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:383\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:385\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:387\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:389\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:391\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:393\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:395\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:397\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:399\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:401\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:403\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:405\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:407\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:409\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:411\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:413\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:415\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:417\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:419\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:421\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:423\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:425\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:427\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:429\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:431\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:433\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:435\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:437\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:439\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:441\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:443\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:445\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:447\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:449\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:451\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:453\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:455\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:457\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:459\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:461\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:463\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:465\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:467\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:469\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:471\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:473\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:475\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:477\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:479\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:481\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:483\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:485\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:487\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:489\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:491\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:493\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:495\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:497\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:499\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:501\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:503\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:505\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:507\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:509\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:511\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:513\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:515\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:517\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:519\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:521\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:523\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:525\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:527\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:529\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:531\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:533\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:535\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:537\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:539\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:541\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:543\nmsgid \"✓\"\nmsgstr \"✓\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:16\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:56\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:303\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:305\nmsgid \"`RFC6120 <https://tools.ietf.org/html/rfc6120>`__\"\nmsgstr \"`RFC6120 <https://tools.ietf.org/html/rfc6120>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:16\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:303\nmsgid \"Extensible Messaging and Presence Protocol (XMPP): Core\"\nmsgstr \"可扩展消息传递和存在协议 (XMPP)：核心\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:18\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:307\nmsgid \"⍻\"\nmsgstr \"⍻\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:18\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:307\nmsgid \"`RFC7622 <https://tools.ietf.org/html/rfc7622>`__\"\nmsgstr \"`RFC7622 <https://tools.ietf.org/html/rfc7622>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:18\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:307\nmsgid \"Extensible Messaging and Presence Protocol (XMPP): Address Format\"\nmsgstr \"可扩展消息传递和存在协议 (XMPP)：地址格式\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:18\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:307\nmsgid \"We support previous version of the specification: RFC6122\"\nmsgstr \"我们支持旧版本的规范：RFC6122\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:20\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:313\nmsgid \"`RFC7590 <https://tools.ietf.org/html/rfc7590>`__\"\nmsgstr \"`RFC7590 <https://tools.ietf.org/html/rfc7590>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:20\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:313\nmsgid \"\"\n\"Use of Transport Layer Security (TLS) in the Extensible Messaging and \"\n\"Presence Protocol (XMPP)\"\nmsgstr \"在可扩展消息传递和存在协议 (XMPP) 中使用传输层安全协定 (TLS)\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:22\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:541\nmsgid \"`XEP-0368 <https://xmpp.org/extensions/xep-0368.html>`__\"\nmsgstr \"`XEP-0368 <https://xmpp.org/extensions/xep-0368.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:22\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:541\nmsgid \"SRV records for XMPP over TLS\"\nmsgstr \"XMPP在TLS 上的 SRV 记录\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:22\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:541\nmsgid \"Requires adding DNS entries pointing to port 5223\"\nmsgstr \"要求添加指向端口 5223 的 DNS 条目\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:24\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:329\nmsgid \"`XEP-0030 <https://xmpp.org/extensions/xep-0030.html>`__\"\nmsgstr \"`XEP-0030 <https://xmpp.org/extensions/xep-0030.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:24\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:329\nmsgid \"Service Discovery\"\nmsgstr \"服务发现\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:26\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:391\nmsgid \"`XEP-0115 <https://xmpp.org/extensions/xep-0115.html>`__\"\nmsgstr \"`XEP-0115 <https://xmpp.org/extensions/xep-0115.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:26\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:391\nmsgid \"Entity Capabilities\"\nmsgstr \"实体能力\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:28\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:389\nmsgid \"`XEP-0114 <https://xmpp.org/extensions/xep-0114.html>`__\"\nmsgstr \"`XEP-0114 <https://xmpp.org/extensions/xep-0114.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:28\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:389\nmsgid \"Jabber Component Protocol\"\nmsgstr \"Jabber 组件协议\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:30\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:429\nmsgid \"`XEP-0163 <https://xmpp.org/extensions/xep-0163.html>`__\"\nmsgstr \"`XEP-0163 <https://xmpp.org/extensions/xep-0163.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:30\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:429\nmsgid \"Personal Eventing Protocol\"\nmsgstr \"个人事件协议\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:34\nmsgid \"Web Compliance Suite\"\nmsgstr \"网络合规套件\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:36\nmsgid \"Table 2.Web Compliance Suite\"\nmsgstr \"Table 2.网络合规套件\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:41\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:91\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:309\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:311\nmsgid \"`RFC7395 <https://tools.ietf.org/html/rfc7395>`__\"\nmsgstr \"`RFC7395 <https://tools.ietf.org/html/rfc7395>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:41\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:91\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:309\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:311\nmsgid \"\"\n\"An Extensible Messaging and Presence Protocol (XMPP) Subprotocol for \"\n\"WebSocket\"\nmsgstr \"WebSocket 的可扩展消息传递和存在协议 (XMPP) 子协议\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:43\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:477\nmsgid \"`XEP-0206 <https://xmpp.org/extensions/xep-0206.html>`__\"\nmsgstr \"`XEP-0206 <https://xmpp.org/extensions/xep-0206.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:43\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:477\nmsgid \"XMPP Over BOSH\"\nmsgstr \"BOSH 上的 XMPP\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:45\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:395\nmsgid \"`XEP-0124 <https://xmpp.org/extensions/xep-0124.html>`__\"\nmsgstr \"`XEP-0124 <https://xmpp.org/extensions/xep-0124.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:45\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:395\nmsgid \"Bidirectional-streams Over Synchronous HTTP (BOSH)\"\nmsgstr \"同步 HTTP (BOSH)上的双向流\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:49\nmsgid \"IM Compliance Suite\"\nmsgstr \"IM 合规套件\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:51\nmsgid \"Table 3.Web Compliance Suite\"\nmsgstr \"Table 3.网络合规套件\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:56\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:305\nmsgid \"\"\n\"Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and \"\n\"Presence\"\nmsgstr \"可扩展消息传递和存在协议 (XMPP)：即时消息传递和存在\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:58\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:369\nmsgid \"`XEP-0084 <https://xmpp.org/extensions/xep-0084.html>`__\"\nmsgstr \"`XEP-0084 <https://xmpp.org/extensions/xep-0084.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:58\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:369\nmsgid \"User Avatar\"\nmsgstr \"用户头像\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:60\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:419\nmsgid \"`XEP-0153 <https://xmpp.org/extensions/xep-0153.html>`__\"\nmsgstr \"`XEP-0153 <https://xmpp.org/extensions/xep-0153.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:60\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:419\nmsgid \"vCard-Based Avatars\"\nmsgstr \"基于 vCard 的头像\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:62\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:341\nmsgid \"`XEP-0054 <https://xmpp.org/extensions/xep-0054.html>`__\"\nmsgstr \"`XEP-0054 <https://xmpp.org/extensions/xep-0054.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:62\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:341\nmsgid \"vcard-temp\"\nmsgstr \"电子名片温度\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:64\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:515\nmsgid \"`XEP-0280 <https://xmpp.org/extensions/xep-0280.html>`__\"\nmsgstr \"`XEP-0280 <https://xmpp.org/extensions/xep-0280.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:64\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:515\nmsgid \"Message Carbons\"\nmsgstr \"消息碳\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:66\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:463\nmsgid \"`XEP-0191 <https://xmpp.org/extensions/xep-0191.html>`__\"\nmsgstr \"`XEP-0191 <https://xmpp.org/extensions/xep-0191.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:66\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:463\nmsgid \"Blocking Command\"\nmsgstr \"阻断命令\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:68\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:331\nmsgid \"`XEP-0045 <https://xmpp.org/extensions/xep-0045.html>`__\"\nmsgstr \"`XEP-0045 <https://xmpp.org/extensions/xep-0045.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:68\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:331\nmsgid \"Multi-User Chat\"\nmsgstr \"多用户聊天\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:70\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:501\nmsgid \"`XEP-0249 <https://xmpp.org/extensions/xep-0249.html>`__\"\nmsgstr \"`XEP-0249 <https://xmpp.org/extensions/xep-0249.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:70\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:501\nmsgid \"Direct MUC Invitations\"\nmsgstr \"直接 MUC 邀请\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:72\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:335\nmsgid \"`XEP-0048 <https://xmpp.org/extensions/xep-0048.html>`__\"\nmsgstr \"`XEP-0048 <https://xmpp.org/extensions/xep-0048.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:72\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:335\nmsgid \"Bookmarks\"\nmsgstr \"书签\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:74\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:483\nmsgid \"`XEP-0223 <https://xmpp.org/extensions/xep-0223.html>`__\"\nmsgstr \"`XEP-0223 <https://xmpp.org/extensions/xep-0223.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:74\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:483\nmsgid \"Persistent Storage of Private Data via PubSub\"\nmsgstr \"通过 PubSub 持久存储私有数据\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:76\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:337\nmsgid \"`XEP-0049 <https://xmpp.org/extensions/xep-0049.html>`__\"\nmsgstr \"`XEP-0049 <https://xmpp.org/extensions/xep-0049.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:76\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:337\nmsgid \"Private XML Storage\"\nmsgstr \"私人XML 存储\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:78\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:93\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:465\nmsgid \"`XEP-0198 <https://xmpp.org/extensions/xep-0198.html>`__\"\nmsgstr \"`XEP-0198 <https://xmpp.org/extensions/xep-0198.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:78\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:93\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:465\nmsgid \"Stream Management\"\nmsgstr \"流管理\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:78\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:93\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:465\nmsgid \"Both ``Session Resumption`` and ``Stanza Acknowledgements``\"\nmsgstr \"``Session Resumption`` 和``Stanza Acknowledgements``\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:80\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:523\nmsgid \"`XEP-0313 <https://xmpp.org/extensions/xep-0313.html>`__\"\nmsgstr \"`XEP-0313 <https://xmpp.org/extensions/xep-0313.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:80\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:523\nmsgid \"Message Archive Management\"\nmsgstr \"消息归档管理\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:84\nmsgid \"Mobile Compliance Suite\"\nmsgstr \"移动合规套件\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:86\nmsgid \"Table 4.Web Compliance Suite\"\nmsgstr \"Table 4.网络合规套件\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:95\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:535\nmsgid \"`XEP-0352 <https://xmpp.org/extensions/xep-0352.html>`__\"\nmsgstr \"`XEP-0352 <https://xmpp.org/extensions/xep-0352.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:95\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:535\nmsgid \"Client State Indication\"\nmsgstr \"客户端状态指示\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:97\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:537\nmsgid \"`XEP-0357 <https://xmpp.org/extensions/xep-0357.html>`__\"\nmsgstr \"`XEP-0357 <https://xmpp.org/extensions/xep-0357.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:97\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:537\nmsgid \"Push Notifications\"\nmsgstr \"推送通知\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:101\nmsgid \"Non-Compliance Suite Extensions\"\nmsgstr \"不合规套件扩展\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:103\nmsgid \"Table 5.Core Compliance Suite\"\nmsgstr \"Table 5.核心合规套件\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:108\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:315\nmsgid \"`XEP-0004 <https://xmpp.org/extensions/xep-0004.html>`__\"\nmsgstr \"`XEP-0004 <https://xmpp.org/extensions/xep-0004.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:108\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:315\nmsgid \"Data Forms\"\nmsgstr \"数据表单\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:110\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:317\nmsgid \"`XEP-0008 <https://xmpp.org/extensions/xep-0004.html>`__\"\nmsgstr \"`XEP-0008 <https://xmpp.org/extensions/xep-0004.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:110\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:317\nmsgid \"IQ-Based Avatars\"\nmsgstr \"基于 IQ 的头像\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:112\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:319\nmsgid \"`XEP-0012 <https://xmpp.org/extensions/xep-0012.html>`__\"\nmsgstr \"`XEP-0012 <https://xmpp.org/extensions/xep-0012.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:112\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:319\nmsgid \"Last Activity\"\nmsgstr \"最后的活动\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:114\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:321\nmsgid \"`XEP-0013 <https://xmpp.org/extensions/xep-0013.html>`__\"\nmsgstr \"`XEP-0013 <https://xmpp.org/extensions/xep-0013.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:114\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:321\nmsgid \"Flexible Offline Message Retrieval\"\nmsgstr \"灵活的离线消息检索\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:116\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:323\nmsgid \"`XEP-0016 <https://xmpp.org/extensions/xep-0016.html>`__\"\nmsgstr \"`XEP-0016 <https://xmpp.org/extensions/xep-0016.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:116\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:323\nmsgid \"Privacy Lists\"\nmsgstr \"隐私列表\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:118\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:325\nmsgid \"`XEP-0020 <https://xmpp.org/extensions/xep-0020.html>`__\"\nmsgstr \"`XEP-0020 <https://xmpp.org/extensions/xep-0020.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:118\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:325\nmsgid \"Feature Negotiation\"\nmsgstr \"功能协商\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:120\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:327\nmsgid \"`XEP-0022 <https://xmpp.org/extensions/xep-0022.html>`__\"\nmsgstr \"`XEP-0022 <https://xmpp.org/extensions/xep-0022.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:120\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:327\nmsgid \"Message Events\"\nmsgstr \"消息事件\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:122\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:333\nmsgid \"`XEP-0047 <https://xmpp.org/extensions/xep-0047.html>`__\"\nmsgstr \"`XEP-0047 <https://xmpp.org/extensions/xep-0047.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:122\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:333\nmsgid \"In-Band Bytestreams\"\nmsgstr \"带内字节流\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:124\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:339\nmsgid \"`XEP-0050 <https://xmpp.org/extensions/xep-0050.html>`__\"\nmsgstr \"`XEP-0050 <https://xmpp.org/extensions/xep-0050.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:124\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:339\nmsgid \"Ad-Hoc Commands\"\nmsgstr \"临时命令\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:126\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:343\nmsgid \"`XEP-0059 <https://xmpp.org/extensions/xep-0059.html>`__\"\nmsgstr \"`XEP-0059 <https://xmpp.org/extensions/xep-0059.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:126\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:343\nmsgid \"Result Set Management\"\nmsgstr \"结果集管理\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:128\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:345\nmsgid \"`XEP-0060 <https://xmpp.org/extensions/xep-0060.html>`__\"\nmsgstr \"`XEP-0060 <https://xmpp.org/extensions/xep-0060.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:128\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:345\nmsgid \"Publish-Subscribe\"\nmsgstr \"发布-订阅\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:130\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:347\nmsgid \"`XEP-0065 <https://xmpp.org/extensions/xep-0065.html>`__\"\nmsgstr \"`XEP-0065 <https://xmpp.org/extensions/xep-0065.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:130\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:347\nmsgid \"SOCKS5 Bytestreams\"\nmsgstr \"SOCKS5 字节流\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:132\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:349\nmsgid \"`XEP-0066 <https://xmpp.org/extensions/xep-0066.html>`__\"\nmsgstr \"`XEP-0066 <https://xmpp.org/extensions/xep-0066.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:132\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:349\nmsgid \"Out of Band Data\"\nmsgstr \"带外数据\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:134\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:351\nmsgid \"`XEP-0068 <https://xmpp.org/extensions/xep-0068.html>`__\"\nmsgstr \"`XEP-0068 <https://xmpp.org/extensions/xep-0068.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:134\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:351\nmsgid \"Field Standardization for Data Forms\"\nmsgstr \"数据表单的字段标准化\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:136\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:353\nmsgid \"`XEP-0071 <https://xmpp.org/extensions/xep-0071.html>`__\"\nmsgstr \"`XEP-0071 <https://xmpp.org/extensions/xep-0071.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:136\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:353\nmsgid \"XHTML-IM\"\nmsgstr \"XHTML-IM\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:138\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:355\nmsgid \"`XEP-0072 <https://xmpp.org/extensions/xep-0072.html>`__\"\nmsgstr \"`XEP-0072 <https://xmpp.org/extensions/xep-0072.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:138\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:355\nmsgid \"SOAP Over XMPP\"\nmsgstr \"XMPP 上的 SOAP\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:140\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:357\nmsgid \"`XEP-0077 <https://xmpp.org/extensions/xep-0077.html>`__\"\nmsgstr \"`XEP-0077 <https://xmpp.org/extensions/xep-0077.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:140\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:357\nmsgid \"In-Band Registration\"\nmsgstr \"带内注册\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:142\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:359\nmsgid \"`XEP-0078 <https://xmpp.org/extensions/xep-0078.html>`__\"\nmsgstr \"`XEP-0078 <https://xmpp.org/extensions/xep-0078.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:142\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:359\nmsgid \"Non-SASL Authentication\"\nmsgstr \"非 SASL 身份验证\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:144\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:361\nmsgid \"`XEP-0079 <https://xmpp.org/extensions/xep-0079.html>`__\"\nmsgstr \"`XEP-0079 <https://xmpp.org/extensions/xep-0079.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:144\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:361\nmsgid \"Advanced Message Processing\"\nmsgstr \"高级消息处理\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:146\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:363\nmsgid \"`XEP-0080 <https://xmpp.org/extensions/xep-0080.html>`__\"\nmsgstr \"`XEP-0080 <https://xmpp.org/extensions/xep-0080.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:146\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:363\nmsgid \"User Location\"\nmsgstr \"用户位置\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:148\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:365\nmsgid \"`XEP-0082 <https://xmpp.org/extensions/xep-0082.html>`__\"\nmsgstr \"`XEP-0082 <https://xmpp.org/extensions/xep-0082.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:148\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:365\nmsgid \"XMPP Date and Time Profiles\"\nmsgstr \"XMPP 日期和时序\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:150\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:367\nmsgid \"`XEP-0083 <https://xmpp.org/extensions/xep-0083.html>`__\"\nmsgstr \"`XEP-0083 <https://xmpp.org/extensions/xep-0083.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:150\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:367\nmsgid \"Nested Roster Groups\"\nmsgstr \"嵌套名册组\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:152\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:371\nmsgid \"`XEP-0085 <https://xmpp.org/extensions/xep-0085.html>`__\"\nmsgstr \"`XEP-0085 <https://xmpp.org/extensions/xep-0085.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:152\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:371\nmsgid \"Chat State Notifications\"\nmsgstr \"聊天状态通知\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:154\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:373\nmsgid \"`XEP-0086 <https://xmpp.org/extensions/xep-0086.html>`__\"\nmsgstr \"`XEP-0086 <https://xmpp.org/extensions/xep-0086.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:154\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:373\nmsgid \"Error Condition Mappings\"\nmsgstr \"错误条件映射\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:156\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:375\nmsgid \"`XEP-0091 <https://xmpp.org/extensions/xep-0091.html>`__\"\nmsgstr \"`XEP-0091 <https://xmpp.org/extensions/xep-0091.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:156\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:375\nmsgid \"Legacy Delayed Delivery\"\nmsgstr \"旧版延迟交付\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:158\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:377\nmsgid \"`XEP-0092 <https://xmpp.org/extensions/xep-0092.html>`__\"\nmsgstr \"`XEP-0092 <https://xmpp.org/extensions/xep-0092.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:158\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:377\nmsgid \"Software Version\"\nmsgstr \"软件版本\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:160\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:379\nmsgid \"`XEP-0096 <https://xmpp.org/extensions/xep-0096.html>`__\"\nmsgstr \"`XEP-0096 <https://xmpp.org/extensions/xep-0096.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:160\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:379\nmsgid \"File Transfer\"\nmsgstr \"文件传输\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:162\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:381\nmsgid \"`XEP-0100 <https://xmpp.org/extensions/xep-0100.html>`__\"\nmsgstr \"`XEP-0100 <https://xmpp.org/extensions/xep-0100.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:162\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:381\nmsgid \"Gateway Interaction\"\nmsgstr \"网关交互\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:164\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:383\nmsgid \"`XEP-0106 <https://xmpp.org/extensions/xep-0106.html>`__\"\nmsgstr \"`XEP-0106 <https://xmpp.org/extensions/xep-0106.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:164\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:383\nmsgid \"JID Escaping\"\nmsgstr \"JID 逃逸\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:166\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:385\nmsgid \"`XEP-0107 <https://xmpp.org/extensions/xep-0107.html>`__\"\nmsgstr \"`XEP-0107 <https://xmpp.org/extensions/xep-0107.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:166\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:385\nmsgid \"User Mood\"\nmsgstr \"用户心情\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:166\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:168\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:170\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:385\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:387\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:393\nmsgid \"Server support via ``Personal Eventing Protocol (XEP-0163)``\"\nmsgstr \"通过``Personal Eventing Protocol (XEP-0163)``支持服务器\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:168\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:387\nmsgid \"`XEP-0108 <https://xmpp.org/extensions/xep-0108.html>`__\"\nmsgstr \"`XEP-0108 <https://xmpp.org/extensions/xep-0108.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:168\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:387\nmsgid \"User Activity\"\nmsgstr \"用户活动\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:170\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:393\nmsgid \"`XEP-0118 <https://xmpp.org/extensions/xep-0118.html>`__\"\nmsgstr \"`XEP-0118 <https://xmpp.org/extensions/xep-0118.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:170\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:393\nmsgid \"User Tune\"\nmsgstr \"用户调谐\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:172\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:399\nmsgid \"`XEP-0127 <https://xmpp.org/extensions/xep-0127.html>`__\"\nmsgstr \"`XEP-0127 <https://xmpp.org/extensions/xep-0127.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:172\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:399\nmsgid \"Common Alerting Protocol (CAP) Over XMPP\"\nmsgstr \"XMPP 上的通用警报协议 (CAP)\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:174\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:397\nmsgid \"`XEP-0128 <https://xmpp.org/extensions/xep-0128.html>`__\"\nmsgstr \"`XEP-0128 <https://xmpp.org/extensions/xep-0128.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:174\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:397\nmsgid \"Service Discovery Extensions\"\nmsgstr \"服务发现扩展\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:176\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:401\nmsgid \"`XEP-0131 <https://xmpp.org/extensions/xep-0131.html>`__\"\nmsgstr \"`XEP-0131 <https://xmpp.org/extensions/xep-0131.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:176\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:401\nmsgid \"Stanza Headers and Internet Metadata (SHIM)\"\nmsgstr \"节标题和 Internet 元数据 (SHIM)\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:178\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:403\nmsgid \"`XEP-0133 <https://xmpp.org/extensions/xep-0133.html>`__\"\nmsgstr \"`XEP-0133 <https://xmpp.org/extensions/xep-0133.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:178\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:403\nmsgid \"Service Administration\"\nmsgstr \"服务管理\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:180\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:405\nmsgid \"`XEP-0136 <https://xmpp.org/extensions/xep-0136.html>`__\"\nmsgstr \"`XEP-0136 <https://xmpp.org/extensions/xep-0136.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:180\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:405\nmsgid \"Message Archiving\"\nmsgstr \"消息归档\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:182\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:407\nmsgid \"`XEP-0141 <https://xmpp.org/extensions/xep-0141.html>`__\"\nmsgstr \"`XEP-0141 <https://xmpp.org/extensions/xep-0141.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:182\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:407\nmsgid \"Data Forms Layout\"\nmsgstr \"数据表单布局\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:12\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:184\nmsgid \"✓ [#]_\"\nmsgstr \"✓ [#]_\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:184\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:409\nmsgid \"`XEP-0142 <https://xmpp.org/extensions/xep-0142.html>`__\"\nmsgstr \"`XEP-0142 <https://xmpp.org/extensions/xep-0142.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:184\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:409\nmsgid \"Workgroup Queues\"\nmsgstr \"工作组队列\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:186\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:411\nmsgid \"`XEP-0144 <https://xmpp.org/extensions/xep-0144.html>`__\"\nmsgstr \"`XEP-0144 <https://xmpp.org/extensions/xep-0144.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:186\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:411\nmsgid \"Roster Item Exchange\"\nmsgstr \"名册项目交换\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:188\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:413\nmsgid \"`XEP-0145 <https://xmpp.org/extensions/xep-0145.html>`__\"\nmsgstr \"`XEP-0145 <https://xmpp.org/extensions/xep-0145.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:188\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:413\nmsgid \"Annotations\"\nmsgstr \"注释\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:190\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:415\nmsgid \"`XEP-0146 <https://xmpp.org/extensions/xep-0146.html>`__\"\nmsgstr \"`XEP-0146 <https://xmpp.org/extensions/xep-0146.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:190\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:415\nmsgid \"Remote Controlling Clients\"\nmsgstr \"远程控制客户端\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:192\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:417\nmsgid \"`XEP-0152 <https://xmpp.org/extensions/xep-0152.html>`__\"\nmsgstr \"`XEP-0152 <https://xmpp.org/extensions/xep-0152.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:192\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:417\nmsgid \"Reachability Addresses\"\nmsgstr \"可达地址\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:194\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:421\nmsgid \"`XEP-0155 <https://xmpp.org/extensions/xep-0155.html>`__\"\nmsgstr \"`XEP-0155 <https://xmpp.org/extensions/xep-0155.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:194\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:421\nmsgid \"Stanza Session Negotiation\"\nmsgstr \"节会话协商\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:196\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:423\nmsgid \"`XEP-0156 <https://xmpp.org/extensions/xep-0156.html>`__\"\nmsgstr \"`XEP-0156 <https://xmpp.org/extensions/xep-0156.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:196\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:423\nmsgid \"Discovering Alternative XMPP Connection Methods\"\nmsgstr \"发现可替代 的XMPP 连接方法\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:196\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:423\nmsgid \"Uses DNS records, so will work with Tigase XMPP Server\"\nmsgstr \"使用 DNS 记录，这样其可以与 Tigase XMPP 服务器一起使用\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:198\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:425\nmsgid \"`XEP-0157 <https://xmpp.org/extensions/xep-0157.html>`__\"\nmsgstr \"`XEP-0157 <https://xmpp.org/extensions/xep-0157.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:198\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:425\nmsgid \"Contact Addresses for XMPP Services\"\nmsgstr \"XMPP 服务的联系地址\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:200\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:427\nmsgid \"`XEP-0160 <https://xmpp.org/extensions/xep-0160.html>`__\"\nmsgstr \"`XEP-0160 <https://xmpp.org/extensions/xep-0160.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:200\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:427\nmsgid \"Best Practices for Handling Offline Messages\"\nmsgstr \"处理离线消息的最佳实践\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:202\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:431\nmsgid \"`XEP-0166 <https://xmpp.org/extensions/xep-0166.html>`__\"\nmsgstr \"`XEP-0166 <https://xmpp.org/extensions/xep-0166.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:202\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:431\nmsgid \"Jingle\"\nmsgstr \"Jingle\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:204\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:433\nmsgid \"`XEP-0167 <https://xmpp.org/extensions/xep-0167.html>`__\"\nmsgstr \"`XEP-0167 <https://xmpp.org/extensions/xep-0167.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:204\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:433\nmsgid \"Jingle RTP Sessions\"\nmsgstr \"Jingle RTP 会话\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:206\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:435\nmsgid \"`XEP-0170 <https://xmpp.org/extensions/xep-0170.html>`__\"\nmsgstr \"`XEP-0170 <https://xmpp.org/extensions/xep-0170.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:206\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:435\nmsgid \"Recommended Order of Stream Feature Negotiation\"\nmsgstr \"流特征协商的推荐顺序\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:208\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:437\nmsgid \"`XEP-0171 <https://xmpp.org/extensions/xep-0171.html>`__\"\nmsgstr \"`XEP-0171 <https://xmpp.org/extensions/xep-0171.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:208\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:437\nmsgid \"Language Translation\"\nmsgstr \"语言翻译\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:210\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:439\nmsgid \"`XEP-0172 <https://xmpp.org/extensions/xep-0172.html>`__\"\nmsgstr \"`XEP-0172 <https://xmpp.org/extensions/xep-0172.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:210\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:439\nmsgid \"User Nickname\"\nmsgstr \"用户昵称\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:212\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:441\nmsgid \"`XEP-0174 <https://xmpp.org/extensions/xep-0174.html>`__\"\nmsgstr \"`XEP-0174 <https://xmpp.org/extensions/xep-0174.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:212\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:441\nmsgid \"Serverless Messaging\"\nmsgstr \"无服务器消息传递\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:214\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:443\nmsgid \"`XEP-0175 <https://xmpp.org/extensions/xep-0175.html>`__\"\nmsgstr \"`XEP-0175 <https://xmpp.org/extensions/xep-0175.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:214\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:443\nmsgid \"Best Practices for Use of SASL ANONYMOUS\"\nmsgstr \"使用 SASL ANONYMOUS 的最佳实践\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:216\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:445\nmsgid \"`XEP-0176 <https://xmpp.org/extensions/xep-0176.html>`__\"\nmsgstr \"`XEP-0176 <https://xmpp.org/extensions/xep-0176.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:216\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:445\nmsgid \"Jingle ICE-UDP Transport Method\"\nmsgstr \"Jingle ICE-UDP 传输方法\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:218\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:447\nmsgid \"`XEP-0177 <https://xmpp.org/extensions/xep-0177.html>`__\"\nmsgstr \"`XEP-0177 <https://xmpp.org/extensions/xep-0177.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:218\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:447\nmsgid \"Jingle Raw UDP Transport Method\"\nmsgstr \"Jingle 原始 UDP 传输方法\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:220\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:449\nmsgid \"`XEP-0178 <https://xmpp.org/extensions/xep-0178.html>`__\"\nmsgstr \"`XEP-0178 <https://xmpp.org/extensions/xep-0178.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:220\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:449\nmsgid \"Best Practices for Use of SASL EXTERNAL with Certificates\"\nmsgstr \"使用带有证书的 SASL EXTERNAL 的最佳实践\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:222\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:451\nmsgid \"`XEP-0179 <https://xmpp.org/extensions/xep-0179.html>`__\"\nmsgstr \"`XEP-0179 <https://xmpp.org/extensions/xep-0179.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:222\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:451\nmsgid \"Jingle IAX Transport Method\"\nmsgstr \"Jingle IAX传输方式\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:224\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:453\nmsgid \"`XEP-0180 <https://xmpp.org/extensions/xep-0180.html>`__\"\nmsgstr \"`XEP-0180 <https://xmpp.org/extensions/xep-0180.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:224\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:453\nmsgid \"Jingle Video via RTP\"\nmsgstr \"通过RTP的Jingle 视频\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:226\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:455\nmsgid \"`XEP-0181 <https://xmpp.org/extensions/xep-0181.html>`__\"\nmsgstr \"`XEP-0181 <https://xmpp.org/extensions/xep-0181.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:226\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:455\nmsgid \"Jingle DTMF\"\nmsgstr \"Jingle DTMF\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:228\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:457\nmsgid \"`XEP-0184 <https://xmpp.org/extensions/xep-0184.html>`__\"\nmsgstr \"`XEP-0184 <https://xmpp.org/extensions/xep-0184.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:228\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:457\nmsgid \"Message Receipts\"\nmsgstr \"消息回执\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:230\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:459\nmsgid \"`XEP-0185 <https://xmpp.org/extensions/xep-0185.html>`__\"\nmsgstr \"`XEP-0185 <https://xmpp.org/extensions/xep-0185.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:230\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:459\nmsgid \"Dialback Key Generation and Validation\"\nmsgstr \"回拨密钥生成和验证\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:232\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:461\nmsgid \"`XEP-0190 <https://xmpp.org/extensions/xep-0190.html>`__\"\nmsgstr \"`XEP-0190 <https://xmpp.org/extensions/xep-0190.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:232\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:461\nmsgid \"Best Practice for Closing Idle Streams\"\nmsgstr \"关闭空闲流的最佳实践\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:234\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:467\nmsgid \"`XEP-0199 <https://xmpp.org/extensions/xep-0199.html>`__\"\nmsgstr \"`XEP-0199 <https://xmpp.org/extensions/xep-0199.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:234\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:467\nmsgid \"XMPP Ping\"\nmsgstr \"XMPP Ping\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:236\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:469\nmsgid \"`XEP-0201 <https://xmpp.org/extensions/xep-0201.html>`__\"\nmsgstr \"`XEP-0201 <https://xmpp.org/extensions/xep-0201.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:236\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:469\nmsgid \"Best Practices for Message Threads\"\nmsgstr \"消息线程的最佳实践\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:238\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:471\nmsgid \"`XEP-0202 <https://xmpp.org/extensions/xep-0202.html>`__\"\nmsgstr \"`XEP-0202 <https://xmpp.org/extensions/xep-0202.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:238\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:471\nmsgid \"Entity Time\"\nmsgstr \"实体时间\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:240\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:473\nmsgid \"`XEP-0203 <https://xmpp.org/extensions/xep-0203.html>`__\"\nmsgstr \"`XEP-0203 <https://xmpp.org/extensions/xep-0203.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:240\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:473\nmsgid \"Delayed Delivery\"\nmsgstr \"延迟交付\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:242\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:475\nmsgid \"`XEP-0205 <https://xmpp.org/extensions/xep-0205.html>`__\"\nmsgstr \"`XEP-0205 <https://xmpp.org/extensions/xep-0205.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:242\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:475\nmsgid \"Best Practices to Discourage Denial of Service Attacks\"\nmsgstr \"阻止阻断服务攻击的最佳实践\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:244\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:479\nmsgid \"`XEP-0209 <https://xmpp.org/extensions/xep-0209.html>`__\"\nmsgstr \"`XEP-0209 <https://xmpp.org/extensions/xep-0209.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:244\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:479\nmsgid \"Metacontacts\"\nmsgstr \"元联系人\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:246\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:481\nmsgid \"`XEP-0220 <https://xmpp.org/extensions/xep-0220.html>`__\"\nmsgstr \"`XEP-0220 <https://xmpp.org/extensions/xep-0220.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:246\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:481\nmsgid \"Server Dialback\"\nmsgstr \"服务器回拨\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:248\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:485\nmsgid \"`XEP-0224 <https://xmpp.org/extensions/xep-0224.html>`__\"\nmsgstr \"`XEP-0224 <https://xmpp.org/extensions/xep-0224.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:248\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:485\nmsgid \"Attention\"\nmsgstr \"注意\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:250\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:487\nmsgid \"`XEP-0225 <https://xmpp.org/extensions/xep-0225.html>`__\"\nmsgstr \"`XEP-0225 <https://xmpp.org/extensions/xep-0225.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:250\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:487\nmsgid \"Component Connections\"\nmsgstr \"组件连接\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:252\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:489\nmsgid \"`XEP-0226 <https://xmpp.org/extensions/xep-0226.html>`__\"\nmsgstr \"`XEP-0226 <https://xmpp.org/extensions/xep-0226.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:252\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:489\nmsgid \"Message Stanza Profiles\"\nmsgstr \"消息节配置\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:254\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:491\nmsgid \"`XEP-0231 <https://xmpp.org/extensions/xep-0231.html>`__\"\nmsgstr \"`XEP-0231 <https://xmpp.org/extensions/xep-0231.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:254\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:491\nmsgid \"Bits of Binary\"\nmsgstr \"二进制位\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:256\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:493\nmsgid \"`XEP-0234 <https://xmpp.org/extensions/xep-0234.html>`__\"\nmsgstr \"`XEP-0234 <https://xmpp.org/extensions/xep-0234.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:256\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:493\nmsgid \"Jingle File Transfer\"\nmsgstr \"Jingle 文件传输\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:258\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:495\nmsgid \"`XEP-0245 <https://xmpp.org/extensions/xep-0245.html>`__\"\nmsgstr \"`XEP-0245 <https://xmpp.org/extensions/xep-0245.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:258\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:495\nmsgid \"The /me Command\"\nmsgstr \"/me 命令\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:260\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:497\nmsgid \"`XEP-0246 <https://xmpp.org/extensions/xep-0246.html>`__\"\nmsgstr \"`XEP-0246 <https://xmpp.org/extensions/xep-0246.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:260\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:497\nmsgid \"End-to-End XML Streams\"\nmsgstr \"端到端 XML 流\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:262\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:499\nmsgid \"`XEP-0247 <https://xmpp.org/extensions/xep-0247.html>`__\"\nmsgstr \"`XEP-0247 <https://xmpp.org/extensions/xep-0247.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:262\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:499\nmsgid \"Jingle XML Streams\"\nmsgstr \"Jingle XML 流\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:264\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:503\nmsgid \"`XEP-0250 <https://xmpp.org/extensions/xep-0250.html>`__\"\nmsgstr \"`XEP-0250 <https://xmpp.org/extensions/xep-0250.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:264\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:503\nmsgid \"C2C Authentication Using TLS\"\nmsgstr \"使用 TLS 的 C2C 身份验证\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:266\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:505\nmsgid \"`XEP-0251 <https://xmpp.org/extensions/xep-0251.html>`__\"\nmsgstr \"`XEP-0251 <https://xmpp.org/extensions/xep-0251.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:266\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:505\nmsgid \"Jingle Session Transfer\"\nmsgstr \"Jingle 会话转移\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:268\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:507\nmsgid \"`XEP-0260 <https://xmpp.org/extensions/xep-0260.html>`__\"\nmsgstr \"`XEP-0260 <https://xmpp.org/extensions/xep-0260.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:268\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:507\nmsgid \"Jingle SOCKS5 Bytestreams Transport Method\"\nmsgstr \"Jingle SOCKS5 字节流传输方法\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:270\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:509\nmsgid \"`XEP-0261 <https://xmpp.org/extensions/xep-0261.html>`__\"\nmsgstr \"`XEP-0261 <https://xmpp.org/extensions/xep-0261.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:270\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:509\nmsgid \"Jingle In-Band Bytestreams Transport\"\nmsgstr \"Jingle 带内字节流传输\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:272\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:511\nmsgid \"`XEP-0262 <https://xmpp.org/extensions/xep-0262.html>`__\"\nmsgstr \"`XEP-0262 <https://xmpp.org/extensions/xep-0262.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:272\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:511\nmsgid \"Use of ZRTP in Jingle RTP Sessions\"\nmsgstr \"在 Jingle RTP 会话中使用 ZRTP\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:274\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:513\nmsgid \"`XEP-0277 <https://xmpp.org/extensions/xep-0277.html>`__\"\nmsgstr \"`XEP-0277 <https://xmpp.org/extensions/xep-0277.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:274\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:513\nmsgid \"Microblogging over XMPP\"\nmsgstr \"XMPP 上的微博\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:276\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:517\nmsgid \"`XEP-0292 <https://xmpp.org/extensions/xep-0292.html>`__\"\nmsgstr \"`XEP-0292 <https://xmpp.org/extensions/xep-0292.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:276\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:517\nmsgid \"vCard4 Over XMPP\"\nmsgstr \"XMPP 上的 vCard4\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:278\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:519\nmsgid \"`XEP-0301 <https://xmpp.org/extensions/xep-0301.html>`__\"\nmsgstr \"`XEP-0301 <https://xmpp.org/extensions/xep-0301.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:278\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:519\nmsgid \"In-Band Real Time Text\"\nmsgstr \"带内实时文本\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:280\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:521\nmsgid \"`XEP-0305 <https://xmpp.org/extensions/xep-0305.html>`__\"\nmsgstr \"`XEP-0305 <https://xmpp.org/extensions/xep-0305.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:280\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:521\nmsgid \"XMPP Quickstart\"\nmsgstr \"XMPP 快速入门\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:282\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:525\nmsgid \"`XEP-0323 <https://xmpp.org/extensions/xep-0323.html>`__\"\nmsgstr \"`XEP-0323 <https://xmpp.org/extensions/xep-0323.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:282\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:525\nmsgid \"Internet of Things - Sensor Data\"\nmsgstr \"物联网 - 传感器数据\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:284\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:527\nmsgid \"`XEP-0324 <https://xmpp.org/extensions/xep-0324.html>`__\"\nmsgstr \"`XEP-0324 <https://xmpp.org/extensions/xep-0324.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:284\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:527\nmsgid \"Internet of Things - Provisioning\"\nmsgstr \"物联网 - 开通\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:286\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:529\nmsgid \"`XEP-0325 <https://xmpp.org/extensions/xep-0325.html>`__\"\nmsgstr \"`XEP-0325 <https://xmpp.org/extensions/xep-0325.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:286\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:529\nmsgid \"Internet of Things - Control\"\nmsgstr \"物联网 - 控制\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:288\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:531\nmsgid \"`XEP-0326 <https://xmpp.org/extensions/xep-0326.html>`__\"\nmsgstr \"`XEP-0326 <https://xmpp.org/extensions/xep-0326.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:288\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:531\nmsgid \"Internet of Things - Concentrators\"\nmsgstr \"物联网 - 集中器\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:290\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:533\nmsgid \"`XEP-0333 <https://xmpp.org/extensions/xep-0333.html>`__\"\nmsgstr \"`XEP-0333 <https://xmpp.org/extensions/xep-0333.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:290\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:533\nmsgid \"Chat Markers\"\nmsgstr \"聊天标记\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:292\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:539\nmsgid \"`XEP-0363 <https://xmpp.org/extensions/xep-0363.html>`__\"\nmsgstr \"`XEP-0363 <https://xmpp.org/extensions/xep-0363.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:292\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:539\nmsgid \"HTTP File Upload\"\nmsgstr \"HTTP 文件上传\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:294\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:543\nmsgid \"`XEP-0387 <https://xmpp.org/extensions/xep-0387.html>`__\"\nmsgstr \"`XEP-0387 <https://xmpp.org/extensions/xep-0387.html>`__\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:294\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:543\nmsgid \"XMPP Compliance Suites 2018\"\nmsgstr \"XMPP 合规性套件 2018\"\n\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:298\nmsgid \"Full, ordered list of supported RFCs and XEPs:\"\nmsgstr \"支持的 RFC 和 XEP 的完整有序列表：\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:80\n#: ../../Tigase_Administration/XMPP_Server/Supported_Extension.inc:547\nmsgid \"Requires commercial license\"\nmsgstr \"要求商业许可证\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:2\nmsgid \"Tigase Custom Extensions\"\nmsgstr \"Tigase 自定义扩展\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:5\nmsgid \"General features\"\nmsgstr \"综合特征\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:7\nmsgid \"tabel 6.Monitoring\"\nmsgstr \"tabel 6.监控\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:12\nmsgid \"AuditLog\"\nmsgstr \"审计日志\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:12\nmsgid \"\"\n\"Ability functionality to log important events in a system (loggins, \"\n\"message exchanges, calls)\"\nmsgstr \"能够记录系统中的重要事件（登录，消息交换，呼叫）\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:14\nmsgid \"Anti Abuse\"\nmsgstr \"反滥用\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:14\nmsgid \"Fight stanza SPAM, DoS, brute-force attacks and other threats\"\nmsgstr \"打击stanza SPAM、DoS、暴力攻击和其他威胁\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:16\nmsgid \"Virtual domains\"\nmsgstr \"虚拟域\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:16\nmsgid \"\"\n\"Ability to create and manage multiple virtual domains from a single \"\n\"instance and restart-less management\"\nmsgstr \"能够从单个实例创建和管理多个虚拟域并且无需重新启动管理\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:18\nmsgid \"MUC subscribe for offline push\"\nmsgstr \"MUC订阅离线推送\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:18\nmsgid \"\"\n\"Option to register permanently to the room to receive push notifications \"\n\"about new messages.\"\nmsgstr \"可选择永久注册到房间以接收新消息的推送通知。\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:20\nmsgid \"Scripting API\"\nmsgstr \"脚本 API\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:20\nmsgid \"Supports the Java Scripting API JSR-223\"\nmsgstr \"支持 Java 脚本 API JSR-223\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:22\nmsgid \"JMX monitoring\"\nmsgstr \"JMX 监控\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:22\nmsgid \"\"\n\"Advanced monitoring the server via JMX protocol with an API for \"\n\"connecting custom monitors and TCP/IP end-point for connecting general \"\n\"purpose JMX tools\"\nmsgstr \"通过 JMX 协议来连接自定义监视器的 API 和连接通用 JMX 工具的 TCP/IP \"\n\"端点对服务器进行高级监控\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:24\nmsgid \"HTTP monitoring\"\nmsgstr \"HTTP 监控\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:24\nmsgid \"Basic monitoring via HTTP protocol\"\nmsgstr \"通过 HTTP 协议进行基本监控\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:26\nmsgid \"XMPP Monitoring\"\nmsgstr \"XMPP 监控\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:26\nmsgid \"\"\n\"Pluggable, active monitoring via XMPP, retrieving detailed server \"\n\"statistics, receiving automatic notifications about possible problems \"\n\"discovered by the self-monitor mechanisms\"\nmsgstr \"通过 XMPP 进行插入式的主动监控，来检索详细的服务器统计信息，并接收通过自我监\"\n\"控机制所发现的可能问题的自动通知\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:28\nmsgid \"SNMP Monitoring\"\nmsgstr \"SNMP 监控\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:28\nmsgid \"Advanced server monitoring via SNMP.\"\nmsgstr \"通过 SNMP 进行高级服务器监控。\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:30\nmsgid \"Bosh Cache\"\nmsgstr \"Bosh缓存\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:30\nmsgid \"\"\n\"Bosh Session Cache - a feature to quickly reload user data - roster, \"\n\"presences and messages history by the web client (for example after web \"\n\"page reload)\"\nmsgstr \"Bosh 会话缓存 - 一种快速重新加载用户数据的功能 - 网络 \"\n\"客户端（例如在网页重新加载后）的名册，出现和消息历史记录\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:32\nmsgid \"Clustering\"\nmsgstr \"集群\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:32\nmsgid \"\"\n\"Full clustering support for HA and LB with pluggabble clustering \"\n\"strategies for perfect optimising the cluster to the client’s system\"\nmsgstr \"对 HA 和 LB \"\n\"的完整集群支持，具有插入式集群策略，从而可完美优化集群到客户端系统\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:34\nmsgid \"Advanced Clustering Strategy\"\nmsgstr \"高级聚群策略\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:34\nmsgid \"Dedicated, specialised clustering strategy for best possible performance\"\nmsgstr \"专用和专门的集群策略以获得最佳性能\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:36\nmsgid \"MUC Clustered\"\nmsgstr \"MUC 集群\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:36\nmsgid \"Support for clustering group chatrooms with various, pluggable strategies\"\nmsgstr \"支持具有各种插入式策略的集群群组聊天室\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:38\nmsgid \"PubSub Clustered\"\nmsgstr \"PubSub 集群\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:38\nmsgid \"Support for clustering PubSub component with various, pluggable strategies\"\nmsgstr \"支持使用各种插入式策略对 PubSub 组件进行集群\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:40\nmsgid \"Mobile optimisations\"\nmsgstr \"移动优化\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:40\nmsgid \"Optimizations designed for Mobile Devices\"\nmsgstr \"对移动设备的优化设计\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:42\nmsgid \"OSGi\"\nmsgstr \"OSGi\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:42\nmsgid \"\"\n\"Support for running in OSGi environment, i.e. as embedded XMPP server in \"\n\"advanced application server\"\nmsgstr \"支持在 OSGi 环境中运行，即作为高级应用服务器中的嵌入式 XMPP 服务器\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:44\nmsgid \"Dynamic rosters\"\nmsgstr \"动态名册\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:44\nmsgid \"\"\n\"Ability to create users' rosters entries on the fly based on data \"\n\"retrieved from any sources\"\nmsgstr \"能够根据从任何来源检索到的数据即时创建用户名册条目\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:46\nmsgid \"Command line admin tools\"\nmsgstr \"命令行管理工具\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:46\nmsgid \"Commandline utility to manage server\"\nmsgstr \"用于管理服务器的命令行工具\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:48\nmsgid \"Unified Archive\"\nmsgstr \"统一档案\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:48\nmsgid \"\"\n\"An extension to XEP-0313 Message Archive Management, with greatly \"\n\"improved flexibility in terms of what can be archived.\"\nmsgstr \"XEP-0313 消息存档管理的扩展，在可以存档的内容方面极大提高了灵活性。\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:53\nmsgid \"Repositories/Databases\"\nmsgstr \"存储库/数据库\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:55\nmsgid \"Table 7.Repositories/Databases\"\nmsgstr \"Table 7.存储库/数据库\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:60\nmsgid \"DB per domain\"\nmsgstr \"每个域的数据库\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:60\nmsgid \"Ability to have multiple databases for specific domains.\"\nmsgstr \"特定域可拥有多个数据库。\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:62\nmsgid \"PostgreSQL\"\nmsgstr \"PostgreSQL\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:62\nmsgid \"\"\n\"Full support for PostgreSQL database with database schemas excluding \"\n\"dedicated DB schema for PubSub component\"\nmsgstr \"完全支持除PubSub 组件的专用数据库模式之外的具有数据库模式的 PostgreSQL 数据库\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:64\nmsgid \"MySQL\"\nmsgstr \"MySQL\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:64\nmsgid \"\"\n\"Full support for MySQL database with database schemas, dedicated DB \"\n\"schema for PubSub component\"\nmsgstr \"完全支持带有数据库模式的 MySQL 数据库，PubSub 组件的专用数据库模式\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:66\nmsgid \"SQL Server\"\nmsgstr \"SQL 服务器\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:66\nmsgid \"\"\n\"Full support for MS SQL Server database with database schemas excluding \"\n\"dedicated DB schema for PubSub component, only in Tigase server version \"\n\"3.x\"\nmsgstr \"\"\n\"完全支持除PubSub 组件的专用数据库架构以外的具有数据库架构的 MS SQL Server \"\n\"数据库，这些仅存在于Tigase 服务器版本 3.x 中\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:68\nmsgid \"Derby DB\"\nmsgstr \"Derby 数据库\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:68\nmsgid \"\"\n\"Full support for built-in Derby database with database schemas excluding \"\n\"dedicated DB schema for PubSub component\"\nmsgstr \"完全支持除PubSub 组件的专用数据库架构以外的具有数据库架构的内置 Derby 数据库\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:70\nmsgid \"JDBC\"\nmsgstr \"JDBC\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:70\nmsgid \"\"\n\"Support for all JDBC enabled databases, although the database schemas are\"\n\" available for some databases\"\nmsgstr \"支持所有启用 JDBC 的数据库，尽管数据库模式只可用于某些数据库\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:72\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:74\nmsgid \"Drupal Auth\"\nmsgstr \"Drupal 身份验证\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:72\nmsgid \"\"\n\"Drupal authentication - the Tigase server can share user authentication \"\n\"database with Drupal CMS and authenticate users agains Drupal user \"\n\"database\"\nmsgstr \"\"\n\"Drupal 身份验证 - Tigase 服务器可以与 Drupal CMS 共享用户身份验证数据库，\"\n\"并可再次对 Drupal 用户数据库进行身份验证\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:74\nmsgid \"\"\n\"Close integration with Drupal CMS, the Tigase can send notifications to \"\n\"subscribed users about new posts, comments and can also publish short \"\n\"news information via XMPP\"\nmsgstr \"与 Drupal CMS 紧密集成，Tigase 可以向订阅用户发送相关新帖子，评论的通知，\"\n\"还可以通过 XMPP 发布短消息\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:76\nmsgid \"LDAP-Auth\"\nmsgstr \"LDAP认证\"\n\n#: ../../Tigase_Administration/XMPP_Server/Custom_Extensions.inc:76\nmsgid \"LDAP Authentication Connector Supported\"\nmsgstr \"支持 LDAP 身份验证连接器\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Administration/index.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2023-02-16 15:01-0800\\n\"\n\"PO-Revision-Date: 2023-02-17 04:51+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/tigase-administation-index/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.11.0\\n\"\n\n#: ../../Tigase_Administration/index.rst:3\nmsgid \"Administration Guide\"\nmsgstr \"管理文档\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Development/Basic_Information.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-05-27 12:30-0700\\n\"\n\"PO-Revision-Date: 2022-09-07 17:14+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/dpguide_basicinformation/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Basic_Information.rst:2\nmsgid \"Basic Information\"\nmsgstr \"基本信息\"\n\n#: ../../Tigase_Development/Basic_Information.rst:5\nmsgid \"Tigase Architecture\"\nmsgstr \"Tigase架构\"\n\n#: ../../Tigase_Development/Basic_Information.rst:7\nmsgid \"\"\n\"The most important thing to understand is that Tigase is very modular and\"\n\" you can have multiple components running inside single instance. However\"\n\" one of the most important components is MessageRouter, which sits in the\"\n\" centre and serves as a, as name suggest, packet router directing packets\"\n\" to the appropriate components.\"\nmsgstr \"\"\n\"要理解的最重要的事情是Tigase是非常模块化的，您可以在单个实例中运行多个组件。\"\n\"然而，最重要的组件之一是 MessageRouter，它位于中心，顾名思义，它充当将数据包\"\n\"引导到适当组件的数据包路由器。\"\n\n#: ../../Tigase_Development/Basic_Information.rst:9\nmsgid \"\"\n\"There is also a group of specialised component responsible for handling \"\n\"users connections: ``ConnectionManagers`` (``c2s``, ``s2s``, ``ws2s``, \"\n\"``bosh``). They receive packets from the incoming connection, then \"\n\"subsequently they forward processed packet to ``MessageRouter``. Most of \"\n\"the time, especially for packets coming from user connections, packet is \"\n\"routed to ``SessionManager`` component (with the session object referring\"\n\" to appropriate user in case of client to server connection). After \"\n\"processing in ``SessionManager`` packet goes back to ``MessageRouter`` \"\n\"and then, based on the stanza addressing\\\\` can go to different component\"\n\" (muc, pubsub) or if it’s addressed to another user it can go through:\"\nmsgstr \"\"\n\"还有一组专门的组件负责处理用户连接：``ConnectionManagers`` \"\n\"（``c2s``，``s2s``，``ws2s``，``bosh``）。他们从传入连接接收数据包，\"\n\"然后将处理后的数据包转发到 \"\n\"``MessageRouter``。大多数时候，特别是对于来自用户连接的数据包，\"\n\"数据包被路由到 ``SessionManager`` \"\n\"组件（在客户端到服务器连接的情况下，会话对象引用适当的用户）。在 \"\n\"``SessionManager`` 处理后，数据包返回到 ``MessageRouter`` 然后，根据节寻址，\"\n\"可以转到不同的组件（muc，pubsub），或者如果它被寻址到另一个用户，它可以通过：\"\n\n#: ../../Tigase_Development/Basic_Information.rst:11\nmsgid \"\"\n\"``SessionManager`` (again), ``MessageRouter`` and then (user) \"\n\"``ConnectionManagers`` or,\"\nmsgstr \"\"\n\"``SessionManager`` （再次），``MessageRouter`` 然后（用户） \"\n\"``ConnectionManagers`` 或者，\"\n\n#: ../../Tigase_Development/Basic_Information.rst:13\nmsgid \"\"\n\"``s2s`` (*server to server connection manager*) if the user or component \"\n\"is on the different, federated, xmpp server;\"\nmsgstr \"``s2s`` \"\n\"（*服务器到服务器连接管理器*）如果用户或组件在不同的，联合的，xmpp服务器上；\"\n\n#: ../../Tigase_Development/Basic_Information.rst:15\nmsgid \"In a very broad view this can be depicted with a following graph:\"\nmsgstr \"从一个非常广泛的角度来看，这可以用下图来描述：\"\n\n#: ../../Tigase_Development/Basic_Information.rst:17\nmsgid \"|Tigase architecture|\"\nmsgstr \"|Tigase architecture|\"\n\n#: ../../Tigase_Development/Basic_Information.rst:90\nmsgid \"Tigase architecture\"\nmsgstr \"Tigase architecture\"\n\n#: ../../Tigase_Development/Basic_Information.rst:20\nmsgid \"Tigase Server Elements\"\nmsgstr \"Tigase服务器元素\"\n\n#: ../../Tigase_Development/Basic_Information.rst:22\nmsgid \"\"\n\"To make it easier to get into the code below are defined basic terms in \"\n\"the Tigase server world and there is a brief explanation how the server \"\n\"is designed and implemented. This document also points you to basic \"\n\"interfaces and implementations which can be used as example code \"\n\"reference.\"\nmsgstr \"\"\n\"为了更容易进入下面的代码，定义了Tigase服务器领域的基本术语，并简要说明了服务\"\n\"器是如何设计和实现的。本文档还为您指出可用作示例代码参考的基本接口和实现。\"\n\n#: ../../Tigase_Development/Basic_Information.rst:24\nmsgid \"\"\n\"Logically all server code can be divided into 3 kinds of modules: \"\n\"**components**, **plug-ins** and **connectors**.\"\nmsgstr \"从逻辑上讲，所有服务器代码都可以分为3种模块：**组件**，**插件** 和 \"\n\"**连接器**。\"\n\n#: ../../Tigase_Development/Basic_Information.rst:26\nmsgid \"\"\n\"**Components** are the main element of Tigase server. Components are a \"\n\"bigger piece of code which can have separate address, receive and send \"\n\"stanzas, and be configured to respond to numerous events. Sample \"\n\"components implemented for Tigase server are: *c2s connection manager*, \"\n\"*s2s connection manager*, *session manager*, *XEP-0114 - external \"\n\"component connection manager*, *MUC - multi user char rooms*.\"\nmsgstr \"\"\n\"**组件** 是Tigas 服务器的主要元素。组件是一段更大的代码，它可以有单独的地址，\"\n\"接收和发送节，并被配置为响应众多事件。为Tigase服务器实现的示例组件有：*c2s \"\n\"连接管理器*，*s2s 连接管理器*，*会话管理器*，*XEP-0114 - \"\n\"外部组件连接管理器*，*MUC - 多用户字符室*。\"\n\n#: ../../Tigase_Development/Basic_Information.rst:28\nmsgid \"\"\n\"**Plug-ins** are usually small pieces of code responsible for processing \"\n\"specific XMPP stanzas. They don’t have their own address. As a result of \"\n\"stanza processing they can produce new XMPP stanzas. Plug-ins are loaded \"\n\"by *session manager* component or the *c2s connection manager* component.\"\n\" Sample plug-ins are: *vCard* stanza processing, *jabber:iq:register* to \"\n\"register new user accounts, *presence* stanza processing, and \"\n\"*jabber:iq:auth* for non-sasl authentication.\"\nmsgstr \"\"\n\"**插件** 通常是一小段代码，负责处理特定的XMPP节。他们没有自己的地址。作为节处\"\n\"理的结果，他们可以生成新的XMPP节。插件由 *session manager* 组件或 *c2s \"\n\"connection manager* 组件加载。示例插件是：*vCard* \"\n\"节处理、*jabber:iq:register* 注册新用户帐户，*presence* 节处理和 \"\n\"用于非sasl身份验证的 *jabber:iq:auth* 。\"\n\n#: ../../Tigase_Development/Basic_Information.rst:30\nmsgid \"\"\n\"**Connectors** are modules responsible for access to data repositories \"\n\"like databases or LDAP to store and retrieve user data. There are 2 kinds\"\n\" of connectors: authentication connectors and user data connectors. Both \"\n\"of them are independent and can connect to different data sources. Sample\"\n\" connectors are: *JDBC database* connector, *XMLDB - embedded database* \"\n\"connector, *Drupal database* connector.\"\nmsgstr \"\"\n\"**连接器** 是负责访问数据库或LDAP等数据存储库以存储和检索用户数据的模块。有两\"\n\"种连接器：身份验证连接器和用户数据连接器。它们都是独立的，可以连接到不同的数\"\n\"据源。示例连接器是：*JDBC 数据库* 连接器，*XMLDB - 嵌入式数据库* 连接器，*\"\n\"Drupal 数据库* 连接器。\"\n\n#: ../../Tigase_Development/Basic_Information.rst:32\nmsgid \"\"\n\"There is an API defined for each kind of above modules and all you have \"\n\"to do is enable the implementation of that specific interface. Then the \"\n\"module can be loaded to the server based on it’s configuration settings. \"\n\"There is also abstract classes available, implementing these interfaces \"\n\"to make development easier.\"\nmsgstr \"\"\n\"为上述每种模块定义了一个API，您所要做的就是启用该特定接口的实现。然后可以根据\"\n\"其配置设置将该模块加载到服务器。还有可用的抽象类，实现这些接口以使开发更容易\"\n\"。\"\n\n#: ../../Tigase_Development/Basic_Information.rst:34\nmsgid \"\"\n\"Here is a brief list of all interfaces to look at and for more details \"\n\"you have to refer to the guide for specific kind of module.\"\nmsgstr \"以下是要查看的所有接口的简要列表，有关更多详细信息，您须参考特定类型模块的指\"\n\"南。\"\n\n#: ../../Tigase_Development/Basic_Information.rst:37\nmsgid \"Components\"\nmsgstr \"组件\"\n\n#: ../../Tigase_Development/Basic_Information.rst:39\nmsgid \"This is list of interfaces to look at when you work on a new component:\"\nmsgstr \"这是处理新组件时要查看的接口列表：\"\n\n#: ../../Tigase_Development/Basic_Information.rst:41\nmsgid \"\"\n\"**tigase.server.ServerComponent** - This is the very basic interface for \"\n\"component. All components must implement it.\"\nmsgstr \"**tigase.server.ServerComponent** - \"\n\"这是组件的非常基本的接口。所有组件都必须实现它。\"\n\n#: ../../Tigase_Development/Basic_Information.rst:43\nmsgid \"\"\n\"**tigase.server.MessageReceiver** - This interface extends \"\n\"``ServerComponent`` and is required to implement by components which want\"\n\" to receive data packets like *session manager* and *c2s connection \"\n\"manager*.\"\nmsgstr \"\"\n\"**tigase.server.MessageReceiver** - 该接口扩展了 \"\n\"``ServerComponent``，并且需要由想要接收数据包的组件实现，例如 *session \"\n\"manager* 和 *c2s connection manager*。\"\n\n#: ../../Tigase_Development/Basic_Information.rst:45\nmsgid \"\"\n\"**tigase.conf.Configurable** - Implementing this interface is required to\"\n\" make it configurable. For each object of this type, configuration is \"\n\"pushed to it at any time at runtime. This is necessary to make it \"\n\"possible to change configuration at runtime. Be careful to implement this\"\n\" properly as it can cause issues for modules that cannot be configured.\"\nmsgstr \"\"\n\"**tigase.conf.Configurable** - 需要实现此接口才能使其可配置。对于这种类型的每\"\n\"个对象，配置会在运行时随时推送给它。这对于在运行时更改配置是必要的。请小心正\"\n\"确实施，因为它可能会导致无法配置的模块出现问题。\"\n\n#: ../../Tigase_Development/Basic_Information.rst:47\nmsgid \"\"\n\"**tigase.disco.XMPPService** - Objects using this interface can respond \"\n\"to \\\"ServiceDiscovery\\\" requests.\"\nmsgstr \"**tigase.disco.XMPPService** - 使用此接口的对象可以响应 \\\"ServiceDiscovery\\\" \"\n\"请求。\"\n\n#: ../../Tigase_Development/Basic_Information.rst:49\nmsgid \"\"\n\"**tigase.stats.StatisticsContainer** - Objects using this interface can \"\n\"return runtime statistics. Any object can collect job statistics and \"\n\"implementing this interface guarantees that statistics will be presented \"\n\"in consisted way to user who wants to see them.\"\nmsgstr \"\"\n\"**tigase.stats.StatisticsContainer** - 使用此接口的对象可以返回运行时统计信息\"\n\"。任何对象都可以收集作业统计信息，并且实现此接口可确保统计信息以一致的方式呈\"\n\"现给想要查看它们的用户。\"\n\n#: ../../Tigase_Development/Basic_Information.rst:51\nmsgid \"\"\n\"Instead of implementing above interfaces directly, it is recommended to \"\n\"extend one of existing abstract classes which take care of the most of \"\n\"\\\"dirty and boring\\\" stuff. Here is a list the most useful abstract \"\n\"classes:\"\nmsgstr \"建议不要直接实现上述接口，而是扩展现有的抽象类之一，\"\n\"它可以处理大部分\\\"又脏又无聊\\\"的东西。以下是最有用的抽象类列表：\"\n\n#: ../../Tigase_Development/Basic_Information.rst:53\nmsgid \"**tigase.server.AbstractMessageReceiver** - Implements 4 basic interfaces:\"\nmsgstr \"**tigase.server.AbstractMessageReceiver** - 实现 4 个基本接口：\"\n\n#: ../../Tigase_Development/Basic_Information.rst:55\nmsgid \"\"\n\"``ServerComponent``, ``MessageReceiver``, ``Configurable`` and \"\n\"``StatisticsContainer``. AbstractMessageReceiver also manages internal \"\n\"data queues using it’s own threads which prevents dead-locks from \"\n\"resource starvation. It offers even-driven data processing which means \"\n\"whenever packet arrives the ``abstract void processPacket(Packet \"\n\"packet);`` method is called to process it. You have to implement this \"\n\"abstract method in your component, if your component wants to send a \"\n\"packet (in response to data it received for example).\"\nmsgstr \"\"\n\"``ServerComponent``、``MessageReceiver``、``Configurable`` 和 \"\n\"``StatisticsContainer``。 AbstractMessageReceiver 还使用它自己的线程来管理内\"\n\"部数据队列，从而防止资源匮乏导致的死锁。它提供了事件驱动的数据处理，这意味着\"\n\"每当数据包到达时，都会调用 ``abstract void processPacket(Packet packet);`` 方\"\n\"法来处理它。如果您的组件想要发送一个数据包（例如响应它收到的数据），您必须在\"\n\"您的组件中实现这个抽象方法。\"\n\n#: ../../Tigase_Development/Basic_Information.rst:61\nmsgid \"\"\n\"**tigase.server.ConnectionManager** - This is an extension of \"\n\"``AbstractMessageReceiver`` abstract class. As the name says this class \"\n\"takes care of all network connection management stuff. If your component \"\n\"needs to send and receive data directly from the network (like c2s \"\n\"connection, s2s connection or external component) you should use this \"\n\"implementation as a basic class. It takes care of all things related to \"\n\"networking, I/O, reconnecting, listening on socket, connecting and so on.\"\n\" If you extend this class you have to expect data coming from to sources:\"\nmsgstr \"\"\n\"**tigase.server.ConnectionManager** - 这是 ``AbstractMessageReceiver`` 抽象类\"\n\"的扩展。顾名思义，这个类负责所有网络连接管理。如果您的组件需要直接从网络发送\"\n\"和接收数据（如c2s连接，s2s连接或外部组件），您应该将此实现用作基本类。它负责\"\n\"与网络，I/O，重新连接，侦听套接字，连接等相关的所有事情。如果你扩展这个类，你\"\n\"必须期望数据来自以下源：\"\n\n#: ../../Tigase_Development/Basic_Information.rst:63\nmsgid \"\"\n\"From the ``MessageRouter`` and this is when the ``abstract void \"\n\"processPacket(Packet packet);`` method is called and second, from network\"\n\" connection and then the ``abstract Queue processSocketData(XMPPIOService\"\n\" serv);`` method is called.\"\nmsgstr \"\"\n\"从 ``MessageRouter`` ，这是当调用 ``abstract void processPacket(Packet \"\n\"packet);`` 方法的时候，其次，从网络连接，然后是 ``abstract Queue \"\n\"processSocketData(XMPPIOService serv);`` 方法被调用。\"\n\n#: ../../Tigase_Development/Basic_Information.rst:66\nmsgid \"Plug-ins\"\nmsgstr \"插件\"\n\n#: ../../Tigase_Development/Basic_Information.rst:68\nmsgid \"\"\n\"All Tigase plugins currently implemented are located in package: \"\n\"tigase.xmpp.impl. You can use this code as a sample code base. There are \"\n\"3 types of plug-ins and they are defined in interfaces located in \"\n\"``tigase.xmpp`` package:\"\nmsgstr \"\"\n\"当前实现的所有Tigase插件都位于以下包中：tigase.xmpp.\"\n\"impl。您可以将此代码用作示例代码库。有3种类型的插件，它们在位于 ``tigase.\"\n\"xmpp`` 包中的接口中定义：\"\n\n#: ../../Tigase_Development/Basic_Information.rst:70\nmsgid \"\"\n\"**XMPPProcessorIfc** - The most important and basic plug-in. This is the \"\n\"most common plug-in type which just processes stanzas in normal mode. It \"\n\"receives packets, processes them on behalf of the user and returns \"\n\"resulting stanzas.\"\nmsgstr \"\"\n\"**XMPPProcessorIfc** - 最重要和最基本的插件。这是最常见的插件类型，仅在正常模\"\n\"式下处理节。它接收数据包，代表用户处理它们并返回结果节。\"\n\n#: ../../Tigase_Development/Basic_Information.rst:72\nmsgid \"\"\n\"**XMPPPreprocessorIfc** - This plugin performs pre-processing of the \"\n\"packet, intended for the pre-processors to setup for packet blocking.\"\nmsgstr \"**XMPPPreprocessorIfc** - \"\n\"此插件执行数据包的预处理，用于预处理器设置数据包阻塞。\"\n\n#: ../../Tigase_Development/Basic_Information.rst:74\nmsgid \"\"\n\"**XMPPPostprocessorIfc** - This plugin performs processing of packets for\"\n\" which there was no specific processor.\"\nmsgstr \"**XMPPPostprocessorIfc** - 该插件对没有特定处理器的数据包进行处理。\"\n\n#: ../../Tigase_Development/Basic_Information.rst:77\nmsgid \"Connector\"\nmsgstr \"连接器\"\n\n#: ../../Tigase_Development/Basic_Information.rst:80\nmsgid \"Data, Stanzas, Packets - Data Flow and Processing\"\nmsgstr \"数据，节，数据包 - 数据流和处理\"\n\n#: ../../Tigase_Development/Basic_Information.rst:82\nmsgid \"\"\n\"Data received from the network are read from the network sockets as bytes\"\n\" by code in the ``tigase.io`` package. Bytes then are changed into \"\n\"characters in classes of ``tigase.net`` package and as characters they \"\n\"are sent to the XML parser (``tigase.xml``) which turns them to XML DOM \"\n\"structures.\"\nmsgstr \"\"\n\"从网络接收的数据通过 ``tigase.io`` 包中的代码从网络套接字读取为字节。\"\n\"然后字节被转换为 ``tigase.net`` 包类中的字符，并作为字符发送到 XML \"\n\"解析器（``tigase.xml``），后者将它们转换为XML DOM结构。\"\n\n#: ../../Tigase_Development/Basic_Information.rst:84\nmsgid \"\"\n\"All data inside the server is exchanged in XML DOM form as this is the \"\n\"format used by XMPP protocol. For basic XML data processing (parsing \"\n\"characters stream, building DOM, manipulate XML elements and attributes) \"\n\"we use `Tigase XML parser and DOM builder <https://github.com/tigase\"\n\"/tigase-xmltools>`__.\"\nmsgstr \"\"\n\"服务器内部的所有数据都以XML \"\n\"DOM形式交换，因为这是XMPP协议使用的格式。对于基本的XML数据处理（解析字符流、\"\n\"构建 DOM、操作 XML 元素和属性），我们使用 `Tigase XML parser and DOM builder \"\n\"<https://github.com/tigase/tigase-xmltools>`__。\"\n\n#: ../../Tigase_Development/Basic_Information.rst:86\nmsgid \"\"\n\"Each stanza is stored in the ``tigase.xml.Element`` object. Every Element\"\n\" can contain any number of **Child Elements** and any number of \"\n\"attributes. You can access all these data through the class API.\"\nmsgstr \"\"\n\"每个节都存储在 ``tigase.xml.Element`` 对象中。每个元素可以包含任意数量的 \"\n\"**子元素** 和任意数量的属性。您可以通过类API访问所有这些数据。\"\n\n#: ../../Tigase_Development/Basic_Information.rst:88\nmsgid \"\"\n\"To simplify some, most common operations Element is wrapped in \"\n\"``tigase.server.Packet`` class which offers another level of API for the \"\n\"most common operations like preparation of response stanza based on the \"\n\"element it contains (swap to/from values, put type=result attribute and \"\n\"others).\"\nmsgstr \"\"\n\"为了简化一些，最常见的操作元素被包在 ``tigase.server.Packet`` 类中，它为最常\"\n\"见的操作提供了另一个级别的API，比如基于它包含的元素准备响应节（交换到/从值，\"\n\"放入 type=result 属性和其他）。\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Development/Cluster_Map_Interface.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-05-27 12:30-0700\\n\"\n\"PO-Revision-Date: 2022-07-25 23:03+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/dg-cluster_map_interface/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:2\nmsgid \"Cluster Map Interface\"\nmsgstr \"集群映射接口\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:4\nmsgid \"\"\n\"Starting with v7.1.0, a cluster map interface has been implemented. The \"\n\"cluster map is aided by use of the distributed event bus system to \"\n\"communicate between all clusters.\"\nmsgstr \"从v7.1.0开始，实现了集群映射接口。集群映射通过使用分布式事件总线系统在所有集\"\n\"群之间进行通信来辅助。\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:7\nmsgid \"Requirements\"\nmsgstr \"要求\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:9\nmsgid \"\"\n\"Any full distribution of Tigase will support the Cluster Map API so long \"\n\"as the eventbus component is not disabled. JDK v8 is required for this \"\n\"feature, however since Tigase requires this, you should already have it \"\n\"installed.\"\nmsgstr \"\"\n\"只要未禁用事件总线组件，任何完整的Tigase发行版都将支持Cluster Map API。\"\n\"此功能需要JDK v8，但由于Tigase需要此功能，您应该已经安装了它。\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:11\nmsgid \"\"\n\"The cluster map is stored in memory and follows the \"\n\"``map.util.interface`` java standards can be used to improve cluster \"\n\"connections, and help clustered servers keep track of each other.\"\nmsgstr \"集群映射存储在内存中并遵循 ``map.util.interface`` \"\n\"java标准，可用于改善集群连接，并帮助集群服务器相互跟踪。\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:14\nmsgid \"Map Creation\"\nmsgstr \"映射创建\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:16\nmsgid \"Map must be created with the following command:\"\nmsgstr \"必须使用以下命令创建映射：\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:22\nmsgid \"\"\n\"Where \\\"type\\\" is the map ID. This creates the map locally and then fires\"\n\" an event to all clustered servers. Each cluster server has an event \"\n\"handler waiting for, in this case, ``NewMapCreate`` event. Map Key class \"\n\"and Map Value class are used to type conversion. Arrays of strings are \"\n\"parameters, for example ID of user session. Once received, the \"\n\"distributed eventbus will create a local map.\"\nmsgstr \"\"\n\"其中\\\"type\\\"是映射ID。这会在本地创建映射，然后向所有集群服务器触发事件。每个\"\n\"集群服务器都有一个事件处理程序等待，在这种情况下，``NewMapCreate`` 事件。 \"\n\"Map Key类和Map Value类用于类型转换。字符串数组是参数，例如用户会话的ID。一旦\"\n\"收到，分布式事件总线将创建一个本地映射。\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:32\nmsgid \"A brief example of a map creation is shown here:\"\nmsgstr \"此处显示了映射创建的简短示例：\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:38\nmsgid \"\"\n\"This will fire event ``MapCreatedEvent`` on all other cluster nodes. \"\n\"Strings \\\"Very_Important_Map_In_User_Session\\\" and \\\"user-session-\"\n\"identifier-123\\\" are given as parameters in :literal:`onMapCreated()\\\\`` \"\n\"method. The event consumer code must know what to do with map with type \"\n\"\\\"Very_Important_Map_In_User_Session\\\". It may retrieve user session \"\n\"\\\"user-session-identifier-123\\\" and put this map in this session. It \"\n\"should be used to tell other nodes how to treat the event with a newly \"\n\"created map, and it should be stored in user session.\"\nmsgstr \"\"\n\"这将在所有其他集群节点上触发事件 ``MapCreatedEvent`` 。字符串 \"\n\"\\\"Very_Important_Map_In_User_Session\\\" 和 \\\"user-session-identifier-123\\\" 在 \"\n\":literal:`onMapCreated()\\\\`` 方法中作为参数给出。\"\n\"事件用户代码必须知道如何处理类型为\\\"Very_Important_Map_In_User_Session\"\n\"\\\"的地图。它可能会检索用户会话 \\\"user-session-identifier-\"\n\"123\\\"并将此映射放入此会话中。它应该用于告诉其他节点如何使用新创建的映射处理事\"\n\"件，并且应该将其存储在用户会话中。\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:41\nmsgid \"Map Changes\"\nmsgstr \"映射更改\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:43\nmsgid \"\"\n\"Changes to the map on one cluster will trigger ``AddValue`` or \"\n\"``RemoveValue`` events in eventbus. Stanzas sent between clusters will \"\n\"look something like this:\"\nmsgstr \"\"\n\"对一个集群上的映射的更改将在eventbus中触发 ``AddValue`` 或 ``RemoveValue`` \"\n\"事件。在集群之间发送的节看起来像这样：\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:59\nmsgid \"Code to handle adding an item:\"\nmsgstr \"处理添加项目的代码：\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:68\nmsgid \"\"\n\"Where the element 'event' is the UID, and the name string is the name of \"\n\"the map key/value pair.\"\nmsgstr \"其中元素 'event'是UID，名称字符串是映射键/值对的名称。\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:70\nmsgid \"\"\n\"This example removes an element from the cluster map. Removal of items \"\n\"look similar:\"\nmsgstr \"此示例从集群映射中删除一个元素。删除项目看起来相似：\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:82\nmsgid \"with the code also being similar:\"\nmsgstr \"代码也相似：\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:93\nmsgid \"Map Destruction\"\nmsgstr \"映射破坏\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:95\nmsgid \"\"\n\"Java Garbage Collector will normally remove a local map if it is no \"\n\"longer used. Clustered maps however are not removed in this manner. These\"\n\" maps must be destroyed manually if they are no longer used:\"\nmsgstr \"如果不再使用本地映射，Java垃圾收集器通常会删除它。然而，集群映射不会以这种方\"\n\"式被删除。如果不再使用这些映射，则必须手动销毁它们：\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:101\nmsgid \"Calling this, the map named clmap will be destroyed on each cluster node.\"\nmsgstr \"调用这个，名为clmap的映射将在每个集群节点上被销毁。\"\n\n#: ../../Tigase_Development/Cluster_Map_Interface.rst:103\nmsgid \"\"\n\"The event handler will catch event when map is destroyed on another \"\n\"cluster node:\"\nmsgstr \"当映射在另一个集群节点上被销毁时，事件处理程序将捕获事件：\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Development/CodeStyleGuide.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: 2022-09-07 17:14+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/codestyleguide/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:2\nmsgid \"Tigase Code Style\"\nmsgstr \"Tigase代码样式\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:5\nmsgid \"Introduction\"\nmsgstr \"介绍\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:8\nmsgid \"\"\n\"This documents defines and describes coding style and standard used in \"\n\"Tigase projects source code.\"\nmsgstr \"本文档定义并描述了Tigase项目源代码中使用的编码风格和标准。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:10\nmsgid \"\"\n\"Examples should be considered as **non-normative**, that is formatting \"\n\"choices should not be treated as rules.\"\nmsgstr \"示例应被视为 **非规范**，即格式选择不应被视为规则。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:13\nmsgid \"Source file basics\"\nmsgstr \"源文件基础\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:16\nmsgid \"Technicals details\"\nmsgstr \"技术细节\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:18\nmsgid \"\"\n\"File name consists of the case-sensitive, camel-cased name of the top-\"\n\"level class it contains plus the ``.java`` extension.\"\nmsgstr \"文件名由它包含的顶级类的区分大小写的驼峰式名称加上 ``.java`` 扩展名组成。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:20\nmsgid \"Source files are encoded in **UTF-8**.\"\nmsgstr \"源文件以 **UTF-8** 编码。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:24\nmsgid \"Source file structure\"\nmsgstr \"源文件结构\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:26\nmsgid \"A source file consists of, **in order**:\"\nmsgstr \"源文件包括， **按顺序**：\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:28\nmsgid \"License or copyright information, if present\"\nmsgstr \"许可或版权信息（如果存在）\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:30\nmsgid \"Package statement\"\nmsgstr \"包装声明\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:32\n#: ../../Tigase_Development/CodeStyleGuide.rst:43\nmsgid \"Import statements\"\nmsgstr \"导入语句\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:34\nmsgid \"Exactly one top-level class\"\nmsgstr \"正是一个顶级类\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:36\nmsgid \"Additionally:\"\nmsgstr \"此外：\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:38\nmsgid \"**Exactly one blank line** separates sections 2-4;\"\nmsgstr \"**正好一个空行** 分隔第 2-4 节；\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:40\nmsgid \"\"\n\"The package statement is **not line-wrapped** (column limit does not \"\n\"apply);\"\nmsgstr \"package 声明是 **非自动换行** （列限制不适用）；\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:45\nmsgid \"Wildcard imports can be used for:\"\nmsgstr \"通配符导入可用于：\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:47\nmsgid \"more than 5 class imports;\"\nmsgstr \"多于5个类导入；\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:49\nmsgid \"more than 3 name imports;\"\nmsgstr \"超过3个名称导入；\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:51\nmsgid \"import statements are **not line-wrapped** (column limit does not apply);\"\nmsgstr \"import 语句 **不是自动换行** （列限制不适用）；\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:53\nmsgid \"following import ordering applies:\"\nmsgstr \"以下导入顺序适用：\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:55\nmsgid \"all imports not pertaining to any of the groups listed below\"\nmsgstr \"所有不属于下列任何组的导入\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:57\n#: ../../Tigase_Development/CodeStyleGuide.rst:63\nmsgid \"``blank line``\"\nmsgstr \"``blank line``\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:59\nmsgid \"``javax.*`` classes\"\nmsgstr \"``javax.*`` 类\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:61\nmsgid \"``java.*`` classes\"\nmsgstr \"``java.*`` 类\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:65\nmsgid \"all static imports in single block\"\nmsgstr \"单个块中的所有静态导入\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:67\nmsgid \"\"\n\"items in each block are ordered by names in ASCII sort order (since ``;``\"\n\" sorts before ``.``)\"\nmsgstr \"每个块中的项目按名称按 ASCII 排序顺序排序（因为 ``;`` 排序在 ``.`` 之前）\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:70\nmsgid \"Class declaration\"\nmsgstr \"类声明\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:72\nmsgid \"Each top-level class resides in a source file of its own.\"\nmsgstr \"每个顶层类都驻留在自己的源文件中。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:75\nmsgid \"Class contents order\"\nmsgstr \"类内容顺序\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:77\nmsgid \"Following order of the elements of the class is mandatory:\"\nmsgstr \"以下类元素的顺序是强制性的：\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:79\nmsgid \"``final``, ``static`` fields in following order:\"\nmsgstr \"``final``, ``static`` 字段按以下顺序：\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:81\n#: ../../Tigase_Development/CodeStyleGuide.rst:93\n#: ../../Tigase_Development/CodeStyleGuide.rst:105\n#: ../../Tigase_Development/CodeStyleGuide.rst:115\nmsgid \"``public``\"\nmsgstr \"``public``\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:83\n#: ../../Tigase_Development/CodeStyleGuide.rst:95\n#: ../../Tigase_Development/CodeStyleGuide.rst:107\n#: ../../Tigase_Development/CodeStyleGuide.rst:117\nmsgid \"``protected``\"\nmsgstr \"``protected``\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:85\n#: ../../Tigase_Development/CodeStyleGuide.rst:97\n#: ../../Tigase_Development/CodeStyleGuide.rst:109\n#: ../../Tigase_Development/CodeStyleGuide.rst:119\nmsgid \"``package-private``\"\nmsgstr \"``package-private``\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:87\n#: ../../Tigase_Development/CodeStyleGuide.rst:99\n#: ../../Tigase_Development/CodeStyleGuide.rst:111\n#: ../../Tigase_Development/CodeStyleGuide.rst:121\nmsgid \"``private``\"\nmsgstr \"``private``\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:89\nmsgid \"``public`` ``enum``\"\nmsgstr \"``public`` ``enum``\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:91\nmsgid \"``static`` fields in following order:\"\nmsgstr \"``static`` 字段按以下顺序：\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:101\nmsgid \"``static`` initializer block\"\nmsgstr \"``static`` 初始化程序块\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:103\nmsgid \"``final`` fields in following order:\"\nmsgstr \"``final`` 字段按以下顺序：\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:113\nmsgid \"fields without modifiers in following order:\"\nmsgstr \"没有修饰符的字段按以下顺序：\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:123\nmsgid \"initializer block\"\nmsgstr \"初始化程序块\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:125\nmsgid \"``static`` method(s)\"\nmsgstr \"``static`` 方法\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:127\nmsgid \"constructor(s)\"\nmsgstr \"构造函数\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:129\nmsgid \"methods(s) without modifiers\"\nmsgstr \"没有修饰符的方法\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:131\nmsgid \"enums(s) without modifiers\"\nmsgstr \"没有修饰符的枚举\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:133\nmsgid \"interfaces(s) without modifiers\"\nmsgstr \"没有修饰符的接口\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:135\nmsgid \"inner ``static`` classes\"\nmsgstr \"内部 ``static`` 类\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:137\nmsgid \"inner classes\"\nmsgstr \"内部类\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:139\nmsgid \"In addition:\"\nmsgstr \"此外：\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:141\nmsgid \"Getters and Setters are kept together\"\nmsgstr \"Getter和Setter保持在一起\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:143\nmsgid \"\"\n\"Overloads are never split - multiple constructors or methods with the \"\n\"same name appear sequentially.\"\nmsgstr \"超载永远不会拆分 - 具有相同名称的多个构造函数或方法按顺序出现。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:146\nmsgid \"Formatting\"\nmsgstr \"格式化\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:149\nmsgid \"Braces\"\nmsgstr \"大括号\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:151\nmsgid \"\"\n\"Braces are mandatory in optional cases - for all syntax where braces use \"\n\"can be optional, Tigase mandate using braces even if the body is empty or\"\n\" contains only single statement.\"\nmsgstr \"在可选情况下大括号是强制性的 - 对于使用大括号的所有语法都是可选的，即使主体为空或仅包含单个语句，Tigase也要求使用大括号。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:153\nmsgid \"\"\n\"Braces follow the Kernighan and Ritchie style (`Egyptian brackets \"\n\"<http://www.codinghorror.com/blog/2012/07/new-programming-\"\n\"jargon.html>`__):\"\nmsgstr \"\"\n\"大括号遵循 Kernighan 和 Ritchie 样式 (`Egyptian brackets \"\n\"<http://www.codinghorror.com/blog/2012/07/new-programming-\"\n\"jargon.html>`__):\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:155\nmsgid \"No line break before the opening brace.\"\nmsgstr \"在左大括号之前没有换行符。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:157\nmsgid \"Line break after the opening brace.\"\nmsgstr \"左大括号后的换行符。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:159\nmsgid \"Line break before the closing brace.\"\nmsgstr \"右大括号之前的换行符。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:161\nmsgid \"\"\n\"Line break after the closing brace, *only if* that brace terminates a \"\n\"statement or terminates the body of a method, constructor, or *named* \"\n\"class. For example, there is *no* line break after the brace if it is \"\n\"followed by ``else`` or a comma.\"\nmsgstr \"\"\n\"右大括号后的换行符，*仅当* 该大括号用于终止语句或终止方法，构造函数或 *named* 类的主体。例如，如果大括号后跟 ``else`` \"\n\"或逗号，则大括号后有 *no* 换行符。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:165\nmsgid \"Block indentation: tab\"\nmsgstr \"块缩进：制表符\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:167\nmsgid \"\"\n\"All indentation (opening a new block of block-like construct) must be \"\n\"made with tabs. After the block, then indent returns to the previous.\"\nmsgstr \"所有缩进（打开一个新的块状结构块）必须使用制表符进行。在块之后，然后缩进返回到前一个。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:169\nmsgid \"Ideal tab-size: 4\"\nmsgstr \"理想制表符个数：4\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:172\nmsgid \"Column limit: 120\"\nmsgstr \"列限制：120\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:174\nmsgid \"\"\n\"Defined column limit is 120 characters and must be line-wrapped as \"\n\"described below Java code has a column limit of 100 characters. Except as\"\n\" noted below, any line that would exceed this limit must be line-wrapped,\"\n\" as explained in section :ref:`Line-wrapping<linewrapping>`.\"\nmsgstr \"\"\n\"定义的列限制为 120 个字符，并且必须按如下所述进行换行， Java 代码的列限制为 \"\n\"100 个字符。除下文所述外，任何超出此限制的行都必须换行，如 :ref:`Line-\"\n\"wrapping<linewrapping>` 部分所述。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:179\nmsgid \"Line-wrapping\"\nmsgstr \"换行\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:181\nmsgid \"\"\n\"*line-wrapping* is a process of dividing long lines that would otherwise \"\n\"go over the defined Column Limit (above). It’s recommended to wrap lines \"\n\"whenever it’s possible even if they are not longer than defined limit.\"\nmsgstr \"*换行* 是一个分割长行的过程，否则会超过定义的列限制（如上所述）。建议尽可能换行，即使它们不超过定义的限制。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:184\nmsgid \"Whitespace\"\nmsgstr \"空白\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:187\nmsgid \"Vertical Whitespace\"\nmsgstr \"垂直空白\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:189\nmsgid \"A single blank line appears:\"\nmsgstr \"出现一个空行：\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:191\nmsgid \"after package statement;\"\nmsgstr \"在包声明后；\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:193\nmsgid \"before imports;\"\nmsgstr \"在导入之前；\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:195\nmsgid \"after imports;\"\nmsgstr \"导入之后；\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:197\nmsgid \"around class;\"\nmsgstr \"围绕类；\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:199\nmsgid \"after class header;\"\nmsgstr \"在类标头之后；\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:201\nmsgid \"around field in interface;\"\nmsgstr \"围绕接口中的字段；\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:203\nmsgid \"around method in interface;\"\nmsgstr \"围绕接口中的方法；\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:205\nmsgid \"around method;\"\nmsgstr \"围绕方法；\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:207\nmsgid \"around initializer;\"\nmsgstr \"围绕初始化器；\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:209\nmsgid \"as required by other sections of this document.\"\nmsgstr \"根据本文档其他部分的要求。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:211\nmsgid \"Multiple blank lines are not permitted.\"\nmsgstr \"不允许有多个空行。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:214\nmsgid \"Horizontal whitespace\"\nmsgstr \"水平空白\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:216\nmsgid \"\"\n\"Beyond where required by the language or other style rules, and apart \"\n\"from literals, comments and Javadoc, a single ASCII space also appears in\"\n\" the following places **only**.\"\nmsgstr \"除了语言或其他样式规则要求的地方，除了文字，注释和 Javadoc，一个 ASCII 空格也 **仅** 出现在以下位置。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:218\nmsgid \"\"\n\"Separating any reserved word, such as ``if``, ``for``, ``while``, \"\n\"``switch``, ``try``, ``catch`` or ``synchronized``, from an open \"\n\"parenthesis (``(``) that follows it on that line\"\nmsgstr \"\"\n\"任何保留字，例如 ``if``, ``for``, ``while``, ``switch``, ``try``, ``catch`` 或 \"\n\"``synchronized``, 与来自该行后面的左括号 (``(``) 分开\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:220\nmsgid \"\"\n\"Separating any reserved word, such as ``else`` or ``catch``, from a \"\n\"closing curly brace (``}``) that precedes it on that line\"\nmsgstr \"将任何保留字，例如 ``else`` 或 ``catch``，与该行之前的右花括号 (``}``) 分开\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:222\nmsgid \"Before any open curly brace (``{``), with two exceptions:\"\nmsgstr \"在任何左大括号（``{``）之前，有两个例外：\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:224\nmsgid \"``@SomeAnnotation({a, b})`` (no space is used)\"\nmsgstr \"``@SomeAnnotation({a, b})`` (不使用空格）\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:226\nmsgid \"\"\n\"``String[][] x = {{\\\"foo\\\"}};`` (no space is required between ``{{``, by \"\n\"item 8 below)\"\nmsgstr \"**``String[][] x = {{\\\"foo\\\"}};`` （``{{`` 之间不需要空格，根据下面的第 8 项）\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:228\nmsgid \"\"\n\"On both sides of any binary or ternary operator. This also applies to the\"\n\" following \\\"operator-like\\\" symbols:\"\nmsgstr \"在任何二元或三元运算符的两侧。这也适用于以下 ''类似运算'' 的符号：\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:230\nmsgid \"the ampersand in a conjunctive type bound: ``<T extends Foo & Bar>``\"\nmsgstr \"合取类型界限中的 & 符号： ``<T extends Foo & Bar>``\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:232\nmsgid \"\"\n\"the pipe for a catch block that handles multiple exceptions: ``catch \"\n\"(FooException | BarException e)``\"\nmsgstr \"处理多个异常的 catch 块的管线： ``catch (FooException | BarException e)``\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:234\nmsgid \"the colon (``:``) in an enhanced ``for`` (\\\"foreach\\\") statement\"\nmsgstr \"增强的 ``for`` (\\\"foreach\\\") 语句中的冒号 (``:``)\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:236\nmsgid \"the arrow in a lambda expression: ``(String str) → str.length()``\"\nmsgstr \"lambda 表达式中的箭头：``(String str) → str.length()``\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:238\nmsgid \"**but not:**\"\nmsgstr \"**但不是：**\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:240\nmsgid \"\"\n\"the two colons (``::``) of a method reference, which is written like \"\n\"``Object::toString``\"\nmsgstr \"方法引用的两个冒号（``::``），写成 ``Object::toString``\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:242\nmsgid \"the dot separator (``.``), which is written like ``object.toString()``\"\nmsgstr \"点分隔符（``.``），写成 ``object.toString()``\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:244\nmsgid \"After ``,:;`` or the closing parenthesis (``)``) of a cast\"\nmsgstr \"在 ``,:;`` 或右括号 (``)``) 之后\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:246\nmsgid \"Between the type and variable of a declaration: ``List<String> list``\"\nmsgstr \"声明的类型和变量之间：``List<String> list``\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:249\nmsgid \"Horizontal alignment: never required\"\nmsgstr \"水平对齐：从不需要\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:251\nmsgid \"\"\n\"*Horizontal alignment* is the practice of adding a variable number of \"\n\"additional spaces in your code with the goal of making certain tokens \"\n\"appear directly below certain other tokens on previous lines.\"\nmsgstr \"*水平对齐* 是在代码中添加可变数量的附加空格的做法，目的是使某些令牌直接出现在前几行的某些其他令牌下方。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:253\nmsgid \"\"\n\"This practice is permitted, but is **never required**. It is not even \"\n\"required to *maintain* horizontal alignment in places where it was \"\n\"already used.\"\nmsgstr \"这种做法是允许的，但 **从不要求**。甚至不需要在已经使用过的地方 *保持* 水平对齐。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:256\nmsgid \"Specific constructs\"\nmsgstr \"具体构造\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:259\nmsgid \"Enum classes\"\nmsgstr \"枚举类\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:261\nmsgid \"After each comma that follows an enum constant, a line break is mandatory.\"\nmsgstr \"在枚举常量后面的每个逗号之后，必须有一个换行符。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:264\nmsgid \"Variable declarations\"\nmsgstr \"变量声明\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:266\nmsgid \"\"\n\"One variable per declaration - Every variable declaration (field or \"\n\"local) declares only one variable: declarations such as ``int a, b;`` are\"\n\" not used.\"\nmsgstr \"每个声明一个变量 - 每个变量声明（字段或本地）只声明一个变量：不使用诸如 ``int a, b;`` 之类的声明。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:268\nmsgid \"\"\n\"Declared when needed -Local variables are **not** habitually declared at \"\n\"the start of their containing block or block-like construct. Instead, \"\n\"local variables are declared close to the point they are first used \"\n\"(within reason), to minimize their scope. Local variable declarations \"\n\"typically have initializers, or are initialized immediately after \"\n\"declaration.\"\nmsgstr \"\"\n\"在需要时声明 - 局部变量 **不** \"\n\"习惯性地在其包含块或类似块的构造的开头声明。相反，局部变量在接近它们首次使用的点（在合理范围内）的时候被声明，以最小化它们的范围。局部变量声明通常具有初始化器，或者在声明后立即初始化。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:271\nmsgid \"Arrays\"\nmsgstr \"数组\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:273\nmsgid \"\"\n\"Any array initializer may *optionally* be formatted as if it were a \"\n\"\\\"block-like construct.\\\" (especially when line-wrapping need to be \"\n\"applied).\"\nmsgstr \"任何数组初始值设定项都可以 *可选地* 被格式化，就好像它是一个 \\\"类似块的构造\\\" （特别是当需要应用换行时）。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:276\nmsgid \"Naming\"\nmsgstr \"命名\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:279\nmsgid \"Rules common to all identifiers\"\nmsgstr \"所有标识符通用的规则\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:281\nmsgid \"\"\n\"Identifiers use only ASCII letters and digits, and, in a small number of \"\n\"cases noted below, underscores. Thus each valid identifier name is \"\n\"matched by the regular expression ``\\\\w+`` .\"\nmsgstr \"标识符仅使用 ASCII 字母和数字，并且在下面提到的少数情况下，使用下划线。因此，每个有效的标识符名称都由正则表达式 ``\\\\w+`` 匹配。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:284\nmsgid \"Specific Rules by identifier type\"\nmsgstr \"标识符类型的特定规则\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:286\nmsgid \"\"\n\"Package names are all lowercase, with consecutive words simply \"\n\"concatenated together (no underscores, not camel-case).\"\nmsgstr \"包的名称都是小写的，连续的单词简单地连接在一起（没有下划线，不是驼峰式）。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:288\nmsgid \"Class names are written in **UpperCamelCase**.\"\nmsgstr \"类名用 **UpperCamelCase** 书写。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:290\nmsgid \"Method names are written in **lowerCamelCase**.\"\nmsgstr \"方法名称以 **lowerCamelCase** 书写。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:292\nmsgid \"\"\n\"Constant names use ``CONSTANT_CASE``: all uppercase letters, with words \"\n\"separated by underscores.\"\nmsgstr \"常量名称使用 ``CONSTANT_CASE``：全部大写字母，单词之间用下划线分隔。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:294\nmsgid \"\"\n\"Non-constant field names (static or otherwise) are written in \"\n\"**lowerCamelCase**.\"\nmsgstr \"非常量字段名称（静态或其他）以 **lowerCamelCase** 编写。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:296\nmsgid \"\"\n\"Parameter names are written in **lowerCamelCase** (one-character \"\n\"parameter names in public methods should be avoided).\"\nmsgstr \"参数名称以 **lowerCamelCase** 书写（应避免在公共方法中使用单字符参数名称）。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:298\nmsgid \"Local variable names are written in **lowerCamelCase**.\"\nmsgstr \"局部变量名称以 **lowerCamelCase** 书写。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:301\nmsgid \"Programming Practices\"\nmsgstr \"编程实践\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:303\nmsgid \"\"\n\"A method is marked with the ``@Override`` annotation whenever it is \"\n\"legal. This includes a class method overriding a superclass method, a \"\n\"class method implementing an interface method, and an interface method \"\n\"re-specifying a super-interface method.\"\nmsgstr \"只要合法，方法就会用 ``@Override`` 注释标记。这包括重写超类方法的类方法，实现接口方法的类方法和重新指定超接口方法的接口方法。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:305\nmsgid \"\"\n\"Caught exceptions should not be ignored (and if this is a must then a log\"\n\" entry is required).\"\nmsgstr \"不应忽略抓到的异常（如果这是必须的，则需要日志条目）。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:308\nmsgid \"Javadoc\"\nmsgstr \"Javadoc\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:310\nmsgid \"blank lines should be inserted after:\"\nmsgstr \"空行应插入：\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:312\nmsgid \"description,\"\nmsgstr \"描述，\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:314\nmsgid \"parameter description,\"\nmsgstr \"参数说明，\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:316\nmsgid \"return tag;\"\nmsgstr \"返回标签；\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:318\nmsgid \"empty tag should be included for following tags:\"\nmsgstr \"以下标签应包含空标签：\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:320\nmsgid \"``@params``\"\nmsgstr \"``@params``\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:322\nmsgid \"``@return``\"\nmsgstr \"``@return``\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:324\nmsgid \"``@throws``\"\nmsgstr \"``@throws``\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:327\nmsgid \"Usage\"\nmsgstr \"用法\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:329\nmsgid \"\"\n\"At the *minimum*, Javadoc is present for every ``public`` class, and \"\n\"every ``public`` or ``protected`` member of such a class, with a few \"\n\"exceptions:\"\nmsgstr \"\"\n\"在 *最低限度*，Javadoc 存在于每个 ``public`` 类，以及此类的每个 ``public`` 或 ``protected`` \"\n\"成员，但有一些例外：\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:331\nmsgid \"\"\n\"is optional for \\\"simple, obvious\\\" methods like ``getFoo``, in cases \"\n\"where there *really and truly* is nothing else worthwhile to say but \"\n\"\\\"Returns the foo\\\".\"\nmsgstr \"对于像 ``getFoo`` 这样的 *简单和明显* 的方法是可选的，在这种情况下，*真正的* 除了\\\"返回 foo\\\"之外没有什么值得说的。\"\n\n#: ../../Tigase_Development/CodeStyleGuide.rst:333\nmsgid \"in methods that overrides a supertype method.\"\nmsgstr \"在覆盖超类型方法的方法中。\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Development/Component_Implementation.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: 2022-09-07 17:14+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/dp-component_implementation/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:2\nmsgid \"Component Development\"\nmsgstr \"组件开发\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:4\nmsgid \"\"\n\"A component in the Tigase is an entity with its own JID address. It can \"\n\"receive packets, process them, and can also generate packets.\"\nmsgstr \"Tigase中的组件是具有自己的JID地址的实体。它可以接收数据包，处理它们，也可以生成数据包。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:6\nmsgid \"\"\n\"An example of the best known components is MUC or PubSub. In Tigase \"\n\"however, almost everything is actually a component: Session Manager, s2s \"\n\"connections manager, Message Router, etc…​ Components are loaded based on\"\n\" the server configuration, new components can be loaded and activated at \"\n\"run-time. You can easily replace a component implementation and the only \"\n\"change to make is a class name in the configuration entry.\"\nmsgstr \"\"\n\"最著名的组件的一个示例是MUC或PubSub。然而，在Tigase中，几乎所有东西实际上都是一个组件：会话管理器、s2s \"\n\"连接管理器、消息路由器等……组件是根据服务器配置加载的，新组件可以在运行时加载和激活。您可以轻松替换组件实现，唯一需要更改的是配置条目中的类名。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:8\nmsgid \"\"\n\"Creating components for Tigase server is an essential part of the server \"\n\"development hence there is a lot of useful API and ready to use code \"\n\"available. This guide should help you to get familiar with the API and \"\n\"how to quickly and efficiently create your own component implementations.\"\nmsgstr \"\"\n\"为Tigase服务器创建组件是服务器开发的重要组成部分，因此有很多有用的API和现成可用的代码。本指南将帮助您熟悉API \"\n\"以及如何快速有效地创建自己的组件实现。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:10\nmsgid \":ref:`Component implementation - Lesson 1 - Basics<cil1>`\"\nmsgstr \":ref:`组件实现 - 第 1 课 - 基础<cil1>`\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:12\nmsgid \":ref:`Component implementation - Lesson 2 - Configuration<cil2>`\"\nmsgstr \":ref:`组件实现 - 第 2 课 - 配置<cil2>`\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:14\nmsgid \":ref:`Component implementation - Lesson 3 - Multi-Threading<cil3>`\"\nmsgstr \":ref:`组件实现 - 第 3 课 - 多线程<cil3>`\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:16\nmsgid \":ref:`Component implementation - Lesson 4 - Service Discovery<cil4>`\"\nmsgstr \":ref:`组件实现 - 第 4 课 - 服务发现<cil4>`\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:18\nmsgid \":ref:`Component implementation - Lesson 5 - Statistics<cil5>`\"\nmsgstr \":ref:`组件实现 - 第 5 课 - 统计<cil5>`\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:20\nmsgid \":ref:`Component implementation - Lesson 6 - Scripting Support<cil6>`\"\nmsgstr \":ref:`组件实现 - 第 6 课 - 脚本支持<cil6>`\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:22\nmsgid \":ref:`Component implementation - Lesson 7 - Data Repository<cil7>`\"\nmsgstr \":ref:`组件实现 - 第 7 课 - 数据存储库<cil7>`\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:24\nmsgid \":ref:`Component implementation - Lesson 8 - Startup Time<cil8>`\"\nmsgstr \":ref:`组件实现 - 第 8 课 - 启动时间<cil8>`\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:26\nmsgid \":ref:`Packet Filtering in Component<packetfiltering>`\"\nmsgstr \":ref:`组件中的包过滤<packetfiltering>`\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:31\nmsgid \"Component Implementation - Lesson 1 - Basics\"\nmsgstr \"组件实现 - 第 1 课 - 基础\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:33\nmsgid \"\"\n\"Creating a Tigase component is actually very simple and with broad API \"\n\"available you can create a powerful component with just a few lines of \"\n\"code. You can find detailed API description elsewhere. This series \"\n\"presents hands on lessons with code examples, teaching how to get desired\"\n\" results in the simplest possible code using existing Tigase API.\"\nmsgstr \"\"\n\"创建Tigase组件实际上非常简单，并且可以使用广泛的API，您只需几行代码即可创建功能强大的组件。您可以在其他地方找到详细的API描述。本系列通过代码示例提供实践课程，教授如何使用现有的Tigase\"\n\" API以最简单的代码获得所需的结果。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:35\nmsgid \"\"\n\"Even though all Tigase components are just implementations of the \"\n\"**ServerComponent** interface I will keep such a low level information to\"\n\" necessary minimum. Creating a new component based on just interfaces, \"\n\"while very possible, is not very effective. This guide intends to teach \"\n\"you how to make use of what is already there, ready to use with a minimal\"\n\" coding effort.\"\nmsgstr \"\"\n\"即使所有Tigase组件只是 **ServerComponent** \"\n\"接口的实现，我也会将这种低级别信息保持在必要的最低限度。仅基于接口创建新组件虽然很有可能，但不是很有效。本指南旨在教您如何利用已有的内容，只需最少的编码工作即可使用。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:37\nmsgid \"\"\n\"This is just the first lesson of the series where I cover basics of the \"\n\"component implementation.\"\nmsgstr \"这只是本系列的第一课，我将介绍组件实现的基础知识。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:39\nmsgid \"Let’s get started and create the Tigase component:\"\nmsgstr \"让我们开始创建Tigase组件：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:69\nmsgid \"\"\n\"As you can see we have 3 mandatory methods when we extends \"\n\"**AbstractKernelBasedComponent**:\"\nmsgstr \"如您所见，当我们扩展 **AbstractKernelBasedComponent** 时，我们有3个强制方法：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:71\nmsgid \"\"\n\"**String getComponentVersion()** which returns version of a component for\"\n\" logging purposes\"\nmsgstr \"**String getComponentVersion()** 返回组件的版本以用于日志记录\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:73\nmsgid \"\"\n\"**boolean isDiscoNonAdmin()** which decides if component will be visible \"\n\"for users other that server administrators\"\nmsgstr \"**boolean isDiscoNonAdmin()** 决定组件是否对服务器管理员以外的用户可见\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:75\nmsgid \"\"\n\"**void registerModules(Kernel kernel)** which allows you to register \"\n\"component modules responsible for actual processing of packets\"\nmsgstr \"**void registerModules(Kernel kernel)** 允许您注册负责实际处理数据包的组件模块\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:79\nmsgid \"\"\n\"If you decide you do not want to use modules for processing packets (even\"\n\" though we strongly suggest to use them, as thanks to modules components \"\n\"are easily extendible) you can implement one more method **void \"\n\"processPacket(Packet packet)** which will be called for every packet sent\"\n\" to a component. This method is actually logical as the main task for \"\n\"your component is processing packets.\"\nmsgstr \"\"\n\"如果您决定不想使用模块来处理数据包（尽管我们强烈建议使用它们，因为模块组件易于扩展）您可以实现另一种方法 **void \"\n\"processPacket(Packet packet)** \"\n\"这将为每个发送到组件的数据包调用。这种方法实际上是合乎逻辑的，因为您的组件的主要任务是处理数据包。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:81\nmsgid \"\"\n\"Class name for our new component is **TestComponent** and we have also \"\n\"initialized a separated logger for this class. Doing This is very useful \"\n\"as it allows us to easily find log entries created by our class.\"\nmsgstr \"\"\n\"我们的新组件的类名是 \"\n\"**TestComponent**，我们还为这个类初始化了一个单独的记录器。这样做非常有用，因为它可以让我们轻松找到我们的类创建的日志条目。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:83\nmsgid \"\"\n\"With these a few lines of code you have a fully functional Tigase \"\n\"component which can be loaded to the Tigase server; it can receive and \"\n\"process packets, shows as an element on service discovery list (for \"\n\"administrators only), responds to administrator ad-hoc commands, supports\"\n\" scripting, generates statistics, can be deployed as an external \"\n\"component, and a few other things.\"\nmsgstr \"使用这几行代码，您就拥有了一个功能齐全的Tigase组件，可以将其加载到Tigase服务器；它可以接收和处理数据包，显示为服务发现列表中的一个元素（仅适用于管理员），响应管理员临时命令，支持脚本，生成统计信息，可以部署为外部组件，以及其他一些事情。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:85\nmsgid \"\"\n\"Next important step is to create modules responsible for processing \"\n\"packets. For now let’s create module responsible for handling messages by\"\n\" appending them to log file:\"\nmsgstr \"下一个重要步骤是创建负责处理数据包的模块。现在让我们通过将消息附加到日志文件来创建负责处理消息的模块：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:107\nmsgid \"\"\n\"Instance of ``Criteria`` class returned by ``Criteria \"\n\"getModuleCriteria()`` is used by component class to decide if packet \"\n\"should be processed by this module or not. In this case we returned \"\n\"instance which matches any packet which is a **message**.\"\nmsgstr \"\"\n\"``Criteria getModuleCriteria()`` 返回的 ``Criteria`` \"\n\"类的实例被组件类用来决定包是否应该被这个模块处理。在这种情况下，我们返回的实例匹配任何作为 **消息** 的数据包。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:109\nmsgid \"\"\n\"And finally we have a very important method ``void process(Packet \"\n\"packet)`` which is main processing method of a component. If component \"\n\"will receive packet that matches criteria returned by module - this \"\n\"method will be called.\"\nmsgstr \"\"\n\"最后我们有一个非常重要的方法 ``void process(Packet \"\n\"packet)``，它是组件的主要处理方法。如果组件将接收到与模块返回的标准匹配的数据包 - 将调用此方法。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:111\nmsgid \"\"\n\"But how we can send packet from a module? **AbstractModule** contains \"\n\"method **void write(Packet packet)** which you can use to send packets \"\n\"from a component.\"\nmsgstr \"\"\n\"但是我们如何从模块发送数据包呢？ **AbstractModule** 包含方法 **void write(Packet \"\n\"packet)**，您可以使用该方法从组件发送数据包。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:113\nmsgid \"\"\n\"Before we go any further with the implementation let’s configure the \"\n\"component in Tigase server so it is loaded next time the server starts. \"\n\"Assuming our **init.tdsl** file looks like this one:\"\nmsgstr \"在我们进一步实现之前，让我们在Tigase服务器中配置组件，以便在下次服务器启动时加载它。假设我们的 **init.tdsl** 文件看起来像这样：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:129\nmsgid \"\"\n\"We can see that it already is configured to load two other components: \"\n\"**MUC** and **PubSub**. Let’s add a third - our new component to the \"\n\"configuration file by appending the following line in the properties \"\n\"file:\"\nmsgstr \"\"\n\"我们可以看到它已经配置为加载另外两个组件：**MUC** 和 **PubSub**。让我们通过在属性文件中附加以下行来添加第三个 - \"\n\"我们的新组件到配置文件：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:135\nmsgid \"Now we have to restart the server.\"\nmsgstr \"现在我们必须重新启动服务器。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:137\nmsgid \"\"\n\"There are a few ways to check whether our component has been loaded to \"\n\"the server. Probably the easiest is to connect to the server from an \"\n\"administrator account and look at the service discovery list.\"\nmsgstr \"有几种方法可以检查我们的组件是否已加载到服务器。可能最简单的方法是从管理员帐户连接到服务器并查看服务发现列表。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:139\nmsgid \"|service disco test comp admin 300|\"\nmsgstr \"|service disco test comp admin 300|\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:178\nmsgid \"service disco test comp admin 300\"\nmsgstr \"service disco test comp admin 300\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:141\nmsgid \"\"\n\"If everything goes well you should see an entry on the list similar to \"\n\"the highlighted one on the screenshot. The component description is \"\n\"\\\"*Undefined description*\\\" which is a default description and we can \"\n\"change it later on, the component default JID is: \"\n\"**test@devel.tigase.org**, where **devel.tigase.org** is the server \"\n\"domain and test is the component name.\"\nmsgstr \"\"\n\"如果一切顺利，您应该会在列表中看到与屏幕截图中突出显示的条目类似的条目。组件描述是''*Undefined \"\n\"description*''，这是一个默认描述，我们可以稍后更改它，组件默认JID是：**test@devel.tigase.org**，其中 \"\n\"**devel.tigase.org** 是服务器域，test是组件名称。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:143\nmsgid \"\"\n\"Another way to find out if the component has been loaded is by looking at\"\n\" the log files. Getting yourself familiar with Tigase log files will be \"\n\"very useful thing if you plan on developing Tigase components. So let’s \"\n\"look at the log file **logs/tigase.log.0**, if the component has been \"\n\"loaded you should find following lines in the log:\"\nmsgstr \"\"\n\"确定组件是否已加载的另一种方法是查看日志文件。如果您计划开发Tigase组件，那么熟悉Tigase日志文件将非常有用。因此，让我们查看日志文件 \"\n\"**logs/tigase.log.0**，如果组件已加载，您应该在日志中找到以下行：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:151\nmsgid \"\"\n\"If your component did not load you should first check configuration \"\n\"files. Maybe the Tigase could not find your class at startup time. Make \"\n\"sure your class is in **CLASSPATH** or copy a JAR file with your class to\"\n\" Tigase **jars/** directory.\"\nmsgstr \"\"\n\"如果您的组件没有加载，您应该首先检查配置文件。也许Tigase在启动时找不到您的类。确保您的类位于 **CLASSPATH** \"\n\"中，或者将您的课程的JAR文件复制到Tigase **jars/** 目录。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:153\nmsgid \"\"\n\"Assuming everything went well and your component is loaded by the sever \"\n\"and it shows on the service discovery list as on the screenshot above you\"\n\" can double click on it to get a window with a list of ad-hoc commands - \"\n\"administrator scripts. A window on the screenshot shows only two basic \"\n\"commands for adding and removing script which is a good start.\"\nmsgstr \"\"\n\"假设一切顺利并且您的组件已被服务器加载并且它显示在服务发现列表中，如上面的屏幕截图所示，您可以双击它以获取一个包含临时命令列表的窗口 - \"\n\"管理员脚本。屏幕截图上的一个窗口仅显示了用于添加和删除脚本的两个基本命令，这是一个好的开始。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:155\nmsgid \"|commands list test 200|\"\nmsgstr \"|commands list test 200|\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:179\nmsgid \"commands list test 200\"\nmsgstr \"commands list test 200\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:157\nmsgid \"\"\n\"Moreover, you can browse the server statistics in the service discovery \"\n\"window to find your new test component on the list. If you click on the \"\n\"component it shows you a window with component statistics, very basic \"\n\"packets counters.\"\nmsgstr \"此外，您可以在服务发现窗口中浏览服务器统计信息，以在列表中找到您的新测试组件。如果您单击组件，它会显示一个包含组件统计信息的窗口，非常基本的数据包计数器。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:159\nmsgid \"|service disco stats 200|\"\nmsgstr \"|service disco stats 200|\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:180\nmsgid \"service disco stats 200\"\nmsgstr \"service disco stats 200\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:161\nmsgid \"\"\n\"As we can see with just a few lines of code our new component is quite \"\n\"mighty and can do a lot of things without much effort from the developer \"\n\"side.\"\nmsgstr \"正如我们只需要几行代码就可以看到的，我们的新组件非常强大，可以在开发人员方面不付出太多努力的情况下做很多事情。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:163\nmsgid \"\"\n\"Now, the time has come to the most important question. Can our new \"\n\"component do something useful, that is can it receive and process XMPP \"\n\"packets?\"\nmsgstr \"现在，到了最重要的问题的时候了。我们的新组件能否做一些有用的事情，即它可以接收和处理XMPP数据包吗？\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:165\nmsgid \"\"\n\"Let’s try it out. Using you favorite client send a message to JID: \"\n\"**test@devel.tigase.org** (assuming your server is configured for \"\n\"**devel.tigase.org** domain). You can either use kind of XML console in \"\n\"your client or just send a plain message to the component JID. According \"\n\"to our code in **process(…​)** method it should log our message. For this\"\n\" test I have sent a message with subject: \\\"*test message*\\\" and body: \"\n\"\\\"*this is a test*\\\". The log file should contain following entry:\"\nmsgstr \"\"\n\"让我们试试看。使用您最喜欢的客户端向JID发送消息：**test@devel.tigase.org** （假设您的服务器配置为 \"\n\"**devel.tigase.org** 域）。您可以在客户端中使用某种XML控制台，也可以只向组件JID发送一条简单的消息。根据我们在 \"\n\"**process(...​)** 方法中的代码，它应该记录我们的消息。对于这个测试，我发送了一条主题为：\\\"*test \"\n\"message*\\\"和正文：\\\"*this is a test*\\\"的消息。日志文件应包含以下条目：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:176\nmsgid \"\"\n\"If this is a case we can be sure that everything works as expected and \"\n\"all we now have to do is to fill the **process(…​)** method with some \"\n\"useful code.\"\nmsgstr \"如果是这种情况，我们可以确定一切都按预期工作，我们现在要做的就是用一些有用的代码填充 **process(...​)** 方法。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:185\nmsgid \"Component Implementation - Lesson 2 - Configuration\"\nmsgstr \"组件实现 - 第 2 课 - 配置\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:187\nmsgid \"\"\n\"It might be hard to tell what the first important thing you should do \"\n\"with your new component implementation. Different developers may have a \"\n\"different view on this. It seems to me however that it is always a good \"\n\"idea to give to your component a way to configure it and provide some \"\n\"runtime settings.\"\nmsgstr \"可能很难说出您应该对新组件实现做的第一件事是什么。不同的开发者可能对此有不同的看法。然而，在我看来，为您的组件提供一种配置它并提供一些运行时设置的方法总是一个好主意。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:189\nmsgid \"This guide describes how to add configuration handling to your component.\"\nmsgstr \"本指南描述了如何向您的组件添加配置处理。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:191\nmsgid \"\"\n\"To demonstrate how to implement component configuration let’s say we want\"\n\" to configure which types of packets will be logged by the component. \"\n\"There are three possible packet types: **message**, **presence** and \"\n\"**iq** and we want to be able to configure logging of any combination of \"\n\"the three. Furthermore we also want to be able to configure the text \"\n\"which is prepended to the logged message and to optionally switch secure \"\n\"login. (Secure logging replaces all packet CData with text: *CData size: \"\n\"NN* to protect user privacy.)\"\nmsgstr \"\"\n\"为了演示如何实现组件配置，假设我们要配置组件将记录哪些类型的数据包。有三种可能的数据包类型：**message**、**presence** 和 \"\n\"**iq**，我们希望能够配置这三者的任意组合的日志记录。此外，我们还希望能够配置附加到记录消息的文本，并可选择切换安全登录。 \"\n\"（安全日志将所有数据包CData替换为文本：*CData 大小：NN* 以保护用户隐私。）\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:193\nmsgid \"\"\n\"Let’s create the following private variables in our component \"\n\"**TestModule**:\"\nmsgstr \"让我们在我们的组件 **TestModule** 中创建以下私有变量：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:201\nmsgid \"\"\n\"To make them configurable we have to annote them with ``@ConfigField`` \"\n\"annotation. It requires ``desc`` field (describing configuration option) \"\n\"and has following optional properties: \\\\* ``alias`` - alternative name \"\n\"of the variable \\\\* ``allowAliasFromParent`` - specifies whether value \"\n\"from alias from parent bean should be allowed \\\\* ``type`` - specifies \"\n\"general type of the field, which impacts possible obfuscation of the \"\n\"value in the logs/\\\"config-dump\\\" file; possible values: ``Plain`` (no \"\n\"changes), ``Password`` (complete value will be obfuscated) and \"\n\"``JdbcUrl`` (only password part will be obfuscated from the URL)\"\nmsgstr \"\"\n\"为了使它们可配置，我们必须使用 ``@ConfigField`` 注释来注释它们。它需要 ``desc`` \"\n\"字段（描述配置选项）并具有以下可选属性： \\\\* ``alias`` - 变量的替代名称 \\\\* ``allowAliasFromParent``\"\n\" - 指定来自父bean的别名的值是否应该是allowed \\\\* ``type`` - 指定字段的一般类型，这会影响日志/\\\"config-\"\n\"dump\\\"文件中值的可能混淆；可能的值： ``Plain`` （不更改）、``Password`` （完整的值将被混淆）和 \"\n\"``JdbcUrl`` （只有密码部分将从URL中混淆）\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:212\nmsgid \"\"\n\"And this is it. Tigase Kernel will take care of this fields and will \"\n\"update them when configuration will change.\"\nmsgstr \"就是这样。 Tigase内核将处理这些字段，并在配置更改时更新它们。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:214\nmsgid \"\"\n\"The syntax in ``config.tdsl`` file is very simple and is described in \"\n\"details in the *Admin Guide*. To set the configuration for your component\"\n\" in ``config.tdsl`` file you have to append following lines to the file \"\n\"inside test component configuration block:\"\nmsgstr \"\"\n\"``config.tdsl`` 文件中的语法非常简单，在 *Admin Guide* 中有详细描述。要在 ``config.tdsl`` \"\n\"文件中为您的组件设置配置，您必须将以下行附加到测试组件配置块内的文件中：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:224\nmsgid \"\"\n\"The square brackets are used to mark that we set a list consisting of a \"\n\"few elements, have a look at the *Admin Guide* documentation for more \"\n\"details.\"\nmsgstr \"方括号用于标记我们设置了一个由几个元素组成的列表，请查看 *Admin Guide* 文档以获取更多详细信息。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:226\nmsgid \"\"\n\"And this is the complete code of the new component module with a modified\"\n\" ``process(…​)`` method taking advantage of configuration settings:\"\nmsgstr \"这是新组件模块的完整代码，它利用配置设置修改了 ``process(...​)`` 方法：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:264\nmsgid \"\"\n\"Of course we can do much more useful packet processing in the \"\n\"``process(…​)`` method. This is just an example code.\"\nmsgstr \"当然，我们可以在 ``process(...​)`` 方法中进行更有用的数据包处理。这只是一个示例代码。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:268\nmsgid \"\"\n\"Here we used a setter **setPacketType(String[] packetTypes)** which is a \"\n\"setter for field **packetTypes**. Tigase Kernel will use it instead of \"\n\"assigning value directly to a field which gives up opportunity to convert\"\n\" value to different type and update other field - in our case we updated \"\n\"**CRITERIA** field which will result in change of packet types which for \"\n\"which method **void process(…​)** will be called.\"\nmsgstr \"\"\n\"这里我们使用了一个setter **setPacketType(String[] packetTypes)**， 它是一个字段 \"\n\"**packetTypes** 的setter。 \"\n\"Tigase内核将使用它而不是直接将值分配给一个字段，这放弃了将值转换为不同类型并更新其他字段的机会 - 在我们的例子中，我们更新了 \"\n\"**CRITERIA** 字段，这将导致数据包类型发生变化方法 **void process(...​)** 将被调用。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:273\nmsgid \"Component Implementation - Lesson 3 - Multi-Threading\"\nmsgstr \"组件实现 - 第 3 课 - 多线程\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:275\nmsgid \"\"\n\"Multi core and multi CPU machines are very common nowadays. Your new \"\n\"custom component however, processes all packets in a single thread.\"\nmsgstr \"如今，多核和多CPU机器非常普遍。但是，您的新自定义组件在单个线程中处理所有数据包。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:277\nmsgid \"\"\n\"This is especially important if the packet processing is CPU expensive \"\n\"like, for example, SPAM checking. In such a case you could experience \"\n\"single Core/CPU usage at 100% while other Cores/CPUs are idling. Ideally,\"\n\" you want your component to use all available CPUs.\"\nmsgstr \"如果数据包处理占用大量CPU资源（例如垃圾邮件检查），这一点尤其重要。在这种情况下，您可能会遇到100%的单个核心/CPU使用率，而其他核心/CPU处于空闲状态。理想情况下，您希望您的组件使用所有可用的CPU。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:279\nmsgid \"\"\n\"Tigase API offers a very simple way to execute component’s \"\n\"``processPacket(Packet packet)`` method in multiple threads. Methods \"\n\"``int processingOutThreads()`` and ``int processingInThreads()`` returns \"\n\"number of threads assigned to the component. By default it returns just \"\n\"'1' as not all component implementations are prepared to process packets \"\n\"concurrently. By overwriting the method you can return any value you \"\n\"think is appropriate for the implementation. Please note, there are two \"\n\"methods, one is for a number of threads for incoming packets to the \"\n\"component and another for outgoing packets from the component. It used to\"\n\" be a single method but different components have different needs and the\"\n\" best performance can be achieved when the outgoing queues have a \"\n\"separate threads pool from incoming queues. Also some components only \"\n\"receive packets while other only send, therefore assigning an equal \"\n\"number of threads for both that could be a waste of resources.\"\nmsgstr \"\"\n\"Tigase API提供了一种非常简单的方法来在多个线程中执行组件的 ``processPacket(Packet packet)`` 方法。方法\"\n\" ``int processingOutThreads()`` 和 ``int processingInThreads()`` \"\n\"返回分配给组件的线程数。默认情况下，它只返回 \"\n\"'1'，因为并非所有组件实现都准备好同时处理数据包。通过覆盖该方法，您可以返回您认为适合实现的任何值。请注意，有两种方法，一种是用于将数据包传入组件的多个线程，另一种是用于从组件传出的数据包。它曾经是一种单一的方法，但不同的组件有不同的需求，当传出队列与传入队列有一个单独的线程池时，可以实现最佳性能。此外，一些组件只接收数据包，而其他组件只发送，因此为两者分配相同数量的线程可能会浪费资源。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:283\nmsgid \"\"\n\"Due to how Kernel works you MUST avoid using variables in those methods. \"\n\"If you would like to have this configurable at startup time you could \"\n\"simply set ``processing-in-threads`` and ``processing-out-threads`` in \"\n\"your component’s bean configuration.\"\nmsgstr \"\"\n\"由于内核的工作方式，您必须避免在这些方法中使用变量。如果你想在启动时进行配置，你可以简单地在组件的bean配置中设置 ``processing-\"\n\"in-threads`` 和 ``processing-out-threads``。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:285\nmsgid \"\"\n\"If the packet processing is CPU bound only, you normally want to have as \"\n\"many threads as there are CPUs available:\"\nmsgstr \"如果数据包处理仅受CPU限制，您通常希望拥有与可用CPU一样多的线程：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:298\nmsgid \"\"\n\"If the processing is I/O bound (network or database) you probably want to\"\n\" have more threads to process requests. It is hard to guess the ideal \"\n\"number of threads right on the first try. Instead you should run a few \"\n\"tests to see how many threads is best for implementation of the \"\n\"component.\"\nmsgstr \"如果处理受I/O限制（网络或数据库），您可能希望有更多线程来处理请求。在第一次尝试时很难猜出理想的线程数。相反，您应该运行一些测试来查看有多少线程最适合组件的实现。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:300\nmsgid \"\"\n\"Now you have many threads for processing your packets, but there is one \"\n\"slight problem with this. In many cases packet order is essential. If our\"\n\" ``processPacket(…​)`` method is executed concurrently by a few threads \"\n\"it is quite possible that a message sent to user can takeover the message\"\n\" sent earlier. Especially if the first message was large and the second \"\n\"was small. We can prevent this by adjusting the method responsible for \"\n\"packet distribution among threads.\"\nmsgstr \"\"\n\"现在你有很多线程来处理你的数据包，但是这有一个小问题。在许多情况下，数据包顺序是必不可少的。如果我们的 \"\n\"``processPacket(...​)`` \"\n\"方法由几个线程并发执行，那么发送给用户的消息很可能会接管之前发送的消息。特别是如果第一条消息很大而第二条消息很小。我们可以通过调整负责在线程之间分配数据包的方法来防止这种情况。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:302\nmsgid \"The algorithm for packets distribution among threads is very simple:\"\nmsgstr \"线程间数据包分发的算法非常简单：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:308\nmsgid \"\"\n\"So the key here is using the ``hashCodeForPacket(…​)`` method. By \"\n\"overwriting it we can make sure that all packets addressed to the same \"\n\"user will always be processed by the same thread:\"\nmsgstr \"\"\n\"所以这里的关键是使用 ``hashCodeForPacket(...​)`` \"\n\"方法。通过覆盖它，我们可以确保发送给同一用户的所有数据包将始终由同一线程处理：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:329\nmsgid \"\"\n\"The above two methods give control over the number of threads assigned to\"\n\" the packets processing in your component and to the packet distribution \"\n\"among threads. This is not all Tigase API has to offer in terms of multi-\"\n\"threading.\"\nmsgstr \"上述两种方法可以控制分配给组件中数据包处理的线程数以及线程之间的数据包分配。这并不是Tigase API在多线程方面必须提供的全部。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:331\nmsgid \"\"\n\"Sometimes you want to perform some periodic actions. You can of course \"\n\"create Timer instance and load it with TimerTasks. As there might be a \"\n\"need for this, every level of the Class hierarchy could end-up with \"\n\"multiple Timer (threads in fact) objects doing similar job and using \"\n\"resources. There are a few methods which allow you to reuse common Timer \"\n\"object to perform all sorts of actions.\"\nmsgstr \"\"\n\"有时你想执行一些周期性的动作。您当然可以创建Timer实例并使用TimerTasks加载它。由于可能需要这样做，因此类层次结构的每一级都可能以多个\"\n\" Timer（实际上是线程）对象完成类似的工作并使用资源而告终。有一些方法允许您重用通用 Timer对象来执行各种操作。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:333\nmsgid \"\"\n\"First, you have three methods allowing your to perform some periodic \"\n\"actions:\"\nmsgstr \"首先，您有三种方法可以让您执行一些周期性操作：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:341\nmsgid \"\"\n\"An example implementation for periodic notifications sent to some address\"\n\" could look like this one:\"\nmsgstr \"发送到某个地址的定期通知的示例实现可能如下所示：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:357\nmsgid \"\"\n\"This method sends every **notificationFrequency** minute a message to \"\n\"**abuseAddress** reporting how many spam messages have been detected \"\n\"during last period. Please note, you have to call ``super.everyMinute()``\"\n\" to make sure other actions are executed as well and you have to also \"\n\"remember to keep processing in this method to minimum, especially if you \"\n\"overwrite ``everySecond()`` method.\"\nmsgstr \"\"\n\"此方法每 **notificationFrequency** 分钟向 **abuseAddress** \"\n\"发送一条消息，报告在最后一段时间内检测到多少垃圾邮件。请注意，您必须调用 ``super.everyMinute()`` \"\n\"以确保其他操作也被执行，并且您还必须记住将此方法中的处理保持在最低限度，特别是如果您覆盖 ``everySecond()`` 方法。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:359\nmsgid \"\"\n\"There is also a method which allow you to schedule tasks executed at \"\n\"certain time, it is very similar to the ``java.util.Timer`` API. The only\"\n\" difference is that we are using **ScheduledExecutorService** as a \"\n\"backend which is being reused among all levels of Class hierarchy. There \"\n\"is a separate ``ScheduledExecutorService`` for each Class instance \"\n\"though, to avoid interferences between separate components:\"\nmsgstr \"\"\n\"还有一种方法可以让您安排在特定时间执行的任务，它与 ``java.util.Timer API`` 非常相似。唯一的区别是我们使用 \"\n\"**ScheduledExecutorService** 作为后端，它在类层次结构的所有级别中被重用。但是，每个Class实例都有一个单独的 \"\n\"``ScheduledExecutorService``，以避免不同组件之间的干扰：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:365\nmsgid \"\"\n\"Here is a code of an example component and module which uses all the API \"\n\"discussed in this article:\"\nmsgstr \"以下是使用本文讨论的所有API的示例组件和模块的代码：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:367\n#: ../../Tigase_Development/Component_Implementation.rst:620\n#: ../../Tigase_Development/Component_Implementation.rst:830\n#: ../../Tigase_Development/Component_Implementation.rst:1117\nmsgid \"**Example component code.**\"\nmsgstr \"**示例组件代码。**\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:429\n#: ../../Tigase_Development/Component_Implementation.rst:693\n#: ../../Tigase_Development/Component_Implementation.rst:912\n#: ../../Tigase_Development/Component_Implementation.rst:1209\nmsgid \"**Example module code.**\"\nmsgstr \"**示例模块代码。**\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:514\nmsgid \"Component Implementation - Lesson 4 - Service Discovery\"\nmsgstr \"组件实现 - 第 4 课 - 服务发现\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:516\nmsgid \"\"\n\"You component still shows in the service discovery list as an element \"\n\"with \\\"*Undefined description*\\\". It also doesn’t provide any interesting\"\n\" features or sub-nodes.\"\nmsgstr \"您的组件仍然在服务发现列表中显示为带有 \\\"*Undefined description*\\\" 的元素。它也没有提供任何有趣的功能或子节点。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:518\nmsgid \"\"\n\"In this article I will show how to, in a simple way, change the basic \"\n\"component information presented on the service discovery list and how to \"\n\"add some service disco features. As a bit more advanced feature the guide\"\n\" will teach you about adding/removing service discovery nodes at run-time\"\n\" and about updating existing elements.\"\nmsgstr \"在本文中，我将展示如何以简单的方式更改服务发现列表中显示的基本组件信息，以及如何添加一些服务disco功能。作为一个更高级的功能，该指南将教您在运行时添加/删除服务发现节点以及更新现有元素。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:520\nmsgid \"\"\n\"In order for the component to properly respond to ``disco#info`` and \"\n\"``disco#items`` request you should register ``DiscoveryModule`` in your \"\n\"component:\"\nmsgstr \"\"\n\"为了让组件正确响应 ``disco#info`` 和 ``disco#items`` 请求，你应该在你的组件中注册 \"\n\"``DiscoveryModule``：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:529\nmsgid \"\"\n\"NOTE It’s essential to **explicitly** register ``DiscoveryModule`` in \"\n\"your component.\"\nmsgstr \"注意在你的组件中 **显式** 注册 ``DiscoveryModule`` 是很重要的。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:531\nmsgid \"\"\n\"Component description and category type can be changed by overriding two \"\n\"following methods:\"\nmsgstr \"可以通过覆盖以下两个方法来更改组件描述和类别类型：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:545\nmsgid \"\"\n\"Please note, there is no such **'spam'** category type defined in the \"\n\"`Service Discovery Identities registry <http://xmpp.org/registrar/disco-\"\n\"categories.html>`__. It has been used here as a demonstration only. \"\n\"Please refer to the Service Discovery Identities registry document for a \"\n\"list of categories and types and pick the one most suitable for you.\"\nmsgstr \"\"\n\"请注意，`Service Discovery Identities registry <http://xmpp.org/registrar\"\n\"/disco-categories.html>`__ 中没有定义这样的 **'spam'** 类别类型。此处仅用作演示。请参阅Service \"\n\"Discovery Identities注册表文档以获取类别和类型的列表，然后选择最适合您的。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:547\nmsgid \"\"\n\"After you have added the two above methods and restarted the server with \"\n\"updated code, have a look at the service discovery window. You should see\"\n\" something like on the screenshot.\"\nmsgstr \"添加上述两种方法并使用更新的代码重新启动服务器后，请查看服务发现窗口。您应该会在屏幕截图上看到类似的内容。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:549\nmsgid \"|spam filtering disco small|\"\nmsgstr \"|spam filtering disco small|\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:788\nmsgid \"spam filtering disco small\"\nmsgstr \"spam filtering disco small\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:551\nmsgid \"\"\n\"Now let’s add method which will allow our module ``TestModule`` to return\"\n\" supported features. This way our component will automatically report \"\n\"features supported by all it’s modules. To do so we need to implement a \"\n\"method **String[] getFeatures()** which returns array of ``String`` \"\n\"items. This items are used to generate a list of features supported by \"\n\"component.\"\nmsgstr \"\"\n\"现在让我们添加允许我们的模块 ``TestModule`` \"\n\"返回支持的功能的方法。这样，我们的组件将自动报告其所有模块支持的功能。为此，我们需要实现一个方法 **String[] \"\n\"getFeatures()**，它返回 ``String`` 项的数组。此项用于生成组件支持的功能列表。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:553\nmsgid \"\"\n\"Although this was easy, this particular change doesn’t affect anything \"\n\"apart from just a visual appearance. Let’s get then to more advanced and \"\n\"more useful changes.\"\nmsgstr \"虽然这很容易，但这种特殊的变化除了视觉外观之外不会影响任何东西。然后让我们进行更高级和更有用的更改。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:555\nmsgid \"\"\n\"One of the limitations of methods above is that you can not update or \"\n\"change component information at run-time with these methods. They are \"\n\"called only once during initialization of a component when component \"\n\"service discovery information is created and prepared for later use. \"\n\"Sometimes, however it is useful to be able to change the service \"\n\"discovery during run-time.\"\nmsgstr \"上述方法的限制之一是您无法使用这些方法在运行时更新或更改组件信息。它们在组件初始化期间仅在创建组件服务发现信息并准备以后使用时调用一次。但是，有时能够在运行时更改服务发现很有用。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:557\nmsgid \"\"\n\"In our simple spam filtering component let’s show how many messages have \"\n\"been checked out as part of the service discovery description string. \"\n\"Every time we receive a message we can to call:\"\nmsgstr \"在我们简单的垃圾邮件过滤组件中，让我们显示有多少消息已作为服务发现描述字符串的一部分被检出。每次我们收到消息时，我们都可以调用：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:563\nmsgid \"\"\n\"*A small performance note, in some cases calling \"\n\"``updateServiceDiscoveryItem(…​)`` might be an expensive operation so \"\n\"probably a better idea would be to call the method not every time we \"\n\"receive a message but maybe every 100 times or so.*\"\nmsgstr \"\"\n\"*一个小的性能说明，在某些情况下调用* ``updateServiceDiscoveryItem(...​)`` \"\n\"*可能是一个昂贵的操作，所以一个更好的主意可能不是每次我们收到消息时调用该方法，而是每100次左右调用一次。*\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:565\nmsgid \"\"\n\"The first parameter is the component JID presented on the service \"\n\"discovery list. However, Tigase server may work for many virtual hosts so\"\n\" the hostname part is added by the lower level functions and we only \"\n\"provide the component name here. The second parameter is the service \"\n\"discovery node which is usually '**null**' for top level disco elements. \"\n\"Third is the item description (which is actually called 'name' in the \"\n\"disco specification). The last parameter specifies if the element is \"\n\"visible to administrators only.\"\nmsgstr \"\"\n\"第一个参数是服务发现列表上显示的组件 \"\n\"JID。但是，Tigase服务器可能适用于许多虚拟主机，因此主机名部分由较低级别的函数添加，我们在这里只提供组件名称。第二个参数是服务发现节点，对于顶级disco元素，它通常是'**null**'\"\n\" 。第三是项目描述（在disco规范中实际上称为 'name'）。最后一个参数指定元素是否仅对管理员可见。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:567\nmsgid \"|spam filter counter small|\"\nmsgstr \"|spam filter counter small|\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:789\nmsgid \"spam filter counter small\"\nmsgstr \"spam filter counter small\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:569\nmsgid \"\"\n\"The complete method code is presented below and the screenshot above \"\n\"shows how the element of the service discovery for our component can \"\n\"change if we apply our code and send a few messages to the component.\"\nmsgstr \"完整的方法代码如下所示，上面的屏幕截图显示了如果我们应用我们的代码并向组件发送一些消息，我们的组件的服务发现元素会如何变化。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:571\nmsgid \"\"\n\"Using the method we can also add submodes to our component element. The \"\n\"XMPP service discovery really is not for showing application counters, \"\n\"but this case it is good enough to demonstrate the API available in \"\n\"Tigase so we continue with presenting our counters via service discovery.\"\n\" This time, instead of using 'null' as a node we put some meaningful \"\n\"texts as in example below:\"\nmsgstr \"\"\n\"使用该方法，我们还可以将子模式添加到我们的组件元素中。 \"\n\"XMPP服务发现确实不是用于显示应用程序计数器，但本例足以演示Tigase中可用的API，因此我们继续通过服务发现显示我们的计数器。这一次，我们不使用\"\n\" 'null'作为节点，而是放置一些有意义的文本，如下例所示：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:584\nmsgid \"\"\n\"Again, have a look at the full method body below for a complete code \"\n\"example. Now if we send a few messages to the component and some of them \"\n\"are spam (contain words recognized as spam) we can browse the service \"\n\"discovery of the server. Your service discovery should show a list \"\n\"similar to the one presented on the screenshot on the left.\"\nmsgstr \"同样，请查看下面的完整方法主体以获取完整的代码示例。现在，如果我们向组件发送一些消息，其中一些是垃圾邮件（包含被识别为垃圾邮件的单词），我们可以浏览服务器的服务发现。您的服务发现应该显示一个类似于左侧屏幕截图中的列表。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:586\nmsgid \"\"\n\"Of course depending on the implementation, initially there might be no \"\n\"sub-nodes under our component element if we call the \"\n\"``updateServiceDiscoveryItem(…​)`` method only when a message is \"\n\"processed. To make sure that sub-nodes of our component show from the \"\n\"very beginning you can call them in ``setProperties(…​)`` for the first \"\n\"time to populate the service discovery with initial sub-nodes.\"\nmsgstr \"\"\n\"当然，取决于实现，如果我们仅在处理消息时调用 ``updateServiceDiscoveryItem(…​)`` \"\n\"方法，最初可能在我们的组件元素下没有子节点。为了确保我们组件的子节点从一开始就显示出来，您可以第一次在 ``setProperties(…​)``\"\n\" 中调用它们，以使用初始子节点填充服务发现。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:588\nmsgid \"\"\n\"Please note, the ``updateServiceDiscoveryItem(…​)`` method is used for \"\n\"adding a new item and updating existing one. There is a separate method \"\n\"though to remove the item:\"\nmsgstr \"\"\n\"请注意，``updateServiceDiscoveryItem(...​)`` \"\n\"方法用于添加新项目和更新现有项目。虽然有一个单独的方法可以删除该项目：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:595\nmsgid \"\"\n\"Actually only two first parameters are important: the **jid** and the \"\n\"**node** which must correspond to the existing, previously created \"\n\"service discovery item.\"\nmsgstr \"实际上只有两个第一个参数很重要：**jid** 和 **node** 必须对应于现有的、先前创建的服务发现项。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:597\nmsgid \"\"\n\"There are two additional variants of the *update* method which give you \"\n\"more control over the service discovery item created. Items can be of \"\n\"different categories and types and can also present a set of features.\"\nmsgstr \"*update* 方法有两个额外的变体，可以让您更好地控制所创建的服务发现项。项目可以是不同的类别和类型，也可以呈现一组特征。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:599\nmsgid \"\"\n\"The simpler is a variant which sets a set of features for the updated \"\n\"service discovery item. There is a `document <http://xmpp.org/registrar\"\n\"/disco-features.html>`__ describing existing, registered features. We are\"\n\" creating an example which is going to be a spam filter and there is no \"\n\"predefined feature for spam filtering but for purpose of this guide we \"\n\"can invent two feature identification strings and set it for our \"\n\"component. Let’s call ``update`` method with following parameters:\"\nmsgstr \"\"\n\"更简单的是为更新的服务发现项设置一组特征的变体。有一个 `文档 <http://xmpp.org/registrar/disco-\"\n\"features.html>`__ \"\n\"描述了现有的注册功能。我们正在创建一个将成为垃圾邮件过滤器的示例，并且没有预定义的垃圾邮件过滤功能，但出于本指南的目的，我们可以发明两个特征识别字符串并将其设置为我们的组件。让我们使用以下参数调用\"\n\" ``update`` 方法：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:606\nmsgid \"\"\n\"The best place to call this method is the ``setProperties(…​)`` method so\"\n\" our component gets a proper service discovery settings at startup time. \"\n\"We have set two features for the component disco: *tigase:x:spam-filter* \"\n\"and *tigase:x:spam-reporting*. This method accepts a variable set of \"\n\"arguments so we can pass to it as many features as we need or following \"\n\"Java spec we can just pass an array of **Strings**.\"\nmsgstr \"\"\n\"调用此方法的最佳位置是 ``setProperties(...​)`` \"\n\"方法，以便我们的组件在启动时获得正确的服务发现设置。我们为组件disco设置了两个功能：*tigase:x:spam-filter* 和 \"\n\"*tigase:x:spam-\"\n\"reporting*。此方法接受一组可变参数，所以我们可以根据需要传递给它尽可能多的特性，或者按照Java规范，我们可以只传递一个 \"\n\"**Strings** 数组。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:608\nmsgid \"\"\n\"Update your code with call presented above, and restart the server. Have \"\n\"a look at the service discovery for the component now.\"\nmsgstr \"使用上述调用更新您的代码，然后重新启动服务器。现在看看组件的服务发现。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:610\nmsgid \"\"\n\"The last functionality might be not very useful for our case of the spam \"\n\"filtering component, but it is for many other cases like MUC or PubSub \"\n\"for which it is setting proper category and type for the service \"\n\"discovery item. There is a document listing all currently registered \"\n\"service discovery identities (categories and types). Again there is entry\"\n\" for spam filtering. Let’s use the *automation* category and *spam-\"\n\"filter* type and set it for our component:\"\nmsgstr \"\"\n\"最后一个功能对于我们的垃圾邮件过滤组件的情况可能不是很有用，但对于许多其他情况，例如MUC或PubSub，它正在为服务发现项设置适当的类别和类型。有一个文档列出了所有当前注册的服务发现身份（类别和类型）。还有垃圾邮件过滤条目。让我们使用\"\n\" *automation* 类别和 *spam-filter* 类型并为我们的组件设置它：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:618\nmsgid \"\"\n\"Of course all these setting can be applied to any service discovery \"\n\"create or update, including sub-nodes. And here is a complete code of the\"\n\" component:\"\nmsgstr \"当然，所有这些设置都可以应用于任何服务发现创建或更新，包括子节点。这是组件的完整代码：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:794\nmsgid \"Component Implementation - Lesson 5 - Statistics\"\nmsgstr \"组件实现 - 第 5 课 - 统计\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:796\nmsgid \"\"\n\"In most cases you’ll want to gather some run-time statistics from your \"\n\"component to see how it works, detect possible performance issues or \"\n\"congestion problems. All server statistics are exposed and are accessible\"\n\" via XMPP with ad-hoc commands, HTTP, JMX and some selected statistics \"\n\"are also available via SNMP. As a component developer you don’t have to \"\n\"do anything to expose your statistic via any of those protocols, you just\"\n\" have to provide your statistics and the admin will be able to access \"\n\"them any way he wants.\"\nmsgstr \"\"\n\"在大多数情况下，您会希望从组件中收集一些运行时统计信息，以了解它是如何工作的，检测可能的性能问题或拥塞问题。所有服务器统计信息都公开并可通过XMPP\"\n\" 使用ad-\"\n\"hoc命令访问，HTTP、JMX和一些选定的统计信息也可通过SNMP获得。作为组件开发人员，您不必做任何事情来通过任何这些协议公开您的统计信息，您只需要提供您的统计信息，管理员就可以按照他想要的任何方式访问它们。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:798\nmsgid \"\"\n\"This lesson will teach you how to add your own statistics and how to make\"\n\" sure that the statistics generation doesn’t affect application \"\n\"performance.\"\nmsgstr \"本课将教您如何添加自己的统计信息以及如何确保生成的统计信息不会影响应用程序性能。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:800\nmsgid \"|spam statitics small|\"\nmsgstr \"|spam statitics small|\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1015\nmsgid \"spam statitics small\"\nmsgstr \"spam statitics small\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:802\nmsgid \"\"\n\"Your component from the very beginning generates some statistics by \"\n\"classes it inherits. Let’s add a few statistics to our spam filtering \"\n\"component:\"\nmsgstr \"您的组件从一开始就通过它继承的类生成一些统计信息。让我们向垃圾邮件过滤组件添加一些统计信息：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:816\nmsgid \"The code should be pretty much self-explanatory.\"\nmsgstr \"代码应该是不言自明的。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:818\nmsgid \"\"\n\"You have to call ``super.getStatistics(…​)`` to update stats of the \"\n\"parent class. ``StatisticsList`` is a collection which keeps all the \"\n\"statistics in a way which is easy to update, search, and retrieve them. \"\n\"You actually don’t need to know all the implementation details but if you\"\n\" are interested please refer to the source code and JavaDoc \"\n\"documentation.\"\nmsgstr \"\"\n\"你必须调用 ``super.getStatistics(...​)`` 来更新父类的统计信息。 ``StatisticsList`` \"\n\"是一个集合，它以一种易于更新、搜索和检索它们的方式保存所有统计信息。您实际上不需要了解所有实现细节，但如果您有兴趣，请参阅源代码和JavaDoc文档。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:820\nmsgid \"\"\n\"The first parameter of the ``add(…​)`` method is the component name. All \"\n\"the statistics are grouped by the component names to make it easier to \"\n\"look at particular component data. Next is a description of the element. \"\n\"The third parameter is the element value which can be any number or \"\n\"string.\"\nmsgstr \"\"\n\"``add(...​)`` \"\n\"方法的第一个参数是组件名称。所有统计信息都按组件名称分组，以便更轻松地查看特定组件数据。接下来是元素的描述。第三个参数是元素值，可以是任意数字或字符串。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:822\nmsgid \"\"\n\"The last parameter is probably the most interesting. The idea has been \"\n\"borrowed from the logging framework. Each statistic item has importance \"\n\"level. Levels are exactly the same as for logging methods with **SEVERE**\"\n\" the most critical and **FINEST** the least important. This parameter has\"\n\" been added to improve performance and statistics retrieval. When the \"\n\"**StatisticsList** object is created it gets assigned a level requested \"\n\"by the user. If the ``add(…​)`` method is called with lower priority \"\n\"level then the element is not even added to the list. This saves network \"\n\"bandwidth, improves statistics retrieving speed and is also more clear to\"\n\" present to the end-user.\"\nmsgstr \"\"\n\"最后一个参数可能是最有趣的。这个想法是从日志框架中借来的。每个统计项目都有重要性级别。级别与 **SEVERE** 最关键和 **FINEST**\"\n\" 最不重要的日志记录方法完全相同。添加了此参数以提高性能和统计信息检索。创建 **StatisticsList** \"\n\"对象时，它会被分配用户请求的级别。如果 ``add(...​)`` \"\n\"方法以较低的优先级调用，则该元素甚至不会添加到列表中。这节省了网络带宽，提高了统计检索速度，也更清晰地呈现给最终用户。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:824\nmsgid \"\"\n\"One thing which may be a bit confusing at first is that, if there is a \"\n\"numerical element added to statistics with **0** value then the Level is \"\n\"always forced to **FINEST**. The assumption is that the administrator is \"\n\"normally not interested **zero-value** statistics, therefore unless he \"\n\"intentionally request the lowest level statistics he won’t see elements \"\n\"with **zeros**.\"\nmsgstr \"\"\n\"一开始可能有点令人困惑的是，如果有一个数字元素添加到具有 **0** 值的统计数据中，则级别总是被强制为 **FINEST**。假设管理员通常对 \"\n\"**零值** 统计信息不感兴趣，因此除非他有意请求最低级别的统计信息，否则他不会看到带有 **零值** 的元素。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:826\nmsgid \"\"\n\"The **if** statement requires some explanation too. Normally adding a new\"\n\" statistics element is not a very expensive operation so passing it with \"\n\"``add(…​)`` method at an appropriate level is enough. Sometimes, however \"\n\"preparing statistics data may be quite expensive, like reading/counting \"\n\"some records from database. Statistics can be collected quite frequently \"\n\"therefore it doesn’t make sense to collect the statistics at all if there\"\n\" not going to be used as the current level is higher then the item we \"\n\"pass anyway. In such a case it is recommended to test whether the element\"\n\" level will be accepted by the collection and if not skip the whole \"\n\"processing altogether.\"\nmsgstr \"\"\n\"**if** 语句也需要一些解释。通常添加一个新的统计元素并不是一个非常昂贵的操作，因此在适当的级别使用 ``add(...​)`` \"\n\"方法传递它就足够了。但是，有时准备统计数据可能会非常昂贵，例如从数据库中读取/计算一些记录。统计数据可以非常频繁地收集，因此如果不使用，那么收集统计数据根本没有意义，因为当前级别高于​​我们通过的项目。在这种情况下，建议测试元素级别是否会被集合接受，如果不完全跳过整个处理。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:828\nmsgid \"\"\n\"As you can see, the API for generating and presenting component \"\n\"statistics is very simple and straightforward. Just one method to \"\n\"overwrite and a simple way to pass your own counters. Below is the whole \"\n\"code of the example component:\"\nmsgstr \"如您所见，用于生成和呈现组件统计信息的API非常简单明了。只有一种覆盖方法和一种简单的方法来传递您自己的计数器。下面是示例组件的完整代码：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1020\nmsgid \"Component Implementation - Lesson 6 - Scripting Support\"\nmsgstr \"组件实现 - 第 6 课 - 脚本支持\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1022\nmsgid \"\"\n\"Scripting support is a basic API built-in to Tigase server and \"\n\"automatically available to any component at no extra resource cost. This \"\n\"framework, however, can only access existing component variables which \"\n\"are inherited by your code from parent classes. It can not access any \"\n\"data or any structures you added in your component. A little effort is \"\n\"needed to expose some of your data to the scripting API.\"\nmsgstr \"脚本支持是Tigase服务器内置的基本API，可自动用于任何组件，无需额外资源成本。但是，此框架只能访问由您的代码从父类继承的现有组件变量。它无法访问您在组件中添加的任何数据或任何结构。将您的一些数据暴露给脚本API需要一点努力。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1024\nmsgid \"\"\n\"This guide shows how to extend existing scripting API with your component\"\n\" specific data structures.\"\nmsgstr \"本指南展示了如何使用组件特定的数据结构扩展现有的脚本API。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1026\nmsgid \"\"\n\"Integrating your component implementation with the scripting API is as \"\n\"simple as the code below:\"\nmsgstr \"将您的组件实现与脚本API集成就像下面的代码一样简单：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1040\nmsgid \"\"\n\"This way you expose two the component variables: ``badWords`` and \"\n\"``whiteList`` to scripts under names the same names - two defined \"\n\"constants. You could use different names of course but it is always a \"\n\"good idea to keep things straightforward, hence we use the same variable \"\n\"names in the component and in the script.\"\nmsgstr \"\"\n\"这样，您可以将两个组件变量：``badWords`` 和 ``whiteList`` 以相同的名称暴露给脚本 - \"\n\"两个已定义的常量。您当然可以使用不同的名称，但保持简单明了总是一个好主意，因此我们在组件和脚本中使用相同的变量名称。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1042\nmsgid \"\"\n\"Almost done, almost…​ In our old implementation these two variables are \"\n\"Java arrays of ``String``. Therefore we can only change their elements \"\n\"but we can not add or remove elements from these structures inside the \"\n\"script. This is not very practical and it puts some serious limits on the\"\n\" script’s code. To overcome this problem I have changed the test \"\n\"component code to keep bad words and whitelist in ``java.util.Set`` \"\n\"collection. This gives us enough flexibility to manipulate data.\"\nmsgstr \"\"\n\"几乎完成了，几乎……​在我们的旧实现中，这两个变量是 ``String`` \"\n\"的Java数组。因此，我们只能更改它们的元素，但不能在脚本内的这些结构中添加或删除元素。这不是很实用，它对脚本的代码施加了一些严重的限制。为了克服这个问题，我更改了测试组件代码以在\"\n\" ``java.util.Set`` 集合中保留坏词和白名单。这给了我们足够的灵活性来操作数据。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1044\nmsgid \"\"\n\"As our component is now ready to cooperate with the scripting API, I will\"\n\" demonstrate now how to add remove or change elements of these \"\n\"collections using a script and ad-hoc commands.\"\nmsgstr \"由于我们的组件现在已准备好与脚本API合作，我现在将演示如何使用脚本和临时命令添加删除或更改这些集合的元素。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1046\nmsgid \"|test comp newscript|\"\nmsgstr \"|test comp newscript|\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1312\nmsgid \"test comp newscript\"\nmsgstr \"test comp newscript\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1048\nmsgid \"\"\n\"First, browse the server service discovery and double click on the test \"\n\"component. If you use `Psi <http://psi-im.org/>`__ client this should \"\n\"bring to you a new window with ad-hoc commands list. Other clients may \"\n\"present available ad-hoc commands differently.\"\nmsgstr \"\"\n\"首先，浏览服务器服务发现并双击测试组件。如果您使用 `Psi <http://psi-im.org/>`__ \"\n\"客户端，这应该会为您带来一个带有临时命令列表的新窗口。其他客户端可能会以不同的方式呈现可用的临时命令。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1050\nmsgid \"\"\n\"The screenshot below shows how this may look. You have to provide some \"\n\"description for the script and an ID string. We use Groovy in this guide \"\n\"but you can as well use any different scripting language.\"\nmsgstr \"下面的屏幕截图显示了它的外观。您必须为脚本和ID字符串提供一些描述。我们在本指南中使用Groovy，但您也可以使用任何不同的脚本语言。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1052\nmsgid \"|badwords list script|\"\nmsgstr \"|badwords list script|\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1313\nmsgid \"badwords list script\"\nmsgstr \"badwords list script\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1054\nmsgid \"\"\n\"Please refer to the Tigase scripting documentation for all the details \"\n\"how to add support for more languages. From the Tigase API point of view \"\n\"it all looks the same. You have to select a proper language from the \"\n\"pull-down list on windows shown on the right. If your preferred language \"\n\"is not on the list, it means it is not installed properly and Tigase is \"\n\"unable to detect it.\"\nmsgstr \"\"\n\"有关如何添加对更多语言的支持的所有详细信息，请参阅Tigase脚本文档。从Tigase \"\n\"API的角度来看，一切看起来都一样。您必须从右侧显示的窗口的下拉列表中选择适当的语言。如果您的首选语言不在列表中，则表示它没有正确安装并且Tigase无法检测到它。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1056\nmsgid \"\"\n\"The script to pull a list of current bad words can be as simple as the \"\n\"following Groovy code:\"\nmsgstr \"提取当前坏词列表的脚本可以像以下Groovy代码一样简单：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1065\nmsgid \"\"\n\"As you see from the code, you have to reference your component variables \"\n\"to a variables in your script to make sure a correct type is used. The \"\n\"rest is very simple and is a pure scripting language stuff.\"\nmsgstr \"正如您从代码中看到的，您必须将组件变量引用到脚本中的变量，以确保使用正确的类型。剩下的就很简单了，是纯脚本语言的东西。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1067\nmsgid \"\"\n\"Load the script on to the server and execute it. You should receive a new\"\n\" window with a list of all bad words currently used by the spam filter.\"\nmsgstr \"将脚本加载到服务器并执行它。您应该会收到一个新窗口，其中列出了垃圾邮件过滤器当前使用的所有坏词。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1069\nmsgid \"\"\n\"Below is another simple script which allows updating (adding/removing) \"\n\"bad words from the list.\"\nmsgstr \"下面是另一个简单的脚本，它允许从列表中更新（添加/删除）坏词。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1111\nmsgid \"\"\n\"These two scripts are just the beginning. The possibilities are endless \"\n\"and with the simple a few lines of code in your test component you can \"\n\"then extend your application at runtime with scripts doing various \"\n\"things; you can reload scripts, add and remove them, extending and \"\n\"modifying functionality as you need. No need to restart the server, no \"\n\"need to recompile the code and you can use whatever scripting language \"\n\"you like.\"\nmsgstr \"这两个脚本只是开始。可能性是无穷无尽的，只需在测试组件中添加几行简单的代码，您就可以在运行时通过执行各种操作的脚本来扩展您的应用程序；您可以重新加载脚本，添加和删除它们，根据需要扩展和修改功能。无需重新启动服务器，无需重新编译代码，您可以使用任何您喜欢的脚本语言。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1113\nmsgid \"\"\n\"Of course, scripts for whitelist modifications would look exactly the \"\n\"same and it doesn’t make sense to attach them here.\"\nmsgstr \"当然，白名单修改的脚本看起来完全一样，在这里附加它们是没有意义的。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1115\nmsgid \"\"\n\"Here is a complete code of the test component with the new method \"\n\"described at the beginning and data structures changed from array of \"\n\"**String*s to Java \\\\*Set**:\"\nmsgstr \"这是测试组件的完整代码，其中包含开头描述的新方法，数据结构从 **String*s 数组更改为 Java \\\\*Set**：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1318\nmsgid \"Component Implementation - Lesson 7 - Data Repository\"\nmsgstr \"组件实现 - 第7课 - 数据存储库\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1321\nmsgid \"ConfigRepository\"\nmsgstr \"ConfigRepository\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1323\nmsgid \"\"\n\"There are cases when you want to store some data permanently by your \"\n\"component. You can of course use the component configuration to provide \"\n\"some database connection settings, implement your own database connector \"\n\"and store records you need. There is, however, a very simple and useful \"\n\"framework which allows you to read and store some data transparently in \"\n\"either a database or a disk file. The framework also supports ad-hoc \"\n\"command interface straight away so you can manipulate your component data\"\n\" using an XMPP client.\"\nmsgstr \"\"\n\"在某些情况下，您希望组件永久存储一些数据。您当然可以使用组件配置来提供一些数据库连接设置，实现自己的数据库连接器并存储您需要的记录。但是，有一个非常简单且有用的框架，它允许您在数据库或磁盘文件中透明地读取和存储一些数据\"\n\"。该框架还直接支持ad-hoc命令接口，因此您可以使用XMPP客户端操作组件数据。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1325\nmsgid \"\"\n\"In order to use it one needs to extend \"\n\"``tigase.db.comp.ConfigRepository`` abstract class.\"\nmsgstr \"为了使用它，需要扩展 ``tigase.db.comp.ConfigRepository`` 抽象类。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1328\nmsgid \"Accessing UserRepository or AuthRepository\"\nmsgstr \"访问UserRepository或AuthRepository\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1330\nmsgid \"\"\n\"To use **AuthRepository** or **UserRepository** you need only to declare \"\n\"fields properly and annotated them with **@Inject**. This fields must be \"\n\"part of a class managed by Tigase Kernel - class of a component or any \"\n\"class annotated with **@Bean** annotation. For that classes proper \"\n\"instances of repositories will be injected by dependency injection.\"\nmsgstr \"\"\n\"要使用 **AuthRepository** 或 **UserRepository**，您只需正确声明字段并使用 **@Inject** \"\n\"注释它们。该字段必须是由Tigase Kernel管理的类的一部分 - 组件的类或使用 **@Bean** \"\n\"注释的任何类。对于该类，将通过依赖注入来注入适当的存储库实例。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1332\nmsgid \"**Example usage of AuthRepository and UserRepository.**\"\nmsgstr \"**AuthRepository和UserRepository 的示例用法。**\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1342\nmsgid \"Accessing other repositories\"\nmsgstr \"访问其他存储库\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1344\nmsgid \"\"\n\"In order to have more freedom while accessing repositories it’s possible \"\n\"to create and use custom repository implementation which implements \"\n\"**DataSourceAware** interface.\"\nmsgstr \"为了在访问存储库时拥有更多自由，可以创建和使用实现 **DataSourceAware** 接口的自定义存储库实现。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1346\nmsgid \"\"\n\"For our example let’s assume it will be class implementing \"\n\"**TestRepositoryIfc** and our implementation will be using JDBC. To make \"\n\"it work, we need to define ``TestRepositoryIfc`` as a generic interface \"\n\"extending ``DataSourceAware`` interface. ``DataSourceAware`` interface \"\n\"will provide definition for methods required by Tigase XMPP Server \"\n\"internals to initialize custom repository classes based on \"\n\"``TestRepositoryIfc``.\"\nmsgstr \"\"\n\"对于我们的示例，假设它将是实现 **TestRepositoryIfc** 的类，并且我们的实现将使用JDBC。为了使其工作，我们需要将 \"\n\"``TestRepositoryIfc`` 定义为扩展 ``DataSourceAware`` 接口的通用接口。 \"\n\"``DataSourceAware`` 接口将为Tigase XMPP服务器内部所需的方法提供定义，以基于 \"\n\"``TestRepositoryIfc`` 初始化自定义存储库类。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1348\nmsgid \"**TestRepositoryIfc.**\"\nmsgstr \"**TestRepositoryIfc.**\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1357\nmsgid \"\"\n\"Next we need to prepare our actual implementation of repository - class \"\n\"responsible for execution of SQL statements. In this class we need to \"\n\"implement all of methods from our interface and method **void \"\n\"setDataSource(DataSource dataSource)** which comes from \"\n\"**DataSourceAware** interface. In this method we need to initialize data \"\n\"source, ie. create prepared statements. We should annotate our new class \"\n\"with ``@Repository.Meta`` annotation which will allow Tigase XMPP Server \"\n\"to find this class whenever class implementing ``TestRepositoryIfc`` and \"\n\"with support for data source with jdbc URI.\"\nmsgstr \"\"\n\"接下来我们需要准备存储库的实际实现 - 负责执行SQL语句的类。在这个类中，我们需要实现我们的接口和方法 **void \"\n\"setDataSource(DataSource dataSource)** 中的所有方法，该方法来自 **DataSourceAware** \"\n\"接口。在这个方法中，我们需要初始化数据源，即创建准备好的语句。我们应该使用 ``@Repository.Meta`` \"\n\"注释来注释我们的新类，这将允许Tigase XMPP服务器在类实现 ``TestRepositoryIfc`` 并支持带有jdbc \"\n\"URI的数据源时找到这个类。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1392\nmsgid \"\"\n\"As you can see we defined type of a data source generic parameter for \"\n\"interface ``TestRepositoryIfc``. With that we make sure that only \"\n\"instance implementing ``DataRepository`` interface will be provided and \"\n\"thanks to that we do not need to cast provided instance of ``DataSource``\"\n\" to this interface before any access to data source.\"\nmsgstr \"\"\n\"如您所见，我们为接口 ``TestRepositoryIfc`` 定义了数据源通用参数的类型。有了这个，我们确保只提供实现 \"\n\"``DataRepository`` 接口的实例，并且幸亏如此我们不需要在访问数据源之前将提供的 ``DataSource`` \"\n\"实例转换为这个接口。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1394\nmsgid \"\"\n\"With that in place we need to create class which will take care of adding\"\n\" support for multi-database setup. In our case it will be \"\n\"**TestRepositoryMDBean**, which will take care of discovery of repository\"\n\" class, initialization and re-injection of data source. It is required to\"\n\" do so, as it was just mentioned our ``TestRepositoryMDBean`` will be \"\n\"responsible for initialization of ``JDBCTestRepository`` (actually this \"\n\"will be done by ``MDRepositoryBean`` which is extended by \"\n\"``TestRepositoryMDBean``.\"\nmsgstr \"\"\n\"有了这些，我们需要创建一个类来增加对多数据库设置的支持。在我们的例子中，它将是 \"\n\"**TestRepositoryMDBean**，它将负责发现存储库类、初始化和重新注入数据源。必须这样做，因为刚才提到我们的 \"\n\"``TestRepositoryMDBean`` 将负责\\n\"\n\"``JDBCTestRepository`` 的初始化（实际上这将由 ``TestRepositoryMDBean`` 扩展的 \"\n\"``MDRepositoryBean`` 完成）。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1431\nmsgid \"\"\n\"Most of this code will be the same in all implementations based on \"\n\"``MDRepositoryBeanWithStatistics``. In our case only custom method is \"\n\"**void addItem(…​)** which uses **getRepository(String domain)** method \"\n\"to retrieve correct repository for a domain. This retrieval of actual \"\n\"repository instance for a domain will need to be done for every custom \"\n\"method of ``TestRepositoryIfc``.\"\nmsgstr \"\"\n\"在所有基于 ``MDRepositoryBeanWithStatistics`` \"\n\"的实现中，大部分代码都是相同的。在我们的例子中，唯一的自定义方法是 **void addItem(...​)** 它使用 \"\n\"**getRepository(String domain)** 方法来检索域的正确存储库。需要为 ``TestRepositoryIfc`` \"\n\"的每个自定义方法完成对域的实际存储库实例的检索。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1435\nmsgid \"\"\n\"It is also possible to extend ``MDRepositoryBean`` or \"\n\"``SDRepositoryBean`` instead of ``MDRepositoryBeanWithStatistics``. \"\n\"However, if you decide to extend abstract repository bean classes without\"\n\" ``withStatistics`` suffix, then no statistics data related to usage of \"\n\"this repository will be gathered. The only change, will be that you will \"\n\"not need to pass interface class to constructor of a superclass as it is \"\n\"not needed.\"\nmsgstr \"\"\n\"也可以扩展 ``MDRepositoryBean`` 或 ``SDRepositoryBean`` 而不是 \"\n\"``MDRepositoryBeanWithStatistics``。但是，如果您决定扩展没有 ``withStatistics`` \"\n\"后缀的抽象存储库bean类，则不会收集与该存储库的使用相关的统计数据。唯一的变化是您不需要将接口类传递给超类的构造函数，因为它不需要。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1439\nmsgid \"\"\n\"As mentioned above, it is also possible to extend ``SDRepostioryBean`` \"\n\"and ``SDRepositoryBeanWithStatistics``. Methods which you would need to \"\n\"implement are the same is in case of extending \"\n\"``MDRepositoryBeanWithStatistics``, however internally \"\n\"``SDRepositoryBean`` will not have support for using different repository\"\n\" for different domain. In fact ``SDRepositoryBeanWithStatistics`` has \"\n\"only one repository instance and uses only one data source for all \"\n\"domains. The same behavior is presented by \"\n\"``MDRepositoryBeanWithStatistics`` if only single ``default`` instance of\"\n\" repository is configured. However, ``MDRepositoryBeanWithStatistics`` \"\n\"gives better flexibility and due to that usage of ``SDRepositoryBean`` \"\n\"and ``SDRepositoryBeanWithStatistics`` is discouraged.\"\nmsgstr \"\"\n\"如上所述，也可以扩展 ``SDRepostioryBean`` 和 ``SDRepositoryBeanWithStatistics``。在扩展 \"\n\"``MDRepositoryBeanWithStatistics`` \"\n\"的情况下，您需要实现的方法是相同的，但是在内部，``SDRepositoryBean`` \"\n\"将不支持为不同的域使用不同的存储库。事实上，``SDRepositoryBeanWithStatistics`` \"\n\"只有一个存储库实例，所有域只使用一个数据源。如果只配置了一个 ``default`` 存储库实例，则 \"\n\"``MDRepositoryBeanWithStatistics`` \"\n\"会呈现相同的行为。然而，``MDRepositoryBeanWithStatistics`` 提供了更好的灵活性，因此不鼓励使用 \"\n\"``SDRepositoryBean`` 和 ``SDRepositoryBeanWithStatistics``。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1441\nmsgid \"\"\n\"While this is more difficult to implement than in previous version, it \"\n\"gives you support for multi database setup and provides you with \"\n\"statistics of database query times which may be used for diagnosis.\"\nmsgstr \"虽然这比以前的版本更难实现，但它为您提供了对多数据库设置的支持，并为您提供了可用于诊断的数据库查询时间的统计信息。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1443\nmsgid \"\"\n\"As you can also see, we’ve annotated **TestRepositoryMDBean** with \"\n\"**@Bean** annotation which will force Tigase Kernel to load it every time\"\n\" **TestComponent** will be loaded. This way it is possible to inject \"\n\"instance of this class as a dependency to any bean used by this component\"\n\" (ie. component, module, etc.) by just creating a field and annotating \"\n\"it:\"\nmsgstr \"\"\n\"正如您还可以看到的，我们使用 **@Bean** 注释对 **TestRepositoryMDBean** \"\n\"进行了注释，这将强制Tigase内核在每次加载 **TestComponent** \"\n\"时加载它。这样，只需创建一个字段并对其进行注释，就可以将此类的实例作为对该组件（即组件、模块等）使用的任何bean的依赖项注入：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1452\nmsgid \"\"\n\"In **testRepository** field instance of **TestRepositoryMDBean** will be \"\n\"injected.\"\nmsgstr \"**TestRepositoryMDBean** 的 **testRepository** 字段实例将被注入。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1456\nmsgid \"\"\n\"If the class in which we intend to use our repository is deeply nested \"\n\"within Kernel dependencies and we want to leverage automatic schema \"\n\"versioning we have to implement ``tigase.kernel.beans.RegistrarBean`` in \"\n\"our class!\"\nmsgstr \"\"\n\"如果我们打算使用我们的存储库的类深深嵌套在内核依赖项中，并且我们想要利用自动模式版本控制，我们必须在我们的类中实现 \"\n\"``tigase.kernel.beans.RegistrarBean`` ！\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1459\nmsgid \"Configuration\"\nmsgstr \"配置\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1461\nmsgid \"\"\n\"Our class ``TestRepositoryMDBean`` is annotated with ``@Bean`` which sets\"\n\" its name as ``repository`` and sets parent as ``TestComponent``. \"\n\"Instance of this component was configured by use under name of ``test`` \"\n\"in Tigase XMPP Server configuration file. As a result, all configuration \"\n\"related to our repositories should be placed in ``repository`` section \"\n\"placed inside ``test`` section.\"\nmsgstr \"\"\n\"我们的类 ``TestRepositoryMDBean`` 用 ``@Bean`` 注释，将其名称设置为 ``repository`` \"\n\"并将parent 设置为 ``TestComponent``。该组件的实例是通过在Tigase XMPP服务器配置文件中的 ``test`` \"\n\"名称下使用来配置的。因此，与我们的存储库相关的所有配置都应该放在 ``test`` 部分的 ``repository`` 部分中。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1463\nmsgid \"**Example.**\"\nmsgstr \"**示例。**\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1474\nmsgid \"Defaults\"\nmsgstr \"默认值\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1476\nmsgid \"\"\n\"As mentioned above, if we use ``MDRepositoryBeanWithStatistics`` as our \"\n\"base class for ``TestRepositoryMDBean``, then we may have different data \"\n\"sources used for different domains. By default, if we will not configure \"\n\"it otherwise, ``MDRepositoryBeanWithStatistics`` will create only single \"\n\"repository instance named ``default``. It will be used for all domains \"\n\"and it will, by default, use data source named the same as repository \"\n\"instance - it will use data source named ``default``. This defaults are \"\n\"equal to following configuration entered in the config file:\"\nmsgstr \"\"\n\"如上所述，如果我们使用 ``MDRepositoryBeanWithStatistics`` 作为 \"\n\"``TestRepositoryMDBean`` \"\n\"的基类，那么我们可能有不同的数据源用于不同的域。默认情况下，如果我们不配置它，``MDRepositoryBeanWithStatistics``\"\n\" 将只创建一个名为``default`` 的存储库实例。它将用于所有域，默认情况下，它将使用与存储库实例命名相同的数据源 - 它将使用名为 \"\n\"``default`` 的数据源。此默认值等于在配置文件中输入的以下配置：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1489\nmsgid \"Changing data source used by repository\"\nmsgstr \"更改存储库使用的数据源\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1491\nmsgid \"\"\n\"It is possible to make any repository use different data source than data\"\n\" source configured under the same name as repository instance. To do so, \"\n\"you need to set ``dataSourceName`` property of repository instance to the\"\n\" name of data source which it should use.\"\nmsgstr \"\"\n\"可以使任何存储库使用与以与存储库实例相同的名称配置的数据源不同的数据源。为此，您需要将存储库实例的 ``dataSourceName`` \"\n\"属性设置为它应该使用的数据源的名称。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1493\nmsgid \"\"\n\"**Example setting repository ``default`` to use data source named \"\n\"``test``.**\"\nmsgstr \"**示例设置存储库** ``default`` **以使用名为** ``test`` **的数据源。**\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1506\nmsgid \"Configuring separate repository for domain\"\nmsgstr \"为域配置单独的存储库\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1508\nmsgid \"\"\n\"To configure repository instance to be used for particular domain, you \"\n\"need to define repository with the same name as domain for which it \"\n\"should be used. It will, by default, use data source with name equal \"\n\"domain name.\"\nmsgstr \"要配置要用于特定域的存储库实例，您需要定义与应该使用它的域同名的存储库。默认情况下，它将使用名称与域名相同的数据源。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1510\nmsgid \"\"\n\"**Separate repository for ``example.com`` using data source named \"\n\"``example.com``.**\"\nmsgstr \"**使用名为** ``example.com`` **的数据源为** ``example.com`` **单独存储库。**\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1533\nmsgid \"\"\n\"**Separate repository for ``example.com`` using data source named \"\n\"``test``.**\"\nmsgstr \"**使用名为** ``test`` **的数据源为** ``example.com`` **单独存储库。**\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1559\nmsgid \"\"\n\"In both examples presented above, for domains other than ``example.com``,\"\n\" repository instance named ``default`` will be used and it will use data \"\n\"source named ``default``.\"\nmsgstr \"\"\n\"在上面介绍的两个示例中，对于 ``example.com`` 以外的域，将使用名为 ``default`` 的存储库实例，并将使用名为 \"\n\"``default`` 的数据源。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1562\nmsgid \"Repository Versioning\"\nmsgstr \"存储库版本控制\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1564\nmsgid \"\"\n\"It’s also possible to enable repository versioning capabilities when \"\n\"creating custom implementation. There are a couple of parts/steps to \"\n\"fully take advantage of this mechanism.\"\nmsgstr \"在创建自定义实现时，还可以启用存储库版本控制功能。有几个部分/步骤可以充分利用这种机制。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1566\nmsgid \"\"\n\"Each ``DataSource`` has a table ``tig_schema_versions`` which contains \"\n\"information about component schema version installed in the database \"\n\"associated with particular DataSource.\"\nmsgstr \"\"\n\"每个 ``DataSource`` 都有一个 ``tig_schema_versions`` \"\n\"表，其中包含有关安装在与特定DataSource关联的数据库中的组件模式版本的信息。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1569\nmsgid \"Enabling version checking in implementation\"\nmsgstr \"在实现中启用版本检查\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1571\nmsgid \"\"\n\"First of all, repository implementation should implement \"\n\"``tigase.db.util.RepositoryVersionAware`` interface (all it’s methods are\"\n\" defined by default) and annotate it with \"\n\"``tigase.db.Repository.SchemaId``. For example .Repository annoted with \"\n\"``SchemaId`` and implementing ``RepositoryVersionAware``\"\nmsgstr \"\"\n\"首先，存储库实现应该实现 ``tigase.db.util.RepositoryVersionAware`` \"\n\"接口（它的所有方法都是默认定义的）并用 ``tigase.db.Repository.SchemaId`` 注释它。例如 .Repository \"\n\"用 ``SchemaId`` 注释并实现 ``RepositoryVersionAware``\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1580\nmsgid \"\"\n\"This action alone will result in performing the check during Tigase XMPP \"\n\"Server startup and initialisation of repository whether tables, indexes, \"\n\"stored procedures and other elements are present in the configured data \"\n\"source in the required version. By default, required version matches the \"\n\"implementation version (obtained via call to \"\n\"``java.lang.Package.getImplementationVersion()``), however it’s possible \"\n\"to specify required version manually, either:\"\nmsgstr \"\"\n\"仅此操作将导致在Tigase \"\n\"XMPP服务器启动和存储库初始化期间执行检查表、索引、存储过程和其他元素是否存在于所需版本的配置数据源中。默认情况下，所需版本与实现版本匹配（通过调用\"\n\" ``java.lang.Package.getImplementationVersion()`` 获得），但是可以手动指定所需版本，或者：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1582\nmsgid \"\"\n\"by utilizing ``tigase.db.util.RepositoryVersionAware.SchemaVersion`` \"\n\"annotation:\"\nmsgstr \"通过利用 ``tigase.db.util.RepositoryVersionAware.SchemaVersion`` 注释:\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1593\nmsgid \"\"\n\"or by overriding ``tigase.db.util.RepositoryVersionAware.getVersion`` \"\n\"method:\"\nmsgstr \"或者通过覆盖 ``tigase.db.util.RepositoryVersionAware.getVersion`` 方法：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1604\nmsgid \"Handling wrong version and the upgrade\"\nmsgstr \"处理错误版本和升级\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1606\nmsgid \"\"\n\"To detect that version information in database is inadequate following \"\n\"logic will take place:\"\nmsgstr \"要检测数据库中的版本信息不足，将发生以下逻辑：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1608\nmsgid \"\"\n\"if there is no version information in the database the service will be \"\n\"stopped completely prompting to install the schema (either via ``update-\"\n\"schema`` or ``install-schema`` depending on user preference);\"\nmsgstr \"\"\n\"如果数据库中没有版本信息，则服务将完全停止，提示安装模式（根据用户偏好通过 ``update-schema`` 或 ``install-\"\n\"schema``）；\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1610\nmsgid \"\"\n\"if there is an information about loaded component schema version in the \"\n\"repository and the base part of the required schema version (i.e. taking \"\n\"into account only *major.minor.bugfix* part) is different from the one \"\n\"present in the repository then:\"\nmsgstr \"\"\n\"如果存储库中有关于加载的组件模式版本的信息，并且所需模式版本的基本部分（即仅考虑 *major.minor.bugfix* \"\n\"部分）与存储库中存在的不同，则：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1612\nmsgid \"\"\n\"if the required version of the component schema is *final* (i.e. non \"\n\"``SNAPSHOT``) the server will shutdown and print in the log file (namely \"\n\"``logs/tigase-console.log``) terminal error forcing the user to upgrade \"\n\"the schema;\"\nmsgstr \"\"\n\"如果所需的组件模式版本是 *final* （即非 ``SNAPSHOT``），服务器将关闭并在日志文件（即 ``logs/tigase-\"\n\"console.log``）中打印强制用户的终端错误升级架构；\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1614\nmsgid \"\"\n\"if the required version of the component schema is *non-final* (i.e. \"\n\"having ``SNAPSHOT`` part) then there will be a warning printed in the log\"\n\" file (namely ``logs/tigase-console.log``) prompting user to run the \"\n\"upgrade procedure due to possible changes in the schema but the \"\n\"*server*\\\\ **will not**\\\\ *stop*;\"\nmsgstr \"\"\n\"如果所需的组件模式版本是 *非最终版本* （即具有 ``SNAPSHOT`` 部分），那么将在日志文件（即 ``logs/tigase-\"\n\"console.log``）中打印警告提示由于架构可能发生变化，用户运行升级过程，但 *server*\\\\ **不会**\\\\ *stop*;\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1616\nmsgid \"\"\n\"Upgrade of the loaded schema in the database will be performed by \"\n\"executing:\"\nmsgstr \"将通过执行以下命令来升级数据库中加载的模式：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1622\nmsgid \"\"\n\"The above command will load current configuration, information about all \"\n\"configured data sources and enabled components, and then perform upgrade \"\n\"of the schema of each configured component in the appropriate data \"\n\"source.\"\nmsgstr \"上述命令将加载当前配置、所有已配置数据源和已启用组件的信息，然后在相应数据源中对每个已配置组件的架构进行升级。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1624\nmsgid \"\"\n\"Depending on the type of the database (or specified annotation), how the \"\n\"upgrade procedure is handled internally is slightly different.\"\nmsgstr \"根据数据库的类型（或指定的注释），内部处理升级过程的方式略有不同。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1629\nmsgid \"Relational databases (external handling)\"\nmsgstr \"关系数据库（外部处理）\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1631\nmsgid \"\"\n\"For all relational databases (MySQL, PostgreSQL, MS SQL Server, etc…) we \"\n\"highly recommend storing complete database schema in external files with \"\n\"following naming convention: \"\n\"``<database_type>-<component_name>-<version>.sql``, for example complete \"\n\"schema for our Test component version 0.0.5 intended for MySQL would be \"\n\"stored in file named ``mysql-test-0.0.5.sql``. What’s more - schema files\"\n\" must be stored under ``database/`` subdirectory in Tigase XMPP Server \"\n\"installation directory.\"\nmsgstr \"\"\n\"对于所有关系数据库（MySQL、PostgreSQL、MS SQL Server \"\n\"等），我们强烈建议使用以下命名约定将完整的数据库模式存储在外部文件中：``<database_type>-<component_name>-<version>.sql``，对于用于MySQL的测试组件版本0.0.5的示例完整模式将存储在名为\"\n\" ``mysql-test-0.0.5.sql`` 的文件中。更重要的是 - 架构文件必须存储在Tigase XMPP Server安装目录的 \"\n\"``database/`` 子目录下。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1635\nmsgid \"\"\n\"this can be controlled with ``external`` property of \"\n\"``Repository.SchemaId`` annotation, which defaults to \\\"true\\\", if set to\"\n\" ``false`` then handling will be done as described in :ref:`Relational \"\n\"databases (external handling)<relationalDatabases>`\"\nmsgstr \"\"\n\"这可以通过 ``Repository.SchemaId`` 注释的 ``external`` 属性来控制，\"\n\"默认为\\\"true\\\"，如果设置为 ``false``，那么处理将按照 \"\n\":ref:`关系数据库(外部处理)<relationalDatabases>` 中的描述进行\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1637\nmsgid \"For example:\"\nmsgstr \"例如：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1639\nmsgid \"``database/mysql-test-0.0.1.sql``\"\nmsgstr \"``database/mysql-test-0.0.1.sql``\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1641\nmsgid \"``database/mysql-test-0.0.2.sql``\"\nmsgstr \"``database/mysql-test-0.0.2.sql``\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1643\nmsgid \"``database/mysql-test-0.0.3.sql``\"\nmsgstr \"``database/mysql-test-0.0.3.sql``\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1645\nmsgid \"``database/mysql-test-0.0.4.sql``\"\nmsgstr \"``database/mysql-test-0.0.4.sql``\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1647\nmsgid \"``database/mysql-test-0.0.5.sql``\"\nmsgstr \"``database/mysql-test-0.0.5.sql``\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1649\nmsgid \"\"\n\"During the upgrade process all required schema files will be loaded in \"\n\"the ascending version order. Version range will depend on the conditions \"\n\"and will follow simple rules:\"\nmsgstr \"在升级过程中，所有必需的模式文件都将按版本升序加载。版本范围将取决于条件并遵循简单的规则：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1651\nmsgid \"\"\n\"Start of the range will start at the next version to the one currently \"\n\"loaded in the database (e.g. if the current version loaded to the \"\n\"database is ``0.0.3`` and we are deploying component version ``0.0.5`` \"\n\"then SchemaLoader will try to load schema from files: ``database/mysql-\"\n\"test-0.0.4.sql`` and ``database/mysql-test-0.0.5.sql``)\"\nmsgstr \"\"\n\"范围的开始将从数据库中当前加载的版本的下一个版本开始（例如，如果加载到数据库的当前版本是0.0.3并且我们正在部署组件版本0.0.5那么SchemaLoader将尝试从文件加载模式：``database\"\n\"/mysql-test-0.0.4.sql`` 和 ``database/mysql-test-0.0.5.sql``）\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1653\nmsgid \"\"\n\"If we are trying to deploy a *SNAPSTHOT* version of the component then \"\n\"schema file matching that version will always be included in the list of \"\n\"files to be loaded (e.g. if we are trying to deploy a nightly build with \"\n\"component version ``0.0.5-SNAPSHOT`` and currently loaded schema version \"\n\"in the database is ``0.0.5`` then SchemaLoader will include ``database\"\n\"/mysql-test-0.0.5.sql`` in the list of files to be loaded)\"\nmsgstr \"\"\n\"如果我们尝试部署组件的 *SNAPSTHOT* \"\n\"版本，则与该版本匹配的模式文件将始终包含在要加载的文件列表中（例如，如果我们尝试部署组件版本为0.0.0的夜间构建。 \"\n\"``0.0.5-SNAPSHOT`` 并且当前在数据库中加载的模式版本是 ``0.0.5`` \"\n\"那么SchemaLoader将在要加载的文件列表中包含数据库 ``database/mysql-test-0.0.5.sql`` ）\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1655\nmsgid \"\"\n\"It’s also possible to skip above filtering logic and force loading all \"\n\"schema files for particular component/database from ``database/`` \"\n\"directory by appending ``--forceReloadAllSchemaFiles=true`` parameter to \"\n\"the ``upgrade-schema``/``install-schema`` command.\"\nmsgstr \"\"\n\"也可以跳过上面的过滤逻辑并通过将 ``--forceReloadAllSchemaFiles=true`` 参数附加到 ``upgrade-\"\n\"schema``/``install-schema``来强制从``database/`` 目录加载特定组件/数据库的所有架构文件。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1658\nmsgid \"Non-relational databases (internal handling)\"\nmsgstr \"非关系数据库（内部处理）\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1660\nmsgid \"\"\n\"If there is a need to handle database schema internally (for example for \"\n\"cases like NoSQL databases or simply there is such preference) then it’s \"\n\"possible to do so by setting ``external`` attribute of \"\n\"``Repository.SchemaId`` annotation to ``false``:\"\nmsgstr \"\"\n\"如果需要在内部处理数据库模式（例如对于像NoSQL数据库这样的情况，或者只是有这样的偏好），那么可以通过将 \"\n\"``Repository.SchemaId`` 注释的 ``external`` 属性设置为 ``false``：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1666\nmsgid \"\"\n\"In such case, ``updateSchema`` method from \"\n\"``tigase.db.util.RepositoryVersionAware`` interface should be implemented\"\n\" to handle installation/updating of the schema. It takes two arguments:\"\nmsgstr \"\"\n\"在这种情况下，应该实现来自 ``tigase.db.util.RepositoryVersionAware`` 接口的 \"\n\"``updateSchema`` 方法来处理架构的安装/更新。它需要两个参数：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1668\nmsgid \"\"\n\"``Optional<Version> oldVersion`` - indicating current version of the \"\n\"schema loaded to the database (if it’s present)\"\nmsgstr \"``Optional<Version> oldVersion`` - 指示加载到数据库的架构的当前版本（如果存在）\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1670\nmsgid \"\"\n\"``Version newVersion`` - indicating required version (either version of \"\n\"component or specific version of the repository)\"\nmsgstr \"``Version newVersion`` - 表明所需的版本（组件的版本或存储库的特定版本）\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1673\nmsgid \"Setting required repository version in database\"\nmsgstr \"在数据库中设置所需的存储库版本\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1675\nmsgid \"\"\n\"Each versioned schema file should consist at the end code responsible for\"\n\" setting appropriate version of the loaded schema in the form of Stored \"\n\"Procedure call with the name of the component and the version as \"\n\"parameters:\"\nmsgstr \"每个版本化的架构文件应在末尾包含负责以存储过程调用的形式设置加载架构的适当版本的代码，其中组件的名称和版本作为参数：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1677\nmsgid \"Postgresql\"\nmsgstr \"Postgresql\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1685\nmsgid \"MsSQL Server\"\nmsgstr \"MsSQL服务器\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1694\nmsgid \"MySQL\"\nmsgstr \"MySQL\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1702\nmsgid \"Derby\"\nmsgstr \"Derby\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1710\nmsgid \"\"\n\"In case of schema handled internally, after successful load (i.e. \"\n\"execution of the implemented \"\n\"``tigase.db.util.RepositoryVersionAware.updateSchema`` method returning \"\n\"``tigase.db.util.SchemaLoader.Result.ok``) the version in the database \"\n\"will be set to the current version of the component.\"\nmsgstr \"\"\n\"在内部处理架构的情况下，成功加载后（即执行已实现的 \"\n\"``tigase.db.util.RepositoryVersionAware.updateSchema`` 方法返回 \"\n\"``tigase.db.util.SchemaLoader.Result.ok`` ）数据库中的版本将设置为组件的当前版本。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1712\nmsgid \"\"\n\"This allows (in case of schema handled externally) to load it by hand by \"\n\"directly importing ``.sql`` files into database.\"\nmsgstr \"这允许（在外部处理模式的情况下）通过直接将 ``.sql`` 文件导入数据库来手动加载它。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1717\nmsgid \"Component Implementation - Lesson 8 - Lifecycle of a component\"\nmsgstr \"组件实现 - 第8课 - 组件的生命周期\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1720\nmsgid \"Initialization of a component\"\nmsgstr \"组件的初始化\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1722\nmsgid \"A startup hook in the Tigase is different from the shutdown hook.\"\nmsgstr \"Tigase中的启动挂钩与关闭挂钩不同。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1724\nmsgid \"\"\n\"This is because you cannot really tell when exactly the startup time is. \"\n\"Is it when the application started, is it when configuration is loaded, \"\n\"is it when all objects are initialized. And this might be even different \"\n\"for each component. Therefore, in fact, there is no startup hook in \"\n\"Tigase in the same sense as the shutdown hook.\"\nmsgstr \"这是因为您无法真正确定启动时间的确切时间。是在应用程序启动时，是在加载配置时，还是在初始化所有对象时。对于每个组件，这甚至可能有所不同。因此，实际上Tigase中并没有与关闭挂钩相同意义的启动挂钩。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1726\nmsgid \"\"\n\"There are a few methods which are called at startup time of a component \"\n\"in the following order:\"\nmsgstr \"有几个方法在组件启动时按以下顺序调用：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1728\nmsgid \"\"\n\"**Constructor** - there is of course constructor which has no parameters.\"\n\" However it does not guarantee that this instance of the component will \"\n\"be used at all. The object could be created just to get default values of\"\n\" a config fields and may be destroyed afterwards.\"\nmsgstr \"**构造函数** - 当然有没有参数的构造函数。但是，它不保证组件的这个实例将被使用。可以创建该对象只是为了获取配置字段的默认值，然后可能会被销毁。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1730\nmsgid \"\"\n\"**Getters/Setters** - at second step of initialization of a component, \"\n\"Kernel configures component by reading and setting values of fields \"\n\"annotated with ``@ConfigField()`` annotation. If there is a public getter\"\n\" or setter for the same name as an annotated field - it will be used.\"\nmsgstr \"\"\n\"**Getters/Setters** - 在组件初始化的第二步，内核通过读取和设置带有 ``@ConfigField()`` \"\n\"注释的字段的值来配置组件。如果有一个与注释字段同名的公共getter或setter - 它将被使用。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1732\nmsgid \"\"\n\"**void beanConfigurationChanged(Collection<String> changedFields)** \"\n\"*(optional)* - if component implements ``ConfigurationChangedAware`` \"\n\"interface, this method will be called to notify component about fields \"\n\"which values were changed. It is useful if case in which component \"\n\"internals depends on configuration stored in more than one field, as it \"\n\"allows you to reconfigure component internals only once.\"\nmsgstr \"\"\n\"**void beanConfigurationChanged(Collection<String> changedFields)** \"\n\"*（可选）* - 如果组件实现了 ``ConfigurationChangedAware`` \"\n\"接口，将调用此方法来通知组件哪些字段的值已更改。如果组件内部依赖于存储在多个字段中的配置，这很有用，因为它只允许您重新配置组件内部一次。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1734\nmsgid \"\"\n\"**void register(Kernel kernel)** *(optional)* - if component implements \"\n\"``RegistrarBean`` interface this method is called to allow registration \"\n\"of component private beans.\"\nmsgstr \"\"\n\"**void register(Kernel kernel)** *(optional)* - 如果组件实现 ``RegistrarBean`` \"\n\"接口，则调用此方法以允许注册组件私有bean。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1736\nmsgid \"\"\n\"**Dependency Injection** - during this time Kernel injects beans to \"\n\"component fields annotated with ``@Inject``. If public getters or setters\"\n\" for this fields exist - kernel will use them.\"\nmsgstr \"\"\n\"**依赖注入** - 在此期间，内核将bean注入到带有 ``@Inject`` 注释的组件字段中。如果此字段的公共getter或setter存在\"\n\" - 内核将使用它们。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1738\nmsgid \"\"\n\"**void initialized()** *(optional)* - called if component implements \"\n\"``Initializable`` interface to notify it that configuration is set and \"\n\"dependencies are injected.\"\nmsgstr \"\"\n\"**void initialized()** *(optional)* - 如果组件实现了 ``Initializable`` \"\n\"接口以通知它配置已设置并注入依赖项，则调用它。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1740\nmsgid \"\"\n\"**void start()** - during this call component starts it’s internal jobs \"\n\"or worker threads or whatever it needs for future activity. Component’s \"\n\"queues and threads are initialized at this point. **(after this method \"\n\"returns the component is ready)**\"\nmsgstr \"\"\n\"**void start()** - 在此调用组件启动期间，它是内部作业或工作线程或未来活动所需的任何东西。组件的队列和线程此时被初始化。 \"\n\"**（此方法返回后组件就绪）**\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1742\nmsgid \"\"\n\"Therefore, the ``start()`` hook is the best point if you want to be sure \"\n\"that component is fully loaded, initialized and functional.\"\nmsgstr \"因此，如果您想确保组件已完全加载、初始化并正常运行，那么 ``start()`` 钩子是最佳点。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1746\nmsgid \"\"\n\"Component instance may be started and stopped only once, however new \"\n\"instances of the same component with the same name may be created during \"\n\"Tigase XMPP Server uptime, ie. as a result of a server reconfiguration.\"\nmsgstr \"组件实例只能启动和停止一次，但是可以在Tigase XMPP服务器正常运行期间创建具有相同名称的相同组件的新实例，即作为服务器重新配置的结果。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1749\nmsgid \"Reconfiguration\"\nmsgstr \"重新配置\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1751\nmsgid \"\"\n\"During lifecycle of a component instance it may happen that Tigase XMPP \"\n\"Server will be reconfigured. If change in configuration of this component\"\n\" will not be related to it’s activity, then Kernel will set values of \"\n\"changes fields annotated with ``@ConfigField()``. In this case public \"\n\"field setters may be used.\"\nmsgstr \"\"\n\"在组件实例的生命周期中，可能会重新配置Tigase XMPP服务器。如果此组件的配置更改与其活动无关，则内核将设置带有 \"\n\"``@ConfigField()`` 注释的更改字段的值。在这种情况下，可以使用公共字段设置器。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1755\nmsgid \"\"\n\"If component implements ``ConfigurationChangedAware`` interface, then \"\n\"method **void beanConfigurationChanged(Collection<String> \"\n\"changedFields)** will be called to notify component about fields which \"\n\"values were changed. It is useful if same component internal depends on \"\n\"configuration stored in more than one field, as it allows you to \"\n\"reconfigure this internal once.\"\nmsgstr \"\"\n\"如果组件实现了 ``ConfigurationChangedAware`` 接口，则将调用方法 **void \"\n\"beanConfigurationChanged(Collection<String> changedFields)** \"\n\"来通知组件哪些字段的值已更改。如果同一组件内部依赖于存储在多个字段中的配置，这很有用，因为它允许您重新配置此内部一次。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1758\nmsgid \"Update of injected dependencies\"\nmsgstr \"更新注入的依赖项\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1760\nmsgid \"\"\n\"During lifecycle of a component instance it may happen that due to \"\n\"reconfiguration of a server other bean needs to be injected as a \"\n\"dependency to a component. In this case Tigase Kernel will inject \"\n\"dependencies to fields annotated with ``@Inject`` which value needs to be\"\n\" updated.\"\nmsgstr \"\"\n\"在组件实例的生命周期中，可能会发生由于重新配置服务器而需要注入其他bean作为组件的依赖项。在这种情况下，Tigase内核会将依赖项注入到带有 \"\n\"``@Inject`` 注释的字段中，该字段的值需要更新。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1763\nmsgid \"Stopping a component\"\nmsgstr \"停止组件\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1765\nmsgid \"\"\n\"Component instance may be stopped at any point of Tigase XMPP Server \"\n\"runtime, ie. due to reconfiguration, or due to server graceful shutdown.\"\nmsgstr \"组件实例可以在Tigase XMPP服务器运行时的任何时候停止，即由于重新配置，或由于服务器正常关闭。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1767\nmsgid \"In both cases following methods of a component will be called:\"\nmsgstr \"在这两种情况下，将调用组件的以下方法：\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1769\nmsgid \"**void stop()** - first method stops component internal processing queues.\"\nmsgstr \"**void stop()** - 第一种方法停止组件内部处理队列。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1771\nmsgid \"\"\n\"**void beforeUnregister()** *(optional)* - if component implements \"\n\"@UnregisterAware@ interface this method is called to notify instance of a\"\n\" component that it is being unloaded.\"\nmsgstr \"\"\n\"**void beforeUnregister()** *（可选）* - 如果组件实现了@UnregisterAware@ \"\n\"接口，则调用此方法来通知组件实例它正在被卸载。\"\n\n#: ../../Tigase_Development/Component_Implementation.rst:1773\nmsgid \"\"\n\"**void unregister(Kernel kernel)** *(optional)* - if component implements\"\n\" ``RegistrarBean`` called to give component a way to unregister beans (if\"\n\" needed).\"\nmsgstr \"\"\n\"**void unregister(Kernel kernel)** *(optional)* - 如果组件实现了 \"\n\"``RegistrarBean`` 被调用来为组件提供一种注销bean的方法（如果需要）。\"\n\n#~ msgid \"`Component implementation - Lesson 1 - Basics <#cil1>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Component implementation - Lesson 2 - Configuration <#cil2>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Component implementation - Lesson 3 - Multi-Threading <#cil3>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Component implementation - Lesson 4 - Service Discovery <#cil4>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Component implementation - Lesson 5 - Statistics <#cil5>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Component implementation - Lesson 6 - Scripting Support <#cil6>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Component implementation - Lesson 7 - Data Repository <#cil7>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Component implementation - Lesson 8 - Startup Time <#cil8>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"`Packet Filtering in Component <#packetfiltering>`__\"\n#~ msgstr \"\"\n\n#~ msgid \"_ cil1\"\n#~ msgstr \"\"\n\n#~ msgid \": _cil2:\"\n#~ msgstr \"\"\n\n#~ msgid \"_ cil3\"\n#~ msgstr \"\"\n\n#~ msgid \"_ cil4\"\n#~ msgstr \"\"\n\n#~ msgid \"_ cil5\"\n#~ msgstr \"\"\n\n#~ msgid \": _cil6:\"\n#~ msgstr \"\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Development/Data_Sources_And_Repositories.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-05-27 12:30-0700\\n\"\n\"PO-Revision-Date: 2022-09-07 17:14+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/dp-data_sources_and_repositories/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:2\nmsgid \"Data Source and Repositories\"\nmsgstr \"数据源和存储库\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:4\nmsgid \"\"\n\"In Tigase XMPP Server 8.0.0 a new concept of data sources was introduced.\"\n\" It was introduced to create distinction between classes responsible for \"\n\"maintaining connection to actual data source and classes operating on \"\n\"this data source.\"\nmsgstr \"\"\n\"在Tigase XMPP Server 8.0.0中引入了数据源的新概念。引入它是为了区分负责维护与\"\n\"实际数据源的连接的类和在该数据源上运行的类。\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:7\nmsgid \"Data sources\"\nmsgstr \"数据源\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:9\nmsgid \"|Relations between DataSourceBean and DataSources|\"\nmsgstr \"|Relations between DataSourceBean and DataSources|\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:68\nmsgid \"Relations between DataSourceBean and DataSources\"\nmsgstr \"Relations between DataSourceBean and DataSources\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:12\nmsgid \"DataSource\"\nmsgstr \"数据源\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:14\nmsgid \"\"\n\"``DataSource`` is an interface which should be implemented by all classes\"\n\" implementing access to data source, i.e. implementing access to database\"\n\" using JDBC connection or to MongoDB. Implementation of ``DataSource`` is\"\n\" automatically selected using uri provided in configuration and \"\n\"``@Repository.Meta`` annotation on classes implementing ``DataSource`` \"\n\"interface.\"\nmsgstr \"\"\n\"``DataSource`` 是一个接口，其应该由实现对数据源的访问的所有类来实现，换言之，\"\n\"使用JDBC连接实现对数据库的访问或对 MongoDB 的访问。 ``DataSource`` \"\n\"的实现是使用配置中提供的uri和实现 ``DataSource`` 接口的类上的 ``@Repository.\"\n\"Meta`` 注释自动选择的。\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:17\nmsgid \"DataSourcePool\"\nmsgstr \"DataSourcePool\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:19\nmsgid \"\"\n\"``DataSourcePool`` is interface which should be implemented by classes \"\n\"acting as a pool of data sources for single domain. There is no \"\n\"requirement to create class implementing this interface, however if \"\n\"implementation of ``DataSource`` is blocking and does not support \"\n\"concurrent requests, then creation of ``DataSourcePool`` is recommended. \"\n\"An example of such case is implementation of ``DataRepositoryImpl`` which\"\n\" executes all requests using single connection and for this class there \"\n\"is ``DataRepositoryPool`` implementing ``DataSourcePool`` interface and \"\n\"improving performance. Implementation of ``DataSourcePool`` is \"\n\"automatically selected using uri provided in configuration and \"\n\"``@Repository.Meta`` annotation on classes implementing \"\n\"``DataSourcePool`` interface.\"\nmsgstr \"\"\n\"``DataSourcePool`` 是一个接口，应该由充当单个域的数据源池的类来实现。不需要创\"\n\"建实现这个接口的类，但是如果 ``DataSource`` \"\n\"的实现是阻塞的并且不支持并发请求，那么建议创建 ``DataSourcePool``。\"\n\"这种情况的一个例子是 ``DataRepositoryImpl`` \"\n\"的实现，它使用单个连接执行所有请求，对于这个类，``DataRepositoryPool`` \"\n\"实现了 ``DataSourcePool`` 接口并提高了性能。使用配置中提供的uri和实现 \"\n\"``DataSourcePool`` 接口的类上的 ``@Repository.Meta`` 注解自动选择 \"\n\"``DataSourcePool`` 的实现。\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:22\nmsgid \"DataSourceBean\"\nmsgstr \"DataSourceBean\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:24\nmsgid \"\"\n\"This class is a helper class and provides support for handling multiple \"\n\"data sources. You can think of a ``DataSourceBean`` as a map of named \"\n\"``DataSource`` or ``DataSourcePool`` instances. This class is also \"\n\"responsible for initialization of data source. Moreover, if data source \"\n\"will change during runtime ``DataSourceBean`` is responsible for firing a\"\n\" ``DataSourceChangedEvent`` to notify other classes about this change.\"\nmsgstr \"\"\n\"该类是一个帮助类，并为处理多个数据源提供支持。您可以将 ``DataSourceBean`` \"\n\"视为命名 ``DataSource`` 或 ``DataSourcePool`` 实例的映射。该类还负责数据源的\"\n\"初始化。此外，如果数据源在运行时发生变化，``DataSourceBean`` 负责触发一个 \"\n\"``DataSourceChangedEvent`` 以通知其他类有关此变化。\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:27\nmsgid \"User and authentication repositories\"\nmsgstr \"用户和身份验证存储库\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:29\nmsgid \"\"\n\"This repositories may be using existing (configured and initialized) data\"\n\" sources. However, it is also possible to that they may have their own \"\n\"connections. Usage of data sources is recommended if possible.\"\nmsgstr \"此存储库可能正在使用现有的（配置和初始化的）数据源。然而，它们也有可能有自己\"\n\"的连接源。如果可能，建议使用数据源。\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:31\nmsgid \"|Relations between AuthRepositories and DataSources|\"\nmsgstr \"|Relations between AuthRepositories and DataSources|\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:69\nmsgid \"Relations between AuthRepositories and DataSources\"\nmsgstr \"Relations between AuthRepositories and DataSources\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:33\nmsgid \"|Relations between UserRepositories and DataSources|\"\nmsgstr \"|Relations between UserRepositories and DataSources|\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:70\nmsgid \"Relations between UserRepositories and DataSources\"\nmsgstr \"Relations between UserRepositories and DataSources\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:36\nmsgid \"AuthRepository and UserRepository\"\nmsgstr \"AuthRepository和UserRepository\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:38\nmsgid \"\"\n\"This are a base interfaces which needs to be implemented by \"\n\"authentication repository (``AuthRepository``) and by repository of users\"\n\" (``UserRepository``). Classes implementing this interfaces should be \"\n\"only responsible for retrieving data from data sources.\"\nmsgstr \"\"\n\"这是一个基本接口，需要由身份验证存储库（``AuthRepository``）和用户存储库（``U\"\n\"serRepository``）实现。实现此接口的类应该只负责从数据源检索数据。\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:41\nmsgid \"AuthRepositoryPool and UserRepositoryPool\"\nmsgstr \"AuthRepositoryPool和UserRepositoryPool\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:43\nmsgid \"\"\n\"If class implementing ``AuthRepositoryPool`` or ``UserRepositoryPool`` is\"\n\" not using data sources or contains blocking or is not good with \"\n\"concurrent access, then it should be wrapped within proper repository \"\n\"pool. Most of implementations provided as part of Tigase XMPP Server do \"\n\"not require to be wrapped within repository pool. If your implementation \"\n\"is blocking or not perform well with concurrent access (ie. due to \"\n\"synchronization), then it should be wrapped within this pool. To wrap \"\n\"implementation within a pool, you need to set ``pool-cls`` property of \"\n\"configured user or authentication repository in your configuration file.\"\nmsgstr \"\"\n\"如果实现 ``AuthRepositoryPool`` 或 ``UserRepositoryPool`` \"\n\"的类不使用数据源或包含阻塞或不适合并发访问，则应将其包装在适当的存储库池中。\"\n\"作为Tigase XMPP Server的一部分提供的大多数实现不需要包装在存储库池中。如果您\"\n\"的实现阻塞或并发访问（即由于同步）性能不佳，则应将其包装在此池中。要将实现包\"\n\"装在池中，您需要在配置文件中设置已配置用户或身份验证存储库的 ``pool-cls`` \"\n\"属性。\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:46\nmsgid \"AuthRepositoryMDPoolBean and UserRepositoryMDPoolBean\"\nmsgstr \"AuthRepositoryMDPoolBean和UserRepositoryMDPoolBean\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:48\nmsgid \"\"\n\"This classes are for classes implementing ``AuthRepository`` and \"\n\"``UserRepository`` what ``DataSourceBean`` is for classes implementing \"\n\"``DataSource`` interface. This classes holds map of named authentication \"\n\"or user repositories. They are also responsible for initialization of \"\n\"classes implementing this repositories.\"\nmsgstr \"\"\n\"这些类用于实现 ``AuthRepository`` 和 ``UserRepository`` 的类，而 \"\n\"``DataSourceBean`` 用于实现 ``DataSource`` 接口的类。此类包含命名身份验证或用\"\n\"户存储库的映射。他们还负责初始化实现此存储库的类。\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:52\nmsgid \"Other repositories\"\nmsgstr \"其他存储库\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:54\nmsgid \"\"\n\"It is possible to implement repositories not implementing \"\n\"``AuthRepository`` or ``UserRepository``. Each type of custom repository \"\n\"should have its own API and its own interface.\"\nmsgstr \"\"\n\"可以实现那些不实现 ``AuthRepository`` 或 ``UserRepository`` \"\n\"的存储库。每种类型的自定义存储库都应该有自己的API和自己的接口。\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:56\nmsgid \"|Relations between custom repositories and DataSources|\"\nmsgstr \"|Relations between custom repositories and DataSources|\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:71\nmsgid \"Relations between custom repositories and DataSources\"\nmsgstr \"Relations between custom repositories and DataSources\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:59\nmsgid \"DataSourceAware\"\nmsgstr \"DataSourceAware\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:61\nmsgid \"\"\n\"Custom repositories should implement they own interface specifying its \"\n\"API. This interfaces should extend ``DataSourceAware`` interface which is\"\n\" base interface required to be implemented by custom repositories. \"\n\"``DataSourceAware`` has a method ``setDataSource()`` which will be called\"\n\" with instance of data source to initialize instance of custom \"\n\"repository. Implementations should be annotated with ``@Repository.Meta``\"\n\" implementation to make the automatically selected for proper type of \"\n\"``DataSource`` implementation.\"\nmsgstr \"\"\n\"自定义存储库应实现它们自己的接口，指定其API。此接口应扩展 ``DataSourceAware``\"\n\" 接口，该接口是自定义存储库需要实现的基本接口。 ``DataSourceAware`` 有一个 \"\n\"``setDataSource()`` \"\n\"方法，它将与数据源实例一起调用以初始化自定义存储库的实例。实现应该用 \"\n\"``@Repository.Meta`` 实现注释，以自动选择正确类型的 ``DataSource`` 实现。\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:64\nmsgid \"MDRepositoryBean\"\nmsgstr \"MDRepositoryBean\"\n\n#: ../../Tigase_Development/Data_Sources_And_Repositories.rst:66\nmsgid \"\"\n\"It is required to create a class extending ``MDRepositoryBean`` \"\n\"implementing same custom interface as the custom repository. This class \"\n\"will be a multi domain pool, allowing you to have separate implementation\"\n\" of custom repository for each domain. Moreover, it will be responsible \"\n\"for creation and initialization of your custom repository instances.\"\nmsgstr \"\"\n\"需要创建一个扩展 ``MDRepositoryBean`` 的类，实现与自定义存储库相同的自定义接\"\n\"口。此类将是一个多域池，允许您为每个域单独实现自定义存储库。此外，它将负责创\"\n\"建和初始化您的自定义存储库实例。\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Development/EventBus_API.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: 2022-09-07 17:14+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/dg-eventbus_api/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/EventBus_API.rst:3\nmsgid \"EventBus API in Tigase\"\nmsgstr \"Tigase中的EventBus API\"\n\n#: ../../Tigase_Development/EventBus_API.rst:5\nmsgid \"\"\n\"EventBus is a custom publish-subscribe mechanism which allows for the use\"\n\" of Event Listener within Tigase Server. For a more detailed overview of \"\n\"EventBus and it’s features, please visit `The Administration Guide \"\n\"<http://docs.tigase.org/tigase-\"\n\"server/snapshot/Administration_Guide/html/#eventBus>`__.\"\nmsgstr \"\"\n\"EventBus是一种自定义发布-订阅机制，它允许在Tigase服务器中使用事件监听器。有关EventBus及其功能的更详细概述，请访问 \"\n\"`管理指南 <http://docs.tigase.org/tigase-\"\n\"server/snapshot/Administration_Guide/html/#eventBus>`__。\"\n\n#: ../../Tigase_Development/EventBus_API.rst:8\nmsgid \"EventBus API\"\nmsgstr \"EventBus API\"\n\n#: ../../Tigase_Development/EventBus_API.rst:10\nmsgid \"To create instance of EventBus use the following code:\"\nmsgstr \"要创建EventBus的实例，请使用以下代码：\"\n\n#: ../../Tigase_Development/EventBus_API.rst:18\nmsgid \"\"\n\"Remember, that EventBus is asynchronous. All handlers are called in a \"\n\"different thread than the thread that initially fired the event.\"\nmsgstr \"请记住，EventBus是异步的。所有处理程序都在与最初触发事件的线程不同的线程中调用。\"\n\n#: ../../Tigase_Development/EventBus_API.rst:21\nmsgid \"Events\"\nmsgstr \"事件\"\n\n#: ../../Tigase_Development/EventBus_API.rst:23\nmsgid \"\"\n\"Events may be defined in two ways: as a class |ss| or as an XML \"\n\"element(XML/Element based events are deprecated since version 8.2 and \"\n\"will be removed in version 9.0)\\\\. |se|\\\\\"\nmsgstr \"\"\n\"事件可以用两种方式定义：作为一个类 |ss| 或作为XML元素（自8.2版起已弃用基于XML/元素的事件，并将在9.0 版中删除）\\\\. \"\n\"|se|\\\\\"\n\n#: ../../Tigase_Development/EventBus_API.rst:25\nmsgid \"**Serialized event class.**\"\nmsgstr \"**序列化事件类。**\"\n\n#: ../../Tigase_Development/EventBus_API.rst:39\nmsgid \"**Event class.**\"\nmsgstr \"**事件类。**\"\n\n#: ../../Tigase_Development/EventBus_API.rst:53\nmsgid \"|ss| **XML Element event(deprecated)**\\\\ |se|\\\\\"\nmsgstr \"|ss| **XML元素事件（已弃用）**\\\\ |se|\\\\\"\n\n#: ../../Tigase_Development/EventBus_API.rst:63\nmsgid \"\"\n\"Events defined as XML element and class implementing ``Serializable`` \"\n\"interface will be distributed to all servers in cluster. Event \"\n\"``SampleEvent`` will be broadcast only in the same instance what fired \"\n\"the event.\"\nmsgstr \"\"\n\"定义为XML元素和实现 ``Serializable`` 接口的类的事件将分发到集群中的所有服务器。事件 ``SampleEvent`` \"\n\"将仅在触发事件的同一实例中广播。\"\n\n#: ../../Tigase_Development/EventBus_API.rst:66\nmsgid \"Requirements for class-based events\"\nmsgstr \"基于类的事件的要求\"\n\n#: ../../Tigase_Development/EventBus_API.rst:68\nmsgid \"Default, explicit, public, paremeter-less constructor is mandatory.\"\nmsgstr \"默认的、显式的、公共的、无参数的构造函数是强制性的。\"\n\n#: ../../Tigase_Development/EventBus_API.rst:70\nmsgid \"\"\n\"If the event should be delivered to all cluster nodes then it **MUST** \"\n\"implement ``Serializable`` interface.\"\nmsgstr \"如果事件应该被传递到所有集群节点，那么它 **必须** 实现 ``Serializable`` 接口。\"\n\n#: ../../Tigase_Development/EventBus_API.rst:72\nmsgid \"\"\n\"Variables serialisation follows ``Serializable`` semantics, which means \"\n\"that ``final``, ``static`` nor ``transient`` fields will be skipped. \"\n\"What’s more, fields with ``null`` value will not be serialised neither.\"\nmsgstr \"\"\n\"变量序列化遵循 ``Serializable`` 语义，这意味着 ``final``、``static`` 和 ``transient`` \"\n\"字段将被跳过。更重要的是，具有 ``null`` 值的字段也不会被序列化。\"\n\n#: ../../Tigase_Development/EventBus_API.rst:75\nmsgid \"Serialisation of class-based events\"\nmsgstr \"基于类的事件的序列化\"\n\n#: ../../Tigase_Development/EventBus_API.rst:77\nmsgid \"\"\n\"Class based events are serialized (if it is required and possible) to XML\"\n\" element. Name of XML element is taken from full name of class:\"\nmsgstr \"基于类的事件被序列化（如果需要并且可能的话）到XML元素。 XML元素的名称取自类的全名：\"\n\n#: ../../Tigase_Development/EventBus_API.rst:79\nmsgid \"**Class based event serialized to XML.**\"\nmsgstr \"**基于类的事件序列化为XML。**\"\n\n#: ../../Tigase_Development/EventBus_API.rst:88\nmsgid \"Firing events\"\nmsgstr \"触发事件\"\n\n#: ../../Tigase_Development/EventBus_API.rst:90\nmsgid \"To fire event, just get instance of EventBus and call method ``fire()``.\"\nmsgstr \"要触发事件，只需获取EventBus的实例并调用方法 ``fire()``。\"\n\n#: ../../Tigase_Development/EventBus_API.rst:92\nmsgid \"**Firing serialized event.**\"\nmsgstr \"**触发序列化事件。**\"\n\n#: ../../Tigase_Development/EventBus_API.rst:100\nmsgid \"**Firing simple event.**\"\nmsgstr \"**触发简单事件。**\"\n\n#: ../../Tigase_Development/EventBus_API.rst:108\nmsgid \"|ss| **Firing event based on XML Element(deprecated)** |se|\\\\\"\nmsgstr \"|ss| **基于XML元素的触发事件（已弃用）** |se|\\\\\"\n\n#: ../../Tigase_Development/EventBus_API.rst:117\nmsgid \"Handling events\"\nmsgstr \"处理事件\"\n\n#: ../../Tigase_Development/EventBus_API.rst:119\nmsgid \"\"\n\"To handle fired event, we have to register listener in EventBus. When \"\n\"listener is registered, EventBus automatically subscribes for this type \"\n\"of event in all instances in cluster.\"\nmsgstr \"要处理触发的事件，我们必须在EventBus中注册监听器。注册监听器后，EventBus会自动在集群中的所有实例中订阅此类事件。\"\n\n#: ../../Tigase_Development/EventBus_API.rst:121\nmsgid \"\"\n\"Depends on expected event type, we have to decide what type of listener \"\n\"we should register.\"\nmsgstr \"取决于预期的事件类型，我们必须决定我们应该注册哪种类型的监听器。\"\n\n#: ../../Tigase_Development/EventBus_API.rst:124\nmsgid \"Handling class based events\"\nmsgstr \"处理基于类的事件\"\n\n#: ../../Tigase_Development/EventBus_API.rst:126\nmsgid \"\"\n\"This option is reserved for class based events only. It doesn’t matter if\"\n\" it is serialized class or not.\"\nmsgstr \"此选项仅用于基于类的事件。是否是序列化类都没有关系。\"\n\n#: ../../Tigase_Development/EventBus_API.rst:137\nmsgid \"\"\n\"To make registering listeners more easy, you can use method \"\n\"``registerAll()`` from EventBus. This method registers all methods given \"\n\"class, annotated by ``@HandleEvent`` as listeners for event declared as \"\n\"the method argument.\"\nmsgstr \"\"\n\"为了使注册监听器更容易，您可以使用EventBus中的方法 ``registerAll()``。此方法注册给定类的所有方法，由 \"\n\"``@HandleEvent`` 注释为声明为方法参数的事件的侦听器。\"\n\n#: ../../Tigase_Development/EventBus_API.rst:154\nmsgid \"|ss| Handling XML events |se|\\\\\"\nmsgstr \"|ss| 处理XML事件 |se|\\\\\"\n\n#: ../../Tigase_Development/EventBus_API.rst:156\nmsgid \"\"\n\"To handle XML events we have to register listener for specific event \"\n\"package and name. In our example, package is empty because event name has\"\n\" no package declared (see also :ref:`Filtering events<filteringEvents>`).\"\nmsgstr \"\"\n\"要处理XML事件，我们必须为特定事件包和名称注册侦听器。在我们的示例中，包是空的\"\n\"，因为事件名称没有声明包（另请参阅 :ref:`过滤事件<filteringEvents>`）。\"\n\n#: ../../Tigase_Development/EventBus_API.rst:173\nmsgid \"\"\n\"Because serialized class events, ale transformed to XML elements, we are \"\n\"able to listen for XML representation of class based event. To do that, \"\n\"we have to register listener for specific package and class name:\"\nmsgstr \"因为序列化的类事件，ale转换为XML元素，所以我们能够侦听基于类的事件的XML表示。为此，我们必须为特定的包和类名注册监听器：\"\n\n#: ../../Tigase_Development/EventBus_API.rst:186\nmsgid \"**Important**\"\nmsgstr \"**重要**\"\n\n#: ../../Tigase_Development/EventBus_API.rst:188\nmsgid \"\"\n\"XML events created on others cluster node, will have attribute ``remote``\"\n\" set to ``true`` and attribute ``source`` set to event creator node name:\"\nmsgstr \"在其他集群节点上创建的XML事件，将属性 ``remote`` 设置为 ``true``，属性 ``source`` 设置为事件创建者节点名称：\"\n\n#: ../../Tigase_Development/EventBus_API.rst:199\nmsgid \"Filtering events\"\nmsgstr \"过滤事件\"\n\n#: ../../Tigase_Development/EventBus_API.rst:201\nmsgid \"\"\n\"Sometimes you may want to receive many kinds of events with the same \"\n\"handler. EventBus has very simple mechanism to generalization:\"\nmsgstr \"有时您可能希望使用同一个处理程序接收多种事件。 EventBus有非常简单的泛化机制：\"\n\n#: ../../Tigase_Development/EventBus_API.rst:208\nmsgid \"\"\n\"This listener will be called for each event with given package name (XML \"\n\"based, or serialized class based).\"\nmsgstr \"将为具有给定包名称（基于XML或基于序列化类）的每个事件调用此侦听器。\"\n\n#: ../../Tigase_Development/EventBus_API.rst:210\nmsgid \"\"\n\"This listener will be called for ALL events (XML based, or serialized \"\n\"class based).\"\nmsgstr \"将为所有事件（基于XML或基于序列化类）调用此侦听器。\"\n\n#: ../../Tigase_Development/EventBus_API.rst:212\nmsgid \"In case of class based events, EventBus is checking class inheritance.\"\nmsgstr \"对于基于类的事件，EventBus正在检查类继承。\"\n\n#: ../../Tigase_Development/EventBus_API.rst:224\nmsgid \"Will be called, because this is listener stricte for ``SpecificEvent``.\"\nmsgstr \"将被调用，因为这是 ``SpecificEvent`` 的严格监听器。\"\n\n#: ../../Tigase_Development/EventBus_API.rst:226\nmsgid \"Will be called, because ``SpecificEvent`` extends ``MainEvent``.\"\nmsgstr \"将被调用，因为 ``SpecificEvent`` 扩展了 ``MainEvent``。\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Development/Experimental.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: 2022-09-07 17:14+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/dg-experimental/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Experimental.rst:2\nmsgid \"Experimental\"\nmsgstr \"实验\"\n\n#: ../../Tigase_Development/Experimental.rst:4\nmsgid \"\"\n\"The guide contains description of non-standard or experimental \"\n\"functionality of the server. Some of them are based on never published \"\n\"extensions, some of them are just test implementation for new ideas or \"\n\"performance improvements.\"\nmsgstr \"该指南包含对服务器的非标准或实验性功能的描述。其中一些基于从未发布的扩展，其中一些只是测试实现以获取新想法或性能改进。\"\n\n#: ../../Tigase_Development/Experimental.rst:6\nmsgid \":ref:`Dynamic Rosters<dynamicRosters>`\"\nmsgstr \":ref:`动态名册<dynamicRosters>`\"\n\n#: ../../Tigase_Development/Experimental.rst:8\nmsgid \":ref:`Mobile Optimizations<mobileoptimizations>`\"\nmsgstr \":ref:`移动优化<mobileoptimizations>`\"\n\n#: ../../Tigase_Development/Experimental.rst:10\nmsgid \":ref:`Bosh Session Cache<boshsessioncache>`\"\nmsgstr \":ref:`Bosh会话缓存<boshsessioncache>`\"\n\n#: ../../Tigase_Development/Experimental.rst:16\nmsgid \"Dynamic Rosters\"\nmsgstr \"动态名册\"\n\n#: ../../Tigase_Development/Experimental.rst:19\n#: ../../Tigase_Development/Experimental.rst:153\n#: ../../Tigase_Development/Experimental.rst:226\nmsgid \"Problem Description\"\nmsgstr \"问题描述\"\n\n#: ../../Tigase_Development/Experimental.rst:21\nmsgid \"\"\n\"Normal roster contacts stored and created as **dynamic roster parts** are\"\n\" delivered to the end user transparently. The XMPP client doesn’t really \"\n\"know what contacts come from its own **static** roster created manually \"\n\"by the user and what contacts come from a **dynamic** roster part; \"\n\"contacts and groups generated dynamically by the server logic.\"\nmsgstr \"\"\n\"作为 **动态花名册部分** \"\n\"存储和创建的正常花名册联系人透明地交付给最终用户。XMPP客户端并不真正知道哪些联系人来自用户手动创建的自己的**静态**名册，哪些联系人来自 \"\n\"**动态** 名册部分；联系人和组由服务器逻辑动态生成的。\"\n\n#: ../../Tigase_Development/Experimental.rst:23\nmsgid \"\"\n\"Some specialized clients need to store extra bits of information about \"\n\"roster contacts. For the normal user **static** roster information can be\"\n\" stored as private data and is available only to this single user. In \"\n\"some cases however, clients need to store information about contacts from\"\n\" the dynamic roster part and this information must be available to all \"\n\"users accessing **dynamic** roster part.\"\nmsgstr \"\"\n\"一些专业客户需要存储有关名册联系人的额外信息。对于普通用户，**静态** \"\n\"名册信息可以作为私有数据存储，并且仅供该单个用户使用。然而，在某些情况下，客户端需要存储来自动态名册部分的联系人信息，并且此信息必须可供所有访问 \"\n\"**动态** 名册部分的用户使用。\"\n\n#: ../../Tigase_Development/Experimental.rst:25\nmsgid \"\"\n\"The protocol defined here allows the exchange of information, saving and \"\n\"retrieving extra data about the contacts.\"\nmsgstr \"此处定义的协议允许交换信息、保存和检索有关联系人的额外数据。\"\n\n#: ../../Tigase_Development/Experimental.rst:28\nmsgid \"Syntax and Semantics\"\nmsgstr \"句法和语义\"\n\n#: ../../Tigase_Development/Experimental.rst:30\nmsgid \"\"\n\"Extra contact data is accessed using IQ stanzas, specifically by means of\"\n\" a child element qualified by the **jabber:iq:roster-dynamic** namespace.\"\n\" The child element MAY contain one or more children, each describing a \"\n\"unique contact item. Content of the element is not specified and is \"\n\"implementation dependent. From Tigase’s point of view it can contain any \"\n\"valid XML data. Whole element is passed to the DynamicRoster \"\n\"implementation class as is and without any verification. Upon retrieving \"\n\"the contact extra data the DynamicRoster implementation is supposed to \"\n\"provide a valid XML element with all the required data for requested \"\n\"**jid**.\"\nmsgstr \"\"\n\"使用IQ节访问额外的联系人数据，特别是通过 **jabber:iq:roster-dynamic** \"\n\"命名空间限定的子元素。子元素可以包含一个或多个子元素，每个子元素描述一个独特的联系人项目。元素的内容未指定，取决于实现。从Tigase的角度来看，它可以包含任何有效的XML数据。整个元素按原样传递给DynamicRoster实现类，无需任何验证。在检索联系人额外数据时，DynamicRoster实现应该提供一个有效的XML元素，其中包含请求的\"\n\" **jid** 所需的所有数据。\"\n\n#: ../../Tigase_Development/Experimental.rst:32\nmsgid \"\"\n\"The **jid** attribute specifies the Jabber Identifier (JID) that uniquely\"\n\" identifies the roster item. Inclusion of the **jid** attribute is \"\n\"**REQUIRED**.\"\nmsgstr \"**jid** 属性指定唯一标识名册项目的Jabber标识符 (JID)。包含 **jid** 属性是 **必需的**。\"\n\n#: ../../Tigase_Development/Experimental.rst:34\nmsgid \"Following actions on the extra contact data are allowed:\"\nmsgstr \"允许对额外联系人数据执行以下操作：\"\n\n#: ../../Tigase_Development/Experimental.rst:36\nmsgid \"**set** - stores extra information about the contact\"\nmsgstr \"**set** - 存储有关联系人的额外信息\"\n\n#: ../../Tigase_Development/Experimental.rst:38\nmsgid \"**get** - retrieves extra information about the contact\"\nmsgstr \"**get** - 检索有关联系人的额外信息\"\n\n#: ../../Tigase_Development/Experimental.rst:41\nmsgid \"Retrieving Contact Data\"\nmsgstr \"检索联系人数据\"\n\n#: ../../Tigase_Development/Experimental.rst:43\nmsgid \"\"\n\"Upon connecting to the server and becoming an active resource, a client \"\n\"can request the extra contact data. This request can be made either \"\n\"before or after requesting the user roster. The client’s request for the \"\n\"extra contact data is **OPTIONAL**.\"\nmsgstr \"在连接到服务器并成为活动资源后，客户端可以请求额外的联系人数据。可以在请求用户名册之前或之后提出此请求。客户对额外联系数据的请求是 **可选的**。\"\n\n#: ../../Tigase_Development/Experimental.rst:45\nmsgid \"\"\n\"Example: Client requests contact extra data from the server using **get**\"\n\" request:\"\nmsgstr \"示例：客户端使用 **get** 请求从服务器请求联系人额外数据：\"\n\n#: ../../Tigase_Development/Experimental.rst:55\nmsgid \"\"\n\"Example: Client receives contact extra data from the server, but there \"\n\"was either no extra information for the user, or the user was not found \"\n\"in the dynamic roster:\"\nmsgstr \"示例：客户端从服务器接收到额外的联系人数据，但没有用户的额外信息，或者在动态名册中找不到用户：\"\n\n#: ../../Tigase_Development/Experimental.rst:65\nmsgid \"\"\n\"Example: Client receives contact extra data from the server, and there \"\n\"was some extra information found about the contact:\"\nmsgstr \"示例：客户端从服务器接收到联系人额外数据，并且发现了一些关于联系人的额外信息：\"\n\n#: ../../Tigase_Development/Experimental.rst:80\nmsgid \"Updating/Saving Extra Information About the Contact\"\nmsgstr \"更新/保存有关联系人的额外信息\"\n\n#: ../../Tigase_Development/Experimental.rst:82\nmsgid \"\"\n\"At any time, a client **MAY** update extra contact information on the \"\n\"server.\"\nmsgstr \"在任何时候，客户端 **可以** 更新服务器上的额外联系信息。\"\n\n#: ../../Tigase_Development/Experimental.rst:84\nmsgid \"Example: Client sends contact extra information using **set** request.\"\nmsgstr \"示例：客户端使用 **set** 请求发送联系人额外信息。\"\n\n#: ../../Tigase_Development/Experimental.rst:97\n#: ../../Tigase_Development/Experimental.rst:126\nmsgid \"Client responds to the server:\"\nmsgstr \"客户端响应服务器：\"\n\n#: ../../Tigase_Development/Experimental.rst:103\nmsgid \"\"\n\"A client **MAY** update contact extra information for more than a single \"\n\"item in one request:\"\nmsgstr \"客户 **可以** 在一个请求中更新多个项目的联系人额外信息：\"\n\n#: ../../Tigase_Development/Experimental.rst:105\nmsgid \"\"\n\"Example: Client sends contact extra information using **set** request \"\n\"with many <item/> elements.\"\nmsgstr \"示例：客户端使用带有许多<item/>元素的 **set** 请求发送联系人额外信息。\"\n\n#: ../../Tigase_Development/Experimental.rst:133\n#: ../../Tigase_Development/Experimental.rst:204\nmsgid \"Configuration\"\nmsgstr \"配置\"\n\n#: ../../Tigase_Development/Experimental.rst:135\nmsgid \"\"\n\"DynamicRoster implementation class should be configured in the \"\n\"**config.tdsl** file:\"\nmsgstr \"DynamicRoster实现类应该在 **config.tdsl** 文件中配置：\"\n\n#: ../../Tigase_Development/Experimental.rst:145\nmsgid \"\"\n\"If you want to pass configuration to your implementation simply use \"\n\"``@ConfigField`` annotation on your variable (see :ref:`Component \"\n\"implementation - Lesson 2 - Configuration<cil2>` for more details).\"\nmsgstr \"\"\n\"如果您想将配置传递给您的实现，只需在变量上使用 ``@ConfigField`` \"\n\"注释（有关详细信息，请参阅 :ref:`组件实现 - 第2课 - 配置<cil2>`）。\"\n\n#: ../../Tigase_Development/Experimental.rst:150\nmsgid \"Mobile Optimizations\"\nmsgstr \"移动优化\"\n\n#: ../../Tigase_Development/Experimental.rst:155\nmsgid \"\"\n\"In default configuration stanzas are sent to the client when processing \"\n\"is finished, but in mobile environment sending or receiving data drains \"\n\"battery due to use of the radio.\"\nmsgstr \"在默认配置中，当处理完成时，节会发送到客户端，但在移动环境中，发送或接收数据会由于使用无线电而消耗电池电量。\"\n\n#: ../../Tigase_Development/Experimental.rst:157\nmsgid \"\"\n\"To save energy data should be sent to client only if it is important or \"\n\"client is waiting for it.\"\nmsgstr \"为节省能源，数据应仅在重要或客户正在等待时才发送给客户。\"\n\n#: ../../Tigase_Development/Experimental.rst:160\nmsgid \"Solution\"\nmsgstr \"解决方案\"\n\n#: ../../Tigase_Development/Experimental.rst:162\nmsgid \"\"\n\"When mobile client is entering inactive state it notifies server about it\"\n\" by sending following stanza:\"\nmsgstr \"当移动客户端进入非活动状态时，它通过发送以下节来通知服务器：\"\n\n#: ../../Tigase_Development/Experimental.rst:172\nmsgid \"\"\n\"After receiving stanza server starts queuing stanza which should be send \"\n\"to mobile client. What kind of queued stanzas depends on the plugins used\"\n\" and in case of **Mobile v3** presence stanzas are queued as well as \"\n\"message stanzas which are Message Carbons. Any other stanza (such as iq \"\n\"or plain message) is sent immediately to the client and every stanza from\"\n\" queue is also sent at this time.\"\nmsgstr \"\"\n\"收到节后，服务器开始排队节，其应被发送到移动客户端。什么样的排队节取决于所使用的插件，如果是 **Mobile v3** \"\n\"存在节，则排队的节以及作为消息碳的消息节。任何其他节（例如iq或纯消息）都会立即发送到客户端，此时队列中的每个节都会被发送。\"\n\n#: ../../Tigase_Development/Experimental.rst:174\nmsgid \"\"\n\"When mobile client is entering active state it notifies server by sending\"\n\" following stanza:\"\nmsgstr \"当移动客户端进入活动状态时，它通过发送以下节来通知服务器：\"\n\n#: ../../Tigase_Development/Experimental.rst:184\nmsgid \"After receiving stanza server sends all queued stanzas to the client.\"\nmsgstr \"收到节后，服务器将所有排队的节发送给客户端。\"\n\n#: ../../Tigase_Development/Experimental.rst:186\nmsgid \"\"\n\"Also all stanzas from queue will be sent if number of stanzas in queue \"\n\"will reach queue size limit. By default this limit is set to 50.\"\nmsgstr \"如果队列中的节数将达到队列大小限制，则队列中的所有节将被发送。默认情况下，此限制设置为50。\"\n\n#: ../../Tigase_Development/Experimental.rst:189\nmsgid \"Queuing Algorithms\"\nmsgstr \"排队算法\"\n\n#: ../../Tigase_Development/Experimental.rst:191\nmsgid \"There are three mobile optimization plugins for Tigase:\"\nmsgstr \"Tigase共有三个移动优化插件：\"\n\n#: ../../Tigase_Development/Experimental.rst:193\nmsgid \"**Mobile v1** - all presence stanzas are kept in queue\"\nmsgstr \"**Mobile v1** - 所有存在节都保留在队列中\"\n\n#: ../../Tigase_Development/Experimental.rst:195\nmsgid \"**Mobile v2** - only last presence from each source is kept in queue\"\nmsgstr \"**Mobile v2** - 只有来自每个来源的最后一次出现才保留在队列中\"\n\n#: ../../Tigase_Development/Experimental.rst:197\nmsgid \"\"\n\"**Mobile v3** - only last presence from each source is kept in queue, \"\n\"also Message Carbons are queued\"\nmsgstr \"**Mobile v3** - 只有来自每个源的最后一次存在保留在队列中，消息碳也排队\"\n\n#: ../../Tigase_Development/Experimental.rst:199\nmsgid \"\"\n\"If you wish to activate you Mobile v1 plugin you need to send presented \"\n\"above with xmlns attribute value replaced with \"\n\"http://tigase.org/protocol/mobile#v1\"\nmsgstr \"\"\n\"如果您希望激活您的Mobile v1插件，您需要发送上面显示的xmlns属性值替换为 \"\n\"http://tigase.org/protocol/mobile#v1\"\n\n#: ../../Tigase_Development/Experimental.rst:201\nmsgid \"\"\n\"If you wish to activate you Mobile v2 plugin you need to send presented \"\n\"above with xmlns attribute value replaced with \"\n\"http://tigase.org/protocol/mobile#v2\"\nmsgstr \"\"\n\"如果您希望激活您的Mobile v2插件，您需要发送上面显示的xmlns属性值替换为 \"\n\"http://tigase.org/protocol/mobile#v2\"\n\n#: ../../Tigase_Development/Experimental.rst:206\nmsgid \"\"\n\"Mobile plugins are not activated by default thus additional entry in the \"\n\"``config.tdsl`` is required:\"\nmsgstr \"移动插件默认不激活，因此需要在 ``config.tdsl`` 中添加额外条目：\"\n\n#: ../../Tigase_Development/Experimental.rst:214\nmsgid \"\"\n\"You may substitute ``mobile_v1`` with ``mobile_v2`` or ``mobile_v3`` \"\n\"depending on which algorithm you wish to use.\"\nmsgstr \"您可以根据您希望使用的算法将 ``mobile_v1`` 替换为 ``mobile_v2`` 或 ``mobile_v3``。\"\n\n#: ../../Tigase_Development/Experimental.rst:218\nmsgid \"USE ONLY ONE PLUGIN AT A TIME!\"\nmsgstr \"一次只能使用一个插件！\"\n\n#: ../../Tigase_Development/Experimental.rst:223\nmsgid \"Bosh Session Cache\"\nmsgstr \"Bosh会话缓存\"\n\n#: ../../Tigase_Development/Experimental.rst:228\nmsgid \"\"\n\"Web clients have no way to store any data locally, on the client side. \"\n\"Therefore after a web page reload the web clients loses all the context \"\n\"it was running in before the page reload.\"\nmsgstr \"Web客户端无法在客户端本地存储任何数据。因此，在重新加载网页后，Web客户端会丢失在重新加载页面之前运行的所有上下文。\"\n\n#: ../../Tigase_Development/Experimental.rst:230\nmsgid \"\"\n\"Some elements of the context can be retrieved from the server like the \"\n\"roster and all contacts presence information. Some other data however, \"\n\"can not be restored easily like opened chat windows and the chat windows \"\n\"contents. Even if the roster restoring is possible, this operation is \"\n\"very expensive in terms of time and resources on the server side.\"\nmsgstr \"可以从服务器检索上下文的某些元素，例如名册和所有联系人的存在信息。但是，其他一些数据无法轻松恢复，例如打开的聊天窗口和聊天窗口的内容。即使可以进行名册恢复，该操作在服务器端的时间和资源方面代价也是非常昂贵的。\"\n\n#: ../../Tigase_Development/Experimental.rst:232\nmsgid \"\"\n\"On of possible solutions is to allow web client to store some data in the\"\n\" Bosh component cache on the server side for the time while the Bosh \"\n\"session is active. After the page reloads, if the client can somehow \"\n\"retrieve SID (stored in cookie or provided by the web application running\"\n\" the web client) it is possible to reload all the data stored in the Bosh\"\n\" cache to the client.\"\nmsgstr \"\"\n\"一种可能的解决方案是允许Web客户端在Bosh会话处于活动状态时将一些数据存储在服务器端的Bosh组件缓存中。页面重新加载后，如果客户端能够以某种方式检索SID（存储在cookie中或由运行Web客户端的Web应用程序提供），则可以将存储在\"\n\" Bosh缓存中的所有数据重新加载到客户端。\"\n\n#: ../../Tigase_Development/Experimental.rst:234\nmsgid \"\"\n\"Bosh session context data are: roster, contacts presence information, \"\n\"opened chat windows, chat windows content and some other minor data. \"\n\"Ideally the web client should be able to store any data in the Bosh \"\n\"component cache it wants.\"\nmsgstr \"Bosh会话上下文数据是：名册、联系人状态信息、打开的聊天窗口、聊天窗口内容和其他一些次要数据。理想情况下，Web客户端应该能够将任何数据存储在它想要的Bosh组件缓存中。\"\n\n#: ../../Tigase_Development/Experimental.rst:238\nmsgid \"Bosh Session Cache Description\"\nmsgstr \"Bosh会话缓存说明\"\n\n#: ../../Tigase_Development/Experimental.rst:240\nmsgid \"\"\n\"The Bosh Session Cache is divided into 2 parts - automatic cache and \"\n\"dynamic cache.\"\nmsgstr \"Bosh Session Cache分为两部分 - 自动缓存和动态缓存。\"\n\n#: ../../Tigase_Development/Experimental.rst:242\nmsgid \"\"\n\"The reason for splitting the cache into 2 parts is that some data can be \"\n\"collected automatically by the Bosh component and it would be very \"\n\"inefficient to require the client to store the data in the Bosh cache. \"\n\"The best example for such data is the Roster and contacts presence \"\n\"information.\"\nmsgstr \"将缓存分成两部分的原因是，一些数据可以由Bosh组件自动收集，并且要求客户端将数据存储在Bosh缓存中，这会非常低效。此类数据的最佳示例是名册和联系人存在信息。\"\n\n#: ../../Tigase_Development/Experimental.rst:244\nmsgid \"\"\n\"**automatic cache** - is the cache part which is created automatically by\"\n\" the Bosh component without any interaction with the client. The client, \"\n\"however, can access the cache at any time. I would say this is a read-\"\n\"only cache but I don’t want to stop client from manipulating the cache if\"\n\" it needs. The client usually, only retrieves data from this part of the \"\n\"cache as all changes should be automatically updated by the Bosh \"\n\"component. The general idea for the automatic cache is that the data \"\n\"stored there are accessible in the standard XMPP form. So no extra code \"\n\"is needed for processing them.\"\nmsgstr \"\"\n\"**自动缓存** - \"\n\"是由Bosh组件自动创建的缓存部分，无需与客户端进行任何交互。但是，客户端可以随时访问缓存。我会说这是一个只读缓存，但我不想阻止客户端在需要时操作缓存。客户端通常只从这部分缓存中检索数据，因为所有更改都应该由Bosh组件自动更新。自动缓存的总体思路是存储在那里的数据可以以标准的XMPP形式访问。所以不需要额外的代码来处理它们。\"\n\n#: ../../Tigase_Development/Experimental.rst:246\nmsgid \"\"\n\"**dynamic cache** - is the cache part which is or can be modified at any \"\n\"time by the client. Client can store, retrieve, delete and modify data in\"\n\" this part of the cache.\"\nmsgstr \"**动态缓存** - 是客户端可以随时修改或可以修改的缓存部分。客户端可以在这部分缓存中存储、检索、删除和修改数据。\"\n\n#: ../../Tigase_Development/Experimental.rst:249\nmsgid \"Cache Protocol\"\nmsgstr \"缓存协议\"\n\n#: ../../Tigase_Development/Experimental.rst:251\nmsgid \"\"\n\"All the Bosh Session Cache actions are executed using additional \"\n\"``<body/>`` element attributes: ``cache`` and ``cache-id``. Attribute \"\n\"cache stores the action performed on the Bosh ``cache`` and the ``cache-\"\n\"id`` attribute refers to the ``cache`` element if the action attribute \"\n\"needs it. ``cache-id`` is optional. There is a default cache ID (empty \"\n\"one) associated with the elements for which the ``cache-id`` is not \"\n\"provided.\"\nmsgstr \"\"\n\"所有Bosh会话缓存操作都使用附加的<body/>元素属性执行：``cache`` 和 ``cache-\"\n\"id``。如果操作属性需要，属性缓存是存储在 Bosh ``cache`` 上执行的操作，``cache-id`` 属性指的是 ``cache``\"\n\" 元素。 ``cache-id`` 是可选的。有一个默认的缓存ID（空的）与未提供 ``cache-id`` 的元素相关联。\"\n\n#: ../../Tigase_Development/Experimental.rst:253\nmsgid \"\"\n\"If the ``<body/>`` element contains the cache attribute it means that all\"\n\" data included in the ``<body/>`` refer to the cache action. It is not \"\n\"allowed, for example to send a message in the body and have the cache \"\n\"action set to **get**. The ``<body/>`` element with cache action **get**,\"\n\" **get_all**, **on**, **off**, **remove** must be empty. The ``<body/>`` \"\n\"element with actions **set** or **add** must contain data to store in the\"\n\" cache.\"\nmsgstr \"\"\n\"如果 ``<body/>`` 元素包含缓存属性，则意味着 ``<body/>`` \"\n\"中包含的所有数据都引用缓存操作。这是不允许的，例如在正文中发送消息并将缓存操作设置为 **get**。具有缓存操作 \"\n\"**get**、**get_all**、**on**、**off**、**remove** 的 ``<body/>`` 元素必须为空。带有动作 \"\n\"**set** 或 **add** 的 ``<body/>`` 元素必须包含要存储在缓存中的数据。\"\n\n#: ../../Tigase_Development/Experimental.rst:256\nmsgid \"Cache Actions\"\nmsgstr \"缓存操作\"\n\n#: ../../Tigase_Development/Experimental.rst:258\nmsgid \"\"\n\"**on** or **off** - the client can switch the cache on or off at any time\"\n\" during the session. It is recommended, however that the client switches \"\n\"the cache **on** in the first body packet, otherwise some information \"\n\"from the automatic cache may be missing. The automatic cache is created \"\n\"from the stream of data passing the Bosh component. Therefore if the \"\n\"cache is switched on after the roster retrieval is completed then the \"\n\"roster information will be missing in the cache. If the cache is set to \"\n\"**off** (the default value) all requests to the cache are ignored. This \"\n\"is to ensure backward compatibility with the original Bosh specification \"\n\"and to make sure that in a default environment the Bosh component doesn’t\"\n\" consume any extra resources for cache processing and storing as the \"\n\"cache wouldn’t be used by the client anyway.\"\nmsgstr \"\"\n\"**on** 或 **off** - 客户端可以在会话期间随时打开或关闭缓存。但是，建议客户端在第一个主体数据包中切换缓存 \"\n\"**on**，否则可能会丢失自动缓存中的某些信息。自动缓存是从通过Bosh组件的数据流创建的。因此，如果在名册检索完成后打开缓存，则名册信息将在缓存中丢失。如果缓存设置为\"\n\" **off** \"\n\"（默认值），所有对缓存的请求都将被忽略。这是为了确保与原始Bosh规范的向后兼容性，并确保在默认环境中，Bosh组件不会为缓存处理和存储消耗任何额外资源，因为客户端无论如何都不会使用缓存。\"\n\n#: ../../Tigase_Development/Experimental.rst:260\nmsgid \"\"\n\"**get** - retrieves the cache element pointing by the cache-id from the \"\n\"Bosh cache. Note there is no **result** cache action. The ``<body/>`` \"\n\"sent as a response from the server to the client may contain cache \"\n\"results for a given cache-id and it may also contain other data received \"\n\"by the Bosh component for the client. It may also happen that large \"\n\"cached data are split into a few parts and each part can be sent in a \"\n\"separate ``<body/>`` element. It may usually happen for the Roster data.\"\nmsgstr \"\"\n\"**get** - 从Bosh缓存中检索由cache-id指向的缓存元素。请注意，没有 **result** \"\n\"缓存操作。作为响应从服务器发送到客户端的 <body/> \"\n\"可能包含给定缓存ID的缓存结果，它还可能包含Bosh组件为客户端接收的其他数据。也可能发生大缓存数据被分成几个部分，每个部分可以在一个单独的 \"\n\"<body/> 元素中发送。名册数据通常会发生这种情况。\"\n\n#: ../../Tigase_Development/Experimental.rst:262\nmsgid \"\"\n\"**get_all** - retrieves all the elements kept in the Bosh cache. That \"\n\"action can can be performed after the page reload. The client doesn’t \"\n\"have to request every single cached item one by one. It can retrieve all \"\n\"cache items in one go. It doesn’t mean however the whole cache is sent to\"\n\" the client in a single ``<body/>`` element. The cache content will be \"\n\"divided into a smaller parts of a reasonable size and will be sent to the\"\n\" client in a separate ``<body/>`` elements. It may also happen that the \"\n\"**``<body/>``** element contain the cache elements as well as the new \"\n\"requests sent to the user like new messages or presence information.\"\nmsgstr \"\"\n\"**get_all** - \"\n\"检索保存在Bosh缓存中的所有元素。该操作可以在页面重新加载后执行。客户端不必逐个请求每个缓存项。它可以一次检索所有缓存项。然而，这并不意味着整个缓存在单个\"\n\" <body/> 元素中发送到客户端。缓存内容将被划分为合理大小的较小部分，并在单独的 <body/> 元素中发送到客户端。 ** \"\n\"``<body/>`` ** 元素也可能包含缓存元素以及发送给用户的新请求，例如新消息或存在信息。\"\n\n#: ../../Tigase_Development/Experimental.rst:264\nmsgid \"\"\n\"**set** - sends data to the Bosh Session cache for later retrieval. The \"\n\"client can store any data it wants in the cache. The Bosh components \"\n\"stores in the cache under the selected ID all the data inside the \"\n\"``<body/>`` element. The only restriction is that the cached data must be\"\n\" a valid XML content. The data are returned to the client in exactly the \"\n\"same form as they were received from the server. The **set** action \"\n\"replaces any previously stored data under this ID.\"\nmsgstr \"\"\n\"**set** - 将数据发送到Bosh Session缓存以供以后检索。客户端可以在缓存中存储它想要的任何数据。 Bosh组件将 <body/>\"\n\" 元素内的所有数据存储在所选ID下的缓存中。唯一的限制是缓存的数据必须是有效的XML内容。数据以与从服务器接收到的完全相同的形式返回给客户端。 \"\n\"**set** 操作将替换此ID下任何以前存储的数据。\"\n\n#: ../../Tigase_Development/Experimental.rst:266\nmsgid \"\"\n\"**add** - adds new element to the cache under the given ID. This action \"\n\"might be useful for storing data for the opened chat window. The client \"\n\"can add new elements for the chat window, like new messages, icons and so\"\n\" on…​\"\nmsgstr \"\"\n\"**add** - \"\n\"将新元素添加到给定ID下的缓存中。此操作对于为打开的聊天窗口存储数据可能很有用。客户端可以为聊天窗口添加新元素，比如新消息、图标等等……​\"\n\n#: ../../Tigase_Development/Experimental.rst:268\nmsgid \"**remove** - removes the cached element for the given cache ID.\"\nmsgstr \"**remove** - 删除给定缓存ID的缓存元素。\"\n\n#: ../../Tigase_Development/Experimental.rst:271\nmsgid \"Cache ID\"\nmsgstr \"缓存ID\"\n\n#: ../../Tigase_Development/Experimental.rst:273\nmsgid \"\"\n\"Cache ID can be an any character string. There might be some IDs reserved\"\n\" for a special cases, like for the Roster content. To avoid any future ID\"\n\" conflicts reserved ID values starts with: **bosh** - string.\"\nmsgstr \"\"\n\"缓存ID可以是任意字符串。可能会为特殊情况保留一些ID，例如用于名册内容。为避免将来出现任何ID冲突，保留的ID值以：**bosh** - \"\n\"字符串开头。\"\n\n#: ../../Tigase_Development/Experimental.rst:275\nmsgid \"\"\n\"There is a default cache ID - en empty string. Thus cache-id attribute \"\n\"can be omitted and then the requests refers to data stored under the \"\n\"default (empty) ID.\"\nmsgstr \"有一个默认缓存ID - 一个空字符串。因此可以省略cache-id属性，然后请求引用存储在默认（空）ID下的数据。\"\n\n#: ../../Tigase_Development/Experimental.rst:278\nmsgid \"Reserved Cache ID Names\"\nmsgstr \"保留的缓存ID名称\"\n\n#: ../../Tigase_Development/Experimental.rst:280\nmsgid \"Here is a list of reserved Cache IDs:\"\nmsgstr \"以下是保留的缓存ID列表：\"\n\n#: ../../Tigase_Development/Experimental.rst:282\nmsgid \"\"\n\"**bosh-roster** - The user roster is cached in the Bosh component in \"\n\"exactly the same form as it was received from the core server. The Bosh \"\n\"Cache might or might not do optimizations on the roster like removing \"\n\"elements from the cached roster if the roster **remove** has been \"\n\"received or may just store all the roster requests and then send them all\"\n\" to the client. There is a one mandatory optimization the Bosh Cache must\"\n\" perform. It must remember the last (and only the last) presence status \"\n\"for each roster item. Upon roster retrieving from the cache the Bosh \"\n\"component must send the roster item first and then the presence for the \"\n\"item. If the presence is missing it means an offline presence. If the \"\n\"roster is small it can be sent to the client in a single packet but for a\"\n\" large roster it is recommended to split contact lists to batches of max \"\n\"100 elements. The Bosh component may send all roster contacts first and \"\n\"then all presences or it can send a part of the roster, presences for \"\n\"sent items, next part of the roster, presences for next items and so on.\"\nmsgstr \"\"\n\"**bosh-roster** - 用户名册以与从核心服务器接收到的完全相同的形式缓存在Bosh组件中。 \"\n\"Bosh缓存可能会也可能不会对名册进行优化，例如如果名册 **删除** \"\n\"已收到，则从缓存的名册中删除元素，或者可能只存储所有名册请求，然后将它们全部发送到客户端。 Bosh \"\n\"Cache必须执行一项强制优化。它必须记住每个花名册项目的最后一个（也是唯一的最后一个）存在状态。在从缓存中检索名册后，Bosh \"\n\"组件必须先发送名册项目，然后再发送该项目的存在。如果存在缺失，则意味着离线存在。如果名册很小，可以在单个数据包中发送给客户端，但对于大型名册，建议将联系人列表拆分为最多100个元素的批次。\"\n\" \"\n\"Bosh组件可以先发送所有名册联系人，然后发送所有存在信息，或者它可以发送名册的一部分、已发送项目的存在信息、名册的下一部分、下一个项目的存在信息等等。\"\n\n#: ../../Tigase_Development/Experimental.rst:284\nmsgid \"\"\n\"**bosh-resource-bind** - The user resource bind is also cached to allow \"\n\"the client quickly retrieve information about the full JID for the \"\n\"established Bosh session.\"\nmsgstr \"**bosh-resource-bind** - 用户资源绑定也被缓存，以允许客户端快速检索有关已建立Bosh会话的完整JID的信息。\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: 2022-09-07 17:14+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/dp-hack_tigase/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:2\nmsgid \"Hack Tigase XMPP Server in Eclipse\"\nmsgstr \"在 Eclipse 中破解 Tigase XMPP 服务器\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:4\nmsgid \"\"\n\"If you want to write code for **Tigase** server we recommend using \"\n\"`Eclipse IDE <//https://eclipse.org/downloads/>`__. Either the IDE for \"\n\"Java or Java EE developers will work.\"\nmsgstr \"\"\n\"如果您想为 **Tigase** 服务器编写代码，我们建议使用 `Eclipse IDE \"\n\"<//https://eclipse.org/downloads/>`__。适用于 Java 或 Java EE 开发人员的 IDE 都可以使用。\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:7\nmsgid \"Requirements\"\nmsgstr \"要求\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:9\nmsgid \"\"\n\"Eclipse IDE currently requires the use of `Java Development Kit 8 \"\n\"<http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html>`__.\"\nmsgstr \"\"\n\"Eclipse IDE 当前需要使用 `Java Development Kit 8 \"\n\"<http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html>`__。\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:11\nmsgid \"\"\n\"You will also need the M2E plugin for Maven integration, however this can\"\n\" be done inside Eclipse now, so refer to the :ref:`Plugin \"\n\"Installation<m2EPlugin>` section for that.\"\nmsgstr \"\"\n\"您还需要用于 Maven 集成的 M2E 插件，但是现在可以在 Eclipse 中完成，\"\n\"因此请参阅 :ref:`插件安装<m2EPlugin>` 部分。\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:14\nmsgid \"Installation\"\nmsgstr \"安装\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:16\nmsgid \"\"\n\"Eclipse does not come as an installer, but rather an archive. Extract the\"\n\" directory to a working location wherever you would like. Now install the\"\n\" JDK software, location is not important as Eclipse will find it \"\n\"automatically.\"\nmsgstr \"\"\n\"Eclipse 不是作为安装程序提供的，而是作为存档提供的。将目录解压缩到您想要的任何工作位置。现在安装 JDK 软件时，位置并不重要，因为 \"\n\"Eclipse 会自动找到它。\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:18\nmsgid \"Before we begin, we will need to clone the repository from git.\"\nmsgstr \"在开始之前，我们需要从 git 克隆存储库。\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:21\nmsgid \"Linux\"\nmsgstr \"Linux\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:23\nmsgid \"\"\n\"For linux operating systems, navigate to a directory where you want the \"\n\"repository to be cloned to and type the following into terminal.\"\nmsgstr \"对于 linux 操作系统，导航到要将存储库克隆到的目录，然后在终端中键入以下内容。\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:30\nmsgid \"Windows\"\nmsgstr \"Windows\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:32\nmsgid \"\"\n\"Please see the Windows coding guide for instructions on how to obtain \"\n\"source code from git. If you don’t want to install git software \"\n\"specifically, you can use Eclipse’s git plugin to obtain the repository \"\n\"without any new software. First click on File, then Import…​ Next select \"\n\"from Git folder and the Projects from Git\"\nmsgstr \"\"\n\"有关如何从 git 获取源代码的说明，请参阅 Windows 编码指南。如果不想专门安装 git 软件，可以使用 Eclipse 的 git \"\n\"插件获取存储库，而无需任何新软件。首先点击 File，然后 Import...​ 接着从 Git 文件夹和 Projects from Git \"\n\"中选择\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:34\nmsgid \"|win git1|\"\nmsgstr \"|win git1|\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:89\nmsgid \"win git1\"\nmsgstr \"win git1\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:36\nmsgid \"Click next, and now select clone URI\"\nmsgstr \"单击下一步，现在选择克隆 URI\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:38\nmsgid \"|win git2|\"\nmsgstr \"|win git2|\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:90\nmsgid \"win git2\"\nmsgstr \"win git2\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:40\nmsgid \"Now click next, and in this window enter the following into the URI field\"\nmsgstr \"现在单击下一步，然后在此窗口中的 URI 字段中输入以下内容\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:46\nmsgid \"The rest of the fields will populate automatically\"\nmsgstr \"其余字段将自动填充\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:48\nmsgid \"|win git3|\"\nmsgstr \"|win git3|\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:91\nmsgid \"win git3\"\nmsgstr \"win git3\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:50\nmsgid \"\"\n\"Select the master branch, and any branches you wish to edit. **The master\"\n\" branch should be the only one you need, branches are used for specific \"\n\"code changes**\"\nmsgstr \"选择master分支，以及您要编辑的任何分支。 **master分支应该是你唯一需要的，其余分支用于特定的代码更改**\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:52\nmsgid \"|win git4|\"\nmsgstr \"|win git4|\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:92\nmsgid \"win git4\"\nmsgstr \"win git4\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:54\nmsgid \"\"\n\"Now select the directory where you wanted to clone the repository to. \"\n\"This was function as the project root directory you will use later on in \"\n\"the setup.\"\nmsgstr \"现在选择要将存储库克隆到的目录。这是您稍后将在设置中使用的项目根目录。\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:56\nmsgid \"|win git5|\"\nmsgstr \"|win git5|\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:93\nmsgid \"win git5\"\nmsgstr \"win git5\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:58\nmsgid \"\"\n\"Once you click next Eclipse will download the repository and any branches\"\n\" you selected to that directory. Note you will be unable to import this \"\n\"git directory since there are no git a project specific files downloaded.\"\n\" However, once downloading is complete you may click cancel, and the git \"\n\"repository will remain in the directory you have chosen.\"\nmsgstr \"\"\n\"单击下一步后，Eclipse 会将存储库和您选择的任何分支下载到该目录。请注意，您将无法导入此 git 目录，因为没有下载特定于项目的 git \"\n\"文件。但是，一旦下载完成，您可以单击取消，git 存储库将保留在您选择的目录中。\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:63\nmsgid \"Setup\"\nmsgstr \"设置\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:65\nmsgid \"\"\n\"Once you have the main window open and have established a workspace \"\n\"(where most of your working files will be stored), click on Help and then\"\n\" Install New Software…​\"\nmsgstr \"打开主窗口并建立工作区（这是将存储大部分工作文件的地方）后，单击帮助，然后单击安装新软件...​\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:67\nmsgid \"|Eclipse help|\"\nmsgstr \"|Eclipse help|\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:94\nmsgid \"Eclipse help\"\nmsgstr \"Eclipse help\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:69\nmsgid \"\"\n\"Under the Work With field enter the following and press enter: \"\n\"http://download.eclipse.org/technology/m2e/releases/\"\nmsgstr \"\"\n\"在 Work With 字段下输入以下内容并按 \"\n\"Enter：http://download.eclipse.org/technology/m2e/releases/\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:71\nmsgid \"\"\n\"**Note: You may wish to click the Add…​ button and add the above location\"\n\" as a permanent software location to keep the location in memory**\"\nmsgstr \"**注意：您可能希望单击添加...按钮并将上述位置添加为永久软件位置，以将该位置保留在内存中**\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:73\nmsgid \"|Eclipse m2Einstall|\"\nmsgstr \"|Eclipse m2Einstall|\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:95\nmsgid \"Eclipse m2Einstall\"\nmsgstr \"Eclipse m2Einstall\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:75\nmsgid \"\"\n\"You should see the M2 Eclipse software packages show in the main window. \"\n\"Click the check-box and click Next. Once the installer is finished it \"\n\"will need to restart Eclipse.\"\nmsgstr \"您应该会在主窗口中看到 M2 Eclipse 软件包。单击复选框，然后单击下一步。安装程序完成后，需要重新启动 Eclipse。\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:77\nmsgid \"Once that is done, lets connect Eclipse to the cloned repository.\"\nmsgstr \"完成后，让我们将 Eclipse 连接到克隆的存储库。\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:79\nmsgid \"\"\n\"Click File and Import…​ to bring up the import dialog window. Select \"\n\"Maven and then Existing Maven Project.\"\nmsgstr \"单击文件和导入...​ 以打开导入对话框窗口。选择 Maven，然后选择现有的 Maven 项目。\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:81\nmsgid \"|Eclipse importMaven|\"\nmsgstr \"|Eclipse importMaven|\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:96\nmsgid \"Eclipse importMaven\"\nmsgstr \"Eclipse importMaven\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:83\nmsgid \"\"\n\"Now click Next and point the root directory to where you cloned the git \"\n\"repository, Eclipse should automatically see the pom.xml file and show up\"\n\" in the next window.\"\nmsgstr \"现在单击 Next 并将根目录指向克隆 git 存储库的位置，Eclipse 应该会自动看到 pom.xml 文件并显示在下一个窗口中。\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:85\nmsgid \"|Eclipse importMaven2|\"\nmsgstr \"|Eclipse importMaven2|\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:97\nmsgid \"Eclipse importMaven2\"\nmsgstr \"Eclipse importMaven2\"\n\n#: ../../Tigase_Development/Hack_Tigase_Jabber-XMPP_Server_in_Eclipse.rst:87\nmsgid \"\"\n\"Once the import is finished, you are able to now begin working with \"\n\"Tigase’s code inside Eclipse! Happy coding!\"\nmsgstr \"导入完成后，您现在可以开始在 Eclipse 中使用 Tigase 的代码了！快乐编码！\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Development/Old_Stuff.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: 2022-09-07 17:14+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/dg-old_stuff/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:2\nmsgid \"Old Stuff\"\nmsgstr \"旧文档\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:4\nmsgid \"\"\n\"This contains sections on old features, or information pertaining to old \"\n\"builds of Tigase. It is kept here for archival purposes.\"\nmsgstr \"这包含有关旧功能的部分，或与Tigase旧版本有关的信息。它被保存在这里用于存档目的。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:7\nmsgid \"Tigase DB Schema Explained\"\nmsgstr \"Tigase DB架构解释\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:9\nmsgid \"\"\n\"The schema basics, how it looks like and brief explanation to all rows \"\n\"can be found in the `list of schema files <https://github.com/tigase\"\n\"/tigase-server/tree/master/src/main/database>`__. However, this is hardly\"\n\" enough to understand how it works and how all the data is accessed. \"\n\"There are only 3 basic tables which actually keep all the Tigase server \"\n\"users' data: **tig_users**, **tig_nodes** and **tig_pairs**. Therefore it\"\n\" is not clear at first how Tigase’s data is organized.\"\nmsgstr \"\"\n\"架构基础知识、外观和对所有行的简要说明可以在 `架构文件列表 <https://github.com/tigase/tigase-\"\n\"server/tree/master/src/main/database>`__ \"\n\"中找到。然而，这还不足以理解它是如何工作的以及如何访问所有数据。只有3个基本表实际上保存了所有Tigase服务器用户的数据：**tig_users**、**tig_nodes**\"\n\" 和 **tig_pairs**。因此，起初并不清楚Tigase的数据是如何组织的。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:11\nmsgid \"\"\n\"Before you can understand the Tigase XMPP Server database schema, how it \"\n\"works and how to use it, is it essential to know what were the goals of \"\n\"it’s development and why it works that way. Let’s start with the API as \"\n\"this gives you the best introduction.\"\nmsgstr \"\"\n\"在您了解Tigase \"\n\"XMPP服务器数据库架构、它是如何工作以及如何使用它之前，了解它的开发目标是什么以及为什么它以这种方式工作是必不可少的。让我们从API开始，因为它为您提供了最好的介绍。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:13\nmsgid \"Simplified access can be made through methods:\"\nmsgstr \"可以通过以下方法进行简化访问：\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:20\nmsgid \"And more a complex version:\"\nmsgstr \"还有更复杂的版本：\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:27\nmsgid \"\"\n\"Even though the API contains more methods, the rest is more or less a \"\n\"variation of presented above. A complete API description for all access \"\n\"methods is available in JavaDoc documentation in the `UserRepository \"\n\"<https://github.com/tigase/tigase-\"\n\"server/tree/master/src/main/java/tigase/db/UserRepository.java>`__ \"\n\"interface. So we are not going into too much detail here except for the \"\n\"main idea.\"\nmsgstr \"\"\n\"尽管API包含更多方法，但其余的或多或少是上面介绍的变体。 `UserRepository <https://github.com/tigase\"\n\"/tigase-\"\n\"server/tree/master/src/main/java/tigase/db/UserRepository.java>`__ \"\n\"接口中的JavaDoc文档中提供了所有访问方法的完整API描述接口。因此，除了主要想法之外，我们不会在这里过多地讨论细节。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:29\nmsgid \"\"\n\"Tigase operates on <*key*, **value**> pairs for the individual user data.\"\n\" The idea behind this was to make the API very simple and also at the \"\n\"same time very flexible, so adding a new plugin or component would not \"\n\"require a database schema change, adding new tables, or conversion of the\"\n\" DB schema to a new version.\"\nmsgstr \"\"\n\"Tigase对单个用户数据的 <*key*, **value**> \"\n\"对进行操作。这背后的想法是想让API非常简单，同时也非常灵活，因此添加新插件或组件不需要更改数据库架构、添加新表或将数据库架构转换为新版本。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:31\nmsgid \"\"\n\"As a result the **UserRepository** interface is exposed to all of \"\n\"Tigase’s code, mainly the components and plugins (let’s call all of them \"\n\"modules). These modules simply call set/get methods to store or access \"\n\"module specific data.\"\nmsgstr \"\"\n\"结果，**UserRepository** 接口暴露给Tigase的所有代码，主要是组件和插件（我们称它们为模块）。这些模块只需调用 \"\n\"set/get 方法来存储或访问模块特定的数据。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:33\nmsgid \"\"\n\"As plugins or components are developed independently it may easily happen\"\n\" that developer choses the same key name to store some information. To \"\n\"avoid key name conflicts in the database a 'node' concept has been \"\n\"introduced. Therefore, most modules when set/get key value they also \"\n\"provide a subnode part, which in most cases is just XMLNS or some other \"\n\"unique string.\"\nmsgstr \"由于插件或组件是独立开发的，开发人员很容易选择相同的键名来存储一些信息。为了避免数据库中的键名冲突，引入了'节点'概念。因此，大多数模块在设置/获取键值时还提供了一个子节点部分，在大多数情况下，它只是XMLNS或其他一些唯一字符串。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:35\nmsgid \"\"\n\"The 'node' thing is a little bit like directory in a file system, it may \"\n\"contain subnodes which makes the Tigase database behave like a \"\n\"hierarchical structure. And the notation is also similar to file systems,\"\n\" you use just **/** to separate node levels. In practice you can have the\"\n\" database organized like this:\"\nmsgstr \"\"\n\"'节点'有点像文件系统中的目录，它可能包含子节点，这使得Tigase数据库表现得像一个层次结构。并且符号也类似于文件系统，您只使用 **/** \"\n\"来分隔节点级别。在实践中，您可以像这样组织数据库：\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:47\nmsgid \"\"\n\"So to access item’s 1 data from the roster you could call method like \"\n\"this:\"\nmsgstr \"因此，要从名册中访问item1的数据，您可以调用如下方法：\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:53\nmsgid \"\"\n\"This is huge convenience for the developer, as he can focus on the module\"\n\" logic instead of worrying about data storage implementation and \"\n\"organization. Especially at the prototype phase it speeds development up \"\n\"and allows for a quick experiments with different solutions. In practice,\"\n\" accessing user’s roster in such a way would be highly inefficient so the\"\n\" roster is stored a bit differently but you get the idea. Also there is a\"\n\" more complex API used in some places allowing for more direct access to \"\n\"the database and store data in any format optimized for the scenario.\"\nmsgstr \"这对开发人员来说是极大的方便，因为他可以专注于模块逻辑，而不用担心数据存储实现和组织。特别是在原型阶段，它加快了开发速度，并允许使用不同的解决方案进行快速实验。在实践中，以这种方式访问用户名册的效率非常低，因此名册的存储方式略有不同，但你已经明白了此方法。此外，在某些地方使用了更复杂的API，允许更直接地访问数据库并以针对场景优化的任何格式存储数据。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:55\nmsgid \"\"\n\"Right now such a hierarchical structure is implemented on top of SQL \"\n\"databases but initially Tigase’s database was implemented as an XML \"\n\"structure, so it was natural and simple.\"\nmsgstr \"现在这种层次结构是在SQL数据库之上实现的，但最初Tigase的数据库是作为XML结构实现的，所以它自然而简单。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:57\nmsgid \"In the SQL database we simulate hierarchical structure with three tables:\"\nmsgstr \"在SQL数据库中，我们用三个表模拟层次结构：\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:59\nmsgid \"\"\n\"**tig_users** - with main users data, user id (JID), optional password, \"\n\"active flag, creation time and some other basic properties of the \"\n\"account. All of them could be actually stored in tig_pairs but for \"\n\"performance reasons they are in one place to quickly access them with a \"\n\"single, simple query.\"\nmsgstr \"\"\n\"**tig_users** - 包含主要用户数据、用户ID \"\n\"(JID)、可选密码、活动标志、创建时间和帐户的一些其他基本属性。所有这些实际上都可以存储在tig_pairs中，但出于性能原因，它们位于一个位置，可以通过一个简单的查询快速访问它们。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:61\nmsgid \"\"\n\"**tig_nodes** - is a table where the hierarchy is implemented. When \"\n\"Tigase was storing data in XML database the hierarchy was quite complex. \"\n\"However, in a SQL database it resulted in a very slow access to the data \"\n\"and a now more flat structure is used by most components. Please note, \"\n\"every user’s entry has something called root node, which is represented \"\n\"by 'root' string;\"\nmsgstr \"\"\n\"**tig_nodes** - \"\n\"是实现层次结构的表。当Tigase在XML数据库中存储数据时，层次结构非常复杂。但是，在SQL数据库中，它会导致对数据的访问非常缓慢，并且现在大多数组件都使用更扁平的结构。请注意，每个用户的条目都有一个叫做根节点的东西，它由'root'字符串表示；\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:63\nmsgid \"\"\n\"**tig_pairs** - this is the table where all the user’s information is \"\n\"stored in form of the <key, value> pairs.\"\nmsgstr \"**tig_pairs** - 这是所有用户信息以<key, value>对的形式存储的表。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:65\nmsgid \"\"\n\"So we now know how the data is organized. Now we are going to learn how \"\n\"to access the data directly in the database using SQL queries.\"\nmsgstr \"所以我们现在知道数据是如何组织的。现在我们将学习如何使用SQL查询直接访问数据库中的数据。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:67\nmsgid \"\"\n\"Let’s assume we have a user 'admin@test-d' for whom we want to retrieve \"\n\"the roster. We could simply execute query:\"\nmsgstr \"假设我们有一个用户'admin@test-d'，我们要为其检索花名册。我们可以简单地执行查询：\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:77\nmsgid \"\"\n\"However, if multiple modules store data under the key 'roster' for a \"\n\"single user, we would receive multiple results. To access the correct \"\n\"'roster' we also have to know the node hierarchy for this particular key.\"\n\" The main users roster is stored under the 'root' node, so the query \"\n\"would look like:\"\nmsgstr \"\"\n\"但是，如果多个模块将数据存储在单个用户的键'roster' \"\n\"下，我们将收到多个结果。要访问正确的'roster'，我们还必须知道这个特定键的节点层次结构。主要用户名册存储在 'root' \"\n\"节点下，因此查询如下所示：\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:89\nmsgid \"\"\n\"How exactly the information is stored in the **tig_pairs** table depends \"\n\"on the particular module. For the roster it looks a bit like XML content:\"\nmsgstr \"**tig_pairs** 表中信息的准确存储方式取决于特定模块。对于名册，它看起来有点像XML内容：\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:96\nmsgid \"Why the most recent JDK?\"\nmsgstr \"为什么是最新的JDK？\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:98\nmsgid \"\"\n\"There are many reasons but the main is that we are a small team working \"\n\"on source code. So the whole approach is to make life easier for us, make\"\n\" the project easier to maintain, and development more efficient.\"\nmsgstr \"原因有很多，但主要原因是我们是一个致力于源代码的小团队。所以整个方法是让我们的生活更轻松，让项目更容易维护，开发更高效。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:100\nmsgid \"Here is the list:\"\nmsgstr \"这是列表：\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:102\nmsgid \"\"\n\"**Easy to maintain** - No third-party libraries are used for the project \"\n\"which makes this project much easier to maintain. This simplifies issues \"\n\"of compatibility between particular versions of libraries. This also \"\n\"unifies coding with a single library package without having to rely on \"\n\"specific versions that may not be supported.\"\nmsgstr \"\"\n\"**易于维护** - \"\n\"该项目不使用第三方库，这使得该项目更易于维护。这简化了特定版本库之间的兼容性问题。这也将编码与单个库包统一起来，而不必依赖可能不受支持的特定版本。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:104\nmsgid \"\"\n\"**Easy to deploy** - Another reason to not use third-party tools is to \"\n\"make it easier for end-users to install and use the server.\"\nmsgstr \"**易于部署** - 不使用第三方工具的另一个原因是使终端用户更容易安装和使用服务器。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:106\nmsgid \"\"\n\"**Efficient development** - As no third-party libraries are used, Tigase \"\n\"needs either to implement many things on its own or use as much as \"\n\"possible of JDK functionality. We try to use as much as possible of \"\n\"existing library provided with JDK and the rest is custom coded.\"\nmsgstr \"\"\n\"**高效开发** - \"\n\"由于没有使用第三方库，Tigase需要自己实现很多东西，或者尽可能多地使用JDK功能。我们尝试尽可能多地使用JDK提供的现有库，其余的都是自定义编码的。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:108\nmsgid \"\"\n\"What features of JDKv5 are critical for Tigase development? Why I can’t \"\n\"simply re-implement some code to make it compatible with earlier JDK \"\n\"versions?\"\nmsgstr \"JDKv5的哪些特性对Tigase开发至关重要？为什么我不能简单地重新实现一些代码以使其与早期的JDK版本兼容？\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:110\nmsgid \"\"\n\"**Non-blocking I/O for SSL/TLS** - This is functionality which can’t be \"\n\"simply re-implemented in JDK-1.4. As the whole server uses NIO it doesn’t\"\n\" make sense to use blocking I/O for SSL and TLS.\"\nmsgstr \"\"\n\"**SSL/TLS 的非阻塞 I/O** - \"\n\"这是在JDK-1.4中不能简单地重新实现的功能。由于整个服务器都使用NIO，因此对SSL和TLS使用阻塞I/O是没有意义的。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:112\nmsgid \"**SASL** - This could be re-implemented for JDK-1.4 without much effort.\"\nmsgstr \"**SASL** - 这可以对JDK-1.4重新实现而无需付出太多努力。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:114\nmsgid \"\"\n\"**Concurrent package** - This could be re-implemented for JDK-1.4 but \"\n\"takes a lot of work. This is a critical part of the server as it uses \"\n\"multi-threading and concurrent processing.\"\nmsgstr \"\"\n\"**Concurrent package** - \"\n\"这可以为JDK-1.4重新实现，但需要做很多工作。这是服务器的关键部分，因为它使用多线程和并发处理。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:116\nmsgid \"\"\n\"**Security package** - There number of extensions to the security package\"\n\" which otherwise would not work as easily with earlier versions of JDK.\"\nmsgstr \"**Security package** - 安全包有许多扩展，否则这些扩展无法与早期版本的JDK一起使用。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:118\nmsgid \"\"\n\"**LinkedHashMap** - in JDKv6 is a basement for the Tigase cache \"\n\"implementation.\"\nmsgstr \"**LinkedHashMap** - 在JDKv6中是Tigase缓存实现的基础。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:120\nmsgid \"\"\n\"**Light HTTP server** - JDKv6 offers built-in light HTTP server which is \"\n\"needed to implement HTTP binding (JEP-0124) and HTTP user interface to \"\n\"monitor server activity and work with the server configuration.\"\nmsgstr \"\"\n\"**Light HTTP server** - JDKv6提供了实现HTTP绑定 (JEP-0124) \"\n\"和HTTP用户界面以监控服务器活动并使用服务器配置所需的内置轻型HTTP服务器。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:122\nmsgid \"\"\n\"As the JDK improves, so does our programming as we gain the ability to \"\n\"use new methods, efficiencies, and sometimes shortcuts.\"\nmsgstr \"随着JDK的改进，我们的编程也随之改进，因为我们获得了使用新方法，高效率，有时甚至是捷径的能力。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:124\nmsgid \"\"\n\"Currently Tigase requires **JDKv8** and we recommend updating it as often\"\n\" as needed!\"\nmsgstr \"目前Tigase需要 **JDKv8**，我们建议根据需要经常更新它！\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:127\nmsgid \"API Description for Virtual Domains Management in the Tigase Server\"\nmsgstr \"Tigase服务器中虚拟域管理的API说明\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:129\nmsgid \"\"\n\"The purpose of this guide is to introduce vhost management in Tigase \"\n\"server. Please refer to the JavaDoc documentation for all specific \"\n\"details not covered in this guide. All interfaces are well documented and\"\n\" you can use existing implementation as an example code base and \"\n\"reference point. The VHost management files are located in the repository\"\n\" and you can browse them using the `source viewer \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts>`__.\"\nmsgstr \"\"\n\"本指南的目的是介绍Tigase服务器中的虚拟主机管理。有关本指南未涵盖的所有具体细节，请参阅JavaDoc文档。所有接口都有很好的文档记录，您可以将现有实现用作示例代码库和参考点。\"\n\" VHost管理文件位于存储库中，您可以使用 `source viewer <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts>`__ 浏览它们。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:131\nmsgid \"\"\n\"Virtual hosts management in Tigase can be adjusted in many ways through \"\n\"the flexible API. The core elements of the virtual domains management is \"\n\"interface `VHostManager <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostManager.java>`__ \"\n\"class. They are responsible for providing the virtual hosts information \"\n\"to the rest of the Tigase server components. In particular to the \"\n\"`MessageRouter <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/server/MessageRouter.java>`__ \"\n\"class which controls how XMPP packets flow inside the server.\"\nmsgstr \"\"\n\"Tigase中的虚拟主机管理可以通过灵活的API以多种方式进行调整。虚拟域管理的核心元素是接口 `VHostManager \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostManager.java>`__类。他们负责向其余的Tigase服务器组件提供虚拟主机信息。特别是\"\n\" `MessageRouter <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/server/MessageRouter.java>`__ \"\n\"类，其控制XMPP数据包如何在服务器内部流动。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:133\nmsgid \"\"\n\"The class you most likely want to re-implement is `VHostJDBCRepository \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostJDBCRepository.java>`__\"\n\" used as a default virtual hosts storage and implementing the \"\n\"`VHostRepository <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostRepository.java>`__ \"\n\"interface. You might need to have your own implementation in order to \"\n\"store and access virtual hosts in other than Tigase’s own data storage. \"\n\"This is especially important if you are going to modify the virtual \"\n\"domains list through systems other than Tigase.\"\nmsgstr \"\"\n\"您最有可能要重新实现的类是 `VHostJDBCRepository <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostJDBCRepository.java>`__\"\n\" 其用作默认虚拟主机存储和实现 `VHostRepository <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostRepository.java>`__ \"\n\"接口。您可能需要拥有自己的实现才能在Tigase自己的数据存储之外存储和访问虚拟主机。如果您要通过Tigase以外的系统修改虚拟域列表，这一点尤其重要。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:135\nmsgid \"\"\n\"The very basic virtual hosts storage is provided by `VHostItem \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostItem.java>`__ class. \"\n\"This is read only storage and provides the server a bootstrap vhosts data\"\n\" at the first startup time when the database with virtual hosts is empty \"\n\"or is not accessible. Therefore it is advised that all `VHostItem \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostItem.java>`__ \"\n\"implementations extend this class. The example code is provided in the \"\n\"`VHostJDBCRepository <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostJDBCRepository.java>`__\"\n\" file.\"\nmsgstr \"\"\n\"非常基本的虚拟主机存储由 `VHostItem <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostItem.java>`__ \"\n\"类提供。这是只读存储，当具有虚拟主机的数据库为空或不可访问时，它会在首次启动时为服务器提供引导虚拟主机数据。因此，建议所有\\n\"\n\"`VHostItem <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostItem.java>`__ \"\n\"实现扩展此类。示例代码在 `VHostJDBCRepository <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostJDBCRepository.java>`__\"\n\" 文件中提供。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:137\nmsgid \"\"\n\"All components which may need virtual hosts information or want to \"\n\"interact with virtual hosts management subsystem should implement the \"\n\"`VHostListener <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostListener.java>`__ \"\n\"interface. In some cases implementing this interface is necessary to \"\n\"receive packets for processing.\"\nmsgstr \"\"\n\"所有可能需要虚拟主机信息或想要与虚拟主机管理子系统交互的组件都应该实现 `VHostListener \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts /VHostListener.java>`__ \"\n\"接口。在某些情况下，实现此接口对于接收数据包进行处理是必要的。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:139\nmsgid \"\"\n\"Virtual host information is carried out in 2 forms inside the Tigase \"\n\"server:\"\nmsgstr \"虚拟主机信息在Tigase服务器内部以2种形式执行：\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:141\nmsgid \"As a **String** value with the domain name\"\nmsgstr \"作为带有域名的 **String** 值\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:143\nmsgid \"\"\n\"As a `VHostItem <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostItem.java>`__ which \"\n\"contains all the domain information including the domain name, maximum \"\n\"number of users for this domain, whether the domain is enabled or \"\n\"disabled and so on. The JavaDoc documentation contains all the details \"\n\"about all available fields and usage.\"\nmsgstr \"\"\n\"作为一个 `VHostItem <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostItem.java>`__，它包含了包括域名在内的所有域信息，此域的最大用户数，域是启用还是禁用等等。\"\n\" JavaDoc文档包含有关所有可用字段和用法的所有详细信息。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:145\nmsgid \"\"\n\"Here is a complete list of all interfaces and classes with a brief \"\n\"description for each of them:\"\nmsgstr \"以下是所有接口和类的完整列表，以及每个接口和类的简要说明：\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:147\nmsgid \"\"\n\"`VHostManagerIfc <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostManagerIfc.java>`__ -\"\n\" is an interface used to access virtual hosts information in all other \"\n\"server components. There is one default implementation of the interface: \"\n\"`VHostManager <#vhostMgr>`__.\"\nmsgstr \"\"\n\"`VHostManagerIfc <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostManagerIfc.java>`__ -\"\n\" 是用于访问所有其他虚拟主机信息的接口服务器组件。该接口有一个默认实现：`VHostManager <#vhostMgr>`__。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:149\nmsgid \"\"\n\"`VHostListener <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostListener.java>`__ - \"\n\"is an interface which allows components to interact with the \"\n\"`VHostManager <#vhostMgr>`__. The interaction is in both ways. The \"\n\"VHostManager provides virtual hosts information to components and \"\n\"components provide some control data required to correctly route packets \"\n\"to components.\"\nmsgstr \"\"\n\"`VHostListener <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostListener.java>`__ - \"\n\"是一个允许组件与 `VHostManager <#vhostMgr>`__ 交互的接口。 其交互是双向的。 \"\n\"VHostManager向组件提供虚拟主机信息，组件提供一些控制数据，以便将数据包正确路由到组件。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:151\nmsgid \"\"\n\"`VHostRepository <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostRepository.java>`__ -\"\n\" is an interface used to store and load virtual domains list from the \"\n\"database or any other storage media. There are 2 implementations for this\"\n\" interface: `VHostConfigRepository <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VhostConfigRepository.java>`__\"\n\" which loads vhosts information for the configuration file and provides \"\n\"read-only storage and - `VHostJDBCRepository <https://github.com/tigase\"\n\"/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostJDBCRepository.java>`__\"\n\" class which extends `VHostConfigRepository <https://github.com/tigase\"\n\"/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VhostConfigRepository.java>`__\"\n\" and allows for both - reading and saving virtual domains list. \"\n\"VHostJDBCRepository is loaded as a default repository by Tigase server.\"\nmsgstr \"\"\n\"`VHostRepository <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostRepository.java>`__ -\"\n\" 是一个用于存储和加载虚拟域列表的接口数据库或任何其他存储介质。此接口有2个实现：`VHostConfigRepository \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VhostConfigRepository.java>`__\"\n\" 加载vhosts信息配置文件并提供只读存储- 和 `VHostJDBCRepository <https://github.com/tigase\"\n\"/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostJDBCRepository.java>`__\"\n\" 类扩展 `VHostConfigRepository <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VhostConfigRepository.java>`__\"\n\" 并允许读取和保存虚拟域列表。 Tigase服务器将VHostJDBCRepository作为默认存储库加载。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:153\nmsgid \"\"\n\"`VHostItem <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostItem.java>`__ - is an\"\n\" interface which allows for accessing all the virtual domain properties. \"\n\"Sometimes the domain name is not sufficient for data processing. The \"\n\"domain may be temporarily disabled, may have a limited number of users \"\n\"and so on. Instances of this class keep all the information about the \"\n\"domain which might be needed by the server components.\"\nmsgstr \"\"\n\"`VHostItem <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostItem.java>`__ - \"\n\"是一个允许访问所有虚拟域属性的接口。有时域名不足以进行数据处理。该域可能被暂时禁用，可能有有限数量的用户等等。此类的实例保留有关服务器组件可能需要的域的所有信息。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:155\nmsgid \"\"\n\"`VHostManager <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostManager.java>`__ - \"\n\"the default implementation of the VHostManagerIfc interface. It provides \"\n\"components with the virtual hosts information and manages the virtual \"\n\"hosts list. Processes ad-hoc commands for reloading, updating and \"\n\"removing domains.\"\nmsgstr \"\"\n\"`VHostManager <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostManager.java>`__ - \"\n\"VHostManagerIfc接口的默认实现。它为组件提供虚拟主机信息并管理虚拟主机列表。处理用于重新加载、更新和删除域的临时命令。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:157\nmsgid \"\"\n\"`VHostConfirRepository <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VhostConfigRepository.java>`__\"\n\" - a very basic implementation of the `VHostRepository \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostRepository.java>`__ \"\n\"for loading domains list from the configuration file.\"\nmsgstr \"\"\n\"`VHostConfirRepository <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VhostConfigRepository.java>`__\"\n\" - `VHostRepository <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostRepository.java>`__ \"\n\"的一个非常基本的实现，用于从配置中加载域列表文件。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:159\nmsgid \"\"\n\"`VHostJDBCRepository <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostJDBCRepository.java>`__\"\n\" - the default implementation of the `VHostRepository \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostRepository.java>`__ \"\n\"loaded by Tigase server. It allows to read and store virtual domains list\"\n\" in the database accessible through UserRepository.\"\nmsgstr \"\"\n\"`VHostJDBCRepository <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostJDBCRepository.java>`__\"\n\" - Tigase服务器加载的 `VHostRepository <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/vhosts/VHostRepository.java>`__ \"\n\"的默认实现。它允许在通过UserRepository访问的数据库中读取和存储虚拟域列表。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:162\nmsgid \"Extending Virtual Domain settings\"\nmsgstr \"扩展虚拟域设置\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:164\nmsgid \"\"\n\"In some cases it is desired to extend Virtual Domain to add some \"\n\"additional settings. Since version 8.1.0 it is possible with use of \"\n\"``VHostItemExtension`` and VHostItemExtensionProvider`.\"\nmsgstr \"\"\n\"在某些情况下，需要扩展虚拟域以添加一些额外的设置。从8.1.0版开始，可以使用 ``VHostItemExtension`` 和 \"\n\"``VHostItemExtensionProvider``。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:166\nmsgid \"\"\n\"To do so, you need to create a class implementing ``VHostItemExtension``.\"\n\" This class will hold values of settings for each virtual host. It is \"\n\"required to make it serializable to ``Element`` and deserializable from \"\n\"``Element``. Moreover, it is required to make values of this class \"\n\"modifiable by ad-hoc commands.\"\nmsgstr \"\"\n\"为此，您需要创建一个实现 ``VHostItemExtension`` 的类。此类将保存每个虚拟主机的设置值。需要使其可序列化为 \"\n\"``Element`` 并且可从 ``Element`` 反序列化。此外，需要使该类的值可以通过临时命令进行修改。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:168\nmsgid \"\"\n\"It is recommended to provide additional methods allowing you to access \"\n\"values of this class.\"\nmsgstr \"建议提供额外的方法允许您访问此类的值。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:170\nmsgid \"\"\n\"Additionally, you need to implement ``VHostItemExtensionProvider`` \"\n\"interface as a bean and return a class of your implementation of \"\n\"``VHostItemExtension``.\"\nmsgstr \"\"\n\"此外，您需要将 ``VHostItemExtensionProvider`` 接口作为bean实现并返回 \"\n\"``VHostItemExtension`` 实现的类。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:172\nmsgid \"\"\n\"*Example VHostItemExtensionProvider implementation for* \"\n\"``SeeOtherHostVHostItemExtension``.\"\nmsgstr \"\"\n\"*用于* ``SeeOtherHostVHostItemExtension`` *的示例 VHostItemExtensionProvider \"\n\"实现*。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:191\nmsgid \"Stanza Limitations\"\nmsgstr \"节限制\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:193\nmsgid \"\"\n\"Although XMPP is robust and can process stanzas of any size in bytes, \"\n\"there are some limitations to keep in mind for Tigase server.\"\nmsgstr \"尽管XMPP很强大并且可以处理任意大小的节（以字节为单位），但对于Tigase服务器有一些限制需要牢记。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:195\nmsgid \"\"\n\"Please keep these in mind when using default Tigase settings and creating\"\n\" custom stanzas.\"\nmsgstr \"在使用默认Tigase设置和创建自定义节时请记住这些。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:197\nmsgid \"Limit to number of attributes of single element = 50 attributes\"\nmsgstr \"限制单个元素的属性数 = 50个属性\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:199\nmsgid \"Limit to number of elements = 1024 elements\"\nmsgstr \"元素数量限制 = 1024个元素\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:201\nmsgid \"Limit to length of element name = 1024 characters\"\nmsgstr \"元素名称长度限制 = 1024个字符\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:203\nmsgid \"Limit to length of attribute name = 1024 characters\"\nmsgstr \"属性名称长度限制 = 1024个字符\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:205\nmsgid \"Limit to length of attribute value = 10240 characters\"\nmsgstr \"属性值长度限制 = 10240个字符\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:207\nmsgid \"Limit to length of content of single element CDATA = 1048576b or 1Mb\"\nmsgstr \"单个元素CDATA的内容长度限制 = 1048576b或1Mb\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:209\nmsgid \"These values may be changed.\"\nmsgstr \"这些值可能会改变。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:211\nmsgid \"\"\n\"**Note that these limitations are to elements and attributes that may be \"\n\"within a stanza, but do not limit the overall stanza length.**\"\nmsgstr \"**请注意，这些限制是针对可能在一个节中的元素和属性，但不限制整个节的长度。**\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:214\nmsgid \"Escape Characters\"\nmsgstr \"转义字符\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:216\nmsgid \"\"\n\"There are special characters that need to be escaped if they are included\"\n\" in the stanza to avoid conflicts. The rules are similar to normal XML \"\n\"escaping. The following is a list of characters that need to be escaped \"\n\"and what to use to escape them:\"\nmsgstr \"如果某些特殊字符包含在节中，则需要对其进行转义以避免冲突。这些规则类似于普通的XML转义。以下是需要转义的字符列表以及使用什么来转义它们：\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:227\nmsgid \"API changes in the Tigase Server 5.x\"\nmsgstr \"Tigase Server 5.x中的API更改\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:229\nmsgid \"**THIS INFORMATION IS FOR OLDER VERSIONS OF TIGASE**\"\nmsgstr \"**此信息适用于旧版本的TIGASE**\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:231\nmsgid \"\"\n\"The API changes can effect you only if you develop own code to run inside\"\n\" Tigase server. The changes are not extensive but in some circumstances \"\n\"may require many simple changes in a few files.\"\nmsgstr \"仅当您开发自己的代码以在Tigase服务器中运行时，API更改才会对您产生影响。这些更改并不广泛，但在某些情况下可能需要在几个文件中进行许多简单的更改。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:233\nmsgid \"\"\n\"All the changes are related to introducing tigase.xmpp.JID and \"\n\"tigase.xmpp.BareJID classes. It is recommended to use them for all \"\n\"operations performed on the user JID instead of the String class which \"\n\"was used before changes.\"\nmsgstr \"\"\n\"所有更改都与引入 tigase.xmpp.JID 和 tigase.xmpp.BareJID \"\n\"类有关。建议将它们用于对用户JID执行的所有操作，而不是更改之前使用的String类。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:235\nmsgid \"\"\n\"There are a few advantages to using the new classes. First of all they do\"\n\" all the user JID checking and parsing, they also perform stringprep \"\n\"processing. Therefore if you use data kept by instance of the JID or \"\n\"BareJID you can be sure they are valid and correct.\"\nmsgstr \"\"\n\"使用新类有一些优点。首先他们对所有的用户JID进行检查和解析，他们也做stringprep处理。因此，如果您使用由JID或 \"\n\"BareJID实例保存的数据，您可以确定它们是有效且正确的。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:237\nmsgid \"\"\n\"These are not all advantages however. JID parsing code appears to use a \"\n\"lot of CPU power to conduct it’s operations. JIDs and parts of the JIDs \"\n\"are used in many places of the stanza processing and the parsing is \"\n\"performed over and over again in all these places, wasting CPU cycles, \"\n\"memory and time. Therefore, great performance benefits can be gained from\"\n\" these new class are in if, once parsed, JIDs are reused in all further \"\n\"stanza processing.\"\nmsgstr \"\"\n\"然而，这些并不是所有优点。 JID解析代码似乎使用了大量大功率的CPU来执行它的操作。 \"\n\"JID和部分JID用于节处理的许多地方，并且在所有这些地方一遍又一遍地执行解析，浪费了CPU周期、内存和时间。因此，如果JID在解析后在所有进一步的节处理中被重用，则可以从这些新类中获得巨大的性能优势。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:239\nmsgid \"\"\n\"This is where the tigase.server.Packet class comes in handy. Instances of\"\n\" the Packet class encloses XML stanza and pre-parses some, the most \"\n\"commonly used elements of the stanza, stanza source and destination \"\n\"addresses among them. As an effect there are all new methods available in\"\n\" the class:\"\nmsgstr \"\"\n\"这就是 tigase.server.Packet 类派上用场的地方。 \"\n\"Packet类的实例包含XML节并预解析一些节中最常用的元素、节源地址和目标地址。作为一种效果，该类中提供了所有新方法：\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:250\nmsgid \"Whereas following methods are still available but have been deprecated:\"\nmsgstr \"尽管以下方法仍然可用，但已被弃用：\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:257\nmsgid \"\"\n\"Please refer to the JavaDoc documentation for the `Packet \"\n\"<http://docs.tigase.org/tigase-\"\n\"server/snapshot/javadoc/tigase/server/Packet.html>`__ class and methods \"\n\"to learn all the details of these methods and difference between them.\"\nmsgstr \"\"\n\"请参考 `Packet <http://docs.tigase.org/tigase-server/snapshot/javadoc/tigase/\"\n\"server/Packet.html>`__ \"\n\"类和方法的JavaDoc文档来学习所有的这些方法的详细信息以及它们之间的区别。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:259\nmsgid \"\"\n\"Another difference is that you can no longer create the ``Packet`` \"\n\"instance using a constructor. Instead there are a few factory methods \"\n\"available:\"\nmsgstr \"另一个区别是您不能再使用构造函数创建 ``Packet`` 实例。相反，有一些工厂方法可用：\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:267\nmsgid \"\"\n\"Again, please refer to the JavaDoc documentation for all the details. The\"\n\" main point of using these methods is that they actually return an \"\n\"instance of one of the following classes instead of the ``Packet`` class:\"\n\" ``Iq``, ``Presence`` or ``Message``.\"\nmsgstr \"\"\n\"同样，请参阅JavaDoc文档了解所有详细信息。使用这些方法的要点是它们实际上返回以下类之一的实例，而不是 ``Packet`` \"\n\"类：``Iq``、``Presence`` 或 ``Message``。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:269\nmsgid \"\"\n\"There is also a number of utility methods helping with creating a copy of\"\n\" the Packet instance preserving as much pre-parsed data as possible:\"\nmsgstr \"还有一些实用方法可以帮助创建Packet实例的副本，以保留尽可能多的预解析数据：\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:279\nmsgid \"\"\n\"We try to keep the `JavaDoc <http://docs.tigase.org/tigase-\"\n\"server/snapshot/javadoc/>`__ documentation as complete as possible. \"\n\"Please contact us if you find missing or incorrect information.\"\nmsgstr \"\"\n\"我们尽量保持 `JavaDoc <http://docs.tigase.org/tigase-\"\n\"server/snapshot/javadoc/>`__ 文档尽可能完整。如果您发现信息缺失或不正确，请与我们联系。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:281\nmsgid \"\"\n\"The main point is to reuse ``JID`` or ``BareJID`` instances in your code \"\n\"as much as possible. You never know, your code may run in highly loaded \"\n\"systems with throughput of 100k XMPP packets per second.\"\nmsgstr \"\"\n\"要点是尽可能地在代码中重用 ``JID`` 或 ``BareJID`` \"\n\"实例。您永远不会知道，您的代码可能会在负载为每秒100k的XMPP数据包的高负载系统中运行。\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:283\nmsgid \"\"\n\"Another change. This one a bit risky as it is very difficult to find all \"\n\"places where this could be used. There are several utility classes and \"\n\"methods which accept source and destination address of a stanza and \"\n\"produce something. There was a great confusion with them, as in some of \"\n\"them the first was the source address and in others the destination \"\n\"address. All the code has been re-factored to keep the parameter order \"\n\"the same in all places. Right now the policy is: **source address \"\n\"first**. Therefore in all places where there was a method:\"\nmsgstr \"另一个变化。这个有点冒险，因为很难找到所有可以使用它的地方。有几个实用程序类和方法可以接受节的源地址和目标地址并产生一些东西。它们很混乱，因为其中一些是源地址，而另一些则是目标地址。所有代码都经过重构，以保持所有位置的参数顺序相同。目前的政策是：**源地址优先**。因此，在所有有以下方法的地方：\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:289\nmsgid \"it has been changed to:\"\nmsgstr \"它已更改为：\"\n\n#: ../../Tigase_Development/Old_Stuff.rst:295\nmsgid \"\"\n\"As far as I know most of these method were used only by myself so I do \"\n\"not expect much trouble for other developers.\"\nmsgstr \"据我所知，这些方法中的大部分都是我自己使用的，所以我不希望其他开发人员有太多麻烦。\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Development/Packet_Filtering_in_Component.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-05-27 12:30-0700\\n\"\n\"PO-Revision-Date: 2022-07-25 21:34+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/dg-packet_filtering_in_component/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:4\nmsgid \"Packet Filtering in Components\"\nmsgstr \"组件中的包过滤\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:7\nmsgid \"The Packet Filter API\"\nmsgstr \"包过滤器API\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:9\nmsgid \"\"\n\"Tigase server offers an API to filter packet traffic inside every \"\n\"component. You can separately filter incoming and outgoing packets.\"\nmsgstr \"Tigase服务器提供了一个API来过滤每个组件内的数据包流量。您可以分别过滤传入和传\"\n\"出的数据包。\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:11\nmsgid \"\"\n\"By filtering we mean intercepting a packet and possibly making some \"\n\"changes to the packet or just blocking the packet completely. By blocking\"\n\" we mean stopping from any further processing and just dropping the \"\n\"packet.\"\nmsgstr \"通过过滤，我们的意思是拦截一个数据包，并可能对数据包进行一些更改，或者只是完\"\n\"全阻止数据包。阻塞是指停止任何进一步的处理并丢弃数据包。\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:13\nmsgid \"\"\n\"The packet filtering is based on the `PacketFilterIfc \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/server/PacketFilterIfc.java>`__ \"\n\"interface. Please have a look in the JavaDoc documentation to this \"\n\"interface for all the details. The main filtering method is ``Packet \"\n\"filter(Packet packet);`` which takes packets as an input, processes it, \"\n\"possibly alerting the packet content (may add or remove some payloads) \"\n\"and returns a **Packet** for further processing. If it returns **null** \"\n\"it means the packet is blocked and no further processing is permitted \"\n\"otherwise it returns a **Packet** object which is either the same object \"\n\"it received as a parameter or a modified copy of the original object.\"\nmsgstr \"\"\n\"包过滤基于 `PacketFilterIfc <https://github.com/tigase/tigase-server/blob/\"\n\"master/src/main/java/tigase/server/PacketFilterIfc.java>`__ \"\n\"接口。有关所有详细信息，请查看此接口的JavaDoc文档。主要的过滤方法是 ``Packet \"\n\"filter(Packet packet);`` ，它将数据包作为输入，对其进行处理，可能会警告数据包\"\n\"内容（可能会添加或删除一些有效负载）并返回一个 **Packet** 以进行进一步处理。\"\n\"如果它返回 **null** 这意味着数据包被阻止并且不允许进一步处理，否则它返回一个 \"\n\"**Packet** 对象，该对象是它作为参数接收的相同对象或原始对象的修改副本。\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:15\nmsgid \"\"\n\"Please note, although **Packet** object is not an unmodifiable instance, \"\n\"it is recommended that changes to the existing object are not made. The \"\n\"same **Packet** might be processed at the same time by other components \"\n\"or threads, therefore modification of the **Packet** may lead to \"\n\"unpredictable results.\"\nmsgstr \"\"\n\"请注意，虽然 **Packet** \"\n\"对象不是不可修改的实例，但建议不要对现有对象进行更改。同一个 **Packet** \"\n\"可能会被其他组件或线程同时处理，因此修改 **Packet** \"\n\"可能会导致不可预知的结果。\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:17\nmsgid \"\"\n\"Please refer to an example code in `PacketCounter \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/server/filters/PacketCounter.java>`__\"\n\" which is a very simple filter counting different types of packets. This \"\n\"filter is by default loaded to all components which might be very helpful\"\n\" for assessing traffic shapes on newly deployed installation. You can get\"\n\" counters for all types of packets, where they are generated, where they \"\n\"flow, what component they put the most load on.\"\nmsgstr \"\"\n\"请参考 `PacketCounter <https://github.com/tigase/tigase-server/blob/master/\"\n\"src/main/java/tigase/server/filters/PacketCounter.java>`__ 中的示例代码，这是\"\n\"一个非常简单的过滤器计算不同类型的数据包。此过滤器默认加载到所有组件，这可能\"\n\"对评估新部署安装的流量形状非常有帮助。您可以获得所有类型的数据包的计数器，它\"\n\"们是在哪里生成的，它们在哪里流动，它们在哪个组件上负载最多。\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:19\nmsgid \"\"\n\"This is because packet filter can also generate and present its own \"\n\"statistics which are accessible via normal statistics monitoring \"\n\"mechanisms. To take advantage of the statistics functionality, the packet\"\n\" filter has to implement the ``void getStatistics(StatisticsList list);``\"\n\" method. Normally, the method is empty. However, you can generate and add\"\n\" statistics from the filter to the list. Please refer to `PacketCounter \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/server/filters/PacketCounter.java>`__\"\n\" for an example implementation code.\"\nmsgstr \"\"\n\"这是因为数据包过滤器还可以生成和呈现自己的统计信息，这些统计信息可以通过正常\"\n\"的统计监控机制访问。为了利用统计功能，包过滤器必须实现 ``void getStatistics(\"\n\"StatisticsList list);`` \"\n\"方法。通常，该方法为空。但是，您可以从过滤器生成统计信息并将其添加到列表中。\"\n\"示例实现代码请参考 `PacketCounter <https://github.com/tigase/tigase-server/\"\n\"blob/master/src/main/java/tigase/server/filters/PacketCounter.java>`__。\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:22\nmsgid \"Configuration\"\nmsgstr \"配置\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:24\nmsgid \"\"\n\"Packet filters are configurable, that is a packet filters instances can \"\n\"be configured in Tigase server’s configuration for each component \"\n\"separately and for each traffic direction. This gives you a great \"\n\"flexibility and control over the data flow inside the Tigase server.\"\nmsgstr \"\"\n\"包过滤器是可配置的，即可以在Tigase服务器的配置中为每个组件和每个流量方向单独\"\n\"配置包过滤器实例。这为您提供了极大的灵活性和对Tigase服务器内部数据流的控制。\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:26\nmsgid \"\"\n\"You can for example, load specific packet filters to all connections \"\n\"managers to block specific traffic or specific packet source from sending\"\n\" messages to users on your server. You could also reduce the server \"\n\"overall load by removing certain payload from all packets. The \"\n\"possibilities are endless.\"\nmsgstr \"\"\n\"例如，您可以将特定的数据包过滤器加载到所有连接管理器，以阻止特定流量或特定数\"\n\"据包源向服务器上的用户发送消息。您还可以通过从所有数据包中删除某些有效负载来\"\n\"减少服务器的整体负载。这有无限的可能性。\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:28\nmsgid \"\"\n\"The default configuration is generated in such a way that each component \"\n\"loads a single packet filter - ``PacketCounter`` for each traffic \"\n\"direction:\"\nmsgstr \"默认配置以这样一种方式生成，即每个组件加载单个数据包过滤器 - 每个流量方向的 \"\n\"``PacketCounter``：\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:83\nmsgid \"\"\n\"Now, let’s say you have a packet filter implemented in class: \"\n\"**com.company.SpamBlocker**. You want to disable PacketCounter on most of\"\n\" the components leaving it only in the message router component and you \"\n\"want to install SpamBlocker in all connection managers.\"\nmsgstr \"\"\n\"现在，假设您在类中实现了一个数据包过滤器：**com.company.SpamBlocker**。您希望\"\n\"在大多数组件上禁用PacketCounter，仅将其留在消息路由器组件中，并且您希望在所有\"\n\"连接管理器中安装SpamBlocker。\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:85\nmsgid \"\"\n\"*Please note, in case of the connection managers 'incoming' and \"\n\"'outgoing' traffic is probably somehow opposite from what you would \"\n\"normally expect.*\"\nmsgstr \"*请注意，连接管理器的'incoming'和 'outgoing'流量可能与您通常的预期相反。*\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:87\nmsgid \"\"\n\"**incoming** is traffic which is submitted to a component by message \"\n\"router and has to be further processed. For connection managers this \"\n\"further processing means sending it out to the network.\"\nmsgstr \"**incoming** 是由消息路由器提交给组件的流量，必须进一步处理。对于连接管理器，\"\n\"此进一步处理意味着将其发送到网络。\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:89\nmsgid \"\"\n\"**outgoing** is traffic which is 'generated' by the component and goes \"\n\"out of the component. Such a packet is submitted to message router which \"\n\"then decides where to send it for further processing. For connection \"\n\"managers **outgoing** traffic is all the packets just received from the \"\n\"network.\"\nmsgstr \"\"\n\"**outgoing** 是由组件'生成'并流出组件的流量。这样的数据包被提交给消息路由器，\"\n\"然后由路由器决定将其发送到何处以进行进一步处理。对于连接管理器，**传出** \"\n\"流量是刚从网络收到的所有数据包。\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:91\nmsgid \"\"\n\"According to that we have to apply the SpamBlocker filter to all \"\n\"'outgoing' traffic in all connection managers. You may also decide that \"\n\"it might be actually useful to compare traffic shape between Bosh \"\n\"connections and standard XMPP c2s connections. So let’s leave packet \"\n\"counters for this components too.\"\nmsgstr \"\"\n\"据此，我们必须将SpamBlocker过滤器应用于所有连接管理器中的所有'传出‘流量。\"\n\"您可能还认为比较Bosh连接和标准XMPP c2s连接之间的流量形状实际上可能很有用。因\"\n\"此，让我们也为这些组件保留数据包计数器。\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:93\nmsgid \"\"\n\"Here is our new configuration applying SpamBlocker to connection managers\"\n\" and PacketCounter to a few other components:\"\nmsgstr \"这是我们将SpamBlocker应用于连接管理器并将PacketCounter应用于其他一些组件的新\"\n\"配置：\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:151\nmsgid \"\"\n\"In case of ``incomingFilters`` ``outgoingFilters`` and ``packetCounter`` \"\n\"we were able to skip providing ``class`` parameter as those classes are \"\n\"properly annotated with ``@Bean`` annotation.\"\nmsgstr \"\"\n\"在 ``incomingFilters`` ``outgoingFilters`` 和 ``packetCounter`` 的情况下，\"\n\"我们能够跳过提供 ``class`` 参数，因为这些类正确地使用了 ``@Bean`` 注释。\"\n\n#: ../../Tigase_Development/Packet_Filtering_in_Component.rst:153\nmsgid \"\"\n\"The simplest way to apply the new configuration is via the \"\n\"``config.tdsl`` file which is in details described in the *Admin Guide*.\"\nmsgstr \"应用新配置的最简单方法是通过 *Admin Guide* 中详细描述的 ``config.tdsl`` \"\n\"文件。\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Development/Plugin_Development.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: 2022-08-31 22:29+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/dg-plugin_development/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:2\nmsgid \"Plugin Development\"\nmsgstr \"插件开发\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:4\nmsgid \"\"\n\"This is a set of documents explaining details what is a plugin, how they \"\n\"are designed and how they work inside the Tigase server. The last part of\"\n\" the documentation explains step by step creating the code for a new \"\n\"plugin.\"\nmsgstr \"这是一组文档，详细解释了什么是插件、它们是如何设计的以及它们如何在Tigase服务器中工作。文档的最后一部分逐步解释了如何为新插件创建代码。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:6\nmsgid \":ref:`Writing Plugin Code<writePluginCode>`\"\nmsgstr \":ref:`编写插件代码<writePluginCode>`\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:8\nmsgid \":ref:`Plugin Configuration<pluginConf>`\"\nmsgstr \":ref:`插件配置<pluginConf>`\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:10\nmsgid \":ref:`How Packets are Processed by the SM and Plugins<packetprocess>`\"\nmsgstr \":ref:`SM和插件如何处理数据包<packetprocess>`\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:12\nmsgid \":ref:`SASL Custom Mechanisms and Configuration<saslcmac>`\"\nmsgstr \":ref:`SASL自定义机制和配置<saslcmac>`\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:17\nmsgid \"Writing Plugin Code\"\nmsgstr \"编写插件代码\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:19\nmsgid \"\"\n\"Stanza processing takes place in 4 steps. A different kind of plugin is \"\n\"responsible for each step of processing:\"\nmsgstr \"节处理分4个步骤进行。不同类型的插件负责处理的每个步骤：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:21\nmsgid \"\"\n\"`XMPPPreprocessorIfc <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/xmpp/XMPPPreprocessorIfc.java>`__\"\n\" - is the interface for packets pre-processing plugins.\"\nmsgstr \"\"\n\"`XMPPPreprocessorIfc <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/xmpp/XMPPPreprocessorIfc.java>`__\"\n\" - 是数据包预处理插件的接口。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:23\nmsgid \"\"\n\"`XMPPProcessorIfc <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/xmpp/XMPPProcessor.java>`__ - is \"\n\"the interface for packets processing plugins.\"\nmsgstr \"\"\n\"`XMPPProcessorIfc <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/xmpp/XMPPProcessor.java>`__ - \"\n\"是数据包处理插件的接口。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:25\nmsgid \"\"\n\"`XMPPPostprocessorIfc <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/xmpp/XMPPPostprocessorIfc.java>`__\"\n\" - is the interface for packets post-processing plugins.\"\nmsgstr \"\"\n\"`XMPPPostprocessorIfc <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/xmpp/XMPPPostprocessorIfc.java>`__\"\n\" - 是数据包后处理插件的接口。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:27\nmsgid \"\"\n\"`XMPPPacketFilterIfc <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/xmpp/XMPPPacketFilterIfc.java>`__\"\n\" - is the interface for processing results filtering.\"\nmsgstr \"\"\n\"`XMPPPacketFilterIfc <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/xmpp/XMPPPacketFilterIfc.java>`__\"\n\" - 是处理结果过滤的接口。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:29\nmsgid \"\"\n\"If you look inside any of these interfaces you will only find a single \"\n\"method. This is where all the packet processing takes place. All of them \"\n\"take a similar set of parameters and below is a description for all of \"\n\"them:\"\nmsgstr \"如果您查看这些接口中的任何一个，您只会发现一个方法。这是所有数据包处理发生的地方。它们都采用一组相似的参数，下面是对它们的描述：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:31\nmsgid \"\"\n\"**Packet packet** - packet is which being processed. This parameter may \"\n\"never be null. Even though this is not an immutable object it mustn’t be \"\n\"altered. None of it’s fields or attributes can be changed during \"\n\"processing.\"\nmsgstr \"\"\n\"**数据包数据包** - \"\n\"正在处理的数据包。此参数可能永远不会为空。即使这不是一个不可变的对象，也不能改变它。在处理过程中，它的任何字段或属性都不能更改。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:33\nmsgid \"\"\n\"**XMPPResourceConnection session** - user session which keeps all the \"\n\"user session data and also gives access to the user’s data repository. It\"\n\" allows for the storing of information in permanent storage or in memory \"\n\"only during the life of the session. This parameter can be null if there \"\n\"is no online user session at the time of the packet processing.\"\nmsgstr \"\"\n\"**XMPPResourceConnection session** - \"\n\"用户会话，它保留所有用户会话数据并提供对用户数据存储库的访问权限。它允许仅在会话期间将信息存储在永久存储器或内存中。如果在处理数据包时没有在线用户会话，则该参数可以为空。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:35\nmsgid \"\"\n\"**NonAuthUserRepository repo** - this is a user data storage which is \"\n\"normally used when the user session (parameter above) is null. This \"\n\"repository allows for a very restricted access only. It allows for \"\n\"storing some user private data (but doesn’t allow overwriting existing \"\n\"data) like messages for offline users and it also allows for reading user\"\n\" public data like VCards.\"\nmsgstr \"\"\n\"**NonAuthUserRepository repo** - \"\n\"这是一个用户数据存储，通常在用户会话（上述参数）为空时使用。此存储库仅允许非常受限的访问。它允许存储一些用户私有数据（但不允许覆盖现有数据），例如离线用户的消息，它还允许读取用户公共数据，例如VCards。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:37\nmsgid \"\"\n\"**Queue<Packet> results** - this a collection with packets which have \"\n\"been generated as input packet processing results. Regardless a response \"\n\"to a user request is sent or the packet is forwarded to it’s destination \"\n\"it is always required that a copy of the input packet is created and \"\n\"stored in the **results** queue.\"\nmsgstr \"\"\n\"**Queue<Packet> results** - \"\n\"这是一个包含作为输入数据包处理结果生成的数据包的集合。无论发送对用户请求的响应还是将数据包转发到其目的地，始终需要创建输入数据包的副本并将其存储在\"\n\" **results** 队列中。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:39\nmsgid \"\"\n\"**Map<String, Object> settings** - this map keeps plugin specific \"\n\"settings loaded from the Tigase server configuration. In most cases it is\"\n\" unused, however if the plugin needs to access an external database that \"\n\"this is a way to pass the database connection string to the plugin.\"\nmsgstr \"\"\n\"**Map<String, Object> settings** - \"\n\"此映射保留从Tigase服务器配置加载的插件特定设置。在大多数情况下，它是未使用的，但是如果插件需要访问外部数据库，这是将数据库连接字符串传递给插件的一种方式。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:41\nmsgid \"\"\n\"After a closer look in some of the interfaces you can see that they \"\n\"extend another interface: `XMPPImplIfc <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/xmpp/XMPPImplIfc.java>`__ which \"\n\"provides a basic meta information about the plugin implementation. Please\"\n\" refer to `JavaDoc <http://docs.tigase.org/tigase-\"\n\"server/snapshot/javadoc/tigase/xmpp/impl/package-summary.html>`__ \"\n\"documentation for all details.\"\nmsgstr \"\"\n\"仔细查看一些接口后，您会发现它们扩展了另一个接口：`XMPPImplIfc <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/xmpp/XMPPImplIfc .java>`__ \"\n\"提供有关插件实现的基本元信息。有关所有详细信息，请参阅 `JavaDoc <http://docs.tigase.org/tigase-\"\n\"server/snapshot/javadoc/tigase/xmpp/impl/package-summary.html>`__ 文档。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:43\nmsgid \"\"\n\"For purpose of this guide we are implementing a simple plugin for \"\n\"handling all **<message/>** packets that is forwarding packets to the \"\n\"destination address. Incoming packets are forwarded to the user \"\n\"connection and outgoing packets are forwarded to the external destination\"\n\" address. This `message plugin <https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/xmpp/impl/Message.java>`__ is \"\n\"actually implemented already and it is available in our Git repository. \"\n\"The code has some comments inside already but this guide goes deeper into\"\n\" the implementation details.\"\nmsgstr \"\"\n\"出于本指南的目的，我们正在实现一个简单的插件，用于处理所有将数据包转发到目标地址的 **<message/>** \"\n\"数据包。传入的数据包被转发到用户连接，而传出的数据包被转发到外部目标地址。这个 `message plugin \"\n\"<https://github.com/tigase/tigase-\"\n\"server/blob/master/src/main/java/tigase/xmpp/impl/Message.java>`__ \"\n\"实际上已经实现并且它可以在我们的Git存储库。代码里面已经有一些注释，但本指南更深入地介绍了实现细节。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:45\nmsgid \"\"\n\"First of all you have to choose what kind of plugin you want to \"\n\"implement. If this is going to be a packet processor you have to \"\n\"implement the **XMPPProcessorIfc** interface, if this is going to be a \"\n\"pre-processor then you have to implement the **XMPPPreprocessorIfc** \"\n\"interface. Of course your implementation can implement more than one \"\n\"interface, even all of them. There are also two abstract helper classes, \"\n\"one of which you should use as a base for all you plugins \"\n\"**XMPPProcessor** or use **AnnotatedXMPPProcessor** for annotation \"\n\"support.\"\nmsgstr \"\"\n\"首先，您必须选择要实现的插件类型。如果这将是一个数据包处理器，您必须实现 **XMPPProcessorIfc** \"\n\"接口，如果这将是一个预处理器，那么您必须实现 **XMPPPreprocessorIfc** \"\n\"接口。当然，您的实现可以实现多个接口，甚至是所有接口。还有两个抽象帮助类，您应该将其中一个用作所有插件的基础 **XMPPProcessor** \"\n\"或使用 **AnnotatedXMPPProcessor** 来支持注释。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:48\nmsgid \"Using annotation support\"\nmsgstr \"使用注释支持\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:50\n#: ../../Tigase_Development/Plugin_Development.rst:85\nmsgid \"\"\n\"The class declaration should look like this (assuming you are \"\n\"implementing just the packet processor):\"\nmsgstr \"类声明应如下所示（假设您只实现数据包处理器）：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:57\nmsgid \"\"\n\"The first thing to create is the plugin **ID**. This is a unique string \"\n\"which you put in the configuration file to tell the server to load and \"\n\"use the plugin. In most cases you can use XMLNS if the plugin wants \"\n\"packets with elements with a very specific name space. Of course there is\"\n\" no guarantee there is no other packet for this specific XML element too.\"\n\" As we want to process all messages and don’t want to spend whole day on \"\n\"thinking about a cool ID, let’s say our ID is: *message*.\"\nmsgstr \"\"\n\"首先要创建插件 \"\n\"**ID**。这是您放入配置文件中的唯一字符串，用于告诉服务器加载和使用插件。在大多数情况下，如果插件想要包含具有非常特定名称空间的元素的数据包，您可以使用XMLNS。当然，不能保证这个特定的XML元素也没有其他数据包。由于我们想处理所有消息并且不想花一整天时间考虑一个很酷的ID，假设我们的ID是：*message*。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:59\nmsgid \"\"\n\"A plugin informs about it’s presence using a static **ID** field and \"\n\"**@Id** annotation placed on class:\"\nmsgstr \"插件使用放置在类上的静态 **ID** 字段和 **@Id** 注释来通知它的存在：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:69\nmsgid \"\"\n\"As mentioned before, this plugin receives only this kind of packets for \"\n\"processing which it is interested in. In this example, the plugin is \"\n\"interested only in packets with **<message/>** elements and only if they \"\n\"are in the \\\"**jabber:client**\\\" namespace. To indicate all supported \"\n\"elements and namespaces we have to add 2 more annotations:\"\nmsgstr \"\"\n\"如前所述，这个插件只接收它感兴趣的数据包进行处理。在这个例子中，插件只对带有 **<message/>** \"\n\"元素的数据包感兴趣，并且只有它们在\\\"**jabber:client**\\\"命名空间。为了表明所有支持的元素和命名空间，我们必须再添加2个注释：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:83\nmsgid \"Using older non-annotation based implementation\"\nmsgstr \"使用较旧的非基于注释的实现\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:92\nmsgid \"The first thing to create is the plugin **ID** like above.\"\nmsgstr \"首先要创建的是插件 **ID** ，如上所示。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:94\nmsgid \"A plugin informs about it’s ID using following code:\"\nmsgstr \"插件使用以下代码通知它的ID：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:101\nmsgid \"\"\n\"As mentioned before this plugin receives only this kind of packets for \"\n\"processing which it is interested in. In this example, the plugin is \"\n\"interested only in packets with **<message/>** elements and only if they \"\n\"are in \\\"**jabber:client**\\\" namespace. To indicate all supported \"\n\"elements and namespaces we have to add 2 more methods:\"\nmsgstr \"\"\n\"如前所述，这个插件只接收它感兴趣的这种数据包进行处理。在这个例子中，插件只对带有 **<message/>** 元素的数据包感兴趣，并且只有它们在\"\n\" \\\"**jabber :client**\\\" 命名空间。为了表明所有支持的元素和命名空间，我们必须再添加2个方法：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:114\nmsgid \"Implementation of processing method\"\nmsgstr \"处理方法的实现\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:116\nmsgid \"\"\n\"Now we have our plugin prepared for loading in Tigase. The next step is \"\n\"the actual packet processing method. For the complete code, please refer \"\n\"to the plugin in the Git. I will only comment here on elements which \"\n\"might be confusing or add a few more lines of code which might be helpful\"\n\" in your case.\"\nmsgstr \"现在我们已经准备好在Tigase中加载的插件。下一步是实际的数据包处理方法。完整代码请参考Git中的插件。我只会在此处对可能令人困惑的元素进行评论或添加更多代码，这可能对您的情况有所帮助。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:215\nmsgid \"Plugin Configuration\"\nmsgstr \"插件配置\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:217\nmsgid \"Plugin configuration is straightforward.\"\nmsgstr \"插件配置很简单。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:219\nmsgid \"\"\n\"Tell the Tigase server to load or not to load the plugins via the \"\n\"``config.tdsl`` file. Plugins fall within the ``'sess-man'`` container. \"\n\"To activate a plugin, simply list it among the sess-man plugins.\"\nmsgstr \"\"\n\"通过 ``config.tdsl`` 文件告诉Tigase服务器加载或不加载插件。插件属于 ``'sess-man'`` \"\n\"容器。要激活插件，只需将其列在 sess-man插件中即可。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:221\nmsgid \"\"\n\"If you do not wish to use this method to find out what plugins are \"\n\"running, there are two ways you can identify if a plugin is running. One \"\n\"is the log file: logs/tigase-console.log. If you look inside you can find\"\n\" following output:\"\nmsgstr \"\"\n\"如果您不想使用此方法来找出正在运行的插件，有两种方法可以识别插件是否正在运行。一是日志文件：logs/tigase-\"\n\"console.log。如果您查看内部，您会发现以下输出：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:240\nmsgid \"and this is a list of plugins which are loaded in your installation.\"\nmsgstr \"这是在您的安装中加载的插件列表。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:242\nmsgid \"\"\n\"Another way is to look inside the session manager source code which has \"\n\"the default list hardcoded:\"\nmsgstr \"另一种方法是查看具有硬编码默认列表的会话管理器源代码：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:254\nmsgid \"\"\n\"In you wish to load a plugin outside these defaults, you have to edit the\"\n\" list and add your plugin IDs as a value to the plugin list under 'sess-\"\n\"man'. Let’s say our plugin ID is **message** as in our all examples:\"\nmsgstr \"\"\n\"如果您希望在这些默认值之外加载插件，您必须编辑列表并将您的插件ID作为值添加到'sess-man'下的插件列表中。假设我们的插件ID是 \"\n\"**message** 其在我们所有的示例中：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:264\nmsgid \"\"\n\"Assuming your plugin class is in the classpath it will be loaded and used\"\n\" at the runtime. You may specify class by adding ``class: \"\n\"class.implementing.plugin`` within the parenthesis of the plugin.\"\nmsgstr \"\"\n\"假设您的插件类位于类路径中，它将在运行时加载和使用。您可以通过在插件的括号内添加 ``class: \"\n\"class.implementing.plugin`` 来指定类。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:268\nmsgid \"\"\n\"If your plugin name has any special characters (-,:\\\\|/.) it needs to be \"\n\"encapsulated in single quotation marks.\"\nmsgstr \"如果您的插件名称有任何特殊字符（-,:\\\\|/.），则需要将其封装在单引号中。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:270\nmsgid \"\"\n\"There is another part of the plugin configuration though. If you looked \"\n\"at the :ref:`Writing Plugin Code <writePluginCode>` guide you can \"\n\"remember the **Map settings** processing parameter. This is a map of \"\n\"properties you can set in the configuration file and these setting will \"\n\"be passed to the plugin at the processing time.\"\nmsgstr \"\"\n\"不过，插件配置还有另一部分。如果您查看了 :ref:`编写插件代码<writePluginCode>`\"\n\" 指南，您可以记住 **Map settings** 处理参数。这是您可以在配置文件中设置的属性\"\n\"映射，这些设置将在处理时传递给插件。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:273\nmsgid \"\"\n\"Again **config.tdsl** is the place to put the stuff. These kind of \"\n\"properties start under your **plugin ID** and each key and value will be \"\n\"a child underneath:\"\nmsgstr \"**config.tdsl** 就是放置这些东西的地方。这些属性从您的 **plugin ID** 开始，每个键和值都是下面的子项：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:287\nmsgid \"\"\n\"From v8.0.0 you will no longer be able to specify one value for multiple \"\n\"keys, you must set each one individually.\"\nmsgstr \"从v8.0.0开始，您将不再能够为多个键指定一个值，您必须单独设置每个键。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:289\nmsgid \"Last but not least - in case you have **omitted plugin ID**:\"\nmsgstr \"最后但并非最不重要 - 如果您有 **省略插件ID**：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:297\nmsgid \"\"\n\"then the configured key-value pair will be a global/common plugin setting\"\n\" available to all loaded plugins.\"\nmsgstr \"那么配置的键值对将是一个全局/通用插件设置，可用于所有加载的插件。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:302\nmsgid \"How Packets are Processed by the SM and Plugins\"\nmsgstr \"SM和插件如何处理数据包\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:304\nmsgid \"\"\n\"For Tigase server plugin development it is important to understand how it\"\n\" all works. There are different kind of plugins responsible for \"\n\"processing packets at different stages of the data flow. Please read the \"\n\"introduction below before proceeding to the actual coding part.\"\nmsgstr \"对于Tigase服务器插件开发，了解它是如何工作的很重要。有不同类型的插件负责在数据流的不同阶段处理数据包。在进行实际编码部分之前，请阅读下面的介绍。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:307\nmsgid \"Introduction\"\nmsgstr \"介绍\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:309\nmsgid \"\"\n\"In Tigase server **plugins** are pieces of code responsible for \"\n\"processing particular XMPP stanzas. A separate plugin might be \"\n\"responsible for processing messages, a different one for processing \"\n\"presences, a separate plugins responsible for iq roster, and a different \"\n\"one for iq version and so on.\"\nmsgstr \"\"\n\"在Tigase服务器中，**插件** \"\n\"是负责处理特定XMPP节的代码片段。一个单独的插件可能负责处理消息，另一个插件负责处理存在，一个单独的插件负责iq \"\n\"roster，另一个插件负责iq版本等等。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:311\nmsgid \"\"\n\"A plugin provides information about what exact XML element(s) name(s) \"\n\"with xmlns it is interested in. So you can create a plugin which is \"\n\"interested in all packets containing caps child.\"\nmsgstr \"插件提供了有关它感兴趣的带有xmlns的确切XML元素名称的信息。因此，您可以创建一个插件，该插件对包含caps子项的所有数据包感兴趣。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:313\nmsgid \"\"\n\"There might be no plugin for a particular stanza element, in this case \"\n\"the default action is used which is simple forwarding stanza to a \"\n\"destination address. There might be also more than one plugin for a \"\n\"specific XML element and then they all process the same stanza \"\n\"simultaneously in separate threads so there is no guarantee on the order \"\n\"in which the stanza is processed by a different plugins.\"\nmsgstr \"特定节元素可能没有插件，在这种情况下，使用默认操作，即简单地将节转发到目标地址。一个特定的XML元素可能还有多个插件，然后它们都在不同的线程中同时处理相同的节，因此不能保证不同插件处理节的顺序。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:315\nmsgid \"\"\n\"Each stanza goes through the Session Manager component which processes \"\n\"packets in a few steps. Have a look at the picture below:\"\nmsgstr \"每个节都通过会话管理器组件，该组件通过几个步骤处理数据包。看看下面的图片：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:317\nmsgid \"|Consumer|\"\nmsgstr \"|Consumer|\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:357\nmsgid \"Consumer\"\nmsgstr \"Consumer\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:319\nmsgid \"\"\n\"The picture shows that each stanza is processed by the session manager in\"\n\" 4 steps:\"\nmsgstr \"图片显示会话管理器分4步处理每个节：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:321\nmsgid \"\"\n\"Pre-processing - All loaded pre-processors receive the packet for \"\n\"processing. They work within session manager thread and they have no \"\n\"internal queue for processing. As they work within Session Manager thread\"\n\" it is important that they limit processing time to absolute minimum as \"\n\"they may affect the Session Manager performance. The intention for the \"\n\"pre-processors is to use them for packet blocking. If the pre-processing \"\n\"result is 'true' then the packet is blocked and no further processing is \"\n\"performed.\"\nmsgstr \"\"\n\"预处理 - \"\n\"所有加载的预处理器接收数据包进行处理。它们在会话管理器线程中工作，并且没有用于处理的内部队列。由于它们在会话管理器线程中工作，因此将处理时间限制在绝对最短非常重要，因为它们可能会影响会话管理器的性能。预处理器的目的是将它们用于数据包阻塞。如果预处理结果为'true'，则数据包被阻塞并且不执行进一步处理。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:323\nmsgid \"\"\n\"Processing - This is the next step the packet gets through if it wasn’t \"\n\"blocked by any of the pre-processors. It gets inserted to all processors \"\n\"queues with requested interest in this particular XML element. Each \"\n\"processor works in a separate thread and has own internal fixed size \"\n\"processing queue.\"\nmsgstr \"\"\n\"处理 - \"\n\"如果数据包没有被任何预处理器阻止，这是数据包通过的下一步。它被插入到所有对该特定XML元素感兴趣的处理器队列中。每个处理器都在一个单独的线程中工作，并有自己的内部固定大小的处理队列。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:325\nmsgid \"\"\n\"Post-processing - If there is no processor for the stanza then the packet\"\n\" goes through all post-processors. The last post-processor that is built \"\n\"into session manager post-processor tries to apply a default action to a \"\n\"packet which hasn’t been processed in step 2. Normally the default action\"\n\" is just forwarding the packet to a destination. Most commonly it is \"\n\"applied to <message/> packets.\"\nmsgstr \"\"\n\"后处理 - \"\n\"如果该节没有处理器，则数据包将通过所有后处理器。会话管理器后处理器中内置的最后一个后处理器尝试对步骤2中未处理的数据包应用默认操作。通常，默认操作只是将数据包转发到目的地。最常见的是应用于\"\n\" <message/>数据包。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:327\nmsgid \"\"\n\"Finally, if any of above 3 steps produced output/result packets all of \"\n\"them go through all filters which may or may not block them.\"\nmsgstr \"最后，如果以上3个步骤中的任何一个产生了输出/结果数据包，它们都会通过所有可能会或可能不会阻止它们的过滤器。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:329\nmsgid \"\"\n\"An important thing to note is that we have two kinds or two places where \"\n\"packets may be blocked or filtered out. One place is before packet is \"\n\"processed by the plugin and another place is after processing where \"\n\"filtering is applied to all results generated by the processor plugins.\"\nmsgstr \"需要注意的重要一点是，我们有两种或两个地方可能会阻止或过滤数据包。一个地方是在插件处理数据包之前，另一个地方是在处理之后对处理器插件生成的所有结果应用过滤。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:331\nmsgid \"\"\n\"It is also important to note that session manager and processor plugins \"\n\"act as packet consumers. The packet is taken for processing and once \"\n\"processing is finished the packet is destroyed. Therefore to forward a \"\n\"packet to a destination one of the processor must create a copy of the \"\n\"packet, set all properties and attributes and return it as a processing \"\n\"result. Of course processor can generate any number of packets as a \"\n\"result. Result packets can be generated in any of above 4 steps of the \"\n\"processing. Have a look at the picture below:\"\nmsgstr \"同样重要的是要注意会话管理器和处理器插件充当数据包消费者。数据包被用于处理，一旦处理完成，数据包就会被销毁。因此，要将数据包转发到目的地，处理器必须创建数据包的副本，设置所有性能和属性并将其作为处理结果返回。当然，处理器可以生成任意数量的数据包作为结果。可以在上述4个处理步骤中的任何一个中生成结果包。看看下面的图片：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:333\nmsgid \"|User Send to Comp|\"\nmsgstr \"|User Send to Comp|\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:358\nmsgid \"User Send to Comp\"\nmsgstr \"User Send to Comp\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:335\nmsgid \"\"\n\"If the packet P1 is sent from outside of the server, for example to a \"\n\"user on another server or to some component (MUC, PubSub, transport), \"\n\"then one of the processor must create a copy (P2) of the packet and set \"\n\"all attributes and destination addresses correctly. Packet P1 has been \"\n\"consumed by the session manager during processing and a new packet has \"\n\"been generated by one of the plugins.\"\nmsgstr \"\"\n\"如果数据包P1是从服务器外部发送的，例如发送到另一台服务器上的用户或某个组件（MUC、PubSub、传输），则其中一个处理器必须创建数据包的副本 \"\n\"(P2) 并正确设置所有属性和目标地址。会话管理器在处理期间已使用数据包P1，并且其中一个插件已生成新数据包。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:337\nmsgid \"The same of course happens on the way back from the component to the user:\"\nmsgstr \"在从组件返回给用户的过程中当然也会发生同样的情况：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:339\nmsgid \"|Comp Sends to User|\"\nmsgstr \"|Comp Sends to User|\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:359\nmsgid \"Comp Sends to User\"\nmsgstr \"Comp Sends to User\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:341\nmsgid \"\"\n\"The packet from the component is processed and one of the plugins must \"\n\"generate a copy of the packet to deliver it to the user. Of course packet\"\n\" forwarding is a default action which is applied when there is no plugin \"\n\"for the particular packet.\"\nmsgstr \"来自组件的数据包被处理，其中一个插件必须生成数据包的副本以将其交付给用户。当然，数据包转发是一个默认操作，其在特定数据包没有插件时被应用。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:343\nmsgid \"\"\n\"It is implemented this way because the input packet P1 can be processed \"\n\"by many plugins at the same time therefore the packet should be in fact \"\n\"immutable and must not change once it got to the session manager for \"\n\"processing.\"\nmsgstr \"之所以这样实现，是因为输入数据包P1可以被许多插件同时处理，因此数据包实际上应该是不可变的，并且一旦到达会话管理器进行处理就不能更改。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:345\nmsgid \"\"\n\"The most obvious processing work flow is when a user sends request to the\"\n\" server and expects a response from the server:\"\nmsgstr \"最明显的处理工作流程是当用户向服务器发送请求并期望服务器响应时：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:347\nmsgid \"|User Request Response|\"\nmsgstr \"|User Request Response|\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:360\nmsgid \"User Request Response\"\nmsgstr \"User Request Response\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:349\nmsgid \"\"\n\"This design has one surprising consequence though. If you look at the \"\n\"picture below showing communication between 2 users you can see that the \"\n\"packet is copied twice before it is delivered to a final destination:\"\nmsgstr \"不过，这种设计有一个令人惊讶的结果。如果您查看显示2个用户之间通信的下图，您可以看到数据包在传送到最终目的地之前被复制了两次：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:351\nmsgid \"|User Sends to User|\"\nmsgstr \"|User Sends to User|\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:361\nmsgid \"User Sends to User\"\nmsgstr \"User Sends to User\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:353\nmsgid \"\"\n\"The packet has to be processed twice by the session manager. The first \"\n\"time it is processed on behalf of the User A as an outgoing packet and \"\n\"the second time it is processed on behalf of the User B as an incoming \"\n\"packet.\"\nmsgstr \"数据包必须由会话管理器处理两次。第一次代表用户A处理它作为传出数据包，第二次代表用户B处理它作为传入数据包。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:355\nmsgid \"\"\n\"This is to make sure the User A has permission to send a packet out and \"\n\"all processing is applied to the packet and also to make sure that User B\"\n\" has permission to receive the packet and all processing is applied. If, \"\n\"for example, the User B is offline there is an offline message processor \"\n\"which should send the packet to a database instead of User B.\"\nmsgstr \"这是为了确保用户A有权发送数据包并且对数据包应用所有处理，同时确保用户B有权接收数据包并且应用所有处理。例如，如果用户B离线，则有一个离线消息处理器应该将数据包发送到数据库而不是用户B。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:366\nmsgid \"SASL Custom Mechanisms and Configuration\"\nmsgstr \"SASL自定义机制和配置\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:368\nmsgid \"\"\n\"**This API is available from Tigase XMPP Server version 5.2.0 or later on\"\n\" our current master branch.**\"\nmsgstr \"**此API可从Tigase XMPP服务器版本5.2.0或我们当前主分支上的更高版本获得。**\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:370\nmsgid \"\"\n\"**In version 8.0.0 there was a major change to the API and configuration \"\n\"of custom SASL mechanisms.**\"\nmsgstr \"**在8.0.0版中，API和自定义SASL机制的配置发生了重大变化。**\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:372\nmsgid \"\"\n\"*Note that API is under active development. This description may be \"\n\"updated at any time.*\"\nmsgstr \"*请注意，API正在积极开发中。此说明可能随时更新。*\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:375\nmsgid \"Basic SASL Configuration\"\nmsgstr \"基本SASL配置\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:377\nmsgid \"\"\n\"SASL implementation in Tigase XMPP Server is compatible with Java API, \"\n\"the same exact interfaces are used.\"\nmsgstr \"Tigase XMPP Server中的SASL实现与Java API兼容，使用完全相同的接口。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:379\nmsgid \"The SASL implementation consists of following parts:\"\nmsgstr \"SASL实现由以下部分组成：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:381\nmsgid \"mechanism\"\nmsgstr \"机制\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:383\nmsgid \"CallbackHandler\"\nmsgstr \"回调处理程序\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:386\nmsgid \"Mechanisms Configuration\"\nmsgstr \"机制配置\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:388\nmsgid \"\"\n\"To add a new mechanism, a new factory for the mechanism has to be \"\n\"implemented and registered.\"\nmsgstr \"要添加新机制，必须实现并注册该机制的新工厂。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:390\nmsgid \"\"\n\"The simplest way to add register a new factory is to annotate its class \"\n\"with ``@Bean`` annotation:\"\nmsgstr \"添加注册新工厂的最简单方法是使用 ``@Bean`` 注释对其类进行注释：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:392\nmsgid \"\"\n\"**Example of the registration of a SASL mechanism factory with an \"\n\"annotation setting id of the factory to** ``customSaslFactory``.\"\nmsgstr \"**使用工厂的注解设置id将SASL机制工厂注册到** ``customSaslFactory`` \"\n\"**的示例。**\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:399\nmsgid \"\"\n\"It can also be done by specifying the class directly for bean \"\n\"``customSaslFactory`` in the ``config.tdsl`` file like in the example \"\n\"below:\"\nmsgstr \"也可以通过在 ``config.tdsl`` 文件中直接为bean ``customSaslFactory``指定类来完成，如下例所示：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:401\nmsgid \"\"\n\"**Example of the registration of a SASL mechanism factory with TDSL \"\n\"setting id of the factory to ``customSaslFactory``.**\"\nmsgstr \"**注册SASL机制工厂的示例，其工厂的TDSL设置id为** ``customSaslFactory``。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:411\nmsgid \"\"\n\"The class must implement the ``SaslServerFactory`` interface and has \"\n\"public constructor without any arguments. All mechanisms returned by \"\n\"``getMechanismNames()`` method will be registered automatically.\"\nmsgstr \"\"\n\"该类必须实现 ``SaslServerFactory`` 接口并具有不带任何参数的公共构造函数。 ``getMechanismNames()`` \"\n\"方法返回的所有机制都将自动注册。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:413\nmsgid \"\"\n\"The default factory that is available and registered by default is \"\n\"``tigase.auth.TigaseSaslServerFactory`` which provides ``PLAIN``, \"\n\"``ANONYMOUS``, ``EXTERNAL``, ``SCRAM-SHA-1``, ``SCRAM-SHA-256`` and \"\n\"``SCRAM-SHA-512`` mechanisms.\"\nmsgstr \"\"\n\"默认情况下可用和注册的默认工厂是 ``tigase.auth.TigaseSaslServerFactory`` ，它提供 ``PLAIN``, \"\n\"``ANONYMOUS``, ``EXTERNAL``, ``SCRAM-SHA-1``, ``SCRAM-SHA-256`` 和 \"\n\"``SCRAM-SHA-512`` 机制。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:416\nmsgid \"CallbackHandler Configuration\"\nmsgstr \"CallbackHandler配置\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:418\nmsgid \"\"\n\"The ``CallbackHandler`` is a helper class used for loading/retrieving \"\n\"authentication data from data repository and providing them to a \"\n\"mechanism.\"\nmsgstr \"``CallbackHandler`` 是一个帮助类，用于从数据存储库加载/检索身份验证数据并将它们提供给机制。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:420\nmsgid \"\"\n\"To register a new callback handler you need to create a new class \"\n\"extending ``tigase.auth.CallbackHandlerFactory`` (if you wish to keep \"\n\"existing SASL callback handlers) or implementing \"\n\"``tigase.auth.CallbackHandlerFactoryIfc``. You will need to override \"\n\"``create()`` method to return an instance of your custom \"\n\"``CallbackHandler`` when appropriate.\"\nmsgstr \"\"\n\"要注册一个新的回调处理程序，您需要创建一个扩展 ``tigase.auth.CallbackHandlerFactory`` \"\n\"的新类（如果您希望保留现有的SASL回调处理程序）或实现 \"\n\"``tigase.auth.CallbackHandlerFactoryIfc``。您将需要重写 ``create()`` \"\n\"方法以在适当的时候返回您的自定义 ``CallbackHandler`` 的实例。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:422\nmsgid \"\"\n\"Next you need to register new implementation of \"\n\"``CallbackHandlerFactoryIfc``. The ``config.tdsl`` file should include:\"\nmsgstr \"接下来，您需要注册 ``CallbackHandlerFactoryIfc`` 的新实现。 ``config.tdsl`` 文件应包括：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:432\nmsgid \"\"\n\"During the authentication process, Tigase server always checks for asks \"\n\"callback handler factory for specific handler to selected mechanisms, and\"\n\" if there is no specific handler the default one is used.\"\nmsgstr \"在身份验证过程中，Tigase服务器始终检查请求回调处理程序工厂以获取特定处理程序到选定机制，如果没有特定处理程序，则使用默认处理程序。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:435\nmsgid \"Selecting Mechanisms Available in the Stream\"\nmsgstr \"选择流中可用的机制\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:437\nmsgid \"\"\n\"The ``tigase.auth.MechanismSelector`` interface is used for selecting \"\n\"mechanisms available in a stream. Method ``filterMechanisms()`` should \"\n\"return a collection with mechanisms available based on:\"\nmsgstr \"\"\n\"``tigase.auth.MechanismSelector`` 接口用于选择流中可用的机制。方法 ``filterMechanisms()``\"\n\" 应该返回一个具有可用机制的集合，基于：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:439\nmsgid \"all registered SASL factories\"\nmsgstr \"所有注册的SASL工厂\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:441\nmsgid \"XMPP session data (from ``XMPPResourceConnection`` class)\"\nmsgstr \"XMPP会话数据（来自 ``XMPPResourceConnection`` 类）\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:443\nmsgid \"\"\n\"The default selector returns mechanisms from all mechanism factories \"\n\"registered in ``sasl-provider`` ``(TigaseSaslProvider)``.\"\nmsgstr \"默认选择器从在 ``sasl-provider`` ``(TigaseSaslProvider)`` 中注册的所有机制工厂返回机制。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:445\nmsgid \"\"\n\"It is possible to use a custom selector by specifying it’s class int the \"\n\"``config.tdsl`` file:\"\nmsgstr \"可以通过在 ``config.tdsl`` 文件中指定它的类来使用自定义选择器：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:456\nmsgid \"Logging/Authentication\"\nmsgstr \"记录/认证\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:458\nmsgid \"\"\n\"After the XMPP stream is opened by a client, the server checks which SASL\"\n\" mechanisms are available for the XMPP session. Depending on whether the \"\n\"stream is encrypted or not, depending on the domain, the server can \"\n\"present different available authentication mechanisms. \"\n\"``MechanismSelector`` is responsible for choosing mechanisms. List of \"\n\"allowed mechanisms is stored in the XMPP session object.\"\nmsgstr \"\"\n\"在客户端打开XMPP流后，服务器会检查哪些SASL机制可用于XMPP会话。根据流是否加密，根据域，服务器可以提供不同的可用身份验证机制。 \"\n\"``MechanismSelector`` 负责选择机制。允许的机制列表存储在XMPP会话对象中。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:460\nmsgid \"\"\n\"When the client/user begins the authentication procedure it uses one \"\n\"particular mechanism. It must use one of the mechanisms provided by the \"\n\"server as available for this session. The server checks whether \"\n\"mechanisms used by the client is on the list of allowed mechanisms. It \"\n\"the check is successful, the server creates ``SaslServer`` class instance\"\n\" and proceeds with exchanging authentication information. Authentication \"\n\"data is different depending on the mechanism used.\"\nmsgstr \"\"\n\"当客户端/用户开始认证过程时，它使用一种特定的机制。它必须使用服务器提供的可用于此会话的机制之一。服务器检查客户端使用的机制是否在允许的机制列表中。如果检查成功，服务器将创建\"\n\" ``SaslServer`` 类实例并继续交换身份验证信息。认证数据因使用的机制而异。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:462\nmsgid \"\"\n\"When the SASL authentication is completed without any error, Tigase \"\n\"server should have authorized user name or authorized BareJID. In the \"\n\"first case, the server automatically builds user’s JID based on the \"\n\"domain used in the stream opening element in ``to`` attribute.\"\nmsgstr \"\"\n\"当SASL认证完成且没有任何错误时，Tigase服务器应该有授权的用户名或授权的BareJID。在第一种情况下，服务器会根据 ``to`` \"\n\"属性中的流打开元素中使用的域自动构建用户的JID。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:464\nmsgid \"\"\n\"If, after a successful authentication, method call: \"\n\"``getNegotiatedProperty(\\\"IS_ANONYMOUS\\\")`` returns ``Boolean.TRUE`` then\"\n\" the user session is marked as anonymous. For valid and registered users \"\n\"this can be used for cases when we do not want to load any user data such\"\n\" as roster, vcard, privacy lists and so on. This is a performance and \"\n\"resource usage implication and can be useful for use cases such as \"\n\"support chat. The authorization is performed based on the client database\"\n\" but we do not need to load any XMPP specific data for the user’s \"\n\"session.\"\nmsgstr \"\"\n\"如果在成功验证后，方法调用：``getNegotiatedProperty(\\\"IS_ANONYMOUS\\\")`` 返回 \"\n\"``Boolean.TRUE`` \"\n\"则用户会话被标记为匿名。对于有效和注册用户，这可用于我们不想加载任何用户数据（例如名册、vcard、隐私列表等）的情况。这是对性能和资源使用的影响，可用于支持聊天等用例。授权是基于客户端数据库执行的，但我们不需要为用户会话加载任何XMPP特定数据。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:466\nmsgid \"\"\n\"More details about implementation can be found in the :ref:`custom \"\n\"mechanisms development<cmd>` section.\"\nmsgstr \"更多关于实现的细节可以在 :ref:`自定义机制开发<cmd>` 部分中找到。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:471\nmsgid \"Custom Mechanisms Development\"\nmsgstr \"自定义机制开发\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:474\nmsgid \"**Mechanism**\"\nmsgstr \"**机制**\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:476\nmsgid \"\"\n\"``getAuthorizationID()`` method from ``SaslServer`` class **should** \"\n\"return bare JID authorized user. In case that the method returns only \"\n\"user name such as **romeo** for example, the server automatically appends\"\n\" domain name to generate a valid BareJID: *romeo@example.com*. In case \"\n\"the method returns a full, valid BareJID, the server does not change \"\n\"anything.\"\nmsgstr \"\"\n\"来自 ``SaslServer`` 类的 ``getAuthorizationID()`` 方法 **应该** \"\n\"返回裸JID授权用户。如果该方法仅返回用户名，例如 \"\n\"**romeo**，则服务器会自动附加域名以生成有效的BareJID：*romeo@example.com*。如果该方法返回完整、有效的 \"\n\"BareJID，则服务器不会更改任何内容。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:478\nmsgid \"\"\n\"``handleLogin()`` method from ``SessionManagerHandler`` will be called \"\n\"with user’s Bare JID provided by ``getAuthorizationID()`` (or created \"\n\"later using stream domain name).\"\nmsgstr \"\"\n\"``SessionManagerHandler`` 的 ``handleLogin()`` 方法将使用 \"\n\"``getAuthorizationID()`` 提供的用户裸JID（或稍后使用流域名创建）来调用。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:481\nmsgid \"**CallbackHandler**\"\nmsgstr \"**回调处理程序**\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:483\nmsgid \"\"\n\"For each session authorization, the server creates a new and separate \"\n\"empty handler. Factory which creates handler instance allows to inject \"\n\"different objects to the handler, depending on interfaces implemented by \"\n\"the handler class:\"\nmsgstr \"对于每个会话授权，服务器都会创建一个新的单独的空处理程序。创建处理程序实例的工厂允许向处理程序注入不同的对象，具体取决于处理程序类实现的接口：\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:485\nmsgid \"``AuthRepositoryAware`` - injects ``AuthRepository;``\"\nmsgstr \"``AuthRepositoryAware`` - 注入 ``AuthRepository;``\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:487\nmsgid \"\"\n\"``DomainAware`` - injects domain name within which the user attempts to \"\n\"authenticate\"\nmsgstr \"``DomainAware`` - 注入用户尝试进行身份验证的域名\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:489\nmsgid \"``NonAuthUserRepositoryAware`` - injects ``NonAuthUserRepository``\"\nmsgstr \"``NonAuthUserRepositoryAware`` - 注入 ``NonAuthUserRepository``\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:492\nmsgid \"General Remarks\"\nmsgstr \"通用说明\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:494\nmsgid \"\"\n\"``JabberIqAuth`` used for non-SASL authentication mechanisms uses the \"\n\"same callback as the SASL mechanisms.\"\nmsgstr \"``JabberIqAuth`` 用于非SASL身份验证机制使用与SASL机制相同的回调。\"\n\n#: ../../Tigase_Development/Plugin_Development.rst:496\nmsgid \"\"\n\"Methods ``auth`` in ``Repository`` interfaces will be deprecated. These \"\n\"interfaces will be treated as user details providers only. There will be \"\n\"new methods available which will allow for additional login operations on\"\n\" the database such as last successful login recording.\"\nmsgstr \"\"\n\"``Repository`` 接口中的 ``auth`` \"\n\"方法将被弃用。这些接口将仅被视为用户详细信息提供者。将有新的可用方法允许对数据库进行额外的登录操作，比如上次成功登录记录。\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Development/Server_Compilation.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-05-29 02:43-0700\\n\"\n\"PO-Revision-Date: 2022-07-20 21:56+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/dp-server_compilation/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:2\nmsgid \"Server Compilation\"\nmsgstr \"服务器编译\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:4\nmsgid \"\"\n\"Tigase XMPP Server Project uses Maven for compilation. For details on \"\n\"Maven and it’s use, please see the :ref:`Maven Guide.<usingMaven>`\"\nmsgstr \"\"\n\"Tigase XMPP 服务器项目使用 Maven 进行编译。有关 Maven 及其使用的详细信息，\"\n\"请参阅 :ref:`Maven 指南<usingMaven>`。\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:7\nmsgid \"Distribution Packages\"\nmsgstr \"分发包\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:9\nmsgid \"Once Compiled, Tigase creates two separate distribution archives:\"\nmsgstr \"编译后，Tigase 会创建两个独立的分发档案：\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:11\nmsgid \"\"\n\"**-dist** is a minimal version containing only tigase-server, tigase-\"\n\"xmltools and tigase-utils, MUC, Pubsub, and HTTP.\"\nmsgstr \"\"\n\"**-dist** 是仅包含 tigase-server，tigase-xmltools 和 tigase-utils，MUC，\"\n\"Pubsub 和 HTTP 的最小版本。\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:13\nmsgid \"\"\n\"**-dist-max** is a version containing all additional tigase components as\"\n\" well as dependencies required by those components.\"\nmsgstr \"**-dist-max** 是一个包含所有其他 tigase 组件以及这些组件所需依赖项的版本。\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:15\nmsgid \"They will be available as both zip and tarball.\"\nmsgstr \"它们将以 zip 和 tarball 的形式提供。\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:18\nmsgid \"Building Server and Generating Packages\"\nmsgstr \"构建服务器和生成包\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:21\nmsgid \"Server binary and it’s documentation\"\nmsgstr \"服务器二进制文件及其文档\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:23\nmsgid \"After cloning tigase-server repository:\"\nmsgstr \"克隆 tigase-server 存储库后：\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:30\nmsgid \"You compile server with maven :\"\nmsgstr \"你用 maven 编译服务器：\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:36\nmsgid \"This will: - Build Tigase XMPP tigase-server jar in tigase-server/target.\"\nmsgstr \"这将： - 在 tigase-server/target 中构建 Tigase XMPP tigase-server jar。\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:38\nmsgid \"\"\n\"If you wish to include compilation of the documentation use \"\n\"*distribution* profile:\"\nmsgstr \"如果您希望包含文档的编译，请使用 *distribution* 配置文件：\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:44\nmsgid \"\"\n\"This will - compile server binaries. - generate javadoc and manual \"\n\"documentation ``tigase-server/target/_docs`` directory.\"\nmsgstr \"这将 - 编译服务器二进制文件。 - 生成 javadoc 和手册文档 ``tigase-server/\"\n\"target/_docs`` 目录。\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:47\nmsgid \"Server distribution packages\"\nmsgstr \"服务器分发包\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:49\nmsgid \"\"\n\"Distribution building is handled by separate project (`Tigase Server \"\n\"Distribution <https://github.com/tigase/tigase-server-distribution>`__)\"\nmsgstr \"\"\n\"分发构建由单独的项目处理 (`Tigase 服务器分发 <https://github.com/tigase/\"\n\"tigase-server-distribution>`__)\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:51\nmsgid \"\"\n\"In order to build distribution packages \\\\* clone tigase-server-\"\n\"distribution repository:\"\nmsgstr \"为了构建分发包 \\\\* 克隆 tigase-server-distribution 存储库：\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:58\nmsgid \"and compile it using maven with *distribution* profile:\"\nmsgstr \"并使用带有 *distribution* 配置文件的 maven 编译它：\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:64\nmsgid \"This will:\"\nmsgstr \"这将：\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:66\nmsgid \"\"\n\"compile all documentation sources (including dependencies) and place them\"\n\" in ``tigase-server-distribution/target/_docs`` directory\"\nmsgstr \"编译所有文档源（包括依赖项）并将它们放在 ``tigase-server-distribution/target/\"\n\"_docs`` 目录中\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:68\nmsgid \"\"\n\"download all dependencies in defined versions and put them in ``tigase-\"\n\"server-distribution/target/dist/jars/`` directory.\"\nmsgstr \"\"\n\"下载定义版本中的所有依赖项并将它们放在 ``tigase-server-distribution/target/\"\n\"dist/jars/`` 目录中。\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:70\nmsgid \"\"\n\"create both types of distribution packages (-dist and -dist-max) and \"\n\"place them in ``tigase-server-distribution/target/_dist/`` directory.\"\nmsgstr \"\"\n\"创建两种类型的分发包（-dist 和 -dist-max）并将它们放在 ``tigase-\"\n\"server-distribution/target/_dist/`` 目录中。\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:73\nmsgid \"Running Server\"\nmsgstr \"运行服务器\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:75\nmsgid \"\"\n\"Afterwards you can run the server with the regular shell script from \"\n\"within ``server`` module:\"\nmsgstr \"之后，您可以在 ``server`` 模块中使用常规的 shell 脚本运行服务器：\"\n\n#: ../../Tigase_Development/Server_Compilation.rst:82\nmsgid \"\"\n\"Please bear in mind, that you need to provide correct setup in \"\n\"etc/config.tdsl configuration files for the server to work correctly.\"\nmsgstr \"请记住，您需要在 etc/config.tdsl \"\n\"配置文件中提供正确的设置，服务器才能正常工作。\"\n\n#~ msgid \"\"\n#~ \"Tigase XMPP Server Project uses Maven\"\n#~ \" for compilation. For details on \"\n#~ \"Maven and it’s use, please see the\"\n#~ \" `Maven Guide. <#usingMaven>`__\"\n#~ msgstr \"\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Development/Tests.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-05-27 12:30-0700\\n\"\n\"PO-Revision-Date: 2022-09-07 17:14+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/dg-tests/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Tests.rst:2 ../../Tigase_Development/Tests.rst:5\nmsgid \"Tests\"\nmsgstr \"测试\"\n\n#: ../../Tigase_Development/Tests.rst:7\nmsgid \"Tests are very important part of Tigase server development process.\"\nmsgstr \"测试是Tigase服务器开发过程中非常重要的一部分。\"\n\n#: ../../Tigase_Development/Tests.rst:9\nmsgid \"\"\n\"Each release goes through fully automated testing process. All server \"\n\"functions are considered implemented only when they pass the testing \"\n\"cycle. Tigase test suite is used for all our automatic tests which allows\"\n\" to define different test scenarios.\"\nmsgstr \"\"\n\"每个版本都经过完全自动化的测试过程。所有服务器功能仅在通过测试周期时才被视为\"\n\"已实现。 Tigase测试套件用于我们所有的自动测试，允许定义不同的测试场景。\"\n\n#: ../../Tigase_Development/Tests.rst:11\nmsgid \"\"\n\"There is no tweaking on databases for tests. All databases are installed \"\n\"in a standard way and run with default settings. Databases are cleared \"\n\"each time before the test cycle starts.\"\nmsgstr \"没有调整用于测试的数据库。所有数据库都以标准方式安装并使用默认设置运行。每次\"\n\"测试周期开始前都会清除数据库。\"\n\n#: ../../Tigase_Development/Tests.rst:13\nmsgid \"\"\n\"There are no modifications needed to be made to Tigase’s configuration \"\n\"file as well. All tests are performed on a default configuration \"\n\"generated by the configuration wizards.\"\nmsgstr \"也不需要对Tigase的配置文件进行修改。所有测试均在配置向导生成的默认配置上执行\"\n\"。\"\n\n#: ../../Tigase_Development/Tests.rst:15\nmsgid \"The server is tested in all supported environments:\"\nmsgstr \"该服务器在所有支持的环境中进行了测试：\"\n\n#: ../../Tigase_Development/Tests.rst:17\nmsgid \"\"\n\"**XMLDB** - tests with built-in simple XML database. This is a simple and\"\n\" efficient solution for small installations. It is recommended for \"\n\"services with up to 100 user accounts although it has been successfully \"\n\"tested with 10,000 user accounts.\"\nmsgstr \"\"\n\"**XMLDB** - \"\n\"使用内置的简单XML数据库进行测试。对于小型安装，这是一种简单而有效的解决方案。\"\n\"尽管它已经成功测试了 10,000 \"\n\"个用户帐户，但建议用于具有多达100个用户帐户的服务。\"\n\n#: ../../Tigase_Development/Tests.rst:19\nmsgid \"\"\n\"**MySQL** - tests with a `MySQL <http://www.mysql.com/>`__ database. Much\"\n\" slower than XMLDB but may handle many more user accounts.\"\nmsgstr \"\"\n\"**MySQL** - 使用 `MySQL <http://www.mysql.com/>`__ 数据库进行测试。比 XMLDB \"\n\"慢得多，但可以处理更多的用户帐户。\"\n\n#: ../../Tigase_Development/Tests.rst:21\nmsgid \"\"\n\"**PostgreSQL** - tests with a `PostgreSQL <http://www.postgresql.org/>`__\"\n\" database. Again it is much slower than XMLDB but may handle much more \"\n\"user accounts. This is basically exactly the same code as for MySQL \"\n\"database (SQL Connector) but tests are executed to make sure the code is \"\n\"compatible with all supported SQL databases and to compare performance.\"\nmsgstr \"\"\n\"**PostgreSQL** - 使用 `PostgreSQL <http://www.postgresql.org/>`__ 数据库进行\"\n\"测试。同样，它比XMLDB慢得多，但可以处理更多的用户帐户。这与MySQL数据库（SQL连\"\n\"接器）的代码基本完全相同，但会执行测试以确保代码与所有支持的SQL数据库兼容并比\"\n\"较性能。\"\n\n#: ../../Tigase_Development/Tests.rst:23\nmsgid \"\"\n\"**Distributed** - is a test for distributed installation where c2s and \"\n\"s2s components run on separated machine which connects using external an \"\n\"component protocol (`XEP-0114 \"\n\"<http://www.xmpp.org/extensions/xep-0114.html>`__) to another machine \"\n\"with SessionManager running.\"\nmsgstr \"\"\n\"**Distributed** - 是分布式安装的测试，其中c2s和s2s组件在分离的机器上运行，\"\n\"该机器使用外部组件协议 (`XEP-0114 <http://www.xmpp.org/extensions/xep-0114.\"\n\"html>`__) 连接到另一台运行SessionManager的机器。\"\n\n#: ../../Tigase_Development/Tests.rst:27\nmsgid \"Functional Tests\"\nmsgstr \"功能测试\"\n\n#: ../../Tigase_Development/Tests.rst:29\nmsgid \"\"\n\"Basic checking to see if all the functions work at correctly. These tests\"\n\" are performed every time the code is sent to source repository.\"\nmsgstr \"基本检查以查看所有功能是否正常工作。每次将代码发送到源存储库时都会执行这些测\"\n\"试。\"\n\n#: ../../Tigase_Development/Tests.rst:32 ../../Tigase_Development/Tests.rst:73\n#: ../../Tigase_Development/Tests.rst:114\nmsgid \"Version\"\nmsgstr \"版本\"\n\n#: ../../Tigase_Development/Tests.rst:32 ../../Tigase_Development/Tests.rst:73\n#: ../../Tigase_Development/Tests.rst:114\nmsgid \"XMLDB\"\nmsgstr \"XMLDB\"\n\n#: ../../Tigase_Development/Tests.rst:32 ../../Tigase_Development/Tests.rst:73\n#: ../../Tigase_Development/Tests.rst:114\nmsgid \"MySQL\"\nmsgstr \"MySQL\"\n\n#: ../../Tigase_Development/Tests.rst:32 ../../Tigase_Development/Tests.rst:73\n#: ../../Tigase_Development/Tests.rst:114\nmsgid \"PGSQL\"\nmsgstr \"PGSQL\"\n\n#: ../../Tigase_Development/Tests.rst:32 ../../Tigase_Development/Tests.rst:73\n#: ../../Tigase_Development/Tests.rst:114\nmsgid \"Distributed\"\nmsgstr \"发布\"\n\n#: ../../Tigase_Development/Tests.rst:34 ../../Tigase_Development/Tests.rst:75\nmsgid \"3.3.2-b889\"\nmsgstr \"3.3.2-b889\"\n\n#: ../../Tigase_Development/Tests.rst:34\nmsgid \"`00:00:12 <tests/3.3.2-b889/func/xmldb/functional-tests.html>`__\"\nmsgstr \"`00:00:12 <tests/3.3.2-b889/func/xmldb/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:34\nmsgid \"`00:00:17 <tests/3.3.2-b889/func/mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:17 <tests/3.3.2-b889/func/mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:34\nmsgid \"`00:00:17 <tests/3.3.2-b889/func/pgsql/functional-tests.html>`__\"\nmsgstr \"`00:00:17 <tests/3.3.2-b889/func/pgsql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:34 ../../Tigase_Development/Tests.rst:75\nmsgid \"none\"\nmsgstr \"无\"\n\n#: ../../Tigase_Development/Tests.rst:36 ../../Tigase_Development/Tests.rst:77\nmsgid \"3.3.2-b880\"\nmsgstr \"3.3.2-b880\"\n\n#: ../../Tigase_Development/Tests.rst:36\nmsgid \"`00:00:13 <tests/3.3.2-b880/func/xmldb/functional-tests.html>`__\"\nmsgstr \"`00:00:13 <tests/3.3.2-b880/func/xmldb/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:36\nmsgid \"`00:00:15 <tests/3.3.2-b880/func/mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:15 <tests/3.3.2-b880/func/mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:36\nmsgid \"`00:00:15 <tests/3.3.2-b880/func/pgsql/functional-tests.html>`__\"\nmsgstr \"`00:00:15 <tests/3.3.2-b880/func/pgsql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:36 ../../Tigase_Development/Tests.rst:64\n#: ../../Tigase_Development/Tests.rst:77 ../../Tigase_Development/Tests.rst:105\n#: ../../Tigase_Development/Tests.rst:116\nmsgid \"None\"\nmsgstr \"无\"\n\n#: ../../Tigase_Development/Tests.rst:38 ../../Tigase_Development/Tests.rst:79\nmsgid \"3.0.2-b700\"\nmsgstr \"3.0.2-b700\"\n\n#: ../../Tigase_Development/Tests.rst:38\nmsgid \"`00:00:22 <tests/3.0.2-b700/func/xmldb/functional-tests.html>`__\"\nmsgstr \"`00:00:22 <tests/3.0.2-b700/func/xmldb/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:38\nmsgid \"`00:00:24 <tests/3.0.2-b700/func/mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:24 <tests/3.0.2-b700/func/mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:38\nmsgid \"`00:00:25 <tests/3.0.2-b700/func/pgsql/functional-tests.html>`__\"\nmsgstr \"`00:00:25 <tests/3.0.2-b700/func/pgsql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:38\nmsgid \"`00:00:25 <tests/3.0.2-b700/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:25 <tests/3.0.2-b700/func/sm-mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:40 ../../Tigase_Development/Tests.rst:81\nmsgid \"2.9.5-b606\"\nmsgstr \"2.9.5-b606\"\n\n#: ../../Tigase_Development/Tests.rst:40\nmsgid \"`00:00:22 <tests/2.9.5-b606/func/xmldb/functional-tests.html>`__\"\nmsgstr \"`00:00:22 <tests/2.9.5-b606/func/xmldb/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:40\nmsgid \"`00:00:24 <tests/2.9.5-b606/func/mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:24 <tests/2.9.5-b606/func/mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:40\nmsgid \"`00:00:24 <tests/2.9.5-b606/func/pgsql/functional-tests.html>`__\"\nmsgstr \"`00:00:24 <tests/2.9.5-b606/func/pgsql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:40\nmsgid \"`00:00:24 <tests/2.9.5-b606/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:24 <tests/2.9.5-b606/func/sm-mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:42 ../../Tigase_Development/Tests.rst:83\nmsgid \"2.9.3-b548\"\nmsgstr \"2.9.3-b548\"\n\n#: ../../Tigase_Development/Tests.rst:42\nmsgid \"`00:00:22 <tests/2.9.3-b548/func/xmldb/functional-tests.html>`__\"\nmsgstr \"`00:00:22 <tests/2.9.3-b548/func/xmldb/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:42\nmsgid \"`00:00:23 <tests/2.9.3-b548/func/mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:23 <tests/2.9.3-b548/func/mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:42\nmsgid \"`00:00:25 <tests/2.9.3-b548/func/pgsql/functional-tests.html>`__\"\nmsgstr \"`00:00:25 <tests/2.9.3-b548/func/pgsql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:42\nmsgid \"`00:00:25 <tests/2.9.3-b548/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:25 <tests/2.9.3-b548/func/sm-mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:44 ../../Tigase_Development/Tests.rst:85\nmsgid \"2.9.1-b528\"\nmsgstr \"2.9.1-b528\"\n\n#: ../../Tigase_Development/Tests.rst:44\nmsgid \"`00:00:21 <tests/2.9.1-b528/func/xmldb/functional-tests.html>`__\"\nmsgstr \"`00:00:21 <tests/2.9.1-b528/func/xmldb/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:44\nmsgid \"`00:00:23 <tests/2.9.1-b528/func/mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:23 <tests/2.9.1-b528/func/mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:44\nmsgid \"`00:00:24 <tests/2.9.1-b528/func/pgsql/functional-tests.html>`__\"\nmsgstr \"`00:00:24 <tests/2.9.1-b528/func/pgsql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:44\nmsgid \"`00:00:25 <tests/2.9.1-b528/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:25 <tests/2.9.1-b528/func/sm-mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:46 ../../Tigase_Development/Tests.rst:87\nmsgid \"2.8.6-b434\"\nmsgstr \"2.8.6-b434\"\n\n#: ../../Tigase_Development/Tests.rst:46\nmsgid \"`00:00:21 <tests/2.8.6-b434/func/xmldb/functional-tests.html>`__\"\nmsgstr \"`00:00:21 <tests/2.8.6-b434/func/xmldb/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:46\nmsgid \"`00:00:24 <tests/2.8.6-b434/func/mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:24 <tests/2.8.6-b434/func/mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:46\nmsgid \"`00:00:24 <tests/2.8.6-b434/func/pgsql/functional-tests.html>`__\"\nmsgstr \"`00:00:24 <tests/2.8.6-b434/func/pgsql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:46\nmsgid \"`00:00:25 <tests/2.8.6-b434/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:25 <tests/2.8.6-b434/func/sm-mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:48 ../../Tigase_Development/Tests.rst:89\nmsgid \"2.8.5-b422\"\nmsgstr \"2.8.5-b422\"\n\n#: ../../Tigase_Development/Tests.rst:48\nmsgid \"`00:00:21 <tests/2.8.5-b422/func/xmldb/functional-tests.html>`__\"\nmsgstr \"`00:00:21 <tests/2.8.5-b422/func/xmldb/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:48\nmsgid \"`00:00:24 <tests/2.8.5-b422/func/mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:24 <tests/2.8.5-b422/func/mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:48\nmsgid \"`00:00:24 <tests/2.8.5-b422/func/pgsql/functional-tests.html>`__\"\nmsgstr \"`00:00:24 <tests/2.8.5-b422/func/pgsql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:48\nmsgid \"`00:00:26 <tests/2.8.5-b422/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:26 <tests/2.8.5-b422/func/sm-mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:50 ../../Tigase_Development/Tests.rst:91\nmsgid \"2.8.3-b409\"\nmsgstr \"2.8.3-b409\"\n\n#: ../../Tigase_Development/Tests.rst:50\nmsgid \"`00:00:27 <tests/2.8.3-b409/func/xmldb/functional-tests.html>`__\"\nmsgstr \"`00:00:27 <tests/2.8.3-b409/func/xmldb/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:50\nmsgid \"`00:00:29 <tests/2.8.3-b409/func/mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:29 <tests/2.8.3-b409/func/mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:50\nmsgid \"`00:00:29 <tests/2.8.3-b409/func/pgsql/functional-tests.html>`__\"\nmsgstr \"`00:00:29 <tests/2.8.3-b409/func/pgsql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:50\nmsgid \"`00:00:32 <tests/2.8.3-b409/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:32 <tests/2.8.3-b409/func/sm-mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:52 ../../Tigase_Development/Tests.rst:93\nmsgid \"2.7.2-b378\"\nmsgstr \"2.7.2-b378\"\n\n#: ../../Tigase_Development/Tests.rst:52\nmsgid \"`00:00:30 <tests/2.7.2-b378/func/xmldb/functional-tests.html>`__\"\nmsgstr \"`00:00:30 <tests/2.7.2-b378/func/xmldb/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:52\nmsgid \"`00:00:34 <tests/2.7.2-b378/func/mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:34 <tests/2.7.2-b378/func/mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:52\nmsgid \"`00:00:33 <tests/2.7.2-b378/func/pgsql/functional-tests.html>`__\"\nmsgstr \"`00:00:33 <tests/2.7.2-b378/func/pgsql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:52\nmsgid \"`00:00:35 <tests/2.7.2-b378/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:35 <tests/2.7.2-b378/func/sm-mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:54 ../../Tigase_Development/Tests.rst:95\nmsgid \"2.6.4-b300\"\nmsgstr \"2.6.4-b300\"\n\n#: ../../Tigase_Development/Tests.rst:54\nmsgid \"`00:00:30 <tests/2.6.4-b300/func/xmldb/functional-tests.html>`__\"\nmsgstr \"`00:00:30 <tests/2.6.4-b300/func/xmldb/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:54\nmsgid \"`00:00:32 <tests/2.6.4-b300/func/mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:32 <tests/2.6.4-b300/func/mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:54\nmsgid \"`00:00:35 <tests/2.6.4-b300/func/pgsql/functional-tests.html>`__\"\nmsgstr \"`00:00:35 <tests/2.6.4-b300/func/pgsql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:54\nmsgid \"`00:00:39 <tests/2.6.4-b300/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:39 <tests/2.6.4-b300/func/sm-mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:56 ../../Tigase_Development/Tests.rst:97\nmsgid \"2.6.4-b295\"\nmsgstr \"2.6.4-b295\"\n\n#: ../../Tigase_Development/Tests.rst:56\nmsgid \"`00:00:29 <tests/2.6.4-b295/func/xmldb/functional-tests.html>`__\"\nmsgstr \"`00:00:29 <tests/2.6.4-b295/func/xmldb/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:56\nmsgid \"`00:00:32 <tests/2.6.4-b295/func/mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:32 <tests/2.6.4-b295/func/mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:56\nmsgid \"`00:00:45 <tests/2.6.4-b295/func/pgsql/functional-tests.html>`__\"\nmsgstr \"`00:00:45 <tests/2.6.4-b295/func/pgsql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:56\nmsgid \"`00:00:36 <tests/2.6.4-b295/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:36 <tests/2.6.4-b295/func/sm-mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:58 ../../Tigase_Development/Tests.rst:99\nmsgid \"2.6.0-b287\"\nmsgstr \"2.6.0-b287\"\n\n#: ../../Tigase_Development/Tests.rst:58\nmsgid \"`00:00:31 <tests/2.6.0-b287/func/xmldb/functional-tests.html>`__\"\nmsgstr \"`00:00:31 <tests/2.6.0-b287/func/xmldb/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:58\nmsgid \"`00:00:34 <tests/2.6.0-b287/func/mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:34 <tests/2.6.0-b287/func/mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:58\nmsgid \"`00:00:47 <tests/2.6.0-b287/func/pgsql/functional-tests.html>`__\"\nmsgstr \"`00:00:47 <tests/2.6.0-b287/func/pgsql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:58\nmsgid \"`00:00:43 <tests/2.6.0-b287/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:43 <tests/2.6.0-b287/func/sm-mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:60 ../../Tigase_Development/Tests.rst:101\nmsgid \"2.5.0-b279\"\nmsgstr \"2.5.0-b279\"\n\n#: ../../Tigase_Development/Tests.rst:60\nmsgid \"`00:00:30 <tests/2.5.0-b279/func/xmldb/functional-tests.html>`__\"\nmsgstr \"`00:00:30 <tests/2.5.0-b279/func/xmldb/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:60\nmsgid \"`00:00:34 <tests/2.5.0-b279/func/mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:34 <tests/2.5.0-b279/func/mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:60\nmsgid \"`00:00:45 <tests/2.5.0-b279/func/pgsql/functional-tests.html>`__\"\nmsgstr \"`00:00:45 <tests/2.5.0-b279/func/pgsql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:60\nmsgid \"`00:00:43 <tests/2.5.0-b279/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:43 <tests/2.5.0-b279/func/sm-mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:62 ../../Tigase_Development/Tests.rst:103\nmsgid \"2.4.0-b263\"\nmsgstr \"2.4.0-b263\"\n\n#: ../../Tigase_Development/Tests.rst:62\nmsgid \"`00:00:29 <tests/2.4.0-b263/func/xmldb/functional-tests.html>`__\"\nmsgstr \"`00:00:29 <tests/2.4.0-b263/func/xmldb/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:62\nmsgid \"`00:00:33 <tests/2.4.0-b263/func/mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:33 <tests/2.4.0-b263/func/mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:62\nmsgid \"`00:00:45 <tests/2.4.0-b263/func/pgsql/functional-tests.html>`__\"\nmsgstr \"`00:00:45 <tests/2.4.0-b263/func/pgsql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:62\nmsgid \"`00:00:44 <tests/2.4.0-b263/func/sm-mysql/functional-tests.html>`__\"\nmsgstr \"`00:00:44 <tests/2.4.0-b263/func/sm-mysql/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:64 ../../Tigase_Development/Tests.rst:105\n#: ../../Tigase_Development/Tests.rst:116\nmsgid \"2.3.4-b226\"\nmsgstr \"2.3.4-b226\"\n\n#: ../../Tigase_Development/Tests.rst:64\nmsgid \"`00:00:48 <tests/functional-tests.html>`__\"\nmsgstr \"`00:00:48 <tests/functional-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:68\nmsgid \"Performance Tests\"\nmsgstr \"性能测试\"\n\n#: ../../Tigase_Development/Tests.rst:70\nmsgid \"Checking to see whether the function performs well enough.\"\nmsgstr \"检查函数是否执行得足够好。\"\n\n#: ../../Tigase_Development/Tests.rst:75\nmsgid \"`00:12:17 <tests/3.3.2-b889/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"`00:12:17 <tests/3.3.2-b889/perf/xmldb/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:75\nmsgid \"`00:13:42 <tests/3.3.2-b889/perf/mysql/performance-tests.html>`__\"\nmsgstr \"`00:13:42 <tests/3.3.2-b889/perf/mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:75\nmsgid \"`00:17:10 <tests/3.3.2-b889/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"`00:17:10 <tests/3.3.2-b889/perf/pgsql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:77\nmsgid \"`00:13:39 <tests/3.3.2-b880/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"`00:13:39 <tests/3.3.2-b880/perf/xmldb/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:77\nmsgid \"`00:14:09 <tests/3.3.2-b880/perf/mysql/performance-tests.html>`__\"\nmsgstr \"`00:14:09 <tests/3.3.2-b880/perf/mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:77\nmsgid \"`00:17:39 <tests/3.3.2-b880/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"`00:17:39 <tests/3.3.2-b880/perf/pgsql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:79\nmsgid \"`00:10:26 <tests/3.0.2-b700/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"`00:10:26 <tests/3.0.2-b700/perf/xmldb/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:79\nmsgid \"`00:11:00 <tests/3.0.2-b700/perf/mysql/performance-tests.html>`__\"\nmsgstr \"`00:11:00 <tests/3.0.2-b700/perf/mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:79\nmsgid \"`00:12:08 <tests/3.0.2-b700/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"`00:12:08 <tests/3.0.2-b700/perf/pgsql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:79\nmsgid \"`00:24:05 <tests/3.0.2-b700/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"`00:24:05 <tests/3.0.2-b700/perf/sm-mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:81\nmsgid \"`00:09:54 <tests/2.9.5-b606/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"`00:09:54 <tests/2.9.5-b606/perf/xmldb/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:81\nmsgid \"`00:11:18 <tests/2.9.5-b606/perf/mysql/performance-tests.html>`__\"\nmsgstr \"`00:11:18 <tests/2.9.5-b606/perf/mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:81\nmsgid \"`00:37:08 <tests/2.9.5-b606/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"`00:37:08 <tests/2.9.5-b606/perf/pgsql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:81\nmsgid \"`00:16:20 <tests/2.9.5-b606/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"`00:16:20 <tests/2.9.5-b606/perf/sm-mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:83\nmsgid \"`00:10:00 <tests/2.9.3-b548/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"`00:10:00 <tests/2.9.3-b548/perf/xmldb/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:83\nmsgid \"`00:11:29 <tests/2.9.3-b548/perf/mysql/performance-tests.html>`__\"\nmsgstr \"`00:11:29 <tests/2.9.3-b548/perf/mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:83\nmsgid \"`00:36:43 <tests/2.9.3-b548/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"`00:36:43 <tests/2.9.3-b548/perf/pgsql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:83\nmsgid \"`00:16:47 <tests/2.9.3-b548/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"`00:16:47 <tests/2.9.3-b548/perf/sm-mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:85\nmsgid \"`00:09:46 <tests/2.9.1-b528/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"`00:09:46 <tests/2.9.1-b528/perf/xmldb/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:85\nmsgid \"`00:11:15 <tests/2.9.1-b528/perf/mysql/performance-tests.html>`__\"\nmsgstr \"`00:11:15 <tests/2.9.1-b528/perf/mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:85\nmsgid \"`00:36:12 <tests/2.9.1-b528/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"`00:36:12 <tests/2.9.1-b528/perf/pgsql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:85\nmsgid \"`00:16:36 <tests/2.9.1-b528/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"`00:16:36 <tests/2.9.1-b528/perf/sm-mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:87\nmsgid \"`00:10:02 <tests/2.8.6-b434/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"`00:10:02 <tests/2.8.6-b434/perf/xmldb/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:87\nmsgid \"`00:11:45 <tests/2.8.6-b434/perf/mysql/performance-tests.html>`__\"\nmsgstr \"`00:11:45 <tests/2.8.6-b434/perf/mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:87\nmsgid \"`00:36:36 <tests/2.8.6-b434/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"`00:36:36 <tests/2.8.6-b434/perf/pgsql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:87\nmsgid \"`00:17:36 <tests/2.8.6-b434/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"`00:17:36 <tests/2.8.6-b434/perf/sm-mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:89\nmsgid \"`00:12:37 <tests/2.8.5-b422/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"`00:12:37 <tests/2.8.5-b422/perf/xmldb/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:89\nmsgid \"`00:14:40 <tests/2.8.5-b422/perf/mysql/performance-tests.html>`__\"\nmsgstr \"`00:14:40 <tests/2.8.5-b422/perf/mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:89\nmsgid \"`00:38:59 <tests/2.8.5-b422/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"`00:38:59 <tests/2.8.5-b422/perf/pgsql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:89\nmsgid \"`00:21:40 <tests/2.8.5-b422/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"`00:21:40 <tests/2.8.5-b422/perf/sm-mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:91\nmsgid \"`00:12:32 <tests/2.8.3-b409/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"`00:12:32 <tests/2.8.3-b409/perf/xmldb/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:91\nmsgid \"`00:14:26 <tests/2.8.3-b409/perf/mysql/performance-tests.html>`__\"\nmsgstr \"`00:14:26 <tests/2.8.3-b409/perf/mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:91\nmsgid \"`00:37:57 <tests/2.8.3-b409/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"`00:37:57 <tests/2.8.3-b409/perf/pgsql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:91\nmsgid \"`00:21:26 <tests/2.8.3-b409/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"`00:21:26 <tests/2.8.3-b409/perf/sm-mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:93\nmsgid \"`00:12:28 <tests/2.7.2-b378/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"`00:12:28 <tests/2.7.2-b378/perf/xmldb/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:93\nmsgid \"`00:14:57 <tests/2.7.2-b378/perf/mysql/performance-tests.html>`__\"\nmsgstr \"`00:14:57 <tests/2.7.2-b378/perf/mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:93\nmsgid \"`00:37:09 <tests/2.7.2-b378/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"`00:37:09 <tests/2.7.2-b378/perf/pgsql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:93\nmsgid \"`00:22:20 <tests/2.7.2-b378/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"`00:22:20 <tests/2.7.2-b378/perf/sm-mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:95\nmsgid \"`00:12:46 <tests/2.6.4-b300/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"`00:12:46 <tests/2.6.4-b300/perf/xmldb/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:95\nmsgid \"`00:14:59 <tests/2.6.4-b300/perf/mysql/performance-tests.html>`__\"\nmsgstr \"`00:14:59 <tests/2.6.4-b300/perf/mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:95\nmsgid \"`00:36:56 <tests/2.6.4-b300/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"`00:36:56 <tests/2.6.4-b300/perf/pgsql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:95\nmsgid \"`00:35:00 <tests/2.6.4-b300/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"`00:35:00 <tests/2.6.4-b300/perf/sm-mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:97\nmsgid \"`00:12:23 <tests/2.6.4-b295/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"`00:12:23 <tests/2.6.4-b295/perf/xmldb/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:97\nmsgid \"`00:14:59 <tests/2.6.4-b295/perf/mysql/performance-tests.html>`__\"\nmsgstr \"`00:14:59 <tests/2.6.4-b295/perf/mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:97\nmsgid \"`00:42:24 <tests/2.6.4-b295/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"`00:42:24 <tests/2.6.4-b295/perf/pgsql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:97\nmsgid \"`00:30:18 <tests/2.6.4-b295/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"`00:30:18 <tests/2.6.4-b295/perf/sm-mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:99\nmsgid \"`00:13:50 <tests/2.6.0-b287/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"`00:13:50 <tests/2.6.0-b287/perf/xmldb/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:99\nmsgid \"`00:16:53 <tests/2.6.0-b287/perf/mysql/performance-tests.html>`__\"\nmsgstr \"`00:16:53 <tests/2.6.0-b287/perf/mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:99\nmsgid \"`00:48:17 <tests/2.6.0-b287/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"`00:48:17 <tests/2.6.0-b287/perf/pgsql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:99\nmsgid \"`00:49:06 <tests/2.6.0-b287/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"`00:49:06 <tests/2.6.0-b287/perf/sm-mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:101\nmsgid \"`00:13:29 <tests/2.5.0-b279/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"`00:13:29 <tests/2.5.0-b279/perf/xmldb/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:101\nmsgid \"`00:16:58 <tests/2.5.0-b279/perf/mysql/performance-tests.html>`__\"\nmsgstr \"`00:16:58 <tests/2.5.0-b279/perf/mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:101\nmsgid \"`00:47:15 <tests/2.5.0-b279/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"`00:47:15 <tests/2.5.0-b279/perf/pgsql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:101\nmsgid \"`00:41:52 <tests/2.5.0-b279/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"`00:41:52 <tests/2.5.0-b279/perf/sm-mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:103\nmsgid \"`00:13:20 <tests/2.4.0-b263/perf/xmldb/performance-tests.html>`__\"\nmsgstr \"`00:13:20 <tests/2.4.0-b263/perf/xmldb/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:103\nmsgid \"`00:16:21 <tests/2.4.0-b263/perf/mysql/performance-tests.html>`__\"\nmsgstr \"`00:16:21 <tests/2.4.0-b263/perf/mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:103\nmsgid \"`00:43:56 <tests/2.4.0-b263/perf/pgsql/performance-tests.html>`__\"\nmsgstr \"`00:43:56 <tests/2.4.0-b263/perf/pgsql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:103\nmsgid \"`00:42:08 <tests/2.4.0-b263/perf/sm-mysql/performance-tests.html>`__\"\nmsgstr \"`00:42:08 <tests/2.4.0-b263/perf/sm-mysql/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:105\nmsgid \"`01:23:30 <tests/performance-tests.html>`__\"\nmsgstr \"`01:23:30 <tests/performance-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:109\nmsgid \"Stability Tests\"\nmsgstr \"稳定性测试\"\n\n#: ../../Tigase_Development/Tests.rst:111\nmsgid \"\"\n\"Checking to see whether the function behaves well in long term run. It \"\n\"must handle hundreds of requests a second in a several hour server run.\"\nmsgstr \"检查该功能在长期运行中是否表现良好。它必须在几个小时的服务器运行中每秒处理数\"\n\"百个请求。\"\n\n#: ../../Tigase_Development/Tests.rst:116\nmsgid \"`16:06:31 <tests/stability-tests.html>`__\"\nmsgstr \"`16:06:31 <tests/stability-tests.html>`__\"\n\n#: ../../Tigase_Development/Tests.rst:120\nmsgid \"Tigase Test Suite\"\nmsgstr \"Tigase测试套件\"\n\n#: ../../Tigase_Development/Tests.rst:122\nmsgid \"\"\n\"Tigase Test Suite is an engine which allows you to run tests. Essentially\"\n\" it just executes **TestCase** implementations. The tests may depend on \"\n\"other tests which means they are executed in specific order. For example \"\n\"authentication test is executed after the stream open test which in turn \"\n\"is executed after network socket connection test.\"\nmsgstr \"\"\n\"Tigase Test Suite是一个允许您运行测试的引擎。本质上它只是执行 **TestCase** 实\"\n\"现。测试可能依赖于其他测试，这意味着它们按特定顺序执行。例如，身份验证测试在\"\n\"流打开测试之后执行，而流打开测试又在网络套接字连接测试之后执行。\"\n\n#: ../../Tigase_Development/Tests.rst:124\nmsgid \"\"\n\"Each **TestCase** implementation may have it’s own set of specific \"\n\"parameters. There is a set of common parameters which may be applied to \"\n\"any **TestCase**. As an example of the common parameter you can take \"\n\"**-loop = 10** which specified that the **TestCase** must be executed 10 \"\n\"times. The test specific parameter might be **-user-name = tester** which\"\n\" may set the user name for authentication test.\"\nmsgstr \"\"\n\"每个 **TestCase** 实现都可能有自己的一组特定参数。\"\n\"有一组通用参数可以应用于任何 **TestCase**。作为通用参数的示例，您可以使用 **-\"\n\"loop = 10** 指定 **TestCase** 必须执行10次。测试特定参数可能是 **-user-name \"\n\"= tester** ，它可以设置身份验证测试的用户名。\"\n\n#: ../../Tigase_Development/Tests.rst:126\nmsgid \"\"\n\"The engine is very generic and allows you to write any kind of tests but \"\n\"for the Tigase projects the current TestCase implementations mimic an \"\n\"XMPP client and are designed to test XMPP servers.\"\nmsgstr \"该引擎非常通用，允许您编写任何类型的测试，但对于Tigase项目，当前的TestCase实\"\n\"现模仿XMPP客户端，旨在测试XMPP 服务器。\"\n\n#: ../../Tigase_Development/Tests.rst:128\nmsgid \"\"\n\"The suite contains a kind of scripting language which allows you to \"\n\"combine test cases into a test scenarios. The test scenario may contain \"\n\"full set of functional tests for example, another test scenario may \"\n\"contain performance tests and so on.\"\nmsgstr \"该套件包含一种脚本语言，允许您将测试用例组合到测试场景中。测试场景可能包含全\"\n\"套功能测试，比如，另一个测试场景可能包含性能测试等。\"\n\n#: ../../Tigase_Development/Tests.rst:131\nmsgid \"Running Tigase Test Suite (TTS)\"\nmsgstr \"运行Tigase测试套件 (TTS)\"\n\n#: ../../Tigase_Development/Tests.rst:133\nmsgid \"To obtain TTS, you will first need to clone the repository\"\nmsgstr \"要获取TTS，您首先需要克隆存储库\"\n\n#: ../../Tigase_Development/Tests.rst:139\nmsgid \"\"\n\"Once cloning is finished, navigate to the TTS root directory and compile \"\n\"with maven:\"\nmsgstr \"克隆完成后，导航到TTS根目录并使用maven编译：\"\n\n#: ../../Tigase_Development/Tests.rst:145\nmsgid \"\"\n\"Maven will compile TTS and place jars in the necessary locations. From \"\n\"the same directory, you can begin running TTS using the following \"\n\"command:\"\nmsgstr \"Maven将编译TTS并将jars放在必要的位置。在同一目录中，您可以使用以下命令开始运\"\n\"行TTS：\"\n\n#: ../../Tigase_Development/Tests.rst:151\nmsgid \"\"\n\"You should see the following, which outlines the possible options to \"\n\"customize your test run\"\nmsgstr \"您应该看到以下内容，其中概述了自定义测试运行的可能选项\"\n\n#: ../../Tigase_Development/Tests.rst:193\nmsgid \"Customizing Tigase Test Suite\"\nmsgstr \"自定义Tigase测试套件\"\n\n#: ../../Tigase_Development/Tests.rst:195\nmsgid \"\"\n\"You may run the tests from a command line like above, however you may \"\n\"create and edit the /scripts/tests-runner-settings.sh file to fit your \"\n\"Tigase installation and avoid having to have long complex commands as \"\n\"this template shows:\"\nmsgstr \"\"\n\"您可以像上面一样从命令行运行测试，但是您可以创建和编辑 /scripts/tests-runner-\"\n\"settings.sh \"\n\"文件以适应您的Tigase安装，并避免像此模板所示的那样使用冗长的复杂命令：\"\n\n#: ../../Tigase_Development/Tests.rst:225\nmsgid \"\"\n\"This will allow you to maintain identical settings through multiple runs \"\n\"of TTS. See the next section for learning how the scripting language \"\n\"works and how you can create and run your own custom tests.\"\nmsgstr \"这将允许您通过多次运行TTS来保持相同的设置。请参阅下一节以了解脚本语言的工作原\"\n\"理以及如何创建和运行自己的自定义测试。\"\n\n#: ../../Tigase_Development/Tests.rst:228\nmsgid \"Test Suite Scripting Language\"\nmsgstr \"测试套件脚本语言\"\n\n#: ../../Tigase_Development/Tests.rst:230\nmsgid \"\"\n\"The test suite contains scripting language which allows you to combine \"\n\"test cases into a test scenarios. On the lowest level, however the \"\n\"language is designed to allow you to describe the test by setting test \"\n\"parameters, test comments, identification and so on.\"\nmsgstr \"测试套件包含脚本语言，允许您将测试用例组合到测试场景中。然而，在最低级别上，\"\n\"该语言旨在允许您通过设置测试参数、测试注释、标识等来描述测试。\"\n\n#: ../../Tigase_Development/Tests.rst:232\nmsgid \"Let’s look at the example test description.\"\nmsgstr \"让我们看一下示例测试描述。\"\n\n#: ../../Tigase_Development/Tests.rst:244\nmsgid \"Meaning of all elements:\"\nmsgstr \"所有元素的含义：\"\n\n#: ../../Tigase_Development/Tests.rst:246\nmsgid \"\"\n\"**Short name** is any descriptive name you want. It doesn’t need to be \"\n\"unique, just something which tells you what this test is about. @ is a \"\n\"separator between the short name and the test ids.\"\nmsgstr \"**Short name** \"\n\"是您想要的任何描述性名称。它不需要是唯一的，只是告诉你这个测试是关于什么的。@\"\n\" 是短名称和测试ID之间的分隔符。\"\n\n#: ../../Tigase_Development/Tests.rst:248\nmsgid \"\"\n\"**test-id-1;test-id-2** is a semicolon separated of the test cases IDs. \"\n\"The tests cases are executed in the listed order. And listing them there \"\n\"means that the test-id-2 depends on test-id-1. Normally you don’t have to\"\n\" list all the dependencies because all mandatory dependencies are \"\n\"included automatically. Which means if you have an authentication test \"\n\"case the suite adds the network socket connection and stream opening \"\n\"tests automatically. Sometimes however, there are dependencies which are \"\n\"optional or multiple mandatory dependencies and you need to select which \"\n\"one has to be executed. As a good example is the authentications test \"\n\"case. There are many authentication tests: PLAIN-AUTH, SASL-DIGESTMD5, \"\n\"SASL-PLAIN, DIGEST-AUTH and they are all mandatory for most of other \"\n\"tests like roster, presence and so on. One of the authentication tests is\"\n\" a default dependency but if you put on the list different authentication\"\n\" it will be used instead of default one.\"\nmsgstr \"\"\n\"**test-id-1;test-id-2** 是用分号分隔的测试用例ID。测试用例按列出的顺序执行\"\n\"。并在那里列出它们其意味着test-id-2取决于 test-id-1。通常，您不必列出所有依赖\"\n\"项，因为所有强制依赖项都会自动包含在内。这意味着如果您有一个身份验证测试用例\"\n\"，该套件会自动添加网络套接字连接和流打开测试。但是，有时存在可选的依赖项或多\"\n\"个强制依赖项，您需要选择必须执行的依赖项。一个很好的例子是身份验证测试用例。\"\n\"有许多身份验证测试：PLAIN-AUTH、SASL-DIGESTMD5、SASL-PLAIN、DIGEST-\"\n\"AUTH，它们对于大多数其他测试（如 roster、presence 等）都是强制性的。其中一项\"\n\"身份验证测试是默认依赖项，但如果您将不同的身份验证放在列表中，它将被使用而不\"\n\"是默认的。\"\n\n#: ../../Tigase_Development/Tests.rst:250\nmsgid \"\"\n\"**:** is a separator between test cases ids list and the short test \"\n\"description.\"\nmsgstr \"**:** 是测试用例ID列表和简短测试描述之间的分隔符。\"\n\n#: ../../Tigase_Development/Tests.rst:252\nmsgid \"\"\n\"**Short test description** is placed between : - colon and opening **{** \"\n\"- curly bracket. This is usually quite brief, single line test \"\n\"description.\"\nmsgstr \"**Short test description** 放在： - 冒号和开头 **{** - \"\n\"大括号之间。这通常是非常简短的单行测试描述。\"\n\n#: ../../Tigase_Development/Tests.rst:254\nmsgid \"\"\n\"**{ }** curly brackets contain all the test parameters, like how many \"\n\"times the test has to be executed or run the test in a separate thread, \"\n\"user name, host IP address for the network connection and many others.\"\nmsgstr \"**{ }** 大括号包含所有测试参数，例如必须执行多少次测试或在单独的线程中运行测\"\n\"试、用户名、网络连接的主机IP地址等等。\"\n\n#: ../../Tigase_Development/Tests.rst:256\nmsgid \"\"\n\"**>> <<** inside the double greater than and double less than you put a \"\n\"very long, multiple line test description.\"\nmsgstr \"**>> <<** 在两个大于号和两个小于号里面你放了一个很长的多行测试描述。\"\n\n#: ../../Tigase_Development/Tests.rst:258\nmsgid \"\"\n\"As for the testing script between open curly brackets { and close one } \"\n\"you can put all the test case parameters you wish. The format for it is:\"\nmsgstr \"对于左大括号 { 和右大括号 } \"\n\"之间的测试脚本，您可以放置您想要的所有测试用例参数。它的格式是：\"\n\n#: ../../Tigase_Development/Tests.rst:260\nmsgid \"**-parameter-name = value**\"\nmsgstr \"**-parameter-name = value**\"\n\n#: ../../Tigase_Development/Tests.rst:262\nmsgid \"\"\n\"Parameter names always start with **-**. Note, some parameters don’t \"\n\"require any value. They can exist on their own without any value \"\n\"assigned:\"\nmsgstr \"参数名称始终以 **-** \"\n\"开头。请注意，某些参数不需要任何值。它们可以独立存在而无需分配任何值：\"\n\n#: ../../Tigase_Development/Tests.rst:264\nmsgid \"**-debug-on-error**\"\nmsgstr \"**-debug-on-error**\"\n\n#: ../../Tigase_Development/Tests.rst:266\nmsgid \"This imitates if you were to put **yes** or **true** as the value.\"\nmsgstr \"这模拟了您是否将 **yes** 或 **true** 作为值。\"\n\n#: ../../Tigase_Development/Tests.rst:268\nmsgid \"\"\n\"The scripting language includes also support for variables which can be \"\n\"assigned any value and used multiple times later on. You assign a value \"\n\"to the variable the same way as you assign it to the parameter:\"\nmsgstr \"脚本语言还包括对可以分配任何值并在以后多次使用的变量的支持。将值分配给变量的\"\n\"方式与将其分配给参数的方式相同：\"\n\n#: ../../Tigase_Development/Tests.rst:270\nmsgid \"**$(variable-name) = value**\"\nmsgstr \"**$(variable-name) = value**\"\n\n#: ../../Tigase_Development/Tests.rst:272\nmsgid \"\"\n\"The variable name must be always enclosed with brackets **()** and start \"\n\"with **$**.\"\nmsgstr \"变量名必须始终用方括号 **()** 括起来并以 **$** 开头。\"\n\n#: ../../Tigase_Development/Tests.rst:274\nmsgid \"\"\n\"The value may be enclosed within double quotes **\\\"\\\"** or double quotes \"\n\"may be omitted. If this is a simple string like a number or character \"\n\"string consisting only of digits, letters, underscore **\\\\_** and hyphen \"\n\"**-** then you can omit double quotes otherwise you must enclose the \"\n\"value.\"\nmsgstr \"\"\n\"该值可以用双引号 **\\\"\\\"** \"\n\"括起来，或者可以省略双引号。如果这是一个简单的字符串，例如仅由数字、字母、\"\n\"下划线 **\\\\_** 和连字符 **-** \"\n\"组成的数字或字符串，则可以省略双引号，否则必须将值括起来。\"\n\n#: ../../Tigase_Development/Tests.rst:276\nmsgid \"\"\n\"The test case descriptions can be nested inside other test case \"\n\"descriptions. Nested test case descriptions inherit parameters and \"\n\"variables from outer test case description.\"\nmsgstr \"测试用例描述可以嵌套在其他测试用例描述中。嵌套测试用例描述从外部测试用例描述\"\n\"继承参数和变量。\"\n\n#: ../../Tigase_Development/Tests.rst:279\nmsgid \"Writing Tests for Plugins\"\nmsgstr \"为插件编写测试\"\n\n#: ../../Tigase_Development/Tests.rst:281\nmsgid \"\"\n\"You can write tests in a simple text file which is loaded during test \"\n\"suite runtime.\"\nmsgstr \"您可以在测试套件运行时加载的简单文本文件中编写测试。\"\n\n#: ../../Tigase_Development/Tests.rst:283\nmsgid \"\"\n\"You simply specify what should be send to the server and what response \"\n\"should be expected from the server. No need to write Java code and \"\n\"recompile the whole test suite for new tests. It means new test cases can\"\n\" be now written easily and quickly which hopefully means more detailed \"\n\"tests for the server.\"\nmsgstr \"\"\n\"您只需指定应发送到服务器哪些内容以及应从服务器获得哪些响应。无需为新测试编写J\"\n\"ava代码和重新编译整个测试套件。这意味着现在可以轻松快速地编写新的测试用例，这\"\n\"可能意味着对服务器进行更详细的测试。\"\n\n#: ../../Tigase_Development/Tests.rst:285\nmsgid \"How it works:\"\nmsgstr \"这个是如何工作的：\"\n\n#: ../../Tigase_Development/Tests.rst:287\nmsgid \"\"\n\"Let’s take `XEP-0049 <http://www.xmpp.org/extensions/xep-0049.html>`__ \"\n\"Private XML Storage. Looking into the spec we can see the first example:\"\nmsgstr \"\"\n\"让我们以 `XEP-0049 <http://www.xmpp.org/extensions/xep-0049.html>`__ \"\n\"私有XML存储为例。查看规范，我们可以看到第一个示例：\"\n\n#: ../../Tigase_Development/Tests.rst:290\nmsgid \"Example: Client Stores Private Data\"\nmsgstr \"示例：客户端存储私有数据\"\n\n#: ../../Tigase_Development/Tests.rst:292\nmsgid \"**CLIENT:**\"\nmsgstr \"**客户：**\"\n\n#: ../../Tigase_Development/Tests.rst:304\nmsgid \"**SERVER:**\"\nmsgstr \"**服务器：**\"\n\n#: ../../Tigase_Development/Tests.rst:310\nmsgid \"\"\n\"This is enough for the first simple test. I have to create text file \"\n\"``JabberIqPrivate.test`` looking like this:\"\nmsgstr \"这对于第一个简单的测试来说已经足够了。我必须创建如下所示的文本文件 \"\n\"``JabberIqPrivate.test``：\"\n\n#: ../../Tigase_Development/Tests.rst:329\nmsgid \"And now I can execute the test:\"\nmsgstr \"现在我可以执行测试了：\"\n\n#: ../../Tigase_Development/Tests.rst:366\nmsgid \"\"\n\"If I just started working on this XEP and there is no code on the server \"\n\"side, the result is perfectly expected although maybe this is not what we\"\n\" want. After a while of working on the server code I can execute the test\"\n\" once again:\"\nmsgstr \"\"\n\"如果我刚开始研究这个XEP并且在服务器端没有代码，那么结果是完全可以预期的，尽管\"\n\"这可能不是我们想要的。在处理服务器代码一段时间后，我可以再次执行测试：\"\n\n#: ../../Tigase_Development/Tests.rst:396\nmsgid \"\"\n\"This is it. The result we want in a simple and efficient way. We can \"\n\"repeat it as many times we want which is especially important in longer \"\n\"term trials. Every time we change the server code we can re-run tests to \"\n\"make sure we get correct responses from the server.\"\nmsgstr \"\"\n\"就是这些。以简单有效的方式获得我们想要的结果。我们可以重复多次，这在长期试验\"\n\"中尤为重要。每次我们更改服务器代码时，我们都可以重新运行测试以确保我们从服务\"\n\"器获得正确的响应。\"\n\n#: ../../Tigase_Development/Tests.rst:398\nmsgid \"\"\n\"You can have a look in the current build, with more complete test cases, \"\n\"file for `JabberIqPrivate <https://github.com/tigase/tigase-\"\n\"testsuite/tree/master/tests/data/JabberIqPrivate.cot>`__.\"\nmsgstr \"\"\n\"您可以查看当前构建，包含更完整的测试用例，文件为 `JabberIqPrivate \"\n\"<https://github.com/tigase/tigase-testsuite/tree/master/tests/data/\"\n\"JabberIqPrivate.cot>`__。\"\n\n#: ../../Tigase_Development/Tests.rst:400\nmsgid \"\"\n\"Now my server tests are no longer outdated. Of course not all cases are \"\n\"so simple. Some XEPs require calculations to be done before stanza is \"\n\"sent or to compare received results. A good example for this case is user\"\n\" authentication like SASL and even NON-SASL. But still, there are many \"\n\"cases which can be covered by simple tests: roster management, privacy \"\n\"lists management, vCard, private data storage and so on.\"\nmsgstr \"\"\n\"现在我的服务器测试不再过时。当然，并非所有情况都如此简单。一些XEP要求在发送节\"\n\"之前进行计算或比较接收到的结果。这种情况的一个很好的例子是像SASL甚至NON-SASL\"\n\"这样的用户身份验证。但是，仍然有很多情况可以通过简单的测试来涵盖：名册管理、\"\n\"隐私列表管理、vCard、私人数据存储等等。\"\n\n#: ../../Tigase_Development/Tests.rst:403\nmsgid \"Test Case Parameters Description\"\nmsgstr \"测试用例参数说明\"\n\n#: ../../Tigase_Development/Tests.rst:405\n#: ../../Tigase_Development/Tests.rst:407\nmsgid \"\"\n\"There is long list of parameters which can be applied to any test case. \"\n\"Here is the description of all possible parameters which can be used to \"\n\"build test scenarios.\"\nmsgstr \"有一长串参数可以应用于任何测试用例。以下是可用于构建测试场景的所有可能参数的\"\n\"描述。\"\n\n#: ../../Tigase_Development/Tests.rst:410\nmsgid \"Test Report Configuration\"\nmsgstr \"测试报告配置\"\n\n#: ../../Tigase_Development/Tests.rst:412\nmsgid \"\"\n\"There are test report parameters which must be set in the main script \"\n\"file in order to generate HTML report from the test. These parameters \"\n\"have no effect is they are set inside the test case description.\"\nmsgstr \"为了从测试中生成HTML报告，必须在主脚本文件中设置测试报告参数。这些参数没有任\"\n\"何影响，因为它们是在测试用例描述中设置的。\"\n\n#: ../../Tigase_Development/Tests.rst:414\nmsgid \"\"\n\"**-version = 2.0.0** sets the test script version. This is used to easily\"\n\" detect incompatibility issues when the test suite loads a script created\"\n\" for more recent version of the suite and may not work properly for this \"\n\"version.\"\nmsgstr \"\"\n\"**-version = 2.0.0** 设置测试脚本版本。当测试套件加载为该套件的更新版本创建的\"\n\"脚本并且可能无法在此版本中正常工作时，这用于轻松检测不兼容问题。\"\n\n#: ../../Tigase_Development/Tests.rst:416\nmsgid \"\"\n\"**-output-format = (html \\\\| html-content)** sets the output format for \"\n\"the test report. There is actually only one format possible right now - \"\n\"HTML. The only difference between these 2 options is that the **html** \"\n\"format creates full HTML page with HTML header and body. The **html-\"\n\"content** format on the other hand creates only what is inside \"\n\"``<body/>`` element. And is used to embed test result inside other HTML \"\n\"content.\"\nmsgstr \"\"\n\"**-output-format = (html \\\\| html-content)** 设置测试报告的输出格式。\"\n\"现在实际上只有一种可能的格式 - HTML。这两个选项之间的唯一区别是 **html** \"\n\"格式创建带有HTML标题和正文的完整HTML页面。另一方面，**html-content** \"\n\"格式只创建 ``<body/>`` 元素内的内容。并用于将测试结果嵌入到其他HTML内容中。\"\n\n#: ../../Tigase_Development/Tests.rst:418\nmsgid \"\"\n\"**-output-file = \\\"report-file.html\\\"** sets the file name for the test \"\n\"report.\"\nmsgstr \"**-output-file = \\\"report-file.html\\\"** 设置测试报告的文件名。\"\n\n#: ../../Tigase_Development/Tests.rst:420\nmsgid \"\"\n\"**-output-history = (yes \\\\| no)** sets logging of the all protocol data \"\n\"sent between test suite and the XMPP server. Normally for functional \"\n\"tests it is recommended to set it to **yes** but for all other tests like\"\n\" performance or load tests it should be set to **no**.\"\nmsgstr \"\"\n\"*-output-history = (yes \\\\| no)** 设置测试套件和XMPP服务器之间发送的所有协议\"\n\"数据的日志记录。通常，对于功能测试，建议将其设置为 \"\n\"**yes**，但对于所有其他测试，如性能或负载测试，应将其设置为 **no**。\"\n\n#: ../../Tigase_Development/Tests.rst:422\nmsgid \"\"\n\"**-history-format = separate-file** sets protocol data logging to a \"\n\"separate file. Currently this is the only possible option.\"\nmsgstr \"**-history-format = separate-file** \"\n\"将协议数据记录设置为单独的文件。目前这是唯一可能的选择。\"\n\n#: ../../Tigase_Development/Tests.rst:424\nmsgid \"**-output-cols = (5 \\\\| 7)** Only valid values are:\"\nmsgstr \"**-output-cols = (5 \\\\| 7)** 仅有有效值是：\"\n\n#: ../../Tigase_Development/Tests.rst:431\nmsgid \"\"\n\"**-title** = \\\"The title of the report page\\\" This parameter sets the \"\n\"test report title which is placed in the HTML page in the ``<title/>`` \"\n\"element as well as in the first page header.\"\nmsgstr \"**-title** = \\\"报告页的标题\\\" 此参数设置测试报告的标题，它位于HTML页面的 <\"\n\"title/> 元素以及第一页标题中。\"\n\n#: ../../Tigase_Development/Tests.rst:434\nmsgid \"Basic Test Parameters\"\nmsgstr \"基本测试参数\"\n\n#: ../../Tigase_Development/Tests.rst:436\nmsgid \"\"\n\"These parameters can be set on per-test case basis but usually they are \"\n\"set in the main script file to apply them to all test cases.\"\nmsgstr \"这些参数可以在每个测试用例的基础上设置，但通常它们在主脚本文件中设置以将它们\"\n\"应用于所有测试用例。\"\n\n#: ../../Tigase_Development/Tests.rst:438\nmsgid \"\"\n\"**-base-ns = \\\"jabber:client\\\"** sets the XML name space used for the XML\"\n\" stream in the XMPP connection. Some test cases can be used to test \"\n\"client to server protocol as well as server to server protocol and \"\n\"possibly different protocols added in the future.\"\nmsgstr \"\"\n\"**-base-ns = \\\"jabber:client\\\"** 设置用于XMPP连接中XML流的XML名称空间。一些测\"\n\"试用例可用于测试客户端到服务器协议以及服务器到服务器协议以及将来可能添加的不\"\n\"同协议。\"\n\n#: ../../Tigase_Development/Tests.rst:440\nmsgid \"\"\n\"**-debug** switches debugging mode on. All the communication between the \"\n\"test suite and the server is printed out to the text console and all \"\n\"other debugging information including java exceptions are displayed as \"\n\"well. It is especially useful when some test fails and you want to find \"\n\"out why.\"\nmsgstr \"\"\n\"**-debug** 打开调试模式。测试套件和服务器之间的所有通信都打印到文本控制台，并\"\n\"且还显示所有其他调试信息，包括 \"\n\"java异常。当某些测试失败并且您想找出原因时，它特别有用。\"\n\n#: ../../Tigase_Development/Tests.rst:442\nmsgid \"\"\n\"**-debug-on-error** switches on debugging mode on error detection. \"\n\"Normally debug output generates lots of message which makes the output \"\n\"very hard to read. Especially in the performance tests not only you can \"\n\"read fast scrolling lines of the protocol data but also it slows the test\"\n\" down. This option however turns debugging off if everything is working \"\n\"well and then generates debug output if any test error us detected.\"\nmsgstr \"\"\n\"**-debug-on-error** 在错误检测时打开调试模式。通常调试输出会生成大量消息，这\"\n\"使得输出很难阅读。特别是在性能测试中，您不仅可以读取协议数据的快速滚动行，而\"\n\"且还会减慢测试速度。但是，如果一切正常，此选项将关闭调试，然后如果我们检测到\"\n\"任何测试错误，则生成调试输出。\"\n\n#: ../../Tigase_Development/Tests.rst:444\nmsgid \"\"\n\"**-def-auth = (auth-plain \\\\| auth-digest \\\\| auth-sasl)** sets the \"\n\"default authentication method for the user connection.\"\nmsgstr \"\"\n\"**-def-auth = (auth-plain \\\\| auth-digest \\\\| auth-sasl)** \"\n\"设置用户连接的默认身份验证方法。\"\n\n#: ../../Tigase_Development/Tests.rst:446\nmsgid \"\"\n\"**-def-stream = (stream-client \\\\| stream-server \\\\| stream-component \\\\|\"\n\" stream-bosh)** sets the connection stream to be tested and the name \"\n\"space for the connection.\"\nmsgstr \"\"\n\"**-def-stream = (stream-client \\\\| stream-server \\\\| stream-component \\\\| \"\n\"stream-bosh)** 设置要测试的连接流和连接的命名空间。\"\n\n#: ../../Tigase_Development/Tests.rst:448\nmsgid \"\"\n\"**-host = \\\"host.name\\\"** the vhost name the tested server runs for. It \"\n\"may be the real DNS name or just configured for testing purposes \"\n\"hostname. It must match however the server configuration.\"\nmsgstr \"\"\n\"**-host = \\\"host.name\\\"** 测试服务器运行的虚拟主机名。它可能是真实的DNS名称，\"\n\"或者只是为了测试目的而配置的主机名。但是它必须与服务器配置相匹配。\"\n\n#: ../../Tigase_Development/Tests.rst:450\nmsgid \"\"\n\"**-keys-file = \\\"certs/keystore\\\"** sets the location of the keys store \"\n\"file. No need to touch it.\"\nmsgstr \"**-keys-file = \\\"certs/keystore\\\"** 设置密钥存储文件的位置。没必要碰它。\"\n\n#: ../../Tigase_Development/Tests.rst:452\nmsgid \"\"\n\"**-keys-file-password = keystore** sets the password for the keystore \"\n\"file. Normally you don’t have to touch it.\"\nmsgstr \"**-keys-file-password = keystore** 设置密钥库文件的密码。通常你不必触摸它。\"\n\n#: ../../Tigase_Development/Tests.rst:454\nmsgid \"\"\n\"**-serverip = \\\"127.0.0.1\\\"** defines the XMPP server IP address. You may\"\n\" omit this parameter and then the IP address will be determined \"\n\"automatically based on the server DNS address. However if the DNS address\"\n\" can not be correctly resolved or if you run tests on the localhost you \"\n\"can use this parameter to enforce the IP address.\"\nmsgstr \"\"\n\"**-serverip = \\\"127.0.0.1\\\"** 定义XMPP服务器IP地址。您可以省略此参数，\"\n\"然后将根据服务器DNS地址自动确定I 地址。但是，如果无法正确解析DNS地址，或者如\"\n\"果您在localhost上运行测试，则可以使用此参数来强制执行IP地址。\"\n\n#: ../../Tigase_Development/Tests.rst:456\nmsgid \"\"\n\"**-socket-wait = 10000** sets the network socket timeout in milliseconds \"\n\"that is maximum time the test suite will wait for the response from the \"\n\"server. You may want to increase the timeout for some specific tests \"\n\"which require lots of computation or database activity on the server. \"\n\"Normally 10 seconds is enough for most cases.\"\nmsgstr \"\"\n\"**-socket-wait = 10000** 以毫秒为单位设置网络套接字超时，这是测试套件等待服务\"\n\"器响应的最长时间。您可能希望增加某些特定测试的超时时间，这些测试需要在服务器\"\n\"上进行大量计算或数据库活动。对于大多数情况，通常10秒就足够了。\"\n\n#: ../../Tigase_Development/Tests.rst:458\nmsgid \"\"\n\"**-stop-on-fail = true** causes the script to terminate all actions on \"\n\"the first failed test case. It helps diagnosing the server state at the \"\n\"failure point.\"\nmsgstr \"**-stop-on-fail = true** 导致脚本终止对第一个失败的测试用例的所有操作。它有助\"\n\"于在故障点诊断服务器状态。\"\n\n#: ../../Tigase_Development/Tests.rst:460\nmsgid \"\"\n\"**-trust-file = \\\"certs/client_truststore\\\"** sets the file name for the \"\n\"client trust store file. No need to change it.\"\nmsgstr \"**-trust-file = \\\"certs/client_truststore\\\"** \"\n\"设置客户端信任存储文件的文件名。无需更改它。\"\n\n#: ../../Tigase_Development/Tests.rst:462\nmsgid \"\"\n\"**-trust-file-password = truststore** sets the password for the trust \"\n\"store file. Normally you don’t have to touch it.\"\nmsgstr \"**-trust-file-password = truststore** \"\n\"设置信任库文件的密码。通常你不必触摸它。\"\n\n#: ../../Tigase_Development/Tests.rst:464\nmsgid \"\"\n\"**-user-name = tester** sets the user name used for the XMPP connections \"\n\"between the test suite and the XMPP server. It is usually set globally \"\n\"the same for all tests and for some tests like receiving the server \"\n\"configuration you may want to use a different account (with admin \"\n\"permissions). Then you can set a different user for this specific test \"\n\"case.\"\nmsgstr \"\"\n\"**-user-name = tester** 设置用于测试套件和XMPP服务器之间的XMPP连接的用户名。\"\n\"它通常对所有测试全局设置相同，对于某些测试，如接收服务器配置，您可能希望使用\"\n\"不同的帐户（具有管理员权限）。然后，您可以为这个特定的测试用例设置不同的用户\"\n\"。\"\n\n#: ../../Tigase_Development/Tests.rst:466\nmsgid \"\"\n\"**-user-pass = tester-password** sets the password for the user used for \"\n\"the XMPP connection between the test suite and the XMPP server.\"\nmsgstr \"**-user-pass = tester-password** \"\n\"为用于测试套件和XMPP服务器之间的XMPP连接的用户设置密码。\"\n\n#: ../../Tigase_Development/Tests.rst:468\nmsgid \"\"\n\"**-user-resr = resource** sets the user JID resource part for the XMPP \"\n\"connection between the test suite and the XMPP server.\"\nmsgstr \"**-user-resr = resource** \"\n\"为测试套件和XMPP服务器之间的XMPP连接设置用户JID资源部分。\"\n\n#: ../../Tigase_Development/Tests.rst:471\nmsgid \"Test Case Parameters\"\nmsgstr \"测试用例参数\"\n\n#: ../../Tigase_Development/Tests.rst:473\nmsgid \"\"\n\"Test parameters which are normally set on per-test case basis and apply \"\n\"only to the test they are set for and all inherited tests. Some of the \"\n\"parameters though are applied only to inherited test cases. Please look \"\n\"in the description below to find more details.\"\nmsgstr \"\"\n\"测试参数通常在每个测试用例的基础上设置，并且仅适用于为其设置的测试和所有继承\"\n\"的测试。不过，有些参数仅适用于继承的测试用例。请查看下面的描述以找到更多详细\"\n\"信息。\"\n\n#: ../../Tigase_Development/Tests.rst:475\nmsgid \"\"\n\"**-active-connection** is a similar parameter to **-on-one-socket** \"\n\"option. If set the suite doesn’t close the network socket and if the test\"\n\" is run in loop each loop run re-uses the network connection. Unlike in \"\n\"the -on-one-socket mode the whole test is executed on each run including \"\n\"XMPP stream initialization and user authentication. This option is \"\n\"currently not recommended in a normal use. It is useful only to debug the\"\n\" server behavior in very special use cases.\"\nmsgstr \"\"\n\"**-active-connection** 是与 **-on-one-socket** 选项类似的参数。如果设置套件不\"\n\"会关闭网络套接字，并且如果测试在循环中运行，则每个循环运行都会重新使用网络连\"\n\"接。与 -on-one-socket 模式不同，整个测试在每次运行时执行，包括XMPP流初始化和\"\n\"用户身份验证。目前不建议在正常使用中使用此选项。仅在非常特殊的用例中调试服务\"\n\"器行为才有用。\"\n\n#: ../../Tigase_Development/Tests.rst:477\nmsgid \"\"\n\"**-background** executes the test in a separate thread in background and \"\n\"immediately returns control to the test suite program without waiting for\"\n\" the test to complete. Default behavior is to execute all tests \"\n\"sequentially and run next test when previous one has been completed. This\"\n\" parameter however allows to run tests concurrently. This a bit similar \"\n\"option to the **-daemon** parameter. The daemon test/task however is \"\n\"ignored completely and results from the daemon are not collected where \"\n\"the background test is a normal test which is run concurrently with \"\n\"another one or possibly many other tests.\"\nmsgstr \"\"\n\"**-background** 在后台的单独线程中执行测试，并立即将控制权返回给测试套件程序\"\n\"，而无需等待测试完成。默认行为是按顺序执行所有测试，并在前一个测试完成后运行\"\n\"下一个测试。但是，此参数允许同时运行测试。这有点类似于 **-daemon** 参数的选项\"\n\"。然而，守护程序测试/任务被完全忽略，并且在后台测试是与另一个或可能许多其他测\"\n\"试同时运行的正常测试的情况下，不会收集来自守护程序的结果。\"\n\n#: ../../Tigase_Development/Tests.rst:479\nmsgid \"\"\n\"**-daemon** creates a task running in background in a separate thread. \"\n\"Such a test runs infinitely as a daemon, it is not recorded in the test \"\n\"report and it’s result is not calculated. The purpose of such test/task \"\n\"is to work as a helper for other test cases. A good example of such \"\n\"daemon test is message responder - the test which runs under a different \"\n\"user name and waits for messages and responding to the sender.\"\nmsgstr \"\"\n\"**-daemon** 在单独的线程中创建在后台运行的任务。这样的测试作为守护进程无限运\"\n\"行，它不记录在测试报告中，也不计算它的结果。此类测试/任务的目的是充当其他测试\"\n\"用例的助手。此类守护程序测试的一个很好的例子是消息响应者 - \"\n\"以不同的用户名运行并等待消息并响应发送者的测试。\"\n\n#: ../../Tigase_Development/Tests.rst:481\nmsgid \"\"\n\"**-delay = 1000** sets the waiting time in milliseconds after the test \"\n\"case is completed. You may use it if you want to introduce short delay \"\n\"between each test cases run in the loop or if you start the helper daemon\"\n\" thread and you have to add the delay to make sure it is ready to work \"\n\"before next real test starts sending requests to the daemon.\"\nmsgstr \"\"\n\"**-delay = 1000** 设置测试用例完成后的等待时间（毫秒）。如果您想在循环中运行\"\n\"的每个测试用例之间引入短暂的延迟，或者如果您启动助手守护线程并且您必须添加延\"\n\"迟以确保它在下一个真实测试开始发送请求之前准备好工作，您可以使用它守护进程。\"\n\n#: ../../Tigase_Development/Tests.rst:483\nmsgid \"\"\n\"**-expect-type = error** sets the type for a packet expected as a \"\n\"response. Some test cases like message sender expects sometimes response \"\n\"with the same type it has sent the packet ( **chat** ) but in some other \"\n\"cases when it sends a message to a user who has privacy lists set to \"\n\"block messages the response should be with an error. This way we can use \"\n\"the same test cases for testing different responses scenarios.\"\nmsgstr \"\"\n\"**-expect-type = error** 设置预期作为响应的数据包的类型。某些测试用例（例如消\"\n\"息发送者）有时会期望以与发送数据包（**chat**）相同的类型进行响应，但在某些其\"\n\"他情况下，当它向已设置隐私列表以阻止消息的用户发送消息时，响应应该会有错误。\"\n\"这样我们就可以使用相同的测试用例来测试不同的响应场景。\"\n\n#: ../../Tigase_Development/Tests.rst:485\nmsgid \"\"\n\"**-loop = 10** sets the number of times the test (and all inherited \"\n\"tests) are repeated. You can use a **$(loop)** pseudo-variable to obtain \"\n\"and use the current loop run number. This is useful if you want to run \"\n\"every loop run for a different user name like registering 10 different \"\n\"user accounts. To do this you stick the $(loop) variable to the user name\"\n\" string: **-user-name = \\\"nick_name_$(loop)\\\"**.\"\nmsgstr \"\"\n\"**-loop = 10** 设置测试（以及所有继承的测试）重复的次数。您可以使用 \"\n\"**$(loop)** 伪变量来获取和使用当前循环运行次数。如果您想为不同的用户名（例如\"\n\"注册10个不同的用户帐户）运行每个循环运行，这很有用。为此，您将$(loop)变量粘贴\"\n\"到用户名字符串：**-user-name = \\\"nick_name_$(loop)\\\"**。\"\n\n#: ../../Tigase_Development/Tests.rst:487\nmsgid \"\"\n\"**-loop-delay = 10** sets a delay in milliseconds between each individual\"\n\" loop run for the tests which is run multiple times. This is similar \"\n\"parameter to the **-delay** one but the **-delay** option introduces a \"\n\"delay after the whole test (or all loop runs) has been completed. The \"\n\"loop delay options adds waiting time between each run of the looped test.\"\nmsgstr \"\"\n\"**-loop-delay = 10** \"\n\"为多次运行的测试设置每个单独循环运行之间的延迟（以毫秒为单位）。这与 \"\n\"**-delay** 参数类似，但 **-delay** 选项会在整个测试（或所有循环运行）完成后引\"\n\"入延迟。循环延迟选项增加了每次循环测试运行之间的等待时间。\"\n\n#: ../../Tigase_Development/Tests.rst:489\nmsgid \"\"\n\"**-loop-start = 5** sets the loop starting value. It doesn’t affect \"\n\"number of loop runs in a any way. It only affects the value of the \"\n\"**$(loop)** variable. Let’s say you want to run a load test for the \"\n\"server with 100k concurrent users and you want to run the test from 3 \"\n\"different machines. To make sure each machine uses distinct user accounts\"\n\" you have to set a different **-loop-start** parameter on each to prevent\"\n\" from overlapping.\"\nmsgstr \"\"\n\"**-loop-start = 5** 设置循环起始值。它不会以任何方式影响循环运行的次数。\"\n\"它只影响 **$(loop)** 变量的值。假设您想为具有100k并发用户的服务器运行负载测试\"\n\"，并且您想从3台不同的机器上运行测试。为确保每台机器使用不同的用户帐户，\"\n\"您必须在每台机器上设置不同的 **-loop-start** 参数以防止重叠。\"\n\n#: ../../Tigase_Development/Tests.rst:491\nmsgid \"\"\n\"**-messages = 10** sets the number of messages to send to the server. \"\n\"This is another way of looping the test. Instead of repeating the whole \"\n\"test with opening network connection, XMPP stream, authentication and so \"\n\"on it causes only to send the message this many times. This parameters is\"\n\" accepted by some test cases only which send messages. For the messages \"\n\"listeners - test cases which is supposed to respond to the messages the \"\n\"number set here specifies how many times the the response must be sent \"\n\"before the test successfully terminates it’s work.\"\nmsgstr \"\"\n\"**-messages = 10** 设置发送到服务器的消息数量。这是循环测试的另一种方式。而不\"\n\"是通过打开网络连接、XMPP流、身份验证等来重复整个测试，它只会导致多次发送消息\"\n\"。这个参数只被一些发送消息的测试用例接受。对于消息侦听器 - 应该响应消息的测试\"\n\"用例，此处设置的数字指定在测试成功终止之前必须发送响应的次数。\"\n\n#: ../../Tigase_Development/Tests.rst:493\nmsgid \"\"\n\"**-multi-thread** option causes to run the test case and all inherited in\"\n\" all levels test cases in separate threads. Normally the test case where \"\n\"you put the parameter doesn’t have a test ID (what you put between '@' \"\n\"and ':' characters so it doesn’t run a test on it’s own. Instead it \"\n\"contains a series of test cases inside which are then run in a separate \"\n\"thread each. This is a key parameter to run tests for many concurrent \"\n\"users. (Not a load tests though.) For example you can see whether the \"\n\"server behaves correctly when 5 simultaneous modifies their roster. The \"\n\"execution time all inherited tests run in a separate threads is added \"\n\"together and also results from each individual test is calculated and \"\n\"added to the total main test results.\"\nmsgstr \"\"\n\"**-multi-thread** 选项导致在单独的线程中运行测试用例并在所有级别的测试用例中\"\n\"全部继承。通常，您放置参数的测试用例没有测试ID（您在 '@' 和 ':' 字符之间放置\"\n\"的内容），因此它不会自行运行测试。相反，它在内部包含一系列测试用例然后每个线\"\n\"程在单独的线程中运行。这是为许多并发用户运行测试的关键参数。（虽然不是负载测\"\n\"试。）例如，当5人同时修改他们的花名册时，您可以查看服务器是否正确运行。执行将\"\n\"在单独线程中运行的所有继承测试加在一起，并且计算每个单独测试的结果并将其添加\"\n\"到总主测试结果中。\"\n\n#: ../../Tigase_Development/Tests.rst:495\nmsgid \"\"\n\"**-no-record** is used for kind of configuration tests (tasks) which are \"\n\"used to prepare the XMPP server or database for later tests. As an \"\n\"example can be creation of the test user account which is later on used \"\n\"for the roster tests. Usually you don’t want to include such tests in the\"\n\" test report and using this parameter you essentially exclude the test \"\n\"from the report. The test and the result however shows in the command \"\n\"line output so you can still track what is really going on.\"\nmsgstr \"\"\n\"**-no-record** 用于一种配置测试（任务），用于为以后的测试准备XMPP服务器或数据\"\n\"库。例如，可以创建稍后用于名册测试的测试用户帐户。通常你不想在测试报告中包含\"\n\"这样的测试，并且使用这个参数你基本上从报告中排除了测试。然而，测试和结果显示\"\n\"在命令行输出中，因此您仍然可以跟踪实际发生的情况。\"\n\n#: ../../Tigase_Development/Tests.rst:497\nmsgid \"\"\n\"**-on-one-socket** is a modifier for a looped test case. Normally when we\"\n\" switch looping on using **-loop** parameter the suite resets the state, \"\n\"closes the network socket and runs the test from the very beginning \"\n\"including opening network socket, XMPP stream, authentication and so on. \"\n\"This parameter however changes this behavior. The network socket is not \"\n\"closed when the test run is completed (successfully) and next run \"\n\"executes only the last part of the test omitting the XMPP stream \"\n\"initialization, authentication and all others but last. This is useful \"\n\"when you want to send many messages to the server (although this effect \"\n\"may be accomplished using **-messages** parameter as well) or registering\"\n\" many user accounts on the server, unregistering user accounts and any \"\n\"other which might make sense repeating many times.\"\nmsgstr \"\"\n\"**-on-one-socket** 是循环测试用例的修饰符。通常，当我们使用 **-loop** 参数打\"\n\"开循环时，套件会重置状态，关闭网络套接字并从一开始就运行测试，包括打开网络套\"\n\"接字、XMPP流、身份验证等。然而，这个参数改变了这种行为。当测试运行完成（成功\"\n\"）时网络套接字不会关闭，下一次运行只执行测试的最后一部分，同时省略XMPP流初始\"\n\"化、身份验证和除最后之外的所有其他部分。当您想向服务器发送许多消息（\"\n\"尽管此效果也可以使用 **-messages** 参数来实现）或在服务器上注册许多用户帐户、\"\n\"注销用户帐户和任何其他可能有意义的情况时，这重复多次可能是有意义的。\"\n\n#: ../../Tigase_Development/Tests.rst:499\nmsgid \"\"\n\"**-port = 5223** this parameter is similar to the IP address setting and \"\n\"can be also set globally for all tests. Normally however you set it for a\"\n\" selected tests only to check SSL connection. For all other tests default\"\n\" port number is used. Therefore this parameters has been included in this\"\n\" section instead of \\\"Basic test parameters\\\".\"\nmsgstr \"\"\n\"**-port = 5223** 该参数与IP地址设置类似，也可以对所有测试进行全局设置。但是，\"\n\"通常您将其设置为仅用于检查SSL连接的选定测试。对于所有其他测试，使用默认端口号\"\n\"。因此，此参数已包含在本节中，而不是\\\"基本测试参数\\\"。\"\n\n#: ../../Tigase_Development/Tests.rst:501\nmsgid \"\"\n\"**-presence** this parameter enables sending initial presence with \"\n\"positive priority after connection and binding the session.\"\nmsgstr \"**-presence** 此参数允许在连接和绑定会话后发送具有正优先级的初始状态。\"\n\n#: ../../Tigase_Development/Tests.rst:503\nmsgid \"\"\n\"**-repeat-script = 100** and **-repeat-wait = 10** are 2 parameters are \"\n\"specific to the common test cases. (The test cases which reads the test \"\n\"input/output data from the pseudo-xml text file. The first parameter is \"\n\"another variation of test looping. It sets how many times the test has to\"\n\" be repeated. It works very much like the **-on-one-socket** parameter. \"\n\"The only difference is that the common test can preserve some internal \"\n\"states between runs and therefore it has more control over the data. The \"\n\"second parameter sets the timeout in milliseconds to wait/delay between \"\n\"each individual test run and it is a very similar parameter to the \"\n\"**-delay** one but it sets a timeout inside the common test instead.\"\nmsgstr \"\"\n\"**-repeat-script = 100** 和 **-repeat-wait = 10** \"\n\"是针对常见测试用例的2个参数。 （从伪xml文本文件中读取测试输入/输出数据的测试\"\n\"用例）。第一个参数是测试循环的另一种变体。它设置测试必须重复多少次。\"\n\"它的工作方式非常类似于 ** -on-one-socket** 参数。唯一的区别是普通测试可以在运\"\n\"行之间保留一些内部状态，因此它可以更好地控制数据。第二个参数以毫秒为单位设置\"\n\"每个人之间的等待/延迟超时测试运行，它是一个与 **-delay** \"\n\"非常相似的参数，但它在通用测试中设置了超时。\"\n\n#: ../../Tigase_Development/Tests.rst:505\nmsgid \"\"\n\"**-source-file = \\\"dir/path/to/file.cot\\\"** is a parameter to set the \"\n\"\\\"common test\\\" script file. The common test is a test cases which \"\n\"depends on the authentication test case and can read data to send and \"\n\"responses to expect from the text file. The \\\"cot\\\" file is a pseudo-xml \"\n\"file with stanzas to send and stanzas to expect. The the test cases \"\n\"compares the received packets with those in the text file and reports the\"\n\" test result. This is usually a more convenient way to write a new test \"\n\"cases than coding them in Java.\"\nmsgstr \"\"\n\"**-source-file = \\\"dir/path/to/file.cot\\\"** 是设置\\\"通用测试\\\"脚本文件的参数\"\n\"。常见的测试是依赖于身份验证测试用例的测试用例，可以从文本文件中读取要发送的\"\n\"数据和期望的响应。 \\\"cot\\\"文件是一个伪xml文件，其中包含要发送的节和要期待的节\"\n\"。测试用例将接收到的数据包与文本文件中的数据包进行比较，并报告测试结果。这通\"\n\"常比用Java编写新的测试用例更方便。\"\n\n#: ../../Tigase_Development/Tests.rst:507\nmsgid \"\"\n\"**-time-out-ok** is set for a test case when we expect socket timeout as \"\n\"a correct result from the test case. Normally the timeout means that the \"\n\"test failed and there was no response from the server at all or the \"\n\"response was incorrect. For some tests however (like sending a message to\"\n\" the user who is blocking messages through privacy lists) the timeout is \"\n\"the desired correct test result.\"\nmsgstr \"\"\n\"当我们期望套接字超时作为测试用例的正确结果时，为测试用例设置 **-time-out-ok**\"\n\"。通常超时意味着测试失败并且服务器根本没有响应或响应不正确。然而，对于某些测\"\n\"试（例如向通过隐私列表阻止消息的用户发送消息）超时是所需的正确测试结果。\"\n\n#: ../../Tigase_Development/Tests.rst:509\nmsgid \"\"\n\"**-to-jid = \\\"**\\\\ user_name@host.name\\\\ **\\\"** sets the destination \"\n\"address for packets sending packets somewhere. As an example is the test \"\n\"case sending ``<message/>`` packet. You can set the destination address \"\n\"for the packet. Mind, normally every test expects some response for the \"\n\"data sent so make sure the destination end-point will send back the data \"\n\"expected by the test case.\"\nmsgstr \"\"\n\"**-to-jid = \\\"**\\\\ user_name@host.name\\\\ **\\\"** \"\n\"为在某处发送数据包的数据包设置目标地址。例如发送 <message/> 数据包的测试用例\"\n\"。您可以设置数据包的目标地址。请注意，通常每个测试都期望对发送的数据有一些响\"\n\"应，因此请确保目标端点将发回测试用例期望的数据。\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Development/Tigase_Kernel.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-08-03 03:02-0700\\n\"\n\"PO-Revision-Date: 2022-09-07 17:14+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/dg-tigase_kernel/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:2\nmsgid \"Tigase Kernel\"\nmsgstr \"Tigase内核\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:4\nmsgid \"\"\n\"Tigase Kernel is an implementation of `IoC \"\n\"<https://en.wikipedia.org/wiki/Inversion_of_control>`__ created for \"\n\"Tigase XMPP Server. It is responsible for maintaining object lifecycle \"\n\"and provides mechanisms for dependency resolutions between beans.\"\nmsgstr \"\"\n\"Tigas内核是为Tigase XMPP服务器创建的 `IoC \"\n\"<https://en.wikipedia.org/wiki/Inversion_of_control>`__ \"\n\"的实现。它负责维护对象生命周期，并为bean之间的依赖关系解析提供机制。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:6\nmsgid \"\"\n\"Additionally, as and optional feature, Tigase Kernel is capable of \"\n\"configuring beans using a provided bean configurator.\"\nmsgstr \"此外，作为可选功能，Tigase内核能够使用提供的bean配置器来配置bean。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:9\nmsgid \"Basics\"\nmsgstr \"基本\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:12\nmsgid \"What is kernel?\"\nmsgstr \"什么是内核？\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:14\nmsgid \"\"\n\"Kernel is an instance of the ``Kernel`` class which is responsible for \"\n\"managing scope and visibility of beans. Kernel handles bean:\"\nmsgstr \"内核是 ``Kernel`` 类的一个实例，它负责管理 bean 的范围和可见性。内核处理 bean：\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:16\nmsgid \"registration of a bean\"\nmsgstr \"一个bean的注册\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:18\nmsgid \"unregistration of a bean\"\nmsgstr \"一个bean的注销\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:20\nmsgid \"initialization of a bean\"\nmsgstr \"一个bean的初始化\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:22\nmsgid \"deinitialization of a bean\"\nmsgstr \"一个bean的去初始化\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:24\nmsgid \"dependency injection to the bean\"\nmsgstr \"bean的依赖注入\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:26\nmsgid \"handling of bean lifecycle\"\nmsgstr \"bean生命周期的处理\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:28\nmsgid \"\"\n\"registration of additional beans based on annotations (*optionally using \"\n\"registered class implementing* ``BeanConfigurator`` *as* \"\n\"``defaultBeanConfigurator``)\"\nmsgstr \"\"\n\"基于注解的附加bean的注册 （*作为* ``defaultBeanConfigurator`` \"\n\"*可选的使用注册类实现* ``BeanConfigurator`` ）\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:30\nmsgid \"\"\n\"configuration of a bean (*optionally thru registered class implementing* \"\n\"``BeanConfigurator`` *as* ``defaultBeanConfigurator``)\"\nmsgstr \"\"\n\"bean的配置 (*作为* ``defaultBeanConfigurator`` *可选的通过注册类实现* \"\n\"``BeanConfigurator`` )\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:32\nmsgid \"\"\n\"Kernel core is responsible for dependency resolution and maintaining \"\n\"lifecycle of beans. Other features, like proper configuration of beans \"\n\"are done by additional beans working inside the Kernel.\"\nmsgstr \"内核负责依赖解性析和维护bean的生命周期。其他功能，比如正确配置bean是由在内核中工作的附加bean完成的。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:34\nmsgid \"\"\n\"Kernel identifies beans by their name, so each kernel may have only one \"\n\"bean named ``abc``. If more than one bean has the same name, then the \"\n\"last one registered will be used as its registration will override \"\n\"previously registered beans. You may use whatever name you want to name a\"\n\" bean inside kernel but it cannot:\"\nmsgstr \"\"\n\"内核通过名称来识别bean，因此每个内核可能只有一个名为 ``abc`` \"\n\"的bean。如果多个bean具有相同的名称，那么最后一个注册此名称的将被使用，因为其注册将覆盖以前注册的bean。您可以使用任何您想在内核中命名的bean名称，但它不能：\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:36\nmsgid \"\"\n\"be ``service`` as this name is used by Tigase Kernel internally when \"\n\"RegistrarBean\\\\`s are in use (see :ref:`RegistrarBean<registrarBean>`)\"\nmsgstr \"\"\n\"是 ``service`` 因为这个名称是Tigase内核在使用 ``RegistrarBean`` \"\n\"时在内部使用的（请参阅 :ref:`RegistrarBean<registrarBean>`)\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:38\nmsgid \"\"\n\"end with ``#KERNEL`` as this names are also used by Tigase Kernel \"\n\"internally\"\nmsgstr \"以 ``#KERNEL`` 结尾，因为Tigase内核也在内部使用此名称\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:42\nmsgid \"\"\n\"Kernel initializes beans using lazy initialization. This means that if a \"\n\"bean is not required by any other beans, or not retrieved from the kernel\"\n\" manually, an instance will not be created.\"\nmsgstr \"内核使用延迟初始化来初始化bean。这意味着，如果任何其他bean都不需要此bean，或者没有从内核手动检索此bean，则不会创建实例。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:44\nmsgid \"\"\n\"During registration of a bean, the kernel checks if there is any beans \"\n\"which requires this newly registered bean and if so, then instance of a \"\n\"newly registered bean will be created and injected to fields which \"\n\"require it.\"\nmsgstr \"在注册bean期间，内核会检查是否有任何bean需要这个新注册的bean，如果是，则将创建新注册的bean的实例并将其注入到需要它的字段中。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:47\nmsgid \"What is a kernel scope?\"\nmsgstr \"什么是内核范围？\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:49\nmsgid \"\"\n\"Each kernel has its own scope in which it can look for beans. By default \"\n\"kernel while injecting dependencies may look for them only in the same \"\n\"kernel instance in which new instance of a bean is created or in the \"\n\"direct parent kernel. This way it is possible to have separate beans \"\n\"named the same in the different kernel scopes.\"\nmsgstr \"每个内核都有自己的范围，可以在其中查找bean。默认情况下，注入依赖项时的内核可能仅在创建bean的新实例的同一个内核实例中或在直接父内核中查找它们。通过这种方式，可以在不同的内核范围内拥有相同名称的单独bean。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:53\nmsgid \"\"\n\"If bean is marked as ``exportable``, it is also visible in all \"\n\"descendants kernel scopes.\"\nmsgstr \"如果 bean 被标记为 ``exportable``，它在所有后代内核范围内也是可见的。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:56\nmsgid \"What is a bean?\"\nmsgstr \"什么是bean?\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:57\nmsgid \"\"\n\"A bean is a named instance of the class which has parameterless \"\n\"constructor and which is registered in the kernel.\"\nmsgstr \"bean是类的命名实例，它有无参数构造函数并在内核中注册。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:61\nmsgid \"\"\n\"Parameterless constructor is a required as it will be used by kernel to \"\n\"create an instance of the bean, see :ref:`bean lifecycle<beanLifecycle>`.\"\nmsgstr \"无参数构造函数是必需的，因为内核将使用它来创建bean的实例，请参阅 \"\n\":ref:`bean生命周期<beanLifecycle>`。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:66\nmsgid \"Lifecycle of a bean\"\nmsgstr \"bean的生命周期\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:69\nmsgid \"Creating instance of a bean\"\nmsgstr \"创建一个bean的实例\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:72\nmsgid \"Instantiation of a bean\"\nmsgstr \"一个bean的实例化\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:74\nmsgid \"\"\n\"During this step, kernel creates instance of the class which was \"\n\"registered for this bean (for more details see **Registration of a \"\n\"bean**). Instance of a bean is created using paremeterless constructor of\"\n\" a class.\"\nmsgstr \"在此步骤中，内核创建该bean注册的类的实例（有关更多详细信息，请参阅 **注册一个bean**）。使用类的无参数构造函数创建bean的实例。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:78\nmsgid \"\"\n\"Bean instance is only created for required beans (i.e. beans that were \"\n\"injected somewhere).\"\nmsgstr \"Bean实例只为所需的bean（即注入某处的bean）创建。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:82\nmsgid \"\"\n\"It’s possible to create bean instance without the need to inject it \"\n\"anywhere - such bean should be annoted with ``@Autostart`` annotation.\"\nmsgstr \"可以创建bean实例而不需要在任何地方注入它 - 这样的bean应该使用 ``@Autostart`` 进行注释。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:87\nmsgid \"Configuring a bean *(optional)*\"\nmsgstr \"配置一个bean *（可选）*\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:89\nmsgid \"\"\n\"In this step kernel passes class instance of a bean to the configurator \"\n\"bean (an instance of ``BeanConfigurator`` if available), for configuring \"\n\"it. During this step, ``BeanConfigurator`` instance, which is aware of \"\n\"the configuration loaded from the file, injects this configuration to the\"\n\" bean fields annotated with ``@ConfigField`` annotation. By default \"\n\"configurator uses reflections to access those fields. However, if a bean \"\n\"has a corresponding public ``setter``/``getter`` methods for a field \"\n\"annotated with ``@ConfigField`` (method parameter/return type matches \"\n\"field type), then configurator will use them instead of accessing a field\"\n\" via reflection.\"\nmsgstr \"\"\n\"在此步骤中，内核将bean的类实例传递给配置器bean（如果可用，则为 ``BeanConfigurator`` \"\n\"的实例），以对其进行配置。在此步骤中， ``BeanConfigurator`` 实例，其知道从文件加载的配置，并将此配置注入到带有 \"\n\"``@ConfigField`` 注释的 bean字段中。默认情况下，配置器使用反射来访问这些字段。但是，如果一个bean有一个对应的公共 \"\n\"``setter``/``getter`` 方法并用于一个用 ``@ConfigField`` \"\n\"注释的字段（方法参数/返回类型匹配字段类型），那么配置器将使用它们而不是通过反射访问字段。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:93\nmsgid \"\"\n\"If there is no value for a field specified in the configuration or value \"\n\"is equal to the current value of the field, then configurator will skip \"\n\"setting value for this field (It will also not call ``setter`` method \"\n\"even if it exists).\"\nmsgstr \"如果配置中指定的字段没有值或值等于该字段的当前值，则配置器将跳过该字段的设置值（即使存在也不会调用 ``setter`` 方法） 。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:95\nmsgid \"\"\n\"At the end of the configuration step, if bean implements \"\n\"``ConfigurationChangedAware`` interface, then method \"\n\"``beanConfigurationChanged(Collection<String> changedFields)`` is being \"\n\"called, to notify bean about field names which values has changed. This \"\n\"is useful, if you need to update bean configuration, when you have all \"\n\"configuration available inside bean.\"\nmsgstr \"\"\n\"在配置步骤结束时，如果bean实现了 ``ConfigurationChangedAware`` 接口，则调用 \"\n\"``beanConfigurationChanged(Collection<String> changedFields)`` \"\n\"方法以通知bean字段名称哪些值已更改。如果您需要更新bean配置，当您在bean中拥有所有可用配置时，这很有用。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:99\nmsgid \"\"\n\"Configuration of the bean may be changed at runtime and it will be \"\n\"applied in the same way as initial configuration is passed to the bean. \"\n\"So please keep in mind that ``getter``/``setter`` may be called multiple \"\n\"times - even for already configured and initialized bean.\"\nmsgstr \"\"\n\"bean的配置可以在运行时更改，它将以与传递给bean的初始配置相同的方式被应用。所以请记住，``getter``/``setter`` \"\n\"可能会被多次调用 - 即使对于已经配置和初始化的bean。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:104\nmsgid \"Injecting dependencies\"\nmsgstr \"注入依赖\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:106\nmsgid \"\"\n\"At this point kernel looks for the bean class fields annotated with \"\n\"``@Inject`` and looks for a value for each of this fields. During this \"\n\"step, kernel checks list of available beans in this kernel, which matches\"\n\" field type and additional constraints specified in the annotation.\"\nmsgstr \"\"\n\"此时，内核查找带有 ``@Inject`` \"\n\"注释的bean类字段，并为每个字段查找一个值。在此步骤中，内核检查此内核中可用bean的列表，该列表与注释中指定的字段类型和附加约束相匹配。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:108\nmsgid \"\"\n\"When a required value (instance of a bean) is found, then kernel tries to\"\n\" inject it using reflection. However, if there is a matching \"\n\"``getter``/``setter`` defined for that field it will be called instead of\"\n\" reflection.\"\nmsgstr \"\"\n\"当找到所需的值（bean的实例）时，内核会尝试使用反射将其注入。但是，如果为该字段定义了匹配的 \"\n\"``getter``/``setter``，它将被调用而不是反射。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:112\nmsgid \"\"\n\"If dependency changes, ie. due to reconfiguration, then value of the \"\n\"dependent field will change and ``setter`` will be called if it exists. \"\n\"So please keep in mind that ``getter``/``setter`` may be called multiple \"\n\"times - even for already configured and initialized bean.\"\nmsgstr \"\"\n\"如果依赖关系发生变化，即由于重新配置，依赖字段的值将发生变化，如果存在，将调用 ``setter`` \"\n\"。所以请记住，``getter``/``setter`` 可能会被多次调用 - 即使对于已经配置和初始化的bean。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:115\nmsgid \"Initialization of a bean\"\nmsgstr \"一个bean的初始化\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:117\nmsgid \"\"\n\"When bean is configured and dependencies are set, then initialization of \"\n\"a bean is almost finished. At this point, if bean implements \"\n\"``Initializable`` interface, kernel calls ``initialize()`` method to \"\n\"allow bean initialize properly if needed.\"\nmsgstr \"\"\n\"当配置了bean并设置了依赖关系时，一个bean的初始化就差不多完成了。此时，如果bean实现了 ``Initializable`` \"\n\"接口，内核会调用 ``initialize()`` 方法来允许bean在需要时正确初始化。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:120\nmsgid \"Destroying instance of a bean\"\nmsgstr \"销毁bean的实例\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:122\nmsgid \"\"\n\"When bean is being unloaded, then reference to its instance is just \"\n\"dropped. However, if bean class implements ``UnregisterAware`` interface,\"\n\" then kernel calls ``beforeUnregister()`` method. This is very useful in \"\n\"case which bean acquires some resources during initialization and should \"\n\"release them now.\"\nmsgstr \"\"\n\"当bean被卸载时，对它的实例的引用就被删除了。但是，如果bean类实现了 ``UnregisterAware`` 接口，那么内核会调用 \"\n\"``beforeUnregister()`` 方法。如果哪个bean在初始化期间获取了一些资源并应该立即释放它们，这非常有用。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:126\nmsgid \"\"\n\"This method will not be called if bean was not initialized fully (bean \"\n\"initialization step was note passed)!\"\nmsgstr \"如果bean没有完全初始化（bean初始化步骤已通过），则不会调用此方法！\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:129\nmsgid \"Reconfiguration of a bean *(optional)*\"\nmsgstr \"重新配置一个bean *（可选）*\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:131\nmsgid \"\"\n\"At any point in time bean may be reconfigured by default bean \"\n\"configurator (instance of ``BeanConfigurator``) registered in the kernel.\"\n\" This will happen in the same way as it described in :ref:`Configuring a \"\n\"bean<beanConfiguration>` in **Creating instace of a bean** section.\"\nmsgstr \"\"\n\"在任何时间点，bean都可能被内核中注册的默认bean配置器（ ``BeanConfigurator`` \"\n\"的实例）重新配置。这将以与 **创建 bean的实例** 部分中的 \"\n\":ref:`配置bean<beanConfiguration>` 所描述的相同方式发生。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:134\nmsgid \"Updating dependencies\"\nmsgstr \"更新依赖项\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:136\nmsgid \"\"\n\"It may happen, that due to reconfiguration or registration/unregistration\"\n\" or activation/deactivation of some other beans dependencies of a bean \"\n\"will change. As a result, Tigase Kernel will inject new dependencies as \"\n\"described in :ref:`Injecting dependencies<beanInjectingDependencies>`\"\nmsgstr \"\"\n\"由于重新配置或注册/取消注册或激活/停用某些其他bean，bean的依赖关系可能会发生\"\n\"变化。因此，Tigase内核将注入新的依赖项，如 :ref:`Injecting \"\n\"dependencies<beanInjectingDependencies>` 中所述\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:139\nmsgid \"Registration of a bean\"\nmsgstr \"一个bean的注册\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:141\nmsgid \"There are few ways to register a bean.\"\nmsgstr \"注册bean的方法很少。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:144\nmsgid \"Using annotation *(recommended but optional)*\"\nmsgstr \"使用注释 *（推荐但可选）*\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:146\nmsgid \"\"\n\"To register a bean using annotation you need to annotate it with \"\n\"``@Bean`` annotation and pass values for following properties:\"\nmsgstr \"要使用注解注册bean，您需要使用 ``@Bean`` 对其进行注解，并为以下属性传递值：\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:148\nmsgid \"``name`` - name under which item should be registered\"\nmsgstr \"``name`` - 应在哪个项目下注册名称\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:150\nmsgid \"\"\n\"``active`` - ``true`` if bean should be enabled without enabling it in \"\n\"the configuration *(however it is still possible to disable it using \"\n\"configuration)*\"\nmsgstr \"``active`` - ``true`` 如果bean应该被启用而不在配置中启用它 *（但是仍然可以使用配置禁用它）*\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:152\nmsgid \"\"\n\"``parent`` - class of the parent bean which activation should trigger \"\n\"registration of your bean. **In most cases parent class should be \"\n\"implementing ``RegistrarBean``**\"\nmsgstr \"``parent`` - 激活应该触发bean注册的父bean的类。 **在大多数情况下，父类应该实现 `RegistrarBean`**\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:154\nmsgid \"\"\n\"``parents`` - array of classes which should be threaten as ``parent`` \"\n\"classes if more than one parent class is required *(optional)*\"\nmsgstr \"``parents`` - 如果需要多个父类，则应作为 ``parent`` 类受到威胁的类数组 *（可选）*\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:156\nmsgid \"\"\n\"``exportable`` - ``true`` if bean should be visible in all descendant \"\n\"kernels (in other case default visibility rules will be applied) \"\n\"*(optional)*\"\nmsgstr \"``exportable`` - ``true`` 如果bean应该在所有后代内核中可见（在其他情况下，将应用默认可见性规则） *（可选）*\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:158\nmsgid \"\"\n\"``selectors`` - array of selector classes which will decide whether class\"\n\" should be registered or not *(optional)*\"\nmsgstr \"``selectors`` - 选择器类的数组，将决定是否应该注册类 *（可选）*\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:162\nmsgid \"\"\n\"If ``parent`` is set to ``Kernel.class`` it tells kernel to register this\"\n\" bean in the root/main kernel (top-level kernel).\"\nmsgstr \"如果 ``parent`` 设置为 ``Kernel.class`` 它告诉内核在根/主内核（顶级内核）中注册这个bean。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:164\nmsgid \"\"\n\"If you want your bean ``SomeDependencyBean`` to be registered when \"\n\"another bean ``ParentBean`` is being registered (like a required \"\n\"dependency), you may annotate your bean ``SomeDependencyBean`` with \"\n\"``@Bean`` annotation like this example:\"\nmsgstr \"\"\n\"如果你希望你的bean ``SomeDependencyBean`` 在另一个 bean ``ParentBean`` \"\n\"正在注册时被注册（就像一个必需的依赖项），你可以用 ``@Bean`` 注释来注释你的 bean \"\n\"``SomeDependencyBean``，正如这个例子：\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:175\nmsgid \"\"\n\"Works only if bean registered as ``defaultBeanConfigurator`` supports \"\n\"this feature. By default Tigase XMPP Server uses ``DSLBeanConfigurator`` \"\n\"which is subclass of ``AbstractBeanConfigurator`` which provides support \"\n\"for this feature.\"\nmsgstr \"\"\n\"仅当注册为 ``defaultBeanConfigurator`` 的bean支持此功能时才有效。默认情况下，Tigase XMPP服务器使用 \"\n\"``DSLBeanConfigurator``，它是 ``AbstractBeanConfigurator`` 的子类，它提供了对这个特性的支持。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:178\nmsgid \"Setting ``parent`` to class not implementing ``RegistrarBean`` interface\"\nmsgstr \"将 ``parent`` 设置为不实现 ``RegistrarBean`` 接口的类\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:180\nmsgid \"\"\n\"If ``parent`` is set to the class which is not implementing \"\n\"``RegistrarBean`` interface, then your bean will be registered in the \"\n\"same kernel scope in which parent bean is registered. If you do so, ie. \"\n\"by setting parent to the class of the bean which is registered in the \"\n\"``kernel1`` and your bean will be also registered in ``kernel1``. As the \"\n\"result it will be exposed to other beans in the same kernel scope. This \"\n\"also means that if you will configure it in the same way as you would set\"\n\" ``parent`` to the ``parent`` of annotation of the class to which your \"\n\"``parent`` point to.\"\nmsgstr \"\"\n\"如果 ``parent`` 设置为没有实现 ``RegistrarBean`` \"\n\"接口的类，那么你的bean将被注册到父bean注册的同一个内核范围内。如果你这样做，即通过将parent设置为在 ``kernel1`` \"\n\"中注册的bean的类，您的bean也将在 ``kernel1`` 中注册。结果，它将暴露给同一内核范围内的其他bean。这也意味着，你是否将以与 \"\n\"``parent`` 设置为 ``parent`` 指向的类的注释的 ``parent`` 相同的方式配置它。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:182\n#: ../../Tigase_Development/Tigase_Kernel.rst:387\nmsgid \"**Example.**\"\nmsgstr \"**示例。**\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:200\nmsgid \"\"\n\"In this case it means that ``bean1`` is registered in the root/main \"\n\"kernel instance. At the same time, ``bean2`` is also registered to the \"\n\"root/main kernel as its value of ``parent`` property of annotation points\"\n\" to class not implementing ``RegistrarBean``.\"\nmsgstr \"\"\n\"在这种情况下，这意味着 ``bean1`` 已在根/主内核实例中注册。同时，``bean2`` 也被注册到根/主内核，因为它的 \"\n\"``parent`` 注释属性的值指向没有实现 ``RegistrarBean`` 的类。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:202\nmsgid \"\"\n\"To configure value of ``field1`` in instance of ``bean1`` and ``field2`` \"\n\"in instance of ``bean2`` in DSL (for more information about DSL format \"\n\"please check section ``DSL file format`` of the ``Admin Guide``) you \"\n\"would need to use following entry in the config file:\"\nmsgstr \"\"\n\"要在DSL中配置 ``bean1`` 实例中的 ``field1`` 和 ``bean2`` 实例中的 ``field2`` 的值（有 \"\n\"DSL格式的更多信息，请查看 ``DSL file format`` 中的 ``Admin Guide``），您需要在配置文件中使用以下条目：\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:213\nmsgid \"\"\n\"As you can see, this resulted in the ``bean2`` configuration being on the\"\n\" same level as ``bean1`` configuration.\"\nmsgstr \"如您所见，这导致 ``bean2`` 配置与 ``bean1`` 配置处于同一级别。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:216\nmsgid \"Calling kernel methods\"\nmsgstr \"调用内核方法\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:219\nmsgid \"As a class\"\nmsgstr \"作为一个类\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:221\nmsgid \"\"\n\"To register a bean as a class, you need to have an instance of a Tigase \"\n\"Kernel execute it’s ``registerBean()`` method passing your ``Bean1`` \"\n\"class.\"\nmsgstr \"要将bean注册为类，您需要让Tigase内核的实例执行它的 ``registerBean()`` 方法，并传递您的 ``Bean1`` 类。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:229\nmsgid \"\"\n\"To be able to use this method you will need to annotate ``Bean1`` class \"\n\"with ``@Bean`` annotation and provide a bean name which will be used for \"\n\"registration of the bean.\"\nmsgstr \"为了能够使用此方法，您需要使用 ``@Bean`` 注释来注释 ``Bean1`` 类，并提供将用于注册bean的bean名称。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:232\nmsgid \"As a factory\"\nmsgstr \"作为工厂\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:234\nmsgid \"\"\n\"To do this you need to have an instance of a Tigase Kernel execute it’s \"\n\"``registerBean()`` method passing your bean ``Bean5`` class.\"\nmsgstr \"为此，您需要让Tigase内核的一个实例执行它的 ``registerBean()`` 方法，并传递您的 bean ``Bean5`` 类。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:241\nmsgid \"As an instance\"\nmsgstr \"作为一个实例\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:243\nmsgid \"\"\n\"For this you need to have an instance of a Tigase Kernel execute it’s \"\n\"``registerBean()`` method passing your bean ``Bean41`` class instance.\"\nmsgstr \"为此，您需要让Tigase内核的实例执行它的 ``registerBean()`` 方法，并传递您的 bean ``Bean41`` 类实例。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:252\nmsgid \"\"\n\"Beans registered as an instance will not inject dependencies. As well \"\n\"this bean instances will not be configured by provided bean \"\n\"configurators.\"\nmsgstr \"注册为实例的Bean不会注入依赖项。同样，提供的bean配置器也不会配置此bean实例。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:255\nmsgid \"Using config file *(optional)*\"\nmsgstr \"使用配置文件 *（可选）*\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:257\nmsgid \"\"\n\"If there is registered a bean ``defaultBeanConfigurator`` which supports \"\n\"registration in the config file, it is possible to do so. By default \"\n\"Tigase XMPP Server uses ``DSLBeanConfigurator`` which provides support \"\n\"for that and registration is possible in the config file in DSL. As \"\n\"registration of beans using a config file is part of the admin of the \"\n\"Tigase XMPP Server tasks, it is described in explained in the Admin Guide\"\n\" in subsection ``Defining bean`` of ``DSL file format`` section.\"\nmsgstr \"\"\n\"如果在配置文件中注册了一个支持注册的bean ``defaultBeanConfigurator``，则可以这样做。默认情况下，Tigase \"\n\"XMPP服务器使用 ``DSLBeanConfigurator`` 提供支持，并且可以在DSL的配置文件中注册。由于使用配置文件注册bean是 \"\n\"Tigase XMPP服务器任务管理的一部分，因此在管理指南中 ``DSL file format`` 部分的 ``Defining bean``\"\n\" 小节中进行了说明。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:261\nmsgid \"\"\n\"This way allows admin to select different class for a bean. This option \"\n\"should be used to provide alternative implementations to the default \"\n\"beans which should be registered using annotations.\"\nmsgstr \"这种方式允许管理员为bean选择不同的类。此选项应该被用于为应使用注释注册的默认bean提供替代实现。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:265\nmsgid \"\"\n\"Works only if bean registered as ``defaultBeanConfigurator`` supports \"\n\"this feature. By default Tigase XMPP Server uses ``DSLBeanConfigurator`` \"\n\"which provides support for that.\"\nmsgstr \"\"\n\"仅当注册为 ``defaultBeanConfigurato`` 的bean支持此功能时才有效。默认情况下，Tigase XMPP服务器使用 \"\n\"``DSLBeanConfigurator`` 来提供支持。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:268\nmsgid \"Defining dependencies\"\nmsgstr \"定义依赖项\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:270\nmsgid \"All dependencies are defined with annotations:\"\nmsgstr \"所有依赖项都使用注释定义：\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:294\nmsgid \"\"\n\"Kernel automatically determines type of a required beans based on field \"\n\"type. As a result, there is no need to specify the type of a bean in case\"\n\" of ``bean4`` field.\"\nmsgstr \"内核根据字段类型自动确定所需bean的类型。因此，在 ``bean4`` 字段的情况下，无需指定bean的类型。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:296\nmsgid \"\"\n\"When there are more than one bean instances matching required dependency \"\n\"fields, the type needs to be an array or collection. If kernel is unable \"\n\"to resolve dependencies, it will throw an exception unless ``@Inject`` \"\n\"annotation has ``nullAllowed`` set to ``true``. This is useful to make \"\n\"some dependencies optional. To help kernel select a single bean instance \"\n\"when more that one bean will match field dependency, you may set name of \"\n\"a required bean as shown in annotation to field ``bean3``.\"\nmsgstr \"\"\n\"当有多个bean实例匹配所需的依赖字段时，类型需要是数组或集合。如果内核无法解析依赖关系，它将抛出异常，除非 ``@Inject`` 注解将 \"\n\"``nullAllowed`` 设置为 \"\n\"``true``。这对于使某些依赖项可选的很有用。当多个bean匹配字段依赖时，为了帮助内核选择单个bean实例，您可以将所需bean的名称设置为字段\"\n\" ``bean3``，如注释中所示。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:298\nmsgid \"\"\n\"Dependencies are inserted using getters/setters if those methods exist, \"\n\"otherwise they are inserted directly to the fields. Thanks to usage of \"\n\"setters, it is possible to detect a change of dependency instance and \"\n\"react as required, i.e. clear internal cache.\"\nmsgstr \"如果存在这些方法，则使用getter/setter插入依赖项，否则将它们直接插入到字段中。由于使用了设置器，可以检测依赖实例的变化并根据需要做出反应，即清除内部缓存。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:302\nmsgid \"\"\n\"Kernel is resolving dependencies during injection only using beans \"\n\"visible in its scope. This makes it unable to inject an instance of a \"\n\"class which is not registered in the same kernel as a bean or not visible\"\n\" in this kernel scope (see :ref:`Scope and visibility<kernelScope>`).\"\nmsgstr \"\"\n\"内核在注入期间仅使用其范围内可见的bean来解决依赖关系。这使得它无法注入未在同\"\n\"一内核中注册为bean或在此内核范围内不可见的类的实例（请参阅 :ref:`Scope and \"\n\"visibility<kernelScope>`）。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:306\nmsgid \"\"\n\"If two beans have bidirectional dependencies, then it is required to \"\n\"allow at least one of them be ``null`` (make it an optional dependency). \"\n\"In other case it will create circular dependency which cannot be \"\n\"satisfied and kernel will throw exceptions at runtime.\"\nmsgstr \"\"\n\"如果两个bean具有双向依赖关系，则要求其中至少一个为 ``null`` \"\n\"（使其成为可选依赖关系）。在其他情况下，它将创建无法满足的循环依赖，并且内核将在运行时抛出异常。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:309\nmsgid \"Nested kernels and exported beans\"\nmsgstr \"嵌套内核和导出的bean\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:311\nmsgid \"\"\n\"Tigase Kernel allows the usage of nested kernels. This allows you to \"\n\"create complex applications and maintain proper separation and visibility\"\n\" of beans in scopes as each module (subkernel) may work within its own \"\n\"scope.\"\nmsgstr \"Tigase内核允许使用嵌套内核。这允许您创建复杂的应用程序并在范围内保持适当的bean分离和可见性，因为每个模块（子内核）都可以在其自己的范围内工作。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:313\nmsgid \"Subkernels may be created using one of two ways:\"\nmsgstr \"可以使用以下两种方式之一创建子内核：\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:316\nmsgid \"Manual registration of new a new kernel\"\nmsgstr \"手动注册新内核\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:318\nmsgid \"\"\n\"You can create an instance of a new kernel and register it as a bean \"\n\"within the parent kernel.\"\nmsgstr \"您可以创建一个新内核的实例并将其注册为父内核中的bean。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:329\nmsgid \"Usage of RegistrarBean\"\nmsgstr \"RegistrarBean的使用\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:331\nmsgid \"\"\n\"You may create a bean which implements the ``RegistrarBean`` interfaces. \"\n\"For all beans that implement this interface, subkernels are created. You \"\n\"can access this new kernel within an instance of ``RegistrarBean`` class \"\n\"as ``register(Kernel)`` and ``unregister(Kernel)`` methods are called \"\n\"once the ``RegistrarBean`` instance is created or destroyed.\"\nmsgstr \"\"\n\"你可以创建一个实现 ``RegistrarBean`` 接口的bean。对于所有实现此接口的bean，都会创建子内核。您可以在 \"\n\"``RegistrarBean`` 类的实例中访问这个新内核，因为一旦 ``RegistrarBean`` \"\n\"实例被创建或销毁，``register(Kernel)`` 和 ``unregister(Kernel)`` 方法就会被调用。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:333\nmsgid \"\"\n\"There is also an interface named ``RegistrarBeanWithDefaultBeanClass``. \"\n\"This interface is very useful if you want or need to create a bean which \"\n\"would allow you to configure many subbeans which will have the same class\"\n\" but different names and you do not know names of those beans before \"\n\"configuration will be set. All you need to do is to implement this \"\n\"interface and in method ``getDefaultBeanClass()`` return class which \"\n\"should be used for all subbeans defined in configuration for which there \"\n\"will be no class configured.\"\nmsgstr \"\"\n\"还有一个名为 ``RegistrarBeanWithDefaultBeanClass`` \"\n\"的接口。如果您想要或需要创建一个bean，该bean将允许您配置许多具有相同类但名称不同的子bean，并且在设置配置之前您不知道这些bean的名称，则此接口非常有用。您需要做的就是实现这个接口，并在方法\"\n\" ``getDefaultBeanClass()`` 中返回类，该类应该用于配置中定义的所有子bean，不会为其配置类。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:335\nmsgid \"\"\n\"As an example of such use case is ``dataSource`` bean, which allows \"\n\"administrator to easily configure many data sources without passing their\"\n\" class names, ie.\"\nmsgstr \"作为这种用例的一个例子是 ``dataSource`` bean，它允许管理员轻松配置许多数据源而无需传递它们的类名，即。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:345\nmsgid \"\"\n\"With this config we just defined 3 beans named ``default``, ``domain1`` \"\n\"and ``domain2``. All of those beans will be instances of a class returned\"\n\" by a ``getDefaultBeanClass()`` method of ``dataSource`` bean.\"\nmsgstr \"\"\n\"通过这个配置，我们只定义了3个名为 ``default``, ``domain1`` 和 ``domain2`` \"\n\"的bean。所有这些bean都将是由 ``dataSource`` bean 的 ``getDefaultBeanClass()`` \"\n\"方法返回的类的实例。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:350\nmsgid \"Scope and visibility\"\nmsgstr \"范围和可见性\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:352\nmsgid \"\"\n\"Beans that are registered within a parent kernel are visible to beans \"\n\"registered within the first level of child kernels. However, **beans \"\n\"registered within child kernels are not available to beans registered in \"\n\"a parent kernel** with the exception that they are visible to bean that \"\n\"created the subkernel (an instance of ``RegistrarBean``).\"\nmsgstr \"\"\n\"在父内核中注册的bean对在第一级子内核中注册的bean可见。但是，**在子内核中注册的bean对在父内核中注册的bean不可用**，除非它们对创建子内核的bean可见（\"\n\" ``RegistrarBean`` 的实例）。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:354\nmsgid \"\"\n\"It is possible to export beans so they can be visible outside the first \"\n\"level of child kernels.\"\nmsgstr \"可以导出beans，以便它们可以在第一级子内核之外可见。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:356\nmsgid \"\"\n\"To do so, you need to mark the bean as exportable using annotations or by\"\n\" calling the ``exportable()`` method.\"\nmsgstr \"为此，您需要使用注释或调用 ``exportable()`` 方法将bean标记为可导出。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:358\nmsgid \"**Using annotation.**\"\nmsgstr \"**使用注释。**\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:366\nmsgid \"*Calling* ``exportable()``.\"\nmsgstr \"*调用* ``exportable()``。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:373\nmsgid \"Dependency graph\"\nmsgstr \"依赖图\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:375\nmsgid \"\"\n\"Kernel allows the creation of a dependency graph. The following lines \"\n\"will generate it in a format supported by `Graphviz \"\n\"<http://www.graphviz.org>`__.\"\nmsgstr \"内核允许创建依赖图。以下行将以 `Graphviz <http://www.graphviz.org>`__ 支持的格式生成它。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:383\nmsgid \"Configuration\"\nmsgstr \"配置\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:385\nmsgid \"\"\n\"The kernel core does not provide any way to configure created beans. Do \"\n\"do that you need to use the ``DSLBeanConfigurator`` class by providing \"\n\"its instance within configuration and registration of this instances \"\n\"within kernel.\"\nmsgstr \"\"\n\"内核核心不提供任何方式来配置创建的bean。这样做你需要通过在配置中提供它的实例并在内核中注册这个实例来使用 \"\n\"``DSLBeanConfigurator`` 类。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:400\nmsgid \"DSL and kernel scopes\"\nmsgstr \"DSL和内核范围\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:402\nmsgid \"\"\n\"DSL is a structure based format explained in `Tigase XMPP Server \"\n\"Administration Guide: DSL file format section <http://docs.tigase.org\"\n\"/tigase-server/snapshot/Administration_Guide/html/#dslConfig>`__. **It is\"\n\" important to know that kernel and beans structure have an impact on what\"\n\" the configuration in DSL will look like.**\"\nmsgstr \"\"\n\"DSL是 `Tigase XMPP服务器管理指南：DSL文件格式部分 <http://docs.tigase.org/tigase-\"\n\"server/snapshot/Administration_Guide/html/#dslConfig>`__ 中解释的基于结构的格式。 \"\n\"**重要的是要知道内核和bean结构会影响DSL中的配置。**\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:404\nmsgid \"**Example kernel and beans classes.**\"\nmsgstr \"**示例内核和bean类。**\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:458\nmsgid \"Following classes will produce following structure of beans:\"\nmsgstr \"以下类将产生以下bean结构：\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:460\nmsgid \"\\\"bean1\\\" of class ``Bean1``\"\nmsgstr \"类 ``Bean1`` 中的 \\\"bean1\\\"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:462\nmsgid \"\\\"bean1_1\\\" of class ``Bean11``\"\nmsgstr \"类 ``Bean11`` 中的 \\\"bean1_1\\\"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:464\nmsgid \"\\\"bean1_2\\\" of class ``Bean12``\"\nmsgstr \"类 ``Bean12`` 中的 \\\"bean1_2\\\"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:466\nmsgid \"\\\"bean4\\\" of class ``Bean2``\"\nmsgstr \"类 ``Bean2`` 中的 \\\"bean4\\\"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:468\nmsgid \"\\\"bean3\\\" of class ``Bean3``\"\nmsgstr \"类 ``Bean3`` 中的 \\\"bean3\\\"\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:472\nmsgid \"\"\n\"This is a simplified structure, the actual structure is slightly more \"\n\"complex. However. this version makes it easier to explain structure of \"\n\"beans and impact on configuration file structure.\"\nmsgstr \"这是一个简化的结构，实际结构稍微复杂一些。然而，这个版本更容易解释bean的结构和对配置文件结构的影响。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:476\nmsgid \"\"\n\"Even though ``Bean2`` was annotated with name ``bean2``, it was \"\n\"registered with name ``bean4`` as this name was passed during \"\n\"registration of a bean in ``main()`` method.\"\nmsgstr \"\"\n\"尽管 ``Bean2`` 使用名称 ``bean2`` 进行了注解，但它是使用名称 ``bean4`` 注册的，因为该名称是在bean注册过程中在\"\n\" ``main()`` 方法中传递的。\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:480\nmsgid \"\"\n\"``Bean12`` was registered under name ``bean1_2`` as subbean of ``Bean1`` \"\n\"as a result of annotation of ``Bean12``\"\nmsgstr \"由于 ``Bean12`` 的注释，``Bean12`` 被注册为 ``Bean1`` 的子bean，名为 ``bean1_2``\"\n\n#: ../../Tigase_Development/Tigase_Kernel.rst:482\nmsgid \"\"\n\"As mentioned DSL file structure depends on structure of beans, a file to \"\n\"set a config field in each bean to bean name should look like that:\"\nmsgstr \"如前所述，DSL文件结构取决于bean的结构，将每个bean中的配置字段设置 bean名称的文件应如下所示：\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Development/Using_Maven.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-05-27 12:30-0700\\n\"\n\"PO-Revision-Date: 2022-09-07 17:14+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/dg-using_maven/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.8.0\\n\"\n\n#: ../../Tigase_Development/Using_Maven.rst:2\nmsgid \"Using Maven\"\nmsgstr \"使用Maven\"\n\n#: ../../Tigase_Development/Using_Maven.rst:4\nmsgid \"Documents Describing Maven Use with the Tigase Projects\"\nmsgstr \"文档描述Maven在Tigase项目中的使用\"\n\n#: ../../Tigase_Development/Using_Maven.rst:7\nmsgid \"Setting up Maven in Windows\"\nmsgstr \"在Windows中设置Maven\"\n\n#: ../../Tigase_Development/Using_Maven.rst:9\nmsgid \"\"\n\"Here at Tigase, we employ Apache Maven to download latest builds, compile\"\n\" codes for export, and check for errors in the code during build. This \"\n\"guide will go over installing and running Maven from a Windows operating \"\n\"environment. We will consider windows versions 7, 8, and 8.1 for this \"\n\"guide. Because Maven does not come with an installer, there is a manual \"\n\"install process which might be a bit daunting for the new user, but setup\"\n\" and use is fairly simple.\"\nmsgstr \"\"\n\"在Tigase，我们使用Apache \"\n\"Maven下载最新版本，编译导出代码，并在构建期间检查代码中的错误。\"\n\"本指南将介绍从 Windows操作环境安装和运行Maven。\"\n\"我们将在本指南中考虑Windows版本 7、8 和 8.1。因为Maven不附带安装程序，所以有\"\n\"一个手动安装过程，这对新用户来说可能有点令人生畏，但设置和使用相当简单。\"\n\n#: ../../Tigase_Development/Using_Maven.rst:12\nmsgid \"Requirements\"\nmsgstr \"要求\"\n\n#: ../../Tigase_Development/Using_Maven.rst:14\nmsgid \"\"\n\"Maven requires Java Development Kit (JDK) 6 or later. As Tigase requires \"\n\"the latest JDK to run, that will do for our purposes. If you haven’t \"\n\"installed it yet, download the installer from `this website \"\n\"<http://www.oracle.com/technetwork/java/javase/downloads/index.html>`__. \"\n\"Once you install JDK and restart your machine, be sure that you have the \"\n\"**JAVA_HOME** variable entered into Environment Variables so calls to \"\n\"Java will work from the command line.\"\nmsgstr \"\"\n\"Maven需要Java开发工具包(JDK) 6或更高版本。由于Tigase需要最新的JDK才能运行，这\"\n\"将满足我们的目的。如果您还没有安装它，请从 `此网站 <http://www.oracle.com/\"\n\"technetwork/java/javase/downloads/index.html>`__ \"\n\"下载安装程序。安装JDK并重新启动计算机后，请确保已将 **JAVA_HOME** \"\n\"变量输入到环境变量中，以便从命令行调用Java。\"\n\n#: ../../Tigase_Development/Using_Maven.rst:16\nmsgid \"\"\n\"Download the Maven package from `here \"\n\"<https://maven.apache.org/download.cgi>`__ and unpack it into a directory\"\n\" of your choice. For this guide we will use ``C:\\\\Maven\\\\`` .\"\nmsgstr \"\"\n\"从 `这里 <https://maven.apache.org/download.cgi>`__ \"\n\"下载Maven包并将其解压到您选择的目录中。对于本指南，我们将使用 ``C:\\\\Maven\\\\``\"\n\" 。\"\n\n#: ../../Tigase_Development/Using_Maven.rst:19\nmsgid \"Setting up Environment Variables\"\nmsgstr \"设置环境变量\"\n\n#: ../../Tigase_Development/Using_Maven.rst:21\nmsgid \"\"\n\"The Environment Variables panel is brought up from the Control Panel by \"\n\"clicking **System and Security** > **System** > **Advanced System \"\n\"Settings**. Now click the |Environment Variables| button at the bottom of\"\n\" the panel and the Environment Variables panel will show.\"\nmsgstr \"\"\n\"通过单击 **System and Security** > **System** > **Advanced System Settings** \"\n\"从控制面板中调出环境变量面板。现在单击 |Environment Variables| \"\n\"面板底部的按钮，将显示环境变量面板。\"\n\n#: ../../Tigase_Development/Using_Maven.rst:70\nmsgid \"Environment Variables\"\nmsgstr \"环境变量\"\n\n#: ../../Tigase_Development/Using_Maven.rst:23\nmsgid \"\"\n\"**IMPORTANT NOTICE: CHANGING THESE SETTINGS CAN BREAK OTHER FUNCTIONS IN \"\n\"THE OPERATING SYSTEM. DO NOT FOLLOW THIS GUIDE IF YOU DO NOT KNOW WHAT \"\n\"YOU ARE DOING!**\"\nmsgstr \"**重要提示：更改这些设置可能会破坏操作系统中的其他功能。如果您不知道自己在做\"\n\"什么，请勿遵循本指南！**\"\n\n#: ../../Tigase_Development/Using_Maven.rst:25\nmsgid \"|Env Panel|\"\nmsgstr \"|Env Panel|\"\n\n#: ../../Tigase_Development/Using_Maven.rst:71\nmsgid \"Env Panel\"\nmsgstr \"Env Panel\"\n\n#: ../../Tigase_Development/Using_Maven.rst:27\nmsgid \"\"\n\"We need to first add two variable paths to the System variables to \"\n\"account for Maven’s install location. As there are some programs that \"\n\"look for M2_HOME, and others that look for MAVEN_HOME, it’s easier to \"\n\"just add both and have all the bases covered.\"\nmsgstr \"\"\n\"我们需要首先向系统变量添加两个变量路径，以说明Maven的安装位置。由于有些程序会\"\n\"查找M2_HOME，而其他程序会查找 \"\n\"MAVEN_HOME，因此只需添加两者并覆盖所有基础就更容易了。\"\n\n#: ../../Tigase_Development/Using_Maven.rst:29\nmsgid \"Click on New…​\"\nmsgstr \"点击新建…​\"\n\n#: ../../Tigase_Development/Using_Maven.rst:31\nmsgid \"|Env New|\"\nmsgstr \"|Env New|\"\n\n#: ../../Tigase_Development/Using_Maven.rst:72\nmsgid \"Env New\"\nmsgstr \"Env New\"\n\n#: ../../Tigase_Development/Using_Maven.rst:33\nmsgid \"\"\n\"For the Name, use M2_HOME, and for the variable enter the path to maven, \"\n\"which in this case is\"\nmsgstr \"对于名称，使用M2_HOME，对于变量，输入maven的路径，在本例中为\"\n\n#: ../../Tigase_Development/Using_Maven.rst:39\nmsgid \"\"\n\"Create another new variable with the MAVEN_HOME name and add the same \"\n\"directory. **These variable values just point to where you have unpacked \"\n\"maven, so they do not have to be in the C directory.**\"\nmsgstr \"\"\n\"使用MAVEN_HOME名称创建另一个新变量并添加相同的目录。 \"\n\"**这些变量值只是指向您解压maven的位置，因此它们不必必须位于C目录中。**\"\n\n#: ../../Tigase_Development/Using_Maven.rst:41\nmsgid \"\"\n\"Go down to the system variables dialog and select Path, then click on \"\n\"Edit. The Path variables are separated by semicolons, find the end of the\"\n\" Variable value string, and add the following after the last entry:\"\nmsgstr \"转到系统变量对话框并选择路径，然后单击编辑。 Path变量用分号隔开，\"\n\"找到Variable value字符串的结尾，在最后一项后添加以下内容：\"\n\n#: ../../Tigase_Development/Using_Maven.rst:47\n#, python-format\nmsgid \"\"\n\"We have added two variables using the %% wildcards surrounding our \"\n\"Variable names from earlier.\"\nmsgstr \"我们使用前面的变量名称周围的 %% 通配符添加了两个变量。\"\n\n#: ../../Tigase_Development/Using_Maven.rst:50\nmsgid \"Testing Maven\"\nmsgstr \"测试Maven\"\n\n#: ../../Tigase_Development/Using_Maven.rst:52\nmsgid \"\"\n\"Now we must test the command line to be sure everything installed \"\n\"correctly. Bring up the command line either by typing ``cmd`` in search, \"\n\"or navigating the start menu.\"\nmsgstr \"现在我们必须测试命令行以确保一切都正确安装。通过在搜索中键入 ``cmd`` \"\n\"或导航开始菜单来调出命令行。\"\n\n#: ../../Tigase_Development/Using_Maven.rst:54\nmsgid \"\"\n\"From the prompt, you do not need to change directory as setting Path \"\n\"allows you to reference it. Type the following command: ``mvn -v``\"\nmsgstr \"在提示符下，您不需要更改目录，因为设置Path允许您引用它。输入以下命令：``mvn \"\n\"-v``\"\n\n#: ../../Tigase_Development/Using_Maven.rst:56\nmsgid \"something like this should show up\"\nmsgstr \"应该出现这样的信息\"\n\n#: ../../Tigase_Development/Using_Maven.rst:68\nmsgid \"\"\n\"If you see this message, success! You have finished installation and are \"\n\"ready to use Maven! If not, go back on your settings and insure that JDK \"\n\"is installed, and the JAVA_HOME, M2_HOME, and MAVEN_HOME variables are \"\n\"set properly.\"\nmsgstr \"\"\n\"如果您看到此消息，则成功！您已完成安装并准备好使用Maven！如果没有，请返回您的\"\n\"设置并确保已安装JDK，并且正确设置了JAVA_HOME、M2_HOME和MAVEN_HOME变量。\"\n\n#: ../../Tigase_Development/Using_Maven.rst:75\nmsgid \"A Very Short Maven Guide\"\nmsgstr \"一个非常简短的Maven指南\"\n\n#: ../../Tigase_Development/Using_Maven.rst:77\nmsgid \"\"\n\"If you don’t use `Maven <http://maven.apache.org/>`__ at all or use it \"\n\"once a year you may find the document a useful maven commands reminder:\"\nmsgstr \"\"\n\"如果你根本不使用 `Maven <http://maven.apache.org/>`__ \"\n\"或者每年使用一次，你可能会发现文档是一个有用的maven命令提醒：\"\n\n#: ../../Tigase_Development/Using_Maven.rst:80\nmsgid \"Snapshot Compilation and Snapshot Package Generation\"\nmsgstr \"快照编译和快照包生成\"\n\n#: ../../Tigase_Development/Using_Maven.rst:82\nmsgid \"``mvn compile`` - compilation of the snapshot package\"\nmsgstr \"``mvn compile`` - 快照包的编译\"\n\n#: ../../Tigase_Development/Using_Maven.rst:84\nmsgid \"``mvn package`` - create snapshot jar file\"\nmsgstr \"``mvn package`` - 创建快照jar文件\"\n\n#: ../../Tigase_Development/Using_Maven.rst:86\nmsgid \"``mvn install`` - install in local repository snapshot jar file\"\nmsgstr \"``mvn install`` - 安装在本地存储库快照jar文件中\"\n\n#: ../../Tigase_Development/Using_Maven.rst:88\nmsgid \"``mvn deploy`` - deploy to the remote repository snapshot jar file\"\nmsgstr \"``mvn deploy`` - 部署到远程仓库快照jar文件\"\n\n#: ../../Tigase_Development/Using_Maven.rst:91\nmsgid \"Release Compilation, Generation\"\nmsgstr \"发布编译，生成\"\n\n#: ../../Tigase_Development/Using_Maven.rst:93\nmsgid \"``mvn release:prepare`` prepare the project for a new version release\"\nmsgstr \"``mvn release:prepare`` 为新版本发布准备项目\"\n\n#: ../../Tigase_Development/Using_Maven.rst:95\nmsgid \"``mvn release:perform`` execute new version release generation\"\nmsgstr \"``mvn release:perform`` 执行新版本的发布生成\"\n\n#: ../../Tigase_Development/Using_Maven.rst:98\nmsgid \"Generating tar.gz, tar.bz2 File With Sources Only\"\nmsgstr \"仅使用源生成tar.gz、tar.bz2文件\"\n\n#: ../../Tigase_Development/Using_Maven.rst:100\nmsgid \"``mvn -DdescriptorId=src assembly:assembly``\"\nmsgstr \"``mvn -DdescriptorId=src assembly:assembly``\"\n\n#: ../../Tigase_Development/Using_Maven.rst:102\nmsgid \"\"\n\"Any of these commands will work when your commandline is in a directory \"\n\"with a pom.xml file. This file will instruct what Maven will do.\"\nmsgstr \"当您的命令行位于带有pom.xml文件的目录中时，这些命令中的任何一个都将起作用。该\"\n\"文件将指示Maven将做什么。\"\n\n#: ../../Tigase_Development/Using_Maven.rst:105\nmsgid \"Profiles\"\nmsgstr \"配置\"\n\n#: ../../Tigase_Development/Using_Maven.rst:107\nmsgid \"\"\n\"Maven uses profiles with the -P switch to tell what to compile and build.\"\n\" Tigase uses two different profiles:\"\nmsgstr \"Maven使用带有 -P 开关的配置文件来告诉编译和构建什么。 \"\n\"Tigase使用两种不同的配置文件：\"\n\n#: ../../Tigase_Development/Using_Maven.rst:109\nmsgid \"-Pdist - creates distribution archives\"\nmsgstr \"-Pdist - 创建分发档案\"\n\n#: ../../Tigase_Development/Using_Maven.rst:111\nmsgid \"-Pdoc - creates documentation\"\nmsgstr \"-Pdoc - 创建文档\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/Tigase_Development/index.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2023-02-16 15:01-0800\\n\"\n\"PO-Revision-Date: 2023-02-17 04:51+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/development_guide_index/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.11.0\\n\"\n\n#: ../../Tigase_Development/index.rst:3\nmsgid \"Development Guide\"\nmsgstr \"开发指南\"\n\n#~ msgid \"Tigase Development Guide -Version 8.3.0\"\n#~ msgstr \"Tigase开发指南 - 版本 8.3.0\"\n"
  },
  {
    "path": "src/main/restructured/locale/zh_CN/LC_MESSAGES/index.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2004-2022, Tigase, Inc\n# This file is distributed under the same license as the TigaseDoc package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: TigaseDoc\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2023-02-22 15:39-0800\\n\"\n\"PO-Revision-Date: 2023-02-25 01:30+0000\\n\"\n\"Last-Translator: Qian Luo <qian.luo@tigase.net>\\n\"\n\"Language-Team: Chinese (Simplified) <http://translate.tigase.net/projects/\"\n\"tigase-xmpp-server/index/zh_Hans/>\\n\"\n\"Language: zh_CN\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=1; plural=0;\\n\"\n\"X-Generator: Weblate 4.11.2\\n\"\n\"Generated-By: Babel 2.11.0\\n\"\n\n#: ../../index.rst:68\nmsgid \"Administration Guide\"\nmsgstr \"管理文档\"\n\n#: ../../index.rst:75\nmsgid \"Development Guide\"\nmsgstr \"开发指南\"\n\n#: ../../index.rst:4\nmsgid \"Tigase Documentation\"\nmsgstr \"Tigase 文档\"\n\n#: ../../index.rst:7\nmsgid \"Tigase Projects documentations\"\nmsgstr \"Tigase 项目文档\"\n\n#: ../../index.rst:10\nmsgid \"Server Components\"\nmsgstr \"服务器组件\"\n\n#: ../../index.rst:12\nmsgid \"\"\n\"`Tigase MIX <../../projects/tigase-tigase-mix/en/latest/>`__ Next \"\n\"generation groupchat protocol\"\nmsgstr \"`Tigase MIX <../../projects/tigase-tigase-mix/en/latest/>`__ 下一代群聊协议\"\n\n#: ../../index.rst:13\nmsgid \"\"\n\"`Tigase MUC <../../projects/tigase-tigase-muc/en/latest/>`__ Current \"\n\"generation groupchat protocol\"\nmsgstr \"`Tigase MUC <../../projects/tigase-tigase-muc/en/latest/>`__ 当前一代群聊协议\"\n\n#: ../../index.rst:14\nmsgid \"\"\n\"`Tigase Spam <../../projects/tigase-tigase-spam/en/latest/>`__ Project \"\n\"aiming at fighting spam\"\nmsgstr \"`Tigase Spam <../../projects/tigase-tigase-spam/en/latest/>`__ 旨在打击垃圾邮件的项目\"\n\n#: ../../index.rst:15\nmsgid \"\"\n\"`Tigase HTTP API <../../projects/tigase-tigase-http-api/en/latest/>`__ \"\n\"Operate and manage Tigase via HTTP... and more!\"\nmsgstr \"\"\n\"`Tigase HTTP API <../../projects/tigase-tigase-http-api/en/latest/>`__ 通过\"\n\" HTTP 操作和管理 Tigase……等等！\"\n\n#: ../../index.rst:16\nmsgid \"\"\n\"`Tigase PubSub <../../projects/tigase-tigase-pubsub/en/latest/>`__ \"\n\"Implementation of XEP-0045: PubSub protocol\"\nmsgstr \"\"\n\"`Tigase PubSub <../../projects/tigase-tigase-pubsub/en/latest/>`__ \"\n\"XEP-0045 的实现：PubSub 协议\"\n\n#: ../../index.rst:17\nmsgid \"\"\n\"`Tigase Message Archiving <../../projects/tigase-tigase-message-\"\n\"archiving/en/latest/>`__ Implementation of XEP-0136: PMessage archiving\"\nmsgstr \"\"\n\"`Tigase Message Archiving <../../projects/tigase-tigase-message-\"\n\"archiving/en/latest/>`__ XEP-0136 的实现：Message 归档\"\n\n#: ../../index.rst:18\nmsgid \"\"\n\"`Tigase Unified Archive <../../projects/tigase-tigase-unified-\"\n\"archive/en/latest/>`__ The extended version of Tigase Message Archiving \"\n\"Component\"\nmsgstr \"\"\n\"`Tigase Unified Archive <../../projects/tigase-tigase-unified-\"\n\"archive/en/latest/>`__ Tigase 消息归档组件的扩展版本\"\n\n#: ../../index.rst:19\nmsgid \"\"\n\"`Tigase Socks5 <../../projects/tigase-socks5/en/latest/>`__ \"\n\"Implementation of XEP-0065 SOCKS5 Bytestreams\"\nmsgstr \"\"\n\"`Tigase Socks5 <../../projects/tigase-socks5/en/latest/>`__ XEP-0065 \"\n\"SOCKS5 字节流的实现\"\n\n#: ../../index.rst:20\nmsgid \"\"\n\"`Tigase Push <../../projects/tigase-tigase-push-2/en/latest/>`__ A \"\n\"gateway between Push Notification services and XMPP servers\"\nmsgstr \"\"\n\"`Tigase Push <../../projects/tigase-tigase-push-2/en/latest/>`__ 推送通知服务和 \"\n\"XMPP 服务器之间的网关\"\n\n#: ../../index.rst:21\nmsgid \"\"\n\"`Tigase ACS <../../projects/tigase-tigase-acs-2/en/latest/>`__ General \"\n\"purpose, commercial clustering strategy\"\nmsgstr \"`Tigase ACS <../../projects/tigase-tigase-acs-2/en/latest/>`__ 通用、商业集群策略\"\n\n#: ../../index.rst:22\nmsgid \"\"\n\"`Tigase Auditlog <../../projects/tigase-tigase-auditlog-2/en/latest/>`__ \"\n\"An implementation of Audit Log pattern functionality to log important \"\n\"events\"\nmsgstr \"\"\n\"`Tigase Auditlog <../../projects/tigase-tigase-auditlog-2/en/latest/>`__ \"\n\"审计日志模式功能的实现，用于记录重要事件\"\n\n#: ../../index.rst:23\nmsgid \"\"\n\"`Tigase Workgroup Queues <../../projects/tigase-tigase-workgroup-\"\n\"queues/en/latest/>`__ Implementation of XEP-0142: Workgroup Queues\"\nmsgstr \"\"\n\"`Tigase Workgroup Queues <../../projects/tigase-tigase-workgroup-\"\n\"queues/en/latest/>`__ XEP-0142 的实现：工作组队列\"\n\n#: ../../index.rst:24\nmsgid \"\"\n\"`Tigase Databse Migrator <../../projects/tigase-database-\"\n\"migrator/en/latest/>`__ Component allowing migration of different types \"\n\"of data from various XMPP servers\"\nmsgstr \"\"\n\"`Tigase Databse Migrator <../../projects/tigase-database-\"\n\"migrator/en/latest/>`__ 组件允许从各种 XMPP 服务器迁移不同类型的数据\"\n\n#: ../../index.rst:25\nmsgid \"\"\n\"`Tigase Extras <../../projects/tigase-tigase-extras/en/latest/>`__ AWS \"\n\"provides you with support for additional features and integrations with \"\n\"Amazon AWS.\"\nmsgstr \"\"\n\"`Tigase Extras <../../projects/tigase-tigase-extras/en/latest/>`__ \"\n\"为您提供附加功能支持以及与 Amazon AWS 的集成。\"\n\n#: ../../index.rst:28\nmsgid \"XMPP Clients\"\nmsgstr \"XMPP 客户端\"\n\n#: ../../index.rst:30\nmsgid \"`SiskinIM <../../projects/tigase-siskin-im/en/latest/>`__ iOS XMPP Client\"\nmsgstr \"`SiskinIM <../../projects/tigase-siskin-im/en/latest/>`__ iOS XMPP 客户端\"\n\n#: ../../index.rst:32\nmsgid \"\"\n\"`BeagleIM <../../projects/tigase-beagle-im/en/latest/>`__ macOS XMPP \"\n\"Client\"\nmsgstr \"`BeagleIM <../../projects/tigase-beagle-im/en/latest/>`__ macOS XMPP 客户端\"\n\n#: ../../index.rst:34\nmsgid \"`StorkIM <../../projects/tigase-stork/en/latest/>`__ android XMPP Client\"\nmsgstr \"`StorkIM <../../projects/tigase-stork/en/latest/>`__ android XMPP 客户端\"\n\n#: ../../index.rst:37\nmsgid \"XMPP Libraries\"\nmsgstr \"XMPP 库\"\n\n#: ../../index.rst:39\nmsgid \"\"\n\"`Martin <../../projects/tigase-tigase-swift/en/latest/>`__ Swift Lang \"\n\"XMPP Library\"\nmsgstr \"\"\n\"`Martin <../../projects/tigase-tigase-swift/en/latest/>`__ Swift Lang \"\n\"XMPP 库\"\n\n#: ../../index.rst:41\nmsgid \"\"\n\"`Halcyon <../../projects/tigase-halcyon/en/latest/>`__ a multiplatform, \"\n\"extensible XMPP client library\"\nmsgstr \"\"\n\"`Halcyon <../../projects/tigase-halcyon/en/latest/>`__ 一个多平台、可扩展的 XMPP \"\n\"客户端库\"\n\n#: ../../index.rst:43\nmsgid \"\"\n\"`Tigase XML Tools <../../projects/tigase-xmltools/en/latest/>`__ A \"\n\"library providing support for working with XML documents\"\nmsgstr \"\"\n\"`Tigase XML Tools <../../projects/tigase-xmltools/en/latest/>`__ 一个为处理 \"\n\"XML 文档提供支持的库\"\n\n#: ../../index.rst:45\nmsgid \"\"\n\"`Tigase JaXMPP <../../projects/tigase-jaxmpp/en/latest/>`__ an extensible\"\n\" XMPP client library\"\nmsgstr \"\"\n\"`Tigase JaXMPP <../../projects/tigase-jaxmpp/en/latest/>`__ 一个可扩展的 XMPP \"\n\"客户端库\"\n\n#: ../../index.rst:49\nmsgid \"Javadoc\"\nmsgstr \"Javadoc\"\n\n#: ../../index.rst:51\nmsgid \"\"\n\"`Tigase Mongodb <../../projects/tigase-tigase-mongodb/en/latest/>`__ Java\"\n\" documentation for tigase mangodb\"\nmsgstr \"\"\n\"`Tigase Mongodb <../../projects/tigase-tigase-mongodb/en/latest/>`__ \"\n\"tigase mangodb 的 Java 文档\"\n\n#: ../../index.rst:53\nmsgid \"\"\n\"`Tigase Utils <../../projects/tigase-tigase-utils/en/latest/>`__ Java \"\n\"documentation for tigase utils\"\nmsgstr \"\"\n\"`Tigase Utils <../../projects/tigase-tigase-utils/en/latest/>`__ tigase \"\n\"实用程序的 Java 文档\"\n\n#: ../../index.rst:56\nmsgid \"Miscellaneous\"\nmsgstr \"杂项\"\n\n#: ../../index.rst:58\nmsgid \"\"\n\"`Tigase TTS NG <../../projects/tigase-tigase-tts-ng/en/latest/>`__ \"\n\"Project intended to run automated funtionality tests\"\nmsgstr \"\"\n\"`Tigase TTS NG <../../projects/tigase-tigase-tts-ng/en/latest/>`__ \"\n\"旨在运行自动化功能测试的项目\"\n\n#: ../../index.rst:60\nmsgid \"\"\n\"`Tigase XEPs <https://xeps.tigase.net/>`__ XMPP extensions that we Tigase\"\n\" is working on with the goal to transition them to full, standard XMPP \"\n\"extensions.\"\nmsgstr \"\"\n\"`Tigase XEP <https://xeps.tigase.net/>`__ 我们 Tigase 正在开发的 XMPP \"\n\"扩展，目标是将它们转换为完整的标准 XMPP 扩展。\"\n\n#: ../../index.rst:64\nmsgid \"Legacy documentation website\"\nmsgstr \"旧文档网站\"\n\n#: ../../index.rst:66\nmsgid \"\"\n\"`Legacy documentation website <https://docs-legacy.tigase.net/>`__ Our \"\n\"legacy documentation website which contains documentation for older \"\n\"versions of componetns and applications\"\nmsgstr \"\"\n\"`Legacy documentation website <https://docs-legacy.tigase.net/>`__ \"\n\"我们的旧文档网站，其中包含旧版本的组件和应用程序的文档\"\n\n#~ msgid \"\"\n#~ \"`SiskinIM <../../projects/tigase-siskinim/en/latest/>`__\"\n#~ \" iOS XMPP Client\"\n#~ msgstr \"\"\n\n#~ msgid \"Tigase XMPP Server Distribution Administration Guide\"\n#~ msgstr \"Tigase XMPP 服务器发行管理指南\"\n"
  },
  {
    "path": "src/main/restructured/make.bat",
    "content": "@REM\r\n@REM Tigase XMPP Server - The instant messaging server\r\n@REM Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\r\n@REM\r\n@REM This program is free software: you can redistribute it and/or modify\r\n@REM it under the terms of the GNU Affero General Public License as published by\r\n@REM the Free Software Foundation, version 3 of the License.\r\n@REM\r\n@REM This program is distributed in the hope that it will be useful,\r\n@REM but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n@REM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r\n@REM GNU Affero General Public License for more details.\r\n@REM\r\n@REM You should have received a copy of the GNU Affero General Public License\r\n@REM along with this program. Look for COPYING file in the top folder.\r\n@REM If not, see http://www.gnu.org/licenses/.\r\n@REM\r\n\r\n@ECHO OFF\r\n\r\npushd %~dp0\r\n\r\nREM Command file for Sphinx documentation\r\n\r\nif \"%SPHINXBUILD%\" == \"\" (\r\n\tset SPHINXBUILD=sphinx-build\r\n)\r\nset SOURCEDIR=source\r\nset BUILDDIR=build\r\n\r\nif \"%1\" == \"\" goto help\r\n\r\n%SPHINXBUILD% >NUL 2>NUL\r\nif errorlevel 9009 (\r\n\techo.\r\n\techo.The 'sphinx-build' command was not found. Make sure you have Sphinx\r\n\techo.installed, then set the SPHINXBUILD environment variable to point\r\n\techo.to the full path of the 'sphinx-build' executable. Alternatively you\r\n\techo.may add the Sphinx directory to PATH.\r\n\techo.\r\n\techo.If you don't have Sphinx installed, grab it from\r\n\techo.http://sphinx-doc.org/\r\n\texit /b 1\r\n)\r\n\r\n%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%\r\ngoto end\r\n\r\n:help\r\n%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%\r\n\r\n:end\r\npopd\r\n"
  },
  {
    "path": "src/main/restructured/readme.md",
    "content": "# Documentation is in ReStructuredText format\n\nCouple of useful links:\n* https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html\n* https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html\n\nHeadings priority/order:\n\n* `========` - for 1\n* `--------` - for 1.1\n* `^^^^^^^^` - for 1.1.1\n* `~~~~~~~~` - for 1.1.1.1 (to keep consistent you previous documents, it will only show highlight title without 1.1.1.1)\n* `''''''''` - for 1.1.1.1.1 (to keep consistent you previous documents, it will only show highlight title without 1.1.1.1.1)\n\n# Configuration of ReadTheDocs and legacy documentation\n\nPrevious documentation in AsciiDoc was moved from https://docs.tigase.net to https://docs-legacy.tigase.net ([CloudFront configuration: `E1OF45YJ7KFOUO` / d2jb0rmum93y88.cloudfront.net](https://us-east-1.console.aws.amazon.com/cloudfront/v3/home?region=us-east-1&skipRegion=true#/distributions/E1OF45YJ7KFOUO)).\n\nIn order to maintain documentation in working state [redirections were configured in ReadTheDocs](https://readthedocs.com/dashboard/tigase-tigase-server/redirects/) for all available projects\n\nEach time documentation is converted and migrated from AsciiDoc to ReadTheDocs it **MUST** be included in the [`index.rst`](./index.rst) file!"
  },
  {
    "path": "src/main/restructured/requirements.txt",
    "content": "sphinx\nsphinx_rtd_theme"
  },
  {
    "path": "src/main/ruby/tigase/admin/RubyExample.rb",
    "content": "#\n# Tigase XMPP Server - The instant messaging server\n# Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU Affero General Public License as published by\n# the Free Software Foundation, version 3 of the License.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Affero General Public License for more details.\n#\n# You should have received a copy of the GNU Affero General Public License\n# along with this program. Look for COPYING file in the top folder.\n# If not, see http://www.gnu.org/licenses/.\n#\n\nrequire 'java'\n\nimport java.lang.System\n\ninclude_class 'tigase.server.Command'\ninclude_class 'tigase.server.Packet'\n\n\nnum1 = Command.getFieldValue($packet, \"num1\")\nnum2 = Command.getFieldValue($packet, \"num2\")\n\nif num1.nil? || num2.nil?\n   res = Packet.commandResultForm($packet)\n   Command.addTextField(res, \"Note\", \"This is JRuby script!\")\n\t Command.addFieldValue(res, \"num1\", \"\")\n   Command.addFieldValue(res, \"num2\", \"\")\n   return res\nelse\n  return num1 + num2\nend\n"
  },
  {
    "path": "src/main/scala/tigase/admin/ScalaExample.scala",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.admin\n\nval num1 = Command getFieldValue (packet, \"num1\")\nval num2 = Command getFieldValue (packet, \"num2\")\n\nscriptResult = if (num1 == null || num2 == null) {\n  val cmd = Packet.commandResultForm (packet)\n  Command.addTextField (cmd, \"Note\", \"This is a Scala script!\")\n  Command.addFieldValue (cmd, \"num1\", \"\")\n  Command.addFieldValue (cmd, \"num2\", \"\")\n  cmd\n} else {\n  num1 + num2\n}\n"
  },
  {
    "path": "src/test/java/tigase/Assert.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase;\n\nimport tigase.xml.Element;\n\nimport java.util.IdentityHashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\nimport static org.junit.Assert.assertTrue;\n\n/**\n * Class implementing assertions for custom classes.\n * <br>\n * Created by andrzej on 04.01.2017.\n */\npublic class Assert {\n\n\t/**\n\t * Method compares if actual element matches expected one.\n\t * <br>\n\t * Warning: Actual element must have attributes and children which are part of expected element, however may contain\n\t * addition elements or attributes and assertion will not fail.\n\t *\n\t */\n\tpublic static void assertElementEquals(Element expected, Element actual) {\n\t\tassertElementEquals(\"\", expected, actual);\n\t}\n\n\tpublic static void assertElementEquals(String message, Element expected, Element actual) {\n\t\tassertTrue(message + \": expected: \" + expected + \" but was:\" + actual, equals(expected, actual));\n\t}\n\n\tpublic static boolean equals(Element expected, Element actual) {\n\t\tif (expected.getName() != actual.getName()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tMap<String, String> expAttributes = expected.getAttributes();\n\t\tif (expAttributes == null) {\n\t\t\texpAttributes = new IdentityHashMap<>();\n\t\t}\n\t\tMap<String, String> actAttributes = actual.getAttributes();\n\t\tif (actAttributes == null) {\n\t\t\tactAttributes = new IdentityHashMap<>();\n\t\t} else {\n\t\t\tactAttributes = new IdentityHashMap<>(actAttributes);\n\t\t\tIterator<String> it = actAttributes.keySet().iterator();\n\t\t\twhile (it.hasNext()) {\n\t\t\t\tString key = it.next();\n\t\t\t\tif (!expAttributes.containsKey(key)) {\n\t\t\t\t\tit.remove();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!expAttributes.equals(actAttributes)) {\n\t\t\treturn false;\n\t\t}\n\t\tList<Element> expChildren = expected.getChildren();\n\t\tif (expChildren != null) {\n\t\t\tif (!expChildren.stream()\n\t\t\t\t\t.allMatch(expChild -> actual.findChild(actChild -> equals(expChild, actChild)) != null)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/TestLogger.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase;\n\nimport tigase.util.log.LogFormatter;\n\nimport java.util.Arrays;\nimport java.util.logging.ConsoleHandler;\nimport java.util.logging.Handler;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class TestLogger {\n\n\tpublic static void configureLogger(Logger log, Level level) {\n\t\tlog.setUseParentHandlers(false);\n\t\tlog.setLevel(level);\n\t\tfinal Handler[] handlers = log.getHandlers();\n\t\tif (Arrays.stream(handlers).noneMatch(ConsoleHandler.class::isInstance)) {\n\t\t\tConsoleHandler ch = new ConsoleHandler();\n\t\t\tch.setLevel(level);\n\t\t\tch.setFormatter(new LogFormatter());\n\t\t\tlog.addHandler(ch);\n\t\t}\n\t\tfor (Handler logHandler : handlers) {\n\t\t\tlogHandler.setLevel(level);\n\t\t}\n\t}\n\n\tpublic static Logger getLogger(Class clazz) {\n\t\treturn Logger.getLogger(\"test.\" + clazz.getName());\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/auth/BruteForceLockerBeanTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport tigase.eventbus.impl.EventBusSerializer;\nimport tigase.kernel.DefaultTypesConverter;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.HashMap;\n\npublic class BruteForceLockerBeanTest {\n\n\t@Test\n\tpublic void testKeyValueSerialization() {\n\t\tfinal BruteForceLockerBean.Key k1 = new BruteForceLockerBean.Key(\"1.2.3.4\", \"a@b.c\", \"c.d\");\n\t\tfinal BruteForceLockerBean.Value v1 = new BruteForceLockerBean.Value(\"c.d\", \"1.2.3.4\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t BareJID.bareJIDInstanceNS(\"a@b.c\"));\n\t\tv1.setBadLoginCounter(412);\n\t\tv1.setInvalidateAtTime(8352);\n\n\t\tDefaultTypesConverter converter = new DefaultTypesConverter();\n\n\t\tString k1s = converter.toString(k1);\n\t\tString v1s = converter.toString(v1);\n\n\t\tfinal BruteForceLockerBean.Key k2 = converter.convert(k1s, k1.getClass());\n\t\tfinal BruteForceLockerBean.Value v2 = converter.convert(v1s, v1.getClass());\n\n\t\tAssert.assertEquals(\"1.2.3.4\", k2.getIp());\n\t\tAssert.assertEquals(\"a@b.c\", k2.getJid());\n\t\tAssert.assertEquals(k1, k2);\n\n\t\tAssert.assertEquals(412, v2.getBadLoginCounter());\n\t\tAssert.assertEquals(v1.getBadLoginCounter(), v2.getBadLoginCounter());\n\n\t\tAssert.assertEquals(8352, v2.getInvalidateAtTime());\n\t\tAssert.assertEquals(v1.getInvalidateAtTime(), v2.getInvalidateAtTime());\n\n\t}\n\n\t@Test\n\tpublic void testStatsSerializer() {\n\t\tBruteForceLockerBean.StatHolder sh1 = new BruteForceLockerBean.StatHolder();\n\t\tsh1.addIP(\"1.2.3.4\");\n\t\tsh1.addIP(\"1.2.3.4\");\n\t\tsh1.addIP(\"1.2.3.5\");\n\t\tsh1.addIP(\"2.2.3.4\");\n\t\tsh1.addIP(\"3.2.3.4\");\n\t\tsh1.addIP(\"4.2.3.4\");\n\t\tsh1.addIP(\"5.2.3.4\");\n\t\tsh1.addIP(\"6.2.3.4\");\n\t\tsh1.addIP(\"7.2.3.4\");\n\t\tsh1.addIP(\"8.2.3.4\");\n\t\tsh1.addIP(\"9.2.3.4\");\n\t\tsh1.addIP(\"10.2.3.4\");\n\t\tsh1.addJID(BareJID.bareJIDInstanceNS(\"a@b.c\"));\n\t\tsh1.addJID(BareJID.bareJIDInstanceNS(\"b@b.c\"));\n\t\tsh1.addJID(BareJID.bareJIDInstanceNS(\"c@b.c\"));\n\t\tsh1.addJID(BareJID.bareJIDInstanceNS(\"c@b.c\"));\n\t\tsh1.addJID(BareJID.bareJIDInstanceNS(\"c@b.c\"));\n\n\t\tString[] parcel = sh1.encodeToStrings();\n\n\t\tBruteForceLockerBean.StatHolder sh2 = new BruteForceLockerBean.StatHolder();\n\n\t\tsh2.fillFromString(parcel);\n\n\t\tAssert.assertEquals(sh1.getIps().size(), sh2.getIps().size());\n\t\tAssert.assertEquals(sh1.getJids().size(), sh2.getJids().size());\n\n\t\tAssert.assertEquals(11, sh2.getIps().size());\n\t\tAssert.assertEquals(3, sh2.getJids().size());\n\n\t\tsh1.getIps().forEach((ip, count) -> Assert.assertEquals(count, sh2.getIps().get(ip)));\n\t\tsh1.getJids().forEach((jid, count) -> Assert.assertEquals(count, sh2.getJids().get(jid)));\n\t}\n\n\t@Test\n\tpublic void testStatsSerializer_empty() {\n\t\tBruteForceLockerBean.StatHolder sh1 = new BruteForceLockerBean.StatHolder();\n\t\tString[] parcel = sh1.encodeToStrings();\n\n\t\tBruteForceLockerBean.StatHolder sh2 = new BruteForceLockerBean.StatHolder();\n\t\tsh2.fillFromString(parcel);\n\n\t\tAssert.assertEquals(sh1.getIps().size(), sh2.getIps().size());\n\t\tAssert.assertEquals(sh1.getJids().size(), sh2.getJids().size());\n\n\t\tAssert.assertEquals(0, sh2.getIps().size());\n\t\tAssert.assertEquals(0, sh2.getJids().size());\n\n\t\tsh1.getIps().forEach((ip, count) -> Assert.assertEquals(count, sh2.getIps().get(ip)));\n\t\tsh1.getJids().forEach((jid, count) -> Assert.assertEquals(count, sh2.getJids().get(jid)));\n\t}\n\n\t@Test\n\tpublic void testStatsSerializer_noips() {\n\t\tBruteForceLockerBean.StatHolder sh1 = new BruteForceLockerBean.StatHolder();\n\t\tsh1.addJID(BareJID.bareJIDInstanceNS(\"a@b.c\"));\n\t\tsh1.addJID(BareJID.bareJIDInstanceNS(\"b@b.c\"));\n\t\tString[] parcel = sh1.encodeToStrings();\n\n\t\tBruteForceLockerBean.StatHolder sh2 = new BruteForceLockerBean.StatHolder();\n\t\tsh2.fillFromString(parcel);\n\n\t\tAssert.assertEquals(sh1.getIps().size(), sh2.getIps().size());\n\t\tAssert.assertEquals(sh1.getJids().size(), sh2.getJids().size());\n\n\t\tAssert.assertEquals(0, sh2.getIps().size());\n\t\tAssert.assertEquals(2, sh2.getJids().size());\n\n\t\tsh1.getIps().forEach((ip, count) -> Assert.assertEquals(count, sh2.getIps().get(ip)));\n\t\tsh1.getJids().forEach((jid, count) -> Assert.assertEquals(count, sh2.getJids().get(jid)));\n\t}\n\n\t@Test\n\tpublic void testStatsSerializer_nojids() {\n\t\tBruteForceLockerBean.StatHolder sh1 = new BruteForceLockerBean.StatHolder();\n\t\tsh1.addIP(\"1.2.3.4\");\n\t\tsh1.addIP(\"1.2.3.4\");\n\t\tsh1.addIP(\"1.2.3.5\");\n\t\tsh1.addIP(\"2.2.3.4\");\n\t\tsh1.addIP(\"3.2.3.4\");\n\t\tsh1.addIP(\"4.2.3.4\");\n\t\tString[] parcel = sh1.encodeToStrings();\n\n\t\tBruteForceLockerBean.StatHolder sh2 = new BruteForceLockerBean.StatHolder();\n\t\tsh2.fillFromString(parcel);\n\n\t\tAssert.assertEquals(sh1.getIps().size(), sh2.getIps().size());\n\t\tAssert.assertEquals(sh1.getJids().size(), sh2.getJids().size());\n\n\t\tAssert.assertEquals(5, sh2.getIps().size());\n\t\tAssert.assertEquals(0, sh2.getJids().size());\n\n\t\tsh1.getIps().forEach((ip, count) -> Assert.assertEquals(count, sh2.getIps().get(ip)));\n\t\tsh1.getJids().forEach((jid, count) -> Assert.assertEquals(count, sh2.getJids().get(jid)));\n\t}\n\n\t@Test\n\tpublic void test3InvalidLoginsAndWait() {\n\t\tBruteForceLockerBean bean = new BruteForceLockerBean();\n\t\tbean.setMap(new HashMap<>());\n\t\tbean.clearAll();\n\n\t\tbean.addInvalidLogin(null, \"1.2.3.4\", BareJID.bareJIDInstanceNS(\"a@bc.d\"), 100000);\n\t\tAssert.assertTrue(bean.isLoginAllowed(null, \"1.2.3.4\", BareJID.bareJIDInstanceNS(\"a@bc.d\"), 100001));\n\t\tbean.addInvalidLogin(null, \"1.2.3.4\", BareJID.bareJIDInstanceNS(\"a@bc.d\"), 100002);\n\t\tAssert.assertTrue(bean.isLoginAllowed(null, \"1.2.3.4\", BareJID.bareJIDInstanceNS(\"a@bc.d\"), 100003));\n\t\tbean.addInvalidLogin(null, \"1.2.3.4\", BareJID.bareJIDInstanceNS(\"a@bc.d\"), 100004);\n\t\tAssert.assertTrue(bean.isLoginAllowed(null, \"1.2.3.4\", BareJID.bareJIDInstanceNS(\"a@bc.d\"), 100005));\n\n\t\t//  invalid login after lock time\n\t\tbean.addInvalidLogin(null, \"1.2.3.4\", BareJID.bareJIDInstanceNS(\"a@bc.d\"), 100006 + 10_000);\n\t\tAssert.assertTrue(bean.isLoginAllowed(null, \"1.2.3.4\", BareJID.bareJIDInstanceNS(\"a@bc.d\"), 100007));\n\t}\n\n\t@Test\n\tpublic void testEventSerialization() {\n\t\tBruteForceLockerBean.StatHolder holder = new BruteForceLockerBean.StatHolder();\n\t\tholder.addIP(\"1.2.3.4\");\n\t\tholder.addIP(\"1.2.3.4\");\n\t\tholder.addIP(\"1.2.3.5\");\n\t\tholder.addIP(\"2.2.3.4\");\n\t\tholder.addIP(\"3.2.3.4\");\n\t\tholder.addIP(\"4.2.3.4\");\n\t\tholder.addIP(\"5.2.3.4\");\n\t\tholder.addIP(\"6.2.3.4\");\n\t\tholder.addIP(\"7.2.3.4\");\n\t\tholder.addIP(\"8.2.3.4\");\n\t\tholder.addIP(\"9.2.3.4\");\n\t\tholder.addIP(\"10.2.3.4\");\n\t\tholder.addJID(BareJID.bareJIDInstanceNS(\"a@b.c\"));\n\t\tholder.addJID(BareJID.bareJIDInstanceNS(\"b@b.c\"));\n\t\tholder.addJID(BareJID.bareJIDInstanceNS(\"c@b.c\"));\n\t\tholder.addJID(BareJID.bareJIDInstanceNS(\"c@b.c\"));\n\t\tholder.addJID(BareJID.bareJIDInstanceNS(\"c@b.c\"));\n\n\t\tBruteForceLockerBean.StatisticsEmitEvent event1 = new BruteForceLockerBean.StatisticsEmitEvent();\n\t\tevent1.setNodeName(\"name-node-123\");\n\t\tevent1.setStatHolder(holder);\n\n\t\tfinal EventBusSerializer serializer = new EventBusSerializer();\n\t\tElement element = serializer.serialize(event1);\n\t\tBruteForceLockerBean.StatisticsEmitEvent event2 = serializer.deserialize(element);\n\n\t\tAssert.assertEquals(event2.getNodeName(), \"name-node-123\");\n\t\tAssert.assertEquals(event1.getNodeName(), event2.getNodeName());\n\n\t\tAssert.assertEquals(event1.getStatHolder().getIps().size(), event2.getStatHolder().getIps().size());\n\t\tAssert.assertEquals(event1.getStatHolder().getJids().size(), event2.getStatHolder().getJids().size());\n\n\t\tevent1.getStatHolder()\n\t\t\t\t.getIps()\n\t\t\t\t.forEach((ip, count) -> Assert.assertEquals(count, event2.getStatHolder().getIps().get(ip)));\n\t\tevent1.getStatHolder()\n\t\t\t\t.getJids()\n\t\t\t\t.forEach((jid, count) -> Assert.assertEquals(count, event2.getStatHolder().getJids().get(jid)));\n\t}\n\n\t@Test\n\tpublic void test4InvalidLogins() {\n\t\tBruteForceLockerBean bean = new BruteForceLockerBean();\n\t\tbean.setMap(new HashMap<>());\n\t\tbean.clearAll();\n\n\t\tAssert.assertTrue(bean.isLoginAllowed(null, \"1.2.3.4\", BareJID.bareJIDInstanceNS(\"a@bc.d\"), 99999));\n\t\tbean.addInvalidLogin(null, \"1.2.3.4\", BareJID.bareJIDInstanceNS(\"a@bc.d\"), 100000);\n\t\tAssert.assertTrue(bean.isLoginAllowed(null, \"1.2.3.4\", BareJID.bareJIDInstanceNS(\"a@bc.d\"), 100001));\n\t\tbean.addInvalidLogin(null, \"1.2.3.4\", BareJID.bareJIDInstanceNS(\"a@bc.d\"), 100002);\n\t\tAssert.assertTrue(bean.isLoginAllowed(null, \"1.2.3.4\", BareJID.bareJIDInstanceNS(\"a@bc.d\"), 100003));\n\t\tbean.addInvalidLogin(null, \"1.2.3.4\", BareJID.bareJIDInstanceNS(\"a@bc.d\"), 100004);\n\t\tAssert.assertTrue(bean.isLoginAllowed(null, \"1.2.3.4\", BareJID.bareJIDInstanceNS(\"a@bc.d\"), 100005));\n\n\t\t// one invalid login too much\n\t\tbean.addInvalidLogin(null, \"1.2.3.4\", BareJID.bareJIDInstanceNS(\"a@bc.d\"), 100006);\n\t\tAssert.assertFalse(bean.isLoginAllowed(null, \"1.2.3.4\", BareJID.bareJIDInstanceNS(\"a@bc.d\"), 100007));\n\t\t// allowed from different IP\n\t\tAssert.assertTrue(bean.isLoginAllowed(null, \"1.2.3.5\", BareJID.bareJIDInstanceNS(\"a@bc.d\"), 100007));\n\t\t// allowed for different JID\n\t\tAssert.assertTrue(bean.isLoginAllowed(null, \"1.2.3.4\", BareJID.bareJIDInstanceNS(\"b@bc.d\"), 100007));\n\n\t\t// try after lock time\n\t\tAssert.assertTrue(bean.isLoginAllowed(null, \"1.2.3.4\", BareJID.bareJIDInstanceNS(\"a@bc.d\"), 100007 + 10_000));\n\n\t}\n\n}"
  },
  {
    "path": "src/test/java/tigase/auth/credentials/entries/MD5PasswordCredentialsEntryTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.credentials.entries;\n\nimport org.junit.Test;\nimport tigase.xmpp.jid.BareJID;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\npublic class MD5PasswordCredentialsEntryTest {\n\n\t@Test\n\tpublic void testDecodingOfStoredValue() {\n\t\tString testPassword = \"some-password-do-protect\";\n\t\tBareJID user = BareJID.bareJIDInstanceNS(\"user@domain\");\n\n\t\tString encPassword = \"cfe7ba4e1a7130fa8a9408b0fee2b7a1\";\n\t\tMD5PasswordCredentialsEntry.Decoder decoder = new MD5PasswordCredentialsEntry.Decoder();\n\t\tMD5PasswordCredentialsEntry entry = decoder.decode(user, encPassword);\n\n\t\tassertTrue(entry.verifyPlainPassword(testPassword));\n\t}\n\n\t@Test\n\tpublic void testEncodingAndDecoding() {\n\t\tString testPassword = \"some-password-do-protect\";\n\t\tBareJID user = BareJID.bareJIDInstanceNS(\"user@domain\");\n\n\t\tMD5PasswordCredentialsEntry.Encoder encoder = new MD5PasswordCredentialsEntry.Encoder();\n\t\tString encPassword = encoder.encode(user, testPassword);\n\n\t\tMD5PasswordCredentialsEntry.Decoder decoder = new MD5PasswordCredentialsEntry.Decoder();\n\t\tMD5PasswordCredentialsEntry entry = decoder.decode(user, encPassword);\n\n\t\tassertTrue(entry.verifyPlainPassword(testPassword));\n\t\tassertEquals(encPassword, encoder.encode(user, entry));\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/auth/credentials/entries/MD5UserIdPasswordCredentialsEntryTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.credentials.entries;\n\nimport org.junit.Test;\nimport tigase.xmpp.jid.BareJID;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\npublic class MD5UserIdPasswordCredentialsEntryTest {\n\n\t@Test\n\tpublic void testDecodingOfStoredValue() {\n\t\tString testPassword = \"some-password-do-protect\";\n\t\tBareJID user = BareJID.bareJIDInstanceNS(\"user@domain\");\n\n\t\tString encPassword = \"29681c1fd36931cff65deb22d77c115d\";\n\t\tMD5UserIdPasswordCredentialsEntry.Decoder decoder = new MD5UserIdPasswordCredentialsEntry.Decoder();\n\t\tMD5UserIdPasswordCredentialsEntry entry = decoder.decode(user, encPassword);\n\n\t\tassertTrue(entry.verifyPlainPassword(testPassword));\n\t}\n\n\t@Test\n\tpublic void testEncodingAndDecoding() {\n\t\tString testPassword = \"some-password-do-protect\";\n\t\tBareJID user = BareJID.bareJIDInstanceNS(\"user@domain\");\n\n\t\tMD5UserIdPasswordCredentialsEntry.Encoder encoder = new MD5UserIdPasswordCredentialsEntry.Encoder();\n\t\tString encPassword = encoder.encode(user, testPassword);\n//\t\tSystem.out.println(encPassword);\n\n\t\tMD5UserIdPasswordCredentialsEntry.Decoder decoder = new MD5UserIdPasswordCredentialsEntry.Decoder();\n\t\tMD5UserIdPasswordCredentialsEntry entry = decoder.decode(user, encPassword);\n\n\t\tassertTrue(entry.verifyPlainPassword(testPassword));\n\t\tassertEquals(encPassword, encoder.encode(user, entry));\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/auth/credentials/entries/MD5UsernamePasswordCredentialsEntryTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.credentials.entries;\n\nimport org.junit.Test;\nimport tigase.xmpp.jid.BareJID;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\npublic class MD5UsernamePasswordCredentialsEntryTest {\n\n\t@Test\n\tpublic void testDecodingOfStoredValue() {\n\t\tString testPassword = \"some-password-do-protect\";\n\t\tBareJID user = BareJID.bareJIDInstanceNS(\"user@domain\");\n\n\t\tString encPassword = \"3e470bf91e38446f3d58ce890b1a8b63\";\n\t\tMD5UsernamePasswordCredentialsEntry.Decoder decoder = new MD5UsernamePasswordCredentialsEntry.Decoder();\n\t\tMD5UsernamePasswordCredentialsEntry entry = decoder.decode(user, encPassword);\n\n\t\tassertTrue(entry.verifyPlainPassword(testPassword));\n\t}\n\n\t@Test\n\tpublic void testEncodingAndDecoding() {\n\t\tString testPassword = \"some-password-do-protect\";\n\t\tBareJID user = BareJID.bareJIDInstanceNS(\"user@domain\");\n\n\t\tMD5UsernamePasswordCredentialsEntry.Encoder encoder = new MD5UsernamePasswordCredentialsEntry.Encoder();\n\t\tString encPassword = encoder.encode(user, testPassword);\n//\t\tSystem.out.println(encPassword);\n\n\t\tMD5UsernamePasswordCredentialsEntry.Decoder decoder = new MD5UsernamePasswordCredentialsEntry.Decoder();\n\t\tMD5UsernamePasswordCredentialsEntry entry = decoder.decode(user, encPassword);\n\n\t\tassertTrue(entry.verifyPlainPassword(testPassword));\n\t\tassertEquals(encPassword, encoder.encode(user, entry));\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/auth/credentials/entries/PlainCredentialsEntryTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.credentials.entries;\n\nimport org.junit.Test;\nimport tigase.xmpp.jid.BareJID;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\npublic class PlainCredentialsEntryTest {\n\n\t@Test\n\tpublic void testDecodingOfStoredValue() {\n\t\tString testPassword = \"some-password-do-protect\";\n\t\tBareJID user = BareJID.bareJIDInstanceNS(\"user@domain\");\n\n\t\tString encPassword = testPassword;\n\t\tPlainCredentialsEntry.Decoder decoder = new PlainCredentialsEntry.Decoder();\n\t\tPlainCredentialsEntry entry = decoder.decode(user, encPassword);\n\n\t\tassertTrue(entry.verifyPlainPassword(testPassword));\n\t}\n\n\t@Test\n\tpublic void testEncodingAndDecoding() {\n\t\tString testPassword = \"some-password-do-protect\";\n\t\tBareJID user = BareJID.bareJIDInstanceNS(\"user@domain\");\n\n\t\tPlainCredentialsEntry.Encoder encoder = new PlainCredentialsEntry.Encoder();\n\t\tString encPassword = encoder.encode(user, testPassword);\n\n\t\tPlainCredentialsEntry.Decoder decoder = new PlainCredentialsEntry.Decoder();\n\t\tPlainCredentialsEntry entry = decoder.decode(user, encPassword);\n\n\t\tassertTrue(entry.verifyPlainPassword(testPassword));\n\t\tassertEquals(encPassword, encoder.encode(user, entry));\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/auth/credentials/entries/ScramCredentialsEntryTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.credentials.entries;\n\nimport org.junit.Test;\nimport tigase.util.Base64;\nimport tigase.xmpp.jid.BareJID;\n\nimport static org.junit.Assert.*;\n\npublic class ScramCredentialsEntryTest {\n\n\t@Test\n\tpublic void testDecoding() {\n\t\tBareJID user = BareJID.bareJIDInstanceNS(\"user@domain\");\n\n\t\tString encPassword = \"s=QSXCR+Q6sek8bf92,i=123,t=6dlGYMOdZcOPutkcNY8U2g7vK9Y=,e=D+CSWLOshSulAsxiupA+qs2/fTE=\";\n\t\tScramCredentialsEntry.Decoder decoder = new ScramCredentialsEntry.Decoder(\"SHA1\");\n\t\tScramCredentialsEntry entry = decoder.decode(user, encPassword);\n\n\t\tassertEquals(\"6dlGYMOdZcOPutkcNY8U2g7vK9Y=\", Base64.encode(entry.getStoredKey()));\n\t\tassertEquals(\"D+CSWLOshSulAsxiupA+qs2/fTE=\", Base64.encode(entry.getServerKey()));\n\t\tassertEquals(\"QSXCR+Q6sek8bf92\", Base64.encode(entry.getSalt()));\n\t\tassertEquals(123, entry.getIterations());\n\t}\n\n\t@Test\n\tpublic void testDecodingOfStoredKeysValue() {\n\t\tString testPassword = \"pencil\";\n\t\tBareJID user = BareJID.bareJIDInstanceNS(\"user@domain\");\n\n\t\tString encPassword = \"s=QSXCR+Q6sek8bf92,i=4096,t=6dlGYMOdZcOPutkcNY8U2g7vK9Y=,e=D+CSWLOshSulAsxiupA+qs2/fTE=\";\n\t\tScramCredentialsEntry.Decoder decoder = new ScramCredentialsEntry.Decoder(\"SHA1\");\n\t\tScramCredentialsEntry entry = decoder.decode(user, encPassword);\n\n\t\tassertEquals(\"6dlGYMOdZcOPutkcNY8U2g7vK9Y=\", Base64.encode(entry.getStoredKey()));\n\t\tassertEquals(\"D+CSWLOshSulAsxiupA+qs2/fTE=\", Base64.encode(entry.getServerKey()));\n\t\tassertEquals(\"QSXCR+Q6sek8bf92\", Base64.encode(entry.getSalt()));\n\t\tassertEquals(4096, entry.getIterations());\n\t\tassertTrue(entry.verifyPlainPassword(testPassword));\n\t}\n\n\t@Test\n\tpublic void testDecodingOfStoredValue() {\n\t\tString testPassword = \"some-password-do-protect\";\n\t\tBareJID user = BareJID.bareJIDInstanceNS(\"user@domain\");\n\n\t\tString encPassword = \"s=a526P5eUQMim7g==,i=4096,p=lMVlJo/obJ9xI7P9+vZdbwrHjoA=\";\n\t\tScramCredentialsEntry.Decoder decoder = new ScramCredentialsEntry.Decoder(\"SHA1\");\n\t\tScramCredentialsEntry entry = decoder.decode(user, encPassword);\n\n\t\tassertTrue(entry.verifyPlainPassword(testPassword));\n\t}\n\n\t@Test\n\tpublic void testDecodingOfValueWithTranscoding() {\n\t\tString testPassword = \"pencil\";\n\t\tBareJID user = BareJID.bareJIDInstanceNS(\"user@domain\");\n\n\t\tString encPassword = \"s=QSXCR+Q6sek8bf92,i=4096,p=HZbuOlKbWl+eR8AfIposuKbhX30=\";\n\t\tScramCredentialsEntry.Decoder decoder = new ScramCredentialsEntry.Decoder(\"SHA1\");\n\t\tScramCredentialsEntry entry = decoder.decode(user, encPassword);\n\n\t\tassertEquals(\"6dlGYMOdZcOPutkcNY8U2g7vK9Y=\", Base64.encode(entry.getStoredKey()));\n\t\tassertEquals(\"D+CSWLOshSulAsxiupA+qs2/fTE=\", Base64.encode(entry.getServerKey()));\n\t\tassertEquals(\"QSXCR+Q6sek8bf92\", Base64.encode(entry.getSalt()));\n\t\tassertEquals(4096, entry.getIterations());\n\t\tassertTrue(entry.verifyPlainPassword(testPassword));\n\t}\n\n\t@Test\n\tpublic void testEncodingAndDecoding() {\n\t\tString testPassword = \"some-password-do-protect\";\n\t\tBareJID user = BareJID.bareJIDInstanceNS(\"user@domain\");\n\n\t\tScramCredentialsEntry.Encoder encoder = new ScramCredentialsEntry.Encoder(\"SHA1\");\n\t\tString encPassword = encoder.encode(user, testPassword);\n\n\t\tScramCredentialsEntry.Decoder decoder = new ScramCredentialsEntry.Decoder(\"SHA1\");\n\t\tScramCredentialsEntry entry = decoder.decode(user, encPassword);\n\n\t\tassertTrue(entry.verifyPlainPassword(testPassword));\n\t\tassertEquals(encPassword, encoder.encode(user, entry));\n\t}\n\n\t@Test\n\tpublic void testInvalidPassword() {\n\t\tBareJID user = BareJID.bareJIDInstanceNS(\"user@domain\");\n\n\t\tString encPassword = \"s=QSXCR+Q6sek8bf92,i=4096,t=6dlGYMOdZcOPutkcNY8U2g7vK9Y=,e=D+CSWLOshSulAsxiupA+qs2/fTE=\";\n\t\tScramCredentialsEntry.Decoder decoder = new ScramCredentialsEntry.Decoder(\"SHA1\");\n\t\tScramCredentialsEntry entry = decoder.decode(user, encPassword);\n\n\t\tassertFalse(entry.verifyPlainPassword(\"crayon\"));\n\t}\n\n\t@Test\n\tpublic void testTranscodingEntries() {\n\t\tBareJID user = BareJID.bareJIDInstanceNS(\"user@domain\");\n\t\tString encPassword = \"s=QSXCR+Q6sek8bf92,i=4096,p=HZbuOlKbWl+eR8AfIposuKbhX30=\";\n\t\tScramCredentialsEntry.Decoder decoder = new ScramCredentialsEntry.Decoder(\"SHA1\");\n\t\tScramCredentialsEntry.Encoder encoder = new ScramCredentialsEntry.Encoder(\"SHA1\");\n\n\t\tScramCredentialsEntry entry = decoder.decode(user, encPassword);\n\n\t\tString encAuthData = encoder.encode(user, entry);\n\n\t\tassertEquals(\"s=QSXCR+Q6sek8bf92,i=4096,t=6dlGYMOdZcOPutkcNY8U2g7vK9Y=,e=D+CSWLOshSulAsxiupA+qs2/fTE=\",\n\t\t\t\t\t encAuthData);\n\t\tassertEquals(\"6dlGYMOdZcOPutkcNY8U2g7vK9Y=\", Base64.encode(entry.getStoredKey()));\n\t\tassertEquals(\"D+CSWLOshSulAsxiupA+qs2/fTE=\", Base64.encode(entry.getServerKey()));\n\t\tassertEquals(\"QSXCR+Q6sek8bf92\", Base64.encode(entry.getSalt()));\n\t\tassertEquals(4096, entry.getIterations());\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/auth/mechanisms/SCRAMHelperTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.mechanisms;\n\nimport junit.framework.TestCase;\nimport org.junit.Assert;\nimport tigase.util.Base64;\n\nimport java.security.InvalidKeyException;\nimport java.security.NoSuchAlgorithmException;\n\npublic class SCRAMHelperTest\n\t\textends TestCase {\n\n\tpublic void testEncodePlainPassword() throws NoSuchAlgorithmException, InvalidKeyException {\n\t\tvar authData = SCRAMHelper.encodePlainPassword(\"SHA1\", Base64.decode(\"QSXCR+Q6sek8bf92\"), 4096, \"pencil\");\n\t\tAssert.assertArrayEquals(Base64.decode(\"D+CSWLOshSulAsxiupA+qs2/fTE=\"), authData.serverKey());\n\t\tAssert.assertArrayEquals(Base64.decode(\"6dlGYMOdZcOPutkcNY8U2g7vK9Y=\"), authData.storedKey());\n\t}\n\n\tpublic void testTranscode() throws NoSuchAlgorithmException, InvalidKeyException {\n\t\tvar authData = SCRAMHelper.transcode(\"SHA1\", Base64.decode(\"HZbuOlKbWl+eR8AfIposuKbhX30=\"));\n\t\tAssert.assertArrayEquals(Base64.decode(\"D+CSWLOshSulAsxiupA+qs2/fTE=\"), authData.serverKey());\n\t\tAssert.assertArrayEquals(Base64.decode(\"6dlGYMOdZcOPutkcNY8U2g7vK9Y=\"), authData.storedKey());\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/auth/mechanisms/SaslANONYMOUSTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.mechanisms;\n\nimport junit.framework.TestCase;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.callback.NameCallback;\nimport javax.security.auth.callback.UnsupportedCallbackException;\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class SaslANONYMOUSTest\n\t\textends TestCase {\n\n\tprivate SaslANONYMOUS sasl;\n\n\t@Override\n\t@Before\n\tpublic void setUp() {\n\t\tMap<? super String, ?> props = new HashMap<String, Object>();\n\t\tCallbackHandler callbackHandler = new CallbackHandler() {\n\n\t\t\t@Override\n\t\t\tpublic void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {\n\t\t\t\tfor (Callback callback : callbacks) {\n\t\t\t\t\tif (callback instanceof NameCallback) {\n\t\t\t\t\t\t((NameCallback) callback).setName(\"somerandomname@domain.com\");\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new UnsupportedCallbackException(callback);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tthis.sasl = new SaslANONYMOUS(props, callbackHandler);\n\t}\n\n\t@Test\n\tpublic void testSuccess() {\n\n\t\ttry {\n\t\t\tsasl.evaluateResponse(\"\".getBytes());\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\tfail(e.getMessage());\n\t\t}\n\n\t\tassertTrue(sasl.isComplete());\n\t\tassertEquals(\"somerandomname@domain.com\", sasl.getAuthorizationID());\n\t\tassertTrue((Boolean) sasl.getNegotiatedProperty(SaslANONYMOUS.IS_ANONYMOUS_PROPERTY));\n\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/auth/mechanisms/SaslPLAINTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.mechanisms;\n\nimport junit.framework.TestCase;\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.auth.XmppSaslException;\nimport tigase.auth.callbacks.AuthorizationIdCallback;\nimport tigase.auth.callbacks.VerifyPasswordCallback;\nimport tigase.xmpp.jid.BareJID;\n\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.callback.NameCallback;\nimport javax.security.auth.callback.UnsupportedCallbackException;\nimport javax.security.sasl.AuthorizeCallback;\nimport javax.security.sasl.SaslException;\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class SaslPLAINTest\n\t\textends TestCase {\n\n\tprivate SaslPLAIN sasl;\n\n\t@Override\n\t@Before\n\tpublic void setUp() {\n\t\tMap<? super String, ?> props = new HashMap<String, Object>();\n\t\tCallbackHandler callbackHandler = new CallbackHandler() {\n\n\t\t\tprivate String username;\n\n\t\t\t@Override\n\t\t\tpublic void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {\n\t\t\t\tfor (Callback callback : callbacks) {\n\t\t\t\t\tif (callback instanceof NameCallback) {\n\t\t\t\t\t\tBareJID jid = BareJID.bareJIDInstanceNS(((NameCallback) callback).getDefaultName());\n\t\t\t\t\t\tif (jid.getLocalpart() == null || !\"domain.com\".equalsIgnoreCase(jid.getDomain())) {\n\t\t\t\t\t\t\tjid = BareJID.bareJIDInstanceNS(((NameCallback) callback).getDefaultName(), \"domain.com\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tusername = jid.toString();\n\t\t\t\t\t\t((NameCallback) callback).setName(username);\n\t\t\t\t\t} else if (callback instanceof VerifyPasswordCallback) {\n\t\t\t\t\t\t((VerifyPasswordCallback) callback).setVerified(\"juliet@domain.com:xsecret\".equals(\n\t\t\t\t\t\t\t\tusername + \":\" + ((VerifyPasswordCallback) callback).getPassword()));\n\t\t\t\t\t} else if (callback instanceof AuthorizationIdCallback) {\n\t\t\t\t\t\t// there is nothing to do..\n\t\t\t\t\t} else if (callback instanceof AuthorizeCallback) {\n\t\t\t\t\t\tboolean a = ((AuthorizeCallback) callback).getAuthorizationID()\n\t\t\t\t\t\t\t\t.equals(((AuthorizeCallback) callback).getAuthenticationID());\n\t\t\t\t\t\t((AuthorizeCallback) callback).setAuthorized(a);\n\t\t\t\t\t\tif (a) {\n\t\t\t\t\t\t\t((AuthorizeCallback) callback).setAuthorizedID(\n\t\t\t\t\t\t\t\t\t((AuthorizeCallback) callback).getAuthorizationID());\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new UnsupportedCallbackException(callback);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tthis.sasl = new SaslPLAIN(props, callbackHandler);\n\t}\n\n\t@Test\n\tpublic void testEmptyPassword() {\n\t\ttry {\n\t\t\tsasl.evaluateResponse(\"\\0juliet\\0\".getBytes());\n\t\t\tfail(\"Exception must be throwed\");\n\t\t} catch (XmppSaslException e) {\n\t\t\tassertEquals(\"not-authorized\", e.getSaslErrorElementName());\n\t\t\tassertEquals(\"Password not verified\", e.getMessage());\n\t\t} catch (SaslException e) {\n\t\t\tassertEquals(\"Password not verified\", e.getMessage());\n\t\t}\n\n\t\tassertFalse(sasl.isComplete());\n\t\tassertNull(\"Authorization ID must be null\", sasl.getAuthorizationID());\n\t}\n\n\t@Test\n\tpublic void testEmptyUsername() {\n\t\ttry {\n\t\t\tsasl.evaluateResponse(\"\\0\\0qaz\".getBytes());\n\t\t\tfail(\"Exception must be throwed\");\n\t\t} catch (XmppSaslException e) {\n\t\t\tassertEquals(\"malformed-request\", e.getSaslErrorElementName());\n\t\t} catch (SaslException e) {\n\t\t}\n\n\t\tassertFalse(sasl.isComplete());\n\t\tassertNull(\"Authorization ID must be null\", sasl.getAuthorizationID());\n\t}\n\n\t@Test\n\tpublic void testEmptyUsernamePassword() {\n\t\ttry {\n\t\t\tsasl.evaluateResponse(\"\\0\\0\".getBytes());\n\t\t\tfail(\"Exception must be throwed\");\n\t\t} catch (XmppSaslException e) {\n\t\t\tassertEquals(\"malformed-request\", e.getSaslErrorElementName());\n\t\t} catch (SaslException e) {\n\t\t}\n\n\t\tassertFalse(sasl.isComplete());\n\t\tassertNull(\"Authorization ID must be null\", sasl.getAuthorizationID());\n\t}\n\n\t@Test\n\tpublic void testInvalidAuthzId() {\n\t\ttry {\n\t\t\tsasl.evaluateResponse(\"romeo\\0juliet\\0xsecret\".getBytes());\n\t\t\tfail(\"Exception must be throwed\");\n\t\t} catch (XmppSaslException e) {\n\t\t\tassertEquals(\"invalid-authzid\", e.getSaslErrorElementName());\n\t\t\tassertEquals(\"PLAIN: juliet is not authorized to act as romeo\", e.getMessage());\n\t\t} catch (SaslException e) {\n\t\t\tassertEquals(\"PLAIN: juliet is not authorized to act as romeo\", e.getMessage());\n\t\t}\n\n\t\tassertFalse(sasl.isComplete());\n\t\tassertNull(\"Authorization ID must be null\", sasl.getAuthorizationID());\n\t}\n\n\t@Test\n\tpublic void testInvalidPassword() {\n\t\ttry {\n\t\t\tsasl.evaluateResponse(\"\\0juliet\\0ysecret\".getBytes());\n\t\t\tfail(\"Exception must be throwed\");\n\t\t} catch (XmppSaslException e) {\n\t\t\tassertEquals(\"not-authorized\", e.getSaslErrorElementName());\n\t\t\tassertEquals(\"Password not verified\", e.getMessage());\n\t\t} catch (SaslException e) {\n\t\t\tassertEquals(\"Password not verified\", e.getMessage());\n\t\t}\n\n\t\tassertFalse(sasl.isComplete());\n\t\tassertNull(\"Authorization ID must be null\", sasl.getAuthorizationID());\n\t}\n\n\t@Test\n\tpublic void testInvalidUsername() {\n\t\ttry {\n\t\t\tsasl.evaluateResponse(\"\\0romeo\\0xsecret\".getBytes());\n\t\t\tfail(\"Exception must be throwed\");\n\t\t} catch (XmppSaslException e) {\n\t\t\tassertEquals(\"not-authorized\", e.getSaslErrorElementName());\n\t\t\tassertEquals(\"Password not verified\", e.getMessage());\n\t\t} catch (SaslException e) {\n\t\t\tassertEquals(\"Password not verified\", e.getMessage());\n\t\t}\n\n\t\tassertFalse(sasl.isComplete());\n\t\tassertNull(\"Authorization ID must be null\", sasl.getAuthorizationID());\n\t}\n\n\t@Test\n\tpublic void testmalformedRequest1() {\n\t\ttry {\n\t\t\tsasl.evaluateResponse(\"\".getBytes());\n\t\t\tfail(\"Exception must be throwed\");\n\t\t} catch (XmppSaslException e) {\n\t\t\tassertEquals(\"malformed-request\", e.getSaslErrorElementName());\n\t\t} catch (SaslException e) {\n\t\t}\n\n\t\tassertFalse(sasl.isComplete());\n\t\tassertNull(\"Authorization ID must be null\", sasl.getAuthorizationID());\n\t}\n\n\t@Test\n\tpublic void testmalformedRequest2() {\n\t\ttry {\n\t\t\tsasl.evaluateResponse(null);\n\t\t\tfail(\"Exception must be throwed\");\n\t\t} catch (XmppSaslException e) {\n\t\t\tassertEquals(\"malformed-request\", e.getSaslErrorElementName());\n\t\t} catch (SaslException e) {\n\t\t}\n\n\t\tassertFalse(sasl.isComplete());\n\t\tassertNull(\"Authorization ID must be null\", sasl.getAuthorizationID());\n\t}\n\n\t@Test\n\tpublic void testmalformedRequest3() {\n\t\ttry {\n\t\t\tsasl.evaluateResponse(\"\\0juliet\\0xsecret\\0\".getBytes());\n\t\t\tfail(\"Exception must be throwed\");\n\t\t} catch (XmppSaslException e) {\n\t\t\tassertEquals(\"malformed-request\", e.getSaslErrorElementName());\n\t\t} catch (SaslException e) {\n\t\t}\n\n\t\tassertFalse(sasl.isComplete());\n\t\tassertNull(\"Authorization ID must be null\", sasl.getAuthorizationID());\n\t}\n\n\t@Test\n\tpublic void testmalformedRequest4() {\n\t\ttry {\n\t\t\tsasl.evaluateResponse(\"xyz\".getBytes());\n\t\t\tfail(\"Exception must be throwed\");\n\t\t} catch (XmppSaslException e) {\n\t\t\tassertEquals(\"malformed-request\", e.getSaslErrorElementName());\n\t\t} catch (SaslException e) {\n\t\t}\n\n\t\tassertFalse(sasl.isComplete());\n\t\tassertNull(\"Authorization ID must be null\", sasl.getAuthorizationID());\n\t}\n\n\t@Test\n\tpublic void testSuccess() {\n\n\t\ttry {\n\t\t\tsasl.evaluateResponse(\"\\0juliet\\0xsecret\".getBytes());\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\tfail(e.getMessage());\n\t\t}\n\n\t\tassertTrue(sasl.isComplete());\n\t\tassertEquals(\"juliet@domain.com\", sasl.getAuthorizationID());\n\n\t}\n\n\t@Test\n\tpublic void testSuccessWithAuthzId() {\n\n\t\ttry {\n\t\t\tsasl.evaluateResponse(\"juliet@domain.com\\0juliet\\0xsecret\".getBytes());\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\tfail(e.getMessage());\n\t\t}\n\n\t\tassertTrue(sasl.isComplete());\n\t\tassertEquals(\"juliet@domain.com\", sasl.getAuthorizationID());\n\t}\n\n\t@Test\n\tpublic void testSuccessWithAuthzId3() {\n\n\t\ttry {\n\t\t\tsasl.evaluateResponse(\"juliet@domain.com\\0juliet@domain.com\\0xsecret\".getBytes());\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\tfail(e.getMessage());\n\t\t}\n\n\t\tassertTrue(sasl.isComplete());\n\t\tassertEquals(\"juliet@domain.com\", sasl.getAuthorizationID());\n\t}\n\n\t@Test\n\tpublic void testSuccessWithAuthzId2() {\n\n\t\tMap<? super String, ?> props = new HashMap<String, Object>();\n\t\tCallbackHandler callbackHandler = new CallbackHandler() {\n\n\t\t\tprivate String username;\n\n\t\t\t@Override\n\t\t\tpublic void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {\n\t\t\t\tfor (Callback callback : callbacks) {\n\t\t\t\t\tif (callback instanceof NameCallback) {\n\t\t\t\t\t\tusername = ((NameCallback) callback).getDefaultName();\n\t\t\t\t\t} else if (callback instanceof VerifyPasswordCallback) {\n\t\t\t\t\t\t((VerifyPasswordCallback) callback).setVerified(\"secondwitch:shakespeare\".equals(\n\t\t\t\t\t\t\t\tusername + \":\" + ((VerifyPasswordCallback) callback).getPassword()));\n\t\t\t\t\t} else if (callback instanceof AuthorizationIdCallback) {\n\t\t\t\t\t\t// nothing to do..\n\t\t\t\t\t} else if (callback instanceof AuthorizeCallback) {\n\t\t\t\t\t\tboolean a = ((AuthorizeCallback) callback).getAuthorizationID().equals(\"romeo@example.net\");\n\t\t\t\t\t\ta = a && username.equals(\"secondwitch\");\n\t\t\t\t\t\t((AuthorizeCallback) callback).setAuthorized(a);\n\t\t\t\t\t\tif (a) {\n\t\t\t\t\t\t\t((AuthorizeCallback) callback).setAuthorizedID(\n\t\t\t\t\t\t\t\t\t((AuthorizeCallback) callback).getAuthorizationID());\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new UnsupportedCallbackException(callback);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tfinal SaslPLAIN sasl = new SaslPLAIN(props, callbackHandler);\n\n\t\ttry {\n\t\t\tsasl.evaluateResponse(\"romeo@example.net\\0secondwitch\\0shakespeare\".getBytes());\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\tfail(e.getMessage());\n\t\t}\n\n\t\tassertTrue(sasl.isComplete());\n\t\tassertEquals(\"romeo@example.net\", sasl.getAuthorizationID());\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/auth/mechanisms/SaslSCRAMPlusTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.mechanisms;\n\nimport junit.framework.TestCase;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport tigase.TestLogger;\nimport tigase.util.Base64;\n\nimport javax.security.sasl.SaslException;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class SaslSCRAMPlusTest\n\t\textends TestCase {\n\n\tprivate static final Logger log = TestLogger.getLogger(SaslSCRAMPlusTest.class);\n\n\t@Test\n\tpublic void testChannelBindingEncodingEncoding() throws SaslException {\n\t\tString CFM = \"cD10bHMtdW5pcXVlLCxuPWJtYWxrb3cscj1TanF1Y3NIdmkzQjR0c1lrTkpCS0lJdHM=\";\n\t\tString SFM = \"cj1TanF1Y3NIdmkzQjR0c1lrTkpCS0lJdHNjdUVZWGMvU210dWtTUjIycVoscz1NZzRxWVlUckh6VkUyUUdZLGk9NDA5Ng==\";\n\t\tString CSM = \"Yz1jRDEwYkhNdGRXNXBjWFZsTEN4WHhZRFpldWFPU01qWTE3cG5QZXE2K2FDaUdIODg1dW9PVlFKMm5rQlk4dz09LHI9U2pxdWNzSHZpM0I0dHNZa05KQktJSXRzY3VFWVhjL1NtdHVrU1IyMnFaLHA9NC9ZeHptOUZsV24xT1duaUJVQ08yeC9jMXo4PQ==\";\n\n\t\tSaslSCRAMPlus m = create(\"Mg4qYYTrHzVE2QGY\", \"cuEYXc/SmtukSR22qZ\", \"123456\",\n\t\t\t\t\t\t\t\t Base64.decode(\"V8WA2XrmjkjI2Ne6Zz3quvmgohh/PObqDlUCdp5AWPM=\"));\n\n\t\tbyte[] req;\n\t\tbyte[] rsp;\n\n\t\treq = Base64.decode(CFM);\n\t\trsp = m.evaluateResponse(req);\n\n\t\tlog.log(Level.FINE, new String(rsp));\n\t\tlog.log(Level.FINE, new String(Base64.decode(SFM)));\n\n\t\treq = Base64.decode(CSM);\n\t\trsp = m.evaluateResponse(req);\n\t}\n\n\t@Test\n\tpublic void testInvalidBinding() {\n\t\tSaslSCRAMPlus m = create(\"AecUfGKyBAbZjjXW\", \"k5m3fXaEqPQ0zxIjpl\", \"123456\", new byte[]{'D', 'P', 'I'});\n\n\t\ttry {\n\t\t\tbyte[] r = m.evaluateResponse(\"p=tls-unique,,n=bmalkow,r=mnKBtk4+09BtRQM3AkSsjsE5\".getBytes());\n\t\t\tAssert.assertEquals(\"r=mnKBtk4+09BtRQM3AkSsjsE5k5m3fXaEqPQ0zxIjpl,s=AecUfGKyBAbZjjXW,i=4096\",\n\t\t\t\t\t\t\t\tnew String(r));\n\n\t\t\tr = m.evaluateResponse(\n\t\t\t\t\t\"c=cD10bHMtdW5pcXVlLCxEVVBB,r=mnKBtk4+09BtRQM3AkSsjsE5k5m3fXaEqPQ0zxIjpl,p=BatbnZpQ+UolSyWBozXyvS8Yl78=\"\n\t\t\t\t\t\t\t.getBytes());\n\t\t\tfail();\n\n\t\t} catch (SaslException e) {\n\t\t\tAssert.assertEquals(\"Channel bindings does not match\", e.getMessage());\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testModifiedBinding() {\n\t\tfinal SaslSCRAMPlus m = create(\"AecUfGKyBAbZjjXW\", \"k5m3fXaEqPQ0zxIjpl\", \"123456\", new byte[]{'D', 'P', 'I'});\n\n\t\ttry {\n\t\t\tbyte[] r = m.evaluateResponse(\"p=tls-unique,,n=bmalkow,r=mnKBtk4+09BtRQM3AkSsjsE5\".getBytes());\n\t\t\tAssert.assertEquals(\"r=mnKBtk4+09BtRQM3AkSsjsE5k5m3fXaEqPQ0zxIjpl,s=AecUfGKyBAbZjjXW,i=4096\",\n\t\t\t\t\t\t\t\tnew String(r));\n\n\t\t\t// Channel binding data modified by Mallet to value expected by server\n\t\t\tr = m.evaluateResponse(\n\t\t\t\t\t\"c=cD10bHMtdW5pcXVlLCxEUEk=,r=mnKBtk4+09BtRQM3AkSsjsE5k5m3fXaEqPQ0zxIjpl,p=BatbnZpQ+UolSyWBozXyvS8Yl78=\"\n\t\t\t\t\t\t\t.getBytes());\n\t\t\tfail();\n\t\t} catch (SaslException e) {\n\t\t\tAssert.assertEquals(\"Password not verified\", e.getMessage());\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testServerFirstMessageFail_1() {\n\t\tSaslSCRAMPlus m = create(\"Ey6OJnGx7JEJAIJp\", \"5kLrhitKUHVoSOmzdR\", \"123456\", new byte[]{'D', 'P', 'I'});\n\t\ttry {\n\t\t\tbyte[] r = m.evaluateResponse(\"n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL\".getBytes());\n\t\t\tfail();\n\t\t} catch (SaslException e) {\n\t\t\tAssert.assertEquals(\"Invalid request for SCRAM-SHA-1-PLUS\", e.getMessage());\n\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testServerFirstMessageFail_2() {\n\t\tSaslSCRAMPlus m = create(\"Ey6OJnGx7JEJAIJp\", \"5kLrhitKUHVoSOmzdR\", \"123456\", new byte[]{'D', 'P', 'I'});\n\t\ttry {\n\t\t\tbyte[] r = m.evaluateResponse(\"y,,n=user,r=fyko+d2lbbFgONRv9qkxdawL\".getBytes());\n\t\t\tfail();\n\t\t} catch (SaslException e) {\n\t\t\tAssert.assertEquals(\"Server supports PLUS. Please use 'p'\", e.getMessage());\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testServerFirstMessageWithBinding() {\n\t\tSaslSCRAMPlus m = create(\"Ey6OJnGx7JEJAIJp\", \"5kLrhitKUHVoSOmzdR\", \"123456\", new byte[]{'D', 'P', 'I'});\n\t\ttry {\n\t\t\tbyte[] r = m.evaluateResponse(\"p=tls-unique,,n=bmalkow,r=SpiXKmhi57DBp5sdE5G3H3ms\".getBytes());\n\t\t\tAssert.assertEquals(\"r=SpiXKmhi57DBp5sdE5G3H3ms5kLrhitKUHVoSOmzdR,s=Ey6OJnGx7JEJAIJp,i=4096\",\n\t\t\t\t\t\t\t\tnew String(r));\n\n\t\t\tr = m.evaluateResponse(\n\t\t\t\t\t\"c=cD10bHMtdW5pcXVlLCxEUEk=,r=SpiXKmhi57DBp5sdE5G3H3ms5kLrhitKUHVoSOmzdR,p=+zQvUd4nQqo03thSCcc2K6gueD4=\"\n\t\t\t\t\t\t\t.getBytes());\n\t\t\tAssert.assertEquals(\"v=NQ/f8FjeMxUuRK9F88G8tMji4pk=\", new String(r));\n\n\t\t} catch (SaslException e) {\n\t\t\te.printStackTrace();\n\t\t\tfail(e.getMessage());\n\t\t}\n\n\t\tassertTrue(m.isComplete());\n\t\tassertEquals(\"user@domain.com\", m.getAuthorizationID());\n\t}\n\n\tprivate SaslSCRAMPlus create(String salt, String snonce, String password, byte[] bindingData) {\n\t\tSaslSCRAMTest.TestCallbackHandler h = new SaslSCRAMTest.TestCallbackHandler();\n\t\th.setBindingData(bindingData);\n\t\th.setPassword(password);\n\t\th.setSalt(salt);\n\t\treturn new SaslSCRAMPlus(null, h, snonce) {\n\t\t};\n\t}\n\n}\n\n\n"
  },
  {
    "path": "src/test/java/tigase/auth/mechanisms/SaslSCRAMTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.mechanisms;\n\nimport junit.framework.TestCase;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.experimental.categories.Category;\nimport tigase.TestLogger;\nimport tigase.auth.callbacks.*;\nimport tigase.util.Base64;\n\nimport javax.crypto.spec.SecretKeySpec;\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.callback.NameCallback;\nimport javax.security.auth.callback.UnsupportedCallbackException;\nimport javax.security.sasl.AuthorizeCallback;\nimport javax.security.sasl.SaslException;\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.nio.charset.StandardCharsets;\nimport java.security.InvalidKeyException;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class SaslSCRAMTest\n\t\textends TestCase {\n\n\tprivate static final Logger log = TestLogger.getLogger(SaslSCRAMTest.class);\n\n\t@Test\n\tpublic void testDataExchange01() throws Exception {\n\t\tfinal byte[] CFM = Base64.decode(\"biwsbj1qZW5raW5zLHI9YmdId0xRSEJkNFMrK3F2VEIzZis0QT09\");\n\t\tfinal byte[] SFM = Base64.decode(\n\t\t\t\t\"cj1iZ0h3TFFIQmQ0UysrcXZUQjNmKzRBPT1lWXY4REhIMk81dHRxNlRtV3pncyxzPUZSelkraGM5TitMc0FnPT0saT00MDk2\");\n\t\tfinal byte[] CSM = Base64.decode(\n\t\t\t\t\"Yz1iaXdzLHI9YmdId0xRSEJkNFMrK3F2VEIzZis0QT09ZVl2OERISDJPNXR0cTZUbVd6Z3MscD1JTlpKaDljTkQyeFJlYzZBQytSYlBoRVdVakk9\");\n\n\t\tSaslSCRAM m = create(\"FRzY+hc9N+LsAg==\", \"eYv8DHH2O5ttq6TmWzgs\", \"test\");\n\t\ttry {\n\t\t\tlog.log(Level.FINE, \">> \" + new String(CFM));\n\t\t\tbyte[] sfm = m.evaluateResponse(CFM);\n\t\t\tlog.log(Level.FINE, \"<< \" + new String(sfm));\n\t\t\tAssert.assertArrayEquals(SFM, sfm);\n\t\t\tlog.log(Level.FINE, \">> \" + new String(CSM));\n\t\t\tbyte[] ssm = m.evaluateResponse(CSM);\n\t\t\tlog.log(Level.FINE, \"<< \" + new String(ssm));\n//\t\t\tAssert.assertArrayEquals(SSM, ssm);\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\tfail(e.getMessage());\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testH() throws UnsupportedEncodingException {\n\t\ttry {\n\t\t\tSaslSCRAM m = create(\"QSXCR+Q6sek8bf92\", \"3rfcNHYJY1ZVvWVs7j\", \"pencil\");\n\t\t\tbyte[] r = m.h(\"The quick brown fox jumps over the lazy cog\".getBytes(StandardCharsets.UTF_8));\n\t\t\tAssert.assertArrayEquals(Base64.decode(\"3p8sf9JeGzr60+haC9F9mxANtLM=\"), r);\n\t\t} catch (NoSuchAlgorithmException e) {\n\t\t\te.printStackTrace();\n\t\t\tfail(e.getMessage());\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testHi() {\n\t\ttry {\n\t\t\tbyte[] r = AbstractSaslSCRAM.hi(\"SHA1\", \"password\".getBytes(StandardCharsets.UTF_8),\n\t\t\t\t\t\t\t\t\t\t\t\"salt\".getBytes(StandardCharsets.UTF_8), 1);\n\t\t\tAssert.assertArrayEquals(\n\t\t\t\t\tnew byte[]{(byte) 0x0c, (byte) 0x60, (byte) 0xc8, (byte) 0x0f, (byte) 0x96, (byte) 0x1f,\n\t\t\t\t\t\t\t   (byte) 0x0e, (byte) 0x71, (byte) 0xf3, (byte) 0xa9, (byte) 0xb5, (byte) 0x24,\n\t\t\t\t\t\t\t   (byte) 0xaf, (byte) 0x60, (byte) 0x12, (byte) 0x06, (byte) 0x2f, (byte) 0xe0,\n\t\t\t\t\t\t\t   (byte) 0x37, (byte) 0xa6}, r);\n\n\t\t\tr = AbstractSaslSCRAM.hi(\"SHA1\", \"password\".getBytes(StandardCharsets.UTF_8),\n\t\t\t\t\t\t\t\t\t \"salt\".getBytes(StandardCharsets.UTF_8), 2);\n\t\t\tAssert.assertArrayEquals(\n\t\t\t\t\tnew byte[]{(byte) 0xea, (byte) 0x6c, (byte) 0x01, (byte) 0x4d, (byte) 0xc7, (byte) 0x2d,\n\t\t\t\t\t\t\t   (byte) 0x6f, (byte) 0x8c, (byte) 0xcd, (byte) 0x1e, (byte) 0xd9, (byte) 0x2a,\n\t\t\t\t\t\t\t   (byte) 0xce, (byte) 0x1d, (byte) 0x41, (byte) 0xf0, (byte) 0xd8, (byte) 0xde,\n\t\t\t\t\t\t\t   (byte) 0x89, (byte) 0x57}, r);\n\n\t\t\tr = AbstractSaslSCRAM.hi(\"SHA1\", \"password\".getBytes(StandardCharsets.UTF_8),\n\t\t\t\t\t\t\t\t\t \"salt\".getBytes(StandardCharsets.UTF_8), 4096);\n\t\t\tAssert.assertArrayEquals(\n\t\t\t\t\tnew byte[]{(byte) 0x4b, (byte) 0x00, (byte) 0x79, (byte) 0x01, (byte) 0xb7, (byte) 0x65,\n\t\t\t\t\t\t\t   (byte) 0x48, (byte) 0x9a, (byte) 0xbe, (byte) 0xad, (byte) 0x49, (byte) 0xd9,\n\t\t\t\t\t\t\t   (byte) 0x26, (byte) 0xf7, (byte) 0x21, (byte) 0xd0, (byte) 0x65, (byte) 0xa4,\n\t\t\t\t\t\t\t   (byte) 0x29, (byte) 0xc1}, r);\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\tfail(e.getMessage());\n\t\t}\n\t}\n\n\t@Category(tigase.tests.SlowTest.class)\n\t@Test\n\tpublic void testHiLong() {\n\t\ttry {\n\t\t\tbyte[] r = AbstractSaslSCRAM.hi(\"SHA1\", \"password\".getBytes(StandardCharsets.UTF_8),\n\t\t\t\t\t\t\t\t\t\t\t\"salt\".getBytes(StandardCharsets.UTF_8), 16777216);\n\t\t\tAssert.assertArrayEquals(\n\t\t\t\t\tnew byte[]{(byte) 0xee, (byte) 0xfe, (byte) 0x3d, (byte) 0x61, (byte) 0xcd, (byte) 0x4d,\n\t\t\t\t\t\t\t   (byte) 0xa4, (byte) 0xe4, (byte) 0xe9, (byte) 0x94, (byte) 0x5b, (byte) 0x3d,\n\t\t\t\t\t\t\t   (byte) 0x6b, (byte) 0xa2, (byte) 0x15, (byte) 0x8c, (byte) 0x26, (byte) 0x34,\n\t\t\t\t\t\t\t   (byte) 0xe9, (byte) 0x84}, r);\n\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\tfail(e.getMessage());\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testHmac() {\n\t\tSaslSCRAM m = create(\"QSXCR+Q6sek8bf92\", \"3rfcNHYJY1ZVvWVs7j\", \"pencil\");\n\t\ttry {\n\t\t\tbyte[] r = AbstractSaslSCRAM.hmac(m.key(\"foo\".getBytes(StandardCharsets.UTF_8)),\n\t\t\t\t\t\t\t\t\t\t\t  \"foobar\".getBytes(StandardCharsets.UTF_8));\n\n\t\t\tAssert.assertArrayEquals(\n\t\t\t\t\tnew byte[]{(byte) 0xa4, (byte) 0xee, (byte) 0xba, (byte) 0x8e, 0x63, 0x3d, 0x77, (byte) 0x88, 0x69,\n\t\t\t\t\t\t\t   (byte) 0xf5, 0x68, (byte) 0xd0, 0x5a, 0x1b, 0x3d, (byte) 0xc7, 0x2b, (byte) 0xfd, 0x4,\n\t\t\t\t\t\t\t   (byte) 0xdd}, r);\n\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\tfail(e.getMessage());\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testServerFirstMessage() throws UnsupportedEncodingException {\n\t\tfinal byte[] CFM = \"n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL\".getBytes(StandardCharsets.UTF_8);\n\t\tfinal byte[] SFM = \"r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096\".getBytes(\n\t\t\t\tStandardCharsets.UTF_8);\n\t\tfinal byte[] CSM = \"c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts=\".getBytes(\n\t\t\t\tStandardCharsets.UTF_8);\n\t\tfinal byte[] SSM = \"v=rmF9pqV8S7suAoZWja4dJRkFsKQ=\".getBytes(StandardCharsets.UTF_8);\n\n\t\tSaslSCRAM m = create(\"QSXCR+Q6sek8bf92\", \"3rfcNHYJY1ZVvWVs7j\", \"pencil\");\n\t\ttry {\n\t\t\tlog.log(Level.FINE, \">> \" + new String(CFM));\n\t\t\tbyte[] sfm = m.evaluateResponse(CFM);\n\t\t\tlog.log(Level.FINE, \"<< \" + new String(sfm));\n\t\t\tAssert.assertArrayEquals(SFM, sfm);\n\t\t\tlog.log(Level.FINE, \">> \" + new String(CSM));\n\t\t\tbyte[] ssm = m.evaluateResponse(CSM);\n\t\t\tlog.log(Level.FINE, \"<< \" + new String(ssm));\n\t\t\tAssert.assertArrayEquals(SSM, ssm);\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\tfail(e.getMessage());\n\t\t}\n\n\t\tassertTrue(m.isComplete());\n\t\tassertEquals(\"user@domain.com\", m.getAuthorizationID());\n\n\t}\n\n\t@Test\n\tpublic void testServerFirstMessageFail_1() throws UnsupportedEncodingException {\n\t\ttry {\n\t\t\tSaslSCRAM m = create(\"QSXCR+Q6sek8bf92\", \"3rfcNHYJY1ZVvWVs7j\", \"pencil\");\n\t\t\tbyte[] r = m.evaluateResponse(\n\t\t\t\t\t\"p=tls-unique,,n=bmalkow,r=SpiXKmhi57DBp5sdE5G3H3ms\".getBytes(StandardCharsets.UTF_8));\n\t\t\tfail();\n\t\t} catch (SaslException e) {\n\t\t\tAssert.assertEquals(\"Invalid request for SCRAM-SHA-1\", e.getMessage());\n\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testServerFirstMessage_Unicode() throws UnsupportedEncodingException {\n\t\tfinal byte[] CFM = \"n,,n=userıÖöŞş,r=fyko+d2lbbFgONRv9qkxdawL\".getBytes(StandardCharsets.UTF_8);\n\t\tfinal byte[] SFM = \"r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096\".getBytes(\n\t\t\t\tStandardCharsets.UTF_8);\n\t\tfinal byte[] CSM = \"c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=vOXsTF9Mn1VrdXTbsN/L3QBi6e8=\".getBytes(\n\t\t\t\tStandardCharsets.UTF_8);\n\t\tfinal byte[] SSM = \"v=s6/uN/3fMxCuRhOsrJTg6eadGzo=\".getBytes(StandardCharsets.UTF_8);\n\n\t\tSaslSCRAM m = create(\"QSXCR+Q6sek8bf92\", \"3rfcNHYJY1ZVvWVs7j\", \"pencil\",\n\t\t\t\t\t\t\t new TestCallbackHandler(\"userıÖöŞş@domain.com\"));\n\t\ttry {\n\t\t\tlog.log(Level.FINE, \">> \" + new String(CFM));\n\t\t\tbyte[] sfm = m.evaluateResponse(CFM);\n\t\t\tlog.log(Level.FINE, \"<< \" + new String(sfm));\n\t\t\tAssert.assertArrayEquals(SFM, sfm);\n\t\t\tlog.log(Level.FINE, \">> \" + new String(CSM));\n\t\t\tbyte[] ssm = m.evaluateResponse(CSM);\n\t\t\tlog.log(Level.FINE, \"<< \" + new String(ssm));\n\t\t\tAssert.assertArrayEquals(SSM, ssm);\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\tfail(e.getMessage());\n\t\t}\n\n\t\tassertTrue(m.isComplete());\n\t\tassertEquals(\"userıÖöŞş@domain.com\", m.getAuthorizationID());\n\n\t}\n\n//\t@Test\n//\tpublic void testServerFirstMessageFail_2() {\n//\t\ttry {\n//\t\t\tSaslSCRAM m = create(\"QSXCR+Q6sek8bf92\", \"3rfcNHYJY1ZVvWVs7j\", \"pencil\");\n//\t\t\tbyte[] r = m.evaluateResponse(\"y,,n=bmalkow,r=SpiXKmhi57DBp5sdE5G3H3ms\".getBytes(\"UTF-8\"));\n//\t\t\tfail();\n//\t\t} catch (SaslException e) {\n//\t\t\tAssert.assertEquals(\"Server supports PLUS. Please use 'p'\", e.getMessage());\n//\n//\t\t}\n//\t}\n\n\t/**\n\t * @param salt password salt\n\t * @param snonce server once\n\t * @param password user password (plaintext)\n\t */\n\tprivate SaslSCRAM create(String salt, String snonce, String password) {\n\t\treturn create(salt, snonce, password, new TestCallbackHandler());\n\t}\n\n\tprivate SaslSCRAM create(String salt, String snonce, String password, TestCallbackHandler h) {\n\t\th.setSalt(salt);\n\t\th.setPassword(password);\n\t\tSaslSCRAM m = new SaslSCRAM(null, h, snonce) {\n\t\t};\n\n\t\treturn m;\n\t}\n\n\tstatic class TestCallbackHandler\n\t\t\timplements CallbackHandler {\n\n\t\tprivate String authorizedId;\n\t\tprivate byte[] bindingData;\n\t\tprivate int iterations = 4096;\n\t\tprivate String name;\n\t\tprivate String password;\n\t\tprivate String salt;\n\n\t\tpublic TestCallbackHandler() {\n\t\t\tthis(\"user@domain.com\");\n\t\t}\n\n\t\tpublic TestCallbackHandler(String jid) {\n\t\t\tauthorizedId = jid;\n\t\t\tname = jid;\n\t\t}\n\n\t\tpublic String getAuthorizedId() {\n\t\t\treturn authorizedId;\n\t\t}\n\n\t\tpublic void setAuthorizedId(String authorizedId) {\n\t\t\tthis.authorizedId = authorizedId;\n\t\t}\n\n\t\tpublic byte[] getBindingData() {\n\t\t\treturn bindingData;\n\t\t}\n\n\t\tpublic void setBindingData(byte[] bindingData) {\n\t\t\tthis.bindingData = bindingData;\n\t\t}\n\n\t\tpublic int getIterations() {\n\t\t\treturn iterations;\n\t\t}\n\n\t\tpublic void setIterations(int iterations) {\n\t\t\tthis.iterations = iterations;\n\t\t}\n\n\t\tpublic String getName() {\n\t\t\treturn name;\n\t\t}\n\n\t\tpublic void setName(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\tpublic String getPassword() {\n\t\t\treturn password;\n\t\t}\n\n\t\tpublic void setPassword(String password) {\n\t\t\tthis.password = password;\n\t\t}\n\n\t\tpublic String getSalt() {\n\t\t\treturn salt;\n\t\t}\n\n\t\tpublic void setSalt(String salt) {\n\t\t\tthis.salt = salt;\n\t\t}\n\n\t\t@SuppressWarnings(\"StatementWithEmptyBody\")\n\t\t@Override\n\t\tpublic void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {\n\t\t\ttry {\n\t\t\t\tfor (Callback callback : callbacks) {\n\t\t\t\t\tif (callback instanceof XMPPSessionCallback) {\n\t\t\t\t\t} else if (callback instanceof ChannelBindingCallback) {\n\t\t\t\t\t\tif (((ChannelBindingCallback) callback).getRequestedBindType() ==\n\t\t\t\t\t\t\t\tAbstractSaslSCRAM.BindType.tls_unique) {\n\t\t\t\t\t\t\t((ChannelBindingCallback) callback).setBindingData(bindingData);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (callback instanceof PBKDIterationsCallback) {\n\t\t\t\t\t\t((PBKDIterationsCallback) callback).setInterations(iterations);\n\t\t\t\t\t} else if (callback instanceof NameCallback) {\n\t\t\t\t\t\t((NameCallback) callback).setName(name);\n\t\t\t\t\t} else if (callback instanceof AuthorizationIdCallback) {\n\t\t\t\t\t\t// nothing to do\n\t\t\t\t\t} else if (callback instanceof SaltCallback) {\n\t\t\t\t\t\t((SaltCallback) callback).setSalt(Base64.decode(salt));\n\t\t\t\t\t} else if (callback instanceof AuthorizeCallback) {\n\t\t\t\t\t\t((AuthorizeCallback) callback).setAuthorized(true);\n\t\t\t\t\t\t((AuthorizeCallback) callback).setAuthorizedID(authorizedId);\n\t\t\t\t\t} else if (callback instanceof ServerKeyCallback) {\n\t\t\t\t\t\tbyte[] saltedPassword = AbstractSaslSCRAM.hi(\"SHA1\", password.getBytes(StandardCharsets.UTF_8),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Base64.decode(salt), iterations);\n\t\t\t\t\t\tbyte[] serverKey = AbstractSaslSCRAM.hmac(new SecretKeySpec(saltedPassword, \"HMacSHA1\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"Server Key\".getBytes());\n\t\t\t\t\t\t((ServerKeyCallback) callback).setServerKey(serverKey);\n\t\t\t\t\t} else if (callback instanceof StoredKeyCallback) {\n\t\t\t\t\t\tbyte[] saltedPassword = AbstractSaslSCRAM.hi(\"SHA1\", password.getBytes(StandardCharsets.UTF_8),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Base64.decode(salt), iterations);\n\t\t\t\t\t\tbyte[] clientKey = AbstractSaslSCRAM.hmac(new SecretKeySpec(saltedPassword, \"HMacSHA1\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"Client Key\".getBytes());\n\t\t\t\t\t\tbyte[] storedKey = MessageDigest.getInstance(\"SHA1\").digest(clientKey);\n\t\t\t\t\t\t((StoredKeyCallback) callback).setStoredKey(storedKey);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new UnsupportedCallbackException(callback, \"Unrecognized Callback \" + callback);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (InvalidKeyException | NoSuchAlgorithmException e) {\n\t\t\t\tthrow new RuntimeException(e);\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/auth/mechanisms/SaslXTOKENTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.auth.mechanisms;\n\nimport junit.framework.TestCase;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Parameterized;\nimport tigase.TestLogger;\nimport tigase.auth.callbacks.AuthorizationIdCallback;\nimport tigase.auth.callbacks.ReplaceServerKeyCallback;\nimport tigase.auth.callbacks.ServerKeyCallback;\nimport tigase.auth.callbacks.SharedSecretKeyCallback;\nimport tigase.util.Base64;\nimport tigase.xmpp.jid.BareJID;\n\nimport javax.crypto.Mac;\nimport javax.crypto.spec.SecretKeySpec;\nimport javax.security.auth.callback.Callback;\nimport javax.security.auth.callback.CallbackHandler;\nimport javax.security.auth.callback.NameCallback;\nimport javax.security.auth.callback.UnsupportedCallbackException;\nimport javax.security.sasl.AuthorizeCallback;\nimport javax.security.sasl.SaslException;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\nimport java.security.InvalidKeyException;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.SecureRandom;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.logging.Logger;\n\n@RunWith(Parameterized.class)\npublic class SaslXTOKENTest extends TestCase {\n\n\tprivate static final Logger log = TestLogger.getLogger(SaslSCRAMTest.class);\n\tprivate byte[] serverKey;\n\tprivate byte[] newServerKey;\n\tprivate final SecureRandom random = new SecureRandom();\n\tprivate SaslXTOKEN sasl;\n\tprivate final byte[] sharedKey;\n\n\tprivate final boolean printTestVectors = false;\n\n\tpublic SaslXTOKENTest(byte[] sharedKey) {\n\t\tthis.sharedKey = sharedKey;\n\t}\n\n\t@Parameterized.Parameters\n\tpublic static Collection<Object[]> data() {\n\t\tbyte[] sharedKey = new byte[32];\n\t\tSecureRandom random = new SecureRandom();\n\t\trandom.nextBytes(sharedKey);\n\t\treturn Arrays.asList(new Object[][] {\n\t\t\t\t{ null },\n\t\t\t\t{ sharedKey }\n\t\t});\n\t}\n\n\t@Override\n\t@Before\n\tpublic void setUp() {\n\t\tserverKey = new byte[32];\n\t\trandom.nextBytes(serverKey);\n\t\tMap<? super String, ?> props = new HashMap<String, Object>();\n\t\tCallbackHandler callbackHandler = new CallbackHandler() {\n\n\t\t\tprivate String username;\n\n\t\t\t@Override\n\t\t\tpublic void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {\n\t\t\t\tfor (Callback callback : callbacks) {\n\t\t\t\t\tif (callback instanceof NameCallback) {\n\t\t\t\t\t\tBareJID jid = BareJID.bareJIDInstanceNS(((NameCallback) callback).getDefaultName());\n\t\t\t\t\t\tif (jid.getLocalpart() == null || !\"domain.com\".equalsIgnoreCase(jid.getDomain())) {\n\t\t\t\t\t\t\tjid = BareJID.bareJIDInstanceNS(((NameCallback) callback).getDefaultName(), \"domain.com\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tusername = jid.toString();\n\t\t\t\t\t\t((NameCallback) callback).setName(username);\n\t\t\t\t\t} else if (callback instanceof ServerKeyCallback) {\n\t\t\t\t\t\t((ServerKeyCallback) callback).setServerKey(serverKey);\n\t\t\t\t\t} else if (callback instanceof AuthorizationIdCallback) {\n\t\t\t\t\t\t// there is nothing to do..\n\t\t\t\t\t} else if (callback instanceof AuthorizeCallback) {\n\t\t\t\t\t\tboolean a = ((AuthorizeCallback) callback).getAuthorizationID()\n\t\t\t\t\t\t\t\t.equals(((AuthorizeCallback) callback).getAuthenticationID());\n\t\t\t\t\t\t((AuthorizeCallback) callback).setAuthorized(a);\n\t\t\t\t\t\tif (a) {\n\t\t\t\t\t\t\t((AuthorizeCallback) callback).setAuthorizedID(\n\t\t\t\t\t\t\t\t\t((AuthorizeCallback) callback).getAuthorizationID());\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (callback instanceof ReplaceServerKeyCallback) {\n\t\t\t\t\t\t((ReplaceServerKeyCallback) callback).setNewServerKey(newServerKey);\n\t\t\t\t\t} else if (callback instanceof SharedSecretKeyCallback) {\n\t\t\t\t\t\t((SharedSecretKeyCallback) callback).setSecret(sharedKey);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new UnsupportedCallbackException(callback);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tsasl = new SaslXTOKEN(props, callbackHandler);\n\t}\n\n\t@Test\n\tpublic void testEmptyToken() {\n\t\ttry {\n\t\t\tsasl.evaluateResponse(new byte[0]);\n\t\t\tfail(\"Exception must be thrown\");\n\t\t} catch (SaslException e) {\n\t\t\t// should be thrown\n\t\t}\n\t\tassertFalse(sasl.isComplete());\n\t}\n\n\t@Test\n\tpublic void testValidToken() throws InvalidKeyException, NoSuchAlgorithmException {\n\t\tnewServerKey = null;\n\t\tbyte[] data = new byte[32];\n\t\trandom.nextBytes(data);\n\n\t\tMac mac = Mac.getInstance(\"HmacSHA256\");\n\t\tmac.init(new SecretKeySpec(serverKey, \"SHA-256\"));\n\t\tmac.update(data);\n\t\tif (sharedKey != null) {\n\t\t\tmac.update(sharedKey);\n\t\t}\n\t\tbyte[] token = mac.doFinal();\n\t\t\n\t\ttry {\n\t\t\tbyte[] jid = \"test@domain.com\".getBytes(StandardCharsets.UTF_8);\n\t\t\tbyte[] response = new byte[data.length + 1 + token.length + 1 + jid.length];\n\t\t\tSystem.arraycopy(data, 0, response,0 , 32);\n\t\t\tSystem.arraycopy(token, 0, response,33 , 32);\n\t\t\tSystem.arraycopy(jid, 0, response,66 , jid.length);\n\t\t\tbyte[] finish = sasl.evaluateResponse(response);\n\t\t\tAssert.assertArrayEquals(jid,finish);\n\t\t} catch (SaslException e) {\n\t\t\t// shouldn't be thrown\n\t\t\te.printStackTrace();\n\t\t\tfail(\"Exception must not be thrown\");\n\t\t}\n\t\tassertTrue(sasl.isComplete());\n\t}\n\n\t@Test\n\tpublic void testValidTokenWithNewKey() throws InvalidKeyException, NoSuchAlgorithmException {\n\t\tnewServerKey = new byte[32];\n\t\trandom.nextBytes(newServerKey);\n\t\tbyte[] data = new byte[32];\n\t\trandom.nextBytes(data);\n\n\t\tMac mac = Mac.getInstance(\"HmacSHA256\");\n\t\tmac.init(new SecretKeySpec(serverKey, \"SHA-256\"));\n\t\tmac.update(data);\n\t\tif (sharedKey != null) {\n\t\t\tmac.update(sharedKey);\n\t\t}\n\t\tbyte[] token = mac.doFinal();\n\n\t\ttry {\n\t\t\tbyte[] jid = \"test@domain.com\".getBytes(StandardCharsets.UTF_8);\n\t\t\tbyte[] response = new byte[data.length + 1 + token.length + 1 + jid.length];\n\t\t\tSystem.arraycopy(data, 0, response,0 , 32);\n\t\t\tSystem.arraycopy(token, 0, response,33 , 32);\n\t\t\tSystem.arraycopy(jid, 0, response,66 , jid.length);\n\t\t\tbyte[] finish = sasl.evaluateResponse(response);\n\t\t\tbyte[] exp = new byte[jid.length + 1 + newServerKey.length];\n\t\t\tSystem.arraycopy(jid, 0, exp, 0, jid.length);\n\t\t\tSystem.arraycopy(newServerKey, 0, exp, jid.length + 1, newServerKey.length);\n\t\t\tAssert.assertArrayEquals(exp,finish);\n\t\t\tprint(jid, serverKey, response, finish, newServerKey);\n\n\t\t\tserverKey = newServerKey;\n\t\t\tnewServerKey = null;\n\t\t\tmac = Mac.getInstance(\"HmacSHA256\");\n\t\t\tmac.init(new SecretKeySpec(serverKey, \"SHA-256\"));\n\t\t\tmac.update(data);\n\t\t\tif (sharedKey != null) {\n\t\t\t\tmac.update(sharedKey);\n\t\t\t}\n\t\t\ttoken = mac.doFinal();\n\t\t\t\n\t\t\tresponse = new byte[data.length + 1 + token.length + 1 + jid.length];\n\t\t\tSystem.arraycopy(data, 0, response,0 , 32);\n\t\t\tSystem.arraycopy(token, 0, response,33 , 32);\n\t\t\tSystem.arraycopy(jid, 0, response,66 , jid.length);\n\t\t\tfinish = sasl.evaluateResponse(response);\n\t\t\tAssert.assertArrayEquals(jid,finish);\n\t\t\tprint(jid, serverKey, response, finish, newServerKey);\n\t\t} catch (SaslException e) {\n\t\t\t// shouldn't be thrown\n\t\t\te.printStackTrace();\n\t\t\tfail(\"Exception must not be thrown\");\n\t\t}\n\t\tassertTrue(sasl.isComplete());\n\t}\n\n\tprivate void print(byte[] jid, byte[] serverKey, byte[] clientSaslRequest, byte[] serverSaslResponse, byte[] newServerKey) {\n\t\tif (!printTestVectors) {\n\t\t\treturn;\n\t\t}\n\t\tSystem.out.println(\"Authentication\" + (newServerKey == null ? \"\" : \" with key replace\"));\n\t\tSystem.out.println(\"JID: \" + new String(jid, StandardCharsets.UTF_8));\n\t\tSystem.out.println(\"Server key: \" + Base64.encode(serverKey));\n\t\tbyte[] data = new byte[serverKey.length + 1 + jid.length];\n\t\tSystem.arraycopy(serverKey, 0, data, 0, serverKey.length);\n\t\tSystem.arraycopy(jid, 0, data, serverKey.length + 1, jid.length);\n\t\tSystem.out.println(\"QR code: \" + Base64.encode(data));\n\t\tSystem.out.println(\"SASL payload sent by client: \" + Base64.encode(clientSaslRequest));\n\t\tSystem.out.println(\"SASL payload sent by server: \" + Base64.encode(serverSaslResponse));\n\t\tif (newServerKey != null) {\n\t\t\tSystem.out.println(\"Server key (new): \" + Base64.encode(newServerKey));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/cluster/ClusterConnectionSelectorTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster;\n\nimport junit.framework.TestCase;\nimport org.junit.Test;\nimport tigase.cluster.api.ClusterConnectionHandler;\nimport tigase.server.Packet;\nimport tigase.server.Priority;\nimport tigase.xml.Element;\nimport tigase.xmpp.XMPPIOService;\n\nimport java.util.*;\n\nimport static tigase.cluster.ClusterConnectionSelector.CLUSTER_SYS_CONNECTIONS_PER_NODE_PROP_KEY;\n\n/**\n * @author andrzej\n */\npublic class ClusterConnectionSelectorTest\n\t\textends TestCase {\n\n\t@Test\n\tpublic void testSelectConnection() throws Exception {\n\t\tClusterConnection conn = new ClusterConnection(\"test\");\n\t\tClusterConnectionSelector selector = new ClusterConnectionSelector();\n\t\tselector.setClusterConnectionHandler(new ClusterConnectionHandler() {\n\n\t\t\t@Override\n\t\t\tpublic int hashCodeForPacket(Packet packet) {\n\t\t\t\treturn packet.getStanzaFrom().hashCode();\n\t\t\t}\n\t\t});\n\n\t\tMap<String, Object> props = new HashMap<>();\n\t\tprops.put(CLUSTER_SYS_CONNECTIONS_PER_NODE_PROP_KEY, 1);\n\t\tselector.setProperties(props);\n\n\t\tElement el = new Element(\"iq\", new String[]{\"from\"}, new String[]{\"test1\"});\n\t\tPacket p = Packet.packetInstance(el);\n\t\tassertNull(selector.selectConnection(p, conn));\n\n\t\tXMPPIOService<Object> serv1 = new XMPPIOService<Object>();\n\t\tconn.addConn(serv1);\n\t\tassertEquals(serv1, selector.selectConnection(p, conn));\n\n\t\tp.setPriority(Priority.SYSTEM);\n\t\tassertEquals(serv1, selector.selectConnection(p, conn));\n\n\t\tp.setPriority(null);\n\t\tXMPPIOService<Object> serv2 = new XMPPIOService<Object>();\n\t\tconn.addConn(serv2);\n\t\tassertEquals(2, conn.size());\n\t\tassertEquals(serv2, selector.selectConnection(p, conn));\n\n\t\tp.setPriority(Priority.SYSTEM);\n\t\tassertEquals(serv1, selector.selectConnection(p, conn));\n\n\t\tp.setPriority(null);\n\t\tXMPPIOService<Object> serv3 = new XMPPIOService<Object>();\n\t\tconn.addConn(serv3);\n\t\tassertEquals(3, conn.size());\n\t\tassertNotSame(serv1, selector.selectConnection(p, conn));\n\n\t\tp.setPriority(Priority.SYSTEM);\n\t\tassertEquals(serv1, selector.selectConnection(p, conn));\n\n\t\tel = new Element(\"iq\", new String[]{\"from\"}, new String[]{\"test2\"});\n\t\tp = Packet.packetInstance(el);\n\t\tassertEquals(3, conn.size());\n\t\tassertNotSame(serv1, selector.selectConnection(p, conn));\n\n\t\tel = new Element(\"iq\", new String[]{\"from\"}, new String[]{\"test3\"});\n\t\tp = Packet.packetInstance(el);\n\t\tassertEquals(3, conn.size());\n\t\tassertNotSame(serv1, selector.selectConnection(p, conn));\n\n\t\tel = new Element(\"iq\", new String[]{\"from\"}, new String[]{\"test4\"});\n\t\tp = Packet.packetInstance(el);\n\t\tassertEquals(3, conn.size());\n\t\tassertNotSame(serv1, selector.selectConnection(p, conn));\n\t}\n\n\t@Test\n\tpublic void testSelectConnectionFor2() throws Exception {\n\t\tClusterConnection conn = new ClusterConnection(\"test\");\n\t\tClusterConnectionSelector selector = new ClusterConnectionSelector();\n\t\tselector.setClusterConnectionHandler(new ClusterConnectionHandler() {\n\n\t\t\t@Override\n\t\t\tpublic int hashCodeForPacket(Packet packet) {\n\t\t\t\treturn packet.getStanzaFrom().hashCode();\n\t\t\t}\n\t\t});\n\t\tMap<String, Object> props = new HashMap<>();\n\t\tprops.put(CLUSTER_SYS_CONNECTIONS_PER_NODE_PROP_KEY, 2);\n\t\tselector.setProperties(props);\n\n\t\tElement el = new Element(\"iq\", new String[]{\"from\"}, new String[]{\"test1\"});\n\t\tPacket p = Packet.packetInstance(el);\n\t\tassertNull(selector.selectConnection(p, conn));\n\n\t\tXMPPIOService<Object> serv1 = new XMPPIOService<Object>();\n\t\tconn.addConn(serv1);\n\t\tassertEquals(serv1, selector.selectConnection(p, conn));\n\n\t\tp.setPriority(Priority.SYSTEM);\n\t\tassertEquals(serv1, selector.selectConnection(p, conn));\n\n\t\tp.setPriority(null);\n\t\tXMPPIOService<Object> serv2 = new XMPPIOService<Object>();\n\t\tconn.addConn(serv2);\n\t\tSet<XMPPIOService<Object>> sysServs = new HashSet<>(Arrays.asList(serv1, serv2));\n\t\tassertEquals(2, conn.size());\n\t\tassertTrue(sysServs.contains(selector.selectConnection(p, conn)));\n\n\t\tp.setPriority(Priority.SYSTEM);\n\t\tassertTrue(sysServs.contains(selector.selectConnection(p, conn)));\n\n\t\tp.setPriority(null);\n\t\tXMPPIOService<Object> serv3 = new XMPPIOService<Object>();\n\t\tconn.addConn(serv3);\n\t\tassertEquals(3, conn.size());\n\t\tassertSame(serv3, selector.selectConnection(p, conn));\n\n\t\tp.setPriority(Priority.SYSTEM);\n\t\tassertTrue(sysServs.contains(selector.selectConnection(p, conn)));\n\n\t\tel = new Element(\"iq\", new String[]{\"from\"}, new String[]{\"test2\"});\n\t\tp = Packet.packetInstance(el);\n\t\tassertEquals(3, conn.size());\n\t\tassertSame(serv3, selector.selectConnection(p, conn));\n\n\t\tel = new Element(\"iq\", new String[]{\"from\"}, new String[]{\"test3\"});\n\t\tp = Packet.packetInstance(el);\n\t\tassertEquals(3, conn.size());\n\t\tassertSame(serv3, selector.selectConnection(p, conn));\n\n\t\tel = new Element(\"iq\", new String[]{\"from\"}, new String[]{\"test4\"});\n\t\tp = Packet.packetInstance(el);\n\t\tassertEquals(3, conn.size());\n\t\tassertSame(serv3, selector.selectConnection(p, conn));\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/cluster/api/ClusterElementTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n/*\n* To change this template, choose Tools | Templates\n* and open the template in the editor.\n */\npackage tigase.cluster.api;\n\nimport junit.framework.TestCase;\nimport org.junit.Test;\nimport tigase.xml.DomBuilderHandler;\nimport tigase.xml.Element;\nimport tigase.xml.SimpleParser;\n\n/**\n * @author andrzej\n */\npublic class ClusterElementTest\n\t\textends TestCase {\n\n\t@Test\n\t@SuppressWarnings(\"deprecation\")\n\tpublic void testGetMethodName() {\n\t\tSimpleParser parser = new SimpleParser();\n\t\tDomBuilderHandler handler = new DomBuilderHandler();\n\t\tchar[] data = \"<cluster to=\\\"sess-man@blue\\\" type=\\\"set\\\" id=\\\"cl-6627\\\" xmlns=\\\"tigase:cluster\\\" from=\\\"sess-man@green\\\"><control><visited-nodes><node-id>sess-man@green</node-id></visited-nodes><method-call name=\\\"packet-forward-sm-cmd\\\"/><first-node>sess-man@green</first-node></control><data><presence to=\\\"test2@test\\\" xmlns=\\\"jabber:client\\\" from=\\\"test1@test/test\\\"><status/><priority>5</priority></presence></data></cluster>\"\n\t\t\t\t.toCharArray();\n\n\t\tparser.parse(handler, data, 0, data.length);\n\n\t\tElement elem = handler.getParsedElements().poll();\n\n\t\tassertEquals(\"packet-forward-sm-cmd\",\n\t\t\t\t\t elem.findChild(\"/cluster/control/method-call\").getAttributeStaticStr(\"name\"));\n\n//  assertEquals(\"cluster/control/method-call\".split(\"/\"),\n//               ClusterElement.CLUSTER_METHOD_PATH);\n\t\tClusterElement clElem = new ClusterElement(elem);\n\n\t\tassertEquals(\"packet-forward-sm-cmd\", clElem.getMethodName());\n\t}\n}\n\n"
  },
  {
    "path": "src/test/java/tigase/cluster/strategy/DefaultClusterStrategyTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.cluster.strategy;\n\nimport org.junit.Test;\nimport tigase.cluster.api.ClusterControllerIfc;\nimport tigase.cluster.api.CommandListener;\nimport tigase.cluster.api.SessionManagerClusteredIfc;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.kernel.AbstractKernelTestCase;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Packet;\nimport tigase.util.Algorithms;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.XMPPSession;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport static junit.framework.TestCase.assertTrue;\nimport static org.junit.Assert.assertEquals;\n\npublic class DefaultClusterStrategyTest extends AbstractKernelTestCase {\n\t\n\t@Override\n\tprotected void registerBeans(Kernel kernel) {\n\t\tsuper.registerBeans(kernel);\n\t\tkernel.registerBean(\"eventbus\").asInstance(EventBusFactory.getInstance()).exportable().exec();\n\t\tkernel.registerBean(\"sess-man\").asInstance(new DummySessionManagerClustered()).exportable().exec();\n\t\tkernel.registerBean(\"cluster-contr\").asInstance(new DummyClusterController()).exportable().exec();\n\t\tkernel.registerBean(\"strategy\").asClass(getStrategyClass()).setActive(true).exportable().exec();\n\t}\n\n\tprotected Class<? extends DefaultClusteringStrategyAbstract> getStrategyClass() {\n\t\treturn DefaultClusteringStrategy.class;\n\t}\n\t\n\t@Test\n\tpublic void testIqResponseRedirection() throws TigaseStringprepException {\n\t\tDefaultClusteringStrategyAbstract strategy = getInstance(DefaultClusteringStrategyAbstract.class);\n\t\tassertEquals(Collections.emptyList(), strategy.getNodesConnected());\n\n\t\tString node1 = UUID.randomUUID().toString();\n\t\tString node2 = UUID.randomUUID().toString();\n\t\tString node3 = UUID.randomUUID().toString(); // local node\n\n\t\tJID senderJid = JID.jidInstance(UUID.randomUUID().toString(), UUID.randomUUID().toString(), null);\n\n\t\tDummySessionManagerClustered sm = getInstance(DummySessionManagerClustered.class);\n\t\tsm.nodeConnected(node1);\n\t\tstrategy.nodeConnected(JID.jidInstance(\"sess-man\", node1));\n\t\tsm.nodeConnected(node2);\n\t\tstrategy.nodeConnected(JID.jidInstance(\"sess-man\", node2));\n\n\t\tPacket packet = Packet.packetInstance(new Element(\"iq\").withAttribute(\"type\", \"result\"), senderJid, JID.jidInstance(null, \"localhost\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAlgorithms.sha256(node2)));\n\t\tassertTrue(strategy.isSuitableForForward(packet));\n\t\tassertTrue(strategy.isIqResponseToNode(packet));\n\t\tassertEquals(Collections.singletonList(JID.jidInstance(\"sess-man\", node2)), strategy.getNodesForIqResponse(packet));\n\t\tassertEquals(Collections.singletonList(JID.jidInstance(\"sess-man\", node2)), strategy.getNodesForPacketForward(sm.getComponentId(), null, packet));\n\n\t\tpacket = Packet.packetInstance(new Element(\"iq\").withAttribute(\"type\", \"result\"), senderJid, JID.jidInstance(null, \"localhost\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAlgorithms.sha256(node3)));\n\t\tassertTrue(strategy.isSuitableForForward(packet));\n\t\tassertTrue(strategy.isIqResponseToNode(packet));\n\t\tassertEquals(null, strategy.getNodesForIqResponse(packet));\n\t\tassertEquals(null, strategy.getNodesForPacketForward(sm.getComponentId(), null, packet));\n\t}\n\n\tprivate class DummyClusterController implements ClusterControllerIfc {\n\n\t\tprivate List<String> nodes = new ArrayList<>();\n\n\t\t@Override\n\t\tpublic void handleClusterPacket(Element packet) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void nodeConnected(String addr) {\n\t\t\tif (!nodes.contains(addr)) {\n\t\t\t\tnodes.add(addr);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void nodeDisconnected(String addr) {\n\t\t\tnodes.remove(addr);\n\t\t}\n\n\t\t@Override\n\t\tpublic void removeCommandListener(CommandListener listener) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void sendToNodes(String command, Map<String, String> data, Queue<Element> packets, JID fromNode,\n\t\t\t\t\t\t\t\tSet<JID> visitedNodes, JID... toNodes) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void sendToNodes(String command, Queue<Element> packets, JID fromNode, Set<JID> visitedNodes,\n\t\t\t\t\t\t\t\tJID... toNodes) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void sendToNodes(String command, Map<String, String> data, JID fromNode, Set<JID> visitedNodes,\n\t\t\t\t\t\t\t\tJID... toNodes) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void sendToNodes(String command, Map<String, String> data, JID fromNode, JID... toNodes) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void sendToNodes(String command, JID fromNode, JID... toNodes) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void sendToNodes(String command, Element packet, JID fromNode, Set<JID> visitedNodes, JID... toNodes) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void sendToNodes(String command, Map<String, String> data, Element packet, JID fromNode,\n\t\t\t\t\t\t\t\tSet<JID> visitedNodes, JID... toNodes) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void setCommandListener(CommandListener listener) {\n\n\t\t}\n\t}\n\n\tprivate class DummySessionManagerClustered\n\t\t\timplements SessionManagerClusteredIfc {\n\n\t\t@Override\n\t\tpublic boolean fastAddOutPacket(Packet packet) {\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic void processPacket(Packet el_packet, XMPPResourceConnection conn) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void processPresenceUpdate(XMPPSession session, Element element) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic XMPPResourceConnection getXMPPResourceConnection(Packet el_packet) {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic ConcurrentHashMap<JID, XMPPResourceConnection> getXMPPResourceConnections() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic ConcurrentHashMap<BareJID, XMPPSession> getXMPPSessions() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean hasXMPPResourceConnectionForConnectionJid(JID connJid) {\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic JID getComponentId() {\n\t\t\treturn JID.jidInstanceNS(\"sess-man@\" + UUID.randomUUID().toString());\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleLogin(BareJID userId, XMPPResourceConnection conn) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleDomainChange(String domain, XMPPResourceConnection conn) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleLogout(BareJID userId, XMPPResourceConnection conn) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void handlePresenceSet(XMPPResourceConnection conn) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleResourceBind(XMPPResourceConnection conn) {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isLocalDomain(String domain, boolean includeComponents) {\n\t\t\treturn \"localhost\".equals(domain);\n\t\t}\n\n\t\tprivate List<JID> connectedNodes = new ArrayList<>();\n\n\t\t@Override\n\t\tpublic List<JID> getNodesConnected() {\n\t\t\treturn connectedNodes;\n\t\t}\n\n\t\tpublic String getName() {\n\t\t\treturn \"sess-man\";\n\t\t}\n\n\t\tpublic void nodeConnected(String node) {\n\t\t\tJID jid = JID.jidInstanceNS(getName(), node, null);\n\t\t\tif (!connectedNodes.contains(jid) && !getComponentId().equals(jid)) {\n\t\t\t\tJID[] tmp = connectedNodes.toArray(new JID[connectedNodes.size() + 1]);\n\t\t\t\ttmp[tmp.length - 1] = jid;\n\t\t\t\tArrays.sort(tmp);\n\t\t\t\tint pos = Arrays.binarySearch(tmp, jid);\n\t\t\t\tconnectedNodes.add(pos, jid);\n\t\t\t}\n\t\t}\n\n\t\tpublic void nodeDisconnected(String node) {\n\t\t\tJID jid = JID.jidInstanceNS(getName(), node, null);\n\t\t\tconnectedNodes.remove(jid);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/component/Bean1.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.component;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.xmpp.jid.JID;\n\n/**\n * Created by bmalkow on 19.10.2015.\n */\n@Bean(name = \"bean1\", active = true)\npublic class Bean1 {\n\n\tprivate String field1;\n\t@ConfigField(desc = \"Field 02\")\n\tprivate int field2;\n\t@ConfigField(desc = \"Field 03\", alias = \"alias1\")\n\tprivate JID field3;\n\t@ConfigField(desc = \"Field 04\", alias = \"alias2\")\n\tprivate JID field4;\n\n\tpublic String getField1() {\n\t\treturn field1;\n\t}\n\n\tpublic Bean1 setField1(String field1) {\n\t\tthis.field1 = field1;\n\t\treturn this;\n\t}\n\n\tpublic int getField2() {\n\t\treturn field2;\n\t}\n\n\tpublic Bean1 setField2(int field2) {\n\t\tthis.field2 = field2;\n\t\treturn this;\n\t}\n\n\tpublic JID getField3() {\n\t\treturn field3;\n\t}\n\n\tpublic Bean1 setField3(JID field3) {\n\t\tthis.field3 = field3;\n\t\treturn this;\n\t}\n\n\tpublic JID getField4() {\n\t\treturn field4;\n\t}\n\n\tpublic Bean1 setField4(JID field4) {\n\t\tthis.field4 = field4;\n\t\treturn this;\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/component/DSLBeanConfiguratorTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.component;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport tigase.kernel.DefaultTypesConverter;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.beans.config.ConfigFieldType;\nimport tigase.kernel.core.Kernel;\n\nimport java.io.IOException;\nimport java.io.StringWriter;\nimport java.util.HashMap;\n\npublic class DSLBeanConfiguratorTest {\n\n\t@Test\n\tpublic void dumpConfiguration() throws IOException {\n\n\t\tfinal Kernel kernel = new Kernel();\n\t\tkernel.registerBean(DefaultTypesConverter.class).exec();\n\t\tkernel.registerBean(DSLBeanConfigurator.class).exec();\n\t\tfinal HashMap<String, Object> props = new HashMap<>();\n\t\tfinal HashMap<String, Object> testBeanProps = new HashMap<>();\n\t\tfinal String plainFieldValue = \"PlainFieldValue\";\n\t\tfinal String jdbcUrlPassword = \"tigase_password\";\n\t\ttestBeanProps.put(\"plainConfigField\", plainFieldValue);\n\t\ttestBeanProps.put(\"jdbcUrl\",\n\t\t\t\t\t\t  \"jdbc:postgresql://localhost/tigasedb?user=tigasedb&password=\" + jdbcUrlPassword + \"&useSSL=false\");\n\t\tfinal String passwordFieldValue = \"PasswordFieldValue\";\n\t\ttestBeanProps.put(\"passwordField\", passwordFieldValue);\n\t\tprops.put(\"TestBean\", testBeanProps);\n\t\tfinal DSLBeanConfigurator configurator = kernel.getInstance(DSLBeanConfigurator.class);\n\t\tconfigurator.setProperties(props);\n\n\t\tkernel.registerBean(TestBean.class).exec();\n\n\t\tfinal StringWriter stringWriter = new StringWriter();\n\n\t\tconfigurator.dumpConfiguration(stringWriter);\n\n\t\tAssert.assertTrue(stringWriter.toString().contains(plainFieldValue));\n\t\tAssert.assertFalse(stringWriter.toString().contains(jdbcUrlPassword));\n\t\tAssert.assertFalse(stringWriter.toString().contains(passwordFieldValue));\n\t}\n\n\t@Bean(name = \"TestBean\", active = true, parent = Kernel.class)\n\tpublic static class TestBean {\n\n\t\t@ConfigField(desc = \"JDBC URL field that should have only password obfuscated\", type = ConfigFieldType.JdbcUrl)\n\t\tString jdbcUrl;\n\t\t@ConfigField(desc = \"Password field that should be completely obfuscated\", type = ConfigFieldType.Password)\n\t\tString passwordField;\n\t\t@ConfigField(desc = \"Plain field without obfuscation\")\n\t\tString plainConfigField;\n\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/conf/ConfigHelperTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\nimport org.junit.Test;\nimport tigase.cluster.ClientConnectionClustered;\nimport tigase.cluster.SessionManagerClustered;\nimport tigase.cluster.strategy.DefaultClusteringStrategy;\nimport tigase.kernel.beans.config.AbstractBeanConfigurator;\nimport tigase.server.xmppclient.ClientConnectionManager;\nimport tigase.server.xmppclient.StreamManagementIOProcessor;\nimport tigase.xmpp.impl.JabberIqRegister;\nimport tigase.xmpp.impl.SaslAuth;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static org.junit.Assert.*;\n\n/**\n * Created by andrzej on 08.11.2016.\n */\npublic class ConfigHelperTest {\n\n\t@Test\n\tpublic void test_merge() {\n\t\tMap<String, Object> prop1 = new HashMap<>();\n\t\t// setting global properties\n\t\tprop1.put(\"test1\", \"123\");\n\t\tprop1.put(\"test2\", \"456\");\n\n\t\tAbstractBeanConfigurator.BeanDefinition bean1 = new AbstractBeanConfigurator.BeanDefinition();\n\t\tbean1.setBeanName(\"sess-man\");\n\t\tbean1.setClazzName(SessionManagerClustered.class.getCanonicalName());\n\t\tbean1.setActive(true);\n\n\t\tbean1.put(\"plugins\", Arrays.asList(JabberIqRegister.ID, SaslAuth.ID));\n\t\tbean1.computeIfAbsent(\"map\", o -> {\n\t\t\tHashMap<String, Object> tmp = new HashMap<>();\n\t\t\ttmp.put(\"left\", \"right\");\n\t\t\treturn tmp;\n\t\t});\n\t\tbean1.computeIfAbsent(\"strategy\", name -> {\n\t\t\tAbstractBeanConfigurator.BeanDefinition strategyBean = new AbstractBeanConfigurator.BeanDefinition();\n\t\t\tstrategyBean.setBeanName((String) name);\n\t\t\tstrategyBean.setClazzName(DefaultClusteringStrategy.class.getCanonicalName());\n\t\t\tstrategyBean.setActive(true);\n\t\t\tstrategyBean.put(\"force\", true);\n\t\t\treturn strategyBean;\n\t\t});\n\n\t\tprop1.put(bean1.getBeanName(), bean1);\n\n\t\tAbstractBeanConfigurator.BeanDefinition bean2 = new AbstractBeanConfigurator.BeanDefinition();\n\t\tbean2.setBeanName(\"c2s\");\n\t\tbean2.setClazzName(ClientConnectionManager.class.getCanonicalName());\n\t\tbean2.setActive(true);\n\n\t\tbean2.put(\"processors\", Arrays.asList(StreamManagementIOProcessor.XMLNS));\n\n\t\tprop1.put(bean2.getBeanName(), bean2);\n\n\t\tMap<String, Object> prop2 = new HashMap<>();\n\t\tprop2.put(\"test3\", 789);\n\n\t\tprop2.computeIfAbsent(\"sess-man\", s -> {\n\t\t\tMap<String, Object> sessMan = new HashMap<>();\n\t\t\tsessMan.put(\"plugins\", Arrays.asList(\"presence-state\", \"presence-subscription\"));\n\t\t\tsessMan.computeIfAbsent(\"map\", o -> {\n\t\t\t\tHashMap<String, Object> tmp = new HashMap<>();\n\t\t\t\ttmp.put(\"up\", \"down\");\n\t\t\t\treturn tmp;\n\t\t\t});\n\t\t\tsessMan.computeIfAbsent(\"strategy\", name -> {\n\t\t\t\tMap<String, Object> sMap = new HashMap<>();\n\t\t\t\tsMap.put(\"force\", false);\n\t\t\t\treturn sMap;\n\t\t\t});\n\t\t\treturn sessMan;\n\t\t});\n\n\t\tAbstractBeanConfigurator.BeanDefinition bean3 = new AbstractBeanConfigurator.BeanDefinition();\n\t\tbean3.setBeanName(\"c2s\");\n\t\tbean3.setClazzName(ClientConnectionClustered.class.getCanonicalName());\n\t\tbean3.setActive(true);\n\n\t\tbean3.put(\"test4\", 987);\n\n\t\tprop2.put(bean3.getBeanName(), bean3);\n\n\t\tMap<String, Object> result = ConfigHelper.merge(prop1, prop2);\n\n\t\tassertEquals(\"123\", result.get(\"test1\"));\n\t\tassertEquals(\"456\", result.get(\"test2\"));\n\n\t\tAbstractBeanConfigurator.BeanDefinition bean1a = ((AbstractBeanConfigurator.BeanDefinition) result.get(\n\t\t\t\tbean1.getBeanName()));\n\t\tassertEquals(bean1.getClazzName(), bean1a.getClazzName());\n\t\tassertEquals(bean1.isActive(), bean1a.isActive());\n\t\tassertEquals(((Map) prop2.get(\"sess-man\")).get(\"plugins\"), bean1a.get(\"plugins\"));\n\t\tassertEquals(\"right\", ((Map) bean1a.get(\"map\")).get(\"left\"));\n\t\tassertEquals(\"down\", ((Map) bean1a.get(\"map\")).get(\"up\"));\n\n\t\tAbstractBeanConfigurator.BeanDefinition sBean = (AbstractBeanConfigurator.BeanDefinition) bean1a.get(\n\t\t\t\t\"strategy\");\n\t\tassertEquals(DefaultClusteringStrategy.class.getCanonicalName(), sBean.getClazzName());\n\t\tassertFalse((Boolean) sBean.get(\"force\"));\n\n\t\tAbstractBeanConfigurator.BeanDefinition bean2a = (AbstractBeanConfigurator.BeanDefinition) result.get(\n\t\t\t\tbean2.getBeanName());\n\t\tassertEquals(bean3.getClazzName(), bean2a.getClazzName());\n\t\tassertEquals(bean3.isActive(), bean2a.isActive());\n\t\tassertEquals(987, bean2a.get(\"test4\"));\n\t\tassertNull(bean2a.get(\"test3\"));\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/conf/ConfigHolderTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\nimport org.junit.Test;\nimport tigase.io.SSLContextContainer;\nimport tigase.kernel.beans.config.AbstractBeanConfigurator;\nimport tigase.server.CmdAcl;\nimport tigase.server.ext.AbstractCompDBRepository;\nimport tigase.server.ext.ComponentProtocol;\nimport tigase.xmpp.impl.roster.DynamicRosterTest;\nimport tigase.xmpp.impl.roster.DynamicRosterTest123;\n\nimport java.io.*;\nimport java.util.*;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport static org.junit.Assert.*;\nimport static tigase.conf.ConfiguratorAbstract.PROPERTY_FILENAME_PROP_KEY;\n\n/**\n * Created by andrzej on 27.04.2017.\n */\npublic class ConfigHolderTest {\n\n\t@Test\n\tpublic void testFormatDetection() throws IOException, ConfigReader.ConfigException {\n\t\tOldConfigHolder holder = new OldConfigHolder();\n\n\t\tFile tmp = File.createTempFile(\"test_\", \".properties\");\n\t\ttry (Writer w = new FileWriter(tmp)) {\n\t\t\tw.append(\"--auth-db[domain4.com]=tigase-custom\")\n\t\t\t\t\t.append(\"\\n\")\n\t\t\t\t\t.append(\"--auth-db-uri[domain4.com]=jdbc:mysql://db14.domain4.com/dbname?user&password\")\n\t\t\t\t\t.append(\"\\n\")\n\t\t\t\t\t.append(\"basic-conf/auth-repo-params/domain4.com/user-login-query={ call UserLogin(?, ?) }\")\n\t\t\t\t\t.append(\"\\n\")\n\t\t\t\t\t.append(\"basic-conf/auth-repo-params/domain4.com/user-logout-query={ call UserLogout(?) }\")\n\t\t\t\t\t.append(\"\\n\")\n\t\t\t\t\t.append(\"basic-conf/auth-repo-params/domain4.com/sasl-mechs=PLAIN,DIGEST-MD5\")\n\t\t\t\t\t.append(\"\\n\")\n\t\t\t\t\t.append(\"--user-db[domain4.com]=mysql\")\n\t\t\t\t\t.append(\"\\n\")\n\t\t\t\t\t.append(\"--user-db-uri[domain4.com]=jdbc:mysql://db14.domain4.com/dbname?user&password\")\n\t\t\t\t\t.append(\"\\n\");\n\t\t}\n\n\t\tholder.getProperties().put(ConfigHolder.PROPERTIES_CONFIG_FILE_KEY, tmp.getAbsolutePath());\n\n\t\tOldConfigHolder.Format format = holder.detectPathAndFormat();\n\n\t\ttmp.delete();\n\n\t\tassertEquals(OldConfigHolder.Format.properties, format);\n\n\t\ttmp = File.createTempFile(\"test_\", \".properties\");\n\t\ttry (Writer w = new FileWriter(tmp)) {\n\t\t\tw.append(\"authRepository () {\")\n\t\t\t\t\t.append(\"\\n\")\n\t\t\t\t\t.append(\"'domain4.com' () {\")\n\t\t\t\t\t.append(\"\\n\")\n\t\t\t\t\t.append(\"'user-login-query' = '{ call UserLogin(?, ?) }'\")\n\t\t\t\t\t.append(\"\\n\")\n\t\t\t\t\t.append(\"'user-logout-query' = \\\"{ call UserLogout(?) }\\\"\")\n\t\t\t\t\t.append(\"\\n\")\n\t\t\t\t\t.append(\"'sasl-mechs' = 'PLAIN,DIGEST-MD5'\")\n\t\t\t\t\t.append(\"\\n\")\n\t\t\t\t\t.append(\"}\")\n\t\t\t\t\t.append(\"\\n\")\n\t\t\t\t\t.append(\"}\")\n\t\t\t\t\t.append(\"\\n\");\n\t\t}\n\n\t\tholder.getProperties().put(ConfigHolder.PROPERTIES_CONFIG_FILE_KEY, tmp.getAbsolutePath());\n\n\t\tformat = holder.detectPathAndFormat();\n\n\t\ttmp.delete();\n\n\t\tassertEquals(OldConfigHolder.Format.dsl, format);\n\t}\n\n\t@Test\n\tpublic void testConversionOfAuthRepositoryOptions() throws IOException, ConfigReader.ConfigException {\n\t\tStringBuilder w = new StringBuilder();\n\t\tw.append(\"--auth-db[domain4.com]=tigase-custom\")\n\t\t\t\t.append(\"\\n\")\n\t\t\t\t.append(\"--auth-db-uri[domain4.com]=jdbc:mysql://db14.domain4.com/dbname?user&password\")\n\t\t\t\t.append(\"\\n\")\n\t\t\t\t.append(\"basic-conf/auth-repo-params/domain4.com/user-login-query={ call UserLogin(?, ?) }\")\n\t\t\t\t.append(\"\\n\")\n\t\t\t\t.append(\"basic-conf/auth-repo-params/domain4.com/user-logout-query={ call UserLogout(?) }\")\n\t\t\t\t.append(\"\\n\")\n\t\t\t\t.append(\"basic-conf/auth-repo-params/domain4.com/sasl-mechs=PLAIN,DIGEST-MD5\")\n\t\t\t\t.append(\"\\n\")\n\t\t\t\t.append(\"--user-db[domain4.com]=mysql\")\n\t\t\t\t.append(\"\\n\")\n\t\t\t\t.append(\"--user-db-uri[domain4.com]=jdbc:mysql://db14.domain4.com/dbname?user&password\")\n\t\t\t\t.append(\"\\n\");\n\n\t\tFile tmp = File.createTempFile(\"test_\", \".properties\");\n\t\ttry (Writer writer = new FileWriter(tmp)) {\n\t\t\twriter.write(w.toString());\n\t\t}\n\n\t\tOldConfigHolder holder = new OldConfigHolder();\n\t\tholder.getProperties().put(ConfigHolder.PROPERTIES_CONFIG_FILE_KEY, tmp.getAbsolutePath());\n\n\t\tOldConfigHolder.Format format = holder.detectPathAndFormat();\n\n\t\ttmp.delete();\n\n\t\tassertEquals(OldConfigHolder.Format.properties, format);\n\t}\n\n\t@Test\n\tpublic void testConversionOfAdHocCommandsACLs() throws IOException, ConfigReader.ConfigException {\n\t\tString cfgStr = Stream.of(\"sess-man/command/http\\\\://jabber.org/protocol/admin#add-user=LOCAL\",\n\t\t\t\t\t\t\t\t  \"sess-man/command/http\\\\://jabber.org/protocol/admin#delete-user=DOMAIN:test.com\",\n\t\t\t\t\t\t\t\t  \"sess-man/command/http\\\\://jabber.org/protocol/admin#change-user-password=LOCAL\",\n\t\t\t\t\t\t\t\t  \"sess-man/command/http\\\\://jabber.org/protocol/admin#get-user-roster=JID:ala1@test.com\",\n\t\t\t\t\t\t\t\t  \"sess-man/command/http\\\\://jabber.org/protocol/admin#user-stats=LOCAL\",\n\t\t\t\t\t\t\t\t  \"sess-man/command/http\\\\://jabber.org/protocol/admin#get-active-users-num=LOCAL, DOMAIN:test.com\",\n\t\t\t\t\t\t\t\t  \"sess-man/command/http\\\\://jabber.org/protocol/admin#get-idle-users-num=LOCAL\",\n\t\t\t\t\t\t\t\t  \"sess-man/command/http\\\\://jabber.org/protocol/admin#get-registered-users-list=JID:ala@test.coms\",\n\t\t\t\t\t\t\t\t  \"sess-man/command/http\\\\://jabber.org/protocol/admin#get-online-users-list=LOCAL\",\n\t\t\t\t\t\t\t\t  \"sess-man/command/http\\\\://jabber.org/protocol/admin#get-active-users=DOMAIN:example.com\",\n\t\t\t\t\t\t\t\t  \"sess-man/command/http\\\\://jabber.org/protocol/admin#get-idle-users=LOCAL\",\n\t\t\t\t\t\t\t\t  \"sess-man/command/http\\\\://jabber.org/protocol/admin#announce=LOCAL\",\n\t\t\t\t\t\t\t\t  \"sess-man/command/http\\\\://jabber.org/protocol/admin#add-user-tracker=LOCAL\",\n\t\t\t\t\t\t\t\t  \"sess-man/command/http\\\\://jabber.org/protocol/admin#get-top-active-users=LOCAL\",\n\t\t\t\t\t\t\t\t  \"sess-man/command/http\\\\://jabber.org/protocol/admin#remove-user-tracker=LOCAL\",\n\t\t\t\t\t\t\t\t  \"basic-conf/command/user-domain-perm=LOCAL\", \"sess-man/command/connection-time=LOCAL\",\n\t\t\t\t\t\t\t\t  \"s2s/command/roster-fixer=LOCAL\", \"sess-man/command/roster-fixer-cluster=LOCAL\",\n\t\t\t\t\t\t\t\t  \"s2s/command/user-roster-management=LOCAL\",\n\t\t\t\t\t\t\t\t  \"c2s/command/user-roster-management-ext=LOCAL\").collect(Collectors.joining(\"\\n\"));\n\n\t\tFile tmp = File.createTempFile(\"test_\", \".properties\");\n\t\ttry (Writer writer = new FileWriter(tmp)) {\n\t\t\twriter.write(cfgStr);\n\t\t}\n\n\t\tFile tdslFile = File.createTempFile(\"test_\", \".tdsl\");\n\t\ttdslFile.delete();\n\n\t\tOldConfigHolder holder = new OldConfigHolder();\n\t\tholder.convert(new String[]{PROPERTY_FILENAME_PROP_KEY, tmp.getAbsolutePath()}, tdslFile.toPath());\n\n\t\tMap<String, Object> result = ConfigWriter.buildTree(holder.getProperties());\n\t\tresult.remove(\"config-type\");\n\t\tresult.forEach((comp, properties) -> {\n\t\t\tassertTrue(Map.class.isAssignableFrom(properties.getClass()));\n\t\t\tMap<String, Object> map = (Map<String, Object>) properties;\n\t\t\tmap.values().forEach(x -> {\n\t\t\t\tMap<String, Object> map1 = (Map<String, Object>) x;\n\t\t\t\tmap1.values().forEach(y -> {\n\t\t\t\t\tif (y instanceof List) {\n\t\t\t\t\t\t((List) y).forEach(z -> {\n\t\t\t\t\t\t\tassertNotNull(new CmdAcl((String) z));\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tassertNotNull(new CmdAcl((String) y));\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t});\n\t\t});\n\t}\n\n\t@Test\n\tpublic void testConversionOfDynamicRosterClasses() throws IOException, ConfigReader.ConfigException {\n\t\tOldConfigHolder holder = new OldConfigHolder();\n\t\tMap<String, Object> props = holder.loadFromPropertyStrings(Arrays.asList(new String[]{\n\t\t\t\t\"sess-man/plugins-conf/dynamic-roster-classes=tigase.xmpp.impl.roster.DynamicRosterTest, tigase.xmpp.impl.roster.DynamicRosterTest123\"}));\n\n\t\tholder.convertFromOldFormat();\n\n\t\tMap<String, Object> result = ConfigWriter.buildTree(props);\n\t\tMap<String, Object> sessMan = (Map<String, Object>) result.get(\"sess-man\");\n\t\tassertNotNull(sessMan);\n\t\tAbstractBeanConfigurator.BeanDefinition dynamicRoster = (AbstractBeanConfigurator.BeanDefinition) sessMan.get(\n\t\t\t\t\"dynamic-rosters\");\n\t\tassertNotNull(dynamicRoster);\n\t\tassertTrue(dynamicRoster.isActive());\n\t\tassertNull(dynamicRoster.getClazzName());\n\n\t\tassertFalse(dynamicRoster.isEmpty());\n\n\t\tAbstractBeanConfigurator.BeanDefinition roster = (AbstractBeanConfigurator.BeanDefinition) dynamicRoster.get(\n\t\t\t\t\"DynamicRosterTest\");\n\t\tassertNotNull(roster);\n\t\tassertTrue(roster.isActive());\n\t\tassertEquals(DynamicRosterTest.class.getCanonicalName(), roster.getClazzName());\n\n\t\troster = (AbstractBeanConfigurator.BeanDefinition) dynamicRoster.get(\"DynamicRosterTest123\");\n\t\tassertNotNull(roster);\n\t\tassertTrue(roster.isActive());\n\t\tassertEquals(DynamicRosterTest123.class.getCanonicalName(), roster.getClazzName());\n\t}\n\n\t@Test\n\tpublic void testConversionOfGlobalProperties() throws IOException, ConfigReader.ConfigException {\n\t\tOldConfigHolder holder = new OldConfigHolder();\n\t\tMap<String, Object> props = holder.loadFromPropertyStrings(Arrays.asList(\"--max-queue-size=10000\"));\n\n\t\tholder.convertFromOldFormat();\n\n\t\tMap<String, Object> result = ConfigWriter.buildTree(props);\n\t\tassertEquals(10000, result.get(\"max-queue-size\"));\n\t}\n\n\t@Test\n\tpublic void testConversionOfExtComponentProperties() throws IOException, ConfigReader.ConfigException {\n\t\tOldConfigHolder holder = new OldConfigHolder();\n\t\tMap<String, Object> props = holder.loadFromPropertyStrings(\n\t\t\t\tArrays.asList(\"--external=muc1.devel.tigase.org:passwd1, muc2.devel.tigase.org:passwd2\",\n\t\t\t\t\t\t\t  \"--comp-name-1=ext\", \"--comp-class-1=\" + ComponentProtocol.class.getCanonicalName()));\n\n\t\tholder.convertFromOldFormat();\n\n\t\tMap<String, Object> result = ConfigWriter.buildTree(props);\n\t\tMap<String, Object> ext = (Map<String, Object>) result.get(\"ext\");\n\t\tassertNotNull(ext);\n\t\tMap<String, Object> repo = (Map<String, Object>) ext.get(\"repository\");\n\t\tassertNull(repo);\n\n\t\tnew File(AbstractCompDBRepository.ITEMS_IMPORT_FILE).delete();\n\t}\n\n\n\t@Test\n\tpublic void testConversionOfPriorityQueue() throws ConfigReader.ConfigException {\n\t\tOldConfigHolder holder = new OldConfigHolder();\n\t\tMap<String,Object> props = holder.loadFromPropertyStrings(\n\t\t\t\tCollections.singletonList(\"--queue-implementation = tigase.util.PriorityQueueStrict\")\n\t\t);\n\n\t\tMap<String,Object> result = ConfigWriter.buildTree(props);\n\t\tConfigHolder.upgradeDSL(result);\n\n\t\tassertEquals(\"tigase.util.workqueue.PriorityQueueStrict\", result.get(\"priority-queue-implementation\"));\n\t}\n\n\t@Test\n\tpublic void testConversionOfVHostsList() throws ConfigReader.ConfigException {\n\t\tOldConfigHolder holder = new OldConfigHolder();\n\t\tMap<String,Object> props = holder.loadFromPropertyStrings(\n\t\t\t\tCollections.singletonList(\"--virt-hosts = international.com, dev.com, qa.com, int.com\")\n\t\t);\n\n\t\tholder.convertFromOldFormat();\n\t\tMap<String,Object> result = ConfigWriter.buildTree(props);\n\t\tConfigHolder.upgradeDSL(result);\n\n\t\tOptional<String> defaultVHost = Optional.ofNullable(result.get(\"default-virtual-host\")).filter(String.class::isInstance).map(String.class::cast);\n\t\tassertTrue(defaultVHost.isPresent());\n\t\tassertEquals(\"international.com\", defaultVHost.get());\n\t}\n\n\t@Test\n\tpublic void testConversionOfAdmins() throws ConfigReader.ConfigException {\n\t\tOldConfigHolder holder = new OldConfigHolder();\n\t\tMap<String,Object> props = holder.loadFromPropertyStrings(\n\t\t\t\tCollections.singletonList(\"--admins = admin@dev.com, admin@qa.com, admin@int.com\")\n\t\t);\n\n\t\tholder.convertFromOldFormat();\n\t\tMap<String,Object> result = ConfigWriter.buildTree(props);\n\t\tConfigHolder.upgradeDSL(result);\n\n\t\tassertArrayEquals(new String[]{\"admin@dev.com\", \"admin@qa.com\", \"admin@int.com\"},\n\t\t\t\t\t\t  ((List<String>) result.get(\"admins\")).toArray(new String[0]));\n\t}\n\n\t@Test\n\tpublic void testConversionOfLogging1() throws ConfigReader.ConfigException, IOException {\n\t\tOldConfigHolder holder = new OldConfigHolder();\n\t\tMap<String, Object> props = holder.loadFromPropertyStrings(Arrays.asList(\n\t\t\t\t\"basic-conf/logging/handlers=java.util.logging.ConsoleHandler java.util.logging.FileHandler\",\n\t\t\t\t\"basic-conf/logging/java.util.logging.ConsoleHandler.formatter=tigase.util.LogFormatter\",\n\t\t\t\t\"basic-conf/logging/java.util.logging.ConsoleHandler.level=WARNING\",\n\t\t\t\t\"basic-conf/logging/tigase.useParentHandlers=true\",\n\t\t\t\t\"basic-conf/logging/tigase.server.level=FINEST\",\n\t\t\t\t\"basic-conf/logging/java.util.logging.FileHandler.limit=100000000\",\n\t\t\t\t\"basic-conf/logging/java.util.logging.FileHandler.count=10\",\n\t\t\t\t\"basic-conf/logging/java.util.logging.FileHandler.pattern=/data/logs/tigase.log\"\n\t\t));\n\n\t\tholder.convertFromOldFormat();\n\t\tMap<String,Object> result = ConfigWriter.buildTree(props);\n\t\tConfigHolder.upgradeDSL(result);\n\n\t\tassertEquals(Arrays.asList(\"java.util.logging.ConsoleHandler\", \"java.util.logging.FileHandler\"), ((Map) result.get(\"logging\")).get(\"rootHandlers\"));\n\t\tassertEquals(\"tigase.util.LogFormatter\", ((Map) ((Map) ((Map) result.get(\"logging\")).get(\"handlers\")).get(\n\t\t\t\t\"java.util.logging.ConsoleHandler\")).get(\"formatter\"));\n\t\tassertEquals(\"/data/logs/tigase.log\", ((Map) ((Map) ((Map) result.get(\"logging\")).get(\"handlers\")).get(\n\t\t\t\t\"java.util.logging.FileHandler\")).get(\"pattern\"));\n\t\tassertEquals(\"FINEST\", ((Map) ((Map) ((Map) result.get(\"logging\")).get(\"loggers\")).get(\n\t\t\t\t\"tigase.server\")).get(\"level\"));\n\t}\n\n\t@Test\n\tpublic void testConversionOfLogging2() throws ConfigReader.ConfigException, IOException {\n\t\tOldConfigHolder holder = new OldConfigHolder();\n\t\tMap<String, Object> props = holder.loadFromPropertyStrings(Arrays.asList(\n\t\t\t\t\"basic-conf/logging/java.util.logging.ConsoleHandler.formatter=tigase.util.LogFormatter\",\n\t\t\t\t\"basic-conf/logging/tigase.server.level=FINEST\",\n\t\t\t\t\"basic-conf/logging/java.util.logging.FileHandler.pattern=/data/logs/tigase.log\"\n\t\t));\n\n\t\tholder.convertFromOldFormat();\n\t\tMap<String,Object> result = ConfigWriter.buildTree(props);\n\t\tConfigHolder.upgradeDSL(result);\n\n\t\tassertNull(((Map) result.get(\"logging\")).get(\"rootHandlers\"));\n\t\tassertEquals(\"tigase.util.LogFormatter\", ((Map) ((Map) ((Map) result.get(\"logging\")).get(\"handlers\")).get(\n\t\t\t\t\"java.util.logging.ConsoleHandler\")).get(\"formatter\"));\n\t\tassertEquals(\"/data/logs/tigase.log\", ((Map) ((Map) ((Map) result.get(\"logging\")).get(\"handlers\")).get(\n\t\t\t\t\"java.util.logging.FileHandler\")).get(\"pattern\"));\n\t\tassertEquals(\"FINEST\", ((Map) ((Map) ((Map) result.get(\"logging\")).get(\"loggers\")).get(\n\t\t\t\t\"tigase.server\")).get(\"level\"));\n\t}\n\n\t@Test\n\tpublic void testConversionOfCustomVirtHostsCerts() throws ConfigReader.ConfigException {\n\t\tOldConfigHolder holder = new OldConfigHolder();\n\t\tMap<String, Object> props = holder.loadFromPropertyStrings(\n\t\t\t\tArrays.asList(\"basic-conf/virtual-hosts-cert-host1.example.net=/home/tigase/certs/host1.pem\",\n\t\t\t\t\t\t\t  \"basic-conf/virt-hosts-cert-host2.example.net=/home/tigase/certs/host2.pem\",\n\t\t\t\t\t\t\t  \"basic-conf/virtual-hosts-cert-host3.example.net=/home/tigase/certs/host3.pem\",\n\t\t\t\t\t\t\t  \"basic-conf/virtual-hosts-cert-*.hostx.example.net=/home/tigase/certs/hostx.pem\"));\n\n\t\tholder.convertFromOldFormat();\n\t\tMap<String,Object> result = ConfigWriter.buildTree(props);\n\t\tConfigHolder.upgradeDSL(result);\n\n\t\tdumpConfig(result);\n\n\t\tassertNotNull(((Map) result.get(\"certificate-container\")));\n\t\tassertEquals(\"/home/tigase/certs/host1.pem\", ((Map) ((Map) result.get(\"certificate-container\")).get(\"custom-certificates\")).get(\n\t\t\t\t\"host1.example.net\"));\n\t\tassertEquals(\"/home/tigase/certs/host2.pem\", ((Map) ((Map) result.get(\"certificate-container\")).get(\"custom-certificates\")).get(\n\t\t\t\t\"host2.example.net\"));\n\t\tassertEquals(\"/home/tigase/certs/host3.pem\", ((Map) ((Map) result.get(\"certificate-container\")).get(\"custom-certificates\")).get(\n\t\t\t\t\"host3.example.net\"));\n\t\tassertEquals(\"/home/tigase/certs/hostx.pem\", ((Map) ((Map) result.get(\"certificate-container\")).get(\"custom-certificates\")).get(\n\t\t\t\t\"*.hostx.example.net\"));\n\t}\n\n\t@Test\n\tpublic void testConversionOfHardenedMode() throws ConfigReader.ConfigException {\n\t\tOldConfigHolder holder = new OldConfigHolder();\n\t\tMap<String, Object> props = holder.loadFromPropertyStrings(\n\t\t\t\tArrays.asList(\"hardened-mode = true\"));\n\n\t\tholder.convertFromOldFormat();\n\t\tMap<String,Object> result = ConfigWriter.buildTree(props);\n\t\tConfigHolder.upgradeDSL(result);\n\n\t\tdumpConfig(result);\n\n//\t\tSystem.out.println(result);\n\t\tassertEquals(SSLContextContainer.HARDENED_MODE.secure, result.get(\"hardened-mode\"));\n\n\t\tholder = new OldConfigHolder();\n\t\tprops = holder.loadFromPropertyStrings(\n\t\t\t\tArrays.asList(\"hardened-mode = false\"));\n\n\t\tholder.convertFromOldFormat();\n\t\tresult = ConfigWriter.buildTree(props);\n\t\tConfigHolder.upgradeDSL(result);\n\n\t\tdumpConfig(result);\n\n//\t\tSystem.out.println(result);\n\t\tassertEquals(SSLContextContainer.HARDENED_MODE.relaxed, result.get(\"hardened-mode\"));\n\t}\n\n\tprivate static final void dumpConfig(Map result) {\n\t\ttry {\n\t\t\tStringWriter sw = new StringWriter();\n\t\t\tnew ConfigWriter().write(sw, result);\n//\t\t\tSystem.out.println(\"\\n\" + sw.toString());\n\t\t} catch (IOException ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/conf/ConfigReaderTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.conf;\n\nimport org.junit.Test;\nimport tigase.TestLogger;\nimport tigase.kernel.beans.config.AbstractBeanConfigurator;\n\nimport java.io.*;\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNull;\n\n/**\n * Created by andrzej on 05.06.2016.\n */\npublic class ConfigReaderTest {\n\n\tprivate static final Logger log = TestLogger.getLogger(ConfigReaderTest.class);\n\n\t@Test\n\tpublic void test1() throws Exception {\n\n\t\t// equals is not working when type is different - ie. bool true vs string \"true\"\n\t\tMap<String, Object> root = new HashMap<>();\n\t\troot.put(\"test123\", \"2313\");\n\t\troot.put(\"tes22\", \"223\");\n\t\troot.put(\"x-gy+=x\", 123);\n\t\troot.put(\"env-1\", new ConfigReader.EnvironmentVariable(\"PATH\", null));\n\t\troot.put(\"env-2\", new ConfigReader.EnvironmentVariable(\"test-1\", null));\n\t\troot.put(\"env-3\", new ConfigReader.EnvironmentVariable(\"test-2\", \"test\"));\n\t\troot.put(\"prop-1\", new ConfigReader.PropertyVariable(\"java.vendor\", null));\n\t\troot.put(\"prop-2\", new ConfigReader.PropertyVariable(\"java.version\", \"-1\"));\n\n\t\tConfigReader.CompositeVariable compositeVariable = new ConfigReader.CompositeVariable();\n\t\tcompositeVariable.add(\"Java: \");\n\t\tcompositeVariable.add('+', new ConfigReader.PropertyVariable(\"java.vendor\", null));\n\t\tcompositeVariable.add('+', \" \");\n\t\tcompositeVariable.add('+', new ConfigReader.PropertyVariable(\"java.version\", null));\n\t\troot.put(\"comp-prop1\", compositeVariable);\n\n\t\tcompositeVariable = new ConfigReader.CompositeVariable();\n\t\tcompositeVariable.add(5);\n\t\tcompositeVariable.add('-', 2);\n\t\tcompositeVariable.add('*', 60);\n\t\tcompositeVariable.add('*', 1000);\n\t\troot.put(\"comp-prop2\", compositeVariable);\n\n\t\tList list = new ArrayList();\n\t\tlist.add(new ConfigReader.EnvironmentVariable(\"USER\", null));\n\t\troot.put(\"env-list\", list);\n\t\tlist = new ArrayList();\n\t\tlist.add(1);\n\t\tlist.add(3);\n\t\troot.put(\"some-list\", list);\n\t\tlist = new ArrayList();\n\t\tlist.add(\"1\");\n\t\tlist.add(\"3\");\n\t\troot.put(\"some-list-2\", list);\n\t\tMap<String, Object> map = new HashMap<>();\n\t\tmap.put(\"ala-ma-kota\", true);\n\t\tmap.put(\"test\", \"false\");\n\t\troot.put(\"some-map\", map);\n\t\troot.put(\"for-null\", null);\n\n\t\tMap<String, Object> embeddedMap = new HashMap<>();\n\t\tembeddedMap.put(\"test\", 123);\n\t\tembeddedMap.put(\"other\", true);\n\n\t\tmap = new HashMap<>();\n\t\tmap.put(\"simple\", \"text\");\n\t\tmap.put(\"integer\", 1);\n\t\tmap.put(\"long\", 2L);\n\t\tmap.put(\"double\", (double) 2.0);\n\t\tlist = new ArrayList();\n\n\t\tlist.add(1);\n\t\tlist.add(embeddedMap);\n\t\tlist.add(\"3\");\n\n\t\tmap.put(\"embedded-list\", list);\n\t\tmap.put(\"embedded-map\", embeddedMap);\n\t\troot.put(\"another-map\", map);\n\n\t\tAbstractBeanConfigurator.BeanDefinition b1 = new AbstractBeanConfigurator.BeanDefinition();\n\t\tb1.setBeanName(\"s2s\");\n\t\tb1.setClazzName(tigase.server.xmppserver.S2SConnectionManager.class.getCanonicalName());\n\n\t\tb1.put(\"list\", new ArrayList(list));\n\t\tb1.put(\"map\", new HashMap(embeddedMap));\n\t\tb1.put(\"some\", true);\n\t\tb1.put(\"other\", 123);\n\t\tb1.put(\"ala\", \"kot\");\n\n\t\tAbstractBeanConfigurator.BeanDefinition conns = new AbstractBeanConfigurator.BeanDefinition();\n\t\tconns.setBeanName(\"connections\");\n\t\tAbstractBeanConfigurator.BeanDefinition port = new AbstractBeanConfigurator.BeanDefinition();\n\t\tport.setBeanName(\"5269\");\n\t\tconns.put(port.getBeanName(), port);\n\t\tb1.put(conns.getBeanName(), conns);\n\n\t\troot.put(b1.getBeanName(), b1);\n\n\t\tAbstractBeanConfigurator.BeanDefinition b2 = new AbstractBeanConfigurator.BeanDefinition();\n\t\tb2.setBeanName(\"c2s\");\n\t\tb2.setClazzName(tigase.server.xmppclient.ClientConnectionManager.class.getCanonicalName());\n\t\tb2.setActive(true);\n\t\tb2.setExportable(true);\n\n\t\troot.put(b2.getBeanName(), b2);\n\n\t\tAbstractBeanConfigurator.BeanDefinition b3 = new AbstractBeanConfigurator.BeanDefinition();\n\t\tb3.setBeanName(\"upload\");\n\n\t\troot.put(b3.getBeanName(), b3);\n\n\t\tMap<String, Object> parsed = null;\n\n\t\tFile f = File.createTempFile(\"xx3232\", \"ccxx\");\n\t\ttry {\n\t\t\tnew ConfigWriter().write(f, root);\n\t\t\tdisplayFile(f);\n\t\t\tparsed = new ConfigReader().read(f);\n\t\t} finally {\n\t\t\tf.delete();\n\t\t}\n\n\t\tf = File.createTempFile(\"xx3232\", \"ccxx\");\n\t\ttry {\n\t\t\tnew ConfigWriter().write(f, root);\n\t\t\tdisplayFile(f);\n\t\t} finally {\n\t\t\tf.delete();\n\t\t}\n\n\t\tassertMapEquals(root, parsed, \"/\");\n\n\t\tassertEquals(root, parsed);\n\n\t\tassertEquals(System.getenv(\"PATH\"), ((ConfigReader.Variable) root.get(\"env-1\")).calculateValue());\n\t\tassertEquals(System.getenv(\"test-1\"), ((ConfigReader.Variable) root.get(\"env-2\")).calculateValue());\n\t\tassertEquals(Optional.ofNullable(System.getenv(\"test-2\")).orElse(\"test\"),\n\t\t\t\t\t ((ConfigReader.Variable) root.get(\"env-3\")).calculateValue());\n\t}\n\n\t@Test\n\tpublic void test2() throws Exception {\n\n\t\t// equals is not working when type is different - ie. bool true vs string \"true\"\n\t\tMap<String, Object> props = new HashMap<>();\n\t\tprops.put(\"--cluster-mode\", true);\n\t\tprops.put(\"dataSource/repo-uri\",\n\t\t\t\t  \"jdbc:postgresql://127.0.0.1/tigase?user=test&password=test&autoCreateUser=true\");\n\t\tprops.put(\"sess-man/commands/ala-ma-kota\", \"DOMAIN\");\n\t\tprops.put(\"c2s/incoming-filters\",\n\t\t\t\t  Arrays.asList(\"tigase.server.filters.PacketCounter\", \"tigase.server.filters.PacketCounter\"));\n\t\tprops.put(\"http/active\", \"true\");\n\t\tMap<String, Object> root = ConfigWriter.buildTree(props);\n\n\t\tMap<String, Object> parsed = null;\n\n\t\tFile f = File.createTempFile(\"xx3232\", \"ccxx\");\n\t\ttry {\n\t\t\tnew ConfigWriter().write(f, root);\n\t\t\tdisplayFile(f);\n\t\t\tparsed = new ConfigReader().read(f);\n\t\t} finally {\n\t\t\tf.delete();\n\t\t}\n\n\t\tassertEquals(root, parsed);\n\t\tassertEquals(props, ConfigReader.flatTree(parsed));\n\t}\n\n\t@Test\n\tpublic void testReadingListOfItemsWithVariables1() throws IOException, ConfigReader.ConfigException {\n\t\tString tmp = \"paths = [ '/xxx/yyy/zzz', 'aaa/' + env('HOME') + '/ccc', 'eee/fff/ggg' ]\";\n\t\tMap<String, Object> data = new ConfigReader().read(new StringReader(tmp));\n\t\tList paths = (List<String>) data.get(\"paths\");\n\t\tassertEquals(3, paths.size());\n\t\tassertEquals(\"/xxx/yyy/zzz\", paths.get(0).toString());\n\t\tassertEquals(\"aaa/\" + System.getenv(\"HOME\") + \"/ccc\", ((ConfigReader.Variable) paths.get(1)).calculateValue());\n\t\tassertEquals(\"eee/fff/ggg\", paths.get(2).toString());\n\t}\n\n\t@Test\n\tpublic void testReadingListOfItemsWithVariables2() throws IOException, ConfigReader.ConfigException {\n\t\tString tmp = \"paths = [\\n'/xxx/yyy/zzz',\\n'aaa/' + env('HOME') + '/ccc'\\n]\";\n\t\tMap<String, Object> data = new ConfigReader().read(new StringReader(tmp));\n\t\tList paths = (List<String>) data.get(\"paths\");\n\t\tassertEquals(2, paths.size());\n\t\tassertEquals(\"/xxx/yyy/zzz\", paths.get(0).toString());\n\t\tassertEquals(\"aaa/\" + System.getenv(\"HOME\") + \"/ccc\", ((ConfigReader.Variable) paths.get(1)).calculateValue());\n\t}\n\n\t@Test\n\tpublic void testReadingListOfItemsWithVariables3() throws IOException, ConfigReader.ConfigException {\n\t\tString tmp = \"paths = [ 'admin@localhost', '' + env('USER') + '@localhost' ]\";\n\t\tMap<String, Object> data = new ConfigReader().read(new StringReader(tmp));\n\t\tList paths = (List<String>) data.get(\"paths\");\n\t\tassertEquals(2, paths.size());\n\t\tassertEquals(\"admin@localhost\", paths.get(0).toString());\n\t\tassertEquals(\"\" + System.getenv(\"USER\") + \"@localhost\",\n\t\t\t\t\t ((ConfigReader.Variable) paths.get(1)).calculateValue());\n\t}\n\n\t@Test\n\tpublic void testReadingNegativeValues() throws IOException, ConfigReader.ConfigException {\n\t\tString tmp = Stream.of(\"sub = 10 - 15\",\n\t\t\t\t\t\t\t   \"sub2 = [ 20 - 25 ]\",\n\t\t\t\t\t\t\t   \"negative1 = -10\",\n\t\t\t\t\t\t\t   \"negative2 = -10l\",\n\t\t\t\t\t\t\t   \"negative3 = -10.9d\",\n\t\t\t\t\t\t\t   \"negative4 = [ -10 ]\").collect(Collectors.joining(\"\\n\"));\n\t\tMap<String, Object> data = new ConfigReader().read(new StringReader(tmp));\n\t\tint negative1 = (Integer) data.get(\"negative1\");\n\t\tassertEquals(-10, negative1);\n\t\tlong negative2 = (Long) data.get(\"negative2\");\n\t\tassertEquals(-10, negative2);\n\t\tdouble negative3 = (Double) data.get(\"negative3\");\n\t\tassertEquals(-10.9, negative3, 0.2);\n\t\tList<Integer> negative4 = (List<Integer>) data.get(\"negative4\");\n\t\tassertEquals(-10, (int) negative4.get(0));\n\t\tassertEquals(-5, ((ConfigReader.CompositeVariable) data.get(\"sub\")).calculateValue());\n\t\tassertEquals(-5, ((ConfigReader.CompositeVariable) ((List<ConfigReader.CompositeVariable>) data.get(\"sub2\")).get(\n\t\t\t\t0)).calculateValue());\n\t}\n\n\tprivate void displayFile(File f) throws IOException {\n\t\tBufferedReader reader = new BufferedReader(new FileReader(f));\n\t\tString line = null;\n\t\tlog.log(Level.FINE, \"file content: \" + f.getAbsolutePath());\n\t\twhile ((line = reader.readLine()) != null) {\n\t\t\tlog.log(Level.FINE, line);\n\t\t}\n\t}\n\n\tprivate void assertMapEquals(Map<String, Object> expected, Map actual, String prefix) {\n\t\tfor (Map.Entry e : expected.entrySet()) {\n\t\t\tObject value = actual.get(e.getKey());\n\t\t\tlog.log(Level.FINE, \"checking key = \" + prefix + e.getKey());\n\t\t\tif (e.getValue() == null) {\n\t\t\t\tassertNull(value);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tassertEquals(\"for key \" + prefix + e.getKey(), e.getValue().getClass(), value.getClass());\n\t\t\tif (value instanceof AbstractBeanConfigurator.BeanDefinition) {\n\t\t\t\tAbstractBeanConfigurator.BeanDefinition av = (AbstractBeanConfigurator.BeanDefinition) value;\n\t\t\t\tAbstractBeanConfigurator.BeanDefinition ev = (AbstractBeanConfigurator.BeanDefinition) e.getValue();\n\t\t\t\tassertEquals(ev.getClazzName(), av.getClazzName());\n\t\t\t\tassertEquals(ev.getBeanName(), av.getBeanName());\n\t\t\t\tassertEquals(ev.isActive(), av.isActive());\n\t\t\t\tassertEquals(ev.isExportable(), av.isExportable());\n\t\t\t}\n\t\t\tif (value instanceof Map) {\n\t\t\t\tassertMapEquals((Map<String, Object>) e.getValue(), (Map) value, prefix + e.getKey() + \"/\");\n\t\t\t} else if (value instanceof List) {\n\t\t\t\tassertListEquals((List) e.getValue(), (List) value);\n\t\t\t} else {\n\t\t\t\tassertEquals(e.getValue(), value);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void assertListEquals(List expected, List actual) {\n\t\tassertEquals(expected.size(), actual.size());\n\n\t\tfor (int i = 0; i < expected.size(); i++) {\n\t\t\tassertEquals(expected.get(i), actual.get(i));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/db/AbstractDataSourceAwareTestCase.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport tigase.component.exceptions.RepositoryException;\n\n/**\n * Abstract class for testing repositories implementing DataSourceAware interface.\n *\n * */\npublic abstract class AbstractDataSourceAwareTestCase<DS extends DataSource, R extends DataSourceAware> extends AbstractDataSourceTestCase<DS> {\n\n\tprotected R repo;\n\n\tprotected abstract Class<? extends DataSourceAware> getDataSourceAwareIfc();\n\n\t@Before\n\tpublic void setupDataSourceAware() throws Exception {\n\t\trepo = prepareDataSourceAware();\n\t}\n\n\t@After\n\tpublic void tearDown() throws Exception {\n\t\trepo = null;\n\t}\n\n\tprotected  R getDataSourceAware() {\n\t\treturn repo;\n\t}\n\n\tprotected R prepareDataSourceAware() throws Exception {\n\t\tClass dataSourceAwareClassForUri = DataSourceHelper.getDefaultClass(getDataSourceAwareIfc(), uri);\n\t\tgetKernel().registerBean(\"repository\").asClass(dataSourceAwareClassForUri).setPinned(false).setActive(true).exec();\n\t\tR repo = getInstance((Class<R>) getDataSourceAwareIfc());\n\t\ttry {\n\t\t\t// we do not check schema version as we are updating schema in loadSchema() method!!\n\t\t\t//dataSource.checkSchemaVersion(repo, true);\n\t\t\trepo.setDataSource(getDataSource());\n\t\t} catch (RuntimeException ex) {\n\t\t\tex.printStackTrace();\n\t\t\tthrow new RepositoryException(ex);\n\t\t}\n\t\treturn repo;\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/db/AbstractDataSourceTestCase.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport org.junit.*;\nimport org.junit.rules.TestRule;\nimport org.junit.runner.Description;\nimport org.junit.runners.model.Statement;\nimport tigase.db.util.SchemaLoader;\nimport tigase.db.util.SchemaManager;\nimport tigase.kernel.AbstractKernelTestCase;\n\nimport java.io.File;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Optional;\nimport java.util.Set;\n\n/**\n * Abstract class providing support for classes requiring DataSource instances.\n * \n */\npublic class AbstractDataSourceTestCase<DS extends DataSource> extends AbstractKernelTestCase {\n\n\tprotected static String uri = System.getProperty(\"testDbUri\");\n\t@ClassRule\n\tpublic static TestRule rule = new TestRule() {\n\t\t@Override\n\t\tpublic Statement apply(Statement stmnt, Description d) {\n\t\t\tif (uri == null) {\n\t\t\t\treturn new Statement() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void evaluate() throws Throwable {\n\t\t\t\t\t\tAssume.assumeTrue(\"Ignored due to not passed DB URI!\", false);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn stmnt;\n\t\t}\n\t};\n\n\tprivate DS dataSource;\n\n\t@AfterClass\n\tpublic static void cleanDerby() {\n\t\tif (uri.contains(\"jdbc:derby:\")) {\n\t\t\tFile f = new File(\"derby_test\");\n\t\t\tif (f.exists()) {\n\t\t\t\tif (f.listFiles() != null) {\n\t\t\t\t\tArrays.asList(f.listFiles()).forEach(f2 -> {\n\t\t\t\t\t\tif (f2.listFiles() != null) {\n\t\t\t\t\t\t\tArrays.asList(f2.listFiles()).forEach(f3 -> f3.delete());\n\t\t\t\t\t\t}\n\t\t\t\t\t\tf2.delete();\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tf.delete();\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected static void loadSchema(String schemaId, String schemaVersion, Set<String> components) throws DBInitException {\n\t\tSchemaLoader loader = SchemaLoader.newInstanceForURI(uri);\n\t\tSchemaLoader.Parameters params = loader.createParameters();\n\t\tparams.parseUri(uri);\n\t\tparams.setDbRootCredentials(null, null);\n\t\tparams.setSchemaDirectory(\"src/main/database/\");\n\t\tloader.init(params, Optional.empty());\n\t\tloader.validateDBConnection();\n\t\tloader.validateDBExists();\n\t\tAssert.assertNotEquals(SchemaLoader.Result.error, loader.loadCommonSchema());\n\t\tOptional<SchemaManager.SchemaInfo> schemaInfo = SchemaManager.getDefaultSchemaFor(uri, schemaId, components);\n\t\tAssert.assertNotEquals(SchemaLoader.Result.error, loader.loadSchema(schemaInfo.get(), schemaVersion));\n\t\tloader.postInstallation();\n\t\tloader.shutdown();\n\t}\n\n\tprotected DS getDataSource() {\n\t\treturn dataSource;\n\t}\n\n\t@Before\n\tpublic void setupDataSource() throws Exception {\n\t\tdataSource = prepareDataSource();\n\t\tgetKernel().registerBean(\"dataSource\").asInstance(dataSource).setPinned(false).exportable().exec();\n\t}\n\t\n\tprotected DS prepareDataSource() throws DBInitException, IllegalAccessException, InstantiationException {\n\t\tDataSource dataSource = DataSourceHelper.getDefaultClass(DataSource.class, uri)\n\t\t\t\t.newInstance();//RepositoryFactory.getRepoClass(DataSource.class, uri).newInstance();\n\t\tdataSource.initRepository(uri, new HashMap<>());\n\t\treturn (DS) dataSource;\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/db/AbstractUserRepositoryTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db;\n\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.db.jdbc.JDBCRepositoryTest;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.time.LocalDateTime;\nimport java.util.Date;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic abstract class AbstractUserRepositoryTest<DS extends DataSource> extends AbstractDataSourceTestCase<DS> {\n\n\tprivate final Logger log;\n\n\tprotected UserRepository repo;\n\n\tpublic AbstractUserRepositoryTest() {\n\t\tlog = Logger.getLogger(this.getClass().getCanonicalName());\n\t}\n\n\t@Before\n\tpublic void setUp() throws Exception {\n\t\trepo = DataSourceHelper.getDefaultClass(UserRepository.class, uri).newInstance();\n\t\tif (repo instanceof DataSourceAware) {\n\t\t\t((DataSourceAware) repo).setDataSource(getDataSource());\n\t\t}\n\t}\n\n\t@After\n\tpublic void tearDown() {\n\t\trepo = null;\n\t}\n\n\t@Test\n\tpublic void testLongNode() throws InterruptedException, TigaseDBException {\n\t\tBareJID user = BareJID.bareJIDInstanceNS(\"user\", \"domain\");\n\t\trepo.addUser(user);\n\t\trepo.setData(user, \"node1/node2/node3\", \"key\", \"value\");\n\t\tString node3val;\n\t\tnode3val = repo.getData(user, \"node1/node2/node3\", \"key\");\n\t\tAssert.assertEquals(\"String differ from expected!\", \"value\", node3val);\n\t\trepo.removeSubnode(user, \"node1\");\n\t\tnode3val = repo.getData(user, \"node1/node2/node3\", \"key\");\n\t\tAssert.assertNull(\"Node not removed\", node3val);\n\t\trepo.removeUser(user);\n\t}\n\n\t@Test\n\tpublic void testGetData() throws InterruptedException {\n\n\t\tlog.log(Level.FINE, \"repo: \" + repo);\n\t\tif (repo != null) {\n\t\t\tLocalDateTime localNow = LocalDateTime.now();\n//\t\t\tgetData( null );\n\n\t\t\tlong initalDelay = 5;\n\n\t\t\tScheduledExecutorService scheduler = Executors.newScheduledThreadPool(10);\n\t\t\tfinal int iter = 50;\n\t\t\tfinal int threads = 10;\n\n\t\t\tfor (int i = 0; i < threads; i++) {\n\t\t\t\tscheduler.scheduleAtFixedRate(new RunnableImpl(iter), initalDelay, 100, TimeUnit.MILLISECONDS);\n\t\t\t}\n\n\t\t\tThread.sleep(threads * 1000);\n\t\t}\n\n\t}\n\n\tprivate void getData(BareJID user) {\n\t\tif (user == null) {\n\t\t\tuser = BareJID.bareJIDInstanceNS(\"user\", \"domain\");\n\t\t}\n\t\tlog.log(Level.FINE, \"retrieve: \" + user + \" / thread: \" + Thread.currentThread().getName());\n\t\ttry {\n//\t\t\trepo.getData( user, \"privacy\", \"default-list\", null );\n\t\t\trepo.addUser(user);\n\t\t} catch (UserExistsException ex) {\n\t\t\tlog.log(Level.FINE, \"User exists, ignore: \" + ex.getUserId());\n\t\t} catch (TigaseDBException ex) {\n\t\t\tLogger.getLogger(JDBCRepositoryTest.class.getName()).log(Level.SEVERE, null, ex);\n\t\t}\n\n\t}\n\n\tprivate class RunnableImpl\n\t\t\timplements Runnable {\n\n\t\tint count = 0;\n\t\tint max = 50;\n\n\t\tpublic RunnableImpl(int max) {\n\t\t\tthis.max = max;\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\twhile (count < max) {\n\t\t\t\tcount++;\n\t\t\t\tBareJID user;\n\t\t\t\tuser = BareJID.bareJIDInstanceNS(String.valueOf((new Date()).getTime() / 10), \"domain\");\n\t\t\t\tgetData(user);\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/db/jdbc/JDBCRepositoryTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.jdbc;\n\nimport org.junit.Assume;\nimport org.junit.BeforeClass;\nimport org.junit.ClassRule;\nimport org.junit.rules.TestRule;\nimport org.junit.runner.Description;\nimport org.junit.runners.model.Statement;\nimport tigase.db.AbstractUserRepositoryTest;\nimport tigase.db.DBInitException;\nimport tigase.db.DataRepository;\nimport tigase.db.Schema;\n\nimport java.util.Collections;\n\n/**\n * @author Wojtek\n */\npublic class JDBCRepositoryTest extends AbstractUserRepositoryTest<DataRepository> {\n\n\t@ClassRule\n\tpublic static TestRule rule = new TestRule() {\n\t\t@Override\n\t\tpublic Statement apply(Statement stmnt, Description d) {\n\t\t\tif (uri == null || !uri.startsWith(\"jdbc:\")) {\n\t\t\t\treturn new Statement() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void evaluate() throws Throwable {\n\t\t\t\t\t\tAssume.assumeTrue(\"Ignored due to not passed DB URI!\", false);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn stmnt;\n\t\t}\n\t};\n\n\t@BeforeClass\n\tpublic static void loadSchema() throws DBInitException {\n\t\tloadSchema(Schema.SERVER_SCHEMA_ID, \"8.0.0\", Collections.emptySet());\n\t}\n\t\n}\n"
  },
  {
    "path": "src/test/java/tigase/db/util/DBSchemaLoaderTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.db.util;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport tigase.util.Version;\n\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.Arrays;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.TreeSet;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport static tigase.db.util.DBSchemaLoader.getSchemaFileNamesInRange;\n\npublic class DBSchemaLoaderTest {\n\n\t@Test\n\tpublic void getSchemaFileNames() {\n\t}\n\n\t@Test\n\tpublic void testAdditionalJDBCParametersParsing() {\n\t\tfinal String verifyServerCertificate = \"verifyServerCertificate=false\";\n\t\tfinal String requireSSL = \"requireSSL=true\";\n\t\tfinal String uri =\n\t\t\t\t\"jdbc:mysql://localhost/tigasedb?user=xxxx&password=xxxx&useSSL=true&\" + verifyServerCertificate + \"&\" +\n\t\t\t\t\t\trequireSSL;\n\t\tSchemaLoader schemaLoader = SchemaLoader.newInstanceForURI(uri);\n\t\tSchemaLoader.Parameters parameters = schemaLoader.createParameters();\n\t\tparameters.parseUri(uri);\n\t\tfinal Map<String, String> otherParameters = ((DBSchemaLoader.Parameters) parameters).getOtherParameters();\n\t\tAssert.assertEquals(\"false\", otherParameters.get(\"verifyServerCertificate\"));\n\t\tAssert.assertEquals(\"true\", otherParameters.get(\"requireSSL\"));\n\t\tSchemaManager.RootCredentialsCache rootCredentialsCache = new SchemaManager.RootCredentialsCache();\n\t\trootCredentialsCache.set(\"localhost\", new SchemaManager.RootCredentials(\"root\", \"root\"));\n\t\tschemaLoader.init(parameters, Optional.ofNullable(rootCredentialsCache));\n\t\tAssert.assertTrue(schemaLoader.getDBUri().contains(verifyServerCertificate));\n\t\tAssert.assertTrue(schemaLoader.getDBUri().contains(requireSSL));\n\n\t\tString recreatedUri = schemaLoader.getDBUri();\n\t\t// we are using MySQL query for testing so separator should be &\n\t\tAssert.assertTrue(\"Invalid separator after '\" + verifyServerCertificate + \"' in '\" + recreatedUri + \"'\", recreatedUri.contains(verifyServerCertificate + \"&\") || recreatedUri.endsWith(verifyServerCertificate));\n\t\tAssert.assertTrue(\"Invalid separator after '\" + requireSSL + \"' in '\" + recreatedUri + \"'\", recreatedUri.contains(requireSSL + \"&\") || recreatedUri.endsWith(requireSSL));\n\t}\n\n\t@Test\n\tpublic void getSchemaFilesCurrentEmptyRequiredSnapshot() {\n\n\t\tMap<Version, Path> fileVersions = new ConcurrentHashMap<>();\n\t\tfileVersions.put(Version.of(\"3.2.0\"), Paths.get(\"mysql-3.2.0.sql\"));\n\t\tfileVersions.put(Version.of(\"3.1.0\"), Paths.get(\"mysql-3.1.0.sql\"));\n\t\tfileVersions.put(Version.of(\"4.0.0\"), Paths.get(\"mysql-4.0.0.sql\"));\n\t\tfileVersions.put(Version.of(\"3.0.0\"), Paths.get(\"mysql-3.0.0.sql\"));\n\n\t\tfinal Optional<Version> currentVersion = Optional.empty();\n\t\tfinal Version requiredVersion = Version.of(\"4.0.0-SNAPSHOT\");\n\n\t\tfinal Map<Version, Path> result = getSchemaFileNamesInRange(fileVersions, currentVersion, requiredVersion);\n\n\t\tAssert.assertEquals(\"Should contain all available files\", result.keySet(), fileVersions.keySet());\n\t}\n\n\t@Test\n\tpublic void getSchemaFilesSameVersionCurrentSnapshotRequiredSnapshot() {\n\n\t\tMap<Version, Path> fileVersions = new ConcurrentHashMap<>();\n\t\tfileVersions.put(Version.of(\"3.2.0\"), Paths.get(\"mysql-3.2.0.sql\"));\n\t\tfileVersions.put(Version.of(\"3.1.0\"), Paths.get(\"mysql-3.1.0.sql\"));\n\t\tfileVersions.put(Version.of(\"3.0.0\"), Paths.get(\"mysql-3.0.0.sql\"));\n\t\tfileVersions.put(Version.of(\"4.0.0\"), Paths.get(\"mysql-4.0.0.sql\"));\n\n\t\tfinal Optional<Version> currentVersion = Optional.of(Version.of(\"4.0.0-SNAPSHOT\"));\n\t\tfinal Version requiredVersion = Version.of(\"4.0.0-SNAPSHOT\");\n\n\t\tfinal Map<Version, Path> result = getSchemaFileNamesInRange(fileVersions, currentVersion, requiredVersion);\n\n\t\tAssert.assertEquals(\"Should contain only latest version (same as required)\", result.keySet(),\n\t\t\t\t\t\t\tnew TreeSet<>(Arrays.asList(Version.of(\"4.0.0\"))));\n\t}\n\n\t@Test\n\tpublic void getSchemaFilesSameVersionCurrentFinalRequiredFinal() {\n\n\t\tMap<Version, Path> fileVersions = new ConcurrentHashMap<>();\n\t\tfileVersions.put(Version.of(\"3.2.0\"), Paths.get(\"mysql-3.2.0.sql\"));\n\t\tfileVersions.put(Version.of(\"3.0.0\"), Paths.get(\"mysql-3.0.0.sql\"));\n\t\tfileVersions.put(Version.of(\"4.0.0\"), Paths.get(\"mysql-4.0.0.sql\"));\n\t\tfileVersions.put(Version.of(\"3.1.0\"), Paths.get(\"mysql-3.1.0.sql\"));\n\n\t\tfinal Optional<Version> currentVersion = Optional.of(Version.of(\"4.0.0\"));\n\t\tfinal Version requiredVersion = Version.of(\"4.0.0\");\n\n\t\tfinal Map<Version, Path> result = getSchemaFileNamesInRange(fileVersions, currentVersion, requiredVersion);\n\n\t\tAssert.assertTrue(\"Should be empty\", result.isEmpty());\n\t}\n\n\t@Test\n\tpublic void getSchemaFilesSameVersionCurrentFinalRequiredSnapshot() {\n\n\t\tMap<Version, Path> fileVersions = new ConcurrentHashMap<>();\n\t\tfileVersions.put(Version.of(\"3.2.0\"), Paths.get(\"mysql-3.2.0.sql\"));\n\t\tfileVersions.put(Version.of(\"3.0.0\"), Paths.get(\"mysql-3.0.0.sql\"));\n\t\tfileVersions.put(Version.of(\"4.0.0\"), Paths.get(\"mysql-4.0.0.sql\"));\n\t\tfileVersions.put(Version.of(\"3.1.0\"), Paths.get(\"mysql-3.1.0.sql\"));\n\n\t\tfinal Optional<Version> currentVersion = Optional.of(Version.of(\"4.0.0\"));\n\t\tfinal Version requiredVersion = Version.of(\"4.0.0-SNAPSHOT\");\n\n\t\tfinal Map<Version, Path> result = getSchemaFileNamesInRange(fileVersions, currentVersion, requiredVersion);\n\n\t\tAssert.assertEquals(\"Should contain only latest version (same as required)\", result.keySet(),\n\t\t\t\t\t\t\tnew TreeSet<>(Arrays.asList()));\n\t}\n\n\t@Test\n\tpublic void getSchemaFilesSameVersionCurrentSnapshotRequiredFinal() {\n\n\t\tMap<Version, Path> fileVersions = new ConcurrentHashMap<>();\n\t\tfileVersions.put(Version.of(\"3.1.0\"), Paths.get(\"mysql-3.1.0.sql\"));\n\t\tfileVersions.put(Version.of(\"4.0.0\"), Paths.get(\"mysql-4.0.0.sql\"));\n\t\tfileVersions.put(Version.of(\"3.2.0\"), Paths.get(\"mysql-3.2.0.sql\"));\n\t\tfileVersions.put(Version.of(\"3.0.0\"), Paths.get(\"mysql-3.0.0.sql\"));\n\n\t\tfinal Optional<Version> currentVersion = Optional.of(Version.of(\"4.0.0-SNAPSHOT\"));\n\t\tfinal Version requiredVersion = Version.of(\"4.0.0\");\n\n\t\tfinal Map<Version, Path> result = getSchemaFileNamesInRange(fileVersions, currentVersion, requiredVersion);\n\n\t\tAssert.assertEquals(\"Should contain only latest version (same as required)\", result.keySet(),\n\t\t\t\t\t\t\tnew TreeSet<>(Arrays.asList(Version.of(\"4.0.0\"))));\n\t}\n\n\t@Test\n\tpublic void getSchemaFileNamesInRangeOrderTest() {\n\n\t\tMap<Version, Path> fileVersions = new ConcurrentHashMap<>();\n\t\tfileVersions.put(Version.of(\"3.1.0\"), Paths.get(\"mysql-3.1.0.sql\"));\n\t\tfileVersions.put(Version.of(\"4.0.0\"), Paths.get(\"mysql-4.0.0.sql\"));\n\t\tfileVersions.put(Version.of(\"3.2.0\"), Paths.get(\"mysql-3.2.0.sql\"));\n\t\tfileVersions.put(Version.of(\"3.0.0\"), Paths.get(\"mysql-3.0.0.sql\"));\n\n\t\tfinal Optional<Version> currentVersion = Optional.of(Version.of(\"3.0.0-SNAPSHOT\"));\n\t\tfinal Version requiredVersion = Version.of(\"4.0.0\");\n\n\t\tfinal Map<Version, Path> result = getSchemaFileNamesInRange(fileVersions, currentVersion, requiredVersion);\n\t\tAssert.assertEquals(result.keySet().toArray(),\n\t\t\t\t\t\t\tnew Object[]{Version.of(\"3.0.0\"), Version.of(\"3.1.0\"), Version.of(\"3.2.0\"),\n\t\t\t\t\t\t\t\t\t\t Version.of(\"4.0.0\")});\n\t}\n\n}"
  },
  {
    "path": "src/test/java/tigase/db/util/JDBCPasswordObfuscatorTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.db.util;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\npublic class JDBCPasswordObfuscatorTest {\n\n\t@Test\n\tpublic void obfuscateMysqlPassword() {\n\t\tString password = \"tigase_password\";\n\t\tfinal String input = \"jdbc:mysql://localhost/tigasedb?user=tigasedb&password=\" + password + \"&useSSL=false\";\n\t\tfinal String obfuscated = JDBCPasswordObfuscator.obfuscatePassword(input);\n\t\tAssert.assertEquals(\"jdbc:mysql://localhost/tigasedb?user=tigasedb&password=***************&useSSL=false\", obfuscated);\n\t}\n\n\t@Test\n\tpublic void obfuscateMysqlPasswordSimple() {\n\t\tString password = \"tigase_password\";\n\t\tfinal String input = \"jdbc:mysql://localhost/tigasedb?user=tigasedb&password=\" + password;\n\t\tfinal String obfuscated = JDBCPasswordObfuscator.obfuscatePassword(input);\n\t\tAssert.assertEquals(\"jdbc:mysql://localhost/tigasedb?user=tigasedb&password=***************\", obfuscated);\n\t}\n\n\t@Test\n\tpublic void obfuscatePostgresqlPassword() {\n\t\tString password = \"tigase_password\";\n\t\tfinal String input = \"jdbc:postgresql://localhost/tigasedb?user=tigasedb&password=\" + password + \"&useSSL=false\";\n\t\tfinal String obfuscated = JDBCPasswordObfuscator.obfuscatePassword(input);\n\t\tAssert.assertEquals( \"jdbc:postgresql://localhost/tigasedb?user=tigasedb&password=\" + \"*\".repeat(password.length()) + \"&useSSL=false\", obfuscated);\n\t}\n\n\t@Test\n\tpublic void obfuscateSqlserverSecurePassword() {\n\t\tString password = \"1Secure*Password1\";\n\t\tfinal String input = \"jdbc:sqlserver://hostname:1433;databaseName=tigasedb;user=tigase;password=\" + password + \";schema=dbo;lastUpdateCount=false\";\n\t\tfinal String obfuscated = JDBCPasswordObfuscator.obfuscatePassword(input);\n\t\tAssert.assertEquals(\"jdbc:sqlserver://hostname:1433;databaseName=tigasedb;user=tigase;password=*****************;schema=dbo;lastUpdateCount=false\", obfuscated);\n\t}\n\t@Test\n\tpublic void obfuscateSqlserverPassword() {\n\t\tString password = \"tigase12\";\n\t\tfinal String input = \"jdbc:sqlserver://hostname:1433;databaseName=tigasedb;user=tigase;password=\" + password + \";schema=dbo;lastUpdateCount=false\";\n\t\tfinal String obfuscated = JDBCPasswordObfuscator.obfuscatePassword(input);\n\t\tAssert.assertEquals(\"jdbc:sqlserver://hostname:1433;databaseName=tigasedb;user=tigase;password=********;schema=dbo;lastUpdateCount=false\", obfuscated);\n\t}\n\t@Test\n\tpublic void obfuscateJtdsSqlserverPassword() {\n\t\tString password = \"tigase12\";\n\t\tfinal String input = \"jdbc:jtds:sqlserver://hostname:1433;databaseName=tigasedb;user=tigase;password=\" + password + \";schema=dbo;lastUpdateCount=false\";\n\t\tfinal String obfuscated = JDBCPasswordObfuscator.obfuscatePassword(input);\n\t\tAssert.assertEquals(\"jdbc:jtds:sqlserver://hostname:1433;databaseName=tigasedb;user=tigase;password=********;schema=dbo;lastUpdateCount=false\", obfuscated);\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/db/util/locker/ConnectionLockTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.db.util.locker;\n\nimport org.junit.Assume;\nimport org.junit.BeforeClass;\nimport org.junit.ClassRule;\nimport org.junit.Test;\nimport org.junit.rules.TestRule;\nimport org.junit.runners.model.Statement;\nimport tigase.TestLogger;\n\nimport java.util.Optional;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\n\npublic class ConnectionLockTest {\n\n\tprotected static String uri = System.getProperty(\"testDbUri\");\n//\tprotected static String uri = \"jdbc:derby://derbydb;create=true\";\n//\tprotected static String uri = \"jdbc:postgresql://localhost:5432/postgres?user=root&password=root\";\n//\tprotected static String uri = \"jdbc:mysql://localhost:3306/?user=root&password=root&useSSL=false\";\n//\tprotected static String uri = \"jdbc:jtds:sqlserver://localhost:1433;user=sa;password=1Secure*Password1;schema=dbo;lastUpdateCount=false\";\n\n\n\t@ClassRule\n\tpublic static TestRule rule = (statement, description) -> {\n\t\tif (uri == null || !uri.startsWith(\"jdbc:\")) {\n\t\t\treturn new Statement() {\n\t\t\t\t@Override\n\t\t\t\tpublic void evaluate() throws Throwable {\n\t\t\t\t\tAssume.assumeTrue(\"Ignored due to not passed DB URI!\", false);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t\treturn statement;\n\t};\n\n\t@BeforeClass\n\tpublic static void setUp() throws Exception {\n\t\tTestLogger.configureLogger(Logger.getLogger(\"tigase.db.util.locker\"), Level.FINEST);\n\t}\n\n\t@Test\n\tpublic void permitSkipIfAbsent() {\n\t\tfinal Optional<ConnectionLock> connectionLock = ConnectionLock.getConnectionLocker(uri);\n\t\tassertTrue(connectionLock.isEmpty() || connectionLock.get().lock());\n\t\tif (connectionLock.isPresent()) {\n\t\t\tfinal ConnectionLock lock = connectionLock.get();\n\t\t\tassertTrue(lock.isLocked());\n\t\t\tassertTrue(lock.unlock());\n\t\t}\n\t}\n\t@Test\n\tpublic void singleLockAndUnlock() {\n\t\tfinal Optional<ConnectionLock> connectionLocker = ConnectionLock.getConnectionLocker(uri);\n\t\tif (connectionLocker.isPresent()) {\n\t\t\tfinal ConnectionLock lock = connectionLocker.get();\n\t\t\tassertFalse(lock.isLocked());\n\t\t\tassertTrue(lock.lock());\n\t\t\tassertTrue(lock.isLocked());\n\t\t\tassertTrue(lock.unlock());\n\t\t\tassertFalse(lock.isLocked());\n\t\t}\n\t}\n\n\t@Test\n\tpublic void multipleLockAndUnlock() {\n\t\tfinal Optional<ConnectionLock> connectionLocker = ConnectionLock.getConnectionLocker(uri);\n\t\tfinal Optional<ConnectionLock> connectionLocker2 = ConnectionLock.getConnectionLocker(uri);\n\t\tif (connectionLocker.isPresent() && connectionLocker2.isPresent()) {\n\t\t\tfinal ConnectionLock lock = connectionLocker.get();\n\t\t\tfinal ConnectionLock lock2 = connectionLocker2.get();\n\t\t\tconnectionLocker.get().lockAttemptsLimit = 3;\n\t\t\tconnectionLocker2.get().lockAttemptsLimit = 3;\n\t\t\tassertFalse(lock.isLocked());\n\t\t\tassertFalse(lock2.isLocked());\n\t\t\tassertTrue(lock.lock());\n\t\t\tassertTrue(lock.isLocked());\n\t\t\tassertFalse(lock2.lock());\n\t\t\tassertFalse(lock2.isLocked());\n\t\t\tassertTrue(lock.unlock());\n\t\t\tassertFalse(lock.isLocked());\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/disco/ServiceIdentityTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.disco;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\npublic class ServiceIdentityTest {\n\n\t@Test\n\tpublic void getAsCapsStringWithoutLang() {\n\t\tfinal ServiceIdentity identity = new ServiceIdentity(\"server\", \"im\", \"tigase\");\n\t\tassertEquals(\"server/im//tigase\", identity.getAsCapsString());\n\t}\n\n\t@Test\n\tpublic void getAsCapsStringWithLang() {\n\t\tfinal ServiceIdentity identity = new ServiceIdentity(\"server\", \"im\", \"tigase\", \"en\");\n\t\tassertEquals(\"server/im/en/tigase\", identity.getAsCapsString());\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/eventbus/component/stores/AffiliationStoreTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.component.stores;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport tigase.xmpp.jid.JID;\n\npublic class AffiliationStoreTest {\n\n\t@Test\n\tpublic void testGetAffiliation() throws Exception {\n\t\tfinal AffiliationStore store = new AffiliationStore();\n\t\tstore.setAllowedSubscribers(new JID[]{JID.jidInstance(\"a@b.c/d\"), JID.jidInstance(\"x@y.z\")});\n\n\t\tAssert.assertEquals(Affiliation.member, store.getAffiliation(JID.jidInstance(\"a@b.c/d\")));\n\t\tAssert.assertEquals(Affiliation.member, store.getAffiliation(JID.jidInstance(\"x@y.z\")));\n\t\tAssert.assertEquals(Affiliation.none, store.getAffiliation(JID.jidInstance(\"NONE@y.z\")));\n\n\t\tAssert.assertEquals(Affiliation.none, store.getAffiliation(JID.jidInstance(\"a@b.c\")));\n\t\tAssert.assertEquals(Affiliation.none, store.getAffiliation(JID.jidInstance(\"a@b.c/DIFFERENT\")));\n\t\tAssert.assertEquals(Affiliation.member, store.getAffiliation(JID.jidInstance(\"x@y.z/any\")));\n\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/eventbus/impl/Event1.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\nimport tigase.eventbus.EventBusEvent;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.HashSet;\n\npublic class Event1 implements EventBusEvent {\n\n\tprivate Element elementField;\n\tprivate String emptyField;\n\tprivate JID jid;\n\tprivate HashSet<String> setField;\n\tprivate String[] strArrField;\n\tprivate transient String transientField;\n\tprivate String v1;\n\tprivate int v2;\n\n\tpublic Element getElementField() {\n\t\treturn elementField;\n\t}\n\n\tpublic void setElementField(Element elementField) {\n\t\tthis.elementField = elementField;\n\t}\n\n\tpublic String getEmptyField() {\n\t\treturn emptyField;\n\t}\n\n\tpublic void setEmptyField(String emptyField) {\n\t\tthis.emptyField = emptyField;\n\t}\n\n\tpublic JID getJid() {\n\t\treturn jid;\n\t}\n\n\tpublic void setJid(JID jid) {\n\t\tthis.jid = jid;\n\t}\n\n\tpublic String[] getStrArrField() {\n\t\treturn this.strArrField;\n\t}\n\n\tpublic void setStrArrField(String[] v) {\n\t\tthis.strArrField = v;\n\t}\n\n\tpublic String getTransientField() {\n\t\treturn transientField;\n\t}\n\n\tpublic void setTransientField(String transientField) {\n\t\tthis.transientField = transientField;\n\t}\n\n\tpublic String getV1() {\n\t\treturn v1;\n\t}\n\n\tpublic void setV1(String v1) {\n\t\tthis.v1 = v1;\n\t}\n\n\tpublic int getV2() {\n\t\treturn v2;\n\t}\n\n\tpublic void setV2(int v2) {\n\t\tthis.v2 = v2;\n\t}\n\n\tpublic HashSet<String> getSetField() {\n\t\treturn setField;\n\t}\n\n\tpublic void setSetField(HashSet<String> set) {\n\t\tthis.setField = set;\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/eventbus/impl/Event12.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\nimport java.io.Serializable;\n\npublic class Event12\n\t\textends Event1\n\t\timplements Serializable {\n\n\tpublic Runnable r;\n\n\tpublic Runnable getR() {\n\t\treturn r;\n\t}\n\n\tpublic void setR(Runnable r) {\n\t\tthis.r = r;\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/eventbus/impl/Event2.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\npublic class Event2 {\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/eventbus/impl/EventBusImplementationTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.eventbus.EventListener;\nimport tigase.eventbus.FillRoutedEvent;\nimport tigase.eventbus.HandleEvent;\nimport tigase.eventbus.RouteEvent;\nimport tigase.eventbus.component.stores.Subscription;\nimport tigase.xml.Element;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.concurrent.*;\n\nimport static org.junit.Assert.*;\n\npublic class EventBusImplementationTest {\n\n\tprivate EventBusImplementation eventBus;\n\n\t@Before\n\tpublic void setUp() throws Exception {\n\t\teventBus = new EventBusImplementation();\n\t\teventBus.setExecutor(new Executor() {\n\t\t\t@Override\n\t\t\tpublic void execute(Runnable command) {\n\t\t\t\tcommand.run();\n\t\t\t}\n\t\t});\n\t}\n\n\t@Test\n\tpublic void test1() {\n\t\tfinal String[] value = new String[5];\n\n\t\teventBus.addListener(Event1.class, new EventListener<Event1>() {\n\n\t\t\t@Override\n\t\t\tpublic void onEvent(Event1 event) {\n\t\t\t\tvalue[1] = \"l\";\n\t\t\t}\n\t\t});\n\t\teventBus.addListener(Event12.class, new EventListener<Event12>() {\n\n\t\t\t@Override\n\t\t\tpublic void onEvent(Event12 event) {\n\t\t\t\tvalue[2] = \"l\";\n\t\t\t}\n\t\t});\n\t\teventBus.addListener(Event2.class, new EventListener<Event2>() {\n\n\t\t\t@Override\n\t\t\tpublic void onEvent(Event2 event) {\n\t\t\t\tvalue[3] = \"l\";\n\t\t\t}\n\t\t});\n\n\t\tEvent1 event = new Event1();\n\t\teventBus.fire(event);\n\n\t\tAssert.assertNull(value[0]);\n\t\tAssert.assertEquals(\"l\", value[1]);\n\t\tAssert.assertNull(value[2]);\n\t\tAssert.assertNull(value[3]);\n\t\tAssert.assertNull(value[4]);\n\t}\n\n\t@Test\n\tpublic void testAddListener() {\n\t\tfinal Object resp[] = new Object[]{null, null, null, null, null};\n\n\t\tArrays.fill(resp, null);\n\t\teventBus.fire(new Event12());\n\t\tAssert.assertNull(resp[0]);\n\t\tAssert.assertNull(resp[1]);\n\t\tAssert.assertNull(resp[2]);\n\t\tAssert.assertNull(resp[3]);\n\t\tAssert.assertNull(resp[4]);\n\n\t\teventBus.addListener(Event1.class, e -> resp[0] = e);\n\t\teventBus.addListener(Event12.class, e -> resp[1] = e);\n\t\teventBus.addListener(Event1.class.getPackage().getName(), Event1.class.getSimpleName(), e -> resp[2] = e);\n\t\teventBus.addListener(Event12.class.getPackage().getName(), Event12.class.getSimpleName(), e -> resp[3] = e);\n\t\teventBus.addHandler(new AbstractListenerHandler(null, null, new Object()) {\n\t\t\t@Override\n\t\t\tpublic void dispatch(Object event, Object source, boolean remotelyGeneratedEvent) {\n\t\t\t\tresp[4] = event;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Type getRequiredEventType() {\n\t\t\t\treturn Type.asIs;\n\t\t\t}\n\n\t\t});\n\n\t\tArrays.fill(resp, null);\n\t\teventBus.fire(new Event12());\n\t\tAssert.assertNotNull(resp[0]);\n\t\tAssert.assertNotNull(resp[1]);\n\t\tAssert.assertNotNull(resp[2]);\n\t\tAssert.assertNotNull(resp[3]);\n\t\tAssert.assertNotNull(resp[4]);\n\t}\n\n\t@Test\n\tpublic void testFire() throws Exception {\n\t\tObject resp[] = new Object[]{null, null, null, null, null};\n\t\teventBus.addListener(Event1.class, e -> resp[0] = e);\n\t\teventBus.addListener(Event12.class, e -> resp[1] = e);\n\t\teventBus.addListener(Event1.class.getPackage().getName(), Event1.class.getSimpleName(), e -> resp[2] = e);\n\t\teventBus.addListener(Event12.class.getPackage().getName(), Event12.class.getSimpleName(), e -> resp[3] = e);\n\t\teventBus.addHandler(new AbstractListenerHandler(null, null, new Object()) {\n\t\t\t@Override\n\t\t\tpublic void dispatch(Object event, Object source, boolean remotelyGeneratedEvent) {\n\t\t\t\tresp[4] = event;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Type getRequiredEventType() {\n\t\t\t\treturn Type.asIs;\n\t\t\t}\n\n\t\t});\n\n\t\teventBus.fire(new Event1());\n\t\tAssert.assertTrue(resp[0] instanceof Event1);\n\t\tAssert.assertNull(resp[1]);\n\t\tAssert.assertTrue(resp[2] instanceof Element);\n\t\tAssert.assertNull(resp[3]);\n\t\tAssert.assertTrue(resp[4] instanceof Event1);\n\n\t\tArrays.fill(resp, null);\n\n\t\teventBus.fire(new Event12());\n\t\tAssert.assertTrue(resp[0] instanceof Event12);\n\t\tAssert.assertTrue(resp[1] instanceof Event12);\n\t\tAssert.assertTrue(resp[2] instanceof Element);\n\t\tAssert.assertTrue(resp[3] instanceof Element);\n\t\tAssert.assertTrue(resp[4] instanceof Event12);\n\n\t\tArrays.fill(resp, null);\n\n\t\teventBus.fire(new Element(\"tigase.eventbus.impl.Event1\"));\n\t\tAssert.assertNull(resp[0]);\n\t\tAssert.assertNull(resp[1]);\n\t\tAssert.assertTrue(resp[2] instanceof Element);\n\t\tAssert.assertNull(resp[3]);\n\t\tAssert.assertTrue(resp[4] instanceof Element);\n\n\t\tArrays.fill(resp, null);\n\n\t\teventBus.fire(new Element(\"tigase.eventbus.impl.Event12\"));\n\t\tAssert.assertNull(resp[0]);\n\t\tAssert.assertNull(resp[1]);\n\t\tAssert.assertNull(resp[2]);\n\t\tAssert.assertTrue(resp[3] instanceof Element);\n\t\tAssert.assertTrue(resp[4] instanceof Element);\n\t}\n\n\t@Test\n\tpublic void testFireSync() throws ExecutionException, InterruptedException {\n\t\tlong threadId = Thread.currentThread().getId();\n\t\teventBus.setExecutor(Executors.newWorkStealingPool());\n\t\tCompletableFuture<Long> future = new CompletableFuture<>();\n\t\tAbstractListenerHandler handler = new ObjectEventsListenerHandler(Event1.class.getPackage().getName(), Event1.class.getSimpleName(), e -> {\n\t\t\tfuture.complete(Thread.currentThread().getId());\n\t\t}) {\n\t\t\t@Override\n\t\t\tpublic boolean isSynchronous() {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t};\n\t\teventBus.addHandler(handler);\n\t\teventBus.fire(new Event1());\n\t\tassertEquals(threadId, (long) future.orTimeout(1, TimeUnit.SECONDS).get());\n\t}\n\n\t@Test\n\tpublic void testFireAsync() throws ExecutionException, InterruptedException {\n\t\tlong threadId = Thread.currentThread().getId();\n\t\teventBus.setExecutor(Executors.newWorkStealingPool());\n\t\tCompletableFuture<Long> future = new CompletableFuture<>();\n\t\tAbstractListenerHandler handler = new ObjectEventsListenerHandler(Event1.class.getPackage().getName(), Event1.class.getSimpleName(), e -> {\n\t\t\tfuture.complete(Thread.currentThread().getId());\n\t\t});\n\t\teventBus.addHandler(handler);\n\t\teventBus.fire(new Event1());\n\t\tassertNotEquals(threadId, (long) future.orTimeout(1, TimeUnit.SECONDS).get());\n\t}\n\n\t@Test\n\tpublic void testFireAndHandleXmlEventNoPackageName() {\n\t\tfinal Element event = new Element(\"ShortNameEvent\");\n\t\tevent.withElement(\"data\", null, \"9842984\");\n\n\t\tfinal Element[] receivedEvent = new Element[]{null};\n\t\teventBus.addListener(\"\", \"ShortNameEvent\", event1 -> receivedEvent[0] = event1);\n\t\teventBus.fire(event);\n\n\t\tassertNotNull(receivedEvent[0]);\n\t\tassertEquals(\"ShortNameEvent\", receivedEvent[0].getName());\n\t\tassertEquals(\"9842984\", receivedEvent[0].getChild(\"data\").getCData());\n\t}\n\n\t@Test\n\tpublic void testFireAndHandleXmlEventLongName() {\n\t\tfinal Element event = new Element(\"package.name.ShortNameEvent\");\n\t\tevent.withElement(\"data\", null, \"9842981\");\n\n\t\tfinal Element[] receivedEvent = new Element[]{null};\n\t\teventBus.addListener(\"package.name\", \"ShortNameEvent\", event1 -> receivedEvent[0] = event1);\n\t\teventBus.fire(event);\n\n\t\tassertNotNull(receivedEvent[0]);\n\t\tassertEquals(\"package.name.ShortNameEvent\", receivedEvent[0].getName());\n\t\tassertEquals(\"9842981\", receivedEvent[0].getChild(\"data\").getCData());\n\t}\n\n\t@Test\n\tpublic void testGetListenersForEvent_Element() throws Exception {\n\t\teventBus.addListener(Event1.class, new EventListener<Event1>() {\n\t\t\t@Override\n\t\t\tpublic void onEvent(Event1 e) {\n\n\t\t\t}\n\t\t});\n\t\teventBus.addListener(Event12.class, new EventListener<Event12>() {\n\t\t\t@Override\n\t\t\tpublic void onEvent(Event12 e) {\n\n\t\t\t}\n\t\t});\n\t\teventBus.addListener(Event1.class.getPackage().getName(), Event1.class.getSimpleName(),\n\t\t\t\t\t\t\t new EventListener<Element>() {\n\t\t\t\t\t\t\t\t @Override\n\t\t\t\t\t\t\t\t public void onEvent(Element e) {\n\n\t\t\t\t\t\t\t\t }\n\t\t\t\t\t\t\t });\n\t\teventBus.addListener(Event1.class.getPackage().getName(), null, new EventListener<Element>() {\n\t\t\t@Override\n\t\t\tpublic void onEvent(Element e) {\n\n\t\t\t}\n\t\t});\n\t\teventBus.addListener(null, null, new EventListener<Element>() {\n\t\t\t@Override\n\t\t\tpublic void onEvent(Element e) {\n\n\t\t\t}\n\t\t});\n\n\t\tAssert.assertEquals(4, eventBus.getListenersForEvent(Event1.class).size());\n\t\tAssert.assertEquals(4, eventBus.getListenersForEvent(Event1.class.getPackage().getName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Event1.class.getSimpleName()).size());\n\t\tAssert.assertEquals(5, eventBus.getListenersForEvent(Event12.class).size());\n\n\t}\n\n\t@Test\n\tpublic void testGetListenersForEvent_Object() throws Exception {\n\t\teventBus.addListener(Event1.class, new EventListener<Event1>() {\n\t\t\t@Override\n\t\t\tpublic void onEvent(Event1 e) {\n\n\t\t\t}\n\t\t});\n\t\teventBus.addListener(Event2.class, new EventListener<Event2>() {\n\t\t\t@Override\n\t\t\tpublic void onEvent(Event2 e) {\n\n\t\t\t}\n\t\t});\n\t\teventBus.addListener(Event2.class, new EventListener<Event2>() {\n\t\t\t@Override\n\t\t\tpublic void onEvent(Event2 e) {\n\n\t\t\t}\n\t\t});\n\t\teventBus.addListener(Event12.class, new EventListener<Event12>() {\n\t\t\t@Override\n\t\t\tpublic void onEvent(Event12 e) {\n\n\t\t\t}\n\t\t});\n\t\teventBus.addListener(Event12.class, new EventListener<Event12>() {\n\t\t\t@Override\n\t\t\tpublic void onEvent(Event12 e) {\n\n\t\t\t}\n\t\t});\n\n\t\tAssert.assertEquals(1, eventBus.getListenersForEvent(Event1.class).size());\n\t\tAssert.assertEquals(2, eventBus.getListenersForEvent(Event2.class).size());\n\t\tAssert.assertEquals(3, eventBus.getListenersForEvent(Event12.class).size());\n\n\t\tAssert.assertEquals(0, eventBus.getListenersForEvent(String.class).size());\n\n\t\tAssert.assertEquals(1, eventBus.getListenersForEvent(Event1.class.getPackage().getName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Event1.class.getSimpleName()).size());\n\t\tAssert.assertEquals(2, eventBus.getListenersForEvent(Event2.class.getPackage().getName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Event12.class.getSimpleName()).size());\n\t\tAssert.assertEquals(2, eventBus.getListenersForEvent(Event12.class.getPackage().getName(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Event12.class.getSimpleName()).size());\n\t}\n\n\t@Test\n\tpublic void testRegisterAll() throws Exception {\n\t\tfinal Consumer c = new Consumer();\n\t\teventBus.registerAll(c);\n\n\t\tAssert.assertNotNull(eventBus.getEventRoutingSelector(Event1.class));\n\t\tAssert.assertNotNull(eventBus.getEventRoutingSelector(Event12.class));\n\t\tAssert.assertNull(eventBus.getEventRoutingSelector(Event2.class));\n\n\t\tAssert.assertEquals(1, eventBus.getEventRoutedTransientFillers(Event1.class).size());\n\t\tAssert.assertEquals(1, eventBus.getEventRoutedTransientFillers(Event12.class).size());\n\t\tAssert.assertEquals(0, eventBus.getEventRoutedTransientFillers(Event2.class).size());\n\n\t\teventBus.fire(new Event12(), this);\n\t\tAssert.assertNotNull(c.resp[0]);\n\t\tAssert.assertNotNull(c.resp[1]);\n\t\tAssert.assertNotNull(c.resp[2]);\n\n\t\tArrays.fill(c.resp, null);\n\n\t\teventBus.unregisterAll(c);\n\n\t\tAssert.assertNull(eventBus.getEventRoutingSelector(Event1.class));\n\t\tAssert.assertNull(eventBus.getEventRoutingSelector(Event12.class));\n\t\tAssert.assertNull(eventBus.getEventRoutingSelector(Event2.class));\n\n\t\tAssert.assertEquals(0, eventBus.getEventRoutedTransientFillers(Event1.class).size());\n\t\tAssert.assertEquals(0, eventBus.getEventRoutedTransientFillers(Event12.class).size());\n\t\tAssert.assertEquals(0, eventBus.getEventRoutedTransientFillers(Event2.class).size());\n\n\t\teventBus.fire(new Event12());\n\n\t\tAssert.assertNull(c.resp[0]);\n\t\tAssert.assertNull(c.resp[1]);\n\t\tAssert.assertNull(c.resp[2]);\n\t}\n\n\t@Test\n\tpublic void testRegisterAll_InheritanceTest() {\n\t\tConsumerChild c = new ConsumerChild();\n\n\t\teventBus.registerAll(c);\n\n\t\teventBus.fire(new Event1());\n\n\t\tassertNotNull(c.respChild[0]);\n\t\tassertNotNull(c.respChild[1]);\n\t\tassertNotNull(c.respChild[2]);\n\t\tassertNull(c.respChild[3]);\n\t\tassertNull(c.respChild[4]);\n\t\tassertNotNull(c.respChild[5]);\n\t\tassertNotNull(c.respChild[6]);\n\t\tassertNull(c.respChild[7]);\n\n\t\tassertNull(c.respParent[0]);\n\t\tassertNull(c.respParent[1]);\n\t\tassertNotNull(c.respParent[2]);\n\t\tassertNotNull(c.respParent[3]);\n\t\tassertNotNull(c.respParent[4]);\n\t\tassertNull(c.respParent[5]);\n\t\tassertNotNull(c.respParent[6]);\n\t\tassertNotNull(c.respParent[7]);\n\t}\n\n\t@Test\n\tpublic void testRegisterAll_MethodVisibilityTest() {\n\t\tConsumerMethodVisibility cmv = new ConsumerMethodVisibility();\n\t\teventBus.registerAll(cmv);\n\n\t\teventBus.fire(new Event1());\n\n\t\tassertNotNull(cmv.resp[0]);\n\t\tassertNotNull(cmv.resp[1]);\n\t\tassertNotNull(cmv.resp[2]);\n\n\t\tArrays.fill(cmv.resp, null);\n\n\t\teventBus.unregisterAll(cmv);\n\n\t\teventBus.fire(new Event1());\n\n\t\tassertNull(cmv.resp[0]);\n\t\tassertNull(cmv.resp[1]);\n\t\tassertNull(cmv.resp[2]);\n\t}\n\n\t@Test\n\tpublic void testRemoveListener() {\n\t\tfinal Object resp[] = new Object[]{null, null, null, null, null};\n\n\t\tEventListener<Event1> l0 = e -> resp[0] = e;\n\t\teventBus.addListener(Event1.class, l0);\n\n\t\tEventListener<Event12> l1 = e -> resp[1] = e;\n\t\teventBus.addListener(Event12.class, l1);\n\n\t\tEventListener<Element> l2 = e -> resp[2] = e;\n\t\teventBus.addListener(Event1.class.getPackage().getName(), Event1.class.getSimpleName(), l2);\n\n\t\tEventListener<Element> l3 = e -> resp[3] = e;\n\t\teventBus.addListener(Event12.class.getPackage().getName(), Event12.class.getSimpleName(), l3);\n\n\t\tAbstractListenerHandler l4 = new AbstractListenerHandler(null, null, new Object()) {\n\t\t\t@Override\n\t\t\tpublic void dispatch(Object event, Object source, boolean remotelyGeneratedEvent) {\n\t\t\t\tresp[4] = event;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Type getRequiredEventType() {\n\t\t\t\treturn Type.asIs;\n\t\t\t}\n\n\t\t};\n\t\teventBus.addHandler(l4);\n\n\t\tArrays.fill(resp, null);\n\t\teventBus.fire(new Event12());\n\t\tAssert.assertNotNull(resp[0]);\n\t\tAssert.assertNotNull(resp[1]);\n\t\tAssert.assertNotNull(resp[2]);\n\t\tAssert.assertNotNull(resp[3]);\n\t\tAssert.assertNotNull(resp[4]);\n\n\t\teventBus.removeListener(l0);\n\n\t\tArrays.fill(resp, null);\n\t\teventBus.fire(new Event12());\n\t\tAssert.assertNull(resp[0]);\n\t\tAssert.assertNotNull(resp[1]);\n\t\tAssert.assertNotNull(resp[2]);\n\t\tAssert.assertNotNull(resp[3]);\n\t\tAssert.assertNotNull(resp[4]);\n\n\t\teventBus.removeListener(l1);\n\n\t\tArrays.fill(resp, null);\n\t\teventBus.fire(new Event12());\n\t\tAssert.assertNull(resp[0]);\n\t\tAssert.assertNull(resp[1]);\n\t\tAssert.assertNotNull(resp[2]);\n\t\tAssert.assertNotNull(resp[3]);\n\t\tAssert.assertNotNull(resp[4]);\n\n\t\teventBus.removeListener(l2);\n\n\t\tArrays.fill(resp, null);\n\t\teventBus.fire(new Event12());\n\t\tAssert.assertNull(resp[0]);\n\t\tAssert.assertNull(resp[1]);\n\t\tAssert.assertNull(resp[2]);\n\t\tAssert.assertNotNull(resp[3]);\n\t\tAssert.assertNotNull(resp[4]);\n\n\t\teventBus.removeListener(l3);\n\n\t\tArrays.fill(resp, null);\n\t\teventBus.fire(new Event12());\n\t\tAssert.assertNull(resp[0]);\n\t\tAssert.assertNull(resp[1]);\n\t\tAssert.assertNull(resp[2]);\n\t\tAssert.assertNull(resp[3]);\n\t\tAssert.assertNotNull(resp[4]);\n\n\t\teventBus.removeHandler(l4);\n\n\t\tArrays.fill(resp, null);\n\t\teventBus.fire(new Event12());\n\t\tAssert.assertNull(resp[0]);\n\t\tAssert.assertNull(resp[1]);\n\t\tAssert.assertNull(resp[2]);\n\t\tAssert.assertNull(resp[3]);\n\t\tAssert.assertNull(resp[4]);\n\t}\n\n\tpublic static class Consumer {\n\n\t\tprivate final Object resp[] = new Object[]{null, null, null};\n\n\t\t@HandleEvent\n\t\tpublic void event0(Event1 e) {\n\t\t\tresp[0] = e;\n\t\t}\n\n\t\t@HandleEvent\n\t\tpublic void event1(Event12 e) {\n\t\t\tresp[1] = e;\n\t\t}\n\n\t\t@HandleEvent\n\t\tpublic void event2(Event12 e, Object source) {\n\t\t\tresp[2] = e;\n\t\t\tAssert.assertTrue(source instanceof EventBusImplementationTest);\n\t\t}\n\n\t\t@FillRoutedEvent\n\t\tpublic void fillRoutedEvent1(Event1 e) {\n\n\t\t}\n\n\t\t@RouteEvent\n\t\tpublic Collection<Subscription> routeEvent1(Event1 e, Collection<Subscription> subscriptions) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tpublic static class ConsumerChild\n\t\t\textends ConsumerParent {\n\n\t\tprivate final Object[] respChild = new Object[]{null, null, null, null, null, null, null, null};\n\n\t\t@HandleEvent\n\t\t@Override\n\t\tpublic void event0(Event1 e) {\n\t\t\trespChild[0] = e;\n\t\t}\n\n\t\t@Override\n\t\tpublic void event1(Event1 e) {\n\t\t\trespChild[1] = e;\n\t\t}\n\n\t\t@Override\n\t\t@HandleEvent\n\t\tpublic void event2(Event1 e) {\n\t\t\trespChild[2] = e;\n\t\t\tsuper.event2(e);\n\t\t}\n\n\t\tpublic void event3(Event1 e, String x) {\n\t\t\trespChild[3] = e;\n\t\t}\n\n\t\t@Override\n\t\tprotected void event5(Event1 e) {\n\t\t\trespChild[5] = e;\n\t\t}\n\n\t\t@Override\n\t\tprotected void event6(Event1 e) {\n\t\t\trespChild[6] = e;\n\t\t\tsuper.event6(e);\n\t\t}\n\n\t\tprivate void event4(Event1 e) {\n\t\t\trespChild[4] = e;\n\t\t}\n\n\t}\n\n\tpublic static class ConsumerMethodVisibility {\n\n\t\tprivate final Object resp[] = new Object[]{null, null, null};\n\n\t\t@HandleEvent\n\t\tpublic void event0public(Event1 e) {\n\t\t\tresp[0] = e;\n\t\t}\n\n\t\t@HandleEvent\n\t\tprotected void event1protected(Event1 e) {\n\t\t\tresp[1] = e;\n\t\t}\n\n\t\t@HandleEvent\n\t\tprivate void event2private(Event1 e) {\n\t\t\tresp[2] = e;\n\t\t}\n\t}\n\n\tpublic static class ConsumerParent {\n\n\t\tprotected final Object[] respParent = new Object[]{null, null, null, null, null, null, null, null};\n\n\t\t@HandleEvent\n\t\tpublic void event0(Event1 e) {\n\t\t\trespParent[0] = e;\n\t\t}\n\n\t\t@HandleEvent\n\t\tpublic void event1(Event1 e) {\n\t\t\trespParent[1] = e;\n\t\t}\n\n\t\t@HandleEvent\n\t\tpublic void event2(Event1 e) {\n\t\t\trespParent[2] = e;\n\t\t}\n\n\t\t@HandleEvent\n\t\tpublic void event3(Event1 e) {\n\t\t\trespParent[3] = e;\n\t\t}\n\n\t\t@HandleEvent\n\t\tprotected void event5(Event1 e) {\n\t\t\trespParent[5] = e;\n\t\t}\n\n\t\t@HandleEvent\n\t\tprotected void event6(Event1 e) {\n\t\t\trespParent[6] = e;\n\t\t}\n\n\t\t@HandleEvent\n\t\tprotected void event7(Event1 e) {\n\t\t\trespParent[7] = e;\n\t\t}\n\n\t\t@HandleEvent\n\t\tprivate void event4(Event1 e) {\n\t\t\trespParent[4] = e;\n\t\t}\n\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/eventbus/impl/EventBusSerializerTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport tigase.auth.BruteForceLockerBean;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.DomBuilderHandler;\nimport tigase.xml.Element;\nimport tigase.xml.SimpleParser;\nimport tigase.xml.SingletonFactory;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\n\nimport static org.junit.Assert.*;\n\npublic class EventBusSerializerTest {\n\n\t@Test\n\tpublic void testDeserialize() {\n\t\tEventBusSerializer serializer = new EventBusSerializer();\n\n\t\tEvent1 eo = new Event1();\n\t\teo.setJid(JID.jidInstanceNS(\"a@b.c/d\"));\n\t\teo.setTransientField(\"123\");\n\t\teo.setV1(\"message\");\n\t\teo.setV2(9898);\n\t\teo.setElementField(new Element(\"x\", \"v\", new String[]{\"a\"}, new String[]{\"b\"}));\n\t\teo.setStrArrField(new String[]{\"ala\", \"m,a\", \"kota\"});\n\t\teo.setSetField(new HashSet<>(Arrays.asList(\"test123\")));\n\n\t\tElement ex = serializer.serialize(eo);\n\n\t\tEvent1 ed = serializer.deserialize(ex);\n\n\t\tassertNotNull(ed);\n\t\tAssert.assertNotSame(eo, ed);\n\t\tAssert.assertEquals(JID.jidInstanceNS(\"a@b.c/d\"), ed.getJid());\n\t\tAssert.assertNull(ed.getTransientField());\n\t\tAssert.assertEquals(\"message\", ed.getV1());\n\t\tAssert.assertEquals(9898, ed.getV2());\n\t\tAssert.assertEquals(new Element(\"x\", \"v\", new String[]{\"a\"}, new String[]{\"b\"}), ed.getElementField());\n\t\tAssert.assertArrayEquals(new String[]{\"ala\", \"m,a\", \"kota\"}, ed.getStrArrField());\n\t\tAssert.assertTrue(eo.getSetField().contains(\"test123\"));\n\t}\n\n\t@Test\n\tpublic void testSerialize() {\n\t\tEventBusSerializer serializer = new EventBusSerializer();\n\n\t\tEvent1 eo = new Event1();\n\t\teo.setJid(JID.jidInstanceNS(\"a@b.c/d\"));\n\t\teo.setTransientField(\"123\");\n\t\teo.setV1(\"message\");\n\t\teo.setV2(9898);\n\t\teo.setElementField(new Element(\"x\", \"v\", new String[]{\"a\"}, new String[]{\"b\"}));\n\t\teo.setStrArrField(new String[]{\"ala\", \"m,a\", \"kota\"});\n\n\t\tElement ex = serializer.serialize(eo);\n\n\t\tAssert.assertEquals(5, ex.getChildren().size());\n\t\tAssert.assertEquals(\"event\", ex.getName());\n\t\tAssert.assertEquals(\"tigase.eventbus.impl.Event1\", ex.getAttributeStaticStr(\"class\"));\n\t\tAssert.assertEquals(\"a@b.c/d\", ex.getCData(new String[]{\"event\", \"jid\"}));\n\t\tAssert.assertNull(ex.getCData(new String[]{\"event\", \"transientField\"}));\n\t\tAssert.assertEquals(\"message\", ex.getCData(new String[]{\"event\", \"v1\"}));\n\t\tAssert.assertEquals(\"9898\", ex.getCData(new String[]{\"event\", \"v2\"}));\n\t\tAssert.assertEquals(\"v\", ex.getCData(new String[]{\"event\", \"elementField\", \"x\"}));\n\t\tAssert.assertNotEquals(\"ala,m,a,kota\", ex.getCData(new String[]{\"event\", \"strArrField\"}));\n\t}\n\n\t@Test\n\tpublic void testSerializeXmlValidity() throws TigaseStringprepException {\n\t\tEventBusSerializer serializer = new EventBusSerializer();\n\n\t\tBruteForceLockerBean.StatHolder statHolder = new BruteForceLockerBean.StatHolder();\n\t\tstatHolder.addIP(\"192.168.0.1\");\n\t\tstatHolder.addIP(\"::1\");\n\t\tstatHolder.addJID(BareJID.bareJIDInstance(\"test@zeus\"));\n\t\tBruteForceLockerBean.StatisticsEmitEvent eo = new BruteForceLockerBean.StatisticsEmitEvent(\"test\", statHolder);\n\t\tString xmlString = serializer.serialize(eo).toString();\n\n\t\tSimpleParser parser = SingletonFactory.getParserInstance();\n\t\tTestDomBuilderHandler domHandler = new TestDomBuilderHandler();\n\t\tparser.parse(domHandler, xmlString.toCharArray(), 0, xmlString.length());\n\t\tassertFalse(domHandler.isError());\n\t\t\n\t\tElement ex = domHandler.getParsedElements().poll();\n\n\t\tBruteForceLockerBean.StatisticsEmitEvent eor = serializer.deserialize(ex);\n\t\tassertNotNull(eor);\n\n\t\tString xmlString2 = serializer.serialize(eor).toString();\n\t\tassertEquals(xmlString, xmlString2);\n\t}\n\n\tprivate static class TestDomBuilderHandler extends DomBuilderHandler {\n\n\t\tprivate boolean error = false;\n\n\t\t@Override\n\t\tpublic void error(String errorMessage) {\n\t\t\tsuper.error(errorMessage);\n\t\t\tthis.error = true;\n\t\t}\n\n\t\tpublic boolean isError() {\n\t\t\treturn error;\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "src/test/java/tigase/eventbus/impl/EventNameTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\npublic class EventNameTest {\n\n\t@Test\n\tpublic void testEquals() {\n\t\tassertEquals(new EventName(Event1.class), new EventName(Event1.class.getName()));\n\n\t\tassertEquals(new EventName(\"pl.ttt\"), new EventName(\"pl\", \"ttt\"));\n\n\t\tassertEquals(new EventName(\"*.ttt\"), new EventName(null, \"ttt\"));\n\t\tassertEquals(new EventName(\"pl.*\"), new EventName(\"pl\", null));\n\t\tassertEquals(new EventName(\"*.*\"), new EventName(null, null));\n\n\t\tassertEquals(new EventName(\".*\"), new EventName(\"\", null));\n\t\tassertEquals(new EventName(\"ttt\"), new EventName(\"\", \"ttt\"));\n\n\t\tassertEquals(new EventName(null, null), new EventName(null, null));\n\t\tassertEquals(new EventName(\"2\", null), new EventName(\"2\", null));\n\t\tassertEquals(new EventName(\"2\", \"1\"), new EventName(\"2\", \"1\"));\n\n\t\tassertNotEquals(new EventName(null, null), new EventName(\"2\", \"2\"));\n\t\tassertNotEquals(new EventName(null, null), new EventName(\"2\", null));\n\t\tassertNotEquals(new EventName(null, null), new EventName(null, \"2\"));\n\t\tassertNotEquals(new EventName(null, \"1\"), new EventName(\"2\", \"2\"));\n\t\tassertNotEquals(new EventName(\"2\", null), new EventName(\"2\", \"2\"));\n\t\tassertNotEquals(new EventName(\"1\", \"2\"), new EventName(\"2\", \"2\"));\n\t}\n\n\t@Test\n\tpublic void testGetters() {\n\t\tEventName e = new EventName(\"net.tigase\", \"Event\");\n\t\tassertEquals(\"net.tigase\", e.getPackage());\n\t\tassertEquals(\"Event\", e.getName());\n\n\t\te = new EventName(\"net.tigase.Event\");\n\t\tassertEquals(\"net.tigase\", e.getPackage());\n\t\tassertEquals(\"Event\", e.getName());\n\n\t\te = new EventName(\"net.tigase\", null);\n\t\tassertEquals(\"net.tigase\", e.getPackage());\n\t\tassertNull(e.getName());\n\n\t\te = new EventName(null, \"Event\");\n\t\tassertNull(e.getPackage());\n\t\tassertEquals(\"Event\", e.getName());\n\t}\n\n\t@Test\n\tpublic void testToString() {\n\t\tassertEquals(\"net.tigase.Event\", new EventName(\"net.tigase\", \"Event\").toString());\n\t\tassertEquals(\"net.tigase.*\", new EventName(\"net.tigase\", null).toString());\n\t\tassertEquals(\"*.Event\", new EventName(null, \"Event\").toString());\n\t\tassertEquals(\"Event\", new EventName(\"\", \"Event\").toString());\n\t\tassertEquals(\"*.*\", new EventName(null, null).toString());\n\t}\n\n\t@Test\n\tpublic void testToStringInt() {\n\t\tassertEquals(\"net.tigase.Event\", EventName.toString(\"net.tigase\", \"Event\"));\n\t\tassertEquals(\"net.tigase.*\", EventName.toString(\"net.tigase\", null));\n\t\tassertEquals(\"*.Event\", EventName.toString(null, \"Event\"));\n\t\tassertEquals(\"Event\", EventName.toString(\"\", \"Event\"));\n\t\tassertEquals(\"*.*\", EventName.toString(null, null));\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/eventbus/impl/EventsNameMapTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\nimport org.hamcrest.CoreMatchers;\nimport org.junit.Test;\n\nimport static org.hamcrest.CoreMatchers.not;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertThat;\n\n/**\n * Created by bmalkow on 17.11.2015.\n */\npublic class EventsNameMapTest {\n\n\t@Test\n\tpublic void test01() throws Exception {\n\t\tEventsNameMap<String> map = new EventsNameMap<String>();\n\t\tmap.put(null, null, \"null-null\");\n\t\tmap.put(null, null, \"null-null2\");\n\t\tmap.put(\"2\", null, \"null-2\");\n\t\tmap.put(null, \"1\", \"1-null\");\n\t\tmap.put(\"2\", \"1\", \"1-2\");\n\t\tmap.put(\"2\", \"1\", \"1-2_2\");\n\n\t\tmap.put(\"b0\", \"a0\", \"U\");\n\t\tmap.put(\"b0\", \"a1\", \"U\");\n\t\tmap.put(\"b1\", \"a0\", \"U\");\n\t\tmap.put(\"b1\", \"a1\", \"U\");\n\n\t\tassertEquals(7, map.getAllData().size());\n\t\tassertEquals(8, map.getAllListenedEvents().size());\n\n\t\tassertEquals(2, map.get(\"2\", \"1\").size());\n\t\tassertThat(map.get(\"2\", \"1\"), CoreMatchers.hasItem(\"1-2\"));\n\t\tassertThat(map.get(\"2\", \"1\"), CoreMatchers.hasItem(\"1-2_2\"));\n\t\tassertThat(map.get(null, \"1\"), CoreMatchers.hasItem(\"1-null\"));\n\t\tassertThat(map.get(\"2\", null), CoreMatchers.hasItem(\"null-2\"));\n\t\tassertThat(map.get(null, null), CoreMatchers.hasItem(\"null-null\"));\n\t\tassertEquals(2, map.get(null, null).size());\n\n\t\tmap.delete(\"2\", \"1\", \"1-2_2\");\n\t\tassertEquals(1, map.get(\"2\", \"1\").size());\n\n\t\tassertThat(map.get(\"2\", \"1\"), CoreMatchers.hasItem(\"1-2\"));\n\t\tassertThat(map.get(\"2\", \"1\"), not(CoreMatchers.hasItem(\"1-2_2\")));\n\n\t\tassertEquals(6, map.getAllData().size());\n\t\tassertEquals(8, map.getAllListenedEvents().size());\n\n\t\tmap.delete(\"U\");\n\n\t\tassertEquals(5, map.getAllData().size());\n\t\tassertEquals(4, map.getAllListenedEvents().size());\n\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/eventbus/impl/ReflectEventListenerHandlerFactoryTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.eventbus.impl;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.Collection;\n\n/**\n * Created by bmalkow on 26.01.2016.\n */\npublic class ReflectEventListenerHandlerFactoryTest {\n\n\t@Test\n\tpublic void testCreate() throws Exception {\n\t\tfinal ReflectEventListenerHandlerFactory f = new ReflectEventListenerHandlerFactory();\n\t\tEventBusImplementationTest.Consumer c = new EventBusImplementationTest.Consumer();\n\t\tCollection<AbstractHandler> handlers1 = f.create(c);\n\t\tCollection<AbstractHandler> handlers2 = f.create(c);\n\n\t\tAssert.assertEquals(handlers1.size(), handlers2.size());\n\t\tAssert.assertEquals(handlers1, handlers2);\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/io/CertificateContainerTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.io;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.cert.CertificateEntry;\nimport tigase.cert.CertificateGenerator;\nimport tigase.cert.CertificateGeneratorFactory;\nimport tigase.cert.CertificateUtil;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.io.repo.CertificateItem;\nimport tigase.io.repo.CertificateRepository;\nimport tigase.kernel.AbstractKernelWithUserRepositoryTestCase;\nimport tigase.kernel.core.Kernel;\n\nimport javax.net.ssl.TrustManager;\nimport java.io.IOException;\nimport java.security.GeneralSecurityException;\nimport java.security.cert.CertificateEncodingException;\nimport java.security.cert.X509Certificate;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.cert.CertificateUtil.createKeyPair;\nimport static tigase.cert.CertificateUtil.exportToPemFormat;\nimport static tigase.io.CertificateContainerIfc.CertificateEntity;\n\npublic class CertificateContainerTest\n\t\textends AbstractKernelWithUserRepositoryTestCase {\n\n\tprivate static final Logger LOGGER = Logger.getLogger(CertificateContainerTest.class.getName());\n\tprivate final String domain = \"example.com\";\n\tprivate final String mucDomain = \"muc.\" + domain;\n\tprivate final String wildcardDomain = \"*.\" + domain;\n\n\tprivate CertificateContainer certificateContainer;\n\tprivate SSLContextContainer sslContextContainer;\n\n\t@Test\n\tpublic void testRegularDomainForExistingCertificate() throws Exception {\n\t\ttestDomain(domain, domain, true);\n\t}\n\n\t@Test\n\tpublic void testSubdomainAgainstWildcardCertificate() throws Exception {\n\t\ttestDomain(\"push.\" + domain, wildcardDomain, true);\n\t}\n\n\t@Test\n\tpublic void testUpperCaseDomain() throws Exception {\n\t\ttestDomain(domain.toUpperCase(), domain, true);\n\t}\n\n\t@Test\n\tpublic void testDomainForNonexistentCertificate() throws Exception {\n\t\ttestDomain(\"xmpp.org\", \"xmpp.org\", false);\n\t}\n\n\t@Override\n\tprotected void registerBeans(Kernel kernel) {\n\t\tsuper.registerBeans(kernel);\n\t\tkernel.registerBean(\"eventBus\").asInstance(EventBusFactory.getInstance()).exportable().exec();\n\t\tkernel.registerBean(TestCertificateRepositoryWithoutStore.class).setActive(true).exportable().exec();\n\t\tkernel.registerBean(TestCertificateContainerWithoutStore.class).exec();\n\t\tkernel.registerBean(SSLContextContainer.class).exec();\n\t}\n\n\t@Before\n\tpublic void setup() throws GeneralSecurityException, IOException {\n\t\tcertificateContainer = getKernel().getInstance(CertificateContainer.class);\n\t\tsslContextContainer = getKernel().getInstance(SSLContextContainer.class);\n\n\t\taddCertificateForDomain(domain, true);\n\t\taddCertificateForDomain(\"*.\" + domain, true);\n\t}\n\t\n\t/**\n\t * If wildcard certificate is added it should be used instead of explicit one...\n\t */\n\t@Test\n\tpublic void testAddingCertificate() throws GeneralSecurityException, IOException {\n\n\t\taddCertificateForDomain(mucDomain, false);\n\t\taddCertificateForDomain(wildcardDomain, true);\n\n\t\tvar domainEntry = certificateContainer.getCertificateEntry(domain);\n\t\tAssert.assertNotNull(domainEntry);\n\t\tAssert.assertEquals(CertificateUtil.getCertCName((X509Certificate) domainEntry.getCertificate().get()), domain);\n\n\t\tvar wildcardDomainEntry = certificateContainer.getCertificateEntry(wildcardDomain);\n\t\tAssert.assertNotNull(wildcardDomainEntry);\n\t\tAssert.assertEquals(CertificateUtil.getCertCName((X509Certificate) wildcardDomainEntry.getCertificate().get()),\n\t\t                    wildcardDomain);\n\n\t\tvar mucDomainEntry = certificateContainer.getCertificateEntry(mucDomain);\n\t\tAssert.assertNotNull(mucDomainEntry);\n\t\t// we expect mucDomain as both certificates are valid, so specific is more important than wildcard\n\t\tAssert.assertEquals(mucDomain, CertificateUtil.getCertCName((X509Certificate) mucDomainEntry.getCertificate().get()));\n\t}\n\n\tprivate void testDomain(String hostname, String expectedDomain, boolean expectsExist) throws Exception {\n\t\tCertificateEntry certificateEntry = certificateContainer.getCertificateEntry(hostname);\n\n\t\tLOGGER.log(Level.INFO, \"Certificate for hostname \" + hostname + \": \" +\n\t\t\t\t(certificateEntry != null ? certificateEntry.toString(true) : \"n/a\"));\n\t\tif (expectsExist) {\n\t\t\tAssert.assertNotNull(certificateEntry);\n\t\t} else {\n\t\t\tAssert.assertNull(certificateEntry);\n\t\t}\n\n\t\tvar ssl = sslContextContainer.createContextHolder(\"SSL\", hostname, hostname, false, new TrustManager[0]);\n\n\t\tAssert.assertNotNull(ssl);\n\t\tAssert.assertNotNull(ssl.domainCertificate);\n\t\tString cNname = CertificateUtil.getCertCName(ssl.domainCertificate);\n\n\t\tif (expectsExist) {\n\t\t\tAssert.assertEquals(certificateEntry.getCertChain()[0], ssl.domainCertificate);\n\t\t} else {\n\t\t\tCertificateEntry generatedEntry = certificateContainer.getCertificateEntry(hostname);\n\t\t\tAssert.assertNotNull(generatedEntry);\n\t\t}\n\t\tAssert.assertEquals(expectedDomain, cNname);\n\t}\n\n\tprivate void addCertificateForDomain(String domain, boolean includeWildcardSubdomain)\n\t\t\tthrows GeneralSecurityException, IOException {\n\t\tfinal CertificateGenerator generator = CertificateGeneratorFactory.getGenerator();\n\t\tCertificateEntry selfSignedCertificate = generator.generateSelfSignedCertificateEntry(\"test@mail.com\", domain,\n\t\t                                                                                      \"OU\", \"O\", \"City\",\n\t\t                                                                                      \"State\", \"Country\",\n\t\t                                                                                      createKeyPair(1024,\n\t\t                                                                                                    \"secret\"),\n\t\t                                                                                      includeWildcardSubdomain);\n\t\tvar pemCertificate = exportToPemFormat(selfSignedCertificate);\n\t\tvar certificateEntity = new CertificateEntity(pemCertificate, domain, false, false);\n\t\tcertificateContainer.addCertificates(certificateEntity);\n\t}\n\n\tpublic static class TestCertificateContainerWithoutStore\n\t\t\textends CertificateContainer {\n\n\t\tpublic TestCertificateContainerWithoutStore() {\n\n\t\t}\n\n\t\t@Override\n\t\tvoid storeCertificateToFile(CertificateEntry entry, String filename)\n\t\t\t\tthrows CertificateEncodingException, IOException {\n\t\t\tthrow new RuntimeException(\n\t\t\t\t\tnew IOException(\"We tried storing certificate to file, even though we shouldn't\"));\n\t\t}\n\t}\n\n\tpublic static class TestCertificateRepositoryWithoutStore\n\t\t\textends CertificateRepository {\n\n\t\tpublic TestCertificateRepositoryWithoutStore() {\n\t\t}\n\n\t\t@Override\n\t\tprotected void storeSingleItem(CertificateItem item) {\n\t\t\tLOGGER.log(Level.SEVERE, \"Storing certificate to repository (we shouldn't?\");\n\t\t\tsuper.storeSingleItem(item);\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/io/SSLContextContainerTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.io;\n\nimport org.junit.Test;\n\nimport java.util.*;\n\nimport static org.junit.Assert.*;\nimport static tigase.io.SSLContextContainerAbstract.getSpareDomainNamesToRemove;\n\npublic class SSLContextContainerTest {\n\n\t@Test\n\tpublic void testFind() {\n\t\tfinal HashMap<String, String> domains = new HashMap<String, String>();\n\t\tdomains.put(\"one.com\", \"one.com\");\n\t\tdomains.put(\"a.two.com\", \"a.two.com\");\n\t\tdomains.put(\"*.two.com\", \"*.two.com\");\n\n\t\tassertEquals(\"one.com\", SSLContextContainer.find(domains, \"one.com\"));\n\t\tassertNull(SSLContextContainer.find(domains, \"tone.com\"));\n\t\tassertNull(SSLContextContainer.find(domains, \"zero.com\"));\n\t\tassertEquals(\"a.two.com\", SSLContextContainer.find(domains, \"a.two.com\"));\n\t\tassertEquals(\"*.two.com\", SSLContextContainer.find(domains, \"b.two.com\"));\n\t\tassertEquals(\"*.two.com\", SSLContextContainer.find(domains, \"b.two.com\"));\n\t\tassertNull(SSLContextContainer.find(domains, \"btwo.com\"));\n\t\tassertEquals(\"*.two.com\", SSLContextContainer.find(domains, \".two.com\"));\n\t}\n\n\t@Test\n\tpublic void testFindMuc() {\n\t\tfinal HashMap<String, String> domains = new HashMap<String, String>();\n\t\tdomains.put(\"*.tigase.org\", \"*.tigase.org\");\n\n\t\tassertEquals(\"*.tigase.org\", SSLContextContainer.find(domains, \"tigase.org\"));\n\t\tassertEquals(\"*.tigase.org\", SSLContextContainer.find(domains, \"muc.tigase.org\"));\n\n\t\tdomains.put(\"tigase.org\", \"tigase.org\");\n\n\t\tassertEquals(\"tigase.org\", SSLContextContainer.find(domains, \"tigase.org\"));\n\t\tassertEquals(\"*.tigase.org\", SSLContextContainer.find(domains, \"muc.tigase.org\"));\n\t}\n\n\t@Test\n\tpublic void testRemoveMatched() {\n\t\tfinal HashMap<String, String> contexts = new HashMap<String, String>();\n\t\tcontexts.put(\"one.com\", \"one.com\");\n\t\tcontexts.put(\"push.one.com\", \"push.one.com\");\n\t\tcontexts.put(\"sub.push.one.com\", \"sub.push.one.com\");\n\t\tcontexts.put(\"*.one.com\", \"*.one.com\");\n\t\tcontexts.put(\"a.two.com\", \"a.two.com\");\n\t\tcontexts.put(\"*.two.com\", \"*.two.com\");\n\n\t\tfinal Set<String> domains = new HashSet<>(Arrays.asList(\"one.com\", \"*.one.com\", \"two.com\"));\n\n\t\tSSLContextContainer.removeMatchedDomains(contexts, domains);\n\n\t\tassertFalse(contexts.containsKey(\"one.com\"));\n\t\tassertFalse(contexts.containsKey(\"push.one.com\"));\n\t\tassertTrue(contexts.containsKey(\"sub.push.one.com\"));\n\t\tassertFalse(contexts.containsKey(\"*.one.com\"));\n\t\tassertTrue(contexts.containsKey(\"a.two.com\"));\n\t\tassertTrue(contexts.containsKey(\"*.two.com\"));\n\t}\n\n\t@Test\n\tpublic void testGetSpareDomainNamesToRemove() {\n\n\t\tfinal Set<String> contexts = new TreeSet<>();\n\t\tcontexts.add(\"one.com\");\n\t\tcontexts.add(\"push.one.com\");\n\t\tcontexts.add(\"muc.one.com\");\n\t\tcontexts.add(\"sub.push.one.com\");\n\t\tcontexts.add(\"*.one.com\");\n\t\tcontexts.add(\"two.com\");\n\t\tcontexts.add(\"a.two.com\");\n\t\tcontexts.add(\"*.two.com\");\n\t\tcontexts.add(\"three.com\");\n\t\tcontexts.add(\"*.three.com\");\n\t\tcontexts.add(\"push.three.com\");\n\t\tcontexts.add(\"muc.three.com\");\n\n\t\tfinal Set<String> domains = new HashSet<>(Arrays.asList(\"one.com\", \"*.one.com\", \"two.com\"));\n\n\t\tfinal Set<String> spareDomainNamesToRemove = getSpareDomainNamesToRemove(contexts, domains);\n\n\t\t// basically only domains that match the wildcard but not the others - those will be overwritten directly by \"put\"\n\t\tassertFalse(spareDomainNamesToRemove.contains(\"one.com\"));\n\t\tassertTrue(spareDomainNamesToRemove.contains(\"push.one.com\"));\n\t\tassertTrue(spareDomainNamesToRemove.contains(\"muc.one.com\"));\n\t\tassertFalse(spareDomainNamesToRemove.contains(\"sub.push.one.com\"));\n\t\tassertFalse(spareDomainNamesToRemove.contains(\"*.one.com\"));\n\t\tassertFalse(spareDomainNamesToRemove.contains(\"two.com\"));\n\t\tassertFalse(spareDomainNamesToRemove.contains(\"a.two.com\"));\n\t\tassertFalse(spareDomainNamesToRemove.contains(\"*.two.com\"));\n\t\tassertFalse(spareDomainNamesToRemove.contains(\"three.com\"));\n\t\tassertFalse(spareDomainNamesToRemove.contains(\"*.three.com\"));\n\t\tassertFalse(spareDomainNamesToRemove.contains(\"push.three.com\"));\n\t\tassertFalse(spareDomainNamesToRemove.contains(\"muc.three.com\"));\n\n\t\tspareDomainNamesToRemove.forEach(contexts::remove);\n\n\t\tassertTrue(contexts.contains(\"one.com\"));\n\t\tassertFalse(contexts.contains(\"push.one.com\"));\n\t\tassertFalse(contexts.contains(\"muc.one.com\"));\n\t\tassertTrue(contexts.contains(\"sub.push.one.com\"));\n\t\tassertTrue(contexts.contains(\"*.one.com\"));\n\t\tassertTrue(contexts.contains(\"two.com\"));\n\t\tassertTrue(contexts.contains(\"a.two.com\"));\n\t\tassertTrue(contexts.contains(\"*.two.com\"));\n\t\tassertTrue(contexts.contains(\"three.com\"));\n\t\tassertTrue(contexts.contains(\"*.three.com\"));\n\t\tassertTrue(contexts.contains(\"push.three.com\"));\n\t\tassertTrue(contexts.contains(\"muc.three.com\"));\n\n\t\tcontexts.addAll(domains);\n\n\t\tassertTrue(contexts.contains(\"one.com\"));\n\t\tassertFalse(contexts.contains(\"push.one.com\"));\n\t\tassertFalse(contexts.contains(\"muc.one.com\"));\n\t\tassertTrue(contexts.contains(\"sub.push.one.com\"));\n\t\tassertTrue(contexts.contains(\"*.one.com\"));\n\t\tassertTrue(contexts.contains(\"two.com\"));\n\t\tassertTrue(contexts.contains(\"a.two.com\"));\n\t\tassertTrue(contexts.contains(\"*.two.com\"));\n\t\tassertTrue(contexts.contains(\"three.com\"));\n\t\tassertTrue(contexts.contains(\"*.three.com\"));\n\t\tassertTrue(contexts.contains(\"push.three.com\"));\n\t\tassertTrue(contexts.contains(\"muc.three.com\"));\n\t}\n\n\t@Test\n\tpublic void testGetSpareDomainNamesToRemoveSimple() {\n\n\t\tfinal Set<String> contexts = new TreeSet<>();\n\t\tcontexts.add(\"one.com\");\n\t\tcontexts.add(\"two.com\");\n\n\t\tfinal Set<String> domains = new HashSet<>(Arrays.asList(\"one.com\", \"*.one.com\"));\n\n\t\tfinal Set<String> spareDomainNamesToRemove = getSpareDomainNamesToRemove(contexts, domains);\n\n\t\t// basically only domains that match the wildcard but not the others - those will be overwritten directly by \"put\"\n\t\tassertTrue(spareDomainNamesToRemove.isEmpty());\n\n\t\tspareDomainNamesToRemove.forEach(contexts::remove);\n\n\t\tassertTrue(contexts.contains(\"one.com\"));\n\t\tassertFalse(contexts.contains(\"*.one.com\"));\n\t\tassertTrue(contexts.contains(\"two.com\"));\n\n\t\tcontexts.addAll(domains);\n\n\t\tassertTrue(contexts.contains(\"one.com\"));\n\t\tassertTrue(contexts.contains(\"*.one.com\"));\n\t\tassertTrue(contexts.contains(\"two.com\"));\n\t}\n\n\t@Test\n\tpublic void testGetSpareDomainNamesToRemoveAtlantiscity() {\n\n\t\tfinal Set<String> contexts = new TreeSet<>();\n\t\tcontexts.add(\"atlantiscity\");\n\t\tcontexts.add(\"firefly\");\n\n\t\tfinal Set<String> domains = new HashSet<>(Arrays.asList(\"atlantiscity\", \"*.atlantiscity\"));\n\n\t\tfinal Set<String> spareDomainNamesToRemove = getSpareDomainNamesToRemove(contexts, domains);\n\n\t\t// basically only domains that match the wildcard but not the others - those will be overwritten directly by \"put\"\n\t\tassertTrue(spareDomainNamesToRemove.isEmpty());\n\n\t\tspareDomainNamesToRemove.forEach(contexts::remove);\n\n\t\tassertTrue(contexts.contains(\"atlantiscity\"));\n\t\tassertFalse(contexts.contains(\"*.atlantiscity\"));\n\t\tassertTrue(contexts.contains(\"firefly\"));\n\n\t\tcontexts.addAll(domains);\n\n\t\tassertTrue(contexts.contains(\"atlantiscity\"));\n\t\tassertTrue(contexts.contains(\"*.atlantiscity\"));\n\t\tassertTrue(contexts.contains(\"firefly\"));\n\t}\n\n\t@Test\n\tpublic void testGetSpareDomainNamesToRemoveLongDomains() {\n\n\t\tfinal Set<String> contexts = new TreeSet<>();\n\t\tcontexts.add(\"chat.example.com\");\n\t\tcontexts.add(\"muc.chat.example.com\");\n\n\t\tfinal Set<String> domains = new HashSet<>(Arrays.asList(\"chat.example.com\", \"*.chat.example.com\"));\n\n\t\tfinal Set<String> spareDomainNamesToRemove = getSpareDomainNamesToRemove(contexts, domains);\n\n\t\t// basically only domains that match the wildcard but not the others - those will be overwritten directly by \"put\"\n\t\tassertEquals(1, spareDomainNamesToRemove.size());\n\t\tassertTrue(spareDomainNamesToRemove.contains(\"muc.chat.example.com\"));\n\n\t\tspareDomainNamesToRemove.forEach(contexts::remove);\n\n\t\tassertTrue(contexts.contains(\"chat.example.com\"));\n\t\tassertFalse(contexts.contains(\"*.chat.example.com\"));\n\n\t\tcontexts.addAll(domains);\n\n\t\tassertTrue(contexts.contains(\"chat.example.com\"));\n\t\tassertTrue(contexts.contains(\"*.chat.example.com\"));\n\t}\n\n\t@Test\n\tpublic void testRemoveMatchedMuc() {\n\t\tfinal HashMap<String, String> contexts = new HashMap<String, String>();\n\t\tcontexts.put(\"one.com\", \"one.com\");\n\t\tcontexts.put(\"muc.one.com\", \"muc.one.com\");\n\t\tcontexts.put(\"*.one.com\", \"*.one.com\");\n\n\t\tfinal Set<String> domains = new HashSet<>(Arrays.asList(\"one.com\", \"*.one.com\"));\n\n\t\tSSLContextContainer.removeMatchedDomains(contexts, domains);\n\n\t\tassertFalse(contexts.containsKey(\"one.com\"));\n\t\tassertFalse(contexts.containsKey(\"muc.one.com\"));\n\t\tassertFalse(contexts.containsKey(\"*.one.com\"));\n\t\tassertTrue(contexts.isEmpty());\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/AbstractKernelTestCase.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport tigase.kernel.core.Kernel;\nimport tigase.util.reflection.ClassUtilBean;\n\n/**\n * Class is a base class for tests requiring usage of Kernel instances.\n */\npublic class AbstractKernelTestCase {\n\n\tprivate Kernel kernel;\n\n\t@Before\n\tpublic void setupKernel() {\n\t\tkernel = new Kernel();\n\t\tregisterBeans(kernel);\n\t}\n\n\t@After\n\tpublic void tearDownKernel() {\n\t\tkernel.gc();\n\t\tkernel = null;\n\t}\n\n\tprotected void registerBeans(Kernel kernel) {\n\t\tkernel.registerBean(\"classUtilBean\")\n\t\t\t\t.asInstance(ClassUtilBean.getInstance())\n\t\t\t\t.exportable()\n\t\t\t\t.setActive(true)\n\t\t\t\t.exec();\n\t}\n\n\tprotected <T> T getInstance(Class<T> clazz) {\n\t\treturn kernel.getInstance(clazz);\n\t}\n\n\tprotected <T> T getInstance(String name) {\n\t\treturn kernel.getInstance(name);\n\t}\n\n\tprotected Kernel getKernel() {\n\t\treturn kernel;\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/AbstractKernelWithUserRepositoryTestCase.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel;\n\nimport tigase.db.AuthRepository;\nimport tigase.db.UserRepository;\nimport tigase.db.xml.XMLRepository;\nimport tigase.kernel.core.Kernel;\n\n/**\n * Class is a base class for tests requiring instances of Kernel and User/Auth repositories.\n */\npublic class AbstractKernelWithUserRepositoryTestCase extends AbstractKernelTestCase {\n\n\tprivate XMLRepository repository;\n\n\t@Override\n\tprotected void registerBeans(Kernel kernel) {\n\t\tsuper.registerBeans(kernel);\n\t\ttry {\n\t\t\tString xmlRepositoryURI = \"memory://xmlRepo?autoCreateUser=true\";\n\t\t\trepository = new XMLRepository();\n\t\t\trepository.initRepository(xmlRepositoryURI, null);\n\t\t\tkernel.registerBean(\"userAuthRepository\").asInstance(repository).exportable().exec();\n\t\t} catch (Exception ex) {\n\t\t\tthrow new RuntimeException(\"Failed to initialize user/auth repository\", ex);\n\t\t}\n\t}\n\n\tpublic UserRepository getUserRepository() {\n\t\treturn repository;\n\t}\n\n\tpublic AuthRepository getAuthRepository() {\n\t\treturn repository;\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/Bean1.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\n\nimport java.util.Set;\n\n@Bean(name = \"bean1\", active = true)\npublic class Bean1 {\n\n\t@Inject(nullAllowed = true)\n\tprivate Bean2 bean2;\n\n\t@Inject(nullAllowed = true)\n\tprivate Bean3 bean3;\n\n\t@Inject(type = Special.class, nullAllowed = true)\n\tprivate Set<Special> collectionOfSpecial;\n\n\t@Inject(nullAllowed = true)\n\tprivate Special[] tableOfSpecial;\n\n\tpublic Bean2 getBean2() {\n\t\treturn bean2;\n\t}\n\n\tpublic void setBean2(Bean2 bean2) {\n\t\tthis.bean2 = bean2;\n\t}\n\n\tpublic Bean3 getBean3() {\n\t\treturn bean3;\n\t}\n\n\tpublic void setBean3(Bean3 bean3) {\n\t\tthis.bean3 = bean3;\n\t}\n\n\tpublic Set<Special> getCollectionOfSpecial() {\n\t\treturn collectionOfSpecial;\n\t}\n\n\tpublic void setCollectionOfSpecial(Set<Special> xxx) {\n\t\tthis.collectionOfSpecial = xxx;\n\t}\n\n\tpublic Special[] getTableOfSpecial() {\n\t\treturn tableOfSpecial;\n\t}\n\n\tpublic void setTableOfSpecial(Special[] ss) {\n\t\tthis.tableOfSpecial = ss;\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/Bean10.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\n\nimport java.util.Set;\n\n@Bean(name = \"bean10\", active = true)\npublic class Bean10 {\n\n\t@Inject(type = Special.class, nullAllowed = true)\n\tprivate Set<Special> collectionOfSpecial;\n\n\t@Inject(nullAllowed = true)\n\tprivate Special[] tableOfSpecial;\n\n\tpublic Set<Special> getCollectionOfSpecial() {\n\t\treturn collectionOfSpecial;\n\t}\n\n\tpublic void setCollectionOfSpecial(Set<Special> collectionOfSpecial) {\n\t\tthis.collectionOfSpecial = collectionOfSpecial;\n\t}\n\n\tpublic Special[] getTableOfSpecial() {\n\t\treturn tableOfSpecial;\n\t}\n\n\tpublic void setTableOfSpecial(Special[] tableOfSpecial) {\n\t\tthis.tableOfSpecial = tableOfSpecial;\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/Bean2.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel;\n\nimport tigase.kernel.beans.Inject;\n\npublic class Bean2 {\n\n\t@Inject\n\tprivate Bean3 bean3;\n\n\t@Inject(bean = \"bean4_1\", nullAllowed = true)\n\tprivate Bean4 bean4;\n\n\tpublic Bean3 getBean3() {\n\t\treturn bean3;\n\t}\n\n\tpublic void setBean3(Bean3 bean3) {\n\t\tthis.bean3 = bean3;\n\t}\n\n\tpublic Bean4 getBean4() {\n\t\treturn bean4;\n\t}\n\n\tpublic void setBean4(Bean4 bean4) {\n\t\tthis.bean4 = bean4;\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/Bean3.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel;\n\nimport tigase.kernel.beans.Inject;\n\npublic class Bean3\n\t\timplements Special {\n\n\t@Inject(bean = \"bean4\")\n\tprivate Bean4 bean4;\n\n\t@Inject(bean = \"bean4_1\", nullAllowed = true)\n\tprivate Bean4 bean41;\n\n\tpublic Bean4 getBean4() {\n\t\treturn bean4;\n\t}\n\n\tpublic Bean4 getBean41() {\n\t\treturn bean41;\n\t}\n\t//\n\t// public void setBean4(Bean4 bean4) {\n\t// this.bean4 = bean4;\n\t// }\n\t//\n\t// public void setBean41(Bean4 bean41) {\n\t// this.bean41 = bean41;\n\t// }\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/Bean4.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel;\n\npublic class Bean4\n\t\timplements Special {\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/Bean5.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel;\n\nimport tigase.TestLogger;\nimport tigase.kernel.beans.UnregisterAware;\nimport tigase.kernel.beans.config.ConfigField;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class Bean5\n\t\timplements UnregisterAware {\n\n\tprivate static final Logger log = TestLogger.getLogger(Bean5.class);\n\n\t@ConfigField(desc = \"One field with value\")\n\tprivate Long value = 15l;\n\n\t@Override\n\tpublic void beforeUnregister() {\n\t\tlog.log(Level.FINE, \"Destroying Bean5 class\");\n\t}\n\n\tpublic Long getValue() {\n\t\treturn value;\n\t}\n\n\tpublic void setValue(Long value) {\n\t\tthis.value = value;\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/Bean5Factory.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel;\n\nimport tigase.kernel.beans.BeanFactory;\nimport tigase.kernel.beans.Inject;\n\npublic class Bean5Factory\n\t\timplements BeanFactory<Bean5> {\n\n\t@Inject(nullAllowed = true)\n\tprivate Bean1 bean;\n\n\t@Override\n\tpublic Bean5 createInstance() throws KernelException {\n\t\treturn new Bean5();\n\t}\n\n\tpublic Bean1 getBean() {\n\t\treturn bean;\n\t}\n\n\tpublic void setBean(Bean1 bean) {\n\t\tthis.bean = bean;\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/Bean6.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel;\n\nimport tigase.kernel.beans.config.ConfigField;\n\npublic class Bean6\n\t\timplements Special {\n\n\t@ConfigField(desc = \"Field with string value\")\n\tprivate String testValue;\n\n\tpublic String getTestValue() {\n\t\treturn testValue;\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/Bean7.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel;\n\nimport tigase.kernel.beans.Inject;\n\npublic class Bean7\n\t\timplements Special {\n\n\t@Inject(bean = \"beanX\")\n\tprivate Object obj;\n\n\tpublic Object getObj() {\n\t\treturn obj;\n\t}\n\n\tpublic void setObj(Object obj) {\n\t\tthis.obj = obj;\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/Bean8.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Converter;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.config.ConfigField;\n\n@Bean(name = \"bean8\", active = true)\npublic class Bean8 {\n\n\t@Inject(nullAllowed = false)\n\tprivate Bean6 bean6;\n\n\t@ConfigField(desc = \"Field with string value\")\n\t@Converter(converter = KernelTest.CustomTypesConverter.class)\n\tprivate String sample;\n\n\tpublic Bean6 getBean6() {\n\t\treturn bean6;\n\t}\n\n\tpublic void setBean6(Bean6 bean6) {\n\t\tthis.bean6 = bean6;\n\t}\n\n\tpublic String getSample() {\n\t\treturn sample;\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/DefaultTypesConverterTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport tigase.conf.ConfigReader;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.io.File;\nimport java.lang.reflect.ParameterizedType;\nimport java.time.Duration;\nimport java.util.*;\nimport java.util.logging.Level;\n\nimport static org.junit.Assert.*;\n\npublic class DefaultTypesConverterTest {\n\n\tpublic enum XT {\n\t\ta1,\n\t\tb2,\n\t\tc3\n\t}\n\n\tprivate HashMap<String, EnumSet<XT>> mapEnumSetField;\n\tprivate Collection<Integer> collectionIntField;\n\tprivate List<Integer> listIntField;\n\tprivate ArrayList<Integer> arrayListIntField;\n\tprivate Set<Integer> setIntField;\n\n\t@Test\n\tpublic void testDurationConvert() throws Exception {\n\t\tTypesConverter converter = new DefaultTypesConverter();\n\n\t\tAssert.assertEquals(Duration.ofDays(1), converter.convert(\"P1D\", Duration.class));\n\t}\n\n\t@Test\n\tpublic void testConvert() throws Exception {\n\t\tTypesConverter converter = new DefaultTypesConverter();\n\n\t\tAssert.assertEquals(Integer.valueOf(123), converter.convert(\"123\", Integer.class));\n\t\tAssert.assertEquals(Integer.valueOf(123), converter.convert(Integer.valueOf(123), Integer.class));\n\t\tAssert.assertEquals(\"123\", converter.convert(Integer.valueOf(123), String.class));\n\n\t\tInteger x1 = new Integer(1);\n\t\tInteger x2 = converter.convert(x1, Integer.class);\n\t\tAssert.assertTrue(x1 == x2);\n\n\t\tAssert.assertEquals(XT.a1, converter.convert(\"a1\", XT.class));\n\n\t\tAssert.assertEquals(JID.jidInstanceNS(\"a@.b.c/d\"), converter.convert(\"a@.b.c/d\", JID.class));\n\t\tAssert.assertEquals(BareJID.bareJIDInstanceNS(\"a@.b.c\"), converter.convert(\"a@.b.c\", BareJID.class));\n\n\t\tAssert.assertEquals(\"test\", converter.convert(\"test\", String.class));\n\t\tAssert.assertEquals(Long.valueOf(123), converter.convert(\"123\", Long.class));\n\t\tAssert.assertEquals(1234l, (long) converter.convert(\"1234\", long.class));\n\n\t\tAssert.assertEquals(Integer.valueOf(123), converter.convert(\"123\", Integer.class));\n\n\t\tAssert.assertEquals(Boolean.FALSE, converter.convert(\"anything\", Boolean.class));\n\t\tAssert.assertEquals(Boolean.TRUE, converter.convert(\"yes\", Boolean.class));\n\t\tAssert.assertEquals(Boolean.TRUE, converter.convert(\"true\", Boolean.class));\n\t\tAssert.assertEquals(Boolean.TRUE, converter.convert(\"on\", Boolean.class));\n\t\tAssert.assertEquals(Boolean.TRUE, converter.convert(\"1\", Boolean.class));\n\t\tAssert.assertTrue(converter.convert(\"1\", boolean.class));\n\n\t\tAssert.assertEquals(Float.valueOf(123.1f), converter.convert(\"123.1\", Float.class));\n\t\tAssert.assertEquals(Float.valueOf(123.1f), converter.convert(\"123.1\", float.class));\n\n\t\tAssert.assertEquals(Double.valueOf(123.1d), converter.convert(\"123.1\", Double.class));\n\t\tAssert.assertEquals(Double.valueOf(123.1d), converter.convert(\"123.1\", double.class));\n\n\t\tassertArrayEquals(\n\t\t\t\tnew JID[]{JID.jidInstanceNS(\"1@b.c/a\"), JID.jidInstanceNS(\"2@b.c/a\"), JID.jidInstanceNS(\"3@b.c/a\")},\n\t\t\t\tconverter.convert(\"1@b.c/a,2@b.c/a,3@b.c/a\", JID[].class));\n\n\t\tassertArrayEquals(new BareJID[]{BareJID.bareJIDInstanceNS(\"1@b.c\"), BareJID.bareJIDInstanceNS(\"2@b.c\"),\n\t\t\t\t\t\t\t\t\t\tBareJID.bareJIDInstanceNS(\"3@b.c\")},\n\t\t\t\t\t\t  converter.convert(\"1@b.c,2@b.c,3@b.c\", BareJID[].class));\n\n\t\tassertArrayEquals(new String[]{\"1\", \"2\", \"3\"}, converter.convert(\"1,2,3\", String[].class));\n\t\tassertArrayEquals(new XT[]{XT.a1, XT.a1, XT.c3}, converter.convert(\"a1,a1,c3\", XT[].class));\n\t\tassertArrayEquals(new Integer[]{1, 2, 3, 1}, converter.convert(\"1,2,3,1\", Integer[].class));\n\n\t\tassertArrayEquals(new int[]{1, 2, 3, 1}, converter.convert(\"1,2,3,1\", int[].class));\n\t\tassertArrayEquals(new long[]{1, 2, 3, 1}, converter.convert(\"1,2,3,1\", long[].class));\n\n\t\tassertArrayEquals(new byte[]{1, 2, 3, 4}, converter.convert(\"1,2,3,4\", byte[].class));\n\t\tassertArrayEquals(new byte[]{48, 49, 50, 52}, converter.convert(\"string:0124\", byte[].class));\n\t\tassertArrayEquals(new byte[]{48, 49, 50, 53}, converter.convert(\"base64:MDEyNQ==\", byte[].class));\n\n\t\tassertArrayEquals(new char[]{49, 50, 51, 52}, converter.convert(\"1,2,3,4\", char[].class));\n\t\tassertArrayEquals(new char[]{48, 49, 50, 52}, converter.convert(\"string:0124\", char[].class));\n\t\tassertArrayEquals(new char[]{48, 49, 50, 53}, converter.convert(\"base64:MDEyNQ==\", char[].class));\n\n\t\tAssert.assertEquals(Level.CONFIG, converter.convert(\"CONFIG\", Level.class));\n\t\tAssert.assertEquals(Level.ALL, converter.convert(\"ALL\", Level.class));\n\n\t\tAssert.assertEquals(new File(\"/dupa.txt\"), converter.convert(\"/dupa.txt\", File.class));\n\t\tAssert.assertEquals(new File(\"/dupa.txt\"),\n\t\t\t\t\t\t\tconverter.convert(converter.toString(new File(\"/dupa.txt\")), File.class));\n\n\t\tHashMap<String, String> values = new HashMap<>();\n\t\tvalues.put(\"t1\", \"a1,b2\");\n\t\tvalues.put(\"t2\", \"b2,c3\");\n\t\tmapEnumSetField = converter.convert(values, HashMap.class,\n\t\t\t\t\t\t\t\t\t\t\tthis.getClass().getDeclaredField(\"mapEnumSetField\").getGenericType());\n\t\tassertEquals(EnumSet.of(XT.a1, XT.b2), mapEnumSetField.get(\"t1\"));\n\t\tassertEquals(EnumSet.of(XT.b2, XT.c3), mapEnumSetField.get(\"t2\"));\n\n\t\tvalues = new HashMap<>();\n\t\tvalues.put(\"t1\", \"A1,B2\");\n\t\tvalues.put(\"t2\", \"B2,C3\");\n\t\tmapEnumSetField = converter.convert(values, HashMap.class,\n\t\t\t\t\t\t\t\t\t\t\tthis.getClass().getDeclaredField(\"mapEnumSetField\").getGenericType());\n\t\tassertEquals(EnumSet.of(XT.a1, XT.b2), mapEnumSetField.get(\"t1\"));\n\t\tassertEquals(EnumSet.of(XT.b2, XT.c3), mapEnumSetField.get(\"t2\"));\n\n\t\tvalues = new HashMap<>();\n\t\tvalues.put(\"t1\", \"A1,b2\");\n\t\tvalues.put(\"t2\", \"b2,C3\");\n\t\tmapEnumSetField = converter.convert(values, HashMap.class,\n\t\t\t\t\t\t\t\t\t\t\tthis.getClass().getDeclaredField(\"mapEnumSetField\").getGenericType());\n\t\tassertEquals(EnumSet.of(XT.a1, XT.b2), mapEnumSetField.get(\"t1\"));\n\t\tassertEquals(EnumSet.of(XT.b2, XT.c3), mapEnumSetField.get(\"t2\"));\n\n\t\tassertEquals(System.getProperty(\"java.home\"),\n\t\t\t\t\t converter.convert(new ConfigReader.PropertyVariable(\"java.home\", null), String.class));\n\n\t\tConfigReader.CompositeVariable compositeVariable = new ConfigReader.CompositeVariable();\n\t\tcompositeVariable.add(\"Java: \");\n\t\tcompositeVariable.add('+', new ConfigReader.PropertyVariable(\"java.vendor\", null));\n\t\tcompositeVariable.add('+', \" \");\n\t\tcompositeVariable.add('+', new ConfigReader.PropertyVariable(\"java.version\", null));\n\t\tassertEquals(\"Java: \" + System.getProperty(\"java.vendor\") + \" \" + System.getProperty(\"java.version\"),\n\t\t\t\t\t converter.convert(compositeVariable, String.class));\n\n\t\tcompositeVariable = new ConfigReader.CompositeVariable();\n\t\tcompositeVariable.add(5);\n\t\tcompositeVariable.add('-', 2);\n\t\tcompositeVariable.add('*', 60);\n\t\tcompositeVariable.add('*', 1000);\n\t\tassertEquals(new Integer(-119995), converter.convert(compositeVariable, Integer.class));\n\n\t\tcompositeVariable = new ConfigReader.CompositeVariable();\n\t\tcompositeVariable.add(5);\n\t\tcompositeVariable.add('*', 60.0);\n\t\tcompositeVariable.add('*', 1000);\n\t\tassertEquals(new Double(300000.0), converter.convert(compositeVariable, Double.class));\n\n\t\ttry {\n\t\t\tHashMap<String, String> test = new HashMap<>();\n\t\t\ttest.put(\"test-domain.com\", \"true\");\n\t\t\tassertFalse(\"Invalid conversion of Map<> to String, should throw!\", converter.convert(test, String.class) != null);\n\t\t} catch (RuntimeException ex) {\n\t\t\tassertTrue(true);\n\t\t}\n\t\ttry {\n\t\t\tHashMap<String, String> test = new HashMap<>();\n\t\t\ttest.put(\"test-domain.com\", \"true\");\n\t\t\tassertFalse(\"Invalid conversion of Map<> to BareJID, should throw!\", converter.convert(test, BareJID.class) != null);\n\t\t} catch (RuntimeException ex) {\n\t\t\tassertTrue(true);\n\t\t}\n\t\ttry {\n\t\t\tHashMap<String, String> test = new HashMap<>();\n\t\t\ttest.put(\"test-domain.com\", \"true\");\n\t\t\tassertFalse(\"Invalid conversion of Map<> to JID, should throw!\", converter.convert(test, JID.class) != null);\n\t\t} catch (RuntimeException ex) {\n\t\t\tassertTrue(true);\n\t\t}\n\n\t\ttry {\n\t\t\tList<String> test = new ArrayList<>();\n\t\t\ttest.add(\"test-domain.com\");\n\t\t\tassertFalse(\"Invalid conversion of List<> to String, should throw!\", converter.convert(test, String.class) != null);\n\t\t} catch (RuntimeException ex) {\n\t\t\tassertTrue(true);\n\t\t}\n\t\ttry {\n\t\t\tList<String> test = new ArrayList<>();\n\t\t\ttest.add(\"test-domain.com\");\n\t\t\tassertFalse(\"Invalid conversion of List<> to BareJID, should throw!\", converter.convert(test, BareJID.class) != null);\n\t\t} catch (RuntimeException ex) {\n\t\t\tassertTrue(true);\n\t\t}\n\t\ttry {\n\t\t\tList<String> test = new ArrayList<>();\n\t\t\ttest.add(\"test-domain.com\");\n\t\t\tassertFalse(\"Invalid conversion of List<> to JID, should throw!\", converter.convert(test, JID.class) != null);\n\t\t} catch (RuntimeException ex) {\n\t\t\tassertTrue(true);\n\t\t}\n\n\t}\n\n\t@Test\n\tpublic void testCollections() throws NoSuchFieldException {\n\t\tTypesConverter converter = new DefaultTypesConverter();\n\n\t\t// -----\n\n\t\tParameterizedType pt = (ParameterizedType) this.getClass().getDeclaredField(\"collectionIntField\").getGenericType();\n\t\tcollectionIntField = converter.convert(\"1,2,3\", (Class<Collection>) pt.getRawType(), pt);\n\t\tassertArrayEquals(new Integer[]{1, 2, 3}, collectionIntField.toArray(Integer[]::new));\n\n\t\tcollectionIntField = converter.convert(new int[]{1, 2, 3}, (Class<Collection>) pt.getRawType(), pt);\n\t\tassertArrayEquals(new Integer[]{1, 2, 3}, collectionIntField.toArray(Integer[]::new));\n\n\t\tcollectionIntField = converter.convert(new String[]{\"1\", \"2\", \"3\"}, (Class<Collection>) pt.getRawType(), pt);\n\t\tassertArrayEquals(new Integer[]{1, 2, 3}, collectionIntField.toArray(Integer[]::new));\n\n\t\tcollectionIntField = converter.convert(Arrays.asList(1,2,3), (Class<Collection>) pt.getRawType(), pt);\n\t\tassertArrayEquals(new Integer[]{1, 2, 3}, collectionIntField.toArray(Integer[]::new));\n\n\t\ttry {\n\t\t\tcollectionIntField.add(4);\n\t\t\tassertFalse(true);\n\t\t} catch (UnsupportedOperationException ex) {\n\t\t\t// ok\n\t\t}\n\n\t\t// -----\n\n\t\tpt = (ParameterizedType) this.getClass().getDeclaredField(\"listIntField\").getGenericType();\n\t\tlistIntField = converter.convert(\"1,2,3\" , (Class<List>) pt.getRawType(), pt);\n\t\tassertArrayEquals(new Integer[]{1, 2, 3}, listIntField.toArray(Integer[]::new));\n\n\t\tlistIntField = converter.convert(new int[]{1, 2, 3}, (Class<List>) pt.getRawType(), pt);\n\t\tassertArrayEquals(new Integer[]{1, 2, 3}, listIntField.toArray(Integer[]::new));\n\n\t\tlistIntField = converter.convert(new String[]{\"1\", \"2\", \"3\"}, (Class<List>) pt.getRawType(), pt);\n\t\tassertArrayEquals(new Integer[]{1, 2, 3}, listIntField.toArray(Integer[]::new));\n\n\t\tlistIntField = converter.convert(Arrays.asList(1,2,3) , (Class<List>) pt.getRawType(), pt);\n\t\tassertArrayEquals(new Integer[]{1, 2, 3}, listIntField.toArray(Integer[]::new));\n\n\t\ttry {\n\t\t\tlistIntField.add(4);\n\t\t\tassertFalse(true);\n\t\t} catch (UnsupportedOperationException ex) {\n\t\t\t// ok\n\t\t}\n\n\t\t// -----\n\n\t\tpt = (ParameterizedType) this.getClass().getDeclaredField(\"setIntField\").getGenericType();\n\t\tsetIntField = converter.convert(\"1,2,3\", (Class<Set>) pt.getRawType(), pt);\n\t\tassertEquals(listIntField.size(), setIntField.stream().filter(i -> listIntField.contains(i)).count());\n\n\t\tsetIntField = converter.convert(new int[]{1, 2, 3}, (Class<Set>) pt.getRawType(), pt);\n\t\tassertEquals(listIntField.size(), setIntField.stream().filter(i -> listIntField.contains(i)).count());\n\n\t\tsetIntField = converter.convert(new String[]{\"1\", \"2\", \"3\"}, (Class<Set>) pt.getRawType(), pt);\n\t\tassertEquals(listIntField.size(), setIntField.stream().filter(i -> listIntField.contains(i)).count());\n\n\t\tsetIntField = converter.convert(Arrays.asList(1,2,3), (Class<Set>) pt.getRawType(), pt);\n\t\tassertEquals(listIntField.size(), setIntField.stream().filter(i -> listIntField.contains(i)).count());\n\n\t\ttry {\n\t\t\tsetIntField.add(4);\n\t\t\tassertFalse(true);\n\t\t} catch (UnsupportedOperationException ex) {\n\t\t\t// ok\n\t\t}\n\t\t// -----\n\n\t\tpt = (ParameterizedType) this.getClass().getDeclaredField(\"arrayListIntField\").getGenericType();\n\t\tarrayListIntField = converter.convert(\"1,2,3\" , (Class<ArrayList>) pt.getRawType(), pt);\n\t\tassertArrayEquals(new Integer[]{1, 2, 3}, arrayListIntField.toArray(Integer[]::new));\n\n\t\tarrayListIntField = converter.convert(new int[]{1, 2, 3}, (Class<ArrayList>) pt.getRawType(), pt);\n\t\tassertArrayEquals(new Integer[]{1, 2, 3}, arrayListIntField.toArray(Integer[]::new));\n\n\t\tarrayListIntField = converter.convert(new String[]{\"1\", \"2\", \"3\"}, (Class<ArrayList>) pt.getRawType(), pt);\n\t\tassertArrayEquals(new Integer[]{1, 2, 3}, arrayListIntField.toArray(Integer[]::new));\n\n\t\tarrayListIntField = converter.convert(Arrays.asList(1,2,3), (Class<ArrayList>) pt.getRawType(), pt);\n\t\tassertArrayEquals(new Integer[]{1, 2, 3}, arrayListIntField.toArray(Integer[]::new));\n\n\t\ttry {\n\t\t\tarrayListIntField.add(4);\n\t\t} catch (UnsupportedOperationException ex) {\n\t\t\tassertFalse(true);\n\t\t}\n\n\t}\n\n\t@Test\n\tpublic void testParcelable() {\n\t\tTypesConverter converter = new DefaultTypesConverter();\n\n\t\tParcelableObject o1 = new ParcelableObject();\n\t\to1.setIp(\"1.2.3.4\");\n\t\to1.setTimestamp(123456);\n\t\to1.setJid(JID.jidInstanceNS(\"ala@ma.kota\"));\n\t\to1.setDescription(\"blah bla, blah; blah... :-)\");\n\n\t\tString encoded = converter.toString(o1);\n\t\tassertEquals(\"1.2.3.4,ala@ma.kota,123456,blah bla\\\\, blah; blah... :-)\", encoded);\n\n\t\tParcelableObject o2 = converter.convert(encoded, ParcelableObject.class);\n\n\t\tassertEquals(o1.getIp(), o2.getIp());\n\t\tassertEquals(o1.getTimestamp(), o2.getTimestamp());\n\t\tassertEquals(o1.getJid(), o2.getJid());\n\t\tassertEquals(o1.getDescription(), o2.getDescription());\n\t\tassertEquals(o1, o2);\n\n\t}\n\n\t@Test\n\tpublic void testToString() {\n\t\tTypesConverter converter = new DefaultTypesConverter();\n\n\t\tAssert.assertEquals(\"a1\", converter.toString(XT.a1));\n\t\tAssert.assertEquals(\"a1,a1,b2\", converter.toString(new XT[]{XT.a1, XT.a1, XT.b2}));\n\t\tAssert.assertEquals(\"a1,a1,b2\", converter.toString(new String[]{\"a1\", \"a1\", \"b2\"}));\n\t\tAssert.assertEquals(\"true,true,false,true\", converter.toString(new boolean[]{true, true, false, true}));\n\t\tAssert.assertEquals(\"1,2,3,4\", converter.toString(new char[]{49, 50, 51, 52}));\n\t\tAssert.assertEquals(\"1,2,3\", converter.toString(new byte[]{1, 2, 3}));\n\t\tAssert.assertEquals(\"1@b.c/a,2@b.c/a,3@b.c/a\", converter.toString(\n\t\t\t\tnew JID[]{JID.jidInstanceNS(\"1@b.c/a\"), JID.jidInstanceNS(\"2@b.c/a\"), JID.jidInstanceNS(\"3@b.c/a\")}));\n\t\tAssert.assertEquals(System.getProperty(\"java.home\"),\n\t\t\t\t\t\t\tconverter.toString(new ConfigReader.PropertyVariable(\"java.home\", null)));\n\n\t\tConfigReader.CompositeVariable compositeVariable = new ConfigReader.CompositeVariable();\n\t\tcompositeVariable.add(\"Java: \");\n\t\tcompositeVariable.add('+', new ConfigReader.PropertyVariable(\"java.vendor\", null));\n\t\tcompositeVariable.add('+', \" \");\n\t\tcompositeVariable.add('+', new ConfigReader.PropertyVariable(\"java.version\", null));\n\t\tassertEquals(\"Java: \" + System.getProperty(\"java.vendor\") + \" \" + System.getProperty(\"java.version\"),\n\t\t\t\t\t converter.toString(compositeVariable));\n\n\t\tcompositeVariable = new ConfigReader.CompositeVariable();\n\t\tcompositeVariable.add(5);\n\t\tcompositeVariable.add('-', 2);\n\t\tcompositeVariable.add('*', 60);\n\t\tcompositeVariable.add('*', 1000);\n\t\tassertEquals(\"-119995\", converter.toString(compositeVariable));\n\n\t\tcompositeVariable = new ConfigReader.CompositeVariable();\n\t\tcompositeVariable.add(5);\n\t\tcompositeVariable.add('*', 60.0);\n\t\tcompositeVariable.add('*', 1000);\n\t\tassertEquals(\"300000.0\", converter.toString(compositeVariable));\n\t}\n\n\tstatic class ParcelableObject\n\t\t\timplements TypesConverter.Parcelable {\n\n\t\tprivate String description;\n\t\tprivate String ip;\n\t\tprivate JID jid;\n\t\tprivate long timestamp;\n\n\t\t@Override\n\t\tpublic String[] encodeToStrings() {\n\t\t\treturn new String[]{ip, jid.toString(), Long.toString(timestamp), description};\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object o) {\n\t\t\tif (this == o) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tParcelableObject that = (ParcelableObject) o;\n\n\t\t\tif (timestamp != that.timestamp) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!description.equals(that.description)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (!ip.equals(that.ip)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn jid.equals(that.jid);\n\t\t}\n\n\t\t@Override\n\t\tpublic void fillFromString(String[] encoded) {\n\t\t\tthis.ip = encoded[0];\n\t\t\tthis.jid = JID.jidInstanceNS(encoded[1]);\n\t\t\tthis.timestamp = Long.valueOf(encoded[2]);\n\t\t\tthis.description = encoded[3];\n\t\t}\n\n\t\tpublic String getDescription() {\n\t\t\treturn description;\n\t\t}\n\n\t\tpublic void setDescription(String description) {\n\t\t\tthis.description = description;\n\t\t}\n\n\t\tpublic String getIp() {\n\t\t\treturn ip;\n\t\t}\n\n\t\tpublic void setIp(String ip) {\n\t\t\tthis.ip = ip;\n\t\t}\n\n\t\tpublic JID getJid() {\n\t\t\treturn jid;\n\t\t}\n\n\t\tpublic void setJid(JID jid) {\n\t\t\tthis.jid = jid;\n\t\t}\n\n\t\tpublic long getTimestamp() {\n\t\t\treturn timestamp;\n\t\t}\n\n\t\tpublic void setTimestamp(long timestamp) {\n\t\t\tthis.timestamp = timestamp;\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\tint result = description.hashCode();\n\t\t\tresult = 31 * result + ip.hashCode();\n\t\t\tresult = 31 * result + jid.hashCode();\n\t\t\tresult = 31 * result + (int) (timestamp ^ (timestamp >>> 32));\n\t\t\treturn result;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/kernel/GenericBeanKernelTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel;\n\nimport org.junit.Test;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.core.Kernel;\n\nimport java.util.List;\n\n/**\n * Created by andrzej on 31.10.2016.\n */\npublic class GenericBeanKernelTest {\n\n\t@Test\n\tpublic void testGenerics() {\n\n\t\tKernel kernel = new Kernel();\n\t\tkernel.registerBean(\"converter1\").asClass(Converter1.class).exec();\n\t\tkernel.registerBean(\"converter2\").asClass(Converter2.class).exec();\n\n\t\tkernel.registerBean(\"consumer\").asClass(ConverterConsumer.class).exec();\n\n\t\tkernel.getInstance(ConverterConsumer.class);\n\n\t}\n\n\tpublic interface Converter<T> {\n\n\t\tString toString(T object);\n\t}\n\n\tpublic static class Converter1\n\t\t\timplements Converter<String> {\n\n\t\t@Override\n\t\tpublic String toString(String object) {\n\t\t\treturn object;\n\t\t}\n\t}\n\n\tpublic static class Converter2\n\t\t\timplements Converter<Long> {\n\n\t\t@Override\n\t\tpublic String toString(Long object) {\n\t\t\treturn String.valueOf(object);\n\t\t}\n\t}\n\n\tpublic static class ConverterConsumer {\n\n\t\t@Inject\n\t\tprivate List<Converter> converters;\n\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/KernelTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel;\n\nimport org.junit.AfterClass;\nimport org.junit.Assert;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport tigase.TestLogger;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.config.AbstractBeanConfigurator;\nimport tigase.kernel.beans.config.BeanConfigurator;\nimport tigase.kernel.core.BeanConfig;\nimport tigase.kernel.core.DependencyGrapher;\nimport tigase.kernel.core.Kernel;\nimport tigase.kernel.core.PlantUMLGrapher;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Type;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static org.junit.Assert.*;\n\npublic class KernelTest {\n\n\tprivate static final Logger log = TestLogger.getLogger(KernelTest.class);\n\n\t@BeforeClass\n\tpublic static void setUpBeforeClass() throws Exception {\n\t}\n\n\t@AfterClass\n\tpublic static void tearDownAfterClass() throws Exception {\n\t}\n\n\tpublic KernelTest() {\n\t}\n\n\t@Test\n\tpublic void test() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {\n\t\tKernel krnl = new Kernel();\n\n\t\tBean4 bean4_1_o = new Bean4();\n\t\tkrnl.registerBean(Bean1.class).exec();\n\t\tkrnl.registerBean(\"bean2\").asClass(Bean2.class).exec();\n\t\tkrnl.registerBean(\"bean3\").asClass(Bean3.class).exec();\n\t\tkrnl.registerBean(\"bean4\").asClass(Bean4.class).exec();\n\t\tkrnl.registerBean(\"bean4_1\").asInstance(bean4_1_o).exec();\n\t\tkrnl.registerBean(\"bean5\").asClass(Bean5.class).withFactory(Bean5Factory.class).exec();\n\n\t\tDependencyGrapher dg = new DependencyGrapher(krnl);\n\t\tlog.log(Level.FINE, dg.getDependencyGraph());\n\n\t\tBean1 b1 = krnl.getInstance(\"bean1\");\n\t\tBean2 b2 = krnl.getInstance(\"bean2\");\n\t\tBean3 b3 = krnl.getInstance(\"bean3\");\n\t\tBean4 b4 = krnl.getInstance(\"bean4\");\n\t\tBean4 b41 = krnl.getInstance(\"bean4_1\");\n\n\t\tassertNotNull(b1);\n\t\tassertNotNull(b2);\n\t\tassertNotNull(b3);\n\t\tassertNotNull(b4);\n\t\tassertNotNull(b41);\n\n\t\tassertEquals(b41, bean4_1_o);\n\n\t\tassertTrue(b1 instanceof Bean1);\n\t\tassertTrue(b2 instanceof Bean2);\n\t\tassertTrue(b3 instanceof Bean3);\n\t\tassertTrue(b4 instanceof Bean4);\n\t\tassertTrue(b41 instanceof Bean4);\n\n\t\tassertEquals(b2, b1.getBean2());\n\t\tassertEquals(b3, b1.getBean3());\n\t\tassertEquals(b3, b2.getBean3());\n\t\tassertEquals(b41, b2.getBean4());\n\t\tassertEquals(b4, b3.getBean4());\n\t\tassertEquals(b41, b3.getBean41());\n\n\t\tassertNotNull(b1.getTableOfSpecial());\n\t\tassertEquals(3, b1.getTableOfSpecial().length);\n\n\t\tassertEquals(3, b1.getCollectionOfSpecial().size());\n\t\tassertTrue(b1.getCollectionOfSpecial().contains(b3));\n\t\tassertTrue(b1.getCollectionOfSpecial().contains(b4));\n\t\tassertTrue(b1.getCollectionOfSpecial().contains(b41));\n\n\t\tkrnl.unregister(\"bean4_1\");\n\t\ttry {\n\t\t\tassertNull(krnl.getInstance(\"bean4_1\"));\n\t\t\tAssert.fail();\n\t\t} catch (KernelException e) {\n\t\t\tassertEquals(\"Unknown bean 'bean4_1'.\", e.getMessage());\n\t\t}\n\t\tassertNull(b3.getBean41());\n\t\tassertNull(b2.getBean4());\n\n\t\tassertNotNull(b1.getBean2());\n\t\tassertNotNull(b1.getBean3());\n\n\t\tassertEquals(2, b1.getTableOfSpecial().length);\n\n\t\tassertEquals(2, b1.getCollectionOfSpecial().size());\n\t\tassertTrue(b1.getCollectionOfSpecial().contains(b3));\n\t\tassertTrue(b1.getCollectionOfSpecial().contains(b4));\n\n\t\tkrnl.registerBean(\"bean6\").asClass(Bean6.class).exec();\n\n\t\tassertEquals(3, b1.getTableOfSpecial().length);\n\t\tassertEquals(3, b1.getCollectionOfSpecial().size());\n\n\t\tlog.log(Level.FINE, dg.getDependencyGraph());\n\n\t}\n\n\t@Test\n\tpublic void test2() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {\n\t\tKernel krnl = new Kernel();\n\t\tkrnl.registerBean(\"bean7\").asClass(Bean7.class).exec();\n\n\t\tkrnl.registerBean(\"beanX\").asClass(Bean4.class).exec();\n\t\tkrnl.registerBean(\"beanX\").asClass(Bean5.class).withFactory(Bean5Factory.class).exec();\n\n\t\tassertEquals(Bean5.class, krnl.getInstance(Bean7.class).getObj().getClass());\n\t\tassertEquals(Bean5.class, krnl.getInstance(\"beanX\").getClass());\n\n\t\tkrnl.registerBean(\"beanX\").asClass(Bean6.class).exec();\n\n\t\tassertEquals(Bean6.class, krnl.getInstance(\"beanX\").getClass());\n\t\tassertEquals(Bean6.class, krnl.getInstance(Bean7.class).getObj().getClass());\n\t}\n\n\t@Test\n\tpublic void test3() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {\n\t\tKernel krnl = new Kernel();\n\n\t\tkrnl.registerBean(Bean1.class).exec();\n\t\tkrnl.registerBean(\"bean4\").asClass(Bean4.class).exec();\n\t\t// krnl.registerBean(\"bean41\").asClass(Bean4.class).exec();\n\n\t\tBean1 b1 = krnl.getInstance(\"bean1\");\n\n\t\tassertNotNull(b1);\n\n\t\tassertNotNull(b1.getTableOfSpecial());\n\t\tassertEquals(1, b1.getTableOfSpecial().length);\n\n\t\tassertEquals(1, b1.getCollectionOfSpecial().size());\n\t}\n\n\t@Test\n\tpublic void test4() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {\n\t\tKernel krnl = new Kernel();\n\n\t\tkrnl.registerBean(Bean1.class).exec();\n\n\t\tBean1 b1 = krnl.getInstance(\"bean1\");\n\n\t\tassertNotNull(b1);\n\n\t\tassertNull(b1.getTableOfSpecial());\n\n\t\tassertEquals(0, b1.getCollectionOfSpecial().size());\n\t}\n\n\t@Test\n\tpublic void test5() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {\n\t\tKernel krnl = new Kernel();\n\n\t\tkrnl.registerBean(Bean8.class).exec();\n\n\t\ttry {\n\t\t\tkrnl.getInstance(\"bean8\");\n\t\t\tfail(\"This bean shouldn't be initialized because of empty dependency.\");\n\t\t} catch (KernelException e) {\n\t\t\tAssert.assertEquals(\"tigase.kernel.KernelException: Can't inject <null> to field tigase.kernel.Bean8.bean6\",\n\t\t\t\t\t\t\t\te.getMessage());\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testBeanConfiguration()\n\t\t\tthrows IllegalAccessException, IllegalArgumentException, InvocationTargetException {\n\t\tKernel krnl = new Kernel();\n\n\t\tkrnl.registerBean(CustomTypesConverter.class).exec();\n\t\tkrnl.registerBean(\"bean5\").asClass(Bean5.class).exec();\n\t\tkrnl.registerBean(DefaultTypesConverter.class).exec();\n\t\tkrnl.registerBean(BeanConfigurator.DEFAULT_CONFIGURATOR_NAME)\n\t\t\t\t.asClass(TestBeanConfigurationProvider.class)\n\t\t\t\t.exec();\n\t\tkrnl.registerBean(\"bean6\").asClass(Bean6.class).exec();\n\t\tkrnl.registerBean(Bean8.class).exec();\n\n\t\tBean5 b5 = krnl.getInstance(Bean5.class);\n\t\tBean6 b6 = krnl.getInstance(Bean6.class);\n\t\tBean8 b8 = krnl.getInstance(Bean8.class);\n\n\t\tassertEquals(\"yytestxx\", b6.getTestValue());\n\t\tassertEquals(9987l, b5.getValue().longValue());\n\t\tassertEquals(\"EXAMPLE\", b8.getSample());\n\n\t\t((TestBeanConfigurationProvider) krnl.getInstance(BeanConfigurator.DEFAULT_CONFIGURATOR_NAME)).restoreDefaults(\n\t\t\t\t\"bean5\");\n\t\tassertEquals(\"yytestxx\", b6.getTestValue());\n\t\tassertEquals(15l, b5.getValue().longValue());\n\n\t\t((TestBeanConfigurationProvider) krnl.getInstance(BeanConfigurator.DEFAULT_CONFIGURATOR_NAME)).restoreDefaults(\n\t\t\t\t\"bean6\");\n\t\tassertNull(b6.getTestValue());\n\t\tassertEquals(15l, b5.getValue().longValue());\n\t}\n\n\t@Test\n\tpublic void testCascadeKernels() throws Exception {\n\t\tKernel krnlParent = new Kernel(\"Parent\");\n\t\tkrnlParent.registerBean(\"bean1\").asClass(Bean1.class).exec();\n\t\tkrnlParent.registerBean(\"bean40\").asClass(Bean4.class).exportable().exec();\n\t\tkrnlParent.registerBean(\"bean41\").asClass(Bean4.class).exec();\n\t\tkrnlParent.registerBean(\"bean42\").asClass(Bean4.class).exportable().exec();\n\t\tkrnlParent.registerBean(\"bean5\").asClass(Bean5.class).exportable().exec();\n\t\tfinal Bean5 b5parent = new Bean5();\n\t\tfinal Bean5 b51parent = new Bean5();\n\t\tkrnlParent.registerBean(\"bean5\").asInstance(b5parent).exportable().exec();\n\t\tkrnlParent.registerBean(\"bean51\").asInstance(b51parent).exportable().exec();\n\n\t\tKernel krnlChild1 = new Kernel(\"Child01\");\n\t\tkrnlChild1.registerBean(\"bean40\").asClass(Bean4.class).exportable().exec();\n\n\t\tfinal Bean5 b5ch1 = new Bean5();\n\t\tkrnlChild1.registerBean(\"bean5\").asInstance(b5ch1).exportable().exec();\n\n\t\tKernel krnlChild2 = new Kernel(\"Child02\");\n\t\tkrnlChild2.registerBean(\"bean1\").asClass(Bean1.class).exec();\n\t\tkrnlChild2.registerBean(\"bean43\").asClass(Bean4.class).exec();\n\n\t\tkrnlParent.registerBean(krnlChild1.getName()).asInstance(krnlChild1).exec();\n\t\tkrnlParent.registerBean(krnlChild2.getName()).asInstance(krnlChild2).exec();\n\n\t\tBean1 bean1 = krnlChild2.getInstance(Bean1.class);\n\n\t\tassertEquals(3, bean1.getCollectionOfSpecial().size());\n\n\t\tassertTrue(\"Bean should be located in parent\", krnlChild2.isBeanClassRegistered(\"bean40\"));\n\t\tassertNotNull(\"Bean should be get from parent!\", krnlChild2.getInstance(\"bean40\"));\n\t\tassertEquals((Object) krnlParent.getInstance(\"bean40\"), krnlChild2.getInstance(\"bean40\"));\n\n\t\tassertTrue(bean1.getCollectionOfSpecial().contains(krnlParent.getInstance(\"bean40\")));\n\t\tassertTrue(bean1.getCollectionOfSpecial().contains(krnlParent.getInstance(\"bean42\")));\n\n\t\tassertEquals(3, bean1.getTableOfSpecial().length);\n\n\t\tDependencyGrapher dg = new DependencyGrapher(krnlParent);\n\t\tlog.log(Level.FINE, dg.getDependencyGraph());\n\n\t\tassertEquals(b5ch1, krnlChild1.getInstance(\"bean5\"));\n\t\tassertEquals(b51parent, krnlChild1.getInstance(\"bean51\"));\n\n\t\tassertNotNull(krnlChild1.getInstance(Bean5.class));\n\n\t\ttry {\n\t\t\tkrnlChild1.getInstance(Bean1.class);\n\t\t\tAssert.fail();\n\t\t} catch (KernelException e) {\n\t\t\tassertEquals(\"Can''t find bean implementing class tigase.kernel.Bean1\", e.getMessage());\n\t\t}\n\t\ttry {\n\t\t\tkrnlChild1.getInstance(Bean3.class);\n\t\t\tAssert.fail();\n\t\t} catch (KernelException e) {\n\t\t\tassertEquals(\"Can''t find bean implementing class tigase.kernel.Bean3\", e.getMessage());\n\t\t}\n\n\t\ttry {\n\t\t\tkrnlChild1.getInstance(\"zzz\");\n\t\t\tAssert.fail();\n\t\t} catch (KernelException e) {\n\t\t\tassertEquals(\"Unknown bean 'zzz'.\", e.getMessage());\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testGc() throws Exception {\n\t\tKernel krnl = new Kernel();\n\t\tkrnl.registerBean(DefaultTypesConverter.class).exec();\n\n\t\tkrnl.registerBean(Bean10.class).setPinned(false).exec();\n\t\tkrnl.registerBean(\"a_1\").asClass(Bean6.class).setPinned(false).exec();\n\n\t\tkrnl.registerBean(Bean1.class).setPinned(false).exec();\n\t\tkrnl.registerBean(\"bean2\").asClass(Bean2.class).setPinned(false).setActive(true).exec();\n\t\tkrnl.registerBean(\"bean3\").asClass(Bean3.class).setPinned(false).exec();\n\t\tkrnl.registerBean(\"bean4\").asClass(Bean4.class).setPinned(false).exec();\n\t\tkrnl.registerBean(\"bean5\").asClass(Bean5.class).setPinned(false).withFactory(Bean5Factory.class).exec();\n\n\t\tPlantUMLGrapher g = new PlantUMLGrapher(krnl);\n\t\tlog.log(Level.FINE, g.getDependencyGraph());\n\n\t\tAssert.assertEquals(1, krnl.getDependencyManager()\n\t\t\t\t.getBeanConfigs()\n\t\t\t\t.stream()\n\t\t\t\t.filter(bc -> bc.getState() == BeanConfig.State.initialized)\n\t\t\t\t.count());\n\n\t\tkrnl.getInstance(Bean1.class);\n\t\tkrnl.getInstance(Bean10.class);\n\n\t\tAssert.assertEquals(7, krnl.getDependencyManager()\n\t\t\t\t.getBeanConfigs()\n\t\t\t\t.stream()\n\t\t\t\t.filter(bc -> bc.getState() == BeanConfig.State.initialized)\n\t\t\t\t.count());\n\n\t\tkrnl.gc();\n\n\t\tAssert.assertEquals(1, krnl.getDependencyManager()\n\t\t\t\t.getBeanConfigs()\n\t\t\t\t.stream()\n\t\t\t\t.filter(bc -> bc.getState() == BeanConfig.State.initialized)\n\t\t\t\t.count());\n\n\t\tkrnl.getInstance(Bean5.class);\n\n\t\tAssert.assertEquals(8, krnl.getDependencyManager()\n\t\t\t\t.getBeanConfigs()\n\t\t\t\t.stream()\n\t\t\t\t.filter(bc -> bc.getState() == BeanConfig.State.initialized)\n\t\t\t\t.count());\n\n\t\tkrnl.gc();\n\n\t\tAssert.assertEquals(1, krnl.getDependencyManager()\n\t\t\t\t.getBeanConfigs()\n\t\t\t\t.stream()\n\t\t\t\t.filter(bc -> bc.getState() == BeanConfig.State.initialized)\n\t\t\t\t.count());\n\n\t\tkrnl.getDependencyManager().getBeanConfig(\"bean10\").setPinned(true);\n\n\t\tkrnl.getInstance(Bean1.class);\n\t\tkrnl.getInstance(Bean10.class);\n\n\t\tAssert.assertEquals(7, krnl.getDependencyManager()\n\t\t\t\t.getBeanConfigs()\n\t\t\t\t.stream()\n\t\t\t\t.filter(bc -> bc.getState() == BeanConfig.State.initialized)\n\t\t\t\t.count());\n\n\t\tkrnl.gc();\n\n\t\tAssert.assertEquals(5, krnl.getDependencyManager()\n\t\t\t\t.getBeanConfigs()\n\t\t\t\t.stream()\n\t\t\t\t.filter(bc -> bc.getState() == BeanConfig.State.initialized)\n\t\t\t\t.count());\n\t}\n\n\t@Test\n\tpublic void testInactiveBean() throws Exception {\n\t\tKernel krnl = new Kernel();\n\t\tkrnl.registerBean(DefaultTypesConverter.class).exec();\n\n\t\tkrnl.registerBean(Bean10.class).exec();\n\t\tkrnl.registerBean(\"a_1\").asClass(Bean6.class).exec();\n\t\tkrnl.registerBean(\"a_2\").asClass(Bean6.class).exec();\n\t\tkrnl.registerBean(\"a_3\").asClass(Bean6.class).setActive(false).exec();\n\n\t\tkrnl.registerBean(Bean1.class).exec();\n\t\tkrnl.registerBean(\"bean2\").asClass(Bean2.class).setActive(false).exec();\n\t\tkrnl.registerBean(\"bean3\").asClass(Bean3.class).exec();\n\t\tkrnl.registerBean(\"bean4\").asClass(Bean4.class).exec();\n\t\tkrnl.registerBean(\"bean5\").asClass(Bean5.class).withFactory(Bean5Factory.class).exec();\n\n\t\tBean1 b1 = krnl.getInstance(Bean1.class);\n\t\tBean10 b10 = krnl.getInstance(\"bean10\");\n\n\t\tassertNull(b1.getBean2());\n\t\tassertEquals(4, b10.getTableOfSpecial().length);\n\n\t\tkrnl.setBeanActive(\"a_3\", true);\n\t\tkrnl.setBeanActive(\"bean2\", true);\n\n\t\tassertNotNull(b1.getBean2());\n\t\tassertEquals(5, b10.getTableOfSpecial().length);\n\n\t\tkrnl.setBeanActive(\"bean2\", false);\n\t\tkrnl.setBeanActive(\"a_1\", false);\n\n\t\tassertNull(b1.getBean2());\n\t\tassertEquals(4, b10.getTableOfSpecial().length);\n\n\t\tkrnl.setBeanActive(\"a_1\", false);\n\t\tassertNull(b1.getBean2());\n\t\tassertEquals(4, b10.getTableOfSpecial().length);\n\t}\n\n\t@Bean(name = \"customTypesConverter\", active = true)\n\tpublic static class CustomTypesConverter\n\t\t\textends DefaultTypesConverter {\n\n\t\t@Override\n\t\tpublic <T> T convert(Object value, Class<T> expectedType, Type type) {\n\t\t\treturn super.convert(value.toString().substring(3), expectedType, type);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString(Object value) {\n\t\t\treturn \"___\" + super.toString(value);\n\t\t}\n\t}\n\n\tpublic static class TestBeanConfigurationProvider\n\t\t\textends AbstractBeanConfigurator {\n\n\t\t@Override\n\t\tpublic Map<String, Object> getProperties() {\n\t\t\treturn new HashMap<>();\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<String, Object> getConfiguration(BeanConfig beanConfig) {\n\t\t\tif (beanConfig.getBeanName().equals(\"bean5\")) {\n\t\t\t\tHashMap<String, Object> result = new HashMap<String, Object>();\n\t\t\t\tresult.put(\"value\", Long.valueOf(9987));\n\t\t\t\treturn result;\n\t\t\t} else if (beanConfig.getBeanName().equals(\"bean6\")) {\n\t\t\t\tHashMap<String, Object> result = new HashMap<String, Object>();\n\t\t\t\tresult.put(\"testValue\", \"yytestxx\");\n\t\t\t\treturn result;\n\t\t\t} else if (beanConfig.getBeanName().equals(\"bean8\")) {\n\t\t\t\tHashMap<String, Object> result = new HashMap<String, Object>();\n\t\t\t\tresult.put(\"sample\", \"___EXAMPLE\");\n\t\t\t\treturn result;\n\t\t\t} else {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tprotected boolean hasDirectConfiguration(BeanConfig bc) {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/RegistrarBeanKernelTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport tigase.TestLogger;\nimport tigase.component.DSLBeanConfigurator;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.RegistrarBean;\nimport tigase.kernel.core.DependencyGrapher;\nimport tigase.kernel.core.Kernel;\nimport tigase.kernel.core.RegistrarKernel;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Created by andrzej on 05.03.2016.\n */\npublic class RegistrarBeanKernelTest {\n\n\tprivate static final Logger log = TestLogger.getLogger(KernelTest.class);\n\n\tpublic RegistrarBeanKernelTest() {\n\t}\n\n\t@Test\n\tpublic void test01() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {\n\n\t\tfinal RegistrarKernel krnl = new RegistrarKernel();\n\t\tkrnl.setName(\"root\");\n\t\tkrnl.registerBean(DefaultTypesConverter.class).exec();\n\t\tkrnl.registerBean(DSLBeanConfigurator.class).exec();\n\t\tkrnl.registerBean(RegistrarBeanImpl.class).exec();\n\n\t\t//krnl.startSubKernels();\n\n\t\tDependencyGrapher dg = new DependencyGrapher(krnl);\n\t\tlog.log(Level.FINE, dg.getDependencyGraph());\n\n\t\tAssert.assertNotNull(krnl.getInstance(\"RegistrarBean\"));\n\n\t\tKernel rb1k = krnl.getInstance(\"RegistrarBean#KERNEL\");\n\t\tAssert.assertNotEquals(null, rb1k.getInstance(Bean1.class));\n\t\tAssert.assertEquals(krnl.getInstance(\"RegistrarBean\"), rb1k.getInstance(RegistrarBeanImpl.class));\n\t\tAssert.assertNotNull(rb1k.getInstance(\"service\"));\n\n\t\tkrnl.setBeanActive(\"RegistrarBean\", false);\n\t\tkrnl.gc();\n\n\t\tdg = new DependencyGrapher(krnl);\n\t\tlog.log(Level.FINE, dg.getDependencyGraph());\n\n\t\tAssert.assertTrue(krnl.isBeanClassRegistered(\"RegistrarBean\"));\n\t\tboolean exception = false;\n\t\ttry {\n\t\t\tAssert.assertNull(krnl.getInstance(\"RegistrarBean#KERNEL\"));\n\t\t} catch (KernelException ex) {\n\t\t\t// unknow bean - this is what we expect\n\t\t\texception = true;\n\t\t}\n\t\tAssert.assertTrue(exception);\n\t}\n\n\t@Test\n\tpublic void test02() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {\n\n\t\tfinal RegistrarKernel krnl = new RegistrarKernel();\n\t\tkrnl.setName(\"root\");\n\t\tkrnl.registerBean(DefaultTypesConverter.class).exec();\n\t\tkrnl.registerBean(DSLBeanConfigurator.class).exec();\n\t\tkrnl.registerBean(DummyBean.class).exec();\n\t\tkrnl.registerBean(RegistrarBeanImplWithLink.class).exec();\n\n\t\t//krnl.startSubKernels();\n\n\t\tDependencyGrapher dg = new DependencyGrapher(krnl);\n\t\tlog.log(Level.FINE, dg.getDependencyGraph());\n\n\t\tAssert.assertNotNull(krnl.getInstance(\"RegistrarBean\"));\n\n\t\tKernel rb1k = krnl.getInstance(\"RegistrarBean#KERNEL\");\n\t\tAssert.assertNotEquals(null, rb1k.getInstance(Bean1.class));\n\t\tAssert.assertEquals(krnl.getInstance(\"RegistrarBean\"), rb1k.getInstance(RegistrarBeanImplWithLink.class));\n\t\tAssert.assertNotNull(rb1k.getInstance(\"service\"));\n\t\tAssert.assertNotNull(rb1k.getInstance(\"dummy\"));\n\t\tAssert.assertNotNull(rb1k.getInstance(\"DummyBeanUser\"));\n\t\tAssert.assertNotNull(((DummyBeanUser) rb1k.getInstance(\"DummyBeanUser\")).dummyBean);\n\n\t\tkrnl.unregister(\"DummyBean\");\n\n\t\ttry {\n\t\t\t// maybe it should still be there but in unresolved state?\n\t\t\tAssert.assertNull(rb1k.getDependencyManager().getBeanConfig(\"dummy\"));\n\t\t\trb1k.getInstance(\"dummy\");\n\t\t\tAssert.assertTrue(false);\n\t\t} catch (KernelException ex) {\n\t\t\t// wrong cause of KernelException (caused by NPE), should be thrown due to fact\n\t\t\t// that \"dummy\" is DelegatedBeanConfig pointing to removed BeanConfig!\n\t\t\tAssert.assertTrue(ex.getMessage().contains(\"Unknown bean\"));\n\t\t}\n\n\t\tAssert.assertNull(((DummyBeanUser) rb1k.getInstance(\"DummyBeanUser\")).dummyBean);\n\n\t\tdg = new DependencyGrapher(krnl);\n\t\tlog.log(Level.FINE, dg.getDependencyGraph());\n\t}\n\n\t@Test\n\tpublic void test03() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {\n\n\t\tfinal RegistrarKernel krnl = new RegistrarKernel();\n\t\tkrnl.setName(\"root\");\n\t\tkrnl.registerBean(DefaultTypesConverter.class).exec();\n\t\tkrnl.registerBean(DSLBeanConfigurator.class).exec();\n\t\tkrnl.registerBean(DummyBean2.class).exec();\n\t\tkrnl.registerBean(RegistrarBeanImplWithLink2.class).exec();\n\n\t\t//krnl.startSubKernels();\n\n\t\tDependencyGrapher dg = new DependencyGrapher(krnl);\n\t\tlog.log(Level.FINE, dg.getDependencyGraph());\n\n\t\tAssert.assertNotNull(krnl.getInstance(\"RegistrarBean\"));\n\n\t\tKernel rb1k = krnl.getInstance(\"RegistrarBean#KERNEL\");\n\t\tAssert.assertNotEquals(null, rb1k.getInstance(Bean1.class));\n\t\tAssert.assertEquals(krnl.getInstance(\"RegistrarBean\"), rb1k.getInstance(RegistrarBeanImplWithLink2.class));\n\t\tAssert.assertNotNull(rb1k.getInstance(\"service\"));\n\t\tAssert.assertNotNull(rb1k.getInstance(DummyBean2.class));\n\t\tAssert.assertNotNull(rb1k.getInstance(\"DummyBean2User\"));\n\t\tAssert.assertNotNull(((DummyBean2User) rb1k.getInstance(\"DummyBean2User\")).dummyBean);\n\n\t\tkrnl.unregister(\"DummyBean2\");\n\n\t\ttry {\n\t\t\trb1k.getInstance(\"DummyBean2\");\n\t\t\tAssert.assertTrue(false);\n\t\t} catch (KernelException ex) {\n\t\t\tAssert.assertTrue(ex.getMessage().contains(\"Unknown bean\"));\n\t\t}\n\n\t\tAssert.assertNull(((DummyBean2User) rb1k.getInstance(\"DummyBean2User\")).dummyBean);\n\n\t\tdg = new DependencyGrapher(krnl);\n\t\tlog.log(Level.FINE, dg.getDependencyGraph());\n\t}\n\n\t@Test\n\tpublic void test04() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {\n\n\t\tfinal RegistrarKernel krnl = new RegistrarKernel();\n\t\tkrnl.setName(\"root\");\n\t\tkrnl.registerBean(DefaultTypesConverter.class).exec();\n\t\tkrnl.registerBean(DSLBeanConfigurator.class).exec();\n\t\tkrnl.registerBean(DummyBean3.class).exec();\n\t\tkrnl.registerBean(RegistrarBeanImplWithLink3.class).exec();\n\n\t\t//krnl.startSubKernels();\n\n\t\tDependencyGrapher dg = new DependencyGrapher(krnl);\n\t\tlog.log(Level.FINE, dg.getDependencyGraph());\n\n\t\tAssert.assertNotNull(krnl.getInstance(\"RegistrarBean\"));\n\n\t\tKernel rb1k = krnl.getInstance(\"RegistrarBean#KERNEL\");\n\t\tAssert.assertNotEquals(null, rb1k.getInstance(Bean1.class));\n\t\tAssert.assertEquals(krnl.getInstance(\"RegistrarBean\"), rb1k.getInstance(RegistrarBeanImplWithLink3.class));\n\t\tAssert.assertNotNull(rb1k.getInstance(\"service\"));\n\t\tAssert.assertNotNull(rb1k.getInstance(\"dummy\"));\n\t\tAssert.assertNotNull(rb1k.getInstance(\"DummyBean3User\"));\n\t\tAssert.assertNotNull(rb1k.getInstance(\"DummyBean34User\"));\n\t\tAssert.assertNotNull(((DummyBean3User) rb1k.getInstance(\"DummyBean3User\")).dummyBean);\n\t\tAssert.assertNotNull(((DummyBean34User) rb1k.getInstance(\"DummyBean34User\")).dummyBean3User);\n\n\t\tkrnl.unregister(\"DummyBean3\");\n\n\t\ttry {\n\t\t\t// maybe it should still be there but in unresolved state?\n\t\t\tAssert.assertNull(rb1k.getDependencyManager().getBeanConfig(\"dummy\"));\n\t\t\trb1k.getInstance(\"dummy\");\n\t\t\tAssert.assertTrue(false);\n\t\t} catch (KernelException ex) {\n\t\t\t// wrong cause of KernelException (caused by NPE), should be thrown due to fact\n\t\t\t// that \"dummy\" is DelegatedBeanConfig pointing to removed BeanConfig!\n\t\t\tAssert.assertTrue(ex.getMessage().contains(\"Unknown bean\"));\n\t\t}\n\n\t\ttry {\n\t\t\trb1k.getInstance(\"DummyBean3User\");\n\t\t\tAssert.assertTrue(false);\n\t\t} catch (KernelException ex) {\n\t\t\tAssert.assertTrue(true);\n\t\t}\n\n\t\tAssert.assertNull(((DummyBean34User) rb1k.getInstance(\"DummyBean34User\")).dummyBean3User);\n\n\t\tkrnl.registerBean(DummyBean3.class).exec();\n\t\tkrnl.ln(\"DummyBean3\", rb1k, \"dummy\");\n\n\t\tAssert.assertNotNull(((DummyBean3User) rb1k.getInstance(\"DummyBean3User\")).dummyBean);\n\t\tAssert.assertNotNull(((DummyBean34User) rb1k.getInstance(\"DummyBean34User\")).dummyBean3User);\n\n\t\tdg = new DependencyGrapher(krnl);\n\t\tlog.log(Level.FINE, dg.getDependencyGraph());\n\t}\n\n\t@Test\n\tpublic void test05() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {\n\n\t\tfinal RegistrarKernel krnl = new RegistrarKernel();\n\t\tkrnl.registerBean(DefaultTypesConverter.class).exportable().exec();\n\t\tkrnl.setName(\"root\");\n\t\tkrnl.registerBean(DSLBeanConfigurator.class).exec();\n\t\tkrnl.registerBean(DummyBean4.class).exec();\n\t\tkrnl.registerBean(RegistrarBeanImplWithLink4.class).exec();\n\n\t\t//krnl.startSubKernels();\n\n\t\tDependencyGrapher dg = new DependencyGrapher(krnl);\n\t\tlog.log(Level.FINE, dg.getDependencyGraph());\n\n\t\tAssert.assertNotNull(krnl.getInstance(\"RegistrarBean\"));\n\n\t\tKernel rb1k = krnl.getInstance(\"RegistrarBean#KERNEL\");\n\t\tAssert.assertNotEquals(null, rb1k.getInstance(Bean1.class));\n\t\tAssert.assertEquals(krnl.getInstance(\"RegistrarBean\"), rb1k.getInstance(RegistrarBeanImplWithLink4.class));\n\t\tAssert.assertNotNull(rb1k.getInstance(\"service\"));\n\t\tAssert.assertNotNull(rb1k.getInstance(DummyBean4.class));\n\t\tAssert.assertNotNull(rb1k.getInstance(\"DummyBean4User\"));\n\t\tAssert.assertNotNull(rb1k.getInstance(\"DummyBean34User\"));\n\t\tAssert.assertNotNull(((DummyBean4User) rb1k.getInstance(\"DummyBean4User\")).dummyBean);\n\t\tAssert.assertNotNull(((DummyBean34User) rb1k.getInstance(\"DummyBean34User\")).dummyBean4User);\n\n\t\tkrnl.unregister(\"DummyBean4\");\n\n\t\ttry {\n\t\t\trb1k.getInstance(\"DummyBean4\");\n\t\t\tAssert.assertTrue(false);\n\t\t} catch (KernelException ex) {\n\t\t\tAssert.assertTrue(ex.getMessage().contains(\"Unknown bean\"));\n\t\t}\n\n\t\ttry {\n\t\t\trb1k.getInstance(\"DummyBean4User\");\n\t\t\tAssert.assertTrue(false);\n\t\t} catch (KernelException ex) {\n\t\t\tAssert.assertTrue(true);\n\t\t}\n\n\t\tAssert.assertNull(((DummyBean34User) rb1k.getInstance(\"DummyBean34User\")).dummyBean4User);\n\n\t\tkrnl.registerBean(DummyBean4.class).exec();\n\n\t\tAssert.assertNotNull(((DummyBean34User) rb1k.getInstance(\"DummyBean34User\")).dummyBean4User);\n\t\tAssert.assertNotNull(((DummyBean4User) rb1k.getInstance(\"DummyBean4User\")).dummyBean);\n\n\t\tdg = new DependencyGrapher(krnl);\n\t\tlog.log(Level.FINE, dg.getDependencyGraph());\n\t}\n\n\t@Bean(name = \"DummyBean\", active = true)\n\tpublic static class DummyBean {\n\n\t\tpublic DummyBean() {\n\t\t}\n\n\t}\n\n\t@Bean(name = \"DummyBean2\", active = true, exportable = true)\n\tpublic static class DummyBean2 {\n\n\t\tpublic DummyBean2() {\n\t\t}\n\n\t}\n\n\t@Bean(name = \"DummyBean2User\", active = true)\n\tpublic static class DummyBean2User {\n\n\t\t@Inject(nullAllowed = true)\n\t\tpublic DummyBean2 dummyBean;\n\n\t\tpublic DummyBean2User() {\n\t\t}\n\n\t}\n\n\t@Bean(name = \"DummyBean3\", active = true)\n\tpublic static class DummyBean3 {\n\n\t\tpublic DummyBean3() {\n\t\t}\n\n\t}\n\n\t@Bean(name = \"DummyBean34User\", active = true)\n\tpublic static class DummyBean34User {\n\n\t\t@Inject(nullAllowed = true)\n\t\tpublic DummyBean3User dummyBean3User;\n\n\t\t@Inject(nullAllowed = true)\n\t\tpublic DummyBean4User dummyBean4User;\n\n\t}\n\n\t@Bean(name = \"DummyBean3User\", active = true)\n\tpublic static class DummyBean3User {\n\n\t\t@Inject\n\t\tpublic DummyBean3 dummyBean;\n\n\t\tpublic DummyBean3User() {\n\t\t}\n\n\t}\n\n\t@Bean(name = \"DummyBean4\", active = true, exportable = true)\n\tpublic static class DummyBean4 {\n\n\t\tpublic DummyBean4() {\n\t\t}\n\n\t}\n\n\t@Bean(name = \"DummyBean4User\", active = true)\n\tpublic static class DummyBean4User {\n\n\t\t@Inject\n\t\tpublic DummyBean4 dummyBean;\n\n\t\tpublic DummyBean4User() {\n\t\t}\n\n\t}\n\n\t@Bean(name = \"DummyBeanUser\", active = true)\n\tpublic static class DummyBeanUser {\n\n\t\t@Inject(nullAllowed = true)\n\t\tpublic DummyBean dummyBean;\n\n\t\tpublic DummyBeanUser() {\n\t\t}\n\n\t}\n\n\t@Bean(name = \"RegistrarBean\", active = true)\n\tpublic static class RegistrarBeanImpl\n\t\t\timplements RegistrarBean {\n\n\t\tpublic RegistrarBeanImpl() {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void register(Kernel kernel) {\n\t\t\tkernel.registerBean(Bean1.class).exec();\n\t\t}\n\n\t\t@Override\n\t\tpublic void unregister(Kernel kernel) {\n\t\t\tkernel.unregister(\"bean1\");\n\t\t}\n\t}\n\n\t@Bean(name = \"RegistrarBean\", active = true)\n\tpublic static class RegistrarBeanImplWithLink\n\t\t\timplements RegistrarBean {\n\n\t\tpublic RegistrarBeanImplWithLink() {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void register(Kernel kernel) {\n\t\t\tkernel.getParent().ln(\"DummyBean\", kernel, \"dummy\");\n\t\t\tkernel.registerBean(DummyBeanUser.class).exec();\n\t\t\tkernel.registerBean(Bean1.class).exec();\n\t\t}\n\n\t\t@Override\n\t\tpublic void unregister(Kernel kernel) {\n\t\t\tkernel.unregister(\"bean1\");\n\t\t}\n\t}\n\n\t@Bean(name = \"RegistrarBean\", active = true)\n\tpublic static class RegistrarBeanImplWithLink2\n\t\t\timplements RegistrarBean {\n\n\t\tpublic RegistrarBeanImplWithLink2() {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void register(Kernel kernel) {\n\t\t\tkernel.registerBean(DummyBean2User.class).exec();\n\t\t\tkernel.registerBean(Bean1.class).exec();\n\t\t}\n\n\t\t@Override\n\t\tpublic void unregister(Kernel kernel) {\n\t\t\tkernel.unregister(\"bean1\");\n\t\t}\n\t}\n\n\t@Bean(name = \"RegistrarBean\", active = true)\n\tpublic static class RegistrarBeanImplWithLink3\n\t\t\timplements RegistrarBean {\n\n\t\tpublic RegistrarBeanImplWithLink3() {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void register(Kernel kernel) {\n\t\t\tkernel.getParent().ln(\"DummyBean3\", kernel, \"dummy\");\n\t\t\tkernel.registerBean(DummyBean3User.class).exec();\n\t\t\tkernel.registerBean(DummyBean34User.class).exec();\n\t\t\tkernel.registerBean(Bean1.class).exec();\n\t\t}\n\n\t\t@Override\n\t\tpublic void unregister(Kernel kernel) {\n\t\t\tkernel.unregister(\"bean1\");\n\t\t}\n\t}\n\n\t@Bean(name = \"RegistrarBean\", active = true)\n\tpublic static class RegistrarBeanImplWithLink4\n\t\t\timplements RegistrarBean {\n\n\t\tpublic RegistrarBeanImplWithLink4() {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void register(Kernel kernel) {\n\t\t\tkernel.registerBean(DummyBean4User.class).exec();\n\t\t\tkernel.registerBean(DummyBean34User.class).exec();\n\t\t\tkernel.registerBean(Bean1.class).exec();\n\t\t}\n\n\t\t@Override\n\t\tpublic void unregister(Kernel kernel) {\n\t\t\tkernel.unregister(\"bean1\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/RegistratBeanCyclicTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport tigase.TestLogger;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.beans.RegistrarBean;\nimport tigase.kernel.core.Kernel;\nimport tigase.kernel.core.PlantUMLGrapher;\nimport tigase.kernel.core.RegistrarKernel;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class RegistratBeanCyclicTest {\n\n\tprivate static final Logger log = TestLogger.getLogger(RegistratBeanCyclicTest.class);\n\n\t@Test\n\tpublic void test01() {\n\t\tKernel k = new RegistrarKernel();\n\t\tk.setName(\"root\");\n\t\tk.registerBean(A.class).exec();\n\n\t\tPlantUMLGrapher gr = new PlantUMLGrapher(k);\n\n\t\tA a = k.getInstance(A.class);\n\t\tB b = a.b;\n\t\tC c = a.b.c;\n\t\tAssert.assertSame(a, b.a);\n\t\tAssert.assertSame(a, c.a);\n\t\tAssert.assertSame(b, ((Kernel) k.getInstance(\"a#KERNEL\")).getInstance(\"b\"));\n\t\tAssert.assertSame(c, ((Kernel) ((Kernel) k.getInstance(\"a#KERNEL\")).getInstance(\"b#KERNEL\")).getInstance(\"c\"));\n\n\t\tlog.log(Level.FINE, gr.getDependencyGraph());\n\t}\n\n\t@Bean(name = \"a\", active = true)\n\tpublic static class A\n\t\t\timplements RegistrarBean {\n\n\t\t@Inject\n\t\tB b;\n\n\t\tpublic void register(Kernel kernel) {\n\t\t\tkernel.registerBean(B.class).exec();\n\t\t}\n\n\t\t@Override\n\t\tpublic void unregister(Kernel kernel) {\n\t\t}\n\t}\n\n\t@Bean(name = \"b\", active = true)\n\tpublic static class B\n\t\t\timplements RegistrarBean {\n\n\t\t@Inject\n\t\tA a;\n\n\t\t@Inject\n\t\tC c;\n\n\t\tpublic void register(Kernel kernel) {\n\t\t\tkernel.registerBean(C.class).exec();\n\t\t\tkernel.getParent().ln(\"service\", kernel, \"a\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void unregister(Kernel kernel) {\n\t\t}\n\t}\n\n\t@Bean(name = \"c\", active = true)\n\tpublic static class C {\n\n\t\t@Inject\n\t\tA a;\n\t}\n}\n\n\n"
  },
  {
    "path": "src/test/java/tigase/kernel/Special.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel;\n\npublic interface Special {\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/modular/ApplicationTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.modular;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport tigase.TestLogger;\nimport tigase.component.DSLBeanConfigurator;\nimport tigase.kernel.DefaultTypesConverter;\nimport tigase.kernel.core.RegistrarKernel;\nimport tigase.kernel.modular.c1.Component1Registrar;\nimport tigase.kernel.modular.c2.Component2Registrar;\n\nimport java.util.Collection;\nimport java.util.logging.Logger;\n\npublic class ApplicationTest {\n\n\tprivate static final Logger log = TestLogger.getLogger(ApplicationTest.class);\n\n\tpublic ApplicationTest() {\n\t}\n\n\t@Test\n\tpublic void testBootstrapModules() {\n\t\tfinal RegistrarKernel krnl = new RegistrarKernel();\n\t\tkrnl.setName(\"root\");\n\t\tkrnl.registerBean(DefaultTypesConverter.class).exportable().exec();\n\t\tkrnl.registerBean(DSLBeanConfigurator.class).exportable().exec();\n\n\t\t// registering our main class of application\n\t\tkrnl.registerBean(ComponentsManager.class).exec();\n\n\t\t// registering components\n\t\tkrnl.registerBean(Component1Registrar.class).exec();\n\t\tkrnl.registerBean(Component2Registrar.class).exec();\n\n\t\t// starting application\n\t\tfinal ComponentsManager componentsManager = krnl.getInstance(ComponentsManager.class);\n\n\t\t// Two components should be injected to ComponentsManager.\n\t\tAssert.assertNotNull(componentsManager.getComponents());\n\t\tAssert.assertEquals(2, componentsManager.getComponents().length);\n\n\t\tCollection<String> response = componentsManager.process(\"call1\");\n\t\tAssert.assertTrue(response.contains(\"response:Component1(h:call1)\"));\n\t\tAssert.assertTrue(response.contains(\"response:Component2(call1)\"));\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/modular/Component.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.modular;\n\npublic interface Component {\n\n\tString execute(String request);\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/modular/ComponentsManager.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.modular;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\n\n@Bean(name = \"componentsManager\", active = true)\npublic class ComponentsManager {\n\n\t@Inject(nullAllowed = true)\n\tprivate Component[] components;\n\n\tpublic Component[] getComponents() {\n\t\treturn components;\n\t}\n\n\tCollection<String> process(final String request) {\n\t\tArrayList<String> response = new ArrayList<>();\n\n\t\tfor (Component component : components) {\n\t\t\tresponse.add(component.execute(request));\n\t\t}\n\n\t\treturn response;\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/modular/c1/Component1.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.modular.c1;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.modular.Component;\n\n@Bean(name = \"Component1\", active = true)\npublic class Component1\n\t\timplements Component {\n\n\t@Inject\n\tprivate Helper helper;\n\n\t@Override\n\tpublic String execute(String request) {\n\t\treturn \"response:Component1(\" + helper.doSomething(request) + \")\";\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/modular/c1/Component1Registrar.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.modular.c1;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.RegistrarBean;\nimport tigase.kernel.core.Kernel;\n\n@Bean(name = \"component1Registrar\", active = true)\npublic class Component1Registrar\n\t\textends Component1\n\t\timplements RegistrarBean {\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\t\tkernel.registerBean(Helper.class).exec();\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\t\tkernel.unregister(\"Helper\");\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/modular/c1/Helper.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.modular.c1;\n\nimport tigase.kernel.beans.Bean;\n\n@Bean(name = \"Helper\", active = true)\npublic class Helper {\n\n\tpublic String doSomething(String request) {\n\t\treturn \"h:\" + request;\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/modular/c2/Component2.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.modular.c2;\n\nimport tigase.component.DSLBeanConfigurator;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.Inject;\nimport tigase.kernel.modular.Component;\n\n@Bean(name = \"Component2\", active = true)\npublic class Component2\n\t\timplements Component {\n\n\t@Inject\n\tprivate DSLBeanConfigurator dslBeanConfigurator;\n\n\t@Override\n\tpublic String execute(String request) {\n\t\treturn \"response:Component2(\" + request + \")\";\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/kernel/modular/c2/Component2Registrar.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.kernel.modular.c2;\n\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.beans.RegistrarBean;\nimport tigase.kernel.core.Kernel;\n\n@Bean(name = \"component2Registrar\", active = true)\npublic class Component2Registrar\n\t\textends Component2\n\t\timplements RegistrarBean {\n\n\t@Override\n\tpublic void register(Kernel kernel) {\n\t}\n\n\t@Override\n\tpublic void unregister(Kernel kernel) {\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/map/ClusterMapFactoryTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.map;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.impl.EventBusImplementation;\n\nimport java.util.Map;\n\npublic class ClusterMapFactoryTest {\n\n\t@Test\n\tpublic void testCreateMap() throws Exception {\n\t\tfinal Object mutex = new Object();\n\n\t\tfinal ClusterMapFactory factory = new ClusterMapFactory();\n\t\tfactory.setEventBus(new EventBusImplementation());\n\t\tfinal EventBus eventBus = factory.getEventBus();\n\n\t\tfinal Object[] createdEvent = new Object[]{null};\n\t\teventBus.addListener(ClusterMapFactory.NewMapCreatedEvent.class, event -> {\n\t\t\tAssert.assertNull(createdEvent[0]);\n\t\t\tcreatedEvent[0] = event;\n\t\t\tsynchronized (mutex) {\n\t\t\t\tmutex.notifyAll();\n\t\t\t}\n\t\t});\n\n\t\tMap<String, String> map = factory.createMap(\"test\", String.class, String.class, \"1\", \"2\", \"3\");\n\n\t\tsynchronized (mutex) {\n\t\t\tmutex.wait(10_000);\n\t\t}\n\n\t\tAssert.assertNotNull(createdEvent[0]);\n\t\tAssert.assertNotNull(map);\n\t}\n\n\t@Test\n\tpublic void testDestroyMap() throws Exception {\n\t\tfinal Object mutex = new Object();\n\t\tfinal ClusterMapFactory factory = new ClusterMapFactory();\n\t\tfactory.setEventBus(new EventBusImplementation());\n\t\tfinal EventBus eventBus = factory.getEventBus();\n\n\t\tfinal Object[] destroyedEvent = new Object[]{null};\n\t\teventBus.addListener(ClusterMapFactory.MapDestroyEvent.class, event -> {\n\t\t\tAssert.assertNull(destroyedEvent[0]);\n\t\t\tdestroyedEvent[0] = event;\n\t\t\tsynchronized (mutex) {\n\t\t\t\tmutex.notifyAll();\n\t\t\t}\n\n\t\t});\n\n\t\tfinal Map<String, String> map = factory.createMap(\"test2\", String.class, String.class, \"1\", \"2\", \"3\");\n\n\t\tfactory.destroyMap(map);\n\n\t\tsynchronized (mutex) {\n\t\t\tmutex.wait(10_000);\n\t\t}\n\n\t\tAssert.assertNotNull(\"MapDestroyEvent not received\", destroyedEvent[0]);\n\t}\n\n\t@Test\n\tpublic void testPutToMap() throws Exception {\n\t\tfinal Object mutex = new Object();\n\n\t\tfinal ClusterMapFactory factory = new ClusterMapFactory();\n\t\tfactory.setEventBus(new EventBusImplementation());\n\t\tfinal EventBus eventBus = factory.getEventBus();\n\n\t\tfinal boolean[] received = new boolean[]{false};\n\n\t\tfinal Map<String, String> map = factory.createMap(\"test\", String.class, String.class);\n\n\t\teventBus.addListener(ClusterMapFactory.ElementAddEvent.class, event -> {\n\t\t\treceived[0] = true;\n\t\t\tAssert.assertEquals(\"kluczyk\", event.getKey());\n\t\t\tAssert.assertEquals(\"wartosc\", event.getValue());\n\t\t\tAssert.assertEquals(((DMap<?,?>) map).getUid(), event.getUid());\n\n\t\t\tsynchronized (mutex) {\n\t\t\t\tmutex.notifyAll();\n\t\t\t}\n\t\t});\n\n\t\tmap.put(\"kluczyk\", \"wartosc\");\n\n\t\tsynchronized (mutex) {\n\t\t\tmutex.wait(10_000);\n\t\t}\n\t\tAssert.assertTrue(received[0]);\n\t}\n\n\t@Test\n\tpublic void testRemoteCreatedMap() throws Exception {\n\t\tfinal Object mutex = new Object();\n\n\t\tClusterMapFactory.NewMapCreatedEvent eventCreate = new ClusterMapFactory.NewMapCreatedEvent();\n\t\teventCreate.setUid(\"test\");\n\t\teventCreate.setKeyClass(java.lang.String.class);\n\t\teventCreate.setValueClass(java.lang.String.class);\n\t\teventCreate.setParams(new String[]{\"1\", \"2\"});\n\n\t\tfinal ClusterMapFactory factory = new ClusterMapFactory();\n\t\tfactory.setEventBus(new EventBusImplementation());\n\t\tfinal EventBus eventBus = factory.getEventBus();\n\n\t\tfinal var maps = new Map[]{null};\n\t\teventBus.addListener(MapCreatedEvent.class, e -> {\n\t\t\tmaps[0] = e.getMap();\n\t\t\tAssert.assertEquals(\"test\", e.getUid());\n\t\t\tAssert.assertArrayEquals(new String[]{\"1\", \"2\"}, e.getParameters());\n\t\t\tsynchronized (mutex) {\n\t\t\t\tmutex.notifyAll();\n\t\t\t}\n\t\t});\n\n\t\tfactory.onNewMapCreated(eventCreate);\n\n\t\tsynchronized (mutex) {\n\t\t\tmutex.wait(10_000);\n\t\t}\n\n\t\tAssert.assertNotNull(\"It seems map was not created\", maps[0]);\n\t\tAssert.assertEquals(\"test\", ((DMap<?,?>) maps[0]).uid);\n\n\t\tClusterMapFactory.ElementAddEvent eventAdd = new ClusterMapFactory.ElementAddEvent();\n\t\teventAdd.setUid(\"test\");\n\t\teventAdd.setKey(\"xKEY\");\n\t\teventAdd.setValue(\"xVALUE\");\n\t\tfactory.onMapElementAdd(eventAdd);\n\n\t\teventAdd = new ClusterMapFactory.ElementAddEvent();\n\t\teventAdd.setUid(\"test\");\n\t\teventAdd.setKey(\"yKEY\");\n\t\teventAdd.setValue(\"yVALUE\");\n\n\t\tfactory.onMapElementAdd(eventAdd);\n\n\t\tAssert.assertEquals(\"xVALUE\", maps[0].get(\"xKEY\"));\n\t\tAssert.assertEquals(\"yVALUE\", maps[0].get(\"yKEY\"));\n\t\tAssert.assertEquals(2, maps[0].size());\n\n\t\tClusterMapFactory.ElementRemoveEvent eventDel = new ClusterMapFactory.ElementRemoveEvent();\n\t\teventDel.setUid(\"test\");\n\t\teventDel.setKey(\"xKEY\");\n\t\tfactory.onMapElementRemove(eventDel);\n\n\t\tAssert.assertNull(maps[0].get(\"xKEY\"));\n\t\tAssert.assertEquals(1, maps[0].size());\n\n\t\tClusterMapFactory.MapClearEvent eventClear = new ClusterMapFactory.MapClearEvent();\n\t\teventClear.setUid(\"test\");\n\t\tfactory.onMapClear(eventClear);\n\n\t\tAssert.assertEquals(0, maps[0].size());\n\n\t\tfinal boolean[] received = new boolean[]{false};\n\t\teventBus.addListener(MapDestroyedEvent.class, event -> {\n\t\t\tAssert.assertEquals(maps[0], event.getMap());\n\t\t\treceived[0] = true;\n\t\t\tsynchronized (mutex) {\n\t\t\t\tmutex.notifyAll();\n\t\t\t}\n\t\t});\n\n\t\tAssert.assertNotNull(factory.getMap(\"test\"));\n\n\t\tClusterMapFactory.MapDestroyEvent eventDestroy = new ClusterMapFactory.MapDestroyEvent();\n\t\teventDestroy.setUid(\"test\");\n\t\tfactory.onMapDestroyed(eventDestroy);\n\n\t\tAssert.assertNull(factory.getMap(\"test\"));\n\t\tsynchronized (mutex) {\n\t\t\tmutex.wait(10_000);\n\t\t}\n\t\tAssert.assertTrue(received[0]);\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/map/DMapTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.map;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.*;\n\n/**\n * Created by bmalkow on 04.12.2015.\n */\npublic class DMapTest {\n\n\t@Test\n\tpublic void testBasicOperations() {\n\t\tfinal Set<String> removedItems = new HashSet<>();\n\t\tfinal Map<String, String> addedItems = new HashMap<>();\n\t\tfinal boolean[] cleared = new boolean[]{false};\n\n\t\tfinal DMap.DMapListener listener = new DMap.DMapListener() {\n\t\t\t@Override\n\t\t\tpublic void onClear(DMap map) {\n\t\t\t\tcleared[0] = true;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onPut(DMap map, Object key, Object value) {\n\t\t\t\tAssert.assertNull(addedItems.put((String) key, (String) value));\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onPutAll(DMap map, Map m) {\n\t\t\t\taddedItems.putAll(m);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onRemove(DMap map, Object key) {\n\t\t\t\tAssert.assertTrue(removedItems.add((String) key));\n\t\t\t}\n\t\t};\n\n\t\tMap<String, String> tmp = new HashMap<>();\n\t\ttmp.put(\"7\", \"seven\");\n\t\ttmp.put(\"8\", \"eight\");\n\t\ttmp.put(\"9\", \"nine\");\n\t\ttmp.put(\"A\", \"ten\");\n\n\t\tMap<String, String> map = new DMap<>(\"test\", listener, String.class, String.class);\n\t\tmap.put(\"1\", \"one\");\n\t\tmap.put(\"2\", \"two\");\n\t\tmap.put(\"3\", \"three\");\n\t\tmap.put(\"4\", \"four\");\n\t\tmap.put(\"5\", \"five\");\n\t\tmap.put(\"6\", \"six\");\n\n\t\tAssert.assertEquals(6, map.size());\n\t\tAssert.assertEquals(map.size(), addedItems.size());\n\t\tAssert.assertTrue(\n\t\t\t\tmap.values().containsAll(addedItems.values()) && addedItems.values().containsAll(map.values()));\n\n\t\tmap.putAll(tmp);\n\n\t\tAssert.assertEquals(10, map.size());\n\t\tAssert.assertTrue(map.values().containsAll(addedItems.values()));\n\t\tAssert.assertTrue(addedItems.values().containsAll(map.values()));\n\n\t\tmap.remove(\"1\");\n\t\tmap.remove(\"2\");\n\n\t\tAssert.assertEquals(8, map.size());\n\t\tAssert.assertEquals(2, removedItems.size());\n\n\t\ttry {\n\t\t\tIterator<String> itK = map.keySet().iterator();\n\t\t\tfinal String elK = itK.next();\n\t\t\titK.remove();\n\t\t\tAssert.fail(\"Should be blocked!\");\n\t\t} catch (UnsupportedOperationException e) {\n\t\t}\n\n\t\tAssert.assertEquals(8, map.size());\n\t\tAssert.assertEquals(2, removedItems.size());\n\n\t\ttry {\n\t\t\tIterator<Map.Entry<String, String>> itE = map.entrySet().iterator();\n\t\t\tfinal Map.Entry<String, String> elE = itE.next();\n\t\t\titE.remove();\n\t\t\tAssert.fail(\"Should be blocked!\");\n\t\t} catch (UnsupportedOperationException e) {\n\t\t}\n\n\t\tAssert.assertEquals(8, map.size());\n\t\tAssert.assertEquals(2, removedItems.size());\n\n\t\ttry {\n\t\t\tIterator<String> itV = map.values().iterator();\n\t\t\tString elV = itV.next();\n\t\t\titV.remove();\n\t\t\tAssert.fail(\"Should be blocked!\");\n\t\t} catch (UnsupportedOperationException e) {\n\t\t}\n\n\t\tAssert.assertEquals(8, map.size());\n\t\tAssert.assertEquals(2, removedItems.size());\n\n\t\tAssert.assertFalse(cleared[0]);\n\t\tmap.clear();\n\t\tAssert.assertTrue(cleared[0]);\n\t\tAssert.assertEquals(0, map.size());\n\n\t}\n\n}"
  },
  {
    "path": "src/test/java/tigase/monitor/tasks/UsersDisconnectTaskTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.monitor.tasks;\n\nimport org.junit.Test;\n\nimport java.util.Optional;\n\nimport static org.junit.Assert.*;\nimport static tigase.monitor.tasks.ConnectionsTask.createUserDisconnectedEvent;\n\npublic class UsersDisconnectTaskTest {\n\n\t@Test\n\tpublic void testCreateAlarmEvent() throws Exception {\n\t\tOptional<ConnectionsTask.UserDisconnectedEvent> e = createUserDisconnectedEvent(100, 200, 10, 50);\n\n\t\tassertTrue(e.isPresent());\n\n\t\tassertEquals(100, e.get().getDisconnections());\n\t\tassertEquals(50f, e.get().getDisconnectionsPercent(), 0);\n\n\t\te = createUserDisconnectedEvent(99, 250, 10, 50);\n\t\tassertTrue(e.isPresent());\n\t\tassertEquals(151, e.get().getDisconnections());\n\t\tassertEquals(60.4, e.get().getDisconnectionsPercent(), 0.01);\n\n\t\te = createUserDisconnectedEvent(0, 99, 10, 50);\n\t\tassertTrue(e.isPresent());\n\t\tassertEquals(99, e.get().getDisconnections());\n\t\tassertEquals(100.0, e.get().getDisconnectionsPercent(), 0.01);\n\n\t\te = createUserDisconnectedEvent(99, 250, 152, 50);\n\t\tassertFalse(e.isPresent());\n\n\t\te = createUserDisconnectedEvent(99, 250, 10, 61);\n\t\tassertFalse(e.isPresent());\n\n\t\te = createUserDisconnectedEvent(250, 0, 10, 50);\n\t\tassertFalse(e.isPresent());\n\n\t\te = createUserDisconnectedEvent(0, 0, 10, 50);\n\t\tassertFalse(e.isPresent());\n\n\t\te = createUserDisconnectedEvent(1, 1, 10, 50);\n\t\tassertFalse(e.isPresent());\n\n\t\te = createUserDisconnectedEvent(0, 1, 10, 50);\n\t\tassertFalse(e.isPresent());\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/server/BasicComponentTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.server;\n\nimport org.junit.Assert;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport tigase.component.DSLBeanConfiguratorWithBackwardCompatibility;\nimport tigase.kernel.DefaultTypesConverter;\nimport tigase.kernel.beans.Bean;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.script.AddScriptCommand;\nimport tigase.server.script.Script;\nimport tigase.util.log.LogFormatter;\nimport tigase.xml.Element;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.JID;\n\nimport javax.script.*;\nimport java.io.BufferedReader;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentLinkedQueue;\nimport java.util.logging.ConsoleHandler;\nimport java.util.logging.Handler;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\npublic class BasicComponentTest {\n\n\tstatic Logger log;\n\tprivate static BasicComponent instance;\n\tprivate static Kernel kernel;\n\n\tstatic void configureLogger(Logger log, Level level) {\n\t\tlog.setUseParentHandlers(false);\n\t\tlog.setLevel(level);\n\t\tfinal Handler[] handlers = log.getHandlers();\n\t\tif (Arrays.stream(handlers).noneMatch(ConsoleHandler.class::isInstance)) {\n\t\t\tConsoleHandler ch = new ConsoleHandler();\n\t\t\tch.setLevel(level);\n\t\t\tch.setFormatter(new LogFormatter());\n\t\t\tlog.addHandler(ch);\n\t\t}\n\t\tfor (Handler logHandler : handlers) {\n\t\t\tlogHandler.setLevel(level);\n\t\t}\n\t}\n\n\tprivate static Script getTestScript() throws ScriptException {\n\t\tfinal AddScriptCommand addScriptCommand = new AddScriptCommand();\n\t\tfinal InputStream resourceAsStream = BasicComponentTest.class.getResourceAsStream(\"/TestScript.groovy\");\n\t\tfinal BufferedReader br = new BufferedReader(new InputStreamReader(resourceAsStream));\n\t\tString scriptTxt = br.lines().collect(Collectors.joining(\"\\n\"));\n\n\t\tfinal Bindings bindings = instance.scriptEngineManager.getBindings();\n\t\treturn addScriptCommand.addAdminScript(\"test-cmd\", \"Test script\", \"tigase\", scriptTxt, \"groovy\", \"groovy\",\n\t\t\t\t\t\t\t\t\t\t\t   bindings);\n\t}\n\n\t@BeforeClass\n\tpublic static void setup() {\n\t\tlog = Logger.getLogger(\"tigase\");\n\t\tconfigureLogger(log, Level.WARNING);\n\n\t\tMap<String, Object> props = new HashMap<>();\n\t\tprops.put(\"name\", \"basic\");\n\n\t\tkernel = new Kernel();\n\t\tkernel.setName(\"basic\");\n\t\tkernel.setForceAllowNull(true);\n\t\tkernel.registerBean(DefaultTypesConverter.class).exec();\n\t\tkernel.registerBean(DSLBeanConfiguratorWithBackwardCompatibility.class).exportable().exec();\n\t\tfinal DSLBeanConfiguratorWithBackwardCompatibility config = kernel.getInstance(\n\t\t\t\tDSLBeanConfiguratorWithBackwardCompatibility.class);\n\t\tconfig.setProperties(props);\n\t\tkernel.registerBean(\"service\").asClass(TestBasicComponent.class).setActive(true).exec();\n\t\ttry {\n\t\t\tinstance = kernel.getInstance(TestBasicComponent.class);\n\t\t\tfinal Script script = getTestScript();\n\t\t\tinstance.scriptCommands.put(\"test-cmd\", script);\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.WARNING, ex, () -> \"There was an error setting up test\");\n\t\t}\n\n\t}\n\n\t@Test\n\tpublic void paralelScriptExecution() {\n\t\trunTest(true);\n\t}\n\n\t@Test\n\tpublic void serialScriptExecution() {\n\t\trunTest(false);\n\t}\n\n\tpublic void runTest(boolean paralel) {\n\t\tfinal Queue<Packet> results = new ConcurrentLinkedQueue<>();\n\t\tfinal Set<Iq> iqPackets = generatePackets(1000);\n\t\tfinal Set<JID> JIDs = (paralel ? iqPackets.parallelStream() : iqPackets.stream()).map(iqPacket -> {\n\t\t\tfinal JID from = iqPacket.getFrom();\n\t\t\tinstance.processScriptCommand(iqPacket, results);\n\t\t\treturn from;\n\t\t}).collect(Collectors.toSet());\n\n\t\tfinal Set<JID> resultJIDs = results.stream()\n\t\t\t\t.map(packet -> JID.jidInstanceNS(Command.getFieldValue(packet, \"Note\")))\n\t\t\t\t.filter(Objects::nonNull)\n\t\t\t\t.collect(Collectors.toSet());\n\n\t\tAssert.assertEquals(JIDs, resultJIDs);\n\n\t}\n\n\t@Test\n\tpublic void simpleTest() throws ScriptException {\n\t\tfinal ScriptEngineManager scriptEngineManager = new ScriptEngineManager();\n\t\tfinal ScriptEngine scriptEngine = scriptEngineManager.getEngineByExtension(\"groovy\");\n\t\tfinal String script = \"return key\";\n\t\tfinal CompiledScript compiled = ((Compilable) scriptEngine).compile(script);\n\t\tfinal Set<String> input = Stream.generate(() -> UUID.randomUUID().toString())\n\t\t\t\t.limit(10)\n\t\t\t\t.collect(Collectors.toSet());\n\n\t\tfinal Set<String> output = input.stream().parallel().map(uuid -> {\n//\t\t\tfinal ScriptContext context = scriptEngine.getContext();\n\t\t\tfinal ScriptContext context = new SimpleScriptContext();\n\t\t\tfinal Bindings bindings = scriptEngine.createBindings();\n\t\t\tcontext.setBindings(bindings, ScriptContext.ENGINE_SCOPE);\n\t\t\tbindings.put(\"key\", uuid);\n\t\t\tObject eval = null;\n\t\t\ttry {\n\t\t\t\teval = compiled.eval(context);\n\t\t\t} catch (ScriptException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\treturn (String) eval;\n\t\t}).filter(Objects::nonNull).collect(Collectors.toSet());\n\t\tAssert.assertEquals(input, output);\n\t}\n\n\tprivate Set<Iq> generatePackets(int maxSize) {\n\t\treturn Stream.generate(() -> {\n\t\t\tJID stanzaAddress = JID.jidInstanceNS(UUID.randomUUID().toString(), \"domain.com\");\n\t\t\tfinal Element iq = Command.createIqCommand(stanzaAddress, stanzaAddress, StanzaType.set,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   stanzaAddress.toString(), \"test-cmd\", Command.DataType.submit);\n\t\t\tfinal Iq iqPacket = new Iq(iq, stanzaAddress, stanzaAddress);\n\t\t\tCommand.addFieldValue(iqPacket, \"accountjid\", stanzaAddress.toString());\n\t\t\tiqPacket.setXMLNS(Packet.CLIENT_XMLNS);\n\t\t\tiqPacket.setPermissions(Permissions.ADMIN);\n\t\t\treturn iqPacket;\n\t\t}).limit(maxSize).collect(Collectors.toSet());\n\t}\n\n\t@Bean(name = \"TestBasicComponent\", parent = Kernel.class, exportable = true, active = true)\n\tpublic static class TestBasicComponent\n\t\t\textends BasicComponent {\n\n\t\tpublic TestBasicComponent() {\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean canCallCommand(JID jid, String commandId) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean canCallCommand(JID jid, String domain, String commandId) {\n\t\t\treturn true;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/server/BootstrapTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport org.junit.Ignore;\nimport org.junit.Test;\nimport tigase.TestLogger;\nimport tigase.conf.ConfigReader;\nimport tigase.db.AuthRepositoryMDImpl;\nimport tigase.db.UserRepositoryMDImpl;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.xmppclient.ClientConnectionManager;\nimport tigase.server.xmppserver.S2SConnectionManager;\nimport tigase.server.xmppsession.SessionManager;\n\nimport java.lang.reflect.Field;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * Created by andrzej on 07.03.2016.\n */\n@Ignore\npublic class BootstrapTest {\n\n\tprivate static final Logger log = TestLogger.getLogger(BootstrapTest.class);\n\n\tprivate Map<String, Object> props = new HashMap<>();\n\n\t@Test\n\tpublic void testNonCluster() throws InterruptedException, ConfigReader.ConfigException {\n\t\tprops.put(\"cluster-mode\", \"false\");\n\t\tBootstrap bootstrap = executeTest();\n\t\tThread.sleep(10 * 60 * 1000);\n\t\tbootstrap.stop();\n\t}\n\n\t@Test\n\tpublic void testCluster() throws ConfigReader.ConfigException {\n\t\tprops.put(\"cluster-mode\", \"true\");\n\t\tBootstrap bootstrap = executeTest();\n\t\tbootstrap.stop();\n\t}\n\n\tpublic Bootstrap executeTest() throws ConfigReader.ConfigException {\n\t\tBootstrap bootstrap = new Bootstrap();\n\n\t\tbootstrap.setProperties(getProps());\n\n\t\tbootstrap.start();\n\n\t\tKernel kernel = bootstrap.getKernel();\n\t\tassertNotNull(kernel);\n\n\t\tMessageRouter mr = kernel.getInstance(\"message-router\");\n\t\tClientConnectionManager c2s = kernel.getInstance(\"c2s\");\n\t\tS2SConnectionManager s2s = kernel.getInstance(\"s2s\");\n\t\tUserRepositoryMDImpl userRepository = kernel.getInstance(\"userRepository\");\n\t\tAuthRepositoryMDImpl authRepository = kernel.getInstance(\"authRepository\");\n\t\tassertNotNull(mr);\n\t\tassertNotNull(c2s);\n\t\tassertNotNull(s2s);\n\t\tassertNotNull(userRepository);\n\t\tassertNotNull(userRepository.getRepo(null));\n\t\tassertNotNull(authRepository);\n\t\tassertNotNull(authRepository.getRepo(\"default\"));\n\n\t\tassertCommandACL(kernel, \"ala-ma-kota\", new CmdAcl(\"LOCAL\"));\n\t\tassertCommandACL(kernel, \"ala-ma-kota1\", new CmdAcl(\"test.com\"));\n\t\tassertCommandACL(kernel, \"ala-ma-kota2\", new CmdAcl(\"ala@test.com\"));\n\n\t\treturn bootstrap;\n\t}\n\n\tpublic Map<String, Object> getProps() {\n\t\tMap<String, Object> props = new HashMap<>(this.props);\n\n\t\t//props.put(\"userRepository/repo-uri\", \"jdbc:postgresql://127.0.0.1/tigase?user=test&password=test&autoCreateUser=true\");\n\t\tprops.put(\"dataSource/repo-uri\",\n\t\t\t\t  \"jdbc:postgresql://127.0.0.1/tigase?user=test&password=test&autoCreateUser=true\");\n\t\tprops.put(\"sess-man/commands/ala-ma-kota\", \"LOCAL\");\n\t\tprops.put(\"sess-man/commands/ala-ma-kota1\", \"test.com\");\n\t\tprops.put(\"sess-man/commands/ala-ma-kota2\", \"ala@test.com\");\n\t\tprops.put(\"c2s/incoming-filters\", \"tigase.server.filters.PacketCounter,tigase.server.filters.PacketCounter\");\n\n\t\treturn props;\n\t}\n\n\tprivate void assertCommandACL(Kernel kernel, String cmdId, CmdAcl expectedAcl) {\n\t\ttry {\n\t\t\tSessionManager sm = kernel.getInstance(SessionManager.class);\n\t\t\tField commandsAcl = BasicComponent.class.getDeclaredField(\"commandsACL\");\n\t\t\tcommandsAcl.setAccessible(true);\n\t\t\tMap<String, Set<CmdAcl>> val = (Map<String, Set<CmdAcl>>) commandsAcl.get(sm);\n\t\t\tlog.log(Level.FINE, \"ACL = \" + val);\n\t\t\tSet<CmdAcl> acl = val.get(cmdId);\n\t\t\tassertTrue(acl.stream().filter(a -> a.equals(expectedAcl)).findAny().isPresent());\n\t\t\tlog.log(Level.FINE, \"\" + acl.getClass() + \", \" + acl);\n//\t\t\tSystem.out.print(\"cmd \" + cmdId + \" = \" + acl + \", expected = \" + expectedAcl);\n\t\t} catch (Exception ex) {\n\t\t\tex.printStackTrace();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/server/CmdAclTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\n\n/**\n * Created by andrzej on 27.04.2017.\n */\npublic class CmdAclTest {\n\n\t@Test\n\tpublic void test() {\n\t\tCmdAcl cmdAcl = new CmdAcl(\"ALL\");\n\t\tassertEquals(CmdAcl.Type.ALL, cmdAcl.getType());\n\t\tcmdAcl = new CmdAcl(\"ADMIN\");\n\t\tassertEquals(CmdAcl.Type.ADMIN, cmdAcl.getType());\n\t\tcmdAcl = new CmdAcl(\"LOCAL\");\n\t\tassertEquals(CmdAcl.Type.LOCAL, cmdAcl.getType());\n\t\tcmdAcl = new CmdAcl(\"NONE\");\n\t\tassertEquals(CmdAcl.Type.NONE, cmdAcl.getType());\n\t\tcmdAcl = new CmdAcl(\"test.com\");\n\t\tassertEquals(CmdAcl.Type.DOMAIN, cmdAcl.getType());\n\t\tcmdAcl = new CmdAcl(\"ala@test.com\");\n\t\tassertEquals(CmdAcl.Type.JID, cmdAcl.getType());\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/server/ConnectionManagerTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport org.junit.Test;\nimport tigase.io.IOInterface;\nimport tigase.net.IOService;\nimport tigase.stats.StatisticsList;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.xmpp.XMPPIOService;\n\nimport java.io.IOException;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.SocketChannel;\nimport java.security.SecureRandom;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.UUID;\n\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * Created by andrzej on 20.01.2017.\n */\npublic class ConnectionManagerTest {\n\n\t@Test\n\tpublic void test_watchDog_timeout_whitespace() throws Exception {\n\t\ttest_watchDogStop(ConnectionManager.WATCHDOG_PING_TYPE.WHITESPACE, Type.timeout, false);\n\t}\n\n\t@Test\n\tpublic void test_watchDog_timeout_xmppPing() throws Exception {\n\t\ttest_watchDogStop(ConnectionManager.WATCHDOG_PING_TYPE.XMPP, Type.timeout, false);\n\t}\n\n\t@Test\n\tpublic void test_watchDog_timeout_whitespace_withDataWaiting() throws Exception {\n\t\ttest_watchDogStop(ConnectionManager.WATCHDOG_PING_TYPE.WHITESPACE, Type.timeout, true);\n\t}\n\n\t@Test\n\tpublic void test_watchDog_timeout_xmppPing_withDataWaiting() throws Exception {\n\t\ttest_watchDogStop(ConnectionManager.WATCHDOG_PING_TYPE.XMPP, Type.timeout, true);\n\t}\n\n\tprotected ConnectionManager newConnectionManager(ConnectionManager.WATCHDOG_PING_TYPE pingType) throws Exception {\n\t\tConnectionManager connectionManager = new ConnectionManager() {\n\t\t\t@Override\n\t\t\tpublic Queue<Packet> processSocketData(XMPPIOService serv) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean processUndeliveredPacket(Packet packet, Long stamp, String errorMessage) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void reconnectionFailed(Map port_props) {\n\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void xmppStreamClosed(XMPPIOService serv) {\n\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String[] xmppStreamOpened(XMPPIOService serv, Map attribs) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void packetsReady(IOService service) throws IOException {\n\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean serviceStopped(IOService service) {\n\t\t\t\treturn super.serviceStopped((XMPPIOService) service);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void tlsHandshakeCompleted(IOService service) {\n\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected long getMaxInactiveTime() {\n\t\t\t\treturn 3 * MINUTE;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected XMPPIOService<?> getXMPPIOServiceInstance() {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t};\n\n\t\tField f = ConnectionManager.class.getDeclaredField(\"watchdogPingType\");\n\t\tf.setAccessible(true);\n\t\tf.set(connectionManager, pingType);\n\t\tf = ConnectionManager.class.getDeclaredField(\"watchdogDelay\");\n\t\tf.setAccessible(true);\n\t\tf.set(connectionManager, 1);\n\t\tf = ConnectionManager.class.getDeclaredField(\"watchdogTimeout\");\n\t\tf.setAccessible(true);\n\t\tf.set(connectionManager, -1);\n\n\t\treturn connectionManager;\n\n\t}\n\n\tprotected XMPPIOService getXMPPIOServiceInstance() {\n\t\treturn new XMPPIOService() {\n\t\t\t@Override\n\t\t\tpublic boolean waitingToRead() {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean waitingToSend() {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t};\n\t}\n\n\tprotected XMPPIOService registerService(ConnectionManager connectionManager, boolean waitingToSend)\n\t\t\tthrows Exception {\n\n\t\tXMPPIOService service = getXMPPIOServiceInstance();\n\t\tassertNotNull(service);\n\n\t\tIOInterface io = new IOInterface() {\n\t\t\t@Override\n\t\t\tpublic int bytesRead() {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean checkCapabilities(String caps) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic int getInputPacketSize() throws IOException {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic SocketChannel getSocketChannel() {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void getStatistics(StatisticsList list, boolean reset) {\n\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic long getBytesSent(boolean reset) {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic long getTotalBytesSent() {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic long getBytesReceived(boolean reset) {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic long getTotalBytesReceived() {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic long getBuffOverflow(boolean reset) {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic long getTotalBuffOverflow() {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean isConnected() {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean isRemoteAddress(String addr) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic ByteBuffer read(ByteBuffer buff) throws IOException {\n\t\t\t\tthrow new IOException(\"Read failed!\");\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void stop() throws IOException {\n\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean waitingToSend() {\n\t\t\t\treturn waitingToSend;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic int waitingToSendSize() {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic int write(ByteBuffer buff) throws IOException {\n\t\t\t\tthrow new IOException(\"Write failed!\");\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void setLogId(String logId) {\n\n\t\t\t}\n\t\t};\n\t\tField f = IOService.class.getDeclaredField(\"socketIO\");\n\t\tf.setAccessible(true);\n\t\tf.set(service, io);\n\n\t\tString serviceId = UUID.randomUUID().toString();\n\t\tf = IOService.class.getDeclaredField(\"id\");\n\t\tf.setAccessible(true);\n\t\tf.set(service, serviceId);\n\t\tgetServices(connectionManager).put(serviceId, service);\n\t\tservice.setIOServiceListener(connectionManager);\n\t\tservice.setUserJid(\"m\" + SecureRandom.getInstanceStrong().nextInt() + \".example.com\");\n\t\tservice.getSessionData().put(XMPPIOService.HOSTNAME_KEY, DNSResolverFactory.getInstance().getDefaultHost());\n\n\t\treturn service;\n\t}\n\n\tprotected void test_watchDogStop(ConnectionManager.WATCHDOG_PING_TYPE pingType, Type testType,\n\t\t\t\t\t\t\t\t\t boolean waitingToSend) throws Exception {\n\t\tConnectionManager connectionManager = newConnectionManager(pingType);\n\n\t\tXMPPIOService service = registerService(connectionManager, waitingToSend);\n\t\tField f = pingType == ConnectionManager.WATCHDOG_PING_TYPE.XMPP ? XMPPIOService.class.getDeclaredField(\n\t\t\t\t\"lastXmppPacketReceivedTime\") : IOService.class.getDeclaredField(\"lastTransferTime\");\n\t\tf.setAccessible(true);\n\t\tswitch (testType) {\n\t\t\tcase inactivity:\n\t\t\t\tf.set(service, System.currentTimeMillis() - connectionManager.getMaxInactiveTime());\n\t\t\tcase timeout:\n\t\t\t\tf.set(service, System.currentTimeMillis() - connectionManager.getMaxInactiveTime() / 2);\n\t\t}\n\n\t\texecute_watchDogStop(connectionManager);\n\t}\n\n\tprotected void execute_watchDogStop(ConnectionManager connectionManager) throws Exception {\n\t\tConnectionManager.Watchdog watchdog = connectionManager.newWatchdog();\n\t\tMethod m = ConnectionManager.Watchdog.class.getDeclaredMethod(\"executeWatchdog\");\n\t\tm.setAccessible(true);\n\t\tm.invoke(watchdog);\n\n\t\tassertTrue(\"All connections should be removed due to IOExceptions\", getServices(connectionManager).isEmpty());\n\t}\n\n\tprotected Map<String, XMPPIOService> getServices(ConnectionManager connectionManager) throws Exception {\n\t\tField f = ConnectionManager.class.getDeclaredField(\"services\");\n\t\tf.setAccessible(true);\n\t\treturn (Map<String, XMPPIOService>) f.get(connectionManager);\n\t}\n\n\tprivate enum Type {\n\t\tinactivity,\n\t\ttimeout\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/server/MessageRouterTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.server;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.DomBuilderHandler;\nimport tigase.xml.Element;\nimport tigase.xml.SimpleParser;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.ArrayDeque;\nimport java.util.Queue;\n\npublic class MessageRouterTest {\n\n\t@Test\n\tpublic void processPacketMR() throws TigaseStringprepException {\n\t\tfinal ArrayDeque<Packet> results = new ArrayDeque<>();\n\t\tfinal MessageRouter messageRouter = new MessageRouter();\n\t\tString stanza = \"<iq to=\\\"message-router@jabber.today\\\" type=\\\"error\\\" id=\\\"43C107AD-9B5C-499C-8A80-87C72ABF7C83\\\" xmlns=\\\"jabber:client\\\" from=\\\"a@jabber.today/iPhone\\\"><query xmlns=\\\"http://jabber.org/protocol/disco#info\\\"><identity name=\\\"Tigase ver. 8.2.0-SNAPSHOT-b11574/c2603d9c\\\" type=\\\"router\\\" category=\\\"component\\\"/><identity name=\\\"Tigase ver. 8.2.0-SNAPSHOT-b11574/c2603d9c\\\" type=\\\"im\\\" category=\\\"server\\\"/><feature var=\\\"http://jabber.org/protocol/commands\\\"/><x type=\\\"result\\\" xmlns=\\\"jabber:x:data\\\"><field type=\\\"hidden\\\" var=\\\"FORM_TYPE\\\"><value>http://jabber.org/network/serverinfo</value></field><field type=\\\"list-multi\\\" var=\\\"abuse-addresses\\\"><value>mailto:support@tigase.net</value><value>xmpp:tigase@muc.tigase.im</value><value>https://tigase.net/technical-support</value></field></x></query><error code=\\\"404\\\" type=\\\"wait\\\"><recipient-unavailable xmlns=\\\"urn:ietf:params:xml:ns:xmpp-stanzas\\\"/><text xmlns=\\\"urn:ietf:params:xml:ns:xmpp-stanzas\\\" xml:lang=\\\"en\\\">The target is unavailable at this time.</text></error></iq>\";\n\n\t\tSimpleParser parser = new SimpleParser();\n\t\tfinal DomBuilderHandler handler = new DomBuilderHandler();\n\t\tparser.parse(handler, stanza);\n\t\tfinal Queue<Element> parsedElements = handler.getParsedElements();\n\t\tfinal Element element = parsedElements.peek();\n\t\tfinal Packet packet = Packet.packetInstance(element);\n\t\tpacket.setPacketFrom(JID.jidInstanceNS(\"sess-man@ip-172-31-38-91.us-west-2.compute.internal\"));\n\t\tpacket.setPacketTo(JID.jidInstanceNS(\"message-router@jabber.today\"));\n\t\tpacket.initVars();\n\t\tmessageRouter.processPacketMR(packet, results);\n\t\tAssert.assertTrue(results.isEmpty());\n\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/server/PacketTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.JID;\n\nimport static org.junit.Assert.assertEquals;\n\n/**\n * @author Wojciech Kapcia\n */\npublic class PacketTest {\n\n\tPacket packetInstance;\n\n\tpublic PacketTest() {\n\t}\n\n\t@Before\n\tpublic void setUp() {\n\n\t\tJID from = JID.jidInstanceNS(\"server\");\n\t\tJID to = JID.jidInstanceNS(\"user\");\n\n\t\tElement property = new Element(\"property\", new String[]{\"name\", \"value\", \"xmlns\"},\n\t\t\t\t\t\t\t\t\t   new String[]{\"some name\", \"some value\", \"namespace\"});\n\t\tElement iq = Command.createIqCommand(from, to, StanzaType.get, \"myId\", \"node\", Command.DataType.submit);\n\t\tiq.findChild(new String[]{\"iq\", \"command\", \"x\"}).addChild(property);\n\t\tpacketInstance = Iq.packetInstance(iq, from, to);\n\t\tpacketInstance.getStanzaFrom().getDomain();\n\n\t}\n\n\t@Test\n\tpublic void testOkResult_String_int() {\n\n\t\tString includeXML = null;\n\t\tint depth = 5;\n\t\tPacket result = packetInstance.okResult(includeXML, depth);\n\t\tElement property = result.getElement().findChild(new String[]{\"iq\", \"command\", \"x\", \"property\"});\n\n\t\tassertEquals(\"some name\", property.getAttributeStaticStr(\"name\"));\n\t\tassertEquals(\"some value\", property.getAttributeStaticStr(\"value\"));\n\t\tassertEquals(\"namespace\", property.getAttributeStaticStr(\"xmlns\"));\n\t\tassertEquals(null, property.getAttributeStaticStr(\"unknown\"));\n\n\t\tdepth = 0;\n\t\tresult = packetInstance.okResult(includeXML, depth);\n\t\tproperty = result.getElement().findChild(new String[]{\"iq\", \"command\", \"x\", \"property\"});\n\n\t\tassertEquals(null, property);\n\n\t\tdepth = 1;\n\t\tresult = packetInstance.okResult(includeXML, depth);\n\t\tproperty = result.getElement().findChild(new String[]{\"iq\", \"command\", \"x\"});\n\t\tassertEquals(null, property);\n\n\t\tproperty = result.getElement().findChild(new String[]{\"iq\", \"command\"});\n\t\tassertEquals(\"command\", property.getName());\n\t\tassertEquals(\"http://jabber.org/protocol/commands\", property.getAttributeStaticStr(\"xmlns\"));\n\t\tassertEquals(\"node\", property.getAttributeStaticStr(\"node\"));\n\t\tassertEquals(null, property.getAttributeStaticStr(\"unknown\"));\n\n\t}\n\n\t@Test\n\tpublic void testOkResult_Element_int() {\n\t\tElement includeXML = null;\n\t\tint depth = 5;\n\t\tPacket result = packetInstance.okResult(includeXML, depth);\n\t\tElement property = result.getElement().findChild(new String[]{\"iq\", \"command\", \"x\", \"property\"});\n\n\t\tassertEquals(\"some name\", property.getAttributeStaticStr(\"name\"));\n\t\tassertEquals(\"some value\", property.getAttributeStaticStr(\"value\"));\n\t\tassertEquals(\"namespace\", property.getAttributeStaticStr(\"xmlns\"));\n\t\tassertEquals(null, property.getAttributeStaticStr(\"unknown\"));\n\n\t\tdepth = 0;\n\t\tresult = packetInstance.okResult(includeXML, depth);\n\t\tproperty = result.getElement().findChild(new String[]{\"iq\", \"command\", \"x\", \"property\"});\n\n\t\tassertEquals(null, property);\n\n\t\tdepth = 1;\n\t\tresult = packetInstance.okResult(includeXML, depth);\n\t\tproperty = result.getElement().findChild(new String[]{\"iq\", \"command\", \"x\"});\n\t\tassertEquals(null, property);\n\n\t\tproperty = result.getElement().findChild(new String[]{\"iq\", \"command\"});\n\t\tassertEquals(\"command\", property.getName());\n\t\tassertEquals(\"http://jabber.org/protocol/commands\", property.getAttributeStaticStr(\"xmlns\"));\n\t\tassertEquals(\"node\", property.getAttributeStaticStr(\"node\"));\n\t\tassertEquals(null, property.getAttributeStaticStr(\"unknown\"));\n\n\t}\n\n\t@Test\n\tpublic void testPacketSecure() throws TigaseStringprepException {\n\t\tJID jid1 = JID.jidInstance(\"user1@example.com/res1\");\n\t\tJID jid2 = JID.jidInstance(\"user1@example.com/res2\");\n\n\t\tElement iqEl = new Element(\"iq\", new String[]{\"from\", \"to\", \"xmlns\", \"type\"},\n\t\t\t\t\t\t\t\t   new String[]{jid1.toString(), jid2.toString(), Iq.CLIENT_XMLNS, \"get\"});\n\t\tiqEl.addChild(new Element(\"command\", new String[]{\"xmlns\"}, new String[]{Command.XMLNS}));\n\t\tPacket result = Packet.packetInstance(iqEl);\n\n\n\t\tCommand.addFieldValue(result, \"password\", \"mySuperSecretPassword\", \"text-private\",\n\t\t\t\t\t\t\t  \"The password for this account\");\n\n\t\tAssert.assertTrue(\"Output secured in default Element.toString(): \" + iqEl.toString(),\n\t\t\t\t\t\t  iqEl.toString().contains(\"mySuperSecretPassword\"));\n\t\tAssert.assertFalse(\"Plain output in Element.toStringSecure(): \" + iqEl.toStringSecure(),\n\t\t\t\t\t\t   iqEl.toStringSecure().contains(\"mySuperSecretPassword\"));\n\n//\t\tDefault `result.toString()` relies on `tigase.server.Packet.FULL_DEBUG` which may be changed by other tests\n//\t\thence testing it could yield unsteady results. Commenting this out\n//\t\tAssert.assertFalse(\"Plain output in Packet.toString(): \" + result.toString(), result.toString().contains(\"mySuperSecretPassword\"));\n\t\tAssert.assertFalse(\"Plain output in Packet.toString(true): \" + result.toString(true),\n\t\t\t\t\t\t   result.toString(true).contains(\"mySuperSecretPassword\"));\n\t\tAssert.assertTrue(\"Output secured in default Packet.toString(false): \" + result.toString(false),\n\t\t\t\t\t\t  result.toString(false).contains(\"mySuperSecretPassword\"));\n\t\tAssert.assertFalse(\"Plain output in Packet.toStringSecure(true): \" + result.toStringSecure(),\n\t\t\t\t\t\t   result.toStringSecure().contains(\"mySuperSecretPassword\"));\n\n\t\tAssert.assertTrue(\"Output secured in default Packet.toStringFull(): \" + result.toStringFull(),\n\t\t\t\t\t\t  result.toStringFull().contains(\"mySuperSecretPassword\"));\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/server/amp/db/AbstractMsgBroadcastRepositoryTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp.db;\n\nimport org.junit.BeforeClass;\nimport org.junit.FixMethodOrder;\nimport org.junit.Test;\nimport org.junit.runners.MethodSorters;\nimport tigase.db.AbstractDataSourceAwareTestCase;\nimport tigase.db.DataSource;\nimport tigase.db.DataSourceAware;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.Date;\nimport java.util.UUID;\n\nimport static org.junit.Assert.*;\n\n/**\n * Created by andrzej on 24.03.2017.\n */\n@FixMethodOrder(MethodSorters.NAME_ASCENDING)\npublic abstract class AbstractMsgBroadcastRepositoryTest<DS extends DataSource> extends AbstractDataSourceAwareTestCase<DS, MsgBroadcastRepository> {\n\n\tprotected static boolean checkEmoji = true;\n\tprotected static String emoji = \"\\uD83D\\uDE97\\uD83D\\uDCA9\\uD83D\\uDE21\";\n\tprivate static BareJID jid;\n\tprivate static Element msg;\n\tprivate static String msgId;\n\n\t@BeforeClass\n\tpublic static void init() throws TigaseStringprepException {\n\t\tjid = BareJID.bareJIDInstance(\"broadcast-\" + UUID.randomUUID(), \"example.com\");\n\t\tmsgId = \"test-\" + UUID.randomUUID().toString();\n\t\tmsg = new Element(\"message\");\n\t\tmsg.addChild(new Element(\"body\", \"Testing broadcast messages\" + (checkEmoji ? emoji : \"\")));\n\t}\n\t\n\t@Test\n\tpublic void test1_addingBroadcastMessage() throws InterruptedException {\n\t\tDate expire = new Date(System.currentTimeMillis() + (60 * 1000 * 5));\n\t\tassertTrue(\"Not added message to broadcast list!\", repo.updateBroadcastMessage(msgId, msg, expire, jid));\n\t\tassertNotNull(\"Not found message with id = \" + msgId, repo.getBroadcastMsg(msgId));\n\t\trepo.loadMessagesToBroadcast();\n\t\tassertNotNull(\"Not found message with id = \" + msgId, repo.getBroadcastMsg(msgId));\n\t}\n\n\t@Test\n\tpublic void test2_loadingBroadcastMessagesAndAddingBroadcastMessageRecipient() throws InterruptedException {\n\t\tThread.sleep(1000);\n\t\trepo.loadMessagesToBroadcast();\n\t\tassertNotNull(\"Not found message with id = \" + msgId, repo.getBroadcastMsg(msgId));\n\n\t\tassertFalse(\"Added message instead of adding message recipient!\",\n\t\t\t\t\trepo.updateBroadcastMessage(msgId, null, null, jid));\n\t}\n\t\n\t@Override\n\tprotected Class<? extends DataSourceAware> getDataSourceAwareIfc() {\n\t\treturn MsgBroadcastRepository.class;\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/server/amp/db/AbstractMsgRepositoryTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp.db;\n\nimport org.junit.*;\nimport org.junit.runners.MethodSorters;\nimport tigase.db.*;\nimport tigase.server.Message;\nimport tigase.server.Packet;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.impl.AbstractProcessorWithDataSourceAwareTestCase;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.concurrent.locks.ReentrantLock;\nimport java.util.stream.Collectors;\n\nimport static org.junit.Assert.*;\n\n/**\n * Created by andrzej on 22.03.2017.\n */\n@FixMethodOrder(MethodSorters.NAME_ASCENDING)\npublic abstract class AbstractMsgRepositoryTest<DS extends DataSource, T>\n\t\textends AbstractProcessorWithDataSourceAwareTestCase<DS,MsgRepository> {\n\n\tprotected static String emoji = \"\\uD83D\\uDE97\\uD83D\\uDCA9\\uD83D\\uDE21\";\n\tprotected boolean checkEmoji = true;\n\tprivate JID recipient;\n\tprivate XMPPResourceConnection recipientSession;\n\tprivate JID sender;\n\t\n\t@Before\n\tpublic void setup() throws Exception {\n\t\tsender = JID.jidInstance(\"sender-\" + UUID.randomUUID(), \"example.com\", \"resource-1\");\n\t\tgetUserRepository().addUser(sender.getBareJID());\n\t\trecipient = JID.jidInstance(\"recipient-\" + UUID.randomUUID(), \"example.com\", \"resource-1\");\n\t\tgetUserRepository().addUser(recipient.getBareJID());\n\n\t\trecipientSession = getSession(recipient, recipient);\n\t}\n\t\n\t@Test\n\tpublic void testStorageOfOfflineMessage()\n\t\t\tthrows TigaseDBException, NotAuthorizedException, TigaseStringprepException {\n\t\tList<Packet> messages = new ArrayList<>();\n\t\tfor (int i = 0; i < 5; i++) {\n\t\t\tPacket message = Message.getMessage(sender, recipient, StanzaType.chat, generateRandomBody(), null, null,\n\t\t\t\t\t\t\t\t\t\t\t\tUUID.randomUUID().toString());\n\t\t\tassertTrue(repo.storeMessage(sender, recipient, null, message.getElement(), null));\n\t\t\tmessages.add(message);\n\t\t}\n\n\t\tMap<Enum, Long> count = repo.getMessagesCount(recipient);\n\t\tassertEquals(1, count.size());\n\t\tassertNotNull(count.get(MsgRepository.MSG_TYPES.message));\n\t\tassertEquals(5, count.get(MsgRepository.MSG_TYPES.message).longValue());\n\n\t\tList<Element> list = repo.getMessagesList(recipient);\n\t\tassertNotNull(list);\n\t\tassertEquals(5, list.size());\n\t\tfor (Element item : list) {\n\t\t\tassertEquals(recipient.getBareJID().toString(), item.getAttributeStaticStr(\"jid\"));\n\t\t\tassertNotNull(item.getAttributeStaticStr(\"node\"));\n\t\t\tassertEquals(\"message\", item.getAttributeStaticStr(\"type\"));\n\t\t\tassertEquals(sender.getBareJID().toString(), item.getAttributeStaticStr(\"name\"));\n\t\t}\n\n\t\tList<String> msgIds = list.stream()\n\t\t\t\t.map(item -> item.getAttributeStaticStr(\"node\"))\n\t\t\t\t.collect(Collectors.toList());\n\n\t\tQueue<Element> loaded = repo.loadMessagesToJID(recipientSession, false);\n\n\t\tfor (Packet message : messages) {\n\t\t\tElement el1 = message.getElement();\n\t\t\tElement el2 = loaded.poll();\n\t\t\tassertNotNull(el2);\n\t\t\tassertEquals(el1.getAttributeStaticStr(\"id\"), el2.getAttributeStaticStr(\"id\"));\n\t\t\tassertEquals(el1.getChildStaticStr(\"body\"), el2.getChildStaticStr(\"body\"));\n\t\t}\n\n\t\tloaded = repo.loadMessagesToJID(msgIds.subList(0, 3), recipientSession, false, null);\n\t\tfor (Packet message : messages.subList(0, 3)) {\n\t\t\tElement el1 = message.getElement();\n\t\t\tElement el2 = loaded.poll();\n\t\t\tassertNotNull(el2);\n\t\t\tassertEquals(el1.getAttributeStaticStr(\"id\"), el2.getAttributeStaticStr(\"id\"));\n\t\t\tassertEquals(el1.getChildStaticStr(\"body\"), el2.getChildStaticStr(\"body\"));\n\t\t}\n\n\t\trepo.deleteMessage(getMsgId(msgIds.remove(0)));\n\n\t\tloaded = repo.loadMessagesToJID(msgIds.subList(0, 3), recipientSession, false, null);\n\t\tfor (Packet message : messages.subList(1, 4)) {\n\t\t\tElement el1 = message.getElement();\n\t\t\tElement el2 = loaded.poll();\n\t\t\tassertNotNull(el2);\n\t\t\tassertEquals(el1.getAttributeStaticStr(\"id\"), el2.getAttributeStaticStr(\"id\"));\n\t\t\tassertEquals(el1.getChildStaticStr(\"body\"), el2.getChildStaticStr(\"body\"));\n\t\t}\n\n\t\tcount = repo.getMessagesCount(recipient);\n\t\tassertEquals(1, count.size());\n\t\tassertNotNull(count.get(MsgRepository.MSG_TYPES.message));\n\t\tassertEquals(4, count.get(MsgRepository.MSG_TYPES.message).longValue());\n\n\t\trepo.loadMessagesToJID(recipientSession, true);\n\t\tcount = repo.getMessagesCount(recipient);\n\t\tassertEquals(0, count.size());\n\t}\n\n\t@Test\n\tpublic void testStorageOfOfflineMessageWithExpiration1()\n\t\t\tthrows TigaseDBException, NotAuthorizedException, TigaseStringprepException {\n\t\ttry {\n\t\t\tDate expire = new Date(System.currentTimeMillis() - 60 * 1000);\n\n\t\t\tList<Packet> messages = new ArrayList<>();\n\t\t\tfor (int i = 0; i < 5; i++) {\n\t\t\t\tPacket message = Message.getMessage(sender, recipient, StanzaType.chat, generateRandomBody(), null,\n\t\t\t\t\t\t\t\t\t\t\t\t\tnull, UUID.randomUUID().toString());\n\t\t\t\tassertTrue(repo.storeMessage(sender, recipient, expire, message.getElement(), null));\n\t\t\t\tmessages.add(message);\n\t\t\t}\n\n\t\t\tMap<Enum, Long> count = repo.getMessagesCount(recipient);\n\t\t\tassertEquals(1, count.size());\n\t\t\tassertNotNull(count.get(MsgRepository.MSG_TYPES.message));\n\t\t\tassertEquals(5, count.get(MsgRepository.MSG_TYPES.message).longValue());\n\n\t\t\tElement elem = repo.getMessageExpired(System.currentTimeMillis(), true);\n\t\t\tassertNotNull(elem);\n\n\t\t\tcount = repo.getMessagesCount(recipient);\n\t\t\tassertEquals(1, count.size());\n\t\t\tassertNotNull(count.get(MsgRepository.MSG_TYPES.message));\n\t\t\tassertEquals(4, count.get(MsgRepository.MSG_TYPES.message).longValue());\n\n\t\t\trepo.expiredQueue.clear();\n\t\t\trepo.earliestOffline = Long.MAX_VALUE;\n\n\t\t\telem = repo.getMessageExpired(System.currentTimeMillis(), true);\n\t\t\tassertNotNull(elem);\n\t\t\tcount = repo.getMessagesCount(recipient);\n\t\t\tassertEquals(1, count.size());\n\t\t\tassertNotNull(count.get(MsgRepository.MSG_TYPES.message));\n\t\t\tassertEquals(3, count.get(MsgRepository.MSG_TYPES.message).longValue());\n\n\t\t\trepo.earliestOffline = Long.MAX_VALUE;\n\n\t\t\telem = repo.getMessageExpired(System.currentTimeMillis(), true);\n\t\t\tassertNotNull(elem);\n\t\t\tcount = repo.getMessagesCount(recipient);\n\t\t\tassertEquals(1, count.size());\n\t\t\tassertNotNull(count.get(MsgRepository.MSG_TYPES.message));\n\t\t\tassertEquals(2, count.get(MsgRepository.MSG_TYPES.message).longValue());\n\t\t} catch (Exception ex) {\n\t\t\tex.printStackTrace();\n\t\t\tthrow ex;\n\t\t}\n\t}\n\t\n\tprotected abstract <T> T getMsgId(String msgIdStr);\n\n\t@Override\n\tprotected Class getDataSourceAwareIfc() {\n\t\treturn MsgRepositoryIfc.class;\n\t}\n\n\t@Override\n\tprotected MsgRepository prepareDataSourceAware() throws Exception {\n\t\tMsgRepository repository =  super.prepareDataSourceAware();\n\t\tReentrantLock lock = new ReentrantLock();\n\t\trepository.setCondition(lock, lock.newCondition());\n\t\treturn repository;\n\t}\n\n\tprotected String generateRandomBody() {\n\t\tString body = \"Body \" + UUID.randomUUID().toString();\n\t\tif (checkEmoji) {\n\t\t\tbody += emoji;\n\t\t}\n\t\treturn body;\n\t}\n\t\n}\n"
  },
  {
    "path": "src/test/java/tigase/server/amp/db/JDBCMsgBroadcastRepositoryTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp.db;\n\nimport org.junit.Assume;\nimport org.junit.BeforeClass;\nimport org.junit.ClassRule;\nimport org.junit.rules.TestRule;\nimport org.junit.runner.Description;\nimport org.junit.runners.model.Statement;\nimport tigase.db.DBInitException;\nimport tigase.db.DataRepository;\nimport tigase.db.Schema;\n\nimport java.util.Collections;\n\n/**\n * Created by andrzej on 24.03.2017.\n */\npublic class JDBCMsgBroadcastRepositoryTest\n\t\textends AbstractMsgBroadcastRepositoryTest<DataRepository> {\n\n\t@ClassRule\n\tpublic static TestRule rule = new TestRule() {\n\t\t@Override\n\t\tpublic Statement apply(Statement stmnt, Description d) {\n\t\t\tif (uri == null || !uri.startsWith(\"jdbc:\")) {\n\t\t\t\treturn new Statement() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void evaluate() throws Throwable {\n\t\t\t\t\t\tAssume.assumeTrue(\"Ignored due to not passed DB URI!\", false);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn stmnt;\n\t\t}\n\t};\n\n\t@BeforeClass\n\tpublic static void loadSchema() throws DBInitException {\n\t\tloadSchema(Schema.SERVER_SCHEMA_ID, \"8.0.0\", Collections.emptySet());\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/server/amp/db/JDBCMsgRepositoryTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.amp.db;\n\nimport org.junit.Assume;\nimport org.junit.BeforeClass;\nimport org.junit.ClassRule;\nimport org.junit.rules.TestRule;\nimport org.junit.runner.Description;\nimport org.junit.runners.model.Statement;\nimport tigase.db.DBInitException;\nimport tigase.db.DataRepository;\nimport tigase.db.Schema;\n\nimport java.util.Collections;\n\n/**\n * Created by andrzej on 22.03.2017.\n */\npublic class JDBCMsgRepositoryTest\n\t\textends AbstractMsgRepositoryTest<DataRepository, Long> {\n\n\t@ClassRule\n\tpublic static TestRule rule = new TestRule() {\n\t\t@Override\n\t\tpublic Statement apply(Statement stmnt, Description d) {\n\t\t\tif (uri == null || !uri.startsWith(\"jdbc:\")) {\n\t\t\t\treturn new Statement() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void evaluate() throws Throwable {\n\t\t\t\t\t\tAssume.assumeTrue(\"Ignored due to not passed DB URI!\", false);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn stmnt;\n\t\t}\n\t};\n\t\n\t@BeforeClass\n\tpublic static void loadSchema() throws DBInitException {\n\t\tloadSchema(Schema.SERVER_SCHEMA_ID, \"8.5.0\", Collections.emptySet());\n\t}\n\n\t@Override\n\tprotected Long getMsgId(String msgIdStr) {\n\t\tif (msgIdStr == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn Long.parseLong(msgIdStr);\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/server/bosh/BoshIOServiceTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.bosh;\n\nimport junit.framework.TestCase;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\n\n\npublic class BoshIOServiceTest extends TestCase {\n    private BoshIOService boshIOService;\n\n    @Before\n    public void setUp() throws Exception {\n        this.boshIOService = new BoshIOService(null);\n\n        //System.setProperty(\"file.encoding\", \"UTF-8\");\n        /*\n        // to mod the test jvm default encoding\n        System.setProperty(\"file.encoding\", \"GBK\");\n        Field charset = Charset.class.getDeclaredField(\"defaultCharset\");\n        charset.setAccessible(true);\n        charset.set(null, null);\n        */\n    }\n\n    @Test\n    public void testGetCharset() {\n        String c1 = \"text/xml; charset=utf-8\";\n        assertEquals(\"utf-8\", this.boshIOService.getCharset(c1));\n        String c2 = \"text/xml; charset=\";\n        assertEquals(\"\", this.boshIOService.getCharset(c2));\n        String c3 = \"text/xml; charset=badEncoding\";\n        assertEquals(\"badEncoding\", this.boshIOService.getCharset(c3));\n        String c4 = \"text/xml; xxx=yy\";\n        assertEquals(null, this.boshIOService.getCharset(c4));\n        String c5 = null;\n        assertEquals(null, this.boshIOService.getCharset(c5));\n        String c6 = \"  \";\n        assertEquals(null, this.boshIOService.getCharset(c6));\n        String c7 = \" ; \";\n        assertEquals(null, this.boshIOService.getCharset(c7));\n\n    }\n\n    @Test\n    public void testGetDataLength() {\n        String data = \"Information:javac 11.0.5\";\n        assertEquals(this.boshIOService.getDataLength(data, \"text/xml; charset=utf-8\"), 24);\n        assertEquals(this.boshIOService.getDataLength(data, \"text/xml; charset=GBK\"), 24);\n        assertEquals(this.boshIOService.getDataLength(data, \"text/xml; charset=badEncoding\"), 24);\n        assertEquals(this.boshIOService.getDataLength(data, \"text/xml; charset=\"), 24);\n        assertEquals(this.boshIOService.getDataLength(data, \"text/xml; xxx=yy\"), 24);\n\n        String chineseData = data + \"中文字符\";\n\n        int charLength = 24 + 4;\n        assertTrue(this.boshIOService.getDataLength(chineseData, \"text/xml; charset=utf-8\") != charLength);\n        assertTrue(this.boshIOService.getDataLength(chineseData, \"text/xml; charset=utf-8\") == (charLength + 8));\n\n        assertTrue(this.boshIOService.getDataLength(chineseData, \"text/xml; charset=GBK\") != (charLength + 8));\n        assertTrue(this.boshIOService.getDataLength(chineseData, \"text/xml; charset=GBK\") == (charLength + 4));\n\n        assertTrue(this.boshIOService.getDataLength(chineseData, \"text/xml; charset=ISO-8859-1\") == charLength);\n\n        int dataLength = this.boshIOService.getDataLength(chineseData, \"text/xml;\");\n\n        assertTrue(Charset.defaultCharset().equals(StandardCharsets.UTF_8) ?\n                dataLength == (charLength + 8) :\n                (Charset.defaultCharset().equals(Charset.forName(\"GBK\")) ?\n                        dataLength == (charLength + 4) :\n                        dataLength == charLength));\n\n    }\n}"
  },
  {
    "path": "src/test/java/tigase/server/rtbl/RTBLRepositoryTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.rtbl;\n\nimport org.junit.Test;\nimport tigase.db.TigaseDBException;\nimport tigase.eventbus.impl.EventBusImplementation;\nimport tigase.kernel.AbstractKernelWithUserRepositoryTestCase;\nimport tigase.kernel.core.Kernel;\nimport tigase.util.Algorithms;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.lang.reflect.Field;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.UUID;\nimport java.util.concurrent.TimeUnit;\nimport java.util.function.Supplier;\n\nimport static junit.framework.TestCase.*;\n\npublic class RTBLRepositoryTest extends AbstractKernelWithUserRepositoryTestCase {\n\n\t@Override\n\tprotected void registerBeans(Kernel kernel) {\n\t\tsuper.registerBeans(kernel);\n\t\tkernel.registerBean(\"eventBus\").asInstance(new EventBusImplementation()).exec();\n\t\tkernel.registerBean(RTBLRepository.class).exec();\n\t}\n\n\tprivate BareJID pubsubJid = BareJID.bareJIDInstanceNS(\"test@localhost\");\n\tprivate String node = \"test\";\n\n\tprivate String hash = \"SHA-256\";\n\n\tprivate static final int limit = 30;\n\n\t@Test\n\tpublic void test() throws TigaseDBException, NoSuchFieldException, IllegalAccessException, InterruptedException {\n\t\tRTBLRepository repository = getInstance(RTBLRepository.class);\n\n\t\tassertEquals(0, repository.getBlockLists().size());\n\n\t\trepository.add(pubsubJid, node, hash);\n\n\t\twaitUntil(() -> repository.getBlockLists().size() != 2);\n\t\tassertEquals(1, repository.getBlockLists().size());\n\n\t\tBareJID user = BareJID.bareJIDInstanceNS(UUID.randomUUID().toString(), \"domain\");\n\t\trepository.update(pubsubJid, node, RTBLRepository.Action.add, Algorithms.sha256(user.toString()));\n\n\t\twaitUntil(() -> !repository.isBlocked(user));\n\t\tassertTrue(repository.isBlocked(user));\n\n\t\tField f = RTBLRepository.class.getDeclaredField(\"cache\");\n\t\tf.setAccessible(true);\n\t\tMap cache = (Map) f.get(repository);\n\t\tcache.clear();\n\n\t\tassertEquals(0, repository.getBlockLists().size());\n\t\tassertFalse(repository.isBlocked(user));\n\n\t\trepository.reload();\n\t\tassertEquals(1, repository.getBlockLists().size());\n\t\tRTBL rtbl = repository.getBlockList(pubsubJid, node);\n\t\tassertNotNull(rtbl);\n\t\tassertEquals(hash, rtbl.getHash());\n\t\tassertTrue(repository.isBlocked(user));\n\n\t\tBareJID user2 = BareJID.bareJIDInstanceNS(UUID.randomUUID().toString(), \"domain\");\n\t\trtbl = new RTBL(rtbl.getKey(), hash, Set.of(Algorithms.sha256(user.toString()), Algorithms.sha256(user2.toString())));\n\t\trepository.update(rtbl);\n\n\t\twaitUntil(() -> !repository.isBlocked(user));\n\t\tassertTrue(repository.isBlocked(user));\n\t\tassertTrue(repository.isBlocked(user2));\n\n\t\trtbl = new RTBL(rtbl.getKey(), hash, Set.of(Algorithms.sha256(user2.toString())));\n\t\trepository.update(rtbl);\n\n\t\twaitUntil(() -> !repository.isBlocked(user2));\n\t\tassertFalse(repository.isBlocked(user));\n\t\tassertTrue(repository.isBlocked(user2));\n\t}\n\n\tprivate void waitUntil(Supplier<Boolean> function) throws InterruptedException {\n\t\tint i = 0;\n\t\twhile (i < limit && function.get()) {\n\t\t\tTimeUnit.MILLISECONDS.sleep(10);\n\t\t\ti++;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/server/rtbl/RTBLTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.rtbl;\n\nimport org.junit.Test;\nimport tigase.util.Algorithms;\nimport tigase.xmpp.jid.BareJID;\n\nimport java.util.Collections;\nimport java.util.UUID;\n\nimport static junit.framework.TestCase.assertTrue;\nimport static junit.framework.TestCase.assertEquals;\nimport static org.junit.Assert.assertFalse;\n\npublic class RTBLTest {\n\n\tprivate BareJID pubsubJid = BareJID.bareJIDInstanceNS(\"test@localhost\");\n\tprivate String node = \"test\";\n\n\tprivate String hash = \"SHA-256\";\n\n\t@Test\n\tpublic void testCreation() {\n\t\tRTBL rtbl = new RTBL(pubsubJid, node, hash, Collections.emptySet());\n\t\tassertEquals(pubsubJid, rtbl.getJID());\n\t\tassertEquals(node, rtbl.getNode());\n\t\tassertEquals(hash, rtbl.getHash());\n\t\tassertEquals(0, rtbl.getBlocked().size());\n\t}\n\n\t@Test\n\tpublic void testUserBlocking() {\n\t\tRTBL rtbl = new RTBL(pubsubJid, node, hash, Collections.emptySet());\n\t\tBareJID user1 = BareJID.bareJIDInstanceNS(UUID.randomUUID().toString(), \"domain-1\");\n\t\tBareJID user2 = BareJID.bareJIDInstanceNS(UUID.randomUUID().toString(), \"domain-1\");\n\t\tBareJID user3 = BareJID.bareJIDInstanceNS(UUID.randomUUID().toString(), \"domain-2\");\n\t\tString userHash = Algorithms.sha256(user1.toString());\n\t\trtbl.getBlocked().add(userHash);\n\n\t\tassertTrue(rtbl.isBlocked(user1));\n\t\tassertFalse(rtbl.isBlocked(user2));\n\t\tassertFalse(rtbl.isBlocked(user3));\n\t}\n\n\t@Test\n\tpublic void testDomainBlocking() {\n\t\tRTBL rtbl = new RTBL(pubsubJid, node, hash, Collections.emptySet());\n\t\tBareJID user1 = BareJID.bareJIDInstanceNS(UUID.randomUUID().toString(), \"domain-1\");\n\t\tBareJID user2 = BareJID.bareJIDInstanceNS(UUID.randomUUID().toString(), \"domain-1\");\n\t\tBareJID user3 = BareJID.bareJIDInstanceNS(UUID.randomUUID().toString(), \"domain-2\");\n\t\tString domainHash = Algorithms.sha256(user1.getDomain().toString());\n\t\trtbl.getBlocked().add(domainHash);\n\n\t\tassertTrue(rtbl.isBlocked(user1));\n\t\tassertTrue(rtbl.isBlocked(user1));\n\t\tassertFalse(rtbl.isBlocked(user3));\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/server/websocket/WebSocketHixie76Test.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.websocket;\n\nimport junit.framework.TestCase;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author andrzej\n */\npublic class WebSocketHixie76Test\n\t\textends TestCase {\n\n\tprivate WebSocketHixie76 impl;\n\n\t@Test\n\tpublic void testFrameEncodingDecoding() throws IOException {\n\t\tString input = \"<test-data><subdata/></test-data>\";\n\t\tByteBuffer buf = ByteBuffer.wrap(input.getBytes());\n\t\tfinal ByteBuffer tmp = ByteBuffer.allocate(1024);\n\t\tWebSocketXMPPIOService<Object> io = new WebSocketXMPPIOService<Object>(\n\t\t\t\tnew WebSocketProtocolIfc[]{new WebSocketHixie76()}) {\n\n\t\t\t@Override\n\t\t\tprotected void writeBytes(ByteBuffer data) {\n\t\t\t\ttmp.put(data);\n\t\t\t}\n\n\t\t};\n\t\timpl.encodeFrameAndWrite(io, buf);\n\t\ttmp.flip();\n\t\tByteBuffer decoded = impl.decodeFrame(io, tmp);\n\t\tAssert.assertArrayEquals(\"Data before encoding do not match data after decoding\", input.getBytes(),\n\t\t\t\t\t\t\t\t decoded.array());\n\t}\n\n\t@Test\n\tpublic void testHandshakeOK() throws NoSuchAlgorithmException, IOException {\n\t\tfinal ByteBuffer tmp = ByteBuffer.allocate(2048);\n\t\tWebSocketXMPPIOService<Object> io = new WebSocketXMPPIOService<Object>(\n\t\t\t\tnew WebSocketProtocolIfc[]{new WebSocketHixie76()}) {\n\n\t\t\t@Override\n\t\t\tpublic int getLocalPort() {\n\t\t\t\treturn 80;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void writeBytes(ByteBuffer data) {\n\t\t\t\ttmp.put(data);\n\t\t\t}\n\n\t\t};\n\t\tMap<String, String> params = new HashMap<String, String>();\n\t\tparams.put(\"Sec-WebSocket-Key1\".toUpperCase(), \"1C2J899_05  6  !  M 9    ^4\");\n\t\tparams.put(\"Sec-WebSocket-Key2\".toUpperCase(), \"23 2ff0M_E0#.454X23\");\n\t\tparams.put(\"Sec-WebSocket-Protocol\".toUpperCase(), \"xmpp\");\n\t\tbyte[] bytes = new byte[10];\n\t\tbytes[0] = '\\r';\n\t\tbytes[1] = '\\n';\n\t\tAssert.assertTrue(\"Handshake failed\", impl.handshake(io, params, bytes));\n\t}\n\n\t@Test\n\tpublic void testHandshakeFail() throws NoSuchAlgorithmException, IOException {\n\t\tfinal ByteBuffer tmp = ByteBuffer.allocate(2048);\n\t\tWebSocketXMPPIOService<Object> io = new WebSocketXMPPIOService<Object>(\n\t\t\t\tnew WebSocketProtocolIfc[]{new WebSocketHixie76()}) {\n\n\t\t\t@Override\n\t\t\tpublic int getLocalPort() {\n\t\t\t\treturn 80;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void writeBytes(ByteBuffer data) {\n\t\t\t\ttmp.put(data);\n\t\t\t}\n\n\t\t};\n\t\tMap<String, String> params = new HashMap<String, String>();\n\t\tparams.put(\"Sec-WebSocket-Version\".toUpperCase(), \"13\");\n\t\tparams.put(\"Sec-WebSocket-Protocol\".toUpperCase(), \"xmpp\");\n\t\tbyte[] bytes = new byte[10];\n\t\tbytes[0] = '\\r';\n\t\tbytes[1] = '\\n';\n\t\tAssert.assertFalse(\"Handshake succeeded\", impl.handshake(io, params, bytes));\n\t}\n\n\t@Override\n\tprotected void setUp() throws Exception {\n\t\timpl = new WebSocketHixie76();\n\t}\n\n\t@Override\n\tprotected void tearDown() throws Exception {\n\t\timpl = null;\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/server/websocket/WebSocketHybiTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.websocket;\n\nimport junit.framework.TestCase;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport tigase.util.Base64;\n\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.charset.Charset;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\n\n/**\n * @author andrzej\n */\npublic class WebSocketHybiTest\n\t\textends TestCase {\n\n\tprivate WebSocketHybi impl;\n\n\t@Test\n\tpublic void testCalculateWsAcceptKey() throws Exception {\n\t\t// From RFC 6455\n\t\tassertEquals(\"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\", WebSocketHybi.calculateWsAcceptKey(\"dGhlIHNhbXBsZSBub25jZQ==\"));\n\n\t\t// Responses generated by echo.websocket.org\n\t\tassertEquals(\"3YUhy9uOn3SwgBgA9kDld2AH2r8=\", WebSocketHybi.calculateWsAcceptKey(\"Ox0pU/Y2qstxzCqKHwtJcQ==\"));\n\t\tassertEquals(\"4vvUf3Izc4cQeL8fr4oZX5qxxFs=\", WebSocketHybi.calculateWsAcceptKey(\"tsmEcS/j84lYkxw4SyfCYg==\"));\n\t}\n\n\t@Test\n\tpublic void testFrameEncodingDecoding() throws IOException {\n\t\tString input = \"<test-data><subdata/></test-data>\";\n\t\tByteBuffer buf = ByteBuffer.wrap(input.getBytes());\n\t\tfinal ByteBuffer tmp = ByteBuffer.allocate(1024);\n\t\tWebSocketXMPPIOService<Object> io = new WebSocketXMPPIOService<Object>(\n\t\t\t\tnew WebSocketProtocolIfc[]{new WebSocketHybi()}) {\n\n\t\t\t@Override\n\t\t\tprotected void writeBytes(ByteBuffer data) {\n\t\t\t\ttmp.put(data);\n\t\t\t}\n\n\t\t};\n\t\tio.maskingKey = new byte[4];\n\t\timpl.encodeFrameAndWrite(io, buf);\n\t\ttmp.flip();\n\t\tByteBuffer tmp1 = maskFrame(tmp);\n\t\tByteBuffer decoded = impl.decodeFrame(io, tmp1);\n\t\tAssert.assertArrayEquals(\"Data before encoding do not match data after decoding\", input.getBytes(),\n\t\t\t\t\t\t\t\t decoded.array());\n\t}\n\n\t@Test\n\tpublic void testHandshakeFail() throws NoSuchAlgorithmException, IOException {\n\t\tfinal ByteBuffer tmp = ByteBuffer.allocate(2048);\n\t\tWebSocketXMPPIOService<Object> io = new WebSocketXMPPIOService<Object>(\n\t\t\t\tnew WebSocketProtocolIfc[]{new WebSocketHybi()}) {\n\n\t\t\t@Override\n\t\t\tpublic int getLocalPort() {\n\t\t\t\treturn 80;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void writeBytes(ByteBuffer data) {\n\t\t\t\ttmp.put(data);\n\t\t\t}\n\n\t\t};\n\t\tMap<String, String> params = new HashMap<String, String>();\n\t\tparams.put(\"Sec-WebSocket-Key1\".toUpperCase(), \"1C2J899_05  6  !  M 9    ^4\");\n\t\tparams.put(\"Sec-WebSocket-Key2\".toUpperCase(), \"23 2ff0M_E0#.454X23\");\n\t\tparams.put(\"Sec-WebSocket-Protocol\".toUpperCase(), \"xmpp\");\n\t\tbyte[] bytes = new byte[10];\n\t\tbytes[0] = '\\r';\n\t\tbytes[1] = '\\n';\n\t\tAssert.assertFalse(\"Handshake succeeded\", impl.handshake(io, params, bytes));\n\t}\n\n\t@Test\n\tpublic void testHandshakeOK() throws NoSuchAlgorithmException, IOException {\n\t\tfinal ByteBuffer tmp = ByteBuffer.allocate(2048);\n\t\tfinal StringBuilder sb = new StringBuilder();\n\t\tWebSocketXMPPIOService<Object> io = new WebSocketXMPPIOService<Object>(\n\t\t\t\tnew WebSocketProtocolIfc[]{new WebSocketHybi()}) {\n\n\t\t\t@Override\n\t\t\tpublic int getLocalPort() {\n\t\t\t\treturn 80;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void writeBytes(ByteBuffer data) {\n\t\t\t\ttmp.put(data);\n\t\t\t}\n\n\t\t\tprotected void writeData(String data) {\n\t\t\t\tsb.append(data);\n\t\t\t}\n\n\t\t};\n\t\tMap<String, String> params = new HashMap<String, String>();\n\t\tparams.put(\"Sec-WebSocket-Version\".toUpperCase(), \"13\");\n\t\tparams.put(\"Sec-WebSocket-Key\".toUpperCase(), \"some random data as a key\");\n\t\tparams.put(\"Sec-WebSocket-Protocol\".toUpperCase(), \"xmpp\");\n\t\tbyte[] bytes = new byte[10];\n\t\tbytes[0] = '\\r';\n\t\tbytes[1] = '\\n';\n\t\tAssert.assertTrue(\"Handshake failed\", impl.handshake(io, params, bytes));\n\t\ttmp.flip();\n\t\tbyte[] read = new byte[tmp.remaining()];\n//\t\tArrays.stream(sb.toString().split(\"\\n\")).forEach(System.out::println);\n\t\tOptional<String> secWebSocketAccept = Arrays.stream(sb.toString().split(\"\\n\"))\n\t\t\t\t.map(line -> line.split(\":\"))\n\t\t\t\t.filter(line -> \"Sec-WebSocket-Accept\".equalsIgnoreCase(line[0].trim()))\n\t\t\t\t.map(line -> line[1])\n\t\t\t\t.map(String::trim)\n\t\t\t\t.findFirst();\n\n\t\tString expSecWebSocketAccept = Base64.encode(MessageDigest.getInstance(\"SHA-1\")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t .digest((\"some random data as a key\" +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t \"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\").getBytes(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t Charset.forName(\"UTF-8\"))));\n\t\tassertEquals(expSecWebSocketAccept, secWebSocketAccept.get());\n\t}\n\n\t@Test\n\tpublic void testTwoWebSocketTextFramesInSingleTcpFrame() throws Exception {\n\t\tString input1 = \"<test-data><subdata/></test-data>\";\n\t\tString input2 = \"<test2/>\";\n\t\tByteBuffer frame1 = generateIncomingFrame(input1);\n\t\tByteBuffer frame2 = generateIncomingFrame(input2);\n\n\t\tByteBuffer tmp = ByteBuffer.allocate(frame1.remaining() + frame2.remaining());\n\t\ttmp.put(frame1);\n\t\ttmp.put(frame2);\n\t\ttmp.flip();\n\n\t\tWebSocketXMPPIOService<Object> io = new WebSocketXMPPIOService<Object>(\n\t\t\t\tnew WebSocketProtocolIfc[]{new WebSocketHybi()});\n\t\tio.maskingKey = new byte[4];\n\t\tByteBuffer decoded = impl.decodeFrame(io, tmp);\n\t\tAssert.assertArrayEquals(\"Data of first frame before encoding do not match data after decoding\",\n\t\t\t\t\t\t\t\t input1.getBytes(), decoded.array());\n\t\tdecoded = impl.decodeFrame(io, tmp);\n\t\tAssert.assertArrayEquals(\"Data of second frame before encoding do not match data after decoding\",\n\t\t\t\t\t\t\t\t input2.getBytes(), decoded.array());\n\t}\n\n\t@Test\n\tpublic void testTwoWebSocketFramesPingAndTextFrameInSingleTcpFrame() throws Exception {\n\t\tString input2 = \"<test-data><subdata/></test-data>\";\n\t\tByteBuffer frame1 = ByteBuffer.allocate(20);\n\t\tframe1.put((byte) 0x89);\n\t\tframe1.put((byte) 0x04);\n\t\tframe1.put(new byte[]{0x00, 0x00, 0x00, 0x00});\n\t\tframe1.flip();\n\t\tframe1 = maskFrame(frame1);\n\t\tByteBuffer frame2 = generateIncomingFrame(input2);\n\n\t\tByteBuffer tmp = ByteBuffer.allocate(frame1.remaining() + frame2.remaining());\n\t\ttmp.put(frame1);\n\t\ttmp.put(frame2);\n\t\ttmp.flip();\n\n\t\tByteBuffer tmp2 = ByteBuffer.allocate(1024);\n\t\tWebSocketXMPPIOService<Object> io = new WebSocketXMPPIOService<Object>(\n\t\t\t\tnew WebSocketProtocolIfc[]{new WebSocketHybi()}) {\n\t\t\t@Override\n\t\t\tprotected void writeBytes(ByteBuffer data) {\n\t\t\t\ttmp2.put(data);\n\t\t\t}\n\t\t};\n\t\tio.maskingKey = new byte[4];\n\t\tByteBuffer decoded = impl.decodeFrame(io, tmp);\n\t\tAssert.assertNotNull(decoded);\n\t\tAssert.assertArrayEquals(\"Data of first frame before encoding do not match data after decoding\", new byte[0],\n\t\t\t\t\t\t\t\t decoded.array());\n\t\ttmp2.flip();\n\t\tAssert.assertNotEquals(\"PONG frame not sent!\", 0, tmp2.remaining());\n\t\tassertEquals(\"PONG frame not sent!\", (byte) 0x8A, tmp2.get(0));\n\n\t\tdecoded = impl.decodeFrame(io, tmp);\n\t\tAssert.assertArrayEquals(\"Data of second frame before encoding do not match data after decoding\",\n\t\t\t\t\t\t\t\t input2.getBytes(), decoded.array());\n\n\t}\n\n\t@Override\n\tprotected void setUp() throws Exception {\n\t\timpl = new WebSocketHybi();\n\t}\n\n\t@Override\n\tprotected void tearDown() throws Exception {\n\t\timpl = null;\n\t}\n\n\tprivate ByteBuffer maskFrame(ByteBuffer data) {\n\t\tByteBuffer tmp = ByteBuffer.allocate(1024);\n\t\tbyte[] header = new byte[2];\n\t\tdata.get(header);\n\t\theader[header.length - 1] = (byte) (header[header.length - 1] | 0x80);\n\t\ttmp.put(header);\n\t\tbyte[] mask = {0x00, 0x00, 0x00, 0x00};\n\t\ttmp.put(mask);\n\t\tbyte b;\n\t\twhile (data.hasRemaining()) {\n\t\t\tb = data.get();\n\t\t\tb = (byte) (b ^ 0x00);\n\t\t\ttmp.put(b);\n\t\t}\n\t\ttmp.flip();\n\t\treturn tmp;\n\t}\n\n\tprivate ByteBuffer generateIncomingFrame(String input) throws IOException {\n\t\tByteBuffer buf = ByteBuffer.wrap(input.getBytes());\n\t\tfinal ByteBuffer tmp = ByteBuffer.allocate(1024);\n\t\tWebSocketXMPPIOService<Object> io = new WebSocketXMPPIOService<Object>(\n\t\t\t\tnew WebSocketProtocolIfc[]{new WebSocketHybi()}) {\n\n\t\t\t@Override\n\t\t\tprotected void writeBytes(ByteBuffer data) {\n\t\t\t\ttmp.put(data);\n\t\t\t}\n\n\t\t};\n\t\tio.maskingKey = new byte[4];\n\t\timpl.encodeFrameAndWrite(io, buf);\n\t\ttmp.flip();\n\t\treturn maskFrame(tmp);\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/server/websocket/WebSocketXMPPIOServiceTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.websocket;\n\nimport junit.framework.TestCase;\nimport org.junit.Test;\n\nimport java.io.UnsupportedEncodingException;\nimport java.util.*;\n\n/**\n * Created by andrzej on 18.07.2016.\n */\npublic class WebSocketXMPPIOServiceTest\n\t\textends TestCase {\n\n\tprivate HashMap<String, String> headers;\n\tprivate WebSocketXMPPIOService service;\n\n\t@Test\n\tpublic void testHttpHeadersParsingWithSpaces() throws UnsupportedEncodingException {\n\t\tbyte[] data = prepareHTTPRequest(headers, true);\n\n\t\tMap<String, String> parsedHeaders = new HashMap<>();\n\t\tservice.parseHttpHeaders(data, parsedHeaders);\n\n\t\tassertMaps(prepareExpectedParsedHeaders(headers), parsedHeaders);\n\t}\n\n\t@Test\n\tpublic void testHttpHeadersParsingWithoutSpaces() throws UnsupportedEncodingException {\n\t\tbyte[] data = prepareHTTPRequest(headers, false);\n\n\t\tMap<String, String> parsedHeaders = new HashMap<>();\n\t\tservice.parseHttpHeaders(data, parsedHeaders);\n\n\t\tassertMaps(prepareExpectedParsedHeaders(headers), parsedHeaders);\n\t}\n\n\t@Override\n\tprotected void setUp() throws Exception {\n\t\tservice = new WebSocketXMPPIOService(new WebSocketProtocolIfc[0]);\n\t\theaders = new HashMap<String, String>();\n\t\theaders.put(\"Connection\", \"Upgrade\");\n\t\theaders.put(\"Host\", \"test.example.com:5291\");\n\t\theaders.put(\"Origin\", \"test.example.com:5291\");\n\t\theaders.put(\"Sec-WebSocket-Key\", \"JRqGsrthbnle6zl8sFQPpQ==\");\n\t\theaders.put(\"Sec-WebSocket-Protocol\", \"xmpp\");\n\t\theaders.put(\"Sec-WebSocket-Version\", \"13\");\n\t\theaders.put(\"Upgrade\", \"websocket\");\n\t}\n\n\t@Override\n\tprotected void tearDown() throws Exception {\n\t\tservice = null;\n\t}\n\n\tprivate void assertMaps(Map<String, String> expected, Map<String, String> actual) {\n\t\tList<String> expectedKeys = new ArrayList<>(expected.keySet());\n\t\tList<String> actualKeys = new ArrayList<>(actual.keySet());\n\n\t\tCollections.sort(expectedKeys);\n\t\tCollections.sort(actualKeys);\n\n\t\tassertEquals(expectedKeys, actualKeys);\n\n\t\tfor (String key : expectedKeys) {\n\t\t\tassertEquals(expected.get(key), actual.get(key));\n\t\t}\n\t}\n\n\tprivate byte[] prepareHTTPRequest(Map<String, String> headers, boolean useSpaces)\n\t\t\tthrows UnsupportedEncodingException {\n\t\tStringBuilder sb = new StringBuilder(\"GET HTTP/1.1\\r\\n\");\n\t\tfor (Map.Entry<String, String> e : headers.entrySet()) {\n\t\t\tsb.append(e.getKey());\n\t\t\tsb.append(':');\n\t\t\tif (useSpaces) {\n\t\t\t\tsb.append(' ');\n\t\t\t}\n\t\t\tsb.append(e.getValue()).append(\"\\r\\n\");\n\t\t}\n\t\tsb.append(\"\\r\\n\");\n\t\treturn sb.toString().getBytes(\"UTF-8\");\n\t}\n\n\tprivate Map<String, String> prepareExpectedParsedHeaders(Map<String, String> headers) {\n\t\tMap<String, String> result = new HashMap<>();\n\t\tfor (Map.Entry<String,String> e : headers.entrySet()) {\n\t\t\tresult.put(e.getKey().toUpperCase(), e.getValue());\n\t\t}\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/server/xmppclient/StreamManagementIOProcessorTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppclient;\n\nimport junit.framework.TestCase;\nimport org.junit.Test;\nimport tigase.TestLogger;\nimport tigase.server.Message;\nimport tigase.server.Packet;\nimport tigase.server.xmppclient.StreamManagementIOProcessor.OutQueue;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\n\nimport java.lang.reflect.Field;\nimport java.util.UUID;\nimport java.util.concurrent.atomic.AtomicLong;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Test class for StreamManagementIOProcessor class.\n * <br>\n * Currently tests if acking works correctly for overflow in OutQueue counter.\n *\n * @author andrzej\n */\npublic class StreamManagementIOProcessorTest\n\t\textends TestCase {\n\n\tprivate static final String[] DELAY_PATH = {Message.ELEM_NAME, \"delay\"};\n\tprivate static final String DELAY_XMLNS = \"urn:xmpp:delay\";\n\n\tprivate static final Logger log = TestLogger.getLogger(StreamManagementIOProcessorTest.class);\n\n\t@Test\n\tpublic void testValidateOutQueueOverflowToZero() {\n\t\tOutQueue queue = new OutQueue();\n\t\tint start = Integer.MAX_VALUE - 10;\n\t\tqueue.setCounter(start - 1);\n\n\t\tint packetToAck = start + 5;\n\n\t\tfor (int i = start; i <= Integer.MAX_VALUE && i > 0; i++) {\n\t\t\ttry {\n\t\t\t\tPacket p = Packet.packetInstance(new Element(\"message\", new String[]{\"id\", \"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t new String[]{String.valueOf(i), \"from@example.com\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"to@example.com\"}));\n\t\t\t\tqueue.append(p, Integer.MAX_VALUE);\n\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\tlog.log(Level.SEVERE, null, ex);\n\t\t\t}\n\t\t}\n\n\t\tqueue.ack(packetToAck);\n\n\t\tint size = queue.waitingForAck();\n\t\tassertEquals(5, size);\n\t\tassertEquals(queue.getQueue().peek().getPacketWithStamp().getElement().getAttributeStaticStr(\"id\"),\n\t\t\t\t\t String.valueOf(packetToAck + 1));\n\t\tqueue.getQueue()\n\t\t\t\t.forEach((OutQueue.Entry e) -> assertTrue(\n\t\t\t\t\t\te.getPacketWithStamp().isXMLNSStaticStr(DELAY_PATH, DELAY_XMLNS)));\n\t}\n\n\t@Test\n\tpublic void testValidateOutQueueOverflowOverZero() {\n\t\tOutQueue queue = new OutQueue();\n\t\tint start = Integer.MAX_VALUE - 5;\n\t\tqueue.setCounter(start - 1);\n\t\tint packetToAck = start + 3;\n\n\t\tfor (int i = start; i <= Integer.MAX_VALUE && i > 0; i++) {\n\t\t\ttry {\n\t\t\t\tPacket p = Packet.packetInstance(new Element(\"message\", new String[]{\"id\", \"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t new String[]{String.valueOf(i), \"from@example.com\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"to@example.com\"}));\n\t\t\t\tqueue.append(p, Integer.MAX_VALUE);\n\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\tlog.log(Level.SEVERE, null, ex);\n\t\t\t}\n\t\t}\n\n\t\tfor (int i = 0; i < 3; i++) {\n\t\t\ttry {\n\t\t\t\tPacket p = Packet.packetInstance(new Element(\"message\", new String[]{\"id\", \"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t new String[]{String.valueOf(i), \"from@example.com\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"to@example.com\"}));\n\t\t\t\tqueue.append(p, Integer.MAX_VALUE);\n\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\tlog.log(Level.SEVERE, null, ex);\n\t\t\t}\n\t\t}\n\n\t\tqueue.ack(packetToAck);\n\n\t\tint size = queue.waitingForAck();\n\t\tassertEquals(5, size);\n\t\tassertEquals(queue.getQueue().peek().getPacketWithStamp().getElement().getAttributeStaticStr(\"id\"),\n\t\t\t\t\t String.valueOf(packetToAck + 1));\n\t\tqueue.getQueue()\n\t\t\t\t.forEach((OutQueue.Entry e) -> assertTrue(\n\t\t\t\t\t\te.getPacketWithStamp().isXMLNSStaticStr(DELAY_PATH, DELAY_XMLNS)));\n\t}\n\n\t@Test\n\tpublic void testNoDelayForIq() {\n\t\tOutQueue queue = new OutQueue();\n\t\tint start = Integer.MAX_VALUE - 10;\n\t\tqueue.setCounter(start - 1);\n\n\t\tint packetToAck = start + 5;\n\n\t\tfor (int i = start; i <= Integer.MAX_VALUE && i > 0; i++) {\n\t\t\ttry {\n\t\t\t\tPacket p = Packet.packetInstance(new Element(\"iq\", new String[]{\"id\", \"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t new String[]{String.valueOf(i), \"from@example.com\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"to@example.com\"}));\n\t\t\t\tqueue.append(p, Integer.MAX_VALUE);\n\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\tlog.log(Level.SEVERE, null, ex);\n\t\t\t}\n\t\t}\n\n\t\tqueue.getQueue()\n\t\t\t\t.forEach((OutQueue.Entry e) -> assertFalse(\n\t\t\t\t\t\te.getPacketWithStamp().isXMLNSStaticStr(new String[]{\"iq\", \"delay\"}, DELAY_XMLNS)));\n\t}\n\n\t@Test\n\tpublic void testForceDelayForResumption() {\n\t\tOutQueue queue = new OutQueue();\n\n\t\ttry {\n\t\t\tPacket p = Packet.packetInstance(new Element(\"message\", new Element[]{\n\t\t\t\t\tnew Element(\"delay\", new String[]{\"xmlns\", \"stamp\"},\n\t\t\t\t\t\t\t\tnew String[]{DELAY_XMLNS, \"2015-07-10T00:00:00.000Z\"})},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t new String[]{\"id\", \"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t new String[]{String.valueOf(\"id-1\"), \"from@example.com\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"to@example.com\"}));\n\t\t\tqueue.append(p, Integer.MAX_VALUE);\n\t\t} catch (TigaseStringprepException ex) {\n\t\t\tlog.log(Level.SEVERE, null, ex);\n\t\t}\n\n\t\tqueue.getQueue()\n\t\t\t\t.forEach((OutQueue.Entry e) -> assertEquals(1, e.getPacketWithStamp()\n\t\t\t\t\t\t.getElement()\n\t\t\t\t\t\t.findChildren((Element el) -> el.getName() == \"delay\")\n\t\t\t\t\t\t.size()));\n\n\t\tqueue.getQueue().clear();\n\n\t\tqueue.setResumptionEnabled(true);\n\n\t\ttry {\n\t\t\tPacket p = Packet.packetInstance(new Element(\"message\", new Element[]{\n\t\t\t\t\tnew Element(\"delay\", new String[]{\"xmlns\", \"stamp\"},\n\t\t\t\t\t\t\t\tnew String[]{DELAY_XMLNS, \"2015-07-10T00:00:00.000Z\"})},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t new String[]{\"id\", \"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t new String[]{String.valueOf(\"id-1\"), \"from@example.com\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"to@example.com\"}));\n\t\t\tqueue.append(p, Integer.MAX_VALUE);\n\t\t} catch (TigaseStringprepException ex) {\n\t\t\tlog.log(Level.SEVERE, null, ex);\n\t\t}\n\n\t\tqueue.getQueue()\n\t\t\t\t.forEach((OutQueue.Entry e) -> assertEquals(1, e.getPacketWithStamp()\n\t\t\t\t\t\t.getElement()\n\t\t\t\t\t\t.findChildren((Element el) -> el.getName() == \"delay\")\n\t\t\t\t\t\t.size()));\n\n\t\tqueue.getQueue().clear();\n\n\t\ttry {\n\t\t\tPacket p = Packet.packetInstance(new Element(\"iq\", new String[]{\"id\", \"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t new String[]{String.valueOf(\"id-2\"), \"from@example.com\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  \"to@example.com\"}));\n\t\t\tqueue.append(p, Integer.MAX_VALUE);\n\t\t} catch (TigaseStringprepException ex) {\n\t\t\tlog.log(Level.SEVERE, null, ex);\n\t\t}\n\n\t\tqueue.getQueue()\n\t\t\t\t.forEach((OutQueue.Entry e) -> assertFalse(\n\t\t\t\t\t\te.getPacketWithStamp().isXMLNSStaticStr(new String[]{\"iq\", \"delay\"}, DELAY_XMLNS)));\n\t}\n\n\t@Test\n\tpublic void testShouldRequestAck()\n\t\t\tthrows NoSuchFieldException, IllegalAccessException, TigaseStringprepException, InterruptedException {\n\t\tStreamManagementIOProcessor processor = new StreamManagementIOProcessor();\n\t\t// settings request for ACK minimal delay to 20ms\n\t\tField ackRefMinDelayField = StreamManagementIOProcessor.class.getDeclaredField(\"ack_request_min_delay\");\n\t\tackRefMinDelayField.setAccessible(true);\n\t\tackRefMinDelayField.set(processor, 20l);\n\n\t\tOutQueue outQueue = new OutQueue();\n\n\t\tassertFalse(\"Requesting ACK if queue is empty\", processor.shouldRequestAck(null, outQueue));\n\t\tfor (int i = 0; i < 10; i++) {\n\t\t\tPacket p = Packet.packetInstance(\n\t\t\t\t\tnew Element(\"message\").withElement(\"body\", null, \"Test \" + UUID.randomUUID().toString()));\n\t\t\toutQueue.append(p, 2000, 2000);\n\t\t\tassertEquals(\"Only call after adding 10th message should request ACK\", i == 9, processor.shouldRequestAck(null, outQueue));\n\t\t}\n\t\toutQueue.sendingRequest();\n\t\tassertFalse(\"ACK shouldn't be requested just after sending one!\", processor.shouldRequestAck(null, outQueue));\n\t\tfor (int i = 0; i < 10; i++) {\n\t\t\tPacket p = Packet.packetInstance(\n\t\t\t\t\tnew Element(\"message\").withElement(\"body\", null, \"Test \" + UUID.randomUUID().toString()));\n\t\t\toutQueue.append(p, 2000, 2000);\n\t\t\tassertFalse(\"ACK shouldn't be requested for 20ms after sending request for ACK\", processor.shouldRequestAck(null, outQueue));\n\t\t}\n\t\tThread.sleep(21);\n\t\tassertTrue(\"Request for ACK should be sent as over 20ms passed\", processor.shouldRequestAck(null, outQueue));\n\t\toutQueue.ack(10);\n\t\tassertFalse(\"Request for ACK shouldn't be sent for 20ms after receiving ACK\", processor.shouldRequestAck(null, outQueue));\n\t\tThread.sleep(21);\n\t\tassertTrue(\"Request for ACK should be sent as over 20ms passed\", processor.shouldRequestAck(null, outQueue));\n\t}\n\n\t@Test\n\tpublic void testBurstLimitPeriodRatio() throws TigaseStringprepException {\n\t\tOutQueue outQueue = new OutQueue();\n\t\tPacket packet = Packet.packetInstance(new Element(\"message\").withElement(\"body\", null, UUID.randomUUID().toString()));\n\t\tboolean result = outQueue.append(packet, 1, 1, 1, 2);\n\t\tassertTrue(result);\n\t\tpacket = Packet.packetInstance(new Element(\"message\").withElement(\"body\", null, UUID.randomUUID().toString()));\n\t\tresult = outQueue.append(packet, 1, 1, 1, 2);\n\t\tassertTrue(result);\n\t\tpacket = Packet.packetInstance(new Element(\"message\").withElement(\"body\", null, UUID.randomUUID().toString()));\n\t\tresult = outQueue.append(packet, 1, 1, 1, 2);\n\t\tassertFalse(result);\n\t}\n\n\t@Test\n\tpublic void testBurstLimitPeriod_Disabled() throws TigaseStringprepException {\n\t\tOutQueue outQueue = new OutQueue();\n\t\tPacket packet = Packet.packetInstance(new Element(\"message\").withElement(\"body\", null, UUID.randomUUID().toString()));\n\t\tboolean result = outQueue.append(packet, 1, 1, 0, 1);\n\t\tassertTrue(result);\n\t\tpacket = Packet.packetInstance(new Element(\"message\").withElement(\"body\", null, UUID.randomUUID().toString()));\n\t\tresult = outQueue.append(packet, 1, 1, 0, 1);\n\t\tassertFalse(result);\n\t}\n\n\t@Test\n\tpublic void testBurstLimitPeriod_1second() throws TigaseStringprepException, InterruptedException {\n\t\tAtomicLong time = new AtomicLong();\n\t\tOutQueue outQueue = new OutQueue(time::get);\n\t\ttime.set(System.currentTimeMillis());\n\t\tPacket packet = Packet.packetInstance(new Element(\"message\").withElement(\"body\", null, UUID.randomUUID().toString()));\n\t\tboolean result = outQueue.append(packet, 1, 1, 1, 1);\n\t\tassertTrue(result);\n\t\ttime.set(time.longValue() + 1000);\n\t\tpacket = Packet.packetInstance(new Element(\"message\").withElement(\"body\", null, UUID.randomUUID().toString()));\n\t\tresult = outQueue.append(packet, 1, 1, 1, 1);\n\t\tassertFalse(result);\n\t}\n\n\t@Test\n\tpublic void testBurstLimitPeriod_2seconds() throws TigaseStringprepException, InterruptedException {\n\t\tAtomicLong time = new AtomicLong();\n\t\tOutQueue outQueue = new OutQueue(time::get);\n\t\ttime.set(System.currentTimeMillis());\n\t\tPacket packet = Packet.packetInstance(new Element(\"message\").withElement(\"body\", null, UUID.randomUUID().toString()));\n\t\tboolean result = outQueue.append(packet, 1, 1, 2, 2);\n\t\tassertTrue(result);\n\t\ttime.set(time.longValue() + 1000);\n\t\tpacket = Packet.packetInstance(new Element(\"message\").withElement(\"body\", null, UUID.randomUUID().toString()));\n\t\tresult = outQueue.append(packet, 1, 1, 2, 2);\n\t\tassertTrue(result);\n\t\ttime.set(time.longValue() + 1000);\n\t\tpacket = Packet.packetInstance(new Element(\"message\").withElement(\"body\", null, UUID.randomUUID().toString()));\n\t\tresult = outQueue.append(packet, 1, 1, 2, 2);\n\t\tassertFalse(result);\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/server/xmppserver/S2SConnManAbstractTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.server.xmppserver;\n\nimport org.junit.Assert;\nimport org.junit.BeforeClass;\nimport tigase.TestLogger;\nimport tigase.cert.CertCheckResult;\nimport tigase.component.DSLBeanConfiguratorWithBackwardCompatibility;\nimport tigase.conf.LoggingBean;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.io.CertificateContainer;\nimport tigase.io.SSLContextContainer;\nimport tigase.kernel.DefaultTypesConverter;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.ConnectionManager;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.xmppserver.proc.AuthenticatorSelectorManager;\nimport tigase.server.xmppserver.proc.Dialback;\nimport tigase.server.xmppserver.proc.StartTLS;\nimport tigase.util.dns.DNSEntry;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.vhosts.DummyVHostManager;\nimport tigase.vhosts.VHostManagerIfc;\nimport tigase.xml.Element;\nimport tigase.xmpp.StanzaType;\n\nimport java.io.IOException;\nimport java.io.StringWriter;\nimport java.net.UnknownHostException;\nimport java.util.*;\nimport java.util.concurrent.TimeUnit;\nimport java.util.function.Consumer;\nimport java.util.function.Predicate;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.net.IOService.CERT_CHECK_RESULT;\n\npublic class S2SConnManAbstractTest\n\t\textends SSLTestAbstract {\n\n\tpublic static Kernel kernel;\n\tprivate static CID cid;\n\tprivate static S2SConnManTest.S2SConnectionHandlerImpl handler = null;\n\n\tprivate static void dumpConfiguration(DSLBeanConfiguratorWithBackwardCompatibility config) throws IOException {\n\t\tfinal StringWriter stringWriter = new StringWriter();\n\n\t\tconfig.dumpConfiguration(stringWriter);\n\t\tSystem.out.println(\"Configuration dump:\" + stringWriter.toString());\n\t}\n\n\tprivate static DSLBeanConfiguratorWithBackwardCompatibility prepareKernel() {\n\t\tMap<String, Object> props = new HashMap<>();\n\t\tprops.put(\"name\", \"s2s\");\n\n\t\tkernel = new Kernel();\n\t\tkernel.setName(\"s2s\");\n\t\tkernel.setForceAllowNull(true);\n\t\tkernel.registerBean(DefaultTypesConverter.class).exec();\n\t\tkernel.registerBean(DSLBeanConfiguratorWithBackwardCompatibility.class).exportable().exec();\n\t\tfinal DSLBeanConfiguratorWithBackwardCompatibility config = kernel.getInstance(\n\t\t\t\tDSLBeanConfiguratorWithBackwardCompatibility.class);\n\t\tconfig.setProperties(props);\n\t\tkernel.registerBean(\"eventBus\").asInstance(EventBusFactory.getInstance()).exportable().exec();\n\t\tkernel.registerBean(ConnectionManager.PortsConfigBean.class).exec();\n\t\tkernel.registerBean(CIDConnections.CIDConnectionsOpenerService.class).exportable().exec();\n\t\tkernel.registerBean(S2SRandomSelector.class).exportable().exec();\n\t\tkernel.registerBean(CertificateContainer.class).exportable().exec();\n\t\tkernel.registerBean(StartTLS.class).exportable().exec();\n\t\tkernel.registerBean(DummyDialbackImpl.class).exportable().setActive(true).exec();\n\t\tkernel.registerBean(KnownDomainsListProvider.class).exportable().setActive(true).exec();\n\t\tkernel.registerBean(AuthenticatorSelectorManager.class).exportable().setActive(true).exec();\n\t\tkernel.registerBean(\"vHostManager\")\n\t\t\t\t.asClass(DummyVHostManager.class)\n\t\t\t\t.exportable()\n\t\t\t\t.setActive(true)\n\t\t\t\t.exec();\n\t\tkernel.registerBean(SSLContextContainer.class).exportable().setActive(true).exec();\n\t\tkernel.registerBean(\"service\").asClass(S2SConnManTest.S2SConnectionHandlerImpl.class).setActive(true).exec();\n\t\tkernel.registerBean(\"logging\").asClass(LoggingBean.class).setActive(true).setPinned(true).exec();\n\t\treturn config;\n\t}\n\n\t@BeforeClass\n\tpublic static void setup() {\n\t\tgetSslDebugString().ifPresent(debug -> System.setProperty(\"javax.net.debug\", debug));\n\n\t\tlog = Logger.getLogger(\"tigase\");\n\t\tTestLogger.configureLogger(log, Level.CONFIG);\n\n\t\tfinal DSLBeanConfiguratorWithBackwardCompatibility config = prepareKernel();\n\n\t\ttry {\n\t\t\tfinal SSLContextContainer context = kernel.getInstance(SSLContextContainer.class);\n\t\t\tfinal LoggingBean loggingBean = kernel.getInstance(LoggingBean.class);\n\t\t\tloggingBean.setPacketFullDebug(true);\n\t\t\tfinal AuthenticatorSelectorManager instance = kernel.getInstance(AuthenticatorSelectorManager.class);\n\t\t\tSystem.out.println(instance);\n\t\t\thandler = kernel.getInstance(S2SConnManTest.S2SConnectionHandlerImpl.class);\n\t\t\thandler.start();\n\t\t\tdumpConfiguration(config);\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.WARNING, ex, () -> \"There was an error setting up test\");\n\t\t}\n\n\t\tTestLogger.configureLogger(log, Level.ALL);\n\t}\n\n\tprotected static void setupCID(String localHostname, String remoteHostname) {\n\t\tcid = new CID(localHostname, remoteHostname);\n\t\tfinal DummyVHostManager instance = (DummyVHostManager) kernel.getInstance(\n\t\t\t\tVHostManagerIfc.class);\n\t\tif (instance.getVHostItem(localHostname) == null) {\n\t\t\tinstance.addVhost(localHostname);\n\t\t}\n\t\ttry {\n\t\t\tfinal SSLContextContainer context = kernel.getInstance(SSLContextContainer.class);\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.WARNING, ex, () -> \"There was an error setting up test\");\n\t\t}\n\t}\n\t\n\tprivate static void setupSslContextContainer(final SSLContextContainer context, final String localDomain,\n\t\t\t\t\t\t\t\t\t\t\t\t SSLContextContainer.HARDENED_MODE mode, final String[] protocols,\n\t\t\t\t\t\t\t\t\t\t\t\t final String[] ciphers) {\n\t\tif (mode != null) {\n\t\t\tcontext.setHardenedMode(mode);\n\t\t}\n\t\tcontext.setEnabledProtocols(protocols);\n\t\tcontext.setEnabledCiphers(ciphers);\n\n\t\t// make sure that we have a certificate for this domain and it is loaded in in-memory key store or SASL EXTERNAL will fail\n\t\tcontext.getSSLContext(\"TLS\", localDomain, false, null);\n\t}\n\n\tprotected void testS2STigaseConnectionManager(String localDomain, String[] protocols) {\n\t\ttestS2STigaseConnectionManager(localDomain, protocols,\n\t\t\t\t\t\t\t\t\t   certCheckResult -> Assert.assertEquals(CertCheckResult.trusted, certCheckResult),\n\t\t\t\t\t\t\t\t\t   Assert::assertTrue);\n\t}\n\n\tprotected void testS2STigaseConnectionManager(String localDomain, String[] protocols, S2SConnectionHandlerImpl.IPFamily ipFamily) {\n\t\ttestS2STigaseConnectionManager(localDomain, protocols, ipFamily,\n\t\t\t\t\t\t\t\t\t   certCheckResult -> Assert.assertEquals(CertCheckResult.trusted, certCheckResult),\n\t\t\t\t\t\t\t\t\t   Assert::assertTrue);\n\t}\n\n\tprotected void testS2STigaseConnectionManager(String localDomain, String[] protocols, Consumer<CertCheckResult> certificateCheckResult,\n\t\t\t\t\t\t\t\t\t\t\t\t  Consumer<Boolean> authenticatedConsumer) {\n\t\ttestS2STigaseConnectionManager(localDomain, protocols, S2SConnectionHandlerImpl.IPFamily.ANY, certificateCheckResult, authenticatedConsumer);\n\t}\n\n\tprotected void testS2STigaseConnectionManager(String localDomain, String[] protocols, S2SConnectionHandlerImpl.IPFamily ipFamily, Consumer<CertCheckResult> certificateCheckResult,\n\t\t\t\t\t\t\t\t\t\t\t\t  Consumer<Boolean> authenticatedConsumer) {\n\t\ttry {\n\t\t\thandler.setIpFamily(ipFamily);\n\t\t\tfinal SSLContextContainer context = kernel.getInstance(SSLContextContainer.class);\n\t\t\tsetupSslContextContainer(context, localDomain, SSLContextContainer.HARDENED_MODE.secure, protocols, null);\n\n\t\t\ttestConnectionForCID(cid, certificateCheckResult, authenticatedConsumer);\n\t\t} catch (Exception e) {\n\t\t\tlog.log(Level.FINE, \"Error running test\", e);\n\t\t\tAssert.fail(\"exception: \" + e);\n\t\t}\n\t}\n\n\t// This method always fails if local certificate is not trusted!\n\tprivate void testConnectionForCID(CID cid, Consumer<CertCheckResult> certificateCheckResult,\n\t\t\t\t\t\t\t\t\t  Consumer<Boolean> authenticatedConsumer)\n\t\t\tthrows NotLocalhostException, LocalhostException, InterruptedException {\n\t\tfinal fastCIDConnections connections = handler.createNewCIDConnections(cid);\n\t\tconnections.openConnections();\n\n\t\ttry {\n\t\t\tfinal Packet packet = Iq.packetInstance(\"iq_version_query_test\" + UUID.randomUUID(), cid.getLocalHost(),\n\t\t\t\t\t\t\t\t\t\t\t\t\tcid.getRemoteHost(), StanzaType.get);\n\t\t\tfinal Element iqElement = packet.getElement();\n\t\t\tiqElement.setAttribute(\"id\", UUID.randomUUID().toString());\n\t\t\tfinal Element query = new Element(\"query\");\n\t\t\tquery.setXMLNS(\"jabber:iq:version\");\n\t\t\tiqElement.addChild(query);\n\n\t\t\thandler.addPacket(packet);\n\t\t} catch (Exception e) {\n\n\t\t}\n\n\t\tS2SIOService s2SIOService = null;\n\t\tint delayRetryLimit = 100;\n\t\tboolean connected = false;\n\t\tboolean authenticated = false;\n\t\tboolean completed = false;\n\t\tCertCheckResult trusted = CertCheckResult.none;\n\t\tboolean dialbackCompleted = false;\n\t\tdo {\n\t\t\tTimeUnit.MILLISECONDS.sleep(100);\n\t\t\tif (dialbackCompleted) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\ts2SIOService = connections.getS2SIOService();\n\t\t\tif (s2SIOService != null) {\n\t\t\t\tconnected = s2SIOService.isConnected();\n\t\t\t\tauthenticated = s2SIOService.isAuthenticated();\n\t\t\t\tcompleted = s2SIOService.isStreamNegotiationCompleted();\n\t\t\t\ttrusted = (CertCheckResult) s2SIOService.getSessionData().get(CERT_CHECK_RESULT);\n\t\t\t\tdialbackCompleted = \"completed\".equals(s2SIOService.getSessionData().get(\"dialback\"));\n\t\t\t}\n\t\t} while ((s2SIOService == null || !connected || !authenticated || !completed || !CertCheckResult.trusted.equals(trusted)) &&\n\t\t\t\tdelayRetryLimit-- > 0);\n\t\tlog.log(Level.INFO, cid + \": isConnected(): \" + connected);\n\t\tlog.log(Level.INFO, cid + \": isAuthenticated(): \" + authenticated);\n\t\tlog.log(Level.INFO, cid + \": isStreamNegotiationCompleted(): \" + completed);\n\t\tlog.log(Level.INFO, cid + \": getSessionData().get(CERT_CHECK_RESULT): \" + trusted);\n\n\t\t// Dialback may fail, we should check if handshake was completed successfully..\n\t\t//Assert.assertTrue(connected);\n\n\t\tAssert.assertNotNull(\"TLS handshake not completed\",s2SIOService.getSessionData().get(\"tlsHandshakeCompleted\"));\n\t\t\n\t\tif (s2SIOService.isConnected()) {\n\t\t\tString value = (String) s2SIOService.getSessionData().get(\"dialback\");\n\t\t\tif (value != null) {\n\t\t\t\tAssert.assertTrue(\"completed\".equals(value) || \"started\".equals(value));\n\t\t\t} else {\n\t\t\t\t// this means that SASL-EXTERNAL worked..\n\t\t\t}\n\t\t} else {\n\t\t\tAssert.assertNotEquals(\"Dialback still not completed\", \"started\",s2SIOService.getSessionData().get(\"dialback\"));\n\t\t}\n\n\t\t// Should we test if the certificate is trusted if we fallback to diallback?\n//\t\tcertificateCheckResult.accept(trusted);\n\n\t\t// it will fail when testing locally as it's not possible to perform dialback that way without mapping domain\n\t\t// domain to local machine\n//\t\tauthenticatedConsumer.accept(authenticated);\n\n\t\tif (s2SIOService.isConnected()) {\n\t\t\tTimeUnit.SECONDS.sleep(5);\n\t\t}\n\n\t\thandler.serviceStopped(s2SIOService);\n\t}\n\n\tpublic static class DummyDialbackImpl\n\t\t\textends Dialback {\n\n\t\t@Override\n\t\tprotected void initDialback(S2SIOService serv, String remote_id) {\n\t\t\tsuper.initDialback(serv, remote_id);\n\t\t\tserv.getSessionData().put(\"dialback\", \"started\");\n\t\t\t// we cannot assume that it is authenticated? It need to go through full S2S dialback..\n//\t\t\ttry {\n//\t\t\t\tCID cid_main = (CID) serv.getSessionData().get(\"cid\");\n//\t\t\t\tCIDConnections cid_conns = handler.getCIDConnections(cid_main, true);\n//\t\t\t\tauthenticatorSelectorManager.authenticateConnection(serv, cid_conns, cid_main);\n//\t\t\t} catch (NotLocalhostException | LocalhostException e) {\n//\t\t\t\tlog.log(Level.WARNING, \"Failure\", e);\n//\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean process(Packet p, S2SIOService serv, Queue<Packet> results) {\n\t\t\tif (p.getElemName() == RESULT_EL_NAME || p.getElemName() == DB_RESULT_EL_NAME) {\n\t\t\t\tserv.getSessionData().put(\"dialback\", \"completed\");\n\t\t\t}\n\t\t\treturn super.process(p, serv, results);\n\t\t}\n\t}\n\n\tpublic static class S2SConnectionHandlerImpl\n\t\t\textends S2SConnectionManager {\n\n\t\tpublic S2SConnectionHandlerImpl() {\n\t\t\tconnectionDelay = 0;\n\t\t}\n\n\t\tpublic enum IPFamily {\n\t\t\tANY,\n\t\t\tIPv6,\n\t\t\tIPv4\n\t\t}\n\n\t\tprivate IPFamily ipFamily = IPFamily.ANY;\n\n\t\tpublic IPFamily getIpFamily() {\n\t\t\treturn ipFamily;\n\t\t}\n\n\t\tpublic void setIpFamily(IPFamily ipFamily) {\n\t\t\tthis.ipFamily = ipFamily;\n\t\t}\n\n\t\t@Override\n\t\tpublic HashSet<Integer> getDefPorts() {\n\t\t\treturn new HashSet<>();\n\t\t}\n\n\t\t@Override\n\t\tpublic fastCIDConnections createNewCIDConnections(CID cid) throws NotLocalhostException, LocalhostException {\n\t\t\tfastCIDConnections conns = new fastCIDConnections(cid, this);\n\t\t\tcidConnections.put(cid, conns);\n\t\t\treturn conns;\n\t\t}\n\n\t\t@Override\n\t\tpublic Queue<Packet> processSocketData(S2SIOService serv) {\n\t\t\tfinal Queue<Packet> receivedPackets = serv.getReceivedPackets();\n\t\t\tif (receivedPackets != null) {\n\t\t\t\treceivedPackets.forEach(x -> log.log(Level.INFO, \"Received packet: \" + x));\n\t\t\t}\n\t\t\treturn super.processSocketData(serv);\n\t\t}\n\n\t\t@Override\n\t\tprotected void addWaitingTask(Map<String, Object> conn) {\n\t\t\treconnectService(conn, connectionDelay);\n\t\t}\n\n\t\tprivate void reconnectService(final Map<String, Object> port_props, long delay) {\n\t\t\tif (log.isLoggable(Level.FINER)) {\n\t\t\t\tString cid = \"\" + port_props.get(\"local-hostname\") + \"@\" + port_props.get(\"remote-hostname\");\n\n\t\t\t\tlog.log(Level.FINER,\n\t\t\t\t\t\t\"Reconnecting service for: {0}, scheduling next try in {1} seconds, cid: {2}, props: {3}\",\n\t\t\t\t\t\tnew Object[]{getName(), delay / 1000, cid, port_props});\n\t\t\t}\n\t\t\tString host = (String) port_props.get(PORT_REMOTE_HOST_PROP_KEY);\n\n\t\t\tif (host == null) {\n\t\t\t\thost = (String) port_props.get(\"remote-hostname\");\n\t\t\t}\n\n\t\t\tint port = (Integer) port_props.get(PORT_KEY);\n\n\t\t\tif (log.isLoggable(Level.FINE)) {\n\t\t\t\tlog.log(Level.FINE,\n\t\t\t\t\t\t\"Reconnecting service for component: {0}, to remote host: {1} on port: {2,number,#}\",\n\t\t\t\t\t\tnew Object[]{getName(), host, port});\n\t\t\t}\n\t\t\tstartService(port_props);\n\t\t}\n\n\t\t@Override\n\t\tpublic void tlsHandshakeCompleted(S2SIOService serv) {\n\t\t\tsuper.tlsHandshakeCompleted(serv);\n\t\t\tserv.getSessionData().put(\"tlsHandshakeCompleted\", \"true\");//serv.getPeerCertificate());\n\t\t}\n\n\t\t@Override\n\t\tpublic void xmppStreamClosed(S2SIOService serv) {\n\t\t\t// it is possible to close socket if SASL EXTERNAL fails without dialback as an option\n\t\t\t// and we need to respect that, so if we start dialback and connection is just closed, then it is OK\n\t\t\t// this is called only if </stream:stream> was received, right?\n\t\t\tif (\"started\".equals(serv.getSessionData().get(\"dialback\"))) {\n\t\t\t\tserv.getSessionData().put(\"dialback\", \"stream-closed\");\n\t\t\t}\n\t\t\tsuper.xmppStreamClosed(serv);\n\t\t}\n\t}\n\n\tpublic static class fastCIDConnections\n\t\t\textends CIDConnections {\n\n\t\tprivate final CID cid;\n\t\tS2SConnection s2s_conn = null;\n\n\t\tpublic fastCIDConnections(CID cid, S2SConnectionHandlerIfc<S2SIOService> handler) {\n\t\t\tsuper(cid, handler, new S2SRandomSelector(), 1, 1, 1, 0);\n\t\t\tthis.cid = cid;\n\t\t}\n\n\t\t@Override\n\t\tpublic void connectionAuthenticated(S2SIOService serv, CID cid) {\n\t\t\tsuper.connectionAuthenticated(serv, cid);\n\t\t}\n\n\t\tpublic S2SIOService getS2SIOService() {\n\t\t\treturn s2s_conn.getS2SIOService();\n\t\t}\n\n\t\t@Override\n\t\tprotected boolean hasExceededMaxWaitingTime() {\n\t\t\treturn false;\n\t\t}\n\n\t\tpublic void openConnections() {\n\t\t\ttry {\n\t\t\t\tif (log.isLoggable(Level.FINEST)) {\n\t\t\t\t\tlog.log(Level.FINEST, \"Checking DNS for host: {0} for: {1}\",\n\t\t\t\t\t\t\tnew Object[]{cid.getRemoteHost(), cid});\n\t\t\t\t}\n\t\t\t\tfinal String serverName = cid.getRemoteHost();\n\t\t\t\tDNSEntry[] dns_entries = DNSResolverFactory.getInstance().getHostSRV_Entries(serverName);\n\t\t\t\tvar dns_entry = selectDnsEntry(dns_entries).orElseThrow(NoValidDnsEntry::new);\n\t\t\t\tfinal String ip = dns_entry.getIp();\n\t\t\t\ts2s_conn = new S2SConnection(handler, ip);\n\t\t\t\tMap<String, Object> port_props = new TreeMap<>();\n\t\t\t\tport_props.put(S2SIOService.CERT_REQUIRED_DOMAIN, serverName);\n\t\t\t\tinitNewConnection(ip, dns_entry.getPort(), s2s_conn, port_props);\n\t\t\t} catch (NoValidDnsEntry ex) {\n\t\t\t\tlog.log(Level.FINE, \"No valid DNS entries found: \" + cid.getRemoteHost() + \", for: \" + cid + \", ipFamily: \" + handler.getIpFamily());\n\t\t\t\t//Assert.fail(\"No valid DNS entries found: \" + cid.getRemoteHost() + \", for: \" + cid + \", ipFamily: \" + handler.getIpFamily());\n\t\t\t} catch (UnknownHostException ex) {\n\t\t\t\tlog.log(Level.FINE, \"Remote host not found: \" + cid.getRemoteHost() + \", for: \" + cid, ex);\n\t\t\t\tAssert.fail(\"Remote host not found: \" + cid.getRemoteHost() + \", for: \" + cid);\n\t\t\t}\n\t\t}\n\n\t\tprotected Optional<DNSEntry> selectDnsEntry(DNSEntry[] dns_entries) {\n\t\t\tif (dns_entries == null) {\n\t\t\t\treturn Optional.empty();\n\t\t\t}\n\t\t\tPredicate<String> ipPredicate = switch (handler.getIpFamily()) {\n\t\t\t\tcase ANY -> ip -> true;\n\t\t\t\tcase IPv4 -> ip -> ip.contains(\".\");\n\t\t\t\tcase IPv6 -> ip -> !ip.contains(\".\");\n\t\t\t};\n\n\t\t\treturn Arrays.stream(dns_entries).map(e -> {\n\t\t\t\tvar ips = Arrays.stream(e.getIps()).filter(ipPredicate).toArray(String[]::new);\n\t\t\t\tif (ips.length == 0) {\n\t\t\t\t\treturn null;\n\t\t\t\t} else {\n\t\t\t\t\treturn new DNSEntry(e.getHostname(), e.getDnsResultHost(), ips, e.getPort(), e.getTtl(), e.getPriority(), e.getWeight());\n\t\t\t\t}\n\t\t\t}).filter(Objects::nonNull).findFirst();\n\t\t}\n\n\t\tprotected class NoValidDnsEntry extends Exception {}\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/server/xmppserver/S2SConnManDomainTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.server.xmppserver;\n\nimport org.junit.*;\nimport tigase.TestLogger;\nimport tigase.cert.CertCheckResult;\nimport tigase.server.xmppserver.proc.AuthenticatorSelectorManager;\nimport tigase.stats.StatRecord;\nimport tigase.stats.StatisticsList;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n@Ignore\npublic class S2SConnManDomainTest\n\t\textends S2SConnManAbstractTest {\n\n\t@AfterClass\n\tpublic static void postStats() {\n\t\tfinal AuthenticatorSelectorManager selector = kernel.getInstance(AuthenticatorSelectorManager.class);\n\t\tfinal StatisticsList list = new StatisticsList(Level.FINEST);\n\t\tselector.getStatistics(\"test\", list);\n\t\tfor (StatRecord statRecord : list) {\n\t\t\tlog.log(Level.ALL, statRecord.toString());\n\t\t}\n\t}\n\n\t@BeforeClass\n\tpublic static void setup() {\n\t\tSystem.setProperty(\"test-ssl-debug\", \"false\");\n\t\tS2SConnManAbstractTest.setup();\n\t\tTestLogger.configureLogger(log, Level.INFO);\n\t\tlog = Logger.getLogger(\"tigase.server.xmppserver\");\n\t\tTestLogger.configureLogger(log, Level.FINEST);\n//\t\tTestLogger.configureLogger(Logger.getLogger(\"tigase.server\"), Level.FINEST);\n//\t\tTestLogger.configureLogger(Logger.getLogger(\"tigase.cert\"), Level.FINEST);\n//\t\tTestLogger.configureLogger(Logger.getLogger(\"tigase.io\"), Level.FINEST);\n//\t\tTestLogger.configureLogger(Logger.getLogger(\"tigase.net\"), Level.FINEST);\n//\t\tTestLogger.configureLogger(Logger.getLogger(\"tigase.xmpp\"), Level.FINEST);\n\t}\n\n\t@Test\n\tpublic void testS2S_convorb_im() {\n\t\tsetupCID(\"tigase.im\", \"convorb.im\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null);\n\t}\n\n\t@Test\n\tpublic void testS2S_404_city() {\n\t\tsetupCID(\"tigase.im\", \"404.city\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null);\n\t}\n\n//\t@Test\n//\tpublic void testS2S_frsra_ml() {\n//\t\tsetupCID(\"tigase.im\", \"frsra.ml\");\n//\t\ttestS2STigaseConnectionManager(null);\n//\t}\n\n\t@Test\n\tpublic void testS2S_jabber_ru() {\n\t\tsetupCID(\"tigase.im\", \"jabber.ru\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null);\n\t}\n\n\t@Test\n\tpublic void testS2S_jabbercity_ru() {\n\t\tsetupCID(\"sure.im\", \"jabbercity.ru\");\n\t\ttestS2STigaseConnectionManager(\"sure.im\", null);\n\t}\n\n\t@Test\n\tpublic void testS2S_jabberix_com() {\n\t\tsetupCID(\"tigase.im\", \"jabberix.com\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null);\n\t}\n\n\t@Test\n\tpublic void testS2S_jwchat_org() {\n\t\tsetupCID(\"tigase.im\", \"jwchat.org\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null);\n\t}\n\n\t@Test\n\tpublic void testS2S_vrcshop_com() {\n\t\tsetupCID(\"tigase.im\", \"vrcshop.com\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null);\n\t}\n\n//\t@Test\n//\tpublic void testS2S_axeos_nl() {\n//\t\tsetupCID(\"tigase.im\", \"axeos.nl\");\n//\t\ttestS2STigaseConnectionManager(null);\n//\t}\n\n\t@Test\n\tpublic void testS2S_tigase_org() {\n\t\tsetupCID(\"tigase.im\", \"tigase.org\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null);\n\t}\n\n\t/**\n\t * local tests\n\t */\n\t@Test\n\t@Ignore\n\tpublic void testS2S_puddlejumper_atlantiscity() {\n\t\tsetupCID(\"puddlejumper\", \"atlantiscity\");\n\t\ttestS2STigaseConnectionManager(\"puddlejumper\", null);\n\t}\n\n//\t@Test\n//\tpublic void testS2S_jit_si() {\n//\t\tsetupCID(\"tigase.im\", \"jit.si\");\n//\t\ttestS2STigaseConnectionManager(null);\n//\t}\n\n\t@Test\n\tpublic void testS2S_jabber_org() {\n\t\tsetupCID(\"tigase.im\", \"jabber.org\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null);\n\t}\n\n\t@Test\n\tpublic void testS2S_cluxia_eu() {\n\t\tsetupCID(\"tigase.im\", \"cluxia.eu\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null);\n\t}\n\n\t@Test\n\tpublic void testS2S_pouet_ovh() {\n\t\tsetupCID(\"tigase.im\", \"pouet.ovh\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null);\n\t}\n\n\t@Test\n\tpublic void testS2S_upload_pouet_ovh() {\n\t\tsetupCID(\"tigase.im\", \"upload.pouet.ovh\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null);\n\t}\n\n\t@Test\n\tpublic void testS2S_rsocks_net() {\n\t\t// can't connect from 404.im, certificate not trusted ; jabster.pl: ejabberd 18.12.1\n\t\tsetupCID(\"tigase.im\", \"rsocks.net\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null);\n\t}\n\n\t@Test\n\tpublic void testS2S_dismail_de() {\n\t\t// ejabberd 19.09.57\n\t\tsetupCID(\"tigase.im\", \"dismail.de\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null);\n\t}\n\n\t@Test\n\tpublic void testS2S_legalize_li() {\n\t\t// can't connect from 404.im, certificate not trusted ; jabster.pl: ejabberd 18.12.1\n\t\tsetupCID(\"tigase.im\", \"legalize.li\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null);\n\t}\n\n//\t@Test\n//\tpublic void testS2S_legaliza_live() {\n//\t\t// invalid namespace?! / Openfire 4.4.2\n//\t\tsetupCID(\"tigase.im\", \"legaliza.live\");\n//\t\ttestS2STigaseConnectionManager(null);\n//\t}\n\n\t@Test\n\tpublic void testS2S_jabber_cz() {\n\t\t// invalid namespace?! / Openfire 4.4.2\n\t\tsetupCID(\"tigase.im\", \"jabber.cz\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null);\n\t}\n\n\t@Test\n\tpublic void testS2S_xmpp_jp() {\n\t\t// invalid namespace?! / Openfire 4.4.2\n\t\tsetupCID(\"tigase.im\", \"xmpp.jp\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null);\n\t}\n\n\t@Test\n\tpublic void testS2S_xmpp_uwpx_org() {\n\t\t// can't connect from 404.im, certificate not trusted ; jabster.pl: ejabberd, 19.09.1\n\t\tsetupCID(\"tigase.im\", \"xmpp.uwpx.org\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null);\n\t}\n\n\t@Test\n\tpublic void testS2S_messaging_one() {\n\t\tsetupCID(\"tigase.im\", \"messaging.one\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null);\n\t}\n\n\t@Test\n\tpublic void testS2S_conference_process_one_net() {\n\t\tsetupCID(\"tigase.im\", \"conference.process-one.net\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null);\n\t}\n\n\t@Test\n\tpublic void testS2S_shreddox_eu() {\n\t\tsetupCID(\"tigase.im\", \"shreddox.eu\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null);\n\t}\n\n\t@Test\n\tpublic void testS2S_xabber_org() {\n\t\tsetupCID(\"tigase.im\", \"xabber.org\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null);\n\t}\n\n\t@Test\n\tpublic void testS2S_dreckshal_de() {\n\t\tsetupCID(\"tigase.im\", \"dreckshal.de\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null);\n\t}\n\n\t@Test\n\t@Ignore\n\tpublic void testS2S_expired_badxmpp_eu() {\n\t\tsetupCID(\"tigase.im\", \"expired.badxmpp.eu\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null,\n\t\t\t\t\t\t\t\t\t   certCheckResult -> Assert.assertEquals(CertCheckResult.expired, certCheckResult),\n\t\t\t\t\t\t\t\t\t   Assert::assertFalse);\n\t}\n\n\t@Test\n\t@Ignore\n\tpublic void testS2S_ipv6_only_badxmpp_eu() {\n\t\tsetupCID(\"tigase.im\", \"ipv6-only.badxmpp.eu\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null);\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null, S2SConnectionHandlerImpl.IPFamily.IPv6);\n\t}\n\n\t@Test\n\t@Ignore\n\tpublic void testS2S_ipv6_conversations_im() {\n\t\tsetupCID(\"tigase.im\", \"conversations.im\");\n\t\ttestS2STigaseConnectionManager(\"tigase.im\", null, S2SConnectionHandlerImpl.IPFamily.IPv6);\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/server/xmppserver/S2SConnManTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.server.xmppserver;\n\nimport org.junit.BeforeClass;\nimport org.junit.Ignore;\nimport org.junit.Test;\n\n/**\n * $ ./scripts/tigase.sh start etc/tigase.conf\n *\n * OR\n *\n * $ /usr/local/sbin/ejabberdctl live\n */\n@Ignore\npublic class S2SConnManTest\n\t\textends S2SConnManAbstractTest {\n\n\t@BeforeClass\n\tpublic static void setup() {\n\t\tS2SConnManAbstractTest.setup();\n\t\tsetupCID();\n\t}\n\n\tprivate static void setupCID() {\n\t\tString localHostname = localHostname();\n\t\tString remoteHostname = System.getProperty(\"test-remote-hostname\", \"wojtek-local.tigase.eu\");\n// //\t\t https://projects.tigase.net/issue/systems-54\n//\t\t\tremoteHostname = \"convorb.im\";\n//\t\t\tremoteHostname = \"404.city\";\n//\t\t\tremoteHostname = \"tigase.org\";\n\t\tsetupCID(localHostname, remoteHostname);\n\t}\n\n\tprivate static String localHostname() {\n\t\treturn System.getProperty(\"test-local-hostname\", \"tigase.im\");\n\t}\n\n\t@Test\n\tpublic void testS2STigase_defaults() {\n\t\ttestS2STigaseConnectionManager(localHostname(), null);\n\t}\n\n\t@Test\n\tpublic void testS2STigase_default_w_TLS13_only() {\n\t\ttestS2STigaseConnectionManager(localHostname(), new String[]{\"TLSv1.3\"});\n\t}\n\n\t@Test\n\tpublic void testS2STigase_default_w_TLS13_w_SSLv2Hello() {\n\t\ttestS2STigaseConnectionManager(localHostname(), new String[]{\"TLSv1.3\", \"TLSv1.2\", \"TLSv1.1\", \"TLSv1\", \"SSLv2Hello\"});\n\t}\n\n\t@Test\n\tpublic void testS2STigase_default_w_TLS13_wo_SSLv2Hello() {\n\t\ttestS2STigaseConnectionManager(localHostname(), new String[]{\"TLSv1.3\", \"TLSv1.2\", \"TLSv1.1\", \"TLSv1\"});\n\t}\n\n\t@Test\n\tpublic void testS2STigase_default_wo_TLS13_wo_SSLv2Hello() {\n\t\ttestS2STigaseConnectionManager(localHostname(), new String[]{\"TLSv1.2\", \"TLSv1.1\", \"TLSv1\"});\n\t}\n\n\t@Test\n\tpublic void testS2STigase_default_wo_TLS13_w_SSLv2Hello() {\n\t\t// \"SSLv2Hello\" failing?! for constricted openssl cipher list [2]\n\t\ttestS2STigaseConnectionManager(localHostname(), new String[]{\"TLSv1.2\", \"TLSv1.1\", \"TLSv1\", \"SSLv2Hello\"});\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/server/xmppserver/S2SConnectionManagerDomainServerNameMapperTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver;\n\nimport junit.framework.TestCase;\nimport org.junit.Test;\nimport tigase.TestLogger;\nimport tigase.server.xmppserver.S2SConnectionManager.DomainServerNameMapper;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * @author andrzej\n */\npublic class S2SConnectionManagerDomainServerNameMapperTest\n\t\textends TestCase {\n\n\tprivate static final Logger log = TestLogger.getLogger(S2SConnectionManagerDomainServerNameMapperTest.class);\n\n\t@Test\n\tpublic void testSortingOfMappings() {\n\t\tDomainServerNameMapper mapper = new DomainServerNameMapper();\n\n\t\t// adding mapping entries\n\t\tmapper.addEntry(\"*\", \"test1\");\n\t\tmapper.addEntry(\"*.local\", \"test2\");\n\t\tmapper.addEntry(\"*.test\", \"test3\");\n\t\tmapper.addEntry(\"*.test.local\", \"test4\");\n\t\tmapper.addEntry(\"test\", \"test5\");\n\t\tmapper.addEntry(\"local\", \"test6\");\n\t\tmapper.addEntry(\"test1.test\", \"test7\");\n\t\tmapper.addEntry(\"test1.test.local\", \"test8\");\n\n\t\tlog.log(Level.FINE, mapper.toString());\n\n\t\t// checking assertions to make sure that due to sorting of mappings\n\t\t// mappings are used in proper order\n\t\tassertEquals(\"test1\", mapper.getServerNameForDomain(\"tigase.org\"));\n\t\tassertEquals(\"test4\", mapper.getServerNameForDomain(\"test.local\"));\n\t\tassertEquals(\"test2\", mapper.getServerNameForDomain(\"test1.local\"));\n\t\tassertEquals(\"test5\", mapper.getServerNameForDomain(\"test\"));\n\t\tassertEquals(\"test6\", mapper.getServerNameForDomain(\"local\"));\n\t\tassertEquals(\"test3\", mapper.getServerNameForDomain(\"test.test\"));\n\t\tassertEquals(\"test4\", mapper.getServerNameForDomain(\"test.test.local\"));\n\t\tassertEquals(\"test7\", mapper.getServerNameForDomain(\"test1.test\"));\n\t\tassertEquals(\"test8\", mapper.getServerNameForDomain(\"test1.test.local\"));\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/server/xmppserver/SSLSocketDirectTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.server.xmppserver;\n\nimport org.junit.Assert;\nimport org.junit.BeforeClass;\nimport org.junit.Ignore;\nimport org.junit.Test;\nimport tigase.TestLogger;\n\nimport javax.net.ssl.SSLSocket;\nimport javax.net.ssl.SSLSocketFactory;\nimport java.io.*;\nimport java.util.Arrays;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * Using Javas's SSLSocket class to connect to openssl made server\n *\n * [1] - default openss:\n * openssl s_server -key privkey.pem -cert cert.pem -CAfile fullchain.pem -accept 5269\n *\n * [2] - ejabberd recommended cipers\n * openssl s_server -key privkey.pem -cert cert.pem -CAfile fullchain.pem -accept 5269 -cipher \"ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256\" -debug\n */\n@Ignore\npublic class SSLSocketDirectTest\n\t\textends SSLTestAbstract {\n\n\tstatic String hostname = \"wojtek-local.tigase.eu\";\n\tstatic int port = 5269;\n\n\tprivate static void reopenStream(SSLSocket socket) throws IOException, InterruptedException {\n\t\tInputStream is = new BufferedInputStream(socket.getInputStream());\n\t\tOutputStream os = new BufferedOutputStream(socket.getOutputStream());\n\t\tfinal PrintWriter writer = new PrintWriter(os, true);\n\t\twriter.println(\"<stream:stream\" + \" xmlns:stream='http://etherx.jabber.org/streams' \" +\n\t\t\t\t\t\t\t   \" xmlns:xml='http://www.w3.org/XML/1998/namespace' xmlns='jabber:client' \" + \" to='\" +\n\t\t\t\t\t\t\t   hostname + \"'\" + \" version='1.0'>\");\n//\t\tfinal InputStreamReader reader = new InputStreamReader(is);\n//\t\tBufferedReader br = new BufferedReader(reader);\n//\t\tSystem.out.println(br.lines().collect(Collectors.joining()));\n\t}\n\n\t@BeforeClass\n\tpublic static void setup() {\n\t\tgetSslDebugString().ifPresent(debug -> System.setProperty(\"javax.net.debug\", debug));\n\n\t\tlog = Logger.getLogger(\"tigase\");\n\t\tTestLogger.configureLogger(log, Level.INFO);\n\n\t\thostname = System.getProperty(\"test-hostname\");\n\t\tfinal String portProperty = System.getProperty(\"test-port\");\n\t\ttry {\n\t\t\tport = Integer.parseInt(portProperty);\n\t\t} catch (NumberFormatException e) {\n\t\t\tlog.log(Level.INFO, () -> \"parsing portProperty: \" + portProperty + \" failed\");\n\t\t}\n\t}\n\n\tpublic static void testSSLSocketConnection(String[] protocols) {\n\n\t\ttry (final SSLSocket socket = (SSLSocket) SSLSocketFactory.getDefault().createSocket(hostname, port)) {\n\t\t\tif (protocols != null) {\n\t\t\t\tsocket.setEnabledProtocols(protocols);\n\t\t\t}\n\t\t\tlog.log(Level.INFO, () -> \"Socket enabled protocols: \" + Arrays.toString(socket.getEnabledProtocols()));\n\t\t\tsocket.addHandshakeCompletedListener(event -> log.log(Level.INFO, \"Connected using: \" +\n\t\t\t\t\tevent.getSession().getProtocol() + \", with cipher: \" + event.getCipherSuite() +\n\t\t\t\t\t\", enabled protocols: \" + Arrays.toString(event.getSocket().getEnabledProtocols())));\n\n\t\t\tsocket.startHandshake();\n\t\t\tAssert.assertTrue(socket.isConnected());\n\t\t\treopenStream(socket);\n\t\t} catch (Exception e) {\n\t\t\tAssert.fail(\"exception: \" + e);\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testSSLSocketConnection_defaults() {\n\t\ttestSSLSocketConnection(null);\n\t}\n\n\t@Test()\n\tpublic void testSSLSocketConnection_TLS13_only() {\n\t\ttestSSLSocketConnection(new String[]{\"TLSv1.3\"});\n\t}\n\n\t@Test\n\tpublic void testSSLSocketConnection_default_w_TLS13_w_SSLv2Hello() {\n\t\t// \"SSLv2Hello\" not failing for constricted cipher list [2] when TLS1.3 is enabled?\n\t\ttestSSLSocketConnection(new String[]{\"TLSv1.3\", \"TLSv1.2\", \"TLSv1.1\", \"TLSv1\", \"SSLv2Hello\"});\n\t}\n\n\t@Test\n\tpublic void testSSLSocketConnection_default_w_TLS13_wo_SSLv2Hello() {\n\t\ttestSSLSocketConnection(new String[]{\"TLSv1.3\", \"TLSv1.2\", \"TLSv1.1\", \"TLSv1\"});\n\t}\n\n\t@Test\n\tpublic void testSSLSocketConnection_default_wo_TLS13_w_SSLv2Hello() {\n\t\t// \"SSLv2Hello\" failing?! for constricted openssl cipher list [2] and with TLS1.3 disabled\n\t\ttestSSLSocketConnection(new String[]{\"TLSv1.2\", \"TLSv1.1\", \"TLSv1\", \"SSLv2Hello\"});\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/server/xmppserver/SSLTestAbstract.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.server.xmppserver;\n\nimport tigase.util.log.LogFormatter;\n\nimport java.util.Arrays;\nimport java.util.Optional;\nimport java.util.logging.ConsoleHandler;\nimport java.util.logging.Handler;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nabstract class SSLTestAbstract {\n\n\tstatic Logger log;\n\n\tstatic Optional<String> getSslDebugString() {\n\t\t// https://docs.oracle.com/en/java/javase/11/security/java-secure-socket-extension-jsse-reference-guide.html#GUID-31B7E142-B874-46E9-8DD0-4E18EC0EB2CF\n\n\t\tStringBuilder sslDebug = new StringBuilder(\"ssl\");\n\t\tsslDebug.append(\":defaultctx\"); //Print default SSL initialization\n\t\tsslDebug.append(\":handshake\"); //Print each handshake message\n//\t\tsslDebug.append(\":keygen\"); //Print key generation data\n//\t\tsslDebug.append(\":keymanager\"); //Print key manager tracing\n//\t\tsslDebug.append(\":pluggability\"); //Print pluggability tracing\n//\t\tsslDebug.append(\":record\"); //Enable per-record tracing\n//\t\tsslDebug.append(\":respmgr\"); //Print status response manager tracing\n\t\tsslDebug.append(\":session\"); //Print session activity\n//\t\tsslDebug.append(\":sessioncache\"); //Print session cache tracing\n\t\tsslDebug.append(\":sslctx\"); //Print SSLContext tracing\n//\t\tsslDebug.append(\":trustmanager\"); //Print trust manager tracing\n//\t\tsslDebug.append(\":data\"); //Messages generated from the handshake: Hex dump of each handshake message\n\t\tsslDebug.append(\":verbose\"); //Messages generated from the handshake: Verbose handshake message printing\n//\t\tsslDebug.append(\":plaintext\"); //Messages generated from the record: Hex dump of record plaintext\n//\t\tsslDebug.append(\":packet\"); //Messages generated from the record: Print raw SSL/TLS packets\n\t\tfinal String sslDebugPropertyString = System.getProperty(\"test-ssl-debug\", \"true\");\n\t\tfinal boolean sslDebugProperty = Boolean.parseBoolean(sslDebugPropertyString);\n\t\treturn sslDebugProperty ? Optional.of(sslDebug.toString()) : Optional.empty();\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/server/xmppserver/proc/DialbackTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppserver.proc;\n\nimport junit.framework.TestCase;\nimport org.junit.Test;\nimport tigase.TestLogger;\nimport tigase.component.DSLBeanConfiguratorWithBackwardCompatibility;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.io.CertificateContainer;\nimport tigase.kernel.DefaultTypesConverter;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.ConnectionManager;\nimport tigase.server.Packet;\nimport tigase.server.xmppserver.*;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.StanzaType;\n\nimport java.util.*;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.net.IOService.PORT_TYPE_PROP_KEY;\nimport static tigase.server.xmppserver.proc.S2SAbstract.FEATURES_EL;\nimport static tigase.server.xmppserver.proc.S2SAbstract.FEATURES_NS;\n\n/**\n * @author andrzej\n */\npublic class DialbackTest\n\t\textends TestCase {\n\n\tprivate Dialback dialback;\n\tprivate S2SConnectionHandlerImpl handler = null;\n\tprivate Kernel kernel;\n\tprivate String local = \"local.com\";\n\tprivate String remote1 = \"remote1.com\";\n\tprivate String remote2 = \"remote2.com\";\n\n\t@Test\n\tpublic void testAuthorizationForSingleDomain() throws TigaseStringprepException {\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\thandler.setResults(results);\n\t\tdialback.init(handler, new HashMap());\n\n\t\tString key = UUID.randomUUID().toString();\n\n\t\tS2SIOService serv = new S2SIOService();\n\t\tserv.setSessionId(\"sess-id-1\");\n\t\tMap<String, Object> props = new HashMap<>();\n\t\tprops.put(PORT_TYPE_PROP_KEY, \"accept\");\n\t\tserv.setSessionData(props);\n\n\t\tPacket p = null;\n\n\t\tElement resultEl = new Element(\"db:result\");\n\t\tresultEl.setXMLNS(Dialback.XMLNS_DB_VAL);\n\t\tresultEl.setAttribute(\"from\", remote1);\n\t\tresultEl.setAttribute(\"to\", local);\n\t\tresultEl.setCData(key);\n\t\tp = Packet.packetInstance(resultEl);\n\t\tdialback.process(p, serv, results);\n\n\t\tPacket r = results.poll();\n\t\tresultEl = new Element(\"db:verify\");\n\t\tresultEl.setXMLNS(Dialback.XMLNS_DB_VAL);\n\t\tresultEl.setAttribute(\"id\", r.getAttributeStaticStr(\"id\"));\n\t\tresultEl.setAttribute(\"from\", r.getAttributeStaticStr(\"to\"));\n\t\tresultEl.setAttribute(\"to\", r.getAttributeStaticStr(\"from\"));\n\t\tresultEl.setAttribute(\"type\", \"valid\");\n\t\tp = Packet.packetInstance(resultEl);\n\t\tdialback.process(p, serv, results);\n\n\t\tp = results.poll();\n\t\tassertTrue(p.getType() == StanzaType.valid && remote1.equals(p.getStanzaTo().getDomain()));\n\n\t\tserv.getCIDs().forEach((CID cid) -> assertEquals(remote1, cid.getRemoteHost()));\n\t}\n\n\t@Test\n\tpublic void testEmptyFeatures() throws TigaseStringprepException {\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\thandler.setResults(results);\n\t\tdialback.init(handler, new HashMap());\n\n\t\tString key = UUID.randomUUID().toString();\n\n\t\tS2SIOService serv = new S2SIOService();\n\t\tserv.setSessionId(\"sess-id-1\");\n\t\tMap<String, Object> props = new HashMap<>();\n\t\tprops.put(PORT_TYPE_PROP_KEY, \"accept\");\n\t\tserv.setSessionData(props);\n\n\t\tPacket p = null;\n\n\t\tElement resultEl = new Element(FEATURES_EL);\n\t\tresultEl.setXMLNS(FEATURES_NS);\n\t\tp = Packet.packetInstance(resultEl);\n\t\tfinal boolean processResult = dialback.process(p, serv, results);\n\t\tassertFalse(processResult);\n\t\tassertTrue(results.isEmpty());\n\t}\n\n\t@Test\n\tpublic void testAuthorizationForSingleDomainFailure() throws TigaseStringprepException {\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\thandler.setResults(results);\n\t\tdialback.init(handler, new HashMap());\n\n\t\tString key = UUID.randomUUID().toString();\n\n\t\tS2SIOService serv = new S2SIOService();\n\t\tserv.setSessionId(\"sess-id-1\");\n\t\tMap<String, Object> props = new HashMap<>();\n\t\tprops.put(PORT_TYPE_PROP_KEY, \"accept\");\n\t\tserv.setSessionData(props);\n\n\t\tPacket p = null;\n\n\t\tElement resultEl = new Element(\"db:result\");\n\t\tresultEl.setXMLNS(Dialback.XMLNS_DB_VAL);\n\t\tresultEl.setAttribute(\"from\", remote1);\n\t\tresultEl.setAttribute(\"to\", local);\n\t\tresultEl.setCData(key);\n\t\tp = Packet.packetInstance(resultEl);\n\t\tdialback.process(p, serv, results);\n\n\t\tPacket r = results.poll();\n\t\tresultEl = new Element(\"db:verify\");\n\t\tresultEl.setXMLNS(Dialback.XMLNS_DB_VAL);\n\t\tresultEl.setAttribute(\"id\", r.getAttributeStaticStr(\"id\"));\n\t\tresultEl.setAttribute(\"from\", r.getAttributeStaticStr(\"to\"));\n\t\tresultEl.setAttribute(\"to\", r.getAttributeStaticStr(\"from\"));\n\t\tresultEl.setAttribute(\"type\", \"invalid\");\n\t\tp = Packet.packetInstance(resultEl);\n\t\tdialback.process(p, serv, results);\n\n\t\tp = results.poll();\n\t\tassertTrue(p.getType() == StanzaType.invalid && remote1.equals(p.getStanzaTo().getDomain()));\n\n\t\tserv.getCIDs().forEach((CID cid) -> assertNotSame(remote1, cid.getRemoteHost()));\n\t}\n\n\t@Test\n\tpublic void testAuthorizationWithMultiplexing() throws TigaseStringprepException {\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\thandler.setResults(results);\n\t\tdialback.init(handler, new HashMap());\n\n\t\tString key = UUID.randomUUID().toString();\n\n\t\tS2SIOService serv = new S2SIOService();\n\t\tserv.setSessionId(\"sess-id-1\");\n\t\tMap<String, Object> props = new HashMap<>();\n\t\tprops.put(PORT_TYPE_PROP_KEY, \"accept\");\n\t\tserv.setSessionData(props);\n\n\t\tPacket p = null;\n\n\t\tElement resultEl = new Element(\"db:result\");\n\t\tresultEl.setXMLNS(Dialback.XMLNS_DB_VAL);\n\t\tresultEl.setAttribute(\"from\", remote1);\n\t\tresultEl.setAttribute(\"to\", local);\n\t\tresultEl.setCData(key);\n\t\tp = Packet.packetInstance(resultEl);\n\t\tdialback.process(p, serv, results);\n\n\t\tPacket r = results.poll();\n\t\tresultEl = new Element(\"db:verify\");\n\t\tresultEl.setXMLNS(Dialback.XMLNS_DB_VAL);\n\t\tresultEl.setAttribute(\"id\", r.getAttributeStaticStr(\"id\"));\n\t\tresultEl.setAttribute(\"from\", r.getAttributeStaticStr(\"to\"));\n\t\tresultEl.setAttribute(\"to\", r.getAttributeStaticStr(\"from\"));\n\t\tresultEl.setAttribute(\"type\", \"valid\");\n\t\tp = Packet.packetInstance(resultEl);\n\t\tdialback.process(p, serv, results);\n\n\t\tp = results.poll();\n\t\tassertTrue(p.getType() == StanzaType.valid && remote1.equals(p.getStanzaTo().getDomain()));\n\t\tserv.getCIDs().forEach((CID cid) -> assertEquals(remote1, cid.getRemoteHost()));\n\n\t\tkey = UUID.randomUUID().toString();\n\n\t\tresultEl = new Element(\"db:result\");\n\t\tresultEl.setXMLNS(Dialback.XMLNS_DB_VAL);\n\t\tresultEl.setAttribute(\"from\", remote2);\n\t\tresultEl.setAttribute(\"to\", local);\n\t\tresultEl.setCData(key);\n\t\tp = Packet.packetInstance(resultEl);\n\t\tdialback.process(p, serv, results);\n\n\t\tr = results.poll();\n\t\tresultEl = new Element(\"db:verify\");\n\t\tresultEl.setXMLNS(Dialback.XMLNS_DB_VAL);\n\t\tresultEl.setAttribute(\"id\", r.getAttributeStaticStr(\"id\"));\n\t\tresultEl.setAttribute(\"from\", r.getAttributeStaticStr(\"to\"));\n\t\tresultEl.setAttribute(\"to\", r.getAttributeStaticStr(\"from\"));\n\t\tresultEl.setAttribute(\"type\", \"valid\");\n\t\tp = Packet.packetInstance(resultEl);\n\t\tdialback.process(p, serv, results);\n\n\t\tp = results.poll();\n\t\tassertTrue(p.getType() == StanzaType.valid && remote2.equals(p.getStanzaTo().getDomain()));\n\t\tfinal Set<CID> CIDs = serv.getCIDs();\n\t\tassertTrue(CIDs.stream().anyMatch((CID cid) -> remote1.equals(cid.getRemoteHost())));\n\t\tassertTrue(CIDs.stream().anyMatch((CID cid) -> remote2.equals(cid.getRemoteHost())));\n\t}\n\n\t@Test\n\tpublic void testAuthorizationWithMultiplexingWithFailure() throws TigaseStringprepException {\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\thandler.setResults(results);\n\t\tdialback.init(handler, new HashMap());\n\n\t\tString key = UUID.randomUUID().toString();\n\n\t\tS2SIOService serv = new S2SIOService();\n\t\tserv.setSessionId(\"sess-id-1\");\n\t\tMap<String, Object> props = new HashMap<>();\n\t\tprops.put(PORT_TYPE_PROP_KEY, \"accept\");\n\t\tserv.setSessionData(props);\n\n\t\tPacket p = null;\n\n\t\tElement resultEl = new Element(\"db:result\");\n\t\tresultEl.setXMLNS(Dialback.XMLNS_DB_VAL);\n\t\tresultEl.setAttribute(\"from\", remote1);\n\t\tresultEl.setAttribute(\"to\", local);\n\t\tresultEl.setCData(key);\n\t\tp = Packet.packetInstance(resultEl);\n\t\tdialback.process(p, serv, results);\n\n\t\tPacket r = results.poll();\n\t\tresultEl = new Element(\"db:verify\");\n\t\tresultEl.setXMLNS(Dialback.XMLNS_DB_VAL);\n\t\tresultEl.setAttribute(\"id\", r.getAttributeStaticStr(\"id\"));\n\t\tresultEl.setAttribute(\"from\", r.getAttributeStaticStr(\"to\"));\n\t\tresultEl.setAttribute(\"to\", r.getAttributeStaticStr(\"from\"));\n\t\tresultEl.setAttribute(\"type\", \"valid\");\n\t\tp = Packet.packetInstance(resultEl);\n\t\tdialback.process(p, serv, results);\n\n\t\tp = results.poll();\n\t\tassertTrue(p.getType() == StanzaType.valid && remote1.equals(p.getStanzaTo().getDomain()));\n\t\tserv.getCIDs().forEach((CID cid) -> assertEquals(remote1, cid.getRemoteHost()));\n\n\t\tkey = UUID.randomUUID().toString();\n\n\t\tresultEl = new Element(\"db:result\");\n\t\tresultEl.setXMLNS(Dialback.XMLNS_DB_VAL);\n\t\tresultEl.setAttribute(\"from\", remote2);\n\t\tresultEl.setAttribute(\"to\", local);\n\t\tresultEl.setCData(key);\n\t\tp = Packet.packetInstance(resultEl);\n\t\tdialback.process(p, serv, results);\n\n\t\tr = results.poll();\n\t\tresultEl = new Element(\"db:verify\");\n\t\tresultEl.setXMLNS(Dialback.XMLNS_DB_VAL);\n\t\tresultEl.setAttribute(\"id\", r.getAttributeStaticStr(\"id\"));\n\t\tresultEl.setAttribute(\"from\", r.getAttributeStaticStr(\"to\"));\n\t\tresultEl.setAttribute(\"to\", r.getAttributeStaticStr(\"from\"));\n\t\tresultEl.setAttribute(\"type\", \"invalid\");\n\t\tp = Packet.packetInstance(resultEl);\n\t\tdialback.process(p, serv, results);\n\n\t\tp = results.poll();\n\t\tassertTrue(p.getType() == StanzaType.invalid && remote2.equals(p.getStanzaTo().getDomain()));\n\t\tassertTrue(serv.getCIDs().stream().anyMatch((CID cid) -> remote1.equals(cid.getRemoteHost())));\n\t\tassertTrue(serv.getCIDs().stream().allMatch((CID cid) -> !remote2.equals(cid.getRemoteHost())));\n\t}\n\n\t@Override\n\tprotected void setUp() throws Exception {\n\t\tsuper.setUp();\n\t\tfinal Logger log = Logger.getLogger(\"tigase\");\n\t\tTestLogger.configureLogger(log, Level.OFF);\n\t\tMap<String, Object> props = new HashMap<>();\n\t\tprops.put(\"name\", \"s2s\");\n\n\t\tkernel = new Kernel();\n\t\tkernel.setName(\"s2s\");\n\t\tkernel.setForceAllowNull(true);\n\t\tkernel.registerBean(DefaultTypesConverter.class).exec();\n\t\tkernel.registerBean(DSLBeanConfiguratorWithBackwardCompatibility.class).exportable().exec();\n\t\tkernel.getInstance(DSLBeanConfiguratorWithBackwardCompatibility.class).setProperties(props);\n\t\tkernel.registerBean(\"eventBus\").asInstance(EventBusFactory.getInstance()).exportable().exec();\n\t\tkernel.registerBean(ConnectionManager.PortsConfigBean.class).exec();\n\t\tkernel.registerBean(CIDConnections.CIDConnectionsOpenerService.class).exportable().exec();\n\t\tkernel.registerBean(S2SRandomSelector.class).exportable().exec();\n\t\tkernel.registerBean(KnownDomainsListProvider.class).exportable().setActive(true).exec();\n\t\tkernel.registerBean(AuthenticatorSelectorManager.class).exportable().exec();\n\t\tkernel.registerBean(DialbackImpl.class).exportable().exec();\n\t\tkernel.registerBean(CertificateContainer.class).exportable().exec();\n\t\tkernel.registerBean(\"service\").asClass(S2SConnectionHandlerImpl.class).setActive(true).exec();\n\n\t\ttry {\n\t\t\thandler = kernel.getInstance(S2SConnectionHandlerImpl.class);\n\t\t} catch (Exception ex) {\n\t\t\tex.printStackTrace();\n\t\t\tthrow ex;\n\t\t}\n//\t\tfinal Logger log = Logger.getLogger(\"tigase\");\n//\t\tTestLogger.configureLogger(log, Level.OFF);\n\t\tdialback = kernel.getInstance(Dialback.class);\n\t}\n\n\tpublic static class DialbackImpl\n\t\t\textends Dialback {\n\n\t\t@Override\n\t\tpublic boolean skipTLSForHost(String hostname) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tprotected boolean wasVerifyRequested(S2SIOService serv, String domain) {\n\t\t\treturn true;\n\t\t}\n\n\t}\n\n\tpublic static class S2SConnectionHandlerImpl\n\t\t\textends S2SConnectionManager {\n\n\t\tprivate Queue<Packet> results;\n\n\t\tpublic S2SConnectionHandlerImpl() {\n\t\t}\n\n\t\tpublic void setResults(Queue<Packet> results) {\n\t\t\tthis.results = results;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isTlsRequired(String domain) {\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic HashSet<Integer> getDefPorts() {\n\t\t\treturn new HashSet<>();\n\t\t}\n\n\t\t@Override\n\t\tprotected CIDConnections createNewCIDConnections(CID cid) throws NotLocalhostException, LocalhostException {\n\n\t\t\tCIDConnections conns = new CIDConnections(cid, this, new S2SRandomSelector(), 5, 5, 5, 5000) {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void sendHandshakingOnly(Packet verify_req) {\n\t\t\t\t\tresults.offer(verify_req);\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic boolean sendControlPacket(String sessionId, Packet packet) {\n\t\t\t\t\treturn results.offer(packet);\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void sendPacket(Packet packet) {\n\t\t\t\t\tresults.offer(packet);\n\t\t\t\t}\n\n\t\t\t};\n\t\t\tcidConnections.put(cid, conns);\n\t\t\treturn conns;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/server/xmppsession/adhoc/SuggestedDomainsListAdhocTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.server.xmppsession.adhoc;\n\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.component.DSLBeanConfiguratorWithBackwardCompatibility;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.form.Form;\nimport tigase.io.CertificateContainer;\nimport tigase.io.SSLContextContainer;\nimport tigase.kernel.DefaultTypesConverter;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.ConnectionManager;\nimport tigase.server.xmppserver.CID;\nimport tigase.server.xmppserver.CIDConnections;\nimport tigase.server.xmppserver.KnownDomainsListProvider;\nimport tigase.server.xmppserver.S2SConnManAbstractTest;\nimport tigase.server.xmppserver.S2SConnManTest;\nimport tigase.server.xmppserver.S2SIOService;\nimport tigase.server.xmppserver.S2SRandomSelector;\nimport tigase.server.xmppserver.proc.AuthenticatorSelectorManager;\nimport tigase.vhosts.DefaultAwareVHostManagerIfc;\nimport tigase.vhosts.DummyVHostManager;\nimport tigase.vhosts.VHostManagerIfc;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class SuggestedDomainsListAdhocTest {\n\n\tpublic static Kernel kernel;\n\tprivate static AuthenticatorSelectorManager authenticatorSelectorManager;\n\tprivate static CID cid;\n\tprivate static DummyVHostManager dummyVHostManager;\n\tprivate static S2SConnManTest.S2SConnectionHandlerImpl handler = null;\n\tprivate static SuggestedDomainsListAdhoc suggestedDomainsListAdhoc;\n\n\tprotected static void addCID(String localHostname, String remoteHostname) {\n\t\tvar cid = new CID(localHostname, remoteHostname);\n\t\tvar fastCIDConnections = new S2SConnManAbstractTest.fastCIDConnections(cid, handler);\n\t\tauthenticatorSelectorManager.authenticateConnection(new S2SIOService(), fastCIDConnections, cid);\n\n\t\tif (dummyVHostManager.getVHostItem(localHostname) == null) {\n\t\t\tdummyVHostManager.addVhost(localHostname);\n\t\t}\n\t}\n\n\tprivate static DSLBeanConfiguratorWithBackwardCompatibility prepareKernel() {\n\t\tMap<String, Object> props = new HashMap<>();\n\t\tprops.put(\"name\", \"s2s\");\n\n\t\tkernel = new Kernel();\n\t\tkernel.setName(\"s2s\");\n\t\tkernel.setForceAllowNull(true);\n\t\tkernel.registerBean(DefaultTypesConverter.class).exec();\n\t\tkernel.registerBean(DSLBeanConfiguratorWithBackwardCompatibility.class).exportable().exec();\n\t\tfinal DSLBeanConfiguratorWithBackwardCompatibility config = kernel.getInstance(\n\t\t\tDSLBeanConfiguratorWithBackwardCompatibility.class);\n\t\tconfig.setProperties(props);\n\t\tkernel.registerBean(\"eventBus\").asInstance(EventBusFactory.getInstance()).exportable().exec();\n\t\tkernel.registerBean(ConnectionManager.PortsConfigBean.class).exec();\n\t\tkernel.registerBean(CIDConnections.CIDConnectionsOpenerService.class).exportable().exec();\n\t\tkernel.registerBean(S2SRandomSelector.class).exportable().exec();\n\t\tkernel.registerBean(CertificateContainer.class).exportable().exec();\n\t\tkernel.registerBean(KnownDomainsListProvider.class).exportable().setActive(true).exec();\n\t\tkernel.registerBean(AuthenticatorSelectorManager.class).exportable().setActive(true).exec();\n\t\tkernel.registerBean(\"vHostManager\").asClass(DummyVHostManager.class).exportable().setActive(true).exec();\n\t\tkernel.registerBean(SSLContextContainer.class).exportable().setActive(true).exec();\n\t\tkernel.registerBean(\"service\").asClass(S2SConnManTest.S2SConnectionHandlerImpl.class).setActive(true).exec();\n\t\tkernel.registerBean(SuggestedDomainsListAdhoc.class).setActive(true).exec();\n\t\treturn config;\n\t}\n\n\t@Test\n\tpublic void prepareForm() {\n\t\tfinal Form form = suggestedDomainsListAdhoc.prepareForm();\n\t\tvar knownRemoteDomains = Arrays.asList(form.getAsStrings(\"knownRemoteDomains\"));\n\t\tvar localDomains = Arrays.asList(form.getAsStrings(\"localDomains\"));\n\n\t\tAssert.assertTrue(knownRemoteDomains.contains(\"tigase.org\"));\n\t\tAssert.assertTrue(knownRemoteDomains.contains(\"tigase.net\"));\n\t\tAssert.assertTrue(knownRemoteDomains.contains(\"tigase.im\"));\n\t\tAssert.assertTrue(knownRemoteDomains.contains(\"sure.im\"));\n\t\tAssert.assertTrue(knownRemoteDomains.contains(\"jabber.today\"));\n\n\t\tAssert.assertTrue(localDomains.contains(\"my-other-local-domain.org\"));\n\t\tAssert.assertTrue(localDomains.contains(\"my-local-domain.net\"));\n\t\tAssert.assertFalse(localDomains.contains(\"default\"));\n\t}\n\n\t@Before\n\tpublic void setUp() throws Exception {\n\t\tprepareKernel();\n\n\t\tauthenticatorSelectorManager = kernel.getInstance(AuthenticatorSelectorManager.class);\n\t\tdummyVHostManager = (DummyVHostManager) kernel.getInstance(DefaultAwareVHostManagerIfc.class);\n\t\thandler = kernel.getInstance(S2SConnManTest.S2SConnectionHandlerImpl.class);\n\t\tsuggestedDomainsListAdhoc = kernel.getInstance(SuggestedDomainsListAdhoc.class);\n\n\t\tdummyVHostManager.addVhost(\"default\");\n\n\t\taddCID(\"my-local-domain.net\", \"tigase.org\");\n\t\taddCID(\"my-local-domain.net\", \"tigase.net\");\n\t\taddCID(\"my-local-domain.net\", \"tigase.im\");\n\t\taddCID(\"my-local-domain.net\", \"sure.im\");\n\t\taddCID(\"my-other-local-domain.org\", \"tigase.org\");\n\t\taddCID(\"my-other-local-domain.org\", \"tigase.net\");\n\t\taddCID(\"my-other-local-domain.org\", \"tigase.im\");\n\t\taddCID(\"my-other-local-domain.org\", \"jabber.today\");\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/stats/MaxDailyCounterQueueTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.math.BigInteger;\nimport java.util.Optional;\nimport java.util.Random;\nimport java.util.stream.Stream;\n\npublic class MaxDailyCounterQueueTest {\n\n\t@Test\n\tpublic void getValueOfEmptyQueue() {\n\t\tMaxDailyCounterQueue<Integer> lq = new MaxDailyCounterQueue<>(5);\n\t\tAssert.assertEquals(Optional.empty(), lq.getMaxValue());\n\n\t\tlq.add(5);\n\t\tAssert.assertNotEquals(Optional.empty(), lq.getMaxValue());\n\t}\n\n\t@Test\n\tpublic void getMaxValueInRange() {\n\n\t\tfinal int limit = 5;\n\t\tMaxDailyCounterQueue<Integer> lq = new MaxDailyCounterQueueEveryXItems<>(limit, 1);\n\n\t\tlq.add(1);\n\t\tAssert.assertEquals(1, lq.getMaxValueInRange(limit).get().intValue());\n\n\t\tlq.add(2);\n\t\tAssert.assertEquals(2, lq.getMaxValueInRange(limit).get().intValue());\n\n\t\tlq.add(4);\n\t\tAssert.assertEquals(4, lq.getMaxValueInRange(limit).get().intValue());\n\n\t\tlq.add(5);\n\t\tAssert.assertEquals(5, lq.getMaxValueInRange(limit).get().intValue());\n\n\t\tlq.add(6);\n\t\tAssert.assertEquals(6, lq.getMaxValueInRange(limit).get().intValue());\n\n\t\tlq.add(1);\n\t\tAssert.assertEquals(6, lq.getMaxValueInRange(3).get().intValue());\n\n\t\tAssert.assertEquals(1, lq.getMaxValueInRange(1).get().intValue());\n\t\tlq.add(1);\n\t\tlq.add(1);\n\t\tAssert.assertEquals(1, lq.getMaxValueInRange(3).get().intValue());\n\n\t\tMaxDailyCounterQueue<Integer> lq2 = new MaxDailyCounterQueueEveryXItems<>(1, 4);\n\t\tlq2.add(4);\n\t\tAssert.assertEquals(4, lq2.peek().intValue());\n\t\tlq2.add(3);\n\t\tAssert.assertEquals(4, lq2.peek().intValue());\n\t\tlq2.add(2);\n\t\tAssert.assertEquals(4, lq2.peek().intValue());\n\t\tlq2.add(1);\n\t\tAssert.assertEquals(4, lq2.peek().intValue());\n\t\tlq2.add(8);\n\t\tAssert.assertEquals(8, lq2.peek().intValue());\n\t}\n\n\t@Test\n\tpublic void isLimitSurpassed() {\n\n\t\tfinal int collectionSize = 5;\n\t\tfinal int limit = 5;\n\t\tMaxDailyCounterQueue<Integer> lq = new MaxDailyCounterQueueEveryXItems<>(collectionSize, 1);\n\n\t\tlq.add(1);\n\t\tAssert.assertFalse(lq.isLimitSurpassed(3, limit));\n\n\t\tlq.add(limit + 1);\n\t\tAssert.assertTrue(lq.isLimitSurpassed(3, limit));\n\n\t\tlq.add(1);\n\t\tAssert.assertTrue(lq.isLimitSurpassed(3, limit));\n\n\t\tlq.add(1);\n\t\tAssert.assertTrue(lq.isLimitSurpassed(3, limit));\n\n\t\tlq.add(1);\n\t\tAssert.assertFalse(lq.isLimitSurpassed(3, limit));\n\t\tAssert.assertTrue(lq.isLimitSurpassed(4, limit));\n\t}\n\n\t@Test\n\tpublic void isLimitSurpassedAllItems() {\n\n\t\tfinal int collectionSize = 5;\n\t\tfinal int limit = 5;\n\t\tMaxDailyCounterQueue<Integer> lq = new MaxDailyCounterQueueEveryXItems<>(collectionSize, 1);\n\n\t\tlq.add(1);\n\t\tAssert.assertFalse(lq.isLimitSurpassedAllItems(3, limit));\n\n\t\tlq.add(limit + 1);\n\t\tAssert.assertFalse(lq.isLimitSurpassedAllItems(3, limit));\n\n\t\tlq.add(limit + 1);\n\t\tAssert.assertFalse(lq.isLimitSurpassedAllItems(3, limit));\n\n\t\tlq.add(limit + 1);\n\t\tAssert.assertTrue(lq.isLimitSurpassedAllItems(3, limit));\n\n\t\tlq.add(1);\n\t\tAssert.assertFalse(lq.isLimitSurpassedAllItems(3, limit));\n\n\t\tlq.add(1);\n\t\tAssert.assertFalse(lq.isLimitSurpassedAllItems(3, limit));\n\n\t}\n\t@Test\n\tpublic void concurrentAddition() {\n\t\tfinal int collectionSize = 500;\n\t\tfinal int limit = 5;\n\t\tMaxDailyCounterQueue<Integer> lq = new MaxDailyCounterQueueEveryXItems<>(collectionSize, 1);\n\n\t\tfinal Random random = new Random();\n\t\tStream.generate(random::nextInt).parallel().limit(collectionSize)\n\t\t\t\t.forEach(lq::add);\n\n\t\tAssert.assertEquals(collectionSize, lq.size());\n\n\t}\n\n\tclass MaxDailyCounterQueueEveryXItems<E extends Number & Comparable<E>>\n\t\t\textends MaxDailyCounterQueue<E> {\n\n\t\tprivate final int modulo;\n\t\tint i = 0;\n\n\t\tMaxDailyCounterQueueEveryXItems(int limit, int everyXItem) {\n\t\t\tsuper(limit);\n\t\t\tmodulo = everyXItem;\n\t\t}\n\n\t\t@Override\n\t\tprotected boolean isNextItem() {\n\t\t\treturn (i++) % modulo == 0;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/stats/StatisticsListTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.stats;\n\nimport org.junit.Test;\n\nimport java.util.LinkedHashMap;\nimport java.util.logging.Level;\n\nimport static org.junit.Assert.*;\n\npublic class StatisticsListTest {\n\n\t@Test\n\tpublic void testNonZeroRecords() {\n\n\t\tfinal String compName = \"comp\";\n\n\t\tStatisticsList statRecords = new StatisticsList(Level.INFO);\n\t\tstatRecords.add(compName, \"long\", 1L, Level.INFO);\n\t\tstatRecords.add(compName, \"int\", 2, Level.INFO);\n\t\tstatRecords.add(compName, \"string\", \"string\", Level.INFO);\n\t\tstatRecords.add(compName, \"float\", 3.4F, Level.INFO);\n\n\t\tstatRecords.add(compName, \"long-zero\", 0L, Level.INFO);\n\t\tstatRecords.add(compName, \"int-zero\", 0, Level.INFO);\n\t\tstatRecords.add(compName, \"string-zero\", \"\", Level.INFO);\n\t\tstatRecords.add(compName, \"float-zero\", 0F, Level.INFO);\n\n\t\tLinkedHashMap<String, StatRecord> compStats = statRecords.getCompStats(compName);\n\t\tassertTrue(\"Wrong collection size!\", compStats.size() == 4);\n\t\tassertNotNull(compStats.get(\"long\"));\n\t\tassertNotNull(compStats.get(\"int\"));\n\t\tassertNotNull(compStats.get(\"string\"));\n\t\tassertNotNull(compStats.get(\"float\"));\n\t\tassertNull(compStats.get(\"long-zero\"));\n\t\tassertNull(compStats.get(\"int-zero\"));\n\t\tassertNull(compStats.get(\"string-zero\"));\n\t\tassertNull(compStats.get(\"float-zero\"));\n\n\t\tstatRecords = new StatisticsList(Level.FINEST);\n\t\tstatRecords.add(compName, \"long\", 1L, Level.INFO);\n\t\tstatRecords.add(compName, \"int\", 2, Level.INFO);\n\t\tstatRecords.add(compName, \"string\", \"string\", Level.INFO);\n\t\tstatRecords.add(compName, \"float\", 3.4F, Level.INFO);\n\n\t\tstatRecords.add(compName, \"long-zero\", 0L, Level.INFO);\n\t\tstatRecords.add(compName, \"int-zero\", 0, Level.INFO);\n\t\tstatRecords.add(compName, \"string-zero\", \"\", Level.INFO);\n\t\tstatRecords.add(compName, \"float-zero\", 0F, Level.INFO);\n\n\t\tcompStats = statRecords.getCompStats(compName);\n\t\tassertTrue(\"Wrong collection size!\", compStats.size() == 8);\n\n\t\tassertNotNull(compStats.get(\"long\"));\n\t\tassertNotNull(compStats.get(\"int\"));\n\t\tassertNotNull(compStats.get(\"string\"));\n\t\tassertNotNull(compStats.get(\"float\"));\n\t\tassertNotNull(compStats.get(\"long-zero\"));\n\t\tassertNotNull(compStats.get(\"int-zero\"));\n\t\tassertNotNull(compStats.get(\"string-zero\"));\n\t\tassertNotNull(compStats.get(\"float-zero\"));\n\n\t}\n\n}"
  },
  {
    "path": "src/test/java/tigase/sys/NativeMemoryTrackingTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.sys;\n\nimport org.junit.Assert;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\npublic class NativeMemoryTrackingTest {\n\n\tprivate static final String summary =\n\t\t\t\"\\n\" + \"Native Memory Tracking:\\n\" + \"\\n\" + \"Total: reserved=5733424KB, committed=366756KB\\n\" +\n\t\t\t\t\t\"-                 Java Heap (reserved=4194304KB, committed=262144KB)\\n\" +\n\t\t\t\t\t\"                            (mmap: reserved=4194304KB, committed=262144KB) \\n\" + \" \\n\" +\n\t\t\t\t\t\"-                     Class (reserved=1059085KB, committed=10637KB)\\n\" +\n\t\t\t\t\t\"                            (classes #1609)\\n\" +\n\t\t\t\t\t\"                            (  instance classes #1450, array classes #159)\\n\" +\n\t\t\t\t\t\"                            (malloc=269KB #2481) \\n\" +\n\t\t\t\t\t\"                            (mmap: reserved=1058816KB, committed=10368KB) \\n\" +\n\t\t\t\t\t\"                            (  Metadata:   )\\n\" +\n\t\t\t\t\t\"                            (    reserved=10240KB, committed=9216KB)\\n\" +\n\t\t\t\t\t\"                            (    used=9076KB)\\n\" +\n\t\t\t\t\t\"                            (    free=140KB)\\n\" +\n\t\t\t\t\t\"                            (    waste=0KB =0.00%)\\n\" +\n\t\t\t\t\t\"                            (  Class space:)\\n\" +\n\t\t\t\t\t\"                            (    reserved=1048576KB, committed=1152KB)\\n\" +\n\t\t\t\t\t\"                            (    used=993KB)\\n\" +\n\t\t\t\t\t\"                            (    free=159KB)\\n\" +\n\t\t\t\t\t\"                            (    waste=0KB =0.00%)\\n\" + \" \\n\" +\n\t\t\t\t\t\"-                    Thread (reserved=17486KB, committed=17486KB)\\n\" +\n\t\t\t\t\t\"                            (thread #17)\\n\" +\n\t\t\t\t\t\"                            (stack: reserved=17408KB, committed=17408KB)\\n\" +\n\t\t\t\t\t\"                            (malloc=57KB #104) \\n\" +\n\t\t\t\t\t\"                            (arena=21KB #33)\\n\" + \" \\n\" +\n\t\t\t\t\t\"-                      Code (reserved=247804KB, committed=7664KB)\\n\" +\n\t\t\t\t\t\"                            (malloc=116KB #1038) \\n\" +\n\t\t\t\t\t\"                            (mmap: reserved=247688KB, committed=7548KB) \\n\" + \" \\n\" +\n\t\t\t\t\t\"-                        GC (reserved=207337KB, committed=61417KB)\\n\" +\n\t\t\t\t\t\"                            (malloc=17861KB #2347) \\n\" +\n\t\t\t\t\t\"                            (mmap: reserved=189476KB, committed=43556KB) \\n\" + \" \\n\" +\n\t\t\t\t\t\"-                  Compiler (reserved=1095KB, committed=1095KB)\\n\" +\n\t\t\t\t\t\"                            (malloc=18KB #89) \\n\" +\n\t\t\t\t\t\"                            (arena=1076KB #14)\\n\" + \" \\n\" +\n\t\t\t\t\t\"-                  Internal (reserved=562KB, committed=562KB)\\n\" +\n\t\t\t\t\t\"                            (malloc=530KB #982) \\n\" +\n\t\t\t\t\t\"                            (mmap: reserved=32KB, committed=32KB) \\n\" + \" \\n\" +\n\t\t\t\t\t\"-                     Other (reserved=2KB, committed=2KB)\\n\" +\n\t\t\t\t\t\"                            (malloc=2KB #1) \\n\" + \" \\n\" +\n\t\t\t\t\t\"-                    Symbol (reserved=3059KB, committed=3059KB)\\n\" +\n\t\t\t\t\t\"                            (malloc=1644KB #4986) \\n\" +\n\t\t\t\t\t\"                            (arena=1415KB #1)\\n\" + \" \\n\" +\n\t\t\t\t\t\"-    Native Memory Tracking (reserved=227KB, committed=227KB)\\n\" +\n\t\t\t\t\t\"                            (malloc=4KB #54) \\n\" +\n\t\t\t\t\t\"                            (tracking overhead=223KB)\\n\" + \" \\n\" +\n\t\t\t\t\t\"-               Arena Chunk (reserved=2337KB, committed=2337KB)\\n\" +\n\t\t\t\t\t\"                            (malloc=2337KB) \\n\" + \" \\n\" +\n\t\t\t\t\t\"-                   Logging (reserved=4KB, committed=4KB)\\n\" +\n\t\t\t\t\t\"                            (malloc=4KB #182) \\n\" + \" \\n\" +\n\t\t\t\t\t\"-                 Arguments (reserved=19KB, committed=19KB)\\n\" +\n\t\t\t\t\t\"                            (malloc=19KB #484) \\n\" + \" \\n\" +\n\t\t\t\t\t\"-                    Module (reserved=60KB, committed=60KB)\\n\" +\n\t\t\t\t\t\"                            (malloc=60KB #1035) \\n\" + \" \\n\" +\n\t\t\t\t\t\"-              Synchronizer (reserved=35KB, committed=35KB)\\n\" +\n\t\t\t\t\t\"                            (malloc=35KB #291) \\n\" + \" \\n\" +\n\t\t\t\t\t\"-                 Safepoint (reserved=8KB, committed=8KB)\\n\" +\n\t\t\t\t\t\"                            (mmap: reserved=8KB, committed=8KB) \\n\" + \" \\n\" + \"\\n\" + \"\\n\" +\n\t\t\t\t\t\"Process finished with exit code 0\\n\";\n\tprivate static NativeMemoryTracking nmt;\n\tprivate Set<String> summaryScopes = new TreeSet<>(\n\t\t\tArrays.asList(\"Total\", \"Java Heap\", \"Class\", \"Thread\", \"Code\", \"GC\", \"Compiler\", \"Internal\", \"Other\",\n\t\t\t\t\t\t  \"Symbol\", \"Native Memory Tracking\", \"Arena Chunk\", \"Logging\", \"Arguments\", \"Module\",\n\t\t\t\t\t\t  \"Synchronizer\", \"Safepoint\"));\n\n\t@BeforeClass\n\tpublic static void setUp() {\n//\t\tfinal Optional<NativeMemoryTracking> nativeMemoryTracking = NativeMemoryTracking.getNativeMemoryTracking();\n\t\tfinal Optional<NativeMemoryTracking> nativeMemoryTracking = NativeMemoryTracking.parse(summary);\n//\t\tSystem.out.println(nativeMemoryTracking);\n\t\tnmt = nativeMemoryTracking.orElse(null);\n\t}\n\n\t@Test\n\tpublic void getTypes() {\n\t\tAssert.assertNotNull(nmt);\n\t\tMap<String, NMTScope> scopes = nmt.getScopes();\n//\t\tscopes.forEach((s, nmtScope) -> System.out.println(nmtScope));\n\t\tfinal Set<String> scopeTypes = scopes.values().stream().map(NMTScope::getScopeType).collect(Collectors.toSet());\n\t\tAssert.assertEquals(17, scopeTypes.size());\n\t\tAssert.assertEquals(summaryScopes, scopeTypes);\n\t}\n\n\t@Test\n\tpublic void getTotalValues() {\n\t\tAssert.assertNotNull(nmt);\n\t\tMap<String, NMTScope> scopes = nmt.getScopes();\n\t\tfinal NMTScope totalScope = scopes.get(NMTScope.COMMON_SCOPES.TOTAL.getName());\n\t\t//\"Total: reserved=5733424KB, committed=366756KB\"\n\t\tAssert.assertEquals(5733424, totalScope.getReserved().longValue());\n\t\tAssert.assertEquals(366756, totalScope.getCommitted().longValue());\n\t\tAssert.assertFalse(totalScope.getArena().isPresent());\n\t\tAssert.assertFalse(totalScope.getMalloc().isPresent());\n\t\tAssert.assertFalse(totalScope.getMmapCommitted().isPresent());\n\t\tAssert.assertFalse(totalScope.getMmapReserved().isPresent());\n\t}\n\n\t@Test\n\tpublic void getExtendedValuesGC() {\n\t\tAssert.assertNotNull(nmt);\n\t\tMap<String, NMTScope> scopes = nmt.getScopes();\n\t\tfinal NMTScope gcScope = scopes.get(NMTScope.COMMON_SCOPES.GC.getName());\n\t\t// GC (reserved=207337KB, committed=61417KB)\n\t\t//    (malloc=17861KB #2347)\n\t\t//    (mmap: reserved=189476KB, committed=43556KB)\n//\t\tSystem.out.println(gcScope);\n\t\tAssert.assertEquals(207337, gcScope.getReserved().longValue());\n\t\tAssert.assertEquals(61417, gcScope.getCommitted().longValue());\n\t\tAssert.assertEquals(17861, gcScope.getMalloc().get().longValue());\n\t\tAssert.assertEquals(43556, gcScope.getMmapCommitted().get().longValue());\n\t\tAssert.assertEquals(189476, gcScope.getMmapReserved().get().longValue());\n\t\tAssert.assertFalse(gcScope.getArena().isPresent());\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/tests/SlowTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.tests;\n\n/**\n * Interface used in annotation as a category to mark long running JUnit tests, so they can be run on server when\n * snapshot is created and not during normal compilation in development environment\n *\n * @author andrzej\n */\npublic interface SlowTest {\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/util/AllHistoryCacheTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util;\n\nimport org.junit.Test;\nimport tigase.stats.StatisticsList;\nimport tigase.util.historyCache.AllHistoryCache;\n\nimport java.lang.reflect.Array;\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.logging.Level;\n\nimport static org.junit.Assert.assertArrayEquals;\nimport static org.junit.Assert.assertEquals;\n\n/**\n * Created by andrzej on 16.03.2016.\n */\npublic class AllHistoryCacheTest {\n\n\tprivate boolean highMemory = false;\n\n\t@Test\n\tpublic void testReductionOnHighMemoryUsage() throws NoSuchFieldException, IllegalAccessException {\n\t\tint limit = 20;\n\t\tList<StatisticsList> entries = new ArrayList<>();\n\t\tAllHistoryCache cache = new AllHistoryCache(limit, 95) {\n\t\t\t@Override\n\t\t\tpublic synchronized void addItem(StatisticsList item) {\n\t\t\t\tsuper.addItem(item);\n\t\t\t\tentries.add(item);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected boolean isHighMemoryUsage() {\n\t\t\t\treturn highMemory;\n\t\t\t}\n\t\t};\n\n\t\tfor (int i = 0; i < limit + 5; i++) {\n\t\t\tStatisticsList stats = new StatisticsList(Level.FINE);\n\t\t\tcache.addItem(stats);\n\t\t}\n\n\t\tassertEquals(20, cache.getCurrentHistory().length);\n\t\tfor (int i = 0; i < 5; i++) {\n\t\t\tentries.remove(0);\n\t\t}\n\t\tassertArrayEquals(entries.toArray(new StatisticsList[entries.size()]), cache.getCurrentHistory());\n\n\t\thighMemory = true;\n\t\tcache.addItem(new StatisticsList(Level.FINE));\n\t\twhile (entries.size() != 10) {\n\t\t\tentries.remove(0);\n\t\t}\n\t\tassertEquals(10, cache.getCurrentHistory().length);\n\t\tassertArrayEquals(entries.toArray(new StatisticsList[entries.size()]), cache.getCurrentHistory());\n\n\t\tcache.addItem(new StatisticsList(Level.FINE));\n\t\twhile (entries.size() != 5) {\n\t\t\tentries.remove(0);\n\t\t}\n\t\tassertEquals(5, cache.getCurrentHistory().length);\n\t\tassertArrayEquals(entries.toArray(new StatisticsList[entries.size()]), cache.getCurrentHistory());\n\n\t\thighMemory = false;\n\t\tcache.addItem(new StatisticsList(Level.FINE));\n\t\tassertEquals(6, cache.getCurrentHistory().length);\n\t\tassertArrayEquals(entries.toArray(new StatisticsList[entries.size()]), cache.getCurrentHistory());\n\n\t\thighMemory = true;\n\t\tcache.addItem(new StatisticsList(Level.FINE));\n\t\twhile (entries.size() != 5) {\n\t\t\tentries.remove(0);\n\t\t}\n\t\tassertEquals(5, cache.getCurrentHistory().length);\n\t\tassertArrayEquals(entries.toArray(new StatisticsList[entries.size()]), cache.getCurrentHistory());\n\n\t\tField f = AllHistoryCache.class.getDeclaredField(\"buffer\");\n\t\tf.setAccessible(true);\n\t\tObject arr = f.get(cache);\n\t\tint count = 0;\n\t\tfor (int i = 0; i < limit; i++) {\n\t\t\tif (Array.get(arr, i) != null) {\n\t\t\t\tcount++;\n\t\t\t}\n\t\t}\n\t\tassertEquals(5, count);\n\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/util/DataTypesTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util;\n\nimport org.junit.Test;\nimport tigase.TestLogger;\nimport tigase.util.repository.DataTypes;\n\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNull;\n\n/**\n * @author Wojtek\n */\npublic class DataTypesTest {\n\n\tprivate static final Logger log = TestLogger.getLogger(DataTypesTest.class);\n\n\t@Test\n\tpublic void testParseNum() {\n\n\t\tassertEquals(new Long(262144L), Long.valueOf(Integer.class.cast(DataTypes.parseNum(\"256k\", Integer.class, 1))));\n\t\tassertEquals(new Long(262144L), Long.class.cast(DataTypes.parseNum(\"256k\", Long.class, 1L)));\n\t\tassertEquals(new Double(670720.0D), Double.class.cast(DataTypes.parseNum(\"655k\", Double.class, 1D)));\n\t\tassertEquals(new Double(262144F),\n\t\t\t\t\t Double.valueOf(Float.class.cast(DataTypes.parseNum(\"256k\", Float.class, 1F))));\n\t\tassertEquals(new Long(25), Long.valueOf((long) DataTypes.parseNum(\"25\", Short.class, Short.valueOf(\"1\"))));\n\t\tassertEquals(new Long(25),\n\t\t\t\t\t Long.valueOf(Byte.class.cast(DataTypes.parseNum(\"25\", Byte.class, Byte.valueOf(\"1\")))));\n\t}\n\n\t@Test\n\tpublic void testParseSizeInt() {\n\t\tlog.log(Level.FINE, \"parseSizeInt\");\n\t\tassertEquals(1, DataTypes.parseSizeInt(\"1\", 1));\n\t\tassertEquals(1024, DataTypes.parseSizeInt(\"1k\", 1));\n\t\tassertEquals(1024 * 1024, DataTypes.parseSizeInt(\"1m\", 1));\n\t\tassertEquals(1024 * 1024 * 1024, DataTypes.parseSizeInt(\"1g\", 1));\n\t\tassertEquals(1, DataTypes.parseSizeInt(\"fail\", 1));\n\t}\n\n\t@Test\n\tpublic void testNull() {\n\t\tfinal Object o = DataTypes.decodeValueType('S', \"null\");\n\t\tassertNull(o);\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/util/ReflectionHelperTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util;\n\nimport org.junit.Test;\nimport tigase.cluster.repo.ClConConfigRepository;\nimport tigase.cluster.repo.ClConDirRepository;\nimport tigase.cluster.repo.ClConSQLRepository;\nimport tigase.cluster.repo.ClusterRepoItem;\nimport tigase.db.comp.ComponentRepository;\nimport tigase.db.comp.RepositoryItem;\nimport tigase.util.reflection.ReflectionHelper;\nimport tigase.vhosts.VHostItem;\nimport tigase.vhosts.VHostJDBCRepository;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Type;\n\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * Created by andrzej on 19.03.2016.\n */\npublic class ReflectionHelperTest {\n\n\t@Test\n\tpublic void testClassMatchesType() throws NoSuchFieldException {\n\t\tField f = null;\n\t\tType type = null;\n\n\t\tf = Test1.class.getDeclaredField(\"repo1\");\n\t\ttype = f.getGenericType();\n\t\tassertTrue(ReflectionHelper.classMatchesType(ClConSQLRepository.class, type));\n\t\tassertTrue(ReflectionHelper.classMatchesType(ClConConfigRepository.class, type));\n\t\tassertTrue(ReflectionHelper.classMatchesType(ClConDirRepository.class, type));\n\t\tassertFalse(ReflectionHelper.classMatchesType(VHostJDBCRepository.class, type));\n\n\t\tf = Test1.class.getDeclaredField(\"repo2\");\n\t\ttype = f.getGenericType();\n\t\tassertFalse(ReflectionHelper.classMatchesType(ClConSQLRepository.class, type));\n\t\tassertFalse(ReflectionHelper.classMatchesType(ClConConfigRepository.class, type));\n\t\tassertFalse(ReflectionHelper.classMatchesType(ClConDirRepository.class, type));\n\t\tassertTrue(ReflectionHelper.classMatchesType(VHostJDBCRepository.class, type));\n\t}\n\n\t@Test\n\tpublic void testClassMatchesClassWithParameters() {\n\t\tassertTrue(ReflectionHelper.classMatchesClassWithParameters(ClConSQLRepository.class, ComponentRepository.class,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew Type[]{ClusterRepoItem.class}));\n\t\tassertFalse(\n\t\t\t\tReflectionHelper.classMatchesClassWithParameters(ClConSQLRepository.class, ComponentRepository.class,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t new Type[]{VHostItem.class}));\n\t\tassertTrue(ReflectionHelper.classMatchesClassWithParameters(ClConSQLRepository.class, ComponentRepository.class,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew Type[]{RepositoryItem.class}));\n\t}\n\n\tprivate class Test1 {\n\n\t\tprivate ComponentRepository<ClusterRepoItem> repo1;\n\t\tprivate ComponentRepository<VHostItem> repo2;\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/util/updater/UpdatesCheckerTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.util.updater;\n\nimport org.junit.Ignore;\nimport org.junit.Test;\nimport tigase.server.XMPPServer;\nimport tigase.util.Version;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.logging.ConsoleHandler;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class UpdatesCheckerTest {\n\n\t@Test\n\t@Ignore\n\tpublic void retrieveVersion() {\n\n\t\tLogger log = Logger.getLogger(UpdatesChecker.class.getName());\n\t\tConsoleHandler ch = new ConsoleHandler();\n\t\tch.setLevel(Level.ALL);\n\t\tlog.addHandler(ch);\n\t\tlog.setLevel(Level.ALL);\n\n\t\tUpdatesChecker.ProductInfoIfc product = new TestProduct(\"my-awesome-product\", \"My Awesome Product\", \"1.2.3\");\n\t\tUpdatesChecker.ProductInfoIfc product2 = new TestProduct(\"iot-hub\", \"IoT hub\", \"7.7.7\");\n\t\tfinal List<UpdatesChecker.ProductInfoIfc> products = Arrays.asList(product, product2);\n\n//\t\tString url = \"http://atlantiscity.local:8080/rest/update/check/\";\n//\t\tString url = \"http://www.tigase.org/files/downloads/tigase-server/descript-redmine.ion\";\n\n\t\tString url = UpdatesChecker.VERSION_URL;\n\n\t\tfinal Version localVersion = XMPPServer.getVersion();\n\t\tfinal Optional<Version> version = UpdatesChecker.retrieveCurrentVersionFromServer(localVersion, products,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  url, 10);\n//\t\tSystem.out.println(version);\n\n\t}\n\n\tprivate class TestProduct\n\t\t\timplements UpdatesChecker.ProductInfoIfc {\n\n\t\tString id;\n\t\tString name;\n\t\tString version;\n\n\t\tpublic TestProduct(String id, String name, String version) {\n\t\t\tthis.id = id;\n\t\t\tthis.name = name;\n\t\t\tthis.version = version;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getProductId() {\n\t\t\treturn id;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getProductName() {\n\t\t\treturn name;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<String> getProductVersion() {\n\t\t\treturn Optional.ofNullable(version);\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/vhosts/DummyVHostManager.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.vhosts;\n\nimport tigase.server.ServerComponent;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Predicate;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\nimport java.util.stream.Collectors;\n\n/**\n * Dummy {@code VHostManagerIfc} implementation\n */\npublic class DummyVHostManager\n\t\timplements DefaultAwareVHostManagerIfc {\n\n\tprivate final static Logger LOG = Logger.getLogger(DummyVHostManager.class.getName());\n\tprivate final Map<String, VHostItem> items = new ConcurrentHashMap<>();\n\n\tpublic DummyVHostManager() {\n\t}\n\n\t@Override\n\tpublic void addComponentDomain(String domain) {\n\n\t}\n\n\tpublic void addVhost(String vhost) {\n\n\t\ttry {\n\t\t\tVHostItem item = new VHostItemImpl(vhost);\n\t\t\titems.put(vhost, item);\n\t\t} catch (TigaseStringprepException e) {\n\t\t\tLOG.log(Level.WARNING, \"Adding VHost failed\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic List<JID> getAllVHosts() {\n\t\treturn getAllVHosts(true);\n\t}\n\n\t@Override\n\tpublic List<JID> getAllVHosts(boolean includeDefaultVhost) {\n\t\treturn items.values().stream()\n\t\t\t\t.filter(vHostItem -> (includeDefaultVhost || !vHostItem.isDefault()))\n\t\t\t\t.map(VHostItem::getVhost)\n\t\t\t\t.collect(Collectors.toList());\n\t}\n\n\t@Override\n\tpublic ServerComponent[] getComponentsForLocalDomain(String domain) {\n\t\treturn new ServerComponent[0];\n\t}\n\n\t@Override\n\tpublic ServerComponent[] getComponentsForNonLocalDomain(String domain) {\n\t\treturn new ServerComponent[0];\n\t}\n\n\t@Override\n\tpublic BareJID getDefVHostItem() {\n\t\treturn items.values()\n\t\t\t\t.stream()\n\t\t\t\t.map(VHostItem::getVhost)\n\t\t\t\t.map(JID::toString)\n\t\t\t\t.map(BareJID::bareJIDInstanceNS)\n\t\t\t\t.findFirst()\n\t\t\t\t.orElse(BareJID.bareJIDInstanceNS(\"not@available\"));\n\t}\n\n\t@Override\n\tpublic VHostItem getVHostItem(String domain) {\n\t\treturn items.get(domain);\n\t}\n\n\t@Override\n\tpublic VHostItem getVHostItemDomainOrComponent(String domain) {\n\t\treturn items.get(domain);\n\t}\n\n\t@Override\n\tpublic boolean isAnonymousEnabled(String domain) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean isLocalDomain(String domain) {\n\t\treturn items.containsKey(domain);\n\t}\n\n\t@Override\n\tpublic boolean isLocalDomainOrComponent(String domain) {\n\t\treturn items.containsKey(domain);\n\t}\n\n\t@Override\n\tpublic void removeComponentDomain(String domain) {\n\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/vhosts/VHostItemTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts;\n\nimport junit.framework.TestCase;\nimport org.junit.Assert;\nimport tigase.TestLogger;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.vhosts.filter.DomainFilterPolicy;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.JID;\n\nimport java.lang.reflect.Field;\nimport java.util.Arrays;\nimport java.util.Optional;\nimport java.util.concurrent.ConcurrentSkipListSet;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * @author Wojciech Kapcia <wojciech.kapcia@tigase.org>\n */\npublic class VHostItemTest\n\t\textends TestCase {\n\n\tprivate static final Logger log = TestLogger.getLogger(VHostItemTest.class);\n\tprivate final VHostItemExtensionManager vHostItemExtensionManager = new VHostItemExtensionManager();\n\n\tpublic void testVHostItem() throws TigaseStringprepException {\n\t\tassertEquals(new VHostItemImpl(\"lowercase.com\"), new VHostItemImpl(\"lowercase.com\"));\n\t\tassertEquals(new VHostItemImpl(\"CAPITAL.COM\"), new VHostItemImpl(\"capital.com\"));\n\t\tassertNotSame(new VHostItemImpl(\"CAPITAL.COM\"), new VHostItemImpl(\"lowercase.com\"));\n\t}\n\n\tpublic void testVHostDomainPolicy() throws TigaseStringprepException {\n\n\t\tElement el;\n\t\tVHostItemImpl vHostItem;\n\n\t\tvHostItem = new VHostItemImpl();\n\t\tvHostItem.setExtensionManager(vHostItemExtensionManager);\n\t\tvHostItem.initFromPropertyString(\"domain1:domain-filter=LOCAL:max-users=1000\");\n\t\tassertEquals(DomainFilterPolicy.LOCAL, vHostItem.getDomainFilter());\n\t\tassertTrue(vHostItem.getDomainFilterDomains() == null);\n\n\t\tvHostItem = new VHostItemImpl();\n\t\tvHostItem.setExtensionManager(vHostItemExtensionManager);\n\t\tvHostItem.initFromPropertyString(\"domain1:domain-filter=LIST=domain1;domain2;domain3:max-users=1000\");\n\t\tassertEquals(DomainFilterPolicy.LIST, vHostItem.getDomainFilter());\n\t\tassertTrue(Arrays.asList(vHostItem.getDomainFilterDomains()).contains(\"domain1\"));\n\t\tassertTrue(Arrays.asList(vHostItem.getDomainFilterDomains()).contains(\"domain3\"));\n\t\tassertFalse(Arrays.asList(vHostItem.getDomainFilterDomains()).contains(\"domain5\"));\n\n\t\tvHostItem = new VHostItemImpl();\n\t\tvHostItem.setExtensionManager(vHostItemExtensionManager);\n\t\tel = new Element(\"vhost\", new String[]{\"hostname\", \"domain-filter\", \"domain-filter-domains\"},\n\t\t\t\t\t\t new String[]{\"domain3\", \"ALL\", \"domain1;domain2;domain3\"});\n\n\t\tvHostItem.initFromElement(el);\n\n\t\tassertEquals(DomainFilterPolicy.ALL, vHostItem.getDomainFilter());\n\t\tassertTrue(vHostItem.getDomainFilterDomains() == null);\n\t\tassertTrue(vHostItem.toPropertyString().contains(\"domain-filter=ALL\"));\n\n\t\tvHostItem = new VHostItemImpl();\n\t\tvHostItem.setExtensionManager(vHostItemExtensionManager);\n\t\tel = new Element(\"vhost\", new String[]{\"hostname\", \"domain-filter\", \"domain-filter-domains\"},\n\t\t\t\t\t\t new String[]{\"domain3\", \"BLACKLIST\", \"domain1;domain2;domain3\"});\n\n\t\tvHostItem.initFromElement(el);\n\t\tassertEquals(DomainFilterPolicy.BLACKLIST, vHostItem.getDomainFilter());\n\t\tassertTrue(Arrays.asList(vHostItem.getDomainFilterDomains()).contains(\"domain1\"));\n\t\tassertTrue(Arrays.asList(vHostItem.getDomainFilterDomains()).contains(\"domain3\"));\n\t\tassertFalse(Arrays.asList(vHostItem.getDomainFilterDomains()).contains(\"domain5\"));\n\t\tassertTrue(vHostItem.toPropertyString().contains(\"domain-filter=BLACKLIST\"));\n\n\t\tvHostItem = new VHostItemImpl();\n\t\tvHostItem.setExtensionManager(vHostItemExtensionManager);\n\t\tel = new Element(\"vhost\", new String[]{\"hostname\", \"domain-filter\", \"domain-filter-domains\"},\n\t\t\t\t\t\t new String[]{\"domain3\", \"CUSTOM\",\n\t\t\t\t\t\t\t\t\t  \"4,deny,all;1,allow,self;3,allow,jid,pubsub@test.com;2,allow,jid,admin@test2.com\"});\n\n\t\tvHostItem.initFromElement(el);\n\t\tassertEquals(DomainFilterPolicy.CUSTOM, vHostItem.getDomainFilter());\n\t\tassertTrue(vHostItem.toPropertyString()\n\t\t\t\t\t\t   .contains(\n\t\t\t\t\t\t\t\t   \"domain-filter=CUSTOM=4,deny,all;1,allow,self;3,allow,jid,pubsub@test.com;2,allow,jid,admin@test2.com\"));\n\n\t\tvHostItem = new VHostItemImpl();\n\t\tvHostItem.setExtensionManager(vHostItemExtensionManager);\n\t\tvHostItem.initFromPropertyString(\n\t\t\t\t\"domain1:domain-filter=CUSTOM=4|deny|all;1|allow|self;3|allow|jid|pubsub@test.com;2|allow|jid|admin@test2.com\");\n\t\tassertEquals(DomainFilterPolicy.CUSTOM, vHostItem.getDomainFilter());\n\t\tfinal String toPropertyString = vHostItem.toPropertyString();\n\t\tlog.log(Level.FINE, \"to property string: \" + toPropertyString);\n\t\tassertTrue(\"different\", toPropertyString.contains(\n\t\t\t\t\"domain-filter=CUSTOM=4|deny|all;1|allow|self;3|allow|jid|pubsub@test.com;2|allow|jid|admin@test2.com\"));\n\n\t}\n\n\tpublic void testInitFromPropertyString()\n\t\t\tthrows TigaseStringprepException, IllegalAccessException, NoSuchFieldException {\n\t\tJID jid = JID.jidInstanceNS(\"comp1@example.com\");\n\t\tJID notTrusted = JID.jidInstanceNS(\"not-trusted@example.com\");\n\n\t\tVHostItemImpl item = new VHostItemImpl();\n\t\titem.setExtensionManager(vHostItemExtensionManager);\n\t\titem.toString();\n\t\tAssert.assertTrue(item.getTrustedJIDs().isEmpty());\n\t\tAssert.assertFalse(item.isTrustedJID(jid));\n\n\t\titem.initFromPropertyString(\"example.com:trusted-jids=comp1@example.com\");\n\t\tAssert.assertArrayEquals(new String[]{jid.toString()}, Optional.ofNullable(item.getTrustedJIDs())\n\t\t\t\t.map(it -> it.toArray(new String[0]))\n\t\t\t\t.orElse(new String[0]));\n\t\tAssert.assertTrue(item.isTrustedJID(jid));\n\t\tAssert.assertTrue(item.isTrustedJID(jid.copyWithResource(\"test\")));\n\t\tAssert.assertFalse(item.isTrustedJID(notTrusted));\n\n\t\titem = new VHostItemImpl();\n\t\titem.setExtensionManager(vHostItemExtensionManager);\n\t\titem.initFromPropertyString(\"example.com:trusted-jids=comp1@example.com,comp2@example.com\");\n\t\tAssert.assertArrayEquals(new String[]{jid.toString(), \"comp2@example.com\"},\n\t\t\t\t\t\t\t\t item.getTrustedJIDs().stream().sorted().toArray(String[]::new));\n\t\tAssert.assertTrue(item.isTrustedJID(jid));\n\t\tAssert.assertTrue(item.isTrustedJID(jid.copyWithResource(\"test\")));\n\t\tAssert.assertFalse(item.isTrustedJID(notTrusted));\n\n\t\titem = new VHostItemImpl();\n\t\titem.setExtensionManager(vHostItemExtensionManager);\n\t\titem.initFromPropertyString(\"example.com:trusted-jids=comp1@example.com;comp2@example.com\");\n\t\tAssert.assertArrayEquals(new String[]{jid.toString(), \"comp2@example.com\"},\n\t\t\t\t\t\t\t\t item.getTrustedJIDs().stream().sorted().toArray(String[]::new));\n\t\tAssert.assertTrue(item.isTrustedJID(jid));\n\t\tAssert.assertTrue(item.isTrustedJID(jid.copyWithResource(\"test\")));\n\t\tAssert.assertFalse(item.isTrustedJID(notTrusted));\n\n\t\titem = new VHostItemImpl();\n\t\titem.setExtensionManager(vHostItemExtensionManager);\n\t\titem.toString();\n\t\titem.initFromPropertyString(\"example.com:trusted-jids=example.com\");\n\t\titem.toString();\n\t\tAssert.assertArrayEquals(new String[]{\"example.com\"}, item.getTrustedJIDs().toArray(new String[0]));\n\t\tAssert.assertTrue(item.isTrustedJID(jid));\n\t\tAssert.assertTrue(item.isTrustedJID(jid.copyWithResource(\"test\")));\n\t\tAssert.assertTrue(item.isTrustedJID(notTrusted));\n\n\t\t//System.setProperty(\"trusted\", \"comp3@example.com,comp4@example.com\");\n\t\tVHostItemDefaults defaults = new VHostItemDefaults();\n\t\tField f = VHostItemDefaults.class.getDeclaredField(\"trusted\");\n\t\tf.setAccessible(true);\n\t\tf.set(defaults, new ConcurrentSkipListSet<String>());\n\t\tdefaults.getTrusted().add(\"comp3@example.com\");\n\t\tdefaults.getTrusted().add(\"comp4@example.com\");\n\t\titem = new VHostItemImpl();\n\t\titem.setExtensionManager(vHostItemExtensionManager);\n\t\titem.initializeFromDefaults(defaults);\n\t\titem.toString();\n\t\tAssert.assertArrayEquals(new String[]{\"comp3@example.com\", \"comp4@example.com\"},\n\t\t\t\t\t\t\t\t item.getTrustedJIDs().stream().sorted().toArray(String[]::new));\n\t\tAssert.assertTrue(item.isTrustedJID(JID.jidInstanceNS(\"comp3@example.com\")));\n\t\tAssert.assertTrue(item.isTrustedJID(JID.jidInstanceNS(\"comp3@example.com\").copyWithResource(\"test\")));\n\t\tAssert.assertFalse(item.isTrustedJID(notTrusted));\n\n\t\titem.initFromPropertyString(\"example.com:trusted-jids=comp1@example.com;comp2@example.com\");\n\t\tAssert.assertArrayEquals(new String[]{jid.toString(), \"comp2@example.com\"},\n\t\t\t\t\t\t\t\t item.getTrustedJIDs().stream().sorted().toArray(String[]::new));\n\t\tAssert.assertTrue(item.isTrustedJID(jid));\n\t\tAssert.assertTrue(item.isTrustedJID(jid.copyWithResource(\"test\")));\n\t\tAssert.assertFalse(item.isTrustedJID(notTrusted));\n\t\tAssert.assertFalse(item.isTrustedJID(JID.jidInstanceNS(\"comp3@example.com\")));\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/vhosts/VHostJDBCRepositoryTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts;\n\nimport org.junit.*;\nimport tigase.TestLogger;\nimport tigase.component.DSLBeanConfiguratorWithBackwardCompatibility;\nimport tigase.conf.LoggingBean;\nimport tigase.db.TigaseDBException;\nimport tigase.db.xml.XMLRepository;\nimport tigase.kernel.DefaultTypesConverter;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Command;\nimport tigase.server.DataForm;\nimport tigase.server.Packet;\nimport tigase.util.dns.DNSEntry;\nimport tigase.util.dns.DNSResolverDefault;\nimport tigase.util.dns.DNSResolverFactory;\nimport tigase.util.dns.DNSResolverIfc;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.DomBuilderHandler;\nimport tigase.xml.Element;\nimport tigase.xml.SimpleParser;\nimport tigase.xml.SingletonFactory;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.jid.JID;\n\nimport java.net.UnknownHostException;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static org.junit.Assert.*;\nimport static tigase.vhosts.VHostItemImpl.*;\n\npublic class VHostJDBCRepositoryTest {\n\n\tfinal static String mainVHost = \"domain.com\";\n\tfinal static String defaultConfigDomainName = \"default\";\n\tprotected static Kernel kernel;\n\tstatic Logger log;\n\tstatic XMLRepository repository;\n\tstatic TestVHostJDBCRepository vHostJDBCRepository;\n\n\t@AfterClass\n\tpublic static void resetDNSResolver() {\n\t\tDNSResolverFactory.setDnsResolverClassName(DNSResolverDefault.class.getCanonicalName());\n\t}\n\n\t@BeforeClass\n\tpublic static void setup() {\n\n\t\tDNSResolverFactory.setDnsResolverClassName(PassThroughDNSResolver.class.getName());\n\n\t\tlog = TestLogger.getLogger(VHostJDBCRepositoryTest.class);\n\t\tTestLogger.configureLogger(log, Level.OFF);\n\n\t\tTestLogger.configureLogger(Logger.getLogger(\"tigase\"), Level.OFF);\n\n\t\tMap<String, Object> props = new HashMap<>();\n\t\tprops.put(\"name\", \"VHost\");\n\t\tprops.put(\"default-virtual-host\", mainVHost);\n\n\t\tkernel = new Kernel();\n\t\tkernel.setName(\"VHost\");\n\t\tkernel.setForceAllowNull(true);\n\t\tkernel.registerBean(DefaultTypesConverter.class).exec();\n\t\tkernel.registerBean(DSLBeanConfiguratorWithBackwardCompatibility.class).exportable().exec();\n\t\tfinal DSLBeanConfiguratorWithBackwardCompatibility config = kernel.getInstance(\n\t\t\t\tDSLBeanConfiguratorWithBackwardCompatibility.class);\n\t\tconfig.setProperties(props);\n\t\tkernel.registerBean(\"vHostJDBCRepository\")\n\t\t\t\t.asClass(TestVHostJDBCRepository.class)\n\t\t\t\t.exportable()\n\t\t\t\t.setActive(true)\n\t\t\t\t.exec();\n\t\tkernel.registerBean(VHostItemExtensionManager.class).exportable().setActive(true).exec();\n\t\tkernel.registerBean(VHostItemDefaults.class).exportable().setActive(true).exec();\n\t\tkernel.registerBean(\"repo\").asClass(XMLRepository.class).exportable().setActive(true).exec();\n\t\tkernel.registerBean(\"logging\").asClass(LoggingBean.class).setActive(true).setPinned(true).exec();\n\n\t\ttry {\n\t\t\tfinal VHostItemExtensionManager extensionManager = kernel.getInstance(VHostItemExtensionManager.class);\n\t\t\textensionManager.setProviders(new VHostItemExtensionProvider[]{new TestVHostExtensionProvider()});\n\t\t\trepository = kernel.getInstance(XMLRepository.class);\n\t\t\trepository.initRepository(\"memory://\", new ConcurrentHashMap<>());\n\t\t\tvHostJDBCRepository = kernel.getInstance(TestVHostJDBCRepository.class);\n\t\t} catch (Exception ex) {\n\t\t\tlog.log(Level.WARNING, ex, () -> \"There was an error setting up test\");\n\t\t}\n\n//\t\tTestLogger.configureLogger(Logger.getLogger(\"tigase\"), Level.FINE);\n\t}\n\n\t@After\n\tpublic void cleanup() {\n\t\tvHostJDBCRepository.removeItem(defaultConfigDomainName);\n\t}\n\n\t@Before\n\tpublic void setupDefault() {\n\t\tfinal VHostItem instance = vHostJDBCRepository.getItemInstance();\n\t\tinstance.setKey(defaultConfigDomainName);\n\t\tvHostJDBCRepository.addItem(instance);\n\t}\n\n\t@Test\n\tpublic void testDomainNameCases() throws TigaseStringprepException {\n\t\tString domain = UUID.randomUUID().toString();\n\t\tVHostItem vHostItem = new VHostItemImpl(domain);\n\t\tvHostJDBCRepository.addItem(vHostItem);\n\t\tassertEquals(vHostItem, vHostJDBCRepository.getItem(domain.toUpperCase()));\n\t}\n\n\t@Test\n\tpublic void testMinimalItemFromCommand() throws TigaseDBException, TigaseStringprepException {\n\t\tString domain = UUID.randomUUID().toString();\n\t\tElement x = Command.createIqCommand(JID.jidInstanceNS(\"test@domain.com\"), null, StanzaType.set,\n\t\t\t\t\t\t\t\t\t\t\tUUID.randomUUID().toString(), \"x\", Command.DataType.submit);\n\t\tPacket packet = Packet.packetInstance(x);\n\t\tCommand.addFieldValue(packet, HOSTNAME_LABEL, domain);\n\t\tCommand.addCheckBoxField(packet, ENABLED_LABEL, true);\n\t\tpacket.initVars();\n\n\t\tfinal VHostItem domainItemInstance = vHostJDBCRepository.getItemInstance();\n\t\tdomainItemInstance.initFromCommand(packet);\n\t\tfinal String validateItem = vHostJDBCRepository.validateItem(domainItemInstance);\n\t\tassertNull(validateItem);\n\t}\n\n\t@Test\n\tpublic void testItemLoading() throws TigaseDBException, TigaseStringprepException {\n\t\tString domain = UUID.randomUUID().toString();\n\t\tfinal VHostItem domainItemInstance = vHostJDBCRepository.getItemInstance();\n\t\tdomainItemInstance.setKey(domain);\n\t\tvHostJDBCRepository.addItem(domainItemInstance);\n\t\tOptional<Element> defaultVHostElement = getVHostElementFromRepository(defaultConfigDomainName);\n\t\tassertTrue(defaultVHostElement.isPresent());\n\t\tassertNull(defaultVHostElement.get().getChildren());\n\t\tOptional<Element> domainVHostElement = getVHostElementFromRepository(domain);\n\t\tassertTrue(domainVHostElement.isPresent());\n\t\tassertNull(domainVHostElement.get().getChildren());\n\n\t\tlog.fine(\"By default extension is enabled for both domain and as a default\");\n\t\tverifyDomainStateInRepository(domain, true);\n\t\tverifyDomainStateInStore(domain, true);\n\t\tverifyDomainStateInRepository(defaultConfigDomainName, true);\n\t\tverifyDomainStateInStore(defaultConfigDomainName, true);\n\t\tassertTrue(vHostJDBCRepository.getItem(domain).isTlsRequired());\n\n\t\tlog.fine(\"let's disable extension\");\n\t\tsetExtensionStateForDomain(domain, false);\n\n\t\tlog.fine(\"extension is still enabled in 'default' it should be true here (effective value in Wrapper)\");\n\t\tverifyDomainStateInRepository(domain, true);\n\t\tassertTrue(vHostJDBCRepository.getItem(domain).isTlsRequired());\n\n\t\tlog.fine(\"...but in repository we store current, correct domain setting\");\n\t\tverifyDomainStateInStore(domain, false);\n\n\t\tlog.fine(\"let's disable extension for default domain\");\n\t\tsetExtensionStateForDomain(defaultConfigDomainName, false);\n\n\t\tlog.fine(\"it should now be disabled everywhere\");\n\t\tverifyDomainStateInRepository(defaultConfigDomainName, false);\n\t\tverifyDomainStateInStore(defaultConfigDomainName, false);\n\t\tverifyDomainStateInRepository(domain, false);\n\t\tverifyDomainStateInStore(domain, false);\n\t\tassertTrue(vHostJDBCRepository.getItem(domain).isTlsRequired());\n\n\t\tlog.fine(\"let's enabled it again for domain\");\n\t\tsetExtensionStateForDomain(domain, true);\n\n\t\tlog.fine(\"it should be enabled for domain (including repository)\");\n\t\tverifyDomainStateInRepository(domain, true);\n\t\tverifyDomainStateInStore(domain, true);\n\t\tassertTrue(vHostJDBCRepository.getItem(domain).isTlsRequired());\n\n\t\tlog.fine(\"...but still disabled for default\");\n\t\tverifyDomainStateInRepository(defaultConfigDomainName, false);\n\t\tverifyDomainStateInStore(defaultConfigDomainName, false);\n\t\tassertTrue(vHostJDBCRepository.getItem(domain).isTlsRequired());\n\n\t}\n\n\t@Test\n\tpublic void testTLSSettings() throws TigaseDBException, TigaseStringprepException {\n\t\tString domain = UUID.randomUUID().toString();\n\t\tfinal VHostItem domainItemInstance = vHostJDBCRepository.getItemInstance();\n\t\tdomainItemInstance.setKey(domain);\n\t\tvHostJDBCRepository.addItem(domainItemInstance);\n\t\tOptional<Element> defaultVHostElement = getVHostElementFromRepository(defaultConfigDomainName);\n\t\tassertTrue(defaultVHostElement.isPresent());\n\t\tassertNull(defaultVHostElement.get().getChildren());\n\t\tOptional<Element> domainVHostElement = getVHostElementFromRepository(domain);\n\t\tassertTrue(domainVHostElement.isPresent());\n\t\tassertNull(domainVHostElement.get().getChildren());\n\n\t\tlog.fine(\"By default TLS is enabled for both domain and as a default\");\n\t\tassertTrue(vHostJDBCRepository.getItem(defaultConfigDomainName).isTlsRequired());\n\t\tassertTrue(vHostJDBCRepository.getItem(domain).isTlsRequired());\n\n\t\tsetTLSRequiredStateForDomain(domain, false);\n\n\t\tlog.fine(\"given, that TLS is still enabled in 'default' it should be true here (effective value in Wrapper)\");\n\t\tassertTrue(vHostJDBCRepository.getItem(domain).isTlsRequired());\n\n\t\tlog.fine(\"let's disable TLS for default domain\");\n\t\tsetTLSRequiredStateForDomain(defaultConfigDomainName, false);\n\n\t\tlog.fine(\"it should now be disabled everywhere\");\n\t\tassertFalse(vHostJDBCRepository.getItem(defaultConfigDomainName).isTlsRequired());\n\t\tassertFalse(vHostJDBCRepository.getItem(domain).isTlsRequired());\n\n\t\tlog.fine(\"let's enabled it again for domain\");\n\t\tsetTLSRequiredStateForDomain(domain, true);\n\n\t\tlog.fine(\"it should be enabled for domain\");\n\t\tassertTrue(vHostJDBCRepository.getItem(domain).isTlsRequired());\n\n\t\tlog.fine(\"...but still disabled for default\");\n\t\tassertFalse(vHostJDBCRepository.getItem(defaultConfigDomainName).isTlsRequired());\n\t}\n\n\tOptional<Element> getVHostElementFromRepository(String domain) throws TigaseDBException {\n\t\tassertNotNull(domain);\n\t\tString items_list = repository.getData(vHostJDBCRepository.getRepoUser(),\n\t\t\t\t\t\t\t\t\t\t\t   vHostJDBCRepository.getItemsListPKey());\n\t\tif (!items_list.isEmpty()) {\n\t\t\tDomBuilderHandler domHandler = new DomBuilderHandler();\n\t\t\tSimpleParser parser = SingletonFactory.getParserInstance();\n\n\t\t\tparser.parse(domHandler, items_list);\n\n\t\t\tQueue<Element> elems = domHandler.getParsedElements();\n\t\t\treturn elems.stream().filter(element -> domain.equals(element.getAttributeStaticStr(\"hostname\"))).findAny();\n\t\t} else {\n\t\t\treturn Optional.empty();\n\t\t}\n\t}\n\n\tprivate void verifyDomainStateInRepository(String domain, boolean state) throws TigaseDBException {\n\t\tfinal VHostItem item = vHostJDBCRepository.getItem(domain);\n\t\tTestVHostExtension extension = item.getExtension(TestVHostExtension.class);\n\t\tlog.log(Level.FINE, \"Verifying state: '\" + state + \"' for domain: '\" + domain + \"' with item: \" + item +\n\t\t\t\t\"; Testing extension: \" + extension);\n\t\tassertNotNull(extension);\n\t\tassertEquals(state, extension.isEnabled());\n\t\tfinal String itemString = item.toString();\n\t\tif (state) {\n\t\t\tassertTrue(itemString.contains(\"TestVHostExtension(enabled: true)\") ||\n\t\t\t\t\t\t\t   !itemString.contains(\"TestVHostExtension\"));\n\t\t} else {\n\t\t\tassertTrue(itemString.contains(\"TestVHostExtension(enabled: false)\"));\n\t\t}\n\t}\n\n\tprivate void verifyDomainStateInStore(String domain, boolean state) throws TigaseDBException {\n\t\tOptional<Element> domainVHostElement = getVHostElementFromRepository(domain);\n\t\tassertTrue(domainVHostElement.isPresent());\n\t\tElement testElementInVHost = domainVHostElement.get().getChild(\"test\");\n\t\tlog.log(Level.FINE, \"Verifying in store state: \" + state + \" for domain: \" + domain + \" with element: \" +\n\t\t\t\tdomainVHostElement);\n\t\tif (state) {\n\t\t\tassertNull(testElementInVHost);\n\t\t} else {\n\t\t\tassertNotNull(testElementInVHost);\n\t\t\tassertEquals(String.valueOf(state), testElementInVHost.getAttributeStaticStr(\"enabled\"));\n\t\t}\n\t}\n\n\tprivate VHostItem setExtensionStateForDomain(String domain, boolean state) throws TigaseStringprepException {\n\t\tlog.log(Level.FINE, \"Setting domain: '\" + domain + \"' extension state to: \" + state);\n\t\tElement x = Command.createIqCommand(JID.jidInstanceNS(\"test@domain.com\"), null, StanzaType.set,\n\t\t\t\t\t\t\t\t\t\t\tUUID.randomUUID().toString(), \"x\", Command.DataType.submit);\n\t\tPacket packet = Packet.packetInstance(x);\n\t\tCommand.addFieldValue(packet, HOSTNAME_LABEL, domain);\n\t\tTestVHostExtension extension = new TestVHostExtension(state);\n\t\textension.addCommandFields(extension.getId(), packet, false);\n\t\tpacket.initVars();\n\t\tVHostItem vHostItem = vHostJDBCRepository.getItemInstance();\n\t\tvHostItem.initFromCommand(packet);\n\t\tvHostJDBCRepository.addItem(vHostItem);\n\t\treturn vHostItem;\n\t}\n\n\tprivate VHostItem setTLSRequiredStateForDomain(String domain, boolean state) throws TigaseStringprepException {\n\t\tlog.log(Level.FINE, \"Setting domain: '\" + domain + \"' TLS-required state to: \" + state);\n\t\tElement x = Command.createIqCommand(JID.jidInstanceNS(\"test@domain.com\"), null, StanzaType.set,\n\t\t\t\t\t\t\t\t\t\t\tUUID.randomUUID().toString(), \"x\", Command.DataType.submit);\n\t\tPacket packet = Packet.packetInstance(x);\n\t\tCommand.addFieldValue(packet, HOSTNAME_LABEL, domain);\n\t\tCommand.addCheckBoxField(packet, TLS_REQUIRED_LABEL, state);\n\t\tpacket.initVars();\n\t\tVHostItem vHostItem = vHostJDBCRepository.getItemInstance();\n\t\tvHostItem.initFromCommand(packet);\n\t\tvHostJDBCRepository.addItem(vHostItem);\n\t\treturn vHostItem;\n\t}\n\n\tpublic static class PassThroughDNSResolver\n\t\t\timplements DNSResolverIfc {\n\n\t\tpublic PassThroughDNSResolver() {\n\t\t}\n\n\t\t@Override\n\t\tpublic DNSEntry[] getHostSRV_Entries(String hostname) throws UnknownHostException {\n\t\t\treturn new DNSEntry[]{new DNSEntry(hostname, hostname),\n\t\t\t\t\t\t\t\t  new DNSEntry(defaultConfigDomainName, defaultConfigDomainName)};\n\t\t}\n\n\t\t@Override\n\t\tpublic String getDefaultHost() {\n\t\t\treturn defaultConfigDomainName;\n\t\t}\n\n\t\t@Override\n\t\tpublic String[] getHostIPs(String s) throws UnknownHostException {\n\t\t\treturn new String[]{s, defaultConfigDomainName};\n\t\t}\n\t}\n\n\tpublic static class TestVHostExtension\n\t\t\textends AbstractVHostItemExtension<TestVHostExtension> {\n\n\t\tpublic static final String ID = \"test\";\n\t\tprivate final static boolean ENABLED_DEFAULT_VAL = true;\n\t\tprivate boolean enabled = true;\n\n\t\tpublic TestVHostExtension() {\n\t\t}\n\n\t\tpublic TestVHostExtension(boolean enabled) {\n\t\t\tthis.enabled = enabled;\n\t\t}\n\n\t\tpublic boolean isEnabled() {\n\t\t\treturn enabled;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getId() {\n\t\t\treturn ID;\n\t\t}\n\n\t\t@Override\n\t\tpublic void initFromElement(Element item) {\n\t\t\tfinal String enabledAttr = item.getAttributeStaticStr(\"enabled\");\n\t\t\tthis.enabled = enabledAttr == null ? ENABLED_DEFAULT_VAL : Boolean.parseBoolean(enabledAttr);\n\t\t}\n\n\t\t@Override\n\t\tpublic void initFromCommand(String prefix, Packet packet) throws IllegalArgumentException {\n\t\t\tOptional.ofNullable(Command.getFieldValue(packet, prefix + \"-enabled\"))\n\t\t\t\t\t.ifPresent(s -> enabled = Boolean.parseBoolean(s));\n\t\t}\n\n\t\t@Override\n\t\tpublic String toDebugString() {\n\t\t\treturn \"enabled: \" + enabled;\n\t\t}\n\n\t\t@Override\n\t\tpublic Element toElement() {\n\t\t\treturn enabled == ENABLED_DEFAULT_VAL\n\t\t\t\t   ? null\n\t\t\t\t   : new Element(ID, new String[]{\"enabled\"}, new String[]{String.valueOf(enabled)});\n\t\t}\n\n\t\t@Override\n\t\tpublic void addCommandFields(String prefix, Packet packet, boolean forDefault) {\n\t\t\tElement commandEl = packet.getElemChild(Command.COMMAND_EL, Command.XMLNS);\n\t\t\tDataForm.addFieldValue(commandEl, prefix + \"-enabled\", String.valueOf(enabled), \"boolean\",\n\t\t\t\t\t\t\t\t   \"Extension Enabled\");\n\t\t}\n\n\t\t@Override\n\t\tpublic TestVHostExtension mergeWithDefaults(TestVHostExtension defaults) {\n\t\t\treturn new TestVHostExtension(this.enabled || defaults.enabled);\n\t\t}\n\t}\n\n\tpublic static class TestVHostExtensionProvider\n\t\t\timplements VHostItemExtensionProvider<TestVHostExtension> {\n\n\t\t@Override\n\t\tpublic String getId() {\n\t\t\treturn TestVHostExtension.ID;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<TestVHostExtension> getExtensionClazz() {\n\t\t\treturn TestVHostExtension.class;\n\t\t}\n\t}\n\n\tpublic static class TestVHostJDBCRepository\n\t\t\textends VHostJDBCRepository {\n\n\t\tpublic TestVHostJDBCRepository() {\n\t\t}\n\n\t\t@Override\n\t\tpublic void setAutoloadTimer(long delay) {\n\t\t\tsuper.setAutoloadTimer(0);\n\t\t}\n\n\t\t@Override\n\t\tpublic void setAutoReloadInterval(long autoLoadInterval) {\n\t\t\tsuper.setAutoReloadInterval(0);\n\t\t}\n\n\t\tvoid reinitialiseRepository() {\n\t\t\titems.clear();\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/vhosts/filter/CustomDomainFilterTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts.filter;\n\nimport org.junit.Before;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport tigase.TestLogger;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.vhosts.filter.Rule.RuleType;\nimport tigase.xmpp.jid.JID;\n\nimport java.text.ParseException;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static org.junit.Assert.*;\n\n/**\n * @author Wojtek\n */\npublic class CustomDomainFilterTest {\n\n\tprivate static final Logger log = TestLogger.getLogger(CustomDomainFilterTest.class);\n\tString[] rules = {\"4|deny|all\", \"1|allow|self\", \"3|allow|jid|pubsub@test.com\", \"2|allow|jid|admin@test2.com\",};\n\n\t@BeforeClass\n\tpublic static void setUpClass() {\n\t}\n\n\tpublic CustomDomainFilterTest() {\n\t}\n\n\t@Before\n\tpublic void setUp() {\n\t}\n\n\t@Test\n\tpublic void testParseRules() throws TigaseStringprepException, ParseException {\n\t\tlog.log(Level.FINE, \"parseRules\");\n\n\t\tSet<Rule> expResult = new TreeSet<>();\n\t\tRule rule = new Rule(1, true, RuleType.self, null);\n\n\t\tif (rule != null) {\n\t\t\texpResult.add(rule);\n\t\t}\n\t\trule = new Rule(2, true, RuleType.jid, JID.jidInstance(\"admin@test2.com\"));\n\t\tif (rule != null) {\n\t\t\texpResult.add(rule);\n\t\t}\n\t\trule = new Rule(3, true, RuleType.jid, JID.jidInstance(\"pubsub@test.com\"));\n\t\tif (rule != null) {\n\t\t\texpResult.add(rule);\n\t\t}\n\t\trule = new Rule(4, false, RuleType.all, null);\n\t\tif (rule != null) {\n\t\t\texpResult.add(rule);\n\t\t}\n\n\t\tSet<Rule> result = CustomDomainFilter.parseRules(rules);\n\t\tassertEquals(expResult, result);\n\t}\n\n\t@Test\n\tpublic void testParseRulesString() throws TigaseStringprepException, ParseException {\n\t\tlog.log(Level.FINE, \"parseRules\");\n\t\tString rulseString = \"4|deny|all;1|allow|self;3|allow|jid|pubsub@test.com;2|allow|jid|admin@test2.com\";\n\n\t\tSet<Rule> expResult = new TreeSet<>();\n\t\tRule rule = new Rule(1, true, RuleType.self, null);\n\n\t\tif (rule != null) {\n\t\t\texpResult.add(rule);\n\t\t}\n\t\trule = new Rule(2, true, RuleType.jid, JID.jidInstance(\"admin@test2.com\"));\n\t\tif (rule != null) {\n\t\t\texpResult.add(rule);\n\t\t}\n\t\trule = new Rule(3, true, RuleType.jid, JID.jidInstance(\"pubsub@test.com\"));\n\t\tif (rule != null) {\n\t\t\texpResult.add(rule);\n\t\t}\n\t\trule = new Rule(4, false, RuleType.all, null);\n\t\tif (rule != null) {\n\t\t\texpResult.add(rule);\n\t\t}\n\n\t\tSet<Rule> result = CustomDomainFilter.parseRules(rulseString);\n\t\tassertEquals(expResult, result);\n\n\t\trulseString = \"1|allow|self;2|allow|jid|admin@test2.com;3|allow|jid|pubsub@test.com;4|deny|all;\";\n\t\tString resultString = new String();\n\t\tfor (Rule res : result) {\n\t\t\tresultString += res.toConfigurationString();\n\t\t}\n\t\tassertEquals(rulseString, resultString);\n\n\t}\n\n\t@Test(expected = ParseException.class)\n\tpublic void testParseRulesException() throws TigaseStringprepException, ParseException {\n\t\tString[] rules_fail = {\"8|deny|||self,\", \"|||18|||deny,self::::\"};\n\t\tSet<Rule> result = CustomDomainFilter.parseRules(rules_fail);\n\t}\n\n\t@Test\n\tpublic void testIsAllowed() throws TigaseStringprepException, ParseException {\n\n\t\tJID jid1_r1 = JID.jidInstance(\"user1\", \"domain1\", \"resource1\");\n\t\tJID jid1_r2 = JID.jidInstance(\"user1\", \"domain1\", \"resource2\");\n\t\tJID jid2_r1 = JID.jidInstance(\"user2\", \"domain1\", \"resource1\");\n\t\tJID jid3_r1 = JID.jidInstance(\"user3\", \"domain1\", \"resource1\");\n\t\tJID admin = JID.jidInstance(\"admin\", \"test2.com\");\n\t\tJID pubsub = JID.jidInstance(\"pubsub\", \"test.com\");\n\n\t\tboolean allowed = CustomDomainFilter.isAllowed(jid1_r1, jid1_r2, rules);\n\t\tassertTrue(\"should be allowed / self / permitted jid\", allowed);\n\n\t\tallowed = CustomDomainFilter.isAllowed(jid1_r1, admin, rules);\n\t\tassertTrue(\"should be allowed / permitted jid\", allowed);\n\n\t\tallowed = CustomDomainFilter.isAllowed(jid1_r1, pubsub, rules);\n\t\tassertTrue(\"should be allowed / permitted jid\", allowed);\n\n\t\tallowed = CustomDomainFilter.isAllowed(jid1_r1, jid2_r1, rules);\n\t\tassertFalse(\"should be denyed / permitted jid\", allowed);\n\n\t\tallowed = CustomDomainFilter.isAllowed(jid2_r1, jid2_r1, rules);\n\t\tassertTrue(\"should be allowed / self\", allowed);\n\n\t\tallowed = CustomDomainFilter.isAllowed(jid3_r1, jid2_r1, rules);\n\t\tassertFalse(\"should be denied / not permitted jids\", allowed);\n\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/vhosts/filter/DomainFilterPolicyTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.vhosts.filter;\n\nimport org.junit.Test;\nimport tigase.TestLogger;\n\nimport java.util.HashSet;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static org.junit.Assert.assertEquals;\n\n/**\n * @author Wojtek\n */\npublic class DomainFilterPolicyTest {\n\n\tprivate static final Logger log = TestLogger.getLogger(DomainFilterPolicyTest.class);\n\n\tpublic DomainFilterPolicyTest() {\n\t}\n\n\t@Test\n\tpublic void testValuePoliciesWithDomainListStr() {\n\t\tlog.log(Level.FINE, \"valuePoliciesWithDomainListStr\");\n\t\tHashSet<String> result = DomainFilterPolicy.valuePoliciesWithDomainListStr();\n\t\tassertEquals(true, result.contains(DomainFilterPolicy.CUSTOM.name()));\n\t\tassertEquals(true, result.contains(DomainFilterPolicy.LIST.name()));\n\t\tassertEquals(true, result.contains(DomainFilterPolicy.BLACKLIST.name()));\n\t\tassertEquals(false, result.contains(DomainFilterPolicy.ALL.name()));\n\t\tassertEquals(false, result.contains(DomainFilterPolicy.BLOCK.name()));\n\t\tassertEquals(false, result.contains(DomainFilterPolicy.LOCAL.name()));\n\t\tassertEquals(false, result.contains(DomainFilterPolicy.OWN.name()));\n\t}\n\n\t@Test\n\tpublic void testIsDomainListRequired() {\n\t\tlog.log(Level.FINE, \"isDomainListRequired\");\n\t\tassertEquals(false, DomainFilterPolicy.ALL.isDomainListRequired());\n\t\tassertEquals(true, DomainFilterPolicy.BLACKLIST.isDomainListRequired());\n\t\tassertEquals(false, DomainFilterPolicy.BLOCK.isDomainListRequired());\n\t\tassertEquals(true, DomainFilterPolicy.CUSTOM.isDomainListRequired());\n\t\tassertEquals(true, DomainFilterPolicy.LIST.isDomainListRequired());\n\t\tassertEquals(false, DomainFilterPolicy.LOCAL.isDomainListRequired());\n\t\tassertEquals(false, DomainFilterPolicy.OWN.isDomainListRequired());\n\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/ElementMatcherTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport org.junit.Test;\nimport tigase.server.Packet;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\n\nimport static org.junit.Assert.*;\n\npublic class ElementMatcherTest {\n\n\t@Test\n\tpublic void testXMLNSOnly() throws TigaseStringprepException {\n\t\tString matcherStr = \"[urn:ietf:params:xml:ns:xmpp-sasl]\";\n\t\tElementMatcher matcher = ElementMatcher.create(matcherStr);\n\t\tassertNotNull(matcher);\n\n\t\tassertEquals(matcherStr, matcher.toString());\n\n\t\tPacket packet = Packet.packetInstance(new Element(\"message\", new String[] { \"xmlns\" }, new String[] { \"jabber:client\" }));\n\t\tassertFalse(matcher.matches(packet));\n\t\tpacket = Packet.packetInstance(new Element(\"message\", new String[] { \"xmlns\" }, new String[] { \"urn:ietf:params:xml:ns:xmpp-sasl\" }));\n\t\tassertTrue(matcher.matches(packet));\n\t}\n\n\t@Test\n\tpublic void testXMLNSAndType() throws TigaseStringprepException {\n\t\tString matcherStr = \"[urn:ietf:params:xml:ns:xmpp-sasl,type=headline]\";\n\t\tElementMatcher matcher = ElementMatcher.create(matcherStr);\n\t\tassertNotNull(matcher);\n\n\t\tassertEquals(matcherStr, matcher.toString());\n\n\t\tPacket packet = Packet.packetInstance(new Element(\"message\", new String[] { \"xmlns\" }, new String[] { \"urn:ietf:params:xml:ns:xmpp-sasl\" }));\n\t\tassertFalse(matcher.matches(packet));\n\t\tpacket = Packet.packetInstance(new Element(\"message\", new String[] { \"xmlns\", \"type\" }, new String[] { \"urn:ietf:params:xml:ns:xmpp-sasl\", \"headline\" }));\n\t\tassertTrue(matcher.matches(packet));\n\t}\n\n\t@Test\n\tpublic void testAttr() throws TigaseStringprepException {\n\t\tString matcherStr = \"/message/dummy[type=headline]\";\n\t\tElementMatcher matcher = ElementMatcher.create(matcherStr);\n\t\tassertNotNull(matcher);\n\n\t\tassertEquals(matcherStr, matcher.toString());\n\n\t\tPacket packet = Packet.packetInstance(new Element(\"message\", new Element[] { new Element(\"dummy\", new String[]{\"type\"}, new String[] {\"fail\"})}, new String[] { \"xmlns\" }, new String[] { \"urn:ietf:params:xml:ns:xmpp-sasl\" }));\n\t\tassertFalse(matcher.matches(packet));\n\t\tpacket = Packet.packetInstance(new Element(\"message\", new Element[] { new Element(\"dummy\", new String[]{\"type\"}, new String[] {\"headline\"})}, new String[] { \"xmlns\", \"type\" }, new String[] { \"urn:ietf:params:xml:ns:xmpp-sasl\", \"headline\" }));\n\t\tassertTrue(matcher.matches(packet));\n\t}\n\n\n\t@Test\n\tpublic void testAllWithXMLNS() throws TigaseStringprepException {\n\t\tString matcherStr = \"/message/*[http://jabber.org/protocol/chatstates]\";\n\t\tElementMatcher matcher = ElementMatcher.create(matcherStr);\n\t\tassertNotNull(matcher);\n\n\t\tassertEquals(matcherStr, matcher.toString());\n\n\t\tPacket packet = Packet.packetInstance(new Element(\"message\", new String[] { \"xmlns\" }, new String[] { \"urn:ietf:params:xml:ns:xmpp-sasl\" }));\n\t\tassertFalse(matcher.matches(packet));\n\t\tpacket = Packet.packetInstance(new Element(\"message\", new Element[] { new Element(\"active\", new String[]{\"xmlns\"}, new String[] {\"http://jabber.org/protocol/chatstates\"})}, new String[] { \"xmlns\", \"type\" }, new String[] { \"urn:ietf:params:xml:ns:xmpp-sasl\", \"headline\" }));\n\t\tassertTrue(matcher.matches(packet));\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/xmpp/XMPPDomBuilderHandlerTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport org.junit.Test;\nimport tigase.xml.Element;\n\nimport java.util.Map;\n\nimport static org.junit.Assert.*;\n\n/**\n * @author andrzej\n */\npublic class XMPPDomBuilderHandlerTest {\n\n\t@Test\n\tpublic void testPrefixesAndNamespacesHandling() {\n\t\tXMPPIOService ioserv = new XMPPIOService() {\n\t\t\t@Override\n\t\t\tprotected void xmppStreamOpened(Map attribs) {\n\t\t\t}\n\n\t\t};\n\t\tboolean error = false;\n\t\tXMPPDomBuilderHandler<Element> handler = new XMPPDomBuilderHandler<>(ioserv);\n\t\thandler.setElementsLimit(10);\n\t\thandler.startElement(new StringBuilder(\"stream:stream\"),\n\t\t\t\t\t\t\t new StringBuilder[]{new StringBuilder(\"xmlns\"), new StringBuilder(\"xmlns:stream\"),\n\t\t\t\t\t\t\t\t\t\t\t\t new StringBuilder(\"xmlns:db\")},\n\t\t\t\t\t\t\t new StringBuilder[]{new StringBuilder(\"jabber:server\"),\n\t\t\t\t\t\t\t\t\t\t\t\t new StringBuilder(\"http://etherx.jabber.org/streams\"),\n\t\t\t\t\t\t\t\t\t\t\t\t new StringBuilder(\"jabber:server:dialback\")});\n\n\t\thandler.startElement(new StringBuilder(\"db:result\"), new StringBuilder[]{new StringBuilder(\"to\")},\n\t\t\t\t\t\t\t new StringBuilder[]{new StringBuilder(\"example.com\")});\n\t\thandler.elementCData(new StringBuilder(\"CAESBxCXyf6RqCoaEGPHnXDLTIeKBNx9ZJ1SmzM=\"));\n\t\terror = !handler.endElement(new StringBuilder(\"db:result\"));\n\n\t\tassertFalse(handler.parseError() || error);\n\t\tassertEquals(\"result\", handler.getParsedElements().peek().getName());\n\t\tassertEquals(\"jabber:server:dialback\", handler.getParsedElements().peek().getXMLNS());\n\n\t\thandler.getParsedElements().clear();\n\t\thandler.startElement(new StringBuilder(\"test:message\"), null, null);\n\t\terror = !handler.endElement(new StringBuilder(\"test:message\"));\n\t\tassertFalse(handler.parseError() || error);\n\n\t\thandler.getParsedElements().clear();\n\t\thandler.startElement(new StringBuilder(\"message\"), null, null);\n\t\terror = !handler.endElement(new StringBuilder(\"message\"));\n\t\tassertFalse(handler.parseError() || error);\n\n\t\thandler.getParsedElements().clear();\n\t\thandler.startElement(new StringBuilder(\"test:message\"), null, null);\n\t\terror = !handler.endElement(new StringBuilder(\"message\"));\n\t\tassertTrue(handler.parseError() || error);\n\n\t\thandler.getParsedElements().clear();\n\t\thandler.startElement(new StringBuilder(\"message\"), null, null);\n\t\terror = !handler.endElement(new StringBuilder(\"test:message\"));\n\t\tassertTrue(handler.parseError() || error);\n\n\t\thandler.getParsedElements().clear();\n\t\thandler.startElement(new StringBuilder(\"message\"), null, null);\n\t\terror = !handler.endElement(new StringBuilder(\"db:message\"));\n\t\tassertTrue(handler.parseError() || error);\n\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/XMPPIOServiceTest_Proxy.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport junit.framework.TestCase;\nimport org.junit.Test;\n\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\nimport java.net.StandardProtocolFamily;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.ServerSocketChannel;\nimport java.nio.channels.SocketChannel;\nimport java.util.Map;\nimport java.util.Random;\n\nimport static tigase.net.IOService.PORT_TYPE_PROP_KEY;\nimport static tigase.server.ConnectionManager.PORT_PROXY_PROTOCOL_PROP_KEY;\n\npublic class XMPPIOServiceTest_Proxy\n\t\textends TestCase {\n\n\tprivate XMPPIOService service;\n\tprivate ServerSocketChannel serverSocket;\n\tprivate SocketChannel clientSocket;\n\n\t@Override\n\tprotected void setUp() throws Exception {\n\t\tservice = new XMPPIOService();\n\t\tservice.setSessionData(Map.of(PORT_TYPE_PROP_KEY, \"accept\", PORT_PROXY_PROTOCOL_PROP_KEY, true));\n\t\tserverSocket = ServerSocketChannel.open(StandardProtocolFamily.INET);\n\t\tserverSocket = serverSocket.bind(new InetSocketAddress(\"localhost\", 6123 + new Random().nextInt(0, 100)));\n\t\tObject lock = new Object();\n\t\tnew Thread(() -> {\n\t\t\ttry {\n\t\t\t\tSocketChannel channel = serverSocket.accept();\n\t\t\t\tservice.accept(channel, 1024);\n\t\t\t} catch (IOException e) {\n\t\t\t}\n\t\t\tsynchronized (lock) {\n\t\t\t\tlock.notifyAll();\n\t\t\t}\n\t\t}).start();\n\t\tclientSocket = SocketChannel.open(StandardProtocolFamily.INET);\n\t\tclientSocket.connect(serverSocket.getLocalAddress());\n\t\tsynchronized (lock) {\n\t\t\tlock.wait();\n\t\t}\n\t\tassertTrue(clientSocket.isOpen());\n\t}\n\n\t@Override\n\tprotected void tearDown() throws Exception {\n\t\tserverSocket.close();\n\t\tclientSocket.close();\n\t}\n\n\t@Test\n\tpublic void testProxy1_IPv4() throws IOException, InterruptedException {\n\t\tclientSocket.write(ByteBuffer.wrap(\"PROXY TCP4 192.168.1.223 192.168.3.22 56342 5222\\r\\n\".getBytes()));\n\t\tThread.sleep(10);\n\t\tservice.processSocketData();\n\t\tassertEquals(\"192.168.1.223\", service.getRemoteAddress());\n\t\tassertEquals(\"192.168.3.22\", service.getLocalAddress());\n\t}\n\n\t@Test\n\tpublic void testProxy1_IPv6() throws IOException, InterruptedException {\n\t\tclientSocket.write(ByteBuffer.wrap(\"PROXY TCP4 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee 56342 5222\\r\\n\".getBytes()));\n\t\tThread.sleep(10);\n\t\tservice.processSocketData();\n\t\tassertEquals(\"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff\", service.getRemoteAddress());\n\t\tassertEquals(\"eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee\", service.getLocalAddress());\n\t}\n\n\t@Test\n\tpublic void testProxy2_IPv4() throws IOException, InterruptedException {\n\t\tString proxy =\n\t\t\t\t// Preamble\n\t\t\t\t\"0D0A0D0A000D0A515549540A\" +\n\t\t\t\t\t\t// V2, PROXY\n\t\t\t\t\t\t\"21\" +\n\t\t\t\t\t\t// 0x1 : AF_INET    0x1 : STREAM.\n\t\t\t\t\t\t\"11\" +\n\t\t\t\t\t\t// Address length is 2*4 + 2*2 = 12 bytes.\n\t\t\t\t\t\t// length of remaining header (4+4+2+2 = 12)\n\t\t\t\t\t\t\"000C\" +\n\t\t\t\t\t\t// uint32_t src_addr; uint32_t dst_addr; uint16_t src_port; uint16_t dst_port;\n\t\t\t\t\t\t\"C0A80001\" + // 192.168.0.1\n\t\t\t\t\t\t\"7f000001\" + // 127.0.0.1\n\t\t\t\t\t\t\"3039\" + // 12345\n\t\t\t\t\t\t\"1F90\"; // 8080\n\n\t\tbyte[] data = new byte[proxy.length() / 2];\n\t\tfor (int i = 0; i < proxy.length(); i += 2) {\n\t\t\tdata[i / 2] = (byte) ((Character.digit(proxy.charAt(i), 16) << 4)\n\t\t\t\t\t+ Character.digit(proxy.charAt(i+1), 16));\n\t\t}\n\t\tclientSocket.write(ByteBuffer.wrap(data));\n\t\tThread.sleep(10);\n\t\tservice.processSocketData();\n\t\tassertEquals(\"192.168.0.1\", service.getRemoteAddress());\n\t\tassertEquals(\"127.0.0.1\", service.getLocalAddress());\n\t}\n\n\t@Test\n\tpublic void testProxy2_IPv6() throws IOException, InterruptedException {\n\t\tString proxy =\n\t\t\t\t// Preamble\n\t\t\t\t\"0D0A0D0A000D0A515549540A\" +\n\t\t\t\t\t\t// V2, PROXY\n\t\t\t\t\t\t\"21\" +\n\t\t\t\t\t\t// 0x1 : AF_INET6    0x1 : STREAM.\n\t\t\t\t\t\t\"21\" +\n\t\t\t\t\t\t// Address length is 2*16 + 2*2 = 36 bytes.\n\t\t\t\t\t\t// length of remaining header (16+16+2+2 = 36)\n\t\t\t\t\t\t\"0024\" +\n\t\t\t\t\t\t// uint8_t src_addr[16]; uint8_t  dst_addr[16]; uint16_t src_port; uint16_t dst_port;\n\t\t\t\t\t\t\"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\" + // ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff\n\t\t\t\t\t\t\"EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\" + // eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee\n\t\t\t\t\t\t\"3039\" + // 12345\n\t\t\t\t\t\t\"1F90\"; // 8080\n\n\t\tbyte[] data = new byte[proxy.length() / 2];\n\t\tfor (int i = 0; i < proxy.length(); i += 2) {\n\t\t\tdata[i / 2] = (byte) ((Character.digit(proxy.charAt(i), 16) << 4)\n\t\t\t\t\t+ Character.digit(proxy.charAt(i+1), 16));\n\t\t}\n\t\tclientSocket.write(ByteBuffer.wrap(data));\n\t\tThread.sleep(10);\n\t\tservice.processSocketData();\n\t\tassertEquals(\"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff\", service.getRemoteAddress());\n\t\tassertEquals(\"eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee\", service.getLocalAddress());\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/XMPPProcessorAbstractTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.impl.ProcessorTestCase;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.ArrayDeque;\nimport java.util.Map;\nimport java.util.Queue;\n\nimport static org.junit.Assert.*;\n\npublic class XMPPProcessorAbstractTest\n\t\textends ProcessorTestCase {\n\n\tJID connId = JID.jidInstanceNS(\"c2s@localhost/recipient1-res1\");\n\tString domain = \"domain\";\n\tJID recipient = JID.jidInstanceNS(\"recipient\", domain, \"resource\");\n\tJID sender = JID.jidInstanceNS(\"sender\", domain, \"resource\");\n\tprivate XMPPProcessorAbstract processor;\n\n\t@Before\n\t@Override\n\tpublic void setUp() throws Exception {\n\t\tsuper.setUp();\n\t\tprocessor = new SimpleXMPPProcessor();\n\t}\n\n\t@Test\n\tpublic void testProcessToUserPacketWithSession()\n\t\t\tthrows TigaseStringprepException, PacketErrorTypeException, NotAuthorizedException {\n\t\tfinal Iq iq = getIqPacket();\n\n\t\tfinal ArrayDeque<Packet> results = new ArrayDeque<>();\n\t\tfinal XMPPResourceConnection session = getSession(connId, recipient, true);\n\t\tprocessor.processToUserPacket(iq, session, null, results, null);\n\t\tassertFalse(results.isEmpty());\n\t\tassertEquals(results.poll().getType(), StanzaType.result);\n\t}\n\n\t@Test\n\tpublic void testProcessToUserPacketWithoutSession()\n\t\t\tthrows TigaseStringprepException, PacketErrorTypeException, NotAuthorizedException {\n\t\tfinal Iq iq = getIqPacket();\n\n\t\tfinal ArrayDeque<Packet> results = new ArrayDeque<>();\n\t\tfinal XMPPResourceConnection emptySession = getSession(connId, recipient, false);\n\n\t\tprocessor.processToUserPacket(iq, emptySession, null, results, null);\n\t\tassertTrue(results.isEmpty());\n\t}\n\n\tprivate Iq getIqPacket() throws TigaseStringprepException {\n\t\tfinal Element iqElement = new Element(\"iq\").withAttribute(\"type\", StanzaType.result.toString())\n\t\t\t\t.withAttribute(\"from\", sender.toString())\n\t\t\t\t.withAttribute(\"to\", recipient.toString());\n\t\tiqElement.addChild(new Element(\"ping\").withAttribute(\"xmlns\", \"urn:xmpp:ping\"));\n\t\treturn new Iq(iqElement);\n\t}\n\n\tprivate static class SimpleXMPPProcessor\n\t\t\textends XMPPProcessorAbstract {\n\n\t\t@Override\n\t\tpublic void processFromUserToServerPacket(JID connectionId, Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t\t  NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t\t\t  Map<String, Object> settings) throws PacketErrorTypeException {\n\n\t\t}\n\n\t\t@Override\n\t\tpublic void processServerSessionPacket(Packet packet, XMPPResourceConnection session,\n\t\t\t\t\t\t\t\t\t\t\t   NonAuthUserRepository repo, Queue<Packet> results,\n\t\t\t\t\t\t\t\t\t\t\t   Map<String, Object> settings) throws PacketErrorTypeException {\n\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/AbstractProcessorWithDataSourceAwareTestCase.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.db.DataSource;\nimport tigase.db.DataSourceAware;\nimport tigase.db.DataSourceHelper;\nimport tigase.kernel.core.Kernel;\n\n/**\n * Class is a base class for testing processors which require instance of class implementing DataSourceAware interface.\n * \n * */\npublic abstract class AbstractProcessorWithDataSourceAwareTestCase<DS extends DataSource, R extends DataSourceAware> extends AbstractProcessorWithDataSourceTestCase<DS> {\n\n\tprotected R repo;\n\n\tprotected abstract Class<? extends DataSourceAware> getDataSourceAwareIfc();\n\n\t@Before\n\tpublic void setupDataSourceAware() throws Exception {\n\t\trepo = prepareDataSourceAware();\n\t}\n\n\t@After\n\tpublic void tearDown() throws Exception {\n\t\trepo = null;\n\t}\n\n\t@Override\n\tprotected void registerBeans(Kernel kernel) {\n\t\tsuper.registerBeans(kernel);\n\t\ttry {\n\t\t\tClass dataSourceAwareClassForUri = DataSourceHelper.getDefaultClass(getDataSourceAwareIfc(), uri);\n\t\t\tkernel.registerBean(\"repository\").asClass(dataSourceAwareClassForUri).setActive(true).exec();\n\t\t} catch (Exception ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n\tprotected R prepareDataSourceAware() throws Exception {\n\t\tR repo = getInstance((Class<R>) getDataSourceAwareIfc());\n\t\ttry {\n\t\t\t// we do not check schema version as we are updating schema in loadSchema() method!!\n\t\t\t//dataSource.checkSchemaVersion(repo, true);\n\t\t\trepo.setDataSource(dataSource);\n\t\t} catch (RuntimeException ex) {\n\t\t\tex.printStackTrace();\n\t\t\tthrow new RepositoryException(ex);\n\t\t}\n\t\treturn repo;\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/AbstractProcessorWithDataSourceTestCase.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.junit.*;\nimport org.junit.rules.TestRule;\nimport org.junit.runner.Description;\nimport org.junit.runners.model.Statement;\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.db.DBInitException;\nimport tigase.db.DataSource;\nimport tigase.db.DataSourceHelper;\nimport tigase.db.util.SchemaLoader;\nimport tigase.db.util.SchemaManager;\n\nimport java.io.File;\nimport java.util.Arrays;\nimport java.util.Optional;\nimport java.util.Set;\n\n/**\n * Class is a base class for testing processors which require access to DataSource instances.\n * \n */\npublic class AbstractProcessorWithDataSourceTestCase<DS extends DataSource> extends ProcessorTestCase {\n\n\tprotected static String uri = System.getProperty(\"testDbUri\");\n\t@ClassRule\n\tpublic static TestRule rule = new TestRule() {\n\t\t@Override\n\t\tpublic Statement apply(Statement stmnt, Description d) {\n\t\t\tif (uri == null) {\n\t\t\t\treturn new Statement() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void evaluate() throws Throwable {\n\t\t\t\t\t\tAssume.assumeTrue(\"Ignored due to not passed DB URI!\", false);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn stmnt;\n\t\t}\n\t};\n\n\tprotected DS dataSource;\n\n\t@AfterClass\n\tpublic static void cleanDerby() {\n\t\tif (uri.contains(\"jdbc:derby:\")) {\n\t\t\tFile f = new File(\"derby_test\");\n\t\t\tif (!f.exists()) {\n\t\t\t\tf = new File(\"tigase_test\");\n\t\t\t}\n\t\t\tif (f.exists()) {\n\t\t\t\tif (f.listFiles() != null) {\n\t\t\t\t\tArrays.asList(f.listFiles()).forEach(f2 -> {\n\t\t\t\t\t\tif (f2.listFiles() != null) {\n\t\t\t\t\t\t\tArrays.asList(f2.listFiles()).forEach(f3 -> f3.delete());\n\t\t\t\t\t\t}\n\t\t\t\t\t\tf2.delete();\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tf.delete();\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected static void loadSchema(String schemaId, String schemaVersion, Set<String> components) throws DBInitException {\n\t\tSchemaLoader loader = SchemaLoader.newInstanceForURI(uri);\n\t\tSchemaLoader.Parameters params = loader.createParameters();\n\t\tparams.parseUri(uri);\n\t\tparams.setDbRootCredentials(null, null);\n\t\tloader.init(params, Optional.empty());\n\t\tloader.validateDBConnection();\n\t\tloader.validateDBExists();\n\t\tAssert.assertEquals(SchemaLoader.Result.ok, loader.loadCommonSchema());\n\t\tOptional<SchemaManager.SchemaInfo> schemaInfo = SchemaManager.getDefaultSchemaFor(uri, schemaId, components);\n\t\tAssert.assertEquals(SchemaLoader.Result.ok, loader.loadSchema(schemaInfo.get(), schemaVersion));\n\t\tloader.postInstallation();\n\t\tloader.shutdown();\n\t}\n\n\t@Before\n\tpublic void setupDataSource() throws Exception {\n\t\tdataSource = prepareDataSource();\n\t\tgetKernel().registerBean(\"dataSource\").asInstance(dataSource).exportable().exec();\n\t}\n\n\tprotected DS prepareDataSource() throws RepositoryException, IllegalAccessException, InstantiationException {\n\t\tDataSource dataSource = DataSourceHelper.getDefaultClass(DataSource.class, uri)\n\t\t\t\t.newInstance();//RepositoryFactory.getRepoClass(DataSource.class, uri).newInstance();\n\t\tdataSource.initialize(uri);\n\t\treturn (DS) dataSource;\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/AddressingSanitizerTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.TestLogger;\nimport tigase.server.Packet;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.NoConnectionIdException;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.logging.Logger;\n\nimport static org.junit.Assert.*;\n\n/**\n * @author Wojtek\n */\npublic class AddressingSanitizerTest\n\t\textends ProcessorTestCase {\n\n\tprivate static final Logger log = TestLogger.getLogger(AddressingSanitizerTest.class);\n\n\tprivate AddressingSanitizer addressingSanitizer;\n\tprivate JID recipientJid = JID.jidInstanceNS(\"recipient@example.com/res-2\");\n\tprivate Queue<Packet> results;\n\tprivate JID senderJid = JID.jidInstanceNS(\"sender@example.com/res-1\");\n\tprivate XMPPResourceConnection senderSession;\n\tprivate Map<String, Object> settings = new HashMap<>();\n\n\tpublic AddressingSanitizerTest() {\n\t}\n\n\t@Before\n\tpublic void setUp() throws Exception {\n\t\taddressingSanitizer = new AddressingSanitizer();\n\t\tsenderSession = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()), senderJid);\n\t\tresults = new ArrayDeque<>();\n\t}\n\n\t@After\n\tpublic void tearDown() throws Exception {\n\t\taddressingSanitizer = null;\n\t\tsenderSession = null;\n\t\tresults = null;\n\t}\n\n\t@Test\n\tpublic void testMessageWithFromAndTo()\n\t\t\tthrows TigaseStringprepException, NotAuthorizedException, NoConnectionIdException {\n\t\tPacket p = getPacket(\"message\", null, null);\n\t\tp.setPacketFrom(senderSession.getConnectionId());\n\n\t\tp.getElement().setAttribute(\"from\", senderJid.getBareJID().toString());\n\t\tp.getElement().setAttribute(\"to\", recipientJid.toString());\n\t\tp.initVars();\n\n\t\tassertFalse(addressingSanitizer.preProcess(p, senderSession, null, results, settings));\n\t\tassertEquals(0, results.size());\n\t\tassertEquals(senderJid, p.getStanzaFrom());\n\t\tassertEquals(recipientJid, p.getStanzaTo());\n\t}\n\n\t@Test\n\tpublic void testMessageWithIncorrectFromInStanza()\n\t\t\tthrows TigaseStringprepException, NotAuthorizedException, NoConnectionIdException {\n\t\tPacket p = getPacket(\"message\", null, \"my_user@domain.com\");\n\t\tp.setPacketFrom(senderSession.getConnectionId());\n\n\t\tassertFalse(addressingSanitizer.preProcess(p, senderSession, null, results, settings));\n\t\tassertEquals(0, results.size());\n\t\tassertEquals(senderJid, p.getStanzaFrom());\n\t\tassertEquals(senderJid.copyWithoutResource(), p.getStanzaTo());\n\t}\n\n\t@Test\n\tpublic void testMessageWithSameFromInStanza()\n\t\t\tthrows TigaseStringprepException, NotAuthorizedException, NoConnectionIdException {\n\t\tPacket p = getPacket(\"message\", null, senderJid.toString());\n\t\tp.setPacketFrom(senderSession.getConnectionId());\n\n\t\tassertFalse(addressingSanitizer.preProcess(p, senderSession, null, results, settings));\n\t\tassertEquals(0, results.size());\n\t\tassertEquals(senderJid, p.getStanzaFrom());\n\t\tassertEquals(senderJid.copyWithoutResource(), p.getStanzaTo());\n\t}\n\n\t@Test\n\tpublic void testMessageWithoutFrom()\n\t\t\tthrows TigaseStringprepException, NotAuthorizedException, NoConnectionIdException {\n\t\tPacket p = getPacket(\"message\", null, null);\n\t\tp.setPacketFrom(senderSession.getConnectionId());\n\n\t\tassertFalse(addressingSanitizer.preProcess(p, senderSession, null, results, settings));\n\t\tassertEquals(0, results.size());\n\t\tassertEquals(senderJid, p.getStanzaFrom());\n\t\tassertEquals(senderJid.copyWithoutResource(), p.getStanzaTo());\n\t}\n\n\t@Test\n\tpublic void testPresenceSubscriptionWithFromBareJid()\n\t\t\tthrows TigaseStringprepException, NotAuthorizedException, NoConnectionIdException {\n\t\tPacket p = getPacket(\"presence\", StanzaType.subscribe, senderJid.getBareJID().toString());\n\t\tp.getElement().setAttribute(\"type\", \"subscribe\");\n\t\tp.setPacketFrom(senderSession.getConnectionId());\n\n\t\tassertFalse(addressingSanitizer.preProcess(p, senderSession, null, results, settings));\n\t\tassertEquals(0, results.size());\n\t\tassertEquals(JID.jidInstance(senderJid.getBareJID()), p.getStanzaFrom());\n\t\tassertNull(p.getStanzaTo());\n\n\t\t// temporarily we set authorised stanza in connection manger and we don't apply full logic here, let's make sure\n\t\t// the result is correct as well (authorised from is _always_ correct).\n\t\tp.setServerAuthorisedStanzaFrom(JID.jidInstanceNS(senderJid.getBareJID()));\n\t\tassertFalse(addressingSanitizer.preProcess(p, senderSession, null, results, settings));\n\t\tassertEquals(0, results.size());\n\t\tassertEquals(JID.jidInstance(senderJid.getBareJID()), p.getStanzaFrom());\n\t\tassertNull(p.getStanzaTo());\n\t}\n\n\t@Test\n\tpublic void testPresenceSubscriptionWithFromFullJid()\n\t\t\tthrows TigaseStringprepException, NotAuthorizedException, NoConnectionIdException {\n\t\tPacket p = getPacket(\"presence\", StanzaType.subscribe, senderJid.toString());\n\t\tp.getElement().setAttribute(\"type\", \"subscribe\");\n\t\tp.setPacketFrom(senderSession.getConnectionId());\n\n\t\tassertFalse(addressingSanitizer.preProcess(p, senderSession, null, results, settings));\n\t\tassertEquals(0, results.size());\n\t\tassertEquals(JID.jidInstance(senderJid.getBareJID()), p.getStanzaFrom());\n\n\t\t// temporarily we set authorised stanza in connection manger and we don't apply full logic here, let's make sure\n\t\t// the result is correct as well (authorised from is _always_ correct).\n\t\tp.setServerAuthorisedStanzaFrom(senderJid);\n\t\tassertFalse(addressingSanitizer.preProcess(p, senderSession, null, results, settings));\n\t\tassertEquals(0, results.size());\n\t\tassertEquals(JID.jidInstance(senderJid.getBareJID()), p.getStanzaFrom());\n\t\tassertNull(p.getStanzaTo());\n\t}\n\n\t@Test\n\tpublic void testPresenceSubscriptionWithIncorrectFrom()\n\t\t\tthrows TigaseStringprepException, NotAuthorizedException, NoConnectionIdException {\n\t\tfinal JID from = JID.jidInstanceNS(\"my_user@domain.com\");\n\t\tPacket p = getPacket(\"presence\", StanzaType.subscribe, from.toString());\n\t\tp.getElement().setAttribute(\"type\", \"subscribe\");\n\t\tp.setPacketFrom(senderSession.getConnectionId());\n\n\t\tassertFalse(addressingSanitizer.preProcess(p, senderSession, null, results, settings));\n\t\tassertEquals(0, results.size());\n\t\tassertEquals(JID.jidInstance(senderJid.getBareJID()), p.getStanzaFrom());\n\t\tassertNull(p.getStanzaTo());\n\n\t\t// temporarily we set authorised stanza in connection manger and we don't apply full logic here, let's make sure\n\t\t// the result is correct as well (authorised from is _always_ correct).\n\t\tp.setServerAuthorisedStanzaFrom(senderJid);\n\t\tassertFalse(addressingSanitizer.preProcess(p, senderSession, null, results, settings));\n\t\tassertEquals(0, results.size());\n\t\tassertEquals(JID.jidInstance(senderJid.getBareJID()), p.getStanzaFrom());\n\t\tassertNull(p.getStanzaTo());\n\t}\n\n\t@Test\n\tpublic void testPresenceSubscriptionWithoutFrom()\n\t\t\tthrows TigaseStringprepException, NotAuthorizedException, NoConnectionIdException {\n\t\tPacket p = getPacket(\"presence\", StanzaType.subscribe, null);\n\t\tp.setPacketFrom(senderSession.getConnectionId());\n\t\tp.initVars();\n\n\t\tassertFalse(addressingSanitizer.preProcess(p, senderSession, null, results, settings));\n\t\tassertEquals(0, results.size());\n\t\tassertEquals(JID.jidInstance(senderJid.getBareJID()), p.getStanzaFrom());\n\t\tassertNull(p.getStanzaTo());\n\n\t\t// temporarily we set authorised stanza in connection manger and we don't apply full logic here.\n\t\tp.setServerAuthorisedStanzaFrom(null);\n\t\tassertFalse(addressingSanitizer.preProcess(p, senderSession, null, results, settings));\n\t\tassertEquals(0, results.size());\n\t\tassertEquals(JID.jidInstance(senderJid.getBareJID()), p.getStanzaFrom());\n\t\tassertNull(p.getStanzaTo());\n\t}\n\n\t@Test\n\tpublic void testPresenceWithFrom()\n\t\t\tthrows TigaseStringprepException, NotAuthorizedException, NoConnectionIdException {\n\t\tPacket p = getPacket(\"presence\", null, senderJid.getBareJID().toString());\n\t\tp.setPacketFrom(senderSession.getConnectionId());\n\n\t\tassertFalse(addressingSanitizer.preProcess(p, senderSession, null, results, settings));\n\t\tassertEquals(0, results.size());\n\t\tassertEquals(senderJid, p.getStanzaFrom());\n\t\tassertNull(p.getStanzaTo());\n\t}\n\n\t@Test\n\tpublic void testPresenceWithWrongFrom()\n\t\t\tthrows TigaseStringprepException, NotAuthorizedException, NoConnectionIdException {\n\t\tPacket p = getPacket(\"presence\", null, \"my_user@domain.com\");\n\t\tp.setPacketFrom(senderSession.getConnectionId());\n\n\t\tassertFalse(addressingSanitizer.preProcess(p, senderSession, null, results, settings));\n\t\tassertEquals(0, results.size());\n\t\tassertEquals(senderJid, p.getStanzaFrom());\n\t\tassertNull(p.getStanzaTo());\n\t}\n\n\t@Test\n\tpublic void testPresenceWithoutFrom()\n\t\t\tthrows TigaseStringprepException, NotAuthorizedException, NoConnectionIdException {\n\t\tPacket p = getPacket(\"presence\", null, null);\n\t\tp.setPacketFrom(senderSession.getConnectionId());\n\n\t\tassertFalse(addressingSanitizer.preProcess(p, senderSession, null, results, settings));\n\t\tassertEquals(0, results.size());\n\t\tassertEquals(senderJid, p.getStanzaFrom());\n\t\tassertNull(p.getStanzaTo());\n\t}\n\n\tprivate Packet getPacket(String elementName, StanzaType type, String from) throws TigaseStringprepException {\n\t\tElement element = new Element(elementName);\n\t\tif (from != null && !from.isEmpty()) {\n\t\t\telement.setAttribute(\"from\", from);\n\t\t}\n\t\tif (type != null) {\n\t\t\telement.setAttribute(\"type\", type.toString());\n\t\t}\n\t\treturn Packet.packetInstance(element);\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/BindResourceTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.ArrayDeque;\nimport java.util.HashMap;\nimport java.util.Queue;\nimport java.util.UUID;\n\nimport static org.junit.Assert.assertEquals;\n\npublic class BindResourceTest extends ProcessorTestCase {\n\n\tprivate BindResource bindResource;\n\n\t@Before\n\t@Override\n\tpublic void setUp() throws Exception {\n\t\tbindResource = getInstance(BindResource.class);\n\t\tbindResource.init(new HashMap<>());\n\t\tsuper.setUp();\n\t}\n\n\t@After\n\t@Override\n\tpublic void tearDown() throws Exception {\n\t\tbindResource = null;\n\t\tsuper.tearDown();\n\t}\n\n\t@Test\n\tpublic void testNoEscaping() throws TigaseStringprepException, XMPPException {\n\t\tBareJID user = BareJID.bareJIDInstance(\"test@example.com\");\n\t\ttestAuthentication(user, \"test-1\", \"test-1\");\n\t}\n\n\t@Test\n\tpublic void testEscapingApos() throws TigaseStringprepException, XMPPException {\n\t\tBareJID user = BareJID.bareJIDInstance(\"test@example.com\");\n\t\ttestAuthentication(user, \"test's\", \"test&apos;s\");\n\t}\n\n\t@Test\n\tpublic void testEscapingQuote() throws TigaseStringprepException, XMPPException {\n\t\tBareJID user = BareJID.bareJIDInstance(\"test@example.com\");\n\t\ttestAuthentication(user, \"test \\\"cat\\\"\", \"test &quot;cat&quot;\");\n\t}\n\t\n\tprivate void testAuthentication(BareJID user, String resource, String expectedResource) throws TigaseStringprepException, XMPPException {\n\t\tXMPPResourceConnection conn = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()),\n\t\t\t\t\t\t\t\t\t\t\t\t JID.jidInstance(user, \"res\"), false);\n\t\tconn.authorizeJID(user, false);\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tElement iq = new Element(\"iq\").withAttribute(\"type\", \"set\")\n\t\t\t\t.withAttribute(\"id\", \"test-1\")\n\t\t\t\t.withElement(\"bind\", BindResource.ID, bindEl -> {\n\t\t\t\t\tbindEl.withElement(\"resource\", null, resource);\n\t\t\t\t});\n\t\tbindResource.process(new Iq(iq), conn, null, results, null);\n\t\tassertEquals(1, results.size());\n\t\tassertEquals(expectedResource, conn.getResource());\n\t\tPacket result = results.poll();\n\t\tassertEquals(StanzaType.result, result.getType());\n\t\tassertEquals(JID.jidInstance(user, expectedResource).toString(), result.getElemChild(\"bind\", BindResource.ID).getChild(\"jid\").getCData());\n\t}\n\n\t@Override\n\tprotected void registerBeans(Kernel kernel) {\n\t\tsuper.registerBeans(kernel);\n\t\tkernel.registerBean(\"eventbus\").asInstance(EventBusFactory.getInstance()).exportable().exec();\n\t\tkernel.registerBean(BindResource.class).setActive(true).exportable().exec();\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/BlockingCommandTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.db.TigaseDBException;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.impl.roster.RosterAbstract;\nimport tigase.xmpp.impl.roster.RosterFactory;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.ArrayDeque;\nimport java.util.List;\n\nimport static org.junit.Assert.*;\n\n/**\n * @author andrzej\n */\npublic class BlockingCommandTest\n\t\textends ProcessorTestCase {\n\n\tprivate BlockingCommand blockingCommand;\n\tprivate JabberIqPrivacy privacy;\n\tprivate ArrayDeque<Packet> results;\n\tprivate RosterAbstract roster_util;\n\n\t@Before\n\t@Override\n\tpublic void setUp() throws Exception {\n\t\tsuper.setUp();\n\t\tgetInstance(RosterFactory.Bean.class);\n\t\tblockingCommand = getInstance(BlockingCommand.class);\n\t\tprivacy = getInstance(JabberIqPrivacy.class);\n\t\troster_util = RosterFactory.getRosterImplementation(true);\n\t\tresults = new ArrayDeque<>();\n\t}\n\n\t@After\n\t@Override\n\tpublic void tearDown() throws Exception {\n\t\tsuper.tearDown();\n\t\troster_util.setEventBus(null);\n\t\tblockingCommand = null;\n\t}\n\n\t@Test\n\tpublic void testEmptyJID() throws TigaseStringprepException, XMPPException {\n\t\tJID connJid = JID.jidInstanceNS(\"c2s@example.com/test-111\");\n\t\tJID userJid = JID.jidInstanceNS(\"user-1@example.com/res-1\");\n\t\tElement iq = new Element(\"iq\", new String[]{\"type\"}, new String[]{\"set\"});\n\t\tElement block = new Element(\"block\", new String[]{\"xmlns\"}, new String[]{BlockingCommand.XMLNS});\n\t\tElement item = new Element(\"item\", new String[]{\"jid\"}, new String[]{\"\"});\n\t\tblock.addChild(item);\n\t\tiq.addChild(block);\n\n\t\tPacket p = Packet.packetInstance(iq, userJid, userJid);\n\n\t\tXMPPResourceConnection sess = getSession(connJid, userJid);\n\t\tblockingCommand.process(p, sess, null, results, null);\n\t\tassertEquals(1, results.size());\n\t\tPacket result = results.poll();\n\t\tassertNotNull(result);\n\t\tassertEquals(Iq.ELEM_NAME, result.getElemName());\n\t\tassertEquals(StanzaType.error, result.getType());\n\n\t}\n\t@Test\n\tpublic void testBlockUnblock() throws Exception {\n\t\tJID connJid = JID.jidInstanceNS(\"c2s@example.com/test-111\");\n\t\tJID userJid = JID.jidInstanceNS(\"user-1@example.com/res-1\");\n\t\tXMPPResourceConnection sess = getSession(connJid, userJid);\n\n\t\tString blockJid = \"block-1@example.com\";\n\n\t\tcheckPrivacyJidBlocked(sess, blockJid, false);\n\t\tList<String> blocked = getBlocked(sess);\n\t\tassertTrue(blocked == null || blocked.isEmpty());\n\n\t\tblock(sess, blockJid);\n\t\tassertEquals(2, results.size());\n\t\tprivacy.filter(null, sess, null, results);\n\n\t\tassertEquals(2, results.size());\n\t\tPacket result = results.poll();\n\t\tassertNotNull(result);\n\t\tassertEquals(Iq.ELEM_NAME, result.getElemName());\n\t\tassertEquals(StanzaType.result, result.getType());\n\t\tresult = results.poll();\n\t\tassertNotNull(result);\n\t\tassertEquals(Iq.ELEM_NAME, result.getElemName());\n\t\tassertEquals(StanzaType.set, result.getType());\n\n\t\tcheckPrivacyJidBlocked(sess, blockJid, true);\n\t\tblocked = getBlocked(sess);\n\t\tassertTrue(blocked.contains(blockJid));\n\n\t\tunblock(sess, blockJid);\n\t\tassertEquals(2, results.size());\n\t\tprivacy.filter(null, sess, null, results);\n\n\t\tassertEquals(2, results.size());\n\t\tresult = results.poll();\n\t\tassertNotNull(result);\n\t\tassertEquals(Iq.ELEM_NAME, result.getElemName());\n\t\tassertEquals(StanzaType.result, result.getType());\n\t\tresult = results.poll();\n\t\tassertNotNull(result);\n\t\tassertEquals(Iq.ELEM_NAME, result.getElemName());\n\t\tassertEquals(StanzaType.set, result.getType());\n\n\t\tcheckPrivacyJidBlocked(sess, blockJid, false);\n\t\tblocked = getBlocked(sess);\n\t\tassertTrue(blocked == null || blocked.isEmpty());\n\t}\n\n\t@Test\n\tpublic void testBlockUnblockAll() throws Exception {\n\t\tJID connJid = JID.jidInstanceNS(\"c2s@example.com/test-111\");\n\t\tJID userJid = JID.jidInstanceNS(\"user-1@example.com/res-1\");\n\t\tXMPPResourceConnection sess = getSession(connJid, userJid);\n\n\t\tString blockJid = \"block-1@example.com\";\n\n\t\tcheckPrivacyJidBlocked(sess, blockJid, false);\n\t\tList<String> blocked = getBlocked(sess);\n\t\tassertTrue(blocked == null || blocked.isEmpty());\n\n\t\tblock(sess, blockJid);\n\t\tassertEquals(2, results.size());\n\t\tprivacy.filter(null, sess, null, results);\n\n\t\tassertEquals(2, results.size());\n\t\tPacket result = results.poll();\n\t\tassertNotNull(result);\n\t\tassertEquals(Iq.ELEM_NAME, result.getElemName());\n\t\tassertEquals(StanzaType.result, result.getType());\n\t\tresult = results.poll();\n\t\tassertNotNull(result);\n\t\tassertEquals(Iq.ELEM_NAME, result.getElemName());\n\t\tassertEquals(StanzaType.set, result.getType());\n\n\t\tcheckPrivacyJidBlocked(sess, blockJid, true);\n\t\tblocked = getBlocked(sess);\n\t\tassertTrue(blocked.contains(blockJid));\n\n\t\tunblock(sess, blockJid);\n\t\tassertEquals(2, results.size());\n\t\tprivacy.filter(null, sess, null, results);\n\n\t\tassertEquals(2, results.size());\n\t\tresult = results.poll();\n\t\tassertNotNull(result);\n\t\tassertEquals(Iq.ELEM_NAME, result.getElemName());\n\t\tassertEquals(StanzaType.result, result.getType());\n\t\tresult = results.poll();\n\t\tassertNotNull(result);\n\t\tassertEquals(Iq.ELEM_NAME, result.getElemName());\n\t\tassertEquals(StanzaType.set, result.getType());\n\n\t\tcheckPrivacyJidBlocked(sess, blockJid, false);\n\t\tblocked = getBlocked(sess);\n\t\tassertTrue(blocked == null || blocked.isEmpty());\n\t}\n\n\t@Test\n\tpublic void testBlockUnblockWithPresence() throws Exception {\n\t\tJID connJid = JID.jidInstanceNS(\"c2s@example.com/test-111\");\n\t\tJID userJid = JID.jidInstanceNS(\"user-1@example.com/res-1\");\n\t\tXMPPResourceConnection sess = getSession(connJid, userJid);\n\n\t\tString blockJid = \"block-1@example.com\";\n\t\troster_util.addBuddy(sess, JID.jidInstance(blockJid), \"Block-1\", null, null, null);\n\t\troster_util.setBuddySubscription(sess, RosterAbstract.SubscriptionType.both, JID.jidInstance(blockJid));\n\n\t\tcheckPrivacyJidBlocked(sess, blockJid, false);\n\t\tList<String> blocked = getBlocked(sess);\n\t\tassertTrue(blocked == null || blocked.isEmpty());\n\n\t\tblock(sess, blockJid);\n\t\tassertEquals(3, results.size());\n\t\tprivacy.filter(null, sess, null, results);\n\n\t\tassertEquals(3, results.size());\n\t\tPacket result = results.poll();\n\t\tassertNotNull(result);\n\t\tassertEquals(tigase.server.Presence.ELEM_NAME, result.getElemName());\n\t\tassertEquals(StanzaType.unavailable, result.getType());\n\t\tresult = results.poll();\n\t\tassertNotNull(result);\n\t\tassertEquals(Iq.ELEM_NAME, result.getElemName());\n\t\tassertEquals(StanzaType.result, result.getType());\n\t\tresult = results.poll();\n\t\tassertNotNull(result);\n\t\tassertEquals(Iq.ELEM_NAME, result.getElemName());\n\t\tassertEquals(StanzaType.set, result.getType());\n\n\t\tcheckPrivacyJidBlocked(sess, blockJid, true);\n\t\tblocked = getBlocked(sess);\n\t\tassertTrue(blocked.contains(blockJid));\n\n\t\tunblock(sess, blockJid);\n\t\tassertEquals(3, results.size());\n\t\tprivacy.filter(null, sess, null, results);\n\n\t\tassertEquals(3, results.size());\n\t\tresult = results.poll();\n\t\tassertNotNull(result);\n\t\tassertEquals(tigase.server.Presence.ELEM_NAME, result.getElemName());\n\t\tassertEquals(StanzaType.probe, result.getType());\n\t\tresult = results.poll();\n\t\tassertNotNull(result);\n\t\tassertEquals(Iq.ELEM_NAME, result.getElemName());\n\t\tassertEquals(StanzaType.result, result.getType());\n\t\tresult = results.poll();\n\t\tassertNotNull(result);\n\t\tassertEquals(Iq.ELEM_NAME, result.getElemName());\n\t\tassertEquals(StanzaType.set, result.getType());\n\n\t\tcheckPrivacyJidBlocked(sess, blockJid, false);\n\t\tblocked = getBlocked(sess);\n\t\tassertTrue(blocked == null || blocked.isEmpty());\n\t}\n\n\t@Test\n\tpublic void testBlockUnblockDomainWithPresence() throws Exception {\n\t\tJID connJid = JID.jidInstanceNS(\"c2s@example.com/test-111\");\n\t\tJID userJid = JID.jidInstanceNS(\"user-1@example.com/res-1\");\n\t\tXMPPResourceConnection sess = getSession(connJid, userJid);\n\n\t\tBareJID blockJid = BareJID.bareJIDInstanceNS( \"block-1@example.com\");\n\t\troster_util.addBuddy(sess, JID.jidInstance(blockJid), \"Block-1\", null, null, null);\n\t\troster_util.setBuddySubscription(sess, RosterAbstract.SubscriptionType.both, JID.jidInstance(blockJid));\n\n\t\tcheckPrivacyJidBlocked(sess, blockJid.getDomain(), false);\n\t\tList<String> blocked = getBlocked(sess);\n\t\tassertTrue(blocked == null || blocked.isEmpty());\n\n\t\tblock(sess, blockJid.getDomain());\n\t\tassertEquals(3, results.size());\n\t\tprivacy.filter(null, sess, null, results);\n\n\t\tassertEquals(3, results.size());\n\t\tPacket result = results.poll();\n\t\tassertNotNull(result);\n\t\tassertEquals(tigase.server.Presence.ELEM_NAME, result.getElemName());\n\t\tassertEquals(JID.jidInstanceNS(blockJid), result.getStanzaTo());\n\t\tassertEquals(StanzaType.unavailable, result.getType());\n\t\tresult = results.poll();\n\t\tassertNotNull(result);\n\t\tassertEquals(Iq.ELEM_NAME, result.getElemName());\n\t\tassertEquals(StanzaType.result, result.getType());\n\t\tresult = results.poll();\n\t\tassertNotNull(result);\n\t\tassertEquals(Iq.ELEM_NAME, result.getElemName());\n\t\tassertEquals(StanzaType.set, result.getType());\n\n\t\tcheckPrivacyJidBlocked(sess, blockJid.getDomain(), true);\n\t\tblocked = getBlocked(sess);\n\t\tassertTrue(blocked.contains(blockJid.getDomain()));\n\n\t\tunblock(sess, blockJid.getDomain());\n\t\tassertEquals(3, results.size());\n\t\tprivacy.filter(null, sess, null, results);\n\n\t\tassertEquals(3, results.size());\n\t\tresult = results.poll();\n\t\tassertNotNull(result);\n\t\tassertEquals(tigase.server.Presence.ELEM_NAME, result.getElemName());\n\t\tassertEquals(JID.jidInstanceNS(blockJid), result.getStanzaTo());\n\t\tassertEquals(StanzaType.probe, result.getType());\n\t\tresult = results.poll();\n\t\tassertNotNull(result);\n\t\tassertEquals(Iq.ELEM_NAME, result.getElemName());\n\t\tassertEquals(StanzaType.result, result.getType());\n\t\tresult = results.poll();\n\t\tassertNotNull(result);\n\t\tassertEquals(Iq.ELEM_NAME, result.getElemName());\n\t\tassertEquals(StanzaType.set, result.getType());\n\n\t\tcheckPrivacyJidBlocked(sess, blockJid.getDomain(), false);\n\t\tblocked = getBlocked(sess);\n\t\tassertTrue(blocked == null || blocked.isEmpty());\n\t}\n\n\n\t@Override\n\tprotected void registerBeans(Kernel kernel) {\n\t\tsuper.registerBeans(kernel);\n\t\tkernel.registerBean(\"eventBus\").asInstance(EventBusFactory.getInstance()).exportable().exec();\n\t\tkernel.registerBean(RosterFactory.Bean.class).setActive(true).exec();\n\t\tkernel.registerBean(JabberIqPrivacy.class).setActive(true).exec();\n\t\tkernel.registerBean(BlockingCommand.class).setActive(true).exec();\n\t}\n\n\tprivate void block(XMPPResourceConnection sess, String jid) throws Exception {\n\t\tElement iq = new Element(\"iq\", new String[]{\"type\"}, new String[]{\"set\"});\n\t\tElement block = new Element(\"block\", new String[]{\"xmlns\"}, new String[]{BlockingCommand.XMLNS});\n\t\tElement item = new Element(\"item\", new String[]{\"jid\"}, new String[]{jid});\n\t\tblock.addChild(item);\n\t\tiq.addChild(block);\n\t\tPacket p = Packet.packetInstance(iq);\n\t\tblockingCommand.process(p, sess, null, results, null);\n\t}\n\n\tprivate void unblock(XMPPResourceConnection sess, String jid) throws Exception {\n\t\tElement iq = new Element(\"iq\", new String[]{\"type\"}, new String[]{\"set\"});\n\t\tElement block = new Element(\"unblock\", new String[]{\"xmlns\"}, new String[]{BlockingCommand.XMLNS});\n\t\tElement item = new Element(\"item\", new String[]{\"jid\"}, new String[]{jid});\n\t\tblock.addChild(item);\n\t\tiq.addChild(block);\n\t\tPacket p = Packet.packetInstance(iq);\n\t\tblockingCommand.process(p, sess, null, results, null);\n\t}\n\n\tprivate void unblockAll(XMPPResourceConnection sess) throws Exception {\n\t\tElement iq = new Element(\"iq\", new String[]{\"type\"}, new String[]{\"set\"});\n\t\tElement block = new Element(\"unblock\", new String[]{\"xmlns\"}, new String[]{BlockingCommand.XMLNS});\n\t\tiq.addChild(block);\n\t\tPacket p = Packet.packetInstance(iq);\n\t\tblockingCommand.process(p, sess, null, results, null);\n\t}\n\n\tprivate void checkPrivacyJidBlocked(XMPPResourceConnection sess, String jid, boolean value)\n\t\t\tthrows NotAuthorizedException, TigaseDBException {\n\t\tList<String> blocked = Privacy.getBlocked(sess);\n\t\tif (value) {\n\t\t\tassertTrue(blocked != null && blocked.contains(jid));\n\t\t} else {\n\t\t\tassertTrue(blocked == null || !blocked.contains(jid));\n\t\t}\n\t}\n\n\tprivate List<String> getBlocked(XMPPResourceConnection sess) throws XMPPException, TigaseStringprepException {\n\t\tElement iq = new Element(\"iq\", new String[]{\"type\"}, new String[]{\"get\"});\n\t\tElement blocklist = new Element(\"blocklist\", new String[]{\"xmlns\"}, new String[]{BlockingCommand.XMLNS});\n\t\tiq.addChild(blocklist);\n\n\t\tPacket p = Packet.packetInstance(iq);\n\t\tblockingCommand.process(p, sess, null, results, null);\n\t\tassertEquals(1, results.size());\n\t\tPacket result = results.poll();\n\t\tassertEquals(Iq.ELEM_NAME, result.getElemName());\n\t\treturn result.getElement()\n\t\t\t\t.getChild(\"blocklist\")\n\t\t\t\t.mapChildren(c -> c.getName() == \"item\", c -> c.getAttributeStaticStr(\"jid\"));\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/C2SDeliveryErrorProcessorTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.JID;\n\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayDeque;\nimport java.util.Queue;\nimport java.util.TimeZone;\nimport java.util.UUID;\n\nimport static org.junit.Assert.*;\n\n/**\n * @author andrzej\n */\npublic class C2SDeliveryErrorProcessorTest\n\t\textends ProcessorTestCase {\n\n\tprivate static final SimpleDateFormat formatter;\n\n\tstatic {\n\t\tformatter = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'\");\n\t\tformatter.setTimeZone(TimeZone.getTimeZone(\"UTC\"));\n\t}\n\n\tprivate MessageDeliveryLogic messageDeliveryLogic;\n\n\t@Before\n\t@Override\n\tpublic void setUp() throws Exception {\n\t\tsuper.setUp();\n\t\tmessageDeliveryLogic = getInstance(MessageDeliveryLogic.class);\n\t}\n\n\t@Override\n\tprotected void registerBeans(Kernel kernel) {\n\t\tsuper.registerBeans(kernel);\n\t\tkernel.registerBean(MessageDeliveryLogic.class).exec();\n\t}\n\n\t@Test\n\tpublic void test() throws Exception {\n\t\tElement packetEl = null;\n\t\tPacket packet = null;\n\t\tJID from = JID.jidInstance(\"from@example.com/res\");\n\t\tJID to = JID.jidInstance(\"to@example.com\");\n\n\t\tpacketEl = new Element(\"iq\", new String[]{\"id\", \"from\", \"to\"},\n\t\t\t\t\t\t\t   new String[]{UUID.randomUUID().toString(), from.toString(), to.toString()});\n\t\tpacket = Packet.packetInstance(packetEl);\n\n\t\tPacket error = C2SDeliveryErrorProcessor.makeDeliveryError(packet, 12L);\n\t\tElement deliveryError = C2SDeliveryErrorProcessor.getDeliveryError(error);\n\t\tassertNotNull(deliveryError);\n\t\tassertEquals(\"12\", deliveryError.getAttributeStaticStr(\"stamp\"));\n\n\t\tpacketEl = new Element(\"message\", new String[]{\"id\", \"from\", \"to\"},\n\t\t\t\t\t\t\t   new String[]{UUID.randomUUID().toString(), from.toString(), to.toString()});\n\t\tpacket = Packet.packetInstance(packetEl);\n\n\t\terror = C2SDeliveryErrorProcessor.makeDeliveryError(packet, 12L);\n\t\tdeliveryError = C2SDeliveryErrorProcessor.getDeliveryError(error);\n\t\tassertNotNull(deliveryError);\n\t\tassertEquals(\"12\", deliveryError.getAttributeStaticStr(\"stamp\"));\n\t}\n\n\t@Test\n\tpublic void testPreprocessingNotSupportedPackets() throws Exception {\n\t\tElement packetEl = null;\n\t\tPacket packet = null;\n\t\tJID from = JID.jidInstance(\"from@example.com/res\");\n\t\tJID to = JID.jidInstance(\"to@example.com\");\n\n\t\tpacketEl = new Element(\"iq\", new String[]{\"id\", \"from\", \"to\"},\n\t\t\t\t\t\t\t   new String[]{UUID.randomUUID().toString(), from.toString(), to.toString()});\n\t\tpacket = Packet.packetInstance(packetEl);\n\t\tassertFalse(C2SDeliveryErrorProcessor.preProcess(packet, null, null, null, null, messageDeliveryLogic));\n\n\t\tpacketEl = new Element(\"message\", new String[]{\"id\", \"from\", \"to\"},\n\t\t\t\t\t\t\t   new String[]{UUID.randomUUID().toString(), from.toString(), to.toString()});\n\t\tpacket = Packet.packetInstance(packetEl);\n\t\tassertFalse(C2SDeliveryErrorProcessor.preProcess(packet, null, null, null, null, messageDeliveryLogic));\n\n\t\t// packet is still not supported as session is null\n\t\tpacketEl = new Element(\"message\", new String[]{\"id\", \"from\", \"to\"},\n\t\t\t\t\t\t\t   new String[]{UUID.randomUUID().toString(), from.toString(), to.toString()});\n\t\tpacketEl.addChild(\n\t\t\t\tnew Element(\"delivery-error\", new String[]{\"xmlns\"}, new String[]{\"http://tigase.org/delivery-error\"}));\n\t\tpacket = Packet.packetInstance(packetEl);\n\t\tassertFalse(C2SDeliveryErrorProcessor.preProcess(packet, null, null, null, null, messageDeliveryLogic));\n\t}\n\n\t@Test\n\tpublic void testPreprocessingWithDeliveryErrorForBareJID() throws Exception {\n\t\tElement packetEl = null;\n\t\tPacket packet = null;\n\t\tJID from = JID.jidInstance(\"from@example.com/res\");\n\t\tJID to = JID.jidInstance(\"to@example.com\");\n\t\tJID toRes1 = to.copyWithResource(\"res1\");\n\t\tJID toRes2 = to.copyWithResource(\"res2\");\n\n\t\tXMPPResourceConnection sessionToRes2 = getSession(\n\t\t\t\tJID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()), toRes2);\n\t\tThread.sleep(1);\n\t\tString stampBefore = String.valueOf(System.currentTimeMillis());\n\t\tThread.sleep(1);\n\t\tXMPPResourceConnection sessionToRes1 = getSession(\n\t\t\t\tJID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()), toRes1);\n\t\tThread.sleep(1);\n\t\tString stampAfter = String.valueOf(System.currentTimeMillis());\n\n\t\tpacketEl = new Element(\"message\", new String[]{\"id\", \"from\", \"to\"},\n\t\t\t\t\t\t\t   new String[]{UUID.randomUUID().toString(), from.toString(), to.toString()});\n\t\tpacketEl.addChild(\n\t\t\t\tnew Element(\"delivery-error\", new String[]{\"xmlns\"}, new String[]{\"http://tigase.org/delivery-error\"}));\n\t\tpacket = Packet.packetInstance(packetEl);\n\t\tassertFalse(C2SDeliveryErrorProcessor.preProcess(packet, sessionToRes1, null, null, null, messageDeliveryLogic));\n\n\t\tsessionToRes1.setPriority(10);\n\t\tsessionToRes1.setPresence(new Element(\"presence\"));\n\n\t\tpacketEl = new Element(\"message\", new String[]{\"id\", \"from\", \"to\"},\n\t\t\t\t\t\t\t   new String[]{UUID.randomUUID().toString(), from.toString(), to.toString()});\n\t\tpacketEl.addChild(\n\t\t\t\tnew Element(\"delivery-error\", new String[]{\"xmlns\"}, new String[]{\"http://tigase.org/delivery-error\"}));\n\t\tpacket = Packet.packetInstance(packetEl);\n\t\tassertTrue(C2SDeliveryErrorProcessor.preProcess(packet, sessionToRes1, null, null, null, messageDeliveryLogic));\n\n\t\tpacketEl = new Element(\"message\", new String[]{\"id\", \"from\", \"to\"},\n\t\t\t\t\t\t\t   new String[]{UUID.randomUUID().toString(), from.toString(), to.toString()});\n\t\tpacketEl.addChild(\n\t\t\t\tnew Element(\"delivery-error\", new String[]{\"xmlns\"}, new String[]{\"http://tigase.org/delivery-error\"}));\n\t\tpacketEl.addChild(new Element(\"delay\", new String[]{\"xmlns\"}, new String[]{\"urn:xmpp:delay\"}));\n\t\tpacket = Packet.packetInstance(packetEl);\n\t\tassertTrue(C2SDeliveryErrorProcessor.preProcess(packet, sessionToRes1, null, null, null, messageDeliveryLogic));\n\n\t\tQueue<Packet> results = new ArrayDeque();\n\t\tresults.clear();\n\n\t\tpacketEl = new Element(\"message\", new String[]{\"id\", \"from\", \"to\"},\n\t\t\t\t\t\t\t   new String[]{UUID.randomUUID().toString(), from.toString(), to.toString()});\n\t\tpacketEl.addChild(new Element(\"delivery-error\", new String[]{\"xmlns\", \"stamp\"},\n\t\t\t\t\t\t\t\t\t  new String[]{\"http://tigase.org/delivery-error\", stampBefore}));\n\t\tpacket = Packet.packetInstance(packetEl);\n\t\tassertTrue(C2SDeliveryErrorProcessor.preProcess(packet, sessionToRes1, null, results, null, messageDeliveryLogic));\n\t\tassertEquals(1, results.size());\n\t\tPacket result = results.poll();\n\t\tassertEquals(sessionToRes1.getConnectionId(), result.getPacketTo());\n\n\t\tsessionToRes2.setPriority(10);\n\t\tsessionToRes2.setPresence(new Element(\"presence\"));\n\t\tresults.clear();\n\t\tpacketEl = new Element(\"message\", new String[]{\"id\", \"from\", \"to\"},\n\t\t\t\t\t\t\t   new String[]{UUID.randomUUID().toString(), from.toString(), to.toString()});\n\t\tpacketEl.addChild(new Element(\"delivery-error\", new String[]{\"xmlns\", \"stamp\"},\n\t\t\t\t\t\t\t\t\t  new String[]{\"http://tigase.org/delivery-error\", stampBefore}));\n\t\tpacket = Packet.packetInstance(packetEl);\n\t\tassertTrue(C2SDeliveryErrorProcessor.preProcess(packet, sessionToRes1, null, results, null, messageDeliveryLogic));\n\t\tassertEquals(1, results.size());\n\t\tresult = results.peek();\n\t\tassertEquals(sessionToRes1.getConnectionId(), result.getPacketTo());\n\t\tC2SDeliveryErrorProcessor.filter(packet, sessionToRes1, null, results, null);\n\t\tassertEquals(1, results.size());\n\t\tresult = results.peek();\n\t\tassertFalse(C2SDeliveryErrorProcessor.isDeliveryError(result));\n\n\t\tresults.clear();\n\t\tpacketEl = new Element(\"message\", new String[]{\"id\", \"from\", \"to\"},\n\t\t\t\t\t\t\t   new String[]{UUID.randomUUID().toString(), from.toString(), to.toString()});\n\t\tpacketEl.addChild(new Element(\"delivery-error\", new String[]{\"xmlns\", \"stamp\"},\n\t\t\t\t\t\t\t\t\t  new String[]{\"http://tigase.org/delivery-error\", stampAfter}));\n\t\tpacket = Packet.packetInstance(packetEl);\n\t\tassertTrue(C2SDeliveryErrorProcessor.preProcess(packet, sessionToRes1, null, results, null, messageDeliveryLogic));\n\t\tassertEquals(0, results.size());\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/DomainFilterTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.TestLogger;\nimport tigase.db.TigaseDBException;\nimport tigase.server.Packet;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.vhosts.VHostItemImpl;\nimport tigase.vhosts.filter.DomainFilterPolicy;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.ArrayDeque;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static tigase.xmpp.impl.DomainFilter.ALLOWED_DOMAINS_KEY;\nimport static tigase.xmpp.impl.DomainFilter.ALLOWED_DOMAINS_LIST_KEY;\n\n/**\n * @author Wojciech Kapcia <wojciech.kapcia@tigase.org>\n */\npublic class DomainFilterTest\n\t\textends ProcessorTestCase {\n\n\tprivate static final Logger log = TestLogger.getLogger(DomainFilterTest.class);\n\tJID connId1 = JID.jidInstanceNS(\"c2s@localhost/recipient1-res1\");\n\tString domain = \"domain\";\n\tJID externalDomainUser = JID.jidInstanceNS(\"user2\", domain + \"-ext\", \"resource2\");\n\tJID localDomainUser = JID.jidInstanceNS(\"user2\", domain + 2, \"resource2\");\n\tPacket p;\n\tJID recp1 = JID.jidInstanceNS(\"user1\", domain + 1, \"resource1\");\n\tArrayDeque<Packet> results;\n\tJID sameDomainUser = JID.jidInstanceNS(\"user2\", domain + 1, \"resource2\");\n\tXMPPResourceConnection session;\n\tprivate DomainFilter domainFilter;\n\n\t@Before\n\t@Override\n\tpublic void setUp() throws Exception {\n\t\tsuper.setUp();\n\t\tdomainFilter = new DomainFilter();\n\t\tdomainFilter.init(new HashMap<String, Object>());\n\t\tresults = new ArrayDeque<Packet>();\n\t}\n\n\t@After\n\t@Override\n\tpublic void tearDown() throws Exception {\n\t\tsuper.tearDown();\n\t\tdomainFilter = null;\n\t}\n\n\t@Test\n\tpublic void testPolicyHierarchy() throws NotAuthorizedException, TigaseStringprepException, TigaseDBException {\n\t\tString[] domainsList;\n\n\t\tsession = getSession(connId1, recp1, DomainFilterPolicy.LIST, new String[]{domain + 1, domain + 2, domain + 3});\n\n\t\tAssert.assertEquals(\"Reading DomainFilterPolicy from VHost\", DomainFilterPolicy.LIST,\n\t\t\t\t\t\t\tdomainFilter.getDomains(session));\n\t\tsession.removeCommonSessionData(ALLOWED_DOMAINS_KEY);\n\n\t\t// configuration from UserRepository\n\t\tsession.setData(null, ALLOWED_DOMAINS_KEY, DomainFilterPolicy.ALL.name());\n\t\tAssert.assertEquals(\"Reading DomainFilterPolicy from UserRepo, takes precendence over VHost\",\n\t\t\t\t\t\t\tDomainFilterPolicy.ALL, domainFilter.getDomains(session));\n\t\tsession.removeCommonSessionData(ALLOWED_DOMAINS_KEY);\n\n\t\t// user session data takes precedence over repository and VHost configuration\n\t\tsession.putCommonSessionData(ALLOWED_DOMAINS_KEY, DomainFilterPolicy.BLOCK);\n\t\tAssert.assertEquals(\"Reading DomainFilterPolicy from user session, takes precendence over UserRepository\",\n\t\t\t\t\t\t\tDomainFilterPolicy.BLOCK, domainFilter.getDomains(session));\n\t\tsession.removeCommonSessionData(ALLOWED_DOMAINS_KEY);\n\n\t\t// let's check hierarchy of domain list: VHost, UserRepo, session\n\t\tdomainsList = domainFilter.getDomainsList(session);\n\t\tAssert.assertTrue(\"Reading domain list from VHost\", Arrays.asList(domainsList).contains(domain + 1));\n\t\tAssert.assertFalse(\"Reading domain list from VHost\", Arrays.asList(domainsList).contains(domain + 5));\n\t\tsession.removeCommonSessionData(ALLOWED_DOMAINS_LIST_KEY);\n\n\t\t// configuration from UserRepository\n\t\tsession.setData(null, ALLOWED_DOMAINS_LIST_KEY, (domain + 11 + \";\" + domain + 12 + \";\" + domain + 13));\n\t\tdomainsList = domainFilter.getDomainsList(session);\n\t\tlog.log(Level.FINE, Arrays.asList(domainsList).toString());\n\t\tAssert.assertTrue(\"Reading domain list from repository\", Arrays.asList(domainsList).contains(domain + 11));\n\t\tAssert.assertFalse(\"Reading domain list from repository\", Arrays.asList(domainsList).contains(domain + 15));\n\t\tsession.removeCommonSessionData(ALLOWED_DOMAINS_LIST_KEY);\n\n\t\t// user session data takes precedence over repository and VHost configuration\n\t\tsession.putCommonSessionData(ALLOWED_DOMAINS_LIST_KEY, new String[]{domain + 21, domain + 22, domain + 23});\n\t\tdomainsList = domainFilter.getDomainsList(session);\n\t\tlog.log(Level.FINE, Arrays.asList(domainsList).toString());\n\t\tAssert.assertTrue(\"Reading domain list from session\", Arrays.asList(domainsList).contains(domain + 21));\n\t\tAssert.assertFalse(\"Reading domain list from session\", Arrays.asList(domainsList).contains(domain + 25));\n\t\tsession.removeCommonSessionData(ALLOWED_DOMAINS_LIST_KEY);\n\n\t\tsession.logout();\n\n\t}\n\n\t@Test\n\tpublic void testFilterAllPolicy() throws NotAuthorizedException, TigaseStringprepException {\n\t\tsession = getSession(connId1, recp1, DomainFilterPolicy.ALL, null);\n\n\t\tfilterPacket(session, sameDomainUser);\n\t\tAssert.assertFalse(\"ALL policy, message between same domains\",\n\t\t\t\t\t\t   results.pop().getType().equals(StanzaType.error));\n\n\t\t// two local domains\n\t\tfilterPacket(session, localDomainUser);\n\t\tAssert.assertFalse(\"ALL policy, message between different local domains\",\n\t\t\t\t\t\t   results.pop().getType().equals(StanzaType.error));\n\n\t\tfilterPacket(session, externalDomainUser);\n\t\tAssert.assertFalse(\"ALL policy, message to external domain\", results.pop().getType().equals(StanzaType.error));\n\n\t\tsession.logout();\n\n\t}\n\n\t@Test\n\tpublic void testFilterLocalPolicy() throws NotAuthorizedException, TigaseStringprepException {\n\t\tsession = getSession(connId1, recp1, DomainFilterPolicy.LOCAL, null);\n\n\t\tfilterPacket(session, sameDomainUser);\n\t\tAssert.assertFalse(\"LOCAL policy, message between same domains\",\n\t\t\t\t\t\t   results.pop().getType().equals(StanzaType.error));\n\n\t\tfilterPacket(session, localDomainUser);\n\t\tAssert.assertFalse(\"LOCAL policy, message between different local domains\",\n\t\t\t\t\t\t   results.pop().getType().equals(StanzaType.error));\n\n\t\tfilterPacket(session, externalDomainUser);\n\t\tAssert.assertTrue(\"LOCAL policy, message to external domain\", results.pop().getType().equals(StanzaType.error));\n\n\t\tsession.logout();\n\n\t}\n\n\t@Test\n\tpublic void testFilterOwnPolicy() throws NotAuthorizedException, TigaseStringprepException {\n\t\tsession = getSession(connId1, recp1, DomainFilterPolicy.OWN, null);\n\n\t\tfilterPacket(session, sameDomainUser);\n\t\tAssert.assertFalse(\"OWN policy, message between same domains\",\n\t\t\t\t\t\t   results.pop().getType().equals(StanzaType.error));\n\n\t\tfilterPacket(session, localDomainUser);\n\t\tAssert.assertTrue(\"OWN policy, message between different local domains\",\n\t\t\t\t\t\t  results.pop().getType().equals(StanzaType.error));\n\n\t\tfilterPacket(session, externalDomainUser);\n\t\tAssert.assertTrue(\"OWN policy, message to external domain\", results.pop().getType().equals(StanzaType.error));\n\n\t\tsession.logout();\n\n\t}\n\n\t@Test\n\tpublic void testFilterWhielistPolicy() throws NotAuthorizedException, TigaseStringprepException {\n\t\tString[] whitelistDomains = new String[]{\"domain1\", externalDomainUser.getDomain()};\n\n\t\tsession = getSession(connId1, recp1, DomainFilterPolicy.LIST, whitelistDomains);\n\n\t\tfilterPacket(session, sameDomainUser);\n\t\tAssert.assertFalse(\"WHITELIST policy, message between same domains (both whitelist)\",\n\t\t\t\t\t\t   results.pop().getType().equals(StanzaType.error));\n\n\t\tfilterPacket(session, localDomainUser);\n\t\tAssert.assertTrue(\"WHITELIST policy, message between different local domains (only sender whitelist)\",\n\t\t\t\t\t\t  results.pop().getType().equals(StanzaType.error));\n\n\t\tfilterPacket(session, externalDomainUser);\n\t\tAssert.assertFalse(\"WHITELIST policy, message to external domain (whitelisted)\",\n\t\t\t\t\t\t   results.pop().getType().equals(StanzaType.error));\n\n\t\tsession.logout();\n\n\t}\n\n\t@Test\n\tpublic void testFilterBlacklistPolicy() throws NotAuthorizedException, TigaseStringprepException {\n\t\tString[] blacklistedDomains = new String[]{localDomainUser.getDomain()};\n\n\t\tsession = getSession(connId1, recp1, DomainFilterPolicy.BLACKLIST, blacklistedDomains);\n\n\t\tfilterPacket(session, sameDomainUser);\n\t\tAssert.assertFalse(\"BLACKLIST policy, message between same domains (not on blacklist)\",\n\t\t\t\t\t\t   results.pop().getType().equals(StanzaType.error));\n\n\t\tfilterPacket(session, localDomainUser);\n\t\tAssert.assertTrue(\"BLACKLIST policy, message between different local domains (receiver domain blacklisted)\",\n\t\t\t\t\t\t  results.pop().getType().equals(StanzaType.error));\n\n\t\tfilterPacket(session, externalDomainUser);\n\t\tAssert.assertFalse(\"BLACKLIST policy, message to external domain (not blacklisted)\",\n\t\t\t\t\t\t   results.pop().getType().equals(StanzaType.error));\n\n\t\tsession.logout();\n\n\t}\n\n\t@Test\n\tpublic void testFilterBlockPolicy() throws NotAuthorizedException, TigaseStringprepException {\n\t\tString[] blacklistedDomains = new String[]{localDomainUser.getDomain()};\n\n\t\tsession = getSession(connId1, recp1, DomainFilterPolicy.BLOCK, blacklistedDomains);\n\n\t\tfilterPacket(session, sameDomainUser);\n\t\tAssert.assertTrue(\"BLOCK policy, message between same domains\",\n\t\t\t\t\t\t  results.pop().getType().equals(StanzaType.error));\n\n\t\tfilterPacket(session, localDomainUser);\n\t\tAssert.assertTrue(\"BLOCK policy, message between different local domains\",\n\t\t\t\t\t\t  results.pop().getType().equals(StanzaType.error));\n\n\t\tfilterPacket(session, externalDomainUser);\n\t\tAssert.assertTrue(\"BLOCK policy, message to external domain\", results.pop().getType().equals(StanzaType.error));\n\n\t\tsession.logout();\n\n\t}\n\n\t@Test\n\tpublic void testPreprocessorAllPolicy() throws NotAuthorizedException, TigaseStringprepException {\n\t\tsession = getSession(connId1, recp1, DomainFilterPolicy.ALL, null);\n\n\t\tprocessPacket(session, sameDomainUser);\n\t\tAssert.assertTrue(\"ALL policy, message between same domains\", results.isEmpty());\n\n\t\tprocessPacket(session, localDomainUser);\n\t\tAssert.assertTrue(\"ALL policy, message between different local domains\", results.isEmpty());\n\n\t\tprocessPacket(session, externalDomainUser);\n\t\tAssert.assertTrue(\"ALL policy, message to external domain\", results.isEmpty());\n\n\t\tsession.logout();\n\n\t}\n\n\t@Test\n\tpublic void testPreprocessorLocalPolicy() throws NotAuthorizedException, TigaseStringprepException {\n\t\tsession = getSession(connId1, recp1, DomainFilterPolicy.LOCAL, null);\n\n\t\tprocessPacket(session, sameDomainUser);\n\t\tAssert.assertTrue(\"LOCAL policy, message between same domains\", results.isEmpty());\n\n\t\tprocessPacket(session, localDomainUser);\n\t\tAssert.assertTrue(\"LOCAL policy, message between different local domains\", results.isEmpty());\n\n\t\tprocessPacket(session, externalDomainUser);\n\t\tAssert.assertTrue(\"LOCAL policy, message to external domain\", results.pop().getType().equals(StanzaType.error));\n\n\t\tsession.logout();\n\n\t}\n\n\t@Test\n\tpublic void testPreprocessorOwnPolicy() throws NotAuthorizedException, TigaseStringprepException {\n\t\tsession = getSession(connId1, recp1, DomainFilterPolicy.OWN, null);\n\n\t\tprocessPacket(session, sameDomainUser);\n\t\tAssert.assertTrue(\"OWN policy, message between same domains\", results.isEmpty());\n\n\t\tprocessPacket(session, localDomainUser);\n\t\tAssert.assertTrue(\"OWN policy, message between different local domains\",\n\t\t\t\t\t\t  results.pop().getType().equals(StanzaType.error));\n\n\t\tprocessPacket(session, externalDomainUser);\n\t\tAssert.assertTrue(\"OWN policy, message to external domain\", results.pop().getType().equals(StanzaType.error));\n\n\t\tsession.logout();\n\n\t}\n\n\t@Test\n\tpublic void testPreprocessorWhielistPolicy() throws NotAuthorizedException, TigaseStringprepException {\n\t\tString[] whitelistDomains = new String[]{\"domain1\", externalDomainUser.getDomain()};\n\n\t\tsession = getSession(connId1, recp1, DomainFilterPolicy.LIST, whitelistDomains);\n\n\t\tprocessPacket(session, sameDomainUser);\n\t\tAssert.assertTrue(\"WHITELIST policy, message between same domains (both whitelist)\", results.isEmpty());\n\n\t\tprocessPacket(session, localDomainUser);\n\t\tAssert.assertTrue(\"WHITELIST policy, message between different local domains (only sender whitelist)\",\n\t\t\t\t\t\t  results.pop().getType().equals(StanzaType.error));\n\n\t\tprocessPacket(session, externalDomainUser);\n\t\tAssert.assertTrue(\"WHITELIST policy, message to external domain (whitelisted)\", results.isEmpty());\n\n\t\tsession.logout();\n\n\t}\n\n\t@Test\n\tpublic void testPreprocessorBlacklistPolicy() throws NotAuthorizedException, TigaseStringprepException {\n\n\t\t// blacklisting other local user\n\t\tString[] blacklistedDomains = new String[]{localDomainUser.getDomain()};\n\n\t\tsession = getSession(connId1, recp1, DomainFilterPolicy.BLACKLIST, blacklistedDomains);\n\n\t\tprocessPacket(session, sameDomainUser);\n\t\tAssert.assertTrue(\"BLACKLIST policy, message between same domains (not on blacklist)\", results.isEmpty());\n\n\t\tprocessPacket(session, localDomainUser);\n\t\tAssert.assertTrue(\"BLACKLIST policy, message between different local domains (receiver domain blacklisted)\",\n\t\t\t\t\t\t  results.pop().getType().equals(StanzaType.error));\n\n\t\tprocessPacket(session, externalDomainUser);\n\t\tAssert.assertTrue(\"BLACKLIST policy, message to external domain (not blacklisted)\", results.isEmpty());\n\n\t\tsession.logout();\n\n\t}\n\n\t@Test\n\tpublic void testPreprocessorBlockPolicy() throws NotAuthorizedException, TigaseStringprepException {\n\n\t\tString[] blacklistedDomains = new String[]{localDomainUser.getDomain()};\n\n\t\tsession = getSession(connId1, recp1, DomainFilterPolicy.BLOCK, blacklistedDomains);\n\n\t\tprocessPacket(session, sameDomainUser);\n\t\tAssert.assertTrue(\"BLOCK policy, message between same domains\",\n\t\t\t\t\t\t  results.pop().getType().equals(StanzaType.error));\n\n\t\tprocessPacket(session, localDomainUser);\n\t\tAssert.assertTrue(\"BLOCK policy, message between different local domains\",\n\t\t\t\t\t\t  results.pop().getType().equals(StanzaType.error));\n\n\t\tprocessPacket(session, externalDomainUser);\n\t\tAssert.assertTrue(\"BLOCK policy, message to external domain\", results.pop().getType().equals(StanzaType.error));\n\n\t\tsession.logout();\n\n\t}\n\n\tprivate void filterPacket(XMPPResourceConnection session, JID reciever) throws TigaseStringprepException {\n\t\tp = Packet.packetInstance(\"message\", recp1.toString(), reciever.toString(), StanzaType.chat);\n\t\tp.setPacketFrom(connId1);\n\t\tresults.offer(p);\n\t\tdomainFilter.filter(p, session, null, results);\n\t\tlog.log(Level.FINEST, \"results: \" + results);\n\t}\n\n\tprivate void processPacket(XMPPResourceConnection session, JID reciever) throws TigaseStringprepException {\n\t\tp = Packet.packetInstance(\"message\", recp1.toString(), reciever.toString(), StanzaType.chat);\n\t\tp.setPacketFrom(connId1);\n\t\tdomainFilter.preProcess(p, session, null, results, null);\n\t\tlog.log(Level.FINEST, \"results: \" + results);\n\t}\n\n\tprivate XMPPResourceConnection getSession(JID connId, JID userJid, DomainFilterPolicy dfp, String[] domains)\n\t\t\tthrows NotAuthorizedException, TigaseStringprepException {\n\t\tXMPPResourceConnection conn = super.getSession(connId, userJid);\n\t\tVHostItemImpl vhost = (VHostItemImpl) conn.getDomain();\n\t\tvhost.setDomainFilter(dfp);\n\t\tvhost.setDomainFilterDomains(domains);\n\n\t\treturn conn;\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/JabberIqAuthTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.auth.BruteForceLockerBean;\nimport tigase.auth.TigaseSaslProvider;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.server.Command;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.HashMap;\nimport java.util.LinkedList;\n\nimport static junit.framework.Assert.assertNotNull;\nimport static junit.framework.TestCase.assertEquals;\n\npublic class JabberIqAuthTest\n\t\textends ProcessorTestCase {\n\n\t@Before\n\tpublic void prepare() {\n\t\tgetKernel().registerBean(\"eventbus\").asInstance(EventBusFactory.getInstance()).exportable().exec();\n\t\tgetKernel().registerBean(\"sess-man\").asInstance(new SessionManager()).setActive(true).exportable().exec();\n\t\tgetKernel().registerBean(BruteForceLockerBean.class).setActive(true).exportable().exec();\n\t\tgetKernel().registerBean(TigaseSaslProvider.class).setActive(true).exportable().exec();\n\t\tgetKernel().registerBean(JabberIqAuth.class).setActive(true).exec();\n\n\t}\n\n\t@Test\n\tpublic void testAuthMissingPassword() throws XMPPException, TigaseStringprepException {\n\t\tJID jid1 = JID.jidInstance(\"user1@example.com/res1\");\n\t\tXMPPResourceConnection session = getSession(jid1, jid1, false);\n\n\t\t/*\n\t\t<iq id=\"mira622e13a35eac130_2\" type=\"set\" xmlns=\"jabber:client\">\n\t\t\t<query xmlns=\"jabber:iq:auth\">\n\t\t\t\t<username>abcde123</username>\n\t\t\t\t<password/>\n\t\t\t\t<resource>Miranda</resource>\n\t\t\t</query>\n\t\t</iq>\n\t\t */\n\n\t\tElement iqElement = new Element(\"iq\", new String[]{\"xmlns\", \"type\"},\n\t\t\t\t\t\t\t\t   new String[]{Iq.CLIENT_XMLNS, StanzaType.set.name()});\n\t\tfinal Element query = new Element(\"query\", new String[]{\"xmlns\"}, new String[]{JabberIqAuth.ID});\n\t\tquery.addChild(new Element(\"username\", \"abcde123\"));\n\t\tquery.addChild(new Element(\"password\"));\n\t\tquery.addChild(new Element(\"resource\", \"Miranda\"));\n\t\tiqElement.addChild(query);\n\t\tPacket iq = Packet.packetInstance(iqElement);\n\t\tiq.setPacketFrom(jid1);\n\n\t\tJabberIqAuth processor = getKernel().getInstance(JabberIqAuth.class);\n\t\tassertEquals(Authorization.AUTHORIZED, processor.canHandle(iq, session));\n\n\t\tLinkedList<Packet> results = new LinkedList<>();\n\t\t\n\t\tprocessor.process(iq, session, null, results, new HashMap<>());\n\n\t\tassertEquals(2, results.size());\n\t\tassertNotNull(results.get(0).getElement().getChild(\"error\").getChild(\"not-acceptable\"));\n\t\tassertEquals(\"CLOSE\", results.get(1).getElement().getChild(\"command\").getAttributeStaticStr(\"node\"));\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/JabberIqCommandTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.server.Command;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.XMPPException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.HashMap;\nimport java.util.LinkedList;\n\nimport static junit.framework.TestCase.assertEquals;\n\npublic class JabberIqCommandTest extends ProcessorTestCase {\n\n\t@Before\n\tpublic void prepare() {\n\t\tgetKernel().registerBean(SessionManager.DefaultHandlerProc.class).exec();\n\t\tgetKernel().registerBean(JabberIqCommand.class).setActive(true).exec();\n\t}\n\n\t@Test\n\tpublic void testIqDelivery1() throws XMPPException, TigaseStringprepException {\n\t\tJID jid1 = JID.jidInstance(\"user1@example.com/res1\");\n\t\tJID jid2 = JID.jidInstance(\"user1@example.com/res2\");\n\t\tXMPPResourceConnection session = getSession(jid1, jid1);\n\n\t\tElement iqEl = new Element(\"iq\", new String[] {\"from\", \"to\", \"xmlns\", \"type\" }, new String[] {jid1.toString(), jid2.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  Iq.CLIENT_XMLNS, \"get\" });\n\t\tiqEl.addChild(new Element(\"command\", new String[] {\"xmlns\"}, new String[] {Command.XMLNS}));\n\t\tPacket iq = Packet.packetInstance(iqEl);\n\t\tiq.setPacketFrom(jid1);\n\n\t\tJabberIqCommand handler = getKernel().getInstance(JabberIqCommand.class);\n\t\tassertEquals(Authorization.AUTHORIZED, handler.canHandle(iq, session));\n\n\t\tLinkedList<Packet> results = new LinkedList<>();\n\t\t\n\t\thandler.process(iq, session, null, results, new HashMap<>());\n\n\t\tassertEquals(1, results.size());\n\t}\n\n\t@Test\n\tpublic void testIqDelivery2() throws XMPPException, TigaseStringprepException {\n\t\tJID jid1 = JID.jidInstance(\"user1@example.com/res1\");\n\t\tJID jid2 = JID.jidInstance(\"user1@example.com/res2\");\n\t\tXMPPResourceConnection session = getSession(jid2, jid2);\n\n\t\tElement iqEl = new Element(\"iq\", new String[] { \"from\", \"to\", \"xmlns\", \"type\" }, new String[] {jid1.toString(), jid2.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   Iq.CLIENT_XMLNS, \"get\" });\n\t\tiqEl.addChild(new Element(\"command\", new String[] {\"xmlns\"}, new String[] {Command.XMLNS}));\n\t\tPacket iq = Packet.packetInstance(iqEl);\n\n\t\tJabberIqCommand handler = getKernel().getInstance(JabberIqCommand.class);\n\t\tassertEquals(Authorization.AUTHORIZED, handler.canHandle(iq, session));\n\n\t\tLinkedList<Packet> results = new LinkedList<>();\n\n\t\thandler.process(iq, session, null, results, new HashMap<>());\n\n\t\tassertEquals(1, results.size());\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/JabberIqPrivacyTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.TestLogger;\nimport tigase.db.TigaseDBException;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.server.Iq;\nimport tigase.server.Packet;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.roster.RosterElement;\nimport tigase.xmpp.impl.roster.RosterFactory;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.ArrayDeque;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport static org.junit.Assert.*;\n\n/**\n * Test class for JabberIqPrivacyTest\n * <br>\n * Currently class tests validateList method checking validation of type, subscription and action. Other cases are not\n * tested due to missing instance of XMPPResourceConnection\n */\npublic class JabberIqPrivacyTest\n\t\textends ProcessorTestCase {\n\n\tprivate static final Logger log = TestLogger.getLogger(JabberIqPrivacyTest.class);\n\n\tprivate JabberIqPrivacy privacyFilter;\n\tprivate ArrayDeque<Packet> results;\n\n\t@Before\n\tpublic void setUp() throws Exception {\n\t\ttry {\n\t\t\tgetKernel().registerBean(\"eventBus\").asInstance(EventBusFactory.getInstance()).exportable().exec();\n\t\t\tgetKernel().registerBean(RosterFactory.Bean.class).setActive(true).exec();\n\t\t\tgetKernel().getInstance(RosterFactory.Bean.class);\n\t\t\tgetKernel().registerBean(JabberIqPrivacy.class).exec();\n\n\t\t\tprivacyFilter = getInstance(JabberIqPrivacy.class);\n\t\t\tgetKernel().getDependencyManager()\n\t\t\t\t\t.getBeanConfig(\"jabber:iq:privacy\")\n\t\t\t\t\t.getKernel()\n\t\t\t\t\t.setBeanActive(\"privacyListOfflineCache\", true);\n\t\t\tresults = new ArrayDeque<Packet>();\n\t\t} catch (Exception ex) {\n\t\t\tex.printStackTrace();\n\t\t\tassert (false);\n\t\t}\n\t}\n\n\t@After\n\tpublic void tearDown() throws Exception {\n\t\tprivacyFilter = null;\n\t}\n\n\t@Test\n\tpublic void testValidateListGood() {\n\t\tList<Element> items = new ArrayList<Element>();\n\n\t\tAuthorization result = null;\n\n\t\titems.add(new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t  new String[]{\"subscription\", \"both\", \"allow\", \"10\"}));\n\t\titems.add(new Element(\"item\", new String[]{\"action\", \"order\"}, new String[]{\"deny\", \"15\"}));\n\n\t\t// session is allowed to be null here\n\t\tresult = JabberIqPrivacy.validateList(null, items);\n\t\tassertEquals(null, result);\n\t}\n\n\t@Test\n\tpublic void testValidateListBadAction() {\n\t\tList<Element> items = new ArrayList<Element>();\n\n\t\tAuthorization result = null;\n\n\t\titems.add(new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t  new String[]{\"subscription\", \"both\", \"ignore\", \"10\"}));\n\t\titems.add(new Element(\"item\", new String[]{\"action\", \"order\"}, new String[]{\"deny\", \"15\"}));\n\n\t\t// session is allowed to be null here\n\t\tresult = JabberIqPrivacy.validateList(null, items);\n\t\tassertEquals(Authorization.BAD_REQUEST, result);\n\t}\n\n\t@Test\n\tpublic void testValidateListBadSubscription() {\n\t\tList<Element> items = new ArrayList<Element>();\n\n\t\tAuthorization result = null;\n\n\t\titems.add(new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t  new String[]{\"subscription\", \"or\", \"allow\", \"10\"}));\n\t\titems.add(new Element(\"item\", new String[]{\"action\", \"order\"}, new String[]{\"deny\", \"15\"}));\n\n\t\t// session is allowed to be null here\n\t\tresult = JabberIqPrivacy.validateList(null, items);\n\t\tassertEquals(Authorization.BAD_REQUEST, result);\n\t}\n\n\t@Test\n\tpublic void testValidateListBadType() {\n\t\tList<Element> items = new ArrayList<Element>();\n\n\t\tAuthorization result = null;\n\n\t\titems.add(new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t  new String[]{\"other\", \"both\", \"allow\", \"10\"}));\n\t\titems.add(new Element(\"item\", new String[]{\"action\", \"order\"}, new String[]{\"deny\", \"15\"}));\n\n\t\t// session is allowed to be null here\n\t\tresult = JabberIqPrivacy.validateList(null, items);\n\t\tassertEquals(Authorization.BAD_REQUEST, result);\n\t}\n\n\t@Test\n\tpublic void testValidateListOrderUnsignedInt() {\n\t\tList<Element> items = new ArrayList<Element>();\n\n\t\tAuthorization result = null;\n\n\t\titems.add(new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t  new String[]{\"subscription\", \"both\", \"allow\", \"-10\"}));\n\t\titems.add(new Element(\"item\", new String[]{\"action\", \"order\"}, new String[]{\"deny\", \"15\"}));\n\n\t\t// session is allowed to be null here\n\t\tresult = JabberIqPrivacy.validateList(null, items);\n\t\tassertEquals(Authorization.BAD_REQUEST, result);\n\n\t}\n\n\t@Test\n\tpublic void testValidateListOrderAttributeDuplicate() {\n\t\tList<Element> items = new ArrayList<Element>();\n\n\t\tAuthorization result = null;\n\n\t\titems.add(new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t  new String[]{\"subscription\", \"both\", \"allow\", \"10\"}));\n\t\titems.add(new Element(\"item\", new String[]{\"action\", \"order\"}, new String[]{\"deny\", \"10\"}));\n\n\t\t// session is allowed to be null here\n\t\tresult = JabberIqPrivacy.validateList(null, items);\n\t\tassertEquals(Authorization.BAD_REQUEST, result);\n\n\t}\n\n\t@Test\n\tpublic void testFilterPresenceOut() throws Exception {\n\t\tJID jid = JID.jidInstance(\"test@example/res-1\");\n\t\tJID connId = JID.jidInstance(\"c2s@example.com/asdasd\");\n\t\tXMPPResourceConnection session = getSession(connId, jid);\n\n\t\t//List<Element> items = new ArrayList<Element>();\n\t\tElement list = new Element(\"list\", new String[]{\"name\"}, new String[]{\"default\"});\n\t\tElement item = new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t\t   new String[]{\"jid\", \"test1.example.com\", \"deny\", \"100\"});\n\t\titem.addChild(new Element(\"presence-out\"));\n\t\tlist.addChild(item);\n\t\tlist.addChild(new Element(\"item\", new String[]{\"action\", \"order\"}, new String[]{\"allow\", \"110\"}));\n\n\t\tsession.putSessionData(\"active-list\", PrivacyList.create(null, list));\n\n\t\tPacket presence = Packet.packetInstance(new Element(\"presence\", new String[]{\"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew String[]{\"test@example/res-1\", \"test1.example.com\"}));\n\n\t\tassertFalse(privacyFilter.allowed(presence, session));\n\n\t\tpresence = Packet.packetInstance(new Element(\"presence\", new String[]{\"to\", \"from\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t new String[]{\"test@example/res-1\", \"test1.example.com\"}));\n\n\t\tassertTrue(privacyFilter.allowed(presence, session));\n\t}\n\n\t@Test\n\tpublic void testGroupSubscriptionTypeFiltering() throws Exception {\n\t\tJID jid = JID.jidInstance(\"test@example/res-1\");\n\t\tJID connId = JID.jidInstance(\"c2s@example.com/asdasd\");\n\t\tXMPPResourceConnection session = getSession(connId, jid);\n\t\tsession.putCommonSessionData(\"roster\", new ConcurrentHashMap<BareJID, RosterElement>());\n\n\t\t//List<Element> items = new ArrayList<Element>();\n\t\tElement list = new Element(\"list\", new String[]{\"name\"}, new String[]{\"default\"});\n\t\tElement item = new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t\t   new String[]{\"subscription\", \"none\", \"deny\", \"100\"});\n\t\titem.addChild(new Element(\"presence-out\"));\n\t\titem.addChild(new Element(\"presence-in\"));\n\t\titem.addChild(new Element(\"message\"));\n\t\tlist.addChild(item);\n\t\tlist.addChild(new Element(\"item\", new String[]{\"action\", \"order\"}, new String[]{\"allow\", \"110\"}));\n\n\t\tsession.putSessionData(\"active-list\",\n\t\t\t\t\t\t\t   PrivacyList.create((Map<BareJID, RosterElement>) session.getCommonSessionData(\"roster\"),\n\t\t\t\t\t\t\t\t\t\t\t\t  list));\n\n\t\tPacket presence = Packet.packetInstance(new Element(\"presence\", new String[]{\"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew String[]{\"test@example/res-1\", \"test1.example.com\"}));\n\n\t\tassertFalse(privacyFilter.allowed(presence, session));\n\n\t\tPacket message = Packet.packetInstance(new Element(\"message\", new String[]{\"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{\"test1.example.com\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"test@example.com/res-1\"}));\n\t\tassertFalse(privacyFilter.allowed(message, session));\n\n\t\tPacket iq = Packet.packetInstance(new Element(\"iq\", new String[]{\"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{\"test1.example.com\", \"test@example.com/res-1\"}));\n\t\tassertTrue(privacyFilter.allowed(iq, session));\n\t}\n\n\t@Test\n\tpublic void testStanzaType() throws Exception {\n\t\tJID jid = JID.jidInstance(\"test@example/res-1\");\n\t\tJID connId = JID.jidInstance(\"c2s@example.com/asdasd\");\n\t\tXMPPResourceConnection session = getSession(connId, jid);\n\n\t\tcheckStanzaType(session, \"get\", null, 1, StanzaType.result);\n\t\tcheckStanzaType(session, \"set\", new Element(\"active\"), 1, StanzaType.result);\n\t\tcheckStanzaType(session, \"error\", null, 0, null);\n\t\tcheckStanzaType(session, \"result\", null, 0, null);\n\t\tcheckStanzaType(session, \"probe\", null, 1, StanzaType.error);\n\t\tcheckStanzaType(session, null, null, 1, StanzaType.error);\n\t}\n\n\t@Test\n\tpublic void testFilterJidCase() throws Exception {\n\t\tJID jid = JID.jidInstance(\"test@example/res-1\");\n\t\tJID connId = JID.jidInstance(\"c2s@example.com/asdasd\");\n\t\tXMPPResourceConnection session = getSession(connId, jid);\n\n\t\tString blockedJID = \"CapitalisedJID@test.domain.com\";\n\n\t\tElement list = new Element(\"list\", new String[]{\"name\"}, new String[]{\"default\"});\n\t\tlist.addChild(new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t\t  new String[]{\"jid\", blockedJID.toLowerCase(), \"deny\", \"100\"}));\n\t\tlist.addChild(new Element(\"item\", new String[]{\"action\", \"order\"}, new String[]{\"allow\", \"110\"}));\n\n\t\tsession.putSessionData(\"active-list\", PrivacyList.create(null, list));\n\n\t\tPacket presence = Packet.packetInstance(\n\t\t\t\tnew Element(\"presence\", new String[]{\"from\", \"to\"}, new String[]{blockedJID, jid.toString()}));\n\n\t\tassertFalse(privacyFilter.allowed(presence, session));\n\n\t\tpresence = Packet.packetInstance(new Element(\"presence\", new String[]{\"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t new String[]{blockedJID.toLowerCase(), jid.toString()}));\n\n\t\tassertFalse(privacyFilter.allowed(presence, session));\n\n\t\tpresence = Packet.packetInstance(\n\t\t\t\tnew Element(\"presence\", new String[]{\"from\", \"to\"}, new String[]{jid.toString(), blockedJID}));\n\n\t\tassertFalse(privacyFilter.allowed(presence, session));\n\n\t\tpresence = Packet.packetInstance(new Element(\"presence\", new String[]{\"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t new String[]{jid.toString(), blockedJID.toLowerCase()}));\n\n\t\tassertFalse(privacyFilter.allowed(presence, session));\n\n\t}\n\n\t@Test\n\tpublic void testPartialJidMatching() throws Exception {\n\n\t\tJID jid = JID.jidInstance(\"test@example/res-1\");\n\t\tJID connId = JID.jidInstance(\"c2s@example.com/resource\");\n\t\tXMPPResourceConnection session = getSession(connId, jid);\n\n\t\ttestList(session, \"partial_blocked@test.domain.com/resource\", \"partial_blocked@test.domain.com/resource\",\n\t\t\t\t false);\n\t\ttestList(session, \"partial_blocked@test.domain.com/resource\", \"partial_blocked@test2.domain.com/resource\",\n\t\t\t\t true);\n\t\ttestList(session, \"partial_blocked@test.domain.com/resource\", \"partial_blocked@test.domain.com/resource2\",\n\t\t\t\t true);\n\t\ttestList(session, \"partial_blocked@test.domain.com/resource\", \"partial_blocked2@test.domain.com/resource\",\n\t\t\t\t true);\n\t\ttestList(session, \"partial_blocked@test.domain.com/resource\", \"partial_blocked@test.domain.com\", true);\n\t\ttestList(session, \"partial_blocked@test.domain.com/resource\", \"test.domain.com/true\", true);\n\t\ttestList(session, \"partial_blocked@test.domain.com/resource\", \"test.domain.com\", true);\n\n\t\ttestList(session, \"partial_blocked@test.domain.com\", \"partial_blocked@test.domain.com/resource\", false);\n\t\ttestList(session, \"partial_blocked@test.domain.com\", \"partial_blocked@test.domain.com\", false);\n\t\ttestList(session, \"partial_blocked@test.domain.com\", \"partial_blocked2@test.domain.com\", true);\n\t\ttestList(session, \"partial_blocked@test.domain.com\", \"partial_blocked@test2.domain.com\", true);\n\t\ttestList(session, \"partial_blocked@test.domain.com\", \"test.domain.com/true\", true);\n\t\ttestList(session, \"partial_blocked@test.domain.com\", \"test.domain.com\", true);\n\n\t\ttestList(session, \"test.domain.com/true\", \"partial_blocked@test.domain.com/resource\", true);\n\t\ttestList(session, \"test.domain.com/true\", \"partial_blocked@test.domain.com\", true);\n\t\ttestList(session, \"test.domain.com/true\", \"test.domain.com/true\", false);\n\t\ttestList(session, \"test.domain.com/true\", \"test.domain.com\", true);\n\t\ttestList(session, \"test.domain.com/true\", \"test.domain.com/true2\", true);\n\t\ttestList(session, \"test.domain.com/true\", \"test.2domain.com\", true);\n\n\t\ttestList(session, \"test.domain.com/true\", \"partial_blocked@test2.domain.com/resource\", true);\n\t\ttestList(session, \"test.domain.com/true\", \"partial_blocked@test2.domain.com\", true);\n\t\ttestList(session, \"test.domain.com/true\", \"test2.domain.com/true\", true);\n\t\ttestList(session, \"test.domain.com/true\", \"test.domain.com/true2\", true);\n\t\ttestList(session, \"test.domain.com/true\", \"test2.domain.com\", true);\n\n\t\ttestList(session, \"test.domain.com\", \"partial_blocked@test.domain.com/resource\", false);\n\t\ttestList(session, \"test.domain.com\", \"partial_blocked@test.domain.com\", false);\n\t\ttestList(session, \"test.domain.com\", \"test.domain.com/true\", false);\n\t\ttestList(session, \"test.domain.com\", \"test.domain.com\", false);\n\n\t\ttestList(session, \"test.domain.com\", \"partial_blocked@test2.domain.com/resource\", true);\n\t\ttestList(session, \"test.domain.com\", \"partial_blocked@test2.domain.com\", true);\n\t\ttestList(session, \"test.domain.com\", \"test2.domain.com/true\", true);\n\t\ttestList(session, \"test.domain.com\", \"test2.domain.com\", true);\n\n\t}\n\n\t@Test\n\tpublic void testPartialJidMatchingOffline() throws Exception {\n\n\t\tXMPPResourceConnection session = null;\n\n\t\ttestList(session, \"partial_blocked@test.domain.com/resource\", \"partial_blocked@test.domain.com/resource\",\n\t\t\t\t false);\n\t\ttestList(session, \"partial_blocked@test.domain.com/resource\", \"partial_blocked@test2.domain.com/resource\",\n\t\t\t\t true);\n\t\ttestList(session, \"partial_blocked@test.domain.com/resource\", \"partial_blocked@test.domain.com/resource2\",\n\t\t\t\t true);\n\t\ttestList(session, \"partial_blocked@test.domain.com/resource\", \"partial_blocked2@test.domain.com/resource\",\n\t\t\t\t true);\n\t\ttestList(session, \"partial_blocked@test.domain.com/resource\", \"partial_blocked@test.domain.com\", true);\n\t\ttestList(session, \"partial_blocked@test.domain.com/resource\", \"test.domain.com/true\", true);\n\t\ttestList(session, \"partial_blocked@test.domain.com/resource\", \"test.domain.com\", true);\n\n\t\ttestList(session, \"partial_blocked@test.domain.com\", \"partial_blocked@test.domain.com/resource\", false);\n\t\ttestList(session, \"partial_blocked@test.domain.com\", \"partial_blocked@test.domain.com\", false);\n\t\ttestList(session, \"partial_blocked@test.domain.com\", \"partial_blocked2@test.domain.com\", true);\n\t\ttestList(session, \"partial_blocked@test.domain.com\", \"partial_blocked@test2.domain.com\", true);\n\t\ttestList(session, \"partial_blocked@test.domain.com\", \"test.domain.com/true\", true);\n\t\ttestList(session, \"partial_blocked@test.domain.com\", \"test.domain.com\", true);\n\n\t\ttestList(session, \"test.domain.com/true\", \"partial_blocked@test.domain.com/resource\", true);\n\t\ttestList(session, \"test.domain.com/true\", \"partial_blocked@test.domain.com\", true);\n\t\ttestList(session, \"test.domain.com/true\", \"test.domain.com/true\", false);\n\t\ttestList(session, \"test.domain.com/true\", \"test.domain.com\", true);\n\t\ttestList(session, \"test.domain.com/true\", \"test.domain.com/true2\", true);\n\t\ttestList(session, \"test.domain.com/true\", \"test.2domain.com\", true);\n\n\t\ttestList(session, \"test.domain.com/true\", \"partial_blocked@test2.domain.com/resource\", true);\n\t\ttestList(session, \"test.domain.com/true\", \"partial_blocked@test2.domain.com\", true);\n\t\ttestList(session, \"test.domain.com/true\", \"test2.domain.com/true\", true);\n\t\ttestList(session, \"test.domain.com/true\", \"test.domain.com/true2\", true);\n\t\ttestList(session, \"test.domain.com/true\", \"test2.domain.com\", true);\n\n\t\ttestList(session, \"test.domain.com\", \"partial_blocked@test.domain.com/resource\", false);\n\t\ttestList(session, \"test.domain.com\", \"partial_blocked@test.domain.com\", false);\n\t\ttestList(session, \"test.domain.com\", \"test.domain.com/true\", false);\n\t\ttestList(session, \"test.domain.com\", \"test.domain.com\", false);\n\n\t\ttestList(session, \"test.domain.com\", \"partial_blocked@test2.domain.com/resource\", true);\n\t\ttestList(session, \"test.domain.com\", \"partial_blocked@test2.domain.com\", true);\n\t\ttestList(session, \"test.domain.com\", \"test2.domain.com/true\", true);\n\t\ttestList(session, \"test.domain.com\", \"test2.domain.com\", true);\n\n\t}\n\t\n\tprivate void checkStanzaType(XMPPResourceConnection session, String type, Element additionalChild,\n\t\t\t\t\t\t\t\t int expectedResultSize, StanzaType expectedStanzaType)\n\t\t\tthrows TigaseStringprepException, XMPPException {\n\t\tElement iq = new Element(\"iq\");\n\t\tif (type != null) {\n\t\t\tiq.setAttribute(\"type\", type);\n\t\t}\n\t\tiq.setAttribute(\"from\", \"someuser@domain.com\");\n\t\tElement query = new Element(\"query\", new String[]{\"xmlns\"}, new String[]{JabberIqPrivacy.XMLNS});\n\t\tif (null != additionalChild) {\n\t\t\tquery.addChild(additionalChild);\n\t\t}\n\t\tiq.addChild(query);\n\t\tPacket p = Packet.packetInstance(iq);\n\t\tprivacyFilter.process(p, session, null, results, null);\n\t\tassertEquals(expectedResultSize, results.size());\n\t\tif (expectedResultSize > 0) {\n\t\t\tPacket result = results.poll();\n\t\t\tassertNotNull(result);\n\t\t\tassertEquals(Iq.ELEM_NAME, result.getElemName());\n\t\t\tassertEquals(expectedStanzaType, result.getType());\n\t\t}\n\t}\n\n\tprivate void testList(XMPPResourceConnection session, String listJID, String testJID, boolean shouldBeAllowed)\n\t\t\tthrows TigaseStringprepException, NotAuthorizedException, TigaseDBException {\n\n\t\tJID jid = JID.jidInstance(\"test@example/res-1\");\n\t\tJID blockedJID = JID.jidInstance(listJID);\n\n\t\tElement list = new Element(\"list\", new String[]{\"name\"}, new String[]{\"default\"});\n\t\tlist.addChild(new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t\t  new String[]{\"jid\", blockedJID.toString(), \"deny\", \"100\"}));\n\t\tlist.addChild(new Element(\"item\", new String[]{\"action\", \"order\"}, new String[]{\"allow\", \"110\"}));\n\n\t\tif (session != null) {\n\t\t\tsession.putSessionData(\"active-list\", PrivacyList.create(null, list));\n\t\t} else {\n\t\t\tthis.getUserRepository().setData(jid.getBareJID(), \"privacy\", \"default-list\", \"default\");\n\t\t\tthis.getUserRepository().setData(jid.getBareJID(), \"privacy/default\", \"privacy-list\", list.toString());\n\t\t\tthis.privacyFilter.cache.clear();\n\t\t}\n\n\t\tPacket presence = Packet.packetInstance(\n\t\t\t\tnew Element(\"presence\", new String[]{\"from\", \"to\"}, new String[]{testJID, jid.toString()}));\n\t\tboolean isAllowed = privacyFilter.allowed(presence, session);\n\t\tlog.log(Level.FINE, \"Privacy item: \" + listJID + \", tested item: \" + testJID + \", result: \" + isAllowed);\n\t\tif (shouldBeAllowed) {\n\t\t\tassertTrue(isAllowed);\n\t\t} else {\n\t\t\tassertFalse(isAllowed);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/JabberIqRegisterTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.xmpp.impl;\n\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.form.Field;\nimport tigase.form.Form;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Packet;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.impl.roster.RosterFactory;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.ArrayDeque;\nimport java.util.HashMap;\nimport java.util.Queue;\nimport java.util.UUID;\n\npublic class JabberIqRegisterTest\n\t\textends ProcessorTestCase {\n\n\tprivate JabberIqRegister jabberIqRegister = new JabberIqRegister();\n\n\t@Override\n\tprotected void registerBeans(Kernel kernel) {\n\t\tsuper.registerBeans(kernel);\n\t\tkernel.registerBean(\"eventBus\").asInstance(EventBusFactory.getInstance()).exportable().exec();\n\t\tkernel.registerBean(JabberIqRegister.class).setActive(true).exec();\n\t}\n\n\t@Before\n\t@Override\n\tpublic void setUp() throws Exception {\n\t\tjabberIqRegister = getInstance(JabberIqRegister.class);\n\t\tsuper.setUp();\n\t}\n\n\t@After\n\t@Override\n\tpublic void tearDown() throws Exception {\n\t\tjabberIqRegister = null;\n\t\tsuper.tearDown();\n\t}\n\n\t@Test\n\tpublic void testRegistrationForm() throws TigaseStringprepException, XMPPException {\n\t\tfinal Element iq = new Element(\"iq\", new String[]{\"type\", \"to\", \"id\"},\n\t\t\t\t\t\t\t\t\t   new String[]{\"set\", \"sure.im\", \"some-id\"});\n\t\tfinal Element query = new Element(\"query\", new String[]{\"xmlns\"}, new String[]{\"jabber:iq:register\"});\n\t\tfinal Form form = new Form(\"submit\", \"Registration form\", \"Fill out the form\");\n\t\tform.addField(Field.fieldTextSingle(\"username\", \"wojtektest\", \"Username\"));\n\t\tform.addField(Field.fieldTextPrivate(\"password\", \"pass\", \"Password\"));\n\t\tform.addField(Field.fieldTextSingle(\"email\", \"wojtek@example.com \", \"Email (MUST BE VALID!)\"));\n\n\t\tquery.addChild(form.getElement());\n\t\tiq.addChild(query);\n\n\t\tfinal Packet packet = Packet.packetInstance(iq);\n\t\tfinal JID connId = JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString());\n\t\tpacket.setPacketFrom(connId);\n\n\t\tBareJID userJid = BareJID.bareJIDInstance(\"wojtektest@example.com\");\n\t\tJID userResource = JID.jidInstance(userJid, \"resource\");\n\t\tXMPPResourceConnection session2 = getSession(connId, userResource, false);\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tjabberIqRegister.process(packet, session2, null, results, null);\n\n\t\tAssert.assertTrue(!results.isEmpty());\n\t\tfinal Packet response = results.poll();\n\t\tAssert.assertTrue(StanzaType.result.equals(response.getType()));\n\t}\n\n\t@Test\n\tpublic void testRegistrationFormInvalidJid() throws TigaseStringprepException, XMPPException {\n\t\tfinal Element iq = new Element(\"iq\", new String[]{\"type\", \"to\", \"id\"},\n\t\t\t\t\t\t\t\t\t   new String[]{\"set\", \"sure.im\", \"some-id\"});\n\t\tfinal Element query = new Element(\"query\", new String[]{\"xmlns\"}, new String[]{\"jabber:iq:register\"});\n\t\tfinal Form form = new Form(\"submit\", \"Registration form\", \"Fill out the form\");\n\t\tform.addField(Field.fieldTextSingle(\"username\", \"    wojtektest   \", \"Username\"));\n\t\tform.addField(Field.fieldTextPrivate(\"password\", \"pass\", \"Password\"));\n\t\tform.addField(Field.fieldTextSingle(\"email\", \"wojtek@example.com \", \"Email (MUST BE VALID!)\"));\n\n\t\tquery.addChild(form.getElement());\n\t\tiq.addChild(query);\n\n\t\tfinal Packet packet = Packet.packetInstance(iq);\n\t\tfinal JID connId = JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString());\n\t\tpacket.setPacketFrom(connId);\n\n\t\tBareJID userJid = BareJID.bareJIDInstance(\"wojtektest@example.com\");\n\t\tJID userResource = JID.jidInstance(userJid, \"resource\");\n\t\tXMPPResourceConnection session2 = getSession(connId, userResource, false);\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tjabberIqRegister.process(packet, session2, null, results, null);\n\n\t\tAssert.assertTrue(!results.isEmpty());\n\t\tfinal Packet response = results.poll();\n\t\tAssert.assertTrue(StanzaType.error.equals(response.getType()));\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/JabberIqRegisterWhitelistTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport junit.framework.TestCase;\nimport org.junit.Test;\nimport tigase.db.TigaseDBException;\nimport tigase.kernel.BeanUtils;\nimport tigase.kernel.beans.config.ConfigField;\nimport tigase.kernel.core.DependencyManager;\nimport tigase.xmpp.jid.JID;\n\nimport java.lang.reflect.Field;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.LinkedList;\nimport java.util.Map;\n\n/**\n * Test class for JabberIqPrivacyTest\n * <br>\n * Currently class tests validateList method checking validation of type, subscription and action. Other cases are not\n * tested due to missing instance of XMPPResourceConnection\n */\npublic class JabberIqRegisterWhitelistTest\n\t\textends TestCase {\n\n\tprivate static final String CONNECTION_ID = connectionId(\"127.0.0.2\");\n\n\tprivate static final String connectionId(String remoteAddress) {\n\t\treturn \"c2s@0123456789/127.0.0.1_5222_\" + remoteAddress + \"_50123\";\n\t}\n\n\t@Test\n\tpublic void testRegistrationAllowedDefaultSettings() throws Exception {\n\t\tJabberIqRegister jabberIqRegister = new JabberIqRegister();\n\t\tassertTrue(jabberIqRegister.isRegistrationAllowedForConnection(JID.jidInstance(CONNECTION_ID)));\n\t}\n\n\t@Test\n\tpublic void testRegistrationAllowedNotInBlacklist() throws Exception {\n\t\tHashMap<String, Object> settings = new HashMap<String, Object>();\n\t\tsettings.put(JabberIqRegister.REGISTRATION_BLACKLIST_PROP_KEY, \"127.0.0.3,127.0.0.4\");\n\n\t\tJabberIqRegister jabberIqRegister = new JabberIqRegister();\n\t\tjabberIqRegister.init(settings);\n\t\tassertTrue(jabberIqRegister.isRegistrationAllowedForConnection(JID.jidInstance(CONNECTION_ID)));\n\t}\n\n\t@Test\n\tpublic void testRegistrationAllowedInWhitelist() throws Exception {\n\t\tHashMap<String, Object> settings = new HashMap<String, Object>();\n\t\tsettings.put(JabberIqRegister.WHITELIST_REGISTRATION_ONLY_PROP_KEY, Boolean.TRUE.toString());\n\t\tsettings.put(JabberIqRegister.REGISTRATION_WHITELIST_PROP_KEY, \"127.0.0.2,127.0.0.3\");\n\n\t\tJabberIqRegister jabberIqRegister = new JabberIqRegister();\n\t\tjabberIqRegister.init(settings);\n\t\tassertTrue(jabberIqRegister.isRegistrationAllowedForConnection(JID.jidInstance(CONNECTION_ID)));\n\t}\n\n\t@Test\n\tpublic void testRegistrationAllowedInCIDRWhitelist() throws Exception {\n\t\tHashMap<String, Object> settings = new HashMap<String, Object>();\n\t\tsettings.put(JabberIqRegister.WHITELIST_REGISTRATION_ONLY_PROP_KEY, Boolean.TRUE.toString());\n\t\tsettings.put(JabberIqRegister.REGISTRATION_WHITELIST_PROP_KEY, \"127.0.0.0/24\");\n\n\t\tJabberIqRegister jabberIqRegister = new JabberIqRegister();\n\t\tjabberIqRegister.init(settings);\n\t\tassertTrue(jabberIqRegister.isRegistrationAllowedForConnection(JID.jidInstance(CONNECTION_ID)));\n\t}\n\n\t@Test\n\tpublic void testRegistrationAllowedInCIDRMultipleWhitelist() throws Exception {\n\t\tHashMap<String, Object> settings = new HashMap<String, Object>();\n\t\tsettings.put(JabberIqRegister.WHITELIST_REGISTRATION_ONLY_PROP_KEY, Boolean.TRUE.toString());\n\t\tsettings.put(JabberIqRegister.REGISTRATION_WHITELIST_PROP_KEY, \"127.0.0.0/24,127.0.0.1/24\");\n\n\t\tJabberIqRegister jabberIqRegister = new JabberIqRegister();\n\t\tjabberIqRegister.init(settings);\n\t\tassertTrue(jabberIqRegister.isRegistrationAllowedForConnection(JID.jidInstance(CONNECTION_ID)));\n\t}\n\n\t@Test\n\tpublic void testRegistrationFromMultipleSourcesAllowedInCIDRWhitelist() throws Exception {\n\t\tHashMap<String, Object> settings = new HashMap<String, Object>();\n\t\tsettings.put(JabberIqRegister.WHITELIST_REGISTRATION_ONLY_PROP_KEY, Boolean.TRUE.toString());\n\t\tsettings.put(JabberIqRegister.REGISTRATION_WHITELIST_PROP_KEY, \"127.0.0.0/24\");\n\n\t\tJabberIqRegister jabberIqRegister = new JabberIqRegister();\n\t\tjabberIqRegister.init(settings);\n\t\tfor (int i = 0; i <= 255; i++) {\n\t\t\tassertTrue(\n\t\t\t\t\tjabberIqRegister.isRegistrationAllowedForConnection(JID.jidInstance(connectionId(\"127.0.0.\" + i))));\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testRegistrationNotAllowedWhitelistOnlyEmpty() throws Exception {\n\t\tHashMap<String, Object> settings = new HashMap<String, Object>();\n\t\tsettings.put(JabberIqRegister.WHITELIST_REGISTRATION_ONLY_PROP_KEY, Boolean.TRUE.toString());\n\n\t\tJabberIqRegister jabberIqRegister = new JabberIqRegister();\n\t\tjabberIqRegister.init(settings);\n\t\tassertFalse(jabberIqRegister.isRegistrationAllowedForConnection(JID.jidInstance(CONNECTION_ID)));\n\t}\n\n\t@Test\n\tpublic void testRegistrationNotAllowedWhitelistOnly() throws Exception {\n\t\tHashMap<String, Object> settings = new HashMap<String, Object>();\n\t\tsettings.put(JabberIqRegister.WHITELIST_REGISTRATION_ONLY_PROP_KEY, Boolean.TRUE.toString());\n\t\tsettings.put(JabberIqRegister.REGISTRATION_WHITELIST_PROP_KEY, \"127.0.0.3,127.0.0.4\");\n\n\t\tJabberIqRegister jabberIqRegister = new JabberIqRegister();\n\t\tjabberIqRegister.init(settings);\n\t\tassertFalse(jabberIqRegister.isRegistrationAllowedForConnection(JID.jidInstance(CONNECTION_ID)));\n\t}\n\n\t@Test\n\tpublic void testRegistrationNotAllowedCIDRWhitelistOnly() throws Exception {\n\t\tHashMap<String, Object> settings = new HashMap<String, Object>();\n\t\tsettings.put(JabberIqRegister.WHITELIST_REGISTRATION_ONLY_PROP_KEY, Boolean.TRUE.toString());\n\t\tsettings.put(JabberIqRegister.REGISTRATION_WHITELIST_PROP_KEY, \"127.0.0.0/24\");\n\n\t\tJabberIqRegister jabberIqRegister = new JabberIqRegister();\n\t\tjabberIqRegister.init(settings);\n\t\tfor (int i = 0; i <= 255; i++) {\n\t\t\tassertFalse(\n\t\t\t\t\tjabberIqRegister.isRegistrationAllowedForConnection(JID.jidInstance(connectionId(\"127.0.1.\" + i))));\n\t\t}\n\t}\n\n\tpublic void testRegistrationNotAllowedWhitelistSingle() throws Exception {\n\t\tHashMap<String, Object> settings = new HashMap<String, Object>();\n\t\tsettings.put(JabberIqRegister.WHITELIST_REGISTRATION_ONLY_PROP_KEY, Boolean.TRUE.toString());\n\t\tsettings.put(JabberIqRegister.REGISTRATION_WHITELIST_PROP_KEY, \"127.0.0.3\");\n\n\t\tJabberIqRegister jabberIqRegister = new JabberIqRegister();\n\t\tjabberIqRegister.init(settings);\n\t\tassertFalse(jabberIqRegister.isRegistrationAllowedForConnection(JID.jidInstance(CONNECTION_ID)));\n\t}\n\n\t@Test\n\tpublic void testRegistrationNotAllowedInBlacklist() throws Exception {\n\t\tHashMap<String, Object> settings = new HashMap<String, Object>();\n\t\tsettings.put(JabberIqRegister.REGISTRATION_BLACKLIST_PROP_KEY, \"127.0.0.2,127.0.0.3\");\n\n\t\tJabberIqRegister jabberIqRegister = new JabberIqRegister();\n\t\tjabberIqRegister.init(settings);\n\t\tassertFalse(jabberIqRegister.isRegistrationAllowedForConnection(JID.jidInstance(CONNECTION_ID)));\n\t}\n\n\t@Test\n\tpublic void testRegistrationNotAllowedInBlacklistSingle() throws Exception {\n\t\tHashMap<String, Object> settings = new HashMap<String, Object>();\n\t\tsettings.put(JabberIqRegister.REGISTRATION_BLACKLIST_PROP_KEY, \"127.0.0.2\");\n\n\t\tJabberIqRegister jabberIqRegister = new JabberIqRegister();\n\t\tjabberIqRegister.init(settings);\n\t\tassertFalse(jabberIqRegister.isRegistrationAllowedForConnection(JID.jidInstance(CONNECTION_ID)));\n\t}\n\n\t@Test\n\tpublic void testRegistrationNotAllowedInCIDRBlacklist() throws Exception {\n\t\tHashMap<String, Object> settings = new HashMap<String, Object>();\n\t\tsettings.put(JabberIqRegister.REGISTRATION_BLACKLIST_PROP_KEY, \"127.0.0.0/24\");\n\n\t\tJabberIqRegister jabberIqRegister = new JabberIqRegister();\n\t\tjabberIqRegister.init(settings);\n\t\tfor (int i = 0; i <= 255; i++) {\n\t\t\tassertFalse(\n\t\t\t\t\tjabberIqRegister.isRegistrationAllowedForConnection(JID.jidInstance(connectionId(\"127.0.0.\" + i))));\n\t\t}\n\t}\n\n\t@Test\n\tpublic void testCIDR() {\n\t\tString val = \"192.168.122.0/24\";\n\t\ttigase.xmpp.impl.JabberIqRegister.CIDRAddress addr = tigase.xmpp.impl.JabberIqRegister.CIDRAddress.parse(val);\n\t\tassertEquals(val, addr.toString());\n\t\tval = \"193.34.32.0/19\";\n\t\taddr = tigase.xmpp.impl.JabberIqRegister.CIDRAddress.parse(val);\n\t\tassertEquals(val, addr.toString());\n\t}\n\n\tprivate class JabberIqRegister\n\t\t\textends tigase.xmpp.impl.JabberIqRegister {\n\n\t\t@Override\n\t\tpublic void init(Map<String, Object> settings) throws TigaseDBException {\n\t\t\tfinal Field[] fields = DependencyManager.getAllFields(this.getClass());\n\t\t\tfor (Field field : fields) {\n\n\t\t\t\tConfigField configField = field.getAnnotation(ConfigField.class);\n\n\t\t\t\tif (configField == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t} else {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tObject value = settings.getOrDefault(field.getName(), settings.get(configField.alias()));\n\t\t\t\t\t\tif (value != null) {\n\t\t\t\t\t\t\tif (boolean.class.equals(field.getType())) {\n\t\t\t\t\t\t\t\tvalue = Boolean.valueOf(value.toString());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (LinkedList.class.equals(field.getType())) {\n\t\t\t\t\t\t\t\tvalue = new LinkedList<String>(Arrays.asList(value.toString().split(\",\")));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tBeanUtils.setValue(this, field, value);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (Exception ex) {\n\t\t\t\t\t\tthrow new RuntimeException(ex);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t;\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/MIXProcessorTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.component.exceptions.RepositoryException;\nimport tigase.db.TigaseDBException;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.server.Message;\nimport tigase.server.Packet;\nimport tigase.server.PolicyViolationException;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.Authorization;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.XMPPException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.impl.roster.RosterAbstract;\nimport tigase.xmpp.impl.roster.RosterElement;\nimport tigase.xmpp.impl.roster.RosterFactory;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.ArrayDeque;\nimport java.util.Map;\n\nimport static junit.framework.Assert.assertFalse;\nimport static junit.framework.Assert.assertNotNull;\nimport static junit.framework.Assert.assertNull;\nimport static junit.framework.Assert.assertTrue;\nimport static junit.framework.TestCase.assertEquals;\nimport static tigase.xmpp.impl.MIXProcessor.generateParticipantId;\n\npublic class MIXProcessorTest\n\textends ProcessorTestCase {\n\n\tprivate JID mixChannel;\n\tprivate XMPPResourceConnection session;\n\tprivate JID user;\n\n\t@Test\n\tpublic void canHandleRetractedParticipantEvent() throws TigaseStringprepException {\n\n\t\tvar packet = getRetractedParticipantMessage(mixChannel.getBareJID(), user.getBareJID());\n\t\tvar mixProcessor = getKernel().getInstance(MIXProcessor.class);\n\t\tassertEquals(Authorization.AUTHORIZED, mixProcessor.canHandle(packet, session));\n\t}\n\n\tprivate Packet getRetractedParticipantMessage(BareJID fromMixChannel, BareJID toUser)\n\t\tthrows TigaseStringprepException {\n\t\tvar messageElement = new Element(\"message\", new String[]{\"xmlns\", \"from\", \"to\"},\n\t\t                                 new String[]{Message.CLIENT_XMLNS, fromMixChannel.toString(), toUser.toString()});\n\t\tvar event = new Element(\"event\", new String[]{\"xmlns\"}, new String[]{\"http://jabber.org/protocol/pubsub#event\"});\n\t\tvar items = new Element(\"items\", new String[]{\"node\"}, new String[]{\"urn:xmpp:mix:nodes:participants\"});\n\t\tvar retracted = new Element(\"retract\");\n\t\tretracted.addAttribute(\"id\", generateParticipantId(fromMixChannel, toUser));\n\t\titems.addChild(retracted);\n\t\tevent.addChild(items);\n\t\tmessageElement.addChild(event);\n\t\tvar packet = Message.packetInstance(messageElement);\n\t\tpacket.setPacketFrom(mixChannel);\n\t\treturn packet;\n\t}\n\n\t@Before\n\tpublic void prepare() throws TigaseStringprepException, NotAuthorizedException {\n\t\tgetKernel().registerBean(\"eventbus\").asInstance(EventBusFactory.getInstance()).exportable().exec();\n\t\tgetKernel().registerBean(\"sess-man\").asInstance(new SessionManager()).setActive(true).exportable().exec();\n\t\tgetKernel().registerBean(RosterFactory.Bean.class).setActive(true).exec();\n\t\tgetKernel().getInstance(RosterFactory.Bean.class);\n\t\tgetKernel().registerBean(MIXProcessor.class).setActive(true).exec();\n\n\t\tmixChannel = JID.jidInstance(\"d6af5eea-ccc2-4b94-8402-387fee89ece9@mix.domain.com\");\n\t\tuser = JID.jidInstance(\"user1@example.com/res1\");\n\t\tsession = getSession(user, user, true);\n\t}\n\n\t@Test\n\tpublic void testNotRemoveMixChannelFromRosterOnUserRetractedEventNotMatchingId()\n\t\tthrows TigaseStringprepException, XMPPException, TigaseDBException, PolicyViolationException {\n\n\t\tRosterAbstract rosterUtil = RosterFactory.getRosterImplementation(true);\n\t\tvar mixChannelRosterElement = new RosterElement(mixChannel, mixChannel.toString(), null);\n\t\trosterUtil.addJidToRoster(getUserRepository(), session.getParentSession(), user.getBareJID(),\n\t\t                          mixChannelRosterElement);\n\n\t\tvar rosterItems = rosterUtil.getRosterItems(session);\n\t\tassertFalse(rosterItems.isEmpty());\n\t\tvar mixRosterItem = rosterUtil.getBuddyItem(session, mixChannel);\n\t\tassertNotNull(mixRosterItem);\n\n\t\tvar processor = getKernel().getInstance(MIXProcessor.class);\n\t\tJID otherUser = JID.jidInstance(\"user2@example.com/res2\");\n\t\tvar packet = getRetractedParticipantMessage(mixChannel.getBareJID(), otherUser.getBareJID());\n\t\tvar results = new ArrayDeque<Packet>();\n\t\tprocessor.process(packet, session, null, results, Map.of());\n\t\tassertEquals(0, results.size());\n\t\tvar rosterItemsAfterProcessing = rosterUtil.getRosterItems(session);\n\t\tassertFalse(rosterItemsAfterProcessing.isEmpty());\n\t\tvar mixRosterItemAfterProcessing = rosterUtil.getBuddyItem(session, mixChannel);\n\t\tassertNotNull(mixRosterItemAfterProcessing);\n\n\t}\n\n\t@Test\n\tpublic void testRemoveMixChannelFromRosterOnUserRetractedEventMatchingId()\n\t\tthrows TigaseStringprepException, XMPPException, TigaseDBException, PolicyViolationException {\n\n\t\tRosterAbstract rosterUtil = RosterFactory.getRosterImplementation(true);\n\t\tvar mixChannelRosterElement = new RosterElement(mixChannel, mixChannel.toString(), null);\n\t\trosterUtil.addJidToRoster(getUserRepository(), session.getParentSession(), user.getBareJID(),\n\t\t                          mixChannelRosterElement);\n\n\t\tvar rosterItems = rosterUtil.getRosterItems(session);\n\t\tassertFalse(rosterItems.isEmpty());\n\t\tvar mixRosterItem = rosterUtil.getBuddyItem(session, mixChannel);\n\t\tassertNotNull(mixRosterItem);\n\n\t\tvar processor = getKernel().getInstance(MIXProcessor.class);\n\t\tvar packet = getRetractedParticipantMessage(mixChannel.getBareJID(), user.getBareJID());\n\t\tvar results = new ArrayDeque<Packet>();\n\t\tprocessor.process(packet, session, null, results, Map.of());\n\t\tassertEquals(1, results.size());\n\t\tfinal Element rosterElement = results.getFirst().getElement();\n\t\tassertEquals(\"jabber:iq:roster\", rosterElement.getChild(\"query\").getXMLNS());\n\t\tassertEquals(\"remove\", rosterElement.getChild(\"query\").getChild(\"item\").getAttributeStaticStr(\"subscription\"));\n\n\t\tvar rosterItemsAfterProcessing = rosterUtil.getRosterItems(session);\n\t\tassertTrue(rosterItemsAfterProcessing.isEmpty());\n\t\tvar mixRosterItemAfterProcessing = rosterUtil.getBuddyItem(session, mixChannel);\n\t\tassertNull(mixRosterItemAfterProcessing);\n\n\t}\n\n\t@Test\n\tpublic void testSha() throws RepositoryException {\n\t\tmixChannel = JID.jidInstanceNS(\"3dc9222b-3316-41f6-927f-fb7f7dd4ad1f@mix.atlantiscity\");\n\t\tuser = JID.jidInstanceNS(\"tigase3@atlantiscity/atlantiscity\");\n\t\tvar participantId = generateParticipantId(mixChannel.getBareJID(), user.getBareJID());\n\t\tassertEquals(\"318cd48257571b007eb68d759f9c59a3736a1584\", participantId);\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/MessageAmpTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.db.MsgRepositoryIfc;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Packet;\nimport tigase.server.amp.db.MsgRepository;\nimport tigase.xml.Element;\nimport tigase.xmpp.PacketErrorTypeException;\nimport tigase.xmpp.PacketInvalidAddressException;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.lang.reflect.Field;\nimport java.util.*;\n\nimport static org.junit.Assert.*;\n\n/**\n * @author andrzej\n */\npublic class MessageAmpTest\n\t\textends ProcessorTestCase {\n\n\tprivate static final String XMLNS = \"http://jabber.org/protocol/amp\";\n\n\tprivate JID ampJid;\n\tprivate MessageAmp messageAmp;\n\n\t@Before\n\t@Override\n\tpublic void setUp() throws Exception {\n\t\tsuper.setUp();\n\t\tmessageAmp = getInstance(MessageAmp.class);\n\t\tMap<String, Object> settings = new HashMap<String, Object>();\n\t\tampJid = JID.jidInstance(\"amp@example1.com\");\n\t\tField f = messageAmp.getClass().getDeclaredField(\"ampJID\");\n\t\tf.setAccessible(true);\n\t\tf.set(messageAmp, ampJid);\n\t}\n\n\t@After\n\t@Override\n\tpublic void tearDown() throws Exception {\n\t\tmessageAmp = null;\n\t\tsuper.tearDown();\n\t}\n\n\t@Test\n\tpublic void testMessageProcessingWithAmp() throws Exception {\n\t\tJID senderJid = JID.jidInstance(\"sender@example.com/res-1\");\n\t\tJID recipientJid = JID.jidInstance(\"recipient@example.com/res-2\");\n\t\tXMPPResourceConnection senderSession = getSession(\n\t\t\t\tJID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()), senderJid);\n\t\tXMPPResourceConnection recipientSession = getSession(\n\t\t\t\tJID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()), recipientJid);\n\n\t\tElement messageEl = new Element(\"message\", new String[]{\"from\", \"to\", \"type\"},\n\t\t\t\t\t\t\t\t\t\tnew String[]{senderJid.toString(), recipientJid.toString(), \"chat\"});\n\t\tmessageEl.addChild(new Element(\"body\", \"Test 123\"));\n\t\tElement ampEl = new Element(\"amp\", new String[]{\"xmlns\"}, new String[]{XMLNS});\n\t\tampEl.addChild(new Element(\"rule\", new String[]{\"condition\", \"action\", \"value\"},\n\t\t\t\t\t\t\t\t   new String[]{\"expire-at\", \"drop\", \"2024-01-01T00:00:00Z\"}));\n\t\tmessageEl.addChild(ampEl);\n\t\tPacket message = Packet.packetInstance(messageEl);\n\t\tmessage.setPacketFrom(senderSession.getConnectionId());\n\n\t\tMap<String, Object> settings = new HashMap<>();\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tassertFalse(messageAmp.preProcess(message, senderSession, null, results, settings));\n\t\tassertEquals(0, results.size());\n\n\t\tmessageAmp.process(message, senderSession, null, results, settings);\n\t\tassertEquals(1, results.size());\n\t\tmessage = results.poll();\n\t\tassertEquals(senderSession.getConnectionId().toString(), message.getAttributeStaticStr(\"from-conn-id\"));\n\t\tassertEquals(ampJid, message.getPacketTo());\n\n\t\tmessage.getElement().removeAttribute(\"from-conn-id\");\n\t\tmessage.setPacketTo(null);\n\t\tassertFalse(messageAmp.preProcess(message, senderSession, null, results, settings));\n\t\tassertEquals(0, results.size());\n\n\t\tassertTrue(messageAmp.preProcess(message, recipientSession, null, results, settings));\n\t\tassertEquals(1, results.size());\n\t\tmessage = results.poll();\n\t\tassertEquals(null, message.getAttributeStaticStr(\"from-conn-id\"));\n\t\tassertEquals(ampJid, message.getPacketTo());\n\n\t\tmessage.setPacketFrom(ampJid);\n\t\tmessage.setPacketTo(null);\n\t\tassertFalse(messageAmp.preProcess(message, recipientSession, null, results, settings));\n\t\tassertEquals(0, results.size());\n\n\t\tmessageAmp.process(message, recipientSession, null, results, settings);\n\t\tassertEquals(1, results.size());\n\t\tmessage = results.poll();\n\t\tassertEquals(null, message.getAttributeStaticStr(\"from-conn-id\"));\n\t\tassertEquals(recipientSession.getConnectionId(), message.getPacketTo());\n\t}\n\n\t@Test\n\tpublic void testSilentlyIgnoringMessages() throws Exception {\n\t\tBareJID userJid = BareJID.bareJIDInstance(\"user1@example.com\");\n\t\tJID res1 = JID.jidInstance(userJid, \"res1\");\n\n\t\t// testing default behaviour - error message\n\t\tElement packetEl = new Element(\"message\", new String[]{\"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t   new String[]{\"remote-user@test.com/res1\", res1.toString()});\n\t\tPacket packet = Packet.packetInstance(packetEl);\n\t\tQueue<Packet> results = new ArrayDeque<Packet>();\n\t\tmessageAmp.process(packet, null, null, results, null);\n\t\tassertTrue(\"no error was generated\", !results.isEmpty());\n\t\tassertTrue(\"generated result is not an error\", results.poll().getType().equals(StanzaType.error));\n\n\t\t// testing silently ignoring error responses\n\t\tresults.clear();\n\n\t\tMessageDeliveryLogic messageProcessor = ((Kernel) this.getInstance(\"amp#KERNEL\")).getInstance(MessageDeliveryLogic.class);\n\t\tField f = messageProcessor.getClass().getDeclaredField(\"silentlyIgnoreError\");\n\t\tf.setAccessible(true);\n\t\tf.set(messageProcessor, true);\n\n\t\tmessageAmp.process(packet, null, null, results, null);\n\t\tassertTrue(\"result was generated\", results.isEmpty());\n\t}\n\n\t@Test(expected = PacketInvalidAddressException.class)\n\tpublic void testMessageToOfflineUserWithoutFrom() throws Exception {\n\t\tBareJID userJid = BareJID.bareJIDInstance(\"user1@example.com\");\n\t\tJID res1 = JID.jidInstance(userJid, \"res1\");\n\n\t\tElement packetEl = new Element(\"message\", new String[]{\"to\"}, new String[]{res1.toString()});\n\t\tPacket packet = Packet.packetInstance(packetEl);\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tmessageAmp.process(packet, null, null, results, null);\n\t}\n\n\t@Test\n\tpublic void testMessageProcessingWithoutAmp() throws Exception {\n\t\tJID senderJid = JID.jidInstance(\"sender@example.com/res-1\");\n\t\tJID recipientJid = JID.jidInstance(\"recipient@example.com/res-2\");\n\t\tXMPPResourceConnection senderSession = getSession(\n\t\t\t\tJID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()), senderJid);\n\t\tXMPPResourceConnection recipientSession = getSession(\n\t\t\t\tJID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()), recipientJid);\n\n\t\tElement messageEl = new Element(\"message\", new String[]{\"from\", \"to\", \"type\"},\n\t\t\t\t\t\t\t\t\t\tnew String[]{senderJid.toString(), recipientJid.toString(), \"chat\"});\n\t\tmessageEl.addChild(new Element(\"body\", \"Test 123\"));\n\n\t\tPacket message = Packet.packetInstance(messageEl);\n\t\tmessage.setPacketFrom(senderSession.getConnectionId());\n\n\t\tMap<String, Object> settings = new HashMap<>();\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tassertFalse(messageAmp.preProcess(message, senderSession, null, results, settings));\n\t\tassertEquals(0, results.size());\n\n\t\tmessageAmp.process(message, senderSession, null, results, settings);\n\t\tassertEquals(1, results.size());\n\t\tmessage = results.poll();\n\t\tassertEquals(null, message.getAttributeStaticStr(\"from-conn-id\"));\n\t\tassertEquals(null, message.getPacketFrom());\n\t\tassertEquals(null, message.getPacketTo());\n\n\t\tassertFalse(messageAmp.preProcess(message, recipientSession, null, results, settings));\n\t\tassertEquals(0, results.size());\n\n\t\tmessageAmp.process(message, recipientSession, null, results, settings);\n\t\tassertEquals(1, results.size());\n\t\tmessage = results.poll();\n\t\tassertEquals(null, message.getAttributeStaticStr(\"from-conn-id\"));\n\t\tassertEquals(recipientSession.getConnectionId(), message.getPacketTo());\n\t}\n\n\t@Override\n\tprotected void registerBeans(Kernel kernel) {\n\t\tkernel.setForceAllowNull(true);\n\t\tsuper.registerBeans(kernel);\n\t\tkernel.registerBean(MessageDeliveryLogic.class).exec();\n\t\tMsgRepositoryIfc msgRepo = new MsgRepository.MsgRepositoryMDBean();\n\t\tkernel.registerBean(\"msgRepository\").asInstance(msgRepo).exportable().exec();\n\t\tkernel.registerBean(MessageAmp.class).exec();\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/MessageCarbonsTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\n\nimport static org.junit.Assert.*;\n\n/**\n * @author andrzej\n */\npublic class MessageCarbonsTest\n\t\textends ProcessorTestCase {\n\n\tprivate MessageCarbons carbonsProcessor;\n\n\t@Override\n\tprotected void registerBeans(Kernel kernel) {\n\t\tsuper.registerBeans(kernel);\n\t\tkernel.registerBean(MessageDeliveryLogic.class).exec();\n\t\tkernel.registerBean(MessageCarbons.class).setActive(true).exec();\n\t}\n\n\t@Before\n\t@Override\n\tpublic void setUp() throws Exception {\n\t\tsuper.setUp();\n\t\tcarbonsProcessor = getInstance(MessageCarbons.class);\n\t}\n\n\t@Test\n\tpublic void testDisco() throws Exception {\n\t\tassertTrue(Arrays.stream(carbonsProcessor.supDiscoFeatures(null))\n\t\t\t\t\t\t   .anyMatch(el -> \"urn:xmpp:carbons:2\".equals(el.getAttributeStaticStr(\"var\"))));\n\t\tassertTrue(Arrays.stream(carbonsProcessor.supDiscoFeatures(null))\n\t\t\t\t\t\t   .anyMatch(el -> \"urn:xmpp:carbons:rules:0\".equals(el.getAttributeStaticStr(\"var\"))));\n\t}\n\n\t@Test\n\tpublic void testDeliveryOfMessageWithBody() throws Exception {\n\t\tPacket packet = Packet.packetInstance(new Element(\"message\", new Element[] { new Element(\"body\") }, new String[] { \"type\"}, new String[] {\"chat\"}));\n\t\tassertTrue(carbonsProcessor.shouldSendCarbons(packet, null));\n\t\tpacket = Packet.packetInstance(new Element(\"message\", new Element[] { new Element(\"body\") }, new String[] { \"type\"}, new String[] {\"normal\"}));\n\t\tassertTrue(carbonsProcessor.shouldSendCarbons(packet, null));\n\t\tpacket = Packet.packetInstance(new Element(\"message\", new Element[] { new Element(\"body\") }, null, null));\n\t\tassertTrue(carbonsProcessor.shouldSendCarbons(packet, null));\n\t\tpacket = Packet.packetInstance(new Element(\"message\", new Element[] { new Element(\"body\") }, new String[] { \"type\"}, new String[] {\"groupchat\"}));\n\t\tassertFalse(carbonsProcessor.shouldSendCarbons(packet, null));\n\t}\n\n\t@Test\n\tpublic void testDeliveryOfMessageWithIMPayload() throws Exception {\n\t\tPacket packet = Packet.packetInstance(new Element(\"message\"));\n\t\tassertFalse(carbonsProcessor.shouldSendCarbons(packet, null));\n\n\t\tpacket.getElement().setChildren(List.of(new Element(\"received\", new String[]{\"xmlns\"}, new String[] {\"urn:xmpp:receipts\"})));\n\t\tassertTrue(carbonsProcessor.shouldSendCarbons(packet, null));\n\t\tpacket.getElement().setChildren(List.of(new Element(\"active\", new String[]{\"xmlns\"}, new String[] {\"http://jabber.org/protocol/chatstates\"})));\n\t\tassertTrue(carbonsProcessor.shouldSendCarbons(packet, null));\n\t\tpacket.getElement().setChildren(List.of(new Element(\"received\", new String[]{\"xmlns\"}, new String[] {\"urn:xmpp:chat-markers:0\"})));\n\t\tassertTrue(carbonsProcessor.shouldSendCarbons(packet, null));\n\t}\n\n\t@Test\n\tpublic void testDeliveryOfMucMessage_Groupchat() throws Exception {\n\t\tPacket packet = Packet.packetInstance(new Element(\"message\", new Element[] { new Element(\"body\") }, new String[] { \"type\"}, new String[] {\"groupchat\"}));\n\t\tassertFalse(carbonsProcessor.shouldSendCarbons(packet, null));\n\t}\n\n\t@Test\n\tpublic void testDeliveryOfMucMessage_DirectInvitation() throws Exception {\n\t\tPacket packet = Packet.packetInstance(new Element(\"message\"));\n\t\tpacket.getElement()\n\t\t\t\t.setChildren(List.of(new Element(\"x\", new Element[]{new Element(\"invite\")}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t\t new String[]{\"http://jabber.org/protocol/muc#user\"})));\n\t\tassertTrue(carbonsProcessor.shouldSendCarbons(packet, null));\n\t}\n\n\t@Test\n\tpublic void testDeliveryOfMucMessage_SentPM() throws Exception {\n\t\tJID sender = JID.jidInstanceNS(\"user@domain.com/res-1\");\n\t\tJID recipient = JID.jidInstanceNS(\"room@muc.domain.com/Julia\");\n\t\tXMPPResourceConnection session = getSession(sender, sender);\n\t\tPacket packet = Packet.packetInstance(new Element(\"message\", new Element[] { new Element(\"body\"), new Element(\"x\", new String[]{\"xmlns\"}, new String[]{\"http://jabber.org/protocol/muc#user\"}) }, new String[] { \"type\"}, new String[] {\"chat\"}), sender, recipient);\n\t\tassertTrue(carbonsProcessor.shouldSendCarbons(packet, session));\n\t}\n\n\t@Test\n\tpublic void testDeliveryOfMucMessage_ReceivedPM() throws Exception {\n\t\tJID recipient = JID.jidInstanceNS(\"user@domain.com/res-1\");\n\t\tJID sender = JID.jidInstanceNS(\"room@muc.domain.com/Julia\");\n\t\tXMPPResourceConnection session = getSession(recipient, recipient);\n\t\tPacket packet = Packet.packetInstance(new Element(\"message\", new Element[] { new Element(\"body\"), new Element(\"x\", new String[]{\"xmlns\"}, new String[]{\"http://jabber.org/protocol/muc#user\"}) }, new String[] { \"type\"}, new String[] {\"chat\"}), sender, recipient);\n\t\tassertFalse(carbonsProcessor.shouldSendCarbons(packet, session));\n\t}\n\n\t@Test\n\tpublic void testDeliveryOfMucMessage_MediatedInvitation() throws Exception {\n\t\tPacket packet = Packet.packetInstance(new Element(\"message\"));\n\t\tpacket.getElement().setChildren(List.of(new Element(\"x\", new String[]{\"xmlns\"}, new String[] {\"jabber:x:conference\"})));\n\t\tassertTrue(carbonsProcessor.shouldSendCarbons(packet, null));\n\t}\n\n\t@Test\n\tpublic void testResourceSelectionForMessageDeliveryForBareJid() throws Exception {\n\t\tBareJID userJid = BareJID.bareJIDInstance(\"user1@example.com\");\n\t\tJID res1 = JID.jidInstance(userJid, \"res1\");\n\t\tJID res2 = JID.jidInstance(userJid, \"res2\");\n\t\tXMPPResourceConnection session1 = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()),\n\t\t\t\t\t\t\t\t\t\t\t\t\t res1);\n\t\tXMPPResourceConnection session2 = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()),\n\t\t\t\t\t\t\t\t\t\t\t\t\t res2);\n\n\t\tsession2.putSessionData(MessageCarbons.XMLNS + \"-enabled\", true);\n\t\tMap<JID, Boolean> enabled = new HashMap<>();\n\t\tenabled.put(res2, true);\n\t\tsession2.putCommonSessionData(MessageCarbons.XMLNS + \"-resources\", enabled);\n\t\tassertEquals(Arrays.asList(session1, session2), session1.getActiveSessions());\n\n\t\tElement packetEl = new Element(\"message\", new String[]{\"type\", \"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t   new String[]{\"chat\", \"remote-user@test.com/res1\", userJid.toString()});\n\t\tPacket packet = Packet.packetInstance(packetEl);\n\t\tQueue<Packet> results = new ArrayDeque<Packet>();\n\t\tcarbonsProcessor.process(packet, session2, null, results, null);\n\t\tassertEquals(\"generated result even than no resource had nonnegative priority\", 1, results.size());\n\t\tassertEquals(\"packet sent to wrong jids\", Arrays.asList(session2.getJID()), collectStanzaTo(results));\n\n\t\tsession1.setPresence(new Element(\"presence\"));\n\t\tresults = new ArrayDeque<Packet>();\n\t\tcarbonsProcessor.process(packet, session2, null, results, null);\n\t\tassertEquals(\"not generated result even than 1 resource had nonnegative priority\", 1, results.size());\n\t\tassertEquals(\"packet sent to wrong jids\", Arrays.asList(session2.getJID()), collectStanzaTo(results));\n\n\t\tsession2.setPresence(new Element(\"presence\"));\n\t\tresults = new ArrayDeque<Packet>();\n\t\tcarbonsProcessor.process(packet, session1, null, results, null);\n\t\tassertEquals(\"not generated result even than 2 resource had nonnegative priority\", 0, results.size());\n\t\tassertEquals(\"packet sent to wrong jids\", Arrays.asList(), collectStanzaTo(results));\n\t}\n\n\t@Test\n\tpublic void testResourceSelectionForMessageDeliveryForFullJid() throws Exception {\n\t\tBareJID userJid = BareJID.bareJIDInstance(\"user1@example.com\");\n\t\tJID res1 = JID.jidInstance(userJid, \"res1\");\n\t\tJID res2 = JID.jidInstance(userJid, \"res2\");\n\t\tXMPPResourceConnection session1 = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()),\n\t\t\t\t\t\t\t\t\t\t\t\t\t res1);\n\t\tXMPPResourceConnection session2 = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()),\n\t\t\t\t\t\t\t\t\t\t\t\t\t res2);\n\n\t\tsession2.putSessionData(MessageCarbons.XMLNS + \"-enabled\", true);\n\t\tMap<JID, Boolean> enabled = new HashMap<>();\n\t\tenabled.put(res2, true);\n\t\tsession2.putCommonSessionData(MessageCarbons.XMLNS + \"-resources\", enabled);\n\t\tassertEquals(Arrays.asList(session1, session2), session1.getActiveSessions());\n\n\t\tElement packetEl = new Element(\"message\", new String[]{\"type\", \"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t   new String[]{\"chat\", \"remote-user@test.com/res1\", res1.toString()});\n\t\tPacket packet = Packet.packetInstance(packetEl);\n\t\tQueue<Packet> results = new ArrayDeque<Packet>();\n\t\tcarbonsProcessor.process(packet, session1, null, results, null);\n\t\tassertEquals(\"generated result even than no resource had nonnegative priority\", 1, results.size());\n\t\tassertEquals(\"packet sent to wrong jids\", Arrays.asList(session2.getJID()), collectStanzaTo(results));\n\n\t\tsession1.setPresence(new Element(\"presence\"));\n\t\tresults = new ArrayDeque<Packet>();\n\t\tcarbonsProcessor.process(packet, session1, null, results, null);\n\t\tassertEquals(\"not generated result even than 1 resource had nonnegative priority\", 1, results.size());\n\t\tassertEquals(\"packet sent to wrong jids\", Arrays.asList(session2.getJID()), collectStanzaTo(results));\n\n\t\tsession2.setPresence(new Element(\"presence\"));\n\t\tresults = new ArrayDeque<Packet>();\n\t\tcarbonsProcessor.process(packet, session1, null, results, null);\n\t\tassertEquals(\"not generated result even than 2 resource had nonnegative priority\", 1, results.size());\n\t\tassertEquals(\"packet sent to wrong jids\", Arrays.asList(session2.getJID()), collectStanzaTo(results));\n\n\t\tresults = new ArrayDeque<Packet>();\n\t\tPacket packet1 = packet.copyElementOnly();\n\t\tpacket1.getElement().addChild(new Element(\"no-copy\", new String[]{\"xmlns\"}, new String[]{\"urn:xmpp:hints\"}));\n\t\tcarbonsProcessor.process(packet1, session1, null, results, null);\n\t\tassertEquals(\"generated result even that no-copy was sent\", 0, results.size());\n\t\tassertEquals(\"packet sent to wrong jids\", Collections.EMPTY_LIST, collectStanzaTo(results));\n\n\t}\n\n\t@Test\n\tpublic void testResourceSelectionForMessageDeliveryForFullJid_NotChat() throws Exception {\n\t\tBareJID userJid = BareJID.bareJIDInstance(\"user1@example.com\");\n\t\tJID res1 = JID.jidInstance(userJid, \"res1\");\n\t\tJID res2 = JID.jidInstance(userJid, \"res2\");\n\t\tXMPPResourceConnection session1 = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()),\n\t\t\t\t\t\t\t\t\t\t\t\t\t res1);\n\t\tXMPPResourceConnection session2 = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()),\n\t\t\t\t\t\t\t\t\t\t\t\t\t res2);\n\n\t\tsession2.putSessionData(MessageCarbons.XMLNS + \"-enabled\", true);\n\t\tMap<JID, Boolean> enabled = new HashMap<>();\n\t\tenabled.put(res2, true);\n\t\tsession2.putCommonSessionData(MessageCarbons.XMLNS + \"-resources\", enabled);\n\t\tassertEquals(Arrays.asList(session1, session2), session1.getActiveSessions());\n\n\t\tElement packetEl = new Element(\"message\", new String[]{\"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t   new String[]{\"remote-user@test.com/res1\", res1.toString()});\n\t\tPacket packet = Packet.packetInstance(packetEl);\n\t\tQueue<Packet> results = new ArrayDeque<Packet>();\n\t\tcarbonsProcessor.process(packet, session1, null, results, null);\n\t\tassertEquals(\"generated result even than no resource had nonnegative priority\", 0, results.size());\n\t\tassertEquals(\"packet sent to wrong jids\", Collections.emptyList(), collectStanzaTo(results));\n\n\t\tpacketEl = new Element(\"message\", new String[]{\"from\", \"to\"},\n\t\t\t\t\t\t\t   new String[]{\"remote-user@test.com/res1\", res1.toString()});\n\t\tpacketEl.addChild(new Element(\"received\", new String[] {\"xmlns\"}, new String[] {\"urn:xmpp:receipts\"}));\n\t\tpacket = Packet.packetInstance(packetEl);\n\t\tresults = new ArrayDeque<Packet>();\n\t\tcarbonsProcessor.process(packet, session1, null, results, null);\n\t\tassertEquals(\"generated result even than no resource had nonnegative priority\", 1, results.size());\n\t\tassertEquals(\"packet sent to wrong jids\", Arrays.asList(session2.getJID()), collectStanzaTo(results));\n\n\t\tsession1.setPresence(new Element(\"presence\"));\n\t\tresults = new ArrayDeque<Packet>();\n\t\tcarbonsProcessor.process(packet, session1, null, results, null);\n\t\tassertEquals(\"not generated result even than 1 resource had nonnegative priority\", 1, results.size());\n\t\tassertEquals(\"packet sent to wrong jids\", Arrays.asList(session2.getJID()), collectStanzaTo(results));\n\n\t\tsession2.setPresence(new Element(\"presence\"));\n\t\tresults = new ArrayDeque<Packet>();\n\t\tcarbonsProcessor.process(packet, session1, null, results, null);\n\t\tassertEquals(\"not generated result even than 2 resource had nonnegative priority\", 1, results.size());\n\t\tassertEquals(\"packet sent to wrong jids\", Arrays.asList(session2.getJID()), collectStanzaTo(results));\n\n\t\tresults = new ArrayDeque<Packet>();\n\t\tPacket packet1 = packet.copyElementOnly();\n\t\tpacket1.getElement().addChild(new Element(\"no-copy\", new String[]{\"xmlns\"}, new String[]{\"urn:xmpp:hints\"}));\n\t\tcarbonsProcessor.process(packet1, session1, null, results, null);\n\t\tassertEquals(\"generated result even that no-copy was sent\", 0, results.size());\n\t\tassertEquals(\"packet sent to wrong jids\", Collections.EMPTY_LIST, collectStanzaTo(results));\n\n\t}\n\n\tprotected List<JID> collectStanzaTo(Queue<Packet> packets) {\n\t\tList<JID> result = new ArrayList<JID>();\n\t\tPacket p;\n\t\twhile ((p = packets.poll()) != null) {\n\t\t\tresult.add(p.getStanzaTo());\n\t\t}\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/MessageDeliveryLogicTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Packet;\nimport tigase.vhosts.DummyVHostManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.UUID;\n\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * @author andrzej\n */\npublic class MessageDeliveryLogicTest\n\t\textends ProcessorTestCase {\n\n\tfinal static String domain = \"example.com\";\n\tprivate MessageDeliveryLogic messageDeliveryLogic;\n\n\t@Override\n\tprotected void registerBeans(Kernel kernel) {\n\t\tsuper.registerBeans(kernel);\n\t\tkernel.registerBean(MessageDeliveryLogic.class).exec();\n\t}\n\n\t@Before\n\t@Override\n\tpublic void setUp() throws Exception {\n\t\tsuper.setUp();\n\t\tmessageDeliveryLogic = getInstance(MessageDeliveryLogic.class);\n\t\tvar vHostManager = getInstance(DummyVHostManager.class);\n\t\tvHostManager.addVhost(domain);\n\t}\n\n\t@Test\n\tpublic void testProcessingErrorMessageBareJidOffline() throws Exception {\n\t\tvar destinationUserJid = BareJID.bareJIDInstance(\"user1\", domain);\n\t\tvar packetElelement = new Element(\"message\", new String[]{\"type\", \"from\", \"to\"},\n\t\t                                  new String[]{\"error\", \"remote-user@test.com/res1\",\n\t\t                                               destinationUserJid.toString()});\n\t\tvar packet = Packet.packetInstance(packetElelement);\n\t\tfinal boolean block = messageDeliveryLogic.preProcessFilter(packet, null);\n\t\tassertTrue(\"BareJID: No Available or Connected Resources > 8.5.2.2.1. Message: drop message\", block);\n\t}\n\n\t@Test\n\tpublic void testProcessingErrorMessageBareJidOnline() throws Exception {\n\t\tvar destinationUserJid = BareJID.bareJIDInstance(\"user1\", domain);\n\t\tvar fullDestinationJid = JID.jidInstance(destinationUserJid, \"res1\");\n\t\tvar session = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID()), fullDestinationJid);\n\n\t\tvar packetElelement = new Element(\"message\", new String[]{\"type\", \"from\", \"to\"},\n\t\t                                  new String[]{\"error\", \"remote-user@test.com/res1\",\n\t\t                                               destinationUserJid.toString()});\n\t\tvar packet = Packet.packetInstance(packetElelement);\n\t\tfinal boolean block = messageDeliveryLogic.preProcessFilter(packet, session);\n\t\tassertTrue(\"BareJID: Available or Connected Resources > 8.5.2.1.1. Message: drop message\", block);\n\t}\n\n\t@Test\n\tpublic void testProcessingErrorMessageFullJidOnlineWithMatchingResource() throws Exception {\n\t\tvar destinationUserJid = BareJID.bareJIDInstance(\"user1\", domain);\n\t\tvar fullDestinationJid = JID.jidInstance(destinationUserJid, \"res1\");\n\t\tvar session = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID()), fullDestinationJid);\n\n\t\tvar packetElelement = new Element(\"message\", new String[]{\"type\", \"from\", \"to\"},\n\t\t                                  new String[]{\"error\", \"remote-user@test.com/res1\",\n\t\t                                               fullDestinationJid.toString()});\n\t\tvar packet = Packet.packetInstance(packetElelement);\n\t\tfinal boolean block = messageDeliveryLogic.preProcessFilter(packet, session);\n\t\tassertFalse(\"FullJID: 8.5.3.1.  Resource Matches. Message: deliver message\", block);\n\t}\n\n\t@Test\n\tpublic void testProcessingErrorMessageFullJidOnlineWithouthMatchingResource() throws Exception {\n\t\tvar destinationUserJid = BareJID.bareJIDInstance(\"user1\", domain);\n\t\tvar fullDestinationJidPacketTo = JID.jidInstance(destinationUserJid, \"res1\");\n\t\tvar fullDestinationJidSession = JID.jidInstance(destinationUserJid, \"res2\");\n\t\tvar session = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID()), fullDestinationJidSession);\n\n\t\tvar packetElelement = new Element(\"message\", new String[]{\"type\", \"from\", \"to\"},\n\t\t                                  new String[]{\"error\", \"remote-user@test.com/res1\",\n\t\t                                               fullDestinationJidPacketTo.toString()});\n\t\tvar packet = Packet.packetInstance(packetElelement);\n\t\tfinal boolean block = messageDeliveryLogic.preProcessFilter(packet, session);\n\t\tassertTrue(\"FullJID: No Resource Matches > 8.5.3.2.1. Message: drop message\", block);\n\t}\n\n\t@Test\n\tpublic void testProcessingErrorMessagePubSubPayload() throws Exception {\n\n\t\tvar destinationChannelJid = JID.jidInstance(\"channel\", \"mix.\" + domain, UUID.randomUUID().toString());\n\t\tvar session = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID()),\n\t\t                         JID.jidInstance(\"user1@example.com/res1\"));\n\n\t\tvar packetElelement = new Element(\"message\", new String[]{\"type\", \"from\", \"to\"},\n\t\t                                  new String[]{\"error\", \"remote-user@test.com/res1\",\n\t\t                                               destinationChannelJid.toString()});\n\t\tvar packet = Packet.packetInstance(packetElelement);\n\t\tboolean block = messageDeliveryLogic.preProcessFilter(packet, session);\n\t\tassertFalse(\"Message error addressed to PubSub/MIX should be correctly forwarded to the component\", block);\n\n\t\tvar destinationChannelFullJid = BareJID.bareJIDInstance(\"channel\", \"mix.\" + domain);\n\t\tpacketElelement = new Element(\"message\", new String[]{\"type\", \"from\", \"to\"},\n\t\t                              new String[]{\"error\", \"remote-user@test.com/res1\",\n\t\t                                           destinationChannelFullJid.toString()});\n\t\tpacket = Packet.packetInstance(packetElelement);\n\t\tblock = messageDeliveryLogic.preProcessFilter(packet, session);\n\t\tassertFalse(\"Message error addressed to PubSub/MIX should be correctly forwarded to the component\", block);\n\t}\n\n\t@Test\n\tpublic void testProcessingFailureErrorMissingToMissingFrom() throws Exception {\n\n\t\t//<failure xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" type=\"error\">\n\t\t//    <not-authorized xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\"/>\n\t\t//    <text xml:lang=\"en\">Password not verified</text>\n\t\t//    <error code=\"404\" type=\"wait\">\n\t\t//        <recipient-unavailable xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"/>\n\t\t//    </error>\n\t\t//</failure>\n\n\t\tvar destinationUserJid = BareJID.bareJIDInstance(\"user1\", domain);\n\t\tvar fullDestinationJid = JID.jidInstance(destinationUserJid, \"res1\");\n\t\tvar session = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID()), fullDestinationJid);\n\n\t\tvar packetElelement = new Element(\"failure\", new String[]{\"type\", \"xmlns\"},\n\t\t                                  new String[]{\"error\", \"urn:ietf:params:xml:ns:xmpp-sasl\"});\n\t\tpacketElelement.addChild(new Element(\"not-authorized\", new String[]{\" xmlns\"},\n\t\t                                     new String[]{\"urn:ietf:params:xml:ns:xmpp-sasl\"}));\n\t\tpacketElelement.addChild(new Element(\"text\", \"Password not verified\"));\n\t\tvar packet = Packet.packetInstance(packetElelement);\n\t\tfinal boolean block = messageDeliveryLogic.preProcessFilter(packet, session);\n\t\tassertFalse(\"Only Message packets should be processed\", block);\n\t}\n\n\t@Test\n\tpublic void testProcessingIqErrorMissingTo() throws Exception {\n\n\t\t//<iq xmlns=\"jabber:client\" from=\"user@sure.im/movimklqp20\" type=\"error\" id=\"id-1\"/>\n\n\t\tvar destinationUserJid = BareJID.bareJIDInstance(\"user1\", domain);\n\t\tvar fullDestinationJid = JID.jidInstance(destinationUserJid, \"res1\");\n\t\tvar session = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID()), fullDestinationJid);\n\n\t\tvar packetElelement = new Element(\"iq\", new String[]{\"type\", \"from\"},\n\t\t                                  new String[]{\"error\", \"remote-user@test.com/res1\"});\n\t\tvar packet = Packet.packetInstance(packetElelement);\n\t\tfinal boolean block = messageDeliveryLogic.preProcessFilter(packet, session);\n\t\tassertFalse(\"Only Message packets should be processed\", block);\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/MessageTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.lang.reflect.Field;\nimport java.util.*;\n\nimport static org.junit.Assert.*;\n\n/**\n * @author andrzej\n */\npublic class MessageTest\n\t\textends ProcessorTestCase {\n\n\tprivate MessageDeliveryLogic messageDeliveryLogic;\n\tprivate Message messageProcessor;\n\n\t@Override\n\tprotected void registerBeans(Kernel kernel) {\n\t\tsuper.registerBeans(kernel);\n\t\tkernel.registerBean(MessageDeliveryLogic.class).exec();\n\t\tkernel.registerBean(Message.class).setActive(true).exec();\n\t}\n\n\t@Before\n\t@Override\n\tpublic void setUp() throws Exception {\n\t\tsuper.setUp();\n\t\tmessageProcessor = getInstance(Message.class);\n\t\tmessageDeliveryLogic = getInstance(MessageDeliveryLogic.class);\n\t}\n\n\t@Test\n\tpublic void testSilentlyIgnoringMessages() throws Exception {\n\t\tBareJID userJid = BareJID.bareJIDInstance(\"user1@example.com\");\n\t\tJID res1 = JID.jidInstance(userJid, \"res1\");\n\n\t\t// testing default behaviour - error message\n\t\tElement packetEl = new Element(\"message\", new String[]{\"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t   new String[]{\"remote-user@test.com/res1\", res1.toString()});\n\t\tPacket packet = Packet.packetInstance(packetEl);\n\t\tQueue<Packet> results = new ArrayDeque<Packet>();\n\t\tmessageProcessor.process(packet, null, null, results, null);\n\t\tassertTrue(\"no error was generated\", !results.isEmpty());\n\t\tassertTrue(\"generated result is not an error\", results.poll().getType().equals(StanzaType.error));\n\n\t\t// testing silently ignoring error responses\n\t\tresults.clear();\n\t\tField f = messageDeliveryLogic.getClass().getDeclaredField(\"silentlyIgnoreError\");\n\t\tf.setAccessible(true);\n\t\tf.set(messageDeliveryLogic, true);\n\n\t\tmessageProcessor.process(packet, null, null, results, null);\n\t\tassertTrue(\"result was generated\", results.isEmpty());\n\t}\n\n\t@Test\n\tpublic void testResourceSelectionForMessageDeliveryMethods() throws Exception {\n\t\tBareJID userJid = BareJID.bareJIDInstance(\"user1@example.com\");\n\t\tJID res1 = JID.jidInstance(userJid, \"res1\");\n\t\tJID res2 = JID.jidInstance(userJid, \"res2\");\n\t\tXMPPResourceConnection session1 = getSession(res1, res1);\n\t\tXMPPResourceConnection session2 = getSession(res2, res2);\n\n\t\tassertEquals(Arrays.asList(session1, session2), session1.getActiveSessions());\n\t\tassertEquals(Collections.emptyList(), messageDeliveryLogic.getConnectionsForMessageDelivery(session1));\n\n\t\tassertFalse(\"found XMPPResourceConnection for delivery of message\",\n\t\t\t\t\tmessageDeliveryLogic.hasConnectionForMessageDelivery(session1));\n\t\tsession1.setPriority(1);\n\t\tassertTrue(\"found XMPPResourceConnection for delivery of message\",\n\t\t\t\t   messageDeliveryLogic.hasConnectionForMessageDelivery(session1));\n\n\t\tsession1.setPresence(new Element(\"presence\"));\n\t\tassertTrue(\"could not find XMPPResourceConnection for delivery of message\",\n\t\t\t\t   messageDeliveryLogic.hasConnectionForMessageDelivery(session1));\n\n\t\tassertEquals(Arrays.asList(session1), messageDeliveryLogic.getConnectionsForMessageDelivery(session2));\n\n\t\tsession2.setPresence(new Element(\"presence\"));\n\t\tassertTrue(\"could not find XMPPResourceConnection for delivery of message\",\n\t\t\t\t   messageDeliveryLogic.hasConnectionForMessageDelivery(session1));\n\t\tassertEquals(Arrays.asList(session1, session2), messageDeliveryLogic.getConnectionsForMessageDelivery(session2));\n\t}\n\n\t@Test\n\tpublic void testResourceSelectionForMessageDeliveryForBareJid() throws Exception {\n\t\tBareJID userJid = BareJID.bareJIDInstance(\"user1@example.com\");\n\t\tJID res1 = JID.jidInstance(userJid, \"res1\");\n\t\tJID res2 = JID.jidInstance(userJid, \"res2\");\n\t\tXMPPResourceConnection session1 = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()),\n\t\t\t\t\t\t\t\t\t\t\t\t\t res1);\n\t\tXMPPResourceConnection session2 = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()),\n\t\t\t\t\t\t\t\t\t\t\t\t\t res2);\n\n\t\tassertEquals(Arrays.asList(session1, session2), session1.getActiveSessions());\n\t\tassertEquals(Collections.emptyList(), messageDeliveryLogic.getConnectionsForMessageDelivery(session1));\n\n\t\tElement packetEl = new Element(\"message\", new String[]{\"type\", \"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t   new String[]{\"chat\", \"remote-user@test.com/res1\", userJid.toString()});\n\t\tPacket packet = Packet.packetInstance(packetEl);\n\t\tQueue<Packet> results = new ArrayDeque<Packet>();\n\t\tmessageProcessor.process(packet, session2, null, results, null);\n\t\tassertTrue(\"generated result even than no resource had nonnegative priority\", results.isEmpty());\n\n//\t\tsession1.setPriority(1);\n//\t\tresults = new ArrayDeque<Packet>();\n//\t\tmessageProcessor.process(packet, session2, null, results, null);\n//\t\tassertTrue(\"generated result even than no resource had nonnegative priority\", results.isEmpty());\n\n\t\tsession1.setPresence(new Element(\"presence\"));\n\t\tassertTrue(\"could not find XMPPResourceConnection for delivery of message\",\n\t\t\t\t   messageDeliveryLogic.hasConnectionForMessageDelivery(session1));\n\t\tresults = new ArrayDeque<Packet>();\n\t\tmessageProcessor.process(packet, session2, null, results, null);\n\t\tassertEquals(\"not generated result even than 1 resource had nonnegative priority\", 1, results.size());\n\t\tassertEquals(\"packet sent to wrong jids\", Arrays.asList(session1.getConnectionId()), collectPacketTo(results));\n\n\t\tsession2.setPresence(new Element(\"presence\"));\n\t\tassertTrue(\"could not find XMPPResourceConnection for delivery of message\",\n\t\t\t\t   messageDeliveryLogic.hasConnectionForMessageDelivery(session1));\n\t\tresults = new ArrayDeque<Packet>();\n\t\tmessageProcessor.process(packet, session1, null, results, null);\n\t\tassertEquals(\"not generated result even than 2 resource had nonnegative priority\", 2, results.size());\n\t\tassertEquals(\"packet sent to wrong jids\", Arrays.asList(session1.getConnectionId(), session2.getConnectionId()),\n\t\t\t\t\t collectPacketTo(results));\n\t}\n\n\t@Test\n\tpublic void testResourceSelectionForMessageDeliveryForFullJid() throws Exception {\n\t\tBareJID userJid = BareJID.bareJIDInstance(\"user1@example.com\");\n\t\tJID res1 = JID.jidInstance(userJid, \"res1\");\n\t\tJID res2 = JID.jidInstance(userJid, \"res2\");\n\t\tXMPPResourceConnection session1 = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()),\n\t\t\t\t\t\t\t\t\t\t\t\t\t res1);\n\t\tXMPPResourceConnection session2 = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()),\n\t\t\t\t\t\t\t\t\t\t\t\t\t res2);\n\n\t\tassertEquals(Arrays.asList(session1, session2), session1.getActiveSessions());\n\t\tassertEquals(Collections.emptyList(), messageDeliveryLogic.getConnectionsForMessageDelivery(session1));\n\n\t\tElement packetEl = new Element(\"message\", new String[]{\"type\", \"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t   new String[]{\"chat\", \"remote-user@test.com/res1\", res1.toString()});\n\t\tPacket packet = Packet.packetInstance(packetEl);\n\t\tQueue<Packet> results = new ArrayDeque<Packet>();\n\t\tmessageProcessor.process(packet, session2, null, results, null);\n\t\tassertEquals(\"not generated result even than no resource had nonnegative priority\", 1, results.size());\n\t\tassertEquals(\"packet sent to wrong jids\", Arrays.asList(session1.getConnectionId()), collectPacketTo(results));\n\n\t\tsession1.setPriority(1);\n\t\tresults = new ArrayDeque<Packet>();\n\t\tmessageProcessor.process(packet, session2, null, results, null);\n\t\tassertEquals(\"not generated result even than no resource had nonnegative priority\", 1, results.size());\n\t\tassertEquals(\"packet sent to wrong jids\", Arrays.asList(session1.getConnectionId()), collectPacketTo(results));\n\n\t\tsession1.setPresence(new Element(\"presence\"));\n\t\tassertTrue(\"could not find XMPPResourceConnection for delivery of message\",\n\t\t\t\t   messageDeliveryLogic.hasConnectionForMessageDelivery(session1));\n\t\tresults = new ArrayDeque<Packet>();\n\t\tmessageProcessor.process(packet, session2, null, results, null);\n\t\tassertEquals(\"not generated result even than 1 resource had nonnegative priority\", 1, results.size());\n\t\tassertEquals(\"packet sent to wrong jids\", Arrays.asList(session1.getConnectionId()), collectPacketTo(results));\n\n\t\tsession2.setPresence(new Element(\"presence\"));\n\t\tassertTrue(\"could not find XMPPResourceConnection for delivery of message\",\n\t\t\t\t   messageDeliveryLogic.hasConnectionForMessageDelivery(session1));\n\t\tresults = new ArrayDeque<Packet>();\n\t\tmessageProcessor.process(packet, session1, null, results, null);\n\t\tassertEquals(\"not generated result even than 2 resource had nonnegative priority\", 1, results.size());\n\t\tassertEquals(\"packet sent to wrong jids\", Arrays.asList(session1.getConnectionId()), collectPacketTo(results));\n\t}\n\n\tprotected List<JID> collectPacketTo(Queue<Packet> packets) {\n\t\tList<JID> result = new ArrayList<JID>();\n\t\tPacket p;\n\t\twhile ((p = packets.poll()) != null) {\n\t\t\tresult.add(p.getPacketTo());\n\t\t}\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/MobileV3Test.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManagerHandler;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.ArrayDeque;\nimport java.util.HashMap;\nimport java.util.Queue;\n\n/**\n * @author andrzej\n */\npublic class MobileV3Test\n\t\textends ProcessorTestCase {\n\n\tprivate SessionManagerHandler loginHandler;\n\tprivate MobileV3 mobileV3;\n\n\t@Before\n\t@Override\n\tpublic void setUp() throws Exception {\n\t\tmobileV3 = new MobileV3();\n\t\tmobileV3.init(new HashMap<String, Object>());\n\t\tsuper.setUp();\n\t}\n\n\t@After\n\t@Override\n\tpublic void tearDown() throws Exception {\n\t\tmobileV3 = null;\n\t\tsuper.tearDown();\n\t}\n\n\t@Test\n\tpublic void testRecipientDisabledFor2ResourcesMessage() throws TigaseStringprepException, NotAuthorizedException {\n\t\tString recipient = \"recipient-1@localhost\";\n\t\tJID recp1 = JID.jidInstanceNS(recipient + \"/res1\");\n\t\tJID recp2 = JID.jidInstanceNS(recipient + \"/res2\");\n\t\tJID connId1 = JID.jidInstanceNS(\"c2s@localhost/recipient1-res1\");\n\t\tJID connId2 = JID.jidInstanceNS(\"c2s@localhost/recipient1-res2\");\n\t\tXMPPResourceConnection session1 = getSession(connId1, recp1);\n\t\tgetSession(connId2, recp2);\n\t\tPacket p = Packet.packetInstance(\"message\", \"sender-1@localhost/res1\", recp1.toString(), StanzaType.chat);\n\t\tp.setPacketTo(connId1);\n\t\tArrayDeque<Packet> results = new ArrayDeque<Packet>();\n\t\tresults.offer(p);\n\t\tPacket[] expected = results.toArray(new Packet[0]);\n\t\tmobileV3.filter(p, session1, null, results);\n\t\tPacket[] processed = results.toArray(new Packet[0]);\n\t\tAssert.assertArrayEquals(expected, processed);\n\t}\n\n\t@Test\n\tpublic void testRecipientEnabledFor2ResourcesMessage() throws TigaseStringprepException, NotAuthorizedException {\n\t\tString recipient = \"recipient-1@localhost\";\n\t\tJID recp1 = JID.jidInstanceNS(recipient + \"/res1\");\n\t\tJID recp2 = JID.jidInstanceNS(recipient + \"/res2\");\n\t\tJID connId1 = JID.jidInstanceNS(\"c2s@localhost/recipient1-res1\");\n\t\tJID connId2 = JID.jidInstanceNS(\"c2s@localhost/recipient1-res2\");\n\t\tXMPPResourceConnection session1 = getSession(connId1, recp1);\n\t\tgetSession(connId2, recp2);\n\n\t\tenableMobileV3(session1, recp1);\n\n\t\tPacket p = Packet.packetInstance(\"message\", \"sender-1@localhost/res1\", recp1.toString(), StanzaType.chat);\n\t\tp.setPacketTo(connId1);\n\t\tArrayDeque<Packet> results = new ArrayDeque<Packet>();\n\t\tresults.offer(p);\n\t\tPacket[] expected = results.toArray(new Packet[0]);\n\t\tmobileV3.filter(p, session1, null, results);\n\t\tPacket[] processed = results.toArray(new Packet[0]);\n\t\tAssert.assertArrayEquals(expected, processed);\n\t}\n\n\t@Test\n\tpublic void testRecipientDisabledFor2ResourcesPresence() throws TigaseStringprepException, NotAuthorizedException {\n\t\tString recipient = \"recipient-1@localhost\";\n\t\tJID recp1 = JID.jidInstanceNS(recipient + \"/res1\");\n\t\tJID recp2 = JID.jidInstanceNS(recipient + \"/res2\");\n\t\tJID connId1 = JID.jidInstanceNS(\"c2s@localhost/recipient1-res1\");\n\t\tJID connId2 = JID.jidInstanceNS(\"c2s@localhost/recipient1-res2\");\n\t\tXMPPResourceConnection session1 = getSession(connId1, recp1);\n\t\tgetSession(connId2, recp2);\n\t\tPacket p = Packet.packetInstance(\"presence\", \"sender-1@localhost/res1\", recp1.toString(), StanzaType.chat);\n\t\tp.setPacketTo(connId1);\n\t\tArrayDeque<Packet> results = new ArrayDeque<Packet>();\n\t\tresults.offer(p);\n\t\tPacket[] expected = results.toArray(new Packet[0]);\n\t\tmobileV3.filter(p, session1, null, results);\n\t\tPacket[] processed = results.toArray(new Packet[0]);\n\t\tAssert.assertArrayEquals(expected, processed);\n\t}\n\n\t@Test\n\tpublic void testRecipientEnabledFor2ResourcesPresence() throws TigaseStringprepException, NotAuthorizedException {\n\t\tString recipient = \"recipient-1@localhost\";\n\t\tJID recp1 = JID.jidInstanceNS(recipient + \"/res1\");\n\t\tJID recp2 = JID.jidInstanceNS(recipient + \"/res2\");\n\t\tJID connId1 = JID.jidInstanceNS(\"c2s@localhost/recipient1-res1\");\n\t\tJID connId2 = JID.jidInstanceNS(\"c2s@localhost/recipient1-res2\");\n\t\tXMPPResourceConnection session1 = getSession(connId1, recp1);\n\t\tgetSession(connId2, recp2);\n\n\t\tenableMobileV3(session1, recp1);\n\n\t\tPacket presence = Packet.packetInstance(\"presence\", \"sender-1@localhost/res1\", recp1.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\tStanzaType.available);\n\t\tpresence.setPacketTo(connId1);\n\t\tArrayDeque<Packet> results = new ArrayDeque<Packet>();\n\t\tresults.offer(presence);\n\t\tPacket[] expected = new Packet[0];// results.toArray(new Packet[0]);\n\t\tmobileV3.filter(presence, session1, null, results);\n\t\tPacket[] processed = results.toArray(new Packet[0]);\n\t\tAssert.assertArrayEquals(expected, processed);\n\t}\n\n\t@Test\n\tpublic void testRecipientEnabledFor2Resources2Presences() throws TigaseStringprepException, NotAuthorizedException {\n\t\tString recipient = \"recipient-1@localhost\";\n\t\tJID recp1 = JID.jidInstanceNS(recipient + \"/res1\");\n\t\tJID recp2 = JID.jidInstanceNS(recipient + \"/res2\");\n\t\tJID connId1 = JID.jidInstanceNS(\"c2s@localhost/recipient1-res1\");\n\t\tJID connId2 = JID.jidInstanceNS(\"c2s@localhost/recipient1-res2\");\n\t\tXMPPResourceConnection session1 = getSession(connId1, recp1);\n\t\tgetSession(connId2, recp2);\n\n\t\tenableMobileV3(session1, recp1);\n\n\t\tPacket presence = Packet.packetInstance(\"presence\", \"sender-1@localhost/res1\", recp1.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\tStanzaType.available);\n\t\tpresence.setPacketTo(connId1);\n\t\tArrayDeque<Packet> results = new ArrayDeque<Packet>();\n\t\tresults.offer(presence);\n\t\tPacket[] expected = new Packet[0];// results.toArray(new Packet[0]);\n\t\tmobileV3.filter(presence, session1, null, results);\n\t\tPacket[] processed = results.toArray(new Packet[0]);\n\t\tAssert.assertArrayEquals(expected, processed);\n\n\t\tpresence = Packet.packetInstance(\"presence\", \"sender-1@localhost/res1\", recp1.toString(), StanzaType.available);\n\t\tpresence.setPacketTo(connId1);\n\t\tresults = new ArrayDeque<Packet>();\n\t\tresults.offer(presence);\n\t\texpected = new Packet[0];// results.toArray(new Packet[0]);\n\t\tmobileV3.filter(presence, session1, null, results);\n\t\tprocessed = results.toArray(new Packet[0]);\n\t\tAssert.assertArrayEquals(expected, processed);\n\n\t\tresults.clear();\n\t\tPacket p = Packet.packetInstance(\"message\", \"sender-1@localhost/res1\", recp1.toString(), StanzaType.chat);\n\t\tp.setPacketTo(connId1);\n\t\tresults.offer(p);\n\t\tPacket p1 = Packet.packetInstance(\"presence\", \"sender-1@localhost/res1\", recp1.toString(), StanzaType.error);\n\t\tp1.setPacketTo(connId1);\n\t\tresults.offer(p1);\n\t\texpected = new Packet[]{presence, p, p1};\n\t\tmobileV3.filter(p, session1, null, results);\n\t\tprocessed = results.toArray(new Packet[0]);\n\t\tAssert.assertArrayEquals(expected, processed);\n\t}\n\n\t@Test\n\tpublic void testRecipientEnabledFor2Resources() throws TigaseStringprepException, NotAuthorizedException {\n\t\tString recipient = \"recipient-1@localhost\";\n\t\tJID recp1 = JID.jidInstanceNS(recipient + \"/res1\");\n\t\tJID recp2 = JID.jidInstanceNS(recipient + \"/res2\");\n\t\tJID connId1 = JID.jidInstanceNS(\"c2s@localhost/recipient1-res1\");\n\t\tJID connId2 = JID.jidInstanceNS(\"c2s@localhost/recipient1-res2\");\n\t\tXMPPResourceConnection session1 = getSession(connId1, recp1);\n\t\tgetSession(connId2, recp2);\n\n\t\tenableMobileV3(session1, recp1);\n\n\t\tPacket presence = Packet.packetInstance(\"presence\", \"sender-1@localhost/res1\", recp1.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\tStanzaType.available);\n\t\tpresence.setPacketTo(connId1);\n\t\tArrayDeque<Packet> results = new ArrayDeque<Packet>();\n\t\tresults.offer(presence);\n\t\tPacket[] expected = new Packet[0];// results.toArray(new Packet[0]);\n\t\tmobileV3.filter(presence, session1, null, results);\n\t\tPacket[] processed = results.toArray(new Packet[0]);\n\t\tAssert.assertArrayEquals(expected, processed);\n\n\t\tresults.clear();\n\t\tPacket p = Packet.packetInstance(\"message\", \"sender-1@localhost/res1\", recp1.toString(), StanzaType.chat);\n\t\tp.setPacketTo(connId1);\n\t\tresults.offer(p);\n\t\tPacket p1 = Packet.packetInstance(\"presence\", \"sender-1@localhost/res1\", recp1.toString(), StanzaType.error);\n\t\tp1.setPacketTo(connId1);\n\t\tresults.offer(p1);\n\t\texpected = new Packet[]{presence, p, p1};\n\t\tmobileV3.filter(p, session1, null, results);\n\t\tprocessed = results.toArray(new Packet[0]);\n\t\tAssert.assertArrayEquals(expected, processed);\n\t}\n\n\t@Test\n\tpublic void testRecipientEnabledFor2ResourcesMixed() throws TigaseStringprepException, NotAuthorizedException {\n\t\tString recipient = \"recipient-1@localhost\";\n\t\tJID recp1 = JID.jidInstanceNS(recipient + \"/res1\");\n\t\tJID recp2 = JID.jidInstanceNS(recipient + \"/res2\");\n\t\tJID connId1 = JID.jidInstanceNS(\"c2s@localhost/recipient1-res1\");\n\t\tJID connId2 = JID.jidInstanceNS(\"c2s@localhost/recipient1-res2\");\n\t\tXMPPResourceConnection session1 = getSession(connId1, recp1);\n\t\tgetSession(connId2, recp2);\n\n\t\tenableMobileV3(session1, recp1);\n\n\t\tPacket presence = Packet.packetInstance(\"presence\", \"sender-1@localhost/res1\", recp1.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\tStanzaType.available);\n\t\tpresence.setPacketTo(connId1);\n\t\tArrayDeque<Packet> results = new ArrayDeque<Packet>();\n\t\tresults.offer(presence);\n\t\tPacket[] expected = new Packet[0];// results.toArray(new Packet[0]);\n\t\tmobileV3.filter(presence, session1, null, results);\n\t\tPacket[] processed = results.toArray(new Packet[0]);\n\t\tAssert.assertArrayEquals(expected, processed);\n\n\t\tresults.clear();\n\t\tPacket m1 = Packet.packetInstance(\"message\", recp2.toString(), recp1.toString(), StanzaType.chat);\n\t\tElement receivedEl = new Element(\"received\", new String[]{\"xmlns\"}, new String[]{\"urn:xmpp:carbons:2\"});\n\t\tElement forwardedEl = new Element(\"forwarded\", new String[]{\"xmlns\"}, new String[]{\"urn:xmpp:forward:0\"});\n\t\tforwardedEl.addChild(new Element(\"message\", new String[]{\"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t\t new String[]{recp2.toString(), \"sender-1@localhost/res1\"}));\n\t\treceivedEl.addChild(forwardedEl);\n\t\tm1.getElement().addChild(receivedEl);\n\t\tm1.setPacketTo(connId1);\n\t\tresults.offer(m1);\n\t\texpected = new Packet[0];\n\t\tmobileV3.filter(m1, session1, null, results);\n\t\tprocessed = results.toArray(new Packet[0]);\n\t\tAssert.assertArrayEquals(expected, processed);\n\n\t\tresults.clear();\n\t\tpresence = Packet.packetInstance(\"presence\", \"sender-1@localhost/res1\", recp1.toString(), StanzaType.available);\n\t\tpresence.setPacketTo(connId1);\n\t\tresults.offer(presence);\n\t\texpected = new Packet[0];// results.toArray(new Packet[0]);\n\t\tmobileV3.filter(presence, session1, null, results);\n\t\tprocessed = results.toArray(new Packet[0]);\n\t\tAssert.assertArrayEquals(expected, processed);\n\n\t\tresults.clear();\n\t\tPacket p = Packet.packetInstance(\"message\", \"sender-1@localhost/res1\", recp1.toString(), StanzaType.chat);\n\t\tp.setPacketTo(connId1);\n\t\tresults.offer(p);\n\t\tPacket p1 = Packet.packetInstance(\"presence\", \"sender-1@localhost/res1\", recp1.toString(), StanzaType.error);\n\t\tp1.setPacketTo(connId1);\n\t\tresults.offer(p1);\n\t\texpected = new Packet[]{presence, m1, p, p1};\n\t\tmobileV3.filter(p, session1, null, results);\n\t\tprocessed = results.toArray(new Packet[0]);\n\t\tAssert.assertArrayEquals(expected, processed);\n\t}\n\n\tprivate Queue<Packet> enableMobileV3(XMPPResourceConnection session, JID userJid) throws TigaseStringprepException {\n\t\tPacket p = Packet.packetInstance(\"iq\", userJid.toString(), userJid.toString(), StanzaType.set);\n\t\tp.getElement()\n\t\t\t\t.addChild(new Element(\"mobile\", new String[]{\"xmlns\", \"enable\"},\n\t\t\t\t\t\t\t\t\t  new String[]{\"http://tigase.org/protocol/mobile#v3\", \"true\"}));\n\t\tArrayDeque<Packet> results = new ArrayDeque<Packet>();\n\t\tmobileV3.process(p, session, null, results, null);\n\t\treturn results;\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/OfflineMessagesTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.db.DBInitException;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.UserNotFoundException;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Packet;\nimport tigase.xml.Element;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\n\nimport static org.junit.Assert.*;\n\n/**\n * @author andrzej\n */\npublic class OfflineMessagesTest\n\t\textends ProcessorTestCase {\n\n\tprivate MsgRepositoryIfcImpl msgRepo;\n\tprivate OfflineMessagesTestImpl offlineProcessor;\n\n\t@Before\n\t@Override\n\tpublic void setUp() throws Exception {\n\t\tmsgRepo = new MsgRepositoryIfcImpl();\n\t\tsuper.setUp();\n\t\tofflineProcessor = getInstance(OfflineMessagesTestImpl.class);\n\t\tofflineProcessor.msgRepo = msgRepo;\n\t\tofflineProcessor.init(new HashMap<String, Object>());\n\t}\n\n\t@After\n\t@Override\n\tpublic void tearDown() throws Exception {\n\t\tofflineProcessor = null;\n\t\tmsgRepo = null;\n\t\tsuper.tearDown();\n\t}\n\n\t@Test\n\tpublic void testStorageOfflineMessageForBareJid() throws Exception {\n\t\tBareJID userJid = BareJID.bareJIDInstance(\"user1@example.com\");\n\t\tJID res1 = JID.jidInstance(userJid, \"res1\");\n\t\tJID res2 = JID.jidInstance(userJid, \"res2\");\n\t\tXMPPResourceConnection session1 = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()),\n\t\t\t\t\t\t\t\t\t\t\t\t\t res1);\n\t\tXMPPResourceConnection session2 = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()),\n\t\t\t\t\t\t\t\t\t\t\t\t\t res2);\n\n\t\tassertEquals(Arrays.asList(session1, session2), session1.getActiveSessions());\n\n\t\tElement packetEl = new Element(\"message\", new String[]{\"type\", \"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t   new String[]{\"chat\", \"remote-user@test.com/res1\", userJid.toString()});\n\t\tpacketEl.addChild(new Element(\"body\", \"Test message\"));\n\t\tPacket packet = Packet.packetInstance(packetEl);\n\t\tQueue<Packet> results = new ArrayDeque<Packet>();\n\t\tofflineProcessor.postProcess(packet, session1, null, results, null);\n\t\tassertTrue(\"generated result even than no result should be generated\", results.isEmpty());\n\t\tassertTrue(\"no message stored, while it should be stored\", !msgRepo.getStored().isEmpty());\n\n\t\tmsgRepo.getStored().clear();\n\n\t\tsession1.setPriority(1);\n\t\tresults = new ArrayDeque<Packet>();\n\t\tofflineProcessor.postProcess(packet, session1, null, results, null);\n\t\tassertTrue(\"generated result even than no result should be generated\", results.isEmpty());\n\t\tassertTrue(\"message stored, while it should not be stored\", msgRepo.getStored().isEmpty());\n\n\t\tmsgRepo.getStored().clear();\n\n\t\tsession1.setPresence(new Element(\"presence\"));\n\t\tresults = new ArrayDeque<Packet>();\n\t\tofflineProcessor.postProcess(packet, session1, null, results, null);\n\t\tassertTrue(\"generated result even than no result should be generated\", results.isEmpty());\n\t\tassertTrue(\"message stored, while it should not be stored\", msgRepo.getStored().isEmpty());\n\n\t\tmsgRepo.getStored().clear();\n\t}\n\n\t@Test\n\tpublic void testStorageOfflineMessageForFullJid() throws Exception {\n\t\tBareJID userJid = BareJID.bareJIDInstance(\"user1@example.com\");\n\t\tJID res1 = JID.jidInstance(userJid, \"res1\");\n\t\tJID res2 = JID.jidInstance(userJid, \"res2\");\n\t\tXMPPResourceConnection session1 = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()),\n\t\t\t\t\t\t\t\t\t\t\t\t\t res1);\n\t\tXMPPResourceConnection session2 = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()),\n\t\t\t\t\t\t\t\t\t\t\t\t\t res2);\n\n\t\tassertEquals(Arrays.asList(session1, session2), session1.getActiveSessions());\n\n\t\tElement packetEl = new Element(\"message\", new String[]{\"type\", \"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t   new String[]{\"chat\", \"remote-user@test.com/res1\", res2.toString()});\n\t\tpacketEl.addChild(new Element(\"body\", \"Test message\"));\n\t\tPacket packet = Packet.packetInstance(packetEl);\n\t\tQueue<Packet> results = new ArrayDeque<Packet>();\n\t\tofflineProcessor.postProcess(packet, session1, null, results, null);\n\t\tassertTrue(\"generated result even than no result should be generated\", results.isEmpty());\n\t\tassertTrue(\"message stored, while it should not be stored\", msgRepo.getStored().isEmpty());\n\n\t\tmsgRepo.getStored().clear();\n\n\t\tsession1.setPriority(1);\n\t\tresults = new ArrayDeque<Packet>();\n\t\tofflineProcessor.postProcess(packet, session1, null, results, null);\n\t\tassertTrue(\"generated result even than no result should be generated\", results.isEmpty());\n\t\tassertTrue(\"message stored, while it should not be stored\", msgRepo.getStored().isEmpty());\n\n\t\tmsgRepo.getStored().clear();\n\n\t\tsession1.setPresence(new Element(\"presence\"));\n\t\tresults = new ArrayDeque<Packet>();\n\t\tofflineProcessor.postProcess(packet, session1, null, results, null);\n\t\tassertTrue(\"generated result even than no result should be generated\", results.isEmpty());\n\t\tassertTrue(\"message stored, while it should not be stored\", msgRepo.getStored().isEmpty());\n\n\t\tmsgRepo.getStored().clear();\n\t}\n\n\t@Test\n\tpublic void testRestorePacketForOffLineUser() throws Exception {\n\t\tBareJID userJid = BareJID.bareJIDInstance(\"user1@example.com\");\n\t\tJID res1 = JID.jidInstance(userJid, \"res1\");\n\t\tXMPPResourceConnection session1 = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()),\n\t\t\t\t\t\t\t\t\t\t\t\t\t res1);\n\n\t\tassertEquals(Arrays.asList(session1), session1.getActiveSessions());\n\n\t\tElement packetEl = new Element(\"iq\", new String[]{\"type\", \"from\", \"to\", \"time\"},\n\t\t\t\t\t\t\t\t\t   new String[]{\"set\", \"dip1@test.com/res1\", userJid.toString(), \"1440810935000\"});\n\t\tpacketEl.addChild(new Element(\"jingle\", new String[]{\"action\", \"sid\", \"offline\", \"xmlns\"},\n\t\t\t\t\t\t\t\t\t  new String[]{\"session-terminate\", UUID.randomUUID().toString(), \"true\",\n\t\t\t\t\t\t\t\t\t\t\t\t   \"urn:xmpp:jingle:1\"}));\n\t\tPacket packet = Packet.packetInstance(packetEl);\n\t\tmsgRepo.storeMessage(packet.getFrom(), packet.getTo(), null, packet.getElement(), null);\n\n\t\tpacketEl = new Element(\"iq\", new String[]{\"type\", \"from\", \"to\", \"time\"},\n\t\t\t\t\t\t\t   new String[]{\"set\", \"dip1@test.com/res1\", userJid.toString(), \"1440810972000\"});\n\t\tpacketEl.addChild(new Element(\"jingle\", new String[]{\"action\", \"sid\", \"offline\", \"xmlns\"},\n\t\t\t\t\t\t\t\t\t  new String[]{\"session-terminate\", UUID.randomUUID().toString(), \"true\",\n\t\t\t\t\t\t\t\t\t\t\t\t   \"urn:xmpp:jingle:1\"}));\n\t\tpacket = Packet.packetInstance(packetEl);\n\t\tmsgRepo.storeMessage(packet.getFrom(), packet.getTo(), null, packet.getElement(), null);\n\n\t\tassertTrue(\"no message stored, while it should be stored\", !msgRepo.getStored().isEmpty());\n\n\t\tQueue<Packet> restorePacketForOffLineUser = offlineProcessor.restorePacketForOffLineUser(session1, msgRepo);\n\n\t\tassertEquals(\"number of restored messages differ!\", restorePacketForOffLineUser.size(), 2);\n\n\t\tmsgRepo.getStored().clear();\n\t}\n\n\t@Test\n\tpublic void testLoadOfflineMessages() throws Exception {\n\t\tBareJID userJid = BareJID.bareJIDInstance(\"user1@example.com\");\n\t\tJID res1 = JID.jidInstance(userJid, \"res1\");\n\t\tJID res2 = JID.jidInstance(userJid, \"res2\");\n\t\tXMPPResourceConnection session1 = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()),\n\t\t\t\t\t\t\t\t\t\t\t\t\t res1);\n\t\tXMPPResourceConnection session2 = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()),\n\t\t\t\t\t\t\t\t\t\t\t\t\t res2);\n\n\t\tassertEquals(Arrays.asList(session1, session2), session1.getActiveSessions());\n\n\t\tElement presenceEl = new Element(\"presence\", new String[]{\"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t\t new String[]{res1.toString(), res2.toString()});\n\t\tPacket packet = Packet.packetInstance(presenceEl);\n\t\tassertFalse(offlineProcessor.loadOfflineMessages(packet, session1));\n\n\t\tpresenceEl = new Element(\"presence\", new String[]{\"from\"}, new String[]{res1.toString()});\n\t\tpacket = Packet.packetInstance(presenceEl);\n\t\tassertTrue(offlineProcessor.loadOfflineMessages(packet, session1));\n\t}\n\n\t@Test\n\tpublic void testIsAllowedForOfflineStorage() throws Exception {\n\t\tPacket packet = Packet.packetInstance(\n\t\t\t\tnew Element(\"message\", new Element[]{new Element(\"body\", \"Test message\")}, new String[]{\"from\", \"to\"},\n\t\t\t\t\t\t\tnew String[]{\"from@example.com/res1\", \"to@example.com/res2\"}));\n\t\tassertTrue(offlineProcessor.isAllowedForOfflineStorage(packet));\n\n\t\tpacket = Packet.packetInstance(new Element(\"message\", new Element[]{\n\t\t\t\tnew Element(\"storeMe1\", new String[]{\"xmlns\"}, new String[]{\"custom_xmlns\"})},\n\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{\"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{\"from@example.com/res1\", \"to@example.com/res2\"}));\n\n\t\tassertFalse(offlineProcessor.isAllowedForOfflineStorage(packet));\n\n\t\tofflineProcessor.setOfflineStorageMatchers(\n\t\t\t\tnew String[]{\"/message/storeMe1[custom_xmlns]\", \"/message/storeMe2\", \"-/message/noStore1\"});\n\n\t\tassertTrue(offlineProcessor.isAllowedForOfflineStorage(packet));\n\n\t\tpacket = Packet.packetInstance(new Element(\"message\", new Element[]{\n\t\t\t\tnew Element(\"storeMe2\", new String[]{\"xmlns\"}, new String[]{\"custom_xmlns\"})},\n\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{\"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{\"from@example.com/res1\", \"to@example.com/res2\"}));\n\n\t\tassertTrue(offlineProcessor.isAllowedForOfflineStorage(packet));\n\n\t\tpacket = Packet.packetInstance(new Element(\"message\", new Element[]{\n\t\t\t\tnew Element(\"storeMe3\", new String[]{\"xmlns\"}, new String[]{\"custom_xmlns\"})},\n\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{\"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{\"from@example.com/res1\", \"to@example.com/res2\"}));\n\n\t\tassertFalse(offlineProcessor.isAllowedForOfflineStorage(packet));\n\n\t\tpacket = Packet.packetInstance(new Element(\"message\", new Element[]{new Element(\"body\", \"Test message 123\"),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew Element(\"no-store\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnew String[]{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"urn:xmpp:hints\"})},\n\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{\"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{\"from@example.com/res1\", \"to@example.com/res2\"}));\n\n\t\tassertFalse(offlineProcessor.isAllowedForOfflineStorage(packet));\n\n\t\tpacket = Packet.packetInstance(\n\t\t\t\tnew Element(\"message\", new Element[]{new Element(\"noStore1\"), new Element(\"body\", \"body of message\")},\n\t\t\t\t\t\t\tnew String[]{\"from\", \"to\"}, new String[]{\"from@example.com/res1\", \"to@example.com/res2\"}));\n\t\tassertFalse(offlineProcessor.isAllowedForOfflineStorage(packet));\n\n\t\tpacket = Packet.packetInstance(new Element(\"message\", new Element[]{new Element(\"body\", \"body of message\")},\n\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{\"from\", \"to\"},\n\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{\"from@example.com/res1\", \"to@example.com/res2\"}));\n\t\tassertTrue(offlineProcessor.isAllowedForOfflineStorage(packet));\n\t}\n\n\t@Override\n\tprotected void registerBeans(Kernel kernel) {\n\t\tsuper.registerBeans(kernel);\n\t\tkernel.registerBean(tigase.xmpp.impl.MessageDeliveryLogic.class).setActive(true).exec();\n\t\tkernel.registerBean(OfflineMessagesTestImpl.class).setActive(true).exec();\n\t}\n\n\tprivate static class MsgRepositoryIfcImpl\n\t\t\timplements OfflineMessages.OfflineMsgRepositoryIfc {\n\n\t\tprivate final Queue<Packet> stored = new ArrayDeque();\n\n\t\tpublic MsgRepositoryIfcImpl() {\n\t\t}\n\n\t\t@Override\n\t\tpublic Element getMessageExpired(long time, boolean delete) {\n\t\t\tthrow new UnsupportedOperationException(\n\t\t\t\t\t\"Not supported yet.\"); //To change body of generated methods, choose Tools | Templates.\n\t\t}\n\n\t\t@Override\n\t\tpublic Queue<Element> loadMessagesToJID(XMPPResourceConnection session, boolean delete)\n\t\t\t\tthrows UserNotFoundException {\n\t\t\tQueue<Element> res = new LinkedList<Element>();\n\t\t\tfor (Packet pac : stored) {\n\t\t\t\tres.add(pac.getElement());\n\t\t\t}\n\t\t\treturn res;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean storeMessage(JID from, JID to, Date expired, Element msg, NonAuthUserRepository userRepo)\n\t\t\t\tthrows UserNotFoundException {\n\t\t\treturn stored.offer(Packet.packetInstance(msg, from, to));\n\t\t}\n\n\t\t@Override\n\t\tpublic void initRepository(String resource_uri, Map<String, String> params) throws DBInitException {\n\t\t}\n\n\t\tpublic Queue<Packet> getStored() {\n\t\t\treturn stored;\n\t\t}\n\n\t\t@Override\n\t\tpublic void init(NonAuthUserRepository repo, XMPPResourceConnection conn) {\n\n\t\t}\n\t}\n\n\tpublic static class OfflineMessagesTestImpl\n\t\t\textends OfflineMessages {\n\n\t\tprivate MsgRepositoryIfcImpl msgRepo;\n\n\t\t@Override\n\t\tprotected OfflineMessages.OfflineMsgRepositoryIfc getMsgRepoImpl(NonAuthUserRepository repo,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t XMPPResourceConnection conn) {\n\t\t\treturn msgRepo;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/OfflineMessages_StampComparatorTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.junit.Test;\nimport tigase.server.Packet;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\n\nimport static org.junit.Assert.assertTrue;\n\n/**\n * Created by andrzej on 12.01.2017.\n */\npublic class OfflineMessages_StampComparatorTest {\n\n\t@Test\n\tpublic void test_jabberXDelay() throws TigaseStringprepException {\n\t\tOfflineMessages.StampComparator comparator = new OfflineMessages.StampComparator();\n\n\t\tPacket p1 = createJabberXDelayPacket(\"20020910T23:08:25\");\n\t\tPacket p2 = createJabberXDelayPacket(\"20020910T22:08:25\");\n\t\tassertTrue(comparator.compare(p1, p2) > 0);\n\t\tassertTrue(comparator.compare(p2, p1) < 0);\n\n\t\tp1 = Packet.packetInstance(new Element(\"message\"));\n\t\tassertTrue(comparator.compare(p1, p2) < 0);\n\t\tassertTrue(comparator.compare(p2, p1) > 0);\n\t}\n\n\t@Test\n\tpublic void test_urnDelay() throws TigaseStringprepException {\n\t\tOfflineMessages.StampComparator comparator = new OfflineMessages.StampComparator();\n\n\t\tPacket p1 = createUrnXmppDelayPacket(\"2002-09-10T23:08:25Z\");\n\t\tPacket p2 = createUrnXmppDelayPacket(\"2002-09-10T22:08:25Z\");\n\t\tassertTrue(comparator.compare(p1, p2) > 0);\n\t\tassertTrue(comparator.compare(p2, p1) < 0);\n\n\t\tp1 = Packet.packetInstance(new Element(\"message\"));\n\t\tassertTrue(comparator.compare(p1, p2) < 0);\n\t\tassertTrue(comparator.compare(p2, p1) > 0);\n\t}\n\n\t@Test\n\tpublic void test_mixedDelay() throws TigaseStringprepException {\n\t\tOfflineMessages.StampComparator comparator = new OfflineMessages.StampComparator();\n\n\t\tPacket p1 = createJabberXDelayPacket(\"20020910T23:08:25\");\n\t\tPacket p2 = createUrnXmppDelayPacket(\"2002-09-10T22:08:25Z\");\n\t\tassertTrue(comparator.compare(p1, p2) > 0);\n\t\tassertTrue(comparator.compare(p2, p1) < 0);\n\n\t\tp1 = createUrnXmppDelayPacket(\"2002-09-10T23:08:25Z\");\n\t\tp2 = createJabberXDelayPacket(\"20020910T22:08:25\");\n\t\tassertTrue(comparator.compare(p1, p2) > 0);\n\t\tassertTrue(comparator.compare(p2, p1) < 0);\n\t}\n\n\tprivate Packet createJabberXDelayPacket(String timestamp) throws TigaseStringprepException {\n\t\tElement elem = new Element(\"message\", new Element[]{\n\t\t\t\tnew Element(\"x\", new String[]{\"xmlns\", \"stamp\"}, new String[]{\"jabber:x:delay\", timestamp})}, null,\n\t\t\t\t\t\t\t\t   null);\n\t\treturn Packet.packetInstance(elem);\n\t}\n\n\tprivate Packet createUrnXmppDelayPacket(String timestamp) throws TigaseStringprepException {\n\t\tElement elem = new Element(\"message\", new Element[]{\n\t\t\t\tnew Element(\"delay\", new String[]{\"xmlns\", \"stamp\"}, new String[]{\"urn:xmpp:delay\", timestamp})}, null,\n\t\t\t\t\t\t\t\t   null);\n\t\treturn Packet.packetInstance(elem);\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/PresenceCapabilitiesManagerTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.junit.Test;\nimport tigase.disco.ServiceIdentity;\nimport tigase.server.Command;\nimport tigase.server.DataForm;\nimport tigase.xml.Element;\n\nimport java.util.Arrays;\n\nimport static org.junit.Assert.assertEquals;\n\npublic class PresenceCapabilitiesManagerTest {\n\n\t@Test\n\tpublic void generateVerificationStringSimpleExample() {\n\t\tString[] features = new String[]{\"http://jabber.org/protocol/caps\", \"http://jabber.org/protocol/disco#info\",\n\t\t\t\t\t\t\t\t\t\t \"http://jabber.org/protocol/disco#items\", \"http://jabber.org/protocol/muc\"};\n\n\t\tfinal ServiceIdentity exodus = new ServiceIdentity(\"client\", \"pc\", \"Exodus 0.9.1\");\n\t\tString[] identities = new String[]{exodus.getAsCapsString()};\n\n\t\tfinal String s = PresenceCapabilitiesManager.generateVerificationString(identities, features, null);\n\t\tassertEquals(\"QgayPKawpkPSDYmwT/WM94uAlu0=\", s);\n\n\t}\n\n\t@Test\n\tpublic void generateVerificationStringComplexExample() {\n\t\tString[] features = new String[]{\"http://jabber.org/protocol/caps\", \"http://jabber.org/protocol/disco#info\",\n\t\t\t\t\t\t\t\t\t\t \"http://jabber.org/protocol/disco#items\", \"http://jabber.org/protocol/muc\"};\n\n\t\tfinal ServiceIdentity psi_en = new ServiceIdentity(\"client\", \"pc\", \"Psi 0.11\", \"en\");\n\t\tfinal ServiceIdentity psi_el = new ServiceIdentity(\"client\", \"pc\", \"Ψ 0.11\", \"el\");\n\t\tString[] identities = new String[]{psi_en.getAsCapsString(), psi_el.getAsCapsString()};\n\n\t\tfinal Element form = DataForm.createDataForm(Command.DataType.result);\n\t\tDataForm.addHiddenField(form, DataForm.FORM_TYPE, \"urn:xmpp:dataforms:softwareinfo\");\n\t\tDataForm.addFieldMultiValue(form, \"ip_version\", Arrays.asList(\"ipv4\", \"ipv6\"));\n\t\tDataForm.addFieldMultiValue(form, \"os\", Arrays.asList(\"Mac\"));\n\t\tDataForm.addFieldMultiValue(form, \"os_version\", Arrays.asList(\"10.5.1\"));\n\t\tDataForm.addFieldMultiValue(form, \"software\", Arrays.asList(\"Psi\"));\n\t\tDataForm.addFieldMultiValue(form, \"software_version\", Arrays.asList(\"0.11\"));\n\n//\t\tSystem.out.println(form.toStringPretty());\n\n\t\tfinal String s = PresenceCapabilitiesManager.generateVerificationString(identities, features, form);\n\t\tassertEquals(\"q07IKJEyjvHSyhy//CH0CxmKi8w=\", s);\n\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/PrivacyListTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.junit.Ignore;\nimport org.junit.Test;\nimport tigase.xml.Element;\n\nimport java.util.Collections;\n\npublic class PrivacyListTest {\n\n\t@Test\n\t@Ignore\n\tpublic void testToString() {\n\t\tElement list = new Element(\"list\");\n\t\tlist.setAttribute(\"name\", \"special\");\n\t\tlist.addChild(new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t\t  new String[]{\"jid\", \"juliet@example.com\", \"allow\", \"6\"}));\n\t\tlist.addChild(new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t\t  new String[]{\"group\", \"my_group\", \"allow\", \"7\"}));\n\t\tlist.addChild(new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t\t  new String[]{\"jid\", \"mercutio@example.org\", \"allow\", \"42\"}));\n\t\tlist.addChild(new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t\t  new String[]{\"subscription\", \"both\", \"allow\", \"42\"}));\n\t\tlist.addChild(new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t\t  new String[]{\"subscription\", \"both\", \"allow\", \"42\"}));\n\t\tlist.addChild(new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t\t  new String[]{\"subscription\", \"both\", \"allow\", \"42\"}));\n\t\tlist.addChild(new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t\t  new String[]{\"subscription\", \"both\", \"allow\", \"42\"}));\n\t\tlist.addChild(new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t\t  new String[]{\"subscription\", \"both\", \"allow\", \"42\"}));\n\t\tlist.addChild(new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t\t  new String[]{\"subscription\", \"both\", \"allow\", \"42\"}));\n\t\tlist.addChild(new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t\t  new String[]{\"subscription\", \"both\", \"allow\", \"42\"}));\n\t\tlist.addChild(new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t\t  new String[]{\"subscription\", \"both\", \"allow\", \"42\"}));\n\t\tlist.addChild(new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t\t  new String[]{\"subscription\", \"both\", \"allow\", \"42\"}));\n\t\tlist.addChild(new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t\t  new String[]{\"subscription\", \"both\", \"allow\", \"42\"}));\n\t\tlist.addChild(new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t\t  new String[]{\"subscription\", \"both\", \"allow\", \"42\"}));\n\t\tlist.addChild(new Element(\"item\", new String[]{\"type\", \"value\", \"action\", \"order\"},\n\t\t\t\t\t\t\t\t  new String[]{\"subscription\", \"both\", \"allow\", \"42\"}));\n\t\tfinal PrivacyList privacyList = PrivacyList.create(Collections.EMPTY_MAP, list);\n\n//\t\tSystem.out.println(privacyList.toString());\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/ProcessorTestCase.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport tigase.TestLogger;\nimport tigase.kernel.AbstractKernelWithUserRepositoryTestCase;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Packet;\nimport tigase.server.PacketWriterWithTimeout;\nimport tigase.server.xmppsession.SessionManagerHandler;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.vhosts.DummyVHostManager;\nimport tigase.vhosts.VHostItemImpl;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.XMPPSession;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.time.Duration;\nimport java.util.ArrayDeque;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\n/**\n * @author andrzej\n */\npublic abstract class ProcessorTestCase extends AbstractKernelWithUserRepositoryTestCase {\n\n\tprivate static final Logger log = TestLogger.getLogger(ProcessorTestCase.class);\n\tprivate SessionManagerHandler loginHandler;\n\n\tpublic SessionManagerHandler getSessionManagerHandler() {\n\t\treturn loginHandler;\n\t}\n\n\t@Before\n\tpublic void setSessionManager() throws Exception {\n\t\tloginHandler = new SessionManagerHandlerImpl();\n\t}\n\n\t@Override\n\tprotected void registerBeans(Kernel kernel) {\n\t\tsuper.registerBeans(kernel);\n\t\tkernel.registerBean(\"vHostManager\")\n\t\t\t\t.asClass(DummyVHostManager.class)\n\t\t\t\t.exportable()\n\t\t\t\t.setActive(true)\n\t\t\t\t.exec();\n\t}\n\n\t@After\n\tpublic void tearDownSessionManager() throws Exception {\n\t\tloginHandler = null;\n\t}\n\n\t@Deprecated\n\tpublic void setUp() throws Exception {\n\n\t}\n\n\t@Deprecated\n\tpublic void tearDown() throws Exception {\n\t\t\n\t}\n\n\tprotected XMPPResourceConnection getSession(JID connId, JID userJid)\n\t\t\tthrows NotAuthorizedException, TigaseStringprepException {\n\t\treturn getSession(connId, userJid, true);\n\t}\n\n\tprotected XMPPResourceConnection getSession(JID connId, JID userJid, boolean authorised)\n\t\t\tthrows NotAuthorizedException, TigaseStringprepException {\n\t\tXMPPResourceConnection conn = new XMPPResourceConnection(connId, getUserRepository(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t getAuthRepository(), loginHandler);\n\t\tVHostItemImpl vhost = new VHostItemImpl();\n\t\tvhost.setVHost(userJid.getDomain());\n\t\tconn.setDomain(vhost);\n\t\tif (authorised) {\n\t\t\tconn.authorizeJID(userJid.getBareJID(), false);\n\t\t\tconn.setResource(userJid.getResource());\n\t\t}\n\n\t\treturn conn;\n\t}\n\n\tpublic class SessionManagerHandlerImpl\n\t\t\timplements SessionManagerHandler, PacketWriterWithTimeout {\n\n\t\tMap<BareJID, XMPPSession> sessions = new HashMap<BareJID, XMPPSession>();\n\n\t\tpublic SessionManagerHandlerImpl() {\n\t\t}\n\n\t\t@Override\n\t\tpublic JID getComponentId() {\n\t\t\treturn JID.jidInstanceNS(\"sess-man@localhost\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleLogin(BareJID userId, XMPPResourceConnection conn) {\n\t\t\tXMPPSession session = sessions.get(userId);\n\t\t\tif (session == null) {\n\t\t\t\tsession = new XMPPSession(userId.getLocalpart());\n\t\t\t\tsessions.put(userId, session);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tsession.addResourceConnection(conn);\n\t\t\t} catch (TigaseStringprepException ex) {\n\t\t\t\tlog.log(Level.SEVERE, null, ex);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleLogout(BareJID userId, XMPPResourceConnection conn) {\n\t\t\tXMPPSession session = sessions.get(conn);\n\t\t\tif (session != null) {\n\t\t\t\tsession.removeResourceConnection(conn);\n\t\t\t\tif (session.getActiveResourcesSize() == 0) {\n\t\t\t\t\tsessions.remove(userId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void handlePresenceSet(XMPPResourceConnection conn) {\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleResourceBind(XMPPResourceConnection conn) {\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isLocalDomain(String domain, boolean includeComponents) {\n\t\t\treturn !domain.contains(\"-ext\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleDomainChange(String domain, XMPPResourceConnection conn) {\n\t\t}\n\n\t\tprivate Queue<Item> outQueue = new ArrayDeque<>();\n\n\t\tpublic Queue<Item> getOutQueue() {\n\t\t\treturn outQueue;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean addOutPacketWithTimeout(Packet packet, Duration timeout, Handler handler) {\n\t\t\treturn outQueue.offer(new Item(packet, handler));\n\t\t}\n\n\t\tpublic class Item {\n\n\t\t\tpublic final Packet packet;\n\t\t\tpublic final Handler handler;\n\n\t\t\tItem(Packet packet, Handler handler) {\n\t\t\t\tthis.packet = packet;\n\t\t\t\tthis.handler = handler;\n\t\t\t}\n\n\t\t\tpublic Packet getPacket() {\n\t\t\t\treturn packet;\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/SaslAuth2Test.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.xmpp.impl;\n\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.auth.BruteForceLockerBean;\nimport tigase.auth.TigaseSaslProvider;\nimport tigase.db.AuthRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.util.Base64;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.XMPPException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\n\nimport static tigase.xmpp.impl.SaslAuth.ALLOWED_SASL_MECHANISMS_KEY;\n\npublic class SaslAuth2Test\n\t\textends ProcessorTestCase {\n\n\tprivate MockSessionmanager mockSessionmanager;\n\tprivate SaslAuth2 saslAuth2;\n\n\tprivate XMPPResourceConnection authenticateSession(AuthRepository.AccountStatus accountStatus,\n\t                                                   Queue<Packet> results)\n\t\t\tthrows TigaseDBException, TigaseStringprepException, NotAuthorizedException {\n\t\tfinal BareJID user = BareJID.bareJIDInstanceNS(\"user@example.com\");\n\t\tgetUserRepository().addUser(user);\n\t\tgetAuthRepository().updateCredential(user, null, \"password\");\n\t\tgetAuthRepository().setAccountStatus(user, accountStatus);\n\t\tJID res = JID.jidInstance(user, \"res\");\n\t\tXMPPResourceConnection session = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID()),\n\t\t                                            res, false);\n\t\tsession.putSessionData(ALLOWED_SASL_MECHANISMS_KEY, Collections.singletonList(\"PLAIN\"));\n\n\t\tPacket packet = getPlainSaslPacket();\n\t\ttry {\n\t\t\tsaslAuth2.process(packet, session, null, results, null);\n\t\t} catch (XMPPException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t\treturn session;\n\t}\n\n\tprivate Packet getPlainSaslPacket() throws TigaseStringprepException {\n\t\tPacket packet;\n\t\tElement packetEl = new Element(\"authenticate\", new String[]{\"xmlns\", \"mechanism\"},\n\t\t                               new String[]{\"urn:xmpp:sasl:2\", \"PLAIN\"});\n\t\tfinal byte[] bytes = \"\\0user\\0password\".getBytes();\n\t\tpacketEl.addChild(new Element(\"initial-response\", Base64.encode(bytes)));\n\t\tfinal Element userAgent = new Element(\"user-agent\");\n\t\tuserAgent.setAttribute(\"id\", \"d4565fa7-4d72-4749-b3d3-740edbf87770\");\n\t\tuserAgent.addChild(new Element(\"software\", \"AwesomeXMPP\"));\n\t\tuserAgent.addChild(new Element(\"device\", \"Kiva's Phone\"));\n\t\tpacketEl.addChild(userAgent);\n\t\tpacket = Packet.packetInstance(packetEl);\n\t\treturn packet;\n\t}\n\n\t@Override\n\tprotected void registerBeans(Kernel kernel) {\n\t\tsuper.registerBeans(kernel);\n\t\tkernel.registerBean(\"eventbus\").asInstance(EventBusFactory.getInstance()).exportable().exec();\n\t\tkernel.registerBean(\"sess-man\").asInstance(new MockSessionmanager()).setActive(true).exportable().exec();\n\t\tkernel.registerBean(Bind2.class).setActive(true).exportable().exec();\n\t\tkernel.registerBean(StreamManagementInline.class).setActive(true).exportable().exec();\n\t\tkernel.registerBean(BruteForceLockerBean.class).setActive(true).exportable().exec();\n\t\tkernel.registerBean(TigaseSaslProvider.class).setActive(true).exportable().exec();\n\t\tkernel.registerBean(SaslAuth2.class).setActive(true).exportable().exec();\n\t}\n\n\t@Before\n\t@Override\n\tpublic void setUp() throws Exception {\n\t\tsaslAuth2 = getInstance(SaslAuth2.class);\n\t\tsaslAuth2.init(new HashMap<>());\n\t\tmockSessionmanager = getInstance(MockSessionmanager.class);\n\t\tmockSessionmanager.clearQueue();\n\t\tsuper.setUp();\n\t}\n\n\t@After\n\t@Override\n\tpublic void tearDown() throws Exception {\n\t\tsaslAuth2 = null;\n\t\tsuper.tearDown();\n\t}\n\n\t@Test\n\tpublic void testAuthenticationAccountStatusActive() throws Exception {\n\t\tfinal AuthRepository.AccountStatus accountStatus = AuthRepository.AccountStatus.active;\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tfinal XMPPResourceConnection session = authenticateSession(accountStatus, results);\n\t\tAssert.assertTrue(session.isAuthorized());\n\t\tPacket result = mockSessionmanager.outQueue.poll();\n\t\tAssert.assertNotNull(result);\n\t\tAssert.assertEquals(\"success\", result.getElemName());\n\t}\n\n\t@Test\n\tpublic void testAuthenticationAccountStatusDisabled() throws Exception {\n\t\tfinal AuthRepository.AccountStatus accountStatus = AuthRepository.AccountStatus.disabled;\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tfinal XMPPResourceConnection session = authenticateSession(accountStatus, results);\n\t\tAssert.assertFalse(session.isAuthorized());\n\t\tPacket result = results.poll();\n\t\tAssert.assertNotNull(result);\n\t\tAssert.assertEquals(\"failure\", result.getElemName());\n\t}\n\n\t@Test\n\tpublic void testAuthenticationAccountStatusPaid() throws Exception {\n\t\tfinal AuthRepository.AccountStatus accountStatus = AuthRepository.AccountStatus.paid;\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tfinal XMPPResourceConnection session = authenticateSession(accountStatus, results);\n\t\tAssert.assertTrue(session.isAuthorized());\n\t\tPacket result = mockSessionmanager.outQueue.poll();\n\t\tAssert.assertNotNull(result);\n\t\tAssert.assertEquals(\"success\", result.getElemName());\n\t}\n\n\t@Test\n\tpublic void testAuthenticationAccountStatusPending() throws Exception {\n\t\tfinal AuthRepository.AccountStatus accountStatus = AuthRepository.AccountStatus.pending;\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tfinal XMPPResourceConnection session = authenticateSession(accountStatus, results);\n\t\tAssert.assertFalse(session.isAuthorized());\n\t\tPacket result = results.poll();\n\t\tAssert.assertNotNull(result);\n\t\tAssert.assertEquals(\"failure\", result.getElemName());\n\t}\n\n\t@Test\n\tpublic void testAuthenticationAccountStatusSystem() throws Exception {\n\t\tfinal AuthRepository.AccountStatus accountStatus = AuthRepository.AccountStatus.system;\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tfinal XMPPResourceConnection session = authenticateSession(accountStatus, results);\n\t\tAssert.assertFalse(session.isAuthorized());\n\t\tPacket result = results.poll();\n\t\tAssert.assertNotNull(result);\n\t\tAssert.assertEquals(\"failure\", result.getElemName());\n\t}\n\n\t@Test\n\tpublic void testAuthenticationAccountStatusVip() throws Exception {\n\t\tfinal AuthRepository.AccountStatus accountStatus = AuthRepository.AccountStatus.vip;\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tfinal XMPPResourceConnection session = authenticateSession(accountStatus, results);\n\t\tAssert.assertTrue(session.isAuthorized());\n\t\tPacket result = mockSessionmanager.outQueue.poll();\n\t\tAssert.assertNotNull(result);\n\t\tAssert.assertEquals(\"success\", result.getElemName());\n\t}\n\n\tprivate static class MockSessionmanager\n\t\t\textends SessionManager {\n\n\t\tprivate final Queue<Packet> outQueue = new ArrayDeque<>();\n\n\t\t@Override\n\t\tpublic boolean addOutPacket(Packet packet) {\n\t\t\treturn outQueue.add(packet);\n\t\t}\n\n\t\tprivate void clearQueue() {\n\t\t\toutQueue.clear();\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/SaslAuthTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.xmpp.impl;\n\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.auth.BruteForceLockerBean;\nimport tigase.auth.TigaseSaslProvider;\nimport tigase.db.AuthRepository;\nimport tigase.db.TigaseDBException;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Packet;\nimport tigase.server.xmppsession.SessionManager;\nimport tigase.util.Base64;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\n\nimport static tigase.xmpp.impl.SaslAuth.ALLOWED_SASL_MECHANISMS_KEY;\n\npublic class SaslAuthTest\n\t\textends ProcessorTestCase {\n\n\tprivate SaslAuth saslAuth;\n\n\t@Before\n\t@Override\n\tpublic void setUp() throws Exception {\n\t\tsaslAuth = getInstance(SaslAuth.class);\n\t\tsaslAuth.init(new HashMap<String, Object>());\n\t\tsuper.setUp();\n\t}\n\n\t@After\n\t@Override\n\tpublic void tearDown() throws Exception {\n\t\tsaslAuth = null;\n\t\tsuper.tearDown();\n\t}\n\n\t@Test\n\tpublic void testAuthenticationAccountStatusPending() throws Exception {\n\t\tfinal AuthRepository.AccountStatus accountStatus = AuthRepository.AccountStatus.pending;\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tfinal XMPPResourceConnection session = authenticateSession(accountStatus, results);\n\t\tAssert.assertFalse(session.isAuthorized());\n\t\tPacket result = results.poll();\n\t\tAssert.assertNotNull(result);\n\t\tAssert.assertEquals(\"failure\", result.getElemName());\n\t}\n\n\t@Test\n\tpublic void testAuthenticationAccountStatusDisabled() throws Exception {\n\t\tfinal AuthRepository.AccountStatus accountStatus = AuthRepository.AccountStatus.disabled;\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tfinal XMPPResourceConnection session = authenticateSession(accountStatus, results);\n\t\tAssert.assertFalse(session.isAuthorized());\n\t\tPacket result = results.poll();\n\t\tAssert.assertNotNull(result);\n\t\tAssert.assertEquals(\"failure\", result.getElemName());\n\t}\n\n\t@Test\n\tpublic void testAuthenticationAccountStatusSystem() throws Exception {\n\t\tfinal AuthRepository.AccountStatus accountStatus = AuthRepository.AccountStatus.system;\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tfinal XMPPResourceConnection session = authenticateSession(accountStatus, results);\n\t\tAssert.assertFalse(session.isAuthorized());\n\t\tPacket result = results.poll();\n\t\tAssert.assertNotNull(result);\n\t\tAssert.assertEquals(\"failure\", result.getElemName());\n\t}\n\n\t@Test\n\tpublic void testAuthenticationAccountStatusActive() throws Exception {\n\t\tfinal AuthRepository.AccountStatus accountStatus = AuthRepository.AccountStatus.active;\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tfinal XMPPResourceConnection session = authenticateSession(accountStatus, results);\n\t\tAssert.assertTrue(session.isAuthorized());\n\t\tPacket result = results.poll();\n\t\tAssert.assertNotNull(result);\n\t\tAssert.assertEquals(\"success\", result.getElemName());\n\t}\n\n\t@Test\n\tpublic void testAuthenticationAccountStatusPaid() throws Exception {\n\t\tfinal AuthRepository.AccountStatus accountStatus = AuthRepository.AccountStatus.paid;\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tfinal XMPPResourceConnection session = authenticateSession(accountStatus, results);\n\t\tAssert.assertTrue(session.isAuthorized());\n\t\tPacket result = results.poll();\n\t\tAssert.assertNotNull(result);\n\t\tAssert.assertEquals(\"success\", result.getElemName());\n\t}\n\n\t@Test\n\tpublic void testAuthenticationAccountStatusVip() throws Exception {\n\t\tfinal AuthRepository.AccountStatus accountStatus = AuthRepository.AccountStatus.vip;\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tfinal XMPPResourceConnection session = authenticateSession(accountStatus, results);\n\t\tAssert.assertTrue(session.isAuthorized());\n\t\tPacket result = results.poll();\n\t\tAssert.assertNotNull(result);\n\t\tAssert.assertEquals(\"success\", result.getElemName());\n\t}\n\n\t@Override\n\tprotected void registerBeans(Kernel kernel) {\n\t\tsuper.registerBeans(kernel);\n\t\tkernel.registerBean(\"eventbus\").asInstance(EventBusFactory.getInstance()).exportable().exec();\n\t\tkernel.registerBean(\"sess-man\").asInstance(new SessionManager()).setActive(true).exportable().exec();\n\t\tkernel.registerBean(BruteForceLockerBean.class).setActive(true).exportable().exec();\n\t\tkernel.registerBean(TigaseSaslProvider.class).setActive(true).exportable().exec();\n\t\tkernel.registerBean(SaslAuth.class).setActive(true).exportable().exec();\n\t}\n\n\tprivate XMPPResourceConnection authenticateSession(AuthRepository.AccountStatus accountStatus,\n\t\t\t\t\t\t\t\t\t\t\t\t\t   Queue<Packet> results)\n\t\t\tthrows TigaseDBException, TigaseStringprepException, NotAuthorizedException {\n\t\tfinal BareJID user = BareJID.bareJIDInstanceNS(\"user@example.com\");\n\t\tgetUserRepository().addUser(user);\n\t\tgetAuthRepository().updateCredential(user, null, \"password\");\n\t\tgetAuthRepository().setAccountStatus(user, accountStatus);\n\t\tJID res = JID.jidInstance(user, \"res\");\n\t\tXMPPResourceConnection session = getSession(JID.jidInstance(\"c2s@example.com/\" + UUID.randomUUID().toString()),\n\t\t\t\t\t\t\t\t\t\t\t\t\tres, false);\n\t\tsession.putSessionData(ALLOWED_SASL_MECHANISMS_KEY, Collections.singletonList(\"PLAIN\"));\n\n\t\tPacket packet = getPlainSaslPacket();\n\t\tsaslAuth.process(packet, session, null, results, null);\n\t\treturn session;\n\t}\n\n\tprivate Packet getPlainSaslPacket() throws TigaseStringprepException {\n\t\tPacket packet;\n\t\tElement packetEl = new Element(\"auth\", new String[]{\"xmlns\", \"mechanism\"},\n\t\t\t\t\t\t\t\t\t   new String[]{\"urn:ietf:params:xml:ns:xmpp-sasl\", \"PLAIN\"});\n\t\tfinal byte[] bytes = \"\\0user\\0password\".getBytes();\n\t\tpacketEl.setCData(Base64.encode(bytes));\n\t\tpacket = Packet.packetInstance(packetEl);\n\t\treturn packet;\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/TokenBucketPoolTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport tigase.TestLogger;\n\nimport java.util.concurrent.TimeUnit;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\npublic class TokenBucketPoolTest {\n\n\tprivate static final Logger log = TestLogger.getLogger(TokenBucketPoolTest.class);\n\n\tprivate static final double makeTest(final TokenBucketPool t, final long testTime) throws InterruptedException {\n\t\tlong registrations = 0;\n\t\tfinal long startTime = System.currentTimeMillis();\n\t\tlong endTime;\n\t\twhile ((endTime = System.currentTimeMillis()) - startTime < testTime) {\n\t\t\tboolean b = t.consume(\"default\");\n\t\t\tif (b) {\n\t\t\t\t++registrations;\n\t\t\t}\n\t\t\tThread.sleep(3);\n\t\t}\n\n\t\tlog.log(Level.FINE,\n\t\t\t\t\"Received \" + registrations + \" events in \" + ((endTime - startTime) / 1000.0) + \" seconds (\" +\n\t\t\t\t\t\tregistrations / ((endTime - startTime) / 1000.0) + \" eps).\");\n\n\t\treturn registrations / ((endTime - startTime) / 1000.0);\n\t}\n\n\t@Test\n\tpublic void testItem02() {\n\t\tTokenBucketPool.TokenBucket it = new TokenBucketPool.TokenBucket(0, 10f, TimeUnit.MILLISECONDS.toNanos(1000));\n\t\tAssert.assertEquals(1, it.getAllowance(), 0);\n\n\t\tit.updateAllowance(TimeUnit.MILLISECONDS.toNanos(1000));\n\t\tAssert.assertEquals(10, it.getAllowance(), 0);\n\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertEquals(0, it.getAllowance(), 0);\n\n\t\tit.updateAllowance(TimeUnit.MILLISECONDS.toNanos(1001));\n\t\tAssert.assertEquals((float) 0.01, it.getAllowance(), 0.001);\n\n\t\tit.updateAllowance(TimeUnit.MILLISECONDS.toNanos(1002));\n\t\tAssert.assertEquals((float) 0.02, it.getAllowance(), 0.001);\n\n\t\tit.updateAllowance(TimeUnit.MILLISECONDS.toNanos(1500));\n\t\tAssert.assertEquals((float) 5, it.getAllowance(), 0.001);\n\n\t\tit.updateAllowance(TimeUnit.MILLISECONDS.toNanos(1501));\n\t\tAssert.assertEquals((float) 5.01, it.getAllowance(), 0.001);\n\n\t\tit.updateAllowance(TimeUnit.MILLISECONDS.toNanos(1503));\n\t\tAssert.assertEquals((float) 5.03, it.getAllowance(), 0.001);\n\n\t\tit.updateAllowance(TimeUnit.MILLISECONDS.toNanos(1601));\n\t\tAssert.assertEquals((float) 6.01, it.getAllowance(), 0.001);\n\n\t\tit.updateAllowance(TimeUnit.MILLISECONDS.toNanos(2000));\n\t\tAssert.assertEquals((float) 10.0, it.getAllowance(), 0.001);\n\n\t\tit.updateAllowance(TimeUnit.MILLISECONDS.toNanos(102000));\n\t\tAssert.assertEquals((float) 10.0, it.getAllowance(), 0.001);\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertEquals(9, it.getAllowance(), 0);\n\t}\n\n\t@Test\n\tpublic void testItem01() {\n\t\tTokenBucketPool.TokenBucket it = new TokenBucketPool.TokenBucket(0, 10f, 2f);\n\t\tAssert.assertEquals(1, it.getAllowance(), 0);\n\n\t\tit.updateAllowance(1);\n\t\tAssert.assertEquals(6, it.getAllowance(), 0);\n\t\tit.updateAllowance(1);\n\t\tAssert.assertEquals(6, it.getAllowance(), 0);\n\n\t\tit.updateAllowance(2);\n\t\tAssert.assertEquals(10, it.getAllowance(), 0);\n\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertEquals(9, it.getAllowance(), 0);\n\n\t\tit.updateAllowance(3);\n\t\tAssert.assertEquals(10, it.getAllowance(), 0);\n\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertEquals(1, it.getAllowance(), 0);\n\n\t\tit.updateAllowance(4);\n\n\t\tAssert.assertEquals(6, it.getAllowance(), 0);\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertTrue(it.consumeNoUpdate());\n\t\tAssert.assertFalse(it.consumeNoUpdate());\n\t\tAssert.assertEquals(0, it.getAllowance(), 0);\n\t}\n\n\t@Test\n\tpublic void testPurge() throws InterruptedException {\n\t\tTokenBucketPool t = new TokenBucketPool(1, 1, TimeUnit.NANOSECONDS);\n\t\tt.setAutoPurgeEnabled(false);\n\n\t\tt.consume(\"jeden\");\n\t\tAssert.assertEquals(1, t.size());\n\t\tt.consume(\"jeden\");\n\t\tAssert.assertEquals(1, t.size());\n\n\t\tt.consume(\"dwa\");\n\t\tAssert.assertEquals(2, t.size());\n\n\t\tt.consume(\"trzy\");\n\t\tAssert.assertEquals(3, t.size());\n\n\t\tThread.sleep(3);\n\n\t\tAssert.assertEquals(3, t.size());\n\t\tt.purge();\n\t\tAssert.assertEquals(0, t.size());\n\n\t}\n\n\tpublic void testConsume() throws Exception {\n\t\tassertInRange(2, 0.06, makeTest(new TokenBucketPool(2, 1, TimeUnit.SECONDS), TimeUnit.SECONDS.toMillis(60)));\n\t}\n\n\tprivate void assertInRange(double expectedValue, double devPercent, double value) {\n\t\tif (Math.abs(value - expectedValue) > (devPercent * expectedValue)) {\n\t\t\tAssert.fail(\"Value not in range: expected \" + expectedValue + \" +/- \" + (devPercent * expectedValue) +\n\t\t\t\t\t\t\t\t\", but received \" + value);\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/VCardHelperTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl;\n\nimport org.junit.Test;\nimport tigase.xml.DomBuilderHandler;\nimport tigase.xml.Element;\nimport tigase.xml.SimpleParser;\nimport tigase.xml.SingletonFactory;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotEquals;\n\n/*\n * To change this license header, choose License Headers in Project Properties.\n * To change this template file, choose Tools | Templates\n * and open the template in the editor.\n */\n\n/**\n * @author andrzej\n */\npublic class VCardHelperTest {\n\n\tprivate static String VCARD2_TEMP_DATA = (\"<vCard xmlns=\\\"vcard-temp\\\">\\n\" + \"  <FN>Peter Saint-Andre</FN>\\n\" +\n\t\t\t\"  <N>\\n\" + \"    <FAMILY>Saint-Andre</FAMILY>\\n\" + \"    <GIVEN>Peter</GIVEN>\\n\" + \"    <MIDDLE/>\\n\" +\n\t\t\t\"  </N>\\n\" + \"  <NICKNAME>stpeter</NICKNAME>\\n\" + \"  <NICKNAME>psa</NICKNAME>\\n\" +\n\t\t\t\"  <PHOTO><EXTVAL>http://stpeter.im/images/stpeter_oscon.jpg</EXTVAL></PHOTO>\\n\" +\n\t\t\t\"  <PHOTO><EXTVAL>http://stpeter.im/images/stpeter_hell.jpg</EXTVAL></PHOTO>\\n\" +\n\t\t\t\"  <BDAY>1966-08-06</BDAY>\\n\" + \"  <ADR>\\n\" + \"    <WORK/>\\n\" + \"    <PREF/>\\n\" +\n\t\t\t\"    <EXTADD>Suite 600</EXTADD>\\n\" + \"    <STREET>1899 Wynkoop Street</STREET>\\n\" +\n\t\t\t\"    <LOCALITY>Denver</LOCALITY>\\n\" + \"    <REGION>CO</REGION>\\n\" + \"    <PCODE>80202</PCODE>\\n\" +\n\t\t\t\"    <CTRY>USA</CTRY>\\n\" + \"  </ADR>\\n\" + \"  <ADR>\\n\" + \"    <HOME/>\\n\" + \"    <EXTADD/>\\n\" +\n\t\t\t\"    <STREET/>\\n\" + \"    <LOCALITY>Parker</LOCALITY>\\n\" + \"    <REGION>CO</REGION>\\n\" +\n\t\t\t\"    <PCODE>80138</PCODE>\\n\" + \"    <CTRY>USA</CTRY>\\n\" + \"  </ADR>\\n\" +\n\t\t\t\"  <TEL><WORK/><VOICE/><PREF/><NUMBER>+1-303-308-3282</NUMBER></TEL>\\n\" +\n\t\t\t\"  <TEL><WORK/><FAX/><NUMBER>+1-303-308-3219</NUMBER></TEL>\\n\" +\n\t\t\t\"  <TEL><CELL/><VOICE/><TEXT/><NUMBER>+1-720-256-6756</NUMBER></TEL>\\n\" +\n\t\t\t\"  <TEL><HOME/><VOICE/><NUMBER>+1-303-555-1212</NUMBER></TEL>\\n\" +\n\t\t\t\"  <EMAIL><USERID>stpeter@jabber.org</USERID></EMAIL>\\n\" +\n\t\t\t\"  <EMAIL><WORK/><USERID>psaintan@cisco.com</USERID></EMAIL>\\n\" +\n\t\t\t\"  <JABBERID>stpeter@jabber.org</JABBERID>\\n\" + \"  <TZ>America/Denver</TZ>\\n\" +\n\t\t\t\"  <GEO><LAT>39.59</LAT><LON>-105.01</LON></GEO>\\n\" + \"  <TITLE>Executive Director</TITLE>\\n\" +\n\t\t\t\"  <ROLE>Patron Saint</ROLE>\\n\" + \"  <LOGO>\\n\" + \"    <TYPE>image/jpeg</TYPE>\\n\" + \"    <BINVAL>\\n\" +\n\t\t\t\"/9j/4AAQSkZJRgABAQEASABIAAD//gAXQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q/9sAQwAIBgYHBgUI\\n\" +\n\t\t\t\"BwcHCQkICgwUDQwLCwwZEhMPFB0aHx4dGhwcICQuJyAiLCMcHCg3KSwwMTQ0NB8nOT04MjwuMzQy\\n\" +\n\t\t\t\"/9sAQwEJCQkMCwwYDQ0YMiEcITIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy\\n\" +\n\t\t\t\"MjIyMjIyMjIyMjIy/8AAEQgAgQB7AwEiAAIRAQMRAf/EABsAAAMAAwEBAAAAAAAAAAAAAAQFBgAD\\n\" +\n\t\t\t\"BwIB/8QAPRAAAgEDAwIDBQQJBAEFAAAAAQIDAAQRBRIhMUEGE1EiYXGBkRQyobEHFSMzNEJScsEk\\n\" +\n\t\t\t\"YtHw0jVDc8Lh/8QAGQEAAwEBAQAAAAAAAAAAAAAAAQIEAwAF/8QAJBEAAgICAgICAgMAAAAAAAAA\\n\" +\n\t\t\t\"AAECEQMhEjEEQSJREzJCYXH/2gAMAwEAAhEDEQA/AOf2c0KGSY2pDFiinYMsCMfhj8aya6gdvZj9\\n\" +\n\t\t\t\"ryg6A8eYuORxQk1rdzSHyZGQYO0GXAPv/MV6luDp8QgntkRyhAfHJ3Y5wOoxxXncbdiA9zp/6xQG\\n\" +\n\t\t\t\"KaGFIhK208MwX09amCOeMEdjT3VLi1EaeVGHEi4WRCQVwAD7PY5/CkQYbcfdPUVbjTS2FGAHGele\\n\" +\n\t\t\t\"1nmXgOw6/iMV4LnHup/4c8L3/iWfbboIoFPt3D/dH/kfcKd0kOotukL/ALWs8SJNEPZGA4/z2omD\\n\" +\n\t\t\t\"T7++dY7azuLlAOPs8Jfb9P8Amu1+Hv0Z+HtPjVp7YX846yXAyM+5elX1tDa2sKxwxRxoBgKigD6U\\n\" +\n\t\t\t\"nJGn4a7OB+G/0Y67rlxuuIRptojDdcXS5c/2R9SfoPfXY/DvgzQvCiLJZW/m3uPavbgb5Sf9vZPl\\n\" +\n\t\t\t\"9TVCBAw5RfiODQ93Fdi3b9XyxiXt5iE59wNPGSEnja6N5VyCzHYh5LOcUvuNc0y0JDTtO/8ATGP8\\n\" +\n\t\t\t\"1Hahc6g87xX0ku9TyhbH4dKEA6AZx1prJ2n7KW58W3L5W1iS3H9XVsfGlMl3NdNvmld8/wBRoMHH\\n\" +\n\t\t\t\"NbM5Hw5oAoIjALHPfA59O9KL218NS3byXtjE9w+GdivqBj8MUyk5ikAOPZ61rtreDUbaO5kiTeV2\\n\" +\n\t\t\t\"HJ7r7P8AiuGSOXDyzBE1zlInAKFR6k9a+XFtHt8qWczjGVUKSAvPHu+VLftckzPu+62U2E5256Y7\\n\" +\n\t\t\t\"1S+Fkj1DxPDaXEe5YYHldc9doGAfdnFQLE+VDdCc2ZXDaQDMTgFDEC6H175zgjimcv6PfFmvXMcy\\n\" +\n\t\t\t\"6SttFtB8+4dIEIIz0PPfsKuE1O7tEMVoy2sfPs28Sxdev3Rmh5Jp52LyzSSnqS7E/nVkIcezrFlj\\n\" +\n\t\t\t\"+ibSrC3aXWNbF9dAezaad93P+6Qjp8AKsNMt4LO1hgto0ihXhVj6CldrsSLoMt7qNhuiiDJyPSsc\\n\" +\n\t\t\t\"k23Rfgi4qyphkCqAMcUQsue9TsGoAkDP1osXOT1oWO0x2JOeua2rPiki3GO/Hxr0L5F/nFGwNB2q\\n\" +\n\t\t\t\"2UWq2+MAXCcxv/g/GopkaNijqVdWIYHqDVSt9uDYbt2pRrKrJJDdgcyrh/7h/wDlawlZLnh7QB96\\n\" +\n\t\t\t\"E46ivSnlDXyAAyFD/Nx9elZGQ0XHJVsH3VqSntk3W7oDgEYz6ULbWkkURUuxO9ySOMksTRY5iPx5\\n\" +\n\t\t\t\"o6KaMxKSygnkjFdQUzhMca3Mau7KXXII28EdvnVv4FtTLq97dOwLR2PlDHUEsD/ioO1uHCg9Og9B\\n\" +\n\t\t\t\"mrPwXrENomp3EynYApYoMkYGOfRfaHuqeKansdlHOMTOeOSelaxjml763ayalcBplW22K8bnoCRl\\n\" +\n\t\t\t\"gT3x2xW21v4bosoDIRgKHGGbPTjPpzWjkmhUrYbFNhgPSjLYPN7K45oGNRtDnGCCc+6grvxhDZsY\\n\" +\n\t\t\t\"rS3LBeN/9VSvbPTi+KK6GwGBucg0T5HljqSPhXMz4/uFmAa1brzk4qisfFS3UIcMR6g9qL12FS5d\\n\" +\n\t\t\t\"FasLOOMAV5ktMDIwamLjxVFagmWTC+g60HF+kOw85UzIfftoJphbrsqTKYwynI7VlzMJdP2fzKQw\\n\" +\n\t\t\t\"+VDJqFjqsPmW0w8wclfWtYV3kKLzlSaeLakZ5acT6jhZIz0Vhj4HtXqQFLuTjaXGTitJztZSuGjY\\n\" +\n\t\t\t\"EgjHuomQ7khlHpzVh5h8Gfs+e5oK6juvtDbCQuAQMj0FGkZgweo3D8DXiUxiTBYZAHYelKdRw85h\\n\" +\n\t\t\t\"uPImyG3glT0z61WadpVteeCdQu4DKk8ZKFVwd53DHPUDDduDioieSYsryZIJ4JGSapvB2pG21CWz\\n\" +\n\t\t\t\"iLSR3kZR0ZcYI6H48kUvH2zRjLTdOngh+23gSGOFdzPMvKgEg4Pr299btN8Qw3Es8kenwolxJgvs\\n\" +\n\t\t\t\"BaMKPZOex+HrQXirWJdQu10m0kLwwkCUr0kk/wDFe3zo/wAP6KqxyW8hO5wWIHfHXFZSjS0beNXO\\n\" +\n\t\t\t\"2Uk2bmyaKH75XaAPSpC48P3qysvnJEf5VPUiq/TzsYhSckkD4U4uNLtL61Czp7XZhwR8xWKu7LeK\\n\" +\n\t\t\t\"emckk0a8VZGmkBYfcG7qaqvB+izSyMJh7O3INOx4bsrUmTy2kI6F2JApxo0ZWfftCx4xxTSbloEI\\n\" +\n\t\t\t\"KPRz7xjp89veMsSkoOmKQR2N9bTKgtzJuI7HH1zXW9XsYrq4kWZCQ3Rh1FJk8LXayfsLobO3mJuo\\n\" +\n\t\t\t\"xlx1QJwUttk3pDzwX8REEkTA9G6H1Hvq41Gznv8ASpoLMSCeZQIihwwbIPB7Vvh0j7PEPtDeY3rj\\n\" +\n\t\t\t\"AHw9KUa3c3EVmY7aXy3LfeHXHGefnSOTsbhy+Jmm61JLdHS9e/02oqTHHdNgJKf6X9D6HpTpoJYk\\n\" +\n\t\t\t\"aF125JIB6qf+4qXk0/8AWFnHJMfM8yMNyevFEaVq11psiWWozNLZniK4ckmP0Uk9vf2quLdbPKmq\\n\" +\n\t\t\t\"k0ig3AxZyOWP1IoG5jZp2IYYwPyo+RCigEdwR7/nXxolJ+5TMCOMy6cjloldDclDtjZcBh2KnOAe\\n\" +\n\t\t\t\"vFePCysNZkQTpbSG3lAkk6Icfn2oW3t7iBnTIwcYB7ntjPz+lPI41jucxxtPPHCHaRkUjOBk+hx+\\n\" +\n\t\t\t\"NYObho1SbDNGh0+3id/MJkAOd45Pfj6U6s7t0j0yWYCMzxud3Ze+KWX7Lc2lr5YjMqIY9yLgMg4y\\n\" +\n\t\t\t\"Pfk0PqazRaXpiSlxuQuAw6ZAroNysWMqdlIbhYH3owK9VPrX1vEwgXJcYHqaTpMG0+DJziNRSe9A\\n\" +\n\t\t\t\"julMn7vbuHxrDjuj0Yz1ZTxa8sjm9v8AzvsiA+yvUntTSw8b6TtKx7gOwNRL3cU1qIzcqM44z0pQ\\n\" +\n\t\t\t\"+lTtlrWWJjnJAcA08YJPsDyy9I68fE2jX0ixy3Pkk9Gz3oLT/FC+e9vM6syMVV+mR2rnFnpNxHNG\\n\" +\n\t\t\t\"8rIWzk5YGnht0lkySEkHcd6E1T0Osl9o6C2qpcIVGDxSHxBMsPh+5lI/anEafE9/oKVafLLFNtkP\\n\" +\n\t\t\t\"TofWvPiu9zZw2QHL/tSfdyB+dGHykLmdQbQfeuNP0G2uIQS0fkjJ/m4yR+BpmY7a7tY5gQ8cyhlB\\n\" +\n\t\t\t\"HUHtSfxJIX0FdoCwxvGqD19k148H3u+Oe1lbLRANCPicED5mrKPLbKDTHNl/o7pZJbM8xsCd0R9P\\n\" +\n\t\t\t\"7fT3+6mhuIFJUTK2OMqQB+Nabc+Vew7sja+XI6+8VN+JpLS08R3sC20MgRgC2M87RkfI8fKhdHUR\\n\" +\n\t\t\t\"Eqs826JiFJ3GJicNj8q1K7QasY4lUJuwgDHA+Jr5qGoFZFEC7QVwccU80S7tbjUYIjGrsrsqtJgA\\n\" +\n\t\t\t\"nBOPxFScZcv6NZP6FzxvFAIyCu1SQAeSCe3zzTXWJpr2y0uNUy8MGXbcAOQMc/KtfiGAx6rHBCQx\\n\" +\n\t\t\t\"8sbQPUscCvfimHz9G8uEYFsqkheAdowf+flVeHC6bMWwG3nVQYDIrFeMq2QOeK3TbJgsUqBjnFLv\\n\" +\n\t\t\t\"Clmt1aaizrgewFYDocmi/NZHCsAJAcEGpsiuWi/G6jsMHh61juRcxoGV12tG3TPrTP8AVOnMuJNO\\n\" +\n\t\t\t\"lRiwO5FJzwO4rxA7m2U7chhjFDy6heWr/swwHcE8Ckv7N0os3X/h+0kikksoJo2z7O4lfzpRHpN5\\n\" +\n\t\t\t\"YzxedcGVmzuH9NUFpfvOp3qzMemelBajMzXMa59r1rm/SOcUj2zYdWX04+NKNbnNzePtIOxAg+PJ\\n\" +\n\t\t\t\"/wA0RdXQhAVTlvypNLMMMzHJ3At9a2wQp2SeRk1xRaeLNsWi2sCHOJUUn4IRU/o119j1uynJOBLh\\n\" +\n\t\t\t\"h6huP8088Uc6Qsz8Ga4Gz3AKT/kVIGTE8RHUOCPqKoSJDqqkhsknJIOflSZ7qzhkZL1Fe53EuxQ8\\n\" +\n\t\t\t\"55/Iinb/AL/AHG4/8U0g01Lm2ilJiJZFzkDPAxQCcCuxslL+YoYnbjOeMVQeFjeR7GjtBcYlLKCv\\n\" +\n\t\t\t\"IOBk5xxkVXw6L4fsmDRWMMsi8B5zvP0PFbri+mCCKJkRcYAXgYrePhv+TOc/oU6rpkz6p9ufbCuw\\n\" +\n\t\t\t\"KsZbLKff9aC3tgq+DgFT3B7UyklySrcE5z76AmAwW2nGOearhj4RpGVtnrTjFBD9liQIgXgD/vNK\\n\" +\n\t\t\t\"tbtnBM0XDDHzFbfPMUgdTlaMd0uYd4OfdXleRheOfJdHoY8inCn2A6TrChtrH7uMBj0p3JrFtMVQ\\n\" +\n\t\t\t\"RqSSecdTUff2qpMWT2eeooSKS4jcMrZI6ZrHgnsP5GtF7cXsKo7sAm3sOKQrc+bcTTM37GH2nk9M\\n\" +\n\t\t\t\"8AUHBHLecXEh2HqF71v19o7Lw+IIgFEsiqR7hz/ihGK5DSk3GwW7Dw3UyOcurHn8vwrVbWzXtzDB\\n\" +\n\t\t\t\"082RUJ9Mmmtvo11rumW1/aNGZBGI5InbaxK8ZHr2r5Z2k1rrVjbSxvE5uEB3jbgZ5q9xa/wgu2OP\\n\" +\n\t\t\t\"HE4k+xIh/ZoZAMd8bakl5ljb1I/MVTeNCpWw8v7oMqjj4c/WphWwV5xyDSJ6CdYdv9Vnk4c1SWlq\\n\" +\n\t\t\t\"4tYwsgC44GKmCf2z9gGz3qxsJozYQFm5KA/WlOOXyXmV3LbuBjqFOKG86VwSu3gdCc0El/Pavtcs\\n\" +\n\t\t\t\"wHGCc0aJBcJ5ls6h+pU165mCudxGXYtnOOgFCzXgSJgxCRgd+rGjmkimO2T9nIOMilV/aOmWOGjJ\\n\" +\n\t\t\t\"/wCmhK60cgAXOx/bJIY1vWeSBtybnU9QBS2aNIs7nJ9MCt1ndMDsyfQZPU9qmbT+Mh1raDpl+0Dc\\n\" +\n\t\t\t\"o+tBeQ6ScJkVkd5JNqYt7OMyqAd2Ty3rj4UwhxOqyRZKn6j3GoMmNReiqMuW2b7RHwCRilviaQyf\\n\" +\n\t\t\t\"Y4V5wxOPU04x5ERZuTj6UhvdUtoL3eEFxMowP6F9ce+kxQVhySqNIrtDjks9DSJWG3GWVutM7e4h\\n\" +\n\t\t\t\"ljXzHDqP5ZBnHw7ilFrL5yQOmfLZAw+fNN7e3jVi7DrXs0qoha2aNV0uDUokEMqxSIxIDHIbIHHu\\n\" +\n\t\t\t\"qQvrK5sJRHPEVcA4PZvgasb28gXIVFDD+YcZoIzG/ga2uYjLG3C46qT3rGeFVaOTKQXDNKwlOT0y\\n\" +\n\t\t\t\"F93f51To5ihiQZAWJB0/2ipRgWiaQnLsNicDjA6mqaa1DuG2/wAqjqewAqFpp0zRHKNR/eCtel/x\\n\" +\n\t\t\t\"ArKyvVZmEah+9Wvp/gz8Kyspl0cIZ/3bUvi6r/dWVlSZOx4maF/6wvz/ADNOtB/eXf8A8jVlZUcz\\n\" +\n\t\t\t\"aPYXq38EfnUbcfxXyFZWUMQch0LTP4SH+2nX/sn4VlZXqx/VEz7EF19/50bpf8SPgPzrKyuAO4P3\\n\" +\n\t\t\t\"0vx/+oqw7D4D8qysrzcv7jro/9k=\\n\" + \"    </BINVAL>\\n\" + \"  </LOGO>\\n\" + \"  <ORG>\\n\" +\n\t\t\t\"    <ORGNAME>XMPP Standards Foundation</ORGNAME>\\n\" + \"  </ORG>\\n\" + \"  <URL>https://stpeter.im/</URL>\\n\" +\n\t\t\t\"  <URL>http://www.saint-andre.com/</URL>\\n\" + \"  <KEY>\\n\" + \"    <CRED>\\n\" +\n\t\t\t\"-----BEGIN PGP PUBLIC KEY BLOCK-----\\n\" + \"Version: GnuPG/MacGPG2 v2.0.18 (Darwin)\\n\" + \"\\n\" +\n\t\t\t\"mQINBFETDzsBEAC0FOv1N3ZJzIIxN6cKD475KVS9CHDPeYpegcOIPnL5eY1DCHeh\\n\" +\n\t\t\t\"/IwS1S7RCePtmiybNoV9FsI4PKUknzXQxA6LVEdAR/LUlhgJKjq+gsgp8lqbEILh\\n\" +\n\t\t\t\"g13ecH66HwLS9rarbQkC47T7kL8miIPBFC6E3A4Lq1L+eueO6UcLhKgoYkMxOjdi\\n\" +\n\t\t\t\"WrMgKTnVpch5ydLkPm/z0Zo8zRgqlPuTLeCrXXZYnjHXLVFN2xy04UzOs7P5u5KV\\n\" +\n\t\t\t\"fx5Z7uQisr8pXtyLd6SpTZo6SHgKBv15uz0rqXhsJojiGtOXfWznAjaS5FUOORq9\\n\" +\n\t\t\t\"CklG5cMOUAT8TNftv0ktsxaWDL1ELDVQPy1m7mtzo+VREG+0xmU6AjMo/GHblW1U\\n\" +\n\t\t\t\"U7MI9yCiuMLsp/HLrFuiosqLVZ85wuLQ2junPe3tK8h15UcxIXAcpQ1VqIaDQFbe\\n\" +\n\t\t\t\"uLOXJTF8YHpHdpHYt/ZM1ll7ZBKGAo8yd7uF7wJ9D3gUazwdz9fFjWV7oIk7ATwO\\n\" +\n\t\t\t\"lFllzmWDn+M2ygbHOGUGMX5hSaa8eDSieiR2QoLdn27Fip7kMBTJ2+GISrfnJTN/\\n\" +\n\t\t\t\"OQvmj0DXXAdxHmu2C4QgmZbkge35n129yzXn9NcqzrGLroV62lL3LgX6cSbiH5i7\\n\" +\n\t\t\t\"GgWY6CAPb1pMogV0K475n9FvOSDRiG4QSO5yqKiA3OP5aKrIRp2TNAk4IwARAQAB\\n\" +\n\t\t\t\"tCZQZXRlciBTYWludC1BbmRyZSA8c3RwZXRlckBzdHBldGVyLmltPokCOQQTAQIA\\n\" +\n\t\t\t\"IwUCURMPOwIbAwcLCQgHAwIBBhUIAgkKCwQWAgMBAh4BAheAAAoJEOoGpJErxa2p\\n\" +\n\t\t\t\"6bgQAKpxu07cMDOLc4+EG8H19NWXIVVybOEvfGuHYZaLKkPrhrMZwJiOwBpyISNR\\n\" +\n\t\t\t\"t9qzX1eLCVaojaoEVX6kD8MGc5zKFfiJZy3j7lBWl+Ybr7FfXYy2BbAXKx49e1n6\\n\" +\n\t\t\t\"ci9LmBrmVfAEaxtDNPITZ9N9oUAb9vS0nrG036EwteEHAveQvlDjO7lhz6+Cv7lZ\\n\" +\n\t\t\t\"QgBj9rZ6khfcQ4S3nSCQaKLQ9Iav4fqxI7SfuPKnx6quHX3JNLGnVo3wl+j/foCK\\n\" +\n\t\t\t\"0iTrmtHxCI3kc/bx6g32pRjHEPX0ALMBhmzU2uca+TE0zCEC96mgYXAUCwdnCFWy\\n\" +\n\t\t\t\"beIEbt6pz65iML13kAVAq0H/GqncnMGN0MbOatnw1Tdz/vkLojIy7QbPcQ0plUFx\\n\" +\n\t\t\t\"v5491xPfIrHhOWdRXp6WUt88fcqhT6MHZpVRtusj2ornKVVn+Y0GLsMMCTcrXJRG\\n\" +\n\t\t\t\"7Ao1YV72t/pJpzfGWSaaxolxDIZ6B+76jrIhUhiWgo/4nf+DN6BIlCZQ6j6xxjjx\\n\" +\n\t\t\t\"462cu02kuhIILTk2pzaMOufTBWx0uJhZk/KP2Fay/41pX7pvVOwRC4uIlKsLnJKL\\n\" +\n\t\t\t\"PS7EDa4BUUxENfd/9LqOGwlII8BbSe98PLMI8sXkcigc3UXMVda9ll0YhQa+lbP1\\n\" +\n\t\t\t\"NaszmnBhwuiCsgnPGbImsJuRzgEEgckwP/dNeyr6MlFMyfaeuQINBFETDzsBEADB\\n\" +\n\t\t\t\"zOsEHpUmhkRUjH9Tek87dn5P/Yh/L/HptgCGk40TL/C+kYdkd3HyteMEf061PNms\\n\" +\n\t\t\t\"S/Rq8k37Fu3VODYb9SPYKxtgksKSYUtIkPKvao09K9QNWPqyWuNf0F+iAjVMUuda\\n\" +\n\t\t\t\"EVFJ7bHF310RDwLY5IvLeCXxtvG+Vv/i+g77d2WdPDp+zLJ8306C4yBKjSJV8xW0\\n\" +\n\t\t\t\"cn2fd7NviIEN6cNHTsZNDZVMlgYPrxnwSq8GTEPGC7HsLIwGcx3hIe9QjnPw9CpA\\n\" +\n\t\t\t\"mQENpDEyWcxgF5uwo2NJECoDswKz1Nb0gfawF3ZIbD+GcLujTu94iJuVg25jATWm\\n\" +\n\t\t\t\"9wTgcfZo4UPllRGXdIb8uWwUFQlLQgd4ROLZZtXNGmHIymJrV2crx53gxup+1j0X\\n\" +\n\t\t\t\"qhlzKg8xbImWhEfS9oHZkRK8VHgmWSIt7TNwNir6N5j3lqwWVBhnu6GzF01sKGNy\\n\" +\n\t\t\t\"SlqNRbd0fqhakCkK71b8ot8tYTcYG5Lg10z6HTbgQx2UwLthUjqbblDQ+GLmrOhi\\n\" +\n\t\t\t\"WklLXRsnlnPMwnEyFePAnsT5tasy2Cn9qjpttNDah7PB8iFUi9mtTF/XDVgpFaB5\\n\" +\n\t\t\t\"G3CDV7Q2NgbAI6g6QhLIAmXzSP635G83mda0TKXHQXHDyLJTTn+WVFU7t4m4uLt+\\n\" +\n\t\t\t\"0DsWU8jXHQWyUTNG9WPUrXhusDUAPHxFCQ/n/lQVBwARAQABiQIfBBgBAgAJBQJR\\n\" +\n\t\t\t\"Ew87AhsMAAoJEOoGpJErxa2pqfgP/ApN+TRu2bBIgaw1dr3AznSSha84DIpXUDh3\\n\" +\n\t\t\t\"udZvQrGbUtz8/mA+e3iZEN/cmmBw2LGlAuQoJNILTZQ318yTP+E5QU7fJH7FVsoh\\n\" +\n\t\t\t\"UyvrMfyt3IMA9jg0Z9MuloLezvIjjMfFeNa0ROgDb/ubOT7JQzi1kwN8Lu3lO80H\\n\" +\n\t\t\t\"wqBHXEeOLoislUSnZajRKvITbKWkZ6PHRjlMw1Wk4oIi6VLHgGgj79zzL3uhML26\\n\" +\n\t\t\t\"63m7imShvz1QcHTwvyR5i8cZbNOEkotZyERiA1p7YHuruS+QvTi3ZPoQbnMUB3a7\\n\" +\n\t\t\t\"py9d11bw1+w3LiAUGZE/z5hBWOFxYtw+w/U/Vx0BwJGYlwU3M2W20uEXe+qxz7wn\\n\" +\n\t\t\t\"akygKjmLiD2z4njfKjcNCiV3FmXrpmWgADln1c4jfxDh0NrndrsM8FPDf1TMPtOZ\\n\" +\n\t\t\t\"gFDkKripc9xkZ/25P6xn27oTOHWKcAC0QhxSH+HuVBBRk8AgF+zAbDZe4/L6+kan\\n\" +\n\t\t\t\"SrycIXW+wCzwBq61aWsz2QhhuKjozVkhk4dRG+CfjzAFjnyxwYERn3uXVKQAwTwc\\n\" +\n\t\t\t\"dNcTI9RV98IsNrw9Y4lJEAg6CjNPmiD5+EASycqaOuToRSGukr8sOQLWLPyTnez/\\n\" +\n\t\t\t\"aG8Xf7a+fntWzK2HuDYoSDhJJrylWw/lMklOBm4wtMeNA0zcQH6AQV/GzQVQkSGq\\n\" + \"rLuMVIV/\\n\" + \"=llGw\\n\" +\n\t\t\t\"-----END PGP PUBLIC KEY BLOCK-----\\n\" + \"    </CRED>\\n\" + \"  </KEY>\\n\" + \"  <NOTE>\\n\" +\n\t\t\t\"    More information about me is located on my \\n\" + \"    personal website: https://stpeter.im/\\n\" +\n\t\t\t\"  </NOTE>\\n\" + \"</vCard>\").replace(\"\\n\", \"\").replace(\"  \", \"\");\n\tprivate static String VCARD4_DATA = (\"<vcard xmlns=\\\"urn:ietf:params:xml:ns:vcard-4.0\\\">\\n\" + \"  <fn>\\n\" +\n\t\t\t\"    <text>Peter Saint-Andre</text>\\n\" + \"  </fn>\\n\" + \"  <n>\\n\" + \"    <surname>Saint-Andre</surname>\\n\" +\n\t\t\t\"    <given>Peter</given>\\n\" + \"    <additional></additional>\\n\" + \"  </n>\\n\" + \"  <nickname>\\n\" +\n\t\t\t\"    <text>stpeter</text>\\n\" + \"  </nickname>\\n\" + \"  <nickname>\\n\" + \"    <text>psa</text>\\n\" +\n\t\t\t\"  </nickname>\\n\" + \"  <photo>\\n\" + \"    <uri>http://stpeter.im/images/stpeter_oscon.jpg</uri>\\n\" +\n\t\t\t\"  </photo>\\n\" + \"  <photo>\\n\" + \"    <uri>http://stpeter.im/images/stpeter_hell.jpg</uri>\\n\" +\n\t\t\t\"  </photo>\\n\" + \"  <bday>\\n\" + \"    <date>1966-08-06</date>\\n\" + \"  </bday>\\n\" + \"  <adr>\\n\" +\n\t\t\t\"    <parameters>\\n\" + \"      <type><text>work</text></type>\\n\" +\n\t\t\t\"      <pref><integer>1</integer></pref>\\n\" + \"    </parameters>\\n\" + \"    <ext>Suite 600</ext>\\n\" +\n\t\t\t\"    <street>1899 Wynkoop Street</street>\\n\" + \"    <locality>Denver</locality>\\n\" +\n\t\t\t\"    <region>CO</region>\\n\" + \"    <code>80202</code>\\n\" + \"    <country>USA</country>\\n\" + \"  </adr>\\n\" +\n\t\t\t\"  <adr>\\n\" + \"    <parameters><type><text>home</text></type></parameters>\\n\" + \"    <ext></ext>\\n\" +\n\t\t\t\"    <street></street>\\n\" + \"    <locality>Parker</locality>\\n\" + \"    <region>CO</region>\\n\" +\n\t\t\t\"    <code>80138</code>\\n\" + \"    <country>USA</country>\\n\" + \"  </adr>\\n\" + \"  <tel>\\n\" +\n\t\t\t\"    <parameters>\\n\" + \"      <type><text>work</text><text>voice</text></type>\\n\" +\n\t\t\t\"      <pref><integer>1</integer></pref>\\n\" + \"    </parameters>\\n\" +\n\t\t\t\"    <uri>tel:+1-303-308-3282</uri>\\n\" + \"  </tel>\\n\" + \"  <tel>\\n\" + \"    <parameters>\\n\" +\n\t\t\t\"      <type><text>work</text><text>fax</text></type>\\n\" + \"    </parameters>\\n\" +\n\t\t\t\"    <uri>tel:+1-303-308-3219</uri>\\n\" + \"  </tel>\\n\" + \"  <tel>\\n\" + \"    <parameters>\\n\" +\n\t\t\t\"      <type><text>cell</text><text>voice</text><text>text</text></type>\\n\" + \"    </parameters>\\n\" +\n\t\t\t\"    <uri>tel:+1-720-256-6756</uri>\\n\" + \"  </tel>\\n\" + \"  <tel>\\n\" + \"    <parameters>\\n\" +\n\t\t\t\"      <type><text>home</text><text>voice</text></type>\\n\" + \"    </parameters>\\n\" +\n\t\t\t\"    <uri>tel:+1-303-555-1212</uri>\\n\" + \"  </tel>\\n\" + \"  <email>\\n\" +\n\t\t\t\"    <text>stpeter@jabber.org</text>\\n\" + \"  </email>\\n\" + \"  <email>\\n\" + \"    <parameters>\\n\" +\n\t\t\t\"      <type><text>work</text></type>\\n\" + \"    </parameters>\\n\" + \"    <text>psaintan@cisco.com</text>\\n\" +\n\t\t\t\"  </email>\\n\" + \"  <impp>\\n\" + \"    <uri>xmpp:stpeter@jabber.org</uri>\\n\" + \"  </impp>\\n\" + \"  <tz>\\n\" +\n\t\t\t\"    <text>America/Denver</text>\\n\" + \"  </tz>\\n\" + \"  <geo>\\n\" + \"    <uri>geo:39.59,-105.01</uri>\\n\" +\n\t\t\t\"  </geo>\\n\" + \"  <title>\\n\" + \"    <text>Executive Director</text>\\n\" + \"  </title>\\n\" + \"  <role>\\n\" +\n\t\t\t\"    <text>Patron Saint</text>\\n\" + \"  </role>\\n\" + \"  <logo>\\n\" + \"    <uri>data:image/jpeg;base64,\\n\" +\n\t\t\t\"/9j/4AAQSkZJRgABAQEASABIAAD//gAXQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q/9sAQwAIBgYHBgUI\\n\" +\n\t\t\t\"BwcHCQkICgwUDQwLCwwZEhMPFB0aHx4dGhwcICQuJyAiLCMcHCg3KSwwMTQ0NB8nOT04MjwuMzQy\\n\" +\n\t\t\t\"/9sAQwEJCQkMCwwYDQ0YMiEcITIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy\\n\" +\n\t\t\t\"MjIyMjIyMjIyMjIy/8AAEQgAgQB7AwEiAAIRAQMRAf/EABsAAAMAAwEBAAAAAAAAAAAAAAQFBgAD\\n\" +\n\t\t\t\"BwIB/8QAPRAAAgEDAwIDBQQJBAEFAAAAAQIDAAQRBRIhMUEGE1EiYXGBkRQyobEHFSMzNEJScsEk\\n\" +\n\t\t\t\"YtHw0jVDc8Lh/8QAGQEAAwEBAQAAAAAAAAAAAAAAAQIEAwAF/8QAJBEAAgICAgICAgMAAAAAAAAA\\n\" +\n\t\t\t\"AAECEQMhEjEEQSJREzJCYXH/2gAMAwEAAhEDEQA/AOf2c0KGSY2pDFiinYMsCMfhj8aya6gdvZj9\\n\" +\n\t\t\t\"ryg6A8eYuORxQk1rdzSHyZGQYO0GXAPv/MV6luDp8QgntkRyhAfHJ3Y5wOoxxXncbdiA9zp/6xQG\\n\" +\n\t\t\t\"KaGFIhK208MwX09amCOeMEdjT3VLi1EaeVGHEi4WRCQVwAD7PY5/CkQYbcfdPUVbjTS2FGAHGele\\n\" +\n\t\t\t\"1nmXgOw6/iMV4LnHup/4c8L3/iWfbboIoFPt3D/dH/kfcKd0kOotukL/ALWs8SJNEPZGA4/z2omD\\n\" +\n\t\t\t\"T7++dY7azuLlAOPs8Jfb9P8Amu1+Hv0Z+HtPjVp7YX846yXAyM+5elX1tDa2sKxwxRxoBgKigD6U\\n\" +\n\t\t\t\"nJGn4a7OB+G/0Y67rlxuuIRptojDdcXS5c/2R9SfoPfXY/DvgzQvCiLJZW/m3uPavbgb5Sf9vZPl\\n\" +\n\t\t\t\"9TVCBAw5RfiODQ93Fdi3b9XyxiXt5iE59wNPGSEnja6N5VyCzHYh5LOcUvuNc0y0JDTtO/8ATGP8\\n\" +\n\t\t\t\"1Hahc6g87xX0ku9TyhbH4dKEA6AZx1prJ2n7KW58W3L5W1iS3H9XVsfGlMl3NdNvmld8/wBRoMHH\\n\" +\n\t\t\t\"NbM5Hw5oAoIjALHPfA59O9KL218NS3byXtjE9w+GdivqBj8MUyk5ikAOPZ61rtreDUbaO5kiTeV2\\n\" +\n\t\t\t\"HJ7r7P8AiuGSOXDyzBE1zlInAKFR6k9a+XFtHt8qWczjGVUKSAvPHu+VLftckzPu+62U2E5256Y7\\n\" +\n\t\t\t\"1S+Fkj1DxPDaXEe5YYHldc9doGAfdnFQLE+VDdCc2ZXDaQDMTgFDEC6H175zgjimcv6PfFmvXMcy\\n\" +\n\t\t\t\"6SttFtB8+4dIEIIz0PPfsKuE1O7tEMVoy2sfPs28Sxdev3Rmh5Jp52LyzSSnqS7E/nVkIcezrFlj\\n\" +\n\t\t\t\"+ibSrC3aXWNbF9dAezaad93P+6Qjp8AKsNMt4LO1hgto0ihXhVj6CldrsSLoMt7qNhuiiDJyPSsc\\n\" +\n\t\t\t\"k23Rfgi4qyphkCqAMcUQsue9TsGoAkDP1osXOT1oWO0x2JOeua2rPiki3GO/Hxr0L5F/nFGwNB2q\\n\" +\n\t\t\t\"2UWq2+MAXCcxv/g/GopkaNijqVdWIYHqDVSt9uDYbt2pRrKrJJDdgcyrh/7h/wDlawlZLnh7QB96\\n\" +\n\t\t\t\"E46ivSnlDXyAAyFD/Nx9elZGQ0XHJVsH3VqSntk3W7oDgEYz6ULbWkkURUuxO9ySOMksTRY5iPx5\\n\" +\n\t\t\t\"o6KaMxKSygnkjFdQUzhMca3Mau7KXXII28EdvnVv4FtTLq97dOwLR2PlDHUEsD/ioO1uHCg9Og9B\\n\" +\n\t\t\t\"mrPwXrENomp3EynYApYoMkYGOfRfaHuqeKansdlHOMTOeOSelaxjml763ayalcBplW22K8bnoCRl\\n\" +\n\t\t\t\"gT3x2xW21v4bosoDIRgKHGGbPTjPpzWjkmhUrYbFNhgPSjLYPN7K45oGNRtDnGCCc+6grvxhDZsY\\n\" +\n\t\t\t\"rS3LBeN/9VSvbPTi+KK6GwGBucg0T5HljqSPhXMz4/uFmAa1brzk4qisfFS3UIcMR6g9qL12FS5d\\n\" +\n\t\t\t\"FasLOOMAV5ktMDIwamLjxVFagmWTC+g60HF+kOw85UzIfftoJphbrsqTKYwynI7VlzMJdP2fzKQw\\n\" +\n\t\t\t\"+VDJqFjqsPmW0w8wclfWtYV3kKLzlSaeLakZ5acT6jhZIz0Vhj4HtXqQFLuTjaXGTitJztZSuGjY\\n\" +\n\t\t\t\"EgjHuomQ7khlHpzVh5h8Gfs+e5oK6juvtDbCQuAQMj0FGkZgweo3D8DXiUxiTBYZAHYelKdRw85h\\n\" +\n\t\t\t\"uPImyG3glT0z61WadpVteeCdQu4DKk8ZKFVwd53DHPUDDduDioieSYsryZIJ4JGSapvB2pG21CWz\\n\" +\n\t\t\t\"iLSR3kZR0ZcYI6H48kUvH2zRjLTdOngh+23gSGOFdzPMvKgEg4Pr299btN8Qw3Es8kenwolxJgvs\\n\" +\n\t\t\t\"BaMKPZOex+HrQXirWJdQu10m0kLwwkCUr0kk/wDFe3zo/wAP6KqxyW8hO5wWIHfHXFZSjS0beNXO\\n\" +\n\t\t\t\"2Uk2bmyaKH75XaAPSpC48P3qysvnJEf5VPUiq/TzsYhSckkD4U4uNLtL61Czp7XZhwR8xWKu7LeK\\n\" +\n\t\t\t\"emckk0a8VZGmkBYfcG7qaqvB+izSyMJh7O3INOx4bsrUmTy2kI6F2JApxo0ZWfftCx4xxTSbloEI\\n\" +\n\t\t\t\"KPRz7xjp89veMsSkoOmKQR2N9bTKgtzJuI7HH1zXW9XsYrq4kWZCQ3Rh1FJk8LXayfsLobO3mJuo\\n\" +\n\t\t\t\"xlx1QJwUttk3pDzwX8REEkTA9G6H1Hvq41Gznv8ASpoLMSCeZQIihwwbIPB7Vvh0j7PEPtDeY3rj\\n\" +\n\t\t\t\"AHw9KUa3c3EVmY7aXy3LfeHXHGefnSOTsbhy+Jmm61JLdHS9e/02oqTHHdNgJKf6X9D6HpTpoJYk\\n\" +\n\t\t\t\"aF125JIB6qf+4qXk0/8AWFnHJMfM8yMNyevFEaVq11psiWWozNLZniK4ckmP0Uk9vf2quLdbPKmq\\n\" +\n\t\t\t\"k0ig3AxZyOWP1IoG5jZp2IYYwPyo+RCigEdwR7/nXxolJ+5TMCOMy6cjloldDclDtjZcBh2KnOAe\\n\" +\n\t\t\t\"vFePCysNZkQTpbSG3lAkk6Icfn2oW3t7iBnTIwcYB7ntjPz+lPI41jucxxtPPHCHaRkUjOBk+hx+\\n\" +\n\t\t\t\"NYObho1SbDNGh0+3id/MJkAOd45Pfj6U6s7t0j0yWYCMzxud3Ze+KWX7Lc2lr5YjMqIY9yLgMg4y\\n\" +\n\t\t\t\"Pfk0PqazRaXpiSlxuQuAw6ZAroNysWMqdlIbhYH3owK9VPrX1vEwgXJcYHqaTpMG0+DJziNRSe9A\\n\" +\n\t\t\t\"julMn7vbuHxrDjuj0Yz1ZTxa8sjm9v8AzvsiA+yvUntTSw8b6TtKx7gOwNRL3cU1qIzcqM44z0pQ\\n\" +\n\t\t\t\"+lTtlrWWJjnJAcA08YJPsDyy9I68fE2jX0ixy3Pkk9Gz3oLT/FC+e9vM6syMVV+mR2rnFnpNxHNG\\n\" +\n\t\t\t\"8rIWzk5YGnht0lkySEkHcd6E1T0Osl9o6C2qpcIVGDxSHxBMsPh+5lI/anEafE9/oKVafLLFNtkP\\n\" +\n\t\t\t\"TofWvPiu9zZw2QHL/tSfdyB+dGHykLmdQbQfeuNP0G2uIQS0fkjJ/m4yR+BpmY7a7tY5gQ8cyhlB\\n\" +\n\t\t\t\"HUHtSfxJIX0FdoCwxvGqD19k148H3u+Oe1lbLRANCPicED5mrKPLbKDTHNl/o7pZJbM8xsCd0R9P\\n\" +\n\t\t\t\"7fT3+6mhuIFJUTK2OMqQB+Nabc+Vew7sja+XI6+8VN+JpLS08R3sC20MgRgC2M87RkfI8fKhdHUR\\n\" +\n\t\t\t\"Eqs826JiFJ3GJicNj8q1K7QasY4lUJuwgDHA+Jr5qGoFZFEC7QVwccU80S7tbjUYIjGrsrsqtJgA\\n\" +\n\t\t\t\"nBOPxFScZcv6NZP6FzxvFAIyCu1SQAeSCe3zzTXWJpr2y0uNUy8MGXbcAOQMc/KtfiGAx6rHBCQx\\n\" +\n\t\t\t\"8sbQPUscCvfimHz9G8uEYFsqkheAdowf+flVeHC6bMWwG3nVQYDIrFeMq2QOeK3TbJgsUqBjnFLv\\n\" +\n\t\t\t\"Clmt1aaizrgewFYDocmi/NZHCsAJAcEGpsiuWi/G6jsMHh61juRcxoGV12tG3TPrTP8AVOnMuJNO\\n\" +\n\t\t\t\"lRiwO5FJzwO4rxA7m2U7chhjFDy6heWr/swwHcE8Ckv7N0os3X/h+0kikksoJo2z7O4lfzpRHpN5\\n\" +\n\t\t\t\"YzxedcGVmzuH9NUFpfvOp3qzMemelBajMzXMa59r1rm/SOcUj2zYdWX04+NKNbnNzePtIOxAg+PJ\\n\" +\n\t\t\t\"/wA0RdXQhAVTlvypNLMMMzHJ3At9a2wQp2SeRk1xRaeLNsWi2sCHOJUUn4IRU/o119j1uynJOBLh\\n\" +\n\t\t\t\"h6huP8088Uc6Qsz8Ga4Gz3AKT/kVIGTE8RHUOCPqKoSJDqqkhsknJIOflSZ7qzhkZL1Fe53EuxQ8\\n\" +\n\t\t\t\"55/Iinb/AL/AHG4/8U0g01Lm2ilJiJZFzkDPAxQCcCuxslL+YoYnbjOeMVQeFjeR7GjtBcYlLKCv\\n\" +\n\t\t\t\"IOBk5xxkVXw6L4fsmDRWMMsi8B5zvP0PFbri+mCCKJkRcYAXgYrePhv+TOc/oU6rpkz6p9ufbCuw\\n\" +\n\t\t\t\"KsZbLKff9aC3tgq+DgFT3B7UyklySrcE5z76AmAwW2nGOearhj4RpGVtnrTjFBD9liQIgXgD/vNK\\n\" +\n\t\t\t\"tbtnBM0XDDHzFbfPMUgdTlaMd0uYd4OfdXleRheOfJdHoY8inCn2A6TrChtrH7uMBj0p3JrFtMVQ\\n\" +\n\t\t\t\"RqSSecdTUff2qpMWT2eeooSKS4jcMrZI6ZrHgnsP5GtF7cXsKo7sAm3sOKQrc+bcTTM37GH2nk9M\\n\" +\n\t\t\t\"8AUHBHLecXEh2HqF71v19o7Lw+IIgFEsiqR7hz/ihGK5DSk3GwW7Dw3UyOcurHn8vwrVbWzXtzDB\\n\" +\n\t\t\t\"082RUJ9Mmmtvo11rumW1/aNGZBGI5InbaxK8ZHr2r5Z2k1rrVjbSxvE5uEB3jbgZ5q9xa/wgu2OP\\n\" +\n\t\t\t\"HE4k+xIh/ZoZAMd8bakl5ljb1I/MVTeNCpWw8v7oMqjj4c/WphWwV5xyDSJ6CdYdv9Vnk4c1SWlq\\n\" +\n\t\t\t\"4tYwsgC44GKmCf2z9gGz3qxsJozYQFm5KA/WlOOXyXmV3LbuBjqFOKG86VwSu3gdCc0El/Pavtcs\\n\" +\n\t\t\t\"wHGCc0aJBcJ5ls6h+pU165mCudxGXYtnOOgFCzXgSJgxCRgd+rGjmkimO2T9nIOMilV/aOmWOGjJ\\n\" +\n\t\t\t\"/wCmhK60cgAXOx/bJIY1vWeSBtybnU9QBS2aNIs7nJ9MCt1ndMDsyfQZPU9qmbT+Mh1raDpl+0Dc\\n\" +\n\t\t\t\"o+tBeQ6ScJkVkd5JNqYt7OMyqAd2Ty3rj4UwhxOqyRZKn6j3GoMmNReiqMuW2b7RHwCRilviaQyf\\n\" +\n\t\t\t\"Y4V5wxOPU04x5ERZuTj6UhvdUtoL3eEFxMowP6F9ce+kxQVhySqNIrtDjks9DSJWG3GWVutM7e4h\\n\" +\n\t\t\t\"ljXzHDqP5ZBnHw7ilFrL5yQOmfLZAw+fNN7e3jVi7DrXs0qoha2aNV0uDUokEMqxSIxIDHIbIHHu\\n\" +\n\t\t\t\"qQvrK5sJRHPEVcA4PZvgasb28gXIVFDD+YcZoIzG/ga2uYjLG3C46qT3rGeFVaOTKQXDNKwlOT0y\\n\" +\n\t\t\t\"F93f51To5ihiQZAWJB0/2ipRgWiaQnLsNicDjA6mqaa1DuG2/wAqjqewAqFpp0zRHKNR/eCtel/x\\n\" +\n\t\t\t\"ArKyvVZmEah+9Wvp/gz8Kyspl0cIZ/3bUvi6r/dWVlSZOx4maF/6wvz/ADNOtB/eXf8A8jVlZUcz\\n\" +\n\t\t\t\"aPYXq38EfnUbcfxXyFZWUMQch0LTP4SH+2nX/sn4VlZXqx/VEz7EF19/50bpf8SPgPzrKyuAO4P3\\n\" +\n\t\t\t\"0vx/+oqw7D4D8qysrzcv7jro/9k=\\n\" + \"      </uri>\\n\" + \"    </logo>\\n\" + \"    <org>\\n\" +\n\t\t\t\"      <text>XMPP Standards Foundation</text>\\n\" + \"    </org>\\n\" + \"    <url>\\n\" +\n\t\t\t\"      <uri>https://stpeter.im/</uri>\\n\" + \"    </url>\\n\" + \"    <url>\\n\" +\n\t\t\t\"      <uri>http://www.saint-andre.com/</uri>\\n\" + \"    </url>\\n\" + \"    <key>\\n\" + \"      <text>\\n\" +\n\t\t\t\"-----BEGIN PGP PUBLIC KEY BLOCK-----\\n\" + \"Version: GnuPG/MacGPG2 v2.0.18 (Darwin)\\n\" + \"\\n\" +\n\t\t\t\"mQINBFETDzsBEAC0FOv1N3ZJzIIxN6cKD475KVS9CHDPeYpegcOIPnL5eY1DCHeh\\n\" +\n\t\t\t\"/IwS1S7RCePtmiybNoV9FsI4PKUknzXQxA6LVEdAR/LUlhgJKjq+gsgp8lqbEILh\\n\" +\n\t\t\t\"g13ecH66HwLS9rarbQkC47T7kL8miIPBFC6E3A4Lq1L+eueO6UcLhKgoYkMxOjdi\\n\" +\n\t\t\t\"WrMgKTnVpch5ydLkPm/z0Zo8zRgqlPuTLeCrXXZYnjHXLVFN2xy04UzOs7P5u5KV\\n\" +\n\t\t\t\"fx5Z7uQisr8pXtyLd6SpTZo6SHgKBv15uz0rqXhsJojiGtOXfWznAjaS5FUOORq9\\n\" +\n\t\t\t\"CklG5cMOUAT8TNftv0ktsxaWDL1ELDVQPy1m7mtzo+VREG+0xmU6AjMo/GHblW1U\\n\" +\n\t\t\t\"U7MI9yCiuMLsp/HLrFuiosqLVZ85wuLQ2junPe3tK8h15UcxIXAcpQ1VqIaDQFbe\\n\" +\n\t\t\t\"uLOXJTF8YHpHdpHYt/ZM1ll7ZBKGAo8yd7uF7wJ9D3gUazwdz9fFjWV7oIk7ATwO\\n\" +\n\t\t\t\"lFllzmWDn+M2ygbHOGUGMX5hSaa8eDSieiR2QoLdn27Fip7kMBTJ2+GISrfnJTN/\\n\" +\n\t\t\t\"OQvmj0DXXAdxHmu2C4QgmZbkge35n129yzXn9NcqzrGLroV62lL3LgX6cSbiH5i7\\n\" +\n\t\t\t\"GgWY6CAPb1pMogV0K475n9FvOSDRiG4QSO5yqKiA3OP5aKrIRp2TNAk4IwARAQAB\\n\" +\n\t\t\t\"tCZQZXRlciBTYWludC1BbmRyZSA8c3RwZXRlckBzdHBldGVyLmltPokCOQQTAQIA\\n\" +\n\t\t\t\"IwUCURMPOwIbAwcLCQgHAwIBBhUIAgkKCwQWAgMBAh4BAheAAAoJEOoGpJErxa2p\\n\" +\n\t\t\t\"6bgQAKpxu07cMDOLc4+EG8H19NWXIVVybOEvfGuHYZaLKkPrhrMZwJiOwBpyISNR\\n\" +\n\t\t\t\"t9qzX1eLCVaojaoEVX6kD8MGc5zKFfiJZy3j7lBWl+Ybr7FfXYy2BbAXKx49e1n6\\n\" +\n\t\t\t\"ci9LmBrmVfAEaxtDNPITZ9N9oUAb9vS0nrG036EwteEHAveQvlDjO7lhz6+Cv7lZ\\n\" +\n\t\t\t\"QgBj9rZ6khfcQ4S3nSCQaKLQ9Iav4fqxI7SfuPKnx6quHX3JNLGnVo3wl+j/foCK\\n\" +\n\t\t\t\"0iTrmtHxCI3kc/bx6g32pRjHEPX0ALMBhmzU2uca+TE0zCEC96mgYXAUCwdnCFWy\\n\" +\n\t\t\t\"beIEbt6pz65iML13kAVAq0H/GqncnMGN0MbOatnw1Tdz/vkLojIy7QbPcQ0plUFx\\n\" +\n\t\t\t\"v5491xPfIrHhOWdRXp6WUt88fcqhT6MHZpVRtusj2ornKVVn+Y0GLsMMCTcrXJRG\\n\" +\n\t\t\t\"7Ao1YV72t/pJpzfGWSaaxolxDIZ6B+76jrIhUhiWgo/4nf+DN6BIlCZQ6j6xxjjx\\n\" +\n\t\t\t\"462cu02kuhIILTk2pzaMOufTBWx0uJhZk/KP2Fay/41pX7pvVOwRC4uIlKsLnJKL\\n\" +\n\t\t\t\"PS7EDa4BUUxENfd/9LqOGwlII8BbSe98PLMI8sXkcigc3UXMVda9ll0YhQa+lbP1\\n\" +\n\t\t\t\"NaszmnBhwuiCsgnPGbImsJuRzgEEgckwP/dNeyr6MlFMyfaeuQINBFETDzsBEADB\\n\" +\n\t\t\t\"zOsEHpUmhkRUjH9Tek87dn5P/Yh/L/HptgCGk40TL/C+kYdkd3HyteMEf061PNms\\n\" +\n\t\t\t\"S/Rq8k37Fu3VODYb9SPYKxtgksKSYUtIkPKvao09K9QNWPqyWuNf0F+iAjVMUuda\\n\" +\n\t\t\t\"EVFJ7bHF310RDwLY5IvLeCXxtvG+Vv/i+g77d2WdPDp+zLJ8306C4yBKjSJV8xW0\\n\" +\n\t\t\t\"cn2fd7NviIEN6cNHTsZNDZVMlgYPrxnwSq8GTEPGC7HsLIwGcx3hIe9QjnPw9CpA\\n\" +\n\t\t\t\"mQENpDEyWcxgF5uwo2NJECoDswKz1Nb0gfawF3ZIbD+GcLujTu94iJuVg25jATWm\\n\" +\n\t\t\t\"9wTgcfZo4UPllRGXdIb8uWwUFQlLQgd4ROLZZtXNGmHIymJrV2crx53gxup+1j0X\\n\" +\n\t\t\t\"qhlzKg8xbImWhEfS9oHZkRK8VHgmWSIt7TNwNir6N5j3lqwWVBhnu6GzF01sKGNy\\n\" +\n\t\t\t\"SlqNRbd0fqhakCkK71b8ot8tYTcYG5Lg10z6HTbgQx2UwLthUjqbblDQ+GLmrOhi\\n\" +\n\t\t\t\"WklLXRsnlnPMwnEyFePAnsT5tasy2Cn9qjpttNDah7PB8iFUi9mtTF/XDVgpFaB5\\n\" +\n\t\t\t\"G3CDV7Q2NgbAI6g6QhLIAmXzSP635G83mda0TKXHQXHDyLJTTn+WVFU7t4m4uLt+\\n\" +\n\t\t\t\"0DsWU8jXHQWyUTNG9WPUrXhusDUAPHxFCQ/n/lQVBwARAQABiQIfBBgBAgAJBQJR\\n\" +\n\t\t\t\"Ew87AhsMAAoJEOoGpJErxa2pqfgP/ApN+TRu2bBIgaw1dr3AznSSha84DIpXUDh3\\n\" +\n\t\t\t\"udZvQrGbUtz8/mA+e3iZEN/cmmBw2LGlAuQoJNILTZQ318yTP+E5QU7fJH7FVsoh\\n\" +\n\t\t\t\"UyvrMfyt3IMA9jg0Z9MuloLezvIjjMfFeNa0ROgDb/ubOT7JQzi1kwN8Lu3lO80H\\n\" +\n\t\t\t\"wqBHXEeOLoislUSnZajRKvITbKWkZ6PHRjlMw1Wk4oIi6VLHgGgj79zzL3uhML26\\n\" +\n\t\t\t\"63m7imShvz1QcHTwvyR5i8cZbNOEkotZyERiA1p7YHuruS+QvTi3ZPoQbnMUB3a7\\n\" +\n\t\t\t\"py9d11bw1+w3LiAUGZE/z5hBWOFxYtw+w/U/Vx0BwJGYlwU3M2W20uEXe+qxz7wn\\n\" +\n\t\t\t\"akygKjmLiD2z4njfKjcNCiV3FmXrpmWgADln1c4jfxDh0NrndrsM8FPDf1TMPtOZ\\n\" +\n\t\t\t\"gFDkKripc9xkZ/25P6xn27oTOHWKcAC0QhxSH+HuVBBRk8AgF+zAbDZe4/L6+kan\\n\" +\n\t\t\t\"SrycIXW+wCzwBq61aWsz2QhhuKjozVkhk4dRG+CfjzAFjnyxwYERn3uXVKQAwTwc\\n\" +\n\t\t\t\"dNcTI9RV98IsNrw9Y4lJEAg6CjNPmiD5+EASycqaOuToRSGukr8sOQLWLPyTnez/\\n\" +\n\t\t\t\"aG8Xf7a+fntWzK2HuDYoSDhJJrylWw/lMklOBm4wtMeNA0zcQH6AQV/GzQVQkSGq\\n\" + \"rLuMVIV/\\n\" + \"=llGw\\n\" +\n\t\t\t\"-----END PGP PUBLIC KEY BLOCK-----\\n\" + \"      </text>\\n\" + \"    </key>\\n\" + \"    <note>\\n\" +\n\t\t\t\"      <text>\\n\" + \"More information about me is located on my \\n\" +\n\t\t\t\"personal website: https://stpeter.im/\\n\" + \"    </text>\\n\" + \"  </note>\\n\" + \"</vcard>\").replace(\"\\n\", \"\")\n\t\t\t.replace(\"  \", \"\");\n\n\tprivate static String VCARD4_NOLOGO_DATA = (\"<vcard \" + \"xmlns=\\\"urn:ietf:params:xml:ns:vcard-4.0\\\">\\n\" +\n\t\t\t\"  <fn>\\n\" + \"    <text>Peter Saint-Andre</text>\\n\" + \"  </fn>\\n\" + \"  <n>\\n\" +\n\t\t\t\"    <surname>Saint-Andre</surname>\\n\" + \"    <given>Peter</given>\\n\" + \"    <additional></additional>\\n\" +\n\t\t\t\"  </n>\\n\" + \"  <nickname>\\n\" + \"    <text>stpeter</text>\\n\" + \"  </nickname>\\n\" + \"  <nickname>\\n\" +\n\t\t\t\"    <text>psa</text>\\n\" + \"  </nickname>\\n\" + \"  <photo>\\n\" +\n\t\t\t\"    <uri>http://stpeter.im/images/stpeter_oscon.jpg</uri>\\n\" + \"  </photo>\\n\" + \"  <photo>\\n\" +\n\t\t\t\"    <uri>http://stpeter.im/images/stpeter_hell.jpg</uri>\\n\" + \"  </photo>\\n\" + \"  <bday>\\n\" +\n\t\t\t\"    <date>1966-08-06</date>\\n\" + \"  </bday>\\n\" + \"  <adr>\\n\" + \"    <parameters>\\n\" +\n\t\t\t\"      <type><text>work</text></type>\\n\" + \"      <pref><integer>1</integer></pref>\\n\" +\n\t\t\t\"    </parameters>\\n\" + \"    <ext>Suite 600</ext>\\n\" + \"    <street>1899 Wynkoop Street</street>\\n\" +\n\t\t\t\"    <locality>Denver</locality>\\n\" + \"    <region>CO</region>\\n\" + \"    <code>80202</code>\\n\" +\n\t\t\t\"    <country>USA</country>\\n\" + \"  </adr>\\n\" + \"  <adr>\\n\" +\n\t\t\t\"    <parameters><type><text>home</text></type></parameters>\\n\" + \"    <ext></ext>\\n\" +\n\t\t\t\"    <street></street>\\n\" + \"    <locality>Parker</locality>\\n\" + \"    <region>CO</region>\\n\" +\n\t\t\t\"    <code>80138</code>\\n\" + \"    <country>USA</country>\\n\" + \"  </adr>\\n\" + \"  <tel>\\n\" +\n\t\t\t\"    <parameters>\\n\" + \"      <type><text>work</text><text>voice</text></type>\\n\" +\n\t\t\t\"      <pref><integer>1</integer></pref>\\n\" + \"    </parameters>\\n\" +\n\t\t\t\"    <uri>tel:+1-303-308-3282</uri>\\n\" + \"  </tel>\\n\" + \"  <tel>\\n\" + \"    <parameters>\\n\" +\n\t\t\t\"      <type><text>work</text><text>fax</text></type>\\n\" + \"    </parameters>\\n\" +\n\t\t\t\"    <uri>tel:+1-303-308-3219</uri>\\n\" + \"  </tel>\\n\" + \"  <tel>\\n\" + \"    <parameters>\\n\" +\n\t\t\t\"      <type><text>cell</text><text>voice</text><text>text</text></type>\\n\" + \"    </parameters>\\n\" +\n\t\t\t\"    <uri>tel:+1-720-256-6756</uri>\\n\" + \"  </tel>\\n\" + \"  <tel>\\n\" + \"    <parameters>\\n\" +\n\t\t\t\"      <type><text>home</text><text>voice</text></type>\\n\" + \"    </parameters>\\n\" +\n\t\t\t\"    <uri>tel:+1-303-555-1212</uri>\\n\" + \"  </tel>\\n\" + \"  <email>\\n\" +\n\t\t\t\"    <text>stpeter@jabber.org</text>\\n\" + \"  </email>\\n\" + \"  <email>\\n\" + \"    <parameters>\\n\" +\n\t\t\t\"      <type><text>work</text></type>\\n\" + \"    </parameters>\\n\" + \"    <text>psaintan@cisco.com</text>\\n\" +\n\t\t\t\"  </email>\\n\" + \"  <impp>\\n\" + \"    <uri>xmpp:stpeter@jabber.org</uri>\\n\" + \"  </impp>\\n\" + \"  <tz>\\n\" +\n\t\t\t\"    <text>America/Denver</text>\\n\" + \"  </tz>\\n\" + \"  <geo>\\n\" + \"    <uri>geo:39.59,-105.01</uri>\\n\" +\n\t\t\t\"  </geo>\\n\" + \"  <title>\\n\" + \"    <text>Executive Director</text>\\n\" + \"  </title>\\n\" + \"  <role>\\n\" +\n\t\t\t\"    <text>Patron Saint</text>\\n\" + \"  </role>\\n\" + \"  <logo/>\\n\" + \"    <org>\\n\" +\n\t\t\t\"      <text>XMPP Standards Foundation</text>\\n\" + \"    </org>\\n\" + \"    <url>\\n\" +\n\t\t\t\"      <uri>https://stpeter.im/</uri>\\n\" + \"    </url>\\n\" + \"    <url>\\n\" +\n\t\t\t\"      <uri>http://www.saint-andre.com/</uri>\\n\" + \"    </url>\\n\" + \"    <key>\\n\" + \"      <text>\\n\" +\n\t\t\t\"-----BEGIN PGP PUBLIC KEY BLOCK-----\\n\" + \"Version: GnuPG/MacGPG2 v2.0.18 (Darwin)\\n\" + \"\\n\" +\n\t\t\t\"mQINBFETDzsBEAC0FOv1N3ZJzIIxN6cKD475KVS9CHDPeYpegcOIPnL5eY1DCHeh\\n\" +\n\t\t\t\"/IwS1S7RCePtmiybNoV9FsI4PKUknzXQxA6LVEdAR/LUlhgJKjq+gsgp8lqbEILh\\n\" +\n\t\t\t\"g13ecH66HwLS9rarbQkC47T7kL8miIPBFC6E3A4Lq1L+eueO6UcLhKgoYkMxOjdi\\n\" +\n\t\t\t\"WrMgKTnVpch5ydLkPm/z0Zo8zRgqlPuTLeCrXXZYnjHXLVFN2xy04UzOs7P5u5KV\\n\" +\n\t\t\t\"fx5Z7uQisr8pXtyLd6SpTZo6SHgKBv15uz0rqXhsJojiGtOXfWznAjaS5FUOORq9\\n\" +\n\t\t\t\"CklG5cMOUAT8TNftv0ktsxaWDL1ELDVQPy1m7mtzo+VREG+0xmU6AjMo/GHblW1U\\n\" +\n\t\t\t\"U7MI9yCiuMLsp/HLrFuiosqLVZ85wuLQ2junPe3tK8h15UcxIXAcpQ1VqIaDQFbe\\n\" +\n\t\t\t\"uLOXJTF8YHpHdpHYt/ZM1ll7ZBKGAo8yd7uF7wJ9D3gUazwdz9fFjWV7oIk7ATwO\\n\" +\n\t\t\t\"lFllzmWDn+M2ygbHOGUGMX5hSaa8eDSieiR2QoLdn27Fip7kMBTJ2+GISrfnJTN/\\n\" +\n\t\t\t\"OQvmj0DXXAdxHmu2C4QgmZbkge35n129yzXn9NcqzrGLroV62lL3LgX6cSbiH5i7\\n\" +\n\t\t\t\"GgWY6CAPb1pMogV0K475n9FvOSDRiG4QSO5yqKiA3OP5aKrIRp2TNAk4IwARAQAB\\n\" +\n\t\t\t\"tCZQZXRlciBTYWludC1BbmRyZSA8c3RwZXRlckBzdHBldGVyLmltPokCOQQTAQIA\\n\" +\n\t\t\t\"IwUCURMPOwIbAwcLCQgHAwIBBhUIAgkKCwQWAgMBAh4BAheAAAoJEOoGpJErxa2p\\n\" +\n\t\t\t\"6bgQAKpxu07cMDOLc4+EG8H19NWXIVVybOEvfGuHYZaLKkPrhrMZwJiOwBpyISNR\\n\" +\n\t\t\t\"t9qzX1eLCVaojaoEVX6kD8MGc5zKFfiJZy3j7lBWl+Ybr7FfXYy2BbAXKx49e1n6\\n\" +\n\t\t\t\"ci9LmBrmVfAEaxtDNPITZ9N9oUAb9vS0nrG036EwteEHAveQvlDjO7lhz6+Cv7lZ\\n\" +\n\t\t\t\"QgBj9rZ6khfcQ4S3nSCQaKLQ9Iav4fqxI7SfuPKnx6quHX3JNLGnVo3wl+j/foCK\\n\" +\n\t\t\t\"0iTrmtHxCI3kc/bx6g32pRjHEPX0ALMBhmzU2uca+TE0zCEC96mgYXAUCwdnCFWy\\n\" +\n\t\t\t\"beIEbt6pz65iML13kAVAq0H/GqncnMGN0MbOatnw1Tdz/vkLojIy7QbPcQ0plUFx\\n\" +\n\t\t\t\"v5491xPfIrHhOWdRXp6WUt88fcqhT6MHZpVRtusj2ornKVVn+Y0GLsMMCTcrXJRG\\n\" +\n\t\t\t\"7Ao1YV72t/pJpzfGWSaaxolxDIZ6B+76jrIhUhiWgo/4nf+DN6BIlCZQ6j6xxjjx\\n\" +\n\t\t\t\"462cu02kuhIILTk2pzaMOufTBWx0uJhZk/KP2Fay/41pX7pvVOwRC4uIlKsLnJKL\\n\" +\n\t\t\t\"PS7EDa4BUUxENfd/9LqOGwlII8BbSe98PLMI8sXkcigc3UXMVda9ll0YhQa+lbP1\\n\" +\n\t\t\t\"NaszmnBhwuiCsgnPGbImsJuRzgEEgckwP/dNeyr6MlFMyfaeuQINBFETDzsBEADB\\n\" +\n\t\t\t\"zOsEHpUmhkRUjH9Tek87dn5P/Yh/L/HptgCGk40TL/C+kYdkd3HyteMEf061PNms\\n\" +\n\t\t\t\"S/Rq8k37Fu3VODYb9SPYKxtgksKSYUtIkPKvao09K9QNWPqyWuNf0F+iAjVMUuda\\n\" +\n\t\t\t\"EVFJ7bHF310RDwLY5IvLeCXxtvG+Vv/i+g77d2WdPDp+zLJ8306C4yBKjSJV8xW0\\n\" +\n\t\t\t\"cn2fd7NviIEN6cNHTsZNDZVMlgYPrxnwSq8GTEPGC7HsLIwGcx3hIe9QjnPw9CpA\\n\" +\n\t\t\t\"mQENpDEyWcxgF5uwo2NJECoDswKz1Nb0gfawF3ZIbD+GcLujTu94iJuVg25jATWm\\n\" +\n\t\t\t\"9wTgcfZo4UPllRGXdIb8uWwUFQlLQgd4ROLZZtXNGmHIymJrV2crx53gxup+1j0X\\n\" +\n\t\t\t\"qhlzKg8xbImWhEfS9oHZkRK8VHgmWSIt7TNwNir6N5j3lqwWVBhnu6GzF01sKGNy\\n\" +\n\t\t\t\"SlqNRbd0fqhakCkK71b8ot8tYTcYG5Lg10z6HTbgQx2UwLthUjqbblDQ+GLmrOhi\\n\" +\n\t\t\t\"WklLXRsnlnPMwnEyFePAnsT5tasy2Cn9qjpttNDah7PB8iFUi9mtTF/XDVgpFaB5\\n\" +\n\t\t\t\"G3CDV7Q2NgbAI6g6QhLIAmXzSP635G83mda0TKXHQXHDyLJTTn+WVFU7t4m4uLt+\\n\" +\n\t\t\t\"0DsWU8jXHQWyUTNG9WPUrXhusDUAPHxFCQ/n/lQVBwARAQABiQIfBBgBAgAJBQJR\\n\" +\n\t\t\t\"Ew87AhsMAAoJEOoGpJErxa2pqfgP/ApN+TRu2bBIgaw1dr3AznSSha84DIpXUDh3\\n\" +\n\t\t\t\"udZvQrGbUtz8/mA+e3iZEN/cmmBw2LGlAuQoJNILTZQ318yTP+E5QU7fJH7FVsoh\\n\" +\n\t\t\t\"UyvrMfyt3IMA9jg0Z9MuloLezvIjjMfFeNa0ROgDb/ubOT7JQzi1kwN8Lu3lO80H\\n\" +\n\t\t\t\"wqBHXEeOLoislUSnZajRKvITbKWkZ6PHRjlMw1Wk4oIi6VLHgGgj79zzL3uhML26\\n\" +\n\t\t\t\"63m7imShvz1QcHTwvyR5i8cZbNOEkotZyERiA1p7YHuruS+QvTi3ZPoQbnMUB3a7\\n\" +\n\t\t\t\"py9d11bw1+w3LiAUGZE/z5hBWOFxYtw+w/U/Vx0BwJGYlwU3M2W20uEXe+qxz7wn\\n\" +\n\t\t\t\"akygKjmLiD2z4njfKjcNCiV3FmXrpmWgADln1c4jfxDh0NrndrsM8FPDf1TMPtOZ\\n\" +\n\t\t\t\"gFDkKripc9xkZ/25P6xn27oTOHWKcAC0QhxSH+HuVBBRk8AgF+zAbDZe4/L6+kan\\n\" +\n\t\t\t\"SrycIXW+wCzwBq61aWsz2QhhuKjozVkhk4dRG+CfjzAFjnyxwYERn3uXVKQAwTwc\\n\" +\n\t\t\t\"dNcTI9RV98IsNrw9Y4lJEAg6CjNPmiD5+EASycqaOuToRSGukr8sOQLWLPyTnez/\\n\" +\n\t\t\t\"aG8Xf7a+fntWzK2HuDYoSDhJJrylWw/lMklOBm4wtMeNA0zcQH6AQV/GzQVQkSGq\\n\" + \"rLuMVIV/\\n\" + \"=llGw\\n\" +\n\t\t\t\"-----END PGP PUBLIC KEY BLOCK-----\\n\" + \"      </text>\\n\" + \"    </key>\\n\" + \"    <note>\\n\" +\n\t\t\t\"      <text>\\n\" + \"More information about me is located on my \\n\" +\n\t\t\t\"personal website: https://stpeter.im/\\n\" + \"    </text>\\n\" + \"  </note>\\n\" + \"</vcard>\").replace(\"\\n\", \"\")\n\t\t\t.replace(\"  \", \"\");\n\tprivate static String VCARD_TEMP_DATA = (\"<vCard xmlns=\\\"vcard-temp\\\">\\n\" + \"  <FN>Peter Saint-Andre</FN>\\n\" +\n\t\t\t\"  <N>\\n\" + \"    <FAMILY>Saint-Andre</FAMILY>\\n\" + \"    <GIVEN>Peter</GIVEN>\\n\" + \"    <MIDDLE/>\\n\" +\n\t\t\t\"  </N>\\n\" + \"  <NICKNAME>stpeter</NICKNAME>\\n\" + \"  <NICKNAME>psa</NICKNAME>\\n\" +\n\t\t\t\"  <PHOTO><EXTVAL>http://stpeter.im/images/stpeter_oscon.jpg</EXTVAL></PHOTO>\\n\" +\n\t\t\t\"  <PHOTO><EXTVAL>http://stpeter.im/images/stpeter_hell.jpg</EXTVAL></PHOTO>\\n\" +\n\t\t\t\"  <BDAY>1966-08-06</BDAY>\\n\" + \"  <ADR>\\n\" + \"    <WORK/>\\n\" + \"    <PREF/>\\n\" +\n\t\t\t\"    <EXTADD>Suite 600</EXTADD>\\n\" + \"    <STREET>1899 Wynkoop Street</STREET>\\n\" +\n\t\t\t\"    <LOCALITY>Denver</LOCALITY>\\n\" + \"    <REGION>CO</REGION>\\n\" + \"    <PCODE>80202</PCODE>\\n\" +\n\t\t\t\"    <CTRY>USA</CTRY>\\n\" + \"  </ADR>\\n\" + \"  <ADR>\\n\" + \"    <HOME/>\\n\" + \"    <EXTADD/>\\n\" +\n\t\t\t\"    <STREET/>\\n\" + \"    <LOCALITY>Parker</LOCALITY>\\n\" + \"    <REGION>CO</REGION>\\n\" +\n\t\t\t\"    <PCODE>80138</PCODE>\\n\" + \"    <CTRY>USA</CTRY>\\n\" + \"  </ADR>\\n\" +\n\t\t\t\"  <TEL><WORK/><VOICE/><NUMBER>+1-303-308-3282</NUMBER><PREF/></TEL>\\n\" +\n\t\t\t\"  <TEL><WORK/><FAX/><NUMBER>+1-303-308-3219</NUMBER></TEL>\\n\" +\n\t\t\t\"  <TEL><CELL/><VOICE/><TEXT/><NUMBER>+1-720-256-6756</NUMBER></TEL>\\n\" +\n\t\t\t\"  <TEL><HOME/><VOICE/><NUMBER>+1-303-555-1212</NUMBER></TEL>\\n\" +\n\t\t\t\"  <EMAIL><INTERNET/><USERID>stpeter@jabber.org</USERID></EMAIL>\\n\" +\n\t\t\t\"  <EMAIL><WORK/><USERID>psaintan@cisco.com</USERID></EMAIL>\\n\" +\n\t\t\t\"  <JABBERID>stpeter@jabber.org</JABBERID>\\n\" + \"  <TZ>America/Denver</TZ>\\n\" +\n\t\t\t\"  <GEO><LAT>39.59</LAT><LON>-105.01</LON></GEO>\\n\" + \"  <TITLE>Executive Director</TITLE>\\n\" +\n\t\t\t\"  <ROLE>Patron Saint</ROLE>\\n\" + \"  <LOGO>\\n\" + \"    <TYPE>image/jpeg</TYPE>\\n\" + \"    <BINVAL>\\n\" +\n\t\t\t\"/9j/4AAQSkZJRgABAQEASABIAAD//gAXQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q/9sAQwAIBgYHBgUI\\n\" +\n\t\t\t\"BwcHCQkICgwUDQwLCwwZEhMPFB0aHx4dGhwcICQuJyAiLCMcHCg3KSwwMTQ0NB8nOT04MjwuMzQy\\n\" +\n\t\t\t\"/9sAQwEJCQkMCwwYDQ0YMiEcITIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy\\n\" +\n\t\t\t\"MjIyMjIyMjIyMjIy/8AAEQgAgQB7AwEiAAIRAQMRAf/EABsAAAMAAwEBAAAAAAAAAAAAAAQFBgAD\\n\" +\n\t\t\t\"BwIB/8QAPRAAAgEDAwIDBQQJBAEFAAAAAQIDAAQRBRIhMUEGE1EiYXGBkRQyobEHFSMzNEJScsEk\\n\" +\n\t\t\t\"YtHw0jVDc8Lh/8QAGQEAAwEBAQAAAAAAAAAAAAAAAQIEAwAF/8QAJBEAAgICAgICAgMAAAAAAAAA\\n\" +\n\t\t\t\"AAECEQMhEjEEQSJREzJCYXH/2gAMAwEAAhEDEQA/AOf2c0KGSY2pDFiinYMsCMfhj8aya6gdvZj9\\n\" +\n\t\t\t\"ryg6A8eYuORxQk1rdzSHyZGQYO0GXAPv/MV6luDp8QgntkRyhAfHJ3Y5wOoxxXncbdiA9zp/6xQG\\n\" +\n\t\t\t\"KaGFIhK208MwX09amCOeMEdjT3VLi1EaeVGHEi4WRCQVwAD7PY5/CkQYbcfdPUVbjTS2FGAHGele\\n\" +\n\t\t\t\"1nmXgOw6/iMV4LnHup/4c8L3/iWfbboIoFPt3D/dH/kfcKd0kOotukL/ALWs8SJNEPZGA4/z2omD\\n\" +\n\t\t\t\"T7++dY7azuLlAOPs8Jfb9P8Amu1+Hv0Z+HtPjVp7YX846yXAyM+5elX1tDa2sKxwxRxoBgKigD6U\\n\" +\n\t\t\t\"nJGn4a7OB+G/0Y67rlxuuIRptojDdcXS5c/2R9SfoPfXY/DvgzQvCiLJZW/m3uPavbgb5Sf9vZPl\\n\" +\n\t\t\t\"9TVCBAw5RfiODQ93Fdi3b9XyxiXt5iE59wNPGSEnja6N5VyCzHYh5LOcUvuNc0y0JDTtO/8ATGP8\\n\" +\n\t\t\t\"1Hahc6g87xX0ku9TyhbH4dKEA6AZx1prJ2n7KW58W3L5W1iS3H9XVsfGlMl3NdNvmld8/wBRoMHH\\n\" +\n\t\t\t\"NbM5Hw5oAoIjALHPfA59O9KL218NS3byXtjE9w+GdivqBj8MUyk5ikAOPZ61rtreDUbaO5kiTeV2\\n\" +\n\t\t\t\"HJ7r7P8AiuGSOXDyzBE1zlInAKFR6k9a+XFtHt8qWczjGVUKSAvPHu+VLftckzPu+62U2E5256Y7\\n\" +\n\t\t\t\"1S+Fkj1DxPDaXEe5YYHldc9doGAfdnFQLE+VDdCc2ZXDaQDMTgFDEC6H175zgjimcv6PfFmvXMcy\\n\" +\n\t\t\t\"6SttFtB8+4dIEIIz0PPfsKuE1O7tEMVoy2sfPs28Sxdev3Rmh5Jp52LyzSSnqS7E/nVkIcezrFlj\\n\" +\n\t\t\t\"+ibSrC3aXWNbF9dAezaad93P+6Qjp8AKsNMt4LO1hgto0ihXhVj6CldrsSLoMt7qNhuiiDJyPSsc\\n\" +\n\t\t\t\"k23Rfgi4qyphkCqAMcUQsue9TsGoAkDP1osXOT1oWO0x2JOeua2rPiki3GO/Hxr0L5F/nFGwNB2q\\n\" +\n\t\t\t\"2UWq2+MAXCcxv/g/GopkaNijqVdWIYHqDVSt9uDYbt2pRrKrJJDdgcyrh/7h/wDlawlZLnh7QB96\\n\" +\n\t\t\t\"E46ivSnlDXyAAyFD/Nx9elZGQ0XHJVsH3VqSntk3W7oDgEYz6ULbWkkURUuxO9ySOMksTRY5iPx5\\n\" +\n\t\t\t\"o6KaMxKSygnkjFdQUzhMca3Mau7KXXII28EdvnVv4FtTLq97dOwLR2PlDHUEsD/ioO1uHCg9Og9B\\n\" +\n\t\t\t\"mrPwXrENomp3EynYApYoMkYGOfRfaHuqeKansdlHOMTOeOSelaxjml763ayalcBplW22K8bnoCRl\\n\" +\n\t\t\t\"gT3x2xW21v4bosoDIRgKHGGbPTjPpzWjkmhUrYbFNhgPSjLYPN7K45oGNRtDnGCCc+6grvxhDZsY\\n\" +\n\t\t\t\"rS3LBeN/9VSvbPTi+KK6GwGBucg0T5HljqSPhXMz4/uFmAa1brzk4qisfFS3UIcMR6g9qL12FS5d\\n\" +\n\t\t\t\"FasLOOMAV5ktMDIwamLjxVFagmWTC+g60HF+kOw85UzIfftoJphbrsqTKYwynI7VlzMJdP2fzKQw\\n\" +\n\t\t\t\"+VDJqFjqsPmW0w8wclfWtYV3kKLzlSaeLakZ5acT6jhZIz0Vhj4HtXqQFLuTjaXGTitJztZSuGjY\\n\" +\n\t\t\t\"EgjHuomQ7khlHpzVh5h8Gfs+e5oK6juvtDbCQuAQMj0FGkZgweo3D8DXiUxiTBYZAHYelKdRw85h\\n\" +\n\t\t\t\"uPImyG3glT0z61WadpVteeCdQu4DKk8ZKFVwd53DHPUDDduDioieSYsryZIJ4JGSapvB2pG21CWz\\n\" +\n\t\t\t\"iLSR3kZR0ZcYI6H48kUvH2zRjLTdOngh+23gSGOFdzPMvKgEg4Pr299btN8Qw3Es8kenwolxJgvs\\n\" +\n\t\t\t\"BaMKPZOex+HrQXirWJdQu10m0kLwwkCUr0kk/wDFe3zo/wAP6KqxyW8hO5wWIHfHXFZSjS0beNXO\\n\" +\n\t\t\t\"2Uk2bmyaKH75XaAPSpC48P3qysvnJEf5VPUiq/TzsYhSckkD4U4uNLtL61Czp7XZhwR8xWKu7LeK\\n\" +\n\t\t\t\"emckk0a8VZGmkBYfcG7qaqvB+izSyMJh7O3INOx4bsrUmTy2kI6F2JApxo0ZWfftCx4xxTSbloEI\\n\" +\n\t\t\t\"KPRz7xjp89veMsSkoOmKQR2N9bTKgtzJuI7HH1zXW9XsYrq4kWZCQ3Rh1FJk8LXayfsLobO3mJuo\\n\" +\n\t\t\t\"xlx1QJwUttk3pDzwX8REEkTA9G6H1Hvq41Gznv8ASpoLMSCeZQIihwwbIPB7Vvh0j7PEPtDeY3rj\\n\" +\n\t\t\t\"AHw9KUa3c3EVmY7aXy3LfeHXHGefnSOTsbhy+Jmm61JLdHS9e/02oqTHHdNgJKf6X9D6HpTpoJYk\\n\" +\n\t\t\t\"aF125JIB6qf+4qXk0/8AWFnHJMfM8yMNyevFEaVq11psiWWozNLZniK4ckmP0Uk9vf2quLdbPKmq\\n\" +\n\t\t\t\"k0ig3AxZyOWP1IoG5jZp2IYYwPyo+RCigEdwR7/nXxolJ+5TMCOMy6cjloldDclDtjZcBh2KnOAe\\n\" +\n\t\t\t\"vFePCysNZkQTpbSG3lAkk6Icfn2oW3t7iBnTIwcYB7ntjPz+lPI41jucxxtPPHCHaRkUjOBk+hx+\\n\" +\n\t\t\t\"NYObho1SbDNGh0+3id/MJkAOd45Pfj6U6s7t0j0yWYCMzxud3Ze+KWX7Lc2lr5YjMqIY9yLgMg4y\\n\" +\n\t\t\t\"Pfk0PqazRaXpiSlxuQuAw6ZAroNysWMqdlIbhYH3owK9VPrX1vEwgXJcYHqaTpMG0+DJziNRSe9A\\n\" +\n\t\t\t\"julMn7vbuHxrDjuj0Yz1ZTxa8sjm9v8AzvsiA+yvUntTSw8b6TtKx7gOwNRL3cU1qIzcqM44z0pQ\\n\" +\n\t\t\t\"+lTtlrWWJjnJAcA08YJPsDyy9I68fE2jX0ixy3Pkk9Gz3oLT/FC+e9vM6syMVV+mR2rnFnpNxHNG\\n\" +\n\t\t\t\"8rIWzk5YGnht0lkySEkHcd6E1T0Osl9o6C2qpcIVGDxSHxBMsPh+5lI/anEafE9/oKVafLLFNtkP\\n\" +\n\t\t\t\"TofWvPiu9zZw2QHL/tSfdyB+dGHykLmdQbQfeuNP0G2uIQS0fkjJ/m4yR+BpmY7a7tY5gQ8cyhlB\\n\" +\n\t\t\t\"HUHtSfxJIX0FdoCwxvGqD19k148H3u+Oe1lbLRANCPicED5mrKPLbKDTHNl/o7pZJbM8xsCd0R9P\\n\" +\n\t\t\t\"7fT3+6mhuIFJUTK2OMqQB+Nabc+Vew7sja+XI6+8VN+JpLS08R3sC20MgRgC2M87RkfI8fKhdHUR\\n\" +\n\t\t\t\"Eqs826JiFJ3GJicNj8q1K7QasY4lUJuwgDHA+Jr5qGoFZFEC7QVwccU80S7tbjUYIjGrsrsqtJgA\\n\" +\n\t\t\t\"nBOPxFScZcv6NZP6FzxvFAIyCu1SQAeSCe3zzTXWJpr2y0uNUy8MGXbcAOQMc/KtfiGAx6rHBCQx\\n\" +\n\t\t\t\"8sbQPUscCvfimHz9G8uEYFsqkheAdowf+flVeHC6bMWwG3nVQYDIrFeMq2QOeK3TbJgsUqBjnFLv\\n\" +\n\t\t\t\"Clmt1aaizrgewFYDocmi/NZHCsAJAcEGpsiuWi/G6jsMHh61juRcxoGV12tG3TPrTP8AVOnMuJNO\\n\" +\n\t\t\t\"lRiwO5FJzwO4rxA7m2U7chhjFDy6heWr/swwHcE8Ckv7N0os3X/h+0kikksoJo2z7O4lfzpRHpN5\\n\" +\n\t\t\t\"YzxedcGVmzuH9NUFpfvOp3qzMemelBajMzXMa59r1rm/SOcUj2zYdWX04+NKNbnNzePtIOxAg+PJ\\n\" +\n\t\t\t\"/wA0RdXQhAVTlvypNLMMMzHJ3At9a2wQp2SeRk1xRaeLNsWi2sCHOJUUn4IRU/o119j1uynJOBLh\\n\" +\n\t\t\t\"h6huP8088Uc6Qsz8Ga4Gz3AKT/kVIGTE8RHUOCPqKoSJDqqkhsknJIOflSZ7qzhkZL1Fe53EuxQ8\\n\" +\n\t\t\t\"55/Iinb/AL/AHG4/8U0g01Lm2ilJiJZFzkDPAxQCcCuxslL+YoYnbjOeMVQeFjeR7GjtBcYlLKCv\\n\" +\n\t\t\t\"IOBk5xxkVXw6L4fsmDRWMMsi8B5zvP0PFbri+mCCKJkRcYAXgYrePhv+TOc/oU6rpkz6p9ufbCuw\\n\" +\n\t\t\t\"KsZbLKff9aC3tgq+DgFT3B7UyklySrcE5z76AmAwW2nGOearhj4RpGVtnrTjFBD9liQIgXgD/vNK\\n\" +\n\t\t\t\"tbtnBM0XDDHzFbfPMUgdTlaMd0uYd4OfdXleRheOfJdHoY8inCn2A6TrChtrH7uMBj0p3JrFtMVQ\\n\" +\n\t\t\t\"RqSSecdTUff2qpMWT2eeooSKS4jcMrZI6ZrHgnsP5GtF7cXsKo7sAm3sOKQrc+bcTTM37GH2nk9M\\n\" +\n\t\t\t\"8AUHBHLecXEh2HqF71v19o7Lw+IIgFEsiqR7hz/ihGK5DSk3GwW7Dw3UyOcurHn8vwrVbWzXtzDB\\n\" +\n\t\t\t\"082RUJ9Mmmtvo11rumW1/aNGZBGI5InbaxK8ZHr2r5Z2k1rrVjbSxvE5uEB3jbgZ5q9xa/wgu2OP\\n\" +\n\t\t\t\"HE4k+xIh/ZoZAMd8bakl5ljb1I/MVTeNCpWw8v7oMqjj4c/WphWwV5xyDSJ6CdYdv9Vnk4c1SWlq\\n\" +\n\t\t\t\"4tYwsgC44GKmCf2z9gGz3qxsJozYQFm5KA/WlOOXyXmV3LbuBjqFOKG86VwSu3gdCc0El/Pavtcs\\n\" +\n\t\t\t\"wHGCc0aJBcJ5ls6h+pU165mCudxGXYtnOOgFCzXgSJgxCRgd+rGjmkimO2T9nIOMilV/aOmWOGjJ\\n\" +\n\t\t\t\"/wCmhK60cgAXOx/bJIY1vWeSBtybnU9QBS2aNIs7nJ9MCt1ndMDsyfQZPU9qmbT+Mh1raDpl+0Dc\\n\" +\n\t\t\t\"o+tBeQ6ScJkVkd5JNqYt7OMyqAd2Ty3rj4UwhxOqyRZKn6j3GoMmNReiqMuW2b7RHwCRilviaQyf\\n\" +\n\t\t\t\"Y4V5wxOPU04x5ERZuTj6UhvdUtoL3eEFxMowP6F9ce+kxQVhySqNIrtDjks9DSJWG3GWVutM7e4h\\n\" +\n\t\t\t\"ljXzHDqP5ZBnHw7ilFrL5yQOmfLZAw+fNN7e3jVi7DrXs0qoha2aNV0uDUokEMqxSIxIDHIbIHHu\\n\" +\n\t\t\t\"qQvrK5sJRHPEVcA4PZvgasb28gXIVFDD+YcZoIzG/ga2uYjLG3C46qT3rGeFVaOTKQXDNKwlOT0y\\n\" +\n\t\t\t\"F93f51To5ihiQZAWJB0/2ipRgWiaQnLsNicDjA6mqaa1DuG2/wAqjqewAqFpp0zRHKNR/eCtel/x\\n\" +\n\t\t\t\"ArKyvVZmEah+9Wvp/gz8Kyspl0cIZ/3bUvi6r/dWVlSZOx4maF/6wvz/ADNOtB/eXf8A8jVlZUcz\\n\" +\n\t\t\t\"aPYXq38EfnUbcfxXyFZWUMQch0LTP4SH+2nX/sn4VlZXqx/VEz7EF19/50bpf8SPgPzrKyuAO4P3\\n\" +\n\t\t\t\"0vx/+oqw7D4D8qysrzcv7jro/9k=\\n\" + \"    </BINVAL>\\n\" + \"  </LOGO>\\n\" + \"  <ORG>\\n\" +\n\t\t\t\"    <ORGNAME>XMPP Standards Foundation</ORGNAME>\\n\" + \"    <ORGUNIT/>\\n\" + \"  </ORG>\\n\" +\n\t\t\t\"  <URL>https://stpeter.im/</URL>\\n\" + \"  <URL>http://www.saint-andre.com/</URL>\\n\" + \"  <KEY>\\n\" +\n\t\t\t\"    <CRED>\\n\" + \"-----BEGIN PGP PUBLIC KEY BLOCK-----\\n\" + \"Version: GnuPG/MacGPG2 v2.0.18 (Darwin)\\n\" +\n\t\t\t\"\\n\" + \"mQINBFETDzsBEAC0FOv1N3ZJzIIxN6cKD475KVS9CHDPeYpegcOIPnL5eY1DCHeh\\n\" +\n\t\t\t\"/IwS1S7RCePtmiybNoV9FsI4PKUknzXQxA6LVEdAR/LUlhgJKjq+gsgp8lqbEILh\\n\" +\n\t\t\t\"g13ecH66HwLS9rarbQkC47T7kL8miIPBFC6E3A4Lq1L+eueO6UcLhKgoYkMxOjdi\\n\" +\n\t\t\t\"WrMgKTnVpch5ydLkPm/z0Zo8zRgqlPuTLeCrXXZYnjHXLVFN2xy04UzOs7P5u5KV\\n\" +\n\t\t\t\"fx5Z7uQisr8pXtyLd6SpTZo6SHgKBv15uz0rqXhsJojiGtOXfWznAjaS5FUOORq9\\n\" +\n\t\t\t\"CklG5cMOUAT8TNftv0ktsxaWDL1ELDVQPy1m7mtzo+VREG+0xmU6AjMo/GHblW1U\\n\" +\n\t\t\t\"U7MI9yCiuMLsp/HLrFuiosqLVZ85wuLQ2junPe3tK8h15UcxIXAcpQ1VqIaDQFbe\\n\" +\n\t\t\t\"uLOXJTF8YHpHdpHYt/ZM1ll7ZBKGAo8yd7uF7wJ9D3gUazwdz9fFjWV7oIk7ATwO\\n\" +\n\t\t\t\"lFllzmWDn+M2ygbHOGUGMX5hSaa8eDSieiR2QoLdn27Fip7kMBTJ2+GISrfnJTN/\\n\" +\n\t\t\t\"OQvmj0DXXAdxHmu2C4QgmZbkge35n129yzXn9NcqzrGLroV62lL3LgX6cSbiH5i7\\n\" +\n\t\t\t\"GgWY6CAPb1pMogV0K475n9FvOSDRiG4QSO5yqKiA3OP5aKrIRp2TNAk4IwARAQAB\\n\" +\n\t\t\t\"tCZQZXRlciBTYWludC1BbmRyZSA8c3RwZXRlckBzdHBldGVyLmltPokCOQQTAQIA\\n\" +\n\t\t\t\"IwUCURMPOwIbAwcLCQgHAwIBBhUIAgkKCwQWAgMBAh4BAheAAAoJEOoGpJErxa2p\\n\" +\n\t\t\t\"6bgQAKpxu07cMDOLc4+EG8H19NWXIVVybOEvfGuHYZaLKkPrhrMZwJiOwBpyISNR\\n\" +\n\t\t\t\"t9qzX1eLCVaojaoEVX6kD8MGc5zKFfiJZy3j7lBWl+Ybr7FfXYy2BbAXKx49e1n6\\n\" +\n\t\t\t\"ci9LmBrmVfAEaxtDNPITZ9N9oUAb9vS0nrG036EwteEHAveQvlDjO7lhz6+Cv7lZ\\n\" +\n\t\t\t\"QgBj9rZ6khfcQ4S3nSCQaKLQ9Iav4fqxI7SfuPKnx6quHX3JNLGnVo3wl+j/foCK\\n\" +\n\t\t\t\"0iTrmtHxCI3kc/bx6g32pRjHEPX0ALMBhmzU2uca+TE0zCEC96mgYXAUCwdnCFWy\\n\" +\n\t\t\t\"beIEbt6pz65iML13kAVAq0H/GqncnMGN0MbOatnw1Tdz/vkLojIy7QbPcQ0plUFx\\n\" +\n\t\t\t\"v5491xPfIrHhOWdRXp6WUt88fcqhT6MHZpVRtusj2ornKVVn+Y0GLsMMCTcrXJRG\\n\" +\n\t\t\t\"7Ao1YV72t/pJpzfGWSaaxolxDIZ6B+76jrIhUhiWgo/4nf+DN6BIlCZQ6j6xxjjx\\n\" +\n\t\t\t\"462cu02kuhIILTk2pzaMOufTBWx0uJhZk/KP2Fay/41pX7pvVOwRC4uIlKsLnJKL\\n\" +\n\t\t\t\"PS7EDa4BUUxENfd/9LqOGwlII8BbSe98PLMI8sXkcigc3UXMVda9ll0YhQa+lbP1\\n\" +\n\t\t\t\"NaszmnBhwuiCsgnPGbImsJuRzgEEgckwP/dNeyr6MlFMyfaeuQINBFETDzsBEADB\\n\" +\n\t\t\t\"zOsEHpUmhkRUjH9Tek87dn5P/Yh/L/HptgCGk40TL/C+kYdkd3HyteMEf061PNms\\n\" +\n\t\t\t\"S/Rq8k37Fu3VODYb9SPYKxtgksKSYUtIkPKvao09K9QNWPqyWuNf0F+iAjVMUuda\\n\" +\n\t\t\t\"EVFJ7bHF310RDwLY5IvLeCXxtvG+Vv/i+g77d2WdPDp+zLJ8306C4yBKjSJV8xW0\\n\" +\n\t\t\t\"cn2fd7NviIEN6cNHTsZNDZVMlgYPrxnwSq8GTEPGC7HsLIwGcx3hIe9QjnPw9CpA\\n\" +\n\t\t\t\"mQENpDEyWcxgF5uwo2NJECoDswKz1Nb0gfawF3ZIbD+GcLujTu94iJuVg25jATWm\\n\" +\n\t\t\t\"9wTgcfZo4UPllRGXdIb8uWwUFQlLQgd4ROLZZtXNGmHIymJrV2crx53gxup+1j0X\\n\" +\n\t\t\t\"qhlzKg8xbImWhEfS9oHZkRK8VHgmWSIt7TNwNir6N5j3lqwWVBhnu6GzF01sKGNy\\n\" +\n\t\t\t\"SlqNRbd0fqhakCkK71b8ot8tYTcYG5Lg10z6HTbgQx2UwLthUjqbblDQ+GLmrOhi\\n\" +\n\t\t\t\"WklLXRsnlnPMwnEyFePAnsT5tasy2Cn9qjpttNDah7PB8iFUi9mtTF/XDVgpFaB5\\n\" +\n\t\t\t\"G3CDV7Q2NgbAI6g6QhLIAmXzSP635G83mda0TKXHQXHDyLJTTn+WVFU7t4m4uLt+\\n\" +\n\t\t\t\"0DsWU8jXHQWyUTNG9WPUrXhusDUAPHxFCQ/n/lQVBwARAQABiQIfBBgBAgAJBQJR\\n\" +\n\t\t\t\"Ew87AhsMAAoJEOoGpJErxa2pqfgP/ApN+TRu2bBIgaw1dr3AznSSha84DIpXUDh3\\n\" +\n\t\t\t\"udZvQrGbUtz8/mA+e3iZEN/cmmBw2LGlAuQoJNILTZQ318yTP+E5QU7fJH7FVsoh\\n\" +\n\t\t\t\"UyvrMfyt3IMA9jg0Z9MuloLezvIjjMfFeNa0ROgDb/ubOT7JQzi1kwN8Lu3lO80H\\n\" +\n\t\t\t\"wqBHXEeOLoislUSnZajRKvITbKWkZ6PHRjlMw1Wk4oIi6VLHgGgj79zzL3uhML26\\n\" +\n\t\t\t\"63m7imShvz1QcHTwvyR5i8cZbNOEkotZyERiA1p7YHuruS+QvTi3ZPoQbnMUB3a7\\n\" +\n\t\t\t\"py9d11bw1+w3LiAUGZE/z5hBWOFxYtw+w/U/Vx0BwJGYlwU3M2W20uEXe+qxz7wn\\n\" +\n\t\t\t\"akygKjmLiD2z4njfKjcNCiV3FmXrpmWgADln1c4jfxDh0NrndrsM8FPDf1TMPtOZ\\n\" +\n\t\t\t\"gFDkKripc9xkZ/25P6xn27oTOHWKcAC0QhxSH+HuVBBRk8AgF+zAbDZe4/L6+kan\\n\" +\n\t\t\t\"SrycIXW+wCzwBq61aWsz2QhhuKjozVkhk4dRG+CfjzAFjnyxwYERn3uXVKQAwTwc\\n\" +\n\t\t\t\"dNcTI9RV98IsNrw9Y4lJEAg6CjNPmiD5+EASycqaOuToRSGukr8sOQLWLPyTnez/\\n\" +\n\t\t\t\"aG8Xf7a+fntWzK2HuDYoSDhJJrylWw/lMklOBm4wtMeNA0zcQH6AQV/GzQVQkSGq\\n\" + \"rLuMVIV/\\n\" + \"=llGw\\n\" +\n\t\t\t\"-----END PGP PUBLIC KEY BLOCK-----\\n\" + \"    </CRED>\\n\" + \"  </KEY>\\n\" + \"  <DESC>\\n\" +\n\t\t\t\"    More information about me is located on my \\n\" + \"    personal website: https://stpeter.im/\\n\" +\n\t\t\t\"  </DESC>\\n\" + \"</vCard>\").replace(\"\\n\", \"\").replace(\"  \", \"\");\n\tprivate static String VCARD_TEMP_NOLOGO_DATA = (\"<vCard xmlns=\\\"vcard-temp\\\">\\n\" +\n\t\t\t\"  <FN>Peter Saint-Andre</FN>\\n\" + \"  <N>\\n\" + \"    <FAMILY>Saint-Andre</FAMILY>\\n\" +\n\t\t\t\"    <GIVEN>Peter</GIVEN>\\n\" + \"    <MIDDLE/>\\n\" + \"  </N>\\n\" + \"  <NICKNAME>stpeter</NICKNAME>\\n\" +\n\t\t\t\"  <NICKNAME>psa</NICKNAME>\\n\" +\n\t\t\t\"  <PHOTO><EXTVAL>http://stpeter.im/images/stpeter_oscon.jpg</EXTVAL></PHOTO>\\n\" +\n\t\t\t\"  <PHOTO><EXTVAL>http://stpeter.im/images/stpeter_hell.jpg</EXTVAL></PHOTO>\\n\" +\n\t\t\t\"  <BDAY>1966-08-06</BDAY>\\n\" + \"  <ADR>\\n\" + \"    <WORK/>\\n\" + \"    <PREF/>\\n\" +\n\t\t\t\"    <EXTADD>Suite 600</EXTADD>\\n\" + \"    <STREET>1899 Wynkoop Street</STREET>\\n\" +\n\t\t\t\"    <LOCALITY>Denver</LOCALITY>\\n\" + \"    <REGION>CO</REGION>\\n\" + \"    <PCODE>80202</PCODE>\\n\" +\n\t\t\t\"    <CTRY>USA</CTRY>\\n\" + \"  </ADR>\\n\" + \"  <ADR>\\n\" + \"    <HOME/>\\n\" + \"    <EXTADD/>\\n\" +\n\t\t\t\"    <STREET/>\\n\" + \"    <LOCALITY>Parker</LOCALITY>\\n\" + \"    <REGION>CO</REGION>\\n\" +\n\t\t\t\"    <PCODE>80138</PCODE>\\n\" + \"    <CTRY>USA</CTRY>\\n\" + \"  </ADR>\\n\" +\n\t\t\t\"  <TEL><WORK/><VOICE/><NUMBER>+1-303-308-3282</NUMBER><PREF/></TEL>\\n\" +\n\t\t\t\"  <TEL><WORK/><FAX/><NUMBER>+1-303-308-3219</NUMBER></TEL>\\n\" +\n\t\t\t\"  <TEL><CELL/><VOICE/><TEXT/><NUMBER>+1-720-256-6756</NUMBER></TEL>\\n\" +\n\t\t\t\"  <TEL><HOME/><VOICE/><NUMBER>+1-303-555-1212</NUMBER></TEL>\\n\" +\n\t\t\t\"  <EMAIL><INTERNET/><USERID>stpeter@jabber.org</USERID></EMAIL>\\n\" +\n\t\t\t\"  <EMAIL><WORK/><USERID>psaintan@cisco.com</USERID></EMAIL>\\n\" +\n\t\t\t\"  <JABBERID>stpeter@jabber.org</JABBERID>\\n\" + \"  <TZ>America/Denver</TZ>\\n\" +\n\t\t\t\"  <GEO><LAT>39.59</LAT><LON>-105.01</LON></GEO>\\n\" + \"  <TITLE>Executive Director</TITLE>\\n\" +\n\t\t\t\"  <ROLE>Patron Saint</ROLE>\\n\" + \"  <LOGO>\\n\" + \"    <TYPE/>\\n\" + \"    <BINVAL/>\\n\" + \"  </LOGO>\\n\" +\n\t\t\t\"  <ORG>\\n\" + \"    <ORGNAME>XMPP Standards Foundation</ORGNAME>\\n\" + \"    <ORGUNIT/>\\n\" + \"  </ORG>\\n\" +\n\t\t\t\"  <URL>https://stpeter.im/</URL>\\n\" + \"  <URL>http://www.saint-andre.com/</URL>\\n\" + \"  <KEY>\\n\" +\n\t\t\t\"    <CRED>\\n\" + \"-----BEGIN PGP PUBLIC KEY BLOCK-----\\n\" + \"Version: GnuPG/MacGPG2 v2.0.18 (Darwin)\\n\" +\n\t\t\t\"\\n\" + \"mQINBFETDzsBEAC0FOv1N3ZJzIIxN6cKD475KVS9CHDPeYpegcOIPnL5eY1DCHeh\\n\" +\n\t\t\t\"/IwS1S7RCePtmiybNoV9FsI4PKUknzXQxA6LVEdAR/LUlhgJKjq+gsgp8lqbEILh\\n\" +\n\t\t\t\"g13ecH66HwLS9rarbQkC47T7kL8miIPBFC6E3A4Lq1L+eueO6UcLhKgoYkMxOjdi\\n\" +\n\t\t\t\"WrMgKTnVpch5ydLkPm/z0Zo8zRgqlPuTLeCrXXZYnjHXLVFN2xy04UzOs7P5u5KV\\n\" +\n\t\t\t\"fx5Z7uQisr8pXtyLd6SpTZo6SHgKBv15uz0rqXhsJojiGtOXfWznAjaS5FUOORq9\\n\" +\n\t\t\t\"CklG5cMOUAT8TNftv0ktsxaWDL1ELDVQPy1m7mtzo+VREG+0xmU6AjMo/GHblW1U\\n\" +\n\t\t\t\"U7MI9yCiuMLsp/HLrFuiosqLVZ85wuLQ2junPe3tK8h15UcxIXAcpQ1VqIaDQFbe\\n\" +\n\t\t\t\"uLOXJTF8YHpHdpHYt/ZM1ll7ZBKGAo8yd7uF7wJ9D3gUazwdz9fFjWV7oIk7ATwO\\n\" +\n\t\t\t\"lFllzmWDn+M2ygbHOGUGMX5hSaa8eDSieiR2QoLdn27Fip7kMBTJ2+GISrfnJTN/\\n\" +\n\t\t\t\"OQvmj0DXXAdxHmu2C4QgmZbkge35n129yzXn9NcqzrGLroV62lL3LgX6cSbiH5i7\\n\" +\n\t\t\t\"GgWY6CAPb1pMogV0K475n9FvOSDRiG4QSO5yqKiA3OP5aKrIRp2TNAk4IwARAQAB\\n\" +\n\t\t\t\"tCZQZXRlciBTYWludC1BbmRyZSA8c3RwZXRlckBzdHBldGVyLmltPokCOQQTAQIA\\n\" +\n\t\t\t\"IwUCURMPOwIbAwcLCQgHAwIBBhUIAgkKCwQWAgMBAh4BAheAAAoJEOoGpJErxa2p\\n\" +\n\t\t\t\"6bgQAKpxu07cMDOLc4+EG8H19NWXIVVybOEvfGuHYZaLKkPrhrMZwJiOwBpyISNR\\n\" +\n\t\t\t\"t9qzX1eLCVaojaoEVX6kD8MGc5zKFfiJZy3j7lBWl+Ybr7FfXYy2BbAXKx49e1n6\\n\" +\n\t\t\t\"ci9LmBrmVfAEaxtDNPITZ9N9oUAb9vS0nrG036EwteEHAveQvlDjO7lhz6+Cv7lZ\\n\" +\n\t\t\t\"QgBj9rZ6khfcQ4S3nSCQaKLQ9Iav4fqxI7SfuPKnx6quHX3JNLGnVo3wl+j/foCK\\n\" +\n\t\t\t\"0iTrmtHxCI3kc/bx6g32pRjHEPX0ALMBhmzU2uca+TE0zCEC96mgYXAUCwdnCFWy\\n\" +\n\t\t\t\"beIEbt6pz65iML13kAVAq0H/GqncnMGN0MbOatnw1Tdz/vkLojIy7QbPcQ0plUFx\\n\" +\n\t\t\t\"v5491xPfIrHhOWdRXp6WUt88fcqhT6MHZpVRtusj2ornKVVn+Y0GLsMMCTcrXJRG\\n\" +\n\t\t\t\"7Ao1YV72t/pJpzfGWSaaxolxDIZ6B+76jrIhUhiWgo/4nf+DN6BIlCZQ6j6xxjjx\\n\" +\n\t\t\t\"462cu02kuhIILTk2pzaMOufTBWx0uJhZk/KP2Fay/41pX7pvVOwRC4uIlKsLnJKL\\n\" +\n\t\t\t\"PS7EDa4BUUxENfd/9LqOGwlII8BbSe98PLMI8sXkcigc3UXMVda9ll0YhQa+lbP1\\n\" +\n\t\t\t\"NaszmnBhwuiCsgnPGbImsJuRzgEEgckwP/dNeyr6MlFMyfaeuQINBFETDzsBEADB\\n\" +\n\t\t\t\"zOsEHpUmhkRUjH9Tek87dn5P/Yh/L/HptgCGk40TL/C+kYdkd3HyteMEf061PNms\\n\" +\n\t\t\t\"S/Rq8k37Fu3VODYb9SPYKxtgksKSYUtIkPKvao09K9QNWPqyWuNf0F+iAjVMUuda\\n\" +\n\t\t\t\"EVFJ7bHF310RDwLY5IvLeCXxtvG+Vv/i+g77d2WdPDp+zLJ8306C4yBKjSJV8xW0\\n\" +\n\t\t\t\"cn2fd7NviIEN6cNHTsZNDZVMlgYPrxnwSq8GTEPGC7HsLIwGcx3hIe9QjnPw9CpA\\n\" +\n\t\t\t\"mQENpDEyWcxgF5uwo2NJECoDswKz1Nb0gfawF3ZIbD+GcLujTu94iJuVg25jATWm\\n\" +\n\t\t\t\"9wTgcfZo4UPllRGXdIb8uWwUFQlLQgd4ROLZZtXNGmHIymJrV2crx53gxup+1j0X\\n\" +\n\t\t\t\"qhlzKg8xbImWhEfS9oHZkRK8VHgmWSIt7TNwNir6N5j3lqwWVBhnu6GzF01sKGNy\\n\" +\n\t\t\t\"SlqNRbd0fqhakCkK71b8ot8tYTcYG5Lg10z6HTbgQx2UwLthUjqbblDQ+GLmrOhi\\n\" +\n\t\t\t\"WklLXRsnlnPMwnEyFePAnsT5tasy2Cn9qjpttNDah7PB8iFUi9mtTF/XDVgpFaB5\\n\" +\n\t\t\t\"G3CDV7Q2NgbAI6g6QhLIAmXzSP635G83mda0TKXHQXHDyLJTTn+WVFU7t4m4uLt+\\n\" +\n\t\t\t\"0DsWU8jXHQWyUTNG9WPUrXhusDUAPHxFCQ/n/lQVBwARAQABiQIfBBgBAgAJBQJR\\n\" +\n\t\t\t\"Ew87AhsMAAoJEOoGpJErxa2pqfgP/ApN+TRu2bBIgaw1dr3AznSSha84DIpXUDh3\\n\" +\n\t\t\t\"udZvQrGbUtz8/mA+e3iZEN/cmmBw2LGlAuQoJNILTZQ318yTP+E5QU7fJH7FVsoh\\n\" +\n\t\t\t\"UyvrMfyt3IMA9jg0Z9MuloLezvIjjMfFeNa0ROgDb/ubOT7JQzi1kwN8Lu3lO80H\\n\" +\n\t\t\t\"wqBHXEeOLoislUSnZajRKvITbKWkZ6PHRjlMw1Wk4oIi6VLHgGgj79zzL3uhML26\\n\" +\n\t\t\t\"63m7imShvz1QcHTwvyR5i8cZbNOEkotZyERiA1p7YHuruS+QvTi3ZPoQbnMUB3a7\\n\" +\n\t\t\t\"py9d11bw1+w3LiAUGZE/z5hBWOFxYtw+w/U/Vx0BwJGYlwU3M2W20uEXe+qxz7wn\\n\" +\n\t\t\t\"akygKjmLiD2z4njfKjcNCiV3FmXrpmWgADln1c4jfxDh0NrndrsM8FPDf1TMPtOZ\\n\" +\n\t\t\t\"gFDkKripc9xkZ/25P6xn27oTOHWKcAC0QhxSH+HuVBBRk8AgF+zAbDZe4/L6+kan\\n\" +\n\t\t\t\"SrycIXW+wCzwBq61aWsz2QhhuKjozVkhk4dRG+CfjzAFjnyxwYERn3uXVKQAwTwc\\n\" +\n\t\t\t\"dNcTI9RV98IsNrw9Y4lJEAg6CjNPmiD5+EASycqaOuToRSGukr8sOQLWLPyTnez/\\n\" +\n\t\t\t\"aG8Xf7a+fntWzK2HuDYoSDhJJrylWw/lMklOBm4wtMeNA0zcQH6AQV/GzQVQkSGq\\n\" + \"rLuMVIV/\\n\" + \"=llGw\\n\" +\n\t\t\t\"-----END PGP PUBLIC KEY BLOCK-----\\n\" + \"    </CRED>\\n\" + \"  </KEY>\\n\" + \"  <DESC>\\n\" +\n\t\t\t\"    More information about me is located on my \\n\" + \"    personal website: https://stpeter.im/\\n\" +\n\t\t\t\"  </DESC>\\n\" + \"</vCard>\").replace(\"\\n\", \"\").replace(\"  \", \"\");\n\tprotected SimpleParser parser = SingletonFactory.getParserInstance();\n\n\t@Test\n\tpublic void testVCard4ToVCardTemp() {\n\t\tchar[] data = VCARD4_DATA.toCharArray();\n\t\tDomBuilderHandler handler = new DomBuilderHandler();\n\t\tparser.parse(handler, data, 0, data.length);\n\t\tElement vcard4 = handler.getParsedElements().poll();\n\t\tdata = VCARD2_TEMP_DATA.toCharArray();\n\t\tparser.parse(handler, data, 0, data.length);\n\t\tElement expResult = handler.getParsedElements().poll();\n\n\t\tElement vcardTemp = VCardXMPPProcessorAbstract.convertVCard4ToVCardTemp(vcard4);\n\t\tassertNotEquals(vcard4, vcardTemp);\n\t\tassertEquals(expResult.toString(), vcardTemp.toString());\n\t}\n\n\t@Test\n\tpublic void testVCardTempNoLogoToVCard4() {\n\t\tchar[] data = VCARD_TEMP_NOLOGO_DATA.toCharArray();\n\t\tDomBuilderHandler handler = new DomBuilderHandler();\n\t\tparser.parse(handler, data, 0, data.length);\n\t\tElement vcardTemp = handler.getParsedElements().poll();\n\t\tdata = VCARD4_NOLOGO_DATA.toCharArray();\n\t\tparser.parse(handler, data, 0, data.length);\n\t\tElement expResult = handler.getParsedElements().poll();\n\n\t\tElement vcard4 = VCardXMPPProcessorAbstract.convertVCardTempToVCard4(vcardTemp);\n\t\tassertNotEquals(vcardTemp, vcard4);\n\t\tassertEquals(expResult.toString(), vcard4.toString());\n\t}\n\n\t@Test\n\tpublic void testVCardTempToVCard4() {\n\t\tchar[] data = VCARD_TEMP_DATA.toCharArray();\n\t\tDomBuilderHandler handler = new DomBuilderHandler();\n\t\tparser.parse(handler, data, 0, data.length);\n\t\tElement vcardTemp = handler.getParsedElements().poll();\n\t\tdata = VCARD4_DATA.toCharArray();\n\t\tparser.parse(handler, data, 0, data.length);\n\t\tElement expResult = handler.getParsedElements().poll();\n\n\t\tElement vcard4 = VCardXMPPProcessorAbstract.convertVCardTempToVCard4(vcardTemp);\n\t\tassertNotEquals(vcardTemp, vcard4);\n\t\tassertEquals(expResult.toString(), vcard4.toString());\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/annotation/AnnotatedXMPPProcessorTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.annotation;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.impl.SessionBind;\n\n/**\n * @author andrzej\n */\npublic class AnnotatedXMPPProcessorTest {\n\n\t@Test\n\tpublic void test1() {\n\t\tTestAnnotatedXMPPProcessor test1 = new TestAnnotatedXMPPProcessor();\n\t\tfor (String[] path : test1.supElementNamePaths()) {\n\t\t\tAssert.assertArrayEquals(\"Wrong element paths\", new String[]{\"iq\", \"query\"}, path);\n\t\t}\n\t\tAssert.assertArrayEquals(\"Wrong xmlnss\", new String[]{\"tigase:test1\", \"tigase:test2\"}, test1.supNamespaces());\n\n\t\tAssert.assertArrayEquals(\"Wrong disco features\", new Element[]{\n\t\t\t\t\t\t\t\t\t\t new Element(\"feature\", new String[]{\"var\"}, new String[]{\"tigase:test1\"}),\n\t\t\t\t\t\t\t\t\t\t new Element(\"feature\", new String[]{\"var\"}, new String[]{\"tigase:test2\"})},\n\t\t\t\t\t\t\t\t test1.supDiscoFeatures(null));\n\t\tAssert.assertArrayEquals(\"Wrong stream features\", new Element[]{\n\t\t\t\t\t\t\t\t\t\t new Element(\"bind\", new String[]{\"xmlns\"}, new String[]{\"urn:ietf:params:xml:ns:xmpp-bind\"})},\n\t\t\t\t\t\t\t\t test1.supStreamFeatures(null));\n\t\tAssert.assertTrue(\"Stanza type not set as 'get'\", test1.supTypes().contains(StanzaType.get));\n\t}\n\n\t@Test\n\tpublic void test2() {\n\t\tTestAnnotatedXMPPProcessor2 test2 = new TestAnnotatedXMPPProcessor2();\n\t\tAssert.assertNull(\"Wrong element paths, should not be inherited\", test2.supElementNamePaths());\n\n\t\tAssert.assertNull(\"Wrong xmlnss\", test2.supNamespaces());\n\n\t\tAssert.assertNull(\"Wrong disco features\", test2.supDiscoFeatures(null));\n\t\tAssert.assertNull(\"Wrong stream features\", test2.supStreamFeatures(null));\n\t}\n\n\t@Test\n\tpublic void testSessionBind() throws NotAuthorizedException, TigaseStringprepException {\n\t\tSessionBind sessionBind = new SessionBind();\n\t\tAssert.assertEquals(\"Wrong processor id\", \"urn:ietf:params:xml:ns:xmpp-session\", sessionBind.id());\n\t\tfor (String[] path : sessionBind.supElementNamePaths()) {\n\t\t\tAssert.assertArrayEquals(\"Wrong element paths\", new String[]{\"iq\", \"session\"}, path);\n\t\t}\n\t\tAssert.assertArrayEquals(\"Wrong xmlnss\", new String[]{\"urn:ietf:params:xml:ns:xmpp-session\"},\n\t\t\t\t\t\t\t\t sessionBind.supNamespaces());\n\n\t\tAssert.assertArrayEquals(\"Wrong disco features\", new Element[]{\n\t\t\t\t\t\t\t\t\t\t new Element(\"feature\", new String[]{\"var\"}, new String[]{\"urn:ietf:params:xml:ns:xmpp-session\"})},\n\t\t\t\t\t\t\t\t sessionBind.supDiscoFeatures(null));\n\n\t\tfinal XMPPResourceConnection session = getSession();\n\n\t\tElement xmppSession = new Element(\"session\");\n\t\txmppSession.setXMLNS(\"urn:ietf:params:xml:ns:xmpp-session\");\n\t\txmppSession.addChild(new Element(\"optional\"));\n\n\t\tAssert.assertArrayEquals(\"Lack optional child\", new Element[]{xmppSession},\n\t\t\t\t\t\t\t\t sessionBind.supStreamFeatures(session));\n\t\tsession.putSessionData(\"Session-Set\", \"true\");\n\t\tAssert.assertArrayEquals(\"Lack optional child\", null, sessionBind.supStreamFeatures(session));\n\t}\n\n\tprivate XMPPResourceConnection getSession() throws NotAuthorizedException, TigaseStringprepException {\n\t\tXMPPResourceConnection conn = new XMPPResourceConnection(null, null, null, null) {\n\t\t\t@Override\n\t\t\tpublic boolean isAuthorized() {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t};\n\t\treturn conn;\n\t}\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/annotation/TestAnnotatedXMPPProcessor.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.annotation;\n\nimport tigase.xmpp.StanzaType;\n\nimport static tigase.xmpp.impl.annotation.TestAnnotatedXMPPProcessor.*;\n\n/**\n * @author andrzej\n */\n@Id(ID)\n@Handles({@Handle(path = {\"iq\", \"query\"}, xmlns = XMLNS1), @Handle(pathStr = IQ_QUERY_PATH, xmlns = XMLNS2)})\n@DiscoFeatures({XMLNS1, XMLNS2})\n@StreamFeatures({@StreamFeature(elem = \"bind\", xmlns = \"urn:ietf:params:xml:ns:xmpp-bind\")})\n@HandleStanzaTypes({StanzaType.get})\nclass TestAnnotatedXMPPProcessor\n\t\textends AnnotatedXMPPProcessor {\n\n\tprotected static final String ID = \"test-123\";\n\tprotected static final String XMLNS1 = \"tigase:test1\";\n\tprotected static final String XMLNS2 = \"tigase:test2\";\n\tprotected static final String IQ_QUERY_PATH = \"iq/query\";\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/annotation/TestAnnotatedXMPPProcessor2.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.annotation;\n\n/**\n * @author andrzej\n */\nclass TestAnnotatedXMPPProcessor2\n\t\textends TestAnnotatedXMPPProcessor {\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/push/EncryptedPushNotificationExtensionTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.push;\n\nimport org.junit.Test;\n\nimport java.nio.charset.StandardCharsets;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\npublic class EncryptedPushNotificationExtensionTest {\n\n\t// for testing message truncation\n\t@Test\n\tpublic void testMessageBodyTruncation() {\n\t\tint maxSize = 3000;\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (int i=0; i<1000; i++) {\n\t\t\tsb.append(\"\\uD83D\\uDE21\");\n\t\t}\n\t\tfor (int i=0; i<(8000-64); i++) {\n\t\t\tif (i % 10 ==0) {\n\t\t\t\tsb.append(\"\\uD83D\\uDE21\");\n\t\t\t} else {\n\t\t\t\tsb.append(String.valueOf(i % 10));\n\t\t\t}\n\t\t}\n\t\tString origBody = sb.toString();\n\t\tString body = EncryptedPushNotificationExtension.trimBodyToSize(maxSize, origBody);\n\t\tassertTrue(body.getBytes(StandardCharsets.UTF_8).length <= 3000);\n\t\tassertTrue(origBody.contains(body));\n\t}\n\n\t@Test\n\tpublic void testMessageBodyTruncation1() {\n\t\trunCharTest(1, \"\\u0800\", 2999);\n\t}\n\n\t@Test\n\tpublic void testMessageBodyTruncation2() {\n\t\trunCharTest(2, \"\\uD83D\\uDC75\\uD83C\\uDFFB\", 2999);\n\t}\n\n\t@Test\n\tpublic void testMessageBodyTruncation3() {\n\t\trunCharTest(3, \"\\uD83D\\uDE21\", 2999);\n\t}\n\n\t@Test\n\tpublic void testMessageBodyTruncation4() {\n\t\trunCharTest(4, \"\\u07DF\", 2999);\n\t}\n\n\t@Test\n\tpublic void testMessageBodyTruncation5() {\n\t\trunCharTest(5, \"\\uFFFD\", 2999);\n\t}\n\n\t@Test\n\tpublic void testJsonEncoding() {\n\t\tString body = \"To be, \\\\ or \\\"not\\\" to \\b\\t\\r be:\\n that is / the question\";\n\t\tStringBuilder sb = new StringBuilder();\n\t\tEncryptedPushNotificationExtension.valueToString(body, sb);\n\t\tassertEquals(\"\\\"To be, \\\\\\\\ or \\\\\\\"not\\\\\\\" to \\\\b\\\\t\\\\r be:\\\\n that is \\\\/ the question\\\"\", sb.toString());\n\t}\n\t\n\tprivate void runCharTest(int tryNo, String ch, int maxSize) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (int i=0; i<5000; i++) {\n\t\t\tsb.append(ch);\n\t\t}\n\t\tString origBody = sb.toString();\n\t\tString body = EncryptedPushNotificationExtension.trimBodyToSize(maxSize, origBody);\n\t\t// DO NOT REMOVE: left intentionally for better analysis of the issue with a code when needed\n\t\t//System.out.println(\"\" + tryNo + \": maxSize: \" + maxSize + \", size: \" + body.getBytes(StandardCharsets.UTF_8).length + \", char size:\" + ch.toCharArray().length + \":\" + ch.getBytes(StandardCharsets.UTF_8).length);\n\t\tassertTrue(body.getBytes(StandardCharsets.UTF_8).length <= 3000);\n\t\tassertTrue(origBody.contains(body));\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/push/PushNotificationHelper.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.push;\n\nimport tigase.server.Packet;\nimport tigase.xml.Element;\nimport tigase.xmpp.jid.JID;\n\n/**\n * Created by andrzej on 03.01.2017.\n */\npublic class PushNotificationHelper {\n\n\tpublic static Element createPlainNotification(long messageCount, JID lastMessageSender, String lastMessageBody) {\n\t\treturn new Element(\"notification\",\n\t\t\t\t\t\t   new Element[]{createNotificationForm(messageCount, lastMessageSender, lastMessageBody)},\n\t\t\t\t\t\t   new String[]{\"xmlns\"}, new String[]{\"urn:xmpp:push:0\"});\n\t}\n\n\tpublic static Element createNotificationForm(long messageCount, JID lastMessageSender, String lastMessageBody) {\n\t\tElement[] fields = new Element[]{\n\t\t\t\tnew Element(\"field\", new Element[]{new Element(\"value\", \"urn:xmpp:push:summary\")}, new String[]{\"var\"},\n\t\t\t\t\t\t\tnew String[]{\"FORM_TYPE\"}),\n\t\t\t\tnew Element(\"field\", new Element[]{new Element(\"value\", String.valueOf(messageCount))},\n\t\t\t\t\t\t\tnew String[]{\"var\"}, new String[]{\"message-count\"}),\n\t\t\t\tnew Element(\"field\", new Element[]{new Element(\"value\", lastMessageSender.toString())},\n\t\t\t\t\t\t\tnew String[]{\"var\"}, new String[]{\"last-message-sender\"}),\n\t\t\t\tnew Element(\"field\", new Element[]{new Element(\"value\", lastMessageBody)}, new String[]{\"var\"},\n\t\t\t\t\t\t\tnew String[]{\"last-message-body\"})};\n\t\treturn new Element(\"x\", fields, new String[]{\"xmlns\"}, new String[]{\"jabber:x:data\"});\n\t}\n\n\tpublic static Packet createPushNotification(JID serviceJid, JID userJid, String node, Element notification) {\n\t\tElement iq = new Element(\"iq\", new Element[]{new Element(\"pubsub\", new Element[]{\n\t\t\t\tnew Element(\"publish\", new Element[]{new Element(\"item\", new Element[]{notification}, null, null)},\n\t\t\t\t\t\t\tnew String[]{\"node\"}, new String[]{node})}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t new String[]{\"http://jabber.org/protocol/pubsub\"})},\n\t\t\t\t\t\t\t\t new String[]{\"type\"}, new String[]{\"set\"});\n\t\treturn Packet.packetInstance(iq, JID.jidInstanceNS(userJid.getDomain()), serviceJid);\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/push/PushNotificationsTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.push;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.db.*;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.eventbus.impl.EventBusImplementation;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Message;\nimport tigase.server.Packet;\nimport tigase.server.PolicyViolationException;\nimport tigase.server.amp.db.MsgRepository;\nimport tigase.util.Base64;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.MessageDeliveryLogic;\nimport tigase.xmpp.impl.OfflineMessages;\nimport tigase.xmpp.impl.ProcessorTestCase;\nimport tigase.xmpp.impl.roster.RosterAbstract;\nimport tigase.xmpp.impl.roster.RosterFactory;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport javax.crypto.BadPaddingException;\nimport javax.crypto.Cipher;\nimport javax.crypto.IllegalBlockSizeException;\nimport javax.crypto.NoSuchPaddingException;\nimport javax.crypto.spec.GCMParameterSpec;\nimport javax.crypto.spec.SecretKeySpec;\nimport java.lang.reflect.Field;\nimport java.nio.charset.StandardCharsets;\nimport java.security.InvalidAlgorithmParameterException;\nimport java.security.InvalidKeyException;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.SecureRandom;\nimport java.util.*;\nimport java.util.function.Consumer;\nimport java.util.stream.Collectors;\n\nimport static org.junit.Assert.*;\nimport static tigase.Assert.assertElementEquals;\n\n/**\n * Created by andrzej on 03.01.2017.\n */\npublic class PushNotificationsTest\n\t\textends ProcessorTestCase {\n\n\tprivate MsgRepository msgRepository;\n\tprivate PushNotifications pushNotifications;\n\tprivate JID pushServiceJid = JID.jidInstanceNS(\"push.example.com\");\n\tprivate JID recipientJid;\n\tprivate JID senderJid;\n\n\t@Before\n\t@Override\n\tpublic void setUp() throws Exception {\n\t\trecipientJid = JID.jidInstanceNS(\"recipient-\" + UUID.randomUUID() + \"@example.com/res-1\");\n\t\tsenderJid = JID.jidInstanceNS(\"sender-\" + UUID.randomUUID() + \"@example.com/res-1\");\n\t\tsuper.setUp();\n\t\tregisterLocalBeans(getKernel());\n\t\tpushNotifications = getInstance(PushNotifications.class);\n\t\tmsgRepository = getInstance(MsgRepository.class);\n\t\tField f = RosterFactory.class.getDeclaredField(\"shared\");\n\t\tf.setAccessible(true);\n\t\tf.set(null, RosterFactory.newRosterInstance(RosterFactory.ROSTER_IMPL_PROP_VAL));\n\t}\n\n\t@After\n\t@Override\n\tpublic void tearDown() throws Exception {\n\t\tsuper.tearDown();\n\t}\n\n\t@Test\n\tpublic void test_initialState() throws Exception {\n\t\tString msgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tElement msg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tPacket packet = Packet.packetInstance(msg, senderJid, recipientJid);\n\n\t\tQueue<SessionManagerHandlerImpl.Item> results = getInstance(SessionManagerHandlerImpl.class).getOutQueue();\n\t\tpushNotifications.notifyNewOfflineMessage(packet, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(0, results.size());\n\t}\n\n\t@Test\n\tpublic void test_enable() throws Exception {\n\t\tXMPPResourceConnection session = getSession(\n\t\t\t\tJID.jidInstanceNS(\"c2s@example.com/\" + UUID.randomUUID().toString()), recipientJid);\n\n\t\tenable(session,null);\n\t}\n\t\n\tprotected void enable(XMPPResourceConnection session, Consumer<Element> consumer) throws Exception {\n\t\tElement enableEl = new Element(\"enable\", new String[]{\"xmlns\", \"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t   new String[]{\"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tpushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"push-node\"});\n\t\tif (consumer != null) {\n\t\t\tconsumer.accept(enableEl);\n\t\t}\n\t\tElement iqEl = new Element(\"iq\", new Element[]{ enableEl },\n\t\t\t\t\t\t\t\t   new String[]{\"type\", \"id\"}, new String[]{\"set\", UUID.randomUUID().toString()});\n\n\n\t\tPacket iq = Packet.packetInstance(iqEl);\n\t\tiq.setPacketFrom(session.getConnectionId());\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tpushNotifications.process(iq, session, null, results, new HashMap<>());\n\t\tassertEquals(1, results.size());\n\t\tPacket result = results.poll();\n\t\tassertEquals(\"wrong result = \" + result, StanzaType.result, result.getType());\n\n\t\tassertNotNull(getInstance(UserRepository.class).getData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpushServiceJid + \"/push-node\"));\n\t\tMap<String, Element> settings = pushNotifications.getPushServices(recipientJid.getBareJID());\n\t\tassertNotNull(settings);\n\t\tassertEquals(1, settings.size());\n\t\tassertElementEquals(new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t\tnew String[]{pushServiceJid.toString(), \"push-node\"}),\n\t\t\t\t\t\t\tsettings.get(pushServiceJid + \"/push-node\"));\n\n\t\tsettings = pushNotifications.getPushServices(session);\n\t\tassertNotNull(settings);\n\t\tassertEquals(1, settings.size());\n\t\tassertElementEquals(new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t\tnew String[]{pushServiceJid.toString(), \"push-node\"}),\n\t\t\t\t\t\t\tsettings.get(pushServiceJid + \"/push-node\"));\n\t}\n\n\t@Test\n\tpublic void test_disable() throws Exception {\n\t\tElement iqEl = new Element(\"iq\", new Element[]{new Element(\"disable\", new String[]{\"xmlns\", \"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{\"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"push-node\"})},\n\t\t\t\t\t\t\t\t   new String[]{\"type\", \"id\"}, new String[]{\"set\", UUID.randomUUID().toString()});\n\n\t\tXMPPResourceConnection session = getSession(\n\t\t\t\tJID.jidInstanceNS(\"c2s@example.com/\" + UUID.randomUUID().toString()), recipientJid);\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t  new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"push-node\"}).toString());\n\n\t\tPacket iq = Packet.packetInstance(iqEl);\n\t\tiq.setPacketFrom(session.getConnectionId());\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tpushNotifications.process(iq, session, null, results, new HashMap<>());\n\t\tassertEquals(1, results.size());\n\t\tPacket result = results.poll();\n\t\tassertEquals(\"wrong result = \" + result, StanzaType.result, result.getType());\n\n\t\tassertNull(getInstance(UserRepository.class).getData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t pushServiceJid + \"/push-node\"));\n\n\n\t\tMap<String, Element> settings = pushNotifications.getPushServices(recipientJid.getBareJID());\n\t\tassertNotNull(settings);\n\t\tassertEquals(0, settings.size());\n\t\t\n\t\tsettings = pushNotifications.getPushServices(session);\n\t\tassertNotNull(settings);\n\t\tassertEquals(0, settings.size());\n\t}\n\n\t@Test\n\tpublic void test_notificationGeneration() throws Exception {\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t  new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"push-node\"}).toString());\n\n\t\tString msgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tElement msg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tPacket packet = Packet.packetInstance(msg, senderJid, recipientJid);\n\n\t\tmsgRepository.storeMessage(senderJid, recipientJid, new Date(), packet.getElement(), null);\n\n\t\tQueue<SessionManagerHandlerImpl.Item> results = getInstance(SessionManagerHandlerImpl.class).getOutQueue();\n\t\tpushNotifications.notifyNewOfflineMessage(packet, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(1, results.size());\n\n\t\tPacket expNotification = PushNotificationHelper.createPushNotification(pushServiceJid, recipientJid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   PushNotificationHelper.createPlainNotification(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   1, senderJid, msgBody));\n\n\t\tassertElementEquals(expNotification.getElement(), results.poll().packet.getElement());\n\n\t\tmsgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tmsg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tpacket = Packet.packetInstance(msg, senderJid, recipientJid);\n\n\t\tmsgRepository.storeMessage(senderJid, recipientJid, new Date(), packet.getElement(), null);\n\n\t\tresults.clear();\n\t\tpushNotifications.notifyNewOfflineMessage(packet, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(1, results.size());\n\n\t\texpNotification = PushNotificationHelper.createPushNotification(pushServiceJid, recipientJid, \"push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tPushNotificationHelper.createPlainNotification(2,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  senderJid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  msgBody));\n\n\t\tassertElementEquals(expNotification.getElement(), results.poll().packet.getElement());\n\t}\n\n\t@Test\n\tpublic void test_notificationGenerationForOMEMO() throws Exception {\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t  new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"push-node\"}).toString());\n\n\t\tString msgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tElement msg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tmsg.addChild(new Element(\"encrypted\", new String[]{\"xmlns\"}, new String[]{\"eu.siacs.conversations.axolotl\"}));\n\t\tPacket packet = Packet.packetInstance(msg, senderJid, recipientJid);\n\n\t\tmsgRepository.storeMessage(senderJid, recipientJid, new Date(), packet.getElement(), null);\n\n\t\tQueue<SessionManagerHandlerImpl.Item> results = getInstance(SessionManagerHandlerImpl.class).getOutQueue();\n\t\tpushNotifications.notifyNewOfflineMessage(packet, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(1, results.size());\n\n\t\tPacket expNotification = PushNotificationHelper.createPushNotification(pushServiceJid, recipientJid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   PushNotificationHelper.createPlainNotification(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   1, senderJid, msgBody));\n\n\t\tassertElementEquals(expNotification.getElement(), results.poll().packet.getElement());\n\n\t\tmsgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tmsg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tpacket = Packet.packetInstance(msg, senderJid, recipientJid);\n\n\t\tmsgRepository.storeMessage(senderJid, recipientJid, new Date(), packet.getElement(), null);\n\n\t\tresults.clear();\n\t\tpushNotifications.notifyNewOfflineMessage(packet, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(1, results.size());\n\n\t\texpNotification = PushNotificationHelper.createPushNotification(pushServiceJid, recipientJid, \"push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tPushNotificationHelper.createPlainNotification(2,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   senderJid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"New secure message. Open to see the message.\"));\n\n\t\tassertElementEquals(expNotification.getElement(), results.poll().packet.getElement());\n\t}\n\n\t@Test\n\tpublic void test_notificationGenerationForMUCwhenOnlineOLD() throws Exception {\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t  new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"push-node\"}).toString());\n\n\t\tXMPPResourceConnection session = this.getSession(JID.jidInstanceNS(UUID.randomUUID().toString(), recipientJid.getDomain()), recipientJid);\n\n\t\tString msgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tElement msg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tPacket packet = Packet.packetInstance(msg, senderJid, recipientJid.copyWithoutResource());\n\n\t\tQueue<SessionManagerHandlerImpl.Item> results = getInstance(SessionManagerHandlerImpl.class).getOutQueue();\n\t\tpushNotifications.process(packet, session, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(0, results.size());\n\n\t\tElement presence = new Element(\"presence\");\n\t\tsession.setPresence(presence);\n\n\t\tmsgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tmsg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tpacket = Packet.packetInstance(msg, senderJid, recipientJid.copyWithoutResource());\n\n\t\tresults.clear();\n\t\tpushNotifications.process(packet, session, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(0, results.size());\n\n\t\tmsgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tmsg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\", \"type\"},\n\t\t\t\t\t\t  new String[]{\"jabber:client\", \"groupchat\"});\n\t\tpacket = Packet.packetInstance(msg, senderJid, recipientJid.copyWithoutResource());\n\n\t\tresults.clear();\n\t\tpushNotifications.process(packet, session, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(1, results.size());\n\t}\n\n\t@Test\n\tpublic void test_notificationGenerationForMUCwhenOnline() throws Exception {\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t  new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"push-node\"}).toString());\n\n\t\tXMPPResourceConnection session = this.getSession(JID.jidInstanceNS(UUID.randomUUID().toString(), recipientJid.getDomain()), recipientJid);\n\n\t\tString msgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tElement msg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tPacket packet = Packet.packetInstance(msg, senderJid, recipientJid.copyWithoutResource());\n\n\t\tQueue<SessionManagerHandlerImpl.Item> results = getInstance(SessionManagerHandlerImpl.class).getOutQueue();\n\t\tpushNotifications.process(packet, session, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(0, results.size());\n\n\t\tElement presence = new Element(\"presence\");\n\t\tsession.setPresence(presence);\n\n\t\tmsgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tmsg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tpacket = Packet.packetInstance(msg, senderJid, recipientJid.copyWithoutResource());\n\n\t\tresults.clear();\n\t\tpushNotifications.process(packet, session, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(0, results.size());\n\n\t\tmsgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tmsg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\", \"type\"},\n\t\t\t\t\t\t  new String[]{\"jabber:client\", \"groupchat\"});\n\t\tpacket = Packet.packetInstance(msg, senderJid, recipientJid.copyWithoutResource());\n\n\t\tresults.clear();\n\t\tpushNotifications.process(packet, session, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(1, results.size());\n\n\t\tenable(session, enableEl -> {\n\t\t\tenableEl.withElement(\"groupchat\", \"tigase:push:filter:groupchat:0\", mucEl -> {\n\t\t\t\tmucEl.withElement(\"room\", roomEl -> {\n\t\t\t\t\troomEl.setAttribute(\"jid\", senderJid.getBareJID().toString());\n\t\t\t\t\troomEl.setAttribute(\"allow\", \"always\");\n\t\t\t\t});\n\t\t\t});\n\t\t});\n\n\t\tmsgBody = \"Message body \" + UUID.randomUUID().toString() + \" - my_nick\";\n\t\tmsg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\", \"type\"},\n\t\t\t\t\t\t  new String[]{\"jabber:client\", \"groupchat\"});\n\t\tpacket = Packet.packetInstance(msg, senderJid, recipientJid.copyWithoutResource());\n\n\t\tresults.clear();\n\t\tpushNotifications.process(packet, session, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(1, results.size());\n\n\t\tenable(session, enableEl -> {\n\t\t\tenableEl.withElement(\"groupchat\", \"tigase:push:filter:groupchat:0\", mucEl -> {\n\t\t\t\tmucEl.withElement(\"room\", roomEl -> {\n\t\t\t\t\troomEl.setAttribute(\"jid\", senderJid.getBareJID().toString());\n\t\t\t\t\troomEl.setAttribute(\"allow\", \"mentioned\");\n\t\t\t\t\troomEl.setAttribute(\"nick\", \"my_nick\");\n\t\t\t\t});\n\t\t\t});\n\t\t});\n\n\t\tmsgBody = \"Message body \" + UUID.randomUUID().toString() + \" - my_nick\";\n\t\tmsg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\", \"type\"},\n\t\t\t\t\t\t  new String[]{\"jabber:client\", \"groupchat\"});\n\t\tpacket = Packet.packetInstance(msg, senderJid, recipientJid.copyWithoutResource());\n\n\t\tresults.clear();\n\t\tpushNotifications.process(packet, session, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(1, results.size());\n\t}\n\n\t@Test\n\tpublic void testMutingNotifications() throws TigaseDBException {\n\t\tElement settings = new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t   new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"push-node\"});\n\t\tsettings.withElement(\"muted\", \"tigase:push:filter:muted:0\", mutedEl -> {\n\t\t\tmutedEl.withElement(\"item\", itemEl -> {\n\t\t\t\titemEl.setAttribute(\"jid\", senderJid.getBareJID().toString());\n\t\t\t});\n\t\t});\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t  settings.toString());\n\n\t\tString msgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tElement msg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tPacket packet = Packet.packetInstance(msg, senderJid, recipientJid);\n\n\t\tmsgRepository.storeMessage(senderJid, recipientJid, new Date(), packet.getElement(), null);\n\n\t\tQueue<SessionManagerHandlerImpl.Item> results = getInstance(SessionManagerHandlerImpl.class).getOutQueue();\n\t\tpushNotifications.notifyNewOfflineMessage(packet, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(0, results.size());\n\t}\n\n\t@Test\n\tpublic void testIgnoreUnknownNotifications()\n\t\t\tthrows TigaseDBException, NotAuthorizedException, TigaseStringprepException, PolicyViolationException {\n\t\tElement settings = new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t   new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"push-node\"});\n\t\tsettings.addAttribute(\"ignore-from-unknown\", \"true\");\n//\t\tsettings.withElement(\"ignore-unknown\", \"tigase:push:ignore-unknown:0\", (String) null);\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t  settings.toString());\n\n\t\tString msgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tElement msg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tPacket packet = Packet.packetInstance(msg, senderJid, recipientJid);\n\n\t\tmsgRepository.storeMessage(senderJid, recipientJid, new Date(), packet.getElement(), null);\n\n\t\tQueue<SessionManagerHandlerImpl.Item> results = getInstance(SessionManagerHandlerImpl.class).getOutQueue();\n\t\tpushNotifications.notifyNewOfflineMessage(packet, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(0, results.size());\n\n\t\tRosterAbstract roster = RosterFactory.getRosterImplementation(true);\n\t\tXMPPResourceConnection session = getSession(JID.jidInstanceNS(\"test@test/1\"), recipientJid);\n\t\troster.addBuddy(session, senderJid.copyWithoutResource(), \"Sender 1\", null, RosterAbstract.SubscriptionType.both, null);\n\n\t\tsession.logout();\n\n\t\tmsgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tmsg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tpacket = Packet.packetInstance(msg, senderJid, recipientJid);\n\n\t\tmsgRepository.storeMessage(senderJid, recipientJid, new Date(), packet.getElement(), null);\n\n\t\tresults.clear();\n\t\tpushNotifications.notifyNewOfflineMessage(packet, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(1, results.size());\n\t}\n\n\t@Test\n\tpublic void testJingleNotification() throws TigaseDBException {\n\t\tElement settings = new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t   new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"push-node\"});\n\t\tsettings.addChild(new Element(\"jingle\", new String[] {\"xmlns\", \"id\"}, new String[] {\"tigase:push:jingle:0\", \"test-1\"}));\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t  settings.toString());\n\n\t\tElement msg = new Element(\"message\", new Element[]{new Element(\"propose\", new String[]{\"xmlns\", \"id\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{\"urn:xmpp:jingle-message:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"test-1\"}).withElement(\n\t\t\t\t\"description\", \"urn:xmpp:jingle:apps:rtp:1\", x -> x.setAttribute(\"media\", \"audio\"))},\n\t\t\t\t\t\t\t\t  new String[]{\"xmlns\"}, new String[]{\"jabber:client\"});\n\t\tPacket packet = Packet.packetInstance(msg, senderJid, recipientJid);\n\n\t\tmsgRepository.storeMessage(senderJid, recipientJid, new Date(), packet.getElement(), null);\n\n\t\tQueue<SessionManagerHandlerImpl.Item> results = getInstance(SessionManagerHandlerImpl.class).getOutQueue();\n\t\tpushNotifications.notifyNewOfflineMessage(packet, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(1, results.size());\n\n\t\tElement notificationElem = results.remove().packet.getElement().findChild(new String[] { \"iq\", \"pubsub\", \"publish\", \"item\", \"notification\"});\n\t\tElement jingleEl = notificationElem.getChild(\"jingle\");\n\t\tassertNotNull(jingleEl);\n\t\tassertEquals(\"test-1\", jingleEl.getAttributeStaticStr(\"sid\"));\n\t\tassertEquals(\"audio\", jingleEl.getChildren().getFirst().getCData());\n\t}\n\n\t@Test\n\tpublic void testMeetNotification() throws TigaseDBException {\n\t\tElement settings = new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t   new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"push-node\"});\n\t\tsettings.addChild(new Element(\"meet\", new String[] {\"xmlns\", \"id\"}, new String[] {\"tigase:push:meet:0\", \"test-1\"}));\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t  settings.toString());\n\n\t\tString meetJid = JID.jidInstanceNS(\"test-meet-1\", \"meet.\" + senderJid.getDomain()).toString();\n\t\tElement msg = new Element(\"message\", new Element[]{new Element(\"propose\", new String[] {\"xmlns\", \"id\", \"jid\"}, new String[] {\"tigase:meet:0\", \"test-1\", meetJid}).withElement(\"media\", mediaEl -> mediaEl.addAttribute(\"type\", \"audio\")).withElement(\"media\", mediaEl -> mediaEl.addAttribute(\"type\", \"video\"))}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tPacket packet = Packet.packetInstance(msg, senderJid, recipientJid);\n\n\t\tmsgRepository.storeMessage(senderJid, recipientJid, new Date(), packet.getElement(), null);\n\n\t\tQueue<SessionManagerHandlerImpl.Item> results = getInstance(SessionManagerHandlerImpl.class).getOutQueue();\n\t\tpushNotifications.notifyNewOfflineMessage(packet, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(1, results.size());\n\n\t\tElement notificationElem = results.remove().packet.getElement().findChild(new String[] { \"iq\", \"pubsub\", \"publish\", \"item\", \"notification\"});\n\t\tElement meetEl = notificationElem.getChild(\"meet\");\n\t\tassertNotNull(meetEl);\n\t\tassertEquals(\"test-1\", meetEl.getAttributeStaticStr(\"id\"));\n\t\tassertEquals(meetJid, meetEl.getAttributeStaticStr(\"jid\"));\n\t\tassertArrayEquals(new String[] { \"audio\", \"video\" }, meetEl.findChildren(el -> el.getName() == \"media\").stream().map(Element::getCData).sorted().toArray(String[]::new));\n\t}\n\n\t@Test\n\tpublic void testEncryptedNotification() throws TigaseDBException, NoSuchPaddingException, NoSuchAlgorithmException,\n\t\t\t\t\t\t\t\t\t\t\t\t   InvalidAlgorithmParameterException, InvalidKeyException,\n\t\t\t\t\t\t\t\t\t\t\t\t   BadPaddingException, IllegalBlockSizeException {\n\t\tElement settings = new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t   new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"push-node\"});\n\t\tSecureRandom random = new SecureRandom();\n\t\tbyte[] key = new byte[128/8];\n\t\trandom.nextBytes(key);\n\t\tsettings.withElement(\"encrypt\", \"tigase:push:encrypt:0\", el -> {\n\t\t\tel.setCData(Base64.encode(key));\n\t\t\tel.setAttribute(\"alg\", \"aes-128-gcm\");\n\t\t});\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t  settings.toString());\n\n\t\tString msgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tElement msg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tPacket packet = Packet.packetInstance(msg, senderJid, recipientJid);\n\n\t\tmsgRepository.storeMessage(senderJid, recipientJid, new Date(), packet.getElement(), null);\n\n\t\tQueue<SessionManagerHandlerImpl.Item> results = getInstance(SessionManagerHandlerImpl.class).getOutQueue();\n\t\tpushNotifications.notifyNewOfflineMessage(packet, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(1, results.size());\n\n\t\tElement notificationElem = results.remove().packet.getElement().findChild(new String[] { \"iq\", \"pubsub\", \"publish\", \"item\", \"notification\"});\n\t\tElement encrypted = notificationElem.getChild(\"encrypted\", \"tigase:push:encrypt:0\");\n\t\tbyte[] enc = Base64.decode(encrypted.getCData());\n\t\tbyte[] iv = Base64.decode(encrypted.getAttributeStaticStr(\"iv\"));\n\n\t\tCipher cipher = Cipher.getInstance(\"AES/GCM/NoPadding\");\n\t\tcipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, \"AES\"), new GCMParameterSpec(128, iv));\n\t\tbyte[] decoded = cipher.doFinal(enc);\n\n\t\tassertTrue(new String(decoded).contains(msgBody));\n\t}\n\n\t@Test\n\tpublic void testEncryptedJingleNotification() throws TigaseDBException, NoSuchPaddingException, NoSuchAlgorithmException,\n\t\t\t\t\t\t\t\t\t\t\t\t   InvalidAlgorithmParameterException, InvalidKeyException,\n\t\t\t\t\t\t\t\t\t\t\t\t   BadPaddingException, IllegalBlockSizeException {\n\t\tElement settings = new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t   new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"push-node\"});\n\t\tsettings.addChild(new Element(\"jingle\", new String[] {\"xmlns\", \"id\"}, new String[] {\"tigase:push:jingle:0\", \"test-1\"}));\n\t\tSecureRandom random = new SecureRandom();\n\t\tbyte[] key = new byte[128/8];\n\t\trandom.nextBytes(key);\n\t\tsettings.withElement(\"encrypt\", \"tigase:push:encrypt:0\", el -> {\n\t\t\tel.setCData(Base64.encode(key));\n\t\t\tel.setAttribute(\"alg\", \"aes-128-gcm\");\n\t\t});\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t  settings.toString());\n\n\t\tElement msg = new Element(\"message\", new Element[]{new Element(\"propose\", new String[] {\"xmlns\", \"id\"}, new String[] {\"urn:xmpp:jingle-message:0\", \"test-1\"})}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tPacket packet = Packet.packetInstance(msg, senderJid, recipientJid);\n\n\t\tmsgRepository.storeMessage(senderJid, recipientJid, new Date(), packet.getElement(), null);\n\n\t\tQueue<SessionManagerHandlerImpl.Item> results = getInstance(SessionManagerHandlerImpl.class).getOutQueue();\n\t\tpushNotifications.notifyNewOfflineMessage(packet, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(1, results.size());\n\n\t\tElement notificationElem = results.remove().packet.getElement().findChild(new String[] { \"iq\", \"pubsub\", \"publish\", \"item\", \"notification\"});\n\t\tElement encrypted = notificationElem.getChild(\"encrypted\", \"tigase:push:encrypt:0\");\n\t\tassertEquals(\"voip\", encrypted.getAttributeStaticStr(\"type\"));\n\t\tbyte[] enc = Base64.decode(encrypted.getCData());\n\t\tbyte[] iv = Base64.decode(encrypted.getAttributeStaticStr(\"iv\"));\n\n\t\tCipher cipher = Cipher.getInstance(\"AES/GCM/NoPadding\");\n\t\tcipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, \"AES\"), new GCMParameterSpec(128, iv));\n\t\tbyte[] decoded = cipher.doFinal(enc);\n\n\t\tassertTrue(new String(decoded).contains(\"test-1\"));\n\t}\n\n\t@Test\n\tpublic void testEncryptedJingleNotification_reject()\n\t\t\tthrows TigaseDBException, NoSuchPaddingException, NoSuchAlgorithmException,\n\t\t\t\t   InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException,\n\t\t\t\t   IllegalBlockSizeException, NotAuthorizedException, TigaseStringprepException {\n\t\tElement settings = new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t   new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"push-node\"});\n\t\tsettings.addChild(new Element(\"jingle\", new String[] {\"xmlns\", \"id\"}, new String[] {\"tigase:push:jingle:0\", \"test-1\"}));\n\t\tSecureRandom random = new SecureRandom();\n\t\tbyte[] key = new byte[128/8];\n\t\trandom.nextBytes(key);\n\t\tsettings.withElement(\"encrypt\", \"tigase:push:encrypt:0\", el -> {\n\t\t\tel.setCData(Base64.encode(key));\n\t\t\tel.setAttribute(\"alg\", \"aes-128-gcm\");\n\t\t});\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t  settings.toString());\n\n\t\tElement msg = new Element(\"message\", new Element[]{new Element(\"reject\", new String[] {\"xmlns\", \"id\"}, new String[] {\"urn:xmpp:jingle-message:0\", \"test-1\"})}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tPacket packet = Packet.packetInstance(msg, recipientJid, senderJid);\n\n\t\tmsgRepository.storeMessage(senderJid, recipientJid, new Date(), packet.getElement(), null);\n\n\t\tXMPPResourceConnection session = getSession(JID.jidInstanceNS(\"test-1\", \"example.com\", \"test-1\"), recipientJid);\n\t\tQueue<SessionManagerHandlerImpl.Item> results = getInstance(SessionManagerHandlerImpl.class).getOutQueue();\n\t\tpushNotifications.notifyNewOfflineMessage(packet, session, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(1, results.size());\n\n\t\tElement notificationElem = results.remove().packet.getElement().findChild(new String[] { \"iq\", \"pubsub\", \"publish\", \"item\", \"notification\"});\n\t\tElement encrypted = notificationElem.getChild(\"encrypted\", \"tigase:push:encrypt:0\");\n\t\tassertEquals(\"voip\", encrypted.getAttributeStaticStr(\"type\"));\n\t\tbyte[] enc = Base64.decode(encrypted.getCData());\n\t\tbyte[] iv = Base64.decode(encrypted.getAttributeStaticStr(\"iv\"));\n\n\t\tCipher cipher = Cipher.getInstance(\"AES/GCM/NoPadding\");\n\t\tcipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, \"AES\"), new GCMParameterSpec(128, iv));\n\t\tbyte[] decoded = cipher.doFinal(enc);\n\n\t\tassertTrue(new String(decoded).contains(\"test-1\"));\n\t}\n\n\t@Test\n\tpublic void testEncryptedNotificationForOMEMO() throws TigaseDBException, NoSuchPaddingException, NoSuchAlgorithmException,\n\t\t\t\t\t\t\t\t\t\t\t\t   InvalidAlgorithmParameterException, InvalidKeyException,\n\t\t\t\t\t\t\t\t\t\t\t\t   BadPaddingException, IllegalBlockSizeException {\n\t\tElement settings = new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t   new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"push-node\"});\n\t\tSecureRandom random = new SecureRandom();\n\t\tbyte[] key = new byte[128/8];\n\t\trandom.nextBytes(key);\n\t\tsettings.withElement(\"encrypt\", \"tigase:push:encrypt:0\", el -> {\n\t\t\tel.setCData(Base64.encode(key));\n\t\t\tel.setAttribute(\"alg\", \"aes-128-gcm\");\n\t\t});\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t  settings.toString());\n\n\t\tString msgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tElement msg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tmsg.addChild(new Element(\"encrypted\", new String[]{\"xmlns\"}, new String[]{\"eu.siacs.conversations.axolotl\"}));\n\t\tPacket packet = Packet.packetInstance(msg, senderJid, recipientJid);\n\n\t\tmsgRepository.storeMessage(senderJid, recipientJid, new Date(), packet.getElement(), null);\n\n\t\tQueue<SessionManagerHandlerImpl.Item> results = getInstance(SessionManagerHandlerImpl.class).getOutQueue();\n\t\tpushNotifications.notifyNewOfflineMessage(packet, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(1, results.size());\n\n\t\tElement notificationElem = results.remove().packet.getElement().findChild(new String[] { \"iq\", \"pubsub\", \"publish\", \"item\", \"notification\"});\n\t\tElement encrypted = notificationElem.getChild(\"encrypted\", \"tigase:push:encrypt:0\");\n\t\tbyte[] enc = Base64.decode(encrypted.getCData());\n\t\tbyte[] iv = Base64.decode(encrypted.getAttributeStaticStr(\"iv\"));\n\n\t\tCipher cipher = Cipher.getInstance(\"AES/GCM/NoPadding\");\n\t\tcipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, \"AES\"), new GCMParameterSpec(128, iv));\n\t\tbyte[] decoded = cipher.doFinal(enc);\n\n\t\tassertTrue(new String(decoded).contains(\"New secure message. Open to see the message.\"));\n\t}\n\n\n\trecord GroupchatSettings(BareJID jid, Allow allow, String nick) {\n\t\tenum Allow {\n\t\t\talways,\n\t\t\tmentioned,\n\t\t\tnever\n\t\t}\n\t}\n\trecord Settings(String pushNode, byte[] key, List<GroupchatSettings> groupchatSettings) {\n\t\tpublic static Settings create(List<GroupchatSettings> groupchatSettings) {\n\t\t\tSecureRandom random = new SecureRandom();\n\t\t\tbyte[] key = new byte[128/8];\n\t\t\trandom.nextBytes(key);\n\t\t\treturn new Settings(\"push-node-\" + UUID.randomUUID(), key, groupchatSettings);\n\t\t}\n\t}\n\n\tprivate void setSettingsWithMuc(Settings dto) throws TigaseDBException {\n\t\tElement settings = new Element(\"settings\", new String[]{\"jid\", \"node\", \"priority\"},\n\t\t\t\t\t\t\t\t\t   new String[]{pushServiceJid.toString(), dto.pushNode(), \"true\"});\n\n\t\tsettings.withElement(\"encrypt\", \"tigase:push:encrypt:0\", el -> {\n\t\t\tel.setCData(Base64.encode(dto.key()));\n\t\t\tel.setAttribute(\"max-size\", \"3072\");\n\t\t\tel.setAttribute(\"alg\", \"aes-128-gcm\");\n\t\t});\n\t\tif (dto.groupchatSettings() != null) {\n\t\t\tsettings.withElement(\"groupchat\", \"tigase:push:filter:groupchat:0\", el -> {\n\t\t\t\tfor (GroupchatSettings groupchat : dto.groupchatSettings()) {\n\t\t\t\t\tel.withElement(\"room\", roomEl -> {\n\t\t\t\t\t\troomEl.withAttribute(\"jid\", groupchat.jid().toString())\n\t\t\t\t\t\t\t\t.withAttribute(\"allow\", groupchat.allow().name());\n\t\t\t\t\t\tOptional.ofNullable(groupchat.nick()).ifPresent(nick -> el.withAttribute(\"nick\", nick));\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\tsettings.withElement(\"jingle\", el -> {\n\t\t\tel.setXMLNS(\"tigase:push:jingle:0\");\n\t\t});\n\t\tSystem.out.println(\"saving settings: \" + settings.toString());\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/\" + dto.pushNode(),\n\t\t\t\t\t\t\t\t\t\t\t\t  settings.toString());\n\t}\n\n\t@Test\n\tpublic void testEncryptedNotificationForGroupchatOMEMO() throws TigaseDBException, NoSuchPaddingException, NoSuchAlgorithmException,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   InvalidAlgorithmParameterException, InvalidKeyException,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t   BadPaddingException, IllegalBlockSizeException {\n\t\tList<Settings> allSettings = List.of(\n\t\t\t\tSettings.create(List.of(new GroupchatSettings(senderJid.getBareJID(), GroupchatSettings.Allow.mentioned, \"test-123\"))),\n\t\t\t\tSettings.create(List.of(new GroupchatSettings(senderJid.getBareJID(), GroupchatSettings.Allow.never, \"test-123\"))),\n\t\t\t\tSettings.create(List.of()),\n\t\t\t\tSettings.create(List.of(new GroupchatSettings(senderJid.getBareJID(), GroupchatSettings.Allow.always, null)))\n\t\t);\n\n\t\tfor (Settings settings : allSettings) {\n\t\t\tsetSettingsWithMuc(settings);\n\t\t}\n\n\t\tString msgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tElement msg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\", \"type\"},\n\t\t\t\t\t\t\t\t  new String[]{\"jabber:client\", \"groupchat\"});\n\t\tmsg.addChild(new Element(\"encrypted\", new String[]{\"xmlns\"}, new String[]{\"eu.siacs.conversations.axolotl\"}));\n\t\tmsg.addChild(new Element(\"store\", new String[]{\"xmlns\"}, new String[]{\"urn:xmpp:hints\"}));\n\t\tPacket packet = Packet.packetInstance(msg, senderJid, recipientJid.copyWithoutResource());\n\n\t\tQueue<SessionManagerHandlerImpl.Item> results = getInstance(SessionManagerHandlerImpl.class).getOutQueue();\n\t\tOfflineMessages offlineMessages = getInstance(OfflineMessages.class);\n\t\tofflineMessages.postProcess(packet, null, null, new ArrayDeque<>(), new HashMap<>());\n\n//\t\tmsgRepository.storeMessage(senderJid, recipientJid, new Date(), packet.getElement(), null);\n//\n//\t\tpushNotifications.notifyNewOfflineMessage(packet, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tSettings usedSettings = allSettings.get(3);\n\t\t\n\t\tassertEquals(1, results.size());\n\n\t\tElement notificationElem = results.remove().packet.getElement().findChild(new String[] { \"iq\", \"pubsub\", \"publish\", \"item\", \"notification\"});\n\t\tSystem.out.println(notificationElem);\n\t\tElement encrypted = notificationElem.getChild(\"encrypted\", \"tigase:push:encrypt:0\");\n\t\tbyte[] enc = Base64.decode(encrypted.getCData());\n\t\tbyte[] iv = Base64.decode(encrypted.getAttributeStaticStr(\"iv\"));\n\n\t\tCipher cipher = Cipher.getInstance(\"AES/GCM/NoPadding\");\n\t\tcipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(usedSettings.key(), \"AES\"), new GCMParameterSpec(128, iv));\n\t\tbyte[] decoded = cipher.doFinal(enc);\n\n\t\tassertTrue(new String(decoded).contains(\"New secure message. Open to see the message.\"));\n\t}\n\t\n\t@Test\n\tpublic void testMessageRetrieved() throws TigaseDBException, NotAuthorizedException, TigaseStringprepException {\n\t\tElement settings = new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t   new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"push-node\"});\n\t\tsettings.setAttribute(\"priority\", \"true\");\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t  settings.toString());\n\n\t\tQueue<SessionManagerHandlerImpl.Item> results = getInstance(SessionManagerHandlerImpl.class).getOutQueue();\n\t\tpushNotifications.notifyOfflineMessagesRetrieved(getSession(recipientJid, recipientJid), new ArrayDeque<>());\n\n\t\tassertEquals(1, results.size());\n\t\tassertEquals(\"low\", results.poll().packet.getElemCDataStaticStr(\n\t\t\t\tnew String[]{\"iq\", \"pubsub\", \"publish\", \"item\", \"notification\", \"priority\"}));\n//\t\tSystem.out.println(\"results:\" + results);\n\t}\n\n\t@Test\n\tpublic void testMessageRetrieved2() throws TigaseDBException, NotAuthorizedException, TigaseStringprepException {\n\t\tElement settings = new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t   new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"push-node\"});\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t  settings.toString());\n\n\t\tQueue<SessionManagerHandlerImpl.Item> results = getInstance(SessionManagerHandlerImpl.class).getOutQueue();\n\t\tpushNotifications.notifyOfflineMessagesRetrieved(getSession(recipientJid, recipientJid), new ArrayDeque<>());\n\n\t\tassertEquals(1, results.size());\n\t\tassertEquals(null, results.poll().packet.getElemCDataStaticStr(\n\t\t\t\tnew String[]{\"iq\", \"pubsub\", \"publish\", \"item\", \"notification\", \"priority\"}));\n//\t\tSystem.out.println(\"results:\" + results);\n\t}\n\n\t@Test\n\tpublic void testPushError() throws TigaseDBException, XMPPException {\n\t\tElement settings = new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t   new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"push-node\"});\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t  settings.toString());\n\t\tElement settings1 = new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t   new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"push-node-1\"});\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node-1\",\n\t\t\t\t\t\t\t\t\t\t\t\t  settings1.toString());\n\t\t\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tPacket message = Message.getMessage(senderJid, recipientJid, StanzaType.chat, \"Message \" +\n\t\t\t\tUUID.randomUUID()\n\t\t\t\t\t\t.toString(), null, null, UUID.randomUUID().toString());\n\t\tpushNotifications.notifyNewOfflineMessage(message, null, results, new HashMap<>());\n\n\t\tQueue<SessionManagerHandlerImpl.Item> items = getInstance(SessionManagerHandlerImpl.class).getOutQueue();\n\n\t\tassertEquals(items.size(), 2);\n\n\t\tSessionManagerHandlerImpl.Item item = null;\n\t\twhile ((item = items.poll()) != null) {\n\t\t\tif (\"push-node-1\".equals(\n\t\t\t\t\titem.packet.getAttributeStaticStr(new String[]{\"iq\", \"pubsub\", \"publish\"}, \"node\"))) {\n\t\t\t\titem.handler.handle(Authorization.ITEM_NOT_FOUND.getResponseMessage(item.packet, null, false));\n\t\t\t} else {\n\t\t\t\titem.handler.handle(item.packet.okResult((String) null, 0));\n\t\t\t}\n\t\t}\n\n\t\tMap<String, String> data = getInstance(UserRepository.class).getDataMap(recipientJid.getBareJID(), \"urn:xmpp:push:0\");\n\t\tassertNotNull(data);\n\t\tassertEquals(1, data.size());\n\t\tassertNotNull(data.get(pushServiceJid.toString() + \"/push-node\"));\n\t}\n\n\t@Test\n\tpublic void testPushDisableByComponent() throws TigaseDBException, NotAuthorizedException {\n\t\tElement settings = new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t   new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"push-node\"});\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t  settings.toString());\n\t\tElement settings1 = new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t\tnew String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t \"push-node-1\"});\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node-1\",\n\t\t\t\t\t\t\t\t\t\t\t\t  settings1.toString());\n\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tPacket message = Packet.packetInstance(new Element(\"message\").withElement(\"pubsub\", \"http://jabber.org/protocol/pubsub\", pubsubEl -> {\n\t\t\tpubsubEl.setAttribute(\"node\", \"push-node-1\");\n\t\t\tpubsubEl.withElement(\"affiliation\", affEl -> {\n\t\t\t\taffEl.setAttribute(\"jid\", recipientJid.toString());\n\t\t\t\taffEl.setAttribute(\"affiliation\", \"none\");\n\t\t\t});\n\t\t}), pushServiceJid, recipientJid);\n\t\tpushNotifications.processMessage(message, null, new ArrayDeque<>()::offer);\n\t\t\n\t\tMap<String, String> data = getInstance(UserRepository.class).getDataMap(recipientJid.getBareJID(), \"urn:xmpp:push:0\");\n\t\tassertNotNull(data);\n\t\tassertEquals(1, data.size());\n\t\tassertNotNull(data.get(pushServiceJid.toString() + \"/push-node\"));\n\t}\n\n\t@Test\n\tpublic void test_accountRemovalNotificationGeneration() throws Exception {\n\t\tEventBus eventBus = new EventBusImplementation();\n\t\teventBus.registerAll(pushNotifications);\n\t\ttry {\n\t\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t  new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"push-node\"}).toString());\n\n\t\t\teventBus.fire(new UserRepository.UserBeforeRemovedEvent(recipientJid.getBareJID()));\n\t\t\tQueue<SessionManagerHandlerImpl.Item> items = getInstance(SessionManagerHandlerImpl.class).getOutQueue();\n\t\t\tassertEquals(0, items.size());\n\n\t\t\tField f = PushNotifications.class.getDeclaredField(\"sendAccountRemovalNotification\");\n\t\t\tf.setAccessible(true);\n\t\t\tf.set(pushNotifications, true);\n\n\t\t\teventBus.fire(new UserRepository.UserBeforeRemovedEvent(recipientJid.getBareJID()));\n\t\t\titems = getInstance(SessionManagerHandlerImpl.class).getOutQueue();\n\t\t\tassertEquals(1, items.size());\n\t\t\tPacket packet = items.poll().getPacket();\n\t\t\tassertTrue(packet.getElement().toString().contains(\"account-removed\"));\n\t\t} finally {\n\t\t\teventBus.unregisterAll(pushNotifications);\n\t\t}\n\t}\n\t\n\t@Test\n\tpublic void test_accountRemovalNotificationGenerationEncrypted() throws Exception {\n\t\tElement settings = new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t   new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"push-node\"});\n\t\tsettings.addChild(new Element(\"jingle\", new String[] {\"xmlns\", \"id\"}, new String[] {\"tigase:push:jingle:0\", \"test-1\"}));\n\t\tSecureRandom random = new SecureRandom();\n\t\tbyte[] key = new byte[128/8];\n\t\trandom.nextBytes(key);\n\t\tsettings.withElement(\"encrypt\", \"tigase:push:encrypt:0\", el -> {\n\t\t\tel.setCData(Base64.encode(key));\n\t\t\tel.setAttribute(\"alg\", \"aes-128-gcm\");\n\t\t});\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t  settings.toString());\n\t\t\n\t\tpushNotifications.onUserRemoved(new UserRepository.UserBeforeRemovedEvent(recipientJid.getBareJID()));\n\t\tQueue<SessionManagerHandlerImpl.Item> items = getInstance(SessionManagerHandlerImpl.class).getOutQueue();\n\t\tassertEquals(0, items.size());\n\n\t\tField f = PushNotifications.class.getDeclaredField(\"sendAccountRemovalNotification\");\n\t\tf.setAccessible(true);\n\t\tf.set(pushNotifications, true);\n\n\t\tpushNotifications.onUserRemoved(new UserRepository.UserBeforeRemovedEvent(recipientJid.getBareJID()));\n\t\t\n\t\tElement notificationElem = items.poll().getPacket().getElement().findChild(new String[] { \"iq\", \"pubsub\", \"publish\", \"item\", \"notification\"});\n\t\tElement encrypted = notificationElem.getChild(\"encrypted\", \"tigase:push:encrypt:0\");\n\t\tbyte[] enc = Base64.decode(encrypted.getCData());\n\t\tbyte[] iv = Base64.decode(encrypted.getAttributeStaticStr(\"iv\"));\n\n\t\tCipher cipher = Cipher.getInstance(\"AES/GCM/NoPadding\");\n\t\tcipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, \"AES\"), new GCMParameterSpec(128, iv));\n\t\tbyte[] decoded = cipher.doFinal(enc);\n\n\t\tassertTrue(new String(decoded, StandardCharsets.UTF_8).contains(\"account-removed\"));\n\t}\n\n\tprotected void registerLocalBeans(Kernel kernel) {\n\t\tsuper.registerBeans(kernel);\n\t\tkernel.registerBean(\"eventBus\").asInstance(EventBusFactory.getInstance()).exportable().exec();\n\t\tkernel.registerBean(\"sess-man\").asInstance(this.getSessionManagerHandler()).setActive(true).exportable().exec();//.asClass(DummySessionManager.class).setActive(true).exportable().exec();\n\t\tkernel.registerBean(MessageDeliveryLogic.class).setActive(true).exportable().exec();\n\t\tkernel.registerBean(\"msgRepository\").asClass(MsgRepositoryIfcImpl.class).exportable().exec();\n\t\tkernel.registerBean(OfflineMessages.class).setActive(true).exec();\n\t\tkernel.registerBean(PushNotifications.class).setActive(true).exec();\n\t}\n\n\tpublic static class MsgRepositoryIfcImpl\n\t\t\textends MsgRepository {\n\n\t\tprivate final Queue<Packet> stored = new ArrayDeque();\n\n\t\tpublic MsgRepositoryIfcImpl() {\n\t\t}\n\n\t\t@Override\n\t\tpublic Element getMessageExpired(long time, boolean delete) {\n\t\t\tthrow new UnsupportedOperationException(\n\t\t\t\t\t\"Not supported yet.\"); //To change body of generated methods, choose Tools | Templates.\n\t\t}\n\n\t\t@Override\n\t\tpublic Queue<Element> loadMessagesToJID(XMPPResourceConnection session, boolean delete)\n\t\t\t\tthrows UserNotFoundException {\n\t\t\tQueue<Element> res = new LinkedList<Element>();\n\t\t\tfor (Packet pac : stored) {\n\t\t\t\tres.add(pac.getElement());\n\t\t\t}\n\t\t\treturn res;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean storeMessage(JID from, JID to, Date expired, Element msg, NonAuthUserRepository userRepo)\n\t\t\t\tthrows UserNotFoundException {\n\t\t\treturn stored.offer(Packet.packetInstance(msg, from, to));\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<Enum, Long> getMessagesCount(JID to) throws UserNotFoundException {\n\t\t\treturn stored.stream().collect(Collectors.groupingBy(packet -> {\n\t\t\t\tswitch (packet.getElemName()) {\n\t\t\t\t\tcase \"message\":\n\t\t\t\t\t\treturn MSG_TYPES.message;\n\t\t\t\t\tcase \"presence\":\n\t\t\t\t\t\treturn MSG_TYPES.presence;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn MSG_TYPES.none;\n\t\t\t\t}\n\t\t\t}, Collectors.counting()));\n\t\t}\n\n\t\t@Override\n\t\tpublic List<Element> getMessagesList(JID to) throws UserNotFoundException {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic int deleteMessagesToJID(List db_ids, XMPPResourceConnection session) throws UserNotFoundException {\n\t\t\treturn 0;\n\t\t}\n\n\t\t@Override\n\t\tpublic Queue<Element> loadMessagesToJID(List db_ids, XMPPResourceConnection session, boolean delete,\n\t\t\t\t\t\t\t\t\t\t\t\tOfflineMessagesProcessor proc) throws UserNotFoundException {\n\t\t\treturn null;\n\t\t}\n\n\t\tpublic Queue<Packet> getStored() {\n\t\t\treturn stored;\n\t\t}\n\n\t\t@Override\n\t\tpublic void setDataSource(DataSource dataSource) {\n\n\t\t}\n\n\t\t@Override\n\t\tprotected void loadExpiredQueue(int max) {\n\n\t\t}\n\n\t\t@Override\n\t\tprotected void loadExpiredQueue(Date expired) {\n\n\t\t}\n\n\t\t@Override\n\t\tprotected void deleteMessage(Object db_id) {\n\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/push/PushNotificationsWithAwayTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.push;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.db.DataSource;\nimport tigase.db.NonAuthUserRepository;\nimport tigase.db.UserNotFoundException;\nimport tigase.db.UserRepository;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Packet;\nimport tigase.server.amp.db.MsgRepository;\nimport tigase.xml.Element;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.impl.MessageDeliveryLogic;\nimport tigase.xmpp.impl.ProcessorTestCase;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.*;\nimport java.util.stream.Collectors;\n\nimport static org.junit.Assert.*;\nimport static tigase.Assert.assertElementEquals;\n\n/**\n * Created by andrzej on 03.01.2017.\n */\npublic class PushNotificationsWithAwayTest\n\t\textends ProcessorTestCase {\n\n\tprivate MsgRepository msgRepository;\n\tprivate PushNotifications pushNotifications;\n\tprivate JID pushServiceJid = JID.jidInstanceNS(\"push.example.com\");\n\tprivate JID recipientJid;\n\tprivate JID senderJid;\n\n\t@Before\n\t@Override\n\tpublic void setUp() throws Exception {\n\t\trecipientJid = JID.jidInstanceNS(\"recipient-\" + UUID.randomUUID() + \"@example.com/res-1\");\n\t\tsenderJid = JID.jidInstanceNS(\"sender-\" + UUID.randomUUID() + \"@example.com/res-1\");\n\t\tsuper.setUp();\n\t\tregisterLocalBeans(getKernel());\n\t\tpushNotifications = getInstance(PushNotifications.class);\n\t\tmsgRepository = getInstance(MsgRepository.class);\n\t\tKernel subkernel = getInstance(PushNotifications.ID + \"#KERNEL\");\n\t\tsubkernel.setBeanActive(\"away\", true);\n\t}\n\n\t@After\n\t@Override\n\tpublic void tearDown() throws Exception {\n\t\tsuper.tearDown();\n\t}\n\n\t@Test\n\tpublic void test_initialState() throws Exception {\n\t\tString msgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tElement msg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tPacket packet = Packet.packetInstance(msg, senderJid, recipientJid);\n\n\t\tQueue<SessionManagerHandlerImpl.Item> results = getInstance(SessionManagerHandlerImpl.class).getOutQueue();\n\t\tpushNotifications.notifyNewOfflineMessage(packet, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(0, results.size());\n\t}\n\n\t@Test\n\tpublic void test_enable() throws Exception {\n\t\tElement iqEl = new Element(\"iq\", new Element[]{new Element(\"enable\", new String[]{\"xmlns\", \"jid\", \"node\", \"away\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{\"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"push-node\", \"true\"})},\n\t\t\t\t\t\t\t\t   new String[]{\"type\", \"id\"}, new String[]{\"set\", UUID.randomUUID().toString()});\n\n\t\tXMPPResourceConnection session = getSession(\n\t\t\t\tJID.jidInstanceNS(\"c2s@example.com/\" + UUID.randomUUID().toString()), recipientJid);\n\n\t\tPacket iq = Packet.packetInstance(iqEl);\n\t\tiq.setPacketFrom(session.getConnectionId());\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tpushNotifications.process(iq, session, null, results, new HashMap<>());\n\t\tassertEquals(1, results.size());\n\t\tPacket result = results.poll();\n\t\tassertEquals(\"wrong result = \" + result, StanzaType.result, result.getType());\n\n\t\tassertNotNull(getInstance(UserRepository.class).getData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpushServiceJid + \"/push-node\"));\n\t\tMap<String, Element> settings = pushNotifications.getPushServices(recipientJid.getBareJID());\n\t\tassertNotNull(settings);\n\t\tassertEquals(1, settings.size());\n\t\tassertElementEquals(new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t\tnew String[]{pushServiceJid.toString(), \"push-node\"}),\n\t\t\t\t\t\t\tsettings.get(pushServiceJid + \"/push-node\"));\n\t\t\n\t\tsettings = pushNotifications.getPushServices(session);\n\t\tassertNotNull(settings);\n\t\tassertEquals(1, settings.size());\n\t\tassertElementEquals(new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t\tnew String[]{pushServiceJid.toString(), \"push-node\"}),\n\t\t\t\t\t\t\tsettings.get(pushServiceJid + \"/push-node\"));\n\t}\n\n\t@Test\n\tpublic void test_disable() throws Exception {\n\t\tElement iqEl = new Element(\"iq\", new Element[]{new Element(\"disable\", new String[]{\"xmlns\", \"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   new String[]{\"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tpushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"push-node\"})},\n\t\t\t\t\t\t\t\t   new String[]{\"type\", \"id\"}, new String[]{\"set\", UUID.randomUUID().toString()});\n\n\t\tXMPPResourceConnection session = getSession(\n\t\t\t\tJID.jidInstanceNS(\"c2s@example.com/\" + UUID.randomUUID().toString()), recipientJid);\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t  new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"push-node\"}).toString());\n\n\t\tPacket iq = Packet.packetInstance(iqEl);\n\t\tiq.setPacketFrom(session.getConnectionId());\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tpushNotifications.process(iq, session, null, results, new HashMap<>());\n\t\tassertEquals(1, results.size());\n\t\tPacket result = results.poll();\n\t\tassertEquals(\"wrong result = \" + result, StanzaType.result, result.getType());\n\n\t\tassertNull(getInstance(UserRepository.class).getData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t pushServiceJid + \"/push-node\"));\n\n\t\tMap<String, Element> settings = pushNotifications.getPushServices(recipientJid.getBareJID());\n\t\tassertNotNull(settings);\n\t\tassertEquals(0, settings.size());\n\n\t\tsettings = pushNotifications.getPushServices(session);\n\t\tassertNotNull(settings);\n\t\tassertEquals(0, settings.size());\n\t}\n\n\t@Test\n\tpublic void test_notificationGeneration() throws Exception {\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t  new Element(\"settings\", new String[]{\"jid\", \"node\", \"away\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"push-node\", \"true\"}).toString());\n\n\t\tString msgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tElement msg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tPacket packet = Packet.packetInstance(msg, senderJid, recipientJid);\n\n\t\tmsgRepository.storeMessage(senderJid, recipientJid, new Date(), packet.getElement(), null);\n\n\t\tQueue<SessionManagerHandlerImpl.Item> results = getInstance(\n\t\t\t\tSessionManagerHandlerImpl.class).getOutQueue();\n\t\tpushNotifications.notifyNewOfflineMessage(packet, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(1, results.size());\n\n\t\tPacket expNotification = PushNotificationHelper.createPushNotification(pushServiceJid, recipientJid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   PushNotificationHelper.createPlainNotification(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   1, senderJid, msgBody));\n\n\t\tassertElementEquals(expNotification.getElement(), results.poll().packet.getElement());\n\n\t\tmsgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tmsg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tpacket = Packet.packetInstance(msg, senderJid, recipientJid);\n\n\t\tmsgRepository.storeMessage(senderJid, recipientJid, new Date(), packet.getElement(), null);\n\n\t\tresults.clear();\n\t\tpushNotifications.notifyNewOfflineMessage(packet, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(1, results.size());\n\n\t\texpNotification = PushNotificationHelper.createPushNotification(pushServiceJid, recipientJid, \"push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tPushNotificationHelper.createPlainNotification(2,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  senderJid,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  msgBody));\n\n\t\tassertElementEquals(expNotification.getElement(), results.poll().packet.getElement());\n\t}\n\n\t@Test\n\tpublic void test_notificationGenerationAway() throws Exception {\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t  new Element(\"settings\", new String[]{\"jid\", \"node\", \"away\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"push-node\", \"true\"}).toString());\n\n\t\tXMPPResourceConnection session = this.getSession(JID.jidInstanceNS(UUID.randomUUID().toString(), recipientJid.getDomain()), recipientJid);\n\n\t\tString msgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tElement msg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tPacket packet = Packet.packetInstance(msg, senderJid, recipientJid.copyWithoutResource());\n\n\t\tQueue<SessionManagerHandlerImpl.Item> results = getInstance(\n\t\t\t\tSessionManagerHandlerImpl.class).getOutQueue();\n\t\tpushNotifications.process(packet, session, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(1, results.size());\n\n\t\tElement presence = new Element(\"presence\");\n\t\tpresence.addChild(new Element(\"show\", \"xa\"));\n\t\tsession.setPresence(presence);\n\n\t\tmsgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tmsg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tpacket = Packet.packetInstance(msg, senderJid, recipientJid.copyWithoutResource());\n\n\t\tresults.clear();\n\t\tpushNotifications.process(packet, session, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(1, results.size());\n\n\t\tpresence = new Element(\"presence\");\n\t\tsession.setPresence(presence);\n\n\t\tmsgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tmsg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tpacket = Packet.packetInstance(msg, senderJid, recipientJid.copyWithoutResource());\n\n\t\tresults.clear();\n\t\tpushNotifications.process(packet, session, null, new ArrayDeque<>(), new HashMap<>());\n\n//\t\tresults.forEach(p -> System.out.println(p.toString()));\n\t\tassertArrayEquals(new Element[0], results.stream().map(\n\t\t\t\tSessionManagerHandlerImpl.Item::getPacket).map(Packet::getElement).toArray(Element[]::new));\n\t\t//assertEquals(0, results.size());\n\n\t}\n\n\t@Test\n\tpublic void test_notificationGenerationForMUCwhenOnline() throws Exception {\n\t\tgetInstance(UserRepository.class).setData(recipientJid.getBareJID(), \"urn:xmpp:push:0\",\n\t\t\t\t\t\t\t\t\t\t\t\t  pushServiceJid + \"/push-node\",\n\t\t\t\t\t\t\t\t\t\t\t\t  new Element(\"settings\", new String[]{\"jid\", \"node\"},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t  new String[]{pushServiceJid.toString(),\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t   \"push-node\"}).toString());\n\n\t\tXMPPResourceConnection session = this.getSession(JID.jidInstanceNS(UUID.randomUUID().toString(), recipientJid.getDomain()), recipientJid);\n\n\t\tString msgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tElement msg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tPacket packet = Packet.packetInstance(msg, senderJid, recipientJid.copyWithoutResource());\n\n\t\tQueue<SessionManagerHandlerImpl.Item> results = getInstance(\n\t\t\t\tSessionManagerHandlerImpl.class).getOutQueue();\n\t\tpushNotifications.process(packet, session, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(0, results.size());\n\n\t\tElement presence = new Element(\"presence\");\n\t\tsession.setPresence(presence);\n\n\t\tmsgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tmsg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\"},\n\t\t\t\t\t\t  new String[]{\"jabber:client\"});\n\t\tpacket = Packet.packetInstance(msg, senderJid, recipientJid.copyWithoutResource());\n\n\t\tresults.clear();\n\t\tpushNotifications.process(packet, session, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(0, results.size());\n\n\t\tmsgBody = \"Message body \" + UUID.randomUUID().toString();\n\t\tmsg = new Element(\"message\", new Element[]{new Element(\"body\", msgBody)}, new String[]{\"xmlns\", \"type\"},\n\t\t\t\t\t\t  new String[]{\"jabber:client\", \"groupchat\"});\n\t\tpacket = Packet.packetInstance(msg, senderJid, recipientJid.copyWithoutResource());\n\n\t\tresults.clear();\n\t\tpushNotifications.process(packet, session, null, new ArrayDeque<>(), new HashMap<>());\n\n\t\tassertEquals(1, results.size());\n\n\t}\n\n\tprotected void registerLocalBeans(Kernel kernel) {\n\t\tsuper.registerBeans(kernel);\n\t\tkernel.registerBean(\"eventBus\").asInstance(EventBusFactory.getInstance()).exportable().exec();\n\t\tkernel.registerBean(\"sess-man\").asInstance(this.getSessionManagerHandler()).setActive(true).exportable().exec();//.asClass(DummySessionManager.class).setActive(true).exportable().exec();\n\t\tkernel.registerBean(MessageDeliveryLogic.class).setActive(true).exportable().exec();\n\t\tkernel.registerBean(\"msgRepository\").asClass(MsgRepositoryIfcImpl.class).exportable().exec();\n\t\tkernel.registerBean(PushNotifications.class).setActive(true).exportable().exec();\n\t}\n\n\tpublic static class MsgRepositoryIfcImpl\n\t\t\textends MsgRepository {\n\n\t\tprivate final Queue<Packet> stored = new ArrayDeque();\n\n\t\tpublic MsgRepositoryIfcImpl() {\n\t\t}\n\n\t\t@Override\n\t\tpublic Element getMessageExpired(long time, boolean delete) {\n\t\t\tthrow new UnsupportedOperationException(\n\t\t\t\t\t\"Not supported yet.\"); //To change body of generated methods, choose Tools | Templates.\n\t\t}\n\n\t\t@Override\n\t\tpublic Queue<Element> loadMessagesToJID(XMPPResourceConnection session, boolean delete)\n\t\t\t\tthrows UserNotFoundException {\n\t\t\tQueue<Element> res = new LinkedList<Element>();\n\t\t\tfor (Packet pac : stored) {\n\t\t\t\tres.add(pac.getElement());\n\t\t\t}\n\t\t\treturn res;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean storeMessage(JID from, JID to, Date expired, Element msg, NonAuthUserRepository userRepo)\n\t\t\t\tthrows UserNotFoundException {\n\t\t\treturn stored.offer(Packet.packetInstance(msg, from, to));\n\t\t}\n\n\t\t@Override\n\t\tpublic Map<Enum, Long> getMessagesCount(JID to) throws UserNotFoundException {\n\t\t\treturn stored.stream().collect(Collectors.groupingBy(packet -> {\n\t\t\t\tswitch (packet.getElemName()) {\n\t\t\t\t\tcase \"message\":\n\t\t\t\t\t\treturn MSG_TYPES.message;\n\t\t\t\t\tcase \"presence\":\n\t\t\t\t\t\treturn MSG_TYPES.presence;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn MSG_TYPES.none;\n\t\t\t\t}\n\t\t\t}, Collectors.counting()));\n\t\t}\n\n\t\t@Override\n\t\tpublic List<Element> getMessagesList(JID to) throws UserNotFoundException {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic int deleteMessagesToJID(List db_ids, XMPPResourceConnection session) throws UserNotFoundException {\n\t\t\treturn 0;\n\t\t}\n\n\t\t@Override\n\t\tpublic Queue<Element> loadMessagesToJID(List db_ids, XMPPResourceConnection session, boolean delete,\n\t\t\t\t\t\t\t\t\t\t\t\tOfflineMessagesProcessor proc) throws UserNotFoundException {\n\t\t\treturn null;\n\t\t}\n\n\t\tpublic Queue<Packet> getStored() {\n\t\t\treturn stored;\n\t\t}\n\n\t\t@Override\n\t\tpublic void setDataSource(DataSource dataSource) {\n\n\t\t}\n\n\t\t@Override\n\t\tprotected void loadExpiredQueue(int max) {\n\n\t\t}\n\n\t\t@Override\n\t\tprotected void loadExpiredQueue(Date expired) {\n\n\t\t}\n\n\t\t@Override\n\t\tprotected void deleteMessage(Object db_id) {\n\n\t\t}\n\t}\n\t\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/push/PushPresenceTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.push;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.FixMethodOrder;\nimport org.junit.Test;\nimport org.junit.runners.MethodSorters;\nimport tigase.component.PacketWriter;\nimport tigase.component.responses.AsyncCallback;\nimport tigase.db.TigaseDBException;\nimport tigase.eventbus.EventBus;\nimport tigase.eventbus.EventBusFactory;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Packet;\nimport tigase.server.PolicyViolationException;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.xml.Element;\nimport tigase.xmpp.*;\nimport tigase.xmpp.impl.MessageDeliveryLogic;\nimport tigase.xmpp.impl.PresenceState;\nimport tigase.xmpp.impl.ProcessorTestCase;\nimport tigase.xmpp.impl.roster.RosterAbstract;\nimport tigase.xmpp.impl.roster.RosterFactory;\nimport tigase.xmpp.jid.BareJID;\nimport tigase.xmpp.jid.JID;\n\nimport java.util.ArrayDeque;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.UUID;\n\nimport static org.junit.Assert.*;\n\n@FixMethodOrder(MethodSorters.NAME_ASCENDING)\npublic class PushPresenceTest\n\t\textends ProcessorTestCase {\n\n\tprivate RosterAbstract rosterUtil;\n\tprivate PushPresence pushPresence;\n\tprivate BareJID userJid;\n\tprivate BareJID buddyJid;\n\n\t@Before\n\t@Override\n\tpublic void setUp() throws Exception {\n\t\tsuper.setUp();\n\t\tregisterLocalBeans(getKernel());\n\t\trosterUtil = RosterFactory.newRosterInstance(RosterFactory.ROSTER_IMPL_PROP_VAL);\n\t\trosterUtil.setEventBus(getInstance(EventBus.class));\n\t\tuserJid = BareJID.bareJIDInstanceNS(UUID.randomUUID().toString(), \"domain.com\");\n\t\tbuddyJid = BareJID.bareJIDInstanceNS(UUID.randomUUID().toString(), \"domain.com\");\n\t\tpushPresence = getInstance(PushPresence.class);\n\t\tpushPresence.setRosterUtil(rosterUtil);\n\t}\n\n\t@After\n\tpublic void tearDown() throws Exception {\n\t\tgetInstance(PushPresence.class).beforeUnregister();\n\t\tsuper.tearDown();\n\t}\n\t\n\t@Test\n\tpublic void testSendingPresence_OnPushEnable_NoSubscription() throws TigaseStringprepException, TigaseDBException, NotAuthorizedException {\n\t\tTestPacketWriter writer = getInstance(TestPacketWriter.class);\n\t\tassertFalse(pushPresence.isPushAvailable(userJid));\n\t\tPushNotifications pushNotifications = getInstance(PushNotifications.class);\n\t\tXMPPResourceConnection session = getSession(JID.jidInstance(UUID.randomUUID().toString(), \"domain\", null), JID.jidInstance(userJid, \"setter\"));\n\t\tpushNotifications.enableNotifications(session, JID.jidInstance(\"push.localhost\"), \"test-node\", new Element(\"enable\"), null, writer::write);\n\t\tassertTrue(pushPresence.isPushAvailable(userJid));\n\t\tassertEquals(0, writer.getQueue().size());\n\t}\n\n\t@Test\n\tpublic void testSendingPresence_OnPushEnable_WithSubscription()\n\t\t\tthrows TigaseStringprepException, TigaseDBException, NotAuthorizedException, PolicyViolationException,\n\t\t\t\t   NoConnectionIdException {\n\t\tTestPacketWriter writer = getInstance(TestPacketWriter.class);\n\t\tassertFalse(pushPresence.isPushAvailable(userJid));\n\t\tPushNotifications pushNotifications = getInstance(PushNotifications.class);\n\t\tXMPPResourceConnection buddySession = getSession(JID.jidInstance(UUID.randomUUID().toString(), \"domain\", null), JID.jidInstance(buddyJid, \"setter\"));\n\t\tXMPPResourceConnection userSession = getSession(JID.jidInstance(UUID.randomUUID().toString(), \"domain\", null), JID.jidInstance(userJid, \"setter\"));\n\n\t\tupdateRoster(userSession, JID.jidInstance(buddyJid), RosterAbstract.SubscriptionType.both);\n\t\tupdateRoster(buddySession, JID.jidInstance(userJid), RosterAbstract.SubscriptionType.both);\n\n\t\tpushNotifications.enableNotifications(userSession, JID.jidInstance(\"push.localhost\"), \"test-node\", new Element(\"enable\"), null, writer::write);\n\t\tassertTrue(pushPresence.isPushAvailable(userJid));\n\t\tassertEquals(1, writer.getQueue().size());\n\t\tassertPresenceAway(writer.getQueue().poll());\n\t}\n\n\t@Test\n\tpublic void testSendingPresence_OnPushDisable_NoSubscription() throws TigaseStringprepException, TigaseDBException, NotAuthorizedException {\n\t\tTestPacketWriter writer = getInstance(TestPacketWriter.class);\n\t\tassertFalse(pushPresence.isPushAvailable(userJid));\n\t\tPushNotifications pushNotifications = getInstance(PushNotifications.class);\n\t\tXMPPResourceConnection session = getSession(JID.jidInstance(UUID.randomUUID().toString(), \"domain\", null), JID.jidInstance(userJid, \"setter\"));\n\t\tpushNotifications.enableNotifications(session, JID.jidInstance(\"push.localhost\"), \"test-node\", new Element(\"enable\"), null, writer::write);\n\t\tassertTrue(pushPresence.isPushAvailable(userJid));\n\t\tassertEquals(0, writer.getQueue().size());\n\n\t\tpushNotifications.disableNotifications(session, userJid,  JID.jidInstance(\"push.localhost\"), \"test-node\", writer::write);\n\t\t\n\t\tassertFalse(pushPresence.isPushAvailable(userJid));\n\t\tassertEquals(0, writer.getQueue().size());\n\t}\n\n\t@Test\n\tpublic void testSendingPresence_OnPushDisable_WithSubscription()\n\t\t\tthrows TigaseStringprepException, TigaseDBException, NotAuthorizedException, PolicyViolationException,\n\t\t\t\t   NoConnectionIdException, InterruptedException {\n\t\tTestPacketWriter writer = getInstance(TestPacketWriter.class);\n\t\tassertFalse(pushPresence.isPushAvailable(userJid));\n\t\tPushNotifications pushNotifications = getInstance(PushNotifications.class);\n\n\t\tXMPPResourceConnection buddySession = getSession(JID.jidInstance(UUID.randomUUID().toString(), \"domain\", null), JID.jidInstance(buddyJid, \"setter\"));\n\t\tXMPPResourceConnection userSession = getSession(JID.jidInstance(UUID.randomUUID().toString(), \"domain\", null), JID.jidInstance(userJid, \"setter\"));\n\n\t\tpushNotifications.enableNotifications(userSession, JID.jidInstance(\"push.localhost\"), \"test-node\", new Element(\"enable\"), null, writer::write);\n\t\tassertTrue(pushPresence.isPushAvailable(userJid));\n\n\t\tupdateRoster(userSession, JID.jidInstance(buddyJid), RosterAbstract.SubscriptionType.both);\n\t\tupdateRoster(buddySession, JID.jidInstance(userJid), RosterAbstract.SubscriptionType.both);\n\n//\t\tSystem.out.println(\"packet writer \" + writer);\n\t\tassertEquals(1, writer.getQueue().size());\n\t\tassertEquals(1, writer.getQueue().size());\n\t\tassertPresenceAway(writer.getQueue().poll());\n\n\t\tpushNotifications.disableNotifications(userSession, userJid,  JID.jidInstance(\"push.localhost\"), \"test-node\", writer::write);\n\t\tassertEquals(1, writer.getQueue().size());\n\t\tassertPresenceUnavailable(writer.getQueue().poll());\n\t}\n\n\t@Test\n\tpublic void testSendingPresence_OnPresenceProbe_NoSubscription()\n\t\t\tthrows TigaseStringprepException, TigaseDBException, XMPPException {\n\t\tTestPacketWriter writer = getInstance(TestPacketWriter.class);\n\t\tassertFalse(pushPresence.isPushAvailable(userJid));\n\t\tPushNotifications pushNotifications = getInstance(PushNotifications.class);\n\t\tXMPPResourceConnection session = getSession(JID.jidInstance(UUID.randomUUID().toString(), \"domain\", null), JID.jidInstance(userJid, \"setter\"));\n\t\tpushNotifications.enableNotifications(session, JID.jidInstance(\"push.localhost\"), \"test-node\", new Element(\"enable\"), null, writer::write);\n\t\tassertTrue(pushPresence.isPushAvailable(userJid));\n\t\tassertEquals(0, writer.getQueue().size());\n\n\t\tPresenceState presenceState = getInstance(PresenceState.class);\n\t\tPacket probe = Packet.packetInstance(new Element(\"presence\").withAttribute(\"type\", StanzaType.probe.name())\n\t\t\t\t\t\t\t\t\t\t\t\t\t .withAttribute(\"from\", buddyJid.toString())\n\t\t\t\t\t\t\t\t\t\t\t\t\t .withAttribute(\"to\", userJid.toString()));\n\t\tpresenceState.process(probe, null, null, writer.getQueue(), new HashMap<>());\n\t\tassertEquals(0, writer.getQueue().size());\n\t}\n\n\t@Test\n\tpublic void testSendingPresence_OnSubscriptionAndOnPresenceProbe_WithSubscription()\n\t\t\tthrows TigaseStringprepException, TigaseDBException, XMPPException, PolicyViolationException {\n\t\tTestPacketWriter writer = getInstance(TestPacketWriter.class);\n\t\tassertFalse(pushPresence.isPushAvailable(userJid));\n\t\tPushNotifications pushNotifications = getInstance(PushNotifications.class);\n\t\tXMPPResourceConnection userSession = getSession(JID.jidInstance(UUID.randomUUID().toString(), \"domain\", null), JID.jidInstance(userJid, \"setter\"));\n\t\tpushNotifications.enableNotifications(userSession, JID.jidInstance(\"push.localhost\"), \"test-node\", new Element(\"enable\"), null, writer::write);\n\t\tXMPPResourceConnection buddySession = getSession(JID.jidInstance(UUID.randomUUID().toString(), \"domain\", null), JID.jidInstance(buddyJid, \"setter\"));\n\n\t\tupdateRoster(userSession, JID.jidInstance(buddyJid), RosterAbstract.SubscriptionType.both);\n\t\tupdateRoster(buddySession, JID.jidInstance(userJid), RosterAbstract.SubscriptionType.both);\n\n\t\tassertTrue(pushPresence.isPushAvailable(userJid));\n\t\t// add user roster subscription triggers sending presence\n\t\tassertEquals(1, writer.getQueue().size());\n\t\tassertPresenceAway(writer.getQueue().poll());\n\t\tassertEquals(0, writer.getQueue().size());\n\n\t\tPresenceState presenceState = getInstance(PresenceState.class);\n\t\tPacket probe = Packet.packetInstance(new Element(\"presence\").withAttribute(\"type\", StanzaType.probe.name())\n\t\t\t\t\t\t\t\t\t\t\t\t\t .withAttribute(\"from\", buddyJid.toString())\n\t\t\t\t\t\t\t\t\t\t\t\t\t .withAttribute(\"to\", userJid.toString()));\n\t\tpresenceState.process(probe, null, null, writer.getQueue(), new HashMap<>());\n\t\tassertEquals(1, writer.getQueue().size());\n\t\tassertPresenceAway(writer.getQueue().poll());\n\t}\n\n\t@Test\n\tpublic void testSendingPresence_OnSubscriptionAndOnRemovingSubscription()\n\t\t\tthrows TigaseStringprepException, TigaseDBException, XMPPException, PolicyViolationException {\n\t\tTestPacketWriter writer = getInstance(TestPacketWriter.class);\n\t\tassertFalse(pushPresence.isPushAvailable(userJid));\n\t\tPushNotifications pushNotifications = getInstance(PushNotifications.class);\n\t\tXMPPResourceConnection userSession = getSession(JID.jidInstance(UUID.randomUUID().toString(), \"domain\", null), JID.jidInstance(userJid, \"setter\"));\n\t\tpushNotifications.enableNotifications(userSession, JID.jidInstance(\"push.localhost\"), \"test-node\", new Element(\"enable\"), null, writer::write);\n\t\tXMPPResourceConnection buddySession = getSession(JID.jidInstance(UUID.randomUUID().toString(), \"domain\", null), JID.jidInstance(buddyJid, \"setter\"));\n\n\t\tupdateRoster(userSession, JID.jidInstance(buddyJid), RosterAbstract.SubscriptionType.both);\n\t\tupdateRoster(buddySession, JID.jidInstance(userJid), RosterAbstract.SubscriptionType.both);\n\n\t\tassertTrue(pushPresence.isPushAvailable(userJid));\n\t\t// add user roster subscription triggers sending presence\n//\t\tSystem.out.println(\"packet writer \" + writer + \" - in test\");\n\t\tassertEquals(1, writer.getQueue().size());\n\t\tassertPresenceAway(writer.getQueue().poll());\n\t\tassertEquals(0, writer.getQueue().size());\n\n\t\tupdateRoster(userSession, JID.jidInstance(buddyJid), RosterAbstract.SubscriptionType.none);\n\t\tupdateRoster(buddySession, JID.jidInstance(userJid), RosterAbstract.SubscriptionType.none);\n\n\t\tassertEquals(1, writer.getQueue().size());\n\t\tassertPresenceUnavailable(writer.getQueue().poll());\n\t}\n\n\tprivate void assertPresenceUnavailable(Packet presence) {\n\t\tassertEquals(\"presence\", presence.getElemName());\n\t\tassertEquals(StanzaType.unavailable, presence.getType());\n\t}\n\n\tprivate void assertPresenceAway(Packet presence) {\n\t\tassertEquals(\"presence\", presence.getElemName());\n\t\tassertEquals(null, presence.getType());\n\t\tassertEquals(JID.jidInstance(buddyJid), presence.getStanzaTo());\n\t\tassertEquals(\"xa\", presence.getElemCDataStaticStr(new String[] { \"presence\", \"show\"}));\n\t}\n\n\tprivate void updateRoster(XMPPResourceConnection session, JID buddy, RosterAbstract.SubscriptionType subscriptionType)\n\t\t\tthrows TigaseDBException, NotAuthorizedException, PolicyViolationException, NoConnectionIdException {\n\t\trosterUtil.addBuddy(session, buddy, null, null, null);\n\t\trosterUtil.setBuddySubscription(session, subscriptionType, buddy);\n\t\tElement new_buddy = rosterUtil.getBuddyItem(session, buddy);\n\t\trosterUtil.updateBuddyChange(session, new ArrayDeque<>(), new_buddy);\n\t\ttry {\n\t\t\tThread.sleep(1000);\n\t\t} catch (Throwable ex) {}\n\t}\n\t\n\tprotected void registerLocalBeans(Kernel kernel) {\n\t\tsuper.registerBeans(kernel);\n\n\t\tkernel.registerBean(\"eventBus\").asInstance(EventBusFactory.getInstance()).exportable().exec();\n\t\tkernel.registerBean(\"sess-man\").asInstance(this.getSessionManagerHandler()).setActive(true).exportable().exec();//.asClass(DummySessionManager.class).setActive(true).exportable().exec();\n\t\tkernel.registerBean(MessageDeliveryLogic.class).setActive(true).exportable().exec();\n\t\tkernel.registerBean(\"msgRepository\").asClass(PushNotificationsTest.MsgRepositoryIfcImpl.class).exportable().exec();\n\t\tkernel.registerBean(PushNotifications.class).setActive(true).exec();\n\t\tkernel.registerBean(\"writer\").asInstance(new TestPacketWriter()).exec();\n\t\tkernel.registerBean(PushPresence.class).setActive(true).exec();\n\t\tkernel.registerBean(PresenceState.class).setActive(true).exec();\n//\t\tSystem.out.println(\"registering local beans done.\");\n\t}\n\n\tprotected static class TestPacketWriter implements PacketWriter {\n\n\t\tprivate final ArrayDeque<Packet> queue = new ArrayDeque<>();\n\n\t\tpublic TestPacketWriter() {\n//\t\t\tSystem.out.println(\"creating new instance \" + this + \"....\");\n\t\t}\n\n\t\t@Override\n\t\tpublic synchronized void write(Collection<Packet> packets) {\n\t\t\tqueue.addAll(packets);\n\t\t}\n\n\t\t@Override\n\t\tpublic synchronized void write(Packet packet) {\n\t\t\tqueue.add(packet);\n\t\t}\n\n\t\t@Override\n\t\tpublic synchronized void write(Packet packet, AsyncCallback callback) {\n\t\t\t queue.add(packet);\n\t\t}\n\n\t\tpublic synchronized ArrayDeque<Packet> getQueue() {\n\t\t\treturn queue;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/roster/PresenceSubscriptionPreApprovalTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.roster;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport tigase.db.TigaseDBException;\nimport tigase.kernel.core.Kernel;\nimport tigase.server.Packet;\nimport tigase.server.PolicyViolationException;\nimport tigase.util.stringprep.TigaseStringprepException;\nimport tigase.vhosts.DummyVHostManager;\nimport tigase.xml.Element;\nimport tigase.xmpp.NotAuthorizedException;\nimport tigase.xmpp.StanzaType;\nimport tigase.xmpp.XMPPException;\nimport tigase.xmpp.XMPPResourceConnection;\nimport tigase.xmpp.impl.PresenceSubscription;\nimport tigase.xmpp.impl.ProcessorTestCase;\nimport tigase.xmpp.jid.JID;\n\nimport java.lang.reflect.Field;\nimport java.util.ArrayDeque;\nimport java.util.HashMap;\nimport java.util.Queue;\nimport java.util.UUID;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\n\npublic class PresenceSubscriptionPreApprovalTest\n\t\textends ProcessorTestCase {\n\n\tprivate JID connID;\n\tprivate JID userJID;\n\tprivate JID buddyJID;\n\tprivate XMPPResourceConnection session;\n\tprivate PresenceSubscription presenceSubscription;\n\tprivate RosterAbstract rosterUtil;\n\t\n\t@Override\n\tprotected void registerBeans(Kernel kernel) {\n\t\tsuper.registerBeans(kernel);\n\t\t//kernel.registerBean(\"sess-man\").asClass(DummySessionManager.class).setActive(true).exportable().exec();\n\t\tkernel.registerBean(PresenceSubscription.class).exec();\n\t}\n\n\t@Before\n\t@Override\n\tpublic void setUp() throws Exception {\n\t\tField f = RosterFactory.class.getDeclaredField(\"shared\");\n\t\tf.setAccessible(true);\n\t\tf.set(null, RosterFactory.newRosterInstance(RosterFactory.ROSTER_IMPL_PROP_VAL));\n\t\tsuper.setUp();\n\t\tregisterBeans(getKernel());\n\t\tvar vHostManager = getInstance(DummyVHostManager.class);\n\t\tvHostManager.addVhost(\"localhost\");\n\t\tpresenceSubscription = getInstance(PresenceSubscription.class);\n\t\trosterUtil = RosterFactory.getRosterImplementation(true);\n\t\tconnID = JID.jidInstanceNS(UUID.randomUUID().toString(), \"localhost\");\n\t\tuserJID = JID.jidInstanceNS(UUID.randomUUID().toString(), \"localhost\");\n\t\tbuddyJID = JID.jidInstanceNS(UUID.randomUUID().toString(), \"localhost\");\n\t\tsession = getSession(connID, userJID, true);\n\t}\n\t\n\t@Test\n\tpublic void test_nonePreApproval_noRosterItem() throws XMPPException, TigaseStringprepException, TigaseDBException {\n\t\tQueue<Packet> results = processPresence(userJID, buddyJID, StanzaType.subscribed);\n\t\tSystem.out.println(results);\n\n\t\tassertTrue(rosterUtil.isPreApproved(session, buddyJID));\n\t\tassertEquals(RosterAbstract.SubscriptionType.none_pre_approved, getSubscription(buddyJID));\n\t}\n\n\t@Test\n\tpublic void test_nonePreApproval_withRosterItem()\n\t\t\tthrows XMPPException, TigaseStringprepException, TigaseDBException, PolicyViolationException {\n\t\trosterUtil.addBuddy(session, buddyJID, \"test\", null, RosterAbstract.SubscriptionType.none, null);\n\t\tQueue<Packet> results = processPresence(userJID, buddyJID, StanzaType.subscribed);\n\t\tSystem.out.println(results);\n\t\tassertTrue(rosterUtil.isPreApproved(session, buddyJID));\n\t\tassertEquals(RosterAbstract.SubscriptionType.none_pre_approved, getSubscription(buddyJID));\n\t}\n\n\t@Test\n\tpublic void test_subscribingBothPreapproval() throws XMPPException, TigaseStringprepException, TigaseDBException {\n\t\tQueue<Packet> results = processPresence(userJID, buddyJID, StanzaType.subscribe);\n\t\tassertEquals(StanzaType.subscribe, results.poll().getType());\n\n\t\tresults = processPresence(userJID, buddyJID, StanzaType.subscribed);\n\t\tassertTrue(rosterUtil.isPreApproved(session, buddyJID));\n\t\tassertEquals(RosterAbstract.SubscriptionType.none_pending_out_pre_approved, getSubscription(buddyJID));\n\n\t\tresults = processPresence(buddyJID, userJID, StanzaType.subscribed);\n\t\tassertEquals(RosterAbstract.SubscriptionType.to_pre_approved, getSubscription(buddyJID));\n\n\t\tresults = processPresence(buddyJID, userJID, StanzaType.subscribe);\n\t\tassertEquals(RosterAbstract.SubscriptionType.both, getSubscription(buddyJID));\n\t}\n\n\t@Test\n\tpublic void test_subscribingBothPreapproval2() throws XMPPException, TigaseStringprepException, TigaseDBException {\n\t\tQueue<Packet> results = processPresence(userJID, buddyJID, StanzaType.subscribe);\n\t\tassertEquals(StanzaType.subscribe, results.poll().getType());\n\n\t\tresults = processPresence(userJID, buddyJID, StanzaType.subscribed);\n\t\tassertTrue(rosterUtil.isPreApproved(session, buddyJID));\n\t\tassertEquals(RosterAbstract.SubscriptionType.none_pending_out_pre_approved, getSubscription(buddyJID));\n\n\t\tresults = processPresence(buddyJID, userJID, StanzaType.subscribe);\n\t\tassertEquals(RosterAbstract.SubscriptionType.from_pending_out, getSubscription(buddyJID));\n\n\t\tresults = processPresence(buddyJID, userJID, StanzaType.subscribed);\n\t\tassertEquals(RosterAbstract.SubscriptionType.both, getSubscription(buddyJID));\n\t}\n\n\tprivate RosterAbstract.SubscriptionType getSubscription(JID jid)\n\t\t\tthrows TigaseDBException, NotAuthorizedException {\n\t\treturn rosterUtil.getBuddySubscription(session, jid);\n\t}\n\n\tprivate Queue<Packet> processPresence(JID from, JID to, StanzaType stanzaType)\n\t\t\tthrows XMPPException {\n\t\tPacket packet = Packet.packetInstance(new Element(\"presence\", new String[] {\"type\"}, new String[]{stanzaType.name()}), from, to);\n\t\tQueue<Packet> results = new ArrayDeque<>();\n\t\tpresenceSubscription.process(packet, session, null, results, new HashMap<>());\n\t\treturn results;\n\t}\n\n}\n"
  },
  {
    "path": "src/test/java/tigase/xmpp/impl/roster/RosterElementTest.java",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\npackage tigase.xmpp.impl.roster;\n\nimport org.junit.Test;\nimport tigase.xmpp.jid.JID;\n\nimport static org.junit.Assert.*;\n\npublic class RosterElementTest {\n\n\t@Test\n\tpublic void testSetName() {\n\t\tRosterElement e = new RosterElement(JID.jidInstanceNS(\"a@b.c\"), null, new String[]{});\n\t\tassertNull(e.getName());\n\t\tassertTrue(e.isModified());\n\n\t\te.getRosterElement();\n\t\tassertFalse(e.isModified());\n\n\t\te.setName(null);\n\t\tassertFalse(e.isModified());\n\t\tassertNull(e.getName());\n\n\t\te.setName(\"jeff\");\n\t\tassertTrue(e.isModified());\n\t\tassertEquals(\"jeff\", e.getName());\n\n\t\te.getRosterElement();\n\t\tassertFalse(e.isModified());\n\n\t\te.setName(\"jeff\");\n\t\tassertFalse(e.isModified());\n\t\tassertEquals(\"jeff\", e.getName());\n\n\t\te.setName(\"bob\");\n\t\tassertTrue(e.isModified());\n\t\tassertEquals(\"bob\", e.getName());\n\n\t\te.getRosterElement();\n\t\tassertFalse(e.isModified());\n\n\t\te.setName(null);\n\t\tassertTrue(e.isModified());\n\t\tassertNull(e.getName());\n\n\t\te.getRosterElement();\n\t\tassertFalse(e.isModified());\n\n\t\te.setName(null);\n\t\tassertFalse(e.isModified());\n\t\tassertNull(e.getName());\n\t}\n\n}\n"
  },
  {
    "path": "src/test/resources/TestScript.groovy",
    "content": "/*\n * Tigase XMPP Server - The instant messaging server\n * Copyright (C) 2004 Tigase, Inc. (office@tigase.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as published by\n * the Free Software Foundation, version 3 of the License.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. Look for COPYING file in the top folder.\n * If not, see http://www.gnu.org/licenses/.\n */\n\npackage tigase.admin\n\nimport tigase.db.UserExistsException\nimport tigase.server.Command\nimport tigase.server.Packet\nimport tigase.xmpp.jid.BareJID\n\nimport java.util.logging.Level\nimport java.util.logging.Logger\n\ndef log = Logger.getLogger(\"tigase.admin\");\n\ndef JID = \"accountjid\"\n\ndef p = (Packet) packet\n\ndef userJid = Command.getFieldValue(packet, JID)\n\nif (log.isLoggable(Level.FINEST)) {\n\tlog.log(Level.FINEST, \"Executing command test-cmd: ${userJid}. Request: ${p}, command: ${this}\")\n}\n\ndef result = p.commandResult(Command.DataType.result)\ntry {\n\tCommand.addTextField(result, \"Note\", \"${userJid}\");\n\tif (log.isLoggable(Level.FINEST)) {\n\t\tlog.log(Level.FINEST, \"User ${userJid} processed correctly. Request packet: ${p}, command: ${this}\")\n\t}\n} catch (Exception ex) {\n\tlog.log(Level.WARNING, \"Error while processing user ${userJid}\", ex)\n\tCommand.addTextField(result, \"Note\", \"Problem processing user ${userJid}.\");\n}\n\nreturn result\n\n"
  },
  {
    "path": "src/test/resources/logging.properties",
    "content": "# Logging\nhandlers = java.util.logging.ConsoleHandler\n\n# tigase levels\ntigase.level = FINEST\n\n# test-cases levels\ntest.tigase.level = FINEST\n\n# Console Logging\njava.util.logging.ConsoleHandler.level = FINEST\n\n\n"
  },
  {
    "path": "tigase-server.doap",
    "content": "<?xml version=\"1.0\"?>\n<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n    <Project xmlns=\"http://usefulinc.com/ns/doap#\"\n             xmlns:foaf=\"http://xmlns.com/foaf/0.1/\"\n             xmlns:xmpp=\"https://linkmauve.fr/ns/xmpp-doap#\"\n             xmlns:schema=\"https://schema.org/\"\n    >\n        <name>Tigase XMPP Server</name>\n        <short-name>tigase-server</short-name>\n\n        <created>2005-10-21</created>\n\n        <shortdesc xml:lang=\"en\">XMPP Server written in Java</shortdesc>\n\n        <description xml:lang=\"en\">Powerful, versatile and FOSS XMPP server written in Java.</description>\n\n        <homepage rdf:resource=\"https://tigase.org/\"/>\n        <download-page rdf:resource=\"https://tigase.net/xmpp-server\"/>\n        <bug-database rdf:resource=\"https://github.com/tigase/tigase-server/issues\"/>\n        <developer-forum rdf:resource=\"xmpp:tigase@muc.tigase.org?join\"/>\n        <support-forum rdf:resource=\"xmpp:tigase@muc.tigase.org?join\"/>\n\n        <license rdf:resource=\"https://github.com/tigase/tigase-server/blob/master/COPYING\"/>\n\n        <!-- See https://github.com/ewilderj/doap/issues/49 -->\n        <language>en</language>\n\n        <schema:logo rdf:resource=\"https://raw.githubusercontent.com/tigase/website-assets/master/tigase/images/tigase-logo.png\"/>\n\n        <programming-language>Java</programming-language>\n\n        <os>Linux</os>\n        <os>FreeBSD</os>\n        <os>Windows</os>\n        <os>macOS</os>\n\n        <category rdf:resource=\"https://linkmauve.fr/ns/xmpp-doap#category-xmpp\"/>\n        <category rdf:resource=\"https://linkmauve.fr/ns/xmpp-doap#category-jabber\"/>\n        <category rdf:resource=\"https://linkmauve.fr/ns/xmpp-doap#category-server\"/>\n\n        <repository>\n            <GitRepository>\n                <location rdf:resource=\"https://github.com/tigase/tigase-server\" />\n                <browse rdf:resource=\"https://github.com/tigase/tigase-server\" />\n            </GitRepository>\n        </repository>\n        \n        <implements rdf:resource=\"https://xmpp.org/rfcs/rfc6120.html\"/>\n        <implements rdf:resource=\"https://xmpp.org/rfcs/rfc6121.html\"/>\n        <implements rdf:resource=\"https://xmpp.org/rfcs/rfc6122.html\"/>\n        <implements rdf:resource=\"https://www.rfc-editor.org/info/rfc7395\"/>\n        <implements rdf:resource=\"https://www.rfc-editor.org/info/rfc7590\"/>\n\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0004.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0009.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0012.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0013.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0016.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0030.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0039.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0045.html\"/>\n                <xmpp:note>implemented in tigase-muc</xmpp:note>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0047.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0049.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0050.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0054.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0059.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0060.html\"/>\n                <xmpp:note>implemented in tigase-pubsub</xmpp:note>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0065.html\"/>\n                <xmpp:note>implemented in tigase-socks5</xmpp:note>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0066.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0068.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0073.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0077.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0079.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0080.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0082.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0083.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0084.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0085.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0086.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0090.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0091.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0092.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0096.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0106.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0107.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0108.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0114.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0115.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0118.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0124.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0128.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0131.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0133.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0138.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0148.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0153.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0157.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0160.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0163.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0170.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0172.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0175.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0176.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0178.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0184.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0185.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0189.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0190.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0191.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0198.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0199.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0203.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0205.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0206.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0209.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0212.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0215.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0220.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0223.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0225.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0237.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0248.html\"/>\n                <xmpp:status>partial</xmpp:status>\n                <xmpp:note>implemented in tigase-pubsub</xmpp:note>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0253.html\"/>\n                <xmpp:status>partial</xmpp:status>\n                <xmpp:note>implemented in tigase-pubsub</xmpp:note>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0280.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0302.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0313.html\"/>\n                <xmpp:note>implemented in tigase-message-archiving</xmpp:note>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0352.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0357.html\"/>\n                <xmpp:note>implementation split between tigase-server (main implementation) and tigase-push (code specific to client-app)</xmpp:note>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0363.html\"/>\n                <xmpp:note>implemented in tigase-http-api</xmpp:note>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0368.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0375.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0387.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0398.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0423.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0433.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n        <implements>\n            <xmpp:SupportedXep>\n                <xmpp:xep rdf:resource=\"https://xmpp.org/extensions/xep-0485.html\"/>\n            </xmpp:SupportedXep>\n        </implements>\n\n        <release>\n            <Version>\n                <revision>8.4.0</revision>\n                <created>2024-06-05</created>\n            </Version>\n        </release>\n    </Project>\n</rdf:RDF>"
  }
]